From 1362f5f450a49d63b92c87082ef9e68d59e98e50 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Wed, 20 Jul 2016 10:00:44 +0200 Subject: [PATCH 0001/1276] reboot: add reboot_panic parameter This "reboot_panic" kernel cmdline parameter allow to change the reboot mode in case of panic only. It use the same format as the "reboot" parameter. Example, for cf9 warm reset in case of panic: reboot_panic=p,w Signed-off-by: Jeremy Rocher Signed-off-by: Tian, Baofeng --- .../admin-guide/kernel-parameters.txt | 3 ++ kernel/reboot.c | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..9437bd12d879 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3899,6 +3899,9 @@ reboot_cpu is s[mp]#### with #### being the processor to be used for rebooting. + reboot_panic= [KNL] + Same as reboot parameter above but only in case of panic. + relax_domain_level= [KNL, SMP] Set scheduler's default relax_domain_level. See Documentation/cgroup-v1/cpusets.txt. diff --git a/kernel/reboot.c b/kernel/reboot.c index 8fb44dec9ad7..ec0108927785 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -43,6 +43,7 @@ int reboot_default = 1; int reboot_cpu; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; +char *reboot_panic_param; /* * If set, this is used for preparing the system to power off. @@ -515,7 +516,7 @@ void orderly_reboot(void) } EXPORT_SYMBOL_GPL(orderly_reboot); -static int __init reboot_setup(char *str) +static int reboot_setup(char *str) { for (;;) { /* @@ -582,3 +583,29 @@ static int __init reboot_setup(char *str) return 1; } __setup("reboot=", reboot_setup); + +static int reboot_panic_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + if (!reboot_panic_param) + return NOTIFY_DONE; + + reboot_setup(reboot_panic_param); + pr_info("panic mode set: %s\n", reboot_panic_param); + + return NOTIFY_DONE; +} + +static struct notifier_block reboot_panic_notifier = { + .notifier_call = reboot_panic_notifier_call, +}; + +static int __init reboot_panic_setup(char *str) +{ + reboot_panic_param = str; + atomic_notifier_chain_register(&panic_notifier_list, + &reboot_panic_notifier); + + return 1; +} +__setup("reboot_panic=", reboot_panic_setup); From 4e1ac5dc0663aca19945d7b59bd9d41652b4265a Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Wed, 27 Dec 2017 12:26:06 +0800 Subject: [PATCH 0002/1276] Debug: Add register dump Store CPU registers during cpu stop in order to get them with ramdump. Signed-off-by: Emmanuel Berthier Signed-off-by: Duan, YayongX --- arch/x86/kernel/smp.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 04adc8d60aed..9bbeec53634c 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -117,6 +117,19 @@ static atomic_t stopping_cpu = ATOMIC_INIT(-1); static bool smp_no_nmi_ipi = false; +static DEFINE_PER_CPU(struct pt_regs, cpu_regs); + +/* Store regs of this CPU for RAM dump decoding help */ +static inline void store_regs(struct pt_regs *regs) +{ + struct pt_regs *print_regs; + print_regs = &get_cpu_var(cpu_regs); + crash_setup_regs(print_regs, regs); + + /* Flush CPU cache */ + wbinvd(); +} + /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing @@ -163,6 +176,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) if (raw_smp_processor_id() == atomic_read(&stopping_cpu)) return NMI_HANDLED; + store_regs(regs); cpu_emergency_vmxoff(); stop_this_cpu(NULL); @@ -173,9 +187,10 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) * this function calls the 'stop' function on all other CPUs in the system. */ -asmlinkage __visible void smp_reboot_interrupt(void) +__visible void smp_reboot_interrupt(struct pt_regs *regs) { ipi_entering_ack_irq(); + store_regs(regs); cpu_emergency_vmxoff(); stop_this_cpu(NULL); irq_exit(); @@ -247,6 +262,7 @@ static void native_stop_other_cpus(int wait) } finish: + store_regs(NULL); local_irq_save(flags); disable_local_APIC(); mcheck_cpu_clear(this_cpu_ptr(&cpu_info)); From ac5a72558af2499ceb99cc0f4ff693f1414a33be Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Fri, 7 Jul 2017 08:40:49 +0530 Subject: [PATCH 0003/1276] [FOR CNL FPGA] Add support for CNL FPGA This includes IMR allocation Change-Id: If53609cd8626c5ab94a418b48b241f6a8572f5fb Signed-off-by: Guneshwor Singh --- sound/soc/intel/Kconfig | 7 +++ sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/cnl-sst.c | 76 +++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0caa1f4eb94d..221283d83619 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -123,7 +123,14 @@ config SND_SOC_ACPI_INTEL_MATCH # this option controls the compilation of ACPI matching tables and # helpers and is not meant to be selected by the user. +config SND_SOC_INTEL_CNL_FPGA + tristate "Enable CNL FPGA board settings" + help + Select Y if you are using FPGA. + If unsure select "N". + endif ## SND_SOC_INTEL_SST_TOPLEVEL + # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 363145716a6d..acf06a4f5144 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -322,6 +322,7 @@ struct sst_dsp { u32 intr_status; const struct firmware *fw; struct snd_dma_buffer dmab; + struct snd_dma_buffer dsp_fw_buf; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 245df1067ba8..f7c832b300d0 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -52,11 +53,69 @@ #define CNL_ADSP_FW_HDR_OFFSET 0x2000 #define CNL_ROM_CTRL_DMA_ID 0x9 +#define CNL_IMR_MEMSIZE 0x400000 /*4MB*/ +#define HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START 0x100 +#define HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START 0x200 +#define HDA_ADSP_REG_ADSPCS_IMR_SIZE 0x8 + +#ifndef writeq +static inline void writeq(u64 val, void __iomem *addr) +{ + writel(((u32) (val)), addr); + writel(((u32) (val >> 32)), addr + 4); +} +#endif + +/* Needed for presilicon platform based on FPGA */ +static int cnl_fpga_alloc_imr(struct sst_dsp *ctx) +{ + u32 pages; + u32 fw_size = CNL_IMR_MEMSIZE; + int ret; + + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, &ctx->dsp_fw_buf, fw_size); + + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); + return ret; + } + + pages = (fw_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + + dev_dbg(ctx->dev, "sst_cnl_fpga_alloc_imr pages=0x%x\n", pages); + set_memory_uc((unsigned long)ctx->dsp_fw_buf.area, pages); + + writeq(virt_to_phys(ctx->dsp_fw_buf.area) + 1, + ctx->addr.shim + HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START); + writeq(virt_to_phys(ctx->dsp_fw_buf.area) + 1, + ctx->addr.shim + HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START); + + writel(CNL_IMR_MEMSIZE, ctx->addr.shim + + HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START + + HDA_ADSP_REG_ADSPCS_IMR_SIZE); + writel(CNL_IMR_MEMSIZE, ctx->addr.shim + + HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START + + HDA_ADSP_REG_ADSPCS_IMR_SIZE); + + memset(ctx->dsp_fw_buf.area, 0, fw_size); + + return 0; +} + +static inline void cnl_fpga_free_imr(struct sst_dsp *ctx) +{ + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->dsp_fw_buf); +} + static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) { int ret, stream_tag; + ret = cnl_fpga_alloc_imr(ctx); + if (ret < 0) + return ret; + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); if (stream_tag <= 0) { dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag); @@ -78,6 +137,21 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) goto base_fw_load_failed; } + + for (ret = CNL_BASEFW_TIMEOUT; + ret > 0 && IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA); --ret) { + u32 reg = sst_dsp_shim_read(ctx, CNL_ADSP_REG_HIPCIDA); + + if (reg & CNL_ADSP_REG_HIPCIDA_DONE) { + sst_dsp_shim_update_bits_forced(ctx, + CNL_ADSP_REG_HIPCIDA, + CNL_ADSP_REG_HIPCIDA_DONE, + CNL_ADSP_REG_HIPCIDA_DONE); + break; + } + + mdelay(1); + } /* enable interrupt */ cnl_ipc_int_enable(ctx); cnl_ipc_op_int_enable(ctx); @@ -95,6 +169,7 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) base_fw_load_failed: ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + cnl_fpga_free_imr(ctx); return ret; } @@ -490,6 +565,7 @@ void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) cnl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); + cnl_fpga_free_imr(ctx->dsp); } EXPORT_SYMBOL_GPL(cnl_sst_dsp_cleanup); From b5180850c13e2b0d7e08fe76b0920c5dd0736659 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 14 Apr 2016 12:02:14 +0530 Subject: [PATCH 0004/1276] [FOR CNL FPGA] Load nhlt from firmware instead of debugfs Change-Id: I316804db785699f28359b35455790c95cccacd10 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 1d17be0f78a0..2d0efe8233a4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -886,6 +886,7 @@ static int skl_probe(struct pci_dev *pci, { struct skl *skl; struct hdac_bus *bus = NULL; + const struct firmware __maybe_unused *nhlt_fw = NULL; int err; /* we use ext core ops, so provide NULL for ops here */ @@ -903,6 +904,8 @@ static int skl_probe(struct pci_dev *pci, device_disable_async_suspend(bus->dev); +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + skl->nhlt_version = skl_get_nhlt_version(bus->dev); skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) { @@ -916,7 +919,25 @@ static int skl_probe(struct pci_dev *pci, skl_nhlt_update_topology_bin(skl); - pci_set_drvdata(skl->pci, bus); +#else + if (request_firmware(&nhlt_fw, "intel/nhlt_blob.bin", bus->dev)) { + dev_err(bus->dev, "Request nhlt fw failed, continuing..\n"); + goto nhlt_continue; + } + + skl->nhlt = devm_kzalloc(&pci->dev, nhlt_fw->size, GFP_KERNEL); + if (skl->nhlt == NULL) + return -ENOMEM; + memcpy(skl->nhlt, nhlt_fw->data, nhlt_fw->size); + release_firmware(nhlt_fw); + +nhlt_continue: +#endif + pci_set_drvdata(skl->pci, ebus); + +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); +#endif /* check if dsp is there */ if (bus->ppcap) { From 0152a84feb272dbd2711fb5714d766fad8506bef Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 17 Nov 2015 05:27:39 +0530 Subject: [PATCH 0005/1276] [FOR CNL FPGA] Add facility to load ROM via debugfs For FPGA images without ROM inbuilt, this adds feature to load the ROM manually via debugfs Change-Id: I653ffd6d6ee735403d0137af33dc2ad701b478ed Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/Makefile | 3 + sound/soc/intel/fpga/Makefile | 4 + sound/soc/intel/fpga/hda_intel_cnl_fpga.c | 274 ++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 sound/soc/intel/fpga/Makefile create mode 100644 sound/soc/intel/fpga/hda_intel_cnl_fpga.c diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 8160520fd74c..ece7c2b228d1 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -10,3 +10,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ # Machine support obj-$(CONFIG_SND_SOC) += boards/ + +# FPGA support +obj-$(CONFIG_SND_SOC_INTEL_CNL_FPGA) += fpga/ diff --git a/sound/soc/intel/fpga/Makefile b/sound/soc/intel/fpga/Makefile new file mode 100644 index 000000000000..f434f7bcfd62 --- /dev/null +++ b/sound/soc/intel/fpga/Makefile @@ -0,0 +1,4 @@ +# for intel cnl fpga device +snd-hda-intel-cnl-fpga-objs := hda_intel_cnl_fpga.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_FPGA) += snd-hda-intel-cnl-fpga.o + diff --git a/sound/soc/intel/fpga/hda_intel_cnl_fpga.c b/sound/soc/intel/fpga/hda_intel_cnl_fpga.c new file mode 100644 index 000000000000..934a1a98b64a --- /dev/null +++ b/sound/soc/intel/fpga/hda_intel_cnl_fpga.c @@ -0,0 +1,274 @@ +/* + * Intel HDA Cannonlake FPGA driver + * + * Copyright (C) 2014, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#define HDA_INTEL_CNL_FPGA_ROM_SIZE (16 * 1024) +#define HDA_INTEL_CNL_FPGA_ROM_BAR_IDX 2 +#define HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET 0x805c +#define HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET 0xc000 + +struct hda_intel_cnl_fpga_dev { + void __iomem *rom_mem_base; + struct pci_dev *pdev; +}; + +static u32 hda_intel_cnl_fpga_readl( + struct hda_intel_cnl_fpga_dev *fpgadev, + u32 offset) +{ + u32 val; + + val = readl((fpgadev)->rom_mem_base + offset); + printk(KERN_INFO "%s: TG@%x == %x\n", __func__, offset, val); + return val; +} + +static void hda_intel_cnl_fpga_writel( + struct hda_intel_cnl_fpga_dev *fpgadev, + u32 offset, + u32 val) +{ + printk("%s: TG@%x <= %x\n", __func__, offset, val); + writel(val, (fpgadev)->rom_mem_base + offset); +} + +static ssize_t hda_intel_cnl_fpga_rom_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = file->private_data; + int i, size; + u32 *buf; + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_read_in\n"); + + dev_dbg(&fpgadev->pdev->dev, "%s: pbuf: %p, *ppos: 0x%llx", + __func__, buffer, *ppos); + + size = HDA_INTEL_CNL_FPGA_ROM_SIZE; + + if (*ppos >= size) + return 0; + if (*ppos + count > size) + count = size - *ppos; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_dbg(&fpgadev->pdev->dev, " %s: kzalloc failed, aborting\n", + __func__); + return -ENOMEM; + } + size = size / sizeof(*buf); + + printk(KERN_INFO "%s: bar: 0x%p\n", __func__, fpgadev->rom_mem_base); + + for (i = 0; i < size; i++) { + hda_intel_cnl_fpga_writel( + fpgadev, HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET, 0); + + buf[i] = hda_intel_cnl_fpga_readl(fpgadev, + HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET + i * sizeof(*buf)); + } + + if (copy_to_user(buffer, buf, count)) { + dev_err(&fpgadev->pdev->dev, " %s: copy_to_user failed, aborting\n", + __func__); + return -EFAULT; + } + kfree(buf); + + *ppos += count; + + dev_dbg(&fpgadev->pdev->dev, "%s: *ppos: 0x%llx, count: %zu", + __func__, *ppos, count); + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_read_out\n"); + + return count; +} + +static ssize_t hda_intel_cnl_fpga_rom_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = file->private_data; + int i, size; + u32 *buf; + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_write_in\n"); + + dev_dbg(&fpgadev->pdev->dev, "%s: pbuf: %p, *ppos: 0x%llx", + __func__, buffer, *ppos); + + size = HDA_INTEL_CNL_FPGA_ROM_SIZE; + + if (*ppos >= size) + return 0; + if (*ppos + count > size) + count = size - *ppos; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(&fpgadev->pdev->dev, " %s: kzalloc failed, aborting\n", + __func__); + return -ENOMEM; + } + + if (copy_from_user(buf, buffer, count)) { + dev_err(&fpgadev->pdev->dev, " %s: copy_from_user failed, aborting\n", + __func__); + return -EFAULT; + } + + size = size / sizeof(*buf); + + for (i = 0; i < size; i++) { + hda_intel_cnl_fpga_writel( + fpgadev, HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET, 0); + + hda_intel_cnl_fpga_writel(fpgadev, + HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET + i * sizeof(*buf), + buf[i]); + } + + kfree(buf); + *ppos += count; + + dev_dbg(&fpgadev->pdev->dev, "%s: *ppos: 0x%llx, count: %zu", + __func__, *ppos, count); + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_write_out\n"); + + return count; +} + +static const struct file_operations hda_intel_cnl_fpga_rom_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = hda_intel_cnl_fpga_rom_read, + .write = hda_intel_cnl_fpga_rom_write, +}; + +static int hda_intel_cnl_fpga_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + struct hda_intel_cnl_fpga_dev *fpgadev; + int ret = 0; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", + __func__, ret); + return ret; + } + + fpgadev = kzalloc(sizeof(struct hda_intel_cnl_fpga_dev), GFP_KERNEL); + if (!fpgadev) { + dev_err(&pdev->dev, "%s: kzalloc failed, aborting.\n", + __func__); + ret = -ENOMEM; + goto err_alloc; + } + + printk(KERN_INFO "%s: bar: 0, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 0)); + printk(KERN_INFO "%s: bar: 0, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 0)); + printk(KERN_INFO "%s: bar: 1, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 1)); + printk(KERN_INFO "%s: bar: 1, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 1)); + printk(KERN_INFO "%s: bar: 2, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 2)); + printk(KERN_INFO "%s: bar: 2, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 2)); + printk(KERN_INFO "%s: bar: 3, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 3)); + printk(KERN_INFO "%s: bar: 3, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 3)); + + fpgadev->rom_mem_base + = pci_ioremap_bar(pdev, HDA_INTEL_CNL_FPGA_ROM_BAR_IDX); + printk("%s: bar: 0x%p\n", __func__, fpgadev->rom_mem_base); + if (!fpgadev->rom_mem_base) { + dev_err(&pdev->dev, "%s: ioremap failed, aborting\n", + __func__); + ret = -ENXIO; + goto err_map; + } + + fpgadev->pdev = pdev; + + pci_set_drvdata(pdev, fpgadev); + + if (!debugfs_create_file("cnl_oed_rom", 0644, NULL, + fpgadev, &hda_intel_cnl_fpga_rom_fops)) { + dev_err(&pdev->dev, "%s: cannot create debugfs entry.\n", + __func__); + ret = -ENODEV; + goto err_map; + } + + return ret; + +err_map: + if (fpgadev->rom_mem_base) { + iounmap(fpgadev->rom_mem_base); + fpgadev->rom_mem_base = NULL; + } + kfree(fpgadev); +err_alloc: + pci_disable_device(pdev); + return ret; +} + +static void hda_intel_cnl_fpga_remove(struct pci_dev *pdev) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = pci_get_drvdata(pdev); + + printk(KERN_INFO "hda_intel_cnl_fpga_remove_in"); + /* unmap PCI memory space, mapped during device init. */ + if (fpgadev->rom_mem_base) { + iounmap(fpgadev->rom_mem_base); + fpgadev->rom_mem_base = NULL; + } + pci_disable_device(pdev); + kfree(fpgadev); + + printk(KERN_INFO "hda_intel_cnl_fpga_remove_out"); +} + +/* PCI IDs */ +static const struct pci_device_id hda_intel_cnl_fpga_ids[] = { + { PCI_DEVICE(0x8086, 0xF501) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, hda_intel_cnl_fpga_ids); + +/* pci_driver definition */ +static struct pci_driver hda_intel_cnl_fpga_driver = { + .name = KBUILD_MODNAME, + .id_table = hda_intel_cnl_fpga_ids, + .probe = hda_intel_cnl_fpga_probe, + .remove = hda_intel_cnl_fpga_remove, +}; + +module_pci_driver(hda_intel_cnl_fpga_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel HDA CNL FPGA driver"); From f092ac66adc09c9b7ccc5f1187df5eda36273289 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Thu, 30 Apr 2015 12:36:21 +0530 Subject: [PATCH 0006/1276] [FOR CNL FPGA] ASoC: mfd: Intel changes for WM8281 integration on K4.0 The changes are related to arizona MFD changes to adopt both FPGA and actual board on kernel 4.0 Change-Id: I40767f73db9f3583797218636e2102190a70cea9 Signed-off-by: Ramesh Babu Signed-off-by: Guneshwor Singh --- drivers/mfd/arizona-i2c.c | 196 +++++++++++++++++++++++++++++++++++++- drivers/mfd/arizona-irq.c | 3 +- 2 files changed, 197 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 5fe12961cfe5..84d29dec0ae1 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -23,6 +23,141 @@ #include "arizona.h" +/************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA +/***********WM8280 1.8V REGULATOR*************/ +static struct regulator_consumer_supply vflorida1_consumer[] = { + REGULATOR_SUPPLY("AVDD", "0-001a"), + REGULATOR_SUPPLY("DBVDD1", "0-001a"), + REGULATOR_SUPPLY("LDOVDD", "0-001a"), + REGULATOR_SUPPLY("CPVDD", "0-001a"), + REGULATOR_SUPPLY("DBVDD2", "0-001a"), + REGULATOR_SUPPLY("DBVDD3", "0-001a"), +}; + +/***********WM8280 5V REGULATOR*************/ +static struct regulator_consumer_supply vflorida2_consumer[] = { + REGULATOR_SUPPLY("SPKVDDL", "0-001a"), + REGULATOR_SUPPLY("SPKVDDR", "0-001a"), +}; +#else +/***********WM8280 1.8V REGULATOR*************/ +static struct regulator_consumer_supply vflorida1_consumer[] = { + REGULATOR_SUPPLY("AVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD1", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("LDOVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("CPVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD2", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD3", "i2c-INT34C1:00"), +}; + +/***********WM8280 5V REGULATOR*************/ +static struct regulator_consumer_supply vflorida2_consumer[] = { + REGULATOR_SUPPLY("SPKVDDL", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("SPKVDDR", "i2c-INT34C1:00"), +}; +#endif + +static struct regulator_init_data vflorida1_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vflorida1_consumer), + .consumer_supplies = vflorida1_consumer, +}; + +static struct fixed_voltage_config vflorida1_config = { + .supply_name = "DC_1V8", + .microvolts = 1800000, + .gpio = -EINVAL, + .init_data = &vflorida1_data, +}; + +static struct platform_device vflorida1_device = { + .name = "reg-fixed-voltage", + .id = PLATFORM_DEVID_AUTO, + .dev = { + .platform_data = &vflorida1_config, + }, +}; + +static struct regulator_init_data vflorida2_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vflorida2_consumer), + .consumer_supplies = vflorida2_consumer, +}; + +static struct fixed_voltage_config vflorida2_config = { + .supply_name = "DC_5V", + .microvolts = 3700000, + .gpio = -EINVAL, + .init_data = &vflorida2_data, +}; + +static struct platform_device vflorida2_device = { + .name = "reg-fixed-voltage", + .id = PLATFORM_DEVID_AUTO, + .dev = { + .platform_data = &vflorida2_config, + }, +}; + +/***********WM8280 Codec Driver platform data*************/ +static const struct arizona_micd_range micd_ctp_ranges[] = { + { .max = 11, .key = BTN_0 }, + { .max = 28, .key = BTN_1 }, + { .max = 54, .key = BTN_2 }, + { .max = 100, .key = BTN_3 }, + { .max = 186, .key = BTN_4 }, + { .max = 430, .key = BTN_5 }, +}; + +static struct arizona_micd_config micd_modes[] = { + /*{Acc Det on Micdet1, Use Micbias2 for detection, + * Set GPIO to 1 to selecte this polarity}*/ + { 0, 2, 1 }, +}; + +static struct arizona_pdata __maybe_unused florida_pdata = { + .reset = 0, /*No Reset GPIO from AP, use SW reset*/ + .irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT, + .clk32k_src = ARIZONA_32KZ_MCLK2, /*Onboard OSC provides 32K on MCLK2*/ + /* + * IN1 uses both MICBIAS1 and MICBIAS2 based on jack polarity, + * the below values in dmic_ref only has meaning for DMIC's and not + * AMIC's + */ +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + .dmic_ref = {ARIZONA_DMIC_MICBIAS1, ARIZONA_DMIC_MICBIAS3, 0, 0}, + .inmode = {ARIZONA_INMODE_DIFF, ARIZONA_INMODE_DMIC, 0, 0}, +#else + .dmic_ref = {ARIZONA_DMIC_MICBIAS1, 0, ARIZONA_DMIC_MICVDD, 0}, + .inmode = {ARIZONA_INMODE_SE, 0, ARIZONA_INMODE_DMIC, 0}, +#endif + .gpio_base = 0, /* Base allocated by gpio core */ + .micd_pol_gpio = 2, /* GPIO3 (offset 2 from gpio_base) of the codec */ + .micd_configs = micd_modes, + .num_micd_configs = ARRAY_SIZE(micd_modes), + .micd_force_micbias = true, +}; + +/************************************************************/ +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA +static struct i2c_board_info arizona_i2c_device = { + I2C_BOARD_INFO("wm8280", 0x1A), + .platform_data = &florida_pdata, +}; +#endif + static int arizona_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -31,10 +166,16 @@ static int arizona_i2c_probe(struct i2c_client *i2c, unsigned long type; int ret; + pr_debug("%s:%d\n", __func__, __LINE__); if (i2c->dev.of_node) type = arizona_of_get_type(&i2c->dev); +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + else + type = WM8280; +#else else type = id->driver_data; +#endif switch (type) { case WM5102: @@ -105,6 +246,13 @@ static const struct i2c_device_id arizona_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, arizona_i2c_id); +#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA +static struct acpi_device_id __maybe_unused arizona_acpi_match[] = { + { "INT34C1", WM8280 }, + { } +}; +#endif + static struct i2c_driver arizona_i2c_driver = { .driver = { .name = "arizona", @@ -116,7 +264,53 @@ static struct i2c_driver arizona_i2c_driver = { .id_table = arizona_i2c_id, }; -module_i2c_driver(arizona_i2c_driver); +static int __init arizona_modinit(void) +{ + int ret = 0; +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + struct i2c_adapter *adapter; + struct i2c_client *client; +#endif + + pr_debug("%s Entry\n", __func__); + /***********WM8280 Register Regulator*************/ + platform_device_register(&vflorida1_device); + platform_device_register(&vflorida2_device); + +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + adapter = i2c_get_adapter(0); + pr_debug("%s:%d\n", __func__, __LINE__); + if (adapter) { + client = i2c_new_device(adapter, &arizona_i2c_device); + pr_debug("%s:%d\n", __func__, __LINE__); + if (!client) { + pr_err("can't create i2c device %s\n", + arizona_i2c_device.type); + i2c_put_adapter(adapter); + pr_debug("%s:%d\n", __func__, __LINE__); + return -ENODEV; + } + } else { + pr_err("adapter is NULL\n"); + return -ENODEV; + } +#endif + pr_debug("%s:%d\n", __func__, __LINE__); + ret = i2c_add_driver(&arizona_i2c_driver); + + pr_debug("%s Exit\n", __func__); + + return ret; +} + +module_init(arizona_modinit); + +static void __exit arizona_modexit(void) +{ + i2c_del_driver(&arizona_i2c_driver); +} + +module_exit(arizona_modexit); MODULE_DESCRIPTION("Arizona I2C bus interface"); MODULE_AUTHOR("Mark Brown "); diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index a307832d7e45..bf50bb41e4a7 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -377,7 +377,8 @@ int arizona_irq_init(struct arizona *arizona) ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread, flags, "arizona", arizona); - if (ret != 0) { + /* FPGA board doesn't have irq line */ + if (ret != 0 && !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)) { dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n", arizona->irq, ret); goto err_main_irq; From f36a34680e09c7b1104bd3cd50489a48f42dcf76 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 30 Dec 2015 13:26:18 +0530 Subject: [PATCH 0007/1276] ASoC: Intel: Add CNL Machine Driver with code wm8281 This adds machine driver for CNL and WM8281 codec. Change-Id: Id910363272f6c1e29bb8963b33f59bf84a800a36 Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_wm8281.c | 643 ++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_wm8281.c diff --git a/sound/soc/intel/boards/cnl_wm8281.c b/sound/soc/intel/boards/cnl_wm8281.c new file mode 100644 index 000000000000..a6a328f1ac7c --- /dev/null +++ b/sound/soc/intel/boards/cnl_wm8281.c @@ -0,0 +1,643 @@ +/* + * cnl_wm8281.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Wolfson Micro + * Copyright (C) 2016 Intel Corp + * Author: Samreen Nilofer + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield MID platform + * Copyright (C) 2014 Wolfson Micro + * Copyright (C) 2014 Intel Corp + * Author: Nikesh Oswal + * Praveen Diwakar + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../codecs/wm5110.h" +#include "../../codecs/wm8998.h" + + +/* Codec PLL output clk rate */ +#define CODEC_SYSCLK_RATE 49152000 +/* Input clock to codec at MCLK1 PIN */ +#define CODEC_IN_MCLK1_RATE 19200000 +/* Input clock to codec at MCLK2 PIN */ +#define CODEC_IN_MCLK2_RATE 32768 +/* Input bit clock to codec */ +#define CODEC_IN_BCLK_RATE 4800000 + +/* define to select between MCLK1 and MCLK2 input to codec as its clock */ +#define CODEC_IN_MCLK1 1 +#define CODEC_IN_MCLK2 2 +#define CODEC_IN_BCLK 3 + +#define SLOT_MASK(x) ((1 << x) - 1) + +struct cnl_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; +static const struct snd_soc_pcm_stream dai_params_modem = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; +static const struct snd_soc_pcm_stream dai_params_bt = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +/* set_osc_clk0- enable/disables the osc clock0 + * addr: address of the register to write to + * enable: bool to enable or disable the clock + */ +static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) +{ + u32 osc_clk_ctrl; + + osc_clk_ctrl = readl(addr); + if (enable) + osc_clk_ctrl |= BIT(31); + else + osc_clk_ctrl &= ~(BIT(31)); + + pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); + + writel(osc_clk_ctrl, addr); +} + +static inline struct snd_soc_codec *cnl_florida_get_codec(struct snd_soc_card *card) +{ + bool found = false; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strstr(component->name, "wm5110-codec")) { + pr_debug("codec was %s", component->name); + continue; + } else { + found = true; + break; + } + } + if (found == false) { + pr_err("%s: cant find codec", __func__); + return NULL; + } + return component->codec; +} + +static struct snd_soc_dai *cnl_florida_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + pr_err("%s: unable to find codec dai\n", __func__); + /* this should never occur */ + WARN_ON(1); + return NULL; +} + +/* Function to switch the input clock for codec, When audio is in + * progress input clock to codec will be through MCLK1 which is 19.2MHz + * while in off state input clock to codec will be through 32KHz through + * MCLK2 + * card : Sound card structure + * src : Input clock source to codec + */ + +static int cnl_florida_set_codec_clk(struct snd_soc_codec *florida_codec, + int src) +{ + int ret; + + pr_debug("cnl_florida_set_codec_clk: source %d\n", src); + + /* reset FLL1 */ + snd_soc_codec_set_pll(florida_codec, WM5110_FLL1_REFCLK, + ARIZONA_FLL_SRC_NONE, 0, 0); + snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_FLL_SRC_NONE, 0, 0); + + switch (src) { + case CODEC_IN_MCLK1: + /* + * Turn ON the PLL to generate required sysclk rate + * from MCLK1 + */ + ret = snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_CLK_SRC_MCLK1, CODEC_IN_MCLK1_RATE, + CODEC_SYSCLK_RATE); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to enable FLL1 with Ref(MCLK) Clock Loop: %d\n", ret); + return ret; + } + break; + case CODEC_IN_BCLK: + /* + * Turn ON the PLL to generate required sysclk rate + * from BCLK + */ + ret = snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_CLK_SRC_AIF1BCLK, CODEC_IN_BCLK_RATE, + CODEC_SYSCLK_RATE); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to enable FLL1 with Ref Clock Loop: %d\n", ret); + return ret; + } + + break; + default: + return -EINVAL; + } + + /*Switch to PLL*/ + ret = snd_soc_codec_set_sysclk(florida_codec, + ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, + CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to set SYSCLK to FLL1: %d\n", ret); + return ret; + } + + return 0; +} + + +static int cnl_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_codec *florida_codec = cnl_florida_get_codec(card); + int ret = 0; + + if (!florida_codec) { + pr_err("%s: florida codec not found\n", __func__); + return -EINVAL; + } + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_info("%s %d Event On\n", __func__, __LINE__); + /* TODO: Ideally MCLK should be used to drive codec PLL + * currently we are using BCLK + */ + ret = cnl_florida_set_codec_clk(florida_codec, CODEC_IN_BCLK); + } else { + pr_info("%s %d Event Off\n", __func__, __LINE__); + /* TODO: Switch to 32K clock for saving power. */ + pr_info("Currently we are not switching to 32K PMIC clock\n"); + } + return ret; + +} +static const struct snd_soc_dapm_widget cnl_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SPK("EP", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_clock_control, SND_SOC_DAPM_PRE_PMU| + SND_SOC_DAPM_POST_PMD), + +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + /* Headphones */ + { "Headphones", NULL, "HPOUT1L" }, + { "Headphones", NULL, "HPOUT1R" }, + + /* Speakers */ + {"Ext Spk", NULL, "SPKOUTLP"}, + {"Ext Spk", NULL, "SPKOUTLN"}, + {"Ext Spk", NULL, "SPKOUTRP"}, + {"Ext Spk", NULL, "SPKOUTRN"}, + + { "AMIC", NULL, "MICBIAS2" }, + { "AMIC", NULL, "MICBIAS1" }, + + { "IN1L", NULL, "AMIC" }, + { "IN1R", NULL, "AMIC" }, + + { "DMic", NULL, "SoC DMIC"}, + { "DMIC01 Rx", NULL, "Capture" }, + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + /* SWM map link the SWM outs to codec AIF */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec1_out"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "ssp0 Rx", NULL, "AIF1 Capture" }, + { "codec0_in", NULL, "ssp0 Rx" }, + + {"Headphones", NULL, "Platform Clock"}, + {"AMIC", NULL, "Platform Clock"}, + {"DMIC", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, + {"EP", NULL, "Platform Clock"}, + {"Tone Generator 1", NULL, "Platform Clock" }, + {"Tone Generator 2", NULL, "Platform Clock" }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), + SOC_DAPM_PIN_SWITCH("EP"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("DMIC"), +}; + +static int cnl_florida_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + unsigned int fmt; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *florida_dai = cnl_florida_get_codec_dai(card, "wm5110-aif1"); + + + pr_info("Entry %s\n", __func__); + + ret = snd_soc_dai_set_tdm_slot(florida_dai, 0, 0, 4, 24); + /* slot width is set as 25, SNDRV_PCM_FORMAT_S32_LE */ + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* bit clock inverse not required */ + fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(florida_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_controls, + ARRAY_SIZE(cnl_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static int cnl_florida_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + + return 0; +} + +struct snd_soc_dai_link cnl_florida_msic_dailink[] = { + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "wm5110-codec", + .codec_dai_name = "wm5110-aif1", + .platform_name = "0000:02:18.0", + .be_hw_params_fixup = cnl_florida_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_florida_init, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .be_hw_params_fixup = cnl_florida_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, + /* codec-codec link */ + { + .name = "CNL SSP0-Loop Port", + .stream_name = "CNL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:02:18.0", + .codec_name = "wm5110-codec", + .codec_dai_name = "wm5110-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, + { + .name = "CNL SSP2-Loop Port", + .stream_name = "CNL SSP2-Loop", + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:02:18.0", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .params = &dai_params_modem, + .dsp_loopback = true, + }, + { + .name = "CNL SSP1-Loop Port", + .stream_name = "CNL SSP1-Loop", + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:02:18.0", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .params = &dai_params_bt, + .dsp_loopback = true, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int snd_cnl_florida_prepare(struct device *dev) +{ + pr_debug("In %s\n", __func__); + return snd_soc_suspend(dev); +} + +static void snd_cnl_florida_complete(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_resume(dev); +} + +static int snd_cnl_florida_poweroff(struct device *dev) +{ + pr_debug("In %s\n", __func__); + return snd_soc_poweroff(dev); +} +#else +#define snd_cnl_florida_prepare NULL +#define snd_cnl_florida_complete NULL +#define snd_cnl_florida_poweroff NULL +#endif + +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:02:18.0"; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "florida-audio", + .dai_link = cnl_florida_msic_dailink, + .num_links = ARRAY_SIZE(cnl_florida_msic_dailink), + .dapm_widgets = cnl_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .add_dai_link = cnl_add_dai_link, +}; + +static int snd_cnl_florida_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl); + if (ret_val) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + devm_kfree(&pdev->dev, drv); + return ret_val; +} + +static int snd_cnl_florida_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +const struct dev_pm_ops snd_cnl_florida_mc_pm_ops = { + .prepare = snd_cnl_florida_prepare, + .complete = snd_cnl_florida_complete, + .poweroff = snd_cnl_florida_poweroff, +}; + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_florida" }, + { .name = "glv_wm8281" }, + { .name = "icl_wm8281" }, + { } +}; +static struct platform_driver snd_cnl_florida_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cnl_florida", + }, + .probe = snd_cnl_florida_mc_probe, + .remove = snd_cnl_florida_mc_remove, + .id_table = cnl_board_ids, +}; + +static int snd_cnl_florida_driver_init(void) +{ + pr_info("cnl_florida: Registering cnl_florida...\n"); + return platform_driver_register(&snd_cnl_florida_mc_driver); +} +module_init(snd_cnl_florida_driver_init); + +static void snd_cnl_florida_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_florida_mc_driver); +} +module_exit(snd_cnl_florida_driver_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_florida"); From 3fc88571750efdee49dc9e24d9f0ff2d8f208f15 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 17 Nov 2016 10:57:43 +0530 Subject: [PATCH 0008/1276] [FPGA] ASoC: rt274: Force load rt274 without acpi ACPI entries are not present in CNL FPGA BIOS, so force load rt274 codec driver. This is required only for FPGA. Change-Id: If901f6488c7dc7aaa3e7534152f2fe0e953f323f Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt274.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index d88e67341083..c5c3ee4d3182 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1220,7 +1220,39 @@ static struct i2c_driver rt274_i2c_driver = { .id_table = rt274_i2c_id, }; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) module_i2c_driver(rt274_i2c_driver); +#else +static struct i2c_board_info rt274_i2c_device = { + I2C_BOARD_INFO("rt274", 0x1c), +}; + +static int __init rt274_modinit(void) +{ + int ret = 0; + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(0); + if (adapter) { + client = i2c_new_device(adapter, &rt274_i2c_device); + if (!client) { + pr_err("can't create i2c device %s\n", + rt274_i2c_device.type); + i2c_put_adapter(adapter); + return -ENODEV; + } + } else { + pr_err("adapter is NULL\n"); + return -ENODEV; + } + + ret = i2c_add_driver(&rt274_i2c_driver); + + return ret; +} +module_init(rt274_modinit); +#endif MODULE_DESCRIPTION("ASoC RT274 driver"); MODULE_AUTHOR("Bard Liao "); From 943bb648a7189a2332dd544aeccf9335f326fa54 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Tue, 17 Nov 2015 02:33:39 +0530 Subject: [PATCH 0009/1276] ASoC: HDA: EXT: Mark dma buffers as un-cacheble Driver shouldn't not assume HDA DMA has snooping enabled. Driver should always allocate non-cached memory. Change-Id: I5b21643d88d7692967c83f4696eb9e3eb7a90178 Signed-off-by: Ramesh Babu Signed-off-by: Hardik T Shah Reviewed-on: --- include/sound/hdaudio.h | 2 ++ sound/hda/ext/hdac_ext_bus.c | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index cd1773d0e08f..0aa0189017ba 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -265,6 +265,8 @@ struct hdac_io_ops { struct snd_dma_buffer *buf); void (*dma_free_pages)(struct hdac_bus *bus, struct snd_dma_buffer *buf); + /* mark memory region as non-cache */ + void (*mark_pages_uc)(struct snd_dma_buffer *buf, bool enable); }; #define HDA_UNSOL_QUEUE_SIZE 64 diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 9c37d9af3023..3e63fcfd6cd8 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include MODULE_DESCRIPTION("HDA extended core"); @@ -55,14 +57,46 @@ static u8 hdac_ext_readb(u8 __iomem *addr) return readb(addr); } +static void hdac_ext_mark_pages_uc(struct snd_dma_buffer *dmab, bool enable) +{ + int pages; + + if (!dmab || !dmab->area || !dmab->bytes) + return; + +#ifdef CONFIG_SND_DMA_SGBUF + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) { + struct snd_sg_buf *sgbuf = dmab->private_data; + + if (enable) + set_pages_array_uc(sgbuf->page_table, sgbuf->pages); + else + set_pages_array_wb(sgbuf->page_table, sgbuf->pages); + return; + } +#endif + pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (enable) + set_memory_uc((unsigned long)dmab->area, pages); + else + set_memory_wb((unsigned long)dmab->area, pages); +} + static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { - return snd_dma_alloc_pages(type, bus->dev, size, buf); + int ret; + + ret = snd_dma_alloc_pages(type, bus->dev, size, buf); + if (ret < 0) + return ret; + hdac_ext_mark_pages_uc(buf, true); + return ret; } static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { + hdac_ext_mark_pages_uc(buf, false); snd_dma_free_pages(buf); } @@ -75,6 +109,7 @@ static const struct hdac_io_ops hdac_ext_default_io = { .reg_readb = hdac_ext_readb, .dma_alloc_pages = hdac_ext_dma_alloc_pages, .dma_free_pages = hdac_ext_dma_free_pages, + .mark_pages_uc = hdac_ext_mark_pages_uc, }; /** From 17ee4122fedc653d2e46e75a3f3c29503347cb49 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 23 Nov 2015 03:22:40 +0530 Subject: [PATCH 0010/1276] ASoC: intel: skylake: mark ring buffer as non-cacheble Change-Id: I8c08f3a91682654e5f1db95033b580258201da91 Signed-off-by: Ramesh Babu Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 823e39103edd..ab31d6a1c842 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -80,17 +80,25 @@ static int skl_substream_alloc_pages(struct hdac_bus *bus, size_t size) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + int ret; hdac_stream(stream)->bufsize = 0; hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; - return snd_pcm_lib_malloc_pages(substream, size); + ret = snd_pcm_lib_malloc_pages(substream, size); + if (ret < 0) + return ret; + ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + + return ret; } static int skl_substream_free_pages(struct hdac_bus *bus, struct snd_pcm_substream *substream) { + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), false); + return snd_pcm_lib_free_pages(substream); } From 711167d9d4fb87d4646f50a2ee684d5d4ca61f9a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 2 Aug 2016 15:37:20 +0530 Subject: [PATCH 0011/1276] REVERTME: ASoC: Intel: CNL: Load firmware in dsp_init. This patch loads the DSP firmware as a part of dsp_init instead of dsp_fw init. SoundWire master controller doesnt get register if the DSP firmware is loaded as part of fw_init, since for master controller registration DSP power up is must, which is part of fw_load function. Change-Id: I37edac4043abca18c189b035cb1e60194e33d79d Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index f7c832b300d0..2969e57e3e93 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -533,21 +533,24 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); - return skl_dsp_acquire_irq(sst); + ret = skl_dsp_acquire_irq(sst); + if (ret < 0) + return ret; + + ret = cnl_load_base_firmware(sst); + if (ret < 0) { + dev_err(dev, "Load base fw failed: %d", ret); + return ret; + } + + return 0; } EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) { - int ret; struct sst_dsp *sst = ctx->dsp; - ret = ctx->dsp->fw_ops.load_fw(sst); - if (ret < 0) { - dev_err(dev, "load base fw failed: %d", ret); - return ret; - } - skl_dsp_init_core_state(sst); ctx->is_first_boot = false; From 0fe631be4f464de32e1f492594af9b7ffc40965b Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 22 Aug 2016 12:13:36 +0530 Subject: [PATCH 0012/1276] ASoC: Intel: CNL: Add library loading support Cannonlake's library loading is similar to broxton's. So reuse it. Change-Id: I3a0d64da0eaef395cd5e6a95252e4025ce7921b2 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/bxt-sst.c | 2 +- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 440bca7afbf1..924c0dfa6ad7 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -53,7 +53,7 @@ static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); } -static int +int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) { struct snd_dma_buffer dmab; diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 2969e57e3e93..a66ed94870b4 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -357,6 +357,7 @@ static const struct skl_dsp_fw_ops cnl_fw_ops = { .set_state_D3 = cnl_set_dsp_D3, .load_fw = cnl_load_base_firmware, .get_fw_errcode = cnl_get_errno, + .load_library = bxt_load_library, }; static struct sst_ops cnl_ops = { @@ -550,9 +551,18 @@ EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) { struct sst_dsp *sst = ctx->dsp; + int ret; skl_dsp_init_core_state(sst); + if (ctx->lib_count > 1) { + ret = sst->fw_ops.load_library(sst, ctx->lib_info, + ctx->lib_count); + if (ret) { + dev_err(dev, "Load Library failed: %#x", ret); + return ret; + } + } ctx->is_first_boot = false; return 0; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index e1d6f6719f7e..33de8c939bc4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -238,6 +238,7 @@ int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); +int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count); int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, unsigned int offset, int index); From 797a17f78dfbdc45750692504cee8de970042760 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 8 Mar 2016 16:00:37 +0530 Subject: [PATCH 0013/1276] Soundwire squashed commits SoundWire: Add place holder for the soundwire bus driver header file This patch creates the place holder file for the SoundWire bus driver header file. Change-Id: Ib71bbe10f5988194ee24f3793950a6c079d56aa7 Signed-off-by: Hardik T Shah SoundWire: Add #defines to the bus driver This patch adds the #defines to the SoundWire bus driver. Change-Id: Ic4dfa9582aa45c3685aee021c156cfbc3e23d817 Signed-off-by: Hardik T Shah SoundWire: Add enum to the SoundWire bus driver This patch adds the enums for the SoundWire bus driver header file. Change-Id: Id9b48b5bacc9137f62099eaa4948390b54d4aac0 Signed-off-by: Hardik T Shah SoundWire: Add forward declaration for the data structures. This patch adds the forward declarations of data structures for the SoundWire bus driver. Change-Id: I8d5d8dc885c7c8a28d68bada9d4b270c603ae4e4 Signed-off-by: Hardik T Shah SoundWire: Add device_id table for SoundWire bus. Add device_id table for the soundwire master and slave devices and driver registration Change-Id: I3848c9476459215ff1d301af69e2eb3c8934bb60 Signed-off-by: Hardik T Shah SoundWire: Add data structures for slave driver and device. This patch adds the data structure for slave driver and device registration with SoundWire bus driver. Change-Id: Ie7f2a0a39fcde3073b560fde41f6c91684975cbe Signed-off-by: Hardik T Shah SoundWire: Add data structures for master driver and device. This patch adds the data structure for master driver and device registration with SoundWire bus driver. Change-Id: I269c3afa0e94979274d0415cfbd0be4c214d1afb Signed-off-by: Hardik T Shah SoundWire: Add message transfer data structure This patch adds the data structure to do register read/write using message tranasfer. This is similar to i2c_transfer on i2c bus. Change-Id: Id10bd88b4fc02c2283334716c38e28b9af862c6e Signed-off-by: Hardik T Shah SoundWire: Add data structure for stream configuration. This patch adds data structure for stream configuration between SoundWire master and slaves. Change-Id: I3a1d06833c881d9ae6bbea38b61f9adb3d8710a0 Signed-off-by: Hardik T Shah SoundWire: Add data structure to support bulk register access This patch adds data structure to support Bulk Register Access (BRA) type of Bulk Transfer Protocol (BTP) between SoundWire master and slaves devices Change-Id: I85a22552428d1388245b9bce7b25cb32779fabf4 Signed-off-by: Hardik T Shah SoundWire: Add data structure to report slave status This patch adds data structure to report slave status to SoundWire bus driver. This is normally called by master controller driver on slave status change on bus. Change-Id: I17d51cbcedb4259015632b6fc0207c705178a19e Signed-off-by: Hardik T Shah SoundWire: Add API to register SoundWire master This patch adds support to register/unregister SoundWire master controller device and driver. Change-Id: I17a65171ea337604af5a868eceade65fb6fcaae6 Signed-off-by: Hardik T Shah SoundWire: Add API for slave driver registration. This patch add supports for registering/unregistering SoundWire slave driver. Slave device is registered by bus driver on enumeration. Change-Id: I9853409c4383e8c87ac04df97d506d0d980f760c Signed-off-by: Hardik T Shah SoundWire: Add API for slave read/write This patch adds API to do register read/write of slaves using message transfer API. This is similar to i2c_transfer for i2c. Change-Id: I7827f8df05db4c7cf264d9b5ea34801f99ab4e48 Signed-off-by: Hardik T Shah SoundWire: Add support for stream configuration This patch adds support for stream configuration APIs between the SoundWire devices. Change-Id: I9b24a937923db93e09675f179b0b62921410c2fc Signed-off-by: Hardik T Shah SoundWire: Add API to update the slave status. This patch adds the bus driver API to update slave status to bus driver. This is called by master controller driver, to update slave status to bus driver. Master controller hardware normally gets interrupt on slave status change. Change-Id: I917dbdbfd45f2172967c8637344e76eb17a53310 Signed-off-by: Hardik T Shah SoundWire: Add miscellaneous helper functions. This patch adds the miscellaneous helper functions to be used by SoundWire master controller and slave device drivers. Change-Id: I1be5d3f2e5bb695ae2b409da5e33217120cf0d3e Signed-off-by: Hardik T Shah SoundWire: Add placeholder for bus driver private file Change-Id: Ib284d063fdc9a653a18114f95feb6c8e73bf46fd Signed-off-by: Hardik T Shah SoundWire: Add #defines to the bus driver private header. This patch adds the #defines to the bus driver private header file. This needs to be exposed only between bus driver source files. These #defines are not publicly exposed. Change-Id: Ib6393566cd95648bfdc790d656f71a3ebe8aaff0 Signed-off-by: Hardik T Shah SoundWire: Add forward declarations of the data structure. This patch adds the forward declarations of the data structures to the soundwire bus driver private header file. Change-Id: I8a5dc317eb143ea0192c85d6f2080314e1a89517 Signed-off-by: Hardik T Shah SoundWire: Add enums to private header file. This patch adds the enums for the SoundWire bus driver private header file. Change-Id: I0927be849cf02da4637510ef3ae094f1cca247d0 Signed-off-by: Hardik T Shah SoundWire: Add data structures to the private header file. This patch adds data structure for the SoundWire private header file. These are used between bus driver source files. Change-Id: I3187078d4be4ef458825a4e0b211059bfa798835 Signed-off-by: Hardik T Shah SoundWire: Add function declarations in private header file. This patch adds the function declartions, to be used between SoundWire source files. Change-Id: Iceff34eafd5406b586cb87206f93012fb05a72c5 Signed-off-by: Hardik T Shah SoundWire: Add SoundWire slave register definition file. This patch adds the register definition file for SoundWire slaves. MIPI defines the register map for the SoundWire slave. This file adds the register map and corresponding bit masks and shift for MIPI defined optional and mandatory registers for slave. This file doesnt add any register definition for vendor specific/implementation defined registers. Change-Id: Ie531e50482703c6f258ae22c3125d3a1f55e4aea Signed-off-by: Hardik T Shah SoundWire: Add placeholder for SoundWire bus driver core file. Change-Id: Ifa29b24717986244ef1cf00fb9865bcf14b2287f Signed-off-by: Hardik T Shah SoundWire: Add includes in SoundWire bus driver core file. This patch adds the includes to the SoundWire bus driver core file. Change-Id: I0ee7f8baa3787a248dae7d5d649d409062fb6112 Signed-off-by: Hardik T Shah SoundWire: Add globals to bus driver core file This patch adds the global instance handling all the buses registered as SoundWire bus. Change-Id: I278e0e418321db7a2fb0aa4adc0c0d15e8b9677b Signed-off-by: Hardik T Shah SoundWire: Add "sdw" bus type registration with kernel core This patch adds the bus type registration to kernel. It also initializes the core data structure as a part of bus initialization. Change-Id: I1cc301a7d7dfe4564cacdd6c5ff37db63060d488 Signed-off-by: Hardik T Shah SoundWire: Implement "match" callback for bus driver This patch implements "match" method for the bus driver registration. Change-Id: I9fabdce35c7010b7fb5f8c52ff5eaa917f68dfc4 Signed-off-by: Hardik T Shah SoundWire: Add probe method for bus driver This patch implementes the probe method for bus driver. It calls the probe of of respective mater/slave driver matching with device name or id. Change-Id: Ie1d7bf8c3e4d86f87f534209c4002e3e28d55c01 Signed-off-by: Hardik T Shah SoundWire: Add remove method for bus driver This patch implementes the remove method for bus driver. Change-Id: Iec7096bfc4bb0017df8006e37018a261d918c514 Signed-off-by: Hardik T Shah SoundWire: Add shutdown method for bus driver This patch implements the shutdown method of the bus. Change-Id: I4c910a4784676399a90c691e32ed31c996fd3623 Signed-off-by: Hardik T Shah SoundWire: Add PM support to bus driver. This patch adds PM support to bus driver. This adds both Runtime PM and Legacy PM support. Change-Id: I9e34c4c06fe41e5da04ce58e6d335f0f29aca6b8 Signed-off-by: Hardik T Shah SoundWire: Add API to register master controller device This patch adds API to register the master controller SoundWire device with the bus as bus master. Change-Id: Id6c7aedb3d7392dcd5672606af5237fccc7d343f Signed-off-by: Hardik T Shah SoundWire: Add API to transfer messages on bus. This patch adds API to transfer register read/write messages on bus. This is used to read/write slave addresses. Change-Id: Iff011f879118440c38d221ea6153bc9eac10049d Signed-off-by: Hardik T Shah SoundWire: Add support to register slaves on enumeration. This patch add supports to register slave devices on enumeration. Master controller driver reports the slave status to bus driver. Bus driver adds the slaves on enumeration. Change-Id: I1b82850f39a15fa18756bb2b28f29bbb1141da4a Signed-off-by: Hardik T Shah SoundWire: Add API to un-register master controller device. This patch adds API to un-register the master controller device. On un-registering the master controller device, all the slaves attached to the bus also gets un-registered. Change-Id: I08eaf9162b3e7d2b7063e8f33cf5d8d79c9f67b3 Signed-off-by: Hardik T Shah SoundWire: Add API to register and un-register master driver This patch adds API to register and un-register the master controller driver controlling the master controller device. Change-Id: I65ffd42c7bd073b0fbb771cb948e24c31f5fa658 Signed-off-by: Hardik T Shah SoundWire: Add API to register/un-register slave driver This patch adds API to register/un-register SoundWire slave device driver. Change-Id: I76732b273ec31fcd34bd819a1384cbf482a997fc Signed-off-by: Hardik T Shah SoundWire: Add API to register slave capabilities to bus This patch adds the API to be used by slave driver to register the slave capabilties to the bus driver. Bus driver uses this to setup the slave for stream transfer over SoundWire interface. Change-Id: I036f01868a417c31adaa70e929b55b9b782702f9 Signed-off-by: Hardik T Shah SoundWire: Add API to alloc and release stream tag. This patch add the bus driver API to allocate and de-allocate stream tag. Stream tag is a unique stream identifier for each audio stream between master and slave. Ports of the masters and slaves part of the same stream has unique and same stream tag. All ports of master and slaves belonging to same stream tag get enabled and disabled together. Change-Id: Ia59ce7f8bfb4e81125cc5334ba4529b78b8afdd4 Signed-off-by: Hardik T Shah SoundWire: Add API to configure and release the stream. This patch adds API to configure and release the soundiwre stream. Once the stream tag is assigned, all the masters and slaves associated with the stream tag configures the stream. Stream params between the master and slave should match. Change-Id: Icdd884d56d09910fcd5e263778362e748f00381c Signed-off-by: Hardik T Shah SoundWire: Add support for masters' and slaves' port config This patch add supports for configuring the master and slave port. Once the stream is configure between master and slave, ports needs to be configured. There may be different ports getting used between master and slave for same stream. Port configuration may also differ like master may use single port to drive stereo stream, but slave may be receiving L & R channels on 2 different ports. So all the slaves and masters part of stream tag calls this function to let bus driver know about its port mapping with stream configuration. Change-Id: I466edd9b2b9430437d4c8081ff42e61f74fe25d0 Signed-off-by: Hardik T Shah SoundWire: Add API to enable and disable the stream. This patch implement bus driver API to enable and disable the stream. All the ports of all the masters and slaves part of the stream tag gets enabled or disable simultaneously with bank switch. Change-Id: Ie205402a78a1e417f49962bacef529f6e9491fac Signed-off-by: Hardik T Shah SoundWire: Add functionality to handle slave alerts This patch adds the functionality to handle slave alerts. SoundWire slave devices can generate alert event on the bus. This patch adds the functionality to handle slave alerts. It also adds functionality to mark slaves as attached or un-attached based on its enumeration status. Change-Id: I69d5af23d891e3ca6684c46ae5f2b5747324b0fe Signed-off-by: Hardik T Shah SoundWire: Add Kconfig and Makefile option This patch adds the Makefile and Kconfig option to compile the bus driver. Change-Id: I47a499e84384c7d0a311b393a64a78b476d6e69e Signed-off-by: Hardik T Shah SoundWire: Added sdw_bwcalc.c file sdw_bwcalc.c file is added in drivers/sdw/ directory. This file will contain APIs which calculates required bandwidth, clock, frameshape, computes all transport params for a given port, enable channel perform bankswitch operations. Change-Id: Ie954bdff81e0faf5ee711c9c33503c4a4d3d39ec Signed-off-by: Sanyog Kale SoundWire: Added APIs to compute bus params Added APIs to compute frame shape, hstart/hstop, block offset & switch bank. This all APIs will be called from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. Change-Id: I9177e36bda0462ccee2bbcf308b49fc65c217a8f Signed-off-by: Sanyog Kale SoundWire: Added API to configure master/slave params Added API which sets xport params of master/slave port and prepare/unprepare, enable/disable channels. This all APIs will be called from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. Change-Id: I5f94b3a1ebee827634dbc3e1475eeed88a2fc716 Signed-off-by: Sanyog Kale SoundWire: Added rest API required for bus params config Added all internal API's which are called from child functions. Change-Id: I652fbe0f52c627875caf0db90624559272bc1131 Signed-off-by: Sanyog Kale SoundWire: Added code in bandwidth calculation API's All the functionality needed to done by sdw_bus_calc_bw & sdw_bus_calc_bw_dis API's are added. Change-Id: Ie833244aea09b30483d9755ec39be5cb35244256 Signed-off-by: Sanyog Kale SoundWire: Added code in sdw_bus_bw_init & sdw_mstr_bw_init All the initializations needed to be done are added in sdw_bus_bw_init & sdw_mstr_bw_init API's Change-Id: Id1c05d994e9a737e01e47dc113b57baa05c5f546 Signed-off-by: Sanyog Kale SoundWire: API's filled to compute bus params Below APIs are modified. sdw_lcm sdw_get_clock_frmshp sdw_compute_sys_interval sdw_compute_hstart_hstop sdw_compute_blk_subblk_offset sdw_configure_frmshp_bnkswtch sdw_get_col_to_num sdw_get_row_to_num Change-Id: I02972b4d992194de3bcd87650b945652e3e9f48d Signed-off-by: Sanyog Kale SoundWire: API's filled to configure mstr/slv parameters Below API's are modified: sdw_cfg_slv_params sdw_cfg_mstr_params sdw_cfg_mstr_slv sdw_cfg_bs_params Change-Id: Id4a2695e1a67be2895414b16f9f89d3fe2366c74 Signed-off-by: Sanyog Kale SoundWire: API's filled for prep/unprep en/dis channel Below APIs are modified: sdw_cfg_slv_prep_unprep sdw_cfg_mstr_prep_unprep sdw_prep_unprep_mstr_slv sdw_cfg_slv_enable_disable sdw_cfg_mstr_activate_disable sdw_en_dis_mstr_slv sdw_en_dis_mstr_slv_state sdw_dis_chan Change-Id: Ic41c4d6bbc8765aa883f10e255f6b9fbb8d8ae64 Signed-off-by: Sanyog Kale SoundWire: Add header file for the intel master controller. This header file has public functions to be used by ASoC platform driver to expose the data path of the Intel master controller. Change-Id: I40c86da2c89c838de69c392ca4a424a3cfb32347 Signed-off-by: Hardik T Shah SoundWire:Intel: Add private file for Intel SDW controller. This file adds the register definition for the Intel SoundWire master controller. Change-Id: I00830ab1b787f486ad20d8da589528a1869ff1ae Signed-off-by: Hardik T Shah SoundWire: CNL: Add SoundWire master controller file. This patch adds the placeholder file for the Intel SoundWire master controller. Change-Id: If16ae98774d576e53007cd6a24d4ba2da7460d3c Signed-off-by: Hardik T Shah SoundWire:Intel: Add exit and init functions. This patch adds the init and exit functions of the Intel SoundWire master controller driver. Change-Id: Ia6a67e8c00786befc2e6a2e39051fdb055ec5c39 Signed-off-by: Hardik T Shah SoundWire: Intel: Add the register read/write functions. This patch adds the register read/write helper functions used by driver. Change-Id: I7fc1c07eb885ecd44a9834b09ec78c200aabe5d5 Signed-off-by: Hardik T Shah SoundWire:Intel: Add probe and remove functionality. This patch adds the probe and remove functionality to the driver. Change-Id: Ieb0bb6ebcec7fba4bbbff41eec9f30b9a544d4e6 Signed-off-by: Hardik T Shah SoundWire:Intel: Add function to power up and down the link. Change-Id: Ib47835a83963985282e7669dc0d2857f6e7eb7ca Signed-off-by: Hardik T Shah SoundWire:Intel: Add functions to initialize the hardware This patch add functions to initialize the Intel SoundWire master controller. Change-Id: I5f360513022482573f1565d7b8daa36971b29a98 Signed-off-by: Hardik T Shah SoundWire:Intel: Add port and stream initialization support. This patch adds the port and stream initialization routine the SoundWire master controller driver. Change-Id: I35c69b890e28e904940dbb91057db6c3126980a6 Signed-off-by: Hardik T Shah SoundWire:Intel: Add support for master controller ops This patch adds the master controller operations callback to bus driver. This callbacks are called by bus driver to controls the bus in device specific way. Change-Id: I962f89f4e9cc2d689cbea754ea4c5a6d1388edb9 Signed-off-by: Hardik T Shah SoundWire:Intel: Add function to the clock frequency on bus. This patch adds the method to be used by bus driver to set the bus frequency. This is used by bus driver to increase or decrease the clock frequency based on bus bandwidth requirement. Bus bandwidth is determined based on active audio streams on bus. Change-Id: Ifb2bfbeaea4cb81ae54e859b3ffa76015f8f394b Signed-off-by: Hardik T Shah SoundWire:Intel: Add method to set SSP and monitor handover. This patch adds the controller driver method to set the Stream Synchronization Point (SSP) and to handover the bus to monitor. Bus driver SSP method to set the SSP based on the active stream sample interval. While monitor handover is used to release the bus ownership to monitor hardware for debugging. Change-Id: Ie6d980717009ab4af437ca5fc494f1859d4496fa Signed-off-by: Hardik T Shah SoundWire:Intel: Add method to transfer message on bus. This patch adds the method to transfer message on bus. This is called by bus driver to accomplish register read/write on bus in hardware specific way. Change-Id: I5507652e321db967c165cf84c2c4a8affff3ff4c Signed-off-by: Hardik T Shah SoundWire: Intel: Add support to configure mater ports. This method registers the port configuration method with bus driver. Since SoundWire master controller register map is not defined by MIPI spec, master controller provides methods to be used by bus driver for master controller port setup. Port setup is done by the bus driver as part of stream setup between SoundWire devices (Master(s) and Slave(s)) Change-Id: I6592102db8c4625822a795918059fd255fbb4780 Signed-off-by: Hardik T Shah SoundWire: Intel: Implement method to set the port params This method implements method to set the SoundWire port params in device specific register map. Change-Id: I3be29f3fc05ac4f1c2d924201c02db795aca1807 Signed-off-by: Hardik T Shah SoundWire: Intel: Add method to set the transport params This patch adds the method to set the transport params of the mater controller port. Since master register map is not defined by MIPI, bus driver calls the master controller method to set the calcuated trasport params based on audio stream configuration. Change-Id: I208776976a347b5158c17c5f02c48fe885ac1c1a Signed-off-by: Hardik T Shah SoundWire:Intel: Add support to activate the port channel. This method add supports to activate the port channels of the master controller. Change-Id: Ia3ace1a3b6038f417ba7ebee2ac7b09b646ad1b8 Signed-off-by: Hardik T Shah SoundWire:Intel: Add API to allocate port Intel master controller supports bi-directional port which can be configured either as Source or Sink ports. This API provides funtionality to dynamically allocate ports for audio streaming. It picks up the free port attaches the ALH stream to it and returns. This is called by ASoC platform driver for port allocation for streaming. Change-Id: Id3747f35b0c476ebe3900115f66daad11adb0d10 Signed-off-by: Hardik T Shah SoundWire:Intel: Add support for ALH stream to port mapping. This patch adds functionality to allocate the ALH stream and map it to the master controller. There can be one-to-one mapping from ALH stream to Port. Function takes care of allocating PCM stream v/s PDM stream based on the calling function requirement. Change-Id: Ic39e6977240df905ff2e95bf9d50abbda6246a28 Signed-off-by: Hardik T Shah SoundWire:Intel: Add the interrupt handler for master controller. This patch adds the interrupt handler for the Intel master controller. It reports the slave status to the bus driver. Change-Id: I1e829c9f830010fe6b6f95a81c81ba48e3b16d79 Signed-off-by: Hardik T Shah SoundWire:Intel: Add the Kconfig and Makefile option. Change-Id: Ic71ae25526b6a00edd9b562864562481e50b3a0e Signed-off-by: Hardik T Shah SoundWire: regmap: Add regmap support for SoundWire bus. This patch adds support for the SoundWire regmap. Change-Id: I0d892f2526c8eb4231f6436e1a539f50911fcd5b Signed-off-by: Hardik T Shah SoundWire: Add API to get and put the master module. This patch implements the API to get and put the master controller module associated with the bus driver. It increments the reference count for every get and decrements for every put. Change-Id: If4f7917b41852b0696a3485fd5f7fcb4258d200f Signed-off-by: Hardik T Shah SoundWire:Maxim: Add support for FPGA based maxim slave. Change-Id: I98154827d88bf87bde49e4d5d704d0e8372fd160 Signed-off-by: Hardik T Shah SoundWire:CNL: Fix the memory corruption. This patch fixes the possible memory corruption. in SoundWire controller driver. Change-Id: I6a19dac25dee9972fb126a75f159b800c18c075a Signed-off-by: Hardik T Shah SoundWire:Maxim: Fix the maxim slave compilation. This patch fixed the Maxim compilation issue. Config option was not correct in Makefile. Change-Id: Idfb287d7c1bbec19fa707e277ee4ddbb3b375217 Signed-off-by: Hardik T Shah SoundWire:Maxim: Add support for multiple slaves. This patch adds support for multiple slaves for the Maxim codec driver. Change-Id: I27602e393e8ddee589bdaf66f833c7ae0be1cee1 Signed-off-by: Hardik T Shah SoundWire: Add support to re-enumerate slave. Slaves might get un-attached because of the link going down due to power management or due to the glitch in clock, where sync is lost. Add support to re-enumerate the slave with same slave number. Change-Id: I443d060cf96c7e7df4e05a188079443f71f0af76 Signed-off-by: Hardik T Shah SoundWire: Add power management support to bus driver 1. Add helper functions to support the power management for both master controller and slave device. 2. Call appropriate runtime PM functions for the master controller and slave device based n bus APIs. Change-Id: I6231cd7258c6d6099f486f26a68bb9bb713db9c1 Signed-off-by: Hardik T Shah SDW:Intel: Add support for PM for master controller Add power management support for the Intel SoundWire master controller driver. Change-Id: Ifa6205cfbf6cf7ec6c4b9f7b4e61ef79e4818889 Signed-off-by: Hardik T Shah SDW: CNL: Fix the PDM port allocation. 1. Fix the PDM stream allocation. 2. Fix port_ctrl and PDI configuration for PDM stream allocation Change-Id: I83f09240d6852f9730b003e607c4dae24b76423e Signed-off-by: Hardik T Shah Reviewed-on: --- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/base/regmap/Kconfig | 11 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-sdwint.c | 252 +++ drivers/sdw/Kconfig | 17 + drivers/sdw/Makefile | 5 + drivers/sdw/sdw.c | 2345 ++++++++++++++++++++++++++ drivers/sdw/sdw_bwcalc.c | 2366 +++++++++++++++++++++++++++ drivers/sdw/sdw_cnl.c | 1583 ++++++++++++++++++ drivers/sdw/sdw_cnl_priv.h | 337 ++++ drivers/sdw/sdw_maxim.c | 146 ++ drivers/sdw/sdw_priv.h | 243 +++ drivers/soundwire/Kconfig | 1 + include/linux/mod_devicetable.h | 13 + include/linux/regmap.h | 7 + include/linux/sdw/sdw_cnl.h | 96 ++ include/linux/sdw/sdw_registers.h | 157 ++ include/linux/sdw_bus.h | 1354 +++++++++++++++ include/trace/events/sdw.h | 232 +++ 20 files changed, 9168 insertions(+) create mode 100644 drivers/base/regmap/regmap-sdwint.c create mode 100644 drivers/sdw/Kconfig create mode 100644 drivers/sdw/Makefile create mode 100644 drivers/sdw/sdw.c create mode 100644 drivers/sdw/sdw_bwcalc.c create mode 100644 drivers/sdw/sdw_cnl.c create mode 100644 drivers/sdw/sdw_cnl_priv.h create mode 100644 drivers/sdw/sdw_maxim.c create mode 100644 drivers/sdw/sdw_priv.h create mode 100644 include/linux/sdw/sdw_cnl.h create mode 100644 include/linux/sdw/sdw_registers.h create mode 100644 include/linux/sdw_bus.h create mode 100644 include/trace/events/sdw.h diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..5af1a08bff23 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -212,6 +212,7 @@ source "drivers/tee/Kconfig" source "drivers/mux/Kconfig" source "drivers/opp/Kconfig" +source "drivers/sdw/Kconfig" source "drivers/visorbus/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..a1a8bf6df4d3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ +obj-$(CONFIG_SDW) += sdw/ diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 6ad5ef48b61e..afa9b8459572 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -49,3 +49,14 @@ config REGMAP_SOUNDWIRE config REGMAP_SCCB tristate depends on I2C + +config REGMAP_HWSPINLOCK + bool + +config REGMAP_SDW + default n + tristate "Regmap support for soundwire" + depends on SDW + help + Enable this if regmap support is required for + soundwire slave devices. diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index f5b4e8851d00..d78c51ae3da2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o obj-$(CONFIG_REGMAP_W1) += regmap-w1.o obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o +obj-$(CONFIG_REGMAP_SDW) += regmap-sdwint.o diff --git a/drivers/base/regmap/regmap-sdwint.c b/drivers/base/regmap/regmap-sdwint.c new file mode 100644 index 000000000000..ed8c28db03b8 --- /dev/null +++ b/drivers/base/regmap/regmap-sdwint.c @@ -0,0 +1,252 @@ +/* + * regmap-sdw.c - Register map access API - SoundWire support + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include + +#include "internal.h" + +#define SDW_SCP_ADDRPAGE1_MASK 0xFF +#define SDW_SCP_ADDRPAGE1_SHIFT 15 + +#define SDW_SCP_ADDRPAGE2_MASK 0xFF +#define SDW_SCP_ADDRPAGE2_SHIFT 22 + +#define SDW_REGADDR_SHIFT 0x0 +#define SDW_REGADDR_MASK 0xFFFF + +#define SDW_MAX_REG_ADDR 65536 + +static int regmap_sdw_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct sdw_slv *sdw = to_sdw_slave(dev); + struct sdw_msg xfer; + int ret, scp_addr1, scp_addr2; + int reg_command; + int reg_addr = *(u32 *)reg; + size_t t_val_size = 0, t_size; + int offset; + u8 *t_val; + + /* All registers are 4 byte on SoundWire bus */ + if (reg_size != 4) + return -ENOTSUPP; + + xfer.slave_addr = sdw->slv_number; + xfer.ssp_tag = 0; + xfer.flag = SDW_MSG_FLAG_READ; + xfer.len = 0; + t_val = val; + + offset = 0; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + if (val_size > SDW_MAX_REG_ADDR) + t_size = SDW_MAX_REG_ADDR - reg_command; + else + t_size = val_size; + while (t_val_size < val_size) { + + scp_addr1 = (reg_addr >> SDW_SCP_ADDRPAGE1_SHIFT) & + SDW_SCP_ADDRPAGE1_MASK; + scp_addr2 = (reg_addr >> SDW_SCP_ADDRPAGE2_SHIFT) & + SDW_SCP_ADDRPAGE2_MASK; + xfer.addr_page1 = scp_addr1; + xfer.addr_page2 = scp_addr2; + xfer.addr = reg_command; + xfer.len += t_size; + xfer.buf = &t_val[offset]; + ret = sdw_slave_transfer(sdw->mstr, &xfer, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EIO; + + t_val_size += t_size; + offset += t_size; + if (val_size - t_val_size > 65535) + t_size = 65535; + else + t_size = val_size - t_val_size; + reg_addr += t_size; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + } + return 0; +} + +static int regmap_sdw_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct sdw_slv *sdw = to_sdw_slave(dev); + struct sdw_msg xfer; + int ret, scp_addr1, scp_addr2; + int reg_command; + int reg_addr = *(u32 *)reg; + size_t t_val_size = 0, t_size; + int offset; + u8 *t_val; + + /* All registers are 4 byte on SoundWire bus */ + if (reg_size != 4) + return -ENOTSUPP; + + if (!sdw) + return 0; + + xfer.slave_addr = sdw->slv_number; + xfer.ssp_tag = 0; + xfer.flag = SDW_MSG_FLAG_WRITE; + xfer.len = 0; + t_val = (u8 *)val; + + offset = 0; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + if (val_size > SDW_MAX_REG_ADDR) + t_size = SDW_MAX_REG_ADDR - reg_command; + else + t_size = val_size; + while (t_val_size < val_size) { + + scp_addr1 = (reg_addr >> SDW_SCP_ADDRPAGE1_SHIFT) & + SDW_SCP_ADDRPAGE1_MASK; + scp_addr2 = (reg_addr >> SDW_SCP_ADDRPAGE2_SHIFT) & + SDW_SCP_ADDRPAGE2_MASK; + xfer.addr_page1 = scp_addr1; + xfer.addr_page2 = scp_addr2; + xfer.addr = reg_command; + xfer.len += t_size; + xfer.buf = &t_val[offset]; + ret = sdw_slave_transfer(sdw->mstr, &xfer, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EIO; + + t_val_size += t_size; + offset += t_size; + if (val_size - t_val_size > 65535) + t_size = 65535; + else + t_size = val_size - t_val_size; + reg_addr += t_size; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + } + return 0; +} + +static inline void regmap_sdw_count_check(size_t count, u32 offset) +{ + BUG_ON(count <= offset); +} + +static int regmap_sdw_write(void *context, const void *data, size_t count) +{ + /* 4-byte register address for the soundwire */ + unsigned int offset = 4; + + regmap_sdw_count_check(count, offset); + return regmap_sdw_gather_write(context, data, 4, + data + offset, count - offset); +} + +static struct regmap_bus regmap_sdw = { + .write = regmap_sdw_write, + .gather_write = regmap_sdw_gather_write, + .read = regmap_sdw_read, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +static int regmap_sdw_config_check(const struct regmap_config *config) +{ + /* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */ + if (config->val_bits != 8) + return -ENOTSUPP; + /* Registers are 32 bit in size, based on SCP_ADDR1 and SCP_ADDR2 + * implementation address range may vary in slave. + */ + if (config->reg_bits != 32) + return -ENOTSUPP; + /* SoundWire register address are contiguous. */ + if (config->reg_stride != 0) + return -ENOTSUPP; + if (config->pad_bits != 0) + return -ENOTSUPP; + + + return 0; +} + +/** + * regmap_init_sdwint(): Initialise register map + * + * @sdw: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return regmap_init(&sdw->dev, ®map_sdw, &sdw->dev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_sdwint); + + +/** + * devm_regmap_init_sdwint(): Initialise managed register map + * + * @sdw Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return devm_regmap_init(&sdw->dev, ®map_sdw, &sdw->dev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_sdwint); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig new file mode 100644 index 000000000000..90e954c392e0 --- /dev/null +++ b/drivers/sdw/Kconfig @@ -0,0 +1,17 @@ +menuconfig SDW + tristate "SoundWire bus support" + help + SoundWire interface is typically used for transporting data + related to audio functions. +menuconfig SDW_CNL + tristate "Intel SoundWire master controller support" + depends on SDW + help + Intel SoundWire master controller driver +menuconfig SDW_MAXIM_SLAVE + bool "SoundWire Slave for the Intel CNL FPGA" + depends on SDW + help + SoundWire Slave on FPGA platform for Intel CNL IP + Mostly N for all the cases other than CNL Slave FPGA + diff --git a/drivers/sdw/Makefile b/drivers/sdw/Makefile new file mode 100644 index 000000000000..184682a88a1a --- /dev/null +++ b/drivers/sdw/Makefile @@ -0,0 +1,5 @@ +sdw_bus-objs := sdw.o sdw_bwcalc.o + +obj-$(CONFIG_SDW) += sdw_bus.o +obj-$(CONFIG_SDW_CNL) += sdw_cnl.o +obj-$(CONFIG_SDW_MAXIM_SLAVE) += sdw_maxim.o diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c new file mode 100644 index 000000000000..6a1ff5e59401 --- /dev/null +++ b/drivers/sdw/sdw.c @@ -0,0 +1,2345 @@ +/* + * sdw.c - SoundWire Bus driver implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdw_priv.h" + +#define sdw_slave_attr_gr NULL +#define sdw_mstr_attr_gr NULL + +#define CREATE_TRACE_POINTS +#include + +/* Global instance handling all the SoundWire buses */ +struct sdw_core sdw_core; + +static void sdw_slave_release(struct device *dev) +{ + kfree(to_sdw_slave(dev)); +} + +static void sdw_mstr_release(struct device *dev) +{ + struct sdw_master *mstr = to_sdw_master(dev); + + complete(&mstr->slv_released); +} + +static struct device_type sdw_slv_type = { + .groups = sdw_slave_attr_gr, + .release = sdw_slave_release, +}; + +static struct device_type sdw_mstr_type = { + .groups = sdw_mstr_attr_gr, + .release = sdw_mstr_release, +}; +/** + * sdw_slave_verify - return parameter as sdw_slv, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-SDW device as an sdw_slv. + */ +struct sdw_slv *sdw_slave_verify(struct device *dev) +{ + return (dev->type == &sdw_slv_type) + ? to_sdw_slave(dev) + : NULL; +} + +/** + * sdw_mstr_verify - return parameter as sdw_master, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-SDW device as an sdw_slv. + */ +struct sdw_master *sdw_mstr_verify(struct device *dev) +{ + return (dev->type == &sdw_mstr_type) + ? to_sdw_master(dev) + : NULL; +} + +static const struct sdw_slv_id *sdw_match_slave(const struct sdw_slv_id *id, + const struct sdw_slv *sdw_slv) +{ + while (id->name[0]) { + if (strncmp(sdw_slv->name, id->name, SOUNDWIRE_NAME_SIZE) == 0) + return id; + id++; + } + return NULL; +} + +static const struct sdw_master_id *sdw_match_master( + const struct sdw_master_id *id, + const struct sdw_master *sdw_mstr) +{ + if (!id) + return NULL; + while (id->name[0]) { + if (strncmp(sdw_mstr->name, id->name, SOUNDWIRE_NAME_SIZE) == 0) + return id; + id++; + } + return NULL; +} + +static int sdw_slv_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_slv *sdw_slv; + struct sdw_slave_driver *drv = to_sdw_slave_driver(driver); + int ret = 0; + + /* Check if driver is slave type or not, both master and slave + * driver has first field as driver_type, so if driver is not + * of slave type return + */ + if (drv->driver_type != SDW_DRIVER_TYPE_SLAVE) + return ret; + + sdw_slv = to_sdw_slave(dev); + + if (drv->id_table) + ret = (sdw_match_slave(drv->id_table, sdw_slv) != NULL); + + if (driver->name && !ret) + ret = (strncmp(sdw_slv->name, driver->name, SOUNDWIRE_NAME_SIZE) + == 0); + if (ret) + sdw_slv->driver = drv; + return ret; +} +static int sdw_mstr_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_master *sdw_mstr; + struct sdw_mstr_driver *drv = to_sdw_mstr_driver(driver); + int ret = 0; + + /* Check if driver is slave type or not, both master and slave + * driver has first field as driver_type, so if driver is not + * of slave type return + */ + if (drv->driver_type != SDW_DRIVER_TYPE_MASTER) + return ret; + + sdw_mstr = to_sdw_master(dev); + + if (drv->id_table) + ret = (sdw_match_master(drv->id_table, sdw_mstr) != NULL); + + if (driver->name) + ret = (strncmp(sdw_mstr->name, driver->name, + SOUNDWIRE_NAME_SIZE) == 0); + if (ret) + sdw_mstr->driver = drv; + + return ret; +} + +static int sdw_mstr_probe(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + struct sdw_master *mstr = to_sdw_master(dev); + int ret = 0; + + if (!sdrv->probe) + return -ENODEV; + ret = dev_pm_domain_attach(dev, true); + if (ret != -EPROBE_DEFER) { + ret = sdrv->probe(mstr, sdw_match_master(sdrv->id_table, mstr)); + if (ret) + dev_pm_domain_detach(dev, true); + } + return ret; +} + +static int sdw_slv_probe(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + struct sdw_slv *sdwslv = to_sdw_slave(dev); + int ret = 0; + + if (!sdrv->probe) + return -ENODEV; + ret = dev_pm_domain_attach(dev, true); + if (ret != -EPROBE_DEFER) { + ret = sdrv->probe(sdwslv, sdw_match_slave(sdrv->id_table, + sdwslv)); + return 0; + if (ret) + dev_pm_domain_detach(dev, true); + } + return ret; +} + +static int sdw_mstr_remove(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + int ret = 0; + + if (sdrv->remove) + ret = sdrv->remove(to_sdw_master(dev)); + else + return -ENODEV; + + dev_pm_domain_detach(dev, true); + return ret; + +} + +static int sdw_slv_remove(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + int ret = 0; + + if (sdrv->remove) + ret = sdrv->remove(to_sdw_slave(dev)); + else + return -ENODEV; + + dev_pm_domain_detach(dev, true); + return ret; +} + +static void sdw_slv_shutdown(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + + if (sdrv->shutdown) + sdrv->shutdown(to_sdw_slave(dev)); +} + +static void sdw_mstr_shutdown(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + struct sdw_master *mstr = to_sdw_master(dev); + + if (sdrv->shutdown) + sdrv->shutdown(mstr); +} + +static void sdw_shutdown(struct device *dev) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + sdw_slv_shutdown(dev); + else if (sdw_mstr) + sdw_mstr_shutdown(dev); +} + +static int sdw_remove(struct device *dev) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_remove(dev); + else if (sdw_mstr) + return sdw_mstr_remove(dev); + + return 0; +} + +static int sdw_probe(struct device *dev) +{ + + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_probe(dev); + else if (sdw_mstr) + return sdw_mstr_probe(dev); + + return -ENODEV; + +} + +static int sdw_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_match(dev, driver); + else if (sdw_mstr) + return sdw_mstr_match(dev, driver); + return 0; + +} + +#ifdef CONFIG_PM_SLEEP +static int sdw_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct sdw_slv *sdw_slv = NULL; + struct sdw_slave_driver *driver; + + if (dev->type == &sdw_slv_type) + sdw_slv = to_sdw_slave(dev); + + if (!sdw_slv || !dev->driver) + return 0; + + driver = to_sdw_slave_driver(dev->driver); + if (!driver->suspend) + return 0; + + return driver->suspend(sdw_slv, mesg); +} + +static int sdw_legacy_resume(struct device *dev) +{ + struct sdw_slv *sdw_slv = NULL; + struct sdw_slave_driver *driver; + + if (dev->type == &sdw_slv_type) + sdw_slv = to_sdw_slave(dev); + + if (!sdw_slv || !dev->driver) + return 0; + + driver = to_sdw_slave_driver(dev->driver); + if (!driver->resume) + return 0; + + return driver->resume(sdw_slv); +} + +static int sdw_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return sdw_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int sdw_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return sdw_legacy_resume(dev); +} + +static const struct dev_pm_ops soundwire_pm = { + .suspend = sdw_pm_suspend, + .resume = sdw_pm_resume, + .runtime_suspend = pm_generic_runtime_suspend, + .runtime_resume = pm_generic_runtime_resume, +}; + +#else +#define sdw_pm_suspend NULL +#define sdw_pm_resume NULL +#endif + +struct bus_type sdwint_bus_type = { + .name = "soundwire", + .match = sdw_match, + .probe = sdw_probe, + .remove = sdw_remove, + .shutdown = sdw_shutdown, + .pm = &soundwire_pm, +}; +EXPORT_SYMBOL_GPL(sdwint_bus_type); + +struct device sdw_slv = { + .init_name = "soundwire", +}; + +static struct static_key sdw_trace_msg = STATIC_KEY_INIT_FALSE; + +int sdw_transfer_trace_reg(void) +{ + static_key_slow_inc(&sdw_trace_msg); +} + +void sdw_transfer_trace_unreg(void) +{ + static_key_slow_dec(&sdw_trace_msg); +} + +/** + * sdw_lock_mstr - Get exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +void sdw_lock_mstr(struct sdw_master *mstr) +{ + rt_mutex_lock(&mstr->bus_lock); +} +EXPORT_SYMBOL_GPL(sdw_lock_mstr); + +/** + * sdw_trylock_mstr - Try to get exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +static int sdw_trylock_mstr(struct sdw_master *mstr) +{ + return rt_mutex_trylock(&mstr->bus_lock); +} + + +/** + * sdw_unlock_mstr - Release exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +void sdw_unlock_mstr(struct sdw_master *mstr) +{ + rt_mutex_unlock(&mstr->bus_lock); +} +EXPORT_SYMBOL_GPL(sdw_unlock_mstr); + + +static int sdw_assign_slv_number(struct sdw_master *mstr, + struct sdw_msg *msg) +{ + int i, j, ret = -1; + + sdw_lock_mstr(mstr); + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned == true) + continue; + mstr->sdw_addr[i].assigned = true; + for (j = 0; j < 6; j++) + mstr->sdw_addr[i].dev_id[j] = msg->buf[j]; + ret = i; + break; + } + sdw_unlock_mstr(mstr); + return ret; +} + +static int sdw_program_slv_address(struct sdw_master *mstr, + u8 slave_addr) +{ + struct sdw_msg msg; + u8 buf[1] = {0}; + int ret; + + buf[0] = slave_addr; + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_DEVNUMBER; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = 0x0; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, &msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "Program Slave address change\n"); + return ret; + } + return 0; +} + +static int sdw_find_slave(struct sdw_master *mstr, struct sdw_msg + *msg, bool *found) +{ + struct sdw_slv_addr *sdw_addr; + int ret = 0, i, comparison; + *found = false; + + sdw_lock_mstr(mstr); + sdw_addr = mstr->sdw_addr; + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + comparison = memcmp(sdw_addr[i].dev_id, msg->buf, + SDW_NUM_DEV_ID_REGISTERS); + if ((!comparison) && (sdw_addr[i].assigned == true)) { + *found = true; + break; + } + } + sdw_unlock_mstr(mstr); + if (*found == true) + ret = sdw_program_slv_address(mstr, sdw_addr[i].slv_number); + return ret; +} + +static void sdw_free_slv_number(struct sdw_master *mstr, + int slv_number) +{ + int i; + + sdw_lock_mstr(mstr); + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (slv_number == mstr->sdw_addr[i].slv_number) { + mstr->sdw_addr[slv_number].assigned = false; + memset(&mstr->sdw_addr[slv_number].dev_id[0], 0x0, 6); + } + } + sdw_unlock_mstr(mstr); +} + +static int sdw_register_slave(struct sdw_master *mstr) +{ + int ret = 0, i, ports; + struct sdw_msg msg; + u8 buf[6] = {0}; + struct sdw_slv *sdw_slv; + int slv_number = -1; + bool found = false; + + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.addr = SDW_SCP_DEVID_0; + msg.len = 6; + msg.buf = buf; + msg.slave_addr = 0x0; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + + while ((ret = (sdw_slave_transfer(mstr, &msg, 1)) == 1)) { + ret = sdw_find_slave(mstr, &msg, &found); + if (found && !ret) { + dev_info(&mstr->dev, "Slave already registered\n"); + continue; + /* Even if slave registering fails we continue for other + * slave status, but we flag error + */ + } else if (ret) { + dev_err(&mstr->dev, "Re-registering slave failed"); + continue; + } + slv_number = sdw_assign_slv_number(mstr, &msg); + if (slv_number <= 0) { + dev_err(&mstr->dev, "Failed to assign slv_number\n"); + ret = -EINVAL; + goto slv_number_assign_fail; + } + sdw_slv = kzalloc(sizeof(struct sdw_slv), GFP_KERNEL); + if (!sdw_slv) { + ret = -ENOMEM; + goto mem_alloc_failed; + } + sdw_slv->mstr = mstr; + sdw_slv->dev.parent = &sdw_slv->mstr->dev; + sdw_slv->dev.bus = &sdwint_bus_type; + sdw_slv->dev.type = &sdw_slv_type; + sdw_slv->slv_addr = &mstr->sdw_addr[slv_number]; + sdw_slv->slv_addr->slave = sdw_slv; + /* We have assigned new slave number, so its not present + * till it again attaches to bus with this new + * slave address + */ + sdw_slv->slv_addr->status = SDW_SLAVE_STAT_NOT_PRESENT; + for (i = 0; i < 6; i++) + sdw_slv->dev_id[i] = msg.buf[i]; + dev_dbg(&mstr->dev, "SDW slave slave id found with values\n"); + dev_dbg(&mstr->dev, "dev_id0 to dev_id5: %x:%x:%x:%x:%x:%x\n", + msg.buf[0], msg.buf[1], msg.buf[2], + msg.buf[3], msg.buf[4], msg.buf[5]); + dev_dbg(&mstr->dev, "Slave number assigned is %x\n", slv_number); + /* TODO: Fill the sdw_slv structre from ACPI */ + ports = sdw_slv->sdw_slv_cap.num_of_sdw_ports; + /* Add 1 for port 0 for simplicity */ + ports++; + sdw_slv->port_ready = + kzalloc((sizeof(struct completion) * ports), + GFP_KERNEL); + if (!sdw_slv->port_ready) { + ret = -ENOMEM; + goto port_alloc_mem_failed; + } + for (i = 0; i < ports; i++) + init_completion(&sdw_slv->port_ready[i]); + + dev_set_name(&sdw_slv->dev, "sdw-slave%d-%02x:%02x:%02x:%02x:%02x:%02x", + sdw_master_id(mstr), + sdw_slv->dev_id[0], + sdw_slv->dev_id[1], + sdw_slv->dev_id[2], + sdw_slv->dev_id[3], + sdw_slv->dev_id[4], + sdw_slv->dev_id[5] + mstr->nr); + /* Set name based on dev_id. This will be + * compared to load driver + */ + sprintf(sdw_slv->name, "%02x:%02x:%02x:%02x:%02x:%02x", + sdw_slv->dev_id[0], + sdw_slv->dev_id[1], + sdw_slv->dev_id[2], + sdw_slv->dev_id[3], + sdw_slv->dev_id[4], + sdw_slv->dev_id[5] + mstr->nr); + ret = device_register(&sdw_slv->dev); + if (ret) { + dev_err(&mstr->dev, "Register slave failed\n"); + goto reg_slv_failed; + } + ret = sdw_program_slv_address(mstr, slv_number); + if (ret) { + dev_err(&mstr->dev, "Programming slave address failed\n"); + goto program_slv_failed; + } + dev_dbg(&mstr->dev, "Slave registered with bus id %s\n", + dev_name(&sdw_slv->dev)); + sdw_slv->slv_number = slv_number; + mstr->num_slv++; + sdw_lock_mstr(mstr); + list_add_tail(&sdw_slv->node, &mstr->slv_list); + sdw_unlock_mstr(mstr); + + } + return 0; +program_slv_failed: + device_unregister(&sdw_slv->dev); +port_alloc_mem_failed: +reg_slv_failed: + kfree(sdw_slv); +mem_alloc_failed: + sdw_free_slv_number(mstr, slv_number); +slv_number_assign_fail: + return ret; + +} + +/** + * __sdw_transfer - unlocked flavor of sdw_slave_transfer + * @mstr: Handle to SDW bus + * @msg: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. mstr->algo->master_xfer existence isn't checked. + */ +int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +{ + unsigned long orig_jiffies; + int ret = 0, try, i; + struct sdw_slv_capabilities *slv_cap; + int program_scp_addr_page; + int addr = msg->slave_addr; + + /* sdw_trace_msg gets enabled when tracepoint sdw_slave_transfer gets + * enabled. This is an efficient way of keeping the for-loop from + * being executed when not needed. + */ + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < num; i++) + if (msg[i].flag & SDW_MSG_FLAG_READ) + trace_sdw_read(mstr, &msg[i], i); + else + trace_sdw_write(mstr, &msg[i], i); + } + orig_jiffies = jiffies; + for (i = 0; i < num; i++) { + for (ret = 0, try = 0; try <= mstr->retries; try++) { + if (msg->slave_addr == 0) + /* If we are enumerating slave address 0, + * we dont program scp, it should be set + * default to 0 + */ + program_scp_addr_page = 0; + else if (msg->slave_addr == 15) + /* If we are broadcasting, we need to program + * the SCP address as some slaves will be + * supporting it while some wont be. + * So it should be programmed + */ + program_scp_addr_page = 1; + + else { + slv_cap = + &mstr->sdw_addr[addr].slave->sdw_slv_cap; + program_scp_addr_page = + slv_cap->paging_supported; + } + ret = mstr->driver->mstr_ops->xfer_msg(mstr, + msg, program_scp_addr_page); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + mstr->timeout)) + break; + } + } + + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < msg->len; i++) + if (msg[i].flag & SDW_MSG_FLAG_READ) + trace_sdw_reply(mstr, &msg[i], i); + trace_sdw_result(mstr, i, ret); + } + if (!ret) + return i; + return ret; +} +EXPORT_SYMBOL_GPL(__sdw_transfer); + +/* NO PM version of slave transfer. Called from power management APIs + * to avoid dead locks. + */ +static int sdw_slave_transfer_nopm(struct sdw_master *mstr, struct sdw_msg *msg, + int num) +{ + int ret; + + if (mstr->driver->mstr_ops->xfer_msg) { + ret = __sdw_transfer(mstr, msg, num); + return ret; + } + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; +} + +/** + * sdw_slave_transfer: Transfer message between slave and mstr on the bus. + * @mstr: mstr master which will transfer the message + * @msg: Array of messages to be transferred. + * @num: Number of messages to be transferred, messages include read and write + * messages, but not the ping messages. + */ +int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +{ + int ret; + + /* REVISIT the fault reporting model here is weak: + * + * - When we get an error after receiving N bytes from a slave, + * there is no way to report "N". + * + * - When we get a NAK after transmitting N bytes to a slave, + * there is no way to report "N" ... or to let the mstr + * continue executing the rest of this combined message, if + * that's the appropriate response. + * + * - When for example "num" is two and we successfully complete + * the first message but get an error part way through the + * second, it's unclear whether that should be reported as + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ + if (!(mstr->driver->mstr_ops->xfer_msg)) { + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; + } + pm_runtime_get_sync(&mstr->dev); + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else { + sdw_lock_mstr(mstr); + } + ret = __sdw_transfer(mstr, msg, num); + sdw_unlock_mstr(mstr); +out: + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_slave_transfer); + +static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, + struct sdw_slave *sdw_slv) +{ + int ret = 0; + struct sdw_msg rd_msg, wr_msg; + int impl_def_mask = 0; + u8 rbuf[1] = {0}, wbuf[1] = {0}; + + /* Create message for clearing the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_DP0_INTCLEAR; + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + /* Create message for reading the interrupts for DP0 interrupts*/ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = SDW_DP0_INTSTAT; + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Interrupt status read failed for slave %x\n", sdw_slv->slv_number); + goto out; + } + if (rd_msg.buf[0] & SDW_DP0_INTSTAT_TEST_FAIL_MASK) { + dev_err(&mstr->dev, "Test fail for slave %d port 0\n", + sdw_slv->slv_number); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_TEST_FAIL_MASK; + } + if (rd_msg.buf[0] & SDW_DP0_INTSTAT_PORT_READY_MASK) { + complete(&sdw_slv->port_ready[0]); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_PORT_READY_MASK; + } + if (rd_msg.buf[0] & SDW_DP0_INTMASK_BRA_FAILURE_MASK) { + /* TODO: Handle BRA failure */ + dev_err(&mstr->dev, "BRA failed for slave %d\n", + sdw_slv->slv_number); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_BRA_FAILURE_MASK; + } + impl_def_mask = SDW_DP0_INTSTAT_IMPDEF1_MASK | + SDW_DP0_INTSTAT_IMPDEF2_MASK | + SDW_DP0_INTSTAT_IMPDEF3_MASK; + if (rd_msg.buf[0] & impl_def_mask) { + /* TODO: Handle implementation defined mask ready */ + wr_msg.buf[0] |= impl_def_mask; + } + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; + +} + +static int sdw_handle_port_interrupt(struct sdw_master *mstr, + struct sdw_slave *sdw_slv, int port_num) +{ + int ret = 0; + struct sdw_msg rd_msg, wr_msg; + u8 rbuf[1], wbuf[1]; + int impl_def_mask = 0; + + if (port_num == 0) + ret = sdw_handle_dp0_interrupts(mstr, sdw_slv); + + /* Create message for reading the port interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_DPN_INTCLEAR + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = SDW_DPN_INTSTAT + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Port Status read failed for slv %x port %x\n", + sdw_slv->slv_number, port_num); + goto out; + } + if (rd_msg.buf[0] & SDW_DPN_INTSTAT_TEST_FAIL_MASK) { + dev_err(&mstr->dev, "Test fail for slave %x port %x\n", + sdw_slv->slv_number, port_num); + wr_msg.buf[0] |= SDW_DPN_INTCLEAR_TEST_FAIL_MASK; + } + if (rd_msg.buf[0] & SDW_DPN_INTSTAT_PORT_READY_MASK) { + complete(&sdw_slv->port_ready[port_num]); + wr_msg.buf[0] |= SDW_DPN_INTCLEAR_PORT_READY_MASK; + } + impl_def_mask = SDW_DPN_INTSTAT_IMPDEF1_MASK | + SDW_DPN_INTSTAT_IMPDEF2_MASK | + SDW_DPN_INTSTAT_IMPDEF3_MASK; + if (rd_msg.buf[0] & impl_def_mask) { + /* TODO: Handle implementation defined mask ready */ + wr_msg.buf[0] |= impl_def_mask; + } + /* Clear and Ack the interrupt */ + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; + +} +static int sdw_handle_slave_alerts(struct sdw_master *mstr, + struct sdw_slv *sdw_slv) +{ + struct sdw_msg rd_msg[3], wr_msg; + u8 rbuf[3], wbuf[1]; + int i, ret = 0; + int cs_port_mask, cs_port_register, cs_port_start, cs_ports; + + + /* Read Instat 1, Instat 2 and Instat 3 registers */ + rd_msg[0].ssp_tag = 0x0; + rd_msg[0].flag = SDW_MSG_FLAG_READ; + rd_msg[0].addr = SDW_SCP_INTSTAT_1; + rd_msg[0].len = 1; + rd_msg[0].buf = &rbuf[0]; + rd_msg[0].slave_addr = sdw_slv->slv_number; + rd_msg[0].addr_page1 = 0x0; + rd_msg[0].addr_page2 = 0x0; + + rd_msg[1].ssp_tag = 0x0; + rd_msg[1].flag = SDW_MSG_FLAG_READ; + rd_msg[1].addr = SDW_SCP_INTSTAT2; + rd_msg[1].len = 1; + rd_msg[1].buf = &rbuf[1]; + rd_msg[1].slave_addr = sdw_slv->slv_number; + rd_msg[1].addr_page1 = 0x0; + rd_msg[1].addr_page2 = 0x0; + + rd_msg[2].ssp_tag = 0x0; + rd_msg[2].flag = SDW_MSG_FLAG_READ; + rd_msg[2].addr = SDW_SCP_INTSTAT3; + rd_msg[2].len = 1; + rd_msg[2].buf = &rbuf[2]; + rd_msg[2].slave_addr = sdw_slv->slv_number; + rd_msg[2].addr_page1 = 0x0; + rd_msg[2].addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_SCP_INTCLEAR1; + wr_msg.len = 1; + wr_msg.buf = &wbuf[0]; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, rd_msg, 3); + if (ret != 3) { + ret = -EINVAL; + dev_err(&mstr->dev, "Reading of register failed\n"); + goto out; + } + /* First handle parity and bus clash interrupts */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_PARITY_MASK) { + dev_err(&mstr->dev, "Parity error detected\n"); + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_PARITY_MASK; + } + /* Handle bus errors */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_BUS_CLASH_MASK) { + dev_err(&mstr->dev, "Bus clash error detected\n"); + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_BUS_CLASH_MASK; + } + /* Handle Port interrupts from Instat_1 registers */ + cs_ports = 4; + cs_port_start = 0; + cs_port_mask = 0x08; + cs_port_register = 0; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } + /* Handle interrupts from instat_2 register */ + if (!(rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK)) + goto handle_instat_3_register; + cs_ports = 7; + cs_port_start = 4; + cs_port_mask = 0x1; + cs_port_register = 1; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } +handle_instat_3_register: + + if (!(rd_msg[1].buf[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK)) + goto handle_instat_3_register; + cs_ports = 4; + cs_port_start = 11; + cs_port_mask = 0x1; + cs_port_register = 2; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } + /* Ack the IntStat 1 interrupts */ + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; +} + +static void handle_slave_status(struct kthread_work *work) +{ + int i, ret = 0; + struct sdw_slv_status *status, *__status__; + struct sdw_bus *bus = + container_of(work, struct sdw_bus, kwork); + struct sdw_master *mstr = bus->mstr; + unsigned long flags; + + /* Handle the new attached slaves to the bus. Register new slave + * to the bus. + */ + list_for_each_entry_safe(status, __status__, &bus->status_list, node) { + if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { + ret += sdw_register_slave(mstr); + if (ret) + /* Even if adding new slave fails, we will + * continue. + */ + dev_err(&mstr->dev, "Registering new slave failed\n"); + } + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && + mstr->sdw_addr[i].assigned == true) + /* Logical address was assigned to slave, but + * now its down, so mark it as not present + */ + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + + else if (status->status[i] == SDW_SLAVE_STAT_ALERT && + mstr->sdw_addr[i].assigned == true) { + /* Handle slave alerts */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; + ret = sdw_handle_slave_alerts(mstr, + mstr->sdw_addr[i].slave); + if (ret) + dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + + + + } else if (status->status[i] == + SDW_SLAVE_STAT_ATTACHED_OK && + mstr->sdw_addr[i].assigned == true) + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_ATTACHED_OK; + } + spin_lock_irqsave(&bus->spinlock, flags); + list_del(&status->node); + spin_unlock_irqrestore(&bus->spinlock, flags); + kfree(status); + } +} + +static int sdw_register_master(struct sdw_master *mstr) +{ + int ret = 0; + int i; + struct sdw_bus *sdw_bus; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdw_bus_type.p))) { + ret = -EAGAIN; + goto bus_init_not_done; + } + /* Sanity checks */ + if (unlikely(mstr->name[0] == '\0')) { + pr_err("sdw-core: Attempt to register an master with no name!\n"); + ret = -EINVAL; + goto mstr_no_name; + } + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) + mstr->sdw_addr[i].slv_number = i; + + rt_mutex_init(&mstr->bus_lock); + INIT_LIST_HEAD(&mstr->slv_list); + INIT_LIST_HEAD(&mstr->mstr_rt_list); + + sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); + if (!sdw_bus) + goto bus_alloc_failed; + sdw_bus->mstr = mstr; + + mutex_lock(&sdw_core.core_lock); + list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); + mutex_unlock(&sdw_core.core_lock); + + dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); + mstr->dev.bus = &sdw_bus_type; + mstr->dev.type = &sdw_mstr_type; + + ret = device_register(&mstr->dev); + if (ret) + goto out_list; + kthread_init_worker(&sdw_bus->kworker); + sdw_bus->status_thread = kthread_run(kthread_worker_fn, + &sdw_bus->kworker, "%s", + dev_name(&mstr->dev)); + if (IS_ERR(sdw_bus->status_thread)) { + dev_err(&mstr->dev, "error: failed to create status message task\n"); + ret = PTR_ERR(sdw_bus->status_thread); + goto task_failed; + } + kthread_init_work(&sdw_bus->kwork, handle_slave_status); + INIT_LIST_HEAD(&sdw_bus->status_list); + spin_lock_init(&sdw_bus->spinlock); + ret = sdw_mstr_bw_init(sdw_bus); + if (ret) { + dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); + goto mstr_bw_init_failed; + } + dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); + + return 0; + +mstr_bw_init_failed: +task_failed: + device_unregister(&mstr->dev); +out_list: + mutex_lock(&sdw_core.core_lock); + list_del(&sdw_bus->bus_node); + mutex_unlock(&sdw_core.core_lock); + kfree(sdw_bus); +bus_alloc_failed: +mstr_no_name: +bus_init_not_done: + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + return ret; +} + +/** + * sdw_master_update_slv_status: Report the status of slave to the bus driver. + * master calls this function based on the + * interrupt it gets once the slave changes its + * state. + * @mstr: Master handle for which status is reported. + * @status: Array of status of each slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status) +{ + struct sdw_bus *bus = NULL; + struct sdw_slv_status *slv_status; + unsigned long flags; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + /* This is master is not registered with bus driver */ + if (!bus) { + dev_info(&mstr->dev, "Master not registered with bus\n"); + return 0; + } + slv_status = kzalloc(sizeof(struct sdw_slv_status), GFP_ATOMIC); + memcpy(slv_status->status, status, sizeof(struct sdw_status)); + + spin_lock_irqsave(&bus->spinlock, flags); + list_add_tail(&slv_status->node, &bus->status_list); + spin_unlock_irqrestore(&bus->spinlock, flags); + + kthread_queue_work(&bus->kworker, &bus->kwork); + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_update_slv_status); + +/** + * sdw_add_master_controller - declare sdw master, use dynamic bus number + * @master: the master to add + * Context: can sleep + * + * This routine is used to declare an sdw master when its bus number + * doesn't matter or when its bus number is specified by an dt alias. + * Examples of bases when the bus number doesn't matter: sdw masters + * dynamically added by USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in mstr->nr, and the specified master became available for slaves. + * Otherwise, a negative errno value is returned. + */ +int sdw_add_master_controller(struct sdw_master *mstr) +{ + int id; + + mutex_lock(&sdw_core.core_lock); + + id = idr_alloc(&sdw_core.idr, mstr, + sdw_core.first_dynamic_bus_num, 0, GFP_KERNEL); + mutex_unlock(&sdw_core.core_lock); + if (id < 0) + return id; + + mstr->nr = id; + + return sdw_register_master(mstr); +} +EXPORT_SYMBOL_GPL(sdw_add_master_controller); + +static void sdw_unregister_slave(struct sdw_slv *sdw_slv) +{ + + struct sdw_master *mstr; + + mstr = sdw_slv->mstr; + sdw_lock_mstr(mstr); + list_del(&sdw_slv->node); + sdw_unlock_mstr(mstr); + mstr->sdw_addr[sdw_slv->slv_number].assigned = false; + memset(mstr->sdw_addr[sdw_slv->slv_number].dev_id, 0x0, 6); + device_unregister(&sdw_slv->dev); + kfree(sdw_slv); +} + +static int __unregister_slave(struct device *dev, void *dummy) +{ + struct sdw_slv *slave = sdw_slave_verify(dev); + + if (slave && strcmp(slave->name, "dummy")) + sdw_unregister_slave(slave); + return 0; +} + +/** + * sdw_del_master_controller - unregister SDW master + * @mstr: the master being unregistered + * Context: can sleep + * + * This unregisters an SDW master which was previously registered + * by @sdw_add_master_controller or @sdw_add_master_controller. + */ +void sdw_del_master_controller(struct sdw_master *mstr) +{ + struct sdw_master *found; + + /* First make sure that this master was ever added */ + mutex_lock(&sdw_core.core_lock); + found = idr_find(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + + if (found != mstr) { + pr_debug("sdw-core: attempting to delete unregistered master [%s]\n", mstr->name); + return; + } + /* Detach any active slaves. This can't fail, thus we do not + * check the returned value. + */ + device_for_each_child(&mstr->dev, NULL, __unregister_slave); + + /* device name is gone after device_unregister */ + dev_dbg(&mstr->dev, "mstrter [%s] unregistered\n", mstr->name); + + /* wait until all references to the device are gone + * + * FIXME: This is old code and should ideally be replaced by an + * alternative which results in decoupling the lifetime of the struct + * device from the sdw_master, like spi or netdev do. Any solution + * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! + */ + init_completion(&mstr->slv_released); + device_unregister(&mstr->dev); + wait_for_completion(&mstr->slv_released); + + /* free bus id */ + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + + /* Clear the device structure in case this mstrter is ever going to be + added again */ + memset(&mstr->dev, 0, sizeof(mstr->dev)); +} +EXPORT_SYMBOL_GPL(sdw_del_master_controller); + +/* + * An sdw_driver is used with one or more sdw_slv (slave) nodes to access + * sdw slave chips, on a bus instance associated with some sdw_master. + */ +int __sdw_mstr_driver_register(struct module *owner, + struct sdw_mstr_driver *driver) +{ + int res; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) + return -EAGAIN; + + /* add the driver to the list of sdw drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &sdwint_bus_type; + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound slaves. + */ + res = driver_register(&driver->driver); + if (res) + return res; + + pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__sdw_mstr_driver_register); + +void sdw_mstr_driver_unregister(struct sdw_mstr_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(sdw_mstr_driver_unregister); + +void sdw_slave_driver_unregister(struct sdw_slave_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(sdw_slave_driver_unregister); + +/* + * An sdw_driver is used with one or more sdw_slv (slave) nodes to access + * sdw slave chips, on a bus instance associated with some sdw_master. + */ +int __sdw_slave_driver_register(struct module *owner, + struct sdw_slave_driver *driver) +{ + int res; + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) + return -EAGAIN; + + /* add the driver to the list of sdw drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &sdwint_bus_type; + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound slaves. + */ + res = driver_register(&driver->driver); + if (res) + return res; + pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__sdw_slave_driver_register); + +int sdw_register_slave_capabilities(struct sdw_slv *sdw, + struct sdw_slv_capabilities *cap) +{ + struct sdw_slv_capabilities *slv_cap; + struct sdw_slv_dpn_capabilities *slv_dpn_cap, *dpn_cap; + struct port_audio_mode_properties *prop, *slv_prop; + int i, j; + + slv_cap = &sdw->sdw_slv_cap; + + slv_cap->wake_up_unavailable = cap->wake_up_unavailable; + slv_cap->wake_up_unavailable = cap->wake_up_unavailable; + slv_cap->test_mode_supported = cap->test_mode_supported; + slv_cap->clock_stop1_mode_supported = cap->clock_stop1_mode_supported; + slv_cap->simplified_clock_stop_prepare = + cap->simplified_clock_stop_prepare; + slv_cap->highphy_capable = cap->highphy_capable; + slv_cap->paging_supported = cap->paging_supported; + slv_cap->bank_delay_support = cap->bank_delay_support; + slv_cap->port_15_read_behavior = cap->port_15_read_behavior; + slv_cap->sdw_dp0_supported = cap->sdw_dp0_supported; + slv_cap->num_of_sdw_ports = cap->num_of_sdw_ports; + slv_cap->sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap->num_of_sdw_ports), GFP_KERNEL); + if (!slv_cap->sdw_dpn_cap) + return -ENOMEM; + + for (i = 0; i < cap->num_of_sdw_ports; i++) { + dpn_cap = &cap->sdw_dpn_cap[i]; + slv_dpn_cap = &slv_cap->sdw_dpn_cap[i]; + slv_dpn_cap->port_direction = dpn_cap->port_direction; + slv_dpn_cap->port_number = dpn_cap->port_number; + slv_dpn_cap->max_word_length = dpn_cap->max_word_length; + slv_dpn_cap->min_word_length = dpn_cap->min_word_length; + slv_dpn_cap->num_word_length = dpn_cap->num_word_length; + if (NULL == dpn_cap->word_length_buffer) + slv_dpn_cap->word_length_buffer = + dpn_cap->word_length_buffer; + else { + slv_dpn_cap->word_length_buffer = + devm_kzalloc(&sdw->dev, + dpn_cap->num_word_length * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_dpn_cap->word_length_buffer) + return -ENOMEM; + memcpy(slv_dpn_cap->word_length_buffer, + dpn_cap->word_length_buffer, + dpn_cap->num_word_length * + (sizeof(unsigned int))); + } + slv_dpn_cap->dpn_type = dpn_cap->dpn_type; + slv_dpn_cap->dpn_grouping = dpn_cap->dpn_grouping; + slv_dpn_cap->prepare_ch = dpn_cap->prepare_ch; + slv_dpn_cap->imp_def_intr_mask = dpn_cap->imp_def_intr_mask; + slv_dpn_cap->min_ch_num = dpn_cap->min_ch_num; + slv_dpn_cap->max_ch_num = dpn_cap->max_ch_num; + slv_dpn_cap->num_ch_supported = dpn_cap->num_ch_supported; + if (NULL == slv_dpn_cap->ch_supported) + slv_dpn_cap->ch_supported = dpn_cap->ch_supported; + else { + slv_dpn_cap->ch_supported = + devm_kzalloc(&sdw->dev, + dpn_cap->num_ch_supported * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_dpn_cap->ch_supported) + return -ENOMEM; + memcpy(slv_dpn_cap->ch_supported, + dpn_cap->ch_supported, + dpn_cap->num_ch_supported * + (sizeof(unsigned int))); + } + slv_dpn_cap->port_flow_mode_mask = + dpn_cap->port_flow_mode_mask; + slv_dpn_cap->block_packing_mode_mask = + dpn_cap->block_packing_mode_mask; + slv_dpn_cap->port_encoding_type_mask = + dpn_cap->port_encoding_type_mask; + slv_dpn_cap->num_audio_modes = dpn_cap->num_audio_modes; + + slv_dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + if (!slv_dpn_cap->mode_properties) + return -ENOMEM; + + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + slv_prop = &slv_dpn_cap->mode_properties[j]; + slv_prop->max_frequency = prop->max_frequency; + slv_prop->min_frequency = prop->min_frequency; + slv_prop->num_freq_configs = prop->num_freq_configs; + if (NULL == slv_prop->freq_supported) + slv_prop->freq_supported = + prop->freq_supported; + else { + slv_prop->freq_supported = + devm_kzalloc(&sdw->dev, + prop->num_freq_configs * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_prop->freq_supported) + return -ENOMEM; + memcpy(slv_prop->freq_supported, + prop->freq_supported, + prop->num_freq_configs * + (sizeof(unsigned int))); + } + slv_prop->glitchless_transitions_mask + = prop->glitchless_transitions_mask; + slv_prop->max_sampling_frequency = + prop->max_sampling_frequency; + slv_prop->min_sampling_frequency = + prop->min_sampling_frequency; + slv_prop->num_sampling_freq_configs = + prop->num_sampling_freq_configs; + if (NULL == prop->sampling_freq_config) + slv_prop->sampling_freq_config = + prop->sampling_freq_config; + else { + slv_prop->sampling_freq_config = + devm_kzalloc(&sdw->dev, + prop->num_sampling_freq_configs * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_prop->sampling_freq_config) + return -ENOMEM; + memcpy(slv_prop->sampling_freq_config, + prop->sampling_freq_config, + prop->num_sampling_freq_configs * + (sizeof(unsigned int))); + } + + slv_prop->ch_prepare_behavior = + prop->ch_prepare_behavior; + } + } + sdw->slave_cap_updated = true; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_register_slave_capabilities); + +static int sdw_get_stream_tag(char *key, int *stream_tag) +{ + int i; + int ret = -EINVAL; + struct sdw_runtime *sdw_rt; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + /* If stream tag is already allocated return that after incrementing + * reference count. This is only possible if key is provided. + */ + mutex_lock(&sdw_core.core_lock); + if (!key) + goto key_check_not_required; + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (!(strcmp(stream_tags[i].key, key))) { + stream_tags[i].ref_count++; + *stream_tag = stream_tags[i].stream_tag; + mutex_unlock(&sdw_core.core_lock); + return 0; + } + } +key_check_not_required: + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (!stream_tags[i].ref_count) { + *stream_tag = stream_tags[i].stream_tag; + mutex_init(&stream_tags[i].stream_lock); + sdw_rt = kzalloc(sizeof(struct sdw_runtime), + GFP_KERNEL); + if (!sdw_rt) { + ret = -ENOMEM; + mutex_unlock(&sdw_core.core_lock); + goto out; + } + stream_tags[i].ref_count++; + INIT_LIST_HEAD(&sdw_rt->slv_rt_list); + INIT_LIST_HEAD(&sdw_rt->mstr_rt_list); + sdw_rt->stream_state = SDW_STATE_INIT_STREAM_TAG; + stream_tags[i].sdw_rt = sdw_rt; + if (key) + strlcpy(stream_tags[i].key, key, + SDW_MAX_STREAM_TAG_KEY_SIZE); + mutex_unlock(&sdw_core.core_lock); + return 0; + } + } + mutex_unlock(&sdw_core.core_lock); +out: + return ret; +} + +void sdw_release_stream_tag(int stream_tag) +{ + int i; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + mutex_lock(&sdw_core.core_lock); + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream_tags[i].ref_count--; + if (stream_tags[i].ref_count == 0) { + kfree(stream_tags[i].sdw_rt); + memset(stream_tags[i].key, 0x0, + SDW_MAX_STREAM_TAG_KEY_SIZE); + } + } + } + mutex_unlock(&sdw_core.core_lock); +} +EXPORT_SYMBOL_GPL(sdw_release_stream_tag); + +/** + * sdw_alloc_stream_tag: Assign the stream tag for the unique streams + * between master and slave device. + * Normally master master will request for the + * stream tag for the stream between master + * and slave device. It programs the same stream + * tag to the slave device. Stream tag is unique + * for all the streams between masters and slave + * across SoCs. + * @guid: Group of the device port. All the ports of the device with + * part of same stream will have same guid. + * + * @stream:tag: Stream tag returned by bus driver. + */ +int sdw_alloc_stream_tag(char *guid, int *stream_tag) +{ + int ret = 0; + + ret = sdw_get_stream_tag(guid, stream_tag); + if (ret) { + pr_err("Stream tag assignment failed\n"); + goto out; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(sdw_alloc_stream_tag); + +static struct sdw_mstr_runtime *sdw_get_mstr_rt(struct sdw_runtime *sdw_rt, + struct sdw_master *mstr) { + + struct sdw_mstr_runtime *mstr_rt; + int ret = 0; + + list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt->mstr == mstr) + return mstr_rt; + } + + /* Allocate sdw_mstr_runtime structure */ + mstr_rt = kzalloc(sizeof(struct sdw_mstr_runtime), GFP_KERNEL); + if (!mstr_rt) { + ret = -ENOMEM; + goto out; + } + + /* Initialize sdw_mstr_runtime structure */ + INIT_LIST_HEAD(&mstr_rt->port_rt_list); + INIT_LIST_HEAD(&mstr_rt->slv_rt_list); + list_add_tail(&mstr_rt->mstr_sdw_node, &sdw_rt->mstr_rt_list); + list_add_tail(&mstr_rt->mstr_node, &mstr->mstr_rt_list); + mstr_rt->rt_state = SDW_STATE_INIT_RT; + mstr_rt->mstr = mstr; +out: + return mstr_rt; +} + +static struct sdw_slave_runtime *sdw_config_slave_stream( + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + struct sdw_runtime *sdw_rt) +{ + struct sdw_slave_runtime *slv_rt; + int ret = 0; + struct sdw_stream_params *str_p; + + slv_rt = kzalloc(sizeof(struct sdw_slave_runtime), GFP_KERNEL); + if (!slv_rt) { + ret = -ENOMEM; + goto out; + } + slv_rt->slave = slave; + str_p = &slv_rt->stream_params; + slv_rt->direction = stream_config->direction; + slv_rt->rt_state = SDW_STATE_CONFIG_RT; + str_p->rate = stream_config->frame_rate; + str_p->channel_count = stream_config->channel_count; + str_p->bps = stream_config->bps; + INIT_LIST_HEAD(&slv_rt->port_rt_list); +out: + return slv_rt; +} + +static void sdw_release_mstr_stream(struct sdw_master *mstr, + struct sdw_runtime *sdw_rt) +{ + struct sdw_mstr_runtime *mstr_rt, *__mstr_rt; + + list_for_each_entry_safe(mstr_rt, __mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt->mstr == mstr) { + list_del(&mstr_rt->mstr_sdw_node); + if (mstr_rt->direction == SDW_DATA_DIR_OUT) + sdw_rt->tx_ref_count--; + else + sdw_rt->rx_ref_count--; + list_del(&mstr_rt->mstr_node); + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + kfree(mstr_rt); + } + } +} + +static void sdw_release_slave_stream(struct sdw_slv *slave, + struct sdw_runtime *sdw_rt) +{ + struct sdw_slave_runtime *slv_rt, *__slv_rt; + + list_for_each_entry_safe(slv_rt, __slv_rt, &sdw_rt->slv_rt_list, + slave_sdw_node) { + if (slv_rt->slave == slave) { + list_del(&slv_rt->slave_sdw_node); + if (slv_rt->direction == SDW_DATA_DIR_OUT) + sdw_rt->tx_ref_count--; + else + sdw_rt->rx_ref_count--; + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_sync_autosuspend(&slave->dev); + kfree(slv_rt); + } + } +} + +/** + * sdw_release_stream: De-allocates the bandwidth allocated to the + * the stream. This is reference counted, + * so for the last stream count, BW will be de-allocated + * for the stream. Normally this will be called + * as part of hw_free. + * + * @mstr: Master handle + * @slave: SoundWire slave handle. + * @stream_config: Stream configuration for the soundwire audio stream. + * @stream_tag: Unique stream tag identifier across SoC for all soundwire + * busses. + * for each audio stream between slaves. This stream tag + * will be allocated by master driver for every + * stream getting open. + */ +int sdw_release_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + unsigned int stream_tag) +{ + int i; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + break; + } + } + if (!sdw_rt) { + dev_err(&mstr->dev, "Invalid stream tag\n"); + return -EINVAL; + } + if (!slave) + sdw_release_mstr_stream(mstr, sdw_rt); + else + sdw_release_slave_stream(slave, sdw_rt); + return 0; +} +EXPORT_SYMBOL_GPL(sdw_release_stream); + +/** + * sdw_configure_stream: Allocates the B/W onto the soundwire bus + * for transferring the data between slave and master. + * This is configuring the single stream of data. + * This will be called by slave, Slave stream + * configuration should match the master stream + * configuration. Normally slave would call this + * as a part of hw_params. + * + * @mstr: Master handle + * @sdw_slv: SoundWire slave handle. + * @stream_config: Stream configuration for the soundwire audio stream. + * @stream_tag: Unique stream tag identifier across the soundwire bus + * for each audio stream between slaves and master. + * This is something like stream_tag in HDA protocol, but + * here its virtual rather than being embedded into protocol. + * Further same stream tag is valid across masters also + * if some ports of the master is participating in + * stream aggregation. This is input parameters to the + * function. + */ +int sdw_config_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + unsigned int stream_tag) +{ + int i; + int ret = 0; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_mstr_runtime *mstr_rt = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + stream = &stream_tags[i]; + break; + } + } + if (!sdw_rt) { + dev_dbg(&mstr->dev, "Valid stream tag not found\n"); + ret = -EINVAL; + goto out; + } + if (static_key_false(&sdw_trace_msg)) + trace_sdw_config_stream(mstr, slave, stream_config, + stream_tag); + + mutex_lock(&stream->stream_lock); + + mstr_rt = sdw_get_mstr_rt(sdw_rt, mstr); + if (!mstr_rt) { + dev_err(&mstr->dev, "master runtime configuration failed\n"); + ret = -EINVAL; + goto out; + } + + if (!slave) { + mstr_rt->direction = stream_config->direction; + mstr_rt->rt_state = SDW_STATE_CONFIG_RT; + sdw_rt->xport_state = SDW_STATE_ONLY_XPORT_STREAM; + + mstr_rt->stream_params.rate = stream_config->frame_rate; + mstr_rt->stream_params.channel_count = + stream_config->channel_count; + mstr_rt->stream_params.bps = stream_config->bps; + + } else + slv_rt = sdw_config_slave_stream(slave, + stream_config, sdw_rt); + /* Stream params will be stored based on Tx only, since there can + * be only one Tx and muliple Rx, There can be muliple Tx if + * there is aggregation on Tx. That is handled by adding the channels + * to stream_params for each aggregated Tx slaves + */ + if (!sdw_rt->tx_ref_count && stream_config->direction == + SDW_DATA_DIR_OUT) { + sdw_rt->stream_params.rate = stream_config->frame_rate; + sdw_rt->stream_params.channel_count = + stream_config->channel_count; + sdw_rt->stream_params.bps = stream_config->bps; + sdw_rt->tx_ref_count++; + } + + + /* Normally there will be only one Tx in system, multiple Tx + * can only be there if we support aggregation. In that case + * there may be multiple slave or masters handing different + * channels of same Tx stream. + */ + else if (sdw_rt->tx_ref_count && stream_config->direction == + SDW_DATA_DIR_OUT) { + if (sdw_rt->stream_params.rate != + stream_config->frame_rate) { + dev_err(&mstr->dev, "Frame rate for aggregated devices not matching\n"); + ret = -EINVAL; + goto free_mem; + } + if (sdw_rt->stream_params.bps != stream_config->bps) { + dev_err(&mstr->dev, "bps for aggregated devices not matching\n"); + ret = -EINVAL; + goto free_mem; + } + /* Number of channels gets added, since both devices will + * be supporting different channels. Like one Codec + * supporting L and other supporting R channel. + */ + sdw_rt->stream_params.channel_count += + stream_config->channel_count; + sdw_rt->tx_ref_count++; + } else + sdw_rt->rx_ref_count++; + + /* SRK: check with hardik */ + sdw_rt->type = stream_config->type; + sdw_rt->stream_state = SDW_STATE_CONFIG_STREAM; + + /* Slaves are added to two list, This is because BW is calculated + * for two masters individually, while Ports are enabled of all + * the aggregated masters and slaves part of the same stream tag + * simultaneously. + */ + if (slave) { + list_add_tail(&slv_rt->slave_sdw_node, &sdw_rt->slv_rt_list); + list_add_tail(&slv_rt->slave_node, &mstr_rt->slv_rt_list); + } + mutex_unlock(&stream->stream_lock); + if (slave) + pm_runtime_get_sync(&slave->dev); + else + pm_runtime_get_sync(&mstr->dev); + return ret; + +free_mem: + mutex_unlock(&stream->stream_lock); + kfree(mstr_rt); + kfree(slv_rt); +out: + return ret; + +} +EXPORT_SYMBOL_GPL(sdw_config_stream); + +static int sdw_mstr_port_configuration(struct sdw_master *mstr, + struct sdw_runtime *sdw_rt, + struct sdw_port_config *port_config) +{ + struct sdw_mstr_runtime *mstr_rt; + struct sdw_port_runtime *port_rt; + int found = 0; + int i; + + list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt->mstr == mstr) { + found = 1; + break; + } + } + if (!found) { + dev_err(&mstr->dev, "Master not found for this port\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * + port_config->num_ports, GFP_KERNEL); + if (!port_rt) + return -EINVAL; + for (i = 0; i < port_config->num_ports; i++) { + port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; + port_rt[i].port_num = port_config->port_cfg[i].port_num; + list_add_tail(&port_rt[i].port_node, &mstr_rt->port_rt_list); + } + return 0; +} + +static int sdw_slv_port_configuration(struct sdw_slv *slave, + struct sdw_runtime *sdw_rt, + struct sdw_port_config *port_config) +{ + struct sdw_slave_runtime *slv_rt; + struct sdw_port_runtime *port_rt; + int found = 0; + int i; + + list_for_each_entry(slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { + if (slv_rt->slave == slave) { + found = 1; + break; + } + } + if (!found) { + dev_err(&slave->mstr->dev, "Slave not found for this port\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * + port_config->num_ports, GFP_KERNEL); + if (!port_rt) + return -EINVAL; + + for (i = 0; i < port_config->num_ports; i++) { + port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; + port_rt[i].port_num = port_config->port_cfg[i].port_num; + list_add_tail(&port_rt[i].port_node, &slv_rt->port_rt_list); + } + return 0; +} + +/** + * sdw_config_port: Port configuration for the SoundWire. Multiple + * soundWire ports may form single stream. Like two + * ports each transferring/receiving mono channels + * forms single stream with stereo channels. + * There will be single ASoC DAI representing + * the both ports. So stream configuration will be + * stereo, but both of the ports will be configured + * for mono channels, each with different channel + * mask. This is used to program port w.r.t to stream. + * params. So no need to de-configure, since these + * are automatically destroyed once stream gets + * destroyed. + * @mstr: Master handle where the slave is connected. + * @slave: Slave handle. + * @port_config: Port configuration for each port of soundwire slave. + * @stream_tag: Stream tag, where this port is connected. + * + */ +int sdw_config_port(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_port_config *port_config, + unsigned int stream_tag) +{ + int ret = 0; + int i; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_stream_tag *stream = NULL; + + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + stream = &stream_tags[i]; + break; + } + } + if (!sdw_rt) { + dev_err(&mstr->dev, "Invalid stream tag\n"); + return -EINVAL; + } + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < port_config->num_ports; i++) { + trace_sdw_config_port(mstr, slave, + &port_config->port_cfg[i], stream_tag); + } + } + mutex_lock(&stream->stream_lock); + if (!slave) + ret = sdw_mstr_port_configuration(mstr, sdw_rt, port_config); + else + ret = sdw_slv_port_configuration(slave, sdw_rt, port_config); + + mutex_unlock(&stream->stream_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_config_port); + +int sdw_prepare_and_enable(int stream_tag, bool enable) +{ + + int i, ret = 0; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + /* TBD: SRK, Check with hardik whether both locks needed + * stream and core?? + */ + mutex_lock(&sdw_core.core_lock); + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream = &stream_tags[i]; + break; + } + } + if (stream == NULL) { + mutex_unlock(&sdw_core.core_lock); + WARN_ON(1); /* Return from here after unlocking core*/ + return -EINVAL; + } + mutex_lock(&stream->stream_lock); + ret = sdw_bus_calc_bw(&stream_tags[i], enable); + if (ret) + pr_err("Bandwidth allocation failed\n"); + + mutex_unlock(&stream->stream_lock); + mutex_unlock(&sdw_core.core_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_prepare_and_enable); + +int sdw_disable_and_unprepare(int stream_tag, bool unprepare) +{ + int i, ret = 0; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + mutex_lock(&sdw_core.core_lock); + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream = &stream_tags[i]; + break; + } + } + if (stream == NULL) { + mutex_unlock(&sdw_core.core_lock); + WARN_ON(1); /* Return from here after unlocking core*/ + return -EINVAL; + } + mutex_lock(&stream->stream_lock); + ret = sdw_bus_calc_bw_dis(&stream_tags[i], unprepare); + if (ret) + pr_err("Bandwidth de-allocation failed\n"); + + mutex_unlock(&stream->stream_lock); + + mutex_unlock(&sdw_core.core_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_disable_and_unprepare); + +int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode) +{ + int ret = 0, i; + struct sdw_msg msg; + u8 buf[1] = {0}; + int slave_present = 0; + + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned && + mstr->sdw_addr[i].status != + SDW_SLAVE_STAT_NOT_PRESENT) + slave_present = 1; + } + + /* Send Broadcast message to the SCP_ctrl register with + * clock stop now + */ + msg.ssp_tag = 1; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_CTRL; + msg.len = 1; + buf[0] |= 0x1 << SDW_SCP_CTRL_CLK_STP_NOW_SHIFT; + msg.buf = buf; + msg.slave_addr = 15; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1 && slave_present) { + dev_err(&mstr->dev, "Failed to stop clk\n"); + return -EBUSY; + } + /* If we are entering clock stop mode1, mark all the slaves un-attached. + */ + if (mode == SDW_CLOCK_STOP_MODE_1) { + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned) + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_stop_clock); + +int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, + struct sdw_slv *slave) +{ + int timeout = 0; + + /* Wait till device gets enumerated. Wait for 2Secs before + * giving up + */ + do { + msleep(100); + timeout++; + } while ((slave->slv_addr->status == SDW_SLAVE_STAT_NOT_PRESENT) && + timeout < 20); + + if (slave->slv_addr->status == SDW_SLAVE_STAT_NOT_PRESENT) + return -EBUSY; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_wait_for_slave_enumeration); + +int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool stop, + enum sdw_clk_stop_mode *clck_stop_mode) +{ + int i; + struct sdw_msg msg; + u8 buf[1] = {0}; + struct sdw_slave *slave; + enum sdw_clk_stop_mode clock_stop_mode; + int timeout = 0; + int ret = 0; + int slave_dev_present = 0; + + /* Find if all slave support clock stop mode1 if all slaves support + * clock stop mode1 use mode1 else use mode0 + */ + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned && + mstr->sdw_addr[i].status != SDW_SLAVE_STAT_NOT_PRESENT) { + slave_dev_present = 1; + slave = mstr->sdw_addr[i].slave; + clock_stop_mode &= + slave->sdw_slv_cap.clock_stop1_mode_supported; + if (!clock_stop_mode) + break; + } + } + if (stop) { + *clck_stop_mode = clock_stop_mode; + dev_info(&mstr->dev, "Entering Clock stop mode %x\n", + clock_stop_mode); + } + /* Slaves might have removed power during its suspend + * in that case no need to do clock stop prepare + * and return from here + */ + if (!slave_dev_present) + return 0; + /* Prepare for the clock stop mode. For simplified clock stop + * prepare only mode is to be set, For others set the ClockStop + * Prepare bit in SCP_SystemCtrl register. For all the other slaves + * set the clock stop prepare bit. For all slave set the clock + * stop mode based on what we got in earlier loop + */ + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned != true) + continue; + if (mstr->sdw_addr[i].status == SDW_SLAVE_STAT_NOT_PRESENT) + continue; + slave = mstr->sdw_addr[i].slave; + msg.ssp_tag = 0; + slave = mstr->sdw_addr[i].slave; + + if (stop) { + /* Even if its simplified clock stop prepare + * setting prepare bit wont harm + */ + buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); + buf[0] |= clock_stop_mode << + SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; + } + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_SYSTEMCTRL; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = i; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "Clock Stop prepare failed\n"); + return -EBUSY; + } + } + /* + * Once clock stop prepare bit is set, broadcast the message to read + * ClockStop_NotFinished bit from SCP_Stat, till we read it as 11 + * we dont exit loop. We wait for definite time before retrying + * if its simple clock stop it will be always 1, while for other + * they will driver 0 on bus so we wont get 1. In total we are + * waiting 1 sec before we timeout. + */ + do { + buf[0] = 0xFF; + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.addr = SDW_SCP_STAT; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = 15; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1) + goto prepare_failed; + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + break; + msleep(100); + timeout++; + } while (timeout != 11); + /* If we are trying to stop and prepare failed its not ok + */ + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) { + dev_info(&mstr->dev, "Clock stop prepare done\n"); + return 0; + /* If we are trying to resume and un-prepare failes its ok + * since codec might be down during suspned and will + * start afresh after resuming + */ + } else if (!stop) { + dev_info(&mstr->dev, "Some Slaves un-prepare un-successful\n"); + return 0; + } + +prepare_failed: + dev_err(&mstr->dev, "Clock Stop prepare failed\n"); + return -EBUSY; + +} +EXPORT_SYMBOL_GPL(sdw_prepare_for_clock_change); + +struct sdw_master *sdw_get_master(int nr) +{ + struct sdw_master *master; + + mutex_lock(&sdw_core.core_lock); + master = idr_find(&sdw_core.idr, nr); + if (master && !try_module_get(master->owner)) + master = NULL; + mutex_unlock(&sdw_core.core_lock); + + return master; +} +EXPORT_SYMBOL_GPL(sdw_get_master); + +void sdw_put_master(struct sdw_master *mstr) +{ + if (mstr) + module_put(mstr->owner); +} +EXPORT_SYMBOL_GPL(sdw_put_master); + +static void sdw_exit(void) +{ + device_unregister(&sdw_slv); + bus_unregister(&sdwint_bus_type); +} + +static int sdw_init(void) +{ + int retval; + int i; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) + sdw_core.stream_tags[i].stream_tag = i; + mutex_init(&sdw_core.core_lock); + INIT_LIST_HEAD(&sdw_core.bus_list); + idr_init(&sdw_core.idr); + retval = bus_register(&sdwint_bus_type); + + if (!retval) + retval = device_register(&sdw_slv); + + + if (retval) + bus_unregister(&sdwint_bus_type); + + retval = sdw_bus_bw_init(); + if (retval) { + device_unregister(&sdw_slv); + bus_unregister(&sdwint_bus_type); + } + + return retval; +} +postcore_initcall(sdw_init); +module_exit(sdw_exit); + +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_DESCRIPTION("SoundWire bus driver"); +MODULE_ALIAS("platform:soundwire"); diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c new file mode 100644 index 000000000000..9c1ebc3297d2 --- /dev/null +++ b/drivers/sdw/sdw_bwcalc.c @@ -0,0 +1,2366 @@ +/* + * sdw_bwcalc.c - SoundWire Bus BW calculation & CHN Enabling implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Sanyog Kale + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include "sdw_priv.h" +#include +#include + + +#define MAXCLOCKFREQ 6 + +#define MAXCLOCKFREQ 6 + +/* TBD: Currently we are using 100x2 as frame shape. to be removed later */ +int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; + +int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; + +/* + * TBD: Get supported clock frequency from ACPI and store + * it in master data structure. + */ +/* Currently only 9.6MHz clock frequency used */ +int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, + 9600000, 9600000, + 9600000, 9600000}; + + +struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { + {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, +}; + +struct sdw_num_to_row sdw_num_row_mapping[MAX_NUM_ROWS] = { + {0, 48}, {1, 50}, {2, 60}, {3, 64}, {4, 75}, {5, 80}, {6, 125}, + {7, 147}, {8, 96}, {9, 100}, {10, 120}, {11, 128}, {12, 150}, + {13, 160}, {14, 250}, {16, 192}, {17, 200}, {18, 240}, {19, 256}, + {20, 72}, {21, 144}, {22, 90}, {23, 180}, +}; + +/** + * sdw_bus_bw_init - returns Success + * + * + * This function is called from sdw_init function when bus driver + * gets intitalized. This function performs all the generic + * intializations required for BW control. + */ +int sdw_bus_bw_init(void) +{ + int r, c, rowcolcount = 0; + int control_bits = 48; + + for (c = 0; c < MAX_NUM_COLS; c++) { + + for (r = 0; r < MAX_NUM_ROWS; r++) { + sdw_core.rowcolcomb[rowcolcount].col = cols[c]; + sdw_core.rowcolcomb[rowcolcount].row = rows[r]; + sdw_core.rowcolcomb[rowcolcount].control_bits = + control_bits; + sdw_core.rowcolcomb[rowcolcount].data_bits = + (cols[c] * rows[r]) - control_bits; + rowcolcount++; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_bw_init); + + +/** + * sdw_mstr_bw_init - returns Success + * + * + * This function is called from sdw_register_master function + * for each master controller gets register. This function performs + * all the intializations per master controller required for BW control. + */ +int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) +{ + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + + /* Initialize required parameters in bus structure */ + sdw_bs->bandwidth = 0; + sdw_bs->system_interval = 0; + sdw_bs->frame_freq = 0; + /* TBD: Base Clock frequency should be read from + * master capabilities + * Currenly hardcoding to 9.6MHz + */ + sdw_bs->clk_freq = 9.6*1000*1000; + sdw_bs->clk_state = SDW_CLK_STATE_ON; + + /* TBD: to be removed later */ + /* Assumption is these should be already filled */ + sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + sdw_mstr_cap->monitor_handover_supported = false; + sdw_mstr_cap->highphy_capable = false; + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); + + +/** + * sdw_get_col_to_num + * + * Returns column number from the mapping. + */ +int sdw_get_col_to_num(int col) +{ + int i; + + for (i = 0; i < MAX_NUM_COLS; i++) { + if (sdw_num_col_mapping[i].col == col) + return sdw_num_col_mapping[i].num; + } + + return 0; /* Lowest Column number = 2 */ +} + + +/** + * sdw_get_row_to_num + * + * Returns row number from the mapping. + */ +int sdw_get_row_to_num(int row) +{ + int i; + + for (i = 0; i < MAX_NUM_ROWS; i++) { + if (sdw_num_row_mapping[i].row == row) + return sdw_num_row_mapping[i].num; + } + + return 0; /* Lowest Row number = 48 */ +} + +/* + * sdw_lcm - returns LCM of two numbers + * + * + * This function is called BW calculation function to find LCM + * of two numbers. + */ +int sdw_lcm(int num1, int num2) +{ + int max; + + /* maximum value is stored in variable max */ + max = (num1 > num2) ? num1 : num2; + + while (1) { + if (max%num1 == 0 && max%num2 == 0) + break; + ++max; + } + + return max; +} + + +/* + * sdw_cfg_slv_params - returns Success + * -EINVAL - In case of error. + * + * + * This function configures slave registers for + * transport and port parameters. + */ +int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt, + struct sdw_transport_params *t_slv_params, + struct sdw_port_params *p_slv_params) +{ + struct sdw_msg wr_msg, wr_msg1, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + u8 wbuf1[2] = {0, 0}; + u8 rbuf[1] = {0}; + u8 rbuf1[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + u8 rbuf2[2] = {0, 0}; + + /* Program slave alternate bank with all transport parameters */ + /* DPN_BlockCtrl2 */ + wbuf[0] = t_slv_params->blockgroupcontrol; + /* DPN_SampleCtrl1 */ + wbuf[1] = (t_slv_params->sample_interval - 1) & + SDW_DPN_SAMPLECTRL1_LOW_MASK; + wbuf[2] = ((t_slv_params->sample_interval - 1) >> 8) & + SDW_DPN_SAMPLECTRL1_LOW_MASK; /* DPN_SampleCtrl2 */ + wbuf[3] = t_slv_params->offset1; /* DPN_OffsetCtrl1 */ + wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl1 */ + /* DPN_HCtrl */ + wbuf[5] = (t_slv_params->hstop | (t_slv_params->hstart << 4)); + wbuf[6] = t_slv_params->blockpackingmode; /* DPN_BlockCtrl3 */ + wbuf[7] = t_slv_params->lanecontrol; /* DPN_LaneCtrl */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + /* Program slave alternate bank with all port parameters */ + rd_msg.addr = SDW_DPN_PORTCTRL + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + + wbuf1[0] = (p_slv_params->port_flow_mode | + (p_slv_params->port_data_mode << + SDW_DPN_PORTCTRL_PORTDATAMODE_SHIFT) | + (rbuf[0])); + + wbuf1[1] = (p_slv_params->word_length - 1); + + /* Check whether address computed is correct for both cases */ + wr_msg.addr = ((SDW_DPN_BLOCKCTRL2 + + (1 * (!t_slv_params->blockgroupcontrol_valid)) + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num)); + + wr_msg1.addr = SDW_DPN_PORTCTRL + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num); + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); + wr_msg.slave_addr = slv_rt->slave->slv_number; + wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + wr_msg1.ssp_tag = 0x0; + wr_msg1.flag = SDW_MSG_FLAG_WRITE; + wr_msg1.len = 2; + wr_msg1.slave_addr = slv_rt->slave->slv_number; + wr_msg1.buf = &wbuf1[0]; + wr_msg1.addr_page1 = 0x0; + wr_msg1.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + +out: + return ret; +} + + +/* + * sdw_cfg_mstr_params - returns Success + * -EINVAL - In case of error. + * + * + * This function configures master registers for + * transport and port parameters. + */ +int sdw_cfg_mstr_params(struct sdw_bus *mstr_bs, + struct sdw_transport_params *t_mstr_params, + struct sdw_port_params *p_mstr_params) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + int banktouse, ret = 0; + + /* 1. Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* 2. Set Master Xport Params */ + if (ops->mstr_port_ops->dpn_set_port_transport_params) { + ret = ops->mstr_port_ops->dpn_set_port_transport_params + (mstr_bs->mstr, t_mstr_params, banktouse); + if (ret < 0) + return ret; + } + + /* 3. Set Master Port Params */ + if (ops->mstr_port_ops->dpn_set_port_params) { + ret = ops->mstr_port_ops->dpn_set_port_params + (mstr_bs->mstr, p_mstr_params, banktouse); + if (ret < 0) + return ret; + } + + return 0; +} + + +/* + * sdw_cfg_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave transport/port + * params configuration API's, called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis API's. + */ +int sdw_cfg_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool is_master) +{ + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_port_params *p_params, *p_slv_params; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int ret = 0; + + if (is_master) { + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + return 0; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + /* Transport and port parameters */ + t_params = &port_rt->transport_params; + p_params = &port_rt->port_params; + + p_params->num = port_rt->port_num; + p_params->word_length = + sdw_mstr_bs_rt->stream_params.bps; + p_params->port_flow_mode = 0x0; /* Isochronous Mode */ + p_params->port_data_mode = 0x0; /* Normal Mode */ + + /* Configure xport params and port params for master */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, + t_params, p_params); + if (ret < 0) + return ret; + + /* Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + + break; + } + + } else { + + + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + /* should not compute any transport params */ + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + /* Fill in port params here */ + port_slv_rt->port_params.num = + port_slv_rt->port_num; + port_slv_rt->port_params.word_length = + slv_rt->stream_params.bps; + /* Isochronous Mode */ + port_slv_rt->port_params.port_flow_mode = 0x0; + /* Normal Mode */ + port_slv_rt->port_params.port_data_mode = 0x0; + t_slv_params = &port_slv_rt->transport_params; + p_slv_params = &port_slv_rt->port_params; + + /* Configure xport & port params for slave */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, + slv_rt, t_slv_params, p_slv_params); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + + break; + } + } + + } + + return 0; +} + + +/* + * sdw_cpy_params_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function copies/configure master/slave transport & + * port params to alternate bank. + * + */ +int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt) +{ + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_port_params *p_params, *p_slv_params; + int ret = 0; + + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + /* Fill in port params here */ + port_slv_rt->port_params.num = port_slv_rt->port_num; + port_slv_rt->port_params.word_length = + slv_rt->stream_params.bps; + /* Normal/Isochronous Mode */ + port_slv_rt->port_params.port_flow_mode = 0x0; + /* Normal Mode */ + port_slv_rt->port_params.port_data_mode = 0x0; + t_slv_params = &port_slv_rt->transport_params; + p_slv_params = &port_slv_rt->port_params; + + /* Configure xport & port params for slave */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, + slv_rt, t_slv_params, p_slv_params); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + } + + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + /* Transport and port parameters */ + t_params = &port_rt->transport_params; + p_params = &port_rt->port_params; + + + p_params->num = port_rt->port_num; + p_params->word_length = sdw_mstr_bs_rt->stream_params.bps; + p_params->port_flow_mode = 0x0; /* Normal/Isochronous Mode */ + p_params->port_data_mode = 0x0; /* Normal Mode */ + + /* Configure xport params and port params for master */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, t_params, p_params); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + return 0; +} + + +/* + * sdw_cfg_slv_enable_disable - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable slave port channels. + */ +int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt_strm, + struct sdw_port_runtime *port_slv_strm, + struct port_chn_en_state *chn_en) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + if ((chn_en->is_activate) || (chn_en->is_bank_sw)) + banktouse = !banktouse; + + rd_msg.addr = wr_msg.addr = ((SDW_DPN_CHANNELEN + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * + port_slv_strm->port_num)); + + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_rt_strm->slave->slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + if (chn_en->is_activate) { + + /* + * 1. slave port enable_ch_pre + * --> callback + * --> no callback available + */ + + /* 2. slave port enable */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] | port_slv_strm->channel_mask); + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + rbuf[0] = 0; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * 3. slave port enable post pre + * --> callback + * --> no callback available + */ + slv_rt_strm->rt_state = SDW_STATE_ENABLE_RT; + + } else { + + /* + * 1. slave port enable_ch_unpre + * --> callback + * --> no callback available + */ + + /* 2. slave port disable */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] & ~(port_slv_strm->channel_mask)); + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + rbuf[0] = 0; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * 3. slave port enable post unpre + * --> callback + * --> no callback available + */ + if (!chn_en->is_bank_sw) + slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; + + } + +out: + return ret; + +} + + +/* + * sdw_cfg_mstr_activate_disable - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable master port channels. + */ +int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, + struct sdw_mstr_runtime *mstr_rt_strm, + struct sdw_port_runtime *port_mstr_strm, + struct port_chn_en_state *chn_en) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_activate_ch activate_ch; + int banktouse, ret = 0; + + activate_ch.num = port_mstr_strm->port_num; + activate_ch.ch_mask = port_mstr_strm->channel_mask; + activate_ch.activate = chn_en->is_activate; /* Enable/Disable */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + if ((chn_en->is_activate) || (chn_en->is_bank_sw)) + banktouse = !banktouse; + + + /* 1. Master port enable_ch_pre */ + if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_pre + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + /* 2. Master port enable */ + if (ops->mstr_port_ops->dpn_port_activate_ch) { + ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, + &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + /* 3. Master port enable_ch_post */ + if (ops->mstr_port_ops->dpn_port_activate_ch_post) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_post + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + if (chn_en->is_activate) + mstr_rt_strm->rt_state = SDW_STATE_ENABLE_RT; + else if (!chn_en->is_bank_sw) + mstr_rt_strm->rt_state = SDW_STATE_DISABLE_RT; + + return 0; +} + + +/* + * sdw_en_dis_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave enable/disable + * channel API's. + */ +int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, bool is_act) +{ + struct sdw_slave_runtime *slv_rt_strm = NULL; + struct sdw_port_runtime *port_slv_strm, *port_mstr_strm; + struct sdw_mstr_runtime *mstr_rt_strm = NULL; + struct port_chn_en_state chn_en; + int ret = 0; + + if (is_act) + chn_en.is_bank_sw = true; + else + chn_en.is_bank_sw = false; + + chn_en.is_activate = is_act; + + list_for_each_entry(slv_rt_strm, &sdw_rt->slv_rt_list, slave_sdw_node) { + + if (slv_rt_strm->slave == NULL) + break; + + list_for_each_entry(port_slv_strm, + &slv_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_slv_enable_disable + (sdw_mstr_bs, slv_rt_strm, + port_slv_strm, &chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + + break; + + } + + list_for_each_entry(mstr_rt_strm, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_strm->mstr == NULL) + break; + + list_for_each_entry(port_mstr_strm, + &mstr_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_activate_disable + (sdw_mstr_bs, mstr_rt_strm, + port_mstr_strm, &chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + + } + + return 0; +} + + +/* + * sdw_en_dis_mstr_slv_state - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave enable/disable + * channel API's based on runtime state. + */ +int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + struct port_chn_en_state *chn_en) +{ + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_slv_rt, *port_rt; + int ret = 0; + + list_for_each_entry(slv_rt, &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_ENABLE_RT) { + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + ret = sdw_cfg_slv_enable_disable + (sdw_mstr_bs, slv_rt, + port_slv_rt, chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + break; + } + } + } + + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_ENABLE_RT) { + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_activate_disable + (sdw_mstr_bs, sdw_mstr_bs_rt, port_rt, chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + + break; + } + } + + return 0; +} + + +/* + * sdw_get_clock_frmshp - returns Success + * -EINVAL - In case of error. + * + * + * This function computes clock and frame shape based on + * clock frequency. + */ +int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, + int *col, int *row) +{ + int i, rc, clock_reqd = 0, frame_interval = 0, frame_frequency = 0; + int sel_row = 0, sel_col = 0; + bool clock_ok = false; + + /* + * Find nearest clock frequency needed by master for + * given bandwidth + */ + + /* + * TBD: Need to run efficient algorithm to make sure we have + * only 1 to 10 percent of control bandwidth usage + */ + for (i = 0; i < MAXCLOCKFREQ; i++) { + + /* TBD: Check why 3000 */ + if ((clock_freq[i] <= sdw_mstr_bs->bandwidth) || + ((clock_freq[i] % 3000) != 0)) + continue; + clock_reqd = clock_freq[i]; + + /* + * TBD: Check all the slave device capabilities + * here and find whether given frequency is + * supported by all slaves + */ + + /* Find frame shape based on bandwidth per controller */ + /* + * TBD: Need to run efficient algorithm to make sure we have + * only 1 to 10 percent of control bandwidth usage + */ + for (rc = 0; rc <= MAX_NUM_ROW_COLS; rc++) { + frame_interval = + sdw_core.rowcolcomb[rc].row * + sdw_core.rowcolcomb[rc].col; + frame_frequency = clock_reqd/frame_interval; + + if ((clock_reqd - + (frame_frequency * + sdw_core.rowcolcomb[rc]. + control_bits)) < + sdw_mstr_bs->bandwidth) + continue; + + break; + } + + sel_row = sdw_core.rowcolcomb[rc].row; + sel_col = sdw_core.rowcolcomb[rc].col; + sdw_mstr_bs->frame_freq = frame_frequency; + sdw_mstr_bs->clk_freq = clock_reqd; + clock_ok = false; + *frame_int = frame_interval; + *col = sel_col; + *row = sel_row; + sdw_mstr_bs->col = sel_col; + sdw_mstr_bs->row = sel_row; + + break; + + } + + return 0; +} + +/* + * sdw_compute_sys_interval - returns Success + * -EINVAL - In case of error. + * + * + * This function computes system interval. + */ +int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, + struct sdw_master_capabilities *sdw_mstr_cap, + int frame_interval) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params; + struct sdw_port_runtime *port_rt; + int lcmnum1 = 0, lcmnum2 = 0, div = 0, lcm = 0; + + /* + * once you got bandwidth frame shape for bus, + * run a loop for all the active streams running + * on bus and compute sample_interval & other transport parameters. + */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + + /* + * Current Assumption: + * One port per bus runtime structure + */ + /* Calculate sample interval */ + t_params->sample_interval = + ((sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate) * 2); + + /* Only BlockPerPort supported */ + t_params->blockpackingmode = 0; + t_params->lanecontrol = 0; + + /* Calculate LCM */ + lcmnum2 = t_params->sample_interval; + if (!lcmnum1) + lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); + else + lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); + + /* + * Since one port per bus runtime, breaking + * port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + } + + + /* 6. compute system_interval */ + if ((sdw_mstr_cap) && (sdw_mstr_bs->clk_freq)) { + + div = ((sdw_mstr_cap->base_clk_freq * 2) / + sdw_mstr_bs->clk_freq); + lcm = sdw_lcm(lcmnum1, frame_interval); + sdw_mstr_bs->system_interval = (div * lcm); + + } + + /* + * Something went wrong, may be sdw_lcm value may be 0, + * return error accordingly + */ + if (!sdw_mstr_bs->system_interval) + return -EINVAL; + + + return 0; +} + + +/* + * sdw_compute_hstart_hstop - returns Success + * -EINVAL - In case of error. + * + * + * This function computes hstart and hstop for running + * streams per master & slaves. + */ +int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int hstop = 0, hwidth = 0; + int payload_bw = 0, full_bw = 0, column_needed = 0; + bool hstop_flag = false; + + /* Calculate hwidth, hstart and hstop */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + t_params->num = port_rt->port_num; + + /* + * 1. find full_bw and payload_bw per stream + * 2. find h_width per stream + * 3. find hstart, hstop, block_offset,sub_block_offset + * Note: full_bw is nothing but sampling interval + * of stream. + * payload_bw is serving size no. + * of channels * bps per stream + */ + full_bw = sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate; + payload_bw = + sdw_mstr_bs_rt->stream_params.bps * + sdw_mstr_bs_rt->stream_params.channel_count; + + hwidth = (sel_col * payload_bw + full_bw - 1)/full_bw; + column_needed += hwidth; + + /* + * These needs to be done only for + * 1st entry in link list + */ + if (!hstop_flag) { + hstop = sel_col - 1; + hstop_flag = true; + } + + /* Assumption: Only block per port is supported + * For blockperport: + * offset1 value = LSB 8 bits of block_offset value + * offset2 value = MSB 8 bits of block_offset value + * For blockperchannel: + * offset1 = LSB 8 bit of block_offset value + * offset2 = MSB 8 bit of sub_block_offload value + * if hstart and hstop of different streams in + * master are different, then block_offset is zero. + * if not then block_offset value for 2nd stream + * is block_offset += payload_bw + */ + + t_params->hstop = hstop; + t_params->hstart = hstop - hwidth + 1; + + + /* + * TBD: perform this when you have 2 ports + * and accordingly configure hstart hstop for slave + * removing for now + */ +#if 0 + hstop = hstop - hwidth; +#endif + /* Since one port per bus runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + /* + * Run loop for slave_rt_list for given master_list + * to compute hstart hstop for slave + */ + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + t_slv_params->num = port_slv_rt->port_num; + + /* + * TBD: Needs to be verifid for + * multiple combination + * 1. 1 master port, 1 slave rt, + * 1 port per slave rt --> + * In this case, use hstart hstop same as master + * for 1 slave rt + * 2. 1 master port, 2 slave rt, + * 1 port per slave rt --> + * In this case, use hstart hstop same as master + * for 2 slave rt + * only offset will change for 2nd slave rt + * Current assumption is one port per rt, + * hence no multiple port combination + * considered. + */ + t_slv_params->hstop = hstop; + t_slv_params->hstart = hstop - hwidth + 1; + + /* Only BlockPerPort supported */ + t_slv_params->blockpackingmode = 0; + t_slv_params->lanecontrol = 0; + + /* + * below copy needs to be changed when + * more than one port is supported + */ + if (t_params) + t_slv_params->sample_interval = + t_params->sample_interval; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + break; + } + + } + } + +#if 0 + /* TBD: To be verified */ + if (column_needed > sel_col - 1) + return -EINVAL; /* Error case, check what has gone wrong */ +#endif + + return 0; +} + + +/* + * sdw_compute_blk_subblk_offset - returns Success + * + * + * This function computes block offset and sub block + * offset for running streams per master & slaves. + */ +int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int hstart1 = 0, hstop1 = 0, hstart2 = 0, hstop2 = 0; + int block_offset = 1; + + + /* Calculate block_offset and subblock_offset */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + + + if ((!hstart2) && (!hstop2)) { + hstart1 = hstart2 = t_params->hstart; + hstop1 = hstop2 = t_params->hstop; + /* TBD: Verify this condition */ + block_offset = 0; + } else { + + hstart1 = t_params->hstart; + hstop1 = t_params->hstop; + + /* hstart/stop not same */ + if ((hstart1 != hstart2) && + (hstop1 != hstop2)) { + /* TBD: Harcoding to 0, to be removed*/ + block_offset = 0; + } else { + /* TBD: Harcoding to 0, to be removed*/ + block_offset = 0; + } + +#if 0 + if ((hstart1 != hstart2) && + (hstop1 != hstop2)) { + block_offset = 1; + } else { + block_offset += + (sdw_mstr_bs_rt->stream_params. + bps + * + sdw_mstr_bs_rt->stream_params. + channel_count); + } +#endif + + } + + + /* + * TBD: Hardcding block control group as true, + * to be changed later + */ + t_params->blockgroupcontrol_valid = true; + t_params->blockgroupcontrol = 0x0; /* Hardcoding to 0 */ + + /* + * Since one port per bus runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + /* + * Run loop for slave_rt_list for given master_list + * to compute block and sub block offset for slave + */ + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + + /* + * TBD: Needs to be verifid for + * multiple combination + * 1. 1 master port, 1 slave rt, + * 1 port per slave rt --> + * In this case, use block_offset same as + * master for 1 slave rt + * 2. 1 master port, 2 slave rt, + * 1 port per slave rt --> + * In this case, use block_offset same as + * master for 1st slave rt and compute for 2nd. + */ + + /* + * Current assumption is one port per rt, + * hence no multiple port combination. + * TBD: block offset to be computed for + * more than 1 slave_rt list. + */ + t_slv_params->offset1 = block_offset; + t_slv_params->offset2 = block_offset >> 8; + + + /* + * TBD: Hardcding block control group as true, + * to be changed later + */ + t_slv_params->blockgroupcontrol_valid = true; + /* Hardcoding to 0 */ + t_slv_params->blockgroupcontrol = 0x0; + /* Since one port per slave runtime, + * breaking port_list loop + * TBD:to be extended for multiple port support + */ + break; + } + } + } + + return 0; +} + + +/* + * sdw_configure_frmshp_bnkswtch - returns Success + * -EINVAL - In case of error. + * + * + * This function broadcast frameshape on framectrl + * register and performs bank switch. + */ +int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) +{ + struct sdw_msg wr_msg; + int ret = 0; + int banktouse, numcol, numrow; + u8 wbuf[1] = {0}; + + numcol = sdw_get_col_to_num(col); + numrow = sdw_get_row_to_num(row); + + wbuf[0] = numcol | (numrow << 3); + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + if (banktouse) { + wr_msg.addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } else { + + wr_msg.addr = SDW_SCP_FRAMECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } + + wr_msg.ssp_tag = 0x1; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = 0xF; /* Broadcast address*/ + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + msleep(100); /* TBD: Remove this */ + + /* + * TBD: check whether we need to poll on + * mcp active bank bit to switch bank + */ + mstr_bs->active_bank = banktouse; + +out: + + return ret; +} + + +/* + * sdw_cfg_bs_params - returns Success + * -EINVAL - In case of error. + * + * + * This function performs master/slave transport + * params config, set SSP interval, set Clock + * frequency, enable channel. This API is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * + */ +int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool is_strm_cpy) +{ + struct port_chn_en_state chn_en; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_driver *ops; + int banktouse, ret = 0; + + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + if (is_strm_cpy) { + /* + * Configure and enable all slave + * transport params first + */ + ret = sdw_cfg_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave config params failed\n"); + return ret; + } + + /* Configure and enable all master params */ + ret = sdw_cfg_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "master config params failed\n"); + return ret; + } + + } else { + + /* + * 7.1 Copy all slave transport and port params + * to alternate bank + * 7.2 copy all master transport and port params + * to alternate bank + */ + ret = sdw_cpy_params_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave/master copy params failed\n"); + return ret; + } + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* Configure SSP */ + banktouse = sdw_mstr_bs->active_bank; + banktouse = !banktouse; + + /* + * TBD: Currently harcoded SSP interval to 24, + * computed value to be taken from system_interval in + * bus data structure. + * Add error check. + */ + if (ops->mstr_ops->set_ssp_interval) + ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, + 24, banktouse); /* hardcoding to 24 */ + /* + * Configure Clock + * TBD: Add error check + */ + if (ops->mstr_ops->set_clock_freq) + ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, + sdw_mstr_bs->clk_freq, banktouse); + + /* Enable channel on alternate bank for running streams */ + chn_en.is_activate = true; + chn_en.is_bank_sw = true; + ret = sdw_en_dis_mstr_slv_state + (sdw_mstr_bs, sdw_mstr_bs_rt, &chn_en); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel enable failed\n"); + return ret; + } + + } + + return 0; +} + +/* + * sdw_dis_chan - returns Success + * -EINVAL - In case of error. + * + * + * This function disables channel on alternate + * bank. This API is called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis when channel on current + * bank is enabled. + * + */ +int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct port_chn_en_state chn_en; + int ret = 0; + + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + chn_en.is_activate = false; + chn_en.is_bank_sw = true; + ret = sdw_en_dis_mstr_slv_state(sdw_mstr_bs, + sdw_mstr_bs_rt, &chn_en); + if (ret < 0) + return ret; + } + + return 0; +} + + +/* + * sdw_cfg_slv_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare slave ports. + */ +int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt_strm, + struct sdw_port_runtime *port_slv_strm, + bool prep) +{ + struct sdw_slave_driver *slv_ops = slv_rt_strm->slave->driver; + struct sdw_slv_capabilities *slv_cap = + &slv_rt_strm->slave->sdw_slv_cap; + struct sdw_slv_dpn_capabilities *sdw_slv_dpn_cap = + slv_cap->sdw_dpn_cap; + + struct sdw_msg wr_msg, rd_msg, rd_msg1; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + u8 rbuf1[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* Read SDW_DPN_PREPARECTRL register */ + rd_msg.addr = wr_msg.addr = SDW_DPN_PREPARECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * port_slv_strm->port_num); + + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + rd_msg1.ssp_tag = 0x0; + rd_msg1.flag = SDW_MSG_FLAG_READ; + rd_msg1.len = 1; + rd_msg1.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg1.buf = rbuf1; + rd_msg1.addr_page1 = 0x0; + rd_msg1.addr_page2 = 0x0; + + + rd_msg1.addr = SDW_DPN_PREPARESTATUS + + (SDW_NUM_DATA_PORT_REGISTERS * port_slv_strm->port_num); + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_rt_strm->slave->slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + if (prep) { /* PREPARE */ + + /* + * 1. slave port prepare_ch_pre + * --> callback + * --> handle_pre_port_prepare + */ + if (slv_ops->handle_pre_port_prepare) { + slv_ops->handle_pre_port_prepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + /* 2. slave port prepare --> to write */ + if (sdw_slv_dpn_cap->prepare_ch) { + + /* NON SIMPLIFIED CM, prepare required */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] | port_slv_strm->channel_mask); + + /* + * TBD: poll for prepare interrupt bit + * before calling post_prepare + * 2. check capabilities if simplified + * CM no need to prepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * TBD: check on port ready, + * ideally we should check on prepare + * status for port_ready + */ + + /* wait for completion on port ready*/ + msleep(100); /* TBD: Remove this */ + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + } + + /* + * 3. slave port post pre + * --> callback + * --> handle_post_port_prepare + */ + if (slv_ops->handle_post_port_prepare) { + slv_ops->handle_post_port_prepare + (slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, banktouse); + } + + slv_rt_strm->rt_state = SDW_STATE_PREPARE_RT; + + } else { + /* UNPREPARE */ + /* + * 1. slave port unprepare_ch_pre + * --> callback + * --> handle_pre_port_prepare + */ + if (slv_ops->handle_pre_port_unprepare) { + slv_ops->handle_pre_port_unprepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + /* 2. slave port unprepare --> to write */ + if (sdw_slv_dpn_cap->prepare_ch) { + + /* NON SIMPLIFIED CM, unprepare required */ + + /* Read SDW_DPN_PREPARECTRL register */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] & ~(port_slv_strm->channel_mask)); + + /* + * TBD: poll for prepare interrupt bit before + * calling post_prepare + * Does it apply for unprepare aswell? + * 2. check capabilities if simplified CM + * no need to unprepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + } + + /* + * 3. slave port post unpre + * --> callback + * --> handle_post_port_unprepare + */ + if (slv_ops->handle_post_port_unprepare) { + slv_ops->handle_post_port_unprepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + slv_rt_strm->rt_state = SDW_STATE_UNPREPARE_RT; + } +out: + return ret; + +} + + +/* + * sdw_cfg_mstr_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare master ports. + */ +int sdw_cfg_mstr_prep_unprep(struct sdw_bus *mstr_bs, + struct sdw_mstr_runtime *mstr_rt_strm, + struct sdw_port_runtime *port_mstr_strm, + bool prep) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_prepare_ch prep_ch; + int ret = 0; + + prep_ch.num = port_mstr_strm->port_num; + prep_ch.ch_mask = port_mstr_strm->channel_mask; + prep_ch.prepare = prep; /* Prepare/Unprepare */ + + /* TBD: Bank configuration */ + + /* 1. Master port prepare_ch_pre */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_pre + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 2. Master port prepare */ + if (ops->mstr_port_ops->dpn_port_prepare_ch) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 3. Master port prepare_ch_post */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_post) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_post + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + if (prep) + mstr_rt_strm->rt_state = SDW_STATE_PREPARE_RT; + else + mstr_rt_strm->rt_state = SDW_STATE_UNPREPARE_RT; + + return 0; +} + + +/* + * sdw_prep_unprep_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave prepare/unprepare + * port configuration API's, called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis API's. + */ +int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, bool is_prep) +{ + struct sdw_slave_runtime *slv_rt_strm = NULL; + struct sdw_port_runtime *port_slv_strm, *port_mstr_strm; + struct sdw_mstr_runtime *mstr_rt_strm = NULL; + int ret = 0; + + list_for_each_entry(slv_rt_strm, + &sdw_rt->slv_rt_list, slave_sdw_node) { + + if (slv_rt_strm->slave == NULL) + break; + + list_for_each_entry(port_slv_strm, + &slv_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_slv_prep_unprep(sdw_mstr_bs, + slv_rt_strm, port_slv_strm, is_prep); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + break; + } + + list_for_each_entry(mstr_rt_strm, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_strm->mstr == NULL) + break; + + list_for_each_entry(port_mstr_strm, + &mstr_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_prep_unprep(sdw_mstr_bs, + mstr_rt_strm, port_mstr_strm, is_prep); + if (ret < 0) + return ret; + + /* Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + } + + return 0; +} + + +/** + * sdw_bus_calc_bw - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_prepare_and_enable + * whenever new stream is processed. The function based + * on the stream associated with controller calculates + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch. + */ +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +{ + + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_stream_params *stream_params = &sdw_rt->stream_params; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + int stream_frame_size; + int frame_interval = 0, sel_row = 0, sel_col = 0; + int ret = 0; + + /* TBD: Add PCM/PDM flag in sdw_config_stream */ + + /* + * TBD: check for mstr_rt is in configured state or not + * If yes, then configure masters as well + * If no, then do not configure/enable master related parameters + */ + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (sdw_mstr_rt->mstr == NULL) + break; + + /* Get bus structure for master */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) + continue; + + + sdw_mstr = sdw_mstr_bs->mstr; + break; + } + + /* + * All data structures required available, + * lets calculate BW for master controller + */ + + /* Check for isochronous mode plus other checks if required */ + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((sdw_rt->stream_state == SDW_STATE_CONFIG_STREAM) || + (sdw_rt->stream_state == + SDW_STATE_UNPREPARE_STREAM)) { + + /* we do not support asynchronous mode Return Error */ + if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) + != 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Asynchronous mode not supported\n"); + return -EINVAL; + } + + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Sampling frequency mismatch\n"); + return -EINVAL; + } + + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; + + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, "clock/frameshape config failed\n"); + return ret; + } + + + /* + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. + */ + + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, + sdw_mstr_cap, frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, "compute system interval failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute hstart/hstop failed\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err( + &sdw_mstr_bs->mstr->dev, + "compute block offset failed\n"); + return ret; + } + + /* Change Stream State */ + sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + + /* Configure bus parameters */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel prepare failed\n"); + return ret; + } + + /* change stream state to prepare */ + sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + } + + if ((enable) && (SDW_STATE_PREPARE_STREAM + == sdw_rt->stream_state)) { + + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel enable failed\n"); + return ret; + } + + /* change stream state to enable */ + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled faile\n"); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); + + +/** + * sdw_bus_calc_bw_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_disable_and_unprepare + * whenever stream is ended. The function based disables/ + * unprepare port/channel of associated stream and computes + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch for remaining streams on given + * controller. + */ +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +{ + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + int stream_frame_size; + int frame_interval = 0, sel_row = 0, sel_col = 0; + int ret = 0; + + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (sdw_mstr_rt->mstr == NULL) + break; + + /* Get bus structure for master */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) + continue; + + + sdw_mstr = sdw_mstr_bs->mstr; + break; + } + + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if (sdw_rt->stream_state == SDW_STATE_ENABLE_STREAM) { + + /* Lets do disabling of port for stream to be freed */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + /* + * Disable channel for slave and + * master on current bank + */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel dis failed\n"); + return ret; + } + + /* Change stream state to disable */ + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; + } + + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure frame shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + } + + if ((unprepare) && + (SDW_STATE_DISABLE_STREAM == + sdw_rt->stream_state)) { + + /* 1. Un-prepare master and slave port */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Chan unprep failed\n"); + return ret; + } + + /* change stream state to unprepare */ + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; + } + + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; + + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; + + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, + "BW calculation failed\n"); + return -EINVAL; + } + + if (!sdw_mstr_bs->bandwidth) { + /* + * Last stream on master should + * return successfully + */ + sdw_rt->stream_state = + SDW_STATE_UNCOMPUTE_STREAM; + return 0; + } + + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "clock/frameshape failed\n"); + return ret; + } + + /* Compute new transport params for running streams */ + /* No sorting required here */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, + sdw_mstr_cap, frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute SI failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute hstart/hstop fail\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute block offset failed\n"); + return ret; + } + + /* Configure bus params */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Change stream state to uncompute */ + sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + } + + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c new file mode 100644 index 000000000000..3f3317a6707a --- /dev/null +++ b/drivers/sdw/sdw_cnl.c @@ -0,0 +1,1583 @@ +/* + * sdw_cnl.c - Intel SoundWire master controller driver implementation. + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdw_cnl_priv.h" + +static inline int cnl_sdw_reg_readl(void __iomem *base, int offset) +{ + int value; + + value = readl(base + offset); + return value; +} + +static inline void cnl_sdw_reg_writel(void __iomem *base, int offset, int value) +{ + writel(value, base + offset); +} + +static inline u16 cnl_sdw_reg_readw(void __iomem *base, int offset) +{ + int value; + + value = readw(base + offset); + return value; +} + +static inline void cnl_sdw_reg_writew(void __iomem *base, int offset, u16 value) +{ + writew(value, base + offset); +} + +static inline int cnl_sdw_port_reg_readl(void __iomem *base, int offset, + int port_num) +{ + return cnl_sdw_reg_readl(base, offset + port_num * 128); +} + +static inline void cnl_sdw_port_reg_writel(u32 __iomem *base, int offset, + int port_num, int value) +{ + return cnl_sdw_reg_writel(base, offset + port_num * 128, value); +} + +struct cnl_sdw { + struct cnl_sdw_data data; + struct sdw_master *mstr; + irqreturn_t (*thread)(int irq, void *context); + void *thread_context; + struct completion tx_complete; + struct cnl_sdw_port port[CNL_SDW_MAX_PORTS]; + int num_pcm_streams; + struct cnl_sdw_pdi_stream *pcm_streams; + int num_in_pcm_streams; + struct cnl_sdw_pdi_stream *in_pcm_streams; + int num_out_pcm_streams; + struct cnl_sdw_pdi_stream *out_pcm_streams; + int num_pdm_streams; + struct cnl_sdw_pdi_stream *pdm_streams; + int num_in_pdm_streams; + struct cnl_sdw_pdi_stream *in_pdm_streams; + int num_out_pdm_streams; + struct cnl_sdw_pdi_stream *out_pdm_streams; + struct mutex stream_lock; + spinlock_t ctrl_lock; + u32 response_buf[0x80]; + bool sdw_link_status; + +}; + +static int sdw_power_up_link(struct cnl_sdw *sdw) +{ + volatile int link_control; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + /* Try 10 times before timing out */ + int timeout = 10; + int spa_mask, cpa_mask; + + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + spa_mask = (CNL_LCTL_SPA_MASK << (data->inst_id + CNL_LCTL_SPA_SHIFT)); + cpa_mask = (CNL_LCTL_CPA_MASK << (data->inst_id + CNL_LCTL_CPA_SHIFT)); + link_control |= spa_mask; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_LCTL, link_control); + do { + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (link_control & cpa_mask) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + /* Read once again to confirm */ + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (link_control & cpa_mask) { + dev_info(&mstr->dev, "SoundWire ctrl %d Powered Up\n", + data->inst_id); + sdw->sdw_link_status = 1; + return 0; + } + dev_err(&mstr->dev, "Failed to Power Up the SDW ctrl %d\n", + data->inst_id); + return -EIO; +} + +static void sdw_power_down_link(struct cnl_sdw *sdw) +{ + volatile int link_control; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + /* Retry 10 times before giving up */ + int timeout = 10; + int spa_mask, cpa_mask; + + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + spa_mask = ~(CNL_LCTL_SPA_MASK << (data->inst_id + CNL_LCTL_SPA_SHIFT)); + cpa_mask = (CNL_LCTL_CPA_MASK << (data->inst_id + CNL_LCTL_CPA_SHIFT)); + link_control &= spa_mask; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_LCTL, link_control); + do { + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (!(link_control & cpa_mask)) + break; + timeout--; + /* Wait for 20ms before each retry */ + msleep(20); + } while (timeout != 0); + /* Read once again to confirm */ + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (!(link_control & cpa_mask)) { + dev_info(&mstr->dev, "SoundWire ctrl %d Powered Down\n", + data->inst_id); + sdw->sdw_link_status = 0; + return; + } + dev_err(&mstr->dev, "Failed to Power Down the SDW ctrl %d\n", + data->inst_id); +} + +static void sdw_init_phyctrl(struct cnl_sdw *sdw) +{ + /* TODO: Initialize based on hardware requirement */ + +} + +static void sdw_switch_to_mip(struct cnl_sdw *sdw) +{ + u16 ioctl; + u16 act = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + int act_offset = SDW_CNL_CTMCTL + (data->inst_id * + SDW_CNL_CTMCTL_REG_OFFSET); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + + ioctl &= ~(CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_MIF_MASK << CNL_IOCTL_MIF_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT); + ioctl &= ~(CNL_IOCTL_COE_MASK << CNL_IOCTL_COE_SHIFT); + + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + act |= 0x1 << CNL_CTMCTL_DOAIS_SHIFT; + act |= CNL_CTMCTL_DACTQE_MASK << CNL_CTMCTL_DACTQE_SHIFT; + act |= CNL_CTMCTL_DODS_MASK << CNL_CTMCTL_DODS_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, act_offset, act); +} + +static void sdw_switch_to_glue(struct cnl_sdw *sdw) +{ + u16 ioctl; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + ioctl |= CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT; + ioctl |= CNL_IOCTL_COE_MASK << CNL_IOCTL_COE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_MIF_MASK << CNL_IOCTL_MIF_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); +} + +static void sdw_init_shim(struct cnl_sdw *sdw) +{ + u16 ioctl = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + + ioctl |= CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_WPDD_MASK << CNL_IOCTL_WPDD_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); +} + +static int sdw_config_update(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + struct sdw_master *mstr = sdw->mstr; + + volatile int config_update = 0; + /* Try 10 times before giving up on configuration update */ + int timeout = 10; + int config_updated = 0; + + config_update |= MCP_CONFIGUPDATE_CONFIGUPDATE_MASK << + MCP_CONFIGUPDATE_CONFIGUPDATE_SHIFT; + /* Bit is self-cleared when configuration gets updated. */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE, + config_update); + do { + config_update = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONFIGUPDATE); + if ((config_update & + MCP_CONFIGUPDATE_CONFIGUPDATE_MASK) == 0) { + config_updated = 1; + break; + } + timeout--; + /* Wait for 20ms between each try */ + msleep(20); + + } while (timeout != 0); + if (!config_updated) { + dev_err(&mstr->dev, "SoundWire update failed\n"); + return -EIO; + } + return 0; +} + +static void sdw_enable_interrupt(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + int int_mask = 0; + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTMASK0, + MCP_SLAVEINTMASK0_MASK); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTMASK1, + MCP_SLAVEINTMASK1_MASK); + /* Enable slave interrupt mask */ + int_mask |= MCP_INTMASK_SLAVERESERVED_MASK << + MCP_INTMASK_SLAVERESERVED_SHIFT; + int_mask |= MCP_INTMASK_SLAVEALERT_MASK << + MCP_INTMASK_SLAVEALERT_SHIFT; + int_mask |= MCP_INTMASK_SLAVEATTACHED_MASK << + MCP_INTMASK_SLAVEATTACHED_SHIFT; + int_mask |= MCP_INTMASK_SLAVENOTATTACHED_MASK << + MCP_INTMASK_SLAVENOTATTACHED_SHIFT; + int_mask |= MCP_INTMASK_CONTROLBUSCLASH_MASK << + MCP_INTMASK_CONTROLBUSCLASH_SHIFT; + int_mask |= MCP_INTMASK_DATABUSCLASH_MASK << + MCP_INTMASK_DATABUSCLASH_SHIFT; + int_mask |= MCP_INTMASK_RXWL_MASK << + MCP_INTMASK_RXWL_SHIFT; + int_mask |= MCP_INTMASK_IRQEN_MASK << + MCP_INTMASK_IRQEN_SHIFT; + int_mask |= MCP_INTMASK_DPPDIINT_MASK << + MCP_INTMASK_DPPDIINT_SHIFT; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_INTMASK, int_mask); +} + +static int sdw_pcm_pdi_init(struct cnl_sdw *sdw) +{ + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int pcm_cap; + int pcm_cap_offset = SDW_CNL_PCMSCAP + (data->inst_id * + SDW_CNL_PCMSCAP_REG_OFFSET); + int ch_cnt_offset; + int i; + + pcm_cap = cnl_sdw_reg_readw(data->sdw_shim, pcm_cap_offset); + sdw->num_pcm_streams = (pcm_cap >> CNL_PCMSCAP_BSS_SHIFT) & + CNL_PCMSCAP_BSS_MASK; + dev_info(&mstr->dev, "Number of Bidirectional PCM stream = %d\n", + sdw->num_pcm_streams); + sdw->pcm_streams = devm_kzalloc(&mstr->dev, + sdw->num_pcm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->pcm_streams) + return -ENOMEM; + /* Two of the PCM streams are reserved for bulk transfers */ + sdw->pcm_streams -= SDW_CNL_PCM_PDI_NUM_OFFSET; + for (i = SDW_CNL_PCM_PDI_NUM_OFFSET; i < sdw->num_pcm_streams; i++) { + ch_cnt_offset = SDW_CNL_PCMSCHC + + (data->inst_id * SDW_CNL_PCMSCHC_REG_OFFSET) + + ((i + SDW_CNL_PCM_PDI_NUM_OFFSET) * 0x2); + + sdw->pcm_streams[i].ch_cnt = cnl_sdw_reg_readw(data->sdw_shim, + ch_cnt_offset); + /* Zero based value in register */ + sdw->pcm_streams[i].ch_cnt++; + sdw->pcm_streams[i].pdi_num = i; + sdw->pcm_streams[i].allocated = false; + dev_info(&mstr->dev, "CH Count for stream %d is %d\n", + i, sdw->pcm_streams[i].ch_cnt); + } + return 0; +} + +static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) +{ + int i; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int pdm_cap, pdm_ch_count, total_pdm_streams; + int pdm_cap_offset = SDW_CNL_PDMSCAP + + (data->inst_id * SDW_CNL_PDMSCAP_REG_OFFSET); + + pdm_cap = cnl_sdw_reg_readw(data->sdw_regs, pdm_cap_offset); + sdw->num_pdm_streams = (pdm_cap >> CNL_PDMSCAP_BSS_SHIFT) & + CNL_PDMSCAP_BSS_MASK; + /* Zero based value in register */ + sdw->num_pdm_streams++; + sdw->pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->pdm_streams) + return -ENOMEM; + + sdw->num_in_pdm_streams = (pdm_cap >> CNL_PDMSCAP_ISS_SHIFT) & + CNL_PDMSCAP_ISS_MASK; + /* Zero based value in register */ + sdw->num_in_pdm_streams++; + sdw->in_pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_in_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + + if (!sdw->in_pdm_streams) + return -ENOMEM; + + sdw->num_out_pdm_streams = (pdm_cap >> CNL_PDMSCAP_OSS_SHIFT) & + CNL_PDMSCAP_OSS_MASK; + /* Zero based value in register */ + sdw->num_out_pdm_streams++; + sdw->out_pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_out_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->out_pdm_streams) + return -ENOMEM; + + total_pdm_streams = sdw->num_pdm_streams + + sdw->num_in_pdm_streams + + sdw->num_out_pdm_streams; + + pdm_ch_count = (pdm_cap >> CNL_PDMSCAP_CPSS_SHIFT) & + CNL_PDMSCAP_CPSS_MASK; + for (i = 0; i < sdw->num_pdm_streams; i++) { + sdw->pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->pdm_streams[i].pdi_num = i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->pdm_streams[i].allocated = false; + } + for (i = 0; i < sdw->num_in_pdm_streams; i++) { + sdw->in_pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->in_pdm_streams[i].pdi_num = i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->in_pdm_streams[i].allocated = false; + } + for (i = 0; i < sdw->num_out_pdm_streams; i++) { + sdw->out_pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->out_pdm_streams[i].pdi_num = + i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->out_pdm_streams[i].allocated = false; + } + return 0; +} + +static int sdw_port_pdi_init(struct cnl_sdw *sdw) +{ + int i, ret = 0; + + for (i = 0; i < CNL_SDW_MAX_PORTS; i++) { + sdw->port[i].port_num = i; + sdw->port[i].allocated = false; + } + ret = sdw_pcm_pdi_init(sdw); + if (ret) + return ret; + ret = sdw_pdm_pdi_init(sdw); + + return ret; +} + +static int sdw_init(struct cnl_sdw *sdw) +{ + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int mcp_config, mcp_control; + int ret = 0; + + /* Power up the link controller */ + ret = sdw_power_up_link(sdw); + if (ret) + return ret; + + /* Initialize the IO control registers */ + sdw_init_shim(sdw); + + /* Switch the ownership to Master IP from glue logic */ + sdw_switch_to_mip(sdw); + + /* Set command acceptance mode. This is required because when + * Master broadcasts the clock_stop command to slaves, slaves + * might be already suspended, so this return NO ACK, in that + * case also master should go to clock stop mode. + */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control |= (MCP_CONTROL_CMDACCEPTMODE_MASK << + MCP_CONTROL_CMDACCEPTMODE_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, 0x48); + + mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); + /* Set Max cmd retry to 15 times */ + mcp_config |= (CNL_SDW_MAX_CMD_RETRIES << + MCP_CONFIG_MAXCMDRETRY_SHIFT); + + /* Set Ping request to ping delay to 15 frames. + * Spec supports 32 max frames + */ + mcp_config |= (CNL_SDW_MAX_PREQ_DELAY << + MCP_CONFIG_MAXPREQDELAY_SHIFT); + + /* If master is synchronized to some other master set Multimode */ + if (mstr->link_sync_mask) { + mcp_config |= (MCP_CONFIG_MMMODEEN_MASK << + MCP_CONFIG_MMMODEEN_SHIFT); + mcp_config |= (MCP_CONFIG_SSPMODE_MASK << + MCP_CONFIG_SSPMODE_SHIFT); + } else { + mcp_config &= ~(MCP_CONFIG_MMMODEEN_MASK << + MCP_CONFIG_MMMODEEN_SHIFT); + mcp_config &= ~(MCP_CONFIG_SSPMODE_MASK << + MCP_CONFIG_SSPMODE_SHIFT); + } + + /* Disable automatic bus release */ + mcp_config &= ~(MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT); + + /* Disable sniffer mode now */ + mcp_config &= ~(MCP_CONFIG_SNIFFEREN_MASK << + MCP_CONFIG_SNIFFEREN_SHIFT); + + /* Set the command mode for Tx and Rx command */ + mcp_config &= ~(MCP_CONFIG_CMDMODE_MASK << + MCP_CONFIG_CMDMODE_SHIFT); + + /* Set operation mode to normal */ + mcp_config &= ~(MCP_CONFIG_OPERATIONMODE_MASK << + MCP_CONFIG_OPERATIONMODE_SHIFT); + mcp_config |= ((MCP_CONFIG_OPERATIONMODE_NORMAL & + MCP_CONFIG_OPERATIONMODE_MASK) << + MCP_CONFIG_OPERATIONMODE_SHIFT); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); + /* Set the SSP interval to 32 for both banks */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, + SDW_CNL_DEFAULT_SSP_INTERVAL); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, + SDW_CNL_DEFAULT_SSP_INTERVAL); + + /* Initialize the phy control registers. */ + sdw_init_phyctrl(sdw); + + /* Initlaize the ports */ + ret = sdw_port_pdi_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + data->inst_id); + sdw_power_down_link(sdw); + return ret; + } + + /* Lastly enable interrupts */ + sdw_enable_interrupt(sdw); + + /* Update soundwire configuration */ + return sdw_config_update(sdw); +} + +static int sdw_alloc_pcm_stream(struct cnl_sdw *sdw, + struct cnl_sdw_port *port, int ch_cnt, + enum sdw_data_direction direction) +{ + int num_pcm_streams, pdi_ch_map = 0, stream_id; + struct cnl_sdw_pdi_stream *stream, *pdi_stream; + unsigned int i; + unsigned int ch_map_offset, port_ctrl_offset, pdi_config_offset; + struct sdw_master *mstr = sdw->mstr; + unsigned int port_ctrl = 0, pdi_config = 0, channel_mask; + unsigned int stream_config; + + /* Currently PCM supports only bi-directional streams only */ + num_pcm_streams = sdw->num_pcm_streams; + stream = sdw->pcm_streams; + + mutex_lock(&sdw->stream_lock); + for (i = SDW_CNL_PCM_PDI_NUM_OFFSET; i < num_pcm_streams; i++) { + if (stream[i].allocated == false) { + stream[i].allocated = true; + stream[i].port_num = port->port_num; + port->pdi_stream = &stream[i]; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port->pdi_stream) { + dev_err(&mstr->dev, "Unable to allocate stream for PCM\n"); + return -EINVAL; + } + pdi_stream = port->pdi_stream; + /* We didnt get enough PDI streams, so free the allocated + * PDI streams. Free the port as well and return with error + */ + pdi_stream->l_ch_num = 0; + pdi_stream->h_ch_num = ch_cnt - 1; + ch_map_offset = SDW_CNL_PCMSCHM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr) + + (0x2 * pdi_stream->pdi_num); + if (port->direction == SDW_DATA_DIR_IN) + pdi_ch_map |= (CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); + else + pdi_ch_map &= ~(CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); + /* TODO: Remove this hardcoding */ + stream_id = mstr->nr * 16 + pdi_stream->pdi_num + 5; + pdi_stream->sdw_pdi_num = stream_id; + pdi_ch_map |= (stream_id & CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + pdi_ch_map |= (pdi_stream->l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + pdi_ch_map |= (0xF & CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + cnl_sdw_reg_writew(sdw->data.sdw_shim, ch_map_offset, + pdi_ch_map); + /* If direction is input, port is sink port*/ + if (direction == SDW_DATA_DIR_IN) + port_ctrl |= (PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + else + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl_offset = SDW_CNL_PORTCTRL + (port->port_num * + SDW_CNL_PORT_REG_OFFSET); + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, port_ctrl); + + pdi_config |= ((port->port_num & PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + channel_mask = (1 << ch_cnt) - 1; + pdi_config |= (channel_mask << PDINCONFIG_CHANNEL_MASK_SHIFT); + /* TODO: Remove below hardcodings */ + pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (pdi_stream->pdi_num * 16)); + cnl_sdw_reg_writel(sdw->data.sdw_regs, pdi_config_offset, pdi_config); + + stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (pdi_stream->sdw_pdi_num * ALH_CNL_STRMZCFG_OFFSET)); + stream_config |= (CNL_STRMZCFG_DMAT_VAL & CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + stream_config |= ((ch_cnt - 1) & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + cnl_sdw_reg_writel(sdw->data.alh_base, + (pdi_stream->sdw_pdi_num * ALH_CNL_STRMZCFG_OFFSET), + stream_config); + return 0; +} + +static int sdw_alloc_pdm_stream(struct cnl_sdw *sdw, + struct cnl_sdw_port *port, int ch_cnt, int direction) +{ + int num_pdm_streams; + struct cnl_sdw_pdi_stream *stream; + int i; + unsigned int port_ctrl_offset, pdi_config_offset; + unsigned int port_ctrl = 0, pdi_config = 0, channel_mask; + + /* Currently PDM supports either Input or Output Streams */ + if (direction == SDW_DATA_DIR_IN) { + num_pdm_streams = sdw->num_in_pdm_streams; + stream = sdw->in_pdm_streams; + } else { + num_pdm_streams = sdw->num_out_pdm_streams; + stream = sdw->out_pdm_streams; + } + mutex_lock(&sdw->stream_lock); + for (i = 0; i < num_pdm_streams; i++) { + if (stream[i].allocated == false) { + stream[i].allocated = true; + stream[i].port_num = port->port_num; + port->pdi_stream = &stream[i]; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port->pdi_stream) + return -EINVAL; + /* If direction is input, port is sink port*/ + if (direction == SDW_DATA_DIR_IN) + port_ctrl |= (PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + else + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl_offset = SDW_CNL_PORTCTRL + (port->port_num * + SDW_CNL_PORT_REG_OFFSET); + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, port_ctrl); + + pdi_config |= ((port->port_num & PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + channel_mask = (1 << ch_cnt) - 1; + pdi_config |= (channel_mask << PDINCONFIG_CHANNEL_MASK_SHIFT); + /* TODO: Remove below hardcodings */ + pdi_config_offset = (SDW_CNL_PDINCONFIG0 + (stream[i].pdi_num * 16)); + cnl_sdw_reg_writel(sdw->data.sdw_regs, pdi_config_offset, pdi_config); + + return 0; +} + +struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, + enum sdw_data_direction direction, + enum cnl_sdw_pdi_stream_type stream_type) +{ + struct cnl_sdw *sdw; + struct cnl_sdw_port *port = NULL; + int i, ret = 0; + struct num_pdi_streams; + + sdw = sdw_master_get_drvdata(mstr); + + mutex_lock(&sdw->stream_lock); + for (i = 1; i < CNL_SDW_MAX_PORTS; i++) { + if (sdw->port[i].allocated == false) { + port = &sdw->port[i]; + port->allocated = true; + port->direction = direction; + port->ch_cnt = ch_count; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port) { + dev_err(&mstr->dev, "Unable to allocate port\n"); + return NULL; + } + port->pdi_stream = NULL; + if (stream_type == CNL_SDW_PDI_TYPE_PDM) + ret = sdw_alloc_pdm_stream(sdw, port, ch_count, direction); + else + ret = sdw_alloc_pcm_stream(sdw, port, ch_count, direction); + if (!ret) + return port; + + dev_err(&mstr->dev, "Unable to allocate stream\n"); + mutex_lock(&sdw->stream_lock); + port->allocated = false; + mutex_unlock(&sdw->stream_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(cnl_sdw_alloc_port); + +void cnl_sdw_free_port(struct sdw_master *mstr, int port_num) +{ + int i; + struct cnl_sdw *sdw; + struct cnl_sdw_port *port = NULL; + + sdw = sdw_master_get_drvdata(mstr); + for (i = 1; i < CNL_SDW_MAX_PORTS; i++) { + if (sdw->port[i].port_num == port_num) { + port = &sdw->port[i]; + break; + } + } + if (!port) + return; + mutex_lock(&sdw->stream_lock); + port->pdi_stream->allocated = false; + port->pdi_stream = NULL; + port->allocated = false; + mutex_unlock(&sdw->stream_lock); +} +EXPORT_SYMBOL_GPL(cnl_sdw_free_port); + +static int cnl_sdw_update_slave_status(struct cnl_sdw *sdw, int slave_intstat0, + int slave_intstat1) +{ + int i; + struct sdw_status slave_status; + u64 slaves_stat, slave_stat; + int ret = 0; + + memset(&slave_status, 0x0, sizeof(slave_status)); + slaves_stat = (u64) slave_intstat1 << + SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT; + slaves_stat |= slave_intstat0; + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) { + slave_stat = slaves_stat >> (i * SDW_CNL_SLAVE_STATUS_BITS); + if (slave_stat & MCP_SLAVEINTSTAT_NOT_PRESENT_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_NOT_PRESENT; + else if (slave_stat & MCP_SLAVEINTSTAT_ATTACHED_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_ATTACHED_OK; + else if (slave_stat & MCP_SLAVEINTSTAT_ALERT_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_ALERT; + else if (slave_stat & MCP_SLAVEINTSTAT_RESERVED_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_RESERVED; + } + ret = sdw_master_update_slv_status(sdw->mstr, &slave_status); + return ret; +} + +static void cnl_sdw_read_response(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + int num_res = 0, i; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + + num_res = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_FIFOSTAT); + num_res &= MCP_RX_FIFO_AVAIL_MASK; + for (i = 0; i < num_res; i++) { + sdw->response_buf[i] = cnl_sdw_reg_readl(data->sdw_regs, + cmd_base); + cmd_base += SDW_CNL_CMD_WORD_LEN; + } +} + +irqreturn_t cnl_sdw_irq_handler(int irq, void *context) +{ + struct cnl_sdw *sdw = context; + volatile int int_status, status, wake_sts; + + struct cnl_sdw_data *data = &sdw->data; + volatile int slave_intstat0 = 0, slave_intstat1 = 0; + struct sdw_master *mstr = sdw->mstr; + + /* + * Return if IP is in power down state. Interrupt can still come + * since its shared irq. + */ + if (!sdw->sdw_link_status) + return IRQ_NONE; + + int_status = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_INTSTAT); + status = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_STAT); + slave_intstat0 = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_SLAVEINTSTAT0); + slave_intstat1 = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_SLAVEINTSTAT1); + wake_sts = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKESTS_REG_OFFSET); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, + wake_sts); + + if (!(int_status & (MCP_INTSTAT_IRQ_MASK << MCP_INTSTAT_IRQ_SHIFT))) + return IRQ_NONE; + + if (int_status & (MCP_INTSTAT_RXWL_MASK << MCP_INTSTAT_RXWL_SHIFT)) { + cnl_sdw_read_response(sdw); + complete(&sdw->tx_complete); + } + if (int_status & (MCP_INTSTAT_CONTROLBUSCLASH_MASK << + MCP_INTSTAT_CONTROLBUSCLASH_SHIFT)) { + /* Some slave is behaving badly, where its driving + * data line during control word bits. + */ + dev_err_ratelimited(&mstr->dev, "Bus clash detected for control word\n"); + WARN_ONCE(1, "Bus clash detected for control word\n"); + } + if (int_status & (MCP_INTSTAT_DATABUSCLASH_MASK << + MCP_INTSTAT_DATABUSCLASH_SHIFT)) { + /* More than 1 slave is trying to drive bus. There is + * some problem with ownership of bus data bits, + * or either of the + * slave is behaving badly. + */ + dev_err_ratelimited(&mstr->dev, "Bus clash detected for control word\n"); + WARN_ONCE(1, "Bus clash detected for data word\n"); + } + + if (int_status & (MCP_INTSTAT_SLAVE_STATUS_CHANGED_MASK << + MCP_INTSTAT_SLAVE_STATUS_CHANGED_SHIFT)) { + dev_info(&mstr->dev, "Slave status change\n"); + cnl_sdw_update_slave_status(sdw, slave_intstat0, + slave_intstat1); + } + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTSTAT0, + slave_intstat0); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTSTAT1, + slave_intstat1); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_INTSTAT, int_status); + return IRQ_HANDLED; +} + +static enum sdw_command_response cnl_program_scp_addr(struct sdw_master *mstr, + struct sdw_msg *msg) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + u32 cmd_data[2] = {0, 0}; + unsigned long time_left; + int no_ack = 0, nack = 0; + int i; + + /* Since we are programming 2 commands, program the + * RX watermark level at 2 + */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FIFOLEVEL, 2); + /* Program device address */ + cmd_data[0] |= (msg->slave_addr & MCP_COMMAND_DEV_ADDR_MASK) << + MCP_COMMAND_DEV_ADDR_SHIFT; + /* Write command to program the scp_addr1 register */ + cmd_data[0] |= (0x3 << MCP_COMMAND_COMMAND_SHIFT); + cmd_data[1] = cmd_data[0]; + /* scp_addr1 register address */ + cmd_data[0] |= (SDW_SCP_ADDRPAGE1 << MCP_COMMAND_REG_ADDR_L_SHIFT); + cmd_data[1] |= (SDW_SCP_ADDRPAGE2 << MCP_COMMAND_REG_ADDR_L_SHIFT); + cmd_data[0] |= msg->addr_page1; + cmd_data[1] |= msg->addr_page2; + + cnl_sdw_reg_writel(data->sdw_regs, cmd_base, cmd_data[0]); + cmd_base += SDW_CNL_CMD_WORD_LEN; + cnl_sdw_reg_writel(data->sdw_regs, cmd_base, cmd_data[1]); + + time_left = wait_for_completion_timeout(&sdw->tx_complete, + 3000); + if (!time_left) { + dev_err(&mstr->dev, "Controller Timed out\n"); + msg->len = 0; + return -ETIMEDOUT; + } + + for (i = 0; i < CNL_SDW_SCP_ADDR_REGS; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + } + /* We dont return error if NACK or No ACK detected for broadcast addr + * because some slave might support SCP addr, while some slaves may not + * support it. This is not correct, since we wont be able to find out + * if NACK is detected because of slave not supporting SCP_addrpage or + * its a genuine NACK because of bus errors. We are not sure what slaves + * will report, NACK or No ACK for the scp_addrpage programming if they + * dont support it. Spec is not clear about this. + * This needs to be thought through + */ + if (nack & (msg->slave_addr != 15)) { + dev_err(&mstr->dev, "SCP_addrpage write NACKed for slave %d\n", msg->slave_addr); + return -EREMOTEIO; + } else if (no_ack && (msg->slave_addr != 15)) { + dev_err(&mstr->dev, "SCP_addrpage write ignored for slave %d\n", msg->slave_addr); + return -EREMOTEIO; + } else + return 0; + +} + +static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, + struct sdw_msg *msg, int cmd, int offset, int count) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int i, j; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + u32 response_base = SDW_CNL_MCP_RESPONSE_BASE; + u32 cmd_data = 0, response_data; + unsigned long time_left; + int no_ack = 0, nack = 0; + u16 addr = msg->addr; + + /* Program the watermark level upto number of count */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FIFOLEVEL, count); + + cmd_base = SDW_CNL_MCP_COMMAND_BASE; + for (j = 0; j < count; j++) { + /* Program device address */ + cmd_data = 0; + cmd_data |= (msg->slave_addr & + MCP_COMMAND_DEV_ADDR_MASK) << + MCP_COMMAND_DEV_ADDR_SHIFT; + /* Program read/write command */ + cmd_data |= (cmd << MCP_COMMAND_COMMAND_SHIFT); + /* program incrementing address register */ + cmd_data |= (addr++ << MCP_COMMAND_REG_ADDR_L_SHIFT); + /* Program the data if write command */ + if (msg->flag == SDW_MSG_FLAG_WRITE) + cmd_data |= + msg->buf[j + offset]; + + cmd_data |= ((msg->ssp_tag & + MCP_COMMAND_SSP_TAG_MASK) << + MCP_COMMAND_SSP_TAG_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, + cmd_base, cmd_data); + cmd_base += SDW_CNL_CMD_WORD_LEN; + } + /* Wait for 3 second for timeout */ + time_left = wait_for_completion_timeout(&sdw->tx_complete, 3 * HZ); + if (!time_left) { + dev_err(&mstr->dev, "Controller timedout\n"); + msg->len = 0; + return -ETIMEDOUT; + } + for (i = 0; i < count; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & + sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + break; + } + if (nack) { + dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } else if (no_ack) { + dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } + if (msg->flag == SDW_MSG_FLAG_WRITE) + return 0; + /* Response and Command has same base address */ + response_base = SDW_CNL_MCP_COMMAND_BASE; + for (j = 0; j < count; j++) { + response_data = cnl_sdw_reg_readl(data->sdw_regs, + cmd_base); + msg->buf[j + offset] = + (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); + cmd_base += 4; + } + return 0; +} + +static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page) +{ + int i, ret = 0, cmd; + + if (program_scp_addr_page) + ret = cnl_program_scp_addr(mstr, msg); + + if (ret) { + msg->len = 0; + return ret; + } + + switch (msg->flag) { + case SDW_MSG_FLAG_READ: + cmd = 0x2; + break; + case SDW_MSG_FLAG_WRITE: + cmd = 0x3; + break; + default: + dev_err(&mstr->dev, "Command not supported\n"); + return -EINVAL; + } + for (i = 0; i < msg->len / SDW_CNL_MCP_COMMAND_LENGTH; i++) { + ret = sdw_xfer_msg(mstr, msg, + cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, + SDW_CNL_MCP_COMMAND_LENGTH); + if (ret < 0) + break; + } + if (!(msg->len % SDW_CNL_MCP_COMMAND_LENGTH)) + return ret; + ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, + msg->len % SDW_CNL_MCP_COMMAND_LENGTH); + if (ret < 0) + return -EINVAL; + return ret; +} + +static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, + struct sdw_bra_block *block) +{ + return 0; +} + +static int cnl_sdw_mon_handover(struct sdw_master *mstr, + bool enable) +{ + int mcp_config; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); + if (enable) + mcp_config |= MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT; + else + mcp_config &= ~(MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); + return 0; +} + +static int cnl_sdw_set_ssp_interval(struct sdw_master *mstr, + int ssp_interval, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int sspctrl_offset, check; + + if (bank) + sspctrl_offset = SDW_CNL_MCP_SSPCTRL1; + else + sspctrl_offset = SDW_CNL_MCP_SSPCTRL0; + + cnl_sdw_reg_writel(data->sdw_regs, sspctrl_offset, ssp_interval); + + check = cnl_sdw_reg_readl(data->sdw_regs, sspctrl_offset); + + return 0; +} + +static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, + int cur_clk_freq, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int mcp_clockctrl_offset, mcp_clockctrl; + + + /* TODO: Retrieve divider value or get value directly from calling + * function + */ + int divider = ((9600000/cur_clk_freq) - 1); + + if (bank) { + mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL1); + + } else { + mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL0; + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL0); + } + + mcp_clockctrl |= divider; + + /* Write value here */ + cnl_sdw_reg_writel(data->sdw_regs, mcp_clockctrl_offset, + mcp_clockctrl); + + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + mcp_clockctrl_offset); + return 0; +} + +static int cnl_sdw_set_port_params(struct sdw_master *mstr, + struct sdw_port_params *params, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int dpn_config = 0, dpn_config_offset; + + if (bank) + dpn_config_offset = SDW_CNL_DPN_CONFIG1; + else + dpn_config_offset = SDW_CNL_DPN_CONFIG0; + + dpn_config = cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + + dpn_config |= (((params->word_length - 1) & DPN_CONFIG_WL_MASK) << + DPN_CONFIG_WL_SHIFT); + dpn_config |= ((params->port_flow_mode & DPN_CONFIG_PF_MODE_MASK) << + DPN_CONFIG_PF_MODE_SHIFT); + dpn_config |= ((params->port_data_mode & DPN_CONFIG_PD_MODE_MASK) << + DPN_CONFIG_PD_MODE_SHIFT); + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_config_offset, params->num, dpn_config); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + return 0; +} + +static int cnl_sdw_set_port_transport_params(struct sdw_master *mstr, + struct sdw_transport_params *params, int bank) +{ +struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + int dpn_config = 0, dpn_config_offset; + int dpn_samplectrl_offset; + int dpn_offsetctrl = 0, dpn_offsetctrl_offset; + int dpn_hctrl = 0, dpn_hctrl_offset; + + if (bank) { + dpn_config_offset = SDW_CNL_DPN_CONFIG1; + dpn_samplectrl_offset = SDW_CNL_DPN_SAMPLECTRL1; + dpn_hctrl_offset = SDW_CNL_DPN_HCTRL1; + dpn_offsetctrl_offset = SDW_CNL_DPN_OFFSETCTRL1; + } else { + dpn_config_offset = SDW_CNL_DPN_CONFIG0; + dpn_samplectrl_offset = SDW_CNL_DPN_SAMPLECTRL0; + dpn_hctrl_offset = SDW_CNL_DPN_HCTRL0; + dpn_offsetctrl_offset = SDW_CNL_DPN_OFFSETCTRL0; + } + dpn_config = cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + dpn_config |= ((params->blockgroupcontrol & DPN_CONFIG_BGC_MASK) << + DPN_CONFIG_BGC_SHIFT); + dpn_config |= ((params->blockpackingmode & DPN_CONFIG_BPM_MASK) << + DPN_CONFIG_BPM_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_config_offset, params->num, dpn_config); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + + dpn_offsetctrl |= ((params->offset1 & DPN_OFFSETCTRL0_OF1_MASK) << + DPN_OFFSETCTRL0_OF1_SHIFT); + + dpn_offsetctrl |= ((params->offset2 & DPN_OFFSETCTRL0_OF2_MASK) << + DPN_OFFSETCTRL0_OF2_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_offsetctrl_offset, params->num, dpn_offsetctrl); + + + dpn_hctrl |= ((params->hstart & DPN_HCTRL_HSTART_MASK) << + DPN_HCTRL_HSTART_SHIFT); + dpn_hctrl |= ((params->hstop & DPN_HCTRL_HSTOP_MASK) << + DPN_HCTRL_HSTOP_SHIFT); + dpn_hctrl |= ((params->lanecontrol & DPN_HCTRL_LCONTROL_MASK) << + DPN_HCTRL_LCONTROL_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_hctrl_offset, params->num, dpn_hctrl); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_samplectrl_offset, params->num, + (params->sample_interval - 1)); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_hctrl_offset, params->num); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_samplectrl_offset, params->num); + + return 0; +} + +static int cnl_sdw_port_activate_ch(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int dpn_channelen_offset; + int ch_mask; + + if (bank) + dpn_channelen_offset = SDW_CNL_DPN_CHANNELEN1; + else + dpn_channelen_offset = SDW_CNL_DPN_CHANNELEN0; + + if (activate_ch->activate) + ch_mask = activate_ch->ch_mask; + else + ch_mask = 0; + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_channelen_offset, activate_ch->num, + ch_mask); + + return 0; +} + +static int cnl_sdw_port_activate_ch_pre(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + int sync_reg; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + if (mstr->link_sync_mask) { + /* Check if this link is synchronized with some other link */ + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + /* If link is synchronized with other link than + * Need to make sure that command doesnt go till + * ssync is applied + */ + sync_reg |= (1 << (data->inst_id + CNL_SYNC_CMDSYNC_SHIFT)); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + } + + return 0; +} +static int cnl_sdw_port_activate_ch_post(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + int sync_reg; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + + return 0; +} + +static int cnl_sdw_probe(struct sdw_master *mstr, + const struct sdw_master_id *sdw_id) +{ + struct cnl_sdw *sdw; + int ret = 0; + struct cnl_sdw_data *data = mstr->dev.platform_data; + + sdw = devm_kzalloc(&mstr->dev, sizeof(*sdw), GFP_KERNEL); + if (!sdw) { + ret = -ENOMEM; + return ret; + } + dev_info(&mstr->dev, + "Controller Resources ctrl_base = %p shim=%p irq=%d inst_id=%d\n", + data->sdw_regs, data->sdw_shim, data->irq, data->inst_id); + sdw->data.sdw_regs = data->sdw_regs; + sdw->data.sdw_shim = data->sdw_shim; + sdw->data.irq = data->irq; + sdw->data.inst_id = data->inst_id; + sdw->data.alh_base = data->alh_base; + sdw->mstr = mstr; + spin_lock_init(&sdw->ctrl_lock); + sdw_master_set_drvdata(mstr, sdw); + init_completion(&sdw->tx_complete); + mutex_init(&sdw->stream_lock); + ret = sdw_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + data->inst_id); + return ret; + } + ret = devm_request_irq(&mstr->dev, + sdw->data.irq, cnl_sdw_irq_handler, IRQF_SHARED, "SDW", sdw); + if (ret) { + dev_err(&mstr->dev, "unable to grab IRQ %d, disabling device\n", + sdw->data.irq); + sdw_power_down_link(sdw); + return ret; + } + pm_runtime_set_autosuspend_delay(&mstr->dev, 3000); + pm_runtime_use_autosuspend(&mstr->dev); + pm_runtime_enable(&mstr->dev); + pm_runtime_get_sync(&mstr->dev); + /* Resuming the device, since its already ON, function will simply + * return doing nothing + */ + pm_runtime_mark_last_busy(&mstr->dev); + /* Suspending the device after 3 secs, by the time + * all the slave would have enumerated. Initial + * clock freq is 9.6MHz and frame shape is 48X2, so + * there are 200000 frames in second, total there are + * minimum 600000 frames before device suspends. Soundwire + * spec says slave should get attached to bus in 4096 + * error free frames after reset. So this should be + * enough to make sure device gets attached to bus. + */ + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} + +static int cnl_sdw_remove(struct sdw_master *mstr) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + sdw_power_down_link(sdw); + + return 0; +} + +#ifdef CONFIG_PM +static int cnl_sdw_runtime_suspend(struct device *dev) +{ + enum sdw_clk_stop_mode clock_stop_mode; + + int volatile mcp_stat; + int mcp_control; + int timeout = 0; + int ret = 0; + + struct cnl_sdw *sdw = dev_get_drvdata(dev); + struct cnl_sdw_data *data = &sdw->data; + + /* If its suspended return */ + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT)) { + dev_info(dev, "Clock is already stopped\n"); + return 0; + } + + /* Write the MCP Control register to prevent block wakeup */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control |= (MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + /* Prepare all the slaves for clock stop */ + ret = sdw_prepare_for_clock_change(sdw->mstr, 1, &clock_stop_mode); + if (ret) + return ret; + + /* Call bus function to broadcast the clock stop now */ + ret = sdw_stop_clock(sdw->mstr, clock_stop_mode); + if (ret) + return ret; + /* Wait for clock to be stopped, we are waiting at max 1sec now */ + while (timeout != 10) { + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT)) + break; + msleep(100); + timeout++; + } + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (!(mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT))) { + dev_err(dev, "Clock Stop failed\n"); + ret = -EBUSY; + goto out; + } + /* Switch control from master IP to glue */ + sdw_switch_to_glue(sdw); + + sdw_power_down_link(sdw); + + /* Enable the wakeup */ + cnl_sdw_reg_writew(data->sdw_shim, + SDW_CNL_SNDWWAKEEN_REG_OFFSET, + (0x1 << data->inst_id)); +out: + return ret; +} + +static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) +{ + u16 wake_en, wake_sts, ioctl; + int volatile mcp_control; + int timeout = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + /* Disable the wake up interrupt */ + wake_en = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKEEN_REG_OFFSET); + wake_en &= ~(0x1 << data->inst_id); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKEEN_REG_OFFSET, + wake_en); + + /* Clear wake status. This may be set if Slave requested wakeup has + * happened, or may not be if it master requested. But in any case + * this wont make any harm + */ + wake_sts = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKESTS_REG_OFFSET); + wake_sts |= (0x1 << data->inst_id); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, + wake_sts); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + /* Switch control back to master */ + sdw_switch_to_mip(sdw); + + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control &= ~(MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + mcp_control |= (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + /* + * Wait for timeout to be clear to successful enabling of the clock + * We will wait for 1sec before giving up + */ + while (timeout != 10) { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) == 0) + break; + msleep(1000); + timeout++; + } + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) != 0) { + dev_err(&sdw->mstr->dev, "Clop Stop Exit failed\n"); + return -EBUSY; + } + + dev_info(&sdw->mstr->dev, "Exit from clock stop successful\n"); + return 0; + +} + +static int cnl_sdw_runtime_resume(struct device *dev) +{ + struct cnl_sdw *sdw = dev_get_drvdata(dev); + struct cnl_sdw_data *data = &sdw->data; + int volatile mcp_stat; + struct sdw_master *mstr; + int ret = 0; + + mstr = sdw->mstr; + /* + * If already resumed, do nothing. This can happen because of + * wakeup enable. + */ + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (!(mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT))) { + dev_info(dev, "Clock is already running\n"); + return 0; + } + dev_info(dev, "%s %d Clock is stopped\n", __func__, __LINE__); + + ret = cnl_sdw_clock_stop_exit(sdw); + if (ret) + return ret; + dev_info(&mstr->dev, "Exit from clock stop successful\n"); + + /* Prepare all the slaves to comeout of clock stop */ + ret = sdw_prepare_for_clock_change(sdw->mstr, 0, NULL); + if (ret) + return ret; + + return 0; +} + +static int cnl_sdw_sleep_resume(struct device *dev) +{ + return cnl_sdw_runtime_resume(dev); +} +static int cnl_sdw_sleep_suspend(struct device *dev) +{ + return cnl_sdw_runtime_suspend(dev); +} +#endif + +static const struct dev_pm_ops cnl_sdw_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cnl_sdw_sleep_suspend, cnl_sdw_sleep_resume) + SET_RUNTIME_PM_OPS(cnl_sdw_runtime_suspend, + cnl_sdw_runtime_resume, NULL) +}; + +static struct sdw_master_ops cnl_sdw_master_ops = { + .xfer_msg = cnl_sdw_xfer_msg, + .xfer_bulk = cnl_sdw_xfer_bulk, + .monitor_handover = cnl_sdw_mon_handover, + .set_ssp_interval = cnl_sdw_set_ssp_interval, + .set_clock_freq = cnl_sdw_set_clock_freq, + .set_frame_shape = NULL, +}; + +static struct sdw_master_port_ops cnl_sdw_master_port_ops = { + .dpn_set_port_params = cnl_sdw_set_port_params, + .dpn_set_port_transport_params = cnl_sdw_set_port_transport_params, + .dpn_port_activate_ch = cnl_sdw_port_activate_ch, + .dpn_port_activate_ch_pre = cnl_sdw_port_activate_ch_pre, + .dpn_port_activate_ch_post = cnl_sdw_port_activate_ch_post, + .dpn_port_prepare_ch = NULL, + .dpn_port_prepare_ch_pre = NULL, + .dpn_port_prepare_ch_post = NULL, + +}; + +static struct sdw_mstr_driver cnl_sdw_mstr_driver = { + .driver_type = SDW_DRIVER_TYPE_MASTER, + .driver = { + .name = "cnl_sdw_mstr", + .pm = &cnl_sdw_pm_ops, + }, + .probe = cnl_sdw_probe, + .remove = cnl_sdw_remove, + .mstr_ops = &cnl_sdw_master_ops, + .mstr_port_ops = &cnl_sdw_master_port_ops, +}; + +static int __init cnl_sdw_init(void) +{ + return sdw_mstr_driver_register(&cnl_sdw_mstr_driver); +} +module_init(cnl_sdw_init); + +static void cnl_sdw_exit(void) +{ + sdw_mstr_driver_unregister(&cnl_sdw_mstr_driver); +} +module_exit(cnl_sdw_exit); + +MODULE_DESCRIPTION("Intel SoundWire Master Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Hardik Shah "); diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h new file mode 100644 index 000000000000..914f7cae2b01 --- /dev/null +++ b/drivers/sdw/sdw_cnl_priv.h @@ -0,0 +1,337 @@ +/* + * sdw_cnl_priv.h - Private definition for intel master controller driver. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_CNL_PRIV_H +#define _LINUX_SDW_CNL_PRIV_H + +#define SDW_CNL_PM_TIMEOUT 3000 /* ms */ +#define SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT 32 +#define SDW_CNL_SLAVE_STATUS_BITS 4 +#define SDW_CNL_CMD_WORD_LEN 4 +#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x32 +#define SDW_CNL_PORT_REG_OFFSET 0x80 +#define CNL_SDW_SCP_ADDR_REGS 0x2 +#define SDW_CNL_PCM_PDI_NUM_OFFSET 0x2 +#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 + +#define SDW_CNL_CTMCTL_REG_OFFSET 0x60 +#define SDW_CNL_IOCTL_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCAP_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCHC_REG_OFFSET 0x60 +#define SDW_CNL_PDMSCAP_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCHM_REG_OFFSET 0x60 +#define SDW_CNL_SNDWWAKEEN_REG_OFFSET 0x190 +#define SDW_CNL_SNDWWAKESTS_REG_OFFSET 0x192 + + +#define SDW_CNL_MCP_CONFIG 0x0 +#define MCP_CONFIG_BRELENABLE_MASK 0x1 +#define MCP_CONFIG_BRELENABLE_SHIFT 0x6 +#define MCP_CONFIG_MAXCMDRETRY_SHIFT 24 +#define MCP_CONFIG_MAXCMDRETRY_MASK 0xF +#define MCP_CONFIG_MAXPREQDELAY_SHIFT 16 +#define MCP_CONFIG_MAXPREQDELAY_MASK 0x1F +#define MCP_CONFIG_MMMODEEN_SHIFT 0x7 +#define MCP_CONFIG_MMMODEEN_MASK 0x1 +#define MCP_CONFIG_SNIFFEREN_SHIFT 0x5 +#define MCP_CONFIG_SNIFFEREN_MASK 0x1 +#define MCP_CONFIG_SSPMODE_SHIFT 0x4 +#define MCP_CONFIG_SSPMODE_MASK 0x1 +#define MCP_CONFIG_CMDMODE_SHIFT 0x3 +#define MCP_CONFIG_CMDMODE_MASK 0x1 + +#define MCP_CONFIG_OPERATIONMODE_MASK 0x7 +#define MCP_CONFIG_OPERATIONMODE_SHIFT 0x0 +#define MCP_CONFIG_OPERATIONMODE_NORMAL 0x0 + +#define SDW_CNL_MCP_CONTROL 0x4 +#define MCP_CONTROL_RESETDELAY_SHIFT 0x8 +#define MCP_CONTROL_CMDRST_SHIFT 0x7 +#define MCP_CONTROL_CMDRST_MASK 0x1 +#define MCP_CONTROL_SOFTRST_SHIFT 0x6 +#define MCP_CONTROL_SOFTCTRLBUSRST_SHIFT 0x5 +#define MCP_CONTROL_HARDCTRLBUSRST_SHIFT 0x4 +#define MCP_CONTROL_CLOCKPAUSEREQ_SHIFT 0x3 +#define MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT 0x2 +#define MCP_CONTROL_CLOCKSTOPCLEAR_MASK 0x1 +#define MCP_CONTROL_CMDACCEPTMODE_MASK 0x1 +#define MCP_CONTROL_CMDACCEPTMODE_SHIFT 0x1 +#define MCP_CONTROL_BLOCKWAKEUP_SHIFT 0x0 +#define MCP_CONTROL_BLOCKWAKEUP_MASK 0x1 + + +#define MCP_SLAVEINTMASK0_MASK 0xFFFFFFFF +#define MCP_SLAVEINTMASK1_MASK 0x0000FFFF + +#define SDW_CNL_MCP_CMDCTRL 0x8 +#define SDW_CNL_MCP_SSPSTAT 0xC +#define SDW_CNL_MCP_FRAMESHAPE 0x10 +#define SDW_CNL_MCP_FRAMESHAPEINIT 0x14 +#define SDW_CNL_MCP_CONFIGUPDATE 0x18 +#define MCP_CONFIGUPDATE_CONFIGUPDATE_SHIFT 0x0 +#define MCP_CONFIGUPDATE_CONFIGUPDATE_MASK 0x1 + +#define SDW_CNL_MCP_PHYCTRL 0x1C +#define SDW_CNL_MCP_SSPCTRL0 0x20 +#define SDW_CNL_MCP_SSPCTRL1 0x28 +#define SDW_CNL_MCP_CLOCKCTRL0 0x30 +#define SDW_CNL_MCP_CLOCKCTRL1 0x38 +#define SDW_CNL_MCP_STAT 0x40 +#define SDW_CNL_MCP_INTSTAT 0x44 +#define MCP_INTSTAT_IRQ_SHIFT 31 +#define MCP_INTSTAT_IRQ_MASK 1 +#define MCP_INTSTAT_WAKEUP_SHIFT 16 +#define MCP_INTSTAT_SLAVE_STATUS_CHANGED_SHIFT 12 +#define MCP_INTSTAT_SLAVE_STATUS_CHANGED_MASK 0xF +#define MCP_INTSTAT_SLAVENOTATTACHED_SHIFT 12 +#define MCP_INTSTAT_SLAVEATTACHED_SHIFT 13 +#define MCP_INTSTAT_SLAVEALERT_SHIFT 14 +#define MCP_INTSTAT_SLAVERESERVED_SHIFT 15 + +#define MCP_INTSTAT_DPPDIINT_SHIFT 11 +#define MCP_INTSTAT_DPPDIINTMASK 0x1 +#define MCP_INTSTAT_CONTROLBUSCLASH_SHIFT 10 +#define MCP_INTSTAT_CONTROLBUSCLASH_MASK 0x1 +#define MCP_INTSTAT_DATABUSCLASH_SHIFT 9 +#define MCP_INTSTAT_DATABUSCLASH_MASK 0x1 +#define MCP_INTSTAT_CMDERR_SHIFT 7 +#define MCP_INTSTAT_CMDERR_MASK 0x1 +#define MCP_INTSTAT_TXE_SHIFT 1 +#define MCP_INTSTAT_TXE_MASK 0x1 +#define MCP_INTSTAT_RXWL_SHIFT 2 +#define MCP_INTSTAT_RXWL_MASK 1 + +#define SDW_CNL_MCP_INTMASK 0x48 +#define MCP_INTMASK_IRQEN_SHIFT 31 +#define MCP_INTMASK_IRQEN_MASK 0x1 +#define MCP_INTMASK_WAKEUP_SHIFT 16 +#define MCP_INTMASK_WAKEUP_MASK 0x1 +#define MCP_INTMASK_SLAVERESERVED_SHIFT 15 +#define MCP_INTMASK_SLAVERESERVED_MASK 0x1 +#define MCP_INTMASK_SLAVEALERT_SHIFT 14 +#define MCP_INTMASK_SLAVEALERT_MASK 0x1 +#define MCP_INTMASK_SLAVEATTACHED_SHIFT 13 +#define MCP_INTMASK_SLAVEATTACHED_MASK 0x1 +#define MCP_INTMASK_SLAVENOTATTACHED_SHIFT 12 +#define MCP_INTMASK_SLAVENOTATTACHED_MASK 0x1 +#define MCP_INTMASK_DPPDIINT_SHIFT 11 +#define MCP_INTMASK_DPPDIINT_MASK 0x1 +#define MCP_INTMASK_CONTROLBUSCLASH_SHIFT 10 +#define MCP_INTMASK_CONTROLBUSCLASH_MASK 1 +#define MCP_INTMASK_DATABUSCLASH_SHIFT 9 +#define MCP_INTMASK_DATABUSCLASH_MASK 1 +#define MCP_INTMASK_CMDERR_SHIFT 7 +#define MCP_INTMASK_CMDERR_MASK 0x1 +#define MCP_INTMASK_TXE_SHIFT 1 +#define MCP_INTMASK_TXE_MASK 0x1 +#define MCP_INTMASK_RXWL_SHIFT 2 +#define MCP_INTMASK_RXWL_MASK 0x1 + +#define SDW_CNL_MCP_INTSET 0x4C +#define SDW_CNL_MCP_STAT 0x40 +#define MCP_STAT_ACTIVE_BANK_MASK 0x1 +#define MCP_STAT_ACTIVE_BANK_SHIT 20 +#define MCP_STAT_CLOCKSTOPPED_MASK 0x1 +#define MCP_STAT_CLOCKSTOPPED_SHIFT 16 + +#define SDW_CNL_MCP_SLAVESTAT 0x50 +#define MCP_SLAVESTAT_MASK 0x3 + +#define SDW_CNL_MCP_SLAVEINTSTAT0 0x54 +#define MCP_SLAVEINTSTAT_NOT_PRESENT_MASK 0x1 +#define MCP_SLAVEINTSTAT_ATTACHED_MASK 0x2 +#define MCP_SLAVEINTSTAT_ALERT_MASK 0x4 +#define MCP_SLAVEINTSTAT_RESERVED_MASK 0x8 + +#define SDW_CNL_MCP_SLAVEINTSTAT1 0x58 +#define SDW_CNL_MCP_SLAVEINTMASK0 0x5C +#define SDW_CNL_MCP_SLAVEINTMASK1 0x60 +#define SDW_CNL_MCP_PORTINTSTAT 0x64 +#define SDW_CNL_MCP_PDISTAT 0x6C + +#define SDW_CNL_MCP_FIFOLEVEL 0x78 +#define SDW_CNL_MCP_FIFOSTAT 0x7C +#define MCP_RX_FIFO_AVAIL_MASK 0x3F +#define SDW_CNL_MCP_COMMAND_BASE 0x80 +#define SDW_CNL_MCP_RESPONSE_BASE 0x80 +#define SDW_CNL_MCP_COMMAND_LENGTH 0x20 + +#define MCP_COMMAND_SSP_TAG_MASK 0x1 +#define MCP_COMMAND_SSP_TAG_SHIFT 31 +#define MCP_COMMAND_COMMAND_MASK 0x7 +#define MCP_COMMAND_COMMAND_SHIFT 28 +#define MCP_COMMAND_DEV_ADDR_MASK 0xF +#define MCP_COMMAND_DEV_ADDR_SHIFT 24 +#define MCP_COMMAND_REG_ADDR_H_MASK 0x7 +#define MCP_COMMAND_REG_ADDR_H_SHIFT 16 +#define MCP_COMMAND_REG_ADDR_L_MASK 0xFF +#define MCP_COMMAND_REG_ADDR_L_SHIFT 8 +#define MCP_COMMAND_REG_DATA_MASK 0xFF +#define MCP_COMMAND_REG_DATA_SHIFT 0x0 + +#define MCP_RESPONSE_RDATA_MASK 0xFF +#define MCP_RESPONSE_RDATA_SHIFT 8 +#define MCP_RESPONSE_ACK_MASK 0x1 +#define MCP_RESPONSE_ACK_SHIFT 0 +#define MCP_RESPONSE_NACK_MASK 0x2 + +#define SDW_CNL_DPN_CONFIG0 0x100 +#define SDW_CNL_DPN_CHANNELEN0 0x104 +#define SDW_CNL_DPN_SAMPLECTRL0 0x108 +#define SDW_CNL_DPN_OFFSETCTRL0 0x10C +#define SDW_CNL_DPN_HCTRL0 0x110 +#define SDW_CNL_DPN_ASYNCCTRL0 0x114 + +#define SDW_CNL_DPN_CONFIG1 0x118 +#define SDW_CNL_DPN_CHANNELEN1 0x11C +#define SDW_CNL_DPN_SAMPLECTRL1 0x120 +#define SDW_CNL_DPN_OFFSETCTRL1 0x124 +#define SDW_CNL_DPN_HCTRL1 0x128 + +#define SDW_CNL_PORTCTRL 0x130 +#define PORTCTRL_PORT_DIRECTION_SHIFT 0x7 +#define PORTCTRL_PORT_DIRECTION_MASK 0x1 +#define PORTCTRL_BANK_INVERT_SHIFT 0x8 +#define PORTCTRL_BANK_INVERT_MASK 0x1 + +#define SDW_CNL_PDINCONFIG0 0x1100 +#define SDW_CNL_PDINCONFIG1 0x1108 +#define PDINCONFIG_CHANNEL_MASK_SHIFT 0x8 +#define PDINCONFIG_CHANNEL_MASK_MASK 0xFF +#define PDINCONFIG_PORT_NUMBER_SHIFT 0x0 +#define PDINCONFIG_PORT_NUMBER_MASK 0x1F + +#define DPN_CONFIG_WL_SHIFT 0x8 +#define DPN_CONFIG_WL_MASK 0x1F +#define DPN_CONFIG_PF_MODE_SHIFT 0x0 +#define DPN_CONFIG_PF_MODE_MASK 0x3 +#define DPN_CONFIG_PD_MODE_SHIFT 0x2 +#define DPN_CONFIG_PD_MODE_MASK 0x3 +#define DPN_CONFIG_BPM_MASK 0x1 +#define DPN_CONFIG_BPM_SHIFT 0x12 +#define DPN_CONFIG_BGC_MASK 0x3 +#define DPN_CONFIG_BGC_SHIFT 0x10 + +#define DPN_SAMPLECTRL_SI_MASK 0xFFFF +#define DPN_SAMPLECTRL_SI_SHIFT 0x0 + +#define DPN_OFFSETCTRL0_OF1_MASK 0xFF +#define DPN_OFFSETCTRL0_OF1_SHIFT 0x0 +#define DPN_OFFSETCTRL0_OF2_MASK 0xFF +#define DPN_OFFSETCTRL0_OF2_SHIFT 0x8 + +#define DPN_HCTRL_HSTOP_MASK 0xF +#define DPN_HCTRL_HSTOP_SHIFT 0x0 +#define DPN_HCTRL_HSTART_MASK 0xF +#define DPN_HCTRL_HSTART_SHIFT 0x4 +#define DPN_HCTRL_LCONTROL_MASK 0x7 +#define DPN_HCTRL_LCONTROL_SHIFT 0x8 + +/* SoundWire Shim registers */ +#define SDW_CNL_LCAP 0x0 +#define SDW_CNL_LCTL 0x4 +#define CNL_LCTL_CPA_SHIFT 8 +#define CNL_LCTL_SPA_SHIFT 0 +#define CNL_LCTL_CPA_MASK 0x1 +#define CNL_LCTL_SPA_MASK 0x1 + +#define SDW_CNL_IPPTR 0x8 +#define SDW_CNL_SYNC 0xC +#define CNL_SYNC_CMDSYNC_MASK 0x1 +#define CNL_SYNC_CMDSYNC_SHIFT 16 +#define CNL_SYNC_SYNCGO_MASK 0x1 +#define CNL_SYNC_SYNCGO_SHIFT 0x18 + +#define SDW_CNL_CTLSCAP 0x10 +#define SDW_CNL_CTLS0CM 0x12 +#define SDW_CNL_CTLS1CM 0x14 +#define SDW_CNL_CTLS2CM 0x16 +#define SDW_CNL_CTLS3CM 0x18 + +#define SDW_CNL_PCMSCAP 0x20 +#define CNL_PCMSCAP_BSS_SHIFT 8 +#define CNL_PCMSCAP_BSS_MASK 0x1F +#define CNL_PCMSCAP_OSS_SHIFT 4 +#define CNL_PCMSCAP_OSS_MASK 0xF +#define CNL_PCMSCAP_ISS_SHIFT 0 +#define CNL_PCMSCAP_ISS_MASK 0xF + +#define SDW_CNL_PCMSCHM 0x22 +#define CNL_PCMSYCM_DIR_SHIFT 15 +#define CNL_PCMSYCM_DIR_MASK 0x1 +#define CNL_PCMSYCM_STREAM_SHIFT 8 +#define CNL_PCMSYCM_STREAM_MASK 0x3F +#define CNL_PCMSYCM_HCHAN_SHIFT 4 +#define CNL_PCMSYCM_HCHAN_MASK 0xF +#define CNL_PCMSYCM_LCHAN_SHIFT 0 +#define CNL_PCMSYCM_LCHAN_MASK 0xF + +#define SDW_CNL_PCMSCHC 0x42 + +#define SDW_CNL_PDMSCAP 0x62 +#define CNL_PDMSCAP_BSS_SHIFT 8 +#define CNL_PDMSCAP_BSS_MASK 0x1F +#define CNL_PDMSCAP_OSS_SHIFT 4 +#define CNL_PDMSCAP_OSS_MASK 0xF +#define CNL_PDMSCAP_ISS_SHIFT 0 +#define CNL_PDMSCAP_ISS_MASK 0xF +#define CNL_PDMSCAP_CPSS_SHIFT 13 +#define CNL_PDMSCAP_CPSS_MASK 0x7 +#define SDW_CNL_PDMSCM + +#define SDW_CNL_IOCTL 0x6C +#define CNL_IOCTL_MIF_SHIFT 0x0 +#define CNL_IOCTL_MIF_MASK 0x1 +#define CNL_IOCTL_CO_SHIFT 0x1 +#define CNL_IOCTL_CO_MASK 0x1 +#define CNL_IOCTL_COE_SHIFT 0x2 +#define CNL_IOCTL_COE_MASK 0x1 +#define CNL_IOCTL_DO_SHIFT 0x3 +#define CNL_IOCTL_DO_MASK 0x1 +#define CNL_IOCTL_DOE_SHIFT 0x4 +#define CNL_IOCTL_DOE_MASK 0x1 +#define CNL_IOCTL_BKE_SHIFT 0x5 +#define CNL_IOCTL_BKE_MASK 0x1 +#define CNL_IOCTL_WPDD_SHIFT 0x6 +#define CNL_IOCTL_WPDD_MASK 0x1 +#define CNL_IOCTL_CIBD_SHIFT 0x8 +#define CNL_IOCTL_CIBD_MASK 0x1 +#define CNL_IOCTL_DIBD_SHIFT 0x9 +#define CNL_IOCTL_DIBD_MASK 0x1 + +#define SDW_CNL_CTMCTL_OFFSET 0x60 +#define SDW_CNL_CTMCTL 0x6E +#define CNL_CTMCTL_DACTQE_SHIFT 0x0 +#define CNL_CTMCTL_DACTQE_MASK 0x1 +#define CNL_CTMCTL_DODS_SHIFT 0x1 +#define CNL_CTMCTL_DODS_MASK 0x1 +#define CNL_CTMCTL_DOAIS_SHIFT 0x3 +#define CNL_CTMCTL_DOAIS_MASK 0x3 + +#define ALH_CNL_STRMZCFG_BASE 0x4 +#define ALH_CNL_STRMZCFG_OFFSET 0x4 +#define CNL_STRMZCFG_DMAT_SHIFT 0x0 +#define CNL_STRMZCFG_DMAT_MASK 0xFF +#define CNL_STRMZCFG_DMAT_VAL 0x3 +#define CNL_STRMZCFG_CHAN_SHIFT 16 +#define CNL_STRMZCFG_CHAN_MASK 0xF + +#endif /* _LINUX_SDW_CNL_H */ diff --git a/drivers/sdw/sdw_maxim.c b/drivers/sdw/sdw_maxim.c new file mode 100644 index 000000000000..0081c5c00497 --- /dev/null +++ b/drivers/sdw/sdw_maxim.c @@ -0,0 +1,146 @@ +/* + * sdw_maxim.c -- Maxim SoundWire slave device driver. Dummy driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + + +static int maxim_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + int i, j; + + cap.wake_up_unavailable = true; + cap.test_mode_supported = false; + cap.clock_stop1_mode_supported = false; + cap.simplified_clock_stop_prepare = false; + cap.highphy_capable = true; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 0; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 3; + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 0; i < cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i]; + if (i == 0 || i == 2) + dpn_cap->port_direction = SDW_PORT_SOURCE; + else + dpn_cap->port_direction = SDW_PORT_SINK; + + dpn_cap->port_number = i+1; + dpn_cap->max_word_length = 24; + dpn_cap->min_word_length = 16; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_CP_SM; + dpn_cap->imp_def_intr_mask = 0x0; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT | + SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE | + SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 16000000; + prop->min_frequency = 1000000; + prop->num_freq_configs = 0; + prop->freq_supported = NULL; + prop->glitchless_transitions_mask = 0x1; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int maxim_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + dev_info(&sdw->dev, "Maxim SoundWire Slave Registered %lx\n", sdw_id->driver_data); + return maxim_register_sdw_capabilties(sdw, sdw_id); +} + +static int maxim_sdw_remove(struct sdw_slv *sdw) +{ + dev_info(&sdw->dev, "Maxim SoundWire Slave un-Registered\n"); + return 0; +} + +static const struct sdw_slv_id maxim_id[] = { + {"03:01:9f:79:00:00", 0}, + {"09:01:9f:79:00:00", 1}, + {"04:01:9f:79:00:00", 2}, + {"0a:01:9f:79:00:00", 3}, + {"04:01:9f:79:00:00", 4}, + {"0a:01:9f:79:00:00", 5}, + {"05:01:9f:79:00:00", 6}, + {"06:01:9f:79:00:00", 7}, + {"05:01:9f:79:00:00", 8}, + {"00:01:9f:79:00:00", 9}, + {"06:01:9f:79:00:00", 10}, + {"07:01:9f:79:00:00", 11}, + {"00:01:9f:79:00:00", 12}, + {"06:01:9f:79:00:00", 13}, + {"01:01:9f:79:00:00", 14}, + {"07:01:9f:79:00:00", 15}, + {"08:01:9f:79:00:00", 16}, + {"01:01:9f:79:00:00", 17}, + {"07:01:9f:79:00:00", 18}, + {"02:01:9f:79:00:00", 19}, + {"08:01:9f:79:00:00", 20}, + {"09:01:9f:79:00:00", 21}, + {"02:01:9f:79:00:00", 22}, + {"08:01:9f:79:00:00", 23}, + {"03:01:9f:79:00:00", 24}, + {"09:01:9f:79:00:00", 25}, + {"0a:01:9f:79:00:00", 26}, + {}, +}; + +MODULE_DEVICE_TABLE(sdwint, maxim_id); + +static struct sdw_slave_driver maxim_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "maxim", + }, + .probe = maxim_sdw_probe, + .remove = maxim_sdw_remove, + .id_table = maxim_id, +}; + +module_sdw_slave_driver(maxim_sdw_driver); + +MODULE_DESCRIPTION("SoundWire Maxim Slave Driver"); +MODULE_AUTHOR("Hardik Shah, "); +MODULE_LICENSE("GPL"); diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h new file mode 100644 index 000000000000..5ec3edc30d27 --- /dev/null +++ b/drivers/sdw/sdw_priv.h @@ -0,0 +1,243 @@ +/* + * sdw_priv.h - Private definition for sdw bus interface. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_PRIV_H +#define _LINUX_SDW_PRIV_H + +#include /* For kthread */ +#include + +#define SDW_MAX_STREAM_TAG_KEY_SIZE 80 +#define SDW_NUM_STREAM_TAGS 100 +#define MAX_NUM_ROWS 23 /* As per SDW Spec */ +#define MAX_NUM_COLS 8/* As per SDW Spec */ +#define MAX_NUM_ROW_COLS (MAX_NUM_ROWS * MAX_NUM_COLS) + +#define SDW_STATE_INIT_STREAM_TAG 0x1 +#define SDW_STATE_ALLOC_STREAM 0x2 +#define SDW_STATE_CONFIG_STREAM 0x3 +#define SDW_STATE_COMPUTE_STREAM 0x4 +#define SDW_STATE_PREPARE_STREAM 0x5 +#define SDW_STATE_ENABLE_STREAM 0x6 +#define SDW_STATE_DISABLE_STREAM 0x7 +#define SDW_STATE_UNPREPARE_STREAM 0x8 +#define SDW_STATE_UNCOMPUTE_STREAM 0x9 +#define SDW_STATE_RELEASE_STREAM 0xa +#define SDW_STATE_FREE_STREAM 0xb +#define SDW_STATE_FREE_STREAM_TAG 0xc +#define SDW_STATE_ONLY_XPORT_STREAM 0xd + +#define SDW_STATE_INIT_RT 0x1 +#define SDW_STATE_CONFIG_RT 0x2 +#define SDW_STATE_PREPARE_RT 0x3 +#define SDW_STATE_ENABLE_RT 0x4 +#define SDW_STATE_DISABLE_RT 0x5 +#define SDW_STATE_UNPREPARE_RT 0x6 +#define SDW_STATE_RELEASE_RT 0x7 + +struct sdw_runtime; +/* Defined in sdw.c, used by multiple files of module */ +extern struct sdw_core sdw_core; + +enum sdw_port_state { + SDW_PORT_STATE_CH_READY, + SDW_PORT_STATE_CH_STOPPED, + SDW_PORT_STATE_CH_PREPARING, + SDW_PORT_STATE_CH_DEPREPARING, +}; + +enum sdw_stream_state { + SDW_STREAM_ALLOCATED, + SDW_STREAM_FREE, + SDW_STREAM_ACTIVE, + SDW_STREAM_INACTIVE, +}; + +enum sdw_clk_state { + SDW_CLK_STATE_OFF = 0, + SDW_CLK_STATE_ON = 1, +}; + +struct port_chn_en_state { + bool is_activate; + bool is_bank_sw; +}; + +struct sdw_stream_tag { + int stream_tag; + struct mutex stream_lock; + int ref_count; + enum sdw_stream_state stream_state; + char key[SDW_MAX_STREAM_TAG_KEY_SIZE]; + struct sdw_runtime *sdw_rt; +}; + +struct sdw_stream_params { + unsigned int rate; + unsigned int channel_count; + unsigned int bps; +}; + +struct sdw_port_runtime { + int port_num; + enum sdw_port_state port_state; + int channel_mask; + /* Frame params and stream params are per port based + * Single stream of audio may be split + * into mutliple port each handling + * subset of channels, channels should + * be contiguous in subset + */ + struct sdw_transport_params transport_params; + struct sdw_port_params port_params; + struct list_head port_node; +}; + +struct sdw_slave_runtime { + /* Simplified port or full port, there cannot be both types of + * data port for single stream, so data structure is kept per + * slave runtime, not per port + */ + enum sdw_dpn_type type; + struct sdw_slv *slave; + int direction; + /* Stream may be split into multiple slaves, so this is for + * this particular slave + */ + struct sdw_stream_params stream_params; + struct list_head port_rt_list; + struct list_head slave_sdw_node; + struct list_head slave_node; + int rt_state; /* State of runtime structure */ + +}; + + +struct sdw_mstr_runtime { + struct sdw_master *mstr; + int direction; + /* Stream may be split between multiple masters so this + * is for invidual master, if stream is split into multiple + * streams. For calculating the bandwidth on the particular bus + * stream params of master is taken into account. + */ + struct sdw_stream_params stream_params; + struct list_head port_rt_list; + /* Two nodes are required because BW calculation is based on master + * while stream enabling is based on stream_tag, where multiple + * masters may be involved + */ + struct list_head mstr_sdw_node; /* This is to add mstr_rt in sdw_rt */ + struct list_head mstr_node; /* This is to add mstr_rt in mstr */ + + struct list_head slv_rt_list; + /* Individual stream bandwidth on given master */ + unsigned int stream_bw; + /* State of runtime structure */ + int rt_state; +}; + +struct sdw_runtime { + int tx_ref_count; + int rx_ref_count; + /* This is stream params for whole stream + * but stream may be split between two + * masters, or two slaves. + */ + struct sdw_stream_params stream_params; + struct list_head slv_rt_list; + struct list_head mstr_rt_list; + enum sdw_stream_type type; + int stream_state; + int xport_state; + +}; + +struct sdw_slv_status { + struct list_head node; + enum sdw_slave_status status[SOUNDWIRE_MAX_DEVICES+1]; +}; + +/** Bus structure which handles bus related information */ +struct sdw_bus { + struct list_head bus_node; + struct sdw_master *mstr; + unsigned int port_grp_mask[2]; + unsigned int slave_grp_mask[2]; + unsigned int clk_state; + unsigned int active_bank; + unsigned int clk_freq; + /* Bus total Bandwidth. Initialize and reset to zero */ + unsigned int bandwidth; + unsigned int system_interval; /* Bus System Interval */ + unsigned int frame_freq; + unsigned int col; + unsigned int row; + struct task_struct *status_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + struct list_head status_list; + spinlock_t spinlock; +}; + +/** Holds supported Row-Column combination related information */ +struct sdw_rowcol { + int row; + int col; + int control_bits; + int data_bits; +}; + +/** + * Global soundwire structure. It handles all the streams spawned + * across masters and has list of bus structure per every master + * registered + */ +struct sdw_core { + struct sdw_stream_tag stream_tags[SDW_NUM_STREAM_TAGS]; + struct sdw_rowcol rowcolcomb[MAX_NUM_ROW_COLS]; + struct list_head bus_list; + struct mutex core_lock; + struct idr idr; + int first_dynamic_bus_num; +}; + +/* Structure holding mapping of numbers to cols */ +struct sdw_num_to_col { + int num; + int col; +}; + +/* Structure holding mapping of numbers to rows */ +struct sdw_num_to_row { + int num; + int row; +}; + +int sdw_slave_port_config_port_params(struct sdw_slave_runtime *slv_rt); +int sdw_slave_port_prepare(struct sdw_slave_runtime, bool prepare); +int sdw_bus_bw_init(void); +int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); +int sdw_chn_enable(void); + +#endif /* _LINUX_SDW_PRIV_H */ diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 19c8efb9a5ee..a4b03e8cd694 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -4,6 +4,7 @@ menuconfig SOUNDWIRE bool "SoundWire support" + depends on !SDW ---help--- SoundWire is a 2-Pin interface with data and clock line ratified by the MIPI Alliance. SoundWire is used for transporting data diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 01797cb4587e..13619e910d6f 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -762,4 +762,17 @@ struct typec_device_id { kernel_ulong_t driver_data; }; +#define SOUNDWIRE_NAME_SIZE 64 +#define SOUNDWIRE_MODULE_PREFIX "sdw:" + +struct sdw_slv_id { + char name[SOUNDWIRE_NAME_SIZE]; + kernel_ulong_t driver_data; /* Data private to the driver */ +}; + +struct sdw_master_id { + char name[SOUNDWIRE_NAME_SIZE]; + kernel_ulong_t driver_data; /* Data private to the driver */ +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 379505a53722..035129bf1ac5 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -34,6 +34,7 @@ struct regmap_range_cfg; struct regmap_field; struct snd_ac97; struct sdw_slave; +struct sdw_slv; /* An enum of all the supported cache types */ enum regcache_type { @@ -561,6 +562,9 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *regmap_init_slave(struct sdw_slv *sdw, + const struct regmap_config *config); + struct regmap *__devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -610,6 +614,9 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *devm_regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config); + /* * Wrapper for regmap_init macros to include a unique lockdep key and name * for each call. No-op if CONFIG_LOCKDEP is not set. diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h new file mode 100644 index 000000000000..acf223cba595 --- /dev/null +++ b/include/linux/sdw/sdw_cnl.h @@ -0,0 +1,96 @@ +/* + * sdw_cnl.h - Shared header file for intel soundwire controller driver. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_CNL_H +#define _LINUX_SDW_CNL_H + +#include + + +#define SDW_CNL_PM_TIMEOUT 3000 /* ms */ + +#define CNL_SDW_MAX_PORTS 15 + +/* Maximum number hardware tries to send command if the command failed */ +#define CNL_SDW_MAX_CMD_RETRIES 15 +/* Standard allows 32 frames delay max between PREQ and Ping command + * We kept midway in hardware + */ +#define CNL_SDW_MAX_PREQ_DELAY 15 + +/* Reset Delay for hw controlled reset + * Reset length = 4096+(ResetDelay*256) clock cycles + */ +#define CNL_SDW_RESET_DELAY 15 + +#define CNL_SDW_SHIM_OFFSET 0x2C000 +#define CNL_SDW_LINK_0_OFFSET 0x30000 +#define CNL_SDW_LINK_1_OFFSET 0x40000 +#define CNL_SDW_LINK_2_OFFSET 0x50000 +#define CNL_SDW_LINK_3_OFFSET 0x60000 + +enum cnl_sdw_pdi_stream_type { + CNL_SDW_PDI_TYPE_PCM = 0, + CNL_SDW_PDI_TYPE_PDM = 1, +}; + +struct cnl_sdw_pdi_stream { + int pdi_num; + int sdw_pdi_num; + int ch_cnt; + bool allocated; + int port_num; + enum sdw_data_direction direction; + int h_ch_num, l_ch_num; + struct list_head node; + +}; + +struct cnl_sdw_port { + int port_num; + int allocated; + bool port_type; + int ch_cnt; + enum sdw_data_direction direction; + struct cnl_sdw_pdi_stream *pdi_stream; +}; + +struct cnl_sdw_data { + /* SoundWire IP registers per instance */ + void __iomem *sdw_regs; + /* SoundWire shim registers */ + void __iomem *sdw_shim; + /* This is just for enaling SoundWire interrupts */ + void __iomem *alh_base; + /* HDA interrupt */ + int irq; + /* Instance id */ + int inst_id; +}; + +struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, + enum sdw_data_direction direction, + enum cnl_sdw_pdi_stream_type stream_type); +void cnl_sdw_free_port(struct sdw_master *mstr, int port_num); + + +#endif + diff --git a/include/linux/sdw/sdw_registers.h b/include/linux/sdw/sdw_registers.h new file mode 100644 index 000000000000..1abdf4bf863a --- /dev/null +++ b/include/linux/sdw/sdw_registers.h @@ -0,0 +1,157 @@ +/* sdw_registers.h - SoundWire MIPI spec 1.0 defined SoundWire slave + * register definition file. This defines the register + * offsets, bit masks and shifts in accordance with + * mipi spec 1.0 + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_SDW_REG_H +#define _LINUX_SDW_REG_H + +#define SDW_NUM_DATA_PORT_REGISTERS 0x100 +#define SDW_BANK1_REGISTER_OFFSET 0x10 + +#define SDW_DP0_INTSTAT 0x0 +#define SDW_DP0_INTSTAT_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTSTAT_TEST_FAIL_SHIFT 0x2 +#define SDW_DP0_INTSTAT_PORT_READY_MASK 0x4 +#define SDW_DP0_INTSTAT_PORT_READY_SHIFT 0x20 +#define SDW_DP0_INTSTAT_BRA_FAILURE_MASK 0x40 +#define SDW_DP0_INTSTAT_BRA_FAILURE_SHIFT 0x80 + +#define SDW_DP0_INTCLEAR 0x0 +#define SDW_DP0_INTCLEAR_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTCLEAR_PORT_READY_MASK 0x2 +#define SDW_DP0_INTCLEAR_BRA_FAILURE_MASK 0x4 +#define SDW_DP0_INTSTAT_IMPDEF1_MASK 0x20 +#define SDW_DP0_INTSTAT_IMPDEF2_MASK 0x40 +#define SDW_DP0_INTSTAT_IMPDEF3_MASK 0x80 + +#define SDW_DP0_INTMASK 0x1 +#define SDW_DP0_INTMASK_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTMASK_PORT_READY_MASK 0x2 +#define SDW_DP0_INTMASK_BRA_FAILURE_MASK 0x4 +#define SDW_DP0_INTMASK_IMPDEF1_MASK 0x20 +#define SDW_DP0_INTMASK_IMPDEF2_MASK 0x40 +#define SDW_DP0_INTMASK_IMPDEF3_MASK 0x80 + +#define SDW_DP0_PORTCTRL 0x2 +#define SDW_DP0_PORTCTRL_PORTDATAMODE_MASK 0x3 +#define SDW_DP0_PORTCTRL_PORTDATAMODE_SHIFT 2 +#define SDW_DP0_PORTCTRL_NEXTINVERTBANK_MASK 0x1 +#define SDW_DP0_PORTCTRL_NEXTINVERTBANK_SHIFT 4 + +#define SDW_DP0_BLOCKCTRL1 0x3 + +#define SDW_DP0_PREPARESTATUS 0x4 + +#define SDW_DP0_PREPARECTRL 0x5 + +#define SDW_DP0_CHANNELEN 0x20 +#define SDW_DP0_SAMPLECTRL1 0x22 +#define SDW_DP0_SAMPLECTRL2 0x23 +#define SDW_DP0_OFFSETCTRL1 0x24 +#define SDW_DP0_OFFSETCTRL2 0x25 +#define SDW_DP0_HCTRL 0x26 +#define SDW_DP0_LANECTRL 0x28 + +#define SDW_SCP_INTSTAT_1 0x40 +#define SDW_SCP_INTSTAT1_PARITY_MASK 0x1 +#define SDW_SCP_INTSTAT1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK 0x80 + +#define SDW_SCP_INTCLEAR1 0x40 +#define SDW_SCP_INTCLEAR1_PARITY_MASK 0x1 +#define SDW_SCP_INTCLEAR1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTCLEAR1_SCP2_CASCADE_MASK 0x80 +#define SDW_SCP_INTMASK1 +#define SDW_SCP_INTSTAT2 0x42 +#define SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK 0x80 +#define SDW_SCP_INTSTAT3 0x43 +#define SDW_SCP_CTRL 0x44 +#define SDW_SCP_CTRL_CLK_STP_NOW_MASK 0x1 +#define SDW_SCP_CTRL_CLK_STP_NOW_SHIFT 0x1 +#define SDW_SCP_STAT 0x44 +#define SDW_SCP_STAT_CLK_STP_NF_MASK 0x1 +#define SDW_SCP_SYSTEMCTRL 0x45 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT 0x0 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT 0x1 +#define SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT 0x2 +#define SDW_SCP_SYSTEMCTRL_HIGH_PHY_SHIFT 0x3 + +#define SDW_SCP_DEVNUMBER 0x46 +#define SDW_SCP_HIGH_PHY_CHECK 0x47 +#define SDW_SCP_ADDRPAGE1 0x48 +#define SDW_SCP_ADDRPAGE2 0x49 +#define SDW_SCP_KEEPEREN 0x4A +#define SDW_SCP_BANKDELAY 0x4B +#define SDW_SCP_TESTMODE 0x4F +#define SDW_SCP_DEVID_0 0x50 +#define SDW_SCP_DEVID_1 0x51 +#define SDW_SCP_DEVID_2 0x52 +#define SDW_SCP_DEVID_3 0x53 +#define SDW_SCP_DEVID_4 0x54 +#define SDW_SCP_DEVID_5 0x55 + +/* Banked Registers */ +#define SDW_SCP_FRAMECTRL 0x60 +#define SDW_SCP_NEXTFRAME 0x61 + +#define SDW_DPN_INTSTAT 0x0 +#define SDW_DPN_INTSTAT_TEST_FAIL_MASK 0x1 +#define SDW_DPN_INTSTAT_PORT_READY_MASK 0x2 +#define SDW_DPN_INTSTAT_IMPDEF1_MASK 0x20 +#define SDW_DPN_INTSTAT_IMPDEF2_MASK 0x40 +#define SDW_DPN_INTSTAT_IMPDEF3_MASK 0x80 + +#define SDW_DPN_INTCLEAR 0x0 +#define SDW_DPN_INTCLEAR_TEST_FAIL_MASK 0x1 +#define SDW_DPN_INTCLEAR_PORT_READY_MASK 0x2 +#define SDW_DPN_INTCLEAR_IMPDEF1_MASK 0x20 +#define SDW_DPN_INTCLEAR_IMPDEF2_MASK 0x40 +#define SDW_DPN_INTCLEAR_IMPDEF3_MASK 0x80 + +#define SDW_DPN_INTMASK 0x1 +#define SDW_DPN_PORTCTRL 0x2 +#define SDW_DPN_PORTCTRL_PORTFLOWMODE_MASK 0x3 +#define SDW_DPN_PORTCTRL_PORTFLOWMODE_SHIFT 0 +#define SDW_DPN_PORTCTRL_PORTDATAMODE_MASK 0x3 +#define SDW_DPN_PORTCTRL_PORTDATAMODE_SHIFT 2 +#define SDW_DPN_PORTCTRL_NEXTINVERTBANK_MASK 0x1 +#define SDW_DPN_PORTCTRL_NEXTINVERTBANK_SHIFT 4 + +#define SDW_DPN_BLOCKCTRL1 0x3 +#define SDW_DPN_BLOCKCTRL1_WORDLENGTH_MASK 0x3F +#define SDW_DPN_BLOCKCTRL1_WORDLENGTH_SHIFT 0 + +#define SDW_DPN_PREPARESTATUS 0x4 +#define SDW_DPN_PREPARECTRL 0x5 +#define SDW_DPN_PREPARECTRL_CH_PREPARE_MASK 0xFF + +#define SDW_DPN_CHANNELEN 0x20 +#define SDW_DPN_BLOCKCTRL2 0x21 +#define SDW_DPN_SAMPLECTRL1 0x22 +#define SDW_DPN_SAMPLECTRL1_LOW_MASK 0xFF +#define SDW_DPN_SAMPLECTRL2 0x23 +#define SDW_DPN_SAMPLECTRL2_LOW_MASK 0xFF00 +#define SDW_DPN_OFFSETCTRL1 0x24 +#define SDW_DPN_OFFSETCTRL2 0x25 +#define SDW_DPN_HCTRL 0x26 +#define SDW_DPN_HCTRL_HSTART_MASK 0xF +#define SDW_DPN_HCTRL_HSTOP_MASK 0xF +#define SDW_DPN_HCTRL_HSTART_SHIFT 4 +#define SDW_DPN_HCTRL_HSTOP_SHIFT 0 +#define SDW_DPN_BLOCKCTRL3 0x27 +#define SDW_DPN_LANECTRL 0x28 + +#endif diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h new file mode 100644 index 000000000000..01c846b0c695 --- /dev/null +++ b/include/linux/sdw_bus.h @@ -0,0 +1,1354 @@ +/* + * sdw_bus.h - Definition for SoundWire bus interface. + * + * This header file refers to the MIPI SoundWire 1.0. The comments try to + * follow the same conventions with a capital letter for all standard + * definitions such as Master, Slave, Data Port, etc. When possible, the + * constant numeric values are kept the same as in the MIPI specifications + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef _LINUX_SDW_BUS_H +#define _LINUX_SDW_BUS_H + +#include /* for struct device */ +#include /* For Name size */ +#include /* For rt mutex */ + + +#define SOUNDWIRE_MAX_DEVICES 11 + +#define SDW_NUM_DEV_ID_REGISTERS 6 + +/* Port flow mode, used to indicate what port flow mode + * slave supports + */ +#define SDW_PORT_FLOW_MODE_ISOCHRONOUS 0x1 +#define SDW_PORT_FLOW_MODE_TX_CONTROLLED 0x2 +#define SDW_PORT_FLOW_MODE_RX_CONTROLLED 0x4 +#define SDW_PORT_FLOW_MODE_ASYNCHRONOUS 0x8 + +/* Bit-mask used to indicate Port capability, OR both bits if + * Port is bidirectional capable + */ +#define SDW_PORT_SOURCE 0x1 +#define SDW_PORT_SINK 0x2 + +/* Mask to specify what type of sample packaging mode + * is supported by port + */ +#define SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK 0x1 +#define SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK 0x2 + +/* Mask to specify data encoding supported by port */ +#define SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT 0x1 +#define SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE 0x2 +#define SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT 0x4 + +/* enum sdw_driver_type: There are different driver callbacks for slave and + * master. This is to differentiate between slave driver + * and master driver. Bus driver binds master driver to + * master device and slave driver to slave device using + * this field. Driver populates this field based on whether + * its handling slave or master device. + */ +enum sdw_driver_type { + SDW_DRIVER_TYPE_MASTER = 0, + SDW_DRIVER_TYPE_SLAVE = 1, +}; + +/** + * enum sdw_block_pkg_mode: Block packing mode for the port. + * @SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT: Block packing per port + * @SDW_PORT_BLK_PKG_MODE_BLK_PER_CH: Block packing per channel. + */ +enum sdw_block_pkg_mode { + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT = 0, + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH = 1, +}; + + +/** + * enum sdw_command_response: Data Port type + * @SDW_COMMAND_OK: Command is Ok. + * @SDW_COMMAND_IGNORED: Command is ignored. + * @SDW_COMMAND_FAILED: Command failed. + */ +enum sdw_command_response { + SDW_COMMAND_OK = 0, + SDW_COMMAND_IGNORED = 1, + SDW_COMMAND_FAILED = 2, +}; +/** + * enum sdw_dpn_type: Data Port type + * @SDW_FULL_DP: Full Data Port supported. + * @SDW_SIMPLIFIED_DP: Simplified Data Port. Following registers are not + * implemented by simplified data port + * DPN_SampleCtrl2, DPN_OffsetCtrl2, DPN_HCtrl and + * DPN_BlockCtrl3 + */ +enum sdw_dpn_type { + SDW_FULL_DP = 0, + SDW_SIMPLIFIED_DP = 1, +}; + +/** + * enum sdw_dpn_grouping: Maximum block group count supported. + * @SDW_BLOCKGROUPCOUNT_1: Maximum Group count 1 supported. + * @SDW_BLOCKGROUPCOUNT_2: Maximum Group count 2 supported. + * @SDW_BLOCKGROUPCOUNT_3: Maximum Group count 3 supported. + * @SDW_BLOCKGROUPCOUNT_4: Maximum Group count 4 supported. + */ +enum sdw_dpn_grouping { + SDW_BLOCKGROUPCOUNT_1 = 0, + SDW_BLOCKGROUPCOUNT_2 = 1, + SDW_BLOCKGROUPCOUNT_3 = 2, + SDW_BLOCKGROUPCOUNT_4 = 3, +}; + +/** + * enum sdw_prep_ch_behavior: Specifies the dependencies between + * Channel Prepare sequence and bus + * clock configuration.This property is not + * required for ports implementing a + * Simplified ChannelPrepare State Machine (SCPSM) + * @SDW_CH_PREP_ANY_TIME: Channel Prepare can happen at any bus clock rate + * @SDW_CH_PREP_AFTER_BUS_CLK_CHANGE: : Channel Prepare sequence needs to + * happen after bus clock is changed to a + * frequency supported by this mode or + * compatible modes described by the next field. + * This may be required, e.g. when the Slave + * internal audio clocks are derived from the + * bus clock. + */ +enum sdw_prep_ch_behavior { + SDW_CH_PREP_ANY_TIME = 0, + SDW_CH_PREP_AFTER_BUS_CLK_CHANGE = 1, +}; + +/** + * enum sdw_slave_status: Slave status reported in PING frames + * @SDW_SLAVE_STAT_NOT_PRESENT: Slave is not present. + * @SDW_SLAVE_STAT_ATTACHED_OK: Slave is Attached to the bus. + * @SDW_SLAVE_STAT_ALERT: Some alert condition on the Slave. + * @SDW_SLAVE_STAT_RESERVED: Reserved. + */ +enum sdw_slave_status { + SDW_SLAVE_STAT_NOT_PRESENT = 0, + SDW_SLAVE_STAT_ATTACHED_OK = 1, + SDW_SLAVE_STAT_ALERT = 2, + SDW_SLAVE_STAT_RESERVED = 3, +}; + +enum sdw_stream_type { + SDW_STREAM_PCM = 0, + SDW_STREAM_PDM = 1, +}; + + +enum sdw_rt_state { + SDW_RT_INITIALIZED = 0, + SDW_RT_CONFIGURED = 1, +}; + +/** + * enum sdw_ch_prepare_mode: Channel prepare mode. + * @SDW_SIMPLIFIED_CP_SM: Simplified channel prepare. + * @SDW_CP_SM: Normal channel prepare. + */ +enum sdw_ch_prepare_mode { + SDW_SIMPLIFIED_CP_SM = 0, + SDW_CP_SM = 1, +}; + +/** + * enums dfw_clk_stop_prepare: Clock Stop prepare mode. + * @SDW_CLOCK_STOP_MODE_0: Clock Stop mode 0 + * @SDW_CLOCK_STOP_MODE_1: Clock Stop mode 1 + */ +enum sdw_clk_stop_mode { + SDW_CLOCK_STOP_MODE_0 = 0, + SDW_CLOCK_STOP_MODE_1 = 1, +}; + +/** + * enum sdw_data_direction: Data direction w.r.t Port. For e.g for playback + * between the Master and Slave, where Slave + * is codec, data direction for the Master + * port will be OUT, since its transmitting + * the data, while for the Slave (codec) it + * will be IN, since its receiving the data. + * @SDW_DATA_DIR_IN: Data is input to Port. + * @SDW_DATA_DIR_OUT: Data is output from Port. + */ +enum sdw_data_direction { + SDW_DATA_DIR_IN = 0, + SDW_DATA_DIR_OUT = 1, +}; + +/* Forward declaration of the data structures */ +struct sdw_master; +struct sdw_slv; +struct sdw_msg; +struct sdw_bra_block; +struct sdw_mstr_driver; + +/** + * struct port_audio_mode_properties: Audio properties for the Port + * + * @max_frequency: Maximum frequency Port can support for the clock. + * The use of max_ and min_ frequency requires num_freq_config + * to be zero + * @min_frequency: Minimum frequency Port can support for the clock. + * @num_freq_configs: Array size for the frequencies supported by Port. + * @freq_supported: Array of frequencies supported by the Port. + * @glitchless_transitions_mask: Glitch transition mask from one mode to + * other mode. Each bit refers to a mode + * number. + */ + +struct port_audio_mode_properties { + unsigned int max_frequency; + unsigned int min_frequency; + unsigned int num_freq_configs; + unsigned int *freq_supported; + unsigned int max_sampling_frequency; + unsigned int min_sampling_frequency; + unsigned int num_sampling_freq_configs; + unsigned int *sampling_freq_config; + enum sdw_prep_ch_behavior ch_prepare_behavior; + unsigned int glitchless_transitions_mask; +}; + +/** + * struct sdw_slv_addr: Structure representing the device_id and + * and SoundWire logical Slave address. + * @dev_id: 6-byte device id of the Slave + * @slv_number: Logical SoundWire Slave number, in the range [1..11] + * @assigned: Logical address is assigned to some Slave or not + * @status: What is the current state of the slave. + * + */ +struct sdw_slv_addr { + struct sdw_slv *slave; + u8 dev_id[SDW_NUM_DEV_ID_REGISTERS]; + u8 slv_number; + bool assigned; + enum sdw_slave_status status; +}; + +/** + * struct sdw_slv_dpn_capabilities: Capabilities of the Data Port, other than + * Data Port 0 for SoundWire Slave + * @port_direction: Direction of the Port. Sink or Source or bidirectional. + * Set appropriate bit mak based on port capabilities. + * @port_number: Port number. + * @max_word_length: Maximum length of the sample word. + * @min_word_length: Minimum length of sample word. + * @num_word_length: Length of supported word length buffer. + * The use of max_ and min_ word length requires + * num_word_length to be zero + * @word_length_buffer: Array of the supported word length. + * @dpn_type: Type of Data Port. Simplified or Normal data port. + * @dpn_grouping: Max Block group count supported for this Port. + * @prepare_ch: Channel prepare scheme. Simplified channel prepare or Normal + * channel prepare. + * @imp_def_intr_mask: Implementation defined interrupt mask. + * @min_ch_num: Minimum number of channels supported. + * @max_ch_num: Maximum number of channels supported. + * @num_ch_supported: Buffer length for the channels supported. + * The use of max_ and min_ ch_num requires + * num_ch_supported to be zero + * @ch_supported: Array of the channel supported. + * @port_flow_mode_mask: Transport flow modes supported by Port. + * @block_packing_mode_mask: Block packing mode mask. + * @port_encoding_type_mask: Port Data encoding type mask. + * @num_audio_modes: Number of audio modes supported by device. + * @mode_properties: Port audio mode properties buffer of size num_audio_modes + */ + +struct sdw_slv_dpn_capabilities { + unsigned int port_direction; + unsigned int port_number; + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + enum sdw_dpn_type dpn_type; + enum sdw_dpn_grouping dpn_grouping; + enum sdw_ch_prepare_mode prepare_ch; + unsigned int imp_def_intr_mask; + unsigned int min_ch_num; + unsigned int max_ch_num; + unsigned int num_ch_supported; + unsigned int *ch_supported; + unsigned int port_flow_mode_mask; + unsigned int block_packing_mode_mask; + unsigned int port_encoding_type_mask; + unsigned int num_audio_modes; + struct port_audio_mode_properties *mode_properties; +}; + +/** + * struct sdw_slv_bra_capabilities: BRA Capabilities of the Slave. + * @max_bus_frequency: Maximum bus frequency of this mode, in Hz + * @min_bus_frequency: Minimum bus frequency of this mode, in Hz + * When using min-max properties, all values in the defined + * range are allowed. Use the config list in the next field + * if only discrete values are supported. + * @num_bus_config_frequency: Number of discrete bus frequency configurations + * @bus_config_frequencies: Array of bus frequency configs. + * @max_data_per_frame: Maximum Data payload, in bytes per frame. + * Excludes header, CRC, footer. Maximum value is 470 + * @min_us_between_transactions: Amount of delay, in microseconds, + * required to be inserted between BRA transactions. + * Use if Slave needs idle time between BRA transactions. + * @max_bandwidth: Maximum bandwidth (in bytes/s) that can be written/read + * (header, CRCs, footer excluded) + * @mode_block_alignment: Size of basic block in bytes. The Data payload + * size needs to be a multiple of this basic block and + * padding/repeating of the same value is required for + * transactions smaller than this basic block. + */ + +struct sdw_slv_bra_capabilities { + unsigned int max_bus_frequency; + unsigned int min_bus_frequency; + unsigned int num_bus_config_frequency; + unsigned int *bus_config_frequencies; + unsigned int max_data_per_frame; + unsigned int min_us_between_transactions; + unsigned int max_bandwidth; + unsigned int mode_block_alignment; +}; + +/** + * struct sdw_slv_dp0_capabilities: Capabilities of the Data Port 0 of Slave. + * + * @max_word_length: Maximum word length supported by the Data Port. + * @min_word_length: Minimum word length supported by the Data Port. + * @num_word_length: Array size of the buffer containing the supported + * word lengths. + * The use of max_ and min_ word length requires + * num_word_length to be zero + * @word_length_buffer: Array containing supported word length. + * @bra_use_flow_control: Flow control is required or not for bra block + * transfer. + * @bra_initiator_supported: Can Slave be BRA initiator. + * @ch_prepare_mode: Type of channel prepare scheme. Simplified or Normal + * channel prepare. + * @impl_def_response_supported;: If True (nonzero), implementation-defined + * response is supported. This information may be used + * by a device driver to request that a generic bus + * driver forwards the response to the client device + * driver. + * @imp_def_intr_mask: Implementation defined interrupt mask for DP0 Port. + * @impl_def_bpt_supported: If True (nonzero), implementation-defined + * Payload Type is supported. This information is used + * to bypass the BRA protocol and may only be of + * interest when a device driver is aware of the + * Capabilities of the Master controller and Slave + * devices. + * @slave_bra_cap: BRA capabilities of the Slave. + */ + +struct sdw_slv_dp0_capabilities { + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + unsigned int bra_use_flow_control; + bool bra_initiator_supported; + enum sdw_ch_prepare_mode ch_prepare_mode; + bool impl_def_response_supported; + unsigned int imp_def_intr_mask; + bool impl_def_bpt_supported; + struct sdw_slv_bra_capabilities slave_bra_cap; +}; + +/** struct sdw_slv_capabilities: Capabilities of the SoundWire Slave. This + * is public structure for slave drivers to + * updated its capability to bus driver. + * + * @wake_up_unavailable: Slave is capable of waking up the Master. + * @test_mode_supported: Slave supports test modes. + * @clock_stop1_mode_supported: Clock stop 1 mode supported by this Slave. + * @simplified_clock_stop_prepare: Simplified clock stop prepare + * supported. + * @highphy_capable: Slave is highphy_capable or not? + * @paging_supported: Paging registers supported for Slave? + * @bank_delay_support: Bank switching delay for Slave + * @port_15_read_behavior: Slave behavior when the Master attempts a Read to + * the Port15 alias + * 0: Command_Ignored + * 1: Command_OK, Data is OR of all registers + * @sdw_dp0_supported: DP0 is supported by Slave. + * @sdw_dp0_cap: Data Port 0 Capabilities of the Slave. + * @num_of_sdw_ports: Number of SoundWire Data ports present. The representation + * assumes contiguous Port numbers starting at 1. + * @sdw_dpn_cap: Capabilities of the SoundWire Slave ports. + */ + +struct sdw_slv_capabilities { + bool wake_up_unavailable; + bool test_mode_supported; + bool clock_stop1_mode_supported; + bool simplified_clock_stop_prepare; + bool highphy_capable; + bool paging_supported; + bool bank_delay_support; + unsigned int port_15_read_behavior; + bool sdw_dp0_supported; + struct sdw_slv_dp0_capabilities *sdw_dp0_cap; + int num_of_sdw_ports; + struct sdw_slv_dpn_capabilities *sdw_dpn_cap; +}; + + +/** + * struct sdw_slv: Represents SoundWire Slave device + * (similar to 'i2c_client' on I2C) + * This is not public structure. Maintained by + * bus driver internally. + * @dev: Driver model representation of the device + * @slave_cap_updated: Did slave device driver updated slave capabilties + * to bus. + * @name: Name of the driver to use with the device. + * @dev_id: 6-byte unique device identification. + * @driver: Slave's driver, pointer to access routine. + * @mstr: SoundWire Master, managing the bus on which this Slave is + * @slv_number: Logical address of the Slave, assigned by bus driver + * @node: Node to add the Slave to the list of Slave devices managed + * by same Master. + * @port_ready: Port ready completion flag for each Port of the Slave; + * @sdw_slv_cap: Slave Capabilities. + */ +struct sdw_slv { + struct device dev; + bool slave_cap_updated; + char name[SOUNDWIRE_NAME_SIZE]; + u8 dev_id[6]; + struct sdw_slv_addr *slv_addr; + struct sdw_slave_driver *driver; + struct sdw_master *mstr; + u8 slv_number; + struct list_head node; + struct completion *port_ready; + struct sdw_slv_capabilities sdw_slv_cap; +}; +#define to_sdw_slave(d) container_of(d, struct sdw_slv, dev) + +/** + * struct sdw_bus_params: Bus params for the Slave to be ready for next + * bus changes. + * @num_rows: Number of rows in new frame to be effective. + * @num_cols: Number of columns in new frame to be effective. + * @bus_clk_freq: Clock frequency for the bus. + * @bank: Register bank, which Slave driver should program for + * implementation define Slave registers. This is the + * inverted value of the current bank. + */ + +struct sdw_bus_params { + int num_rows; + int num_cols; + int bus_clk_freq; + int bank; +}; + +/** + * struct sdw_slave_driver: Manage SoundWire generic/Slave device driver + * @driver_type: To distinguish between master and slave driver. Set and + * used by bus driver. + * @probe: Binds this driver to a SoundWire Slave. + * @remove: Unbinds this driver from the SoundWire Slave. + * @shutdown: Standard shutdown callback used during powerdown/halt. + * @suspend: Standard suspend callback used during system suspend + * @resume: Standard resume callback used during system resume + * @driver: Generic driver structure, according to driver model. + * @handle_impl_def_interrupts: Slave driver callback, for status of the + * Slave other than "REPORT_PRESENT". There may be + * jack detect, pll locked kind of status update + * interrupt required by Slave, which Slave need to + * handle in impl_defined way, using implementation + * defined interrupts. This is callback function to + * Slave to handle implementation defined interrupts. + * @handle_bus_changes: Slave callback function to let Slave configure + * implementation defined registers prior to any bus + * configuration changes. Bus configuration changes + * will be signaled by a bank switch initiated by the bus + * driver once all Slaves drivers have performed their + * imp-def configuration sequence (if any). + * If this callback is not implemented the bus driver + * will assume the Slave can tolerate bus configurations + * changes at any time. + * + * @handle_pre_port_prepare: Slave driver callback to allow Slave Port to be + * prepared by configuring impl defined register + * as part of Port prepare state machine. + * This fn is called before DPn_Prepare ctrl is + * written. Before this function is + * called Port state is un-prepared (CP_Stopped). + * This is optional based on any impl + * defined register needs to be set by Slave + * driver before Port is prepared. + * @handle_post_port_prepare: Slave driver callback to allow Slave Port to be + * prepared by configuring impl defined register + * as part of Port prepare state machine. + * This is called after DPn_Prepare + * ctrl is written, and DPn_status reports as + * Port prepared(CP_Ready). This is optional + * based on any impl defined register needs to + * be set by Slave driver once Port is ready. + * @handle_pre_port_unprepare: Slave driver callback to allow Slave Port to be + * un-prepared by configuring impl defined register + * as part of Port un-prepare state machine. + * This is called before DPn_Prepare ctrl is + * written. Before this function is called + * Port state is ready (CP_Ready). + * This is optional based on any impl + * defined register needs to be set by Slave + * driver before Port is un-prepared. + * @handle_post_port_unprepare: Slave driver callback to allow Slave Port to be + * un-prepared by configuring impl defined register + * as part of Port prepare state machine. + * This is called after DPn_Prepare + * ctrl is written, and DPn_status reports as + * Port un-prepared (CP_Stopped). + * This is optional based on any impl defined + * register needs to be set by Slave driver once + * Port is un-prepared. + * + * @id_table: List of SoundWire Slaves supported by this driver + */ +struct sdw_slave_driver { + enum sdw_driver_type driver_type; + int (*probe)(struct sdw_slv *swdev, const struct sdw_slv_id *); + int (*remove)(struct sdw_slv *swdev); + void (*shutdown)(struct sdw_slv *swdev); + int (*suspend)(struct sdw_slv *swdev, + pm_message_t pmesg); + int (*resume)(struct sdw_slv *swdev); + struct device_driver driver; + int (*handle_impl_def_interrupts)(struct sdw_slv *swdev, + struct sdw_impl_def_intr_stat *intr_status); + int (*handle_bus_changes)(struct sdw_slv *swdev, + struct sdw_bus_params *params); + int (*handle_pre_port_prepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_post_port_prepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_pre_port_unprepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_post_port_unprepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + const struct sdw_slv_id *id_table; +}; +#define to_sdw_slave_driver(d) container_of(d, struct sdw_slave_driver, driver) + +/** + * struct sdw_mstr_dpn_capabilities: Capabilities of the Data Port, other than + * Data Port 0 for SoundWire Master + * @port_direction: Direction of the Port. + * @port_number: Port number. + * @max_word_length: Maximum length of the sample word. + * @min_word_length: Minimum length of sample word. + * @num_word_length: Length of supported word length buffer. This should be + * 0 in order to use min and max. + * @word_length_buffer: Array of the supported word length. + * @dpn_type: Type of Data Port. + * @dpn_grouping: Max Block count grouping supported for this Port. if + * slave supports only 1 block group count, than DPN_BlockCtrl2 + * wont be programmed. + * @min_ch_num: Minimum number of channels supported. + * @max_ch_num: Maximum number of channels supported. + * @num_ch_supported: Buffer length for the channels supported.This should be + * 0 in order to use min and max. + * @ch_supported: Array of the channel supported. + * @port_mode_mask: Transport modes supported by Port. + * @block_packing_mode_mask: Block packing mode mask. + */ + +struct sdw_mstr_dpn_capabilities { + unsigned int port_direction; + unsigned int port_number; + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + enum sdw_dpn_type dpn_type; + enum sdw_dpn_grouping dpn_grouping; + unsigned int min_ch_num; + unsigned int max_ch_num; + unsigned int num_ch_supported; + unsigned int *ch_supported; + unsigned int port_mode_mask; + unsigned int block_packing_mode_mask; +}; + +/** + * struct sdw_mstr_dp0_capabilities: Capabilities of the Data Port 0 of Slave. + * + * @max_word_length: Maximum word length supported by the Data Port. + * @min_word_length: Minimum word length supported by the Data Port. + * @num_word_length: Array size of the buffer containing the supported + * word lengths. + * @word_length_buffer: Array containing supported word length. + * @bra_max_data_per_frame: Maximum Data size per BRA. + */ +struct sdw_mstr_dp0_capabilities { + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + unsigned int bra_max_data_per_frame; +}; + +/** + * struct sdw_master_capabilities: Capabilities of the Master. + * This is filled by the software registering Master. + * @base_clk_freq: Highest base frequency at which Master can be driven + * This is in Hz. + * @monitor_handover_supported: Does Master support monitor handover. + * @highphy_capable: Is Master Highphy capable? + * @sdw_dp0_supported: Data port0 supported? + * @sdw_dp0_cap: Capabilities of the dataport 0 of the Master. + * @num_data_ports: Array size for the number of Data ports present in + * Master. + * @sdw_dpn_cap: Array containing information about SoundWire Master + * Data Port Capabilities + * + */ +struct sdw_master_capabilities { + unsigned int base_clk_freq; + bool monitor_handover_supported; + bool highphy_capable; + bool sdw_dp0_supported; + struct sdw_mstr_dp0_capabilities sdw_dp0_cap; + unsigned int num_data_ports; + struct sdw_mstr_dpn_capabilities *sdw_dpn_cap; + +}; + +/** + * struct sdw_master: Master device controller on SoundWire bus. + * (similar to 'Master' on I2C) + * @owner: Owner of this module. Generally THIS module. + * @dev: Slave interface for this driver; + * @nr: Bus number of SoundWire Master bus. Also referred to as link number. + * @slv_list: List of SoundWire Slaves registered to the bus. + * @name: Name of the Master driver. + * @sdw_addr: Array containing Slave SoundWire bus Slave address information. + * @bus_lock: Global lock for bus functions. + * @num_slv: Number of SoundWire Slaves assigned logical address. + * @wq: Workqueue instance for Slave detection. + * @mstr_capabilities: Capabilities of the SoundWire Master controller. + * @driver: Driver handling the Master. + * @slv_released: Flag to indicate Slave release completion. Internally used + * by bus driver. + * @timeout: Timeout before getting response from Slave. + * @retries: How many times to retry before giving up on Slave response. + * @ssp_tag_synchronized: Do bus driver needs to set SSP tag based on + * sample interval of of all streams. + * @link_sync_mask: Bit mask representing all the other controller links + * with which this link is synchronized. + * + */ +struct sdw_master { + struct module *owner; + struct device dev; + unsigned int nr; + struct list_head slv_list; + char name[SOUNDWIRE_NAME_SIZE]; + struct sdw_slv_addr sdw_addr[SOUNDWIRE_MAX_DEVICES + 1]; + struct rt_mutex bus_lock; + u8 num_slv; + struct workqueue_struct *wq; + struct sdw_master_capabilities mstr_capabilities; + struct sdw_mstr_driver *driver; + struct completion slv_released; + struct list_head mstr_rt_list; + int timeout; + int retries; + bool ssp_tag_synchronized; + int link_sync_mask; +}; +#define to_sdw_master(d) container_of(d, struct sdw_master, dev) + + +/** struct sdw_port_params: This is used to program the + * Data Port based on Data Port + * stream params. These parameters cannot be changed + * dynamically + * + * @num : Port number for which params are there. + * @word_length: Word length of the Port + * @port_flow_mode: Port Data flow mode. + * @port_data_mode: Test mode or normal mode. + */ +struct sdw_port_params { + int num; + int word_length; + int port_flow_mode; + int port_data_mode; +}; + +/** struct sdw_transport_params: This is used to program the + * Data Port based on Data Port + * transport params. These parameters may be changed + * dynamically based on Frame Shape changes and bandwidth + * allocation + * + * @num : Port number for which params are there. + * @blockgroupcontrol_valid: Does Port implement block group control? + * @blockgroupcontrol: Block group control value. + * @sample_interval: Sample interval. + * @offset1: Blockoffset of the payload Data. + * @offset2: Blockoffset of the payload Data. + * @hstart: Horizontal start of the payload Data. + * @hstop: Horizontal stop of the payload Data. + * @blockpackingmode: Block per channel or block per Port. + * @lanecontrol: Data lane Port uses for Data transfer. + */ +struct sdw_transport_params { + int num; + bool blockgroupcontrol_valid; + int blockgroupcontrol; /* DPN_BlockCtrl2 */ + int sample_interval; /* DPN_SampleCtrl1 and DPN_SampleCtrl2 */ + int offset1; /* DPN_OffsetCtrl1 */ + int offset2; /* DPN_OffsetCtrl2 */ + int hstart; /* DPN_HCtrl */ + int hstop; /* DPN_HCtrl */ + int blockpackingmode; /* DPN_BlockCtrl3 */ + int lanecontrol; /* DPN_LaneCtrl */ +}; + +/** struct sdw_prepare_ch: Prepare/Un-prepare the Data Port channel. + * + * @num : Port number for which params are there. + * @ch_mask: prepare/un-prepare channels specified by ch_mask + * @prepare: Prepare/Un-prepare channel + */ +struct sdw_prepare_ch { + int num; + int ch_mask; + bool prepare; +}; + +/** struct sdw_activate_ch: Activate/Deactivate Data Port channel. + * + * @num : Port number for which params are there. + * @ch_mask: Active channel mask for this port. + * @activate: Activate/Deactivate channel + */ +struct sdw_activate_ch { + int num; + int ch_mask; + bool activate; +}; + +/** + * struct sdw_master_port_ops: Callback functions from bus driver + * to Master driver to set Master + * Data ports. Since Master registers + * are not standard, commands are passed + * to Master from bus and Master + * converts commands to register settings + * based on Master register map. + * @dpn_set_port_params: Set the Port parameters for the Master Port. + * @dpn_set_port_transport_params: Set transport parameters for the + * Master Port. + * @dpn_port_prepare_ch: Prepare/Un-prepare the Master channels of the Port + * @dpn_port_prepare_ch_pre: Called before calling dpn_port_prepare_ch, if + * Master driver needs to do update + * register settings before ch_prepare + * @dpn_port_prepare_ch_post: Called after calling dpn_port_prepare_ch, if + * Master driver needs to do some + * register settings after ch_prepare + * @dpn_port_activate_ch: Activate the channels of particular Master Port + * @dpn_port_activate_ch_pre: Called before calling dpn_port_activate_ch, if + * Master driver needs to some register + * setting before activating channel. + * @dpn_port_activate_ch_post : Called after calling dpn_port_activate_ch, if + * Master driver needs to some register + * setting after activating channel. + */ +struct sdw_master_port_ops { + int (*dpn_set_port_params)(struct sdw_master *mstr, + struct sdw_port_params *port_params, int bank); + int (*dpn_set_port_transport_params)(struct sdw_master *mstr, + struct sdw_transport_params *transport_params, + int bank); + int (*dpn_port_prepare_ch)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_prepare_ch_pre)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_prepare_ch_post)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_activate_ch)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); + int (*dpn_port_activate_ch_pre)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); + int (*dpn_port_activate_ch_post)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); +}; + +/** + * struct sdw_master_ops: Callback operations from bus driver to Master + * Master driver.Bus driver calls these + * functions to control the bus parameters + * in Master hardware specific way. Its + * like i2c_algorithm to access the bus + * in Master specific way. + * + * Slave registers are standard. + * @xfer_msg: Callback function to Master driver to read/write + * Slave registers. + * @xfer_bulk: Callback function to Master driver for bulk transfer. + * @monitor_handover: Allow monitor to be owner of command, if requested. + * @set_ssp_interval: Set SSP interval. + * @set_clock_freq: Set the clock frequency based on bandwidth requirement. + * Controller driver sets the frequency in hardware + * specific way. + * + */ + +struct sdw_master_ops { + enum sdw_command_response (*xfer_msg)(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page); + int (*xfer_bulk)(struct sdw_master *mstr, + struct sdw_bra_block *block); + int (*monitor_handover)(struct sdw_master *mstr, + bool handover); + int (*set_ssp_interval)(struct sdw_master *mstr, + int ssp_interval, int bank); + int (*set_clock_freq)(struct sdw_master *mstr, + int cur_clk_freq, int bank); + int (*set_frame_shape)(struct sdw_master *mstr, + int col, int row, int bank); +}; + +/** + * struct sdw_mstr_driver: Manage SoundWire Master/Master device driver + * @driver_type: To distinguish between master and slave driver. Set and + * used by bus driver. + * @probe: Binds this driver to a SoundWire Master. + * @remove: Unbinds this driver from the SoundWire Master. + * @shutdown: Standard shutdown callback used during powerdown/halt. + * @suspend: Standard suspend callback used during system suspend + * @resume: Standard resume callback used during system resume + * @driver: SoundWire device drivers should initialize name and owner field of + * this structure. + * @mstr_ops: Callback operations from bus driver to Master driver for + * programming and controlling bus parameters and to program + * Slave registers. + * @mstr_port_ops: Commands to setup the Master ports. Master register + * map is not defined by standard. So these ops represents the + * commands to setup Master ports. + * @id_table: List of SoundWire devices supported by this driver. + */ +struct sdw_mstr_driver { + enum sdw_driver_type driver_type; + int (*probe)(struct sdw_master *sdwmstr, const struct sdw_master_id *); + int (*remove)(struct sdw_master *sdwmstr); + void (*shutdown)(struct sdw_master *sdwmstr); + int (*suspend)(struct sdw_master *sdwmstr, + pm_message_t pmesg); + int (*resume)(struct sdw_master *sdwmstr); + struct device_driver driver; + struct sdw_master_ops *mstr_ops; + struct sdw_master_port_ops *mstr_port_ops; + const struct sdw_master_id *id_table; +}; +#define to_sdw_mstr_driver(d) container_of(d, struct sdw_mstr_driver, driver) + +/** + * struct sdw_msg : Message to be sent on bus. This is similar to i2c_msg + * on I2C bus. + * Actually controller sends the message on bus + * in hardware specific way. This interface is from + * bus driver to Slaves. + * @slave_addr: Slave address + * @ssp_tag: send message at ssp_tag. It should be used when a command needs + * to be issued during the next SSP. For all normal reads/writes + * this should be zero. This will be used for broadcast write + * to SCP_FrameCtrl register by bus driver only. Normally + * slave driver should always set ssp_tag to 0. + * @addr_page1: SCP address page 1 + * @addr_page2: SCP address page 2 + * @flag: Message to be read or write. + * @addr: Address of the register to be read; + * @len: Length of the message to be read. Successive increment in the + * register address for every message. + * @buf: Buf to be written or read from the register. + */ +struct sdw_msg { + u8 slave_addr; + bool ssp_tag; + u8 addr_page1; + u8 addr_page2; +#define SDW_MSG_FLAG_READ 0x0 +#define SDW_MSG_FLAG_WRITE 0x1 + u8 flag; + u16 addr; + u16 len; + u8 *buf; +}; + +/** + * sdw_stream_config: Stream configuration of the device. This includes + * Master and Slave. + * @frame_rate: Audio frame rate of the stream. + * @channel_count: Channel count of the stream. + * @bps: Number of bits per audio sample. + * @direction: Direction of the Data. What is the data direction for the + * device calling stream_config. This is w.r.t device. + * @type: Stream type PCM or PDM + * + */ +struct sdw_stream_config { + unsigned int frame_rate; + unsigned int channel_count; + unsigned int bps; + enum sdw_data_direction direction; + enum sdw_stream_type type; +}; + +/** + * sdw_port_cfg: SoundWire Port configuration, Configuration is done + * for all port of all the devices which are part of stream. + * All the ports of stream handles same pcm parameters accept + * channels. e.g Master may handle stereo channels using single + * port, but slave may handle Left and Right channel on one + * port each. + * @port_num: Port number to be configured + * @ch_mask: Which channels needs to be activated for this Port. + */ +struct sdw_port_cfg { + int port_num; + unsigned int ch_mask; +}; + +/** + * sdw_port_config: List of the ports handled by slave or master + * for particular stream. Both slave and master calls + * this with ports they trasmit/recevie onfor particular + * stream. + * @num_ports: Number of ports to be configured. + * @port_cfg : Port configuration for each Port. + */ +struct sdw_port_config { + unsigned int num_ports; + struct sdw_port_cfg *port_cfg; +}; + +/** + * struct sdw_bra_block: Data block to be sent/received using SoundWire + * bulk transfer protocol + * @slave_addr: Slave logical address from/to which transfer + * needs to take place. + * @operation: Read operation or write operation. + * @num_bytes: Number of Data bytes to be transferred. + * @reg_offset: Register offset from where the first byte to read/write. + * @values: Array containing value for write operation and to be filled + * for read operation. + */ +struct sdw_bra_block { + int slave_addr; + int cmd; + int num_bytes; + int reg_offset; + u8 *values; +}; + +/** + * Struct sdw_slave_status: Status of all the SoundWire Slave devices. + * @status: Array of status of SoundWire Slave devices. 0 is also + * a soundwire device during enumeration, so adding +1 for + * that. Actual number fo devices that can be supported are + * 11 as defined by SOUNDWIRE_MAX_DEVICES + */ +struct sdw_status { + enum sdw_slave_status status[SOUNDWIRE_MAX_DEVICES + 1]; +}; + +/** + * sdw_add_master_controller: Add SoundWire Master controller interface + * @mstr: Controller to be registered as SoundWire Master interface. + * This is to be called for each Master interface. + * This is same as I2C, where each adapter register specifies one + * pair of clock and Data lines (link). + */ +int sdw_add_master_controller(struct sdw_master *mstr); + +/** + * sdw_del_master_controller: Master tear-down. + * Master added with the "sdw_add_master_controller" API is teared down + * using this API. + * @mstr: Master to be teared down + */ +void sdw_del_master_controller(struct sdw_master *mstr); + +/** + * sdw_mstr_driver_register: SoundWire Master driver registration with SDW bus. + * This API will register the Master driver with the + * SoundWire bus. It is typically called from the + * driver's module-init function. + * @drv: Master Driver to be associated with device. + * + */ +int __sdw_mstr_driver_register(struct module *owner, + struct sdw_mstr_driver *driver); +#define sdw_mstr_driver_register(drv) \ + __sdw_mstr_driver_register(THIS_MODULE, drv) + +/** + * sdw_mstr_driver_unregister: Undo effects of sdw_mstr_driver_register + * @drv: SDW Master driver to be unregistered + */ +void sdw_mstr_driver_unregister(struct sdw_mstr_driver *drv); + +/** + * __sdw_slave_driver_register: SoundWire Slave driver registration with + * SDW bus. This API will register the Slave + * driver with the SoundWire bus. It is typically + * called from the driver's module-init function. + * @drv: Driver to be associated with Slave. + */ +int __sdw_slave_driver_register(struct module *owner, + struct sdw_slave_driver *drv); +#define sdw_slave_driver_register(drv) \ + __sdw_slave_driver_register(THIS_MODULE, drv) + +/** + * sdw_register_slave_capabilities: Register slave device capabilties to the + * bus driver. Since bus driver handles bunch + * of slave register programming it should + * be aware of slave device capabilties. + * Slave device is attached to bus based on + * enumeration. Once slave driver is attached + * to device and probe of slave driver is called + * on device and driver binding, slave driver + * should call this function to register its + * capabilties to bus. This should be the very + * first function to bus driver from slave driver + * once slave driver is registered and probed. + * @slave: SoundWire Slave handle + * @cap: Slave capabilities to be updated to bus driver. + */ +int sdw_register_slave_capabilities(struct sdw_slv *slave, + struct sdw_slv_capabilities *cap); + +/** + * sdw_slave_driver_unregister: Undo effects of sdw_slave_driver_register + * @drv: SDW Slave driver to be unregistered + */ +void sdw_slave_driver_unregister(struct sdw_slave_driver *drv); + +/** + * sdw_slave_transfer: Transfer SDW message on bus. + * @mstr: Master which will transfer the message. + * @msg: Array of messages to be transferred. + * @num: Number of messages to be transferred, messages include read and write + * messages, but not the ping messages. + */ +int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num); + +/** + * sdw_alloc_stream_tag: Allocate stream_tag for each audio stream + * between SoundWire Masters and Slaves. + * (Multiple Masters and Slave in case of + * aggregation) stream_tag is + * unique across system. Stream tag represents the + * independent audio stream which can be controlled + * configure individually. Stream can be split between + * multiple Masters and Slaves. It can also be + * split between multiple ports of the Master and Slave. + * All the stream configuration for Masters and Slaves + * and ports of the Master and Slave for particular + * stream is done on stream_tag as handle. Normally + * stream is between CPU and Codec. CPU dai ops + * allocate the stream tag and programs same to the + * codec dai. If there are multiple codecs attached + * to each CPU DAI, like L and R digital speaker, both + * codecs should be programmed with same stream tag. + * + * + * @uuid: uuid is used to make sure same stream tag gets + * allocated for same uuid. If stream tag is not + * allocated for uuid, it will get allocated and + * uuid-stream tag pair will be saved, for next + * allocation based on uuid. If this is NULL, + * new stream tag will be allocated each time this + * function is called. + * + * + * @stream_tag: Stream tag returned by bus driver. + */ +int sdw_alloc_stream_tag(char *uuid, int *stream_tag); + +/** + * sdw_release_stream_tag: Free the already assigned stream tag. + * + * @stream_tag: Stream tag to be freed. + */ +void sdw_release_stream_tag(int stream_tag); + +/** + * sdw_config_stream: Configure the audio stream. Each stream between + * master and slave, or between slaves has unique + * stream tag. + * Master and Slave attaches to + * the stream using the unique stream_tag for each + * stream between Master(s) and Slave(s). Both + * Master and Slave driver call this function to + * attach to stream and let bus driver know the + * stream params. Master and Slave calls this function + * typically as part of hw_params ops of their respective + * DAIs to let bus driver know about stream parameters. + * Stream parameters between Tx and Rx direction + * should match. Function is reference counted so + * multiple Master and Slaves attached to particular + * stream can call to setup stream config. + * Master calls this function with Slave handle as + * NULL. + * @mstr: Master handle, + * @slave: SoundWire Slave handle, Null if stream configuration is called + * by Master driver. + * @stream_config: Stream configuration for the SoundWire audio stream. + * @stream_tag: Stream_tag representing the audio stream. All Masters and Slaves + * part of the same stream will have same stream tag. So bus drivers + * know which all Masters and Slaves are part of stream. + * + */ +int sdw_config_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + unsigned int stream_tag); + +/** + * sdw_release_stream: De-associates Master(s) and Slave(s) from stream. Reverse + * effect of the sdw_config_stream + * + * @mstr: Master handle, + * @slave: SoundWire Slave handle, Null if stream configuration is called + * by Master driver. + * @stream_tag: Stream_tag representing the audio stream. All Masters and Slaves + * part of the same stream has same stream tag. So bus drivers + * know which all Masters and Slaves are part of stream. + * + */ +int sdw_release_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + unsigned int stream_tag); + +/** + * sdw_config_port: Master(s) and Slave(s) are associated to stream. + * Each Master and Slave can handle stream using + * different SoundWire Port number(s). + * e.g Master may handle stereo stream using single + * Data Port, while Slave may handle each channel + * of Data stream using one Port each. Bus driver + * needs to know the stream to Port association + * for each Master(s) and Slave(s) assocated with the + * stream for configuring Master and Slave ports + * based on transport params calculate by bus for a + * stream. Both Master and Slave call this function to let + * bus driver know about stream to Port mapping. + * Master driver calls this with Slave handle + * as NULL. + * @mstr: Master handle where the Slave is connected. + * @slave: Slave handle. + * @port_config: Port configuration for each Port of SoundWire Slave. + * @stream_tag: Stream tag, where this Port is connected. + * + */ +int sdw_config_port(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_port_config *port_config, + unsigned int stream_tag); + +/** + * sdw_prepare_and_enable: Prepare and enable all the ports of all the Master(s) + * and Slave(s) associated with this stream tag. + * Following will be done as part of prepare and + * enable by bus driver. + * 1. Calculate new bandwidth required on bus + * because of addition be this new stream. + * 2. Calculate new frameshape based on bandwidth + * 3. Calculate the new clock frequency on which + * bus will be transistioned based on new + * bandwidth and frameshape. + * 4. Calculate new transport params for the already + * active on the bus based on clock frequency, + * frameshape and bandwidth changes. + * 5. Calculate transport params for this stream + * 6. Program already active ports with new transport + * params and frame shape, + * change clock frequency of master. + * 7. Prepare ports for this stream. + * 8. Enable ports for this stream. + * @stream_tag: Audio stream to be activated. Each stream has unique + * stream_tag. All the channels of all the ports of Slave(s) + * and Master(s) attached to this stream will be activated + * deactivated simultaneously at proper SSP or gsync. + * @enable: Enable the ports as part of current call or not. If its + * false only till steps 1 to 7 will be executed as part of this + * function call. if its true, than steps 1 to 7 will be executed + * if not already done, else only step 8 will be executed. + * + */ +int sdw_prepare_and_enable(int stream_tag, bool enable); + +/** + * sdw_disable_and_unprepare: Un-Prepare and disable all the ports of all the + * Master(s) and Slave(s) associated with stream tag. + * Following will be done as part of Un-prepare and + * disable by bus driver. + * 1. Disable all the ports for this stream. + * 2. Un-Prepare ports for this stream. + * 3. Calculate new bandwidth required on bus + * because of removal of this new stream. + * 4. Calculate new frameshape based on bandwidth + * 5. Calculate the new clock frequency on which + * bus will be transistioned based on new + * bandwidth and frameshape. + * 6. Calculate new transport params for the already + * active on the bus based on clock frequency, + * frameshape and bandwidth changes. + * 7.Program already active ports with new transport + * params and frame shape, + * change clock frequency of master. + * @stream_tag: Audio stream to be disabled. Each stream has unique + * stream_tag. All the channels of all the ports of Slave(s) + * and Master(s) attached to this stream will be activated + * deactivated simultaneously at proper SSP or gsync. + * @un_prepare: Un-prepare the ports as part of current call or not. If its + * false only step 1 will be executed as part of this + * function call. if its true, than step 1 will be executed + * if not already done, else only 2 to 7 will be executed. + */ +int sdw_disable_and_unprepare(int stream_tag, bool un_prepare); + +/** + * sdw_master_update_slv_status: Update the status of the Slave to the bus + * driver. Master calls this function based on the + * interrupt it gets once the Slave changes its + * state or from interrupts for the Master hardware + * that caches status information reported in PING frames + * @mstr: Master handle for which status is reported. + * @status: Array of status of each Slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status); + +/** + * sdw_get_master: Return the Master handle from Master number. + * Increments the reference count of the module. + * Similar to i2c_get_adapter. + * nr: Master controller number. + * returns Master handle on success, else NULL + */ +struct sdw_master *sdw_get_master(int nr); + +/** + * sdw_put_master: Reverses the effect of sdw_get_master + * mstr: Master controller handle. + */ +void sdw_put_master(struct sdw_master *mstr); + + +/** + * module_sdw_slave_driver() - Helper macro for registering a sdw Slave driver + * @__sdw_slave_driver: sdw_slave_driver struct + * + * Helper macro for sdw drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_sdw_slave_driver(__sdw_slave_driver) \ + module_driver(__sdw_slave_driver, sdw_slave_driver_register, \ + sdw_slave_driver_unregister) +/** + * sdw_prepare_for_clock_change: Prepare all the Slaves for clock stop or + * clock start. Prepares Slaves based on what they support + * simplified clock stop or normal clock stop based on + * their capabilities registered to slave driver. + * @mstr: Master handle for which clock state has to be changed. + * @start: Prepare for starting or stopping the clock + * @clk_stop_mode: Bus used which clock mode, if bus finds all the Slaves + * on the bus to be supported clock stop mode1 it prepares + * all the Slaves for mode1 else it will prepare all the + * Slaves for mode0. + */ +int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool start, + enum sdw_clk_stop_mode *clck_stop_mode); + +/** + * sdw_wait_for_slave_enumeration: Wait till all the slaves are enumerated. + * Typicall this function is called by master once + * it resumes its clock. This function waits in + * loop for about 2Secs before all slaves gets enumerated + * This function returns immediately if the clock + * stop mode0 was entered earlier, where slave need + * not re-enumerated. + * + * @mstr: Master handle + * @slave: Slave handle + */ +int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, + struct sdw_slv *slave); + +/** + * sdw_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * register with clock_stop_now bit set. + * @mstr: Master handle for which clock has to be stopped. + * @clk_stop_mode: Bus used which clock mode. + */ + +int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode); + +/* Return the adapter number for a specific adapter */ +static inline int sdw_master_id(struct sdw_master *mstr) +{ + return mstr->nr; +} + +static inline void *sdw_master_get_drvdata(const struct sdw_master *mstr) +{ + return dev_get_drvdata(&mstr->dev); +} + +static inline void sdw_master_set_drvdata(struct sdw_master *mstr, + void *data) +{ + dev_set_drvdata(&mstr->dev, data); +} + +static inline void *sdw_slave_get_drvdata(const struct sdw_slv *slv) +{ + return dev_get_drvdata(&slv->dev); +} + +static inline void sdw_slave_set_drvdata(struct sdw_slv *slv, + void *data) +{ + dev_set_drvdata(&slv->dev, data); +} + +#endif /* _LINUX_SDW_BUS_H */ diff --git a/include/trace/events/sdw.h b/include/trace/events/sdw.h new file mode 100644 index 000000000000..0c08e58a18bf --- /dev/null +++ b/include/trace/events/sdw.h @@ -0,0 +1,232 @@ +/* SDW message transfer tracepoints + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sdw + +#if !defined(_TRACE_SDW_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SDW_H + +#include +#include +#include + +/* + * drivers/sdw/sdw.c + */ +extern int sdw_transfer_trace_reg(void); +extern void sdw_transfer_trace_unreg(void); +/* + * __sdw_transfer() write request + */ +TRACE_EVENT_FN(sdw_write, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u8, addr_page1) + __field(__u8, addr_page2) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + __entry->addr_page1 = msg->addr_page1; + __entry->addr_page2 = msg->addr_page2; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x addr_page1=%04x addr_page2=%04x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->addr_page1, + __entry->addr_page2, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() read request + */ +TRACE_EVENT_FN(sdw_read, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u8, addr_page1) + __field(__u8, addr_page2) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + __entry->addr_page1 = msg->addr_page1; + __entry->addr_page2 = msg->addr_page2; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x addr_page1=%04x addr_page2=%04x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->addr_page1, + __entry->addr_page2, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() read reply + */ +TRACE_EVENT_FN(sdw_reply, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() result + */ +TRACE_EVENT_FN(sdw_result, + TP_PROTO(const struct sdw_master *mstr, int num, int ret), + TP_ARGS(mstr, num, ret), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, nr_msgs) + __field(__s16, ret) + ), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->nr_msgs = num; + __entry->ret = ret; + ), + TP_printk("sdw-%d n=%u ret=%d", + __entry->master_nr, + __entry->nr_msgs, + __entry->ret + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * sdw_stream_config() configuration + */ +TRACE_EVENT_FN(sdw_config_stream, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_slv *slv, const struct sdw_stream_config *str_cfg, int stream_tag), + TP_ARGS(mstr, slv, str_cfg, stream_tag), + TP_STRUCT__entry( + __field(unsigned int, frame_rate) + __field(unsigned int, ch_cnt) + __field(unsigned int, bps) + __field(unsigned int, direction) + __field(unsigned int, stream_tag) + __array(char, name, SOUNDWIRE_NAME_SIZE) + ), + TP_fast_assign( + __entry->frame_rate = str_cfg->frame_rate; + __entry->ch_cnt = str_cfg->channel_count; + __entry->bps = str_cfg->bps; + __entry->direction = str_cfg->direction; + __entry->stream_tag = stream_tag; + slv ? strncpy(entry->name, dev_name(&slv->dev), SOUNDWIRE_NAME_SIZE) : strncpy(entry->name, dev_name(&mstr->dev), SOUNDWIRE_NAME_SIZE); + ), + TP_printk("Stream_config dev = %s stream_tag = %d, frame_rate = %d, ch_count = %d bps = %d dir = %d", + __entry->name, + __entry->stream_tag, + __entry->frame_rate, + __entry->ch_cnt, + __entry->bps, + __entry->direction + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * sdw_port_config() configuration + */ +TRACE_EVENT_FN(sdw_config_port, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_slv *slv, const struct sdw_port_cfg *port_cfg, int stream_tag), + TP_ARGS(mstr, slv, port_cfg, stream_tag), + TP_STRUCT__entry( + __field(unsigned int, port_num) + __field(unsigned int, ch_mask) + __field(unsigned int, stream_tag) + __array(char, name, SOUNDWIRE_NAME_SIZE) + ), + TP_fast_assign( + __entry->port_num = port_cfg->port_num; + __entry->ch_mask = port_cfg->ch_mask; + __entry->stream_tag = stream_tag; + slv ? strncpy(entry->name, dev_name(&slv->dev), SOUNDWIRE_NAME_SIZE) : strncpy(entry->name, dev_name(&mstr->dev), SOUNDWIRE_NAME_SIZE); + ), + TP_printk("Port_config dev = %s stream_tag = %d, port = %d, ch_mask = %d", + __entry->name, + __entry->stream_tag, + __entry->port_num, + __entry->ch_mask + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +#endif /* _TRACE_SDW_H */ + +/* This part must be outside protection */ +#include From 3bfb260df358501a87e8fb56f3c65efea031fca5 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 10:16:10 +0530 Subject: [PATCH 0014/1276] ASoC: Add dai_ops to set the stream tag. Stream tag is introduced for supporting SoundWire links as part of ASoC. stream tag is unique stream identifier for each stream. Same stream tag is assigned to all the CPU and codec DAIs part of same stream tag. This function provides dai ops to be called to let the DAI know its stream tag. Normally stream tag is allocated by CPU DAI and get programmed to codec dai. This function calls all the codec DAI ops to program the stream tag allocated by CPU DAI. Change-Id: I9a74dc1329c21b34aaf7f096c68f932b537653f2 Signed-off-by: Hardik T Shah --- include/sound/soc-dai.h | 17 +++++++++++++ sound/soc/soc-core.c | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index f5d70041108f..c393f1126839 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -145,6 +145,13 @@ int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, int snd_soc_dai_is_dummy(struct snd_soc_dai *dai); + +/* Stream tag programming for codec and cpu dai */ +int snd_soc_dai_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, int stream_tag); +void snd_soc_dai_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai); + struct snd_soc_dai_ops { /* * DAI clocking configuration, all optional. @@ -184,6 +191,16 @@ struct snd_soc_dai_ops { int (*digital_mute)(struct snd_soc_dai *dai, int mute); int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); + /* + * stream_tag - Optional + * Used by SoundWire and HDA driver to set same stream + * tag for both CPU and Codec DAI + */ + int (*program_stream_tag)(struct snd_pcm_substream *, + struct snd_soc_dai *, int); + int (*remove_stream_tag)(struct snd_pcm_substream *, + struct snd_soc_dai *); + /* * ALSA PCM audio operations - all optional. * Called by soc-core during audio PCM operations. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 473eefe8658e..29b42f8f1eff 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2607,6 +2607,61 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); +/** + * snd_soc_dai_program_stream_tag - Program the stream tag allocated by + * CPU DAI to codec DAI. This will be + * used in HDA and soundwire, wherex + * audio stream between codec and + * SoC need to have same stream tag. + * substream: Substream + * cpu_dai: CPU DAI + * stream_tag: Stream tag to be programmed. + */ +int snd_soc_dai_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, int stream_tag) +{ + int i; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + const struct snd_soc_dai_ops *codec_dai_ops; + struct snd_soc_dai *codec_dai; + int ret = 0; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + codec_dai_ops = codec_dai->driver->ops; + if (codec_dai_ops->program_stream_tag) { + ret = codec_dai_ops->program_stream_tag(substream, + codec_dai, stream_tag); + if (ret) + return ret; + } + } + return ret; + +} +EXPORT_SYMBOL_GPL(snd_soc_dai_program_stream_tag); +/** + * snd_soc_dai_remove_stream_tag - Reverse the programmed stream tag + * substream: Substream + * cpu_dai: CPU DAI + */ +void snd_soc_dai_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + int i; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + const struct snd_soc_dai_ops *codec_dai_ops; + struct snd_soc_dai *codec_dai; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + codec_dai_ops = codec_dai->driver->ops; + if (codec_dai_ops->program_stream_tag) + codec_dai_ops->remove_stream_tag(substream, codec_dai); + } +} +EXPORT_SYMBOL_GPL(snd_soc_dai_remove_stream_tag); + /** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI From 5d244a22c890d60943a061f9bc2829d5797feaaf Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 10:33:33 +0530 Subject: [PATCH 0015/1276] ASoC: CNL: Register soundwire controller to bus driver. This patch registers the SoundWire controller to the SoundWire bus driver. Change-Id: Iefc4a7cd30d2f5ad043fe63e82f519ebc488bf65 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst-dsp.c | 6 ++ sound/soc/intel/skylake/cnl-sst-dsp.h | 13 +++ sound/soc/intel/skylake/cnl-sst.c | 121 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 5 ++ 4 files changed, 145 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.c b/sound/soc/intel/skylake/cnl-sst-dsp.c index 2f8326707c21..2d9c86ddcdf0 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.c +++ b/sound/soc/intel/skylake/cnl-sst-dsp.c @@ -237,6 +237,12 @@ void cnl_ipc_int_disable(struct sst_dsp *ctx) CNL_ADSPIC_IPC, 0); } +void cnl_sdw_int_enable(struct sst_dsp *ctx, bool enable) +{ + sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_ADSPIC2, + CNL_ADSPIC2_SNDW, CNL_ADSPIC2_SNDW); +} + void cnl_ipc_op_int_enable(struct sst_dsp *ctx) { /* enable IPC DONE interrupt */ diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 09bd218df5c4..323cebc4e389 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -26,6 +26,8 @@ struct sst_generic_ipc; #define CNL_ADSP_REG_ADSPCS (CNL_ADSP_GEN_BASE + 0x04) #define CNL_ADSP_REG_ADSPIC (CNL_ADSP_GEN_BASE + 0x08) #define CNL_ADSP_REG_ADSPIS (CNL_ADSP_GEN_BASE + 0x0c) +#define CNL_ADSP_REG_ADSPIC2 (CNL_ADSP_GEN_BASE + 0x10) +#define CNL_ADSP_REG_ADSPIS2 (CNL_ADSP_GEN_BASE + 0x14) /* Intel HD Audio Inter-Processor Communication Registers */ #define CNL_ADSP_IPC_BASE 0xc0 @@ -72,6 +74,16 @@ struct sst_generic_ipc; #define CNL_ADSPIC_IPC 0x1 #define CNL_ADSPIS_IPC 0x1 +#define CNL_ADSPIC2_SNDW 0x20 + +#define CNL_SDW_SHIM_BASE 0x2C000 +#define CNL_SDW_LINK_0_BASE 0x30000 +#define CNL_SDW_LINK_1_BASE 0x40000 +#define CNL_SDW_LINK_2_BASE 0x50000 +#define CNL_SDW_LINK_3_BASE 0x60000 +#define CNL_ALH_BASE 0x2C800 + +/* ADSPCS - Audio DSP Control & Status */ #define CNL_DSP_CORES 4 #define CNL_DSP_CORES_MASK ((1 << CNL_DSP_CORES) - 1) @@ -98,6 +110,7 @@ void cnl_dsp_free(struct sst_dsp *dsp); void cnl_ipc_int_enable(struct sst_dsp *ctx); void cnl_ipc_int_disable(struct sst_dsp *ctx); +void cnl_sdw_int_enable(struct sst_dsp *ctx, bool enable); void cnl_ipc_op_int_enable(struct sst_dsp *ctx); void cnl_ipc_op_int_disable(struct sst_dsp *ctx); bool cnl_ipc_int_status(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a66ed94870b4..335ec68b6a32 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include "../common/sst-dsp.h" @@ -497,6 +499,118 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) return 0; } +static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, + void __iomem *mmio_base, int irq) +{ + struct sdw_master_capabilities *m_cap; + struct sdw_mstr_dp0_capabilities *dp0_cap; + struct sdw_mstr_dpn_capabilities *dpn_cap; + struct sdw_master *master; + struct cnl_sdw_data *p_data; + int ret = 0, i, j; + /* TODO: This number 4 should come from ACPI */ + dsp->num_sdw_controllers = 1; + master = devm_kzalloc(dev, + (sizeof(*master) * dsp->num_sdw_controllers), + GFP_KERNEL); + if (!master) { + return -ENOMEM; + dsp->num_sdw_controllers = 0; + } + dsp->mstr = master; + /* TODO This should come from ACPI */ + for (i = 0; i < dsp->num_sdw_controllers; i++) { + p_data = devm_kzalloc(dev, sizeof(*p_data), GFP_KERNEL); + if (!p_data) + return -ENOMEM; + /* PCI Device is parent of the SoundWire master device */ + /* TODO: All these hardcoding should come from ACPI */ + master[i].dev.parent = dev; + master[i].dev.platform_data = p_data; + m_cap = &master[i].mstr_capabilities; + dp0_cap = &m_cap->sdw_dp0_cap; + master[i].nr = i; + master[i].timeout = -1; + master[i].retries = CNL_SDW_MAX_CMD_RETRIES; + m_cap->base_clk_freq = 9.6 * 1000 * 1000; + strcpy(master[i].name, "cnl_sdw_mstr"); + m_cap->highphy_capable = 0; + m_cap->sdw_dp0_supported = 1; + m_cap->num_data_ports = CNL_SDW_MAX_PORTS; + dp0_cap->max_word_length = 32; + dp0_cap->min_word_length = 1; + dp0_cap->num_word_length = 0; + dp0_cap->word_length_buffer = NULL; + dp0_cap->bra_max_data_per_frame = 0; + m_cap->sdw_dpn_cap = kzalloc(((sizeof(*dpn_cap)) * + CNL_SDW_MAX_PORTS), GFP_KERNEL); + if (!m_cap->sdw_dpn_cap) + return -ENOMEM; + for (j = 0; j < m_cap->num_data_ports; j++) { + dpn_cap = &m_cap->sdw_dpn_cap[i]; + /* Both Tx and Rx */ + dpn_cap->port_direction = 0x3; + dpn_cap->port_number = i; + dpn_cap->max_word_length = 32; + dpn_cap->min_word_length = 1; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 8; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + /* IP supports all, but we are going to support only + * isochronous + */ + dpn_cap->port_mode_mask = + SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; + } + switch (i) { + case 0: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; + break; + case 1: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; + break; + case 2: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; + break; + case 3: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_3_BASE; + break; + default: + return -EINVAL; + } + p_data->sdw_shim = mmio_base + CNL_SDW_SHIM_BASE; + p_data->alh_base = mmio_base + CNL_ALH_BASE; + p_data->inst_id = i; + p_data->irq = irq; + ret = sdw_add_master_controller(&master[i]); + if (ret) { + dev_err(dev, "Failed to register soundwire master\n"); + return ret; + } + } + /* Enable the global soundwire interrupts */ + cnl_sdw_int_enable(dsp->dsp, 1); + return 0; +} + +static void skl_unregister_sdw_masters(struct skl_sst *ctx) +{ + int i; + + /* Disable global soundwire interrupts */ + cnl_sdw_int_enable(ctx->dsp, 0); + for (i = 0; i < ctx->num_sdw_controllers; i++) + sdw_del_master_controller(&ctx->mstr[i]); + +} + int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) @@ -544,6 +658,12 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); + if (ret) { + dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); @@ -575,6 +695,7 @@ void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) release_firmware(ctx->dsp->fw); skl_freeup_uuid_list(ctx); + skl_unregister_sdw_masters(ctx); cnl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f74f040dfd83..10a486939515 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -121,6 +121,11 @@ struct skl_sst { /* Callback to update dynamic clock and power gating registers */ void (*clock_power_gating)(struct device *dev, bool enable); + + /* SDW Devices in DSP Space */ + int num_sdw_controllers; + /* Array of sdw masters */ + struct sdw_master *mstr; }; struct skl_ipc_init_instance_msg { From f66c3e0d6e4cac19817b678f7e9d03f8f6db9b4c Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 11:25:19 +0530 Subject: [PATCH 0016/1276] ASoC: Intel: Add support for SoundWire link in copier. This patch adds the support for the SoundWire link in copier. Copier needs to be programmed differently for different link. Change-Id: I16d811b61ac253a893c2a9afebb6d418327e4387 Signed-off-by: Hardik T Shah Signed-off-by: Guneshwor Singh --- include/uapi/sound/skl-tplg-interface.h | 7 +++++++ sound/soc/intel/skylake/skl-messages.c | 7 +++++++ sound/soc/intel/skylake/skl-topology.h | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index f39352cef382..68eda1a15e39 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -104,9 +104,16 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, + SKL_DEVICE_SDW = 0x6, SKL_DEVICE_NONE }; +enum skl_pdi_type { + SKL_PDI_PCM = 0, + SKL_PDI_PDM = 1, + SKL_PDI_INVALID = 2 +}; + /** * enum skl_interleaving - interleaving style * diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8bfb8b0fa3d5..f63b50e43257 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -561,6 +561,13 @@ static u32 skl_get_node_id(struct skl_sst *ctx, SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + case SKL_DEVICE_SDW: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_SDW_LINK_OUTPUT_CLASS : + SKL_DMA_SDW_LINK_INPUT_CLASS; + node_id.node.vindex = mconfig->sdw_stream_num; + break; default: node_id.val = 0xFFFFFFFF; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 82282cac9751..d94709ffd323 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -184,6 +184,8 @@ enum skl_dma_type { SKL_DMA_DMIC_LINK_INPUT_CLASS = 0xB, SKL_DMA_I2S_LINK_OUTPUT_CLASS = 0xC, SKL_DMA_I2S_LINK_INPUT_CLASS = 0xD, + SKL_DMA_SDW_LINK_OUTPUT_CLASS = 0x10, + SKL_DMA_SDW_LINK_INPUT_CLASS = 0x11, }; union skl_ssp_dma_node { @@ -414,6 +416,8 @@ struct skl_module_cfg { u32 mem_pages; enum d0i3_capability d0i3_caps; u32 dma_buffer_size; /* in milli seconds */ + u8 pdi_type; + u32 sdw_stream_num; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; From 33ffa0c5d8a622583ce029bf3a94a93b0dfc6ff7 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 11:32:51 +0530 Subject: [PATCH 0017/1276] ASoC: Intel: Skylake: Interface change between firmware and driver. Copier interface changed between the firmware and driver. This patch takes care of the firmware interface change. Change-Id: I475cde41a4a008808cf7d88fadc20639879fbff4 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/skl-topology.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index d94709ffd323..353c90cbd336 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -200,8 +200,8 @@ union skl_connector_node_id { u32 val; struct { u32 vindex:8; - u32 dma_type:4; - u32 rsvd:20; + u32 dma_type:5; + u32 rsvd:19; } node; }; From 9609e1ca13016936e14543c8700dc3c71fdc0693 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 12:55:38 +0530 Subject: [PATCH 0018/1276] ASoC: Intel: Add support to bypass NHLT reading for SDW link. NHLT reading is not required for the SDW link. So bypass NHLT reading for SoundWire link. Change-Id: I4c256874ce57631e8c3e72f2f033a303acb4006a Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/skl-nhlt.h | 1 + sound/soc/intel/skylake/skl-topology.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h index 116534e7b3c5..fc17da503b4d 100644 --- a/sound/soc/intel/skylake/skl-nhlt.h +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -48,6 +48,7 @@ enum nhlt_link_type { NHLT_LINK_DSP = 1, NHLT_LINK_DMIC = 2, NHLT_LINK_SSP = 3, + NHLT_LINK_SDW = 4, NHLT_LINK_INVALID }; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2620d77729c5..413a72dada50 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1850,7 +1850,9 @@ static u8 skl_tplg_be_link_type(int dev_type) case SKL_DEVICE_HDALINK: ret = NHLT_LINK_HDA; break; - + case SKL_DEVICE_SDW: + ret = NHLT_LINK_SDW; + break; default: ret = NHLT_LINK_INVALID; break; @@ -1877,7 +1879,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, skl_tplg_fill_dma_id(mconfig, params); - if (link_type == NHLT_LINK_HDA) + if (link_type == NHLT_LINK_HDA || link_type == NHLT_LINK_SDW) return 0; /* update the blob based on virtual bus_id*/ From a0155da7cd4f738efcfd5695d7eb07735a6ec131 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 13:00:27 +0530 Subject: [PATCH 0019/1276] ASoC: Intel: Skylake: Add support for the SDW dai ops. Add support for the SoundWire dai link operation. DAI ops needs to be different for different link based on Link requirement. Add support for the DAI ops link. Change-Id: I2c3d24c3d982f339f8b7ca180079b547227c6e70 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-pcm.c | 94 ++++++++++ sound/soc/intel/skylake/skl-sdw-pcm.c | 246 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sdw-pcm.h | 41 +++++ 4 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-sdw-pcm.c create mode 100644 sound/soc/intel/skylake/skl-sdw-pcm.h diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 86f6e1d801af..c5ee108bf5d9 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +snd-soc-skl-objs := skl.o skl-sdw-pcm.o skl-pcm.o skl-nhlt.o skl-messages.o \ skl-topology.o ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ab31d6a1c842..d411cf28dd91 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -28,6 +28,7 @@ #include "skl-topology.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-sdw-pcm.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -662,6 +663,67 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } +static int skl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* Find the type of DAI, Its decided based on which copier + * is connected to the DAI. All the soundwire DAIs are identical + * but some registers needs to be programmed based on its a + * PDM or PCM. Copier tells DAI is to be used as PDM or PCM + * This makes sure no change is required in code, only change + * required is in the topology to change DAI from PDM to PCM or + * vice versa. + */ + return cnl_sdw_startup(substream, dai); + +} + +static int skl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + + ret = pm_runtime_get_sync(dai->dev); + if (!ret) + return ret; + /* Allocate the port based on hw_params. + * Allocate PDI stream based on hw_params + * Program stream params to the sdw bus driver + * program Port params to sdw bus driver + */ + return cnl_sdw_hw_params(substream, params, dai); +} + +static int skl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* De-allocate the port from master controller + * De allocate stream from bus driver + */ + return cnl_sdw_hw_free(substream, dai); +} + +static int skl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return cnl_sdw_pcm_prepare(substream, dai); +} + +static int skl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + return cnl_sdw_pcm_trigger(substream, cmd, dai); +} + +static void skl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + cnl_sdw_shutdown(substream, dai); + pm_runtime_mark_last_busy(dai->dev); + pm_runtime_put_autosuspend(dai->dev); +} + static const struct snd_soc_dai_ops skl_pcm_dai_ops = { .startup = skl_pcm_open, .shutdown = skl_pcm_close, @@ -686,6 +748,15 @@ static const struct snd_soc_dai_ops skl_link_dai_ops = { .trigger = skl_link_pcm_trigger, }; +static struct snd_soc_dai_ops skl_sdw_dai_ops = { + .startup = skl_sdw_startup, + .prepare = skl_sdw_pcm_prepare, + .hw_params = skl_sdw_hw_params, + .hw_free = skl_sdw_hw_free, + .trigger = skl_sdw_pcm_trigger, + .shutdown = skl_sdw_shutdown, +}; + static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", @@ -1019,6 +1090,29 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW Pin", + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + + }, }; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c new file mode 100644 index 000000000000..90a11f47586b --- /dev/null +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -0,0 +1,246 @@ +/* + * skl-sdw-pcm.c - Handle PCM ops for soundwire DAIs. + * + * Copyright (C) 2014 Intel Corp + * Author: Hardik Shah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include "skl.h" +#include "skl-topology.h" + +#define STREAM_STATE_ALLOC_STREAM_TAG 0x1 +#define STREAM_STATE_ALLOC_STREAM 0x2 +#define STREAM_STATE_CONFIG_STREAM 0x3 +#define STREAM_STATE_PREPARE_STREAM 0x4 +#define STREAM_STATE_ENABLE_STREAM 0x5 +#define STREAM_STATE_DISABLE_STREAM 0x6 +#define STREAM_STATE_UNPREPARE_STREAM 0x7 +#define STREAM_STATE_RELEASE_STREAM 0x8 +#define STREAM_STATE_FREE_STREAM 0x9 +#define STREAM_STATE_FREE_STREAM_TAG 0xa + +struct sdw_dma_data { + int stream_tag; + struct cnl_sdw_port *port; + struct sdw_master *mstr; + enum cnl_sdw_pdi_stream_type stream_type; + int stream_state; +}; + + + +int cnl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct skl_module_cfg *m_cfg; + int sdw_ctrl_nr; + struct sdw_master *mstr; + struct sdw_dma_data *dma; + int ret = 0; + + + m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (!m_cfg) { + dev_err(dai->dev, "BE Copier not found\n"); + return -EINVAL; + } + sdw_ctrl_nr = m_cfg->vbus_id; + mstr = sdw_get_master(sdw_ctrl_nr); + if (!mstr) { + dev_err(dai->dev, "Master controller not found\n"); + return -EINVAL; + } + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto alloc_failed; + } + if (m_cfg->pdi_type == SKL_PDI_PCM) + dma->stream_type = CNL_SDW_PDI_TYPE_PCM; + else if (m_cfg->pdi_type == SKL_PDI_PDM) + dma->stream_type = CNL_SDW_PDI_TYPE_PDM; + else { + dev_err(dai->dev, "Stream type not known\n"); + return -EINVAL; + } + dma->mstr = mstr; + snd_soc_dai_set_dma_data(dai, substream, dma); + + ret = sdw_alloc_stream_tag(NULL, &dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to allocate stream tag"); + ret = -EINVAL; + goto alloc_stream_tag_failed; + } + ret = snd_soc_dai_program_stream_tag(substream, dai, dma->stream_tag); + + dma->stream_state = STREAM_STATE_ALLOC_STREAM_TAG; + return 0; +alloc_stream_tag_failed: + kfree(dma); +alloc_failed: + sdw_put_master(mstr); + return ret; +} + +int cnl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int channels; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + int ret = 0; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + + ret = skl_tplg_be_update_params(dai, &p_params); + if (ret) + return ret; + + + dma = snd_soc_dai_get_dma_data(dai, substream); + channels = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + direction = SDW_DATA_DIR_IN; + else + direction = SDW_DATA_DIR_OUT; + /* Dynamically alloc port and PDI streams for this DAI */ + dma->port = cnl_sdw_alloc_port(dma->mstr, channels, + direction, dma->stream_type); + if (!dma->port) { + dev_err(dai->dev, "Unable to allocate port\n"); + return -EINVAL; + } + dma->stream_state = STREAM_STATE_ALLOC_STREAM; + m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (!m_cfg) { + dev_err(dai->dev, "BE Copier not found\n"); + return -EINVAL; + } + m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + stream_config.frame_rate = params_rate(params); + stream_config.channel_count = channels; + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + ret = sdw_config_stream(dma->mstr, NULL, &stream_config, + dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return ret; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = dma->port->port_num; + port_cfg.ch_mask = ((1 << channels) - 1); + ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to configure port\n"); + return ret; + } + dma->stream_state = STREAM_STATE_CONFIG_STREAM; + return 0; +} + +int cnl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int ret = 0; + + dma = snd_soc_dai_get_dma_data(dai, substream); + + if (dma->stream_state == STREAM_STATE_UNPREPARE_STREAM) { + ret = sdw_release_stream(dma->mstr, NULL, dma->stream_tag); + if (ret) + dev_err(dai->dev, "Unable to release stream\n"); + dma->stream_state = STREAM_STATE_RELEASE_STREAM; + if (dma->port && dma->stream_state == + STREAM_STATE_RELEASE_STREAM) { + /* Even if release fails, we continue, + * while winding up we have + * to continue till last one gets winded up + */ + cnl_sdw_free_port(dma->mstr, dma->port->port_num); + dma->stream_state = STREAM_STATE_FREE_STREAM; + dma->port = NULL; + } + } + return 0; +} + +int cnl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + return ret; +} + +int cnl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int ret = 0; + + dma = snd_soc_dai_get_dma_data(dai, substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_prepare_and_enable(dma->stream_tag, true); + dma->stream_state = STREAM_STATE_ENABLE_STREAM; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_and_unprepare(dma->stream_tag, true); + dma->stream_state = STREAM_STATE_UNPREPARE_STREAM; + break; + + default: + return -EINVAL; + } + if (ret) + dev_err(dai->dev, "SoundWire commit changes failed\n"); + return ret; + +} + +void cnl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_remove_stream_tag(substream, dai); + sdw_release_stream_tag(dma->stream_tag); + dma->stream_state = STREAM_STATE_FREE_STREAM_TAG; + kfree(dma); +} diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.h b/sound/soc/intel/skylake/skl-sdw-pcm.h new file mode 100644 index 000000000000..ab1314a6f408 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sdw-pcm.h @@ -0,0 +1,41 @@ +/* + * skl-sdw-pcm.h - Shared header file skylake PCM operations on soundwire + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef _SKL_SDW_PCM_H +#define _SKL_SDW_PCM_H +#include +#include +#include + +int cnl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int cnl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai); +void cnl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +#endif /* _SKL_SDW_PCM_H */ From c1b50743af4f0ca7c87aa18e285c5c0fe653a153 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Mon, 14 Mar 2016 18:07:38 +0530 Subject: [PATCH 0020/1276] ASoC:CNL: Add SoundWire machine file. This patch adds the machine driver file for the SoundWire codec cirrus logic cs4l42 and SKL platform driver. Change-Id: I9add4935f6fa8513ce21888214d790fa7d8ff4d7 Signed-off-by: Hardik T Shah Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_cs42l42.c | 280 +++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_cs42l42.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cccda87f4b34..35787e69f31b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -295,6 +295,17 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_CNL_CS42L42_MACH + tristate "Cannonlake with CS42L42 SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_CS42L42 + select SND_SOC_DMIC + help + This adds support for ASoC CS42L42 codec SDW machine driver. This + will create an alsa sound card for CS42L42. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 87ef8b4058e5..4298c5ad8ca0 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -46,3 +47,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c new file mode 100644 index 000000000000..78fb9cc1389e --- /dev/null +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -0,0 +1,280 @@ +/* + * cnl_cs42l42.c - ASOC Machine driver for Intel cnl_cs42l42 platform + * with CS42L42 soundwire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_cs42l42_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_cs42l42_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), +}; + +static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + + + { "sdw_codec0_in", NULL, "SDW Rx" }, + { "SDW Rx", NULL, "Capture" }, + + +}; + +static const struct snd_kcontrol_new cnl_cs42l42_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), +}; + +static struct snd_soc_dai *cnl_cs42l42_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strncmp(rtd->codec_dai->name, dai_name, + strlen(dai_name))) + return rtd->codec_dai; + } + pr_err("%s: unable to find codec dai\n", __func__); + + return NULL; +} + +static int cnl_cs42l42_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_dai *cs_dai = cnl_cs42l42_get_codec_dai(card, "cs42l42"); + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + /*Switch to PLL */ + ret = snd_soc_dai_set_sysclk(cs_dai, 0, 9600000, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to set SYSCLK to FLL1: %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, cnl_cs42l42_controls, + ARRAY_SIZE(cnl_cs42l42_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_cs42l42_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_cs42l42_ops = { + .startup = cnl_cs42l42_startup, +}; + +static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_cs42l42_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_cs42l42_ops, + }, + { + .name = "SDW0-Codec", + .cpu_dai_name = "SDW Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-00:01:fa:42:42:00", + .codec_dai_name = "cs42l42", + .be_hw_params_fixup = cnl_cs42l42_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + }, + +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_cs42l42 = { + .name = "cnl_cs42l42-audio", + .dai_link = cnl_cs42l42_msic_dailink, + .num_links = ARRAY_SIZE(cnl_cs42l42_msic_dailink), + .dapm_widgets = cnl_cs42l42_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_cs42l42_widgets), + .dapm_routes = cnl_cs42l42_map, + .num_dapm_routes = ARRAY_SIZE(cnl_cs42l42_map), +}; + + +static int snd_cnl_cs42l42_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_cs42l42_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_cs42l42.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_cs42l42, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_cs42l42); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_cs42l42); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_cs42l42_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_cs42l42_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_cnl_cs42l42_mc_driver = { + .driver = { + .name = "cnl_cs42l42", + }, + .probe = snd_cnl_cs42l42_mc_probe, + .remove = snd_cnl_cs42l42_mc_remove, +}; + +static int snd_cnl_cs42l42_driver_init(void) +{ + pr_info("Canonlake Machine Driver cnl_cs42l42: cs42l42 registered\n"); + return platform_driver_register(&snd_cnl_cs42l42_mc_driver); +} +module_init(snd_cnl_cs42l42_driver_init); + +static void snd_cnl_cs42l42_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_cs42l42_mc_driver); +} +module_exit(snd_cnl_cs42l42_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_cs42l42"); From 3a8bbbbd305cb858730737f87b607a7cbc205e5c Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 10:17:13 +0530 Subject: [PATCH 0021/1276] SoundWire:Intel: Register 4 master controller to the bus. This patch registers the 4 master controller to the bus. Actually this number should come from ACPI BIOS. Change-Id: Id96731765271b72e7f0f5908e17f76996be3b886 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 335ec68b6a32..c90870ee8a61 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,7 +509,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ - dsp->num_sdw_controllers = 1; + dsp->num_sdw_controllers = 4; master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); From f7b71b57df8921c598790a40c80f454d34de754d Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 11:19:29 +0530 Subject: [PATCH 0022/1276] REVERTME:SDW:CNL: Register only 3 master controller to bus. There is constant BUS errors from the slaves on the 4th SDW bus. This results in kernel dumps, as bus driver reports the bus errors. This is a FPGA slave issue. So when FPGA slave is connected register only 3 master controllers. Change-Id: I0c61d5611ddf51dc56ce87e7b1e389d6638698bc Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c90870ee8a61..e4d531680a5a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,7 +509,12 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ +#ifdef CONFIG_SDW_MAXIM_SLAVE + + dsp->num_sdw_controllers = 3; +#else dsp->num_sdw_controllers = 4; +#endif master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); From 5f57f4534539996635c9d311de095e5098351cfd Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 17:44:39 +0530 Subject: [PATCH 0023/1276] ASoC:CNL: Add support for DMIC link in SDW machine driver. Add support for DMIC link in the SoundWire machine driver. Change-Id: Ia491792ea9561c6f4cf11250167d6eda82b8d555 Signed-off-by: Hardik T Shah --- sound/soc/intel/boards/cnl_cs42l42.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c index 78fb9cc1389e..879aa4e552f3 100644 --- a/sound/soc/intel/boards/cnl_cs42l42.c +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -47,6 +47,7 @@ struct cnl_cs42l42_mc_private { static const struct snd_soc_dapm_widget cnl_cs42l42_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { @@ -62,6 +63,9 @@ static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { { "sdw_codec0_in", NULL, "SDW Rx" }, { "SDW Rx", NULL, "Capture" }, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, }; @@ -158,6 +162,16 @@ static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { { .name = "Bxtn Audio Port", From 2ed308379eac78cbfe821477bfd29b90309049de Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:06:12 +0530 Subject: [PATCH 0024/1276] ASoC:SKL: Add DAI for the SoundWire PDM interface. Add DAI for the PDM Capture between PDM Codec and SoundWire Master. Change-Id: I6d7ba95d06ee7143cb303dcf0a8ae436f6aa1742 Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d411cf28dd91..da4461762c4e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1111,6 +1111,21 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 capture pin, for PDM ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW PDM Pin", + .ops = &skl_sdw_dai_ops, + .capture = { + .stream_name = "SDW Rx1", + .channels_min = HDA_MONO, + .channels_max = HDA_QUAD, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, From 4c9ac680801a8a8c2b9bfe4f0a1a6c4d4b8866c8 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:15:06 +0530 Subject: [PATCH 0025/1276] Intel:ASoc: Handle SDW PCM hw_params for PDM. Bus driver needs to be configured differently for the PCM and PDM interface between master and slave. Handle the PDM case. Currently upscale factor and the BPS should come from either NHLT or XML. Need to figure out from poland team from where its coming. Change-Id: Ic2e6080f0502f9212ab4256aecda18880248d16b Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 90a11f47586b..af8d117c34ed 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -113,6 +113,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, int ret = 0; struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; + int upscale_factor = 16; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -145,9 +146,21 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, } m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; stream_config.frame_rate = params_rate(params); + /* TODO: Get the multiplication factor from NHLT or the XML + * to decide with Poland team from where to get it + */ + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + stream_config.frame_rate *= upscale_factor; stream_config.channel_count = channels; - stream_config.bps = + /* TODO: Get the PDM BPS from NHLT or the XML + * to decide with Poland team from where to get it + */ + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + stream_config.bps = 1; + else + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; ret = sdw_config_stream(dma->mstr, NULL, &stream_config, dma->stream_tag); From e464ed386c3e7c1112af5a12a1de706e7c53b60c Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:41:07 +0530 Subject: [PATCH 0026/1276] ASoC:Codecs: Add support for SV FPGA SoundWire PDM Codec. Change-Id: I4f48659c9be3238b78ee911449557142a694bf0b Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/codecs/Kconfig | 17 ++ sound/soc/codecs/Makefile | 6 + sound/soc/codecs/svfpga-i2c.c | 90 +++++++ sound/soc/codecs/svfpga-sdw.c | 142 ++++++++++ sound/soc/codecs/svfpga.c | 485 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/svfpga.h | 398 ++++++++++++++++++++++++++++ 6 files changed, 1138 insertions(+) create mode 100644 sound/soc/codecs/svfpga-i2c.c create mode 100644 sound/soc/codecs/svfpga-sdw.c create mode 100644 sound/soc/codecs/svfpga.c create mode 100644 sound/soc/codecs/svfpga.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efb095dbcd71..461f41268b68 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -234,6 +234,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_SVFPGA_I2C if I2C help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -465,6 +466,22 @@ config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C +config SND_SOC_SVFPGA + tristate "Intel SVFPGA Codec" + depends on I2C + depends on SDW + +config SND_SOC_SVFPGA_SDW + tristate "Intel SVFPGA Codec - SDW" + depends on SDW + select SND_SOC_SVFPGA + select REGMAP_SDW + +config SND_SOC_SVFPGA_I2C + tristate "Intel SVFPGA Codec - I2C" + depends on I2C + select SND_SOC_SVFPGA + config SND_SOC_CS42L51 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7ae7c85e8219..f37fca3b9960 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -46,6 +46,9 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs42l42-objs := cs42l42.o +snd-soc-svfpga-objs := svfpga.o +snd-soc-svfpga-i2c-objs := svfpga-i2c.o +snd-soc-svfpga-sdw-objs := svfpga-sdw.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -306,6 +309,9 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o +obj-$(CONFIG_SND_SOC_SVFPGA) += snd-soc-svfpga.o +obj-$(CONFIG_SND_SOC_SVFPGA_I2C) += snd-soc-svfpga-i2c.o +obj-$(CONFIG_SND_SOC_SVFPGA_SDW) += snd-soc-svfpga-sdw.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o diff --git a/sound/soc/codecs/svfpga-i2c.c b/sound/soc/codecs/svfpga-i2c.c new file mode 100644 index 000000000000..07dc09f9ec5a --- /dev/null +++ b/sound/soc/codecs/svfpga-i2c.c @@ -0,0 +1,90 @@ +/* + * svfpga-i2c.c -- SVFPGA ALSA SoC audio driver + * + * Copyright 2015 Intel, Inc. + * + * Author: Hardik Shah + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "svfpga.h" + +extern const struct regmap_config svfpga_i2c_regmap; +extern const struct dev_pm_ops svfpga_runtime_pm; +static int svfpga_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct regmap *regmap; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + regmap = devm_regmap_init_i2c(i2c, &svfpga_i2c_regmap); + return svfpga_probe(&i2c->dev, regmap, NULL); +} + +static int svfpga_i2c_remove(struct i2c_client *i2c) +{ + return svfpga_remove(&i2c->dev); +} + +static const struct of_device_id svfpga_of_match[] = { + { .compatible = "svfpga", }, + {}, +}; +MODULE_DEVICE_TABLE(of, svfpga_of_match); + + +static const struct i2c_device_id svfpga_id[] = { + {"svfpga", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, svfpga_id); + +static struct i2c_driver svfpga_i2c_driver = { + .driver = { + .name = "svfpga", + .pm = &svfpga_runtime_pm, + .of_match_table = svfpga_of_match, + }, + .id_table = svfpga_id, + .probe = svfpga_i2c_probe, + .remove = svfpga_i2c_remove, +}; + +module_i2c_driver(svfpga_i2c_driver); + +MODULE_DESCRIPTION("ASoC SVFPGA driver I2C"); +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include "svfpga.h" + +extern const struct regmap_config svfpga_sdw_regmap; +extern const struct dev_pm_ops svfpga_runtime_pm; + +static int svfpga_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + int i, j; + + cap.wake_up_unavailable = true; + cap.test_mode_supported = false; + cap.clock_stop1_mode_supported = false; + cap.simplified_clock_stop_prepare = false; + cap.highphy_capable = true; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 0; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 3; + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 0; i < cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i]; + if (i == 0 || i == 2) + dpn_cap->port_direction = SDW_PORT_SOURCE; + else + dpn_cap->port_direction = SDW_PORT_SINK; + + dpn_cap->port_number = i+1; + dpn_cap->max_word_length = 24; + dpn_cap->min_word_length = 16; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; + dpn_cap->imp_def_intr_mask = 0x0; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT | + SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE | + SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 16000000; + prop->min_frequency = 1000000; + prop->num_freq_configs = 0; + prop->freq_supported = NULL; + prop->glitchless_transitions_mask = 0x1; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int svfpga_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct regmap *regmap; + int ret; + + /* Wait for codec internal initialization for 2.5ms minimum */ + msleep(5); + regmap = devm_regmap_init_sdwint(sdw, &svfpga_sdw_regmap); + if (!regmap) + return -EINVAL; + ret = svfpga_register_sdw_capabilties(sdw, sdw_id); + if (ret) + return ret; + return svfpga_probe(&sdw->dev, regmap, sdw); +} + +static int svfpga_sdw_remove(struct sdw_slv *sdw) +{ + return svfpga_remove(&sdw->dev); +} + +static const struct sdw_slv_id svfpga_id[] = { + {"00:01:fa:42:42:00", 0}, + {"14:13:20:05:86:80", 0}, + {} +}; + +MODULE_DEVICE_TABLE(sdwint, svfpga_id); + +static struct sdw_slave_driver svfpga_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "svfpga-codec", + .pm = &svfpga_runtime_pm, + }, + .probe = svfpga_sdw_probe, + .remove = svfpga_sdw_remove, + .id_table = svfpga_id, +}; + +module_sdw_slave_driver(svfpga_sdw_driver); + +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* #define DEBUG 1 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "svfpga.h" + +struct sdw_stream_data { + int stream_tag; +}; + + + +static const struct reg_default svfpga_reg_defaults[] = { +}; + +static bool svfpga_readable_register(struct device *dev, unsigned int reg) +{ + return false; +} + +static bool svfpga_volatile_register(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_range_cfg svfpga_page_range = { + .name = "Pages", + .range_min = 0, + .range_max = 0, + .selector_reg = SVFPGA_PAGE_REGISTER, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 256, +}; + +const struct regmap_config svfpga_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = svfpga_readable_register, + .volatile_reg = svfpga_volatile_register, + + .ranges = &svfpga_page_range, + .num_ranges = 1, + + .max_register = 0, + .reg_defaults = svfpga_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(svfpga_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(svfpga_i2c_regmap); + +const struct regmap_config svfpga_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + + .readable_reg = svfpga_readable_register, + .volatile_reg = svfpga_volatile_register, + + .max_register = SVFPGA_MAX_REGISTER, + .reg_defaults = svfpga_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(svfpga_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(svfpga_sdw_regmap); + +static int svfpga_hpdrv_evt(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} + +static const struct snd_soc_dapm_widget svfpga_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("I2NP"), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, SVFPGA_ASP_CLK_CFG, + SVFPGA_ASP_SCLK_EN_SHIFT, false), + SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, + 0, NULL, 0, svfpga_hpdrv_evt, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD) +}; + +static const struct snd_soc_dapm_route svfpga_audio_map[] = { + {"SDIN", NULL, "Playback"}, + {"HPDRV", NULL, "SDIN"}, + {"HP", NULL, "HPDRV"}, + {"Capture", NULL, "I2NP"}, + +}; + +static int svfpga_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_svfpga = { + .set_bias_level = svfpga_set_bias_level, + .dapm_widgets = svfpga_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(svfpga_dapm_widgets), + .dapm_routes = svfpga_audio_map, + .num_dapm_routes = ARRAY_SIZE(svfpga_audio_map), +}; + +static int svfpga_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream_tag) +{ + struct sdw_stream_data *stream_data; + + stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL); + if (!stream_data) + return -ENOMEM; + stream_data->stream_tag = stream_tag; + snd_soc_dai_set_dma_data(dai, substream, stream_data); + return 0; +} + +static int svfpga_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream_data; + + stream_data = snd_soc_dai_get_dma_data(dai, substream); + kfree(stream_data); + return 0; +} + + + +static int svfpga_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct svfpga_private *svfpga = snd_soc_component_get_drvdata(component); + int retval; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + struct sdw_stream_data *stream; + int port; + int num_channels; + int upscale_factor = 16; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!svfpga->sdw) + return 0; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_IN; + port = 2; + } else { + direction = SDW_DATA_DIR_OUT; + port = 0; + } + stream_config.frame_rate = params_rate(params); + stream_config.frame_rate *= upscale_factor; + stream_config.channel_count = params_channels(params); + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.bps = 1; + stream_config.direction = direction; + retval = sdw_config_stream(svfpga->sdw->mstr, svfpga->sdw, &stream_config, + stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return retval; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = port; + num_channels = params_channels(params); + port_cfg.ch_mask = (1 << (num_channels)) - 1; + retval = sdw_config_port(svfpga->sdw->mstr, svfpga->sdw, + &port_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return retval; +} + +int svfpga_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct svfpga_private *svfpga = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = snd_soc_dai_get_dma_data(dai, + substream); + if (!svfpga->sdw) + return 0; + sdw_release_stream(svfpga->sdw->mstr, svfpga->sdw, stream->stream_tag); + return 0; +} + +static struct snd_soc_dai_ops svfpga_ops = { + .hw_params = svfpga_pcm_hw_params, + .hw_free = svfpga_pcm_hw_free, + .program_stream_tag = svfpga_program_stream_tag, + .remove_stream_tag = svfpga_remove_stream_tag, +}; + +static struct snd_soc_dai_driver svfpga_dai = { + .name = "svfpga", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &svfpga_ops, +}; + +int svfpga_probe(struct device *dev, struct regmap *regmap, + struct sdw_slv *slave) +{ + struct svfpga_private *svfpga; + int ret; + struct sdw_msg rd_msg, wr_msg; + u8 rbuf[1] = {0}, wbuf[1] = {0}; + + + svfpga = devm_kzalloc(dev, sizeof(struct svfpga_private), + GFP_KERNEL); + if (!svfpga) + return -ENOMEM; + + dev_set_drvdata(dev, svfpga); + + svfpga->regmap = regmap; + svfpga->sdw = slave; + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_svfpga, &svfpga_dai, 1); + dev_info(&slave->dev, "Configuring codec for pattern\n"); + + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = 0x8; + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = slave->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1804; + wbuf[0] = 0x1; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = 0x180b; + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = slave->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(slave->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180b; + wbuf[0] = 0x1a; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180c; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180d; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180e; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180f; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1810; + wbuf[0] = 0xb8; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1811; + wbuf[0] = 0xd; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1812; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1813; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181c; + wbuf[0] = 0x6e; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181d; + wbuf[0] = 0x3; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181e; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181f; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180b; + wbuf[0] = 0x1b; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + rd_msg.addr = 0x180b; + rbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + dev_info(&slave->dev, "Codec programmed with Value %x\n", rbuf[0]); + return ret; +out: + return -EIO; +} +EXPORT_SYMBOL(svfpga_probe); + +int svfpga_remove(struct device *dev) +{ + + dev_info(dev, "Removing\n"); + + return 0; +} +EXPORT_SYMBOL(svfpga_remove); + +#ifdef CONFIG_PM +static int svfpga_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int svfpga_runtime_resume(struct device *dev) +{ + + return 0; +} +#endif + +const struct dev_pm_ops svfpga_runtime_pm = { + SET_RUNTIME_PM_OPS(svfpga_runtime_suspend, svfpga_runtime_resume, + NULL) +}; +EXPORT_SYMBOL(svfpga_runtime_pm); + +MODULE_DESCRIPTION("ASoC SVFPGA driver"); +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __SVFPGA_H__ +#define __SVFPGA_H__ + +#include + +#define SVFPGA_PAGE_REGISTER 0x00 /* Page Select Register */ +#define SVFPGA_WIN_START 0x00 +#define SVFPGA_WIN_LEN 0x100 +#define SVFPGA_RANGE_MIN 0x00 +#define SVFPGA_RANGE_MAX 0x7F + +#define SVFPGA_PAGE_10 0x9000 +#define SVFPGA_PAGE_11 0x9100 +#define SVFPGA_PAGE_12 0x9200 +#define SVFPGA_PAGE_13 0x9300 +#define SVFPGA_PAGE_15 0x9500 +#define SVFPGA_PAGE_19 0x9900 +#define SVFPGA_PAGE_1B 0x9B00 +#define SVFPGA_PAGE_1C 0x9C00 +#define SVFPGA_PAGE_1D 0x9D00 +#define SVFPGA_PAGE_1F 0x9F00 +#define SVFPGA_PAGE_20 0xA000 +#define SVFPGA_PAGE_21 0xA100 +#define SVFPGA_PAGE_23 0xA300 +#define SVFPGA_PAGE_24 0xA400 +#define SVFPGA_PAGE_25 0xA500 +#define SVFPGA_PAGE_26 0xA600 +#define SVFPGA_PAGE_28 0xA800 +#define SVFPGA_PAGE_29 0xA900 +#define SVFPGA_PAGE_2A 0xAA00 +#define SVFPGA_PAGE_30 0xB000 + +#define SVFPGA_CHIP_ID 0x42A42 + +/* Page 0x10 Global Registers */ +#define SVFPGA_DEVID_AB (SVFPGA_PAGE_10 + 0x01) +#define SVFPGA_DEVID_CD (SVFPGA_PAGE_10 + 0x02) +#define SVFPGA_DEVID_E (SVFPGA_PAGE_10 + 0x03) +#define SVFPGA_FABID (SVFPGA_PAGE_10 + 0x04) +#define SVFPGA_REVID (SVFPGA_PAGE_10 + 0x05) +#define SVFPGA_FRZ_CTL (SVFPGA_PAGE_10 + 0x06) +#define SVFPGA_SRC_CTL (SVFPGA_PAGE_10 + 0x07) +#define SVFPGA_MCLK_STAT (SVFPGA_PAGE_10 + 0x08) + +#define SVFPGA_MCLK_CTL (SVFPGA_PAGE_10 + 0x09) +#define SVFPGA_INTERNAL_FS_SHIFT 1 +#define SVFPGA_INTERNAL_FS_MASK (1 << SVFPGA_INTERNAL_FS_SHIFT) + +#define SVFPGA_SFTRAMP_RATE (SVFPGA_PAGE_10 + 0x0A) +#define SVFPGA_I2C_DEBOUNCE (SVFPGA_PAGE_10 + 0x0E) +#define SVFPGA_I2C_STRETCH (SVFPGA_PAGE_10 + 0x0F) +#define SVFPGA_I2C_TIMEOUT (SVFPGA_PAGE_10 + 0x10) + +/* Page 0x11 Power and Headset Detect Registers */ +#define SVFPGA_PWR_CTL1 (SVFPGA_PAGE_11 + 0x01) +#define SVFPGA_ASP_DAI_PDN_SHIFT 6 +#define SVFPGA_ASP_DAI_PDN_MASK (1 << SVFPGA_ASP_DAI_PDN_SHIFT) +#define SVFPGA_MIXER_PDN_SHIFT 5 +#define SVFPGA_MIXER_PDN_MASK (1 << SVFPGA_MIXER_PDN_SHIFT) +#define SVFPGA_HP_PDN_SHIFT 3 +#define SVFPGA_HP_PDN_MASK (1 << SVFPGA_HP_PDN_SHIFT) +#define SVFPGA_PDN_ALL_SHIFT 0 +#define SVFPGA_PDN_ALL_MASK (1 << SVFPGA_PDN_ALL_SHIFT) + +#define SVFPGA_PWR_CTL2 (SVFPGA_PAGE_11 + 0x02) +#define SVFPGA_PWR_CTL3 (SVFPGA_PAGE_11 + 0x03) +#define SVFPGA_RSENSE_CTL1 (SVFPGA_PAGE_11 + 0x04) +#define SVFPGA_RSENSE_CTL2 (SVFPGA_PAGE_11 + 0x05) + +#define SVFPGA_OSC_SWITCH (SVFPGA_PAGE_11 + 0x07) +#define SVFPGA_SCLK_PRESENT_SHIFT 0 +#define SVFPGA_SCLK_PRESENT_MASK (1 << SVFPGA_SCLK_PRESENT_SHIFT) + +#define SVFPGA_OSC_SWITCH_STATUS (SVFPGA_PAGE_11 + 0x09) +#define SVFPGA_RSENSE_CTL3 (SVFPGA_PAGE_11 + 0x12) +#define SVFPGA_TSENSE_CTL (SVFPGA_PAGE_11 + 0x13) +#define SVFPGA_TRSENSE_STATUS (SVFPGA_PAGE_11 + 0x15) +#define SVFPGA_HSDET_CTL1 (SVFPGA_PAGE_11 + 0x1F) +#define SVFPGA_HSDET_CTL2 (SVFPGA_PAGE_11 + 0x20) +#define SVFPGA_HS_SWITCH_CTL (SVFPGA_PAGE_11 + 0x21) +#define SVFPGA_HS_DET_STATUS (SVFPGA_PAGE_11 + 0x24) +#define SVFPGA_HS_CLAMP_DISABLE (SVFPGA_PAGE_11 + 0x29) + +/* Page 0x12 Clocking Registers */ +#define SVFPGA_MCLK_SRC_SEL (SVFPGA_PAGE_12 + 0x01) +#define SVFPGA_MCLKDIV_SHIFT 1 +#define SVFPGA_MCLKDIV_MASK (1 << SVFPGA_MCLKDIV_SHIFT) +#define SVFPGA_MCLK_SRC_SEL_SHIFT 0 +#define SVFPGA_MCLK_SRC_SEL_MASK (1 << SVFPGA_MCLK_SRC_SEL_SHIFT) + +#define SVFPGA_SPDIF_CLK_CFG (SVFPGA_PAGE_12 + 0x02) + +#define SVFPGA_FSYNC_PW_LOWER (SVFPGA_PAGE_12 + 0x03) +#define SVFPGA_FSYNC_PW_UPPER (SVFPGA_PAGE_12 + 0x04) +#define SVFPGA_FSYNC_PULSE_WIDTH_SHIFT 0 +#define SVFPGA_FSYNC_PULSE_WIDTH_MASK (0xff << SVFPGA_FSYNC_PULSE_WIDTH_SHIFT) + +#define SVFPGA_FSYNC_P_LOWER (SVFPGA_PAGE_12 + 0x05) +#define SVFPGA_FSYNC_P_UPPER (SVFPGA_PAGE_12 + 0x06) +#define SVFPGA_FSYNC_PERIOD_SHIFT 0 +#define SVFPGA_FSYNC_PERIOD_MASK (0xff << SVFPGA_FSYNC_PERIOD_SHIFT) + +#define SVFPGA_ASP_CLK_CFG (SVFPGA_PAGE_12 + 0x07) +#define SVFPGA_ASP_SCLK_EN_SHIFT 5 +#define SVFPGA_ASP_SCLK_EN_MASK (1 << SVFPGA_ASP_SCLK_EN_SHIFT) +#define SVFPGA_ASP_MASTER_MODE 0x01 +#define SVFPGA_ASP_SLAVE_MODE 0x00 +#define SVFPGA_ASP_MODE_SHIFT 4 +#define SVFPGA_ASP_MODE_MASK (1 << SVFPGA_ASP_MODE_SHIFT) +#define SVFPGA_ASP_SCPOL_IN_DAC_SHIFT 2 +#define SVFPGA_ASP_SCPOL_IN_DAC_MASK (1 << SVFPGA_ASP_SCPOL_IN_DAC_SHIFT) +#define SVFPGA_ASP_LCPOL_IN_SHIFT 0 +#define SVFPGA_ASP_LCPOL_IN_MASK (1 << SVFPGA_ASP_LCPOL_IN_SHIFT) +#define SVFPGA_ASP_POL_INV 1 + +#define SVFPGA_ASP_FRM_CFG (SVFPGA_PAGE_12 + 0x08) +#define SVFPGA_ASP_5050_SHIFT 3 +#define SVFPGA_ASP_5050_MASK (1 << SVFPGA_ASP_5050_SHIFT) +#define SVFPGA_ASP_FSD_SHIFT 0 +#define SVFPGA_ASP_FSD_MASK (7 << SVFPGA_ASP_FSD_SHIFT) +#define SVFPGA_ASP_FSD_1_0 2 + +#define SVFPGA_FS_RATE_EN (SVFPGA_PAGE_12 + 0x09) +#define SVFPGA_FS_EN_SHIFT 0 +#define SVFPGA_FS_EN_MASK (0xf << SVFPGA_FS_EN_SHIFT) +#define SVFPGA_FS_EN_IASRC_96K 0x1 +#define SVFPGA_FS_EN_OASRC_96K 0x2 + +#define SVFPGA_IN_ASRC_CLK (SVFPGA_PAGE_12 + 0x0A) +#define SVFPGA_CLK_IASRC_SEL_SHIFT 0 +#define SVFPGA_CLK_IASRC_SEL_MASK (1 << SVFPGA_CLK_IASRC_SEL_SHIFT) +#define SVFPGA_CLK_IASRC_SEL_12 1 + +#define SVFPGA_OUT_ASRC_CLK (SVFPGA_PAGE_12 + 0x0B) +#define SVFPGA_CLK_OASRC_SEL_SHIFT 0 +#define SVFPGA_CLK_OASRC_SEL_MASK (1 << SVFPGA_CLK_OASRC_SEL_SHIFT) +#define SVFPGA_CLK_OASRC_SEL_12 1 + +#define SVFPGA_PLL_DIV_CFG1 (SVFPGA_PAGE_12 + 0x0C) +#define SVFPGA_SCLK_PREDIV_SHIFT 0 +#define SVFPGA_SCLK_PREDIV_MASK (3 << SVFPGA_SCLK_PREDIV_SHIFT) + +/* Page 0x13 Interrupt Registers */ +/* Interrupts */ +#define SVFPGA_ADC_OVFL_STATUS (SVFPGA_PAGE_13 + 0x01) +#define SVFPGA_MIXER_STATUS (SVFPGA_PAGE_13 + 0x02) +#define SVFPGA_SRC_STATUS (SVFPGA_PAGE_13 + 0x03) +#define SVFPGA_ASP_RX_STATUS (SVFPGA_PAGE_13 + 0x04) +#define SVFPGA_ASP_TX_STATUS (SVFPGA_PAGE_13 + 0x05) +#define SVFPGA_CODEC_STATUS (SVFPGA_PAGE_13 + 0x08) +#define SVFPGA_ALTDET_STATUS1 (SVFPGA_PAGE_13 + 0x09) +#define SVFPGA_ALTDET_STATUS2 (SVFPGA_PAGE_13 + 0x0A) +#define SVFPGA_DACADC_LSTATUS (SVFPGA_PAGE_13 + 0x0B) +#define SVFPGA_VPMON_STATUS (SVFPGA_PAGE_13 + 0x0D) +#define SVFPGA_PLL_LOCK (SVFPGA_PAGE_13 + 0x0E) +#define SVFPGA_TSRS_PLUG_STATUS (SVFPGA_PAGE_13 + 0x0F) +/* Masks */ +#define SVFPGA_ADC_OVFL_MASK (SVFPGA_PAGE_13 + 0x16) +#define SVFPGA_MIXER_MASK (SVFPGA_PAGE_13 + 0x17) +#define SVFPGA_SRC_MASK (SVFPGA_PAGE_13 + 0x18) +#define SVFPGA_ASP_RX_MASK (SVFPGA_PAGE_13 + 0x19) +#define SVFPGA_ASP_TX_MASK (SVFPGA_PAGE_13 + 0x1A) +#define SVFPGA_CODEC_MASK (SVFPGA_PAGE_13 + 0x1B) +#define SVFPGA_DACADC_LMASK (SVFPGA_PAGE_13 + 0x1C) +#define SVFPGA_VPMON_MASK (SVFPGA_PAGE_13 + 0x1E) +#define SVFPGA_PLL_LOCK_MASK (SVFPGA_PAGE_13 + 0x1F) +#define SVFPGA_TSRS_PLUG_MASK (SVFPGA_PAGE_13 + 0x20) + +/* Page 0x15 Fractional-N PLL Registers */ +#define SVFPGA_PLL_CTL1 (SVFPGA_PAGE_15 + 0x01) +#define SVFPGA_PLL_START_SHIFT 0 +#define SVFPGA_PLL_START_MASK (1 << SVFPGA_PLL_START_SHIFT) + +#define SVFPGA_PLL_DIV_FRAC0 (SVFPGA_PAGE_15 + 0x02) +#define SVFPGA_PLL_DIV_FRAC_SHIFT 0 +#define SVFPGA_PLL_DIV_FRAC_MASK (0xff << SVFPGA_PLL_DIV_FRAC_SHIFT) +#define SVFPGA_PLL_DIV_FRAC1 (SVFPGA_PAGE_15 + 0x03) +#define SVFPGA_PLL_DIV_FRAC2 (SVFPGA_PAGE_15 + 0x04) + +#define SVFPGA_PLL_DIV_INT (SVFPGA_PAGE_15 + 0x05) +#define SVFPGA_PLL_DIV_INT_SHIFT 0 +#define SVFPGA_PLL_DIV_INT_MASK (0xff << SVFPGA_PLL_DIV_INT_SHIFT) + +#define SVFPGA_PLL_CTL3 (SVFPGA_PAGE_15 + 0x08) +#define SVFPGA_PLL_DIVOUT_SHIFT 0 +#define SVFPGA_PLL_DIVOUT_MASK (0xff << SVFPGA_PLL_DIVOUT_SHIFT) + +#define SVFPGA_PLL_CAL_RATIO (SVFPGA_PAGE_15 + 0x0A) +#define SVFPGA_PLL_CAL_RATIO_SHIFT 0 +#define SVFPGA_PLL_CAL_RATIO_MASK (0xff << SVFPGA_PLL_CAL_RATIO_SHIFT) + +#define SVFPGA_PLL_CTL4 (SVFPGA_PAGE_15 + 0x1B) +#define SVFPGA_PLL_MODE_SHIFT 0 +#define SVFPGA_PLL_MODE_MASK (3 << SVFPGA_PLL_MODE_SHIFT) + +/* Page 0x19 HP Load Detect Registers */ +#define SVFPGA_LOAD_DET_RCSTAT (SVFPGA_PAGE_19 + 0x25) +#define SVFPGA_RLA_STAT_SHIFT 0 +#define SVFPGA_RLA_STAT_MASK (3 << SVFPGA_RLA_STAT_SHIFT) +#define SVFPGA_RLA_STAT_15_OHM 0 + +#define SVFPGA_LOAD_DET_DONE (SVFPGA_PAGE_19 + 0x26) +#define SVFPGA_LOAD_DET_EN (SVFPGA_PAGE_19 + 0x27) + +/* Page 0x1B Headset Interface Registers */ +#define SVFPGA_HSBIAS_SC_AUTOCTL (SVFPGA_PAGE_1B + 0x70) +#define SVFPGA_WAKE_CTL (SVFPGA_PAGE_1B + 0x71) +#define SVFPGA_ADC_DISABLE_MUTE (SVFPGA_PAGE_1B + 0x72) +#define SVFPGA_TIPSENSE_CTL (SVFPGA_PAGE_1B + 0x73) +#define SVFPGA_MISC_DET_CTL (SVFPGA_PAGE_1B + 0x74) +#define SVFPGA_MIC_DET_CTL1 (SVFPGA_PAGE_1B + 0x75) +#define SVFPGA_MIC_DET_CTL2 (SVFPGA_PAGE_1B + 0x76) +#define SVFPGA_DET_STATUS1 (SVFPGA_PAGE_1B + 0x77) +#define SVFPGA_DET_STATUS2 (SVFPGA_PAGE_1B + 0x78) +#define SVFPGA_DET_MASK1 (SVFPGA_PAGE_1B + 0x79) +#define SVFPGA_DET_MASK2 (SVFPGA_PAGE_1B + 0x7A) + +/* Page 0x1C Headset Bias Registers */ +#define SVFPGA_HS_BIAS_CTL (SVFPGA_PAGE_1C + 0x03) + +/* Page 0x1D ADC Registers */ +#define SVFPGA_ADC_CTL (SVFPGA_PAGE_1D + 0x01) +#define SVFPGA_ADC_NOTCH_DIS_SHIFT 5 +#define SVFPGA_ADC_FORCE_WEAK_VCM_SHIFT 4 +#define SVFPGA_ADC_INV_SHIFT 2 +#define SVFPGA_ADC_DIG_BOOST_SHIFT 0 + +#define SVFPGA_ADC_VOLUME (SVFPGA_PAGE_1D + 0x03) +#define SVFPGA_ADC_VOL_SHIFT 0 + +#define SVFPGA_ADC_WNF_HPF_CTL (SVFPGA_PAGE_1D + 0x04) +#define SVFPGA_ADC_WNF_CF_SHIFT 4 +#define SVFPGA_ADC_WNF_EN_SHIFT 3 +#define SVFPGA_ADC_HPF_CF_SHIFT 1 +#define SVFPGA_ADC_HPF_EN_SHIFT 0 + +/* Page 0x1F DAC Registers */ +#define SVFPGA_DAC_CTL1 (SVFPGA_PAGE_1F + 0x01) +#define SVFPGA_DACB_INV_SHIFT 1 +#define SVFPGA_DACA_INV_SHIFT 0 + +#define SVFPGA_DAC_CTL2 (SVFPGA_PAGE_1F + 0x06) +#define SVFPGA_HPOUT_LOAD_SHIFT 3 +#define SVFPGA_HPOUT_LOAD_MASK (1 << SVFPGA_HPOUT_LOAD_SHIFT) +#define SVFPGA_HPOUT_CLAMP_SHIFT 2 +#define SVFPGA_HPOUT_CLAMP_MASK (1 << SVFPGA_HPOUT_CLAMP_SHIFT) +#define SVFPGA_DAC_HPF_EN_SHIFT 1 +#define SVFPGA_DAC_HPF_EN_MASK (1 << SVFPGA_DAC_HPF_EN_SHIFT) + +/* Page 0x20 HP CTL Registers */ +#define SVFPGA_HP_CTL (SVFPGA_PAGE_20 + 0x01) +#define SVFPGA_HP_ANA_BMUTE_SHIFT 3 +#define SVFPGA_HP_ANA_BMUTE_MASK (1 << SVFPGA_HP_ANA_BMUTE_SHIFT) +#define SVFPGA_HP_ANA_AMUTE_SHIFT 2 +#define SVFPGA_HP_ANA_AMUTE_MASK (1 << SVFPGA_HP_ANA_AMUTE_SHIFT) +#define SVFPGA_HP_FULL_SCALE_VOL_SHIFT 1 +#define SVFPGA_HP_FULL_SCALE_VOL_MASK (1 << SVFPGA_HP_FULL_SCALE_VOL_SHIFT) + +/* Page 0x21 Class H Registers */ +#define SVFPGA_CLASSH_CTL (SVFPGA_PAGE_21 + 0x01) + +/* Page 0x23 Mixer Volume Registers */ +#define SVFPGA_MIXER_CHA_VOL (SVFPGA_PAGE_23 + 0x01) +#define SVFPGA_MIXER_ADC_VOL (SVFPGA_PAGE_23 + 0x02) +#define SVFPGA_MIXER_CHB_VOL (SVFPGA_PAGE_23 + 0x03) +#define SVFPGA_MIXER_CH_VOL_SHIFT 0 +#define SVFPGA_MIXER_CH_VOL_MASK (0x3f << SVFPGA_MIXER_CH_VOL_SHIFT) + +/* Page 0x24 EQ Registers */ +#define SVFPGA_EQ_COEF_IN0 (SVFPGA_PAGE_24 + 0x01) +#define SVFPGA_EQ_COEF_IN1 (SVFPGA_PAGE_24 + 0x02) +#define SVFPGA_EQ_COEF_IN2 (SVFPGA_PAGE_24 + 0x03) +#define SVFPGA_EQ_COEF_IN3 (SVFPGA_PAGE_24 + 0x04) +#define SVFPGA_EQ_COEF_RW (SVFPGA_PAGE_24 + 0x06) +#define SVFPGA_EQ_COEF_OUT0 (SVFPGA_PAGE_24 + 0x07) +#define SVFPGA_EQ_COEF_OUT1 (SVFPGA_PAGE_24 + 0x08) +#define SVFPGA_EQ_COEF_OUT2 (SVFPGA_PAGE_24 + 0x09) +#define SVFPGA_EQ_COEF_OUT3 (SVFPGA_PAGE_24 + 0x0A) +#define SVFPGA_EQ_INIT_STAT (SVFPGA_PAGE_24 + 0x0B) +#define SVFPGA_EQ_START_FILT (SVFPGA_PAGE_24 + 0x0C) +#define SVFPGA_EQ_MUTE_CTL (SVFPGA_PAGE_24 + 0x0E) + +/* Page 0x25 Audio Port Registers */ +#define SVFPGA_SP_RX_CH_SEL (SVFPGA_PAGE_25 + 0x01) +#define SVFPGA_SP_RX_ISOC_CTL (SVFPGA_PAGE_25 + 0x02) +#define SVFPGA_SP_RX_FS (SVFPGA_PAGE_25 + 0x03) +#define CS42l42_SPDIF_CH_SEL (SVFPGA_PAGE_25 + 0x04) +#define SVFPGA_SP_TX_ISOC_CTL (SVFPGA_PAGE_25 + 0x05) +#define SVFPGA_SP_TX_FS (SVFPGA_PAGE_25 + 0x06) +#define SVFPGA_SPDIF_SW_CTL1 (SVFPGA_PAGE_25 + 0x07) + +/* Page 0x26 SRC Registers */ +#define SVFPGA_SRC_SDIN_FS (SVFPGA_PAGE_26 + 0x01) +#define SVFPGA_SRC_SDOUT_FS (SVFPGA_PAGE_26 + 0x09) + +/* Page 0x28 S/PDIF Registers */ +#define SVFPGA_SPDIF_CTL1 (SVFPGA_PAGE_28 + 0x01) +#define SVFPGA_SPDIF_CTL2 (SVFPGA_PAGE_28 + 0x02) +#define SVFPGA_SPDIF_CTL3 (SVFPGA_PAGE_28 + 0x03) +#define SVFPGA_SPDIF_CTL4 (SVFPGA_PAGE_28 + 0x04) + +/* Page 0x29 Serial Port TX Registers */ +#define SVFPGA_ASP_TX_SZ_EN (SVFPGA_PAGE_29 + 0x01) +#define SVFPGA_ASP_TX_CH_EN (SVFPGA_PAGE_29 + 0x02) +#define SVFPGA_ASP_TX_CH_AP_RES (SVFPGA_PAGE_29 + 0x03) +#define SVFPGA_ASP_TX_CH1_BIT_MSB (SVFPGA_PAGE_29 + 0x04) +#define SVFPGA_ASP_TX_CH1_BIT_LSB (SVFPGA_PAGE_29 + 0x05) +#define SVFPGA_ASP_TX_HIZ_DLY_CFG (SVFPGA_PAGE_29 + 0x06) +#define SVFPGA_ASP_TX_CH2_BIT_MSB (SVFPGA_PAGE_29 + 0x0A) +#define SVFPGA_ASP_TX_CH2_BIT_LSB (SVFPGA_PAGE_29 + 0x0B) + +/* Page 0x2A Serial Port RX Registers */ +#define SVFPGA_ASP_RX_DAI0_EN (SVFPGA_PAGE_2A + 0x01) +#define SVFPGA_ASP_RX0_CH_EN_SHIFT 2 +#define SVFPGA_ASP_RX0_CH_EN_MASK (0xf << SVFPGA_ASP_RX0_CH_EN_SHIFT) +#define SVFPGA_ASP_RX0_CH1_EN 1 +#define SVFPGA_ASP_RX0_CH2_EN 2 +#define SVFPGA_ASP_RX0_CH3_EN 4 +#define SVFPGA_ASP_RX0_CH4_EN 8 + +#define SVFPGA_ASP_RX_DAI0_CH1_AP_RES (SVFPGA_PAGE_2A + 0x02) +#define SVFPGA_ASP_RX_DAI0_CH1_BIT_MSB (SVFPGA_PAGE_2A + 0x03) +#define SVFPGA_ASP_RX_DAI0_CH1_BIT_LSB (SVFPGA_PAGE_2A + 0x04) +#define SVFPGA_ASP_RX_DAI0_CH2_AP_RES (SVFPGA_PAGE_2A + 0x05) +#define SVFPGA_ASP_RX_DAI0_CH2_BIT_MSB (SVFPGA_PAGE_2A + 0x06) +#define SVFPGA_ASP_RX_DAI0_CH2_BIT_LSB (SVFPGA_PAGE_2A + 0x07) +#define SVFPGA_ASP_RX_DAI0_CH3_AP_RES (SVFPGA_PAGE_2A + 0x08) +#define SVFPGA_ASP_RX_DAI0_CH3_BIT_MSB (SVFPGA_PAGE_2A + 0x09) +#define SVFPGA_ASP_RX_DAI0_CH3_BIT_LSB (SVFPGA_PAGE_2A + 0x0A) +#define SVFPGA_ASP_RX_DAI0_CH4_AP_RES (SVFPGA_PAGE_2A + 0x0B) +#define SVFPGA_ASP_RX_DAI0_CH4_BIT_MSB (SVFPGA_PAGE_2A + 0x0C) +#define SVFPGA_ASP_RX_DAI0_CH4_BIT_LSB (SVFPGA_PAGE_2A + 0x0D) +#define SVFPGA_ASP_RX_DAI1_CH1_AP_RES (SVFPGA_PAGE_2A + 0x0E) +#define SVFPGA_ASP_RX_DAI1_CH1_BIT_MSB (SVFPGA_PAGE_2A + 0x0F) +#define SVFPGA_ASP_RX_DAI1_CH1_BIT_LSB (SVFPGA_PAGE_2A + 0x10) +#define SVFPGA_ASP_RX_DAI1_CH2_AP_RES (SVFPGA_PAGE_2A + 0x11) +#define SVFPGA_ASP_RX_DAI1_CH2_BIT_MSB (SVFPGA_PAGE_2A + 0x12) +#define SVFPGA_ASP_RX_DAI1_CH2_BIT_LSB (SVFPGA_PAGE_2A + 0x13) + +#define SVFPGA_ASP_RX_CH_AP_SHIFT 6 +#define SVFPGA_ASP_RX_CH_AP_MASK (1 << SVFPGA_ASP_RX_CH_AP_SHIFT) +#define SVFPGA_ASP_RX_CH_AP_LOW 0 +#define SVFPGA_ASP_RX_CH_AP_HI 1 +#define SVFPGA_ASP_RX_CH_RES_SHIFT 0 +#define SVFPGA_ASP_RX_CH_RES_MASK (3 << SVFPGA_ASP_RX_CH_RES_SHIFT) +#define SVFPGA_ASP_RX_CH_RES_32 3 +#define SVFPGA_ASP_RX_CH_BIT_ST_SHIFT 0 +#define SVFPGA_ASP_RX_CH_BIT_ST_MASK (0xff << SVFPGA_ASP_RX_CH_BIT_ST_SHIFT) + +/* Page 0x30 ID Registers */ +#define SVFPGA_SUB_REVID (SVFPGA_PAGE_30 + 0x14) +#define SVFPGA_MAX_REGISTER (SVFPGA_PAGE_30 + 0x14) + +#define SVFPGA_SUPPORTED_RATE 48000 + +/* Defines for fracturing values spread across multiple registers */ +#define SVFPGA_FRAC0_VAL(val) ((val) & 0x0000ff) +#define SVFPGA_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8) +#define SVFPGA_FRAC2_VAL(val) (((val) & 0xff0000) >> 16) + +#define SVFPGA_NUM_SUPPLIES 5 +static const char *const svfpga_supply_names[SVFPGA_NUM_SUPPLIES] = { + "VA", + "VP", + "VCP", + "VD_FILT", + "VL", +}; + +struct svfpga_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct regulator_bulk_data supplies[SVFPGA_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + u32 sclk; + u8 hpout_load; + u8 hpout_clamp; + struct sdw_slv *sdw; +}; + +int svfpga_probe(struct device *dev, struct regmap *regmap, + struct sdw_slv *slave); +int svfpga_remove(struct device *dev); +#endif /* __SVFPGA_H__ */ From 784a7e370a1a60aa5a9cd7fe44a24cdf47e67261 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:58:44 +0530 Subject: [PATCH 0027/1276] ASoC:Intel: Add machine driver for SoundWire SV PDM Codec. This patch is for testing. Not to be upstreamed. Change-Id: Ia92c5543d94fb3ac045f8c13414b9af3ffa8d7d3 Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/boards/Kconfig | 11 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_svfpga.c | 273 ++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_svfpga.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 35787e69f31b..ca140a77d992 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -306,6 +306,17 @@ config SND_SOC_INTEL_CNL_CS42L42_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_SVFPGA_MACH + tristate "Cannonlake with SVFPGA PDM SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_SVFPGA + select SND_SOC_DMIC + help + This adds support for ASoC SVFPGA PDM codec SDW machine driver. This + will create an alsa sound card for SVFPGA. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 4298c5ad8ca0..4f2b9b38edd0 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o +snd-soc-cnl_svfpga-objs := cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_svfpga.c b/sound/soc/intel/boards/cnl_svfpga.c new file mode 100644 index 000000000000..312c0437ac15 --- /dev/null +++ b/sound/soc/intel/boards/cnl_svfpga.c @@ -0,0 +1,273 @@ +/* + * cnl_svfpga.c - ASOC Machine driver for Intel cnl_svfpga platform + * with SVFPGA PDM soundwire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_svfpga_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_svfpga_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route cnl_svfpga_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + + + { "sdw_codec1_in", NULL, "SDW Rx1" }, + { "SDW Rx1", NULL, "Capture" }, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; + +static const struct snd_kcontrol_new cnl_svfpga_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), +}; + +static int cnl_svfpga_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_svfpga_controls, + ARRAY_SIZE(cnl_svfpga_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_svfpga_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_svfpga_ops = { + .startup = cnl_svfpga_startup, +}; + +static int cnl_svfpga_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 1; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S16_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +struct snd_soc_dai_link cnl_svfpga_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_svfpga_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_svfpga_ops, + }, + { + .name = "SDW0-Codec", + .id = 3, + .cpu_dai_name = "SDW PDM Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-14:13:20:05:86:80", + .codec_dai_name = "svfpga", + .be_hw_params_fixup = cnl_svfpga_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 4, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, + +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_svfpga = { + .name = "cnl_svfpga-audio", + .dai_link = cnl_svfpga_msic_dailink, + .num_links = ARRAY_SIZE(cnl_svfpga_msic_dailink), + .dapm_widgets = cnl_svfpga_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_svfpga_widgets), + .dapm_routes = cnl_svfpga_map, + .num_dapm_routes = ARRAY_SIZE(cnl_svfpga_map), +}; + + +static int snd_cnl_svfpga_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_svfpga_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_svfpga.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_svfpga, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_svfpga); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_svfpga); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_svfpga_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_svfpga_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_cnl_svfpga_mc_driver = { + .driver = { + .name = "cnl_svfpga", + }, + .probe = snd_cnl_svfpga_mc_probe, + .remove = snd_cnl_svfpga_mc_remove, +}; + +static int snd_cnl_svfpga_driver_init(void) +{ + pr_info("Canonlake Machine Driver cnl_svfpga: svfpga registered\n"); + return platform_driver_register(&snd_cnl_svfpga_mc_driver); +} +module_init(snd_cnl_svfpga_driver_init); + +static void snd_cnl_svfpga_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_svfpga_mc_driver); +} +module_exit(snd_cnl_svfpga_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_svfpga"); From 75931a4e8411e679c1274ba87c27e5b932a6bf88 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:16:25 +0530 Subject: [PATCH 0028/1276] [REVERTME] SoundWire: Hardcoding in bus driver for SVFPGA PDM codec. SVFPGA PDM codec implements the codec in orthogonal way to MIPI to verify the PDM on CNL Master controller. Added hardcodings to support SVFPGA. This is just for testing. This patch wont be upstreamed. Change-Id: I42b0fd5a16e59577a89bbab7bc024aed1b04c222 Signed-off-by: Hardik T Shah Reviewed-on: Signed-off-by: Sanyog Kale --- drivers/sdw/sdw_bwcalc.c | 83 ++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index 9c1ebc3297d2..b76ace3b0de7 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -27,8 +27,19 @@ #define MAXCLOCKFREQ 6 -#define MAXCLOCKFREQ 6 +#ifdef CONFIG_SND_SOC_SVFPGA +/* For PDM Capture, frameshape used is 50x10 */ +int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; + +int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; +int clock_freq[MAXCLOCKFREQ] = {19200000, 19200000, + 19200000, 19200000, + 19200000, 19200000}; + +#else /* TBD: Currently we are using 100x2 as frame shape. to be removed later */ int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, 96, 125, 144, 147, 120, 128, 150, @@ -44,7 +55,7 @@ int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, 9600000, 9600000, 9600000, 9600000}; - +#endif struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, @@ -104,20 +115,27 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->bandwidth = 0; sdw_bs->system_interval = 0; sdw_bs->frame_freq = 0; - /* TBD: Base Clock frequency should be read from - * master capabilities - * Currenly hardcoding to 9.6MHz - */ - sdw_bs->clk_freq = 9.6*1000*1000; sdw_bs->clk_state = SDW_CLK_STATE_ON; /* TBD: to be removed later */ /* Assumption is these should be already filled */ sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; sdw_mstr_cap->monitor_handover_supported = false; sdw_mstr_cap->highphy_capable = false; +#ifdef CONFIG_SND_SOC_SVFPGA + /* TBD: For PDM capture to be removed later */ + sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000 * 2; +#else + /* TBD: Base Clock frequency should be read from + * master capabilities + * Currenly hardcoding to 9.6MHz + */ + sdw_bs->clk_freq = 9.6 * 1000 * 1000; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + +#endif return 0; } EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); @@ -201,8 +219,24 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, u8 wbuf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; u8 wbuf1[2] = {0, 0}; u8 rbuf[1] = {0}; - u8 rbuf1[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - u8 rbuf2[2] = {0, 0}; + + +#ifdef CONFIG_SND_SOC_SVFPGA + /* + * The below hardcoding is required + * for running PDM capture with SV conora card + * because the transport params of card is not + * same as master parameters. Also not all + * standard registers are valid. + */ + t_slv_params->blockgroupcontrol_valid = false; + t_slv_params->sample_interval = 50; + t_slv_params->offset1 = 0; + t_slv_params->offset2 = 0; + t_slv_params->hstart = 1; + t_slv_params->hstop = 6; + p_slv_params->word_length = 30; +#endif /* Program slave alternate bank with all transport parameters */ /* DPN_BlockCtrl2 */ @@ -259,7 +293,11 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg.ssp_tag = 0x0; wr_msg.flag = SDW_MSG_FLAG_WRITE; +#ifdef CONFIG_SND_SOC_SVFPGA + wr_msg.len = (5 + (1 * (t_slv_params->blockgroupcontrol_valid))); +#else wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); +#endif wr_msg.slave_addr = slv_rt->slave->slv_number; wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; wr_msg.addr_page1 = 0x0; @@ -981,10 +1019,16 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, * One port per bus runtime structure */ /* Calculate sample interval */ +#ifdef CONFIG_SND_SOC_SVFPGA + t_params->sample_interval = + ((sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate)); +#else t_params->sample_interval = ((sdw_mstr_bs->clk_freq/ sdw_mstr_bs_rt->stream_params.rate) * 2); +#endif /* Only BlockPerPort supported */ t_params->blockpackingmode = 0; t_params->lanecontrol = 0; @@ -1106,8 +1150,12 @@ int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) */ t_params->hstop = hstop; +#ifdef CONFIG_SND_SOC_SVFPGA + /* For PDM capture, 0th col is also used */ + t_params->hstart = 0; +#else t_params->hstart = hstop - hwidth + 1; - +#endif /* * TBD: perform this when you have 2 ports @@ -1234,12 +1282,17 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) hstart1 = hstart2 = t_params->hstart; hstop1 = hstop2 = t_params->hstop; /* TBD: Verify this condition */ +#ifdef CONFIG_SND_SOC_SVFPGA + block_offset = 1; +#else block_offset = 0; +#endif } else { hstart1 = t_params->hstart; hstop1 = t_params->hstop; +#ifndef CONFIG_SND_SOC_SVFPGA /* hstart/stop not same */ if ((hstart1 != hstart2) && (hstop1 != hstop2)) { @@ -1249,8 +1302,7 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) /* TBD: Harcoding to 0, to be removed*/ block_offset = 0; } - -#if 0 +#else if ((hstart1 != hstart2) && (hstop1 != hstop2)) { block_offset = 1; @@ -1481,14 +1533,15 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, banktouse = !banktouse; /* - * TBD: Currently harcoded SSP interval to 24, + * TBD: Currently harcoded SSP interval to 50, * computed value to be taken from system_interval in * bus data structure. * Add error check. */ if (ops->mstr_ops->set_ssp_interval) ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, - 24, banktouse); /* hardcoding to 24 */ + 50, banktouse); + /* * Configure Clock * TBD: Add error check From bdeb25396b55f10cf408a56dc063ea6775ba2826 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Mon, 25 Apr 2016 13:39:37 +0530 Subject: [PATCH 0029/1276] SDW:Intel: Fix hardcoding for SVFPGA codec. SVFPGA codec requires special handling as its not modelled as MIPI defined SoundWire Slave. This is used for testing PDM mode on master. So hardcode clock setting for only SVFPGA codec. This patch doesnt need to be upstream. Change-Id: I723b1258d2186783a16ef7a60934a6ce7d6ffacc Signed-off-by: Hardik T Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 3f3317a6707a..9686aa2f9caf 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1097,7 +1097,11 @@ static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, /* TODO: Retrieve divider value or get value directly from calling * function */ +#ifdef CONFIG_SND_SOC_SVFPGA + int divider = ((9600000 * 2/cur_clk_freq) - 1); +#else int divider = ((9600000/cur_clk_freq) - 1); +#endif if (bank) { mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; From 371dc85ecae34e4ec106302d2c650b48f99ceca9 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 12:14:37 +0530 Subject: [PATCH 0030/1276] REVERTME:SDW: Increment the dev_id for every slave register to bus. Ideally every slave getting registered to bus should have separate dev_id if they are of same type but instances are different.But for maxim all the slaves have same_id, so increment the dev_id by one to register all slaves with different names. Change-Id: Iec8d21ae73bb1631803ba6faceef140d9cd41417 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 6a1ff5e59401..71f1532657da 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -524,6 +524,8 @@ static void sdw_free_slv_number(struct sdw_master *mstr, sdw_unlock_mstr(mstr); } + +int count; static int sdw_register_slave(struct sdw_master *mstr) { int ret = 0, i, ports; @@ -635,6 +637,7 @@ static int sdw_register_slave(struct sdw_master *mstr) sdw_unlock_mstr(mstr); } + count++; return 0; program_slv_failed: device_unregister(&sdw_slv->dev); From 77f6b97b56921f08be31465abfb02ce9956deed2 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 14:22:16 +0530 Subject: [PATCH 0031/1276] SDW: Support async messages for bus driver. All of the messages to the master controller for read/write register are synchronous for calling function. Master controller completes the operation and return back to calling function with result. But when audio stream is split between two masters (aggregation) bus driver needs to send the message asynchronously to master controller part of aggregation. Bus driver waits for result outide master controller context, instead of master controller waiting. Change-Id: Icf5d44c372bca742da21408f074dbf90080a3ea9 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 51 +++++++++++++++++++++++++++++++++++------ drivers/sdw/sdw_priv.h | 7 ++++++ include/linux/sdw_bus.h | 26 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 71f1532657da..5f4aea04a85b 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -419,13 +419,12 @@ void sdw_lock_mstr(struct sdw_master *mstr) { rt_mutex_lock(&mstr->bus_lock); } -EXPORT_SYMBOL_GPL(sdw_lock_mstr); /** * sdw_trylock_mstr - Try to get exclusive access to an SDW bus segment * @mstr: Target SDW bus segment */ -static int sdw_trylock_mstr(struct sdw_master *mstr) +int sdw_trylock_mstr(struct sdw_master *mstr) { return rt_mutex_trylock(&mstr->bus_lock); } @@ -439,7 +438,6 @@ void sdw_unlock_mstr(struct sdw_master *mstr) { rt_mutex_unlock(&mstr->bus_lock); } -EXPORT_SYMBOL_GPL(sdw_unlock_mstr); static int sdw_assign_slv_number(struct sdw_master *mstr, @@ -663,7 +661,8 @@ static int sdw_register_slave(struct sdw_master *mstr) * Adapter lock must be held when calling this function. No debug logging * takes place. mstr->algo->master_xfer existence isn't checked. */ -int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num, + struct sdw_async_xfer_data *async_data) { unsigned long orig_jiffies; int ret = 0, try, i; @@ -707,8 +706,24 @@ int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) program_scp_addr_page = slv_cap->paging_supported; } - ret = mstr->driver->mstr_ops->xfer_msg(mstr, + /* Call async or sync handler based on call */ + if (!async_data) + ret = mstr->driver->mstr_ops->xfer_msg(mstr, msg, program_scp_addr_page); + /* Async transfer is not mandatory to support + * It requires only if stream is split across the + * masters, where bus driver need to send the commands + * for bank switch individually and wait for them + * to complete out side of the master context + */ + else if (mstr->driver->mstr_ops->xfer_msg_async && + async_data) + ret = mstr->driver->mstr_ops->xfer_msg_async( + mstr, msg, + program_scp_addr_page, + async_data); + else + return -ENOTSUPP; if (ret != -EAGAIN) break; if (time_after(jiffies, @@ -740,13 +755,34 @@ static int sdw_slave_transfer_nopm(struct sdw_master *mstr, struct sdw_msg *msg, int ret; if (mstr->driver->mstr_ops->xfer_msg) { - ret = __sdw_transfer(mstr, msg, num); + ret = __sdw_transfer(mstr, msg, num, NULL); return ret; } dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); return -EOPNOTSUPP; } +int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg, + int num, + struct sdw_async_xfer_data *async_data) +{ + int ret; + /* Currently we support only message asynchronously, This is mainly + * used to do bank switch for multiple controllers + */ + if (num != 1) + return -EINVAL; + if (!(mstr->driver->mstr_ops->xfer_msg)) { + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; + } + pm_runtime_get_sync(&mstr->dev); + ret = __sdw_transfer(mstr, msg, num, async_data); + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} + /** * sdw_slave_transfer: Transfer message between slave and mstr on the bus. * @mstr: mstr master which will transfer the message @@ -789,7 +825,7 @@ int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) } else { sdw_lock_mstr(mstr); } - ret = __sdw_transfer(mstr, msg, num); + ret = __sdw_transfer(mstr, msg, num, NULL); sdw_unlock_mstr(mstr); out: pm_runtime_mark_last_busy(&mstr->dev); @@ -1125,6 +1161,7 @@ static int sdw_register_master(struct sdw_master *mstr) if (!sdw_bus) goto bus_alloc_failed; sdw_bus->mstr = mstr; + init_completion(&sdw_bus->async_data.xfer_complete); mutex_lock(&sdw_core.core_lock); list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h index 5ec3edc30d27..42e948440481 100644 --- a/drivers/sdw/sdw_priv.h +++ b/drivers/sdw/sdw_priv.h @@ -196,6 +196,7 @@ struct sdw_bus { struct kthread_work kwork; struct list_head status_list; spinlock_t spinlock; + struct sdw_async_xfer_data async_data; }; /** Holds supported Row-Column combination related information */ @@ -239,5 +240,11 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); int sdw_chn_enable(void); +void sdw_unlock_mstr(struct sdw_master *mstr); +int sdw_trylock_mstr(struct sdw_master *mstr); +void sdw_lock_mstr(struct sdw_master *mstr); +int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg, + int num, + struct sdw_async_xfer_data *async_data); #endif /* _LINUX_SDW_PRIV_H */ diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h index 01c846b0c695..d16579b35f8a 100644 --- a/include/linux/sdw_bus.h +++ b/include/linux/sdw_bus.h @@ -337,6 +337,29 @@ struct sdw_slv_bra_capabilities { unsigned int mode_block_alignment; }; +/** + * struct sdw_async_xfer_data: Data to be provided by bus driver to + * to master controller, in case bus driver + * driver doesnt want to call synchronous + * xfer message API. This is used by bus + * driver during aggregation, where it calls + * the bank switch of multiple master + * if the stream is split between two master. + * In this case bus driver will wait outside + * master controller context for bank switch + * to happen. + * @result: Result of the asynchronous transfer. + * @xfer_complete Bus driver will wait on this. Master controller + * needs to ack on this for transfer complete. + * @msg Message to be transferred. + */ + +struct sdw_async_xfer_data { + int result; + struct completion xfer_complete; + struct sdw_msg *msg; +}; + /** * struct sdw_slv_dp0_capabilities: Capabilities of the Data Port 0 of Slave. * @@ -831,6 +854,9 @@ struct sdw_master_port_ops { struct sdw_master_ops { enum sdw_command_response (*xfer_msg)(struct sdw_master *mstr, struct sdw_msg *msg, bool program_scp_addr_page); + enum sdw_command_response (*xfer_msg_async)(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page, + struct sdw_async_xfer_data *data); int (*xfer_bulk)(struct sdw_master *mstr, struct sdw_bra_block *block); int (*monitor_handover)(struct sdw_master *mstr, From 6c6b527cf5ba9a7663bcbb25a6cdf2661a8849f6 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 14:59:48 +0530 Subject: [PATCH 0032/1276] SDW: Change log level to error from debug. For any errors, log level should be error. By mistake it was debug. Change-Id: I81d55a2efaae172329515f6e000bbed3063400ef Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 5f4aea04a85b..78c8cfd32d4c 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -1844,7 +1844,7 @@ int sdw_config_stream(struct sdw_master *mstr, } } if (!sdw_rt) { - dev_dbg(&mstr->dev, "Valid stream tag not found\n"); + dev_err(&mstr->dev, "Valid stream tag not found\n"); ret = -EINVAL; goto out; } From e12bcc00172f258a3a779f95fc21cb9a0a869a6d Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:26:15 +0530 Subject: [PATCH 0033/1276] SDW:Intel: Enabled the Multimode for Intel SDW master controller. Initialize the SDW controller for multimode. Set the Sync period to default Change the default SSP for multimode. Change-Id: Ie45c00aeeaea5062abe76abb91b7504e4e8b01fb Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 25 ++++++++++++++++++++++++- drivers/sdw/sdw_cnl_priv.h | 12 +++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 9686aa2f9caf..21a61b4f010a 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -440,7 +440,11 @@ static int sdw_init(struct cnl_sdw *sdw) { struct sdw_master *mstr = sdw->mstr; struct cnl_sdw_data *data = &sdw->data; - int mcp_config, mcp_control; + int mcp_config, mcp_control, sync_reg; + + volatile int sync_update = 0; + /* Try 10 times before timing out */ + int timeout = 10; int ret = 0; /* Power up the link controller */ @@ -454,6 +458,25 @@ static int sdw_init(struct cnl_sdw *sdw) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); + /* Set the Sync period to default */ + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); + sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + if ((sync_update & CNL_SYNC_SYNCCPU_MASK) == 0) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + if ((sync_update & CNL_SYNC_SYNCCPU_MASK) != 0) { + dev_err(&mstr->dev, "Fail to set sync period\n"); + return -EINVAL; + } + /* Set command acceptance mode. This is required because when * Master broadcasts the clock_stop command to slaves, slaves * might be already suspended, so this return NO ACK, in that diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 914f7cae2b01..c99433882dff 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -26,11 +26,13 @@ #define SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT 32 #define SDW_CNL_SLAVE_STATUS_BITS 4 #define SDW_CNL_CMD_WORD_LEN 4 -#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x32 +#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 +#define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F + #define SDW_CNL_PORT_REG_OFFSET 0x80 #define CNL_SDW_SCP_ADDR_REGS 0x2 #define SDW_CNL_PCM_PDI_NUM_OFFSET 0x2 -#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 +#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 #define SDW_CNL_CTMCTL_REG_OFFSET 0x60 #define SDW_CNL_IOCTL_REG_OFFSET 0x60 @@ -257,8 +259,12 @@ #define SDW_CNL_SYNC 0xC #define CNL_SYNC_CMDSYNC_MASK 0x1 #define CNL_SYNC_CMDSYNC_SHIFT 16 -#define CNL_SYNC_SYNCGO_MASK 0x1 +#define CNL_SYNC_SYNCGO_MASK 0x1000000 #define CNL_SYNC_SYNCGO_SHIFT 0x18 +#define CNL_SYNC_SYNCPRD_MASK 0x7FFF +#define CNL_SYNC_SYNCPRD_SHIFT 0x0 +#define CNL_SYNC_SYNCCPU_MASK 0x8000 +#define CNL_SYNC_SYNCCPU_SHIFT 0xF #define SDW_CNL_CTLSCAP 0x10 #define SDW_CNL_CTLS0CM 0x12 From 522d38ecea28352e5fdcac0d0ac86a694c1139cc Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:30:53 +0530 Subject: [PATCH 0034/1276] SDW: Intel: Add the handler for async message transfer. Async message transfer is used by the bus driver in case stream is split between two masters. Else synchronous message transfer is always used. Change-Id: I7fda7fd954f41941ab054cdc0c41de1e9ceca24a Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 148 +++++++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 39 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 21a61b4f010a..a5fc6db79f83 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -79,6 +79,12 @@ static inline void cnl_sdw_port_reg_writel(u32 __iomem *base, int offset, return cnl_sdw_reg_writel(base, offset + port_num * 128, value); } +struct cnl_sdw_async_msg { + struct completion *async_xfer_complete; + struct sdw_msg *msg; + int length; +}; + struct cnl_sdw { struct cnl_sdw_data data; struct sdw_master *mstr; @@ -100,6 +106,7 @@ struct cnl_sdw { struct cnl_sdw_pdi_stream *out_pdm_streams; struct mutex stream_lock; spinlock_t ctrl_lock; + struct cnl_sdw_async_msg async_msg; u32 response_buf[0x80]; bool sdw_link_status; @@ -808,6 +815,46 @@ static void cnl_sdw_read_response(struct cnl_sdw *sdw) } } +static enum sdw_command_response sdw_fill_message_response( + struct sdw_master *mstr, + struct sdw_msg *msg, + int count, int offset) +{ + int i, j; + int no_ack = 0, nack = 0; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + for (i = 0; i < count; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & + sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + break; + } + if (nack) { + dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } else if (no_ack) { + dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } + if (msg->flag == SDW_MSG_FLAG_WRITE) + return 0; + /* Response and Command has same base address */ + for (j = 0; j < count; j++) + msg->buf[j + offset] = + (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); + return 0; +} + + irqreturn_t cnl_sdw_irq_handler(int irq, void *context) { struct cnl_sdw *sdw = context; @@ -840,7 +887,14 @@ irqreturn_t cnl_sdw_irq_handler(int irq, void *context) if (int_status & (MCP_INTSTAT_RXWL_MASK << MCP_INTSTAT_RXWL_SHIFT)) { cnl_sdw_read_response(sdw); - complete(&sdw->tx_complete); + if (sdw->async_msg.async_xfer_complete) { + sdw_fill_message_response(mstr, sdw->async_msg.msg, + sdw->async_msg.length, 0); + complete(sdw->async_msg.async_xfer_complete); + sdw->async_msg.async_xfer_complete = NULL; + sdw->async_msg.msg = NULL; + } else + complete(&sdw->tx_complete); } if (int_status & (MCP_INTSTAT_CONTROLBUSCLASH_MASK << MCP_INTSTAT_CONTROLBUSCLASH_SHIFT)) { @@ -945,16 +999,14 @@ static enum sdw_command_response cnl_program_scp_addr(struct sdw_master *mstr, } static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, - struct sdw_msg *msg, int cmd, int offset, int count) + struct sdw_msg *msg, int cmd, int offset, int count, bool async) { struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; - int i, j; + int j; u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; - u32 response_base = SDW_CNL_MCP_RESPONSE_BASE; - u32 cmd_data = 0, response_data; + u32 cmd_data = 0; unsigned long time_left; - int no_ack = 0, nack = 0; u16 addr = msg->addr; /* Program the watermark level upto number of count */ @@ -983,6 +1035,10 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, cmd_base, cmd_data); cmd_base += SDW_CNL_CMD_WORD_LEN; } + + /* If Async dont wait for completion */ + if (async) + return 0; /* Wait for 3 second for timeout */ time_left = wait_for_completion_timeout(&sdw->tx_complete, 3 * HZ); if (!time_left) { @@ -990,39 +1046,52 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, msg->len = 0; return -ETIMEDOUT; } - for (i = 0; i < count; i++) { - if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { - no_ack = 1; - dev_err(&mstr->dev, "Ack not recevied\n"); - if ((MCP_RESPONSE_NACK_MASK & - sdw->response_buf[i])) { - nack = 1; - dev_err(&mstr->dev, "NACK recevied\n"); - } - } - break; - } - if (nack) { - dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); - msg->len = 0; - return -EREMOTEIO; - } else if (no_ack) { - dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); - msg->len = 0; - return -EREMOTEIO; + return sdw_fill_message_response(mstr, msg, count, offset); +} + +static enum sdw_command_response cnl_sdw_xfer_msg_async(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page, + struct sdw_async_xfer_data *data) +{ + int ret = 0, cmd; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + /* Only 1 message can be handled in Async fashion. This is used + * only for Bank switching where during aggregation it is required + * to synchronously switch the bank on more than 1 controller + */ + if (msg->len > 1) { + ret = -EINVAL; + goto error; } - if (msg->flag == SDW_MSG_FLAG_WRITE) - return 0; - /* Response and Command has same base address */ - response_base = SDW_CNL_MCP_COMMAND_BASE; - for (j = 0; j < count; j++) { - response_data = cnl_sdw_reg_readl(data->sdw_regs, - cmd_base); - msg->buf[j + offset] = - (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); - cmd_base += 4; + /* If scp addr programming fails goto error */ + if (program_scp_addr_page) + ret = cnl_program_scp_addr(mstr, msg); + if (ret) + goto error; + + switch (msg->flag) { + case SDW_MSG_FLAG_READ: + cmd = 0x2; + break; + case SDW_MSG_FLAG_WRITE: + cmd = 0x3; + break; + default: + dev_err(&mstr->dev, "Command not supported\n"); + return -EINVAL; } - return 0; + sdw->async_msg.async_xfer_complete = &data->xfer_complete; + sdw->async_msg.msg = msg; + sdw->async_msg.length = msg->len; + /* Dont wait for reply, calling function will wait for reply. */ + ret = sdw_xfer_msg(mstr, msg, cmd, 0, msg->len, true); + return ret; +error: + msg->len = 0; + complete(&data->xfer_complete); + return -EINVAL; + } static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, @@ -1052,14 +1121,14 @@ static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, for (i = 0; i < msg->len / SDW_CNL_MCP_COMMAND_LENGTH; i++) { ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, - SDW_CNL_MCP_COMMAND_LENGTH); + SDW_CNL_MCP_COMMAND_LENGTH, false); if (ret < 0) break; } if (!(msg->len % SDW_CNL_MCP_COMMAND_LENGTH)) return ret; ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, - msg->len % SDW_CNL_MCP_COMMAND_LENGTH); + msg->len % SDW_CNL_MCP_COMMAND_LENGTH, false); if (ret < 0) return -EINVAL; return ret; @@ -1561,6 +1630,7 @@ static const struct dev_pm_ops cnl_sdw_pm_ops = { }; static struct sdw_master_ops cnl_sdw_master_ops = { + .xfer_msg_async = cnl_sdw_xfer_msg_async, .xfer_msg = cnl_sdw_xfer_msg, .xfer_bulk = cnl_sdw_xfer_bulk, .monitor_handover = cnl_sdw_mon_handover, From f3b9719a43acc7574fc078a216c367527917c20d Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:39:23 +0530 Subject: [PATCH 0035/1276] SDW:CNL: Fix the syncgo functionality. Sync go is used to synchronously send the commands on more than 1 master to support aggregation. Fix the implementation of sync go. Change-Id: I2d62b9d11a0319a3eda3d6e5cf9a3800d31d2463 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 23 ++++++++++++++++++++++- drivers/sdw/sdw_cnl_priv.h | 3 ++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index a5fc6db79f83..274966572499 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1365,11 +1365,32 @@ static int cnl_sdw_port_activate_ch_post(struct sdw_master *mstr, int sync_reg; struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; + volatile int sync_update = 0; + int timeout = 10; + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); - sync_reg |= CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT; + /* If waiting for synchronization set the go bit, else return */ + if (!(sync_reg & SDW_CMDSYNC_SET_MASK)) + return 0; + sync_reg |= (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + if ((sync_update & + (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT)) == 0) + break; + msleep(20); + timeout--; + + } while (timeout); + + if ((sync_update & + (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT)) != 0) { + dev_err(&mstr->dev, "Failed to set sync go\n"); + return -EIO; + } return 0; } diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index c99433882dff..8e9d68c2bc2c 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -255,11 +255,12 @@ #define CNL_LCTL_CPA_MASK 0x1 #define CNL_LCTL_SPA_MASK 0x1 +#define SDW_CMDSYNC_SET_MASK 0xF0000 #define SDW_CNL_IPPTR 0x8 #define SDW_CNL_SYNC 0xC #define CNL_SYNC_CMDSYNC_MASK 0x1 #define CNL_SYNC_CMDSYNC_SHIFT 16 -#define CNL_SYNC_SYNCGO_MASK 0x1000000 +#define CNL_SYNC_SYNCGO_MASK 0x1 #define CNL_SYNC_SYNCGO_SHIFT 0x18 #define CNL_SYNC_SYNCPRD_MASK 0x7FFF #define CNL_SYNC_SYNCPRD_SHIFT 0x0 From 8dc5fff253c74b6d00b4fff7095f4e4015cb49ae Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:18:36 +0530 Subject: [PATCH 0036/1276] SoundWire: Remove dead code from SoundWire BW calculation Remove some of the dead code from SoundWire BW calculation file. Change-Id: I8306e3d253c290741bbf8e1408551176bd986a80 Signed-off-by: Hardik Shah Reviewed-on: Signed-off-by: Sanyog Kale --- drivers/sdw/sdw_bwcalc.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index b76ace3b0de7..ea080f101d0f 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -117,12 +117,6 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; - /* TBD: to be removed later */ - /* Assumption is these should be already filled */ - sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; - sdw_mstr_cap->monitor_handover_supported = false; - sdw_mstr_cap->highphy_capable = false; - #ifdef CONFIG_SND_SOC_SVFPGA /* TBD: For PDM capture to be removed later */ sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; @@ -718,15 +712,6 @@ int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, if ((chn_en->is_activate) || (chn_en->is_bank_sw)) banktouse = !banktouse; - - /* 1. Master port enable_ch_pre */ - if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { - ret = ops->mstr_port_ops->dpn_port_activate_ch_pre - (mstr_bs->mstr, &activate_ch, banktouse); - if (ret < 0) - return ret; - } - /* 2. Master port enable */ if (ops->mstr_port_ops->dpn_port_activate_ch) { ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, @@ -735,14 +720,6 @@ int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, return ret; } - /* 3. Master port enable_ch_post */ - if (ops->mstr_port_ops->dpn_port_activate_ch_post) { - ret = ops->mstr_port_ops->dpn_port_activate_ch_post - (mstr_bs->mstr, &activate_ch, banktouse); - if (ret < 0) - return ret; - } - if (chn_en->is_activate) mstr_rt_strm->rt_state = SDW_STATE_ENABLE_RT; else if (!chn_en->is_bank_sw) From d090542c676183d8ce1530606355994ae818cb5d Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Sun, 1 May 2016 18:13:41 +0530 Subject: [PATCH 0037/1276] REVERTME:SDW: Skip the Slave programming for the stream Aggretation is tested with Maxim Slave device. This is just used to enable the aggregation test. Actually loop back test is used to demonstrate aggregation. Disable slave programming for testing aggretation with master loopback. Change-Id: Ie1fd183b5ebb155234d52ea17c78ca522df41e4d Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_bwcalc.c | 42 ++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index ea080f101d0f..5b362dc0a2f4 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -260,14 +260,17 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, rd_msg.buf = rbuf; rd_msg.addr_page1 = 0x0; rd_msg.addr_page2 = 0x0; - +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } - +#endif wbuf1[0] = (p_slv_params->port_flow_mode | (p_slv_params->port_data_mode << @@ -304,7 +307,10 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg1.buf = &wbuf1[0]; wr_msg1.addr_page1 = 0x0; wr_msg1.addr_page2 = 0x0; - +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -319,8 +325,9 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } - out: +#endif + return ret; } @@ -601,6 +608,10 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, */ /* 2. slave port enable */ +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -627,7 +638,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } - +#endif /* * 3. slave port enable post pre * --> callback @@ -642,6 +653,10 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, * --> callback * --> no callback available */ +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA /* 2. slave port disable */ ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); @@ -670,7 +685,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } - +#endif /* * 3. slave port enable post unpre * --> callback @@ -680,8 +695,9 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; } - +#ifndef CONFIG_SND_SOC_MXFPGA out: +#endif return ret; } @@ -1284,12 +1300,20 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) (hstop1 != hstop2)) { block_offset = 1; } else { +/* We are doing loopback for the Aggregation so block offset should + * always remain same. This is not a requirement. This we are doing + * to test aggregation without codec. + */ +#ifdef CONFIG_SND_SOC_MXFPGA + block_offset = 1; +#else block_offset += (sdw_mstr_bs_rt->stream_params. bps * sdw_mstr_bs_rt->stream_params. channel_count); +#endif } #endif @@ -1641,7 +1665,9 @@ int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; - +#ifdef CONFIG_SND_SOC_MXFPGA + sdw_slv_dpn_cap->prepare_ch = 0; +#endif if (prep) { /* PREPARE */ /* From b65bcee78d04fe6499c241633a9711de4dea036a Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Sun, 1 May 2016 18:29:30 +0530 Subject: [PATCH 0038/1276] SoundWire: Add support for the aggregation Add support for the master aggregation in SoundWire bus driver. For the aggregated masters bus driver calls async message transfer APIs for the bank swith to make sure that bank switch for multiple masters happen at same time. Change-Id: I4272b2ff35c6aee9ee3d1919113b2d11be766abc Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_bwcalc.c | 1031 ++++++++++++++++++++++++++++---------- 1 file changed, 755 insertions(+), 276 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index 5b362dc0a2f4..cafaccbeea3a 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -17,7 +17,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - +#include #include #include #include "sdw_priv.h" @@ -116,7 +116,7 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->system_interval = 0; sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; - + sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; #ifdef CONFIG_SND_SOC_SVFPGA /* TBD: For PDM capture to be removed later */ sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; @@ -1457,6 +1457,103 @@ int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) return ret; } +/* + * sdw_configure_frmshp_bnkswtch - returns Success + * -EINVAL - In case of error. + * + * + * This function broadcast frameshape on framectrl + * register and performs bank switch. + */ +int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) +{ + int ret = 0; + int banktouse, numcol, numrow; + u8 *wbuf; + struct sdw_msg *wr_msg; + + wr_msg = kzalloc(sizeof(struct sdw_msg), GFP_KERNEL); + mstr_bs->async_data.msg = wr_msg; + if (!wr_msg) + return -ENOMEM; + wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); + if (!wbuf) + return -ENOMEM; + numcol = sdw_get_col_to_num(col); + numrow = sdw_get_row_to_num(row); + + wbuf[0] = numcol | (numrow << 3); + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + if (banktouse) { + wr_msg->addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } else { + + wr_msg->addr = SDW_SCP_FRAMECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } + + wr_msg->ssp_tag = 0x1; + wr_msg->flag = SDW_MSG_FLAG_WRITE; + wr_msg->len = 1; + wr_msg->slave_addr = 0xF; /* Broadcast address*/ + wr_msg->buf = wbuf; + wr_msg->addr_page1 = 0x0; + wr_msg->addr_page2 = 0x0; + + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr_bs->mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else { + sdw_lock_mstr(mstr_bs->mstr); + } + + ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, + 1, &mstr_bs->async_data); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + msleep(100); /* TBD: Remove this */ + + /* + * TBD: check whether we need to poll on + * mcp active bank bit to switch bank + */ + mstr_bs->active_bank = banktouse; + +out: + + return ret; +} + +int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) +{ + unsigned long time_left; + struct sdw_master *mstr = mstr_bs->mstr; + + time_left = wait_for_completion_timeout( + &mstr_bs->async_data.xfer_complete, + 3000); + if (!time_left) { + dev_err(&mstr->dev, "Controller Timed out\n"); + sdw_unlock_mstr(mstr); + return -ETIMEDOUT; + } + kfree(mstr_bs->async_data.msg->buf); + kfree(mstr_bs->async_data.msg); + sdw_unlock_mstr(mstr); + return 0; +} /* * sdw_cfg_bs_params - returns Success @@ -1937,6 +2034,21 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, return 0; } +struct sdw_bus *master_to_bus(struct sdw_master *mstr) +{ + struct sdw_bus *sdw_mstr_bs = NULL; + + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != mstr) + continue; + return sdw_mstr_bs; + } + /* This should never happen, added to suppress warning */ + WARN_ON(1); + + return NULL; +} /** * sdw_bus_calc_bw - returns Success @@ -1956,13 +2068,16 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; struct sdw_stream_params *stream_params = &sdw_rt->stream_params; struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; int stream_frame_size; int frame_interval = 0, sel_row = 0, sel_col = 0; int ret = 0; + bool last_node = false; + struct sdw_master_port_ops *ops; /* TBD: Add PCM/PDM flag in sdw_config_stream */ @@ -1973,22 +2088,21 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) */ /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; /* Get bus structure for master */ - list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { - - /* Match master structure pointer */ - if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) - continue; - - - sdw_mstr = sdw_mstr_bs->mstr; - break; - } + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; /* * All data structures required available, @@ -1999,180 +2113,384 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; mstr_params = &sdw_mstr_rt->stream_params; - if ((sdw_rt->stream_state == SDW_STATE_CONFIG_STREAM) || - (sdw_rt->stream_state == - SDW_STATE_UNPREPARE_STREAM)) { + if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && + (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) + goto enable_stream; - /* we do not support asynchronous mode Return Error */ - if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) - != 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Asynchronous mode not supported\n"); - return -EINVAL; - } + /* we do not support asynchronous mode Return Error */ + if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) != 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Async mode not supported\n"); + return -EINVAL; + } - /* Check for sampling frequency */ - if (stream_params->rate != mstr_params->rate) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Sampling frequency mismatch\n"); - return -EINVAL; - } + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Sample frequency mismatch\n"); + return -EINVAL; + } - /* - * Calculate stream bandwidth, frame size and - * total BW required for master controller - */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; - sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, "clock/frameshape config failed\n"); - return ret; - } + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); + return ret; + } + /* + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. + */ - /* - * TBD: find right place to run sorting on - * master rt_list. Below sorting is done based on - * bps from low to high, that means PDM streams - * will be placed before PCM. - */ + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ - /* - * TBD Should we also perform sorting based on rate - * for PCM stream check. if yes then how?? - * creating two different list. - */ + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + return ret; + } - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, - sdw_mstr_cap, frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, "compute system interval failed\n"); - return ret; + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + return ret; + } + + /* Change Stream State */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + + /* Configure bus parameters */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport param config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * pre_activate ports + */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute hstart/hstop failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "bank switch failed\n"); + return ret; + } } - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err( - &sdw_mstr_bs->mstr->dev, - "compute block offset failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Change Stream State */ - sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Configure bus parameters */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + if (mstr_rt_act->mstr == NULL) + break; + + mstr_bs_act = master_to_bus( + mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Disable all channels + * enabled on previous bank + */ + ret = sdw_dis_chan(mstr_bs_act, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel disabled faile\n"); + return ret; + } + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Disable all channels enabled on previous bank */ ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); - return ret; - } - - /* Prepare new port for master and slave */ - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel prepare failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } + } + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + return ret; + } - /* change stream state to prepare */ + /* change stream state to prepare */ + if (last_node) sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + } +enable_stream: + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + + if (sdw_mstr_rt->mstr == NULL) + break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((!enable) || + (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) + return 0; + + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } + + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + return ret; } - if ((enable) && (SDW_STATE_PREPARE_STREAM - == sdw_rt->stream_state)) { + /* change stream state to enable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * pre_activate ports + */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Enable new port for master and slave */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel enable failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "bank switch failed\n"); + return ret; + } } - /* change stream state to enable */ - sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + } + + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Disable all channels + * enabled on previous bank + */ + ret = sdw_dis_chan(mstr_bs_act, + sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, + "Channel disabled faile\n"); + return ret; + } + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + ret = sdw_configure_frmshp_bnkswtch( + sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Disable all channels enabled on previous bank */ ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled faile\n"); + dev_err(&sdw_mstr->dev, "Ch disabled failed\n"); return ret; } } @@ -2182,7 +2500,6 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) } EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); - /** * sdw_bus_calc_bw_dis - returns Success * -EINVAL - In case of error. @@ -2200,221 +2517,383 @@ int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) { struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; int stream_frame_size; int frame_interval = 0, sel_row = 0, sel_col = 0; int ret = 0; - + bool last_node = false; + struct sdw_master_port_ops *ops; /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (sdw_mstr_rt->mstr == NULL) break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + /* Get bus structure for master */ - list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; - /* Match master structure pointer */ - if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) - continue; + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; - sdw_mstr = sdw_mstr_bs->mstr; - break; - } + if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) + goto unprepare_stream; + /* Lets do disabling of port for stream to be freed */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; + if (sdw_mstr_bs_rt->mstr == NULL) + continue; - if (sdw_rt->stream_state == SDW_STATE_ENABLE_STREAM) { + /* + * Disable channel for slave and + * master on current bank + */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch dis failed\n"); + return ret; + } - /* Lets do disabling of port for stream to be freed */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* Change stream state to disable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; + } - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - /* - * Disable channel for slave and - * master on current bank + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* Run for all mstr_list and + * pre_activate ports */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, - sdw_rt, false); + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + } + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel dis failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - - /* Change stream state to disable */ - sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; } - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + if (mstr_rt_act->mstr == NULL) + break; - /* Configure frame shape/Switch Bank */ + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + + } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + } + if (!sdw_mstr->link_sync_mask) { + + /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } + } + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); + return ret; + } + } +unprepare_stream: + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (sdw_mstr_rt->mstr == NULL) + break; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; + + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((!unprepare) || + (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) + return 0; + + /* 1. Un-prepare master and slave port */ + list_for_each_entry(sdw_mstr_bs_rt, &sdw_mstr->mstr_rt_list, + mstr_node) { + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); + dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); return ret; } - } - if ((unprepare) && - (SDW_STATE_DISABLE_STREAM == - sdw_rt->stream_state)) { + /* change stream state to unprepare */ + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; + } - /* 1. Un-prepare master and slave port */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Chan unprep failed\n"); - return ret; - } - - /* change stream state to unprepare */ - sdw_rt->stream_state = - SDW_STATE_UNPREPARE_STREAM; - } + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr->dev, "BW calculation failed\n"); + return -EINVAL; + } + if (!sdw_mstr_bs->bandwidth) { /* - * Calculate new bandwidth, frame size - * and total BW required for master controller + * Last stream on master should + * return successfully */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNCOMPUTE_STREAM; + continue; + } - sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - /* Something went wrong in bandwidth calulation */ - if (sdw_mstr_bs->bandwidth < 0) { - dev_err(&sdw_mstr_bs->mstr->dev, - "BW calculation failed\n"); - return -EINVAL; - } + ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, + &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape failed\n"); + return ret; + } + + /* Compute new transport params for running streams */ + /* No sorting required here */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute SI failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop fail\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + return ret; + } + + /* Configure bus params */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } + if ((last_node) && (sdw_mstr->link_sync_mask)) { + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; - if (!sdw_mstr_bs->bandwidth) { /* - * Last stream on master should - * return successfully + * Run for all mstr_list and + * pre_activate ports */ - sdw_rt->stream_state = - SDW_STATE_UNCOMPUTE_STREAM; - return 0; + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "clock/frameshape failed\n"); - return ret; - } + if (mstr_rt_act->mstr == NULL) + break; - /* Compute new transport params for running streams */ - /* No sorting required here */ + /* Get bus structure for master */ + mstr_bs_act = master_to_bus( + mstr_rt_act->mstr); - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, - sdw_mstr_cap, frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute SI failed\n"); - return ret; + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, + "bank switch failed\n"); + return ret; + } } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute hstart/hstop fail\n"); - return ret; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute block offset failed\n"); - return ret; - } - /* Configure bus params */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Change stream state to uncompute */ + } + /* Change stream state to uncompute */ + if (last_node) sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); - return ret; - } + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; } - } return 0; From c1311f645eba1753dbe3bf23bb81b0f95e90a876 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:41:26 +0530 Subject: [PATCH 0039/1276] REVERTME:ASoC:CNL: Mark SDW master 1 and 2 as aggregated. This patch is to to test the aggregation. Mark the master1 and master2 as aggregated Change-Id: I0fa73fdf12bef071f0054b8844105eac1d698638 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index e4d531680a5a..0caf9bf070a3 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,8 +509,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ -#ifdef CONFIG_SDW_MAXIM_SLAVE - +#if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) dsp->num_sdw_controllers = 3; #else dsp->num_sdw_controllers = 4; @@ -574,12 +573,19 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; } + master[i].link_sync_mask = 0x0; switch (i) { case 0: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; +#ifdef CONFIG_SND_SOC_MXFPGA + master[i].link_sync_mask = 0x1; +#endif break; case 1: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; +#ifdef CONFIG_SND_SOC_MXFPGA + master[i].link_sync_mask = 0x2; +#endif break; case 2: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; From cf5e3e6b912d0caf980b9e5f90ebb1339f4f0860 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 00:14:23 +0530 Subject: [PATCH 0040/1276] ASoC:CNL: Update capabilities fields of SDW master Capabilities fields are missing while updating master controller update the fields based on hardware/topology capability Change-Id: I65b9b6ee77a61accc21fb34fa7db8b6b15e5e431 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/cnl-sst.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 0caf9bf070a3..f70d70d0822a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -538,7 +538,8 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, master[i].retries = CNL_SDW_MAX_CMD_RETRIES; m_cap->base_clk_freq = 9.6 * 1000 * 1000; strcpy(master[i].name, "cnl_sdw_mstr"); - m_cap->highphy_capable = 0; + m_cap->highphy_capable = false; + m_cap->monitor_handover_supported = false; m_cap->sdw_dp0_supported = 1; m_cap->num_data_ports = CNL_SDW_MAX_PORTS; dp0_cap->max_word_length = 32; From 86935a41efc4a1061221a51fb22de0c15f524185 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 15:09:22 +0530 Subject: [PATCH 0041/1276] ASoC:CNL: Add support for aggregated gateways. Gateway Link copier needs to be programmed differently for aggregated case. Add support for SoundWire aggregated gateway programming. Change-Id: I91925fbeff68b8ecbb1dd13591f6a185a1cdd8d8 Signed-off-by: Hardik Shah Reviewed-on: Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 5 ++++- sound/soc/intel/skylake/skl-topology.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f63b50e43257..a9eaa4aed643 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -566,7 +566,10 @@ static u32 skl_get_node_id(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_SDW_LINK_OUTPUT_CLASS : SKL_DMA_SDW_LINK_INPUT_CLASS; - node_id.node.vindex = mconfig->sdw_stream_num; + if (mconfig->sdw_agg_enable) + node_id.node.vindex = 0x50; + else + node_id.node.vindex = mconfig->sdw_stream_num; break; default: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 353c90cbd336..44e4109ba4dc 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -383,6 +383,16 @@ struct skl_module { struct skl_module_iface formats[SKL_MAX_MODULE_FORMATS]; }; +struct skl_sdw_agg_data { + int alh_stream_num; + int ch_mask; +}; + +struct skl_sdw_aggregation { + int num_masters; + struct skl_sdw_agg_data agg_data[4]; +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -418,6 +428,8 @@ struct skl_module_cfg { u32 dma_buffer_size; /* in milli seconds */ u8 pdi_type; u32 sdw_stream_num; + bool sdw_agg_enable; + struct skl_sdw_aggregation sdw_agg; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; From cd08da9fbe20d5a092492f4e36abac2342ff14b7 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 15:12:59 +0530 Subject: [PATCH 0042/1276] ASoC:CNL: Add DAIS for SoundWire masters. Add new DAIs for SoundWire master for aggregation. Add DAI IDs to find the master controller from DAI. Earlier DAI was found from the copier vbus, but with aggregation, copier is linked to multiple DAIs which are aggregated. So add be_ids range to idendify SDW master controller from DAIs. Change-Id: Ida7bd5893e073c3ec5d7ab7dda0c94306c42051b Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 46 ++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sdw-pcm.h | 7 ++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index da4461762c4e..f27b7188b7c8 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1096,6 +1096,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW Pin", + .id = SDW_BE_DAI_ID_MSTR0, .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx", @@ -1118,6 +1119,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW PDM Pin", .ops = &skl_sdw_dai_ops, + .id = SDW_BE_DAI_ID_MSTR0 + 1, .capture = { .stream_name = "SDW Rx1", .channels_min = HDA_MONO, @@ -1125,8 +1127,50 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW1 Pin", + .id = SDW_BE_DAI_ID_MSTR1, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW1 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW1 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, - +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW3 Pin", + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW3 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, }, }; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.h b/sound/soc/intel/skylake/skl-sdw-pcm.h index ab1314a6f408..7166ef9992ab 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.h +++ b/sound/soc/intel/skylake/skl-sdw-pcm.h @@ -24,6 +24,13 @@ #include #include +#define SDW_BE_DAI_ID_BASE 256 +#define SDW_BE_DAI_ID_MSTR0 256 +#define SDW_BE_DAI_ID_MSTR1 (SDW_BE_DAI_ID_MSTR0 + 32) +#define SDW_BE_DAI_ID_MSTR2 (SDW_BE_DAI_ID_MSTR1 + 32) +#define SDW_BE_DAI_ID_MSTR3 (SDW_BE_DAI_ID_MSTR2 + 32) + + int cnl_sdw_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); int cnl_sdw_hw_params(struct snd_pcm_substream *substream, From b86c0fd4194c9709b2b2653c5a3b2e12f2afa320 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 16 Sep 2015 01:56:54 +0530 Subject: [PATCH 0043/1276] ASoC: core: Adds support for DSP loopback dai link Only codec-codec dai link is supported. This patch adds support for dsp loopback dai link Change-Id: I6163e5e572116aeac026ee1815708f7342ae1e3f Signed-off-by: Jeeja KP --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 41cec42fb456..c32c6aaca529 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -920,6 +920,9 @@ struct snd_soc_dai_link { const struct snd_soc_pcm_stream *params; unsigned int num_params; + /* flag to create dsp loopback link */ + unsigned int dsp_loopback:1; + unsigned int dai_fmt; /* format to set on init */ enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 29b42f8f1eff..2ecf3442c965 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1444,8 +1444,14 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); /* link the DAI widgets */ - sink = codec_dai->playback_widget; - source = cpu_dai->capture_widget; + if (!dai_link->dsp_loopback) { + sink = codec_dai->playback_widget; + source = cpu_dai->capture_widget; + } else { + sink = codec_dai->playback_widget; + source = cpu_dai->playback_widget; + } + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, rtd, dai_link->params, dai_link->num_params, @@ -1457,8 +1463,14 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, } } - sink = cpu_dai->playback_widget; - source = codec_dai->capture_widget; + if (!dai_link->dsp_loopback) { + sink = cpu_dai->playback_widget; + source = codec_dai->capture_widget; + } else { + sink = cpu_dai->capture_widget; + source = codec_dai->capture_widget; + } + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, rtd, dai_link->params, dai_link->num_params, From 8447481a399054f63915221cc506d4203b810c99 Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Fri, 18 Sep 2015 20:23:30 +0530 Subject: [PATCH 0044/1276] [WORKAROUND] ASoC: dapm: fix stream directions for dsp_loopback links DSP loopback links have directions reversed for the source and sink widgets. Signed-off-by: Omair M Abdullah --- sound/soc/soc-dapm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 461d951917c0..7e3ec95f985f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3716,7 +3716,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - substream.stream = SNDRV_PCM_STREAM_CAPTURE; + /*substream.stream = SNDRV_PCM_STREAM_CAPTURE;*/ + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; if (source->driver->ops->startup) { ret = source->driver->ops->startup(&substream, source); if (ret < 0) { @@ -3730,7 +3731,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, if (ret < 0) goto out; - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + /*substream.stream = SNDRV_PCM_STREAM_PLAYBACK;*/ + substream.stream = SNDRV_PCM_STREAM_CAPTURE; if (sink->driver->ops->startup) { ret = sink->driver->ops->startup(&substream, sink); if (ret < 0) { From 0dc2d56d3fa912f17c2bacef6be1d94c39160190 Mon Sep 17 00:00:00 2001 From: Omair Mohammed Abdullah Date: Mon, 10 Nov 2014 21:52:55 +0530 Subject: [PATCH 0045/1276] ASoC: utils: add inputs and outputs to dummy codec Add a dummy input and a dummy output to the codec, so that the platform side widgets can be triggered if a backend uses a dummy codec. Make the dummy codec stream names explicit to avoid confusion. Change-Id: I3891e7b670a413c74d71aae1feed9f04e00041e3 Tracked-On: Signed-off-by: Omair Mohammed Abdullah Reviewed-by: Koul, Vinod Tested-by: Koul, Vinod Signed-off-by: Dharageswari.R Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/soc-utils.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index e0c93496c0cd..8503a36beff7 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -282,7 +282,28 @@ static const struct snd_soc_component_driver dummy_platform = { .ops = &dummy_dma_ops, }; +static struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_INPUT("Dummy Input"), + SND_SOC_DAPM_OUTPUT("Dummy Output"), +}; + +static struct snd_soc_dapm_route intercon[] = { + { "Dummy Output", NULL, "Dummy Playback"}, + { "Dummy Capture", NULL, "Dummy Input"}, +}; + +static int dummy_codec_probe(struct snd_soc_component *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + snd_soc_dapm_new_controls(dapm, dapm_widgets, + ARRAY_SIZE(dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + return 0; +} + static const struct snd_soc_component_driver dummy_codec = { + .probe = dummy_codec_probe, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, @@ -308,17 +329,18 @@ static const struct snd_soc_component_driver dummy_codec = { * which should be modelled. And the data flow graph also should be modelled * using DAPM. */ + static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", .playback = { - .stream_name = "Playback", + .stream_name = "Dummy Playback", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, .formats = STUB_FORMATS, }, .capture = { - .stream_name = "Capture", + .stream_name = "Dummy Capture", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, From 55296a9ce0fe29c4d38880f6d2435017947bb201 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 16 Aug 2016 16:26:31 +0530 Subject: [PATCH 0046/1276] ASoC: core: Do not return for dummy codec in soc_probe_component Change-Id: I1bcf17ab9731675e4586382054e5d44645cb18f9 Signed-off-by: Guneshwor Singh --- sound/soc/soc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2ecf3442c965..f41ff22e1317 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1242,10 +1242,10 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; int ret; - +#if 0 if (!strcmp(component->name, "snd-soc-dummy")) return 0; - +#endif if (component->card) { if (component->card != card) { dev_err(component->dev, From 83d4a1c85a3d609882002071da562c8ef0e9bf82 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 17 Aug 2016 13:17:48 +0530 Subject: [PATCH 0047/1276] ASoC: SKL: Fix ch_cfg when fixup is applied Change-Id: I3d1198ea3ff0120f28736a7e7a81029887164634 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-topology.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 413a72dada50..80934faa70d8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -256,6 +256,11 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt, if (fixup & SKL_CH_FIXUP_MASK) { fmt->channels = params->ch; skl_tplg_update_chmap(fmt, fmt->channels); + if (fmt->channels == 1) + fmt->ch_cfg = SKL_CH_CFG_MONO; + else if (fmt->channels == 2) + fmt->ch_cfg = SKL_CH_CFG_STEREO; + } if (fixup & SKL_FMT_FIXUP_MASK) { fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt); From a9326fa994380e4d92ec9caa787f2d514e3db9fc Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 31 Aug 2015 14:05:10 +0530 Subject: [PATCH 0048/1276] ASoC: Intel: Skylake: Add NHLT override control For debugging purposes we may want to not use the BIOS values and test our own values, so allow the override by adding a control file for override method Signed-off-by: Vinod Koul Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-debug.c | 65 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 1 + 2 files changed, 66 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 5d7ac2ee7a3c..09321ba98e78 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -33,6 +33,7 @@ struct skl_debug { struct dentry *fs; struct dentry *modules; + struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; }; @@ -221,6 +222,61 @@ static const struct file_operations soft_regs_ctrl_fops = { .llseek = default_llseek, }; +static ssize_t nhlt_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char *state; + + state = d->skl->nhlt_override ? "enable\n" : "disable\n"; + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); +} + +static ssize_t nhlt_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[16]; + int len = min(count, (sizeof(buf) - 1)); + + + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = 0; + + if (!strncmp(buf, "enable\n", len)) + d->skl->nhlt_override = true; + else if (!strncmp(buf, "disable\n", len)) + d->skl->nhlt_override = false; + else + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + return len; +} + +static const struct file_operations ssp_cntrl_nhlt_fops = { + .open = simple_open, + .read = nhlt_control_read, + .write = nhlt_control_write, + .llseek = default_llseek, +}; + +static int skl_init_nhlt(struct skl_debug *d) +{ + if (!debugfs_create_file("control", + 0644, d->nhlt, + d, &ssp_cntrl_nhlt_fops)) { + dev_err(d->dev, "nhlt control debugfs init failed\n"); + return -EIO; + } + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -253,6 +309,15 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) goto err; } + /* now create the NHLT dir */ + d->nhlt = debugfs_create_dir("nhlt", d->fs); + if (IS_ERR(d->nhlt) || !d->nhlt) { + dev_err(&skl->pci->dev, "nhlt debugfs create failed\n"); + return NULL; + } + + skl_init_nhlt(d); + return d; err: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 78aa8bdcb619..28c62dda07db 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -103,6 +103,7 @@ struct skl { bool use_tplg_pcm; struct skl_fw_config cfg; struct snd_soc_acpi_mach *mach; + bool nhlt_override; }; #define skl_to_bus(s) (&(s)->hbus) From 23957def0f14c3d9c677c1f266a2ed37d0d11b34 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 31 Aug 2015 14:12:14 +0530 Subject: [PATCH 0049/1276] ASoC: Intel: Skylake: Add debugfs NHLT ssp override Add debugfs entries for reading and writing SSP blobs which driver can use to program DSP Signed-off-by: Vinod Koul --- sound/soc/intel/skylake/skl-debug.c | 92 +++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 09321ba98e78..8485e2e4aa90 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -22,10 +22,17 @@ #include "skl-topology.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" +#include "skl-nhlt.h" #define MOD_BUF PAGE_SIZE #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 +#define MAX_SSP 4 + +struct nhlt_blob { + size_t size; + struct nhlt_specific_cfg *cfg; +}; struct skl_debug { struct skl *skl; @@ -35,6 +42,62 @@ struct skl_debug { struct dentry *modules; struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; + struct nhlt_blob ssp_blob[MAX_SSP]; +}; + +static ssize_t nhlt_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct nhlt_blob *blob = file->private_data; + + if (!blob->cfg) + return -EIO; + + return simple_read_from_buffer(user_buf, count, ppos, + blob->cfg, blob->size); +} + +static ssize_t nhlt_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct nhlt_blob *blob = file->private_data; + struct nhlt_specific_cfg *new_cfg; + ssize_t written; + size_t size = blob->size; + + if (!blob->cfg) { + /* allocate mem for blob */ + blob->cfg = kzalloc(count, GFP_KERNEL); + if (!blob->cfg) + return -ENOMEM; + size = count; + } else if (blob->size < count) { + /* size if different, so relloc */ + new_cfg = krealloc(blob->cfg, count, GFP_KERNEL); + if (!new_cfg) + return -ENOMEM; + size = count; + blob->cfg = new_cfg; + } + + written = simple_write_to_buffer(blob->cfg, size, ppos, + user_buf, count); + blob->size = written; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + print_hex_dump(KERN_DEBUG, "Debugfs Blob:", DUMP_PREFIX_OFFSET, 8, 4, + blob->cfg, blob->size, false); + + return written; +} + +static const struct file_operations nhlt_fops = { + .open = simple_open, + .read = nhlt_read, + .write = nhlt_write, + .llseek = default_llseek, }; static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, @@ -166,7 +229,6 @@ static const struct file_operations mcfg_fops = { .llseek = default_llseek, }; - void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig) @@ -222,6 +284,15 @@ static const struct file_operations soft_regs_ctrl_fops = { .llseek = default_llseek, }; +static void skl_exit_nhlt(struct skl_debug *d) +{ + int i; + + /* free blob memory, if allocated */ + for (i = 0; i < MAX_SSP; i++) + kfree(d->ssp_blob[i].cfg); +} + static ssize_t nhlt_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -245,12 +316,14 @@ static ssize_t nhlt_control_write(struct file *file, return -EFAULT; buf[len] = 0; - if (!strncmp(buf, "enable\n", len)) + if (!strncmp(buf, "enable\n", len)) { d->skl->nhlt_override = true; - else if (!strncmp(buf, "disable\n", len)) + } else if (!strncmp(buf, "disable\n", len)) { d->skl->nhlt_override = false; - else + skl_exit_nhlt(d); + } else { return -EINVAL; + } /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); @@ -267,6 +340,9 @@ static const struct file_operations ssp_cntrl_nhlt_fops = { static int skl_init_nhlt(struct skl_debug *d) { + int i; + char name[12]; + if (!debugfs_create_file("control", 0644, d->nhlt, d, &ssp_cntrl_nhlt_fops)) { @@ -274,6 +350,14 @@ static int skl_init_nhlt(struct skl_debug *d) return -EIO; } + for (i = 0; i < MAX_SSP; i++) { + snprintf(name, (sizeof(name)-1), "ssp%d", i); + if (!debugfs_create_file(name, + 0644, d->nhlt, + &d->ssp_blob[i], &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); + } + return 0; } From 60643cc3c53b1889406c5b82044dfbcd0f571e34 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 26 Aug 2015 13:13:56 +0530 Subject: [PATCH 0050/1276] ASoC: Intel: Skylake: Add debugfs NHLT dmic override Add debugfs entries for reading and writing DMIC blobs which driver can use to program DSP Signed-off-by: Vinod Koul --- sound/soc/intel/skylake/skl-debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8485e2e4aa90..9266e478d3c6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -43,6 +43,7 @@ struct skl_debug { struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; struct nhlt_blob ssp_blob[MAX_SSP]; + struct nhlt_blob dmic_blob; }; static ssize_t nhlt_read(struct file *file, char __user *user_buf, @@ -358,6 +359,11 @@ static int skl_init_nhlt(struct skl_debug *d) dev_err(d->dev, "%s: debugfs init failed\n", name); } + if (!debugfs_create_file("dmic", 0644, + d->nhlt, &d->dmic_blob, + &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); + return 0; } From 264715c6120dd2a29b95be765b170496aa8a95b0 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 26 Aug 2015 13:13:56 +0530 Subject: [PATCH 0051/1276] ASoC: Intel: Skylake: Read blobs from debugfs on override Add API to read blobs from debugfs when override is enabled and use that API when sending IPCs to DSP Signed-off-by: Vinod Koul Signed-off-by: Jeeja KP --- sound/soc/intel/skylake/skl-debug.c | 21 +++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.c | 14 +++++++++++++- sound/soc/intel/skylake/skl.h | 8 ++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 9266e478d3c6..9ae01285f399 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -46,6 +46,27 @@ struct skl_debug { struct nhlt_blob dmic_blob; }; +struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +{ + switch (link_type) { + case NHLT_LINK_DMIC: + return d->dmic_blob.cfg; + + case NHLT_LINK_SSP: + if (instance >= MAX_SSP) + return NULL; + + return d->ssp_blob[instance].cfg; + + default: + break; + } + + dev_err(d->dev, "NHLT debugfs query failed\n"); + return NULL; +} + static ssize_t nhlt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 80934faa70d8..e62a1eb57f22 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1888,10 +1888,22 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, return 0; /* update the blob based on virtual bus_id*/ - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + if (!skl->nhlt_override) { + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, params->s_freq, params->stream, dev_type); + } else { + dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); + cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, + link_type, mconfig->vbus_id); + if (cfg->size > HDA_SST_CFG_MAX) { + dev_err(dai->dev, "NHLT debugfs blob is vv large\n"); + dev_err(dai->dev, "First word is size in blob!!!\n"); + dev_err(dai->dev, "Recieved size %d\n", cfg->size); + return -EIO; + } + } if (cfg) { mconfig->formats_config.caps_size = cfg->size; mconfig->formats_config.caps = (u32 *) &cfg->caps; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 28c62dda07db..05561be0efe6 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -165,6 +165,9 @@ struct skl_debug *skl_debugfs_init(struct skl *skl); void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig); +struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance); + #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -174,6 +177,11 @@ static inline void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig) {} +static inline struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +{ + return NULL; +} #endif #endif /* __SOUND_SOC_SKL_H */ From 3541095a5e198d9e3fc1383967f10744861bb7f0 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 8 Sep 2015 22:16:08 +0530 Subject: [PATCH 0052/1276] ASoC: Intel: Skylake: NHLT override, check cfg size in debugfs blob write When blob is updated, check the cfg size. If cfg size exceeds maximum, return error from debugfs in write. Removed check in update_params(), we will pass the pointer to cfg param instead of memcpy. Signed-off-by: Jeeja KP --- sound/soc/intel/skylake/skl-debug.c | 3 +++ sound/soc/intel/skylake/skl-topology.c | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 9ae01285f399..85850b495eb4 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -87,6 +87,9 @@ static ssize_t nhlt_write(struct file *file, ssize_t written; size_t size = blob->size; + if (count > 2 * HDA_SST_CFG_MAX) + return -EIO; + if (!blob->cfg) { /* allocate mem for blob */ blob->cfg = kzalloc(count, GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e62a1eb57f22..7c94fc8e5a0c 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1897,12 +1897,6 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, mconfig->vbus_id); - if (cfg->size > HDA_SST_CFG_MAX) { - dev_err(dai->dev, "NHLT debugfs blob is vv large\n"); - dev_err(dai->dev, "First word is size in blob!!!\n"); - dev_err(dai->dev, "Recieved size %d\n", cfg->size); - return -EIO; - } } if (cfg) { mconfig->formats_config.caps_size = cfg->size; From db09a779198731828f26c7fa6a5f899289e4da80 Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Tue, 15 Sep 2015 17:46:57 +0530 Subject: [PATCH 0053/1276] ASoC: Intel: Skylake: add ssp blob override support for capture Capture on SSP can have different blob, so add support for different blobs for PB/CAP on same SSP. Signed-off-by: Omair M Abdullah --- sound/soc/intel/skylake/skl-debug.c | 19 ++++++++++++++----- sound/soc/intel/skylake/skl-topology.c | 3 ++- sound/soc/intel/skylake/skl.h | 7 ++++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 85850b495eb4..52b65bb096ab 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -42,12 +42,13 @@ struct skl_debug { struct dentry *modules; struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; - struct nhlt_blob ssp_blob[MAX_SSP]; + struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; }; struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream) { switch (link_type) { case NHLT_LINK_DMIC: @@ -57,7 +58,10 @@ struct nhlt_specific_cfg if (instance >= MAX_SSP) return NULL; - return d->ssp_blob[instance].cfg; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + return d->ssp_blob[instance].cfg; + else + return d->ssp_blob[MAX_SSP + instance].cfg; default: break; @@ -315,7 +319,7 @@ static void skl_exit_nhlt(struct skl_debug *d) /* free blob memory, if allocated */ for (i = 0; i < MAX_SSP; i++) - kfree(d->ssp_blob[i].cfg); + kfree(d->ssp_blob[MAX_SSP + i].cfg); } static ssize_t nhlt_control_read(struct file *file, @@ -376,11 +380,16 @@ static int skl_init_nhlt(struct skl_debug *d) } for (i = 0; i < MAX_SSP; i++) { - snprintf(name, (sizeof(name)-1), "ssp%d", i); + snprintf(name, (sizeof(name)-1), "ssp%dp", i); if (!debugfs_create_file(name, 0644, d->nhlt, &d->ssp_blob[i], &nhlt_fops)) dev_err(d->dev, "%s: debugfs init failed\n", name); + snprintf(name, (sizeof(name)-1), "ssp%dc", i); + if (!debugfs_create_file(name, + 0644, d->nhlt, + &d->ssp_blob[MAX_SSP + i], &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); } if (!debugfs_create_file("dmic", 0644, diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7c94fc8e5a0c..d6a3a6f43c91 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1896,7 +1896,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, } else { dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, - link_type, mconfig->vbus_id); + link_type, mconfig->vbus_id, + params->stream); } if (cfg) { mconfig->formats_config.caps_size = cfg->size; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 05561be0efe6..4056ea3de714 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -166,8 +166,8 @@ void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig); struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance); - +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream); #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -178,7 +178,8 @@ static inline void skl_debug_init_module(struct skl_debug *d, struct skl_module_cfg *mconfig) {} static inline struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream) { return NULL; } From 9df917192d177aeb4b387a98545d8e46f376b525 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 2 Nov 2015 07:06:38 +0530 Subject: [PATCH 0054/1276] WORKAROUND: Remove size check for DMIC blob Change-Id: Ic7c70d4f0b1bf137c8bfbfbb9ef9962fdad8daf9 Signed-off-by: Ramesh Babu Reviewed-on: --- sound/soc/intel/skylake/skl-debug.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 52b65bb096ab..f9db7ea363ef 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -91,9 +91,6 @@ static ssize_t nhlt_write(struct file *file, ssize_t written; size_t size = blob->size; - if (count > 2 * HDA_SST_CFG_MAX) - return -EIO; - if (!blob->cfg) { /* allocate mem for blob */ blob->cfg = kzalloc(count, GFP_KERNEL); From 8aa3effef41ce0ce273c7d25f54ad5aee8c6c986 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 14:42:17 +0530 Subject: [PATCH 0055/1276] REVERTME:SKL:Topology: Add logic to create SDW aggregation blob. Blob needs to be sent to copier for SDW aggregation. Normally blob is derived from NHLT, but since in SDW blob, stream number is dynamic, blob needs to be created in driver. Blob creation logic for the SDW is implemented in this function. Actually this should go in skl-messages.c as per review. Need to move this to skl-messages.c in next patch set. Change-Id: Ifc58a2343498190f736295754be2c14e3c6d5bea Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-topology.c | 40 ++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index d6a3a6f43c91..80c82d97d226 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1865,6 +1865,17 @@ static u8 skl_tplg_be_link_type(int dev_type) return ret; } +struct skl_sdw_agg_data_caps { + u32 alh_stream_num; + u32 ch_mask; +} __packed; + +struct skl_sdw_caps_cfg { + u32 gw_attributes; + u32 count; + struct skl_sdw_agg_data_caps data[0]; + +} __packed; /* * Fill the BE gateway parameters @@ -1873,20 +1884,45 @@ static u8 skl_tplg_be_link_type(int dev_type) * The port can have multiple settings so pick based on the PCM * parameters */ +#define SDW_MAX_MASTERS 4 static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { + int i; struct nhlt_specific_cfg *cfg; + struct skl_sdw_caps_cfg *sdw_cfg; struct skl *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type); skl_tplg_fill_dma_id(mconfig, params); - if (link_type == NHLT_LINK_HDA || link_type == NHLT_LINK_SDW) + if (link_type == NHLT_LINK_HDA) return 0; - + if (link_type == NHLT_LINK_SDW) { + sdw_cfg = kzalloc((((sizeof(struct skl_sdw_agg_data_caps)) * + (mconfig->sdw_agg.num_masters)) + 2), + GFP_KERNEL); + if (!sdw_cfg) + return -ENOMEM; + mconfig->formats_config.caps_size = (((sizeof(u32)) * + (mconfig->sdw_agg.num_masters) * 2) + + (2 * (sizeof(u32)))); + + sdw_cfg->count = mconfig->sdw_agg.num_masters; + for (i = 0; i < SDW_MAX_MASTERS; i++) { + if (mconfig->sdw_agg.agg_data[i].ch_mask) { + sdw_cfg->data[i].ch_mask = + mconfig->sdw_agg.agg_data[i].ch_mask; + sdw_cfg->data[i].alh_stream_num = + mconfig->sdw_agg.agg_data[i].alh_stream_num; + } + } + sdw_cfg->count = mconfig->sdw_agg.num_masters; + mconfig->formats_config.caps = (u32 *) sdw_cfg; + return 0; + } /* update the blob based on virtual bus_id*/ if (!skl->nhlt_override) { cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, From 15a4996fb96c7a2e9dff82f4b55281d6c784193b Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 14:55:05 +0530 Subject: [PATCH 0056/1276] SKL:PCM: Derive the SDW master controller number from be_id. SDW Master number or SSP instance number can be derived from VBUS ID. But for aggregation one copier is linked to two SDW master controllers, so its not feasible to get controller number from vbus_id. So fixing the range of BE_IDs for number of controllers to find for which controller PCM ops got called. Change-Id: I6cb995404e0488e660f35f943e9bfc3fdc518e07 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 31 ++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index af8d117c34ed..504228e2cba5 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -24,6 +24,7 @@ #include #include "skl.h" #include "skl-topology.h" +#include "skl-sdw-pcm.h" #define STREAM_STATE_ALLOC_STREAM_TAG 0x1 #define STREAM_STATE_ALLOC_STREAM 0x2 @@ -42,6 +43,7 @@ struct sdw_dma_data { struct sdw_master *mstr; enum cnl_sdw_pdi_stream_type stream_type; int stream_state; + int mstr_nr; }; @@ -54,6 +56,7 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, struct sdw_master *mstr; struct sdw_dma_data *dma; int ret = 0; + char *uuid = NULL; m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); @@ -61,7 +64,18 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } - sdw_ctrl_nr = m_cfg->vbus_id; + if (dai->id >= SDW_BE_DAI_ID_MSTR3) + sdw_ctrl_nr = 3; + + else if (dai->id >= SDW_BE_DAI_ID_MSTR2) + sdw_ctrl_nr = 2; + + else if (dai->id >= SDW_BE_DAI_ID_MSTR1) + sdw_ctrl_nr = 1; + + else + sdw_ctrl_nr = 0; + mstr = sdw_get_master(sdw_ctrl_nr); if (!mstr) { dev_err(dai->dev, "Master controller not found\n"); @@ -81,9 +95,10 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return -EINVAL; } dma->mstr = mstr; + dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); - ret = sdw_alloc_stream_tag(NULL, &dma->stream_tag); + ret = sdw_alloc_stream_tag(uuid, &dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); ret = -EINVAL; @@ -144,7 +159,17 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } - m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + + if (!m_cfg->sdw_agg_enable) + m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + else + m_cfg->sdw_agg.agg_data[dma->mstr_nr].alh_stream_num = + dma->port->pdi_stream->sdw_pdi_num; + ret = skl_tplg_be_update_params(dai, &p_params); + if (ret) + return ret; + + stream_config.frame_rate = params_rate(params); /* TODO: Get the multiplication factor from NHLT or the XML * to decide with Poland team from where to get it From ead135482b151535804c78b2847511b999cbdca8 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 15:02:36 +0530 Subject: [PATCH 0057/1276] REVERTME:SKL:PCM: Enable aggregation for the Maxim codec. Set the aggregation parameters if Maxim codec is enabled. Aggregation parameters should ideally come from ITT topology. ITT change is still not done, so hardcoding in driver for now. Change-Id: I9d8d7ee9f1740b0fa4bc6f20f275272a9577f733 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 504228e2cba5..e41a3b38aa81 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -46,7 +46,10 @@ struct sdw_dma_data { int mstr_nr; }; - +#ifdef CONFIG_SND_SOC_MXFPGA +static char uuid_playback[] = "Agg_p"; +static char uuid_capture[] = "Agg_c"; +#endif int cnl_sdw_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -98,6 +101,12 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); +#ifdef CONFIG_SND_SOC_MXFPGA + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + uuid = uuid_playback; + else + uuid = uuid_capture; +#endif ret = sdw_alloc_stream_tag(uuid, &dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); @@ -115,6 +124,18 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return ret; } +#ifdef CONFIG_SND_SOC_MXFPGA +static void skl_set_agg(struct skl_module_cfg *m_cfg, int be_id) { + m_cfg->sdw_agg_enable = true; + m_cfg->sdw_agg.num_masters = 2; + if (be_id > SDW_BE_DAI_ID_MSTR0) + m_cfg->sdw_agg.agg_data[1].ch_mask = 0x2; + else + m_cfg->sdw_agg.agg_data[0].ch_mask = 0x1; + +} +#endif + int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -159,6 +180,10 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } +#ifdef CONFIG_SND_SOC_MXFPGA + /* Ideally this will come from DFW */ + skl_set_agg(m_cfg, dai->id); +#endif if (!m_cfg->sdw_agg_enable) m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; From c1956975e1422e73b69a632d697d4e02adbbf57e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:09:05 +0530 Subject: [PATCH 0058/1276] ASoC: Intel: Skylake: Driver ring buffer APIs for firmware logging. Data from trace window will be copied in this buffer and passed to user space through compress stream APIs. Assuming a single reader and single writer usecase, no locking has been implemented on this buffer and hence we use kernel kfifo API for the ring buffer implementation. Change-Id: I4d9eb40abf3d4cc7fdb63ef7626bc49aaa378777 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/common/sst-dsp-priv.h | 39 +++++- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-fwlog.c | 168 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-fwlog.h | 20 +++ 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/skylake/skl-fwlog.c create mode 100644 sound/soc/intel/skylake/skl-fwlog.h diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index acf06a4f5144..e7151a7e6bae 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -21,7 +21,9 @@ #include #include #include - +#include +#include +#include #include "../skylake/skl-sst-dsp.h" struct sst_mem_block; @@ -97,6 +99,37 @@ struct sst_mailbox { size_t out_size; }; +/* + * Audio DSP Trace Buffer Configuration. +*/ +struct sst_dbg_rbuffer { + DECLARE_KFIFO_PTR(fifo_dsp, u32); + struct kref refcount; + unsigned long total_avail; + /* To set the state of the stream incase of XRUN */ + struct snd_compr_stream *stream; +}; + +/* + * DSP Trace Buffer for FW Logging + * Assumption: Each core is assigned equal proportion of memory window for fw + * logging addressed in the increasing order of core id (i.e., the first trace + * buffer belong to core 0 and so on). +*/ +struct sst_trace_window { + /* base address and size of fw logging windows */ + void __iomem *addr; + u32 size; + /* driver ringbuffer array for each DSP */ + struct sst_dbg_rbuffer **dbg_buffers; + /* fw write pointer array for each DSP */ + void __iomem **dsp_wps; + /* number of buffers within fw logging window */ + u32 nr_dsp; + /* indicates which DSPs have logging enabled */ + u32 flags; +}; + /* * Audio DSP memory block types. */ @@ -288,6 +321,9 @@ struct sst_dsp { /* mailbox */ struct sst_mailbox mailbox; + /* Trace Buffer */ + struct sst_trace_window trace_wind; + /* HSW/Byt data */ /* list of free and used ADSP memory blocks */ @@ -390,4 +426,5 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp); u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, enum sst_mem_type type); + #endif diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index c5ee108bf5d9..831218ee43c8 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ - skl-sst-utils.o + skl-sst-utils.o skl-fwlog.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c new file mode 100644 index 000000000000..bc25b9e22252 --- /dev/null +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -0,0 +1,168 @@ +/* + * Intel SST FW Log Tracing + * + * Copyright (C) 2015-16, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" +#include "skl.h" +#include "skl-fwlog.h" + +/* + * Initialize trace window and firmware write pointers for the platform + */ +int skl_dsp_init_trace_window(struct sst_dsp *sst, u32 *wp, u32 offset, + u32 size, int cores) +{ + int idx, alloc_size; + void **dsp_wps; + struct sst_dbg_rbuffer **buff; + + alloc_size = sizeof(buff) * cores; + buff = devm_kzalloc(sst->dev, alloc_size, GFP_KERNEL); + if (!buff) + goto failure; + + dsp_wps = devm_kzalloc(sst->dev, sizeof(void *) * cores, GFP_KERNEL); + + if (!dsp_wps) + goto failure; + + sst->trace_wind.addr = sst->addr.lpe + offset; + sst->trace_wind.size = size; + sst->trace_wind.nr_dsp = cores; + sst->trace_wind.flags = 0; + sst->trace_wind.dbg_buffers = buff; + sst->trace_wind.dsp_wps = (void __iomem**)dsp_wps; + for (idx = 0; idx < cores; idx++) + sst->trace_wind.dsp_wps[idx] = (void __iomem*)(sst->addr.lpe + + wp[idx]); + return 0; + +failure: + dev_err(sst->dev, "Trace buffer init failed for the platform\n"); + return -ENOMEM; +} + +/* + * Initialize ring buffer for a dsp fw logging + */ +int skl_dsp_init_log_buffer(struct sst_dsp *sst, int size, int core, + struct snd_compr_stream *stream) +{ + int ret = 0; + struct sst_dbg_rbuffer *tmp; + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = kfifo_alloc(&tmp->fifo_dsp, size, GFP_KERNEL); + if (!ret) { + tmp->stream = stream; + tmp->total_avail = 0; + kref_init(&tmp->refcount); + sst->trace_wind.dbg_buffers[core] = tmp; + } else + kfree(tmp); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_dsp_init_log_buffer); + +unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + if (buff->stream->runtime->state == SNDRV_PCM_STATE_XRUN) + return 0; + + return buff->total_avail; +} +EXPORT_SYMBOL(skl_dsp_log_avail); + +void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, + int count) +{ + int i; + u32 *data = (u32 *)src; + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + if (buff->stream->runtime->state == SNDRV_PCM_STATE_XRUN) + return; + + for (i = 0; i < count; i += 4) { + if (!kfifo_put(&buff->fifo_dsp, *data)) { + dev_err(sst->dev, "fw log buffer overrun on dsp %d\n", + core); + buff->stream->runtime->state = SNDRV_PCM_STATE_XRUN; + break; + } + data++; + } + buff->total_avail += count; +} + +int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, + void __user *dest, int count) +{ + int copied, ret; + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + ret = kfifo_to_user(&buff->fifo_dsp, dest, count, &copied); + + return ret ? ret : copied; +} +EXPORT_SYMBOL_GPL(skl_dsp_copy_log_user); + +static void skl_dsp_free_log_buffer(struct kref *ref) +{ + struct sst_dbg_rbuffer *buff = container_of(ref, struct sst_dbg_rbuffer, + refcount); + kfifo_free(&buff->fifo_dsp); + kfree(buff); +} + +void skl_dsp_get_log_buff(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + kref_get(&buff->refcount); +} +EXPORT_SYMBOL_GPL(skl_dsp_get_log_buff); + +void skl_dsp_put_log_buff(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + kref_put(&buff->refcount, skl_dsp_free_log_buffer); +} +EXPORT_SYMBOL_GPL(skl_dsp_put_log_buff); + +void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core) +{ + skl_dsp_put_log_buff(sst, core); +} +EXPORT_SYMBOL_GPL(skl_dsp_done_log_buffer); + +/* Module Information */ +MODULE_AUTHOR("Ashish Panwar Date: Wed, 20 Jan 2016 19:19:21 +0530 Subject: [PATCH 0059/1276] ASoC: Intel: Skylake: Handler for firmware log buffer status notification We copy half of the firmware log buffer on this notification. We figure which half of trace buffer to copy based on the write pointer. Change-Id: I32d8f47b5eaed3d35b4d2a0761bf4878f0c14d97 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-sst-ipc.c | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5234fafb758a..9864801e9ed1 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -19,9 +19,9 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-fwlog.h" #include "sound/hdaudio_ext.h" - #define IPC_IXC_STATUS_BITS 24 /* Global Message - Generic */ @@ -53,6 +53,10 @@ #define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ << IPC_MSG_DIR_SHIFT) /* Global Notification Message */ +#define IPC_GLB_NOTIFY_CORE_SHIFT 15 +#define IPC_GLB_NOTIFY_CORE_MASK 0x1 +#define IPC_GLB_NOTIFY_CORE_ID(x) (((x) >> IPC_GLB_NOTIFY_CORE_SHIFT) \ + & IPC_GLB_NOTIFY_CORE_MASK) #define IPC_GLB_NOTIFY_TYPE_SHIFT 16 #define IPC_GLB_NOTIFY_TYPE_MASK 0xFF #define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \ @@ -347,6 +351,31 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, } +static void +skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) +{ + int core, size; + u32 *ptr, avail; + u8 *base; + + core = IPC_GLB_NOTIFY_CORE_ID(header.primary); + if (!(BIT(core) & sst->trace_wind.flags)) { + dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); + return; + } + skl_dsp_get_log_buff(sst, core); + size = sst->trace_wind.size/sst->trace_wind.nr_dsp; + base = (u8 *)sst->trace_wind.addr; + /* move to the source dsp tracing window */ + base += (core * size); + ptr = (u32 *)sst->trace_wind.dsp_wps[core]; + avail = *ptr; + if (avail < size/2) + base += size/2; + skl_dsp_write_log(sst, (void __iomem *)base, core, size/2); + skl_dsp_put_log_buff(sst, core); +} + int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { @@ -369,6 +398,10 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, wake_up(&skl->boot_wait); break; + case IPC_GLB_NOTIFY_LOG_BUFFER_STATUS: + skl_process_log_buffer(skl->dsp, header); + break; + case IPC_GLB_NOTIFY_PHRASE_DETECTED: dev_dbg(ipc->dev, "***** Phrase Detected **********\n"); From 487de95b1fa7169f61dce1e66aed9db88733aceb Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:22:05 +0530 Subject: [PATCH 0060/1276] ASoC: Intel: Skylake: Compress ops for firmware logging. Implementation of compress ops for firmware logging. These ops are used to transfer data from driver buffer to user space. Compress device to DSP core mapping is determined by the DAI name. Change-Id: I760f07873cb259a58fb0f517f958b4043719d8a6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-messages.c | 22 ++++ sound/soc/intel/skylake/skl-pcm.c | 157 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 12 ++ 4 files changed, 192 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 221283d83619..2f56dbfea444 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -111,6 +111,7 @@ config SND_SOC_INTEL_SKYLAKE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + select SND_SOC_COMPRESS select SND_SOC_ACPI_INTEL_MATCH help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index a9eaa4aed643..d0121984cf4b 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -66,6 +66,28 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) skl_ipc_set_large_config(&ctx->ipc, &msg, data); } +#define ENABLE_LOGS 6 +#define DEFAULT_LOG_PRIORITY 5 + +/* set firmware logging state via IPC */ +int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) +{ + struct skl_log_state_msg log_msg; + struct skl_ipc_large_config_msg msg = {0}; + int ret = 0; + + log_msg.core_mask = (1 << core); + log_msg.logs_core[core].enable = enable; + log_msg.logs_core[core].priority = DEFAULT_LOG_PRIORITY; + + msg.large_param_id = ENABLE_LOGS; + msg.param_data_size = sizeof(log_msg); + + ret = skl_ipc_set_large_config(ipc, &msg, (u32 *)&log_msg); + + return ret; +} + #define NOTIFICATION_PARAM_ID 3 #define NOTIFICATION_MASK 0xf diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index f27b7188b7c8..284e34fb60f8 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -29,6 +29,7 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-sdw-pcm.h" +#include "skl-fwlog.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -724,6 +725,141 @@ static void skl_sdw_shutdown(struct snd_pcm_substream *substream, pm_runtime_put_autosuspend(dai->dev); } +static bool skl_is_core_valid(int core) +{ + if (core != INT_MIN) + return true; + else + return false; +} + +static int skl_get_compr_core(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + + if (!strcmp(dai->name, "TraceBuffer0 Pin")) + return 0; + else if (!strcmp(dai->name, "TraceBuffer1 Pin")) + return 1; + else + return INT_MIN; +} + +static int skl_is_logging_core(int core) +{ + if (core == 0 || core == 1) + return 1; + else + return 0; +} + +static struct skl_sst *skl_get_sst_compr(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct skl *skl = ebus_to_skl(ebus); + struct skl_sst *sst = skl->skl_sst; + + return sst; +} + +static int skl_trace_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params, + struct snd_soc_dai *cpu_dai) +{ + int ret; + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + struct sst_generic_ipc *ipc = &skl_sst->ipc; + int size = params->buffer.fragment_size * params->buffer.fragments; + int core = skl_get_compr_core(stream); + + if (!skl_is_core_valid(core)) + return -EINVAL; + + if (size & (size - 1)) { + dev_err(sst->dev, "Buffer size must be a power of 2\n"); + return -EINVAL; + } + + ret = skl_dsp_init_log_buffer(sst, size, core, stream); + if (ret) { + dev_err(sst->dev, "set params failed for dsp %d\n", core); + return ret; + } + + skl_dsp_get_log_buff(sst, core); + sst->trace_wind.flags |= BIT(core); + ret = skl_dsp_enable_logging(ipc, core, 1); + if (ret < 0) { + dev_err(sst->dev, "enable logs failed for dsp %d\n", core); + sst->trace_wind.flags &= ~BIT(core); + skl_dsp_put_log_buff(sst, core); + return ret; + } + return 0; +} + +static int skl_trace_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *cpu_dai) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + int core = skl_get_compr_core(stream); + + if (!skl_is_core_valid(core)) + return -EINVAL; + + tstamp->copied_total = skl_dsp_log_avail(sst, core); + return 0; +} + +static int skl_trace_compr_copy(struct snd_compr_stream *stream, + char __user *dest, size_t count) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + int core = skl_get_compr_core(stream); + + if (skl_is_logging_core(core)) + return skl_dsp_copy_log_user(sst, core, dest, count); + else + return 0; +} + +static int skl_trace_compr_free(struct snd_compr_stream *stream, + struct snd_soc_dai *cpu_dai) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + struct sst_generic_ipc *ipc = &skl_sst->ipc; + int core = skl_get_compr_core(stream); + int is_enabled = sst->trace_wind.flags & BIT(core); + + if (!skl_is_core_valid(core)) + return -EINVAL; + if (is_enabled) { + sst->trace_wind.flags &= ~BIT(core); + skl_dsp_enable_logging(ipc, core, 0); + skl_dsp_put_log_buff(sst, core); + skl_dsp_done_log_buffer(sst, core); + } + return 0; +} + +static struct snd_compr_ops skl_platform_compr_ops = { + .copy = skl_trace_compr_copy, +}; + +static struct snd_soc_cdai_ops skl_trace_compr_ops = { + .shutdown = skl_trace_compr_free, + .pointer = skl_trace_compr_tstamp, + .set_params = skl_trace_compr_set_params, +}; + static const struct snd_soc_dai_ops skl_pcm_dai_ops = { .startup = skl_pcm_open, .shutdown = skl_pcm_close, @@ -758,6 +894,26 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { }; static struct snd_soc_dai_driver skl_fe_dai[] = { +{ + .name = "TraceBuffer0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer1 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -1576,6 +1732,7 @@ static const struct snd_soc_component_driver skl_component = { .name = "pcm", .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, + .compr_ops = &skl_platform_compr_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, }; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 10a486939515..a8de8cfac6ee 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -161,6 +161,16 @@ struct skl_ipc_d0ix_msg { u8 wake; }; +struct skl_log_state { + u32 enable; + u32 priority; +}; + +struct skl_log_state_msg { + u32 core_mask; + struct skl_log_state logs_core[2]; +}; + #define SKL_IPC_BOOT_MSECS 3000 #define SKL_IPC_D3_MASK 0 @@ -210,6 +220,8 @@ int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); +int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); From 14bb3ed012eaf6ee98cd2ff17f6fec394e42e27a Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 26 Feb 2016 02:02:48 +0530 Subject: [PATCH 0061/1276] ASoC: Intel: Skylake: Check buffer users and prevent concurrent writers More than one writer on log buffer will cause data corruption. Fix this by checking for an existing writer and dropping log buffer status notification if the prev writer is still writing. Despite the fear of loosing some data, it helps in maintaining the sanity of logs. Change-Id: Id85827221fa50b71da48087f82ac08ed488f9929 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-fwlog.c | 7 +++++++ sound/soc/intel/skylake/skl-fwlog.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index bc25b9e22252..96c5d245849d 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -98,6 +98,13 @@ unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) } EXPORT_SYMBOL(skl_dsp_log_avail); +int skl_dsp_get_buff_users(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + return refcount_read(&buff->refcount.refcount); +} + void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, int count) { diff --git a/sound/soc/intel/skylake/skl-fwlog.h b/sound/soc/intel/skylake/skl-fwlog.h index 06304de3d2ab..d6307cafd156 100644 --- a/sound/soc/intel/skylake/skl-fwlog.h +++ b/sound/soc/intel/skylake/skl-fwlog.h @@ -17,4 +17,5 @@ int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, void __user *dest, void skl_dsp_get_log_buff(struct sst_dsp *sst, int core); void skl_dsp_put_log_buff(struct sst_dsp *sst, int core); void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core); +int skl_dsp_get_buff_users(struct sst_dsp *sst, int core); #endif /* __SKL_FWLOG_H__ */ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 9864801e9ed1..36c22c57cf90 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -363,6 +363,12 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); return; } + if (skl_dsp_get_buff_users(sst, core) > 2) { + dev_err(sst->dev, "Can't handle log buffer notification, \ + previous writer is not finished yet !\n \ + dropping log buffer\n"); + return; + } skl_dsp_get_log_buff(sst, core); size = sst->trace_wind.size/sst->trace_wind.nr_dsp; base = (u8 *)sst->trace_wind.addr; From ee7e891f3cdedd660de0d1303c2ac7cb585dd40b Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 4 Mar 2016 11:14:28 +0530 Subject: [PATCH 0062/1276] ASoC: Intel: Skylake: Wake up any potential reader after copying log data Change-Id: I0f9587d0c7f6bdd83b5b0cd62afc51f767cffb92 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-fwlog.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index 96c5d245849d..1aacf8625658 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -125,6 +125,7 @@ void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, data++; } buff->total_avail += count; + wake_up(&buff->stream->runtime->sleep); } int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, From 83294ff94c9e5ee1d0e11b73fbccfbc0fd0a4301 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 4 Mar 2016 11:20:00 +0530 Subject: [PATCH 0063/1276] ASoC: Intel: Skylake: Convert buffer size to # of u32 elements before allocating memory While allocating memory, kfifo expects the number of elements in arguments and not the # of bytes. Due to this, kfifo is currently allocating 4 times the memory requested by user. Convert buffer size to the number of elements before allocating memory. Change-Id: Ib8938bc1645896a5b342aa7fe5602b4e415960a6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 284e34fb60f8..d462b1b89cb4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -779,6 +779,7 @@ static int skl_trace_compr_set_params(struct snd_compr_stream *stream, if (!skl_is_core_valid(core)) return -EINVAL; + size = size / sizeof(u32); if (size & (size - 1)) { dev_err(sst->dev, "Buffer size must be a power of 2\n"); return -EINVAL; From 1c42eae1eb455021787ce6d969ffbc71f1a6526e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 18 Mar 2016 16:01:07 +0530 Subject: [PATCH 0064/1276] ASoC: Intel: CNL: Initialize trace buffer window for CNL Initialize address, size of tracing window and write pointers of each core for CNL platform. Change-Id: I9febfe5bd1eef76f50f5de170c6c99fc98cdc6d9 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/skylake/cnl-sst.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index f70d70d0822a..b82de714c916 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -37,6 +37,7 @@ #include "cnl-sst-dsp.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-fwlog.h" #define CNL_FW_ROM_INIT 0x1 #define CNL_FW_INIT 0x5 @@ -46,6 +47,14 @@ #define CNL_ADSP_SRAM0_BASE 0x80000 +/* Trace Buffer Window */ +#define CNL_ADSP_SRAM2_BASE 0x0C0000 +#define CNL_ADSP_W2_SIZE 0x2000 +#define CNL_ADSP_WP_DSP0 (CNL_ADSP_SRAM0_BASE+0x30) +#define CNL_ADSP_WP_DSP1 (CNL_ADSP_SRAM0_BASE+0x34) +#define CNL_ADSP_WP_DSP2 (CNL_ADSP_SRAM0_BASE+0x38) +#define CNL_ADSP_WP_DSP3 (CNL_ADSP_SRAM0_BASE+0x3C) + /* Firmware status window */ #define CNL_ADSP_FW_STATUS CNL_ADSP_SRAM0_BASE #define CNL_ADSP_ERROR_CODE (CNL_ADSP_FW_STATUS + 0x4) @@ -629,6 +638,8 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, { struct skl_sst *cnl; struct sst_dsp *sst; + u32 dsp_wp[] = {CNL_ADSP_WP_DSP0, CNL_ADSP_WP_DSP1, CNL_ADSP_WP_DSP2, + CNL_ADSP_WP_DSP3}; int ret; ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev); @@ -651,6 +662,13 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, CNL_ADSP_W0_UP_SZ, CNL_ADSP_SRAM1_BASE, CNL_ADSP_W1_SZ); + ret = skl_dsp_init_trace_window(sst, dsp_wp, CNL_ADSP_SRAM2_BASE, + CNL_ADSP_W2_SIZE, CNL_DSP_CORES); + if (ret) { + dev_err(dev, "FW tracing init failed : %x", ret); + return ret; + } + ret = cnl_ipc_init(dev, cnl); if (ret) { skl_dsp_free(sst); From 1a4c96081a1c41b4660cdc1204d22cde383308f9 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 18 Mar 2016 16:02:19 +0530 Subject: [PATCH 0065/1276] ASoC: Intel: Skylake: Add trace buffer dais for CNL CNL needs 4 dais for logging, one for each core. Added 2 more dais here. It depends on the machine driver to make use of appropriate dais based on the hardware platform. Change-Id: If9df857895b8261cfebd61617542365187499ea1 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-pcm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d462b1b89cb4..dc842e289dfb 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -742,6 +742,10 @@ static int skl_get_compr_core(struct snd_compr_stream *stream) return 0; else if (!strcmp(dai->name, "TraceBuffer1 Pin")) return 1; + else if (!strcmp(dai->name, "TraceBuffer2 Pin")) + return 2; + else if (!strcmp(dai->name, "TraceBuffer3 Pin")) + return 3; else return INT_MIN; } @@ -900,7 +904,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .compress_new = snd_soc_new_compress, .cops = &skl_trace_compr_ops, .capture = { - .stream_name = "TraceBuffer Capture", + .stream_name = "TraceBuffer0 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, }, @@ -915,6 +919,26 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .channels_max = HDA_MONO, }, }, +{ + .name = "TraceBuffer2 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer2 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer3 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer3 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, From e579e503169167c4a0b1fcc29b0aae0be9ab13ac Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Sat, 19 Mar 2016 08:44:11 +0530 Subject: [PATCH 0066/1276] REVERTME-1: Revert when logging is updated in driver/firmware. Core 0 is currently using entire 8KB window for logging. Revert this patch when it is shared by all 4 cores or logging infrastructure is updated in driver. Change-Id: I4798c501030ce9216918d629f6c5f898e1e16aa6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/skylake/skl-messages.c | 4 ++++ sound/soc/intel/skylake/skl-sst-ipc.c | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d0121984cf4b..3336a23d9898 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -67,7 +67,11 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#define DEFAULT_LOG_PRIORITY 6 +#else #define DEFAULT_LOG_PRIORITY 5 +#endif /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 36c22c57cf90..d34ff3ffeced 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -358,7 +358,11 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) u32 *ptr, avail; u8 *base; +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) + core = 0; +#else core = IPC_GLB_NOTIFY_CORE_ID(header.primary); +#endif if (!(BIT(core) & sst->trace_wind.flags)) { dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); return; @@ -370,7 +374,11 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) return; } skl_dsp_get_log_buff(sst, core); +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) + size = sst->trace_wind.size; +#else size = sst->trace_wind.size/sst->trace_wind.nr_dsp; +#endif base = (u8 *)sst->trace_wind.addr; /* move to the source dsp tracing window */ base += (core * size); From 48ca9e4971ff19151bd689dea315785af1b78354 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 5 Jul 2017 14:30:41 +0530 Subject: [PATCH 0067/1276] ASoC: Intel: Skylake: Add dsp log level selection kcontrol Add platform kcontrol for DSP log level. User can select between QUIET, CRITICAL, HIGH, MEDIUM, LOW, VERBOSE levels before starting the compressed stream for log capture. Change-Id: I2cc1965fe58ed60defaa50fa494a0d9a39e4d477 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar Reviewed-by: Diwakar, Praveen Signed-off-by: Guneshwor Singh --- sound/soc/intel/common/sst-dsp-priv.h | 2 ++ sound/soc/intel/skylake/skl-fwlog.c | 23 +++++++++++++++++++++++ sound/soc/intel/skylake/skl-fwlog.h | 2 ++ sound/soc/intel/skylake/skl-messages.c | 7 +------ sound/soc/intel/skylake/skl-pcm.c | 13 +++++++++++++ sound/soc/intel/skylake/skl-topology.c | 24 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 4 ++++ 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index e7151a7e6bae..8e80c6f177f8 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -128,6 +128,8 @@ struct sst_trace_window { u32 nr_dsp; /* indicates which DSPs have logging enabled */ u32 flags; + /* dsp fw log level*/ + u32 log_priority; }; /* diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index 1aacf8625658..f7506135e311 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -25,6 +25,8 @@ #include "skl.h" #include "skl-fwlog.h" +#define DEF_LOG_PRIORITY 3 + /* * Initialize trace window and firmware write pointers for the platform */ @@ -51,6 +53,7 @@ int skl_dsp_init_trace_window(struct sst_dsp *sst, u32 *wp, u32 offset, sst->trace_wind.flags = 0; sst->trace_wind.dbg_buffers = buff; sst->trace_wind.dsp_wps = (void __iomem**)dsp_wps; + sst->trace_wind.log_priority = DEF_LOG_PRIORITY; for (idx = 0; idx < cores; idx++) sst->trace_wind.dsp_wps[idx] = (void __iomem*)(sst->addr.lpe + wp[idx]); @@ -87,6 +90,26 @@ int skl_dsp_init_log_buffer(struct sst_dsp *sst, int size, int core, } EXPORT_SYMBOL_GPL(skl_dsp_init_log_buffer); +int update_dsp_log_priority(int value, struct skl *skl) +{ + int ret = 0; + struct skl_sst *ctx = skl->skl_sst; + + ctx->dsp->trace_wind.log_priority = value; + return ret; +} +EXPORT_SYMBOL_GPL(update_dsp_log_priority); + +int get_dsp_log_priority(struct skl *skl) +{ + u32 value; + struct skl_sst *ctx = skl->skl_sst; + + value = ctx->dsp->trace_wind.log_priority; + return value; +} +EXPORT_SYMBOL_GPL(get_dsp_log_priority); + unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) { struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; diff --git a/sound/soc/intel/skylake/skl-fwlog.h b/sound/soc/intel/skylake/skl-fwlog.h index d6307cafd156..df9146686ea2 100644 --- a/sound/soc/intel/skylake/skl-fwlog.h +++ b/sound/soc/intel/skylake/skl-fwlog.h @@ -18,4 +18,6 @@ void skl_dsp_get_log_buff(struct sst_dsp *sst, int core); void skl_dsp_put_log_buff(struct sst_dsp *sst, int core); void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core); int skl_dsp_get_buff_users(struct sst_dsp *sst, int core); +int update_dsp_log_priority(int value, struct skl *skl); +int get_dsp_log_priority(struct skl *skl); #endif /* __SKL_FWLOG_H__ */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 3336a23d9898..4f88595815a8 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -67,11 +67,6 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 -#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) -#define DEFAULT_LOG_PRIORITY 6 -#else -#define DEFAULT_LOG_PRIORITY 5 -#endif /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) @@ -82,7 +77,7 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) log_msg.core_mask = (1 << core); log_msg.logs_core[core].enable = enable; - log_msg.logs_core[core].priority = DEFAULT_LOG_PRIORITY; + log_msg.logs_core[core].priority = ipc->dsp->trace_wind.log_priority; msg.large_param_id = ENABLE_LOGS; msg.param_data_size = sizeof(log_msg); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dc842e289dfb..388397d70be4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1753,6 +1753,17 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return 0; } +static const char* const dsp_log_text[] = + {"QUIET", "CRITICAL", "HIGH", "MEDIUM", "LOW", "VERBOSE"}; + +static const struct soc_enum dsp_log_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dsp_log_text), dsp_log_text); + +static struct snd_kcontrol_new skl_controls[] = { + SOC_ENUM_EXT("DSP Log Level", dsp_log_enum, skl_tplg_dsp_log_get, + skl_tplg_dsp_log_set), +}; + static const struct snd_soc_component_driver skl_component = { .name = "pcm", .probe = skl_platform_soc_probe, @@ -1760,6 +1771,8 @@ static const struct snd_soc_component_driver skl_component = { .compr_ops = &skl_platform_compr_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, + .controls = skl_controls, + .num_controls = ARRAY_SIZE(skl_controls), }; int skl_platform_register(struct device *dev) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 80c82d97d226..5c5e3c45008d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -30,6 +30,7 @@ #include "skl.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" +#include "skl-fwlog.h" #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) @@ -1453,6 +1454,29 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } +int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + + ucontrol->value.integer.value[0] = get_dsp_log_priority(skl); + + return 0; +} + +int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + + update_dsp_log_priority(ucontrol->value.integer.value[0], skl); + + return 0; +} static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 44e4109ba4dc..5998e6926df3 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -533,4 +533,8 @@ int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); +int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); #endif From e5ad9ce3e92db7ecec237144fdb87cc274674fdb Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 9 Jun 2016 17:33:01 +0530 Subject: [PATCH 0068/1276] ASoC: Intel: CNL: Enable SDW aggregation support from ITT Change-Id: I7f1dac7aab70b340e3faa0b1d8330c0416484cb8 Signed-off-by: Guneshwor Singh --- include/uapi/sound/skl-tplg-interface.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 68eda1a15e39..47f033884b2f 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -23,6 +23,8 @@ #define MAX_IN_QUEUE 8 #define MAX_OUT_QUEUE 8 +#define SDW_MAX_MASTERS 4 + #define SKL_UUID_STR_SZ 40 /* Event types goes here */ /* Reserve event type 0 for no event handlers */ @@ -151,6 +153,11 @@ enum skl_module_param_type { SKL_PARAM_BIND }; +struct skl_dfw_sdw_aggdata { + u32 alh_stream_num; + u32 channel_mask; +} __packed; + struct skl_dfw_algo_data { __u32 set_params:2; __u32 rsvd:30; From c4a93101b25dfdabb96f42c6a81f47bb2738dad7 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 9 Jun 2016 17:35:39 +0530 Subject: [PATCH 0069/1276] ASoC: Intel: SKL: Remove SDW aggregation hardcode data Change-Id: I6601af59781950f4fcf2a13e93be6e3014957c69 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-sdw-pcm.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index e41a3b38aa81..ea9a3e14434a 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -124,18 +124,6 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return ret; } -#ifdef CONFIG_SND_SOC_MXFPGA -static void skl_set_agg(struct skl_module_cfg *m_cfg, int be_id) { - m_cfg->sdw_agg_enable = true; - m_cfg->sdw_agg.num_masters = 2; - if (be_id > SDW_BE_DAI_ID_MSTR0) - m_cfg->sdw_agg.agg_data[1].ch_mask = 0x2; - else - m_cfg->sdw_agg.agg_data[0].ch_mask = 0x1; - -} -#endif - int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -180,10 +168,6 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } -#ifdef CONFIG_SND_SOC_MXFPGA - /* Ideally this will come from DFW */ - skl_set_agg(m_cfg, dai->id); -#endif if (!m_cfg->sdw_agg_enable) m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; From a58c3777bd8c19eef90b4521bbbbda8c986bb25b Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Fri, 5 Aug 2016 16:35:34 +0530 Subject: [PATCH 0070/1276] ASoC: Intel: Aggregation fixes for masters other than 0 and 1 Change-Id: Ie26608b30f75c69d039ee17c5703ad1f79e88f00 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-topology.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 5c5e3c45008d..35c0ec938a18 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1908,12 +1908,11 @@ struct skl_sdw_caps_cfg { * The port can have multiple settings so pick based on the PCM * parameters */ -#define SDW_MAX_MASTERS 4 static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - int i; + int i, j; struct nhlt_specific_cfg *cfg; struct skl_sdw_caps_cfg *sdw_cfg; struct skl *skl = get_skl_ctx(dai->dev); @@ -1935,12 +1934,14 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + (2 * (sizeof(u32)))); sdw_cfg->count = mconfig->sdw_agg.num_masters; + j = 0; for (i = 0; i < SDW_MAX_MASTERS; i++) { if (mconfig->sdw_agg.agg_data[i].ch_mask) { - sdw_cfg->data[i].ch_mask = + sdw_cfg->data[j].ch_mask = mconfig->sdw_agg.agg_data[i].ch_mask; - sdw_cfg->data[i].alh_stream_num = + sdw_cfg->data[j].alh_stream_num = mconfig->sdw_agg.agg_data[i].alh_stream_num; + j++; } } sdw_cfg->count = mconfig->sdw_agg.num_masters; From 35885f6aff39b965592cc2ee4ae5d7d166c34db4 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 24 Feb 2016 17:03:16 +0530 Subject: [PATCH 0071/1276] ASoC: Fix TLV control size in TLV handler Size passed to TLV callback is sum of payload size and TLV header. TLV header consumes 8 bytes. The callback handler has to pass the entire data (including TLV header) to drivers. Change-Id: Ic7dcd0d3ab015e340ff907b2dc507b63677cc2ff Tracked-On: Signed-off-by: Pawse, GuruprasadX Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/soc-ops.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 592efb370c44..f5b48d53bc19 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -764,11 +764,16 @@ int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); +/* TLV header size*/ +#define TLV_HEADER_SIZE (2 * sizeof(unsigned int)) + int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct soc_bytes_ext *params = (void *)kcontrol->private_value; - unsigned int count = size < params->max ? size : params->max; + /* Data includes includes TLV Header */ + unsigned int count = size < (params->max + TLV_HEADER_SIZE) ? + size : (params->max + TLV_HEADER_SIZE); int ret = -ENXIO; switch (op_flag) { From 40e83490523b178355f764f5a2a00c37e9178506 Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Thu, 7 Apr 2016 10:32:45 +0530 Subject: [PATCH 0072/1276] ALSA: hda: Enhance HD audio framework to support compress streams Introduce APIs for compress stream DMA assignment, BDL setup and buffer allocation. Change-Id: I8cf4c8b96367cacc131a304c758d0aef53010d25 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- include/sound/compress_driver.h | 6 ++++ include/sound/hdaudio.h | 2 ++ include/sound/hdaudio_ext.h | 4 +++ sound/hda/ext/hdac_ext_stream.c | 43 +++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 51 ++++++++++++++++++++++----------- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index ea8c93bbb0e0..47a8af466727 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -45,6 +45,11 @@ struct snd_compr_runtime { u64 total_bytes_transferred; wait_queue_head_t sleep; void *private_data; + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + size_t dma_bytes; /* size of DMA area */ + struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ }; /** @@ -69,6 +74,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; + struct snd_dma_buffer dma_buffer; }; /** diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 0aa0189017ba..5eee13a0c045 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -487,6 +487,7 @@ struct hdac_stream { struct snd_pcm_substream *substream; /* assigned substream, * set in PCM open */ + struct snd_compr_stream *stream; unsigned int format_val; /* format value to be set in the * controller and the codec */ @@ -500,6 +501,7 @@ struct hdac_stream { bool no_period_wakeup:1; bool locked:1; + unsigned long curr_pos; /* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ unsigned long period_wallclk; /* wallclk for period */ diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index f34aced69ca8..2d93a039a286 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -88,6 +88,10 @@ void snd_hdac_link_free_all(struct hdac_bus *bus); struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index a835558ddbc9..4bd95f4b2234 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include /** * snd_hdac_ext_stream_init - initialize each stream (aka device) @@ -549,3 +551,44 @@ int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + + if (!hbus->ppcap) { + dev_err(hbus->dev, "stream type not supported\n"); + return NULL; + } + + list_for_each_entry(stream, &hbus->stream_list, list) { + struct hdac_ext_stream *hstream = container_of(stream, + struct hdac_ext_stream, + hstream); + if (stream->direction != direction) + continue; + + if (!stream->opened) { + if (!hstream->decoupled) + snd_hdac_ext_stream_decouple(ebus, + hstream, true); + res = hstream; + break; + } + } + if (res) { + spin_lock_irq(&hbus->reg_lock); + res->hstream.opened = 1; + res->hstream.running = 0; + res->hstream.stream = substream; + spin_unlock_irq(&hbus->reg_lock); + } + dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + res->hstream.stream_tag, res->hstream.index); + return res; +} +EXPORT_SYMBOL_GPL(hdac_ext_host_stream_compr_assign); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index eee422390d8e..d3b1e22ac050 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "trace.h" /** @@ -363,11 +364,22 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; + struct snd_dma_buffer *dma_buffer_p = NULL; __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + if (substream) { + runtime = substream->runtime; + dma_buffer_p = snd_pcm_get_dma_buf(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + dma_buffer_p = csubstream->runtime->dma_buffer_p; + } + /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); @@ -381,7 +393,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->frags = 0; pos_adj = bus->bdl_pos_adj; - if (!azx_dev->no_period_wakeup && pos_adj > 0) { + if (!azx_dev->no_period_wakeup && pos_adj > 0 && substream) { pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; if (!pos_adj) @@ -395,8 +407,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) pos_adj); pos_adj = 0; } else { - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; @@ -406,14 +417,12 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->no_period_wakeup); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes, + !azx_dev->no_period_wakeup); if (ofs < 0) goto error; } @@ -440,14 +449,21 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int bufsize, period_bytes; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; int err; - if (!substream) + if (substream) { + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + bufsize = cruntime->buffer_size; + period_bytes = cruntime->fragment_size; + } else return -EINVAL; - runtime = substream->runtime; - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || @@ -456,7 +472,8 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + if (substream) + azx_dev->no_period_wakeup = runtime->no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; From e15a19b8a9202dd1bca8d46bf75d6f3640d5c0ce Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Wed, 20 Apr 2016 10:04:07 +0530 Subject: [PATCH 0073/1276] ALSA: hda: Service buffer completed interrupts for compress streams In case of the IOC bit being enabled, receive and service the buffer completed interrupts for compress streams. Change-Id: Ic391b6757c374379637f1889dbd519b4b757f708 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/hda/hdac_controller.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 74244d8e2909..ff841cd452fd 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -549,8 +549,9 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; - if (!azx_dev->substream || !azx_dev->running || - !(sd_status & SD_INT_COMPLETE)) + if ((!azx_dev->substream && !azx_dev->stream)|| + !azx_dev->running || + !(sd_status & SD_INT_COMPLETE)) continue; if (ack) ack(bus, azx_dev); From bbad360cf72539e8966932f5f07571ba1441107a Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Sun, 10 Apr 2016 13:36:16 +0530 Subject: [PATCH 0074/1276] ASoC: Intel: Add delete module IPC This IPC is to explicitly delete a module instance. Till now, destroying a pipe was taking care of deleting module instance in the pipe. This IPC would be needed in the case of standalone modules like probe. Change-Id: I6aa9bec8ae3b3311a3c43257cdcd354c7fd3712b Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-sst-ipc.c | 30 ++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 3 +++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index d34ff3ffeced..b83a3076a1e3 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -284,7 +284,8 @@ enum skl_ipc_module_msg { IPC_MOD_BIND = 5, IPC_MOD_UNBIND = 6, IPC_MOD_SET_DX = 7, - IPC_MOD_SET_D0IX = 8 + IPC_MOD_SET_D0IX = 8, + IPC_MOD_DELETE_INSTANCE = 11 }; void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, @@ -802,6 +803,33 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, } EXPORT_SYMBOL_GPL(skl_ipc_set_dx); +int skl_ipc_delete_instance(struct sst_generic_ipc *ipc, + struct skl_ipc_init_instance_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_DELETE_INSTANCE); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, + msg->param_data_size, NULL, 0); + + if (ret < 0) { + dev_err(ipc->dev, "ipc: delete instance failed\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_delete_instance); + int skl_ipc_init_instance(struct sst_generic_ipc *ipc, struct skl_ipc_init_instance_msg *msg, void *param_data) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index a8de8cfac6ee..eb030dfb88ca 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -194,6 +194,9 @@ int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id); int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, struct skl_ipc_init_instance_msg *msg, void *param_data); +int skl_ipc_delete_instance(struct sst_generic_ipc *sst_ipc, + struct skl_ipc_init_instance_msg *msg); + int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, struct skl_ipc_bind_unbind_msg *msg); From 22feb7af281a06d427095adad99c2ff2dad87b4b Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Wed, 20 Apr 2016 10:08:03 +0530 Subject: [PATCH 0075/1276] ASoC: Intel: Add Probe compress APIs Using the compress framework, introduce probe compress dai APIs to open, set parameters, start, stop, and close a probe stream. Change-Id: Id869e23c5a59602f171aadff89bf5acb0419abab Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Skylake: Add probe CPU dai and DAI ops Add 2 CPU dais, one each for extractor and injector and register the corresponding DAI ops Change-Id: I0df64b2b6a1e242f8e10bec833c3f5ab28aa2d84 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Maintain the platform compress APIs as common code. Keep the platform driver compress APIs in a seperate file for common use across different compress DAI ops Change-Id: I62f2396f5e04007ba1454a2187c807c196547c4a Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Implement probe TLV handler Implement probe TLV handler to set and get probe points. The controls parameters contains following information: - whether it is a connection or disconnection - whether the probe type is injector, extractor or injector-reextract - Probe point to probe, uniquely represented by Module ID, Instance ID, queue type and index This information is parsed by the driver in the handler to send the corrensponding IPC and IPC payload.Therefore a custom TLV handler implementation is required for probe. Change-Id: I54d6dc656b0629d85d64a793d54b29f02cc43c35 Signed-off-by: Divya Prakash Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Probe stream operations in alignment with new firmware interface Based on the new firmware interface and IPCs, following are implemented: - Probe as a standalone module and not as a pipeline - Probe module initialization, gateway configuration and deletion - Single extractor and multiple injector stream setup - Caching stream info such as DMA channel, buffer size, DMA type for all streams - Single probe instance running for all extractors and injectors - IPCs for connection, disconnection and DMA attach Change-Id: Icad5108227ff12a7f6cb23b3ae47358f1c8f2cf4 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ALSA: hda: ext: KW fixes for probe feature in hdac_ext stream This patch will add fixes for below klocwork errors, 1. Null pointer 'res' that comes from line 528 may be dereferenced at line 560. 2. Pointer 'res' checked for NULL at line 552 may be dereferenced at line 560. Change-Id: I083c779466589db36d38ccfdda2483004deaeb43 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ALSA: hda: KW fixes for probe feature This patch will add fixes for below klocwork errors, 1. Null pointer 'dma_buffer_p' that comes from line 390 may be passed to function and can be dereferenced there by passing argument 2 to function 'setup_bdle' at line 443. 2. Null pointer 'runtime' that comes from line 472 may be dereferenced at line 490. Change-Id: Iee59594e8571c06568980204f7352583554c5bf1 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- include/uapi/sound/skl-tplg-interface.h | 13 + sound/hda/ext/hdac_ext_stream.c | 4 +- sound/hda/hdac_stream.c | 11 +- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-compress.c | 132 ++++++++++ sound/soc/intel/skylake/skl-compress.h | 35 +++ sound/soc/intel/skylake/skl-messages.c | 123 +++++++++ sound/soc/intel/skylake/skl-pcm.c | 57 ++++- sound/soc/intel/skylake/skl-probe.c | 324 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-probe.h | 37 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 31 +++ sound/soc/intel/skylake/skl-topology.c | 220 +++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 48 +++- sound/soc/intel/skylake/skl.c | 23 +- 14 files changed, 1048 insertions(+), 12 deletions(-) create mode 100644 sound/soc/intel/skylake/skl-compress.c create mode 100644 sound/soc/intel/skylake/skl-compress.h create mode 100644 sound/soc/intel/skylake/skl-probe.c create mode 100644 sound/soc/intel/skylake/skl-probe.h diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 47f033884b2f..e1a7771f4873 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -18,6 +18,7 @@ */ #define SKL_CONTROL_TYPE_BYTE_TLV 0x100 #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 +#define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 @@ -79,6 +80,7 @@ enum skl_module_type { SKL_MODULE_TYPE_BASE_OUTFMT, SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, + SKL_MODULE_TYPE_PROBE }; enum skl_core_affinity { @@ -153,6 +155,17 @@ enum skl_module_param_type { SKL_PARAM_BIND }; +enum skl_probe_connect_type { + SKL_PROBE_CONNECT = 3, + SKL_PROBE_DISCONNECT +}; + +enum skl_probe_direction { + SKL_PROBE_EXTRACT = 0, + SKL_PROBE_INJECT, + SKL_PROBE_INJECT_REEXTRACT +}; + struct skl_dfw_sdw_aggdata { u32 alh_stream_num; u32 channel_mask; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 4bd95f4b2234..1b2a1c996422 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -586,9 +586,9 @@ hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, res->hstream.running = 0; res->hstream.stream = substream; spin_unlock_irq(&hbus->reg_lock); - } - dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", res->hstream.stream_tag, res->hstream.index); + } return res; } EXPORT_SYMBOL_GPL(hdac_ext_host_stream_compr_assign); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index d3b1e22ac050..3c0c7c353566 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -378,7 +378,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) } else if (csubstream) { cruntime = csubstream->runtime; dma_buffer_p = csubstream->runtime->dma_buffer_p; - } + } else + return -EINVAL; /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); @@ -453,27 +454,29 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, struct snd_pcm_runtime *runtime = NULL; struct snd_compr_runtime *cruntime = NULL; int err; + unsigned int no_period_wakeup; if (substream) { runtime = substream->runtime; bufsize = snd_pcm_lib_buffer_bytes(substream); period_bytes = snd_pcm_lib_period_bytes(substream); + no_period_wakeup = runtime->no_period_wakeup; } else if (csubstream) { cruntime = csubstream->runtime; bufsize = cruntime->buffer_size; period_bytes = cruntime->fragment_size; + no_period_wakeup = 0; } else return -EINVAL; if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || format_val != azx_dev->format_val || - runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + no_period_wakeup != azx_dev->no_period_wakeup) { azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - if (substream) - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + azx_dev->no_period_wakeup = no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 831218ee43c8..9a3ddb969d4e 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-skl-objs := skl.o skl-sdw-pcm.o skl-pcm.o skl-nhlt.o skl-messages.o \ -skl-topology.o +skl-topology.o skl-compress.o skl-probe.o ifdef CONFIG_DEBUG_FS snd-soc-skl-objs += skl-debug.o diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c new file mode 100644 index 000000000000..c8b26e80b974 --- /dev/null +++ b/sound/soc/intel/skylake/skl-compress.c @@ -0,0 +1,132 @@ + +/* + * skl-compress.c -ASoC HDA Platform driver file implementing compress functionality + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Divya Prakash + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include "skl.h" +inline +struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) +{ + return stream->runtime->private_data; +} + +struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct hdac_bus *bus = hstream->bus; + + return hbus_to_ebus(bus); +} + +void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, + struct snd_dma_buffer *bufp, size_t size) +{ + struct snd_compr_runtime *runtime = substream->runtime; + + if (bufp) { + runtime->dma_buffer_p = bufp; + runtime->dma_area = bufp->area; + runtime->dma_addr = bufp->addr; + runtime->dma_bytes = size; + } else { + runtime->dma_buffer_p = NULL; + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } +} + +int skl_compr_malloc_pages(struct snd_compr_stream *substream, + struct hdac_ext_bus *ebus, size_t size) +{ + struct snd_compr_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + struct skl *skl = ebus_to_skl(ebus); + + runtime = substream->runtime; + + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + substream->dma_buffer.dev.dev = snd_dma_pci_data(skl->pci); + dmab->dev = substream->dma_buffer.dev; + if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, + substream->dma_buffer.dev.dev, + size, dmab) < 0) { + dev_err(ebus_to_hbus(ebus)->dev, + "Error in snd_dma_alloc_pages\n"); + kfree(dmab); + return -ENOMEM; + } + skl_set_compr_runtime_buffer(substream, dmab, size); + + return 1; +} + +int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + size_t size) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + int ret; + + hdac_stream(stream)->bufsize = 0; + hdac_stream(stream)->period_bytes = 0; + hdac_stream(stream)->format_val = 0; + + ret = skl_compr_malloc_pages(substream, ebus, size); + if (ret < 0) + return ret; + ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + + return ret; +} + +int skl_compr_free_pages(struct snd_compr_stream *substream) +{ + struct snd_compr_runtime *runtime; + + runtime = substream->runtime; + if (runtime->dma_area == NULL) + return 0; + + if (runtime->dma_buffer_p != &substream->dma_buffer) { + /* it's a newly allocated buffer. release it now. */ + snd_dma_free_pages(runtime->dma_buffer_p); + kfree(runtime->dma_buffer_p); + } + + skl_set_compr_runtime_buffer(substream, NULL, 0); + return 0; +} + +int skl_substream_free_compr_pages(struct hdac_bus *bus, + struct snd_compr_stream *substream) +{ + bus->io_ops->mark_pages_uc((substream)->runtime->dma_buffer_p, false); + + return skl_compr_free_pages(substream); +} diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h new file mode 100644 index 000000000000..9fcf6c38f5b8 --- /dev/null +++ b/sound/soc/intel/skylake/skl-compress.h @@ -0,0 +1,35 @@ + +/* + * skl-compress.h - Skylake compress header file + * + * Copyright (C) 2015-16 Intel Corp + * Author: Divya Prakash + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +inline +struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); +struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); +void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, + struct snd_dma_buffer *bufp, size_t size); +int skl_compr_malloc_pages(struct snd_compr_stream *substream, + struct hdac_ext_bus *ebus, size_t size); +int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + size_t size); +int skl_compr_free_pages(struct snd_compr_stream *substream); +int skl_substream_free_compr_pages(struct hdac_bus *bus, + struct snd_compr_stream *substream); + diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 4f88595815a8..fa8ef710900c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -787,6 +787,30 @@ static void skl_set_copier_format(struct skl_sst *ctx, skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig); } +static void skl_setup_probe_gateway_cfg(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_probe_cfg *probe_cfg) +{ + union skl_connector_node_id node_id = {0}; + struct skl_probe_config *pconfig = &ctx->probe_config; + + node_id.node.dma_type = pconfig->edma_type; + node_id.node.vindex = pconfig->edma_id; + probe_cfg->prb_cfg.dma_buffer_size = pconfig->edma_buffsize; + + memcpy(&(probe_cfg->prb_cfg.node_id), &node_id, sizeof(u32)); +} + +static void skl_set_probe_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_probe_cfg *probe_mconfig) +{ + struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)probe_mconfig; + + skl_set_base_module_format(ctx, mconfig, base_cfg); + skl_setup_probe_gateway_cfg(ctx, mconfig, probe_mconfig); +} + /* * Algo module are DSP pre processing modules. Algo module take base module * configuration and params @@ -839,6 +863,9 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, param_size += mconfig->formats_config.caps_size; return param_size; + case SKL_MODULE_TYPE_PROBE: + return sizeof(struct skl_probe_cfg); + case SKL_MODULE_TYPE_SRCINT: return sizeof(struct skl_src_module_cfg); @@ -893,6 +920,10 @@ static int skl_set_module_format(struct skl_sst *ctx, skl_set_copier_format(ctx, module_config, *param_data); break; + case SKL_MODULE_TYPE_PROBE: + skl_set_probe_format(ctx, module_config, *param_data); + break; + case SKL_MODULE_TYPE_SRCINT: skl_set_src_format(ctx, module_config, *param_data); break; @@ -1060,6 +1091,70 @@ int skl_init_module(struct skl_sst *ctx, return ret; } +int skl_init_probe_module(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) +{ + u16 module_config_size = 0; + void *param_data = NULL; + int ret; + struct skl_ipc_init_instance_msg msg; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + + ret = skl_set_module_format(ctx, mconfig, + &module_config_size, ¶m_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret); + return ret; + } + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = -1; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + msg.domain = mconfig->domain; + + ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret); + kfree(param_data); + return ret; + } + mconfig->m_state = SKL_MODULE_INIT_DONE; + kfree(param_data); + return ret; +} + +int skl_uninit_probe_module(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) +{ + u16 module_config_size = 0; + int ret; + struct skl_ipc_init_instance_msg msg; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = -1; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + msg.domain = mconfig->domain; + + ret = skl_ipc_delete_instance(&ctx->ipc, &msg); + if (ret < 0) { + dev_err(ctx->dev, "Failed to delete instance ret=%d\n", ret); + return ret; + } + mconfig->m_state = SKL_MODULE_UNINIT; + + return ret; +} + static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module) { @@ -1072,6 +1167,34 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg src_module->m_state, dst_module->m_state); } +int skl_disconnect_probe_point(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w) +{ + struct skl_ipc_large_config_msg msg; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_module_cfg *mcfg; + int probe_point[8] = {0}; + int n = 0, i; + int no_of_extractor = pconfig->no_extractor; + + dev_dbg(ctx->dev, "Disconnecting probe\n"); + mcfg = w->priv; + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.large_param_id = SKL_PROBE_DISCONNECT; + + for (i = 0; i < no_of_extractor; i++) { + if (pconfig->eprobe[i].set) { + probe_point[n] = pconfig->eprobe[i].id; + pconfig->eprobe[i].set = -1; + n++; + } + } + + msg.param_data_size = n * sizeof(u32); + return skl_ipc_set_large_config(&ctx->ipc, &msg, + probe_point); +} /* * On module freeup, we need to unbind the module with modules * it is already bind. diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 388397d70be4..65a110664ff6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -30,6 +30,7 @@ #include "skl-sst-ipc.h" #include "skl-sdw-pcm.h" #include "skl-fwlog.h" +#include "skl-probe.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -826,13 +827,15 @@ static int skl_trace_compr_copy(struct snd_compr_stream *stream, char __user *dest, size_t count) { struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sst_dsp *sst = skl_sst->dsp; int core = skl_get_compr_core(stream); if (skl_is_logging_core(core)) return skl_dsp_copy_log_user(sst, core, dest, count); else - return 0; + return skl_probe_compr_copy(stream, dest, count, cpu_dai); } static int skl_trace_compr_free(struct snd_compr_stream *stream, @@ -859,6 +862,15 @@ static struct snd_compr_ops skl_platform_compr_ops = { .copy = skl_trace_compr_copy, }; +static struct snd_soc_cdai_ops skl_probe_compr_ops = { + .startup = skl_probe_compr_open, + .shutdown = skl_probe_compr_close, + .trigger = skl_probe_compr_trigger, + .ack = skl_probe_compr_ack, + .pointer = skl_probe_compr_tstamp, + .set_params = skl_probe_compr_set_params, +}; + static struct snd_soc_cdai_ops skl_trace_compr_ops = { .shutdown = skl_trace_compr_free, .pointer = skl_trace_compr_tstamp, @@ -1010,6 +1022,24 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .sig_bits = 32, }, }, +{ + .name = "Compress Probe0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .playback = { + .stream_name = "Probe Playback", + .channels_min = HDA_MONO, + }, +}, +{ + .name = "Compress Probe1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .capture = { + .stream_name = "Probe Capture", + .channels_min = HDA_MONO, + }, +}, { .name = "LowLatency Pin", .ops = &skl_pcm_dai_ops, @@ -1692,6 +1722,29 @@ static int skl_populate_modules(struct skl *skl) return ret; } +static int skl_get_probe_widget(struct snd_soc_component *component, + struct skl *skl) +{ + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, &component->card->widgets, list) { + if (is_skl_dsp_widget_type(w, skl->skl_sst->dev) && + (strstr(w->name, "probe") != NULL)) { + pconfig->w = w; + + dev_dbg(component->dev, "widget type=%d name=%s\n", + w->id, w->name); + break; + } + } + + pconfig->probe_count = 0; + pconfig->no_injector = 6; + pconfig->no_extractor = 8; + + return 0; +} static int skl_platform_soc_probe(struct snd_soc_component *component) { @@ -1746,6 +1799,8 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) skl->cfg.astate_cfg->count, skl->cfg.astate_cfg); } + + skl_get_probe_widget(component, skl); } pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c new file mode 100644 index 000000000000..7c206ec1b6a7 --- /dev/null +++ b/sound/soc/intel/skylake/skl-probe.c @@ -0,0 +1,324 @@ +/* + * skl-probe.c -ASoC HDA Platform driver file implementing probe functionality + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Divya Prakash + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include "skl.h" +#include "skl-topology.h" +#include "skl-sst-ipc.h" +#include "skl-compress.h" + +#define USE_SPIB 0 + +static int set_injector_stream(struct hdac_ext_stream *stream, + struct snd_soc_dai *dai) +{ + /* + * In the case of injector probe since there can be multiple + * streams, we derive the injector stream number from the dai + * that is opened. + */ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int i; + + if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + pconfig->iprobe[i].stream = stream; + pconfig->iprobe[i].dma_id = + hdac_stream(stream)->stream_tag - 1; + } + return 0; +} + +int skl_probe_compr_open(struct snd_compr_stream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = NULL; + struct snd_compr_runtime *runtime = substream->runtime; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + + dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); + + if (!pconfig->probe_count) { + /*TODO: Configuring the right DMA buffer size*/ + pconfig->edma_buffsize = 832; + pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; + pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, + substream, + SND_COMPRESS_CAPTURE); + pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; + } + + if (substream->direction == SND_COMPRESS_PLAYBACK) { + stream = hdac_ext_host_stream_compr_assign(ebus, substream, + SND_COMPRESS_PLAYBACK); + set_injector_stream(stream, dai); + runtime->private_data = stream; + + } else if (substream->direction == SND_COMPRESS_CAPTURE) { + stream = pconfig->estream; + runtime->private_data = pconfig->estream; + } + + if (stream == NULL) { + dev_err(dai->dev, "stream = NULL\n"); + return -EBUSY; + } + + hdac_stream(stream)->curr_pos = 0; + + return 0; +} + +int skl_probe_compr_set_params(struct snd_compr_stream *substream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct snd_compr_runtime *runtime = substream->runtime; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct skl_module_cfg *mconfig = pconfig->w->priv; + int ret, dma_id; + unsigned int format_val = 0; + int err; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = skl_substream_alloc_compr_pages(ebus, substream, + runtime->fragments*runtime->fragment_size); + if (ret < 0) + return ret; + + dma_id = hdac_stream(stream)->stream_tag - 1; + dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + + snd_hdac_stream_reset(hdac_stream(stream)); + + err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); + if (err < 0) + return err; + + err = snd_hdac_stream_setup(hdac_stream(stream)); + if (err < 0) { + dev_err(dai->dev, "snd_hdac_stream_setup err = %d\n", err); + return err; + } + + hdac_stream(stream)->prepared = 1; + + /* Initialize probe module only the first time */ + if (!pconfig->probe_count) { + + ret = skl_init_probe_module(skl->skl_sst, mconfig); + if (ret < 0) + return ret; + } + + if (substream->direction == SND_COMPRESS_PLAYBACK) + skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); + + skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + pconfig->probe_count++; + +#if USE_SPIB + snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); +#endif + return 0; +} + +int skl_probe_compr_close(struct snd_compr_stream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); +#if USE_SPIB + snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); +#endif + + if (!--pconfig->probe_count) { + skl_disconnect_probe_point(skl->skl_sst, pconfig->w); + ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); + if (ret < 0) + return ret; + } + + snd_hdac_stream_cleanup(hdac_stream(stream)); + hdac_stream(stream)->prepared = 0; + + skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + + snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); + + return 0; +} + +int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u64 new_spib_pos; + struct snd_compr_runtime *runtime = substream->runtime; + u64 spib_pos = div64_u64(runtime->total_bytes_available, + runtime->buffer_size); + + spib_pos = runtime->total_bytes_available - + (spib_pos * runtime->buffer_size); + /*SPIB position is a wrap around counter that indicates + the position relative to the buffer start address*/ + new_spib_pos = (spib_pos + bytes) % runtime->buffer_size; + + if (!bus->spbcap) { + dev_err(dai->dev, "Address of SPB capability is NULL"); + return -EINVAL; + } +#if USE_SPIB + writel(new_spib_pos, stream->spib_addr); +#endif + return 0; +} + +int skl_probe_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *hstream = get_hdac_ext_compr_stream(stream); + + tstamp->copied_total = hstream->hstream.curr_pos; + + return 0; + +} + +int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, + size_t count, struct snd_soc_dai *dai) +{ + int offset = 0, availcount = 0, retval = 0, copy; + void *dstn; + + if (stream->direction == SND_COMPRESS_CAPTURE) { + offset = stream->runtime->total_bytes_transferred % + stream->runtime->buffer_size; + dstn = stream->runtime->dma_area + offset; + availcount = (stream->runtime->buffer_size - offset); + if (count > availcount) { + + retval = copy_to_user(buf, dstn, availcount); + retval += copy_to_user(buf + availcount, + stream->runtime->dma_area, + count - availcount); + } else + retval = copy_to_user(buf, stream->runtime->dma_area + + offset, count); + + if (!retval) + retval = count; + else + retval = count - retval; + + } else if (stream->direction == SND_COMPRESS_PLAYBACK) { + + offset = stream->runtime->total_bytes_available % + stream->runtime->buffer_size; + dstn = stream->runtime->dma_area + offset; + + if (count < stream->runtime->buffer_size - offset) + retval = copy_from_user(dstn, buf, count); + else { + copy = stream->runtime->buffer_size - offset; + retval = copy_from_user(dstn, buf, copy); + retval += copy_from_user(stream->runtime->dma_area, + buf + copy, count - copy); + } + if (!retval) + retval = count; + else + retval = count - retval; + } + +#if USE_SPIB + spib_pos = (offset + retval)%stream->runtime->dma_bytes; + snd_hdac_ext_stream_set_spib(ebus, estream, spib_pos); +#endif + + return retval; + +} + +int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = get_bus_compr_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + struct hdac_stream *hstr; + int start; + unsigned long cookie; + + stream = get_hdac_ext_compr_stream(substream); + hstr = hdac_stream(stream); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + if (start) + snd_hdac_stream_start(hdac_stream(stream), true); + else + snd_hdac_stream_stop(hdac_stream(stream)); + + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-probe.h b/sound/soc/intel/skylake/skl-probe.h new file mode 100644 index 000000000000..a7b8f34712a2 --- /dev/null +++ b/sound/soc/intel/skylake/skl-probe.h @@ -0,0 +1,37 @@ + +/* + * skl-probe.h - Skylake probe header file + * + * Copyright (C) 2015-16 Intel Corp + * Author: Divya Prakash + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +int skl_probe_compr_open(struct snd_compr_stream *substream, + struct snd_soc_dai *dai); + +int skl_probe_compr_set_params(struct snd_compr_stream *substream, + struct snd_compr_params *params, struct snd_soc_dai *dai); + +int skl_probe_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai); +int skl_probe_compr_close(struct snd_compr_stream *substream, + struct snd_soc_dai *dai); +int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, + struct snd_soc_dai *dai); +int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, + size_t count, struct snd_soc_dai *dai); +int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, + struct snd_soc_dai *dai); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index eb030dfb88ca..0437e4cf1261 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -18,11 +18,15 @@ #include #include "../common/sst-ipc.h" +#include "skl-sst-dsp.h" struct sst_dsp; struct skl_sst; struct sst_generic_ipc; +#define NO_OF_INJECTOR 6 +#define NO_OF_EXTRACTOR 8 + enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, PPL_UNINITIALIZED = 1, @@ -75,6 +79,32 @@ struct skl_lib_info { const struct firmware *fw; }; +struct injector_data { + int set; + int id; + struct hdac_ext_stream *stream; + int dma_id; + int dma_buf_size; +}; + +struct extractor_data { + int set; + int id; +}; + +struct skl_probe_config { + struct snd_soc_dapm_widget *w; + int probe_count; + int edma_id; + int edma_type; + int edma_buffsize; + int no_extractor; + int no_injector; + struct hdac_ext_stream *estream; + struct injector_data iprobe[NO_OF_INJECTOR]; + struct extractor_data eprobe[NO_OF_EXTRACTOR]; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -126,6 +156,7 @@ struct skl_sst { int num_sdw_controllers; /* Array of sdw masters */ struct sdw_master *mstr; + struct skl_probe_config probe_config; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 35c0ec938a18..1d02c88da5e4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -96,7 +96,7 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type */ -static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, +int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, struct device *dev) { if (w->dapm->dev != dev) @@ -479,12 +479,111 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, skl_dump_mconfig(ctx, m_cfg); } +int skl_get_probe_index(struct snd_soc_dai *dai, + struct skl_probe_config *pconfig) +{ + int i, ret = -1; + char pos[4]; + + for (i = 0; i < pconfig->no_injector; i++) { + snprintf(pos, 4, "%d", i); + if (strstr(dai->name, pos)) + return i; + } + return ret; +} + +int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, struct snd_soc_dai *dai) +{ + int i, ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_attach_probe_dma ad; + struct skl_probe_config *pconfig = &ctx->probe_config; + + if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; + ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + ad.node_id.node.rsvd = 0; + ad.dma_buff_size = 1536;/* TODO:Configure based on calculation*/ + } + + ret = skl_set_module_params(ctx, (u32 *)&ad, + sizeof(struct skl_attach_probe_dma), 1, mconfig); + return ret; + +} + +int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int direction, + struct snd_soc_dai *dai) +{ + int i, ret = 0, n = 0; + struct skl_module_cfg *mconfig = w->priv; + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_probe_data *bc; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct probe_pt_param prb_pt_param[8] = {{0}}; + + if (direction == SND_COMPRESS_PLAYBACK) { + + /* only one injector point can be set at a time*/ + n = skl_get_probe_index(dai, pconfig); + k = &w->kcontrol_news[pconfig->no_extractor + n]; + + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_probe_data *)sb->dobj.private; + pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", + bc->is_ext_inj, bc->params, bc->is_connect); + if (!(bc->is_ext_inj == SKL_PROBE_INJECT || + bc->is_ext_inj == SKL_PROBE_INJECT_REEXTRACT)) + return -EINVAL; + + prb_pt_param[0].params = (int)bc->params; + prb_pt_param[0].connection = bc->is_ext_inj; + prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; + ret = skl_set_module_params(ctx, (void *)prb_pt_param, sizeof(struct probe_pt_param), + bc->is_connect, mconfig); + } + + } else if (direction == SND_COMPRESS_CAPTURE) { + + /*multiple extractor points can be set simultaneously*/ + for (i = 0; i < pconfig->no_extractor; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_probe_data *)sb->dobj.private; + + pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", + bc->is_ext_inj, bc->params, bc->is_connect); + if (bc->is_ext_inj == SKL_PROBE_EXTRACT && + pconfig->eprobe[i].set == 1) { + pr_debug("Retrieving the exractor params \n"); + prb_pt_param[n].params = (int)bc->params; + prb_pt_param[n].connection = bc->is_ext_inj; + prb_pt_param[n].node_id = -1; + n++; + } + } + } + + if (n > 0) + ret = skl_set_module_params(ctx, (void *)prb_pt_param, n * sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + + } + return ret; +} + /* * some modules can have multiple params set from user control and * need to be set after module is initialized. If set_param flag is * set module params will be done after module is initialised. */ -static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, +int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, struct skl_sst *ctx) { int i, ret; @@ -1691,6 +1790,121 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, memcpy(pipe->p_params, params, sizeof(*params)); } } +static int skl_cache_probe_param(struct snd_kcontrol *kctl, + struct skl_probe_data *ap, struct skl_sst *ctx) +{ + struct skl_probe_config *pconfig = &ctx->probe_config; + union skl_connector_node_id node_id = {-1}; + int index = -1, i; + char buf[20], pos[10]; + + if (ap->is_ext_inj == SKL_PROBE_EXTRACT) { + /* From the control ID get the extractor index */ + for (i = 0; i < pconfig->no_extractor; i++) { + strcpy(buf, "Extractor"); + snprintf(pos, 4, "%d", i); + if (strstr(kctl->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + pr_debug("Setting extractor probe index %d\n", index); + memcpy(&ap->node_id, &node_id, sizeof(u32)); + pconfig->eprobe[index].id = ap->params; + if (ap->is_connect == SKL_PROBE_CONNECT) + pconfig->eprobe[index].set = 1; + else if (ap->is_connect == SKL_PROBE_DISCONNECT) + pconfig->eprobe[index].set = -1; + + } else { + /* From the control ID get the injector index */ + for (i = 0; i < pconfig->no_injector; i++) { + strcpy(buf, "Injector"); + snprintf(pos, 4, "%d", i); + if (strstr(kctl->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + pconfig->iprobe[index].id = ap->params; + node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + node_id.node.vindex = pconfig->iprobe[index].dma_id; + memcpy(&ap->node_id, &node_id, sizeof(u32)); + if (ap->is_connect == SKL_PROBE_CONNECT) + pconfig->iprobe[index].set = 1; + else if (ap->is_connect == SKL_PROBE_DISCONNECT) + pconfig->iprobe[index].set = -1; + } + return 0; +} + +static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, + const unsigned int __user *data, unsigned int size) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct probe_pt_param connect_point; + int disconnect_point; + void *offset; + + dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); + dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); + + if (data) { + offset = (unsigned char *)data; + offset += 2 * sizeof(u32); /* To skip TLV heeader */ + if (copy_from_user(&ap->is_connect, + offset, sizeof(ap->is_connect))) + return -EIO; + + offset += sizeof(ap->is_connect); + if (copy_from_user(&ap->is_ext_inj, + offset, sizeof(ap->is_ext_inj))) + return -EIO; + + offset += sizeof(ap->is_ext_inj); + if (copy_from_user(&ap->params, + offset, sizeof(ap->params))) + return -EIO; + + dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", + ap->is_connect, ap->is_ext_inj, ap->params); + + skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + + if (pconfig->probe_count) { + /* In the case of extraction, additional probe points can be set when + * the stream is in progress and the driver can immediately send the + * connect IPC. But in the case of injector, for each probe point + * connection a new stream with the DAI number corresponding to that + * control has to be opened. Hence below check ensures that the + * connect IPC is sent only in case of extractor. + */ + if ((ap->is_connect == SKL_PROBE_CONNECT) + && (ap->is_ext_inj == SKL_PROBE_EXTRACT)) { + + memcpy(&connect_point.params, &ap->params, sizeof(u32)); + connect_point.connection = ap->is_ext_inj; + memcpy(&connect_point.node_id, (&ap->node_id), sizeof(u32)); + return skl_set_module_params(skl->skl_sst, (void *)&connect_point, + sizeof(struct probe_pt_param), ap->is_connect, mconfig); + + } else if (ap->is_connect == SKL_PROBE_DISCONNECT) { + + disconnect_point = (int)ap->params; + return skl_set_module_params(skl->skl_sst, (void *)&disconnect_point, + sizeof(disconnect_point), ap->is_connect, mconfig); + } + } + } + return 0; +} /* * The FE params are passed by hw_params of the DAI. @@ -2059,6 +2273,8 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { {SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get, skl_tplg_tlv_control_set}, + {SKL_CONTROL_TYPE_BYTE_PROBE, skl_tplg_tlv_control_get, + skl_tplg_tlv_probe_set}, }; static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 5998e6926df3..94152704e22e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -96,6 +96,11 @@ enum skl_widget_type { SKL_WIDGET_PGA = 3, SKL_WIDGET_MUX = 4 }; +struct probe_pt_param { + u32 params; + u32 connection; + u32 node_id; +}; struct skl_audio_data_format { enum skl_s_freq s_freq; @@ -117,6 +122,16 @@ struct skl_base_cfg { struct skl_audio_data_format audio_fmt; }; +struct skl_probe_gtw_cfg { + u32 node_id; + u32 dma_buffer_size; +} __packed; + +struct skl_probe_cfg { + struct skl_base_cfg base_cfg; + struct skl_probe_gtw_cfg prb_cfg; +} __packed; + struct skl_cpr_gtw_cfg { u32 node_id; u32 dma_buffer_size; @@ -448,6 +463,17 @@ struct skl_algo_data { char *params; }; +struct skl_probe_data { + u8 is_connect; + u32 is_ext_inj; + u32 params; + u32 node_id; +} __packed; + +struct skl_attach_probe_dma { + union skl_connector_node_id node_id; + u32 dma_buff_size; +} __packed; struct skl_pipeline { struct skl_pipe *pipe; struct list_head node; @@ -479,6 +505,7 @@ static inline struct skl *get_skl_ctx(struct device *dev) return bus_to_skl(bus); } +struct skl_probe_config; int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, @@ -509,12 +536,28 @@ int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); +int skl_init_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); + +int skl_uninit_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); + +int skl_get_probe_index(struct snd_soc_dai *dai, + struct skl_probe_config *pconfig); + +int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, struct snd_soc_dai *dai); +int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int direction, + struct snd_soc_dai *dai); +int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx); + int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); - +int skl_disconnect_probe_point(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w); int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, @@ -522,6 +565,9 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream); + +int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, + struct device *dev); enum skl_bitdepth skl_get_bit_depth(int params); int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2d0efe8233a4..feb4ab5fc9b4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -185,10 +186,30 @@ void skl_update_d0i3c(struct device *dev, bool enable) snd_hdac_chip_readb(bus, VS_D0I3C)); } +static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) +{ + int pos, prev_pos, no_of_bytes; + + prev_pos = hstr->curr_pos % hstr->stream->runtime->buffer_size; + pos = snd_hdac_stream_get_pos_posbuf(hstr); + + if (pos < prev_pos) + no_of_bytes = (hstr->stream->runtime->buffer_size - prev_pos) + pos; + else + no_of_bytes = pos - prev_pos; + + hstr->curr_pos += no_of_bytes; +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { - snd_pcm_period_elapsed(hstr->substream); + if (hstr->substream) + snd_pcm_period_elapsed(hstr->substream); + else if (hstr->stream) { + skl_get_total_bytes_transferred(hstr); + snd_compr_fragment_elapsed(hstr->stream); + } } static irqreturn_t skl_interrupt(int irq, void *dev_id) From c7bf90f7bb99e14cbe624c245498172086a5f12a Mon Sep 17 00:00:00 2001 From: G Kranthi Date: Tue, 10 May 2016 11:11:09 +0530 Subject: [PATCH 0076/1276] ASoC: Intel: Skylake: KW fixes for probe feature This patch will add fixes for below klocwork errors, 1. Array 'eprobe' of size 8 may use index value(s) -1. 2. Array 'iprobe' of size 6 may use index value(s) -1. 3. Pointer 'pconfig->estream' returned from call to function 'hdac_ext_host_stream_compr_assign' at line 68 may be NULL and will be dereferenced at line 71. Change-Id: I407d9b2758addfd78508f96378a00d583c5d8110 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 8 +++++++- sound/soc/intel/skylake/skl-topology.c | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7c206ec1b6a7..7c6e3779c213 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -68,6 +68,9 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, substream, SND_COMPRESS_CAPTURE); + if (!pconfig->estream) + return -EINVAL; + pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; } @@ -146,7 +149,10 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, if (substream->direction == SND_COMPRESS_PLAYBACK) skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); - skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + if (ret < 0) + return -EINVAL; + pconfig->probe_count++; #if USE_SPIB diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1d02c88da5e4..3ef44ba263c4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -530,6 +530,9 @@ int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, /* only one injector point can be set at a time*/ n = skl_get_probe_index(dai, pconfig); + if (n < 0) + return -EINVAL; + k = &w->kcontrol_news[pconfig->no_extractor + n]; if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { @@ -1808,6 +1811,10 @@ static int skl_cache_probe_param(struct snd_kcontrol *kctl, break; } } + + if (index < 0) + return -EINVAL; + pr_debug("Setting extractor probe index %d\n", index); memcpy(&ap->node_id, &node_id, sizeof(u32)); pconfig->eprobe[index].id = ap->params; @@ -1826,6 +1833,10 @@ static int skl_cache_probe_param(struct snd_kcontrol *kctl, break; } } + + if (index < 0) + return -EINVAL; + pconfig->iprobe[index].id = ap->params; node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; node_id.node.vindex = pconfig->iprobe[index].dma_id; @@ -1852,6 +1863,7 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, struct probe_pt_param connect_point; int disconnect_point; void *offset; + int ret; dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); @@ -1876,7 +1888,9 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", ap->is_connect, ap->is_ext_inj, ap->params); - skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + ret = skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + if (ret < 0) + return -EINVAL; if (pconfig->probe_count) { /* In the case of extraction, additional probe points can be set when From e7e078a594cea31d84bb8d55b2cf6678a070f29f Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 14 Jun 2016 18:19:44 +0530 Subject: [PATCH 0077/1276] ASoC: Intel: Skylake: Probe - Start DMA before setting probe params FW starts probe module soon after its params are set. But to avoid xruns from FW point of view, DMA has to be started before this. So, reorder the sequence as below so that there is no initial packet loss in the captured data: 1. Call skl_probe_compr_trigger(START) which starts the DMA. 2. Call skl_tplg_set_probe_params() which tells the DSP to start the probe stream. Change-Id: I0a23ded05ca60ab0e35e53c784681ebf502f138a Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7c6e3779c213..7d2379806daf 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -149,10 +149,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, if (substream->direction == SND_COMPRESS_PLAYBACK) skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); - ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); - if (ret < 0) - return -EINVAL; - pconfig->probe_count++; #if USE_SPIB @@ -293,6 +289,9 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, struct hdac_stream *hstr; int start; unsigned long cookie; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int ret; stream = get_hdac_ext_compr_stream(substream); hstr = hdac_stream(stream); @@ -326,5 +325,14 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, spin_unlock_irqrestore(&bus->reg_lock, cookie); + if (start) { + /* FW starts probe module soon after its params are set. + * So to avoid xruns, start DMA first and then set probe params. + */ + ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + if (ret < 0) + return -EINVAL; + } + return 0; } From fd2f055c1018bf19780ca6c24e1f30b16cc33a3f Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Tue, 14 Jun 2016 10:39:48 +0530 Subject: [PATCH 0078/1276] ASoC: Intel: Skylake: Probe-Limit the bytes to copy If userspace happens to issue a copy with count > ring buffer size, limit the count to the allocated ring buffer size to avoid out of bound access into the buffer. Change-Id: I7acbfb64bda299237a9d56bbac4a022d36b28bfd Signed-off-by: Jayachandran B Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7d2379806daf..507513eabbd6 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -230,6 +230,12 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, { int offset = 0, availcount = 0, retval = 0, copy; void *dstn; + /* + * If userspace happens to issue a copy with count > ring buffer size, + * limit the count to the allocated ring buffer size. + */ + if (count > stream->runtime->buffer_size) + count = stream->runtime->buffer_size; if (stream->direction == SND_COMPRESS_CAPTURE) { offset = stream->runtime->total_bytes_transferred % From 4c29f64dc0a83f2bec7fbedc8eaafada61c48530 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 14 Jun 2016 16:37:20 +0530 Subject: [PATCH 0079/1276] ASoC: Intel: Skylake:Probe-Increase the DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: I4b902f8c078a9a3c2e19c82b0ebeaf99dd99c2b1 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 507513eabbd6..9c4d96ecc762 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -30,6 +30,13 @@ #define USE_SPIB 0 +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 + static int set_injector_stream(struct hdac_ext_stream *stream, struct snd_soc_dai *dai) { @@ -62,8 +69,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); if (!pconfig->probe_count) { - /*TODO: Configuring the right DMA buffer size*/ - pconfig->edma_buffsize = 832; + pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, substream, From 4b48302dd5d52af0cf36eb668eeb328d463c59ac Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 12 Jul 2016 10:57:10 +0530 Subject: [PATCH 0080/1276] ASoC: Intel: Skylake: Probe-Increase Injector DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: Iaf7c3229c6217c10c0c6871c358e9df74b552414 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha --- sound/soc/intel/skylake/skl-topology.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3ef44ba263c4..bb8ce1d37522 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -37,6 +37,12 @@ #define SKL_FMT_FIXUP_MASK (1 << 2) #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4) +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 static const int mic_mono_list[] = { 0, 1, 2, 3, @@ -505,7 +511,7 @@ int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; - ad.dma_buff_size = 1536;/* TODO:Configure based on calculation*/ + ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; } ret = skl_set_module_params(ctx, (u32 *)&ad, From e388f9e1fbdf42469675d0d4fc571a7aa40741e4 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 12 Jul 2016 10:57:10 +0530 Subject: [PATCH 0081/1276] ASoC: Intel: Skylake: Probe-Increase Injector DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: Iaf7c3229c6217c10c0c6871c358e9df74b552414 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha --- sound/soc/intel/skylake/skl-topology.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index bb8ce1d37522..2e4a894f58a4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -98,6 +98,13 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) } } +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type From b6314bdffaf5817f880686151121d5a5e99fb83e Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:25:37 +0530 Subject: [PATCH 0082/1276] Soundwire squashed commits [2] SoundWire: Optimizations in BW calculation and runtime ops Includes: - Removed multiple ifdefs from code. - Clock frequency divider changes. - clock divider added for clock scaling - cleanup in row column definition - block offset adjusted for loopback test Change-Id: I6e9344d7c44681d696ffc5baa61f33ae5f3ac436 Signed-off-by: Sanyog Kale SoundWire: Underrun/Overrun fix for SDW playback and capture Includes: - Fix for playback & capture not working after underrun/overrun and pause/resume scenario. - Optimization in Master/Slave configuration. Change-Id: Id8e6ebe0083e0b5d8bf255128b43d245bb177bc9 Signed-off-by: Sanyog Kale SoundWire: Optimization in BW calculation and runtime operations. Includes: - Optimizations in APIs - Renaming of APIs - Split of bankswitch function. - Split of calc_bw & calc_bw_dis functions. - Individial APIs for prepare, enable, disable and unprepare operations. Change-Id: I5c72bc451d943ced60d1f40b15ae816a048796a6 Signed-off-by: Sanyog Kale SoundWire: Fix for assigning port capabilities for all the master ports while registering Also adds check for Master and Slave capabilities SoundWire: Fix for assigning port capabilities for all the master ports while registering2 Also adds check for Master and Slave capabilities SoundWire: Port configuration changes for Multiple port support This patch supports multiple port configuration for given stream. Signed-off-by: Ashish Panwar Signed-off-by: Sanyog Kale SoundWire: Multiple port support for Master and Slave ports Includes: - Computes transport parameters for all Master and Slave Ports. - SV codec driver changes to support multi port PCM capture. - Machine driver changes to support multi port PCM capture. - Free up resources for port runtime handle. Change-Id: I18d7247f44a9aff400bc709bd35f968ecfc66eea Signed-off-by: Sanyog Kale Soundwire: Add Interrupt Status SCP Registers Change-Id: I7a037c74861bfcce5b263fac54a07f58cac078e0 Signed-off-by: Guneshwor Singh SoundWire: Add support for getting bus params. Some Slave may require to know bus params at probe to program its registers. Provide API to get current bus params. Signed-off-by: Sanyog Kale SoundWire: Fix the Slave alert handling. 1. Return the proper status to slave for interrupt. 2. Enable to specify scp_interrupt mask register. 3. Re-enable interrupts when slave changes from unattached to attached. 4. Ack only the handled interrupts. Change-Id: If1732460e0c4ca286b8d09f5e212b4834e53b533 Signed-off-by: Sanyog Kale SoundWire: Add deprepare after clock resume. 1. According to SoundWire spec deprepare is required after resuming from clock stop mode0. Add this functionality. 2. According to SoundWire spec deprepare is optionally required after resuming from clock stop mode1. Add this functionality. 3. Add Slave callbacks to call the pre and post clock stop prepeare before doing actual clock stop. Signed-off-by: Sanyog Kale SDW: Remove hardcoding to enable normal capture Remove hardcode for loopback and enable normal capture/playback. Change-Id: If0d16c8d0d0e6409ffe5372002e2bc18b8ba0588 Signed-off-by: Shreyas NC Soundwire: Change clockstop exit sequence for losing ctx When Cavs runtime pm is enabled, the sequence for clockstop exit is also changed. Change-Id: I834aa87c65aa97172f477dced11c3610e412edc2 Signed-off-by: Guneshwor Singh Soundwire: Hard bus reset is not required in resume According to MIPI, bus reset is not required during clockstop exit. So remove bus reset in the clockstop exit sequence. Change-Id: Iea7b3a8030cb683caa97d9648ac873f8000ca072 Signed-off-by: Guneshwor Singh [CNL FPGA] Soundwire: Add #if for frameshape change in CNL RVP This is added for supporting both RVP and FPGA setups. Frameshape is different for both cases, so add #if to distinguish. Change-Id: Ib8ca64c4b0e138e8260392adeba6e27524c438aa Signed-off-by: Guneshwor Singh SoundWire: Bus header file changes for BRA feature This patch includes: - Bus API for supporting BRA feature. - BRA defines as per MIPI 1.1 spec. Signed-off-by: Sanyog Kale SoundWire: Bus implementation for BRA feature This patch includes: - Implementation of bus API sdw_slave_xfer_bra_block used for BRA transfers by SoundWire Slave(s). - Bandwidth allocation for BRA. - Data port 0 prepare/enable/de-prepare/disable ops. Signed-off-by: Sanyog Kale SoundWire: Bus header file changes for CRC8 helper function This patch adds bus CRC8 helper function used in BRA feature to compute CRC8. Signed-off-by: Sanyog Kale SoundWire: Bus CRC8 helper function implementation This patch implements helper function for calculating CRC8 values. Signed-off-by: Sanyog Kale Signed-off-by: Guneshwor Singh SoundWire: Master driver header file changes for BRA feature This patch includes: - Data structure required for BRA operations. - BRA ops definition. - Defines used by Master driver for BRA operations. Signed-off-by: Sanyog Kale SoundWire: Master driver implementation for BRA feature This patch includes: - Implementation for Master API for BRA. - Preparation of TX and RX PDI buffer. - Preparation of BRA packets. - Verification of RX packets. - PDI configuration for BRA. Signed-off-by: Sanyog Kale Soundwire: Fix build regression when PM is disabled Build regression observed when CONFIG_PM and CONFIG_PM_SLEEP is disabled. To fix this #ifdefs are added for functions specific to PM. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d305 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A Soundwire: Add return value to avoid warning Since return type of sdw_transfer_trace_reg, return zero to avoid compiler warning. Signed-off-by: Guneshwor Singh SoundWire: TX and RX Host DMA & Pipeline creation support for BRA Signed-off-by: Sanyog Kale SoundWire: Creates single module for SoundWire bus framework This patch make SoundWire bus framework as single module. Change-Id: I966d42e57a9899d82ad99ec75f879a0b627afa7f Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Nemallapudi, JaikrishnaX Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar SoundWire: Add export symbol for SoundWire Bus BRA API This patch adds export symbol for sdw_slave_xfer_bra_block SoundWire Bus BRA API Change-Id: I8bb8d6b1595c46077bc0914b9e8f3b9d89bcd686 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar Reviewed-by: Nemallapudi, JaikrishnaX SoundWire: Mask gSync pulse to avoid gSync and frame mis-aligment Cadence Master IP supports Multi-Master Mode where the IP can be configured such that its generated Frame boundary is synchronized to the periodically occurring gSync pulses. Certain versions of the IP implementation have a bug whereby if a gSync pulse collides with the register configuration update that brings up the IP into Normal operation (where the IP begins Frame tracking), then the resulting Frame boundary will misalign with the periodic gSync pulses. This patch adds gSync masking logic where gSync pulse is masked before performing register configuration and is un-masked after setting Mcp_ConfigUpdate bit. Due to this the initialization-pending Master IP SoundWire bus clock will start up synchronizing to gSync, leading to bus reset entry, subsequent exit, and 1st Frame generation aligning to gSync. Change-Id: I8e3620244de3f0c0636520db017df4296c7ae5e5 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar Reviewed-by: Koul, Vinod SoundWire: Remove Maxim FPGA support from SoundWire bus Maxim codec FPGA is no more used for SoundWire use case verification, removing related code changes. Change-Id: I7584e7f81922df3f3d168d41ef7192a6449ff044 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar SoundWire: Remove hardcoding for SSP Interval This patch removes hardcoding for setting SSP Interval and sets default value based on the platform configuration. Change-Id: I4cd14a9a2ddda28e4b9d2a8cee931ac5eec88e03 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- drivers/sdw/Kconfig | 1 + drivers/sdw/Makefile | 2 +- drivers/sdw/sdw.c | 1600 +++++++++++--- drivers/sdw/sdw_bwcalc.c | 2858 +++++++++++++------------ drivers/sdw/sdw_cnl.c | 943 +++++++- drivers/sdw/sdw_cnl_priv.h | 40 + drivers/sdw/sdw_priv.h | 50 +- drivers/sdw/sdw_utils.c | 49 + include/linux/sdw/sdw_cnl.h | 29 + include/linux/sdw/sdw_registers.h | 7 +- include/linux/sdw_bus.h | 207 +- sound/soc/codecs/svfpga-sdw.c | 2 +- sound/soc/intel/boards/cnl_svfpga.c | 2 +- sound/soc/intel/skylake/cnl-sst.c | 16 +- sound/soc/intel/skylake/skl-sdw-pcm.c | 77 +- 15 files changed, 4151 insertions(+), 1732 deletions(-) create mode 100644 drivers/sdw/sdw_utils.c diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 90e954c392e0..1b7e2cc2ebc3 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,5 +1,6 @@ menuconfig SDW tristate "SoundWire bus support" + depends on CRC8 help SoundWire interface is typically used for transporting data related to audio functions. diff --git a/drivers/sdw/Makefile b/drivers/sdw/Makefile index 184682a88a1a..e2ba440f4ef2 100644 --- a/drivers/sdw/Makefile +++ b/drivers/sdw/Makefile @@ -1,4 +1,4 @@ -sdw_bus-objs := sdw.o sdw_bwcalc.o +sdw_bus-objs := sdw.o sdw_bwcalc.o sdw_utils.o obj-$(CONFIG_SDW) += sdw_bus.o obj-$(CONFIG_SDW_CNL) += sdw_cnl.o diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 78c8cfd32d4c..aefd25d4e393 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -210,6 +210,29 @@ static int sdw_slv_probe(struct device *dev) return ret; } + +int sdw_slave_get_bus_params(struct sdw_slv *sdw_slv, + struct sdw_bus_params *params) +{ + struct sdw_bus *bus; + struct sdw_master *mstr = sdw_slv->mstr; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + if (!bus) + return -EFAULT; + + params->num_rows = bus->row; + params->num_cols = bus->col; + params->bus_clk_freq = bus->clk_freq >> 1; + params->bank = bus->active_bank; + + return 0; +} +EXPORT_SYMBOL(sdw_slave_get_bus_params); + static int sdw_mstr_remove(struct device *dev) { const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); @@ -373,17 +396,19 @@ static int sdw_pm_resume(struct device *dev) return sdw_legacy_resume(dev); } +#else +#define sdw_pm_suspend NULL +#define sdw_pm_resume NULL +#endif /* CONFIG_PM_SLEEP */ + static const struct dev_pm_ops soundwire_pm = { .suspend = sdw_pm_suspend, .resume = sdw_pm_resume, +#ifdef CONFIG_PM .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, -}; - -#else -#define sdw_pm_suspend NULL -#define sdw_pm_resume NULL #endif +}; struct bus_type sdwint_bus_type = { .name = "soundwire", @@ -404,6 +429,8 @@ static struct static_key sdw_trace_msg = STATIC_KEY_INIT_FALSE; int sdw_transfer_trace_reg(void) { static_key_slow_inc(&sdw_trace_msg); + + return 0; } void sdw_transfer_trace_unreg(void) @@ -835,7 +862,7 @@ int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) EXPORT_SYMBOL_GPL(sdw_slave_transfer); static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, - struct sdw_slave *sdw_slv) + struct sdw_slv *sdw_slv, u8 *status) { int ret = 0; struct sdw_msg rd_msg, wr_msg; @@ -886,8 +913,8 @@ static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, SDW_DP0_INTSTAT_IMPDEF2_MASK | SDW_DP0_INTSTAT_IMPDEF3_MASK; if (rd_msg.buf[0] & impl_def_mask) { - /* TODO: Handle implementation defined mask ready */ wr_msg.buf[0] |= impl_def_mask; + *status = wr_msg.buf[0]; } ret = sdw_slave_transfer(mstr, &wr_msg, 1); if (ret != 1) { @@ -901,15 +928,20 @@ static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, } static int sdw_handle_port_interrupt(struct sdw_master *mstr, - struct sdw_slave *sdw_slv, int port_num) + struct sdw_slv *sdw_slv, int port_num, + u8 *status) { int ret = 0; struct sdw_msg rd_msg, wr_msg; u8 rbuf[1], wbuf[1]; int impl_def_mask = 0; - if (port_num == 0) - ret = sdw_handle_dp0_interrupts(mstr, sdw_slv); +/* + * Handle the Data port0 interrupt separately since the interrupt + * mask and stat register is different than other DPn registers + */ + if (port_num == 0 && sdw_slv->sdw_slv_cap.sdw_dp0_supported) + return sdw_handle_dp0_interrupts(mstr, sdw_slv, status); /* Create message for reading the port interrupts */ wr_msg.ssp_tag = 0; @@ -953,6 +985,7 @@ static int sdw_handle_port_interrupt(struct sdw_master *mstr, if (rd_msg.buf[0] & impl_def_mask) { /* TODO: Handle implementation defined mask ready */ wr_msg.buf[0] |= impl_def_mask; + *status = wr_msg.buf[0]; } /* Clear and Ack the interrupt */ ret = sdw_slave_transfer(mstr, &wr_msg, 1); @@ -972,6 +1005,10 @@ static int sdw_handle_slave_alerts(struct sdw_master *mstr, u8 rbuf[3], wbuf[1]; int i, ret = 0; int cs_port_mask, cs_port_register, cs_port_start, cs_ports; + struct sdw_impl_def_intr_stat *intr_status; + struct sdw_portn_intr_stat *portn_stat; + u8 port_status[15] = {0}; + u8 control_port_stat = 0; /* Read Instat 1, Instat 2 and Instat 3 registers */ @@ -1027,214 +1064,711 @@ static int sdw_handle_slave_alerts(struct sdw_master *mstr, dev_err(&mstr->dev, "Bus clash error detected\n"); wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_BUS_CLASH_MASK; } - /* Handle Port interrupts from Instat_1 registers */ + /* Handle implementation defined mask */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_IMPL_DEF_MASK) { + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_IMPL_DEF_MASK; + control_port_stat = (rd_msg[0].buf[0] & + SDW_SCP_INTSTAT1_IMPL_DEF_MASK); + } + + /* Handle Cascaded Port interrupts from Instat_1 registers */ + + /* Number of port status bits in this register */ cs_ports = 4; + /* Port number starts at in this register */ cs_port_start = 0; + /* Bit mask for the starting port intr status */ cs_port_mask = 0x08; + /* Bit mask for the starting port intr status */ cs_port_register = 0; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + + /* Look for cascaded port interrupts, if found handle port + * interrupts. Do this for all the Int_stat registers. + */ + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } - /* Handle interrupts from instat_2 register */ + + /* + * Handle cascaded interrupts from instat_2 register, + * if no cascaded interrupt from SCP2 cascade move to SCP3 + */ if (!(rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK)) goto handle_instat_3_register; + + cs_ports = 7; cs_port_start = 4; cs_port_mask = 0x1; cs_port_register = 1; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } -handle_instat_3_register: + /* + * Handle cascaded interrupts from instat_2 register, + * if no cascaded interrupt from SCP2 cascade move to impl_def intrs + */ +handle_instat_3_register: if (!(rd_msg[1].buf[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK)) - goto handle_instat_3_register; + goto handle_impl_def_interrupts; + cs_ports = 4; cs_port_start = 11; cs_port_mask = 0x1; cs_port_register = 2; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } - /* Ack the IntStat 1 interrupts */ + +handle_impl_def_interrupts: + + /* + * If slave has not registered for implementation defined + * interrupts, dont read it. + */ + if (!sdw_slv->driver->handle_impl_def_interrupts) + goto ack_interrupts; + + intr_status = kzalloc(sizeof(*intr_status), GFP_KERNEL); + if (!intr_status) + return -ENOMEM; + + portn_stat = kzalloc((sizeof(*portn_stat)) * + sdw_slv->sdw_slv_cap.num_of_sdw_ports, + GFP_KERNEL); + if (!portn_stat) + return -ENOMEM; + + intr_status->portn_stat = portn_stat; + intr_status->control_port_stat = control_port_stat; + + /* Update the implementation defined status to Slave */ + for (i = 1; i < sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + + intr_status->portn_stat[i].status = port_status[i]; + intr_status->portn_stat[i].num = i; + } + + intr_status->port0_stat = port_status[0]; + intr_status->control_port_stat = wr_msg.buf[0]; + + ret = sdw_slv->driver->handle_impl_def_interrupts(sdw_slv, + intr_status); + if (ret) + dev_err(&mstr->dev, "Implementation defined interrupt handling failed\n"); + + kfree(portn_stat); + kfree(intr_status); + +ack_interrupts: + /* Ack the interrupts */ ret = sdw_slave_transfer(mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr->dev, "Register transfer failed\n"); - goto out; } out: - return ret; + return 0; } -static void handle_slave_status(struct kthread_work *work) +int sdw_en_intr(struct sdw_slv *sdw_slv, int port_num, int mask) { - int i, ret = 0; - struct sdw_slv_status *status, *__status__; - struct sdw_bus *bus = - container_of(work, struct sdw_bus, kwork); - struct sdw_master *mstr = bus->mstr; - unsigned long flags; - /* Handle the new attached slaves to the bus. Register new slave - * to the bus. - */ - list_for_each_entry_safe(status, __status__, &bus->status_list, node) { - if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { - ret += sdw_register_slave(mstr); - if (ret) - /* Even if adding new slave fails, we will - * continue. - */ - dev_err(&mstr->dev, "Registering new slave failed\n"); - } - for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { - if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && - mstr->sdw_addr[i].assigned == true) - /* Logical address was assigned to slave, but - * now its down, so mark it as not present - */ - mstr->sdw_addr[i].status = - SDW_SLAVE_STAT_NOT_PRESENT; + struct sdw_msg rd_msg, wr_msg; + u8 buf; + int ret; + struct sdw_master *mstr = sdw_slv->mstr; - else if (status->status[i] == SDW_SLAVE_STAT_ALERT && - mstr->sdw_addr[i].assigned == true) { - /* Handle slave alerts */ - mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; - ret = sdw_handle_slave_alerts(mstr, - mstr->sdw_addr[i].slave); - if (ret) - dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + rd_msg.addr = wr_msg.addr = SDW_DPN_INTMASK + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + /* Create message for enabling the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + /* Create message for reading the interrupts for DP0 interrupts*/ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DPn Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } - } else if (status->status[i] == - SDW_SLAVE_STAT_ATTACHED_OK && - mstr->sdw_addr[i].assigned == true) - mstr->sdw_addr[i].status = - SDW_SLAVE_STAT_ATTACHED_OK; - } - spin_lock_irqsave(&bus->spinlock, flags); - list_del(&status->node); - spin_unlock_irqrestore(&bus->spinlock, flags); - kfree(status); + buf |= mask; + + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_DPN_INTSTAT_TEST_FAIL_MASK; + buf |= SDW_DPN_INTSTAT_PORT_READY_MASK; + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DPn Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; } + return 0; } -static int sdw_register_master(struct sdw_master *mstr) +static int sdw_en_scp_intr(struct sdw_slv *sdw_slv, int mask) { - int ret = 0; - int i; - struct sdw_bus *sdw_bus; + struct sdw_msg rd_msg, wr_msg; + u8 buf = 0; + int ret; + struct sdw_master *mstr = sdw_slv->mstr; + u16 reg_addr; - /* Can't register until after driver model init */ - if (unlikely(WARN_ON(!sdw_bus_type.p))) { - ret = -EAGAIN; - goto bus_init_not_done; - } - /* Sanity checks */ - if (unlikely(mstr->name[0] == '\0')) { - pr_err("sdw-core: Attempt to register an master with no name!\n"); - ret = -EINVAL; - goto mstr_no_name; - } - for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) - mstr->sdw_addr[i].slv_number = i; + reg_addr = SDW_SCP_INTMASK1; - rt_mutex_init(&mstr->bus_lock); - INIT_LIST_HEAD(&mstr->slv_list); - INIT_LIST_HEAD(&mstr->mstr_rt_list); + rd_msg.addr = wr_msg.addr = reg_addr; - sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); - if (!sdw_bus) - goto bus_alloc_failed; - sdw_bus->mstr = mstr; - init_completion(&sdw_bus->async_data.xfer_complete); + /* Create message for reading the interrupt mask */ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "SCP Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } - mutex_lock(&sdw_core.core_lock); - list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); - mutex_unlock(&sdw_core.core_lock); + /* Enable the Slave defined interrupts. */ + buf |= mask; - dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); - mstr->dev.bus = &sdw_bus_type; - mstr->dev.type = &sdw_mstr_type; + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_SCP_INTMASK1_BUS_CLASH_MASK; + buf |= SDW_SCP_INTMASK1_PARITY_MASK; - ret = device_register(&mstr->dev); - if (ret) - goto out_list; - kthread_init_worker(&sdw_bus->kworker); - sdw_bus->status_thread = kthread_run(kthread_worker_fn, - &sdw_bus->kworker, "%s", - dev_name(&mstr->dev)); - if (IS_ERR(sdw_bus->status_thread)) { - dev_err(&mstr->dev, "error: failed to create status message task\n"); - ret = PTR_ERR(sdw_bus->status_thread); - goto task_failed; - } - kthread_init_work(&sdw_bus->kwork, handle_slave_status); - INIT_LIST_HEAD(&sdw_bus->status_list); - spin_lock_init(&sdw_bus->spinlock); - ret = sdw_mstr_bw_init(sdw_bus); - if (ret) { - dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); - goto mstr_bw_init_failed; + /* Create message for enabling the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "SCP Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; } - dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); - return 0; + /* Return if DP0 is not present */ + if (!sdw_slv->sdw_slv_cap.sdw_dp0_supported) + return 0; -mstr_bw_init_failed: -task_failed: - device_unregister(&mstr->dev); -out_list: - mutex_lock(&sdw_core.core_lock); - list_del(&sdw_bus->bus_node); - mutex_unlock(&sdw_core.core_lock); - kfree(sdw_bus); -bus_alloc_failed: -mstr_no_name: -bus_init_not_done: - mutex_lock(&sdw_core.core_lock); - idr_remove(&sdw_core.idr, mstr->nr); - mutex_unlock(&sdw_core.core_lock); - return ret; -} -/** - * sdw_master_update_slv_status: Report the status of slave to the bus driver. - * master calls this function based on the - * interrupt it gets once the slave changes its - * state. - * @mstr: Master handle for which status is reported. - * @status: Array of status of each slave. - */ -int sdw_master_update_slv_status(struct sdw_master *mstr, - struct sdw_status *status) -{ - struct sdw_bus *bus = NULL; - struct sdw_slv_status *slv_status; - unsigned long flags; + reg_addr = SDW_DP0_INTMASK; + rd_msg.addr = wr_msg.addr = reg_addr; + mask = sdw_slv->sdw_slv_cap.sdw_dp0_cap->imp_def_intr_mask; + buf = 0; - list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { - if (bus->mstr == mstr) - break; - } - /* This is master is not registered with bus driver */ - if (!bus) { - dev_info(&mstr->dev, "Master not registered with bus\n"); - return 0; + /* Create message for reading the interrupt mask */ + /* Create message for reading the interrupt mask */ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DP0 Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } + + /* Enable the Slave defined interrupts. */ + buf |= mask; + + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_DP0_INTSTAT_TEST_FAIL_MASK; + buf |= SDW_DP0_INTSTAT_PORT_READY_MASK; + buf |= SDW_DP0_INTSTAT_BRA_FAILURE_MASK; + + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DP0 Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } + return 0; +} + +static int sdw_prog_slv(struct sdw_slv *sdw_slv) +{ + + struct sdw_slv_capabilities *cap; + int ret, i; + struct sdw_slv_dpn_capabilities *dpn_cap; + struct sdw_master *mstr = sdw_slv->mstr; + + if (!sdw_slv->slave_cap_updated) + return 0; + cap = &sdw_slv->sdw_slv_cap; + + /* Enable DP0 and SCP interrupts */ + ret = sdw_en_scp_intr(sdw_slv, cap->scp_impl_def_intr_mask); + + /* Failure should never happen, even if it happens we continue */ + if (ret) + dev_err(&mstr->dev, "SCP program failed\n"); + + for (i = 0; i < cap->num_of_sdw_ports; i++) { + dpn_cap = &cap->sdw_dpn_cap[i]; + ret = sdw_en_intr(sdw_slv, (i + 1), + dpn_cap->imp_def_intr_mask); + + if (ret) + break; + } + return ret; +} + + +static void sdw_send_slave_status(struct sdw_slv *slave, + enum sdw_slave_status *status) +{ + struct sdw_slave_driver *slv_drv = slave->driver; + + if (slv_drv && slv_drv->update_slv_status) + slv_drv->update_slv_status(slave, status); +} + +static int sdw_wait_for_deprepare(struct sdw_slv *slave) +{ + int ret; + struct sdw_msg msg; + u8 buf[1] = {0}; + int timeout = 0; + struct sdw_master *mstr = slave->mstr; + + /* Create message to read clock stop status, its broadcast message. */ + buf[0] = 0xFF; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_STAT; + /* + * Read the ClockStopNotFinished bit from the SCP_Stat register + * of particular Slave to make sure that clock stop prepare is done + */ + do { + /* + * Ideally this should not fail, but even if it fails + * in exceptional situation, we go ahead for clock stop + */ + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + + if (ret != 1) { + WARN_ONCE(1, "Clock stop status read failed\n"); + break; + } + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + break; + + /* + * TODO: Need to find from spec what is requirement. + * Since we are in suspend we should not sleep for more + * Ideally Slave should be ready to stop clock in less than + * few ms. + * So sleep less and increase loop time. This is not + * harmful, since if Slave is ready loop will terminate. + * + */ + msleep(2); + timeout++; + + } while (timeout != 500); + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + + dev_info(&mstr->dev, "Clock stop prepare done\n"); + else + WARN_ONCE(1, "Clk stp deprepare failed for slave %d\n", + slave->slv_number); + + return -EINVAL; +} + +static void sdw_prep_slave_for_clk_stp(struct sdw_master *mstr, + struct sdw_slv *slave, + enum sdw_clk_stop_mode clock_stop_mode, + bool prep) +{ + bool wake_en; + struct sdw_slv_capabilities *cap; + u8 buf[1] = {0}; + struct sdw_msg msg; + int ret; + + cap = &slave->sdw_slv_cap; + + /* Set the wakeup enable based on Slave capability */ + wake_en = !cap->wake_up_unavailable; + + if (prep) { + /* Even if its simplified clock stop prepare, + * setting prepare bit wont harm + */ + buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); + buf[0] |= clock_stop_mode << + SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; + buf[0] |= wake_en << SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT; + } else + buf[0] = 0; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_SYSTEMCTRL; + + /* + * We are calling NOPM version of the transfer API, because + * Master controllers calls this from the suspend handler, + * so if we call the normal transfer API, it tries to resume + * controller, which result in deadlock + */ + + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + /* We should continue even if it fails for some Slave */ + if (ret != 1) + WARN_ONCE(1, "Clock Stop prepare failed for slave %d\n", + slave->slv_number); +} + +static int sdw_check_for_prep_bit(struct sdw_slv *slave) +{ + u8 buf[1] = {0}; + struct sdw_msg msg; + int ret; + struct sdw_master *mstr = slave->mstr; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_SYSTEMCTRL; + + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + /* We should continue even if it fails for some Slave */ + if (ret != 1) { + dev_err(&mstr->dev, "SCP_SystemCtrl read failed for Slave %d\n", + slave->slv_number); + return -EINVAL; + + } + return (buf[0] & SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_MASK); + +} + +static int sdw_slv_deprepare_clk_stp1(struct sdw_slv *slave) +{ + struct sdw_slv_capabilities *cap; + int ret; + struct sdw_master *mstr = slave->mstr; + + cap = &slave->sdw_slv_cap; + + /* + * Slave might have enumerated 1st time or from clock stop mode 1 + * return if Slave doesn't require deprepare + */ + if (!cap->clk_stp1_deprep_required) + return 0; + + /* + * If Slave requires de-prepare after exiting from Clock Stop + * mode 1, than check for ClockStopPrepare bit in SystemCtrl register + * if its 1, de-prepare Slave from clock stop prepare, else + * return + */ + ret = sdw_check_for_prep_bit(slave); + /* If prepare bit is not set, return without error */ + if (!ret) + return 0; + + /* If error in reading register, return with error */ + if (ret < 0) + return ret; + + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) { + ret = slave->driver->pre_clk_stop_prep(slave, + cap->clock_stop1_mode_supported, false); + if (ret) { + dev_warn(&mstr->dev, "Pre de-prepare failed for Slave %d\n", + slave->slv_number); + return ret; + } + } + + sdw_prep_slave_for_clk_stp(slave->mstr, slave, + cap->clock_stop1_mode_supported, false); + + /* Make sure NF = 0 for deprepare to complete */ + ret = sdw_wait_for_deprepare(slave); + + /* Return in de-prepare unsuccessful */ + if (ret) + return ret; + + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + cap->clock_stop1_mode_supported, false); + + if (ret) + dev_err(&mstr->dev, "Post de-prepare failed for Slave %d\n", + slave->slv_number); + } + + return ret; +} + +static void handle_slave_status(struct kthread_work *work) +{ + int i, ret = 0; + struct sdw_slv_status *status, *__status__; + struct sdw_bus *bus = + container_of(work, struct sdw_bus, kwork); + struct sdw_master *mstr = bus->mstr; + unsigned long flags; + bool slave_present = 0; + + /* Handle the new attached slaves to the bus. Register new slave + * to the bus. + */ + list_for_each_entry_safe(status, __status__, &bus->status_list, node) { + if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { + ret += sdw_register_slave(mstr); + if (ret) + /* Even if adding new slave fails, we will + * continue. + */ + dev_err(&mstr->dev, "Registering new slave failed\n"); + } + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + slave_present = false; + if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && + mstr->sdw_addr[i].assigned == true) { + /* Logical address was assigned to slave, but + * now its down, so mark it as not present + */ + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + slave_present = true; + } + + else if (status->status[i] == SDW_SLAVE_STAT_ALERT && + mstr->sdw_addr[i].assigned == true) { + ret = 0; + /* Handle slave alerts */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; + ret = sdw_handle_slave_alerts(mstr, + mstr->sdw_addr[i].slave); + if (ret) + dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + + slave_present = true; + + + } else if (status->status[i] == + SDW_SLAVE_STAT_ATTACHED_OK && + mstr->sdw_addr[i].assigned == true) { + + sdw_prog_slv(mstr->sdw_addr[i].slave); + + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_ATTACHED_OK; + ret = sdw_slv_deprepare_clk_stp1( + mstr->sdw_addr[i].slave); + + /* + * If depreparing Slave fails, no need to + * reprogram Slave, this should never happen + * in ideal case. + */ + if (ret) + continue; + slave_present = true; + } + + if (!slave_present) + continue; + + sdw_send_slave_status(mstr->sdw_addr[i].slave, + &mstr->sdw_addr[i].status); + } + spin_lock_irqsave(&bus->spinlock, flags); + list_del(&status->node); + spin_unlock_irqrestore(&bus->spinlock, flags); + kfree(status); + } +} + +static int sdw_register_master(struct sdw_master *mstr) +{ + int ret = 0; + int i; + struct sdw_bus *sdw_bus; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) { + ret = -EAGAIN; + goto bus_init_not_done; + } + /* Sanity checks */ + if (unlikely(mstr->name[0] == '\0')) { + pr_err("sdw-core: Attempt to register an master with no name!\n"); + ret = -EINVAL; + goto mstr_no_name; + } + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) + mstr->sdw_addr[i].slv_number = i; + + rt_mutex_init(&mstr->bus_lock); + INIT_LIST_HEAD(&mstr->slv_list); + INIT_LIST_HEAD(&mstr->mstr_rt_list); + + sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); + if (!sdw_bus) + goto bus_alloc_failed; + sdw_bus->mstr = mstr; + init_completion(&sdw_bus->async_data.xfer_complete); + + mutex_lock(&sdw_core.core_lock); + list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); + mutex_unlock(&sdw_core.core_lock); + + dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); + mstr->dev.bus = &sdwint_bus_type; + mstr->dev.type = &sdw_mstr_type; + + ret = device_register(&mstr->dev); + if (ret) + goto out_list; + kthread_init_worker(&sdw_bus->kworker); + sdw_bus->status_thread = kthread_run(kthread_worker_fn, + &sdw_bus->kworker, "%s", + dev_name(&mstr->dev)); + if (IS_ERR(sdw_bus->status_thread)) { + dev_err(&mstr->dev, "error: failed to create status message task\n"); + ret = PTR_ERR(sdw_bus->status_thread); + goto task_failed; + } + kthread_init_work(&sdw_bus->kwork, handle_slave_status); + INIT_LIST_HEAD(&sdw_bus->status_list); + spin_lock_init(&sdw_bus->spinlock); + ret = sdw_mstr_bw_init(sdw_bus); + if (ret) { + dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); + goto mstr_bw_init_failed; + } + dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); + + return 0; + +mstr_bw_init_failed: +task_failed: + device_unregister(&mstr->dev); +out_list: + mutex_lock(&sdw_core.core_lock); + list_del(&sdw_bus->bus_node); + mutex_unlock(&sdw_core.core_lock); + kfree(sdw_bus); +bus_alloc_failed: +mstr_no_name: +bus_init_not_done: + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + return ret; +} + +/** + * sdw_master_update_slv_status: Report the status of slave to the bus driver. + * master calls this function based on the + * interrupt it gets once the slave changes its + * state. + * @mstr: Master handle for which status is reported. + * @status: Array of status of each slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status) +{ + struct sdw_bus *bus = NULL; + struct sdw_slv_status *slv_status; + unsigned long flags; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + /* This is master is not registered with bus driver */ + if (!bus) { + dev_info(&mstr->dev, "Master not registered with bus\n"); + return 0; } slv_status = kzalloc(sizeof(struct sdw_slv_status), GFP_ATOMIC); memcpy(slv_status->status, status, sizeof(struct sdw_status)); @@ -1355,6 +1889,106 @@ void sdw_del_master_controller(struct sdw_master *mstr) } EXPORT_SYMBOL_GPL(sdw_del_master_controller); +/** + * sdw_slave_xfer_bra_block: Transfer the data block using the BTP/BRA + * protocol. + * @mstr: SoundWire Master Master + * @block: Data block to be transferred. + */ +int sdw_slave_xfer_bra_block(struct sdw_master *mstr, + struct sdw_bra_block *block) +{ + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_driver *ops = NULL; + int ret; + + /* + * This API will be called by slave/codec + * when it needs to xfer firmware to + * its memory or perform bulk read/writes of registers. + */ + + /* + * Acquire core lock + * TODO: Acquire Master lock inside core lock + * similar way done in upstream. currently + * keeping it as core lock + */ + mutex_lock(&sdw_core.core_lock); + + /* Get master data structure */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != mstr) + continue; + + break; + } + + /* + * Here assumption is made that complete SDW bandwidth is used + * by BRA. So bus will return -EBUSY if any active stream + * is running on given master. + * TODO: In final implementation extra bandwidth will be always + * allocated for BRA. In that case all the computation of clock, + * frame shape, transport parameters for DP0 will be done + * considering BRA feature. + */ + if (!list_empty(&mstr->mstr_rt_list)) { + + /* + * Currently not allowing BRA when any + * active stream on master, returning -EBUSY + */ + + /* Release lock */ + mutex_unlock(&sdw_core.core_lock); + return -EBUSY; + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* + * Check whether Master is supporting bulk transfer. If not, then + * bus will use alternate method of performing BRA request using + * normal register read/write API. + * TODO: Currently if Master is not supporting BRA transfers, bus + * returns error. Bus driver to extend support for normal register + * read/write as alternate method. + */ + if (!ops->mstr_ops->xfer_bulk) + return -EINVAL; + + /* Data port Programming (ON) */ + ret = sdw_bus_bra_xport_config(sdw_mstr_bs, block, true); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Xport parameter config failed ret=%d\n", ret); + goto error; + } + + /* Bulk Setup */ + ret = ops->mstr_ops->xfer_bulk(mstr, block); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Transfer failed ret=%d\n", ret); + goto error; + } + + /* Data port Programming (OFF) */ + ret = sdw_bus_bra_xport_config(sdw_mstr_bs, block, false); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Xport parameter de-config failed ret=%d\n", ret); + goto error; + } + +error: + /* Release lock */ + mutex_unlock(&sdw_core.core_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(sdw_slave_xfer_bra_block); + /* * An sdw_driver is used with one or more sdw_slv (slave) nodes to access * sdw slave chips, on a bus instance associated with some sdw_master. @@ -1432,6 +2066,7 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, struct sdw_slv_dpn_capabilities *slv_dpn_cap, *dpn_cap; struct port_audio_mode_properties *prop, *slv_prop; int i, j; + int ret = 0; slv_cap = &sdw->sdw_slv_cap; @@ -1441,6 +2076,8 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, slv_cap->clock_stop1_mode_supported = cap->clock_stop1_mode_supported; slv_cap->simplified_clock_stop_prepare = cap->simplified_clock_stop_prepare; + slv_cap->scp_impl_def_intr_mask = cap->scp_impl_def_intr_mask; + slv_cap->highphy_capable = cap->highphy_capable; slv_cap->paging_supported = cap->paging_supported; slv_cap->bank_delay_support = cap->bank_delay_support; @@ -1560,6 +2197,9 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, prop->ch_prepare_behavior; } } + ret = sdw_prog_slv(sdw); + if (ret) + return ret; sdw->slave_cap_updated = true; return 0; } @@ -1723,10 +2363,23 @@ static void sdw_release_mstr_stream(struct sdw_master *mstr, struct sdw_runtime *sdw_rt) { struct sdw_mstr_runtime *mstr_rt, *__mstr_rt; + struct sdw_port_runtime *port_rt, *__port_rt, *first_port_rt = NULL; list_for_each_entry_safe(mstr_rt, __mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (mstr_rt->mstr == mstr) { + + /* Get first runtime node from port list */ + first_port_rt = list_first_entry(&mstr_rt->port_rt_list, + struct sdw_port_runtime, + port_node); + + /* Release Master port resources */ + list_for_each_entry_safe(port_rt, __port_rt, + &mstr_rt->port_rt_list, port_node) + list_del(&port_rt->port_node); + + kfree(first_port_rt); list_del(&mstr_rt->mstr_sdw_node); if (mstr_rt->direction == SDW_DATA_DIR_OUT) sdw_rt->tx_ref_count--; @@ -1744,10 +2397,23 @@ static void sdw_release_slave_stream(struct sdw_slv *slave, struct sdw_runtime *sdw_rt) { struct sdw_slave_runtime *slv_rt, *__slv_rt; + struct sdw_port_runtime *port_rt, *__port_rt, *first_port_rt = NULL; list_for_each_entry_safe(slv_rt, __slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { if (slv_rt->slave == slave) { + + /* Get first runtime node from port list */ + first_port_rt = list_first_entry(&slv_rt->port_rt_list, + struct sdw_port_runtime, + port_node); + + /* Release Slave port resources */ + list_for_each_entry_safe(port_rt, __port_rt, + &slv_rt->port_rt_list, port_node) + list_del(&port_rt->port_node); + + kfree(first_port_rt); list_del(&slv_rt->slave_sdw_node); if (slv_rt->direction == SDW_DATA_DIR_OUT) sdw_rt->tx_ref_count--; @@ -1917,7 +2583,6 @@ int sdw_config_stream(struct sdw_master *mstr, } else sdw_rt->rx_ref_count++; - /* SRK: check with hardik */ sdw_rt->type = stream_config->type; sdw_rt->stream_state = SDW_STATE_CONFIG_STREAM; @@ -1947,6 +2612,142 @@ int sdw_config_stream(struct sdw_master *mstr, } EXPORT_SYMBOL_GPL(sdw_config_stream); +/** + * sdw_chk_slv_dpn_caps - Return success + * -EINVAL - In case of error + * + * This function checks all slave port capabilities + * for given stream parameters. If any of parameters + * is not supported in port capabilities, it returns + * error. + */ +int sdw_chk_slv_dpn_caps(struct sdw_slv_dpn_capabilities *dpn_cap, + struct sdw_stream_params *strm_prms) +{ + struct port_audio_mode_properties *mode_prop = + dpn_cap->mode_properties; + int ret = 0, i, value; + + /* Check Sampling frequency */ + if (mode_prop->num_sampling_freq_configs) { + for (i = 0; i < mode_prop->num_sampling_freq_configs; i++) { + + value = mode_prop->sampling_freq_config[i]; + if (strm_prms->rate == value) + break; + } + + if (i == mode_prop->num_sampling_freq_configs) + return -EINVAL; + + } else { + + if ((strm_prms->rate < mode_prop->min_sampling_frequency) + || (strm_prms->rate > + mode_prop->max_sampling_frequency)) + return -EINVAL; + } + + /* check for bit rate */ + if (dpn_cap->num_word_length) { + for (i = 0; i < dpn_cap->num_word_length; i++) { + + value = dpn_cap->word_length_buffer[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_word_length) + return -EINVAL; + + } else { + + if ((strm_prms->bps < dpn_cap->min_word_length) + || (strm_prms->bps > dpn_cap->max_word_length)) + return -EINVAL; + } + + /* check for number of channels */ + if (dpn_cap->num_ch_supported) { + for (i = 0; i < dpn_cap->num_ch_supported; i++) { + + value = dpn_cap->ch_supported[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_ch_supported) + return -EINVAL; + + } else { + + if ((strm_prms->channel_count < dpn_cap->min_ch_num) + || (strm_prms->channel_count > dpn_cap->max_ch_num)) + return -EINVAL; + } + + return ret; +} + +/** + * sdw_chk_mstr_dpn_caps - Return success + * -EINVAL - In case of error + * + * This function checks all master port capabilities + * for given stream parameters. If any of parameters + * is not supported in port capabilities, it returns + * error. + */ +int sdw_chk_mstr_dpn_caps(struct sdw_mstr_dpn_capabilities *dpn_cap, + struct sdw_stream_params *strm_prms) +{ + + int ret = 0, i, value; + + /* check for bit rate */ + if (dpn_cap->num_word_length) { + for (i = 0; i < dpn_cap->num_word_length; i++) { + + value = dpn_cap->word_length_buffer[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_word_length) + return -EINVAL; + + } else { + + if ((strm_prms->bps < dpn_cap->min_word_length) + || (strm_prms->bps > dpn_cap->max_word_length)) { + return -EINVAL; + } + + + } + + /* check for number of channels */ + if (dpn_cap->num_ch_supported) { + for (i = 0; i < dpn_cap->num_ch_supported; i++) { + + value = dpn_cap->ch_supported[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_ch_supported) + return -EINVAL; + + } else { + + if ((strm_prms->channel_count < dpn_cap->min_ch_num) + || (strm_prms->channel_count > dpn_cap->max_ch_num)) + return -EINVAL; + } + + return ret; +} + static int sdw_mstr_port_configuration(struct sdw_master *mstr, struct sdw_runtime *sdw_rt, struct sdw_port_config *port_config) @@ -1955,6 +2756,9 @@ static int sdw_mstr_port_configuration(struct sdw_master *mstr, struct sdw_port_runtime *port_rt; int found = 0; int i; + int ret = 0, pn = 0; + struct sdw_mstr_dpn_capabilities *dpn_cap = + mstr->mstr_capabilities.sdw_dpn_cap; list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (mstr_rt->mstr == mstr) { @@ -1966,16 +2770,35 @@ static int sdw_mstr_port_configuration(struct sdw_master *mstr, dev_err(&mstr->dev, "Master not found for this port\n"); return -EINVAL; } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * port_config->num_ports, GFP_KERNEL); if (!port_rt) return -EINVAL; + + if (!dpn_cap) + return -EINVAL; + /* + * Note: Here the assumption the configuration is not + * received for 0th port. + */ for (i = 0; i < port_config->num_ports; i++) { port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; - port_rt[i].port_num = port_config->port_cfg[i].port_num; + port_rt[i].port_num = pn = port_config->port_cfg[i].port_num; + + /* Perform capability check for master port */ + ret = sdw_chk_mstr_dpn_caps(&dpn_cap[pn], + &mstr_rt->stream_params); + if (ret < 0) { + dev_err(&mstr->dev, + "Master capabilities check failed\n"); + return -EINVAL; + } + list_add_tail(&port_rt[i].port_node, &mstr_rt->port_rt_list); } - return 0; + + return ret; } static int sdw_slv_port_configuration(struct sdw_slv *slave, @@ -1984,8 +2807,10 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, { struct sdw_slave_runtime *slv_rt; struct sdw_port_runtime *port_rt; - int found = 0; - int i; + struct sdw_slv_dpn_capabilities *dpn_cap = + slave->sdw_slv_cap.sdw_dpn_cap; + int found = 0, ret = 0; + int i, pn; list_for_each_entry(slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { if (slv_rt->slave == slave) { @@ -1997,6 +2822,12 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, dev_err(&slave->mstr->dev, "Slave not found for this port\n"); return -EINVAL; } + + if (!slave->slave_cap_updated) { + dev_err(&slave->mstr->dev, "Slave capabilities not updated\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * port_config->num_ports, GFP_KERNEL); if (!port_rt) @@ -2004,10 +2835,21 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, for (i = 0; i < port_config->num_ports; i++) { port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; - port_rt[i].port_num = port_config->port_cfg[i].port_num; + port_rt[i].port_num = pn = port_config->port_cfg[i].port_num; + + /* Perform capability check for master port */ + ret = sdw_chk_slv_dpn_caps(&dpn_cap[pn], + &slv_rt->stream_params); + if (ret < 0) { + dev_err(&slave->mstr->dev, + "Slave capabilities check failed\n"); + return -EINVAL; + } + list_add_tail(&port_rt[i].port_node, &slv_rt->port_rt_list); } - return 0; + + return ret; } /** @@ -2040,7 +2882,6 @@ int sdw_config_port(struct sdw_master *mstr, struct sdw_runtime *sdw_rt = NULL; struct sdw_stream_tag *stream = NULL; - for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { if (stream_tags[i].stream_tag == stream_tag) { sdw_rt = stream_tags[i].sdw_rt; @@ -2048,10 +2889,12 @@ int sdw_config_port(struct sdw_master *mstr, break; } } + if (!sdw_rt) { dev_err(&mstr->dev, "Invalid stream tag\n"); return -EINVAL; } + if (static_key_false(&sdw_trace_msg)) { int i; @@ -2060,13 +2903,16 @@ int sdw_config_port(struct sdw_master *mstr, &port_config->port_cfg[i], stream_tag); } } + mutex_lock(&stream->stream_lock); + if (!slave) ret = sdw_mstr_port_configuration(mstr, sdw_rt, port_config); else ret = sdw_slv_port_configuration(slave, sdw_rt, port_config); mutex_unlock(&stream->stream_lock); + return ret; } EXPORT_SYMBOL_GPL(sdw_config_port); @@ -2078,9 +2924,6 @@ int sdw_prepare_and_enable(int stream_tag, bool enable) struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; struct sdw_stream_tag *stream = NULL; - /* TBD: SRK, Check with hardik whether both locks needed - * stream and core?? - */ mutex_lock(&sdw_core.core_lock); for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { @@ -2200,126 +3043,357 @@ int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, } EXPORT_SYMBOL_GPL(sdw_wait_for_slave_enumeration); -int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool stop, - enum sdw_clk_stop_mode *clck_stop_mode) +static enum sdw_clk_stop_mode sdw_get_clk_stp_mode(struct sdw_slv *slave) { - int i; + enum sdw_clk_stop_mode clock_stop_mode = SDW_CLOCK_STOP_MODE_0; + struct sdw_slv_capabilities *cap = &slave->sdw_slv_cap; + + if (!slave->driver) + return clock_stop_mode; + /* + * Get the dynamic value of clock stop from Slave driver + * if supported, else use the static value from + * capabilities register. Update the capabilities also + * if we have new dynamic value. + */ + if (slave->driver->get_dyn_clk_stp_mod) { + clock_stop_mode = slave->driver->get_dyn_clk_stp_mod(slave); + + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + cap->clock_stop1_mode_supported = true; + else + cap->clock_stop1_mode_supported = false; + } else + clock_stop_mode = cap->clock_stop1_mode_supported; + + return clock_stop_mode; +} + +/** + * sdw_master_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * register with clock_stop_now bit set. + * + * @mstr: Master handle for which clock has to be stopped. + * + * Returns 0 on success, appropriate error code on failure. + */ +int sdw_master_stop_clock(struct sdw_master *mstr) +{ + int ret = 0, i; struct sdw_msg msg; u8 buf[1] = {0}; - struct sdw_slave *slave; - enum sdw_clk_stop_mode clock_stop_mode; - int timeout = 0; - int ret = 0; - int slave_dev_present = 0; + enum sdw_clk_stop_mode mode; - /* Find if all slave support clock stop mode1 if all slaves support - * clock stop mode1 use mode1 else use mode0 - */ - for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { - if (mstr->sdw_addr[i].assigned && - mstr->sdw_addr[i].status != SDW_SLAVE_STAT_NOT_PRESENT) { - slave_dev_present = 1; - slave = mstr->sdw_addr[i].slave; - clock_stop_mode &= - slave->sdw_slv_cap.clock_stop1_mode_supported; - if (!clock_stop_mode) - break; - } - } - if (stop) { - *clck_stop_mode = clock_stop_mode; - dev_info(&mstr->dev, "Entering Clock stop mode %x\n", - clock_stop_mode); - } - /* Slaves might have removed power during its suspend - * in that case no need to do clock stop prepare - * and return from here + /* Send Broadcast message to the SCP_ctrl register with + * clock stop now. If none of the Slaves are attached, then there + * may not be ACK, flag the error about ACK not recevied but + * clock will be still stopped. */ - if (!slave_dev_present) - return 0; - /* Prepare for the clock stop mode. For simplified clock stop - * prepare only mode is to be set, For others set the ClockStop - * Prepare bit in SCP_SystemCtrl register. For all the other slaves - * set the clock stop prepare bit. For all slave set the clock - * stop mode based on what we got in earlier loop + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = SDW_SLAVE_BDCAST_ADDR; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_CTRL; + buf[0] |= 0x1 << SDW_SCP_CTRL_CLK_STP_NOW_SHIFT; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + + /* Even if broadcast fails, we stop the clock and flag error */ + if (ret != 1) + dev_err(&mstr->dev, "ClockStopNow Broadcast message failed\n"); + + /* + * Mark all Slaves as un-attached which are entering clock stop + * mode1 */ for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + + if (!mstr->sdw_addr[i].assigned) + continue; + + /* Get clock stop mode for all Slaves */ + mode = sdw_get_clk_stp_mode(mstr->sdw_addr[i].slave); + if (mode == SDW_CLOCK_STOP_MODE_0) + continue; + + /* If clock stop mode 1, mark Slave as not present */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_NOT_PRESENT; + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_stop_clock); + +static struct sdw_slv *get_slave_for_prep_deprep(struct sdw_master *mstr, + int *slave_index) +{ + int i; + + for (i = *slave_index; i <= SOUNDWIRE_MAX_DEVICES; i++) { if (mstr->sdw_addr[i].assigned != true) continue; + if (mstr->sdw_addr[i].status == SDW_SLAVE_STAT_NOT_PRESENT) continue; - slave = mstr->sdw_addr[i].slave; - msg.ssp_tag = 0; - slave = mstr->sdw_addr[i].slave; - if (stop) { - /* Even if its simplified clock stop prepare - * setting prepare bit wont harm - */ - buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); - buf[0] |= clock_stop_mode << - SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; - } - msg.flag = SDW_MSG_FLAG_WRITE; - msg.addr = SDW_SCP_SYSTEMCTRL; - msg.len = 1; - msg.buf = buf; - msg.slave_addr = i; - msg.addr_page1 = 0x0; - msg.addr_page2 = 0x0; - ret = sdw_slave_transfer_nopm(mstr, &msg, 1); - if (ret != 1) { - dev_err(&mstr->dev, "Clock Stop prepare failed\n"); - return -EBUSY; - } + *slave_index = i + 1; + return mstr->sdw_addr[i].slave; } + return NULL; +} + +/* + * Wait till clock stop prepare/deprepare is finished. Prepare for all + * mode, De-prepare only for the Slaves resuming from clock stop mode 0 + */ +static void sdw_wait_for_clk_prep(struct sdw_master *mstr) +{ + int ret; + struct sdw_msg msg; + u8 buf[1] = {0}; + int timeout = 0; + + /* Create message to read clock stop status, its broadcast message. */ + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = SDW_SLAVE_BDCAST_ADDR; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_STAT; + buf[0] = 0xFF; /* - * Once clock stop prepare bit is set, broadcast the message to read - * ClockStop_NotFinished bit from SCP_Stat, till we read it as 11 - * we dont exit loop. We wait for definite time before retrying - * if its simple clock stop it will be always 1, while for other - * they will driver 0 on bus so we wont get 1. In total we are - * waiting 1 sec before we timeout. + * Once all the Slaves are written with prepare bit, + * we go ahead and broadcast the read message for the + * SCP_STAT register to read the ClockStopNotFinished bit + * Read till we get this a 0. Currently we have timeout of 1sec + * before giving up. Even if its not read as 0 after timeout, + * controller can stop the clock after warning. */ do { - buf[0] = 0xFF; - msg.ssp_tag = 0; - msg.flag = SDW_MSG_FLAG_READ; - msg.addr = SDW_SCP_STAT; - msg.len = 1; - msg.buf = buf; - msg.slave_addr = 15; - msg.addr_page1 = 0x0; - msg.addr_page2 = 0x0; + /* + * Ideally this should not fail, but even if it fails + * in exceptional situation, we go ahead for clock stop + */ ret = sdw_slave_transfer_nopm(mstr, &msg, 1); - if (ret != 1) - goto prepare_failed; + + if (ret != 1) { + WARN_ONCE(1, "Clock stop status read failed\n"); + break; + } if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) - break; - msleep(100); + break; + + /* + * TODO: Need to find from spec what is requirement. + * Since we are in suspend we should not sleep for more + * Ideally Slave should be ready to stop clock in less than + * few ms. + * So sleep less and increase loop time. This is not + * harmful, since if Slave is ready loop will terminate. + * + */ + msleep(2); timeout++; - } while (timeout != 11); - /* If we are trying to stop and prepare failed its not ok - */ - if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) { + + } while (timeout != 500); + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + dev_info(&mstr->dev, "Clock stop prepare done\n"); - return 0; - /* If we are trying to resume and un-prepare failes its ok - * since codec might be down during suspned and will - * start afresh after resuming + else + WARN_ONCE(1, "Some Slaves prepare un-successful\n"); +} + +/** + * sdw_master_prep_for_clk_stop: Prepare all the Slaves for clock stop. + * Iterate through each of the enumerated Slave. + * Prepare each Slave according to the clock stop + * mode supported by Slave. Use dynamic value from + * Slave callback if registered, else use static values + * from Slave capabilities registered. + * 1. Get clock stop mode for each Slave. + * 2. Call pre_prepare callback of each Slave if + * registered. + * 3. Prepare each Slave for clock stop + * 4. Broadcast the Read message to make sure + * all Slaves are prepared for clock stop. + * 5. Call post_prepare callback of each Slave if + * registered. + * + * @mstr: Master handle for which clock state has to be changed. + * + * Returns 0 + */ +int sdw_master_prep_for_clk_stop(struct sdw_master *mstr) +{ + struct sdw_slv_capabilities *cap; + enum sdw_clk_stop_mode clock_stop_mode; + int ret = 0; + struct sdw_slv *slave = NULL; + int slv_index = 1; + + /* + * Get all the Slaves registered to the master driver for preparing + * for clock stop. Start from Slave with logical address as 1. */ - } else if (!stop) { - dev_info(&mstr->dev, "Some Slaves un-prepare un-successful\n"); - return 0; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) { + ret = slave->driver->pre_clk_stop_prep(slave, + clock_stop_mode, true); + + /* If it fails we still continue */ + if (ret) + dev_warn(&mstr->dev, "Pre prepare failed for Slave %d\n", + slave->slv_number); + } + + sdw_prep_slave_for_clk_stp(mstr, slave, clock_stop_mode, true); + } + + /* Wait till prepare for all Slaves is finished */ + /* + * We should continue even if the prepare fails. Clock stop + * prepare failure on Slaves, should not impact the broadcasting + * of ClockStopNow. + */ + sdw_wait_for_clk_prep(mstr); + + slv_index = 1; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + clock_stop_mode, + true); + /* + * Even if Slave fails we continue with other + * Slaves. This should never happen ideally. + */ + if (ret) + dev_err(&mstr->dev, "Post prepare failed for Slave %d\n", + slave->slv_number); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_prep_for_clk_stop); + +/** + * sdw_mstr_deprep_after_clk_start: De-prepare all the Slaves + * exiting clock stop mode 0 after clock resumes. Clock + * is already resumed before this. De-prepare all the Slaves + * which were earlier in ClockStop mode0. De-prepare for the + * Slaves which were there in ClockStop mode1 is done after + * they enumerated back. Its not done here as part of master + * getting resumed. + * 1. Get clock stop mode for each Slave its exiting from + * 2. Call pre_prepare callback of each Slave exiting from + * clock stop mode 0. + * 3. De-Prepare each Slave exiting from Clock Stop mode0 + * 4. Broadcast the Read message to make sure + * all Slaves are de-prepared for clock stop. + * 5. Call post_prepare callback of each Slave exiting from + * clock stop mode0 + * + * + * @mstr: Master handle + * + * Returns 0 + */ +int sdw_mstr_deprep_after_clk_start(struct sdw_master *mstr) +{ + struct sdw_slv_capabilities *cap; + enum sdw_clk_stop_mode clock_stop_mode; + int ret = 0; + struct sdw_slv *slave = NULL; + /* We are preparing for stop */ + bool stop = false; + int slv_index = 1; + + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + /* Get the clock stop mode from which Slave is exiting */ + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Slave is exiting from Clock stop mode 1, De-prepare + * is optional based on capability, and it has to be done + * after Slave is enumerated. So nothing to be done + * here. + */ + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + continue; + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) + ret = slave->driver->pre_clk_stop_prep(slave, + clock_stop_mode, false); + + /* If it fails we still continue */ + if (ret) + dev_warn(&mstr->dev, "Pre de-prepare failed for Slave %d\n", + slave->slv_number); + + sdw_prep_slave_for_clk_stp(mstr, slave, clock_stop_mode, false); } -prepare_failed: - dev_err(&mstr->dev, "Clock Stop prepare failed\n"); - return -EBUSY; + /* + * Wait till prepare is finished for all the Slaves. + */ + sdw_wait_for_clk_prep(mstr); + + slv_index = 1; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Slave is exiting from Clock stop mode 1, De-prepare + * is optional based on capability, and it has to be done + * after Slave is enumerated. + */ + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + continue; + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + clock_stop_mode, + stop); + /* + * Even if Slave fails we continue with other + * Slaves. This should never happen ideally. + */ + if (ret) + dev_err(&mstr->dev, "Post de-prepare failed for Slave %d\n", + slave->slv_number); + } + } + return 0; } -EXPORT_SYMBOL_GPL(sdw_prepare_for_clock_change); +EXPORT_SYMBOL_GPL(sdw_mstr_deprep_after_clk_start); + struct sdw_master *sdw_get_master(int nr) { diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index cafaccbeea3a..7ebb26756f59 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -25,37 +25,38 @@ #include -#define MAXCLOCKFREQ 6 -#ifdef CONFIG_SND_SOC_SVFPGA -/* For PDM Capture, frameshape used is 50x10 */ -int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, - 96, 125, 144, 147, 120, 128, 150, +#ifndef CONFIG_SND_SOC_SVFPGA /* Original */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +int rows[MAX_NUM_ROWS] = {48, 50, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 100, 120, 128, 150, 160, 180, 192, 200, 240, 250, 256}; +#define SDW_DEFAULT_SSP 50 +#else +int rows[MAX_NUM_ROWS] = {125, 64, 48, 50, 60, 72, 75, 80, 90, + 96, 144, 147, 100, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; +#define SDW_DEFAULT_SSP 24 +#endif /* IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) */ -int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; - -int clock_freq[MAXCLOCKFREQ] = {19200000, 19200000, - 19200000, 19200000, - 19200000, 19200000}; +int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; #else -/* TBD: Currently we are using 100x2 as frame shape. to be removed later */ -int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, +/* For PDM Capture, frameshape used is 50x10 */ +int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, 96, 125, 144, 147, 120, 128, 150, 160, 180, 192, 200, 240, 250, 256}; -int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; +#define SDW_DEFAULT_SSP 50 +#endif /* * TBD: Get supported clock frequency from ACPI and store * it in master data structure. */ -/* Currently only 9.6MHz clock frequency used */ -int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, - 9600000, 9600000, - 9600000, 9600000}; -#endif +#define MAXCLOCKDIVS 1 +int clock_div[MAXCLOCKDIVS] = {1}; struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, @@ -117,19 +118,8 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; -#ifdef CONFIG_SND_SOC_SVFPGA - /* TBD: For PDM capture to be removed later */ - sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000 * 2; -#else - /* TBD: Base Clock frequency should be read from - * master capabilities - * Currenly hardcoding to 9.6MHz - */ - sdw_bs->clk_freq = 9.6 * 1000 * 1000; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + sdw_bs->clk_freq = (sdw_mstr_cap->base_clk_freq * 2); -#endif return 0; } EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); @@ -203,9 +193,8 @@ int sdw_lcm(int num1, int num2) * transport and port parameters. */ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, - struct sdw_slave_runtime *slv_rt, struct sdw_transport_params *t_slv_params, - struct sdw_port_params *p_slv_params) + struct sdw_port_params *p_slv_params, int slv_number) { struct sdw_msg wr_msg, wr_msg1, rd_msg; int ret = 0; @@ -241,7 +230,7 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wbuf[2] = ((t_slv_params->sample_interval - 1) >> 8) & SDW_DPN_SAMPLECTRL1_LOW_MASK; /* DPN_SampleCtrl2 */ wbuf[3] = t_slv_params->offset1; /* DPN_OffsetCtrl1 */ - wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl1 */ + wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl2 */ /* DPN_HCtrl */ wbuf[5] = (t_slv_params->hstop | (t_slv_params->hstart << 4)); wbuf[6] = t_slv_params->blockpackingmode; /* DPN_BlockCtrl3 */ @@ -256,21 +245,18 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, rd_msg.ssp_tag = 0x0; rd_msg.flag = SDW_MSG_FLAG_READ; rd_msg.len = 1; - rd_msg.slave_addr = slv_rt->slave->slv_number; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; rd_msg.addr_page1 = 0x0; rd_msg.addr_page2 = 0x0; -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } -#endif wbuf1[0] = (p_slv_params->port_flow_mode | (p_slv_params->port_data_mode << @@ -295,7 +281,8 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, #else wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); #endif - wr_msg.slave_addr = slv_rt->slave->slv_number; + + wr_msg.slave_addr = slv_number; wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; @@ -303,14 +290,12 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg1.ssp_tag = 0x0; wr_msg1.flag = SDW_MSG_FLAG_WRITE; wr_msg1.len = 2; - wr_msg1.slave_addr = slv_rt->slave->slv_number; + + wr_msg1.slave_addr = slv_number; wr_msg1.buf = &wbuf1[0]; wr_msg1.addr_page1 = 0x0; wr_msg1.addr_page2 = 0x0; -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -326,7 +311,6 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, goto out; } out: -#endif return ret; } @@ -370,119 +354,16 @@ int sdw_cfg_mstr_params(struct sdw_bus *mstr_bs, return 0; } - -/* - * sdw_cfg_mstr_slv - returns Success - * -EINVAL - In case of error. - * - * - * This function call master/slave transport/port - * params configuration API's, called from sdw_bus_calc_bw - * & sdw_bus_calc_bw_dis API's. - */ -int sdw_cfg_mstr_slv(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt, - bool is_master) -{ - struct sdw_transport_params *t_params, *t_slv_params; - struct sdw_port_params *p_params, *p_slv_params; - struct sdw_slave_runtime *slv_rt = NULL; - struct sdw_port_runtime *port_rt, *port_slv_rt; - int ret = 0; - - if (is_master) { - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - return 0; - - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { - - /* Transport and port parameters */ - t_params = &port_rt->transport_params; - p_params = &port_rt->port_params; - - p_params->num = port_rt->port_num; - p_params->word_length = - sdw_mstr_bs_rt->stream_params.bps; - p_params->port_flow_mode = 0x0; /* Isochronous Mode */ - p_params->port_data_mode = 0x0; /* Normal Mode */ - - /* Configure xport params and port params for master */ - ret = sdw_cfg_mstr_params(sdw_mstr_bs, - t_params, p_params); - if (ret < 0) - return ret; - - /* Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - - break; - } - - } else { - - - list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { - - if (slv_rt->slave == NULL) - break; - - /* should not compute any transport params */ - if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; - - list_for_each_entry(port_slv_rt, - &slv_rt->port_rt_list, port_node) { - - /* Fill in port params here */ - port_slv_rt->port_params.num = - port_slv_rt->port_num; - port_slv_rt->port_params.word_length = - slv_rt->stream_params.bps; - /* Isochronous Mode */ - port_slv_rt->port_params.port_flow_mode = 0x0; - /* Normal Mode */ - port_slv_rt->port_params.port_data_mode = 0x0; - t_slv_params = &port_slv_rt->transport_params; - p_slv_params = &port_slv_rt->port_params; - - /* Configure xport & port params for slave */ - ret = sdw_cfg_slv_params(sdw_mstr_bs, - slv_rt, t_slv_params, p_slv_params); - if (ret < 0) - return ret; - - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - - break; - } - } - - } - - return 0; -} - - /* - * sdw_cpy_params_mstr_slv - returns Success - * -EINVAL - In case of error. - * + * sdw_cfg_params_mstr_slv - returns Success * * This function copies/configure master/slave transport & - * port params to alternate bank. + * port params. * */ -int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt) +int sdw_cfg_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool state_check) { struct sdw_slave_runtime *slv_rt = NULL; struct sdw_port_runtime *port_rt, *port_slv_rt; @@ -496,6 +377,11 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (slv_rt->slave == NULL) break; + /* configure transport params based on state */ + if ((state_check) && + (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT)) + continue; + list_for_each_entry(port_slv_rt, &slv_rt->port_rt_list, port_node) { @@ -511,20 +397,17 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, p_slv_params = &port_slv_rt->port_params; /* Configure xport & port params for slave */ - ret = sdw_cfg_slv_params(sdw_mstr_bs, - slv_rt, t_slv_params, p_slv_params); + ret = sdw_cfg_slv_params(sdw_mstr_bs, t_slv_params, + p_slv_params, slv_rt->slave->slv_number); if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } } + if ((state_check) && + (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT)) + return 0; list_for_each_entry(port_rt, &sdw_mstr_bs_rt->port_rt_list, port_node) { @@ -544,10 +427,6 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* Since one port per slave runtime, breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } return 0; @@ -608,10 +487,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, */ /* 2. slave port enable */ -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -638,7 +513,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } -#endif /* * 3. slave port enable post pre * --> callback @@ -653,10 +527,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, * --> callback * --> no callback available */ -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA /* 2. slave port disable */ ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); @@ -685,7 +555,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } -#endif + /* * 3. slave port enable post unpre * --> callback @@ -695,9 +565,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; } -#ifndef CONFIG_SND_SOC_MXFPGA out: -#endif return ret; } @@ -783,13 +651,6 @@ int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } break; @@ -811,13 +672,6 @@ int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } } @@ -858,13 +712,6 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - break; } } } @@ -879,13 +726,6 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - - break; } } @@ -902,41 +742,109 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, * clock frequency. */ int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, - int *col, int *row) + struct sdw_mstr_runtime *sdw_mstr_rt) { - int i, rc, clock_reqd = 0, frame_interval = 0, frame_frequency = 0; - int sel_row = 0, sel_col = 0; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_slv_dpn_capabilities *sdw_slv_dpn_cap = NULL; + struct port_audio_mode_properties *mode_prop = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_slv_rt = NULL; + int i, j, rc; + int clock_reqd = 0, frame_interval = 0, frame_frequency = 0; + int sel_row = 0, sel_col = 0, pn = 0; + int value; bool clock_ok = false; + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + /* * Find nearest clock frequency needed by master for * given bandwidth */ - - /* - * TBD: Need to run efficient algorithm to make sure we have - * only 1 to 10 percent of control bandwidth usage - */ - for (i = 0; i < MAXCLOCKFREQ; i++) { + for (i = 0; i < MAXCLOCKDIVS; i++) { /* TBD: Check why 3000 */ - if ((clock_freq[i] <= sdw_mstr_bs->bandwidth) || - ((clock_freq[i] % 3000) != 0)) + if ((((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]) <= + sdw_mstr_bs->bandwidth) || + ((((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]) + % 3000) != 0)) continue; - clock_reqd = clock_freq[i]; + + clock_reqd = ((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]); /* - * TBD: Check all the slave device capabilities + * Check all the slave device capabilities * here and find whether given frequency is * supported by all slaves */ + list_for_each_entry(slv_rt, &sdw_mstr_rt->slv_rt_list, + slave_node) { + + /* check for valid slave */ + if (slv_rt->slave == NULL) + break; + + /* check clock req for each port */ + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + pn = port_slv_rt->port_num; + + + sdw_slv_dpn_cap = + &slv_rt->slave->sdw_slv_cap.sdw_dpn_cap[pn]; + mode_prop = sdw_slv_dpn_cap->mode_properties; + + /* + * TBD: Indentation to be fixed, + * code refactoring to be considered. + */ + if (mode_prop->num_freq_configs) { + for (j = 0; j < + mode_prop->num_freq_configs; j++) { + value = + mode_prop->freq_supported[j]; + if (clock_reqd == value) { + clock_ok = true; + break; + } + if (j == + mode_prop->num_freq_configs) { + clock_ok = false; + break; + } + + } + + } else { + if ((clock_reqd < + mode_prop->min_frequency) || + (clock_reqd > + mode_prop->max_frequency)) { + clock_ok = false; + } else + clock_ok = true; + } + + /* Go for next clock frequency */ + if (!clock_ok) + break; + } + + /* + * Dont check next slave, go for next clock + * frequency + */ + if (!clock_ok) + break; + } + + /* check for next clock divider */ + if (!clock_ok) + continue; /* Find frame shape based on bandwidth per controller */ - /* - * TBD: Need to run efficient algorithm to make sure we have - * only 1 to 10 percent of control bandwidth usage - */ - for (rc = 0; rc <= MAX_NUM_ROW_COLS; rc++) { + for (rc = 0; rc < MAX_NUM_ROW_COLS; rc++) { frame_interval = sdw_core.rowcolcomb[rc].row * sdw_core.rowcolcomb[rc].col; @@ -952,21 +860,27 @@ int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, break; } + /* Valid frameshape not found, check for next clock freq */ + if (rc == MAX_NUM_ROW_COLS) + continue; + sel_row = sdw_core.rowcolcomb[rc].row; sel_col = sdw_core.rowcolcomb[rc].col; sdw_mstr_bs->frame_freq = frame_frequency; sdw_mstr_bs->clk_freq = clock_reqd; + sdw_mstr_bs->clk_div = clock_div[i]; clock_ok = false; *frame_int = frame_interval; - *col = sel_col; - *row = sel_row; sdw_mstr_bs->col = sel_col; sdw_mstr_bs->row = sel_row; - break; - + return 0; } + /* None of clock frequency matches, return error */ + if (i == MAXCLOCKDIVS) + return -EINVAL; + return 0; } @@ -982,74 +896,95 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, int frame_interval) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; - struct sdw_transport_params *t_params; - struct sdw_port_runtime *port_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; int lcmnum1 = 0, lcmnum2 = 0, div = 0, lcm = 0; + int sample_interval; /* * once you got bandwidth frame shape for bus, * run a loop for all the active streams running - * on bus and compute sample_interval & other transport parameters. + * on bus and compute stream interval & sample_interval. */ - list_for_each_entry(sdw_mstr_bs_rt, + list_for_each_entry(sdw_mstr_rt, &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) + if (sdw_mstr_rt->mstr == NULL) break; - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* + * Calculate sample interval for stream + * running on given master. + */ + if (sdw_mstr_rt->stream_params.rate) + sample_interval = (sdw_mstr_bs->clk_freq/ + sdw_mstr_rt->stream_params.rate); + else + return -EINVAL; + /* Run port loop to assign sample interval per port */ list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { + &sdw_mstr_rt->port_rt_list, port_node) { t_params = &port_rt->transport_params; /* - * Current Assumption: - * One port per bus runtime structure + * Assign sample interval each port transport + * properties. Assumption is that sample interval + * per port for given master will be same. */ - /* Calculate sample interval */ -#ifdef CONFIG_SND_SOC_SVFPGA - t_params->sample_interval = - ((sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate)); -#else - t_params->sample_interval = - ((sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate) * 2); + t_params->sample_interval = sample_interval; + } -#endif - /* Only BlockPerPort supported */ - t_params->blockpackingmode = 0; - t_params->lanecontrol = 0; + /* Calculate LCM */ + lcmnum2 = sample_interval; + if (!lcmnum1) + lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); + else + lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); - /* Calculate LCM */ - lcmnum2 = t_params->sample_interval; - if (!lcmnum1) - lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); - else - lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); + /* Run loop for slave per master runtime */ + list_for_each_entry(slv_rt, + &sdw_mstr_rt->slv_rt_list, slave_node) { - /* - * Since one port per bus runtime, breaking - * port_list loop - * TBD: to be extended for multiple port support - */ - break; + if (slv_rt->slave == NULL) + break; + + /* Assign sample interval for each port of slave */ + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + /* Assign sample interval each port */ + t_slv_params->sample_interval = sample_interval; + } } } + /* + * If system interval already calculated + * In pause/resume, underrun scenario + */ + if (sdw_mstr_bs->system_interval) + return 0; + + /* Assign frame stream interval */ + sdw_mstr_bs->stream_interval = lcmnum1; /* 6. compute system_interval */ if ((sdw_mstr_cap) && (sdw_mstr_bs->clk_freq)) { div = ((sdw_mstr_cap->base_clk_freq * 2) / sdw_mstr_bs->clk_freq); - lcm = sdw_lcm(lcmnum1, frame_interval); + + if ((lcmnum1) && (frame_interval)) + lcm = sdw_lcm(lcmnum1, frame_interval); + else + return -EINVAL; + sdw_mstr_bs->system_interval = (div * lcm); } @@ -1065,6 +1000,25 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, return 0; } +/** + * sdw_chk_first_node - returns True or false + * + * This function returns true in case of first node + * else returns false. + */ +bool sdw_chk_first_node(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_master *sdw_mstr) +{ + struct sdw_mstr_runtime *first_rt = NULL; + + first_rt = list_first_entry(&sdw_mstr->mstr_rt_list, + struct sdw_mstr_runtime, mstr_node); + if (sdw_mstr_rt == first_rt) + return true; + else + return false; + +} /* * sdw_compute_hstart_hstop - returns Success @@ -1074,273 +1028,199 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, * This function computes hstart and hstop for running * streams per master & slaves. */ -int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) +int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_mstr_runtime *sdw_mstr_rt; struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; struct sdw_slave_runtime *slv_rt = NULL; struct sdw_port_runtime *port_rt, *port_slv_rt; - int hstop = 0, hwidth = 0; - int payload_bw = 0, full_bw = 0, column_needed = 0; - bool hstop_flag = false; - - /* Calculate hwidth, hstart and hstop */ - list_for_each_entry(sdw_mstr_bs_rt, + int hstart = 0, hstop = 0; + int column_needed = 0; + int sel_col = sdw_mstr_bs->col; + int group_count = 0, no_of_channels = 0; + struct temp_elements *temp, *element; + int rates[10]; + int num, ch_mask, block_offset, i, port_block_offset; + + /* Run loop for all master runtimes for given master */ + list_for_each_entry(sdw_mstr_rt, &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) + if (sdw_mstr_rt->mstr == NULL) break; /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) continue; - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { - - t_params = &port_rt->transport_params; - t_params->num = port_rt->port_num; + /* Perform grouping of streams based on stream rate */ + if (sdw_mstr_rt == list_first_entry(&sdw_mstr->mstr_rt_list, + struct sdw_mstr_runtime, mstr_node)) + rates[group_count++] = sdw_mstr_rt->stream_params.rate; + else { + num = group_count; + for (i = 0; i < num; i++) { + if (sdw_mstr_rt->stream_params.rate == rates[i]) + break; - /* - * 1. find full_bw and payload_bw per stream - * 2. find h_width per stream - * 3. find hstart, hstop, block_offset,sub_block_offset - * Note: full_bw is nothing but sampling interval - * of stream. - * payload_bw is serving size no. - * of channels * bps per stream - */ - full_bw = sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate; - payload_bw = - sdw_mstr_bs_rt->stream_params.bps * - sdw_mstr_bs_rt->stream_params.channel_count; + if (i == num) + rates[group_count++] = + sdw_mstr_rt->stream_params.rate; + } + } + } - hwidth = (sel_col * payload_bw + full_bw - 1)/full_bw; - column_needed += hwidth; + /* check for number of streams and number of group count */ + if (group_count == 0) + return 0; - /* - * These needs to be done only for - * 1st entry in link list - */ - if (!hstop_flag) { - hstop = sel_col - 1; - hstop_flag = true; - } + /* Allocate temporary memory holding temp variables */ + temp = kzalloc((sizeof(struct temp_elements) * group_count), + GFP_KERNEL); + if (!temp) + return -ENOMEM; - /* Assumption: Only block per port is supported - * For blockperport: - * offset1 value = LSB 8 bits of block_offset value - * offset2 value = MSB 8 bits of block_offset value - * For blockperchannel: - * offset1 = LSB 8 bit of block_offset value - * offset2 = MSB 8 bit of sub_block_offload value - * if hstart and hstop of different streams in - * master are different, then block_offset is zero. - * if not then block_offset value for 2nd stream - * is block_offset += payload_bw - */ + /* Calculate full bandwidth per group */ + for (i = 0; i < group_count; i++) { + element = &temp[i]; + element->rate = rates[i]; + element->full_bw = sdw_mstr_bs->clk_freq/element->rate; + } - t_params->hstop = hstop; -#ifdef CONFIG_SND_SOC_SVFPGA - /* For PDM capture, 0th col is also used */ - t_params->hstart = 0; -#else - t_params->hstart = hstop - hwidth + 1; -#endif + /* Calculate payload bandwidth per group */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { - /* - * TBD: perform this when you have 2 ports - * and accordingly configure hstart hstop for slave - * removing for now - */ -#if 0 - hstop = hstop - hwidth; -#endif - /* Since one port per bus runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ + if (sdw_mstr_rt->mstr == NULL) break; - } - /* - * Run loop for slave_rt_list for given master_list - * to compute hstart hstop for slave - */ - list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + /* should not compute any transport params */ + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; - if (slv_rt->slave == NULL) - break; + for (i = 0; i < group_count; i++) { + element = &temp[i]; + if (sdw_mstr_rt->stream_params.rate == element->rate) { + element->payload_bw += + sdw_mstr_rt->stream_params.bps * + sdw_mstr_rt->stream_params.channel_count; + } - if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* Any of stream rate should match */ + if (i == group_count) + return -EINVAL; + } + } - list_for_each_entry(port_slv_rt, - &slv_rt->port_rt_list, port_node) { + /* Calculate hwidth per group and total column needed per master */ + for (i = 0; i < group_count; i++) { + element = &temp[i]; + element->hwidth = + (sel_col * element->payload_bw + + element->full_bw - 1)/element->full_bw; + column_needed += element->hwidth; + } - t_slv_params = &port_slv_rt->transport_params; - t_slv_params->num = port_slv_rt->port_num; - - /* - * TBD: Needs to be verifid for - * multiple combination - * 1. 1 master port, 1 slave rt, - * 1 port per slave rt --> - * In this case, use hstart hstop same as master - * for 1 slave rt - * 2. 1 master port, 2 slave rt, - * 1 port per slave rt --> - * In this case, use hstart hstop same as master - * for 2 slave rt - * only offset will change for 2nd slave rt - * Current assumption is one port per rt, - * hence no multiple port combination - * considered. - */ - t_slv_params->hstop = hstop; - t_slv_params->hstart = hstop - hwidth + 1; - - /* Only BlockPerPort supported */ - t_slv_params->blockpackingmode = 0; - t_slv_params->lanecontrol = 0; - - /* - * below copy needs to be changed when - * more than one port is supported - */ - if (t_params) - t_slv_params->sample_interval = - t_params->sample_interval; - - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - break; - } - - } - } - -#if 0 - /* TBD: To be verified */ + /* Check column required should not be greater than selected columns*/ if (column_needed > sel_col - 1) - return -EINVAL; /* Error case, check what has gone wrong */ -#endif + return -EINVAL; - return 0; -} + /* Compute hstop */ + hstop = sel_col - 1; + /* Run loop for all groups to compute transport parameters */ + for (i = 0; i < group_count; i++) { + port_block_offset = block_offset = 1; + element = &temp[i]; -/* - * sdw_compute_blk_subblk_offset - returns Success - * - * - * This function computes block offset and sub block - * offset for running streams per master & slaves. - */ -int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) -{ - struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; - struct sdw_transport_params *t_params, *t_slv_params; - struct sdw_slave_runtime *slv_rt = NULL; - struct sdw_port_runtime *port_rt, *port_slv_rt; - int hstart1 = 0, hstop1 = 0, hstart2 = 0, hstop2 = 0; - int block_offset = 1; + /* Find streams associated with each group */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + if (sdw_mstr_rt->mstr == NULL) + break; - /* Calculate block_offset and subblock_offset */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* should not compute any transport params */ + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; - if (sdw_mstr_bs_rt->mstr == NULL) - break; + if (sdw_mstr_rt->stream_params.rate != element->rate) + continue; - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* Compute hstart */ + sdw_mstr_rt->hstart = hstart = + hstop - element->hwidth + 1; + sdw_mstr_rt->hstop = hstop; - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { + /* Assign hstart, hstop, block offset for each port */ + list_for_each_entry(port_rt, + &sdw_mstr_rt->port_rt_list, port_node) { - t_params = &port_rt->transport_params; + t_params = &port_rt->transport_params; + t_params->num = port_rt->port_num; + t_params->hstart = hstart; + t_params->hstop = hstop; + t_params->offset1 = port_block_offset; + t_params->offset2 = port_block_offset >> 8; + /* Only BlockPerPort supported */ + t_params->blockgroupcontrol_valid = true; + t_params->blockgroupcontrol = 0x0; + t_params->lanecontrol = 0x0; + /* Copy parameters if first node */ + if (port_rt == list_first_entry + (&sdw_mstr_rt->port_rt_list, + struct sdw_port_runtime, port_node)) { - if ((!hstart2) && (!hstop2)) { - hstart1 = hstart2 = t_params->hstart; - hstop1 = hstop2 = t_params->hstop; - /* TBD: Verify this condition */ -#ifdef CONFIG_SND_SOC_SVFPGA - block_offset = 1; -#else - block_offset = 0; -#endif - } else { + sdw_mstr_rt->hstart = hstart; + sdw_mstr_rt->hstop = hstop; - hstart1 = t_params->hstart; - hstop1 = t_params->hstop; + sdw_mstr_rt->block_offset = + port_block_offset; -#ifndef CONFIG_SND_SOC_SVFPGA - /* hstart/stop not same */ - if ((hstart1 != hstart2) && - (hstop1 != hstop2)) { - /* TBD: Harcoding to 0, to be removed*/ - block_offset = 0; - } else { - /* TBD: Harcoding to 0, to be removed*/ - block_offset = 0; - } -#else - if ((hstart1 != hstart2) && - (hstop1 != hstop2)) { - block_offset = 1; - } else { -/* We are doing loopback for the Aggregation so block offset should - * always remain same. This is not a requirement. This we are doing - * to test aggregation without codec. - */ -#ifdef CONFIG_SND_SOC_MXFPGA - block_offset = 1; -#else - block_offset += - (sdw_mstr_bs_rt->stream_params. - bps - * - sdw_mstr_bs_rt->stream_params. - channel_count); -#endif } -#endif - } + /* Get no. of channels running on curr. port */ + ch_mask = port_rt->channel_mask; + no_of_channels = (((ch_mask >> 3) & 1) + + ((ch_mask >> 2) & 1) + + ((ch_mask >> 1) & 1) + + (ch_mask & 1)); - /* - * TBD: Hardcding block control group as true, - * to be changed later - */ - t_params->blockgroupcontrol_valid = true; - t_params->blockgroupcontrol = 0x0; /* Hardcoding to 0 */ + port_block_offset += + sdw_mstr_rt->stream_params.bps * + no_of_channels; + } + + /* Compute block offset */ + block_offset += sdw_mstr_rt->stream_params.bps * + sdw_mstr_rt->stream_params.channel_count; /* - * Since one port per bus runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support + * Re-assign port_block_offset for next stream + * under same group */ - break; + port_block_offset = block_offset; } - /* - * Run loop for slave_rt_list for given master_list - * to compute block and sub block offset for slave - */ + /* Compute hstop for next group */ + hstop = hstop - element->hwidth; + } + + /* Compute transport params for slave */ + + /* Run loop for master runtime streams running on master */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + /* Get block offset from master runtime */ + port_block_offset = sdw_mstr_rt->block_offset; + + /* Run loop for slave per master runtime */ list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + &sdw_mstr_rt->slv_rt_list, slave_node) { if (slv_rt->slave == NULL) break; @@ -1348,139 +1228,72 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) continue; + /* Run loop for each port of slave */ list_for_each_entry(port_slv_rt, &slv_rt->port_rt_list, port_node) { t_slv_params = &port_slv_rt->transport_params; + t_slv_params->num = port_slv_rt->port_num; - /* - * TBD: Needs to be verifid for - * multiple combination - * 1. 1 master port, 1 slave rt, - * 1 port per slave rt --> - * In this case, use block_offset same as - * master for 1 slave rt - * 2. 1 master port, 2 slave rt, - * 1 port per slave rt --> - * In this case, use block_offset same as - * master for 1st slave rt and compute for 2nd. - */ - - /* - * Current assumption is one port per rt, - * hence no multiple port combination. - * TBD: block offset to be computed for - * more than 1 slave_rt list. - */ - t_slv_params->offset1 = block_offset; - t_slv_params->offset2 = block_offset >> 8; - + /* Assign transport parameters */ + t_slv_params->hstart = sdw_mstr_rt->hstart; + t_slv_params->hstop = sdw_mstr_rt->hstop; + t_slv_params->offset1 = port_block_offset; + t_slv_params->offset2 = port_block_offset >> 8; - /* - * TBD: Hardcding block control group as true, - * to be changed later - */ + /* Only BlockPerPort supported */ t_slv_params->blockgroupcontrol_valid = true; - /* Hardcoding to 0 */ t_slv_params->blockgroupcontrol = 0x0; - /* Since one port per slave runtime, - * breaking port_list loop - * TBD:to be extended for multiple port support - */ - break; + t_slv_params->lanecontrol = 0x0; + + /* Get no. of channels running on curr. port */ + ch_mask = port_slv_rt->channel_mask; + no_of_channels = (((ch_mask >> 3) & 1) + + ((ch_mask >> 2) & 1) + + ((ch_mask >> 1) & 1) + + (ch_mask & 1)); + + /* Increment block offset for next port/slave */ + port_block_offset += slv_rt->stream_params.bps * + no_of_channels; } } } - return 0; -} - - -/* - * sdw_configure_frmshp_bnkswtch - returns Success - * -EINVAL - In case of error. - * - * - * This function broadcast frameshape on framectrl - * register and performs bank switch. - */ -int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) -{ - struct sdw_msg wr_msg; - int ret = 0; - int banktouse, numcol, numrow; - u8 wbuf[1] = {0}; - - numcol = sdw_get_col_to_num(col); - numrow = sdw_get_row_to_num(row); - - wbuf[0] = numcol | (numrow << 3); - /* Get current bank in use from bus structure*/ - banktouse = mstr_bs->active_bank; - banktouse = !banktouse; - - if (banktouse) { - wr_msg.addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + - (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ - } else { - - wr_msg.addr = SDW_SCP_FRAMECTRL + - (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ - } - - wr_msg.ssp_tag = 0x1; - wr_msg.flag = SDW_MSG_FLAG_WRITE; - wr_msg.len = 1; - wr_msg.slave_addr = 0xF; /* Broadcast address*/ - wr_msg.buf = wbuf; - wr_msg.addr_page1 = 0x0; - wr_msg.addr_page2 = 0x0; - - - ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); - if (ret != 1) { - ret = -EINVAL; - dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); - goto out; - } - - msleep(100); /* TBD: Remove this */ - - /* - * TBD: check whether we need to poll on - * mcp active bank bit to switch bank - */ - mstr_bs->active_bank = banktouse; - -out: + kfree(temp); - return ret; + return 0; } /* - * sdw_configure_frmshp_bnkswtch - returns Success + * sdw_cfg_frmshp_bnkswtch - returns Success * -EINVAL - In case of error. + * -ENOMEM - In case of memory alloc failure. + * -EAGAIN - In case of activity ongoing. * * * This function broadcast frameshape on framectrl * register and performs bank switch. */ -int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) +int sdw_cfg_frmshp_bnkswtch(struct sdw_bus *mstr_bs, bool is_wait) { + struct sdw_msg *wr_msg; int ret = 0; int banktouse, numcol, numrow; u8 *wbuf; - struct sdw_msg *wr_msg; wr_msg = kzalloc(sizeof(struct sdw_msg), GFP_KERNEL); - mstr_bs->async_data.msg = wr_msg; if (!wr_msg) return -ENOMEM; + + mstr_bs->async_data.msg = wr_msg; + wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); - if (!wbuf) - return -ENOMEM; - numcol = sdw_get_col_to_num(col); - numrow = sdw_get_row_to_num(row); + if (!wbuf) + return -ENOMEM; + + numcol = sdw_get_col_to_num(mstr_bs->col); + numrow = sdw_get_row_to_num(mstr_bs->row); wbuf[0] = numcol | (numrow << 3); /* Get current bank in use from bus structure*/ @@ -1504,23 +1317,34 @@ int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) wr_msg->addr_page1 = 0x0; wr_msg->addr_page2 = 0x0; - if (in_atomic() || irqs_disabled()) { - ret = sdw_trylock_mstr(mstr_bs->mstr); - if (!ret) { - /* SDW activity is ongoing. */ - ret = -EAGAIN; + if (is_wait) { + + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr_bs->mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else + sdw_lock_mstr(mstr_bs->mstr); + + ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, + 1, &mstr_bs->async_data); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } + } else { - sdw_lock_mstr(mstr_bs->mstr); - } + ret = sdw_slave_transfer(mstr_bs->mstr, wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } - ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, - 1, &mstr_bs->async_data); - if (ret != 1) { - ret = -EINVAL; - dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); - goto out; } msleep(100); /* TBD: Remove this */ @@ -1531,12 +1355,25 @@ int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) */ mstr_bs->active_bank = banktouse; + if (!is_wait) { + kfree(mstr_bs->async_data.msg->buf); + kfree(mstr_bs->async_data.msg); + } + + out: return ret; } -int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) +/* + * sdw_cfg_frmshp_bnkswtch_wait - returns Success + * -ETIMEDOUT - In case of timeout + * + * This function waits on completion of + * bank switch. + */ +int sdw_cfg_frmshp_bnkswtch_wait(struct sdw_bus *mstr_bs) { unsigned long time_left; struct sdw_master *mstr = mstr_bs->mstr; @@ -1556,7 +1393,7 @@ int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) } /* - * sdw_cfg_bs_params - returns Success + * sdw_config_bs_prms - returns Success * -EINVAL - In case of error. * * @@ -1566,61 +1403,31 @@ int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. * */ -int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt, - bool is_strm_cpy) +int sdw_config_bs_prms(struct sdw_bus *sdw_mstr_bs, bool state_check) { struct port_chn_en_state chn_en; struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt = NULL; struct sdw_mstr_driver *ops; int banktouse, ret = 0; list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + &sdw_mstr->mstr_rt_list, mstr_node) { if (sdw_mstr_bs_rt->mstr == NULL) continue; - if (is_strm_cpy) { - /* - * Configure and enable all slave - * transport params first - */ - ret = sdw_cfg_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "slave config params failed\n"); - return ret; - } - - /* Configure and enable all master params */ - ret = sdw_cfg_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "master config params failed\n"); - return ret; - } - - } else { - - /* - * 7.1 Copy all slave transport and port params - * to alternate bank - * 7.2 copy all master transport and port params - * to alternate bank - */ - ret = sdw_cpy_params_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "slave/master copy params failed\n"); - return ret; - } + /* + * Configure transport and port params + * for master and slave ports. + */ + ret = sdw_cfg_params_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, state_check); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave/master config params failed\n"); + return ret; } /* Get master driver ops */ @@ -1631,14 +1438,14 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, banktouse = !banktouse; /* - * TBD: Currently harcoded SSP interval to 50, + * TBD: Currently harcoded SSP interval, * computed value to be taken from system_interval in * bus data structure. * Add error check. */ if (ops->mstr_ops->set_ssp_interval) ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, - 50, banktouse); + SDW_DEFAULT_SSP, banktouse); /* * Configure Clock @@ -1646,7 +1453,7 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, */ if (ops->mstr_ops->set_clock_freq) ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, - sdw_mstr_bs->clk_freq, banktouse); + sdw_mstr_bs->clk_div, banktouse); /* Enable channel on alternate bank for running streams */ chn_en.is_activate = true; @@ -1676,10 +1483,10 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, * bank is enabled. * */ -int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt) +int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt = NULL; struct port_chn_en_state chn_en; int ret = 0; @@ -1762,9 +1569,6 @@ int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; -#ifdef CONFIG_SND_SOC_MXFPGA - sdw_slv_dpn_cap->prepare_ch = 0; -#endif if (prep) { /* PREPARE */ /* @@ -1998,15 +1802,8 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, slv_rt_strm, port_slv_strm, is_prep); if (ret < 0) return ret; + } - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } - - break; } list_for_each_entry(mstr_rt_strm, @@ -2022,12 +1819,6 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, mstr_rt_strm, port_mstr_strm, is_prep); if (ret < 0) return ret; - - /* Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } } @@ -2050,852 +1841,1257 @@ struct sdw_bus *master_to_bus(struct sdw_master *mstr) return NULL; } -/** - * sdw_bus_calc_bw - returns Success +/* + * sdw_chk_strm_prms - returns Success * -EINVAL - In case of error. * * - * This function is called from sdw_prepare_and_enable - * whenever new stream is processed. The function based - * on the stream associated with controller calculates - * required bandwidth, clock, frameshape, computes - * all transport params for a given port, enable channel - * & perform bankswitch. + * This function performs all the required + * check such as isynchronous mode support, + * stream rates etc. This API is called + * from sdw_bus_calc_bw API. + * */ -int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +int sdw_chk_strm_prms(struct sdw_master_capabilities *sdw_mstr_cap, + struct sdw_stream_params *mstr_params, + struct sdw_stream_params *stream_params) +{ + /* Asynchronous mode not supported, return Error */ + if (((sdw_mstr_cap->base_clk_freq * 2) % mstr_params->rate) != 0) + return -EINVAL; + + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) + return -EINVAL; + + return 0; +} + +/* + * sdw_compute_bs_prms - returns Success + * -EINVAL - In case of error. + * + * + * This function performs master/slave transport + * params computation. This API is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * + */ +int sdw_compute_bs_prms(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt) { - struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; - struct sdw_stream_params *stream_params = &sdw_rt->stream_params; - struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; - struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; - struct sdw_stream_params *mstr_params; - int stream_frame_size; - int frame_interval = 0, sel_row = 0, sel_col = 0; - int ret = 0; - bool last_node = false; - struct sdw_master_port_ops *ops; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + int ret = 0, frame_interval = 0; + + sdw_mstr_cap = &sdw_mstr->mstr_capabilities; - /* TBD: Add PCM/PDM flag in sdw_config_stream */ + ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, + sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); + return ret; + } /* - * TBD: check for mstr_rt is in configured state or not - * If yes, then configure masters as well - * If no, then do not configure/enable master related parameters + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. */ - /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (sdw_mstr_rt->mstr == NULL) - break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ - /* Get bus structure for master */ - sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + return ret; + } - /* - * All data structures required available, - * lets calculate BW for master controller - */ + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + return ret; + } - /* Check for isochronous mode plus other checks if required */ - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; + return 0; +} - if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && - (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) - goto enable_stream; +/* + * sdw_bs_pre_bnkswtch_post - returns Success + * -EINVAL or ret value - In case of error. + * + * This API performs on of the following operation + * based on bs_state value: + * pre-activate port + * bank switch operation + * post-activate port + * bankswitch wait operation + * disable channel operation + */ +int sdw_bs_pre_bnkswtch_post(struct sdw_runtime *sdw_rt, int bs_state) +{ + struct sdw_mstr_runtime *mstr_rt_act = NULL; + struct sdw_bus *mstr_bs_act = NULL; + struct sdw_master_port_ops *ops; + int ret = 0; - /* we do not support asynchronous mode Return Error */ - if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) != 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Async mode not supported\n"); - return -EINVAL; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - /* Check for sampling frequency */ - if (stream_params->rate != mstr_params->rate) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Sample frequency mismatch\n"); + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + if (!mstr_bs_act) return -EINVAL; - } + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; /* - * Calculate stream bandwidth, frame size and - * total BW required for master controller + * Note that current all the operations + * of pre->bankswitch->post->wait->disable + * are performed sequentially.The switch case + * is kept in order for code to scale where + * pre->bankswitch->post->wait->disable are + * not sequential and called from different + * instances. */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + switch (bs_state) { - sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; - - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); - return ret; + case SDW_UPDATE_BS_PRE: + /* Pre activate ports */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + break; + case SDW_UPDATE_BS_BNKSWTCH: + /* Configure Frame Shape/Switch Bank */ + ret = sdw_cfg_frmshp_bnkswtch(mstr_bs_act, true); + if (ret < 0) + return ret; + break; + case SDW_UPDATE_BS_POST: + /* Post activate ports */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + break; + case SDW_UPDATE_BS_BNKSWTCH_WAIT: + /* Post Bankswitch wait operation */ + ret = sdw_cfg_frmshp_bnkswtch_wait(mstr_bs_act); + if (ret < 0) + return ret; + break; + case SDW_UPDATE_BS_DIS_CHN: + /* Disable channel on previous bank */ + ret = sdw_dis_chan(mstr_bs_act); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + break; } + } - /* - * TBD: find right place to run sorting on - * master rt_list. Below sorting is done based on - * bps from low to high, that means PDM streams - * will be placed before PCM. - */ + return ret; - /* - * TBD Should we also perform sorting based on rate - * for PCM stream check. if yes then how?? - * creating two different list. - */ +} + +/* + * sdw_update_bs_prms - returns Success + * -EINVAL - In case of error. + * + * Once all the parameters are configured + * for ports, this function performs bankswitch + * where all the new configured parameters + * gets in effect. This function is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * This function also disables all the channels + * enabled on previous bank after bankswitch. + */ +int sdw_update_bs_prms(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, + int last_node) +{ + + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + int ret = 0; + + /* + * Optimization scope. + * Check whether we can assign function pointer + * link sync value is 1, and call that function + * if its not NULL. + */ + if ((last_node) && (sdw_mstr->link_sync_mask)) { - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, - frame_interval); + /* Perform pre-activate ports */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_PRE); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + dev_err(&sdw_mstr->dev, "Pre-activate port failed\n"); return ret; } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + /* Perform bankswitch operation*/ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_BNKSWTCH); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + dev_err(&sdw_mstr->dev, "Bank Switch operation failed\n"); return ret; } - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + /* Perform post-activate ports */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_POST); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + dev_err(&sdw_mstr->dev, "Pre-activate port failed\n"); return ret; } - /* Change Stream State */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; - - /* Configure bus parameters */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + /* Perform bankswitch post wait opearation */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, + SDW_UPDATE_BS_BNKSWTCH_WAIT); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport param config failed\n"); + dev_err(&sdw_mstr->dev, "BnkSwtch wait op failed\n"); return ret; } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; - - if ((last_node) && (sdw_mstr->link_sync_mask)) { - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - mstr_bs_act = master_to_bus( - mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - /* Disable all channels - * enabled on previous bank - */ - ret = sdw_dis_chan(mstr_bs_act, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled faile\n"); - return ret; - } - } - } - if (!sdw_mstr->link_sync_mask) { - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); - return ret; - } - } - /* Prepare new port for master and slave */ - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + /* Disable channels on previous bank */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_DIS_CHN); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } - /* change stream state to prepare */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; } -enable_stream: - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { - - - if (sdw_mstr_rt->mstr == NULL) - break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; - - /* Get bus structure for master */ - sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; - - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; - if ((!enable) || - (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) - return 0; + if (!sdw_mstr->link_sync_mask) { - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + /* Configure Frame Shape/Switch Bank */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Enable new port for master and slave */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } + } - /* change stream state to enable */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + return ret; +} - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; +/** + * sdw_chk_last_node - returns True or false + * + * This function returns true in case of last node + * else returns false. + */ +bool sdw_chk_last_node(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + struct sdw_mstr_runtime *last_rt = NULL; - if ((last_node) && (sdw_mstr->link_sync_mask)) { + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + return true; + else + return false; +} - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { +/** + * sdw_unprepare_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to unprepare ports and does recomputation of + * bus parameters. + */ +int sdw_unprepare_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_stream_params *mstr_params; + bool last_node = false; + int ret = 0; - if (mstr_rt_act->mstr == NULL) - break; + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + mstr_params = &sdw_mstr_rt->stream_params; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* 1. Un-prepare master and slave port */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); + return ret; + } - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* change stream state to unprepare */ + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr->dev, "BW calculation failed\n"); + return -EINVAL; + } + if (!sdw_mstr_bs->bandwidth) { + /* + * Last stream on master should + * return successfully + */ + sdw_mstr_bs->system_interval = 0; + sdw_mstr_bs->stream_interval = 0; + sdw_mstr_bs->frame_freq = 0; + sdw_mstr_bs->row = 0; + sdw_mstr_bs->col = 0; + return 0; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Compute transport params */ + ret = sdw_compute_bs_prms(sdw_mstr_bs, sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Params computation failed\n"); + return -EINVAL; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Configure bus params */ + ret = sdw_config_bs_prms(sdw_mstr_bs, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + return ret; +} - if (mstr_rt_act->mstr == NULL) - break; +/** + * sdw_disable_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to disable ports. + */ +int sdw_disable_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + bool last_node = false; + int ret = 0; - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + /* Lets do disabling of port for stream to be freed */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch dis failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Change stream state to disable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + ret = sdw_config_bs_prms(sdw_mstr_bs, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + return ret; +} - /* Disable all channels - * enabled on previous bank - */ - ret = sdw_dis_chan(mstr_bs_act, - sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, - "Channel disabled faile\n"); - return ret; - } - } - } - if (!sdw_mstr->link_sync_mask) { - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch( - sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch disabled failed\n"); - return ret; - } - } +/** + * sdw_enable_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to enable ports. + */ +int sdw_enable_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + bool last_node = false; + int ret = 0; + + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + + ret = sdw_config_bs_prms(sdw_mstr_bs, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; } - return 0; + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + return ret; + } + + /* change stream state to enable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); + + return ret; } -EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); /** - * sdw_bus_calc_bw_dis - returns Success + * sdw_prepare_op - returns Success * -EINVAL - In case of error. * - * - * This function is called from sdw_disable_and_unprepare - * whenever stream is ended. The function based disables/ - * unprepare port/channel of associated stream and computes - * required bandwidth, clock, frameshape, computes - * all transport params for a given port, enable channel - * & perform bankswitch for remaining streams on given - * controller. + * This function perform all operations required + * to prepare ports and does computation of + * bus parameters. */ -int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +int sdw_prepare_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) { - struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; - struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; - struct sdw_master *sdw_mstr = NULL; + struct sdw_stream_params *stream_params = &sdw_rt->stream_params; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; - int stream_frame_size; - int frame_interval = 0, sel_row = 0, sel_col = 0; - int ret = 0; + bool last_node = false; - struct sdw_master_port_ops *ops; + int ret = 0; - /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + /* + * check all the stream parameters received + * Check for isochronous mode, sample rate etc + */ + ret = sdw_chk_strm_prms(sdw_mstr_cap, mstr_params, + stream_params); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Stream param check failed\n"); + return -EINVAL; + } + + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + + /* Compute transport params */ + ret = sdw_compute_bs_prms(sdw_mstr_bs, sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Params computation failed\n"); + return -EINVAL; + } + + /* Configure bus parameters */ + ret = sdw_config_bs_prms(sdw_mstr_bs, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport param config failed\n"); + return ret; + } + + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); + + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + return ret; + } + + /* change stream state to prepare */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + + + return ret; +} + +/** + * sdw_pre_en_dis_unprep_op - returns Success + * -EINVAL - In case of error. + * + * This function is called by sdw_bus_calc_bw + * and sdw_bus_calc_bw_dis to prepare, enable, + * unprepare and disable ports. Based on state + * value, individual APIs are called. + */ +int sdw_pre_en_dis_unprep_op(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt, int state) +{ + struct sdw_master *sdw_mstr = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + int ret = 0; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; + + sdw_mstr = sdw_mstr_bs->mstr; + + /* + * All data structures required available, + * lets calculate BW for master controller + */ + + switch (state) { + + case SDW_STATE_PREPARE_STREAM: /* Prepare */ + ret = sdw_prepare_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_ENABLE_STREAM: /* Enable */ + ret = sdw_enable_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_DISABLE_STREAM: /* Disable */ + ret = sdw_disable_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_UNPREPARE_STREAM: /* UnPrepare */ + ret = sdw_unprepare_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + default: + ret = -EINVAL; + break; + + } + + return ret; +} + +/** + * sdw_bus_calc_bw - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_prepare_and_enable + * whenever new stream is processed. The function based + * on the stream associated with controller calculates + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch. + */ +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +{ + + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + int ret = 0; + + + /* + * TBD: check for mstr_rt is in configured state or not + * If yes, then configure masters as well + * If no, then do not configure/enable master related parameters + */ + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; + if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && + (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) + goto enable_stream; /* Get bus structure for master */ sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; + if (!sdw_mstr_bs) + return -EINVAL; + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_PREPARE_STREAM); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Prepare Operation failed\n"); + return -EINVAL; + } + } - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; +enable_stream: - if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) - goto unprepare_stream; + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Lets do disabling of port for stream to be freed */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + if (sdw_mstr_rt->mstr == NULL) + break; - /* - * Disable channel for slave and - * master on current bank - */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch dis failed\n"); - return ret; - } + if ((!enable) || + (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) + return 0; + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; - /* Change stream state to disable */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; - } + sdw_mstr = sdw_mstr_bs->mstr; - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_ENABLE_STREAM); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); - return ret; + dev_err(&sdw_mstr->dev, "Enable Operation failed\n"); + return -EINVAL; } + } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; - - if ((last_node) && (sdw_mstr->link_sync_mask)) { - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } +/** + * sdw_bus_calc_bw_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_disable_and_unprepare + * whenever stream is ended. The function based disables/ + * unprepare port/channel of associated stream and computes + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch for remaining streams on given + * controller. + */ +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +{ + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + int ret = 0; - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } + if (sdw_mstr_rt->mstr == NULL) + break; - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) + goto unprepare_stream; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - } - if (!sdw_mstr->link_sync_mask) { + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_DISABLE_STREAM); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); - return ret; + dev_err(&sdw_mstr->dev, "Disable Operation failed\n"); + return -EINVAL; } } + unprepare_stream: list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; + if ((!unprepare) || + (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) + return 0; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; - - /* Get bus structure for master */ sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_UNPREPARE_STREAM); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Unprepare Operation failed\n"); + return -EINVAL; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; +/* + * sdw_slv_dp0_en_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable Slave DP0 channels. + */ +int sdw_slv_dp0_en_dis(struct sdw_bus *mstr_bs, + bool is_enable, u8 slv_number) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; - if ((!unprepare) || - (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) - return 0; + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; - /* 1. Un-prepare master and slave port */ - list_for_each_entry(sdw_mstr_bs_rt, &sdw_mstr->mstr_rt_list, - mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) - continue; - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); - return ret; - } + rd_msg.addr = wr_msg.addr = ((SDW_DPN_CHANNELEN + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * + 0x0)); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; - /* change stream state to unprepare */ - if (last_node) - sdw_rt->stream_state = - SDW_STATE_UNPREPARE_STREAM; - } + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; - /* - * Calculate new bandwidth, frame size - * and total BW required for master controller - */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } - sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; + if (is_enable) + wbuf[0] = (rbuf[0] | 0x1); + else + wbuf[0] = (rbuf[0] & ~(0x1)); - /* Something went wrong in bandwidth calulation */ - if (sdw_mstr_bs->bandwidth < 0) { - dev_err(&sdw_mstr->dev, "BW calculation failed\n"); - return -EINVAL; - } + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } - if (!sdw_mstr_bs->bandwidth) { - /* - * Last stream on master should - * return successfully - */ - if (last_node) - sdw_rt->stream_state = - SDW_STATE_UNCOMPUTE_STREAM; - continue; - } + rbuf[0] = 0; + /* This is just status read, can be removed later */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } +out: + return ret; - ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, - &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "clock/frameshape failed\n"); +} + + +/* + * sdw_mstr_dp0_act_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable Master DP0 channels. + */ +int sdw_mstr_dp0_act_dis(struct sdw_bus *mstr_bs, bool is_enable) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_activate_ch activate_ch; + int banktouse, ret = 0; + + activate_ch.num = 0; + activate_ch.ch_mask = 0x1; + activate_ch.activate = is_enable; /* Enable/Disable */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* 1. Master port enable_ch_pre */ + if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_pre + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) return ret; - } + } - /* Compute new transport params for running streams */ - /* No sorting required here */ + /* 2. Master port enable */ + if (ops->mstr_port_ops->dpn_port_activate_ch) { + ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, + &activate_ch, banktouse); + if (ret < 0) + return ret; + } - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, - frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute SI failed\n"); + /* 3. Master port enable_ch_post */ + if (ops->mstr_port_ops->dpn_port_activate_ch_post) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_post + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) return ret; - } + } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute hstart/hstop fail\n"); + return 0; +} + +/* + * sdw_slv_dp0_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare Slave DP0. + */ +int sdw_slv_dp0_prep_unprep(struct sdw_bus *mstr_bs, + u8 slv_number, bool prepare) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* Read SDW_DPN_PREPARECTRL register */ + rd_msg.addr = wr_msg.addr = SDW_DPN_PREPARECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0x0); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + if (prepare) + wbuf[0] = (rbuf[0] | 0x1); + else + wbuf[0] = (rbuf[0] & ~(0x1)); + + /* + * TBD: poll for prepare interrupt bit + * before calling post_prepare + * 2. check capabilities if simplified + * CM no need to prepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * Sleep for 100ms. + * TODO: check on check on prepare status for port_ready + */ + msleep(100); + +out: + return ret; + +} + +/* + * sdw_mstr_dp0_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare Master DP0. + */ +int sdw_mstr_dp0_prep_unprep(struct sdw_bus *mstr_bs, + bool prep) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_prepare_ch prep_ch; + int ret = 0; + + prep_ch.num = 0x0; + prep_ch.ch_mask = 0x1; + prep_ch.prepare = prep; /* Prepare/Unprepare */ + + /* 1. Master port prepare_ch_pre */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_pre + (mstr_bs->mstr, &prep_ch); + if (ret < 0) return ret; - } + } + + /* 2. Master port prepare */ + if (ops->mstr_port_ops->dpn_port_prepare_ch) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 3. Master port prepare_ch_post */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_post) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_post + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + return 0; +} + +static int sdw_bra_config_ops(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, + struct sdw_transport_params *t_params, + struct sdw_port_params *p_params) +{ + struct sdw_mstr_driver *ops; + int ret, banktouse; + + /* configure Master transport params */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master xport params config failed\n"); + return ret; + } + + /* configure Slave transport params */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, t_params, + p_params, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave xport params config failed\n"); + return ret; + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* Configure SSP */ + banktouse = sdw_mstr_bs->active_bank; + banktouse = !banktouse; - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ops->mstr_ops->set_ssp_interval) { + ret = ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, + 24, banktouse); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: SSP interval config failed\n"); return ret; } + } - /* Configure bus params */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + /* Configure Clock */ + if (ops->mstr_ops->set_clock_freq) { + ret = ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, + sdw_mstr_bs->clk_div, banktouse); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Clock config failed\n"); return ret; } - if ((last_node) && (sdw_mstr->link_sync_mask)) { - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + } - if (mstr_rt_act->mstr == NULL) - break; + return 0; +} - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); +static int sdw_bra_xport_config_enable(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, + struct sdw_transport_params *t_params, + struct sdw_port_params *p_params) +{ + int ret; - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* Prepare sequence */ + ret = sdw_bra_config_ops(sdw_mstr_bs, block, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: config operation failed\n"); + return ret; + } - /* - * Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* + * TODO: There may be some slave which doesn't support + * prepare for DP0. We have two options here. + * 1. Just call prepare and ignore error from those + * codec who doesn't support prepare for DP0. + * 2. Get slave capabilities and based on prepare DP0 + * support, Program Slave prepare register. + * Currently going with approach 1, not checking return + * value. + * 3. Try to use existing prep_unprep API both for master + * and slave. + */ + sdw_slv_dp0_prep_unprep(sdw_mstr_bs, block->slave_addr, true); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus( - mstr_rt_act->mstr); - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, - "bank switch failed\n"); - return ret; - } - } + /* Prepare Master port */ + ret = sdw_mstr_dp0_prep_unprep(sdw_mstr_bs, true); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master prepare failed\n"); + return ret; + } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + /* Enable sequence */ + ret = sdw_bra_config_ops(sdw_mstr_bs, block, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: config operation failed\n"); + return ret; + } + /* Enable DP0 channel (Slave) */ + ret = sdw_slv_dp0_en_dis(sdw_mstr_bs, true, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave DP0 enable failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Enable DP0 channel (Master) */ + ret = sdw_mstr_dp0_act_dis(sdw_mstr_bs, true); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master DP0 enable failed\n"); + return ret; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + return 0; +} - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { +static int sdw_bra_xport_config_disable(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block) +{ + int ret; - if (mstr_rt_act->mstr == NULL) - break; + /* Disable DP0 channel (Slave) */ + ret = sdw_slv_dp0_en_dis(sdw_mstr_bs, false, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave DP0 disable failed\n"); + return ret; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - } - if (!sdw_mstr->link_sync_mask) { - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } + /* Disable DP0 channel (Master) */ + ret = sdw_mstr_dp0_act_dis(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master DP0 disable failed\n"); + return ret; + } + + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } + /* + * TODO: There may be some slave which doesn't support + * de-prepare for DP0. We have two options here. + * 1. Just call prepare and ignore error from those + * codec who doesn't support de-prepare for DP0. + * 2. Get slave capabilities and based on prepare DP0 + * support, Program Slave prepare register. + * Currently going with approach 1, not checking return + * value. + */ + sdw_slv_dp0_prep_unprep(sdw_mstr_bs, block->slave_addr, false); + + /* De-prepare Master port */ + ret = sdw_mstr_dp0_prep_unprep(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master de-prepare failed\n"); + return ret; + } + + return 0; +} + +int sdw_bus_bra_xport_config(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, bool enable) +{ + struct sdw_transport_params t_params; + struct sdw_port_params p_params; + int ret; + + /* TODO: + * compute transport parameters based on current clock and + * frameshape. need to check how algorithm should be designed + * for BRA for computing clock, frameshape, SSP and transport params. + */ + + /* Transport Parameters */ + t_params.num = 0x0; /* DP 0 */ + t_params.blockpackingmode = 0x0; + t_params.blockgroupcontrol_valid = false; + t_params.blockgroupcontrol = 0x0; + t_params.lanecontrol = 0; + t_params.sample_interval = 10; + + t_params.hstart = 7; + t_params.hstop = 9; + t_params.offset1 = 0; + t_params.offset2 = 0; + + /* Port Parameters */ + p_params.num = 0x0; /* DP 0 */ + + /* Isochronous Mode */ + p_params.port_flow_mode = 0x0; + + /* Normal Mode */ + p_params.port_data_mode = 0x0; + + /* Word length */ + p_params.word_length = 3; + + /* Frameshape and clock params */ + sdw_mstr_bs->clk_div = 1; + sdw_mstr_bs->col = 10; + sdw_mstr_bs->row = 80; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + sdw_mstr_bs->bandwidth = 9.6 * 1000 * 1000; +#else + sdw_mstr_bs->bandwidth = 12 * 1000 * 1000; +#endif + + if (enable) { + ret = sdw_bra_xport_config_enable(sdw_mstr_bs, block, + &t_params, &p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Xport params config failed\n"); + return ret; } - /* Change stream state to uncompute */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + } else { + ret = sdw_bra_xport_config_disable(sdw_mstr_bs, block); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Xport params de-config failed\n"); return ret; } } return 0; } -EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 274966572499..9f8e77c20699 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -260,8 +260,9 @@ static int sdw_config_update(struct cnl_sdw *sdw) { struct cnl_sdw_data *data = &sdw->data; struct sdw_master *mstr = sdw->mstr; - + int sync_reg, syncgo_mask; volatile int config_update = 0; + volatile int sync_update = 0; /* Try 10 times before giving up on configuration update */ int timeout = 10; int config_updated = 0; @@ -271,6 +272,44 @@ static int sdw_config_update(struct cnl_sdw *sdw) /* Bit is self-cleared when configuration gets updated. */ cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE, config_update); + + /* + * Set SYNCGO bit for Master(s) running in aggregated mode + * (MMModeEN = 1). This action causes all gSyncs of all Master IPs + * to be unmasked and asserted at the currently active gSync rate. + * The initialization-pending Master IP SoundWire bus clock will + * start up synchronizing to gSync, leading to bus reset entry, + * subsequent exit, and 1st Frame generation aligning to gSync. + * Note that this is done in order to overcome hardware bug related + * to mis-alignment of gSync and frame. + */ + if (mstr->link_sync_mask) { + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + syncgo_mask = (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); + + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, + SDW_CNL_SYNC); + if ((sync_update & syncgo_mask) == 0) + break; + + msleep(20); + timeout--; + + } while (timeout); + + if ((sync_update & syncgo_mask) != 0) { + dev_err(&mstr->dev, "Failed to set sync go\n"); + return -EIO; + } + + /* Reset timeout */ + timeout = 10; + } + + /* Wait for config update bit to be self cleared */ do { config_update = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE); @@ -369,12 +408,10 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) int pdm_cap, pdm_ch_count, total_pdm_streams; int pdm_cap_offset = SDW_CNL_PDMSCAP + (data->inst_id * SDW_CNL_PDMSCAP_REG_OFFSET); - - pdm_cap = cnl_sdw_reg_readw(data->sdw_regs, pdm_cap_offset); + pdm_cap = cnl_sdw_reg_readw(data->sdw_shim, pdm_cap_offset); sdw->num_pdm_streams = (pdm_cap >> CNL_PDMSCAP_BSS_SHIFT) & CNL_PDMSCAP_BSS_MASK; - /* Zero based value in register */ - sdw->num_pdm_streams++; + sdw->pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -383,8 +420,7 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) sdw->num_in_pdm_streams = (pdm_cap >> CNL_PDMSCAP_ISS_SHIFT) & CNL_PDMSCAP_ISS_MASK; - /* Zero based value in register */ - sdw->num_in_pdm_streams++; + sdw->in_pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_in_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -395,7 +431,6 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) sdw->num_out_pdm_streams = (pdm_cap >> CNL_PDMSCAP_OSS_SHIFT) & CNL_PDMSCAP_OSS_MASK; /* Zero based value in register */ - sdw->num_out_pdm_streams++; sdw->out_pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_out_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -443,15 +478,13 @@ static int sdw_port_pdi_init(struct cnl_sdw *sdw) return ret; } -static int sdw_init(struct cnl_sdw *sdw) +static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) { struct sdw_master *mstr = sdw->mstr; struct cnl_sdw_data *data = &sdw->data; - int mcp_config, mcp_control, sync_reg; - + int mcp_config, mcp_control, sync_reg, mcp_clockctrl; volatile int sync_update = 0; - /* Try 10 times before timing out */ - int timeout = 10; + int timeout = 10; /* Try 10 times before timing out */ int ret = 0; /* Power up the link controller */ @@ -465,9 +498,11 @@ static int sdw_init(struct cnl_sdw *sdw) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); - /* Set the Sync period to default */ + /* Set SyncPRD period */ sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); + + /* Set SyncPU bit */ sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); @@ -484,6 +519,39 @@ static int sdw_init(struct cnl_sdw *sdw) return -EINVAL; } + /* + * Set CMDSYNC bit based on Master ID + * Note that this bit is set only for the Master which will be + * running in aggregated mode (MMModeEN = 1). By doing + * this the gSync to Master IP to be masked inactive. + * Note that this is done in order to overcome hardware bug related + * to mis-alignment of gSync and frame. + */ + if (mstr->link_sync_mask) { + + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (1 << (data->inst_id + CNL_SYNC_CMDSYNC_SHIFT)); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + } + + /* Set clock divider to default value in default bank */ + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL0); + mcp_clockctrl |= SDW_CNL_DEFAULT_CLK_DIVIDER; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CLOCKCTRL0, + mcp_clockctrl); + + /* Set the Frame shape init to default value */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, + SDW_CNL_DEFAULT_FRAME_SHAPE); + + + /* Set the SSP interval to default value for both banks */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, + SDW_CNL_DEFAULT_SSP_INTERVAL); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, + SDW_CNL_DEFAULT_SSP_INTERVAL); + /* Set command acceptance mode. This is required because when * Master broadcasts the clock_stop command to slaves, slaves * might be already suspended, so this return NO ACK, in that @@ -495,7 +563,6 @@ static int sdw_init(struct cnl_sdw *sdw) MCP_CONTROL_CMDACCEPTMODE_SHIFT); cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, 0x48); mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); /* Set Max cmd retry to 15 times */ @@ -541,22 +608,19 @@ static int sdw_init(struct cnl_sdw *sdw) MCP_CONFIG_OPERATIONMODE_SHIFT); cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); - /* Set the SSP interval to 32 for both banks */ - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, - SDW_CNL_DEFAULT_SSP_INTERVAL); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, - SDW_CNL_DEFAULT_SSP_INTERVAL); /* Initialize the phy control registers. */ sdw_init_phyctrl(sdw); - /* Initlaize the ports */ - ret = sdw_port_pdi_init(sdw); - if (ret) { - dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + if (is_first_init) { + /* Initlaize the ports */ + ret = sdw_port_pdi_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", data->inst_id); - sdw_power_down_link(sdw); - return ret; + sdw_power_down_link(sdw); + return ret; + } } /* Lastly enable interrupts */ @@ -604,7 +668,7 @@ static int sdw_alloc_pcm_stream(struct cnl_sdw *sdw, pdi_stream->h_ch_num = ch_cnt - 1; ch_map_offset = SDW_CNL_PCMSCHM + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr) + - (0x2 * pdi_stream->pdi_num); + (SDW_PCM_STRM_START_INDEX * pdi_stream->pdi_num); if (port->direction == SDW_DATA_DIR_IN) pdi_ch_map |= (CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); else @@ -1134,10 +1198,758 @@ static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, return ret; } +static void cnl_sdw_bra_prep_crc(u8 *txdata_buf, + struct sdw_bra_block *block, int data_offset, int addr_offset) +{ + + int addr = addr_offset; + + txdata_buf[addr++] = sdw_bus_compute_crc8((block->values + data_offset), + block->num_bytes); + txdata_buf[addr++] = 0x0; + txdata_buf[addr++] = 0x0; + txdata_buf[addr] |= ((0x2 & SDW_BRA_SOP_EOP_PDI_MASK) + << SDW_BRA_SOP_EOP_PDI_SHIFT); +} + +static void cnl_sdw_bra_prep_data(u8 *txdata_buf, + struct sdw_bra_block *block, int data_offset, int addr_offset) +{ + + int i; + int addr = addr_offset; + + for (i = 0; i < block->num_bytes; i += 2) { + + txdata_buf[addr++] = block->values[i + data_offset]; + if ((block->num_bytes - 1) - i) + txdata_buf[addr++] = block->values[i + data_offset + 1]; + else + txdata_buf[addr++] = 0; + + txdata_buf[addr++] = 0; + txdata_buf[addr++] = 0; + } +} + +static void cnl_sdw_bra_prep_hdr(u8 *txdata_buf, + struct sdw_bra_block *block, int rolling_id, int offset) +{ + + u8 tmp_hdr[6] = {0, 0, 0, 0, 0, 0}; + u8 temp = 0x0; + + /* + * 6 bytes header + * 1st byte: b11001010 + * b11: Header is active + * b0010: Device number 2 is selected + * b1: Write operation + * b0: MSB of BRA_NumBytes is 0 + * 2nd byte: LSB of number of bytes + * 3rd byte to 6th byte: Slave register offset + */ + temp |= (SDW_BRA_HDR_ACTIVE & SDW_BRA_HDR_ACTIVE_MASK) << + SDW_BRA_HDR_ACTIVE_SHIFT; + temp |= (block->slave_addr & SDW_BRA_HDR_SLV_ADDR_MASK) << + SDW_BRA_HDR_SLV_ADDR_SHIFT; + temp |= (block->cmd & SDW_BRA_HDR_RD_WR_MASK) << + SDW_BRA_HDR_RD_WR_SHIFT; + + if (block->num_bytes > SDW_BRA_HDR_MSB_BYTE_CHK) + temp |= (SDW_BRA_HDR_MSB_BYTE_SET & SDW_BRA_HDR_MSB_BYTE_MASK); + else + temp |= (SDW_BRA_HDR_MSB_BYTE_UNSET & + SDW_BRA_HDR_MSB_BYTE_MASK); + + txdata_buf[offset + 0] = tmp_hdr[0] = temp; + txdata_buf[offset + 1] = tmp_hdr[1] = block->num_bytes; + txdata_buf[offset + 3] |= ((SDW_BRA_SOP_EOP_PDI_STRT_VALUE & + SDW_BRA_SOP_EOP_PDI_MASK) << + SDW_BRA_SOP_EOP_PDI_SHIFT); + + txdata_buf[offset + 3] |= ((rolling_id & SDW_BRA_ROLLINGID_PDI_MASK) + << SDW_BRA_ROLLINGID_PDI_SHIFT); + + txdata_buf[offset + 4] = tmp_hdr[2] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK24) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT24); + + txdata_buf[offset + 5] = tmp_hdr[3] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK16) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT16); + + txdata_buf[offset + 8] = tmp_hdr[4] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK8) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT8); + + txdata_buf[offset + 9] = tmp_hdr[5] = (block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK0); + + /* CRC check */ + txdata_buf[offset + 0xc] = sdw_bus_compute_crc8(tmp_hdr, + SDW_BRA_HEADER_SIZE); + + if (!block->cmd) + txdata_buf[offset + 0xf] = ((SDW_BRA_SOP_EOP_PDI_END_VALUE & + SDW_BRA_SOP_EOP_PDI_MASK) << + SDW_BRA_SOP_EOP_PDI_SHIFT); +} + +static void cnl_sdw_bra_pdi_tx_config(struct sdw_master *mstr, + struct cnl_sdw *sdw, bool enable) +{ + struct cnl_sdw_pdi_stream tx_pdi_stream; + unsigned int tx_ch_map_offset, port_ctrl_offset, tx_pdi_config_offset; + unsigned int port_ctrl = 0, tx_pdi_config = 0, tx_stream_config; + int tx_pdi_ch_map = 0; + + if (enable) { + /* DP0 PORT CTRL REG */ + port_ctrl_offset = SDW_CNL_PORTCTRL + (SDW_BRA_PORT_ID * + SDW_CNL_PORT_REG_OFFSET); + + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl |= ((SDW_BRA_BULK_ENABLE & SDW_BRA_BLK_EN_MASK) << + SDW_BRA_BLK_EN_SHIFT); + + port_ctrl |= ((SDW_BRA_BPT_PAYLOAD_TYPE & + SDW_BRA_BPT_PYLD_TY_MASK) << + SDW_BRA_BPT_PYLD_TY_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, + port_ctrl); + + /* PDI0 Programming */ + tx_pdi_stream.l_ch_num = 0; + tx_pdi_stream.h_ch_num = 0xF; + tx_pdi_stream.pdi_num = SDW_BRA_PDI_TX_ID; + /* TODO: Remove hardcoding */ + tx_pdi_stream.sdw_pdi_num = mstr->nr * 16 + + tx_pdi_stream.pdi_num + 3; + + /* SNDWxPCMS2CM SHIM REG */ + tx_ch_map_offset = SDW_CNL_CTLS2CM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr); + + tx_pdi_ch_map |= (tx_pdi_stream.sdw_pdi_num & + CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + + tx_pdi_ch_map |= (tx_pdi_stream.l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + + tx_pdi_ch_map |= (tx_pdi_stream.h_ch_num & + CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + + cnl_sdw_reg_writew(sdw->data.sdw_shim, tx_ch_map_offset, + tx_pdi_ch_map); + + /* TX PDI0 CONFIG REG BANK 0 */ + tx_pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (tx_pdi_stream.pdi_num * 16)); + + tx_pdi_config |= ((SDW_BRA_PORT_ID & + PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + tx_pdi_config |= (SDW_BRA_CHN_MASK << + PDINCONFIG_CHANNEL_MASK_SHIFT); + + tx_pdi_config |= (SDW_BRA_SOFT_RESET << + PDINCONFIG_PORT_SOFT_RESET_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, + tx_pdi_config_offset, tx_pdi_config); + + /* ALH STRMzCFG REG */ + tx_stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (tx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET)); + + tx_stream_config |= (CNL_STRMZCFG_DMAT_VAL & + CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + + tx_stream_config |= (0x0 & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + + cnl_sdw_reg_writel(sdw->data.alh_base, + (tx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET), + tx_stream_config); + + + } else { + + /* + * TODO: There is official workaround which needs to be + * performed for PDI config register. The workaround + * is to perform SoftRst twice in order to clear + * PDI fifo contents. + */ + + } +} + +static void cnl_sdw_bra_pdi_rx_config(struct sdw_master *mstr, + struct cnl_sdw *sdw, bool enable) +{ + + struct cnl_sdw_pdi_stream rx_pdi_stream; + unsigned int rx_ch_map_offset, rx_pdi_config_offset, rx_stream_config; + unsigned int rx_pdi_config = 0; + int rx_pdi_ch_map = 0; + + if (enable) { + + /* RX PDI1 Configuration */ + rx_pdi_stream.l_ch_num = 0; + rx_pdi_stream.h_ch_num = 0xF; + rx_pdi_stream.pdi_num = SDW_BRA_PDI_RX_ID; + rx_pdi_stream.sdw_pdi_num = mstr->nr * 16 + + rx_pdi_stream.pdi_num + 3; + + /* SNDWxPCMS3CM SHIM REG */ + rx_ch_map_offset = SDW_CNL_CTLS3CM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr); + + rx_pdi_ch_map |= (rx_pdi_stream.sdw_pdi_num & + CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + + rx_pdi_ch_map |= (rx_pdi_stream.l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + + rx_pdi_ch_map |= (rx_pdi_stream.h_ch_num & + CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + + cnl_sdw_reg_writew(sdw->data.sdw_shim, rx_ch_map_offset, + rx_pdi_ch_map); + + /* RX PDI1 CONFIG REG */ + rx_pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (rx_pdi_stream.pdi_num * 16)); + + rx_pdi_config |= ((SDW_BRA_PORT_ID & + PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + rx_pdi_config |= (SDW_BRA_CHN_MASK << + PDINCONFIG_CHANNEL_MASK_SHIFT); + + rx_pdi_config |= (SDW_BRA_SOFT_RESET << + PDINCONFIG_PORT_SOFT_RESET_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, + rx_pdi_config_offset, rx_pdi_config); + + + /* ALH STRMzCFG REG */ + rx_stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (rx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET)); + + rx_stream_config |= (CNL_STRMZCFG_DMAT_VAL & + CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + + rx_stream_config |= (0 & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + + cnl_sdw_reg_writel(sdw->data.alh_base, + (rx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET), + rx_stream_config); + + } else { + + /* + * TODO: There is official workaround which needs to be + * performed for PDI config register. The workaround + * is to perform SoftRst twice in order to clear + * PDI fifo contents. + */ + + } +} + +static void cnl_sdw_bra_pdi_config(struct sdw_master *mstr, bool enable) +{ + struct cnl_sdw *sdw; + + /* Get driver data for master */ + sdw = sdw_master_get_drvdata(mstr); + + /* PDI0 configuration */ + cnl_sdw_bra_pdi_tx_config(mstr, sdw, enable); + + /* PDI1 configuration */ + cnl_sdw_bra_pdi_rx_config(mstr, sdw, enable); +} + +static int cnl_sdw_bra_verify_footer(u8 *rx_buf, int offset) +{ + int ret = 0; + u8 ftr_response; + u8 ack_nack = 0; + u8 ftr_result = 0; + + ftr_response = rx_buf[offset]; + + /* + * ACK/NACK check + * NACK+ACK value from target: + * 00 -> Ignored + * 01 -> OK + * 10 -> Failed (Header CRC check failed) + * 11 -> Reserved + * NACK+ACK values at Target or initiator + * 00 -> Ignored + * 01 -> OK + * 10 -> Abort (Header cannot be trusted) + * 11 -> Abort (Header cannot be trusted) + */ + ack_nack = ((ftr_response >> SDW_BRA_FTR_RESP_ACK_SHIFT) & + SDW_BRA_FTR_RESP_ACK_MASK); + if (ack_nack == SDW_BRA_ACK_NAK_IGNORED) { + pr_info("BRA Packet Ignored\n"); + ret = -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_OK) + pr_info("BRA: Packet OK\n"); + else if (ack_nack == SDW_BRA_ACK_NAK_FAILED_ABORT) { + pr_info("BRA: Packet Failed/Reserved\n"); + return -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_RSVD_ABORT) { + pr_info("BRA: Packet Reserved/Abort\n"); + return -EINVAL; + } + + /* + * BRA footer result check + * Writes: + * 0 -> Good. Target accepted write payload + * 1 -> Bad. Target did not accept write payload + * Reads: + * 0 -> Good. Target completed read operation successfully + * 1 -> Bad. Target failed to complete read operation successfully + */ + ftr_result = (ftr_response >> SDW_BRA_FTR_RESP_RES_SHIFT) & + SDW_BRA_FTR_RESP_RES_MASK; + if (ftr_result == SDW_BRA_FTR_RESULT_BAD) { + pr_info("BRA: Read/Write operation failed on target side\n"); + /* Error scenario */ + return -EINVAL; + } + + pr_info("BRA: Read/Write operation complete on target side\n"); + + return ret; +} + +static int cnl_sdw_bra_verify_hdr(u8 *rx_buf, int offset, bool *chk_footer, + int roll_id) +{ + int ret = 0; + u8 hdr_response, rolling_id; + u8 ack_nack = 0; + u8 not_ready = 0; + + /* Match rolling ID */ + hdr_response = rx_buf[offset]; + rolling_id = rx_buf[offset + SDW_BRA_ROLLINGID_PDI_INDX]; + + rolling_id = (rolling_id & SDW_BRA_ROLLINGID_PDI_MASK); + if (roll_id != rolling_id) { + pr_info("BRA: Rolling ID doesn't match, returning error\n"); + return -EINVAL; + } + + /* + * ACK/NACK check + * NACK+ACK value from target: + * 00 -> Ignored + * 01 -> OK + * 10 -> Failed (Header CRC check failed) + * 11 -> Reserved + * NACK+ACK values at Target or initiator + * 00 -> Ignored + * 01 -> OK + * 10 -> Abort (Header cannot be trusted) + * 11 -> Abort (Header cannot be trusted) + */ + ack_nack = ((hdr_response >> SDW_BRA_HDR_RESP_ACK_SHIFT) & + SDW_BRA_HDR_RESP_ACK_MASK); + if (ack_nack == SDW_BRA_ACK_NAK_IGNORED) { + pr_info("BRA: Packet Ignored rolling_id:%d\n", rolling_id); + ret = -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_OK) + pr_info("BRA: Packet OK rolling_id:%d\n", rolling_id); + else if (ack_nack == SDW_BRA_ACK_NAK_FAILED_ABORT) { + pr_info("BRA: Packet Failed/Abort rolling_id:%d\n", rolling_id); + return -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_RSVD_ABORT) { + pr_info("BRA: Packet Reserved/Abort rolling_id:%d\n", rolling_id); + return -EINVAL; + } + + /* BRA not ready check */ + not_ready = (hdr_response >> SDW_BRA_HDR_RESP_NRDY_SHIFT) & + SDW_BRA_HDR_RESP_NRDY_MASK; + if (not_ready == SDW_BRA_TARGET_NOT_READY) { + pr_info("BRA: Target not ready for read/write operation rolling_id:%d\n", + rolling_id); + chk_footer = false; + return -EBUSY; + } + + pr_info("BRA: Target ready for read/write operation rolling_id:%d\n", rolling_id); + return ret; +} + +static void cnl_sdw_bra_remove_data_padding(u8 *src_buf, u8 *dst_buf, + u8 size) { + + int i; + + for (i = 0; i < size/2; i++) { + + *dst_buf++ = *src_buf++; + *dst_buf++ = *src_buf++; + src_buf++; + src_buf++; + } +} + + +static int cnl_sdw_bra_check_data(struct sdw_master *mstr, + struct sdw_bra_block *block, struct bra_info *info) { + + int offset = 0, rolling_id = 0, tmp_offset = 0; + int rx_crc_comp = 0, rx_crc_rvd = 0; + int i, ret; + bool chk_footer = true; + int rx_buf_size = info->rx_block_size; + u8 *rx_buf = info->rx_ptr; + u8 *tmp_buf = NULL; + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA RX DATA:", DUMP_PREFIX_OFFSET, 8, 4, + rx_buf, rx_buf_size, false); + + /* Allocate temporary buffer in case of read request */ + if (!block->cmd) { + tmp_buf = kzalloc(block->num_bytes, GFP_KERNEL); + if (!tmp_buf) { + ret = -ENOMEM; + goto error; + } + } + + /* + * TODO: From the response header and footer there is no mention of + * read or write packet so controller needs to keep transmit packet + * information in order to verify rx packet. Also the current + * approach used for error mechanism is any of the packet response + * is not success, just report the whole transfer failed to Slave. + */ + + /* + * Verification of response packet for one known + * hardcoded configuration. This needs to be extended + * once we have dynamic algorithm integrated. + */ + + /* 2 valid read response */ + for (i = 0; i < info->valid_packets; i++) { + + + pr_info("BRA: Verifying packet number:%d with rolling id:%d\n", + info->packet_info[i].packet_num, + rolling_id); + chk_footer = true; + ret = cnl_sdw_bra_verify_hdr(rx_buf, offset, &chk_footer, + rolling_id); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Header verification failed for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + /* Increment offset for header response */ + offset = offset + SDW_BRA_HEADER_RESP_SIZE_PDI; + + if (!block->cmd) { + + /* Remove PDI padding for data */ + cnl_sdw_bra_remove_data_padding(&rx_buf[offset], + &tmp_buf[tmp_offset], + info->packet_info[i].num_data_bytes); + + /* Increment offset for consumed data */ + offset = offset + + (info->packet_info[i].num_data_bytes * 2); + + rx_crc_comp = sdw_bus_compute_crc8(&tmp_buf[tmp_offset], + info->packet_info[i].num_data_bytes); + + /* Match Data CRC */ + rx_crc_rvd = rx_buf[offset]; + if (rx_crc_comp != rx_crc_rvd) { + ret = -EINVAL; + dev_err(&mstr->dev, "BRA: Data CRC doesn't match for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + /* Increment destination buffer with copied data */ + tmp_offset = tmp_offset + + info->packet_info[i].num_data_bytes; + + /* Increment offset for CRC */ + offset = offset + SDW_BRA_DATA_CRC_SIZE_PDI; + } + + if (chk_footer) { + ret = cnl_sdw_bra_verify_footer(rx_buf, offset); + if (ret < 0) { + ret = -EINVAL; + dev_err(&mstr->dev, "BRA: Footer verification failed for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + } + + /* Increment offset for footer response */ + offset = offset + SDW_BRA_HEADER_RESP_SIZE_PDI; + + /* Increment rolling id for next packet */ + rolling_id++; + if (rolling_id > 0xF) + rolling_id = 0; + } + + /* + * No need to check for dummy responses from codec + * Assumption made here is that dummy packets are + * added in 1ms buffer only after valid packets. + */ + + /* Copy data to codec buffer in case of read request */ + if (!block->cmd) + memcpy(block->values, tmp_buf, block->num_bytes); + +error: + /* Free up temp buffer allocated in case of read request */ + if (!block->cmd) + kfree(tmp_buf); + + /* Free up buffer allocated in cnl_sdw_bra_data_ops */ + kfree(info->tx_ptr); + kfree(info->rx_ptr); + kfree(info->packet_info); + + return ret; +} + +static int cnl_sdw_bra_data_ops(struct sdw_master *mstr, + struct sdw_bra_block *block, struct bra_info *info) +{ + + struct sdw_bra_block tmp_block; + int i; + int tx_buf_size = 384, rx_buf_size = 1152; + u8 *tx_buf = NULL, *rx_buf = NULL; + int rolling_id = 0, total_bytes = 0, offset = 0, reg_offset = 0; + int dummy_read = 0x0000; + int ret; + + /* + * TODO: Run an algorithm here to identify the buffer size + * for TX and RX buffers + number of dummy packets (read + * or write) to be added for to align buffers. + */ + + info->tx_block_size = tx_buf_size; + info->tx_ptr = tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto error; + } + + info->rx_block_size = rx_buf_size; + info->rx_ptr = rx_buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto error; + } + + /* Fill valid packets transferred per millisecond buffer */ + info->valid_packets = 2; + info->packet_info = kcalloc(info->valid_packets, + sizeof(*info->packet_info), + GFP_KERNEL); + if (!info->packet_info) { + ret = -ENOMEM; + goto error; + } + + /* + * Below code performs packet preparation for one known + * configuration. + * 1. 2 Valid Read request with 18 bytes each. + * 2. 22 dummy read packets with 18 bytes each. + */ + for (i = 0; i < info->valid_packets; i++) { + tmp_block.slave_addr = block->slave_addr; + tmp_block.cmd = block->cmd; /* Read Request */ + tmp_block.num_bytes = 18; + tmp_block.reg_offset = block->reg_offset + reg_offset; + tmp_block.values = NULL; + reg_offset += tmp_block.num_bytes; + + cnl_sdw_bra_prep_hdr(tx_buf, &tmp_block, rolling_id, offset); + /* Total Header size: Header + Header CRC size on PDI */ + offset += SDW_BRA_HEADER_TOTAL_SZ_PDI; + + if (block->cmd) { + /* + * PDI data preparation in case of write request + * Assumption made here is data size from codec will + * be always an even number. + */ + cnl_sdw_bra_prep_data(tx_buf, &tmp_block, + total_bytes, offset); + offset += tmp_block.num_bytes * 2; + + /* Data CRC */ + cnl_sdw_bra_prep_crc(tx_buf, &tmp_block, + total_bytes, offset); + offset += SDW_BRA_DATA_CRC_SIZE_PDI; + } + + total_bytes += tmp_block.num_bytes; + rolling_id++; + + /* Fill packet info data structure */ + info->packet_info[i].packet_num = i + 1; + info->packet_info[i].num_data_bytes = tmp_block.num_bytes; + } + + /* Prepare dummy packets */ + for (i = 0; i < 22; i++) { + tmp_block.slave_addr = block->slave_addr; + tmp_block.cmd = 0; /* Read request */ + tmp_block.num_bytes = 18; + tmp_block.reg_offset = dummy_read++; + tmp_block.values = NULL; + + cnl_sdw_bra_prep_hdr(tx_buf, &tmp_block, rolling_id, offset); + + /* Total Header size: RD header + RD header CRC size on PDI */ + offset += SDW_BRA_HEADER_TOTAL_SZ_PDI; + + total_bytes += tmp_block.num_bytes; + rolling_id++; + } + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA PDI VALID TX DATA:", + DUMP_PREFIX_OFFSET, 8, 4, tx_buf, tx_buf_size, false); + + return 0; + +error: + kfree(info->tx_ptr); + kfree(info->rx_ptr); + kfree(info->packet_info); + + return ret; +} + static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, struct sdw_bra_block *block) { - return 0; + struct cnl_sdw *sdw = sdw_master_get_platdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + struct cnl_bra_operation *ops = data->bra_data->bra_ops; + struct bra_info info; + int ret; + + /* + * 1. PDI Configuration + * 2. Prepare BRA packets including CRC calculation. + * 3. Configure TX and RX DMA in one shot mode. + * 4. Configure TX and RX Pipeline. + * 5. Run TX and RX DMA. + * 6. Run TX and RX pipelines. + * 7. Wait on completion for RX buffer. + * 8. Match TX and RX buffer packets and check for errors. + */ + + /* Memset bra_info data structure */ + memset(&info, 0x0, sizeof(info)); + + /* Fill master number in bra info data structure */ + info.mstr_num = mstr->nr; + + /* PDI Configuration (ON) */ + cnl_sdw_bra_pdi_config(mstr, true); + + /* Prepare TX buffer */ + ret = cnl_sdw_bra_data_ops(mstr, block, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Request packet(s) creation failed\n"); + goto out; + } + + /* Pipeline Setup (ON) */ + ret = ops->bra_platform_setup(data->bra_data->drv_data, true, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline setup failed\n"); + goto out; + } + + /* Trigger START host DMA and pipeline */ + ret = ops->bra_platform_xfer(data->bra_data->drv_data, true, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline start failed\n"); + goto out; + } + + /* Trigger STOP host DMA and pipeline */ + ret = ops->bra_platform_xfer(data->bra_data->drv_data, false, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline stop failed\n"); + goto out; + } + + /* Pipeline Setup (OFF) */ + ret = ops->bra_platform_setup(data->bra_data->drv_data, false, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline de-setup failed\n"); + goto out; + } + + /* Verify RX buffer */ + ret = cnl_sdw_bra_check_data(mstr, block, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Response packet(s) incorrect\n"); + goto out; + } + + /* PDI Configuration (OFF) */ + cnl_sdw_bra_pdi_config(mstr, false); + +out: + return ret; } static int cnl_sdw_mon_handover(struct sdw_master *mstr, @@ -1179,7 +1991,7 @@ static int cnl_sdw_set_ssp_interval(struct sdw_master *mstr, } static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, - int cur_clk_freq, int bank) + int cur_clk_div, int bank) { struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; @@ -1189,11 +2001,7 @@ static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, /* TODO: Retrieve divider value or get value directly from calling * function */ -#ifdef CONFIG_SND_SOC_SVFPGA - int divider = ((9600000 * 2/cur_clk_freq) - 1); -#else - int divider = ((9600000/cur_clk_freq) - 1); -#endif + int divider = (cur_clk_div - 1); if (bank) { mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; @@ -1419,7 +2227,7 @@ static int cnl_sdw_probe(struct sdw_master *mstr, sdw_master_set_drvdata(mstr, sdw); init_completion(&sdw->tx_complete); mutex_init(&sdw->stream_lock); - ret = sdw_init(sdw); + ret = sdw_init(sdw, true); if (ret) { dev_err(&mstr->dev, "SoundWire controller init failed %d\n", data->inst_id); @@ -1466,8 +2274,6 @@ static int cnl_sdw_remove(struct sdw_master *mstr) #ifdef CONFIG_PM static int cnl_sdw_runtime_suspend(struct device *dev) { - enum sdw_clk_stop_mode clock_stop_mode; - int volatile mcp_stat; int mcp_control; int timeout = 0; @@ -1493,12 +2299,12 @@ static int cnl_sdw_runtime_suspend(struct device *dev) cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); /* Prepare all the slaves for clock stop */ - ret = sdw_prepare_for_clock_change(sdw->mstr, 1, &clock_stop_mode); + ret = sdw_master_prep_for_clk_stop(sdw->mstr); if (ret) return ret; /* Call bus function to broadcast the clock stop now */ - ret = sdw_stop_clock(sdw->mstr, clock_stop_mode); + ret = sdw_master_stop_clock(sdw->mstr); if (ret) return ret; /* Wait for clock to be stopped, we are waiting at max 1sec now */ @@ -1534,12 +2340,9 @@ static int cnl_sdw_runtime_suspend(struct device *dev) static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) { - u16 wake_en, wake_sts, ioctl; - int volatile mcp_control; - int timeout = 0; + u16 wake_en, wake_sts; + int ret; struct cnl_sdw_data *data = &sdw->data; - int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * - SDW_CNL_IOCTL_REG_OFFSET); /* Disable the wake up interrupt */ wake_en = cnl_sdw_reg_readw(data->sdw_shim, @@ -1557,41 +2360,10 @@ static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) wake_sts |= (0x1 << data->inst_id); cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, wake_sts); - - ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); - ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; - cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); - ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; - cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); - /* Switch control back to master */ - sdw_switch_to_mip(sdw); - - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - mcp_control &= ~(MCP_CONTROL_BLOCKWAKEUP_MASK << - MCP_CONTROL_BLOCKWAKEUP_SHIFT); - mcp_control |= (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); - /* - * Wait for timeout to be clear to successful enabling of the clock - * We will wait for 1sec before giving up - */ - while (timeout != 10) { - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) == 0) - break; - msleep(1000); - timeout++; - } - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) != 0) { - dev_err(&sdw->mstr->dev, "Clop Stop Exit failed\n"); - return -EBUSY; + ret = sdw_init(sdw, false); + if (ret < 0) { + pr_err("sdw_init fail: %d\n", ret); + return ret; } dev_info(&sdw->mstr->dev, "Exit from clock stop successful\n"); @@ -1627,13 +2399,14 @@ static int cnl_sdw_runtime_resume(struct device *dev) dev_info(&mstr->dev, "Exit from clock stop successful\n"); /* Prepare all the slaves to comeout of clock stop */ - ret = sdw_prepare_for_clock_change(sdw->mstr, 0, NULL); + ret = sdw_mstr_deprep_after_clk_start(sdw->mstr); if (ret) return ret; return 0; } +#ifdef CONFIG_PM_SLEEP static int cnl_sdw_sleep_resume(struct device *dev) { return cnl_sdw_runtime_resume(dev); @@ -1642,7 +2415,15 @@ static int cnl_sdw_sleep_suspend(struct device *dev) { return cnl_sdw_runtime_suspend(dev); } -#endif +#else +#define cnl_sdw_sleep_suspend NULL +#define cnl_sdw_sleep_resume NULL +#endif /* CONFIG_PM_SLEEP */ +#else +#define cnl_sdw_runtime_suspend NULL +#define cnl_sdw_runtime_resume NULL +#endif /* CONFIG_PM */ + static const struct dev_pm_ops cnl_sdw_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(cnl_sdw_sleep_suspend, cnl_sdw_sleep_resume) diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 8e9d68c2bc2c..504df88d681a 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -27,7 +27,14 @@ #define SDW_CNL_SLAVE_STATUS_BITS 4 #define SDW_CNL_CMD_WORD_LEN 4 #define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 +#define SDW_CNL_DEFAULT_CLK_DIVIDER 0 +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F +#else +#define SDW_CNL_DEFAULT_SYNC_PERIOD 0x176F +#endif #define SDW_CNL_PORT_REG_OFFSET 0x80 #define CNL_SDW_SCP_ADDR_REGS 0x2 @@ -70,6 +77,7 @@ #define MCP_CONTROL_CMDRST_MASK 0x1 #define MCP_CONTROL_SOFTRST_SHIFT 0x6 #define MCP_CONTROL_SOFTCTRLBUSRST_SHIFT 0x5 +#define MCP_CONTROL_HARDCTRLBUSRST_MASK 0x1 #define MCP_CONTROL_HARDCTRLBUSRST_SHIFT 0x4 #define MCP_CONTROL_CLOCKPAUSEREQ_SHIFT 0x3 #define MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT 0x2 @@ -220,6 +228,8 @@ #define PDINCONFIG_CHANNEL_MASK_MASK 0xFF #define PDINCONFIG_PORT_NUMBER_SHIFT 0x0 #define PDINCONFIG_PORT_NUMBER_MASK 0x1F +#define PDINCONFIG_PORT_SOFT_RESET_SHIFT 0x18 +#define PDINCONFIG_PORT_SOFT_RESET 0x1F #define DPN_CONFIG_WL_SHIFT 0x8 #define DPN_CONFIG_WL_MASK 0x1F @@ -341,4 +351,34 @@ #define CNL_STRMZCFG_CHAN_SHIFT 16 #define CNL_STRMZCFG_CHAN_MASK 0xF +#define SDW_BRA_HEADER_SIZE_PDI 12 /* In bytes */ +#define SDW_BRA_HEADER_CRC_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_DATA_CRC_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_HEADER_RESP_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_FOOTER_RESP_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_PADDING_SZ_PDI 4 /* In bytes */ +#define SDW_BRA_HEADER_TOTAL_SZ_PDI 16 /* In bytes */ + +#define SDW_BRA_SOP_EOP_PDI_STRT_VALUE 0x4 +#define SDW_BRA_SOP_EOP_PDI_END_VALUE 0x2 +#define SDW_BRA_SOP_EOP_PDI_MASK 0x1F +#define SDW_BRA_SOP_EOP_PDI_SHIFT 5 + +#define SDW_BRA_STRM_ID_BLK_OUT 3 +#define SDW_BRA_STRM_ID_BLK_IN 4 + +#define SDW_BRA_PDI_TX_ID 0 +#define SDW_BRA_PDI_RX_ID 1 + +#define SDW_BRA_SOFT_RESET 0x1 +#define SDW_BRA_BULK_ENABLE 1 +#define SDW_BRA_BLK_EN_MASK 0xFFFEFFFF +#define SDW_BRA_BLK_EN_SHIFT 16 + +#define SDW_BRA_ROLLINGID_PDI_INDX 3 +#define SDW_BRA_ROLLINGID_PDI_MASK 0xF +#define SDW_BRA_ROLLINGID_PDI_SHIFT 0 + +#define SDW_PCM_STRM_START_INDEX 0x2 + #endif /* _LINUX_SDW_CNL_H */ diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h index 42e948440481..fd060bfa74c4 100644 --- a/drivers/sdw/sdw_priv.h +++ b/drivers/sdw/sdw_priv.h @@ -34,16 +34,14 @@ #define SDW_STATE_INIT_STREAM_TAG 0x1 #define SDW_STATE_ALLOC_STREAM 0x2 #define SDW_STATE_CONFIG_STREAM 0x3 -#define SDW_STATE_COMPUTE_STREAM 0x4 -#define SDW_STATE_PREPARE_STREAM 0x5 -#define SDW_STATE_ENABLE_STREAM 0x6 -#define SDW_STATE_DISABLE_STREAM 0x7 -#define SDW_STATE_UNPREPARE_STREAM 0x8 -#define SDW_STATE_UNCOMPUTE_STREAM 0x9 -#define SDW_STATE_RELEASE_STREAM 0xa -#define SDW_STATE_FREE_STREAM 0xb -#define SDW_STATE_FREE_STREAM_TAG 0xc -#define SDW_STATE_ONLY_XPORT_STREAM 0xd +#define SDW_STATE_PREPARE_STREAM 0x4 +#define SDW_STATE_ENABLE_STREAM 0x5 +#define SDW_STATE_DISABLE_STREAM 0x6 +#define SDW_STATE_UNPREPARE_STREAM 0x7 +#define SDW_STATE_RELEASE_STREAM 0x8 +#define SDW_STATE_FREE_STREAM 0x9 +#define SDW_STATE_FREE_STREAM_TAG 0xA +#define SDW_STATE_ONLY_XPORT_STREAM 0xB #define SDW_STATE_INIT_RT 0x1 #define SDW_STATE_CONFIG_RT 0x2 @@ -53,6 +51,8 @@ #define SDW_STATE_UNPREPARE_RT 0x6 #define SDW_STATE_RELEASE_RT 0x7 +#define SDW_SLAVE_BDCAST_ADDR 15 + struct sdw_runtime; /* Defined in sdw.c, used by multiple files of module */ extern struct sdw_core sdw_core; @@ -76,11 +76,33 @@ enum sdw_clk_state { SDW_CLK_STATE_ON = 1, }; +enum sdw_update_bs_state { + SDW_UPDATE_BS_PRE, + SDW_UPDATE_BS_BNKSWTCH, + SDW_UPDATE_BS_POST, + SDW_UPDATE_BS_BNKSWTCH_WAIT, + SDW_UPDATE_BS_DIS_CHN, +}; + +enum sdw_port_en_state { + SDW_PORT_STATE_PREPARE, + SDW_PORT_STATE_ENABLE, + SDW_PORT_STATE_DISABLE, + SDW_PORT_STATE_UNPREPARE, +}; + struct port_chn_en_state { bool is_activate; bool is_bank_sw; }; +struct temp_elements { + int rate; + int full_bw; + int payload_bw; + int hwidth; +}; + struct sdw_stream_tag { int stream_tag; struct mutex stream_lock; @@ -153,6 +175,10 @@ struct sdw_mstr_runtime { unsigned int stream_bw; /* State of runtime structure */ int rt_state; + int hstart; + int hstop; + int block_offset; + int sub_block_offset; }; struct sdw_runtime { @@ -185,8 +211,10 @@ struct sdw_bus { unsigned int clk_state; unsigned int active_bank; unsigned int clk_freq; + unsigned int clk_div; /* Bus total Bandwidth. Initialize and reset to zero */ unsigned int bandwidth; + unsigned int stream_interval; /* Stream Interval */ unsigned int system_interval; /* Bus System Interval */ unsigned int frame_freq; unsigned int col; @@ -239,6 +267,8 @@ int sdw_bus_bw_init(void); int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); +int sdw_bus_bra_xport_config(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, bool enable); int sdw_chn_enable(void); void sdw_unlock_mstr(struct sdw_master *mstr); int sdw_trylock_mstr(struct sdw_master *mstr); diff --git a/drivers/sdw/sdw_utils.c b/drivers/sdw/sdw_utils.c new file mode 100644 index 000000000000..724323d01993 --- /dev/null +++ b/drivers/sdw/sdw_utils.c @@ -0,0 +1,49 @@ +/* + * sdw_bwcalc.c - SoundWire Bus BW calculation & CHN Enabling implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Sanyog Kale + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include + + + +/** + * sdw_bus_compute_crc8: SoundWire bus helper function to compute crc8. + * This API uses crc8 helper functions internally. + * + * @values: Data buffer. + * @num_bytes: Number of bytes. + */ +u8 sdw_bus_compute_crc8(u8 *values, u8 num_bytes) +{ + u8 table[256]; + u8 poly = 0x4D; /* polynomial = x^8 + x^6 + x^3 + x^2 + 1 */ + u8 crc = CRC8_INIT_VALUE; /* Initialize 8 bit to 11111111 */ + + /* Populate MSB */ + crc8_populate_msb(table, poly); + + /* CRC computation */ + crc = crc8(table, values, num_bytes, crc); + + return crc; +} +EXPORT_SYMBOL(sdw_bus_compute_crc8); diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h index acf223cba595..6a9281c458b7 100644 --- a/include/linux/sdw/sdw_cnl.h +++ b/include/linux/sdw/sdw_cnl.h @@ -73,6 +73,33 @@ struct cnl_sdw_port { struct cnl_sdw_pdi_stream *pdi_stream; }; +struct bra_packet_info { + u8 packet_num; + u8 num_data_bytes; +}; + +struct bra_info { + unsigned int mstr_num; + u8 *tx_ptr; + u8 *rx_ptr; + unsigned int tx_block_size; + unsigned int rx_block_size; + u8 valid_packets; + struct bra_packet_info *packet_info; +}; + +struct cnl_bra_operation { + int (*bra_platform_setup)(void *context, bool is_enable, + struct bra_info *info); + int (*bra_platform_xfer)(void *context, bool is_enable, + struct bra_info *info); +}; + +struct cnl_sdw_bra_cfg { + void *drv_data; + struct cnl_bra_operation *bra_ops; +}; + struct cnl_sdw_data { /* SoundWire IP registers per instance */ void __iomem *sdw_regs; @@ -84,6 +111,8 @@ struct cnl_sdw_data { int irq; /* Instance id */ int inst_id; + /* BRA data pointer */ + struct cnl_sdw_bra_cfg *bra_data; }; struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, diff --git a/include/linux/sdw/sdw_registers.h b/include/linux/sdw/sdw_registers.h index 1abdf4bf863a..2e831c0e93cf 100644 --- a/include/linux/sdw/sdw_registers.h +++ b/include/linux/sdw/sdw_registers.h @@ -68,13 +68,17 @@ #define SDW_SCP_INTSTAT_1 0x40 #define SDW_SCP_INTSTAT1_PARITY_MASK 0x1 #define SDW_SCP_INTSTAT1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTSTAT1_IMPL_DEF_MASK 0x4 #define SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK 0x80 #define SDW_SCP_INTCLEAR1 0x40 #define SDW_SCP_INTCLEAR1_PARITY_MASK 0x1 #define SDW_SCP_INTCLEAR1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTCLEAR1_IMPL_DEF_MASK 0x4 #define SDW_SCP_INTCLEAR1_SCP2_CASCADE_MASK 0x80 -#define SDW_SCP_INTMASK1 +#define SDW_SCP_INTMASK1 0x41 +#define SDW_SCP_INTMASK1_PARITY_MASK 0x1 +#define SDW_SCP_INTMASK1_BUS_CLASH_MASK 0x2 #define SDW_SCP_INTSTAT2 0x42 #define SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK 0x80 #define SDW_SCP_INTSTAT3 0x43 @@ -84,6 +88,7 @@ #define SDW_SCP_STAT 0x44 #define SDW_SCP_STAT_CLK_STP_NF_MASK 0x1 #define SDW_SCP_SYSTEMCTRL 0x45 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_MASK 0x1 #define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT 0x0 #define SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT 0x1 #define SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT 0x2 diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h index d16579b35f8a..ed075fbd9a99 100644 --- a/include/linux/sdw_bus.h +++ b/include/linux/sdw_bus.h @@ -60,6 +60,66 @@ #define SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE 0x2 #define SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT 0x4 +#define SDW_BRA_PORT_ID 0 +#define SDW_BRA_CHN_MASK 0x1 + +#define SDW_BRA_HEADER_SIZE 6 /* In bytes */ +#define SDW_BRA_HEADER_CRC_SIZE 1 /* In bytes */ +#define SDW_BRA_DATA_CRC_SIZE 1 /* In bytes */ +#define SDW_BRA_HEADER_RESP_SIZE 1 /* In bytes */ +#define SDW_BRA_FOOTER_RESP_SIZE 1 /* In bytes */ +#define SDW_BRA_PADDING_SZ 1 /* In bytes */ +#define SDW_BRA_HEADER_TOTAL_SZ 8 /* In bytes */ + +#define SDW_BRA_BPT_PAYLOAD_TYPE 0x0 +#define SDW_BRA_BPT_PYLD_TY_MASK 0xFF3FFFFF +#define SDW_BRA_BPT_PYLD_TY_SHIFT 22 + +#define SDW_BRA_HDR_ACTIVE 0x3 +#define SDW_BRA_HDR_ACTIVE_SHIFT 6 +#define SDW_BRA_HDR_ACTIVE_MASK 0x3F + +#define SDW_BRA_HDR_SLV_ADDR_SHIFT 2 +#define SDW_BRA_HDR_SLV_ADDR_MASK 0xC3 + +#define SDW_BRA_HDR_RD_WR_SHIFT 1 +#define SDW_BRA_HDR_RD_WR_MASK 0xFD + +#define SDW_BRA_HDR_MSB_BYTE_SET 1 +#define SDW_BRA_HDR_MSB_BYTE_UNSET 0 +#define SDW_BRA_HDR_MSB_BYTE_CHK 255 +#define SDW_BRA_HDR_MSB_BYTE_MASK 0xFE +#define SDW_BRA_HDR_MSB_BYTE_SHIFT 0 + +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT0 0 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK0 0xFF +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT8 8 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK8 0xFF00 +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT16 16 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK16 0xFF0000 +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT24 24 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK24 0xFF000000 + +#define SDW_BRA_HDR_RESP_ACK_SHIFT 3 +#define SDW_BRA_HDR_RESP_NRDY_SHIFT 5 +#define SDW_BRA_FTR_RESP_ACK_SHIFT 3 +#define SDW_BRA_FTR_RESP_RES_SHIFT 5 +#define SDW_BRA_HDR_RESP_ACK_MASK 0x3 +#define SDW_BRA_HDR_RESP_NRDY_MASK 0x1 +#define SDW_BRA_FTR_RESP_ACK_MASK 0x3 +#define SDW_BRA_FTR_RESP_RES_MASK 0x1 + +#define SDW_BRA_TARGET_READY 0 +#define SDW_BRA_TARGET_NOT_READY 1 + +#define SDW_BRA_ACK_NAK_IGNORED 0 +#define SDW_BRA_ACK_NAK_OK 1 +#define SDW_BRA_ACK_NAK_FAILED_ABORT 2 +#define SDW_BRA_ACK_NAK_RSVD_ABORT 3 + +#define SDW_BRA_FTR_RESULT_GOOD 0 +#define SDW_BRA_FTR_RESULT_BAD 1 + /* enum sdw_driver_type: There are different driver callbacks for slave and * master. This is to differentiate between slave driver * and master driver. Bus driver binds master driver to @@ -420,6 +480,14 @@ struct sdw_slv_dp0_capabilities { * the Port15 alias * 0: Command_Ignored * 1: Command_OK, Data is OR of all registers + * @scp_impl_def_intr_mask: Implementation defined interrupt for Slave control + * port + * @clk_stp1_deprep_required: De-prepare is required after exiting the clock + * stop mode 1. Noramlly exit from clock stop 1 is like + * hard reset, so de-prepare shouldn't be required but + * some Slave requires de-prepare after exiting from + * clock stop 1. Mark as true if Slave requires + * deprepare after exiting from clock stop mode 1. * @sdw_dp0_supported: DP0 is supported by Slave. * @sdw_dp0_cap: Data Port 0 Capabilities of the Slave. * @num_of_sdw_ports: Number of SoundWire Data ports present. The representation @@ -436,6 +504,8 @@ struct sdw_slv_capabilities { bool paging_supported; bool bank_delay_support; unsigned int port_15_read_behavior; + u8 scp_impl_def_intr_mask; + bool clk_stp1_deprep_required; bool sdw_dp0_supported; struct sdw_slv_dp0_capabilities *sdw_dp0_cap; int num_of_sdw_ports; @@ -494,6 +564,40 @@ struct sdw_bus_params { int bank; }; +/** struct sdw_portn_intr_stat: Implementation defined interrupt + * status for slave ports other than port 0 + * + * num: Port number for which status is reported. + * status: status of the implementation defined interrupts + */ +struct sdw_portn_intr_stat { + int num; + u8 status; +}; + +/** struct sdw_impl_def_intr_stat: Implementation define interrupt + * status for slave. + * + * control_port_stat: Implementation defined interrupt status mask + * for control ports. Mask Bits are exactly + * same as defined in MIPI Spec 1.0 + * port0_stat: Implementation defined interrupt status mask + * for port 0. Mask bits are exactly same as defined + * in MIPI spec 1.0. + * num_ports: Number of ports in slave other than port 0. + * portn_stat: Implementation defined status for slave ports + * other than port0. Mask bits are exactly same + * as defined in MIPI spec 1.0. Array size is + * same as number of ports in Slave. + */ +struct sdw_impl_def_intr_stat { + u8 control_port_stat; + u8 port0_stat; + int num_ports; + struct sdw_portn_intr_stat *portn_stat; +}; + + /** * struct sdw_slave_driver: Manage SoundWire generic/Slave device driver * @driver_type: To distinguish between master and slave driver. Set and @@ -580,6 +684,13 @@ struct sdw_slave_driver { int port, int ch_mask, int bank); int (*handle_post_port_unprepare)(struct sdw_slv *swdev, int port, int ch_mask, int bank); + int (*pre_clk_stop_prep)(struct sdw_slv *sdwdev, + enum sdw_clk_stop_mode mode, bool stop); + int (*post_clk_stop_prep)(struct sdw_slv *sdwdev, + enum sdw_clk_stop_mode mode, bool stop); + enum sdw_clk_stop_mode (*get_dyn_clk_stp_mod)(struct sdw_slv *swdev); + void (*update_slv_status)(struct sdw_slv *swdev, + enum sdw_slave_status *status); const struct sdw_slv_id *id_table; }; #define to_sdw_slave_driver(d) container_of(d, struct sdw_slave_driver, driver) @@ -1298,6 +1409,15 @@ struct sdw_master *sdw_get_master(int nr); */ void sdw_put_master(struct sdw_master *mstr); +/** + * sdw_slave_xfer_bra_block: Transfer the data block using the BTP/BRA + * protocol. + * @mstr: SoundWire Master Master + * @block: Data block to be transferred. + */ +int sdw_slave_xfer_bra_block(struct sdw_master *mstr, + struct sdw_bra_block *block); + /** * module_sdw_slave_driver() - Helper macro for registering a sdw Slave driver @@ -1311,19 +1431,50 @@ void sdw_put_master(struct sdw_master *mstr); module_driver(__sdw_slave_driver, sdw_slave_driver_register, \ sdw_slave_driver_unregister) /** - * sdw_prepare_for_clock_change: Prepare all the Slaves for clock stop or - * clock start. Prepares Slaves based on what they support - * simplified clock stop or normal clock stop based on - * their capabilities registered to slave driver. + * sdw_master_prep_for_clk_stop: Prepare all the Slaves for clock stop. + * Iterate through each of the enumerated Slave. + * Prepare each Slave according to the clock stop + * mode supported by Slave. Use dynamic value from + * Slave callback if registered, else use static values + * from Slave capabilities registered. + * 1. Get clock stop mode for each Slave. + * 2. Call pre_prepare callback of each Slave if + * registered. + * 3. Prepare each Slave for clock stop + * 4. Broadcast the Read message to make sure + * all Slaves are prepared for clock stop. + * 5. Call post_prepare callback of each Slave if + * registered. + * * @mstr: Master handle for which clock state has to be changed. - * @start: Prepare for starting or stopping the clock - * @clk_stop_mode: Bus used which clock mode, if bus finds all the Slaves - * on the bus to be supported clock stop mode1 it prepares - * all the Slaves for mode1 else it will prepare all the - * Slaves for mode0. + * + * Returns 0 + */ +int sdw_master_prep_for_clk_stop(struct sdw_master *mstr); + +/** + * sdw_mstr_deprep_after_clk_start: De-prepare all the Slaves + * exiting clock stop mode 0 after clock resumes. Clock + * is already resumed before this. De-prepare all the Slaves + * which were earlier in ClockStop mode0. De-prepare for the + * Slaves which were there in ClockStop mode1 is done after + * they enumerated back. Its not done here as part of master + * getting resumed. + * 1. Get clock stop mode for each Slave its exiting from + * 2. Call pre_prepare callback of each Slave exiting from + * clock stop mode 0. + * 3. De-Prepare each Slave exiting from Clock Stop mode0 + * 4. Broadcast the Read message to make sure + * all Slaves are de-prepared for clock stop. + * 5. Call post_prepare callback of each Slave exiting from + * clock stop mode0 + * + * + * @mstr: Master handle + * + * Returns 0 */ -int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool start, - enum sdw_clk_stop_mode *clck_stop_mode); +int sdw_mstr_deprep_after_clk_start(struct sdw_master *mstr); /** * sdw_wait_for_slave_enumeration: Wait till all the slaves are enumerated. @@ -1341,13 +1492,14 @@ int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, struct sdw_slv *slave); /** - * sdw_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * sdw_master_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL * register with clock_stop_now bit set. + * * @mstr: Master handle for which clock has to be stopped. - * @clk_stop_mode: Bus used which clock mode. + * + * Returns 0 on success, appropriate error code on failure. */ - -int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode); +int sdw_master_stop_clock(struct sdw_master *mstr); /* Return the adapter number for a specific adapter */ static inline int sdw_master_id(struct sdw_master *mstr) @@ -1377,4 +1529,29 @@ static inline void sdw_slave_set_drvdata(struct sdw_slv *slv, dev_set_drvdata(&slv->dev, data); } +static inline void *sdw_master_get_platdata(const struct sdw_master *mstr) +{ + return dev_get_platdata(&mstr->dev); +} + +/** + * sdw_slave_get_bus_params: Get the current bus params. Some Slaves + * requires bus params at the probe to program its + * registers based on bus params. This API provides + * current bus params + * + * @sdw_slv: Slave handle + * @params: Bus params + */ +int sdw_slave_get_bus_params(struct sdw_slv *sdw_slv, + struct sdw_bus_params *params); +/** + * sdw_bus_compute_crc8: SoundWire bus helper function to compute crc8. + * This API uses crc8 helper functions internally. + * + * @values: Data buffer. + * @num_bytes: Number of bytes. + */ +u8 sdw_bus_compute_crc8(u8 *values, u8 num_bytes); + #endif /* _LINUX_SDW_BUS_H */ diff --git a/sound/soc/codecs/svfpga-sdw.c b/sound/soc/codecs/svfpga-sdw.c index 1fa06ef82ab2..dec412af6d5c 100644 --- a/sound/soc/codecs/svfpga-sdw.c +++ b/sound/soc/codecs/svfpga-sdw.c @@ -79,7 +79,7 @@ static int svfpga_register_sdw_capabilties(struct sdw_slv *sdw, dpn_cap->num_audio_modes), GFP_KERNEL); for (j = 0; j < dpn_cap->num_audio_modes; j++) { prop = &dpn_cap->mode_properties[j]; - prop->max_frequency = 16000000; + prop->max_frequency = 19200000; prop->min_frequency = 1000000; prop->num_freq_configs = 0; prop->freq_supported = NULL; diff --git a/sound/soc/intel/boards/cnl_svfpga.c b/sound/soc/intel/boards/cnl_svfpga.c index 312c0437ac15..23c1f6437cda 100644 --- a/sound/soc/intel/boards/cnl_svfpga.c +++ b/sound/soc/intel/boards/cnl_svfpga.c @@ -125,7 +125,7 @@ static int cnl_svfpga_codec_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); slot_width = 24; rate->min = rate->max = 48000; - channels->min = channels->max = 1; + channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), SNDRV_PCM_FORMAT_S16_LE); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index b82de714c916..d888d31d0ea2 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -516,7 +516,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; struct cnl_sdw_data *p_data; - int ret = 0, i, j; + int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ #if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) dsp->num_sdw_controllers = 3; @@ -561,14 +561,20 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, if (!m_cap->sdw_dpn_cap) return -ENOMEM; for (j = 0; j < m_cap->num_data_ports; j++) { - dpn_cap = &m_cap->sdw_dpn_cap[i]; + dpn_cap = &m_cap->sdw_dpn_cap[j]; /* Both Tx and Rx */ dpn_cap->port_direction = 0x3; - dpn_cap->port_number = i; + dpn_cap->port_number = j; dpn_cap->max_word_length = 32; dpn_cap->min_word_length = 1; - dpn_cap->num_word_length = 0; - dpn_cap->word_length_buffer = NULL; + dpn_cap->num_word_length = 4; + + dpn_cap->word_length_buffer = + kzalloc(((sizeof(unsigned int)) * + dpn_cap->num_word_length), GFP_KERNEL); + for (k = 0; k < dpn_cap->num_word_length; k++) + dpn_cap->word_length_buffer[k] = wl = wl + 8; + wl = 0; dpn_cap->dpn_type = SDW_FULL_DP; dpn_cap->min_ch_num = 1; dpn_cap->max_ch_num = 8; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index ea9a3e14434a..564602c0ee12 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -39,7 +39,8 @@ struct sdw_dma_data { int stream_tag; - struct cnl_sdw_port *port; + int nr_ports; + struct cnl_sdw_port **port; struct sdw_master *mstr; enum cnl_sdw_pdi_stream_type stream_type; int stream_state; @@ -133,11 +134,11 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, enum sdw_data_direction direction; struct sdw_stream_config stream_config; struct sdw_port_config port_config; - struct sdw_port_cfg port_cfg; + struct sdw_port_cfg *port_cfg; int ret = 0; struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; - int upscale_factor = 16; + int i, upscale_factor = 16; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -155,13 +156,26 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, direction = SDW_DATA_DIR_IN; else direction = SDW_DATA_DIR_OUT; - /* Dynamically alloc port and PDI streams for this DAI */ - dma->port = cnl_sdw_alloc_port(dma->mstr, channels, + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + dma->nr_ports = channels; + else + dma->nr_ports = 1; + + dma->port = kcalloc(dma->nr_ports, sizeof(struct cnl_sdw_port), + GFP_KERNEL); + if (!dma->port) + return -ENOMEM; + + for (i = 0; i < dma->nr_ports; i++) { + /* Dynamically alloc port and PDI streams for this DAI */ + dma->port[i] = cnl_sdw_alloc_port(dma->mstr, channels, direction, dma->stream_type); - if (!dma->port) { - dev_err(dai->dev, "Unable to allocate port\n"); - return -EINVAL; + if (!dma->port[i]) { + dev_err(dai->dev, "Unable to allocate port\n"); + return -EINVAL; + } } + dma->stream_state = STREAM_STATE_ALLOC_STREAM; m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); if (!m_cfg) { @@ -170,10 +184,10 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, } if (!m_cfg->sdw_agg_enable) - m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + m_cfg->sdw_stream_num = dma->port[0]->pdi_stream->sdw_pdi_num; else m_cfg->sdw_agg.agg_data[dma->mstr_nr].alh_stream_num = - dma->port->pdi_stream->sdw_pdi_num; + dma->port[0]->pdi_stream->sdw_pdi_num; ret = skl_tplg_be_update_params(dai, &p_params); if (ret) return ret; @@ -202,10 +216,23 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "Unable to configure the stream\n"); return ret; } - port_config.num_ports = 1; - port_config.port_cfg = &port_cfg; - port_cfg.port_num = dma->port->port_num; - port_cfg.ch_mask = ((1 << channels) - 1); + port_cfg = kcalloc(dma->nr_ports, sizeof(struct sdw_port_cfg), + GFP_KERNEL); + if (!port_cfg) + return -ENOMEM; + + port_config.num_ports = dma->nr_ports; + port_config.port_cfg = port_cfg; + + for (i = 0; i < dma->nr_ports; i++) { + port_cfg[i].port_num = dma->port[i]->port_num; + + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + port_cfg[i].ch_mask = 0x1; + else + port_cfg[i].ch_mask = ((1 << channels) - 1); + } + ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure port\n"); @@ -219,7 +246,7 @@ int cnl_sdw_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_dma_data *dma; - int ret = 0; + int ret = 0, i; dma = snd_soc_dai_get_dma_data(dai, substream); @@ -228,16 +255,20 @@ int cnl_sdw_hw_free(struct snd_pcm_substream *substream, if (ret) dev_err(dai->dev, "Unable to release stream\n"); dma->stream_state = STREAM_STATE_RELEASE_STREAM; - if (dma->port && dma->stream_state == + for (i = 0; i < dma->nr_ports; i++) { + if (dma->port[i] && dma->stream_state == STREAM_STATE_RELEASE_STREAM) { - /* Even if release fails, we continue, - * while winding up we have - * to continue till last one gets winded up - */ - cnl_sdw_free_port(dma->mstr, dma->port->port_num); - dma->stream_state = STREAM_STATE_FREE_STREAM; - dma->port = NULL; + /* Even if release fails, we continue, + * while winding up we have + * to continue till last one gets winded up + */ + cnl_sdw_free_port(dma->mstr, + dma->port[i]->port_num); + dma->port[i] = NULL; + } } + + dma->stream_state = STREAM_STATE_FREE_STREAM; } return 0; } From 9297763d5599e034483d71d72dcc425a11e13561 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 May 2016 14:00:15 +0530 Subject: [PATCH 0083/1276] ALSA: core: let low-level driver or userspace disable rewinds Add new hw_params flag to explicitly tell driver that rewinds will never be used. This can be used by low-level driver to optimize DMA operations and reduce power consumption. Use this flag only when data written in ring buffer will never be invalidated, e.g. any update of appl_ptr is final. Note that the update of appl_ptr include both a read/write data operation as well as snd_pcm_forward() whose behavior is not modified. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty --- include/sound/pcm.h | 1 + include/uapi/sound/asound.h | 1 + sound/core/pcm_native.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index d6bd3caf6878..2eef65a9404e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -379,6 +379,7 @@ struct snd_pcm_runtime { unsigned int rate_num; unsigned int rate_den; unsigned int no_period_wakeup: 1; + unsigned int no_rewinds:1; /* -- SW params -- */ int tstamp_mode; /* mmap timestamp is updated */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index ed0a120d4f08..ff57e4c89de4 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -377,6 +377,7 @@ typedef int snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */ #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */ +#define SNDRV_PCM_HW_PARAMS_NO_REWINDS (1<<3) /* disable rewinds */ struct snd_interval { unsigned int min, max; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 66c90f486af9..41beeb825240 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -726,6 +726,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->no_period_wakeup = (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + runtime->no_rewinds = + (params->flags & SNDRV_PCM_HW_PARAMS_NO_REWINDS) ? 1 : 0; bits = snd_pcm_format_physical_width(runtime->format); runtime->sample_bits = bits; @@ -2642,11 +2644,15 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, snd_pcm_uframes_t frames) { + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t ret; if (frames == 0) return 0; + if (runtime->no_rewinds) + return 0; + snd_pcm_stream_lock_irq(substream); ret = do_pcm_hwsync(substream); if (!ret) From d969463324e30dff5771346f4ade8266bd02a9ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jun 2016 16:01:00 +0530 Subject: [PATCH 0084/1276] ALSA: pcm: conditionally avoid mmap of control data. In case of mmap, by default alsa-lib mmaps both control and status data. If driver subscribes for application pointer update, driver needs to get notification whenever appl ptr changes. With the above case driver won't get appl ptr notifications. This patch check on a hw info flag and returns error when user land asks for mmaping control & status data, thus forcing user to issue IOCTL_SYNC_PTR. Change-Id: I05b83f630812face322c474d9bbb6d56cbdc08fb Suggested-by: Takashi Iwai Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ramesh Babu Signed-off-by: Jaikrishna Nemallapudi Signed-off-by: Subhransu S. Prusty Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sm, Bhadur A Tested-by: Sm, Bhadur A --- include/uapi/sound/asound.h | 1 + sound/core/pcm_native.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index ff57e4c89de4..9120cce11265 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -296,6 +296,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ +#define SNDRV_PCM_INFO_NO_STATUS_MMAP 0x10000000 /* status and control mmap not supported */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 41beeb825240..6ba593bc673d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3460,21 +3460,38 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) struct snd_pcm_file * pcm_file; struct snd_pcm_substream *substream; unsigned long offset; + unsigned int info; pcm_file = file->private_data; substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + info = substream->runtime->hw.info; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { case SNDRV_PCM_MMAP_OFFSET_STATUS: if (!pcm_status_mmap_allowed(pcm_file)) return -ENXIO; + /* + * force fallback to ioctl if driver doesn't support status + * and control mmap. + */ + if (info & SNDRV_PCM_INFO_NO_STATUS_MMAP) + return -ENXIO; + return snd_pcm_mmap_status(substream, file, area); case SNDRV_PCM_MMAP_OFFSET_CONTROL: if (!pcm_control_mmap_allowed(pcm_file)) return -ENXIO; + + /* + * force fallback to ioctl if driver doesn't support status + * and control mmap. + */ + if (info & SNDRV_PCM_INFO_NO_STATUS_MMAP) + return -ENXIO; + return snd_pcm_mmap_control(substream, file, area); default: return snd_pcm_mmap_data(substream, file, area); From e7d955c6ecc5b50060e166107d73db184329c3d9 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 6 Jun 2016 13:13:15 +0530 Subject: [PATCH 0085/1276] ALSA: hda: ext: add spib to stream context. Platforms like skylake support SPIB (software position index in Buffer) capability, through which application pointer can be programmed in DMA. This helps DMA stop rendering stale data. This patch saves spib values in stream context which can be restored during resume from S3. Change-Id: I2992242087ee0732b6fc571b5e65eb59aa1fa251 Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sm, Bhadur A Tested-by: Sm, Bhadur A --- include/sound/hdaudio_ext.h | 1 + sound/hda/ext/hdac_ext_stream.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 2d93a039a286..62181677f009 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -67,6 +67,7 @@ struct hdac_ext_stream { u32 dpib; u32 lpib; + u32 spib; bool decoupled:1; bool link_locked:1; bool link_prepared; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 1b2a1c996422..0ede36f51e2e 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -450,6 +450,8 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, } writel(value, stream->spib_addr); + /* save the value in stream context */ + stream->spib = value; return 0; } From cf70a1b68d33484bcb8fc705d624c62ee73161fc Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 23 May 2016 11:39:19 +0530 Subject: [PATCH 0086/1276] ASoC: Intel: Skylake: add support for spib mode Skylake audio controller sports SPIB capability, which can be used to inform position of appl pointer to host DMA controller. When SPIB mode is enabled, driver could write the appl pointer position in SPIB register. Host DMA will make sure it won't read/write beyond bytes specified in SPIB register. SPIB mode will be useful in low power use cases, where DSP could pre-fetch large buffers to avoid frequent wakes due to interrupts. Skylake driver makes use of no_rewind flag and appl_ptr_update callback to enable and update SPIB register respectively. Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty --- sound/soc/intel/skylake/skl-pcm.c | 46 +++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 65a110664ff6..228f00763094 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -46,7 +46,8 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ SNDRV_PCM_INFO_HAS_LINK_ATIME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE, @@ -154,6 +155,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; + struct snd_pcm_runtime *runtime; int err; hstream = snd_hdac_get_stream(bus, params->stream, @@ -179,6 +181,11 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) if (err < 0) return err; + runtime = hdac_stream(stream)->substream->runtime; + /* enable SPIB if no_rewinds flag is set */ + if (runtime->no_rewinds) + snd_hdac_ext_stream_spbcap_enable(ebus, 1, hstream->index); + hdac_stream(stream)->prepared = 1; return 0; @@ -390,6 +397,8 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, { struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct snd_pcm_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); struct skl_module_cfg *mconfig; int ret; @@ -398,6 +407,10 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (runtime->no_rewinds) { + snd_hdac_ext_stream_set_spib(ebus, stream, 0); + snd_hdac_ext_stream_spbcap_enable(ebus, 0, hstream->index); + } if (mconfig) { ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); if (ret < 0) @@ -479,6 +492,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl_module_cfg *mconfig; struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dapm_widget *w; int ret; @@ -504,8 +518,10 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, snd_hdac_ext_stream_set_dpibr(bus, stream, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + if (runtime->no_rewinds) + snd_hdac_ext_stream_set_spib(ebus, + stream, stream->spib); } - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* @@ -1492,6 +1508,31 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, return 0; } +/* update SPIB register with appl position */ +static int skl_platform_ack(struct snd_pcm_substream *substream) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_ext_stream *estream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + ssize_t appl_pos, buf_size; + u32 spib; + + /* Use spib mode only if no_rewind mode is set */ + if (runtime->no_rewinds == 0) + return 0; + + appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + + spib = appl_pos % buf_size; + + /* Allowable value for SPIB is 1 byte to max buffer size */ + spib = (spib == 0) ? buf_size : spib; + snd_hdac_ext_stream_set_spib(ebus, estream, spib); + + return 0; +} + static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { @@ -1599,6 +1640,7 @@ static const struct snd_pcm_ops skl_platform_ops = { .get_time_info = skl_get_time_info, .mmap = snd_pcm_lib_default_mmap, .page = snd_pcm_sgbuf_ops_page, + .ack = skl_platform_ack, }; static void skl_pcm_free(struct snd_pcm *pcm) From cc362899460886a877f10dcfcba09b8f3b8febda Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Wed, 17 Aug 2016 12:19:51 +0530 Subject: [PATCH 0087/1276] ASoC: Intel: CNL: Fetch ACPI data for SoundWire Master The DSDT table contains information about the SoundWire capabalities for Master controller and device. This patch add API's which fetch the SoundWire capabilities for Master. Change-Id: Iddb147a48faaf67f7fb4ee378e72858cb14e721a Signed-off-by: Anil Bhawangirkar Signed-off-by: Guneshwor Singh --- include/linux/sdw/sdw_cnl.h | 8 +- sound/soc/intel/skylake/Makefile | 4 + sound/soc/intel/skylake/cnl-acpi.c | 160 +++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 sound/soc/intel/skylake/cnl-acpi.c diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h index 6a9281c458b7..1a40244e466d 100644 --- a/include/linux/sdw/sdw_cnl.h +++ b/include/linux/sdw/sdw_cnl.h @@ -27,7 +27,7 @@ #define SDW_CNL_PM_TIMEOUT 3000 /* ms */ -#define CNL_SDW_MAX_PORTS 15 +#define CNL_SDW_MAX_PORTS 9 /* Maximum number hardware tries to send command if the command failed */ #define CNL_SDW_MAX_CMD_RETRIES 15 @@ -119,7 +119,9 @@ struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, enum sdw_data_direction direction, enum cnl_sdw_pdi_stream_type stream_type); void cnl_sdw_free_port(struct sdw_master *mstr, int port_num); - - +int cnl_sdw_get_master_caps(struct device *dev, + struct sdw_master_capabilities *m_cap); +int cnl_sdw_get_master_dev_caps(struct device *dev, + struct sdw_master_capabilities *m_cap, int dev_port_num); #endif diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 9a3ddb969d4e..7b8cf119aeae 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -13,6 +13,10 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ skl-sst-utils.o skl-fwlog.o +ifdef CONFIG_SDW + snd-soc-skl-ipc-objs += cnl-acpi.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o #Skylake Clock device support diff --git a/sound/soc/intel/skylake/cnl-acpi.c b/sound/soc/intel/skylake/cnl-acpi.c new file mode 100644 index 000000000000..1bee574f2ab8 --- /dev/null +++ b/sound/soc/intel/skylake/cnl-acpi.c @@ -0,0 +1,160 @@ +/* + * cnl-acpi.c - Intel CNL Platform ACPI parsing + * + * Copyright (C) 2016 Intel Corp + * + * Author: Anil Bhawangirkar + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * This is SoundWire device path defined for Master and Slave. + */ +static acpi_string path_sdw_dev[] = {"SWD0.", "SWD1.", "SWD2.", "SWD3.", + "SWD4.", "SWD5.", "SWD6.", "SWD7.", "SWD8."}; +#define DSDT_ACPI_PATH "\\_SB.PCI0.RP01.PXSX.HDAS.SNDW" +#define SDW_PATH_CTRL_MAX 4 +#define SDW_PATH_DEV_MAX 9 +ACPI_MODULE_NAME("ACPI"); + +static union acpi_object *sdw_dpn_extract_capab(union acpi_object *obj) +{ + union acpi_object *data, *package1, *package2; + + data = &obj->package.elements[1]; + package1 = &data->package.elements[0]; + package2 = &package1->package.elements[1]; + return package2; +} + +static union acpi_object *sdw_scd_extract_capab(union acpi_object *obj) +{ + union acpi_object *data, *package1, *package2, *package3; + + data = &obj->package.elements[1]; + package1 = &data->package.elements[3]; + package2 = &package1->package.elements[1]; + package3 = &package2->package.elements[0]; + return package3; +} + +static union acpi_object *sdw_acpi_init_object(struct device *dev, char path[]) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, DSDT_ACPI_PATH, &handle); + if (ACPI_FAILURE(status)) { + dev_err(dev, "ACPI Object evaluation is failed...\n"); + return NULL; + } + + status = acpi_evaluate_object(handle, path, NULL, &buf); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + ACPI_EXCEPTION((AE_INFO, status, "Invalid pathname\n")); + return NULL; + } + obj = buf.pointer; + return obj; +} + +static int sdw_fill_master_dpn_caps(struct sdw_master_capabilities *map, + union acpi_object *obj) +{ + struct sdw_mstr_dpn_capabilities *dpn_cap; + union acpi_object *ret_data; + + dpn_cap = &map->sdw_dpn_cap[0]; + ret_data = sdw_dpn_extract_capab(obj); + dpn_cap->dpn_type = ret_data->integer.value; + return 0; +} + +static int sdw_fill_master_scd_caps(struct sdw_master_capabilities *map, + union acpi_object *obj) +{ + union acpi_object *ret_data; + + ret_data = sdw_scd_extract_capab(obj); + map->base_clk_freq = ret_data->integer.value; + return 0; +} + +static int sdw_acpi_mstr_map_data(struct sdw_master_capabilities *mcap, + struct device *dev, acpi_string path_name, char path[]) +{ + union acpi_object *obj; + + obj = sdw_acpi_init_object(dev, path); + if (obj && (obj->type == ACPI_TYPE_PACKAGE)) { + if (!strcmp(path_name, "SCD")) + sdw_fill_master_scd_caps(mcap, obj); + else if (!strcmp(path_name, "DPN")) + sdw_fill_master_dpn_caps(mcap, obj); + } + + kfree(obj); + return 0; +} + +/* + * get the ACPI data for SoundWire Master contoller capablities + */ +int cnl_sdw_get_master_caps(struct device *dev, + struct sdw_master_capabilities *m_cap) +{ + acpi_string path_sdw_ctrl = {"SCD"}; + char path[SDW_PATH_CTRL_MAX]; + + strcpy(path, path_sdw_ctrl); + sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_ctrl, path); + if (!m_cap) { + dev_err(dev, "SoundWire controller mapping failed...\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(cnl_sdw_get_master_caps); + +/* + * get the ACPI data for SoundWire Master devices capabilities + */ +int cnl_sdw_get_master_dev_caps(struct device *dev, + struct sdw_master_capabilities *m_cap, int dev_port_num) +{ + acpi_string path_sdw_dpn = {"DPN"}; + char path[SDW_PATH_DEV_MAX]; + + snprintf(path, sizeof(path), "%s%s", + path_sdw_dev[dev_port_num], path_sdw_dpn); + sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_dpn, path); + if (!m_cap) { + dev_err(dev, "SoundWire device mapping failed...\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(cnl_sdw_get_master_dev_caps); From 7639ba8500b926d21252972f1931cd255bd9c3e0 Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Tue, 23 Aug 2016 15:08:03 +0530 Subject: [PATCH 0088/1276] ASoC: Intel: CNL: get SoundWire Master capabilities and map it This patch get the SoundWire Master capabilities and map the SoundWire capabalities to Master controller and device. Change-Id: I78eaafc640bd0bb3d8a423569a2590b94c5d7de8 Signed-off-by: Anil Bhawangirkar --- sound/soc/intel/skylake/cnl-sst.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index d888d31d0ea2..c5ad9fde2fed 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -511,7 +511,7 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, void __iomem *mmio_base, int irq) { - struct sdw_master_capabilities *m_cap; + struct sdw_master_capabilities *m_cap, *map_data; struct sdw_mstr_dp0_capabilities *dp0_cap; struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; @@ -542,10 +542,21 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, master[i].dev.platform_data = p_data; m_cap = &master[i].mstr_capabilities; dp0_cap = &m_cap->sdw_dp0_cap; + map_data = kzalloc(sizeof(*m_cap), GFP_KERNEL); + if (!map_data) + return -ENOMEM; + /* This function retrieves the data for SoundWire controller */ + cnl_sdw_get_master_caps(dev, map_data); master[i].nr = i; master[i].timeout = -1; master[i].retries = CNL_SDW_MAX_CMD_RETRIES; - m_cap->base_clk_freq = 9.6 * 1000 * 1000; + m_cap->base_clk_freq = map_data->base_clk_freq; + /* TODO: Frequency is not read correctly in ACPI code */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + m_cap->base_clk_freq = 9600000; +#else + m_cap->base_clk_freq = 12000000; +#endif strcpy(master[i].name, "cnl_sdw_mstr"); m_cap->highphy_capable = false; m_cap->monitor_handover_supported = false; @@ -562,7 +573,17 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, return -ENOMEM; for (j = 0; j < m_cap->num_data_ports; j++) { dpn_cap = &m_cap->sdw_dpn_cap[j]; + map_data->sdw_dpn_cap = kzalloc(sizeof(*dpn_cap), + GFP_KERNEL); + if (!map_data->sdw_dpn_cap) + return -ENOMEM; + /* + * This function retrieves the data + * for SoundWire devices. + */ + cnl_sdw_get_master_dev_caps(dev, map_data, j); /* Both Tx and Rx */ + dpn_cap->dpn_type = map_data->sdw_dpn_cap->dpn_type; dpn_cap->port_direction = 0x3; dpn_cap->port_number = j; dpn_cap->max_word_length = 32; @@ -588,7 +609,9 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dpn_cap->block_packing_mode_mask = SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; + kfree(map_data->sdw_dpn_cap); } + kfree(map_data); master[i].link_sync_mask = 0x0; switch (i) { case 0: From 75dd2784c5f17e3b960e93c32bbafba40991214f Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Wed, 17 Aug 2016 12:23:35 +0530 Subject: [PATCH 0089/1276] [REVERTME]ASoC: Intel: CNL: Define DPN package for SDW1. As DPN package entries are not defined in the BIOS for SoundWire device 1(SDW1), the capability data-port-type can not be mapped for SDW1. As a workaround, hard code data-port-type for SDW1. Workaround is not required for other devices, since BIOS has entry for them. This patch can be reverted once BIOS has entry for SDW1. Change-Id: I15b0ec44731feb1f1f45466a0f9a9426d31a8dee Signed-off-by: Anil Bhawangirkar --- sound/soc/intel/skylake/cnl-sst.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c5ad9fde2fed..18be70b87728 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -583,7 +583,12 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, */ cnl_sdw_get_master_dev_caps(dev, map_data, j); /* Both Tx and Rx */ - dpn_cap->dpn_type = map_data->sdw_dpn_cap->dpn_type; + /* WORKAROUND: hard code for SDW1 */ + if (j == 1) + dpn_cap->dpn_type = SDW_FULL_DP; + else + dpn_cap->dpn_type = + map_data->sdw_dpn_cap->dpn_type; dpn_cap->port_direction = 0x3; dpn_cap->port_number = j; dpn_cap->max_word_length = 32; From f90087702b406dd65cdf2373ebc8d729f12b75be Mon Sep 17 00:00:00 2001 From: "Yadav, PramodX K" Date: Fri, 15 Jul 2016 20:01:42 +0530 Subject: [PATCH 0090/1276] ASoC: Intel: Skylake: Support for 24KHz SoC DMIC capture Change-Id: I69a1dc19badb335747a15c2b8e0994f81ac95116 Signed-off-by: Yadav, PramodX K --- sound/soc/intel/skylake/skl-pcm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 228f00763094..3ac58520240a 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -983,7 +983,9 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "System Capture", .channels_min = HDA_MONO, .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_KNOT | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, @@ -1021,7 +1023,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "Reference Capture", .channels_min = HDA_MONO, .channels_max = HDA_QUAD, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, From d710333f66db4417350e4ba1ac00d9d6e320d28e Mon Sep 17 00:00:00 2001 From: bardliao Date: Tue, 28 Jun 2016 16:33:32 +0800 Subject: [PATCH 0091/1276] ASoC: add rt700 codec driver Change-Id: I1b817a96b47310091307a45e7c85aa99009f3b78 Signed-off-by: Bard Liao Signed-off-by: Hardik Shah --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 4 + sound/soc/codecs/rt700-sdw.c | 387 + sound/soc/codecs/rt700-sdw.h | 53635 +++++++++++++++++++++++++++++++++ sound/soc/codecs/rt700.c | 1600 + sound/soc/codecs/rt700.h | 161 + 6 files changed, 55797 insertions(+) create mode 100644 sound/soc/codecs/rt700-sdw.c create mode 100644 sound/soc/codecs/rt700-sdw.h create mode 100644 sound/soc/codecs/rt700.c create mode 100644 sound/soc/codecs/rt700.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 461f41268b68..b002d79d910e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -894,6 +894,16 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate +config SND_SOC_RT700 + tristate "Realtek RT700 Codec" + depends on SDW + +config SND_SOC_RT700_SDW + tristate "Realtek RT700 Codec - SDW" + depends on SDW + select SND_SOC_RT700 + select REGMAP_SDW + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f37fca3b9960..5aed74a73acf 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -151,6 +151,8 @@ snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-rt5682-objs := rt5682.o +snd-soc-rt700-objs := rt700.o +snd-soc-rt700-sdw-objs := rt700-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -415,6 +417,8 @@ obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o +obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o +obj-$(CONFIG_SND_SOC_RT700_SDW) += snd-soc-rt700-sdw.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c new file mode 100644 index 000000000000..a2533edfa48a --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.c @@ -0,0 +1,387 @@ +/* + * rt700-sdw.c -- rt700 ALSA SoC audio driver + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * ALC700 ASoC Codec Driver based Intel Dummy SdW codec driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include "rt700.h" +#include "rt700-sdw.h" + +static bool rt700_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0020: + case 0x0030: + case 0x0060: + case 0x0070: + case 0x00e0: + case 0x00f0: + case 0x0000 ... 0x0005: + case 0x0023 ... 0x0026: + case 0x0032 ... 0x0036: + case 0x0040 ... 0x0046: + case 0x0050 ... 0x0055: + case 0x0100 ... 0x0105: + case 0x0120 ... 0x0127: + case 0x0130 ... 0x0137: + case 0x0200 ... 0x0205: + case 0x0220 ... 0x0227: + case 0x0230 ... 0x0237: + case 0x0300 ... 0x0305: + case 0x0320 ... 0x0327: + case 0x0330 ... 0x0337: + case 0x0400 ... 0x0405: + case 0x0420 ... 0x0427: + case 0x0430 ... 0x0437: + case 0x0500 ... 0x0505: + case 0x0520 ... 0x0527: + case 0x0530 ... 0x0537: + case 0x0600 ... 0x0605: + case 0x0620 ... 0x0627: + case 0x0630 ... 0x0637: + case 0x0700 ... 0x0705: + case 0x0720 ... 0x0727: + case 0x0730 ... 0x0737: + case 0x0800 ... 0x0805: + case 0x0820 ... 0x0827: + case 0x0830 ... 0x0837: + case 0x0f00 ... 0x0f27: + case 0x0f30 ... 0x0f37: + case 0x2000 ... 0x200e: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2100 ... 0x213f: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2231: + case 0x3000 ... 0xffff: + return true; + default: + return false; + } +} + +static bool rt700_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0000: + case 0x0001: + case 0x0004: + case 0x0005: + case 0x0040: + case 0x0042: + case 0x0043: + case 0x0044: + case 0x0045: + case 0x0104: + case 0x0204: + case 0x0304: + case 0x0404: + case 0x0504: + case 0x0604: + case 0x0704: + case 0x0804: + case 0x0105: + case 0x0205: + case 0x0305: + case 0x0405: + case 0x0505: + case 0x0605: + case 0x0705: + case 0x0805: + case 0x0f00: + case 0x0f04: + case 0x0f05: + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x0050 ... 0x0055: /* device id */ + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + return true; + case 0x3000 ... 0xffff: /* HD-A command */ + return false; /* should always read from cache */ + default: + return true; /*for debug*/ + } +} + +const struct regmap_config rt700_sdw_regmap = { + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + + .readable_reg = rt700_readable_register, /* Readable registers */ + .volatile_reg = rt700_volatile_register, /* volatile register */ + + .max_register = 0xffff, /* Maximum number of register */ + .reg_defaults = rt700_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l) { + unsigned int offset_h, offset_l, e_verb; + + if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ + if (verb == 0x7ff) /* special case */ + offset_h = 0; + else + offset_h = 0x3000; + + if (verb & 0x800) /* get command */ + e_verb = (verb - 0xf00) | 0x80; + else /* set command */ + e_verb = (verb - 0x700); + + *sdw_data_h = payload; /* 7 bits payload */ + *sdw_addr_l = *sdw_data_l = 0; + } else { /* 4 bits command */ + if ((verb & 0x800) == 0x800) { /* read */ + offset_h = 0x9000; + offset_l = 0xa000; + } else { /* write */ + offset_h = 0x7000; + offset_l = 0x8000; + } + e_verb = verb >> 8; + *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ + *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ + *sdw_addr_l += offset_l; + *sdw_data_l = payload & 0xff; + } + + *sdw_addr_h = (e_verb << 8) | nid; + *sdw_addr_h += offset_h; + + return 0; +} +EXPORT_SYMBOL(hda_to_sdw); + +static int rt700_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + unsigned int bus_config_frequencies[] = { + 2400000, 4800000, 9600000, 3000000, 6000000, 12000000, 19200000}; + unsigned int freq_supported[] = { + 19200000, 2400000, 4800000, 9600000, 12000000, 12288000}; + int i, j; + + cap.wake_up_unavailable = false; + cap.test_mode_supported = true; + cap.clock_stop1_mode_supported = true; + cap.simplified_clock_stop_prepare = true; + cap.highphy_capable = false; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 1; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 8; /* Inport 4, Outport 4 */ + cap.scp_impl_def_intr_mask = 0x4; + cap.sdw_dp0_cap = devm_kzalloc(&sdw->dev, + sizeof(struct sdw_slv_dp0_capabilities), GFP_KERNEL); + + cap.sdw_dp0_cap->max_word_length = 64; + cap.sdw_dp0_cap->min_word_length = 1; + cap.sdw_dp0_cap->num_word_length = 0; + cap.sdw_dp0_cap->word_length_buffer = NULL; + cap.sdw_dp0_cap->bra_use_flow_control = 0; + cap.sdw_dp0_cap->bra_initiator_supported = 0; + cap.sdw_dp0_cap->ch_prepare_mode = SDW_SIMPLIFIED_CP_SM; + cap.sdw_dp0_cap->impl_def_response_supported = false; + /* Address 0x0001, bit 0: port ready, bit 2: BRA failure*/ + cap.sdw_dp0_cap->imp_def_intr_mask = 0; + cap.sdw_dp0_cap->impl_def_bpt_supported = true; + cap.sdw_dp0_cap->slave_bra_cap.max_bus_frequency = 24000000; + cap.sdw_dp0_cap->slave_bra_cap.min_bus_frequency = 2400000; + cap.sdw_dp0_cap->slave_bra_cap.num_bus_config_frequency = 7; + cap.sdw_dp0_cap->slave_bra_cap.bus_config_frequencies = + bus_config_frequencies; + cap.sdw_dp0_cap->slave_bra_cap.max_data_per_frame = 470; + cap.sdw_dp0_cap->slave_bra_cap.min_us_between_transactions = 0; + cap.sdw_dp0_cap->slave_bra_cap.max_bandwidth = (500 - 48) * 48000; + cap.sdw_dp0_cap->slave_bra_cap.mode_block_alignment = 1; + + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 1; i <= cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i-1]; + if ((i % 2) == 0) + dpn_cap->port_direction = SDW_PORT_SINK; + else + dpn_cap->port_direction = SDW_PORT_SOURCE; + + dpn_cap->port_number = i; + dpn_cap->max_word_length = 64; + dpn_cap->min_word_length = 1; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; + dpn_cap->imp_def_intr_mask = 0; /* bit 0: Test Fail */ + dpn_cap->min_ch_num = 2; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 24000000; + prop->min_frequency = 2400000; + prop->num_freq_configs = 0; + prop->freq_supported = devm_kzalloc(&sdw->dev, + prop->num_freq_configs * + (sizeof(unsigned int)), + GFP_KERNEL); + if (!prop->freq_supported) + return -ENOMEM; + + memcpy(prop->freq_supported, freq_supported, + prop->num_freq_configs * + (sizeof(unsigned int))); + prop->glitchless_transitions_mask = 0x0; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int rt700_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct alc700 *alc700_priv; + struct regmap *regmap; + int ret; + + alc700_priv = kzalloc(sizeof(struct alc700), GFP_KERNEL); + if(!alc700_priv) { + return -ENOMEM; + } + dev_set_drvdata(&sdw->dev, alc700_priv); + alc700_priv->sdw = sdw; + alc700_priv->params = kzalloc(sizeof(struct sdw_bus_params), GFP_KERNEL); + if(!alc700_priv->params) { + return -ENOMEM; + } + regmap = devm_regmap_init_sdwint(sdw, &rt700_sdw_regmap); + if (!regmap) + return -EINVAL; + ret = rt700_register_sdw_capabilties(sdw, sdw_id); + if (ret) + return ret; + ret = sdw_slave_get_bus_params(sdw, alc700_priv->params); + if (ret) + return -EFAULT; + return rt700_probe(&sdw->dev, regmap, sdw); +} + +static int rt700_sdw_remove(struct sdw_slv *sdw) +{ + return rt700_remove(&sdw->dev); +} + +static const struct sdw_slv_id rt700_id[] = { + {"10:02:5d:07:00:00", 0}, + {"11:02:5d:07:00:00", 0}, + {"12:02:5d:07:00:00", 0}, + {"13:02:5d:07:00:00", 0}, + {"14:02:5d:07:00:00", 0}, + {"15:02:5d:07:00:00", 0}, + {"16:02:5d:07:00:00", 0}, + {"17:02:5d:07:00:00", 0}, + {"10:02:5d:07:01:00", 0}, + {"11:02:5d:07:01:00", 0}, + {"12:02:5d:07:01:00", 0}, + {"13:02:5d:07:01:00", 0}, + {"14:02:5d:07:01:00", 0}, + {"15:02:5d:07:01:00", 0}, + {"16:02:5d:07:01:00", 0}, + {"17:02:5d:07:01:00", 0}, + {"10:02:5d:07:00:01", 0}, + {} +}; + +MODULE_DEVICE_TABLE(sdwint, rt700_id); + + +static int rt700_sdw_handle_impl_def_interrupts(struct sdw_slv *swdev, + struct sdw_impl_def_intr_stat *intr_status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&swdev->dev); + bool hp, mic; + + pr_debug("%s control_port_stat=%x port0_stat=%x\n", __func__, + intr_status->control_port_stat, intr_status->port0_stat); + if (intr_status->control_port_stat & 0x4) { + rt700_jack_detect(rt700, &hp, &mic); + pr_info("%s hp=%d mic=%d\n", __func__, hp, mic); + } + + return 0; +} + +static struct sdw_slave_driver rt700_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "rt700", + .pm = &rt700_runtime_pm, + }, + .probe = rt700_sdw_probe, + .remove = rt700_sdw_remove, + .handle_impl_def_interrupts = rt700_sdw_handle_impl_def_interrupts, + + .id_table = rt700_id, +}; + +module_sdw_slave_driver(rt700_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h new file mode 100644 index 000000000000..b691ba01819d --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.h @@ -0,0 +1,53635 @@ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __RT700_SDW_H__ +#define __RT700_SDW_H__ + +static const struct reg_default rt700_reg_defaults[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x0000 }, + { 0x0002, 0x0000 }, + { 0x0003, 0x0000 }, + { 0x0004, 0x0000 }, + { 0x0005, 0x0001 }, + { 0x0020, 0x0000 }, + { 0x0022, 0x0000 }, + { 0x0023, 0x0000 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x0000 }, + { 0x0026, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x0000 }, + { 0x0042, 0x0000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0020 }, + { 0x0045, 0x0001 }, + { 0x0046, 0x0000 }, + { 0x0050, 0x0000 }, + { 0x0051, 0x0000 }, + { 0x0052, 0x0000 }, + { 0x0053, 0x0000 }, + { 0x0054, 0x0000 }, + { 0x0055, 0x0000 }, + { 0x0060, 0x0000 }, + { 0x0070, 0x0000 }, + { 0x00e0, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x0100, 0x0000 }, + { 0x0101, 0x0000 }, + { 0x0102, 0x0000 }, + { 0x0103, 0x0000 }, + { 0x0104, 0x0000 }, + { 0x0105, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x0000 }, + { 0x0122, 0x0000 }, + { 0x0123, 0x0000 }, + { 0x0124, 0x0000 }, + { 0x0125, 0x0000 }, + { 0x0126, 0x0000 }, + { 0x0127, 0x0000 }, + { 0x0130, 0x0000 }, + { 0x0131, 0x0000 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0134, 0x0000 }, + { 0x0135, 0x0000 }, + { 0x0136, 0x0000 }, + { 0x0137, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0220, 0x0000 }, + { 0x0221, 0x0000 }, + { 0x0222, 0x0000 }, + { 0x0223, 0x0000 }, + { 0x0224, 0x0000 }, + { 0x0225, 0x0000 }, + { 0x0226, 0x0000 }, + { 0x0227, 0x0000 }, + { 0x0230, 0x0000 }, + { 0x0231, 0x0000 }, + { 0x0232, 0x0000 }, + { 0x0233, 0x0000 }, + { 0x0234, 0x0000 }, + { 0x0235, 0x0000 }, + { 0x0236, 0x0000 }, + { 0x0237, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x0301, 0x0000 }, + { 0x0302, 0x0000 }, + { 0x0303, 0x0000 }, + { 0x0304, 0x0000 }, + { 0x0305, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0323, 0x0000 }, + { 0x0324, 0x0000 }, + { 0x0325, 0x0000 }, + { 0x0326, 0x0000 }, + { 0x0327, 0x0000 }, + { 0x0330, 0x0000 }, + { 0x0331, 0x0000 }, + { 0x0332, 0x0000 }, + { 0x0333, 0x0000 }, + { 0x0334, 0x0000 }, + { 0x0335, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0337, 0x0000 }, + { 0x0400, 0x0000 }, + { 0x0401, 0x0000 }, + { 0x0402, 0x0000 }, + { 0x0403, 0x0000 }, + { 0x0404, 0x0000 }, + { 0x0405, 0x0000 }, + { 0x0420, 0x0000 }, + { 0x0421, 0x0000 }, + { 0x0422, 0x0000 }, + { 0x0423, 0x0000 }, + { 0x0424, 0x0000 }, + { 0x0425, 0x0000 }, + { 0x0426, 0x0000 }, + { 0x0427, 0x0000 }, + { 0x0430, 0x0000 }, + { 0x0431, 0x0000 }, + { 0x0432, 0x0000 }, + { 0x0433, 0x0000 }, + { 0x0434, 0x0000 }, + { 0x0435, 0x0000 }, + { 0x0436, 0x0000 }, + { 0x0437, 0x0000 }, + { 0x0500, 0x0000 }, + { 0x0501, 0x0000 }, + { 0x0502, 0x0000 }, + { 0x0503, 0x0000 }, + { 0x0504, 0x0000 }, + { 0x0505, 0x0000 }, + { 0x0520, 0x0000 }, + { 0x0521, 0x0000 }, + { 0x0522, 0x0000 }, + { 0x0523, 0x0000 }, + { 0x0524, 0x0000 }, + { 0x0525, 0x0000 }, + { 0x0526, 0x0000 }, + { 0x0527, 0x0000 }, + { 0x0530, 0x0000 }, + { 0x0531, 0x0000 }, + { 0x0532, 0x0000 }, + { 0x0533, 0x0000 }, + { 0x0534, 0x0000 }, + { 0x0535, 0x0000 }, + { 0x0536, 0x0000 }, + { 0x0537, 0x0000 }, + { 0x0600, 0x0000 }, + { 0x0601, 0x0000 }, + { 0x0602, 0x0000 }, + { 0x0603, 0x0000 }, + { 0x0604, 0x0000 }, + { 0x0605, 0x0000 }, + { 0x0620, 0x0000 }, + { 0x0621, 0x0000 }, + { 0x0622, 0x0000 }, + { 0x0623, 0x0000 }, + { 0x0624, 0x0000 }, + { 0x0625, 0x0000 }, + { 0x0626, 0x0000 }, + { 0x0627, 0x0000 }, + { 0x0630, 0x0000 }, + { 0x0631, 0x0000 }, + { 0x0632, 0x0000 }, + { 0x0633, 0x0000 }, + { 0x0634, 0x0000 }, + { 0x0635, 0x0000 }, + { 0x0636, 0x0000 }, + { 0x0637, 0x0000 }, + { 0x0700, 0x0000 }, + { 0x0701, 0x0000 }, + { 0x0702, 0x0000 }, + { 0x0703, 0x0000 }, + { 0x0704, 0x0000 }, + { 0x0705, 0x0000 }, + { 0x0720, 0x0000 }, + { 0x0721, 0x0000 }, + { 0x0722, 0x0000 }, + { 0x0723, 0x0000 }, + { 0x0724, 0x0000 }, + { 0x0725, 0x0000 }, + { 0x0726, 0x0000 }, + { 0x0727, 0x0000 }, + { 0x0730, 0x0000 }, + { 0x0731, 0x0000 }, + { 0x0732, 0x0000 }, + { 0x0733, 0x0000 }, + { 0x0734, 0x0000 }, + { 0x0735, 0x0000 }, + { 0x0736, 0x0000 }, + { 0x0737, 0x0000 }, + { 0x0800, 0x0000 }, + { 0x0801, 0x0000 }, + { 0x0802, 0x0000 }, + { 0x0803, 0x0000 }, + { 0x0804, 0x0000 }, + { 0x0805, 0x0000 }, + { 0x0820, 0x0000 }, + { 0x0821, 0x0000 }, + { 0x0822, 0x0000 }, + { 0x0823, 0x0000 }, + { 0x0824, 0x0000 }, + { 0x0825, 0x0000 }, + { 0x0826, 0x0000 }, + { 0x0827, 0x0000 }, + { 0x0830, 0x0000 }, + { 0x0831, 0x0000 }, + { 0x0832, 0x0000 }, + { 0x0833, 0x0000 }, + { 0x0834, 0x0000 }, + { 0x0835, 0x0000 }, + { 0x0836, 0x0000 }, + { 0x0837, 0x0000 }, + { 0x0f00, 0x0000 }, + { 0x0f01, 0x0000 }, + { 0x0f02, 0x0000 }, + { 0x0f03, 0x0000 }, + { 0x0f04, 0x0000 }, + { 0x0f05, 0x0000 }, + { 0x0f20, 0x0000 }, + { 0x0f21, 0x0000 }, + { 0x0f22, 0x0000 }, + { 0x0f23, 0x0000 }, + { 0x0f24, 0x0000 }, + { 0x0f25, 0x0000 }, + { 0x0f26, 0x0000 }, + { 0x0f27, 0x0000 }, + { 0x0f30, 0x0000 }, + { 0x0f31, 0x0000 }, + { 0x0f32, 0x0000 }, + { 0x0f33, 0x0000 }, + { 0x0f34, 0x0000 }, + { 0x0f35, 0x0000 }, + { 0x0f36, 0x0000 }, + { 0x0f37, 0x0000 }, + { 0x2000, 0x0000 }, + { 0x2001, 0x0000 }, + { 0x2002, 0x0000 }, + { 0x2003, 0x0000 }, + { 0x2004, 0x0000 }, + { 0x2005, 0x0000 }, + { 0x2006, 0x0000 }, + { 0x2007, 0x0000 }, + { 0x2008, 0x0000 }, + { 0x2009, 0x0003 }, + { 0x200a, 0x0003 }, + { 0x200b, 0x0000 }, + { 0x200c, 0x0000 }, + { 0x200d, 0x0000 }, + { 0x200e, 0x0000 }, + { 0x2012, 0x0000 }, + { 0x2013, 0x0000 }, + { 0x2014, 0x0000 }, + { 0x2015, 0x0000 }, + { 0x2016, 0x0000 }, + { 0x201a, 0x0000 }, + { 0x201b, 0x0000 }, + { 0x201c, 0x0000 }, + { 0x201d, 0x0000 }, + { 0x201e, 0x0000 }, + { 0x201f, 0x0000 }, + { 0x2020, 0x0000 }, + { 0x2021, 0x0000 }, + { 0x2022, 0x0000 }, + { 0x2023, 0x0000 }, + { 0x2024, 0x0000 }, + { 0x2025, 0x0002 }, + { 0x2026, 0x0000 }, + { 0x2027, 0x0000 }, + { 0x2029, 0x0000 }, + { 0x202a, 0x0000 }, + { 0x202d, 0x0000 }, + { 0x202e, 0x0000 }, + { 0x202f, 0x0000 }, + { 0x2030, 0x0000 }, + { 0x2031, 0x0000 }, + { 0x2032, 0x0000 }, + { 0x2033, 0x0000 }, + { 0x2034, 0x0000 }, + { 0x2100, 0x0000 }, + { 0x2101, 0x0000 }, + { 0x2102, 0x0000 }, + { 0x2103, 0x0000 }, + { 0x2104, 0x0000 }, + { 0x2105, 0x0000 }, + { 0x2106, 0x0000 }, + { 0x2107, 0x0000 }, + { 0x2108, 0x0000 }, + { 0x2109, 0x0000 }, + { 0x210a, 0x0000 }, + { 0x210b, 0x0000 }, + { 0x210c, 0x0000 }, + { 0x210d, 0x0000 }, + { 0x210e, 0x0000 }, + { 0x210f, 0x0000 }, + { 0x2110, 0x0000 }, + { 0x2111, 0x0000 }, + { 0x2112, 0x0000 }, + { 0x2113, 0x0000 }, + { 0x2114, 0x0000 }, + { 0x2115, 0x0000 }, + { 0x2116, 0x0000 }, + { 0x2117, 0x0000 }, + { 0x2118, 0x0000 }, + { 0x2119, 0x0000 }, + { 0x211a, 0x0000 }, + { 0x211b, 0x0000 }, + { 0x211c, 0x0000 }, + { 0x211d, 0x0000 }, + { 0x211e, 0x0000 }, + { 0x211f, 0x0000 }, + { 0x2120, 0x0000 }, + { 0x2121, 0x0000 }, + { 0x2122, 0x0000 }, + { 0x2123, 0x0000 }, + { 0x2124, 0x0000 }, + { 0x2125, 0x0000 }, + { 0x2126, 0x0000 }, + { 0x2127, 0x0000 }, + { 0x2128, 0x0000 }, + { 0x2129, 0x0000 }, + { 0x212a, 0x0000 }, + { 0x212b, 0x0000 }, + { 0x212c, 0x0000 }, + { 0x212d, 0x0000 }, + { 0x212e, 0x0000 }, + { 0x212f, 0x0000 }, + { 0x2130, 0x0000 }, + { 0x2131, 0x0000 }, + { 0x2132, 0x0000 }, + { 0x2133, 0x0000 }, + { 0x2134, 0x0000 }, + { 0x2135, 0x0000 }, + { 0x2136, 0x0000 }, + { 0x2137, 0x0000 }, + { 0x2138, 0x0000 }, + { 0x2139, 0x0000 }, + { 0x213a, 0x0000 }, + { 0x213b, 0x0000 }, + { 0x213c, 0x0000 }, + { 0x213d, 0x0000 }, + { 0x213e, 0x0000 }, + { 0x213f, 0x0000 }, + { 0x2200, 0x0000 }, + { 0x2201, 0x0000 }, + { 0x2202, 0x0000 }, + { 0x2203, 0x0000 }, + { 0x2204, 0x0000 }, + { 0x2206, 0x0000 }, + { 0x2207, 0x0000 }, + { 0x2208, 0x0000 }, + { 0x2209, 0x0000 }, + { 0x220a, 0x0000 }, + { 0x220b, 0x0000 }, + { 0x220c, 0x0000 }, + { 0x220d, 0x0000 }, + { 0x220e, 0x0000 }, + { 0x220f, 0x0000 }, + { 0x2211, 0x0000 }, + { 0x2212, 0x0000 }, + { 0x2220, 0x0000 }, + { 0x2221, 0x0000 }, + { 0x2222, 0x0000 }, + { 0x2223, 0x0000 }, + { 0x2230, 0x0000 }, + { 0x2231, 0x0000 }, + { 0x3000, 0x0000 }, + { 0x3001, 0x0000 }, + { 0x3002, 0x0000 }, + { 0x3003, 0x0000 }, + { 0x3004, 0x0000 }, + { 0x3005, 0x0000 }, + { 0x3006, 0x0000 }, + { 0x3007, 0x0000 }, + { 0x3008, 0x0000 }, + { 0x3009, 0x0000 }, + { 0x300a, 0x0000 }, + { 0x300b, 0x0000 }, + { 0x300c, 0x0000 }, + { 0x300d, 0x0000 }, + { 0x300e, 0x0000 }, + { 0x300f, 0x0000 }, + { 0x3010, 0x0000 }, + { 0x3011, 0x0000 }, + { 0x3012, 0x0000 }, + { 0x3013, 0x0000 }, + { 0x3014, 0x0000 }, + { 0x3015, 0x0000 }, + { 0x3016, 0x0000 }, + { 0x3017, 0x0000 }, + { 0x3018, 0x0000 }, + { 0x3019, 0x0000 }, + { 0x301a, 0x0000 }, + { 0x301b, 0x0000 }, + { 0x301c, 0x0000 }, + { 0x301d, 0x0000 }, + { 0x301e, 0x0000 }, + { 0x301f, 0x0000 }, + { 0x3020, 0x0000 }, + { 0x3021, 0x0000 }, + { 0x3022, 0x0000 }, + { 0x3023, 0x0000 }, + { 0x3024, 0x0000 }, + { 0x3025, 0x0000 }, + { 0x3026, 0x0000 }, + { 0x3027, 0x0000 }, + { 0x3028, 0x0000 }, + { 0x3029, 0x0000 }, + { 0x302a, 0x0000 }, + { 0x302b, 0x0000 }, + { 0x302c, 0x0000 }, + { 0x302d, 0x0000 }, + { 0x302e, 0x0000 }, + { 0x302f, 0x0000 }, + { 0x3030, 0x0000 }, + { 0x3031, 0x0000 }, + { 0x3032, 0x0000 }, + { 0x3033, 0x0000 }, + { 0x3034, 0x0000 }, + { 0x3035, 0x0000 }, + { 0x3036, 0x0000 }, + { 0x3037, 0x0000 }, + { 0x3038, 0x0000 }, + { 0x3039, 0x0000 }, + { 0x303a, 0x0000 }, + { 0x303b, 0x0000 }, + { 0x303c, 0x0000 }, + { 0x303d, 0x0000 }, + { 0x303e, 0x0000 }, + { 0x303f, 0x0000 }, + { 0x3040, 0x0000 }, + { 0x3041, 0x0000 }, + { 0x3042, 0x0000 }, + { 0x3043, 0x0000 }, + { 0x3044, 0x0000 }, + { 0x3045, 0x0000 }, + { 0x3046, 0x0000 }, + { 0x3047, 0x0000 }, + { 0x3048, 0x0000 }, + { 0x3049, 0x0000 }, + { 0x304a, 0x0000 }, + { 0x304b, 0x0000 }, + { 0x304c, 0x0000 }, + { 0x304d, 0x0000 }, + { 0x304e, 0x0000 }, + { 0x304f, 0x0000 }, + { 0x3050, 0x0000 }, + { 0x3051, 0x0000 }, + { 0x3052, 0x0000 }, + { 0x3053, 0x0000 }, + { 0x3054, 0x0000 }, + { 0x3055, 0x0000 }, + { 0x3056, 0x0000 }, + { 0x3057, 0x0000 }, + { 0x3058, 0x0000 }, + { 0x3059, 0x0000 }, + { 0x305a, 0x0000 }, + { 0x305b, 0x0000 }, + { 0x305c, 0x0000 }, + { 0x305d, 0x0000 }, + { 0x305e, 0x0000 }, + { 0x305f, 0x0000 }, + { 0x3060, 0x0000 }, + { 0x3061, 0x0000 }, + { 0x3062, 0x0000 }, + { 0x3063, 0x0000 }, + { 0x3064, 0x0000 }, + { 0x3065, 0x0000 }, + { 0x3066, 0x0000 }, + { 0x3067, 0x0000 }, + { 0x3068, 0x0000 }, + { 0x3069, 0x0000 }, + { 0x306a, 0x0000 }, + { 0x306b, 0x0000 }, + { 0x306c, 0x0000 }, + { 0x306d, 0x0000 }, + { 0x306e, 0x0000 }, + { 0x306f, 0x0000 }, + { 0x3070, 0x0000 }, + { 0x3071, 0x0000 }, + { 0x3072, 0x0000 }, + { 0x3073, 0x0000 }, + { 0x3074, 0x0000 }, + { 0x3075, 0x0000 }, + { 0x3076, 0x0000 }, + { 0x3077, 0x0000 }, + { 0x3078, 0x0000 }, + { 0x3079, 0x0000 }, + { 0x307a, 0x0000 }, + { 0x307b, 0x0000 }, + { 0x307c, 0x0000 }, + { 0x307d, 0x0000 }, + { 0x307e, 0x0000 }, + { 0x307f, 0x0000 }, + { 0x3080, 0x0000 }, + { 0x3081, 0x0000 }, + { 0x3082, 0x0000 }, + { 0x3083, 0x0000 }, + { 0x3084, 0x0000 }, + { 0x3085, 0x0000 }, + { 0x3086, 0x0000 }, + { 0x3087, 0x0000 }, + { 0x3088, 0x0000 }, + { 0x3089, 0x0000 }, + { 0x308a, 0x0000 }, + { 0x308b, 0x0000 }, + { 0x308c, 0x0000 }, + { 0x308d, 0x0000 }, + { 0x308e, 0x0000 }, + { 0x308f, 0x0000 }, + { 0x3090, 0x0000 }, + { 0x3091, 0x0000 }, + { 0x3092, 0x0000 }, + { 0x3093, 0x0000 }, + { 0x3094, 0x0000 }, + { 0x3095, 0x0000 }, + { 0x3096, 0x0000 }, + { 0x3097, 0x0000 }, + { 0x3098, 0x0000 }, + { 0x3099, 0x0000 }, + { 0x309a, 0x0000 }, + { 0x309b, 0x0000 }, + { 0x309c, 0x0000 }, + { 0x309d, 0x0000 }, + { 0x309e, 0x0000 }, + { 0x309f, 0x0000 }, + { 0x30a0, 0x0000 }, + { 0x30a1, 0x0000 }, + { 0x30a2, 0x0000 }, + { 0x30a3, 0x0000 }, + { 0x30a4, 0x0000 }, + { 0x30a5, 0x0000 }, + { 0x30a6, 0x0000 }, + { 0x30a7, 0x0000 }, + { 0x30a8, 0x0000 }, + { 0x30a9, 0x0000 }, + { 0x30aa, 0x0000 }, + { 0x30ab, 0x0000 }, + { 0x30ac, 0x0000 }, + { 0x30ad, 0x0000 }, + { 0x30ae, 0x0000 }, + { 0x30af, 0x0000 }, + { 0x30b0, 0x0000 }, + { 0x30b1, 0x0000 }, + { 0x30b2, 0x0000 }, + { 0x30b3, 0x0000 }, + { 0x30b4, 0x0000 }, + { 0x30b5, 0x0000 }, + { 0x30b6, 0x0000 }, + { 0x30b7, 0x0000 }, + { 0x30b8, 0x0000 }, + { 0x30b9, 0x0000 }, + { 0x30ba, 0x0000 }, + { 0x30bb, 0x0000 }, + { 0x30bc, 0x0000 }, + { 0x30bd, 0x0000 }, + { 0x30be, 0x0000 }, + { 0x30bf, 0x0000 }, + { 0x30c0, 0x0000 }, + { 0x30c1, 0x0000 }, + { 0x30c2, 0x0000 }, + { 0x30c3, 0x0000 }, + { 0x30c4, 0x0000 }, + { 0x30c5, 0x0000 }, + { 0x30c6, 0x0000 }, + { 0x30c7, 0x0000 }, + { 0x30c8, 0x0000 }, + { 0x30c9, 0x0000 }, + { 0x30ca, 0x0000 }, + { 0x30cb, 0x0000 }, + { 0x30cc, 0x0000 }, + { 0x30cd, 0x0000 }, + { 0x30ce, 0x0000 }, + { 0x30cf, 0x0000 }, + { 0x30d0, 0x0000 }, + { 0x30d1, 0x0000 }, + { 0x30d2, 0x0000 }, + { 0x30d3, 0x0000 }, + { 0x30d4, 0x0000 }, + { 0x30d5, 0x0000 }, + { 0x30d6, 0x0000 }, + { 0x30d7, 0x0000 }, + { 0x30d8, 0x0000 }, + { 0x30d9, 0x0000 }, + { 0x30da, 0x0000 }, + { 0x30db, 0x0000 }, + { 0x30dc, 0x0000 }, + { 0x30dd, 0x0000 }, + { 0x30de, 0x0000 }, + { 0x30df, 0x0000 }, + { 0x30e0, 0x0000 }, + { 0x30e1, 0x0000 }, + { 0x30e2, 0x0000 }, + { 0x30e3, 0x0000 }, + { 0x30e4, 0x0000 }, + { 0x30e5, 0x0000 }, + { 0x30e6, 0x0000 }, + { 0x30e7, 0x0000 }, + { 0x30e8, 0x0000 }, + { 0x30e9, 0x0000 }, + { 0x30ea, 0x0000 }, + { 0x30eb, 0x0000 }, + { 0x30ec, 0x0000 }, + { 0x30ed, 0x0000 }, + { 0x30ee, 0x0000 }, + { 0x30ef, 0x0000 }, + { 0x30f0, 0x0000 }, + { 0x30f1, 0x0000 }, + { 0x30f2, 0x0000 }, + { 0x30f3, 0x0000 }, + { 0x30f4, 0x0000 }, + { 0x30f5, 0x0000 }, + { 0x30f6, 0x0000 }, + { 0x30f7, 0x0000 }, + { 0x30f8, 0x0000 }, + { 0x30f9, 0x0000 }, + { 0x30fa, 0x0000 }, + { 0x30fb, 0x0000 }, + { 0x30fc, 0x0000 }, + { 0x30fd, 0x0000 }, + { 0x30fe, 0x0000 }, + { 0x30ff, 0x0000 }, + { 0x3100, 0x0000 }, + { 0x3101, 0x0000 }, + { 0x3102, 0x0000 }, + { 0x3103, 0x0000 }, + { 0x3104, 0x0000 }, + { 0x3105, 0x0000 }, + { 0x3106, 0x0000 }, + { 0x3107, 0x0000 }, + { 0x3108, 0x0000 }, + { 0x3109, 0x0000 }, + { 0x310a, 0x0000 }, + { 0x310b, 0x0000 }, + { 0x310c, 0x0000 }, + { 0x310d, 0x0000 }, + { 0x310e, 0x0000 }, + { 0x310f, 0x0000 }, + { 0x3110, 0x0000 }, + { 0x3111, 0x0000 }, + { 0x3112, 0x0000 }, + { 0x3113, 0x0000 }, + { 0x3114, 0x0000 }, + { 0x3115, 0x0000 }, + { 0x3116, 0x0000 }, + { 0x3117, 0x0000 }, + { 0x3118, 0x0000 }, + { 0x3119, 0x0000 }, + { 0x311a, 0x0000 }, + { 0x311b, 0x0000 }, + { 0x311c, 0x0000 }, + { 0x311d, 0x0000 }, + { 0x311e, 0x0000 }, + { 0x311f, 0x0000 }, + { 0x3120, 0x0000 }, + { 0x3121, 0x0000 }, + { 0x3122, 0x0000 }, + { 0x3123, 0x0000 }, + { 0x3124, 0x0000 }, + { 0x3125, 0x0000 }, + { 0x3126, 0x0000 }, + { 0x3127, 0x0000 }, + { 0x3128, 0x0000 }, + { 0x3129, 0x0000 }, + { 0x312a, 0x0000 }, + { 0x312b, 0x0000 }, + { 0x312c, 0x0000 }, + { 0x312d, 0x0000 }, + { 0x312e, 0x0000 }, + { 0x312f, 0x0000 }, + { 0x3130, 0x0000 }, + { 0x3131, 0x0000 }, + { 0x3132, 0x0000 }, + { 0x3133, 0x0000 }, + { 0x3134, 0x0000 }, + { 0x3135, 0x0000 }, + { 0x3136, 0x0000 }, + { 0x3137, 0x0000 }, + { 0x3138, 0x0000 }, + { 0x3139, 0x0000 }, + { 0x313a, 0x0000 }, + { 0x313b, 0x0000 }, + { 0x313c, 0x0000 }, + { 0x313d, 0x0000 }, + { 0x313e, 0x0000 }, + { 0x313f, 0x0000 }, + { 0x3140, 0x0000 }, + { 0x3141, 0x0000 }, + { 0x3142, 0x0000 }, + { 0x3143, 0x0000 }, + { 0x3144, 0x0000 }, + { 0x3145, 0x0000 }, + { 0x3146, 0x0000 }, + { 0x3147, 0x0000 }, + { 0x3148, 0x0000 }, + { 0x3149, 0x0000 }, + { 0x314a, 0x0000 }, + { 0x314b, 0x0000 }, + { 0x314c, 0x0000 }, + { 0x314d, 0x0000 }, + { 0x314e, 0x0000 }, + { 0x314f, 0x0000 }, + { 0x3150, 0x0000 }, + { 0x3151, 0x0000 }, + { 0x3152, 0x0000 }, + { 0x3153, 0x0000 }, + { 0x3154, 0x0000 }, + { 0x3155, 0x0000 }, + { 0x3156, 0x0000 }, + { 0x3157, 0x0000 }, + { 0x3158, 0x0000 }, + { 0x3159, 0x0000 }, + { 0x315a, 0x0000 }, + { 0x315b, 0x0000 }, + { 0x315c, 0x0000 }, + { 0x315d, 0x0000 }, + { 0x315e, 0x0000 }, + { 0x315f, 0x0000 }, + { 0x3160, 0x0000 }, + { 0x3161, 0x0000 }, + { 0x3162, 0x0000 }, + { 0x3163, 0x0000 }, + { 0x3164, 0x0000 }, + { 0x3165, 0x0000 }, + { 0x3166, 0x0000 }, + { 0x3167, 0x0000 }, + { 0x3168, 0x0000 }, + { 0x3169, 0x0000 }, + { 0x316a, 0x0000 }, + { 0x316b, 0x0000 }, + { 0x316c, 0x0000 }, + { 0x316d, 0x0000 }, + { 0x316e, 0x0000 }, + { 0x316f, 0x0000 }, + { 0x3170, 0x0000 }, + { 0x3171, 0x0000 }, + { 0x3172, 0x0000 }, + { 0x3173, 0x0000 }, + { 0x3174, 0x0000 }, + { 0x3175, 0x0000 }, + { 0x3176, 0x0000 }, + { 0x3177, 0x0000 }, + { 0x3178, 0x0000 }, + { 0x3179, 0x0000 }, + { 0x317a, 0x0000 }, + { 0x317b, 0x0000 }, + { 0x317c, 0x0000 }, + { 0x317d, 0x0000 }, + { 0x317e, 0x0000 }, + { 0x317f, 0x0000 }, + { 0x3180, 0x0000 }, + { 0x3181, 0x0000 }, + { 0x3182, 0x0000 }, + { 0x3183, 0x0000 }, + { 0x3184, 0x0000 }, + { 0x3185, 0x0000 }, + { 0x3186, 0x0000 }, + { 0x3187, 0x0000 }, + { 0x3188, 0x0000 }, + { 0x3189, 0x0000 }, + { 0x318a, 0x0000 }, + { 0x318b, 0x0000 }, + { 0x318c, 0x0000 }, + { 0x318d, 0x0000 }, + { 0x318e, 0x0000 }, + { 0x318f, 0x0000 }, + { 0x3190, 0x0000 }, + { 0x3191, 0x0000 }, + { 0x3192, 0x0000 }, + { 0x3193, 0x0000 }, + { 0x3194, 0x0000 }, + { 0x3195, 0x0000 }, + { 0x3196, 0x0000 }, + { 0x3197, 0x0000 }, + { 0x3198, 0x0000 }, + { 0x3199, 0x0000 }, + { 0x319a, 0x0000 }, + { 0x319b, 0x0000 }, + { 0x319c, 0x0000 }, + { 0x319d, 0x0000 }, + { 0x319e, 0x0000 }, + { 0x319f, 0x0000 }, + { 0x31a0, 0x0000 }, + { 0x31a1, 0x0000 }, + { 0x31a2, 0x0000 }, + { 0x31a3, 0x0000 }, + { 0x31a4, 0x0000 }, + { 0x31a5, 0x0000 }, + { 0x31a6, 0x0000 }, + { 0x31a7, 0x0000 }, + { 0x31a8, 0x0000 }, + { 0x31a9, 0x0000 }, + { 0x31aa, 0x0000 }, + { 0x31ab, 0x0000 }, + { 0x31ac, 0x0000 }, + { 0x31ad, 0x0000 }, + { 0x31ae, 0x0000 }, + { 0x31af, 0x0000 }, + { 0x31b0, 0x0000 }, + { 0x31b1, 0x0000 }, + { 0x31b2, 0x0000 }, + { 0x31b3, 0x0000 }, + { 0x31b4, 0x0000 }, + { 0x31b5, 0x0000 }, + { 0x31b6, 0x0000 }, + { 0x31b7, 0x0000 }, + { 0x31b8, 0x0000 }, + { 0x31b9, 0x0000 }, + { 0x31ba, 0x0000 }, + { 0x31bb, 0x0000 }, + { 0x31bc, 0x0000 }, + { 0x31bd, 0x0000 }, + { 0x31be, 0x0000 }, + { 0x31bf, 0x0000 }, + { 0x31c0, 0x0000 }, + { 0x31c1, 0x0000 }, + { 0x31c2, 0x0000 }, + { 0x31c3, 0x0000 }, + { 0x31c4, 0x0000 }, + { 0x31c5, 0x0000 }, + { 0x31c6, 0x0000 }, + { 0x31c7, 0x0000 }, + { 0x31c8, 0x0000 }, + { 0x31c9, 0x0000 }, + { 0x31ca, 0x0000 }, + { 0x31cb, 0x0000 }, + { 0x31cc, 0x0000 }, + { 0x31cd, 0x0000 }, + { 0x31ce, 0x0000 }, + { 0x31cf, 0x0000 }, + { 0x31d0, 0x0000 }, + { 0x31d1, 0x0000 }, + { 0x31d2, 0x0000 }, + { 0x31d3, 0x0000 }, + { 0x31d4, 0x0000 }, + { 0x31d5, 0x0000 }, + { 0x31d6, 0x0000 }, + { 0x31d7, 0x0000 }, + { 0x31d8, 0x0000 }, + { 0x31d9, 0x0000 }, + { 0x31da, 0x0000 }, + { 0x31db, 0x0000 }, + { 0x31dc, 0x0000 }, + { 0x31dd, 0x0000 }, + { 0x31de, 0x0000 }, + { 0x31df, 0x0000 }, + { 0x31e0, 0x0000 }, + { 0x31e1, 0x0000 }, + { 0x31e2, 0x0000 }, + { 0x31e3, 0x0000 }, + { 0x31e4, 0x0000 }, + { 0x31e5, 0x0000 }, + { 0x31e6, 0x0000 }, + { 0x31e7, 0x0000 }, + { 0x31e8, 0x0000 }, + { 0x31e9, 0x0000 }, + { 0x31ea, 0x0000 }, + { 0x31eb, 0x0000 }, + { 0x31ec, 0x0000 }, + { 0x31ed, 0x0000 }, + { 0x31ee, 0x0000 }, + { 0x31ef, 0x0000 }, + { 0x31f0, 0x0000 }, + { 0x31f1, 0x0000 }, + { 0x31f2, 0x0000 }, + { 0x31f3, 0x0000 }, + { 0x31f4, 0x0000 }, + { 0x31f5, 0x0000 }, + { 0x31f6, 0x0000 }, + { 0x31f7, 0x0000 }, + { 0x31f8, 0x0000 }, + { 0x31f9, 0x0000 }, + { 0x31fa, 0x0000 }, + { 0x31fb, 0x0000 }, + { 0x31fc, 0x0000 }, + { 0x31fd, 0x0000 }, + { 0x31fe, 0x0000 }, + { 0x31ff, 0x0000 }, + { 0x3200, 0x0000 }, + { 0x3201, 0x0000 }, + { 0x3202, 0x0000 }, + { 0x3203, 0x0000 }, + { 0x3204, 0x0000 }, + { 0x3205, 0x0000 }, + { 0x3206, 0x0000 }, + { 0x3207, 0x0000 }, + { 0x3208, 0x0000 }, + { 0x3209, 0x0000 }, + { 0x320a, 0x0000 }, + { 0x320b, 0x0000 }, + { 0x320c, 0x0000 }, + { 0x320d, 0x0000 }, + { 0x320e, 0x0000 }, + { 0x320f, 0x0000 }, + { 0x3210, 0x0000 }, + { 0x3211, 0x0000 }, + { 0x3212, 0x0000 }, + { 0x3213, 0x0000 }, + { 0x3214, 0x0000 }, + { 0x3215, 0x0000 }, + { 0x3216, 0x0000 }, + { 0x3217, 0x0000 }, + { 0x3218, 0x0000 }, + { 0x3219, 0x0000 }, + { 0x321a, 0x0000 }, + { 0x321b, 0x0000 }, + { 0x321c, 0x0000 }, + { 0x321d, 0x0000 }, + { 0x321e, 0x0000 }, + { 0x321f, 0x0000 }, + { 0x3220, 0x0000 }, + { 0x3221, 0x0000 }, + { 0x3222, 0x0000 }, + { 0x3223, 0x0000 }, + { 0x3224, 0x0000 }, + { 0x3225, 0x0000 }, + { 0x3226, 0x0000 }, + { 0x3227, 0x0000 }, + { 0x3228, 0x0000 }, + { 0x3229, 0x0000 }, + { 0x322a, 0x0000 }, + { 0x322b, 0x0000 }, + { 0x322c, 0x0000 }, + { 0x322d, 0x0000 }, + { 0x322e, 0x0000 }, + { 0x322f, 0x0000 }, + { 0x3230, 0x0000 }, + { 0x3231, 0x0000 }, + { 0x3232, 0x0000 }, + { 0x3233, 0x0000 }, + { 0x3234, 0x0000 }, + { 0x3235, 0x0000 }, + { 0x3236, 0x0000 }, + { 0x3237, 0x0000 }, + { 0x3238, 0x0000 }, + { 0x3239, 0x0000 }, + { 0x323a, 0x0000 }, + { 0x323b, 0x0000 }, + { 0x323c, 0x0000 }, + { 0x323d, 0x0000 }, + { 0x323e, 0x0000 }, + { 0x323f, 0x0000 }, + { 0x3240, 0x0000 }, + { 0x3241, 0x0000 }, + { 0x3242, 0x0000 }, + { 0x3243, 0x0000 }, + { 0x3244, 0x0000 }, + { 0x3245, 0x0000 }, + { 0x3246, 0x0000 }, + { 0x3247, 0x0000 }, + { 0x3248, 0x0000 }, + { 0x3249, 0x0000 }, + { 0x324a, 0x0000 }, + { 0x324b, 0x0000 }, + { 0x324c, 0x0000 }, + { 0x324d, 0x0000 }, + { 0x324e, 0x0000 }, + { 0x324f, 0x0000 }, + { 0x3250, 0x0000 }, + { 0x3251, 0x0000 }, + { 0x3252, 0x0000 }, + { 0x3253, 0x0000 }, + { 0x3254, 0x0000 }, + { 0x3255, 0x0000 }, + { 0x3256, 0x0000 }, + { 0x3257, 0x0000 }, + { 0x3258, 0x0000 }, + { 0x3259, 0x0000 }, + { 0x325a, 0x0000 }, + { 0x325b, 0x0000 }, + { 0x325c, 0x0000 }, + { 0x325d, 0x0000 }, + { 0x325e, 0x0000 }, + { 0x325f, 0x0000 }, + { 0x3260, 0x0000 }, + { 0x3261, 0x0000 }, + { 0x3262, 0x0000 }, + { 0x3263, 0x0000 }, + { 0x3264, 0x0000 }, + { 0x3265, 0x0000 }, + { 0x3266, 0x0000 }, + { 0x3267, 0x0000 }, + { 0x3268, 0x0000 }, + { 0x3269, 0x0000 }, + { 0x326a, 0x0000 }, + { 0x326b, 0x0000 }, + { 0x326c, 0x0000 }, + { 0x326d, 0x0000 }, + { 0x326e, 0x0000 }, + { 0x326f, 0x0000 }, + { 0x3270, 0x0000 }, + { 0x3271, 0x0000 }, + { 0x3272, 0x0000 }, + { 0x3273, 0x0000 }, + { 0x3274, 0x0000 }, + { 0x3275, 0x0000 }, + { 0x3276, 0x0000 }, + { 0x3277, 0x0000 }, + { 0x3278, 0x0000 }, + { 0x3279, 0x0000 }, + { 0x327a, 0x0000 }, + { 0x327b, 0x0000 }, + { 0x327c, 0x0000 }, + { 0x327d, 0x0000 }, + { 0x327e, 0x0000 }, + { 0x327f, 0x0000 }, + { 0x3280, 0x0000 }, + { 0x3281, 0x0000 }, + { 0x3282, 0x0000 }, + { 0x3283, 0x0000 }, + { 0x3284, 0x0000 }, + { 0x3285, 0x0000 }, + { 0x3286, 0x0000 }, + { 0x3287, 0x0000 }, + { 0x3288, 0x0000 }, + { 0x3289, 0x0000 }, + { 0x328a, 0x0000 }, + { 0x328b, 0x0000 }, + { 0x328c, 0x0000 }, + { 0x328d, 0x0000 }, + { 0x328e, 0x0000 }, + { 0x328f, 0x0000 }, + { 0x3290, 0x0000 }, + { 0x3291, 0x0000 }, + { 0x3292, 0x0000 }, + { 0x3293, 0x0000 }, + { 0x3294, 0x0000 }, + { 0x3295, 0x0000 }, + { 0x3296, 0x0000 }, + { 0x3297, 0x0000 }, + { 0x3298, 0x0000 }, + { 0x3299, 0x0000 }, + { 0x329a, 0x0000 }, + { 0x329b, 0x0000 }, + { 0x329c, 0x0000 }, + { 0x329d, 0x0000 }, + { 0x329e, 0x0000 }, + { 0x329f, 0x0000 }, + { 0x32a0, 0x0000 }, + { 0x32a1, 0x0000 }, + { 0x32a2, 0x0000 }, + { 0x32a3, 0x0000 }, + { 0x32a4, 0x0000 }, + { 0x32a5, 0x0000 }, + { 0x32a6, 0x0000 }, + { 0x32a7, 0x0000 }, + { 0x32a8, 0x0000 }, + { 0x32a9, 0x0000 }, + { 0x32aa, 0x0000 }, + { 0x32ab, 0x0000 }, + { 0x32ac, 0x0000 }, + { 0x32ad, 0x0000 }, + { 0x32ae, 0x0000 }, + { 0x32af, 0x0000 }, + { 0x32b0, 0x0000 }, + { 0x32b1, 0x0000 }, + { 0x32b2, 0x0000 }, + { 0x32b3, 0x0000 }, + { 0x32b4, 0x0000 }, + { 0x32b5, 0x0000 }, + { 0x32b6, 0x0000 }, + { 0x32b7, 0x0000 }, + { 0x32b8, 0x0000 }, + { 0x32b9, 0x0000 }, + { 0x32ba, 0x0000 }, + { 0x32bb, 0x0000 }, + { 0x32bc, 0x0000 }, + { 0x32bd, 0x0000 }, + { 0x32be, 0x0000 }, + { 0x32bf, 0x0000 }, + { 0x32c0, 0x0000 }, + { 0x32c1, 0x0000 }, + { 0x32c2, 0x0000 }, + { 0x32c3, 0x0000 }, + { 0x32c4, 0x0000 }, + { 0x32c5, 0x0000 }, + { 0x32c6, 0x0000 }, + { 0x32c7, 0x0000 }, + { 0x32c8, 0x0000 }, + { 0x32c9, 0x0000 }, + { 0x32ca, 0x0000 }, + { 0x32cb, 0x0000 }, + { 0x32cc, 0x0000 }, + { 0x32cd, 0x0000 }, + { 0x32ce, 0x0000 }, + { 0x32cf, 0x0000 }, + { 0x32d0, 0x0000 }, + { 0x32d1, 0x0000 }, + { 0x32d2, 0x0000 }, + { 0x32d3, 0x0000 }, + { 0x32d4, 0x0000 }, + { 0x32d5, 0x0000 }, + { 0x32d6, 0x0000 }, + { 0x32d7, 0x0000 }, + { 0x32d8, 0x0000 }, + { 0x32d9, 0x0000 }, + { 0x32da, 0x0000 }, + { 0x32db, 0x0000 }, + { 0x32dc, 0x0000 }, + { 0x32dd, 0x0000 }, + { 0x32de, 0x0000 }, + { 0x32df, 0x0000 }, + { 0x32e0, 0x0000 }, + { 0x32e1, 0x0000 }, + { 0x32e2, 0x0000 }, + { 0x32e3, 0x0000 }, + { 0x32e4, 0x0000 }, + { 0x32e5, 0x0000 }, + { 0x32e6, 0x0000 }, + { 0x32e7, 0x0000 }, + { 0x32e8, 0x0000 }, + { 0x32e9, 0x0000 }, + { 0x32ea, 0x0000 }, + { 0x32eb, 0x0000 }, + { 0x32ec, 0x0000 }, + { 0x32ed, 0x0000 }, + { 0x32ee, 0x0000 }, + { 0x32ef, 0x0000 }, + { 0x32f0, 0x0000 }, + { 0x32f1, 0x0000 }, + { 0x32f2, 0x0000 }, + { 0x32f3, 0x0000 }, + { 0x32f4, 0x0000 }, + { 0x32f5, 0x0000 }, + { 0x32f6, 0x0000 }, + { 0x32f7, 0x0000 }, + { 0x32f8, 0x0000 }, + { 0x32f9, 0x0000 }, + { 0x32fa, 0x0000 }, + { 0x32fb, 0x0000 }, + { 0x32fc, 0x0000 }, + { 0x32fd, 0x0000 }, + { 0x32fe, 0x0000 }, + { 0x32ff, 0x0000 }, + { 0x3300, 0x0000 }, + { 0x3301, 0x0000 }, + { 0x3302, 0x0000 }, + { 0x3303, 0x0000 }, + { 0x3304, 0x0000 }, + { 0x3305, 0x0000 }, + { 0x3306, 0x0000 }, + { 0x3307, 0x0000 }, + { 0x3308, 0x0000 }, + { 0x3309, 0x0000 }, + { 0x330a, 0x0000 }, + { 0x330b, 0x0000 }, + { 0x330c, 0x0000 }, + { 0x330d, 0x0000 }, + { 0x330e, 0x0000 }, + { 0x330f, 0x0000 }, + { 0x3310, 0x0000 }, + { 0x3311, 0x0000 }, + { 0x3312, 0x0000 }, + { 0x3313, 0x0000 }, + { 0x3314, 0x0000 }, + { 0x3315, 0x0000 }, + { 0x3316, 0x0000 }, + { 0x3317, 0x0000 }, + { 0x3318, 0x0000 }, + { 0x3319, 0x0000 }, + { 0x331a, 0x0000 }, + { 0x331b, 0x0000 }, + { 0x331c, 0x0000 }, + { 0x331d, 0x0000 }, + { 0x331e, 0x0000 }, + { 0x331f, 0x0000 }, + { 0x3320, 0x0000 }, + { 0x3321, 0x0000 }, + { 0x3322, 0x0000 }, + { 0x3323, 0x0000 }, + { 0x3324, 0x0000 }, + { 0x3325, 0x0000 }, + { 0x3326, 0x0000 }, + { 0x3327, 0x0000 }, + { 0x3328, 0x0000 }, + { 0x3329, 0x0000 }, + { 0x332a, 0x0000 }, + { 0x332b, 0x0000 }, + { 0x332c, 0x0000 }, + { 0x332d, 0x0000 }, + { 0x332e, 0x0000 }, + { 0x332f, 0x0000 }, + { 0x3330, 0x0000 }, + { 0x3331, 0x0000 }, + { 0x3332, 0x0000 }, + { 0x3333, 0x0000 }, + { 0x3334, 0x0000 }, + { 0x3335, 0x0000 }, + { 0x3336, 0x0000 }, + { 0x3337, 0x0000 }, + { 0x3338, 0x0000 }, + { 0x3339, 0x0000 }, + { 0x333a, 0x0000 }, + { 0x333b, 0x0000 }, + { 0x333c, 0x0000 }, + { 0x333d, 0x0000 }, + { 0x333e, 0x0000 }, + { 0x333f, 0x0000 }, + { 0x3340, 0x0000 }, + { 0x3341, 0x0000 }, + { 0x3342, 0x0000 }, + { 0x3343, 0x0000 }, + { 0x3344, 0x0000 }, + { 0x3345, 0x0000 }, + { 0x3346, 0x0000 }, + { 0x3347, 0x0000 }, + { 0x3348, 0x0000 }, + { 0x3349, 0x0000 }, + { 0x334a, 0x0000 }, + { 0x334b, 0x0000 }, + { 0x334c, 0x0000 }, + { 0x334d, 0x0000 }, + { 0x334e, 0x0000 }, + { 0x334f, 0x0000 }, + { 0x3350, 0x0000 }, + { 0x3351, 0x0000 }, + { 0x3352, 0x0000 }, + { 0x3353, 0x0000 }, + { 0x3354, 0x0000 }, + { 0x3355, 0x0000 }, + { 0x3356, 0x0000 }, + { 0x3357, 0x0000 }, + { 0x3358, 0x0000 }, + { 0x3359, 0x0000 }, + { 0x335a, 0x0000 }, + { 0x335b, 0x0000 }, + { 0x335c, 0x0000 }, + { 0x335d, 0x0000 }, + { 0x335e, 0x0000 }, + { 0x335f, 0x0000 }, + { 0x3360, 0x0000 }, + { 0x3361, 0x0000 }, + { 0x3362, 0x0000 }, + { 0x3363, 0x0000 }, + { 0x3364, 0x0000 }, + { 0x3365, 0x0000 }, + { 0x3366, 0x0000 }, + { 0x3367, 0x0000 }, + { 0x3368, 0x0000 }, + { 0x3369, 0x0000 }, + { 0x336a, 0x0000 }, + { 0x336b, 0x0000 }, + { 0x336c, 0x0000 }, + { 0x336d, 0x0000 }, + { 0x336e, 0x0000 }, + { 0x336f, 0x0000 }, + { 0x3370, 0x0000 }, + { 0x3371, 0x0000 }, + { 0x3372, 0x0000 }, + { 0x3373, 0x0000 }, + { 0x3374, 0x0000 }, + { 0x3375, 0x0000 }, + { 0x3376, 0x0000 }, + { 0x3377, 0x0000 }, + { 0x3378, 0x0000 }, + { 0x3379, 0x0000 }, + { 0x337a, 0x0000 }, + { 0x337b, 0x0000 }, + { 0x337c, 0x0000 }, + { 0x337d, 0x0000 }, + { 0x337e, 0x0000 }, + { 0x337f, 0x0000 }, + { 0x3380, 0x0000 }, + { 0x3381, 0x0000 }, + { 0x3382, 0x0000 }, + { 0x3383, 0x0000 }, + { 0x3384, 0x0000 }, + { 0x3385, 0x0000 }, + { 0x3386, 0x0000 }, + { 0x3387, 0x0000 }, + { 0x3388, 0x0000 }, + { 0x3389, 0x0000 }, + { 0x338a, 0x0000 }, + { 0x338b, 0x0000 }, + { 0x338c, 0x0000 }, + { 0x338d, 0x0000 }, + { 0x338e, 0x0000 }, + { 0x338f, 0x0000 }, + { 0x3390, 0x0000 }, + { 0x3391, 0x0000 }, + { 0x3392, 0x0000 }, + { 0x3393, 0x0000 }, + { 0x3394, 0x0000 }, + { 0x3395, 0x0000 }, + { 0x3396, 0x0000 }, + { 0x3397, 0x0000 }, + { 0x3398, 0x0000 }, + { 0x3399, 0x0000 }, + { 0x339a, 0x0000 }, + { 0x339b, 0x0000 }, + { 0x339c, 0x0000 }, + { 0x339d, 0x0000 }, + { 0x339e, 0x0000 }, + { 0x339f, 0x0000 }, + { 0x33a0, 0x0000 }, + { 0x33a1, 0x0000 }, + { 0x33a2, 0x0000 }, + { 0x33a3, 0x0000 }, + { 0x33a4, 0x0000 }, + { 0x33a5, 0x0000 }, + { 0x33a6, 0x0000 }, + { 0x33a7, 0x0000 }, + { 0x33a8, 0x0000 }, + { 0x33a9, 0x0000 }, + { 0x33aa, 0x0000 }, + { 0x33ab, 0x0000 }, + { 0x33ac, 0x0000 }, + { 0x33ad, 0x0000 }, + { 0x33ae, 0x0000 }, + { 0x33af, 0x0000 }, + { 0x33b0, 0x0000 }, + { 0x33b1, 0x0000 }, + { 0x33b2, 0x0000 }, + { 0x33b3, 0x0000 }, + { 0x33b4, 0x0000 }, + { 0x33b5, 0x0000 }, + { 0x33b6, 0x0000 }, + { 0x33b7, 0x0000 }, + { 0x33b8, 0x0000 }, + { 0x33b9, 0x0000 }, + { 0x33ba, 0x0000 }, + { 0x33bb, 0x0000 }, + { 0x33bc, 0x0000 }, + { 0x33bd, 0x0000 }, + { 0x33be, 0x0000 }, + { 0x33bf, 0x0000 }, + { 0x33c0, 0x0000 }, + { 0x33c1, 0x0000 }, + { 0x33c2, 0x0000 }, + { 0x33c3, 0x0000 }, + { 0x33c4, 0x0000 }, + { 0x33c5, 0x0000 }, + { 0x33c6, 0x0000 }, + { 0x33c7, 0x0000 }, + { 0x33c8, 0x0000 }, + { 0x33c9, 0x0000 }, + { 0x33ca, 0x0000 }, + { 0x33cb, 0x0000 }, + { 0x33cc, 0x0000 }, + { 0x33cd, 0x0000 }, + { 0x33ce, 0x0000 }, + { 0x33cf, 0x0000 }, + { 0x33d0, 0x0000 }, + { 0x33d1, 0x0000 }, + { 0x33d2, 0x0000 }, + { 0x33d3, 0x0000 }, + { 0x33d4, 0x0000 }, + { 0x33d5, 0x0000 }, + { 0x33d6, 0x0000 }, + { 0x33d7, 0x0000 }, + { 0x33d8, 0x0000 }, + { 0x33d9, 0x0000 }, + { 0x33da, 0x0000 }, + { 0x33db, 0x0000 }, + { 0x33dc, 0x0000 }, + { 0x33dd, 0x0000 }, + { 0x33de, 0x0000 }, + { 0x33df, 0x0000 }, + { 0x33e0, 0x0000 }, + { 0x33e1, 0x0000 }, + { 0x33e2, 0x0000 }, + { 0x33e3, 0x0000 }, + { 0x33e4, 0x0000 }, + { 0x33e5, 0x0000 }, + { 0x33e6, 0x0000 }, + { 0x33e7, 0x0000 }, + { 0x33e8, 0x0000 }, + { 0x33e9, 0x0000 }, + { 0x33ea, 0x0000 }, + { 0x33eb, 0x0000 }, + { 0x33ec, 0x0000 }, + { 0x33ed, 0x0000 }, + { 0x33ee, 0x0000 }, + { 0x33ef, 0x0000 }, + { 0x33f0, 0x0000 }, + { 0x33f1, 0x0000 }, + { 0x33f2, 0x0000 }, + { 0x33f3, 0x0000 }, + { 0x33f4, 0x0000 }, + { 0x33f5, 0x0000 }, + { 0x33f6, 0x0000 }, + { 0x33f7, 0x0000 }, + { 0x33f8, 0x0000 }, + { 0x33f9, 0x0000 }, + { 0x33fa, 0x0000 }, + { 0x33fb, 0x0000 }, + { 0x33fc, 0x0000 }, + { 0x33fd, 0x0000 }, + { 0x33fe, 0x0000 }, + { 0x33ff, 0x0000 }, + { 0x3400, 0x0000 }, + { 0x3401, 0x0000 }, + { 0x3402, 0x0000 }, + { 0x3403, 0x0000 }, + { 0x3404, 0x0000 }, + { 0x3405, 0x0000 }, + { 0x3406, 0x0000 }, + { 0x3407, 0x0000 }, + { 0x3408, 0x0000 }, + { 0x3409, 0x0000 }, + { 0x340a, 0x0000 }, + { 0x340b, 0x0000 }, + { 0x340c, 0x0000 }, + { 0x340d, 0x0000 }, + { 0x340e, 0x0000 }, + { 0x340f, 0x0000 }, + { 0x3410, 0x0000 }, + { 0x3411, 0x0000 }, + { 0x3412, 0x0000 }, + { 0x3413, 0x0000 }, + { 0x3414, 0x0000 }, + { 0x3415, 0x0000 }, + { 0x3416, 0x0000 }, + { 0x3417, 0x0000 }, + { 0x3418, 0x0000 }, + { 0x3419, 0x0000 }, + { 0x341a, 0x0000 }, + { 0x341b, 0x0000 }, + { 0x341c, 0x0000 }, + { 0x341d, 0x0000 }, + { 0x341e, 0x0000 }, + { 0x341f, 0x0000 }, + { 0x3420, 0x0000 }, + { 0x3421, 0x0000 }, + { 0x3422, 0x0000 }, + { 0x3423, 0x0000 }, + { 0x3424, 0x0000 }, + { 0x3425, 0x0000 }, + { 0x3426, 0x0000 }, + { 0x3427, 0x0000 }, + { 0x3428, 0x0000 }, + { 0x3429, 0x0000 }, + { 0x342a, 0x0000 }, + { 0x342b, 0x0000 }, + { 0x342c, 0x0000 }, + { 0x342d, 0x0000 }, + { 0x342e, 0x0000 }, + { 0x342f, 0x0000 }, + { 0x3430, 0x0000 }, + { 0x3431, 0x0000 }, + { 0x3432, 0x0000 }, + { 0x3433, 0x0000 }, + { 0x3434, 0x0000 }, + { 0x3435, 0x0000 }, + { 0x3436, 0x0000 }, + { 0x3437, 0x0000 }, + { 0x3438, 0x0000 }, + { 0x3439, 0x0000 }, + { 0x343a, 0x0000 }, + { 0x343b, 0x0000 }, + { 0x343c, 0x0000 }, + { 0x343d, 0x0000 }, + { 0x343e, 0x0000 }, + { 0x343f, 0x0000 }, + { 0x3440, 0x0000 }, + { 0x3441, 0x0000 }, + { 0x3442, 0x0000 }, + { 0x3443, 0x0000 }, + { 0x3444, 0x0000 }, + { 0x3445, 0x0000 }, + { 0x3446, 0x0000 }, + { 0x3447, 0x0000 }, + { 0x3448, 0x0000 }, + { 0x3449, 0x0000 }, + { 0x344a, 0x0000 }, + { 0x344b, 0x0000 }, + { 0x344c, 0x0000 }, + { 0x344d, 0x0000 }, + { 0x344e, 0x0000 }, + { 0x344f, 0x0000 }, + { 0x3450, 0x0000 }, + { 0x3451, 0x0000 }, + { 0x3452, 0x0000 }, + { 0x3453, 0x0000 }, + { 0x3454, 0x0000 }, + { 0x3455, 0x0000 }, + { 0x3456, 0x0000 }, + { 0x3457, 0x0000 }, + { 0x3458, 0x0000 }, + { 0x3459, 0x0000 }, + { 0x345a, 0x0000 }, + { 0x345b, 0x0000 }, + { 0x345c, 0x0000 }, + { 0x345d, 0x0000 }, + { 0x345e, 0x0000 }, + { 0x345f, 0x0000 }, + { 0x3460, 0x0000 }, + { 0x3461, 0x0000 }, + { 0x3462, 0x0000 }, + { 0x3463, 0x0000 }, + { 0x3464, 0x0000 }, + { 0x3465, 0x0000 }, + { 0x3466, 0x0000 }, + { 0x3467, 0x0000 }, + { 0x3468, 0x0000 }, + { 0x3469, 0x0000 }, + { 0x346a, 0x0000 }, + { 0x346b, 0x0000 }, + { 0x346c, 0x0000 }, + { 0x346d, 0x0000 }, + { 0x346e, 0x0000 }, + { 0x346f, 0x0000 }, + { 0x3470, 0x0000 }, + { 0x3471, 0x0000 }, + { 0x3472, 0x0000 }, + { 0x3473, 0x0000 }, + { 0x3474, 0x0000 }, + { 0x3475, 0x0000 }, + { 0x3476, 0x0000 }, + { 0x3477, 0x0000 }, + { 0x3478, 0x0000 }, + { 0x3479, 0x0000 }, + { 0x347a, 0x0000 }, + { 0x347b, 0x0000 }, + { 0x347c, 0x0000 }, + { 0x347d, 0x0000 }, + { 0x347e, 0x0000 }, + { 0x347f, 0x0000 }, + { 0x3480, 0x0000 }, + { 0x3481, 0x0000 }, + { 0x3482, 0x0000 }, + { 0x3483, 0x0000 }, + { 0x3484, 0x0000 }, + { 0x3485, 0x0000 }, + { 0x3486, 0x0000 }, + { 0x3487, 0x0000 }, + { 0x3488, 0x0000 }, + { 0x3489, 0x0000 }, + { 0x348a, 0x0000 }, + { 0x348b, 0x0000 }, + { 0x348c, 0x0000 }, + { 0x348d, 0x0000 }, + { 0x348e, 0x0000 }, + { 0x348f, 0x0000 }, + { 0x3490, 0x0000 }, + { 0x3491, 0x0000 }, + { 0x3492, 0x0000 }, + { 0x3493, 0x0000 }, + { 0x3494, 0x0000 }, + { 0x3495, 0x0000 }, + { 0x3496, 0x0000 }, + { 0x3497, 0x0000 }, + { 0x3498, 0x0000 }, + { 0x3499, 0x0000 }, + { 0x349a, 0x0000 }, + { 0x349b, 0x0000 }, + { 0x349c, 0x0000 }, + { 0x349d, 0x0000 }, + { 0x349e, 0x0000 }, + { 0x349f, 0x0000 }, + { 0x34a0, 0x0000 }, + { 0x34a1, 0x0000 }, + { 0x34a2, 0x0000 }, + { 0x34a3, 0x0000 }, + { 0x34a4, 0x0000 }, + { 0x34a5, 0x0000 }, + { 0x34a6, 0x0000 }, + { 0x34a7, 0x0000 }, + { 0x34a8, 0x0000 }, + { 0x34a9, 0x0000 }, + { 0x34aa, 0x0000 }, + { 0x34ab, 0x0000 }, + { 0x34ac, 0x0000 }, + { 0x34ad, 0x0000 }, + { 0x34ae, 0x0000 }, + { 0x34af, 0x0000 }, + { 0x34b0, 0x0000 }, + { 0x34b1, 0x0000 }, + { 0x34b2, 0x0000 }, + { 0x34b3, 0x0000 }, + { 0x34b4, 0x0000 }, + { 0x34b5, 0x0000 }, + { 0x34b6, 0x0000 }, + { 0x34b7, 0x0000 }, + { 0x34b8, 0x0000 }, + { 0x34b9, 0x0000 }, + { 0x34ba, 0x0000 }, + { 0x34bb, 0x0000 }, + { 0x34bc, 0x0000 }, + { 0x34bd, 0x0000 }, + { 0x34be, 0x0000 }, + { 0x34bf, 0x0000 }, + { 0x34c0, 0x0000 }, + { 0x34c1, 0x0000 }, + { 0x34c2, 0x0000 }, + { 0x34c3, 0x0000 }, + { 0x34c4, 0x0000 }, + { 0x34c5, 0x0000 }, + { 0x34c6, 0x0000 }, + { 0x34c7, 0x0000 }, + { 0x34c8, 0x0000 }, + { 0x34c9, 0x0000 }, + { 0x34ca, 0x0000 }, + { 0x34cb, 0x0000 }, + { 0x34cc, 0x0000 }, + { 0x34cd, 0x0000 }, + { 0x34ce, 0x0000 }, + { 0x34cf, 0x0000 }, + { 0x34d0, 0x0000 }, + { 0x34d1, 0x0000 }, + { 0x34d2, 0x0000 }, + { 0x34d3, 0x0000 }, + { 0x34d4, 0x0000 }, + { 0x34d5, 0x0000 }, + { 0x34d6, 0x0000 }, + { 0x34d7, 0x0000 }, + { 0x34d8, 0x0000 }, + { 0x34d9, 0x0000 }, + { 0x34da, 0x0000 }, + { 0x34db, 0x0000 }, + { 0x34dc, 0x0000 }, + { 0x34dd, 0x0000 }, + { 0x34de, 0x0000 }, + { 0x34df, 0x0000 }, + { 0x34e0, 0x0000 }, + { 0x34e1, 0x0000 }, + { 0x34e2, 0x0000 }, + { 0x34e3, 0x0000 }, + { 0x34e4, 0x0000 }, + { 0x34e5, 0x0000 }, + { 0x34e6, 0x0000 }, + { 0x34e7, 0x0000 }, + { 0x34e8, 0x0000 }, + { 0x34e9, 0x0000 }, + { 0x34ea, 0x0000 }, + { 0x34eb, 0x0000 }, + { 0x34ec, 0x0000 }, + { 0x34ed, 0x0000 }, + { 0x34ee, 0x0000 }, + { 0x34ef, 0x0000 }, + { 0x34f0, 0x0000 }, + { 0x34f1, 0x0000 }, + { 0x34f2, 0x0000 }, + { 0x34f3, 0x0000 }, + { 0x34f4, 0x0000 }, + { 0x34f5, 0x0000 }, + { 0x34f6, 0x0000 }, + { 0x34f7, 0x0000 }, + { 0x34f8, 0x0000 }, + { 0x34f9, 0x0000 }, + { 0x34fa, 0x0000 }, + { 0x34fb, 0x0000 }, + { 0x34fc, 0x0000 }, + { 0x34fd, 0x0000 }, + { 0x34fe, 0x0000 }, + { 0x34ff, 0x0000 }, + { 0x3500, 0x0000 }, + { 0x3501, 0x0000 }, + { 0x3502, 0x0000 }, + { 0x3503, 0x0000 }, + { 0x3504, 0x0000 }, + { 0x3505, 0x0000 }, + { 0x3506, 0x0000 }, + { 0x3507, 0x0000 }, + { 0x3508, 0x0000 }, + { 0x3509, 0x0000 }, + { 0x350a, 0x0000 }, + { 0x350b, 0x0000 }, + { 0x350c, 0x0000 }, + { 0x350d, 0x0000 }, + { 0x350e, 0x0000 }, + { 0x350f, 0x0000 }, + { 0x3510, 0x0000 }, + { 0x3511, 0x0000 }, + { 0x3512, 0x0000 }, + { 0x3513, 0x0000 }, + { 0x3514, 0x0000 }, + { 0x3515, 0x0000 }, + { 0x3516, 0x0000 }, + { 0x3517, 0x0000 }, + { 0x3518, 0x0000 }, + { 0x3519, 0x0000 }, + { 0x351a, 0x0000 }, + { 0x351b, 0x0000 }, + { 0x351c, 0x0000 }, + { 0x351d, 0x0000 }, + { 0x351e, 0x0000 }, + { 0x351f, 0x0000 }, + { 0x3520, 0x0000 }, + { 0x3521, 0x0000 }, + { 0x3522, 0x0000 }, + { 0x3523, 0x0000 }, + { 0x3524, 0x0000 }, + { 0x3525, 0x0000 }, + { 0x3526, 0x0000 }, + { 0x3527, 0x0000 }, + { 0x3528, 0x0000 }, + { 0x3529, 0x0000 }, + { 0x352a, 0x0000 }, + { 0x352b, 0x0000 }, + { 0x352c, 0x0000 }, + { 0x352d, 0x0000 }, + { 0x352e, 0x0000 }, + { 0x352f, 0x0000 }, + { 0x3530, 0x0000 }, + { 0x3531, 0x0000 }, + { 0x3532, 0x0000 }, + { 0x3533, 0x0000 }, + { 0x3534, 0x0000 }, + { 0x3535, 0x0000 }, + { 0x3536, 0x0000 }, + { 0x3537, 0x0000 }, + { 0x3538, 0x0000 }, + { 0x3539, 0x0000 }, + { 0x353a, 0x0000 }, + { 0x353b, 0x0000 }, + { 0x353c, 0x0000 }, + { 0x353d, 0x0000 }, + { 0x353e, 0x0000 }, + { 0x353f, 0x0000 }, + { 0x3540, 0x0000 }, + { 0x3541, 0x0000 }, + { 0x3542, 0x0000 }, + { 0x3543, 0x0000 }, + { 0x3544, 0x0000 }, + { 0x3545, 0x0000 }, + { 0x3546, 0x0000 }, + { 0x3547, 0x0000 }, + { 0x3548, 0x0000 }, + { 0x3549, 0x0000 }, + { 0x354a, 0x0000 }, + { 0x354b, 0x0000 }, + { 0x354c, 0x0000 }, + { 0x354d, 0x0000 }, + { 0x354e, 0x0000 }, + { 0x354f, 0x0000 }, + { 0x3550, 0x0000 }, + { 0x3551, 0x0000 }, + { 0x3552, 0x0000 }, + { 0x3553, 0x0000 }, + { 0x3554, 0x0000 }, + { 0x3555, 0x0000 }, + { 0x3556, 0x0000 }, + { 0x3557, 0x0000 }, + { 0x3558, 0x0000 }, + { 0x3559, 0x0000 }, + { 0x355a, 0x0000 }, + { 0x355b, 0x0000 }, + { 0x355c, 0x0000 }, + { 0x355d, 0x0000 }, + { 0x355e, 0x0000 }, + { 0x355f, 0x0000 }, + { 0x3560, 0x0000 }, + { 0x3561, 0x0000 }, + { 0x3562, 0x0000 }, + { 0x3563, 0x0000 }, + { 0x3564, 0x0000 }, + { 0x3565, 0x0000 }, + { 0x3566, 0x0000 }, + { 0x3567, 0x0000 }, + { 0x3568, 0x0000 }, + { 0x3569, 0x0000 }, + { 0x356a, 0x0000 }, + { 0x356b, 0x0000 }, + { 0x356c, 0x0000 }, + { 0x356d, 0x0000 }, + { 0x356e, 0x0000 }, + { 0x356f, 0x0000 }, + { 0x3570, 0x0000 }, + { 0x3571, 0x0000 }, + { 0x3572, 0x0000 }, + { 0x3573, 0x0000 }, + { 0x3574, 0x0000 }, + { 0x3575, 0x0000 }, + { 0x3576, 0x0000 }, + { 0x3577, 0x0000 }, + { 0x3578, 0x0000 }, + { 0x3579, 0x0000 }, + { 0x357a, 0x0000 }, + { 0x357b, 0x0000 }, + { 0x357c, 0x0000 }, + { 0x357d, 0x0000 }, + { 0x357e, 0x0000 }, + { 0x357f, 0x0000 }, + { 0x3580, 0x0000 }, + { 0x3581, 0x0000 }, + { 0x3582, 0x0000 }, + { 0x3583, 0x0000 }, + { 0x3584, 0x0000 }, + { 0x3585, 0x0000 }, + { 0x3586, 0x0000 }, + { 0x3587, 0x0000 }, + { 0x3588, 0x0000 }, + { 0x3589, 0x0000 }, + { 0x358a, 0x0000 }, + { 0x358b, 0x0000 }, + { 0x358c, 0x0000 }, + { 0x358d, 0x0000 }, + { 0x358e, 0x0000 }, + { 0x358f, 0x0000 }, + { 0x3590, 0x0000 }, + { 0x3591, 0x0000 }, + { 0x3592, 0x0000 }, + { 0x3593, 0x0000 }, + { 0x3594, 0x0000 }, + { 0x3595, 0x0000 }, + { 0x3596, 0x0000 }, + { 0x3597, 0x0000 }, + { 0x3598, 0x0000 }, + { 0x3599, 0x0000 }, + { 0x359a, 0x0000 }, + { 0x359b, 0x0000 }, + { 0x359c, 0x0000 }, + { 0x359d, 0x0000 }, + { 0x359e, 0x0000 }, + { 0x359f, 0x0000 }, + { 0x35a0, 0x0000 }, + { 0x35a1, 0x0000 }, + { 0x35a2, 0x0000 }, + { 0x35a3, 0x0000 }, + { 0x35a4, 0x0000 }, + { 0x35a5, 0x0000 }, + { 0x35a6, 0x0000 }, + { 0x35a7, 0x0000 }, + { 0x35a8, 0x0000 }, + { 0x35a9, 0x0000 }, + { 0x35aa, 0x0000 }, + { 0x35ab, 0x0000 }, + { 0x35ac, 0x0000 }, + { 0x35ad, 0x0000 }, + { 0x35ae, 0x0000 }, + { 0x35af, 0x0000 }, + { 0x35b0, 0x0000 }, + { 0x35b1, 0x0000 }, + { 0x35b2, 0x0000 }, + { 0x35b3, 0x0000 }, + { 0x35b4, 0x0000 }, + { 0x35b5, 0x0000 }, + { 0x35b6, 0x0000 }, + { 0x35b7, 0x0000 }, + { 0x35b8, 0x0000 }, + { 0x35b9, 0x0000 }, + { 0x35ba, 0x0000 }, + { 0x35bb, 0x0000 }, + { 0x35bc, 0x0000 }, + { 0x35bd, 0x0000 }, + { 0x35be, 0x0000 }, + { 0x35bf, 0x0000 }, + { 0x35c0, 0x0000 }, + { 0x35c1, 0x0000 }, + { 0x35c2, 0x0000 }, + { 0x35c3, 0x0000 }, + { 0x35c4, 0x0000 }, + { 0x35c5, 0x0000 }, + { 0x35c6, 0x0000 }, + { 0x35c7, 0x0000 }, + { 0x35c8, 0x0000 }, + { 0x35c9, 0x0000 }, + { 0x35ca, 0x0000 }, + { 0x35cb, 0x0000 }, + { 0x35cc, 0x0000 }, + { 0x35cd, 0x0000 }, + { 0x35ce, 0x0000 }, + { 0x35cf, 0x0000 }, + { 0x35d0, 0x0000 }, + { 0x35d1, 0x0000 }, + { 0x35d2, 0x0000 }, + { 0x35d3, 0x0000 }, + { 0x35d4, 0x0000 }, + { 0x35d5, 0x0000 }, + { 0x35d6, 0x0000 }, + { 0x35d7, 0x0000 }, + { 0x35d8, 0x0000 }, + { 0x35d9, 0x0000 }, + { 0x35da, 0x0000 }, + { 0x35db, 0x0000 }, + { 0x35dc, 0x0000 }, + { 0x35dd, 0x0000 }, + { 0x35de, 0x0000 }, + { 0x35df, 0x0000 }, + { 0x35e0, 0x0000 }, + { 0x35e1, 0x0000 }, + { 0x35e2, 0x0000 }, + { 0x35e3, 0x0000 }, + { 0x35e4, 0x0000 }, + { 0x35e5, 0x0000 }, + { 0x35e6, 0x0000 }, + { 0x35e7, 0x0000 }, + { 0x35e8, 0x0000 }, + { 0x35e9, 0x0000 }, + { 0x35ea, 0x0000 }, + { 0x35eb, 0x0000 }, + { 0x35ec, 0x0000 }, + { 0x35ed, 0x0000 }, + { 0x35ee, 0x0000 }, + { 0x35ef, 0x0000 }, + { 0x35f0, 0x0000 }, + { 0x35f1, 0x0000 }, + { 0x35f2, 0x0000 }, + { 0x35f3, 0x0000 }, + { 0x35f4, 0x0000 }, + { 0x35f5, 0x0000 }, + { 0x35f6, 0x0000 }, + { 0x35f7, 0x0000 }, + { 0x35f8, 0x0000 }, + { 0x35f9, 0x0000 }, + { 0x35fa, 0x0000 }, + { 0x35fb, 0x0000 }, + { 0x35fc, 0x0000 }, + { 0x35fd, 0x0000 }, + { 0x35fe, 0x0000 }, + { 0x35ff, 0x0000 }, + { 0x3600, 0x0000 }, + { 0x3601, 0x0000 }, + { 0x3602, 0x0000 }, + { 0x3603, 0x0000 }, + { 0x3604, 0x0000 }, + { 0x3605, 0x0000 }, + { 0x3606, 0x0000 }, + { 0x3607, 0x0000 }, + { 0x3608, 0x0000 }, + { 0x3609, 0x0000 }, + { 0x360a, 0x0000 }, + { 0x360b, 0x0000 }, + { 0x360c, 0x0000 }, + { 0x360d, 0x0000 }, + { 0x360e, 0x0000 }, + { 0x360f, 0x0000 }, + { 0x3610, 0x0000 }, + { 0x3611, 0x0000 }, + { 0x3612, 0x0000 }, + { 0x3613, 0x0000 }, + { 0x3614, 0x0000 }, + { 0x3615, 0x0000 }, + { 0x3616, 0x0000 }, + { 0x3617, 0x0000 }, + { 0x3618, 0x0000 }, + { 0x3619, 0x0000 }, + { 0x361a, 0x0000 }, + { 0x361b, 0x0000 }, + { 0x361c, 0x0000 }, + { 0x361d, 0x0000 }, + { 0x361e, 0x0000 }, + { 0x361f, 0x0000 }, + { 0x3620, 0x0000 }, + { 0x3621, 0x0000 }, + { 0x3622, 0x0000 }, + { 0x3623, 0x0000 }, + { 0x3624, 0x0000 }, + { 0x3625, 0x0000 }, + { 0x3626, 0x0000 }, + { 0x3627, 0x0000 }, + { 0x3628, 0x0000 }, + { 0x3629, 0x0000 }, + { 0x362a, 0x0000 }, + { 0x362b, 0x0000 }, + { 0x362c, 0x0000 }, + { 0x362d, 0x0000 }, + { 0x362e, 0x0000 }, + { 0x362f, 0x0000 }, + { 0x3630, 0x0000 }, + { 0x3631, 0x0000 }, + { 0x3632, 0x0000 }, + { 0x3633, 0x0000 }, + { 0x3634, 0x0000 }, + { 0x3635, 0x0000 }, + { 0x3636, 0x0000 }, + { 0x3637, 0x0000 }, + { 0x3638, 0x0000 }, + { 0x3639, 0x0000 }, + { 0x363a, 0x0000 }, + { 0x363b, 0x0000 }, + { 0x363c, 0x0000 }, + { 0x363d, 0x0000 }, + { 0x363e, 0x0000 }, + { 0x363f, 0x0000 }, + { 0x3640, 0x0000 }, + { 0x3641, 0x0000 }, + { 0x3642, 0x0000 }, + { 0x3643, 0x0000 }, + { 0x3644, 0x0000 }, + { 0x3645, 0x0000 }, + { 0x3646, 0x0000 }, + { 0x3647, 0x0000 }, + { 0x3648, 0x0000 }, + { 0x3649, 0x0000 }, + { 0x364a, 0x0000 }, + { 0x364b, 0x0000 }, + { 0x364c, 0x0000 }, + { 0x364d, 0x0000 }, + { 0x364e, 0x0000 }, + { 0x364f, 0x0000 }, + { 0x3650, 0x0000 }, + { 0x3651, 0x0000 }, + { 0x3652, 0x0000 }, + { 0x3653, 0x0000 }, + { 0x3654, 0x0000 }, + { 0x3655, 0x0000 }, + { 0x3656, 0x0000 }, + { 0x3657, 0x0000 }, + { 0x3658, 0x0000 }, + { 0x3659, 0x0000 }, + { 0x365a, 0x0000 }, + { 0x365b, 0x0000 }, + { 0x365c, 0x0000 }, + { 0x365d, 0x0000 }, + { 0x365e, 0x0000 }, + { 0x365f, 0x0000 }, + { 0x3660, 0x0000 }, + { 0x3661, 0x0000 }, + { 0x3662, 0x0000 }, + { 0x3663, 0x0000 }, + { 0x3664, 0x0000 }, + { 0x3665, 0x0000 }, + { 0x3666, 0x0000 }, + { 0x3667, 0x0000 }, + { 0x3668, 0x0000 }, + { 0x3669, 0x0000 }, + { 0x366a, 0x0000 }, + { 0x366b, 0x0000 }, + { 0x366c, 0x0000 }, + { 0x366d, 0x0000 }, + { 0x366e, 0x0000 }, + { 0x366f, 0x0000 }, + { 0x3670, 0x0000 }, + { 0x3671, 0x0000 }, + { 0x3672, 0x0000 }, + { 0x3673, 0x0000 }, + { 0x3674, 0x0000 }, + { 0x3675, 0x0000 }, + { 0x3676, 0x0000 }, + { 0x3677, 0x0000 }, + { 0x3678, 0x0000 }, + { 0x3679, 0x0000 }, + { 0x367a, 0x0000 }, + { 0x367b, 0x0000 }, + { 0x367c, 0x0000 }, + { 0x367d, 0x0000 }, + { 0x367e, 0x0000 }, + { 0x367f, 0x0000 }, + { 0x3680, 0x0000 }, + { 0x3681, 0x0000 }, + { 0x3682, 0x0000 }, + { 0x3683, 0x0000 }, + { 0x3684, 0x0000 }, + { 0x3685, 0x0000 }, + { 0x3686, 0x0000 }, + { 0x3687, 0x0000 }, + { 0x3688, 0x0000 }, + { 0x3689, 0x0000 }, + { 0x368a, 0x0000 }, + { 0x368b, 0x0000 }, + { 0x368c, 0x0000 }, + { 0x368d, 0x0000 }, + { 0x368e, 0x0000 }, + { 0x368f, 0x0000 }, + { 0x3690, 0x0000 }, + { 0x3691, 0x0000 }, + { 0x3692, 0x0000 }, + { 0x3693, 0x0000 }, + { 0x3694, 0x0000 }, + { 0x3695, 0x0000 }, + { 0x3696, 0x0000 }, + { 0x3697, 0x0000 }, + { 0x3698, 0x0000 }, + { 0x3699, 0x0000 }, + { 0x369a, 0x0000 }, + { 0x369b, 0x0000 }, + { 0x369c, 0x0000 }, + { 0x369d, 0x0000 }, + { 0x369e, 0x0000 }, + { 0x369f, 0x0000 }, + { 0x36a0, 0x0000 }, + { 0x36a1, 0x0000 }, + { 0x36a2, 0x0000 }, + { 0x36a3, 0x0000 }, + { 0x36a4, 0x0000 }, + { 0x36a5, 0x0000 }, + { 0x36a6, 0x0000 }, + { 0x36a7, 0x0000 }, + { 0x36a8, 0x0000 }, + { 0x36a9, 0x0000 }, + { 0x36aa, 0x0000 }, + { 0x36ab, 0x0000 }, + { 0x36ac, 0x0000 }, + { 0x36ad, 0x0000 }, + { 0x36ae, 0x0000 }, + { 0x36af, 0x0000 }, + { 0x36b0, 0x0000 }, + { 0x36b1, 0x0000 }, + { 0x36b2, 0x0000 }, + { 0x36b3, 0x0000 }, + { 0x36b4, 0x0000 }, + { 0x36b5, 0x0000 }, + { 0x36b6, 0x0000 }, + { 0x36b7, 0x0000 }, + { 0x36b8, 0x0000 }, + { 0x36b9, 0x0000 }, + { 0x36ba, 0x0000 }, + { 0x36bb, 0x0000 }, + { 0x36bc, 0x0000 }, + { 0x36bd, 0x0000 }, + { 0x36be, 0x0000 }, + { 0x36bf, 0x0000 }, + { 0x36c0, 0x0000 }, + { 0x36c1, 0x0000 }, + { 0x36c2, 0x0000 }, + { 0x36c3, 0x0000 }, + { 0x36c4, 0x0000 }, + { 0x36c5, 0x0000 }, + { 0x36c6, 0x0000 }, + { 0x36c7, 0x0000 }, + { 0x36c8, 0x0000 }, + { 0x36c9, 0x0000 }, + { 0x36ca, 0x0000 }, + { 0x36cb, 0x0000 }, + { 0x36cc, 0x0000 }, + { 0x36cd, 0x0000 }, + { 0x36ce, 0x0000 }, + { 0x36cf, 0x0000 }, + { 0x36d0, 0x0000 }, + { 0x36d1, 0x0000 }, + { 0x36d2, 0x0000 }, + { 0x36d3, 0x0000 }, + { 0x36d4, 0x0000 }, + { 0x36d5, 0x0000 }, + { 0x36d6, 0x0000 }, + { 0x36d7, 0x0000 }, + { 0x36d8, 0x0000 }, + { 0x36d9, 0x0000 }, + { 0x36da, 0x0000 }, + { 0x36db, 0x0000 }, + { 0x36dc, 0x0000 }, + { 0x36dd, 0x0000 }, + { 0x36de, 0x0000 }, + { 0x36df, 0x0000 }, + { 0x36e0, 0x0000 }, + { 0x36e1, 0x0000 }, + { 0x36e2, 0x0000 }, + { 0x36e3, 0x0000 }, + { 0x36e4, 0x0000 }, + { 0x36e5, 0x0000 }, + { 0x36e6, 0x0000 }, + { 0x36e7, 0x0000 }, + { 0x36e8, 0x0000 }, + { 0x36e9, 0x0000 }, + { 0x36ea, 0x0000 }, + { 0x36eb, 0x0000 }, + { 0x36ec, 0x0000 }, + { 0x36ed, 0x0000 }, + { 0x36ee, 0x0000 }, + { 0x36ef, 0x0000 }, + { 0x36f0, 0x0000 }, + { 0x36f1, 0x0000 }, + { 0x36f2, 0x0000 }, + { 0x36f3, 0x0000 }, + { 0x36f4, 0x0000 }, + { 0x36f5, 0x0000 }, + { 0x36f6, 0x0000 }, + { 0x36f7, 0x0000 }, + { 0x36f8, 0x0000 }, + { 0x36f9, 0x0000 }, + { 0x36fa, 0x0000 }, + { 0x36fb, 0x0000 }, + { 0x36fc, 0x0000 }, + { 0x36fd, 0x0000 }, + { 0x36fe, 0x0000 }, + { 0x36ff, 0x0000 }, + { 0x3700, 0x0000 }, + { 0x3701, 0x0000 }, + { 0x3702, 0x0000 }, + { 0x3703, 0x0000 }, + { 0x3704, 0x0000 }, + { 0x3705, 0x0000 }, + { 0x3706, 0x0000 }, + { 0x3707, 0x0000 }, + { 0x3708, 0x0000 }, + { 0x3709, 0x0000 }, + { 0x370a, 0x0000 }, + { 0x370b, 0x0000 }, + { 0x370c, 0x0000 }, + { 0x370d, 0x0000 }, + { 0x370e, 0x0000 }, + { 0x370f, 0x0000 }, + { 0x3710, 0x0000 }, + { 0x3711, 0x0000 }, + { 0x3712, 0x0000 }, + { 0x3713, 0x0000 }, + { 0x3714, 0x0000 }, + { 0x3715, 0x0000 }, + { 0x3716, 0x0000 }, + { 0x3717, 0x0000 }, + { 0x3718, 0x0000 }, + { 0x3719, 0x0000 }, + { 0x371a, 0x0000 }, + { 0x371b, 0x0000 }, + { 0x371c, 0x0000 }, + { 0x371d, 0x0000 }, + { 0x371e, 0x0000 }, + { 0x371f, 0x0000 }, + { 0x3720, 0x0000 }, + { 0x3721, 0x0000 }, + { 0x3722, 0x0000 }, + { 0x3723, 0x0000 }, + { 0x3724, 0x0000 }, + { 0x3725, 0x0000 }, + { 0x3726, 0x0000 }, + { 0x3727, 0x0000 }, + { 0x3728, 0x0000 }, + { 0x3729, 0x0000 }, + { 0x372a, 0x0000 }, + { 0x372b, 0x0000 }, + { 0x372c, 0x0000 }, + { 0x372d, 0x0000 }, + { 0x372e, 0x0000 }, + { 0x372f, 0x0000 }, + { 0x3730, 0x0000 }, + { 0x3731, 0x0000 }, + { 0x3732, 0x0000 }, + { 0x3733, 0x0000 }, + { 0x3734, 0x0000 }, + { 0x3735, 0x0000 }, + { 0x3736, 0x0000 }, + { 0x3737, 0x0000 }, + { 0x3738, 0x0000 }, + { 0x3739, 0x0000 }, + { 0x373a, 0x0000 }, + { 0x373b, 0x0000 }, + { 0x373c, 0x0000 }, + { 0x373d, 0x0000 }, + { 0x373e, 0x0000 }, + { 0x373f, 0x0000 }, + { 0x3740, 0x0000 }, + { 0x3741, 0x0000 }, + { 0x3742, 0x0000 }, + { 0x3743, 0x0000 }, + { 0x3744, 0x0000 }, + { 0x3745, 0x0000 }, + { 0x3746, 0x0000 }, + { 0x3747, 0x0000 }, + { 0x3748, 0x0000 }, + { 0x3749, 0x0000 }, + { 0x374a, 0x0000 }, + { 0x374b, 0x0000 }, + { 0x374c, 0x0000 }, + { 0x374d, 0x0000 }, + { 0x374e, 0x0000 }, + { 0x374f, 0x0000 }, + { 0x3750, 0x0000 }, + { 0x3751, 0x0000 }, + { 0x3752, 0x0000 }, + { 0x3753, 0x0000 }, + { 0x3754, 0x0000 }, + { 0x3755, 0x0000 }, + { 0x3756, 0x0000 }, + { 0x3757, 0x0000 }, + { 0x3758, 0x0000 }, + { 0x3759, 0x0000 }, + { 0x375a, 0x0000 }, + { 0x375b, 0x0000 }, + { 0x375c, 0x0000 }, + { 0x375d, 0x0000 }, + { 0x375e, 0x0000 }, + { 0x375f, 0x0000 }, + { 0x3760, 0x0000 }, + { 0x3761, 0x0000 }, + { 0x3762, 0x0000 }, + { 0x3763, 0x0000 }, + { 0x3764, 0x0000 }, + { 0x3765, 0x0000 }, + { 0x3766, 0x0000 }, + { 0x3767, 0x0000 }, + { 0x3768, 0x0000 }, + { 0x3769, 0x0000 }, + { 0x376a, 0x0000 }, + { 0x376b, 0x0000 }, + { 0x376c, 0x0000 }, + { 0x376d, 0x0000 }, + { 0x376e, 0x0000 }, + { 0x376f, 0x0000 }, + { 0x3770, 0x0000 }, + { 0x3771, 0x0000 }, + { 0x3772, 0x0000 }, + { 0x3773, 0x0000 }, + { 0x3774, 0x0000 }, + { 0x3775, 0x0000 }, + { 0x3776, 0x0000 }, + { 0x3777, 0x0000 }, + { 0x3778, 0x0000 }, + { 0x3779, 0x0000 }, + { 0x377a, 0x0000 }, + { 0x377b, 0x0000 }, + { 0x377c, 0x0000 }, + { 0x377d, 0x0000 }, + { 0x377e, 0x0000 }, + { 0x377f, 0x0000 }, + { 0x3780, 0x0000 }, + { 0x3781, 0x0000 }, + { 0x3782, 0x0000 }, + { 0x3783, 0x0000 }, + { 0x3784, 0x0000 }, + { 0x3785, 0x0000 }, + { 0x3786, 0x0000 }, + { 0x3787, 0x0000 }, + { 0x3788, 0x0000 }, + { 0x3789, 0x0000 }, + { 0x378a, 0x0000 }, + { 0x378b, 0x0000 }, + { 0x378c, 0x0000 }, + { 0x378d, 0x0000 }, + { 0x378e, 0x0000 }, + { 0x378f, 0x0000 }, + { 0x3790, 0x0000 }, + { 0x3791, 0x0000 }, + { 0x3792, 0x0000 }, + { 0x3793, 0x0000 }, + { 0x3794, 0x0000 }, + { 0x3795, 0x0000 }, + { 0x3796, 0x0000 }, + { 0x3797, 0x0000 }, + { 0x3798, 0x0000 }, + { 0x3799, 0x0000 }, + { 0x379a, 0x0000 }, + { 0x379b, 0x0000 }, + { 0x379c, 0x0000 }, + { 0x379d, 0x0000 }, + { 0x379e, 0x0000 }, + { 0x379f, 0x0000 }, + { 0x37a0, 0x0000 }, + { 0x37a1, 0x0000 }, + { 0x37a2, 0x0000 }, + { 0x37a3, 0x0000 }, + { 0x37a4, 0x0000 }, + { 0x37a5, 0x0000 }, + { 0x37a6, 0x0000 }, + { 0x37a7, 0x0000 }, + { 0x37a8, 0x0000 }, + { 0x37a9, 0x0000 }, + { 0x37aa, 0x0000 }, + { 0x37ab, 0x0000 }, + { 0x37ac, 0x0000 }, + { 0x37ad, 0x0000 }, + { 0x37ae, 0x0000 }, + { 0x37af, 0x0000 }, + { 0x37b0, 0x0000 }, + { 0x37b1, 0x0000 }, + { 0x37b2, 0x0000 }, + { 0x37b3, 0x0000 }, + { 0x37b4, 0x0000 }, + { 0x37b5, 0x0000 }, + { 0x37b6, 0x0000 }, + { 0x37b7, 0x0000 }, + { 0x37b8, 0x0000 }, + { 0x37b9, 0x0000 }, + { 0x37ba, 0x0000 }, + { 0x37bb, 0x0000 }, + { 0x37bc, 0x0000 }, + { 0x37bd, 0x0000 }, + { 0x37be, 0x0000 }, + { 0x37bf, 0x0000 }, + { 0x37c0, 0x0000 }, + { 0x37c1, 0x0000 }, + { 0x37c2, 0x0000 }, + { 0x37c3, 0x0000 }, + { 0x37c4, 0x0000 }, + { 0x37c5, 0x0000 }, + { 0x37c6, 0x0000 }, + { 0x37c7, 0x0000 }, + { 0x37c8, 0x0000 }, + { 0x37c9, 0x0000 }, + { 0x37ca, 0x0000 }, + { 0x37cb, 0x0000 }, + { 0x37cc, 0x0000 }, + { 0x37cd, 0x0000 }, + { 0x37ce, 0x0000 }, + { 0x37cf, 0x0000 }, + { 0x37d0, 0x0000 }, + { 0x37d1, 0x0000 }, + { 0x37d2, 0x0000 }, + { 0x37d3, 0x0000 }, + { 0x37d4, 0x0000 }, + { 0x37d5, 0x0000 }, + { 0x37d6, 0x0000 }, + { 0x37d7, 0x0000 }, + { 0x37d8, 0x0000 }, + { 0x37d9, 0x0000 }, + { 0x37da, 0x0000 }, + { 0x37db, 0x0000 }, + { 0x37dc, 0x0000 }, + { 0x37dd, 0x0000 }, + { 0x37de, 0x0000 }, + { 0x37df, 0x0000 }, + { 0x37e0, 0x0000 }, + { 0x37e1, 0x0000 }, + { 0x37e2, 0x0000 }, + { 0x37e3, 0x0000 }, + { 0x37e4, 0x0000 }, + { 0x37e5, 0x0000 }, + { 0x37e6, 0x0000 }, + { 0x37e7, 0x0000 }, + { 0x37e8, 0x0000 }, + { 0x37e9, 0x0000 }, + { 0x37ea, 0x0000 }, + { 0x37eb, 0x0000 }, + { 0x37ec, 0x0000 }, + { 0x37ed, 0x0000 }, + { 0x37ee, 0x0000 }, + { 0x37ef, 0x0000 }, + { 0x37f0, 0x0000 }, + { 0x37f1, 0x0000 }, + { 0x37f2, 0x0000 }, + { 0x37f3, 0x0000 }, + { 0x37f4, 0x0000 }, + { 0x37f5, 0x0000 }, + { 0x37f6, 0x0000 }, + { 0x37f7, 0x0000 }, + { 0x37f8, 0x0000 }, + { 0x37f9, 0x0000 }, + { 0x37fa, 0x0000 }, + { 0x37fb, 0x0000 }, + { 0x37fc, 0x0000 }, + { 0x37fd, 0x0000 }, + { 0x37fe, 0x0000 }, + { 0x37ff, 0x0000 }, + { 0x3800, 0x0000 }, + { 0x3801, 0x0000 }, + { 0x3802, 0x0000 }, + { 0x3803, 0x0000 }, + { 0x3804, 0x0000 }, + { 0x3805, 0x0000 }, + { 0x3806, 0x0000 }, + { 0x3807, 0x0000 }, + { 0x3808, 0x0000 }, + { 0x3809, 0x0000 }, + { 0x380a, 0x0000 }, + { 0x380b, 0x0000 }, + { 0x380c, 0x0000 }, + { 0x380d, 0x0000 }, + { 0x380e, 0x0000 }, + { 0x380f, 0x0000 }, + { 0x3810, 0x0000 }, + { 0x3811, 0x0000 }, + { 0x3812, 0x0000 }, + { 0x3813, 0x0000 }, + { 0x3814, 0x0000 }, + { 0x3815, 0x0000 }, + { 0x3816, 0x0000 }, + { 0x3817, 0x0000 }, + { 0x3818, 0x0000 }, + { 0x3819, 0x0000 }, + { 0x381a, 0x0000 }, + { 0x381b, 0x0000 }, + { 0x381c, 0x0000 }, + { 0x381d, 0x0000 }, + { 0x381e, 0x0000 }, + { 0x381f, 0x0000 }, + { 0x3820, 0x0000 }, + { 0x3821, 0x0000 }, + { 0x3822, 0x0000 }, + { 0x3823, 0x0000 }, + { 0x3824, 0x0000 }, + { 0x3825, 0x0000 }, + { 0x3826, 0x0000 }, + { 0x3827, 0x0000 }, + { 0x3828, 0x0000 }, + { 0x3829, 0x0000 }, + { 0x382a, 0x0000 }, + { 0x382b, 0x0000 }, + { 0x382c, 0x0000 }, + { 0x382d, 0x0000 }, + { 0x382e, 0x0000 }, + { 0x382f, 0x0000 }, + { 0x3830, 0x0000 }, + { 0x3831, 0x0000 }, + { 0x3832, 0x0000 }, + { 0x3833, 0x0000 }, + { 0x3834, 0x0000 }, + { 0x3835, 0x0000 }, + { 0x3836, 0x0000 }, + { 0x3837, 0x0000 }, + { 0x3838, 0x0000 }, + { 0x3839, 0x0000 }, + { 0x383a, 0x0000 }, + { 0x383b, 0x0000 }, + { 0x383c, 0x0000 }, + { 0x383d, 0x0000 }, + { 0x383e, 0x0000 }, + { 0x383f, 0x0000 }, + { 0x3840, 0x0000 }, + { 0x3841, 0x0000 }, + { 0x3842, 0x0000 }, + { 0x3843, 0x0000 }, + { 0x3844, 0x0000 }, + { 0x3845, 0x0000 }, + { 0x3846, 0x0000 }, + { 0x3847, 0x0000 }, + { 0x3848, 0x0000 }, + { 0x3849, 0x0000 }, + { 0x384a, 0x0000 }, + { 0x384b, 0x0000 }, + { 0x384c, 0x0000 }, + { 0x384d, 0x0000 }, + { 0x384e, 0x0000 }, + { 0x384f, 0x0000 }, + { 0x3850, 0x0000 }, + { 0x3851, 0x0000 }, + { 0x3852, 0x0000 }, + { 0x3853, 0x0000 }, + { 0x3854, 0x0000 }, + { 0x3855, 0x0000 }, + { 0x3856, 0x0000 }, + { 0x3857, 0x0000 }, + { 0x3858, 0x0000 }, + { 0x3859, 0x0000 }, + { 0x385a, 0x0000 }, + { 0x385b, 0x0000 }, + { 0x385c, 0x0000 }, + { 0x385d, 0x0000 }, + { 0x385e, 0x0000 }, + { 0x385f, 0x0000 }, + { 0x3860, 0x0000 }, + { 0x3861, 0x0000 }, + { 0x3862, 0x0000 }, + { 0x3863, 0x0000 }, + { 0x3864, 0x0000 }, + { 0x3865, 0x0000 }, + { 0x3866, 0x0000 }, + { 0x3867, 0x0000 }, + { 0x3868, 0x0000 }, + { 0x3869, 0x0000 }, + { 0x386a, 0x0000 }, + { 0x386b, 0x0000 }, + { 0x386c, 0x0000 }, + { 0x386d, 0x0000 }, + { 0x386e, 0x0000 }, + { 0x386f, 0x0000 }, + { 0x3870, 0x0000 }, + { 0x3871, 0x0000 }, + { 0x3872, 0x0000 }, + { 0x3873, 0x0000 }, + { 0x3874, 0x0000 }, + { 0x3875, 0x0000 }, + { 0x3876, 0x0000 }, + { 0x3877, 0x0000 }, + { 0x3878, 0x0000 }, + { 0x3879, 0x0000 }, + { 0x387a, 0x0000 }, + { 0x387b, 0x0000 }, + { 0x387c, 0x0000 }, + { 0x387d, 0x0000 }, + { 0x387e, 0x0000 }, + { 0x387f, 0x0000 }, + { 0x3880, 0x0000 }, + { 0x3881, 0x0000 }, + { 0x3882, 0x0000 }, + { 0x3883, 0x0000 }, + { 0x3884, 0x0000 }, + { 0x3885, 0x0000 }, + { 0x3886, 0x0000 }, + { 0x3887, 0x0000 }, + { 0x3888, 0x0000 }, + { 0x3889, 0x0000 }, + { 0x388a, 0x0000 }, + { 0x388b, 0x0000 }, + { 0x388c, 0x0000 }, + { 0x388d, 0x0000 }, + { 0x388e, 0x0000 }, + { 0x388f, 0x0000 }, + { 0x3890, 0x0000 }, + { 0x3891, 0x0000 }, + { 0x3892, 0x0000 }, + { 0x3893, 0x0000 }, + { 0x3894, 0x0000 }, + { 0x3895, 0x0000 }, + { 0x3896, 0x0000 }, + { 0x3897, 0x0000 }, + { 0x3898, 0x0000 }, + { 0x3899, 0x0000 }, + { 0x389a, 0x0000 }, + { 0x389b, 0x0000 }, + { 0x389c, 0x0000 }, + { 0x389d, 0x0000 }, + { 0x389e, 0x0000 }, + { 0x389f, 0x0000 }, + { 0x38a0, 0x0000 }, + { 0x38a1, 0x0000 }, + { 0x38a2, 0x0000 }, + { 0x38a3, 0x0000 }, + { 0x38a4, 0x0000 }, + { 0x38a5, 0x0000 }, + { 0x38a6, 0x0000 }, + { 0x38a7, 0x0000 }, + { 0x38a8, 0x0000 }, + { 0x38a9, 0x0000 }, + { 0x38aa, 0x0000 }, + { 0x38ab, 0x0000 }, + { 0x38ac, 0x0000 }, + { 0x38ad, 0x0000 }, + { 0x38ae, 0x0000 }, + { 0x38af, 0x0000 }, + { 0x38b0, 0x0000 }, + { 0x38b1, 0x0000 }, + { 0x38b2, 0x0000 }, + { 0x38b3, 0x0000 }, + { 0x38b4, 0x0000 }, + { 0x38b5, 0x0000 }, + { 0x38b6, 0x0000 }, + { 0x38b7, 0x0000 }, + { 0x38b8, 0x0000 }, + { 0x38b9, 0x0000 }, + { 0x38ba, 0x0000 }, + { 0x38bb, 0x0000 }, + { 0x38bc, 0x0000 }, + { 0x38bd, 0x0000 }, + { 0x38be, 0x0000 }, + { 0x38bf, 0x0000 }, + { 0x38c0, 0x0000 }, + { 0x38c1, 0x0000 }, + { 0x38c2, 0x0000 }, + { 0x38c3, 0x0000 }, + { 0x38c4, 0x0000 }, + { 0x38c5, 0x0000 }, + { 0x38c6, 0x0000 }, + { 0x38c7, 0x0000 }, + { 0x38c8, 0x0000 }, + { 0x38c9, 0x0000 }, + { 0x38ca, 0x0000 }, + { 0x38cb, 0x0000 }, + { 0x38cc, 0x0000 }, + { 0x38cd, 0x0000 }, + { 0x38ce, 0x0000 }, + { 0x38cf, 0x0000 }, + { 0x38d0, 0x0000 }, + { 0x38d1, 0x0000 }, + { 0x38d2, 0x0000 }, + { 0x38d3, 0x0000 }, + { 0x38d4, 0x0000 }, + { 0x38d5, 0x0000 }, + { 0x38d6, 0x0000 }, + { 0x38d7, 0x0000 }, + { 0x38d8, 0x0000 }, + { 0x38d9, 0x0000 }, + { 0x38da, 0x0000 }, + { 0x38db, 0x0000 }, + { 0x38dc, 0x0000 }, + { 0x38dd, 0x0000 }, + { 0x38de, 0x0000 }, + { 0x38df, 0x0000 }, + { 0x38e0, 0x0000 }, + { 0x38e1, 0x0000 }, + { 0x38e2, 0x0000 }, + { 0x38e3, 0x0000 }, + { 0x38e4, 0x0000 }, + { 0x38e5, 0x0000 }, + { 0x38e6, 0x0000 }, + { 0x38e7, 0x0000 }, + { 0x38e8, 0x0000 }, + { 0x38e9, 0x0000 }, + { 0x38ea, 0x0000 }, + { 0x38eb, 0x0000 }, + { 0x38ec, 0x0000 }, + { 0x38ed, 0x0000 }, + { 0x38ee, 0x0000 }, + { 0x38ef, 0x0000 }, + { 0x38f0, 0x0000 }, + { 0x38f1, 0x0000 }, + { 0x38f2, 0x0000 }, + { 0x38f3, 0x0000 }, + { 0x38f4, 0x0000 }, + { 0x38f5, 0x0000 }, + { 0x38f6, 0x0000 }, + { 0x38f7, 0x0000 }, + { 0x38f8, 0x0000 }, + { 0x38f9, 0x0000 }, + { 0x38fa, 0x0000 }, + { 0x38fb, 0x0000 }, + { 0x38fc, 0x0000 }, + { 0x38fd, 0x0000 }, + { 0x38fe, 0x0000 }, + { 0x38ff, 0x0000 }, + { 0x3900, 0x0000 }, + { 0x3901, 0x0000 }, + { 0x3902, 0x0000 }, + { 0x3903, 0x0000 }, + { 0x3904, 0x0000 }, + { 0x3905, 0x0000 }, + { 0x3906, 0x0000 }, + { 0x3907, 0x0000 }, + { 0x3908, 0x0000 }, + { 0x3909, 0x0000 }, + { 0x390a, 0x0000 }, + { 0x390b, 0x0000 }, + { 0x390c, 0x0000 }, + { 0x390d, 0x0000 }, + { 0x390e, 0x0000 }, + { 0x390f, 0x0000 }, + { 0x3910, 0x0000 }, + { 0x3911, 0x0000 }, + { 0x3912, 0x0000 }, + { 0x3913, 0x0000 }, + { 0x3914, 0x0000 }, + { 0x3915, 0x0000 }, + { 0x3916, 0x0000 }, + { 0x3917, 0x0000 }, + { 0x3918, 0x0000 }, + { 0x3919, 0x0000 }, + { 0x391a, 0x0000 }, + { 0x391b, 0x0000 }, + { 0x391c, 0x0000 }, + { 0x391d, 0x0000 }, + { 0x391e, 0x0000 }, + { 0x391f, 0x0000 }, + { 0x3920, 0x0000 }, + { 0x3921, 0x0000 }, + { 0x3922, 0x0000 }, + { 0x3923, 0x0000 }, + { 0x3924, 0x0000 }, + { 0x3925, 0x0000 }, + { 0x3926, 0x0000 }, + { 0x3927, 0x0000 }, + { 0x3928, 0x0000 }, + { 0x3929, 0x0000 }, + { 0x392a, 0x0000 }, + { 0x392b, 0x0000 }, + { 0x392c, 0x0000 }, + { 0x392d, 0x0000 }, + { 0x392e, 0x0000 }, + { 0x392f, 0x0000 }, + { 0x3930, 0x0000 }, + { 0x3931, 0x0000 }, + { 0x3932, 0x0000 }, + { 0x3933, 0x0000 }, + { 0x3934, 0x0000 }, + { 0x3935, 0x0000 }, + { 0x3936, 0x0000 }, + { 0x3937, 0x0000 }, + { 0x3938, 0x0000 }, + { 0x3939, 0x0000 }, + { 0x393a, 0x0000 }, + { 0x393b, 0x0000 }, + { 0x393c, 0x0000 }, + { 0x393d, 0x0000 }, + { 0x393e, 0x0000 }, + { 0x393f, 0x0000 }, + { 0x3940, 0x0000 }, + { 0x3941, 0x0000 }, + { 0x3942, 0x0000 }, + { 0x3943, 0x0000 }, + { 0x3944, 0x0000 }, + { 0x3945, 0x0000 }, + { 0x3946, 0x0000 }, + { 0x3947, 0x0000 }, + { 0x3948, 0x0000 }, + { 0x3949, 0x0000 }, + { 0x394a, 0x0000 }, + { 0x394b, 0x0000 }, + { 0x394c, 0x0000 }, + { 0x394d, 0x0000 }, + { 0x394e, 0x0000 }, + { 0x394f, 0x0000 }, + { 0x3950, 0x0000 }, + { 0x3951, 0x0000 }, + { 0x3952, 0x0000 }, + { 0x3953, 0x0000 }, + { 0x3954, 0x0000 }, + { 0x3955, 0x0000 }, + { 0x3956, 0x0000 }, + { 0x3957, 0x0000 }, + { 0x3958, 0x0000 }, + { 0x3959, 0x0000 }, + { 0x395a, 0x0000 }, + { 0x395b, 0x0000 }, + { 0x395c, 0x0000 }, + { 0x395d, 0x0000 }, + { 0x395e, 0x0000 }, + { 0x395f, 0x0000 }, + { 0x3960, 0x0000 }, + { 0x3961, 0x0000 }, + { 0x3962, 0x0000 }, + { 0x3963, 0x0000 }, + { 0x3964, 0x0000 }, + { 0x3965, 0x0000 }, + { 0x3966, 0x0000 }, + { 0x3967, 0x0000 }, + { 0x3968, 0x0000 }, + { 0x3969, 0x0000 }, + { 0x396a, 0x0000 }, + { 0x396b, 0x0000 }, + { 0x396c, 0x0000 }, + { 0x396d, 0x0000 }, + { 0x396e, 0x0000 }, + { 0x396f, 0x0000 }, + { 0x3970, 0x0000 }, + { 0x3971, 0x0000 }, + { 0x3972, 0x0000 }, + { 0x3973, 0x0000 }, + { 0x3974, 0x0000 }, + { 0x3975, 0x0000 }, + { 0x3976, 0x0000 }, + { 0x3977, 0x0000 }, + { 0x3978, 0x0000 }, + { 0x3979, 0x0000 }, + { 0x397a, 0x0000 }, + { 0x397b, 0x0000 }, + { 0x397c, 0x0000 }, + { 0x397d, 0x0000 }, + { 0x397e, 0x0000 }, + { 0x397f, 0x0000 }, + { 0x3980, 0x0000 }, + { 0x3981, 0x0000 }, + { 0x3982, 0x0000 }, + { 0x3983, 0x0000 }, + { 0x3984, 0x0000 }, + { 0x3985, 0x0000 }, + { 0x3986, 0x0000 }, + { 0x3987, 0x0000 }, + { 0x3988, 0x0000 }, + { 0x3989, 0x0000 }, + { 0x398a, 0x0000 }, + { 0x398b, 0x0000 }, + { 0x398c, 0x0000 }, + { 0x398d, 0x0000 }, + { 0x398e, 0x0000 }, + { 0x398f, 0x0000 }, + { 0x3990, 0x0000 }, + { 0x3991, 0x0000 }, + { 0x3992, 0x0000 }, + { 0x3993, 0x0000 }, + { 0x3994, 0x0000 }, + { 0x3995, 0x0000 }, + { 0x3996, 0x0000 }, + { 0x3997, 0x0000 }, + { 0x3998, 0x0000 }, + { 0x3999, 0x0000 }, + { 0x399a, 0x0000 }, + { 0x399b, 0x0000 }, + { 0x399c, 0x0000 }, + { 0x399d, 0x0000 }, + { 0x399e, 0x0000 }, + { 0x399f, 0x0000 }, + { 0x39a0, 0x0000 }, + { 0x39a1, 0x0000 }, + { 0x39a2, 0x0000 }, + { 0x39a3, 0x0000 }, + { 0x39a4, 0x0000 }, + { 0x39a5, 0x0000 }, + { 0x39a6, 0x0000 }, + { 0x39a7, 0x0000 }, + { 0x39a8, 0x0000 }, + { 0x39a9, 0x0000 }, + { 0x39aa, 0x0000 }, + { 0x39ab, 0x0000 }, + { 0x39ac, 0x0000 }, + { 0x39ad, 0x0000 }, + { 0x39ae, 0x0000 }, + { 0x39af, 0x0000 }, + { 0x39b0, 0x0000 }, + { 0x39b1, 0x0000 }, + { 0x39b2, 0x0000 }, + { 0x39b3, 0x0000 }, + { 0x39b4, 0x0000 }, + { 0x39b5, 0x0000 }, + { 0x39b6, 0x0000 }, + { 0x39b7, 0x0000 }, + { 0x39b8, 0x0000 }, + { 0x39b9, 0x0000 }, + { 0x39ba, 0x0000 }, + { 0x39bb, 0x0000 }, + { 0x39bc, 0x0000 }, + { 0x39bd, 0x0000 }, + { 0x39be, 0x0000 }, + { 0x39bf, 0x0000 }, + { 0x39c0, 0x0000 }, + { 0x39c1, 0x0000 }, + { 0x39c2, 0x0000 }, + { 0x39c3, 0x0000 }, + { 0x39c4, 0x0000 }, + { 0x39c5, 0x0000 }, + { 0x39c6, 0x0000 }, + { 0x39c7, 0x0000 }, + { 0x39c8, 0x0000 }, + { 0x39c9, 0x0000 }, + { 0x39ca, 0x0000 }, + { 0x39cb, 0x0000 }, + { 0x39cc, 0x0000 }, + { 0x39cd, 0x0000 }, + { 0x39ce, 0x0000 }, + { 0x39cf, 0x0000 }, + { 0x39d0, 0x0000 }, + { 0x39d1, 0x0000 }, + { 0x39d2, 0x0000 }, + { 0x39d3, 0x0000 }, + { 0x39d4, 0x0000 }, + { 0x39d5, 0x0000 }, + { 0x39d6, 0x0000 }, + { 0x39d7, 0x0000 }, + { 0x39d8, 0x0000 }, + { 0x39d9, 0x0000 }, + { 0x39da, 0x0000 }, + { 0x39db, 0x0000 }, + { 0x39dc, 0x0000 }, + { 0x39dd, 0x0000 }, + { 0x39de, 0x0000 }, + { 0x39df, 0x0000 }, + { 0x39e0, 0x0000 }, + { 0x39e1, 0x0000 }, + { 0x39e2, 0x0000 }, + { 0x39e3, 0x0000 }, + { 0x39e4, 0x0000 }, + { 0x39e5, 0x0000 }, + { 0x39e6, 0x0000 }, + { 0x39e7, 0x0000 }, + { 0x39e8, 0x0000 }, + { 0x39e9, 0x0000 }, + { 0x39ea, 0x0000 }, + { 0x39eb, 0x0000 }, + { 0x39ec, 0x0000 }, + { 0x39ed, 0x0000 }, + { 0x39ee, 0x0000 }, + { 0x39ef, 0x0000 }, + { 0x39f0, 0x0000 }, + { 0x39f1, 0x0000 }, + { 0x39f2, 0x0000 }, + { 0x39f3, 0x0000 }, + { 0x39f4, 0x0000 }, + { 0x39f5, 0x0000 }, + { 0x39f6, 0x0000 }, + { 0x39f7, 0x0000 }, + { 0x39f8, 0x0000 }, + { 0x39f9, 0x0000 }, + { 0x39fa, 0x0000 }, + { 0x39fb, 0x0000 }, + { 0x39fc, 0x0000 }, + { 0x39fd, 0x0000 }, + { 0x39fe, 0x0000 }, + { 0x39ff, 0x0000 }, + { 0x3a00, 0x0000 }, + { 0x3a01, 0x0000 }, + { 0x3a02, 0x0000 }, + { 0x3a03, 0x0000 }, + { 0x3a04, 0x0000 }, + { 0x3a05, 0x0000 }, + { 0x3a06, 0x0000 }, + { 0x3a07, 0x0000 }, + { 0x3a08, 0x0000 }, + { 0x3a09, 0x0000 }, + { 0x3a0a, 0x0000 }, + { 0x3a0b, 0x0000 }, + { 0x3a0c, 0x0000 }, + { 0x3a0d, 0x0000 }, + { 0x3a0e, 0x0000 }, + { 0x3a0f, 0x0000 }, + { 0x3a10, 0x0000 }, + { 0x3a11, 0x0000 }, + { 0x3a12, 0x0000 }, + { 0x3a13, 0x0000 }, + { 0x3a14, 0x0000 }, + { 0x3a15, 0x0000 }, + { 0x3a16, 0x0000 }, + { 0x3a17, 0x0000 }, + { 0x3a18, 0x0000 }, + { 0x3a19, 0x0000 }, + { 0x3a1a, 0x0000 }, + { 0x3a1b, 0x0000 }, + { 0x3a1c, 0x0000 }, + { 0x3a1d, 0x0000 }, + { 0x3a1e, 0x0000 }, + { 0x3a1f, 0x0000 }, + { 0x3a20, 0x0000 }, + { 0x3a21, 0x0000 }, + { 0x3a22, 0x0000 }, + { 0x3a23, 0x0000 }, + { 0x3a24, 0x0000 }, + { 0x3a25, 0x0000 }, + { 0x3a26, 0x0000 }, + { 0x3a27, 0x0000 }, + { 0x3a28, 0x0000 }, + { 0x3a29, 0x0000 }, + { 0x3a2a, 0x0000 }, + { 0x3a2b, 0x0000 }, + { 0x3a2c, 0x0000 }, + { 0x3a2d, 0x0000 }, + { 0x3a2e, 0x0000 }, + { 0x3a2f, 0x0000 }, + { 0x3a30, 0x0000 }, + { 0x3a31, 0x0000 }, + { 0x3a32, 0x0000 }, + { 0x3a33, 0x0000 }, + { 0x3a34, 0x0000 }, + { 0x3a35, 0x0000 }, + { 0x3a36, 0x0000 }, + { 0x3a37, 0x0000 }, + { 0x3a38, 0x0000 }, + { 0x3a39, 0x0000 }, + { 0x3a3a, 0x0000 }, + { 0x3a3b, 0x0000 }, + { 0x3a3c, 0x0000 }, + { 0x3a3d, 0x0000 }, + { 0x3a3e, 0x0000 }, + { 0x3a3f, 0x0000 }, + { 0x3a40, 0x0000 }, + { 0x3a41, 0x0000 }, + { 0x3a42, 0x0000 }, + { 0x3a43, 0x0000 }, + { 0x3a44, 0x0000 }, + { 0x3a45, 0x0000 }, + { 0x3a46, 0x0000 }, + { 0x3a47, 0x0000 }, + { 0x3a48, 0x0000 }, + { 0x3a49, 0x0000 }, + { 0x3a4a, 0x0000 }, + { 0x3a4b, 0x0000 }, + { 0x3a4c, 0x0000 }, + { 0x3a4d, 0x0000 }, + { 0x3a4e, 0x0000 }, + { 0x3a4f, 0x0000 }, + { 0x3a50, 0x0000 }, + { 0x3a51, 0x0000 }, + { 0x3a52, 0x0000 }, + { 0x3a53, 0x0000 }, + { 0x3a54, 0x0000 }, + { 0x3a55, 0x0000 }, + { 0x3a56, 0x0000 }, + { 0x3a57, 0x0000 }, + { 0x3a58, 0x0000 }, + { 0x3a59, 0x0000 }, + { 0x3a5a, 0x0000 }, + { 0x3a5b, 0x0000 }, + { 0x3a5c, 0x0000 }, + { 0x3a5d, 0x0000 }, + { 0x3a5e, 0x0000 }, + { 0x3a5f, 0x0000 }, + { 0x3a60, 0x0000 }, + { 0x3a61, 0x0000 }, + { 0x3a62, 0x0000 }, + { 0x3a63, 0x0000 }, + { 0x3a64, 0x0000 }, + { 0x3a65, 0x0000 }, + { 0x3a66, 0x0000 }, + { 0x3a67, 0x0000 }, + { 0x3a68, 0x0000 }, + { 0x3a69, 0x0000 }, + { 0x3a6a, 0x0000 }, + { 0x3a6b, 0x0000 }, + { 0x3a6c, 0x0000 }, + { 0x3a6d, 0x0000 }, + { 0x3a6e, 0x0000 }, + { 0x3a6f, 0x0000 }, + { 0x3a70, 0x0000 }, + { 0x3a71, 0x0000 }, + { 0x3a72, 0x0000 }, + { 0x3a73, 0x0000 }, + { 0x3a74, 0x0000 }, + { 0x3a75, 0x0000 }, + { 0x3a76, 0x0000 }, + { 0x3a77, 0x0000 }, + { 0x3a78, 0x0000 }, + { 0x3a79, 0x0000 }, + { 0x3a7a, 0x0000 }, + { 0x3a7b, 0x0000 }, + { 0x3a7c, 0x0000 }, + { 0x3a7d, 0x0000 }, + { 0x3a7e, 0x0000 }, + { 0x3a7f, 0x0000 }, + { 0x3a80, 0x0000 }, + { 0x3a81, 0x0000 }, + { 0x3a82, 0x0000 }, + { 0x3a83, 0x0000 }, + { 0x3a84, 0x0000 }, + { 0x3a85, 0x0000 }, + { 0x3a86, 0x0000 }, + { 0x3a87, 0x0000 }, + { 0x3a88, 0x0000 }, + { 0x3a89, 0x0000 }, + { 0x3a8a, 0x0000 }, + { 0x3a8b, 0x0000 }, + { 0x3a8c, 0x0000 }, + { 0x3a8d, 0x0000 }, + { 0x3a8e, 0x0000 }, + { 0x3a8f, 0x0000 }, + { 0x3a90, 0x0000 }, + { 0x3a91, 0x0000 }, + { 0x3a92, 0x0000 }, + { 0x3a93, 0x0000 }, + { 0x3a94, 0x0000 }, + { 0x3a95, 0x0000 }, + { 0x3a96, 0x0000 }, + { 0x3a97, 0x0000 }, + { 0x3a98, 0x0000 }, + { 0x3a99, 0x0000 }, + { 0x3a9a, 0x0000 }, + { 0x3a9b, 0x0000 }, + { 0x3a9c, 0x0000 }, + { 0x3a9d, 0x0000 }, + { 0x3a9e, 0x0000 }, + { 0x3a9f, 0x0000 }, + { 0x3aa0, 0x0000 }, + { 0x3aa1, 0x0000 }, + { 0x3aa2, 0x0000 }, + { 0x3aa3, 0x0000 }, + { 0x3aa4, 0x0000 }, + { 0x3aa5, 0x0000 }, + { 0x3aa6, 0x0000 }, + { 0x3aa7, 0x0000 }, + { 0x3aa8, 0x0000 }, + { 0x3aa9, 0x0000 }, + { 0x3aaa, 0x0000 }, + { 0x3aab, 0x0000 }, + { 0x3aac, 0x0000 }, + { 0x3aad, 0x0000 }, + { 0x3aae, 0x0000 }, + { 0x3aaf, 0x0000 }, + { 0x3ab0, 0x0000 }, + { 0x3ab1, 0x0000 }, + { 0x3ab2, 0x0000 }, + { 0x3ab3, 0x0000 }, + { 0x3ab4, 0x0000 }, + { 0x3ab5, 0x0000 }, + { 0x3ab6, 0x0000 }, + { 0x3ab7, 0x0000 }, + { 0x3ab8, 0x0000 }, + { 0x3ab9, 0x0000 }, + { 0x3aba, 0x0000 }, + { 0x3abb, 0x0000 }, + { 0x3abc, 0x0000 }, + { 0x3abd, 0x0000 }, + { 0x3abe, 0x0000 }, + { 0x3abf, 0x0000 }, + { 0x3ac0, 0x0000 }, + { 0x3ac1, 0x0000 }, + { 0x3ac2, 0x0000 }, + { 0x3ac3, 0x0000 }, + { 0x3ac4, 0x0000 }, + { 0x3ac5, 0x0000 }, + { 0x3ac6, 0x0000 }, + { 0x3ac7, 0x0000 }, + { 0x3ac8, 0x0000 }, + { 0x3ac9, 0x0000 }, + { 0x3aca, 0x0000 }, + { 0x3acb, 0x0000 }, + { 0x3acc, 0x0000 }, + { 0x3acd, 0x0000 }, + { 0x3ace, 0x0000 }, + { 0x3acf, 0x0000 }, + { 0x3ad0, 0x0000 }, + { 0x3ad1, 0x0000 }, + { 0x3ad2, 0x0000 }, + { 0x3ad3, 0x0000 }, + { 0x3ad4, 0x0000 }, + { 0x3ad5, 0x0000 }, + { 0x3ad6, 0x0000 }, + { 0x3ad7, 0x0000 }, + { 0x3ad8, 0x0000 }, + { 0x3ad9, 0x0000 }, + { 0x3ada, 0x0000 }, + { 0x3adb, 0x0000 }, + { 0x3adc, 0x0000 }, + { 0x3add, 0x0000 }, + { 0x3ade, 0x0000 }, + { 0x3adf, 0x0000 }, + { 0x3ae0, 0x0000 }, + { 0x3ae1, 0x0000 }, + { 0x3ae2, 0x0000 }, + { 0x3ae3, 0x0000 }, + { 0x3ae4, 0x0000 }, + { 0x3ae5, 0x0000 }, + { 0x3ae6, 0x0000 }, + { 0x3ae7, 0x0000 }, + { 0x3ae8, 0x0000 }, + { 0x3ae9, 0x0000 }, + { 0x3aea, 0x0000 }, + { 0x3aeb, 0x0000 }, + { 0x3aec, 0x0000 }, + { 0x3aed, 0x0000 }, + { 0x3aee, 0x0000 }, + { 0x3aef, 0x0000 }, + { 0x3af0, 0x0000 }, + { 0x3af1, 0x0000 }, + { 0x3af2, 0x0000 }, + { 0x3af3, 0x0000 }, + { 0x3af4, 0x0000 }, + { 0x3af5, 0x0000 }, + { 0x3af6, 0x0000 }, + { 0x3af7, 0x0000 }, + { 0x3af8, 0x0000 }, + { 0x3af9, 0x0000 }, + { 0x3afa, 0x0000 }, + { 0x3afb, 0x0000 }, + { 0x3afc, 0x0000 }, + { 0x3afd, 0x0000 }, + { 0x3afe, 0x0000 }, + { 0x3aff, 0x0000 }, + { 0x3b00, 0x0000 }, + { 0x3b01, 0x0000 }, + { 0x3b02, 0x0000 }, + { 0x3b03, 0x0000 }, + { 0x3b04, 0x0000 }, + { 0x3b05, 0x0000 }, + { 0x3b06, 0x0000 }, + { 0x3b07, 0x0000 }, + { 0x3b08, 0x0000 }, + { 0x3b09, 0x0000 }, + { 0x3b0a, 0x0000 }, + { 0x3b0b, 0x0000 }, + { 0x3b0c, 0x0000 }, + { 0x3b0d, 0x0000 }, + { 0x3b0e, 0x0000 }, + { 0x3b0f, 0x0000 }, + { 0x3b10, 0x0000 }, + { 0x3b11, 0x0000 }, + { 0x3b12, 0x0000 }, + { 0x3b13, 0x0000 }, + { 0x3b14, 0x0000 }, + { 0x3b15, 0x0000 }, + { 0x3b16, 0x0000 }, + { 0x3b17, 0x0000 }, + { 0x3b18, 0x0000 }, + { 0x3b19, 0x0000 }, + { 0x3b1a, 0x0000 }, + { 0x3b1b, 0x0000 }, + { 0x3b1c, 0x0000 }, + { 0x3b1d, 0x0000 }, + { 0x3b1e, 0x0000 }, + { 0x3b1f, 0x0000 }, + { 0x3b20, 0x0000 }, + { 0x3b21, 0x0000 }, + { 0x3b22, 0x0000 }, + { 0x3b23, 0x0000 }, + { 0x3b24, 0x0000 }, + { 0x3b25, 0x0000 }, + { 0x3b26, 0x0000 }, + { 0x3b27, 0x0000 }, + { 0x3b28, 0x0000 }, + { 0x3b29, 0x0000 }, + { 0x3b2a, 0x0000 }, + { 0x3b2b, 0x0000 }, + { 0x3b2c, 0x0000 }, + { 0x3b2d, 0x0000 }, + { 0x3b2e, 0x0000 }, + { 0x3b2f, 0x0000 }, + { 0x3b30, 0x0000 }, + { 0x3b31, 0x0000 }, + { 0x3b32, 0x0000 }, + { 0x3b33, 0x0000 }, + { 0x3b34, 0x0000 }, + { 0x3b35, 0x0000 }, + { 0x3b36, 0x0000 }, + { 0x3b37, 0x0000 }, + { 0x3b38, 0x0000 }, + { 0x3b39, 0x0000 }, + { 0x3b3a, 0x0000 }, + { 0x3b3b, 0x0000 }, + { 0x3b3c, 0x0000 }, + { 0x3b3d, 0x0000 }, + { 0x3b3e, 0x0000 }, + { 0x3b3f, 0x0000 }, + { 0x3b40, 0x0000 }, + { 0x3b41, 0x0000 }, + { 0x3b42, 0x0000 }, + { 0x3b43, 0x0000 }, + { 0x3b44, 0x0000 }, + { 0x3b45, 0x0000 }, + { 0x3b46, 0x0000 }, + { 0x3b47, 0x0000 }, + { 0x3b48, 0x0000 }, + { 0x3b49, 0x0000 }, + { 0x3b4a, 0x0000 }, + { 0x3b4b, 0x0000 }, + { 0x3b4c, 0x0000 }, + { 0x3b4d, 0x0000 }, + { 0x3b4e, 0x0000 }, + { 0x3b4f, 0x0000 }, + { 0x3b50, 0x0000 }, + { 0x3b51, 0x0000 }, + { 0x3b52, 0x0000 }, + { 0x3b53, 0x0000 }, + { 0x3b54, 0x0000 }, + { 0x3b55, 0x0000 }, + { 0x3b56, 0x0000 }, + { 0x3b57, 0x0000 }, + { 0x3b58, 0x0000 }, + { 0x3b59, 0x0000 }, + { 0x3b5a, 0x0000 }, + { 0x3b5b, 0x0000 }, + { 0x3b5c, 0x0000 }, + { 0x3b5d, 0x0000 }, + { 0x3b5e, 0x0000 }, + { 0x3b5f, 0x0000 }, + { 0x3b60, 0x0000 }, + { 0x3b61, 0x0000 }, + { 0x3b62, 0x0000 }, + { 0x3b63, 0x0000 }, + { 0x3b64, 0x0000 }, + { 0x3b65, 0x0000 }, + { 0x3b66, 0x0000 }, + { 0x3b67, 0x0000 }, + { 0x3b68, 0x0000 }, + { 0x3b69, 0x0000 }, + { 0x3b6a, 0x0000 }, + { 0x3b6b, 0x0000 }, + { 0x3b6c, 0x0000 }, + { 0x3b6d, 0x0000 }, + { 0x3b6e, 0x0000 }, + { 0x3b6f, 0x0000 }, + { 0x3b70, 0x0000 }, + { 0x3b71, 0x0000 }, + { 0x3b72, 0x0000 }, + { 0x3b73, 0x0000 }, + { 0x3b74, 0x0000 }, + { 0x3b75, 0x0000 }, + { 0x3b76, 0x0000 }, + { 0x3b77, 0x0000 }, + { 0x3b78, 0x0000 }, + { 0x3b79, 0x0000 }, + { 0x3b7a, 0x0000 }, + { 0x3b7b, 0x0000 }, + { 0x3b7c, 0x0000 }, + { 0x3b7d, 0x0000 }, + { 0x3b7e, 0x0000 }, + { 0x3b7f, 0x0000 }, + { 0x3b80, 0x0000 }, + { 0x3b81, 0x0000 }, + { 0x3b82, 0x0000 }, + { 0x3b83, 0x0000 }, + { 0x3b84, 0x0000 }, + { 0x3b85, 0x0000 }, + { 0x3b86, 0x0000 }, + { 0x3b87, 0x0000 }, + { 0x3b88, 0x0000 }, + { 0x3b89, 0x0000 }, + { 0x3b8a, 0x0000 }, + { 0x3b8b, 0x0000 }, + { 0x3b8c, 0x0000 }, + { 0x3b8d, 0x0000 }, + { 0x3b8e, 0x0000 }, + { 0x3b8f, 0x0000 }, + { 0x3b90, 0x0000 }, + { 0x3b91, 0x0000 }, + { 0x3b92, 0x0000 }, + { 0x3b93, 0x0000 }, + { 0x3b94, 0x0000 }, + { 0x3b95, 0x0000 }, + { 0x3b96, 0x0000 }, + { 0x3b97, 0x0000 }, + { 0x3b98, 0x0000 }, + { 0x3b99, 0x0000 }, + { 0x3b9a, 0x0000 }, + { 0x3b9b, 0x0000 }, + { 0x3b9c, 0x0000 }, + { 0x3b9d, 0x0000 }, + { 0x3b9e, 0x0000 }, + { 0x3b9f, 0x0000 }, + { 0x3ba0, 0x0000 }, + { 0x3ba1, 0x0000 }, + { 0x3ba2, 0x0000 }, + { 0x3ba3, 0x0000 }, + { 0x3ba4, 0x0000 }, + { 0x3ba5, 0x0000 }, + { 0x3ba6, 0x0000 }, + { 0x3ba7, 0x0000 }, + { 0x3ba8, 0x0000 }, + { 0x3ba9, 0x0000 }, + { 0x3baa, 0x0000 }, + { 0x3bab, 0x0000 }, + { 0x3bac, 0x0000 }, + { 0x3bad, 0x0000 }, + { 0x3bae, 0x0000 }, + { 0x3baf, 0x0000 }, + { 0x3bb0, 0x0000 }, + { 0x3bb1, 0x0000 }, + { 0x3bb2, 0x0000 }, + { 0x3bb3, 0x0000 }, + { 0x3bb4, 0x0000 }, + { 0x3bb5, 0x0000 }, + { 0x3bb6, 0x0000 }, + { 0x3bb7, 0x0000 }, + { 0x3bb8, 0x0000 }, + { 0x3bb9, 0x0000 }, + { 0x3bba, 0x0000 }, + { 0x3bbb, 0x0000 }, + { 0x3bbc, 0x0000 }, + { 0x3bbd, 0x0000 }, + { 0x3bbe, 0x0000 }, + { 0x3bbf, 0x0000 }, + { 0x3bc0, 0x0000 }, + { 0x3bc1, 0x0000 }, + { 0x3bc2, 0x0000 }, + { 0x3bc3, 0x0000 }, + { 0x3bc4, 0x0000 }, + { 0x3bc5, 0x0000 }, + { 0x3bc6, 0x0000 }, + { 0x3bc7, 0x0000 }, + { 0x3bc8, 0x0000 }, + { 0x3bc9, 0x0000 }, + { 0x3bca, 0x0000 }, + { 0x3bcb, 0x0000 }, + { 0x3bcc, 0x0000 }, + { 0x3bcd, 0x0000 }, + { 0x3bce, 0x0000 }, + { 0x3bcf, 0x0000 }, + { 0x3bd0, 0x0000 }, + { 0x3bd1, 0x0000 }, + { 0x3bd2, 0x0000 }, + { 0x3bd3, 0x0000 }, + { 0x3bd4, 0x0000 }, + { 0x3bd5, 0x0000 }, + { 0x3bd6, 0x0000 }, + { 0x3bd7, 0x0000 }, + { 0x3bd8, 0x0000 }, + { 0x3bd9, 0x0000 }, + { 0x3bda, 0x0000 }, + { 0x3bdb, 0x0000 }, + { 0x3bdc, 0x0000 }, + { 0x3bdd, 0x0000 }, + { 0x3bde, 0x0000 }, + { 0x3bdf, 0x0000 }, + { 0x3be0, 0x0000 }, + { 0x3be1, 0x0000 }, + { 0x3be2, 0x0000 }, + { 0x3be3, 0x0000 }, + { 0x3be4, 0x0000 }, + { 0x3be5, 0x0000 }, + { 0x3be6, 0x0000 }, + { 0x3be7, 0x0000 }, + { 0x3be8, 0x0000 }, + { 0x3be9, 0x0000 }, + { 0x3bea, 0x0000 }, + { 0x3beb, 0x0000 }, + { 0x3bec, 0x0000 }, + { 0x3bed, 0x0000 }, + { 0x3bee, 0x0000 }, + { 0x3bef, 0x0000 }, + { 0x3bf0, 0x0000 }, + { 0x3bf1, 0x0000 }, + { 0x3bf2, 0x0000 }, + { 0x3bf3, 0x0000 }, + { 0x3bf4, 0x0000 }, + { 0x3bf5, 0x0000 }, + { 0x3bf6, 0x0000 }, + { 0x3bf7, 0x0000 }, + { 0x3bf8, 0x0000 }, + { 0x3bf9, 0x0000 }, + { 0x3bfa, 0x0000 }, + { 0x3bfb, 0x0000 }, + { 0x3bfc, 0x0000 }, + { 0x3bfd, 0x0000 }, + { 0x3bfe, 0x0000 }, + { 0x3bff, 0x0000 }, + { 0x3c00, 0x0000 }, + { 0x3c01, 0x0000 }, + { 0x3c02, 0x0000 }, + { 0x3c03, 0x0000 }, + { 0x3c04, 0x0000 }, + { 0x3c05, 0x0000 }, + { 0x3c06, 0x0000 }, + { 0x3c07, 0x0000 }, + { 0x3c08, 0x0000 }, + { 0x3c09, 0x0000 }, + { 0x3c0a, 0x0000 }, + { 0x3c0b, 0x0000 }, + { 0x3c0c, 0x0000 }, + { 0x3c0d, 0x0000 }, + { 0x3c0e, 0x0000 }, + { 0x3c0f, 0x0000 }, + { 0x3c10, 0x0000 }, + { 0x3c11, 0x0000 }, + { 0x3c12, 0x0000 }, + { 0x3c13, 0x0000 }, + { 0x3c14, 0x0000 }, + { 0x3c15, 0x0000 }, + { 0x3c16, 0x0000 }, + { 0x3c17, 0x0000 }, + { 0x3c18, 0x0000 }, + { 0x3c19, 0x0000 }, + { 0x3c1a, 0x0000 }, + { 0x3c1b, 0x0000 }, + { 0x3c1c, 0x0000 }, + { 0x3c1d, 0x0000 }, + { 0x3c1e, 0x0000 }, + { 0x3c1f, 0x0000 }, + { 0x3c20, 0x0000 }, + { 0x3c21, 0x0000 }, + { 0x3c22, 0x0000 }, + { 0x3c23, 0x0000 }, + { 0x3c24, 0x0000 }, + { 0x3c25, 0x0000 }, + { 0x3c26, 0x0000 }, + { 0x3c27, 0x0000 }, + { 0x3c28, 0x0000 }, + { 0x3c29, 0x0000 }, + { 0x3c2a, 0x0000 }, + { 0x3c2b, 0x0000 }, + { 0x3c2c, 0x0000 }, + { 0x3c2d, 0x0000 }, + { 0x3c2e, 0x0000 }, + { 0x3c2f, 0x0000 }, + { 0x3c30, 0x0000 }, + { 0x3c31, 0x0000 }, + { 0x3c32, 0x0000 }, + { 0x3c33, 0x0000 }, + { 0x3c34, 0x0000 }, + { 0x3c35, 0x0000 }, + { 0x3c36, 0x0000 }, + { 0x3c37, 0x0000 }, + { 0x3c38, 0x0000 }, + { 0x3c39, 0x0000 }, + { 0x3c3a, 0x0000 }, + { 0x3c3b, 0x0000 }, + { 0x3c3c, 0x0000 }, + { 0x3c3d, 0x0000 }, + { 0x3c3e, 0x0000 }, + { 0x3c3f, 0x0000 }, + { 0x3c40, 0x0000 }, + { 0x3c41, 0x0000 }, + { 0x3c42, 0x0000 }, + { 0x3c43, 0x0000 }, + { 0x3c44, 0x0000 }, + { 0x3c45, 0x0000 }, + { 0x3c46, 0x0000 }, + { 0x3c47, 0x0000 }, + { 0x3c48, 0x0000 }, + { 0x3c49, 0x0000 }, + { 0x3c4a, 0x0000 }, + { 0x3c4b, 0x0000 }, + { 0x3c4c, 0x0000 }, + { 0x3c4d, 0x0000 }, + { 0x3c4e, 0x0000 }, + { 0x3c4f, 0x0000 }, + { 0x3c50, 0x0000 }, + { 0x3c51, 0x0000 }, + { 0x3c52, 0x0000 }, + { 0x3c53, 0x0000 }, + { 0x3c54, 0x0000 }, + { 0x3c55, 0x0000 }, + { 0x3c56, 0x0000 }, + { 0x3c57, 0x0000 }, + { 0x3c58, 0x0000 }, + { 0x3c59, 0x0000 }, + { 0x3c5a, 0x0000 }, + { 0x3c5b, 0x0000 }, + { 0x3c5c, 0x0000 }, + { 0x3c5d, 0x0000 }, + { 0x3c5e, 0x0000 }, + { 0x3c5f, 0x0000 }, + { 0x3c60, 0x0000 }, + { 0x3c61, 0x0000 }, + { 0x3c62, 0x0000 }, + { 0x3c63, 0x0000 }, + { 0x3c64, 0x0000 }, + { 0x3c65, 0x0000 }, + { 0x3c66, 0x0000 }, + { 0x3c67, 0x0000 }, + { 0x3c68, 0x0000 }, + { 0x3c69, 0x0000 }, + { 0x3c6a, 0x0000 }, + { 0x3c6b, 0x0000 }, + { 0x3c6c, 0x0000 }, + { 0x3c6d, 0x0000 }, + { 0x3c6e, 0x0000 }, + { 0x3c6f, 0x0000 }, + { 0x3c70, 0x0000 }, + { 0x3c71, 0x0000 }, + { 0x3c72, 0x0000 }, + { 0x3c73, 0x0000 }, + { 0x3c74, 0x0000 }, + { 0x3c75, 0x0000 }, + { 0x3c76, 0x0000 }, + { 0x3c77, 0x0000 }, + { 0x3c78, 0x0000 }, + { 0x3c79, 0x0000 }, + { 0x3c7a, 0x0000 }, + { 0x3c7b, 0x0000 }, + { 0x3c7c, 0x0000 }, + { 0x3c7d, 0x0000 }, + { 0x3c7e, 0x0000 }, + { 0x3c7f, 0x0000 }, + { 0x3c80, 0x0000 }, + { 0x3c81, 0x0000 }, + { 0x3c82, 0x0000 }, + { 0x3c83, 0x0000 }, + { 0x3c84, 0x0000 }, + { 0x3c85, 0x0000 }, + { 0x3c86, 0x0000 }, + { 0x3c87, 0x0000 }, + { 0x3c88, 0x0000 }, + { 0x3c89, 0x0000 }, + { 0x3c8a, 0x0000 }, + { 0x3c8b, 0x0000 }, + { 0x3c8c, 0x0000 }, + { 0x3c8d, 0x0000 }, + { 0x3c8e, 0x0000 }, + { 0x3c8f, 0x0000 }, + { 0x3c90, 0x0000 }, + { 0x3c91, 0x0000 }, + { 0x3c92, 0x0000 }, + { 0x3c93, 0x0000 }, + { 0x3c94, 0x0000 }, + { 0x3c95, 0x0000 }, + { 0x3c96, 0x0000 }, + { 0x3c97, 0x0000 }, + { 0x3c98, 0x0000 }, + { 0x3c99, 0x0000 }, + { 0x3c9a, 0x0000 }, + { 0x3c9b, 0x0000 }, + { 0x3c9c, 0x0000 }, + { 0x3c9d, 0x0000 }, + { 0x3c9e, 0x0000 }, + { 0x3c9f, 0x0000 }, + { 0x3ca0, 0x0000 }, + { 0x3ca1, 0x0000 }, + { 0x3ca2, 0x0000 }, + { 0x3ca3, 0x0000 }, + { 0x3ca4, 0x0000 }, + { 0x3ca5, 0x0000 }, + { 0x3ca6, 0x0000 }, + { 0x3ca7, 0x0000 }, + { 0x3ca8, 0x0000 }, + { 0x3ca9, 0x0000 }, + { 0x3caa, 0x0000 }, + { 0x3cab, 0x0000 }, + { 0x3cac, 0x0000 }, + { 0x3cad, 0x0000 }, + { 0x3cae, 0x0000 }, + { 0x3caf, 0x0000 }, + { 0x3cb0, 0x0000 }, + { 0x3cb1, 0x0000 }, + { 0x3cb2, 0x0000 }, + { 0x3cb3, 0x0000 }, + { 0x3cb4, 0x0000 }, + { 0x3cb5, 0x0000 }, + { 0x3cb6, 0x0000 }, + { 0x3cb7, 0x0000 }, + { 0x3cb8, 0x0000 }, + { 0x3cb9, 0x0000 }, + { 0x3cba, 0x0000 }, + { 0x3cbb, 0x0000 }, + { 0x3cbc, 0x0000 }, + { 0x3cbd, 0x0000 }, + { 0x3cbe, 0x0000 }, + { 0x3cbf, 0x0000 }, + { 0x3cc0, 0x0000 }, + { 0x3cc1, 0x0000 }, + { 0x3cc2, 0x0000 }, + { 0x3cc3, 0x0000 }, + { 0x3cc4, 0x0000 }, + { 0x3cc5, 0x0000 }, + { 0x3cc6, 0x0000 }, + { 0x3cc7, 0x0000 }, + { 0x3cc8, 0x0000 }, + { 0x3cc9, 0x0000 }, + { 0x3cca, 0x0000 }, + { 0x3ccb, 0x0000 }, + { 0x3ccc, 0x0000 }, + { 0x3ccd, 0x0000 }, + { 0x3cce, 0x0000 }, + { 0x3ccf, 0x0000 }, + { 0x3cd0, 0x0000 }, + { 0x3cd1, 0x0000 }, + { 0x3cd2, 0x0000 }, + { 0x3cd3, 0x0000 }, + { 0x3cd4, 0x0000 }, + { 0x3cd5, 0x0000 }, + { 0x3cd6, 0x0000 }, + { 0x3cd7, 0x0000 }, + { 0x3cd8, 0x0000 }, + { 0x3cd9, 0x0000 }, + { 0x3cda, 0x0000 }, + { 0x3cdb, 0x0000 }, + { 0x3cdc, 0x0000 }, + { 0x3cdd, 0x0000 }, + { 0x3cde, 0x0000 }, + { 0x3cdf, 0x0000 }, + { 0x3ce0, 0x0000 }, + { 0x3ce1, 0x0000 }, + { 0x3ce2, 0x0000 }, + { 0x3ce3, 0x0000 }, + { 0x3ce4, 0x0000 }, + { 0x3ce5, 0x0000 }, + { 0x3ce6, 0x0000 }, + { 0x3ce7, 0x0000 }, + { 0x3ce8, 0x0000 }, + { 0x3ce9, 0x0000 }, + { 0x3cea, 0x0000 }, + { 0x3ceb, 0x0000 }, + { 0x3cec, 0x0000 }, + { 0x3ced, 0x0000 }, + { 0x3cee, 0x0000 }, + { 0x3cef, 0x0000 }, + { 0x3cf0, 0x0000 }, + { 0x3cf1, 0x0000 }, + { 0x3cf2, 0x0000 }, + { 0x3cf3, 0x0000 }, + { 0x3cf4, 0x0000 }, + { 0x3cf5, 0x0000 }, + { 0x3cf6, 0x0000 }, + { 0x3cf7, 0x0000 }, + { 0x3cf8, 0x0000 }, + { 0x3cf9, 0x0000 }, + { 0x3cfa, 0x0000 }, + { 0x3cfb, 0x0000 }, + { 0x3cfc, 0x0000 }, + { 0x3cfd, 0x0000 }, + { 0x3cfe, 0x0000 }, + { 0x3cff, 0x0000 }, + { 0x3d00, 0x0000 }, + { 0x3d01, 0x0000 }, + { 0x3d02, 0x0000 }, + { 0x3d03, 0x0000 }, + { 0x3d04, 0x0000 }, + { 0x3d05, 0x0000 }, + { 0x3d06, 0x0000 }, + { 0x3d07, 0x0000 }, + { 0x3d08, 0x0000 }, + { 0x3d09, 0x0000 }, + { 0x3d0a, 0x0000 }, + { 0x3d0b, 0x0000 }, + { 0x3d0c, 0x0000 }, + { 0x3d0d, 0x0000 }, + { 0x3d0e, 0x0000 }, + { 0x3d0f, 0x0000 }, + { 0x3d10, 0x0000 }, + { 0x3d11, 0x0000 }, + { 0x3d12, 0x0000 }, + { 0x3d13, 0x0000 }, + { 0x3d14, 0x0000 }, + { 0x3d15, 0x0000 }, + { 0x3d16, 0x0000 }, + { 0x3d17, 0x0000 }, + { 0x3d18, 0x0000 }, + { 0x3d19, 0x0000 }, + { 0x3d1a, 0x0000 }, + { 0x3d1b, 0x0000 }, + { 0x3d1c, 0x0000 }, + { 0x3d1d, 0x0000 }, + { 0x3d1e, 0x0000 }, + { 0x3d1f, 0x0000 }, + { 0x3d20, 0x0000 }, + { 0x3d21, 0x0000 }, + { 0x3d22, 0x0000 }, + { 0x3d23, 0x0000 }, + { 0x3d24, 0x0000 }, + { 0x3d25, 0x0000 }, + { 0x3d26, 0x0000 }, + { 0x3d27, 0x0000 }, + { 0x3d28, 0x0000 }, + { 0x3d29, 0x0000 }, + { 0x3d2a, 0x0000 }, + { 0x3d2b, 0x0000 }, + { 0x3d2c, 0x0000 }, + { 0x3d2d, 0x0000 }, + { 0x3d2e, 0x0000 }, + { 0x3d2f, 0x0000 }, + { 0x3d30, 0x0000 }, + { 0x3d31, 0x0000 }, + { 0x3d32, 0x0000 }, + { 0x3d33, 0x0000 }, + { 0x3d34, 0x0000 }, + { 0x3d35, 0x0000 }, + { 0x3d36, 0x0000 }, + { 0x3d37, 0x0000 }, + { 0x3d38, 0x0000 }, + { 0x3d39, 0x0000 }, + { 0x3d3a, 0x0000 }, + { 0x3d3b, 0x0000 }, + { 0x3d3c, 0x0000 }, + { 0x3d3d, 0x0000 }, + { 0x3d3e, 0x0000 }, + { 0x3d3f, 0x0000 }, + { 0x3d40, 0x0000 }, + { 0x3d41, 0x0000 }, + { 0x3d42, 0x0000 }, + { 0x3d43, 0x0000 }, + { 0x3d44, 0x0000 }, + { 0x3d45, 0x0000 }, + { 0x3d46, 0x0000 }, + { 0x3d47, 0x0000 }, + { 0x3d48, 0x0000 }, + { 0x3d49, 0x0000 }, + { 0x3d4a, 0x0000 }, + { 0x3d4b, 0x0000 }, + { 0x3d4c, 0x0000 }, + { 0x3d4d, 0x0000 }, + { 0x3d4e, 0x0000 }, + { 0x3d4f, 0x0000 }, + { 0x3d50, 0x0000 }, + { 0x3d51, 0x0000 }, + { 0x3d52, 0x0000 }, + { 0x3d53, 0x0000 }, + { 0x3d54, 0x0000 }, + { 0x3d55, 0x0000 }, + { 0x3d56, 0x0000 }, + { 0x3d57, 0x0000 }, + { 0x3d58, 0x0000 }, + { 0x3d59, 0x0000 }, + { 0x3d5a, 0x0000 }, + { 0x3d5b, 0x0000 }, + { 0x3d5c, 0x0000 }, + { 0x3d5d, 0x0000 }, + { 0x3d5e, 0x0000 }, + { 0x3d5f, 0x0000 }, + { 0x3d60, 0x0000 }, + { 0x3d61, 0x0000 }, + { 0x3d62, 0x0000 }, + { 0x3d63, 0x0000 }, + { 0x3d64, 0x0000 }, + { 0x3d65, 0x0000 }, + { 0x3d66, 0x0000 }, + { 0x3d67, 0x0000 }, + { 0x3d68, 0x0000 }, + { 0x3d69, 0x0000 }, + { 0x3d6a, 0x0000 }, + { 0x3d6b, 0x0000 }, + { 0x3d6c, 0x0000 }, + { 0x3d6d, 0x0000 }, + { 0x3d6e, 0x0000 }, + { 0x3d6f, 0x0000 }, + { 0x3d70, 0x0000 }, + { 0x3d71, 0x0000 }, + { 0x3d72, 0x0000 }, + { 0x3d73, 0x0000 }, + { 0x3d74, 0x0000 }, + { 0x3d75, 0x0000 }, + { 0x3d76, 0x0000 }, + { 0x3d77, 0x0000 }, + { 0x3d78, 0x0000 }, + { 0x3d79, 0x0000 }, + { 0x3d7a, 0x0000 }, + { 0x3d7b, 0x0000 }, + { 0x3d7c, 0x0000 }, + { 0x3d7d, 0x0000 }, + { 0x3d7e, 0x0000 }, + { 0x3d7f, 0x0000 }, + { 0x3d80, 0x0000 }, + { 0x3d81, 0x0000 }, + { 0x3d82, 0x0000 }, + { 0x3d83, 0x0000 }, + { 0x3d84, 0x0000 }, + { 0x3d85, 0x0000 }, + { 0x3d86, 0x0000 }, + { 0x3d87, 0x0000 }, + { 0x3d88, 0x0000 }, + { 0x3d89, 0x0000 }, + { 0x3d8a, 0x0000 }, + { 0x3d8b, 0x0000 }, + { 0x3d8c, 0x0000 }, + { 0x3d8d, 0x0000 }, + { 0x3d8e, 0x0000 }, + { 0x3d8f, 0x0000 }, + { 0x3d90, 0x0000 }, + { 0x3d91, 0x0000 }, + { 0x3d92, 0x0000 }, + { 0x3d93, 0x0000 }, + { 0x3d94, 0x0000 }, + { 0x3d95, 0x0000 }, + { 0x3d96, 0x0000 }, + { 0x3d97, 0x0000 }, + { 0x3d98, 0x0000 }, + { 0x3d99, 0x0000 }, + { 0x3d9a, 0x0000 }, + { 0x3d9b, 0x0000 }, + { 0x3d9c, 0x0000 }, + { 0x3d9d, 0x0000 }, + { 0x3d9e, 0x0000 }, + { 0x3d9f, 0x0000 }, + { 0x3da0, 0x0000 }, + { 0x3da1, 0x0000 }, + { 0x3da2, 0x0000 }, + { 0x3da3, 0x0000 }, + { 0x3da4, 0x0000 }, + { 0x3da5, 0x0000 }, + { 0x3da6, 0x0000 }, + { 0x3da7, 0x0000 }, + { 0x3da8, 0x0000 }, + { 0x3da9, 0x0000 }, + { 0x3daa, 0x0000 }, + { 0x3dab, 0x0000 }, + { 0x3dac, 0x0000 }, + { 0x3dad, 0x0000 }, + { 0x3dae, 0x0000 }, + { 0x3daf, 0x0000 }, + { 0x3db0, 0x0000 }, + { 0x3db1, 0x0000 }, + { 0x3db2, 0x0000 }, + { 0x3db3, 0x0000 }, + { 0x3db4, 0x0000 }, + { 0x3db5, 0x0000 }, + { 0x3db6, 0x0000 }, + { 0x3db7, 0x0000 }, + { 0x3db8, 0x0000 }, + { 0x3db9, 0x0000 }, + { 0x3dba, 0x0000 }, + { 0x3dbb, 0x0000 }, + { 0x3dbc, 0x0000 }, + { 0x3dbd, 0x0000 }, + { 0x3dbe, 0x0000 }, + { 0x3dbf, 0x0000 }, + { 0x3dc0, 0x0000 }, + { 0x3dc1, 0x0000 }, + { 0x3dc2, 0x0000 }, + { 0x3dc3, 0x0000 }, + { 0x3dc4, 0x0000 }, + { 0x3dc5, 0x0000 }, + { 0x3dc6, 0x0000 }, + { 0x3dc7, 0x0000 }, + { 0x3dc8, 0x0000 }, + { 0x3dc9, 0x0000 }, + { 0x3dca, 0x0000 }, + { 0x3dcb, 0x0000 }, + { 0x3dcc, 0x0000 }, + { 0x3dcd, 0x0000 }, + { 0x3dce, 0x0000 }, + { 0x3dcf, 0x0000 }, + { 0x3dd0, 0x0000 }, + { 0x3dd1, 0x0000 }, + { 0x3dd2, 0x0000 }, + { 0x3dd3, 0x0000 }, + { 0x3dd4, 0x0000 }, + { 0x3dd5, 0x0000 }, + { 0x3dd6, 0x0000 }, + { 0x3dd7, 0x0000 }, + { 0x3dd8, 0x0000 }, + { 0x3dd9, 0x0000 }, + { 0x3dda, 0x0000 }, + { 0x3ddb, 0x0000 }, + { 0x3ddc, 0x0000 }, + { 0x3ddd, 0x0000 }, + { 0x3dde, 0x0000 }, + { 0x3ddf, 0x0000 }, + { 0x3de0, 0x0000 }, + { 0x3de1, 0x0000 }, + { 0x3de2, 0x0000 }, + { 0x3de3, 0x0000 }, + { 0x3de4, 0x0000 }, + { 0x3de5, 0x0000 }, + { 0x3de6, 0x0000 }, + { 0x3de7, 0x0000 }, + { 0x3de8, 0x0000 }, + { 0x3de9, 0x0000 }, + { 0x3dea, 0x0000 }, + { 0x3deb, 0x0000 }, + { 0x3dec, 0x0000 }, + { 0x3ded, 0x0000 }, + { 0x3dee, 0x0000 }, + { 0x3def, 0x0000 }, + { 0x3df0, 0x0000 }, + { 0x3df1, 0x0000 }, + { 0x3df2, 0x0000 }, + { 0x3df3, 0x0000 }, + { 0x3df4, 0x0000 }, + { 0x3df5, 0x0000 }, + { 0x3df6, 0x0000 }, + { 0x3df7, 0x0000 }, + { 0x3df8, 0x0000 }, + { 0x3df9, 0x0000 }, + { 0x3dfa, 0x0000 }, + { 0x3dfb, 0x0000 }, + { 0x3dfc, 0x0000 }, + { 0x3dfd, 0x0000 }, + { 0x3dfe, 0x0000 }, + { 0x3dff, 0x0000 }, + { 0x3e00, 0x0000 }, + { 0x3e01, 0x0000 }, + { 0x3e02, 0x0000 }, + { 0x3e03, 0x0000 }, + { 0x3e04, 0x0000 }, + { 0x3e05, 0x0000 }, + { 0x3e06, 0x0000 }, + { 0x3e07, 0x0000 }, + { 0x3e08, 0x0000 }, + { 0x3e09, 0x0000 }, + { 0x3e0a, 0x0000 }, + { 0x3e0b, 0x0000 }, + { 0x3e0c, 0x0000 }, + { 0x3e0d, 0x0000 }, + { 0x3e0e, 0x0000 }, + { 0x3e0f, 0x0000 }, + { 0x3e10, 0x0000 }, + { 0x3e11, 0x0000 }, + { 0x3e12, 0x0000 }, + { 0x3e13, 0x0000 }, + { 0x3e14, 0x0000 }, + { 0x3e15, 0x0000 }, + { 0x3e16, 0x0000 }, + { 0x3e17, 0x0000 }, + { 0x3e18, 0x0000 }, + { 0x3e19, 0x0000 }, + { 0x3e1a, 0x0000 }, + { 0x3e1b, 0x0000 }, + { 0x3e1c, 0x0000 }, + { 0x3e1d, 0x0000 }, + { 0x3e1e, 0x0000 }, + { 0x3e1f, 0x0000 }, + { 0x3e20, 0x0000 }, + { 0x3e21, 0x0000 }, + { 0x3e22, 0x0000 }, + { 0x3e23, 0x0000 }, + { 0x3e24, 0x0000 }, + { 0x3e25, 0x0000 }, + { 0x3e26, 0x0000 }, + { 0x3e27, 0x0000 }, + { 0x3e28, 0x0000 }, + { 0x3e29, 0x0000 }, + { 0x3e2a, 0x0000 }, + { 0x3e2b, 0x0000 }, + { 0x3e2c, 0x0000 }, + { 0x3e2d, 0x0000 }, + { 0x3e2e, 0x0000 }, + { 0x3e2f, 0x0000 }, + { 0x3e30, 0x0000 }, + { 0x3e31, 0x0000 }, + { 0x3e32, 0x0000 }, + { 0x3e33, 0x0000 }, + { 0x3e34, 0x0000 }, + { 0x3e35, 0x0000 }, + { 0x3e36, 0x0000 }, + { 0x3e37, 0x0000 }, + { 0x3e38, 0x0000 }, + { 0x3e39, 0x0000 }, + { 0x3e3a, 0x0000 }, + { 0x3e3b, 0x0000 }, + { 0x3e3c, 0x0000 }, + { 0x3e3d, 0x0000 }, + { 0x3e3e, 0x0000 }, + { 0x3e3f, 0x0000 }, + { 0x3e40, 0x0000 }, + { 0x3e41, 0x0000 }, + { 0x3e42, 0x0000 }, + { 0x3e43, 0x0000 }, + { 0x3e44, 0x0000 }, + { 0x3e45, 0x0000 }, + { 0x3e46, 0x0000 }, + { 0x3e47, 0x0000 }, + { 0x3e48, 0x0000 }, + { 0x3e49, 0x0000 }, + { 0x3e4a, 0x0000 }, + { 0x3e4b, 0x0000 }, + { 0x3e4c, 0x0000 }, + { 0x3e4d, 0x0000 }, + { 0x3e4e, 0x0000 }, + { 0x3e4f, 0x0000 }, + { 0x3e50, 0x0000 }, + { 0x3e51, 0x0000 }, + { 0x3e52, 0x0000 }, + { 0x3e53, 0x0000 }, + { 0x3e54, 0x0000 }, + { 0x3e55, 0x0000 }, + { 0x3e56, 0x0000 }, + { 0x3e57, 0x0000 }, + { 0x3e58, 0x0000 }, + { 0x3e59, 0x0000 }, + { 0x3e5a, 0x0000 }, + { 0x3e5b, 0x0000 }, + { 0x3e5c, 0x0000 }, + { 0x3e5d, 0x0000 }, + { 0x3e5e, 0x0000 }, + { 0x3e5f, 0x0000 }, + { 0x3e60, 0x0000 }, + { 0x3e61, 0x0000 }, + { 0x3e62, 0x0000 }, + { 0x3e63, 0x0000 }, + { 0x3e64, 0x0000 }, + { 0x3e65, 0x0000 }, + { 0x3e66, 0x0000 }, + { 0x3e67, 0x0000 }, + { 0x3e68, 0x0000 }, + { 0x3e69, 0x0000 }, + { 0x3e6a, 0x0000 }, + { 0x3e6b, 0x0000 }, + { 0x3e6c, 0x0000 }, + { 0x3e6d, 0x0000 }, + { 0x3e6e, 0x0000 }, + { 0x3e6f, 0x0000 }, + { 0x3e70, 0x0000 }, + { 0x3e71, 0x0000 }, + { 0x3e72, 0x0000 }, + { 0x3e73, 0x0000 }, + { 0x3e74, 0x0000 }, + { 0x3e75, 0x0000 }, + { 0x3e76, 0x0000 }, + { 0x3e77, 0x0000 }, + { 0x3e78, 0x0000 }, + { 0x3e79, 0x0000 }, + { 0x3e7a, 0x0000 }, + { 0x3e7b, 0x0000 }, + { 0x3e7c, 0x0000 }, + { 0x3e7d, 0x0000 }, + { 0x3e7e, 0x0000 }, + { 0x3e7f, 0x0000 }, + { 0x3e80, 0x0000 }, + { 0x3e81, 0x0000 }, + { 0x3e82, 0x0000 }, + { 0x3e83, 0x0000 }, + { 0x3e84, 0x0000 }, + { 0x3e85, 0x0000 }, + { 0x3e86, 0x0000 }, + { 0x3e87, 0x0000 }, + { 0x3e88, 0x0000 }, + { 0x3e89, 0x0000 }, + { 0x3e8a, 0x0000 }, + { 0x3e8b, 0x0000 }, + { 0x3e8c, 0x0000 }, + { 0x3e8d, 0x0000 }, + { 0x3e8e, 0x0000 }, + { 0x3e8f, 0x0000 }, + { 0x3e90, 0x0000 }, + { 0x3e91, 0x0000 }, + { 0x3e92, 0x0000 }, + { 0x3e93, 0x0000 }, + { 0x3e94, 0x0000 }, + { 0x3e95, 0x0000 }, + { 0x3e96, 0x0000 }, + { 0x3e97, 0x0000 }, + { 0x3e98, 0x0000 }, + { 0x3e99, 0x0000 }, + { 0x3e9a, 0x0000 }, + { 0x3e9b, 0x0000 }, + { 0x3e9c, 0x0000 }, + { 0x3e9d, 0x0000 }, + { 0x3e9e, 0x0000 }, + { 0x3e9f, 0x0000 }, + { 0x3ea0, 0x0000 }, + { 0x3ea1, 0x0000 }, + { 0x3ea2, 0x0000 }, + { 0x3ea3, 0x0000 }, + { 0x3ea4, 0x0000 }, + { 0x3ea5, 0x0000 }, + { 0x3ea6, 0x0000 }, + { 0x3ea7, 0x0000 }, + { 0x3ea8, 0x0000 }, + { 0x3ea9, 0x0000 }, + { 0x3eaa, 0x0000 }, + { 0x3eab, 0x0000 }, + { 0x3eac, 0x0000 }, + { 0x3ead, 0x0000 }, + { 0x3eae, 0x0000 }, + { 0x3eaf, 0x0000 }, + { 0x3eb0, 0x0000 }, + { 0x3eb1, 0x0000 }, + { 0x3eb2, 0x0000 }, + { 0x3eb3, 0x0000 }, + { 0x3eb4, 0x0000 }, + { 0x3eb5, 0x0000 }, + { 0x3eb6, 0x0000 }, + { 0x3eb7, 0x0000 }, + { 0x3eb8, 0x0000 }, + { 0x3eb9, 0x0000 }, + { 0x3eba, 0x0000 }, + { 0x3ebb, 0x0000 }, + { 0x3ebc, 0x0000 }, + { 0x3ebd, 0x0000 }, + { 0x3ebe, 0x0000 }, + { 0x3ebf, 0x0000 }, + { 0x3ec0, 0x0000 }, + { 0x3ec1, 0x0000 }, + { 0x3ec2, 0x0000 }, + { 0x3ec3, 0x0000 }, + { 0x3ec4, 0x0000 }, + { 0x3ec5, 0x0000 }, + { 0x3ec6, 0x0000 }, + { 0x3ec7, 0x0000 }, + { 0x3ec8, 0x0000 }, + { 0x3ec9, 0x0000 }, + { 0x3eca, 0x0000 }, + { 0x3ecb, 0x0000 }, + { 0x3ecc, 0x0000 }, + { 0x3ecd, 0x0000 }, + { 0x3ece, 0x0000 }, + { 0x3ecf, 0x0000 }, + { 0x3ed0, 0x0000 }, + { 0x3ed1, 0x0000 }, + { 0x3ed2, 0x0000 }, + { 0x3ed3, 0x0000 }, + { 0x3ed4, 0x0000 }, + { 0x3ed5, 0x0000 }, + { 0x3ed6, 0x0000 }, + { 0x3ed7, 0x0000 }, + { 0x3ed8, 0x0000 }, + { 0x3ed9, 0x0000 }, + { 0x3eda, 0x0000 }, + { 0x3edb, 0x0000 }, + { 0x3edc, 0x0000 }, + { 0x3edd, 0x0000 }, + { 0x3ede, 0x0000 }, + { 0x3edf, 0x0000 }, + { 0x3ee0, 0x0000 }, + { 0x3ee1, 0x0000 }, + { 0x3ee2, 0x0000 }, + { 0x3ee3, 0x0000 }, + { 0x3ee4, 0x0000 }, + { 0x3ee5, 0x0000 }, + { 0x3ee6, 0x0000 }, + { 0x3ee7, 0x0000 }, + { 0x3ee8, 0x0000 }, + { 0x3ee9, 0x0000 }, + { 0x3eea, 0x0000 }, + { 0x3eeb, 0x0000 }, + { 0x3eec, 0x0000 }, + { 0x3eed, 0x0000 }, + { 0x3eee, 0x0000 }, + { 0x3eef, 0x0000 }, + { 0x3ef0, 0x0000 }, + { 0x3ef1, 0x0000 }, + { 0x3ef2, 0x0000 }, + { 0x3ef3, 0x0000 }, + { 0x3ef4, 0x0000 }, + { 0x3ef5, 0x0000 }, + { 0x3ef6, 0x0000 }, + { 0x3ef7, 0x0000 }, + { 0x3ef8, 0x0000 }, + { 0x3ef9, 0x0000 }, + { 0x3efa, 0x0000 }, + { 0x3efb, 0x0000 }, + { 0x3efc, 0x0000 }, + { 0x3efd, 0x0000 }, + { 0x3efe, 0x0000 }, + { 0x3eff, 0x0000 }, + { 0x3f00, 0x0000 }, + { 0x3f01, 0x0000 }, + { 0x3f02, 0x0000 }, + { 0x3f03, 0x0000 }, + { 0x3f04, 0x0000 }, + { 0x3f05, 0x0000 }, + { 0x3f06, 0x0000 }, + { 0x3f07, 0x0000 }, + { 0x3f08, 0x0000 }, + { 0x3f09, 0x0000 }, + { 0x3f0a, 0x0000 }, + { 0x3f0b, 0x0000 }, + { 0x3f0c, 0x0000 }, + { 0x3f0d, 0x0000 }, + { 0x3f0e, 0x0000 }, + { 0x3f0f, 0x0000 }, + { 0x3f10, 0x0000 }, + { 0x3f11, 0x0000 }, + { 0x3f12, 0x0000 }, + { 0x3f13, 0x0000 }, + { 0x3f14, 0x0000 }, + { 0x3f15, 0x0000 }, + { 0x3f16, 0x0000 }, + { 0x3f17, 0x0000 }, + { 0x3f18, 0x0000 }, + { 0x3f19, 0x0000 }, + { 0x3f1a, 0x0000 }, + { 0x3f1b, 0x0000 }, + { 0x3f1c, 0x0000 }, + { 0x3f1d, 0x0000 }, + { 0x3f1e, 0x0000 }, + { 0x3f1f, 0x0000 }, + { 0x3f20, 0x0000 }, + { 0x3f21, 0x0000 }, + { 0x3f22, 0x0000 }, + { 0x3f23, 0x0000 }, + { 0x3f24, 0x0000 }, + { 0x3f25, 0x0000 }, + { 0x3f26, 0x0000 }, + { 0x3f27, 0x0000 }, + { 0x3f28, 0x0000 }, + { 0x3f29, 0x0000 }, + { 0x3f2a, 0x0000 }, + { 0x3f2b, 0x0000 }, + { 0x3f2c, 0x0000 }, + { 0x3f2d, 0x0000 }, + { 0x3f2e, 0x0000 }, + { 0x3f2f, 0x0000 }, + { 0x3f30, 0x0000 }, + { 0x3f31, 0x0000 }, + { 0x3f32, 0x0000 }, + { 0x3f33, 0x0000 }, + { 0x3f34, 0x0000 }, + { 0x3f35, 0x0000 }, + { 0x3f36, 0x0000 }, + { 0x3f37, 0x0000 }, + { 0x3f38, 0x0000 }, + { 0x3f39, 0x0000 }, + { 0x3f3a, 0x0000 }, + { 0x3f3b, 0x0000 }, + { 0x3f3c, 0x0000 }, + { 0x3f3d, 0x0000 }, + { 0x3f3e, 0x0000 }, + { 0x3f3f, 0x0000 }, + { 0x3f40, 0x0000 }, + { 0x3f41, 0x0000 }, + { 0x3f42, 0x0000 }, + { 0x3f43, 0x0000 }, + { 0x3f44, 0x0000 }, + { 0x3f45, 0x0000 }, + { 0x3f46, 0x0000 }, + { 0x3f47, 0x0000 }, + { 0x3f48, 0x0000 }, + { 0x3f49, 0x0000 }, + { 0x3f4a, 0x0000 }, + { 0x3f4b, 0x0000 }, + { 0x3f4c, 0x0000 }, + { 0x3f4d, 0x0000 }, + { 0x3f4e, 0x0000 }, + { 0x3f4f, 0x0000 }, + { 0x3f50, 0x0000 }, + { 0x3f51, 0x0000 }, + { 0x3f52, 0x0000 }, + { 0x3f53, 0x0000 }, + { 0x3f54, 0x0000 }, + { 0x3f55, 0x0000 }, + { 0x3f56, 0x0000 }, + { 0x3f57, 0x0000 }, + { 0x3f58, 0x0000 }, + { 0x3f59, 0x0000 }, + { 0x3f5a, 0x0000 }, + { 0x3f5b, 0x0000 }, + { 0x3f5c, 0x0000 }, + { 0x3f5d, 0x0000 }, + { 0x3f5e, 0x0000 }, + { 0x3f5f, 0x0000 }, + { 0x3f60, 0x0000 }, + { 0x3f61, 0x0000 }, + { 0x3f62, 0x0000 }, + { 0x3f63, 0x0000 }, + { 0x3f64, 0x0000 }, + { 0x3f65, 0x0000 }, + { 0x3f66, 0x0000 }, + { 0x3f67, 0x0000 }, + { 0x3f68, 0x0000 }, + { 0x3f69, 0x0000 }, + { 0x3f6a, 0x0000 }, + { 0x3f6b, 0x0000 }, + { 0x3f6c, 0x0000 }, + { 0x3f6d, 0x0000 }, + { 0x3f6e, 0x0000 }, + { 0x3f6f, 0x0000 }, + { 0x3f70, 0x0000 }, + { 0x3f71, 0x0000 }, + { 0x3f72, 0x0000 }, + { 0x3f73, 0x0000 }, + { 0x3f74, 0x0000 }, + { 0x3f75, 0x0000 }, + { 0x3f76, 0x0000 }, + { 0x3f77, 0x0000 }, + { 0x3f78, 0x0000 }, + { 0x3f79, 0x0000 }, + { 0x3f7a, 0x0000 }, + { 0x3f7b, 0x0000 }, + { 0x3f7c, 0x0000 }, + { 0x3f7d, 0x0000 }, + { 0x3f7e, 0x0000 }, + { 0x3f7f, 0x0000 }, + { 0x3f80, 0x0000 }, + { 0x3f81, 0x0000 }, + { 0x3f82, 0x0000 }, + { 0x3f83, 0x0000 }, + { 0x3f84, 0x0000 }, + { 0x3f85, 0x0000 }, + { 0x3f86, 0x0000 }, + { 0x3f87, 0x0000 }, + { 0x3f88, 0x0000 }, + { 0x3f89, 0x0000 }, + { 0x3f8a, 0x0000 }, + { 0x3f8b, 0x0000 }, + { 0x3f8c, 0x0000 }, + { 0x3f8d, 0x0000 }, + { 0x3f8e, 0x0000 }, + { 0x3f8f, 0x0000 }, + { 0x3f90, 0x0000 }, + { 0x3f91, 0x0000 }, + { 0x3f92, 0x0000 }, + { 0x3f93, 0x0000 }, + { 0x3f94, 0x0000 }, + { 0x3f95, 0x0000 }, + { 0x3f96, 0x0000 }, + { 0x3f97, 0x0000 }, + { 0x3f98, 0x0000 }, + { 0x3f99, 0x0000 }, + { 0x3f9a, 0x0000 }, + { 0x3f9b, 0x0000 }, + { 0x3f9c, 0x0000 }, + { 0x3f9d, 0x0000 }, + { 0x3f9e, 0x0000 }, + { 0x3f9f, 0x0000 }, + { 0x3fa0, 0x0000 }, + { 0x3fa1, 0x0000 }, + { 0x3fa2, 0x0000 }, + { 0x3fa3, 0x0000 }, + { 0x3fa4, 0x0000 }, + { 0x3fa5, 0x0000 }, + { 0x3fa6, 0x0000 }, + { 0x3fa7, 0x0000 }, + { 0x3fa8, 0x0000 }, + { 0x3fa9, 0x0000 }, + { 0x3faa, 0x0000 }, + { 0x3fab, 0x0000 }, + { 0x3fac, 0x0000 }, + { 0x3fad, 0x0000 }, + { 0x3fae, 0x0000 }, + { 0x3faf, 0x0000 }, + { 0x3fb0, 0x0000 }, + { 0x3fb1, 0x0000 }, + { 0x3fb2, 0x0000 }, + { 0x3fb3, 0x0000 }, + { 0x3fb4, 0x0000 }, + { 0x3fb5, 0x0000 }, + { 0x3fb6, 0x0000 }, + { 0x3fb7, 0x0000 }, + { 0x3fb8, 0x0000 }, + { 0x3fb9, 0x0000 }, + { 0x3fba, 0x0000 }, + { 0x3fbb, 0x0000 }, + { 0x3fbc, 0x0000 }, + { 0x3fbd, 0x0000 }, + { 0x3fbe, 0x0000 }, + { 0x3fbf, 0x0000 }, + { 0x3fc0, 0x0000 }, + { 0x3fc1, 0x0000 }, + { 0x3fc2, 0x0000 }, + { 0x3fc3, 0x0000 }, + { 0x3fc4, 0x0000 }, + { 0x3fc5, 0x0000 }, + { 0x3fc6, 0x0000 }, + { 0x3fc7, 0x0000 }, + { 0x3fc8, 0x0000 }, + { 0x3fc9, 0x0000 }, + { 0x3fca, 0x0000 }, + { 0x3fcb, 0x0000 }, + { 0x3fcc, 0x0000 }, + { 0x3fcd, 0x0000 }, + { 0x3fce, 0x0000 }, + { 0x3fcf, 0x0000 }, + { 0x3fd0, 0x0000 }, + { 0x3fd1, 0x0000 }, + { 0x3fd2, 0x0000 }, + { 0x3fd3, 0x0000 }, + { 0x3fd4, 0x0000 }, + { 0x3fd5, 0x0000 }, + { 0x3fd6, 0x0000 }, + { 0x3fd7, 0x0000 }, + { 0x3fd8, 0x0000 }, + { 0x3fd9, 0x0000 }, + { 0x3fda, 0x0000 }, + { 0x3fdb, 0x0000 }, + { 0x3fdc, 0x0000 }, + { 0x3fdd, 0x0000 }, + { 0x3fde, 0x0000 }, + { 0x3fdf, 0x0000 }, + { 0x3fe0, 0x0000 }, + { 0x3fe1, 0x0000 }, + { 0x3fe2, 0x0000 }, + { 0x3fe3, 0x0000 }, + { 0x3fe4, 0x0000 }, + { 0x3fe5, 0x0000 }, + { 0x3fe6, 0x0000 }, + { 0x3fe7, 0x0000 }, + { 0x3fe8, 0x0000 }, + { 0x3fe9, 0x0000 }, + { 0x3fea, 0x0000 }, + { 0x3feb, 0x0000 }, + { 0x3fec, 0x0000 }, + { 0x3fed, 0x0000 }, + { 0x3fee, 0x0000 }, + { 0x3fef, 0x0000 }, + { 0x3ff0, 0x0000 }, + { 0x3ff1, 0x0000 }, + { 0x3ff2, 0x0000 }, + { 0x3ff3, 0x0000 }, + { 0x3ff4, 0x0000 }, + { 0x3ff5, 0x0000 }, + { 0x3ff6, 0x0000 }, + { 0x3ff7, 0x0000 }, + { 0x3ff8, 0x0000 }, + { 0x3ff9, 0x0000 }, + { 0x3ffa, 0x0000 }, + { 0x3ffb, 0x0000 }, + { 0x3ffc, 0x0000 }, + { 0x3ffd, 0x0000 }, + { 0x3ffe, 0x0000 }, + { 0x3fff, 0x0000 }, + { 0x4000, 0x0000 }, + { 0x4001, 0x0000 }, + { 0x4002, 0x0000 }, + { 0x4003, 0x0000 }, + { 0x4004, 0x0000 }, + { 0x4005, 0x0000 }, + { 0x4006, 0x0000 }, + { 0x4007, 0x0000 }, + { 0x4008, 0x0000 }, + { 0x4009, 0x0000 }, + { 0x400a, 0x0000 }, + { 0x400b, 0x0000 }, + { 0x400c, 0x0000 }, + { 0x400d, 0x0000 }, + { 0x400e, 0x0000 }, + { 0x400f, 0x0000 }, + { 0x4010, 0x0000 }, + { 0x4011, 0x0000 }, + { 0x4012, 0x0000 }, + { 0x4013, 0x0000 }, + { 0x4014, 0x0000 }, + { 0x4015, 0x0000 }, + { 0x4016, 0x0000 }, + { 0x4017, 0x0000 }, + { 0x4018, 0x0000 }, + { 0x4019, 0x0000 }, + { 0x401a, 0x0000 }, + { 0x401b, 0x0000 }, + { 0x401c, 0x0000 }, + { 0x401d, 0x0000 }, + { 0x401e, 0x0000 }, + { 0x401f, 0x0000 }, + { 0x4020, 0x0000 }, + { 0x4021, 0x0000 }, + { 0x4022, 0x0000 }, + { 0x4023, 0x0000 }, + { 0x4024, 0x0000 }, + { 0x4025, 0x0000 }, + { 0x4026, 0x0000 }, + { 0x4027, 0x0000 }, + { 0x4028, 0x0000 }, + { 0x4029, 0x0000 }, + { 0x402a, 0x0000 }, + { 0x402b, 0x0000 }, + { 0x402c, 0x0000 }, + { 0x402d, 0x0000 }, + { 0x402e, 0x0000 }, + { 0x402f, 0x0000 }, + { 0x4030, 0x0000 }, + { 0x4031, 0x0000 }, + { 0x4032, 0x0000 }, + { 0x4033, 0x0000 }, + { 0x4034, 0x0000 }, + { 0x4035, 0x0000 }, + { 0x4036, 0x0000 }, + { 0x4037, 0x0000 }, + { 0x4038, 0x0000 }, + { 0x4039, 0x0000 }, + { 0x403a, 0x0000 }, + { 0x403b, 0x0000 }, + { 0x403c, 0x0000 }, + { 0x403d, 0x0000 }, + { 0x403e, 0x0000 }, + { 0x403f, 0x0000 }, + { 0x4040, 0x0000 }, + { 0x4041, 0x0000 }, + { 0x4042, 0x0000 }, + { 0x4043, 0x0000 }, + { 0x4044, 0x0000 }, + { 0x4045, 0x0000 }, + { 0x4046, 0x0000 }, + { 0x4047, 0x0000 }, + { 0x4048, 0x0000 }, + { 0x4049, 0x0000 }, + { 0x404a, 0x0000 }, + { 0x404b, 0x0000 }, + { 0x404c, 0x0000 }, + { 0x404d, 0x0000 }, + { 0x404e, 0x0000 }, + { 0x404f, 0x0000 }, + { 0x4050, 0x0000 }, + { 0x4051, 0x0000 }, + { 0x4052, 0x0000 }, + { 0x4053, 0x0000 }, + { 0x4054, 0x0000 }, + { 0x4055, 0x0000 }, + { 0x4056, 0x0000 }, + { 0x4057, 0x0000 }, + { 0x4058, 0x0000 }, + { 0x4059, 0x0000 }, + { 0x405a, 0x0000 }, + { 0x405b, 0x0000 }, + { 0x405c, 0x0000 }, + { 0x405d, 0x0000 }, + { 0x405e, 0x0000 }, + { 0x405f, 0x0000 }, + { 0x4060, 0x0000 }, + { 0x4061, 0x0000 }, + { 0x4062, 0x0000 }, + { 0x4063, 0x0000 }, + { 0x4064, 0x0000 }, + { 0x4065, 0x0000 }, + { 0x4066, 0x0000 }, + { 0x4067, 0x0000 }, + { 0x4068, 0x0000 }, + { 0x4069, 0x0000 }, + { 0x406a, 0x0000 }, + { 0x406b, 0x0000 }, + { 0x406c, 0x0000 }, + { 0x406d, 0x0000 }, + { 0x406e, 0x0000 }, + { 0x406f, 0x0000 }, + { 0x4070, 0x0000 }, + { 0x4071, 0x0000 }, + { 0x4072, 0x0000 }, + { 0x4073, 0x0000 }, + { 0x4074, 0x0000 }, + { 0x4075, 0x0000 }, + { 0x4076, 0x0000 }, + { 0x4077, 0x0000 }, + { 0x4078, 0x0000 }, + { 0x4079, 0x0000 }, + { 0x407a, 0x0000 }, + { 0x407b, 0x0000 }, + { 0x407c, 0x0000 }, + { 0x407d, 0x0000 }, + { 0x407e, 0x0000 }, + { 0x407f, 0x0000 }, + { 0x4080, 0x0000 }, + { 0x4081, 0x0000 }, + { 0x4082, 0x0000 }, + { 0x4083, 0x0000 }, + { 0x4084, 0x0000 }, + { 0x4085, 0x0000 }, + { 0x4086, 0x0000 }, + { 0x4087, 0x0000 }, + { 0x4088, 0x0000 }, + { 0x4089, 0x0000 }, + { 0x408a, 0x0000 }, + { 0x408b, 0x0000 }, + { 0x408c, 0x0000 }, + { 0x408d, 0x0000 }, + { 0x408e, 0x0000 }, + { 0x408f, 0x0000 }, + { 0x4090, 0x0000 }, + { 0x4091, 0x0000 }, + { 0x4092, 0x0000 }, + { 0x4093, 0x0000 }, + { 0x4094, 0x0000 }, + { 0x4095, 0x0000 }, + { 0x4096, 0x0000 }, + { 0x4097, 0x0000 }, + { 0x4098, 0x0000 }, + { 0x4099, 0x0000 }, + { 0x409a, 0x0000 }, + { 0x409b, 0x0000 }, + { 0x409c, 0x0000 }, + { 0x409d, 0x0000 }, + { 0x409e, 0x0000 }, + { 0x409f, 0x0000 }, + { 0x40a0, 0x0000 }, + { 0x40a1, 0x0000 }, + { 0x40a2, 0x0000 }, + { 0x40a3, 0x0000 }, + { 0x40a4, 0x0000 }, + { 0x40a5, 0x0000 }, + { 0x40a6, 0x0000 }, + { 0x40a7, 0x0000 }, + { 0x40a8, 0x0000 }, + { 0x40a9, 0x0000 }, + { 0x40aa, 0x0000 }, + { 0x40ab, 0x0000 }, + { 0x40ac, 0x0000 }, + { 0x40ad, 0x0000 }, + { 0x40ae, 0x0000 }, + { 0x40af, 0x0000 }, + { 0x40b0, 0x0000 }, + { 0x40b1, 0x0000 }, + { 0x40b2, 0x0000 }, + { 0x40b3, 0x0000 }, + { 0x40b4, 0x0000 }, + { 0x40b5, 0x0000 }, + { 0x40b6, 0x0000 }, + { 0x40b7, 0x0000 }, + { 0x40b8, 0x0000 }, + { 0x40b9, 0x0000 }, + { 0x40ba, 0x0000 }, + { 0x40bb, 0x0000 }, + { 0x40bc, 0x0000 }, + { 0x40bd, 0x0000 }, + { 0x40be, 0x0000 }, + { 0x40bf, 0x0000 }, + { 0x40c0, 0x0000 }, + { 0x40c1, 0x0000 }, + { 0x40c2, 0x0000 }, + { 0x40c3, 0x0000 }, + { 0x40c4, 0x0000 }, + { 0x40c5, 0x0000 }, + { 0x40c6, 0x0000 }, + { 0x40c7, 0x0000 }, + { 0x40c8, 0x0000 }, + { 0x40c9, 0x0000 }, + { 0x40ca, 0x0000 }, + { 0x40cb, 0x0000 }, + { 0x40cc, 0x0000 }, + { 0x40cd, 0x0000 }, + { 0x40ce, 0x0000 }, + { 0x40cf, 0x0000 }, + { 0x40d0, 0x0000 }, + { 0x40d1, 0x0000 }, + { 0x40d2, 0x0000 }, + { 0x40d3, 0x0000 }, + { 0x40d4, 0x0000 }, + { 0x40d5, 0x0000 }, + { 0x40d6, 0x0000 }, + { 0x40d7, 0x0000 }, + { 0x40d8, 0x0000 }, + { 0x40d9, 0x0000 }, + { 0x40da, 0x0000 }, + { 0x40db, 0x0000 }, + { 0x40dc, 0x0000 }, + { 0x40dd, 0x0000 }, + { 0x40de, 0x0000 }, + { 0x40df, 0x0000 }, + { 0x40e0, 0x0000 }, + { 0x40e1, 0x0000 }, + { 0x40e2, 0x0000 }, + { 0x40e3, 0x0000 }, + { 0x40e4, 0x0000 }, + { 0x40e5, 0x0000 }, + { 0x40e6, 0x0000 }, + { 0x40e7, 0x0000 }, + { 0x40e8, 0x0000 }, + { 0x40e9, 0x0000 }, + { 0x40ea, 0x0000 }, + { 0x40eb, 0x0000 }, + { 0x40ec, 0x0000 }, + { 0x40ed, 0x0000 }, + { 0x40ee, 0x0000 }, + { 0x40ef, 0x0000 }, + { 0x40f0, 0x0000 }, + { 0x40f1, 0x0000 }, + { 0x40f2, 0x0000 }, + { 0x40f3, 0x0000 }, + { 0x40f4, 0x0000 }, + { 0x40f5, 0x0000 }, + { 0x40f6, 0x0000 }, + { 0x40f7, 0x0000 }, + { 0x40f8, 0x0000 }, + { 0x40f9, 0x0000 }, + { 0x40fa, 0x0000 }, + { 0x40fb, 0x0000 }, + { 0x40fc, 0x0000 }, + { 0x40fd, 0x0000 }, + { 0x40fe, 0x0000 }, + { 0x40ff, 0x0000 }, + { 0x4100, 0x0000 }, + { 0x4101, 0x0000 }, + { 0x4102, 0x0000 }, + { 0x4103, 0x0000 }, + { 0x4104, 0x0000 }, + { 0x4105, 0x0000 }, + { 0x4106, 0x0000 }, + { 0x4107, 0x0000 }, + { 0x4108, 0x0000 }, + { 0x4109, 0x0000 }, + { 0x410a, 0x0000 }, + { 0x410b, 0x0000 }, + { 0x410c, 0x0000 }, + { 0x410d, 0x0000 }, + { 0x410e, 0x0000 }, + { 0x410f, 0x0000 }, + { 0x4110, 0x0000 }, + { 0x4111, 0x0000 }, + { 0x4112, 0x0000 }, + { 0x4113, 0x0000 }, + { 0x4114, 0x0000 }, + { 0x4115, 0x0000 }, + { 0x4116, 0x0000 }, + { 0x4117, 0x0000 }, + { 0x4118, 0x0000 }, + { 0x4119, 0x0000 }, + { 0x411a, 0x0000 }, + { 0x411b, 0x0000 }, + { 0x411c, 0x0000 }, + { 0x411d, 0x0000 }, + { 0x411e, 0x0000 }, + { 0x411f, 0x0000 }, + { 0x4120, 0x0000 }, + { 0x4121, 0x0000 }, + { 0x4122, 0x0000 }, + { 0x4123, 0x0000 }, + { 0x4124, 0x0000 }, + { 0x4125, 0x0000 }, + { 0x4126, 0x0000 }, + { 0x4127, 0x0000 }, + { 0x4128, 0x0000 }, + { 0x4129, 0x0000 }, + { 0x412a, 0x0000 }, + { 0x412b, 0x0000 }, + { 0x412c, 0x0000 }, + { 0x412d, 0x0000 }, + { 0x412e, 0x0000 }, + { 0x412f, 0x0000 }, + { 0x4130, 0x0000 }, + { 0x4131, 0x0000 }, + { 0x4132, 0x0000 }, + { 0x4133, 0x0000 }, + { 0x4134, 0x0000 }, + { 0x4135, 0x0000 }, + { 0x4136, 0x0000 }, + { 0x4137, 0x0000 }, + { 0x4138, 0x0000 }, + { 0x4139, 0x0000 }, + { 0x413a, 0x0000 }, + { 0x413b, 0x0000 }, + { 0x413c, 0x0000 }, + { 0x413d, 0x0000 }, + { 0x413e, 0x0000 }, + { 0x413f, 0x0000 }, + { 0x4140, 0x0000 }, + { 0x4141, 0x0000 }, + { 0x4142, 0x0000 }, + { 0x4143, 0x0000 }, + { 0x4144, 0x0000 }, + { 0x4145, 0x0000 }, + { 0x4146, 0x0000 }, + { 0x4147, 0x0000 }, + { 0x4148, 0x0000 }, + { 0x4149, 0x0000 }, + { 0x414a, 0x0000 }, + { 0x414b, 0x0000 }, + { 0x414c, 0x0000 }, + { 0x414d, 0x0000 }, + { 0x414e, 0x0000 }, + { 0x414f, 0x0000 }, + { 0x4150, 0x0000 }, + { 0x4151, 0x0000 }, + { 0x4152, 0x0000 }, + { 0x4153, 0x0000 }, + { 0x4154, 0x0000 }, + { 0x4155, 0x0000 }, + { 0x4156, 0x0000 }, + { 0x4157, 0x0000 }, + { 0x4158, 0x0000 }, + { 0x4159, 0x0000 }, + { 0x415a, 0x0000 }, + { 0x415b, 0x0000 }, + { 0x415c, 0x0000 }, + { 0x415d, 0x0000 }, + { 0x415e, 0x0000 }, + { 0x415f, 0x0000 }, + { 0x4160, 0x0000 }, + { 0x4161, 0x0000 }, + { 0x4162, 0x0000 }, + { 0x4163, 0x0000 }, + { 0x4164, 0x0000 }, + { 0x4165, 0x0000 }, + { 0x4166, 0x0000 }, + { 0x4167, 0x0000 }, + { 0x4168, 0x0000 }, + { 0x4169, 0x0000 }, + { 0x416a, 0x0000 }, + { 0x416b, 0x0000 }, + { 0x416c, 0x0000 }, + { 0x416d, 0x0000 }, + { 0x416e, 0x0000 }, + { 0x416f, 0x0000 }, + { 0x4170, 0x0000 }, + { 0x4171, 0x0000 }, + { 0x4172, 0x0000 }, + { 0x4173, 0x0000 }, + { 0x4174, 0x0000 }, + { 0x4175, 0x0000 }, + { 0x4176, 0x0000 }, + { 0x4177, 0x0000 }, + { 0x4178, 0x0000 }, + { 0x4179, 0x0000 }, + { 0x417a, 0x0000 }, + { 0x417b, 0x0000 }, + { 0x417c, 0x0000 }, + { 0x417d, 0x0000 }, + { 0x417e, 0x0000 }, + { 0x417f, 0x0000 }, + { 0x4180, 0x0000 }, + { 0x4181, 0x0000 }, + { 0x4182, 0x0000 }, + { 0x4183, 0x0000 }, + { 0x4184, 0x0000 }, + { 0x4185, 0x0000 }, + { 0x4186, 0x0000 }, + { 0x4187, 0x0000 }, + { 0x4188, 0x0000 }, + { 0x4189, 0x0000 }, + { 0x418a, 0x0000 }, + { 0x418b, 0x0000 }, + { 0x418c, 0x0000 }, + { 0x418d, 0x0000 }, + { 0x418e, 0x0000 }, + { 0x418f, 0x0000 }, + { 0x4190, 0x0000 }, + { 0x4191, 0x0000 }, + { 0x4192, 0x0000 }, + { 0x4193, 0x0000 }, + { 0x4194, 0x0000 }, + { 0x4195, 0x0000 }, + { 0x4196, 0x0000 }, + { 0x4197, 0x0000 }, + { 0x4198, 0x0000 }, + { 0x4199, 0x0000 }, + { 0x419a, 0x0000 }, + { 0x419b, 0x0000 }, + { 0x419c, 0x0000 }, + { 0x419d, 0x0000 }, + { 0x419e, 0x0000 }, + { 0x419f, 0x0000 }, + { 0x41a0, 0x0000 }, + { 0x41a1, 0x0000 }, + { 0x41a2, 0x0000 }, + { 0x41a3, 0x0000 }, + { 0x41a4, 0x0000 }, + { 0x41a5, 0x0000 }, + { 0x41a6, 0x0000 }, + { 0x41a7, 0x0000 }, + { 0x41a8, 0x0000 }, + { 0x41a9, 0x0000 }, + { 0x41aa, 0x0000 }, + { 0x41ab, 0x0000 }, + { 0x41ac, 0x0000 }, + { 0x41ad, 0x0000 }, + { 0x41ae, 0x0000 }, + { 0x41af, 0x0000 }, + { 0x41b0, 0x0000 }, + { 0x41b1, 0x0000 }, + { 0x41b2, 0x0000 }, + { 0x41b3, 0x0000 }, + { 0x41b4, 0x0000 }, + { 0x41b5, 0x0000 }, + { 0x41b6, 0x0000 }, + { 0x41b7, 0x0000 }, + { 0x41b8, 0x0000 }, + { 0x41b9, 0x0000 }, + { 0x41ba, 0x0000 }, + { 0x41bb, 0x0000 }, + { 0x41bc, 0x0000 }, + { 0x41bd, 0x0000 }, + { 0x41be, 0x0000 }, + { 0x41bf, 0x0000 }, + { 0x41c0, 0x0000 }, + { 0x41c1, 0x0000 }, + { 0x41c2, 0x0000 }, + { 0x41c3, 0x0000 }, + { 0x41c4, 0x0000 }, + { 0x41c5, 0x0000 }, + { 0x41c6, 0x0000 }, + { 0x41c7, 0x0000 }, + { 0x41c8, 0x0000 }, + { 0x41c9, 0x0000 }, + { 0x41ca, 0x0000 }, + { 0x41cb, 0x0000 }, + { 0x41cc, 0x0000 }, + { 0x41cd, 0x0000 }, + { 0x41ce, 0x0000 }, + { 0x41cf, 0x0000 }, + { 0x41d0, 0x0000 }, + { 0x41d1, 0x0000 }, + { 0x41d2, 0x0000 }, + { 0x41d3, 0x0000 }, + { 0x41d4, 0x0000 }, + { 0x41d5, 0x0000 }, + { 0x41d6, 0x0000 }, + { 0x41d7, 0x0000 }, + { 0x41d8, 0x0000 }, + { 0x41d9, 0x0000 }, + { 0x41da, 0x0000 }, + { 0x41db, 0x0000 }, + { 0x41dc, 0x0000 }, + { 0x41dd, 0x0000 }, + { 0x41de, 0x0000 }, + { 0x41df, 0x0000 }, + { 0x41e0, 0x0000 }, + { 0x41e1, 0x0000 }, + { 0x41e2, 0x0000 }, + { 0x41e3, 0x0000 }, + { 0x41e4, 0x0000 }, + { 0x41e5, 0x0000 }, + { 0x41e6, 0x0000 }, + { 0x41e7, 0x0000 }, + { 0x41e8, 0x0000 }, + { 0x41e9, 0x0000 }, + { 0x41ea, 0x0000 }, + { 0x41eb, 0x0000 }, + { 0x41ec, 0x0000 }, + { 0x41ed, 0x0000 }, + { 0x41ee, 0x0000 }, + { 0x41ef, 0x0000 }, + { 0x41f0, 0x0000 }, + { 0x41f1, 0x0000 }, + { 0x41f2, 0x0000 }, + { 0x41f3, 0x0000 }, + { 0x41f4, 0x0000 }, + { 0x41f5, 0x0000 }, + { 0x41f6, 0x0000 }, + { 0x41f7, 0x0000 }, + { 0x41f8, 0x0000 }, + { 0x41f9, 0x0000 }, + { 0x41fa, 0x0000 }, + { 0x41fb, 0x0000 }, + { 0x41fc, 0x0000 }, + { 0x41fd, 0x0000 }, + { 0x41fe, 0x0000 }, + { 0x41ff, 0x0000 }, + { 0x4200, 0x0000 }, + { 0x4201, 0x0000 }, + { 0x4202, 0x0000 }, + { 0x4203, 0x0000 }, + { 0x4204, 0x0000 }, + { 0x4205, 0x0000 }, + { 0x4206, 0x0000 }, + { 0x4207, 0x0000 }, + { 0x4208, 0x0000 }, + { 0x4209, 0x0000 }, + { 0x420a, 0x0000 }, + { 0x420b, 0x0000 }, + { 0x420c, 0x0000 }, + { 0x420d, 0x0000 }, + { 0x420e, 0x0000 }, + { 0x420f, 0x0000 }, + { 0x4210, 0x0000 }, + { 0x4211, 0x0000 }, + { 0x4212, 0x0000 }, + { 0x4213, 0x0000 }, + { 0x4214, 0x0000 }, + { 0x4215, 0x0000 }, + { 0x4216, 0x0000 }, + { 0x4217, 0x0000 }, + { 0x4218, 0x0000 }, + { 0x4219, 0x0000 }, + { 0x421a, 0x0000 }, + { 0x421b, 0x0000 }, + { 0x421c, 0x0000 }, + { 0x421d, 0x0000 }, + { 0x421e, 0x0000 }, + { 0x421f, 0x0000 }, + { 0x4220, 0x0000 }, + { 0x4221, 0x0000 }, + { 0x4222, 0x0000 }, + { 0x4223, 0x0000 }, + { 0x4224, 0x0000 }, + { 0x4225, 0x0000 }, + { 0x4226, 0x0000 }, + { 0x4227, 0x0000 }, + { 0x4228, 0x0000 }, + { 0x4229, 0x0000 }, + { 0x422a, 0x0000 }, + { 0x422b, 0x0000 }, + { 0x422c, 0x0000 }, + { 0x422d, 0x0000 }, + { 0x422e, 0x0000 }, + { 0x422f, 0x0000 }, + { 0x4230, 0x0000 }, + { 0x4231, 0x0000 }, + { 0x4232, 0x0000 }, + { 0x4233, 0x0000 }, + { 0x4234, 0x0000 }, + { 0x4235, 0x0000 }, + { 0x4236, 0x0000 }, + { 0x4237, 0x0000 }, + { 0x4238, 0x0000 }, + { 0x4239, 0x0000 }, + { 0x423a, 0x0000 }, + { 0x423b, 0x0000 }, + { 0x423c, 0x0000 }, + { 0x423d, 0x0000 }, + { 0x423e, 0x0000 }, + { 0x423f, 0x0000 }, + { 0x4240, 0x0000 }, + { 0x4241, 0x0000 }, + { 0x4242, 0x0000 }, + { 0x4243, 0x0000 }, + { 0x4244, 0x0000 }, + { 0x4245, 0x0000 }, + { 0x4246, 0x0000 }, + { 0x4247, 0x0000 }, + { 0x4248, 0x0000 }, + { 0x4249, 0x0000 }, + { 0x424a, 0x0000 }, + { 0x424b, 0x0000 }, + { 0x424c, 0x0000 }, + { 0x424d, 0x0000 }, + { 0x424e, 0x0000 }, + { 0x424f, 0x0000 }, + { 0x4250, 0x0000 }, + { 0x4251, 0x0000 }, + { 0x4252, 0x0000 }, + { 0x4253, 0x0000 }, + { 0x4254, 0x0000 }, + { 0x4255, 0x0000 }, + { 0x4256, 0x0000 }, + { 0x4257, 0x0000 }, + { 0x4258, 0x0000 }, + { 0x4259, 0x0000 }, + { 0x425a, 0x0000 }, + { 0x425b, 0x0000 }, + { 0x425c, 0x0000 }, + { 0x425d, 0x0000 }, + { 0x425e, 0x0000 }, + { 0x425f, 0x0000 }, + { 0x4260, 0x0000 }, + { 0x4261, 0x0000 }, + { 0x4262, 0x0000 }, + { 0x4263, 0x0000 }, + { 0x4264, 0x0000 }, + { 0x4265, 0x0000 }, + { 0x4266, 0x0000 }, + { 0x4267, 0x0000 }, + { 0x4268, 0x0000 }, + { 0x4269, 0x0000 }, + { 0x426a, 0x0000 }, + { 0x426b, 0x0000 }, + { 0x426c, 0x0000 }, + { 0x426d, 0x0000 }, + { 0x426e, 0x0000 }, + { 0x426f, 0x0000 }, + { 0x4270, 0x0000 }, + { 0x4271, 0x0000 }, + { 0x4272, 0x0000 }, + { 0x4273, 0x0000 }, + { 0x4274, 0x0000 }, + { 0x4275, 0x0000 }, + { 0x4276, 0x0000 }, + { 0x4277, 0x0000 }, + { 0x4278, 0x0000 }, + { 0x4279, 0x0000 }, + { 0x427a, 0x0000 }, + { 0x427b, 0x0000 }, + { 0x427c, 0x0000 }, + { 0x427d, 0x0000 }, + { 0x427e, 0x0000 }, + { 0x427f, 0x0000 }, + { 0x4280, 0x0000 }, + { 0x4281, 0x0000 }, + { 0x4282, 0x0000 }, + { 0x4283, 0x0000 }, + { 0x4284, 0x0000 }, + { 0x4285, 0x0000 }, + { 0x4286, 0x0000 }, + { 0x4287, 0x0000 }, + { 0x4288, 0x0000 }, + { 0x4289, 0x0000 }, + { 0x428a, 0x0000 }, + { 0x428b, 0x0000 }, + { 0x428c, 0x0000 }, + { 0x428d, 0x0000 }, + { 0x428e, 0x0000 }, + { 0x428f, 0x0000 }, + { 0x4290, 0x0000 }, + { 0x4291, 0x0000 }, + { 0x4292, 0x0000 }, + { 0x4293, 0x0000 }, + { 0x4294, 0x0000 }, + { 0x4295, 0x0000 }, + { 0x4296, 0x0000 }, + { 0x4297, 0x0000 }, + { 0x4298, 0x0000 }, + { 0x4299, 0x0000 }, + { 0x429a, 0x0000 }, + { 0x429b, 0x0000 }, + { 0x429c, 0x0000 }, + { 0x429d, 0x0000 }, + { 0x429e, 0x0000 }, + { 0x429f, 0x0000 }, + { 0x42a0, 0x0000 }, + { 0x42a1, 0x0000 }, + { 0x42a2, 0x0000 }, + { 0x42a3, 0x0000 }, + { 0x42a4, 0x0000 }, + { 0x42a5, 0x0000 }, + { 0x42a6, 0x0000 }, + { 0x42a7, 0x0000 }, + { 0x42a8, 0x0000 }, + { 0x42a9, 0x0000 }, + { 0x42aa, 0x0000 }, + { 0x42ab, 0x0000 }, + { 0x42ac, 0x0000 }, + { 0x42ad, 0x0000 }, + { 0x42ae, 0x0000 }, + { 0x42af, 0x0000 }, + { 0x42b0, 0x0000 }, + { 0x42b1, 0x0000 }, + { 0x42b2, 0x0000 }, + { 0x42b3, 0x0000 }, + { 0x42b4, 0x0000 }, + { 0x42b5, 0x0000 }, + { 0x42b6, 0x0000 }, + { 0x42b7, 0x0000 }, + { 0x42b8, 0x0000 }, + { 0x42b9, 0x0000 }, + { 0x42ba, 0x0000 }, + { 0x42bb, 0x0000 }, + { 0x42bc, 0x0000 }, + { 0x42bd, 0x0000 }, + { 0x42be, 0x0000 }, + { 0x42bf, 0x0000 }, + { 0x42c0, 0x0000 }, + { 0x42c1, 0x0000 }, + { 0x42c2, 0x0000 }, + { 0x42c3, 0x0000 }, + { 0x42c4, 0x0000 }, + { 0x42c5, 0x0000 }, + { 0x42c6, 0x0000 }, + { 0x42c7, 0x0000 }, + { 0x42c8, 0x0000 }, + { 0x42c9, 0x0000 }, + { 0x42ca, 0x0000 }, + { 0x42cb, 0x0000 }, + { 0x42cc, 0x0000 }, + { 0x42cd, 0x0000 }, + { 0x42ce, 0x0000 }, + { 0x42cf, 0x0000 }, + { 0x42d0, 0x0000 }, + { 0x42d1, 0x0000 }, + { 0x42d2, 0x0000 }, + { 0x42d3, 0x0000 }, + { 0x42d4, 0x0000 }, + { 0x42d5, 0x0000 }, + { 0x42d6, 0x0000 }, + { 0x42d7, 0x0000 }, + { 0x42d8, 0x0000 }, + { 0x42d9, 0x0000 }, + { 0x42da, 0x0000 }, + { 0x42db, 0x0000 }, + { 0x42dc, 0x0000 }, + { 0x42dd, 0x0000 }, + { 0x42de, 0x0000 }, + { 0x42df, 0x0000 }, + { 0x42e0, 0x0000 }, + { 0x42e1, 0x0000 }, + { 0x42e2, 0x0000 }, + { 0x42e3, 0x0000 }, + { 0x42e4, 0x0000 }, + { 0x42e5, 0x0000 }, + { 0x42e6, 0x0000 }, + { 0x42e7, 0x0000 }, + { 0x42e8, 0x0000 }, + { 0x42e9, 0x0000 }, + { 0x42ea, 0x0000 }, + { 0x42eb, 0x0000 }, + { 0x42ec, 0x0000 }, + { 0x42ed, 0x0000 }, + { 0x42ee, 0x0000 }, + { 0x42ef, 0x0000 }, + { 0x42f0, 0x0000 }, + { 0x42f1, 0x0000 }, + { 0x42f2, 0x0000 }, + { 0x42f3, 0x0000 }, + { 0x42f4, 0x0000 }, + { 0x42f5, 0x0000 }, + { 0x42f6, 0x0000 }, + { 0x42f7, 0x0000 }, + { 0x42f8, 0x0000 }, + { 0x42f9, 0x0000 }, + { 0x42fa, 0x0000 }, + { 0x42fb, 0x0000 }, + { 0x42fc, 0x0000 }, + { 0x42fd, 0x0000 }, + { 0x42fe, 0x0000 }, + { 0x42ff, 0x0000 }, + { 0x4300, 0x0000 }, + { 0x4301, 0x0000 }, + { 0x4302, 0x0000 }, + { 0x4303, 0x0000 }, + { 0x4304, 0x0000 }, + { 0x4305, 0x0000 }, + { 0x4306, 0x0000 }, + { 0x4307, 0x0000 }, + { 0x4308, 0x0000 }, + { 0x4309, 0x0000 }, + { 0x430a, 0x0000 }, + { 0x430b, 0x0000 }, + { 0x430c, 0x0000 }, + { 0x430d, 0x0000 }, + { 0x430e, 0x0000 }, + { 0x430f, 0x0000 }, + { 0x4310, 0x0000 }, + { 0x4311, 0x0000 }, + { 0x4312, 0x0000 }, + { 0x4313, 0x0000 }, + { 0x4314, 0x0000 }, + { 0x4315, 0x0000 }, + { 0x4316, 0x0000 }, + { 0x4317, 0x0000 }, + { 0x4318, 0x0000 }, + { 0x4319, 0x0000 }, + { 0x431a, 0x0000 }, + { 0x431b, 0x0000 }, + { 0x431c, 0x0000 }, + { 0x431d, 0x0000 }, + { 0x431e, 0x0000 }, + { 0x431f, 0x0000 }, + { 0x4320, 0x0000 }, + { 0x4321, 0x0000 }, + { 0x4322, 0x0000 }, + { 0x4323, 0x0000 }, + { 0x4324, 0x0000 }, + { 0x4325, 0x0000 }, + { 0x4326, 0x0000 }, + { 0x4327, 0x0000 }, + { 0x4328, 0x0000 }, + { 0x4329, 0x0000 }, + { 0x432a, 0x0000 }, + { 0x432b, 0x0000 }, + { 0x432c, 0x0000 }, + { 0x432d, 0x0000 }, + { 0x432e, 0x0000 }, + { 0x432f, 0x0000 }, + { 0x4330, 0x0000 }, + { 0x4331, 0x0000 }, + { 0x4332, 0x0000 }, + { 0x4333, 0x0000 }, + { 0x4334, 0x0000 }, + { 0x4335, 0x0000 }, + { 0x4336, 0x0000 }, + { 0x4337, 0x0000 }, + { 0x4338, 0x0000 }, + { 0x4339, 0x0000 }, + { 0x433a, 0x0000 }, + { 0x433b, 0x0000 }, + { 0x433c, 0x0000 }, + { 0x433d, 0x0000 }, + { 0x433e, 0x0000 }, + { 0x433f, 0x0000 }, + { 0x4340, 0x0000 }, + { 0x4341, 0x0000 }, + { 0x4342, 0x0000 }, + { 0x4343, 0x0000 }, + { 0x4344, 0x0000 }, + { 0x4345, 0x0000 }, + { 0x4346, 0x0000 }, + { 0x4347, 0x0000 }, + { 0x4348, 0x0000 }, + { 0x4349, 0x0000 }, + { 0x434a, 0x0000 }, + { 0x434b, 0x0000 }, + { 0x434c, 0x0000 }, + { 0x434d, 0x0000 }, + { 0x434e, 0x0000 }, + { 0x434f, 0x0000 }, + { 0x4350, 0x0000 }, + { 0x4351, 0x0000 }, + { 0x4352, 0x0000 }, + { 0x4353, 0x0000 }, + { 0x4354, 0x0000 }, + { 0x4355, 0x0000 }, + { 0x4356, 0x0000 }, + { 0x4357, 0x0000 }, + { 0x4358, 0x0000 }, + { 0x4359, 0x0000 }, + { 0x435a, 0x0000 }, + { 0x435b, 0x0000 }, + { 0x435c, 0x0000 }, + { 0x435d, 0x0000 }, + { 0x435e, 0x0000 }, + { 0x435f, 0x0000 }, + { 0x4360, 0x0000 }, + { 0x4361, 0x0000 }, + { 0x4362, 0x0000 }, + { 0x4363, 0x0000 }, + { 0x4364, 0x0000 }, + { 0x4365, 0x0000 }, + { 0x4366, 0x0000 }, + { 0x4367, 0x0000 }, + { 0x4368, 0x0000 }, + { 0x4369, 0x0000 }, + { 0x436a, 0x0000 }, + { 0x436b, 0x0000 }, + { 0x436c, 0x0000 }, + { 0x436d, 0x0000 }, + { 0x436e, 0x0000 }, + { 0x436f, 0x0000 }, + { 0x4370, 0x0000 }, + { 0x4371, 0x0000 }, + { 0x4372, 0x0000 }, + { 0x4373, 0x0000 }, + { 0x4374, 0x0000 }, + { 0x4375, 0x0000 }, + { 0x4376, 0x0000 }, + { 0x4377, 0x0000 }, + { 0x4378, 0x0000 }, + { 0x4379, 0x0000 }, + { 0x437a, 0x0000 }, + { 0x437b, 0x0000 }, + { 0x437c, 0x0000 }, + { 0x437d, 0x0000 }, + { 0x437e, 0x0000 }, + { 0x437f, 0x0000 }, + { 0x4380, 0x0000 }, + { 0x4381, 0x0000 }, + { 0x4382, 0x0000 }, + { 0x4383, 0x0000 }, + { 0x4384, 0x0000 }, + { 0x4385, 0x0000 }, + { 0x4386, 0x0000 }, + { 0x4387, 0x0000 }, + { 0x4388, 0x0000 }, + { 0x4389, 0x0000 }, + { 0x438a, 0x0000 }, + { 0x438b, 0x0000 }, + { 0x438c, 0x0000 }, + { 0x438d, 0x0000 }, + { 0x438e, 0x0000 }, + { 0x438f, 0x0000 }, + { 0x4390, 0x0000 }, + { 0x4391, 0x0000 }, + { 0x4392, 0x0000 }, + { 0x4393, 0x0000 }, + { 0x4394, 0x0000 }, + { 0x4395, 0x0000 }, + { 0x4396, 0x0000 }, + { 0x4397, 0x0000 }, + { 0x4398, 0x0000 }, + { 0x4399, 0x0000 }, + { 0x439a, 0x0000 }, + { 0x439b, 0x0000 }, + { 0x439c, 0x0000 }, + { 0x439d, 0x0000 }, + { 0x439e, 0x0000 }, + { 0x439f, 0x0000 }, + { 0x43a0, 0x0000 }, + { 0x43a1, 0x0000 }, + { 0x43a2, 0x0000 }, + { 0x43a3, 0x0000 }, + { 0x43a4, 0x0000 }, + { 0x43a5, 0x0000 }, + { 0x43a6, 0x0000 }, + { 0x43a7, 0x0000 }, + { 0x43a8, 0x0000 }, + { 0x43a9, 0x0000 }, + { 0x43aa, 0x0000 }, + { 0x43ab, 0x0000 }, + { 0x43ac, 0x0000 }, + { 0x43ad, 0x0000 }, + { 0x43ae, 0x0000 }, + { 0x43af, 0x0000 }, + { 0x43b0, 0x0000 }, + { 0x43b1, 0x0000 }, + { 0x43b2, 0x0000 }, + { 0x43b3, 0x0000 }, + { 0x43b4, 0x0000 }, + { 0x43b5, 0x0000 }, + { 0x43b6, 0x0000 }, + { 0x43b7, 0x0000 }, + { 0x43b8, 0x0000 }, + { 0x43b9, 0x0000 }, + { 0x43ba, 0x0000 }, + { 0x43bb, 0x0000 }, + { 0x43bc, 0x0000 }, + { 0x43bd, 0x0000 }, + { 0x43be, 0x0000 }, + { 0x43bf, 0x0000 }, + { 0x43c0, 0x0000 }, + { 0x43c1, 0x0000 }, + { 0x43c2, 0x0000 }, + { 0x43c3, 0x0000 }, + { 0x43c4, 0x0000 }, + { 0x43c5, 0x0000 }, + { 0x43c6, 0x0000 }, + { 0x43c7, 0x0000 }, + { 0x43c8, 0x0000 }, + { 0x43c9, 0x0000 }, + { 0x43ca, 0x0000 }, + { 0x43cb, 0x0000 }, + { 0x43cc, 0x0000 }, + { 0x43cd, 0x0000 }, + { 0x43ce, 0x0000 }, + { 0x43cf, 0x0000 }, + { 0x43d0, 0x0000 }, + { 0x43d1, 0x0000 }, + { 0x43d2, 0x0000 }, + { 0x43d3, 0x0000 }, + { 0x43d4, 0x0000 }, + { 0x43d5, 0x0000 }, + { 0x43d6, 0x0000 }, + { 0x43d7, 0x0000 }, + { 0x43d8, 0x0000 }, + { 0x43d9, 0x0000 }, + { 0x43da, 0x0000 }, + { 0x43db, 0x0000 }, + { 0x43dc, 0x0000 }, + { 0x43dd, 0x0000 }, + { 0x43de, 0x0000 }, + { 0x43df, 0x0000 }, + { 0x43e0, 0x0000 }, + { 0x43e1, 0x0000 }, + { 0x43e2, 0x0000 }, + { 0x43e3, 0x0000 }, + { 0x43e4, 0x0000 }, + { 0x43e5, 0x0000 }, + { 0x43e6, 0x0000 }, + { 0x43e7, 0x0000 }, + { 0x43e8, 0x0000 }, + { 0x43e9, 0x0000 }, + { 0x43ea, 0x0000 }, + { 0x43eb, 0x0000 }, + { 0x43ec, 0x0000 }, + { 0x43ed, 0x0000 }, + { 0x43ee, 0x0000 }, + { 0x43ef, 0x0000 }, + { 0x43f0, 0x0000 }, + { 0x43f1, 0x0000 }, + { 0x43f2, 0x0000 }, + { 0x43f3, 0x0000 }, + { 0x43f4, 0x0000 }, + { 0x43f5, 0x0000 }, + { 0x43f6, 0x0000 }, + { 0x43f7, 0x0000 }, + { 0x43f8, 0x0000 }, + { 0x43f9, 0x0000 }, + { 0x43fa, 0x0000 }, + { 0x43fb, 0x0000 }, + { 0x43fc, 0x0000 }, + { 0x43fd, 0x0000 }, + { 0x43fe, 0x0000 }, + { 0x43ff, 0x0000 }, + { 0x4400, 0x0000 }, + { 0x4401, 0x0000 }, + { 0x4402, 0x0000 }, + { 0x4403, 0x0000 }, + { 0x4404, 0x0000 }, + { 0x4405, 0x0000 }, + { 0x4406, 0x0000 }, + { 0x4407, 0x0000 }, + { 0x4408, 0x0000 }, + { 0x4409, 0x0000 }, + { 0x440a, 0x0000 }, + { 0x440b, 0x0000 }, + { 0x440c, 0x0000 }, + { 0x440d, 0x0000 }, + { 0x440e, 0x0000 }, + { 0x440f, 0x0000 }, + { 0x4410, 0x0000 }, + { 0x4411, 0x0000 }, + { 0x4412, 0x0000 }, + { 0x4413, 0x0000 }, + { 0x4414, 0x0000 }, + { 0x4415, 0x0000 }, + { 0x4416, 0x0000 }, + { 0x4417, 0x0000 }, + { 0x4418, 0x0000 }, + { 0x4419, 0x0000 }, + { 0x441a, 0x0000 }, + { 0x441b, 0x0000 }, + { 0x441c, 0x0000 }, + { 0x441d, 0x0000 }, + { 0x441e, 0x0000 }, + { 0x441f, 0x0000 }, + { 0x4420, 0x0000 }, + { 0x4421, 0x0000 }, + { 0x4422, 0x0000 }, + { 0x4423, 0x0000 }, + { 0x4424, 0x0000 }, + { 0x4425, 0x0000 }, + { 0x4426, 0x0000 }, + { 0x4427, 0x0000 }, + { 0x4428, 0x0000 }, + { 0x4429, 0x0000 }, + { 0x442a, 0x0000 }, + { 0x442b, 0x0000 }, + { 0x442c, 0x0000 }, + { 0x442d, 0x0000 }, + { 0x442e, 0x0000 }, + { 0x442f, 0x0000 }, + { 0x4430, 0x0000 }, + { 0x4431, 0x0000 }, + { 0x4432, 0x0000 }, + { 0x4433, 0x0000 }, + { 0x4434, 0x0000 }, + { 0x4435, 0x0000 }, + { 0x4436, 0x0000 }, + { 0x4437, 0x0000 }, + { 0x4438, 0x0000 }, + { 0x4439, 0x0000 }, + { 0x443a, 0x0000 }, + { 0x443b, 0x0000 }, + { 0x443c, 0x0000 }, + { 0x443d, 0x0000 }, + { 0x443e, 0x0000 }, + { 0x443f, 0x0000 }, + { 0x4440, 0x0000 }, + { 0x4441, 0x0000 }, + { 0x4442, 0x0000 }, + { 0x4443, 0x0000 }, + { 0x4444, 0x0000 }, + { 0x4445, 0x0000 }, + { 0x4446, 0x0000 }, + { 0x4447, 0x0000 }, + { 0x4448, 0x0000 }, + { 0x4449, 0x0000 }, + { 0x444a, 0x0000 }, + { 0x444b, 0x0000 }, + { 0x444c, 0x0000 }, + { 0x444d, 0x0000 }, + { 0x444e, 0x0000 }, + { 0x444f, 0x0000 }, + { 0x4450, 0x0000 }, + { 0x4451, 0x0000 }, + { 0x4452, 0x0000 }, + { 0x4453, 0x0000 }, + { 0x4454, 0x0000 }, + { 0x4455, 0x0000 }, + { 0x4456, 0x0000 }, + { 0x4457, 0x0000 }, + { 0x4458, 0x0000 }, + { 0x4459, 0x0000 }, + { 0x445a, 0x0000 }, + { 0x445b, 0x0000 }, + { 0x445c, 0x0000 }, + { 0x445d, 0x0000 }, + { 0x445e, 0x0000 }, + { 0x445f, 0x0000 }, + { 0x4460, 0x0000 }, + { 0x4461, 0x0000 }, + { 0x4462, 0x0000 }, + { 0x4463, 0x0000 }, + { 0x4464, 0x0000 }, + { 0x4465, 0x0000 }, + { 0x4466, 0x0000 }, + { 0x4467, 0x0000 }, + { 0x4468, 0x0000 }, + { 0x4469, 0x0000 }, + { 0x446a, 0x0000 }, + { 0x446b, 0x0000 }, + { 0x446c, 0x0000 }, + { 0x446d, 0x0000 }, + { 0x446e, 0x0000 }, + { 0x446f, 0x0000 }, + { 0x4470, 0x0000 }, + { 0x4471, 0x0000 }, + { 0x4472, 0x0000 }, + { 0x4473, 0x0000 }, + { 0x4474, 0x0000 }, + { 0x4475, 0x0000 }, + { 0x4476, 0x0000 }, + { 0x4477, 0x0000 }, + { 0x4478, 0x0000 }, + { 0x4479, 0x0000 }, + { 0x447a, 0x0000 }, + { 0x447b, 0x0000 }, + { 0x447c, 0x0000 }, + { 0x447d, 0x0000 }, + { 0x447e, 0x0000 }, + { 0x447f, 0x0000 }, + { 0x4480, 0x0000 }, + { 0x4481, 0x0000 }, + { 0x4482, 0x0000 }, + { 0x4483, 0x0000 }, + { 0x4484, 0x0000 }, + { 0x4485, 0x0000 }, + { 0x4486, 0x0000 }, + { 0x4487, 0x0000 }, + { 0x4488, 0x0000 }, + { 0x4489, 0x0000 }, + { 0x448a, 0x0000 }, + { 0x448b, 0x0000 }, + { 0x448c, 0x0000 }, + { 0x448d, 0x0000 }, + { 0x448e, 0x0000 }, + { 0x448f, 0x0000 }, + { 0x4490, 0x0000 }, + { 0x4491, 0x0000 }, + { 0x4492, 0x0000 }, + { 0x4493, 0x0000 }, + { 0x4494, 0x0000 }, + { 0x4495, 0x0000 }, + { 0x4496, 0x0000 }, + { 0x4497, 0x0000 }, + { 0x4498, 0x0000 }, + { 0x4499, 0x0000 }, + { 0x449a, 0x0000 }, + { 0x449b, 0x0000 }, + { 0x449c, 0x0000 }, + { 0x449d, 0x0000 }, + { 0x449e, 0x0000 }, + { 0x449f, 0x0000 }, + { 0x44a0, 0x0000 }, + { 0x44a1, 0x0000 }, + { 0x44a2, 0x0000 }, + { 0x44a3, 0x0000 }, + { 0x44a4, 0x0000 }, + { 0x44a5, 0x0000 }, + { 0x44a6, 0x0000 }, + { 0x44a7, 0x0000 }, + { 0x44a8, 0x0000 }, + { 0x44a9, 0x0000 }, + { 0x44aa, 0x0000 }, + { 0x44ab, 0x0000 }, + { 0x44ac, 0x0000 }, + { 0x44ad, 0x0000 }, + { 0x44ae, 0x0000 }, + { 0x44af, 0x0000 }, + { 0x44b0, 0x0000 }, + { 0x44b1, 0x0000 }, + { 0x44b2, 0x0000 }, + { 0x44b3, 0x0000 }, + { 0x44b4, 0x0000 }, + { 0x44b5, 0x0000 }, + { 0x44b6, 0x0000 }, + { 0x44b7, 0x0000 }, + { 0x44b8, 0x0000 }, + { 0x44b9, 0x0000 }, + { 0x44ba, 0x0000 }, + { 0x44bb, 0x0000 }, + { 0x44bc, 0x0000 }, + { 0x44bd, 0x0000 }, + { 0x44be, 0x0000 }, + { 0x44bf, 0x0000 }, + { 0x44c0, 0x0000 }, + { 0x44c1, 0x0000 }, + { 0x44c2, 0x0000 }, + { 0x44c3, 0x0000 }, + { 0x44c4, 0x0000 }, + { 0x44c5, 0x0000 }, + { 0x44c6, 0x0000 }, + { 0x44c7, 0x0000 }, + { 0x44c8, 0x0000 }, + { 0x44c9, 0x0000 }, + { 0x44ca, 0x0000 }, + { 0x44cb, 0x0000 }, + { 0x44cc, 0x0000 }, + { 0x44cd, 0x0000 }, + { 0x44ce, 0x0000 }, + { 0x44cf, 0x0000 }, + { 0x44d0, 0x0000 }, + { 0x44d1, 0x0000 }, + { 0x44d2, 0x0000 }, + { 0x44d3, 0x0000 }, + { 0x44d4, 0x0000 }, + { 0x44d5, 0x0000 }, + { 0x44d6, 0x0000 }, + { 0x44d7, 0x0000 }, + { 0x44d8, 0x0000 }, + { 0x44d9, 0x0000 }, + { 0x44da, 0x0000 }, + { 0x44db, 0x0000 }, + { 0x44dc, 0x0000 }, + { 0x44dd, 0x0000 }, + { 0x44de, 0x0000 }, + { 0x44df, 0x0000 }, + { 0x44e0, 0x0000 }, + { 0x44e1, 0x0000 }, + { 0x44e2, 0x0000 }, + { 0x44e3, 0x0000 }, + { 0x44e4, 0x0000 }, + { 0x44e5, 0x0000 }, + { 0x44e6, 0x0000 }, + { 0x44e7, 0x0000 }, + { 0x44e8, 0x0000 }, + { 0x44e9, 0x0000 }, + { 0x44ea, 0x0000 }, + { 0x44eb, 0x0000 }, + { 0x44ec, 0x0000 }, + { 0x44ed, 0x0000 }, + { 0x44ee, 0x0000 }, + { 0x44ef, 0x0000 }, + { 0x44f0, 0x0000 }, + { 0x44f1, 0x0000 }, + { 0x44f2, 0x0000 }, + { 0x44f3, 0x0000 }, + { 0x44f4, 0x0000 }, + { 0x44f5, 0x0000 }, + { 0x44f6, 0x0000 }, + { 0x44f7, 0x0000 }, + { 0x44f8, 0x0000 }, + { 0x44f9, 0x0000 }, + { 0x44fa, 0x0000 }, + { 0x44fb, 0x0000 }, + { 0x44fc, 0x0000 }, + { 0x44fd, 0x0000 }, + { 0x44fe, 0x0000 }, + { 0x44ff, 0x0000 }, + { 0x4500, 0x0000 }, + { 0x4501, 0x0000 }, + { 0x4502, 0x0000 }, + { 0x4503, 0x0000 }, + { 0x4504, 0x0000 }, + { 0x4505, 0x0000 }, + { 0x4506, 0x0000 }, + { 0x4507, 0x0000 }, + { 0x4508, 0x0000 }, + { 0x4509, 0x0000 }, + { 0x450a, 0x0000 }, + { 0x450b, 0x0000 }, + { 0x450c, 0x0000 }, + { 0x450d, 0x0000 }, + { 0x450e, 0x0000 }, + { 0x450f, 0x0000 }, + { 0x4510, 0x0000 }, + { 0x4511, 0x0000 }, + { 0x4512, 0x0000 }, + { 0x4513, 0x0000 }, + { 0x4514, 0x0000 }, + { 0x4515, 0x0000 }, + { 0x4516, 0x0000 }, + { 0x4517, 0x0000 }, + { 0x4518, 0x0000 }, + { 0x4519, 0x0000 }, + { 0x451a, 0x0000 }, + { 0x451b, 0x0000 }, + { 0x451c, 0x0000 }, + { 0x451d, 0x0000 }, + { 0x451e, 0x0000 }, + { 0x451f, 0x0000 }, + { 0x4520, 0x0000 }, + { 0x4521, 0x0000 }, + { 0x4522, 0x0000 }, + { 0x4523, 0x0000 }, + { 0x4524, 0x0000 }, + { 0x4525, 0x0000 }, + { 0x4526, 0x0000 }, + { 0x4527, 0x0000 }, + { 0x4528, 0x0000 }, + { 0x4529, 0x0000 }, + { 0x452a, 0x0000 }, + { 0x452b, 0x0000 }, + { 0x452c, 0x0000 }, + { 0x452d, 0x0000 }, + { 0x452e, 0x0000 }, + { 0x452f, 0x0000 }, + { 0x4530, 0x0000 }, + { 0x4531, 0x0000 }, + { 0x4532, 0x0000 }, + { 0x4533, 0x0000 }, + { 0x4534, 0x0000 }, + { 0x4535, 0x0000 }, + { 0x4536, 0x0000 }, + { 0x4537, 0x0000 }, + { 0x4538, 0x0000 }, + { 0x4539, 0x0000 }, + { 0x453a, 0x0000 }, + { 0x453b, 0x0000 }, + { 0x453c, 0x0000 }, + { 0x453d, 0x0000 }, + { 0x453e, 0x0000 }, + { 0x453f, 0x0000 }, + { 0x4540, 0x0000 }, + { 0x4541, 0x0000 }, + { 0x4542, 0x0000 }, + { 0x4543, 0x0000 }, + { 0x4544, 0x0000 }, + { 0x4545, 0x0000 }, + { 0x4546, 0x0000 }, + { 0x4547, 0x0000 }, + { 0x4548, 0x0000 }, + { 0x4549, 0x0000 }, + { 0x454a, 0x0000 }, + { 0x454b, 0x0000 }, + { 0x454c, 0x0000 }, + { 0x454d, 0x0000 }, + { 0x454e, 0x0000 }, + { 0x454f, 0x0000 }, + { 0x4550, 0x0000 }, + { 0x4551, 0x0000 }, + { 0x4552, 0x0000 }, + { 0x4553, 0x0000 }, + { 0x4554, 0x0000 }, + { 0x4555, 0x0000 }, + { 0x4556, 0x0000 }, + { 0x4557, 0x0000 }, + { 0x4558, 0x0000 }, + { 0x4559, 0x0000 }, + { 0x455a, 0x0000 }, + { 0x455b, 0x0000 }, + { 0x455c, 0x0000 }, + { 0x455d, 0x0000 }, + { 0x455e, 0x0000 }, + { 0x455f, 0x0000 }, + { 0x4560, 0x0000 }, + { 0x4561, 0x0000 }, + { 0x4562, 0x0000 }, + { 0x4563, 0x0000 }, + { 0x4564, 0x0000 }, + { 0x4565, 0x0000 }, + { 0x4566, 0x0000 }, + { 0x4567, 0x0000 }, + { 0x4568, 0x0000 }, + { 0x4569, 0x0000 }, + { 0x456a, 0x0000 }, + { 0x456b, 0x0000 }, + { 0x456c, 0x0000 }, + { 0x456d, 0x0000 }, + { 0x456e, 0x0000 }, + { 0x456f, 0x0000 }, + { 0x4570, 0x0000 }, + { 0x4571, 0x0000 }, + { 0x4572, 0x0000 }, + { 0x4573, 0x0000 }, + { 0x4574, 0x0000 }, + { 0x4575, 0x0000 }, + { 0x4576, 0x0000 }, + { 0x4577, 0x0000 }, + { 0x4578, 0x0000 }, + { 0x4579, 0x0000 }, + { 0x457a, 0x0000 }, + { 0x457b, 0x0000 }, + { 0x457c, 0x0000 }, + { 0x457d, 0x0000 }, + { 0x457e, 0x0000 }, + { 0x457f, 0x0000 }, + { 0x4580, 0x0000 }, + { 0x4581, 0x0000 }, + { 0x4582, 0x0000 }, + { 0x4583, 0x0000 }, + { 0x4584, 0x0000 }, + { 0x4585, 0x0000 }, + { 0x4586, 0x0000 }, + { 0x4587, 0x0000 }, + { 0x4588, 0x0000 }, + { 0x4589, 0x0000 }, + { 0x458a, 0x0000 }, + { 0x458b, 0x0000 }, + { 0x458c, 0x0000 }, + { 0x458d, 0x0000 }, + { 0x458e, 0x0000 }, + { 0x458f, 0x0000 }, + { 0x4590, 0x0000 }, + { 0x4591, 0x0000 }, + { 0x4592, 0x0000 }, + { 0x4593, 0x0000 }, + { 0x4594, 0x0000 }, + { 0x4595, 0x0000 }, + { 0x4596, 0x0000 }, + { 0x4597, 0x0000 }, + { 0x4598, 0x0000 }, + { 0x4599, 0x0000 }, + { 0x459a, 0x0000 }, + { 0x459b, 0x0000 }, + { 0x459c, 0x0000 }, + { 0x459d, 0x0000 }, + { 0x459e, 0x0000 }, + { 0x459f, 0x0000 }, + { 0x45a0, 0x0000 }, + { 0x45a1, 0x0000 }, + { 0x45a2, 0x0000 }, + { 0x45a3, 0x0000 }, + { 0x45a4, 0x0000 }, + { 0x45a5, 0x0000 }, + { 0x45a6, 0x0000 }, + { 0x45a7, 0x0000 }, + { 0x45a8, 0x0000 }, + { 0x45a9, 0x0000 }, + { 0x45aa, 0x0000 }, + { 0x45ab, 0x0000 }, + { 0x45ac, 0x0000 }, + { 0x45ad, 0x0000 }, + { 0x45ae, 0x0000 }, + { 0x45af, 0x0000 }, + { 0x45b0, 0x0000 }, + { 0x45b1, 0x0000 }, + { 0x45b2, 0x0000 }, + { 0x45b3, 0x0000 }, + { 0x45b4, 0x0000 }, + { 0x45b5, 0x0000 }, + { 0x45b6, 0x0000 }, + { 0x45b7, 0x0000 }, + { 0x45b8, 0x0000 }, + { 0x45b9, 0x0000 }, + { 0x45ba, 0x0000 }, + { 0x45bb, 0x0000 }, + { 0x45bc, 0x0000 }, + { 0x45bd, 0x0000 }, + { 0x45be, 0x0000 }, + { 0x45bf, 0x0000 }, + { 0x45c0, 0x0000 }, + { 0x45c1, 0x0000 }, + { 0x45c2, 0x0000 }, + { 0x45c3, 0x0000 }, + { 0x45c4, 0x0000 }, + { 0x45c5, 0x0000 }, + { 0x45c6, 0x0000 }, + { 0x45c7, 0x0000 }, + { 0x45c8, 0x0000 }, + { 0x45c9, 0x0000 }, + { 0x45ca, 0x0000 }, + { 0x45cb, 0x0000 }, + { 0x45cc, 0x0000 }, + { 0x45cd, 0x0000 }, + { 0x45ce, 0x0000 }, + { 0x45cf, 0x0000 }, + { 0x45d0, 0x0000 }, + { 0x45d1, 0x0000 }, + { 0x45d2, 0x0000 }, + { 0x45d3, 0x0000 }, + { 0x45d4, 0x0000 }, + { 0x45d5, 0x0000 }, + { 0x45d6, 0x0000 }, + { 0x45d7, 0x0000 }, + { 0x45d8, 0x0000 }, + { 0x45d9, 0x0000 }, + { 0x45da, 0x0000 }, + { 0x45db, 0x0000 }, + { 0x45dc, 0x0000 }, + { 0x45dd, 0x0000 }, + { 0x45de, 0x0000 }, + { 0x45df, 0x0000 }, + { 0x45e0, 0x0000 }, + { 0x45e1, 0x0000 }, + { 0x45e2, 0x0000 }, + { 0x45e3, 0x0000 }, + { 0x45e4, 0x0000 }, + { 0x45e5, 0x0000 }, + { 0x45e6, 0x0000 }, + { 0x45e7, 0x0000 }, + { 0x45e8, 0x0000 }, + { 0x45e9, 0x0000 }, + { 0x45ea, 0x0000 }, + { 0x45eb, 0x0000 }, + { 0x45ec, 0x0000 }, + { 0x45ed, 0x0000 }, + { 0x45ee, 0x0000 }, + { 0x45ef, 0x0000 }, + { 0x45f0, 0x0000 }, + { 0x45f1, 0x0000 }, + { 0x45f2, 0x0000 }, + { 0x45f3, 0x0000 }, + { 0x45f4, 0x0000 }, + { 0x45f5, 0x0000 }, + { 0x45f6, 0x0000 }, + { 0x45f7, 0x0000 }, + { 0x45f8, 0x0000 }, + { 0x45f9, 0x0000 }, + { 0x45fa, 0x0000 }, + { 0x45fb, 0x0000 }, + { 0x45fc, 0x0000 }, + { 0x45fd, 0x0000 }, + { 0x45fe, 0x0000 }, + { 0x45ff, 0x0000 }, + { 0x4600, 0x0000 }, + { 0x4601, 0x0000 }, + { 0x4602, 0x0000 }, + { 0x4603, 0x0000 }, + { 0x4604, 0x0000 }, + { 0x4605, 0x0000 }, + { 0x4606, 0x0000 }, + { 0x4607, 0x0000 }, + { 0x4608, 0x0000 }, + { 0x4609, 0x0000 }, + { 0x460a, 0x0000 }, + { 0x460b, 0x0000 }, + { 0x460c, 0x0000 }, + { 0x460d, 0x0000 }, + { 0x460e, 0x0000 }, + { 0x460f, 0x0000 }, + { 0x4610, 0x0000 }, + { 0x4611, 0x0000 }, + { 0x4612, 0x0000 }, + { 0x4613, 0x0000 }, + { 0x4614, 0x0000 }, + { 0x4615, 0x0000 }, + { 0x4616, 0x0000 }, + { 0x4617, 0x0000 }, + { 0x4618, 0x0000 }, + { 0x4619, 0x0000 }, + { 0x461a, 0x0000 }, + { 0x461b, 0x0000 }, + { 0x461c, 0x0000 }, + { 0x461d, 0x0000 }, + { 0x461e, 0x0000 }, + { 0x461f, 0x0000 }, + { 0x4620, 0x0000 }, + { 0x4621, 0x0000 }, + { 0x4622, 0x0000 }, + { 0x4623, 0x0000 }, + { 0x4624, 0x0000 }, + { 0x4625, 0x0000 }, + { 0x4626, 0x0000 }, + { 0x4627, 0x0000 }, + { 0x4628, 0x0000 }, + { 0x4629, 0x0000 }, + { 0x462a, 0x0000 }, + { 0x462b, 0x0000 }, + { 0x462c, 0x0000 }, + { 0x462d, 0x0000 }, + { 0x462e, 0x0000 }, + { 0x462f, 0x0000 }, + { 0x4630, 0x0000 }, + { 0x4631, 0x0000 }, + { 0x4632, 0x0000 }, + { 0x4633, 0x0000 }, + { 0x4634, 0x0000 }, + { 0x4635, 0x0000 }, + { 0x4636, 0x0000 }, + { 0x4637, 0x0000 }, + { 0x4638, 0x0000 }, + { 0x4639, 0x0000 }, + { 0x463a, 0x0000 }, + { 0x463b, 0x0000 }, + { 0x463c, 0x0000 }, + { 0x463d, 0x0000 }, + { 0x463e, 0x0000 }, + { 0x463f, 0x0000 }, + { 0x4640, 0x0000 }, + { 0x4641, 0x0000 }, + { 0x4642, 0x0000 }, + { 0x4643, 0x0000 }, + { 0x4644, 0x0000 }, + { 0x4645, 0x0000 }, + { 0x4646, 0x0000 }, + { 0x4647, 0x0000 }, + { 0x4648, 0x0000 }, + { 0x4649, 0x0000 }, + { 0x464a, 0x0000 }, + { 0x464b, 0x0000 }, + { 0x464c, 0x0000 }, + { 0x464d, 0x0000 }, + { 0x464e, 0x0000 }, + { 0x464f, 0x0000 }, + { 0x4650, 0x0000 }, + { 0x4651, 0x0000 }, + { 0x4652, 0x0000 }, + { 0x4653, 0x0000 }, + { 0x4654, 0x0000 }, + { 0x4655, 0x0000 }, + { 0x4656, 0x0000 }, + { 0x4657, 0x0000 }, + { 0x4658, 0x0000 }, + { 0x4659, 0x0000 }, + { 0x465a, 0x0000 }, + { 0x465b, 0x0000 }, + { 0x465c, 0x0000 }, + { 0x465d, 0x0000 }, + { 0x465e, 0x0000 }, + { 0x465f, 0x0000 }, + { 0x4660, 0x0000 }, + { 0x4661, 0x0000 }, + { 0x4662, 0x0000 }, + { 0x4663, 0x0000 }, + { 0x4664, 0x0000 }, + { 0x4665, 0x0000 }, + { 0x4666, 0x0000 }, + { 0x4667, 0x0000 }, + { 0x4668, 0x0000 }, + { 0x4669, 0x0000 }, + { 0x466a, 0x0000 }, + { 0x466b, 0x0000 }, + { 0x466c, 0x0000 }, + { 0x466d, 0x0000 }, + { 0x466e, 0x0000 }, + { 0x466f, 0x0000 }, + { 0x4670, 0x0000 }, + { 0x4671, 0x0000 }, + { 0x4672, 0x0000 }, + { 0x4673, 0x0000 }, + { 0x4674, 0x0000 }, + { 0x4675, 0x0000 }, + { 0x4676, 0x0000 }, + { 0x4677, 0x0000 }, + { 0x4678, 0x0000 }, + { 0x4679, 0x0000 }, + { 0x467a, 0x0000 }, + { 0x467b, 0x0000 }, + { 0x467c, 0x0000 }, + { 0x467d, 0x0000 }, + { 0x467e, 0x0000 }, + { 0x467f, 0x0000 }, + { 0x4680, 0x0000 }, + { 0x4681, 0x0000 }, + { 0x4682, 0x0000 }, + { 0x4683, 0x0000 }, + { 0x4684, 0x0000 }, + { 0x4685, 0x0000 }, + { 0x4686, 0x0000 }, + { 0x4687, 0x0000 }, + { 0x4688, 0x0000 }, + { 0x4689, 0x0000 }, + { 0x468a, 0x0000 }, + { 0x468b, 0x0000 }, + { 0x468c, 0x0000 }, + { 0x468d, 0x0000 }, + { 0x468e, 0x0000 }, + { 0x468f, 0x0000 }, + { 0x4690, 0x0000 }, + { 0x4691, 0x0000 }, + { 0x4692, 0x0000 }, + { 0x4693, 0x0000 }, + { 0x4694, 0x0000 }, + { 0x4695, 0x0000 }, + { 0x4696, 0x0000 }, + { 0x4697, 0x0000 }, + { 0x4698, 0x0000 }, + { 0x4699, 0x0000 }, + { 0x469a, 0x0000 }, + { 0x469b, 0x0000 }, + { 0x469c, 0x0000 }, + { 0x469d, 0x0000 }, + { 0x469e, 0x0000 }, + { 0x469f, 0x0000 }, + { 0x46a0, 0x0000 }, + { 0x46a1, 0x0000 }, + { 0x46a2, 0x0000 }, + { 0x46a3, 0x0000 }, + { 0x46a4, 0x0000 }, + { 0x46a5, 0x0000 }, + { 0x46a6, 0x0000 }, + { 0x46a7, 0x0000 }, + { 0x46a8, 0x0000 }, + { 0x46a9, 0x0000 }, + { 0x46aa, 0x0000 }, + { 0x46ab, 0x0000 }, + { 0x46ac, 0x0000 }, + { 0x46ad, 0x0000 }, + { 0x46ae, 0x0000 }, + { 0x46af, 0x0000 }, + { 0x46b0, 0x0000 }, + { 0x46b1, 0x0000 }, + { 0x46b2, 0x0000 }, + { 0x46b3, 0x0000 }, + { 0x46b4, 0x0000 }, + { 0x46b5, 0x0000 }, + { 0x46b6, 0x0000 }, + { 0x46b7, 0x0000 }, + { 0x46b8, 0x0000 }, + { 0x46b9, 0x0000 }, + { 0x46ba, 0x0000 }, + { 0x46bb, 0x0000 }, + { 0x46bc, 0x0000 }, + { 0x46bd, 0x0000 }, + { 0x46be, 0x0000 }, + { 0x46bf, 0x0000 }, + { 0x46c0, 0x0000 }, + { 0x46c1, 0x0000 }, + { 0x46c2, 0x0000 }, + { 0x46c3, 0x0000 }, + { 0x46c4, 0x0000 }, + { 0x46c5, 0x0000 }, + { 0x46c6, 0x0000 }, + { 0x46c7, 0x0000 }, + { 0x46c8, 0x0000 }, + { 0x46c9, 0x0000 }, + { 0x46ca, 0x0000 }, + { 0x46cb, 0x0000 }, + { 0x46cc, 0x0000 }, + { 0x46cd, 0x0000 }, + { 0x46ce, 0x0000 }, + { 0x46cf, 0x0000 }, + { 0x46d0, 0x0000 }, + { 0x46d1, 0x0000 }, + { 0x46d2, 0x0000 }, + { 0x46d3, 0x0000 }, + { 0x46d4, 0x0000 }, + { 0x46d5, 0x0000 }, + { 0x46d6, 0x0000 }, + { 0x46d7, 0x0000 }, + { 0x46d8, 0x0000 }, + { 0x46d9, 0x0000 }, + { 0x46da, 0x0000 }, + { 0x46db, 0x0000 }, + { 0x46dc, 0x0000 }, + { 0x46dd, 0x0000 }, + { 0x46de, 0x0000 }, + { 0x46df, 0x0000 }, + { 0x46e0, 0x0000 }, + { 0x46e1, 0x0000 }, + { 0x46e2, 0x0000 }, + { 0x46e3, 0x0000 }, + { 0x46e4, 0x0000 }, + { 0x46e5, 0x0000 }, + { 0x46e6, 0x0000 }, + { 0x46e7, 0x0000 }, + { 0x46e8, 0x0000 }, + { 0x46e9, 0x0000 }, + { 0x46ea, 0x0000 }, + { 0x46eb, 0x0000 }, + { 0x46ec, 0x0000 }, + { 0x46ed, 0x0000 }, + { 0x46ee, 0x0000 }, + { 0x46ef, 0x0000 }, + { 0x46f0, 0x0000 }, + { 0x46f1, 0x0000 }, + { 0x46f2, 0x0000 }, + { 0x46f3, 0x0000 }, + { 0x46f4, 0x0000 }, + { 0x46f5, 0x0000 }, + { 0x46f6, 0x0000 }, + { 0x46f7, 0x0000 }, + { 0x46f8, 0x0000 }, + { 0x46f9, 0x0000 }, + { 0x46fa, 0x0000 }, + { 0x46fb, 0x0000 }, + { 0x46fc, 0x0000 }, + { 0x46fd, 0x0000 }, + { 0x46fe, 0x0000 }, + { 0x46ff, 0x0000 }, + { 0x4700, 0x0000 }, + { 0x4701, 0x0000 }, + { 0x4702, 0x0000 }, + { 0x4703, 0x0000 }, + { 0x4704, 0x0000 }, + { 0x4705, 0x0000 }, + { 0x4706, 0x0000 }, + { 0x4707, 0x0000 }, + { 0x4708, 0x0000 }, + { 0x4709, 0x0000 }, + { 0x470a, 0x0000 }, + { 0x470b, 0x0000 }, + { 0x470c, 0x0000 }, + { 0x470d, 0x0000 }, + { 0x470e, 0x0000 }, + { 0x470f, 0x0000 }, + { 0x4710, 0x0000 }, + { 0x4711, 0x0000 }, + { 0x4712, 0x0000 }, + { 0x4713, 0x0000 }, + { 0x4714, 0x0000 }, + { 0x4715, 0x0000 }, + { 0x4716, 0x0000 }, + { 0x4717, 0x0000 }, + { 0x4718, 0x0000 }, + { 0x4719, 0x0000 }, + { 0x471a, 0x0000 }, + { 0x471b, 0x0000 }, + { 0x471c, 0x0000 }, + { 0x471d, 0x0000 }, + { 0x471e, 0x0000 }, + { 0x471f, 0x0000 }, + { 0x4720, 0x0000 }, + { 0x4721, 0x0000 }, + { 0x4722, 0x0000 }, + { 0x4723, 0x0000 }, + { 0x4724, 0x0000 }, + { 0x4725, 0x0000 }, + { 0x4726, 0x0000 }, + { 0x4727, 0x0000 }, + { 0x4728, 0x0000 }, + { 0x4729, 0x0000 }, + { 0x472a, 0x0000 }, + { 0x472b, 0x0000 }, + { 0x472c, 0x0000 }, + { 0x472d, 0x0000 }, + { 0x472e, 0x0000 }, + { 0x472f, 0x0000 }, + { 0x4730, 0x0000 }, + { 0x4731, 0x0000 }, + { 0x4732, 0x0000 }, + { 0x4733, 0x0000 }, + { 0x4734, 0x0000 }, + { 0x4735, 0x0000 }, + { 0x4736, 0x0000 }, + { 0x4737, 0x0000 }, + { 0x4738, 0x0000 }, + { 0x4739, 0x0000 }, + { 0x473a, 0x0000 }, + { 0x473b, 0x0000 }, + { 0x473c, 0x0000 }, + { 0x473d, 0x0000 }, + { 0x473e, 0x0000 }, + { 0x473f, 0x0000 }, + { 0x4740, 0x0000 }, + { 0x4741, 0x0000 }, + { 0x4742, 0x0000 }, + { 0x4743, 0x0000 }, + { 0x4744, 0x0000 }, + { 0x4745, 0x0000 }, + { 0x4746, 0x0000 }, + { 0x4747, 0x0000 }, + { 0x4748, 0x0000 }, + { 0x4749, 0x0000 }, + { 0x474a, 0x0000 }, + { 0x474b, 0x0000 }, + { 0x474c, 0x0000 }, + { 0x474d, 0x0000 }, + { 0x474e, 0x0000 }, + { 0x474f, 0x0000 }, + { 0x4750, 0x0000 }, + { 0x4751, 0x0000 }, + { 0x4752, 0x0000 }, + { 0x4753, 0x0000 }, + { 0x4754, 0x0000 }, + { 0x4755, 0x0000 }, + { 0x4756, 0x0000 }, + { 0x4757, 0x0000 }, + { 0x4758, 0x0000 }, + { 0x4759, 0x0000 }, + { 0x475a, 0x0000 }, + { 0x475b, 0x0000 }, + { 0x475c, 0x0000 }, + { 0x475d, 0x0000 }, + { 0x475e, 0x0000 }, + { 0x475f, 0x0000 }, + { 0x4760, 0x0000 }, + { 0x4761, 0x0000 }, + { 0x4762, 0x0000 }, + { 0x4763, 0x0000 }, + { 0x4764, 0x0000 }, + { 0x4765, 0x0000 }, + { 0x4766, 0x0000 }, + { 0x4767, 0x0000 }, + { 0x4768, 0x0000 }, + { 0x4769, 0x0000 }, + { 0x476a, 0x0000 }, + { 0x476b, 0x0000 }, + { 0x476c, 0x0000 }, + { 0x476d, 0x0000 }, + { 0x476e, 0x0000 }, + { 0x476f, 0x0000 }, + { 0x4770, 0x0000 }, + { 0x4771, 0x0000 }, + { 0x4772, 0x0000 }, + { 0x4773, 0x0000 }, + { 0x4774, 0x0000 }, + { 0x4775, 0x0000 }, + { 0x4776, 0x0000 }, + { 0x4777, 0x0000 }, + { 0x4778, 0x0000 }, + { 0x4779, 0x0000 }, + { 0x477a, 0x0000 }, + { 0x477b, 0x0000 }, + { 0x477c, 0x0000 }, + { 0x477d, 0x0000 }, + { 0x477e, 0x0000 }, + { 0x477f, 0x0000 }, + { 0x4780, 0x0000 }, + { 0x4781, 0x0000 }, + { 0x4782, 0x0000 }, + { 0x4783, 0x0000 }, + { 0x4784, 0x0000 }, + { 0x4785, 0x0000 }, + { 0x4786, 0x0000 }, + { 0x4787, 0x0000 }, + { 0x4788, 0x0000 }, + { 0x4789, 0x0000 }, + { 0x478a, 0x0000 }, + { 0x478b, 0x0000 }, + { 0x478c, 0x0000 }, + { 0x478d, 0x0000 }, + { 0x478e, 0x0000 }, + { 0x478f, 0x0000 }, + { 0x4790, 0x0000 }, + { 0x4791, 0x0000 }, + { 0x4792, 0x0000 }, + { 0x4793, 0x0000 }, + { 0x4794, 0x0000 }, + { 0x4795, 0x0000 }, + { 0x4796, 0x0000 }, + { 0x4797, 0x0000 }, + { 0x4798, 0x0000 }, + { 0x4799, 0x0000 }, + { 0x479a, 0x0000 }, + { 0x479b, 0x0000 }, + { 0x479c, 0x0000 }, + { 0x479d, 0x0000 }, + { 0x479e, 0x0000 }, + { 0x479f, 0x0000 }, + { 0x47a0, 0x0000 }, + { 0x47a1, 0x0000 }, + { 0x47a2, 0x0000 }, + { 0x47a3, 0x0000 }, + { 0x47a4, 0x0000 }, + { 0x47a5, 0x0000 }, + { 0x47a6, 0x0000 }, + { 0x47a7, 0x0000 }, + { 0x47a8, 0x0000 }, + { 0x47a9, 0x0000 }, + { 0x47aa, 0x0000 }, + { 0x47ab, 0x0000 }, + { 0x47ac, 0x0000 }, + { 0x47ad, 0x0000 }, + { 0x47ae, 0x0000 }, + { 0x47af, 0x0000 }, + { 0x47b0, 0x0000 }, + { 0x47b1, 0x0000 }, + { 0x47b2, 0x0000 }, + { 0x47b3, 0x0000 }, + { 0x47b4, 0x0000 }, + { 0x47b5, 0x0000 }, + { 0x47b6, 0x0000 }, + { 0x47b7, 0x0000 }, + { 0x47b8, 0x0000 }, + { 0x47b9, 0x0000 }, + { 0x47ba, 0x0000 }, + { 0x47bb, 0x0000 }, + { 0x47bc, 0x0000 }, + { 0x47bd, 0x0000 }, + { 0x47be, 0x0000 }, + { 0x47bf, 0x0000 }, + { 0x47c0, 0x0000 }, + { 0x47c1, 0x0000 }, + { 0x47c2, 0x0000 }, + { 0x47c3, 0x0000 }, + { 0x47c4, 0x0000 }, + { 0x47c5, 0x0000 }, + { 0x47c6, 0x0000 }, + { 0x47c7, 0x0000 }, + { 0x47c8, 0x0000 }, + { 0x47c9, 0x0000 }, + { 0x47ca, 0x0000 }, + { 0x47cb, 0x0000 }, + { 0x47cc, 0x0000 }, + { 0x47cd, 0x0000 }, + { 0x47ce, 0x0000 }, + { 0x47cf, 0x0000 }, + { 0x47d0, 0x0000 }, + { 0x47d1, 0x0000 }, + { 0x47d2, 0x0000 }, + { 0x47d3, 0x0000 }, + { 0x47d4, 0x0000 }, + { 0x47d5, 0x0000 }, + { 0x47d6, 0x0000 }, + { 0x47d7, 0x0000 }, + { 0x47d8, 0x0000 }, + { 0x47d9, 0x0000 }, + { 0x47da, 0x0000 }, + { 0x47db, 0x0000 }, + { 0x47dc, 0x0000 }, + { 0x47dd, 0x0000 }, + { 0x47de, 0x0000 }, + { 0x47df, 0x0000 }, + { 0x47e0, 0x0000 }, + { 0x47e1, 0x0000 }, + { 0x47e2, 0x0000 }, + { 0x47e3, 0x0000 }, + { 0x47e4, 0x0000 }, + { 0x47e5, 0x0000 }, + { 0x47e6, 0x0000 }, + { 0x47e7, 0x0000 }, + { 0x47e8, 0x0000 }, + { 0x47e9, 0x0000 }, + { 0x47ea, 0x0000 }, + { 0x47eb, 0x0000 }, + { 0x47ec, 0x0000 }, + { 0x47ed, 0x0000 }, + { 0x47ee, 0x0000 }, + { 0x47ef, 0x0000 }, + { 0x47f0, 0x0000 }, + { 0x47f1, 0x0000 }, + { 0x47f2, 0x0000 }, + { 0x47f3, 0x0000 }, + { 0x47f4, 0x0000 }, + { 0x47f5, 0x0000 }, + { 0x47f6, 0x0000 }, + { 0x47f7, 0x0000 }, + { 0x47f8, 0x0000 }, + { 0x47f9, 0x0000 }, + { 0x47fa, 0x0000 }, + { 0x47fb, 0x0000 }, + { 0x47fc, 0x0000 }, + { 0x47fd, 0x0000 }, + { 0x47fe, 0x0000 }, + { 0x47ff, 0x0000 }, + { 0x4800, 0x0000 }, + { 0x4801, 0x0000 }, + { 0x4802, 0x0000 }, + { 0x4803, 0x0000 }, + { 0x4804, 0x0000 }, + { 0x4805, 0x0000 }, + { 0x4806, 0x0000 }, + { 0x4807, 0x0000 }, + { 0x4808, 0x0000 }, + { 0x4809, 0x0000 }, + { 0x480a, 0x0000 }, + { 0x480b, 0x0000 }, + { 0x480c, 0x0000 }, + { 0x480d, 0x0000 }, + { 0x480e, 0x0000 }, + { 0x480f, 0x0000 }, + { 0x4810, 0x0000 }, + { 0x4811, 0x0000 }, + { 0x4812, 0x0000 }, + { 0x4813, 0x0000 }, + { 0x4814, 0x0000 }, + { 0x4815, 0x0000 }, + { 0x4816, 0x0000 }, + { 0x4817, 0x0000 }, + { 0x4818, 0x0000 }, + { 0x4819, 0x0000 }, + { 0x481a, 0x0000 }, + { 0x481b, 0x0000 }, + { 0x481c, 0x0000 }, + { 0x481d, 0x0000 }, + { 0x481e, 0x0000 }, + { 0x481f, 0x0000 }, + { 0x4820, 0x0000 }, + { 0x4821, 0x0000 }, + { 0x4822, 0x0000 }, + { 0x4823, 0x0000 }, + { 0x4824, 0x0000 }, + { 0x4825, 0x0000 }, + { 0x4826, 0x0000 }, + { 0x4827, 0x0000 }, + { 0x4828, 0x0000 }, + { 0x4829, 0x0000 }, + { 0x482a, 0x0000 }, + { 0x482b, 0x0000 }, + { 0x482c, 0x0000 }, + { 0x482d, 0x0000 }, + { 0x482e, 0x0000 }, + { 0x482f, 0x0000 }, + { 0x4830, 0x0000 }, + { 0x4831, 0x0000 }, + { 0x4832, 0x0000 }, + { 0x4833, 0x0000 }, + { 0x4834, 0x0000 }, + { 0x4835, 0x0000 }, + { 0x4836, 0x0000 }, + { 0x4837, 0x0000 }, + { 0x4838, 0x0000 }, + { 0x4839, 0x0000 }, + { 0x483a, 0x0000 }, + { 0x483b, 0x0000 }, + { 0x483c, 0x0000 }, + { 0x483d, 0x0000 }, + { 0x483e, 0x0000 }, + { 0x483f, 0x0000 }, + { 0x4840, 0x0000 }, + { 0x4841, 0x0000 }, + { 0x4842, 0x0000 }, + { 0x4843, 0x0000 }, + { 0x4844, 0x0000 }, + { 0x4845, 0x0000 }, + { 0x4846, 0x0000 }, + { 0x4847, 0x0000 }, + { 0x4848, 0x0000 }, + { 0x4849, 0x0000 }, + { 0x484a, 0x0000 }, + { 0x484b, 0x0000 }, + { 0x484c, 0x0000 }, + { 0x484d, 0x0000 }, + { 0x484e, 0x0000 }, + { 0x484f, 0x0000 }, + { 0x4850, 0x0000 }, + { 0x4851, 0x0000 }, + { 0x4852, 0x0000 }, + { 0x4853, 0x0000 }, + { 0x4854, 0x0000 }, + { 0x4855, 0x0000 }, + { 0x4856, 0x0000 }, + { 0x4857, 0x0000 }, + { 0x4858, 0x0000 }, + { 0x4859, 0x0000 }, + { 0x485a, 0x0000 }, + { 0x485b, 0x0000 }, + { 0x485c, 0x0000 }, + { 0x485d, 0x0000 }, + { 0x485e, 0x0000 }, + { 0x485f, 0x0000 }, + { 0x4860, 0x0000 }, + { 0x4861, 0x0000 }, + { 0x4862, 0x0000 }, + { 0x4863, 0x0000 }, + { 0x4864, 0x0000 }, + { 0x4865, 0x0000 }, + { 0x4866, 0x0000 }, + { 0x4867, 0x0000 }, + { 0x4868, 0x0000 }, + { 0x4869, 0x0000 }, + { 0x486a, 0x0000 }, + { 0x486b, 0x0000 }, + { 0x486c, 0x0000 }, + { 0x486d, 0x0000 }, + { 0x486e, 0x0000 }, + { 0x486f, 0x0000 }, + { 0x4870, 0x0000 }, + { 0x4871, 0x0000 }, + { 0x4872, 0x0000 }, + { 0x4873, 0x0000 }, + { 0x4874, 0x0000 }, + { 0x4875, 0x0000 }, + { 0x4876, 0x0000 }, + { 0x4877, 0x0000 }, + { 0x4878, 0x0000 }, + { 0x4879, 0x0000 }, + { 0x487a, 0x0000 }, + { 0x487b, 0x0000 }, + { 0x487c, 0x0000 }, + { 0x487d, 0x0000 }, + { 0x487e, 0x0000 }, + { 0x487f, 0x0000 }, + { 0x4880, 0x0000 }, + { 0x4881, 0x0000 }, + { 0x4882, 0x0000 }, + { 0x4883, 0x0000 }, + { 0x4884, 0x0000 }, + { 0x4885, 0x0000 }, + { 0x4886, 0x0000 }, + { 0x4887, 0x0000 }, + { 0x4888, 0x0000 }, + { 0x4889, 0x0000 }, + { 0x488a, 0x0000 }, + { 0x488b, 0x0000 }, + { 0x488c, 0x0000 }, + { 0x488d, 0x0000 }, + { 0x488e, 0x0000 }, + { 0x488f, 0x0000 }, + { 0x4890, 0x0000 }, + { 0x4891, 0x0000 }, + { 0x4892, 0x0000 }, + { 0x4893, 0x0000 }, + { 0x4894, 0x0000 }, + { 0x4895, 0x0000 }, + { 0x4896, 0x0000 }, + { 0x4897, 0x0000 }, + { 0x4898, 0x0000 }, + { 0x4899, 0x0000 }, + { 0x489a, 0x0000 }, + { 0x489b, 0x0000 }, + { 0x489c, 0x0000 }, + { 0x489d, 0x0000 }, + { 0x489e, 0x0000 }, + { 0x489f, 0x0000 }, + { 0x48a0, 0x0000 }, + { 0x48a1, 0x0000 }, + { 0x48a2, 0x0000 }, + { 0x48a3, 0x0000 }, + { 0x48a4, 0x0000 }, + { 0x48a5, 0x0000 }, + { 0x48a6, 0x0000 }, + { 0x48a7, 0x0000 }, + { 0x48a8, 0x0000 }, + { 0x48a9, 0x0000 }, + { 0x48aa, 0x0000 }, + { 0x48ab, 0x0000 }, + { 0x48ac, 0x0000 }, + { 0x48ad, 0x0000 }, + { 0x48ae, 0x0000 }, + { 0x48af, 0x0000 }, + { 0x48b0, 0x0000 }, + { 0x48b1, 0x0000 }, + { 0x48b2, 0x0000 }, + { 0x48b3, 0x0000 }, + { 0x48b4, 0x0000 }, + { 0x48b5, 0x0000 }, + { 0x48b6, 0x0000 }, + { 0x48b7, 0x0000 }, + { 0x48b8, 0x0000 }, + { 0x48b9, 0x0000 }, + { 0x48ba, 0x0000 }, + { 0x48bb, 0x0000 }, + { 0x48bc, 0x0000 }, + { 0x48bd, 0x0000 }, + { 0x48be, 0x0000 }, + { 0x48bf, 0x0000 }, + { 0x48c0, 0x0000 }, + { 0x48c1, 0x0000 }, + { 0x48c2, 0x0000 }, + { 0x48c3, 0x0000 }, + { 0x48c4, 0x0000 }, + { 0x48c5, 0x0000 }, + { 0x48c6, 0x0000 }, + { 0x48c7, 0x0000 }, + { 0x48c8, 0x0000 }, + { 0x48c9, 0x0000 }, + { 0x48ca, 0x0000 }, + { 0x48cb, 0x0000 }, + { 0x48cc, 0x0000 }, + { 0x48cd, 0x0000 }, + { 0x48ce, 0x0000 }, + { 0x48cf, 0x0000 }, + { 0x48d0, 0x0000 }, + { 0x48d1, 0x0000 }, + { 0x48d2, 0x0000 }, + { 0x48d3, 0x0000 }, + { 0x48d4, 0x0000 }, + { 0x48d5, 0x0000 }, + { 0x48d6, 0x0000 }, + { 0x48d7, 0x0000 }, + { 0x48d8, 0x0000 }, + { 0x48d9, 0x0000 }, + { 0x48da, 0x0000 }, + { 0x48db, 0x0000 }, + { 0x48dc, 0x0000 }, + { 0x48dd, 0x0000 }, + { 0x48de, 0x0000 }, + { 0x48df, 0x0000 }, + { 0x48e0, 0x0000 }, + { 0x48e1, 0x0000 }, + { 0x48e2, 0x0000 }, + { 0x48e3, 0x0000 }, + { 0x48e4, 0x0000 }, + { 0x48e5, 0x0000 }, + { 0x48e6, 0x0000 }, + { 0x48e7, 0x0000 }, + { 0x48e8, 0x0000 }, + { 0x48e9, 0x0000 }, + { 0x48ea, 0x0000 }, + { 0x48eb, 0x0000 }, + { 0x48ec, 0x0000 }, + { 0x48ed, 0x0000 }, + { 0x48ee, 0x0000 }, + { 0x48ef, 0x0000 }, + { 0x48f0, 0x0000 }, + { 0x48f1, 0x0000 }, + { 0x48f2, 0x0000 }, + { 0x48f3, 0x0000 }, + { 0x48f4, 0x0000 }, + { 0x48f5, 0x0000 }, + { 0x48f6, 0x0000 }, + { 0x48f7, 0x0000 }, + { 0x48f8, 0x0000 }, + { 0x48f9, 0x0000 }, + { 0x48fa, 0x0000 }, + { 0x48fb, 0x0000 }, + { 0x48fc, 0x0000 }, + { 0x48fd, 0x0000 }, + { 0x48fe, 0x0000 }, + { 0x48ff, 0x0000 }, + { 0x4900, 0x0000 }, + { 0x4901, 0x0000 }, + { 0x4902, 0x0000 }, + { 0x4903, 0x0000 }, + { 0x4904, 0x0000 }, + { 0x4905, 0x0000 }, + { 0x4906, 0x0000 }, + { 0x4907, 0x0000 }, + { 0x4908, 0x0000 }, + { 0x4909, 0x0000 }, + { 0x490a, 0x0000 }, + { 0x490b, 0x0000 }, + { 0x490c, 0x0000 }, + { 0x490d, 0x0000 }, + { 0x490e, 0x0000 }, + { 0x490f, 0x0000 }, + { 0x4910, 0x0000 }, + { 0x4911, 0x0000 }, + { 0x4912, 0x0000 }, + { 0x4913, 0x0000 }, + { 0x4914, 0x0000 }, + { 0x4915, 0x0000 }, + { 0x4916, 0x0000 }, + { 0x4917, 0x0000 }, + { 0x4918, 0x0000 }, + { 0x4919, 0x0000 }, + { 0x491a, 0x0000 }, + { 0x491b, 0x0000 }, + { 0x491c, 0x0000 }, + { 0x491d, 0x0000 }, + { 0x491e, 0x0000 }, + { 0x491f, 0x0000 }, + { 0x4920, 0x0000 }, + { 0x4921, 0x0000 }, + { 0x4922, 0x0000 }, + { 0x4923, 0x0000 }, + { 0x4924, 0x0000 }, + { 0x4925, 0x0000 }, + { 0x4926, 0x0000 }, + { 0x4927, 0x0000 }, + { 0x4928, 0x0000 }, + { 0x4929, 0x0000 }, + { 0x492a, 0x0000 }, + { 0x492b, 0x0000 }, + { 0x492c, 0x0000 }, + { 0x492d, 0x0000 }, + { 0x492e, 0x0000 }, + { 0x492f, 0x0000 }, + { 0x4930, 0x0000 }, + { 0x4931, 0x0000 }, + { 0x4932, 0x0000 }, + { 0x4933, 0x0000 }, + { 0x4934, 0x0000 }, + { 0x4935, 0x0000 }, + { 0x4936, 0x0000 }, + { 0x4937, 0x0000 }, + { 0x4938, 0x0000 }, + { 0x4939, 0x0000 }, + { 0x493a, 0x0000 }, + { 0x493b, 0x0000 }, + { 0x493c, 0x0000 }, + { 0x493d, 0x0000 }, + { 0x493e, 0x0000 }, + { 0x493f, 0x0000 }, + { 0x4940, 0x0000 }, + { 0x4941, 0x0000 }, + { 0x4942, 0x0000 }, + { 0x4943, 0x0000 }, + { 0x4944, 0x0000 }, + { 0x4945, 0x0000 }, + { 0x4946, 0x0000 }, + { 0x4947, 0x0000 }, + { 0x4948, 0x0000 }, + { 0x4949, 0x0000 }, + { 0x494a, 0x0000 }, + { 0x494b, 0x0000 }, + { 0x494c, 0x0000 }, + { 0x494d, 0x0000 }, + { 0x494e, 0x0000 }, + { 0x494f, 0x0000 }, + { 0x4950, 0x0000 }, + { 0x4951, 0x0000 }, + { 0x4952, 0x0000 }, + { 0x4953, 0x0000 }, + { 0x4954, 0x0000 }, + { 0x4955, 0x0000 }, + { 0x4956, 0x0000 }, + { 0x4957, 0x0000 }, + { 0x4958, 0x0000 }, + { 0x4959, 0x0000 }, + { 0x495a, 0x0000 }, + { 0x495b, 0x0000 }, + { 0x495c, 0x0000 }, + { 0x495d, 0x0000 }, + { 0x495e, 0x0000 }, + { 0x495f, 0x0000 }, + { 0x4960, 0x0000 }, + { 0x4961, 0x0000 }, + { 0x4962, 0x0000 }, + { 0x4963, 0x0000 }, + { 0x4964, 0x0000 }, + { 0x4965, 0x0000 }, + { 0x4966, 0x0000 }, + { 0x4967, 0x0000 }, + { 0x4968, 0x0000 }, + { 0x4969, 0x0000 }, + { 0x496a, 0x0000 }, + { 0x496b, 0x0000 }, + { 0x496c, 0x0000 }, + { 0x496d, 0x0000 }, + { 0x496e, 0x0000 }, + { 0x496f, 0x0000 }, + { 0x4970, 0x0000 }, + { 0x4971, 0x0000 }, + { 0x4972, 0x0000 }, + { 0x4973, 0x0000 }, + { 0x4974, 0x0000 }, + { 0x4975, 0x0000 }, + { 0x4976, 0x0000 }, + { 0x4977, 0x0000 }, + { 0x4978, 0x0000 }, + { 0x4979, 0x0000 }, + { 0x497a, 0x0000 }, + { 0x497b, 0x0000 }, + { 0x497c, 0x0000 }, + { 0x497d, 0x0000 }, + { 0x497e, 0x0000 }, + { 0x497f, 0x0000 }, + { 0x4980, 0x0000 }, + { 0x4981, 0x0000 }, + { 0x4982, 0x0000 }, + { 0x4983, 0x0000 }, + { 0x4984, 0x0000 }, + { 0x4985, 0x0000 }, + { 0x4986, 0x0000 }, + { 0x4987, 0x0000 }, + { 0x4988, 0x0000 }, + { 0x4989, 0x0000 }, + { 0x498a, 0x0000 }, + { 0x498b, 0x0000 }, + { 0x498c, 0x0000 }, + { 0x498d, 0x0000 }, + { 0x498e, 0x0000 }, + { 0x498f, 0x0000 }, + { 0x4990, 0x0000 }, + { 0x4991, 0x0000 }, + { 0x4992, 0x0000 }, + { 0x4993, 0x0000 }, + { 0x4994, 0x0000 }, + { 0x4995, 0x0000 }, + { 0x4996, 0x0000 }, + { 0x4997, 0x0000 }, + { 0x4998, 0x0000 }, + { 0x4999, 0x0000 }, + { 0x499a, 0x0000 }, + { 0x499b, 0x0000 }, + { 0x499c, 0x0000 }, + { 0x499d, 0x0000 }, + { 0x499e, 0x0000 }, + { 0x499f, 0x0000 }, + { 0x49a0, 0x0000 }, + { 0x49a1, 0x0000 }, + { 0x49a2, 0x0000 }, + { 0x49a3, 0x0000 }, + { 0x49a4, 0x0000 }, + { 0x49a5, 0x0000 }, + { 0x49a6, 0x0000 }, + { 0x49a7, 0x0000 }, + { 0x49a8, 0x0000 }, + { 0x49a9, 0x0000 }, + { 0x49aa, 0x0000 }, + { 0x49ab, 0x0000 }, + { 0x49ac, 0x0000 }, + { 0x49ad, 0x0000 }, + { 0x49ae, 0x0000 }, + { 0x49af, 0x0000 }, + { 0x49b0, 0x0000 }, + { 0x49b1, 0x0000 }, + { 0x49b2, 0x0000 }, + { 0x49b3, 0x0000 }, + { 0x49b4, 0x0000 }, + { 0x49b5, 0x0000 }, + { 0x49b6, 0x0000 }, + { 0x49b7, 0x0000 }, + { 0x49b8, 0x0000 }, + { 0x49b9, 0x0000 }, + { 0x49ba, 0x0000 }, + { 0x49bb, 0x0000 }, + { 0x49bc, 0x0000 }, + { 0x49bd, 0x0000 }, + { 0x49be, 0x0000 }, + { 0x49bf, 0x0000 }, + { 0x49c0, 0x0000 }, + { 0x49c1, 0x0000 }, + { 0x49c2, 0x0000 }, + { 0x49c3, 0x0000 }, + { 0x49c4, 0x0000 }, + { 0x49c5, 0x0000 }, + { 0x49c6, 0x0000 }, + { 0x49c7, 0x0000 }, + { 0x49c8, 0x0000 }, + { 0x49c9, 0x0000 }, + { 0x49ca, 0x0000 }, + { 0x49cb, 0x0000 }, + { 0x49cc, 0x0000 }, + { 0x49cd, 0x0000 }, + { 0x49ce, 0x0000 }, + { 0x49cf, 0x0000 }, + { 0x49d0, 0x0000 }, + { 0x49d1, 0x0000 }, + { 0x49d2, 0x0000 }, + { 0x49d3, 0x0000 }, + { 0x49d4, 0x0000 }, + { 0x49d5, 0x0000 }, + { 0x49d6, 0x0000 }, + { 0x49d7, 0x0000 }, + { 0x49d8, 0x0000 }, + { 0x49d9, 0x0000 }, + { 0x49da, 0x0000 }, + { 0x49db, 0x0000 }, + { 0x49dc, 0x0000 }, + { 0x49dd, 0x0000 }, + { 0x49de, 0x0000 }, + { 0x49df, 0x0000 }, + { 0x49e0, 0x0000 }, + { 0x49e1, 0x0000 }, + { 0x49e2, 0x0000 }, + { 0x49e3, 0x0000 }, + { 0x49e4, 0x0000 }, + { 0x49e5, 0x0000 }, + { 0x49e6, 0x0000 }, + { 0x49e7, 0x0000 }, + { 0x49e8, 0x0000 }, + { 0x49e9, 0x0000 }, + { 0x49ea, 0x0000 }, + { 0x49eb, 0x0000 }, + { 0x49ec, 0x0000 }, + { 0x49ed, 0x0000 }, + { 0x49ee, 0x0000 }, + { 0x49ef, 0x0000 }, + { 0x49f0, 0x0000 }, + { 0x49f1, 0x0000 }, + { 0x49f2, 0x0000 }, + { 0x49f3, 0x0000 }, + { 0x49f4, 0x0000 }, + { 0x49f5, 0x0000 }, + { 0x49f6, 0x0000 }, + { 0x49f7, 0x0000 }, + { 0x49f8, 0x0000 }, + { 0x49f9, 0x0000 }, + { 0x49fa, 0x0000 }, + { 0x49fb, 0x0000 }, + { 0x49fc, 0x0000 }, + { 0x49fd, 0x0000 }, + { 0x49fe, 0x0000 }, + { 0x49ff, 0x0000 }, + { 0x4a00, 0x0000 }, + { 0x4a01, 0x0000 }, + { 0x4a02, 0x0000 }, + { 0x4a03, 0x0000 }, + { 0x4a04, 0x0000 }, + { 0x4a05, 0x0000 }, + { 0x4a06, 0x0000 }, + { 0x4a07, 0x0000 }, + { 0x4a08, 0x0000 }, + { 0x4a09, 0x0000 }, + { 0x4a0a, 0x0000 }, + { 0x4a0b, 0x0000 }, + { 0x4a0c, 0x0000 }, + { 0x4a0d, 0x0000 }, + { 0x4a0e, 0x0000 }, + { 0x4a0f, 0x0000 }, + { 0x4a10, 0x0000 }, + { 0x4a11, 0x0000 }, + { 0x4a12, 0x0000 }, + { 0x4a13, 0x0000 }, + { 0x4a14, 0x0000 }, + { 0x4a15, 0x0000 }, + { 0x4a16, 0x0000 }, + { 0x4a17, 0x0000 }, + { 0x4a18, 0x0000 }, + { 0x4a19, 0x0000 }, + { 0x4a1a, 0x0000 }, + { 0x4a1b, 0x0000 }, + { 0x4a1c, 0x0000 }, + { 0x4a1d, 0x0000 }, + { 0x4a1e, 0x0000 }, + { 0x4a1f, 0x0000 }, + { 0x4a20, 0x0000 }, + { 0x4a21, 0x0000 }, + { 0x4a22, 0x0000 }, + { 0x4a23, 0x0000 }, + { 0x4a24, 0x0000 }, + { 0x4a25, 0x0000 }, + { 0x4a26, 0x0000 }, + { 0x4a27, 0x0000 }, + { 0x4a28, 0x0000 }, + { 0x4a29, 0x0000 }, + { 0x4a2a, 0x0000 }, + { 0x4a2b, 0x0000 }, + { 0x4a2c, 0x0000 }, + { 0x4a2d, 0x0000 }, + { 0x4a2e, 0x0000 }, + { 0x4a2f, 0x0000 }, + { 0x4a30, 0x0000 }, + { 0x4a31, 0x0000 }, + { 0x4a32, 0x0000 }, + { 0x4a33, 0x0000 }, + { 0x4a34, 0x0000 }, + { 0x4a35, 0x0000 }, + { 0x4a36, 0x0000 }, + { 0x4a37, 0x0000 }, + { 0x4a38, 0x0000 }, + { 0x4a39, 0x0000 }, + { 0x4a3a, 0x0000 }, + { 0x4a3b, 0x0000 }, + { 0x4a3c, 0x0000 }, + { 0x4a3d, 0x0000 }, + { 0x4a3e, 0x0000 }, + { 0x4a3f, 0x0000 }, + { 0x4a40, 0x0000 }, + { 0x4a41, 0x0000 }, + { 0x4a42, 0x0000 }, + { 0x4a43, 0x0000 }, + { 0x4a44, 0x0000 }, + { 0x4a45, 0x0000 }, + { 0x4a46, 0x0000 }, + { 0x4a47, 0x0000 }, + { 0x4a48, 0x0000 }, + { 0x4a49, 0x0000 }, + { 0x4a4a, 0x0000 }, + { 0x4a4b, 0x0000 }, + { 0x4a4c, 0x0000 }, + { 0x4a4d, 0x0000 }, + { 0x4a4e, 0x0000 }, + { 0x4a4f, 0x0000 }, + { 0x4a50, 0x0000 }, + { 0x4a51, 0x0000 }, + { 0x4a52, 0x0000 }, + { 0x4a53, 0x0000 }, + { 0x4a54, 0x0000 }, + { 0x4a55, 0x0000 }, + { 0x4a56, 0x0000 }, + { 0x4a57, 0x0000 }, + { 0x4a58, 0x0000 }, + { 0x4a59, 0x0000 }, + { 0x4a5a, 0x0000 }, + { 0x4a5b, 0x0000 }, + { 0x4a5c, 0x0000 }, + { 0x4a5d, 0x0000 }, + { 0x4a5e, 0x0000 }, + { 0x4a5f, 0x0000 }, + { 0x4a60, 0x0000 }, + { 0x4a61, 0x0000 }, + { 0x4a62, 0x0000 }, + { 0x4a63, 0x0000 }, + { 0x4a64, 0x0000 }, + { 0x4a65, 0x0000 }, + { 0x4a66, 0x0000 }, + { 0x4a67, 0x0000 }, + { 0x4a68, 0x0000 }, + { 0x4a69, 0x0000 }, + { 0x4a6a, 0x0000 }, + { 0x4a6b, 0x0000 }, + { 0x4a6c, 0x0000 }, + { 0x4a6d, 0x0000 }, + { 0x4a6e, 0x0000 }, + { 0x4a6f, 0x0000 }, + { 0x4a70, 0x0000 }, + { 0x4a71, 0x0000 }, + { 0x4a72, 0x0000 }, + { 0x4a73, 0x0000 }, + { 0x4a74, 0x0000 }, + { 0x4a75, 0x0000 }, + { 0x4a76, 0x0000 }, + { 0x4a77, 0x0000 }, + { 0x4a78, 0x0000 }, + { 0x4a79, 0x0000 }, + { 0x4a7a, 0x0000 }, + { 0x4a7b, 0x0000 }, + { 0x4a7c, 0x0000 }, + { 0x4a7d, 0x0000 }, + { 0x4a7e, 0x0000 }, + { 0x4a7f, 0x0000 }, + { 0x4a80, 0x0000 }, + { 0x4a81, 0x0000 }, + { 0x4a82, 0x0000 }, + { 0x4a83, 0x0000 }, + { 0x4a84, 0x0000 }, + { 0x4a85, 0x0000 }, + { 0x4a86, 0x0000 }, + { 0x4a87, 0x0000 }, + { 0x4a88, 0x0000 }, + { 0x4a89, 0x0000 }, + { 0x4a8a, 0x0000 }, + { 0x4a8b, 0x0000 }, + { 0x4a8c, 0x0000 }, + { 0x4a8d, 0x0000 }, + { 0x4a8e, 0x0000 }, + { 0x4a8f, 0x0000 }, + { 0x4a90, 0x0000 }, + { 0x4a91, 0x0000 }, + { 0x4a92, 0x0000 }, + { 0x4a93, 0x0000 }, + { 0x4a94, 0x0000 }, + { 0x4a95, 0x0000 }, + { 0x4a96, 0x0000 }, + { 0x4a97, 0x0000 }, + { 0x4a98, 0x0000 }, + { 0x4a99, 0x0000 }, + { 0x4a9a, 0x0000 }, + { 0x4a9b, 0x0000 }, + { 0x4a9c, 0x0000 }, + { 0x4a9d, 0x0000 }, + { 0x4a9e, 0x0000 }, + { 0x4a9f, 0x0000 }, + { 0x4aa0, 0x0000 }, + { 0x4aa1, 0x0000 }, + { 0x4aa2, 0x0000 }, + { 0x4aa3, 0x0000 }, + { 0x4aa4, 0x0000 }, + { 0x4aa5, 0x0000 }, + { 0x4aa6, 0x0000 }, + { 0x4aa7, 0x0000 }, + { 0x4aa8, 0x0000 }, + { 0x4aa9, 0x0000 }, + { 0x4aaa, 0x0000 }, + { 0x4aab, 0x0000 }, + { 0x4aac, 0x0000 }, + { 0x4aad, 0x0000 }, + { 0x4aae, 0x0000 }, + { 0x4aaf, 0x0000 }, + { 0x4ab0, 0x0000 }, + { 0x4ab1, 0x0000 }, + { 0x4ab2, 0x0000 }, + { 0x4ab3, 0x0000 }, + { 0x4ab4, 0x0000 }, + { 0x4ab5, 0x0000 }, + { 0x4ab6, 0x0000 }, + { 0x4ab7, 0x0000 }, + { 0x4ab8, 0x0000 }, + { 0x4ab9, 0x0000 }, + { 0x4aba, 0x0000 }, + { 0x4abb, 0x0000 }, + { 0x4abc, 0x0000 }, + { 0x4abd, 0x0000 }, + { 0x4abe, 0x0000 }, + { 0x4abf, 0x0000 }, + { 0x4ac0, 0x0000 }, + { 0x4ac1, 0x0000 }, + { 0x4ac2, 0x0000 }, + { 0x4ac3, 0x0000 }, + { 0x4ac4, 0x0000 }, + { 0x4ac5, 0x0000 }, + { 0x4ac6, 0x0000 }, + { 0x4ac7, 0x0000 }, + { 0x4ac8, 0x0000 }, + { 0x4ac9, 0x0000 }, + { 0x4aca, 0x0000 }, + { 0x4acb, 0x0000 }, + { 0x4acc, 0x0000 }, + { 0x4acd, 0x0000 }, + { 0x4ace, 0x0000 }, + { 0x4acf, 0x0000 }, + { 0x4ad0, 0x0000 }, + { 0x4ad1, 0x0000 }, + { 0x4ad2, 0x0000 }, + { 0x4ad3, 0x0000 }, + { 0x4ad4, 0x0000 }, + { 0x4ad5, 0x0000 }, + { 0x4ad6, 0x0000 }, + { 0x4ad7, 0x0000 }, + { 0x4ad8, 0x0000 }, + { 0x4ad9, 0x0000 }, + { 0x4ada, 0x0000 }, + { 0x4adb, 0x0000 }, + { 0x4adc, 0x0000 }, + { 0x4add, 0x0000 }, + { 0x4ade, 0x0000 }, + { 0x4adf, 0x0000 }, + { 0x4ae0, 0x0000 }, + { 0x4ae1, 0x0000 }, + { 0x4ae2, 0x0000 }, + { 0x4ae3, 0x0000 }, + { 0x4ae4, 0x0000 }, + { 0x4ae5, 0x0000 }, + { 0x4ae6, 0x0000 }, + { 0x4ae7, 0x0000 }, + { 0x4ae8, 0x0000 }, + { 0x4ae9, 0x0000 }, + { 0x4aea, 0x0000 }, + { 0x4aeb, 0x0000 }, + { 0x4aec, 0x0000 }, + { 0x4aed, 0x0000 }, + { 0x4aee, 0x0000 }, + { 0x4aef, 0x0000 }, + { 0x4af0, 0x0000 }, + { 0x4af1, 0x0000 }, + { 0x4af2, 0x0000 }, + { 0x4af3, 0x0000 }, + { 0x4af4, 0x0000 }, + { 0x4af5, 0x0000 }, + { 0x4af6, 0x0000 }, + { 0x4af7, 0x0000 }, + { 0x4af8, 0x0000 }, + { 0x4af9, 0x0000 }, + { 0x4afa, 0x0000 }, + { 0x4afb, 0x0000 }, + { 0x4afc, 0x0000 }, + { 0x4afd, 0x0000 }, + { 0x4afe, 0x0000 }, + { 0x4aff, 0x0000 }, + { 0x4b00, 0x0000 }, + { 0x4b01, 0x0000 }, + { 0x4b02, 0x0000 }, + { 0x4b03, 0x0000 }, + { 0x4b04, 0x0000 }, + { 0x4b05, 0x0000 }, + { 0x4b06, 0x0000 }, + { 0x4b07, 0x0000 }, + { 0x4b08, 0x0000 }, + { 0x4b09, 0x0000 }, + { 0x4b0a, 0x0000 }, + { 0x4b0b, 0x0000 }, + { 0x4b0c, 0x0000 }, + { 0x4b0d, 0x0000 }, + { 0x4b0e, 0x0000 }, + { 0x4b0f, 0x0000 }, + { 0x4b10, 0x0000 }, + { 0x4b11, 0x0000 }, + { 0x4b12, 0x0000 }, + { 0x4b13, 0x0000 }, + { 0x4b14, 0x0000 }, + { 0x4b15, 0x0000 }, + { 0x4b16, 0x0000 }, + { 0x4b17, 0x0000 }, + { 0x4b18, 0x0000 }, + { 0x4b19, 0x0000 }, + { 0x4b1a, 0x0000 }, + { 0x4b1b, 0x0000 }, + { 0x4b1c, 0x0000 }, + { 0x4b1d, 0x0000 }, + { 0x4b1e, 0x0000 }, + { 0x4b1f, 0x0000 }, + { 0x4b20, 0x0000 }, + { 0x4b21, 0x0000 }, + { 0x4b22, 0x0000 }, + { 0x4b23, 0x0000 }, + { 0x4b24, 0x0000 }, + { 0x4b25, 0x0000 }, + { 0x4b26, 0x0000 }, + { 0x4b27, 0x0000 }, + { 0x4b28, 0x0000 }, + { 0x4b29, 0x0000 }, + { 0x4b2a, 0x0000 }, + { 0x4b2b, 0x0000 }, + { 0x4b2c, 0x0000 }, + { 0x4b2d, 0x0000 }, + { 0x4b2e, 0x0000 }, + { 0x4b2f, 0x0000 }, + { 0x4b30, 0x0000 }, + { 0x4b31, 0x0000 }, + { 0x4b32, 0x0000 }, + { 0x4b33, 0x0000 }, + { 0x4b34, 0x0000 }, + { 0x4b35, 0x0000 }, + { 0x4b36, 0x0000 }, + { 0x4b37, 0x0000 }, + { 0x4b38, 0x0000 }, + { 0x4b39, 0x0000 }, + { 0x4b3a, 0x0000 }, + { 0x4b3b, 0x0000 }, + { 0x4b3c, 0x0000 }, + { 0x4b3d, 0x0000 }, + { 0x4b3e, 0x0000 }, + { 0x4b3f, 0x0000 }, + { 0x4b40, 0x0000 }, + { 0x4b41, 0x0000 }, + { 0x4b42, 0x0000 }, + { 0x4b43, 0x0000 }, + { 0x4b44, 0x0000 }, + { 0x4b45, 0x0000 }, + { 0x4b46, 0x0000 }, + { 0x4b47, 0x0000 }, + { 0x4b48, 0x0000 }, + { 0x4b49, 0x0000 }, + { 0x4b4a, 0x0000 }, + { 0x4b4b, 0x0000 }, + { 0x4b4c, 0x0000 }, + { 0x4b4d, 0x0000 }, + { 0x4b4e, 0x0000 }, + { 0x4b4f, 0x0000 }, + { 0x4b50, 0x0000 }, + { 0x4b51, 0x0000 }, + { 0x4b52, 0x0000 }, + { 0x4b53, 0x0000 }, + { 0x4b54, 0x0000 }, + { 0x4b55, 0x0000 }, + { 0x4b56, 0x0000 }, + { 0x4b57, 0x0000 }, + { 0x4b58, 0x0000 }, + { 0x4b59, 0x0000 }, + { 0x4b5a, 0x0000 }, + { 0x4b5b, 0x0000 }, + { 0x4b5c, 0x0000 }, + { 0x4b5d, 0x0000 }, + { 0x4b5e, 0x0000 }, + { 0x4b5f, 0x0000 }, + { 0x4b60, 0x0000 }, + { 0x4b61, 0x0000 }, + { 0x4b62, 0x0000 }, + { 0x4b63, 0x0000 }, + { 0x4b64, 0x0000 }, + { 0x4b65, 0x0000 }, + { 0x4b66, 0x0000 }, + { 0x4b67, 0x0000 }, + { 0x4b68, 0x0000 }, + { 0x4b69, 0x0000 }, + { 0x4b6a, 0x0000 }, + { 0x4b6b, 0x0000 }, + { 0x4b6c, 0x0000 }, + { 0x4b6d, 0x0000 }, + { 0x4b6e, 0x0000 }, + { 0x4b6f, 0x0000 }, + { 0x4b70, 0x0000 }, + { 0x4b71, 0x0000 }, + { 0x4b72, 0x0000 }, + { 0x4b73, 0x0000 }, + { 0x4b74, 0x0000 }, + { 0x4b75, 0x0000 }, + { 0x4b76, 0x0000 }, + { 0x4b77, 0x0000 }, + { 0x4b78, 0x0000 }, + { 0x4b79, 0x0000 }, + { 0x4b7a, 0x0000 }, + { 0x4b7b, 0x0000 }, + { 0x4b7c, 0x0000 }, + { 0x4b7d, 0x0000 }, + { 0x4b7e, 0x0000 }, + { 0x4b7f, 0x0000 }, + { 0x4b80, 0x0000 }, + { 0x4b81, 0x0000 }, + { 0x4b82, 0x0000 }, + { 0x4b83, 0x0000 }, + { 0x4b84, 0x0000 }, + { 0x4b85, 0x0000 }, + { 0x4b86, 0x0000 }, + { 0x4b87, 0x0000 }, + { 0x4b88, 0x0000 }, + { 0x4b89, 0x0000 }, + { 0x4b8a, 0x0000 }, + { 0x4b8b, 0x0000 }, + { 0x4b8c, 0x0000 }, + { 0x4b8d, 0x0000 }, + { 0x4b8e, 0x0000 }, + { 0x4b8f, 0x0000 }, + { 0x4b90, 0x0000 }, + { 0x4b91, 0x0000 }, + { 0x4b92, 0x0000 }, + { 0x4b93, 0x0000 }, + { 0x4b94, 0x0000 }, + { 0x4b95, 0x0000 }, + { 0x4b96, 0x0000 }, + { 0x4b97, 0x0000 }, + { 0x4b98, 0x0000 }, + { 0x4b99, 0x0000 }, + { 0x4b9a, 0x0000 }, + { 0x4b9b, 0x0000 }, + { 0x4b9c, 0x0000 }, + { 0x4b9d, 0x0000 }, + { 0x4b9e, 0x0000 }, + { 0x4b9f, 0x0000 }, + { 0x4ba0, 0x0000 }, + { 0x4ba1, 0x0000 }, + { 0x4ba2, 0x0000 }, + { 0x4ba3, 0x0000 }, + { 0x4ba4, 0x0000 }, + { 0x4ba5, 0x0000 }, + { 0x4ba6, 0x0000 }, + { 0x4ba7, 0x0000 }, + { 0x4ba8, 0x0000 }, + { 0x4ba9, 0x0000 }, + { 0x4baa, 0x0000 }, + { 0x4bab, 0x0000 }, + { 0x4bac, 0x0000 }, + { 0x4bad, 0x0000 }, + { 0x4bae, 0x0000 }, + { 0x4baf, 0x0000 }, + { 0x4bb0, 0x0000 }, + { 0x4bb1, 0x0000 }, + { 0x4bb2, 0x0000 }, + { 0x4bb3, 0x0000 }, + { 0x4bb4, 0x0000 }, + { 0x4bb5, 0x0000 }, + { 0x4bb6, 0x0000 }, + { 0x4bb7, 0x0000 }, + { 0x4bb8, 0x0000 }, + { 0x4bb9, 0x0000 }, + { 0x4bba, 0x0000 }, + { 0x4bbb, 0x0000 }, + { 0x4bbc, 0x0000 }, + { 0x4bbd, 0x0000 }, + { 0x4bbe, 0x0000 }, + { 0x4bbf, 0x0000 }, + { 0x4bc0, 0x0000 }, + { 0x4bc1, 0x0000 }, + { 0x4bc2, 0x0000 }, + { 0x4bc3, 0x0000 }, + { 0x4bc4, 0x0000 }, + { 0x4bc5, 0x0000 }, + { 0x4bc6, 0x0000 }, + { 0x4bc7, 0x0000 }, + { 0x4bc8, 0x0000 }, + { 0x4bc9, 0x0000 }, + { 0x4bca, 0x0000 }, + { 0x4bcb, 0x0000 }, + { 0x4bcc, 0x0000 }, + { 0x4bcd, 0x0000 }, + { 0x4bce, 0x0000 }, + { 0x4bcf, 0x0000 }, + { 0x4bd0, 0x0000 }, + { 0x4bd1, 0x0000 }, + { 0x4bd2, 0x0000 }, + { 0x4bd3, 0x0000 }, + { 0x4bd4, 0x0000 }, + { 0x4bd5, 0x0000 }, + { 0x4bd6, 0x0000 }, + { 0x4bd7, 0x0000 }, + { 0x4bd8, 0x0000 }, + { 0x4bd9, 0x0000 }, + { 0x4bda, 0x0000 }, + { 0x4bdb, 0x0000 }, + { 0x4bdc, 0x0000 }, + { 0x4bdd, 0x0000 }, + { 0x4bde, 0x0000 }, + { 0x4bdf, 0x0000 }, + { 0x4be0, 0x0000 }, + { 0x4be1, 0x0000 }, + { 0x4be2, 0x0000 }, + { 0x4be3, 0x0000 }, + { 0x4be4, 0x0000 }, + { 0x4be5, 0x0000 }, + { 0x4be6, 0x0000 }, + { 0x4be7, 0x0000 }, + { 0x4be8, 0x0000 }, + { 0x4be9, 0x0000 }, + { 0x4bea, 0x0000 }, + { 0x4beb, 0x0000 }, + { 0x4bec, 0x0000 }, + { 0x4bed, 0x0000 }, + { 0x4bee, 0x0000 }, + { 0x4bef, 0x0000 }, + { 0x4bf0, 0x0000 }, + { 0x4bf1, 0x0000 }, + { 0x4bf2, 0x0000 }, + { 0x4bf3, 0x0000 }, + { 0x4bf4, 0x0000 }, + { 0x4bf5, 0x0000 }, + { 0x4bf6, 0x0000 }, + { 0x4bf7, 0x0000 }, + { 0x4bf8, 0x0000 }, + { 0x4bf9, 0x0000 }, + { 0x4bfa, 0x0000 }, + { 0x4bfb, 0x0000 }, + { 0x4bfc, 0x0000 }, + { 0x4bfd, 0x0000 }, + { 0x4bfe, 0x0000 }, + { 0x4bff, 0x0000 }, + { 0x4c00, 0x0000 }, + { 0x4c01, 0x0000 }, + { 0x4c02, 0x0000 }, + { 0x4c03, 0x0000 }, + { 0x4c04, 0x0000 }, + { 0x4c05, 0x0000 }, + { 0x4c06, 0x0000 }, + { 0x4c07, 0x0000 }, + { 0x4c08, 0x0000 }, + { 0x4c09, 0x0000 }, + { 0x4c0a, 0x0000 }, + { 0x4c0b, 0x0000 }, + { 0x4c0c, 0x0000 }, + { 0x4c0d, 0x0000 }, + { 0x4c0e, 0x0000 }, + { 0x4c0f, 0x0000 }, + { 0x4c10, 0x0000 }, + { 0x4c11, 0x0000 }, + { 0x4c12, 0x0000 }, + { 0x4c13, 0x0000 }, + { 0x4c14, 0x0000 }, + { 0x4c15, 0x0000 }, + { 0x4c16, 0x0000 }, + { 0x4c17, 0x0000 }, + { 0x4c18, 0x0000 }, + { 0x4c19, 0x0000 }, + { 0x4c1a, 0x0000 }, + { 0x4c1b, 0x0000 }, + { 0x4c1c, 0x0000 }, + { 0x4c1d, 0x0000 }, + { 0x4c1e, 0x0000 }, + { 0x4c1f, 0x0000 }, + { 0x4c20, 0x0000 }, + { 0x4c21, 0x0000 }, + { 0x4c22, 0x0000 }, + { 0x4c23, 0x0000 }, + { 0x4c24, 0x0000 }, + { 0x4c25, 0x0000 }, + { 0x4c26, 0x0000 }, + { 0x4c27, 0x0000 }, + { 0x4c28, 0x0000 }, + { 0x4c29, 0x0000 }, + { 0x4c2a, 0x0000 }, + { 0x4c2b, 0x0000 }, + { 0x4c2c, 0x0000 }, + { 0x4c2d, 0x0000 }, + { 0x4c2e, 0x0000 }, + { 0x4c2f, 0x0000 }, + { 0x4c30, 0x0000 }, + { 0x4c31, 0x0000 }, + { 0x4c32, 0x0000 }, + { 0x4c33, 0x0000 }, + { 0x4c34, 0x0000 }, + { 0x4c35, 0x0000 }, + { 0x4c36, 0x0000 }, + { 0x4c37, 0x0000 }, + { 0x4c38, 0x0000 }, + { 0x4c39, 0x0000 }, + { 0x4c3a, 0x0000 }, + { 0x4c3b, 0x0000 }, + { 0x4c3c, 0x0000 }, + { 0x4c3d, 0x0000 }, + { 0x4c3e, 0x0000 }, + { 0x4c3f, 0x0000 }, + { 0x4c40, 0x0000 }, + { 0x4c41, 0x0000 }, + { 0x4c42, 0x0000 }, + { 0x4c43, 0x0000 }, + { 0x4c44, 0x0000 }, + { 0x4c45, 0x0000 }, + { 0x4c46, 0x0000 }, + { 0x4c47, 0x0000 }, + { 0x4c48, 0x0000 }, + { 0x4c49, 0x0000 }, + { 0x4c4a, 0x0000 }, + { 0x4c4b, 0x0000 }, + { 0x4c4c, 0x0000 }, + { 0x4c4d, 0x0000 }, + { 0x4c4e, 0x0000 }, + { 0x4c4f, 0x0000 }, + { 0x4c50, 0x0000 }, + { 0x4c51, 0x0000 }, + { 0x4c52, 0x0000 }, + { 0x4c53, 0x0000 }, + { 0x4c54, 0x0000 }, + { 0x4c55, 0x0000 }, + { 0x4c56, 0x0000 }, + { 0x4c57, 0x0000 }, + { 0x4c58, 0x0000 }, + { 0x4c59, 0x0000 }, + { 0x4c5a, 0x0000 }, + { 0x4c5b, 0x0000 }, + { 0x4c5c, 0x0000 }, + { 0x4c5d, 0x0000 }, + { 0x4c5e, 0x0000 }, + { 0x4c5f, 0x0000 }, + { 0x4c60, 0x0000 }, + { 0x4c61, 0x0000 }, + { 0x4c62, 0x0000 }, + { 0x4c63, 0x0000 }, + { 0x4c64, 0x0000 }, + { 0x4c65, 0x0000 }, + { 0x4c66, 0x0000 }, + { 0x4c67, 0x0000 }, + { 0x4c68, 0x0000 }, + { 0x4c69, 0x0000 }, + { 0x4c6a, 0x0000 }, + { 0x4c6b, 0x0000 }, + { 0x4c6c, 0x0000 }, + { 0x4c6d, 0x0000 }, + { 0x4c6e, 0x0000 }, + { 0x4c6f, 0x0000 }, + { 0x4c70, 0x0000 }, + { 0x4c71, 0x0000 }, + { 0x4c72, 0x0000 }, + { 0x4c73, 0x0000 }, + { 0x4c74, 0x0000 }, + { 0x4c75, 0x0000 }, + { 0x4c76, 0x0000 }, + { 0x4c77, 0x0000 }, + { 0x4c78, 0x0000 }, + { 0x4c79, 0x0000 }, + { 0x4c7a, 0x0000 }, + { 0x4c7b, 0x0000 }, + { 0x4c7c, 0x0000 }, + { 0x4c7d, 0x0000 }, + { 0x4c7e, 0x0000 }, + { 0x4c7f, 0x0000 }, + { 0x4c80, 0x0000 }, + { 0x4c81, 0x0000 }, + { 0x4c82, 0x0000 }, + { 0x4c83, 0x0000 }, + { 0x4c84, 0x0000 }, + { 0x4c85, 0x0000 }, + { 0x4c86, 0x0000 }, + { 0x4c87, 0x0000 }, + { 0x4c88, 0x0000 }, + { 0x4c89, 0x0000 }, + { 0x4c8a, 0x0000 }, + { 0x4c8b, 0x0000 }, + { 0x4c8c, 0x0000 }, + { 0x4c8d, 0x0000 }, + { 0x4c8e, 0x0000 }, + { 0x4c8f, 0x0000 }, + { 0x4c90, 0x0000 }, + { 0x4c91, 0x0000 }, + { 0x4c92, 0x0000 }, + { 0x4c93, 0x0000 }, + { 0x4c94, 0x0000 }, + { 0x4c95, 0x0000 }, + { 0x4c96, 0x0000 }, + { 0x4c97, 0x0000 }, + { 0x4c98, 0x0000 }, + { 0x4c99, 0x0000 }, + { 0x4c9a, 0x0000 }, + { 0x4c9b, 0x0000 }, + { 0x4c9c, 0x0000 }, + { 0x4c9d, 0x0000 }, + { 0x4c9e, 0x0000 }, + { 0x4c9f, 0x0000 }, + { 0x4ca0, 0x0000 }, + { 0x4ca1, 0x0000 }, + { 0x4ca2, 0x0000 }, + { 0x4ca3, 0x0000 }, + { 0x4ca4, 0x0000 }, + { 0x4ca5, 0x0000 }, + { 0x4ca6, 0x0000 }, + { 0x4ca7, 0x0000 }, + { 0x4ca8, 0x0000 }, + { 0x4ca9, 0x0000 }, + { 0x4caa, 0x0000 }, + { 0x4cab, 0x0000 }, + { 0x4cac, 0x0000 }, + { 0x4cad, 0x0000 }, + { 0x4cae, 0x0000 }, + { 0x4caf, 0x0000 }, + { 0x4cb0, 0x0000 }, + { 0x4cb1, 0x0000 }, + { 0x4cb2, 0x0000 }, + { 0x4cb3, 0x0000 }, + { 0x4cb4, 0x0000 }, + { 0x4cb5, 0x0000 }, + { 0x4cb6, 0x0000 }, + { 0x4cb7, 0x0000 }, + { 0x4cb8, 0x0000 }, + { 0x4cb9, 0x0000 }, + { 0x4cba, 0x0000 }, + { 0x4cbb, 0x0000 }, + { 0x4cbc, 0x0000 }, + { 0x4cbd, 0x0000 }, + { 0x4cbe, 0x0000 }, + { 0x4cbf, 0x0000 }, + { 0x4cc0, 0x0000 }, + { 0x4cc1, 0x0000 }, + { 0x4cc2, 0x0000 }, + { 0x4cc3, 0x0000 }, + { 0x4cc4, 0x0000 }, + { 0x4cc5, 0x0000 }, + { 0x4cc6, 0x0000 }, + { 0x4cc7, 0x0000 }, + { 0x4cc8, 0x0000 }, + { 0x4cc9, 0x0000 }, + { 0x4cca, 0x0000 }, + { 0x4ccb, 0x0000 }, + { 0x4ccc, 0x0000 }, + { 0x4ccd, 0x0000 }, + { 0x4cce, 0x0000 }, + { 0x4ccf, 0x0000 }, + { 0x4cd0, 0x0000 }, + { 0x4cd1, 0x0000 }, + { 0x4cd2, 0x0000 }, + { 0x4cd3, 0x0000 }, + { 0x4cd4, 0x0000 }, + { 0x4cd5, 0x0000 }, + { 0x4cd6, 0x0000 }, + { 0x4cd7, 0x0000 }, + { 0x4cd8, 0x0000 }, + { 0x4cd9, 0x0000 }, + { 0x4cda, 0x0000 }, + { 0x4cdb, 0x0000 }, + { 0x4cdc, 0x0000 }, + { 0x4cdd, 0x0000 }, + { 0x4cde, 0x0000 }, + { 0x4cdf, 0x0000 }, + { 0x4ce0, 0x0000 }, + { 0x4ce1, 0x0000 }, + { 0x4ce2, 0x0000 }, + { 0x4ce3, 0x0000 }, + { 0x4ce4, 0x0000 }, + { 0x4ce5, 0x0000 }, + { 0x4ce6, 0x0000 }, + { 0x4ce7, 0x0000 }, + { 0x4ce8, 0x0000 }, + { 0x4ce9, 0x0000 }, + { 0x4cea, 0x0000 }, + { 0x4ceb, 0x0000 }, + { 0x4cec, 0x0000 }, + { 0x4ced, 0x0000 }, + { 0x4cee, 0x0000 }, + { 0x4cef, 0x0000 }, + { 0x4cf0, 0x0000 }, + { 0x4cf1, 0x0000 }, + { 0x4cf2, 0x0000 }, + { 0x4cf3, 0x0000 }, + { 0x4cf4, 0x0000 }, + { 0x4cf5, 0x0000 }, + { 0x4cf6, 0x0000 }, + { 0x4cf7, 0x0000 }, + { 0x4cf8, 0x0000 }, + { 0x4cf9, 0x0000 }, + { 0x4cfa, 0x0000 }, + { 0x4cfb, 0x0000 }, + { 0x4cfc, 0x0000 }, + { 0x4cfd, 0x0000 }, + { 0x4cfe, 0x0000 }, + { 0x4cff, 0x0000 }, + { 0x4d00, 0x0000 }, + { 0x4d01, 0x0000 }, + { 0x4d02, 0x0000 }, + { 0x4d03, 0x0000 }, + { 0x4d04, 0x0000 }, + { 0x4d05, 0x0000 }, + { 0x4d06, 0x0000 }, + { 0x4d07, 0x0000 }, + { 0x4d08, 0x0000 }, + { 0x4d09, 0x0000 }, + { 0x4d0a, 0x0000 }, + { 0x4d0b, 0x0000 }, + { 0x4d0c, 0x0000 }, + { 0x4d0d, 0x0000 }, + { 0x4d0e, 0x0000 }, + { 0x4d0f, 0x0000 }, + { 0x4d10, 0x0000 }, + { 0x4d11, 0x0000 }, + { 0x4d12, 0x0000 }, + { 0x4d13, 0x0000 }, + { 0x4d14, 0x0000 }, + { 0x4d15, 0x0000 }, + { 0x4d16, 0x0000 }, + { 0x4d17, 0x0000 }, + { 0x4d18, 0x0000 }, + { 0x4d19, 0x0000 }, + { 0x4d1a, 0x0000 }, + { 0x4d1b, 0x0000 }, + { 0x4d1c, 0x0000 }, + { 0x4d1d, 0x0000 }, + { 0x4d1e, 0x0000 }, + { 0x4d1f, 0x0000 }, + { 0x4d20, 0x0000 }, + { 0x4d21, 0x0000 }, + { 0x4d22, 0x0000 }, + { 0x4d23, 0x0000 }, + { 0x4d24, 0x0000 }, + { 0x4d25, 0x0000 }, + { 0x4d26, 0x0000 }, + { 0x4d27, 0x0000 }, + { 0x4d28, 0x0000 }, + { 0x4d29, 0x0000 }, + { 0x4d2a, 0x0000 }, + { 0x4d2b, 0x0000 }, + { 0x4d2c, 0x0000 }, + { 0x4d2d, 0x0000 }, + { 0x4d2e, 0x0000 }, + { 0x4d2f, 0x0000 }, + { 0x4d30, 0x0000 }, + { 0x4d31, 0x0000 }, + { 0x4d32, 0x0000 }, + { 0x4d33, 0x0000 }, + { 0x4d34, 0x0000 }, + { 0x4d35, 0x0000 }, + { 0x4d36, 0x0000 }, + { 0x4d37, 0x0000 }, + { 0x4d38, 0x0000 }, + { 0x4d39, 0x0000 }, + { 0x4d3a, 0x0000 }, + { 0x4d3b, 0x0000 }, + { 0x4d3c, 0x0000 }, + { 0x4d3d, 0x0000 }, + { 0x4d3e, 0x0000 }, + { 0x4d3f, 0x0000 }, + { 0x4d40, 0x0000 }, + { 0x4d41, 0x0000 }, + { 0x4d42, 0x0000 }, + { 0x4d43, 0x0000 }, + { 0x4d44, 0x0000 }, + { 0x4d45, 0x0000 }, + { 0x4d46, 0x0000 }, + { 0x4d47, 0x0000 }, + { 0x4d48, 0x0000 }, + { 0x4d49, 0x0000 }, + { 0x4d4a, 0x0000 }, + { 0x4d4b, 0x0000 }, + { 0x4d4c, 0x0000 }, + { 0x4d4d, 0x0000 }, + { 0x4d4e, 0x0000 }, + { 0x4d4f, 0x0000 }, + { 0x4d50, 0x0000 }, + { 0x4d51, 0x0000 }, + { 0x4d52, 0x0000 }, + { 0x4d53, 0x0000 }, + { 0x4d54, 0x0000 }, + { 0x4d55, 0x0000 }, + { 0x4d56, 0x0000 }, + { 0x4d57, 0x0000 }, + { 0x4d58, 0x0000 }, + { 0x4d59, 0x0000 }, + { 0x4d5a, 0x0000 }, + { 0x4d5b, 0x0000 }, + { 0x4d5c, 0x0000 }, + { 0x4d5d, 0x0000 }, + { 0x4d5e, 0x0000 }, + { 0x4d5f, 0x0000 }, + { 0x4d60, 0x0000 }, + { 0x4d61, 0x0000 }, + { 0x4d62, 0x0000 }, + { 0x4d63, 0x0000 }, + { 0x4d64, 0x0000 }, + { 0x4d65, 0x0000 }, + { 0x4d66, 0x0000 }, + { 0x4d67, 0x0000 }, + { 0x4d68, 0x0000 }, + { 0x4d69, 0x0000 }, + { 0x4d6a, 0x0000 }, + { 0x4d6b, 0x0000 }, + { 0x4d6c, 0x0000 }, + { 0x4d6d, 0x0000 }, + { 0x4d6e, 0x0000 }, + { 0x4d6f, 0x0000 }, + { 0x4d70, 0x0000 }, + { 0x4d71, 0x0000 }, + { 0x4d72, 0x0000 }, + { 0x4d73, 0x0000 }, + { 0x4d74, 0x0000 }, + { 0x4d75, 0x0000 }, + { 0x4d76, 0x0000 }, + { 0x4d77, 0x0000 }, + { 0x4d78, 0x0000 }, + { 0x4d79, 0x0000 }, + { 0x4d7a, 0x0000 }, + { 0x4d7b, 0x0000 }, + { 0x4d7c, 0x0000 }, + { 0x4d7d, 0x0000 }, + { 0x4d7e, 0x0000 }, + { 0x4d7f, 0x0000 }, + { 0x4d80, 0x0000 }, + { 0x4d81, 0x0000 }, + { 0x4d82, 0x0000 }, + { 0x4d83, 0x0000 }, + { 0x4d84, 0x0000 }, + { 0x4d85, 0x0000 }, + { 0x4d86, 0x0000 }, + { 0x4d87, 0x0000 }, + { 0x4d88, 0x0000 }, + { 0x4d89, 0x0000 }, + { 0x4d8a, 0x0000 }, + { 0x4d8b, 0x0000 }, + { 0x4d8c, 0x0000 }, + { 0x4d8d, 0x0000 }, + { 0x4d8e, 0x0000 }, + { 0x4d8f, 0x0000 }, + { 0x4d90, 0x0000 }, + { 0x4d91, 0x0000 }, + { 0x4d92, 0x0000 }, + { 0x4d93, 0x0000 }, + { 0x4d94, 0x0000 }, + { 0x4d95, 0x0000 }, + { 0x4d96, 0x0000 }, + { 0x4d97, 0x0000 }, + { 0x4d98, 0x0000 }, + { 0x4d99, 0x0000 }, + { 0x4d9a, 0x0000 }, + { 0x4d9b, 0x0000 }, + { 0x4d9c, 0x0000 }, + { 0x4d9d, 0x0000 }, + { 0x4d9e, 0x0000 }, + { 0x4d9f, 0x0000 }, + { 0x4da0, 0x0000 }, + { 0x4da1, 0x0000 }, + { 0x4da2, 0x0000 }, + { 0x4da3, 0x0000 }, + { 0x4da4, 0x0000 }, + { 0x4da5, 0x0000 }, + { 0x4da6, 0x0000 }, + { 0x4da7, 0x0000 }, + { 0x4da8, 0x0000 }, + { 0x4da9, 0x0000 }, + { 0x4daa, 0x0000 }, + { 0x4dab, 0x0000 }, + { 0x4dac, 0x0000 }, + { 0x4dad, 0x0000 }, + { 0x4dae, 0x0000 }, + { 0x4daf, 0x0000 }, + { 0x4db0, 0x0000 }, + { 0x4db1, 0x0000 }, + { 0x4db2, 0x0000 }, + { 0x4db3, 0x0000 }, + { 0x4db4, 0x0000 }, + { 0x4db5, 0x0000 }, + { 0x4db6, 0x0000 }, + { 0x4db7, 0x0000 }, + { 0x4db8, 0x0000 }, + { 0x4db9, 0x0000 }, + { 0x4dba, 0x0000 }, + { 0x4dbb, 0x0000 }, + { 0x4dbc, 0x0000 }, + { 0x4dbd, 0x0000 }, + { 0x4dbe, 0x0000 }, + { 0x4dbf, 0x0000 }, + { 0x4dc0, 0x0000 }, + { 0x4dc1, 0x0000 }, + { 0x4dc2, 0x0000 }, + { 0x4dc3, 0x0000 }, + { 0x4dc4, 0x0000 }, + { 0x4dc5, 0x0000 }, + { 0x4dc6, 0x0000 }, + { 0x4dc7, 0x0000 }, + { 0x4dc8, 0x0000 }, + { 0x4dc9, 0x0000 }, + { 0x4dca, 0x0000 }, + { 0x4dcb, 0x0000 }, + { 0x4dcc, 0x0000 }, + { 0x4dcd, 0x0000 }, + { 0x4dce, 0x0000 }, + { 0x4dcf, 0x0000 }, + { 0x4dd0, 0x0000 }, + { 0x4dd1, 0x0000 }, + { 0x4dd2, 0x0000 }, + { 0x4dd3, 0x0000 }, + { 0x4dd4, 0x0000 }, + { 0x4dd5, 0x0000 }, + { 0x4dd6, 0x0000 }, + { 0x4dd7, 0x0000 }, + { 0x4dd8, 0x0000 }, + { 0x4dd9, 0x0000 }, + { 0x4dda, 0x0000 }, + { 0x4ddb, 0x0000 }, + { 0x4ddc, 0x0000 }, + { 0x4ddd, 0x0000 }, + { 0x4dde, 0x0000 }, + { 0x4ddf, 0x0000 }, + { 0x4de0, 0x0000 }, + { 0x4de1, 0x0000 }, + { 0x4de2, 0x0000 }, + { 0x4de3, 0x0000 }, + { 0x4de4, 0x0000 }, + { 0x4de5, 0x0000 }, + { 0x4de6, 0x0000 }, + { 0x4de7, 0x0000 }, + { 0x4de8, 0x0000 }, + { 0x4de9, 0x0000 }, + { 0x4dea, 0x0000 }, + { 0x4deb, 0x0000 }, + { 0x4dec, 0x0000 }, + { 0x4ded, 0x0000 }, + { 0x4dee, 0x0000 }, + { 0x4def, 0x0000 }, + { 0x4df0, 0x0000 }, + { 0x4df1, 0x0000 }, + { 0x4df2, 0x0000 }, + { 0x4df3, 0x0000 }, + { 0x4df4, 0x0000 }, + { 0x4df5, 0x0000 }, + { 0x4df6, 0x0000 }, + { 0x4df7, 0x0000 }, + { 0x4df8, 0x0000 }, + { 0x4df9, 0x0000 }, + { 0x4dfa, 0x0000 }, + { 0x4dfb, 0x0000 }, + { 0x4dfc, 0x0000 }, + { 0x4dfd, 0x0000 }, + { 0x4dfe, 0x0000 }, + { 0x4dff, 0x0000 }, + { 0x4e00, 0x0000 }, + { 0x4e01, 0x0000 }, + { 0x4e02, 0x0000 }, + { 0x4e03, 0x0000 }, + { 0x4e04, 0x0000 }, + { 0x4e05, 0x0000 }, + { 0x4e06, 0x0000 }, + { 0x4e07, 0x0000 }, + { 0x4e08, 0x0000 }, + { 0x4e09, 0x0000 }, + { 0x4e0a, 0x0000 }, + { 0x4e0b, 0x0000 }, + { 0x4e0c, 0x0000 }, + { 0x4e0d, 0x0000 }, + { 0x4e0e, 0x0000 }, + { 0x4e0f, 0x0000 }, + { 0x4e10, 0x0000 }, + { 0x4e11, 0x0000 }, + { 0x4e12, 0x0000 }, + { 0x4e13, 0x0000 }, + { 0x4e14, 0x0000 }, + { 0x4e15, 0x0000 }, + { 0x4e16, 0x0000 }, + { 0x4e17, 0x0000 }, + { 0x4e18, 0x0000 }, + { 0x4e19, 0x0000 }, + { 0x4e1a, 0x0000 }, + { 0x4e1b, 0x0000 }, + { 0x4e1c, 0x0000 }, + { 0x4e1d, 0x0000 }, + { 0x4e1e, 0x0000 }, + { 0x4e1f, 0x0000 }, + { 0x4e20, 0x0000 }, + { 0x4e21, 0x0000 }, + { 0x4e22, 0x0000 }, + { 0x4e23, 0x0000 }, + { 0x4e24, 0x0000 }, + { 0x4e25, 0x0000 }, + { 0x4e26, 0x0000 }, + { 0x4e27, 0x0000 }, + { 0x4e28, 0x0000 }, + { 0x4e29, 0x0000 }, + { 0x4e2a, 0x0000 }, + { 0x4e2b, 0x0000 }, + { 0x4e2c, 0x0000 }, + { 0x4e2d, 0x0000 }, + { 0x4e2e, 0x0000 }, + { 0x4e2f, 0x0000 }, + { 0x4e30, 0x0000 }, + { 0x4e31, 0x0000 }, + { 0x4e32, 0x0000 }, + { 0x4e33, 0x0000 }, + { 0x4e34, 0x0000 }, + { 0x4e35, 0x0000 }, + { 0x4e36, 0x0000 }, + { 0x4e37, 0x0000 }, + { 0x4e38, 0x0000 }, + { 0x4e39, 0x0000 }, + { 0x4e3a, 0x0000 }, + { 0x4e3b, 0x0000 }, + { 0x4e3c, 0x0000 }, + { 0x4e3d, 0x0000 }, + { 0x4e3e, 0x0000 }, + { 0x4e3f, 0x0000 }, + { 0x4e40, 0x0000 }, + { 0x4e41, 0x0000 }, + { 0x4e42, 0x0000 }, + { 0x4e43, 0x0000 }, + { 0x4e44, 0x0000 }, + { 0x4e45, 0x0000 }, + { 0x4e46, 0x0000 }, + { 0x4e47, 0x0000 }, + { 0x4e48, 0x0000 }, + { 0x4e49, 0x0000 }, + { 0x4e4a, 0x0000 }, + { 0x4e4b, 0x0000 }, + { 0x4e4c, 0x0000 }, + { 0x4e4d, 0x0000 }, + { 0x4e4e, 0x0000 }, + { 0x4e4f, 0x0000 }, + { 0x4e50, 0x0000 }, + { 0x4e51, 0x0000 }, + { 0x4e52, 0x0000 }, + { 0x4e53, 0x0000 }, + { 0x4e54, 0x0000 }, + { 0x4e55, 0x0000 }, + { 0x4e56, 0x0000 }, + { 0x4e57, 0x0000 }, + { 0x4e58, 0x0000 }, + { 0x4e59, 0x0000 }, + { 0x4e5a, 0x0000 }, + { 0x4e5b, 0x0000 }, + { 0x4e5c, 0x0000 }, + { 0x4e5d, 0x0000 }, + { 0x4e5e, 0x0000 }, + { 0x4e5f, 0x0000 }, + { 0x4e60, 0x0000 }, + { 0x4e61, 0x0000 }, + { 0x4e62, 0x0000 }, + { 0x4e63, 0x0000 }, + { 0x4e64, 0x0000 }, + { 0x4e65, 0x0000 }, + { 0x4e66, 0x0000 }, + { 0x4e67, 0x0000 }, + { 0x4e68, 0x0000 }, + { 0x4e69, 0x0000 }, + { 0x4e6a, 0x0000 }, + { 0x4e6b, 0x0000 }, + { 0x4e6c, 0x0000 }, + { 0x4e6d, 0x0000 }, + { 0x4e6e, 0x0000 }, + { 0x4e6f, 0x0000 }, + { 0x4e70, 0x0000 }, + { 0x4e71, 0x0000 }, + { 0x4e72, 0x0000 }, + { 0x4e73, 0x0000 }, + { 0x4e74, 0x0000 }, + { 0x4e75, 0x0000 }, + { 0x4e76, 0x0000 }, + { 0x4e77, 0x0000 }, + { 0x4e78, 0x0000 }, + { 0x4e79, 0x0000 }, + { 0x4e7a, 0x0000 }, + { 0x4e7b, 0x0000 }, + { 0x4e7c, 0x0000 }, + { 0x4e7d, 0x0000 }, + { 0x4e7e, 0x0000 }, + { 0x4e7f, 0x0000 }, + { 0x4e80, 0x0000 }, + { 0x4e81, 0x0000 }, + { 0x4e82, 0x0000 }, + { 0x4e83, 0x0000 }, + { 0x4e84, 0x0000 }, + { 0x4e85, 0x0000 }, + { 0x4e86, 0x0000 }, + { 0x4e87, 0x0000 }, + { 0x4e88, 0x0000 }, + { 0x4e89, 0x0000 }, + { 0x4e8a, 0x0000 }, + { 0x4e8b, 0x0000 }, + { 0x4e8c, 0x0000 }, + { 0x4e8d, 0x0000 }, + { 0x4e8e, 0x0000 }, + { 0x4e8f, 0x0000 }, + { 0x4e90, 0x0000 }, + { 0x4e91, 0x0000 }, + { 0x4e92, 0x0000 }, + { 0x4e93, 0x0000 }, + { 0x4e94, 0x0000 }, + { 0x4e95, 0x0000 }, + { 0x4e96, 0x0000 }, + { 0x4e97, 0x0000 }, + { 0x4e98, 0x0000 }, + { 0x4e99, 0x0000 }, + { 0x4e9a, 0x0000 }, + { 0x4e9b, 0x0000 }, + { 0x4e9c, 0x0000 }, + { 0x4e9d, 0x0000 }, + { 0x4e9e, 0x0000 }, + { 0x4e9f, 0x0000 }, + { 0x4ea0, 0x0000 }, + { 0x4ea1, 0x0000 }, + { 0x4ea2, 0x0000 }, + { 0x4ea3, 0x0000 }, + { 0x4ea4, 0x0000 }, + { 0x4ea5, 0x0000 }, + { 0x4ea6, 0x0000 }, + { 0x4ea7, 0x0000 }, + { 0x4ea8, 0x0000 }, + { 0x4ea9, 0x0000 }, + { 0x4eaa, 0x0000 }, + { 0x4eab, 0x0000 }, + { 0x4eac, 0x0000 }, + { 0x4ead, 0x0000 }, + { 0x4eae, 0x0000 }, + { 0x4eaf, 0x0000 }, + { 0x4eb0, 0x0000 }, + { 0x4eb1, 0x0000 }, + { 0x4eb2, 0x0000 }, + { 0x4eb3, 0x0000 }, + { 0x4eb4, 0x0000 }, + { 0x4eb5, 0x0000 }, + { 0x4eb6, 0x0000 }, + { 0x4eb7, 0x0000 }, + { 0x4eb8, 0x0000 }, + { 0x4eb9, 0x0000 }, + { 0x4eba, 0x0000 }, + { 0x4ebb, 0x0000 }, + { 0x4ebc, 0x0000 }, + { 0x4ebd, 0x0000 }, + { 0x4ebe, 0x0000 }, + { 0x4ebf, 0x0000 }, + { 0x4ec0, 0x0000 }, + { 0x4ec1, 0x0000 }, + { 0x4ec2, 0x0000 }, + { 0x4ec3, 0x0000 }, + { 0x4ec4, 0x0000 }, + { 0x4ec5, 0x0000 }, + { 0x4ec6, 0x0000 }, + { 0x4ec7, 0x0000 }, + { 0x4ec8, 0x0000 }, + { 0x4ec9, 0x0000 }, + { 0x4eca, 0x0000 }, + { 0x4ecb, 0x0000 }, + { 0x4ecc, 0x0000 }, + { 0x4ecd, 0x0000 }, + { 0x4ece, 0x0000 }, + { 0x4ecf, 0x0000 }, + { 0x4ed0, 0x0000 }, + { 0x4ed1, 0x0000 }, + { 0x4ed2, 0x0000 }, + { 0x4ed3, 0x0000 }, + { 0x4ed4, 0x0000 }, + { 0x4ed5, 0x0000 }, + { 0x4ed6, 0x0000 }, + { 0x4ed7, 0x0000 }, + { 0x4ed8, 0x0000 }, + { 0x4ed9, 0x0000 }, + { 0x4eda, 0x0000 }, + { 0x4edb, 0x0000 }, + { 0x4edc, 0x0000 }, + { 0x4edd, 0x0000 }, + { 0x4ede, 0x0000 }, + { 0x4edf, 0x0000 }, + { 0x4ee0, 0x0000 }, + { 0x4ee1, 0x0000 }, + { 0x4ee2, 0x0000 }, + { 0x4ee3, 0x0000 }, + { 0x4ee4, 0x0000 }, + { 0x4ee5, 0x0000 }, + { 0x4ee6, 0x0000 }, + { 0x4ee7, 0x0000 }, + { 0x4ee8, 0x0000 }, + { 0x4ee9, 0x0000 }, + { 0x4eea, 0x0000 }, + { 0x4eeb, 0x0000 }, + { 0x4eec, 0x0000 }, + { 0x4eed, 0x0000 }, + { 0x4eee, 0x0000 }, + { 0x4eef, 0x0000 }, + { 0x4ef0, 0x0000 }, + { 0x4ef1, 0x0000 }, + { 0x4ef2, 0x0000 }, + { 0x4ef3, 0x0000 }, + { 0x4ef4, 0x0000 }, + { 0x4ef5, 0x0000 }, + { 0x4ef6, 0x0000 }, + { 0x4ef7, 0x0000 }, + { 0x4ef8, 0x0000 }, + { 0x4ef9, 0x0000 }, + { 0x4efa, 0x0000 }, + { 0x4efb, 0x0000 }, + { 0x4efc, 0x0000 }, + { 0x4efd, 0x0000 }, + { 0x4efe, 0x0000 }, + { 0x4eff, 0x0000 }, + { 0x4f00, 0x0000 }, + { 0x4f01, 0x0000 }, + { 0x4f02, 0x0000 }, + { 0x4f03, 0x0000 }, + { 0x4f04, 0x0000 }, + { 0x4f05, 0x0000 }, + { 0x4f06, 0x0000 }, + { 0x4f07, 0x0000 }, + { 0x4f08, 0x0000 }, + { 0x4f09, 0x0000 }, + { 0x4f0a, 0x0000 }, + { 0x4f0b, 0x0000 }, + { 0x4f0c, 0x0000 }, + { 0x4f0d, 0x0000 }, + { 0x4f0e, 0x0000 }, + { 0x4f0f, 0x0000 }, + { 0x4f10, 0x0000 }, + { 0x4f11, 0x0000 }, + { 0x4f12, 0x0000 }, + { 0x4f13, 0x0000 }, + { 0x4f14, 0x0000 }, + { 0x4f15, 0x0000 }, + { 0x4f16, 0x0000 }, + { 0x4f17, 0x0000 }, + { 0x4f18, 0x0000 }, + { 0x4f19, 0x0000 }, + { 0x4f1a, 0x0000 }, + { 0x4f1b, 0x0000 }, + { 0x4f1c, 0x0000 }, + { 0x4f1d, 0x0000 }, + { 0x4f1e, 0x0000 }, + { 0x4f1f, 0x0000 }, + { 0x4f20, 0x0000 }, + { 0x4f21, 0x0000 }, + { 0x4f22, 0x0000 }, + { 0x4f23, 0x0000 }, + { 0x4f24, 0x0000 }, + { 0x4f25, 0x0000 }, + { 0x4f26, 0x0000 }, + { 0x4f27, 0x0000 }, + { 0x4f28, 0x0000 }, + { 0x4f29, 0x0000 }, + { 0x4f2a, 0x0000 }, + { 0x4f2b, 0x0000 }, + { 0x4f2c, 0x0000 }, + { 0x4f2d, 0x0000 }, + { 0x4f2e, 0x0000 }, + { 0x4f2f, 0x0000 }, + { 0x4f30, 0x0000 }, + { 0x4f31, 0x0000 }, + { 0x4f32, 0x0000 }, + { 0x4f33, 0x0000 }, + { 0x4f34, 0x0000 }, + { 0x4f35, 0x0000 }, + { 0x4f36, 0x0000 }, + { 0x4f37, 0x0000 }, + { 0x4f38, 0x0000 }, + { 0x4f39, 0x0000 }, + { 0x4f3a, 0x0000 }, + { 0x4f3b, 0x0000 }, + { 0x4f3c, 0x0000 }, + { 0x4f3d, 0x0000 }, + { 0x4f3e, 0x0000 }, + { 0x4f3f, 0x0000 }, + { 0x4f40, 0x0000 }, + { 0x4f41, 0x0000 }, + { 0x4f42, 0x0000 }, + { 0x4f43, 0x0000 }, + { 0x4f44, 0x0000 }, + { 0x4f45, 0x0000 }, + { 0x4f46, 0x0000 }, + { 0x4f47, 0x0000 }, + { 0x4f48, 0x0000 }, + { 0x4f49, 0x0000 }, + { 0x4f4a, 0x0000 }, + { 0x4f4b, 0x0000 }, + { 0x4f4c, 0x0000 }, + { 0x4f4d, 0x0000 }, + { 0x4f4e, 0x0000 }, + { 0x4f4f, 0x0000 }, + { 0x4f50, 0x0000 }, + { 0x4f51, 0x0000 }, + { 0x4f52, 0x0000 }, + { 0x4f53, 0x0000 }, + { 0x4f54, 0x0000 }, + { 0x4f55, 0x0000 }, + { 0x4f56, 0x0000 }, + { 0x4f57, 0x0000 }, + { 0x4f58, 0x0000 }, + { 0x4f59, 0x0000 }, + { 0x4f5a, 0x0000 }, + { 0x4f5b, 0x0000 }, + { 0x4f5c, 0x0000 }, + { 0x4f5d, 0x0000 }, + { 0x4f5e, 0x0000 }, + { 0x4f5f, 0x0000 }, + { 0x4f60, 0x0000 }, + { 0x4f61, 0x0000 }, + { 0x4f62, 0x0000 }, + { 0x4f63, 0x0000 }, + { 0x4f64, 0x0000 }, + { 0x4f65, 0x0000 }, + { 0x4f66, 0x0000 }, + { 0x4f67, 0x0000 }, + { 0x4f68, 0x0000 }, + { 0x4f69, 0x0000 }, + { 0x4f6a, 0x0000 }, + { 0x4f6b, 0x0000 }, + { 0x4f6c, 0x0000 }, + { 0x4f6d, 0x0000 }, + { 0x4f6e, 0x0000 }, + { 0x4f6f, 0x0000 }, + { 0x4f70, 0x0000 }, + { 0x4f71, 0x0000 }, + { 0x4f72, 0x0000 }, + { 0x4f73, 0x0000 }, + { 0x4f74, 0x0000 }, + { 0x4f75, 0x0000 }, + { 0x4f76, 0x0000 }, + { 0x4f77, 0x0000 }, + { 0x4f78, 0x0000 }, + { 0x4f79, 0x0000 }, + { 0x4f7a, 0x0000 }, + { 0x4f7b, 0x0000 }, + { 0x4f7c, 0x0000 }, + { 0x4f7d, 0x0000 }, + { 0x4f7e, 0x0000 }, + { 0x4f7f, 0x0000 }, + { 0x4f80, 0x0000 }, + { 0x4f81, 0x0000 }, + { 0x4f82, 0x0000 }, + { 0x4f83, 0x0000 }, + { 0x4f84, 0x0000 }, + { 0x4f85, 0x0000 }, + { 0x4f86, 0x0000 }, + { 0x4f87, 0x0000 }, + { 0x4f88, 0x0000 }, + { 0x4f89, 0x0000 }, + { 0x4f8a, 0x0000 }, + { 0x4f8b, 0x0000 }, + { 0x4f8c, 0x0000 }, + { 0x4f8d, 0x0000 }, + { 0x4f8e, 0x0000 }, + { 0x4f8f, 0x0000 }, + { 0x4f90, 0x0000 }, + { 0x4f91, 0x0000 }, + { 0x4f92, 0x0000 }, + { 0x4f93, 0x0000 }, + { 0x4f94, 0x0000 }, + { 0x4f95, 0x0000 }, + { 0x4f96, 0x0000 }, + { 0x4f97, 0x0000 }, + { 0x4f98, 0x0000 }, + { 0x4f99, 0x0000 }, + { 0x4f9a, 0x0000 }, + { 0x4f9b, 0x0000 }, + { 0x4f9c, 0x0000 }, + { 0x4f9d, 0x0000 }, + { 0x4f9e, 0x0000 }, + { 0x4f9f, 0x0000 }, + { 0x4fa0, 0x0000 }, + { 0x4fa1, 0x0000 }, + { 0x4fa2, 0x0000 }, + { 0x4fa3, 0x0000 }, + { 0x4fa4, 0x0000 }, + { 0x4fa5, 0x0000 }, + { 0x4fa6, 0x0000 }, + { 0x4fa7, 0x0000 }, + { 0x4fa8, 0x0000 }, + { 0x4fa9, 0x0000 }, + { 0x4faa, 0x0000 }, + { 0x4fab, 0x0000 }, + { 0x4fac, 0x0000 }, + { 0x4fad, 0x0000 }, + { 0x4fae, 0x0000 }, + { 0x4faf, 0x0000 }, + { 0x4fb0, 0x0000 }, + { 0x4fb1, 0x0000 }, + { 0x4fb2, 0x0000 }, + { 0x4fb3, 0x0000 }, + { 0x4fb4, 0x0000 }, + { 0x4fb5, 0x0000 }, + { 0x4fb6, 0x0000 }, + { 0x4fb7, 0x0000 }, + { 0x4fb8, 0x0000 }, + { 0x4fb9, 0x0000 }, + { 0x4fba, 0x0000 }, + { 0x4fbb, 0x0000 }, + { 0x4fbc, 0x0000 }, + { 0x4fbd, 0x0000 }, + { 0x4fbe, 0x0000 }, + { 0x4fbf, 0x0000 }, + { 0x4fc0, 0x0000 }, + { 0x4fc1, 0x0000 }, + { 0x4fc2, 0x0000 }, + { 0x4fc3, 0x0000 }, + { 0x4fc4, 0x0000 }, + { 0x4fc5, 0x0000 }, + { 0x4fc6, 0x0000 }, + { 0x4fc7, 0x0000 }, + { 0x4fc8, 0x0000 }, + { 0x4fc9, 0x0000 }, + { 0x4fca, 0x0000 }, + { 0x4fcb, 0x0000 }, + { 0x4fcc, 0x0000 }, + { 0x4fcd, 0x0000 }, + { 0x4fce, 0x0000 }, + { 0x4fcf, 0x0000 }, + { 0x4fd0, 0x0000 }, + { 0x4fd1, 0x0000 }, + { 0x4fd2, 0x0000 }, + { 0x4fd3, 0x0000 }, + { 0x4fd4, 0x0000 }, + { 0x4fd5, 0x0000 }, + { 0x4fd6, 0x0000 }, + { 0x4fd7, 0x0000 }, + { 0x4fd8, 0x0000 }, + { 0x4fd9, 0x0000 }, + { 0x4fda, 0x0000 }, + { 0x4fdb, 0x0000 }, + { 0x4fdc, 0x0000 }, + { 0x4fdd, 0x0000 }, + { 0x4fde, 0x0000 }, + { 0x4fdf, 0x0000 }, + { 0x4fe0, 0x0000 }, + { 0x4fe1, 0x0000 }, + { 0x4fe2, 0x0000 }, + { 0x4fe3, 0x0000 }, + { 0x4fe4, 0x0000 }, + { 0x4fe5, 0x0000 }, + { 0x4fe6, 0x0000 }, + { 0x4fe7, 0x0000 }, + { 0x4fe8, 0x0000 }, + { 0x4fe9, 0x0000 }, + { 0x4fea, 0x0000 }, + { 0x4feb, 0x0000 }, + { 0x4fec, 0x0000 }, + { 0x4fed, 0x0000 }, + { 0x4fee, 0x0000 }, + { 0x4fef, 0x0000 }, + { 0x4ff0, 0x0000 }, + { 0x4ff1, 0x0000 }, + { 0x4ff2, 0x0000 }, + { 0x4ff3, 0x0000 }, + { 0x4ff4, 0x0000 }, + { 0x4ff5, 0x0000 }, + { 0x4ff6, 0x0000 }, + { 0x4ff7, 0x0000 }, + { 0x4ff8, 0x0000 }, + { 0x4ff9, 0x0000 }, + { 0x4ffa, 0x0000 }, + { 0x4ffb, 0x0000 }, + { 0x4ffc, 0x0000 }, + { 0x4ffd, 0x0000 }, + { 0x4ffe, 0x0000 }, + { 0x4fff, 0x0000 }, + { 0x5000, 0x0000 }, + { 0x5001, 0x0000 }, + { 0x5002, 0x0000 }, + { 0x5003, 0x0000 }, + { 0x5004, 0x0000 }, + { 0x5005, 0x0000 }, + { 0x5006, 0x0000 }, + { 0x5007, 0x0000 }, + { 0x5008, 0x0000 }, + { 0x5009, 0x0000 }, + { 0x500a, 0x0000 }, + { 0x500b, 0x0000 }, + { 0x500c, 0x0000 }, + { 0x500d, 0x0000 }, + { 0x500e, 0x0000 }, + { 0x500f, 0x0000 }, + { 0x5010, 0x0000 }, + { 0x5011, 0x0000 }, + { 0x5012, 0x0000 }, + { 0x5013, 0x0000 }, + { 0x5014, 0x0000 }, + { 0x5015, 0x0000 }, + { 0x5016, 0x0000 }, + { 0x5017, 0x0000 }, + { 0x5018, 0x0000 }, + { 0x5019, 0x0000 }, + { 0x501a, 0x0000 }, + { 0x501b, 0x0000 }, + { 0x501c, 0x0000 }, + { 0x501d, 0x0000 }, + { 0x501e, 0x0000 }, + { 0x501f, 0x0000 }, + { 0x5020, 0x0000 }, + { 0x5021, 0x0000 }, + { 0x5022, 0x0000 }, + { 0x5023, 0x0000 }, + { 0x5024, 0x0000 }, + { 0x5025, 0x0000 }, + { 0x5026, 0x0000 }, + { 0x5027, 0x0000 }, + { 0x5028, 0x0000 }, + { 0x5029, 0x0000 }, + { 0x502a, 0x0000 }, + { 0x502b, 0x0000 }, + { 0x502c, 0x0000 }, + { 0x502d, 0x0000 }, + { 0x502e, 0x0000 }, + { 0x502f, 0x0000 }, + { 0x5030, 0x0000 }, + { 0x5031, 0x0000 }, + { 0x5032, 0x0000 }, + { 0x5033, 0x0000 }, + { 0x5034, 0x0000 }, + { 0x5035, 0x0000 }, + { 0x5036, 0x0000 }, + { 0x5037, 0x0000 }, + { 0x5038, 0x0000 }, + { 0x5039, 0x0000 }, + { 0x503a, 0x0000 }, + { 0x503b, 0x0000 }, + { 0x503c, 0x0000 }, + { 0x503d, 0x0000 }, + { 0x503e, 0x0000 }, + { 0x503f, 0x0000 }, + { 0x5040, 0x0000 }, + { 0x5041, 0x0000 }, + { 0x5042, 0x0000 }, + { 0x5043, 0x0000 }, + { 0x5044, 0x0000 }, + { 0x5045, 0x0000 }, + { 0x5046, 0x0000 }, + { 0x5047, 0x0000 }, + { 0x5048, 0x0000 }, + { 0x5049, 0x0000 }, + { 0x504a, 0x0000 }, + { 0x504b, 0x0000 }, + { 0x504c, 0x0000 }, + { 0x504d, 0x0000 }, + { 0x504e, 0x0000 }, + { 0x504f, 0x0000 }, + { 0x5050, 0x0000 }, + { 0x5051, 0x0000 }, + { 0x5052, 0x0000 }, + { 0x5053, 0x0000 }, + { 0x5054, 0x0000 }, + { 0x5055, 0x0000 }, + { 0x5056, 0x0000 }, + { 0x5057, 0x0000 }, + { 0x5058, 0x0000 }, + { 0x5059, 0x0000 }, + { 0x505a, 0x0000 }, + { 0x505b, 0x0000 }, + { 0x505c, 0x0000 }, + { 0x505d, 0x0000 }, + { 0x505e, 0x0000 }, + { 0x505f, 0x0000 }, + { 0x5060, 0x0000 }, + { 0x5061, 0x0000 }, + { 0x5062, 0x0000 }, + { 0x5063, 0x0000 }, + { 0x5064, 0x0000 }, + { 0x5065, 0x0000 }, + { 0x5066, 0x0000 }, + { 0x5067, 0x0000 }, + { 0x5068, 0x0000 }, + { 0x5069, 0x0000 }, + { 0x506a, 0x0000 }, + { 0x506b, 0x0000 }, + { 0x506c, 0x0000 }, + { 0x506d, 0x0000 }, + { 0x506e, 0x0000 }, + { 0x506f, 0x0000 }, + { 0x5070, 0x0000 }, + { 0x5071, 0x0000 }, + { 0x5072, 0x0000 }, + { 0x5073, 0x0000 }, + { 0x5074, 0x0000 }, + { 0x5075, 0x0000 }, + { 0x5076, 0x0000 }, + { 0x5077, 0x0000 }, + { 0x5078, 0x0000 }, + { 0x5079, 0x0000 }, + { 0x507a, 0x0000 }, + { 0x507b, 0x0000 }, + { 0x507c, 0x0000 }, + { 0x507d, 0x0000 }, + { 0x507e, 0x0000 }, + { 0x507f, 0x0000 }, + { 0x5080, 0x0000 }, + { 0x5081, 0x0000 }, + { 0x5082, 0x0000 }, + { 0x5083, 0x0000 }, + { 0x5084, 0x0000 }, + { 0x5085, 0x0000 }, + { 0x5086, 0x0000 }, + { 0x5087, 0x0000 }, + { 0x5088, 0x0000 }, + { 0x5089, 0x0000 }, + { 0x508a, 0x0000 }, + { 0x508b, 0x0000 }, + { 0x508c, 0x0000 }, + { 0x508d, 0x0000 }, + { 0x508e, 0x0000 }, + { 0x508f, 0x0000 }, + { 0x5090, 0x0000 }, + { 0x5091, 0x0000 }, + { 0x5092, 0x0000 }, + { 0x5093, 0x0000 }, + { 0x5094, 0x0000 }, + { 0x5095, 0x0000 }, + { 0x5096, 0x0000 }, + { 0x5097, 0x0000 }, + { 0x5098, 0x0000 }, + { 0x5099, 0x0000 }, + { 0x509a, 0x0000 }, + { 0x509b, 0x0000 }, + { 0x509c, 0x0000 }, + { 0x509d, 0x0000 }, + { 0x509e, 0x0000 }, + { 0x509f, 0x0000 }, + { 0x50a0, 0x0000 }, + { 0x50a1, 0x0000 }, + { 0x50a2, 0x0000 }, + { 0x50a3, 0x0000 }, + { 0x50a4, 0x0000 }, + { 0x50a5, 0x0000 }, + { 0x50a6, 0x0000 }, + { 0x50a7, 0x0000 }, + { 0x50a8, 0x0000 }, + { 0x50a9, 0x0000 }, + { 0x50aa, 0x0000 }, + { 0x50ab, 0x0000 }, + { 0x50ac, 0x0000 }, + { 0x50ad, 0x0000 }, + { 0x50ae, 0x0000 }, + { 0x50af, 0x0000 }, + { 0x50b0, 0x0000 }, + { 0x50b1, 0x0000 }, + { 0x50b2, 0x0000 }, + { 0x50b3, 0x0000 }, + { 0x50b4, 0x0000 }, + { 0x50b5, 0x0000 }, + { 0x50b6, 0x0000 }, + { 0x50b7, 0x0000 }, + { 0x50b8, 0x0000 }, + { 0x50b9, 0x0000 }, + { 0x50ba, 0x0000 }, + { 0x50bb, 0x0000 }, + { 0x50bc, 0x0000 }, + { 0x50bd, 0x0000 }, + { 0x50be, 0x0000 }, + { 0x50bf, 0x0000 }, + { 0x50c0, 0x0000 }, + { 0x50c1, 0x0000 }, + { 0x50c2, 0x0000 }, + { 0x50c3, 0x0000 }, + { 0x50c4, 0x0000 }, + { 0x50c5, 0x0000 }, + { 0x50c6, 0x0000 }, + { 0x50c7, 0x0000 }, + { 0x50c8, 0x0000 }, + { 0x50c9, 0x0000 }, + { 0x50ca, 0x0000 }, + { 0x50cb, 0x0000 }, + { 0x50cc, 0x0000 }, + { 0x50cd, 0x0000 }, + { 0x50ce, 0x0000 }, + { 0x50cf, 0x0000 }, + { 0x50d0, 0x0000 }, + { 0x50d1, 0x0000 }, + { 0x50d2, 0x0000 }, + { 0x50d3, 0x0000 }, + { 0x50d4, 0x0000 }, + { 0x50d5, 0x0000 }, + { 0x50d6, 0x0000 }, + { 0x50d7, 0x0000 }, + { 0x50d8, 0x0000 }, + { 0x50d9, 0x0000 }, + { 0x50da, 0x0000 }, + { 0x50db, 0x0000 }, + { 0x50dc, 0x0000 }, + { 0x50dd, 0x0000 }, + { 0x50de, 0x0000 }, + { 0x50df, 0x0000 }, + { 0x50e0, 0x0000 }, + { 0x50e1, 0x0000 }, + { 0x50e2, 0x0000 }, + { 0x50e3, 0x0000 }, + { 0x50e4, 0x0000 }, + { 0x50e5, 0x0000 }, + { 0x50e6, 0x0000 }, + { 0x50e7, 0x0000 }, + { 0x50e8, 0x0000 }, + { 0x50e9, 0x0000 }, + { 0x50ea, 0x0000 }, + { 0x50eb, 0x0000 }, + { 0x50ec, 0x0000 }, + { 0x50ed, 0x0000 }, + { 0x50ee, 0x0000 }, + { 0x50ef, 0x0000 }, + { 0x50f0, 0x0000 }, + { 0x50f1, 0x0000 }, + { 0x50f2, 0x0000 }, + { 0x50f3, 0x0000 }, + { 0x50f4, 0x0000 }, + { 0x50f5, 0x0000 }, + { 0x50f6, 0x0000 }, + { 0x50f7, 0x0000 }, + { 0x50f8, 0x0000 }, + { 0x50f9, 0x0000 }, + { 0x50fa, 0x0000 }, + { 0x50fb, 0x0000 }, + { 0x50fc, 0x0000 }, + { 0x50fd, 0x0000 }, + { 0x50fe, 0x0000 }, + { 0x50ff, 0x0000 }, + { 0x5100, 0x0000 }, + { 0x5101, 0x0000 }, + { 0x5102, 0x0000 }, + { 0x5103, 0x0000 }, + { 0x5104, 0x0000 }, + { 0x5105, 0x0000 }, + { 0x5106, 0x0000 }, + { 0x5107, 0x0000 }, + { 0x5108, 0x0000 }, + { 0x5109, 0x0000 }, + { 0x510a, 0x0000 }, + { 0x510b, 0x0000 }, + { 0x510c, 0x0000 }, + { 0x510d, 0x0000 }, + { 0x510e, 0x0000 }, + { 0x510f, 0x0000 }, + { 0x5110, 0x0000 }, + { 0x5111, 0x0000 }, + { 0x5112, 0x0000 }, + { 0x5113, 0x0000 }, + { 0x5114, 0x0000 }, + { 0x5115, 0x0000 }, + { 0x5116, 0x0000 }, + { 0x5117, 0x0000 }, + { 0x5118, 0x0000 }, + { 0x5119, 0x0000 }, + { 0x511a, 0x0000 }, + { 0x511b, 0x0000 }, + { 0x511c, 0x0000 }, + { 0x511d, 0x0000 }, + { 0x511e, 0x0000 }, + { 0x511f, 0x0000 }, + { 0x5120, 0x0000 }, + { 0x5121, 0x0000 }, + { 0x5122, 0x0000 }, + { 0x5123, 0x0000 }, + { 0x5124, 0x0000 }, + { 0x5125, 0x0000 }, + { 0x5126, 0x0000 }, + { 0x5127, 0x0000 }, + { 0x5128, 0x0000 }, + { 0x5129, 0x0000 }, + { 0x512a, 0x0000 }, + { 0x512b, 0x0000 }, + { 0x512c, 0x0000 }, + { 0x512d, 0x0000 }, + { 0x512e, 0x0000 }, + { 0x512f, 0x0000 }, + { 0x5130, 0x0000 }, + { 0x5131, 0x0000 }, + { 0x5132, 0x0000 }, + { 0x5133, 0x0000 }, + { 0x5134, 0x0000 }, + { 0x5135, 0x0000 }, + { 0x5136, 0x0000 }, + { 0x5137, 0x0000 }, + { 0x5138, 0x0000 }, + { 0x5139, 0x0000 }, + { 0x513a, 0x0000 }, + { 0x513b, 0x0000 }, + { 0x513c, 0x0000 }, + { 0x513d, 0x0000 }, + { 0x513e, 0x0000 }, + { 0x513f, 0x0000 }, + { 0x5140, 0x0000 }, + { 0x5141, 0x0000 }, + { 0x5142, 0x0000 }, + { 0x5143, 0x0000 }, + { 0x5144, 0x0000 }, + { 0x5145, 0x0000 }, + { 0x5146, 0x0000 }, + { 0x5147, 0x0000 }, + { 0x5148, 0x0000 }, + { 0x5149, 0x0000 }, + { 0x514a, 0x0000 }, + { 0x514b, 0x0000 }, + { 0x514c, 0x0000 }, + { 0x514d, 0x0000 }, + { 0x514e, 0x0000 }, + { 0x514f, 0x0000 }, + { 0x5150, 0x0000 }, + { 0x5151, 0x0000 }, + { 0x5152, 0x0000 }, + { 0x5153, 0x0000 }, + { 0x5154, 0x0000 }, + { 0x5155, 0x0000 }, + { 0x5156, 0x0000 }, + { 0x5157, 0x0000 }, + { 0x5158, 0x0000 }, + { 0x5159, 0x0000 }, + { 0x515a, 0x0000 }, + { 0x515b, 0x0000 }, + { 0x515c, 0x0000 }, + { 0x515d, 0x0000 }, + { 0x515e, 0x0000 }, + { 0x515f, 0x0000 }, + { 0x5160, 0x0000 }, + { 0x5161, 0x0000 }, + { 0x5162, 0x0000 }, + { 0x5163, 0x0000 }, + { 0x5164, 0x0000 }, + { 0x5165, 0x0000 }, + { 0x5166, 0x0000 }, + { 0x5167, 0x0000 }, + { 0x5168, 0x0000 }, + { 0x5169, 0x0000 }, + { 0x516a, 0x0000 }, + { 0x516b, 0x0000 }, + { 0x516c, 0x0000 }, + { 0x516d, 0x0000 }, + { 0x516e, 0x0000 }, + { 0x516f, 0x0000 }, + { 0x5170, 0x0000 }, + { 0x5171, 0x0000 }, + { 0x5172, 0x0000 }, + { 0x5173, 0x0000 }, + { 0x5174, 0x0000 }, + { 0x5175, 0x0000 }, + { 0x5176, 0x0000 }, + { 0x5177, 0x0000 }, + { 0x5178, 0x0000 }, + { 0x5179, 0x0000 }, + { 0x517a, 0x0000 }, + { 0x517b, 0x0000 }, + { 0x517c, 0x0000 }, + { 0x517d, 0x0000 }, + { 0x517e, 0x0000 }, + { 0x517f, 0x0000 }, + { 0x5180, 0x0000 }, + { 0x5181, 0x0000 }, + { 0x5182, 0x0000 }, + { 0x5183, 0x0000 }, + { 0x5184, 0x0000 }, + { 0x5185, 0x0000 }, + { 0x5186, 0x0000 }, + { 0x5187, 0x0000 }, + { 0x5188, 0x0000 }, + { 0x5189, 0x0000 }, + { 0x518a, 0x0000 }, + { 0x518b, 0x0000 }, + { 0x518c, 0x0000 }, + { 0x518d, 0x0000 }, + { 0x518e, 0x0000 }, + { 0x518f, 0x0000 }, + { 0x5190, 0x0000 }, + { 0x5191, 0x0000 }, + { 0x5192, 0x0000 }, + { 0x5193, 0x0000 }, + { 0x5194, 0x0000 }, + { 0x5195, 0x0000 }, + { 0x5196, 0x0000 }, + { 0x5197, 0x0000 }, + { 0x5198, 0x0000 }, + { 0x5199, 0x0000 }, + { 0x519a, 0x0000 }, + { 0x519b, 0x0000 }, + { 0x519c, 0x0000 }, + { 0x519d, 0x0000 }, + { 0x519e, 0x0000 }, + { 0x519f, 0x0000 }, + { 0x51a0, 0x0000 }, + { 0x51a1, 0x0000 }, + { 0x51a2, 0x0000 }, + { 0x51a3, 0x0000 }, + { 0x51a4, 0x0000 }, + { 0x51a5, 0x0000 }, + { 0x51a6, 0x0000 }, + { 0x51a7, 0x0000 }, + { 0x51a8, 0x0000 }, + { 0x51a9, 0x0000 }, + { 0x51aa, 0x0000 }, + { 0x51ab, 0x0000 }, + { 0x51ac, 0x0000 }, + { 0x51ad, 0x0000 }, + { 0x51ae, 0x0000 }, + { 0x51af, 0x0000 }, + { 0x51b0, 0x0000 }, + { 0x51b1, 0x0000 }, + { 0x51b2, 0x0000 }, + { 0x51b3, 0x0000 }, + { 0x51b4, 0x0000 }, + { 0x51b5, 0x0000 }, + { 0x51b6, 0x0000 }, + { 0x51b7, 0x0000 }, + { 0x51b8, 0x0000 }, + { 0x51b9, 0x0000 }, + { 0x51ba, 0x0000 }, + { 0x51bb, 0x0000 }, + { 0x51bc, 0x0000 }, + { 0x51bd, 0x0000 }, + { 0x51be, 0x0000 }, + { 0x51bf, 0x0000 }, + { 0x51c0, 0x0000 }, + { 0x51c1, 0x0000 }, + { 0x51c2, 0x0000 }, + { 0x51c3, 0x0000 }, + { 0x51c4, 0x0000 }, + { 0x51c5, 0x0000 }, + { 0x51c6, 0x0000 }, + { 0x51c7, 0x0000 }, + { 0x51c8, 0x0000 }, + { 0x51c9, 0x0000 }, + { 0x51ca, 0x0000 }, + { 0x51cb, 0x0000 }, + { 0x51cc, 0x0000 }, + { 0x51cd, 0x0000 }, + { 0x51ce, 0x0000 }, + { 0x51cf, 0x0000 }, + { 0x51d0, 0x0000 }, + { 0x51d1, 0x0000 }, + { 0x51d2, 0x0000 }, + { 0x51d3, 0x0000 }, + { 0x51d4, 0x0000 }, + { 0x51d5, 0x0000 }, + { 0x51d6, 0x0000 }, + { 0x51d7, 0x0000 }, + { 0x51d8, 0x0000 }, + { 0x51d9, 0x0000 }, + { 0x51da, 0x0000 }, + { 0x51db, 0x0000 }, + { 0x51dc, 0x0000 }, + { 0x51dd, 0x0000 }, + { 0x51de, 0x0000 }, + { 0x51df, 0x0000 }, + { 0x51e0, 0x0000 }, + { 0x51e1, 0x0000 }, + { 0x51e2, 0x0000 }, + { 0x51e3, 0x0000 }, + { 0x51e4, 0x0000 }, + { 0x51e5, 0x0000 }, + { 0x51e6, 0x0000 }, + { 0x51e7, 0x0000 }, + { 0x51e8, 0x0000 }, + { 0x51e9, 0x0000 }, + { 0x51ea, 0x0000 }, + { 0x51eb, 0x0000 }, + { 0x51ec, 0x0000 }, + { 0x51ed, 0x0000 }, + { 0x51ee, 0x0000 }, + { 0x51ef, 0x0000 }, + { 0x51f0, 0x0000 }, + { 0x51f1, 0x0000 }, + { 0x51f2, 0x0000 }, + { 0x51f3, 0x0000 }, + { 0x51f4, 0x0000 }, + { 0x51f5, 0x0000 }, + { 0x51f6, 0x0000 }, + { 0x51f7, 0x0000 }, + { 0x51f8, 0x0000 }, + { 0x51f9, 0x0000 }, + { 0x51fa, 0x0000 }, + { 0x51fb, 0x0000 }, + { 0x51fc, 0x0000 }, + { 0x51fd, 0x0000 }, + { 0x51fe, 0x0000 }, + { 0x51ff, 0x0000 }, + { 0x5200, 0x0000 }, + { 0x5201, 0x0000 }, + { 0x5202, 0x0000 }, + { 0x5203, 0x0000 }, + { 0x5204, 0x0000 }, + { 0x5205, 0x0000 }, + { 0x5206, 0x0000 }, + { 0x5207, 0x0000 }, + { 0x5208, 0x0000 }, + { 0x5209, 0x0000 }, + { 0x520a, 0x0000 }, + { 0x520b, 0x0000 }, + { 0x520c, 0x0000 }, + { 0x520d, 0x0000 }, + { 0x520e, 0x0000 }, + { 0x520f, 0x0000 }, + { 0x5210, 0x0000 }, + { 0x5211, 0x0000 }, + { 0x5212, 0x0000 }, + { 0x5213, 0x0000 }, + { 0x5214, 0x0000 }, + { 0x5215, 0x0000 }, + { 0x5216, 0x0000 }, + { 0x5217, 0x0000 }, + { 0x5218, 0x0000 }, + { 0x5219, 0x0000 }, + { 0x521a, 0x0000 }, + { 0x521b, 0x0000 }, + { 0x521c, 0x0000 }, + { 0x521d, 0x0000 }, + { 0x521e, 0x0000 }, + { 0x521f, 0x0000 }, + { 0x5220, 0x0000 }, + { 0x5221, 0x0000 }, + { 0x5222, 0x0000 }, + { 0x5223, 0x0000 }, + { 0x5224, 0x0000 }, + { 0x5225, 0x0000 }, + { 0x5226, 0x0000 }, + { 0x5227, 0x0000 }, + { 0x5228, 0x0000 }, + { 0x5229, 0x0000 }, + { 0x522a, 0x0000 }, + { 0x522b, 0x0000 }, + { 0x522c, 0x0000 }, + { 0x522d, 0x0000 }, + { 0x522e, 0x0000 }, + { 0x522f, 0x0000 }, + { 0x5230, 0x0000 }, + { 0x5231, 0x0000 }, + { 0x5232, 0x0000 }, + { 0x5233, 0x0000 }, + { 0x5234, 0x0000 }, + { 0x5235, 0x0000 }, + { 0x5236, 0x0000 }, + { 0x5237, 0x0000 }, + { 0x5238, 0x0000 }, + { 0x5239, 0x0000 }, + { 0x523a, 0x0000 }, + { 0x523b, 0x0000 }, + { 0x523c, 0x0000 }, + { 0x523d, 0x0000 }, + { 0x523e, 0x0000 }, + { 0x523f, 0x0000 }, + { 0x5240, 0x0000 }, + { 0x5241, 0x0000 }, + { 0x5242, 0x0000 }, + { 0x5243, 0x0000 }, + { 0x5244, 0x0000 }, + { 0x5245, 0x0000 }, + { 0x5246, 0x0000 }, + { 0x5247, 0x0000 }, + { 0x5248, 0x0000 }, + { 0x5249, 0x0000 }, + { 0x524a, 0x0000 }, + { 0x524b, 0x0000 }, + { 0x524c, 0x0000 }, + { 0x524d, 0x0000 }, + { 0x524e, 0x0000 }, + { 0x524f, 0x0000 }, + { 0x5250, 0x0000 }, + { 0x5251, 0x0000 }, + { 0x5252, 0x0000 }, + { 0x5253, 0x0000 }, + { 0x5254, 0x0000 }, + { 0x5255, 0x0000 }, + { 0x5256, 0x0000 }, + { 0x5257, 0x0000 }, + { 0x5258, 0x0000 }, + { 0x5259, 0x0000 }, + { 0x525a, 0x0000 }, + { 0x525b, 0x0000 }, + { 0x525c, 0x0000 }, + { 0x525d, 0x0000 }, + { 0x525e, 0x0000 }, + { 0x525f, 0x0000 }, + { 0x5260, 0x0000 }, + { 0x5261, 0x0000 }, + { 0x5262, 0x0000 }, + { 0x5263, 0x0000 }, + { 0x5264, 0x0000 }, + { 0x5265, 0x0000 }, + { 0x5266, 0x0000 }, + { 0x5267, 0x0000 }, + { 0x5268, 0x0000 }, + { 0x5269, 0x0000 }, + { 0x526a, 0x0000 }, + { 0x526b, 0x0000 }, + { 0x526c, 0x0000 }, + { 0x526d, 0x0000 }, + { 0x526e, 0x0000 }, + { 0x526f, 0x0000 }, + { 0x5270, 0x0000 }, + { 0x5271, 0x0000 }, + { 0x5272, 0x0000 }, + { 0x5273, 0x0000 }, + { 0x5274, 0x0000 }, + { 0x5275, 0x0000 }, + { 0x5276, 0x0000 }, + { 0x5277, 0x0000 }, + { 0x5278, 0x0000 }, + { 0x5279, 0x0000 }, + { 0x527a, 0x0000 }, + { 0x527b, 0x0000 }, + { 0x527c, 0x0000 }, + { 0x527d, 0x0000 }, + { 0x527e, 0x0000 }, + { 0x527f, 0x0000 }, + { 0x5280, 0x0000 }, + { 0x5281, 0x0000 }, + { 0x5282, 0x0000 }, + { 0x5283, 0x0000 }, + { 0x5284, 0x0000 }, + { 0x5285, 0x0000 }, + { 0x5286, 0x0000 }, + { 0x5287, 0x0000 }, + { 0x5288, 0x0000 }, + { 0x5289, 0x0000 }, + { 0x528a, 0x0000 }, + { 0x528b, 0x0000 }, + { 0x528c, 0x0000 }, + { 0x528d, 0x0000 }, + { 0x528e, 0x0000 }, + { 0x528f, 0x0000 }, + { 0x5290, 0x0000 }, + { 0x5291, 0x0000 }, + { 0x5292, 0x0000 }, + { 0x5293, 0x0000 }, + { 0x5294, 0x0000 }, + { 0x5295, 0x0000 }, + { 0x5296, 0x0000 }, + { 0x5297, 0x0000 }, + { 0x5298, 0x0000 }, + { 0x5299, 0x0000 }, + { 0x529a, 0x0000 }, + { 0x529b, 0x0000 }, + { 0x529c, 0x0000 }, + { 0x529d, 0x0000 }, + { 0x529e, 0x0000 }, + { 0x529f, 0x0000 }, + { 0x52a0, 0x0000 }, + { 0x52a1, 0x0000 }, + { 0x52a2, 0x0000 }, + { 0x52a3, 0x0000 }, + { 0x52a4, 0x0000 }, + { 0x52a5, 0x0000 }, + { 0x52a6, 0x0000 }, + { 0x52a7, 0x0000 }, + { 0x52a8, 0x0000 }, + { 0x52a9, 0x0000 }, + { 0x52aa, 0x0000 }, + { 0x52ab, 0x0000 }, + { 0x52ac, 0x0000 }, + { 0x52ad, 0x0000 }, + { 0x52ae, 0x0000 }, + { 0x52af, 0x0000 }, + { 0x52b0, 0x0000 }, + { 0x52b1, 0x0000 }, + { 0x52b2, 0x0000 }, + { 0x52b3, 0x0000 }, + { 0x52b4, 0x0000 }, + { 0x52b5, 0x0000 }, + { 0x52b6, 0x0000 }, + { 0x52b7, 0x0000 }, + { 0x52b8, 0x0000 }, + { 0x52b9, 0x0000 }, + { 0x52ba, 0x0000 }, + { 0x52bb, 0x0000 }, + { 0x52bc, 0x0000 }, + { 0x52bd, 0x0000 }, + { 0x52be, 0x0000 }, + { 0x52bf, 0x0000 }, + { 0x52c0, 0x0000 }, + { 0x52c1, 0x0000 }, + { 0x52c2, 0x0000 }, + { 0x52c3, 0x0000 }, + { 0x52c4, 0x0000 }, + { 0x52c5, 0x0000 }, + { 0x52c6, 0x0000 }, + { 0x52c7, 0x0000 }, + { 0x52c8, 0x0000 }, + { 0x52c9, 0x0000 }, + { 0x52ca, 0x0000 }, + { 0x52cb, 0x0000 }, + { 0x52cc, 0x0000 }, + { 0x52cd, 0x0000 }, + { 0x52ce, 0x0000 }, + { 0x52cf, 0x0000 }, + { 0x52d0, 0x0000 }, + { 0x52d1, 0x0000 }, + { 0x52d2, 0x0000 }, + { 0x52d3, 0x0000 }, + { 0x52d4, 0x0000 }, + { 0x52d5, 0x0000 }, + { 0x52d6, 0x0000 }, + { 0x52d7, 0x0000 }, + { 0x52d8, 0x0000 }, + { 0x52d9, 0x0000 }, + { 0x52da, 0x0000 }, + { 0x52db, 0x0000 }, + { 0x52dc, 0x0000 }, + { 0x52dd, 0x0000 }, + { 0x52de, 0x0000 }, + { 0x52df, 0x0000 }, + { 0x52e0, 0x0000 }, + { 0x52e1, 0x0000 }, + { 0x52e2, 0x0000 }, + { 0x52e3, 0x0000 }, + { 0x52e4, 0x0000 }, + { 0x52e5, 0x0000 }, + { 0x52e6, 0x0000 }, + { 0x52e7, 0x0000 }, + { 0x52e8, 0x0000 }, + { 0x52e9, 0x0000 }, + { 0x52ea, 0x0000 }, + { 0x52eb, 0x0000 }, + { 0x52ec, 0x0000 }, + { 0x52ed, 0x0000 }, + { 0x52ee, 0x0000 }, + { 0x52ef, 0x0000 }, + { 0x52f0, 0x0000 }, + { 0x52f1, 0x0000 }, + { 0x52f2, 0x0000 }, + { 0x52f3, 0x0000 }, + { 0x52f4, 0x0000 }, + { 0x52f5, 0x0000 }, + { 0x52f6, 0x0000 }, + { 0x52f7, 0x0000 }, + { 0x52f8, 0x0000 }, + { 0x52f9, 0x0000 }, + { 0x52fa, 0x0000 }, + { 0x52fb, 0x0000 }, + { 0x52fc, 0x0000 }, + { 0x52fd, 0x0000 }, + { 0x52fe, 0x0000 }, + { 0x52ff, 0x0000 }, + { 0x5300, 0x0000 }, + { 0x5301, 0x0000 }, + { 0x5302, 0x0000 }, + { 0x5303, 0x0000 }, + { 0x5304, 0x0000 }, + { 0x5305, 0x0000 }, + { 0x5306, 0x0000 }, + { 0x5307, 0x0000 }, + { 0x5308, 0x0000 }, + { 0x5309, 0x0000 }, + { 0x530a, 0x0000 }, + { 0x530b, 0x0000 }, + { 0x530c, 0x0000 }, + { 0x530d, 0x0000 }, + { 0x530e, 0x0000 }, + { 0x530f, 0x0000 }, + { 0x5310, 0x0000 }, + { 0x5311, 0x0000 }, + { 0x5312, 0x0000 }, + { 0x5313, 0x0000 }, + { 0x5314, 0x0000 }, + { 0x5315, 0x0000 }, + { 0x5316, 0x0000 }, + { 0x5317, 0x0000 }, + { 0x5318, 0x0000 }, + { 0x5319, 0x0000 }, + { 0x531a, 0x0000 }, + { 0x531b, 0x0000 }, + { 0x531c, 0x0000 }, + { 0x531d, 0x0000 }, + { 0x531e, 0x0000 }, + { 0x531f, 0x0000 }, + { 0x5320, 0x0000 }, + { 0x5321, 0x0000 }, + { 0x5322, 0x0000 }, + { 0x5323, 0x0000 }, + { 0x5324, 0x0000 }, + { 0x5325, 0x0000 }, + { 0x5326, 0x0000 }, + { 0x5327, 0x0000 }, + { 0x5328, 0x0000 }, + { 0x5329, 0x0000 }, + { 0x532a, 0x0000 }, + { 0x532b, 0x0000 }, + { 0x532c, 0x0000 }, + { 0x532d, 0x0000 }, + { 0x532e, 0x0000 }, + { 0x532f, 0x0000 }, + { 0x5330, 0x0000 }, + { 0x5331, 0x0000 }, + { 0x5332, 0x0000 }, + { 0x5333, 0x0000 }, + { 0x5334, 0x0000 }, + { 0x5335, 0x0000 }, + { 0x5336, 0x0000 }, + { 0x5337, 0x0000 }, + { 0x5338, 0x0000 }, + { 0x5339, 0x0000 }, + { 0x533a, 0x0000 }, + { 0x533b, 0x0000 }, + { 0x533c, 0x0000 }, + { 0x533d, 0x0000 }, + { 0x533e, 0x0000 }, + { 0x533f, 0x0000 }, + { 0x5340, 0x0000 }, + { 0x5341, 0x0000 }, + { 0x5342, 0x0000 }, + { 0x5343, 0x0000 }, + { 0x5344, 0x0000 }, + { 0x5345, 0x0000 }, + { 0x5346, 0x0000 }, + { 0x5347, 0x0000 }, + { 0x5348, 0x0000 }, + { 0x5349, 0x0000 }, + { 0x534a, 0x0000 }, + { 0x534b, 0x0000 }, + { 0x534c, 0x0000 }, + { 0x534d, 0x0000 }, + { 0x534e, 0x0000 }, + { 0x534f, 0x0000 }, + { 0x5350, 0x0000 }, + { 0x5351, 0x0000 }, + { 0x5352, 0x0000 }, + { 0x5353, 0x0000 }, + { 0x5354, 0x0000 }, + { 0x5355, 0x0000 }, + { 0x5356, 0x0000 }, + { 0x5357, 0x0000 }, + { 0x5358, 0x0000 }, + { 0x5359, 0x0000 }, + { 0x535a, 0x0000 }, + { 0x535b, 0x0000 }, + { 0x535c, 0x0000 }, + { 0x535d, 0x0000 }, + { 0x535e, 0x0000 }, + { 0x535f, 0x0000 }, + { 0x5360, 0x0000 }, + { 0x5361, 0x0000 }, + { 0x5362, 0x0000 }, + { 0x5363, 0x0000 }, + { 0x5364, 0x0000 }, + { 0x5365, 0x0000 }, + { 0x5366, 0x0000 }, + { 0x5367, 0x0000 }, + { 0x5368, 0x0000 }, + { 0x5369, 0x0000 }, + { 0x536a, 0x0000 }, + { 0x536b, 0x0000 }, + { 0x536c, 0x0000 }, + { 0x536d, 0x0000 }, + { 0x536e, 0x0000 }, + { 0x536f, 0x0000 }, + { 0x5370, 0x0000 }, + { 0x5371, 0x0000 }, + { 0x5372, 0x0000 }, + { 0x5373, 0x0000 }, + { 0x5374, 0x0000 }, + { 0x5375, 0x0000 }, + { 0x5376, 0x0000 }, + { 0x5377, 0x0000 }, + { 0x5378, 0x0000 }, + { 0x5379, 0x0000 }, + { 0x537a, 0x0000 }, + { 0x537b, 0x0000 }, + { 0x537c, 0x0000 }, + { 0x537d, 0x0000 }, + { 0x537e, 0x0000 }, + { 0x537f, 0x0000 }, + { 0x5380, 0x0000 }, + { 0x5381, 0x0000 }, + { 0x5382, 0x0000 }, + { 0x5383, 0x0000 }, + { 0x5384, 0x0000 }, + { 0x5385, 0x0000 }, + { 0x5386, 0x0000 }, + { 0x5387, 0x0000 }, + { 0x5388, 0x0000 }, + { 0x5389, 0x0000 }, + { 0x538a, 0x0000 }, + { 0x538b, 0x0000 }, + { 0x538c, 0x0000 }, + { 0x538d, 0x0000 }, + { 0x538e, 0x0000 }, + { 0x538f, 0x0000 }, + { 0x5390, 0x0000 }, + { 0x5391, 0x0000 }, + { 0x5392, 0x0000 }, + { 0x5393, 0x0000 }, + { 0x5394, 0x0000 }, + { 0x5395, 0x0000 }, + { 0x5396, 0x0000 }, + { 0x5397, 0x0000 }, + { 0x5398, 0x0000 }, + { 0x5399, 0x0000 }, + { 0x539a, 0x0000 }, + { 0x539b, 0x0000 }, + { 0x539c, 0x0000 }, + { 0x539d, 0x0000 }, + { 0x539e, 0x0000 }, + { 0x539f, 0x0000 }, + { 0x53a0, 0x0000 }, + { 0x53a1, 0x0000 }, + { 0x53a2, 0x0000 }, + { 0x53a3, 0x0000 }, + { 0x53a4, 0x0000 }, + { 0x53a5, 0x0000 }, + { 0x53a6, 0x0000 }, + { 0x53a7, 0x0000 }, + { 0x53a8, 0x0000 }, + { 0x53a9, 0x0000 }, + { 0x53aa, 0x0000 }, + { 0x53ab, 0x0000 }, + { 0x53ac, 0x0000 }, + { 0x53ad, 0x0000 }, + { 0x53ae, 0x0000 }, + { 0x53af, 0x0000 }, + { 0x53b0, 0x0000 }, + { 0x53b1, 0x0000 }, + { 0x53b2, 0x0000 }, + { 0x53b3, 0x0000 }, + { 0x53b4, 0x0000 }, + { 0x53b5, 0x0000 }, + { 0x53b6, 0x0000 }, + { 0x53b7, 0x0000 }, + { 0x53b8, 0x0000 }, + { 0x53b9, 0x0000 }, + { 0x53ba, 0x0000 }, + { 0x53bb, 0x0000 }, + { 0x53bc, 0x0000 }, + { 0x53bd, 0x0000 }, + { 0x53be, 0x0000 }, + { 0x53bf, 0x0000 }, + { 0x53c0, 0x0000 }, + { 0x53c1, 0x0000 }, + { 0x53c2, 0x0000 }, + { 0x53c3, 0x0000 }, + { 0x53c4, 0x0000 }, + { 0x53c5, 0x0000 }, + { 0x53c6, 0x0000 }, + { 0x53c7, 0x0000 }, + { 0x53c8, 0x0000 }, + { 0x53c9, 0x0000 }, + { 0x53ca, 0x0000 }, + { 0x53cb, 0x0000 }, + { 0x53cc, 0x0000 }, + { 0x53cd, 0x0000 }, + { 0x53ce, 0x0000 }, + { 0x53cf, 0x0000 }, + { 0x53d0, 0x0000 }, + { 0x53d1, 0x0000 }, + { 0x53d2, 0x0000 }, + { 0x53d3, 0x0000 }, + { 0x53d4, 0x0000 }, + { 0x53d5, 0x0000 }, + { 0x53d6, 0x0000 }, + { 0x53d7, 0x0000 }, + { 0x53d8, 0x0000 }, + { 0x53d9, 0x0000 }, + { 0x53da, 0x0000 }, + { 0x53db, 0x0000 }, + { 0x53dc, 0x0000 }, + { 0x53dd, 0x0000 }, + { 0x53de, 0x0000 }, + { 0x53df, 0x0000 }, + { 0x53e0, 0x0000 }, + { 0x53e1, 0x0000 }, + { 0x53e2, 0x0000 }, + { 0x53e3, 0x0000 }, + { 0x53e4, 0x0000 }, + { 0x53e5, 0x0000 }, + { 0x53e6, 0x0000 }, + { 0x53e7, 0x0000 }, + { 0x53e8, 0x0000 }, + { 0x53e9, 0x0000 }, + { 0x53ea, 0x0000 }, + { 0x53eb, 0x0000 }, + { 0x53ec, 0x0000 }, + { 0x53ed, 0x0000 }, + { 0x53ee, 0x0000 }, + { 0x53ef, 0x0000 }, + { 0x53f0, 0x0000 }, + { 0x53f1, 0x0000 }, + { 0x53f2, 0x0000 }, + { 0x53f3, 0x0000 }, + { 0x53f4, 0x0000 }, + { 0x53f5, 0x0000 }, + { 0x53f6, 0x0000 }, + { 0x53f7, 0x0000 }, + { 0x53f8, 0x0000 }, + { 0x53f9, 0x0000 }, + { 0x53fa, 0x0000 }, + { 0x53fb, 0x0000 }, + { 0x53fc, 0x0000 }, + { 0x53fd, 0x0000 }, + { 0x53fe, 0x0000 }, + { 0x53ff, 0x0000 }, + { 0x5400, 0x0000 }, + { 0x5401, 0x0000 }, + { 0x5402, 0x0000 }, + { 0x5403, 0x0000 }, + { 0x5404, 0x0000 }, + { 0x5405, 0x0000 }, + { 0x5406, 0x0000 }, + { 0x5407, 0x0000 }, + { 0x5408, 0x0000 }, + { 0x5409, 0x0000 }, + { 0x540a, 0x0000 }, + { 0x540b, 0x0000 }, + { 0x540c, 0x0000 }, + { 0x540d, 0x0000 }, + { 0x540e, 0x0000 }, + { 0x540f, 0x0000 }, + { 0x5410, 0x0000 }, + { 0x5411, 0x0000 }, + { 0x5412, 0x0000 }, + { 0x5413, 0x0000 }, + { 0x5414, 0x0000 }, + { 0x5415, 0x0000 }, + { 0x5416, 0x0000 }, + { 0x5417, 0x0000 }, + { 0x5418, 0x0000 }, + { 0x5419, 0x0000 }, + { 0x541a, 0x0000 }, + { 0x541b, 0x0000 }, + { 0x541c, 0x0000 }, + { 0x541d, 0x0000 }, + { 0x541e, 0x0000 }, + { 0x541f, 0x0000 }, + { 0x5420, 0x0000 }, + { 0x5421, 0x0000 }, + { 0x5422, 0x0000 }, + { 0x5423, 0x0000 }, + { 0x5424, 0x0000 }, + { 0x5425, 0x0000 }, + { 0x5426, 0x0000 }, + { 0x5427, 0x0000 }, + { 0x5428, 0x0000 }, + { 0x5429, 0x0000 }, + { 0x542a, 0x0000 }, + { 0x542b, 0x0000 }, + { 0x542c, 0x0000 }, + { 0x542d, 0x0000 }, + { 0x542e, 0x0000 }, + { 0x542f, 0x0000 }, + { 0x5430, 0x0000 }, + { 0x5431, 0x0000 }, + { 0x5432, 0x0000 }, + { 0x5433, 0x0000 }, + { 0x5434, 0x0000 }, + { 0x5435, 0x0000 }, + { 0x5436, 0x0000 }, + { 0x5437, 0x0000 }, + { 0x5438, 0x0000 }, + { 0x5439, 0x0000 }, + { 0x543a, 0x0000 }, + { 0x543b, 0x0000 }, + { 0x543c, 0x0000 }, + { 0x543d, 0x0000 }, + { 0x543e, 0x0000 }, + { 0x543f, 0x0000 }, + { 0x5440, 0x0000 }, + { 0x5441, 0x0000 }, + { 0x5442, 0x0000 }, + { 0x5443, 0x0000 }, + { 0x5444, 0x0000 }, + { 0x5445, 0x0000 }, + { 0x5446, 0x0000 }, + { 0x5447, 0x0000 }, + { 0x5448, 0x0000 }, + { 0x5449, 0x0000 }, + { 0x544a, 0x0000 }, + { 0x544b, 0x0000 }, + { 0x544c, 0x0000 }, + { 0x544d, 0x0000 }, + { 0x544e, 0x0000 }, + { 0x544f, 0x0000 }, + { 0x5450, 0x0000 }, + { 0x5451, 0x0000 }, + { 0x5452, 0x0000 }, + { 0x5453, 0x0000 }, + { 0x5454, 0x0000 }, + { 0x5455, 0x0000 }, + { 0x5456, 0x0000 }, + { 0x5457, 0x0000 }, + { 0x5458, 0x0000 }, + { 0x5459, 0x0000 }, + { 0x545a, 0x0000 }, + { 0x545b, 0x0000 }, + { 0x545c, 0x0000 }, + { 0x545d, 0x0000 }, + { 0x545e, 0x0000 }, + { 0x545f, 0x0000 }, + { 0x5460, 0x0000 }, + { 0x5461, 0x0000 }, + { 0x5462, 0x0000 }, + { 0x5463, 0x0000 }, + { 0x5464, 0x0000 }, + { 0x5465, 0x0000 }, + { 0x5466, 0x0000 }, + { 0x5467, 0x0000 }, + { 0x5468, 0x0000 }, + { 0x5469, 0x0000 }, + { 0x546a, 0x0000 }, + { 0x546b, 0x0000 }, + { 0x546c, 0x0000 }, + { 0x546d, 0x0000 }, + { 0x546e, 0x0000 }, + { 0x546f, 0x0000 }, + { 0x5470, 0x0000 }, + { 0x5471, 0x0000 }, + { 0x5472, 0x0000 }, + { 0x5473, 0x0000 }, + { 0x5474, 0x0000 }, + { 0x5475, 0x0000 }, + { 0x5476, 0x0000 }, + { 0x5477, 0x0000 }, + { 0x5478, 0x0000 }, + { 0x5479, 0x0000 }, + { 0x547a, 0x0000 }, + { 0x547b, 0x0000 }, + { 0x547c, 0x0000 }, + { 0x547d, 0x0000 }, + { 0x547e, 0x0000 }, + { 0x547f, 0x0000 }, + { 0x5480, 0x0000 }, + { 0x5481, 0x0000 }, + { 0x5482, 0x0000 }, + { 0x5483, 0x0000 }, + { 0x5484, 0x0000 }, + { 0x5485, 0x0000 }, + { 0x5486, 0x0000 }, + { 0x5487, 0x0000 }, + { 0x5488, 0x0000 }, + { 0x5489, 0x0000 }, + { 0x548a, 0x0000 }, + { 0x548b, 0x0000 }, + { 0x548c, 0x0000 }, + { 0x548d, 0x0000 }, + { 0x548e, 0x0000 }, + { 0x548f, 0x0000 }, + { 0x5490, 0x0000 }, + { 0x5491, 0x0000 }, + { 0x5492, 0x0000 }, + { 0x5493, 0x0000 }, + { 0x5494, 0x0000 }, + { 0x5495, 0x0000 }, + { 0x5496, 0x0000 }, + { 0x5497, 0x0000 }, + { 0x5498, 0x0000 }, + { 0x5499, 0x0000 }, + { 0x549a, 0x0000 }, + { 0x549b, 0x0000 }, + { 0x549c, 0x0000 }, + { 0x549d, 0x0000 }, + { 0x549e, 0x0000 }, + { 0x549f, 0x0000 }, + { 0x54a0, 0x0000 }, + { 0x54a1, 0x0000 }, + { 0x54a2, 0x0000 }, + { 0x54a3, 0x0000 }, + { 0x54a4, 0x0000 }, + { 0x54a5, 0x0000 }, + { 0x54a6, 0x0000 }, + { 0x54a7, 0x0000 }, + { 0x54a8, 0x0000 }, + { 0x54a9, 0x0000 }, + { 0x54aa, 0x0000 }, + { 0x54ab, 0x0000 }, + { 0x54ac, 0x0000 }, + { 0x54ad, 0x0000 }, + { 0x54ae, 0x0000 }, + { 0x54af, 0x0000 }, + { 0x54b0, 0x0000 }, + { 0x54b1, 0x0000 }, + { 0x54b2, 0x0000 }, + { 0x54b3, 0x0000 }, + { 0x54b4, 0x0000 }, + { 0x54b5, 0x0000 }, + { 0x54b6, 0x0000 }, + { 0x54b7, 0x0000 }, + { 0x54b8, 0x0000 }, + { 0x54b9, 0x0000 }, + { 0x54ba, 0x0000 }, + { 0x54bb, 0x0000 }, + { 0x54bc, 0x0000 }, + { 0x54bd, 0x0000 }, + { 0x54be, 0x0000 }, + { 0x54bf, 0x0000 }, + { 0x54c0, 0x0000 }, + { 0x54c1, 0x0000 }, + { 0x54c2, 0x0000 }, + { 0x54c3, 0x0000 }, + { 0x54c4, 0x0000 }, + { 0x54c5, 0x0000 }, + { 0x54c6, 0x0000 }, + { 0x54c7, 0x0000 }, + { 0x54c8, 0x0000 }, + { 0x54c9, 0x0000 }, + { 0x54ca, 0x0000 }, + { 0x54cb, 0x0000 }, + { 0x54cc, 0x0000 }, + { 0x54cd, 0x0000 }, + { 0x54ce, 0x0000 }, + { 0x54cf, 0x0000 }, + { 0x54d0, 0x0000 }, + { 0x54d1, 0x0000 }, + { 0x54d2, 0x0000 }, + { 0x54d3, 0x0000 }, + { 0x54d4, 0x0000 }, + { 0x54d5, 0x0000 }, + { 0x54d6, 0x0000 }, + { 0x54d7, 0x0000 }, + { 0x54d8, 0x0000 }, + { 0x54d9, 0x0000 }, + { 0x54da, 0x0000 }, + { 0x54db, 0x0000 }, + { 0x54dc, 0x0000 }, + { 0x54dd, 0x0000 }, + { 0x54de, 0x0000 }, + { 0x54df, 0x0000 }, + { 0x54e0, 0x0000 }, + { 0x54e1, 0x0000 }, + { 0x54e2, 0x0000 }, + { 0x54e3, 0x0000 }, + { 0x54e4, 0x0000 }, + { 0x54e5, 0x0000 }, + { 0x54e6, 0x0000 }, + { 0x54e7, 0x0000 }, + { 0x54e8, 0x0000 }, + { 0x54e9, 0x0000 }, + { 0x54ea, 0x0000 }, + { 0x54eb, 0x0000 }, + { 0x54ec, 0x0000 }, + { 0x54ed, 0x0000 }, + { 0x54ee, 0x0000 }, + { 0x54ef, 0x0000 }, + { 0x54f0, 0x0000 }, + { 0x54f1, 0x0000 }, + { 0x54f2, 0x0000 }, + { 0x54f3, 0x0000 }, + { 0x54f4, 0x0000 }, + { 0x54f5, 0x0000 }, + { 0x54f6, 0x0000 }, + { 0x54f7, 0x0000 }, + { 0x54f8, 0x0000 }, + { 0x54f9, 0x0000 }, + { 0x54fa, 0x0000 }, + { 0x54fb, 0x0000 }, + { 0x54fc, 0x0000 }, + { 0x54fd, 0x0000 }, + { 0x54fe, 0x0000 }, + { 0x54ff, 0x0000 }, + { 0x5500, 0x0000 }, + { 0x5501, 0x0000 }, + { 0x5502, 0x0000 }, + { 0x5503, 0x0000 }, + { 0x5504, 0x0000 }, + { 0x5505, 0x0000 }, + { 0x5506, 0x0000 }, + { 0x5507, 0x0000 }, + { 0x5508, 0x0000 }, + { 0x5509, 0x0000 }, + { 0x550a, 0x0000 }, + { 0x550b, 0x0000 }, + { 0x550c, 0x0000 }, + { 0x550d, 0x0000 }, + { 0x550e, 0x0000 }, + { 0x550f, 0x0000 }, + { 0x5510, 0x0000 }, + { 0x5511, 0x0000 }, + { 0x5512, 0x0000 }, + { 0x5513, 0x0000 }, + { 0x5514, 0x0000 }, + { 0x5515, 0x0000 }, + { 0x5516, 0x0000 }, + { 0x5517, 0x0000 }, + { 0x5518, 0x0000 }, + { 0x5519, 0x0000 }, + { 0x551a, 0x0000 }, + { 0x551b, 0x0000 }, + { 0x551c, 0x0000 }, + { 0x551d, 0x0000 }, + { 0x551e, 0x0000 }, + { 0x551f, 0x0000 }, + { 0x5520, 0x0000 }, + { 0x5521, 0x0000 }, + { 0x5522, 0x0000 }, + { 0x5523, 0x0000 }, + { 0x5524, 0x0000 }, + { 0x5525, 0x0000 }, + { 0x5526, 0x0000 }, + { 0x5527, 0x0000 }, + { 0x5528, 0x0000 }, + { 0x5529, 0x0000 }, + { 0x552a, 0x0000 }, + { 0x552b, 0x0000 }, + { 0x552c, 0x0000 }, + { 0x552d, 0x0000 }, + { 0x552e, 0x0000 }, + { 0x552f, 0x0000 }, + { 0x5530, 0x0000 }, + { 0x5531, 0x0000 }, + { 0x5532, 0x0000 }, + { 0x5533, 0x0000 }, + { 0x5534, 0x0000 }, + { 0x5535, 0x0000 }, + { 0x5536, 0x0000 }, + { 0x5537, 0x0000 }, + { 0x5538, 0x0000 }, + { 0x5539, 0x0000 }, + { 0x553a, 0x0000 }, + { 0x553b, 0x0000 }, + { 0x553c, 0x0000 }, + { 0x553d, 0x0000 }, + { 0x553e, 0x0000 }, + { 0x553f, 0x0000 }, + { 0x5540, 0x0000 }, + { 0x5541, 0x0000 }, + { 0x5542, 0x0000 }, + { 0x5543, 0x0000 }, + { 0x5544, 0x0000 }, + { 0x5545, 0x0000 }, + { 0x5546, 0x0000 }, + { 0x5547, 0x0000 }, + { 0x5548, 0x0000 }, + { 0x5549, 0x0000 }, + { 0x554a, 0x0000 }, + { 0x554b, 0x0000 }, + { 0x554c, 0x0000 }, + { 0x554d, 0x0000 }, + { 0x554e, 0x0000 }, + { 0x554f, 0x0000 }, + { 0x5550, 0x0000 }, + { 0x5551, 0x0000 }, + { 0x5552, 0x0000 }, + { 0x5553, 0x0000 }, + { 0x5554, 0x0000 }, + { 0x5555, 0x0000 }, + { 0x5556, 0x0000 }, + { 0x5557, 0x0000 }, + { 0x5558, 0x0000 }, + { 0x5559, 0x0000 }, + { 0x555a, 0x0000 }, + { 0x555b, 0x0000 }, + { 0x555c, 0x0000 }, + { 0x555d, 0x0000 }, + { 0x555e, 0x0000 }, + { 0x555f, 0x0000 }, + { 0x5560, 0x0000 }, + { 0x5561, 0x0000 }, + { 0x5562, 0x0000 }, + { 0x5563, 0x0000 }, + { 0x5564, 0x0000 }, + { 0x5565, 0x0000 }, + { 0x5566, 0x0000 }, + { 0x5567, 0x0000 }, + { 0x5568, 0x0000 }, + { 0x5569, 0x0000 }, + { 0x556a, 0x0000 }, + { 0x556b, 0x0000 }, + { 0x556c, 0x0000 }, + { 0x556d, 0x0000 }, + { 0x556e, 0x0000 }, + { 0x556f, 0x0000 }, + { 0x5570, 0x0000 }, + { 0x5571, 0x0000 }, + { 0x5572, 0x0000 }, + { 0x5573, 0x0000 }, + { 0x5574, 0x0000 }, + { 0x5575, 0x0000 }, + { 0x5576, 0x0000 }, + { 0x5577, 0x0000 }, + { 0x5578, 0x0000 }, + { 0x5579, 0x0000 }, + { 0x557a, 0x0000 }, + { 0x557b, 0x0000 }, + { 0x557c, 0x0000 }, + { 0x557d, 0x0000 }, + { 0x557e, 0x0000 }, + { 0x557f, 0x0000 }, + { 0x5580, 0x0000 }, + { 0x5581, 0x0000 }, + { 0x5582, 0x0000 }, + { 0x5583, 0x0000 }, + { 0x5584, 0x0000 }, + { 0x5585, 0x0000 }, + { 0x5586, 0x0000 }, + { 0x5587, 0x0000 }, + { 0x5588, 0x0000 }, + { 0x5589, 0x0000 }, + { 0x558a, 0x0000 }, + { 0x558b, 0x0000 }, + { 0x558c, 0x0000 }, + { 0x558d, 0x0000 }, + { 0x558e, 0x0000 }, + { 0x558f, 0x0000 }, + { 0x5590, 0x0000 }, + { 0x5591, 0x0000 }, + { 0x5592, 0x0000 }, + { 0x5593, 0x0000 }, + { 0x5594, 0x0000 }, + { 0x5595, 0x0000 }, + { 0x5596, 0x0000 }, + { 0x5597, 0x0000 }, + { 0x5598, 0x0000 }, + { 0x5599, 0x0000 }, + { 0x559a, 0x0000 }, + { 0x559b, 0x0000 }, + { 0x559c, 0x0000 }, + { 0x559d, 0x0000 }, + { 0x559e, 0x0000 }, + { 0x559f, 0x0000 }, + { 0x55a0, 0x0000 }, + { 0x55a1, 0x0000 }, + { 0x55a2, 0x0000 }, + { 0x55a3, 0x0000 }, + { 0x55a4, 0x0000 }, + { 0x55a5, 0x0000 }, + { 0x55a6, 0x0000 }, + { 0x55a7, 0x0000 }, + { 0x55a8, 0x0000 }, + { 0x55a9, 0x0000 }, + { 0x55aa, 0x0000 }, + { 0x55ab, 0x0000 }, + { 0x55ac, 0x0000 }, + { 0x55ad, 0x0000 }, + { 0x55ae, 0x0000 }, + { 0x55af, 0x0000 }, + { 0x55b0, 0x0000 }, + { 0x55b1, 0x0000 }, + { 0x55b2, 0x0000 }, + { 0x55b3, 0x0000 }, + { 0x55b4, 0x0000 }, + { 0x55b5, 0x0000 }, + { 0x55b6, 0x0000 }, + { 0x55b7, 0x0000 }, + { 0x55b8, 0x0000 }, + { 0x55b9, 0x0000 }, + { 0x55ba, 0x0000 }, + { 0x55bb, 0x0000 }, + { 0x55bc, 0x0000 }, + { 0x55bd, 0x0000 }, + { 0x55be, 0x0000 }, + { 0x55bf, 0x0000 }, + { 0x55c0, 0x0000 }, + { 0x55c1, 0x0000 }, + { 0x55c2, 0x0000 }, + { 0x55c3, 0x0000 }, + { 0x55c4, 0x0000 }, + { 0x55c5, 0x0000 }, + { 0x55c6, 0x0000 }, + { 0x55c7, 0x0000 }, + { 0x55c8, 0x0000 }, + { 0x55c9, 0x0000 }, + { 0x55ca, 0x0000 }, + { 0x55cb, 0x0000 }, + { 0x55cc, 0x0000 }, + { 0x55cd, 0x0000 }, + { 0x55ce, 0x0000 }, + { 0x55cf, 0x0000 }, + { 0x55d0, 0x0000 }, + { 0x55d1, 0x0000 }, + { 0x55d2, 0x0000 }, + { 0x55d3, 0x0000 }, + { 0x55d4, 0x0000 }, + { 0x55d5, 0x0000 }, + { 0x55d6, 0x0000 }, + { 0x55d7, 0x0000 }, + { 0x55d8, 0x0000 }, + { 0x55d9, 0x0000 }, + { 0x55da, 0x0000 }, + { 0x55db, 0x0000 }, + { 0x55dc, 0x0000 }, + { 0x55dd, 0x0000 }, + { 0x55de, 0x0000 }, + { 0x55df, 0x0000 }, + { 0x55e0, 0x0000 }, + { 0x55e1, 0x0000 }, + { 0x55e2, 0x0000 }, + { 0x55e3, 0x0000 }, + { 0x55e4, 0x0000 }, + { 0x55e5, 0x0000 }, + { 0x55e6, 0x0000 }, + { 0x55e7, 0x0000 }, + { 0x55e8, 0x0000 }, + { 0x55e9, 0x0000 }, + { 0x55ea, 0x0000 }, + { 0x55eb, 0x0000 }, + { 0x55ec, 0x0000 }, + { 0x55ed, 0x0000 }, + { 0x55ee, 0x0000 }, + { 0x55ef, 0x0000 }, + { 0x55f0, 0x0000 }, + { 0x55f1, 0x0000 }, + { 0x55f2, 0x0000 }, + { 0x55f3, 0x0000 }, + { 0x55f4, 0x0000 }, + { 0x55f5, 0x0000 }, + { 0x55f6, 0x0000 }, + { 0x55f7, 0x0000 }, + { 0x55f8, 0x0000 }, + { 0x55f9, 0x0000 }, + { 0x55fa, 0x0000 }, + { 0x55fb, 0x0000 }, + { 0x55fc, 0x0000 }, + { 0x55fd, 0x0000 }, + { 0x55fe, 0x0000 }, + { 0x55ff, 0x0000 }, + { 0x5600, 0x0000 }, + { 0x5601, 0x0000 }, + { 0x5602, 0x0000 }, + { 0x5603, 0x0000 }, + { 0x5604, 0x0000 }, + { 0x5605, 0x0000 }, + { 0x5606, 0x0000 }, + { 0x5607, 0x0000 }, + { 0x5608, 0x0000 }, + { 0x5609, 0x0000 }, + { 0x560a, 0x0000 }, + { 0x560b, 0x0000 }, + { 0x560c, 0x0000 }, + { 0x560d, 0x0000 }, + { 0x560e, 0x0000 }, + { 0x560f, 0x0000 }, + { 0x5610, 0x0000 }, + { 0x5611, 0x0000 }, + { 0x5612, 0x0000 }, + { 0x5613, 0x0000 }, + { 0x5614, 0x0000 }, + { 0x5615, 0x0000 }, + { 0x5616, 0x0000 }, + { 0x5617, 0x0000 }, + { 0x5618, 0x0000 }, + { 0x5619, 0x0000 }, + { 0x561a, 0x0000 }, + { 0x561b, 0x0000 }, + { 0x561c, 0x0000 }, + { 0x561d, 0x0000 }, + { 0x561e, 0x0000 }, + { 0x561f, 0x0000 }, + { 0x5620, 0x0000 }, + { 0x5621, 0x0000 }, + { 0x5622, 0x0000 }, + { 0x5623, 0x0000 }, + { 0x5624, 0x0000 }, + { 0x5625, 0x0000 }, + { 0x5626, 0x0000 }, + { 0x5627, 0x0000 }, + { 0x5628, 0x0000 }, + { 0x5629, 0x0000 }, + { 0x562a, 0x0000 }, + { 0x562b, 0x0000 }, + { 0x562c, 0x0000 }, + { 0x562d, 0x0000 }, + { 0x562e, 0x0000 }, + { 0x562f, 0x0000 }, + { 0x5630, 0x0000 }, + { 0x5631, 0x0000 }, + { 0x5632, 0x0000 }, + { 0x5633, 0x0000 }, + { 0x5634, 0x0000 }, + { 0x5635, 0x0000 }, + { 0x5636, 0x0000 }, + { 0x5637, 0x0000 }, + { 0x5638, 0x0000 }, + { 0x5639, 0x0000 }, + { 0x563a, 0x0000 }, + { 0x563b, 0x0000 }, + { 0x563c, 0x0000 }, + { 0x563d, 0x0000 }, + { 0x563e, 0x0000 }, + { 0x563f, 0x0000 }, + { 0x5640, 0x0000 }, + { 0x5641, 0x0000 }, + { 0x5642, 0x0000 }, + { 0x5643, 0x0000 }, + { 0x5644, 0x0000 }, + { 0x5645, 0x0000 }, + { 0x5646, 0x0000 }, + { 0x5647, 0x0000 }, + { 0x5648, 0x0000 }, + { 0x5649, 0x0000 }, + { 0x564a, 0x0000 }, + { 0x564b, 0x0000 }, + { 0x564c, 0x0000 }, + { 0x564d, 0x0000 }, + { 0x564e, 0x0000 }, + { 0x564f, 0x0000 }, + { 0x5650, 0x0000 }, + { 0x5651, 0x0000 }, + { 0x5652, 0x0000 }, + { 0x5653, 0x0000 }, + { 0x5654, 0x0000 }, + { 0x5655, 0x0000 }, + { 0x5656, 0x0000 }, + { 0x5657, 0x0000 }, + { 0x5658, 0x0000 }, + { 0x5659, 0x0000 }, + { 0x565a, 0x0000 }, + { 0x565b, 0x0000 }, + { 0x565c, 0x0000 }, + { 0x565d, 0x0000 }, + { 0x565e, 0x0000 }, + { 0x565f, 0x0000 }, + { 0x5660, 0x0000 }, + { 0x5661, 0x0000 }, + { 0x5662, 0x0000 }, + { 0x5663, 0x0000 }, + { 0x5664, 0x0000 }, + { 0x5665, 0x0000 }, + { 0x5666, 0x0000 }, + { 0x5667, 0x0000 }, + { 0x5668, 0x0000 }, + { 0x5669, 0x0000 }, + { 0x566a, 0x0000 }, + { 0x566b, 0x0000 }, + { 0x566c, 0x0000 }, + { 0x566d, 0x0000 }, + { 0x566e, 0x0000 }, + { 0x566f, 0x0000 }, + { 0x5670, 0x0000 }, + { 0x5671, 0x0000 }, + { 0x5672, 0x0000 }, + { 0x5673, 0x0000 }, + { 0x5674, 0x0000 }, + { 0x5675, 0x0000 }, + { 0x5676, 0x0000 }, + { 0x5677, 0x0000 }, + { 0x5678, 0x0000 }, + { 0x5679, 0x0000 }, + { 0x567a, 0x0000 }, + { 0x567b, 0x0000 }, + { 0x567c, 0x0000 }, + { 0x567d, 0x0000 }, + { 0x567e, 0x0000 }, + { 0x567f, 0x0000 }, + { 0x5680, 0x0000 }, + { 0x5681, 0x0000 }, + { 0x5682, 0x0000 }, + { 0x5683, 0x0000 }, + { 0x5684, 0x0000 }, + { 0x5685, 0x0000 }, + { 0x5686, 0x0000 }, + { 0x5687, 0x0000 }, + { 0x5688, 0x0000 }, + { 0x5689, 0x0000 }, + { 0x568a, 0x0000 }, + { 0x568b, 0x0000 }, + { 0x568c, 0x0000 }, + { 0x568d, 0x0000 }, + { 0x568e, 0x0000 }, + { 0x568f, 0x0000 }, + { 0x5690, 0x0000 }, + { 0x5691, 0x0000 }, + { 0x5692, 0x0000 }, + { 0x5693, 0x0000 }, + { 0x5694, 0x0000 }, + { 0x5695, 0x0000 }, + { 0x5696, 0x0000 }, + { 0x5697, 0x0000 }, + { 0x5698, 0x0000 }, + { 0x5699, 0x0000 }, + { 0x569a, 0x0000 }, + { 0x569b, 0x0000 }, + { 0x569c, 0x0000 }, + { 0x569d, 0x0000 }, + { 0x569e, 0x0000 }, + { 0x569f, 0x0000 }, + { 0x56a0, 0x0000 }, + { 0x56a1, 0x0000 }, + { 0x56a2, 0x0000 }, + { 0x56a3, 0x0000 }, + { 0x56a4, 0x0000 }, + { 0x56a5, 0x0000 }, + { 0x56a6, 0x0000 }, + { 0x56a7, 0x0000 }, + { 0x56a8, 0x0000 }, + { 0x56a9, 0x0000 }, + { 0x56aa, 0x0000 }, + { 0x56ab, 0x0000 }, + { 0x56ac, 0x0000 }, + { 0x56ad, 0x0000 }, + { 0x56ae, 0x0000 }, + { 0x56af, 0x0000 }, + { 0x56b0, 0x0000 }, + { 0x56b1, 0x0000 }, + { 0x56b2, 0x0000 }, + { 0x56b3, 0x0000 }, + { 0x56b4, 0x0000 }, + { 0x56b5, 0x0000 }, + { 0x56b6, 0x0000 }, + { 0x56b7, 0x0000 }, + { 0x56b8, 0x0000 }, + { 0x56b9, 0x0000 }, + { 0x56ba, 0x0000 }, + { 0x56bb, 0x0000 }, + { 0x56bc, 0x0000 }, + { 0x56bd, 0x0000 }, + { 0x56be, 0x0000 }, + { 0x56bf, 0x0000 }, + { 0x56c0, 0x0000 }, + { 0x56c1, 0x0000 }, + { 0x56c2, 0x0000 }, + { 0x56c3, 0x0000 }, + { 0x56c4, 0x0000 }, + { 0x56c5, 0x0000 }, + { 0x56c6, 0x0000 }, + { 0x56c7, 0x0000 }, + { 0x56c8, 0x0000 }, + { 0x56c9, 0x0000 }, + { 0x56ca, 0x0000 }, + { 0x56cb, 0x0000 }, + { 0x56cc, 0x0000 }, + { 0x56cd, 0x0000 }, + { 0x56ce, 0x0000 }, + { 0x56cf, 0x0000 }, + { 0x56d0, 0x0000 }, + { 0x56d1, 0x0000 }, + { 0x56d2, 0x0000 }, + { 0x56d3, 0x0000 }, + { 0x56d4, 0x0000 }, + { 0x56d5, 0x0000 }, + { 0x56d6, 0x0000 }, + { 0x56d7, 0x0000 }, + { 0x56d8, 0x0000 }, + { 0x56d9, 0x0000 }, + { 0x56da, 0x0000 }, + { 0x56db, 0x0000 }, + { 0x56dc, 0x0000 }, + { 0x56dd, 0x0000 }, + { 0x56de, 0x0000 }, + { 0x56df, 0x0000 }, + { 0x56e0, 0x0000 }, + { 0x56e1, 0x0000 }, + { 0x56e2, 0x0000 }, + { 0x56e3, 0x0000 }, + { 0x56e4, 0x0000 }, + { 0x56e5, 0x0000 }, + { 0x56e6, 0x0000 }, + { 0x56e7, 0x0000 }, + { 0x56e8, 0x0000 }, + { 0x56e9, 0x0000 }, + { 0x56ea, 0x0000 }, + { 0x56eb, 0x0000 }, + { 0x56ec, 0x0000 }, + { 0x56ed, 0x0000 }, + { 0x56ee, 0x0000 }, + { 0x56ef, 0x0000 }, + { 0x56f0, 0x0000 }, + { 0x56f1, 0x0000 }, + { 0x56f2, 0x0000 }, + { 0x56f3, 0x0000 }, + { 0x56f4, 0x0000 }, + { 0x56f5, 0x0000 }, + { 0x56f6, 0x0000 }, + { 0x56f7, 0x0000 }, + { 0x56f8, 0x0000 }, + { 0x56f9, 0x0000 }, + { 0x56fa, 0x0000 }, + { 0x56fb, 0x0000 }, + { 0x56fc, 0x0000 }, + { 0x56fd, 0x0000 }, + { 0x56fe, 0x0000 }, + { 0x56ff, 0x0000 }, + { 0x5700, 0x0000 }, + { 0x5701, 0x0000 }, + { 0x5702, 0x0000 }, + { 0x5703, 0x0000 }, + { 0x5704, 0x0000 }, + { 0x5705, 0x0000 }, + { 0x5706, 0x0000 }, + { 0x5707, 0x0000 }, + { 0x5708, 0x0000 }, + { 0x5709, 0x0000 }, + { 0x570a, 0x0000 }, + { 0x570b, 0x0000 }, + { 0x570c, 0x0000 }, + { 0x570d, 0x0000 }, + { 0x570e, 0x0000 }, + { 0x570f, 0x0000 }, + { 0x5710, 0x0000 }, + { 0x5711, 0x0000 }, + { 0x5712, 0x0000 }, + { 0x5713, 0x0000 }, + { 0x5714, 0x0000 }, + { 0x5715, 0x0000 }, + { 0x5716, 0x0000 }, + { 0x5717, 0x0000 }, + { 0x5718, 0x0000 }, + { 0x5719, 0x0000 }, + { 0x571a, 0x0000 }, + { 0x571b, 0x0000 }, + { 0x571c, 0x0000 }, + { 0x571d, 0x0000 }, + { 0x571e, 0x0000 }, + { 0x571f, 0x0000 }, + { 0x5720, 0x0000 }, + { 0x5721, 0x0000 }, + { 0x5722, 0x0000 }, + { 0x5723, 0x0000 }, + { 0x5724, 0x0000 }, + { 0x5725, 0x0000 }, + { 0x5726, 0x0000 }, + { 0x5727, 0x0000 }, + { 0x5728, 0x0000 }, + { 0x5729, 0x0000 }, + { 0x572a, 0x0000 }, + { 0x572b, 0x0000 }, + { 0x572c, 0x0000 }, + { 0x572d, 0x0000 }, + { 0x572e, 0x0000 }, + { 0x572f, 0x0000 }, + { 0x5730, 0x0000 }, + { 0x5731, 0x0000 }, + { 0x5732, 0x0000 }, + { 0x5733, 0x0000 }, + { 0x5734, 0x0000 }, + { 0x5735, 0x0000 }, + { 0x5736, 0x0000 }, + { 0x5737, 0x0000 }, + { 0x5738, 0x0000 }, + { 0x5739, 0x0000 }, + { 0x573a, 0x0000 }, + { 0x573b, 0x0000 }, + { 0x573c, 0x0000 }, + { 0x573d, 0x0000 }, + { 0x573e, 0x0000 }, + { 0x573f, 0x0000 }, + { 0x5740, 0x0000 }, + { 0x5741, 0x0000 }, + { 0x5742, 0x0000 }, + { 0x5743, 0x0000 }, + { 0x5744, 0x0000 }, + { 0x5745, 0x0000 }, + { 0x5746, 0x0000 }, + { 0x5747, 0x0000 }, + { 0x5748, 0x0000 }, + { 0x5749, 0x0000 }, + { 0x574a, 0x0000 }, + { 0x574b, 0x0000 }, + { 0x574c, 0x0000 }, + { 0x574d, 0x0000 }, + { 0x574e, 0x0000 }, + { 0x574f, 0x0000 }, + { 0x5750, 0x0000 }, + { 0x5751, 0x0000 }, + { 0x5752, 0x0000 }, + { 0x5753, 0x0000 }, + { 0x5754, 0x0000 }, + { 0x5755, 0x0000 }, + { 0x5756, 0x0000 }, + { 0x5757, 0x0000 }, + { 0x5758, 0x0000 }, + { 0x5759, 0x0000 }, + { 0x575a, 0x0000 }, + { 0x575b, 0x0000 }, + { 0x575c, 0x0000 }, + { 0x575d, 0x0000 }, + { 0x575e, 0x0000 }, + { 0x575f, 0x0000 }, + { 0x5760, 0x0000 }, + { 0x5761, 0x0000 }, + { 0x5762, 0x0000 }, + { 0x5763, 0x0000 }, + { 0x5764, 0x0000 }, + { 0x5765, 0x0000 }, + { 0x5766, 0x0000 }, + { 0x5767, 0x0000 }, + { 0x5768, 0x0000 }, + { 0x5769, 0x0000 }, + { 0x576a, 0x0000 }, + { 0x576b, 0x0000 }, + { 0x576c, 0x0000 }, + { 0x576d, 0x0000 }, + { 0x576e, 0x0000 }, + { 0x576f, 0x0000 }, + { 0x5770, 0x0000 }, + { 0x5771, 0x0000 }, + { 0x5772, 0x0000 }, + { 0x5773, 0x0000 }, + { 0x5774, 0x0000 }, + { 0x5775, 0x0000 }, + { 0x5776, 0x0000 }, + { 0x5777, 0x0000 }, + { 0x5778, 0x0000 }, + { 0x5779, 0x0000 }, + { 0x577a, 0x0000 }, + { 0x577b, 0x0000 }, + { 0x577c, 0x0000 }, + { 0x577d, 0x0000 }, + { 0x577e, 0x0000 }, + { 0x577f, 0x0000 }, + { 0x5780, 0x0000 }, + { 0x5781, 0x0000 }, + { 0x5782, 0x0000 }, + { 0x5783, 0x0000 }, + { 0x5784, 0x0000 }, + { 0x5785, 0x0000 }, + { 0x5786, 0x0000 }, + { 0x5787, 0x0000 }, + { 0x5788, 0x0000 }, + { 0x5789, 0x0000 }, + { 0x578a, 0x0000 }, + { 0x578b, 0x0000 }, + { 0x578c, 0x0000 }, + { 0x578d, 0x0000 }, + { 0x578e, 0x0000 }, + { 0x578f, 0x0000 }, + { 0x5790, 0x0000 }, + { 0x5791, 0x0000 }, + { 0x5792, 0x0000 }, + { 0x5793, 0x0000 }, + { 0x5794, 0x0000 }, + { 0x5795, 0x0000 }, + { 0x5796, 0x0000 }, + { 0x5797, 0x0000 }, + { 0x5798, 0x0000 }, + { 0x5799, 0x0000 }, + { 0x579a, 0x0000 }, + { 0x579b, 0x0000 }, + { 0x579c, 0x0000 }, + { 0x579d, 0x0000 }, + { 0x579e, 0x0000 }, + { 0x579f, 0x0000 }, + { 0x57a0, 0x0000 }, + { 0x57a1, 0x0000 }, + { 0x57a2, 0x0000 }, + { 0x57a3, 0x0000 }, + { 0x57a4, 0x0000 }, + { 0x57a5, 0x0000 }, + { 0x57a6, 0x0000 }, + { 0x57a7, 0x0000 }, + { 0x57a8, 0x0000 }, + { 0x57a9, 0x0000 }, + { 0x57aa, 0x0000 }, + { 0x57ab, 0x0000 }, + { 0x57ac, 0x0000 }, + { 0x57ad, 0x0000 }, + { 0x57ae, 0x0000 }, + { 0x57af, 0x0000 }, + { 0x57b0, 0x0000 }, + { 0x57b1, 0x0000 }, + { 0x57b2, 0x0000 }, + { 0x57b3, 0x0000 }, + { 0x57b4, 0x0000 }, + { 0x57b5, 0x0000 }, + { 0x57b6, 0x0000 }, + { 0x57b7, 0x0000 }, + { 0x57b8, 0x0000 }, + { 0x57b9, 0x0000 }, + { 0x57ba, 0x0000 }, + { 0x57bb, 0x0000 }, + { 0x57bc, 0x0000 }, + { 0x57bd, 0x0000 }, + { 0x57be, 0x0000 }, + { 0x57bf, 0x0000 }, + { 0x57c0, 0x0000 }, + { 0x57c1, 0x0000 }, + { 0x57c2, 0x0000 }, + { 0x57c3, 0x0000 }, + { 0x57c4, 0x0000 }, + { 0x57c5, 0x0000 }, + { 0x57c6, 0x0000 }, + { 0x57c7, 0x0000 }, + { 0x57c8, 0x0000 }, + { 0x57c9, 0x0000 }, + { 0x57ca, 0x0000 }, + { 0x57cb, 0x0000 }, + { 0x57cc, 0x0000 }, + { 0x57cd, 0x0000 }, + { 0x57ce, 0x0000 }, + { 0x57cf, 0x0000 }, + { 0x57d0, 0x0000 }, + { 0x57d1, 0x0000 }, + { 0x57d2, 0x0000 }, + { 0x57d3, 0x0000 }, + { 0x57d4, 0x0000 }, + { 0x57d5, 0x0000 }, + { 0x57d6, 0x0000 }, + { 0x57d7, 0x0000 }, + { 0x57d8, 0x0000 }, + { 0x57d9, 0x0000 }, + { 0x57da, 0x0000 }, + { 0x57db, 0x0000 }, + { 0x57dc, 0x0000 }, + { 0x57dd, 0x0000 }, + { 0x57de, 0x0000 }, + { 0x57df, 0x0000 }, + { 0x57e0, 0x0000 }, + { 0x57e1, 0x0000 }, + { 0x57e2, 0x0000 }, + { 0x57e3, 0x0000 }, + { 0x57e4, 0x0000 }, + { 0x57e5, 0x0000 }, + { 0x57e6, 0x0000 }, + { 0x57e7, 0x0000 }, + { 0x57e8, 0x0000 }, + { 0x57e9, 0x0000 }, + { 0x57ea, 0x0000 }, + { 0x57eb, 0x0000 }, + { 0x57ec, 0x0000 }, + { 0x57ed, 0x0000 }, + { 0x57ee, 0x0000 }, + { 0x57ef, 0x0000 }, + { 0x57f0, 0x0000 }, + { 0x57f1, 0x0000 }, + { 0x57f2, 0x0000 }, + { 0x57f3, 0x0000 }, + { 0x57f4, 0x0000 }, + { 0x57f5, 0x0000 }, + { 0x57f6, 0x0000 }, + { 0x57f7, 0x0000 }, + { 0x57f8, 0x0000 }, + { 0x57f9, 0x0000 }, + { 0x57fa, 0x0000 }, + { 0x57fb, 0x0000 }, + { 0x57fc, 0x0000 }, + { 0x57fd, 0x0000 }, + { 0x57fe, 0x0000 }, + { 0x57ff, 0x0000 }, + { 0x5800, 0x0000 }, + { 0x5801, 0x0000 }, + { 0x5802, 0x0000 }, + { 0x5803, 0x0000 }, + { 0x5804, 0x0000 }, + { 0x5805, 0x0000 }, + { 0x5806, 0x0000 }, + { 0x5807, 0x0000 }, + { 0x5808, 0x0000 }, + { 0x5809, 0x0000 }, + { 0x580a, 0x0000 }, + { 0x580b, 0x0000 }, + { 0x580c, 0x0000 }, + { 0x580d, 0x0000 }, + { 0x580e, 0x0000 }, + { 0x580f, 0x0000 }, + { 0x5810, 0x0000 }, + { 0x5811, 0x0000 }, + { 0x5812, 0x0000 }, + { 0x5813, 0x0000 }, + { 0x5814, 0x0000 }, + { 0x5815, 0x0000 }, + { 0x5816, 0x0000 }, + { 0x5817, 0x0000 }, + { 0x5818, 0x0000 }, + { 0x5819, 0x0000 }, + { 0x581a, 0x0000 }, + { 0x581b, 0x0000 }, + { 0x581c, 0x0000 }, + { 0x581d, 0x0000 }, + { 0x581e, 0x0000 }, + { 0x581f, 0x0000 }, + { 0x5820, 0x0000 }, + { 0x5821, 0x0000 }, + { 0x5822, 0x0000 }, + { 0x5823, 0x0000 }, + { 0x5824, 0x0000 }, + { 0x5825, 0x0000 }, + { 0x5826, 0x0000 }, + { 0x5827, 0x0000 }, + { 0x5828, 0x0000 }, + { 0x5829, 0x0000 }, + { 0x582a, 0x0000 }, + { 0x582b, 0x0000 }, + { 0x582c, 0x0000 }, + { 0x582d, 0x0000 }, + { 0x582e, 0x0000 }, + { 0x582f, 0x0000 }, + { 0x5830, 0x0000 }, + { 0x5831, 0x0000 }, + { 0x5832, 0x0000 }, + { 0x5833, 0x0000 }, + { 0x5834, 0x0000 }, + { 0x5835, 0x0000 }, + { 0x5836, 0x0000 }, + { 0x5837, 0x0000 }, + { 0x5838, 0x0000 }, + { 0x5839, 0x0000 }, + { 0x583a, 0x0000 }, + { 0x583b, 0x0000 }, + { 0x583c, 0x0000 }, + { 0x583d, 0x0000 }, + { 0x583e, 0x0000 }, + { 0x583f, 0x0000 }, + { 0x5840, 0x0000 }, + { 0x5841, 0x0000 }, + { 0x5842, 0x0000 }, + { 0x5843, 0x0000 }, + { 0x5844, 0x0000 }, + { 0x5845, 0x0000 }, + { 0x5846, 0x0000 }, + { 0x5847, 0x0000 }, + { 0x5848, 0x0000 }, + { 0x5849, 0x0000 }, + { 0x584a, 0x0000 }, + { 0x584b, 0x0000 }, + { 0x584c, 0x0000 }, + { 0x584d, 0x0000 }, + { 0x584e, 0x0000 }, + { 0x584f, 0x0000 }, + { 0x5850, 0x0000 }, + { 0x5851, 0x0000 }, + { 0x5852, 0x0000 }, + { 0x5853, 0x0000 }, + { 0x5854, 0x0000 }, + { 0x5855, 0x0000 }, + { 0x5856, 0x0000 }, + { 0x5857, 0x0000 }, + { 0x5858, 0x0000 }, + { 0x5859, 0x0000 }, + { 0x585a, 0x0000 }, + { 0x585b, 0x0000 }, + { 0x585c, 0x0000 }, + { 0x585d, 0x0000 }, + { 0x585e, 0x0000 }, + { 0x585f, 0x0000 }, + { 0x5860, 0x0000 }, + { 0x5861, 0x0000 }, + { 0x5862, 0x0000 }, + { 0x5863, 0x0000 }, + { 0x5864, 0x0000 }, + { 0x5865, 0x0000 }, + { 0x5866, 0x0000 }, + { 0x5867, 0x0000 }, + { 0x5868, 0x0000 }, + { 0x5869, 0x0000 }, + { 0x586a, 0x0000 }, + { 0x586b, 0x0000 }, + { 0x586c, 0x0000 }, + { 0x586d, 0x0000 }, + { 0x586e, 0x0000 }, + { 0x586f, 0x0000 }, + { 0x5870, 0x0000 }, + { 0x5871, 0x0000 }, + { 0x5872, 0x0000 }, + { 0x5873, 0x0000 }, + { 0x5874, 0x0000 }, + { 0x5875, 0x0000 }, + { 0x5876, 0x0000 }, + { 0x5877, 0x0000 }, + { 0x5878, 0x0000 }, + { 0x5879, 0x0000 }, + { 0x587a, 0x0000 }, + { 0x587b, 0x0000 }, + { 0x587c, 0x0000 }, + { 0x587d, 0x0000 }, + { 0x587e, 0x0000 }, + { 0x587f, 0x0000 }, + { 0x5880, 0x0000 }, + { 0x5881, 0x0000 }, + { 0x5882, 0x0000 }, + { 0x5883, 0x0000 }, + { 0x5884, 0x0000 }, + { 0x5885, 0x0000 }, + { 0x5886, 0x0000 }, + { 0x5887, 0x0000 }, + { 0x5888, 0x0000 }, + { 0x5889, 0x0000 }, + { 0x588a, 0x0000 }, + { 0x588b, 0x0000 }, + { 0x588c, 0x0000 }, + { 0x588d, 0x0000 }, + { 0x588e, 0x0000 }, + { 0x588f, 0x0000 }, + { 0x5890, 0x0000 }, + { 0x5891, 0x0000 }, + { 0x5892, 0x0000 }, + { 0x5893, 0x0000 }, + { 0x5894, 0x0000 }, + { 0x5895, 0x0000 }, + { 0x5896, 0x0000 }, + { 0x5897, 0x0000 }, + { 0x5898, 0x0000 }, + { 0x5899, 0x0000 }, + { 0x589a, 0x0000 }, + { 0x589b, 0x0000 }, + { 0x589c, 0x0000 }, + { 0x589d, 0x0000 }, + { 0x589e, 0x0000 }, + { 0x589f, 0x0000 }, + { 0x58a0, 0x0000 }, + { 0x58a1, 0x0000 }, + { 0x58a2, 0x0000 }, + { 0x58a3, 0x0000 }, + { 0x58a4, 0x0000 }, + { 0x58a5, 0x0000 }, + { 0x58a6, 0x0000 }, + { 0x58a7, 0x0000 }, + { 0x58a8, 0x0000 }, + { 0x58a9, 0x0000 }, + { 0x58aa, 0x0000 }, + { 0x58ab, 0x0000 }, + { 0x58ac, 0x0000 }, + { 0x58ad, 0x0000 }, + { 0x58ae, 0x0000 }, + { 0x58af, 0x0000 }, + { 0x58b0, 0x0000 }, + { 0x58b1, 0x0000 }, + { 0x58b2, 0x0000 }, + { 0x58b3, 0x0000 }, + { 0x58b4, 0x0000 }, + { 0x58b5, 0x0000 }, + { 0x58b6, 0x0000 }, + { 0x58b7, 0x0000 }, + { 0x58b8, 0x0000 }, + { 0x58b9, 0x0000 }, + { 0x58ba, 0x0000 }, + { 0x58bb, 0x0000 }, + { 0x58bc, 0x0000 }, + { 0x58bd, 0x0000 }, + { 0x58be, 0x0000 }, + { 0x58bf, 0x0000 }, + { 0x58c0, 0x0000 }, + { 0x58c1, 0x0000 }, + { 0x58c2, 0x0000 }, + { 0x58c3, 0x0000 }, + { 0x58c4, 0x0000 }, + { 0x58c5, 0x0000 }, + { 0x58c6, 0x0000 }, + { 0x58c7, 0x0000 }, + { 0x58c8, 0x0000 }, + { 0x58c9, 0x0000 }, + { 0x58ca, 0x0000 }, + { 0x58cb, 0x0000 }, + { 0x58cc, 0x0000 }, + { 0x58cd, 0x0000 }, + { 0x58ce, 0x0000 }, + { 0x58cf, 0x0000 }, + { 0x58d0, 0x0000 }, + { 0x58d1, 0x0000 }, + { 0x58d2, 0x0000 }, + { 0x58d3, 0x0000 }, + { 0x58d4, 0x0000 }, + { 0x58d5, 0x0000 }, + { 0x58d6, 0x0000 }, + { 0x58d7, 0x0000 }, + { 0x58d8, 0x0000 }, + { 0x58d9, 0x0000 }, + { 0x58da, 0x0000 }, + { 0x58db, 0x0000 }, + { 0x58dc, 0x0000 }, + { 0x58dd, 0x0000 }, + { 0x58de, 0x0000 }, + { 0x58df, 0x0000 }, + { 0x58e0, 0x0000 }, + { 0x58e1, 0x0000 }, + { 0x58e2, 0x0000 }, + { 0x58e3, 0x0000 }, + { 0x58e4, 0x0000 }, + { 0x58e5, 0x0000 }, + { 0x58e6, 0x0000 }, + { 0x58e7, 0x0000 }, + { 0x58e8, 0x0000 }, + { 0x58e9, 0x0000 }, + { 0x58ea, 0x0000 }, + { 0x58eb, 0x0000 }, + { 0x58ec, 0x0000 }, + { 0x58ed, 0x0000 }, + { 0x58ee, 0x0000 }, + { 0x58ef, 0x0000 }, + { 0x58f0, 0x0000 }, + { 0x58f1, 0x0000 }, + { 0x58f2, 0x0000 }, + { 0x58f3, 0x0000 }, + { 0x58f4, 0x0000 }, + { 0x58f5, 0x0000 }, + { 0x58f6, 0x0000 }, + { 0x58f7, 0x0000 }, + { 0x58f8, 0x0000 }, + { 0x58f9, 0x0000 }, + { 0x58fa, 0x0000 }, + { 0x58fb, 0x0000 }, + { 0x58fc, 0x0000 }, + { 0x58fd, 0x0000 }, + { 0x58fe, 0x0000 }, + { 0x58ff, 0x0000 }, + { 0x5900, 0x0000 }, + { 0x5901, 0x0000 }, + { 0x5902, 0x0000 }, + { 0x5903, 0x0000 }, + { 0x5904, 0x0000 }, + { 0x5905, 0x0000 }, + { 0x5906, 0x0000 }, + { 0x5907, 0x0000 }, + { 0x5908, 0x0000 }, + { 0x5909, 0x0000 }, + { 0x590a, 0x0000 }, + { 0x590b, 0x0000 }, + { 0x590c, 0x0000 }, + { 0x590d, 0x0000 }, + { 0x590e, 0x0000 }, + { 0x590f, 0x0000 }, + { 0x5910, 0x0000 }, + { 0x5911, 0x0000 }, + { 0x5912, 0x0000 }, + { 0x5913, 0x0000 }, + { 0x5914, 0x0000 }, + { 0x5915, 0x0000 }, + { 0x5916, 0x0000 }, + { 0x5917, 0x0000 }, + { 0x5918, 0x0000 }, + { 0x5919, 0x0000 }, + { 0x591a, 0x0000 }, + { 0x591b, 0x0000 }, + { 0x591c, 0x0000 }, + { 0x591d, 0x0000 }, + { 0x591e, 0x0000 }, + { 0x591f, 0x0000 }, + { 0x5920, 0x0000 }, + { 0x5921, 0x0000 }, + { 0x5922, 0x0000 }, + { 0x5923, 0x0000 }, + { 0x5924, 0x0000 }, + { 0x5925, 0x0000 }, + { 0x5926, 0x0000 }, + { 0x5927, 0x0000 }, + { 0x5928, 0x0000 }, + { 0x5929, 0x0000 }, + { 0x592a, 0x0000 }, + { 0x592b, 0x0000 }, + { 0x592c, 0x0000 }, + { 0x592d, 0x0000 }, + { 0x592e, 0x0000 }, + { 0x592f, 0x0000 }, + { 0x5930, 0x0000 }, + { 0x5931, 0x0000 }, + { 0x5932, 0x0000 }, + { 0x5933, 0x0000 }, + { 0x5934, 0x0000 }, + { 0x5935, 0x0000 }, + { 0x5936, 0x0000 }, + { 0x5937, 0x0000 }, + { 0x5938, 0x0000 }, + { 0x5939, 0x0000 }, + { 0x593a, 0x0000 }, + { 0x593b, 0x0000 }, + { 0x593c, 0x0000 }, + { 0x593d, 0x0000 }, + { 0x593e, 0x0000 }, + { 0x593f, 0x0000 }, + { 0x5940, 0x0000 }, + { 0x5941, 0x0000 }, + { 0x5942, 0x0000 }, + { 0x5943, 0x0000 }, + { 0x5944, 0x0000 }, + { 0x5945, 0x0000 }, + { 0x5946, 0x0000 }, + { 0x5947, 0x0000 }, + { 0x5948, 0x0000 }, + { 0x5949, 0x0000 }, + { 0x594a, 0x0000 }, + { 0x594b, 0x0000 }, + { 0x594c, 0x0000 }, + { 0x594d, 0x0000 }, + { 0x594e, 0x0000 }, + { 0x594f, 0x0000 }, + { 0x5950, 0x0000 }, + { 0x5951, 0x0000 }, + { 0x5952, 0x0000 }, + { 0x5953, 0x0000 }, + { 0x5954, 0x0000 }, + { 0x5955, 0x0000 }, + { 0x5956, 0x0000 }, + { 0x5957, 0x0000 }, + { 0x5958, 0x0000 }, + { 0x5959, 0x0000 }, + { 0x595a, 0x0000 }, + { 0x595b, 0x0000 }, + { 0x595c, 0x0000 }, + { 0x595d, 0x0000 }, + { 0x595e, 0x0000 }, + { 0x595f, 0x0000 }, + { 0x5960, 0x0000 }, + { 0x5961, 0x0000 }, + { 0x5962, 0x0000 }, + { 0x5963, 0x0000 }, + { 0x5964, 0x0000 }, + { 0x5965, 0x0000 }, + { 0x5966, 0x0000 }, + { 0x5967, 0x0000 }, + { 0x5968, 0x0000 }, + { 0x5969, 0x0000 }, + { 0x596a, 0x0000 }, + { 0x596b, 0x0000 }, + { 0x596c, 0x0000 }, + { 0x596d, 0x0000 }, + { 0x596e, 0x0000 }, + { 0x596f, 0x0000 }, + { 0x5970, 0x0000 }, + { 0x5971, 0x0000 }, + { 0x5972, 0x0000 }, + { 0x5973, 0x0000 }, + { 0x5974, 0x0000 }, + { 0x5975, 0x0000 }, + { 0x5976, 0x0000 }, + { 0x5977, 0x0000 }, + { 0x5978, 0x0000 }, + { 0x5979, 0x0000 }, + { 0x597a, 0x0000 }, + { 0x597b, 0x0000 }, + { 0x597c, 0x0000 }, + { 0x597d, 0x0000 }, + { 0x597e, 0x0000 }, + { 0x597f, 0x0000 }, + { 0x5980, 0x0000 }, + { 0x5981, 0x0000 }, + { 0x5982, 0x0000 }, + { 0x5983, 0x0000 }, + { 0x5984, 0x0000 }, + { 0x5985, 0x0000 }, + { 0x5986, 0x0000 }, + { 0x5987, 0x0000 }, + { 0x5988, 0x0000 }, + { 0x5989, 0x0000 }, + { 0x598a, 0x0000 }, + { 0x598b, 0x0000 }, + { 0x598c, 0x0000 }, + { 0x598d, 0x0000 }, + { 0x598e, 0x0000 }, + { 0x598f, 0x0000 }, + { 0x5990, 0x0000 }, + { 0x5991, 0x0000 }, + { 0x5992, 0x0000 }, + { 0x5993, 0x0000 }, + { 0x5994, 0x0000 }, + { 0x5995, 0x0000 }, + { 0x5996, 0x0000 }, + { 0x5997, 0x0000 }, + { 0x5998, 0x0000 }, + { 0x5999, 0x0000 }, + { 0x599a, 0x0000 }, + { 0x599b, 0x0000 }, + { 0x599c, 0x0000 }, + { 0x599d, 0x0000 }, + { 0x599e, 0x0000 }, + { 0x599f, 0x0000 }, + { 0x59a0, 0x0000 }, + { 0x59a1, 0x0000 }, + { 0x59a2, 0x0000 }, + { 0x59a3, 0x0000 }, + { 0x59a4, 0x0000 }, + { 0x59a5, 0x0000 }, + { 0x59a6, 0x0000 }, + { 0x59a7, 0x0000 }, + { 0x59a8, 0x0000 }, + { 0x59a9, 0x0000 }, + { 0x59aa, 0x0000 }, + { 0x59ab, 0x0000 }, + { 0x59ac, 0x0000 }, + { 0x59ad, 0x0000 }, + { 0x59ae, 0x0000 }, + { 0x59af, 0x0000 }, + { 0x59b0, 0x0000 }, + { 0x59b1, 0x0000 }, + { 0x59b2, 0x0000 }, + { 0x59b3, 0x0000 }, + { 0x59b4, 0x0000 }, + { 0x59b5, 0x0000 }, + { 0x59b6, 0x0000 }, + { 0x59b7, 0x0000 }, + { 0x59b8, 0x0000 }, + { 0x59b9, 0x0000 }, + { 0x59ba, 0x0000 }, + { 0x59bb, 0x0000 }, + { 0x59bc, 0x0000 }, + { 0x59bd, 0x0000 }, + { 0x59be, 0x0000 }, + { 0x59bf, 0x0000 }, + { 0x59c0, 0x0000 }, + { 0x59c1, 0x0000 }, + { 0x59c2, 0x0000 }, + { 0x59c3, 0x0000 }, + { 0x59c4, 0x0000 }, + { 0x59c5, 0x0000 }, + { 0x59c6, 0x0000 }, + { 0x59c7, 0x0000 }, + { 0x59c8, 0x0000 }, + { 0x59c9, 0x0000 }, + { 0x59ca, 0x0000 }, + { 0x59cb, 0x0000 }, + { 0x59cc, 0x0000 }, + { 0x59cd, 0x0000 }, + { 0x59ce, 0x0000 }, + { 0x59cf, 0x0000 }, + { 0x59d0, 0x0000 }, + { 0x59d1, 0x0000 }, + { 0x59d2, 0x0000 }, + { 0x59d3, 0x0000 }, + { 0x59d4, 0x0000 }, + { 0x59d5, 0x0000 }, + { 0x59d6, 0x0000 }, + { 0x59d7, 0x0000 }, + { 0x59d8, 0x0000 }, + { 0x59d9, 0x0000 }, + { 0x59da, 0x0000 }, + { 0x59db, 0x0000 }, + { 0x59dc, 0x0000 }, + { 0x59dd, 0x0000 }, + { 0x59de, 0x0000 }, + { 0x59df, 0x0000 }, + { 0x59e0, 0x0000 }, + { 0x59e1, 0x0000 }, + { 0x59e2, 0x0000 }, + { 0x59e3, 0x0000 }, + { 0x59e4, 0x0000 }, + { 0x59e5, 0x0000 }, + { 0x59e6, 0x0000 }, + { 0x59e7, 0x0000 }, + { 0x59e8, 0x0000 }, + { 0x59e9, 0x0000 }, + { 0x59ea, 0x0000 }, + { 0x59eb, 0x0000 }, + { 0x59ec, 0x0000 }, + { 0x59ed, 0x0000 }, + { 0x59ee, 0x0000 }, + { 0x59ef, 0x0000 }, + { 0x59f0, 0x0000 }, + { 0x59f1, 0x0000 }, + { 0x59f2, 0x0000 }, + { 0x59f3, 0x0000 }, + { 0x59f4, 0x0000 }, + { 0x59f5, 0x0000 }, + { 0x59f6, 0x0000 }, + { 0x59f7, 0x0000 }, + { 0x59f8, 0x0000 }, + { 0x59f9, 0x0000 }, + { 0x59fa, 0x0000 }, + { 0x59fb, 0x0000 }, + { 0x59fc, 0x0000 }, + { 0x59fd, 0x0000 }, + { 0x59fe, 0x0000 }, + { 0x59ff, 0x0000 }, + { 0x5a00, 0x0000 }, + { 0x5a01, 0x0000 }, + { 0x5a02, 0x0000 }, + { 0x5a03, 0x0000 }, + { 0x5a04, 0x0000 }, + { 0x5a05, 0x0000 }, + { 0x5a06, 0x0000 }, + { 0x5a07, 0x0000 }, + { 0x5a08, 0x0000 }, + { 0x5a09, 0x0000 }, + { 0x5a0a, 0x0000 }, + { 0x5a0b, 0x0000 }, + { 0x5a0c, 0x0000 }, + { 0x5a0d, 0x0000 }, + { 0x5a0e, 0x0000 }, + { 0x5a0f, 0x0000 }, + { 0x5a10, 0x0000 }, + { 0x5a11, 0x0000 }, + { 0x5a12, 0x0000 }, + { 0x5a13, 0x0000 }, + { 0x5a14, 0x0000 }, + { 0x5a15, 0x0000 }, + { 0x5a16, 0x0000 }, + { 0x5a17, 0x0000 }, + { 0x5a18, 0x0000 }, + { 0x5a19, 0x0000 }, + { 0x5a1a, 0x0000 }, + { 0x5a1b, 0x0000 }, + { 0x5a1c, 0x0000 }, + { 0x5a1d, 0x0000 }, + { 0x5a1e, 0x0000 }, + { 0x5a1f, 0x0000 }, + { 0x5a20, 0x0000 }, + { 0x5a21, 0x0000 }, + { 0x5a22, 0x0000 }, + { 0x5a23, 0x0000 }, + { 0x5a24, 0x0000 }, + { 0x5a25, 0x0000 }, + { 0x5a26, 0x0000 }, + { 0x5a27, 0x0000 }, + { 0x5a28, 0x0000 }, + { 0x5a29, 0x0000 }, + { 0x5a2a, 0x0000 }, + { 0x5a2b, 0x0000 }, + { 0x5a2c, 0x0000 }, + { 0x5a2d, 0x0000 }, + { 0x5a2e, 0x0000 }, + { 0x5a2f, 0x0000 }, + { 0x5a30, 0x0000 }, + { 0x5a31, 0x0000 }, + { 0x5a32, 0x0000 }, + { 0x5a33, 0x0000 }, + { 0x5a34, 0x0000 }, + { 0x5a35, 0x0000 }, + { 0x5a36, 0x0000 }, + { 0x5a37, 0x0000 }, + { 0x5a38, 0x0000 }, + { 0x5a39, 0x0000 }, + { 0x5a3a, 0x0000 }, + { 0x5a3b, 0x0000 }, + { 0x5a3c, 0x0000 }, + { 0x5a3d, 0x0000 }, + { 0x5a3e, 0x0000 }, + { 0x5a3f, 0x0000 }, + { 0x5a40, 0x0000 }, + { 0x5a41, 0x0000 }, + { 0x5a42, 0x0000 }, + { 0x5a43, 0x0000 }, + { 0x5a44, 0x0000 }, + { 0x5a45, 0x0000 }, + { 0x5a46, 0x0000 }, + { 0x5a47, 0x0000 }, + { 0x5a48, 0x0000 }, + { 0x5a49, 0x0000 }, + { 0x5a4a, 0x0000 }, + { 0x5a4b, 0x0000 }, + { 0x5a4c, 0x0000 }, + { 0x5a4d, 0x0000 }, + { 0x5a4e, 0x0000 }, + { 0x5a4f, 0x0000 }, + { 0x5a50, 0x0000 }, + { 0x5a51, 0x0000 }, + { 0x5a52, 0x0000 }, + { 0x5a53, 0x0000 }, + { 0x5a54, 0x0000 }, + { 0x5a55, 0x0000 }, + { 0x5a56, 0x0000 }, + { 0x5a57, 0x0000 }, + { 0x5a58, 0x0000 }, + { 0x5a59, 0x0000 }, + { 0x5a5a, 0x0000 }, + { 0x5a5b, 0x0000 }, + { 0x5a5c, 0x0000 }, + { 0x5a5d, 0x0000 }, + { 0x5a5e, 0x0000 }, + { 0x5a5f, 0x0000 }, + { 0x5a60, 0x0000 }, + { 0x5a61, 0x0000 }, + { 0x5a62, 0x0000 }, + { 0x5a63, 0x0000 }, + { 0x5a64, 0x0000 }, + { 0x5a65, 0x0000 }, + { 0x5a66, 0x0000 }, + { 0x5a67, 0x0000 }, + { 0x5a68, 0x0000 }, + { 0x5a69, 0x0000 }, + { 0x5a6a, 0x0000 }, + { 0x5a6b, 0x0000 }, + { 0x5a6c, 0x0000 }, + { 0x5a6d, 0x0000 }, + { 0x5a6e, 0x0000 }, + { 0x5a6f, 0x0000 }, + { 0x5a70, 0x0000 }, + { 0x5a71, 0x0000 }, + { 0x5a72, 0x0000 }, + { 0x5a73, 0x0000 }, + { 0x5a74, 0x0000 }, + { 0x5a75, 0x0000 }, + { 0x5a76, 0x0000 }, + { 0x5a77, 0x0000 }, + { 0x5a78, 0x0000 }, + { 0x5a79, 0x0000 }, + { 0x5a7a, 0x0000 }, + { 0x5a7b, 0x0000 }, + { 0x5a7c, 0x0000 }, + { 0x5a7d, 0x0000 }, + { 0x5a7e, 0x0000 }, + { 0x5a7f, 0x0000 }, + { 0x5a80, 0x0000 }, + { 0x5a81, 0x0000 }, + { 0x5a82, 0x0000 }, + { 0x5a83, 0x0000 }, + { 0x5a84, 0x0000 }, + { 0x5a85, 0x0000 }, + { 0x5a86, 0x0000 }, + { 0x5a87, 0x0000 }, + { 0x5a88, 0x0000 }, + { 0x5a89, 0x0000 }, + { 0x5a8a, 0x0000 }, + { 0x5a8b, 0x0000 }, + { 0x5a8c, 0x0000 }, + { 0x5a8d, 0x0000 }, + { 0x5a8e, 0x0000 }, + { 0x5a8f, 0x0000 }, + { 0x5a90, 0x0000 }, + { 0x5a91, 0x0000 }, + { 0x5a92, 0x0000 }, + { 0x5a93, 0x0000 }, + { 0x5a94, 0x0000 }, + { 0x5a95, 0x0000 }, + { 0x5a96, 0x0000 }, + { 0x5a97, 0x0000 }, + { 0x5a98, 0x0000 }, + { 0x5a99, 0x0000 }, + { 0x5a9a, 0x0000 }, + { 0x5a9b, 0x0000 }, + { 0x5a9c, 0x0000 }, + { 0x5a9d, 0x0000 }, + { 0x5a9e, 0x0000 }, + { 0x5a9f, 0x0000 }, + { 0x5aa0, 0x0000 }, + { 0x5aa1, 0x0000 }, + { 0x5aa2, 0x0000 }, + { 0x5aa3, 0x0000 }, + { 0x5aa4, 0x0000 }, + { 0x5aa5, 0x0000 }, + { 0x5aa6, 0x0000 }, + { 0x5aa7, 0x0000 }, + { 0x5aa8, 0x0000 }, + { 0x5aa9, 0x0000 }, + { 0x5aaa, 0x0000 }, + { 0x5aab, 0x0000 }, + { 0x5aac, 0x0000 }, + { 0x5aad, 0x0000 }, + { 0x5aae, 0x0000 }, + { 0x5aaf, 0x0000 }, + { 0x5ab0, 0x0000 }, + { 0x5ab1, 0x0000 }, + { 0x5ab2, 0x0000 }, + { 0x5ab3, 0x0000 }, + { 0x5ab4, 0x0000 }, + { 0x5ab5, 0x0000 }, + { 0x5ab6, 0x0000 }, + { 0x5ab7, 0x0000 }, + { 0x5ab8, 0x0000 }, + { 0x5ab9, 0x0000 }, + { 0x5aba, 0x0000 }, + { 0x5abb, 0x0000 }, + { 0x5abc, 0x0000 }, + { 0x5abd, 0x0000 }, + { 0x5abe, 0x0000 }, + { 0x5abf, 0x0000 }, + { 0x5ac0, 0x0000 }, + { 0x5ac1, 0x0000 }, + { 0x5ac2, 0x0000 }, + { 0x5ac3, 0x0000 }, + { 0x5ac4, 0x0000 }, + { 0x5ac5, 0x0000 }, + { 0x5ac6, 0x0000 }, + { 0x5ac7, 0x0000 }, + { 0x5ac8, 0x0000 }, + { 0x5ac9, 0x0000 }, + { 0x5aca, 0x0000 }, + { 0x5acb, 0x0000 }, + { 0x5acc, 0x0000 }, + { 0x5acd, 0x0000 }, + { 0x5ace, 0x0000 }, + { 0x5acf, 0x0000 }, + { 0x5ad0, 0x0000 }, + { 0x5ad1, 0x0000 }, + { 0x5ad2, 0x0000 }, + { 0x5ad3, 0x0000 }, + { 0x5ad4, 0x0000 }, + { 0x5ad5, 0x0000 }, + { 0x5ad6, 0x0000 }, + { 0x5ad7, 0x0000 }, + { 0x5ad8, 0x0000 }, + { 0x5ad9, 0x0000 }, + { 0x5ada, 0x0000 }, + { 0x5adb, 0x0000 }, + { 0x5adc, 0x0000 }, + { 0x5add, 0x0000 }, + { 0x5ade, 0x0000 }, + { 0x5adf, 0x0000 }, + { 0x5ae0, 0x0000 }, + { 0x5ae1, 0x0000 }, + { 0x5ae2, 0x0000 }, + { 0x5ae3, 0x0000 }, + { 0x5ae4, 0x0000 }, + { 0x5ae5, 0x0000 }, + { 0x5ae6, 0x0000 }, + { 0x5ae7, 0x0000 }, + { 0x5ae8, 0x0000 }, + { 0x5ae9, 0x0000 }, + { 0x5aea, 0x0000 }, + { 0x5aeb, 0x0000 }, + { 0x5aec, 0x0000 }, + { 0x5aed, 0x0000 }, + { 0x5aee, 0x0000 }, + { 0x5aef, 0x0000 }, + { 0x5af0, 0x0000 }, + { 0x5af1, 0x0000 }, + { 0x5af2, 0x0000 }, + { 0x5af3, 0x0000 }, + { 0x5af4, 0x0000 }, + { 0x5af5, 0x0000 }, + { 0x5af6, 0x0000 }, + { 0x5af7, 0x0000 }, + { 0x5af8, 0x0000 }, + { 0x5af9, 0x0000 }, + { 0x5afa, 0x0000 }, + { 0x5afb, 0x0000 }, + { 0x5afc, 0x0000 }, + { 0x5afd, 0x0000 }, + { 0x5afe, 0x0000 }, + { 0x5aff, 0x0000 }, + { 0x5b00, 0x0000 }, + { 0x5b01, 0x0000 }, + { 0x5b02, 0x0000 }, + { 0x5b03, 0x0000 }, + { 0x5b04, 0x0000 }, + { 0x5b05, 0x0000 }, + { 0x5b06, 0x0000 }, + { 0x5b07, 0x0000 }, + { 0x5b08, 0x0000 }, + { 0x5b09, 0x0000 }, + { 0x5b0a, 0x0000 }, + { 0x5b0b, 0x0000 }, + { 0x5b0c, 0x0000 }, + { 0x5b0d, 0x0000 }, + { 0x5b0e, 0x0000 }, + { 0x5b0f, 0x0000 }, + { 0x5b10, 0x0000 }, + { 0x5b11, 0x0000 }, + { 0x5b12, 0x0000 }, + { 0x5b13, 0x0000 }, + { 0x5b14, 0x0000 }, + { 0x5b15, 0x0000 }, + { 0x5b16, 0x0000 }, + { 0x5b17, 0x0000 }, + { 0x5b18, 0x0000 }, + { 0x5b19, 0x0000 }, + { 0x5b1a, 0x0000 }, + { 0x5b1b, 0x0000 }, + { 0x5b1c, 0x0000 }, + { 0x5b1d, 0x0000 }, + { 0x5b1e, 0x0000 }, + { 0x5b1f, 0x0000 }, + { 0x5b20, 0x0000 }, + { 0x5b21, 0x0000 }, + { 0x5b22, 0x0000 }, + { 0x5b23, 0x0000 }, + { 0x5b24, 0x0000 }, + { 0x5b25, 0x0000 }, + { 0x5b26, 0x0000 }, + { 0x5b27, 0x0000 }, + { 0x5b28, 0x0000 }, + { 0x5b29, 0x0000 }, + { 0x5b2a, 0x0000 }, + { 0x5b2b, 0x0000 }, + { 0x5b2c, 0x0000 }, + { 0x5b2d, 0x0000 }, + { 0x5b2e, 0x0000 }, + { 0x5b2f, 0x0000 }, + { 0x5b30, 0x0000 }, + { 0x5b31, 0x0000 }, + { 0x5b32, 0x0000 }, + { 0x5b33, 0x0000 }, + { 0x5b34, 0x0000 }, + { 0x5b35, 0x0000 }, + { 0x5b36, 0x0000 }, + { 0x5b37, 0x0000 }, + { 0x5b38, 0x0000 }, + { 0x5b39, 0x0000 }, + { 0x5b3a, 0x0000 }, + { 0x5b3b, 0x0000 }, + { 0x5b3c, 0x0000 }, + { 0x5b3d, 0x0000 }, + { 0x5b3e, 0x0000 }, + { 0x5b3f, 0x0000 }, + { 0x5b40, 0x0000 }, + { 0x5b41, 0x0000 }, + { 0x5b42, 0x0000 }, + { 0x5b43, 0x0000 }, + { 0x5b44, 0x0000 }, + { 0x5b45, 0x0000 }, + { 0x5b46, 0x0000 }, + { 0x5b47, 0x0000 }, + { 0x5b48, 0x0000 }, + { 0x5b49, 0x0000 }, + { 0x5b4a, 0x0000 }, + { 0x5b4b, 0x0000 }, + { 0x5b4c, 0x0000 }, + { 0x5b4d, 0x0000 }, + { 0x5b4e, 0x0000 }, + { 0x5b4f, 0x0000 }, + { 0x5b50, 0x0000 }, + { 0x5b51, 0x0000 }, + { 0x5b52, 0x0000 }, + { 0x5b53, 0x0000 }, + { 0x5b54, 0x0000 }, + { 0x5b55, 0x0000 }, + { 0x5b56, 0x0000 }, + { 0x5b57, 0x0000 }, + { 0x5b58, 0x0000 }, + { 0x5b59, 0x0000 }, + { 0x5b5a, 0x0000 }, + { 0x5b5b, 0x0000 }, + { 0x5b5c, 0x0000 }, + { 0x5b5d, 0x0000 }, + { 0x5b5e, 0x0000 }, + { 0x5b5f, 0x0000 }, + { 0x5b60, 0x0000 }, + { 0x5b61, 0x0000 }, + { 0x5b62, 0x0000 }, + { 0x5b63, 0x0000 }, + { 0x5b64, 0x0000 }, + { 0x5b65, 0x0000 }, + { 0x5b66, 0x0000 }, + { 0x5b67, 0x0000 }, + { 0x5b68, 0x0000 }, + { 0x5b69, 0x0000 }, + { 0x5b6a, 0x0000 }, + { 0x5b6b, 0x0000 }, + { 0x5b6c, 0x0000 }, + { 0x5b6d, 0x0000 }, + { 0x5b6e, 0x0000 }, + { 0x5b6f, 0x0000 }, + { 0x5b70, 0x0000 }, + { 0x5b71, 0x0000 }, + { 0x5b72, 0x0000 }, + { 0x5b73, 0x0000 }, + { 0x5b74, 0x0000 }, + { 0x5b75, 0x0000 }, + { 0x5b76, 0x0000 }, + { 0x5b77, 0x0000 }, + { 0x5b78, 0x0000 }, + { 0x5b79, 0x0000 }, + { 0x5b7a, 0x0000 }, + { 0x5b7b, 0x0000 }, + { 0x5b7c, 0x0000 }, + { 0x5b7d, 0x0000 }, + { 0x5b7e, 0x0000 }, + { 0x5b7f, 0x0000 }, + { 0x5b80, 0x0000 }, + { 0x5b81, 0x0000 }, + { 0x5b82, 0x0000 }, + { 0x5b83, 0x0000 }, + { 0x5b84, 0x0000 }, + { 0x5b85, 0x0000 }, + { 0x5b86, 0x0000 }, + { 0x5b87, 0x0000 }, + { 0x5b88, 0x0000 }, + { 0x5b89, 0x0000 }, + { 0x5b8a, 0x0000 }, + { 0x5b8b, 0x0000 }, + { 0x5b8c, 0x0000 }, + { 0x5b8d, 0x0000 }, + { 0x5b8e, 0x0000 }, + { 0x5b8f, 0x0000 }, + { 0x5b90, 0x0000 }, + { 0x5b91, 0x0000 }, + { 0x5b92, 0x0000 }, + { 0x5b93, 0x0000 }, + { 0x5b94, 0x0000 }, + { 0x5b95, 0x0000 }, + { 0x5b96, 0x0000 }, + { 0x5b97, 0x0000 }, + { 0x5b98, 0x0000 }, + { 0x5b99, 0x0000 }, + { 0x5b9a, 0x0000 }, + { 0x5b9b, 0x0000 }, + { 0x5b9c, 0x0000 }, + { 0x5b9d, 0x0000 }, + { 0x5b9e, 0x0000 }, + { 0x5b9f, 0x0000 }, + { 0x5ba0, 0x0000 }, + { 0x5ba1, 0x0000 }, + { 0x5ba2, 0x0000 }, + { 0x5ba3, 0x0000 }, + { 0x5ba4, 0x0000 }, + { 0x5ba5, 0x0000 }, + { 0x5ba6, 0x0000 }, + { 0x5ba7, 0x0000 }, + { 0x5ba8, 0x0000 }, + { 0x5ba9, 0x0000 }, + { 0x5baa, 0x0000 }, + { 0x5bab, 0x0000 }, + { 0x5bac, 0x0000 }, + { 0x5bad, 0x0000 }, + { 0x5bae, 0x0000 }, + { 0x5baf, 0x0000 }, + { 0x5bb0, 0x0000 }, + { 0x5bb1, 0x0000 }, + { 0x5bb2, 0x0000 }, + { 0x5bb3, 0x0000 }, + { 0x5bb4, 0x0000 }, + { 0x5bb5, 0x0000 }, + { 0x5bb6, 0x0000 }, + { 0x5bb7, 0x0000 }, + { 0x5bb8, 0x0000 }, + { 0x5bb9, 0x0000 }, + { 0x5bba, 0x0000 }, + { 0x5bbb, 0x0000 }, + { 0x5bbc, 0x0000 }, + { 0x5bbd, 0x0000 }, + { 0x5bbe, 0x0000 }, + { 0x5bbf, 0x0000 }, + { 0x5bc0, 0x0000 }, + { 0x5bc1, 0x0000 }, + { 0x5bc2, 0x0000 }, + { 0x5bc3, 0x0000 }, + { 0x5bc4, 0x0000 }, + { 0x5bc5, 0x0000 }, + { 0x5bc6, 0x0000 }, + { 0x5bc7, 0x0000 }, + { 0x5bc8, 0x0000 }, + { 0x5bc9, 0x0000 }, + { 0x5bca, 0x0000 }, + { 0x5bcb, 0x0000 }, + { 0x5bcc, 0x0000 }, + { 0x5bcd, 0x0000 }, + { 0x5bce, 0x0000 }, + { 0x5bcf, 0x0000 }, + { 0x5bd0, 0x0000 }, + { 0x5bd1, 0x0000 }, + { 0x5bd2, 0x0000 }, + { 0x5bd3, 0x0000 }, + { 0x5bd4, 0x0000 }, + { 0x5bd5, 0x0000 }, + { 0x5bd6, 0x0000 }, + { 0x5bd7, 0x0000 }, + { 0x5bd8, 0x0000 }, + { 0x5bd9, 0x0000 }, + { 0x5bda, 0x0000 }, + { 0x5bdb, 0x0000 }, + { 0x5bdc, 0x0000 }, + { 0x5bdd, 0x0000 }, + { 0x5bde, 0x0000 }, + { 0x5bdf, 0x0000 }, + { 0x5be0, 0x0000 }, + { 0x5be1, 0x0000 }, + { 0x5be2, 0x0000 }, + { 0x5be3, 0x0000 }, + { 0x5be4, 0x0000 }, + { 0x5be5, 0x0000 }, + { 0x5be6, 0x0000 }, + { 0x5be7, 0x0000 }, + { 0x5be8, 0x0000 }, + { 0x5be9, 0x0000 }, + { 0x5bea, 0x0000 }, + { 0x5beb, 0x0000 }, + { 0x5bec, 0x0000 }, + { 0x5bed, 0x0000 }, + { 0x5bee, 0x0000 }, + { 0x5bef, 0x0000 }, + { 0x5bf0, 0x0000 }, + { 0x5bf1, 0x0000 }, + { 0x5bf2, 0x0000 }, + { 0x5bf3, 0x0000 }, + { 0x5bf4, 0x0000 }, + { 0x5bf5, 0x0000 }, + { 0x5bf6, 0x0000 }, + { 0x5bf7, 0x0000 }, + { 0x5bf8, 0x0000 }, + { 0x5bf9, 0x0000 }, + { 0x5bfa, 0x0000 }, + { 0x5bfb, 0x0000 }, + { 0x5bfc, 0x0000 }, + { 0x5bfd, 0x0000 }, + { 0x5bfe, 0x0000 }, + { 0x5bff, 0x0000 }, + { 0x5c00, 0x0000 }, + { 0x5c01, 0x0000 }, + { 0x5c02, 0x0000 }, + { 0x5c03, 0x0000 }, + { 0x5c04, 0x0000 }, + { 0x5c05, 0x0000 }, + { 0x5c06, 0x0000 }, + { 0x5c07, 0x0000 }, + { 0x5c08, 0x0000 }, + { 0x5c09, 0x0000 }, + { 0x5c0a, 0x0000 }, + { 0x5c0b, 0x0000 }, + { 0x5c0c, 0x0000 }, + { 0x5c0d, 0x0000 }, + { 0x5c0e, 0x0000 }, + { 0x5c0f, 0x0000 }, + { 0x5c10, 0x0000 }, + { 0x5c11, 0x0000 }, + { 0x5c12, 0x0000 }, + { 0x5c13, 0x0000 }, + { 0x5c14, 0x0000 }, + { 0x5c15, 0x0000 }, + { 0x5c16, 0x0000 }, + { 0x5c17, 0x0000 }, + { 0x5c18, 0x0000 }, + { 0x5c19, 0x0000 }, + { 0x5c1a, 0x0000 }, + { 0x5c1b, 0x0000 }, + { 0x5c1c, 0x0000 }, + { 0x5c1d, 0x0000 }, + { 0x5c1e, 0x0000 }, + { 0x5c1f, 0x0000 }, + { 0x5c20, 0x0000 }, + { 0x5c21, 0x0000 }, + { 0x5c22, 0x0000 }, + { 0x5c23, 0x0000 }, + { 0x5c24, 0x0000 }, + { 0x5c25, 0x0000 }, + { 0x5c26, 0x0000 }, + { 0x5c27, 0x0000 }, + { 0x5c28, 0x0000 }, + { 0x5c29, 0x0000 }, + { 0x5c2a, 0x0000 }, + { 0x5c2b, 0x0000 }, + { 0x5c2c, 0x0000 }, + { 0x5c2d, 0x0000 }, + { 0x5c2e, 0x0000 }, + { 0x5c2f, 0x0000 }, + { 0x5c30, 0x0000 }, + { 0x5c31, 0x0000 }, + { 0x5c32, 0x0000 }, + { 0x5c33, 0x0000 }, + { 0x5c34, 0x0000 }, + { 0x5c35, 0x0000 }, + { 0x5c36, 0x0000 }, + { 0x5c37, 0x0000 }, + { 0x5c38, 0x0000 }, + { 0x5c39, 0x0000 }, + { 0x5c3a, 0x0000 }, + { 0x5c3b, 0x0000 }, + { 0x5c3c, 0x0000 }, + { 0x5c3d, 0x0000 }, + { 0x5c3e, 0x0000 }, + { 0x5c3f, 0x0000 }, + { 0x5c40, 0x0000 }, + { 0x5c41, 0x0000 }, + { 0x5c42, 0x0000 }, + { 0x5c43, 0x0000 }, + { 0x5c44, 0x0000 }, + { 0x5c45, 0x0000 }, + { 0x5c46, 0x0000 }, + { 0x5c47, 0x0000 }, + { 0x5c48, 0x0000 }, + { 0x5c49, 0x0000 }, + { 0x5c4a, 0x0000 }, + { 0x5c4b, 0x0000 }, + { 0x5c4c, 0x0000 }, + { 0x5c4d, 0x0000 }, + { 0x5c4e, 0x0000 }, + { 0x5c4f, 0x0000 }, + { 0x5c50, 0x0000 }, + { 0x5c51, 0x0000 }, + { 0x5c52, 0x0000 }, + { 0x5c53, 0x0000 }, + { 0x5c54, 0x0000 }, + { 0x5c55, 0x0000 }, + { 0x5c56, 0x0000 }, + { 0x5c57, 0x0000 }, + { 0x5c58, 0x0000 }, + { 0x5c59, 0x0000 }, + { 0x5c5a, 0x0000 }, + { 0x5c5b, 0x0000 }, + { 0x5c5c, 0x0000 }, + { 0x5c5d, 0x0000 }, + { 0x5c5e, 0x0000 }, + { 0x5c5f, 0x0000 }, + { 0x5c60, 0x0000 }, + { 0x5c61, 0x0000 }, + { 0x5c62, 0x0000 }, + { 0x5c63, 0x0000 }, + { 0x5c64, 0x0000 }, + { 0x5c65, 0x0000 }, + { 0x5c66, 0x0000 }, + { 0x5c67, 0x0000 }, + { 0x5c68, 0x0000 }, + { 0x5c69, 0x0000 }, + { 0x5c6a, 0x0000 }, + { 0x5c6b, 0x0000 }, + { 0x5c6c, 0x0000 }, + { 0x5c6d, 0x0000 }, + { 0x5c6e, 0x0000 }, + { 0x5c6f, 0x0000 }, + { 0x5c70, 0x0000 }, + { 0x5c71, 0x0000 }, + { 0x5c72, 0x0000 }, + { 0x5c73, 0x0000 }, + { 0x5c74, 0x0000 }, + { 0x5c75, 0x0000 }, + { 0x5c76, 0x0000 }, + { 0x5c77, 0x0000 }, + { 0x5c78, 0x0000 }, + { 0x5c79, 0x0000 }, + { 0x5c7a, 0x0000 }, + { 0x5c7b, 0x0000 }, + { 0x5c7c, 0x0000 }, + { 0x5c7d, 0x0000 }, + { 0x5c7e, 0x0000 }, + { 0x5c7f, 0x0000 }, + { 0x5c80, 0x0000 }, + { 0x5c81, 0x0000 }, + { 0x5c82, 0x0000 }, + { 0x5c83, 0x0000 }, + { 0x5c84, 0x0000 }, + { 0x5c85, 0x0000 }, + { 0x5c86, 0x0000 }, + { 0x5c87, 0x0000 }, + { 0x5c88, 0x0000 }, + { 0x5c89, 0x0000 }, + { 0x5c8a, 0x0000 }, + { 0x5c8b, 0x0000 }, + { 0x5c8c, 0x0000 }, + { 0x5c8d, 0x0000 }, + { 0x5c8e, 0x0000 }, + { 0x5c8f, 0x0000 }, + { 0x5c90, 0x0000 }, + { 0x5c91, 0x0000 }, + { 0x5c92, 0x0000 }, + { 0x5c93, 0x0000 }, + { 0x5c94, 0x0000 }, + { 0x5c95, 0x0000 }, + { 0x5c96, 0x0000 }, + { 0x5c97, 0x0000 }, + { 0x5c98, 0x0000 }, + { 0x5c99, 0x0000 }, + { 0x5c9a, 0x0000 }, + { 0x5c9b, 0x0000 }, + { 0x5c9c, 0x0000 }, + { 0x5c9d, 0x0000 }, + { 0x5c9e, 0x0000 }, + { 0x5c9f, 0x0000 }, + { 0x5ca0, 0x0000 }, + { 0x5ca1, 0x0000 }, + { 0x5ca2, 0x0000 }, + { 0x5ca3, 0x0000 }, + { 0x5ca4, 0x0000 }, + { 0x5ca5, 0x0000 }, + { 0x5ca6, 0x0000 }, + { 0x5ca7, 0x0000 }, + { 0x5ca8, 0x0000 }, + { 0x5ca9, 0x0000 }, + { 0x5caa, 0x0000 }, + { 0x5cab, 0x0000 }, + { 0x5cac, 0x0000 }, + { 0x5cad, 0x0000 }, + { 0x5cae, 0x0000 }, + { 0x5caf, 0x0000 }, + { 0x5cb0, 0x0000 }, + { 0x5cb1, 0x0000 }, + { 0x5cb2, 0x0000 }, + { 0x5cb3, 0x0000 }, + { 0x5cb4, 0x0000 }, + { 0x5cb5, 0x0000 }, + { 0x5cb6, 0x0000 }, + { 0x5cb7, 0x0000 }, + { 0x5cb8, 0x0000 }, + { 0x5cb9, 0x0000 }, + { 0x5cba, 0x0000 }, + { 0x5cbb, 0x0000 }, + { 0x5cbc, 0x0000 }, + { 0x5cbd, 0x0000 }, + { 0x5cbe, 0x0000 }, + { 0x5cbf, 0x0000 }, + { 0x5cc0, 0x0000 }, + { 0x5cc1, 0x0000 }, + { 0x5cc2, 0x0000 }, + { 0x5cc3, 0x0000 }, + { 0x5cc4, 0x0000 }, + { 0x5cc5, 0x0000 }, + { 0x5cc6, 0x0000 }, + { 0x5cc7, 0x0000 }, + { 0x5cc8, 0x0000 }, + { 0x5cc9, 0x0000 }, + { 0x5cca, 0x0000 }, + { 0x5ccb, 0x0000 }, + { 0x5ccc, 0x0000 }, + { 0x5ccd, 0x0000 }, + { 0x5cce, 0x0000 }, + { 0x5ccf, 0x0000 }, + { 0x5cd0, 0x0000 }, + { 0x5cd1, 0x0000 }, + { 0x5cd2, 0x0000 }, + { 0x5cd3, 0x0000 }, + { 0x5cd4, 0x0000 }, + { 0x5cd5, 0x0000 }, + { 0x5cd6, 0x0000 }, + { 0x5cd7, 0x0000 }, + { 0x5cd8, 0x0000 }, + { 0x5cd9, 0x0000 }, + { 0x5cda, 0x0000 }, + { 0x5cdb, 0x0000 }, + { 0x5cdc, 0x0000 }, + { 0x5cdd, 0x0000 }, + { 0x5cde, 0x0000 }, + { 0x5cdf, 0x0000 }, + { 0x5ce0, 0x0000 }, + { 0x5ce1, 0x0000 }, + { 0x5ce2, 0x0000 }, + { 0x5ce3, 0x0000 }, + { 0x5ce4, 0x0000 }, + { 0x5ce5, 0x0000 }, + { 0x5ce6, 0x0000 }, + { 0x5ce7, 0x0000 }, + { 0x5ce8, 0x0000 }, + { 0x5ce9, 0x0000 }, + { 0x5cea, 0x0000 }, + { 0x5ceb, 0x0000 }, + { 0x5cec, 0x0000 }, + { 0x5ced, 0x0000 }, + { 0x5cee, 0x0000 }, + { 0x5cef, 0x0000 }, + { 0x5cf0, 0x0000 }, + { 0x5cf1, 0x0000 }, + { 0x5cf2, 0x0000 }, + { 0x5cf3, 0x0000 }, + { 0x5cf4, 0x0000 }, + { 0x5cf5, 0x0000 }, + { 0x5cf6, 0x0000 }, + { 0x5cf7, 0x0000 }, + { 0x5cf8, 0x0000 }, + { 0x5cf9, 0x0000 }, + { 0x5cfa, 0x0000 }, + { 0x5cfb, 0x0000 }, + { 0x5cfc, 0x0000 }, + { 0x5cfd, 0x0000 }, + { 0x5cfe, 0x0000 }, + { 0x5cff, 0x0000 }, + { 0x5d00, 0x0000 }, + { 0x5d01, 0x0000 }, + { 0x5d02, 0x0000 }, + { 0x5d03, 0x0000 }, + { 0x5d04, 0x0000 }, + { 0x5d05, 0x0000 }, + { 0x5d06, 0x0000 }, + { 0x5d07, 0x0000 }, + { 0x5d08, 0x0000 }, + { 0x5d09, 0x0000 }, + { 0x5d0a, 0x0000 }, + { 0x5d0b, 0x0000 }, + { 0x5d0c, 0x0000 }, + { 0x5d0d, 0x0000 }, + { 0x5d0e, 0x0000 }, + { 0x5d0f, 0x0000 }, + { 0x5d10, 0x0000 }, + { 0x5d11, 0x0000 }, + { 0x5d12, 0x0000 }, + { 0x5d13, 0x0000 }, + { 0x5d14, 0x0000 }, + { 0x5d15, 0x0000 }, + { 0x5d16, 0x0000 }, + { 0x5d17, 0x0000 }, + { 0x5d18, 0x0000 }, + { 0x5d19, 0x0000 }, + { 0x5d1a, 0x0000 }, + { 0x5d1b, 0x0000 }, + { 0x5d1c, 0x0000 }, + { 0x5d1d, 0x0000 }, + { 0x5d1e, 0x0000 }, + { 0x5d1f, 0x0000 }, + { 0x5d20, 0x0000 }, + { 0x5d21, 0x0000 }, + { 0x5d22, 0x0000 }, + { 0x5d23, 0x0000 }, + { 0x5d24, 0x0000 }, + { 0x5d25, 0x0000 }, + { 0x5d26, 0x0000 }, + { 0x5d27, 0x0000 }, + { 0x5d28, 0x0000 }, + { 0x5d29, 0x0000 }, + { 0x5d2a, 0x0000 }, + { 0x5d2b, 0x0000 }, + { 0x5d2c, 0x0000 }, + { 0x5d2d, 0x0000 }, + { 0x5d2e, 0x0000 }, + { 0x5d2f, 0x0000 }, + { 0x5d30, 0x0000 }, + { 0x5d31, 0x0000 }, + { 0x5d32, 0x0000 }, + { 0x5d33, 0x0000 }, + { 0x5d34, 0x0000 }, + { 0x5d35, 0x0000 }, + { 0x5d36, 0x0000 }, + { 0x5d37, 0x0000 }, + { 0x5d38, 0x0000 }, + { 0x5d39, 0x0000 }, + { 0x5d3a, 0x0000 }, + { 0x5d3b, 0x0000 }, + { 0x5d3c, 0x0000 }, + { 0x5d3d, 0x0000 }, + { 0x5d3e, 0x0000 }, + { 0x5d3f, 0x0000 }, + { 0x5d40, 0x0000 }, + { 0x5d41, 0x0000 }, + { 0x5d42, 0x0000 }, + { 0x5d43, 0x0000 }, + { 0x5d44, 0x0000 }, + { 0x5d45, 0x0000 }, + { 0x5d46, 0x0000 }, + { 0x5d47, 0x0000 }, + { 0x5d48, 0x0000 }, + { 0x5d49, 0x0000 }, + { 0x5d4a, 0x0000 }, + { 0x5d4b, 0x0000 }, + { 0x5d4c, 0x0000 }, + { 0x5d4d, 0x0000 }, + { 0x5d4e, 0x0000 }, + { 0x5d4f, 0x0000 }, + { 0x5d50, 0x0000 }, + { 0x5d51, 0x0000 }, + { 0x5d52, 0x0000 }, + { 0x5d53, 0x0000 }, + { 0x5d54, 0x0000 }, + { 0x5d55, 0x0000 }, + { 0x5d56, 0x0000 }, + { 0x5d57, 0x0000 }, + { 0x5d58, 0x0000 }, + { 0x5d59, 0x0000 }, + { 0x5d5a, 0x0000 }, + { 0x5d5b, 0x0000 }, + { 0x5d5c, 0x0000 }, + { 0x5d5d, 0x0000 }, + { 0x5d5e, 0x0000 }, + { 0x5d5f, 0x0000 }, + { 0x5d60, 0x0000 }, + { 0x5d61, 0x0000 }, + { 0x5d62, 0x0000 }, + { 0x5d63, 0x0000 }, + { 0x5d64, 0x0000 }, + { 0x5d65, 0x0000 }, + { 0x5d66, 0x0000 }, + { 0x5d67, 0x0000 }, + { 0x5d68, 0x0000 }, + { 0x5d69, 0x0000 }, + { 0x5d6a, 0x0000 }, + { 0x5d6b, 0x0000 }, + { 0x5d6c, 0x0000 }, + { 0x5d6d, 0x0000 }, + { 0x5d6e, 0x0000 }, + { 0x5d6f, 0x0000 }, + { 0x5d70, 0x0000 }, + { 0x5d71, 0x0000 }, + { 0x5d72, 0x0000 }, + { 0x5d73, 0x0000 }, + { 0x5d74, 0x0000 }, + { 0x5d75, 0x0000 }, + { 0x5d76, 0x0000 }, + { 0x5d77, 0x0000 }, + { 0x5d78, 0x0000 }, + { 0x5d79, 0x0000 }, + { 0x5d7a, 0x0000 }, + { 0x5d7b, 0x0000 }, + { 0x5d7c, 0x0000 }, + { 0x5d7d, 0x0000 }, + { 0x5d7e, 0x0000 }, + { 0x5d7f, 0x0000 }, + { 0x5d80, 0x0000 }, + { 0x5d81, 0x0000 }, + { 0x5d82, 0x0000 }, + { 0x5d83, 0x0000 }, + { 0x5d84, 0x0000 }, + { 0x5d85, 0x0000 }, + { 0x5d86, 0x0000 }, + { 0x5d87, 0x0000 }, + { 0x5d88, 0x0000 }, + { 0x5d89, 0x0000 }, + { 0x5d8a, 0x0000 }, + { 0x5d8b, 0x0000 }, + { 0x5d8c, 0x0000 }, + { 0x5d8d, 0x0000 }, + { 0x5d8e, 0x0000 }, + { 0x5d8f, 0x0000 }, + { 0x5d90, 0x0000 }, + { 0x5d91, 0x0000 }, + { 0x5d92, 0x0000 }, + { 0x5d93, 0x0000 }, + { 0x5d94, 0x0000 }, + { 0x5d95, 0x0000 }, + { 0x5d96, 0x0000 }, + { 0x5d97, 0x0000 }, + { 0x5d98, 0x0000 }, + { 0x5d99, 0x0000 }, + { 0x5d9a, 0x0000 }, + { 0x5d9b, 0x0000 }, + { 0x5d9c, 0x0000 }, + { 0x5d9d, 0x0000 }, + { 0x5d9e, 0x0000 }, + { 0x5d9f, 0x0000 }, + { 0x5da0, 0x0000 }, + { 0x5da1, 0x0000 }, + { 0x5da2, 0x0000 }, + { 0x5da3, 0x0000 }, + { 0x5da4, 0x0000 }, + { 0x5da5, 0x0000 }, + { 0x5da6, 0x0000 }, + { 0x5da7, 0x0000 }, + { 0x5da8, 0x0000 }, + { 0x5da9, 0x0000 }, + { 0x5daa, 0x0000 }, + { 0x5dab, 0x0000 }, + { 0x5dac, 0x0000 }, + { 0x5dad, 0x0000 }, + { 0x5dae, 0x0000 }, + { 0x5daf, 0x0000 }, + { 0x5db0, 0x0000 }, + { 0x5db1, 0x0000 }, + { 0x5db2, 0x0000 }, + { 0x5db3, 0x0000 }, + { 0x5db4, 0x0000 }, + { 0x5db5, 0x0000 }, + { 0x5db6, 0x0000 }, + { 0x5db7, 0x0000 }, + { 0x5db8, 0x0000 }, + { 0x5db9, 0x0000 }, + { 0x5dba, 0x0000 }, + { 0x5dbb, 0x0000 }, + { 0x5dbc, 0x0000 }, + { 0x5dbd, 0x0000 }, + { 0x5dbe, 0x0000 }, + { 0x5dbf, 0x0000 }, + { 0x5dc0, 0x0000 }, + { 0x5dc1, 0x0000 }, + { 0x5dc2, 0x0000 }, + { 0x5dc3, 0x0000 }, + { 0x5dc4, 0x0000 }, + { 0x5dc5, 0x0000 }, + { 0x5dc6, 0x0000 }, + { 0x5dc7, 0x0000 }, + { 0x5dc8, 0x0000 }, + { 0x5dc9, 0x0000 }, + { 0x5dca, 0x0000 }, + { 0x5dcb, 0x0000 }, + { 0x5dcc, 0x0000 }, + { 0x5dcd, 0x0000 }, + { 0x5dce, 0x0000 }, + { 0x5dcf, 0x0000 }, + { 0x5dd0, 0x0000 }, + { 0x5dd1, 0x0000 }, + { 0x5dd2, 0x0000 }, + { 0x5dd3, 0x0000 }, + { 0x5dd4, 0x0000 }, + { 0x5dd5, 0x0000 }, + { 0x5dd6, 0x0000 }, + { 0x5dd7, 0x0000 }, + { 0x5dd8, 0x0000 }, + { 0x5dd9, 0x0000 }, + { 0x5dda, 0x0000 }, + { 0x5ddb, 0x0000 }, + { 0x5ddc, 0x0000 }, + { 0x5ddd, 0x0000 }, + { 0x5dde, 0x0000 }, + { 0x5ddf, 0x0000 }, + { 0x5de0, 0x0000 }, + { 0x5de1, 0x0000 }, + { 0x5de2, 0x0000 }, + { 0x5de3, 0x0000 }, + { 0x5de4, 0x0000 }, + { 0x5de5, 0x0000 }, + { 0x5de6, 0x0000 }, + { 0x5de7, 0x0000 }, + { 0x5de8, 0x0000 }, + { 0x5de9, 0x0000 }, + { 0x5dea, 0x0000 }, + { 0x5deb, 0x0000 }, + { 0x5dec, 0x0000 }, + { 0x5ded, 0x0000 }, + { 0x5dee, 0x0000 }, + { 0x5def, 0x0000 }, + { 0x5df0, 0x0000 }, + { 0x5df1, 0x0000 }, + { 0x5df2, 0x0000 }, + { 0x5df3, 0x0000 }, + { 0x5df4, 0x0000 }, + { 0x5df5, 0x0000 }, + { 0x5df6, 0x0000 }, + { 0x5df7, 0x0000 }, + { 0x5df8, 0x0000 }, + { 0x5df9, 0x0000 }, + { 0x5dfa, 0x0000 }, + { 0x5dfb, 0x0000 }, + { 0x5dfc, 0x0000 }, + { 0x5dfd, 0x0000 }, + { 0x5dfe, 0x0000 }, + { 0x5dff, 0x0000 }, + { 0x5e00, 0x0000 }, + { 0x5e01, 0x0000 }, + { 0x5e02, 0x0000 }, + { 0x5e03, 0x0000 }, + { 0x5e04, 0x0000 }, + { 0x5e05, 0x0000 }, + { 0x5e06, 0x0000 }, + { 0x5e07, 0x0000 }, + { 0x5e08, 0x0000 }, + { 0x5e09, 0x0000 }, + { 0x5e0a, 0x0000 }, + { 0x5e0b, 0x0000 }, + { 0x5e0c, 0x0000 }, + { 0x5e0d, 0x0000 }, + { 0x5e0e, 0x0000 }, + { 0x5e0f, 0x0000 }, + { 0x5e10, 0x0000 }, + { 0x5e11, 0x0000 }, + { 0x5e12, 0x0000 }, + { 0x5e13, 0x0000 }, + { 0x5e14, 0x0000 }, + { 0x5e15, 0x0000 }, + { 0x5e16, 0x0000 }, + { 0x5e17, 0x0000 }, + { 0x5e18, 0x0000 }, + { 0x5e19, 0x0000 }, + { 0x5e1a, 0x0000 }, + { 0x5e1b, 0x0000 }, + { 0x5e1c, 0x0000 }, + { 0x5e1d, 0x0000 }, + { 0x5e1e, 0x0000 }, + { 0x5e1f, 0x0000 }, + { 0x5e20, 0x0000 }, + { 0x5e21, 0x0000 }, + { 0x5e22, 0x0000 }, + { 0x5e23, 0x0000 }, + { 0x5e24, 0x0000 }, + { 0x5e25, 0x0000 }, + { 0x5e26, 0x0000 }, + { 0x5e27, 0x0000 }, + { 0x5e28, 0x0000 }, + { 0x5e29, 0x0000 }, + { 0x5e2a, 0x0000 }, + { 0x5e2b, 0x0000 }, + { 0x5e2c, 0x0000 }, + { 0x5e2d, 0x0000 }, + { 0x5e2e, 0x0000 }, + { 0x5e2f, 0x0000 }, + { 0x5e30, 0x0000 }, + { 0x5e31, 0x0000 }, + { 0x5e32, 0x0000 }, + { 0x5e33, 0x0000 }, + { 0x5e34, 0x0000 }, + { 0x5e35, 0x0000 }, + { 0x5e36, 0x0000 }, + { 0x5e37, 0x0000 }, + { 0x5e38, 0x0000 }, + { 0x5e39, 0x0000 }, + { 0x5e3a, 0x0000 }, + { 0x5e3b, 0x0000 }, + { 0x5e3c, 0x0000 }, + { 0x5e3d, 0x0000 }, + { 0x5e3e, 0x0000 }, + { 0x5e3f, 0x0000 }, + { 0x5e40, 0x0000 }, + { 0x5e41, 0x0000 }, + { 0x5e42, 0x0000 }, + { 0x5e43, 0x0000 }, + { 0x5e44, 0x0000 }, + { 0x5e45, 0x0000 }, + { 0x5e46, 0x0000 }, + { 0x5e47, 0x0000 }, + { 0x5e48, 0x0000 }, + { 0x5e49, 0x0000 }, + { 0x5e4a, 0x0000 }, + { 0x5e4b, 0x0000 }, + { 0x5e4c, 0x0000 }, + { 0x5e4d, 0x0000 }, + { 0x5e4e, 0x0000 }, + { 0x5e4f, 0x0000 }, + { 0x5e50, 0x0000 }, + { 0x5e51, 0x0000 }, + { 0x5e52, 0x0000 }, + { 0x5e53, 0x0000 }, + { 0x5e54, 0x0000 }, + { 0x5e55, 0x0000 }, + { 0x5e56, 0x0000 }, + { 0x5e57, 0x0000 }, + { 0x5e58, 0x0000 }, + { 0x5e59, 0x0000 }, + { 0x5e5a, 0x0000 }, + { 0x5e5b, 0x0000 }, + { 0x5e5c, 0x0000 }, + { 0x5e5d, 0x0000 }, + { 0x5e5e, 0x0000 }, + { 0x5e5f, 0x0000 }, + { 0x5e60, 0x0000 }, + { 0x5e61, 0x0000 }, + { 0x5e62, 0x0000 }, + { 0x5e63, 0x0000 }, + { 0x5e64, 0x0000 }, + { 0x5e65, 0x0000 }, + { 0x5e66, 0x0000 }, + { 0x5e67, 0x0000 }, + { 0x5e68, 0x0000 }, + { 0x5e69, 0x0000 }, + { 0x5e6a, 0x0000 }, + { 0x5e6b, 0x0000 }, + { 0x5e6c, 0x0000 }, + { 0x5e6d, 0x0000 }, + { 0x5e6e, 0x0000 }, + { 0x5e6f, 0x0000 }, + { 0x5e70, 0x0000 }, + { 0x5e71, 0x0000 }, + { 0x5e72, 0x0000 }, + { 0x5e73, 0x0000 }, + { 0x5e74, 0x0000 }, + { 0x5e75, 0x0000 }, + { 0x5e76, 0x0000 }, + { 0x5e77, 0x0000 }, + { 0x5e78, 0x0000 }, + { 0x5e79, 0x0000 }, + { 0x5e7a, 0x0000 }, + { 0x5e7b, 0x0000 }, + { 0x5e7c, 0x0000 }, + { 0x5e7d, 0x0000 }, + { 0x5e7e, 0x0000 }, + { 0x5e7f, 0x0000 }, + { 0x5e80, 0x0000 }, + { 0x5e81, 0x0000 }, + { 0x5e82, 0x0000 }, + { 0x5e83, 0x0000 }, + { 0x5e84, 0x0000 }, + { 0x5e85, 0x0000 }, + { 0x5e86, 0x0000 }, + { 0x5e87, 0x0000 }, + { 0x5e88, 0x0000 }, + { 0x5e89, 0x0000 }, + { 0x5e8a, 0x0000 }, + { 0x5e8b, 0x0000 }, + { 0x5e8c, 0x0000 }, + { 0x5e8d, 0x0000 }, + { 0x5e8e, 0x0000 }, + { 0x5e8f, 0x0000 }, + { 0x5e90, 0x0000 }, + { 0x5e91, 0x0000 }, + { 0x5e92, 0x0000 }, + { 0x5e93, 0x0000 }, + { 0x5e94, 0x0000 }, + { 0x5e95, 0x0000 }, + { 0x5e96, 0x0000 }, + { 0x5e97, 0x0000 }, + { 0x5e98, 0x0000 }, + { 0x5e99, 0x0000 }, + { 0x5e9a, 0x0000 }, + { 0x5e9b, 0x0000 }, + { 0x5e9c, 0x0000 }, + { 0x5e9d, 0x0000 }, + { 0x5e9e, 0x0000 }, + { 0x5e9f, 0x0000 }, + { 0x5ea0, 0x0000 }, + { 0x5ea1, 0x0000 }, + { 0x5ea2, 0x0000 }, + { 0x5ea3, 0x0000 }, + { 0x5ea4, 0x0000 }, + { 0x5ea5, 0x0000 }, + { 0x5ea6, 0x0000 }, + { 0x5ea7, 0x0000 }, + { 0x5ea8, 0x0000 }, + { 0x5ea9, 0x0000 }, + { 0x5eaa, 0x0000 }, + { 0x5eab, 0x0000 }, + { 0x5eac, 0x0000 }, + { 0x5ead, 0x0000 }, + { 0x5eae, 0x0000 }, + { 0x5eaf, 0x0000 }, + { 0x5eb0, 0x0000 }, + { 0x5eb1, 0x0000 }, + { 0x5eb2, 0x0000 }, + { 0x5eb3, 0x0000 }, + { 0x5eb4, 0x0000 }, + { 0x5eb5, 0x0000 }, + { 0x5eb6, 0x0000 }, + { 0x5eb7, 0x0000 }, + { 0x5eb8, 0x0000 }, + { 0x5eb9, 0x0000 }, + { 0x5eba, 0x0000 }, + { 0x5ebb, 0x0000 }, + { 0x5ebc, 0x0000 }, + { 0x5ebd, 0x0000 }, + { 0x5ebe, 0x0000 }, + { 0x5ebf, 0x0000 }, + { 0x5ec0, 0x0000 }, + { 0x5ec1, 0x0000 }, + { 0x5ec2, 0x0000 }, + { 0x5ec3, 0x0000 }, + { 0x5ec4, 0x0000 }, + { 0x5ec5, 0x0000 }, + { 0x5ec6, 0x0000 }, + { 0x5ec7, 0x0000 }, + { 0x5ec8, 0x0000 }, + { 0x5ec9, 0x0000 }, + { 0x5eca, 0x0000 }, + { 0x5ecb, 0x0000 }, + { 0x5ecc, 0x0000 }, + { 0x5ecd, 0x0000 }, + { 0x5ece, 0x0000 }, + { 0x5ecf, 0x0000 }, + { 0x5ed0, 0x0000 }, + { 0x5ed1, 0x0000 }, + { 0x5ed2, 0x0000 }, + { 0x5ed3, 0x0000 }, + { 0x5ed4, 0x0000 }, + { 0x5ed5, 0x0000 }, + { 0x5ed6, 0x0000 }, + { 0x5ed7, 0x0000 }, + { 0x5ed8, 0x0000 }, + { 0x5ed9, 0x0000 }, + { 0x5eda, 0x0000 }, + { 0x5edb, 0x0000 }, + { 0x5edc, 0x0000 }, + { 0x5edd, 0x0000 }, + { 0x5ede, 0x0000 }, + { 0x5edf, 0x0000 }, + { 0x5ee0, 0x0000 }, + { 0x5ee1, 0x0000 }, + { 0x5ee2, 0x0000 }, + { 0x5ee3, 0x0000 }, + { 0x5ee4, 0x0000 }, + { 0x5ee5, 0x0000 }, + { 0x5ee6, 0x0000 }, + { 0x5ee7, 0x0000 }, + { 0x5ee8, 0x0000 }, + { 0x5ee9, 0x0000 }, + { 0x5eea, 0x0000 }, + { 0x5eeb, 0x0000 }, + { 0x5eec, 0x0000 }, + { 0x5eed, 0x0000 }, + { 0x5eee, 0x0000 }, + { 0x5eef, 0x0000 }, + { 0x5ef0, 0x0000 }, + { 0x5ef1, 0x0000 }, + { 0x5ef2, 0x0000 }, + { 0x5ef3, 0x0000 }, + { 0x5ef4, 0x0000 }, + { 0x5ef5, 0x0000 }, + { 0x5ef6, 0x0000 }, + { 0x5ef7, 0x0000 }, + { 0x5ef8, 0x0000 }, + { 0x5ef9, 0x0000 }, + { 0x5efa, 0x0000 }, + { 0x5efb, 0x0000 }, + { 0x5efc, 0x0000 }, + { 0x5efd, 0x0000 }, + { 0x5efe, 0x0000 }, + { 0x5eff, 0x0000 }, + { 0x5f00, 0x0000 }, + { 0x5f01, 0x0000 }, + { 0x5f02, 0x0000 }, + { 0x5f03, 0x0000 }, + { 0x5f04, 0x0000 }, + { 0x5f05, 0x0000 }, + { 0x5f06, 0x0000 }, + { 0x5f07, 0x0000 }, + { 0x5f08, 0x0000 }, + { 0x5f09, 0x0000 }, + { 0x5f0a, 0x0000 }, + { 0x5f0b, 0x0000 }, + { 0x5f0c, 0x0000 }, + { 0x5f0d, 0x0000 }, + { 0x5f0e, 0x0000 }, + { 0x5f0f, 0x0000 }, + { 0x5f10, 0x0000 }, + { 0x5f11, 0x0000 }, + { 0x5f12, 0x0000 }, + { 0x5f13, 0x0000 }, + { 0x5f14, 0x0000 }, + { 0x5f15, 0x0000 }, + { 0x5f16, 0x0000 }, + { 0x5f17, 0x0000 }, + { 0x5f18, 0x0000 }, + { 0x5f19, 0x0000 }, + { 0x5f1a, 0x0000 }, + { 0x5f1b, 0x0000 }, + { 0x5f1c, 0x0000 }, + { 0x5f1d, 0x0000 }, + { 0x5f1e, 0x0000 }, + { 0x5f1f, 0x0000 }, + { 0x5f20, 0x0000 }, + { 0x5f21, 0x0000 }, + { 0x5f22, 0x0000 }, + { 0x5f23, 0x0000 }, + { 0x5f24, 0x0000 }, + { 0x5f25, 0x0000 }, + { 0x5f26, 0x0000 }, + { 0x5f27, 0x0000 }, + { 0x5f28, 0x0000 }, + { 0x5f29, 0x0000 }, + { 0x5f2a, 0x0000 }, + { 0x5f2b, 0x0000 }, + { 0x5f2c, 0x0000 }, + { 0x5f2d, 0x0000 }, + { 0x5f2e, 0x0000 }, + { 0x5f2f, 0x0000 }, + { 0x5f30, 0x0000 }, + { 0x5f31, 0x0000 }, + { 0x5f32, 0x0000 }, + { 0x5f33, 0x0000 }, + { 0x5f34, 0x0000 }, + { 0x5f35, 0x0000 }, + { 0x5f36, 0x0000 }, + { 0x5f37, 0x0000 }, + { 0x5f38, 0x0000 }, + { 0x5f39, 0x0000 }, + { 0x5f3a, 0x0000 }, + { 0x5f3b, 0x0000 }, + { 0x5f3c, 0x0000 }, + { 0x5f3d, 0x0000 }, + { 0x5f3e, 0x0000 }, + { 0x5f3f, 0x0000 }, + { 0x5f40, 0x0000 }, + { 0x5f41, 0x0000 }, + { 0x5f42, 0x0000 }, + { 0x5f43, 0x0000 }, + { 0x5f44, 0x0000 }, + { 0x5f45, 0x0000 }, + { 0x5f46, 0x0000 }, + { 0x5f47, 0x0000 }, + { 0x5f48, 0x0000 }, + { 0x5f49, 0x0000 }, + { 0x5f4a, 0x0000 }, + { 0x5f4b, 0x0000 }, + { 0x5f4c, 0x0000 }, + { 0x5f4d, 0x0000 }, + { 0x5f4e, 0x0000 }, + { 0x5f4f, 0x0000 }, + { 0x5f50, 0x0000 }, + { 0x5f51, 0x0000 }, + { 0x5f52, 0x0000 }, + { 0x5f53, 0x0000 }, + { 0x5f54, 0x0000 }, + { 0x5f55, 0x0000 }, + { 0x5f56, 0x0000 }, + { 0x5f57, 0x0000 }, + { 0x5f58, 0x0000 }, + { 0x5f59, 0x0000 }, + { 0x5f5a, 0x0000 }, + { 0x5f5b, 0x0000 }, + { 0x5f5c, 0x0000 }, + { 0x5f5d, 0x0000 }, + { 0x5f5e, 0x0000 }, + { 0x5f5f, 0x0000 }, + { 0x5f60, 0x0000 }, + { 0x5f61, 0x0000 }, + { 0x5f62, 0x0000 }, + { 0x5f63, 0x0000 }, + { 0x5f64, 0x0000 }, + { 0x5f65, 0x0000 }, + { 0x5f66, 0x0000 }, + { 0x5f67, 0x0000 }, + { 0x5f68, 0x0000 }, + { 0x5f69, 0x0000 }, + { 0x5f6a, 0x0000 }, + { 0x5f6b, 0x0000 }, + { 0x5f6c, 0x0000 }, + { 0x5f6d, 0x0000 }, + { 0x5f6e, 0x0000 }, + { 0x5f6f, 0x0000 }, + { 0x5f70, 0x0000 }, + { 0x5f71, 0x0000 }, + { 0x5f72, 0x0000 }, + { 0x5f73, 0x0000 }, + { 0x5f74, 0x0000 }, + { 0x5f75, 0x0000 }, + { 0x5f76, 0x0000 }, + { 0x5f77, 0x0000 }, + { 0x5f78, 0x0000 }, + { 0x5f79, 0x0000 }, + { 0x5f7a, 0x0000 }, + { 0x5f7b, 0x0000 }, + { 0x5f7c, 0x0000 }, + { 0x5f7d, 0x0000 }, + { 0x5f7e, 0x0000 }, + { 0x5f7f, 0x0000 }, + { 0x5f80, 0x0000 }, + { 0x5f81, 0x0000 }, + { 0x5f82, 0x0000 }, + { 0x5f83, 0x0000 }, + { 0x5f84, 0x0000 }, + { 0x5f85, 0x0000 }, + { 0x5f86, 0x0000 }, + { 0x5f87, 0x0000 }, + { 0x5f88, 0x0000 }, + { 0x5f89, 0x0000 }, + { 0x5f8a, 0x0000 }, + { 0x5f8b, 0x0000 }, + { 0x5f8c, 0x0000 }, + { 0x5f8d, 0x0000 }, + { 0x5f8e, 0x0000 }, + { 0x5f8f, 0x0000 }, + { 0x5f90, 0x0000 }, + { 0x5f91, 0x0000 }, + { 0x5f92, 0x0000 }, + { 0x5f93, 0x0000 }, + { 0x5f94, 0x0000 }, + { 0x5f95, 0x0000 }, + { 0x5f96, 0x0000 }, + { 0x5f97, 0x0000 }, + { 0x5f98, 0x0000 }, + { 0x5f99, 0x0000 }, + { 0x5f9a, 0x0000 }, + { 0x5f9b, 0x0000 }, + { 0x5f9c, 0x0000 }, + { 0x5f9d, 0x0000 }, + { 0x5f9e, 0x0000 }, + { 0x5f9f, 0x0000 }, + { 0x5fa0, 0x0000 }, + { 0x5fa1, 0x0000 }, + { 0x5fa2, 0x0000 }, + { 0x5fa3, 0x0000 }, + { 0x5fa4, 0x0000 }, + { 0x5fa5, 0x0000 }, + { 0x5fa6, 0x0000 }, + { 0x5fa7, 0x0000 }, + { 0x5fa8, 0x0000 }, + { 0x5fa9, 0x0000 }, + { 0x5faa, 0x0000 }, + { 0x5fab, 0x0000 }, + { 0x5fac, 0x0000 }, + { 0x5fad, 0x0000 }, + { 0x5fae, 0x0000 }, + { 0x5faf, 0x0000 }, + { 0x5fb0, 0x0000 }, + { 0x5fb1, 0x0000 }, + { 0x5fb2, 0x0000 }, + { 0x5fb3, 0x0000 }, + { 0x5fb4, 0x0000 }, + { 0x5fb5, 0x0000 }, + { 0x5fb6, 0x0000 }, + { 0x5fb7, 0x0000 }, + { 0x5fb8, 0x0000 }, + { 0x5fb9, 0x0000 }, + { 0x5fba, 0x0000 }, + { 0x5fbb, 0x0000 }, + { 0x5fbc, 0x0000 }, + { 0x5fbd, 0x0000 }, + { 0x5fbe, 0x0000 }, + { 0x5fbf, 0x0000 }, + { 0x5fc0, 0x0000 }, + { 0x5fc1, 0x0000 }, + { 0x5fc2, 0x0000 }, + { 0x5fc3, 0x0000 }, + { 0x5fc4, 0x0000 }, + { 0x5fc5, 0x0000 }, + { 0x5fc6, 0x0000 }, + { 0x5fc7, 0x0000 }, + { 0x5fc8, 0x0000 }, + { 0x5fc9, 0x0000 }, + { 0x5fca, 0x0000 }, + { 0x5fcb, 0x0000 }, + { 0x5fcc, 0x0000 }, + { 0x5fcd, 0x0000 }, + { 0x5fce, 0x0000 }, + { 0x5fcf, 0x0000 }, + { 0x5fd0, 0x0000 }, + { 0x5fd1, 0x0000 }, + { 0x5fd2, 0x0000 }, + { 0x5fd3, 0x0000 }, + { 0x5fd4, 0x0000 }, + { 0x5fd5, 0x0000 }, + { 0x5fd6, 0x0000 }, + { 0x5fd7, 0x0000 }, + { 0x5fd8, 0x0000 }, + { 0x5fd9, 0x0000 }, + { 0x5fda, 0x0000 }, + { 0x5fdb, 0x0000 }, + { 0x5fdc, 0x0000 }, + { 0x5fdd, 0x0000 }, + { 0x5fde, 0x0000 }, + { 0x5fdf, 0x0000 }, + { 0x5fe0, 0x0000 }, + { 0x5fe1, 0x0000 }, + { 0x5fe2, 0x0000 }, + { 0x5fe3, 0x0000 }, + { 0x5fe4, 0x0000 }, + { 0x5fe5, 0x0000 }, + { 0x5fe6, 0x0000 }, + { 0x5fe7, 0x0000 }, + { 0x5fe8, 0x0000 }, + { 0x5fe9, 0x0000 }, + { 0x5fea, 0x0000 }, + { 0x5feb, 0x0000 }, + { 0x5fec, 0x0000 }, + { 0x5fed, 0x0000 }, + { 0x5fee, 0x0000 }, + { 0x5fef, 0x0000 }, + { 0x5ff0, 0x0000 }, + { 0x5ff1, 0x0000 }, + { 0x5ff2, 0x0000 }, + { 0x5ff3, 0x0000 }, + { 0x5ff4, 0x0000 }, + { 0x5ff5, 0x0000 }, + { 0x5ff6, 0x0000 }, + { 0x5ff7, 0x0000 }, + { 0x5ff8, 0x0000 }, + { 0x5ff9, 0x0000 }, + { 0x5ffa, 0x0000 }, + { 0x5ffb, 0x0000 }, + { 0x5ffc, 0x0000 }, + { 0x5ffd, 0x0000 }, + { 0x5ffe, 0x0000 }, + { 0x5fff, 0x0000 }, + { 0x6000, 0x0000 }, + { 0x6001, 0x0000 }, + { 0x6002, 0x0000 }, + { 0x6003, 0x0000 }, + { 0x6004, 0x0000 }, + { 0x6005, 0x0000 }, + { 0x6006, 0x0000 }, + { 0x6007, 0x0000 }, + { 0x6008, 0x0000 }, + { 0x6009, 0x0000 }, + { 0x600a, 0x0000 }, + { 0x600b, 0x0000 }, + { 0x600c, 0x0000 }, + { 0x600d, 0x0000 }, + { 0x600e, 0x0000 }, + { 0x600f, 0x0000 }, + { 0x6010, 0x0000 }, + { 0x6011, 0x0000 }, + { 0x6012, 0x0000 }, + { 0x6013, 0x0000 }, + { 0x6014, 0x0000 }, + { 0x6015, 0x0000 }, + { 0x6016, 0x0000 }, + { 0x6017, 0x0000 }, + { 0x6018, 0x0000 }, + { 0x6019, 0x0000 }, + { 0x601a, 0x0000 }, + { 0x601b, 0x0000 }, + { 0x601c, 0x0000 }, + { 0x601d, 0x0000 }, + { 0x601e, 0x0000 }, + { 0x601f, 0x0000 }, + { 0x6020, 0x0000 }, + { 0x6021, 0x0000 }, + { 0x6022, 0x0000 }, + { 0x6023, 0x0000 }, + { 0x6024, 0x0000 }, + { 0x6025, 0x0000 }, + { 0x6026, 0x0000 }, + { 0x6027, 0x0000 }, + { 0x6028, 0x0000 }, + { 0x6029, 0x0000 }, + { 0x602a, 0x0000 }, + { 0x602b, 0x0000 }, + { 0x602c, 0x0000 }, + { 0x602d, 0x0000 }, + { 0x602e, 0x0000 }, + { 0x602f, 0x0000 }, + { 0x6030, 0x0000 }, + { 0x6031, 0x0000 }, + { 0x6032, 0x0000 }, + { 0x6033, 0x0000 }, + { 0x6034, 0x0000 }, + { 0x6035, 0x0000 }, + { 0x6036, 0x0000 }, + { 0x6037, 0x0000 }, + { 0x6038, 0x0000 }, + { 0x6039, 0x0000 }, + { 0x603a, 0x0000 }, + { 0x603b, 0x0000 }, + { 0x603c, 0x0000 }, + { 0x603d, 0x0000 }, + { 0x603e, 0x0000 }, + { 0x603f, 0x0000 }, + { 0x6040, 0x0000 }, + { 0x6041, 0x0000 }, + { 0x6042, 0x0000 }, + { 0x6043, 0x0000 }, + { 0x6044, 0x0000 }, + { 0x6045, 0x0000 }, + { 0x6046, 0x0000 }, + { 0x6047, 0x0000 }, + { 0x6048, 0x0000 }, + { 0x6049, 0x0000 }, + { 0x604a, 0x0000 }, + { 0x604b, 0x0000 }, + { 0x604c, 0x0000 }, + { 0x604d, 0x0000 }, + { 0x604e, 0x0000 }, + { 0x604f, 0x0000 }, + { 0x6050, 0x0000 }, + { 0x6051, 0x0000 }, + { 0x6052, 0x0000 }, + { 0x6053, 0x0000 }, + { 0x6054, 0x0000 }, + { 0x6055, 0x0000 }, + { 0x6056, 0x0000 }, + { 0x6057, 0x0000 }, + { 0x6058, 0x0000 }, + { 0x6059, 0x0000 }, + { 0x605a, 0x0000 }, + { 0x605b, 0x0000 }, + { 0x605c, 0x0000 }, + { 0x605d, 0x0000 }, + { 0x605e, 0x0000 }, + { 0x605f, 0x0000 }, + { 0x6060, 0x0000 }, + { 0x6061, 0x0000 }, + { 0x6062, 0x0000 }, + { 0x6063, 0x0000 }, + { 0x6064, 0x0000 }, + { 0x6065, 0x0000 }, + { 0x6066, 0x0000 }, + { 0x6067, 0x0000 }, + { 0x6068, 0x0000 }, + { 0x6069, 0x0000 }, + { 0x606a, 0x0000 }, + { 0x606b, 0x0000 }, + { 0x606c, 0x0000 }, + { 0x606d, 0x0000 }, + { 0x606e, 0x0000 }, + { 0x606f, 0x0000 }, + { 0x6070, 0x0000 }, + { 0x6071, 0x0000 }, + { 0x6072, 0x0000 }, + { 0x6073, 0x0000 }, + { 0x6074, 0x0000 }, + { 0x6075, 0x0000 }, + { 0x6076, 0x0000 }, + { 0x6077, 0x0000 }, + { 0x6078, 0x0000 }, + { 0x6079, 0x0000 }, + { 0x607a, 0x0000 }, + { 0x607b, 0x0000 }, + { 0x607c, 0x0000 }, + { 0x607d, 0x0000 }, + { 0x607e, 0x0000 }, + { 0x607f, 0x0000 }, + { 0x6080, 0x0000 }, + { 0x6081, 0x0000 }, + { 0x6082, 0x0000 }, + { 0x6083, 0x0000 }, + { 0x6084, 0x0000 }, + { 0x6085, 0x0000 }, + { 0x6086, 0x0000 }, + { 0x6087, 0x0000 }, + { 0x6088, 0x0000 }, + { 0x6089, 0x0000 }, + { 0x608a, 0x0000 }, + { 0x608b, 0x0000 }, + { 0x608c, 0x0000 }, + { 0x608d, 0x0000 }, + { 0x608e, 0x0000 }, + { 0x608f, 0x0000 }, + { 0x6090, 0x0000 }, + { 0x6091, 0x0000 }, + { 0x6092, 0x0000 }, + { 0x6093, 0x0000 }, + { 0x6094, 0x0000 }, + { 0x6095, 0x0000 }, + { 0x6096, 0x0000 }, + { 0x6097, 0x0000 }, + { 0x6098, 0x0000 }, + { 0x6099, 0x0000 }, + { 0x609a, 0x0000 }, + { 0x609b, 0x0000 }, + { 0x609c, 0x0000 }, + { 0x609d, 0x0000 }, + { 0x609e, 0x0000 }, + { 0x609f, 0x0000 }, + { 0x60a0, 0x0000 }, + { 0x60a1, 0x0000 }, + { 0x60a2, 0x0000 }, + { 0x60a3, 0x0000 }, + { 0x60a4, 0x0000 }, + { 0x60a5, 0x0000 }, + { 0x60a6, 0x0000 }, + { 0x60a7, 0x0000 }, + { 0x60a8, 0x0000 }, + { 0x60a9, 0x0000 }, + { 0x60aa, 0x0000 }, + { 0x60ab, 0x0000 }, + { 0x60ac, 0x0000 }, + { 0x60ad, 0x0000 }, + { 0x60ae, 0x0000 }, + { 0x60af, 0x0000 }, + { 0x60b0, 0x0000 }, + { 0x60b1, 0x0000 }, + { 0x60b2, 0x0000 }, + { 0x60b3, 0x0000 }, + { 0x60b4, 0x0000 }, + { 0x60b5, 0x0000 }, + { 0x60b6, 0x0000 }, + { 0x60b7, 0x0000 }, + { 0x60b8, 0x0000 }, + { 0x60b9, 0x0000 }, + { 0x60ba, 0x0000 }, + { 0x60bb, 0x0000 }, + { 0x60bc, 0x0000 }, + { 0x60bd, 0x0000 }, + { 0x60be, 0x0000 }, + { 0x60bf, 0x0000 }, + { 0x60c0, 0x0000 }, + { 0x60c1, 0x0000 }, + { 0x60c2, 0x0000 }, + { 0x60c3, 0x0000 }, + { 0x60c4, 0x0000 }, + { 0x60c5, 0x0000 }, + { 0x60c6, 0x0000 }, + { 0x60c7, 0x0000 }, + { 0x60c8, 0x0000 }, + { 0x60c9, 0x0000 }, + { 0x60ca, 0x0000 }, + { 0x60cb, 0x0000 }, + { 0x60cc, 0x0000 }, + { 0x60cd, 0x0000 }, + { 0x60ce, 0x0000 }, + { 0x60cf, 0x0000 }, + { 0x60d0, 0x0000 }, + { 0x60d1, 0x0000 }, + { 0x60d2, 0x0000 }, + { 0x60d3, 0x0000 }, + { 0x60d4, 0x0000 }, + { 0x60d5, 0x0000 }, + { 0x60d6, 0x0000 }, + { 0x60d7, 0x0000 }, + { 0x60d8, 0x0000 }, + { 0x60d9, 0x0000 }, + { 0x60da, 0x0000 }, + { 0x60db, 0x0000 }, + { 0x60dc, 0x0000 }, + { 0x60dd, 0x0000 }, + { 0x60de, 0x0000 }, + { 0x60df, 0x0000 }, + { 0x60e0, 0x0000 }, + { 0x60e1, 0x0000 }, + { 0x60e2, 0x0000 }, + { 0x60e3, 0x0000 }, + { 0x60e4, 0x0000 }, + { 0x60e5, 0x0000 }, + { 0x60e6, 0x0000 }, + { 0x60e7, 0x0000 }, + { 0x60e8, 0x0000 }, + { 0x60e9, 0x0000 }, + { 0x60ea, 0x0000 }, + { 0x60eb, 0x0000 }, + { 0x60ec, 0x0000 }, + { 0x60ed, 0x0000 }, + { 0x60ee, 0x0000 }, + { 0x60ef, 0x0000 }, + { 0x60f0, 0x0000 }, + { 0x60f1, 0x0000 }, + { 0x60f2, 0x0000 }, + { 0x60f3, 0x0000 }, + { 0x60f4, 0x0000 }, + { 0x60f5, 0x0000 }, + { 0x60f6, 0x0000 }, + { 0x60f7, 0x0000 }, + { 0x60f8, 0x0000 }, + { 0x60f9, 0x0000 }, + { 0x60fa, 0x0000 }, + { 0x60fb, 0x0000 }, + { 0x60fc, 0x0000 }, + { 0x60fd, 0x0000 }, + { 0x60fe, 0x0000 }, + { 0x60ff, 0x0000 }, + { 0x6100, 0x0000 }, + { 0x6101, 0x0000 }, + { 0x6102, 0x0000 }, + { 0x6103, 0x0000 }, + { 0x6104, 0x0000 }, + { 0x6105, 0x0000 }, + { 0x6106, 0x0000 }, + { 0x6107, 0x0000 }, + { 0x6108, 0x0000 }, + { 0x6109, 0x0000 }, + { 0x610a, 0x0000 }, + { 0x610b, 0x0000 }, + { 0x610c, 0x0000 }, + { 0x610d, 0x0000 }, + { 0x610e, 0x0000 }, + { 0x610f, 0x0000 }, + { 0x6110, 0x0000 }, + { 0x6111, 0x0000 }, + { 0x6112, 0x0000 }, + { 0x6113, 0x0000 }, + { 0x6114, 0x0000 }, + { 0x6115, 0x0000 }, + { 0x6116, 0x0000 }, + { 0x6117, 0x0000 }, + { 0x6118, 0x0000 }, + { 0x6119, 0x0000 }, + { 0x611a, 0x0000 }, + { 0x611b, 0x0000 }, + { 0x611c, 0x0000 }, + { 0x611d, 0x0000 }, + { 0x611e, 0x0000 }, + { 0x611f, 0x0000 }, + { 0x6120, 0x0000 }, + { 0x6121, 0x0000 }, + { 0x6122, 0x0000 }, + { 0x6123, 0x0000 }, + { 0x6124, 0x0000 }, + { 0x6125, 0x0000 }, + { 0x6126, 0x0000 }, + { 0x6127, 0x0000 }, + { 0x6128, 0x0000 }, + { 0x6129, 0x0000 }, + { 0x612a, 0x0000 }, + { 0x612b, 0x0000 }, + { 0x612c, 0x0000 }, + { 0x612d, 0x0000 }, + { 0x612e, 0x0000 }, + { 0x612f, 0x0000 }, + { 0x6130, 0x0000 }, + { 0x6131, 0x0000 }, + { 0x6132, 0x0000 }, + { 0x6133, 0x0000 }, + { 0x6134, 0x0000 }, + { 0x6135, 0x0000 }, + { 0x6136, 0x0000 }, + { 0x6137, 0x0000 }, + { 0x6138, 0x0000 }, + { 0x6139, 0x0000 }, + { 0x613a, 0x0000 }, + { 0x613b, 0x0000 }, + { 0x613c, 0x0000 }, + { 0x613d, 0x0000 }, + { 0x613e, 0x0000 }, + { 0x613f, 0x0000 }, + { 0x6140, 0x0000 }, + { 0x6141, 0x0000 }, + { 0x6142, 0x0000 }, + { 0x6143, 0x0000 }, + { 0x6144, 0x0000 }, + { 0x6145, 0x0000 }, + { 0x6146, 0x0000 }, + { 0x6147, 0x0000 }, + { 0x6148, 0x0000 }, + { 0x6149, 0x0000 }, + { 0x614a, 0x0000 }, + { 0x614b, 0x0000 }, + { 0x614c, 0x0000 }, + { 0x614d, 0x0000 }, + { 0x614e, 0x0000 }, + { 0x614f, 0x0000 }, + { 0x6150, 0x0000 }, + { 0x6151, 0x0000 }, + { 0x6152, 0x0000 }, + { 0x6153, 0x0000 }, + { 0x6154, 0x0000 }, + { 0x6155, 0x0000 }, + { 0x6156, 0x0000 }, + { 0x6157, 0x0000 }, + { 0x6158, 0x0000 }, + { 0x6159, 0x0000 }, + { 0x615a, 0x0000 }, + { 0x615b, 0x0000 }, + { 0x615c, 0x0000 }, + { 0x615d, 0x0000 }, + { 0x615e, 0x0000 }, + { 0x615f, 0x0000 }, + { 0x6160, 0x0000 }, + { 0x6161, 0x0000 }, + { 0x6162, 0x0000 }, + { 0x6163, 0x0000 }, + { 0x6164, 0x0000 }, + { 0x6165, 0x0000 }, + { 0x6166, 0x0000 }, + { 0x6167, 0x0000 }, + { 0x6168, 0x0000 }, + { 0x6169, 0x0000 }, + { 0x616a, 0x0000 }, + { 0x616b, 0x0000 }, + { 0x616c, 0x0000 }, + { 0x616d, 0x0000 }, + { 0x616e, 0x0000 }, + { 0x616f, 0x0000 }, + { 0x6170, 0x0000 }, + { 0x6171, 0x0000 }, + { 0x6172, 0x0000 }, + { 0x6173, 0x0000 }, + { 0x6174, 0x0000 }, + { 0x6175, 0x0000 }, + { 0x6176, 0x0000 }, + { 0x6177, 0x0000 }, + { 0x6178, 0x0000 }, + { 0x6179, 0x0000 }, + { 0x617a, 0x0000 }, + { 0x617b, 0x0000 }, + { 0x617c, 0x0000 }, + { 0x617d, 0x0000 }, + { 0x617e, 0x0000 }, + { 0x617f, 0x0000 }, + { 0x6180, 0x0000 }, + { 0x6181, 0x0000 }, + { 0x6182, 0x0000 }, + { 0x6183, 0x0000 }, + { 0x6184, 0x0000 }, + { 0x6185, 0x0000 }, + { 0x6186, 0x0000 }, + { 0x6187, 0x0000 }, + { 0x6188, 0x0000 }, + { 0x6189, 0x0000 }, + { 0x618a, 0x0000 }, + { 0x618b, 0x0000 }, + { 0x618c, 0x0000 }, + { 0x618d, 0x0000 }, + { 0x618e, 0x0000 }, + { 0x618f, 0x0000 }, + { 0x6190, 0x0000 }, + { 0x6191, 0x0000 }, + { 0x6192, 0x0000 }, + { 0x6193, 0x0000 }, + { 0x6194, 0x0000 }, + { 0x6195, 0x0000 }, + { 0x6196, 0x0000 }, + { 0x6197, 0x0000 }, + { 0x6198, 0x0000 }, + { 0x6199, 0x0000 }, + { 0x619a, 0x0000 }, + { 0x619b, 0x0000 }, + { 0x619c, 0x0000 }, + { 0x619d, 0x0000 }, + { 0x619e, 0x0000 }, + { 0x619f, 0x0000 }, + { 0x61a0, 0x0000 }, + { 0x61a1, 0x0000 }, + { 0x61a2, 0x0000 }, + { 0x61a3, 0x0000 }, + { 0x61a4, 0x0000 }, + { 0x61a5, 0x0000 }, + { 0x61a6, 0x0000 }, + { 0x61a7, 0x0000 }, + { 0x61a8, 0x0000 }, + { 0x61a9, 0x0000 }, + { 0x61aa, 0x0000 }, + { 0x61ab, 0x0000 }, + { 0x61ac, 0x0000 }, + { 0x61ad, 0x0000 }, + { 0x61ae, 0x0000 }, + { 0x61af, 0x0000 }, + { 0x61b0, 0x0000 }, + { 0x61b1, 0x0000 }, + { 0x61b2, 0x0000 }, + { 0x61b3, 0x0000 }, + { 0x61b4, 0x0000 }, + { 0x61b5, 0x0000 }, + { 0x61b6, 0x0000 }, + { 0x61b7, 0x0000 }, + { 0x61b8, 0x0000 }, + { 0x61b9, 0x0000 }, + { 0x61ba, 0x0000 }, + { 0x61bb, 0x0000 }, + { 0x61bc, 0x0000 }, + { 0x61bd, 0x0000 }, + { 0x61be, 0x0000 }, + { 0x61bf, 0x0000 }, + { 0x61c0, 0x0000 }, + { 0x61c1, 0x0000 }, + { 0x61c2, 0x0000 }, + { 0x61c3, 0x0000 }, + { 0x61c4, 0x0000 }, + { 0x61c5, 0x0000 }, + { 0x61c6, 0x0000 }, + { 0x61c7, 0x0000 }, + { 0x61c8, 0x0000 }, + { 0x61c9, 0x0000 }, + { 0x61ca, 0x0000 }, + { 0x61cb, 0x0000 }, + { 0x61cc, 0x0000 }, + { 0x61cd, 0x0000 }, + { 0x61ce, 0x0000 }, + { 0x61cf, 0x0000 }, + { 0x61d0, 0x0000 }, + { 0x61d1, 0x0000 }, + { 0x61d2, 0x0000 }, + { 0x61d3, 0x0000 }, + { 0x61d4, 0x0000 }, + { 0x61d5, 0x0000 }, + { 0x61d6, 0x0000 }, + { 0x61d7, 0x0000 }, + { 0x61d8, 0x0000 }, + { 0x61d9, 0x0000 }, + { 0x61da, 0x0000 }, + { 0x61db, 0x0000 }, + { 0x61dc, 0x0000 }, + { 0x61dd, 0x0000 }, + { 0x61de, 0x0000 }, + { 0x61df, 0x0000 }, + { 0x61e0, 0x0000 }, + { 0x61e1, 0x0000 }, + { 0x61e2, 0x0000 }, + { 0x61e3, 0x0000 }, + { 0x61e4, 0x0000 }, + { 0x61e5, 0x0000 }, + { 0x61e6, 0x0000 }, + { 0x61e7, 0x0000 }, + { 0x61e8, 0x0000 }, + { 0x61e9, 0x0000 }, + { 0x61ea, 0x0000 }, + { 0x61eb, 0x0000 }, + { 0x61ec, 0x0000 }, + { 0x61ed, 0x0000 }, + { 0x61ee, 0x0000 }, + { 0x61ef, 0x0000 }, + { 0x61f0, 0x0000 }, + { 0x61f1, 0x0000 }, + { 0x61f2, 0x0000 }, + { 0x61f3, 0x0000 }, + { 0x61f4, 0x0000 }, + { 0x61f5, 0x0000 }, + { 0x61f6, 0x0000 }, + { 0x61f7, 0x0000 }, + { 0x61f8, 0x0000 }, + { 0x61f9, 0x0000 }, + { 0x61fa, 0x0000 }, + { 0x61fb, 0x0000 }, + { 0x61fc, 0x0000 }, + { 0x61fd, 0x0000 }, + { 0x61fe, 0x0000 }, + { 0x61ff, 0x0000 }, + { 0x6200, 0x0000 }, + { 0x6201, 0x0000 }, + { 0x6202, 0x0000 }, + { 0x6203, 0x0000 }, + { 0x6204, 0x0000 }, + { 0x6205, 0x0000 }, + { 0x6206, 0x0000 }, + { 0x6207, 0x0000 }, + { 0x6208, 0x0000 }, + { 0x6209, 0x0000 }, + { 0x620a, 0x0000 }, + { 0x620b, 0x0000 }, + { 0x620c, 0x0000 }, + { 0x620d, 0x0000 }, + { 0x620e, 0x0000 }, + { 0x620f, 0x0000 }, + { 0x6210, 0x0000 }, + { 0x6211, 0x0000 }, + { 0x6212, 0x0000 }, + { 0x6213, 0x0000 }, + { 0x6214, 0x0000 }, + { 0x6215, 0x0000 }, + { 0x6216, 0x0000 }, + { 0x6217, 0x0000 }, + { 0x6218, 0x0000 }, + { 0x6219, 0x0000 }, + { 0x621a, 0x0000 }, + { 0x621b, 0x0000 }, + { 0x621c, 0x0000 }, + { 0x621d, 0x0000 }, + { 0x621e, 0x0000 }, + { 0x621f, 0x0000 }, + { 0x6220, 0x0000 }, + { 0x6221, 0x0000 }, + { 0x6222, 0x0000 }, + { 0x6223, 0x0000 }, + { 0x6224, 0x0000 }, + { 0x6225, 0x0000 }, + { 0x6226, 0x0000 }, + { 0x6227, 0x0000 }, + { 0x6228, 0x0000 }, + { 0x6229, 0x0000 }, + { 0x622a, 0x0000 }, + { 0x622b, 0x0000 }, + { 0x622c, 0x0000 }, + { 0x622d, 0x0000 }, + { 0x622e, 0x0000 }, + { 0x622f, 0x0000 }, + { 0x6230, 0x0000 }, + { 0x6231, 0x0000 }, + { 0x6232, 0x0000 }, + { 0x6233, 0x0000 }, + { 0x6234, 0x0000 }, + { 0x6235, 0x0000 }, + { 0x6236, 0x0000 }, + { 0x6237, 0x0000 }, + { 0x6238, 0x0000 }, + { 0x6239, 0x0000 }, + { 0x623a, 0x0000 }, + { 0x623b, 0x0000 }, + { 0x623c, 0x0000 }, + { 0x623d, 0x0000 }, + { 0x623e, 0x0000 }, + { 0x623f, 0x0000 }, + { 0x6240, 0x0000 }, + { 0x6241, 0x0000 }, + { 0x6242, 0x0000 }, + { 0x6243, 0x0000 }, + { 0x6244, 0x0000 }, + { 0x6245, 0x0000 }, + { 0x6246, 0x0000 }, + { 0x6247, 0x0000 }, + { 0x6248, 0x0000 }, + { 0x6249, 0x0000 }, + { 0x624a, 0x0000 }, + { 0x624b, 0x0000 }, + { 0x624c, 0x0000 }, + { 0x624d, 0x0000 }, + { 0x624e, 0x0000 }, + { 0x624f, 0x0000 }, + { 0x6250, 0x0000 }, + { 0x6251, 0x0000 }, + { 0x6252, 0x0000 }, + { 0x6253, 0x0000 }, + { 0x6254, 0x0000 }, + { 0x6255, 0x0000 }, + { 0x6256, 0x0000 }, + { 0x6257, 0x0000 }, + { 0x6258, 0x0000 }, + { 0x6259, 0x0000 }, + { 0x625a, 0x0000 }, + { 0x625b, 0x0000 }, + { 0x625c, 0x0000 }, + { 0x625d, 0x0000 }, + { 0x625e, 0x0000 }, + { 0x625f, 0x0000 }, + { 0x6260, 0x0000 }, + { 0x6261, 0x0000 }, + { 0x6262, 0x0000 }, + { 0x6263, 0x0000 }, + { 0x6264, 0x0000 }, + { 0x6265, 0x0000 }, + { 0x6266, 0x0000 }, + { 0x6267, 0x0000 }, + { 0x6268, 0x0000 }, + { 0x6269, 0x0000 }, + { 0x626a, 0x0000 }, + { 0x626b, 0x0000 }, + { 0x626c, 0x0000 }, + { 0x626d, 0x0000 }, + { 0x626e, 0x0000 }, + { 0x626f, 0x0000 }, + { 0x6270, 0x0000 }, + { 0x6271, 0x0000 }, + { 0x6272, 0x0000 }, + { 0x6273, 0x0000 }, + { 0x6274, 0x0000 }, + { 0x6275, 0x0000 }, + { 0x6276, 0x0000 }, + { 0x6277, 0x0000 }, + { 0x6278, 0x0000 }, + { 0x6279, 0x0000 }, + { 0x627a, 0x0000 }, + { 0x627b, 0x0000 }, + { 0x627c, 0x0000 }, + { 0x627d, 0x0000 }, + { 0x627e, 0x0000 }, + { 0x627f, 0x0000 }, + { 0x6280, 0x0000 }, + { 0x6281, 0x0000 }, + { 0x6282, 0x0000 }, + { 0x6283, 0x0000 }, + { 0x6284, 0x0000 }, + { 0x6285, 0x0000 }, + { 0x6286, 0x0000 }, + { 0x6287, 0x0000 }, + { 0x6288, 0x0000 }, + { 0x6289, 0x0000 }, + { 0x628a, 0x0000 }, + { 0x628b, 0x0000 }, + { 0x628c, 0x0000 }, + { 0x628d, 0x0000 }, + { 0x628e, 0x0000 }, + { 0x628f, 0x0000 }, + { 0x6290, 0x0000 }, + { 0x6291, 0x0000 }, + { 0x6292, 0x0000 }, + { 0x6293, 0x0000 }, + { 0x6294, 0x0000 }, + { 0x6295, 0x0000 }, + { 0x6296, 0x0000 }, + { 0x6297, 0x0000 }, + { 0x6298, 0x0000 }, + { 0x6299, 0x0000 }, + { 0x629a, 0x0000 }, + { 0x629b, 0x0000 }, + { 0x629c, 0x0000 }, + { 0x629d, 0x0000 }, + { 0x629e, 0x0000 }, + { 0x629f, 0x0000 }, + { 0x62a0, 0x0000 }, + { 0x62a1, 0x0000 }, + { 0x62a2, 0x0000 }, + { 0x62a3, 0x0000 }, + { 0x62a4, 0x0000 }, + { 0x62a5, 0x0000 }, + { 0x62a6, 0x0000 }, + { 0x62a7, 0x0000 }, + { 0x62a8, 0x0000 }, + { 0x62a9, 0x0000 }, + { 0x62aa, 0x0000 }, + { 0x62ab, 0x0000 }, + { 0x62ac, 0x0000 }, + { 0x62ad, 0x0000 }, + { 0x62ae, 0x0000 }, + { 0x62af, 0x0000 }, + { 0x62b0, 0x0000 }, + { 0x62b1, 0x0000 }, + { 0x62b2, 0x0000 }, + { 0x62b3, 0x0000 }, + { 0x62b4, 0x0000 }, + { 0x62b5, 0x0000 }, + { 0x62b6, 0x0000 }, + { 0x62b7, 0x0000 }, + { 0x62b8, 0x0000 }, + { 0x62b9, 0x0000 }, + { 0x62ba, 0x0000 }, + { 0x62bb, 0x0000 }, + { 0x62bc, 0x0000 }, + { 0x62bd, 0x0000 }, + { 0x62be, 0x0000 }, + { 0x62bf, 0x0000 }, + { 0x62c0, 0x0000 }, + { 0x62c1, 0x0000 }, + { 0x62c2, 0x0000 }, + { 0x62c3, 0x0000 }, + { 0x62c4, 0x0000 }, + { 0x62c5, 0x0000 }, + { 0x62c6, 0x0000 }, + { 0x62c7, 0x0000 }, + { 0x62c8, 0x0000 }, + { 0x62c9, 0x0000 }, + { 0x62ca, 0x0000 }, + { 0x62cb, 0x0000 }, + { 0x62cc, 0x0000 }, + { 0x62cd, 0x0000 }, + { 0x62ce, 0x0000 }, + { 0x62cf, 0x0000 }, + { 0x62d0, 0x0000 }, + { 0x62d1, 0x0000 }, + { 0x62d2, 0x0000 }, + { 0x62d3, 0x0000 }, + { 0x62d4, 0x0000 }, + { 0x62d5, 0x0000 }, + { 0x62d6, 0x0000 }, + { 0x62d7, 0x0000 }, + { 0x62d8, 0x0000 }, + { 0x62d9, 0x0000 }, + { 0x62da, 0x0000 }, + { 0x62db, 0x0000 }, + { 0x62dc, 0x0000 }, + { 0x62dd, 0x0000 }, + { 0x62de, 0x0000 }, + { 0x62df, 0x0000 }, + { 0x62e0, 0x0000 }, + { 0x62e1, 0x0000 }, + { 0x62e2, 0x0000 }, + { 0x62e3, 0x0000 }, + { 0x62e4, 0x0000 }, + { 0x62e5, 0x0000 }, + { 0x62e6, 0x0000 }, + { 0x62e7, 0x0000 }, + { 0x62e8, 0x0000 }, + { 0x62e9, 0x0000 }, + { 0x62ea, 0x0000 }, + { 0x62eb, 0x0000 }, + { 0x62ec, 0x0000 }, + { 0x62ed, 0x0000 }, + { 0x62ee, 0x0000 }, + { 0x62ef, 0x0000 }, + { 0x62f0, 0x0000 }, + { 0x62f1, 0x0000 }, + { 0x62f2, 0x0000 }, + { 0x62f3, 0x0000 }, + { 0x62f4, 0x0000 }, + { 0x62f5, 0x0000 }, + { 0x62f6, 0x0000 }, + { 0x62f7, 0x0000 }, + { 0x62f8, 0x0000 }, + { 0x62f9, 0x0000 }, + { 0x62fa, 0x0000 }, + { 0x62fb, 0x0000 }, + { 0x62fc, 0x0000 }, + { 0x62fd, 0x0000 }, + { 0x62fe, 0x0000 }, + { 0x62ff, 0x0000 }, + { 0x6300, 0x0000 }, + { 0x6301, 0x0000 }, + { 0x6302, 0x0000 }, + { 0x6303, 0x0000 }, + { 0x6304, 0x0000 }, + { 0x6305, 0x0000 }, + { 0x6306, 0x0000 }, + { 0x6307, 0x0000 }, + { 0x6308, 0x0000 }, + { 0x6309, 0x0000 }, + { 0x630a, 0x0000 }, + { 0x630b, 0x0000 }, + { 0x630c, 0x0000 }, + { 0x630d, 0x0000 }, + { 0x630e, 0x0000 }, + { 0x630f, 0x0000 }, + { 0x6310, 0x0000 }, + { 0x6311, 0x0000 }, + { 0x6312, 0x0000 }, + { 0x6313, 0x0000 }, + { 0x6314, 0x0000 }, + { 0x6315, 0x0000 }, + { 0x6316, 0x0000 }, + { 0x6317, 0x0000 }, + { 0x6318, 0x0000 }, + { 0x6319, 0x0000 }, + { 0x631a, 0x0000 }, + { 0x631b, 0x0000 }, + { 0x631c, 0x0000 }, + { 0x631d, 0x0000 }, + { 0x631e, 0x0000 }, + { 0x631f, 0x0000 }, + { 0x6320, 0x0000 }, + { 0x6321, 0x0000 }, + { 0x6322, 0x0000 }, + { 0x6323, 0x0000 }, + { 0x6324, 0x0000 }, + { 0x6325, 0x0000 }, + { 0x6326, 0x0000 }, + { 0x6327, 0x0000 }, + { 0x6328, 0x0000 }, + { 0x6329, 0x0000 }, + { 0x632a, 0x0000 }, + { 0x632b, 0x0000 }, + { 0x632c, 0x0000 }, + { 0x632d, 0x0000 }, + { 0x632e, 0x0000 }, + { 0x632f, 0x0000 }, + { 0x6330, 0x0000 }, + { 0x6331, 0x0000 }, + { 0x6332, 0x0000 }, + { 0x6333, 0x0000 }, + { 0x6334, 0x0000 }, + { 0x6335, 0x0000 }, + { 0x6336, 0x0000 }, + { 0x6337, 0x0000 }, + { 0x6338, 0x0000 }, + { 0x6339, 0x0000 }, + { 0x633a, 0x0000 }, + { 0x633b, 0x0000 }, + { 0x633c, 0x0000 }, + { 0x633d, 0x0000 }, + { 0x633e, 0x0000 }, + { 0x633f, 0x0000 }, + { 0x6340, 0x0000 }, + { 0x6341, 0x0000 }, + { 0x6342, 0x0000 }, + { 0x6343, 0x0000 }, + { 0x6344, 0x0000 }, + { 0x6345, 0x0000 }, + { 0x6346, 0x0000 }, + { 0x6347, 0x0000 }, + { 0x6348, 0x0000 }, + { 0x6349, 0x0000 }, + { 0x634a, 0x0000 }, + { 0x634b, 0x0000 }, + { 0x634c, 0x0000 }, + { 0x634d, 0x0000 }, + { 0x634e, 0x0000 }, + { 0x634f, 0x0000 }, + { 0x6350, 0x0000 }, + { 0x6351, 0x0000 }, + { 0x6352, 0x0000 }, + { 0x6353, 0x0000 }, + { 0x6354, 0x0000 }, + { 0x6355, 0x0000 }, + { 0x6356, 0x0000 }, + { 0x6357, 0x0000 }, + { 0x6358, 0x0000 }, + { 0x6359, 0x0000 }, + { 0x635a, 0x0000 }, + { 0x635b, 0x0000 }, + { 0x635c, 0x0000 }, + { 0x635d, 0x0000 }, + { 0x635e, 0x0000 }, + { 0x635f, 0x0000 }, + { 0x6360, 0x0000 }, + { 0x6361, 0x0000 }, + { 0x6362, 0x0000 }, + { 0x6363, 0x0000 }, + { 0x6364, 0x0000 }, + { 0x6365, 0x0000 }, + { 0x6366, 0x0000 }, + { 0x6367, 0x0000 }, + { 0x6368, 0x0000 }, + { 0x6369, 0x0000 }, + { 0x636a, 0x0000 }, + { 0x636b, 0x0000 }, + { 0x636c, 0x0000 }, + { 0x636d, 0x0000 }, + { 0x636e, 0x0000 }, + { 0x636f, 0x0000 }, + { 0x6370, 0x0000 }, + { 0x6371, 0x0000 }, + { 0x6372, 0x0000 }, + { 0x6373, 0x0000 }, + { 0x6374, 0x0000 }, + { 0x6375, 0x0000 }, + { 0x6376, 0x0000 }, + { 0x6377, 0x0000 }, + { 0x6378, 0x0000 }, + { 0x6379, 0x0000 }, + { 0x637a, 0x0000 }, + { 0x637b, 0x0000 }, + { 0x637c, 0x0000 }, + { 0x637d, 0x0000 }, + { 0x637e, 0x0000 }, + { 0x637f, 0x0000 }, + { 0x6380, 0x0000 }, + { 0x6381, 0x0000 }, + { 0x6382, 0x0000 }, + { 0x6383, 0x0000 }, + { 0x6384, 0x0000 }, + { 0x6385, 0x0000 }, + { 0x6386, 0x0000 }, + { 0x6387, 0x0000 }, + { 0x6388, 0x0000 }, + { 0x6389, 0x0000 }, + { 0x638a, 0x0000 }, + { 0x638b, 0x0000 }, + { 0x638c, 0x0000 }, + { 0x638d, 0x0000 }, + { 0x638e, 0x0000 }, + { 0x638f, 0x0000 }, + { 0x6390, 0x0000 }, + { 0x6391, 0x0000 }, + { 0x6392, 0x0000 }, + { 0x6393, 0x0000 }, + { 0x6394, 0x0000 }, + { 0x6395, 0x0000 }, + { 0x6396, 0x0000 }, + { 0x6397, 0x0000 }, + { 0x6398, 0x0000 }, + { 0x6399, 0x0000 }, + { 0x639a, 0x0000 }, + { 0x639b, 0x0000 }, + { 0x639c, 0x0000 }, + { 0x639d, 0x0000 }, + { 0x639e, 0x0000 }, + { 0x639f, 0x0000 }, + { 0x63a0, 0x0000 }, + { 0x63a1, 0x0000 }, + { 0x63a2, 0x0000 }, + { 0x63a3, 0x0000 }, + { 0x63a4, 0x0000 }, + { 0x63a5, 0x0000 }, + { 0x63a6, 0x0000 }, + { 0x63a7, 0x0000 }, + { 0x63a8, 0x0000 }, + { 0x63a9, 0x0000 }, + { 0x63aa, 0x0000 }, + { 0x63ab, 0x0000 }, + { 0x63ac, 0x0000 }, + { 0x63ad, 0x0000 }, + { 0x63ae, 0x0000 }, + { 0x63af, 0x0000 }, + { 0x63b0, 0x0000 }, + { 0x63b1, 0x0000 }, + { 0x63b2, 0x0000 }, + { 0x63b3, 0x0000 }, + { 0x63b4, 0x0000 }, + { 0x63b5, 0x0000 }, + { 0x63b6, 0x0000 }, + { 0x63b7, 0x0000 }, + { 0x63b8, 0x0000 }, + { 0x63b9, 0x0000 }, + { 0x63ba, 0x0000 }, + { 0x63bb, 0x0000 }, + { 0x63bc, 0x0000 }, + { 0x63bd, 0x0000 }, + { 0x63be, 0x0000 }, + { 0x63bf, 0x0000 }, + { 0x63c0, 0x0000 }, + { 0x63c1, 0x0000 }, + { 0x63c2, 0x0000 }, + { 0x63c3, 0x0000 }, + { 0x63c4, 0x0000 }, + { 0x63c5, 0x0000 }, + { 0x63c6, 0x0000 }, + { 0x63c7, 0x0000 }, + { 0x63c8, 0x0000 }, + { 0x63c9, 0x0000 }, + { 0x63ca, 0x0000 }, + { 0x63cb, 0x0000 }, + { 0x63cc, 0x0000 }, + { 0x63cd, 0x0000 }, + { 0x63ce, 0x0000 }, + { 0x63cf, 0x0000 }, + { 0x63d0, 0x0000 }, + { 0x63d1, 0x0000 }, + { 0x63d2, 0x0000 }, + { 0x63d3, 0x0000 }, + { 0x63d4, 0x0000 }, + { 0x63d5, 0x0000 }, + { 0x63d6, 0x0000 }, + { 0x63d7, 0x0000 }, + { 0x63d8, 0x0000 }, + { 0x63d9, 0x0000 }, + { 0x63da, 0x0000 }, + { 0x63db, 0x0000 }, + { 0x63dc, 0x0000 }, + { 0x63dd, 0x0000 }, + { 0x63de, 0x0000 }, + { 0x63df, 0x0000 }, + { 0x63e0, 0x0000 }, + { 0x63e1, 0x0000 }, + { 0x63e2, 0x0000 }, + { 0x63e3, 0x0000 }, + { 0x63e4, 0x0000 }, + { 0x63e5, 0x0000 }, + { 0x63e6, 0x0000 }, + { 0x63e7, 0x0000 }, + { 0x63e8, 0x0000 }, + { 0x63e9, 0x0000 }, + { 0x63ea, 0x0000 }, + { 0x63eb, 0x0000 }, + { 0x63ec, 0x0000 }, + { 0x63ed, 0x0000 }, + { 0x63ee, 0x0000 }, + { 0x63ef, 0x0000 }, + { 0x63f0, 0x0000 }, + { 0x63f1, 0x0000 }, + { 0x63f2, 0x0000 }, + { 0x63f3, 0x0000 }, + { 0x63f4, 0x0000 }, + { 0x63f5, 0x0000 }, + { 0x63f6, 0x0000 }, + { 0x63f7, 0x0000 }, + { 0x63f8, 0x0000 }, + { 0x63f9, 0x0000 }, + { 0x63fa, 0x0000 }, + { 0x63fb, 0x0000 }, + { 0x63fc, 0x0000 }, + { 0x63fd, 0x0000 }, + { 0x63fe, 0x0000 }, + { 0x63ff, 0x0000 }, + { 0x6400, 0x0000 }, + { 0x6401, 0x0000 }, + { 0x6402, 0x0000 }, + { 0x6403, 0x0000 }, + { 0x6404, 0x0000 }, + { 0x6405, 0x0000 }, + { 0x6406, 0x0000 }, + { 0x6407, 0x0000 }, + { 0x6408, 0x0000 }, + { 0x6409, 0x0000 }, + { 0x640a, 0x0000 }, + { 0x640b, 0x0000 }, + { 0x640c, 0x0000 }, + { 0x640d, 0x0000 }, + { 0x640e, 0x0000 }, + { 0x640f, 0x0000 }, + { 0x6410, 0x0000 }, + { 0x6411, 0x0000 }, + { 0x6412, 0x0000 }, + { 0x6413, 0x0000 }, + { 0x6414, 0x0000 }, + { 0x6415, 0x0000 }, + { 0x6416, 0x0000 }, + { 0x6417, 0x0000 }, + { 0x6418, 0x0000 }, + { 0x6419, 0x0000 }, + { 0x641a, 0x0000 }, + { 0x641b, 0x0000 }, + { 0x641c, 0x0000 }, + { 0x641d, 0x0000 }, + { 0x641e, 0x0000 }, + { 0x641f, 0x0000 }, + { 0x6420, 0x0000 }, + { 0x6421, 0x0000 }, + { 0x6422, 0x0000 }, + { 0x6423, 0x0000 }, + { 0x6424, 0x0000 }, + { 0x6425, 0x0000 }, + { 0x6426, 0x0000 }, + { 0x6427, 0x0000 }, + { 0x6428, 0x0000 }, + { 0x6429, 0x0000 }, + { 0x642a, 0x0000 }, + { 0x642b, 0x0000 }, + { 0x642c, 0x0000 }, + { 0x642d, 0x0000 }, + { 0x642e, 0x0000 }, + { 0x642f, 0x0000 }, + { 0x6430, 0x0000 }, + { 0x6431, 0x0000 }, + { 0x6432, 0x0000 }, + { 0x6433, 0x0000 }, + { 0x6434, 0x0000 }, + { 0x6435, 0x0000 }, + { 0x6436, 0x0000 }, + { 0x6437, 0x0000 }, + { 0x6438, 0x0000 }, + { 0x6439, 0x0000 }, + { 0x643a, 0x0000 }, + { 0x643b, 0x0000 }, + { 0x643c, 0x0000 }, + { 0x643d, 0x0000 }, + { 0x643e, 0x0000 }, + { 0x643f, 0x0000 }, + { 0x6440, 0x0000 }, + { 0x6441, 0x0000 }, + { 0x6442, 0x0000 }, + { 0x6443, 0x0000 }, + { 0x6444, 0x0000 }, + { 0x6445, 0x0000 }, + { 0x6446, 0x0000 }, + { 0x6447, 0x0000 }, + { 0x6448, 0x0000 }, + { 0x6449, 0x0000 }, + { 0x644a, 0x0000 }, + { 0x644b, 0x0000 }, + { 0x644c, 0x0000 }, + { 0x644d, 0x0000 }, + { 0x644e, 0x0000 }, + { 0x644f, 0x0000 }, + { 0x6450, 0x0000 }, + { 0x6451, 0x0000 }, + { 0x6452, 0x0000 }, + { 0x6453, 0x0000 }, + { 0x6454, 0x0000 }, + { 0x6455, 0x0000 }, + { 0x6456, 0x0000 }, + { 0x6457, 0x0000 }, + { 0x6458, 0x0000 }, + { 0x6459, 0x0000 }, + { 0x645a, 0x0000 }, + { 0x645b, 0x0000 }, + { 0x645c, 0x0000 }, + { 0x645d, 0x0000 }, + { 0x645e, 0x0000 }, + { 0x645f, 0x0000 }, + { 0x6460, 0x0000 }, + { 0x6461, 0x0000 }, + { 0x6462, 0x0000 }, + { 0x6463, 0x0000 }, + { 0x6464, 0x0000 }, + { 0x6465, 0x0000 }, + { 0x6466, 0x0000 }, + { 0x6467, 0x0000 }, + { 0x6468, 0x0000 }, + { 0x6469, 0x0000 }, + { 0x646a, 0x0000 }, + { 0x646b, 0x0000 }, + { 0x646c, 0x0000 }, + { 0x646d, 0x0000 }, + { 0x646e, 0x0000 }, + { 0x646f, 0x0000 }, + { 0x6470, 0x0000 }, + { 0x6471, 0x0000 }, + { 0x6472, 0x0000 }, + { 0x6473, 0x0000 }, + { 0x6474, 0x0000 }, + { 0x6475, 0x0000 }, + { 0x6476, 0x0000 }, + { 0x6477, 0x0000 }, + { 0x6478, 0x0000 }, + { 0x6479, 0x0000 }, + { 0x647a, 0x0000 }, + { 0x647b, 0x0000 }, + { 0x647c, 0x0000 }, + { 0x647d, 0x0000 }, + { 0x647e, 0x0000 }, + { 0x647f, 0x0000 }, + { 0x6480, 0x0000 }, + { 0x6481, 0x0000 }, + { 0x6482, 0x0000 }, + { 0x6483, 0x0000 }, + { 0x6484, 0x0000 }, + { 0x6485, 0x0000 }, + { 0x6486, 0x0000 }, + { 0x6487, 0x0000 }, + { 0x6488, 0x0000 }, + { 0x6489, 0x0000 }, + { 0x648a, 0x0000 }, + { 0x648b, 0x0000 }, + { 0x648c, 0x0000 }, + { 0x648d, 0x0000 }, + { 0x648e, 0x0000 }, + { 0x648f, 0x0000 }, + { 0x6490, 0x0000 }, + { 0x6491, 0x0000 }, + { 0x6492, 0x0000 }, + { 0x6493, 0x0000 }, + { 0x6494, 0x0000 }, + { 0x6495, 0x0000 }, + { 0x6496, 0x0000 }, + { 0x6497, 0x0000 }, + { 0x6498, 0x0000 }, + { 0x6499, 0x0000 }, + { 0x649a, 0x0000 }, + { 0x649b, 0x0000 }, + { 0x649c, 0x0000 }, + { 0x649d, 0x0000 }, + { 0x649e, 0x0000 }, + { 0x649f, 0x0000 }, + { 0x64a0, 0x0000 }, + { 0x64a1, 0x0000 }, + { 0x64a2, 0x0000 }, + { 0x64a3, 0x0000 }, + { 0x64a4, 0x0000 }, + { 0x64a5, 0x0000 }, + { 0x64a6, 0x0000 }, + { 0x64a7, 0x0000 }, + { 0x64a8, 0x0000 }, + { 0x64a9, 0x0000 }, + { 0x64aa, 0x0000 }, + { 0x64ab, 0x0000 }, + { 0x64ac, 0x0000 }, + { 0x64ad, 0x0000 }, + { 0x64ae, 0x0000 }, + { 0x64af, 0x0000 }, + { 0x64b0, 0x0000 }, + { 0x64b1, 0x0000 }, + { 0x64b2, 0x0000 }, + { 0x64b3, 0x0000 }, + { 0x64b4, 0x0000 }, + { 0x64b5, 0x0000 }, + { 0x64b6, 0x0000 }, + { 0x64b7, 0x0000 }, + { 0x64b8, 0x0000 }, + { 0x64b9, 0x0000 }, + { 0x64ba, 0x0000 }, + { 0x64bb, 0x0000 }, + { 0x64bc, 0x0000 }, + { 0x64bd, 0x0000 }, + { 0x64be, 0x0000 }, + { 0x64bf, 0x0000 }, + { 0x64c0, 0x0000 }, + { 0x64c1, 0x0000 }, + { 0x64c2, 0x0000 }, + { 0x64c3, 0x0000 }, + { 0x64c4, 0x0000 }, + { 0x64c5, 0x0000 }, + { 0x64c6, 0x0000 }, + { 0x64c7, 0x0000 }, + { 0x64c8, 0x0000 }, + { 0x64c9, 0x0000 }, + { 0x64ca, 0x0000 }, + { 0x64cb, 0x0000 }, + { 0x64cc, 0x0000 }, + { 0x64cd, 0x0000 }, + { 0x64ce, 0x0000 }, + { 0x64cf, 0x0000 }, + { 0x64d0, 0x0000 }, + { 0x64d1, 0x0000 }, + { 0x64d2, 0x0000 }, + { 0x64d3, 0x0000 }, + { 0x64d4, 0x0000 }, + { 0x64d5, 0x0000 }, + { 0x64d6, 0x0000 }, + { 0x64d7, 0x0000 }, + { 0x64d8, 0x0000 }, + { 0x64d9, 0x0000 }, + { 0x64da, 0x0000 }, + { 0x64db, 0x0000 }, + { 0x64dc, 0x0000 }, + { 0x64dd, 0x0000 }, + { 0x64de, 0x0000 }, + { 0x64df, 0x0000 }, + { 0x64e0, 0x0000 }, + { 0x64e1, 0x0000 }, + { 0x64e2, 0x0000 }, + { 0x64e3, 0x0000 }, + { 0x64e4, 0x0000 }, + { 0x64e5, 0x0000 }, + { 0x64e6, 0x0000 }, + { 0x64e7, 0x0000 }, + { 0x64e8, 0x0000 }, + { 0x64e9, 0x0000 }, + { 0x64ea, 0x0000 }, + { 0x64eb, 0x0000 }, + { 0x64ec, 0x0000 }, + { 0x64ed, 0x0000 }, + { 0x64ee, 0x0000 }, + { 0x64ef, 0x0000 }, + { 0x64f0, 0x0000 }, + { 0x64f1, 0x0000 }, + { 0x64f2, 0x0000 }, + { 0x64f3, 0x0000 }, + { 0x64f4, 0x0000 }, + { 0x64f5, 0x0000 }, + { 0x64f6, 0x0000 }, + { 0x64f7, 0x0000 }, + { 0x64f8, 0x0000 }, + { 0x64f9, 0x0000 }, + { 0x64fa, 0x0000 }, + { 0x64fb, 0x0000 }, + { 0x64fc, 0x0000 }, + { 0x64fd, 0x0000 }, + { 0x64fe, 0x0000 }, + { 0x64ff, 0x0000 }, + { 0x6500, 0x0000 }, + { 0x6501, 0x0000 }, + { 0x6502, 0x0000 }, + { 0x6503, 0x0000 }, + { 0x6504, 0x0000 }, + { 0x6505, 0x0000 }, + { 0x6506, 0x0000 }, + { 0x6507, 0x0000 }, + { 0x6508, 0x0000 }, + { 0x6509, 0x0000 }, + { 0x650a, 0x0000 }, + { 0x650b, 0x0000 }, + { 0x650c, 0x0000 }, + { 0x650d, 0x0000 }, + { 0x650e, 0x0000 }, + { 0x650f, 0x0000 }, + { 0x6510, 0x0000 }, + { 0x6511, 0x0000 }, + { 0x6512, 0x0000 }, + { 0x6513, 0x0000 }, + { 0x6514, 0x0000 }, + { 0x6515, 0x0000 }, + { 0x6516, 0x0000 }, + { 0x6517, 0x0000 }, + { 0x6518, 0x0000 }, + { 0x6519, 0x0000 }, + { 0x651a, 0x0000 }, + { 0x651b, 0x0000 }, + { 0x651c, 0x0000 }, + { 0x651d, 0x0000 }, + { 0x651e, 0x0000 }, + { 0x651f, 0x0000 }, + { 0x6520, 0x0000 }, + { 0x6521, 0x0000 }, + { 0x6522, 0x0000 }, + { 0x6523, 0x0000 }, + { 0x6524, 0x0000 }, + { 0x6525, 0x0000 }, + { 0x6526, 0x0000 }, + { 0x6527, 0x0000 }, + { 0x6528, 0x0000 }, + { 0x6529, 0x0000 }, + { 0x652a, 0x0000 }, + { 0x652b, 0x0000 }, + { 0x652c, 0x0000 }, + { 0x652d, 0x0000 }, + { 0x652e, 0x0000 }, + { 0x652f, 0x0000 }, + { 0x6530, 0x0000 }, + { 0x6531, 0x0000 }, + { 0x6532, 0x0000 }, + { 0x6533, 0x0000 }, + { 0x6534, 0x0000 }, + { 0x6535, 0x0000 }, + { 0x6536, 0x0000 }, + { 0x6537, 0x0000 }, + { 0x6538, 0x0000 }, + { 0x6539, 0x0000 }, + { 0x653a, 0x0000 }, + { 0x653b, 0x0000 }, + { 0x653c, 0x0000 }, + { 0x653d, 0x0000 }, + { 0x653e, 0x0000 }, + { 0x653f, 0x0000 }, + { 0x6540, 0x0000 }, + { 0x6541, 0x0000 }, + { 0x6542, 0x0000 }, + { 0x6543, 0x0000 }, + { 0x6544, 0x0000 }, + { 0x6545, 0x0000 }, + { 0x6546, 0x0000 }, + { 0x6547, 0x0000 }, + { 0x6548, 0x0000 }, + { 0x6549, 0x0000 }, + { 0x654a, 0x0000 }, + { 0x654b, 0x0000 }, + { 0x654c, 0x0000 }, + { 0x654d, 0x0000 }, + { 0x654e, 0x0000 }, + { 0x654f, 0x0000 }, + { 0x6550, 0x0000 }, + { 0x6551, 0x0000 }, + { 0x6552, 0x0000 }, + { 0x6553, 0x0000 }, + { 0x6554, 0x0000 }, + { 0x6555, 0x0000 }, + { 0x6556, 0x0000 }, + { 0x6557, 0x0000 }, + { 0x6558, 0x0000 }, + { 0x6559, 0x0000 }, + { 0x655a, 0x0000 }, + { 0x655b, 0x0000 }, + { 0x655c, 0x0000 }, + { 0x655d, 0x0000 }, + { 0x655e, 0x0000 }, + { 0x655f, 0x0000 }, + { 0x6560, 0x0000 }, + { 0x6561, 0x0000 }, + { 0x6562, 0x0000 }, + { 0x6563, 0x0000 }, + { 0x6564, 0x0000 }, + { 0x6565, 0x0000 }, + { 0x6566, 0x0000 }, + { 0x6567, 0x0000 }, + { 0x6568, 0x0000 }, + { 0x6569, 0x0000 }, + { 0x656a, 0x0000 }, + { 0x656b, 0x0000 }, + { 0x656c, 0x0000 }, + { 0x656d, 0x0000 }, + { 0x656e, 0x0000 }, + { 0x656f, 0x0000 }, + { 0x6570, 0x0000 }, + { 0x6571, 0x0000 }, + { 0x6572, 0x0000 }, + { 0x6573, 0x0000 }, + { 0x6574, 0x0000 }, + { 0x6575, 0x0000 }, + { 0x6576, 0x0000 }, + { 0x6577, 0x0000 }, + { 0x6578, 0x0000 }, + { 0x6579, 0x0000 }, + { 0x657a, 0x0000 }, + { 0x657b, 0x0000 }, + { 0x657c, 0x0000 }, + { 0x657d, 0x0000 }, + { 0x657e, 0x0000 }, + { 0x657f, 0x0000 }, + { 0x6580, 0x0000 }, + { 0x6581, 0x0000 }, + { 0x6582, 0x0000 }, + { 0x6583, 0x0000 }, + { 0x6584, 0x0000 }, + { 0x6585, 0x0000 }, + { 0x6586, 0x0000 }, + { 0x6587, 0x0000 }, + { 0x6588, 0x0000 }, + { 0x6589, 0x0000 }, + { 0x658a, 0x0000 }, + { 0x658b, 0x0000 }, + { 0x658c, 0x0000 }, + { 0x658d, 0x0000 }, + { 0x658e, 0x0000 }, + { 0x658f, 0x0000 }, + { 0x6590, 0x0000 }, + { 0x6591, 0x0000 }, + { 0x6592, 0x0000 }, + { 0x6593, 0x0000 }, + { 0x6594, 0x0000 }, + { 0x6595, 0x0000 }, + { 0x6596, 0x0000 }, + { 0x6597, 0x0000 }, + { 0x6598, 0x0000 }, + { 0x6599, 0x0000 }, + { 0x659a, 0x0000 }, + { 0x659b, 0x0000 }, + { 0x659c, 0x0000 }, + { 0x659d, 0x0000 }, + { 0x659e, 0x0000 }, + { 0x659f, 0x0000 }, + { 0x65a0, 0x0000 }, + { 0x65a1, 0x0000 }, + { 0x65a2, 0x0000 }, + { 0x65a3, 0x0000 }, + { 0x65a4, 0x0000 }, + { 0x65a5, 0x0000 }, + { 0x65a6, 0x0000 }, + { 0x65a7, 0x0000 }, + { 0x65a8, 0x0000 }, + { 0x65a9, 0x0000 }, + { 0x65aa, 0x0000 }, + { 0x65ab, 0x0000 }, + { 0x65ac, 0x0000 }, + { 0x65ad, 0x0000 }, + { 0x65ae, 0x0000 }, + { 0x65af, 0x0000 }, + { 0x65b0, 0x0000 }, + { 0x65b1, 0x0000 }, + { 0x65b2, 0x0000 }, + { 0x65b3, 0x0000 }, + { 0x65b4, 0x0000 }, + { 0x65b5, 0x0000 }, + { 0x65b6, 0x0000 }, + { 0x65b7, 0x0000 }, + { 0x65b8, 0x0000 }, + { 0x65b9, 0x0000 }, + { 0x65ba, 0x0000 }, + { 0x65bb, 0x0000 }, + { 0x65bc, 0x0000 }, + { 0x65bd, 0x0000 }, + { 0x65be, 0x0000 }, + { 0x65bf, 0x0000 }, + { 0x65c0, 0x0000 }, + { 0x65c1, 0x0000 }, + { 0x65c2, 0x0000 }, + { 0x65c3, 0x0000 }, + { 0x65c4, 0x0000 }, + { 0x65c5, 0x0000 }, + { 0x65c6, 0x0000 }, + { 0x65c7, 0x0000 }, + { 0x65c8, 0x0000 }, + { 0x65c9, 0x0000 }, + { 0x65ca, 0x0000 }, + { 0x65cb, 0x0000 }, + { 0x65cc, 0x0000 }, + { 0x65cd, 0x0000 }, + { 0x65ce, 0x0000 }, + { 0x65cf, 0x0000 }, + { 0x65d0, 0x0000 }, + { 0x65d1, 0x0000 }, + { 0x65d2, 0x0000 }, + { 0x65d3, 0x0000 }, + { 0x65d4, 0x0000 }, + { 0x65d5, 0x0000 }, + { 0x65d6, 0x0000 }, + { 0x65d7, 0x0000 }, + { 0x65d8, 0x0000 }, + { 0x65d9, 0x0000 }, + { 0x65da, 0x0000 }, + { 0x65db, 0x0000 }, + { 0x65dc, 0x0000 }, + { 0x65dd, 0x0000 }, + { 0x65de, 0x0000 }, + { 0x65df, 0x0000 }, + { 0x65e0, 0x0000 }, + { 0x65e1, 0x0000 }, + { 0x65e2, 0x0000 }, + { 0x65e3, 0x0000 }, + { 0x65e4, 0x0000 }, + { 0x65e5, 0x0000 }, + { 0x65e6, 0x0000 }, + { 0x65e7, 0x0000 }, + { 0x65e8, 0x0000 }, + { 0x65e9, 0x0000 }, + { 0x65ea, 0x0000 }, + { 0x65eb, 0x0000 }, + { 0x65ec, 0x0000 }, + { 0x65ed, 0x0000 }, + { 0x65ee, 0x0000 }, + { 0x65ef, 0x0000 }, + { 0x65f0, 0x0000 }, + { 0x65f1, 0x0000 }, + { 0x65f2, 0x0000 }, + { 0x65f3, 0x0000 }, + { 0x65f4, 0x0000 }, + { 0x65f5, 0x0000 }, + { 0x65f6, 0x0000 }, + { 0x65f7, 0x0000 }, + { 0x65f8, 0x0000 }, + { 0x65f9, 0x0000 }, + { 0x65fa, 0x0000 }, + { 0x65fb, 0x0000 }, + { 0x65fc, 0x0000 }, + { 0x65fd, 0x0000 }, + { 0x65fe, 0x0000 }, + { 0x65ff, 0x0000 }, + { 0x6600, 0x0000 }, + { 0x6601, 0x0000 }, + { 0x6602, 0x0000 }, + { 0x6603, 0x0000 }, + { 0x6604, 0x0000 }, + { 0x6605, 0x0000 }, + { 0x6606, 0x0000 }, + { 0x6607, 0x0000 }, + { 0x6608, 0x0000 }, + { 0x6609, 0x0000 }, + { 0x660a, 0x0000 }, + { 0x660b, 0x0000 }, + { 0x660c, 0x0000 }, + { 0x660d, 0x0000 }, + { 0x660e, 0x0000 }, + { 0x660f, 0x0000 }, + { 0x6610, 0x0000 }, + { 0x6611, 0x0000 }, + { 0x6612, 0x0000 }, + { 0x6613, 0x0000 }, + { 0x6614, 0x0000 }, + { 0x6615, 0x0000 }, + { 0x6616, 0x0000 }, + { 0x6617, 0x0000 }, + { 0x6618, 0x0000 }, + { 0x6619, 0x0000 }, + { 0x661a, 0x0000 }, + { 0x661b, 0x0000 }, + { 0x661c, 0x0000 }, + { 0x661d, 0x0000 }, + { 0x661e, 0x0000 }, + { 0x661f, 0x0000 }, + { 0x6620, 0x0000 }, + { 0x6621, 0x0000 }, + { 0x6622, 0x0000 }, + { 0x6623, 0x0000 }, + { 0x6624, 0x0000 }, + { 0x6625, 0x0000 }, + { 0x6626, 0x0000 }, + { 0x6627, 0x0000 }, + { 0x6628, 0x0000 }, + { 0x6629, 0x0000 }, + { 0x662a, 0x0000 }, + { 0x662b, 0x0000 }, + { 0x662c, 0x0000 }, + { 0x662d, 0x0000 }, + { 0x662e, 0x0000 }, + { 0x662f, 0x0000 }, + { 0x6630, 0x0000 }, + { 0x6631, 0x0000 }, + { 0x6632, 0x0000 }, + { 0x6633, 0x0000 }, + { 0x6634, 0x0000 }, + { 0x6635, 0x0000 }, + { 0x6636, 0x0000 }, + { 0x6637, 0x0000 }, + { 0x6638, 0x0000 }, + { 0x6639, 0x0000 }, + { 0x663a, 0x0000 }, + { 0x663b, 0x0000 }, + { 0x663c, 0x0000 }, + { 0x663d, 0x0000 }, + { 0x663e, 0x0000 }, + { 0x663f, 0x0000 }, + { 0x6640, 0x0000 }, + { 0x6641, 0x0000 }, + { 0x6642, 0x0000 }, + { 0x6643, 0x0000 }, + { 0x6644, 0x0000 }, + { 0x6645, 0x0000 }, + { 0x6646, 0x0000 }, + { 0x6647, 0x0000 }, + { 0x6648, 0x0000 }, + { 0x6649, 0x0000 }, + { 0x664a, 0x0000 }, + { 0x664b, 0x0000 }, + { 0x664c, 0x0000 }, + { 0x664d, 0x0000 }, + { 0x664e, 0x0000 }, + { 0x664f, 0x0000 }, + { 0x6650, 0x0000 }, + { 0x6651, 0x0000 }, + { 0x6652, 0x0000 }, + { 0x6653, 0x0000 }, + { 0x6654, 0x0000 }, + { 0x6655, 0x0000 }, + { 0x6656, 0x0000 }, + { 0x6657, 0x0000 }, + { 0x6658, 0x0000 }, + { 0x6659, 0x0000 }, + { 0x665a, 0x0000 }, + { 0x665b, 0x0000 }, + { 0x665c, 0x0000 }, + { 0x665d, 0x0000 }, + { 0x665e, 0x0000 }, + { 0x665f, 0x0000 }, + { 0x6660, 0x0000 }, + { 0x6661, 0x0000 }, + { 0x6662, 0x0000 }, + { 0x6663, 0x0000 }, + { 0x6664, 0x0000 }, + { 0x6665, 0x0000 }, + { 0x6666, 0x0000 }, + { 0x6667, 0x0000 }, + { 0x6668, 0x0000 }, + { 0x6669, 0x0000 }, + { 0x666a, 0x0000 }, + { 0x666b, 0x0000 }, + { 0x666c, 0x0000 }, + { 0x666d, 0x0000 }, + { 0x666e, 0x0000 }, + { 0x666f, 0x0000 }, + { 0x6670, 0x0000 }, + { 0x6671, 0x0000 }, + { 0x6672, 0x0000 }, + { 0x6673, 0x0000 }, + { 0x6674, 0x0000 }, + { 0x6675, 0x0000 }, + { 0x6676, 0x0000 }, + { 0x6677, 0x0000 }, + { 0x6678, 0x0000 }, + { 0x6679, 0x0000 }, + { 0x667a, 0x0000 }, + { 0x667b, 0x0000 }, + { 0x667c, 0x0000 }, + { 0x667d, 0x0000 }, + { 0x667e, 0x0000 }, + { 0x667f, 0x0000 }, + { 0x6680, 0x0000 }, + { 0x6681, 0x0000 }, + { 0x6682, 0x0000 }, + { 0x6683, 0x0000 }, + { 0x6684, 0x0000 }, + { 0x6685, 0x0000 }, + { 0x6686, 0x0000 }, + { 0x6687, 0x0000 }, + { 0x6688, 0x0000 }, + { 0x6689, 0x0000 }, + { 0x668a, 0x0000 }, + { 0x668b, 0x0000 }, + { 0x668c, 0x0000 }, + { 0x668d, 0x0000 }, + { 0x668e, 0x0000 }, + { 0x668f, 0x0000 }, + { 0x6690, 0x0000 }, + { 0x6691, 0x0000 }, + { 0x6692, 0x0000 }, + { 0x6693, 0x0000 }, + { 0x6694, 0x0000 }, + { 0x6695, 0x0000 }, + { 0x6696, 0x0000 }, + { 0x6697, 0x0000 }, + { 0x6698, 0x0000 }, + { 0x6699, 0x0000 }, + { 0x669a, 0x0000 }, + { 0x669b, 0x0000 }, + { 0x669c, 0x0000 }, + { 0x669d, 0x0000 }, + { 0x669e, 0x0000 }, + { 0x669f, 0x0000 }, + { 0x66a0, 0x0000 }, + { 0x66a1, 0x0000 }, + { 0x66a2, 0x0000 }, + { 0x66a3, 0x0000 }, + { 0x66a4, 0x0000 }, + { 0x66a5, 0x0000 }, + { 0x66a6, 0x0000 }, + { 0x66a7, 0x0000 }, + { 0x66a8, 0x0000 }, + { 0x66a9, 0x0000 }, + { 0x66aa, 0x0000 }, + { 0x66ab, 0x0000 }, + { 0x66ac, 0x0000 }, + { 0x66ad, 0x0000 }, + { 0x66ae, 0x0000 }, + { 0x66af, 0x0000 }, + { 0x66b0, 0x0000 }, + { 0x66b1, 0x0000 }, + { 0x66b2, 0x0000 }, + { 0x66b3, 0x0000 }, + { 0x66b4, 0x0000 }, + { 0x66b5, 0x0000 }, + { 0x66b6, 0x0000 }, + { 0x66b7, 0x0000 }, + { 0x66b8, 0x0000 }, + { 0x66b9, 0x0000 }, + { 0x66ba, 0x0000 }, + { 0x66bb, 0x0000 }, + { 0x66bc, 0x0000 }, + { 0x66bd, 0x0000 }, + { 0x66be, 0x0000 }, + { 0x66bf, 0x0000 }, + { 0x66c0, 0x0000 }, + { 0x66c1, 0x0000 }, + { 0x66c2, 0x0000 }, + { 0x66c3, 0x0000 }, + { 0x66c4, 0x0000 }, + { 0x66c5, 0x0000 }, + { 0x66c6, 0x0000 }, + { 0x66c7, 0x0000 }, + { 0x66c8, 0x0000 }, + { 0x66c9, 0x0000 }, + { 0x66ca, 0x0000 }, + { 0x66cb, 0x0000 }, + { 0x66cc, 0x0000 }, + { 0x66cd, 0x0000 }, + { 0x66ce, 0x0000 }, + { 0x66cf, 0x0000 }, + { 0x66d0, 0x0000 }, + { 0x66d1, 0x0000 }, + { 0x66d2, 0x0000 }, + { 0x66d3, 0x0000 }, + { 0x66d4, 0x0000 }, + { 0x66d5, 0x0000 }, + { 0x66d6, 0x0000 }, + { 0x66d7, 0x0000 }, + { 0x66d8, 0x0000 }, + { 0x66d9, 0x0000 }, + { 0x66da, 0x0000 }, + { 0x66db, 0x0000 }, + { 0x66dc, 0x0000 }, + { 0x66dd, 0x0000 }, + { 0x66de, 0x0000 }, + { 0x66df, 0x0000 }, + { 0x66e0, 0x0000 }, + { 0x66e1, 0x0000 }, + { 0x66e2, 0x0000 }, + { 0x66e3, 0x0000 }, + { 0x66e4, 0x0000 }, + { 0x66e5, 0x0000 }, + { 0x66e6, 0x0000 }, + { 0x66e7, 0x0000 }, + { 0x66e8, 0x0000 }, + { 0x66e9, 0x0000 }, + { 0x66ea, 0x0000 }, + { 0x66eb, 0x0000 }, + { 0x66ec, 0x0000 }, + { 0x66ed, 0x0000 }, + { 0x66ee, 0x0000 }, + { 0x66ef, 0x0000 }, + { 0x66f0, 0x0000 }, + { 0x66f1, 0x0000 }, + { 0x66f2, 0x0000 }, + { 0x66f3, 0x0000 }, + { 0x66f4, 0x0000 }, + { 0x66f5, 0x0000 }, + { 0x66f6, 0x0000 }, + { 0x66f7, 0x0000 }, + { 0x66f8, 0x0000 }, + { 0x66f9, 0x0000 }, + { 0x66fa, 0x0000 }, + { 0x66fb, 0x0000 }, + { 0x66fc, 0x0000 }, + { 0x66fd, 0x0000 }, + { 0x66fe, 0x0000 }, + { 0x66ff, 0x0000 }, + { 0x6700, 0x0000 }, + { 0x6701, 0x0000 }, + { 0x6702, 0x0000 }, + { 0x6703, 0x0000 }, + { 0x6704, 0x0000 }, + { 0x6705, 0x0000 }, + { 0x6706, 0x0000 }, + { 0x6707, 0x0000 }, + { 0x6708, 0x0000 }, + { 0x6709, 0x0000 }, + { 0x670a, 0x0000 }, + { 0x670b, 0x0000 }, + { 0x670c, 0x0000 }, + { 0x670d, 0x0000 }, + { 0x670e, 0x0000 }, + { 0x670f, 0x0000 }, + { 0x6710, 0x0000 }, + { 0x6711, 0x0000 }, + { 0x6712, 0x0000 }, + { 0x6713, 0x0000 }, + { 0x6714, 0x0000 }, + { 0x6715, 0x0000 }, + { 0x6716, 0x0000 }, + { 0x6717, 0x0000 }, + { 0x6718, 0x0000 }, + { 0x6719, 0x0000 }, + { 0x671a, 0x0000 }, + { 0x671b, 0x0000 }, + { 0x671c, 0x0000 }, + { 0x671d, 0x0000 }, + { 0x671e, 0x0000 }, + { 0x671f, 0x0000 }, + { 0x6720, 0x0000 }, + { 0x6721, 0x0000 }, + { 0x6722, 0x0000 }, + { 0x6723, 0x0000 }, + { 0x6724, 0x0000 }, + { 0x6725, 0x0000 }, + { 0x6726, 0x0000 }, + { 0x6727, 0x0000 }, + { 0x6728, 0x0000 }, + { 0x6729, 0x0000 }, + { 0x672a, 0x0000 }, + { 0x672b, 0x0000 }, + { 0x672c, 0x0000 }, + { 0x672d, 0x0000 }, + { 0x672e, 0x0000 }, + { 0x672f, 0x0000 }, + { 0x6730, 0x0000 }, + { 0x6731, 0x0000 }, + { 0x6732, 0x0000 }, + { 0x6733, 0x0000 }, + { 0x6734, 0x0000 }, + { 0x6735, 0x0000 }, + { 0x6736, 0x0000 }, + { 0x6737, 0x0000 }, + { 0x6738, 0x0000 }, + { 0x6739, 0x0000 }, + { 0x673a, 0x0000 }, + { 0x673b, 0x0000 }, + { 0x673c, 0x0000 }, + { 0x673d, 0x0000 }, + { 0x673e, 0x0000 }, + { 0x673f, 0x0000 }, + { 0x6740, 0x0000 }, + { 0x6741, 0x0000 }, + { 0x6742, 0x0000 }, + { 0x6743, 0x0000 }, + { 0x6744, 0x0000 }, + { 0x6745, 0x0000 }, + { 0x6746, 0x0000 }, + { 0x6747, 0x0000 }, + { 0x6748, 0x0000 }, + { 0x6749, 0x0000 }, + { 0x674a, 0x0000 }, + { 0x674b, 0x0000 }, + { 0x674c, 0x0000 }, + { 0x674d, 0x0000 }, + { 0x674e, 0x0000 }, + { 0x674f, 0x0000 }, + { 0x6750, 0x0000 }, + { 0x6751, 0x0000 }, + { 0x6752, 0x0000 }, + { 0x6753, 0x0000 }, + { 0x6754, 0x0000 }, + { 0x6755, 0x0000 }, + { 0x6756, 0x0000 }, + { 0x6757, 0x0000 }, + { 0x6758, 0x0000 }, + { 0x6759, 0x0000 }, + { 0x675a, 0x0000 }, + { 0x675b, 0x0000 }, + { 0x675c, 0x0000 }, + { 0x675d, 0x0000 }, + { 0x675e, 0x0000 }, + { 0x675f, 0x0000 }, + { 0x6760, 0x0000 }, + { 0x6761, 0x0000 }, + { 0x6762, 0x0000 }, + { 0x6763, 0x0000 }, + { 0x6764, 0x0000 }, + { 0x6765, 0x0000 }, + { 0x6766, 0x0000 }, + { 0x6767, 0x0000 }, + { 0x6768, 0x0000 }, + { 0x6769, 0x0000 }, + { 0x676a, 0x0000 }, + { 0x676b, 0x0000 }, + { 0x676c, 0x0000 }, + { 0x676d, 0x0000 }, + { 0x676e, 0x0000 }, + { 0x676f, 0x0000 }, + { 0x6770, 0x0000 }, + { 0x6771, 0x0000 }, + { 0x6772, 0x0000 }, + { 0x6773, 0x0000 }, + { 0x6774, 0x0000 }, + { 0x6775, 0x0000 }, + { 0x6776, 0x0000 }, + { 0x6777, 0x0000 }, + { 0x6778, 0x0000 }, + { 0x6779, 0x0000 }, + { 0x677a, 0x0000 }, + { 0x677b, 0x0000 }, + { 0x677c, 0x0000 }, + { 0x677d, 0x0000 }, + { 0x677e, 0x0000 }, + { 0x677f, 0x0000 }, + { 0x6780, 0x0000 }, + { 0x6781, 0x0000 }, + { 0x6782, 0x0000 }, + { 0x6783, 0x0000 }, + { 0x6784, 0x0000 }, + { 0x6785, 0x0000 }, + { 0x6786, 0x0000 }, + { 0x6787, 0x0000 }, + { 0x6788, 0x0000 }, + { 0x6789, 0x0000 }, + { 0x678a, 0x0000 }, + { 0x678b, 0x0000 }, + { 0x678c, 0x0000 }, + { 0x678d, 0x0000 }, + { 0x678e, 0x0000 }, + { 0x678f, 0x0000 }, + { 0x6790, 0x0000 }, + { 0x6791, 0x0000 }, + { 0x6792, 0x0000 }, + { 0x6793, 0x0000 }, + { 0x6794, 0x0000 }, + { 0x6795, 0x0000 }, + { 0x6796, 0x0000 }, + { 0x6797, 0x0000 }, + { 0x6798, 0x0000 }, + { 0x6799, 0x0000 }, + { 0x679a, 0x0000 }, + { 0x679b, 0x0000 }, + { 0x679c, 0x0000 }, + { 0x679d, 0x0000 }, + { 0x679e, 0x0000 }, + { 0x679f, 0x0000 }, + { 0x67a0, 0x0000 }, + { 0x67a1, 0x0000 }, + { 0x67a2, 0x0000 }, + { 0x67a3, 0x0000 }, + { 0x67a4, 0x0000 }, + { 0x67a5, 0x0000 }, + { 0x67a6, 0x0000 }, + { 0x67a7, 0x0000 }, + { 0x67a8, 0x0000 }, + { 0x67a9, 0x0000 }, + { 0x67aa, 0x0000 }, + { 0x67ab, 0x0000 }, + { 0x67ac, 0x0000 }, + { 0x67ad, 0x0000 }, + { 0x67ae, 0x0000 }, + { 0x67af, 0x0000 }, + { 0x67b0, 0x0000 }, + { 0x67b1, 0x0000 }, + { 0x67b2, 0x0000 }, + { 0x67b3, 0x0000 }, + { 0x67b4, 0x0000 }, + { 0x67b5, 0x0000 }, + { 0x67b6, 0x0000 }, + { 0x67b7, 0x0000 }, + { 0x67b8, 0x0000 }, + { 0x67b9, 0x0000 }, + { 0x67ba, 0x0000 }, + { 0x67bb, 0x0000 }, + { 0x67bc, 0x0000 }, + { 0x67bd, 0x0000 }, + { 0x67be, 0x0000 }, + { 0x67bf, 0x0000 }, + { 0x67c0, 0x0000 }, + { 0x67c1, 0x0000 }, + { 0x67c2, 0x0000 }, + { 0x67c3, 0x0000 }, + { 0x67c4, 0x0000 }, + { 0x67c5, 0x0000 }, + { 0x67c6, 0x0000 }, + { 0x67c7, 0x0000 }, + { 0x67c8, 0x0000 }, + { 0x67c9, 0x0000 }, + { 0x67ca, 0x0000 }, + { 0x67cb, 0x0000 }, + { 0x67cc, 0x0000 }, + { 0x67cd, 0x0000 }, + { 0x67ce, 0x0000 }, + { 0x67cf, 0x0000 }, + { 0x67d0, 0x0000 }, + { 0x67d1, 0x0000 }, + { 0x67d2, 0x0000 }, + { 0x67d3, 0x0000 }, + { 0x67d4, 0x0000 }, + { 0x67d5, 0x0000 }, + { 0x67d6, 0x0000 }, + { 0x67d7, 0x0000 }, + { 0x67d8, 0x0000 }, + { 0x67d9, 0x0000 }, + { 0x67da, 0x0000 }, + { 0x67db, 0x0000 }, + { 0x67dc, 0x0000 }, + { 0x67dd, 0x0000 }, + { 0x67de, 0x0000 }, + { 0x67df, 0x0000 }, + { 0x67e0, 0x0000 }, + { 0x67e1, 0x0000 }, + { 0x67e2, 0x0000 }, + { 0x67e3, 0x0000 }, + { 0x67e4, 0x0000 }, + { 0x67e5, 0x0000 }, + { 0x67e6, 0x0000 }, + { 0x67e7, 0x0000 }, + { 0x67e8, 0x0000 }, + { 0x67e9, 0x0000 }, + { 0x67ea, 0x0000 }, + { 0x67eb, 0x0000 }, + { 0x67ec, 0x0000 }, + { 0x67ed, 0x0000 }, + { 0x67ee, 0x0000 }, + { 0x67ef, 0x0000 }, + { 0x67f0, 0x0000 }, + { 0x67f1, 0x0000 }, + { 0x67f2, 0x0000 }, + { 0x67f3, 0x0000 }, + { 0x67f4, 0x0000 }, + { 0x67f5, 0x0000 }, + { 0x67f6, 0x0000 }, + { 0x67f7, 0x0000 }, + { 0x67f8, 0x0000 }, + { 0x67f9, 0x0000 }, + { 0x67fa, 0x0000 }, + { 0x67fb, 0x0000 }, + { 0x67fc, 0x0000 }, + { 0x67fd, 0x0000 }, + { 0x67fe, 0x0000 }, + { 0x67ff, 0x0000 }, + { 0x6800, 0x0000 }, + { 0x6801, 0x0000 }, + { 0x6802, 0x0000 }, + { 0x6803, 0x0000 }, + { 0x6804, 0x0000 }, + { 0x6805, 0x0000 }, + { 0x6806, 0x0000 }, + { 0x6807, 0x0000 }, + { 0x6808, 0x0000 }, + { 0x6809, 0x0000 }, + { 0x680a, 0x0000 }, + { 0x680b, 0x0000 }, + { 0x680c, 0x0000 }, + { 0x680d, 0x0000 }, + { 0x680e, 0x0000 }, + { 0x680f, 0x0000 }, + { 0x6810, 0x0000 }, + { 0x6811, 0x0000 }, + { 0x6812, 0x0000 }, + { 0x6813, 0x0000 }, + { 0x6814, 0x0000 }, + { 0x6815, 0x0000 }, + { 0x6816, 0x0000 }, + { 0x6817, 0x0000 }, + { 0x6818, 0x0000 }, + { 0x6819, 0x0000 }, + { 0x681a, 0x0000 }, + { 0x681b, 0x0000 }, + { 0x681c, 0x0000 }, + { 0x681d, 0x0000 }, + { 0x681e, 0x0000 }, + { 0x681f, 0x0000 }, + { 0x6820, 0x0000 }, + { 0x6821, 0x0000 }, + { 0x6822, 0x0000 }, + { 0x6823, 0x0000 }, + { 0x6824, 0x0000 }, + { 0x6825, 0x0000 }, + { 0x6826, 0x0000 }, + { 0x6827, 0x0000 }, + { 0x6828, 0x0000 }, + { 0x6829, 0x0000 }, + { 0x682a, 0x0000 }, + { 0x682b, 0x0000 }, + { 0x682c, 0x0000 }, + { 0x682d, 0x0000 }, + { 0x682e, 0x0000 }, + { 0x682f, 0x0000 }, + { 0x6830, 0x0000 }, + { 0x6831, 0x0000 }, + { 0x6832, 0x0000 }, + { 0x6833, 0x0000 }, + { 0x6834, 0x0000 }, + { 0x6835, 0x0000 }, + { 0x6836, 0x0000 }, + { 0x6837, 0x0000 }, + { 0x6838, 0x0000 }, + { 0x6839, 0x0000 }, + { 0x683a, 0x0000 }, + { 0x683b, 0x0000 }, + { 0x683c, 0x0000 }, + { 0x683d, 0x0000 }, + { 0x683e, 0x0000 }, + { 0x683f, 0x0000 }, + { 0x6840, 0x0000 }, + { 0x6841, 0x0000 }, + { 0x6842, 0x0000 }, + { 0x6843, 0x0000 }, + { 0x6844, 0x0000 }, + { 0x6845, 0x0000 }, + { 0x6846, 0x0000 }, + { 0x6847, 0x0000 }, + { 0x6848, 0x0000 }, + { 0x6849, 0x0000 }, + { 0x684a, 0x0000 }, + { 0x684b, 0x0000 }, + { 0x684c, 0x0000 }, + { 0x684d, 0x0000 }, + { 0x684e, 0x0000 }, + { 0x684f, 0x0000 }, + { 0x6850, 0x0000 }, + { 0x6851, 0x0000 }, + { 0x6852, 0x0000 }, + { 0x6853, 0x0000 }, + { 0x6854, 0x0000 }, + { 0x6855, 0x0000 }, + { 0x6856, 0x0000 }, + { 0x6857, 0x0000 }, + { 0x6858, 0x0000 }, + { 0x6859, 0x0000 }, + { 0x685a, 0x0000 }, + { 0x685b, 0x0000 }, + { 0x685c, 0x0000 }, + { 0x685d, 0x0000 }, + { 0x685e, 0x0000 }, + { 0x685f, 0x0000 }, + { 0x6860, 0x0000 }, + { 0x6861, 0x0000 }, + { 0x6862, 0x0000 }, + { 0x6863, 0x0000 }, + { 0x6864, 0x0000 }, + { 0x6865, 0x0000 }, + { 0x6866, 0x0000 }, + { 0x6867, 0x0000 }, + { 0x6868, 0x0000 }, + { 0x6869, 0x0000 }, + { 0x686a, 0x0000 }, + { 0x686b, 0x0000 }, + { 0x686c, 0x0000 }, + { 0x686d, 0x0000 }, + { 0x686e, 0x0000 }, + { 0x686f, 0x0000 }, + { 0x6870, 0x0000 }, + { 0x6871, 0x0000 }, + { 0x6872, 0x0000 }, + { 0x6873, 0x0000 }, + { 0x6874, 0x0000 }, + { 0x6875, 0x0000 }, + { 0x6876, 0x0000 }, + { 0x6877, 0x0000 }, + { 0x6878, 0x0000 }, + { 0x6879, 0x0000 }, + { 0x687a, 0x0000 }, + { 0x687b, 0x0000 }, + { 0x687c, 0x0000 }, + { 0x687d, 0x0000 }, + { 0x687e, 0x0000 }, + { 0x687f, 0x0000 }, + { 0x6880, 0x0000 }, + { 0x6881, 0x0000 }, + { 0x6882, 0x0000 }, + { 0x6883, 0x0000 }, + { 0x6884, 0x0000 }, + { 0x6885, 0x0000 }, + { 0x6886, 0x0000 }, + { 0x6887, 0x0000 }, + { 0x6888, 0x0000 }, + { 0x6889, 0x0000 }, + { 0x688a, 0x0000 }, + { 0x688b, 0x0000 }, + { 0x688c, 0x0000 }, + { 0x688d, 0x0000 }, + { 0x688e, 0x0000 }, + { 0x688f, 0x0000 }, + { 0x6890, 0x0000 }, + { 0x6891, 0x0000 }, + { 0x6892, 0x0000 }, + { 0x6893, 0x0000 }, + { 0x6894, 0x0000 }, + { 0x6895, 0x0000 }, + { 0x6896, 0x0000 }, + { 0x6897, 0x0000 }, + { 0x6898, 0x0000 }, + { 0x6899, 0x0000 }, + { 0x689a, 0x0000 }, + { 0x689b, 0x0000 }, + { 0x689c, 0x0000 }, + { 0x689d, 0x0000 }, + { 0x689e, 0x0000 }, + { 0x689f, 0x0000 }, + { 0x68a0, 0x0000 }, + { 0x68a1, 0x0000 }, + { 0x68a2, 0x0000 }, + { 0x68a3, 0x0000 }, + { 0x68a4, 0x0000 }, + { 0x68a5, 0x0000 }, + { 0x68a6, 0x0000 }, + { 0x68a7, 0x0000 }, + { 0x68a8, 0x0000 }, + { 0x68a9, 0x0000 }, + { 0x68aa, 0x0000 }, + { 0x68ab, 0x0000 }, + { 0x68ac, 0x0000 }, + { 0x68ad, 0x0000 }, + { 0x68ae, 0x0000 }, + { 0x68af, 0x0000 }, + { 0x68b0, 0x0000 }, + { 0x68b1, 0x0000 }, + { 0x68b2, 0x0000 }, + { 0x68b3, 0x0000 }, + { 0x68b4, 0x0000 }, + { 0x68b5, 0x0000 }, + { 0x68b6, 0x0000 }, + { 0x68b7, 0x0000 }, + { 0x68b8, 0x0000 }, + { 0x68b9, 0x0000 }, + { 0x68ba, 0x0000 }, + { 0x68bb, 0x0000 }, + { 0x68bc, 0x0000 }, + { 0x68bd, 0x0000 }, + { 0x68be, 0x0000 }, + { 0x68bf, 0x0000 }, + { 0x68c0, 0x0000 }, + { 0x68c1, 0x0000 }, + { 0x68c2, 0x0000 }, + { 0x68c3, 0x0000 }, + { 0x68c4, 0x0000 }, + { 0x68c5, 0x0000 }, + { 0x68c6, 0x0000 }, + { 0x68c7, 0x0000 }, + { 0x68c8, 0x0000 }, + { 0x68c9, 0x0000 }, + { 0x68ca, 0x0000 }, + { 0x68cb, 0x0000 }, + { 0x68cc, 0x0000 }, + { 0x68cd, 0x0000 }, + { 0x68ce, 0x0000 }, + { 0x68cf, 0x0000 }, + { 0x68d0, 0x0000 }, + { 0x68d1, 0x0000 }, + { 0x68d2, 0x0000 }, + { 0x68d3, 0x0000 }, + { 0x68d4, 0x0000 }, + { 0x68d5, 0x0000 }, + { 0x68d6, 0x0000 }, + { 0x68d7, 0x0000 }, + { 0x68d8, 0x0000 }, + { 0x68d9, 0x0000 }, + { 0x68da, 0x0000 }, + { 0x68db, 0x0000 }, + { 0x68dc, 0x0000 }, + { 0x68dd, 0x0000 }, + { 0x68de, 0x0000 }, + { 0x68df, 0x0000 }, + { 0x68e0, 0x0000 }, + { 0x68e1, 0x0000 }, + { 0x68e2, 0x0000 }, + { 0x68e3, 0x0000 }, + { 0x68e4, 0x0000 }, + { 0x68e5, 0x0000 }, + { 0x68e6, 0x0000 }, + { 0x68e7, 0x0000 }, + { 0x68e8, 0x0000 }, + { 0x68e9, 0x0000 }, + { 0x68ea, 0x0000 }, + { 0x68eb, 0x0000 }, + { 0x68ec, 0x0000 }, + { 0x68ed, 0x0000 }, + { 0x68ee, 0x0000 }, + { 0x68ef, 0x0000 }, + { 0x68f0, 0x0000 }, + { 0x68f1, 0x0000 }, + { 0x68f2, 0x0000 }, + { 0x68f3, 0x0000 }, + { 0x68f4, 0x0000 }, + { 0x68f5, 0x0000 }, + { 0x68f6, 0x0000 }, + { 0x68f7, 0x0000 }, + { 0x68f8, 0x0000 }, + { 0x68f9, 0x0000 }, + { 0x68fa, 0x0000 }, + { 0x68fb, 0x0000 }, + { 0x68fc, 0x0000 }, + { 0x68fd, 0x0000 }, + { 0x68fe, 0x0000 }, + { 0x68ff, 0x0000 }, + { 0x6900, 0x0000 }, + { 0x6901, 0x0000 }, + { 0x6902, 0x0000 }, + { 0x6903, 0x0000 }, + { 0x6904, 0x0000 }, + { 0x6905, 0x0000 }, + { 0x6906, 0x0000 }, + { 0x6907, 0x0000 }, + { 0x6908, 0x0000 }, + { 0x6909, 0x0000 }, + { 0x690a, 0x0000 }, + { 0x690b, 0x0000 }, + { 0x690c, 0x0000 }, + { 0x690d, 0x0000 }, + { 0x690e, 0x0000 }, + { 0x690f, 0x0000 }, + { 0x6910, 0x0000 }, + { 0x6911, 0x0000 }, + { 0x6912, 0x0000 }, + { 0x6913, 0x0000 }, + { 0x6914, 0x0000 }, + { 0x6915, 0x0000 }, + { 0x6916, 0x0000 }, + { 0x6917, 0x0000 }, + { 0x6918, 0x0000 }, + { 0x6919, 0x0000 }, + { 0x691a, 0x0000 }, + { 0x691b, 0x0000 }, + { 0x691c, 0x0000 }, + { 0x691d, 0x0000 }, + { 0x691e, 0x0000 }, + { 0x691f, 0x0000 }, + { 0x6920, 0x0000 }, + { 0x6921, 0x0000 }, + { 0x6922, 0x0000 }, + { 0x6923, 0x0000 }, + { 0x6924, 0x0000 }, + { 0x6925, 0x0000 }, + { 0x6926, 0x0000 }, + { 0x6927, 0x0000 }, + { 0x6928, 0x0000 }, + { 0x6929, 0x0000 }, + { 0x692a, 0x0000 }, + { 0x692b, 0x0000 }, + { 0x692c, 0x0000 }, + { 0x692d, 0x0000 }, + { 0x692e, 0x0000 }, + { 0x692f, 0x0000 }, + { 0x6930, 0x0000 }, + { 0x6931, 0x0000 }, + { 0x6932, 0x0000 }, + { 0x6933, 0x0000 }, + { 0x6934, 0x0000 }, + { 0x6935, 0x0000 }, + { 0x6936, 0x0000 }, + { 0x6937, 0x0000 }, + { 0x6938, 0x0000 }, + { 0x6939, 0x0000 }, + { 0x693a, 0x0000 }, + { 0x693b, 0x0000 }, + { 0x693c, 0x0000 }, + { 0x693d, 0x0000 }, + { 0x693e, 0x0000 }, + { 0x693f, 0x0000 }, + { 0x6940, 0x0000 }, + { 0x6941, 0x0000 }, + { 0x6942, 0x0000 }, + { 0x6943, 0x0000 }, + { 0x6944, 0x0000 }, + { 0x6945, 0x0000 }, + { 0x6946, 0x0000 }, + { 0x6947, 0x0000 }, + { 0x6948, 0x0000 }, + { 0x6949, 0x0000 }, + { 0x694a, 0x0000 }, + { 0x694b, 0x0000 }, + { 0x694c, 0x0000 }, + { 0x694d, 0x0000 }, + { 0x694e, 0x0000 }, + { 0x694f, 0x0000 }, + { 0x6950, 0x0000 }, + { 0x6951, 0x0000 }, + { 0x6952, 0x0000 }, + { 0x6953, 0x0000 }, + { 0x6954, 0x0000 }, + { 0x6955, 0x0000 }, + { 0x6956, 0x0000 }, + { 0x6957, 0x0000 }, + { 0x6958, 0x0000 }, + { 0x6959, 0x0000 }, + { 0x695a, 0x0000 }, + { 0x695b, 0x0000 }, + { 0x695c, 0x0000 }, + { 0x695d, 0x0000 }, + { 0x695e, 0x0000 }, + { 0x695f, 0x0000 }, + { 0x6960, 0x0000 }, + { 0x6961, 0x0000 }, + { 0x6962, 0x0000 }, + { 0x6963, 0x0000 }, + { 0x6964, 0x0000 }, + { 0x6965, 0x0000 }, + { 0x6966, 0x0000 }, + { 0x6967, 0x0000 }, + { 0x6968, 0x0000 }, + { 0x6969, 0x0000 }, + { 0x696a, 0x0000 }, + { 0x696b, 0x0000 }, + { 0x696c, 0x0000 }, + { 0x696d, 0x0000 }, + { 0x696e, 0x0000 }, + { 0x696f, 0x0000 }, + { 0x6970, 0x0000 }, + { 0x6971, 0x0000 }, + { 0x6972, 0x0000 }, + { 0x6973, 0x0000 }, + { 0x6974, 0x0000 }, + { 0x6975, 0x0000 }, + { 0x6976, 0x0000 }, + { 0x6977, 0x0000 }, + { 0x6978, 0x0000 }, + { 0x6979, 0x0000 }, + { 0x697a, 0x0000 }, + { 0x697b, 0x0000 }, + { 0x697c, 0x0000 }, + { 0x697d, 0x0000 }, + { 0x697e, 0x0000 }, + { 0x697f, 0x0000 }, + { 0x6980, 0x0000 }, + { 0x6981, 0x0000 }, + { 0x6982, 0x0000 }, + { 0x6983, 0x0000 }, + { 0x6984, 0x0000 }, + { 0x6985, 0x0000 }, + { 0x6986, 0x0000 }, + { 0x6987, 0x0000 }, + { 0x6988, 0x0000 }, + { 0x6989, 0x0000 }, + { 0x698a, 0x0000 }, + { 0x698b, 0x0000 }, + { 0x698c, 0x0000 }, + { 0x698d, 0x0000 }, + { 0x698e, 0x0000 }, + { 0x698f, 0x0000 }, + { 0x6990, 0x0000 }, + { 0x6991, 0x0000 }, + { 0x6992, 0x0000 }, + { 0x6993, 0x0000 }, + { 0x6994, 0x0000 }, + { 0x6995, 0x0000 }, + { 0x6996, 0x0000 }, + { 0x6997, 0x0000 }, + { 0x6998, 0x0000 }, + { 0x6999, 0x0000 }, + { 0x699a, 0x0000 }, + { 0x699b, 0x0000 }, + { 0x699c, 0x0000 }, + { 0x699d, 0x0000 }, + { 0x699e, 0x0000 }, + { 0x699f, 0x0000 }, + { 0x69a0, 0x0000 }, + { 0x69a1, 0x0000 }, + { 0x69a2, 0x0000 }, + { 0x69a3, 0x0000 }, + { 0x69a4, 0x0000 }, + { 0x69a5, 0x0000 }, + { 0x69a6, 0x0000 }, + { 0x69a7, 0x0000 }, + { 0x69a8, 0x0000 }, + { 0x69a9, 0x0000 }, + { 0x69aa, 0x0000 }, + { 0x69ab, 0x0000 }, + { 0x69ac, 0x0000 }, + { 0x69ad, 0x0000 }, + { 0x69ae, 0x0000 }, + { 0x69af, 0x0000 }, + { 0x69b0, 0x0000 }, + { 0x69b1, 0x0000 }, + { 0x69b2, 0x0000 }, + { 0x69b3, 0x0000 }, + { 0x69b4, 0x0000 }, + { 0x69b5, 0x0000 }, + { 0x69b6, 0x0000 }, + { 0x69b7, 0x0000 }, + { 0x69b8, 0x0000 }, + { 0x69b9, 0x0000 }, + { 0x69ba, 0x0000 }, + { 0x69bb, 0x0000 }, + { 0x69bc, 0x0000 }, + { 0x69bd, 0x0000 }, + { 0x69be, 0x0000 }, + { 0x69bf, 0x0000 }, + { 0x69c0, 0x0000 }, + { 0x69c1, 0x0000 }, + { 0x69c2, 0x0000 }, + { 0x69c3, 0x0000 }, + { 0x69c4, 0x0000 }, + { 0x69c5, 0x0000 }, + { 0x69c6, 0x0000 }, + { 0x69c7, 0x0000 }, + { 0x69c8, 0x0000 }, + { 0x69c9, 0x0000 }, + { 0x69ca, 0x0000 }, + { 0x69cb, 0x0000 }, + { 0x69cc, 0x0000 }, + { 0x69cd, 0x0000 }, + { 0x69ce, 0x0000 }, + { 0x69cf, 0x0000 }, + { 0x69d0, 0x0000 }, + { 0x69d1, 0x0000 }, + { 0x69d2, 0x0000 }, + { 0x69d3, 0x0000 }, + { 0x69d4, 0x0000 }, + { 0x69d5, 0x0000 }, + { 0x69d6, 0x0000 }, + { 0x69d7, 0x0000 }, + { 0x69d8, 0x0000 }, + { 0x69d9, 0x0000 }, + { 0x69da, 0x0000 }, + { 0x69db, 0x0000 }, + { 0x69dc, 0x0000 }, + { 0x69dd, 0x0000 }, + { 0x69de, 0x0000 }, + { 0x69df, 0x0000 }, + { 0x69e0, 0x0000 }, + { 0x69e1, 0x0000 }, + { 0x69e2, 0x0000 }, + { 0x69e3, 0x0000 }, + { 0x69e4, 0x0000 }, + { 0x69e5, 0x0000 }, + { 0x69e6, 0x0000 }, + { 0x69e7, 0x0000 }, + { 0x69e8, 0x0000 }, + { 0x69e9, 0x0000 }, + { 0x69ea, 0x0000 }, + { 0x69eb, 0x0000 }, + { 0x69ec, 0x0000 }, + { 0x69ed, 0x0000 }, + { 0x69ee, 0x0000 }, + { 0x69ef, 0x0000 }, + { 0x69f0, 0x0000 }, + { 0x69f1, 0x0000 }, + { 0x69f2, 0x0000 }, + { 0x69f3, 0x0000 }, + { 0x69f4, 0x0000 }, + { 0x69f5, 0x0000 }, + { 0x69f6, 0x0000 }, + { 0x69f7, 0x0000 }, + { 0x69f8, 0x0000 }, + { 0x69f9, 0x0000 }, + { 0x69fa, 0x0000 }, + { 0x69fb, 0x0000 }, + { 0x69fc, 0x0000 }, + { 0x69fd, 0x0000 }, + { 0x69fe, 0x0000 }, + { 0x69ff, 0x0000 }, + { 0x6a00, 0x0000 }, + { 0x6a01, 0x0000 }, + { 0x6a02, 0x0000 }, + { 0x6a03, 0x0000 }, + { 0x6a04, 0x0000 }, + { 0x6a05, 0x0000 }, + { 0x6a06, 0x0000 }, + { 0x6a07, 0x0000 }, + { 0x6a08, 0x0000 }, + { 0x6a09, 0x0000 }, + { 0x6a0a, 0x0000 }, + { 0x6a0b, 0x0000 }, + { 0x6a0c, 0x0000 }, + { 0x6a0d, 0x0000 }, + { 0x6a0e, 0x0000 }, + { 0x6a0f, 0x0000 }, + { 0x6a10, 0x0000 }, + { 0x6a11, 0x0000 }, + { 0x6a12, 0x0000 }, + { 0x6a13, 0x0000 }, + { 0x6a14, 0x0000 }, + { 0x6a15, 0x0000 }, + { 0x6a16, 0x0000 }, + { 0x6a17, 0x0000 }, + { 0x6a18, 0x0000 }, + { 0x6a19, 0x0000 }, + { 0x6a1a, 0x0000 }, + { 0x6a1b, 0x0000 }, + { 0x6a1c, 0x0000 }, + { 0x6a1d, 0x0000 }, + { 0x6a1e, 0x0000 }, + { 0x6a1f, 0x0000 }, + { 0x6a20, 0x0000 }, + { 0x6a21, 0x0000 }, + { 0x6a22, 0x0000 }, + { 0x6a23, 0x0000 }, + { 0x6a24, 0x0000 }, + { 0x6a25, 0x0000 }, + { 0x6a26, 0x0000 }, + { 0x6a27, 0x0000 }, + { 0x6a28, 0x0000 }, + { 0x6a29, 0x0000 }, + { 0x6a2a, 0x0000 }, + { 0x6a2b, 0x0000 }, + { 0x6a2c, 0x0000 }, + { 0x6a2d, 0x0000 }, + { 0x6a2e, 0x0000 }, + { 0x6a2f, 0x0000 }, + { 0x6a30, 0x0000 }, + { 0x6a31, 0x0000 }, + { 0x6a32, 0x0000 }, + { 0x6a33, 0x0000 }, + { 0x6a34, 0x0000 }, + { 0x6a35, 0x0000 }, + { 0x6a36, 0x0000 }, + { 0x6a37, 0x0000 }, + { 0x6a38, 0x0000 }, + { 0x6a39, 0x0000 }, + { 0x6a3a, 0x0000 }, + { 0x6a3b, 0x0000 }, + { 0x6a3c, 0x0000 }, + { 0x6a3d, 0x0000 }, + { 0x6a3e, 0x0000 }, + { 0x6a3f, 0x0000 }, + { 0x6a40, 0x0000 }, + { 0x6a41, 0x0000 }, + { 0x6a42, 0x0000 }, + { 0x6a43, 0x0000 }, + { 0x6a44, 0x0000 }, + { 0x6a45, 0x0000 }, + { 0x6a46, 0x0000 }, + { 0x6a47, 0x0000 }, + { 0x6a48, 0x0000 }, + { 0x6a49, 0x0000 }, + { 0x6a4a, 0x0000 }, + { 0x6a4b, 0x0000 }, + { 0x6a4c, 0x0000 }, + { 0x6a4d, 0x0000 }, + { 0x6a4e, 0x0000 }, + { 0x6a4f, 0x0000 }, + { 0x6a50, 0x0000 }, + { 0x6a51, 0x0000 }, + { 0x6a52, 0x0000 }, + { 0x6a53, 0x0000 }, + { 0x6a54, 0x0000 }, + { 0x6a55, 0x0000 }, + { 0x6a56, 0x0000 }, + { 0x6a57, 0x0000 }, + { 0x6a58, 0x0000 }, + { 0x6a59, 0x0000 }, + { 0x6a5a, 0x0000 }, + { 0x6a5b, 0x0000 }, + { 0x6a5c, 0x0000 }, + { 0x6a5d, 0x0000 }, + { 0x6a5e, 0x0000 }, + { 0x6a5f, 0x0000 }, + { 0x6a60, 0x0000 }, + { 0x6a61, 0x0000 }, + { 0x6a62, 0x0000 }, + { 0x6a63, 0x0000 }, + { 0x6a64, 0x0000 }, + { 0x6a65, 0x0000 }, + { 0x6a66, 0x0000 }, + { 0x6a67, 0x0000 }, + { 0x6a68, 0x0000 }, + { 0x6a69, 0x0000 }, + { 0x6a6a, 0x0000 }, + { 0x6a6b, 0x0000 }, + { 0x6a6c, 0x0000 }, + { 0x6a6d, 0x0000 }, + { 0x6a6e, 0x0000 }, + { 0x6a6f, 0x0000 }, + { 0x6a70, 0x0000 }, + { 0x6a71, 0x0000 }, + { 0x6a72, 0x0000 }, + { 0x6a73, 0x0000 }, + { 0x6a74, 0x0000 }, + { 0x6a75, 0x0000 }, + { 0x6a76, 0x0000 }, + { 0x6a77, 0x0000 }, + { 0x6a78, 0x0000 }, + { 0x6a79, 0x0000 }, + { 0x6a7a, 0x0000 }, + { 0x6a7b, 0x0000 }, + { 0x6a7c, 0x0000 }, + { 0x6a7d, 0x0000 }, + { 0x6a7e, 0x0000 }, + { 0x6a7f, 0x0000 }, + { 0x6a80, 0x0000 }, + { 0x6a81, 0x0000 }, + { 0x6a82, 0x0000 }, + { 0x6a83, 0x0000 }, + { 0x6a84, 0x0000 }, + { 0x6a85, 0x0000 }, + { 0x6a86, 0x0000 }, + { 0x6a87, 0x0000 }, + { 0x6a88, 0x0000 }, + { 0x6a89, 0x0000 }, + { 0x6a8a, 0x0000 }, + { 0x6a8b, 0x0000 }, + { 0x6a8c, 0x0000 }, + { 0x6a8d, 0x0000 }, + { 0x6a8e, 0x0000 }, + { 0x6a8f, 0x0000 }, + { 0x6a90, 0x0000 }, + { 0x6a91, 0x0000 }, + { 0x6a92, 0x0000 }, + { 0x6a93, 0x0000 }, + { 0x6a94, 0x0000 }, + { 0x6a95, 0x0000 }, + { 0x6a96, 0x0000 }, + { 0x6a97, 0x0000 }, + { 0x6a98, 0x0000 }, + { 0x6a99, 0x0000 }, + { 0x6a9a, 0x0000 }, + { 0x6a9b, 0x0000 }, + { 0x6a9c, 0x0000 }, + { 0x6a9d, 0x0000 }, + { 0x6a9e, 0x0000 }, + { 0x6a9f, 0x0000 }, + { 0x6aa0, 0x0000 }, + { 0x6aa1, 0x0000 }, + { 0x6aa2, 0x0000 }, + { 0x6aa3, 0x0000 }, + { 0x6aa4, 0x0000 }, + { 0x6aa5, 0x0000 }, + { 0x6aa6, 0x0000 }, + { 0x6aa7, 0x0000 }, + { 0x6aa8, 0x0000 }, + { 0x6aa9, 0x0000 }, + { 0x6aaa, 0x0000 }, + { 0x6aab, 0x0000 }, + { 0x6aac, 0x0000 }, + { 0x6aad, 0x0000 }, + { 0x6aae, 0x0000 }, + { 0x6aaf, 0x0000 }, + { 0x6ab0, 0x0000 }, + { 0x6ab1, 0x0000 }, + { 0x6ab2, 0x0000 }, + { 0x6ab3, 0x0000 }, + { 0x6ab4, 0x0000 }, + { 0x6ab5, 0x0000 }, + { 0x6ab6, 0x0000 }, + { 0x6ab7, 0x0000 }, + { 0x6ab8, 0x0000 }, + { 0x6ab9, 0x0000 }, + { 0x6aba, 0x0000 }, + { 0x6abb, 0x0000 }, + { 0x6abc, 0x0000 }, + { 0x6abd, 0x0000 }, + { 0x6abe, 0x0000 }, + { 0x6abf, 0x0000 }, + { 0x6ac0, 0x0000 }, + { 0x6ac1, 0x0000 }, + { 0x6ac2, 0x0000 }, + { 0x6ac3, 0x0000 }, + { 0x6ac4, 0x0000 }, + { 0x6ac5, 0x0000 }, + { 0x6ac6, 0x0000 }, + { 0x6ac7, 0x0000 }, + { 0x6ac8, 0x0000 }, + { 0x6ac9, 0x0000 }, + { 0x6aca, 0x0000 }, + { 0x6acb, 0x0000 }, + { 0x6acc, 0x0000 }, + { 0x6acd, 0x0000 }, + { 0x6ace, 0x0000 }, + { 0x6acf, 0x0000 }, + { 0x6ad0, 0x0000 }, + { 0x6ad1, 0x0000 }, + { 0x6ad2, 0x0000 }, + { 0x6ad3, 0x0000 }, + { 0x6ad4, 0x0000 }, + { 0x6ad5, 0x0000 }, + { 0x6ad6, 0x0000 }, + { 0x6ad7, 0x0000 }, + { 0x6ad8, 0x0000 }, + { 0x6ad9, 0x0000 }, + { 0x6ada, 0x0000 }, + { 0x6adb, 0x0000 }, + { 0x6adc, 0x0000 }, + { 0x6add, 0x0000 }, + { 0x6ade, 0x0000 }, + { 0x6adf, 0x0000 }, + { 0x6ae0, 0x0000 }, + { 0x6ae1, 0x0000 }, + { 0x6ae2, 0x0000 }, + { 0x6ae3, 0x0000 }, + { 0x6ae4, 0x0000 }, + { 0x6ae5, 0x0000 }, + { 0x6ae6, 0x0000 }, + { 0x6ae7, 0x0000 }, + { 0x6ae8, 0x0000 }, + { 0x6ae9, 0x0000 }, + { 0x6aea, 0x0000 }, + { 0x6aeb, 0x0000 }, + { 0x6aec, 0x0000 }, + { 0x6aed, 0x0000 }, + { 0x6aee, 0x0000 }, + { 0x6aef, 0x0000 }, + { 0x6af0, 0x0000 }, + { 0x6af1, 0x0000 }, + { 0x6af2, 0x0000 }, + { 0x6af3, 0x0000 }, + { 0x6af4, 0x0000 }, + { 0x6af5, 0x0000 }, + { 0x6af6, 0x0000 }, + { 0x6af7, 0x0000 }, + { 0x6af8, 0x0000 }, + { 0x6af9, 0x0000 }, + { 0x6afa, 0x0000 }, + { 0x6afb, 0x0000 }, + { 0x6afc, 0x0000 }, + { 0x6afd, 0x0000 }, + { 0x6afe, 0x0000 }, + { 0x6aff, 0x0000 }, + { 0x6b00, 0x0000 }, + { 0x6b01, 0x0000 }, + { 0x6b02, 0x0000 }, + { 0x6b03, 0x0000 }, + { 0x6b04, 0x0000 }, + { 0x6b05, 0x0000 }, + { 0x6b06, 0x0000 }, + { 0x6b07, 0x0000 }, + { 0x6b08, 0x0000 }, + { 0x6b09, 0x0000 }, + { 0x6b0a, 0x0000 }, + { 0x6b0b, 0x0000 }, + { 0x6b0c, 0x0000 }, + { 0x6b0d, 0x0000 }, + { 0x6b0e, 0x0000 }, + { 0x6b0f, 0x0000 }, + { 0x6b10, 0x0000 }, + { 0x6b11, 0x0000 }, + { 0x6b12, 0x0000 }, + { 0x6b13, 0x0000 }, + { 0x6b14, 0x0000 }, + { 0x6b15, 0x0000 }, + { 0x6b16, 0x0000 }, + { 0x6b17, 0x0000 }, + { 0x6b18, 0x0000 }, + { 0x6b19, 0x0000 }, + { 0x6b1a, 0x0000 }, + { 0x6b1b, 0x0000 }, + { 0x6b1c, 0x0000 }, + { 0x6b1d, 0x0000 }, + { 0x6b1e, 0x0000 }, + { 0x6b1f, 0x0000 }, + { 0x6b20, 0x0000 }, + { 0x6b21, 0x0000 }, + { 0x6b22, 0x0000 }, + { 0x6b23, 0x0000 }, + { 0x6b24, 0x0000 }, + { 0x6b25, 0x0000 }, + { 0x6b26, 0x0000 }, + { 0x6b27, 0x0000 }, + { 0x6b28, 0x0000 }, + { 0x6b29, 0x0000 }, + { 0x6b2a, 0x0000 }, + { 0x6b2b, 0x0000 }, + { 0x6b2c, 0x0000 }, + { 0x6b2d, 0x0000 }, + { 0x6b2e, 0x0000 }, + { 0x6b2f, 0x0000 }, + { 0x6b30, 0x0000 }, + { 0x6b31, 0x0000 }, + { 0x6b32, 0x0000 }, + { 0x6b33, 0x0000 }, + { 0x6b34, 0x0000 }, + { 0x6b35, 0x0000 }, + { 0x6b36, 0x0000 }, + { 0x6b37, 0x0000 }, + { 0x6b38, 0x0000 }, + { 0x6b39, 0x0000 }, + { 0x6b3a, 0x0000 }, + { 0x6b3b, 0x0000 }, + { 0x6b3c, 0x0000 }, + { 0x6b3d, 0x0000 }, + { 0x6b3e, 0x0000 }, + { 0x6b3f, 0x0000 }, + { 0x6b40, 0x0000 }, + { 0x6b41, 0x0000 }, + { 0x6b42, 0x0000 }, + { 0x6b43, 0x0000 }, + { 0x6b44, 0x0000 }, + { 0x6b45, 0x0000 }, + { 0x6b46, 0x0000 }, + { 0x6b47, 0x0000 }, + { 0x6b48, 0x0000 }, + { 0x6b49, 0x0000 }, + { 0x6b4a, 0x0000 }, + { 0x6b4b, 0x0000 }, + { 0x6b4c, 0x0000 }, + { 0x6b4d, 0x0000 }, + { 0x6b4e, 0x0000 }, + { 0x6b4f, 0x0000 }, + { 0x6b50, 0x0000 }, + { 0x6b51, 0x0000 }, + { 0x6b52, 0x0000 }, + { 0x6b53, 0x0000 }, + { 0x6b54, 0x0000 }, + { 0x6b55, 0x0000 }, + { 0x6b56, 0x0000 }, + { 0x6b57, 0x0000 }, + { 0x6b58, 0x0000 }, + { 0x6b59, 0x0000 }, + { 0x6b5a, 0x0000 }, + { 0x6b5b, 0x0000 }, + { 0x6b5c, 0x0000 }, + { 0x6b5d, 0x0000 }, + { 0x6b5e, 0x0000 }, + { 0x6b5f, 0x0000 }, + { 0x6b60, 0x0000 }, + { 0x6b61, 0x0000 }, + { 0x6b62, 0x0000 }, + { 0x6b63, 0x0000 }, + { 0x6b64, 0x0000 }, + { 0x6b65, 0x0000 }, + { 0x6b66, 0x0000 }, + { 0x6b67, 0x0000 }, + { 0x6b68, 0x0000 }, + { 0x6b69, 0x0000 }, + { 0x6b6a, 0x0000 }, + { 0x6b6b, 0x0000 }, + { 0x6b6c, 0x0000 }, + { 0x6b6d, 0x0000 }, + { 0x6b6e, 0x0000 }, + { 0x6b6f, 0x0000 }, + { 0x6b70, 0x0000 }, + { 0x6b71, 0x0000 }, + { 0x6b72, 0x0000 }, + { 0x6b73, 0x0000 }, + { 0x6b74, 0x0000 }, + { 0x6b75, 0x0000 }, + { 0x6b76, 0x0000 }, + { 0x6b77, 0x0000 }, + { 0x6b78, 0x0000 }, + { 0x6b79, 0x0000 }, + { 0x6b7a, 0x0000 }, + { 0x6b7b, 0x0000 }, + { 0x6b7c, 0x0000 }, + { 0x6b7d, 0x0000 }, + { 0x6b7e, 0x0000 }, + { 0x6b7f, 0x0000 }, + { 0x6b80, 0x0000 }, + { 0x6b81, 0x0000 }, + { 0x6b82, 0x0000 }, + { 0x6b83, 0x0000 }, + { 0x6b84, 0x0000 }, + { 0x6b85, 0x0000 }, + { 0x6b86, 0x0000 }, + { 0x6b87, 0x0000 }, + { 0x6b88, 0x0000 }, + { 0x6b89, 0x0000 }, + { 0x6b8a, 0x0000 }, + { 0x6b8b, 0x0000 }, + { 0x6b8c, 0x0000 }, + { 0x6b8d, 0x0000 }, + { 0x6b8e, 0x0000 }, + { 0x6b8f, 0x0000 }, + { 0x6b90, 0x0000 }, + { 0x6b91, 0x0000 }, + { 0x6b92, 0x0000 }, + { 0x6b93, 0x0000 }, + { 0x6b94, 0x0000 }, + { 0x6b95, 0x0000 }, + { 0x6b96, 0x0000 }, + { 0x6b97, 0x0000 }, + { 0x6b98, 0x0000 }, + { 0x6b99, 0x0000 }, + { 0x6b9a, 0x0000 }, + { 0x6b9b, 0x0000 }, + { 0x6b9c, 0x0000 }, + { 0x6b9d, 0x0000 }, + { 0x6b9e, 0x0000 }, + { 0x6b9f, 0x0000 }, + { 0x6ba0, 0x0000 }, + { 0x6ba1, 0x0000 }, + { 0x6ba2, 0x0000 }, + { 0x6ba3, 0x0000 }, + { 0x6ba4, 0x0000 }, + { 0x6ba5, 0x0000 }, + { 0x6ba6, 0x0000 }, + { 0x6ba7, 0x0000 }, + { 0x6ba8, 0x0000 }, + { 0x6ba9, 0x0000 }, + { 0x6baa, 0x0000 }, + { 0x6bab, 0x0000 }, + { 0x6bac, 0x0000 }, + { 0x6bad, 0x0000 }, + { 0x6bae, 0x0000 }, + { 0x6baf, 0x0000 }, + { 0x6bb0, 0x0000 }, + { 0x6bb1, 0x0000 }, + { 0x6bb2, 0x0000 }, + { 0x6bb3, 0x0000 }, + { 0x6bb4, 0x0000 }, + { 0x6bb5, 0x0000 }, + { 0x6bb6, 0x0000 }, + { 0x6bb7, 0x0000 }, + { 0x6bb8, 0x0000 }, + { 0x6bb9, 0x0000 }, + { 0x6bba, 0x0000 }, + { 0x6bbb, 0x0000 }, + { 0x6bbc, 0x0000 }, + { 0x6bbd, 0x0000 }, + { 0x6bbe, 0x0000 }, + { 0x6bbf, 0x0000 }, + { 0x6bc0, 0x0000 }, + { 0x6bc1, 0x0000 }, + { 0x6bc2, 0x0000 }, + { 0x6bc3, 0x0000 }, + { 0x6bc4, 0x0000 }, + { 0x6bc5, 0x0000 }, + { 0x6bc6, 0x0000 }, + { 0x6bc7, 0x0000 }, + { 0x6bc8, 0x0000 }, + { 0x6bc9, 0x0000 }, + { 0x6bca, 0x0000 }, + { 0x6bcb, 0x0000 }, + { 0x6bcc, 0x0000 }, + { 0x6bcd, 0x0000 }, + { 0x6bce, 0x0000 }, + { 0x6bcf, 0x0000 }, + { 0x6bd0, 0x0000 }, + { 0x6bd1, 0x0000 }, + { 0x6bd2, 0x0000 }, + { 0x6bd3, 0x0000 }, + { 0x6bd4, 0x0000 }, + { 0x6bd5, 0x0000 }, + { 0x6bd6, 0x0000 }, + { 0x6bd7, 0x0000 }, + { 0x6bd8, 0x0000 }, + { 0x6bd9, 0x0000 }, + { 0x6bda, 0x0000 }, + { 0x6bdb, 0x0000 }, + { 0x6bdc, 0x0000 }, + { 0x6bdd, 0x0000 }, + { 0x6bde, 0x0000 }, + { 0x6bdf, 0x0000 }, + { 0x6be0, 0x0000 }, + { 0x6be1, 0x0000 }, + { 0x6be2, 0x0000 }, + { 0x6be3, 0x0000 }, + { 0x6be4, 0x0000 }, + { 0x6be5, 0x0000 }, + { 0x6be6, 0x0000 }, + { 0x6be7, 0x0000 }, + { 0x6be8, 0x0000 }, + { 0x6be9, 0x0000 }, + { 0x6bea, 0x0000 }, + { 0x6beb, 0x0000 }, + { 0x6bec, 0x0000 }, + { 0x6bed, 0x0000 }, + { 0x6bee, 0x0000 }, + { 0x6bef, 0x0000 }, + { 0x6bf0, 0x0000 }, + { 0x6bf1, 0x0000 }, + { 0x6bf2, 0x0000 }, + { 0x6bf3, 0x0000 }, + { 0x6bf4, 0x0000 }, + { 0x6bf5, 0x0000 }, + { 0x6bf6, 0x0000 }, + { 0x6bf7, 0x0000 }, + { 0x6bf8, 0x0000 }, + { 0x6bf9, 0x0000 }, + { 0x6bfa, 0x0000 }, + { 0x6bfb, 0x0000 }, + { 0x6bfc, 0x0000 }, + { 0x6bfd, 0x0000 }, + { 0x6bfe, 0x0000 }, + { 0x6bff, 0x0000 }, + { 0x6c00, 0x0000 }, + { 0x6c01, 0x0000 }, + { 0x6c02, 0x0000 }, + { 0x6c03, 0x0000 }, + { 0x6c04, 0x0000 }, + { 0x6c05, 0x0000 }, + { 0x6c06, 0x0000 }, + { 0x6c07, 0x0000 }, + { 0x6c08, 0x0000 }, + { 0x6c09, 0x0000 }, + { 0x6c0a, 0x0000 }, + { 0x6c0b, 0x0000 }, + { 0x6c0c, 0x0000 }, + { 0x6c0d, 0x0000 }, + { 0x6c0e, 0x0000 }, + { 0x6c0f, 0x0000 }, + { 0x6c10, 0x0000 }, + { 0x6c11, 0x0000 }, + { 0x6c12, 0x0000 }, + { 0x6c13, 0x0000 }, + { 0x6c14, 0x0000 }, + { 0x6c15, 0x0000 }, + { 0x6c16, 0x0000 }, + { 0x6c17, 0x0000 }, + { 0x6c18, 0x0000 }, + { 0x6c19, 0x0000 }, + { 0x6c1a, 0x0000 }, + { 0x6c1b, 0x0000 }, + { 0x6c1c, 0x0000 }, + { 0x6c1d, 0x0000 }, + { 0x6c1e, 0x0000 }, + { 0x6c1f, 0x0000 }, + { 0x6c20, 0x0000 }, + { 0x6c21, 0x0000 }, + { 0x6c22, 0x0000 }, + { 0x6c23, 0x0000 }, + { 0x6c24, 0x0000 }, + { 0x6c25, 0x0000 }, + { 0x6c26, 0x0000 }, + { 0x6c27, 0x0000 }, + { 0x6c28, 0x0000 }, + { 0x6c29, 0x0000 }, + { 0x6c2a, 0x0000 }, + { 0x6c2b, 0x0000 }, + { 0x6c2c, 0x0000 }, + { 0x6c2d, 0x0000 }, + { 0x6c2e, 0x0000 }, + { 0x6c2f, 0x0000 }, + { 0x6c30, 0x0000 }, + { 0x6c31, 0x0000 }, + { 0x6c32, 0x0000 }, + { 0x6c33, 0x0000 }, + { 0x6c34, 0x0000 }, + { 0x6c35, 0x0000 }, + { 0x6c36, 0x0000 }, + { 0x6c37, 0x0000 }, + { 0x6c38, 0x0000 }, + { 0x6c39, 0x0000 }, + { 0x6c3a, 0x0000 }, + { 0x6c3b, 0x0000 }, + { 0x6c3c, 0x0000 }, + { 0x6c3d, 0x0000 }, + { 0x6c3e, 0x0000 }, + { 0x6c3f, 0x0000 }, + { 0x6c40, 0x0000 }, + { 0x6c41, 0x0000 }, + { 0x6c42, 0x0000 }, + { 0x6c43, 0x0000 }, + { 0x6c44, 0x0000 }, + { 0x6c45, 0x0000 }, + { 0x6c46, 0x0000 }, + { 0x6c47, 0x0000 }, + { 0x6c48, 0x0000 }, + { 0x6c49, 0x0000 }, + { 0x6c4a, 0x0000 }, + { 0x6c4b, 0x0000 }, + { 0x6c4c, 0x0000 }, + { 0x6c4d, 0x0000 }, + { 0x6c4e, 0x0000 }, + { 0x6c4f, 0x0000 }, + { 0x6c50, 0x0000 }, + { 0x6c51, 0x0000 }, + { 0x6c52, 0x0000 }, + { 0x6c53, 0x0000 }, + { 0x6c54, 0x0000 }, + { 0x6c55, 0x0000 }, + { 0x6c56, 0x0000 }, + { 0x6c57, 0x0000 }, + { 0x6c58, 0x0000 }, + { 0x6c59, 0x0000 }, + { 0x6c5a, 0x0000 }, + { 0x6c5b, 0x0000 }, + { 0x6c5c, 0x0000 }, + { 0x6c5d, 0x0000 }, + { 0x6c5e, 0x0000 }, + { 0x6c5f, 0x0000 }, + { 0x6c60, 0x0000 }, + { 0x6c61, 0x0000 }, + { 0x6c62, 0x0000 }, + { 0x6c63, 0x0000 }, + { 0x6c64, 0x0000 }, + { 0x6c65, 0x0000 }, + { 0x6c66, 0x0000 }, + { 0x6c67, 0x0000 }, + { 0x6c68, 0x0000 }, + { 0x6c69, 0x0000 }, + { 0x6c6a, 0x0000 }, + { 0x6c6b, 0x0000 }, + { 0x6c6c, 0x0000 }, + { 0x6c6d, 0x0000 }, + { 0x6c6e, 0x0000 }, + { 0x6c6f, 0x0000 }, + { 0x6c70, 0x0000 }, + { 0x6c71, 0x0000 }, + { 0x6c72, 0x0000 }, + { 0x6c73, 0x0000 }, + { 0x6c74, 0x0000 }, + { 0x6c75, 0x0000 }, + { 0x6c76, 0x0000 }, + { 0x6c77, 0x0000 }, + { 0x6c78, 0x0000 }, + { 0x6c79, 0x0000 }, + { 0x6c7a, 0x0000 }, + { 0x6c7b, 0x0000 }, + { 0x6c7c, 0x0000 }, + { 0x6c7d, 0x0000 }, + { 0x6c7e, 0x0000 }, + { 0x6c7f, 0x0000 }, + { 0x6c80, 0x0000 }, + { 0x6c81, 0x0000 }, + { 0x6c82, 0x0000 }, + { 0x6c83, 0x0000 }, + { 0x6c84, 0x0000 }, + { 0x6c85, 0x0000 }, + { 0x6c86, 0x0000 }, + { 0x6c87, 0x0000 }, + { 0x6c88, 0x0000 }, + { 0x6c89, 0x0000 }, + { 0x6c8a, 0x0000 }, + { 0x6c8b, 0x0000 }, + { 0x6c8c, 0x0000 }, + { 0x6c8d, 0x0000 }, + { 0x6c8e, 0x0000 }, + { 0x6c8f, 0x0000 }, + { 0x6c90, 0x0000 }, + { 0x6c91, 0x0000 }, + { 0x6c92, 0x0000 }, + { 0x6c93, 0x0000 }, + { 0x6c94, 0x0000 }, + { 0x6c95, 0x0000 }, + { 0x6c96, 0x0000 }, + { 0x6c97, 0x0000 }, + { 0x6c98, 0x0000 }, + { 0x6c99, 0x0000 }, + { 0x6c9a, 0x0000 }, + { 0x6c9b, 0x0000 }, + { 0x6c9c, 0x0000 }, + { 0x6c9d, 0x0000 }, + { 0x6c9e, 0x0000 }, + { 0x6c9f, 0x0000 }, + { 0x6ca0, 0x0000 }, + { 0x6ca1, 0x0000 }, + { 0x6ca2, 0x0000 }, + { 0x6ca3, 0x0000 }, + { 0x6ca4, 0x0000 }, + { 0x6ca5, 0x0000 }, + { 0x6ca6, 0x0000 }, + { 0x6ca7, 0x0000 }, + { 0x6ca8, 0x0000 }, + { 0x6ca9, 0x0000 }, + { 0x6caa, 0x0000 }, + { 0x6cab, 0x0000 }, + { 0x6cac, 0x0000 }, + { 0x6cad, 0x0000 }, + { 0x6cae, 0x0000 }, + { 0x6caf, 0x0000 }, + { 0x6cb0, 0x0000 }, + { 0x6cb1, 0x0000 }, + { 0x6cb2, 0x0000 }, + { 0x6cb3, 0x0000 }, + { 0x6cb4, 0x0000 }, + { 0x6cb5, 0x0000 }, + { 0x6cb6, 0x0000 }, + { 0x6cb7, 0x0000 }, + { 0x6cb8, 0x0000 }, + { 0x6cb9, 0x0000 }, + { 0x6cba, 0x0000 }, + { 0x6cbb, 0x0000 }, + { 0x6cbc, 0x0000 }, + { 0x6cbd, 0x0000 }, + { 0x6cbe, 0x0000 }, + { 0x6cbf, 0x0000 }, + { 0x6cc0, 0x0000 }, + { 0x6cc1, 0x0000 }, + { 0x6cc2, 0x0000 }, + { 0x6cc3, 0x0000 }, + { 0x6cc4, 0x0000 }, + { 0x6cc5, 0x0000 }, + { 0x6cc6, 0x0000 }, + { 0x6cc7, 0x0000 }, + { 0x6cc8, 0x0000 }, + { 0x6cc9, 0x0000 }, + { 0x6cca, 0x0000 }, + { 0x6ccb, 0x0000 }, + { 0x6ccc, 0x0000 }, + { 0x6ccd, 0x0000 }, + { 0x6cce, 0x0000 }, + { 0x6ccf, 0x0000 }, + { 0x6cd0, 0x0000 }, + { 0x6cd1, 0x0000 }, + { 0x6cd2, 0x0000 }, + { 0x6cd3, 0x0000 }, + { 0x6cd4, 0x0000 }, + { 0x6cd5, 0x0000 }, + { 0x6cd6, 0x0000 }, + { 0x6cd7, 0x0000 }, + { 0x6cd8, 0x0000 }, + { 0x6cd9, 0x0000 }, + { 0x6cda, 0x0000 }, + { 0x6cdb, 0x0000 }, + { 0x6cdc, 0x0000 }, + { 0x6cdd, 0x0000 }, + { 0x6cde, 0x0000 }, + { 0x6cdf, 0x0000 }, + { 0x6ce0, 0x0000 }, + { 0x6ce1, 0x0000 }, + { 0x6ce2, 0x0000 }, + { 0x6ce3, 0x0000 }, + { 0x6ce4, 0x0000 }, + { 0x6ce5, 0x0000 }, + { 0x6ce6, 0x0000 }, + { 0x6ce7, 0x0000 }, + { 0x6ce8, 0x0000 }, + { 0x6ce9, 0x0000 }, + { 0x6cea, 0x0000 }, + { 0x6ceb, 0x0000 }, + { 0x6cec, 0x0000 }, + { 0x6ced, 0x0000 }, + { 0x6cee, 0x0000 }, + { 0x6cef, 0x0000 }, + { 0x6cf0, 0x0000 }, + { 0x6cf1, 0x0000 }, + { 0x6cf2, 0x0000 }, + { 0x6cf3, 0x0000 }, + { 0x6cf4, 0x0000 }, + { 0x6cf5, 0x0000 }, + { 0x6cf6, 0x0000 }, + { 0x6cf7, 0x0000 }, + { 0x6cf8, 0x0000 }, + { 0x6cf9, 0x0000 }, + { 0x6cfa, 0x0000 }, + { 0x6cfb, 0x0000 }, + { 0x6cfc, 0x0000 }, + { 0x6cfd, 0x0000 }, + { 0x6cfe, 0x0000 }, + { 0x6cff, 0x0000 }, + { 0x6d00, 0x0000 }, + { 0x6d01, 0x0000 }, + { 0x6d02, 0x0000 }, + { 0x6d03, 0x0000 }, + { 0x6d04, 0x0000 }, + { 0x6d05, 0x0000 }, + { 0x6d06, 0x0000 }, + { 0x6d07, 0x0000 }, + { 0x6d08, 0x0000 }, + { 0x6d09, 0x0000 }, + { 0x6d0a, 0x0000 }, + { 0x6d0b, 0x0000 }, + { 0x6d0c, 0x0000 }, + { 0x6d0d, 0x0000 }, + { 0x6d0e, 0x0000 }, + { 0x6d0f, 0x0000 }, + { 0x6d10, 0x0000 }, + { 0x6d11, 0x0000 }, + { 0x6d12, 0x0000 }, + { 0x6d13, 0x0000 }, + { 0x6d14, 0x0000 }, + { 0x6d15, 0x0000 }, + { 0x6d16, 0x0000 }, + { 0x6d17, 0x0000 }, + { 0x6d18, 0x0000 }, + { 0x6d19, 0x0000 }, + { 0x6d1a, 0x0000 }, + { 0x6d1b, 0x0000 }, + { 0x6d1c, 0x0000 }, + { 0x6d1d, 0x0000 }, + { 0x6d1e, 0x0000 }, + { 0x6d1f, 0x0000 }, + { 0x6d20, 0x0000 }, + { 0x6d21, 0x0000 }, + { 0x6d22, 0x0000 }, + { 0x6d23, 0x0000 }, + { 0x6d24, 0x0000 }, + { 0x6d25, 0x0000 }, + { 0x6d26, 0x0000 }, + { 0x6d27, 0x0000 }, + { 0x6d28, 0x0000 }, + { 0x6d29, 0x0000 }, + { 0x6d2a, 0x0000 }, + { 0x6d2b, 0x0000 }, + { 0x6d2c, 0x0000 }, + { 0x6d2d, 0x0000 }, + { 0x6d2e, 0x0000 }, + { 0x6d2f, 0x0000 }, + { 0x6d30, 0x0000 }, + { 0x6d31, 0x0000 }, + { 0x6d32, 0x0000 }, + { 0x6d33, 0x0000 }, + { 0x6d34, 0x0000 }, + { 0x6d35, 0x0000 }, + { 0x6d36, 0x0000 }, + { 0x6d37, 0x0000 }, + { 0x6d38, 0x0000 }, + { 0x6d39, 0x0000 }, + { 0x6d3a, 0x0000 }, + { 0x6d3b, 0x0000 }, + { 0x6d3c, 0x0000 }, + { 0x6d3d, 0x0000 }, + { 0x6d3e, 0x0000 }, + { 0x6d3f, 0x0000 }, + { 0x6d40, 0x0000 }, + { 0x6d41, 0x0000 }, + { 0x6d42, 0x0000 }, + { 0x6d43, 0x0000 }, + { 0x6d44, 0x0000 }, + { 0x6d45, 0x0000 }, + { 0x6d46, 0x0000 }, + { 0x6d47, 0x0000 }, + { 0x6d48, 0x0000 }, + { 0x6d49, 0x0000 }, + { 0x6d4a, 0x0000 }, + { 0x6d4b, 0x0000 }, + { 0x6d4c, 0x0000 }, + { 0x6d4d, 0x0000 }, + { 0x6d4e, 0x0000 }, + { 0x6d4f, 0x0000 }, + { 0x6d50, 0x0000 }, + { 0x6d51, 0x0000 }, + { 0x6d52, 0x0000 }, + { 0x6d53, 0x0000 }, + { 0x6d54, 0x0000 }, + { 0x6d55, 0x0000 }, + { 0x6d56, 0x0000 }, + { 0x6d57, 0x0000 }, + { 0x6d58, 0x0000 }, + { 0x6d59, 0x0000 }, + { 0x6d5a, 0x0000 }, + { 0x6d5b, 0x0000 }, + { 0x6d5c, 0x0000 }, + { 0x6d5d, 0x0000 }, + { 0x6d5e, 0x0000 }, + { 0x6d5f, 0x0000 }, + { 0x6d60, 0x0000 }, + { 0x6d61, 0x0000 }, + { 0x6d62, 0x0000 }, + { 0x6d63, 0x0000 }, + { 0x6d64, 0x0000 }, + { 0x6d65, 0x0000 }, + { 0x6d66, 0x0000 }, + { 0x6d67, 0x0000 }, + { 0x6d68, 0x0000 }, + { 0x6d69, 0x0000 }, + { 0x6d6a, 0x0000 }, + { 0x6d6b, 0x0000 }, + { 0x6d6c, 0x0000 }, + { 0x6d6d, 0x0000 }, + { 0x6d6e, 0x0000 }, + { 0x6d6f, 0x0000 }, + { 0x6d70, 0x0000 }, + { 0x6d71, 0x0000 }, + { 0x6d72, 0x0000 }, + { 0x6d73, 0x0000 }, + { 0x6d74, 0x0000 }, + { 0x6d75, 0x0000 }, + { 0x6d76, 0x0000 }, + { 0x6d77, 0x0000 }, + { 0x6d78, 0x0000 }, + { 0x6d79, 0x0000 }, + { 0x6d7a, 0x0000 }, + { 0x6d7b, 0x0000 }, + { 0x6d7c, 0x0000 }, + { 0x6d7d, 0x0000 }, + { 0x6d7e, 0x0000 }, + { 0x6d7f, 0x0000 }, + { 0x6d80, 0x0000 }, + { 0x6d81, 0x0000 }, + { 0x6d82, 0x0000 }, + { 0x6d83, 0x0000 }, + { 0x6d84, 0x0000 }, + { 0x6d85, 0x0000 }, + { 0x6d86, 0x0000 }, + { 0x6d87, 0x0000 }, + { 0x6d88, 0x0000 }, + { 0x6d89, 0x0000 }, + { 0x6d8a, 0x0000 }, + { 0x6d8b, 0x0000 }, + { 0x6d8c, 0x0000 }, + { 0x6d8d, 0x0000 }, + { 0x6d8e, 0x0000 }, + { 0x6d8f, 0x0000 }, + { 0x6d90, 0x0000 }, + { 0x6d91, 0x0000 }, + { 0x6d92, 0x0000 }, + { 0x6d93, 0x0000 }, + { 0x6d94, 0x0000 }, + { 0x6d95, 0x0000 }, + { 0x6d96, 0x0000 }, + { 0x6d97, 0x0000 }, + { 0x6d98, 0x0000 }, + { 0x6d99, 0x0000 }, + { 0x6d9a, 0x0000 }, + { 0x6d9b, 0x0000 }, + { 0x6d9c, 0x0000 }, + { 0x6d9d, 0x0000 }, + { 0x6d9e, 0x0000 }, + { 0x6d9f, 0x0000 }, + { 0x6da0, 0x0000 }, + { 0x6da1, 0x0000 }, + { 0x6da2, 0x0000 }, + { 0x6da3, 0x0000 }, + { 0x6da4, 0x0000 }, + { 0x6da5, 0x0000 }, + { 0x6da6, 0x0000 }, + { 0x6da7, 0x0000 }, + { 0x6da8, 0x0000 }, + { 0x6da9, 0x0000 }, + { 0x6daa, 0x0000 }, + { 0x6dab, 0x0000 }, + { 0x6dac, 0x0000 }, + { 0x6dad, 0x0000 }, + { 0x6dae, 0x0000 }, + { 0x6daf, 0x0000 }, + { 0x6db0, 0x0000 }, + { 0x6db1, 0x0000 }, + { 0x6db2, 0x0000 }, + { 0x6db3, 0x0000 }, + { 0x6db4, 0x0000 }, + { 0x6db5, 0x0000 }, + { 0x6db6, 0x0000 }, + { 0x6db7, 0x0000 }, + { 0x6db8, 0x0000 }, + { 0x6db9, 0x0000 }, + { 0x6dba, 0x0000 }, + { 0x6dbb, 0x0000 }, + { 0x6dbc, 0x0000 }, + { 0x6dbd, 0x0000 }, + { 0x6dbe, 0x0000 }, + { 0x6dbf, 0x0000 }, + { 0x6dc0, 0x0000 }, + { 0x6dc1, 0x0000 }, + { 0x6dc2, 0x0000 }, + { 0x6dc3, 0x0000 }, + { 0x6dc4, 0x0000 }, + { 0x6dc5, 0x0000 }, + { 0x6dc6, 0x0000 }, + { 0x6dc7, 0x0000 }, + { 0x6dc8, 0x0000 }, + { 0x6dc9, 0x0000 }, + { 0x6dca, 0x0000 }, + { 0x6dcb, 0x0000 }, + { 0x6dcc, 0x0000 }, + { 0x6dcd, 0x0000 }, + { 0x6dce, 0x0000 }, + { 0x6dcf, 0x0000 }, + { 0x6dd0, 0x0000 }, + { 0x6dd1, 0x0000 }, + { 0x6dd2, 0x0000 }, + { 0x6dd3, 0x0000 }, + { 0x6dd4, 0x0000 }, + { 0x6dd5, 0x0000 }, + { 0x6dd6, 0x0000 }, + { 0x6dd7, 0x0000 }, + { 0x6dd8, 0x0000 }, + { 0x6dd9, 0x0000 }, + { 0x6dda, 0x0000 }, + { 0x6ddb, 0x0000 }, + { 0x6ddc, 0x0000 }, + { 0x6ddd, 0x0000 }, + { 0x6dde, 0x0000 }, + { 0x6ddf, 0x0000 }, + { 0x6de0, 0x0000 }, + { 0x6de1, 0x0000 }, + { 0x6de2, 0x0000 }, + { 0x6de3, 0x0000 }, + { 0x6de4, 0x0000 }, + { 0x6de5, 0x0000 }, + { 0x6de6, 0x0000 }, + { 0x6de7, 0x0000 }, + { 0x6de8, 0x0000 }, + { 0x6de9, 0x0000 }, + { 0x6dea, 0x0000 }, + { 0x6deb, 0x0000 }, + { 0x6dec, 0x0000 }, + { 0x6ded, 0x0000 }, + { 0x6dee, 0x0000 }, + { 0x6def, 0x0000 }, + { 0x6df0, 0x0000 }, + { 0x6df1, 0x0000 }, + { 0x6df2, 0x0000 }, + { 0x6df3, 0x0000 }, + { 0x6df4, 0x0000 }, + { 0x6df5, 0x0000 }, + { 0x6df6, 0x0000 }, + { 0x6df7, 0x0000 }, + { 0x6df8, 0x0000 }, + { 0x6df9, 0x0000 }, + { 0x6dfa, 0x0000 }, + { 0x6dfb, 0x0000 }, + { 0x6dfc, 0x0000 }, + { 0x6dfd, 0x0000 }, + { 0x6dfe, 0x0000 }, + { 0x6dff, 0x0000 }, + { 0x6e00, 0x0000 }, + { 0x6e01, 0x0000 }, + { 0x6e02, 0x0000 }, + { 0x6e03, 0x0000 }, + { 0x6e04, 0x0000 }, + { 0x6e05, 0x0000 }, + { 0x6e06, 0x0000 }, + { 0x6e07, 0x0000 }, + { 0x6e08, 0x0000 }, + { 0x6e09, 0x0000 }, + { 0x6e0a, 0x0000 }, + { 0x6e0b, 0x0000 }, + { 0x6e0c, 0x0000 }, + { 0x6e0d, 0x0000 }, + { 0x6e0e, 0x0000 }, + { 0x6e0f, 0x0000 }, + { 0x6e10, 0x0000 }, + { 0x6e11, 0x0000 }, + { 0x6e12, 0x0000 }, + { 0x6e13, 0x0000 }, + { 0x6e14, 0x0000 }, + { 0x6e15, 0x0000 }, + { 0x6e16, 0x0000 }, + { 0x6e17, 0x0000 }, + { 0x6e18, 0x0000 }, + { 0x6e19, 0x0000 }, + { 0x6e1a, 0x0000 }, + { 0x6e1b, 0x0000 }, + { 0x6e1c, 0x0000 }, + { 0x6e1d, 0x0000 }, + { 0x6e1e, 0x0000 }, + { 0x6e1f, 0x0000 }, + { 0x6e20, 0x0000 }, + { 0x6e21, 0x0000 }, + { 0x6e22, 0x0000 }, + { 0x6e23, 0x0000 }, + { 0x6e24, 0x0000 }, + { 0x6e25, 0x0000 }, + { 0x6e26, 0x0000 }, + { 0x6e27, 0x0000 }, + { 0x6e28, 0x0000 }, + { 0x6e29, 0x0000 }, + { 0x6e2a, 0x0000 }, + { 0x6e2b, 0x0000 }, + { 0x6e2c, 0x0000 }, + { 0x6e2d, 0x0000 }, + { 0x6e2e, 0x0000 }, + { 0x6e2f, 0x0000 }, + { 0x6e30, 0x0000 }, + { 0x6e31, 0x0000 }, + { 0x6e32, 0x0000 }, + { 0x6e33, 0x0000 }, + { 0x6e34, 0x0000 }, + { 0x6e35, 0x0000 }, + { 0x6e36, 0x0000 }, + { 0x6e37, 0x0000 }, + { 0x6e38, 0x0000 }, + { 0x6e39, 0x0000 }, + { 0x6e3a, 0x0000 }, + { 0x6e3b, 0x0000 }, + { 0x6e3c, 0x0000 }, + { 0x6e3d, 0x0000 }, + { 0x6e3e, 0x0000 }, + { 0x6e3f, 0x0000 }, + { 0x6e40, 0x0000 }, + { 0x6e41, 0x0000 }, + { 0x6e42, 0x0000 }, + { 0x6e43, 0x0000 }, + { 0x6e44, 0x0000 }, + { 0x6e45, 0x0000 }, + { 0x6e46, 0x0000 }, + { 0x6e47, 0x0000 }, + { 0x6e48, 0x0000 }, + { 0x6e49, 0x0000 }, + { 0x6e4a, 0x0000 }, + { 0x6e4b, 0x0000 }, + { 0x6e4c, 0x0000 }, + { 0x6e4d, 0x0000 }, + { 0x6e4e, 0x0000 }, + { 0x6e4f, 0x0000 }, + { 0x6e50, 0x0000 }, + { 0x6e51, 0x0000 }, + { 0x6e52, 0x0000 }, + { 0x6e53, 0x0000 }, + { 0x6e54, 0x0000 }, + { 0x6e55, 0x0000 }, + { 0x6e56, 0x0000 }, + { 0x6e57, 0x0000 }, + { 0x6e58, 0x0000 }, + { 0x6e59, 0x0000 }, + { 0x6e5a, 0x0000 }, + { 0x6e5b, 0x0000 }, + { 0x6e5c, 0x0000 }, + { 0x6e5d, 0x0000 }, + { 0x6e5e, 0x0000 }, + { 0x6e5f, 0x0000 }, + { 0x6e60, 0x0000 }, + { 0x6e61, 0x0000 }, + { 0x6e62, 0x0000 }, + { 0x6e63, 0x0000 }, + { 0x6e64, 0x0000 }, + { 0x6e65, 0x0000 }, + { 0x6e66, 0x0000 }, + { 0x6e67, 0x0000 }, + { 0x6e68, 0x0000 }, + { 0x6e69, 0x0000 }, + { 0x6e6a, 0x0000 }, + { 0x6e6b, 0x0000 }, + { 0x6e6c, 0x0000 }, + { 0x6e6d, 0x0000 }, + { 0x6e6e, 0x0000 }, + { 0x6e6f, 0x0000 }, + { 0x6e70, 0x0000 }, + { 0x6e71, 0x0000 }, + { 0x6e72, 0x0000 }, + { 0x6e73, 0x0000 }, + { 0x6e74, 0x0000 }, + { 0x6e75, 0x0000 }, + { 0x6e76, 0x0000 }, + { 0x6e77, 0x0000 }, + { 0x6e78, 0x0000 }, + { 0x6e79, 0x0000 }, + { 0x6e7a, 0x0000 }, + { 0x6e7b, 0x0000 }, + { 0x6e7c, 0x0000 }, + { 0x6e7d, 0x0000 }, + { 0x6e7e, 0x0000 }, + { 0x6e7f, 0x0000 }, + { 0x6e80, 0x0000 }, + { 0x6e81, 0x0000 }, + { 0x6e82, 0x0000 }, + { 0x6e83, 0x0000 }, + { 0x6e84, 0x0000 }, + { 0x6e85, 0x0000 }, + { 0x6e86, 0x0000 }, + { 0x6e87, 0x0000 }, + { 0x6e88, 0x0000 }, + { 0x6e89, 0x0000 }, + { 0x6e8a, 0x0000 }, + { 0x6e8b, 0x0000 }, + { 0x6e8c, 0x0000 }, + { 0x6e8d, 0x0000 }, + { 0x6e8e, 0x0000 }, + { 0x6e8f, 0x0000 }, + { 0x6e90, 0x0000 }, + { 0x6e91, 0x0000 }, + { 0x6e92, 0x0000 }, + { 0x6e93, 0x0000 }, + { 0x6e94, 0x0000 }, + { 0x6e95, 0x0000 }, + { 0x6e96, 0x0000 }, + { 0x6e97, 0x0000 }, + { 0x6e98, 0x0000 }, + { 0x6e99, 0x0000 }, + { 0x6e9a, 0x0000 }, + { 0x6e9b, 0x0000 }, + { 0x6e9c, 0x0000 }, + { 0x6e9d, 0x0000 }, + { 0x6e9e, 0x0000 }, + { 0x6e9f, 0x0000 }, + { 0x6ea0, 0x0000 }, + { 0x6ea1, 0x0000 }, + { 0x6ea2, 0x0000 }, + { 0x6ea3, 0x0000 }, + { 0x6ea4, 0x0000 }, + { 0x6ea5, 0x0000 }, + { 0x6ea6, 0x0000 }, + { 0x6ea7, 0x0000 }, + { 0x6ea8, 0x0000 }, + { 0x6ea9, 0x0000 }, + { 0x6eaa, 0x0000 }, + { 0x6eab, 0x0000 }, + { 0x6eac, 0x0000 }, + { 0x6ead, 0x0000 }, + { 0x6eae, 0x0000 }, + { 0x6eaf, 0x0000 }, + { 0x6eb0, 0x0000 }, + { 0x6eb1, 0x0000 }, + { 0x6eb2, 0x0000 }, + { 0x6eb3, 0x0000 }, + { 0x6eb4, 0x0000 }, + { 0x6eb5, 0x0000 }, + { 0x6eb6, 0x0000 }, + { 0x6eb7, 0x0000 }, + { 0x6eb8, 0x0000 }, + { 0x6eb9, 0x0000 }, + { 0x6eba, 0x0000 }, + { 0x6ebb, 0x0000 }, + { 0x6ebc, 0x0000 }, + { 0x6ebd, 0x0000 }, + { 0x6ebe, 0x0000 }, + { 0x6ebf, 0x0000 }, + { 0x6ec0, 0x0000 }, + { 0x6ec1, 0x0000 }, + { 0x6ec2, 0x0000 }, + { 0x6ec3, 0x0000 }, + { 0x6ec4, 0x0000 }, + { 0x6ec5, 0x0000 }, + { 0x6ec6, 0x0000 }, + { 0x6ec7, 0x0000 }, + { 0x6ec8, 0x0000 }, + { 0x6ec9, 0x0000 }, + { 0x6eca, 0x0000 }, + { 0x6ecb, 0x0000 }, + { 0x6ecc, 0x0000 }, + { 0x6ecd, 0x0000 }, + { 0x6ece, 0x0000 }, + { 0x6ecf, 0x0000 }, + { 0x6ed0, 0x0000 }, + { 0x6ed1, 0x0000 }, + { 0x6ed2, 0x0000 }, + { 0x6ed3, 0x0000 }, + { 0x6ed4, 0x0000 }, + { 0x6ed5, 0x0000 }, + { 0x6ed6, 0x0000 }, + { 0x6ed7, 0x0000 }, + { 0x6ed8, 0x0000 }, + { 0x6ed9, 0x0000 }, + { 0x6eda, 0x0000 }, + { 0x6edb, 0x0000 }, + { 0x6edc, 0x0000 }, + { 0x6edd, 0x0000 }, + { 0x6ede, 0x0000 }, + { 0x6edf, 0x0000 }, + { 0x6ee0, 0x0000 }, + { 0x6ee1, 0x0000 }, + { 0x6ee2, 0x0000 }, + { 0x6ee3, 0x0000 }, + { 0x6ee4, 0x0000 }, + { 0x6ee5, 0x0000 }, + { 0x6ee6, 0x0000 }, + { 0x6ee7, 0x0000 }, + { 0x6ee8, 0x0000 }, + { 0x6ee9, 0x0000 }, + { 0x6eea, 0x0000 }, + { 0x6eeb, 0x0000 }, + { 0x6eec, 0x0000 }, + { 0x6eed, 0x0000 }, + { 0x6eee, 0x0000 }, + { 0x6eef, 0x0000 }, + { 0x6ef0, 0x0000 }, + { 0x6ef1, 0x0000 }, + { 0x6ef2, 0x0000 }, + { 0x6ef3, 0x0000 }, + { 0x6ef4, 0x0000 }, + { 0x6ef5, 0x0000 }, + { 0x6ef6, 0x0000 }, + { 0x6ef7, 0x0000 }, + { 0x6ef8, 0x0000 }, + { 0x6ef9, 0x0000 }, + { 0x6efa, 0x0000 }, + { 0x6efb, 0x0000 }, + { 0x6efc, 0x0000 }, + { 0x6efd, 0x0000 }, + { 0x6efe, 0x0000 }, + { 0x6eff, 0x0000 }, + { 0x6f00, 0x0000 }, + { 0x6f01, 0x0000 }, + { 0x6f02, 0x0000 }, + { 0x6f03, 0x0000 }, + { 0x6f04, 0x0000 }, + { 0x6f05, 0x0000 }, + { 0x6f06, 0x0000 }, + { 0x6f07, 0x0000 }, + { 0x6f08, 0x0000 }, + { 0x6f09, 0x0000 }, + { 0x6f0a, 0x0000 }, + { 0x6f0b, 0x0000 }, + { 0x6f0c, 0x0000 }, + { 0x6f0d, 0x0000 }, + { 0x6f0e, 0x0000 }, + { 0x6f0f, 0x0000 }, + { 0x6f10, 0x0000 }, + { 0x6f11, 0x0000 }, + { 0x6f12, 0x0000 }, + { 0x6f13, 0x0000 }, + { 0x6f14, 0x0000 }, + { 0x6f15, 0x0000 }, + { 0x6f16, 0x0000 }, + { 0x6f17, 0x0000 }, + { 0x6f18, 0x0000 }, + { 0x6f19, 0x0000 }, + { 0x6f1a, 0x0000 }, + { 0x6f1b, 0x0000 }, + { 0x6f1c, 0x0000 }, + { 0x6f1d, 0x0000 }, + { 0x6f1e, 0x0000 }, + { 0x6f1f, 0x0000 }, + { 0x6f20, 0x0000 }, + { 0x6f21, 0x0000 }, + { 0x6f22, 0x0000 }, + { 0x6f23, 0x0000 }, + { 0x6f24, 0x0000 }, + { 0x6f25, 0x0000 }, + { 0x6f26, 0x0000 }, + { 0x6f27, 0x0000 }, + { 0x6f28, 0x0000 }, + { 0x6f29, 0x0000 }, + { 0x6f2a, 0x0000 }, + { 0x6f2b, 0x0000 }, + { 0x6f2c, 0x0000 }, + { 0x6f2d, 0x0000 }, + { 0x6f2e, 0x0000 }, + { 0x6f2f, 0x0000 }, + { 0x6f30, 0x0000 }, + { 0x6f31, 0x0000 }, + { 0x6f32, 0x0000 }, + { 0x6f33, 0x0000 }, + { 0x6f34, 0x0000 }, + { 0x6f35, 0x0000 }, + { 0x6f36, 0x0000 }, + { 0x6f37, 0x0000 }, + { 0x6f38, 0x0000 }, + { 0x6f39, 0x0000 }, + { 0x6f3a, 0x0000 }, + { 0x6f3b, 0x0000 }, + { 0x6f3c, 0x0000 }, + { 0x6f3d, 0x0000 }, + { 0x6f3e, 0x0000 }, + { 0x6f3f, 0x0000 }, + { 0x6f40, 0x0000 }, + { 0x6f41, 0x0000 }, + { 0x6f42, 0x0000 }, + { 0x6f43, 0x0000 }, + { 0x6f44, 0x0000 }, + { 0x6f45, 0x0000 }, + { 0x6f46, 0x0000 }, + { 0x6f47, 0x0000 }, + { 0x6f48, 0x0000 }, + { 0x6f49, 0x0000 }, + { 0x6f4a, 0x0000 }, + { 0x6f4b, 0x0000 }, + { 0x6f4c, 0x0000 }, + { 0x6f4d, 0x0000 }, + { 0x6f4e, 0x0000 }, + { 0x6f4f, 0x0000 }, + { 0x6f50, 0x0000 }, + { 0x6f51, 0x0000 }, + { 0x6f52, 0x0000 }, + { 0x6f53, 0x0000 }, + { 0x6f54, 0x0000 }, + { 0x6f55, 0x0000 }, + { 0x6f56, 0x0000 }, + { 0x6f57, 0x0000 }, + { 0x6f58, 0x0000 }, + { 0x6f59, 0x0000 }, + { 0x6f5a, 0x0000 }, + { 0x6f5b, 0x0000 }, + { 0x6f5c, 0x0000 }, + { 0x6f5d, 0x0000 }, + { 0x6f5e, 0x0000 }, + { 0x6f5f, 0x0000 }, + { 0x6f60, 0x0000 }, + { 0x6f61, 0x0000 }, + { 0x6f62, 0x0000 }, + { 0x6f63, 0x0000 }, + { 0x6f64, 0x0000 }, + { 0x6f65, 0x0000 }, + { 0x6f66, 0x0000 }, + { 0x6f67, 0x0000 }, + { 0x6f68, 0x0000 }, + { 0x6f69, 0x0000 }, + { 0x6f6a, 0x0000 }, + { 0x6f6b, 0x0000 }, + { 0x6f6c, 0x0000 }, + { 0x6f6d, 0x0000 }, + { 0x6f6e, 0x0000 }, + { 0x6f6f, 0x0000 }, + { 0x6f70, 0x0000 }, + { 0x6f71, 0x0000 }, + { 0x6f72, 0x0000 }, + { 0x6f73, 0x0000 }, + { 0x6f74, 0x0000 }, + { 0x6f75, 0x0000 }, + { 0x6f76, 0x0000 }, + { 0x6f77, 0x0000 }, + { 0x6f78, 0x0000 }, + { 0x6f79, 0x0000 }, + { 0x6f7a, 0x0000 }, + { 0x6f7b, 0x0000 }, + { 0x6f7c, 0x0000 }, + { 0x6f7d, 0x0000 }, + { 0x6f7e, 0x0000 }, + { 0x6f7f, 0x0000 }, + { 0x6f80, 0x0000 }, + { 0x6f81, 0x0000 }, + { 0x6f82, 0x0000 }, + { 0x6f83, 0x0000 }, + { 0x6f84, 0x0000 }, + { 0x6f85, 0x0000 }, + { 0x6f86, 0x0000 }, + { 0x6f87, 0x0000 }, + { 0x6f88, 0x0000 }, + { 0x6f89, 0x0000 }, + { 0x6f8a, 0x0000 }, + { 0x6f8b, 0x0000 }, + { 0x6f8c, 0x0000 }, + { 0x6f8d, 0x0000 }, + { 0x6f8e, 0x0000 }, + { 0x6f8f, 0x0000 }, + { 0x6f90, 0x0000 }, + { 0x6f91, 0x0000 }, + { 0x6f92, 0x0000 }, + { 0x6f93, 0x0000 }, + { 0x6f94, 0x0000 }, + { 0x6f95, 0x0000 }, + { 0x6f96, 0x0000 }, + { 0x6f97, 0x0000 }, + { 0x6f98, 0x0000 }, + { 0x6f99, 0x0000 }, + { 0x6f9a, 0x0000 }, + { 0x6f9b, 0x0000 }, + { 0x6f9c, 0x0000 }, + { 0x6f9d, 0x0000 }, + { 0x6f9e, 0x0000 }, + { 0x6f9f, 0x0000 }, + { 0x6fa0, 0x0000 }, + { 0x6fa1, 0x0000 }, + { 0x6fa2, 0x0000 }, + { 0x6fa3, 0x0000 }, + { 0x6fa4, 0x0000 }, + { 0x6fa5, 0x0000 }, + { 0x6fa6, 0x0000 }, + { 0x6fa7, 0x0000 }, + { 0x6fa8, 0x0000 }, + { 0x6fa9, 0x0000 }, + { 0x6faa, 0x0000 }, + { 0x6fab, 0x0000 }, + { 0x6fac, 0x0000 }, + { 0x6fad, 0x0000 }, + { 0x6fae, 0x0000 }, + { 0x6faf, 0x0000 }, + { 0x6fb0, 0x0000 }, + { 0x6fb1, 0x0000 }, + { 0x6fb2, 0x0000 }, + { 0x6fb3, 0x0000 }, + { 0x6fb4, 0x0000 }, + { 0x6fb5, 0x0000 }, + { 0x6fb6, 0x0000 }, + { 0x6fb7, 0x0000 }, + { 0x6fb8, 0x0000 }, + { 0x6fb9, 0x0000 }, + { 0x6fba, 0x0000 }, + { 0x6fbb, 0x0000 }, + { 0x6fbc, 0x0000 }, + { 0x6fbd, 0x0000 }, + { 0x6fbe, 0x0000 }, + { 0x6fbf, 0x0000 }, + { 0x6fc0, 0x0000 }, + { 0x6fc1, 0x0000 }, + { 0x6fc2, 0x0000 }, + { 0x6fc3, 0x0000 }, + { 0x6fc4, 0x0000 }, + { 0x6fc5, 0x0000 }, + { 0x6fc6, 0x0000 }, + { 0x6fc7, 0x0000 }, + { 0x6fc8, 0x0000 }, + { 0x6fc9, 0x0000 }, + { 0x6fca, 0x0000 }, + { 0x6fcb, 0x0000 }, + { 0x6fcc, 0x0000 }, + { 0x6fcd, 0x0000 }, + { 0x6fce, 0x0000 }, + { 0x6fcf, 0x0000 }, + { 0x6fd0, 0x0000 }, + { 0x6fd1, 0x0000 }, + { 0x6fd2, 0x0000 }, + { 0x6fd3, 0x0000 }, + { 0x6fd4, 0x0000 }, + { 0x6fd5, 0x0000 }, + { 0x6fd6, 0x0000 }, + { 0x6fd7, 0x0000 }, + { 0x6fd8, 0x0000 }, + { 0x6fd9, 0x0000 }, + { 0x6fda, 0x0000 }, + { 0x6fdb, 0x0000 }, + { 0x6fdc, 0x0000 }, + { 0x6fdd, 0x0000 }, + { 0x6fde, 0x0000 }, + { 0x6fdf, 0x0000 }, + { 0x6fe0, 0x0000 }, + { 0x6fe1, 0x0000 }, + { 0x6fe2, 0x0000 }, + { 0x6fe3, 0x0000 }, + { 0x6fe4, 0x0000 }, + { 0x6fe5, 0x0000 }, + { 0x6fe6, 0x0000 }, + { 0x6fe7, 0x0000 }, + { 0x6fe8, 0x0000 }, + { 0x6fe9, 0x0000 }, + { 0x6fea, 0x0000 }, + { 0x6feb, 0x0000 }, + { 0x6fec, 0x0000 }, + { 0x6fed, 0x0000 }, + { 0x6fee, 0x0000 }, + { 0x6fef, 0x0000 }, + { 0x6ff0, 0x0000 }, + { 0x6ff1, 0x0000 }, + { 0x6ff2, 0x0000 }, + { 0x6ff3, 0x0000 }, + { 0x6ff4, 0x0000 }, + { 0x6ff5, 0x0000 }, + { 0x6ff6, 0x0000 }, + { 0x6ff7, 0x0000 }, + { 0x6ff8, 0x0000 }, + { 0x6ff9, 0x0000 }, + { 0x6ffa, 0x0000 }, + { 0x6ffb, 0x0000 }, + { 0x6ffc, 0x0000 }, + { 0x6ffd, 0x0000 }, + { 0x6ffe, 0x0000 }, + { 0x6fff, 0x0000 }, + { 0x7000, 0x0000 }, + { 0x7001, 0x0000 }, + { 0x7002, 0x0000 }, + { 0x7003, 0x0000 }, + { 0x7004, 0x0000 }, + { 0x7005, 0x0000 }, + { 0x7006, 0x0000 }, + { 0x7007, 0x0000 }, + { 0x7008, 0x0000 }, + { 0x7009, 0x0000 }, + { 0x700a, 0x0000 }, + { 0x700b, 0x0000 }, + { 0x700c, 0x0000 }, + { 0x700d, 0x0000 }, + { 0x700e, 0x0000 }, + { 0x700f, 0x0000 }, + { 0x7010, 0x0000 }, + { 0x7011, 0x0000 }, + { 0x7012, 0x0000 }, + { 0x7013, 0x0000 }, + { 0x7014, 0x0000 }, + { 0x7015, 0x0000 }, + { 0x7016, 0x0000 }, + { 0x7017, 0x0000 }, + { 0x7018, 0x0000 }, + { 0x7019, 0x0000 }, + { 0x701a, 0x0000 }, + { 0x701b, 0x0000 }, + { 0x701c, 0x0000 }, + { 0x701d, 0x0000 }, + { 0x701e, 0x0000 }, + { 0x701f, 0x0000 }, + { 0x7020, 0x0000 }, + { 0x7021, 0x0000 }, + { 0x7022, 0x0000 }, + { 0x7023, 0x0000 }, + { 0x7024, 0x0000 }, + { 0x7025, 0x0000 }, + { 0x7026, 0x0000 }, + { 0x7027, 0x0000 }, + { 0x7028, 0x0000 }, + { 0x7029, 0x0000 }, + { 0x702a, 0x0000 }, + { 0x702b, 0x0000 }, + { 0x702c, 0x0000 }, + { 0x702d, 0x0000 }, + { 0x702e, 0x0000 }, + { 0x702f, 0x0000 }, + { 0x7030, 0x0000 }, + { 0x7031, 0x0000 }, + { 0x7032, 0x0000 }, + { 0x7033, 0x0000 }, + { 0x7034, 0x0000 }, + { 0x7035, 0x0000 }, + { 0x7036, 0x0000 }, + { 0x7037, 0x0000 }, + { 0x7038, 0x0000 }, + { 0x7039, 0x0000 }, + { 0x703a, 0x0000 }, + { 0x703b, 0x0000 }, + { 0x703c, 0x0000 }, + { 0x703d, 0x0000 }, + { 0x703e, 0x0000 }, + { 0x703f, 0x0000 }, + { 0x7040, 0x0000 }, + { 0x7041, 0x0000 }, + { 0x7042, 0x0000 }, + { 0x7043, 0x0000 }, + { 0x7044, 0x0000 }, + { 0x7045, 0x0000 }, + { 0x7046, 0x0000 }, + { 0x7047, 0x0000 }, + { 0x7048, 0x0000 }, + { 0x7049, 0x0000 }, + { 0x704a, 0x0000 }, + { 0x704b, 0x0000 }, + { 0x704c, 0x0000 }, + { 0x704d, 0x0000 }, + { 0x704e, 0x0000 }, + { 0x704f, 0x0000 }, + { 0x7050, 0x0000 }, + { 0x7051, 0x0000 }, + { 0x7052, 0x0000 }, + { 0x7053, 0x0000 }, + { 0x7054, 0x0000 }, + { 0x7055, 0x0000 }, + { 0x7056, 0x0000 }, + { 0x7057, 0x0000 }, + { 0x7058, 0x0000 }, + { 0x7059, 0x0000 }, + { 0x705a, 0x0000 }, + { 0x705b, 0x0000 }, + { 0x705c, 0x0000 }, + { 0x705d, 0x0000 }, + { 0x705e, 0x0000 }, + { 0x705f, 0x0000 }, + { 0x7060, 0x0000 }, + { 0x7061, 0x0000 }, + { 0x7062, 0x0000 }, + { 0x7063, 0x0000 }, + { 0x7064, 0x0000 }, + { 0x7065, 0x0000 }, + { 0x7066, 0x0000 }, + { 0x7067, 0x0000 }, + { 0x7068, 0x0000 }, + { 0x7069, 0x0000 }, + { 0x706a, 0x0000 }, + { 0x706b, 0x0000 }, + { 0x706c, 0x0000 }, + { 0x706d, 0x0000 }, + { 0x706e, 0x0000 }, + { 0x706f, 0x0000 }, + { 0x7070, 0x0000 }, + { 0x7071, 0x0000 }, + { 0x7072, 0x0000 }, + { 0x7073, 0x0000 }, + { 0x7074, 0x0000 }, + { 0x7075, 0x0000 }, + { 0x7076, 0x0000 }, + { 0x7077, 0x0000 }, + { 0x7078, 0x0000 }, + { 0x7079, 0x0000 }, + { 0x707a, 0x0000 }, + { 0x707b, 0x0000 }, + { 0x707c, 0x0000 }, + { 0x707d, 0x0000 }, + { 0x707e, 0x0000 }, + { 0x707f, 0x0000 }, + { 0x7080, 0x0000 }, + { 0x7081, 0x0000 }, + { 0x7082, 0x0000 }, + { 0x7083, 0x0000 }, + { 0x7084, 0x0000 }, + { 0x7085, 0x0000 }, + { 0x7086, 0x0000 }, + { 0x7087, 0x0000 }, + { 0x7088, 0x0000 }, + { 0x7089, 0x0000 }, + { 0x708a, 0x0000 }, + { 0x708b, 0x0000 }, + { 0x708c, 0x0000 }, + { 0x708d, 0x0000 }, + { 0x708e, 0x0000 }, + { 0x708f, 0x0000 }, + { 0x7090, 0x0000 }, + { 0x7091, 0x0000 }, + { 0x7092, 0x0000 }, + { 0x7093, 0x0000 }, + { 0x7094, 0x0000 }, + { 0x7095, 0x0000 }, + { 0x7096, 0x0000 }, + { 0x7097, 0x0000 }, + { 0x7098, 0x0000 }, + { 0x7099, 0x0000 }, + { 0x709a, 0x0000 }, + { 0x709b, 0x0000 }, + { 0x709c, 0x0000 }, + { 0x709d, 0x0000 }, + { 0x709e, 0x0000 }, + { 0x709f, 0x0000 }, + { 0x70a0, 0x0000 }, + { 0x70a1, 0x0000 }, + { 0x70a2, 0x0000 }, + { 0x70a3, 0x0000 }, + { 0x70a4, 0x0000 }, + { 0x70a5, 0x0000 }, + { 0x70a6, 0x0000 }, + { 0x70a7, 0x0000 }, + { 0x70a8, 0x0000 }, + { 0x70a9, 0x0000 }, + { 0x70aa, 0x0000 }, + { 0x70ab, 0x0000 }, + { 0x70ac, 0x0000 }, + { 0x70ad, 0x0000 }, + { 0x70ae, 0x0000 }, + { 0x70af, 0x0000 }, + { 0x70b0, 0x0000 }, + { 0x70b1, 0x0000 }, + { 0x70b2, 0x0000 }, + { 0x70b3, 0x0000 }, + { 0x70b4, 0x0000 }, + { 0x70b5, 0x0000 }, + { 0x70b6, 0x0000 }, + { 0x70b7, 0x0000 }, + { 0x70b8, 0x0000 }, + { 0x70b9, 0x0000 }, + { 0x70ba, 0x0000 }, + { 0x70bb, 0x0000 }, + { 0x70bc, 0x0000 }, + { 0x70bd, 0x0000 }, + { 0x70be, 0x0000 }, + { 0x70bf, 0x0000 }, + { 0x70c0, 0x0000 }, + { 0x70c1, 0x0000 }, + { 0x70c2, 0x0000 }, + { 0x70c3, 0x0000 }, + { 0x70c4, 0x0000 }, + { 0x70c5, 0x0000 }, + { 0x70c6, 0x0000 }, + { 0x70c7, 0x0000 }, + { 0x70c8, 0x0000 }, + { 0x70c9, 0x0000 }, + { 0x70ca, 0x0000 }, + { 0x70cb, 0x0000 }, + { 0x70cc, 0x0000 }, + { 0x70cd, 0x0000 }, + { 0x70ce, 0x0000 }, + { 0x70cf, 0x0000 }, + { 0x70d0, 0x0000 }, + { 0x70d1, 0x0000 }, + { 0x70d2, 0x0000 }, + { 0x70d3, 0x0000 }, + { 0x70d4, 0x0000 }, + { 0x70d5, 0x0000 }, + { 0x70d6, 0x0000 }, + { 0x70d7, 0x0000 }, + { 0x70d8, 0x0000 }, + { 0x70d9, 0x0000 }, + { 0x70da, 0x0000 }, + { 0x70db, 0x0000 }, + { 0x70dc, 0x0000 }, + { 0x70dd, 0x0000 }, + { 0x70de, 0x0000 }, + { 0x70df, 0x0000 }, + { 0x70e0, 0x0000 }, + { 0x70e1, 0x0000 }, + { 0x70e2, 0x0000 }, + { 0x70e3, 0x0000 }, + { 0x70e4, 0x0000 }, + { 0x70e5, 0x0000 }, + { 0x70e6, 0x0000 }, + { 0x70e7, 0x0000 }, + { 0x70e8, 0x0000 }, + { 0x70e9, 0x0000 }, + { 0x70ea, 0x0000 }, + { 0x70eb, 0x0000 }, + { 0x70ec, 0x0000 }, + { 0x70ed, 0x0000 }, + { 0x70ee, 0x0000 }, + { 0x70ef, 0x0000 }, + { 0x70f0, 0x0000 }, + { 0x70f1, 0x0000 }, + { 0x70f2, 0x0000 }, + { 0x70f3, 0x0000 }, + { 0x70f4, 0x0000 }, + { 0x70f5, 0x0000 }, + { 0x70f6, 0x0000 }, + { 0x70f7, 0x0000 }, + { 0x70f8, 0x0000 }, + { 0x70f9, 0x0000 }, + { 0x70fa, 0x0000 }, + { 0x70fb, 0x0000 }, + { 0x70fc, 0x0000 }, + { 0x70fd, 0x0000 }, + { 0x70fe, 0x0000 }, + { 0x70ff, 0x0000 }, + { 0x7100, 0x0000 }, + { 0x7101, 0x0000 }, + { 0x7102, 0x0000 }, + { 0x7103, 0x0000 }, + { 0x7104, 0x0000 }, + { 0x7105, 0x0000 }, + { 0x7106, 0x0000 }, + { 0x7107, 0x0000 }, + { 0x7108, 0x0000 }, + { 0x7109, 0x0000 }, + { 0x710a, 0x0000 }, + { 0x710b, 0x0000 }, + { 0x710c, 0x0000 }, + { 0x710d, 0x0000 }, + { 0x710e, 0x0000 }, + { 0x710f, 0x0000 }, + { 0x7110, 0x0000 }, + { 0x7111, 0x0000 }, + { 0x7112, 0x0000 }, + { 0x7113, 0x0000 }, + { 0x7114, 0x0000 }, + { 0x7115, 0x0000 }, + { 0x7116, 0x0000 }, + { 0x7117, 0x0000 }, + { 0x7118, 0x0000 }, + { 0x7119, 0x0000 }, + { 0x711a, 0x0000 }, + { 0x711b, 0x0000 }, + { 0x711c, 0x0000 }, + { 0x711d, 0x0000 }, + { 0x711e, 0x0000 }, + { 0x711f, 0x0000 }, + { 0x7120, 0x0000 }, + { 0x7121, 0x0000 }, + { 0x7122, 0x0000 }, + { 0x7123, 0x0000 }, + { 0x7124, 0x0000 }, + { 0x7125, 0x0000 }, + { 0x7126, 0x0000 }, + { 0x7127, 0x0000 }, + { 0x7128, 0x0000 }, + { 0x7129, 0x0000 }, + { 0x712a, 0x0000 }, + { 0x712b, 0x0000 }, + { 0x712c, 0x0000 }, + { 0x712d, 0x0000 }, + { 0x712e, 0x0000 }, + { 0x712f, 0x0000 }, + { 0x7130, 0x0000 }, + { 0x7131, 0x0000 }, + { 0x7132, 0x0000 }, + { 0x7133, 0x0000 }, + { 0x7134, 0x0000 }, + { 0x7135, 0x0000 }, + { 0x7136, 0x0000 }, + { 0x7137, 0x0000 }, + { 0x7138, 0x0000 }, + { 0x7139, 0x0000 }, + { 0x713a, 0x0000 }, + { 0x713b, 0x0000 }, + { 0x713c, 0x0000 }, + { 0x713d, 0x0000 }, + { 0x713e, 0x0000 }, + { 0x713f, 0x0000 }, + { 0x7140, 0x0000 }, + { 0x7141, 0x0000 }, + { 0x7142, 0x0000 }, + { 0x7143, 0x0000 }, + { 0x7144, 0x0000 }, + { 0x7145, 0x0000 }, + { 0x7146, 0x0000 }, + { 0x7147, 0x0000 }, + { 0x7148, 0x0000 }, + { 0x7149, 0x0000 }, + { 0x714a, 0x0000 }, + { 0x714b, 0x0000 }, + { 0x714c, 0x0000 }, + { 0x714d, 0x0000 }, + { 0x714e, 0x0000 }, + { 0x714f, 0x0000 }, + { 0x7150, 0x0000 }, + { 0x7151, 0x0000 }, + { 0x7152, 0x0000 }, + { 0x7153, 0x0000 }, + { 0x7154, 0x0000 }, + { 0x7155, 0x0000 }, + { 0x7156, 0x0000 }, + { 0x7157, 0x0000 }, + { 0x7158, 0x0000 }, + { 0x7159, 0x0000 }, + { 0x715a, 0x0000 }, + { 0x715b, 0x0000 }, + { 0x715c, 0x0000 }, + { 0x715d, 0x0000 }, + { 0x715e, 0x0000 }, + { 0x715f, 0x0000 }, + { 0x7160, 0x0000 }, + { 0x7161, 0x0000 }, + { 0x7162, 0x0000 }, + { 0x7163, 0x0000 }, + { 0x7164, 0x0000 }, + { 0x7165, 0x0000 }, + { 0x7166, 0x0000 }, + { 0x7167, 0x0000 }, + { 0x7168, 0x0000 }, + { 0x7169, 0x0000 }, + { 0x716a, 0x0000 }, + { 0x716b, 0x0000 }, + { 0x716c, 0x0000 }, + { 0x716d, 0x0000 }, + { 0x716e, 0x0000 }, + { 0x716f, 0x0000 }, + { 0x7170, 0x0000 }, + { 0x7171, 0x0000 }, + { 0x7172, 0x0000 }, + { 0x7173, 0x0000 }, + { 0x7174, 0x0000 }, + { 0x7175, 0x0000 }, + { 0x7176, 0x0000 }, + { 0x7177, 0x0000 }, + { 0x7178, 0x0000 }, + { 0x7179, 0x0000 }, + { 0x717a, 0x0000 }, + { 0x717b, 0x0000 }, + { 0x717c, 0x0000 }, + { 0x717d, 0x0000 }, + { 0x717e, 0x0000 }, + { 0x717f, 0x0000 }, + { 0x7180, 0x0000 }, + { 0x7181, 0x0000 }, + { 0x7182, 0x0000 }, + { 0x7183, 0x0000 }, + { 0x7184, 0x0000 }, + { 0x7185, 0x0000 }, + { 0x7186, 0x0000 }, + { 0x7187, 0x0000 }, + { 0x7188, 0x0000 }, + { 0x7189, 0x0000 }, + { 0x718a, 0x0000 }, + { 0x718b, 0x0000 }, + { 0x718c, 0x0000 }, + { 0x718d, 0x0000 }, + { 0x718e, 0x0000 }, + { 0x718f, 0x0000 }, + { 0x7190, 0x0000 }, + { 0x7191, 0x0000 }, + { 0x7192, 0x0000 }, + { 0x7193, 0x0000 }, + { 0x7194, 0x0000 }, + { 0x7195, 0x0000 }, + { 0x7196, 0x0000 }, + { 0x7197, 0x0000 }, + { 0x7198, 0x0000 }, + { 0x7199, 0x0000 }, + { 0x719a, 0x0000 }, + { 0x719b, 0x0000 }, + { 0x719c, 0x0000 }, + { 0x719d, 0x0000 }, + { 0x719e, 0x0000 }, + { 0x719f, 0x0000 }, + { 0x71a0, 0x0000 }, + { 0x71a1, 0x0000 }, + { 0x71a2, 0x0000 }, + { 0x71a3, 0x0000 }, + { 0x71a4, 0x0000 }, + { 0x71a5, 0x0000 }, + { 0x71a6, 0x0000 }, + { 0x71a7, 0x0000 }, + { 0x71a8, 0x0000 }, + { 0x71a9, 0x0000 }, + { 0x71aa, 0x0000 }, + { 0x71ab, 0x0000 }, + { 0x71ac, 0x0000 }, + { 0x71ad, 0x0000 }, + { 0x71ae, 0x0000 }, + { 0x71af, 0x0000 }, + { 0x71b0, 0x0000 }, + { 0x71b1, 0x0000 }, + { 0x71b2, 0x0000 }, + { 0x71b3, 0x0000 }, + { 0x71b4, 0x0000 }, + { 0x71b5, 0x0000 }, + { 0x71b6, 0x0000 }, + { 0x71b7, 0x0000 }, + { 0x71b8, 0x0000 }, + { 0x71b9, 0x0000 }, + { 0x71ba, 0x0000 }, + { 0x71bb, 0x0000 }, + { 0x71bc, 0x0000 }, + { 0x71bd, 0x0000 }, + { 0x71be, 0x0000 }, + { 0x71bf, 0x0000 }, + { 0x71c0, 0x0000 }, + { 0x71c1, 0x0000 }, + { 0x71c2, 0x0000 }, + { 0x71c3, 0x0000 }, + { 0x71c4, 0x0000 }, + { 0x71c5, 0x0000 }, + { 0x71c6, 0x0000 }, + { 0x71c7, 0x0000 }, + { 0x71c8, 0x0000 }, + { 0x71c9, 0x0000 }, + { 0x71ca, 0x0000 }, + { 0x71cb, 0x0000 }, + { 0x71cc, 0x0000 }, + { 0x71cd, 0x0000 }, + { 0x71ce, 0x0000 }, + { 0x71cf, 0x0000 }, + { 0x71d0, 0x0000 }, + { 0x71d1, 0x0000 }, + { 0x71d2, 0x0000 }, + { 0x71d3, 0x0000 }, + { 0x71d4, 0x0000 }, + { 0x71d5, 0x0000 }, + { 0x71d6, 0x0000 }, + { 0x71d7, 0x0000 }, + { 0x71d8, 0x0000 }, + { 0x71d9, 0x0000 }, + { 0x71da, 0x0000 }, + { 0x71db, 0x0000 }, + { 0x71dc, 0x0000 }, + { 0x71dd, 0x0000 }, + { 0x71de, 0x0000 }, + { 0x71df, 0x0000 }, + { 0x71e0, 0x0000 }, + { 0x71e1, 0x0000 }, + { 0x71e2, 0x0000 }, + { 0x71e3, 0x0000 }, + { 0x71e4, 0x0000 }, + { 0x71e5, 0x0000 }, + { 0x71e6, 0x0000 }, + { 0x71e7, 0x0000 }, + { 0x71e8, 0x0000 }, + { 0x71e9, 0x0000 }, + { 0x71ea, 0x0000 }, + { 0x71eb, 0x0000 }, + { 0x71ec, 0x0000 }, + { 0x71ed, 0x0000 }, + { 0x71ee, 0x0000 }, + { 0x71ef, 0x0000 }, + { 0x71f0, 0x0000 }, + { 0x71f1, 0x0000 }, + { 0x71f2, 0x0000 }, + { 0x71f3, 0x0000 }, + { 0x71f4, 0x0000 }, + { 0x71f5, 0x0000 }, + { 0x71f6, 0x0000 }, + { 0x71f7, 0x0000 }, + { 0x71f8, 0x0000 }, + { 0x71f9, 0x0000 }, + { 0x71fa, 0x0000 }, + { 0x71fb, 0x0000 }, + { 0x71fc, 0x0000 }, + { 0x71fd, 0x0000 }, + { 0x71fe, 0x0000 }, + { 0x71ff, 0x0000 }, + { 0x7200, 0x0000 }, + { 0x7201, 0x0000 }, + { 0x7202, 0x0000 }, + { 0x7203, 0x0000 }, + { 0x7204, 0x0000 }, + { 0x7205, 0x0000 }, + { 0x7206, 0x0000 }, + { 0x7207, 0x0000 }, + { 0x7208, 0x0000 }, + { 0x7209, 0x0000 }, + { 0x720a, 0x0000 }, + { 0x720b, 0x0000 }, + { 0x720c, 0x0000 }, + { 0x720d, 0x0000 }, + { 0x720e, 0x0000 }, + { 0x720f, 0x0000 }, + { 0x7210, 0x0000 }, + { 0x7211, 0x0000 }, + { 0x7212, 0x0000 }, + { 0x7213, 0x0000 }, + { 0x7214, 0x0000 }, + { 0x7215, 0x0000 }, + { 0x7216, 0x0000 }, + { 0x7217, 0x0000 }, + { 0x7218, 0x0000 }, + { 0x7219, 0x0000 }, + { 0x721a, 0x0000 }, + { 0x721b, 0x0000 }, + { 0x721c, 0x0000 }, + { 0x721d, 0x0000 }, + { 0x721e, 0x0000 }, + { 0x721f, 0x0000 }, + { 0x7220, 0x0000 }, + { 0x7221, 0x0000 }, + { 0x7222, 0x0000 }, + { 0x7223, 0x0000 }, + { 0x7224, 0x0000 }, + { 0x7225, 0x0000 }, + { 0x7226, 0x0000 }, + { 0x7227, 0x0000 }, + { 0x7228, 0x0000 }, + { 0x7229, 0x0000 }, + { 0x722a, 0x0000 }, + { 0x722b, 0x0000 }, + { 0x722c, 0x0000 }, + { 0x722d, 0x0000 }, + { 0x722e, 0x0000 }, + { 0x722f, 0x0000 }, + { 0x7230, 0x0000 }, + { 0x7231, 0x0000 }, + { 0x7232, 0x0000 }, + { 0x7233, 0x0000 }, + { 0x7234, 0x0000 }, + { 0x7235, 0x0000 }, + { 0x7236, 0x0000 }, + { 0x7237, 0x0000 }, + { 0x7238, 0x0000 }, + { 0x7239, 0x0000 }, + { 0x723a, 0x0000 }, + { 0x723b, 0x0000 }, + { 0x723c, 0x0000 }, + { 0x723d, 0x0000 }, + { 0x723e, 0x0000 }, + { 0x723f, 0x0000 }, + { 0x7240, 0x0000 }, + { 0x7241, 0x0000 }, + { 0x7242, 0x0000 }, + { 0x7243, 0x0000 }, + { 0x7244, 0x0000 }, + { 0x7245, 0x0000 }, + { 0x7246, 0x0000 }, + { 0x7247, 0x0000 }, + { 0x7248, 0x0000 }, + { 0x7249, 0x0000 }, + { 0x724a, 0x0000 }, + { 0x724b, 0x0000 }, + { 0x724c, 0x0000 }, + { 0x724d, 0x0000 }, + { 0x724e, 0x0000 }, + { 0x724f, 0x0000 }, + { 0x7250, 0x0000 }, + { 0x7251, 0x0000 }, + { 0x7252, 0x0000 }, + { 0x7253, 0x0000 }, + { 0x7254, 0x0000 }, + { 0x7255, 0x0000 }, + { 0x7256, 0x0000 }, + { 0x7257, 0x0000 }, + { 0x7258, 0x0000 }, + { 0x7259, 0x0000 }, + { 0x725a, 0x0000 }, + { 0x725b, 0x0000 }, + { 0x725c, 0x0000 }, + { 0x725d, 0x0000 }, + { 0x725e, 0x0000 }, + { 0x725f, 0x0000 }, + { 0x7260, 0x0000 }, + { 0x7261, 0x0000 }, + { 0x7262, 0x0000 }, + { 0x7263, 0x0000 }, + { 0x7264, 0x0000 }, + { 0x7265, 0x0000 }, + { 0x7266, 0x0000 }, + { 0x7267, 0x0000 }, + { 0x7268, 0x0000 }, + { 0x7269, 0x0000 }, + { 0x726a, 0x0000 }, + { 0x726b, 0x0000 }, + { 0x726c, 0x0000 }, + { 0x726d, 0x0000 }, + { 0x726e, 0x0000 }, + { 0x726f, 0x0000 }, + { 0x7270, 0x0000 }, + { 0x7271, 0x0000 }, + { 0x7272, 0x0000 }, + { 0x7273, 0x0000 }, + { 0x7274, 0x0000 }, + { 0x7275, 0x0000 }, + { 0x7276, 0x0000 }, + { 0x7277, 0x0000 }, + { 0x7278, 0x0000 }, + { 0x7279, 0x0000 }, + { 0x727a, 0x0000 }, + { 0x727b, 0x0000 }, + { 0x727c, 0x0000 }, + { 0x727d, 0x0000 }, + { 0x727e, 0x0000 }, + { 0x727f, 0x0000 }, + { 0x7280, 0x0000 }, + { 0x7281, 0x0000 }, + { 0x7282, 0x0000 }, + { 0x7283, 0x0000 }, + { 0x7284, 0x0000 }, + { 0x7285, 0x0000 }, + { 0x7286, 0x0000 }, + { 0x7287, 0x0000 }, + { 0x7288, 0x0000 }, + { 0x7289, 0x0000 }, + { 0x728a, 0x0000 }, + { 0x728b, 0x0000 }, + { 0x728c, 0x0000 }, + { 0x728d, 0x0000 }, + { 0x728e, 0x0000 }, + { 0x728f, 0x0000 }, + { 0x7290, 0x0000 }, + { 0x7291, 0x0000 }, + { 0x7292, 0x0000 }, + { 0x7293, 0x0000 }, + { 0x7294, 0x0000 }, + { 0x7295, 0x0000 }, + { 0x7296, 0x0000 }, + { 0x7297, 0x0000 }, + { 0x7298, 0x0000 }, + { 0x7299, 0x0000 }, + { 0x729a, 0x0000 }, + { 0x729b, 0x0000 }, + { 0x729c, 0x0000 }, + { 0x729d, 0x0000 }, + { 0x729e, 0x0000 }, + { 0x729f, 0x0000 }, + { 0x72a0, 0x0000 }, + { 0x72a1, 0x0000 }, + { 0x72a2, 0x0000 }, + { 0x72a3, 0x0000 }, + { 0x72a4, 0x0000 }, + { 0x72a5, 0x0000 }, + { 0x72a6, 0x0000 }, + { 0x72a7, 0x0000 }, + { 0x72a8, 0x0000 }, + { 0x72a9, 0x0000 }, + { 0x72aa, 0x0000 }, + { 0x72ab, 0x0000 }, + { 0x72ac, 0x0000 }, + { 0x72ad, 0x0000 }, + { 0x72ae, 0x0000 }, + { 0x72af, 0x0000 }, + { 0x72b0, 0x0000 }, + { 0x72b1, 0x0000 }, + { 0x72b2, 0x0000 }, + { 0x72b3, 0x0000 }, + { 0x72b4, 0x0000 }, + { 0x72b5, 0x0000 }, + { 0x72b6, 0x0000 }, + { 0x72b7, 0x0000 }, + { 0x72b8, 0x0000 }, + { 0x72b9, 0x0000 }, + { 0x72ba, 0x0000 }, + { 0x72bb, 0x0000 }, + { 0x72bc, 0x0000 }, + { 0x72bd, 0x0000 }, + { 0x72be, 0x0000 }, + { 0x72bf, 0x0000 }, + { 0x72c0, 0x0000 }, + { 0x72c1, 0x0000 }, + { 0x72c2, 0x0000 }, + { 0x72c3, 0x0000 }, + { 0x72c4, 0x0000 }, + { 0x72c5, 0x0000 }, + { 0x72c6, 0x0000 }, + { 0x72c7, 0x0000 }, + { 0x72c8, 0x0000 }, + { 0x72c9, 0x0000 }, + { 0x72ca, 0x0000 }, + { 0x72cb, 0x0000 }, + { 0x72cc, 0x0000 }, + { 0x72cd, 0x0000 }, + { 0x72ce, 0x0000 }, + { 0x72cf, 0x0000 }, + { 0x72d0, 0x0000 }, + { 0x72d1, 0x0000 }, + { 0x72d2, 0x0000 }, + { 0x72d3, 0x0000 }, + { 0x72d4, 0x0000 }, + { 0x72d5, 0x0000 }, + { 0x72d6, 0x0000 }, + { 0x72d7, 0x0000 }, + { 0x72d8, 0x0000 }, + { 0x72d9, 0x0000 }, + { 0x72da, 0x0000 }, + { 0x72db, 0x0000 }, + { 0x72dc, 0x0000 }, + { 0x72dd, 0x0000 }, + { 0x72de, 0x0000 }, + { 0x72df, 0x0000 }, + { 0x72e0, 0x0000 }, + { 0x72e1, 0x0000 }, + { 0x72e2, 0x0000 }, + { 0x72e3, 0x0000 }, + { 0x72e4, 0x0000 }, + { 0x72e5, 0x0000 }, + { 0x72e6, 0x0000 }, + { 0x72e7, 0x0000 }, + { 0x72e8, 0x0000 }, + { 0x72e9, 0x0000 }, + { 0x72ea, 0x0000 }, + { 0x72eb, 0x0000 }, + { 0x72ec, 0x0000 }, + { 0x72ed, 0x0000 }, + { 0x72ee, 0x0000 }, + { 0x72ef, 0x0000 }, + { 0x72f0, 0x0000 }, + { 0x72f1, 0x0000 }, + { 0x72f2, 0x0000 }, + { 0x72f3, 0x0000 }, + { 0x72f4, 0x0000 }, + { 0x72f5, 0x0000 }, + { 0x72f6, 0x0000 }, + { 0x72f7, 0x0000 }, + { 0x72f8, 0x0000 }, + { 0x72f9, 0x0000 }, + { 0x72fa, 0x0000 }, + { 0x72fb, 0x0000 }, + { 0x72fc, 0x0000 }, + { 0x72fd, 0x0000 }, + { 0x72fe, 0x0000 }, + { 0x72ff, 0x0000 }, + { 0x7300, 0x0000 }, + { 0x7301, 0x0000 }, + { 0x7302, 0x0000 }, + { 0x7303, 0x0000 }, + { 0x7304, 0x0000 }, + { 0x7305, 0x0000 }, + { 0x7306, 0x0000 }, + { 0x7307, 0x0000 }, + { 0x7308, 0x0000 }, + { 0x7309, 0x0000 }, + { 0x730a, 0x0000 }, + { 0x730b, 0x0000 }, + { 0x730c, 0x0000 }, + { 0x730d, 0x0000 }, + { 0x730e, 0x0000 }, + { 0x730f, 0x0000 }, + { 0x7310, 0x0000 }, + { 0x7311, 0x0000 }, + { 0x7312, 0x0000 }, + { 0x7313, 0x0000 }, + { 0x7314, 0x0000 }, + { 0x7315, 0x0000 }, + { 0x7316, 0x0000 }, + { 0x7317, 0x0000 }, + { 0x7318, 0x0000 }, + { 0x7319, 0x0000 }, + { 0x731a, 0x0000 }, + { 0x731b, 0x0000 }, + { 0x731c, 0x0000 }, + { 0x731d, 0x0000 }, + { 0x731e, 0x0000 }, + { 0x731f, 0x0000 }, + { 0x7320, 0x0000 }, + { 0x7321, 0x0000 }, + { 0x7322, 0x0000 }, + { 0x7323, 0x0000 }, + { 0x7324, 0x0000 }, + { 0x7325, 0x0000 }, + { 0x7326, 0x0000 }, + { 0x7327, 0x0000 }, + { 0x7328, 0x0000 }, + { 0x7329, 0x0000 }, + { 0x732a, 0x0000 }, + { 0x732b, 0x0000 }, + { 0x732c, 0x0000 }, + { 0x732d, 0x0000 }, + { 0x732e, 0x0000 }, + { 0x732f, 0x0000 }, + { 0x7330, 0x0000 }, + { 0x7331, 0x0000 }, + { 0x7332, 0x0000 }, + { 0x7333, 0x0000 }, + { 0x7334, 0x0000 }, + { 0x7335, 0x0000 }, + { 0x7336, 0x0000 }, + { 0x7337, 0x0000 }, + { 0x7338, 0x0000 }, + { 0x7339, 0x0000 }, + { 0x733a, 0x0000 }, + { 0x733b, 0x0000 }, + { 0x733c, 0x0000 }, + { 0x733d, 0x0000 }, + { 0x733e, 0x0000 }, + { 0x733f, 0x0000 }, + { 0x7340, 0x0000 }, + { 0x7341, 0x0000 }, + { 0x7342, 0x0000 }, + { 0x7343, 0x0000 }, + { 0x7344, 0x0000 }, + { 0x7345, 0x0000 }, + { 0x7346, 0x0000 }, + { 0x7347, 0x0000 }, + { 0x7348, 0x0000 }, + { 0x7349, 0x0000 }, + { 0x734a, 0x0000 }, + { 0x734b, 0x0000 }, + { 0x734c, 0x0000 }, + { 0x734d, 0x0000 }, + { 0x734e, 0x0000 }, + { 0x734f, 0x0000 }, + { 0x7350, 0x0000 }, + { 0x7351, 0x0000 }, + { 0x7352, 0x0000 }, + { 0x7353, 0x0000 }, + { 0x7354, 0x0000 }, + { 0x7355, 0x0000 }, + { 0x7356, 0x0000 }, + { 0x7357, 0x0000 }, + { 0x7358, 0x0000 }, + { 0x7359, 0x0000 }, + { 0x735a, 0x0000 }, + { 0x735b, 0x0000 }, + { 0x735c, 0x0000 }, + { 0x735d, 0x0000 }, + { 0x735e, 0x0000 }, + { 0x735f, 0x0000 }, + { 0x7360, 0x0000 }, + { 0x7361, 0x0000 }, + { 0x7362, 0x0000 }, + { 0x7363, 0x0000 }, + { 0x7364, 0x0000 }, + { 0x7365, 0x0000 }, + { 0x7366, 0x0000 }, + { 0x7367, 0x0000 }, + { 0x7368, 0x0000 }, + { 0x7369, 0x0000 }, + { 0x736a, 0x0000 }, + { 0x736b, 0x0000 }, + { 0x736c, 0x0000 }, + { 0x736d, 0x0000 }, + { 0x736e, 0x0000 }, + { 0x736f, 0x0000 }, + { 0x7370, 0x0000 }, + { 0x7371, 0x0000 }, + { 0x7372, 0x0000 }, + { 0x7373, 0x0000 }, + { 0x7374, 0x0000 }, + { 0x7375, 0x0000 }, + { 0x7376, 0x0000 }, + { 0x7377, 0x0000 }, + { 0x7378, 0x0000 }, + { 0x7379, 0x0000 }, + { 0x737a, 0x0000 }, + { 0x737b, 0x0000 }, + { 0x737c, 0x0000 }, + { 0x737d, 0x0000 }, + { 0x737e, 0x0000 }, + { 0x737f, 0x0000 }, + { 0x7380, 0x0000 }, + { 0x7381, 0x0000 }, + { 0x7382, 0x0000 }, + { 0x7383, 0x0000 }, + { 0x7384, 0x0000 }, + { 0x7385, 0x0000 }, + { 0x7386, 0x0000 }, + { 0x7387, 0x0000 }, + { 0x7388, 0x0000 }, + { 0x7389, 0x0000 }, + { 0x738a, 0x0000 }, + { 0x738b, 0x0000 }, + { 0x738c, 0x0000 }, + { 0x738d, 0x0000 }, + { 0x738e, 0x0000 }, + { 0x738f, 0x0000 }, + { 0x7390, 0x0000 }, + { 0x7391, 0x0000 }, + { 0x7392, 0x0000 }, + { 0x7393, 0x0000 }, + { 0x7394, 0x0000 }, + { 0x7395, 0x0000 }, + { 0x7396, 0x0000 }, + { 0x7397, 0x0000 }, + { 0x7398, 0x0000 }, + { 0x7399, 0x0000 }, + { 0x739a, 0x0000 }, + { 0x739b, 0x0000 }, + { 0x739c, 0x0000 }, + { 0x739d, 0x0000 }, + { 0x739e, 0x0000 }, + { 0x739f, 0x0000 }, + { 0x73a0, 0x0000 }, + { 0x73a1, 0x0000 }, + { 0x73a2, 0x0000 }, + { 0x73a3, 0x0000 }, + { 0x73a4, 0x0000 }, + { 0x73a5, 0x0000 }, + { 0x73a6, 0x0000 }, + { 0x73a7, 0x0000 }, + { 0x73a8, 0x0000 }, + { 0x73a9, 0x0000 }, + { 0x73aa, 0x0000 }, + { 0x73ab, 0x0000 }, + { 0x73ac, 0x0000 }, + { 0x73ad, 0x0000 }, + { 0x73ae, 0x0000 }, + { 0x73af, 0x0000 }, + { 0x73b0, 0x0000 }, + { 0x73b1, 0x0000 }, + { 0x73b2, 0x0000 }, + { 0x73b3, 0x0000 }, + { 0x73b4, 0x0000 }, + { 0x73b5, 0x0000 }, + { 0x73b6, 0x0000 }, + { 0x73b7, 0x0000 }, + { 0x73b8, 0x0000 }, + { 0x73b9, 0x0000 }, + { 0x73ba, 0x0000 }, + { 0x73bb, 0x0000 }, + { 0x73bc, 0x0000 }, + { 0x73bd, 0x0000 }, + { 0x73be, 0x0000 }, + { 0x73bf, 0x0000 }, + { 0x73c0, 0x0000 }, + { 0x73c1, 0x0000 }, + { 0x73c2, 0x0000 }, + { 0x73c3, 0x0000 }, + { 0x73c4, 0x0000 }, + { 0x73c5, 0x0000 }, + { 0x73c6, 0x0000 }, + { 0x73c7, 0x0000 }, + { 0x73c8, 0x0000 }, + { 0x73c9, 0x0000 }, + { 0x73ca, 0x0000 }, + { 0x73cb, 0x0000 }, + { 0x73cc, 0x0000 }, + { 0x73cd, 0x0000 }, + { 0x73ce, 0x0000 }, + { 0x73cf, 0x0000 }, + { 0x73d0, 0x0000 }, + { 0x73d1, 0x0000 }, + { 0x73d2, 0x0000 }, + { 0x73d3, 0x0000 }, + { 0x73d4, 0x0000 }, + { 0x73d5, 0x0000 }, + { 0x73d6, 0x0000 }, + { 0x73d7, 0x0000 }, + { 0x73d8, 0x0000 }, + { 0x73d9, 0x0000 }, + { 0x73da, 0x0000 }, + { 0x73db, 0x0000 }, + { 0x73dc, 0x0000 }, + { 0x73dd, 0x0000 }, + { 0x73de, 0x0000 }, + { 0x73df, 0x0000 }, + { 0x73e0, 0x0000 }, + { 0x73e1, 0x0000 }, + { 0x73e2, 0x0000 }, + { 0x73e3, 0x0000 }, + { 0x73e4, 0x0000 }, + { 0x73e5, 0x0000 }, + { 0x73e6, 0x0000 }, + { 0x73e7, 0x0000 }, + { 0x73e8, 0x0000 }, + { 0x73e9, 0x0000 }, + { 0x73ea, 0x0000 }, + { 0x73eb, 0x0000 }, + { 0x73ec, 0x0000 }, + { 0x73ed, 0x0000 }, + { 0x73ee, 0x0000 }, + { 0x73ef, 0x0000 }, + { 0x73f0, 0x0000 }, + { 0x73f1, 0x0000 }, + { 0x73f2, 0x0000 }, + { 0x73f3, 0x0000 }, + { 0x73f4, 0x0000 }, + { 0x73f5, 0x0000 }, + { 0x73f6, 0x0000 }, + { 0x73f7, 0x0000 }, + { 0x73f8, 0x0000 }, + { 0x73f9, 0x0000 }, + { 0x73fa, 0x0000 }, + { 0x73fb, 0x0000 }, + { 0x73fc, 0x0000 }, + { 0x73fd, 0x0000 }, + { 0x73fe, 0x0000 }, + { 0x73ff, 0x0000 }, + { 0x7400, 0x0000 }, + { 0x7401, 0x0000 }, + { 0x7402, 0x0000 }, + { 0x7403, 0x0000 }, + { 0x7404, 0x0000 }, + { 0x7405, 0x0000 }, + { 0x7406, 0x0000 }, + { 0x7407, 0x0000 }, + { 0x7408, 0x0000 }, + { 0x7409, 0x0000 }, + { 0x740a, 0x0000 }, + { 0x740b, 0x0000 }, + { 0x740c, 0x0000 }, + { 0x740d, 0x0000 }, + { 0x740e, 0x0000 }, + { 0x740f, 0x0000 }, + { 0x7410, 0x0000 }, + { 0x7411, 0x0000 }, + { 0x7412, 0x0000 }, + { 0x7413, 0x0000 }, + { 0x7414, 0x0000 }, + { 0x7415, 0x0000 }, + { 0x7416, 0x0000 }, + { 0x7417, 0x0000 }, + { 0x7418, 0x0000 }, + { 0x7419, 0x0000 }, + { 0x741a, 0x0000 }, + { 0x741b, 0x0000 }, + { 0x741c, 0x0000 }, + { 0x741d, 0x0000 }, + { 0x741e, 0x0000 }, + { 0x741f, 0x0000 }, + { 0x7420, 0x0000 }, + { 0x7421, 0x0000 }, + { 0x7422, 0x0000 }, + { 0x7423, 0x0000 }, + { 0x7424, 0x0000 }, + { 0x7425, 0x0000 }, + { 0x7426, 0x0000 }, + { 0x7427, 0x0000 }, + { 0x7428, 0x0000 }, + { 0x7429, 0x0000 }, + { 0x742a, 0x0000 }, + { 0x742b, 0x0000 }, + { 0x742c, 0x0000 }, + { 0x742d, 0x0000 }, + { 0x742e, 0x0000 }, + { 0x742f, 0x0000 }, + { 0x7430, 0x0000 }, + { 0x7431, 0x0000 }, + { 0x7432, 0x0000 }, + { 0x7433, 0x0000 }, + { 0x7434, 0x0000 }, + { 0x7435, 0x0000 }, + { 0x7436, 0x0000 }, + { 0x7437, 0x0000 }, + { 0x7438, 0x0000 }, + { 0x7439, 0x0000 }, + { 0x743a, 0x0000 }, + { 0x743b, 0x0000 }, + { 0x743c, 0x0000 }, + { 0x743d, 0x0000 }, + { 0x743e, 0x0000 }, + { 0x743f, 0x0000 }, + { 0x7440, 0x0000 }, + { 0x7441, 0x0000 }, + { 0x7442, 0x0000 }, + { 0x7443, 0x0000 }, + { 0x7444, 0x0000 }, + { 0x7445, 0x0000 }, + { 0x7446, 0x0000 }, + { 0x7447, 0x0000 }, + { 0x7448, 0x0000 }, + { 0x7449, 0x0000 }, + { 0x744a, 0x0000 }, + { 0x744b, 0x0000 }, + { 0x744c, 0x0000 }, + { 0x744d, 0x0000 }, + { 0x744e, 0x0000 }, + { 0x744f, 0x0000 }, + { 0x7450, 0x0000 }, + { 0x7451, 0x0000 }, + { 0x7452, 0x0000 }, + { 0x7453, 0x0000 }, + { 0x7454, 0x0000 }, + { 0x7455, 0x0000 }, + { 0x7456, 0x0000 }, + { 0x7457, 0x0000 }, + { 0x7458, 0x0000 }, + { 0x7459, 0x0000 }, + { 0x745a, 0x0000 }, + { 0x745b, 0x0000 }, + { 0x745c, 0x0000 }, + { 0x745d, 0x0000 }, + { 0x745e, 0x0000 }, + { 0x745f, 0x0000 }, + { 0x7460, 0x0000 }, + { 0x7461, 0x0000 }, + { 0x7462, 0x0000 }, + { 0x7463, 0x0000 }, + { 0x7464, 0x0000 }, + { 0x7465, 0x0000 }, + { 0x7466, 0x0000 }, + { 0x7467, 0x0000 }, + { 0x7468, 0x0000 }, + { 0x7469, 0x0000 }, + { 0x746a, 0x0000 }, + { 0x746b, 0x0000 }, + { 0x746c, 0x0000 }, + { 0x746d, 0x0000 }, + { 0x746e, 0x0000 }, + { 0x746f, 0x0000 }, + { 0x7470, 0x0000 }, + { 0x7471, 0x0000 }, + { 0x7472, 0x0000 }, + { 0x7473, 0x0000 }, + { 0x7474, 0x0000 }, + { 0x7475, 0x0000 }, + { 0x7476, 0x0000 }, + { 0x7477, 0x0000 }, + { 0x7478, 0x0000 }, + { 0x7479, 0x0000 }, + { 0x747a, 0x0000 }, + { 0x747b, 0x0000 }, + { 0x747c, 0x0000 }, + { 0x747d, 0x0000 }, + { 0x747e, 0x0000 }, + { 0x747f, 0x0000 }, + { 0x7480, 0x0000 }, + { 0x7481, 0x0000 }, + { 0x7482, 0x0000 }, + { 0x7483, 0x0000 }, + { 0x7484, 0x0000 }, + { 0x7485, 0x0000 }, + { 0x7486, 0x0000 }, + { 0x7487, 0x0000 }, + { 0x7488, 0x0000 }, + { 0x7489, 0x0000 }, + { 0x748a, 0x0000 }, + { 0x748b, 0x0000 }, + { 0x748c, 0x0000 }, + { 0x748d, 0x0000 }, + { 0x748e, 0x0000 }, + { 0x748f, 0x0000 }, + { 0x7490, 0x0000 }, + { 0x7491, 0x0000 }, + { 0x7492, 0x0000 }, + { 0x7493, 0x0000 }, + { 0x7494, 0x0000 }, + { 0x7495, 0x0000 }, + { 0x7496, 0x0000 }, + { 0x7497, 0x0000 }, + { 0x7498, 0x0000 }, + { 0x7499, 0x0000 }, + { 0x749a, 0x0000 }, + { 0x749b, 0x0000 }, + { 0x749c, 0x0000 }, + { 0x749d, 0x0000 }, + { 0x749e, 0x0000 }, + { 0x749f, 0x0000 }, + { 0x74a0, 0x0000 }, + { 0x74a1, 0x0000 }, + { 0x74a2, 0x0000 }, + { 0x74a3, 0x0000 }, + { 0x74a4, 0x0000 }, + { 0x74a5, 0x0000 }, + { 0x74a6, 0x0000 }, + { 0x74a7, 0x0000 }, + { 0x74a8, 0x0000 }, + { 0x74a9, 0x0000 }, + { 0x74aa, 0x0000 }, + { 0x74ab, 0x0000 }, + { 0x74ac, 0x0000 }, + { 0x74ad, 0x0000 }, + { 0x74ae, 0x0000 }, + { 0x74af, 0x0000 }, + { 0x74b0, 0x0000 }, + { 0x74b1, 0x0000 }, + { 0x74b2, 0x0000 }, + { 0x74b3, 0x0000 }, + { 0x74b4, 0x0000 }, + { 0x74b5, 0x0000 }, + { 0x74b6, 0x0000 }, + { 0x74b7, 0x0000 }, + { 0x74b8, 0x0000 }, + { 0x74b9, 0x0000 }, + { 0x74ba, 0x0000 }, + { 0x74bb, 0x0000 }, + { 0x74bc, 0x0000 }, + { 0x74bd, 0x0000 }, + { 0x74be, 0x0000 }, + { 0x74bf, 0x0000 }, + { 0x74c0, 0x0000 }, + { 0x74c1, 0x0000 }, + { 0x74c2, 0x0000 }, + { 0x74c3, 0x0000 }, + { 0x74c4, 0x0000 }, + { 0x74c5, 0x0000 }, + { 0x74c6, 0x0000 }, + { 0x74c7, 0x0000 }, + { 0x74c8, 0x0000 }, + { 0x74c9, 0x0000 }, + { 0x74ca, 0x0000 }, + { 0x74cb, 0x0000 }, + { 0x74cc, 0x0000 }, + { 0x74cd, 0x0000 }, + { 0x74ce, 0x0000 }, + { 0x74cf, 0x0000 }, + { 0x74d0, 0x0000 }, + { 0x74d1, 0x0000 }, + { 0x74d2, 0x0000 }, + { 0x74d3, 0x0000 }, + { 0x74d4, 0x0000 }, + { 0x74d5, 0x0000 }, + { 0x74d6, 0x0000 }, + { 0x74d7, 0x0000 }, + { 0x74d8, 0x0000 }, + { 0x74d9, 0x0000 }, + { 0x74da, 0x0000 }, + { 0x74db, 0x0000 }, + { 0x74dc, 0x0000 }, + { 0x74dd, 0x0000 }, + { 0x74de, 0x0000 }, + { 0x74df, 0x0000 }, + { 0x74e0, 0x0000 }, + { 0x74e1, 0x0000 }, + { 0x74e2, 0x0000 }, + { 0x74e3, 0x0000 }, + { 0x74e4, 0x0000 }, + { 0x74e5, 0x0000 }, + { 0x74e6, 0x0000 }, + { 0x74e7, 0x0000 }, + { 0x74e8, 0x0000 }, + { 0x74e9, 0x0000 }, + { 0x74ea, 0x0000 }, + { 0x74eb, 0x0000 }, + { 0x74ec, 0x0000 }, + { 0x74ed, 0x0000 }, + { 0x74ee, 0x0000 }, + { 0x74ef, 0x0000 }, + { 0x74f0, 0x0000 }, + { 0x74f1, 0x0000 }, + { 0x74f2, 0x0000 }, + { 0x74f3, 0x0000 }, + { 0x74f4, 0x0000 }, + { 0x74f5, 0x0000 }, + { 0x74f6, 0x0000 }, + { 0x74f7, 0x0000 }, + { 0x74f8, 0x0000 }, + { 0x74f9, 0x0000 }, + { 0x74fa, 0x0000 }, + { 0x74fb, 0x0000 }, + { 0x74fc, 0x0000 }, + { 0x74fd, 0x0000 }, + { 0x74fe, 0x0000 }, + { 0x74ff, 0x0000 }, + { 0x7500, 0x0000 }, + { 0x7501, 0x0000 }, + { 0x7502, 0x0000 }, + { 0x7503, 0x0000 }, + { 0x7504, 0x0000 }, + { 0x7505, 0x0000 }, + { 0x7506, 0x0000 }, + { 0x7507, 0x0000 }, + { 0x7508, 0x0000 }, + { 0x7509, 0x0000 }, + { 0x750a, 0x0000 }, + { 0x750b, 0x0000 }, + { 0x750c, 0x0000 }, + { 0x750d, 0x0000 }, + { 0x750e, 0x0000 }, + { 0x750f, 0x0000 }, + { 0x7510, 0x0000 }, + { 0x7511, 0x0000 }, + { 0x7512, 0x0000 }, + { 0x7513, 0x0000 }, + { 0x7514, 0x0000 }, + { 0x7515, 0x0000 }, + { 0x7516, 0x0000 }, + { 0x7517, 0x0000 }, + { 0x7518, 0x0000 }, + { 0x7519, 0x0000 }, + { 0x751a, 0x0000 }, + { 0x751b, 0x0000 }, + { 0x751c, 0x0000 }, + { 0x751d, 0x0000 }, + { 0x751e, 0x0000 }, + { 0x751f, 0x0000 }, + { 0x7520, 0x0000 }, + { 0x7521, 0x0000 }, + { 0x7522, 0x0000 }, + { 0x7523, 0x0000 }, + { 0x7524, 0x0000 }, + { 0x7525, 0x0000 }, + { 0x7526, 0x0000 }, + { 0x7527, 0x0000 }, + { 0x7528, 0x0000 }, + { 0x7529, 0x0000 }, + { 0x752a, 0x0000 }, + { 0x752b, 0x0000 }, + { 0x752c, 0x0000 }, + { 0x752d, 0x0000 }, + { 0x752e, 0x0000 }, + { 0x752f, 0x0000 }, + { 0x7530, 0x0000 }, + { 0x7531, 0x0000 }, + { 0x7532, 0x0000 }, + { 0x7533, 0x0000 }, + { 0x7534, 0x0000 }, + { 0x7535, 0x0000 }, + { 0x7536, 0x0000 }, + { 0x7537, 0x0000 }, + { 0x7538, 0x0000 }, + { 0x7539, 0x0000 }, + { 0x753a, 0x0000 }, + { 0x753b, 0x0000 }, + { 0x753c, 0x0000 }, + { 0x753d, 0x0000 }, + { 0x753e, 0x0000 }, + { 0x753f, 0x0000 }, + { 0x7540, 0x0000 }, + { 0x7541, 0x0000 }, + { 0x7542, 0x0000 }, + { 0x7543, 0x0000 }, + { 0x7544, 0x0000 }, + { 0x7545, 0x0000 }, + { 0x7546, 0x0000 }, + { 0x7547, 0x0000 }, + { 0x7548, 0x0000 }, + { 0x7549, 0x0000 }, + { 0x754a, 0x0000 }, + { 0x754b, 0x0000 }, + { 0x754c, 0x0000 }, + { 0x754d, 0x0000 }, + { 0x754e, 0x0000 }, + { 0x754f, 0x0000 }, + { 0x7550, 0x0000 }, + { 0x7551, 0x0000 }, + { 0x7552, 0x0000 }, + { 0x7553, 0x0000 }, + { 0x7554, 0x0000 }, + { 0x7555, 0x0000 }, + { 0x7556, 0x0000 }, + { 0x7557, 0x0000 }, + { 0x7558, 0x0000 }, + { 0x7559, 0x0000 }, + { 0x755a, 0x0000 }, + { 0x755b, 0x0000 }, + { 0x755c, 0x0000 }, + { 0x755d, 0x0000 }, + { 0x755e, 0x0000 }, + { 0x755f, 0x0000 }, + { 0x7560, 0x0000 }, + { 0x7561, 0x0000 }, + { 0x7562, 0x0000 }, + { 0x7563, 0x0000 }, + { 0x7564, 0x0000 }, + { 0x7565, 0x0000 }, + { 0x7566, 0x0000 }, + { 0x7567, 0x0000 }, + { 0x7568, 0x0000 }, + { 0x7569, 0x0000 }, + { 0x756a, 0x0000 }, + { 0x756b, 0x0000 }, + { 0x756c, 0x0000 }, + { 0x756d, 0x0000 }, + { 0x756e, 0x0000 }, + { 0x756f, 0x0000 }, + { 0x7570, 0x0000 }, + { 0x7571, 0x0000 }, + { 0x7572, 0x0000 }, + { 0x7573, 0x0000 }, + { 0x7574, 0x0000 }, + { 0x7575, 0x0000 }, + { 0x7576, 0x0000 }, + { 0x7577, 0x0000 }, + { 0x7578, 0x0000 }, + { 0x7579, 0x0000 }, + { 0x757a, 0x0000 }, + { 0x757b, 0x0000 }, + { 0x757c, 0x0000 }, + { 0x757d, 0x0000 }, + { 0x757e, 0x0000 }, + { 0x757f, 0x0000 }, + { 0x7580, 0x0000 }, + { 0x7581, 0x0000 }, + { 0x7582, 0x0000 }, + { 0x7583, 0x0000 }, + { 0x7584, 0x0000 }, + { 0x7585, 0x0000 }, + { 0x7586, 0x0000 }, + { 0x7587, 0x0000 }, + { 0x7588, 0x0000 }, + { 0x7589, 0x0000 }, + { 0x758a, 0x0000 }, + { 0x758b, 0x0000 }, + { 0x758c, 0x0000 }, + { 0x758d, 0x0000 }, + { 0x758e, 0x0000 }, + { 0x758f, 0x0000 }, + { 0x7590, 0x0000 }, + { 0x7591, 0x0000 }, + { 0x7592, 0x0000 }, + { 0x7593, 0x0000 }, + { 0x7594, 0x0000 }, + { 0x7595, 0x0000 }, + { 0x7596, 0x0000 }, + { 0x7597, 0x0000 }, + { 0x7598, 0x0000 }, + { 0x7599, 0x0000 }, + { 0x759a, 0x0000 }, + { 0x759b, 0x0000 }, + { 0x759c, 0x0000 }, + { 0x759d, 0x0000 }, + { 0x759e, 0x0000 }, + { 0x759f, 0x0000 }, + { 0x75a0, 0x0000 }, + { 0x75a1, 0x0000 }, + { 0x75a2, 0x0000 }, + { 0x75a3, 0x0000 }, + { 0x75a4, 0x0000 }, + { 0x75a5, 0x0000 }, + { 0x75a6, 0x0000 }, + { 0x75a7, 0x0000 }, + { 0x75a8, 0x0000 }, + { 0x75a9, 0x0000 }, + { 0x75aa, 0x0000 }, + { 0x75ab, 0x0000 }, + { 0x75ac, 0x0000 }, + { 0x75ad, 0x0000 }, + { 0x75ae, 0x0000 }, + { 0x75af, 0x0000 }, + { 0x75b0, 0x0000 }, + { 0x75b1, 0x0000 }, + { 0x75b2, 0x0000 }, + { 0x75b3, 0x0000 }, + { 0x75b4, 0x0000 }, + { 0x75b5, 0x0000 }, + { 0x75b6, 0x0000 }, + { 0x75b7, 0x0000 }, + { 0x75b8, 0x0000 }, + { 0x75b9, 0x0000 }, + { 0x75ba, 0x0000 }, + { 0x75bb, 0x0000 }, + { 0x75bc, 0x0000 }, + { 0x75bd, 0x0000 }, + { 0x75be, 0x0000 }, + { 0x75bf, 0x0000 }, + { 0x75c0, 0x0000 }, + { 0x75c1, 0x0000 }, + { 0x75c2, 0x0000 }, + { 0x75c3, 0x0000 }, + { 0x75c4, 0x0000 }, + { 0x75c5, 0x0000 }, + { 0x75c6, 0x0000 }, + { 0x75c7, 0x0000 }, + { 0x75c8, 0x0000 }, + { 0x75c9, 0x0000 }, + { 0x75ca, 0x0000 }, + { 0x75cb, 0x0000 }, + { 0x75cc, 0x0000 }, + { 0x75cd, 0x0000 }, + { 0x75ce, 0x0000 }, + { 0x75cf, 0x0000 }, + { 0x75d0, 0x0000 }, + { 0x75d1, 0x0000 }, + { 0x75d2, 0x0000 }, + { 0x75d3, 0x0000 }, + { 0x75d4, 0x0000 }, + { 0x75d5, 0x0000 }, + { 0x75d6, 0x0000 }, + { 0x75d7, 0x0000 }, + { 0x75d8, 0x0000 }, + { 0x75d9, 0x0000 }, + { 0x75da, 0x0000 }, + { 0x75db, 0x0000 }, + { 0x75dc, 0x0000 }, + { 0x75dd, 0x0000 }, + { 0x75de, 0x0000 }, + { 0x75df, 0x0000 }, + { 0x75e0, 0x0000 }, + { 0x75e1, 0x0000 }, + { 0x75e2, 0x0000 }, + { 0x75e3, 0x0000 }, + { 0x75e4, 0x0000 }, + { 0x75e5, 0x0000 }, + { 0x75e6, 0x0000 }, + { 0x75e7, 0x0000 }, + { 0x75e8, 0x0000 }, + { 0x75e9, 0x0000 }, + { 0x75ea, 0x0000 }, + { 0x75eb, 0x0000 }, + { 0x75ec, 0x0000 }, + { 0x75ed, 0x0000 }, + { 0x75ee, 0x0000 }, + { 0x75ef, 0x0000 }, + { 0x75f0, 0x0000 }, + { 0x75f1, 0x0000 }, + { 0x75f2, 0x0000 }, + { 0x75f3, 0x0000 }, + { 0x75f4, 0x0000 }, + { 0x75f5, 0x0000 }, + { 0x75f6, 0x0000 }, + { 0x75f7, 0x0000 }, + { 0x75f8, 0x0000 }, + { 0x75f9, 0x0000 }, + { 0x75fa, 0x0000 }, + { 0x75fb, 0x0000 }, + { 0x75fc, 0x0000 }, + { 0x75fd, 0x0000 }, + { 0x75fe, 0x0000 }, + { 0x75ff, 0x0000 }, + { 0x7600, 0x0000 }, + { 0x7601, 0x0000 }, + { 0x7602, 0x0000 }, + { 0x7603, 0x0000 }, + { 0x7604, 0x0000 }, + { 0x7605, 0x0000 }, + { 0x7606, 0x0000 }, + { 0x7607, 0x0000 }, + { 0x7608, 0x0000 }, + { 0x7609, 0x0000 }, + { 0x760a, 0x0000 }, + { 0x760b, 0x0000 }, + { 0x760c, 0x0000 }, + { 0x760d, 0x0000 }, + { 0x760e, 0x0000 }, + { 0x760f, 0x0000 }, + { 0x7610, 0x0000 }, + { 0x7611, 0x0000 }, + { 0x7612, 0x0000 }, + { 0x7613, 0x0000 }, + { 0x7614, 0x0000 }, + { 0x7615, 0x0000 }, + { 0x7616, 0x0000 }, + { 0x7617, 0x0000 }, + { 0x7618, 0x0000 }, + { 0x7619, 0x0000 }, + { 0x761a, 0x0000 }, + { 0x761b, 0x0000 }, + { 0x761c, 0x0000 }, + { 0x761d, 0x0000 }, + { 0x761e, 0x0000 }, + { 0x761f, 0x0000 }, + { 0x7620, 0x0000 }, + { 0x7621, 0x0000 }, + { 0x7622, 0x0000 }, + { 0x7623, 0x0000 }, + { 0x7624, 0x0000 }, + { 0x7625, 0x0000 }, + { 0x7626, 0x0000 }, + { 0x7627, 0x0000 }, + { 0x7628, 0x0000 }, + { 0x7629, 0x0000 }, + { 0x762a, 0x0000 }, + { 0x762b, 0x0000 }, + { 0x762c, 0x0000 }, + { 0x762d, 0x0000 }, + { 0x762e, 0x0000 }, + { 0x762f, 0x0000 }, + { 0x7630, 0x0000 }, + { 0x7631, 0x0000 }, + { 0x7632, 0x0000 }, + { 0x7633, 0x0000 }, + { 0x7634, 0x0000 }, + { 0x7635, 0x0000 }, + { 0x7636, 0x0000 }, + { 0x7637, 0x0000 }, + { 0x7638, 0x0000 }, + { 0x7639, 0x0000 }, + { 0x763a, 0x0000 }, + { 0x763b, 0x0000 }, + { 0x763c, 0x0000 }, + { 0x763d, 0x0000 }, + { 0x763e, 0x0000 }, + { 0x763f, 0x0000 }, + { 0x7640, 0x0000 }, + { 0x7641, 0x0000 }, + { 0x7642, 0x0000 }, + { 0x7643, 0x0000 }, + { 0x7644, 0x0000 }, + { 0x7645, 0x0000 }, + { 0x7646, 0x0000 }, + { 0x7647, 0x0000 }, + { 0x7648, 0x0000 }, + { 0x7649, 0x0000 }, + { 0x764a, 0x0000 }, + { 0x764b, 0x0000 }, + { 0x764c, 0x0000 }, + { 0x764d, 0x0000 }, + { 0x764e, 0x0000 }, + { 0x764f, 0x0000 }, + { 0x7650, 0x0000 }, + { 0x7651, 0x0000 }, + { 0x7652, 0x0000 }, + { 0x7653, 0x0000 }, + { 0x7654, 0x0000 }, + { 0x7655, 0x0000 }, + { 0x7656, 0x0000 }, + { 0x7657, 0x0000 }, + { 0x7658, 0x0000 }, + { 0x7659, 0x0000 }, + { 0x765a, 0x0000 }, + { 0x765b, 0x0000 }, + { 0x765c, 0x0000 }, + { 0x765d, 0x0000 }, + { 0x765e, 0x0000 }, + { 0x765f, 0x0000 }, + { 0x7660, 0x0000 }, + { 0x7661, 0x0000 }, + { 0x7662, 0x0000 }, + { 0x7663, 0x0000 }, + { 0x7664, 0x0000 }, + { 0x7665, 0x0000 }, + { 0x7666, 0x0000 }, + { 0x7667, 0x0000 }, + { 0x7668, 0x0000 }, + { 0x7669, 0x0000 }, + { 0x766a, 0x0000 }, + { 0x766b, 0x0000 }, + { 0x766c, 0x0000 }, + { 0x766d, 0x0000 }, + { 0x766e, 0x0000 }, + { 0x766f, 0x0000 }, + { 0x7670, 0x0000 }, + { 0x7671, 0x0000 }, + { 0x7672, 0x0000 }, + { 0x7673, 0x0000 }, + { 0x7674, 0x0000 }, + { 0x7675, 0x0000 }, + { 0x7676, 0x0000 }, + { 0x7677, 0x0000 }, + { 0x7678, 0x0000 }, + { 0x7679, 0x0000 }, + { 0x767a, 0x0000 }, + { 0x767b, 0x0000 }, + { 0x767c, 0x0000 }, + { 0x767d, 0x0000 }, + { 0x767e, 0x0000 }, + { 0x767f, 0x0000 }, + { 0x7680, 0x0000 }, + { 0x7681, 0x0000 }, + { 0x7682, 0x0000 }, + { 0x7683, 0x0000 }, + { 0x7684, 0x0000 }, + { 0x7685, 0x0000 }, + { 0x7686, 0x0000 }, + { 0x7687, 0x0000 }, + { 0x7688, 0x0000 }, + { 0x7689, 0x0000 }, + { 0x768a, 0x0000 }, + { 0x768b, 0x0000 }, + { 0x768c, 0x0000 }, + { 0x768d, 0x0000 }, + { 0x768e, 0x0000 }, + { 0x768f, 0x0000 }, + { 0x7690, 0x0000 }, + { 0x7691, 0x0000 }, + { 0x7692, 0x0000 }, + { 0x7693, 0x0000 }, + { 0x7694, 0x0000 }, + { 0x7695, 0x0000 }, + { 0x7696, 0x0000 }, + { 0x7697, 0x0000 }, + { 0x7698, 0x0000 }, + { 0x7699, 0x0000 }, + { 0x769a, 0x0000 }, + { 0x769b, 0x0000 }, + { 0x769c, 0x0000 }, + { 0x769d, 0x0000 }, + { 0x769e, 0x0000 }, + { 0x769f, 0x0000 }, + { 0x76a0, 0x0000 }, + { 0x76a1, 0x0000 }, + { 0x76a2, 0x0000 }, + { 0x76a3, 0x0000 }, + { 0x76a4, 0x0000 }, + { 0x76a5, 0x0000 }, + { 0x76a6, 0x0000 }, + { 0x76a7, 0x0000 }, + { 0x76a8, 0x0000 }, + { 0x76a9, 0x0000 }, + { 0x76aa, 0x0000 }, + { 0x76ab, 0x0000 }, + { 0x76ac, 0x0000 }, + { 0x76ad, 0x0000 }, + { 0x76ae, 0x0000 }, + { 0x76af, 0x0000 }, + { 0x76b0, 0x0000 }, + { 0x76b1, 0x0000 }, + { 0x76b2, 0x0000 }, + { 0x76b3, 0x0000 }, + { 0x76b4, 0x0000 }, + { 0x76b5, 0x0000 }, + { 0x76b6, 0x0000 }, + { 0x76b7, 0x0000 }, + { 0x76b8, 0x0000 }, + { 0x76b9, 0x0000 }, + { 0x76ba, 0x0000 }, + { 0x76bb, 0x0000 }, + { 0x76bc, 0x0000 }, + { 0x76bd, 0x0000 }, + { 0x76be, 0x0000 }, + { 0x76bf, 0x0000 }, + { 0x76c0, 0x0000 }, + { 0x76c1, 0x0000 }, + { 0x76c2, 0x0000 }, + { 0x76c3, 0x0000 }, + { 0x76c4, 0x0000 }, + { 0x76c5, 0x0000 }, + { 0x76c6, 0x0000 }, + { 0x76c7, 0x0000 }, + { 0x76c8, 0x0000 }, + { 0x76c9, 0x0000 }, + { 0x76ca, 0x0000 }, + { 0x76cb, 0x0000 }, + { 0x76cc, 0x0000 }, + { 0x76cd, 0x0000 }, + { 0x76ce, 0x0000 }, + { 0x76cf, 0x0000 }, + { 0x76d0, 0x0000 }, + { 0x76d1, 0x0000 }, + { 0x76d2, 0x0000 }, + { 0x76d3, 0x0000 }, + { 0x76d4, 0x0000 }, + { 0x76d5, 0x0000 }, + { 0x76d6, 0x0000 }, + { 0x76d7, 0x0000 }, + { 0x76d8, 0x0000 }, + { 0x76d9, 0x0000 }, + { 0x76da, 0x0000 }, + { 0x76db, 0x0000 }, + { 0x76dc, 0x0000 }, + { 0x76dd, 0x0000 }, + { 0x76de, 0x0000 }, + { 0x76df, 0x0000 }, + { 0x76e0, 0x0000 }, + { 0x76e1, 0x0000 }, + { 0x76e2, 0x0000 }, + { 0x76e3, 0x0000 }, + { 0x76e4, 0x0000 }, + { 0x76e5, 0x0000 }, + { 0x76e6, 0x0000 }, + { 0x76e7, 0x0000 }, + { 0x76e8, 0x0000 }, + { 0x76e9, 0x0000 }, + { 0x76ea, 0x0000 }, + { 0x76eb, 0x0000 }, + { 0x76ec, 0x0000 }, + { 0x76ed, 0x0000 }, + { 0x76ee, 0x0000 }, + { 0x76ef, 0x0000 }, + { 0x76f0, 0x0000 }, + { 0x76f1, 0x0000 }, + { 0x76f2, 0x0000 }, + { 0x76f3, 0x0000 }, + { 0x76f4, 0x0000 }, + { 0x76f5, 0x0000 }, + { 0x76f6, 0x0000 }, + { 0x76f7, 0x0000 }, + { 0x76f8, 0x0000 }, + { 0x76f9, 0x0000 }, + { 0x76fa, 0x0000 }, + { 0x76fb, 0x0000 }, + { 0x76fc, 0x0000 }, + { 0x76fd, 0x0000 }, + { 0x76fe, 0x0000 }, + { 0x76ff, 0x0000 }, + { 0x7700, 0x0000 }, + { 0x7701, 0x0000 }, + { 0x7702, 0x0000 }, + { 0x7703, 0x0000 }, + { 0x7704, 0x0000 }, + { 0x7705, 0x0000 }, + { 0x7706, 0x0000 }, + { 0x7707, 0x0000 }, + { 0x7708, 0x0000 }, + { 0x7709, 0x0000 }, + { 0x770a, 0x0000 }, + { 0x770b, 0x0000 }, + { 0x770c, 0x0000 }, + { 0x770d, 0x0000 }, + { 0x770e, 0x0000 }, + { 0x770f, 0x0000 }, + { 0x7710, 0x0000 }, + { 0x7711, 0x0000 }, + { 0x7712, 0x0000 }, + { 0x7713, 0x0000 }, + { 0x7714, 0x0000 }, + { 0x7715, 0x0000 }, + { 0x7716, 0x0000 }, + { 0x7717, 0x0000 }, + { 0x7718, 0x0000 }, + { 0x7719, 0x0000 }, + { 0x771a, 0x0000 }, + { 0x771b, 0x0000 }, + { 0x771c, 0x0000 }, + { 0x771d, 0x0000 }, + { 0x771e, 0x0000 }, + { 0x771f, 0x0000 }, + { 0x7720, 0x0000 }, + { 0x7721, 0x0000 }, + { 0x7722, 0x0000 }, + { 0x7723, 0x0000 }, + { 0x7724, 0x0000 }, + { 0x7725, 0x0000 }, + { 0x7726, 0x0000 }, + { 0x7727, 0x0000 }, + { 0x7728, 0x0000 }, + { 0x7729, 0x0000 }, + { 0x772a, 0x0000 }, + { 0x772b, 0x0000 }, + { 0x772c, 0x0000 }, + { 0x772d, 0x0000 }, + { 0x772e, 0x0000 }, + { 0x772f, 0x0000 }, + { 0x7730, 0x0000 }, + { 0x7731, 0x0000 }, + { 0x7732, 0x0000 }, + { 0x7733, 0x0000 }, + { 0x7734, 0x0000 }, + { 0x7735, 0x0000 }, + { 0x7736, 0x0000 }, + { 0x7737, 0x0000 }, + { 0x7738, 0x0000 }, + { 0x7739, 0x0000 }, + { 0x773a, 0x0000 }, + { 0x773b, 0x0000 }, + { 0x773c, 0x0000 }, + { 0x773d, 0x0000 }, + { 0x773e, 0x0000 }, + { 0x773f, 0x0000 }, + { 0x7740, 0x0000 }, + { 0x7741, 0x0000 }, + { 0x7742, 0x0000 }, + { 0x7743, 0x0000 }, + { 0x7744, 0x0000 }, + { 0x7745, 0x0000 }, + { 0x7746, 0x0000 }, + { 0x7747, 0x0000 }, + { 0x7748, 0x0000 }, + { 0x7749, 0x0000 }, + { 0x774a, 0x0000 }, + { 0x774b, 0x0000 }, + { 0x774c, 0x0000 }, + { 0x774d, 0x0000 }, + { 0x774e, 0x0000 }, + { 0x774f, 0x0000 }, + { 0x7750, 0x0000 }, + { 0x7751, 0x0000 }, + { 0x7752, 0x0000 }, + { 0x7753, 0x0000 }, + { 0x7754, 0x0000 }, + { 0x7755, 0x0000 }, + { 0x7756, 0x0000 }, + { 0x7757, 0x0000 }, + { 0x7758, 0x0000 }, + { 0x7759, 0x0000 }, + { 0x775a, 0x0000 }, + { 0x775b, 0x0000 }, + { 0x775c, 0x0000 }, + { 0x775d, 0x0000 }, + { 0x775e, 0x0000 }, + { 0x775f, 0x0000 }, + { 0x7760, 0x0000 }, + { 0x7761, 0x0000 }, + { 0x7762, 0x0000 }, + { 0x7763, 0x0000 }, + { 0x7764, 0x0000 }, + { 0x7765, 0x0000 }, + { 0x7766, 0x0000 }, + { 0x7767, 0x0000 }, + { 0x7768, 0x0000 }, + { 0x7769, 0x0000 }, + { 0x776a, 0x0000 }, + { 0x776b, 0x0000 }, + { 0x776c, 0x0000 }, + { 0x776d, 0x0000 }, + { 0x776e, 0x0000 }, + { 0x776f, 0x0000 }, + { 0x7770, 0x0000 }, + { 0x7771, 0x0000 }, + { 0x7772, 0x0000 }, + { 0x7773, 0x0000 }, + { 0x7774, 0x0000 }, + { 0x7775, 0x0000 }, + { 0x7776, 0x0000 }, + { 0x7777, 0x0000 }, + { 0x7778, 0x0000 }, + { 0x7779, 0x0000 }, + { 0x777a, 0x0000 }, + { 0x777b, 0x0000 }, + { 0x777c, 0x0000 }, + { 0x777d, 0x0000 }, + { 0x777e, 0x0000 }, + { 0x777f, 0x0000 }, + { 0x7780, 0x0000 }, + { 0x7781, 0x0000 }, + { 0x7782, 0x0000 }, + { 0x7783, 0x0000 }, + { 0x7784, 0x0000 }, + { 0x7785, 0x0000 }, + { 0x7786, 0x0000 }, + { 0x7787, 0x0000 }, + { 0x7788, 0x0000 }, + { 0x7789, 0x0000 }, + { 0x778a, 0x0000 }, + { 0x778b, 0x0000 }, + { 0x778c, 0x0000 }, + { 0x778d, 0x0000 }, + { 0x778e, 0x0000 }, + { 0x778f, 0x0000 }, + { 0x7790, 0x0000 }, + { 0x7791, 0x0000 }, + { 0x7792, 0x0000 }, + { 0x7793, 0x0000 }, + { 0x7794, 0x0000 }, + { 0x7795, 0x0000 }, + { 0x7796, 0x0000 }, + { 0x7797, 0x0000 }, + { 0x7798, 0x0000 }, + { 0x7799, 0x0000 }, + { 0x779a, 0x0000 }, + { 0x779b, 0x0000 }, + { 0x779c, 0x0000 }, + { 0x779d, 0x0000 }, + { 0x779e, 0x0000 }, + { 0x779f, 0x0000 }, + { 0x77a0, 0x0000 }, + { 0x77a1, 0x0000 }, + { 0x77a2, 0x0000 }, + { 0x77a3, 0x0000 }, + { 0x77a4, 0x0000 }, + { 0x77a5, 0x0000 }, + { 0x77a6, 0x0000 }, + { 0x77a7, 0x0000 }, + { 0x77a8, 0x0000 }, + { 0x77a9, 0x0000 }, + { 0x77aa, 0x0000 }, + { 0x77ab, 0x0000 }, + { 0x77ac, 0x0000 }, + { 0x77ad, 0x0000 }, + { 0x77ae, 0x0000 }, + { 0x77af, 0x0000 }, + { 0x77b0, 0x0000 }, + { 0x77b1, 0x0000 }, + { 0x77b2, 0x0000 }, + { 0x77b3, 0x0000 }, + { 0x77b4, 0x0000 }, + { 0x77b5, 0x0000 }, + { 0x77b6, 0x0000 }, + { 0x77b7, 0x0000 }, + { 0x77b8, 0x0000 }, + { 0x77b9, 0x0000 }, + { 0x77ba, 0x0000 }, + { 0x77bb, 0x0000 }, + { 0x77bc, 0x0000 }, + { 0x77bd, 0x0000 }, + { 0x77be, 0x0000 }, + { 0x77bf, 0x0000 }, + { 0x77c0, 0x0000 }, + { 0x77c1, 0x0000 }, + { 0x77c2, 0x0000 }, + { 0x77c3, 0x0000 }, + { 0x77c4, 0x0000 }, + { 0x77c5, 0x0000 }, + { 0x77c6, 0x0000 }, + { 0x77c7, 0x0000 }, + { 0x77c8, 0x0000 }, + { 0x77c9, 0x0000 }, + { 0x77ca, 0x0000 }, + { 0x77cb, 0x0000 }, + { 0x77cc, 0x0000 }, + { 0x77cd, 0x0000 }, + { 0x77ce, 0x0000 }, + { 0x77cf, 0x0000 }, + { 0x77d0, 0x0000 }, + { 0x77d1, 0x0000 }, + { 0x77d2, 0x0000 }, + { 0x77d3, 0x0000 }, + { 0x77d4, 0x0000 }, + { 0x77d5, 0x0000 }, + { 0x77d6, 0x0000 }, + { 0x77d7, 0x0000 }, + { 0x77d8, 0x0000 }, + { 0x77d9, 0x0000 }, + { 0x77da, 0x0000 }, + { 0x77db, 0x0000 }, + { 0x77dc, 0x0000 }, + { 0x77dd, 0x0000 }, + { 0x77de, 0x0000 }, + { 0x77df, 0x0000 }, + { 0x77e0, 0x0000 }, + { 0x77e1, 0x0000 }, + { 0x77e2, 0x0000 }, + { 0x77e3, 0x0000 }, + { 0x77e4, 0x0000 }, + { 0x77e5, 0x0000 }, + { 0x77e6, 0x0000 }, + { 0x77e7, 0x0000 }, + { 0x77e8, 0x0000 }, + { 0x77e9, 0x0000 }, + { 0x77ea, 0x0000 }, + { 0x77eb, 0x0000 }, + { 0x77ec, 0x0000 }, + { 0x77ed, 0x0000 }, + { 0x77ee, 0x0000 }, + { 0x77ef, 0x0000 }, + { 0x77f0, 0x0000 }, + { 0x77f1, 0x0000 }, + { 0x77f2, 0x0000 }, + { 0x77f3, 0x0000 }, + { 0x77f4, 0x0000 }, + { 0x77f5, 0x0000 }, + { 0x77f6, 0x0000 }, + { 0x77f7, 0x0000 }, + { 0x77f8, 0x0000 }, + { 0x77f9, 0x0000 }, + { 0x77fa, 0x0000 }, + { 0x77fb, 0x0000 }, + { 0x77fc, 0x0000 }, + { 0x77fd, 0x0000 }, + { 0x77fe, 0x0000 }, + { 0x77ff, 0x0000 }, + { 0x7800, 0x0000 }, + { 0x7801, 0x0000 }, + { 0x7802, 0x0000 }, + { 0x7803, 0x0000 }, + { 0x7804, 0x0000 }, + { 0x7805, 0x0000 }, + { 0x7806, 0x0000 }, + { 0x7807, 0x0000 }, + { 0x7808, 0x0000 }, + { 0x7809, 0x0000 }, + { 0x780a, 0x0000 }, + { 0x780b, 0x0000 }, + { 0x780c, 0x0000 }, + { 0x780d, 0x0000 }, + { 0x780e, 0x0000 }, + { 0x780f, 0x0000 }, + { 0x7810, 0x0000 }, + { 0x7811, 0x0000 }, + { 0x7812, 0x0000 }, + { 0x7813, 0x0000 }, + { 0x7814, 0x0000 }, + { 0x7815, 0x0000 }, + { 0x7816, 0x0000 }, + { 0x7817, 0x0000 }, + { 0x7818, 0x0000 }, + { 0x7819, 0x0000 }, + { 0x781a, 0x0000 }, + { 0x781b, 0x0000 }, + { 0x781c, 0x0000 }, + { 0x781d, 0x0000 }, + { 0x781e, 0x0000 }, + { 0x781f, 0x0000 }, + { 0x7820, 0x0000 }, + { 0x7821, 0x0000 }, + { 0x7822, 0x0000 }, + { 0x7823, 0x0000 }, + { 0x7824, 0x0000 }, + { 0x7825, 0x0000 }, + { 0x7826, 0x0000 }, + { 0x7827, 0x0000 }, + { 0x7828, 0x0000 }, + { 0x7829, 0x0000 }, + { 0x782a, 0x0000 }, + { 0x782b, 0x0000 }, + { 0x782c, 0x0000 }, + { 0x782d, 0x0000 }, + { 0x782e, 0x0000 }, + { 0x782f, 0x0000 }, + { 0x7830, 0x0000 }, + { 0x7831, 0x0000 }, + { 0x7832, 0x0000 }, + { 0x7833, 0x0000 }, + { 0x7834, 0x0000 }, + { 0x7835, 0x0000 }, + { 0x7836, 0x0000 }, + { 0x7837, 0x0000 }, + { 0x7838, 0x0000 }, + { 0x7839, 0x0000 }, + { 0x783a, 0x0000 }, + { 0x783b, 0x0000 }, + { 0x783c, 0x0000 }, + { 0x783d, 0x0000 }, + { 0x783e, 0x0000 }, + { 0x783f, 0x0000 }, + { 0x7840, 0x0000 }, + { 0x7841, 0x0000 }, + { 0x7842, 0x0000 }, + { 0x7843, 0x0000 }, + { 0x7844, 0x0000 }, + { 0x7845, 0x0000 }, + { 0x7846, 0x0000 }, + { 0x7847, 0x0000 }, + { 0x7848, 0x0000 }, + { 0x7849, 0x0000 }, + { 0x784a, 0x0000 }, + { 0x784b, 0x0000 }, + { 0x784c, 0x0000 }, + { 0x784d, 0x0000 }, + { 0x784e, 0x0000 }, + { 0x784f, 0x0000 }, + { 0x7850, 0x0000 }, + { 0x7851, 0x0000 }, + { 0x7852, 0x0000 }, + { 0x7853, 0x0000 }, + { 0x7854, 0x0000 }, + { 0x7855, 0x0000 }, + { 0x7856, 0x0000 }, + { 0x7857, 0x0000 }, + { 0x7858, 0x0000 }, + { 0x7859, 0x0000 }, + { 0x785a, 0x0000 }, + { 0x785b, 0x0000 }, + { 0x785c, 0x0000 }, + { 0x785d, 0x0000 }, + { 0x785e, 0x0000 }, + { 0x785f, 0x0000 }, + { 0x7860, 0x0000 }, + { 0x7861, 0x0000 }, + { 0x7862, 0x0000 }, + { 0x7863, 0x0000 }, + { 0x7864, 0x0000 }, + { 0x7865, 0x0000 }, + { 0x7866, 0x0000 }, + { 0x7867, 0x0000 }, + { 0x7868, 0x0000 }, + { 0x7869, 0x0000 }, + { 0x786a, 0x0000 }, + { 0x786b, 0x0000 }, + { 0x786c, 0x0000 }, + { 0x786d, 0x0000 }, + { 0x786e, 0x0000 }, + { 0x786f, 0x0000 }, + { 0x7870, 0x0000 }, + { 0x7871, 0x0000 }, + { 0x7872, 0x0000 }, + { 0x7873, 0x0000 }, + { 0x7874, 0x0000 }, + { 0x7875, 0x0000 }, + { 0x7876, 0x0000 }, + { 0x7877, 0x0000 }, + { 0x7878, 0x0000 }, + { 0x7879, 0x0000 }, + { 0x787a, 0x0000 }, + { 0x787b, 0x0000 }, + { 0x787c, 0x0000 }, + { 0x787d, 0x0000 }, + { 0x787e, 0x0000 }, + { 0x787f, 0x0000 }, + { 0x7880, 0x0000 }, + { 0x7881, 0x0000 }, + { 0x7882, 0x0000 }, + { 0x7883, 0x0000 }, + { 0x7884, 0x0000 }, + { 0x7885, 0x0000 }, + { 0x7886, 0x0000 }, + { 0x7887, 0x0000 }, + { 0x7888, 0x0000 }, + { 0x7889, 0x0000 }, + { 0x788a, 0x0000 }, + { 0x788b, 0x0000 }, + { 0x788c, 0x0000 }, + { 0x788d, 0x0000 }, + { 0x788e, 0x0000 }, + { 0x788f, 0x0000 }, + { 0x7890, 0x0000 }, + { 0x7891, 0x0000 }, + { 0x7892, 0x0000 }, + { 0x7893, 0x0000 }, + { 0x7894, 0x0000 }, + { 0x7895, 0x0000 }, + { 0x7896, 0x0000 }, + { 0x7897, 0x0000 }, + { 0x7898, 0x0000 }, + { 0x7899, 0x0000 }, + { 0x789a, 0x0000 }, + { 0x789b, 0x0000 }, + { 0x789c, 0x0000 }, + { 0x789d, 0x0000 }, + { 0x789e, 0x0000 }, + { 0x789f, 0x0000 }, + { 0x78a0, 0x0000 }, + { 0x78a1, 0x0000 }, + { 0x78a2, 0x0000 }, + { 0x78a3, 0x0000 }, + { 0x78a4, 0x0000 }, + { 0x78a5, 0x0000 }, + { 0x78a6, 0x0000 }, + { 0x78a7, 0x0000 }, + { 0x78a8, 0x0000 }, + { 0x78a9, 0x0000 }, + { 0x78aa, 0x0000 }, + { 0x78ab, 0x0000 }, + { 0x78ac, 0x0000 }, + { 0x78ad, 0x0000 }, + { 0x78ae, 0x0000 }, + { 0x78af, 0x0000 }, + { 0x78b0, 0x0000 }, + { 0x78b1, 0x0000 }, + { 0x78b2, 0x0000 }, + { 0x78b3, 0x0000 }, + { 0x78b4, 0x0000 }, + { 0x78b5, 0x0000 }, + { 0x78b6, 0x0000 }, + { 0x78b7, 0x0000 }, + { 0x78b8, 0x0000 }, + { 0x78b9, 0x0000 }, + { 0x78ba, 0x0000 }, + { 0x78bb, 0x0000 }, + { 0x78bc, 0x0000 }, + { 0x78bd, 0x0000 }, + { 0x78be, 0x0000 }, + { 0x78bf, 0x0000 }, + { 0x78c0, 0x0000 }, + { 0x78c1, 0x0000 }, + { 0x78c2, 0x0000 }, + { 0x78c3, 0x0000 }, + { 0x78c4, 0x0000 }, + { 0x78c5, 0x0000 }, + { 0x78c6, 0x0000 }, + { 0x78c7, 0x0000 }, + { 0x78c8, 0x0000 }, + { 0x78c9, 0x0000 }, + { 0x78ca, 0x0000 }, + { 0x78cb, 0x0000 }, + { 0x78cc, 0x0000 }, + { 0x78cd, 0x0000 }, + { 0x78ce, 0x0000 }, + { 0x78cf, 0x0000 }, + { 0x78d0, 0x0000 }, + { 0x78d1, 0x0000 }, + { 0x78d2, 0x0000 }, + { 0x78d3, 0x0000 }, + { 0x78d4, 0x0000 }, + { 0x78d5, 0x0000 }, + { 0x78d6, 0x0000 }, + { 0x78d7, 0x0000 }, + { 0x78d8, 0x0000 }, + { 0x78d9, 0x0000 }, + { 0x78da, 0x0000 }, + { 0x78db, 0x0000 }, + { 0x78dc, 0x0000 }, + { 0x78dd, 0x0000 }, + { 0x78de, 0x0000 }, + { 0x78df, 0x0000 }, + { 0x78e0, 0x0000 }, + { 0x78e1, 0x0000 }, + { 0x78e2, 0x0000 }, + { 0x78e3, 0x0000 }, + { 0x78e4, 0x0000 }, + { 0x78e5, 0x0000 }, + { 0x78e6, 0x0000 }, + { 0x78e7, 0x0000 }, + { 0x78e8, 0x0000 }, + { 0x78e9, 0x0000 }, + { 0x78ea, 0x0000 }, + { 0x78eb, 0x0000 }, + { 0x78ec, 0x0000 }, + { 0x78ed, 0x0000 }, + { 0x78ee, 0x0000 }, + { 0x78ef, 0x0000 }, + { 0x78f0, 0x0000 }, + { 0x78f1, 0x0000 }, + { 0x78f2, 0x0000 }, + { 0x78f3, 0x0000 }, + { 0x78f4, 0x0000 }, + { 0x78f5, 0x0000 }, + { 0x78f6, 0x0000 }, + { 0x78f7, 0x0000 }, + { 0x78f8, 0x0000 }, + { 0x78f9, 0x0000 }, + { 0x78fa, 0x0000 }, + { 0x78fb, 0x0000 }, + { 0x78fc, 0x0000 }, + { 0x78fd, 0x0000 }, + { 0x78fe, 0x0000 }, + { 0x78ff, 0x0000 }, + { 0x7900, 0x0000 }, + { 0x7901, 0x0000 }, + { 0x7902, 0x0000 }, + { 0x7903, 0x0000 }, + { 0x7904, 0x0000 }, + { 0x7905, 0x0000 }, + { 0x7906, 0x0000 }, + { 0x7907, 0x0000 }, + { 0x7908, 0x0000 }, + { 0x7909, 0x0000 }, + { 0x790a, 0x0000 }, + { 0x790b, 0x0000 }, + { 0x790c, 0x0000 }, + { 0x790d, 0x0000 }, + { 0x790e, 0x0000 }, + { 0x790f, 0x0000 }, + { 0x7910, 0x0000 }, + { 0x7911, 0x0000 }, + { 0x7912, 0x0000 }, + { 0x7913, 0x0000 }, + { 0x7914, 0x0000 }, + { 0x7915, 0x0000 }, + { 0x7916, 0x0000 }, + { 0x7917, 0x0000 }, + { 0x7918, 0x0000 }, + { 0x7919, 0x0000 }, + { 0x791a, 0x0000 }, + { 0x791b, 0x0000 }, + { 0x791c, 0x0000 }, + { 0x791d, 0x0000 }, + { 0x791e, 0x0000 }, + { 0x791f, 0x0000 }, + { 0x7920, 0x0000 }, + { 0x7921, 0x0000 }, + { 0x7922, 0x0000 }, + { 0x7923, 0x0000 }, + { 0x7924, 0x0000 }, + { 0x7925, 0x0000 }, + { 0x7926, 0x0000 }, + { 0x7927, 0x0000 }, + { 0x7928, 0x0000 }, + { 0x7929, 0x0000 }, + { 0x792a, 0x0000 }, + { 0x792b, 0x0000 }, + { 0x792c, 0x0000 }, + { 0x792d, 0x0000 }, + { 0x792e, 0x0000 }, + { 0x792f, 0x0000 }, + { 0x7930, 0x0000 }, + { 0x7931, 0x0000 }, + { 0x7932, 0x0000 }, + { 0x7933, 0x0000 }, + { 0x7934, 0x0000 }, + { 0x7935, 0x0000 }, + { 0x7936, 0x0000 }, + { 0x7937, 0x0000 }, + { 0x7938, 0x0000 }, + { 0x7939, 0x0000 }, + { 0x793a, 0x0000 }, + { 0x793b, 0x0000 }, + { 0x793c, 0x0000 }, + { 0x793d, 0x0000 }, + { 0x793e, 0x0000 }, + { 0x793f, 0x0000 }, + { 0x7940, 0x0000 }, + { 0x7941, 0x0000 }, + { 0x7942, 0x0000 }, + { 0x7943, 0x0000 }, + { 0x7944, 0x0000 }, + { 0x7945, 0x0000 }, + { 0x7946, 0x0000 }, + { 0x7947, 0x0000 }, + { 0x7948, 0x0000 }, + { 0x7949, 0x0000 }, + { 0x794a, 0x0000 }, + { 0x794b, 0x0000 }, + { 0x794c, 0x0000 }, + { 0x794d, 0x0000 }, + { 0x794e, 0x0000 }, + { 0x794f, 0x0000 }, + { 0x7950, 0x0000 }, + { 0x7951, 0x0000 }, + { 0x7952, 0x0000 }, + { 0x7953, 0x0000 }, + { 0x7954, 0x0000 }, + { 0x7955, 0x0000 }, + { 0x7956, 0x0000 }, + { 0x7957, 0x0000 }, + { 0x7958, 0x0000 }, + { 0x7959, 0x0000 }, + { 0x795a, 0x0000 }, + { 0x795b, 0x0000 }, + { 0x795c, 0x0000 }, + { 0x795d, 0x0000 }, + { 0x795e, 0x0000 }, + { 0x795f, 0x0000 }, + { 0x7960, 0x0000 }, + { 0x7961, 0x0000 }, + { 0x7962, 0x0000 }, + { 0x7963, 0x0000 }, + { 0x7964, 0x0000 }, + { 0x7965, 0x0000 }, + { 0x7966, 0x0000 }, + { 0x7967, 0x0000 }, + { 0x7968, 0x0000 }, + { 0x7969, 0x0000 }, + { 0x796a, 0x0000 }, + { 0x796b, 0x0000 }, + { 0x796c, 0x0000 }, + { 0x796d, 0x0000 }, + { 0x796e, 0x0000 }, + { 0x796f, 0x0000 }, + { 0x7970, 0x0000 }, + { 0x7971, 0x0000 }, + { 0x7972, 0x0000 }, + { 0x7973, 0x0000 }, + { 0x7974, 0x0000 }, + { 0x7975, 0x0000 }, + { 0x7976, 0x0000 }, + { 0x7977, 0x0000 }, + { 0x7978, 0x0000 }, + { 0x7979, 0x0000 }, + { 0x797a, 0x0000 }, + { 0x797b, 0x0000 }, + { 0x797c, 0x0000 }, + { 0x797d, 0x0000 }, + { 0x797e, 0x0000 }, + { 0x797f, 0x0000 }, + { 0x7980, 0x0000 }, + { 0x7981, 0x0000 }, + { 0x7982, 0x0000 }, + { 0x7983, 0x0000 }, + { 0x7984, 0x0000 }, + { 0x7985, 0x0000 }, + { 0x7986, 0x0000 }, + { 0x7987, 0x0000 }, + { 0x7988, 0x0000 }, + { 0x7989, 0x0000 }, + { 0x798a, 0x0000 }, + { 0x798b, 0x0000 }, + { 0x798c, 0x0000 }, + { 0x798d, 0x0000 }, + { 0x798e, 0x0000 }, + { 0x798f, 0x0000 }, + { 0x7990, 0x0000 }, + { 0x7991, 0x0000 }, + { 0x7992, 0x0000 }, + { 0x7993, 0x0000 }, + { 0x7994, 0x0000 }, + { 0x7995, 0x0000 }, + { 0x7996, 0x0000 }, + { 0x7997, 0x0000 }, + { 0x7998, 0x0000 }, + { 0x7999, 0x0000 }, + { 0x799a, 0x0000 }, + { 0x799b, 0x0000 }, + { 0x799c, 0x0000 }, + { 0x799d, 0x0000 }, + { 0x799e, 0x0000 }, + { 0x799f, 0x0000 }, + { 0x79a0, 0x0000 }, + { 0x79a1, 0x0000 }, + { 0x79a2, 0x0000 }, + { 0x79a3, 0x0000 }, + { 0x79a4, 0x0000 }, + { 0x79a5, 0x0000 }, + { 0x79a6, 0x0000 }, + { 0x79a7, 0x0000 }, + { 0x79a8, 0x0000 }, + { 0x79a9, 0x0000 }, + { 0x79aa, 0x0000 }, + { 0x79ab, 0x0000 }, + { 0x79ac, 0x0000 }, + { 0x79ad, 0x0000 }, + { 0x79ae, 0x0000 }, + { 0x79af, 0x0000 }, + { 0x79b0, 0x0000 }, + { 0x79b1, 0x0000 }, + { 0x79b2, 0x0000 }, + { 0x79b3, 0x0000 }, + { 0x79b4, 0x0000 }, + { 0x79b5, 0x0000 }, + { 0x79b6, 0x0000 }, + { 0x79b7, 0x0000 }, + { 0x79b8, 0x0000 }, + { 0x79b9, 0x0000 }, + { 0x79ba, 0x0000 }, + { 0x79bb, 0x0000 }, + { 0x79bc, 0x0000 }, + { 0x79bd, 0x0000 }, + { 0x79be, 0x0000 }, + { 0x79bf, 0x0000 }, + { 0x79c0, 0x0000 }, + { 0x79c1, 0x0000 }, + { 0x79c2, 0x0000 }, + { 0x79c3, 0x0000 }, + { 0x79c4, 0x0000 }, + { 0x79c5, 0x0000 }, + { 0x79c6, 0x0000 }, + { 0x79c7, 0x0000 }, + { 0x79c8, 0x0000 }, + { 0x79c9, 0x0000 }, + { 0x79ca, 0x0000 }, + { 0x79cb, 0x0000 }, + { 0x79cc, 0x0000 }, + { 0x79cd, 0x0000 }, + { 0x79ce, 0x0000 }, + { 0x79cf, 0x0000 }, + { 0x79d0, 0x0000 }, + { 0x79d1, 0x0000 }, + { 0x79d2, 0x0000 }, + { 0x79d3, 0x0000 }, + { 0x79d4, 0x0000 }, + { 0x79d5, 0x0000 }, + { 0x79d6, 0x0000 }, + { 0x79d7, 0x0000 }, + { 0x79d8, 0x0000 }, + { 0x79d9, 0x0000 }, + { 0x79da, 0x0000 }, + { 0x79db, 0x0000 }, + { 0x79dc, 0x0000 }, + { 0x79dd, 0x0000 }, + { 0x79de, 0x0000 }, + { 0x79df, 0x0000 }, + { 0x79e0, 0x0000 }, + { 0x79e1, 0x0000 }, + { 0x79e2, 0x0000 }, + { 0x79e3, 0x0000 }, + { 0x79e4, 0x0000 }, + { 0x79e5, 0x0000 }, + { 0x79e6, 0x0000 }, + { 0x79e7, 0x0000 }, + { 0x79e8, 0x0000 }, + { 0x79e9, 0x0000 }, + { 0x79ea, 0x0000 }, + { 0x79eb, 0x0000 }, + { 0x79ec, 0x0000 }, + { 0x79ed, 0x0000 }, + { 0x79ee, 0x0000 }, + { 0x79ef, 0x0000 }, + { 0x79f0, 0x0000 }, + { 0x79f1, 0x0000 }, + { 0x79f2, 0x0000 }, + { 0x79f3, 0x0000 }, + { 0x79f4, 0x0000 }, + { 0x79f5, 0x0000 }, + { 0x79f6, 0x0000 }, + { 0x79f7, 0x0000 }, + { 0x79f8, 0x0000 }, + { 0x79f9, 0x0000 }, + { 0x79fa, 0x0000 }, + { 0x79fb, 0x0000 }, + { 0x79fc, 0x0000 }, + { 0x79fd, 0x0000 }, + { 0x79fe, 0x0000 }, + { 0x79ff, 0x0000 }, + { 0x7a00, 0x0000 }, + { 0x7a01, 0x0000 }, + { 0x7a02, 0x0000 }, + { 0x7a03, 0x0000 }, + { 0x7a04, 0x0000 }, + { 0x7a05, 0x0000 }, + { 0x7a06, 0x0000 }, + { 0x7a07, 0x0000 }, + { 0x7a08, 0x0000 }, + { 0x7a09, 0x0000 }, + { 0x7a0a, 0x0000 }, + { 0x7a0b, 0x0000 }, + { 0x7a0c, 0x0000 }, + { 0x7a0d, 0x0000 }, + { 0x7a0e, 0x0000 }, + { 0x7a0f, 0x0000 }, + { 0x7a10, 0x0000 }, + { 0x7a11, 0x0000 }, + { 0x7a12, 0x0000 }, + { 0x7a13, 0x0000 }, + { 0x7a14, 0x0000 }, + { 0x7a15, 0x0000 }, + { 0x7a16, 0x0000 }, + { 0x7a17, 0x0000 }, + { 0x7a18, 0x0000 }, + { 0x7a19, 0x0000 }, + { 0x7a1a, 0x0000 }, + { 0x7a1b, 0x0000 }, + { 0x7a1c, 0x0000 }, + { 0x7a1d, 0x0000 }, + { 0x7a1e, 0x0000 }, + { 0x7a1f, 0x0000 }, + { 0x7a20, 0x0000 }, + { 0x7a21, 0x0000 }, + { 0x7a22, 0x0000 }, + { 0x7a23, 0x0000 }, + { 0x7a24, 0x0000 }, + { 0x7a25, 0x0000 }, + { 0x7a26, 0x0000 }, + { 0x7a27, 0x0000 }, + { 0x7a28, 0x0000 }, + { 0x7a29, 0x0000 }, + { 0x7a2a, 0x0000 }, + { 0x7a2b, 0x0000 }, + { 0x7a2c, 0x0000 }, + { 0x7a2d, 0x0000 }, + { 0x7a2e, 0x0000 }, + { 0x7a2f, 0x0000 }, + { 0x7a30, 0x0000 }, + { 0x7a31, 0x0000 }, + { 0x7a32, 0x0000 }, + { 0x7a33, 0x0000 }, + { 0x7a34, 0x0000 }, + { 0x7a35, 0x0000 }, + { 0x7a36, 0x0000 }, + { 0x7a37, 0x0000 }, + { 0x7a38, 0x0000 }, + { 0x7a39, 0x0000 }, + { 0x7a3a, 0x0000 }, + { 0x7a3b, 0x0000 }, + { 0x7a3c, 0x0000 }, + { 0x7a3d, 0x0000 }, + { 0x7a3e, 0x0000 }, + { 0x7a3f, 0x0000 }, + { 0x7a40, 0x0000 }, + { 0x7a41, 0x0000 }, + { 0x7a42, 0x0000 }, + { 0x7a43, 0x0000 }, + { 0x7a44, 0x0000 }, + { 0x7a45, 0x0000 }, + { 0x7a46, 0x0000 }, + { 0x7a47, 0x0000 }, + { 0x7a48, 0x0000 }, + { 0x7a49, 0x0000 }, + { 0x7a4a, 0x0000 }, + { 0x7a4b, 0x0000 }, + { 0x7a4c, 0x0000 }, + { 0x7a4d, 0x0000 }, + { 0x7a4e, 0x0000 }, + { 0x7a4f, 0x0000 }, + { 0x7a50, 0x0000 }, + { 0x7a51, 0x0000 }, + { 0x7a52, 0x0000 }, + { 0x7a53, 0x0000 }, + { 0x7a54, 0x0000 }, + { 0x7a55, 0x0000 }, + { 0x7a56, 0x0000 }, + { 0x7a57, 0x0000 }, + { 0x7a58, 0x0000 }, + { 0x7a59, 0x0000 }, + { 0x7a5a, 0x0000 }, + { 0x7a5b, 0x0000 }, + { 0x7a5c, 0x0000 }, + { 0x7a5d, 0x0000 }, + { 0x7a5e, 0x0000 }, + { 0x7a5f, 0x0000 }, + { 0x7a60, 0x0000 }, + { 0x7a61, 0x0000 }, + { 0x7a62, 0x0000 }, + { 0x7a63, 0x0000 }, + { 0x7a64, 0x0000 }, + { 0x7a65, 0x0000 }, + { 0x7a66, 0x0000 }, + { 0x7a67, 0x0000 }, + { 0x7a68, 0x0000 }, + { 0x7a69, 0x0000 }, + { 0x7a6a, 0x0000 }, + { 0x7a6b, 0x0000 }, + { 0x7a6c, 0x0000 }, + { 0x7a6d, 0x0000 }, + { 0x7a6e, 0x0000 }, + { 0x7a6f, 0x0000 }, + { 0x7a70, 0x0000 }, + { 0x7a71, 0x0000 }, + { 0x7a72, 0x0000 }, + { 0x7a73, 0x0000 }, + { 0x7a74, 0x0000 }, + { 0x7a75, 0x0000 }, + { 0x7a76, 0x0000 }, + { 0x7a77, 0x0000 }, + { 0x7a78, 0x0000 }, + { 0x7a79, 0x0000 }, + { 0x7a7a, 0x0000 }, + { 0x7a7b, 0x0000 }, + { 0x7a7c, 0x0000 }, + { 0x7a7d, 0x0000 }, + { 0x7a7e, 0x0000 }, + { 0x7a7f, 0x0000 }, + { 0x7a80, 0x0000 }, + { 0x7a81, 0x0000 }, + { 0x7a82, 0x0000 }, + { 0x7a83, 0x0000 }, + { 0x7a84, 0x0000 }, + { 0x7a85, 0x0000 }, + { 0x7a86, 0x0000 }, + { 0x7a87, 0x0000 }, + { 0x7a88, 0x0000 }, + { 0x7a89, 0x0000 }, + { 0x7a8a, 0x0000 }, + { 0x7a8b, 0x0000 }, + { 0x7a8c, 0x0000 }, + { 0x7a8d, 0x0000 }, + { 0x7a8e, 0x0000 }, + { 0x7a8f, 0x0000 }, + { 0x7a90, 0x0000 }, + { 0x7a91, 0x0000 }, + { 0x7a92, 0x0000 }, + { 0x7a93, 0x0000 }, + { 0x7a94, 0x0000 }, + { 0x7a95, 0x0000 }, + { 0x7a96, 0x0000 }, + { 0x7a97, 0x0000 }, + { 0x7a98, 0x0000 }, + { 0x7a99, 0x0000 }, + { 0x7a9a, 0x0000 }, + { 0x7a9b, 0x0000 }, + { 0x7a9c, 0x0000 }, + { 0x7a9d, 0x0000 }, + { 0x7a9e, 0x0000 }, + { 0x7a9f, 0x0000 }, + { 0x7aa0, 0x0000 }, + { 0x7aa1, 0x0000 }, + { 0x7aa2, 0x0000 }, + { 0x7aa3, 0x0000 }, + { 0x7aa4, 0x0000 }, + { 0x7aa5, 0x0000 }, + { 0x7aa6, 0x0000 }, + { 0x7aa7, 0x0000 }, + { 0x7aa8, 0x0000 }, + { 0x7aa9, 0x0000 }, + { 0x7aaa, 0x0000 }, + { 0x7aab, 0x0000 }, + { 0x7aac, 0x0000 }, + { 0x7aad, 0x0000 }, + { 0x7aae, 0x0000 }, + { 0x7aaf, 0x0000 }, + { 0x7ab0, 0x0000 }, + { 0x7ab1, 0x0000 }, + { 0x7ab2, 0x0000 }, + { 0x7ab3, 0x0000 }, + { 0x7ab4, 0x0000 }, + { 0x7ab5, 0x0000 }, + { 0x7ab6, 0x0000 }, + { 0x7ab7, 0x0000 }, + { 0x7ab8, 0x0000 }, + { 0x7ab9, 0x0000 }, + { 0x7aba, 0x0000 }, + { 0x7abb, 0x0000 }, + { 0x7abc, 0x0000 }, + { 0x7abd, 0x0000 }, + { 0x7abe, 0x0000 }, + { 0x7abf, 0x0000 }, + { 0x7ac0, 0x0000 }, + { 0x7ac1, 0x0000 }, + { 0x7ac2, 0x0000 }, + { 0x7ac3, 0x0000 }, + { 0x7ac4, 0x0000 }, + { 0x7ac5, 0x0000 }, + { 0x7ac6, 0x0000 }, + { 0x7ac7, 0x0000 }, + { 0x7ac8, 0x0000 }, + { 0x7ac9, 0x0000 }, + { 0x7aca, 0x0000 }, + { 0x7acb, 0x0000 }, + { 0x7acc, 0x0000 }, + { 0x7acd, 0x0000 }, + { 0x7ace, 0x0000 }, + { 0x7acf, 0x0000 }, + { 0x7ad0, 0x0000 }, + { 0x7ad1, 0x0000 }, + { 0x7ad2, 0x0000 }, + { 0x7ad3, 0x0000 }, + { 0x7ad4, 0x0000 }, + { 0x7ad5, 0x0000 }, + { 0x7ad6, 0x0000 }, + { 0x7ad7, 0x0000 }, + { 0x7ad8, 0x0000 }, + { 0x7ad9, 0x0000 }, + { 0x7ada, 0x0000 }, + { 0x7adb, 0x0000 }, + { 0x7adc, 0x0000 }, + { 0x7add, 0x0000 }, + { 0x7ade, 0x0000 }, + { 0x7adf, 0x0000 }, + { 0x7ae0, 0x0000 }, + { 0x7ae1, 0x0000 }, + { 0x7ae2, 0x0000 }, + { 0x7ae3, 0x0000 }, + { 0x7ae4, 0x0000 }, + { 0x7ae5, 0x0000 }, + { 0x7ae6, 0x0000 }, + { 0x7ae7, 0x0000 }, + { 0x7ae8, 0x0000 }, + { 0x7ae9, 0x0000 }, + { 0x7aea, 0x0000 }, + { 0x7aeb, 0x0000 }, + { 0x7aec, 0x0000 }, + { 0x7aed, 0x0000 }, + { 0x7aee, 0x0000 }, + { 0x7aef, 0x0000 }, + { 0x7af0, 0x0000 }, + { 0x7af1, 0x0000 }, + { 0x7af2, 0x0000 }, + { 0x7af3, 0x0000 }, + { 0x7af4, 0x0000 }, + { 0x7af5, 0x0000 }, + { 0x7af6, 0x0000 }, + { 0x7af7, 0x0000 }, + { 0x7af8, 0x0000 }, + { 0x7af9, 0x0000 }, + { 0x7afa, 0x0000 }, + { 0x7afb, 0x0000 }, + { 0x7afc, 0x0000 }, + { 0x7afd, 0x0000 }, + { 0x7afe, 0x0000 }, + { 0x7aff, 0x0000 }, + { 0x7b00, 0x0000 }, + { 0x7b01, 0x0000 }, + { 0x7b02, 0x0000 }, + { 0x7b03, 0x0000 }, + { 0x7b04, 0x0000 }, + { 0x7b05, 0x0000 }, + { 0x7b06, 0x0000 }, + { 0x7b07, 0x0000 }, + { 0x7b08, 0x0000 }, + { 0x7b09, 0x0000 }, + { 0x7b0a, 0x0000 }, + { 0x7b0b, 0x0000 }, + { 0x7b0c, 0x0000 }, + { 0x7b0d, 0x0000 }, + { 0x7b0e, 0x0000 }, + { 0x7b0f, 0x0000 }, + { 0x7b10, 0x0000 }, + { 0x7b11, 0x0000 }, + { 0x7b12, 0x0000 }, + { 0x7b13, 0x0000 }, + { 0x7b14, 0x0000 }, + { 0x7b15, 0x0000 }, + { 0x7b16, 0x0000 }, + { 0x7b17, 0x0000 }, + { 0x7b18, 0x0000 }, + { 0x7b19, 0x0000 }, + { 0x7b1a, 0x0000 }, + { 0x7b1b, 0x0000 }, + { 0x7b1c, 0x0000 }, + { 0x7b1d, 0x0000 }, + { 0x7b1e, 0x0000 }, + { 0x7b1f, 0x0000 }, + { 0x7b20, 0x0000 }, + { 0x7b21, 0x0000 }, + { 0x7b22, 0x0000 }, + { 0x7b23, 0x0000 }, + { 0x7b24, 0x0000 }, + { 0x7b25, 0x0000 }, + { 0x7b26, 0x0000 }, + { 0x7b27, 0x0000 }, + { 0x7b28, 0x0000 }, + { 0x7b29, 0x0000 }, + { 0x7b2a, 0x0000 }, + { 0x7b2b, 0x0000 }, + { 0x7b2c, 0x0000 }, + { 0x7b2d, 0x0000 }, + { 0x7b2e, 0x0000 }, + { 0x7b2f, 0x0000 }, + { 0x7b30, 0x0000 }, + { 0x7b31, 0x0000 }, + { 0x7b32, 0x0000 }, + { 0x7b33, 0x0000 }, + { 0x7b34, 0x0000 }, + { 0x7b35, 0x0000 }, + { 0x7b36, 0x0000 }, + { 0x7b37, 0x0000 }, + { 0x7b38, 0x0000 }, + { 0x7b39, 0x0000 }, + { 0x7b3a, 0x0000 }, + { 0x7b3b, 0x0000 }, + { 0x7b3c, 0x0000 }, + { 0x7b3d, 0x0000 }, + { 0x7b3e, 0x0000 }, + { 0x7b3f, 0x0000 }, + { 0x7b40, 0x0000 }, + { 0x7b41, 0x0000 }, + { 0x7b42, 0x0000 }, + { 0x7b43, 0x0000 }, + { 0x7b44, 0x0000 }, + { 0x7b45, 0x0000 }, + { 0x7b46, 0x0000 }, + { 0x7b47, 0x0000 }, + { 0x7b48, 0x0000 }, + { 0x7b49, 0x0000 }, + { 0x7b4a, 0x0000 }, + { 0x7b4b, 0x0000 }, + { 0x7b4c, 0x0000 }, + { 0x7b4d, 0x0000 }, + { 0x7b4e, 0x0000 }, + { 0x7b4f, 0x0000 }, + { 0x7b50, 0x0000 }, + { 0x7b51, 0x0000 }, + { 0x7b52, 0x0000 }, + { 0x7b53, 0x0000 }, + { 0x7b54, 0x0000 }, + { 0x7b55, 0x0000 }, + { 0x7b56, 0x0000 }, + { 0x7b57, 0x0000 }, + { 0x7b58, 0x0000 }, + { 0x7b59, 0x0000 }, + { 0x7b5a, 0x0000 }, + { 0x7b5b, 0x0000 }, + { 0x7b5c, 0x0000 }, + { 0x7b5d, 0x0000 }, + { 0x7b5e, 0x0000 }, + { 0x7b5f, 0x0000 }, + { 0x7b60, 0x0000 }, + { 0x7b61, 0x0000 }, + { 0x7b62, 0x0000 }, + { 0x7b63, 0x0000 }, + { 0x7b64, 0x0000 }, + { 0x7b65, 0x0000 }, + { 0x7b66, 0x0000 }, + { 0x7b67, 0x0000 }, + { 0x7b68, 0x0000 }, + { 0x7b69, 0x0000 }, + { 0x7b6a, 0x0000 }, + { 0x7b6b, 0x0000 }, + { 0x7b6c, 0x0000 }, + { 0x7b6d, 0x0000 }, + { 0x7b6e, 0x0000 }, + { 0x7b6f, 0x0000 }, + { 0x7b70, 0x0000 }, + { 0x7b71, 0x0000 }, + { 0x7b72, 0x0000 }, + { 0x7b73, 0x0000 }, + { 0x7b74, 0x0000 }, + { 0x7b75, 0x0000 }, + { 0x7b76, 0x0000 }, + { 0x7b77, 0x0000 }, + { 0x7b78, 0x0000 }, + { 0x7b79, 0x0000 }, + { 0x7b7a, 0x0000 }, + { 0x7b7b, 0x0000 }, + { 0x7b7c, 0x0000 }, + { 0x7b7d, 0x0000 }, + { 0x7b7e, 0x0000 }, + { 0x7b7f, 0x0000 }, + { 0x7b80, 0x0000 }, + { 0x7b81, 0x0000 }, + { 0x7b82, 0x0000 }, + { 0x7b83, 0x0000 }, + { 0x7b84, 0x0000 }, + { 0x7b85, 0x0000 }, + { 0x7b86, 0x0000 }, + { 0x7b87, 0x0000 }, + { 0x7b88, 0x0000 }, + { 0x7b89, 0x0000 }, + { 0x7b8a, 0x0000 }, + { 0x7b8b, 0x0000 }, + { 0x7b8c, 0x0000 }, + { 0x7b8d, 0x0000 }, + { 0x7b8e, 0x0000 }, + { 0x7b8f, 0x0000 }, + { 0x7b90, 0x0000 }, + { 0x7b91, 0x0000 }, + { 0x7b92, 0x0000 }, + { 0x7b93, 0x0000 }, + { 0x7b94, 0x0000 }, + { 0x7b95, 0x0000 }, + { 0x7b96, 0x0000 }, + { 0x7b97, 0x0000 }, + { 0x7b98, 0x0000 }, + { 0x7b99, 0x0000 }, + { 0x7b9a, 0x0000 }, + { 0x7b9b, 0x0000 }, + { 0x7b9c, 0x0000 }, + { 0x7b9d, 0x0000 }, + { 0x7b9e, 0x0000 }, + { 0x7b9f, 0x0000 }, + { 0x7ba0, 0x0000 }, + { 0x7ba1, 0x0000 }, + { 0x7ba2, 0x0000 }, + { 0x7ba3, 0x0000 }, + { 0x7ba4, 0x0000 }, + { 0x7ba5, 0x0000 }, + { 0x7ba6, 0x0000 }, + { 0x7ba7, 0x0000 }, + { 0x7ba8, 0x0000 }, + { 0x7ba9, 0x0000 }, + { 0x7baa, 0x0000 }, + { 0x7bab, 0x0000 }, + { 0x7bac, 0x0000 }, + { 0x7bad, 0x0000 }, + { 0x7bae, 0x0000 }, + { 0x7baf, 0x0000 }, + { 0x7bb0, 0x0000 }, + { 0x7bb1, 0x0000 }, + { 0x7bb2, 0x0000 }, + { 0x7bb3, 0x0000 }, + { 0x7bb4, 0x0000 }, + { 0x7bb5, 0x0000 }, + { 0x7bb6, 0x0000 }, + { 0x7bb7, 0x0000 }, + { 0x7bb8, 0x0000 }, + { 0x7bb9, 0x0000 }, + { 0x7bba, 0x0000 }, + { 0x7bbb, 0x0000 }, + { 0x7bbc, 0x0000 }, + { 0x7bbd, 0x0000 }, + { 0x7bbe, 0x0000 }, + { 0x7bbf, 0x0000 }, + { 0x7bc0, 0x0000 }, + { 0x7bc1, 0x0000 }, + { 0x7bc2, 0x0000 }, + { 0x7bc3, 0x0000 }, + { 0x7bc4, 0x0000 }, + { 0x7bc5, 0x0000 }, + { 0x7bc6, 0x0000 }, + { 0x7bc7, 0x0000 }, + { 0x7bc8, 0x0000 }, + { 0x7bc9, 0x0000 }, + { 0x7bca, 0x0000 }, + { 0x7bcb, 0x0000 }, + { 0x7bcc, 0x0000 }, + { 0x7bcd, 0x0000 }, + { 0x7bce, 0x0000 }, + { 0x7bcf, 0x0000 }, + { 0x7bd0, 0x0000 }, + { 0x7bd1, 0x0000 }, + { 0x7bd2, 0x0000 }, + { 0x7bd3, 0x0000 }, + { 0x7bd4, 0x0000 }, + { 0x7bd5, 0x0000 }, + { 0x7bd6, 0x0000 }, + { 0x7bd7, 0x0000 }, + { 0x7bd8, 0x0000 }, + { 0x7bd9, 0x0000 }, + { 0x7bda, 0x0000 }, + { 0x7bdb, 0x0000 }, + { 0x7bdc, 0x0000 }, + { 0x7bdd, 0x0000 }, + { 0x7bde, 0x0000 }, + { 0x7bdf, 0x0000 }, + { 0x7be0, 0x0000 }, + { 0x7be1, 0x0000 }, + { 0x7be2, 0x0000 }, + { 0x7be3, 0x0000 }, + { 0x7be4, 0x0000 }, + { 0x7be5, 0x0000 }, + { 0x7be6, 0x0000 }, + { 0x7be7, 0x0000 }, + { 0x7be8, 0x0000 }, + { 0x7be9, 0x0000 }, + { 0x7bea, 0x0000 }, + { 0x7beb, 0x0000 }, + { 0x7bec, 0x0000 }, + { 0x7bed, 0x0000 }, + { 0x7bee, 0x0000 }, + { 0x7bef, 0x0000 }, + { 0x7bf0, 0x0000 }, + { 0x7bf1, 0x0000 }, + { 0x7bf2, 0x0000 }, + { 0x7bf3, 0x0000 }, + { 0x7bf4, 0x0000 }, + { 0x7bf5, 0x0000 }, + { 0x7bf6, 0x0000 }, + { 0x7bf7, 0x0000 }, + { 0x7bf8, 0x0000 }, + { 0x7bf9, 0x0000 }, + { 0x7bfa, 0x0000 }, + { 0x7bfb, 0x0000 }, + { 0x7bfc, 0x0000 }, + { 0x7bfd, 0x0000 }, + { 0x7bfe, 0x0000 }, + { 0x7bff, 0x0000 }, + { 0x7c00, 0x0000 }, + { 0x7c01, 0x0000 }, + { 0x7c02, 0x0000 }, + { 0x7c03, 0x0000 }, + { 0x7c04, 0x0000 }, + { 0x7c05, 0x0000 }, + { 0x7c06, 0x0000 }, + { 0x7c07, 0x0000 }, + { 0x7c08, 0x0000 }, + { 0x7c09, 0x0000 }, + { 0x7c0a, 0x0000 }, + { 0x7c0b, 0x0000 }, + { 0x7c0c, 0x0000 }, + { 0x7c0d, 0x0000 }, + { 0x7c0e, 0x0000 }, + { 0x7c0f, 0x0000 }, + { 0x7c10, 0x0000 }, + { 0x7c11, 0x0000 }, + { 0x7c12, 0x0000 }, + { 0x7c13, 0x0000 }, + { 0x7c14, 0x0000 }, + { 0x7c15, 0x0000 }, + { 0x7c16, 0x0000 }, + { 0x7c17, 0x0000 }, + { 0x7c18, 0x0000 }, + { 0x7c19, 0x0000 }, + { 0x7c1a, 0x0000 }, + { 0x7c1b, 0x0000 }, + { 0x7c1c, 0x0000 }, + { 0x7c1d, 0x0000 }, + { 0x7c1e, 0x0000 }, + { 0x7c1f, 0x0000 }, + { 0x7c20, 0x0000 }, + { 0x7c21, 0x0000 }, + { 0x7c22, 0x0000 }, + { 0x7c23, 0x0000 }, + { 0x7c24, 0x0000 }, + { 0x7c25, 0x0000 }, + { 0x7c26, 0x0000 }, + { 0x7c27, 0x0000 }, + { 0x7c28, 0x0000 }, + { 0x7c29, 0x0000 }, + { 0x7c2a, 0x0000 }, + { 0x7c2b, 0x0000 }, + { 0x7c2c, 0x0000 }, + { 0x7c2d, 0x0000 }, + { 0x7c2e, 0x0000 }, + { 0x7c2f, 0x0000 }, + { 0x7c30, 0x0000 }, + { 0x7c31, 0x0000 }, + { 0x7c32, 0x0000 }, + { 0x7c33, 0x0000 }, + { 0x7c34, 0x0000 }, + { 0x7c35, 0x0000 }, + { 0x7c36, 0x0000 }, + { 0x7c37, 0x0000 }, + { 0x7c38, 0x0000 }, + { 0x7c39, 0x0000 }, + { 0x7c3a, 0x0000 }, + { 0x7c3b, 0x0000 }, + { 0x7c3c, 0x0000 }, + { 0x7c3d, 0x0000 }, + { 0x7c3e, 0x0000 }, + { 0x7c3f, 0x0000 }, + { 0x7c40, 0x0000 }, + { 0x7c41, 0x0000 }, + { 0x7c42, 0x0000 }, + { 0x7c43, 0x0000 }, + { 0x7c44, 0x0000 }, + { 0x7c45, 0x0000 }, + { 0x7c46, 0x0000 }, + { 0x7c47, 0x0000 }, + { 0x7c48, 0x0000 }, + { 0x7c49, 0x0000 }, + { 0x7c4a, 0x0000 }, + { 0x7c4b, 0x0000 }, + { 0x7c4c, 0x0000 }, + { 0x7c4d, 0x0000 }, + { 0x7c4e, 0x0000 }, + { 0x7c4f, 0x0000 }, + { 0x7c50, 0x0000 }, + { 0x7c51, 0x0000 }, + { 0x7c52, 0x0000 }, + { 0x7c53, 0x0000 }, + { 0x7c54, 0x0000 }, + { 0x7c55, 0x0000 }, + { 0x7c56, 0x0000 }, + { 0x7c57, 0x0000 }, + { 0x7c58, 0x0000 }, + { 0x7c59, 0x0000 }, + { 0x7c5a, 0x0000 }, + { 0x7c5b, 0x0000 }, + { 0x7c5c, 0x0000 }, + { 0x7c5d, 0x0000 }, + { 0x7c5e, 0x0000 }, + { 0x7c5f, 0x0000 }, + { 0x7c60, 0x0000 }, + { 0x7c61, 0x0000 }, + { 0x7c62, 0x0000 }, + { 0x7c63, 0x0000 }, + { 0x7c64, 0x0000 }, + { 0x7c65, 0x0000 }, + { 0x7c66, 0x0000 }, + { 0x7c67, 0x0000 }, + { 0x7c68, 0x0000 }, + { 0x7c69, 0x0000 }, + { 0x7c6a, 0x0000 }, + { 0x7c6b, 0x0000 }, + { 0x7c6c, 0x0000 }, + { 0x7c6d, 0x0000 }, + { 0x7c6e, 0x0000 }, + { 0x7c6f, 0x0000 }, + { 0x7c70, 0x0000 }, + { 0x7c71, 0x0000 }, + { 0x7c72, 0x0000 }, + { 0x7c73, 0x0000 }, + { 0x7c74, 0x0000 }, + { 0x7c75, 0x0000 }, + { 0x7c76, 0x0000 }, + { 0x7c77, 0x0000 }, + { 0x7c78, 0x0000 }, + { 0x7c79, 0x0000 }, + { 0x7c7a, 0x0000 }, + { 0x7c7b, 0x0000 }, + { 0x7c7c, 0x0000 }, + { 0x7c7d, 0x0000 }, + { 0x7c7e, 0x0000 }, + { 0x7c7f, 0x0000 }, + { 0x7c80, 0x0000 }, + { 0x7c81, 0x0000 }, + { 0x7c82, 0x0000 }, + { 0x7c83, 0x0000 }, + { 0x7c84, 0x0000 }, + { 0x7c85, 0x0000 }, + { 0x7c86, 0x0000 }, + { 0x7c87, 0x0000 }, + { 0x7c88, 0x0000 }, + { 0x7c89, 0x0000 }, + { 0x7c8a, 0x0000 }, + { 0x7c8b, 0x0000 }, + { 0x7c8c, 0x0000 }, + { 0x7c8d, 0x0000 }, + { 0x7c8e, 0x0000 }, + { 0x7c8f, 0x0000 }, + { 0x7c90, 0x0000 }, + { 0x7c91, 0x0000 }, + { 0x7c92, 0x0000 }, + { 0x7c93, 0x0000 }, + { 0x7c94, 0x0000 }, + { 0x7c95, 0x0000 }, + { 0x7c96, 0x0000 }, + { 0x7c97, 0x0000 }, + { 0x7c98, 0x0000 }, + { 0x7c99, 0x0000 }, + { 0x7c9a, 0x0000 }, + { 0x7c9b, 0x0000 }, + { 0x7c9c, 0x0000 }, + { 0x7c9d, 0x0000 }, + { 0x7c9e, 0x0000 }, + { 0x7c9f, 0x0000 }, + { 0x7ca0, 0x0000 }, + { 0x7ca1, 0x0000 }, + { 0x7ca2, 0x0000 }, + { 0x7ca3, 0x0000 }, + { 0x7ca4, 0x0000 }, + { 0x7ca5, 0x0000 }, + { 0x7ca6, 0x0000 }, + { 0x7ca7, 0x0000 }, + { 0x7ca8, 0x0000 }, + { 0x7ca9, 0x0000 }, + { 0x7caa, 0x0000 }, + { 0x7cab, 0x0000 }, + { 0x7cac, 0x0000 }, + { 0x7cad, 0x0000 }, + { 0x7cae, 0x0000 }, + { 0x7caf, 0x0000 }, + { 0x7cb0, 0x0000 }, + { 0x7cb1, 0x0000 }, + { 0x7cb2, 0x0000 }, + { 0x7cb3, 0x0000 }, + { 0x7cb4, 0x0000 }, + { 0x7cb5, 0x0000 }, + { 0x7cb6, 0x0000 }, + { 0x7cb7, 0x0000 }, + { 0x7cb8, 0x0000 }, + { 0x7cb9, 0x0000 }, + { 0x7cba, 0x0000 }, + { 0x7cbb, 0x0000 }, + { 0x7cbc, 0x0000 }, + { 0x7cbd, 0x0000 }, + { 0x7cbe, 0x0000 }, + { 0x7cbf, 0x0000 }, + { 0x7cc0, 0x0000 }, + { 0x7cc1, 0x0000 }, + { 0x7cc2, 0x0000 }, + { 0x7cc3, 0x0000 }, + { 0x7cc4, 0x0000 }, + { 0x7cc5, 0x0000 }, + { 0x7cc6, 0x0000 }, + { 0x7cc7, 0x0000 }, + { 0x7cc8, 0x0000 }, + { 0x7cc9, 0x0000 }, + { 0x7cca, 0x0000 }, + { 0x7ccb, 0x0000 }, + { 0x7ccc, 0x0000 }, + { 0x7ccd, 0x0000 }, + { 0x7cce, 0x0000 }, + { 0x7ccf, 0x0000 }, + { 0x7cd0, 0x0000 }, + { 0x7cd1, 0x0000 }, + { 0x7cd2, 0x0000 }, + { 0x7cd3, 0x0000 }, + { 0x7cd4, 0x0000 }, + { 0x7cd5, 0x0000 }, + { 0x7cd6, 0x0000 }, + { 0x7cd7, 0x0000 }, + { 0x7cd8, 0x0000 }, + { 0x7cd9, 0x0000 }, + { 0x7cda, 0x0000 }, + { 0x7cdb, 0x0000 }, + { 0x7cdc, 0x0000 }, + { 0x7cdd, 0x0000 }, + { 0x7cde, 0x0000 }, + { 0x7cdf, 0x0000 }, + { 0x7ce0, 0x0000 }, + { 0x7ce1, 0x0000 }, + { 0x7ce2, 0x0000 }, + { 0x7ce3, 0x0000 }, + { 0x7ce4, 0x0000 }, + { 0x7ce5, 0x0000 }, + { 0x7ce6, 0x0000 }, + { 0x7ce7, 0x0000 }, + { 0x7ce8, 0x0000 }, + { 0x7ce9, 0x0000 }, + { 0x7cea, 0x0000 }, + { 0x7ceb, 0x0000 }, + { 0x7cec, 0x0000 }, + { 0x7ced, 0x0000 }, + { 0x7cee, 0x0000 }, + { 0x7cef, 0x0000 }, + { 0x7cf0, 0x0000 }, + { 0x7cf1, 0x0000 }, + { 0x7cf2, 0x0000 }, + { 0x7cf3, 0x0000 }, + { 0x7cf4, 0x0000 }, + { 0x7cf5, 0x0000 }, + { 0x7cf6, 0x0000 }, + { 0x7cf7, 0x0000 }, + { 0x7cf8, 0x0000 }, + { 0x7cf9, 0x0000 }, + { 0x7cfa, 0x0000 }, + { 0x7cfb, 0x0000 }, + { 0x7cfc, 0x0000 }, + { 0x7cfd, 0x0000 }, + { 0x7cfe, 0x0000 }, + { 0x7cff, 0x0000 }, + { 0x7d00, 0x0000 }, + { 0x7d01, 0x0000 }, + { 0x7d02, 0x0000 }, + { 0x7d03, 0x0000 }, + { 0x7d04, 0x0000 }, + { 0x7d05, 0x0000 }, + { 0x7d06, 0x0000 }, + { 0x7d07, 0x0000 }, + { 0x7d08, 0x0000 }, + { 0x7d09, 0x0000 }, + { 0x7d0a, 0x0000 }, + { 0x7d0b, 0x0000 }, + { 0x7d0c, 0x0000 }, + { 0x7d0d, 0x0000 }, + { 0x7d0e, 0x0000 }, + { 0x7d0f, 0x0000 }, + { 0x7d10, 0x0000 }, + { 0x7d11, 0x0000 }, + { 0x7d12, 0x0000 }, + { 0x7d13, 0x0000 }, + { 0x7d14, 0x0000 }, + { 0x7d15, 0x0000 }, + { 0x7d16, 0x0000 }, + { 0x7d17, 0x0000 }, + { 0x7d18, 0x0000 }, + { 0x7d19, 0x0000 }, + { 0x7d1a, 0x0000 }, + { 0x7d1b, 0x0000 }, + { 0x7d1c, 0x0000 }, + { 0x7d1d, 0x0000 }, + { 0x7d1e, 0x0000 }, + { 0x7d1f, 0x0000 }, + { 0x7d20, 0x0000 }, + { 0x7d21, 0x0000 }, + { 0x7d22, 0x0000 }, + { 0x7d23, 0x0000 }, + { 0x7d24, 0x0000 }, + { 0x7d25, 0x0000 }, + { 0x7d26, 0x0000 }, + { 0x7d27, 0x0000 }, + { 0x7d28, 0x0000 }, + { 0x7d29, 0x0000 }, + { 0x7d2a, 0x0000 }, + { 0x7d2b, 0x0000 }, + { 0x7d2c, 0x0000 }, + { 0x7d2d, 0x0000 }, + { 0x7d2e, 0x0000 }, + { 0x7d2f, 0x0000 }, + { 0x7d30, 0x0000 }, + { 0x7d31, 0x0000 }, + { 0x7d32, 0x0000 }, + { 0x7d33, 0x0000 }, + { 0x7d34, 0x0000 }, + { 0x7d35, 0x0000 }, + { 0x7d36, 0x0000 }, + { 0x7d37, 0x0000 }, + { 0x7d38, 0x0000 }, + { 0x7d39, 0x0000 }, + { 0x7d3a, 0x0000 }, + { 0x7d3b, 0x0000 }, + { 0x7d3c, 0x0000 }, + { 0x7d3d, 0x0000 }, + { 0x7d3e, 0x0000 }, + { 0x7d3f, 0x0000 }, + { 0x7d40, 0x0000 }, + { 0x7d41, 0x0000 }, + { 0x7d42, 0x0000 }, + { 0x7d43, 0x0000 }, + { 0x7d44, 0x0000 }, + { 0x7d45, 0x0000 }, + { 0x7d46, 0x0000 }, + { 0x7d47, 0x0000 }, + { 0x7d48, 0x0000 }, + { 0x7d49, 0x0000 }, + { 0x7d4a, 0x0000 }, + { 0x7d4b, 0x0000 }, + { 0x7d4c, 0x0000 }, + { 0x7d4d, 0x0000 }, + { 0x7d4e, 0x0000 }, + { 0x7d4f, 0x0000 }, + { 0x7d50, 0x0000 }, + { 0x7d51, 0x0000 }, + { 0x7d52, 0x0000 }, + { 0x7d53, 0x0000 }, + { 0x7d54, 0x0000 }, + { 0x7d55, 0x0000 }, + { 0x7d56, 0x0000 }, + { 0x7d57, 0x0000 }, + { 0x7d58, 0x0000 }, + { 0x7d59, 0x0000 }, + { 0x7d5a, 0x0000 }, + { 0x7d5b, 0x0000 }, + { 0x7d5c, 0x0000 }, + { 0x7d5d, 0x0000 }, + { 0x7d5e, 0x0000 }, + { 0x7d5f, 0x0000 }, + { 0x7d60, 0x0000 }, + { 0x7d61, 0x0000 }, + { 0x7d62, 0x0000 }, + { 0x7d63, 0x0000 }, + { 0x7d64, 0x0000 }, + { 0x7d65, 0x0000 }, + { 0x7d66, 0x0000 }, + { 0x7d67, 0x0000 }, + { 0x7d68, 0x0000 }, + { 0x7d69, 0x0000 }, + { 0x7d6a, 0x0000 }, + { 0x7d6b, 0x0000 }, + { 0x7d6c, 0x0000 }, + { 0x7d6d, 0x0000 }, + { 0x7d6e, 0x0000 }, + { 0x7d6f, 0x0000 }, + { 0x7d70, 0x0000 }, + { 0x7d71, 0x0000 }, + { 0x7d72, 0x0000 }, + { 0x7d73, 0x0000 }, + { 0x7d74, 0x0000 }, + { 0x7d75, 0x0000 }, + { 0x7d76, 0x0000 }, + { 0x7d77, 0x0000 }, + { 0x7d78, 0x0000 }, + { 0x7d79, 0x0000 }, + { 0x7d7a, 0x0000 }, + { 0x7d7b, 0x0000 }, + { 0x7d7c, 0x0000 }, + { 0x7d7d, 0x0000 }, + { 0x7d7e, 0x0000 }, + { 0x7d7f, 0x0000 }, + { 0x7d80, 0x0000 }, + { 0x7d81, 0x0000 }, + { 0x7d82, 0x0000 }, + { 0x7d83, 0x0000 }, + { 0x7d84, 0x0000 }, + { 0x7d85, 0x0000 }, + { 0x7d86, 0x0000 }, + { 0x7d87, 0x0000 }, + { 0x7d88, 0x0000 }, + { 0x7d89, 0x0000 }, + { 0x7d8a, 0x0000 }, + { 0x7d8b, 0x0000 }, + { 0x7d8c, 0x0000 }, + { 0x7d8d, 0x0000 }, + { 0x7d8e, 0x0000 }, + { 0x7d8f, 0x0000 }, + { 0x7d90, 0x0000 }, + { 0x7d91, 0x0000 }, + { 0x7d92, 0x0000 }, + { 0x7d93, 0x0000 }, + { 0x7d94, 0x0000 }, + { 0x7d95, 0x0000 }, + { 0x7d96, 0x0000 }, + { 0x7d97, 0x0000 }, + { 0x7d98, 0x0000 }, + { 0x7d99, 0x0000 }, + { 0x7d9a, 0x0000 }, + { 0x7d9b, 0x0000 }, + { 0x7d9c, 0x0000 }, + { 0x7d9d, 0x0000 }, + { 0x7d9e, 0x0000 }, + { 0x7d9f, 0x0000 }, + { 0x7da0, 0x0000 }, + { 0x7da1, 0x0000 }, + { 0x7da2, 0x0000 }, + { 0x7da3, 0x0000 }, + { 0x7da4, 0x0000 }, + { 0x7da5, 0x0000 }, + { 0x7da6, 0x0000 }, + { 0x7da7, 0x0000 }, + { 0x7da8, 0x0000 }, + { 0x7da9, 0x0000 }, + { 0x7daa, 0x0000 }, + { 0x7dab, 0x0000 }, + { 0x7dac, 0x0000 }, + { 0x7dad, 0x0000 }, + { 0x7dae, 0x0000 }, + { 0x7daf, 0x0000 }, + { 0x7db0, 0x0000 }, + { 0x7db1, 0x0000 }, + { 0x7db2, 0x0000 }, + { 0x7db3, 0x0000 }, + { 0x7db4, 0x0000 }, + { 0x7db5, 0x0000 }, + { 0x7db6, 0x0000 }, + { 0x7db7, 0x0000 }, + { 0x7db8, 0x0000 }, + { 0x7db9, 0x0000 }, + { 0x7dba, 0x0000 }, + { 0x7dbb, 0x0000 }, + { 0x7dbc, 0x0000 }, + { 0x7dbd, 0x0000 }, + { 0x7dbe, 0x0000 }, + { 0x7dbf, 0x0000 }, + { 0x7dc0, 0x0000 }, + { 0x7dc1, 0x0000 }, + { 0x7dc2, 0x0000 }, + { 0x7dc3, 0x0000 }, + { 0x7dc4, 0x0000 }, + { 0x7dc5, 0x0000 }, + { 0x7dc6, 0x0000 }, + { 0x7dc7, 0x0000 }, + { 0x7dc8, 0x0000 }, + { 0x7dc9, 0x0000 }, + { 0x7dca, 0x0000 }, + { 0x7dcb, 0x0000 }, + { 0x7dcc, 0x0000 }, + { 0x7dcd, 0x0000 }, + { 0x7dce, 0x0000 }, + { 0x7dcf, 0x0000 }, + { 0x7dd0, 0x0000 }, + { 0x7dd1, 0x0000 }, + { 0x7dd2, 0x0000 }, + { 0x7dd3, 0x0000 }, + { 0x7dd4, 0x0000 }, + { 0x7dd5, 0x0000 }, + { 0x7dd6, 0x0000 }, + { 0x7dd7, 0x0000 }, + { 0x7dd8, 0x0000 }, + { 0x7dd9, 0x0000 }, + { 0x7dda, 0x0000 }, + { 0x7ddb, 0x0000 }, + { 0x7ddc, 0x0000 }, + { 0x7ddd, 0x0000 }, + { 0x7dde, 0x0000 }, + { 0x7ddf, 0x0000 }, + { 0x7de0, 0x0000 }, + { 0x7de1, 0x0000 }, + { 0x7de2, 0x0000 }, + { 0x7de3, 0x0000 }, + { 0x7de4, 0x0000 }, + { 0x7de5, 0x0000 }, + { 0x7de6, 0x0000 }, + { 0x7de7, 0x0000 }, + { 0x7de8, 0x0000 }, + { 0x7de9, 0x0000 }, + { 0x7dea, 0x0000 }, + { 0x7deb, 0x0000 }, + { 0x7dec, 0x0000 }, + { 0x7ded, 0x0000 }, + { 0x7dee, 0x0000 }, + { 0x7def, 0x0000 }, + { 0x7df0, 0x0000 }, + { 0x7df1, 0x0000 }, + { 0x7df2, 0x0000 }, + { 0x7df3, 0x0000 }, + { 0x7df4, 0x0000 }, + { 0x7df5, 0x0000 }, + { 0x7df6, 0x0000 }, + { 0x7df7, 0x0000 }, + { 0x7df8, 0x0000 }, + { 0x7df9, 0x0000 }, + { 0x7dfa, 0x0000 }, + { 0x7dfb, 0x0000 }, + { 0x7dfc, 0x0000 }, + { 0x7dfd, 0x0000 }, + { 0x7dfe, 0x0000 }, + { 0x7dff, 0x0000 }, + { 0x7e00, 0x0000 }, + { 0x7e01, 0x0000 }, + { 0x7e02, 0x0000 }, + { 0x7e03, 0x0000 }, + { 0x7e04, 0x0000 }, + { 0x7e05, 0x0000 }, + { 0x7e06, 0x0000 }, + { 0x7e07, 0x0000 }, + { 0x7e08, 0x0000 }, + { 0x7e09, 0x0000 }, + { 0x7e0a, 0x0000 }, + { 0x7e0b, 0x0000 }, + { 0x7e0c, 0x0000 }, + { 0x7e0d, 0x0000 }, + { 0x7e0e, 0x0000 }, + { 0x7e0f, 0x0000 }, + { 0x7e10, 0x0000 }, + { 0x7e11, 0x0000 }, + { 0x7e12, 0x0000 }, + { 0x7e13, 0x0000 }, + { 0x7e14, 0x0000 }, + { 0x7e15, 0x0000 }, + { 0x7e16, 0x0000 }, + { 0x7e17, 0x0000 }, + { 0x7e18, 0x0000 }, + { 0x7e19, 0x0000 }, + { 0x7e1a, 0x0000 }, + { 0x7e1b, 0x0000 }, + { 0x7e1c, 0x0000 }, + { 0x7e1d, 0x0000 }, + { 0x7e1e, 0x0000 }, + { 0x7e1f, 0x0000 }, + { 0x7e20, 0x0000 }, + { 0x7e21, 0x0000 }, + { 0x7e22, 0x0000 }, + { 0x7e23, 0x0000 }, + { 0x7e24, 0x0000 }, + { 0x7e25, 0x0000 }, + { 0x7e26, 0x0000 }, + { 0x7e27, 0x0000 }, + { 0x7e28, 0x0000 }, + { 0x7e29, 0x0000 }, + { 0x7e2a, 0x0000 }, + { 0x7e2b, 0x0000 }, + { 0x7e2c, 0x0000 }, + { 0x7e2d, 0x0000 }, + { 0x7e2e, 0x0000 }, + { 0x7e2f, 0x0000 }, + { 0x7e30, 0x0000 }, + { 0x7e31, 0x0000 }, + { 0x7e32, 0x0000 }, + { 0x7e33, 0x0000 }, + { 0x7e34, 0x0000 }, + { 0x7e35, 0x0000 }, + { 0x7e36, 0x0000 }, + { 0x7e37, 0x0000 }, + { 0x7e38, 0x0000 }, + { 0x7e39, 0x0000 }, + { 0x7e3a, 0x0000 }, + { 0x7e3b, 0x0000 }, + { 0x7e3c, 0x0000 }, + { 0x7e3d, 0x0000 }, + { 0x7e3e, 0x0000 }, + { 0x7e3f, 0x0000 }, + { 0x7e40, 0x0000 }, + { 0x7e41, 0x0000 }, + { 0x7e42, 0x0000 }, + { 0x7e43, 0x0000 }, + { 0x7e44, 0x0000 }, + { 0x7e45, 0x0000 }, + { 0x7e46, 0x0000 }, + { 0x7e47, 0x0000 }, + { 0x7e48, 0x0000 }, + { 0x7e49, 0x0000 }, + { 0x7e4a, 0x0000 }, + { 0x7e4b, 0x0000 }, + { 0x7e4c, 0x0000 }, + { 0x7e4d, 0x0000 }, + { 0x7e4e, 0x0000 }, + { 0x7e4f, 0x0000 }, + { 0x7e50, 0x0000 }, + { 0x7e51, 0x0000 }, + { 0x7e52, 0x0000 }, + { 0x7e53, 0x0000 }, + { 0x7e54, 0x0000 }, + { 0x7e55, 0x0000 }, + { 0x7e56, 0x0000 }, + { 0x7e57, 0x0000 }, + { 0x7e58, 0x0000 }, + { 0x7e59, 0x0000 }, + { 0x7e5a, 0x0000 }, + { 0x7e5b, 0x0000 }, + { 0x7e5c, 0x0000 }, + { 0x7e5d, 0x0000 }, + { 0x7e5e, 0x0000 }, + { 0x7e5f, 0x0000 }, + { 0x7e60, 0x0000 }, + { 0x7e61, 0x0000 }, + { 0x7e62, 0x0000 }, + { 0x7e63, 0x0000 }, + { 0x7e64, 0x0000 }, + { 0x7e65, 0x0000 }, + { 0x7e66, 0x0000 }, + { 0x7e67, 0x0000 }, + { 0x7e68, 0x0000 }, + { 0x7e69, 0x0000 }, + { 0x7e6a, 0x0000 }, + { 0x7e6b, 0x0000 }, + { 0x7e6c, 0x0000 }, + { 0x7e6d, 0x0000 }, + { 0x7e6e, 0x0000 }, + { 0x7e6f, 0x0000 }, + { 0x7e70, 0x0000 }, + { 0x7e71, 0x0000 }, + { 0x7e72, 0x0000 }, + { 0x7e73, 0x0000 }, + { 0x7e74, 0x0000 }, + { 0x7e75, 0x0000 }, + { 0x7e76, 0x0000 }, + { 0x7e77, 0x0000 }, + { 0x7e78, 0x0000 }, + { 0x7e79, 0x0000 }, + { 0x7e7a, 0x0000 }, + { 0x7e7b, 0x0000 }, + { 0x7e7c, 0x0000 }, + { 0x7e7d, 0x0000 }, + { 0x7e7e, 0x0000 }, + { 0x7e7f, 0x0000 }, + { 0x7e80, 0x0000 }, + { 0x7e81, 0x0000 }, + { 0x7e82, 0x0000 }, + { 0x7e83, 0x0000 }, + { 0x7e84, 0x0000 }, + { 0x7e85, 0x0000 }, + { 0x7e86, 0x0000 }, + { 0x7e87, 0x0000 }, + { 0x7e88, 0x0000 }, + { 0x7e89, 0x0000 }, + { 0x7e8a, 0x0000 }, + { 0x7e8b, 0x0000 }, + { 0x7e8c, 0x0000 }, + { 0x7e8d, 0x0000 }, + { 0x7e8e, 0x0000 }, + { 0x7e8f, 0x0000 }, + { 0x7e90, 0x0000 }, + { 0x7e91, 0x0000 }, + { 0x7e92, 0x0000 }, + { 0x7e93, 0x0000 }, + { 0x7e94, 0x0000 }, + { 0x7e95, 0x0000 }, + { 0x7e96, 0x0000 }, + { 0x7e97, 0x0000 }, + { 0x7e98, 0x0000 }, + { 0x7e99, 0x0000 }, + { 0x7e9a, 0x0000 }, + { 0x7e9b, 0x0000 }, + { 0x7e9c, 0x0000 }, + { 0x7e9d, 0x0000 }, + { 0x7e9e, 0x0000 }, + { 0x7e9f, 0x0000 }, + { 0x7ea0, 0x0000 }, + { 0x7ea1, 0x0000 }, + { 0x7ea2, 0x0000 }, + { 0x7ea3, 0x0000 }, + { 0x7ea4, 0x0000 }, + { 0x7ea5, 0x0000 }, + { 0x7ea6, 0x0000 }, + { 0x7ea7, 0x0000 }, + { 0x7ea8, 0x0000 }, + { 0x7ea9, 0x0000 }, + { 0x7eaa, 0x0000 }, + { 0x7eab, 0x0000 }, + { 0x7eac, 0x0000 }, + { 0x7ead, 0x0000 }, + { 0x7eae, 0x0000 }, + { 0x7eaf, 0x0000 }, + { 0x7eb0, 0x0000 }, + { 0x7eb1, 0x0000 }, + { 0x7eb2, 0x0000 }, + { 0x7eb3, 0x0000 }, + { 0x7eb4, 0x0000 }, + { 0x7eb5, 0x0000 }, + { 0x7eb6, 0x0000 }, + { 0x7eb7, 0x0000 }, + { 0x7eb8, 0x0000 }, + { 0x7eb9, 0x0000 }, + { 0x7eba, 0x0000 }, + { 0x7ebb, 0x0000 }, + { 0x7ebc, 0x0000 }, + { 0x7ebd, 0x0000 }, + { 0x7ebe, 0x0000 }, + { 0x7ebf, 0x0000 }, + { 0x7ec0, 0x0000 }, + { 0x7ec1, 0x0000 }, + { 0x7ec2, 0x0000 }, + { 0x7ec3, 0x0000 }, + { 0x7ec4, 0x0000 }, + { 0x7ec5, 0x0000 }, + { 0x7ec6, 0x0000 }, + { 0x7ec7, 0x0000 }, + { 0x7ec8, 0x0000 }, + { 0x7ec9, 0x0000 }, + { 0x7eca, 0x0000 }, + { 0x7ecb, 0x0000 }, + { 0x7ecc, 0x0000 }, + { 0x7ecd, 0x0000 }, + { 0x7ece, 0x0000 }, + { 0x7ecf, 0x0000 }, + { 0x7ed0, 0x0000 }, + { 0x7ed1, 0x0000 }, + { 0x7ed2, 0x0000 }, + { 0x7ed3, 0x0000 }, + { 0x7ed4, 0x0000 }, + { 0x7ed5, 0x0000 }, + { 0x7ed6, 0x0000 }, + { 0x7ed7, 0x0000 }, + { 0x7ed8, 0x0000 }, + { 0x7ed9, 0x0000 }, + { 0x7eda, 0x0000 }, + { 0x7edb, 0x0000 }, + { 0x7edc, 0x0000 }, + { 0x7edd, 0x0000 }, + { 0x7ede, 0x0000 }, + { 0x7edf, 0x0000 }, + { 0x7ee0, 0x0000 }, + { 0x7ee1, 0x0000 }, + { 0x7ee2, 0x0000 }, + { 0x7ee3, 0x0000 }, + { 0x7ee4, 0x0000 }, + { 0x7ee5, 0x0000 }, + { 0x7ee6, 0x0000 }, + { 0x7ee7, 0x0000 }, + { 0x7ee8, 0x0000 }, + { 0x7ee9, 0x0000 }, + { 0x7eea, 0x0000 }, + { 0x7eeb, 0x0000 }, + { 0x7eec, 0x0000 }, + { 0x7eed, 0x0000 }, + { 0x7eee, 0x0000 }, + { 0x7eef, 0x0000 }, + { 0x7ef0, 0x0000 }, + { 0x7ef1, 0x0000 }, + { 0x7ef2, 0x0000 }, + { 0x7ef3, 0x0000 }, + { 0x7ef4, 0x0000 }, + { 0x7ef5, 0x0000 }, + { 0x7ef6, 0x0000 }, + { 0x7ef7, 0x0000 }, + { 0x7ef8, 0x0000 }, + { 0x7ef9, 0x0000 }, + { 0x7efa, 0x0000 }, + { 0x7efb, 0x0000 }, + { 0x7efc, 0x0000 }, + { 0x7efd, 0x0000 }, + { 0x7efe, 0x0000 }, + { 0x7eff, 0x0000 }, + { 0x7f00, 0x0000 }, + { 0x7f01, 0x0000 }, + { 0x7f02, 0x0000 }, + { 0x7f03, 0x0000 }, + { 0x7f04, 0x0000 }, + { 0x7f05, 0x0000 }, + { 0x7f06, 0x0000 }, + { 0x7f07, 0x0000 }, + { 0x7f08, 0x0000 }, + { 0x7f09, 0x0000 }, + { 0x7f0a, 0x0000 }, + { 0x7f0b, 0x0000 }, + { 0x7f0c, 0x0000 }, + { 0x7f0d, 0x0000 }, + { 0x7f0e, 0x0000 }, + { 0x7f0f, 0x0000 }, + { 0x7f10, 0x0000 }, + { 0x7f11, 0x0000 }, + { 0x7f12, 0x0000 }, + { 0x7f13, 0x0000 }, + { 0x7f14, 0x0000 }, + { 0x7f15, 0x0000 }, + { 0x7f16, 0x0000 }, + { 0x7f17, 0x0000 }, + { 0x7f18, 0x0000 }, + { 0x7f19, 0x0000 }, + { 0x7f1a, 0x0000 }, + { 0x7f1b, 0x0000 }, + { 0x7f1c, 0x0000 }, + { 0x7f1d, 0x0000 }, + { 0x7f1e, 0x0000 }, + { 0x7f1f, 0x0000 }, + { 0x7f20, 0x0000 }, + { 0x7f21, 0x0000 }, + { 0x7f22, 0x0000 }, + { 0x7f23, 0x0000 }, + { 0x7f24, 0x0000 }, + { 0x7f25, 0x0000 }, + { 0x7f26, 0x0000 }, + { 0x7f27, 0x0000 }, + { 0x7f28, 0x0000 }, + { 0x7f29, 0x0000 }, + { 0x7f2a, 0x0000 }, + { 0x7f2b, 0x0000 }, + { 0x7f2c, 0x0000 }, + { 0x7f2d, 0x0000 }, + { 0x7f2e, 0x0000 }, + { 0x7f2f, 0x0000 }, + { 0x7f30, 0x0000 }, + { 0x7f31, 0x0000 }, + { 0x7f32, 0x0000 }, + { 0x7f33, 0x0000 }, + { 0x7f34, 0x0000 }, + { 0x7f35, 0x0000 }, + { 0x7f36, 0x0000 }, + { 0x7f37, 0x0000 }, + { 0x7f38, 0x0000 }, + { 0x7f39, 0x0000 }, + { 0x7f3a, 0x0000 }, + { 0x7f3b, 0x0000 }, + { 0x7f3c, 0x0000 }, + { 0x7f3d, 0x0000 }, + { 0x7f3e, 0x0000 }, + { 0x7f3f, 0x0000 }, + { 0x7f40, 0x0000 }, + { 0x7f41, 0x0000 }, + { 0x7f42, 0x0000 }, + { 0x7f43, 0x0000 }, + { 0x7f44, 0x0000 }, + { 0x7f45, 0x0000 }, + { 0x7f46, 0x0000 }, + { 0x7f47, 0x0000 }, + { 0x7f48, 0x0000 }, + { 0x7f49, 0x0000 }, + { 0x7f4a, 0x0000 }, + { 0x7f4b, 0x0000 }, + { 0x7f4c, 0x0000 }, + { 0x7f4d, 0x0000 }, + { 0x7f4e, 0x0000 }, + { 0x7f4f, 0x0000 }, + { 0x7f50, 0x0000 }, + { 0x7f51, 0x0000 }, + { 0x7f52, 0x0000 }, + { 0x7f53, 0x0000 }, + { 0x7f54, 0x0000 }, + { 0x7f55, 0x0000 }, + { 0x7f56, 0x0000 }, + { 0x7f57, 0x0000 }, + { 0x7f58, 0x0000 }, + { 0x7f59, 0x0000 }, + { 0x7f5a, 0x0000 }, + { 0x7f5b, 0x0000 }, + { 0x7f5c, 0x0000 }, + { 0x7f5d, 0x0000 }, + { 0x7f5e, 0x0000 }, + { 0x7f5f, 0x0000 }, + { 0x7f60, 0x0000 }, + { 0x7f61, 0x0000 }, + { 0x7f62, 0x0000 }, + { 0x7f63, 0x0000 }, + { 0x7f64, 0x0000 }, + { 0x7f65, 0x0000 }, + { 0x7f66, 0x0000 }, + { 0x7f67, 0x0000 }, + { 0x7f68, 0x0000 }, + { 0x7f69, 0x0000 }, + { 0x7f6a, 0x0000 }, + { 0x7f6b, 0x0000 }, + { 0x7f6c, 0x0000 }, + { 0x7f6d, 0x0000 }, + { 0x7f6e, 0x0000 }, + { 0x7f6f, 0x0000 }, + { 0x7f70, 0x0000 }, + { 0x7f71, 0x0000 }, + { 0x7f72, 0x0000 }, + { 0x7f73, 0x0000 }, + { 0x7f74, 0x0000 }, + { 0x7f75, 0x0000 }, + { 0x7f76, 0x0000 }, + { 0x7f77, 0x0000 }, + { 0x7f78, 0x0000 }, + { 0x7f79, 0x0000 }, + { 0x7f7a, 0x0000 }, + { 0x7f7b, 0x0000 }, + { 0x7f7c, 0x0000 }, + { 0x7f7d, 0x0000 }, + { 0x7f7e, 0x0000 }, + { 0x7f7f, 0x0000 }, + { 0x7f80, 0x0000 }, + { 0x7f81, 0x0000 }, + { 0x7f82, 0x0000 }, + { 0x7f83, 0x0000 }, + { 0x7f84, 0x0000 }, + { 0x7f85, 0x0000 }, + { 0x7f86, 0x0000 }, + { 0x7f87, 0x0000 }, + { 0x7f88, 0x0000 }, + { 0x7f89, 0x0000 }, + { 0x7f8a, 0x0000 }, + { 0x7f8b, 0x0000 }, + { 0x7f8c, 0x0000 }, + { 0x7f8d, 0x0000 }, + { 0x7f8e, 0x0000 }, + { 0x7f8f, 0x0000 }, + { 0x7f90, 0x0000 }, + { 0x7f91, 0x0000 }, + { 0x7f92, 0x0000 }, + { 0x7f93, 0x0000 }, + { 0x7f94, 0x0000 }, + { 0x7f95, 0x0000 }, + { 0x7f96, 0x0000 }, + { 0x7f97, 0x0000 }, + { 0x7f98, 0x0000 }, + { 0x7f99, 0x0000 }, + { 0x7f9a, 0x0000 }, + { 0x7f9b, 0x0000 }, + { 0x7f9c, 0x0000 }, + { 0x7f9d, 0x0000 }, + { 0x7f9e, 0x0000 }, + { 0x7f9f, 0x0000 }, + { 0x7fa0, 0x0000 }, + { 0x7fa1, 0x0000 }, + { 0x7fa2, 0x0000 }, + { 0x7fa3, 0x0000 }, + { 0x7fa4, 0x0000 }, + { 0x7fa5, 0x0000 }, + { 0x7fa6, 0x0000 }, + { 0x7fa7, 0x0000 }, + { 0x7fa8, 0x0000 }, + { 0x7fa9, 0x0000 }, + { 0x7faa, 0x0000 }, + { 0x7fab, 0x0000 }, + { 0x7fac, 0x0000 }, + { 0x7fad, 0x0000 }, + { 0x7fae, 0x0000 }, + { 0x7faf, 0x0000 }, + { 0x7fb0, 0x0000 }, + { 0x7fb1, 0x0000 }, + { 0x7fb2, 0x0000 }, + { 0x7fb3, 0x0000 }, + { 0x7fb4, 0x0000 }, + { 0x7fb5, 0x0000 }, + { 0x7fb6, 0x0000 }, + { 0x7fb7, 0x0000 }, + { 0x7fb8, 0x0000 }, + { 0x7fb9, 0x0000 }, + { 0x7fba, 0x0000 }, + { 0x7fbb, 0x0000 }, + { 0x7fbc, 0x0000 }, + { 0x7fbd, 0x0000 }, + { 0x7fbe, 0x0000 }, + { 0x7fbf, 0x0000 }, + { 0x7fc0, 0x0000 }, + { 0x7fc1, 0x0000 }, + { 0x7fc2, 0x0000 }, + { 0x7fc3, 0x0000 }, + { 0x7fc4, 0x0000 }, + { 0x7fc5, 0x0000 }, + { 0x7fc6, 0x0000 }, + { 0x7fc7, 0x0000 }, + { 0x7fc8, 0x0000 }, + { 0x7fc9, 0x0000 }, + { 0x7fca, 0x0000 }, + { 0x7fcb, 0x0000 }, + { 0x7fcc, 0x0000 }, + { 0x7fcd, 0x0000 }, + { 0x7fce, 0x0000 }, + { 0x7fcf, 0x0000 }, + { 0x7fd0, 0x0000 }, + { 0x7fd1, 0x0000 }, + { 0x7fd2, 0x0000 }, + { 0x7fd3, 0x0000 }, + { 0x7fd4, 0x0000 }, + { 0x7fd5, 0x0000 }, + { 0x7fd6, 0x0000 }, + { 0x7fd7, 0x0000 }, + { 0x7fd8, 0x0000 }, + { 0x7fd9, 0x0000 }, + { 0x7fda, 0x0000 }, + { 0x7fdb, 0x0000 }, + { 0x7fdc, 0x0000 }, + { 0x7fdd, 0x0000 }, + { 0x7fde, 0x0000 }, + { 0x7fdf, 0x0000 }, + { 0x7fe0, 0x0000 }, + { 0x7fe1, 0x0000 }, + { 0x7fe2, 0x0000 }, + { 0x7fe3, 0x0000 }, + { 0x7fe4, 0x0000 }, + { 0x7fe5, 0x0000 }, + { 0x7fe6, 0x0000 }, + { 0x7fe7, 0x0000 }, + { 0x7fe8, 0x0000 }, + { 0x7fe9, 0x0000 }, + { 0x7fea, 0x0000 }, + { 0x7feb, 0x0000 }, + { 0x7fec, 0x0000 }, + { 0x7fed, 0x0000 }, + { 0x7fee, 0x0000 }, + { 0x7fef, 0x0000 }, + { 0x7ff0, 0x0000 }, + { 0x7ff1, 0x0000 }, + { 0x7ff2, 0x0000 }, + { 0x7ff3, 0x0000 }, + { 0x7ff4, 0x0000 }, + { 0x7ff5, 0x0000 }, + { 0x7ff6, 0x0000 }, + { 0x7ff7, 0x0000 }, + { 0x7ff8, 0x0000 }, + { 0x7ff9, 0x0000 }, + { 0x7ffa, 0x0000 }, + { 0x7ffb, 0x0000 }, + { 0x7ffc, 0x0000 }, + { 0x7ffd, 0x0000 }, + { 0x7ffe, 0x0000 }, + { 0x7fff, 0x0000 }, + { 0x8000, 0x0000 }, + { 0x8001, 0x0000 }, + { 0x8002, 0x0000 }, + { 0x8003, 0x0000 }, + { 0x8004, 0x0000 }, + { 0x8005, 0x0000 }, + { 0x8006, 0x0000 }, + { 0x8007, 0x0000 }, + { 0x8008, 0x0000 }, + { 0x8009, 0x0000 }, + { 0x800a, 0x0000 }, + { 0x800b, 0x0000 }, + { 0x800c, 0x0000 }, + { 0x800d, 0x0000 }, + { 0x800e, 0x0000 }, + { 0x800f, 0x0000 }, + { 0x8010, 0x0000 }, + { 0x8011, 0x0000 }, + { 0x8012, 0x0000 }, + { 0x8013, 0x0000 }, + { 0x8014, 0x0000 }, + { 0x8015, 0x0000 }, + { 0x8016, 0x0000 }, + { 0x8017, 0x0000 }, + { 0x8018, 0x0000 }, + { 0x8019, 0x0000 }, + { 0x801a, 0x0000 }, + { 0x801b, 0x0000 }, + { 0x801c, 0x0000 }, + { 0x801d, 0x0000 }, + { 0x801e, 0x0000 }, + { 0x801f, 0x0000 }, + { 0x8020, 0x0000 }, + { 0x8021, 0x0000 }, + { 0x8022, 0x0000 }, + { 0x8023, 0x0000 }, + { 0x8024, 0x0000 }, + { 0x8025, 0x0000 }, + { 0x8026, 0x0000 }, + { 0x8027, 0x0000 }, + { 0x8028, 0x0000 }, + { 0x8029, 0x0000 }, + { 0x802a, 0x0000 }, + { 0x802b, 0x0000 }, + { 0x802c, 0x0000 }, + { 0x802d, 0x0000 }, + { 0x802e, 0x0000 }, + { 0x802f, 0x0000 }, + { 0x8030, 0x0000 }, + { 0x8031, 0x0000 }, + { 0x8032, 0x0000 }, + { 0x8033, 0x0000 }, + { 0x8034, 0x0000 }, + { 0x8035, 0x0000 }, + { 0x8036, 0x0000 }, + { 0x8037, 0x0000 }, + { 0x8038, 0x0000 }, + { 0x8039, 0x0000 }, + { 0x803a, 0x0000 }, + { 0x803b, 0x0000 }, + { 0x803c, 0x0000 }, + { 0x803d, 0x0000 }, + { 0x803e, 0x0000 }, + { 0x803f, 0x0000 }, + { 0x8040, 0x0000 }, + { 0x8041, 0x0000 }, + { 0x8042, 0x0000 }, + { 0x8043, 0x0000 }, + { 0x8044, 0x0000 }, + { 0x8045, 0x0000 }, + { 0x8046, 0x0000 }, + { 0x8047, 0x0000 }, + { 0x8048, 0x0000 }, + { 0x8049, 0x0000 }, + { 0x804a, 0x0000 }, + { 0x804b, 0x0000 }, + { 0x804c, 0x0000 }, + { 0x804d, 0x0000 }, + { 0x804e, 0x0000 }, + { 0x804f, 0x0000 }, + { 0x8050, 0x0000 }, + { 0x8051, 0x0000 }, + { 0x8052, 0x0000 }, + { 0x8053, 0x0000 }, + { 0x8054, 0x0000 }, + { 0x8055, 0x0000 }, + { 0x8056, 0x0000 }, + { 0x8057, 0x0000 }, + { 0x8058, 0x0000 }, + { 0x8059, 0x0000 }, + { 0x805a, 0x0000 }, + { 0x805b, 0x0000 }, + { 0x805c, 0x0000 }, + { 0x805d, 0x0000 }, + { 0x805e, 0x0000 }, + { 0x805f, 0x0000 }, + { 0x8060, 0x0000 }, + { 0x8061, 0x0000 }, + { 0x8062, 0x0000 }, + { 0x8063, 0x0000 }, + { 0x8064, 0x0000 }, + { 0x8065, 0x0000 }, + { 0x8066, 0x0000 }, + { 0x8067, 0x0000 }, + { 0x8068, 0x0000 }, + { 0x8069, 0x0000 }, + { 0x806a, 0x0000 }, + { 0x806b, 0x0000 }, + { 0x806c, 0x0000 }, + { 0x806d, 0x0000 }, + { 0x806e, 0x0000 }, + { 0x806f, 0x0000 }, + { 0x8070, 0x0000 }, + { 0x8071, 0x0000 }, + { 0x8072, 0x0000 }, + { 0x8073, 0x0000 }, + { 0x8074, 0x0000 }, + { 0x8075, 0x0000 }, + { 0x8076, 0x0000 }, + { 0x8077, 0x0000 }, + { 0x8078, 0x0000 }, + { 0x8079, 0x0000 }, + { 0x807a, 0x0000 }, + { 0x807b, 0x0000 }, + { 0x807c, 0x0000 }, + { 0x807d, 0x0000 }, + { 0x807e, 0x0000 }, + { 0x807f, 0x0000 }, + { 0x8080, 0x0000 }, + { 0x8081, 0x0000 }, + { 0x8082, 0x0000 }, + { 0x8083, 0x0000 }, + { 0x8084, 0x0000 }, + { 0x8085, 0x0000 }, + { 0x8086, 0x0000 }, + { 0x8087, 0x0000 }, + { 0x8088, 0x0000 }, + { 0x8089, 0x0000 }, + { 0x808a, 0x0000 }, + { 0x808b, 0x0000 }, + { 0x808c, 0x0000 }, + { 0x808d, 0x0000 }, + { 0x808e, 0x0000 }, + { 0x808f, 0x0000 }, + { 0x8090, 0x0000 }, + { 0x8091, 0x0000 }, + { 0x8092, 0x0000 }, + { 0x8093, 0x0000 }, + { 0x8094, 0x0000 }, + { 0x8095, 0x0000 }, + { 0x8096, 0x0000 }, + { 0x8097, 0x0000 }, + { 0x8098, 0x0000 }, + { 0x8099, 0x0000 }, + { 0x809a, 0x0000 }, + { 0x809b, 0x0000 }, + { 0x809c, 0x0000 }, + { 0x809d, 0x0000 }, + { 0x809e, 0x0000 }, + { 0x809f, 0x0000 }, + { 0x80a0, 0x0000 }, + { 0x80a1, 0x0000 }, + { 0x80a2, 0x0000 }, + { 0x80a3, 0x0000 }, + { 0x80a4, 0x0000 }, + { 0x80a5, 0x0000 }, + { 0x80a6, 0x0000 }, + { 0x80a7, 0x0000 }, + { 0x80a8, 0x0000 }, + { 0x80a9, 0x0000 }, + { 0x80aa, 0x0000 }, + { 0x80ab, 0x0000 }, + { 0x80ac, 0x0000 }, + { 0x80ad, 0x0000 }, + { 0x80ae, 0x0000 }, + { 0x80af, 0x0000 }, + { 0x80b0, 0x0000 }, + { 0x80b1, 0x0000 }, + { 0x80b2, 0x0000 }, + { 0x80b3, 0x0000 }, + { 0x80b4, 0x0000 }, + { 0x80b5, 0x0000 }, + { 0x80b6, 0x0000 }, + { 0x80b7, 0x0000 }, + { 0x80b8, 0x0000 }, + { 0x80b9, 0x0000 }, + { 0x80ba, 0x0000 }, + { 0x80bb, 0x0000 }, + { 0x80bc, 0x0000 }, + { 0x80bd, 0x0000 }, + { 0x80be, 0x0000 }, + { 0x80bf, 0x0000 }, + { 0x80c0, 0x0000 }, + { 0x80c1, 0x0000 }, + { 0x80c2, 0x0000 }, + { 0x80c3, 0x0000 }, + { 0x80c4, 0x0000 }, + { 0x80c5, 0x0000 }, + { 0x80c6, 0x0000 }, + { 0x80c7, 0x0000 }, + { 0x80c8, 0x0000 }, + { 0x80c9, 0x0000 }, + { 0x80ca, 0x0000 }, + { 0x80cb, 0x0000 }, + { 0x80cc, 0x0000 }, + { 0x80cd, 0x0000 }, + { 0x80ce, 0x0000 }, + { 0x80cf, 0x0000 }, + { 0x80d0, 0x0000 }, + { 0x80d1, 0x0000 }, + { 0x80d2, 0x0000 }, + { 0x80d3, 0x0000 }, + { 0x80d4, 0x0000 }, + { 0x80d5, 0x0000 }, + { 0x80d6, 0x0000 }, + { 0x80d7, 0x0000 }, + { 0x80d8, 0x0000 }, + { 0x80d9, 0x0000 }, + { 0x80da, 0x0000 }, + { 0x80db, 0x0000 }, + { 0x80dc, 0x0000 }, + { 0x80dd, 0x0000 }, + { 0x80de, 0x0000 }, + { 0x80df, 0x0000 }, + { 0x80e0, 0x0000 }, + { 0x80e1, 0x0000 }, + { 0x80e2, 0x0000 }, + { 0x80e3, 0x0000 }, + { 0x80e4, 0x0000 }, + { 0x80e5, 0x0000 }, + { 0x80e6, 0x0000 }, + { 0x80e7, 0x0000 }, + { 0x80e8, 0x0000 }, + { 0x80e9, 0x0000 }, + { 0x80ea, 0x0000 }, + { 0x80eb, 0x0000 }, + { 0x80ec, 0x0000 }, + { 0x80ed, 0x0000 }, + { 0x80ee, 0x0000 }, + { 0x80ef, 0x0000 }, + { 0x80f0, 0x0000 }, + { 0x80f1, 0x0000 }, + { 0x80f2, 0x0000 }, + { 0x80f3, 0x0000 }, + { 0x80f4, 0x0000 }, + { 0x80f5, 0x0000 }, + { 0x80f6, 0x0000 }, + { 0x80f7, 0x0000 }, + { 0x80f8, 0x0000 }, + { 0x80f9, 0x0000 }, + { 0x80fa, 0x0000 }, + { 0x80fb, 0x0000 }, + { 0x80fc, 0x0000 }, + { 0x80fd, 0x0000 }, + { 0x80fe, 0x0000 }, + { 0x80ff, 0x0000 }, + { 0x8100, 0x0000 }, + { 0x8101, 0x0000 }, + { 0x8102, 0x0000 }, + { 0x8103, 0x0000 }, + { 0x8104, 0x0000 }, + { 0x8105, 0x0000 }, + { 0x8106, 0x0000 }, + { 0x8107, 0x0000 }, + { 0x8108, 0x0000 }, + { 0x8109, 0x0000 }, + { 0x810a, 0x0000 }, + { 0x810b, 0x0000 }, + { 0x810c, 0x0000 }, + { 0x810d, 0x0000 }, + { 0x810e, 0x0000 }, + { 0x810f, 0x0000 }, + { 0x8110, 0x0000 }, + { 0x8111, 0x0000 }, + { 0x8112, 0x0000 }, + { 0x8113, 0x0000 }, + { 0x8114, 0x0000 }, + { 0x8115, 0x0000 }, + { 0x8116, 0x0000 }, + { 0x8117, 0x0000 }, + { 0x8118, 0x0000 }, + { 0x8119, 0x0000 }, + { 0x811a, 0x0000 }, + { 0x811b, 0x0000 }, + { 0x811c, 0x0000 }, + { 0x811d, 0x0000 }, + { 0x811e, 0x0000 }, + { 0x811f, 0x0000 }, + { 0x8120, 0x0000 }, + { 0x8121, 0x0000 }, + { 0x8122, 0x0000 }, + { 0x8123, 0x0000 }, + { 0x8124, 0x0000 }, + { 0x8125, 0x0000 }, + { 0x8126, 0x0000 }, + { 0x8127, 0x0000 }, + { 0x8128, 0x0000 }, + { 0x8129, 0x0000 }, + { 0x812a, 0x0000 }, + { 0x812b, 0x0000 }, + { 0x812c, 0x0000 }, + { 0x812d, 0x0000 }, + { 0x812e, 0x0000 }, + { 0x812f, 0x0000 }, + { 0x8130, 0x0000 }, + { 0x8131, 0x0000 }, + { 0x8132, 0x0000 }, + { 0x8133, 0x0000 }, + { 0x8134, 0x0000 }, + { 0x8135, 0x0000 }, + { 0x8136, 0x0000 }, + { 0x8137, 0x0000 }, + { 0x8138, 0x0000 }, + { 0x8139, 0x0000 }, + { 0x813a, 0x0000 }, + { 0x813b, 0x0000 }, + { 0x813c, 0x0000 }, + { 0x813d, 0x0000 }, + { 0x813e, 0x0000 }, + { 0x813f, 0x0000 }, + { 0x8140, 0x0000 }, + { 0x8141, 0x0000 }, + { 0x8142, 0x0000 }, + { 0x8143, 0x0000 }, + { 0x8144, 0x0000 }, + { 0x8145, 0x0000 }, + { 0x8146, 0x0000 }, + { 0x8147, 0x0000 }, + { 0x8148, 0x0000 }, + { 0x8149, 0x0000 }, + { 0x814a, 0x0000 }, + { 0x814b, 0x0000 }, + { 0x814c, 0x0000 }, + { 0x814d, 0x0000 }, + { 0x814e, 0x0000 }, + { 0x814f, 0x0000 }, + { 0x8150, 0x0000 }, + { 0x8151, 0x0000 }, + { 0x8152, 0x0000 }, + { 0x8153, 0x0000 }, + { 0x8154, 0x0000 }, + { 0x8155, 0x0000 }, + { 0x8156, 0x0000 }, + { 0x8157, 0x0000 }, + { 0x8158, 0x0000 }, + { 0x8159, 0x0000 }, + { 0x815a, 0x0000 }, + { 0x815b, 0x0000 }, + { 0x815c, 0x0000 }, + { 0x815d, 0x0000 }, + { 0x815e, 0x0000 }, + { 0x815f, 0x0000 }, + { 0x8160, 0x0000 }, + { 0x8161, 0x0000 }, + { 0x8162, 0x0000 }, + { 0x8163, 0x0000 }, + { 0x8164, 0x0000 }, + { 0x8165, 0x0000 }, + { 0x8166, 0x0000 }, + { 0x8167, 0x0000 }, + { 0x8168, 0x0000 }, + { 0x8169, 0x0000 }, + { 0x816a, 0x0000 }, + { 0x816b, 0x0000 }, + { 0x816c, 0x0000 }, + { 0x816d, 0x0000 }, + { 0x816e, 0x0000 }, + { 0x816f, 0x0000 }, + { 0x8170, 0x0000 }, + { 0x8171, 0x0000 }, + { 0x8172, 0x0000 }, + { 0x8173, 0x0000 }, + { 0x8174, 0x0000 }, + { 0x8175, 0x0000 }, + { 0x8176, 0x0000 }, + { 0x8177, 0x0000 }, + { 0x8178, 0x0000 }, + { 0x8179, 0x0000 }, + { 0x817a, 0x0000 }, + { 0x817b, 0x0000 }, + { 0x817c, 0x0000 }, + { 0x817d, 0x0000 }, + { 0x817e, 0x0000 }, + { 0x817f, 0x0000 }, + { 0x8180, 0x0000 }, + { 0x8181, 0x0000 }, + { 0x8182, 0x0000 }, + { 0x8183, 0x0000 }, + { 0x8184, 0x0000 }, + { 0x8185, 0x0000 }, + { 0x8186, 0x0000 }, + { 0x8187, 0x0000 }, + { 0x8188, 0x0000 }, + { 0x8189, 0x0000 }, + { 0x818a, 0x0000 }, + { 0x818b, 0x0000 }, + { 0x818c, 0x0000 }, + { 0x818d, 0x0000 }, + { 0x818e, 0x0000 }, + { 0x818f, 0x0000 }, + { 0x8190, 0x0000 }, + { 0x8191, 0x0000 }, + { 0x8192, 0x0000 }, + { 0x8193, 0x0000 }, + { 0x8194, 0x0000 }, + { 0x8195, 0x0000 }, + { 0x8196, 0x0000 }, + { 0x8197, 0x0000 }, + { 0x8198, 0x0000 }, + { 0x8199, 0x0000 }, + { 0x819a, 0x0000 }, + { 0x819b, 0x0000 }, + { 0x819c, 0x0000 }, + { 0x819d, 0x0000 }, + { 0x819e, 0x0000 }, + { 0x819f, 0x0000 }, + { 0x81a0, 0x0000 }, + { 0x81a1, 0x0000 }, + { 0x81a2, 0x0000 }, + { 0x81a3, 0x0000 }, + { 0x81a4, 0x0000 }, + { 0x81a5, 0x0000 }, + { 0x81a6, 0x0000 }, + { 0x81a7, 0x0000 }, + { 0x81a8, 0x0000 }, + { 0x81a9, 0x0000 }, + { 0x81aa, 0x0000 }, + { 0x81ab, 0x0000 }, + { 0x81ac, 0x0000 }, + { 0x81ad, 0x0000 }, + { 0x81ae, 0x0000 }, + { 0x81af, 0x0000 }, + { 0x81b0, 0x0000 }, + { 0x81b1, 0x0000 }, + { 0x81b2, 0x0000 }, + { 0x81b3, 0x0000 }, + { 0x81b4, 0x0000 }, + { 0x81b5, 0x0000 }, + { 0x81b6, 0x0000 }, + { 0x81b7, 0x0000 }, + { 0x81b8, 0x0000 }, + { 0x81b9, 0x0000 }, + { 0x81ba, 0x0000 }, + { 0x81bb, 0x0000 }, + { 0x81bc, 0x0000 }, + { 0x81bd, 0x0000 }, + { 0x81be, 0x0000 }, + { 0x81bf, 0x0000 }, + { 0x81c0, 0x0000 }, + { 0x81c1, 0x0000 }, + { 0x81c2, 0x0000 }, + { 0x81c3, 0x0000 }, + { 0x81c4, 0x0000 }, + { 0x81c5, 0x0000 }, + { 0x81c6, 0x0000 }, + { 0x81c7, 0x0000 }, + { 0x81c8, 0x0000 }, + { 0x81c9, 0x0000 }, + { 0x81ca, 0x0000 }, + { 0x81cb, 0x0000 }, + { 0x81cc, 0x0000 }, + { 0x81cd, 0x0000 }, + { 0x81ce, 0x0000 }, + { 0x81cf, 0x0000 }, + { 0x81d0, 0x0000 }, + { 0x81d1, 0x0000 }, + { 0x81d2, 0x0000 }, + { 0x81d3, 0x0000 }, + { 0x81d4, 0x0000 }, + { 0x81d5, 0x0000 }, + { 0x81d6, 0x0000 }, + { 0x81d7, 0x0000 }, + { 0x81d8, 0x0000 }, + { 0x81d9, 0x0000 }, + { 0x81da, 0x0000 }, + { 0x81db, 0x0000 }, + { 0x81dc, 0x0000 }, + { 0x81dd, 0x0000 }, + { 0x81de, 0x0000 }, + { 0x81df, 0x0000 }, + { 0x81e0, 0x0000 }, + { 0x81e1, 0x0000 }, + { 0x81e2, 0x0000 }, + { 0x81e3, 0x0000 }, + { 0x81e4, 0x0000 }, + { 0x81e5, 0x0000 }, + { 0x81e6, 0x0000 }, + { 0x81e7, 0x0000 }, + { 0x81e8, 0x0000 }, + { 0x81e9, 0x0000 }, + { 0x81ea, 0x0000 }, + { 0x81eb, 0x0000 }, + { 0x81ec, 0x0000 }, + { 0x81ed, 0x0000 }, + { 0x81ee, 0x0000 }, + { 0x81ef, 0x0000 }, + { 0x81f0, 0x0000 }, + { 0x81f1, 0x0000 }, + { 0x81f2, 0x0000 }, + { 0x81f3, 0x0000 }, + { 0x81f4, 0x0000 }, + { 0x81f5, 0x0000 }, + { 0x81f6, 0x0000 }, + { 0x81f7, 0x0000 }, + { 0x81f8, 0x0000 }, + { 0x81f9, 0x0000 }, + { 0x81fa, 0x0000 }, + { 0x81fb, 0x0000 }, + { 0x81fc, 0x0000 }, + { 0x81fd, 0x0000 }, + { 0x81fe, 0x0000 }, + { 0x81ff, 0x0000 }, + { 0x8200, 0x0000 }, + { 0x8201, 0x0000 }, + { 0x8202, 0x0000 }, + { 0x8203, 0x0000 }, + { 0x8204, 0x0000 }, + { 0x8205, 0x0000 }, + { 0x8206, 0x0000 }, + { 0x8207, 0x0000 }, + { 0x8208, 0x0000 }, + { 0x8209, 0x0000 }, + { 0x820a, 0x0000 }, + { 0x820b, 0x0000 }, + { 0x820c, 0x0000 }, + { 0x820d, 0x0000 }, + { 0x820e, 0x0000 }, + { 0x820f, 0x0000 }, + { 0x8210, 0x0000 }, + { 0x8211, 0x0000 }, + { 0x8212, 0x0000 }, + { 0x8213, 0x0000 }, + { 0x8214, 0x0000 }, + { 0x8215, 0x0000 }, + { 0x8216, 0x0000 }, + { 0x8217, 0x0000 }, + { 0x8218, 0x0000 }, + { 0x8219, 0x0000 }, + { 0x821a, 0x0000 }, + { 0x821b, 0x0000 }, + { 0x821c, 0x0000 }, + { 0x821d, 0x0000 }, + { 0x821e, 0x0000 }, + { 0x821f, 0x0000 }, + { 0x8220, 0x0000 }, + { 0x8221, 0x0000 }, + { 0x8222, 0x0000 }, + { 0x8223, 0x0000 }, + { 0x8224, 0x0000 }, + { 0x8225, 0x0000 }, + { 0x8226, 0x0000 }, + { 0x8227, 0x0000 }, + { 0x8228, 0x0000 }, + { 0x8229, 0x0000 }, + { 0x822a, 0x0000 }, + { 0x822b, 0x0000 }, + { 0x822c, 0x0000 }, + { 0x822d, 0x0000 }, + { 0x822e, 0x0000 }, + { 0x822f, 0x0000 }, + { 0x8230, 0x0000 }, + { 0x8231, 0x0000 }, + { 0x8232, 0x0000 }, + { 0x8233, 0x0000 }, + { 0x8234, 0x0000 }, + { 0x8235, 0x0000 }, + { 0x8236, 0x0000 }, + { 0x8237, 0x0000 }, + { 0x8238, 0x0000 }, + { 0x8239, 0x0000 }, + { 0x823a, 0x0000 }, + { 0x823b, 0x0000 }, + { 0x823c, 0x0000 }, + { 0x823d, 0x0000 }, + { 0x823e, 0x0000 }, + { 0x823f, 0x0000 }, + { 0x8240, 0x0000 }, + { 0x8241, 0x0000 }, + { 0x8242, 0x0000 }, + { 0x8243, 0x0000 }, + { 0x8244, 0x0000 }, + { 0x8245, 0x0000 }, + { 0x8246, 0x0000 }, + { 0x8247, 0x0000 }, + { 0x8248, 0x0000 }, + { 0x8249, 0x0000 }, + { 0x824a, 0x0000 }, + { 0x824b, 0x0000 }, + { 0x824c, 0x0000 }, + { 0x824d, 0x0000 }, + { 0x824e, 0x0000 }, + { 0x824f, 0x0000 }, + { 0x8250, 0x0000 }, + { 0x8251, 0x0000 }, + { 0x8252, 0x0000 }, + { 0x8253, 0x0000 }, + { 0x8254, 0x0000 }, + { 0x8255, 0x0000 }, + { 0x8256, 0x0000 }, + { 0x8257, 0x0000 }, + { 0x8258, 0x0000 }, + { 0x8259, 0x0000 }, + { 0x825a, 0x0000 }, + { 0x825b, 0x0000 }, + { 0x825c, 0x0000 }, + { 0x825d, 0x0000 }, + { 0x825e, 0x0000 }, + { 0x825f, 0x0000 }, + { 0x8260, 0x0000 }, + { 0x8261, 0x0000 }, + { 0x8262, 0x0000 }, + { 0x8263, 0x0000 }, + { 0x8264, 0x0000 }, + { 0x8265, 0x0000 }, + { 0x8266, 0x0000 }, + { 0x8267, 0x0000 }, + { 0x8268, 0x0000 }, + { 0x8269, 0x0000 }, + { 0x826a, 0x0000 }, + { 0x826b, 0x0000 }, + { 0x826c, 0x0000 }, + { 0x826d, 0x0000 }, + { 0x826e, 0x0000 }, + { 0x826f, 0x0000 }, + { 0x8270, 0x0000 }, + { 0x8271, 0x0000 }, + { 0x8272, 0x0000 }, + { 0x8273, 0x0000 }, + { 0x8274, 0x0000 }, + { 0x8275, 0x0000 }, + { 0x8276, 0x0000 }, + { 0x8277, 0x0000 }, + { 0x8278, 0x0000 }, + { 0x8279, 0x0000 }, + { 0x827a, 0x0000 }, + { 0x827b, 0x0000 }, + { 0x827c, 0x0000 }, + { 0x827d, 0x0000 }, + { 0x827e, 0x0000 }, + { 0x827f, 0x0000 }, + { 0x8280, 0x0000 }, + { 0x8281, 0x0000 }, + { 0x8282, 0x0000 }, + { 0x8283, 0x0000 }, + { 0x8284, 0x0000 }, + { 0x8285, 0x0000 }, + { 0x8286, 0x0000 }, + { 0x8287, 0x0000 }, + { 0x8288, 0x0000 }, + { 0x8289, 0x0000 }, + { 0x828a, 0x0000 }, + { 0x828b, 0x0000 }, + { 0x828c, 0x0000 }, + { 0x828d, 0x0000 }, + { 0x828e, 0x0000 }, + { 0x828f, 0x0000 }, + { 0x8290, 0x0000 }, + { 0x8291, 0x0000 }, + { 0x8292, 0x0000 }, + { 0x8293, 0x0000 }, + { 0x8294, 0x0000 }, + { 0x8295, 0x0000 }, + { 0x8296, 0x0000 }, + { 0x8297, 0x0000 }, + { 0x8298, 0x0000 }, + { 0x8299, 0x0000 }, + { 0x829a, 0x0000 }, + { 0x829b, 0x0000 }, + { 0x829c, 0x0000 }, + { 0x829d, 0x0000 }, + { 0x829e, 0x0000 }, + { 0x829f, 0x0000 }, + { 0x82a0, 0x0000 }, + { 0x82a1, 0x0000 }, + { 0x82a2, 0x0000 }, + { 0x82a3, 0x0000 }, + { 0x82a4, 0x0000 }, + { 0x82a5, 0x0000 }, + { 0x82a6, 0x0000 }, + { 0x82a7, 0x0000 }, + { 0x82a8, 0x0000 }, + { 0x82a9, 0x0000 }, + { 0x82aa, 0x0000 }, + { 0x82ab, 0x0000 }, + { 0x82ac, 0x0000 }, + { 0x82ad, 0x0000 }, + { 0x82ae, 0x0000 }, + { 0x82af, 0x0000 }, + { 0x82b0, 0x0000 }, + { 0x82b1, 0x0000 }, + { 0x82b2, 0x0000 }, + { 0x82b3, 0x0000 }, + { 0x82b4, 0x0000 }, + { 0x82b5, 0x0000 }, + { 0x82b6, 0x0000 }, + { 0x82b7, 0x0000 }, + { 0x82b8, 0x0000 }, + { 0x82b9, 0x0000 }, + { 0x82ba, 0x0000 }, + { 0x82bb, 0x0000 }, + { 0x82bc, 0x0000 }, + { 0x82bd, 0x0000 }, + { 0x82be, 0x0000 }, + { 0x82bf, 0x0000 }, + { 0x82c0, 0x0000 }, + { 0x82c1, 0x0000 }, + { 0x82c2, 0x0000 }, + { 0x82c3, 0x0000 }, + { 0x82c4, 0x0000 }, + { 0x82c5, 0x0000 }, + { 0x82c6, 0x0000 }, + { 0x82c7, 0x0000 }, + { 0x82c8, 0x0000 }, + { 0x82c9, 0x0000 }, + { 0x82ca, 0x0000 }, + { 0x82cb, 0x0000 }, + { 0x82cc, 0x0000 }, + { 0x82cd, 0x0000 }, + { 0x82ce, 0x0000 }, + { 0x82cf, 0x0000 }, + { 0x82d0, 0x0000 }, + { 0x82d1, 0x0000 }, + { 0x82d2, 0x0000 }, + { 0x82d3, 0x0000 }, + { 0x82d4, 0x0000 }, + { 0x82d5, 0x0000 }, + { 0x82d6, 0x0000 }, + { 0x82d7, 0x0000 }, + { 0x82d8, 0x0000 }, + { 0x82d9, 0x0000 }, + { 0x82da, 0x0000 }, + { 0x82db, 0x0000 }, + { 0x82dc, 0x0000 }, + { 0x82dd, 0x0000 }, + { 0x82de, 0x0000 }, + { 0x82df, 0x0000 }, + { 0x82e0, 0x0000 }, + { 0x82e1, 0x0000 }, + { 0x82e2, 0x0000 }, + { 0x82e3, 0x0000 }, + { 0x82e4, 0x0000 }, + { 0x82e5, 0x0000 }, + { 0x82e6, 0x0000 }, + { 0x82e7, 0x0000 }, + { 0x82e8, 0x0000 }, + { 0x82e9, 0x0000 }, + { 0x82ea, 0x0000 }, + { 0x82eb, 0x0000 }, + { 0x82ec, 0x0000 }, + { 0x82ed, 0x0000 }, + { 0x82ee, 0x0000 }, + { 0x82ef, 0x0000 }, + { 0x82f0, 0x0000 }, + { 0x82f1, 0x0000 }, + { 0x82f2, 0x0000 }, + { 0x82f3, 0x0000 }, + { 0x82f4, 0x0000 }, + { 0x82f5, 0x0000 }, + { 0x82f6, 0x0000 }, + { 0x82f7, 0x0000 }, + { 0x82f8, 0x0000 }, + { 0x82f9, 0x0000 }, + { 0x82fa, 0x0000 }, + { 0x82fb, 0x0000 }, + { 0x82fc, 0x0000 }, + { 0x82fd, 0x0000 }, + { 0x82fe, 0x0000 }, + { 0x82ff, 0x0000 }, + { 0x8300, 0x0000 }, + { 0x8301, 0x0000 }, + { 0x8302, 0x0000 }, + { 0x8303, 0x0000 }, + { 0x8304, 0x0000 }, + { 0x8305, 0x0000 }, + { 0x8306, 0x0000 }, + { 0x8307, 0x0000 }, + { 0x8308, 0x0000 }, + { 0x8309, 0x0000 }, + { 0x830a, 0x0000 }, + { 0x830b, 0x0000 }, + { 0x830c, 0x0000 }, + { 0x830d, 0x0000 }, + { 0x830e, 0x0000 }, + { 0x830f, 0x0000 }, + { 0x8310, 0x0000 }, + { 0x8311, 0x0000 }, + { 0x8312, 0x0000 }, + { 0x8313, 0x0000 }, + { 0x8314, 0x0000 }, + { 0x8315, 0x0000 }, + { 0x8316, 0x0000 }, + { 0x8317, 0x0000 }, + { 0x8318, 0x0000 }, + { 0x8319, 0x0000 }, + { 0x831a, 0x0000 }, + { 0x831b, 0x0000 }, + { 0x831c, 0x0000 }, + { 0x831d, 0x0000 }, + { 0x831e, 0x0000 }, + { 0x831f, 0x0000 }, + { 0x8320, 0x0000 }, + { 0x8321, 0x0000 }, + { 0x8322, 0x0000 }, + { 0x8323, 0x0000 }, + { 0x8324, 0x0000 }, + { 0x8325, 0x0000 }, + { 0x8326, 0x0000 }, + { 0x8327, 0x0000 }, + { 0x8328, 0x0000 }, + { 0x8329, 0x0000 }, + { 0x832a, 0x0000 }, + { 0x832b, 0x0000 }, + { 0x832c, 0x0000 }, + { 0x832d, 0x0000 }, + { 0x832e, 0x0000 }, + { 0x832f, 0x0000 }, + { 0x8330, 0x0000 }, + { 0x8331, 0x0000 }, + { 0x8332, 0x0000 }, + { 0x8333, 0x0000 }, + { 0x8334, 0x0000 }, + { 0x8335, 0x0000 }, + { 0x8336, 0x0000 }, + { 0x8337, 0x0000 }, + { 0x8338, 0x0000 }, + { 0x8339, 0x0000 }, + { 0x833a, 0x0000 }, + { 0x833b, 0x0000 }, + { 0x833c, 0x0000 }, + { 0x833d, 0x0000 }, + { 0x833e, 0x0000 }, + { 0x833f, 0x0000 }, + { 0x8340, 0x0000 }, + { 0x8341, 0x0000 }, + { 0x8342, 0x0000 }, + { 0x8343, 0x0000 }, + { 0x8344, 0x0000 }, + { 0x8345, 0x0000 }, + { 0x8346, 0x0000 }, + { 0x8347, 0x0000 }, + { 0x8348, 0x0000 }, + { 0x8349, 0x0000 }, + { 0x834a, 0x0000 }, + { 0x834b, 0x0000 }, + { 0x834c, 0x0000 }, + { 0x834d, 0x0000 }, + { 0x834e, 0x0000 }, + { 0x834f, 0x0000 }, + { 0x8350, 0x0000 }, + { 0x8351, 0x0000 }, + { 0x8352, 0x0000 }, + { 0x8353, 0x0000 }, + { 0x8354, 0x0000 }, + { 0x8355, 0x0000 }, + { 0x8356, 0x0000 }, + { 0x8357, 0x0000 }, + { 0x8358, 0x0000 }, + { 0x8359, 0x0000 }, + { 0x835a, 0x0000 }, + { 0x835b, 0x0000 }, + { 0x835c, 0x0000 }, + { 0x835d, 0x0000 }, + { 0x835e, 0x0000 }, + { 0x835f, 0x0000 }, + { 0x8360, 0x0000 }, + { 0x8361, 0x0000 }, + { 0x8362, 0x0000 }, + { 0x8363, 0x0000 }, + { 0x8364, 0x0000 }, + { 0x8365, 0x0000 }, + { 0x8366, 0x0000 }, + { 0x8367, 0x0000 }, + { 0x8368, 0x0000 }, + { 0x8369, 0x0000 }, + { 0x836a, 0x0000 }, + { 0x836b, 0x0000 }, + { 0x836c, 0x0000 }, + { 0x836d, 0x0000 }, + { 0x836e, 0x0000 }, + { 0x836f, 0x0000 }, + { 0x8370, 0x0000 }, + { 0x8371, 0x0000 }, + { 0x8372, 0x0000 }, + { 0x8373, 0x0000 }, + { 0x8374, 0x0000 }, + { 0x8375, 0x0000 }, + { 0x8376, 0x0000 }, + { 0x8377, 0x0000 }, + { 0x8378, 0x0000 }, + { 0x8379, 0x0000 }, + { 0x837a, 0x0000 }, + { 0x837b, 0x0000 }, + { 0x837c, 0x0000 }, + { 0x837d, 0x0000 }, + { 0x837e, 0x0000 }, + { 0x837f, 0x0000 }, + { 0x8380, 0x0000 }, + { 0x8381, 0x0000 }, + { 0x8382, 0x0000 }, + { 0x8383, 0x0000 }, + { 0x8384, 0x0000 }, + { 0x8385, 0x0000 }, + { 0x8386, 0x0000 }, + { 0x8387, 0x0000 }, + { 0x8388, 0x0000 }, + { 0x8389, 0x0000 }, + { 0x838a, 0x0000 }, + { 0x838b, 0x0000 }, + { 0x838c, 0x0000 }, + { 0x838d, 0x0000 }, + { 0x838e, 0x0000 }, + { 0x838f, 0x0000 }, + { 0x8390, 0x0000 }, + { 0x8391, 0x0000 }, + { 0x8392, 0x0000 }, + { 0x8393, 0x0000 }, + { 0x8394, 0x0000 }, + { 0x8395, 0x0000 }, + { 0x8396, 0x0000 }, + { 0x8397, 0x0000 }, + { 0x8398, 0x0000 }, + { 0x8399, 0x0000 }, + { 0x839a, 0x0000 }, + { 0x839b, 0x0000 }, + { 0x839c, 0x0000 }, + { 0x839d, 0x0000 }, + { 0x839e, 0x0000 }, + { 0x839f, 0x0000 }, + { 0x83a0, 0x0000 }, + { 0x83a1, 0x0000 }, + { 0x83a2, 0x0000 }, + { 0x83a3, 0x0000 }, + { 0x83a4, 0x0000 }, + { 0x83a5, 0x0000 }, + { 0x83a6, 0x0000 }, + { 0x83a7, 0x0000 }, + { 0x83a8, 0x0000 }, + { 0x83a9, 0x0000 }, + { 0x83aa, 0x0000 }, + { 0x83ab, 0x0000 }, + { 0x83ac, 0x0000 }, + { 0x83ad, 0x0000 }, + { 0x83ae, 0x0000 }, + { 0x83af, 0x0000 }, + { 0x83b0, 0x0000 }, + { 0x83b1, 0x0000 }, + { 0x83b2, 0x0000 }, + { 0x83b3, 0x0000 }, + { 0x83b4, 0x0000 }, + { 0x83b5, 0x0000 }, + { 0x83b6, 0x0000 }, + { 0x83b7, 0x0000 }, + { 0x83b8, 0x0000 }, + { 0x83b9, 0x0000 }, + { 0x83ba, 0x0000 }, + { 0x83bb, 0x0000 }, + { 0x83bc, 0x0000 }, + { 0x83bd, 0x0000 }, + { 0x83be, 0x0000 }, + { 0x83bf, 0x0000 }, + { 0x83c0, 0x0000 }, + { 0x83c1, 0x0000 }, + { 0x83c2, 0x0000 }, + { 0x83c3, 0x0000 }, + { 0x83c4, 0x0000 }, + { 0x83c5, 0x0000 }, + { 0x83c6, 0x0000 }, + { 0x83c7, 0x0000 }, + { 0x83c8, 0x0000 }, + { 0x83c9, 0x0000 }, + { 0x83ca, 0x0000 }, + { 0x83cb, 0x0000 }, + { 0x83cc, 0x0000 }, + { 0x83cd, 0x0000 }, + { 0x83ce, 0x0000 }, + { 0x83cf, 0x0000 }, + { 0x83d0, 0x0000 }, + { 0x83d1, 0x0000 }, + { 0x83d2, 0x0000 }, + { 0x83d3, 0x0000 }, + { 0x83d4, 0x0000 }, + { 0x83d5, 0x0000 }, + { 0x83d6, 0x0000 }, + { 0x83d7, 0x0000 }, + { 0x83d8, 0x0000 }, + { 0x83d9, 0x0000 }, + { 0x83da, 0x0000 }, + { 0x83db, 0x0000 }, + { 0x83dc, 0x0000 }, + { 0x83dd, 0x0000 }, + { 0x83de, 0x0000 }, + { 0x83df, 0x0000 }, + { 0x83e0, 0x0000 }, + { 0x83e1, 0x0000 }, + { 0x83e2, 0x0000 }, + { 0x83e3, 0x0000 }, + { 0x83e4, 0x0000 }, + { 0x83e5, 0x0000 }, + { 0x83e6, 0x0000 }, + { 0x83e7, 0x0000 }, + { 0x83e8, 0x0000 }, + { 0x83e9, 0x0000 }, + { 0x83ea, 0x0000 }, + { 0x83eb, 0x0000 }, + { 0x83ec, 0x0000 }, + { 0x83ed, 0x0000 }, + { 0x83ee, 0x0000 }, + { 0x83ef, 0x0000 }, + { 0x83f0, 0x0000 }, + { 0x83f1, 0x0000 }, + { 0x83f2, 0x0000 }, + { 0x83f3, 0x0000 }, + { 0x83f4, 0x0000 }, + { 0x83f5, 0x0000 }, + { 0x83f6, 0x0000 }, + { 0x83f7, 0x0000 }, + { 0x83f8, 0x0000 }, + { 0x83f9, 0x0000 }, + { 0x83fa, 0x0000 }, + { 0x83fb, 0x0000 }, + { 0x83fc, 0x0000 }, + { 0x83fd, 0x0000 }, + { 0x83fe, 0x0000 }, + { 0x83ff, 0x0000 }, + { 0x8400, 0x0000 }, + { 0x8401, 0x0000 }, + { 0x8402, 0x0000 }, + { 0x8403, 0x0000 }, + { 0x8404, 0x0000 }, + { 0x8405, 0x0000 }, + { 0x8406, 0x0000 }, + { 0x8407, 0x0000 }, + { 0x8408, 0x0000 }, + { 0x8409, 0x0000 }, + { 0x840a, 0x0000 }, + { 0x840b, 0x0000 }, + { 0x840c, 0x0000 }, + { 0x840d, 0x0000 }, + { 0x840e, 0x0000 }, + { 0x840f, 0x0000 }, + { 0x8410, 0x0000 }, + { 0x8411, 0x0000 }, + { 0x8412, 0x0000 }, + { 0x8413, 0x0000 }, + { 0x8414, 0x0000 }, + { 0x8415, 0x0000 }, + { 0x8416, 0x0000 }, + { 0x8417, 0x0000 }, + { 0x8418, 0x0000 }, + { 0x8419, 0x0000 }, + { 0x841a, 0x0000 }, + { 0x841b, 0x0000 }, + { 0x841c, 0x0000 }, + { 0x841d, 0x0000 }, + { 0x841e, 0x0000 }, + { 0x841f, 0x0000 }, + { 0x8420, 0x0000 }, + { 0x8421, 0x0000 }, + { 0x8422, 0x0000 }, + { 0x8423, 0x0000 }, + { 0x8424, 0x0000 }, + { 0x8425, 0x0000 }, + { 0x8426, 0x0000 }, + { 0x8427, 0x0000 }, + { 0x8428, 0x0000 }, + { 0x8429, 0x0000 }, + { 0x842a, 0x0000 }, + { 0x842b, 0x0000 }, + { 0x842c, 0x0000 }, + { 0x842d, 0x0000 }, + { 0x842e, 0x0000 }, + { 0x842f, 0x0000 }, + { 0x8430, 0x0000 }, + { 0x8431, 0x0000 }, + { 0x8432, 0x0000 }, + { 0x8433, 0x0000 }, + { 0x8434, 0x0000 }, + { 0x8435, 0x0000 }, + { 0x8436, 0x0000 }, + { 0x8437, 0x0000 }, + { 0x8438, 0x0000 }, + { 0x8439, 0x0000 }, + { 0x843a, 0x0000 }, + { 0x843b, 0x0000 }, + { 0x843c, 0x0000 }, + { 0x843d, 0x0000 }, + { 0x843e, 0x0000 }, + { 0x843f, 0x0000 }, + { 0x8440, 0x0000 }, + { 0x8441, 0x0000 }, + { 0x8442, 0x0000 }, + { 0x8443, 0x0000 }, + { 0x8444, 0x0000 }, + { 0x8445, 0x0000 }, + { 0x8446, 0x0000 }, + { 0x8447, 0x0000 }, + { 0x8448, 0x0000 }, + { 0x8449, 0x0000 }, + { 0x844a, 0x0000 }, + { 0x844b, 0x0000 }, + { 0x844c, 0x0000 }, + { 0x844d, 0x0000 }, + { 0x844e, 0x0000 }, + { 0x844f, 0x0000 }, + { 0x8450, 0x0000 }, + { 0x8451, 0x0000 }, + { 0x8452, 0x0000 }, + { 0x8453, 0x0000 }, + { 0x8454, 0x0000 }, + { 0x8455, 0x0000 }, + { 0x8456, 0x0000 }, + { 0x8457, 0x0000 }, + { 0x8458, 0x0000 }, + { 0x8459, 0x0000 }, + { 0x845a, 0x0000 }, + { 0x845b, 0x0000 }, + { 0x845c, 0x0000 }, + { 0x845d, 0x0000 }, + { 0x845e, 0x0000 }, + { 0x845f, 0x0000 }, + { 0x8460, 0x0000 }, + { 0x8461, 0x0000 }, + { 0x8462, 0x0000 }, + { 0x8463, 0x0000 }, + { 0x8464, 0x0000 }, + { 0x8465, 0x0000 }, + { 0x8466, 0x0000 }, + { 0x8467, 0x0000 }, + { 0x8468, 0x0000 }, + { 0x8469, 0x0000 }, + { 0x846a, 0x0000 }, + { 0x846b, 0x0000 }, + { 0x846c, 0x0000 }, + { 0x846d, 0x0000 }, + { 0x846e, 0x0000 }, + { 0x846f, 0x0000 }, + { 0x8470, 0x0000 }, + { 0x8471, 0x0000 }, + { 0x8472, 0x0000 }, + { 0x8473, 0x0000 }, + { 0x8474, 0x0000 }, + { 0x8475, 0x0000 }, + { 0x8476, 0x0000 }, + { 0x8477, 0x0000 }, + { 0x8478, 0x0000 }, + { 0x8479, 0x0000 }, + { 0x847a, 0x0000 }, + { 0x847b, 0x0000 }, + { 0x847c, 0x0000 }, + { 0x847d, 0x0000 }, + { 0x847e, 0x0000 }, + { 0x847f, 0x0000 }, + { 0x8480, 0x0000 }, + { 0x8481, 0x0000 }, + { 0x8482, 0x0000 }, + { 0x8483, 0x0000 }, + { 0x8484, 0x0000 }, + { 0x8485, 0x0000 }, + { 0x8486, 0x0000 }, + { 0x8487, 0x0000 }, + { 0x8488, 0x0000 }, + { 0x8489, 0x0000 }, + { 0x848a, 0x0000 }, + { 0x848b, 0x0000 }, + { 0x848c, 0x0000 }, + { 0x848d, 0x0000 }, + { 0x848e, 0x0000 }, + { 0x848f, 0x0000 }, + { 0x8490, 0x0000 }, + { 0x8491, 0x0000 }, + { 0x8492, 0x0000 }, + { 0x8493, 0x0000 }, + { 0x8494, 0x0000 }, + { 0x8495, 0x0000 }, + { 0x8496, 0x0000 }, + { 0x8497, 0x0000 }, + { 0x8498, 0x0000 }, + { 0x8499, 0x0000 }, + { 0x849a, 0x0000 }, + { 0x849b, 0x0000 }, + { 0x849c, 0x0000 }, + { 0x849d, 0x0000 }, + { 0x849e, 0x0000 }, + { 0x849f, 0x0000 }, + { 0x84a0, 0x0000 }, + { 0x84a1, 0x0000 }, + { 0x84a2, 0x0000 }, + { 0x84a3, 0x0000 }, + { 0x84a4, 0x0000 }, + { 0x84a5, 0x0000 }, + { 0x84a6, 0x0000 }, + { 0x84a7, 0x0000 }, + { 0x84a8, 0x0000 }, + { 0x84a9, 0x0000 }, + { 0x84aa, 0x0000 }, + { 0x84ab, 0x0000 }, + { 0x84ac, 0x0000 }, + { 0x84ad, 0x0000 }, + { 0x84ae, 0x0000 }, + { 0x84af, 0x0000 }, + { 0x84b0, 0x0000 }, + { 0x84b1, 0x0000 }, + { 0x84b2, 0x0000 }, + { 0x84b3, 0x0000 }, + { 0x84b4, 0x0000 }, + { 0x84b5, 0x0000 }, + { 0x84b6, 0x0000 }, + { 0x84b7, 0x0000 }, + { 0x84b8, 0x0000 }, + { 0x84b9, 0x0000 }, + { 0x84ba, 0x0000 }, + { 0x84bb, 0x0000 }, + { 0x84bc, 0x0000 }, + { 0x84bd, 0x0000 }, + { 0x84be, 0x0000 }, + { 0x84bf, 0x0000 }, + { 0x84c0, 0x0000 }, + { 0x84c1, 0x0000 }, + { 0x84c2, 0x0000 }, + { 0x84c3, 0x0000 }, + { 0x84c4, 0x0000 }, + { 0x84c5, 0x0000 }, + { 0x84c6, 0x0000 }, + { 0x84c7, 0x0000 }, + { 0x84c8, 0x0000 }, + { 0x84c9, 0x0000 }, + { 0x84ca, 0x0000 }, + { 0x84cb, 0x0000 }, + { 0x84cc, 0x0000 }, + { 0x84cd, 0x0000 }, + { 0x84ce, 0x0000 }, + { 0x84cf, 0x0000 }, + { 0x84d0, 0x0000 }, + { 0x84d1, 0x0000 }, + { 0x84d2, 0x0000 }, + { 0x84d3, 0x0000 }, + { 0x84d4, 0x0000 }, + { 0x84d5, 0x0000 }, + { 0x84d6, 0x0000 }, + { 0x84d7, 0x0000 }, + { 0x84d8, 0x0000 }, + { 0x84d9, 0x0000 }, + { 0x84da, 0x0000 }, + { 0x84db, 0x0000 }, + { 0x84dc, 0x0000 }, + { 0x84dd, 0x0000 }, + { 0x84de, 0x0000 }, + { 0x84df, 0x0000 }, + { 0x84e0, 0x0000 }, + { 0x84e1, 0x0000 }, + { 0x84e2, 0x0000 }, + { 0x84e3, 0x0000 }, + { 0x84e4, 0x0000 }, + { 0x84e5, 0x0000 }, + { 0x84e6, 0x0000 }, + { 0x84e7, 0x0000 }, + { 0x84e8, 0x0000 }, + { 0x84e9, 0x0000 }, + { 0x84ea, 0x0000 }, + { 0x84eb, 0x0000 }, + { 0x84ec, 0x0000 }, + { 0x84ed, 0x0000 }, + { 0x84ee, 0x0000 }, + { 0x84ef, 0x0000 }, + { 0x84f0, 0x0000 }, + { 0x84f1, 0x0000 }, + { 0x84f2, 0x0000 }, + { 0x84f3, 0x0000 }, + { 0x84f4, 0x0000 }, + { 0x84f5, 0x0000 }, + { 0x84f6, 0x0000 }, + { 0x84f7, 0x0000 }, + { 0x84f8, 0x0000 }, + { 0x84f9, 0x0000 }, + { 0x84fa, 0x0000 }, + { 0x84fb, 0x0000 }, + { 0x84fc, 0x0000 }, + { 0x84fd, 0x0000 }, + { 0x84fe, 0x0000 }, + { 0x84ff, 0x0000 }, + { 0x8500, 0x0000 }, + { 0x8501, 0x0000 }, + { 0x8502, 0x0000 }, + { 0x8503, 0x0000 }, + { 0x8504, 0x0000 }, + { 0x8505, 0x0000 }, + { 0x8506, 0x0000 }, + { 0x8507, 0x0000 }, + { 0x8508, 0x0000 }, + { 0x8509, 0x0000 }, + { 0x850a, 0x0000 }, + { 0x850b, 0x0000 }, + { 0x850c, 0x0000 }, + { 0x850d, 0x0000 }, + { 0x850e, 0x0000 }, + { 0x850f, 0x0000 }, + { 0x8510, 0x0000 }, + { 0x8511, 0x0000 }, + { 0x8512, 0x0000 }, + { 0x8513, 0x0000 }, + { 0x8514, 0x0000 }, + { 0x8515, 0x0000 }, + { 0x8516, 0x0000 }, + { 0x8517, 0x0000 }, + { 0x8518, 0x0000 }, + { 0x8519, 0x0000 }, + { 0x851a, 0x0000 }, + { 0x851b, 0x0000 }, + { 0x851c, 0x0000 }, + { 0x851d, 0x0000 }, + { 0x851e, 0x0000 }, + { 0x851f, 0x0000 }, + { 0x8520, 0x0000 }, + { 0x8521, 0x0000 }, + { 0x8522, 0x0000 }, + { 0x8523, 0x0000 }, + { 0x8524, 0x0000 }, + { 0x8525, 0x0000 }, + { 0x8526, 0x0000 }, + { 0x8527, 0x0000 }, + { 0x8528, 0x0000 }, + { 0x8529, 0x0000 }, + { 0x852a, 0x0000 }, + { 0x852b, 0x0000 }, + { 0x852c, 0x0000 }, + { 0x852d, 0x0000 }, + { 0x852e, 0x0000 }, + { 0x852f, 0x0000 }, + { 0x8530, 0x0000 }, + { 0x8531, 0x0000 }, + { 0x8532, 0x0000 }, + { 0x8533, 0x0000 }, + { 0x8534, 0x0000 }, + { 0x8535, 0x0000 }, + { 0x8536, 0x0000 }, + { 0x8537, 0x0000 }, + { 0x8538, 0x0000 }, + { 0x8539, 0x0000 }, + { 0x853a, 0x0000 }, + { 0x853b, 0x0000 }, + { 0x853c, 0x0000 }, + { 0x853d, 0x0000 }, + { 0x853e, 0x0000 }, + { 0x853f, 0x0000 }, + { 0x8540, 0x0000 }, + { 0x8541, 0x0000 }, + { 0x8542, 0x0000 }, + { 0x8543, 0x0000 }, + { 0x8544, 0x0000 }, + { 0x8545, 0x0000 }, + { 0x8546, 0x0000 }, + { 0x8547, 0x0000 }, + { 0x8548, 0x0000 }, + { 0x8549, 0x0000 }, + { 0x854a, 0x0000 }, + { 0x854b, 0x0000 }, + { 0x854c, 0x0000 }, + { 0x854d, 0x0000 }, + { 0x854e, 0x0000 }, + { 0x854f, 0x0000 }, + { 0x8550, 0x0000 }, + { 0x8551, 0x0000 }, + { 0x8552, 0x0000 }, + { 0x8553, 0x0000 }, + { 0x8554, 0x0000 }, + { 0x8555, 0x0000 }, + { 0x8556, 0x0000 }, + { 0x8557, 0x0000 }, + { 0x8558, 0x0000 }, + { 0x8559, 0x0000 }, + { 0x855a, 0x0000 }, + { 0x855b, 0x0000 }, + { 0x855c, 0x0000 }, + { 0x855d, 0x0000 }, + { 0x855e, 0x0000 }, + { 0x855f, 0x0000 }, + { 0x8560, 0x0000 }, + { 0x8561, 0x0000 }, + { 0x8562, 0x0000 }, + { 0x8563, 0x0000 }, + { 0x8564, 0x0000 }, + { 0x8565, 0x0000 }, + { 0x8566, 0x0000 }, + { 0x8567, 0x0000 }, + { 0x8568, 0x0000 }, + { 0x8569, 0x0000 }, + { 0x856a, 0x0000 }, + { 0x856b, 0x0000 }, + { 0x856c, 0x0000 }, + { 0x856d, 0x0000 }, + { 0x856e, 0x0000 }, + { 0x856f, 0x0000 }, + { 0x8570, 0x0000 }, + { 0x8571, 0x0000 }, + { 0x8572, 0x0000 }, + { 0x8573, 0x0000 }, + { 0x8574, 0x0000 }, + { 0x8575, 0x0000 }, + { 0x8576, 0x0000 }, + { 0x8577, 0x0000 }, + { 0x8578, 0x0000 }, + { 0x8579, 0x0000 }, + { 0x857a, 0x0000 }, + { 0x857b, 0x0000 }, + { 0x857c, 0x0000 }, + { 0x857d, 0x0000 }, + { 0x857e, 0x0000 }, + { 0x857f, 0x0000 }, + { 0x8580, 0x0000 }, + { 0x8581, 0x0000 }, + { 0x8582, 0x0000 }, + { 0x8583, 0x0000 }, + { 0x8584, 0x0000 }, + { 0x8585, 0x0000 }, + { 0x8586, 0x0000 }, + { 0x8587, 0x0000 }, + { 0x8588, 0x0000 }, + { 0x8589, 0x0000 }, + { 0x858a, 0x0000 }, + { 0x858b, 0x0000 }, + { 0x858c, 0x0000 }, + { 0x858d, 0x0000 }, + { 0x858e, 0x0000 }, + { 0x858f, 0x0000 }, + { 0x8590, 0x0000 }, + { 0x8591, 0x0000 }, + { 0x8592, 0x0000 }, + { 0x8593, 0x0000 }, + { 0x8594, 0x0000 }, + { 0x8595, 0x0000 }, + { 0x8596, 0x0000 }, + { 0x8597, 0x0000 }, + { 0x8598, 0x0000 }, + { 0x8599, 0x0000 }, + { 0x859a, 0x0000 }, + { 0x859b, 0x0000 }, + { 0x859c, 0x0000 }, + { 0x859d, 0x0000 }, + { 0x859e, 0x0000 }, + { 0x859f, 0x0000 }, + { 0x85a0, 0x0000 }, + { 0x85a1, 0x0000 }, + { 0x85a2, 0x0000 }, + { 0x85a3, 0x0000 }, + { 0x85a4, 0x0000 }, + { 0x85a5, 0x0000 }, + { 0x85a6, 0x0000 }, + { 0x85a7, 0x0000 }, + { 0x85a8, 0x0000 }, + { 0x85a9, 0x0000 }, + { 0x85aa, 0x0000 }, + { 0x85ab, 0x0000 }, + { 0x85ac, 0x0000 }, + { 0x85ad, 0x0000 }, + { 0x85ae, 0x0000 }, + { 0x85af, 0x0000 }, + { 0x85b0, 0x0000 }, + { 0x85b1, 0x0000 }, + { 0x85b2, 0x0000 }, + { 0x85b3, 0x0000 }, + { 0x85b4, 0x0000 }, + { 0x85b5, 0x0000 }, + { 0x85b6, 0x0000 }, + { 0x85b7, 0x0000 }, + { 0x85b8, 0x0000 }, + { 0x85b9, 0x0000 }, + { 0x85ba, 0x0000 }, + { 0x85bb, 0x0000 }, + { 0x85bc, 0x0000 }, + { 0x85bd, 0x0000 }, + { 0x85be, 0x0000 }, + { 0x85bf, 0x0000 }, + { 0x85c0, 0x0000 }, + { 0x85c1, 0x0000 }, + { 0x85c2, 0x0000 }, + { 0x85c3, 0x0000 }, + { 0x85c4, 0x0000 }, + { 0x85c5, 0x0000 }, + { 0x85c6, 0x0000 }, + { 0x85c7, 0x0000 }, + { 0x85c8, 0x0000 }, + { 0x85c9, 0x0000 }, + { 0x85ca, 0x0000 }, + { 0x85cb, 0x0000 }, + { 0x85cc, 0x0000 }, + { 0x85cd, 0x0000 }, + { 0x85ce, 0x0000 }, + { 0x85cf, 0x0000 }, + { 0x85d0, 0x0000 }, + { 0x85d1, 0x0000 }, + { 0x85d2, 0x0000 }, + { 0x85d3, 0x0000 }, + { 0x85d4, 0x0000 }, + { 0x85d5, 0x0000 }, + { 0x85d6, 0x0000 }, + { 0x85d7, 0x0000 }, + { 0x85d8, 0x0000 }, + { 0x85d9, 0x0000 }, + { 0x85da, 0x0000 }, + { 0x85db, 0x0000 }, + { 0x85dc, 0x0000 }, + { 0x85dd, 0x0000 }, + { 0x85de, 0x0000 }, + { 0x85df, 0x0000 }, + { 0x85e0, 0x0000 }, + { 0x85e1, 0x0000 }, + { 0x85e2, 0x0000 }, + { 0x85e3, 0x0000 }, + { 0x85e4, 0x0000 }, + { 0x85e5, 0x0000 }, + { 0x85e6, 0x0000 }, + { 0x85e7, 0x0000 }, + { 0x85e8, 0x0000 }, + { 0x85e9, 0x0000 }, + { 0x85ea, 0x0000 }, + { 0x85eb, 0x0000 }, + { 0x85ec, 0x0000 }, + { 0x85ed, 0x0000 }, + { 0x85ee, 0x0000 }, + { 0x85ef, 0x0000 }, + { 0x85f0, 0x0000 }, + { 0x85f1, 0x0000 }, + { 0x85f2, 0x0000 }, + { 0x85f3, 0x0000 }, + { 0x85f4, 0x0000 }, + { 0x85f5, 0x0000 }, + { 0x85f6, 0x0000 }, + { 0x85f7, 0x0000 }, + { 0x85f8, 0x0000 }, + { 0x85f9, 0x0000 }, + { 0x85fa, 0x0000 }, + { 0x85fb, 0x0000 }, + { 0x85fc, 0x0000 }, + { 0x85fd, 0x0000 }, + { 0x85fe, 0x0000 }, + { 0x85ff, 0x0000 }, + { 0x8600, 0x0000 }, + { 0x8601, 0x0000 }, + { 0x8602, 0x0000 }, + { 0x8603, 0x0000 }, + { 0x8604, 0x0000 }, + { 0x8605, 0x0000 }, + { 0x8606, 0x0000 }, + { 0x8607, 0x0000 }, + { 0x8608, 0x0000 }, + { 0x8609, 0x0000 }, + { 0x860a, 0x0000 }, + { 0x860b, 0x0000 }, + { 0x860c, 0x0000 }, + { 0x860d, 0x0000 }, + { 0x860e, 0x0000 }, + { 0x860f, 0x0000 }, + { 0x8610, 0x0000 }, + { 0x8611, 0x0000 }, + { 0x8612, 0x0000 }, + { 0x8613, 0x0000 }, + { 0x8614, 0x0000 }, + { 0x8615, 0x0000 }, + { 0x8616, 0x0000 }, + { 0x8617, 0x0000 }, + { 0x8618, 0x0000 }, + { 0x8619, 0x0000 }, + { 0x861a, 0x0000 }, + { 0x861b, 0x0000 }, + { 0x861c, 0x0000 }, + { 0x861d, 0x0000 }, + { 0x861e, 0x0000 }, + { 0x861f, 0x0000 }, + { 0x8620, 0x0000 }, + { 0x8621, 0x0000 }, + { 0x8622, 0x0000 }, + { 0x8623, 0x0000 }, + { 0x8624, 0x0000 }, + { 0x8625, 0x0000 }, + { 0x8626, 0x0000 }, + { 0x8627, 0x0000 }, + { 0x8628, 0x0000 }, + { 0x8629, 0x0000 }, + { 0x862a, 0x0000 }, + { 0x862b, 0x0000 }, + { 0x862c, 0x0000 }, + { 0x862d, 0x0000 }, + { 0x862e, 0x0000 }, + { 0x862f, 0x0000 }, + { 0x8630, 0x0000 }, + { 0x8631, 0x0000 }, + { 0x8632, 0x0000 }, + { 0x8633, 0x0000 }, + { 0x8634, 0x0000 }, + { 0x8635, 0x0000 }, + { 0x8636, 0x0000 }, + { 0x8637, 0x0000 }, + { 0x8638, 0x0000 }, + { 0x8639, 0x0000 }, + { 0x863a, 0x0000 }, + { 0x863b, 0x0000 }, + { 0x863c, 0x0000 }, + { 0x863d, 0x0000 }, + { 0x863e, 0x0000 }, + { 0x863f, 0x0000 }, + { 0x8640, 0x0000 }, + { 0x8641, 0x0000 }, + { 0x8642, 0x0000 }, + { 0x8643, 0x0000 }, + { 0x8644, 0x0000 }, + { 0x8645, 0x0000 }, + { 0x8646, 0x0000 }, + { 0x8647, 0x0000 }, + { 0x8648, 0x0000 }, + { 0x8649, 0x0000 }, + { 0x864a, 0x0000 }, + { 0x864b, 0x0000 }, + { 0x864c, 0x0000 }, + { 0x864d, 0x0000 }, + { 0x864e, 0x0000 }, + { 0x864f, 0x0000 }, + { 0x8650, 0x0000 }, + { 0x8651, 0x0000 }, + { 0x8652, 0x0000 }, + { 0x8653, 0x0000 }, + { 0x8654, 0x0000 }, + { 0x8655, 0x0000 }, + { 0x8656, 0x0000 }, + { 0x8657, 0x0000 }, + { 0x8658, 0x0000 }, + { 0x8659, 0x0000 }, + { 0x865a, 0x0000 }, + { 0x865b, 0x0000 }, + { 0x865c, 0x0000 }, + { 0x865d, 0x0000 }, + { 0x865e, 0x0000 }, + { 0x865f, 0x0000 }, + { 0x8660, 0x0000 }, + { 0x8661, 0x0000 }, + { 0x8662, 0x0000 }, + { 0x8663, 0x0000 }, + { 0x8664, 0x0000 }, + { 0x8665, 0x0000 }, + { 0x8666, 0x0000 }, + { 0x8667, 0x0000 }, + { 0x8668, 0x0000 }, + { 0x8669, 0x0000 }, + { 0x866a, 0x0000 }, + { 0x866b, 0x0000 }, + { 0x866c, 0x0000 }, + { 0x866d, 0x0000 }, + { 0x866e, 0x0000 }, + { 0x866f, 0x0000 }, + { 0x8670, 0x0000 }, + { 0x8671, 0x0000 }, + { 0x8672, 0x0000 }, + { 0x8673, 0x0000 }, + { 0x8674, 0x0000 }, + { 0x8675, 0x0000 }, + { 0x8676, 0x0000 }, + { 0x8677, 0x0000 }, + { 0x8678, 0x0000 }, + { 0x8679, 0x0000 }, + { 0x867a, 0x0000 }, + { 0x867b, 0x0000 }, + { 0x867c, 0x0000 }, + { 0x867d, 0x0000 }, + { 0x867e, 0x0000 }, + { 0x867f, 0x0000 }, + { 0x8680, 0x0000 }, + { 0x8681, 0x0000 }, + { 0x8682, 0x0000 }, + { 0x8683, 0x0000 }, + { 0x8684, 0x0000 }, + { 0x8685, 0x0000 }, + { 0x8686, 0x0000 }, + { 0x8687, 0x0000 }, + { 0x8688, 0x0000 }, + { 0x8689, 0x0000 }, + { 0x868a, 0x0000 }, + { 0x868b, 0x0000 }, + { 0x868c, 0x0000 }, + { 0x868d, 0x0000 }, + { 0x868e, 0x0000 }, + { 0x868f, 0x0000 }, + { 0x8690, 0x0000 }, + { 0x8691, 0x0000 }, + { 0x8692, 0x0000 }, + { 0x8693, 0x0000 }, + { 0x8694, 0x0000 }, + { 0x8695, 0x0000 }, + { 0x8696, 0x0000 }, + { 0x8697, 0x0000 }, + { 0x8698, 0x0000 }, + { 0x8699, 0x0000 }, + { 0x869a, 0x0000 }, + { 0x869b, 0x0000 }, + { 0x869c, 0x0000 }, + { 0x869d, 0x0000 }, + { 0x869e, 0x0000 }, + { 0x869f, 0x0000 }, + { 0x86a0, 0x0000 }, + { 0x86a1, 0x0000 }, + { 0x86a2, 0x0000 }, + { 0x86a3, 0x0000 }, + { 0x86a4, 0x0000 }, + { 0x86a5, 0x0000 }, + { 0x86a6, 0x0000 }, + { 0x86a7, 0x0000 }, + { 0x86a8, 0x0000 }, + { 0x86a9, 0x0000 }, + { 0x86aa, 0x0000 }, + { 0x86ab, 0x0000 }, + { 0x86ac, 0x0000 }, + { 0x86ad, 0x0000 }, + { 0x86ae, 0x0000 }, + { 0x86af, 0x0000 }, + { 0x86b0, 0x0000 }, + { 0x86b1, 0x0000 }, + { 0x86b2, 0x0000 }, + { 0x86b3, 0x0000 }, + { 0x86b4, 0x0000 }, + { 0x86b5, 0x0000 }, + { 0x86b6, 0x0000 }, + { 0x86b7, 0x0000 }, + { 0x86b8, 0x0000 }, + { 0x86b9, 0x0000 }, + { 0x86ba, 0x0000 }, + { 0x86bb, 0x0000 }, + { 0x86bc, 0x0000 }, + { 0x86bd, 0x0000 }, + { 0x86be, 0x0000 }, + { 0x86bf, 0x0000 }, + { 0x86c0, 0x0000 }, + { 0x86c1, 0x0000 }, + { 0x86c2, 0x0000 }, + { 0x86c3, 0x0000 }, + { 0x86c4, 0x0000 }, + { 0x86c5, 0x0000 }, + { 0x86c6, 0x0000 }, + { 0x86c7, 0x0000 }, + { 0x86c8, 0x0000 }, + { 0x86c9, 0x0000 }, + { 0x86ca, 0x0000 }, + { 0x86cb, 0x0000 }, + { 0x86cc, 0x0000 }, + { 0x86cd, 0x0000 }, + { 0x86ce, 0x0000 }, + { 0x86cf, 0x0000 }, + { 0x86d0, 0x0000 }, + { 0x86d1, 0x0000 }, + { 0x86d2, 0x0000 }, + { 0x86d3, 0x0000 }, + { 0x86d4, 0x0000 }, + { 0x86d5, 0x0000 }, + { 0x86d6, 0x0000 }, + { 0x86d7, 0x0000 }, + { 0x86d8, 0x0000 }, + { 0x86d9, 0x0000 }, + { 0x86da, 0x0000 }, + { 0x86db, 0x0000 }, + { 0x86dc, 0x0000 }, + { 0x86dd, 0x0000 }, + { 0x86de, 0x0000 }, + { 0x86df, 0x0000 }, + { 0x86e0, 0x0000 }, + { 0x86e1, 0x0000 }, + { 0x86e2, 0x0000 }, + { 0x86e3, 0x0000 }, + { 0x86e4, 0x0000 }, + { 0x86e5, 0x0000 }, + { 0x86e6, 0x0000 }, + { 0x86e7, 0x0000 }, + { 0x86e8, 0x0000 }, + { 0x86e9, 0x0000 }, + { 0x86ea, 0x0000 }, + { 0x86eb, 0x0000 }, + { 0x86ec, 0x0000 }, + { 0x86ed, 0x0000 }, + { 0x86ee, 0x0000 }, + { 0x86ef, 0x0000 }, + { 0x86f0, 0x0000 }, + { 0x86f1, 0x0000 }, + { 0x86f2, 0x0000 }, + { 0x86f3, 0x0000 }, + { 0x86f4, 0x0000 }, + { 0x86f5, 0x0000 }, + { 0x86f6, 0x0000 }, + { 0x86f7, 0x0000 }, + { 0x86f8, 0x0000 }, + { 0x86f9, 0x0000 }, + { 0x86fa, 0x0000 }, + { 0x86fb, 0x0000 }, + { 0x86fc, 0x0000 }, + { 0x86fd, 0x0000 }, + { 0x86fe, 0x0000 }, + { 0x86ff, 0x0000 }, + { 0x8700, 0x0000 }, + { 0x8701, 0x0000 }, + { 0x8702, 0x0000 }, + { 0x8703, 0x0000 }, + { 0x8704, 0x0000 }, + { 0x8705, 0x0000 }, + { 0x8706, 0x0000 }, + { 0x8707, 0x0000 }, + { 0x8708, 0x0000 }, + { 0x8709, 0x0000 }, + { 0x870a, 0x0000 }, + { 0x870b, 0x0000 }, + { 0x870c, 0x0000 }, + { 0x870d, 0x0000 }, + { 0x870e, 0x0000 }, + { 0x870f, 0x0000 }, + { 0x8710, 0x0000 }, + { 0x8711, 0x0000 }, + { 0x8712, 0x0000 }, + { 0x8713, 0x0000 }, + { 0x8714, 0x0000 }, + { 0x8715, 0x0000 }, + { 0x8716, 0x0000 }, + { 0x8717, 0x0000 }, + { 0x8718, 0x0000 }, + { 0x8719, 0x0000 }, + { 0x871a, 0x0000 }, + { 0x871b, 0x0000 }, + { 0x871c, 0x0000 }, + { 0x871d, 0x0000 }, + { 0x871e, 0x0000 }, + { 0x871f, 0x0000 }, + { 0x8720, 0x0000 }, + { 0x8721, 0x0000 }, + { 0x8722, 0x0000 }, + { 0x8723, 0x0000 }, + { 0x8724, 0x0000 }, + { 0x8725, 0x0000 }, + { 0x8726, 0x0000 }, + { 0x8727, 0x0000 }, + { 0x8728, 0x0000 }, + { 0x8729, 0x0000 }, + { 0x872a, 0x0000 }, + { 0x872b, 0x0000 }, + { 0x872c, 0x0000 }, + { 0x872d, 0x0000 }, + { 0x872e, 0x0000 }, + { 0x872f, 0x0000 }, + { 0x8730, 0x0000 }, + { 0x8731, 0x0000 }, + { 0x8732, 0x0000 }, + { 0x8733, 0x0000 }, + { 0x8734, 0x0000 }, + { 0x8735, 0x0000 }, + { 0x8736, 0x0000 }, + { 0x8737, 0x0000 }, + { 0x8738, 0x0000 }, + { 0x8739, 0x0000 }, + { 0x873a, 0x0000 }, + { 0x873b, 0x0000 }, + { 0x873c, 0x0000 }, + { 0x873d, 0x0000 }, + { 0x873e, 0x0000 }, + { 0x873f, 0x0000 }, + { 0x8740, 0x0000 }, + { 0x8741, 0x0000 }, + { 0x8742, 0x0000 }, + { 0x8743, 0x0000 }, + { 0x8744, 0x0000 }, + { 0x8745, 0x0000 }, + { 0x8746, 0x0000 }, + { 0x8747, 0x0000 }, + { 0x8748, 0x0000 }, + { 0x8749, 0x0000 }, + { 0x874a, 0x0000 }, + { 0x874b, 0x0000 }, + { 0x874c, 0x0000 }, + { 0x874d, 0x0000 }, + { 0x874e, 0x0000 }, + { 0x874f, 0x0000 }, + { 0x8750, 0x0000 }, + { 0x8751, 0x0000 }, + { 0x8752, 0x0000 }, + { 0x8753, 0x0000 }, + { 0x8754, 0x0000 }, + { 0x8755, 0x0000 }, + { 0x8756, 0x0000 }, + { 0x8757, 0x0000 }, + { 0x8758, 0x0000 }, + { 0x8759, 0x0000 }, + { 0x875a, 0x0000 }, + { 0x875b, 0x0000 }, + { 0x875c, 0x0000 }, + { 0x875d, 0x0000 }, + { 0x875e, 0x0000 }, + { 0x875f, 0x0000 }, + { 0x8760, 0x0000 }, + { 0x8761, 0x0000 }, + { 0x8762, 0x0000 }, + { 0x8763, 0x0000 }, + { 0x8764, 0x0000 }, + { 0x8765, 0x0000 }, + { 0x8766, 0x0000 }, + { 0x8767, 0x0000 }, + { 0x8768, 0x0000 }, + { 0x8769, 0x0000 }, + { 0x876a, 0x0000 }, + { 0x876b, 0x0000 }, + { 0x876c, 0x0000 }, + { 0x876d, 0x0000 }, + { 0x876e, 0x0000 }, + { 0x876f, 0x0000 }, + { 0x8770, 0x0000 }, + { 0x8771, 0x0000 }, + { 0x8772, 0x0000 }, + { 0x8773, 0x0000 }, + { 0x8774, 0x0000 }, + { 0x8775, 0x0000 }, + { 0x8776, 0x0000 }, + { 0x8777, 0x0000 }, + { 0x8778, 0x0000 }, + { 0x8779, 0x0000 }, + { 0x877a, 0x0000 }, + { 0x877b, 0x0000 }, + { 0x877c, 0x0000 }, + { 0x877d, 0x0000 }, + { 0x877e, 0x0000 }, + { 0x877f, 0x0000 }, + { 0x8780, 0x0000 }, + { 0x8781, 0x0000 }, + { 0x8782, 0x0000 }, + { 0x8783, 0x0000 }, + { 0x8784, 0x0000 }, + { 0x8785, 0x0000 }, + { 0x8786, 0x0000 }, + { 0x8787, 0x0000 }, + { 0x8788, 0x0000 }, + { 0x8789, 0x0000 }, + { 0x878a, 0x0000 }, + { 0x878b, 0x0000 }, + { 0x878c, 0x0000 }, + { 0x878d, 0x0000 }, + { 0x878e, 0x0000 }, + { 0x878f, 0x0000 }, + { 0x8790, 0x0000 }, + { 0x8791, 0x0000 }, + { 0x8792, 0x0000 }, + { 0x8793, 0x0000 }, + { 0x8794, 0x0000 }, + { 0x8795, 0x0000 }, + { 0x8796, 0x0000 }, + { 0x8797, 0x0000 }, + { 0x8798, 0x0000 }, + { 0x8799, 0x0000 }, + { 0x879a, 0x0000 }, + { 0x879b, 0x0000 }, + { 0x879c, 0x0000 }, + { 0x879d, 0x0000 }, + { 0x879e, 0x0000 }, + { 0x879f, 0x0000 }, + { 0x87a0, 0x0000 }, + { 0x87a1, 0x0000 }, + { 0x87a2, 0x0000 }, + { 0x87a3, 0x0000 }, + { 0x87a4, 0x0000 }, + { 0x87a5, 0x0000 }, + { 0x87a6, 0x0000 }, + { 0x87a7, 0x0000 }, + { 0x87a8, 0x0000 }, + { 0x87a9, 0x0000 }, + { 0x87aa, 0x0000 }, + { 0x87ab, 0x0000 }, + { 0x87ac, 0x0000 }, + { 0x87ad, 0x0000 }, + { 0x87ae, 0x0000 }, + { 0x87af, 0x0000 }, + { 0x87b0, 0x0000 }, + { 0x87b1, 0x0000 }, + { 0x87b2, 0x0000 }, + { 0x87b3, 0x0000 }, + { 0x87b4, 0x0000 }, + { 0x87b5, 0x0000 }, + { 0x87b6, 0x0000 }, + { 0x87b7, 0x0000 }, + { 0x87b8, 0x0000 }, + { 0x87b9, 0x0000 }, + { 0x87ba, 0x0000 }, + { 0x87bb, 0x0000 }, + { 0x87bc, 0x0000 }, + { 0x87bd, 0x0000 }, + { 0x87be, 0x0000 }, + { 0x87bf, 0x0000 }, + { 0x87c0, 0x0000 }, + { 0x87c1, 0x0000 }, + { 0x87c2, 0x0000 }, + { 0x87c3, 0x0000 }, + { 0x87c4, 0x0000 }, + { 0x87c5, 0x0000 }, + { 0x87c6, 0x0000 }, + { 0x87c7, 0x0000 }, + { 0x87c8, 0x0000 }, + { 0x87c9, 0x0000 }, + { 0x87ca, 0x0000 }, + { 0x87cb, 0x0000 }, + { 0x87cc, 0x0000 }, + { 0x87cd, 0x0000 }, + { 0x87ce, 0x0000 }, + { 0x87cf, 0x0000 }, + { 0x87d0, 0x0000 }, + { 0x87d1, 0x0000 }, + { 0x87d2, 0x0000 }, + { 0x87d3, 0x0000 }, + { 0x87d4, 0x0000 }, + { 0x87d5, 0x0000 }, + { 0x87d6, 0x0000 }, + { 0x87d7, 0x0000 }, + { 0x87d8, 0x0000 }, + { 0x87d9, 0x0000 }, + { 0x87da, 0x0000 }, + { 0x87db, 0x0000 }, + { 0x87dc, 0x0000 }, + { 0x87dd, 0x0000 }, + { 0x87de, 0x0000 }, + { 0x87df, 0x0000 }, + { 0x87e0, 0x0000 }, + { 0x87e1, 0x0000 }, + { 0x87e2, 0x0000 }, + { 0x87e3, 0x0000 }, + { 0x87e4, 0x0000 }, + { 0x87e5, 0x0000 }, + { 0x87e6, 0x0000 }, + { 0x87e7, 0x0000 }, + { 0x87e8, 0x0000 }, + { 0x87e9, 0x0000 }, + { 0x87ea, 0x0000 }, + { 0x87eb, 0x0000 }, + { 0x87ec, 0x0000 }, + { 0x87ed, 0x0000 }, + { 0x87ee, 0x0000 }, + { 0x87ef, 0x0000 }, + { 0x87f0, 0x0000 }, + { 0x87f1, 0x0000 }, + { 0x87f2, 0x0000 }, + { 0x87f3, 0x0000 }, + { 0x87f4, 0x0000 }, + { 0x87f5, 0x0000 }, + { 0x87f6, 0x0000 }, + { 0x87f7, 0x0000 }, + { 0x87f8, 0x0000 }, + { 0x87f9, 0x0000 }, + { 0x87fa, 0x0000 }, + { 0x87fb, 0x0000 }, + { 0x87fc, 0x0000 }, + { 0x87fd, 0x0000 }, + { 0x87fe, 0x0000 }, + { 0x87ff, 0x0000 }, + { 0x8800, 0x0000 }, + { 0x8801, 0x0000 }, + { 0x8802, 0x0000 }, + { 0x8803, 0x0000 }, + { 0x8804, 0x0000 }, + { 0x8805, 0x0000 }, + { 0x8806, 0x0000 }, + { 0x8807, 0x0000 }, + { 0x8808, 0x0000 }, + { 0x8809, 0x0000 }, + { 0x880a, 0x0000 }, + { 0x880b, 0x0000 }, + { 0x880c, 0x0000 }, + { 0x880d, 0x0000 }, + { 0x880e, 0x0000 }, + { 0x880f, 0x0000 }, + { 0x8810, 0x0000 }, + { 0x8811, 0x0000 }, + { 0x8812, 0x0000 }, + { 0x8813, 0x0000 }, + { 0x8814, 0x0000 }, + { 0x8815, 0x0000 }, + { 0x8816, 0x0000 }, + { 0x8817, 0x0000 }, + { 0x8818, 0x0000 }, + { 0x8819, 0x0000 }, + { 0x881a, 0x0000 }, + { 0x881b, 0x0000 }, + { 0x881c, 0x0000 }, + { 0x881d, 0x0000 }, + { 0x881e, 0x0000 }, + { 0x881f, 0x0000 }, + { 0x8820, 0x0000 }, + { 0x8821, 0x0000 }, + { 0x8822, 0x0000 }, + { 0x8823, 0x0000 }, + { 0x8824, 0x0000 }, + { 0x8825, 0x0000 }, + { 0x8826, 0x0000 }, + { 0x8827, 0x0000 }, + { 0x8828, 0x0000 }, + { 0x8829, 0x0000 }, + { 0x882a, 0x0000 }, + { 0x882b, 0x0000 }, + { 0x882c, 0x0000 }, + { 0x882d, 0x0000 }, + { 0x882e, 0x0000 }, + { 0x882f, 0x0000 }, + { 0x8830, 0x0000 }, + { 0x8831, 0x0000 }, + { 0x8832, 0x0000 }, + { 0x8833, 0x0000 }, + { 0x8834, 0x0000 }, + { 0x8835, 0x0000 }, + { 0x8836, 0x0000 }, + { 0x8837, 0x0000 }, + { 0x8838, 0x0000 }, + { 0x8839, 0x0000 }, + { 0x883a, 0x0000 }, + { 0x883b, 0x0000 }, + { 0x883c, 0x0000 }, + { 0x883d, 0x0000 }, + { 0x883e, 0x0000 }, + { 0x883f, 0x0000 }, + { 0x8840, 0x0000 }, + { 0x8841, 0x0000 }, + { 0x8842, 0x0000 }, + { 0x8843, 0x0000 }, + { 0x8844, 0x0000 }, + { 0x8845, 0x0000 }, + { 0x8846, 0x0000 }, + { 0x8847, 0x0000 }, + { 0x8848, 0x0000 }, + { 0x8849, 0x0000 }, + { 0x884a, 0x0000 }, + { 0x884b, 0x0000 }, + { 0x884c, 0x0000 }, + { 0x884d, 0x0000 }, + { 0x884e, 0x0000 }, + { 0x884f, 0x0000 }, + { 0x8850, 0x0000 }, + { 0x8851, 0x0000 }, + { 0x8852, 0x0000 }, + { 0x8853, 0x0000 }, + { 0x8854, 0x0000 }, + { 0x8855, 0x0000 }, + { 0x8856, 0x0000 }, + { 0x8857, 0x0000 }, + { 0x8858, 0x0000 }, + { 0x8859, 0x0000 }, + { 0x885a, 0x0000 }, + { 0x885b, 0x0000 }, + { 0x885c, 0x0000 }, + { 0x885d, 0x0000 }, + { 0x885e, 0x0000 }, + { 0x885f, 0x0000 }, + { 0x8860, 0x0000 }, + { 0x8861, 0x0000 }, + { 0x8862, 0x0000 }, + { 0x8863, 0x0000 }, + { 0x8864, 0x0000 }, + { 0x8865, 0x0000 }, + { 0x8866, 0x0000 }, + { 0x8867, 0x0000 }, + { 0x8868, 0x0000 }, + { 0x8869, 0x0000 }, + { 0x886a, 0x0000 }, + { 0x886b, 0x0000 }, + { 0x886c, 0x0000 }, + { 0x886d, 0x0000 }, + { 0x886e, 0x0000 }, + { 0x886f, 0x0000 }, + { 0x8870, 0x0000 }, + { 0x8871, 0x0000 }, + { 0x8872, 0x0000 }, + { 0x8873, 0x0000 }, + { 0x8874, 0x0000 }, + { 0x8875, 0x0000 }, + { 0x8876, 0x0000 }, + { 0x8877, 0x0000 }, + { 0x8878, 0x0000 }, + { 0x8879, 0x0000 }, + { 0x887a, 0x0000 }, + { 0x887b, 0x0000 }, + { 0x887c, 0x0000 }, + { 0x887d, 0x0000 }, + { 0x887e, 0x0000 }, + { 0x887f, 0x0000 }, + { 0x8880, 0x0000 }, + { 0x8881, 0x0000 }, + { 0x8882, 0x0000 }, + { 0x8883, 0x0000 }, + { 0x8884, 0x0000 }, + { 0x8885, 0x0000 }, + { 0x8886, 0x0000 }, + { 0x8887, 0x0000 }, + { 0x8888, 0x0000 }, + { 0x8889, 0x0000 }, + { 0x888a, 0x0000 }, + { 0x888b, 0x0000 }, + { 0x888c, 0x0000 }, + { 0x888d, 0x0000 }, + { 0x888e, 0x0000 }, + { 0x888f, 0x0000 }, + { 0x8890, 0x0000 }, + { 0x8891, 0x0000 }, + { 0x8892, 0x0000 }, + { 0x8893, 0x0000 }, + { 0x8894, 0x0000 }, + { 0x8895, 0x0000 }, + { 0x8896, 0x0000 }, + { 0x8897, 0x0000 }, + { 0x8898, 0x0000 }, + { 0x8899, 0x0000 }, + { 0x889a, 0x0000 }, + { 0x889b, 0x0000 }, + { 0x889c, 0x0000 }, + { 0x889d, 0x0000 }, + { 0x889e, 0x0000 }, + { 0x889f, 0x0000 }, + { 0x88a0, 0x0000 }, + { 0x88a1, 0x0000 }, + { 0x88a2, 0x0000 }, + { 0x88a3, 0x0000 }, + { 0x88a4, 0x0000 }, + { 0x88a5, 0x0000 }, + { 0x88a6, 0x0000 }, + { 0x88a7, 0x0000 }, + { 0x88a8, 0x0000 }, + { 0x88a9, 0x0000 }, + { 0x88aa, 0x0000 }, + { 0x88ab, 0x0000 }, + { 0x88ac, 0x0000 }, + { 0x88ad, 0x0000 }, + { 0x88ae, 0x0000 }, + { 0x88af, 0x0000 }, + { 0x88b0, 0x0000 }, + { 0x88b1, 0x0000 }, + { 0x88b2, 0x0000 }, + { 0x88b3, 0x0000 }, + { 0x88b4, 0x0000 }, + { 0x88b5, 0x0000 }, + { 0x88b6, 0x0000 }, + { 0x88b7, 0x0000 }, + { 0x88b8, 0x0000 }, + { 0x88b9, 0x0000 }, + { 0x88ba, 0x0000 }, + { 0x88bb, 0x0000 }, + { 0x88bc, 0x0000 }, + { 0x88bd, 0x0000 }, + { 0x88be, 0x0000 }, + { 0x88bf, 0x0000 }, + { 0x88c0, 0x0000 }, + { 0x88c1, 0x0000 }, + { 0x88c2, 0x0000 }, + { 0x88c3, 0x0000 }, + { 0x88c4, 0x0000 }, + { 0x88c5, 0x0000 }, + { 0x88c6, 0x0000 }, + { 0x88c7, 0x0000 }, + { 0x88c8, 0x0000 }, + { 0x88c9, 0x0000 }, + { 0x88ca, 0x0000 }, + { 0x88cb, 0x0000 }, + { 0x88cc, 0x0000 }, + { 0x88cd, 0x0000 }, + { 0x88ce, 0x0000 }, + { 0x88cf, 0x0000 }, + { 0x88d0, 0x0000 }, + { 0x88d1, 0x0000 }, + { 0x88d2, 0x0000 }, + { 0x88d3, 0x0000 }, + { 0x88d4, 0x0000 }, + { 0x88d5, 0x0000 }, + { 0x88d6, 0x0000 }, + { 0x88d7, 0x0000 }, + { 0x88d8, 0x0000 }, + { 0x88d9, 0x0000 }, + { 0x88da, 0x0000 }, + { 0x88db, 0x0000 }, + { 0x88dc, 0x0000 }, + { 0x88dd, 0x0000 }, + { 0x88de, 0x0000 }, + { 0x88df, 0x0000 }, + { 0x88e0, 0x0000 }, + { 0x88e1, 0x0000 }, + { 0x88e2, 0x0000 }, + { 0x88e3, 0x0000 }, + { 0x88e4, 0x0000 }, + { 0x88e5, 0x0000 }, + { 0x88e6, 0x0000 }, + { 0x88e7, 0x0000 }, + { 0x88e8, 0x0000 }, + { 0x88e9, 0x0000 }, + { 0x88ea, 0x0000 }, + { 0x88eb, 0x0000 }, + { 0x88ec, 0x0000 }, + { 0x88ed, 0x0000 }, + { 0x88ee, 0x0000 }, + { 0x88ef, 0x0000 }, + { 0x88f0, 0x0000 }, + { 0x88f1, 0x0000 }, + { 0x88f2, 0x0000 }, + { 0x88f3, 0x0000 }, + { 0x88f4, 0x0000 }, + { 0x88f5, 0x0000 }, + { 0x88f6, 0x0000 }, + { 0x88f7, 0x0000 }, + { 0x88f8, 0x0000 }, + { 0x88f9, 0x0000 }, + { 0x88fa, 0x0000 }, + { 0x88fb, 0x0000 }, + { 0x88fc, 0x0000 }, + { 0x88fd, 0x0000 }, + { 0x88fe, 0x0000 }, + { 0x88ff, 0x0000 }, + { 0x8900, 0x0000 }, + { 0x8901, 0x0000 }, + { 0x8902, 0x0000 }, + { 0x8903, 0x0000 }, + { 0x8904, 0x0000 }, + { 0x8905, 0x0000 }, + { 0x8906, 0x0000 }, + { 0x8907, 0x0000 }, + { 0x8908, 0x0000 }, + { 0x8909, 0x0000 }, + { 0x890a, 0x0000 }, + { 0x890b, 0x0000 }, + { 0x890c, 0x0000 }, + { 0x890d, 0x0000 }, + { 0x890e, 0x0000 }, + { 0x890f, 0x0000 }, + { 0x8910, 0x0000 }, + { 0x8911, 0x0000 }, + { 0x8912, 0x0000 }, + { 0x8913, 0x0000 }, + { 0x8914, 0x0000 }, + { 0x8915, 0x0000 }, + { 0x8916, 0x0000 }, + { 0x8917, 0x0000 }, + { 0x8918, 0x0000 }, + { 0x8919, 0x0000 }, + { 0x891a, 0x0000 }, + { 0x891b, 0x0000 }, + { 0x891c, 0x0000 }, + { 0x891d, 0x0000 }, + { 0x891e, 0x0000 }, + { 0x891f, 0x0000 }, + { 0x8920, 0x0000 }, + { 0x8921, 0x0000 }, + { 0x8922, 0x0000 }, + { 0x8923, 0x0000 }, + { 0x8924, 0x0000 }, + { 0x8925, 0x0000 }, + { 0x8926, 0x0000 }, + { 0x8927, 0x0000 }, + { 0x8928, 0x0000 }, + { 0x8929, 0x0000 }, + { 0x892a, 0x0000 }, + { 0x892b, 0x0000 }, + { 0x892c, 0x0000 }, + { 0x892d, 0x0000 }, + { 0x892e, 0x0000 }, + { 0x892f, 0x0000 }, + { 0x8930, 0x0000 }, + { 0x8931, 0x0000 }, + { 0x8932, 0x0000 }, + { 0x8933, 0x0000 }, + { 0x8934, 0x0000 }, + { 0x8935, 0x0000 }, + { 0x8936, 0x0000 }, + { 0x8937, 0x0000 }, + { 0x8938, 0x0000 }, + { 0x8939, 0x0000 }, + { 0x893a, 0x0000 }, + { 0x893b, 0x0000 }, + { 0x893c, 0x0000 }, + { 0x893d, 0x0000 }, + { 0x893e, 0x0000 }, + { 0x893f, 0x0000 }, + { 0x8940, 0x0000 }, + { 0x8941, 0x0000 }, + { 0x8942, 0x0000 }, + { 0x8943, 0x0000 }, + { 0x8944, 0x0000 }, + { 0x8945, 0x0000 }, + { 0x8946, 0x0000 }, + { 0x8947, 0x0000 }, + { 0x8948, 0x0000 }, + { 0x8949, 0x0000 }, + { 0x894a, 0x0000 }, + { 0x894b, 0x0000 }, + { 0x894c, 0x0000 }, + { 0x894d, 0x0000 }, + { 0x894e, 0x0000 }, + { 0x894f, 0x0000 }, + { 0x8950, 0x0000 }, + { 0x8951, 0x0000 }, + { 0x8952, 0x0000 }, + { 0x8953, 0x0000 }, + { 0x8954, 0x0000 }, + { 0x8955, 0x0000 }, + { 0x8956, 0x0000 }, + { 0x8957, 0x0000 }, + { 0x8958, 0x0000 }, + { 0x8959, 0x0000 }, + { 0x895a, 0x0000 }, + { 0x895b, 0x0000 }, + { 0x895c, 0x0000 }, + { 0x895d, 0x0000 }, + { 0x895e, 0x0000 }, + { 0x895f, 0x0000 }, + { 0x8960, 0x0000 }, + { 0x8961, 0x0000 }, + { 0x8962, 0x0000 }, + { 0x8963, 0x0000 }, + { 0x8964, 0x0000 }, + { 0x8965, 0x0000 }, + { 0x8966, 0x0000 }, + { 0x8967, 0x0000 }, + { 0x8968, 0x0000 }, + { 0x8969, 0x0000 }, + { 0x896a, 0x0000 }, + { 0x896b, 0x0000 }, + { 0x896c, 0x0000 }, + { 0x896d, 0x0000 }, + { 0x896e, 0x0000 }, + { 0x896f, 0x0000 }, + { 0x8970, 0x0000 }, + { 0x8971, 0x0000 }, + { 0x8972, 0x0000 }, + { 0x8973, 0x0000 }, + { 0x8974, 0x0000 }, + { 0x8975, 0x0000 }, + { 0x8976, 0x0000 }, + { 0x8977, 0x0000 }, + { 0x8978, 0x0000 }, + { 0x8979, 0x0000 }, + { 0x897a, 0x0000 }, + { 0x897b, 0x0000 }, + { 0x897c, 0x0000 }, + { 0x897d, 0x0000 }, + { 0x897e, 0x0000 }, + { 0x897f, 0x0000 }, + { 0x8980, 0x0000 }, + { 0x8981, 0x0000 }, + { 0x8982, 0x0000 }, + { 0x8983, 0x0000 }, + { 0x8984, 0x0000 }, + { 0x8985, 0x0000 }, + { 0x8986, 0x0000 }, + { 0x8987, 0x0000 }, + { 0x8988, 0x0000 }, + { 0x8989, 0x0000 }, + { 0x898a, 0x0000 }, + { 0x898b, 0x0000 }, + { 0x898c, 0x0000 }, + { 0x898d, 0x0000 }, + { 0x898e, 0x0000 }, + { 0x898f, 0x0000 }, + { 0x8990, 0x0000 }, + { 0x8991, 0x0000 }, + { 0x8992, 0x0000 }, + { 0x8993, 0x0000 }, + { 0x8994, 0x0000 }, + { 0x8995, 0x0000 }, + { 0x8996, 0x0000 }, + { 0x8997, 0x0000 }, + { 0x8998, 0x0000 }, + { 0x8999, 0x0000 }, + { 0x899a, 0x0000 }, + { 0x899b, 0x0000 }, + { 0x899c, 0x0000 }, + { 0x899d, 0x0000 }, + { 0x899e, 0x0000 }, + { 0x899f, 0x0000 }, + { 0x89a0, 0x0000 }, + { 0x89a1, 0x0000 }, + { 0x89a2, 0x0000 }, + { 0x89a3, 0x0000 }, + { 0x89a4, 0x0000 }, + { 0x89a5, 0x0000 }, + { 0x89a6, 0x0000 }, + { 0x89a7, 0x0000 }, + { 0x89a8, 0x0000 }, + { 0x89a9, 0x0000 }, + { 0x89aa, 0x0000 }, + { 0x89ab, 0x0000 }, + { 0x89ac, 0x0000 }, + { 0x89ad, 0x0000 }, + { 0x89ae, 0x0000 }, + { 0x89af, 0x0000 }, + { 0x89b0, 0x0000 }, + { 0x89b1, 0x0000 }, + { 0x89b2, 0x0000 }, + { 0x89b3, 0x0000 }, + { 0x89b4, 0x0000 }, + { 0x89b5, 0x0000 }, + { 0x89b6, 0x0000 }, + { 0x89b7, 0x0000 }, + { 0x89b8, 0x0000 }, + { 0x89b9, 0x0000 }, + { 0x89ba, 0x0000 }, + { 0x89bb, 0x0000 }, + { 0x89bc, 0x0000 }, + { 0x89bd, 0x0000 }, + { 0x89be, 0x0000 }, + { 0x89bf, 0x0000 }, + { 0x89c0, 0x0000 }, + { 0x89c1, 0x0000 }, + { 0x89c2, 0x0000 }, + { 0x89c3, 0x0000 }, + { 0x89c4, 0x0000 }, + { 0x89c5, 0x0000 }, + { 0x89c6, 0x0000 }, + { 0x89c7, 0x0000 }, + { 0x89c8, 0x0000 }, + { 0x89c9, 0x0000 }, + { 0x89ca, 0x0000 }, + { 0x89cb, 0x0000 }, + { 0x89cc, 0x0000 }, + { 0x89cd, 0x0000 }, + { 0x89ce, 0x0000 }, + { 0x89cf, 0x0000 }, + { 0x89d0, 0x0000 }, + { 0x89d1, 0x0000 }, + { 0x89d2, 0x0000 }, + { 0x89d3, 0x0000 }, + { 0x89d4, 0x0000 }, + { 0x89d5, 0x0000 }, + { 0x89d6, 0x0000 }, + { 0x89d7, 0x0000 }, + { 0x89d8, 0x0000 }, + { 0x89d9, 0x0000 }, + { 0x89da, 0x0000 }, + { 0x89db, 0x0000 }, + { 0x89dc, 0x0000 }, + { 0x89dd, 0x0000 }, + { 0x89de, 0x0000 }, + { 0x89df, 0x0000 }, + { 0x89e0, 0x0000 }, + { 0x89e1, 0x0000 }, + { 0x89e2, 0x0000 }, + { 0x89e3, 0x0000 }, + { 0x89e4, 0x0000 }, + { 0x89e5, 0x0000 }, + { 0x89e6, 0x0000 }, + { 0x89e7, 0x0000 }, + { 0x89e8, 0x0000 }, + { 0x89e9, 0x0000 }, + { 0x89ea, 0x0000 }, + { 0x89eb, 0x0000 }, + { 0x89ec, 0x0000 }, + { 0x89ed, 0x0000 }, + { 0x89ee, 0x0000 }, + { 0x89ef, 0x0000 }, + { 0x89f0, 0x0000 }, + { 0x89f1, 0x0000 }, + { 0x89f2, 0x0000 }, + { 0x89f3, 0x0000 }, + { 0x89f4, 0x0000 }, + { 0x89f5, 0x0000 }, + { 0x89f6, 0x0000 }, + { 0x89f7, 0x0000 }, + { 0x89f8, 0x0000 }, + { 0x89f9, 0x0000 }, + { 0x89fa, 0x0000 }, + { 0x89fb, 0x0000 }, + { 0x89fc, 0x0000 }, + { 0x89fd, 0x0000 }, + { 0x89fe, 0x0000 }, + { 0x89ff, 0x0000 }, + { 0x8a00, 0x0000 }, + { 0x8a01, 0x0000 }, + { 0x8a02, 0x0000 }, + { 0x8a03, 0x0000 }, + { 0x8a04, 0x0000 }, + { 0x8a05, 0x0000 }, + { 0x8a06, 0x0000 }, + { 0x8a07, 0x0000 }, + { 0x8a08, 0x0000 }, + { 0x8a09, 0x0000 }, + { 0x8a0a, 0x0000 }, + { 0x8a0b, 0x0000 }, + { 0x8a0c, 0x0000 }, + { 0x8a0d, 0x0000 }, + { 0x8a0e, 0x0000 }, + { 0x8a0f, 0x0000 }, + { 0x8a10, 0x0000 }, + { 0x8a11, 0x0000 }, + { 0x8a12, 0x0000 }, + { 0x8a13, 0x0000 }, + { 0x8a14, 0x0000 }, + { 0x8a15, 0x0000 }, + { 0x8a16, 0x0000 }, + { 0x8a17, 0x0000 }, + { 0x8a18, 0x0000 }, + { 0x8a19, 0x0000 }, + { 0x8a1a, 0x0000 }, + { 0x8a1b, 0x0000 }, + { 0x8a1c, 0x0000 }, + { 0x8a1d, 0x0000 }, + { 0x8a1e, 0x0000 }, + { 0x8a1f, 0x0000 }, + { 0x8a20, 0x0000 }, + { 0x8a21, 0x0000 }, + { 0x8a22, 0x0000 }, + { 0x8a23, 0x0000 }, + { 0x8a24, 0x0000 }, + { 0x8a25, 0x0000 }, + { 0x8a26, 0x0000 }, + { 0x8a27, 0x0000 }, + { 0x8a28, 0x0000 }, + { 0x8a29, 0x0000 }, + { 0x8a2a, 0x0000 }, + { 0x8a2b, 0x0000 }, + { 0x8a2c, 0x0000 }, + { 0x8a2d, 0x0000 }, + { 0x8a2e, 0x0000 }, + { 0x8a2f, 0x0000 }, + { 0x8a30, 0x0000 }, + { 0x8a31, 0x0000 }, + { 0x8a32, 0x0000 }, + { 0x8a33, 0x0000 }, + { 0x8a34, 0x0000 }, + { 0x8a35, 0x0000 }, + { 0x8a36, 0x0000 }, + { 0x8a37, 0x0000 }, + { 0x8a38, 0x0000 }, + { 0x8a39, 0x0000 }, + { 0x8a3a, 0x0000 }, + { 0x8a3b, 0x0000 }, + { 0x8a3c, 0x0000 }, + { 0x8a3d, 0x0000 }, + { 0x8a3e, 0x0000 }, + { 0x8a3f, 0x0000 }, + { 0x8a40, 0x0000 }, + { 0x8a41, 0x0000 }, + { 0x8a42, 0x0000 }, + { 0x8a43, 0x0000 }, + { 0x8a44, 0x0000 }, + { 0x8a45, 0x0000 }, + { 0x8a46, 0x0000 }, + { 0x8a47, 0x0000 }, + { 0x8a48, 0x0000 }, + { 0x8a49, 0x0000 }, + { 0x8a4a, 0x0000 }, + { 0x8a4b, 0x0000 }, + { 0x8a4c, 0x0000 }, + { 0x8a4d, 0x0000 }, + { 0x8a4e, 0x0000 }, + { 0x8a4f, 0x0000 }, + { 0x8a50, 0x0000 }, + { 0x8a51, 0x0000 }, + { 0x8a52, 0x0000 }, + { 0x8a53, 0x0000 }, + { 0x8a54, 0x0000 }, + { 0x8a55, 0x0000 }, + { 0x8a56, 0x0000 }, + { 0x8a57, 0x0000 }, + { 0x8a58, 0x0000 }, + { 0x8a59, 0x0000 }, + { 0x8a5a, 0x0000 }, + { 0x8a5b, 0x0000 }, + { 0x8a5c, 0x0000 }, + { 0x8a5d, 0x0000 }, + { 0x8a5e, 0x0000 }, + { 0x8a5f, 0x0000 }, + { 0x8a60, 0x0000 }, + { 0x8a61, 0x0000 }, + { 0x8a62, 0x0000 }, + { 0x8a63, 0x0000 }, + { 0x8a64, 0x0000 }, + { 0x8a65, 0x0000 }, + { 0x8a66, 0x0000 }, + { 0x8a67, 0x0000 }, + { 0x8a68, 0x0000 }, + { 0x8a69, 0x0000 }, + { 0x8a6a, 0x0000 }, + { 0x8a6b, 0x0000 }, + { 0x8a6c, 0x0000 }, + { 0x8a6d, 0x0000 }, + { 0x8a6e, 0x0000 }, + { 0x8a6f, 0x0000 }, + { 0x8a70, 0x0000 }, + { 0x8a71, 0x0000 }, + { 0x8a72, 0x0000 }, + { 0x8a73, 0x0000 }, + { 0x8a74, 0x0000 }, + { 0x8a75, 0x0000 }, + { 0x8a76, 0x0000 }, + { 0x8a77, 0x0000 }, + { 0x8a78, 0x0000 }, + { 0x8a79, 0x0000 }, + { 0x8a7a, 0x0000 }, + { 0x8a7b, 0x0000 }, + { 0x8a7c, 0x0000 }, + { 0x8a7d, 0x0000 }, + { 0x8a7e, 0x0000 }, + { 0x8a7f, 0x0000 }, + { 0x8a80, 0x0000 }, + { 0x8a81, 0x0000 }, + { 0x8a82, 0x0000 }, + { 0x8a83, 0x0000 }, + { 0x8a84, 0x0000 }, + { 0x8a85, 0x0000 }, + { 0x8a86, 0x0000 }, + { 0x8a87, 0x0000 }, + { 0x8a88, 0x0000 }, + { 0x8a89, 0x0000 }, + { 0x8a8a, 0x0000 }, + { 0x8a8b, 0x0000 }, + { 0x8a8c, 0x0000 }, + { 0x8a8d, 0x0000 }, + { 0x8a8e, 0x0000 }, + { 0x8a8f, 0x0000 }, + { 0x8a90, 0x0000 }, + { 0x8a91, 0x0000 }, + { 0x8a92, 0x0000 }, + { 0x8a93, 0x0000 }, + { 0x8a94, 0x0000 }, + { 0x8a95, 0x0000 }, + { 0x8a96, 0x0000 }, + { 0x8a97, 0x0000 }, + { 0x8a98, 0x0000 }, + { 0x8a99, 0x0000 }, + { 0x8a9a, 0x0000 }, + { 0x8a9b, 0x0000 }, + { 0x8a9c, 0x0000 }, + { 0x8a9d, 0x0000 }, + { 0x8a9e, 0x0000 }, + { 0x8a9f, 0x0000 }, + { 0x8aa0, 0x0000 }, + { 0x8aa1, 0x0000 }, + { 0x8aa2, 0x0000 }, + { 0x8aa3, 0x0000 }, + { 0x8aa4, 0x0000 }, + { 0x8aa5, 0x0000 }, + { 0x8aa6, 0x0000 }, + { 0x8aa7, 0x0000 }, + { 0x8aa8, 0x0000 }, + { 0x8aa9, 0x0000 }, + { 0x8aaa, 0x0000 }, + { 0x8aab, 0x0000 }, + { 0x8aac, 0x0000 }, + { 0x8aad, 0x0000 }, + { 0x8aae, 0x0000 }, + { 0x8aaf, 0x0000 }, + { 0x8ab0, 0x0000 }, + { 0x8ab1, 0x0000 }, + { 0x8ab2, 0x0000 }, + { 0x8ab3, 0x0000 }, + { 0x8ab4, 0x0000 }, + { 0x8ab5, 0x0000 }, + { 0x8ab6, 0x0000 }, + { 0x8ab7, 0x0000 }, + { 0x8ab8, 0x0000 }, + { 0x8ab9, 0x0000 }, + { 0x8aba, 0x0000 }, + { 0x8abb, 0x0000 }, + { 0x8abc, 0x0000 }, + { 0x8abd, 0x0000 }, + { 0x8abe, 0x0000 }, + { 0x8abf, 0x0000 }, + { 0x8ac0, 0x0000 }, + { 0x8ac1, 0x0000 }, + { 0x8ac2, 0x0000 }, + { 0x8ac3, 0x0000 }, + { 0x8ac4, 0x0000 }, + { 0x8ac5, 0x0000 }, + { 0x8ac6, 0x0000 }, + { 0x8ac7, 0x0000 }, + { 0x8ac8, 0x0000 }, + { 0x8ac9, 0x0000 }, + { 0x8aca, 0x0000 }, + { 0x8acb, 0x0000 }, + { 0x8acc, 0x0000 }, + { 0x8acd, 0x0000 }, + { 0x8ace, 0x0000 }, + { 0x8acf, 0x0000 }, + { 0x8ad0, 0x0000 }, + { 0x8ad1, 0x0000 }, + { 0x8ad2, 0x0000 }, + { 0x8ad3, 0x0000 }, + { 0x8ad4, 0x0000 }, + { 0x8ad5, 0x0000 }, + { 0x8ad6, 0x0000 }, + { 0x8ad7, 0x0000 }, + { 0x8ad8, 0x0000 }, + { 0x8ad9, 0x0000 }, + { 0x8ada, 0x0000 }, + { 0x8adb, 0x0000 }, + { 0x8adc, 0x0000 }, + { 0x8add, 0x0000 }, + { 0x8ade, 0x0000 }, + { 0x8adf, 0x0000 }, + { 0x8ae0, 0x0000 }, + { 0x8ae1, 0x0000 }, + { 0x8ae2, 0x0000 }, + { 0x8ae3, 0x0000 }, + { 0x8ae4, 0x0000 }, + { 0x8ae5, 0x0000 }, + { 0x8ae6, 0x0000 }, + { 0x8ae7, 0x0000 }, + { 0x8ae8, 0x0000 }, + { 0x8ae9, 0x0000 }, + { 0x8aea, 0x0000 }, + { 0x8aeb, 0x0000 }, + { 0x8aec, 0x0000 }, + { 0x8aed, 0x0000 }, + { 0x8aee, 0x0000 }, + { 0x8aef, 0x0000 }, + { 0x8af0, 0x0000 }, + { 0x8af1, 0x0000 }, + { 0x8af2, 0x0000 }, + { 0x8af3, 0x0000 }, + { 0x8af4, 0x0000 }, + { 0x8af5, 0x0000 }, + { 0x8af6, 0x0000 }, + { 0x8af7, 0x0000 }, + { 0x8af8, 0x0000 }, + { 0x8af9, 0x0000 }, + { 0x8afa, 0x0000 }, + { 0x8afb, 0x0000 }, + { 0x8afc, 0x0000 }, + { 0x8afd, 0x0000 }, + { 0x8afe, 0x0000 }, + { 0x8aff, 0x0000 }, + { 0x8b00, 0x0000 }, + { 0x8b01, 0x0000 }, + { 0x8b02, 0x0000 }, + { 0x8b03, 0x0000 }, + { 0x8b04, 0x0000 }, + { 0x8b05, 0x0000 }, + { 0x8b06, 0x0000 }, + { 0x8b07, 0x0000 }, + { 0x8b08, 0x0000 }, + { 0x8b09, 0x0000 }, + { 0x8b0a, 0x0000 }, + { 0x8b0b, 0x0000 }, + { 0x8b0c, 0x0000 }, + { 0x8b0d, 0x0000 }, + { 0x8b0e, 0x0000 }, + { 0x8b0f, 0x0000 }, + { 0x8b10, 0x0000 }, + { 0x8b11, 0x0000 }, + { 0x8b12, 0x0000 }, + { 0x8b13, 0x0000 }, + { 0x8b14, 0x0000 }, + { 0x8b15, 0x0000 }, + { 0x8b16, 0x0000 }, + { 0x8b17, 0x0000 }, + { 0x8b18, 0x0000 }, + { 0x8b19, 0x0000 }, + { 0x8b1a, 0x0000 }, + { 0x8b1b, 0x0000 }, + { 0x8b1c, 0x0000 }, + { 0x8b1d, 0x0000 }, + { 0x8b1e, 0x0000 }, + { 0x8b1f, 0x0000 }, + { 0x8b20, 0x0000 }, + { 0x8b21, 0x0000 }, + { 0x8b22, 0x0000 }, + { 0x8b23, 0x0000 }, + { 0x8b24, 0x0000 }, + { 0x8b25, 0x0000 }, + { 0x8b26, 0x0000 }, + { 0x8b27, 0x0000 }, + { 0x8b28, 0x0000 }, + { 0x8b29, 0x0000 }, + { 0x8b2a, 0x0000 }, + { 0x8b2b, 0x0000 }, + { 0x8b2c, 0x0000 }, + { 0x8b2d, 0x0000 }, + { 0x8b2e, 0x0000 }, + { 0x8b2f, 0x0000 }, + { 0x8b30, 0x0000 }, + { 0x8b31, 0x0000 }, + { 0x8b32, 0x0000 }, + { 0x8b33, 0x0000 }, + { 0x8b34, 0x0000 }, + { 0x8b35, 0x0000 }, + { 0x8b36, 0x0000 }, + { 0x8b37, 0x0000 }, + { 0x8b38, 0x0000 }, + { 0x8b39, 0x0000 }, + { 0x8b3a, 0x0000 }, + { 0x8b3b, 0x0000 }, + { 0x8b3c, 0x0000 }, + { 0x8b3d, 0x0000 }, + { 0x8b3e, 0x0000 }, + { 0x8b3f, 0x0000 }, + { 0x8b40, 0x0000 }, + { 0x8b41, 0x0000 }, + { 0x8b42, 0x0000 }, + { 0x8b43, 0x0000 }, + { 0x8b44, 0x0000 }, + { 0x8b45, 0x0000 }, + { 0x8b46, 0x0000 }, + { 0x8b47, 0x0000 }, + { 0x8b48, 0x0000 }, + { 0x8b49, 0x0000 }, + { 0x8b4a, 0x0000 }, + { 0x8b4b, 0x0000 }, + { 0x8b4c, 0x0000 }, + { 0x8b4d, 0x0000 }, + { 0x8b4e, 0x0000 }, + { 0x8b4f, 0x0000 }, + { 0x8b50, 0x0000 }, + { 0x8b51, 0x0000 }, + { 0x8b52, 0x0000 }, + { 0x8b53, 0x0000 }, + { 0x8b54, 0x0000 }, + { 0x8b55, 0x0000 }, + { 0x8b56, 0x0000 }, + { 0x8b57, 0x0000 }, + { 0x8b58, 0x0000 }, + { 0x8b59, 0x0000 }, + { 0x8b5a, 0x0000 }, + { 0x8b5b, 0x0000 }, + { 0x8b5c, 0x0000 }, + { 0x8b5d, 0x0000 }, + { 0x8b5e, 0x0000 }, + { 0x8b5f, 0x0000 }, + { 0x8b60, 0x0000 }, + { 0x8b61, 0x0000 }, + { 0x8b62, 0x0000 }, + { 0x8b63, 0x0000 }, + { 0x8b64, 0x0000 }, + { 0x8b65, 0x0000 }, + { 0x8b66, 0x0000 }, + { 0x8b67, 0x0000 }, + { 0x8b68, 0x0000 }, + { 0x8b69, 0x0000 }, + { 0x8b6a, 0x0000 }, + { 0x8b6b, 0x0000 }, + { 0x8b6c, 0x0000 }, + { 0x8b6d, 0x0000 }, + { 0x8b6e, 0x0000 }, + { 0x8b6f, 0x0000 }, + { 0x8b70, 0x0000 }, + { 0x8b71, 0x0000 }, + { 0x8b72, 0x0000 }, + { 0x8b73, 0x0000 }, + { 0x8b74, 0x0000 }, + { 0x8b75, 0x0000 }, + { 0x8b76, 0x0000 }, + { 0x8b77, 0x0000 }, + { 0x8b78, 0x0000 }, + { 0x8b79, 0x0000 }, + { 0x8b7a, 0x0000 }, + { 0x8b7b, 0x0000 }, + { 0x8b7c, 0x0000 }, + { 0x8b7d, 0x0000 }, + { 0x8b7e, 0x0000 }, + { 0x8b7f, 0x0000 }, + { 0x8b80, 0x0000 }, + { 0x8b81, 0x0000 }, + { 0x8b82, 0x0000 }, + { 0x8b83, 0x0000 }, + { 0x8b84, 0x0000 }, + { 0x8b85, 0x0000 }, + { 0x8b86, 0x0000 }, + { 0x8b87, 0x0000 }, + { 0x8b88, 0x0000 }, + { 0x8b89, 0x0000 }, + { 0x8b8a, 0x0000 }, + { 0x8b8b, 0x0000 }, + { 0x8b8c, 0x0000 }, + { 0x8b8d, 0x0000 }, + { 0x8b8e, 0x0000 }, + { 0x8b8f, 0x0000 }, + { 0x8b90, 0x0000 }, + { 0x8b91, 0x0000 }, + { 0x8b92, 0x0000 }, + { 0x8b93, 0x0000 }, + { 0x8b94, 0x0000 }, + { 0x8b95, 0x0000 }, + { 0x8b96, 0x0000 }, + { 0x8b97, 0x0000 }, + { 0x8b98, 0x0000 }, + { 0x8b99, 0x0000 }, + { 0x8b9a, 0x0000 }, + { 0x8b9b, 0x0000 }, + { 0x8b9c, 0x0000 }, + { 0x8b9d, 0x0000 }, + { 0x8b9e, 0x0000 }, + { 0x8b9f, 0x0000 }, + { 0x8ba0, 0x0000 }, + { 0x8ba1, 0x0000 }, + { 0x8ba2, 0x0000 }, + { 0x8ba3, 0x0000 }, + { 0x8ba4, 0x0000 }, + { 0x8ba5, 0x0000 }, + { 0x8ba6, 0x0000 }, + { 0x8ba7, 0x0000 }, + { 0x8ba8, 0x0000 }, + { 0x8ba9, 0x0000 }, + { 0x8baa, 0x0000 }, + { 0x8bab, 0x0000 }, + { 0x8bac, 0x0000 }, + { 0x8bad, 0x0000 }, + { 0x8bae, 0x0000 }, + { 0x8baf, 0x0000 }, + { 0x8bb0, 0x0000 }, + { 0x8bb1, 0x0000 }, + { 0x8bb2, 0x0000 }, + { 0x8bb3, 0x0000 }, + { 0x8bb4, 0x0000 }, + { 0x8bb5, 0x0000 }, + { 0x8bb6, 0x0000 }, + { 0x8bb7, 0x0000 }, + { 0x8bb8, 0x0000 }, + { 0x8bb9, 0x0000 }, + { 0x8bba, 0x0000 }, + { 0x8bbb, 0x0000 }, + { 0x8bbc, 0x0000 }, + { 0x8bbd, 0x0000 }, + { 0x8bbe, 0x0000 }, + { 0x8bbf, 0x0000 }, + { 0x8bc0, 0x0000 }, + { 0x8bc1, 0x0000 }, + { 0x8bc2, 0x0000 }, + { 0x8bc3, 0x0000 }, + { 0x8bc4, 0x0000 }, + { 0x8bc5, 0x0000 }, + { 0x8bc6, 0x0000 }, + { 0x8bc7, 0x0000 }, + { 0x8bc8, 0x0000 }, + { 0x8bc9, 0x0000 }, + { 0x8bca, 0x0000 }, + { 0x8bcb, 0x0000 }, + { 0x8bcc, 0x0000 }, + { 0x8bcd, 0x0000 }, + { 0x8bce, 0x0000 }, + { 0x8bcf, 0x0000 }, + { 0x8bd0, 0x0000 }, + { 0x8bd1, 0x0000 }, + { 0x8bd2, 0x0000 }, + { 0x8bd3, 0x0000 }, + { 0x8bd4, 0x0000 }, + { 0x8bd5, 0x0000 }, + { 0x8bd6, 0x0000 }, + { 0x8bd7, 0x0000 }, + { 0x8bd8, 0x0000 }, + { 0x8bd9, 0x0000 }, + { 0x8bda, 0x0000 }, + { 0x8bdb, 0x0000 }, + { 0x8bdc, 0x0000 }, + { 0x8bdd, 0x0000 }, + { 0x8bde, 0x0000 }, + { 0x8bdf, 0x0000 }, + { 0x8be0, 0x0000 }, + { 0x8be1, 0x0000 }, + { 0x8be2, 0x0000 }, + { 0x8be3, 0x0000 }, + { 0x8be4, 0x0000 }, + { 0x8be5, 0x0000 }, + { 0x8be6, 0x0000 }, + { 0x8be7, 0x0000 }, + { 0x8be8, 0x0000 }, + { 0x8be9, 0x0000 }, + { 0x8bea, 0x0000 }, + { 0x8beb, 0x0000 }, + { 0x8bec, 0x0000 }, + { 0x8bed, 0x0000 }, + { 0x8bee, 0x0000 }, + { 0x8bef, 0x0000 }, + { 0x8bf0, 0x0000 }, + { 0x8bf1, 0x0000 }, + { 0x8bf2, 0x0000 }, + { 0x8bf3, 0x0000 }, + { 0x8bf4, 0x0000 }, + { 0x8bf5, 0x0000 }, + { 0x8bf6, 0x0000 }, + { 0x8bf7, 0x0000 }, + { 0x8bf8, 0x0000 }, + { 0x8bf9, 0x0000 }, + { 0x8bfa, 0x0000 }, + { 0x8bfb, 0x0000 }, + { 0x8bfc, 0x0000 }, + { 0x8bfd, 0x0000 }, + { 0x8bfe, 0x0000 }, + { 0x8bff, 0x0000 }, + { 0x8c00, 0x0000 }, + { 0x8c01, 0x0000 }, + { 0x8c02, 0x0000 }, + { 0x8c03, 0x0000 }, + { 0x8c04, 0x0000 }, + { 0x8c05, 0x0000 }, + { 0x8c06, 0x0000 }, + { 0x8c07, 0x0000 }, + { 0x8c08, 0x0000 }, + { 0x8c09, 0x0000 }, + { 0x8c0a, 0x0000 }, + { 0x8c0b, 0x0000 }, + { 0x8c0c, 0x0000 }, + { 0x8c0d, 0x0000 }, + { 0x8c0e, 0x0000 }, + { 0x8c0f, 0x0000 }, + { 0x8c10, 0x0000 }, + { 0x8c11, 0x0000 }, + { 0x8c12, 0x0000 }, + { 0x8c13, 0x0000 }, + { 0x8c14, 0x0000 }, + { 0x8c15, 0x0000 }, + { 0x8c16, 0x0000 }, + { 0x8c17, 0x0000 }, + { 0x8c18, 0x0000 }, + { 0x8c19, 0x0000 }, + { 0x8c1a, 0x0000 }, + { 0x8c1b, 0x0000 }, + { 0x8c1c, 0x0000 }, + { 0x8c1d, 0x0000 }, + { 0x8c1e, 0x0000 }, + { 0x8c1f, 0x0000 }, + { 0x8c20, 0x0000 }, + { 0x8c21, 0x0000 }, + { 0x8c22, 0x0000 }, + { 0x8c23, 0x0000 }, + { 0x8c24, 0x0000 }, + { 0x8c25, 0x0000 }, + { 0x8c26, 0x0000 }, + { 0x8c27, 0x0000 }, + { 0x8c28, 0x0000 }, + { 0x8c29, 0x0000 }, + { 0x8c2a, 0x0000 }, + { 0x8c2b, 0x0000 }, + { 0x8c2c, 0x0000 }, + { 0x8c2d, 0x0000 }, + { 0x8c2e, 0x0000 }, + { 0x8c2f, 0x0000 }, + { 0x8c30, 0x0000 }, + { 0x8c31, 0x0000 }, + { 0x8c32, 0x0000 }, + { 0x8c33, 0x0000 }, + { 0x8c34, 0x0000 }, + { 0x8c35, 0x0000 }, + { 0x8c36, 0x0000 }, + { 0x8c37, 0x0000 }, + { 0x8c38, 0x0000 }, + { 0x8c39, 0x0000 }, + { 0x8c3a, 0x0000 }, + { 0x8c3b, 0x0000 }, + { 0x8c3c, 0x0000 }, + { 0x8c3d, 0x0000 }, + { 0x8c3e, 0x0000 }, + { 0x8c3f, 0x0000 }, + { 0x8c40, 0x0000 }, + { 0x8c41, 0x0000 }, + { 0x8c42, 0x0000 }, + { 0x8c43, 0x0000 }, + { 0x8c44, 0x0000 }, + { 0x8c45, 0x0000 }, + { 0x8c46, 0x0000 }, + { 0x8c47, 0x0000 }, + { 0x8c48, 0x0000 }, + { 0x8c49, 0x0000 }, + { 0x8c4a, 0x0000 }, + { 0x8c4b, 0x0000 }, + { 0x8c4c, 0x0000 }, + { 0x8c4d, 0x0000 }, + { 0x8c4e, 0x0000 }, + { 0x8c4f, 0x0000 }, + { 0x8c50, 0x0000 }, + { 0x8c51, 0x0000 }, + { 0x8c52, 0x0000 }, + { 0x8c53, 0x0000 }, + { 0x8c54, 0x0000 }, + { 0x8c55, 0x0000 }, + { 0x8c56, 0x0000 }, + { 0x8c57, 0x0000 }, + { 0x8c58, 0x0000 }, + { 0x8c59, 0x0000 }, + { 0x8c5a, 0x0000 }, + { 0x8c5b, 0x0000 }, + { 0x8c5c, 0x0000 }, + { 0x8c5d, 0x0000 }, + { 0x8c5e, 0x0000 }, + { 0x8c5f, 0x0000 }, + { 0x8c60, 0x0000 }, + { 0x8c61, 0x0000 }, + { 0x8c62, 0x0000 }, + { 0x8c63, 0x0000 }, + { 0x8c64, 0x0000 }, + { 0x8c65, 0x0000 }, + { 0x8c66, 0x0000 }, + { 0x8c67, 0x0000 }, + { 0x8c68, 0x0000 }, + { 0x8c69, 0x0000 }, + { 0x8c6a, 0x0000 }, + { 0x8c6b, 0x0000 }, + { 0x8c6c, 0x0000 }, + { 0x8c6d, 0x0000 }, + { 0x8c6e, 0x0000 }, + { 0x8c6f, 0x0000 }, + { 0x8c70, 0x0000 }, + { 0x8c71, 0x0000 }, + { 0x8c72, 0x0000 }, + { 0x8c73, 0x0000 }, + { 0x8c74, 0x0000 }, + { 0x8c75, 0x0000 }, + { 0x8c76, 0x0000 }, + { 0x8c77, 0x0000 }, + { 0x8c78, 0x0000 }, + { 0x8c79, 0x0000 }, + { 0x8c7a, 0x0000 }, + { 0x8c7b, 0x0000 }, + { 0x8c7c, 0x0000 }, + { 0x8c7d, 0x0000 }, + { 0x8c7e, 0x0000 }, + { 0x8c7f, 0x0000 }, + { 0x8c80, 0x0000 }, + { 0x8c81, 0x0000 }, + { 0x8c82, 0x0000 }, + { 0x8c83, 0x0000 }, + { 0x8c84, 0x0000 }, + { 0x8c85, 0x0000 }, + { 0x8c86, 0x0000 }, + { 0x8c87, 0x0000 }, + { 0x8c88, 0x0000 }, + { 0x8c89, 0x0000 }, + { 0x8c8a, 0x0000 }, + { 0x8c8b, 0x0000 }, + { 0x8c8c, 0x0000 }, + { 0x8c8d, 0x0000 }, + { 0x8c8e, 0x0000 }, + { 0x8c8f, 0x0000 }, + { 0x8c90, 0x0000 }, + { 0x8c91, 0x0000 }, + { 0x8c92, 0x0000 }, + { 0x8c93, 0x0000 }, + { 0x8c94, 0x0000 }, + { 0x8c95, 0x0000 }, + { 0x8c96, 0x0000 }, + { 0x8c97, 0x0000 }, + { 0x8c98, 0x0000 }, + { 0x8c99, 0x0000 }, + { 0x8c9a, 0x0000 }, + { 0x8c9b, 0x0000 }, + { 0x8c9c, 0x0000 }, + { 0x8c9d, 0x0000 }, + { 0x8c9e, 0x0000 }, + { 0x8c9f, 0x0000 }, + { 0x8ca0, 0x0000 }, + { 0x8ca1, 0x0000 }, + { 0x8ca2, 0x0000 }, + { 0x8ca3, 0x0000 }, + { 0x8ca4, 0x0000 }, + { 0x8ca5, 0x0000 }, + { 0x8ca6, 0x0000 }, + { 0x8ca7, 0x0000 }, + { 0x8ca8, 0x0000 }, + { 0x8ca9, 0x0000 }, + { 0x8caa, 0x0000 }, + { 0x8cab, 0x0000 }, + { 0x8cac, 0x0000 }, + { 0x8cad, 0x0000 }, + { 0x8cae, 0x0000 }, + { 0x8caf, 0x0000 }, + { 0x8cb0, 0x0000 }, + { 0x8cb1, 0x0000 }, + { 0x8cb2, 0x0000 }, + { 0x8cb3, 0x0000 }, + { 0x8cb4, 0x0000 }, + { 0x8cb5, 0x0000 }, + { 0x8cb6, 0x0000 }, + { 0x8cb7, 0x0000 }, + { 0x8cb8, 0x0000 }, + { 0x8cb9, 0x0000 }, + { 0x8cba, 0x0000 }, + { 0x8cbb, 0x0000 }, + { 0x8cbc, 0x0000 }, + { 0x8cbd, 0x0000 }, + { 0x8cbe, 0x0000 }, + { 0x8cbf, 0x0000 }, + { 0x8cc0, 0x0000 }, + { 0x8cc1, 0x0000 }, + { 0x8cc2, 0x0000 }, + { 0x8cc3, 0x0000 }, + { 0x8cc4, 0x0000 }, + { 0x8cc5, 0x0000 }, + { 0x8cc6, 0x0000 }, + { 0x8cc7, 0x0000 }, + { 0x8cc8, 0x0000 }, + { 0x8cc9, 0x0000 }, + { 0x8cca, 0x0000 }, + { 0x8ccb, 0x0000 }, + { 0x8ccc, 0x0000 }, + { 0x8ccd, 0x0000 }, + { 0x8cce, 0x0000 }, + { 0x8ccf, 0x0000 }, + { 0x8cd0, 0x0000 }, + { 0x8cd1, 0x0000 }, + { 0x8cd2, 0x0000 }, + { 0x8cd3, 0x0000 }, + { 0x8cd4, 0x0000 }, + { 0x8cd5, 0x0000 }, + { 0x8cd6, 0x0000 }, + { 0x8cd7, 0x0000 }, + { 0x8cd8, 0x0000 }, + { 0x8cd9, 0x0000 }, + { 0x8cda, 0x0000 }, + { 0x8cdb, 0x0000 }, + { 0x8cdc, 0x0000 }, + { 0x8cdd, 0x0000 }, + { 0x8cde, 0x0000 }, + { 0x8cdf, 0x0000 }, + { 0x8ce0, 0x0000 }, + { 0x8ce1, 0x0000 }, + { 0x8ce2, 0x0000 }, + { 0x8ce3, 0x0000 }, + { 0x8ce4, 0x0000 }, + { 0x8ce5, 0x0000 }, + { 0x8ce6, 0x0000 }, + { 0x8ce7, 0x0000 }, + { 0x8ce8, 0x0000 }, + { 0x8ce9, 0x0000 }, + { 0x8cea, 0x0000 }, + { 0x8ceb, 0x0000 }, + { 0x8cec, 0x0000 }, + { 0x8ced, 0x0000 }, + { 0x8cee, 0x0000 }, + { 0x8cef, 0x0000 }, + { 0x8cf0, 0x0000 }, + { 0x8cf1, 0x0000 }, + { 0x8cf2, 0x0000 }, + { 0x8cf3, 0x0000 }, + { 0x8cf4, 0x0000 }, + { 0x8cf5, 0x0000 }, + { 0x8cf6, 0x0000 }, + { 0x8cf7, 0x0000 }, + { 0x8cf8, 0x0000 }, + { 0x8cf9, 0x0000 }, + { 0x8cfa, 0x0000 }, + { 0x8cfb, 0x0000 }, + { 0x8cfc, 0x0000 }, + { 0x8cfd, 0x0000 }, + { 0x8cfe, 0x0000 }, + { 0x8cff, 0x0000 }, + { 0x8d00, 0x0000 }, + { 0x8d01, 0x0000 }, + { 0x8d02, 0x0000 }, + { 0x8d03, 0x0000 }, + { 0x8d04, 0x0000 }, + { 0x8d05, 0x0000 }, + { 0x8d06, 0x0000 }, + { 0x8d07, 0x0000 }, + { 0x8d08, 0x0000 }, + { 0x8d09, 0x0000 }, + { 0x8d0a, 0x0000 }, + { 0x8d0b, 0x0000 }, + { 0x8d0c, 0x0000 }, + { 0x8d0d, 0x0000 }, + { 0x8d0e, 0x0000 }, + { 0x8d0f, 0x0000 }, + { 0x8d10, 0x0000 }, + { 0x8d11, 0x0000 }, + { 0x8d12, 0x0000 }, + { 0x8d13, 0x0000 }, + { 0x8d14, 0x0000 }, + { 0x8d15, 0x0000 }, + { 0x8d16, 0x0000 }, + { 0x8d17, 0x0000 }, + { 0x8d18, 0x0000 }, + { 0x8d19, 0x0000 }, + { 0x8d1a, 0x0000 }, + { 0x8d1b, 0x0000 }, + { 0x8d1c, 0x0000 }, + { 0x8d1d, 0x0000 }, + { 0x8d1e, 0x0000 }, + { 0x8d1f, 0x0000 }, + { 0x8d20, 0x0000 }, + { 0x8d21, 0x0000 }, + { 0x8d22, 0x0000 }, + { 0x8d23, 0x0000 }, + { 0x8d24, 0x0000 }, + { 0x8d25, 0x0000 }, + { 0x8d26, 0x0000 }, + { 0x8d27, 0x0000 }, + { 0x8d28, 0x0000 }, + { 0x8d29, 0x0000 }, + { 0x8d2a, 0x0000 }, + { 0x8d2b, 0x0000 }, + { 0x8d2c, 0x0000 }, + { 0x8d2d, 0x0000 }, + { 0x8d2e, 0x0000 }, + { 0x8d2f, 0x0000 }, + { 0x8d30, 0x0000 }, + { 0x8d31, 0x0000 }, + { 0x8d32, 0x0000 }, + { 0x8d33, 0x0000 }, + { 0x8d34, 0x0000 }, + { 0x8d35, 0x0000 }, + { 0x8d36, 0x0000 }, + { 0x8d37, 0x0000 }, + { 0x8d38, 0x0000 }, + { 0x8d39, 0x0000 }, + { 0x8d3a, 0x0000 }, + { 0x8d3b, 0x0000 }, + { 0x8d3c, 0x0000 }, + { 0x8d3d, 0x0000 }, + { 0x8d3e, 0x0000 }, + { 0x8d3f, 0x0000 }, + { 0x8d40, 0x0000 }, + { 0x8d41, 0x0000 }, + { 0x8d42, 0x0000 }, + { 0x8d43, 0x0000 }, + { 0x8d44, 0x0000 }, + { 0x8d45, 0x0000 }, + { 0x8d46, 0x0000 }, + { 0x8d47, 0x0000 }, + { 0x8d48, 0x0000 }, + { 0x8d49, 0x0000 }, + { 0x8d4a, 0x0000 }, + { 0x8d4b, 0x0000 }, + { 0x8d4c, 0x0000 }, + { 0x8d4d, 0x0000 }, + { 0x8d4e, 0x0000 }, + { 0x8d4f, 0x0000 }, + { 0x8d50, 0x0000 }, + { 0x8d51, 0x0000 }, + { 0x8d52, 0x0000 }, + { 0x8d53, 0x0000 }, + { 0x8d54, 0x0000 }, + { 0x8d55, 0x0000 }, + { 0x8d56, 0x0000 }, + { 0x8d57, 0x0000 }, + { 0x8d58, 0x0000 }, + { 0x8d59, 0x0000 }, + { 0x8d5a, 0x0000 }, + { 0x8d5b, 0x0000 }, + { 0x8d5c, 0x0000 }, + { 0x8d5d, 0x0000 }, + { 0x8d5e, 0x0000 }, + { 0x8d5f, 0x0000 }, + { 0x8d60, 0x0000 }, + { 0x8d61, 0x0000 }, + { 0x8d62, 0x0000 }, + { 0x8d63, 0x0000 }, + { 0x8d64, 0x0000 }, + { 0x8d65, 0x0000 }, + { 0x8d66, 0x0000 }, + { 0x8d67, 0x0000 }, + { 0x8d68, 0x0000 }, + { 0x8d69, 0x0000 }, + { 0x8d6a, 0x0000 }, + { 0x8d6b, 0x0000 }, + { 0x8d6c, 0x0000 }, + { 0x8d6d, 0x0000 }, + { 0x8d6e, 0x0000 }, + { 0x8d6f, 0x0000 }, + { 0x8d70, 0x0000 }, + { 0x8d71, 0x0000 }, + { 0x8d72, 0x0000 }, + { 0x8d73, 0x0000 }, + { 0x8d74, 0x0000 }, + { 0x8d75, 0x0000 }, + { 0x8d76, 0x0000 }, + { 0x8d77, 0x0000 }, + { 0x8d78, 0x0000 }, + { 0x8d79, 0x0000 }, + { 0x8d7a, 0x0000 }, + { 0x8d7b, 0x0000 }, + { 0x8d7c, 0x0000 }, + { 0x8d7d, 0x0000 }, + { 0x8d7e, 0x0000 }, + { 0x8d7f, 0x0000 }, + { 0x8d80, 0x0000 }, + { 0x8d81, 0x0000 }, + { 0x8d82, 0x0000 }, + { 0x8d83, 0x0000 }, + { 0x8d84, 0x0000 }, + { 0x8d85, 0x0000 }, + { 0x8d86, 0x0000 }, + { 0x8d87, 0x0000 }, + { 0x8d88, 0x0000 }, + { 0x8d89, 0x0000 }, + { 0x8d8a, 0x0000 }, + { 0x8d8b, 0x0000 }, + { 0x8d8c, 0x0000 }, + { 0x8d8d, 0x0000 }, + { 0x8d8e, 0x0000 }, + { 0x8d8f, 0x0000 }, + { 0x8d90, 0x0000 }, + { 0x8d91, 0x0000 }, + { 0x8d92, 0x0000 }, + { 0x8d93, 0x0000 }, + { 0x8d94, 0x0000 }, + { 0x8d95, 0x0000 }, + { 0x8d96, 0x0000 }, + { 0x8d97, 0x0000 }, + { 0x8d98, 0x0000 }, + { 0x8d99, 0x0000 }, + { 0x8d9a, 0x0000 }, + { 0x8d9b, 0x0000 }, + { 0x8d9c, 0x0000 }, + { 0x8d9d, 0x0000 }, + { 0x8d9e, 0x0000 }, + { 0x8d9f, 0x0000 }, + { 0x8da0, 0x0000 }, + { 0x8da1, 0x0000 }, + { 0x8da2, 0x0000 }, + { 0x8da3, 0x0000 }, + { 0x8da4, 0x0000 }, + { 0x8da5, 0x0000 }, + { 0x8da6, 0x0000 }, + { 0x8da7, 0x0000 }, + { 0x8da8, 0x0000 }, + { 0x8da9, 0x0000 }, + { 0x8daa, 0x0000 }, + { 0x8dab, 0x0000 }, + { 0x8dac, 0x0000 }, + { 0x8dad, 0x0000 }, + { 0x8dae, 0x0000 }, + { 0x8daf, 0x0000 }, + { 0x8db0, 0x0000 }, + { 0x8db1, 0x0000 }, + { 0x8db2, 0x0000 }, + { 0x8db3, 0x0000 }, + { 0x8db4, 0x0000 }, + { 0x8db5, 0x0000 }, + { 0x8db6, 0x0000 }, + { 0x8db7, 0x0000 }, + { 0x8db8, 0x0000 }, + { 0x8db9, 0x0000 }, + { 0x8dba, 0x0000 }, + { 0x8dbb, 0x0000 }, + { 0x8dbc, 0x0000 }, + { 0x8dbd, 0x0000 }, + { 0x8dbe, 0x0000 }, + { 0x8dbf, 0x0000 }, + { 0x8dc0, 0x0000 }, + { 0x8dc1, 0x0000 }, + { 0x8dc2, 0x0000 }, + { 0x8dc3, 0x0000 }, + { 0x8dc4, 0x0000 }, + { 0x8dc5, 0x0000 }, + { 0x8dc6, 0x0000 }, + { 0x8dc7, 0x0000 }, + { 0x8dc8, 0x0000 }, + { 0x8dc9, 0x0000 }, + { 0x8dca, 0x0000 }, + { 0x8dcb, 0x0000 }, + { 0x8dcc, 0x0000 }, + { 0x8dcd, 0x0000 }, + { 0x8dce, 0x0000 }, + { 0x8dcf, 0x0000 }, + { 0x8dd0, 0x0000 }, + { 0x8dd1, 0x0000 }, + { 0x8dd2, 0x0000 }, + { 0x8dd3, 0x0000 }, + { 0x8dd4, 0x0000 }, + { 0x8dd5, 0x0000 }, + { 0x8dd6, 0x0000 }, + { 0x8dd7, 0x0000 }, + { 0x8dd8, 0x0000 }, + { 0x8dd9, 0x0000 }, + { 0x8dda, 0x0000 }, + { 0x8ddb, 0x0000 }, + { 0x8ddc, 0x0000 }, + { 0x8ddd, 0x0000 }, + { 0x8dde, 0x0000 }, + { 0x8ddf, 0x0000 }, + { 0x8de0, 0x0000 }, + { 0x8de1, 0x0000 }, + { 0x8de2, 0x0000 }, + { 0x8de3, 0x0000 }, + { 0x8de4, 0x0000 }, + { 0x8de5, 0x0000 }, + { 0x8de6, 0x0000 }, + { 0x8de7, 0x0000 }, + { 0x8de8, 0x0000 }, + { 0x8de9, 0x0000 }, + { 0x8dea, 0x0000 }, + { 0x8deb, 0x0000 }, + { 0x8dec, 0x0000 }, + { 0x8ded, 0x0000 }, + { 0x8dee, 0x0000 }, + { 0x8def, 0x0000 }, + { 0x8df0, 0x0000 }, + { 0x8df1, 0x0000 }, + { 0x8df2, 0x0000 }, + { 0x8df3, 0x0000 }, + { 0x8df4, 0x0000 }, + { 0x8df5, 0x0000 }, + { 0x8df6, 0x0000 }, + { 0x8df7, 0x0000 }, + { 0x8df8, 0x0000 }, + { 0x8df9, 0x0000 }, + { 0x8dfa, 0x0000 }, + { 0x8dfb, 0x0000 }, + { 0x8dfc, 0x0000 }, + { 0x8dfd, 0x0000 }, + { 0x8dfe, 0x0000 }, + { 0x8dff, 0x0000 }, + { 0x8e00, 0x0000 }, + { 0x8e01, 0x0000 }, + { 0x8e02, 0x0000 }, + { 0x8e03, 0x0000 }, + { 0x8e04, 0x0000 }, + { 0x8e05, 0x0000 }, + { 0x8e06, 0x0000 }, + { 0x8e07, 0x0000 }, + { 0x8e08, 0x0000 }, + { 0x8e09, 0x0000 }, + { 0x8e0a, 0x0000 }, + { 0x8e0b, 0x0000 }, + { 0x8e0c, 0x0000 }, + { 0x8e0d, 0x0000 }, + { 0x8e0e, 0x0000 }, + { 0x8e0f, 0x0000 }, + { 0x8e10, 0x0000 }, + { 0x8e11, 0x0000 }, + { 0x8e12, 0x0000 }, + { 0x8e13, 0x0000 }, + { 0x8e14, 0x0000 }, + { 0x8e15, 0x0000 }, + { 0x8e16, 0x0000 }, + { 0x8e17, 0x0000 }, + { 0x8e18, 0x0000 }, + { 0x8e19, 0x0000 }, + { 0x8e1a, 0x0000 }, + { 0x8e1b, 0x0000 }, + { 0x8e1c, 0x0000 }, + { 0x8e1d, 0x0000 }, + { 0x8e1e, 0x0000 }, + { 0x8e1f, 0x0000 }, + { 0x8e20, 0x0000 }, + { 0x8e21, 0x0000 }, + { 0x8e22, 0x0000 }, + { 0x8e23, 0x0000 }, + { 0x8e24, 0x0000 }, + { 0x8e25, 0x0000 }, + { 0x8e26, 0x0000 }, + { 0x8e27, 0x0000 }, + { 0x8e28, 0x0000 }, + { 0x8e29, 0x0000 }, + { 0x8e2a, 0x0000 }, + { 0x8e2b, 0x0000 }, + { 0x8e2c, 0x0000 }, + { 0x8e2d, 0x0000 }, + { 0x8e2e, 0x0000 }, + { 0x8e2f, 0x0000 }, + { 0x8e30, 0x0000 }, + { 0x8e31, 0x0000 }, + { 0x8e32, 0x0000 }, + { 0x8e33, 0x0000 }, + { 0x8e34, 0x0000 }, + { 0x8e35, 0x0000 }, + { 0x8e36, 0x0000 }, + { 0x8e37, 0x0000 }, + { 0x8e38, 0x0000 }, + { 0x8e39, 0x0000 }, + { 0x8e3a, 0x0000 }, + { 0x8e3b, 0x0000 }, + { 0x8e3c, 0x0000 }, + { 0x8e3d, 0x0000 }, + { 0x8e3e, 0x0000 }, + { 0x8e3f, 0x0000 }, + { 0x8e40, 0x0000 }, + { 0x8e41, 0x0000 }, + { 0x8e42, 0x0000 }, + { 0x8e43, 0x0000 }, + { 0x8e44, 0x0000 }, + { 0x8e45, 0x0000 }, + { 0x8e46, 0x0000 }, + { 0x8e47, 0x0000 }, + { 0x8e48, 0x0000 }, + { 0x8e49, 0x0000 }, + { 0x8e4a, 0x0000 }, + { 0x8e4b, 0x0000 }, + { 0x8e4c, 0x0000 }, + { 0x8e4d, 0x0000 }, + { 0x8e4e, 0x0000 }, + { 0x8e4f, 0x0000 }, + { 0x8e50, 0x0000 }, + { 0x8e51, 0x0000 }, + { 0x8e52, 0x0000 }, + { 0x8e53, 0x0000 }, + { 0x8e54, 0x0000 }, + { 0x8e55, 0x0000 }, + { 0x8e56, 0x0000 }, + { 0x8e57, 0x0000 }, + { 0x8e58, 0x0000 }, + { 0x8e59, 0x0000 }, + { 0x8e5a, 0x0000 }, + { 0x8e5b, 0x0000 }, + { 0x8e5c, 0x0000 }, + { 0x8e5d, 0x0000 }, + { 0x8e5e, 0x0000 }, + { 0x8e5f, 0x0000 }, + { 0x8e60, 0x0000 }, + { 0x8e61, 0x0000 }, + { 0x8e62, 0x0000 }, + { 0x8e63, 0x0000 }, + { 0x8e64, 0x0000 }, + { 0x8e65, 0x0000 }, + { 0x8e66, 0x0000 }, + { 0x8e67, 0x0000 }, + { 0x8e68, 0x0000 }, + { 0x8e69, 0x0000 }, + { 0x8e6a, 0x0000 }, + { 0x8e6b, 0x0000 }, + { 0x8e6c, 0x0000 }, + { 0x8e6d, 0x0000 }, + { 0x8e6e, 0x0000 }, + { 0x8e6f, 0x0000 }, + { 0x8e70, 0x0000 }, + { 0x8e71, 0x0000 }, + { 0x8e72, 0x0000 }, + { 0x8e73, 0x0000 }, + { 0x8e74, 0x0000 }, + { 0x8e75, 0x0000 }, + { 0x8e76, 0x0000 }, + { 0x8e77, 0x0000 }, + { 0x8e78, 0x0000 }, + { 0x8e79, 0x0000 }, + { 0x8e7a, 0x0000 }, + { 0x8e7b, 0x0000 }, + { 0x8e7c, 0x0000 }, + { 0x8e7d, 0x0000 }, + { 0x8e7e, 0x0000 }, + { 0x8e7f, 0x0000 }, + { 0x8e80, 0x0000 }, + { 0x8e81, 0x0000 }, + { 0x8e82, 0x0000 }, + { 0x8e83, 0x0000 }, + { 0x8e84, 0x0000 }, + { 0x8e85, 0x0000 }, + { 0x8e86, 0x0000 }, + { 0x8e87, 0x0000 }, + { 0x8e88, 0x0000 }, + { 0x8e89, 0x0000 }, + { 0x8e8a, 0x0000 }, + { 0x8e8b, 0x0000 }, + { 0x8e8c, 0x0000 }, + { 0x8e8d, 0x0000 }, + { 0x8e8e, 0x0000 }, + { 0x8e8f, 0x0000 }, + { 0x8e90, 0x0000 }, + { 0x8e91, 0x0000 }, + { 0x8e92, 0x0000 }, + { 0x8e93, 0x0000 }, + { 0x8e94, 0x0000 }, + { 0x8e95, 0x0000 }, + { 0x8e96, 0x0000 }, + { 0x8e97, 0x0000 }, + { 0x8e98, 0x0000 }, + { 0x8e99, 0x0000 }, + { 0x8e9a, 0x0000 }, + { 0x8e9b, 0x0000 }, + { 0x8e9c, 0x0000 }, + { 0x8e9d, 0x0000 }, + { 0x8e9e, 0x0000 }, + { 0x8e9f, 0x0000 }, + { 0x8ea0, 0x0000 }, + { 0x8ea1, 0x0000 }, + { 0x8ea2, 0x0000 }, + { 0x8ea3, 0x0000 }, + { 0x8ea4, 0x0000 }, + { 0x8ea5, 0x0000 }, + { 0x8ea6, 0x0000 }, + { 0x8ea7, 0x0000 }, + { 0x8ea8, 0x0000 }, + { 0x8ea9, 0x0000 }, + { 0x8eaa, 0x0000 }, + { 0x8eab, 0x0000 }, + { 0x8eac, 0x0000 }, + { 0x8ead, 0x0000 }, + { 0x8eae, 0x0000 }, + { 0x8eaf, 0x0000 }, + { 0x8eb0, 0x0000 }, + { 0x8eb1, 0x0000 }, + { 0x8eb2, 0x0000 }, + { 0x8eb3, 0x0000 }, + { 0x8eb4, 0x0000 }, + { 0x8eb5, 0x0000 }, + { 0x8eb6, 0x0000 }, + { 0x8eb7, 0x0000 }, + { 0x8eb8, 0x0000 }, + { 0x8eb9, 0x0000 }, + { 0x8eba, 0x0000 }, + { 0x8ebb, 0x0000 }, + { 0x8ebc, 0x0000 }, + { 0x8ebd, 0x0000 }, + { 0x8ebe, 0x0000 }, + { 0x8ebf, 0x0000 }, + { 0x8ec0, 0x0000 }, + { 0x8ec1, 0x0000 }, + { 0x8ec2, 0x0000 }, + { 0x8ec3, 0x0000 }, + { 0x8ec4, 0x0000 }, + { 0x8ec5, 0x0000 }, + { 0x8ec6, 0x0000 }, + { 0x8ec7, 0x0000 }, + { 0x8ec8, 0x0000 }, + { 0x8ec9, 0x0000 }, + { 0x8eca, 0x0000 }, + { 0x8ecb, 0x0000 }, + { 0x8ecc, 0x0000 }, + { 0x8ecd, 0x0000 }, + { 0x8ece, 0x0000 }, + { 0x8ecf, 0x0000 }, + { 0x8ed0, 0x0000 }, + { 0x8ed1, 0x0000 }, + { 0x8ed2, 0x0000 }, + { 0x8ed3, 0x0000 }, + { 0x8ed4, 0x0000 }, + { 0x8ed5, 0x0000 }, + { 0x8ed6, 0x0000 }, + { 0x8ed7, 0x0000 }, + { 0x8ed8, 0x0000 }, + { 0x8ed9, 0x0000 }, + { 0x8eda, 0x0000 }, + { 0x8edb, 0x0000 }, + { 0x8edc, 0x0000 }, + { 0x8edd, 0x0000 }, + { 0x8ede, 0x0000 }, + { 0x8edf, 0x0000 }, + { 0x8ee0, 0x0000 }, + { 0x8ee1, 0x0000 }, + { 0x8ee2, 0x0000 }, + { 0x8ee3, 0x0000 }, + { 0x8ee4, 0x0000 }, + { 0x8ee5, 0x0000 }, + { 0x8ee6, 0x0000 }, + { 0x8ee7, 0x0000 }, + { 0x8ee8, 0x0000 }, + { 0x8ee9, 0x0000 }, + { 0x8eea, 0x0000 }, + { 0x8eeb, 0x0000 }, + { 0x8eec, 0x0000 }, + { 0x8eed, 0x0000 }, + { 0x8eee, 0x0000 }, + { 0x8eef, 0x0000 }, + { 0x8ef0, 0x0000 }, + { 0x8ef1, 0x0000 }, + { 0x8ef2, 0x0000 }, + { 0x8ef3, 0x0000 }, + { 0x8ef4, 0x0000 }, + { 0x8ef5, 0x0000 }, + { 0x8ef6, 0x0000 }, + { 0x8ef7, 0x0000 }, + { 0x8ef8, 0x0000 }, + { 0x8ef9, 0x0000 }, + { 0x8efa, 0x0000 }, + { 0x8efb, 0x0000 }, + { 0x8efc, 0x0000 }, + { 0x8efd, 0x0000 }, + { 0x8efe, 0x0000 }, + { 0x8eff, 0x0000 }, + { 0x8f00, 0x0000 }, + { 0x8f01, 0x0000 }, + { 0x8f02, 0x0000 }, + { 0x8f03, 0x0000 }, + { 0x8f04, 0x0000 }, + { 0x8f05, 0x0000 }, + { 0x8f06, 0x0000 }, + { 0x8f07, 0x0000 }, + { 0x8f08, 0x0000 }, + { 0x8f09, 0x0000 }, + { 0x8f0a, 0x0000 }, + { 0x8f0b, 0x0000 }, + { 0x8f0c, 0x0000 }, + { 0x8f0d, 0x0000 }, + { 0x8f0e, 0x0000 }, + { 0x8f0f, 0x0000 }, + { 0x8f10, 0x0000 }, + { 0x8f11, 0x0000 }, + { 0x8f12, 0x0000 }, + { 0x8f13, 0x0000 }, + { 0x8f14, 0x0000 }, + { 0x8f15, 0x0000 }, + { 0x8f16, 0x0000 }, + { 0x8f17, 0x0000 }, + { 0x8f18, 0x0000 }, + { 0x8f19, 0x0000 }, + { 0x8f1a, 0x0000 }, + { 0x8f1b, 0x0000 }, + { 0x8f1c, 0x0000 }, + { 0x8f1d, 0x0000 }, + { 0x8f1e, 0x0000 }, + { 0x8f1f, 0x0000 }, + { 0x8f20, 0x0000 }, + { 0x8f21, 0x0000 }, + { 0x8f22, 0x0000 }, + { 0x8f23, 0x0000 }, + { 0x8f24, 0x0000 }, + { 0x8f25, 0x0000 }, + { 0x8f26, 0x0000 }, + { 0x8f27, 0x0000 }, + { 0x8f28, 0x0000 }, + { 0x8f29, 0x0000 }, + { 0x8f2a, 0x0000 }, + { 0x8f2b, 0x0000 }, + { 0x8f2c, 0x0000 }, + { 0x8f2d, 0x0000 }, + { 0x8f2e, 0x0000 }, + { 0x8f2f, 0x0000 }, + { 0x8f30, 0x0000 }, + { 0x8f31, 0x0000 }, + { 0x8f32, 0x0000 }, + { 0x8f33, 0x0000 }, + { 0x8f34, 0x0000 }, + { 0x8f35, 0x0000 }, + { 0x8f36, 0x0000 }, + { 0x8f37, 0x0000 }, + { 0x8f38, 0x0000 }, + { 0x8f39, 0x0000 }, + { 0x8f3a, 0x0000 }, + { 0x8f3b, 0x0000 }, + { 0x8f3c, 0x0000 }, + { 0x8f3d, 0x0000 }, + { 0x8f3e, 0x0000 }, + { 0x8f3f, 0x0000 }, + { 0x8f40, 0x0000 }, + { 0x8f41, 0x0000 }, + { 0x8f42, 0x0000 }, + { 0x8f43, 0x0000 }, + { 0x8f44, 0x0000 }, + { 0x8f45, 0x0000 }, + { 0x8f46, 0x0000 }, + { 0x8f47, 0x0000 }, + { 0x8f48, 0x0000 }, + { 0x8f49, 0x0000 }, + { 0x8f4a, 0x0000 }, + { 0x8f4b, 0x0000 }, + { 0x8f4c, 0x0000 }, + { 0x8f4d, 0x0000 }, + { 0x8f4e, 0x0000 }, + { 0x8f4f, 0x0000 }, + { 0x8f50, 0x0000 }, + { 0x8f51, 0x0000 }, + { 0x8f52, 0x0000 }, + { 0x8f53, 0x0000 }, + { 0x8f54, 0x0000 }, + { 0x8f55, 0x0000 }, + { 0x8f56, 0x0000 }, + { 0x8f57, 0x0000 }, + { 0x8f58, 0x0000 }, + { 0x8f59, 0x0000 }, + { 0x8f5a, 0x0000 }, + { 0x8f5b, 0x0000 }, + { 0x8f5c, 0x0000 }, + { 0x8f5d, 0x0000 }, + { 0x8f5e, 0x0000 }, + { 0x8f5f, 0x0000 }, + { 0x8f60, 0x0000 }, + { 0x8f61, 0x0000 }, + { 0x8f62, 0x0000 }, + { 0x8f63, 0x0000 }, + { 0x8f64, 0x0000 }, + { 0x8f65, 0x0000 }, + { 0x8f66, 0x0000 }, + { 0x8f67, 0x0000 }, + { 0x8f68, 0x0000 }, + { 0x8f69, 0x0000 }, + { 0x8f6a, 0x0000 }, + { 0x8f6b, 0x0000 }, + { 0x8f6c, 0x0000 }, + { 0x8f6d, 0x0000 }, + { 0x8f6e, 0x0000 }, + { 0x8f6f, 0x0000 }, + { 0x8f70, 0x0000 }, + { 0x8f71, 0x0000 }, + { 0x8f72, 0x0000 }, + { 0x8f73, 0x0000 }, + { 0x8f74, 0x0000 }, + { 0x8f75, 0x0000 }, + { 0x8f76, 0x0000 }, + { 0x8f77, 0x0000 }, + { 0x8f78, 0x0000 }, + { 0x8f79, 0x0000 }, + { 0x8f7a, 0x0000 }, + { 0x8f7b, 0x0000 }, + { 0x8f7c, 0x0000 }, + { 0x8f7d, 0x0000 }, + { 0x8f7e, 0x0000 }, + { 0x8f7f, 0x0000 }, + { 0x8f80, 0x0000 }, + { 0x8f81, 0x0000 }, + { 0x8f82, 0x0000 }, + { 0x8f83, 0x0000 }, + { 0x8f84, 0x0000 }, + { 0x8f85, 0x0000 }, + { 0x8f86, 0x0000 }, + { 0x8f87, 0x0000 }, + { 0x8f88, 0x0000 }, + { 0x8f89, 0x0000 }, + { 0x8f8a, 0x0000 }, + { 0x8f8b, 0x0000 }, + { 0x8f8c, 0x0000 }, + { 0x8f8d, 0x0000 }, + { 0x8f8e, 0x0000 }, + { 0x8f8f, 0x0000 }, + { 0x8f90, 0x0000 }, + { 0x8f91, 0x0000 }, + { 0x8f92, 0x0000 }, + { 0x8f93, 0x0000 }, + { 0x8f94, 0x0000 }, + { 0x8f95, 0x0000 }, + { 0x8f96, 0x0000 }, + { 0x8f97, 0x0000 }, + { 0x8f98, 0x0000 }, + { 0x8f99, 0x0000 }, + { 0x8f9a, 0x0000 }, + { 0x8f9b, 0x0000 }, + { 0x8f9c, 0x0000 }, + { 0x8f9d, 0x0000 }, + { 0x8f9e, 0x0000 }, + { 0x8f9f, 0x0000 }, + { 0x8fa0, 0x0000 }, + { 0x8fa1, 0x0000 }, + { 0x8fa2, 0x0000 }, + { 0x8fa3, 0x0000 }, + { 0x8fa4, 0x0000 }, + { 0x8fa5, 0x0000 }, + { 0x8fa6, 0x0000 }, + { 0x8fa7, 0x0000 }, + { 0x8fa8, 0x0000 }, + { 0x8fa9, 0x0000 }, + { 0x8faa, 0x0000 }, + { 0x8fab, 0x0000 }, + { 0x8fac, 0x0000 }, + { 0x8fad, 0x0000 }, + { 0x8fae, 0x0000 }, + { 0x8faf, 0x0000 }, + { 0x8fb0, 0x0000 }, + { 0x8fb1, 0x0000 }, + { 0x8fb2, 0x0000 }, + { 0x8fb3, 0x0000 }, + { 0x8fb4, 0x0000 }, + { 0x8fb5, 0x0000 }, + { 0x8fb6, 0x0000 }, + { 0x8fb7, 0x0000 }, + { 0x8fb8, 0x0000 }, + { 0x8fb9, 0x0000 }, + { 0x8fba, 0x0000 }, + { 0x8fbb, 0x0000 }, + { 0x8fbc, 0x0000 }, + { 0x8fbd, 0x0000 }, + { 0x8fbe, 0x0000 }, + { 0x8fbf, 0x0000 }, + { 0x8fc0, 0x0000 }, + { 0x8fc1, 0x0000 }, + { 0x8fc2, 0x0000 }, + { 0x8fc3, 0x0000 }, + { 0x8fc4, 0x0000 }, + { 0x8fc5, 0x0000 }, + { 0x8fc6, 0x0000 }, + { 0x8fc7, 0x0000 }, + { 0x8fc8, 0x0000 }, + { 0x8fc9, 0x0000 }, + { 0x8fca, 0x0000 }, + { 0x8fcb, 0x0000 }, + { 0x8fcc, 0x0000 }, + { 0x8fcd, 0x0000 }, + { 0x8fce, 0x0000 }, + { 0x8fcf, 0x0000 }, + { 0x8fd0, 0x0000 }, + { 0x8fd1, 0x0000 }, + { 0x8fd2, 0x0000 }, + { 0x8fd3, 0x0000 }, + { 0x8fd4, 0x0000 }, + { 0x8fd5, 0x0000 }, + { 0x8fd6, 0x0000 }, + { 0x8fd7, 0x0000 }, + { 0x8fd8, 0x0000 }, + { 0x8fd9, 0x0000 }, + { 0x8fda, 0x0000 }, + { 0x8fdb, 0x0000 }, + { 0x8fdc, 0x0000 }, + { 0x8fdd, 0x0000 }, + { 0x8fde, 0x0000 }, + { 0x8fdf, 0x0000 }, + { 0x8fe0, 0x0000 }, + { 0x8fe1, 0x0000 }, + { 0x8fe2, 0x0000 }, + { 0x8fe3, 0x0000 }, + { 0x8fe4, 0x0000 }, + { 0x8fe5, 0x0000 }, + { 0x8fe6, 0x0000 }, + { 0x8fe7, 0x0000 }, + { 0x8fe8, 0x0000 }, + { 0x8fe9, 0x0000 }, + { 0x8fea, 0x0000 }, + { 0x8feb, 0x0000 }, + { 0x8fec, 0x0000 }, + { 0x8fed, 0x0000 }, + { 0x8fee, 0x0000 }, + { 0x8fef, 0x0000 }, + { 0x8ff0, 0x0000 }, + { 0x8ff1, 0x0000 }, + { 0x8ff2, 0x0000 }, + { 0x8ff3, 0x0000 }, + { 0x8ff4, 0x0000 }, + { 0x8ff5, 0x0000 }, + { 0x8ff6, 0x0000 }, + { 0x8ff7, 0x0000 }, + { 0x8ff8, 0x0000 }, + { 0x8ff9, 0x0000 }, + { 0x8ffa, 0x0000 }, + { 0x8ffb, 0x0000 }, + { 0x8ffc, 0x0000 }, + { 0x8ffd, 0x0000 }, + { 0x8ffe, 0x0000 }, + { 0x8fff, 0x0000 }, + { 0x9000, 0x0000 }, + { 0x9001, 0x0000 }, + { 0x9002, 0x0000 }, + { 0x9003, 0x0000 }, + { 0x9004, 0x0000 }, + { 0x9005, 0x0000 }, + { 0x9006, 0x0000 }, + { 0x9007, 0x0000 }, + { 0x9008, 0x0000 }, + { 0x9009, 0x0000 }, + { 0x900a, 0x0000 }, + { 0x900b, 0x0000 }, + { 0x900c, 0x0000 }, + { 0x900d, 0x0000 }, + { 0x900e, 0x0000 }, + { 0x900f, 0x0000 }, + { 0x9010, 0x0000 }, + { 0x9011, 0x0000 }, + { 0x9012, 0x0000 }, + { 0x9013, 0x0000 }, + { 0x9014, 0x0000 }, + { 0x9015, 0x0000 }, + { 0x9016, 0x0000 }, + { 0x9017, 0x0000 }, + { 0x9018, 0x0000 }, + { 0x9019, 0x0000 }, + { 0x901a, 0x0000 }, + { 0x901b, 0x0000 }, + { 0x901c, 0x0000 }, + { 0x901d, 0x0000 }, + { 0x901e, 0x0000 }, + { 0x901f, 0x0000 }, + { 0x9020, 0x0000 }, + { 0x9021, 0x0000 }, + { 0x9022, 0x0000 }, + { 0x9023, 0x0000 }, + { 0x9024, 0x0000 }, + { 0x9025, 0x0000 }, + { 0x9026, 0x0000 }, + { 0x9027, 0x0000 }, + { 0x9028, 0x0000 }, + { 0x9029, 0x0000 }, + { 0x902a, 0x0000 }, + { 0x902b, 0x0000 }, + { 0x902c, 0x0000 }, + { 0x902d, 0x0000 }, + { 0x902e, 0x0000 }, + { 0x902f, 0x0000 }, + { 0x9030, 0x0000 }, + { 0x9031, 0x0000 }, + { 0x9032, 0x0000 }, + { 0x9033, 0x0000 }, + { 0x9034, 0x0000 }, + { 0x9035, 0x0000 }, + { 0x9036, 0x0000 }, + { 0x9037, 0x0000 }, + { 0x9038, 0x0000 }, + { 0x9039, 0x0000 }, + { 0x903a, 0x0000 }, + { 0x903b, 0x0000 }, + { 0x903c, 0x0000 }, + { 0x903d, 0x0000 }, + { 0x903e, 0x0000 }, + { 0x903f, 0x0000 }, + { 0x9040, 0x0000 }, + { 0x9041, 0x0000 }, + { 0x9042, 0x0000 }, + { 0x9043, 0x0000 }, + { 0x9044, 0x0000 }, + { 0x9045, 0x0000 }, + { 0x9046, 0x0000 }, + { 0x9047, 0x0000 }, + { 0x9048, 0x0000 }, + { 0x9049, 0x0000 }, + { 0x904a, 0x0000 }, + { 0x904b, 0x0000 }, + { 0x904c, 0x0000 }, + { 0x904d, 0x0000 }, + { 0x904e, 0x0000 }, + { 0x904f, 0x0000 }, + { 0x9050, 0x0000 }, + { 0x9051, 0x0000 }, + { 0x9052, 0x0000 }, + { 0x9053, 0x0000 }, + { 0x9054, 0x0000 }, + { 0x9055, 0x0000 }, + { 0x9056, 0x0000 }, + { 0x9057, 0x0000 }, + { 0x9058, 0x0000 }, + { 0x9059, 0x0000 }, + { 0x905a, 0x0000 }, + { 0x905b, 0x0000 }, + { 0x905c, 0x0000 }, + { 0x905d, 0x0000 }, + { 0x905e, 0x0000 }, + { 0x905f, 0x0000 }, + { 0x9060, 0x0000 }, + { 0x9061, 0x0000 }, + { 0x9062, 0x0000 }, + { 0x9063, 0x0000 }, + { 0x9064, 0x0000 }, + { 0x9065, 0x0000 }, + { 0x9066, 0x0000 }, + { 0x9067, 0x0000 }, + { 0x9068, 0x0000 }, + { 0x9069, 0x0000 }, + { 0x906a, 0x0000 }, + { 0x906b, 0x0000 }, + { 0x906c, 0x0000 }, + { 0x906d, 0x0000 }, + { 0x906e, 0x0000 }, + { 0x906f, 0x0000 }, + { 0x9070, 0x0000 }, + { 0x9071, 0x0000 }, + { 0x9072, 0x0000 }, + { 0x9073, 0x0000 }, + { 0x9074, 0x0000 }, + { 0x9075, 0x0000 }, + { 0x9076, 0x0000 }, + { 0x9077, 0x0000 }, + { 0x9078, 0x0000 }, + { 0x9079, 0x0000 }, + { 0x907a, 0x0000 }, + { 0x907b, 0x0000 }, + { 0x907c, 0x0000 }, + { 0x907d, 0x0000 }, + { 0x907e, 0x0000 }, + { 0x907f, 0x0000 }, + { 0x9080, 0x0000 }, + { 0x9081, 0x0000 }, + { 0x9082, 0x0000 }, + { 0x9083, 0x0000 }, + { 0x9084, 0x0000 }, + { 0x9085, 0x0000 }, + { 0x9086, 0x0000 }, + { 0x9087, 0x0000 }, + { 0x9088, 0x0000 }, + { 0x9089, 0x0000 }, + { 0x908a, 0x0000 }, + { 0x908b, 0x0000 }, + { 0x908c, 0x0000 }, + { 0x908d, 0x0000 }, + { 0x908e, 0x0000 }, + { 0x908f, 0x0000 }, + { 0x9090, 0x0000 }, + { 0x9091, 0x0000 }, + { 0x9092, 0x0000 }, + { 0x9093, 0x0000 }, + { 0x9094, 0x0000 }, + { 0x9095, 0x0000 }, + { 0x9096, 0x0000 }, + { 0x9097, 0x0000 }, + { 0x9098, 0x0000 }, + { 0x9099, 0x0000 }, + { 0x909a, 0x0000 }, + { 0x909b, 0x0000 }, + { 0x909c, 0x0000 }, + { 0x909d, 0x0000 }, + { 0x909e, 0x0000 }, + { 0x909f, 0x0000 }, + { 0x90a0, 0x0000 }, + { 0x90a1, 0x0000 }, + { 0x90a2, 0x0000 }, + { 0x90a3, 0x0000 }, + { 0x90a4, 0x0000 }, + { 0x90a5, 0x0000 }, + { 0x90a6, 0x0000 }, + { 0x90a7, 0x0000 }, + { 0x90a8, 0x0000 }, + { 0x90a9, 0x0000 }, + { 0x90aa, 0x0000 }, + { 0x90ab, 0x0000 }, + { 0x90ac, 0x0000 }, + { 0x90ad, 0x0000 }, + { 0x90ae, 0x0000 }, + { 0x90af, 0x0000 }, + { 0x90b0, 0x0000 }, + { 0x90b1, 0x0000 }, + { 0x90b2, 0x0000 }, + { 0x90b3, 0x0000 }, + { 0x90b4, 0x0000 }, + { 0x90b5, 0x0000 }, + { 0x90b6, 0x0000 }, + { 0x90b7, 0x0000 }, + { 0x90b8, 0x0000 }, + { 0x90b9, 0x0000 }, + { 0x90ba, 0x0000 }, + { 0x90bb, 0x0000 }, + { 0x90bc, 0x0000 }, + { 0x90bd, 0x0000 }, + { 0x90be, 0x0000 }, + { 0x90bf, 0x0000 }, + { 0x90c0, 0x0000 }, + { 0x90c1, 0x0000 }, + { 0x90c2, 0x0000 }, + { 0x90c3, 0x0000 }, + { 0x90c4, 0x0000 }, + { 0x90c5, 0x0000 }, + { 0x90c6, 0x0000 }, + { 0x90c7, 0x0000 }, + { 0x90c8, 0x0000 }, + { 0x90c9, 0x0000 }, + { 0x90ca, 0x0000 }, + { 0x90cb, 0x0000 }, + { 0x90cc, 0x0000 }, + { 0x90cd, 0x0000 }, + { 0x90ce, 0x0000 }, + { 0x90cf, 0x0000 }, + { 0x90d0, 0x0000 }, + { 0x90d1, 0x0000 }, + { 0x90d2, 0x0000 }, + { 0x90d3, 0x0000 }, + { 0x90d4, 0x0000 }, + { 0x90d5, 0x0000 }, + { 0x90d6, 0x0000 }, + { 0x90d7, 0x0000 }, + { 0x90d8, 0x0000 }, + { 0x90d9, 0x0000 }, + { 0x90da, 0x0000 }, + { 0x90db, 0x0000 }, + { 0x90dc, 0x0000 }, + { 0x90dd, 0x0000 }, + { 0x90de, 0x0000 }, + { 0x90df, 0x0000 }, + { 0x90e0, 0x0000 }, + { 0x90e1, 0x0000 }, + { 0x90e2, 0x0000 }, + { 0x90e3, 0x0000 }, + { 0x90e4, 0x0000 }, + { 0x90e5, 0x0000 }, + { 0x90e6, 0x0000 }, + { 0x90e7, 0x0000 }, + { 0x90e8, 0x0000 }, + { 0x90e9, 0x0000 }, + { 0x90ea, 0x0000 }, + { 0x90eb, 0x0000 }, + { 0x90ec, 0x0000 }, + { 0x90ed, 0x0000 }, + { 0x90ee, 0x0000 }, + { 0x90ef, 0x0000 }, + { 0x90f0, 0x0000 }, + { 0x90f1, 0x0000 }, + { 0x90f2, 0x0000 }, + { 0x90f3, 0x0000 }, + { 0x90f4, 0x0000 }, + { 0x90f5, 0x0000 }, + { 0x90f6, 0x0000 }, + { 0x90f7, 0x0000 }, + { 0x90f8, 0x0000 }, + { 0x90f9, 0x0000 }, + { 0x90fa, 0x0000 }, + { 0x90fb, 0x0000 }, + { 0x90fc, 0x0000 }, + { 0x90fd, 0x0000 }, + { 0x90fe, 0x0000 }, + { 0x90ff, 0x0000 }, + { 0x9100, 0x0000 }, + { 0x9101, 0x0000 }, + { 0x9102, 0x0000 }, + { 0x9103, 0x0000 }, + { 0x9104, 0x0000 }, + { 0x9105, 0x0000 }, + { 0x9106, 0x0000 }, + { 0x9107, 0x0000 }, + { 0x9108, 0x0000 }, + { 0x9109, 0x0000 }, + { 0x910a, 0x0000 }, + { 0x910b, 0x0000 }, + { 0x910c, 0x0000 }, + { 0x910d, 0x0000 }, + { 0x910e, 0x0000 }, + { 0x910f, 0x0000 }, + { 0x9110, 0x0000 }, + { 0x9111, 0x0000 }, + { 0x9112, 0x0000 }, + { 0x9113, 0x0000 }, + { 0x9114, 0x0000 }, + { 0x9115, 0x0000 }, + { 0x9116, 0x0000 }, + { 0x9117, 0x0000 }, + { 0x9118, 0x0000 }, + { 0x9119, 0x0000 }, + { 0x911a, 0x0000 }, + { 0x911b, 0x0000 }, + { 0x911c, 0x0000 }, + { 0x911d, 0x0000 }, + { 0x911e, 0x0000 }, + { 0x911f, 0x0000 }, + { 0x9120, 0x0000 }, + { 0x9121, 0x0000 }, + { 0x9122, 0x0000 }, + { 0x9123, 0x0000 }, + { 0x9124, 0x0000 }, + { 0x9125, 0x0000 }, + { 0x9126, 0x0000 }, + { 0x9127, 0x0000 }, + { 0x9128, 0x0000 }, + { 0x9129, 0x0000 }, + { 0x912a, 0x0000 }, + { 0x912b, 0x0000 }, + { 0x912c, 0x0000 }, + { 0x912d, 0x0000 }, + { 0x912e, 0x0000 }, + { 0x912f, 0x0000 }, + { 0x9130, 0x0000 }, + { 0x9131, 0x0000 }, + { 0x9132, 0x0000 }, + { 0x9133, 0x0000 }, + { 0x9134, 0x0000 }, + { 0x9135, 0x0000 }, + { 0x9136, 0x0000 }, + { 0x9137, 0x0000 }, + { 0x9138, 0x0000 }, + { 0x9139, 0x0000 }, + { 0x913a, 0x0000 }, + { 0x913b, 0x0000 }, + { 0x913c, 0x0000 }, + { 0x913d, 0x0000 }, + { 0x913e, 0x0000 }, + { 0x913f, 0x0000 }, + { 0x9140, 0x0000 }, + { 0x9141, 0x0000 }, + { 0x9142, 0x0000 }, + { 0x9143, 0x0000 }, + { 0x9144, 0x0000 }, + { 0x9145, 0x0000 }, + { 0x9146, 0x0000 }, + { 0x9147, 0x0000 }, + { 0x9148, 0x0000 }, + { 0x9149, 0x0000 }, + { 0x914a, 0x0000 }, + { 0x914b, 0x0000 }, + { 0x914c, 0x0000 }, + { 0x914d, 0x0000 }, + { 0x914e, 0x0000 }, + { 0x914f, 0x0000 }, + { 0x9150, 0x0000 }, + { 0x9151, 0x0000 }, + { 0x9152, 0x0000 }, + { 0x9153, 0x0000 }, + { 0x9154, 0x0000 }, + { 0x9155, 0x0000 }, + { 0x9156, 0x0000 }, + { 0x9157, 0x0000 }, + { 0x9158, 0x0000 }, + { 0x9159, 0x0000 }, + { 0x915a, 0x0000 }, + { 0x915b, 0x0000 }, + { 0x915c, 0x0000 }, + { 0x915d, 0x0000 }, + { 0x915e, 0x0000 }, + { 0x915f, 0x0000 }, + { 0x9160, 0x0000 }, + { 0x9161, 0x0000 }, + { 0x9162, 0x0000 }, + { 0x9163, 0x0000 }, + { 0x9164, 0x0000 }, + { 0x9165, 0x0000 }, + { 0x9166, 0x0000 }, + { 0x9167, 0x0000 }, + { 0x9168, 0x0000 }, + { 0x9169, 0x0000 }, + { 0x916a, 0x0000 }, + { 0x916b, 0x0000 }, + { 0x916c, 0x0000 }, + { 0x916d, 0x0000 }, + { 0x916e, 0x0000 }, + { 0x916f, 0x0000 }, + { 0x9170, 0x0000 }, + { 0x9171, 0x0000 }, + { 0x9172, 0x0000 }, + { 0x9173, 0x0000 }, + { 0x9174, 0x0000 }, + { 0x9175, 0x0000 }, + { 0x9176, 0x0000 }, + { 0x9177, 0x0000 }, + { 0x9178, 0x0000 }, + { 0x9179, 0x0000 }, + { 0x917a, 0x0000 }, + { 0x917b, 0x0000 }, + { 0x917c, 0x0000 }, + { 0x917d, 0x0000 }, + { 0x917e, 0x0000 }, + { 0x917f, 0x0000 }, + { 0x9180, 0x0000 }, + { 0x9181, 0x0000 }, + { 0x9182, 0x0000 }, + { 0x9183, 0x0000 }, + { 0x9184, 0x0000 }, + { 0x9185, 0x0000 }, + { 0x9186, 0x0000 }, + { 0x9187, 0x0000 }, + { 0x9188, 0x0000 }, + { 0x9189, 0x0000 }, + { 0x918a, 0x0000 }, + { 0x918b, 0x0000 }, + { 0x918c, 0x0000 }, + { 0x918d, 0x0000 }, + { 0x918e, 0x0000 }, + { 0x918f, 0x0000 }, + { 0x9190, 0x0000 }, + { 0x9191, 0x0000 }, + { 0x9192, 0x0000 }, + { 0x9193, 0x0000 }, + { 0x9194, 0x0000 }, + { 0x9195, 0x0000 }, + { 0x9196, 0x0000 }, + { 0x9197, 0x0000 }, + { 0x9198, 0x0000 }, + { 0x9199, 0x0000 }, + { 0x919a, 0x0000 }, + { 0x919b, 0x0000 }, + { 0x919c, 0x0000 }, + { 0x919d, 0x0000 }, + { 0x919e, 0x0000 }, + { 0x919f, 0x0000 }, + { 0x91a0, 0x0000 }, + { 0x91a1, 0x0000 }, + { 0x91a2, 0x0000 }, + { 0x91a3, 0x0000 }, + { 0x91a4, 0x0000 }, + { 0x91a5, 0x0000 }, + { 0x91a6, 0x0000 }, + { 0x91a7, 0x0000 }, + { 0x91a8, 0x0000 }, + { 0x91a9, 0x0000 }, + { 0x91aa, 0x0000 }, + { 0x91ab, 0x0000 }, + { 0x91ac, 0x0000 }, + { 0x91ad, 0x0000 }, + { 0x91ae, 0x0000 }, + { 0x91af, 0x0000 }, + { 0x91b0, 0x0000 }, + { 0x91b1, 0x0000 }, + { 0x91b2, 0x0000 }, + { 0x91b3, 0x0000 }, + { 0x91b4, 0x0000 }, + { 0x91b5, 0x0000 }, + { 0x91b6, 0x0000 }, + { 0x91b7, 0x0000 }, + { 0x91b8, 0x0000 }, + { 0x91b9, 0x0000 }, + { 0x91ba, 0x0000 }, + { 0x91bb, 0x0000 }, + { 0x91bc, 0x0000 }, + { 0x91bd, 0x0000 }, + { 0x91be, 0x0000 }, + { 0x91bf, 0x0000 }, + { 0x91c0, 0x0000 }, + { 0x91c1, 0x0000 }, + { 0x91c2, 0x0000 }, + { 0x91c3, 0x0000 }, + { 0x91c4, 0x0000 }, + { 0x91c5, 0x0000 }, + { 0x91c6, 0x0000 }, + { 0x91c7, 0x0000 }, + { 0x91c8, 0x0000 }, + { 0x91c9, 0x0000 }, + { 0x91ca, 0x0000 }, + { 0x91cb, 0x0000 }, + { 0x91cc, 0x0000 }, + { 0x91cd, 0x0000 }, + { 0x91ce, 0x0000 }, + { 0x91cf, 0x0000 }, + { 0x91d0, 0x0000 }, + { 0x91d1, 0x0000 }, + { 0x91d2, 0x0000 }, + { 0x91d3, 0x0000 }, + { 0x91d4, 0x0000 }, + { 0x91d5, 0x0000 }, + { 0x91d6, 0x0000 }, + { 0x91d7, 0x0000 }, + { 0x91d8, 0x0000 }, + { 0x91d9, 0x0000 }, + { 0x91da, 0x0000 }, + { 0x91db, 0x0000 }, + { 0x91dc, 0x0000 }, + { 0x91dd, 0x0000 }, + { 0x91de, 0x0000 }, + { 0x91df, 0x0000 }, + { 0x91e0, 0x0000 }, + { 0x91e1, 0x0000 }, + { 0x91e2, 0x0000 }, + { 0x91e3, 0x0000 }, + { 0x91e4, 0x0000 }, + { 0x91e5, 0x0000 }, + { 0x91e6, 0x0000 }, + { 0x91e7, 0x0000 }, + { 0x91e8, 0x0000 }, + { 0x91e9, 0x0000 }, + { 0x91ea, 0x0000 }, + { 0x91eb, 0x0000 }, + { 0x91ec, 0x0000 }, + { 0x91ed, 0x0000 }, + { 0x91ee, 0x0000 }, + { 0x91ef, 0x0000 }, + { 0x91f0, 0x0000 }, + { 0x91f1, 0x0000 }, + { 0x91f2, 0x0000 }, + { 0x91f3, 0x0000 }, + { 0x91f4, 0x0000 }, + { 0x91f5, 0x0000 }, + { 0x91f6, 0x0000 }, + { 0x91f7, 0x0000 }, + { 0x91f8, 0x0000 }, + { 0x91f9, 0x0000 }, + { 0x91fa, 0x0000 }, + { 0x91fb, 0x0000 }, + { 0x91fc, 0x0000 }, + { 0x91fd, 0x0000 }, + { 0x91fe, 0x0000 }, + { 0x91ff, 0x0000 }, + { 0x9200, 0x0000 }, + { 0x9201, 0x0000 }, + { 0x9202, 0x0000 }, + { 0x9203, 0x0000 }, + { 0x9204, 0x0000 }, + { 0x9205, 0x0000 }, + { 0x9206, 0x0000 }, + { 0x9207, 0x0000 }, + { 0x9208, 0x0000 }, + { 0x9209, 0x0000 }, + { 0x920a, 0x0000 }, + { 0x920b, 0x0000 }, + { 0x920c, 0x0000 }, + { 0x920d, 0x0000 }, + { 0x920e, 0x0000 }, + { 0x920f, 0x0000 }, + { 0x9210, 0x0000 }, + { 0x9211, 0x0000 }, + { 0x9212, 0x0000 }, + { 0x9213, 0x0000 }, + { 0x9214, 0x0000 }, + { 0x9215, 0x0000 }, + { 0x9216, 0x0000 }, + { 0x9217, 0x0000 }, + { 0x9218, 0x0000 }, + { 0x9219, 0x0000 }, + { 0x921a, 0x0000 }, + { 0x921b, 0x0000 }, + { 0x921c, 0x0000 }, + { 0x921d, 0x0000 }, + { 0x921e, 0x0000 }, + { 0x921f, 0x0000 }, + { 0x9220, 0x0000 }, + { 0x9221, 0x0000 }, + { 0x9222, 0x0000 }, + { 0x9223, 0x0000 }, + { 0x9224, 0x0000 }, + { 0x9225, 0x0000 }, + { 0x9226, 0x0000 }, + { 0x9227, 0x0000 }, + { 0x9228, 0x0000 }, + { 0x9229, 0x0000 }, + { 0x922a, 0x0000 }, + { 0x922b, 0x0000 }, + { 0x922c, 0x0000 }, + { 0x922d, 0x0000 }, + { 0x922e, 0x0000 }, + { 0x922f, 0x0000 }, + { 0x9230, 0x0000 }, + { 0x9231, 0x0000 }, + { 0x9232, 0x0000 }, + { 0x9233, 0x0000 }, + { 0x9234, 0x0000 }, + { 0x9235, 0x0000 }, + { 0x9236, 0x0000 }, + { 0x9237, 0x0000 }, + { 0x9238, 0x0000 }, + { 0x9239, 0x0000 }, + { 0x923a, 0x0000 }, + { 0x923b, 0x0000 }, + { 0x923c, 0x0000 }, + { 0x923d, 0x0000 }, + { 0x923e, 0x0000 }, + { 0x923f, 0x0000 }, + { 0x9240, 0x0000 }, + { 0x9241, 0x0000 }, + { 0x9242, 0x0000 }, + { 0x9243, 0x0000 }, + { 0x9244, 0x0000 }, + { 0x9245, 0x0000 }, + { 0x9246, 0x0000 }, + { 0x9247, 0x0000 }, + { 0x9248, 0x0000 }, + { 0x9249, 0x0000 }, + { 0x924a, 0x0000 }, + { 0x924b, 0x0000 }, + { 0x924c, 0x0000 }, + { 0x924d, 0x0000 }, + { 0x924e, 0x0000 }, + { 0x924f, 0x0000 }, + { 0x9250, 0x0000 }, + { 0x9251, 0x0000 }, + { 0x9252, 0x0000 }, + { 0x9253, 0x0000 }, + { 0x9254, 0x0000 }, + { 0x9255, 0x0000 }, + { 0x9256, 0x0000 }, + { 0x9257, 0x0000 }, + { 0x9258, 0x0000 }, + { 0x9259, 0x0000 }, + { 0x925a, 0x0000 }, + { 0x925b, 0x0000 }, + { 0x925c, 0x0000 }, + { 0x925d, 0x0000 }, + { 0x925e, 0x0000 }, + { 0x925f, 0x0000 }, + { 0x9260, 0x0000 }, + { 0x9261, 0x0000 }, + { 0x9262, 0x0000 }, + { 0x9263, 0x0000 }, + { 0x9264, 0x0000 }, + { 0x9265, 0x0000 }, + { 0x9266, 0x0000 }, + { 0x9267, 0x0000 }, + { 0x9268, 0x0000 }, + { 0x9269, 0x0000 }, + { 0x926a, 0x0000 }, + { 0x926b, 0x0000 }, + { 0x926c, 0x0000 }, + { 0x926d, 0x0000 }, + { 0x926e, 0x0000 }, + { 0x926f, 0x0000 }, + { 0x9270, 0x0000 }, + { 0x9271, 0x0000 }, + { 0x9272, 0x0000 }, + { 0x9273, 0x0000 }, + { 0x9274, 0x0000 }, + { 0x9275, 0x0000 }, + { 0x9276, 0x0000 }, + { 0x9277, 0x0000 }, + { 0x9278, 0x0000 }, + { 0x9279, 0x0000 }, + { 0x927a, 0x0000 }, + { 0x927b, 0x0000 }, + { 0x927c, 0x0000 }, + { 0x927d, 0x0000 }, + { 0x927e, 0x0000 }, + { 0x927f, 0x0000 }, + { 0x9280, 0x0000 }, + { 0x9281, 0x0000 }, + { 0x9282, 0x0000 }, + { 0x9283, 0x0000 }, + { 0x9284, 0x0000 }, + { 0x9285, 0x0000 }, + { 0x9286, 0x0000 }, + { 0x9287, 0x0000 }, + { 0x9288, 0x0000 }, + { 0x9289, 0x0000 }, + { 0x928a, 0x0000 }, + { 0x928b, 0x0000 }, + { 0x928c, 0x0000 }, + { 0x928d, 0x0000 }, + { 0x928e, 0x0000 }, + { 0x928f, 0x0000 }, + { 0x9290, 0x0000 }, + { 0x9291, 0x0000 }, + { 0x9292, 0x0000 }, + { 0x9293, 0x0000 }, + { 0x9294, 0x0000 }, + { 0x9295, 0x0000 }, + { 0x9296, 0x0000 }, + { 0x9297, 0x0000 }, + { 0x9298, 0x0000 }, + { 0x9299, 0x0000 }, + { 0x929a, 0x0000 }, + { 0x929b, 0x0000 }, + { 0x929c, 0x0000 }, + { 0x929d, 0x0000 }, + { 0x929e, 0x0000 }, + { 0x929f, 0x0000 }, + { 0x92a0, 0x0000 }, + { 0x92a1, 0x0000 }, + { 0x92a2, 0x0000 }, + { 0x92a3, 0x0000 }, + { 0x92a4, 0x0000 }, + { 0x92a5, 0x0000 }, + { 0x92a6, 0x0000 }, + { 0x92a7, 0x0000 }, + { 0x92a8, 0x0000 }, + { 0x92a9, 0x0000 }, + { 0x92aa, 0x0000 }, + { 0x92ab, 0x0000 }, + { 0x92ac, 0x0000 }, + { 0x92ad, 0x0000 }, + { 0x92ae, 0x0000 }, + { 0x92af, 0x0000 }, + { 0x92b0, 0x0000 }, + { 0x92b1, 0x0000 }, + { 0x92b2, 0x0000 }, + { 0x92b3, 0x0000 }, + { 0x92b4, 0x0000 }, + { 0x92b5, 0x0000 }, + { 0x92b6, 0x0000 }, + { 0x92b7, 0x0000 }, + { 0x92b8, 0x0000 }, + { 0x92b9, 0x0000 }, + { 0x92ba, 0x0000 }, + { 0x92bb, 0x0000 }, + { 0x92bc, 0x0000 }, + { 0x92bd, 0x0000 }, + { 0x92be, 0x0000 }, + { 0x92bf, 0x0000 }, + { 0x92c0, 0x0000 }, + { 0x92c1, 0x0000 }, + { 0x92c2, 0x0000 }, + { 0x92c3, 0x0000 }, + { 0x92c4, 0x0000 }, + { 0x92c5, 0x0000 }, + { 0x92c6, 0x0000 }, + { 0x92c7, 0x0000 }, + { 0x92c8, 0x0000 }, + { 0x92c9, 0x0000 }, + { 0x92ca, 0x0000 }, + { 0x92cb, 0x0000 }, + { 0x92cc, 0x0000 }, + { 0x92cd, 0x0000 }, + { 0x92ce, 0x0000 }, + { 0x92cf, 0x0000 }, + { 0x92d0, 0x0000 }, + { 0x92d1, 0x0000 }, + { 0x92d2, 0x0000 }, + { 0x92d3, 0x0000 }, + { 0x92d4, 0x0000 }, + { 0x92d5, 0x0000 }, + { 0x92d6, 0x0000 }, + { 0x92d7, 0x0000 }, + { 0x92d8, 0x0000 }, + { 0x92d9, 0x0000 }, + { 0x92da, 0x0000 }, + { 0x92db, 0x0000 }, + { 0x92dc, 0x0000 }, + { 0x92dd, 0x0000 }, + { 0x92de, 0x0000 }, + { 0x92df, 0x0000 }, + { 0x92e0, 0x0000 }, + { 0x92e1, 0x0000 }, + { 0x92e2, 0x0000 }, + { 0x92e3, 0x0000 }, + { 0x92e4, 0x0000 }, + { 0x92e5, 0x0000 }, + { 0x92e6, 0x0000 }, + { 0x92e7, 0x0000 }, + { 0x92e8, 0x0000 }, + { 0x92e9, 0x0000 }, + { 0x92ea, 0x0000 }, + { 0x92eb, 0x0000 }, + { 0x92ec, 0x0000 }, + { 0x92ed, 0x0000 }, + { 0x92ee, 0x0000 }, + { 0x92ef, 0x0000 }, + { 0x92f0, 0x0000 }, + { 0x92f1, 0x0000 }, + { 0x92f2, 0x0000 }, + { 0x92f3, 0x0000 }, + { 0x92f4, 0x0000 }, + { 0x92f5, 0x0000 }, + { 0x92f6, 0x0000 }, + { 0x92f7, 0x0000 }, + { 0x92f8, 0x0000 }, + { 0x92f9, 0x0000 }, + { 0x92fa, 0x0000 }, + { 0x92fb, 0x0000 }, + { 0x92fc, 0x0000 }, + { 0x92fd, 0x0000 }, + { 0x92fe, 0x0000 }, + { 0x92ff, 0x0000 }, + { 0x9300, 0x0000 }, + { 0x9301, 0x0000 }, + { 0x9302, 0x0000 }, + { 0x9303, 0x0000 }, + { 0x9304, 0x0000 }, + { 0x9305, 0x0000 }, + { 0x9306, 0x0000 }, + { 0x9307, 0x0000 }, + { 0x9308, 0x0000 }, + { 0x9309, 0x0000 }, + { 0x930a, 0x0000 }, + { 0x930b, 0x0000 }, + { 0x930c, 0x0000 }, + { 0x930d, 0x0000 }, + { 0x930e, 0x0000 }, + { 0x930f, 0x0000 }, + { 0x9310, 0x0000 }, + { 0x9311, 0x0000 }, + { 0x9312, 0x0000 }, + { 0x9313, 0x0000 }, + { 0x9314, 0x0000 }, + { 0x9315, 0x0000 }, + { 0x9316, 0x0000 }, + { 0x9317, 0x0000 }, + { 0x9318, 0x0000 }, + { 0x9319, 0x0000 }, + { 0x931a, 0x0000 }, + { 0x931b, 0x0000 }, + { 0x931c, 0x0000 }, + { 0x931d, 0x0000 }, + { 0x931e, 0x0000 }, + { 0x931f, 0x0000 }, + { 0x9320, 0x0000 }, + { 0x9321, 0x0000 }, + { 0x9322, 0x0000 }, + { 0x9323, 0x0000 }, + { 0x9324, 0x0000 }, + { 0x9325, 0x0000 }, + { 0x9326, 0x0000 }, + { 0x9327, 0x0000 }, + { 0x9328, 0x0000 }, + { 0x9329, 0x0000 }, + { 0x932a, 0x0000 }, + { 0x932b, 0x0000 }, + { 0x932c, 0x0000 }, + { 0x932d, 0x0000 }, + { 0x932e, 0x0000 }, + { 0x932f, 0x0000 }, + { 0x9330, 0x0000 }, + { 0x9331, 0x0000 }, + { 0x9332, 0x0000 }, + { 0x9333, 0x0000 }, + { 0x9334, 0x0000 }, + { 0x9335, 0x0000 }, + { 0x9336, 0x0000 }, + { 0x9337, 0x0000 }, + { 0x9338, 0x0000 }, + { 0x9339, 0x0000 }, + { 0x933a, 0x0000 }, + { 0x933b, 0x0000 }, + { 0x933c, 0x0000 }, + { 0x933d, 0x0000 }, + { 0x933e, 0x0000 }, + { 0x933f, 0x0000 }, + { 0x9340, 0x0000 }, + { 0x9341, 0x0000 }, + { 0x9342, 0x0000 }, + { 0x9343, 0x0000 }, + { 0x9344, 0x0000 }, + { 0x9345, 0x0000 }, + { 0x9346, 0x0000 }, + { 0x9347, 0x0000 }, + { 0x9348, 0x0000 }, + { 0x9349, 0x0000 }, + { 0x934a, 0x0000 }, + { 0x934b, 0x0000 }, + { 0x934c, 0x0000 }, + { 0x934d, 0x0000 }, + { 0x934e, 0x0000 }, + { 0x934f, 0x0000 }, + { 0x9350, 0x0000 }, + { 0x9351, 0x0000 }, + { 0x9352, 0x0000 }, + { 0x9353, 0x0000 }, + { 0x9354, 0x0000 }, + { 0x9355, 0x0000 }, + { 0x9356, 0x0000 }, + { 0x9357, 0x0000 }, + { 0x9358, 0x0000 }, + { 0x9359, 0x0000 }, + { 0x935a, 0x0000 }, + { 0x935b, 0x0000 }, + { 0x935c, 0x0000 }, + { 0x935d, 0x0000 }, + { 0x935e, 0x0000 }, + { 0x935f, 0x0000 }, + { 0x9360, 0x0000 }, + { 0x9361, 0x0000 }, + { 0x9362, 0x0000 }, + { 0x9363, 0x0000 }, + { 0x9364, 0x0000 }, + { 0x9365, 0x0000 }, + { 0x9366, 0x0000 }, + { 0x9367, 0x0000 }, + { 0x9368, 0x0000 }, + { 0x9369, 0x0000 }, + { 0x936a, 0x0000 }, + { 0x936b, 0x0000 }, + { 0x936c, 0x0000 }, + { 0x936d, 0x0000 }, + { 0x936e, 0x0000 }, + { 0x936f, 0x0000 }, + { 0x9370, 0x0000 }, + { 0x9371, 0x0000 }, + { 0x9372, 0x0000 }, + { 0x9373, 0x0000 }, + { 0x9374, 0x0000 }, + { 0x9375, 0x0000 }, + { 0x9376, 0x0000 }, + { 0x9377, 0x0000 }, + { 0x9378, 0x0000 }, + { 0x9379, 0x0000 }, + { 0x937a, 0x0000 }, + { 0x937b, 0x0000 }, + { 0x937c, 0x0000 }, + { 0x937d, 0x0000 }, + { 0x937e, 0x0000 }, + { 0x937f, 0x0000 }, + { 0x9380, 0x0000 }, + { 0x9381, 0x0000 }, + { 0x9382, 0x0000 }, + { 0x9383, 0x0000 }, + { 0x9384, 0x0000 }, + { 0x9385, 0x0000 }, + { 0x9386, 0x0000 }, + { 0x9387, 0x0000 }, + { 0x9388, 0x0000 }, + { 0x9389, 0x0000 }, + { 0x938a, 0x0000 }, + { 0x938b, 0x0000 }, + { 0x938c, 0x0000 }, + { 0x938d, 0x0000 }, + { 0x938e, 0x0000 }, + { 0x938f, 0x0000 }, + { 0x9390, 0x0000 }, + { 0x9391, 0x0000 }, + { 0x9392, 0x0000 }, + { 0x9393, 0x0000 }, + { 0x9394, 0x0000 }, + { 0x9395, 0x0000 }, + { 0x9396, 0x0000 }, + { 0x9397, 0x0000 }, + { 0x9398, 0x0000 }, + { 0x9399, 0x0000 }, + { 0x939a, 0x0000 }, + { 0x939b, 0x0000 }, + { 0x939c, 0x0000 }, + { 0x939d, 0x0000 }, + { 0x939e, 0x0000 }, + { 0x939f, 0x0000 }, + { 0x93a0, 0x0000 }, + { 0x93a1, 0x0000 }, + { 0x93a2, 0x0000 }, + { 0x93a3, 0x0000 }, + { 0x93a4, 0x0000 }, + { 0x93a5, 0x0000 }, + { 0x93a6, 0x0000 }, + { 0x93a7, 0x0000 }, + { 0x93a8, 0x0000 }, + { 0x93a9, 0x0000 }, + { 0x93aa, 0x0000 }, + { 0x93ab, 0x0000 }, + { 0x93ac, 0x0000 }, + { 0x93ad, 0x0000 }, + { 0x93ae, 0x0000 }, + { 0x93af, 0x0000 }, + { 0x93b0, 0x0000 }, + { 0x93b1, 0x0000 }, + { 0x93b2, 0x0000 }, + { 0x93b3, 0x0000 }, + { 0x93b4, 0x0000 }, + { 0x93b5, 0x0000 }, + { 0x93b6, 0x0000 }, + { 0x93b7, 0x0000 }, + { 0x93b8, 0x0000 }, + { 0x93b9, 0x0000 }, + { 0x93ba, 0x0000 }, + { 0x93bb, 0x0000 }, + { 0x93bc, 0x0000 }, + { 0x93bd, 0x0000 }, + { 0x93be, 0x0000 }, + { 0x93bf, 0x0000 }, + { 0x93c0, 0x0000 }, + { 0x93c1, 0x0000 }, + { 0x93c2, 0x0000 }, + { 0x93c3, 0x0000 }, + { 0x93c4, 0x0000 }, + { 0x93c5, 0x0000 }, + { 0x93c6, 0x0000 }, + { 0x93c7, 0x0000 }, + { 0x93c8, 0x0000 }, + { 0x93c9, 0x0000 }, + { 0x93ca, 0x0000 }, + { 0x93cb, 0x0000 }, + { 0x93cc, 0x0000 }, + { 0x93cd, 0x0000 }, + { 0x93ce, 0x0000 }, + { 0x93cf, 0x0000 }, + { 0x93d0, 0x0000 }, + { 0x93d1, 0x0000 }, + { 0x93d2, 0x0000 }, + { 0x93d3, 0x0000 }, + { 0x93d4, 0x0000 }, + { 0x93d5, 0x0000 }, + { 0x93d6, 0x0000 }, + { 0x93d7, 0x0000 }, + { 0x93d8, 0x0000 }, + { 0x93d9, 0x0000 }, + { 0x93da, 0x0000 }, + { 0x93db, 0x0000 }, + { 0x93dc, 0x0000 }, + { 0x93dd, 0x0000 }, + { 0x93de, 0x0000 }, + { 0x93df, 0x0000 }, + { 0x93e0, 0x0000 }, + { 0x93e1, 0x0000 }, + { 0x93e2, 0x0000 }, + { 0x93e3, 0x0000 }, + { 0x93e4, 0x0000 }, + { 0x93e5, 0x0000 }, + { 0x93e6, 0x0000 }, + { 0x93e7, 0x0000 }, + { 0x93e8, 0x0000 }, + { 0x93e9, 0x0000 }, + { 0x93ea, 0x0000 }, + { 0x93eb, 0x0000 }, + { 0x93ec, 0x0000 }, + { 0x93ed, 0x0000 }, + { 0x93ee, 0x0000 }, + { 0x93ef, 0x0000 }, + { 0x93f0, 0x0000 }, + { 0x93f1, 0x0000 }, + { 0x93f2, 0x0000 }, + { 0x93f3, 0x0000 }, + { 0x93f4, 0x0000 }, + { 0x93f5, 0x0000 }, + { 0x93f6, 0x0000 }, + { 0x93f7, 0x0000 }, + { 0x93f8, 0x0000 }, + { 0x93f9, 0x0000 }, + { 0x93fa, 0x0000 }, + { 0x93fb, 0x0000 }, + { 0x93fc, 0x0000 }, + { 0x93fd, 0x0000 }, + { 0x93fe, 0x0000 }, + { 0x93ff, 0x0000 }, + { 0x9400, 0x0000 }, + { 0x9401, 0x0000 }, + { 0x9402, 0x0000 }, + { 0x9403, 0x0000 }, + { 0x9404, 0x0000 }, + { 0x9405, 0x0000 }, + { 0x9406, 0x0000 }, + { 0x9407, 0x0000 }, + { 0x9408, 0x0000 }, + { 0x9409, 0x0000 }, + { 0x940a, 0x0000 }, + { 0x940b, 0x0000 }, + { 0x940c, 0x0000 }, + { 0x940d, 0x0000 }, + { 0x940e, 0x0000 }, + { 0x940f, 0x0000 }, + { 0x9410, 0x0000 }, + { 0x9411, 0x0000 }, + { 0x9412, 0x0000 }, + { 0x9413, 0x0000 }, + { 0x9414, 0x0000 }, + { 0x9415, 0x0000 }, + { 0x9416, 0x0000 }, + { 0x9417, 0x0000 }, + { 0x9418, 0x0000 }, + { 0x9419, 0x0000 }, + { 0x941a, 0x0000 }, + { 0x941b, 0x0000 }, + { 0x941c, 0x0000 }, + { 0x941d, 0x0000 }, + { 0x941e, 0x0000 }, + { 0x941f, 0x0000 }, + { 0x9420, 0x0000 }, + { 0x9421, 0x0000 }, + { 0x9422, 0x0000 }, + { 0x9423, 0x0000 }, + { 0x9424, 0x0000 }, + { 0x9425, 0x0000 }, + { 0x9426, 0x0000 }, + { 0x9427, 0x0000 }, + { 0x9428, 0x0000 }, + { 0x9429, 0x0000 }, + { 0x942a, 0x0000 }, + { 0x942b, 0x0000 }, + { 0x942c, 0x0000 }, + { 0x942d, 0x0000 }, + { 0x942e, 0x0000 }, + { 0x942f, 0x0000 }, + { 0x9430, 0x0000 }, + { 0x9431, 0x0000 }, + { 0x9432, 0x0000 }, + { 0x9433, 0x0000 }, + { 0x9434, 0x0000 }, + { 0x9435, 0x0000 }, + { 0x9436, 0x0000 }, + { 0x9437, 0x0000 }, + { 0x9438, 0x0000 }, + { 0x9439, 0x0000 }, + { 0x943a, 0x0000 }, + { 0x943b, 0x0000 }, + { 0x943c, 0x0000 }, + { 0x943d, 0x0000 }, + { 0x943e, 0x0000 }, + { 0x943f, 0x0000 }, + { 0x9440, 0x0000 }, + { 0x9441, 0x0000 }, + { 0x9442, 0x0000 }, + { 0x9443, 0x0000 }, + { 0x9444, 0x0000 }, + { 0x9445, 0x0000 }, + { 0x9446, 0x0000 }, + { 0x9447, 0x0000 }, + { 0x9448, 0x0000 }, + { 0x9449, 0x0000 }, + { 0x944a, 0x0000 }, + { 0x944b, 0x0000 }, + { 0x944c, 0x0000 }, + { 0x944d, 0x0000 }, + { 0x944e, 0x0000 }, + { 0x944f, 0x0000 }, + { 0x9450, 0x0000 }, + { 0x9451, 0x0000 }, + { 0x9452, 0x0000 }, + { 0x9453, 0x0000 }, + { 0x9454, 0x0000 }, + { 0x9455, 0x0000 }, + { 0x9456, 0x0000 }, + { 0x9457, 0x0000 }, + { 0x9458, 0x0000 }, + { 0x9459, 0x0000 }, + { 0x945a, 0x0000 }, + { 0x945b, 0x0000 }, + { 0x945c, 0x0000 }, + { 0x945d, 0x0000 }, + { 0x945e, 0x0000 }, + { 0x945f, 0x0000 }, + { 0x9460, 0x0000 }, + { 0x9461, 0x0000 }, + { 0x9462, 0x0000 }, + { 0x9463, 0x0000 }, + { 0x9464, 0x0000 }, + { 0x9465, 0x0000 }, + { 0x9466, 0x0000 }, + { 0x9467, 0x0000 }, + { 0x9468, 0x0000 }, + { 0x9469, 0x0000 }, + { 0x946a, 0x0000 }, + { 0x946b, 0x0000 }, + { 0x946c, 0x0000 }, + { 0x946d, 0x0000 }, + { 0x946e, 0x0000 }, + { 0x946f, 0x0000 }, + { 0x9470, 0x0000 }, + { 0x9471, 0x0000 }, + { 0x9472, 0x0000 }, + { 0x9473, 0x0000 }, + { 0x9474, 0x0000 }, + { 0x9475, 0x0000 }, + { 0x9476, 0x0000 }, + { 0x9477, 0x0000 }, + { 0x9478, 0x0000 }, + { 0x9479, 0x0000 }, + { 0x947a, 0x0000 }, + { 0x947b, 0x0000 }, + { 0x947c, 0x0000 }, + { 0x947d, 0x0000 }, + { 0x947e, 0x0000 }, + { 0x947f, 0x0000 }, + { 0x9480, 0x0000 }, + { 0x9481, 0x0000 }, + { 0x9482, 0x0000 }, + { 0x9483, 0x0000 }, + { 0x9484, 0x0000 }, + { 0x9485, 0x0000 }, + { 0x9486, 0x0000 }, + { 0x9487, 0x0000 }, + { 0x9488, 0x0000 }, + { 0x9489, 0x0000 }, + { 0x948a, 0x0000 }, + { 0x948b, 0x0000 }, + { 0x948c, 0x0000 }, + { 0x948d, 0x0000 }, + { 0x948e, 0x0000 }, + { 0x948f, 0x0000 }, + { 0x9490, 0x0000 }, + { 0x9491, 0x0000 }, + { 0x9492, 0x0000 }, + { 0x9493, 0x0000 }, + { 0x9494, 0x0000 }, + { 0x9495, 0x0000 }, + { 0x9496, 0x0000 }, + { 0x9497, 0x0000 }, + { 0x9498, 0x0000 }, + { 0x9499, 0x0000 }, + { 0x949a, 0x0000 }, + { 0x949b, 0x0000 }, + { 0x949c, 0x0000 }, + { 0x949d, 0x0000 }, + { 0x949e, 0x0000 }, + { 0x949f, 0x0000 }, + { 0x94a0, 0x0000 }, + { 0x94a1, 0x0000 }, + { 0x94a2, 0x0000 }, + { 0x94a3, 0x0000 }, + { 0x94a4, 0x0000 }, + { 0x94a5, 0x0000 }, + { 0x94a6, 0x0000 }, + { 0x94a7, 0x0000 }, + { 0x94a8, 0x0000 }, + { 0x94a9, 0x0000 }, + { 0x94aa, 0x0000 }, + { 0x94ab, 0x0000 }, + { 0x94ac, 0x0000 }, + { 0x94ad, 0x0000 }, + { 0x94ae, 0x0000 }, + { 0x94af, 0x0000 }, + { 0x94b0, 0x0000 }, + { 0x94b1, 0x0000 }, + { 0x94b2, 0x0000 }, + { 0x94b3, 0x0000 }, + { 0x94b4, 0x0000 }, + { 0x94b5, 0x0000 }, + { 0x94b6, 0x0000 }, + { 0x94b7, 0x0000 }, + { 0x94b8, 0x0000 }, + { 0x94b9, 0x0000 }, + { 0x94ba, 0x0000 }, + { 0x94bb, 0x0000 }, + { 0x94bc, 0x0000 }, + { 0x94bd, 0x0000 }, + { 0x94be, 0x0000 }, + { 0x94bf, 0x0000 }, + { 0x94c0, 0x0000 }, + { 0x94c1, 0x0000 }, + { 0x94c2, 0x0000 }, + { 0x94c3, 0x0000 }, + { 0x94c4, 0x0000 }, + { 0x94c5, 0x0000 }, + { 0x94c6, 0x0000 }, + { 0x94c7, 0x0000 }, + { 0x94c8, 0x0000 }, + { 0x94c9, 0x0000 }, + { 0x94ca, 0x0000 }, + { 0x94cb, 0x0000 }, + { 0x94cc, 0x0000 }, + { 0x94cd, 0x0000 }, + { 0x94ce, 0x0000 }, + { 0x94cf, 0x0000 }, + { 0x94d0, 0x0000 }, + { 0x94d1, 0x0000 }, + { 0x94d2, 0x0000 }, + { 0x94d3, 0x0000 }, + { 0x94d4, 0x0000 }, + { 0x94d5, 0x0000 }, + { 0x94d6, 0x0000 }, + { 0x94d7, 0x0000 }, + { 0x94d8, 0x0000 }, + { 0x94d9, 0x0000 }, + { 0x94da, 0x0000 }, + { 0x94db, 0x0000 }, + { 0x94dc, 0x0000 }, + { 0x94dd, 0x0000 }, + { 0x94de, 0x0000 }, + { 0x94df, 0x0000 }, + { 0x94e0, 0x0000 }, + { 0x94e1, 0x0000 }, + { 0x94e2, 0x0000 }, + { 0x94e3, 0x0000 }, + { 0x94e4, 0x0000 }, + { 0x94e5, 0x0000 }, + { 0x94e6, 0x0000 }, + { 0x94e7, 0x0000 }, + { 0x94e8, 0x0000 }, + { 0x94e9, 0x0000 }, + { 0x94ea, 0x0000 }, + { 0x94eb, 0x0000 }, + { 0x94ec, 0x0000 }, + { 0x94ed, 0x0000 }, + { 0x94ee, 0x0000 }, + { 0x94ef, 0x0000 }, + { 0x94f0, 0x0000 }, + { 0x94f1, 0x0000 }, + { 0x94f2, 0x0000 }, + { 0x94f3, 0x0000 }, + { 0x94f4, 0x0000 }, + { 0x94f5, 0x0000 }, + { 0x94f6, 0x0000 }, + { 0x94f7, 0x0000 }, + { 0x94f8, 0x0000 }, + { 0x94f9, 0x0000 }, + { 0x94fa, 0x0000 }, + { 0x94fb, 0x0000 }, + { 0x94fc, 0x0000 }, + { 0x94fd, 0x0000 }, + { 0x94fe, 0x0000 }, + { 0x94ff, 0x0000 }, + { 0x9500, 0x0000 }, + { 0x9501, 0x0000 }, + { 0x9502, 0x0000 }, + { 0x9503, 0x0000 }, + { 0x9504, 0x0000 }, + { 0x9505, 0x0000 }, + { 0x9506, 0x0000 }, + { 0x9507, 0x0000 }, + { 0x9508, 0x0000 }, + { 0x9509, 0x0000 }, + { 0x950a, 0x0000 }, + { 0x950b, 0x0000 }, + { 0x950c, 0x0000 }, + { 0x950d, 0x0000 }, + { 0x950e, 0x0000 }, + { 0x950f, 0x0000 }, + { 0x9510, 0x0000 }, + { 0x9511, 0x0000 }, + { 0x9512, 0x0000 }, + { 0x9513, 0x0000 }, + { 0x9514, 0x0000 }, + { 0x9515, 0x0000 }, + { 0x9516, 0x0000 }, + { 0x9517, 0x0000 }, + { 0x9518, 0x0000 }, + { 0x9519, 0x0000 }, + { 0x951a, 0x0000 }, + { 0x951b, 0x0000 }, + { 0x951c, 0x0000 }, + { 0x951d, 0x0000 }, + { 0x951e, 0x0000 }, + { 0x951f, 0x0000 }, + { 0x9520, 0x0000 }, + { 0x9521, 0x0000 }, + { 0x9522, 0x0000 }, + { 0x9523, 0x0000 }, + { 0x9524, 0x0000 }, + { 0x9525, 0x0000 }, + { 0x9526, 0x0000 }, + { 0x9527, 0x0000 }, + { 0x9528, 0x0000 }, + { 0x9529, 0x0000 }, + { 0x952a, 0x0000 }, + { 0x952b, 0x0000 }, + { 0x952c, 0x0000 }, + { 0x952d, 0x0000 }, + { 0x952e, 0x0000 }, + { 0x952f, 0x0000 }, + { 0x9530, 0x0000 }, + { 0x9531, 0x0000 }, + { 0x9532, 0x0000 }, + { 0x9533, 0x0000 }, + { 0x9534, 0x0000 }, + { 0x9535, 0x0000 }, + { 0x9536, 0x0000 }, + { 0x9537, 0x0000 }, + { 0x9538, 0x0000 }, + { 0x9539, 0x0000 }, + { 0x953a, 0x0000 }, + { 0x953b, 0x0000 }, + { 0x953c, 0x0000 }, + { 0x953d, 0x0000 }, + { 0x953e, 0x0000 }, + { 0x953f, 0x0000 }, + { 0x9540, 0x0000 }, + { 0x9541, 0x0000 }, + { 0x9542, 0x0000 }, + { 0x9543, 0x0000 }, + { 0x9544, 0x0000 }, + { 0x9545, 0x0000 }, + { 0x9546, 0x0000 }, + { 0x9547, 0x0000 }, + { 0x9548, 0x0000 }, + { 0x9549, 0x0000 }, + { 0x954a, 0x0000 }, + { 0x954b, 0x0000 }, + { 0x954c, 0x0000 }, + { 0x954d, 0x0000 }, + { 0x954e, 0x0000 }, + { 0x954f, 0x0000 }, + { 0x9550, 0x0000 }, + { 0x9551, 0x0000 }, + { 0x9552, 0x0000 }, + { 0x9553, 0x0000 }, + { 0x9554, 0x0000 }, + { 0x9555, 0x0000 }, + { 0x9556, 0x0000 }, + { 0x9557, 0x0000 }, + { 0x9558, 0x0000 }, + { 0x9559, 0x0000 }, + { 0x955a, 0x0000 }, + { 0x955b, 0x0000 }, + { 0x955c, 0x0000 }, + { 0x955d, 0x0000 }, + { 0x955e, 0x0000 }, + { 0x955f, 0x0000 }, + { 0x9560, 0x0000 }, + { 0x9561, 0x0000 }, + { 0x9562, 0x0000 }, + { 0x9563, 0x0000 }, + { 0x9564, 0x0000 }, + { 0x9565, 0x0000 }, + { 0x9566, 0x0000 }, + { 0x9567, 0x0000 }, + { 0x9568, 0x0000 }, + { 0x9569, 0x0000 }, + { 0x956a, 0x0000 }, + { 0x956b, 0x0000 }, + { 0x956c, 0x0000 }, + { 0x956d, 0x0000 }, + { 0x956e, 0x0000 }, + { 0x956f, 0x0000 }, + { 0x9570, 0x0000 }, + { 0x9571, 0x0000 }, + { 0x9572, 0x0000 }, + { 0x9573, 0x0000 }, + { 0x9574, 0x0000 }, + { 0x9575, 0x0000 }, + { 0x9576, 0x0000 }, + { 0x9577, 0x0000 }, + { 0x9578, 0x0000 }, + { 0x9579, 0x0000 }, + { 0x957a, 0x0000 }, + { 0x957b, 0x0000 }, + { 0x957c, 0x0000 }, + { 0x957d, 0x0000 }, + { 0x957e, 0x0000 }, + { 0x957f, 0x0000 }, + { 0x9580, 0x0000 }, + { 0x9581, 0x0000 }, + { 0x9582, 0x0000 }, + { 0x9583, 0x0000 }, + { 0x9584, 0x0000 }, + { 0x9585, 0x0000 }, + { 0x9586, 0x0000 }, + { 0x9587, 0x0000 }, + { 0x9588, 0x0000 }, + { 0x9589, 0x0000 }, + { 0x958a, 0x0000 }, + { 0x958b, 0x0000 }, + { 0x958c, 0x0000 }, + { 0x958d, 0x0000 }, + { 0x958e, 0x0000 }, + { 0x958f, 0x0000 }, + { 0x9590, 0x0000 }, + { 0x9591, 0x0000 }, + { 0x9592, 0x0000 }, + { 0x9593, 0x0000 }, + { 0x9594, 0x0000 }, + { 0x9595, 0x0000 }, + { 0x9596, 0x0000 }, + { 0x9597, 0x0000 }, + { 0x9598, 0x0000 }, + { 0x9599, 0x0000 }, + { 0x959a, 0x0000 }, + { 0x959b, 0x0000 }, + { 0x959c, 0x0000 }, + { 0x959d, 0x0000 }, + { 0x959e, 0x0000 }, + { 0x959f, 0x0000 }, + { 0x95a0, 0x0000 }, + { 0x95a1, 0x0000 }, + { 0x95a2, 0x0000 }, + { 0x95a3, 0x0000 }, + { 0x95a4, 0x0000 }, + { 0x95a5, 0x0000 }, + { 0x95a6, 0x0000 }, + { 0x95a7, 0x0000 }, + { 0x95a8, 0x0000 }, + { 0x95a9, 0x0000 }, + { 0x95aa, 0x0000 }, + { 0x95ab, 0x0000 }, + { 0x95ac, 0x0000 }, + { 0x95ad, 0x0000 }, + { 0x95ae, 0x0000 }, + { 0x95af, 0x0000 }, + { 0x95b0, 0x0000 }, + { 0x95b1, 0x0000 }, + { 0x95b2, 0x0000 }, + { 0x95b3, 0x0000 }, + { 0x95b4, 0x0000 }, + { 0x95b5, 0x0000 }, + { 0x95b6, 0x0000 }, + { 0x95b7, 0x0000 }, + { 0x95b8, 0x0000 }, + { 0x95b9, 0x0000 }, + { 0x95ba, 0x0000 }, + { 0x95bb, 0x0000 }, + { 0x95bc, 0x0000 }, + { 0x95bd, 0x0000 }, + { 0x95be, 0x0000 }, + { 0x95bf, 0x0000 }, + { 0x95c0, 0x0000 }, + { 0x95c1, 0x0000 }, + { 0x95c2, 0x0000 }, + { 0x95c3, 0x0000 }, + { 0x95c4, 0x0000 }, + { 0x95c5, 0x0000 }, + { 0x95c6, 0x0000 }, + { 0x95c7, 0x0000 }, + { 0x95c8, 0x0000 }, + { 0x95c9, 0x0000 }, + { 0x95ca, 0x0000 }, + { 0x95cb, 0x0000 }, + { 0x95cc, 0x0000 }, + { 0x95cd, 0x0000 }, + { 0x95ce, 0x0000 }, + { 0x95cf, 0x0000 }, + { 0x95d0, 0x0000 }, + { 0x95d1, 0x0000 }, + { 0x95d2, 0x0000 }, + { 0x95d3, 0x0000 }, + { 0x95d4, 0x0000 }, + { 0x95d5, 0x0000 }, + { 0x95d6, 0x0000 }, + { 0x95d7, 0x0000 }, + { 0x95d8, 0x0000 }, + { 0x95d9, 0x0000 }, + { 0x95da, 0x0000 }, + { 0x95db, 0x0000 }, + { 0x95dc, 0x0000 }, + { 0x95dd, 0x0000 }, + { 0x95de, 0x0000 }, + { 0x95df, 0x0000 }, + { 0x95e0, 0x0000 }, + { 0x95e1, 0x0000 }, + { 0x95e2, 0x0000 }, + { 0x95e3, 0x0000 }, + { 0x95e4, 0x0000 }, + { 0x95e5, 0x0000 }, + { 0x95e6, 0x0000 }, + { 0x95e7, 0x0000 }, + { 0x95e8, 0x0000 }, + { 0x95e9, 0x0000 }, + { 0x95ea, 0x0000 }, + { 0x95eb, 0x0000 }, + { 0x95ec, 0x0000 }, + { 0x95ed, 0x0000 }, + { 0x95ee, 0x0000 }, + { 0x95ef, 0x0000 }, + { 0x95f0, 0x0000 }, + { 0x95f1, 0x0000 }, + { 0x95f2, 0x0000 }, + { 0x95f3, 0x0000 }, + { 0x95f4, 0x0000 }, + { 0x95f5, 0x0000 }, + { 0x95f6, 0x0000 }, + { 0x95f7, 0x0000 }, + { 0x95f8, 0x0000 }, + { 0x95f9, 0x0000 }, + { 0x95fa, 0x0000 }, + { 0x95fb, 0x0000 }, + { 0x95fc, 0x0000 }, + { 0x95fd, 0x0000 }, + { 0x95fe, 0x0000 }, + { 0x95ff, 0x0000 }, + { 0x9600, 0x0000 }, + { 0x9601, 0x0000 }, + { 0x9602, 0x0000 }, + { 0x9603, 0x0000 }, + { 0x9604, 0x0000 }, + { 0x9605, 0x0000 }, + { 0x9606, 0x0000 }, + { 0x9607, 0x0000 }, + { 0x9608, 0x0000 }, + { 0x9609, 0x0000 }, + { 0x960a, 0x0000 }, + { 0x960b, 0x0000 }, + { 0x960c, 0x0000 }, + { 0x960d, 0x0000 }, + { 0x960e, 0x0000 }, + { 0x960f, 0x0000 }, + { 0x9610, 0x0000 }, + { 0x9611, 0x0000 }, + { 0x9612, 0x0000 }, + { 0x9613, 0x0000 }, + { 0x9614, 0x0000 }, + { 0x9615, 0x0000 }, + { 0x9616, 0x0000 }, + { 0x9617, 0x0000 }, + { 0x9618, 0x0000 }, + { 0x9619, 0x0000 }, + { 0x961a, 0x0000 }, + { 0x961b, 0x0000 }, + { 0x961c, 0x0000 }, + { 0x961d, 0x0000 }, + { 0x961e, 0x0000 }, + { 0x961f, 0x0000 }, + { 0x9620, 0x0000 }, + { 0x9621, 0x0000 }, + { 0x9622, 0x0000 }, + { 0x9623, 0x0000 }, + { 0x9624, 0x0000 }, + { 0x9625, 0x0000 }, + { 0x9626, 0x0000 }, + { 0x9627, 0x0000 }, + { 0x9628, 0x0000 }, + { 0x9629, 0x0000 }, + { 0x962a, 0x0000 }, + { 0x962b, 0x0000 }, + { 0x962c, 0x0000 }, + { 0x962d, 0x0000 }, + { 0x962e, 0x0000 }, + { 0x962f, 0x0000 }, + { 0x9630, 0x0000 }, + { 0x9631, 0x0000 }, + { 0x9632, 0x0000 }, + { 0x9633, 0x0000 }, + { 0x9634, 0x0000 }, + { 0x9635, 0x0000 }, + { 0x9636, 0x0000 }, + { 0x9637, 0x0000 }, + { 0x9638, 0x0000 }, + { 0x9639, 0x0000 }, + { 0x963a, 0x0000 }, + { 0x963b, 0x0000 }, + { 0x963c, 0x0000 }, + { 0x963d, 0x0000 }, + { 0x963e, 0x0000 }, + { 0x963f, 0x0000 }, + { 0x9640, 0x0000 }, + { 0x9641, 0x0000 }, + { 0x9642, 0x0000 }, + { 0x9643, 0x0000 }, + { 0x9644, 0x0000 }, + { 0x9645, 0x0000 }, + { 0x9646, 0x0000 }, + { 0x9647, 0x0000 }, + { 0x9648, 0x0000 }, + { 0x9649, 0x0000 }, + { 0x964a, 0x0000 }, + { 0x964b, 0x0000 }, + { 0x964c, 0x0000 }, + { 0x964d, 0x0000 }, + { 0x964e, 0x0000 }, + { 0x964f, 0x0000 }, + { 0x9650, 0x0000 }, + { 0x9651, 0x0000 }, + { 0x9652, 0x0000 }, + { 0x9653, 0x0000 }, + { 0x9654, 0x0000 }, + { 0x9655, 0x0000 }, + { 0x9656, 0x0000 }, + { 0x9657, 0x0000 }, + { 0x9658, 0x0000 }, + { 0x9659, 0x0000 }, + { 0x965a, 0x0000 }, + { 0x965b, 0x0000 }, + { 0x965c, 0x0000 }, + { 0x965d, 0x0000 }, + { 0x965e, 0x0000 }, + { 0x965f, 0x0000 }, + { 0x9660, 0x0000 }, + { 0x9661, 0x0000 }, + { 0x9662, 0x0000 }, + { 0x9663, 0x0000 }, + { 0x9664, 0x0000 }, + { 0x9665, 0x0000 }, + { 0x9666, 0x0000 }, + { 0x9667, 0x0000 }, + { 0x9668, 0x0000 }, + { 0x9669, 0x0000 }, + { 0x966a, 0x0000 }, + { 0x966b, 0x0000 }, + { 0x966c, 0x0000 }, + { 0x966d, 0x0000 }, + { 0x966e, 0x0000 }, + { 0x966f, 0x0000 }, + { 0x9670, 0x0000 }, + { 0x9671, 0x0000 }, + { 0x9672, 0x0000 }, + { 0x9673, 0x0000 }, + { 0x9674, 0x0000 }, + { 0x9675, 0x0000 }, + { 0x9676, 0x0000 }, + { 0x9677, 0x0000 }, + { 0x9678, 0x0000 }, + { 0x9679, 0x0000 }, + { 0x967a, 0x0000 }, + { 0x967b, 0x0000 }, + { 0x967c, 0x0000 }, + { 0x967d, 0x0000 }, + { 0x967e, 0x0000 }, + { 0x967f, 0x0000 }, + { 0x9680, 0x0000 }, + { 0x9681, 0x0000 }, + { 0x9682, 0x0000 }, + { 0x9683, 0x0000 }, + { 0x9684, 0x0000 }, + { 0x9685, 0x0000 }, + { 0x9686, 0x0000 }, + { 0x9687, 0x0000 }, + { 0x9688, 0x0000 }, + { 0x9689, 0x0000 }, + { 0x968a, 0x0000 }, + { 0x968b, 0x0000 }, + { 0x968c, 0x0000 }, + { 0x968d, 0x0000 }, + { 0x968e, 0x0000 }, + { 0x968f, 0x0000 }, + { 0x9690, 0x0000 }, + { 0x9691, 0x0000 }, + { 0x9692, 0x0000 }, + { 0x9693, 0x0000 }, + { 0x9694, 0x0000 }, + { 0x9695, 0x0000 }, + { 0x9696, 0x0000 }, + { 0x9697, 0x0000 }, + { 0x9698, 0x0000 }, + { 0x9699, 0x0000 }, + { 0x969a, 0x0000 }, + { 0x969b, 0x0000 }, + { 0x969c, 0x0000 }, + { 0x969d, 0x0000 }, + { 0x969e, 0x0000 }, + { 0x969f, 0x0000 }, + { 0x96a0, 0x0000 }, + { 0x96a1, 0x0000 }, + { 0x96a2, 0x0000 }, + { 0x96a3, 0x0000 }, + { 0x96a4, 0x0000 }, + { 0x96a5, 0x0000 }, + { 0x96a6, 0x0000 }, + { 0x96a7, 0x0000 }, + { 0x96a8, 0x0000 }, + { 0x96a9, 0x0000 }, + { 0x96aa, 0x0000 }, + { 0x96ab, 0x0000 }, + { 0x96ac, 0x0000 }, + { 0x96ad, 0x0000 }, + { 0x96ae, 0x0000 }, + { 0x96af, 0x0000 }, + { 0x96b0, 0x0000 }, + { 0x96b1, 0x0000 }, + { 0x96b2, 0x0000 }, + { 0x96b3, 0x0000 }, + { 0x96b4, 0x0000 }, + { 0x96b5, 0x0000 }, + { 0x96b6, 0x0000 }, + { 0x96b7, 0x0000 }, + { 0x96b8, 0x0000 }, + { 0x96b9, 0x0000 }, + { 0x96ba, 0x0000 }, + { 0x96bb, 0x0000 }, + { 0x96bc, 0x0000 }, + { 0x96bd, 0x0000 }, + { 0x96be, 0x0000 }, + { 0x96bf, 0x0000 }, + { 0x96c0, 0x0000 }, + { 0x96c1, 0x0000 }, + { 0x96c2, 0x0000 }, + { 0x96c3, 0x0000 }, + { 0x96c4, 0x0000 }, + { 0x96c5, 0x0000 }, + { 0x96c6, 0x0000 }, + { 0x96c7, 0x0000 }, + { 0x96c8, 0x0000 }, + { 0x96c9, 0x0000 }, + { 0x96ca, 0x0000 }, + { 0x96cb, 0x0000 }, + { 0x96cc, 0x0000 }, + { 0x96cd, 0x0000 }, + { 0x96ce, 0x0000 }, + { 0x96cf, 0x0000 }, + { 0x96d0, 0x0000 }, + { 0x96d1, 0x0000 }, + { 0x96d2, 0x0000 }, + { 0x96d3, 0x0000 }, + { 0x96d4, 0x0000 }, + { 0x96d5, 0x0000 }, + { 0x96d6, 0x0000 }, + { 0x96d7, 0x0000 }, + { 0x96d8, 0x0000 }, + { 0x96d9, 0x0000 }, + { 0x96da, 0x0000 }, + { 0x96db, 0x0000 }, + { 0x96dc, 0x0000 }, + { 0x96dd, 0x0000 }, + { 0x96de, 0x0000 }, + { 0x96df, 0x0000 }, + { 0x96e0, 0x0000 }, + { 0x96e1, 0x0000 }, + { 0x96e2, 0x0000 }, + { 0x96e3, 0x0000 }, + { 0x96e4, 0x0000 }, + { 0x96e5, 0x0000 }, + { 0x96e6, 0x0000 }, + { 0x96e7, 0x0000 }, + { 0x96e8, 0x0000 }, + { 0x96e9, 0x0000 }, + { 0x96ea, 0x0000 }, + { 0x96eb, 0x0000 }, + { 0x96ec, 0x0000 }, + { 0x96ed, 0x0000 }, + { 0x96ee, 0x0000 }, + { 0x96ef, 0x0000 }, + { 0x96f0, 0x0000 }, + { 0x96f1, 0x0000 }, + { 0x96f2, 0x0000 }, + { 0x96f3, 0x0000 }, + { 0x96f4, 0x0000 }, + { 0x96f5, 0x0000 }, + { 0x96f6, 0x0000 }, + { 0x96f7, 0x0000 }, + { 0x96f8, 0x0000 }, + { 0x96f9, 0x0000 }, + { 0x96fa, 0x0000 }, + { 0x96fb, 0x0000 }, + { 0x96fc, 0x0000 }, + { 0x96fd, 0x0000 }, + { 0x96fe, 0x0000 }, + { 0x96ff, 0x0000 }, + { 0x9700, 0x0000 }, + { 0x9701, 0x0000 }, + { 0x9702, 0x0000 }, + { 0x9703, 0x0000 }, + { 0x9704, 0x0000 }, + { 0x9705, 0x0000 }, + { 0x9706, 0x0000 }, + { 0x9707, 0x0000 }, + { 0x9708, 0x0000 }, + { 0x9709, 0x0000 }, + { 0x970a, 0x0000 }, + { 0x970b, 0x0000 }, + { 0x970c, 0x0000 }, + { 0x970d, 0x0000 }, + { 0x970e, 0x0000 }, + { 0x970f, 0x0000 }, + { 0x9710, 0x0000 }, + { 0x9711, 0x0000 }, + { 0x9712, 0x0000 }, + { 0x9713, 0x0000 }, + { 0x9714, 0x0000 }, + { 0x9715, 0x0000 }, + { 0x9716, 0x0000 }, + { 0x9717, 0x0000 }, + { 0x9718, 0x0000 }, + { 0x9719, 0x0000 }, + { 0x971a, 0x0000 }, + { 0x971b, 0x0000 }, + { 0x971c, 0x0000 }, + { 0x971d, 0x0000 }, + { 0x971e, 0x0000 }, + { 0x971f, 0x0000 }, + { 0x9720, 0x0000 }, + { 0x9721, 0x0000 }, + { 0x9722, 0x0000 }, + { 0x9723, 0x0000 }, + { 0x9724, 0x0000 }, + { 0x9725, 0x0000 }, + { 0x9726, 0x0000 }, + { 0x9727, 0x0000 }, + { 0x9728, 0x0000 }, + { 0x9729, 0x0000 }, + { 0x972a, 0x0000 }, + { 0x972b, 0x0000 }, + { 0x972c, 0x0000 }, + { 0x972d, 0x0000 }, + { 0x972e, 0x0000 }, + { 0x972f, 0x0000 }, + { 0x9730, 0x0000 }, + { 0x9731, 0x0000 }, + { 0x9732, 0x0000 }, + { 0x9733, 0x0000 }, + { 0x9734, 0x0000 }, + { 0x9735, 0x0000 }, + { 0x9736, 0x0000 }, + { 0x9737, 0x0000 }, + { 0x9738, 0x0000 }, + { 0x9739, 0x0000 }, + { 0x973a, 0x0000 }, + { 0x973b, 0x0000 }, + { 0x973c, 0x0000 }, + { 0x973d, 0x0000 }, + { 0x973e, 0x0000 }, + { 0x973f, 0x0000 }, + { 0x9740, 0x0000 }, + { 0x9741, 0x0000 }, + { 0x9742, 0x0000 }, + { 0x9743, 0x0000 }, + { 0x9744, 0x0000 }, + { 0x9745, 0x0000 }, + { 0x9746, 0x0000 }, + { 0x9747, 0x0000 }, + { 0x9748, 0x0000 }, + { 0x9749, 0x0000 }, + { 0x974a, 0x0000 }, + { 0x974b, 0x0000 }, + { 0x974c, 0x0000 }, + { 0x974d, 0x0000 }, + { 0x974e, 0x0000 }, + { 0x974f, 0x0000 }, + { 0x9750, 0x0000 }, + { 0x9751, 0x0000 }, + { 0x9752, 0x0000 }, + { 0x9753, 0x0000 }, + { 0x9754, 0x0000 }, + { 0x9755, 0x0000 }, + { 0x9756, 0x0000 }, + { 0x9757, 0x0000 }, + { 0x9758, 0x0000 }, + { 0x9759, 0x0000 }, + { 0x975a, 0x0000 }, + { 0x975b, 0x0000 }, + { 0x975c, 0x0000 }, + { 0x975d, 0x0000 }, + { 0x975e, 0x0000 }, + { 0x975f, 0x0000 }, + { 0x9760, 0x0000 }, + { 0x9761, 0x0000 }, + { 0x9762, 0x0000 }, + { 0x9763, 0x0000 }, + { 0x9764, 0x0000 }, + { 0x9765, 0x0000 }, + { 0x9766, 0x0000 }, + { 0x9767, 0x0000 }, + { 0x9768, 0x0000 }, + { 0x9769, 0x0000 }, + { 0x976a, 0x0000 }, + { 0x976b, 0x0000 }, + { 0x976c, 0x0000 }, + { 0x976d, 0x0000 }, + { 0x976e, 0x0000 }, + { 0x976f, 0x0000 }, + { 0x9770, 0x0000 }, + { 0x9771, 0x0000 }, + { 0x9772, 0x0000 }, + { 0x9773, 0x0000 }, + { 0x9774, 0x0000 }, + { 0x9775, 0x0000 }, + { 0x9776, 0x0000 }, + { 0x9777, 0x0000 }, + { 0x9778, 0x0000 }, + { 0x9779, 0x0000 }, + { 0x977a, 0x0000 }, + { 0x977b, 0x0000 }, + { 0x977c, 0x0000 }, + { 0x977d, 0x0000 }, + { 0x977e, 0x0000 }, + { 0x977f, 0x0000 }, + { 0x9780, 0x0000 }, + { 0x9781, 0x0000 }, + { 0x9782, 0x0000 }, + { 0x9783, 0x0000 }, + { 0x9784, 0x0000 }, + { 0x9785, 0x0000 }, + { 0x9786, 0x0000 }, + { 0x9787, 0x0000 }, + { 0x9788, 0x0000 }, + { 0x9789, 0x0000 }, + { 0x978a, 0x0000 }, + { 0x978b, 0x0000 }, + { 0x978c, 0x0000 }, + { 0x978d, 0x0000 }, + { 0x978e, 0x0000 }, + { 0x978f, 0x0000 }, + { 0x9790, 0x0000 }, + { 0x9791, 0x0000 }, + { 0x9792, 0x0000 }, + { 0x9793, 0x0000 }, + { 0x9794, 0x0000 }, + { 0x9795, 0x0000 }, + { 0x9796, 0x0000 }, + { 0x9797, 0x0000 }, + { 0x9798, 0x0000 }, + { 0x9799, 0x0000 }, + { 0x979a, 0x0000 }, + { 0x979b, 0x0000 }, + { 0x979c, 0x0000 }, + { 0x979d, 0x0000 }, + { 0x979e, 0x0000 }, + { 0x979f, 0x0000 }, + { 0x97a0, 0x0000 }, + { 0x97a1, 0x0000 }, + { 0x97a2, 0x0000 }, + { 0x97a3, 0x0000 }, + { 0x97a4, 0x0000 }, + { 0x97a5, 0x0000 }, + { 0x97a6, 0x0000 }, + { 0x97a7, 0x0000 }, + { 0x97a8, 0x0000 }, + { 0x97a9, 0x0000 }, + { 0x97aa, 0x0000 }, + { 0x97ab, 0x0000 }, + { 0x97ac, 0x0000 }, + { 0x97ad, 0x0000 }, + { 0x97ae, 0x0000 }, + { 0x97af, 0x0000 }, + { 0x97b0, 0x0000 }, + { 0x97b1, 0x0000 }, + { 0x97b2, 0x0000 }, + { 0x97b3, 0x0000 }, + { 0x97b4, 0x0000 }, + { 0x97b5, 0x0000 }, + { 0x97b6, 0x0000 }, + { 0x97b7, 0x0000 }, + { 0x97b8, 0x0000 }, + { 0x97b9, 0x0000 }, + { 0x97ba, 0x0000 }, + { 0x97bb, 0x0000 }, + { 0x97bc, 0x0000 }, + { 0x97bd, 0x0000 }, + { 0x97be, 0x0000 }, + { 0x97bf, 0x0000 }, + { 0x97c0, 0x0000 }, + { 0x97c1, 0x0000 }, + { 0x97c2, 0x0000 }, + { 0x97c3, 0x0000 }, + { 0x97c4, 0x0000 }, + { 0x97c5, 0x0000 }, + { 0x97c6, 0x0000 }, + { 0x97c7, 0x0000 }, + { 0x97c8, 0x0000 }, + { 0x97c9, 0x0000 }, + { 0x97ca, 0x0000 }, + { 0x97cb, 0x0000 }, + { 0x97cc, 0x0000 }, + { 0x97cd, 0x0000 }, + { 0x97ce, 0x0000 }, + { 0x97cf, 0x0000 }, + { 0x97d0, 0x0000 }, + { 0x97d1, 0x0000 }, + { 0x97d2, 0x0000 }, + { 0x97d3, 0x0000 }, + { 0x97d4, 0x0000 }, + { 0x97d5, 0x0000 }, + { 0x97d6, 0x0000 }, + { 0x97d7, 0x0000 }, + { 0x97d8, 0x0000 }, + { 0x97d9, 0x0000 }, + { 0x97da, 0x0000 }, + { 0x97db, 0x0000 }, + { 0x97dc, 0x0000 }, + { 0x97dd, 0x0000 }, + { 0x97de, 0x0000 }, + { 0x97df, 0x0000 }, + { 0x97e0, 0x0000 }, + { 0x97e1, 0x0000 }, + { 0x97e2, 0x0000 }, + { 0x97e3, 0x0000 }, + { 0x97e4, 0x0000 }, + { 0x97e5, 0x0000 }, + { 0x97e6, 0x0000 }, + { 0x97e7, 0x0000 }, + { 0x97e8, 0x0000 }, + { 0x97e9, 0x0000 }, + { 0x97ea, 0x0000 }, + { 0x97eb, 0x0000 }, + { 0x97ec, 0x0000 }, + { 0x97ed, 0x0000 }, + { 0x97ee, 0x0000 }, + { 0x97ef, 0x0000 }, + { 0x97f0, 0x0000 }, + { 0x97f1, 0x0000 }, + { 0x97f2, 0x0000 }, + { 0x97f3, 0x0000 }, + { 0x97f4, 0x0000 }, + { 0x97f5, 0x0000 }, + { 0x97f6, 0x0000 }, + { 0x97f7, 0x0000 }, + { 0x97f8, 0x0000 }, + { 0x97f9, 0x0000 }, + { 0x97fa, 0x0000 }, + { 0x97fb, 0x0000 }, + { 0x97fc, 0x0000 }, + { 0x97fd, 0x0000 }, + { 0x97fe, 0x0000 }, + { 0x97ff, 0x0000 }, + { 0x9800, 0x0000 }, + { 0x9801, 0x0000 }, + { 0x9802, 0x0000 }, + { 0x9803, 0x0000 }, + { 0x9804, 0x0000 }, + { 0x9805, 0x0000 }, + { 0x9806, 0x0000 }, + { 0x9807, 0x0000 }, + { 0x9808, 0x0000 }, + { 0x9809, 0x0000 }, + { 0x980a, 0x0000 }, + { 0x980b, 0x0000 }, + { 0x980c, 0x0000 }, + { 0x980d, 0x0000 }, + { 0x980e, 0x0000 }, + { 0x980f, 0x0000 }, + { 0x9810, 0x0000 }, + { 0x9811, 0x0000 }, + { 0x9812, 0x0000 }, + { 0x9813, 0x0000 }, + { 0x9814, 0x0000 }, + { 0x9815, 0x0000 }, + { 0x9816, 0x0000 }, + { 0x9817, 0x0000 }, + { 0x9818, 0x0000 }, + { 0x9819, 0x0000 }, + { 0x981a, 0x0000 }, + { 0x981b, 0x0000 }, + { 0x981c, 0x0000 }, + { 0x981d, 0x0000 }, + { 0x981e, 0x0000 }, + { 0x981f, 0x0000 }, + { 0x9820, 0x0000 }, + { 0x9821, 0x0000 }, + { 0x9822, 0x0000 }, + { 0x9823, 0x0000 }, + { 0x9824, 0x0000 }, + { 0x9825, 0x0000 }, + { 0x9826, 0x0000 }, + { 0x9827, 0x0000 }, + { 0x9828, 0x0000 }, + { 0x9829, 0x0000 }, + { 0x982a, 0x0000 }, + { 0x982b, 0x0000 }, + { 0x982c, 0x0000 }, + { 0x982d, 0x0000 }, + { 0x982e, 0x0000 }, + { 0x982f, 0x0000 }, + { 0x9830, 0x0000 }, + { 0x9831, 0x0000 }, + { 0x9832, 0x0000 }, + { 0x9833, 0x0000 }, + { 0x9834, 0x0000 }, + { 0x9835, 0x0000 }, + { 0x9836, 0x0000 }, + { 0x9837, 0x0000 }, + { 0x9838, 0x0000 }, + { 0x9839, 0x0000 }, + { 0x983a, 0x0000 }, + { 0x983b, 0x0000 }, + { 0x983c, 0x0000 }, + { 0x983d, 0x0000 }, + { 0x983e, 0x0000 }, + { 0x983f, 0x0000 }, + { 0x9840, 0x0000 }, + { 0x9841, 0x0000 }, + { 0x9842, 0x0000 }, + { 0x9843, 0x0000 }, + { 0x9844, 0x0000 }, + { 0x9845, 0x0000 }, + { 0x9846, 0x0000 }, + { 0x9847, 0x0000 }, + { 0x9848, 0x0000 }, + { 0x9849, 0x0000 }, + { 0x984a, 0x0000 }, + { 0x984b, 0x0000 }, + { 0x984c, 0x0000 }, + { 0x984d, 0x0000 }, + { 0x984e, 0x0000 }, + { 0x984f, 0x0000 }, + { 0x9850, 0x0000 }, + { 0x9851, 0x0000 }, + { 0x9852, 0x0000 }, + { 0x9853, 0x0000 }, + { 0x9854, 0x0000 }, + { 0x9855, 0x0000 }, + { 0x9856, 0x0000 }, + { 0x9857, 0x0000 }, + { 0x9858, 0x0000 }, + { 0x9859, 0x0000 }, + { 0x985a, 0x0000 }, + { 0x985b, 0x0000 }, + { 0x985c, 0x0000 }, + { 0x985d, 0x0000 }, + { 0x985e, 0x0000 }, + { 0x985f, 0x0000 }, + { 0x9860, 0x0000 }, + { 0x9861, 0x0000 }, + { 0x9862, 0x0000 }, + { 0x9863, 0x0000 }, + { 0x9864, 0x0000 }, + { 0x9865, 0x0000 }, + { 0x9866, 0x0000 }, + { 0x9867, 0x0000 }, + { 0x9868, 0x0000 }, + { 0x9869, 0x0000 }, + { 0x986a, 0x0000 }, + { 0x986b, 0x0000 }, + { 0x986c, 0x0000 }, + { 0x986d, 0x0000 }, + { 0x986e, 0x0000 }, + { 0x986f, 0x0000 }, + { 0x9870, 0x0000 }, + { 0x9871, 0x0000 }, + { 0x9872, 0x0000 }, + { 0x9873, 0x0000 }, + { 0x9874, 0x0000 }, + { 0x9875, 0x0000 }, + { 0x9876, 0x0000 }, + { 0x9877, 0x0000 }, + { 0x9878, 0x0000 }, + { 0x9879, 0x0000 }, + { 0x987a, 0x0000 }, + { 0x987b, 0x0000 }, + { 0x987c, 0x0000 }, + { 0x987d, 0x0000 }, + { 0x987e, 0x0000 }, + { 0x987f, 0x0000 }, + { 0x9880, 0x0000 }, + { 0x9881, 0x0000 }, + { 0x9882, 0x0000 }, + { 0x9883, 0x0000 }, + { 0x9884, 0x0000 }, + { 0x9885, 0x0000 }, + { 0x9886, 0x0000 }, + { 0x9887, 0x0000 }, + { 0x9888, 0x0000 }, + { 0x9889, 0x0000 }, + { 0x988a, 0x0000 }, + { 0x988b, 0x0000 }, + { 0x988c, 0x0000 }, + { 0x988d, 0x0000 }, + { 0x988e, 0x0000 }, + { 0x988f, 0x0000 }, + { 0x9890, 0x0000 }, + { 0x9891, 0x0000 }, + { 0x9892, 0x0000 }, + { 0x9893, 0x0000 }, + { 0x9894, 0x0000 }, + { 0x9895, 0x0000 }, + { 0x9896, 0x0000 }, + { 0x9897, 0x0000 }, + { 0x9898, 0x0000 }, + { 0x9899, 0x0000 }, + { 0x989a, 0x0000 }, + { 0x989b, 0x0000 }, + { 0x989c, 0x0000 }, + { 0x989d, 0x0000 }, + { 0x989e, 0x0000 }, + { 0x989f, 0x0000 }, + { 0x98a0, 0x0000 }, + { 0x98a1, 0x0000 }, + { 0x98a2, 0x0000 }, + { 0x98a3, 0x0000 }, + { 0x98a4, 0x0000 }, + { 0x98a5, 0x0000 }, + { 0x98a6, 0x0000 }, + { 0x98a7, 0x0000 }, + { 0x98a8, 0x0000 }, + { 0x98a9, 0x0000 }, + { 0x98aa, 0x0000 }, + { 0x98ab, 0x0000 }, + { 0x98ac, 0x0000 }, + { 0x98ad, 0x0000 }, + { 0x98ae, 0x0000 }, + { 0x98af, 0x0000 }, + { 0x98b0, 0x0000 }, + { 0x98b1, 0x0000 }, + { 0x98b2, 0x0000 }, + { 0x98b3, 0x0000 }, + { 0x98b4, 0x0000 }, + { 0x98b5, 0x0000 }, + { 0x98b6, 0x0000 }, + { 0x98b7, 0x0000 }, + { 0x98b8, 0x0000 }, + { 0x98b9, 0x0000 }, + { 0x98ba, 0x0000 }, + { 0x98bb, 0x0000 }, + { 0x98bc, 0x0000 }, + { 0x98bd, 0x0000 }, + { 0x98be, 0x0000 }, + { 0x98bf, 0x0000 }, + { 0x98c0, 0x0000 }, + { 0x98c1, 0x0000 }, + { 0x98c2, 0x0000 }, + { 0x98c3, 0x0000 }, + { 0x98c4, 0x0000 }, + { 0x98c5, 0x0000 }, + { 0x98c6, 0x0000 }, + { 0x98c7, 0x0000 }, + { 0x98c8, 0x0000 }, + { 0x98c9, 0x0000 }, + { 0x98ca, 0x0000 }, + { 0x98cb, 0x0000 }, + { 0x98cc, 0x0000 }, + { 0x98cd, 0x0000 }, + { 0x98ce, 0x0000 }, + { 0x98cf, 0x0000 }, + { 0x98d0, 0x0000 }, + { 0x98d1, 0x0000 }, + { 0x98d2, 0x0000 }, + { 0x98d3, 0x0000 }, + { 0x98d4, 0x0000 }, + { 0x98d5, 0x0000 }, + { 0x98d6, 0x0000 }, + { 0x98d7, 0x0000 }, + { 0x98d8, 0x0000 }, + { 0x98d9, 0x0000 }, + { 0x98da, 0x0000 }, + { 0x98db, 0x0000 }, + { 0x98dc, 0x0000 }, + { 0x98dd, 0x0000 }, + { 0x98de, 0x0000 }, + { 0x98df, 0x0000 }, + { 0x98e0, 0x0000 }, + { 0x98e1, 0x0000 }, + { 0x98e2, 0x0000 }, + { 0x98e3, 0x0000 }, + { 0x98e4, 0x0000 }, + { 0x98e5, 0x0000 }, + { 0x98e6, 0x0000 }, + { 0x98e7, 0x0000 }, + { 0x98e8, 0x0000 }, + { 0x98e9, 0x0000 }, + { 0x98ea, 0x0000 }, + { 0x98eb, 0x0000 }, + { 0x98ec, 0x0000 }, + { 0x98ed, 0x0000 }, + { 0x98ee, 0x0000 }, + { 0x98ef, 0x0000 }, + { 0x98f0, 0x0000 }, + { 0x98f1, 0x0000 }, + { 0x98f2, 0x0000 }, + { 0x98f3, 0x0000 }, + { 0x98f4, 0x0000 }, + { 0x98f5, 0x0000 }, + { 0x98f6, 0x0000 }, + { 0x98f7, 0x0000 }, + { 0x98f8, 0x0000 }, + { 0x98f9, 0x0000 }, + { 0x98fa, 0x0000 }, + { 0x98fb, 0x0000 }, + { 0x98fc, 0x0000 }, + { 0x98fd, 0x0000 }, + { 0x98fe, 0x0000 }, + { 0x98ff, 0x0000 }, + { 0x9900, 0x0000 }, + { 0x9901, 0x0000 }, + { 0x9902, 0x0000 }, + { 0x9903, 0x0000 }, + { 0x9904, 0x0000 }, + { 0x9905, 0x0000 }, + { 0x9906, 0x0000 }, + { 0x9907, 0x0000 }, + { 0x9908, 0x0000 }, + { 0x9909, 0x0000 }, + { 0x990a, 0x0000 }, + { 0x990b, 0x0000 }, + { 0x990c, 0x0000 }, + { 0x990d, 0x0000 }, + { 0x990e, 0x0000 }, + { 0x990f, 0x0000 }, + { 0x9910, 0x0000 }, + { 0x9911, 0x0000 }, + { 0x9912, 0x0000 }, + { 0x9913, 0x0000 }, + { 0x9914, 0x0000 }, + { 0x9915, 0x0000 }, + { 0x9916, 0x0000 }, + { 0x9917, 0x0000 }, + { 0x9918, 0x0000 }, + { 0x9919, 0x0000 }, + { 0x991a, 0x0000 }, + { 0x991b, 0x0000 }, + { 0x991c, 0x0000 }, + { 0x991d, 0x0000 }, + { 0x991e, 0x0000 }, + { 0x991f, 0x0000 }, + { 0x9920, 0x0000 }, + { 0x9921, 0x0000 }, + { 0x9922, 0x0000 }, + { 0x9923, 0x0000 }, + { 0x9924, 0x0000 }, + { 0x9925, 0x0000 }, + { 0x9926, 0x0000 }, + { 0x9927, 0x0000 }, + { 0x9928, 0x0000 }, + { 0x9929, 0x0000 }, + { 0x992a, 0x0000 }, + { 0x992b, 0x0000 }, + { 0x992c, 0x0000 }, + { 0x992d, 0x0000 }, + { 0x992e, 0x0000 }, + { 0x992f, 0x0000 }, + { 0x9930, 0x0000 }, + { 0x9931, 0x0000 }, + { 0x9932, 0x0000 }, + { 0x9933, 0x0000 }, + { 0x9934, 0x0000 }, + { 0x9935, 0x0000 }, + { 0x9936, 0x0000 }, + { 0x9937, 0x0000 }, + { 0x9938, 0x0000 }, + { 0x9939, 0x0000 }, + { 0x993a, 0x0000 }, + { 0x993b, 0x0000 }, + { 0x993c, 0x0000 }, + { 0x993d, 0x0000 }, + { 0x993e, 0x0000 }, + { 0x993f, 0x0000 }, + { 0x9940, 0x0000 }, + { 0x9941, 0x0000 }, + { 0x9942, 0x0000 }, + { 0x9943, 0x0000 }, + { 0x9944, 0x0000 }, + { 0x9945, 0x0000 }, + { 0x9946, 0x0000 }, + { 0x9947, 0x0000 }, + { 0x9948, 0x0000 }, + { 0x9949, 0x0000 }, + { 0x994a, 0x0000 }, + { 0x994b, 0x0000 }, + { 0x994c, 0x0000 }, + { 0x994d, 0x0000 }, + { 0x994e, 0x0000 }, + { 0x994f, 0x0000 }, + { 0x9950, 0x0000 }, + { 0x9951, 0x0000 }, + { 0x9952, 0x0000 }, + { 0x9953, 0x0000 }, + { 0x9954, 0x0000 }, + { 0x9955, 0x0000 }, + { 0x9956, 0x0000 }, + { 0x9957, 0x0000 }, + { 0x9958, 0x0000 }, + { 0x9959, 0x0000 }, + { 0x995a, 0x0000 }, + { 0x995b, 0x0000 }, + { 0x995c, 0x0000 }, + { 0x995d, 0x0000 }, + { 0x995e, 0x0000 }, + { 0x995f, 0x0000 }, + { 0x9960, 0x0000 }, + { 0x9961, 0x0000 }, + { 0x9962, 0x0000 }, + { 0x9963, 0x0000 }, + { 0x9964, 0x0000 }, + { 0x9965, 0x0000 }, + { 0x9966, 0x0000 }, + { 0x9967, 0x0000 }, + { 0x9968, 0x0000 }, + { 0x9969, 0x0000 }, + { 0x996a, 0x0000 }, + { 0x996b, 0x0000 }, + { 0x996c, 0x0000 }, + { 0x996d, 0x0000 }, + { 0x996e, 0x0000 }, + { 0x996f, 0x0000 }, + { 0x9970, 0x0000 }, + { 0x9971, 0x0000 }, + { 0x9972, 0x0000 }, + { 0x9973, 0x0000 }, + { 0x9974, 0x0000 }, + { 0x9975, 0x0000 }, + { 0x9976, 0x0000 }, + { 0x9977, 0x0000 }, + { 0x9978, 0x0000 }, + { 0x9979, 0x0000 }, + { 0x997a, 0x0000 }, + { 0x997b, 0x0000 }, + { 0x997c, 0x0000 }, + { 0x997d, 0x0000 }, + { 0x997e, 0x0000 }, + { 0x997f, 0x0000 }, + { 0x9980, 0x0000 }, + { 0x9981, 0x0000 }, + { 0x9982, 0x0000 }, + { 0x9983, 0x0000 }, + { 0x9984, 0x0000 }, + { 0x9985, 0x0000 }, + { 0x9986, 0x0000 }, + { 0x9987, 0x0000 }, + { 0x9988, 0x0000 }, + { 0x9989, 0x0000 }, + { 0x998a, 0x0000 }, + { 0x998b, 0x0000 }, + { 0x998c, 0x0000 }, + { 0x998d, 0x0000 }, + { 0x998e, 0x0000 }, + { 0x998f, 0x0000 }, + { 0x9990, 0x0000 }, + { 0x9991, 0x0000 }, + { 0x9992, 0x0000 }, + { 0x9993, 0x0000 }, + { 0x9994, 0x0000 }, + { 0x9995, 0x0000 }, + { 0x9996, 0x0000 }, + { 0x9997, 0x0000 }, + { 0x9998, 0x0000 }, + { 0x9999, 0x0000 }, + { 0x999a, 0x0000 }, + { 0x999b, 0x0000 }, + { 0x999c, 0x0000 }, + { 0x999d, 0x0000 }, + { 0x999e, 0x0000 }, + { 0x999f, 0x0000 }, + { 0x99a0, 0x0000 }, + { 0x99a1, 0x0000 }, + { 0x99a2, 0x0000 }, + { 0x99a3, 0x0000 }, + { 0x99a4, 0x0000 }, + { 0x99a5, 0x0000 }, + { 0x99a6, 0x0000 }, + { 0x99a7, 0x0000 }, + { 0x99a8, 0x0000 }, + { 0x99a9, 0x0000 }, + { 0x99aa, 0x0000 }, + { 0x99ab, 0x0000 }, + { 0x99ac, 0x0000 }, + { 0x99ad, 0x0000 }, + { 0x99ae, 0x0000 }, + { 0x99af, 0x0000 }, + { 0x99b0, 0x0000 }, + { 0x99b1, 0x0000 }, + { 0x99b2, 0x0000 }, + { 0x99b3, 0x0000 }, + { 0x99b4, 0x0000 }, + { 0x99b5, 0x0000 }, + { 0x99b6, 0x0000 }, + { 0x99b7, 0x0000 }, + { 0x99b8, 0x0000 }, + { 0x99b9, 0x0000 }, + { 0x99ba, 0x0000 }, + { 0x99bb, 0x0000 }, + { 0x99bc, 0x0000 }, + { 0x99bd, 0x0000 }, + { 0x99be, 0x0000 }, + { 0x99bf, 0x0000 }, + { 0x99c0, 0x0000 }, + { 0x99c1, 0x0000 }, + { 0x99c2, 0x0000 }, + { 0x99c3, 0x0000 }, + { 0x99c4, 0x0000 }, + { 0x99c5, 0x0000 }, + { 0x99c6, 0x0000 }, + { 0x99c7, 0x0000 }, + { 0x99c8, 0x0000 }, + { 0x99c9, 0x0000 }, + { 0x99ca, 0x0000 }, + { 0x99cb, 0x0000 }, + { 0x99cc, 0x0000 }, + { 0x99cd, 0x0000 }, + { 0x99ce, 0x0000 }, + { 0x99cf, 0x0000 }, + { 0x99d0, 0x0000 }, + { 0x99d1, 0x0000 }, + { 0x99d2, 0x0000 }, + { 0x99d3, 0x0000 }, + { 0x99d4, 0x0000 }, + { 0x99d5, 0x0000 }, + { 0x99d6, 0x0000 }, + { 0x99d7, 0x0000 }, + { 0x99d8, 0x0000 }, + { 0x99d9, 0x0000 }, + { 0x99da, 0x0000 }, + { 0x99db, 0x0000 }, + { 0x99dc, 0x0000 }, + { 0x99dd, 0x0000 }, + { 0x99de, 0x0000 }, + { 0x99df, 0x0000 }, + { 0x99e0, 0x0000 }, + { 0x99e1, 0x0000 }, + { 0x99e2, 0x0000 }, + { 0x99e3, 0x0000 }, + { 0x99e4, 0x0000 }, + { 0x99e5, 0x0000 }, + { 0x99e6, 0x0000 }, + { 0x99e7, 0x0000 }, + { 0x99e8, 0x0000 }, + { 0x99e9, 0x0000 }, + { 0x99ea, 0x0000 }, + { 0x99eb, 0x0000 }, + { 0x99ec, 0x0000 }, + { 0x99ed, 0x0000 }, + { 0x99ee, 0x0000 }, + { 0x99ef, 0x0000 }, + { 0x99f0, 0x0000 }, + { 0x99f1, 0x0000 }, + { 0x99f2, 0x0000 }, + { 0x99f3, 0x0000 }, + { 0x99f4, 0x0000 }, + { 0x99f5, 0x0000 }, + { 0x99f6, 0x0000 }, + { 0x99f7, 0x0000 }, + { 0x99f8, 0x0000 }, + { 0x99f9, 0x0000 }, + { 0x99fa, 0x0000 }, + { 0x99fb, 0x0000 }, + { 0x99fc, 0x0000 }, + { 0x99fd, 0x0000 }, + { 0x99fe, 0x0000 }, + { 0x99ff, 0x0000 }, + { 0x9a00, 0x0000 }, + { 0x9a01, 0x0000 }, + { 0x9a02, 0x0000 }, + { 0x9a03, 0x0000 }, + { 0x9a04, 0x0000 }, + { 0x9a05, 0x0000 }, + { 0x9a06, 0x0000 }, + { 0x9a07, 0x0000 }, + { 0x9a08, 0x0000 }, + { 0x9a09, 0x0000 }, + { 0x9a0a, 0x0000 }, + { 0x9a0b, 0x0000 }, + { 0x9a0c, 0x0000 }, + { 0x9a0d, 0x0000 }, + { 0x9a0e, 0x0000 }, + { 0x9a0f, 0x0000 }, + { 0x9a10, 0x0000 }, + { 0x9a11, 0x0000 }, + { 0x9a12, 0x0000 }, + { 0x9a13, 0x0000 }, + { 0x9a14, 0x0000 }, + { 0x9a15, 0x0000 }, + { 0x9a16, 0x0000 }, + { 0x9a17, 0x0000 }, + { 0x9a18, 0x0000 }, + { 0x9a19, 0x0000 }, + { 0x9a1a, 0x0000 }, + { 0x9a1b, 0x0000 }, + { 0x9a1c, 0x0000 }, + { 0x9a1d, 0x0000 }, + { 0x9a1e, 0x0000 }, + { 0x9a1f, 0x0000 }, + { 0x9a20, 0x0000 }, + { 0x9a21, 0x0000 }, + { 0x9a22, 0x0000 }, + { 0x9a23, 0x0000 }, + { 0x9a24, 0x0000 }, + { 0x9a25, 0x0000 }, + { 0x9a26, 0x0000 }, + { 0x9a27, 0x0000 }, + { 0x9a28, 0x0000 }, + { 0x9a29, 0x0000 }, + { 0x9a2a, 0x0000 }, + { 0x9a2b, 0x0000 }, + { 0x9a2c, 0x0000 }, + { 0x9a2d, 0x0000 }, + { 0x9a2e, 0x0000 }, + { 0x9a2f, 0x0000 }, + { 0x9a30, 0x0000 }, + { 0x9a31, 0x0000 }, + { 0x9a32, 0x0000 }, + { 0x9a33, 0x0000 }, + { 0x9a34, 0x0000 }, + { 0x9a35, 0x0000 }, + { 0x9a36, 0x0000 }, + { 0x9a37, 0x0000 }, + { 0x9a38, 0x0000 }, + { 0x9a39, 0x0000 }, + { 0x9a3a, 0x0000 }, + { 0x9a3b, 0x0000 }, + { 0x9a3c, 0x0000 }, + { 0x9a3d, 0x0000 }, + { 0x9a3e, 0x0000 }, + { 0x9a3f, 0x0000 }, + { 0x9a40, 0x0000 }, + { 0x9a41, 0x0000 }, + { 0x9a42, 0x0000 }, + { 0x9a43, 0x0000 }, + { 0x9a44, 0x0000 }, + { 0x9a45, 0x0000 }, + { 0x9a46, 0x0000 }, + { 0x9a47, 0x0000 }, + { 0x9a48, 0x0000 }, + { 0x9a49, 0x0000 }, + { 0x9a4a, 0x0000 }, + { 0x9a4b, 0x0000 }, + { 0x9a4c, 0x0000 }, + { 0x9a4d, 0x0000 }, + { 0x9a4e, 0x0000 }, + { 0x9a4f, 0x0000 }, + { 0x9a50, 0x0000 }, + { 0x9a51, 0x0000 }, + { 0x9a52, 0x0000 }, + { 0x9a53, 0x0000 }, + { 0x9a54, 0x0000 }, + { 0x9a55, 0x0000 }, + { 0x9a56, 0x0000 }, + { 0x9a57, 0x0000 }, + { 0x9a58, 0x0000 }, + { 0x9a59, 0x0000 }, + { 0x9a5a, 0x0000 }, + { 0x9a5b, 0x0000 }, + { 0x9a5c, 0x0000 }, + { 0x9a5d, 0x0000 }, + { 0x9a5e, 0x0000 }, + { 0x9a5f, 0x0000 }, + { 0x9a60, 0x0000 }, + { 0x9a61, 0x0000 }, + { 0x9a62, 0x0000 }, + { 0x9a63, 0x0000 }, + { 0x9a64, 0x0000 }, + { 0x9a65, 0x0000 }, + { 0x9a66, 0x0000 }, + { 0x9a67, 0x0000 }, + { 0x9a68, 0x0000 }, + { 0x9a69, 0x0000 }, + { 0x9a6a, 0x0000 }, + { 0x9a6b, 0x0000 }, + { 0x9a6c, 0x0000 }, + { 0x9a6d, 0x0000 }, + { 0x9a6e, 0x0000 }, + { 0x9a6f, 0x0000 }, + { 0x9a70, 0x0000 }, + { 0x9a71, 0x0000 }, + { 0x9a72, 0x0000 }, + { 0x9a73, 0x0000 }, + { 0x9a74, 0x0000 }, + { 0x9a75, 0x0000 }, + { 0x9a76, 0x0000 }, + { 0x9a77, 0x0000 }, + { 0x9a78, 0x0000 }, + { 0x9a79, 0x0000 }, + { 0x9a7a, 0x0000 }, + { 0x9a7b, 0x0000 }, + { 0x9a7c, 0x0000 }, + { 0x9a7d, 0x0000 }, + { 0x9a7e, 0x0000 }, + { 0x9a7f, 0x0000 }, + { 0x9a80, 0x0000 }, + { 0x9a81, 0x0000 }, + { 0x9a82, 0x0000 }, + { 0x9a83, 0x0000 }, + { 0x9a84, 0x0000 }, + { 0x9a85, 0x0000 }, + { 0x9a86, 0x0000 }, + { 0x9a87, 0x0000 }, + { 0x9a88, 0x0000 }, + { 0x9a89, 0x0000 }, + { 0x9a8a, 0x0000 }, + { 0x9a8b, 0x0000 }, + { 0x9a8c, 0x0000 }, + { 0x9a8d, 0x0000 }, + { 0x9a8e, 0x0000 }, + { 0x9a8f, 0x0000 }, + { 0x9a90, 0x0000 }, + { 0x9a91, 0x0000 }, + { 0x9a92, 0x0000 }, + { 0x9a93, 0x0000 }, + { 0x9a94, 0x0000 }, + { 0x9a95, 0x0000 }, + { 0x9a96, 0x0000 }, + { 0x9a97, 0x0000 }, + { 0x9a98, 0x0000 }, + { 0x9a99, 0x0000 }, + { 0x9a9a, 0x0000 }, + { 0x9a9b, 0x0000 }, + { 0x9a9c, 0x0000 }, + { 0x9a9d, 0x0000 }, + { 0x9a9e, 0x0000 }, + { 0x9a9f, 0x0000 }, + { 0x9aa0, 0x0000 }, + { 0x9aa1, 0x0000 }, + { 0x9aa2, 0x0000 }, + { 0x9aa3, 0x0000 }, + { 0x9aa4, 0x0000 }, + { 0x9aa5, 0x0000 }, + { 0x9aa6, 0x0000 }, + { 0x9aa7, 0x0000 }, + { 0x9aa8, 0x0000 }, + { 0x9aa9, 0x0000 }, + { 0x9aaa, 0x0000 }, + { 0x9aab, 0x0000 }, + { 0x9aac, 0x0000 }, + { 0x9aad, 0x0000 }, + { 0x9aae, 0x0000 }, + { 0x9aaf, 0x0000 }, + { 0x9ab0, 0x0000 }, + { 0x9ab1, 0x0000 }, + { 0x9ab2, 0x0000 }, + { 0x9ab3, 0x0000 }, + { 0x9ab4, 0x0000 }, + { 0x9ab5, 0x0000 }, + { 0x9ab6, 0x0000 }, + { 0x9ab7, 0x0000 }, + { 0x9ab8, 0x0000 }, + { 0x9ab9, 0x0000 }, + { 0x9aba, 0x0000 }, + { 0x9abb, 0x0000 }, + { 0x9abc, 0x0000 }, + { 0x9abd, 0x0000 }, + { 0x9abe, 0x0000 }, + { 0x9abf, 0x0000 }, + { 0x9ac0, 0x0000 }, + { 0x9ac1, 0x0000 }, + { 0x9ac2, 0x0000 }, + { 0x9ac3, 0x0000 }, + { 0x9ac4, 0x0000 }, + { 0x9ac5, 0x0000 }, + { 0x9ac6, 0x0000 }, + { 0x9ac7, 0x0000 }, + { 0x9ac8, 0x0000 }, + { 0x9ac9, 0x0000 }, + { 0x9aca, 0x0000 }, + { 0x9acb, 0x0000 }, + { 0x9acc, 0x0000 }, + { 0x9acd, 0x0000 }, + { 0x9ace, 0x0000 }, + { 0x9acf, 0x0000 }, + { 0x9ad0, 0x0000 }, + { 0x9ad1, 0x0000 }, + { 0x9ad2, 0x0000 }, + { 0x9ad3, 0x0000 }, + { 0x9ad4, 0x0000 }, + { 0x9ad5, 0x0000 }, + { 0x9ad6, 0x0000 }, + { 0x9ad7, 0x0000 }, + { 0x9ad8, 0x0000 }, + { 0x9ad9, 0x0000 }, + { 0x9ada, 0x0000 }, + { 0x9adb, 0x0000 }, + { 0x9adc, 0x0000 }, + { 0x9add, 0x0000 }, + { 0x9ade, 0x0000 }, + { 0x9adf, 0x0000 }, + { 0x9ae0, 0x0000 }, + { 0x9ae1, 0x0000 }, + { 0x9ae2, 0x0000 }, + { 0x9ae3, 0x0000 }, + { 0x9ae4, 0x0000 }, + { 0x9ae5, 0x0000 }, + { 0x9ae6, 0x0000 }, + { 0x9ae7, 0x0000 }, + { 0x9ae8, 0x0000 }, + { 0x9ae9, 0x0000 }, + { 0x9aea, 0x0000 }, + { 0x9aeb, 0x0000 }, + { 0x9aec, 0x0000 }, + { 0x9aed, 0x0000 }, + { 0x9aee, 0x0000 }, + { 0x9aef, 0x0000 }, + { 0x9af0, 0x0000 }, + { 0x9af1, 0x0000 }, + { 0x9af2, 0x0000 }, + { 0x9af3, 0x0000 }, + { 0x9af4, 0x0000 }, + { 0x9af5, 0x0000 }, + { 0x9af6, 0x0000 }, + { 0x9af7, 0x0000 }, + { 0x9af8, 0x0000 }, + { 0x9af9, 0x0000 }, + { 0x9afa, 0x0000 }, + { 0x9afb, 0x0000 }, + { 0x9afc, 0x0000 }, + { 0x9afd, 0x0000 }, + { 0x9afe, 0x0000 }, + { 0x9aff, 0x0000 }, + { 0x9b00, 0x0000 }, + { 0x9b01, 0x0000 }, + { 0x9b02, 0x0000 }, + { 0x9b03, 0x0000 }, + { 0x9b04, 0x0000 }, + { 0x9b05, 0x0000 }, + { 0x9b06, 0x0000 }, + { 0x9b07, 0x0000 }, + { 0x9b08, 0x0000 }, + { 0x9b09, 0x0000 }, + { 0x9b0a, 0x0000 }, + { 0x9b0b, 0x0000 }, + { 0x9b0c, 0x0000 }, + { 0x9b0d, 0x0000 }, + { 0x9b0e, 0x0000 }, + { 0x9b0f, 0x0000 }, + { 0x9b10, 0x0000 }, + { 0x9b11, 0x0000 }, + { 0x9b12, 0x0000 }, + { 0x9b13, 0x0000 }, + { 0x9b14, 0x0000 }, + { 0x9b15, 0x0000 }, + { 0x9b16, 0x0000 }, + { 0x9b17, 0x0000 }, + { 0x9b18, 0x0000 }, + { 0x9b19, 0x0000 }, + { 0x9b1a, 0x0000 }, + { 0x9b1b, 0x0000 }, + { 0x9b1c, 0x0000 }, + { 0x9b1d, 0x0000 }, + { 0x9b1e, 0x0000 }, + { 0x9b1f, 0x0000 }, + { 0x9b20, 0x0000 }, + { 0x9b21, 0x0000 }, + { 0x9b22, 0x0000 }, + { 0x9b23, 0x0000 }, + { 0x9b24, 0x0000 }, + { 0x9b25, 0x0000 }, + { 0x9b26, 0x0000 }, + { 0x9b27, 0x0000 }, + { 0x9b28, 0x0000 }, + { 0x9b29, 0x0000 }, + { 0x9b2a, 0x0000 }, + { 0x9b2b, 0x0000 }, + { 0x9b2c, 0x0000 }, + { 0x9b2d, 0x0000 }, + { 0x9b2e, 0x0000 }, + { 0x9b2f, 0x0000 }, + { 0x9b30, 0x0000 }, + { 0x9b31, 0x0000 }, + { 0x9b32, 0x0000 }, + { 0x9b33, 0x0000 }, + { 0x9b34, 0x0000 }, + { 0x9b35, 0x0000 }, + { 0x9b36, 0x0000 }, + { 0x9b37, 0x0000 }, + { 0x9b38, 0x0000 }, + { 0x9b39, 0x0000 }, + { 0x9b3a, 0x0000 }, + { 0x9b3b, 0x0000 }, + { 0x9b3c, 0x0000 }, + { 0x9b3d, 0x0000 }, + { 0x9b3e, 0x0000 }, + { 0x9b3f, 0x0000 }, + { 0x9b40, 0x0000 }, + { 0x9b41, 0x0000 }, + { 0x9b42, 0x0000 }, + { 0x9b43, 0x0000 }, + { 0x9b44, 0x0000 }, + { 0x9b45, 0x0000 }, + { 0x9b46, 0x0000 }, + { 0x9b47, 0x0000 }, + { 0x9b48, 0x0000 }, + { 0x9b49, 0x0000 }, + { 0x9b4a, 0x0000 }, + { 0x9b4b, 0x0000 }, + { 0x9b4c, 0x0000 }, + { 0x9b4d, 0x0000 }, + { 0x9b4e, 0x0000 }, + { 0x9b4f, 0x0000 }, + { 0x9b50, 0x0000 }, + { 0x9b51, 0x0000 }, + { 0x9b52, 0x0000 }, + { 0x9b53, 0x0000 }, + { 0x9b54, 0x0000 }, + { 0x9b55, 0x0000 }, + { 0x9b56, 0x0000 }, + { 0x9b57, 0x0000 }, + { 0x9b58, 0x0000 }, + { 0x9b59, 0x0000 }, + { 0x9b5a, 0x0000 }, + { 0x9b5b, 0x0000 }, + { 0x9b5c, 0x0000 }, + { 0x9b5d, 0x0000 }, + { 0x9b5e, 0x0000 }, + { 0x9b5f, 0x0000 }, + { 0x9b60, 0x0000 }, + { 0x9b61, 0x0000 }, + { 0x9b62, 0x0000 }, + { 0x9b63, 0x0000 }, + { 0x9b64, 0x0000 }, + { 0x9b65, 0x0000 }, + { 0x9b66, 0x0000 }, + { 0x9b67, 0x0000 }, + { 0x9b68, 0x0000 }, + { 0x9b69, 0x0000 }, + { 0x9b6a, 0x0000 }, + { 0x9b6b, 0x0000 }, + { 0x9b6c, 0x0000 }, + { 0x9b6d, 0x0000 }, + { 0x9b6e, 0x0000 }, + { 0x9b6f, 0x0000 }, + { 0x9b70, 0x0000 }, + { 0x9b71, 0x0000 }, + { 0x9b72, 0x0000 }, + { 0x9b73, 0x0000 }, + { 0x9b74, 0x0000 }, + { 0x9b75, 0x0000 }, + { 0x9b76, 0x0000 }, + { 0x9b77, 0x0000 }, + { 0x9b78, 0x0000 }, + { 0x9b79, 0x0000 }, + { 0x9b7a, 0x0000 }, + { 0x9b7b, 0x0000 }, + { 0x9b7c, 0x0000 }, + { 0x9b7d, 0x0000 }, + { 0x9b7e, 0x0000 }, + { 0x9b7f, 0x0000 }, + { 0x9b80, 0x0000 }, + { 0x9b81, 0x0000 }, + { 0x9b82, 0x0000 }, + { 0x9b83, 0x0000 }, + { 0x9b84, 0x0000 }, + { 0x9b85, 0x0000 }, + { 0x9b86, 0x0000 }, + { 0x9b87, 0x0000 }, + { 0x9b88, 0x0000 }, + { 0x9b89, 0x0000 }, + { 0x9b8a, 0x0000 }, + { 0x9b8b, 0x0000 }, + { 0x9b8c, 0x0000 }, + { 0x9b8d, 0x0000 }, + { 0x9b8e, 0x0000 }, + { 0x9b8f, 0x0000 }, + { 0x9b90, 0x0000 }, + { 0x9b91, 0x0000 }, + { 0x9b92, 0x0000 }, + { 0x9b93, 0x0000 }, + { 0x9b94, 0x0000 }, + { 0x9b95, 0x0000 }, + { 0x9b96, 0x0000 }, + { 0x9b97, 0x0000 }, + { 0x9b98, 0x0000 }, + { 0x9b99, 0x0000 }, + { 0x9b9a, 0x0000 }, + { 0x9b9b, 0x0000 }, + { 0x9b9c, 0x0000 }, + { 0x9b9d, 0x0000 }, + { 0x9b9e, 0x0000 }, + { 0x9b9f, 0x0000 }, + { 0x9ba0, 0x0000 }, + { 0x9ba1, 0x0000 }, + { 0x9ba2, 0x0000 }, + { 0x9ba3, 0x0000 }, + { 0x9ba4, 0x0000 }, + { 0x9ba5, 0x0000 }, + { 0x9ba6, 0x0000 }, + { 0x9ba7, 0x0000 }, + { 0x9ba8, 0x0000 }, + { 0x9ba9, 0x0000 }, + { 0x9baa, 0x0000 }, + { 0x9bab, 0x0000 }, + { 0x9bac, 0x0000 }, + { 0x9bad, 0x0000 }, + { 0x9bae, 0x0000 }, + { 0x9baf, 0x0000 }, + { 0x9bb0, 0x0000 }, + { 0x9bb1, 0x0000 }, + { 0x9bb2, 0x0000 }, + { 0x9bb3, 0x0000 }, + { 0x9bb4, 0x0000 }, + { 0x9bb5, 0x0000 }, + { 0x9bb6, 0x0000 }, + { 0x9bb7, 0x0000 }, + { 0x9bb8, 0x0000 }, + { 0x9bb9, 0x0000 }, + { 0x9bba, 0x0000 }, + { 0x9bbb, 0x0000 }, + { 0x9bbc, 0x0000 }, + { 0x9bbd, 0x0000 }, + { 0x9bbe, 0x0000 }, + { 0x9bbf, 0x0000 }, + { 0x9bc0, 0x0000 }, + { 0x9bc1, 0x0000 }, + { 0x9bc2, 0x0000 }, + { 0x9bc3, 0x0000 }, + { 0x9bc4, 0x0000 }, + { 0x9bc5, 0x0000 }, + { 0x9bc6, 0x0000 }, + { 0x9bc7, 0x0000 }, + { 0x9bc8, 0x0000 }, + { 0x9bc9, 0x0000 }, + { 0x9bca, 0x0000 }, + { 0x9bcb, 0x0000 }, + { 0x9bcc, 0x0000 }, + { 0x9bcd, 0x0000 }, + { 0x9bce, 0x0000 }, + { 0x9bcf, 0x0000 }, + { 0x9bd0, 0x0000 }, + { 0x9bd1, 0x0000 }, + { 0x9bd2, 0x0000 }, + { 0x9bd3, 0x0000 }, + { 0x9bd4, 0x0000 }, + { 0x9bd5, 0x0000 }, + { 0x9bd6, 0x0000 }, + { 0x9bd7, 0x0000 }, + { 0x9bd8, 0x0000 }, + { 0x9bd9, 0x0000 }, + { 0x9bda, 0x0000 }, + { 0x9bdb, 0x0000 }, + { 0x9bdc, 0x0000 }, + { 0x9bdd, 0x0000 }, + { 0x9bde, 0x0000 }, + { 0x9bdf, 0x0000 }, + { 0x9be0, 0x0000 }, + { 0x9be1, 0x0000 }, + { 0x9be2, 0x0000 }, + { 0x9be3, 0x0000 }, + { 0x9be4, 0x0000 }, + { 0x9be5, 0x0000 }, + { 0x9be6, 0x0000 }, + { 0x9be7, 0x0000 }, + { 0x9be8, 0x0000 }, + { 0x9be9, 0x0000 }, + { 0x9bea, 0x0000 }, + { 0x9beb, 0x0000 }, + { 0x9bec, 0x0000 }, + { 0x9bed, 0x0000 }, + { 0x9bee, 0x0000 }, + { 0x9bef, 0x0000 }, + { 0x9bf0, 0x0000 }, + { 0x9bf1, 0x0000 }, + { 0x9bf2, 0x0000 }, + { 0x9bf3, 0x0000 }, + { 0x9bf4, 0x0000 }, + { 0x9bf5, 0x0000 }, + { 0x9bf6, 0x0000 }, + { 0x9bf7, 0x0000 }, + { 0x9bf8, 0x0000 }, + { 0x9bf9, 0x0000 }, + { 0x9bfa, 0x0000 }, + { 0x9bfb, 0x0000 }, + { 0x9bfc, 0x0000 }, + { 0x9bfd, 0x0000 }, + { 0x9bfe, 0x0000 }, + { 0x9bff, 0x0000 }, + { 0x9c00, 0x0000 }, + { 0x9c01, 0x0000 }, + { 0x9c02, 0x0000 }, + { 0x9c03, 0x0000 }, + { 0x9c04, 0x0000 }, + { 0x9c05, 0x0000 }, + { 0x9c06, 0x0000 }, + { 0x9c07, 0x0000 }, + { 0x9c08, 0x0000 }, + { 0x9c09, 0x0000 }, + { 0x9c0a, 0x0000 }, + { 0x9c0b, 0x0000 }, + { 0x9c0c, 0x0000 }, + { 0x9c0d, 0x0000 }, + { 0x9c0e, 0x0000 }, + { 0x9c0f, 0x0000 }, + { 0x9c10, 0x0000 }, + { 0x9c11, 0x0000 }, + { 0x9c12, 0x0000 }, + { 0x9c13, 0x0000 }, + { 0x9c14, 0x0000 }, + { 0x9c15, 0x0000 }, + { 0x9c16, 0x0000 }, + { 0x9c17, 0x0000 }, + { 0x9c18, 0x0000 }, + { 0x9c19, 0x0000 }, + { 0x9c1a, 0x0000 }, + { 0x9c1b, 0x0000 }, + { 0x9c1c, 0x0000 }, + { 0x9c1d, 0x0000 }, + { 0x9c1e, 0x0000 }, + { 0x9c1f, 0x0000 }, + { 0x9c20, 0x0000 }, + { 0x9c21, 0x0000 }, + { 0x9c22, 0x0000 }, + { 0x9c23, 0x0000 }, + { 0x9c24, 0x0000 }, + { 0x9c25, 0x0000 }, + { 0x9c26, 0x0000 }, + { 0x9c27, 0x0000 }, + { 0x9c28, 0x0000 }, + { 0x9c29, 0x0000 }, + { 0x9c2a, 0x0000 }, + { 0x9c2b, 0x0000 }, + { 0x9c2c, 0x0000 }, + { 0x9c2d, 0x0000 }, + { 0x9c2e, 0x0000 }, + { 0x9c2f, 0x0000 }, + { 0x9c30, 0x0000 }, + { 0x9c31, 0x0000 }, + { 0x9c32, 0x0000 }, + { 0x9c33, 0x0000 }, + { 0x9c34, 0x0000 }, + { 0x9c35, 0x0000 }, + { 0x9c36, 0x0000 }, + { 0x9c37, 0x0000 }, + { 0x9c38, 0x0000 }, + { 0x9c39, 0x0000 }, + { 0x9c3a, 0x0000 }, + { 0x9c3b, 0x0000 }, + { 0x9c3c, 0x0000 }, + { 0x9c3d, 0x0000 }, + { 0x9c3e, 0x0000 }, + { 0x9c3f, 0x0000 }, + { 0x9c40, 0x0000 }, + { 0x9c41, 0x0000 }, + { 0x9c42, 0x0000 }, + { 0x9c43, 0x0000 }, + { 0x9c44, 0x0000 }, + { 0x9c45, 0x0000 }, + { 0x9c46, 0x0000 }, + { 0x9c47, 0x0000 }, + { 0x9c48, 0x0000 }, + { 0x9c49, 0x0000 }, + { 0x9c4a, 0x0000 }, + { 0x9c4b, 0x0000 }, + { 0x9c4c, 0x0000 }, + { 0x9c4d, 0x0000 }, + { 0x9c4e, 0x0000 }, + { 0x9c4f, 0x0000 }, + { 0x9c50, 0x0000 }, + { 0x9c51, 0x0000 }, + { 0x9c52, 0x0000 }, + { 0x9c53, 0x0000 }, + { 0x9c54, 0x0000 }, + { 0x9c55, 0x0000 }, + { 0x9c56, 0x0000 }, + { 0x9c57, 0x0000 }, + { 0x9c58, 0x0000 }, + { 0x9c59, 0x0000 }, + { 0x9c5a, 0x0000 }, + { 0x9c5b, 0x0000 }, + { 0x9c5c, 0x0000 }, + { 0x9c5d, 0x0000 }, + { 0x9c5e, 0x0000 }, + { 0x9c5f, 0x0000 }, + { 0x9c60, 0x0000 }, + { 0x9c61, 0x0000 }, + { 0x9c62, 0x0000 }, + { 0x9c63, 0x0000 }, + { 0x9c64, 0x0000 }, + { 0x9c65, 0x0000 }, + { 0x9c66, 0x0000 }, + { 0x9c67, 0x0000 }, + { 0x9c68, 0x0000 }, + { 0x9c69, 0x0000 }, + { 0x9c6a, 0x0000 }, + { 0x9c6b, 0x0000 }, + { 0x9c6c, 0x0000 }, + { 0x9c6d, 0x0000 }, + { 0x9c6e, 0x0000 }, + { 0x9c6f, 0x0000 }, + { 0x9c70, 0x0000 }, + { 0x9c71, 0x0000 }, + { 0x9c72, 0x0000 }, + { 0x9c73, 0x0000 }, + { 0x9c74, 0x0000 }, + { 0x9c75, 0x0000 }, + { 0x9c76, 0x0000 }, + { 0x9c77, 0x0000 }, + { 0x9c78, 0x0000 }, + { 0x9c79, 0x0000 }, + { 0x9c7a, 0x0000 }, + { 0x9c7b, 0x0000 }, + { 0x9c7c, 0x0000 }, + { 0x9c7d, 0x0000 }, + { 0x9c7e, 0x0000 }, + { 0x9c7f, 0x0000 }, + { 0x9c80, 0x0000 }, + { 0x9c81, 0x0000 }, + { 0x9c82, 0x0000 }, + { 0x9c83, 0x0000 }, + { 0x9c84, 0x0000 }, + { 0x9c85, 0x0000 }, + { 0x9c86, 0x0000 }, + { 0x9c87, 0x0000 }, + { 0x9c88, 0x0000 }, + { 0x9c89, 0x0000 }, + { 0x9c8a, 0x0000 }, + { 0x9c8b, 0x0000 }, + { 0x9c8c, 0x0000 }, + { 0x9c8d, 0x0000 }, + { 0x9c8e, 0x0000 }, + { 0x9c8f, 0x0000 }, + { 0x9c90, 0x0000 }, + { 0x9c91, 0x0000 }, + { 0x9c92, 0x0000 }, + { 0x9c93, 0x0000 }, + { 0x9c94, 0x0000 }, + { 0x9c95, 0x0000 }, + { 0x9c96, 0x0000 }, + { 0x9c97, 0x0000 }, + { 0x9c98, 0x0000 }, + { 0x9c99, 0x0000 }, + { 0x9c9a, 0x0000 }, + { 0x9c9b, 0x0000 }, + { 0x9c9c, 0x0000 }, + { 0x9c9d, 0x0000 }, + { 0x9c9e, 0x0000 }, + { 0x9c9f, 0x0000 }, + { 0x9ca0, 0x0000 }, + { 0x9ca1, 0x0000 }, + { 0x9ca2, 0x0000 }, + { 0x9ca3, 0x0000 }, + { 0x9ca4, 0x0000 }, + { 0x9ca5, 0x0000 }, + { 0x9ca6, 0x0000 }, + { 0x9ca7, 0x0000 }, + { 0x9ca8, 0x0000 }, + { 0x9ca9, 0x0000 }, + { 0x9caa, 0x0000 }, + { 0x9cab, 0x0000 }, + { 0x9cac, 0x0000 }, + { 0x9cad, 0x0000 }, + { 0x9cae, 0x0000 }, + { 0x9caf, 0x0000 }, + { 0x9cb0, 0x0000 }, + { 0x9cb1, 0x0000 }, + { 0x9cb2, 0x0000 }, + { 0x9cb3, 0x0000 }, + { 0x9cb4, 0x0000 }, + { 0x9cb5, 0x0000 }, + { 0x9cb6, 0x0000 }, + { 0x9cb7, 0x0000 }, + { 0x9cb8, 0x0000 }, + { 0x9cb9, 0x0000 }, + { 0x9cba, 0x0000 }, + { 0x9cbb, 0x0000 }, + { 0x9cbc, 0x0000 }, + { 0x9cbd, 0x0000 }, + { 0x9cbe, 0x0000 }, + { 0x9cbf, 0x0000 }, + { 0x9cc0, 0x0000 }, + { 0x9cc1, 0x0000 }, + { 0x9cc2, 0x0000 }, + { 0x9cc3, 0x0000 }, + { 0x9cc4, 0x0000 }, + { 0x9cc5, 0x0000 }, + { 0x9cc6, 0x0000 }, + { 0x9cc7, 0x0000 }, + { 0x9cc8, 0x0000 }, + { 0x9cc9, 0x0000 }, + { 0x9cca, 0x0000 }, + { 0x9ccb, 0x0000 }, + { 0x9ccc, 0x0000 }, + { 0x9ccd, 0x0000 }, + { 0x9cce, 0x0000 }, + { 0x9ccf, 0x0000 }, + { 0x9cd0, 0x0000 }, + { 0x9cd1, 0x0000 }, + { 0x9cd2, 0x0000 }, + { 0x9cd3, 0x0000 }, + { 0x9cd4, 0x0000 }, + { 0x9cd5, 0x0000 }, + { 0x9cd6, 0x0000 }, + { 0x9cd7, 0x0000 }, + { 0x9cd8, 0x0000 }, + { 0x9cd9, 0x0000 }, + { 0x9cda, 0x0000 }, + { 0x9cdb, 0x0000 }, + { 0x9cdc, 0x0000 }, + { 0x9cdd, 0x0000 }, + { 0x9cde, 0x0000 }, + { 0x9cdf, 0x0000 }, + { 0x9ce0, 0x0000 }, + { 0x9ce1, 0x0000 }, + { 0x9ce2, 0x0000 }, + { 0x9ce3, 0x0000 }, + { 0x9ce4, 0x0000 }, + { 0x9ce5, 0x0000 }, + { 0x9ce6, 0x0000 }, + { 0x9ce7, 0x0000 }, + { 0x9ce8, 0x0000 }, + { 0x9ce9, 0x0000 }, + { 0x9cea, 0x0000 }, + { 0x9ceb, 0x0000 }, + { 0x9cec, 0x0000 }, + { 0x9ced, 0x0000 }, + { 0x9cee, 0x0000 }, + { 0x9cef, 0x0000 }, + { 0x9cf0, 0x0000 }, + { 0x9cf1, 0x0000 }, + { 0x9cf2, 0x0000 }, + { 0x9cf3, 0x0000 }, + { 0x9cf4, 0x0000 }, + { 0x9cf5, 0x0000 }, + { 0x9cf6, 0x0000 }, + { 0x9cf7, 0x0000 }, + { 0x9cf8, 0x0000 }, + { 0x9cf9, 0x0000 }, + { 0x9cfa, 0x0000 }, + { 0x9cfb, 0x0000 }, + { 0x9cfc, 0x0000 }, + { 0x9cfd, 0x0000 }, + { 0x9cfe, 0x0000 }, + { 0x9cff, 0x0000 }, + { 0x9d00, 0x0000 }, + { 0x9d01, 0x0000 }, + { 0x9d02, 0x0000 }, + { 0x9d03, 0x0000 }, + { 0x9d04, 0x0000 }, + { 0x9d05, 0x0000 }, + { 0x9d06, 0x0000 }, + { 0x9d07, 0x0000 }, + { 0x9d08, 0x0000 }, + { 0x9d09, 0x0000 }, + { 0x9d0a, 0x0000 }, + { 0x9d0b, 0x0000 }, + { 0x9d0c, 0x0000 }, + { 0x9d0d, 0x0000 }, + { 0x9d0e, 0x0000 }, + { 0x9d0f, 0x0000 }, + { 0x9d10, 0x0000 }, + { 0x9d11, 0x0000 }, + { 0x9d12, 0x0000 }, + { 0x9d13, 0x0000 }, + { 0x9d14, 0x0000 }, + { 0x9d15, 0x0000 }, + { 0x9d16, 0x0000 }, + { 0x9d17, 0x0000 }, + { 0x9d18, 0x0000 }, + { 0x9d19, 0x0000 }, + { 0x9d1a, 0x0000 }, + { 0x9d1b, 0x0000 }, + { 0x9d1c, 0x0000 }, + { 0x9d1d, 0x0000 }, + { 0x9d1e, 0x0000 }, + { 0x9d1f, 0x0000 }, + { 0x9d20, 0x0000 }, + { 0x9d21, 0x0000 }, + { 0x9d22, 0x0000 }, + { 0x9d23, 0x0000 }, + { 0x9d24, 0x0000 }, + { 0x9d25, 0x0000 }, + { 0x9d26, 0x0000 }, + { 0x9d27, 0x0000 }, + { 0x9d28, 0x0000 }, + { 0x9d29, 0x0000 }, + { 0x9d2a, 0x0000 }, + { 0x9d2b, 0x0000 }, + { 0x9d2c, 0x0000 }, + { 0x9d2d, 0x0000 }, + { 0x9d2e, 0x0000 }, + { 0x9d2f, 0x0000 }, + { 0x9d30, 0x0000 }, + { 0x9d31, 0x0000 }, + { 0x9d32, 0x0000 }, + { 0x9d33, 0x0000 }, + { 0x9d34, 0x0000 }, + { 0x9d35, 0x0000 }, + { 0x9d36, 0x0000 }, + { 0x9d37, 0x0000 }, + { 0x9d38, 0x0000 }, + { 0x9d39, 0x0000 }, + { 0x9d3a, 0x0000 }, + { 0x9d3b, 0x0000 }, + { 0x9d3c, 0x0000 }, + { 0x9d3d, 0x0000 }, + { 0x9d3e, 0x0000 }, + { 0x9d3f, 0x0000 }, + { 0x9d40, 0x0000 }, + { 0x9d41, 0x0000 }, + { 0x9d42, 0x0000 }, + { 0x9d43, 0x0000 }, + { 0x9d44, 0x0000 }, + { 0x9d45, 0x0000 }, + { 0x9d46, 0x0000 }, + { 0x9d47, 0x0000 }, + { 0x9d48, 0x0000 }, + { 0x9d49, 0x0000 }, + { 0x9d4a, 0x0000 }, + { 0x9d4b, 0x0000 }, + { 0x9d4c, 0x0000 }, + { 0x9d4d, 0x0000 }, + { 0x9d4e, 0x0000 }, + { 0x9d4f, 0x0000 }, + { 0x9d50, 0x0000 }, + { 0x9d51, 0x0000 }, + { 0x9d52, 0x0000 }, + { 0x9d53, 0x0000 }, + { 0x9d54, 0x0000 }, + { 0x9d55, 0x0000 }, + { 0x9d56, 0x0000 }, + { 0x9d57, 0x0000 }, + { 0x9d58, 0x0000 }, + { 0x9d59, 0x0000 }, + { 0x9d5a, 0x0000 }, + { 0x9d5b, 0x0000 }, + { 0x9d5c, 0x0000 }, + { 0x9d5d, 0x0000 }, + { 0x9d5e, 0x0000 }, + { 0x9d5f, 0x0000 }, + { 0x9d60, 0x0000 }, + { 0x9d61, 0x0000 }, + { 0x9d62, 0x0000 }, + { 0x9d63, 0x0000 }, + { 0x9d64, 0x0000 }, + { 0x9d65, 0x0000 }, + { 0x9d66, 0x0000 }, + { 0x9d67, 0x0000 }, + { 0x9d68, 0x0000 }, + { 0x9d69, 0x0000 }, + { 0x9d6a, 0x0000 }, + { 0x9d6b, 0x0000 }, + { 0x9d6c, 0x0000 }, + { 0x9d6d, 0x0000 }, + { 0x9d6e, 0x0000 }, + { 0x9d6f, 0x0000 }, + { 0x9d70, 0x0000 }, + { 0x9d71, 0x0000 }, + { 0x9d72, 0x0000 }, + { 0x9d73, 0x0000 }, + { 0x9d74, 0x0000 }, + { 0x9d75, 0x0000 }, + { 0x9d76, 0x0000 }, + { 0x9d77, 0x0000 }, + { 0x9d78, 0x0000 }, + { 0x9d79, 0x0000 }, + { 0x9d7a, 0x0000 }, + { 0x9d7b, 0x0000 }, + { 0x9d7c, 0x0000 }, + { 0x9d7d, 0x0000 }, + { 0x9d7e, 0x0000 }, + { 0x9d7f, 0x0000 }, + { 0x9d80, 0x0000 }, + { 0x9d81, 0x0000 }, + { 0x9d82, 0x0000 }, + { 0x9d83, 0x0000 }, + { 0x9d84, 0x0000 }, + { 0x9d85, 0x0000 }, + { 0x9d86, 0x0000 }, + { 0x9d87, 0x0000 }, + { 0x9d88, 0x0000 }, + { 0x9d89, 0x0000 }, + { 0x9d8a, 0x0000 }, + { 0x9d8b, 0x0000 }, + { 0x9d8c, 0x0000 }, + { 0x9d8d, 0x0000 }, + { 0x9d8e, 0x0000 }, + { 0x9d8f, 0x0000 }, + { 0x9d90, 0x0000 }, + { 0x9d91, 0x0000 }, + { 0x9d92, 0x0000 }, + { 0x9d93, 0x0000 }, + { 0x9d94, 0x0000 }, + { 0x9d95, 0x0000 }, + { 0x9d96, 0x0000 }, + { 0x9d97, 0x0000 }, + { 0x9d98, 0x0000 }, + { 0x9d99, 0x0000 }, + { 0x9d9a, 0x0000 }, + { 0x9d9b, 0x0000 }, + { 0x9d9c, 0x0000 }, + { 0x9d9d, 0x0000 }, + { 0x9d9e, 0x0000 }, + { 0x9d9f, 0x0000 }, + { 0x9da0, 0x0000 }, + { 0x9da1, 0x0000 }, + { 0x9da2, 0x0000 }, + { 0x9da3, 0x0000 }, + { 0x9da4, 0x0000 }, + { 0x9da5, 0x0000 }, + { 0x9da6, 0x0000 }, + { 0x9da7, 0x0000 }, + { 0x9da8, 0x0000 }, + { 0x9da9, 0x0000 }, + { 0x9daa, 0x0000 }, + { 0x9dab, 0x0000 }, + { 0x9dac, 0x0000 }, + { 0x9dad, 0x0000 }, + { 0x9dae, 0x0000 }, + { 0x9daf, 0x0000 }, + { 0x9db0, 0x0000 }, + { 0x9db1, 0x0000 }, + { 0x9db2, 0x0000 }, + { 0x9db3, 0x0000 }, + { 0x9db4, 0x0000 }, + { 0x9db5, 0x0000 }, + { 0x9db6, 0x0000 }, + { 0x9db7, 0x0000 }, + { 0x9db8, 0x0000 }, + { 0x9db9, 0x0000 }, + { 0x9dba, 0x0000 }, + { 0x9dbb, 0x0000 }, + { 0x9dbc, 0x0000 }, + { 0x9dbd, 0x0000 }, + { 0x9dbe, 0x0000 }, + { 0x9dbf, 0x0000 }, + { 0x9dc0, 0x0000 }, + { 0x9dc1, 0x0000 }, + { 0x9dc2, 0x0000 }, + { 0x9dc3, 0x0000 }, + { 0x9dc4, 0x0000 }, + { 0x9dc5, 0x0000 }, + { 0x9dc6, 0x0000 }, + { 0x9dc7, 0x0000 }, + { 0x9dc8, 0x0000 }, + { 0x9dc9, 0x0000 }, + { 0x9dca, 0x0000 }, + { 0x9dcb, 0x0000 }, + { 0x9dcc, 0x0000 }, + { 0x9dcd, 0x0000 }, + { 0x9dce, 0x0000 }, + { 0x9dcf, 0x0000 }, + { 0x9dd0, 0x0000 }, + { 0x9dd1, 0x0000 }, + { 0x9dd2, 0x0000 }, + { 0x9dd3, 0x0000 }, + { 0x9dd4, 0x0000 }, + { 0x9dd5, 0x0000 }, + { 0x9dd6, 0x0000 }, + { 0x9dd7, 0x0000 }, + { 0x9dd8, 0x0000 }, + { 0x9dd9, 0x0000 }, + { 0x9dda, 0x0000 }, + { 0x9ddb, 0x0000 }, + { 0x9ddc, 0x0000 }, + { 0x9ddd, 0x0000 }, + { 0x9dde, 0x0000 }, + { 0x9ddf, 0x0000 }, + { 0x9de0, 0x0000 }, + { 0x9de1, 0x0000 }, + { 0x9de2, 0x0000 }, + { 0x9de3, 0x0000 }, + { 0x9de4, 0x0000 }, + { 0x9de5, 0x0000 }, + { 0x9de6, 0x0000 }, + { 0x9de7, 0x0000 }, + { 0x9de8, 0x0000 }, + { 0x9de9, 0x0000 }, + { 0x9dea, 0x0000 }, + { 0x9deb, 0x0000 }, + { 0x9dec, 0x0000 }, + { 0x9ded, 0x0000 }, + { 0x9dee, 0x0000 }, + { 0x9def, 0x0000 }, + { 0x9df0, 0x0000 }, + { 0x9df1, 0x0000 }, + { 0x9df2, 0x0000 }, + { 0x9df3, 0x0000 }, + { 0x9df4, 0x0000 }, + { 0x9df5, 0x0000 }, + { 0x9df6, 0x0000 }, + { 0x9df7, 0x0000 }, + { 0x9df8, 0x0000 }, + { 0x9df9, 0x0000 }, + { 0x9dfa, 0x0000 }, + { 0x9dfb, 0x0000 }, + { 0x9dfc, 0x0000 }, + { 0x9dfd, 0x0000 }, + { 0x9dfe, 0x0000 }, + { 0x9dff, 0x0000 }, + { 0x9e00, 0x0000 }, + { 0x9e01, 0x0000 }, + { 0x9e02, 0x0000 }, + { 0x9e03, 0x0000 }, + { 0x9e04, 0x0000 }, + { 0x9e05, 0x0000 }, + { 0x9e06, 0x0000 }, + { 0x9e07, 0x0000 }, + { 0x9e08, 0x0000 }, + { 0x9e09, 0x0000 }, + { 0x9e0a, 0x0000 }, + { 0x9e0b, 0x0000 }, + { 0x9e0c, 0x0000 }, + { 0x9e0d, 0x0000 }, + { 0x9e0e, 0x0000 }, + { 0x9e0f, 0x0000 }, + { 0x9e10, 0x0000 }, + { 0x9e11, 0x0000 }, + { 0x9e12, 0x0000 }, + { 0x9e13, 0x0000 }, + { 0x9e14, 0x0000 }, + { 0x9e15, 0x0000 }, + { 0x9e16, 0x0000 }, + { 0x9e17, 0x0000 }, + { 0x9e18, 0x0000 }, + { 0x9e19, 0x0000 }, + { 0x9e1a, 0x0000 }, + { 0x9e1b, 0x0000 }, + { 0x9e1c, 0x0000 }, + { 0x9e1d, 0x0000 }, + { 0x9e1e, 0x0000 }, + { 0x9e1f, 0x0000 }, + { 0x9e20, 0x0000 }, + { 0x9e21, 0x0000 }, + { 0x9e22, 0x0000 }, + { 0x9e23, 0x0000 }, + { 0x9e24, 0x0000 }, + { 0x9e25, 0x0000 }, + { 0x9e26, 0x0000 }, + { 0x9e27, 0x0000 }, + { 0x9e28, 0x0000 }, + { 0x9e29, 0x0000 }, + { 0x9e2a, 0x0000 }, + { 0x9e2b, 0x0000 }, + { 0x9e2c, 0x0000 }, + { 0x9e2d, 0x0000 }, + { 0x9e2e, 0x0000 }, + { 0x9e2f, 0x0000 }, + { 0x9e30, 0x0000 }, + { 0x9e31, 0x0000 }, + { 0x9e32, 0x0000 }, + { 0x9e33, 0x0000 }, + { 0x9e34, 0x0000 }, + { 0x9e35, 0x0000 }, + { 0x9e36, 0x0000 }, + { 0x9e37, 0x0000 }, + { 0x9e38, 0x0000 }, + { 0x9e39, 0x0000 }, + { 0x9e3a, 0x0000 }, + { 0x9e3b, 0x0000 }, + { 0x9e3c, 0x0000 }, + { 0x9e3d, 0x0000 }, + { 0x9e3e, 0x0000 }, + { 0x9e3f, 0x0000 }, + { 0x9e40, 0x0000 }, + { 0x9e41, 0x0000 }, + { 0x9e42, 0x0000 }, + { 0x9e43, 0x0000 }, + { 0x9e44, 0x0000 }, + { 0x9e45, 0x0000 }, + { 0x9e46, 0x0000 }, + { 0x9e47, 0x0000 }, + { 0x9e48, 0x0000 }, + { 0x9e49, 0x0000 }, + { 0x9e4a, 0x0000 }, + { 0x9e4b, 0x0000 }, + { 0x9e4c, 0x0000 }, + { 0x9e4d, 0x0000 }, + { 0x9e4e, 0x0000 }, + { 0x9e4f, 0x0000 }, + { 0x9e50, 0x0000 }, + { 0x9e51, 0x0000 }, + { 0x9e52, 0x0000 }, + { 0x9e53, 0x0000 }, + { 0x9e54, 0x0000 }, + { 0x9e55, 0x0000 }, + { 0x9e56, 0x0000 }, + { 0x9e57, 0x0000 }, + { 0x9e58, 0x0000 }, + { 0x9e59, 0x0000 }, + { 0x9e5a, 0x0000 }, + { 0x9e5b, 0x0000 }, + { 0x9e5c, 0x0000 }, + { 0x9e5d, 0x0000 }, + { 0x9e5e, 0x0000 }, + { 0x9e5f, 0x0000 }, + { 0x9e60, 0x0000 }, + { 0x9e61, 0x0000 }, + { 0x9e62, 0x0000 }, + { 0x9e63, 0x0000 }, + { 0x9e64, 0x0000 }, + { 0x9e65, 0x0000 }, + { 0x9e66, 0x0000 }, + { 0x9e67, 0x0000 }, + { 0x9e68, 0x0000 }, + { 0x9e69, 0x0000 }, + { 0x9e6a, 0x0000 }, + { 0x9e6b, 0x0000 }, + { 0x9e6c, 0x0000 }, + { 0x9e6d, 0x0000 }, + { 0x9e6e, 0x0000 }, + { 0x9e6f, 0x0000 }, + { 0x9e70, 0x0000 }, + { 0x9e71, 0x0000 }, + { 0x9e72, 0x0000 }, + { 0x9e73, 0x0000 }, + { 0x9e74, 0x0000 }, + { 0x9e75, 0x0000 }, + { 0x9e76, 0x0000 }, + { 0x9e77, 0x0000 }, + { 0x9e78, 0x0000 }, + { 0x9e79, 0x0000 }, + { 0x9e7a, 0x0000 }, + { 0x9e7b, 0x0000 }, + { 0x9e7c, 0x0000 }, + { 0x9e7d, 0x0000 }, + { 0x9e7e, 0x0000 }, + { 0x9e7f, 0x0000 }, + { 0x9e80, 0x0000 }, + { 0x9e81, 0x0000 }, + { 0x9e82, 0x0000 }, + { 0x9e83, 0x0000 }, + { 0x9e84, 0x0000 }, + { 0x9e85, 0x0000 }, + { 0x9e86, 0x0000 }, + { 0x9e87, 0x0000 }, + { 0x9e88, 0x0000 }, + { 0x9e89, 0x0000 }, + { 0x9e8a, 0x0000 }, + { 0x9e8b, 0x0000 }, + { 0x9e8c, 0x0000 }, + { 0x9e8d, 0x0000 }, + { 0x9e8e, 0x0000 }, + { 0x9e8f, 0x0000 }, + { 0x9e90, 0x0000 }, + { 0x9e91, 0x0000 }, + { 0x9e92, 0x0000 }, + { 0x9e93, 0x0000 }, + { 0x9e94, 0x0000 }, + { 0x9e95, 0x0000 }, + { 0x9e96, 0x0000 }, + { 0x9e97, 0x0000 }, + { 0x9e98, 0x0000 }, + { 0x9e99, 0x0000 }, + { 0x9e9a, 0x0000 }, + { 0x9e9b, 0x0000 }, + { 0x9e9c, 0x0000 }, + { 0x9e9d, 0x0000 }, + { 0x9e9e, 0x0000 }, + { 0x9e9f, 0x0000 }, + { 0x9ea0, 0x0000 }, + { 0x9ea1, 0x0000 }, + { 0x9ea2, 0x0000 }, + { 0x9ea3, 0x0000 }, + { 0x9ea4, 0x0000 }, + { 0x9ea5, 0x0000 }, + { 0x9ea6, 0x0000 }, + { 0x9ea7, 0x0000 }, + { 0x9ea8, 0x0000 }, + { 0x9ea9, 0x0000 }, + { 0x9eaa, 0x0000 }, + { 0x9eab, 0x0000 }, + { 0x9eac, 0x0000 }, + { 0x9ead, 0x0000 }, + { 0x9eae, 0x0000 }, + { 0x9eaf, 0x0000 }, + { 0x9eb0, 0x0000 }, + { 0x9eb1, 0x0000 }, + { 0x9eb2, 0x0000 }, + { 0x9eb3, 0x0000 }, + { 0x9eb4, 0x0000 }, + { 0x9eb5, 0x0000 }, + { 0x9eb6, 0x0000 }, + { 0x9eb7, 0x0000 }, + { 0x9eb8, 0x0000 }, + { 0x9eb9, 0x0000 }, + { 0x9eba, 0x0000 }, + { 0x9ebb, 0x0000 }, + { 0x9ebc, 0x0000 }, + { 0x9ebd, 0x0000 }, + { 0x9ebe, 0x0000 }, + { 0x9ebf, 0x0000 }, + { 0x9ec0, 0x0000 }, + { 0x9ec1, 0x0000 }, + { 0x9ec2, 0x0000 }, + { 0x9ec3, 0x0000 }, + { 0x9ec4, 0x0000 }, + { 0x9ec5, 0x0000 }, + { 0x9ec6, 0x0000 }, + { 0x9ec7, 0x0000 }, + { 0x9ec8, 0x0000 }, + { 0x9ec9, 0x0000 }, + { 0x9eca, 0x0000 }, + { 0x9ecb, 0x0000 }, + { 0x9ecc, 0x0000 }, + { 0x9ecd, 0x0000 }, + { 0x9ece, 0x0000 }, + { 0x9ecf, 0x0000 }, + { 0x9ed0, 0x0000 }, + { 0x9ed1, 0x0000 }, + { 0x9ed2, 0x0000 }, + { 0x9ed3, 0x0000 }, + { 0x9ed4, 0x0000 }, + { 0x9ed5, 0x0000 }, + { 0x9ed6, 0x0000 }, + { 0x9ed7, 0x0000 }, + { 0x9ed8, 0x0000 }, + { 0x9ed9, 0x0000 }, + { 0x9eda, 0x0000 }, + { 0x9edb, 0x0000 }, + { 0x9edc, 0x0000 }, + { 0x9edd, 0x0000 }, + { 0x9ede, 0x0000 }, + { 0x9edf, 0x0000 }, + { 0x9ee0, 0x0000 }, + { 0x9ee1, 0x0000 }, + { 0x9ee2, 0x0000 }, + { 0x9ee3, 0x0000 }, + { 0x9ee4, 0x0000 }, + { 0x9ee5, 0x0000 }, + { 0x9ee6, 0x0000 }, + { 0x9ee7, 0x0000 }, + { 0x9ee8, 0x0000 }, + { 0x9ee9, 0x0000 }, + { 0x9eea, 0x0000 }, + { 0x9eeb, 0x0000 }, + { 0x9eec, 0x0000 }, + { 0x9eed, 0x0000 }, + { 0x9eee, 0x0000 }, + { 0x9eef, 0x0000 }, + { 0x9ef0, 0x0000 }, + { 0x9ef1, 0x0000 }, + { 0x9ef2, 0x0000 }, + { 0x9ef3, 0x0000 }, + { 0x9ef4, 0x0000 }, + { 0x9ef5, 0x0000 }, + { 0x9ef6, 0x0000 }, + { 0x9ef7, 0x0000 }, + { 0x9ef8, 0x0000 }, + { 0x9ef9, 0x0000 }, + { 0x9efa, 0x0000 }, + { 0x9efb, 0x0000 }, + { 0x9efc, 0x0000 }, + { 0x9efd, 0x0000 }, + { 0x9efe, 0x0000 }, + { 0x9eff, 0x0000 }, + { 0x9f00, 0x0000 }, + { 0x9f01, 0x0000 }, + { 0x9f02, 0x0000 }, + { 0x9f03, 0x0000 }, + { 0x9f04, 0x0000 }, + { 0x9f05, 0x0000 }, + { 0x9f06, 0x0000 }, + { 0x9f07, 0x0000 }, + { 0x9f08, 0x0000 }, + { 0x9f09, 0x0000 }, + { 0x9f0a, 0x0000 }, + { 0x9f0b, 0x0000 }, + { 0x9f0c, 0x0000 }, + { 0x9f0d, 0x0000 }, + { 0x9f0e, 0x0000 }, + { 0x9f0f, 0x0000 }, + { 0x9f10, 0x0000 }, + { 0x9f11, 0x0000 }, + { 0x9f12, 0x0000 }, + { 0x9f13, 0x0000 }, + { 0x9f14, 0x0000 }, + { 0x9f15, 0x0000 }, + { 0x9f16, 0x0000 }, + { 0x9f17, 0x0000 }, + { 0x9f18, 0x0000 }, + { 0x9f19, 0x0000 }, + { 0x9f1a, 0x0000 }, + { 0x9f1b, 0x0000 }, + { 0x9f1c, 0x0000 }, + { 0x9f1d, 0x0000 }, + { 0x9f1e, 0x0000 }, + { 0x9f1f, 0x0000 }, + { 0x9f20, 0x0000 }, + { 0x9f21, 0x0000 }, + { 0x9f22, 0x0000 }, + { 0x9f23, 0x0000 }, + { 0x9f24, 0x0000 }, + { 0x9f25, 0x0000 }, + { 0x9f26, 0x0000 }, + { 0x9f27, 0x0000 }, + { 0x9f28, 0x0000 }, + { 0x9f29, 0x0000 }, + { 0x9f2a, 0x0000 }, + { 0x9f2b, 0x0000 }, + { 0x9f2c, 0x0000 }, + { 0x9f2d, 0x0000 }, + { 0x9f2e, 0x0000 }, + { 0x9f2f, 0x0000 }, + { 0x9f30, 0x0000 }, + { 0x9f31, 0x0000 }, + { 0x9f32, 0x0000 }, + { 0x9f33, 0x0000 }, + { 0x9f34, 0x0000 }, + { 0x9f35, 0x0000 }, + { 0x9f36, 0x0000 }, + { 0x9f37, 0x0000 }, + { 0x9f38, 0x0000 }, + { 0x9f39, 0x0000 }, + { 0x9f3a, 0x0000 }, + { 0x9f3b, 0x0000 }, + { 0x9f3c, 0x0000 }, + { 0x9f3d, 0x0000 }, + { 0x9f3e, 0x0000 }, + { 0x9f3f, 0x0000 }, + { 0x9f40, 0x0000 }, + { 0x9f41, 0x0000 }, + { 0x9f42, 0x0000 }, + { 0x9f43, 0x0000 }, + { 0x9f44, 0x0000 }, + { 0x9f45, 0x0000 }, + { 0x9f46, 0x0000 }, + { 0x9f47, 0x0000 }, + { 0x9f48, 0x0000 }, + { 0x9f49, 0x0000 }, + { 0x9f4a, 0x0000 }, + { 0x9f4b, 0x0000 }, + { 0x9f4c, 0x0000 }, + { 0x9f4d, 0x0000 }, + { 0x9f4e, 0x0000 }, + { 0x9f4f, 0x0000 }, + { 0x9f50, 0x0000 }, + { 0x9f51, 0x0000 }, + { 0x9f52, 0x0000 }, + { 0x9f53, 0x0000 }, + { 0x9f54, 0x0000 }, + { 0x9f55, 0x0000 }, + { 0x9f56, 0x0000 }, + { 0x9f57, 0x0000 }, + { 0x9f58, 0x0000 }, + { 0x9f59, 0x0000 }, + { 0x9f5a, 0x0000 }, + { 0x9f5b, 0x0000 }, + { 0x9f5c, 0x0000 }, + { 0x9f5d, 0x0000 }, + { 0x9f5e, 0x0000 }, + { 0x9f5f, 0x0000 }, + { 0x9f60, 0x0000 }, + { 0x9f61, 0x0000 }, + { 0x9f62, 0x0000 }, + { 0x9f63, 0x0000 }, + { 0x9f64, 0x0000 }, + { 0x9f65, 0x0000 }, + { 0x9f66, 0x0000 }, + { 0x9f67, 0x0000 }, + { 0x9f68, 0x0000 }, + { 0x9f69, 0x0000 }, + { 0x9f6a, 0x0000 }, + { 0x9f6b, 0x0000 }, + { 0x9f6c, 0x0000 }, + { 0x9f6d, 0x0000 }, + { 0x9f6e, 0x0000 }, + { 0x9f6f, 0x0000 }, + { 0x9f70, 0x0000 }, + { 0x9f71, 0x0000 }, + { 0x9f72, 0x0000 }, + { 0x9f73, 0x0000 }, + { 0x9f74, 0x0000 }, + { 0x9f75, 0x0000 }, + { 0x9f76, 0x0000 }, + { 0x9f77, 0x0000 }, + { 0x9f78, 0x0000 }, + { 0x9f79, 0x0000 }, + { 0x9f7a, 0x0000 }, + { 0x9f7b, 0x0000 }, + { 0x9f7c, 0x0000 }, + { 0x9f7d, 0x0000 }, + { 0x9f7e, 0x0000 }, + { 0x9f7f, 0x0000 }, + { 0x9f80, 0x0000 }, + { 0x9f81, 0x0000 }, + { 0x9f82, 0x0000 }, + { 0x9f83, 0x0000 }, + { 0x9f84, 0x0000 }, + { 0x9f85, 0x0000 }, + { 0x9f86, 0x0000 }, + { 0x9f87, 0x0000 }, + { 0x9f88, 0x0000 }, + { 0x9f89, 0x0000 }, + { 0x9f8a, 0x0000 }, + { 0x9f8b, 0x0000 }, + { 0x9f8c, 0x0000 }, + { 0x9f8d, 0x0000 }, + { 0x9f8e, 0x0000 }, + { 0x9f8f, 0x0000 }, + { 0x9f90, 0x0000 }, + { 0x9f91, 0x0000 }, + { 0x9f92, 0x0000 }, + { 0x9f93, 0x0000 }, + { 0x9f94, 0x0000 }, + { 0x9f95, 0x0000 }, + { 0x9f96, 0x0000 }, + { 0x9f97, 0x0000 }, + { 0x9f98, 0x0000 }, + { 0x9f99, 0x0000 }, + { 0x9f9a, 0x0000 }, + { 0x9f9b, 0x0000 }, + { 0x9f9c, 0x0000 }, + { 0x9f9d, 0x0000 }, + { 0x9f9e, 0x0000 }, + { 0x9f9f, 0x0000 }, + { 0x9fa0, 0x0000 }, + { 0x9fa1, 0x0000 }, + { 0x9fa2, 0x0000 }, + { 0x9fa3, 0x0000 }, + { 0x9fa4, 0x0000 }, + { 0x9fa5, 0x0000 }, + { 0x9fa6, 0x0000 }, + { 0x9fa7, 0x0000 }, + { 0x9fa8, 0x0000 }, + { 0x9fa9, 0x0000 }, + { 0x9faa, 0x0000 }, + { 0x9fab, 0x0000 }, + { 0x9fac, 0x0000 }, + { 0x9fad, 0x0000 }, + { 0x9fae, 0x0000 }, + { 0x9faf, 0x0000 }, + { 0x9fb0, 0x0000 }, + { 0x9fb1, 0x0000 }, + { 0x9fb2, 0x0000 }, + { 0x9fb3, 0x0000 }, + { 0x9fb4, 0x0000 }, + { 0x9fb5, 0x0000 }, + { 0x9fb6, 0x0000 }, + { 0x9fb7, 0x0000 }, + { 0x9fb8, 0x0000 }, + { 0x9fb9, 0x0000 }, + { 0x9fba, 0x0000 }, + { 0x9fbb, 0x0000 }, + { 0x9fbc, 0x0000 }, + { 0x9fbd, 0x0000 }, + { 0x9fbe, 0x0000 }, + { 0x9fbf, 0x0000 }, + { 0x9fc0, 0x0000 }, + { 0x9fc1, 0x0000 }, + { 0x9fc2, 0x0000 }, + { 0x9fc3, 0x0000 }, + { 0x9fc4, 0x0000 }, + { 0x9fc5, 0x0000 }, + { 0x9fc6, 0x0000 }, + { 0x9fc7, 0x0000 }, + { 0x9fc8, 0x0000 }, + { 0x9fc9, 0x0000 }, + { 0x9fca, 0x0000 }, + { 0x9fcb, 0x0000 }, + { 0x9fcc, 0x0000 }, + { 0x9fcd, 0x0000 }, + { 0x9fce, 0x0000 }, + { 0x9fcf, 0x0000 }, + { 0x9fd0, 0x0000 }, + { 0x9fd1, 0x0000 }, + { 0x9fd2, 0x0000 }, + { 0x9fd3, 0x0000 }, + { 0x9fd4, 0x0000 }, + { 0x9fd5, 0x0000 }, + { 0x9fd6, 0x0000 }, + { 0x9fd7, 0x0000 }, + { 0x9fd8, 0x0000 }, + { 0x9fd9, 0x0000 }, + { 0x9fda, 0x0000 }, + { 0x9fdb, 0x0000 }, + { 0x9fdc, 0x0000 }, + { 0x9fdd, 0x0000 }, + { 0x9fde, 0x0000 }, + { 0x9fdf, 0x0000 }, + { 0x9fe0, 0x0000 }, + { 0x9fe1, 0x0000 }, + { 0x9fe2, 0x0000 }, + { 0x9fe3, 0x0000 }, + { 0x9fe4, 0x0000 }, + { 0x9fe5, 0x0000 }, + { 0x9fe6, 0x0000 }, + { 0x9fe7, 0x0000 }, + { 0x9fe8, 0x0000 }, + { 0x9fe9, 0x0000 }, + { 0x9fea, 0x0000 }, + { 0x9feb, 0x0000 }, + { 0x9fec, 0x0000 }, + { 0x9fed, 0x0000 }, + { 0x9fee, 0x0000 }, + { 0x9fef, 0x0000 }, + { 0x9ff0, 0x0000 }, + { 0x9ff1, 0x0000 }, + { 0x9ff2, 0x0000 }, + { 0x9ff3, 0x0000 }, + { 0x9ff4, 0x0000 }, + { 0x9ff5, 0x0000 }, + { 0x9ff6, 0x0000 }, + { 0x9ff7, 0x0000 }, + { 0x9ff8, 0x0000 }, + { 0x9ff9, 0x0000 }, + { 0x9ffa, 0x0000 }, + { 0x9ffb, 0x0000 }, + { 0x9ffc, 0x0000 }, + { 0x9ffd, 0x0000 }, + { 0x9ffe, 0x0000 }, + { 0x9fff, 0x0000 }, + { 0xa000, 0x0000 }, + { 0xa001, 0x0000 }, + { 0xa002, 0x0000 }, + { 0xa003, 0x0000 }, + { 0xa004, 0x0000 }, + { 0xa005, 0x0000 }, + { 0xa006, 0x0000 }, + { 0xa007, 0x0000 }, + { 0xa008, 0x0000 }, + { 0xa009, 0x0000 }, + { 0xa00a, 0x0000 }, + { 0xa00b, 0x0000 }, + { 0xa00c, 0x0000 }, + { 0xa00d, 0x0000 }, + { 0xa00e, 0x0000 }, + { 0xa00f, 0x0000 }, + { 0xa010, 0x0000 }, + { 0xa011, 0x0000 }, + { 0xa012, 0x0000 }, + { 0xa013, 0x0000 }, + { 0xa014, 0x0000 }, + { 0xa015, 0x0000 }, + { 0xa016, 0x0000 }, + { 0xa017, 0x0000 }, + { 0xa018, 0x0000 }, + { 0xa019, 0x0000 }, + { 0xa01a, 0x0000 }, + { 0xa01b, 0x0000 }, + { 0xa01c, 0x0000 }, + { 0xa01d, 0x0000 }, + { 0xa01e, 0x0000 }, + { 0xa01f, 0x0000 }, + { 0xa020, 0x0000 }, + { 0xa021, 0x0000 }, + { 0xa022, 0x0000 }, + { 0xa023, 0x0000 }, + { 0xa024, 0x0000 }, + { 0xa025, 0x0000 }, + { 0xa026, 0x0000 }, + { 0xa027, 0x0000 }, + { 0xa028, 0x0000 }, + { 0xa029, 0x0000 }, + { 0xa02a, 0x0000 }, + { 0xa02b, 0x0000 }, + { 0xa02c, 0x0000 }, + { 0xa02d, 0x0000 }, + { 0xa02e, 0x0000 }, + { 0xa02f, 0x0000 }, + { 0xa030, 0x0000 }, + { 0xa031, 0x0000 }, + { 0xa032, 0x0000 }, + { 0xa033, 0x0000 }, + { 0xa034, 0x0000 }, + { 0xa035, 0x0000 }, + { 0xa036, 0x0000 }, + { 0xa037, 0x0000 }, + { 0xa038, 0x0000 }, + { 0xa039, 0x0000 }, + { 0xa03a, 0x0000 }, + { 0xa03b, 0x0000 }, + { 0xa03c, 0x0000 }, + { 0xa03d, 0x0000 }, + { 0xa03e, 0x0000 }, + { 0xa03f, 0x0000 }, + { 0xa040, 0x0000 }, + { 0xa041, 0x0000 }, + { 0xa042, 0x0000 }, + { 0xa043, 0x0000 }, + { 0xa044, 0x0000 }, + { 0xa045, 0x0000 }, + { 0xa046, 0x0000 }, + { 0xa047, 0x0000 }, + { 0xa048, 0x0000 }, + { 0xa049, 0x0000 }, + { 0xa04a, 0x0000 }, + { 0xa04b, 0x0000 }, + { 0xa04c, 0x0000 }, + { 0xa04d, 0x0000 }, + { 0xa04e, 0x0000 }, + { 0xa04f, 0x0000 }, + { 0xa050, 0x0000 }, + { 0xa051, 0x0000 }, + { 0xa052, 0x0000 }, + { 0xa053, 0x0000 }, + { 0xa054, 0x0000 }, + { 0xa055, 0x0000 }, + { 0xa056, 0x0000 }, + { 0xa057, 0x0000 }, + { 0xa058, 0x0000 }, + { 0xa059, 0x0000 }, + { 0xa05a, 0x0000 }, + { 0xa05b, 0x0000 }, + { 0xa05c, 0x0000 }, + { 0xa05d, 0x0000 }, + { 0xa05e, 0x0000 }, + { 0xa05f, 0x0000 }, + { 0xa060, 0x0000 }, + { 0xa061, 0x0000 }, + { 0xa062, 0x0000 }, + { 0xa063, 0x0000 }, + { 0xa064, 0x0000 }, + { 0xa065, 0x0000 }, + { 0xa066, 0x0000 }, + { 0xa067, 0x0000 }, + { 0xa068, 0x0000 }, + { 0xa069, 0x0000 }, + { 0xa06a, 0x0000 }, + { 0xa06b, 0x0000 }, + { 0xa06c, 0x0000 }, + { 0xa06d, 0x0000 }, + { 0xa06e, 0x0000 }, + { 0xa06f, 0x0000 }, + { 0xa070, 0x0000 }, + { 0xa071, 0x0000 }, + { 0xa072, 0x0000 }, + { 0xa073, 0x0000 }, + { 0xa074, 0x0000 }, + { 0xa075, 0x0000 }, + { 0xa076, 0x0000 }, + { 0xa077, 0x0000 }, + { 0xa078, 0x0000 }, + { 0xa079, 0x0000 }, + { 0xa07a, 0x0000 }, + { 0xa07b, 0x0000 }, + { 0xa07c, 0x0000 }, + { 0xa07d, 0x0000 }, + { 0xa07e, 0x0000 }, + { 0xa07f, 0x0000 }, + { 0xa080, 0x0000 }, + { 0xa081, 0x0000 }, + { 0xa082, 0x0000 }, + { 0xa083, 0x0000 }, + { 0xa084, 0x0000 }, + { 0xa085, 0x0000 }, + { 0xa086, 0x0000 }, + { 0xa087, 0x0000 }, + { 0xa088, 0x0000 }, + { 0xa089, 0x0000 }, + { 0xa08a, 0x0000 }, + { 0xa08b, 0x0000 }, + { 0xa08c, 0x0000 }, + { 0xa08d, 0x0000 }, + { 0xa08e, 0x0000 }, + { 0xa08f, 0x0000 }, + { 0xa090, 0x0000 }, + { 0xa091, 0x0000 }, + { 0xa092, 0x0000 }, + { 0xa093, 0x0000 }, + { 0xa094, 0x0000 }, + { 0xa095, 0x0000 }, + { 0xa096, 0x0000 }, + { 0xa097, 0x0000 }, + { 0xa098, 0x0000 }, + { 0xa099, 0x0000 }, + { 0xa09a, 0x0000 }, + { 0xa09b, 0x0000 }, + { 0xa09c, 0x0000 }, + { 0xa09d, 0x0000 }, + { 0xa09e, 0x0000 }, + { 0xa09f, 0x0000 }, + { 0xa0a0, 0x0000 }, + { 0xa0a1, 0x0000 }, + { 0xa0a2, 0x0000 }, + { 0xa0a3, 0x0000 }, + { 0xa0a4, 0x0000 }, + { 0xa0a5, 0x0000 }, + { 0xa0a6, 0x0000 }, + { 0xa0a7, 0x0000 }, + { 0xa0a8, 0x0000 }, + { 0xa0a9, 0x0000 }, + { 0xa0aa, 0x0000 }, + { 0xa0ab, 0x0000 }, + { 0xa0ac, 0x0000 }, + { 0xa0ad, 0x0000 }, + { 0xa0ae, 0x0000 }, + { 0xa0af, 0x0000 }, + { 0xa0b0, 0x0000 }, + { 0xa0b1, 0x0000 }, + { 0xa0b2, 0x0000 }, + { 0xa0b3, 0x0000 }, + { 0xa0b4, 0x0000 }, + { 0xa0b5, 0x0000 }, + { 0xa0b6, 0x0000 }, + { 0xa0b7, 0x0000 }, + { 0xa0b8, 0x0000 }, + { 0xa0b9, 0x0000 }, + { 0xa0ba, 0x0000 }, + { 0xa0bb, 0x0000 }, + { 0xa0bc, 0x0000 }, + { 0xa0bd, 0x0000 }, + { 0xa0be, 0x0000 }, + { 0xa0bf, 0x0000 }, + { 0xa0c0, 0x0000 }, + { 0xa0c1, 0x0000 }, + { 0xa0c2, 0x0000 }, + { 0xa0c3, 0x0000 }, + { 0xa0c4, 0x0000 }, + { 0xa0c5, 0x0000 }, + { 0xa0c6, 0x0000 }, + { 0xa0c7, 0x0000 }, + { 0xa0c8, 0x0000 }, + { 0xa0c9, 0x0000 }, + { 0xa0ca, 0x0000 }, + { 0xa0cb, 0x0000 }, + { 0xa0cc, 0x0000 }, + { 0xa0cd, 0x0000 }, + { 0xa0ce, 0x0000 }, + { 0xa0cf, 0x0000 }, + { 0xa0d0, 0x0000 }, + { 0xa0d1, 0x0000 }, + { 0xa0d2, 0x0000 }, + { 0xa0d3, 0x0000 }, + { 0xa0d4, 0x0000 }, + { 0xa0d5, 0x0000 }, + { 0xa0d6, 0x0000 }, + { 0xa0d7, 0x0000 }, + { 0xa0d8, 0x0000 }, + { 0xa0d9, 0x0000 }, + { 0xa0da, 0x0000 }, + { 0xa0db, 0x0000 }, + { 0xa0dc, 0x0000 }, + { 0xa0dd, 0x0000 }, + { 0xa0de, 0x0000 }, + { 0xa0df, 0x0000 }, + { 0xa0e0, 0x0000 }, + { 0xa0e1, 0x0000 }, + { 0xa0e2, 0x0000 }, + { 0xa0e3, 0x0000 }, + { 0xa0e4, 0x0000 }, + { 0xa0e5, 0x0000 }, + { 0xa0e6, 0x0000 }, + { 0xa0e7, 0x0000 }, + { 0xa0e8, 0x0000 }, + { 0xa0e9, 0x0000 }, + { 0xa0ea, 0x0000 }, + { 0xa0eb, 0x0000 }, + { 0xa0ec, 0x0000 }, + { 0xa0ed, 0x0000 }, + { 0xa0ee, 0x0000 }, + { 0xa0ef, 0x0000 }, + { 0xa0f0, 0x0000 }, + { 0xa0f1, 0x0000 }, + { 0xa0f2, 0x0000 }, + { 0xa0f3, 0x0000 }, + { 0xa0f4, 0x0000 }, + { 0xa0f5, 0x0000 }, + { 0xa0f6, 0x0000 }, + { 0xa0f7, 0x0000 }, + { 0xa0f8, 0x0000 }, + { 0xa0f9, 0x0000 }, + { 0xa0fa, 0x0000 }, + { 0xa0fb, 0x0000 }, + { 0xa0fc, 0x0000 }, + { 0xa0fd, 0x0000 }, + { 0xa0fe, 0x0000 }, + { 0xa0ff, 0x0000 }, + { 0xa100, 0x0000 }, + { 0xa101, 0x0000 }, + { 0xa102, 0x0000 }, + { 0xa103, 0x0000 }, + { 0xa104, 0x0000 }, + { 0xa105, 0x0000 }, + { 0xa106, 0x0000 }, + { 0xa107, 0x0000 }, + { 0xa108, 0x0000 }, + { 0xa109, 0x0000 }, + { 0xa10a, 0x0000 }, + { 0xa10b, 0x0000 }, + { 0xa10c, 0x0000 }, + { 0xa10d, 0x0000 }, + { 0xa10e, 0x0000 }, + { 0xa10f, 0x0000 }, + { 0xa110, 0x0000 }, + { 0xa111, 0x0000 }, + { 0xa112, 0x0000 }, + { 0xa113, 0x0000 }, + { 0xa114, 0x0000 }, + { 0xa115, 0x0000 }, + { 0xa116, 0x0000 }, + { 0xa117, 0x0000 }, + { 0xa118, 0x0000 }, + { 0xa119, 0x0000 }, + { 0xa11a, 0x0000 }, + { 0xa11b, 0x0000 }, + { 0xa11c, 0x0000 }, + { 0xa11d, 0x0000 }, + { 0xa11e, 0x0000 }, + { 0xa11f, 0x0000 }, + { 0xa120, 0x0000 }, + { 0xa121, 0x0000 }, + { 0xa122, 0x0000 }, + { 0xa123, 0x0000 }, + { 0xa124, 0x0000 }, + { 0xa125, 0x0000 }, + { 0xa126, 0x0000 }, + { 0xa127, 0x0000 }, + { 0xa128, 0x0000 }, + { 0xa129, 0x0000 }, + { 0xa12a, 0x0000 }, + { 0xa12b, 0x0000 }, + { 0xa12c, 0x0000 }, + { 0xa12d, 0x0000 }, + { 0xa12e, 0x0000 }, + { 0xa12f, 0x0000 }, + { 0xa130, 0x0000 }, + { 0xa131, 0x0000 }, + { 0xa132, 0x0000 }, + { 0xa133, 0x0000 }, + { 0xa134, 0x0000 }, + { 0xa135, 0x0000 }, + { 0xa136, 0x0000 }, + { 0xa137, 0x0000 }, + { 0xa138, 0x0000 }, + { 0xa139, 0x0000 }, + { 0xa13a, 0x0000 }, + { 0xa13b, 0x0000 }, + { 0xa13c, 0x0000 }, + { 0xa13d, 0x0000 }, + { 0xa13e, 0x0000 }, + { 0xa13f, 0x0000 }, + { 0xa140, 0x0000 }, + { 0xa141, 0x0000 }, + { 0xa142, 0x0000 }, + { 0xa143, 0x0000 }, + { 0xa144, 0x0000 }, + { 0xa145, 0x0000 }, + { 0xa146, 0x0000 }, + { 0xa147, 0x0000 }, + { 0xa148, 0x0000 }, + { 0xa149, 0x0000 }, + { 0xa14a, 0x0000 }, + { 0xa14b, 0x0000 }, + { 0xa14c, 0x0000 }, + { 0xa14d, 0x0000 }, + { 0xa14e, 0x0000 }, + { 0xa14f, 0x0000 }, + { 0xa150, 0x0000 }, + { 0xa151, 0x0000 }, + { 0xa152, 0x0000 }, + { 0xa153, 0x0000 }, + { 0xa154, 0x0000 }, + { 0xa155, 0x0000 }, + { 0xa156, 0x0000 }, + { 0xa157, 0x0000 }, + { 0xa158, 0x0000 }, + { 0xa159, 0x0000 }, + { 0xa15a, 0x0000 }, + { 0xa15b, 0x0000 }, + { 0xa15c, 0x0000 }, + { 0xa15d, 0x0000 }, + { 0xa15e, 0x0000 }, + { 0xa15f, 0x0000 }, + { 0xa160, 0x0000 }, + { 0xa161, 0x0000 }, + { 0xa162, 0x0000 }, + { 0xa163, 0x0000 }, + { 0xa164, 0x0000 }, + { 0xa165, 0x0000 }, + { 0xa166, 0x0000 }, + { 0xa167, 0x0000 }, + { 0xa168, 0x0000 }, + { 0xa169, 0x0000 }, + { 0xa16a, 0x0000 }, + { 0xa16b, 0x0000 }, + { 0xa16c, 0x0000 }, + { 0xa16d, 0x0000 }, + { 0xa16e, 0x0000 }, + { 0xa16f, 0x0000 }, + { 0xa170, 0x0000 }, + { 0xa171, 0x0000 }, + { 0xa172, 0x0000 }, + { 0xa173, 0x0000 }, + { 0xa174, 0x0000 }, + { 0xa175, 0x0000 }, + { 0xa176, 0x0000 }, + { 0xa177, 0x0000 }, + { 0xa178, 0x0000 }, + { 0xa179, 0x0000 }, + { 0xa17a, 0x0000 }, + { 0xa17b, 0x0000 }, + { 0xa17c, 0x0000 }, + { 0xa17d, 0x0000 }, + { 0xa17e, 0x0000 }, + { 0xa17f, 0x0000 }, + { 0xa180, 0x0000 }, + { 0xa181, 0x0000 }, + { 0xa182, 0x0000 }, + { 0xa183, 0x0000 }, + { 0xa184, 0x0000 }, + { 0xa185, 0x0000 }, + { 0xa186, 0x0000 }, + { 0xa187, 0x0000 }, + { 0xa188, 0x0000 }, + { 0xa189, 0x0000 }, + { 0xa18a, 0x0000 }, + { 0xa18b, 0x0000 }, + { 0xa18c, 0x0000 }, + { 0xa18d, 0x0000 }, + { 0xa18e, 0x0000 }, + { 0xa18f, 0x0000 }, + { 0xa190, 0x0000 }, + { 0xa191, 0x0000 }, + { 0xa192, 0x0000 }, + { 0xa193, 0x0000 }, + { 0xa194, 0x0000 }, + { 0xa195, 0x0000 }, + { 0xa196, 0x0000 }, + { 0xa197, 0x0000 }, + { 0xa198, 0x0000 }, + { 0xa199, 0x0000 }, + { 0xa19a, 0x0000 }, + { 0xa19b, 0x0000 }, + { 0xa19c, 0x0000 }, + { 0xa19d, 0x0000 }, + { 0xa19e, 0x0000 }, + { 0xa19f, 0x0000 }, + { 0xa1a0, 0x0000 }, + { 0xa1a1, 0x0000 }, + { 0xa1a2, 0x0000 }, + { 0xa1a3, 0x0000 }, + { 0xa1a4, 0x0000 }, + { 0xa1a5, 0x0000 }, + { 0xa1a6, 0x0000 }, + { 0xa1a7, 0x0000 }, + { 0xa1a8, 0x0000 }, + { 0xa1a9, 0x0000 }, + { 0xa1aa, 0x0000 }, + { 0xa1ab, 0x0000 }, + { 0xa1ac, 0x0000 }, + { 0xa1ad, 0x0000 }, + { 0xa1ae, 0x0000 }, + { 0xa1af, 0x0000 }, + { 0xa1b0, 0x0000 }, + { 0xa1b1, 0x0000 }, + { 0xa1b2, 0x0000 }, + { 0xa1b3, 0x0000 }, + { 0xa1b4, 0x0000 }, + { 0xa1b5, 0x0000 }, + { 0xa1b6, 0x0000 }, + { 0xa1b7, 0x0000 }, + { 0xa1b8, 0x0000 }, + { 0xa1b9, 0x0000 }, + { 0xa1ba, 0x0000 }, + { 0xa1bb, 0x0000 }, + { 0xa1bc, 0x0000 }, + { 0xa1bd, 0x0000 }, + { 0xa1be, 0x0000 }, + { 0xa1bf, 0x0000 }, + { 0xa1c0, 0x0000 }, + { 0xa1c1, 0x0000 }, + { 0xa1c2, 0x0000 }, + { 0xa1c3, 0x0000 }, + { 0xa1c4, 0x0000 }, + { 0xa1c5, 0x0000 }, + { 0xa1c6, 0x0000 }, + { 0xa1c7, 0x0000 }, + { 0xa1c8, 0x0000 }, + { 0xa1c9, 0x0000 }, + { 0xa1ca, 0x0000 }, + { 0xa1cb, 0x0000 }, + { 0xa1cc, 0x0000 }, + { 0xa1cd, 0x0000 }, + { 0xa1ce, 0x0000 }, + { 0xa1cf, 0x0000 }, + { 0xa1d0, 0x0000 }, + { 0xa1d1, 0x0000 }, + { 0xa1d2, 0x0000 }, + { 0xa1d3, 0x0000 }, + { 0xa1d4, 0x0000 }, + { 0xa1d5, 0x0000 }, + { 0xa1d6, 0x0000 }, + { 0xa1d7, 0x0000 }, + { 0xa1d8, 0x0000 }, + { 0xa1d9, 0x0000 }, + { 0xa1da, 0x0000 }, + { 0xa1db, 0x0000 }, + { 0xa1dc, 0x0000 }, + { 0xa1dd, 0x0000 }, + { 0xa1de, 0x0000 }, + { 0xa1df, 0x0000 }, + { 0xa1e0, 0x0000 }, + { 0xa1e1, 0x0000 }, + { 0xa1e2, 0x0000 }, + { 0xa1e3, 0x0000 }, + { 0xa1e4, 0x0000 }, + { 0xa1e5, 0x0000 }, + { 0xa1e6, 0x0000 }, + { 0xa1e7, 0x0000 }, + { 0xa1e8, 0x0000 }, + { 0xa1e9, 0x0000 }, + { 0xa1ea, 0x0000 }, + { 0xa1eb, 0x0000 }, + { 0xa1ec, 0x0000 }, + { 0xa1ed, 0x0000 }, + { 0xa1ee, 0x0000 }, + { 0xa1ef, 0x0000 }, + { 0xa1f0, 0x0000 }, + { 0xa1f1, 0x0000 }, + { 0xa1f2, 0x0000 }, + { 0xa1f3, 0x0000 }, + { 0xa1f4, 0x0000 }, + { 0xa1f5, 0x0000 }, + { 0xa1f6, 0x0000 }, + { 0xa1f7, 0x0000 }, + { 0xa1f8, 0x0000 }, + { 0xa1f9, 0x0000 }, + { 0xa1fa, 0x0000 }, + { 0xa1fb, 0x0000 }, + { 0xa1fc, 0x0000 }, + { 0xa1fd, 0x0000 }, + { 0xa1fe, 0x0000 }, + { 0xa1ff, 0x0000 }, + { 0xa200, 0x0000 }, + { 0xa201, 0x0000 }, + { 0xa202, 0x0000 }, + { 0xa203, 0x0000 }, + { 0xa204, 0x0000 }, + { 0xa205, 0x0000 }, + { 0xa206, 0x0000 }, + { 0xa207, 0x0000 }, + { 0xa208, 0x0000 }, + { 0xa209, 0x0000 }, + { 0xa20a, 0x0000 }, + { 0xa20b, 0x0000 }, + { 0xa20c, 0x0000 }, + { 0xa20d, 0x0000 }, + { 0xa20e, 0x0000 }, + { 0xa20f, 0x0000 }, + { 0xa210, 0x0000 }, + { 0xa211, 0x0000 }, + { 0xa212, 0x0000 }, + { 0xa213, 0x0000 }, + { 0xa214, 0x0000 }, + { 0xa215, 0x0000 }, + { 0xa216, 0x0000 }, + { 0xa217, 0x0000 }, + { 0xa218, 0x0000 }, + { 0xa219, 0x0000 }, + { 0xa21a, 0x0000 }, + { 0xa21b, 0x0000 }, + { 0xa21c, 0x0000 }, + { 0xa21d, 0x0000 }, + { 0xa21e, 0x0000 }, + { 0xa21f, 0x0000 }, + { 0xa220, 0x0000 }, + { 0xa221, 0x0000 }, + { 0xa222, 0x0000 }, + { 0xa223, 0x0000 }, + { 0xa224, 0x0000 }, + { 0xa225, 0x0000 }, + { 0xa226, 0x0000 }, + { 0xa227, 0x0000 }, + { 0xa228, 0x0000 }, + { 0xa229, 0x0000 }, + { 0xa22a, 0x0000 }, + { 0xa22b, 0x0000 }, + { 0xa22c, 0x0000 }, + { 0xa22d, 0x0000 }, + { 0xa22e, 0x0000 }, + { 0xa22f, 0x0000 }, + { 0xa230, 0x0000 }, + { 0xa231, 0x0000 }, + { 0xa232, 0x0000 }, + { 0xa233, 0x0000 }, + { 0xa234, 0x0000 }, + { 0xa235, 0x0000 }, + { 0xa236, 0x0000 }, + { 0xa237, 0x0000 }, + { 0xa238, 0x0000 }, + { 0xa239, 0x0000 }, + { 0xa23a, 0x0000 }, + { 0xa23b, 0x0000 }, + { 0xa23c, 0x0000 }, + { 0xa23d, 0x0000 }, + { 0xa23e, 0x0000 }, + { 0xa23f, 0x0000 }, + { 0xa240, 0x0000 }, + { 0xa241, 0x0000 }, + { 0xa242, 0x0000 }, + { 0xa243, 0x0000 }, + { 0xa244, 0x0000 }, + { 0xa245, 0x0000 }, + { 0xa246, 0x0000 }, + { 0xa247, 0x0000 }, + { 0xa248, 0x0000 }, + { 0xa249, 0x0000 }, + { 0xa24a, 0x0000 }, + { 0xa24b, 0x0000 }, + { 0xa24c, 0x0000 }, + { 0xa24d, 0x0000 }, + { 0xa24e, 0x0000 }, + { 0xa24f, 0x0000 }, + { 0xa250, 0x0000 }, + { 0xa251, 0x0000 }, + { 0xa252, 0x0000 }, + { 0xa253, 0x0000 }, + { 0xa254, 0x0000 }, + { 0xa255, 0x0000 }, + { 0xa256, 0x0000 }, + { 0xa257, 0x0000 }, + { 0xa258, 0x0000 }, + { 0xa259, 0x0000 }, + { 0xa25a, 0x0000 }, + { 0xa25b, 0x0000 }, + { 0xa25c, 0x0000 }, + { 0xa25d, 0x0000 }, + { 0xa25e, 0x0000 }, + { 0xa25f, 0x0000 }, + { 0xa260, 0x0000 }, + { 0xa261, 0x0000 }, + { 0xa262, 0x0000 }, + { 0xa263, 0x0000 }, + { 0xa264, 0x0000 }, + { 0xa265, 0x0000 }, + { 0xa266, 0x0000 }, + { 0xa267, 0x0000 }, + { 0xa268, 0x0000 }, + { 0xa269, 0x0000 }, + { 0xa26a, 0x0000 }, + { 0xa26b, 0x0000 }, + { 0xa26c, 0x0000 }, + { 0xa26d, 0x0000 }, + { 0xa26e, 0x0000 }, + { 0xa26f, 0x0000 }, + { 0xa270, 0x0000 }, + { 0xa271, 0x0000 }, + { 0xa272, 0x0000 }, + { 0xa273, 0x0000 }, + { 0xa274, 0x0000 }, + { 0xa275, 0x0000 }, + { 0xa276, 0x0000 }, + { 0xa277, 0x0000 }, + { 0xa278, 0x0000 }, + { 0xa279, 0x0000 }, + { 0xa27a, 0x0000 }, + { 0xa27b, 0x0000 }, + { 0xa27c, 0x0000 }, + { 0xa27d, 0x0000 }, + { 0xa27e, 0x0000 }, + { 0xa27f, 0x0000 }, + { 0xa280, 0x0000 }, + { 0xa281, 0x0000 }, + { 0xa282, 0x0000 }, + { 0xa283, 0x0000 }, + { 0xa284, 0x0000 }, + { 0xa285, 0x0000 }, + { 0xa286, 0x0000 }, + { 0xa287, 0x0000 }, + { 0xa288, 0x0000 }, + { 0xa289, 0x0000 }, + { 0xa28a, 0x0000 }, + { 0xa28b, 0x0000 }, + { 0xa28c, 0x0000 }, + { 0xa28d, 0x0000 }, + { 0xa28e, 0x0000 }, + { 0xa28f, 0x0000 }, + { 0xa290, 0x0000 }, + { 0xa291, 0x0000 }, + { 0xa292, 0x0000 }, + { 0xa293, 0x0000 }, + { 0xa294, 0x0000 }, + { 0xa295, 0x0000 }, + { 0xa296, 0x0000 }, + { 0xa297, 0x0000 }, + { 0xa298, 0x0000 }, + { 0xa299, 0x0000 }, + { 0xa29a, 0x0000 }, + { 0xa29b, 0x0000 }, + { 0xa29c, 0x0000 }, + { 0xa29d, 0x0000 }, + { 0xa29e, 0x0000 }, + { 0xa29f, 0x0000 }, + { 0xa2a0, 0x0000 }, + { 0xa2a1, 0x0000 }, + { 0xa2a2, 0x0000 }, + { 0xa2a3, 0x0000 }, + { 0xa2a4, 0x0000 }, + { 0xa2a5, 0x0000 }, + { 0xa2a6, 0x0000 }, + { 0xa2a7, 0x0000 }, + { 0xa2a8, 0x0000 }, + { 0xa2a9, 0x0000 }, + { 0xa2aa, 0x0000 }, + { 0xa2ab, 0x0000 }, + { 0xa2ac, 0x0000 }, + { 0xa2ad, 0x0000 }, + { 0xa2ae, 0x0000 }, + { 0xa2af, 0x0000 }, + { 0xa2b0, 0x0000 }, + { 0xa2b1, 0x0000 }, + { 0xa2b2, 0x0000 }, + { 0xa2b3, 0x0000 }, + { 0xa2b4, 0x0000 }, + { 0xa2b5, 0x0000 }, + { 0xa2b6, 0x0000 }, + { 0xa2b7, 0x0000 }, + { 0xa2b8, 0x0000 }, + { 0xa2b9, 0x0000 }, + { 0xa2ba, 0x0000 }, + { 0xa2bb, 0x0000 }, + { 0xa2bc, 0x0000 }, + { 0xa2bd, 0x0000 }, + { 0xa2be, 0x0000 }, + { 0xa2bf, 0x0000 }, + { 0xa2c0, 0x0000 }, + { 0xa2c1, 0x0000 }, + { 0xa2c2, 0x0000 }, + { 0xa2c3, 0x0000 }, + { 0xa2c4, 0x0000 }, + { 0xa2c5, 0x0000 }, + { 0xa2c6, 0x0000 }, + { 0xa2c7, 0x0000 }, + { 0xa2c8, 0x0000 }, + { 0xa2c9, 0x0000 }, + { 0xa2ca, 0x0000 }, + { 0xa2cb, 0x0000 }, + { 0xa2cc, 0x0000 }, + { 0xa2cd, 0x0000 }, + { 0xa2ce, 0x0000 }, + { 0xa2cf, 0x0000 }, + { 0xa2d0, 0x0000 }, + { 0xa2d1, 0x0000 }, + { 0xa2d2, 0x0000 }, + { 0xa2d3, 0x0000 }, + { 0xa2d4, 0x0000 }, + { 0xa2d5, 0x0000 }, + { 0xa2d6, 0x0000 }, + { 0xa2d7, 0x0000 }, + { 0xa2d8, 0x0000 }, + { 0xa2d9, 0x0000 }, + { 0xa2da, 0x0000 }, + { 0xa2db, 0x0000 }, + { 0xa2dc, 0x0000 }, + { 0xa2dd, 0x0000 }, + { 0xa2de, 0x0000 }, + { 0xa2df, 0x0000 }, + { 0xa2e0, 0x0000 }, + { 0xa2e1, 0x0000 }, + { 0xa2e2, 0x0000 }, + { 0xa2e3, 0x0000 }, + { 0xa2e4, 0x0000 }, + { 0xa2e5, 0x0000 }, + { 0xa2e6, 0x0000 }, + { 0xa2e7, 0x0000 }, + { 0xa2e8, 0x0000 }, + { 0xa2e9, 0x0000 }, + { 0xa2ea, 0x0000 }, + { 0xa2eb, 0x0000 }, + { 0xa2ec, 0x0000 }, + { 0xa2ed, 0x0000 }, + { 0xa2ee, 0x0000 }, + { 0xa2ef, 0x0000 }, + { 0xa2f0, 0x0000 }, + { 0xa2f1, 0x0000 }, + { 0xa2f2, 0x0000 }, + { 0xa2f3, 0x0000 }, + { 0xa2f4, 0x0000 }, + { 0xa2f5, 0x0000 }, + { 0xa2f6, 0x0000 }, + { 0xa2f7, 0x0000 }, + { 0xa2f8, 0x0000 }, + { 0xa2f9, 0x0000 }, + { 0xa2fa, 0x0000 }, + { 0xa2fb, 0x0000 }, + { 0xa2fc, 0x0000 }, + { 0xa2fd, 0x0000 }, + { 0xa2fe, 0x0000 }, + { 0xa2ff, 0x0000 }, + { 0xa300, 0x0000 }, + { 0xa301, 0x0000 }, + { 0xa302, 0x0000 }, + { 0xa303, 0x0000 }, + { 0xa304, 0x0000 }, + { 0xa305, 0x0000 }, + { 0xa306, 0x0000 }, + { 0xa307, 0x0000 }, + { 0xa308, 0x0000 }, + { 0xa309, 0x0000 }, + { 0xa30a, 0x0000 }, + { 0xa30b, 0x0000 }, + { 0xa30c, 0x0000 }, + { 0xa30d, 0x0000 }, + { 0xa30e, 0x0000 }, + { 0xa30f, 0x0000 }, + { 0xa310, 0x0000 }, + { 0xa311, 0x0000 }, + { 0xa312, 0x0000 }, + { 0xa313, 0x0000 }, + { 0xa314, 0x0000 }, + { 0xa315, 0x0000 }, + { 0xa316, 0x0000 }, + { 0xa317, 0x0000 }, + { 0xa318, 0x0000 }, + { 0xa319, 0x0000 }, + { 0xa31a, 0x0000 }, + { 0xa31b, 0x0000 }, + { 0xa31c, 0x0000 }, + { 0xa31d, 0x0000 }, + { 0xa31e, 0x0000 }, + { 0xa31f, 0x0000 }, + { 0xa320, 0x0000 }, + { 0xa321, 0x0000 }, + { 0xa322, 0x0000 }, + { 0xa323, 0x0000 }, + { 0xa324, 0x0000 }, + { 0xa325, 0x0000 }, + { 0xa326, 0x0000 }, + { 0xa327, 0x0000 }, + { 0xa328, 0x0000 }, + { 0xa329, 0x0000 }, + { 0xa32a, 0x0000 }, + { 0xa32b, 0x0000 }, + { 0xa32c, 0x0000 }, + { 0xa32d, 0x0000 }, + { 0xa32e, 0x0000 }, + { 0xa32f, 0x0000 }, + { 0xa330, 0x0000 }, + { 0xa331, 0x0000 }, + { 0xa332, 0x0000 }, + { 0xa333, 0x0000 }, + { 0xa334, 0x0000 }, + { 0xa335, 0x0000 }, + { 0xa336, 0x0000 }, + { 0xa337, 0x0000 }, + { 0xa338, 0x0000 }, + { 0xa339, 0x0000 }, + { 0xa33a, 0x0000 }, + { 0xa33b, 0x0000 }, + { 0xa33c, 0x0000 }, + { 0xa33d, 0x0000 }, + { 0xa33e, 0x0000 }, + { 0xa33f, 0x0000 }, + { 0xa340, 0x0000 }, + { 0xa341, 0x0000 }, + { 0xa342, 0x0000 }, + { 0xa343, 0x0000 }, + { 0xa344, 0x0000 }, + { 0xa345, 0x0000 }, + { 0xa346, 0x0000 }, + { 0xa347, 0x0000 }, + { 0xa348, 0x0000 }, + { 0xa349, 0x0000 }, + { 0xa34a, 0x0000 }, + { 0xa34b, 0x0000 }, + { 0xa34c, 0x0000 }, + { 0xa34d, 0x0000 }, + { 0xa34e, 0x0000 }, + { 0xa34f, 0x0000 }, + { 0xa350, 0x0000 }, + { 0xa351, 0x0000 }, + { 0xa352, 0x0000 }, + { 0xa353, 0x0000 }, + { 0xa354, 0x0000 }, + { 0xa355, 0x0000 }, + { 0xa356, 0x0000 }, + { 0xa357, 0x0000 }, + { 0xa358, 0x0000 }, + { 0xa359, 0x0000 }, + { 0xa35a, 0x0000 }, + { 0xa35b, 0x0000 }, + { 0xa35c, 0x0000 }, + { 0xa35d, 0x0000 }, + { 0xa35e, 0x0000 }, + { 0xa35f, 0x0000 }, + { 0xa360, 0x0000 }, + { 0xa361, 0x0000 }, + { 0xa362, 0x0000 }, + { 0xa363, 0x0000 }, + { 0xa364, 0x0000 }, + { 0xa365, 0x0000 }, + { 0xa366, 0x0000 }, + { 0xa367, 0x0000 }, + { 0xa368, 0x0000 }, + { 0xa369, 0x0000 }, + { 0xa36a, 0x0000 }, + { 0xa36b, 0x0000 }, + { 0xa36c, 0x0000 }, + { 0xa36d, 0x0000 }, + { 0xa36e, 0x0000 }, + { 0xa36f, 0x0000 }, + { 0xa370, 0x0000 }, + { 0xa371, 0x0000 }, + { 0xa372, 0x0000 }, + { 0xa373, 0x0000 }, + { 0xa374, 0x0000 }, + { 0xa375, 0x0000 }, + { 0xa376, 0x0000 }, + { 0xa377, 0x0000 }, + { 0xa378, 0x0000 }, + { 0xa379, 0x0000 }, + { 0xa37a, 0x0000 }, + { 0xa37b, 0x0000 }, + { 0xa37c, 0x0000 }, + { 0xa37d, 0x0000 }, + { 0xa37e, 0x0000 }, + { 0xa37f, 0x0000 }, + { 0xa380, 0x0000 }, + { 0xa381, 0x0000 }, + { 0xa382, 0x0000 }, + { 0xa383, 0x0000 }, + { 0xa384, 0x0000 }, + { 0xa385, 0x0000 }, + { 0xa386, 0x0000 }, + { 0xa387, 0x0000 }, + { 0xa388, 0x0000 }, + { 0xa389, 0x0000 }, + { 0xa38a, 0x0000 }, + { 0xa38b, 0x0000 }, + { 0xa38c, 0x0000 }, + { 0xa38d, 0x0000 }, + { 0xa38e, 0x0000 }, + { 0xa38f, 0x0000 }, + { 0xa390, 0x0000 }, + { 0xa391, 0x0000 }, + { 0xa392, 0x0000 }, + { 0xa393, 0x0000 }, + { 0xa394, 0x0000 }, + { 0xa395, 0x0000 }, + { 0xa396, 0x0000 }, + { 0xa397, 0x0000 }, + { 0xa398, 0x0000 }, + { 0xa399, 0x0000 }, + { 0xa39a, 0x0000 }, + { 0xa39b, 0x0000 }, + { 0xa39c, 0x0000 }, + { 0xa39d, 0x0000 }, + { 0xa39e, 0x0000 }, + { 0xa39f, 0x0000 }, + { 0xa3a0, 0x0000 }, + { 0xa3a1, 0x0000 }, + { 0xa3a2, 0x0000 }, + { 0xa3a3, 0x0000 }, + { 0xa3a4, 0x0000 }, + { 0xa3a5, 0x0000 }, + { 0xa3a6, 0x0000 }, + { 0xa3a7, 0x0000 }, + { 0xa3a8, 0x0000 }, + { 0xa3a9, 0x0000 }, + { 0xa3aa, 0x0000 }, + { 0xa3ab, 0x0000 }, + { 0xa3ac, 0x0000 }, + { 0xa3ad, 0x0000 }, + { 0xa3ae, 0x0000 }, + { 0xa3af, 0x0000 }, + { 0xa3b0, 0x0000 }, + { 0xa3b1, 0x0000 }, + { 0xa3b2, 0x0000 }, + { 0xa3b3, 0x0000 }, + { 0xa3b4, 0x0000 }, + { 0xa3b5, 0x0000 }, + { 0xa3b6, 0x0000 }, + { 0xa3b7, 0x0000 }, + { 0xa3b8, 0x0000 }, + { 0xa3b9, 0x0000 }, + { 0xa3ba, 0x0000 }, + { 0xa3bb, 0x0000 }, + { 0xa3bc, 0x0000 }, + { 0xa3bd, 0x0000 }, + { 0xa3be, 0x0000 }, + { 0xa3bf, 0x0000 }, + { 0xa3c0, 0x0000 }, + { 0xa3c1, 0x0000 }, + { 0xa3c2, 0x0000 }, + { 0xa3c3, 0x0000 }, + { 0xa3c4, 0x0000 }, + { 0xa3c5, 0x0000 }, + { 0xa3c6, 0x0000 }, + { 0xa3c7, 0x0000 }, + { 0xa3c8, 0x0000 }, + { 0xa3c9, 0x0000 }, + { 0xa3ca, 0x0000 }, + { 0xa3cb, 0x0000 }, + { 0xa3cc, 0x0000 }, + { 0xa3cd, 0x0000 }, + { 0xa3ce, 0x0000 }, + { 0xa3cf, 0x0000 }, + { 0xa3d0, 0x0000 }, + { 0xa3d1, 0x0000 }, + { 0xa3d2, 0x0000 }, + { 0xa3d3, 0x0000 }, + { 0xa3d4, 0x0000 }, + { 0xa3d5, 0x0000 }, + { 0xa3d6, 0x0000 }, + { 0xa3d7, 0x0000 }, + { 0xa3d8, 0x0000 }, + { 0xa3d9, 0x0000 }, + { 0xa3da, 0x0000 }, + { 0xa3db, 0x0000 }, + { 0xa3dc, 0x0000 }, + { 0xa3dd, 0x0000 }, + { 0xa3de, 0x0000 }, + { 0xa3df, 0x0000 }, + { 0xa3e0, 0x0000 }, + { 0xa3e1, 0x0000 }, + { 0xa3e2, 0x0000 }, + { 0xa3e3, 0x0000 }, + { 0xa3e4, 0x0000 }, + { 0xa3e5, 0x0000 }, + { 0xa3e6, 0x0000 }, + { 0xa3e7, 0x0000 }, + { 0xa3e8, 0x0000 }, + { 0xa3e9, 0x0000 }, + { 0xa3ea, 0x0000 }, + { 0xa3eb, 0x0000 }, + { 0xa3ec, 0x0000 }, + { 0xa3ed, 0x0000 }, + { 0xa3ee, 0x0000 }, + { 0xa3ef, 0x0000 }, + { 0xa3f0, 0x0000 }, + { 0xa3f1, 0x0000 }, + { 0xa3f2, 0x0000 }, + { 0xa3f3, 0x0000 }, + { 0xa3f4, 0x0000 }, + { 0xa3f5, 0x0000 }, + { 0xa3f6, 0x0000 }, + { 0xa3f7, 0x0000 }, + { 0xa3f8, 0x0000 }, + { 0xa3f9, 0x0000 }, + { 0xa3fa, 0x0000 }, + { 0xa3fb, 0x0000 }, + { 0xa3fc, 0x0000 }, + { 0xa3fd, 0x0000 }, + { 0xa3fe, 0x0000 }, + { 0xa3ff, 0x0000 }, + { 0xa400, 0x0000 }, + { 0xa401, 0x0000 }, + { 0xa402, 0x0000 }, + { 0xa403, 0x0000 }, + { 0xa404, 0x0000 }, + { 0xa405, 0x0000 }, + { 0xa406, 0x0000 }, + { 0xa407, 0x0000 }, + { 0xa408, 0x0000 }, + { 0xa409, 0x0000 }, + { 0xa40a, 0x0000 }, + { 0xa40b, 0x0000 }, + { 0xa40c, 0x0000 }, + { 0xa40d, 0x0000 }, + { 0xa40e, 0x0000 }, + { 0xa40f, 0x0000 }, + { 0xa410, 0x0000 }, + { 0xa411, 0x0000 }, + { 0xa412, 0x0000 }, + { 0xa413, 0x0000 }, + { 0xa414, 0x0000 }, + { 0xa415, 0x0000 }, + { 0xa416, 0x0000 }, + { 0xa417, 0x0000 }, + { 0xa418, 0x0000 }, + { 0xa419, 0x0000 }, + { 0xa41a, 0x0000 }, + { 0xa41b, 0x0000 }, + { 0xa41c, 0x0000 }, + { 0xa41d, 0x0000 }, + { 0xa41e, 0x0000 }, + { 0xa41f, 0x0000 }, + { 0xa420, 0x0000 }, + { 0xa421, 0x0000 }, + { 0xa422, 0x0000 }, + { 0xa423, 0x0000 }, + { 0xa424, 0x0000 }, + { 0xa425, 0x0000 }, + { 0xa426, 0x0000 }, + { 0xa427, 0x0000 }, + { 0xa428, 0x0000 }, + { 0xa429, 0x0000 }, + { 0xa42a, 0x0000 }, + { 0xa42b, 0x0000 }, + { 0xa42c, 0x0000 }, + { 0xa42d, 0x0000 }, + { 0xa42e, 0x0000 }, + { 0xa42f, 0x0000 }, + { 0xa430, 0x0000 }, + { 0xa431, 0x0000 }, + { 0xa432, 0x0000 }, + { 0xa433, 0x0000 }, + { 0xa434, 0x0000 }, + { 0xa435, 0x0000 }, + { 0xa436, 0x0000 }, + { 0xa437, 0x0000 }, + { 0xa438, 0x0000 }, + { 0xa439, 0x0000 }, + { 0xa43a, 0x0000 }, + { 0xa43b, 0x0000 }, + { 0xa43c, 0x0000 }, + { 0xa43d, 0x0000 }, + { 0xa43e, 0x0000 }, + { 0xa43f, 0x0000 }, + { 0xa440, 0x0000 }, + { 0xa441, 0x0000 }, + { 0xa442, 0x0000 }, + { 0xa443, 0x0000 }, + { 0xa444, 0x0000 }, + { 0xa445, 0x0000 }, + { 0xa446, 0x0000 }, + { 0xa447, 0x0000 }, + { 0xa448, 0x0000 }, + { 0xa449, 0x0000 }, + { 0xa44a, 0x0000 }, + { 0xa44b, 0x0000 }, + { 0xa44c, 0x0000 }, + { 0xa44d, 0x0000 }, + { 0xa44e, 0x0000 }, + { 0xa44f, 0x0000 }, + { 0xa450, 0x0000 }, + { 0xa451, 0x0000 }, + { 0xa452, 0x0000 }, + { 0xa453, 0x0000 }, + { 0xa454, 0x0000 }, + { 0xa455, 0x0000 }, + { 0xa456, 0x0000 }, + { 0xa457, 0x0000 }, + { 0xa458, 0x0000 }, + { 0xa459, 0x0000 }, + { 0xa45a, 0x0000 }, + { 0xa45b, 0x0000 }, + { 0xa45c, 0x0000 }, + { 0xa45d, 0x0000 }, + { 0xa45e, 0x0000 }, + { 0xa45f, 0x0000 }, + { 0xa460, 0x0000 }, + { 0xa461, 0x0000 }, + { 0xa462, 0x0000 }, + { 0xa463, 0x0000 }, + { 0xa464, 0x0000 }, + { 0xa465, 0x0000 }, + { 0xa466, 0x0000 }, + { 0xa467, 0x0000 }, + { 0xa468, 0x0000 }, + { 0xa469, 0x0000 }, + { 0xa46a, 0x0000 }, + { 0xa46b, 0x0000 }, + { 0xa46c, 0x0000 }, + { 0xa46d, 0x0000 }, + { 0xa46e, 0x0000 }, + { 0xa46f, 0x0000 }, + { 0xa470, 0x0000 }, + { 0xa471, 0x0000 }, + { 0xa472, 0x0000 }, + { 0xa473, 0x0000 }, + { 0xa474, 0x0000 }, + { 0xa475, 0x0000 }, + { 0xa476, 0x0000 }, + { 0xa477, 0x0000 }, + { 0xa478, 0x0000 }, + { 0xa479, 0x0000 }, + { 0xa47a, 0x0000 }, + { 0xa47b, 0x0000 }, + { 0xa47c, 0x0000 }, + { 0xa47d, 0x0000 }, + { 0xa47e, 0x0000 }, + { 0xa47f, 0x0000 }, + { 0xa480, 0x0000 }, + { 0xa481, 0x0000 }, + { 0xa482, 0x0000 }, + { 0xa483, 0x0000 }, + { 0xa484, 0x0000 }, + { 0xa485, 0x0000 }, + { 0xa486, 0x0000 }, + { 0xa487, 0x0000 }, + { 0xa488, 0x0000 }, + { 0xa489, 0x0000 }, + { 0xa48a, 0x0000 }, + { 0xa48b, 0x0000 }, + { 0xa48c, 0x0000 }, + { 0xa48d, 0x0000 }, + { 0xa48e, 0x0000 }, + { 0xa48f, 0x0000 }, + { 0xa490, 0x0000 }, + { 0xa491, 0x0000 }, + { 0xa492, 0x0000 }, + { 0xa493, 0x0000 }, + { 0xa494, 0x0000 }, + { 0xa495, 0x0000 }, + { 0xa496, 0x0000 }, + { 0xa497, 0x0000 }, + { 0xa498, 0x0000 }, + { 0xa499, 0x0000 }, + { 0xa49a, 0x0000 }, + { 0xa49b, 0x0000 }, + { 0xa49c, 0x0000 }, + { 0xa49d, 0x0000 }, + { 0xa49e, 0x0000 }, + { 0xa49f, 0x0000 }, + { 0xa4a0, 0x0000 }, + { 0xa4a1, 0x0000 }, + { 0xa4a2, 0x0000 }, + { 0xa4a3, 0x0000 }, + { 0xa4a4, 0x0000 }, + { 0xa4a5, 0x0000 }, + { 0xa4a6, 0x0000 }, + { 0xa4a7, 0x0000 }, + { 0xa4a8, 0x0000 }, + { 0xa4a9, 0x0000 }, + { 0xa4aa, 0x0000 }, + { 0xa4ab, 0x0000 }, + { 0xa4ac, 0x0000 }, + { 0xa4ad, 0x0000 }, + { 0xa4ae, 0x0000 }, + { 0xa4af, 0x0000 }, + { 0xa4b0, 0x0000 }, + { 0xa4b1, 0x0000 }, + { 0xa4b2, 0x0000 }, + { 0xa4b3, 0x0000 }, + { 0xa4b4, 0x0000 }, + { 0xa4b5, 0x0000 }, + { 0xa4b6, 0x0000 }, + { 0xa4b7, 0x0000 }, + { 0xa4b8, 0x0000 }, + { 0xa4b9, 0x0000 }, + { 0xa4ba, 0x0000 }, + { 0xa4bb, 0x0000 }, + { 0xa4bc, 0x0000 }, + { 0xa4bd, 0x0000 }, + { 0xa4be, 0x0000 }, + { 0xa4bf, 0x0000 }, + { 0xa4c0, 0x0000 }, + { 0xa4c1, 0x0000 }, + { 0xa4c2, 0x0000 }, + { 0xa4c3, 0x0000 }, + { 0xa4c4, 0x0000 }, + { 0xa4c5, 0x0000 }, + { 0xa4c6, 0x0000 }, + { 0xa4c7, 0x0000 }, + { 0xa4c8, 0x0000 }, + { 0xa4c9, 0x0000 }, + { 0xa4ca, 0x0000 }, + { 0xa4cb, 0x0000 }, + { 0xa4cc, 0x0000 }, + { 0xa4cd, 0x0000 }, + { 0xa4ce, 0x0000 }, + { 0xa4cf, 0x0000 }, + { 0xa4d0, 0x0000 }, + { 0xa4d1, 0x0000 }, + { 0xa4d2, 0x0000 }, + { 0xa4d3, 0x0000 }, + { 0xa4d4, 0x0000 }, + { 0xa4d5, 0x0000 }, + { 0xa4d6, 0x0000 }, + { 0xa4d7, 0x0000 }, + { 0xa4d8, 0x0000 }, + { 0xa4d9, 0x0000 }, + { 0xa4da, 0x0000 }, + { 0xa4db, 0x0000 }, + { 0xa4dc, 0x0000 }, + { 0xa4dd, 0x0000 }, + { 0xa4de, 0x0000 }, + { 0xa4df, 0x0000 }, + { 0xa4e0, 0x0000 }, + { 0xa4e1, 0x0000 }, + { 0xa4e2, 0x0000 }, + { 0xa4e3, 0x0000 }, + { 0xa4e4, 0x0000 }, + { 0xa4e5, 0x0000 }, + { 0xa4e6, 0x0000 }, + { 0xa4e7, 0x0000 }, + { 0xa4e8, 0x0000 }, + { 0xa4e9, 0x0000 }, + { 0xa4ea, 0x0000 }, + { 0xa4eb, 0x0000 }, + { 0xa4ec, 0x0000 }, + { 0xa4ed, 0x0000 }, + { 0xa4ee, 0x0000 }, + { 0xa4ef, 0x0000 }, + { 0xa4f0, 0x0000 }, + { 0xa4f1, 0x0000 }, + { 0xa4f2, 0x0000 }, + { 0xa4f3, 0x0000 }, + { 0xa4f4, 0x0000 }, + { 0xa4f5, 0x0000 }, + { 0xa4f6, 0x0000 }, + { 0xa4f7, 0x0000 }, + { 0xa4f8, 0x0000 }, + { 0xa4f9, 0x0000 }, + { 0xa4fa, 0x0000 }, + { 0xa4fb, 0x0000 }, + { 0xa4fc, 0x0000 }, + { 0xa4fd, 0x0000 }, + { 0xa4fe, 0x0000 }, + { 0xa4ff, 0x0000 }, + { 0xa500, 0x0000 }, + { 0xa501, 0x0000 }, + { 0xa502, 0x0000 }, + { 0xa503, 0x0000 }, + { 0xa504, 0x0000 }, + { 0xa505, 0x0000 }, + { 0xa506, 0x0000 }, + { 0xa507, 0x0000 }, + { 0xa508, 0x0000 }, + { 0xa509, 0x0000 }, + { 0xa50a, 0x0000 }, + { 0xa50b, 0x0000 }, + { 0xa50c, 0x0000 }, + { 0xa50d, 0x0000 }, + { 0xa50e, 0x0000 }, + { 0xa50f, 0x0000 }, + { 0xa510, 0x0000 }, + { 0xa511, 0x0000 }, + { 0xa512, 0x0000 }, + { 0xa513, 0x0000 }, + { 0xa514, 0x0000 }, + { 0xa515, 0x0000 }, + { 0xa516, 0x0000 }, + { 0xa517, 0x0000 }, + { 0xa518, 0x0000 }, + { 0xa519, 0x0000 }, + { 0xa51a, 0x0000 }, + { 0xa51b, 0x0000 }, + { 0xa51c, 0x0000 }, + { 0xa51d, 0x0000 }, + { 0xa51e, 0x0000 }, + { 0xa51f, 0x0000 }, + { 0xa520, 0x0000 }, + { 0xa521, 0x0000 }, + { 0xa522, 0x0000 }, + { 0xa523, 0x0000 }, + { 0xa524, 0x0000 }, + { 0xa525, 0x0000 }, + { 0xa526, 0x0000 }, + { 0xa527, 0x0000 }, + { 0xa528, 0x0000 }, + { 0xa529, 0x0000 }, + { 0xa52a, 0x0000 }, + { 0xa52b, 0x0000 }, + { 0xa52c, 0x0000 }, + { 0xa52d, 0x0000 }, + { 0xa52e, 0x0000 }, + { 0xa52f, 0x0000 }, + { 0xa530, 0x0000 }, + { 0xa531, 0x0000 }, + { 0xa532, 0x0000 }, + { 0xa533, 0x0000 }, + { 0xa534, 0x0000 }, + { 0xa535, 0x0000 }, + { 0xa536, 0x0000 }, + { 0xa537, 0x0000 }, + { 0xa538, 0x0000 }, + { 0xa539, 0x0000 }, + { 0xa53a, 0x0000 }, + { 0xa53b, 0x0000 }, + { 0xa53c, 0x0000 }, + { 0xa53d, 0x0000 }, + { 0xa53e, 0x0000 }, + { 0xa53f, 0x0000 }, + { 0xa540, 0x0000 }, + { 0xa541, 0x0000 }, + { 0xa542, 0x0000 }, + { 0xa543, 0x0000 }, + { 0xa544, 0x0000 }, + { 0xa545, 0x0000 }, + { 0xa546, 0x0000 }, + { 0xa547, 0x0000 }, + { 0xa548, 0x0000 }, + { 0xa549, 0x0000 }, + { 0xa54a, 0x0000 }, + { 0xa54b, 0x0000 }, + { 0xa54c, 0x0000 }, + { 0xa54d, 0x0000 }, + { 0xa54e, 0x0000 }, + { 0xa54f, 0x0000 }, + { 0xa550, 0x0000 }, + { 0xa551, 0x0000 }, + { 0xa552, 0x0000 }, + { 0xa553, 0x0000 }, + { 0xa554, 0x0000 }, + { 0xa555, 0x0000 }, + { 0xa556, 0x0000 }, + { 0xa557, 0x0000 }, + { 0xa558, 0x0000 }, + { 0xa559, 0x0000 }, + { 0xa55a, 0x0000 }, + { 0xa55b, 0x0000 }, + { 0xa55c, 0x0000 }, + { 0xa55d, 0x0000 }, + { 0xa55e, 0x0000 }, + { 0xa55f, 0x0000 }, + { 0xa560, 0x0000 }, + { 0xa561, 0x0000 }, + { 0xa562, 0x0000 }, + { 0xa563, 0x0000 }, + { 0xa564, 0x0000 }, + { 0xa565, 0x0000 }, + { 0xa566, 0x0000 }, + { 0xa567, 0x0000 }, + { 0xa568, 0x0000 }, + { 0xa569, 0x0000 }, + { 0xa56a, 0x0000 }, + { 0xa56b, 0x0000 }, + { 0xa56c, 0x0000 }, + { 0xa56d, 0x0000 }, + { 0xa56e, 0x0000 }, + { 0xa56f, 0x0000 }, + { 0xa570, 0x0000 }, + { 0xa571, 0x0000 }, + { 0xa572, 0x0000 }, + { 0xa573, 0x0000 }, + { 0xa574, 0x0000 }, + { 0xa575, 0x0000 }, + { 0xa576, 0x0000 }, + { 0xa577, 0x0000 }, + { 0xa578, 0x0000 }, + { 0xa579, 0x0000 }, + { 0xa57a, 0x0000 }, + { 0xa57b, 0x0000 }, + { 0xa57c, 0x0000 }, + { 0xa57d, 0x0000 }, + { 0xa57e, 0x0000 }, + { 0xa57f, 0x0000 }, + { 0xa580, 0x0000 }, + { 0xa581, 0x0000 }, + { 0xa582, 0x0000 }, + { 0xa583, 0x0000 }, + { 0xa584, 0x0000 }, + { 0xa585, 0x0000 }, + { 0xa586, 0x0000 }, + { 0xa587, 0x0000 }, + { 0xa588, 0x0000 }, + { 0xa589, 0x0000 }, + { 0xa58a, 0x0000 }, + { 0xa58b, 0x0000 }, + { 0xa58c, 0x0000 }, + { 0xa58d, 0x0000 }, + { 0xa58e, 0x0000 }, + { 0xa58f, 0x0000 }, + { 0xa590, 0x0000 }, + { 0xa591, 0x0000 }, + { 0xa592, 0x0000 }, + { 0xa593, 0x0000 }, + { 0xa594, 0x0000 }, + { 0xa595, 0x0000 }, + { 0xa596, 0x0000 }, + { 0xa597, 0x0000 }, + { 0xa598, 0x0000 }, + { 0xa599, 0x0000 }, + { 0xa59a, 0x0000 }, + { 0xa59b, 0x0000 }, + { 0xa59c, 0x0000 }, + { 0xa59d, 0x0000 }, + { 0xa59e, 0x0000 }, + { 0xa59f, 0x0000 }, + { 0xa5a0, 0x0000 }, + { 0xa5a1, 0x0000 }, + { 0xa5a2, 0x0000 }, + { 0xa5a3, 0x0000 }, + { 0xa5a4, 0x0000 }, + { 0xa5a5, 0x0000 }, + { 0xa5a6, 0x0000 }, + { 0xa5a7, 0x0000 }, + { 0xa5a8, 0x0000 }, + { 0xa5a9, 0x0000 }, + { 0xa5aa, 0x0000 }, + { 0xa5ab, 0x0000 }, + { 0xa5ac, 0x0000 }, + { 0xa5ad, 0x0000 }, + { 0xa5ae, 0x0000 }, + { 0xa5af, 0x0000 }, + { 0xa5b0, 0x0000 }, + { 0xa5b1, 0x0000 }, + { 0xa5b2, 0x0000 }, + { 0xa5b3, 0x0000 }, + { 0xa5b4, 0x0000 }, + { 0xa5b5, 0x0000 }, + { 0xa5b6, 0x0000 }, + { 0xa5b7, 0x0000 }, + { 0xa5b8, 0x0000 }, + { 0xa5b9, 0x0000 }, + { 0xa5ba, 0x0000 }, + { 0xa5bb, 0x0000 }, + { 0xa5bc, 0x0000 }, + { 0xa5bd, 0x0000 }, + { 0xa5be, 0x0000 }, + { 0xa5bf, 0x0000 }, + { 0xa5c0, 0x0000 }, + { 0xa5c1, 0x0000 }, + { 0xa5c2, 0x0000 }, + { 0xa5c3, 0x0000 }, + { 0xa5c4, 0x0000 }, + { 0xa5c5, 0x0000 }, + { 0xa5c6, 0x0000 }, + { 0xa5c7, 0x0000 }, + { 0xa5c8, 0x0000 }, + { 0xa5c9, 0x0000 }, + { 0xa5ca, 0x0000 }, + { 0xa5cb, 0x0000 }, + { 0xa5cc, 0x0000 }, + { 0xa5cd, 0x0000 }, + { 0xa5ce, 0x0000 }, + { 0xa5cf, 0x0000 }, + { 0xa5d0, 0x0000 }, + { 0xa5d1, 0x0000 }, + { 0xa5d2, 0x0000 }, + { 0xa5d3, 0x0000 }, + { 0xa5d4, 0x0000 }, + { 0xa5d5, 0x0000 }, + { 0xa5d6, 0x0000 }, + { 0xa5d7, 0x0000 }, + { 0xa5d8, 0x0000 }, + { 0xa5d9, 0x0000 }, + { 0xa5da, 0x0000 }, + { 0xa5db, 0x0000 }, + { 0xa5dc, 0x0000 }, + { 0xa5dd, 0x0000 }, + { 0xa5de, 0x0000 }, + { 0xa5df, 0x0000 }, + { 0xa5e0, 0x0000 }, + { 0xa5e1, 0x0000 }, + { 0xa5e2, 0x0000 }, + { 0xa5e3, 0x0000 }, + { 0xa5e4, 0x0000 }, + { 0xa5e5, 0x0000 }, + { 0xa5e6, 0x0000 }, + { 0xa5e7, 0x0000 }, + { 0xa5e8, 0x0000 }, + { 0xa5e9, 0x0000 }, + { 0xa5ea, 0x0000 }, + { 0xa5eb, 0x0000 }, + { 0xa5ec, 0x0000 }, + { 0xa5ed, 0x0000 }, + { 0xa5ee, 0x0000 }, + { 0xa5ef, 0x0000 }, + { 0xa5f0, 0x0000 }, + { 0xa5f1, 0x0000 }, + { 0xa5f2, 0x0000 }, + { 0xa5f3, 0x0000 }, + { 0xa5f4, 0x0000 }, + { 0xa5f5, 0x0000 }, + { 0xa5f6, 0x0000 }, + { 0xa5f7, 0x0000 }, + { 0xa5f8, 0x0000 }, + { 0xa5f9, 0x0000 }, + { 0xa5fa, 0x0000 }, + { 0xa5fb, 0x0000 }, + { 0xa5fc, 0x0000 }, + { 0xa5fd, 0x0000 }, + { 0xa5fe, 0x0000 }, + { 0xa5ff, 0x0000 }, + { 0xa600, 0x0000 }, + { 0xa601, 0x0000 }, + { 0xa602, 0x0000 }, + { 0xa603, 0x0000 }, + { 0xa604, 0x0000 }, + { 0xa605, 0x0000 }, + { 0xa606, 0x0000 }, + { 0xa607, 0x0000 }, + { 0xa608, 0x0000 }, + { 0xa609, 0x0000 }, + { 0xa60a, 0x0000 }, + { 0xa60b, 0x0000 }, + { 0xa60c, 0x0000 }, + { 0xa60d, 0x0000 }, + { 0xa60e, 0x0000 }, + { 0xa60f, 0x0000 }, + { 0xa610, 0x0000 }, + { 0xa611, 0x0000 }, + { 0xa612, 0x0000 }, + { 0xa613, 0x0000 }, + { 0xa614, 0x0000 }, + { 0xa615, 0x0000 }, + { 0xa616, 0x0000 }, + { 0xa617, 0x0000 }, + { 0xa618, 0x0000 }, + { 0xa619, 0x0000 }, + { 0xa61a, 0x0000 }, + { 0xa61b, 0x0000 }, + { 0xa61c, 0x0000 }, + { 0xa61d, 0x0000 }, + { 0xa61e, 0x0000 }, + { 0xa61f, 0x0000 }, + { 0xa620, 0x0000 }, + { 0xa621, 0x0000 }, + { 0xa622, 0x0000 }, + { 0xa623, 0x0000 }, + { 0xa624, 0x0000 }, + { 0xa625, 0x0000 }, + { 0xa626, 0x0000 }, + { 0xa627, 0x0000 }, + { 0xa628, 0x0000 }, + { 0xa629, 0x0000 }, + { 0xa62a, 0x0000 }, + { 0xa62b, 0x0000 }, + { 0xa62c, 0x0000 }, + { 0xa62d, 0x0000 }, + { 0xa62e, 0x0000 }, + { 0xa62f, 0x0000 }, + { 0xa630, 0x0000 }, + { 0xa631, 0x0000 }, + { 0xa632, 0x0000 }, + { 0xa633, 0x0000 }, + { 0xa634, 0x0000 }, + { 0xa635, 0x0000 }, + { 0xa636, 0x0000 }, + { 0xa637, 0x0000 }, + { 0xa638, 0x0000 }, + { 0xa639, 0x0000 }, + { 0xa63a, 0x0000 }, + { 0xa63b, 0x0000 }, + { 0xa63c, 0x0000 }, + { 0xa63d, 0x0000 }, + { 0xa63e, 0x0000 }, + { 0xa63f, 0x0000 }, + { 0xa640, 0x0000 }, + { 0xa641, 0x0000 }, + { 0xa642, 0x0000 }, + { 0xa643, 0x0000 }, + { 0xa644, 0x0000 }, + { 0xa645, 0x0000 }, + { 0xa646, 0x0000 }, + { 0xa647, 0x0000 }, + { 0xa648, 0x0000 }, + { 0xa649, 0x0000 }, + { 0xa64a, 0x0000 }, + { 0xa64b, 0x0000 }, + { 0xa64c, 0x0000 }, + { 0xa64d, 0x0000 }, + { 0xa64e, 0x0000 }, + { 0xa64f, 0x0000 }, + { 0xa650, 0x0000 }, + { 0xa651, 0x0000 }, + { 0xa652, 0x0000 }, + { 0xa653, 0x0000 }, + { 0xa654, 0x0000 }, + { 0xa655, 0x0000 }, + { 0xa656, 0x0000 }, + { 0xa657, 0x0000 }, + { 0xa658, 0x0000 }, + { 0xa659, 0x0000 }, + { 0xa65a, 0x0000 }, + { 0xa65b, 0x0000 }, + { 0xa65c, 0x0000 }, + { 0xa65d, 0x0000 }, + { 0xa65e, 0x0000 }, + { 0xa65f, 0x0000 }, + { 0xa660, 0x0000 }, + { 0xa661, 0x0000 }, + { 0xa662, 0x0000 }, + { 0xa663, 0x0000 }, + { 0xa664, 0x0000 }, + { 0xa665, 0x0000 }, + { 0xa666, 0x0000 }, + { 0xa667, 0x0000 }, + { 0xa668, 0x0000 }, + { 0xa669, 0x0000 }, + { 0xa66a, 0x0000 }, + { 0xa66b, 0x0000 }, + { 0xa66c, 0x0000 }, + { 0xa66d, 0x0000 }, + { 0xa66e, 0x0000 }, + { 0xa66f, 0x0000 }, + { 0xa670, 0x0000 }, + { 0xa671, 0x0000 }, + { 0xa672, 0x0000 }, + { 0xa673, 0x0000 }, + { 0xa674, 0x0000 }, + { 0xa675, 0x0000 }, + { 0xa676, 0x0000 }, + { 0xa677, 0x0000 }, + { 0xa678, 0x0000 }, + { 0xa679, 0x0000 }, + { 0xa67a, 0x0000 }, + { 0xa67b, 0x0000 }, + { 0xa67c, 0x0000 }, + { 0xa67d, 0x0000 }, + { 0xa67e, 0x0000 }, + { 0xa67f, 0x0000 }, + { 0xa680, 0x0000 }, + { 0xa681, 0x0000 }, + { 0xa682, 0x0000 }, + { 0xa683, 0x0000 }, + { 0xa684, 0x0000 }, + { 0xa685, 0x0000 }, + { 0xa686, 0x0000 }, + { 0xa687, 0x0000 }, + { 0xa688, 0x0000 }, + { 0xa689, 0x0000 }, + { 0xa68a, 0x0000 }, + { 0xa68b, 0x0000 }, + { 0xa68c, 0x0000 }, + { 0xa68d, 0x0000 }, + { 0xa68e, 0x0000 }, + { 0xa68f, 0x0000 }, + { 0xa690, 0x0000 }, + { 0xa691, 0x0000 }, + { 0xa692, 0x0000 }, + { 0xa693, 0x0000 }, + { 0xa694, 0x0000 }, + { 0xa695, 0x0000 }, + { 0xa696, 0x0000 }, + { 0xa697, 0x0000 }, + { 0xa698, 0x0000 }, + { 0xa699, 0x0000 }, + { 0xa69a, 0x0000 }, + { 0xa69b, 0x0000 }, + { 0xa69c, 0x0000 }, + { 0xa69d, 0x0000 }, + { 0xa69e, 0x0000 }, + { 0xa69f, 0x0000 }, + { 0xa6a0, 0x0000 }, + { 0xa6a1, 0x0000 }, + { 0xa6a2, 0x0000 }, + { 0xa6a3, 0x0000 }, + { 0xa6a4, 0x0000 }, + { 0xa6a5, 0x0000 }, + { 0xa6a6, 0x0000 }, + { 0xa6a7, 0x0000 }, + { 0xa6a8, 0x0000 }, + { 0xa6a9, 0x0000 }, + { 0xa6aa, 0x0000 }, + { 0xa6ab, 0x0000 }, + { 0xa6ac, 0x0000 }, + { 0xa6ad, 0x0000 }, + { 0xa6ae, 0x0000 }, + { 0xa6af, 0x0000 }, + { 0xa6b0, 0x0000 }, + { 0xa6b1, 0x0000 }, + { 0xa6b2, 0x0000 }, + { 0xa6b3, 0x0000 }, + { 0xa6b4, 0x0000 }, + { 0xa6b5, 0x0000 }, + { 0xa6b6, 0x0000 }, + { 0xa6b7, 0x0000 }, + { 0xa6b8, 0x0000 }, + { 0xa6b9, 0x0000 }, + { 0xa6ba, 0x0000 }, + { 0xa6bb, 0x0000 }, + { 0xa6bc, 0x0000 }, + { 0xa6bd, 0x0000 }, + { 0xa6be, 0x0000 }, + { 0xa6bf, 0x0000 }, + { 0xa6c0, 0x0000 }, + { 0xa6c1, 0x0000 }, + { 0xa6c2, 0x0000 }, + { 0xa6c3, 0x0000 }, + { 0xa6c4, 0x0000 }, + { 0xa6c5, 0x0000 }, + { 0xa6c6, 0x0000 }, + { 0xa6c7, 0x0000 }, + { 0xa6c8, 0x0000 }, + { 0xa6c9, 0x0000 }, + { 0xa6ca, 0x0000 }, + { 0xa6cb, 0x0000 }, + { 0xa6cc, 0x0000 }, + { 0xa6cd, 0x0000 }, + { 0xa6ce, 0x0000 }, + { 0xa6cf, 0x0000 }, + { 0xa6d0, 0x0000 }, + { 0xa6d1, 0x0000 }, + { 0xa6d2, 0x0000 }, + { 0xa6d3, 0x0000 }, + { 0xa6d4, 0x0000 }, + { 0xa6d5, 0x0000 }, + { 0xa6d6, 0x0000 }, + { 0xa6d7, 0x0000 }, + { 0xa6d8, 0x0000 }, + { 0xa6d9, 0x0000 }, + { 0xa6da, 0x0000 }, + { 0xa6db, 0x0000 }, + { 0xa6dc, 0x0000 }, + { 0xa6dd, 0x0000 }, + { 0xa6de, 0x0000 }, + { 0xa6df, 0x0000 }, + { 0xa6e0, 0x0000 }, + { 0xa6e1, 0x0000 }, + { 0xa6e2, 0x0000 }, + { 0xa6e3, 0x0000 }, + { 0xa6e4, 0x0000 }, + { 0xa6e5, 0x0000 }, + { 0xa6e6, 0x0000 }, + { 0xa6e7, 0x0000 }, + { 0xa6e8, 0x0000 }, + { 0xa6e9, 0x0000 }, + { 0xa6ea, 0x0000 }, + { 0xa6eb, 0x0000 }, + { 0xa6ec, 0x0000 }, + { 0xa6ed, 0x0000 }, + { 0xa6ee, 0x0000 }, + { 0xa6ef, 0x0000 }, + { 0xa6f0, 0x0000 }, + { 0xa6f1, 0x0000 }, + { 0xa6f2, 0x0000 }, + { 0xa6f3, 0x0000 }, + { 0xa6f4, 0x0000 }, + { 0xa6f5, 0x0000 }, + { 0xa6f6, 0x0000 }, + { 0xa6f7, 0x0000 }, + { 0xa6f8, 0x0000 }, + { 0xa6f9, 0x0000 }, + { 0xa6fa, 0x0000 }, + { 0xa6fb, 0x0000 }, + { 0xa6fc, 0x0000 }, + { 0xa6fd, 0x0000 }, + { 0xa6fe, 0x0000 }, + { 0xa6ff, 0x0000 }, + { 0xa700, 0x0000 }, + { 0xa701, 0x0000 }, + { 0xa702, 0x0000 }, + { 0xa703, 0x0000 }, + { 0xa704, 0x0000 }, + { 0xa705, 0x0000 }, + { 0xa706, 0x0000 }, + { 0xa707, 0x0000 }, + { 0xa708, 0x0000 }, + { 0xa709, 0x0000 }, + { 0xa70a, 0x0000 }, + { 0xa70b, 0x0000 }, + { 0xa70c, 0x0000 }, + { 0xa70d, 0x0000 }, + { 0xa70e, 0x0000 }, + { 0xa70f, 0x0000 }, + { 0xa710, 0x0000 }, + { 0xa711, 0x0000 }, + { 0xa712, 0x0000 }, + { 0xa713, 0x0000 }, + { 0xa714, 0x0000 }, + { 0xa715, 0x0000 }, + { 0xa716, 0x0000 }, + { 0xa717, 0x0000 }, + { 0xa718, 0x0000 }, + { 0xa719, 0x0000 }, + { 0xa71a, 0x0000 }, + { 0xa71b, 0x0000 }, + { 0xa71c, 0x0000 }, + { 0xa71d, 0x0000 }, + { 0xa71e, 0x0000 }, + { 0xa71f, 0x0000 }, + { 0xa720, 0x0000 }, + { 0xa721, 0x0000 }, + { 0xa722, 0x0000 }, + { 0xa723, 0x0000 }, + { 0xa724, 0x0000 }, + { 0xa725, 0x0000 }, + { 0xa726, 0x0000 }, + { 0xa727, 0x0000 }, + { 0xa728, 0x0000 }, + { 0xa729, 0x0000 }, + { 0xa72a, 0x0000 }, + { 0xa72b, 0x0000 }, + { 0xa72c, 0x0000 }, + { 0xa72d, 0x0000 }, + { 0xa72e, 0x0000 }, + { 0xa72f, 0x0000 }, + { 0xa730, 0x0000 }, + { 0xa731, 0x0000 }, + { 0xa732, 0x0000 }, + { 0xa733, 0x0000 }, + { 0xa734, 0x0000 }, + { 0xa735, 0x0000 }, + { 0xa736, 0x0000 }, + { 0xa737, 0x0000 }, + { 0xa738, 0x0000 }, + { 0xa739, 0x0000 }, + { 0xa73a, 0x0000 }, + { 0xa73b, 0x0000 }, + { 0xa73c, 0x0000 }, + { 0xa73d, 0x0000 }, + { 0xa73e, 0x0000 }, + { 0xa73f, 0x0000 }, + { 0xa740, 0x0000 }, + { 0xa741, 0x0000 }, + { 0xa742, 0x0000 }, + { 0xa743, 0x0000 }, + { 0xa744, 0x0000 }, + { 0xa745, 0x0000 }, + { 0xa746, 0x0000 }, + { 0xa747, 0x0000 }, + { 0xa748, 0x0000 }, + { 0xa749, 0x0000 }, + { 0xa74a, 0x0000 }, + { 0xa74b, 0x0000 }, + { 0xa74c, 0x0000 }, + { 0xa74d, 0x0000 }, + { 0xa74e, 0x0000 }, + { 0xa74f, 0x0000 }, + { 0xa750, 0x0000 }, + { 0xa751, 0x0000 }, + { 0xa752, 0x0000 }, + { 0xa753, 0x0000 }, + { 0xa754, 0x0000 }, + { 0xa755, 0x0000 }, + { 0xa756, 0x0000 }, + { 0xa757, 0x0000 }, + { 0xa758, 0x0000 }, + { 0xa759, 0x0000 }, + { 0xa75a, 0x0000 }, + { 0xa75b, 0x0000 }, + { 0xa75c, 0x0000 }, + { 0xa75d, 0x0000 }, + { 0xa75e, 0x0000 }, + { 0xa75f, 0x0000 }, + { 0xa760, 0x0000 }, + { 0xa761, 0x0000 }, + { 0xa762, 0x0000 }, + { 0xa763, 0x0000 }, + { 0xa764, 0x0000 }, + { 0xa765, 0x0000 }, + { 0xa766, 0x0000 }, + { 0xa767, 0x0000 }, + { 0xa768, 0x0000 }, + { 0xa769, 0x0000 }, + { 0xa76a, 0x0000 }, + { 0xa76b, 0x0000 }, + { 0xa76c, 0x0000 }, + { 0xa76d, 0x0000 }, + { 0xa76e, 0x0000 }, + { 0xa76f, 0x0000 }, + { 0xa770, 0x0000 }, + { 0xa771, 0x0000 }, + { 0xa772, 0x0000 }, + { 0xa773, 0x0000 }, + { 0xa774, 0x0000 }, + { 0xa775, 0x0000 }, + { 0xa776, 0x0000 }, + { 0xa777, 0x0000 }, + { 0xa778, 0x0000 }, + { 0xa779, 0x0000 }, + { 0xa77a, 0x0000 }, + { 0xa77b, 0x0000 }, + { 0xa77c, 0x0000 }, + { 0xa77d, 0x0000 }, + { 0xa77e, 0x0000 }, + { 0xa77f, 0x0000 }, + { 0xa780, 0x0000 }, + { 0xa781, 0x0000 }, + { 0xa782, 0x0000 }, + { 0xa783, 0x0000 }, + { 0xa784, 0x0000 }, + { 0xa785, 0x0000 }, + { 0xa786, 0x0000 }, + { 0xa787, 0x0000 }, + { 0xa788, 0x0000 }, + { 0xa789, 0x0000 }, + { 0xa78a, 0x0000 }, + { 0xa78b, 0x0000 }, + { 0xa78c, 0x0000 }, + { 0xa78d, 0x0000 }, + { 0xa78e, 0x0000 }, + { 0xa78f, 0x0000 }, + { 0xa790, 0x0000 }, + { 0xa791, 0x0000 }, + { 0xa792, 0x0000 }, + { 0xa793, 0x0000 }, + { 0xa794, 0x0000 }, + { 0xa795, 0x0000 }, + { 0xa796, 0x0000 }, + { 0xa797, 0x0000 }, + { 0xa798, 0x0000 }, + { 0xa799, 0x0000 }, + { 0xa79a, 0x0000 }, + { 0xa79b, 0x0000 }, + { 0xa79c, 0x0000 }, + { 0xa79d, 0x0000 }, + { 0xa79e, 0x0000 }, + { 0xa79f, 0x0000 }, + { 0xa7a0, 0x0000 }, + { 0xa7a1, 0x0000 }, + { 0xa7a2, 0x0000 }, + { 0xa7a3, 0x0000 }, + { 0xa7a4, 0x0000 }, + { 0xa7a5, 0x0000 }, + { 0xa7a6, 0x0000 }, + { 0xa7a7, 0x0000 }, + { 0xa7a8, 0x0000 }, + { 0xa7a9, 0x0000 }, + { 0xa7aa, 0x0000 }, + { 0xa7ab, 0x0000 }, + { 0xa7ac, 0x0000 }, + { 0xa7ad, 0x0000 }, + { 0xa7ae, 0x0000 }, + { 0xa7af, 0x0000 }, + { 0xa7b0, 0x0000 }, + { 0xa7b1, 0x0000 }, + { 0xa7b2, 0x0000 }, + { 0xa7b3, 0x0000 }, + { 0xa7b4, 0x0000 }, + { 0xa7b5, 0x0000 }, + { 0xa7b6, 0x0000 }, + { 0xa7b7, 0x0000 }, + { 0xa7b8, 0x0000 }, + { 0xa7b9, 0x0000 }, + { 0xa7ba, 0x0000 }, + { 0xa7bb, 0x0000 }, + { 0xa7bc, 0x0000 }, + { 0xa7bd, 0x0000 }, + { 0xa7be, 0x0000 }, + { 0xa7bf, 0x0000 }, + { 0xa7c0, 0x0000 }, + { 0xa7c1, 0x0000 }, + { 0xa7c2, 0x0000 }, + { 0xa7c3, 0x0000 }, + { 0xa7c4, 0x0000 }, + { 0xa7c5, 0x0000 }, + { 0xa7c6, 0x0000 }, + { 0xa7c7, 0x0000 }, + { 0xa7c8, 0x0000 }, + { 0xa7c9, 0x0000 }, + { 0xa7ca, 0x0000 }, + { 0xa7cb, 0x0000 }, + { 0xa7cc, 0x0000 }, + { 0xa7cd, 0x0000 }, + { 0xa7ce, 0x0000 }, + { 0xa7cf, 0x0000 }, + { 0xa7d0, 0x0000 }, + { 0xa7d1, 0x0000 }, + { 0xa7d2, 0x0000 }, + { 0xa7d3, 0x0000 }, + { 0xa7d4, 0x0000 }, + { 0xa7d5, 0x0000 }, + { 0xa7d6, 0x0000 }, + { 0xa7d7, 0x0000 }, + { 0xa7d8, 0x0000 }, + { 0xa7d9, 0x0000 }, + { 0xa7da, 0x0000 }, + { 0xa7db, 0x0000 }, + { 0xa7dc, 0x0000 }, + { 0xa7dd, 0x0000 }, + { 0xa7de, 0x0000 }, + { 0xa7df, 0x0000 }, + { 0xa7e0, 0x0000 }, + { 0xa7e1, 0x0000 }, + { 0xa7e2, 0x0000 }, + { 0xa7e3, 0x0000 }, + { 0xa7e4, 0x0000 }, + { 0xa7e5, 0x0000 }, + { 0xa7e6, 0x0000 }, + { 0xa7e7, 0x0000 }, + { 0xa7e8, 0x0000 }, + { 0xa7e9, 0x0000 }, + { 0xa7ea, 0x0000 }, + { 0xa7eb, 0x0000 }, + { 0xa7ec, 0x0000 }, + { 0xa7ed, 0x0000 }, + { 0xa7ee, 0x0000 }, + { 0xa7ef, 0x0000 }, + { 0xa7f0, 0x0000 }, + { 0xa7f1, 0x0000 }, + { 0xa7f2, 0x0000 }, + { 0xa7f3, 0x0000 }, + { 0xa7f4, 0x0000 }, + { 0xa7f5, 0x0000 }, + { 0xa7f6, 0x0000 }, + { 0xa7f7, 0x0000 }, + { 0xa7f8, 0x0000 }, + { 0xa7f9, 0x0000 }, + { 0xa7fa, 0x0000 }, + { 0xa7fb, 0x0000 }, + { 0xa7fc, 0x0000 }, + { 0xa7fd, 0x0000 }, + { 0xa7fe, 0x0000 }, + { 0xa7ff, 0x0000 }, + { 0xa800, 0x0000 }, + { 0xa801, 0x0000 }, + { 0xa802, 0x0000 }, + { 0xa803, 0x0000 }, + { 0xa804, 0x0000 }, + { 0xa805, 0x0000 }, + { 0xa806, 0x0000 }, + { 0xa807, 0x0000 }, + { 0xa808, 0x0000 }, + { 0xa809, 0x0000 }, + { 0xa80a, 0x0000 }, + { 0xa80b, 0x0000 }, + { 0xa80c, 0x0000 }, + { 0xa80d, 0x0000 }, + { 0xa80e, 0x0000 }, + { 0xa80f, 0x0000 }, + { 0xa810, 0x0000 }, + { 0xa811, 0x0000 }, + { 0xa812, 0x0000 }, + { 0xa813, 0x0000 }, + { 0xa814, 0x0000 }, + { 0xa815, 0x0000 }, + { 0xa816, 0x0000 }, + { 0xa817, 0x0000 }, + { 0xa818, 0x0000 }, + { 0xa819, 0x0000 }, + { 0xa81a, 0x0000 }, + { 0xa81b, 0x0000 }, + { 0xa81c, 0x0000 }, + { 0xa81d, 0x0000 }, + { 0xa81e, 0x0000 }, + { 0xa81f, 0x0000 }, + { 0xa820, 0x0000 }, + { 0xa821, 0x0000 }, + { 0xa822, 0x0000 }, + { 0xa823, 0x0000 }, + { 0xa824, 0x0000 }, + { 0xa825, 0x0000 }, + { 0xa826, 0x0000 }, + { 0xa827, 0x0000 }, + { 0xa828, 0x0000 }, + { 0xa829, 0x0000 }, + { 0xa82a, 0x0000 }, + { 0xa82b, 0x0000 }, + { 0xa82c, 0x0000 }, + { 0xa82d, 0x0000 }, + { 0xa82e, 0x0000 }, + { 0xa82f, 0x0000 }, + { 0xa830, 0x0000 }, + { 0xa831, 0x0000 }, + { 0xa832, 0x0000 }, + { 0xa833, 0x0000 }, + { 0xa834, 0x0000 }, + { 0xa835, 0x0000 }, + { 0xa836, 0x0000 }, + { 0xa837, 0x0000 }, + { 0xa838, 0x0000 }, + { 0xa839, 0x0000 }, + { 0xa83a, 0x0000 }, + { 0xa83b, 0x0000 }, + { 0xa83c, 0x0000 }, + { 0xa83d, 0x0000 }, + { 0xa83e, 0x0000 }, + { 0xa83f, 0x0000 }, + { 0xa840, 0x0000 }, + { 0xa841, 0x0000 }, + { 0xa842, 0x0000 }, + { 0xa843, 0x0000 }, + { 0xa844, 0x0000 }, + { 0xa845, 0x0000 }, + { 0xa846, 0x0000 }, + { 0xa847, 0x0000 }, + { 0xa848, 0x0000 }, + { 0xa849, 0x0000 }, + { 0xa84a, 0x0000 }, + { 0xa84b, 0x0000 }, + { 0xa84c, 0x0000 }, + { 0xa84d, 0x0000 }, + { 0xa84e, 0x0000 }, + { 0xa84f, 0x0000 }, + { 0xa850, 0x0000 }, + { 0xa851, 0x0000 }, + { 0xa852, 0x0000 }, + { 0xa853, 0x0000 }, + { 0xa854, 0x0000 }, + { 0xa855, 0x0000 }, + { 0xa856, 0x0000 }, + { 0xa857, 0x0000 }, + { 0xa858, 0x0000 }, + { 0xa859, 0x0000 }, + { 0xa85a, 0x0000 }, + { 0xa85b, 0x0000 }, + { 0xa85c, 0x0000 }, + { 0xa85d, 0x0000 }, + { 0xa85e, 0x0000 }, + { 0xa85f, 0x0000 }, + { 0xa860, 0x0000 }, + { 0xa861, 0x0000 }, + { 0xa862, 0x0000 }, + { 0xa863, 0x0000 }, + { 0xa864, 0x0000 }, + { 0xa865, 0x0000 }, + { 0xa866, 0x0000 }, + { 0xa867, 0x0000 }, + { 0xa868, 0x0000 }, + { 0xa869, 0x0000 }, + { 0xa86a, 0x0000 }, + { 0xa86b, 0x0000 }, + { 0xa86c, 0x0000 }, + { 0xa86d, 0x0000 }, + { 0xa86e, 0x0000 }, + { 0xa86f, 0x0000 }, + { 0xa870, 0x0000 }, + { 0xa871, 0x0000 }, + { 0xa872, 0x0000 }, + { 0xa873, 0x0000 }, + { 0xa874, 0x0000 }, + { 0xa875, 0x0000 }, + { 0xa876, 0x0000 }, + { 0xa877, 0x0000 }, + { 0xa878, 0x0000 }, + { 0xa879, 0x0000 }, + { 0xa87a, 0x0000 }, + { 0xa87b, 0x0000 }, + { 0xa87c, 0x0000 }, + { 0xa87d, 0x0000 }, + { 0xa87e, 0x0000 }, + { 0xa87f, 0x0000 }, + { 0xa880, 0x0000 }, + { 0xa881, 0x0000 }, + { 0xa882, 0x0000 }, + { 0xa883, 0x0000 }, + { 0xa884, 0x0000 }, + { 0xa885, 0x0000 }, + { 0xa886, 0x0000 }, + { 0xa887, 0x0000 }, + { 0xa888, 0x0000 }, + { 0xa889, 0x0000 }, + { 0xa88a, 0x0000 }, + { 0xa88b, 0x0000 }, + { 0xa88c, 0x0000 }, + { 0xa88d, 0x0000 }, + { 0xa88e, 0x0000 }, + { 0xa88f, 0x0000 }, + { 0xa890, 0x0000 }, + { 0xa891, 0x0000 }, + { 0xa892, 0x0000 }, + { 0xa893, 0x0000 }, + { 0xa894, 0x0000 }, + { 0xa895, 0x0000 }, + { 0xa896, 0x0000 }, + { 0xa897, 0x0000 }, + { 0xa898, 0x0000 }, + { 0xa899, 0x0000 }, + { 0xa89a, 0x0000 }, + { 0xa89b, 0x0000 }, + { 0xa89c, 0x0000 }, + { 0xa89d, 0x0000 }, + { 0xa89e, 0x0000 }, + { 0xa89f, 0x0000 }, + { 0xa8a0, 0x0000 }, + { 0xa8a1, 0x0000 }, + { 0xa8a2, 0x0000 }, + { 0xa8a3, 0x0000 }, + { 0xa8a4, 0x0000 }, + { 0xa8a5, 0x0000 }, + { 0xa8a6, 0x0000 }, + { 0xa8a7, 0x0000 }, + { 0xa8a8, 0x0000 }, + { 0xa8a9, 0x0000 }, + { 0xa8aa, 0x0000 }, + { 0xa8ab, 0x0000 }, + { 0xa8ac, 0x0000 }, + { 0xa8ad, 0x0000 }, + { 0xa8ae, 0x0000 }, + { 0xa8af, 0x0000 }, + { 0xa8b0, 0x0000 }, + { 0xa8b1, 0x0000 }, + { 0xa8b2, 0x0000 }, + { 0xa8b3, 0x0000 }, + { 0xa8b4, 0x0000 }, + { 0xa8b5, 0x0000 }, + { 0xa8b6, 0x0000 }, + { 0xa8b7, 0x0000 }, + { 0xa8b8, 0x0000 }, + { 0xa8b9, 0x0000 }, + { 0xa8ba, 0x0000 }, + { 0xa8bb, 0x0000 }, + { 0xa8bc, 0x0000 }, + { 0xa8bd, 0x0000 }, + { 0xa8be, 0x0000 }, + { 0xa8bf, 0x0000 }, + { 0xa8c0, 0x0000 }, + { 0xa8c1, 0x0000 }, + { 0xa8c2, 0x0000 }, + { 0xa8c3, 0x0000 }, + { 0xa8c4, 0x0000 }, + { 0xa8c5, 0x0000 }, + { 0xa8c6, 0x0000 }, + { 0xa8c7, 0x0000 }, + { 0xa8c8, 0x0000 }, + { 0xa8c9, 0x0000 }, + { 0xa8ca, 0x0000 }, + { 0xa8cb, 0x0000 }, + { 0xa8cc, 0x0000 }, + { 0xa8cd, 0x0000 }, + { 0xa8ce, 0x0000 }, + { 0xa8cf, 0x0000 }, + { 0xa8d0, 0x0000 }, + { 0xa8d1, 0x0000 }, + { 0xa8d2, 0x0000 }, + { 0xa8d3, 0x0000 }, + { 0xa8d4, 0x0000 }, + { 0xa8d5, 0x0000 }, + { 0xa8d6, 0x0000 }, + { 0xa8d7, 0x0000 }, + { 0xa8d8, 0x0000 }, + { 0xa8d9, 0x0000 }, + { 0xa8da, 0x0000 }, + { 0xa8db, 0x0000 }, + { 0xa8dc, 0x0000 }, + { 0xa8dd, 0x0000 }, + { 0xa8de, 0x0000 }, + { 0xa8df, 0x0000 }, + { 0xa8e0, 0x0000 }, + { 0xa8e1, 0x0000 }, + { 0xa8e2, 0x0000 }, + { 0xa8e3, 0x0000 }, + { 0xa8e4, 0x0000 }, + { 0xa8e5, 0x0000 }, + { 0xa8e6, 0x0000 }, + { 0xa8e7, 0x0000 }, + { 0xa8e8, 0x0000 }, + { 0xa8e9, 0x0000 }, + { 0xa8ea, 0x0000 }, + { 0xa8eb, 0x0000 }, + { 0xa8ec, 0x0000 }, + { 0xa8ed, 0x0000 }, + { 0xa8ee, 0x0000 }, + { 0xa8ef, 0x0000 }, + { 0xa8f0, 0x0000 }, + { 0xa8f1, 0x0000 }, + { 0xa8f2, 0x0000 }, + { 0xa8f3, 0x0000 }, + { 0xa8f4, 0x0000 }, + { 0xa8f5, 0x0000 }, + { 0xa8f6, 0x0000 }, + { 0xa8f7, 0x0000 }, + { 0xa8f8, 0x0000 }, + { 0xa8f9, 0x0000 }, + { 0xa8fa, 0x0000 }, + { 0xa8fb, 0x0000 }, + { 0xa8fc, 0x0000 }, + { 0xa8fd, 0x0000 }, + { 0xa8fe, 0x0000 }, + { 0xa8ff, 0x0000 }, + { 0xa900, 0x0000 }, + { 0xa901, 0x0000 }, + { 0xa902, 0x0000 }, + { 0xa903, 0x0000 }, + { 0xa904, 0x0000 }, + { 0xa905, 0x0000 }, + { 0xa906, 0x0000 }, + { 0xa907, 0x0000 }, + { 0xa908, 0x0000 }, + { 0xa909, 0x0000 }, + { 0xa90a, 0x0000 }, + { 0xa90b, 0x0000 }, + { 0xa90c, 0x0000 }, + { 0xa90d, 0x0000 }, + { 0xa90e, 0x0000 }, + { 0xa90f, 0x0000 }, + { 0xa910, 0x0000 }, + { 0xa911, 0x0000 }, + { 0xa912, 0x0000 }, + { 0xa913, 0x0000 }, + { 0xa914, 0x0000 }, + { 0xa915, 0x0000 }, + { 0xa916, 0x0000 }, + { 0xa917, 0x0000 }, + { 0xa918, 0x0000 }, + { 0xa919, 0x0000 }, + { 0xa91a, 0x0000 }, + { 0xa91b, 0x0000 }, + { 0xa91c, 0x0000 }, + { 0xa91d, 0x0000 }, + { 0xa91e, 0x0000 }, + { 0xa91f, 0x0000 }, + { 0xa920, 0x0000 }, + { 0xa921, 0x0000 }, + { 0xa922, 0x0000 }, + { 0xa923, 0x0000 }, + { 0xa924, 0x0000 }, + { 0xa925, 0x0000 }, + { 0xa926, 0x0000 }, + { 0xa927, 0x0000 }, + { 0xa928, 0x0000 }, + { 0xa929, 0x0000 }, + { 0xa92a, 0x0000 }, + { 0xa92b, 0x0000 }, + { 0xa92c, 0x0000 }, + { 0xa92d, 0x0000 }, + { 0xa92e, 0x0000 }, + { 0xa92f, 0x0000 }, + { 0xa930, 0x0000 }, + { 0xa931, 0x0000 }, + { 0xa932, 0x0000 }, + { 0xa933, 0x0000 }, + { 0xa934, 0x0000 }, + { 0xa935, 0x0000 }, + { 0xa936, 0x0000 }, + { 0xa937, 0x0000 }, + { 0xa938, 0x0000 }, + { 0xa939, 0x0000 }, + { 0xa93a, 0x0000 }, + { 0xa93b, 0x0000 }, + { 0xa93c, 0x0000 }, + { 0xa93d, 0x0000 }, + { 0xa93e, 0x0000 }, + { 0xa93f, 0x0000 }, + { 0xa940, 0x0000 }, + { 0xa941, 0x0000 }, + { 0xa942, 0x0000 }, + { 0xa943, 0x0000 }, + { 0xa944, 0x0000 }, + { 0xa945, 0x0000 }, + { 0xa946, 0x0000 }, + { 0xa947, 0x0000 }, + { 0xa948, 0x0000 }, + { 0xa949, 0x0000 }, + { 0xa94a, 0x0000 }, + { 0xa94b, 0x0000 }, + { 0xa94c, 0x0000 }, + { 0xa94d, 0x0000 }, + { 0xa94e, 0x0000 }, + { 0xa94f, 0x0000 }, + { 0xa950, 0x0000 }, + { 0xa951, 0x0000 }, + { 0xa952, 0x0000 }, + { 0xa953, 0x0000 }, + { 0xa954, 0x0000 }, + { 0xa955, 0x0000 }, + { 0xa956, 0x0000 }, + { 0xa957, 0x0000 }, + { 0xa958, 0x0000 }, + { 0xa959, 0x0000 }, + { 0xa95a, 0x0000 }, + { 0xa95b, 0x0000 }, + { 0xa95c, 0x0000 }, + { 0xa95d, 0x0000 }, + { 0xa95e, 0x0000 }, + { 0xa95f, 0x0000 }, + { 0xa960, 0x0000 }, + { 0xa961, 0x0000 }, + { 0xa962, 0x0000 }, + { 0xa963, 0x0000 }, + { 0xa964, 0x0000 }, + { 0xa965, 0x0000 }, + { 0xa966, 0x0000 }, + { 0xa967, 0x0000 }, + { 0xa968, 0x0000 }, + { 0xa969, 0x0000 }, + { 0xa96a, 0x0000 }, + { 0xa96b, 0x0000 }, + { 0xa96c, 0x0000 }, + { 0xa96d, 0x0000 }, + { 0xa96e, 0x0000 }, + { 0xa96f, 0x0000 }, + { 0xa970, 0x0000 }, + { 0xa971, 0x0000 }, + { 0xa972, 0x0000 }, + { 0xa973, 0x0000 }, + { 0xa974, 0x0000 }, + { 0xa975, 0x0000 }, + { 0xa976, 0x0000 }, + { 0xa977, 0x0000 }, + { 0xa978, 0x0000 }, + { 0xa979, 0x0000 }, + { 0xa97a, 0x0000 }, + { 0xa97b, 0x0000 }, + { 0xa97c, 0x0000 }, + { 0xa97d, 0x0000 }, + { 0xa97e, 0x0000 }, + { 0xa97f, 0x0000 }, + { 0xa980, 0x0000 }, + { 0xa981, 0x0000 }, + { 0xa982, 0x0000 }, + { 0xa983, 0x0000 }, + { 0xa984, 0x0000 }, + { 0xa985, 0x0000 }, + { 0xa986, 0x0000 }, + { 0xa987, 0x0000 }, + { 0xa988, 0x0000 }, + { 0xa989, 0x0000 }, + { 0xa98a, 0x0000 }, + { 0xa98b, 0x0000 }, + { 0xa98c, 0x0000 }, + { 0xa98d, 0x0000 }, + { 0xa98e, 0x0000 }, + { 0xa98f, 0x0000 }, + { 0xa990, 0x0000 }, + { 0xa991, 0x0000 }, + { 0xa992, 0x0000 }, + { 0xa993, 0x0000 }, + { 0xa994, 0x0000 }, + { 0xa995, 0x0000 }, + { 0xa996, 0x0000 }, + { 0xa997, 0x0000 }, + { 0xa998, 0x0000 }, + { 0xa999, 0x0000 }, + { 0xa99a, 0x0000 }, + { 0xa99b, 0x0000 }, + { 0xa99c, 0x0000 }, + { 0xa99d, 0x0000 }, + { 0xa99e, 0x0000 }, + { 0xa99f, 0x0000 }, + { 0xa9a0, 0x0000 }, + { 0xa9a1, 0x0000 }, + { 0xa9a2, 0x0000 }, + { 0xa9a3, 0x0000 }, + { 0xa9a4, 0x0000 }, + { 0xa9a5, 0x0000 }, + { 0xa9a6, 0x0000 }, + { 0xa9a7, 0x0000 }, + { 0xa9a8, 0x0000 }, + { 0xa9a9, 0x0000 }, + { 0xa9aa, 0x0000 }, + { 0xa9ab, 0x0000 }, + { 0xa9ac, 0x0000 }, + { 0xa9ad, 0x0000 }, + { 0xa9ae, 0x0000 }, + { 0xa9af, 0x0000 }, + { 0xa9b0, 0x0000 }, + { 0xa9b1, 0x0000 }, + { 0xa9b2, 0x0000 }, + { 0xa9b3, 0x0000 }, + { 0xa9b4, 0x0000 }, + { 0xa9b5, 0x0000 }, + { 0xa9b6, 0x0000 }, + { 0xa9b7, 0x0000 }, + { 0xa9b8, 0x0000 }, + { 0xa9b9, 0x0000 }, + { 0xa9ba, 0x0000 }, + { 0xa9bb, 0x0000 }, + { 0xa9bc, 0x0000 }, + { 0xa9bd, 0x0000 }, + { 0xa9be, 0x0000 }, + { 0xa9bf, 0x0000 }, + { 0xa9c0, 0x0000 }, + { 0xa9c1, 0x0000 }, + { 0xa9c2, 0x0000 }, + { 0xa9c3, 0x0000 }, + { 0xa9c4, 0x0000 }, + { 0xa9c5, 0x0000 }, + { 0xa9c6, 0x0000 }, + { 0xa9c7, 0x0000 }, + { 0xa9c8, 0x0000 }, + { 0xa9c9, 0x0000 }, + { 0xa9ca, 0x0000 }, + { 0xa9cb, 0x0000 }, + { 0xa9cc, 0x0000 }, + { 0xa9cd, 0x0000 }, + { 0xa9ce, 0x0000 }, + { 0xa9cf, 0x0000 }, + { 0xa9d0, 0x0000 }, + { 0xa9d1, 0x0000 }, + { 0xa9d2, 0x0000 }, + { 0xa9d3, 0x0000 }, + { 0xa9d4, 0x0000 }, + { 0xa9d5, 0x0000 }, + { 0xa9d6, 0x0000 }, + { 0xa9d7, 0x0000 }, + { 0xa9d8, 0x0000 }, + { 0xa9d9, 0x0000 }, + { 0xa9da, 0x0000 }, + { 0xa9db, 0x0000 }, + { 0xa9dc, 0x0000 }, + { 0xa9dd, 0x0000 }, + { 0xa9de, 0x0000 }, + { 0xa9df, 0x0000 }, + { 0xa9e0, 0x0000 }, + { 0xa9e1, 0x0000 }, + { 0xa9e2, 0x0000 }, + { 0xa9e3, 0x0000 }, + { 0xa9e4, 0x0000 }, + { 0xa9e5, 0x0000 }, + { 0xa9e6, 0x0000 }, + { 0xa9e7, 0x0000 }, + { 0xa9e8, 0x0000 }, + { 0xa9e9, 0x0000 }, + { 0xa9ea, 0x0000 }, + { 0xa9eb, 0x0000 }, + { 0xa9ec, 0x0000 }, + { 0xa9ed, 0x0000 }, + { 0xa9ee, 0x0000 }, + { 0xa9ef, 0x0000 }, + { 0xa9f0, 0x0000 }, + { 0xa9f1, 0x0000 }, + { 0xa9f2, 0x0000 }, + { 0xa9f3, 0x0000 }, + { 0xa9f4, 0x0000 }, + { 0xa9f5, 0x0000 }, + { 0xa9f6, 0x0000 }, + { 0xa9f7, 0x0000 }, + { 0xa9f8, 0x0000 }, + { 0xa9f9, 0x0000 }, + { 0xa9fa, 0x0000 }, + { 0xa9fb, 0x0000 }, + { 0xa9fc, 0x0000 }, + { 0xa9fd, 0x0000 }, + { 0xa9fe, 0x0000 }, + { 0xa9ff, 0x0000 }, + { 0xaa00, 0x0000 }, + { 0xaa01, 0x0000 }, + { 0xaa02, 0x0000 }, + { 0xaa03, 0x0000 }, + { 0xaa04, 0x0000 }, + { 0xaa05, 0x0000 }, + { 0xaa06, 0x0000 }, + { 0xaa07, 0x0000 }, + { 0xaa08, 0x0000 }, + { 0xaa09, 0x0000 }, + { 0xaa0a, 0x0000 }, + { 0xaa0b, 0x0000 }, + { 0xaa0c, 0x0000 }, + { 0xaa0d, 0x0000 }, + { 0xaa0e, 0x0000 }, + { 0xaa0f, 0x0000 }, + { 0xaa10, 0x0000 }, + { 0xaa11, 0x0000 }, + { 0xaa12, 0x0000 }, + { 0xaa13, 0x0000 }, + { 0xaa14, 0x0000 }, + { 0xaa15, 0x0000 }, + { 0xaa16, 0x0000 }, + { 0xaa17, 0x0000 }, + { 0xaa18, 0x0000 }, + { 0xaa19, 0x0000 }, + { 0xaa1a, 0x0000 }, + { 0xaa1b, 0x0000 }, + { 0xaa1c, 0x0000 }, + { 0xaa1d, 0x0000 }, + { 0xaa1e, 0x0000 }, + { 0xaa1f, 0x0000 }, + { 0xaa20, 0x0000 }, + { 0xaa21, 0x0000 }, + { 0xaa22, 0x0000 }, + { 0xaa23, 0x0000 }, + { 0xaa24, 0x0000 }, + { 0xaa25, 0x0000 }, + { 0xaa26, 0x0000 }, + { 0xaa27, 0x0000 }, + { 0xaa28, 0x0000 }, + { 0xaa29, 0x0000 }, + { 0xaa2a, 0x0000 }, + { 0xaa2b, 0x0000 }, + { 0xaa2c, 0x0000 }, + { 0xaa2d, 0x0000 }, + { 0xaa2e, 0x0000 }, + { 0xaa2f, 0x0000 }, + { 0xaa30, 0x0000 }, + { 0xaa31, 0x0000 }, + { 0xaa32, 0x0000 }, + { 0xaa33, 0x0000 }, + { 0xaa34, 0x0000 }, + { 0xaa35, 0x0000 }, + { 0xaa36, 0x0000 }, + { 0xaa37, 0x0000 }, + { 0xaa38, 0x0000 }, + { 0xaa39, 0x0000 }, + { 0xaa3a, 0x0000 }, + { 0xaa3b, 0x0000 }, + { 0xaa3c, 0x0000 }, + { 0xaa3d, 0x0000 }, + { 0xaa3e, 0x0000 }, + { 0xaa3f, 0x0000 }, + { 0xaa40, 0x0000 }, + { 0xaa41, 0x0000 }, + { 0xaa42, 0x0000 }, + { 0xaa43, 0x0000 }, + { 0xaa44, 0x0000 }, + { 0xaa45, 0x0000 }, + { 0xaa46, 0x0000 }, + { 0xaa47, 0x0000 }, + { 0xaa48, 0x0000 }, + { 0xaa49, 0x0000 }, + { 0xaa4a, 0x0000 }, + { 0xaa4b, 0x0000 }, + { 0xaa4c, 0x0000 }, + { 0xaa4d, 0x0000 }, + { 0xaa4e, 0x0000 }, + { 0xaa4f, 0x0000 }, + { 0xaa50, 0x0000 }, + { 0xaa51, 0x0000 }, + { 0xaa52, 0x0000 }, + { 0xaa53, 0x0000 }, + { 0xaa54, 0x0000 }, + { 0xaa55, 0x0000 }, + { 0xaa56, 0x0000 }, + { 0xaa57, 0x0000 }, + { 0xaa58, 0x0000 }, + { 0xaa59, 0x0000 }, + { 0xaa5a, 0x0000 }, + { 0xaa5b, 0x0000 }, + { 0xaa5c, 0x0000 }, + { 0xaa5d, 0x0000 }, + { 0xaa5e, 0x0000 }, + { 0xaa5f, 0x0000 }, + { 0xaa60, 0x0000 }, + { 0xaa61, 0x0000 }, + { 0xaa62, 0x0000 }, + { 0xaa63, 0x0000 }, + { 0xaa64, 0x0000 }, + { 0xaa65, 0x0000 }, + { 0xaa66, 0x0000 }, + { 0xaa67, 0x0000 }, + { 0xaa68, 0x0000 }, + { 0xaa69, 0x0000 }, + { 0xaa6a, 0x0000 }, + { 0xaa6b, 0x0000 }, + { 0xaa6c, 0x0000 }, + { 0xaa6d, 0x0000 }, + { 0xaa6e, 0x0000 }, + { 0xaa6f, 0x0000 }, + { 0xaa70, 0x0000 }, + { 0xaa71, 0x0000 }, + { 0xaa72, 0x0000 }, + { 0xaa73, 0x0000 }, + { 0xaa74, 0x0000 }, + { 0xaa75, 0x0000 }, + { 0xaa76, 0x0000 }, + { 0xaa77, 0x0000 }, + { 0xaa78, 0x0000 }, + { 0xaa79, 0x0000 }, + { 0xaa7a, 0x0000 }, + { 0xaa7b, 0x0000 }, + { 0xaa7c, 0x0000 }, + { 0xaa7d, 0x0000 }, + { 0xaa7e, 0x0000 }, + { 0xaa7f, 0x0000 }, + { 0xaa80, 0x0000 }, + { 0xaa81, 0x0000 }, + { 0xaa82, 0x0000 }, + { 0xaa83, 0x0000 }, + { 0xaa84, 0x0000 }, + { 0xaa85, 0x0000 }, + { 0xaa86, 0x0000 }, + { 0xaa87, 0x0000 }, + { 0xaa88, 0x0000 }, + { 0xaa89, 0x0000 }, + { 0xaa8a, 0x0000 }, + { 0xaa8b, 0x0000 }, + { 0xaa8c, 0x0000 }, + { 0xaa8d, 0x0000 }, + { 0xaa8e, 0x0000 }, + { 0xaa8f, 0x0000 }, + { 0xaa90, 0x0000 }, + { 0xaa91, 0x0000 }, + { 0xaa92, 0x0000 }, + { 0xaa93, 0x0000 }, + { 0xaa94, 0x0000 }, + { 0xaa95, 0x0000 }, + { 0xaa96, 0x0000 }, + { 0xaa97, 0x0000 }, + { 0xaa98, 0x0000 }, + { 0xaa99, 0x0000 }, + { 0xaa9a, 0x0000 }, + { 0xaa9b, 0x0000 }, + { 0xaa9c, 0x0000 }, + { 0xaa9d, 0x0000 }, + { 0xaa9e, 0x0000 }, + { 0xaa9f, 0x0000 }, + { 0xaaa0, 0x0000 }, + { 0xaaa1, 0x0000 }, + { 0xaaa2, 0x0000 }, + { 0xaaa3, 0x0000 }, + { 0xaaa4, 0x0000 }, + { 0xaaa5, 0x0000 }, + { 0xaaa6, 0x0000 }, + { 0xaaa7, 0x0000 }, + { 0xaaa8, 0x0000 }, + { 0xaaa9, 0x0000 }, + { 0xaaaa, 0x0000 }, + { 0xaaab, 0x0000 }, + { 0xaaac, 0x0000 }, + { 0xaaad, 0x0000 }, + { 0xaaae, 0x0000 }, + { 0xaaaf, 0x0000 }, + { 0xaab0, 0x0000 }, + { 0xaab1, 0x0000 }, + { 0xaab2, 0x0000 }, + { 0xaab3, 0x0000 }, + { 0xaab4, 0x0000 }, + { 0xaab5, 0x0000 }, + { 0xaab6, 0x0000 }, + { 0xaab7, 0x0000 }, + { 0xaab8, 0x0000 }, + { 0xaab9, 0x0000 }, + { 0xaaba, 0x0000 }, + { 0xaabb, 0x0000 }, + { 0xaabc, 0x0000 }, + { 0xaabd, 0x0000 }, + { 0xaabe, 0x0000 }, + { 0xaabf, 0x0000 }, + { 0xaac0, 0x0000 }, + { 0xaac1, 0x0000 }, + { 0xaac2, 0x0000 }, + { 0xaac3, 0x0000 }, + { 0xaac4, 0x0000 }, + { 0xaac5, 0x0000 }, + { 0xaac6, 0x0000 }, + { 0xaac7, 0x0000 }, + { 0xaac8, 0x0000 }, + { 0xaac9, 0x0000 }, + { 0xaaca, 0x0000 }, + { 0xaacb, 0x0000 }, + { 0xaacc, 0x0000 }, + { 0xaacd, 0x0000 }, + { 0xaace, 0x0000 }, + { 0xaacf, 0x0000 }, + { 0xaad0, 0x0000 }, + { 0xaad1, 0x0000 }, + { 0xaad2, 0x0000 }, + { 0xaad3, 0x0000 }, + { 0xaad4, 0x0000 }, + { 0xaad5, 0x0000 }, + { 0xaad6, 0x0000 }, + { 0xaad7, 0x0000 }, + { 0xaad8, 0x0000 }, + { 0xaad9, 0x0000 }, + { 0xaada, 0x0000 }, + { 0xaadb, 0x0000 }, + { 0xaadc, 0x0000 }, + { 0xaadd, 0x0000 }, + { 0xaade, 0x0000 }, + { 0xaadf, 0x0000 }, + { 0xaae0, 0x0000 }, + { 0xaae1, 0x0000 }, + { 0xaae2, 0x0000 }, + { 0xaae3, 0x0000 }, + { 0xaae4, 0x0000 }, + { 0xaae5, 0x0000 }, + { 0xaae6, 0x0000 }, + { 0xaae7, 0x0000 }, + { 0xaae8, 0x0000 }, + { 0xaae9, 0x0000 }, + { 0xaaea, 0x0000 }, + { 0xaaeb, 0x0000 }, + { 0xaaec, 0x0000 }, + { 0xaaed, 0x0000 }, + { 0xaaee, 0x0000 }, + { 0xaaef, 0x0000 }, + { 0xaaf0, 0x0000 }, + { 0xaaf1, 0x0000 }, + { 0xaaf2, 0x0000 }, + { 0xaaf3, 0x0000 }, + { 0xaaf4, 0x0000 }, + { 0xaaf5, 0x0000 }, + { 0xaaf6, 0x0000 }, + { 0xaaf7, 0x0000 }, + { 0xaaf8, 0x0000 }, + { 0xaaf9, 0x0000 }, + { 0xaafa, 0x0000 }, + { 0xaafb, 0x0000 }, + { 0xaafc, 0x0000 }, + { 0xaafd, 0x0000 }, + { 0xaafe, 0x0000 }, + { 0xaaff, 0x0000 }, + { 0xab00, 0x0000 }, + { 0xab01, 0x0000 }, + { 0xab02, 0x0000 }, + { 0xab03, 0x0000 }, + { 0xab04, 0x0000 }, + { 0xab05, 0x0000 }, + { 0xab06, 0x0000 }, + { 0xab07, 0x0000 }, + { 0xab08, 0x0000 }, + { 0xab09, 0x0000 }, + { 0xab0a, 0x0000 }, + { 0xab0b, 0x0000 }, + { 0xab0c, 0x0000 }, + { 0xab0d, 0x0000 }, + { 0xab0e, 0x0000 }, + { 0xab0f, 0x0000 }, + { 0xab10, 0x0000 }, + { 0xab11, 0x0000 }, + { 0xab12, 0x0000 }, + { 0xab13, 0x0000 }, + { 0xab14, 0x0000 }, + { 0xab15, 0x0000 }, + { 0xab16, 0x0000 }, + { 0xab17, 0x0000 }, + { 0xab18, 0x0000 }, + { 0xab19, 0x0000 }, + { 0xab1a, 0x0000 }, + { 0xab1b, 0x0000 }, + { 0xab1c, 0x0000 }, + { 0xab1d, 0x0000 }, + { 0xab1e, 0x0000 }, + { 0xab1f, 0x0000 }, + { 0xab20, 0x0000 }, + { 0xab21, 0x0000 }, + { 0xab22, 0x0000 }, + { 0xab23, 0x0000 }, + { 0xab24, 0x0000 }, + { 0xab25, 0x0000 }, + { 0xab26, 0x0000 }, + { 0xab27, 0x0000 }, + { 0xab28, 0x0000 }, + { 0xab29, 0x0000 }, + { 0xab2a, 0x0000 }, + { 0xab2b, 0x0000 }, + { 0xab2c, 0x0000 }, + { 0xab2d, 0x0000 }, + { 0xab2e, 0x0000 }, + { 0xab2f, 0x0000 }, + { 0xab30, 0x0000 }, + { 0xab31, 0x0000 }, + { 0xab32, 0x0000 }, + { 0xab33, 0x0000 }, + { 0xab34, 0x0000 }, + { 0xab35, 0x0000 }, + { 0xab36, 0x0000 }, + { 0xab37, 0x0000 }, + { 0xab38, 0x0000 }, + { 0xab39, 0x0000 }, + { 0xab3a, 0x0000 }, + { 0xab3b, 0x0000 }, + { 0xab3c, 0x0000 }, + { 0xab3d, 0x0000 }, + { 0xab3e, 0x0000 }, + { 0xab3f, 0x0000 }, + { 0xab40, 0x0000 }, + { 0xab41, 0x0000 }, + { 0xab42, 0x0000 }, + { 0xab43, 0x0000 }, + { 0xab44, 0x0000 }, + { 0xab45, 0x0000 }, + { 0xab46, 0x0000 }, + { 0xab47, 0x0000 }, + { 0xab48, 0x0000 }, + { 0xab49, 0x0000 }, + { 0xab4a, 0x0000 }, + { 0xab4b, 0x0000 }, + { 0xab4c, 0x0000 }, + { 0xab4d, 0x0000 }, + { 0xab4e, 0x0000 }, + { 0xab4f, 0x0000 }, + { 0xab50, 0x0000 }, + { 0xab51, 0x0000 }, + { 0xab52, 0x0000 }, + { 0xab53, 0x0000 }, + { 0xab54, 0x0000 }, + { 0xab55, 0x0000 }, + { 0xab56, 0x0000 }, + { 0xab57, 0x0000 }, + { 0xab58, 0x0000 }, + { 0xab59, 0x0000 }, + { 0xab5a, 0x0000 }, + { 0xab5b, 0x0000 }, + { 0xab5c, 0x0000 }, + { 0xab5d, 0x0000 }, + { 0xab5e, 0x0000 }, + { 0xab5f, 0x0000 }, + { 0xab60, 0x0000 }, + { 0xab61, 0x0000 }, + { 0xab62, 0x0000 }, + { 0xab63, 0x0000 }, + { 0xab64, 0x0000 }, + { 0xab65, 0x0000 }, + { 0xab66, 0x0000 }, + { 0xab67, 0x0000 }, + { 0xab68, 0x0000 }, + { 0xab69, 0x0000 }, + { 0xab6a, 0x0000 }, + { 0xab6b, 0x0000 }, + { 0xab6c, 0x0000 }, + { 0xab6d, 0x0000 }, + { 0xab6e, 0x0000 }, + { 0xab6f, 0x0000 }, + { 0xab70, 0x0000 }, + { 0xab71, 0x0000 }, + { 0xab72, 0x0000 }, + { 0xab73, 0x0000 }, + { 0xab74, 0x0000 }, + { 0xab75, 0x0000 }, + { 0xab76, 0x0000 }, + { 0xab77, 0x0000 }, + { 0xab78, 0x0000 }, + { 0xab79, 0x0000 }, + { 0xab7a, 0x0000 }, + { 0xab7b, 0x0000 }, + { 0xab7c, 0x0000 }, + { 0xab7d, 0x0000 }, + { 0xab7e, 0x0000 }, + { 0xab7f, 0x0000 }, + { 0xab80, 0x0000 }, + { 0xab81, 0x0000 }, + { 0xab82, 0x0000 }, + { 0xab83, 0x0000 }, + { 0xab84, 0x0000 }, + { 0xab85, 0x0000 }, + { 0xab86, 0x0000 }, + { 0xab87, 0x0000 }, + { 0xab88, 0x0000 }, + { 0xab89, 0x0000 }, + { 0xab8a, 0x0000 }, + { 0xab8b, 0x0000 }, + { 0xab8c, 0x0000 }, + { 0xab8d, 0x0000 }, + { 0xab8e, 0x0000 }, + { 0xab8f, 0x0000 }, + { 0xab90, 0x0000 }, + { 0xab91, 0x0000 }, + { 0xab92, 0x0000 }, + { 0xab93, 0x0000 }, + { 0xab94, 0x0000 }, + { 0xab95, 0x0000 }, + { 0xab96, 0x0000 }, + { 0xab97, 0x0000 }, + { 0xab98, 0x0000 }, + { 0xab99, 0x0000 }, + { 0xab9a, 0x0000 }, + { 0xab9b, 0x0000 }, + { 0xab9c, 0x0000 }, + { 0xab9d, 0x0000 }, + { 0xab9e, 0x0000 }, + { 0xab9f, 0x0000 }, + { 0xaba0, 0x0000 }, + { 0xaba1, 0x0000 }, + { 0xaba2, 0x0000 }, + { 0xaba3, 0x0000 }, + { 0xaba4, 0x0000 }, + { 0xaba5, 0x0000 }, + { 0xaba6, 0x0000 }, + { 0xaba7, 0x0000 }, + { 0xaba8, 0x0000 }, + { 0xaba9, 0x0000 }, + { 0xabaa, 0x0000 }, + { 0xabab, 0x0000 }, + { 0xabac, 0x0000 }, + { 0xabad, 0x0000 }, + { 0xabae, 0x0000 }, + { 0xabaf, 0x0000 }, + { 0xabb0, 0x0000 }, + { 0xabb1, 0x0000 }, + { 0xabb2, 0x0000 }, + { 0xabb3, 0x0000 }, + { 0xabb4, 0x0000 }, + { 0xabb5, 0x0000 }, + { 0xabb6, 0x0000 }, + { 0xabb7, 0x0000 }, + { 0xabb8, 0x0000 }, + { 0xabb9, 0x0000 }, + { 0xabba, 0x0000 }, + { 0xabbb, 0x0000 }, + { 0xabbc, 0x0000 }, + { 0xabbd, 0x0000 }, + { 0xabbe, 0x0000 }, + { 0xabbf, 0x0000 }, + { 0xabc0, 0x0000 }, + { 0xabc1, 0x0000 }, + { 0xabc2, 0x0000 }, + { 0xabc3, 0x0000 }, + { 0xabc4, 0x0000 }, + { 0xabc5, 0x0000 }, + { 0xabc6, 0x0000 }, + { 0xabc7, 0x0000 }, + { 0xabc8, 0x0000 }, + { 0xabc9, 0x0000 }, + { 0xabca, 0x0000 }, + { 0xabcb, 0x0000 }, + { 0xabcc, 0x0000 }, + { 0xabcd, 0x0000 }, + { 0xabce, 0x0000 }, + { 0xabcf, 0x0000 }, + { 0xabd0, 0x0000 }, + { 0xabd1, 0x0000 }, + { 0xabd2, 0x0000 }, + { 0xabd3, 0x0000 }, + { 0xabd4, 0x0000 }, + { 0xabd5, 0x0000 }, + { 0xabd6, 0x0000 }, + { 0xabd7, 0x0000 }, + { 0xabd8, 0x0000 }, + { 0xabd9, 0x0000 }, + { 0xabda, 0x0000 }, + { 0xabdb, 0x0000 }, + { 0xabdc, 0x0000 }, + { 0xabdd, 0x0000 }, + { 0xabde, 0x0000 }, + { 0xabdf, 0x0000 }, + { 0xabe0, 0x0000 }, + { 0xabe1, 0x0000 }, + { 0xabe2, 0x0000 }, + { 0xabe3, 0x0000 }, + { 0xabe4, 0x0000 }, + { 0xabe5, 0x0000 }, + { 0xabe6, 0x0000 }, + { 0xabe7, 0x0000 }, + { 0xabe8, 0x0000 }, + { 0xabe9, 0x0000 }, + { 0xabea, 0x0000 }, + { 0xabeb, 0x0000 }, + { 0xabec, 0x0000 }, + { 0xabed, 0x0000 }, + { 0xabee, 0x0000 }, + { 0xabef, 0x0000 }, + { 0xabf0, 0x0000 }, + { 0xabf1, 0x0000 }, + { 0xabf2, 0x0000 }, + { 0xabf3, 0x0000 }, + { 0xabf4, 0x0000 }, + { 0xabf5, 0x0000 }, + { 0xabf6, 0x0000 }, + { 0xabf7, 0x0000 }, + { 0xabf8, 0x0000 }, + { 0xabf9, 0x0000 }, + { 0xabfa, 0x0000 }, + { 0xabfb, 0x0000 }, + { 0xabfc, 0x0000 }, + { 0xabfd, 0x0000 }, + { 0xabfe, 0x0000 }, + { 0xabff, 0x0000 }, + { 0xac00, 0x0000 }, + { 0xac01, 0x0000 }, + { 0xac02, 0x0000 }, + { 0xac03, 0x0000 }, + { 0xac04, 0x0000 }, + { 0xac05, 0x0000 }, + { 0xac06, 0x0000 }, + { 0xac07, 0x0000 }, + { 0xac08, 0x0000 }, + { 0xac09, 0x0000 }, + { 0xac0a, 0x0000 }, + { 0xac0b, 0x0000 }, + { 0xac0c, 0x0000 }, + { 0xac0d, 0x0000 }, + { 0xac0e, 0x0000 }, + { 0xac0f, 0x0000 }, + { 0xac10, 0x0000 }, + { 0xac11, 0x0000 }, + { 0xac12, 0x0000 }, + { 0xac13, 0x0000 }, + { 0xac14, 0x0000 }, + { 0xac15, 0x0000 }, + { 0xac16, 0x0000 }, + { 0xac17, 0x0000 }, + { 0xac18, 0x0000 }, + { 0xac19, 0x0000 }, + { 0xac1a, 0x0000 }, + { 0xac1b, 0x0000 }, + { 0xac1c, 0x0000 }, + { 0xac1d, 0x0000 }, + { 0xac1e, 0x0000 }, + { 0xac1f, 0x0000 }, + { 0xac20, 0x0000 }, + { 0xac21, 0x0000 }, + { 0xac22, 0x0000 }, + { 0xac23, 0x0000 }, + { 0xac24, 0x0000 }, + { 0xac25, 0x0000 }, + { 0xac26, 0x0000 }, + { 0xac27, 0x0000 }, + { 0xac28, 0x0000 }, + { 0xac29, 0x0000 }, + { 0xac2a, 0x0000 }, + { 0xac2b, 0x0000 }, + { 0xac2c, 0x0000 }, + { 0xac2d, 0x0000 }, + { 0xac2e, 0x0000 }, + { 0xac2f, 0x0000 }, + { 0xac30, 0x0000 }, + { 0xac31, 0x0000 }, + { 0xac32, 0x0000 }, + { 0xac33, 0x0000 }, + { 0xac34, 0x0000 }, + { 0xac35, 0x0000 }, + { 0xac36, 0x0000 }, + { 0xac37, 0x0000 }, + { 0xac38, 0x0000 }, + { 0xac39, 0x0000 }, + { 0xac3a, 0x0000 }, + { 0xac3b, 0x0000 }, + { 0xac3c, 0x0000 }, + { 0xac3d, 0x0000 }, + { 0xac3e, 0x0000 }, + { 0xac3f, 0x0000 }, + { 0xac40, 0x0000 }, + { 0xac41, 0x0000 }, + { 0xac42, 0x0000 }, + { 0xac43, 0x0000 }, + { 0xac44, 0x0000 }, + { 0xac45, 0x0000 }, + { 0xac46, 0x0000 }, + { 0xac47, 0x0000 }, + { 0xac48, 0x0000 }, + { 0xac49, 0x0000 }, + { 0xac4a, 0x0000 }, + { 0xac4b, 0x0000 }, + { 0xac4c, 0x0000 }, + { 0xac4d, 0x0000 }, + { 0xac4e, 0x0000 }, + { 0xac4f, 0x0000 }, + { 0xac50, 0x0000 }, + { 0xac51, 0x0000 }, + { 0xac52, 0x0000 }, + { 0xac53, 0x0000 }, + { 0xac54, 0x0000 }, + { 0xac55, 0x0000 }, + { 0xac56, 0x0000 }, + { 0xac57, 0x0000 }, + { 0xac58, 0x0000 }, + { 0xac59, 0x0000 }, + { 0xac5a, 0x0000 }, + { 0xac5b, 0x0000 }, + { 0xac5c, 0x0000 }, + { 0xac5d, 0x0000 }, + { 0xac5e, 0x0000 }, + { 0xac5f, 0x0000 }, + { 0xac60, 0x0000 }, + { 0xac61, 0x0000 }, + { 0xac62, 0x0000 }, + { 0xac63, 0x0000 }, + { 0xac64, 0x0000 }, + { 0xac65, 0x0000 }, + { 0xac66, 0x0000 }, + { 0xac67, 0x0000 }, + { 0xac68, 0x0000 }, + { 0xac69, 0x0000 }, + { 0xac6a, 0x0000 }, + { 0xac6b, 0x0000 }, + { 0xac6c, 0x0000 }, + { 0xac6d, 0x0000 }, + { 0xac6e, 0x0000 }, + { 0xac6f, 0x0000 }, + { 0xac70, 0x0000 }, + { 0xac71, 0x0000 }, + { 0xac72, 0x0000 }, + { 0xac73, 0x0000 }, + { 0xac74, 0x0000 }, + { 0xac75, 0x0000 }, + { 0xac76, 0x0000 }, + { 0xac77, 0x0000 }, + { 0xac78, 0x0000 }, + { 0xac79, 0x0000 }, + { 0xac7a, 0x0000 }, + { 0xac7b, 0x0000 }, + { 0xac7c, 0x0000 }, + { 0xac7d, 0x0000 }, + { 0xac7e, 0x0000 }, + { 0xac7f, 0x0000 }, + { 0xac80, 0x0000 }, + { 0xac81, 0x0000 }, + { 0xac82, 0x0000 }, + { 0xac83, 0x0000 }, + { 0xac84, 0x0000 }, + { 0xac85, 0x0000 }, + { 0xac86, 0x0000 }, + { 0xac87, 0x0000 }, + { 0xac88, 0x0000 }, + { 0xac89, 0x0000 }, + { 0xac8a, 0x0000 }, + { 0xac8b, 0x0000 }, + { 0xac8c, 0x0000 }, + { 0xac8d, 0x0000 }, + { 0xac8e, 0x0000 }, + { 0xac8f, 0x0000 }, + { 0xac90, 0x0000 }, + { 0xac91, 0x0000 }, + { 0xac92, 0x0000 }, + { 0xac93, 0x0000 }, + { 0xac94, 0x0000 }, + { 0xac95, 0x0000 }, + { 0xac96, 0x0000 }, + { 0xac97, 0x0000 }, + { 0xac98, 0x0000 }, + { 0xac99, 0x0000 }, + { 0xac9a, 0x0000 }, + { 0xac9b, 0x0000 }, + { 0xac9c, 0x0000 }, + { 0xac9d, 0x0000 }, + { 0xac9e, 0x0000 }, + { 0xac9f, 0x0000 }, + { 0xaca0, 0x0000 }, + { 0xaca1, 0x0000 }, + { 0xaca2, 0x0000 }, + { 0xaca3, 0x0000 }, + { 0xaca4, 0x0000 }, + { 0xaca5, 0x0000 }, + { 0xaca6, 0x0000 }, + { 0xaca7, 0x0000 }, + { 0xaca8, 0x0000 }, + { 0xaca9, 0x0000 }, + { 0xacaa, 0x0000 }, + { 0xacab, 0x0000 }, + { 0xacac, 0x0000 }, + { 0xacad, 0x0000 }, + { 0xacae, 0x0000 }, + { 0xacaf, 0x0000 }, + { 0xacb0, 0x0000 }, + { 0xacb1, 0x0000 }, + { 0xacb2, 0x0000 }, + { 0xacb3, 0x0000 }, + { 0xacb4, 0x0000 }, + { 0xacb5, 0x0000 }, + { 0xacb6, 0x0000 }, + { 0xacb7, 0x0000 }, + { 0xacb8, 0x0000 }, + { 0xacb9, 0x0000 }, + { 0xacba, 0x0000 }, + { 0xacbb, 0x0000 }, + { 0xacbc, 0x0000 }, + { 0xacbd, 0x0000 }, + { 0xacbe, 0x0000 }, + { 0xacbf, 0x0000 }, + { 0xacc0, 0x0000 }, + { 0xacc1, 0x0000 }, + { 0xacc2, 0x0000 }, + { 0xacc3, 0x0000 }, + { 0xacc4, 0x0000 }, + { 0xacc5, 0x0000 }, + { 0xacc6, 0x0000 }, + { 0xacc7, 0x0000 }, + { 0xacc8, 0x0000 }, + { 0xacc9, 0x0000 }, + { 0xacca, 0x0000 }, + { 0xaccb, 0x0000 }, + { 0xaccc, 0x0000 }, + { 0xaccd, 0x0000 }, + { 0xacce, 0x0000 }, + { 0xaccf, 0x0000 }, + { 0xacd0, 0x0000 }, + { 0xacd1, 0x0000 }, + { 0xacd2, 0x0000 }, + { 0xacd3, 0x0000 }, + { 0xacd4, 0x0000 }, + { 0xacd5, 0x0000 }, + { 0xacd6, 0x0000 }, + { 0xacd7, 0x0000 }, + { 0xacd8, 0x0000 }, + { 0xacd9, 0x0000 }, + { 0xacda, 0x0000 }, + { 0xacdb, 0x0000 }, + { 0xacdc, 0x0000 }, + { 0xacdd, 0x0000 }, + { 0xacde, 0x0000 }, + { 0xacdf, 0x0000 }, + { 0xace0, 0x0000 }, + { 0xace1, 0x0000 }, + { 0xace2, 0x0000 }, + { 0xace3, 0x0000 }, + { 0xace4, 0x0000 }, + { 0xace5, 0x0000 }, + { 0xace6, 0x0000 }, + { 0xace7, 0x0000 }, + { 0xace8, 0x0000 }, + { 0xace9, 0x0000 }, + { 0xacea, 0x0000 }, + { 0xaceb, 0x0000 }, + { 0xacec, 0x0000 }, + { 0xaced, 0x0000 }, + { 0xacee, 0x0000 }, + { 0xacef, 0x0000 }, + { 0xacf0, 0x0000 }, + { 0xacf1, 0x0000 }, + { 0xacf2, 0x0000 }, + { 0xacf3, 0x0000 }, + { 0xacf4, 0x0000 }, + { 0xacf5, 0x0000 }, + { 0xacf6, 0x0000 }, + { 0xacf7, 0x0000 }, + { 0xacf8, 0x0000 }, + { 0xacf9, 0x0000 }, + { 0xacfa, 0x0000 }, + { 0xacfb, 0x0000 }, + { 0xacfc, 0x0000 }, + { 0xacfd, 0x0000 }, + { 0xacfe, 0x0000 }, + { 0xacff, 0x0000 }, + { 0xad00, 0x0000 }, + { 0xad01, 0x0000 }, + { 0xad02, 0x0000 }, + { 0xad03, 0x0000 }, + { 0xad04, 0x0000 }, + { 0xad05, 0x0000 }, + { 0xad06, 0x0000 }, + { 0xad07, 0x0000 }, + { 0xad08, 0x0000 }, + { 0xad09, 0x0000 }, + { 0xad0a, 0x0000 }, + { 0xad0b, 0x0000 }, + { 0xad0c, 0x0000 }, + { 0xad0d, 0x0000 }, + { 0xad0e, 0x0000 }, + { 0xad0f, 0x0000 }, + { 0xad10, 0x0000 }, + { 0xad11, 0x0000 }, + { 0xad12, 0x0000 }, + { 0xad13, 0x0000 }, + { 0xad14, 0x0000 }, + { 0xad15, 0x0000 }, + { 0xad16, 0x0000 }, + { 0xad17, 0x0000 }, + { 0xad18, 0x0000 }, + { 0xad19, 0x0000 }, + { 0xad1a, 0x0000 }, + { 0xad1b, 0x0000 }, + { 0xad1c, 0x0000 }, + { 0xad1d, 0x0000 }, + { 0xad1e, 0x0000 }, + { 0xad1f, 0x0000 }, + { 0xad20, 0x0000 }, + { 0xad21, 0x0000 }, + { 0xad22, 0x0000 }, + { 0xad23, 0x0000 }, + { 0xad24, 0x0000 }, + { 0xad25, 0x0000 }, + { 0xad26, 0x0000 }, + { 0xad27, 0x0000 }, + { 0xad28, 0x0000 }, + { 0xad29, 0x0000 }, + { 0xad2a, 0x0000 }, + { 0xad2b, 0x0000 }, + { 0xad2c, 0x0000 }, + { 0xad2d, 0x0000 }, + { 0xad2e, 0x0000 }, + { 0xad2f, 0x0000 }, + { 0xad30, 0x0000 }, + { 0xad31, 0x0000 }, + { 0xad32, 0x0000 }, + { 0xad33, 0x0000 }, + { 0xad34, 0x0000 }, + { 0xad35, 0x0000 }, + { 0xad36, 0x0000 }, + { 0xad37, 0x0000 }, + { 0xad38, 0x0000 }, + { 0xad39, 0x0000 }, + { 0xad3a, 0x0000 }, + { 0xad3b, 0x0000 }, + { 0xad3c, 0x0000 }, + { 0xad3d, 0x0000 }, + { 0xad3e, 0x0000 }, + { 0xad3f, 0x0000 }, + { 0xad40, 0x0000 }, + { 0xad41, 0x0000 }, + { 0xad42, 0x0000 }, + { 0xad43, 0x0000 }, + { 0xad44, 0x0000 }, + { 0xad45, 0x0000 }, + { 0xad46, 0x0000 }, + { 0xad47, 0x0000 }, + { 0xad48, 0x0000 }, + { 0xad49, 0x0000 }, + { 0xad4a, 0x0000 }, + { 0xad4b, 0x0000 }, + { 0xad4c, 0x0000 }, + { 0xad4d, 0x0000 }, + { 0xad4e, 0x0000 }, + { 0xad4f, 0x0000 }, + { 0xad50, 0x0000 }, + { 0xad51, 0x0000 }, + { 0xad52, 0x0000 }, + { 0xad53, 0x0000 }, + { 0xad54, 0x0000 }, + { 0xad55, 0x0000 }, + { 0xad56, 0x0000 }, + { 0xad57, 0x0000 }, + { 0xad58, 0x0000 }, + { 0xad59, 0x0000 }, + { 0xad5a, 0x0000 }, + { 0xad5b, 0x0000 }, + { 0xad5c, 0x0000 }, + { 0xad5d, 0x0000 }, + { 0xad5e, 0x0000 }, + { 0xad5f, 0x0000 }, + { 0xad60, 0x0000 }, + { 0xad61, 0x0000 }, + { 0xad62, 0x0000 }, + { 0xad63, 0x0000 }, + { 0xad64, 0x0000 }, + { 0xad65, 0x0000 }, + { 0xad66, 0x0000 }, + { 0xad67, 0x0000 }, + { 0xad68, 0x0000 }, + { 0xad69, 0x0000 }, + { 0xad6a, 0x0000 }, + { 0xad6b, 0x0000 }, + { 0xad6c, 0x0000 }, + { 0xad6d, 0x0000 }, + { 0xad6e, 0x0000 }, + { 0xad6f, 0x0000 }, + { 0xad70, 0x0000 }, + { 0xad71, 0x0000 }, + { 0xad72, 0x0000 }, + { 0xad73, 0x0000 }, + { 0xad74, 0x0000 }, + { 0xad75, 0x0000 }, + { 0xad76, 0x0000 }, + { 0xad77, 0x0000 }, + { 0xad78, 0x0000 }, + { 0xad79, 0x0000 }, + { 0xad7a, 0x0000 }, + { 0xad7b, 0x0000 }, + { 0xad7c, 0x0000 }, + { 0xad7d, 0x0000 }, + { 0xad7e, 0x0000 }, + { 0xad7f, 0x0000 }, + { 0xad80, 0x0000 }, + { 0xad81, 0x0000 }, + { 0xad82, 0x0000 }, + { 0xad83, 0x0000 }, + { 0xad84, 0x0000 }, + { 0xad85, 0x0000 }, + { 0xad86, 0x0000 }, + { 0xad87, 0x0000 }, + { 0xad88, 0x0000 }, + { 0xad89, 0x0000 }, + { 0xad8a, 0x0000 }, + { 0xad8b, 0x0000 }, + { 0xad8c, 0x0000 }, + { 0xad8d, 0x0000 }, + { 0xad8e, 0x0000 }, + { 0xad8f, 0x0000 }, + { 0xad90, 0x0000 }, + { 0xad91, 0x0000 }, + { 0xad92, 0x0000 }, + { 0xad93, 0x0000 }, + { 0xad94, 0x0000 }, + { 0xad95, 0x0000 }, + { 0xad96, 0x0000 }, + { 0xad97, 0x0000 }, + { 0xad98, 0x0000 }, + { 0xad99, 0x0000 }, + { 0xad9a, 0x0000 }, + { 0xad9b, 0x0000 }, + { 0xad9c, 0x0000 }, + { 0xad9d, 0x0000 }, + { 0xad9e, 0x0000 }, + { 0xad9f, 0x0000 }, + { 0xada0, 0x0000 }, + { 0xada1, 0x0000 }, + { 0xada2, 0x0000 }, + { 0xada3, 0x0000 }, + { 0xada4, 0x0000 }, + { 0xada5, 0x0000 }, + { 0xada6, 0x0000 }, + { 0xada7, 0x0000 }, + { 0xada8, 0x0000 }, + { 0xada9, 0x0000 }, + { 0xadaa, 0x0000 }, + { 0xadab, 0x0000 }, + { 0xadac, 0x0000 }, + { 0xadad, 0x0000 }, + { 0xadae, 0x0000 }, + { 0xadaf, 0x0000 }, + { 0xadb0, 0x0000 }, + { 0xadb1, 0x0000 }, + { 0xadb2, 0x0000 }, + { 0xadb3, 0x0000 }, + { 0xadb4, 0x0000 }, + { 0xadb5, 0x0000 }, + { 0xadb6, 0x0000 }, + { 0xadb7, 0x0000 }, + { 0xadb8, 0x0000 }, + { 0xadb9, 0x0000 }, + { 0xadba, 0x0000 }, + { 0xadbb, 0x0000 }, + { 0xadbc, 0x0000 }, + { 0xadbd, 0x0000 }, + { 0xadbe, 0x0000 }, + { 0xadbf, 0x0000 }, + { 0xadc0, 0x0000 }, + { 0xadc1, 0x0000 }, + { 0xadc2, 0x0000 }, + { 0xadc3, 0x0000 }, + { 0xadc4, 0x0000 }, + { 0xadc5, 0x0000 }, + { 0xadc6, 0x0000 }, + { 0xadc7, 0x0000 }, + { 0xadc8, 0x0000 }, + { 0xadc9, 0x0000 }, + { 0xadca, 0x0000 }, + { 0xadcb, 0x0000 }, + { 0xadcc, 0x0000 }, + { 0xadcd, 0x0000 }, + { 0xadce, 0x0000 }, + { 0xadcf, 0x0000 }, + { 0xadd0, 0x0000 }, + { 0xadd1, 0x0000 }, + { 0xadd2, 0x0000 }, + { 0xadd3, 0x0000 }, + { 0xadd4, 0x0000 }, + { 0xadd5, 0x0000 }, + { 0xadd6, 0x0000 }, + { 0xadd7, 0x0000 }, + { 0xadd8, 0x0000 }, + { 0xadd9, 0x0000 }, + { 0xadda, 0x0000 }, + { 0xaddb, 0x0000 }, + { 0xaddc, 0x0000 }, + { 0xaddd, 0x0000 }, + { 0xadde, 0x0000 }, + { 0xaddf, 0x0000 }, + { 0xade0, 0x0000 }, + { 0xade1, 0x0000 }, + { 0xade2, 0x0000 }, + { 0xade3, 0x0000 }, + { 0xade4, 0x0000 }, + { 0xade5, 0x0000 }, + { 0xade6, 0x0000 }, + { 0xade7, 0x0000 }, + { 0xade8, 0x0000 }, + { 0xade9, 0x0000 }, + { 0xadea, 0x0000 }, + { 0xadeb, 0x0000 }, + { 0xadec, 0x0000 }, + { 0xaded, 0x0000 }, + { 0xadee, 0x0000 }, + { 0xadef, 0x0000 }, + { 0xadf0, 0x0000 }, + { 0xadf1, 0x0000 }, + { 0xadf2, 0x0000 }, + { 0xadf3, 0x0000 }, + { 0xadf4, 0x0000 }, + { 0xadf5, 0x0000 }, + { 0xadf6, 0x0000 }, + { 0xadf7, 0x0000 }, + { 0xadf8, 0x0000 }, + { 0xadf9, 0x0000 }, + { 0xadfa, 0x0000 }, + { 0xadfb, 0x0000 }, + { 0xadfc, 0x0000 }, + { 0xadfd, 0x0000 }, + { 0xadfe, 0x0000 }, + { 0xadff, 0x0000 }, + { 0xae00, 0x0000 }, + { 0xae01, 0x0000 }, + { 0xae02, 0x0000 }, + { 0xae03, 0x0000 }, + { 0xae04, 0x0000 }, + { 0xae05, 0x0000 }, + { 0xae06, 0x0000 }, + { 0xae07, 0x0000 }, + { 0xae08, 0x0000 }, + { 0xae09, 0x0000 }, + { 0xae0a, 0x0000 }, + { 0xae0b, 0x0000 }, + { 0xae0c, 0x0000 }, + { 0xae0d, 0x0000 }, + { 0xae0e, 0x0000 }, + { 0xae0f, 0x0000 }, + { 0xae10, 0x0000 }, + { 0xae11, 0x0000 }, + { 0xae12, 0x0000 }, + { 0xae13, 0x0000 }, + { 0xae14, 0x0000 }, + { 0xae15, 0x0000 }, + { 0xae16, 0x0000 }, + { 0xae17, 0x0000 }, + { 0xae18, 0x0000 }, + { 0xae19, 0x0000 }, + { 0xae1a, 0x0000 }, + { 0xae1b, 0x0000 }, + { 0xae1c, 0x0000 }, + { 0xae1d, 0x0000 }, + { 0xae1e, 0x0000 }, + { 0xae1f, 0x0000 }, + { 0xae20, 0x0000 }, + { 0xae21, 0x0000 }, + { 0xae22, 0x0000 }, + { 0xae23, 0x0000 }, + { 0xae24, 0x0000 }, + { 0xae25, 0x0000 }, + { 0xae26, 0x0000 }, + { 0xae27, 0x0000 }, + { 0xae28, 0x0000 }, + { 0xae29, 0x0000 }, + { 0xae2a, 0x0000 }, + { 0xae2b, 0x0000 }, + { 0xae2c, 0x0000 }, + { 0xae2d, 0x0000 }, + { 0xae2e, 0x0000 }, + { 0xae2f, 0x0000 }, + { 0xae30, 0x0000 }, + { 0xae31, 0x0000 }, + { 0xae32, 0x0000 }, + { 0xae33, 0x0000 }, + { 0xae34, 0x0000 }, + { 0xae35, 0x0000 }, + { 0xae36, 0x0000 }, + { 0xae37, 0x0000 }, + { 0xae38, 0x0000 }, + { 0xae39, 0x0000 }, + { 0xae3a, 0x0000 }, + { 0xae3b, 0x0000 }, + { 0xae3c, 0x0000 }, + { 0xae3d, 0x0000 }, + { 0xae3e, 0x0000 }, + { 0xae3f, 0x0000 }, + { 0xae40, 0x0000 }, + { 0xae41, 0x0000 }, + { 0xae42, 0x0000 }, + { 0xae43, 0x0000 }, + { 0xae44, 0x0000 }, + { 0xae45, 0x0000 }, + { 0xae46, 0x0000 }, + { 0xae47, 0x0000 }, + { 0xae48, 0x0000 }, + { 0xae49, 0x0000 }, + { 0xae4a, 0x0000 }, + { 0xae4b, 0x0000 }, + { 0xae4c, 0x0000 }, + { 0xae4d, 0x0000 }, + { 0xae4e, 0x0000 }, + { 0xae4f, 0x0000 }, + { 0xae50, 0x0000 }, + { 0xae51, 0x0000 }, + { 0xae52, 0x0000 }, + { 0xae53, 0x0000 }, + { 0xae54, 0x0000 }, + { 0xae55, 0x0000 }, + { 0xae56, 0x0000 }, + { 0xae57, 0x0000 }, + { 0xae58, 0x0000 }, + { 0xae59, 0x0000 }, + { 0xae5a, 0x0000 }, + { 0xae5b, 0x0000 }, + { 0xae5c, 0x0000 }, + { 0xae5d, 0x0000 }, + { 0xae5e, 0x0000 }, + { 0xae5f, 0x0000 }, + { 0xae60, 0x0000 }, + { 0xae61, 0x0000 }, + { 0xae62, 0x0000 }, + { 0xae63, 0x0000 }, + { 0xae64, 0x0000 }, + { 0xae65, 0x0000 }, + { 0xae66, 0x0000 }, + { 0xae67, 0x0000 }, + { 0xae68, 0x0000 }, + { 0xae69, 0x0000 }, + { 0xae6a, 0x0000 }, + { 0xae6b, 0x0000 }, + { 0xae6c, 0x0000 }, + { 0xae6d, 0x0000 }, + { 0xae6e, 0x0000 }, + { 0xae6f, 0x0000 }, + { 0xae70, 0x0000 }, + { 0xae71, 0x0000 }, + { 0xae72, 0x0000 }, + { 0xae73, 0x0000 }, + { 0xae74, 0x0000 }, + { 0xae75, 0x0000 }, + { 0xae76, 0x0000 }, + { 0xae77, 0x0000 }, + { 0xae78, 0x0000 }, + { 0xae79, 0x0000 }, + { 0xae7a, 0x0000 }, + { 0xae7b, 0x0000 }, + { 0xae7c, 0x0000 }, + { 0xae7d, 0x0000 }, + { 0xae7e, 0x0000 }, + { 0xae7f, 0x0000 }, + { 0xae80, 0x0000 }, + { 0xae81, 0x0000 }, + { 0xae82, 0x0000 }, + { 0xae83, 0x0000 }, + { 0xae84, 0x0000 }, + { 0xae85, 0x0000 }, + { 0xae86, 0x0000 }, + { 0xae87, 0x0000 }, + { 0xae88, 0x0000 }, + { 0xae89, 0x0000 }, + { 0xae8a, 0x0000 }, + { 0xae8b, 0x0000 }, + { 0xae8c, 0x0000 }, + { 0xae8d, 0x0000 }, + { 0xae8e, 0x0000 }, + { 0xae8f, 0x0000 }, + { 0xae90, 0x0000 }, + { 0xae91, 0x0000 }, + { 0xae92, 0x0000 }, + { 0xae93, 0x0000 }, + { 0xae94, 0x0000 }, + { 0xae95, 0x0000 }, + { 0xae96, 0x0000 }, + { 0xae97, 0x0000 }, + { 0xae98, 0x0000 }, + { 0xae99, 0x0000 }, + { 0xae9a, 0x0000 }, + { 0xae9b, 0x0000 }, + { 0xae9c, 0x0000 }, + { 0xae9d, 0x0000 }, + { 0xae9e, 0x0000 }, + { 0xae9f, 0x0000 }, + { 0xaea0, 0x0000 }, + { 0xaea1, 0x0000 }, + { 0xaea2, 0x0000 }, + { 0xaea3, 0x0000 }, + { 0xaea4, 0x0000 }, + { 0xaea5, 0x0000 }, + { 0xaea6, 0x0000 }, + { 0xaea7, 0x0000 }, + { 0xaea8, 0x0000 }, + { 0xaea9, 0x0000 }, + { 0xaeaa, 0x0000 }, + { 0xaeab, 0x0000 }, + { 0xaeac, 0x0000 }, + { 0xaead, 0x0000 }, + { 0xaeae, 0x0000 }, + { 0xaeaf, 0x0000 }, + { 0xaeb0, 0x0000 }, + { 0xaeb1, 0x0000 }, + { 0xaeb2, 0x0000 }, + { 0xaeb3, 0x0000 }, + { 0xaeb4, 0x0000 }, + { 0xaeb5, 0x0000 }, + { 0xaeb6, 0x0000 }, + { 0xaeb7, 0x0000 }, + { 0xaeb8, 0x0000 }, + { 0xaeb9, 0x0000 }, + { 0xaeba, 0x0000 }, + { 0xaebb, 0x0000 }, + { 0xaebc, 0x0000 }, + { 0xaebd, 0x0000 }, + { 0xaebe, 0x0000 }, + { 0xaebf, 0x0000 }, + { 0xaec0, 0x0000 }, + { 0xaec1, 0x0000 }, + { 0xaec2, 0x0000 }, + { 0xaec3, 0x0000 }, + { 0xaec4, 0x0000 }, + { 0xaec5, 0x0000 }, + { 0xaec6, 0x0000 }, + { 0xaec7, 0x0000 }, + { 0xaec8, 0x0000 }, + { 0xaec9, 0x0000 }, + { 0xaeca, 0x0000 }, + { 0xaecb, 0x0000 }, + { 0xaecc, 0x0000 }, + { 0xaecd, 0x0000 }, + { 0xaece, 0x0000 }, + { 0xaecf, 0x0000 }, + { 0xaed0, 0x0000 }, + { 0xaed1, 0x0000 }, + { 0xaed2, 0x0000 }, + { 0xaed3, 0x0000 }, + { 0xaed4, 0x0000 }, + { 0xaed5, 0x0000 }, + { 0xaed6, 0x0000 }, + { 0xaed7, 0x0000 }, + { 0xaed8, 0x0000 }, + { 0xaed9, 0x0000 }, + { 0xaeda, 0x0000 }, + { 0xaedb, 0x0000 }, + { 0xaedc, 0x0000 }, + { 0xaedd, 0x0000 }, + { 0xaede, 0x0000 }, + { 0xaedf, 0x0000 }, + { 0xaee0, 0x0000 }, + { 0xaee1, 0x0000 }, + { 0xaee2, 0x0000 }, + { 0xaee3, 0x0000 }, + { 0xaee4, 0x0000 }, + { 0xaee5, 0x0000 }, + { 0xaee6, 0x0000 }, + { 0xaee7, 0x0000 }, + { 0xaee8, 0x0000 }, + { 0xaee9, 0x0000 }, + { 0xaeea, 0x0000 }, + { 0xaeeb, 0x0000 }, + { 0xaeec, 0x0000 }, + { 0xaeed, 0x0000 }, + { 0xaeee, 0x0000 }, + { 0xaeef, 0x0000 }, + { 0xaef0, 0x0000 }, + { 0xaef1, 0x0000 }, + { 0xaef2, 0x0000 }, + { 0xaef3, 0x0000 }, + { 0xaef4, 0x0000 }, + { 0xaef5, 0x0000 }, + { 0xaef6, 0x0000 }, + { 0xaef7, 0x0000 }, + { 0xaef8, 0x0000 }, + { 0xaef9, 0x0000 }, + { 0xaefa, 0x0000 }, + { 0xaefb, 0x0000 }, + { 0xaefc, 0x0000 }, + { 0xaefd, 0x0000 }, + { 0xaefe, 0x0000 }, + { 0xaeff, 0x0000 }, + { 0xaf00, 0x0000 }, + { 0xaf01, 0x0000 }, + { 0xaf02, 0x0000 }, + { 0xaf03, 0x0000 }, + { 0xaf04, 0x0000 }, + { 0xaf05, 0x0000 }, + { 0xaf06, 0x0000 }, + { 0xaf07, 0x0000 }, + { 0xaf08, 0x0000 }, + { 0xaf09, 0x0000 }, + { 0xaf0a, 0x0000 }, + { 0xaf0b, 0x0000 }, + { 0xaf0c, 0x0000 }, + { 0xaf0d, 0x0000 }, + { 0xaf0e, 0x0000 }, + { 0xaf0f, 0x0000 }, + { 0xaf10, 0x0000 }, + { 0xaf11, 0x0000 }, + { 0xaf12, 0x0000 }, + { 0xaf13, 0x0000 }, + { 0xaf14, 0x0000 }, + { 0xaf15, 0x0000 }, + { 0xaf16, 0x0000 }, + { 0xaf17, 0x0000 }, + { 0xaf18, 0x0000 }, + { 0xaf19, 0x0000 }, + { 0xaf1a, 0x0000 }, + { 0xaf1b, 0x0000 }, + { 0xaf1c, 0x0000 }, + { 0xaf1d, 0x0000 }, + { 0xaf1e, 0x0000 }, + { 0xaf1f, 0x0000 }, + { 0xaf20, 0x0000 }, + { 0xaf21, 0x0000 }, + { 0xaf22, 0x0000 }, + { 0xaf23, 0x0000 }, + { 0xaf24, 0x0000 }, + { 0xaf25, 0x0000 }, + { 0xaf26, 0x0000 }, + { 0xaf27, 0x0000 }, + { 0xaf28, 0x0000 }, + { 0xaf29, 0x0000 }, + { 0xaf2a, 0x0000 }, + { 0xaf2b, 0x0000 }, + { 0xaf2c, 0x0000 }, + { 0xaf2d, 0x0000 }, + { 0xaf2e, 0x0000 }, + { 0xaf2f, 0x0000 }, + { 0xaf30, 0x0000 }, + { 0xaf31, 0x0000 }, + { 0xaf32, 0x0000 }, + { 0xaf33, 0x0000 }, + { 0xaf34, 0x0000 }, + { 0xaf35, 0x0000 }, + { 0xaf36, 0x0000 }, + { 0xaf37, 0x0000 }, + { 0xaf38, 0x0000 }, + { 0xaf39, 0x0000 }, + { 0xaf3a, 0x0000 }, + { 0xaf3b, 0x0000 }, + { 0xaf3c, 0x0000 }, + { 0xaf3d, 0x0000 }, + { 0xaf3e, 0x0000 }, + { 0xaf3f, 0x0000 }, + { 0xaf40, 0x0000 }, + { 0xaf41, 0x0000 }, + { 0xaf42, 0x0000 }, + { 0xaf43, 0x0000 }, + { 0xaf44, 0x0000 }, + { 0xaf45, 0x0000 }, + { 0xaf46, 0x0000 }, + { 0xaf47, 0x0000 }, + { 0xaf48, 0x0000 }, + { 0xaf49, 0x0000 }, + { 0xaf4a, 0x0000 }, + { 0xaf4b, 0x0000 }, + { 0xaf4c, 0x0000 }, + { 0xaf4d, 0x0000 }, + { 0xaf4e, 0x0000 }, + { 0xaf4f, 0x0000 }, + { 0xaf50, 0x0000 }, + { 0xaf51, 0x0000 }, + { 0xaf52, 0x0000 }, + { 0xaf53, 0x0000 }, + { 0xaf54, 0x0000 }, + { 0xaf55, 0x0000 }, + { 0xaf56, 0x0000 }, + { 0xaf57, 0x0000 }, + { 0xaf58, 0x0000 }, + { 0xaf59, 0x0000 }, + { 0xaf5a, 0x0000 }, + { 0xaf5b, 0x0000 }, + { 0xaf5c, 0x0000 }, + { 0xaf5d, 0x0000 }, + { 0xaf5e, 0x0000 }, + { 0xaf5f, 0x0000 }, + { 0xaf60, 0x0000 }, + { 0xaf61, 0x0000 }, + { 0xaf62, 0x0000 }, + { 0xaf63, 0x0000 }, + { 0xaf64, 0x0000 }, + { 0xaf65, 0x0000 }, + { 0xaf66, 0x0000 }, + { 0xaf67, 0x0000 }, + { 0xaf68, 0x0000 }, + { 0xaf69, 0x0000 }, + { 0xaf6a, 0x0000 }, + { 0xaf6b, 0x0000 }, + { 0xaf6c, 0x0000 }, + { 0xaf6d, 0x0000 }, + { 0xaf6e, 0x0000 }, + { 0xaf6f, 0x0000 }, + { 0xaf70, 0x0000 }, + { 0xaf71, 0x0000 }, + { 0xaf72, 0x0000 }, + { 0xaf73, 0x0000 }, + { 0xaf74, 0x0000 }, + { 0xaf75, 0x0000 }, + { 0xaf76, 0x0000 }, + { 0xaf77, 0x0000 }, + { 0xaf78, 0x0000 }, + { 0xaf79, 0x0000 }, + { 0xaf7a, 0x0000 }, + { 0xaf7b, 0x0000 }, + { 0xaf7c, 0x0000 }, + { 0xaf7d, 0x0000 }, + { 0xaf7e, 0x0000 }, + { 0xaf7f, 0x0000 }, + { 0xaf80, 0x0000 }, + { 0xaf81, 0x0000 }, + { 0xaf82, 0x0000 }, + { 0xaf83, 0x0000 }, + { 0xaf84, 0x0000 }, + { 0xaf85, 0x0000 }, + { 0xaf86, 0x0000 }, + { 0xaf87, 0x0000 }, + { 0xaf88, 0x0000 }, + { 0xaf89, 0x0000 }, + { 0xaf8a, 0x0000 }, + { 0xaf8b, 0x0000 }, + { 0xaf8c, 0x0000 }, + { 0xaf8d, 0x0000 }, + { 0xaf8e, 0x0000 }, + { 0xaf8f, 0x0000 }, + { 0xaf90, 0x0000 }, + { 0xaf91, 0x0000 }, + { 0xaf92, 0x0000 }, + { 0xaf93, 0x0000 }, + { 0xaf94, 0x0000 }, + { 0xaf95, 0x0000 }, + { 0xaf96, 0x0000 }, + { 0xaf97, 0x0000 }, + { 0xaf98, 0x0000 }, + { 0xaf99, 0x0000 }, + { 0xaf9a, 0x0000 }, + { 0xaf9b, 0x0000 }, + { 0xaf9c, 0x0000 }, + { 0xaf9d, 0x0000 }, + { 0xaf9e, 0x0000 }, + { 0xaf9f, 0x0000 }, + { 0xafa0, 0x0000 }, + { 0xafa1, 0x0000 }, + { 0xafa2, 0x0000 }, + { 0xafa3, 0x0000 }, + { 0xafa4, 0x0000 }, + { 0xafa5, 0x0000 }, + { 0xafa6, 0x0000 }, + { 0xafa7, 0x0000 }, + { 0xafa8, 0x0000 }, + { 0xafa9, 0x0000 }, + { 0xafaa, 0x0000 }, + { 0xafab, 0x0000 }, + { 0xafac, 0x0000 }, + { 0xafad, 0x0000 }, + { 0xafae, 0x0000 }, + { 0xafaf, 0x0000 }, + { 0xafb0, 0x0000 }, + { 0xafb1, 0x0000 }, + { 0xafb2, 0x0000 }, + { 0xafb3, 0x0000 }, + { 0xafb4, 0x0000 }, + { 0xafb5, 0x0000 }, + { 0xafb6, 0x0000 }, + { 0xafb7, 0x0000 }, + { 0xafb8, 0x0000 }, + { 0xafb9, 0x0000 }, + { 0xafba, 0x0000 }, + { 0xafbb, 0x0000 }, + { 0xafbc, 0x0000 }, + { 0xafbd, 0x0000 }, + { 0xafbe, 0x0000 }, + { 0xafbf, 0x0000 }, + { 0xafc0, 0x0000 }, + { 0xafc1, 0x0000 }, + { 0xafc2, 0x0000 }, + { 0xafc3, 0x0000 }, + { 0xafc4, 0x0000 }, + { 0xafc5, 0x0000 }, + { 0xafc6, 0x0000 }, + { 0xafc7, 0x0000 }, + { 0xafc8, 0x0000 }, + { 0xafc9, 0x0000 }, + { 0xafca, 0x0000 }, + { 0xafcb, 0x0000 }, + { 0xafcc, 0x0000 }, + { 0xafcd, 0x0000 }, + { 0xafce, 0x0000 }, + { 0xafcf, 0x0000 }, + { 0xafd0, 0x0000 }, + { 0xafd1, 0x0000 }, + { 0xafd2, 0x0000 }, + { 0xafd3, 0x0000 }, + { 0xafd4, 0x0000 }, + { 0xafd5, 0x0000 }, + { 0xafd6, 0x0000 }, + { 0xafd7, 0x0000 }, + { 0xafd8, 0x0000 }, + { 0xafd9, 0x0000 }, + { 0xafda, 0x0000 }, + { 0xafdb, 0x0000 }, + { 0xafdc, 0x0000 }, + { 0xafdd, 0x0000 }, + { 0xafde, 0x0000 }, + { 0xafdf, 0x0000 }, + { 0xafe0, 0x0000 }, + { 0xafe1, 0x0000 }, + { 0xafe2, 0x0000 }, + { 0xafe3, 0x0000 }, + { 0xafe4, 0x0000 }, + { 0xafe5, 0x0000 }, + { 0xafe6, 0x0000 }, + { 0xafe7, 0x0000 }, + { 0xafe8, 0x0000 }, + { 0xafe9, 0x0000 }, + { 0xafea, 0x0000 }, + { 0xafeb, 0x0000 }, + { 0xafec, 0x0000 }, + { 0xafed, 0x0000 }, + { 0xafee, 0x0000 }, + { 0xafef, 0x0000 }, + { 0xaff0, 0x0000 }, + { 0xaff1, 0x0000 }, + { 0xaff2, 0x0000 }, + { 0xaff3, 0x0000 }, + { 0xaff4, 0x0000 }, + { 0xaff5, 0x0000 }, + { 0xaff6, 0x0000 }, + { 0xaff7, 0x0000 }, + { 0xaff8, 0x0000 }, + { 0xaff9, 0x0000 }, + { 0xaffa, 0x0000 }, + { 0xaffb, 0x0000 }, + { 0xaffc, 0x0000 }, + { 0xaffd, 0x0000 }, + { 0xaffe, 0x0000 }, + { 0xafff, 0x0000 }, + { 0xb000, 0x0000 }, + { 0xb001, 0x0000 }, + { 0xb002, 0x0000 }, + { 0xb003, 0x0000 }, + { 0xb004, 0x0000 }, + { 0xb005, 0x0000 }, + { 0xb006, 0x0000 }, + { 0xb007, 0x0000 }, + { 0xb008, 0x0000 }, + { 0xb009, 0x0000 }, + { 0xb00a, 0x0000 }, + { 0xb00b, 0x0000 }, + { 0xb00c, 0x0000 }, + { 0xb00d, 0x0000 }, + { 0xb00e, 0x0000 }, + { 0xb00f, 0x0000 }, + { 0xb010, 0x0000 }, + { 0xb011, 0x0000 }, + { 0xb012, 0x0000 }, + { 0xb013, 0x0000 }, + { 0xb014, 0x0000 }, + { 0xb015, 0x0000 }, + { 0xb016, 0x0000 }, + { 0xb017, 0x0000 }, + { 0xb018, 0x0000 }, + { 0xb019, 0x0000 }, + { 0xb01a, 0x0000 }, + { 0xb01b, 0x0000 }, + { 0xb01c, 0x0000 }, + { 0xb01d, 0x0000 }, + { 0xb01e, 0x0000 }, + { 0xb01f, 0x0000 }, + { 0xb020, 0x0000 }, + { 0xb021, 0x0000 }, + { 0xb022, 0x0000 }, + { 0xb023, 0x0000 }, + { 0xb024, 0x0000 }, + { 0xb025, 0x0000 }, + { 0xb026, 0x0000 }, + { 0xb027, 0x0000 }, + { 0xb028, 0x0000 }, + { 0xb029, 0x0000 }, + { 0xb02a, 0x0000 }, + { 0xb02b, 0x0000 }, + { 0xb02c, 0x0000 }, + { 0xb02d, 0x0000 }, + { 0xb02e, 0x0000 }, + { 0xb02f, 0x0000 }, + { 0xb030, 0x0000 }, + { 0xb031, 0x0000 }, + { 0xb032, 0x0000 }, + { 0xb033, 0x0000 }, + { 0xb034, 0x0000 }, + { 0xb035, 0x0000 }, + { 0xb036, 0x0000 }, + { 0xb037, 0x0000 }, + { 0xb038, 0x0000 }, + { 0xb039, 0x0000 }, + { 0xb03a, 0x0000 }, + { 0xb03b, 0x0000 }, + { 0xb03c, 0x0000 }, + { 0xb03d, 0x0000 }, + { 0xb03e, 0x0000 }, + { 0xb03f, 0x0000 }, + { 0xb040, 0x0000 }, + { 0xb041, 0x0000 }, + { 0xb042, 0x0000 }, + { 0xb043, 0x0000 }, + { 0xb044, 0x0000 }, + { 0xb045, 0x0000 }, + { 0xb046, 0x0000 }, + { 0xb047, 0x0000 }, + { 0xb048, 0x0000 }, + { 0xb049, 0x0000 }, + { 0xb04a, 0x0000 }, + { 0xb04b, 0x0000 }, + { 0xb04c, 0x0000 }, + { 0xb04d, 0x0000 }, + { 0xb04e, 0x0000 }, + { 0xb04f, 0x0000 }, + { 0xb050, 0x0000 }, + { 0xb051, 0x0000 }, + { 0xb052, 0x0000 }, + { 0xb053, 0x0000 }, + { 0xb054, 0x0000 }, + { 0xb055, 0x0000 }, + { 0xb056, 0x0000 }, + { 0xb057, 0x0000 }, + { 0xb058, 0x0000 }, + { 0xb059, 0x0000 }, + { 0xb05a, 0x0000 }, + { 0xb05b, 0x0000 }, + { 0xb05c, 0x0000 }, + { 0xb05d, 0x0000 }, + { 0xb05e, 0x0000 }, + { 0xb05f, 0x0000 }, + { 0xb060, 0x0000 }, + { 0xb061, 0x0000 }, + { 0xb062, 0x0000 }, + { 0xb063, 0x0000 }, + { 0xb064, 0x0000 }, + { 0xb065, 0x0000 }, + { 0xb066, 0x0000 }, + { 0xb067, 0x0000 }, + { 0xb068, 0x0000 }, + { 0xb069, 0x0000 }, + { 0xb06a, 0x0000 }, + { 0xb06b, 0x0000 }, + { 0xb06c, 0x0000 }, + { 0xb06d, 0x0000 }, + { 0xb06e, 0x0000 }, + { 0xb06f, 0x0000 }, + { 0xb070, 0x0000 }, + { 0xb071, 0x0000 }, + { 0xb072, 0x0000 }, + { 0xb073, 0x0000 }, + { 0xb074, 0x0000 }, + { 0xb075, 0x0000 }, + { 0xb076, 0x0000 }, + { 0xb077, 0x0000 }, + { 0xb078, 0x0000 }, + { 0xb079, 0x0000 }, + { 0xb07a, 0x0000 }, + { 0xb07b, 0x0000 }, + { 0xb07c, 0x0000 }, + { 0xb07d, 0x0000 }, + { 0xb07e, 0x0000 }, + { 0xb07f, 0x0000 }, + { 0xb080, 0x0000 }, + { 0xb081, 0x0000 }, + { 0xb082, 0x0000 }, + { 0xb083, 0x0000 }, + { 0xb084, 0x0000 }, + { 0xb085, 0x0000 }, + { 0xb086, 0x0000 }, + { 0xb087, 0x0000 }, + { 0xb088, 0x0000 }, + { 0xb089, 0x0000 }, + { 0xb08a, 0x0000 }, + { 0xb08b, 0x0000 }, + { 0xb08c, 0x0000 }, + { 0xb08d, 0x0000 }, + { 0xb08e, 0x0000 }, + { 0xb08f, 0x0000 }, + { 0xb090, 0x0000 }, + { 0xb091, 0x0000 }, + { 0xb092, 0x0000 }, + { 0xb093, 0x0000 }, + { 0xb094, 0x0000 }, + { 0xb095, 0x0000 }, + { 0xb096, 0x0000 }, + { 0xb097, 0x0000 }, + { 0xb098, 0x0000 }, + { 0xb099, 0x0000 }, + { 0xb09a, 0x0000 }, + { 0xb09b, 0x0000 }, + { 0xb09c, 0x0000 }, + { 0xb09d, 0x0000 }, + { 0xb09e, 0x0000 }, + { 0xb09f, 0x0000 }, + { 0xb0a0, 0x0000 }, + { 0xb0a1, 0x0000 }, + { 0xb0a2, 0x0000 }, + { 0xb0a3, 0x0000 }, + { 0xb0a4, 0x0000 }, + { 0xb0a5, 0x0000 }, + { 0xb0a6, 0x0000 }, + { 0xb0a7, 0x0000 }, + { 0xb0a8, 0x0000 }, + { 0xb0a9, 0x0000 }, + { 0xb0aa, 0x0000 }, + { 0xb0ab, 0x0000 }, + { 0xb0ac, 0x0000 }, + { 0xb0ad, 0x0000 }, + { 0xb0ae, 0x0000 }, + { 0xb0af, 0x0000 }, + { 0xb0b0, 0x0000 }, + { 0xb0b1, 0x0000 }, + { 0xb0b2, 0x0000 }, + { 0xb0b3, 0x0000 }, + { 0xb0b4, 0x0000 }, + { 0xb0b5, 0x0000 }, + { 0xb0b6, 0x0000 }, + { 0xb0b7, 0x0000 }, + { 0xb0b8, 0x0000 }, + { 0xb0b9, 0x0000 }, + { 0xb0ba, 0x0000 }, + { 0xb0bb, 0x0000 }, + { 0xb0bc, 0x0000 }, + { 0xb0bd, 0x0000 }, + { 0xb0be, 0x0000 }, + { 0xb0bf, 0x0000 }, + { 0xb0c0, 0x0000 }, + { 0xb0c1, 0x0000 }, + { 0xb0c2, 0x0000 }, + { 0xb0c3, 0x0000 }, + { 0xb0c4, 0x0000 }, + { 0xb0c5, 0x0000 }, + { 0xb0c6, 0x0000 }, + { 0xb0c7, 0x0000 }, + { 0xb0c8, 0x0000 }, + { 0xb0c9, 0x0000 }, + { 0xb0ca, 0x0000 }, + { 0xb0cb, 0x0000 }, + { 0xb0cc, 0x0000 }, + { 0xb0cd, 0x0000 }, + { 0xb0ce, 0x0000 }, + { 0xb0cf, 0x0000 }, + { 0xb0d0, 0x0000 }, + { 0xb0d1, 0x0000 }, + { 0xb0d2, 0x0000 }, + { 0xb0d3, 0x0000 }, + { 0xb0d4, 0x0000 }, + { 0xb0d5, 0x0000 }, + { 0xb0d6, 0x0000 }, + { 0xb0d7, 0x0000 }, + { 0xb0d8, 0x0000 }, + { 0xb0d9, 0x0000 }, + { 0xb0da, 0x0000 }, + { 0xb0db, 0x0000 }, + { 0xb0dc, 0x0000 }, + { 0xb0dd, 0x0000 }, + { 0xb0de, 0x0000 }, + { 0xb0df, 0x0000 }, + { 0xb0e0, 0x0000 }, + { 0xb0e1, 0x0000 }, + { 0xb0e2, 0x0000 }, + { 0xb0e3, 0x0000 }, + { 0xb0e4, 0x0000 }, + { 0xb0e5, 0x0000 }, + { 0xb0e6, 0x0000 }, + { 0xb0e7, 0x0000 }, + { 0xb0e8, 0x0000 }, + { 0xb0e9, 0x0000 }, + { 0xb0ea, 0x0000 }, + { 0xb0eb, 0x0000 }, + { 0xb0ec, 0x0000 }, + { 0xb0ed, 0x0000 }, + { 0xb0ee, 0x0000 }, + { 0xb0ef, 0x0000 }, + { 0xb0f0, 0x0000 }, + { 0xb0f1, 0x0000 }, + { 0xb0f2, 0x0000 }, + { 0xb0f3, 0x0000 }, + { 0xb0f4, 0x0000 }, + { 0xb0f5, 0x0000 }, + { 0xb0f6, 0x0000 }, + { 0xb0f7, 0x0000 }, + { 0xb0f8, 0x0000 }, + { 0xb0f9, 0x0000 }, + { 0xb0fa, 0x0000 }, + { 0xb0fb, 0x0000 }, + { 0xb0fc, 0x0000 }, + { 0xb0fd, 0x0000 }, + { 0xb0fe, 0x0000 }, + { 0xb0ff, 0x0000 }, + { 0xb100, 0x0000 }, + { 0xb101, 0x0000 }, + { 0xb102, 0x0000 }, + { 0xb103, 0x0000 }, + { 0xb104, 0x0000 }, + { 0xb105, 0x0000 }, + { 0xb106, 0x0000 }, + { 0xb107, 0x0000 }, + { 0xb108, 0x0000 }, + { 0xb109, 0x0000 }, + { 0xb10a, 0x0000 }, + { 0xb10b, 0x0000 }, + { 0xb10c, 0x0000 }, + { 0xb10d, 0x0000 }, + { 0xb10e, 0x0000 }, + { 0xb10f, 0x0000 }, + { 0xb110, 0x0000 }, + { 0xb111, 0x0000 }, + { 0xb112, 0x0000 }, + { 0xb113, 0x0000 }, + { 0xb114, 0x0000 }, + { 0xb115, 0x0000 }, + { 0xb116, 0x0000 }, + { 0xb117, 0x0000 }, + { 0xb118, 0x0000 }, + { 0xb119, 0x0000 }, + { 0xb11a, 0x0000 }, + { 0xb11b, 0x0000 }, + { 0xb11c, 0x0000 }, + { 0xb11d, 0x0000 }, + { 0xb11e, 0x0000 }, + { 0xb11f, 0x0000 }, + { 0xb120, 0x0000 }, + { 0xb121, 0x0000 }, + { 0xb122, 0x0000 }, + { 0xb123, 0x0000 }, + { 0xb124, 0x0000 }, + { 0xb125, 0x0000 }, + { 0xb126, 0x0000 }, + { 0xb127, 0x0000 }, + { 0xb128, 0x0000 }, + { 0xb129, 0x0000 }, + { 0xb12a, 0x0000 }, + { 0xb12b, 0x0000 }, + { 0xb12c, 0x0000 }, + { 0xb12d, 0x0000 }, + { 0xb12e, 0x0000 }, + { 0xb12f, 0x0000 }, + { 0xb130, 0x0000 }, + { 0xb131, 0x0000 }, + { 0xb132, 0x0000 }, + { 0xb133, 0x0000 }, + { 0xb134, 0x0000 }, + { 0xb135, 0x0000 }, + { 0xb136, 0x0000 }, + { 0xb137, 0x0000 }, + { 0xb138, 0x0000 }, + { 0xb139, 0x0000 }, + { 0xb13a, 0x0000 }, + { 0xb13b, 0x0000 }, + { 0xb13c, 0x0000 }, + { 0xb13d, 0x0000 }, + { 0xb13e, 0x0000 }, + { 0xb13f, 0x0000 }, + { 0xb140, 0x0000 }, + { 0xb141, 0x0000 }, + { 0xb142, 0x0000 }, + { 0xb143, 0x0000 }, + { 0xb144, 0x0000 }, + { 0xb145, 0x0000 }, + { 0xb146, 0x0000 }, + { 0xb147, 0x0000 }, + { 0xb148, 0x0000 }, + { 0xb149, 0x0000 }, + { 0xb14a, 0x0000 }, + { 0xb14b, 0x0000 }, + { 0xb14c, 0x0000 }, + { 0xb14d, 0x0000 }, + { 0xb14e, 0x0000 }, + { 0xb14f, 0x0000 }, + { 0xb150, 0x0000 }, + { 0xb151, 0x0000 }, + { 0xb152, 0x0000 }, + { 0xb153, 0x0000 }, + { 0xb154, 0x0000 }, + { 0xb155, 0x0000 }, + { 0xb156, 0x0000 }, + { 0xb157, 0x0000 }, + { 0xb158, 0x0000 }, + { 0xb159, 0x0000 }, + { 0xb15a, 0x0000 }, + { 0xb15b, 0x0000 }, + { 0xb15c, 0x0000 }, + { 0xb15d, 0x0000 }, + { 0xb15e, 0x0000 }, + { 0xb15f, 0x0000 }, + { 0xb160, 0x0000 }, + { 0xb161, 0x0000 }, + { 0xb162, 0x0000 }, + { 0xb163, 0x0000 }, + { 0xb164, 0x0000 }, + { 0xb165, 0x0000 }, + { 0xb166, 0x0000 }, + { 0xb167, 0x0000 }, + { 0xb168, 0x0000 }, + { 0xb169, 0x0000 }, + { 0xb16a, 0x0000 }, + { 0xb16b, 0x0000 }, + { 0xb16c, 0x0000 }, + { 0xb16d, 0x0000 }, + { 0xb16e, 0x0000 }, + { 0xb16f, 0x0000 }, + { 0xb170, 0x0000 }, + { 0xb171, 0x0000 }, + { 0xb172, 0x0000 }, + { 0xb173, 0x0000 }, + { 0xb174, 0x0000 }, + { 0xb175, 0x0000 }, + { 0xb176, 0x0000 }, + { 0xb177, 0x0000 }, + { 0xb178, 0x0000 }, + { 0xb179, 0x0000 }, + { 0xb17a, 0x0000 }, + { 0xb17b, 0x0000 }, + { 0xb17c, 0x0000 }, + { 0xb17d, 0x0000 }, + { 0xb17e, 0x0000 }, + { 0xb17f, 0x0000 }, + { 0xb180, 0x0000 }, + { 0xb181, 0x0000 }, + { 0xb182, 0x0000 }, + { 0xb183, 0x0000 }, + { 0xb184, 0x0000 }, + { 0xb185, 0x0000 }, + { 0xb186, 0x0000 }, + { 0xb187, 0x0000 }, + { 0xb188, 0x0000 }, + { 0xb189, 0x0000 }, + { 0xb18a, 0x0000 }, + { 0xb18b, 0x0000 }, + { 0xb18c, 0x0000 }, + { 0xb18d, 0x0000 }, + { 0xb18e, 0x0000 }, + { 0xb18f, 0x0000 }, + { 0xb190, 0x0000 }, + { 0xb191, 0x0000 }, + { 0xb192, 0x0000 }, + { 0xb193, 0x0000 }, + { 0xb194, 0x0000 }, + { 0xb195, 0x0000 }, + { 0xb196, 0x0000 }, + { 0xb197, 0x0000 }, + { 0xb198, 0x0000 }, + { 0xb199, 0x0000 }, + { 0xb19a, 0x0000 }, + { 0xb19b, 0x0000 }, + { 0xb19c, 0x0000 }, + { 0xb19d, 0x0000 }, + { 0xb19e, 0x0000 }, + { 0xb19f, 0x0000 }, + { 0xb1a0, 0x0000 }, + { 0xb1a1, 0x0000 }, + { 0xb1a2, 0x0000 }, + { 0xb1a3, 0x0000 }, + { 0xb1a4, 0x0000 }, + { 0xb1a5, 0x0000 }, + { 0xb1a6, 0x0000 }, + { 0xb1a7, 0x0000 }, + { 0xb1a8, 0x0000 }, + { 0xb1a9, 0x0000 }, + { 0xb1aa, 0x0000 }, + { 0xb1ab, 0x0000 }, + { 0xb1ac, 0x0000 }, + { 0xb1ad, 0x0000 }, + { 0xb1ae, 0x0000 }, + { 0xb1af, 0x0000 }, + { 0xb1b0, 0x0000 }, + { 0xb1b1, 0x0000 }, + { 0xb1b2, 0x0000 }, + { 0xb1b3, 0x0000 }, + { 0xb1b4, 0x0000 }, + { 0xb1b5, 0x0000 }, + { 0xb1b6, 0x0000 }, + { 0xb1b7, 0x0000 }, + { 0xb1b8, 0x0000 }, + { 0xb1b9, 0x0000 }, + { 0xb1ba, 0x0000 }, + { 0xb1bb, 0x0000 }, + { 0xb1bc, 0x0000 }, + { 0xb1bd, 0x0000 }, + { 0xb1be, 0x0000 }, + { 0xb1bf, 0x0000 }, + { 0xb1c0, 0x0000 }, + { 0xb1c1, 0x0000 }, + { 0xb1c2, 0x0000 }, + { 0xb1c3, 0x0000 }, + { 0xb1c4, 0x0000 }, + { 0xb1c5, 0x0000 }, + { 0xb1c6, 0x0000 }, + { 0xb1c7, 0x0000 }, + { 0xb1c8, 0x0000 }, + { 0xb1c9, 0x0000 }, + { 0xb1ca, 0x0000 }, + { 0xb1cb, 0x0000 }, + { 0xb1cc, 0x0000 }, + { 0xb1cd, 0x0000 }, + { 0xb1ce, 0x0000 }, + { 0xb1cf, 0x0000 }, + { 0xb1d0, 0x0000 }, + { 0xb1d1, 0x0000 }, + { 0xb1d2, 0x0000 }, + { 0xb1d3, 0x0000 }, + { 0xb1d4, 0x0000 }, + { 0xb1d5, 0x0000 }, + { 0xb1d6, 0x0000 }, + { 0xb1d7, 0x0000 }, + { 0xb1d8, 0x0000 }, + { 0xb1d9, 0x0000 }, + { 0xb1da, 0x0000 }, + { 0xb1db, 0x0000 }, + { 0xb1dc, 0x0000 }, + { 0xb1dd, 0x0000 }, + { 0xb1de, 0x0000 }, + { 0xb1df, 0x0000 }, + { 0xb1e0, 0x0000 }, + { 0xb1e1, 0x0000 }, + { 0xb1e2, 0x0000 }, + { 0xb1e3, 0x0000 }, + { 0xb1e4, 0x0000 }, + { 0xb1e5, 0x0000 }, + { 0xb1e6, 0x0000 }, + { 0xb1e7, 0x0000 }, + { 0xb1e8, 0x0000 }, + { 0xb1e9, 0x0000 }, + { 0xb1ea, 0x0000 }, + { 0xb1eb, 0x0000 }, + { 0xb1ec, 0x0000 }, + { 0xb1ed, 0x0000 }, + { 0xb1ee, 0x0000 }, + { 0xb1ef, 0x0000 }, + { 0xb1f0, 0x0000 }, + { 0xb1f1, 0x0000 }, + { 0xb1f2, 0x0000 }, + { 0xb1f3, 0x0000 }, + { 0xb1f4, 0x0000 }, + { 0xb1f5, 0x0000 }, + { 0xb1f6, 0x0000 }, + { 0xb1f7, 0x0000 }, + { 0xb1f8, 0x0000 }, + { 0xb1f9, 0x0000 }, + { 0xb1fa, 0x0000 }, + { 0xb1fb, 0x0000 }, + { 0xb1fc, 0x0000 }, + { 0xb1fd, 0x0000 }, + { 0xb1fe, 0x0000 }, + { 0xb1ff, 0x0000 }, + { 0xb200, 0x0000 }, + { 0xb201, 0x0000 }, + { 0xb202, 0x0000 }, + { 0xb203, 0x0000 }, + { 0xb204, 0x0000 }, + { 0xb205, 0x0000 }, + { 0xb206, 0x0000 }, + { 0xb207, 0x0000 }, + { 0xb208, 0x0000 }, + { 0xb209, 0x0000 }, + { 0xb20a, 0x0000 }, + { 0xb20b, 0x0000 }, + { 0xb20c, 0x0000 }, + { 0xb20d, 0x0000 }, + { 0xb20e, 0x0000 }, + { 0xb20f, 0x0000 }, + { 0xb210, 0x0000 }, + { 0xb211, 0x0000 }, + { 0xb212, 0x0000 }, + { 0xb213, 0x0000 }, + { 0xb214, 0x0000 }, + { 0xb215, 0x0000 }, + { 0xb216, 0x0000 }, + { 0xb217, 0x0000 }, + { 0xb218, 0x0000 }, + { 0xb219, 0x0000 }, + { 0xb21a, 0x0000 }, + { 0xb21b, 0x0000 }, + { 0xb21c, 0x0000 }, + { 0xb21d, 0x0000 }, + { 0xb21e, 0x0000 }, + { 0xb21f, 0x0000 }, + { 0xb220, 0x0000 }, + { 0xb221, 0x0000 }, + { 0xb222, 0x0000 }, + { 0xb223, 0x0000 }, + { 0xb224, 0x0000 }, + { 0xb225, 0x0000 }, + { 0xb226, 0x0000 }, + { 0xb227, 0x0000 }, + { 0xb228, 0x0000 }, + { 0xb229, 0x0000 }, + { 0xb22a, 0x0000 }, + { 0xb22b, 0x0000 }, + { 0xb22c, 0x0000 }, + { 0xb22d, 0x0000 }, + { 0xb22e, 0x0000 }, + { 0xb22f, 0x0000 }, + { 0xb230, 0x0000 }, + { 0xb231, 0x0000 }, + { 0xb232, 0x0000 }, + { 0xb233, 0x0000 }, + { 0xb234, 0x0000 }, + { 0xb235, 0x0000 }, + { 0xb236, 0x0000 }, + { 0xb237, 0x0000 }, + { 0xb238, 0x0000 }, + { 0xb239, 0x0000 }, + { 0xb23a, 0x0000 }, + { 0xb23b, 0x0000 }, + { 0xb23c, 0x0000 }, + { 0xb23d, 0x0000 }, + { 0xb23e, 0x0000 }, + { 0xb23f, 0x0000 }, + { 0xb240, 0x0000 }, + { 0xb241, 0x0000 }, + { 0xb242, 0x0000 }, + { 0xb243, 0x0000 }, + { 0xb244, 0x0000 }, + { 0xb245, 0x0000 }, + { 0xb246, 0x0000 }, + { 0xb247, 0x0000 }, + { 0xb248, 0x0000 }, + { 0xb249, 0x0000 }, + { 0xb24a, 0x0000 }, + { 0xb24b, 0x0000 }, + { 0xb24c, 0x0000 }, + { 0xb24d, 0x0000 }, + { 0xb24e, 0x0000 }, + { 0xb24f, 0x0000 }, + { 0xb250, 0x0000 }, + { 0xb251, 0x0000 }, + { 0xb252, 0x0000 }, + { 0xb253, 0x0000 }, + { 0xb254, 0x0000 }, + { 0xb255, 0x0000 }, + { 0xb256, 0x0000 }, + { 0xb257, 0x0000 }, + { 0xb258, 0x0000 }, + { 0xb259, 0x0000 }, + { 0xb25a, 0x0000 }, + { 0xb25b, 0x0000 }, + { 0xb25c, 0x0000 }, + { 0xb25d, 0x0000 }, + { 0xb25e, 0x0000 }, + { 0xb25f, 0x0000 }, + { 0xb260, 0x0000 }, + { 0xb261, 0x0000 }, + { 0xb262, 0x0000 }, + { 0xb263, 0x0000 }, + { 0xb264, 0x0000 }, + { 0xb265, 0x0000 }, + { 0xb266, 0x0000 }, + { 0xb267, 0x0000 }, + { 0xb268, 0x0000 }, + { 0xb269, 0x0000 }, + { 0xb26a, 0x0000 }, + { 0xb26b, 0x0000 }, + { 0xb26c, 0x0000 }, + { 0xb26d, 0x0000 }, + { 0xb26e, 0x0000 }, + { 0xb26f, 0x0000 }, + { 0xb270, 0x0000 }, + { 0xb271, 0x0000 }, + { 0xb272, 0x0000 }, + { 0xb273, 0x0000 }, + { 0xb274, 0x0000 }, + { 0xb275, 0x0000 }, + { 0xb276, 0x0000 }, + { 0xb277, 0x0000 }, + { 0xb278, 0x0000 }, + { 0xb279, 0x0000 }, + { 0xb27a, 0x0000 }, + { 0xb27b, 0x0000 }, + { 0xb27c, 0x0000 }, + { 0xb27d, 0x0000 }, + { 0xb27e, 0x0000 }, + { 0xb27f, 0x0000 }, + { 0xb280, 0x0000 }, + { 0xb281, 0x0000 }, + { 0xb282, 0x0000 }, + { 0xb283, 0x0000 }, + { 0xb284, 0x0000 }, + { 0xb285, 0x0000 }, + { 0xb286, 0x0000 }, + { 0xb287, 0x0000 }, + { 0xb288, 0x0000 }, + { 0xb289, 0x0000 }, + { 0xb28a, 0x0000 }, + { 0xb28b, 0x0000 }, + { 0xb28c, 0x0000 }, + { 0xb28d, 0x0000 }, + { 0xb28e, 0x0000 }, + { 0xb28f, 0x0000 }, + { 0xb290, 0x0000 }, + { 0xb291, 0x0000 }, + { 0xb292, 0x0000 }, + { 0xb293, 0x0000 }, + { 0xb294, 0x0000 }, + { 0xb295, 0x0000 }, + { 0xb296, 0x0000 }, + { 0xb297, 0x0000 }, + { 0xb298, 0x0000 }, + { 0xb299, 0x0000 }, + { 0xb29a, 0x0000 }, + { 0xb29b, 0x0000 }, + { 0xb29c, 0x0000 }, + { 0xb29d, 0x0000 }, + { 0xb29e, 0x0000 }, + { 0xb29f, 0x0000 }, + { 0xb2a0, 0x0000 }, + { 0xb2a1, 0x0000 }, + { 0xb2a2, 0x0000 }, + { 0xb2a3, 0x0000 }, + { 0xb2a4, 0x0000 }, + { 0xb2a5, 0x0000 }, + { 0xb2a6, 0x0000 }, + { 0xb2a7, 0x0000 }, + { 0xb2a8, 0x0000 }, + { 0xb2a9, 0x0000 }, + { 0xb2aa, 0x0000 }, + { 0xb2ab, 0x0000 }, + { 0xb2ac, 0x0000 }, + { 0xb2ad, 0x0000 }, + { 0xb2ae, 0x0000 }, + { 0xb2af, 0x0000 }, + { 0xb2b0, 0x0000 }, + { 0xb2b1, 0x0000 }, + { 0xb2b2, 0x0000 }, + { 0xb2b3, 0x0000 }, + { 0xb2b4, 0x0000 }, + { 0xb2b5, 0x0000 }, + { 0xb2b6, 0x0000 }, + { 0xb2b7, 0x0000 }, + { 0xb2b8, 0x0000 }, + { 0xb2b9, 0x0000 }, + { 0xb2ba, 0x0000 }, + { 0xb2bb, 0x0000 }, + { 0xb2bc, 0x0000 }, + { 0xb2bd, 0x0000 }, + { 0xb2be, 0x0000 }, + { 0xb2bf, 0x0000 }, + { 0xb2c0, 0x0000 }, + { 0xb2c1, 0x0000 }, + { 0xb2c2, 0x0000 }, + { 0xb2c3, 0x0000 }, + { 0xb2c4, 0x0000 }, + { 0xb2c5, 0x0000 }, + { 0xb2c6, 0x0000 }, + { 0xb2c7, 0x0000 }, + { 0xb2c8, 0x0000 }, + { 0xb2c9, 0x0000 }, + { 0xb2ca, 0x0000 }, + { 0xb2cb, 0x0000 }, + { 0xb2cc, 0x0000 }, + { 0xb2cd, 0x0000 }, + { 0xb2ce, 0x0000 }, + { 0xb2cf, 0x0000 }, + { 0xb2d0, 0x0000 }, + { 0xb2d1, 0x0000 }, + { 0xb2d2, 0x0000 }, + { 0xb2d3, 0x0000 }, + { 0xb2d4, 0x0000 }, + { 0xb2d5, 0x0000 }, + { 0xb2d6, 0x0000 }, + { 0xb2d7, 0x0000 }, + { 0xb2d8, 0x0000 }, + { 0xb2d9, 0x0000 }, + { 0xb2da, 0x0000 }, + { 0xb2db, 0x0000 }, + { 0xb2dc, 0x0000 }, + { 0xb2dd, 0x0000 }, + { 0xb2de, 0x0000 }, + { 0xb2df, 0x0000 }, + { 0xb2e0, 0x0000 }, + { 0xb2e1, 0x0000 }, + { 0xb2e2, 0x0000 }, + { 0xb2e3, 0x0000 }, + { 0xb2e4, 0x0000 }, + { 0xb2e5, 0x0000 }, + { 0xb2e6, 0x0000 }, + { 0xb2e7, 0x0000 }, + { 0xb2e8, 0x0000 }, + { 0xb2e9, 0x0000 }, + { 0xb2ea, 0x0000 }, + { 0xb2eb, 0x0000 }, + { 0xb2ec, 0x0000 }, + { 0xb2ed, 0x0000 }, + { 0xb2ee, 0x0000 }, + { 0xb2ef, 0x0000 }, + { 0xb2f0, 0x0000 }, + { 0xb2f1, 0x0000 }, + { 0xb2f2, 0x0000 }, + { 0xb2f3, 0x0000 }, + { 0xb2f4, 0x0000 }, + { 0xb2f5, 0x0000 }, + { 0xb2f6, 0x0000 }, + { 0xb2f7, 0x0000 }, + { 0xb2f8, 0x0000 }, + { 0xb2f9, 0x0000 }, + { 0xb2fa, 0x0000 }, + { 0xb2fb, 0x0000 }, + { 0xb2fc, 0x0000 }, + { 0xb2fd, 0x0000 }, + { 0xb2fe, 0x0000 }, + { 0xb2ff, 0x0000 }, + { 0xb300, 0x0000 }, + { 0xb301, 0x0000 }, + { 0xb302, 0x0000 }, + { 0xb303, 0x0000 }, + { 0xb304, 0x0000 }, + { 0xb305, 0x0000 }, + { 0xb306, 0x0000 }, + { 0xb307, 0x0000 }, + { 0xb308, 0x0000 }, + { 0xb309, 0x0000 }, + { 0xb30a, 0x0000 }, + { 0xb30b, 0x0000 }, + { 0xb30c, 0x0000 }, + { 0xb30d, 0x0000 }, + { 0xb30e, 0x0000 }, + { 0xb30f, 0x0000 }, + { 0xb310, 0x0000 }, + { 0xb311, 0x0000 }, + { 0xb312, 0x0000 }, + { 0xb313, 0x0000 }, + { 0xb314, 0x0000 }, + { 0xb315, 0x0000 }, + { 0xb316, 0x0000 }, + { 0xb317, 0x0000 }, + { 0xb318, 0x0000 }, + { 0xb319, 0x0000 }, + { 0xb31a, 0x0000 }, + { 0xb31b, 0x0000 }, + { 0xb31c, 0x0000 }, + { 0xb31d, 0x0000 }, + { 0xb31e, 0x0000 }, + { 0xb31f, 0x0000 }, + { 0xb320, 0x0000 }, + { 0xb321, 0x0000 }, + { 0xb322, 0x0000 }, + { 0xb323, 0x0000 }, + { 0xb324, 0x0000 }, + { 0xb325, 0x0000 }, + { 0xb326, 0x0000 }, + { 0xb327, 0x0000 }, + { 0xb328, 0x0000 }, + { 0xb329, 0x0000 }, + { 0xb32a, 0x0000 }, + { 0xb32b, 0x0000 }, + { 0xb32c, 0x0000 }, + { 0xb32d, 0x0000 }, + { 0xb32e, 0x0000 }, + { 0xb32f, 0x0000 }, + { 0xb330, 0x0000 }, + { 0xb331, 0x0000 }, + { 0xb332, 0x0000 }, + { 0xb333, 0x0000 }, + { 0xb334, 0x0000 }, + { 0xb335, 0x0000 }, + { 0xb336, 0x0000 }, + { 0xb337, 0x0000 }, + { 0xb338, 0x0000 }, + { 0xb339, 0x0000 }, + { 0xb33a, 0x0000 }, + { 0xb33b, 0x0000 }, + { 0xb33c, 0x0000 }, + { 0xb33d, 0x0000 }, + { 0xb33e, 0x0000 }, + { 0xb33f, 0x0000 }, + { 0xb340, 0x0000 }, + { 0xb341, 0x0000 }, + { 0xb342, 0x0000 }, + { 0xb343, 0x0000 }, + { 0xb344, 0x0000 }, + { 0xb345, 0x0000 }, + { 0xb346, 0x0000 }, + { 0xb347, 0x0000 }, + { 0xb348, 0x0000 }, + { 0xb349, 0x0000 }, + { 0xb34a, 0x0000 }, + { 0xb34b, 0x0000 }, + { 0xb34c, 0x0000 }, + { 0xb34d, 0x0000 }, + { 0xb34e, 0x0000 }, + { 0xb34f, 0x0000 }, + { 0xb350, 0x0000 }, + { 0xb351, 0x0000 }, + { 0xb352, 0x0000 }, + { 0xb353, 0x0000 }, + { 0xb354, 0x0000 }, + { 0xb355, 0x0000 }, + { 0xb356, 0x0000 }, + { 0xb357, 0x0000 }, + { 0xb358, 0x0000 }, + { 0xb359, 0x0000 }, + { 0xb35a, 0x0000 }, + { 0xb35b, 0x0000 }, + { 0xb35c, 0x0000 }, + { 0xb35d, 0x0000 }, + { 0xb35e, 0x0000 }, + { 0xb35f, 0x0000 }, + { 0xb360, 0x0000 }, + { 0xb361, 0x0000 }, + { 0xb362, 0x0000 }, + { 0xb363, 0x0000 }, + { 0xb364, 0x0000 }, + { 0xb365, 0x0000 }, + { 0xb366, 0x0000 }, + { 0xb367, 0x0000 }, + { 0xb368, 0x0000 }, + { 0xb369, 0x0000 }, + { 0xb36a, 0x0000 }, + { 0xb36b, 0x0000 }, + { 0xb36c, 0x0000 }, + { 0xb36d, 0x0000 }, + { 0xb36e, 0x0000 }, + { 0xb36f, 0x0000 }, + { 0xb370, 0x0000 }, + { 0xb371, 0x0000 }, + { 0xb372, 0x0000 }, + { 0xb373, 0x0000 }, + { 0xb374, 0x0000 }, + { 0xb375, 0x0000 }, + { 0xb376, 0x0000 }, + { 0xb377, 0x0000 }, + { 0xb378, 0x0000 }, + { 0xb379, 0x0000 }, + { 0xb37a, 0x0000 }, + { 0xb37b, 0x0000 }, + { 0xb37c, 0x0000 }, + { 0xb37d, 0x0000 }, + { 0xb37e, 0x0000 }, + { 0xb37f, 0x0000 }, + { 0xb380, 0x0000 }, + { 0xb381, 0x0000 }, + { 0xb382, 0x0000 }, + { 0xb383, 0x0000 }, + { 0xb384, 0x0000 }, + { 0xb385, 0x0000 }, + { 0xb386, 0x0000 }, + { 0xb387, 0x0000 }, + { 0xb388, 0x0000 }, + { 0xb389, 0x0000 }, + { 0xb38a, 0x0000 }, + { 0xb38b, 0x0000 }, + { 0xb38c, 0x0000 }, + { 0xb38d, 0x0000 }, + { 0xb38e, 0x0000 }, + { 0xb38f, 0x0000 }, + { 0xb390, 0x0000 }, + { 0xb391, 0x0000 }, + { 0xb392, 0x0000 }, + { 0xb393, 0x0000 }, + { 0xb394, 0x0000 }, + { 0xb395, 0x0000 }, + { 0xb396, 0x0000 }, + { 0xb397, 0x0000 }, + { 0xb398, 0x0000 }, + { 0xb399, 0x0000 }, + { 0xb39a, 0x0000 }, + { 0xb39b, 0x0000 }, + { 0xb39c, 0x0000 }, + { 0xb39d, 0x0000 }, + { 0xb39e, 0x0000 }, + { 0xb39f, 0x0000 }, + { 0xb3a0, 0x0000 }, + { 0xb3a1, 0x0000 }, + { 0xb3a2, 0x0000 }, + { 0xb3a3, 0x0000 }, + { 0xb3a4, 0x0000 }, + { 0xb3a5, 0x0000 }, + { 0xb3a6, 0x0000 }, + { 0xb3a7, 0x0000 }, + { 0xb3a8, 0x0000 }, + { 0xb3a9, 0x0000 }, + { 0xb3aa, 0x0000 }, + { 0xb3ab, 0x0000 }, + { 0xb3ac, 0x0000 }, + { 0xb3ad, 0x0000 }, + { 0xb3ae, 0x0000 }, + { 0xb3af, 0x0000 }, + { 0xb3b0, 0x0000 }, + { 0xb3b1, 0x0000 }, + { 0xb3b2, 0x0000 }, + { 0xb3b3, 0x0000 }, + { 0xb3b4, 0x0000 }, + { 0xb3b5, 0x0000 }, + { 0xb3b6, 0x0000 }, + { 0xb3b7, 0x0000 }, + { 0xb3b8, 0x0000 }, + { 0xb3b9, 0x0000 }, + { 0xb3ba, 0x0000 }, + { 0xb3bb, 0x0000 }, + { 0xb3bc, 0x0000 }, + { 0xb3bd, 0x0000 }, + { 0xb3be, 0x0000 }, + { 0xb3bf, 0x0000 }, + { 0xb3c0, 0x0000 }, + { 0xb3c1, 0x0000 }, + { 0xb3c2, 0x0000 }, + { 0xb3c3, 0x0000 }, + { 0xb3c4, 0x0000 }, + { 0xb3c5, 0x0000 }, + { 0xb3c6, 0x0000 }, + { 0xb3c7, 0x0000 }, + { 0xb3c8, 0x0000 }, + { 0xb3c9, 0x0000 }, + { 0xb3ca, 0x0000 }, + { 0xb3cb, 0x0000 }, + { 0xb3cc, 0x0000 }, + { 0xb3cd, 0x0000 }, + { 0xb3ce, 0x0000 }, + { 0xb3cf, 0x0000 }, + { 0xb3d0, 0x0000 }, + { 0xb3d1, 0x0000 }, + { 0xb3d2, 0x0000 }, + { 0xb3d3, 0x0000 }, + { 0xb3d4, 0x0000 }, + { 0xb3d5, 0x0000 }, + { 0xb3d6, 0x0000 }, + { 0xb3d7, 0x0000 }, + { 0xb3d8, 0x0000 }, + { 0xb3d9, 0x0000 }, + { 0xb3da, 0x0000 }, + { 0xb3db, 0x0000 }, + { 0xb3dc, 0x0000 }, + { 0xb3dd, 0x0000 }, + { 0xb3de, 0x0000 }, + { 0xb3df, 0x0000 }, + { 0xb3e0, 0x0000 }, + { 0xb3e1, 0x0000 }, + { 0xb3e2, 0x0000 }, + { 0xb3e3, 0x0000 }, + { 0xb3e4, 0x0000 }, + { 0xb3e5, 0x0000 }, + { 0xb3e6, 0x0000 }, + { 0xb3e7, 0x0000 }, + { 0xb3e8, 0x0000 }, + { 0xb3e9, 0x0000 }, + { 0xb3ea, 0x0000 }, + { 0xb3eb, 0x0000 }, + { 0xb3ec, 0x0000 }, + { 0xb3ed, 0x0000 }, + { 0xb3ee, 0x0000 }, + { 0xb3ef, 0x0000 }, + { 0xb3f0, 0x0000 }, + { 0xb3f1, 0x0000 }, + { 0xb3f2, 0x0000 }, + { 0xb3f3, 0x0000 }, + { 0xb3f4, 0x0000 }, + { 0xb3f5, 0x0000 }, + { 0xb3f6, 0x0000 }, + { 0xb3f7, 0x0000 }, + { 0xb3f8, 0x0000 }, + { 0xb3f9, 0x0000 }, + { 0xb3fa, 0x0000 }, + { 0xb3fb, 0x0000 }, + { 0xb3fc, 0x0000 }, + { 0xb3fd, 0x0000 }, + { 0xb3fe, 0x0000 }, + { 0xb3ff, 0x0000 }, + { 0xb400, 0x0000 }, + { 0xb401, 0x0000 }, + { 0xb402, 0x0000 }, + { 0xb403, 0x0000 }, + { 0xb404, 0x0000 }, + { 0xb405, 0x0000 }, + { 0xb406, 0x0000 }, + { 0xb407, 0x0000 }, + { 0xb408, 0x0000 }, + { 0xb409, 0x0000 }, + { 0xb40a, 0x0000 }, + { 0xb40b, 0x0000 }, + { 0xb40c, 0x0000 }, + { 0xb40d, 0x0000 }, + { 0xb40e, 0x0000 }, + { 0xb40f, 0x0000 }, + { 0xb410, 0x0000 }, + { 0xb411, 0x0000 }, + { 0xb412, 0x0000 }, + { 0xb413, 0x0000 }, + { 0xb414, 0x0000 }, + { 0xb415, 0x0000 }, + { 0xb416, 0x0000 }, + { 0xb417, 0x0000 }, + { 0xb418, 0x0000 }, + { 0xb419, 0x0000 }, + { 0xb41a, 0x0000 }, + { 0xb41b, 0x0000 }, + { 0xb41c, 0x0000 }, + { 0xb41d, 0x0000 }, + { 0xb41e, 0x0000 }, + { 0xb41f, 0x0000 }, + { 0xb420, 0x0000 }, + { 0xb421, 0x0000 }, + { 0xb422, 0x0000 }, + { 0xb423, 0x0000 }, + { 0xb424, 0x0000 }, + { 0xb425, 0x0000 }, + { 0xb426, 0x0000 }, + { 0xb427, 0x0000 }, + { 0xb428, 0x0000 }, + { 0xb429, 0x0000 }, + { 0xb42a, 0x0000 }, + { 0xb42b, 0x0000 }, + { 0xb42c, 0x0000 }, + { 0xb42d, 0x0000 }, + { 0xb42e, 0x0000 }, + { 0xb42f, 0x0000 }, + { 0xb430, 0x0000 }, + { 0xb431, 0x0000 }, + { 0xb432, 0x0000 }, + { 0xb433, 0x0000 }, + { 0xb434, 0x0000 }, + { 0xb435, 0x0000 }, + { 0xb436, 0x0000 }, + { 0xb437, 0x0000 }, + { 0xb438, 0x0000 }, + { 0xb439, 0x0000 }, + { 0xb43a, 0x0000 }, + { 0xb43b, 0x0000 }, + { 0xb43c, 0x0000 }, + { 0xb43d, 0x0000 }, + { 0xb43e, 0x0000 }, + { 0xb43f, 0x0000 }, + { 0xb440, 0x0000 }, + { 0xb441, 0x0000 }, + { 0xb442, 0x0000 }, + { 0xb443, 0x0000 }, + { 0xb444, 0x0000 }, + { 0xb445, 0x0000 }, + { 0xb446, 0x0000 }, + { 0xb447, 0x0000 }, + { 0xb448, 0x0000 }, + { 0xb449, 0x0000 }, + { 0xb44a, 0x0000 }, + { 0xb44b, 0x0000 }, + { 0xb44c, 0x0000 }, + { 0xb44d, 0x0000 }, + { 0xb44e, 0x0000 }, + { 0xb44f, 0x0000 }, + { 0xb450, 0x0000 }, + { 0xb451, 0x0000 }, + { 0xb452, 0x0000 }, + { 0xb453, 0x0000 }, + { 0xb454, 0x0000 }, + { 0xb455, 0x0000 }, + { 0xb456, 0x0000 }, + { 0xb457, 0x0000 }, + { 0xb458, 0x0000 }, + { 0xb459, 0x0000 }, + { 0xb45a, 0x0000 }, + { 0xb45b, 0x0000 }, + { 0xb45c, 0x0000 }, + { 0xb45d, 0x0000 }, + { 0xb45e, 0x0000 }, + { 0xb45f, 0x0000 }, + { 0xb460, 0x0000 }, + { 0xb461, 0x0000 }, + { 0xb462, 0x0000 }, + { 0xb463, 0x0000 }, + { 0xb464, 0x0000 }, + { 0xb465, 0x0000 }, + { 0xb466, 0x0000 }, + { 0xb467, 0x0000 }, + { 0xb468, 0x0000 }, + { 0xb469, 0x0000 }, + { 0xb46a, 0x0000 }, + { 0xb46b, 0x0000 }, + { 0xb46c, 0x0000 }, + { 0xb46d, 0x0000 }, + { 0xb46e, 0x0000 }, + { 0xb46f, 0x0000 }, + { 0xb470, 0x0000 }, + { 0xb471, 0x0000 }, + { 0xb472, 0x0000 }, + { 0xb473, 0x0000 }, + { 0xb474, 0x0000 }, + { 0xb475, 0x0000 }, + { 0xb476, 0x0000 }, + { 0xb477, 0x0000 }, + { 0xb478, 0x0000 }, + { 0xb479, 0x0000 }, + { 0xb47a, 0x0000 }, + { 0xb47b, 0x0000 }, + { 0xb47c, 0x0000 }, + { 0xb47d, 0x0000 }, + { 0xb47e, 0x0000 }, + { 0xb47f, 0x0000 }, + { 0xb480, 0x0000 }, + { 0xb481, 0x0000 }, + { 0xb482, 0x0000 }, + { 0xb483, 0x0000 }, + { 0xb484, 0x0000 }, + { 0xb485, 0x0000 }, + { 0xb486, 0x0000 }, + { 0xb487, 0x0000 }, + { 0xb488, 0x0000 }, + { 0xb489, 0x0000 }, + { 0xb48a, 0x0000 }, + { 0xb48b, 0x0000 }, + { 0xb48c, 0x0000 }, + { 0xb48d, 0x0000 }, + { 0xb48e, 0x0000 }, + { 0xb48f, 0x0000 }, + { 0xb490, 0x0000 }, + { 0xb491, 0x0000 }, + { 0xb492, 0x0000 }, + { 0xb493, 0x0000 }, + { 0xb494, 0x0000 }, + { 0xb495, 0x0000 }, + { 0xb496, 0x0000 }, + { 0xb497, 0x0000 }, + { 0xb498, 0x0000 }, + { 0xb499, 0x0000 }, + { 0xb49a, 0x0000 }, + { 0xb49b, 0x0000 }, + { 0xb49c, 0x0000 }, + { 0xb49d, 0x0000 }, + { 0xb49e, 0x0000 }, + { 0xb49f, 0x0000 }, + { 0xb4a0, 0x0000 }, + { 0xb4a1, 0x0000 }, + { 0xb4a2, 0x0000 }, + { 0xb4a3, 0x0000 }, + { 0xb4a4, 0x0000 }, + { 0xb4a5, 0x0000 }, + { 0xb4a6, 0x0000 }, + { 0xb4a7, 0x0000 }, + { 0xb4a8, 0x0000 }, + { 0xb4a9, 0x0000 }, + { 0xb4aa, 0x0000 }, + { 0xb4ab, 0x0000 }, + { 0xb4ac, 0x0000 }, + { 0xb4ad, 0x0000 }, + { 0xb4ae, 0x0000 }, + { 0xb4af, 0x0000 }, + { 0xb4b0, 0x0000 }, + { 0xb4b1, 0x0000 }, + { 0xb4b2, 0x0000 }, + { 0xb4b3, 0x0000 }, + { 0xb4b4, 0x0000 }, + { 0xb4b5, 0x0000 }, + { 0xb4b6, 0x0000 }, + { 0xb4b7, 0x0000 }, + { 0xb4b8, 0x0000 }, + { 0xb4b9, 0x0000 }, + { 0xb4ba, 0x0000 }, + { 0xb4bb, 0x0000 }, + { 0xb4bc, 0x0000 }, + { 0xb4bd, 0x0000 }, + { 0xb4be, 0x0000 }, + { 0xb4bf, 0x0000 }, + { 0xb4c0, 0x0000 }, + { 0xb4c1, 0x0000 }, + { 0xb4c2, 0x0000 }, + { 0xb4c3, 0x0000 }, + { 0xb4c4, 0x0000 }, + { 0xb4c5, 0x0000 }, + { 0xb4c6, 0x0000 }, + { 0xb4c7, 0x0000 }, + { 0xb4c8, 0x0000 }, + { 0xb4c9, 0x0000 }, + { 0xb4ca, 0x0000 }, + { 0xb4cb, 0x0000 }, + { 0xb4cc, 0x0000 }, + { 0xb4cd, 0x0000 }, + { 0xb4ce, 0x0000 }, + { 0xb4cf, 0x0000 }, + { 0xb4d0, 0x0000 }, + { 0xb4d1, 0x0000 }, + { 0xb4d2, 0x0000 }, + { 0xb4d3, 0x0000 }, + { 0xb4d4, 0x0000 }, + { 0xb4d5, 0x0000 }, + { 0xb4d6, 0x0000 }, + { 0xb4d7, 0x0000 }, + { 0xb4d8, 0x0000 }, + { 0xb4d9, 0x0000 }, + { 0xb4da, 0x0000 }, + { 0xb4db, 0x0000 }, + { 0xb4dc, 0x0000 }, + { 0xb4dd, 0x0000 }, + { 0xb4de, 0x0000 }, + { 0xb4df, 0x0000 }, + { 0xb4e0, 0x0000 }, + { 0xb4e1, 0x0000 }, + { 0xb4e2, 0x0000 }, + { 0xb4e3, 0x0000 }, + { 0xb4e4, 0x0000 }, + { 0xb4e5, 0x0000 }, + { 0xb4e6, 0x0000 }, + { 0xb4e7, 0x0000 }, + { 0xb4e8, 0x0000 }, + { 0xb4e9, 0x0000 }, + { 0xb4ea, 0x0000 }, + { 0xb4eb, 0x0000 }, + { 0xb4ec, 0x0000 }, + { 0xb4ed, 0x0000 }, + { 0xb4ee, 0x0000 }, + { 0xb4ef, 0x0000 }, + { 0xb4f0, 0x0000 }, + { 0xb4f1, 0x0000 }, + { 0xb4f2, 0x0000 }, + { 0xb4f3, 0x0000 }, + { 0xb4f4, 0x0000 }, + { 0xb4f5, 0x0000 }, + { 0xb4f6, 0x0000 }, + { 0xb4f7, 0x0000 }, + { 0xb4f8, 0x0000 }, + { 0xb4f9, 0x0000 }, + { 0xb4fa, 0x0000 }, + { 0xb4fb, 0x0000 }, + { 0xb4fc, 0x0000 }, + { 0xb4fd, 0x0000 }, + { 0xb4fe, 0x0000 }, + { 0xb4ff, 0x0000 }, + { 0xb500, 0x0000 }, + { 0xb501, 0x0000 }, + { 0xb502, 0x0000 }, + { 0xb503, 0x0000 }, + { 0xb504, 0x0000 }, + { 0xb505, 0x0000 }, + { 0xb506, 0x0000 }, + { 0xb507, 0x0000 }, + { 0xb508, 0x0000 }, + { 0xb509, 0x0000 }, + { 0xb50a, 0x0000 }, + { 0xb50b, 0x0000 }, + { 0xb50c, 0x0000 }, + { 0xb50d, 0x0000 }, + { 0xb50e, 0x0000 }, + { 0xb50f, 0x0000 }, + { 0xb510, 0x0000 }, + { 0xb511, 0x0000 }, + { 0xb512, 0x0000 }, + { 0xb513, 0x0000 }, + { 0xb514, 0x0000 }, + { 0xb515, 0x0000 }, + { 0xb516, 0x0000 }, + { 0xb517, 0x0000 }, + { 0xb518, 0x0000 }, + { 0xb519, 0x0000 }, + { 0xb51a, 0x0000 }, + { 0xb51b, 0x0000 }, + { 0xb51c, 0x0000 }, + { 0xb51d, 0x0000 }, + { 0xb51e, 0x0000 }, + { 0xb51f, 0x0000 }, + { 0xb520, 0x0000 }, + { 0xb521, 0x0000 }, + { 0xb522, 0x0000 }, + { 0xb523, 0x0000 }, + { 0xb524, 0x0000 }, + { 0xb525, 0x0000 }, + { 0xb526, 0x0000 }, + { 0xb527, 0x0000 }, + { 0xb528, 0x0000 }, + { 0xb529, 0x0000 }, + { 0xb52a, 0x0000 }, + { 0xb52b, 0x0000 }, + { 0xb52c, 0x0000 }, + { 0xb52d, 0x0000 }, + { 0xb52e, 0x0000 }, + { 0xb52f, 0x0000 }, + { 0xb530, 0x0000 }, + { 0xb531, 0x0000 }, + { 0xb532, 0x0000 }, + { 0xb533, 0x0000 }, + { 0xb534, 0x0000 }, + { 0xb535, 0x0000 }, + { 0xb536, 0x0000 }, + { 0xb537, 0x0000 }, + { 0xb538, 0x0000 }, + { 0xb539, 0x0000 }, + { 0xb53a, 0x0000 }, + { 0xb53b, 0x0000 }, + { 0xb53c, 0x0000 }, + { 0xb53d, 0x0000 }, + { 0xb53e, 0x0000 }, + { 0xb53f, 0x0000 }, + { 0xb540, 0x0000 }, + { 0xb541, 0x0000 }, + { 0xb542, 0x0000 }, + { 0xb543, 0x0000 }, + { 0xb544, 0x0000 }, + { 0xb545, 0x0000 }, + { 0xb546, 0x0000 }, + { 0xb547, 0x0000 }, + { 0xb548, 0x0000 }, + { 0xb549, 0x0000 }, + { 0xb54a, 0x0000 }, + { 0xb54b, 0x0000 }, + { 0xb54c, 0x0000 }, + { 0xb54d, 0x0000 }, + { 0xb54e, 0x0000 }, + { 0xb54f, 0x0000 }, + { 0xb550, 0x0000 }, + { 0xb551, 0x0000 }, + { 0xb552, 0x0000 }, + { 0xb553, 0x0000 }, + { 0xb554, 0x0000 }, + { 0xb555, 0x0000 }, + { 0xb556, 0x0000 }, + { 0xb557, 0x0000 }, + { 0xb558, 0x0000 }, + { 0xb559, 0x0000 }, + { 0xb55a, 0x0000 }, + { 0xb55b, 0x0000 }, + { 0xb55c, 0x0000 }, + { 0xb55d, 0x0000 }, + { 0xb55e, 0x0000 }, + { 0xb55f, 0x0000 }, + { 0xb560, 0x0000 }, + { 0xb561, 0x0000 }, + { 0xb562, 0x0000 }, + { 0xb563, 0x0000 }, + { 0xb564, 0x0000 }, + { 0xb565, 0x0000 }, + { 0xb566, 0x0000 }, + { 0xb567, 0x0000 }, + { 0xb568, 0x0000 }, + { 0xb569, 0x0000 }, + { 0xb56a, 0x0000 }, + { 0xb56b, 0x0000 }, + { 0xb56c, 0x0000 }, + { 0xb56d, 0x0000 }, + { 0xb56e, 0x0000 }, + { 0xb56f, 0x0000 }, + { 0xb570, 0x0000 }, + { 0xb571, 0x0000 }, + { 0xb572, 0x0000 }, + { 0xb573, 0x0000 }, + { 0xb574, 0x0000 }, + { 0xb575, 0x0000 }, + { 0xb576, 0x0000 }, + { 0xb577, 0x0000 }, + { 0xb578, 0x0000 }, + { 0xb579, 0x0000 }, + { 0xb57a, 0x0000 }, + { 0xb57b, 0x0000 }, + { 0xb57c, 0x0000 }, + { 0xb57d, 0x0000 }, + { 0xb57e, 0x0000 }, + { 0xb57f, 0x0000 }, + { 0xb580, 0x0000 }, + { 0xb581, 0x0000 }, + { 0xb582, 0x0000 }, + { 0xb583, 0x0000 }, + { 0xb584, 0x0000 }, + { 0xb585, 0x0000 }, + { 0xb586, 0x0000 }, + { 0xb587, 0x0000 }, + { 0xb588, 0x0000 }, + { 0xb589, 0x0000 }, + { 0xb58a, 0x0000 }, + { 0xb58b, 0x0000 }, + { 0xb58c, 0x0000 }, + { 0xb58d, 0x0000 }, + { 0xb58e, 0x0000 }, + { 0xb58f, 0x0000 }, + { 0xb590, 0x0000 }, + { 0xb591, 0x0000 }, + { 0xb592, 0x0000 }, + { 0xb593, 0x0000 }, + { 0xb594, 0x0000 }, + { 0xb595, 0x0000 }, + { 0xb596, 0x0000 }, + { 0xb597, 0x0000 }, + { 0xb598, 0x0000 }, + { 0xb599, 0x0000 }, + { 0xb59a, 0x0000 }, + { 0xb59b, 0x0000 }, + { 0xb59c, 0x0000 }, + { 0xb59d, 0x0000 }, + { 0xb59e, 0x0000 }, + { 0xb59f, 0x0000 }, + { 0xb5a0, 0x0000 }, + { 0xb5a1, 0x0000 }, + { 0xb5a2, 0x0000 }, + { 0xb5a3, 0x0000 }, + { 0xb5a4, 0x0000 }, + { 0xb5a5, 0x0000 }, + { 0xb5a6, 0x0000 }, + { 0xb5a7, 0x0000 }, + { 0xb5a8, 0x0000 }, + { 0xb5a9, 0x0000 }, + { 0xb5aa, 0x0000 }, + { 0xb5ab, 0x0000 }, + { 0xb5ac, 0x0000 }, + { 0xb5ad, 0x0000 }, + { 0xb5ae, 0x0000 }, + { 0xb5af, 0x0000 }, + { 0xb5b0, 0x0000 }, + { 0xb5b1, 0x0000 }, + { 0xb5b2, 0x0000 }, + { 0xb5b3, 0x0000 }, + { 0xb5b4, 0x0000 }, + { 0xb5b5, 0x0000 }, + { 0xb5b6, 0x0000 }, + { 0xb5b7, 0x0000 }, + { 0xb5b8, 0x0000 }, + { 0xb5b9, 0x0000 }, + { 0xb5ba, 0x0000 }, + { 0xb5bb, 0x0000 }, + { 0xb5bc, 0x0000 }, + { 0xb5bd, 0x0000 }, + { 0xb5be, 0x0000 }, + { 0xb5bf, 0x0000 }, + { 0xb5c0, 0x0000 }, + { 0xb5c1, 0x0000 }, + { 0xb5c2, 0x0000 }, + { 0xb5c3, 0x0000 }, + { 0xb5c4, 0x0000 }, + { 0xb5c5, 0x0000 }, + { 0xb5c6, 0x0000 }, + { 0xb5c7, 0x0000 }, + { 0xb5c8, 0x0000 }, + { 0xb5c9, 0x0000 }, + { 0xb5ca, 0x0000 }, + { 0xb5cb, 0x0000 }, + { 0xb5cc, 0x0000 }, + { 0xb5cd, 0x0000 }, + { 0xb5ce, 0x0000 }, + { 0xb5cf, 0x0000 }, + { 0xb5d0, 0x0000 }, + { 0xb5d1, 0x0000 }, + { 0xb5d2, 0x0000 }, + { 0xb5d3, 0x0000 }, + { 0xb5d4, 0x0000 }, + { 0xb5d5, 0x0000 }, + { 0xb5d6, 0x0000 }, + { 0xb5d7, 0x0000 }, + { 0xb5d8, 0x0000 }, + { 0xb5d9, 0x0000 }, + { 0xb5da, 0x0000 }, + { 0xb5db, 0x0000 }, + { 0xb5dc, 0x0000 }, + { 0xb5dd, 0x0000 }, + { 0xb5de, 0x0000 }, + { 0xb5df, 0x0000 }, + { 0xb5e0, 0x0000 }, + { 0xb5e1, 0x0000 }, + { 0xb5e2, 0x0000 }, + { 0xb5e3, 0x0000 }, + { 0xb5e4, 0x0000 }, + { 0xb5e5, 0x0000 }, + { 0xb5e6, 0x0000 }, + { 0xb5e7, 0x0000 }, + { 0xb5e8, 0x0000 }, + { 0xb5e9, 0x0000 }, + { 0xb5ea, 0x0000 }, + { 0xb5eb, 0x0000 }, + { 0xb5ec, 0x0000 }, + { 0xb5ed, 0x0000 }, + { 0xb5ee, 0x0000 }, + { 0xb5ef, 0x0000 }, + { 0xb5f0, 0x0000 }, + { 0xb5f1, 0x0000 }, + { 0xb5f2, 0x0000 }, + { 0xb5f3, 0x0000 }, + { 0xb5f4, 0x0000 }, + { 0xb5f5, 0x0000 }, + { 0xb5f6, 0x0000 }, + { 0xb5f7, 0x0000 }, + { 0xb5f8, 0x0000 }, + { 0xb5f9, 0x0000 }, + { 0xb5fa, 0x0000 }, + { 0xb5fb, 0x0000 }, + { 0xb5fc, 0x0000 }, + { 0xb5fd, 0x0000 }, + { 0xb5fe, 0x0000 }, + { 0xb5ff, 0x0000 }, + { 0xb600, 0x0000 }, + { 0xb601, 0x0000 }, + { 0xb602, 0x0000 }, + { 0xb603, 0x0000 }, + { 0xb604, 0x0000 }, + { 0xb605, 0x0000 }, + { 0xb606, 0x0000 }, + { 0xb607, 0x0000 }, + { 0xb608, 0x0000 }, + { 0xb609, 0x0000 }, + { 0xb60a, 0x0000 }, + { 0xb60b, 0x0000 }, + { 0xb60c, 0x0000 }, + { 0xb60d, 0x0000 }, + { 0xb60e, 0x0000 }, + { 0xb60f, 0x0000 }, + { 0xb610, 0x0000 }, + { 0xb611, 0x0000 }, + { 0xb612, 0x0000 }, + { 0xb613, 0x0000 }, + { 0xb614, 0x0000 }, + { 0xb615, 0x0000 }, + { 0xb616, 0x0000 }, + { 0xb617, 0x0000 }, + { 0xb618, 0x0000 }, + { 0xb619, 0x0000 }, + { 0xb61a, 0x0000 }, + { 0xb61b, 0x0000 }, + { 0xb61c, 0x0000 }, + { 0xb61d, 0x0000 }, + { 0xb61e, 0x0000 }, + { 0xb61f, 0x0000 }, + { 0xb620, 0x0000 }, + { 0xb621, 0x0000 }, + { 0xb622, 0x0000 }, + { 0xb623, 0x0000 }, + { 0xb624, 0x0000 }, + { 0xb625, 0x0000 }, + { 0xb626, 0x0000 }, + { 0xb627, 0x0000 }, + { 0xb628, 0x0000 }, + { 0xb629, 0x0000 }, + { 0xb62a, 0x0000 }, + { 0xb62b, 0x0000 }, + { 0xb62c, 0x0000 }, + { 0xb62d, 0x0000 }, + { 0xb62e, 0x0000 }, + { 0xb62f, 0x0000 }, + { 0xb630, 0x0000 }, + { 0xb631, 0x0000 }, + { 0xb632, 0x0000 }, + { 0xb633, 0x0000 }, + { 0xb634, 0x0000 }, + { 0xb635, 0x0000 }, + { 0xb636, 0x0000 }, + { 0xb637, 0x0000 }, + { 0xb638, 0x0000 }, + { 0xb639, 0x0000 }, + { 0xb63a, 0x0000 }, + { 0xb63b, 0x0000 }, + { 0xb63c, 0x0000 }, + { 0xb63d, 0x0000 }, + { 0xb63e, 0x0000 }, + { 0xb63f, 0x0000 }, + { 0xb640, 0x0000 }, + { 0xb641, 0x0000 }, + { 0xb642, 0x0000 }, + { 0xb643, 0x0000 }, + { 0xb644, 0x0000 }, + { 0xb645, 0x0000 }, + { 0xb646, 0x0000 }, + { 0xb647, 0x0000 }, + { 0xb648, 0x0000 }, + { 0xb649, 0x0000 }, + { 0xb64a, 0x0000 }, + { 0xb64b, 0x0000 }, + { 0xb64c, 0x0000 }, + { 0xb64d, 0x0000 }, + { 0xb64e, 0x0000 }, + { 0xb64f, 0x0000 }, + { 0xb650, 0x0000 }, + { 0xb651, 0x0000 }, + { 0xb652, 0x0000 }, + { 0xb653, 0x0000 }, + { 0xb654, 0x0000 }, + { 0xb655, 0x0000 }, + { 0xb656, 0x0000 }, + { 0xb657, 0x0000 }, + { 0xb658, 0x0000 }, + { 0xb659, 0x0000 }, + { 0xb65a, 0x0000 }, + { 0xb65b, 0x0000 }, + { 0xb65c, 0x0000 }, + { 0xb65d, 0x0000 }, + { 0xb65e, 0x0000 }, + { 0xb65f, 0x0000 }, + { 0xb660, 0x0000 }, + { 0xb661, 0x0000 }, + { 0xb662, 0x0000 }, + { 0xb663, 0x0000 }, + { 0xb664, 0x0000 }, + { 0xb665, 0x0000 }, + { 0xb666, 0x0000 }, + { 0xb667, 0x0000 }, + { 0xb668, 0x0000 }, + { 0xb669, 0x0000 }, + { 0xb66a, 0x0000 }, + { 0xb66b, 0x0000 }, + { 0xb66c, 0x0000 }, + { 0xb66d, 0x0000 }, + { 0xb66e, 0x0000 }, + { 0xb66f, 0x0000 }, + { 0xb670, 0x0000 }, + { 0xb671, 0x0000 }, + { 0xb672, 0x0000 }, + { 0xb673, 0x0000 }, + { 0xb674, 0x0000 }, + { 0xb675, 0x0000 }, + { 0xb676, 0x0000 }, + { 0xb677, 0x0000 }, + { 0xb678, 0x0000 }, + { 0xb679, 0x0000 }, + { 0xb67a, 0x0000 }, + { 0xb67b, 0x0000 }, + { 0xb67c, 0x0000 }, + { 0xb67d, 0x0000 }, + { 0xb67e, 0x0000 }, + { 0xb67f, 0x0000 }, + { 0xb680, 0x0000 }, + { 0xb681, 0x0000 }, + { 0xb682, 0x0000 }, + { 0xb683, 0x0000 }, + { 0xb684, 0x0000 }, + { 0xb685, 0x0000 }, + { 0xb686, 0x0000 }, + { 0xb687, 0x0000 }, + { 0xb688, 0x0000 }, + { 0xb689, 0x0000 }, + { 0xb68a, 0x0000 }, + { 0xb68b, 0x0000 }, + { 0xb68c, 0x0000 }, + { 0xb68d, 0x0000 }, + { 0xb68e, 0x0000 }, + { 0xb68f, 0x0000 }, + { 0xb690, 0x0000 }, + { 0xb691, 0x0000 }, + { 0xb692, 0x0000 }, + { 0xb693, 0x0000 }, + { 0xb694, 0x0000 }, + { 0xb695, 0x0000 }, + { 0xb696, 0x0000 }, + { 0xb697, 0x0000 }, + { 0xb698, 0x0000 }, + { 0xb699, 0x0000 }, + { 0xb69a, 0x0000 }, + { 0xb69b, 0x0000 }, + { 0xb69c, 0x0000 }, + { 0xb69d, 0x0000 }, + { 0xb69e, 0x0000 }, + { 0xb69f, 0x0000 }, + { 0xb6a0, 0x0000 }, + { 0xb6a1, 0x0000 }, + { 0xb6a2, 0x0000 }, + { 0xb6a3, 0x0000 }, + { 0xb6a4, 0x0000 }, + { 0xb6a5, 0x0000 }, + { 0xb6a6, 0x0000 }, + { 0xb6a7, 0x0000 }, + { 0xb6a8, 0x0000 }, + { 0xb6a9, 0x0000 }, + { 0xb6aa, 0x0000 }, + { 0xb6ab, 0x0000 }, + { 0xb6ac, 0x0000 }, + { 0xb6ad, 0x0000 }, + { 0xb6ae, 0x0000 }, + { 0xb6af, 0x0000 }, + { 0xb6b0, 0x0000 }, + { 0xb6b1, 0x0000 }, + { 0xb6b2, 0x0000 }, + { 0xb6b3, 0x0000 }, + { 0xb6b4, 0x0000 }, + { 0xb6b5, 0x0000 }, + { 0xb6b6, 0x0000 }, + { 0xb6b7, 0x0000 }, + { 0xb6b8, 0x0000 }, + { 0xb6b9, 0x0000 }, + { 0xb6ba, 0x0000 }, + { 0xb6bb, 0x0000 }, + { 0xb6bc, 0x0000 }, + { 0xb6bd, 0x0000 }, + { 0xb6be, 0x0000 }, + { 0xb6bf, 0x0000 }, + { 0xb6c0, 0x0000 }, + { 0xb6c1, 0x0000 }, + { 0xb6c2, 0x0000 }, + { 0xb6c3, 0x0000 }, + { 0xb6c4, 0x0000 }, + { 0xb6c5, 0x0000 }, + { 0xb6c6, 0x0000 }, + { 0xb6c7, 0x0000 }, + { 0xb6c8, 0x0000 }, + { 0xb6c9, 0x0000 }, + { 0xb6ca, 0x0000 }, + { 0xb6cb, 0x0000 }, + { 0xb6cc, 0x0000 }, + { 0xb6cd, 0x0000 }, + { 0xb6ce, 0x0000 }, + { 0xb6cf, 0x0000 }, + { 0xb6d0, 0x0000 }, + { 0xb6d1, 0x0000 }, + { 0xb6d2, 0x0000 }, + { 0xb6d3, 0x0000 }, + { 0xb6d4, 0x0000 }, + { 0xb6d5, 0x0000 }, + { 0xb6d6, 0x0000 }, + { 0xb6d7, 0x0000 }, + { 0xb6d8, 0x0000 }, + { 0xb6d9, 0x0000 }, + { 0xb6da, 0x0000 }, + { 0xb6db, 0x0000 }, + { 0xb6dc, 0x0000 }, + { 0xb6dd, 0x0000 }, + { 0xb6de, 0x0000 }, + { 0xb6df, 0x0000 }, + { 0xb6e0, 0x0000 }, + { 0xb6e1, 0x0000 }, + { 0xb6e2, 0x0000 }, + { 0xb6e3, 0x0000 }, + { 0xb6e4, 0x0000 }, + { 0xb6e5, 0x0000 }, + { 0xb6e6, 0x0000 }, + { 0xb6e7, 0x0000 }, + { 0xb6e8, 0x0000 }, + { 0xb6e9, 0x0000 }, + { 0xb6ea, 0x0000 }, + { 0xb6eb, 0x0000 }, + { 0xb6ec, 0x0000 }, + { 0xb6ed, 0x0000 }, + { 0xb6ee, 0x0000 }, + { 0xb6ef, 0x0000 }, + { 0xb6f0, 0x0000 }, + { 0xb6f1, 0x0000 }, + { 0xb6f2, 0x0000 }, + { 0xb6f3, 0x0000 }, + { 0xb6f4, 0x0000 }, + { 0xb6f5, 0x0000 }, + { 0xb6f6, 0x0000 }, + { 0xb6f7, 0x0000 }, + { 0xb6f8, 0x0000 }, + { 0xb6f9, 0x0000 }, + { 0xb6fa, 0x0000 }, + { 0xb6fb, 0x0000 }, + { 0xb6fc, 0x0000 }, + { 0xb6fd, 0x0000 }, + { 0xb6fe, 0x0000 }, + { 0xb6ff, 0x0000 }, + { 0xb700, 0x0000 }, + { 0xb701, 0x0000 }, + { 0xb702, 0x0000 }, + { 0xb703, 0x0000 }, + { 0xb704, 0x0000 }, + { 0xb705, 0x0000 }, + { 0xb706, 0x0000 }, + { 0xb707, 0x0000 }, + { 0xb708, 0x0000 }, + { 0xb709, 0x0000 }, + { 0xb70a, 0x0000 }, + { 0xb70b, 0x0000 }, + { 0xb70c, 0x0000 }, + { 0xb70d, 0x0000 }, + { 0xb70e, 0x0000 }, + { 0xb70f, 0x0000 }, + { 0xb710, 0x0000 }, + { 0xb711, 0x0000 }, + { 0xb712, 0x0000 }, + { 0xb713, 0x0000 }, + { 0xb714, 0x0000 }, + { 0xb715, 0x0000 }, + { 0xb716, 0x0000 }, + { 0xb717, 0x0000 }, + { 0xb718, 0x0000 }, + { 0xb719, 0x0000 }, + { 0xb71a, 0x0000 }, + { 0xb71b, 0x0000 }, + { 0xb71c, 0x0000 }, + { 0xb71d, 0x0000 }, + { 0xb71e, 0x0000 }, + { 0xb71f, 0x0000 }, + { 0xb720, 0x0000 }, + { 0xb721, 0x0000 }, + { 0xb722, 0x0000 }, + { 0xb723, 0x0000 }, + { 0xb724, 0x0000 }, + { 0xb725, 0x0000 }, + { 0xb726, 0x0000 }, + { 0xb727, 0x0000 }, + { 0xb728, 0x0000 }, + { 0xb729, 0x0000 }, + { 0xb72a, 0x0000 }, + { 0xb72b, 0x0000 }, + { 0xb72c, 0x0000 }, + { 0xb72d, 0x0000 }, + { 0xb72e, 0x0000 }, + { 0xb72f, 0x0000 }, + { 0xb730, 0x0000 }, + { 0xb731, 0x0000 }, + { 0xb732, 0x0000 }, + { 0xb733, 0x0000 }, + { 0xb734, 0x0000 }, + { 0xb735, 0x0000 }, + { 0xb736, 0x0000 }, + { 0xb737, 0x0000 }, + { 0xb738, 0x0000 }, + { 0xb739, 0x0000 }, + { 0xb73a, 0x0000 }, + { 0xb73b, 0x0000 }, + { 0xb73c, 0x0000 }, + { 0xb73d, 0x0000 }, + { 0xb73e, 0x0000 }, + { 0xb73f, 0x0000 }, + { 0xb740, 0x0000 }, + { 0xb741, 0x0000 }, + { 0xb742, 0x0000 }, + { 0xb743, 0x0000 }, + { 0xb744, 0x0000 }, + { 0xb745, 0x0000 }, + { 0xb746, 0x0000 }, + { 0xb747, 0x0000 }, + { 0xb748, 0x0000 }, + { 0xb749, 0x0000 }, + { 0xb74a, 0x0000 }, + { 0xb74b, 0x0000 }, + { 0xb74c, 0x0000 }, + { 0xb74d, 0x0000 }, + { 0xb74e, 0x0000 }, + { 0xb74f, 0x0000 }, + { 0xb750, 0x0000 }, + { 0xb751, 0x0000 }, + { 0xb752, 0x0000 }, + { 0xb753, 0x0000 }, + { 0xb754, 0x0000 }, + { 0xb755, 0x0000 }, + { 0xb756, 0x0000 }, + { 0xb757, 0x0000 }, + { 0xb758, 0x0000 }, + { 0xb759, 0x0000 }, + { 0xb75a, 0x0000 }, + { 0xb75b, 0x0000 }, + { 0xb75c, 0x0000 }, + { 0xb75d, 0x0000 }, + { 0xb75e, 0x0000 }, + { 0xb75f, 0x0000 }, + { 0xb760, 0x0000 }, + { 0xb761, 0x0000 }, + { 0xb762, 0x0000 }, + { 0xb763, 0x0000 }, + { 0xb764, 0x0000 }, + { 0xb765, 0x0000 }, + { 0xb766, 0x0000 }, + { 0xb767, 0x0000 }, + { 0xb768, 0x0000 }, + { 0xb769, 0x0000 }, + { 0xb76a, 0x0000 }, + { 0xb76b, 0x0000 }, + { 0xb76c, 0x0000 }, + { 0xb76d, 0x0000 }, + { 0xb76e, 0x0000 }, + { 0xb76f, 0x0000 }, + { 0xb770, 0x0000 }, + { 0xb771, 0x0000 }, + { 0xb772, 0x0000 }, + { 0xb773, 0x0000 }, + { 0xb774, 0x0000 }, + { 0xb775, 0x0000 }, + { 0xb776, 0x0000 }, + { 0xb777, 0x0000 }, + { 0xb778, 0x0000 }, + { 0xb779, 0x0000 }, + { 0xb77a, 0x0000 }, + { 0xb77b, 0x0000 }, + { 0xb77c, 0x0000 }, + { 0xb77d, 0x0000 }, + { 0xb77e, 0x0000 }, + { 0xb77f, 0x0000 }, + { 0xb780, 0x0000 }, + { 0xb781, 0x0000 }, + { 0xb782, 0x0000 }, + { 0xb783, 0x0000 }, + { 0xb784, 0x0000 }, + { 0xb785, 0x0000 }, + { 0xb786, 0x0000 }, + { 0xb787, 0x0000 }, + { 0xb788, 0x0000 }, + { 0xb789, 0x0000 }, + { 0xb78a, 0x0000 }, + { 0xb78b, 0x0000 }, + { 0xb78c, 0x0000 }, + { 0xb78d, 0x0000 }, + { 0xb78e, 0x0000 }, + { 0xb78f, 0x0000 }, + { 0xb790, 0x0000 }, + { 0xb791, 0x0000 }, + { 0xb792, 0x0000 }, + { 0xb793, 0x0000 }, + { 0xb794, 0x0000 }, + { 0xb795, 0x0000 }, + { 0xb796, 0x0000 }, + { 0xb797, 0x0000 }, + { 0xb798, 0x0000 }, + { 0xb799, 0x0000 }, + { 0xb79a, 0x0000 }, + { 0xb79b, 0x0000 }, + { 0xb79c, 0x0000 }, + { 0xb79d, 0x0000 }, + { 0xb79e, 0x0000 }, + { 0xb79f, 0x0000 }, + { 0xb7a0, 0x0000 }, + { 0xb7a1, 0x0000 }, + { 0xb7a2, 0x0000 }, + { 0xb7a3, 0x0000 }, + { 0xb7a4, 0x0000 }, + { 0xb7a5, 0x0000 }, + { 0xb7a6, 0x0000 }, + { 0xb7a7, 0x0000 }, + { 0xb7a8, 0x0000 }, + { 0xb7a9, 0x0000 }, + { 0xb7aa, 0x0000 }, + { 0xb7ab, 0x0000 }, + { 0xb7ac, 0x0000 }, + { 0xb7ad, 0x0000 }, + { 0xb7ae, 0x0000 }, + { 0xb7af, 0x0000 }, + { 0xb7b0, 0x0000 }, + { 0xb7b1, 0x0000 }, + { 0xb7b2, 0x0000 }, + { 0xb7b3, 0x0000 }, + { 0xb7b4, 0x0000 }, + { 0xb7b5, 0x0000 }, + { 0xb7b6, 0x0000 }, + { 0xb7b7, 0x0000 }, + { 0xb7b8, 0x0000 }, + { 0xb7b9, 0x0000 }, + { 0xb7ba, 0x0000 }, + { 0xb7bb, 0x0000 }, + { 0xb7bc, 0x0000 }, + { 0xb7bd, 0x0000 }, + { 0xb7be, 0x0000 }, + { 0xb7bf, 0x0000 }, + { 0xb7c0, 0x0000 }, + { 0xb7c1, 0x0000 }, + { 0xb7c2, 0x0000 }, + { 0xb7c3, 0x0000 }, + { 0xb7c4, 0x0000 }, + { 0xb7c5, 0x0000 }, + { 0xb7c6, 0x0000 }, + { 0xb7c7, 0x0000 }, + { 0xb7c8, 0x0000 }, + { 0xb7c9, 0x0000 }, + { 0xb7ca, 0x0000 }, + { 0xb7cb, 0x0000 }, + { 0xb7cc, 0x0000 }, + { 0xb7cd, 0x0000 }, + { 0xb7ce, 0x0000 }, + { 0xb7cf, 0x0000 }, + { 0xb7d0, 0x0000 }, + { 0xb7d1, 0x0000 }, + { 0xb7d2, 0x0000 }, + { 0xb7d3, 0x0000 }, + { 0xb7d4, 0x0000 }, + { 0xb7d5, 0x0000 }, + { 0xb7d6, 0x0000 }, + { 0xb7d7, 0x0000 }, + { 0xb7d8, 0x0000 }, + { 0xb7d9, 0x0000 }, + { 0xb7da, 0x0000 }, + { 0xb7db, 0x0000 }, + { 0xb7dc, 0x0000 }, + { 0xb7dd, 0x0000 }, + { 0xb7de, 0x0000 }, + { 0xb7df, 0x0000 }, + { 0xb7e0, 0x0000 }, + { 0xb7e1, 0x0000 }, + { 0xb7e2, 0x0000 }, + { 0xb7e3, 0x0000 }, + { 0xb7e4, 0x0000 }, + { 0xb7e5, 0x0000 }, + { 0xb7e6, 0x0000 }, + { 0xb7e7, 0x0000 }, + { 0xb7e8, 0x0000 }, + { 0xb7e9, 0x0000 }, + { 0xb7ea, 0x0000 }, + { 0xb7eb, 0x0000 }, + { 0xb7ec, 0x0000 }, + { 0xb7ed, 0x0000 }, + { 0xb7ee, 0x0000 }, + { 0xb7ef, 0x0000 }, + { 0xb7f0, 0x0000 }, + { 0xb7f1, 0x0000 }, + { 0xb7f2, 0x0000 }, + { 0xb7f3, 0x0000 }, + { 0xb7f4, 0x0000 }, + { 0xb7f5, 0x0000 }, + { 0xb7f6, 0x0000 }, + { 0xb7f7, 0x0000 }, + { 0xb7f8, 0x0000 }, + { 0xb7f9, 0x0000 }, + { 0xb7fa, 0x0000 }, + { 0xb7fb, 0x0000 }, + { 0xb7fc, 0x0000 }, + { 0xb7fd, 0x0000 }, + { 0xb7fe, 0x0000 }, + { 0xb7ff, 0x0000 }, + { 0xb800, 0x0000 }, + { 0xb801, 0x0000 }, + { 0xb802, 0x0000 }, + { 0xb803, 0x0000 }, + { 0xb804, 0x0000 }, + { 0xb805, 0x0000 }, + { 0xb806, 0x0000 }, + { 0xb807, 0x0000 }, + { 0xb808, 0x0000 }, + { 0xb809, 0x0000 }, + { 0xb80a, 0x0000 }, + { 0xb80b, 0x0000 }, + { 0xb80c, 0x0000 }, + { 0xb80d, 0x0000 }, + { 0xb80e, 0x0000 }, + { 0xb80f, 0x0000 }, + { 0xb810, 0x0000 }, + { 0xb811, 0x0000 }, + { 0xb812, 0x0000 }, + { 0xb813, 0x0000 }, + { 0xb814, 0x0000 }, + { 0xb815, 0x0000 }, + { 0xb816, 0x0000 }, + { 0xb817, 0x0000 }, + { 0xb818, 0x0000 }, + { 0xb819, 0x0000 }, + { 0xb81a, 0x0000 }, + { 0xb81b, 0x0000 }, + { 0xb81c, 0x0000 }, + { 0xb81d, 0x0000 }, + { 0xb81e, 0x0000 }, + { 0xb81f, 0x0000 }, + { 0xb820, 0x0000 }, + { 0xb821, 0x0000 }, + { 0xb822, 0x0000 }, + { 0xb823, 0x0000 }, + { 0xb824, 0x0000 }, + { 0xb825, 0x0000 }, + { 0xb826, 0x0000 }, + { 0xb827, 0x0000 }, + { 0xb828, 0x0000 }, + { 0xb829, 0x0000 }, + { 0xb82a, 0x0000 }, + { 0xb82b, 0x0000 }, + { 0xb82c, 0x0000 }, + { 0xb82d, 0x0000 }, + { 0xb82e, 0x0000 }, + { 0xb82f, 0x0000 }, + { 0xb830, 0x0000 }, + { 0xb831, 0x0000 }, + { 0xb832, 0x0000 }, + { 0xb833, 0x0000 }, + { 0xb834, 0x0000 }, + { 0xb835, 0x0000 }, + { 0xb836, 0x0000 }, + { 0xb837, 0x0000 }, + { 0xb838, 0x0000 }, + { 0xb839, 0x0000 }, + { 0xb83a, 0x0000 }, + { 0xb83b, 0x0000 }, + { 0xb83c, 0x0000 }, + { 0xb83d, 0x0000 }, + { 0xb83e, 0x0000 }, + { 0xb83f, 0x0000 }, + { 0xb840, 0x0000 }, + { 0xb841, 0x0000 }, + { 0xb842, 0x0000 }, + { 0xb843, 0x0000 }, + { 0xb844, 0x0000 }, + { 0xb845, 0x0000 }, + { 0xb846, 0x0000 }, + { 0xb847, 0x0000 }, + { 0xb848, 0x0000 }, + { 0xb849, 0x0000 }, + { 0xb84a, 0x0000 }, + { 0xb84b, 0x0000 }, + { 0xb84c, 0x0000 }, + { 0xb84d, 0x0000 }, + { 0xb84e, 0x0000 }, + { 0xb84f, 0x0000 }, + { 0xb850, 0x0000 }, + { 0xb851, 0x0000 }, + { 0xb852, 0x0000 }, + { 0xb853, 0x0000 }, + { 0xb854, 0x0000 }, + { 0xb855, 0x0000 }, + { 0xb856, 0x0000 }, + { 0xb857, 0x0000 }, + { 0xb858, 0x0000 }, + { 0xb859, 0x0000 }, + { 0xb85a, 0x0000 }, + { 0xb85b, 0x0000 }, + { 0xb85c, 0x0000 }, + { 0xb85d, 0x0000 }, + { 0xb85e, 0x0000 }, + { 0xb85f, 0x0000 }, + { 0xb860, 0x0000 }, + { 0xb861, 0x0000 }, + { 0xb862, 0x0000 }, + { 0xb863, 0x0000 }, + { 0xb864, 0x0000 }, + { 0xb865, 0x0000 }, + { 0xb866, 0x0000 }, + { 0xb867, 0x0000 }, + { 0xb868, 0x0000 }, + { 0xb869, 0x0000 }, + { 0xb86a, 0x0000 }, + { 0xb86b, 0x0000 }, + { 0xb86c, 0x0000 }, + { 0xb86d, 0x0000 }, + { 0xb86e, 0x0000 }, + { 0xb86f, 0x0000 }, + { 0xb870, 0x0000 }, + { 0xb871, 0x0000 }, + { 0xb872, 0x0000 }, + { 0xb873, 0x0000 }, + { 0xb874, 0x0000 }, + { 0xb875, 0x0000 }, + { 0xb876, 0x0000 }, + { 0xb877, 0x0000 }, + { 0xb878, 0x0000 }, + { 0xb879, 0x0000 }, + { 0xb87a, 0x0000 }, + { 0xb87b, 0x0000 }, + { 0xb87c, 0x0000 }, + { 0xb87d, 0x0000 }, + { 0xb87e, 0x0000 }, + { 0xb87f, 0x0000 }, + { 0xb880, 0x0000 }, + { 0xb881, 0x0000 }, + { 0xb882, 0x0000 }, + { 0xb883, 0x0000 }, + { 0xb884, 0x0000 }, + { 0xb885, 0x0000 }, + { 0xb886, 0x0000 }, + { 0xb887, 0x0000 }, + { 0xb888, 0x0000 }, + { 0xb889, 0x0000 }, + { 0xb88a, 0x0000 }, + { 0xb88b, 0x0000 }, + { 0xb88c, 0x0000 }, + { 0xb88d, 0x0000 }, + { 0xb88e, 0x0000 }, + { 0xb88f, 0x0000 }, + { 0xb890, 0x0000 }, + { 0xb891, 0x0000 }, + { 0xb892, 0x0000 }, + { 0xb893, 0x0000 }, + { 0xb894, 0x0000 }, + { 0xb895, 0x0000 }, + { 0xb896, 0x0000 }, + { 0xb897, 0x0000 }, + { 0xb898, 0x0000 }, + { 0xb899, 0x0000 }, + { 0xb89a, 0x0000 }, + { 0xb89b, 0x0000 }, + { 0xb89c, 0x0000 }, + { 0xb89d, 0x0000 }, + { 0xb89e, 0x0000 }, + { 0xb89f, 0x0000 }, + { 0xb8a0, 0x0000 }, + { 0xb8a1, 0x0000 }, + { 0xb8a2, 0x0000 }, + { 0xb8a3, 0x0000 }, + { 0xb8a4, 0x0000 }, + { 0xb8a5, 0x0000 }, + { 0xb8a6, 0x0000 }, + { 0xb8a7, 0x0000 }, + { 0xb8a8, 0x0000 }, + { 0xb8a9, 0x0000 }, + { 0xb8aa, 0x0000 }, + { 0xb8ab, 0x0000 }, + { 0xb8ac, 0x0000 }, + { 0xb8ad, 0x0000 }, + { 0xb8ae, 0x0000 }, + { 0xb8af, 0x0000 }, + { 0xb8b0, 0x0000 }, + { 0xb8b1, 0x0000 }, + { 0xb8b2, 0x0000 }, + { 0xb8b3, 0x0000 }, + { 0xb8b4, 0x0000 }, + { 0xb8b5, 0x0000 }, + { 0xb8b6, 0x0000 }, + { 0xb8b7, 0x0000 }, + { 0xb8b8, 0x0000 }, + { 0xb8b9, 0x0000 }, + { 0xb8ba, 0x0000 }, + { 0xb8bb, 0x0000 }, + { 0xb8bc, 0x0000 }, + { 0xb8bd, 0x0000 }, + { 0xb8be, 0x0000 }, + { 0xb8bf, 0x0000 }, + { 0xb8c0, 0x0000 }, + { 0xb8c1, 0x0000 }, + { 0xb8c2, 0x0000 }, + { 0xb8c3, 0x0000 }, + { 0xb8c4, 0x0000 }, + { 0xb8c5, 0x0000 }, + { 0xb8c6, 0x0000 }, + { 0xb8c7, 0x0000 }, + { 0xb8c8, 0x0000 }, + { 0xb8c9, 0x0000 }, + { 0xb8ca, 0x0000 }, + { 0xb8cb, 0x0000 }, + { 0xb8cc, 0x0000 }, + { 0xb8cd, 0x0000 }, + { 0xb8ce, 0x0000 }, + { 0xb8cf, 0x0000 }, + { 0xb8d0, 0x0000 }, + { 0xb8d1, 0x0000 }, + { 0xb8d2, 0x0000 }, + { 0xb8d3, 0x0000 }, + { 0xb8d4, 0x0000 }, + { 0xb8d5, 0x0000 }, + { 0xb8d6, 0x0000 }, + { 0xb8d7, 0x0000 }, + { 0xb8d8, 0x0000 }, + { 0xb8d9, 0x0000 }, + { 0xb8da, 0x0000 }, + { 0xb8db, 0x0000 }, + { 0xb8dc, 0x0000 }, + { 0xb8dd, 0x0000 }, + { 0xb8de, 0x0000 }, + { 0xb8df, 0x0000 }, + { 0xb8e0, 0x0000 }, + { 0xb8e1, 0x0000 }, + { 0xb8e2, 0x0000 }, + { 0xb8e3, 0x0000 }, + { 0xb8e4, 0x0000 }, + { 0xb8e5, 0x0000 }, + { 0xb8e6, 0x0000 }, + { 0xb8e7, 0x0000 }, + { 0xb8e8, 0x0000 }, + { 0xb8e9, 0x0000 }, + { 0xb8ea, 0x0000 }, + { 0xb8eb, 0x0000 }, + { 0xb8ec, 0x0000 }, + { 0xb8ed, 0x0000 }, + { 0xb8ee, 0x0000 }, + { 0xb8ef, 0x0000 }, + { 0xb8f0, 0x0000 }, + { 0xb8f1, 0x0000 }, + { 0xb8f2, 0x0000 }, + { 0xb8f3, 0x0000 }, + { 0xb8f4, 0x0000 }, + { 0xb8f5, 0x0000 }, + { 0xb8f6, 0x0000 }, + { 0xb8f7, 0x0000 }, + { 0xb8f8, 0x0000 }, + { 0xb8f9, 0x0000 }, + { 0xb8fa, 0x0000 }, + { 0xb8fb, 0x0000 }, + { 0xb8fc, 0x0000 }, + { 0xb8fd, 0x0000 }, + { 0xb8fe, 0x0000 }, + { 0xb8ff, 0x0000 }, + { 0xb900, 0x0000 }, + { 0xb901, 0x0000 }, + { 0xb902, 0x0000 }, + { 0xb903, 0x0000 }, + { 0xb904, 0x0000 }, + { 0xb905, 0x0000 }, + { 0xb906, 0x0000 }, + { 0xb907, 0x0000 }, + { 0xb908, 0x0000 }, + { 0xb909, 0x0000 }, + { 0xb90a, 0x0000 }, + { 0xb90b, 0x0000 }, + { 0xb90c, 0x0000 }, + { 0xb90d, 0x0000 }, + { 0xb90e, 0x0000 }, + { 0xb90f, 0x0000 }, + { 0xb910, 0x0000 }, + { 0xb911, 0x0000 }, + { 0xb912, 0x0000 }, + { 0xb913, 0x0000 }, + { 0xb914, 0x0000 }, + { 0xb915, 0x0000 }, + { 0xb916, 0x0000 }, + { 0xb917, 0x0000 }, + { 0xb918, 0x0000 }, + { 0xb919, 0x0000 }, + { 0xb91a, 0x0000 }, + { 0xb91b, 0x0000 }, + { 0xb91c, 0x0000 }, + { 0xb91d, 0x0000 }, + { 0xb91e, 0x0000 }, + { 0xb91f, 0x0000 }, + { 0xb920, 0x0000 }, + { 0xb921, 0x0000 }, + { 0xb922, 0x0000 }, + { 0xb923, 0x0000 }, + { 0xb924, 0x0000 }, + { 0xb925, 0x0000 }, + { 0xb926, 0x0000 }, + { 0xb927, 0x0000 }, + { 0xb928, 0x0000 }, + { 0xb929, 0x0000 }, + { 0xb92a, 0x0000 }, + { 0xb92b, 0x0000 }, + { 0xb92c, 0x0000 }, + { 0xb92d, 0x0000 }, + { 0xb92e, 0x0000 }, + { 0xb92f, 0x0000 }, + { 0xb930, 0x0000 }, + { 0xb931, 0x0000 }, + { 0xb932, 0x0000 }, + { 0xb933, 0x0000 }, + { 0xb934, 0x0000 }, + { 0xb935, 0x0000 }, + { 0xb936, 0x0000 }, + { 0xb937, 0x0000 }, + { 0xb938, 0x0000 }, + { 0xb939, 0x0000 }, + { 0xb93a, 0x0000 }, + { 0xb93b, 0x0000 }, + { 0xb93c, 0x0000 }, + { 0xb93d, 0x0000 }, + { 0xb93e, 0x0000 }, + { 0xb93f, 0x0000 }, + { 0xb940, 0x0000 }, + { 0xb941, 0x0000 }, + { 0xb942, 0x0000 }, + { 0xb943, 0x0000 }, + { 0xb944, 0x0000 }, + { 0xb945, 0x0000 }, + { 0xb946, 0x0000 }, + { 0xb947, 0x0000 }, + { 0xb948, 0x0000 }, + { 0xb949, 0x0000 }, + { 0xb94a, 0x0000 }, + { 0xb94b, 0x0000 }, + { 0xb94c, 0x0000 }, + { 0xb94d, 0x0000 }, + { 0xb94e, 0x0000 }, + { 0xb94f, 0x0000 }, + { 0xb950, 0x0000 }, + { 0xb951, 0x0000 }, + { 0xb952, 0x0000 }, + { 0xb953, 0x0000 }, + { 0xb954, 0x0000 }, + { 0xb955, 0x0000 }, + { 0xb956, 0x0000 }, + { 0xb957, 0x0000 }, + { 0xb958, 0x0000 }, + { 0xb959, 0x0000 }, + { 0xb95a, 0x0000 }, + { 0xb95b, 0x0000 }, + { 0xb95c, 0x0000 }, + { 0xb95d, 0x0000 }, + { 0xb95e, 0x0000 }, + { 0xb95f, 0x0000 }, + { 0xb960, 0x0000 }, + { 0xb961, 0x0000 }, + { 0xb962, 0x0000 }, + { 0xb963, 0x0000 }, + { 0xb964, 0x0000 }, + { 0xb965, 0x0000 }, + { 0xb966, 0x0000 }, + { 0xb967, 0x0000 }, + { 0xb968, 0x0000 }, + { 0xb969, 0x0000 }, + { 0xb96a, 0x0000 }, + { 0xb96b, 0x0000 }, + { 0xb96c, 0x0000 }, + { 0xb96d, 0x0000 }, + { 0xb96e, 0x0000 }, + { 0xb96f, 0x0000 }, + { 0xb970, 0x0000 }, + { 0xb971, 0x0000 }, + { 0xb972, 0x0000 }, + { 0xb973, 0x0000 }, + { 0xb974, 0x0000 }, + { 0xb975, 0x0000 }, + { 0xb976, 0x0000 }, + { 0xb977, 0x0000 }, + { 0xb978, 0x0000 }, + { 0xb979, 0x0000 }, + { 0xb97a, 0x0000 }, + { 0xb97b, 0x0000 }, + { 0xb97c, 0x0000 }, + { 0xb97d, 0x0000 }, + { 0xb97e, 0x0000 }, + { 0xb97f, 0x0000 }, + { 0xb980, 0x0000 }, + { 0xb981, 0x0000 }, + { 0xb982, 0x0000 }, + { 0xb983, 0x0000 }, + { 0xb984, 0x0000 }, + { 0xb985, 0x0000 }, + { 0xb986, 0x0000 }, + { 0xb987, 0x0000 }, + { 0xb988, 0x0000 }, + { 0xb989, 0x0000 }, + { 0xb98a, 0x0000 }, + { 0xb98b, 0x0000 }, + { 0xb98c, 0x0000 }, + { 0xb98d, 0x0000 }, + { 0xb98e, 0x0000 }, + { 0xb98f, 0x0000 }, + { 0xb990, 0x0000 }, + { 0xb991, 0x0000 }, + { 0xb992, 0x0000 }, + { 0xb993, 0x0000 }, + { 0xb994, 0x0000 }, + { 0xb995, 0x0000 }, + { 0xb996, 0x0000 }, + { 0xb997, 0x0000 }, + { 0xb998, 0x0000 }, + { 0xb999, 0x0000 }, + { 0xb99a, 0x0000 }, + { 0xb99b, 0x0000 }, + { 0xb99c, 0x0000 }, + { 0xb99d, 0x0000 }, + { 0xb99e, 0x0000 }, + { 0xb99f, 0x0000 }, + { 0xb9a0, 0x0000 }, + { 0xb9a1, 0x0000 }, + { 0xb9a2, 0x0000 }, + { 0xb9a3, 0x0000 }, + { 0xb9a4, 0x0000 }, + { 0xb9a5, 0x0000 }, + { 0xb9a6, 0x0000 }, + { 0xb9a7, 0x0000 }, + { 0xb9a8, 0x0000 }, + { 0xb9a9, 0x0000 }, + { 0xb9aa, 0x0000 }, + { 0xb9ab, 0x0000 }, + { 0xb9ac, 0x0000 }, + { 0xb9ad, 0x0000 }, + { 0xb9ae, 0x0000 }, + { 0xb9af, 0x0000 }, + { 0xb9b0, 0x0000 }, + { 0xb9b1, 0x0000 }, + { 0xb9b2, 0x0000 }, + { 0xb9b3, 0x0000 }, + { 0xb9b4, 0x0000 }, + { 0xb9b5, 0x0000 }, + { 0xb9b6, 0x0000 }, + { 0xb9b7, 0x0000 }, + { 0xb9b8, 0x0000 }, + { 0xb9b9, 0x0000 }, + { 0xb9ba, 0x0000 }, + { 0xb9bb, 0x0000 }, + { 0xb9bc, 0x0000 }, + { 0xb9bd, 0x0000 }, + { 0xb9be, 0x0000 }, + { 0xb9bf, 0x0000 }, + { 0xb9c0, 0x0000 }, + { 0xb9c1, 0x0000 }, + { 0xb9c2, 0x0000 }, + { 0xb9c3, 0x0000 }, + { 0xb9c4, 0x0000 }, + { 0xb9c5, 0x0000 }, + { 0xb9c6, 0x0000 }, + { 0xb9c7, 0x0000 }, + { 0xb9c8, 0x0000 }, + { 0xb9c9, 0x0000 }, + { 0xb9ca, 0x0000 }, + { 0xb9cb, 0x0000 }, + { 0xb9cc, 0x0000 }, + { 0xb9cd, 0x0000 }, + { 0xb9ce, 0x0000 }, + { 0xb9cf, 0x0000 }, + { 0xb9d0, 0x0000 }, + { 0xb9d1, 0x0000 }, + { 0xb9d2, 0x0000 }, + { 0xb9d3, 0x0000 }, + { 0xb9d4, 0x0000 }, + { 0xb9d5, 0x0000 }, + { 0xb9d6, 0x0000 }, + { 0xb9d7, 0x0000 }, + { 0xb9d8, 0x0000 }, + { 0xb9d9, 0x0000 }, + { 0xb9da, 0x0000 }, + { 0xb9db, 0x0000 }, + { 0xb9dc, 0x0000 }, + { 0xb9dd, 0x0000 }, + { 0xb9de, 0x0000 }, + { 0xb9df, 0x0000 }, + { 0xb9e0, 0x0000 }, + { 0xb9e1, 0x0000 }, + { 0xb9e2, 0x0000 }, + { 0xb9e3, 0x0000 }, + { 0xb9e4, 0x0000 }, + { 0xb9e5, 0x0000 }, + { 0xb9e6, 0x0000 }, + { 0xb9e7, 0x0000 }, + { 0xb9e8, 0x0000 }, + { 0xb9e9, 0x0000 }, + { 0xb9ea, 0x0000 }, + { 0xb9eb, 0x0000 }, + { 0xb9ec, 0x0000 }, + { 0xb9ed, 0x0000 }, + { 0xb9ee, 0x0000 }, + { 0xb9ef, 0x0000 }, + { 0xb9f0, 0x0000 }, + { 0xb9f1, 0x0000 }, + { 0xb9f2, 0x0000 }, + { 0xb9f3, 0x0000 }, + { 0xb9f4, 0x0000 }, + { 0xb9f5, 0x0000 }, + { 0xb9f6, 0x0000 }, + { 0xb9f7, 0x0000 }, + { 0xb9f8, 0x0000 }, + { 0xb9f9, 0x0000 }, + { 0xb9fa, 0x0000 }, + { 0xb9fb, 0x0000 }, + { 0xb9fc, 0x0000 }, + { 0xb9fd, 0x0000 }, + { 0xb9fe, 0x0000 }, + { 0xb9ff, 0x0000 }, + { 0xba00, 0x0000 }, + { 0xba01, 0x0000 }, + { 0xba02, 0x0000 }, + { 0xba03, 0x0000 }, + { 0xba04, 0x0000 }, + { 0xba05, 0x0000 }, + { 0xba06, 0x0000 }, + { 0xba07, 0x0000 }, + { 0xba08, 0x0000 }, + { 0xba09, 0x0000 }, + { 0xba0a, 0x0000 }, + { 0xba0b, 0x0000 }, + { 0xba0c, 0x0000 }, + { 0xba0d, 0x0000 }, + { 0xba0e, 0x0000 }, + { 0xba0f, 0x0000 }, + { 0xba10, 0x0000 }, + { 0xba11, 0x0000 }, + { 0xba12, 0x0000 }, + { 0xba13, 0x0000 }, + { 0xba14, 0x0000 }, + { 0xba15, 0x0000 }, + { 0xba16, 0x0000 }, + { 0xba17, 0x0000 }, + { 0xba18, 0x0000 }, + { 0xba19, 0x0000 }, + { 0xba1a, 0x0000 }, + { 0xba1b, 0x0000 }, + { 0xba1c, 0x0000 }, + { 0xba1d, 0x0000 }, + { 0xba1e, 0x0000 }, + { 0xba1f, 0x0000 }, + { 0xba20, 0x0000 }, + { 0xba21, 0x0000 }, + { 0xba22, 0x0000 }, + { 0xba23, 0x0000 }, + { 0xba24, 0x0000 }, + { 0xba25, 0x0000 }, + { 0xba26, 0x0000 }, + { 0xba27, 0x0000 }, + { 0xba28, 0x0000 }, + { 0xba29, 0x0000 }, + { 0xba2a, 0x0000 }, + { 0xba2b, 0x0000 }, + { 0xba2c, 0x0000 }, + { 0xba2d, 0x0000 }, + { 0xba2e, 0x0000 }, + { 0xba2f, 0x0000 }, + { 0xba30, 0x0000 }, + { 0xba31, 0x0000 }, + { 0xba32, 0x0000 }, + { 0xba33, 0x0000 }, + { 0xba34, 0x0000 }, + { 0xba35, 0x0000 }, + { 0xba36, 0x0000 }, + { 0xba37, 0x0000 }, + { 0xba38, 0x0000 }, + { 0xba39, 0x0000 }, + { 0xba3a, 0x0000 }, + { 0xba3b, 0x0000 }, + { 0xba3c, 0x0000 }, + { 0xba3d, 0x0000 }, + { 0xba3e, 0x0000 }, + { 0xba3f, 0x0000 }, + { 0xba40, 0x0000 }, + { 0xba41, 0x0000 }, + { 0xba42, 0x0000 }, + { 0xba43, 0x0000 }, + { 0xba44, 0x0000 }, + { 0xba45, 0x0000 }, + { 0xba46, 0x0000 }, + { 0xba47, 0x0000 }, + { 0xba48, 0x0000 }, + { 0xba49, 0x0000 }, + { 0xba4a, 0x0000 }, + { 0xba4b, 0x0000 }, + { 0xba4c, 0x0000 }, + { 0xba4d, 0x0000 }, + { 0xba4e, 0x0000 }, + { 0xba4f, 0x0000 }, + { 0xba50, 0x0000 }, + { 0xba51, 0x0000 }, + { 0xba52, 0x0000 }, + { 0xba53, 0x0000 }, + { 0xba54, 0x0000 }, + { 0xba55, 0x0000 }, + { 0xba56, 0x0000 }, + { 0xba57, 0x0000 }, + { 0xba58, 0x0000 }, + { 0xba59, 0x0000 }, + { 0xba5a, 0x0000 }, + { 0xba5b, 0x0000 }, + { 0xba5c, 0x0000 }, + { 0xba5d, 0x0000 }, + { 0xba5e, 0x0000 }, + { 0xba5f, 0x0000 }, + { 0xba60, 0x0000 }, + { 0xba61, 0x0000 }, + { 0xba62, 0x0000 }, + { 0xba63, 0x0000 }, + { 0xba64, 0x0000 }, + { 0xba65, 0x0000 }, + { 0xba66, 0x0000 }, + { 0xba67, 0x0000 }, + { 0xba68, 0x0000 }, + { 0xba69, 0x0000 }, + { 0xba6a, 0x0000 }, + { 0xba6b, 0x0000 }, + { 0xba6c, 0x0000 }, + { 0xba6d, 0x0000 }, + { 0xba6e, 0x0000 }, + { 0xba6f, 0x0000 }, + { 0xba70, 0x0000 }, + { 0xba71, 0x0000 }, + { 0xba72, 0x0000 }, + { 0xba73, 0x0000 }, + { 0xba74, 0x0000 }, + { 0xba75, 0x0000 }, + { 0xba76, 0x0000 }, + { 0xba77, 0x0000 }, + { 0xba78, 0x0000 }, + { 0xba79, 0x0000 }, + { 0xba7a, 0x0000 }, + { 0xba7b, 0x0000 }, + { 0xba7c, 0x0000 }, + { 0xba7d, 0x0000 }, + { 0xba7e, 0x0000 }, + { 0xba7f, 0x0000 }, + { 0xba80, 0x0000 }, + { 0xba81, 0x0000 }, + { 0xba82, 0x0000 }, + { 0xba83, 0x0000 }, + { 0xba84, 0x0000 }, + { 0xba85, 0x0000 }, + { 0xba86, 0x0000 }, + { 0xba87, 0x0000 }, + { 0xba88, 0x0000 }, + { 0xba89, 0x0000 }, + { 0xba8a, 0x0000 }, + { 0xba8b, 0x0000 }, + { 0xba8c, 0x0000 }, + { 0xba8d, 0x0000 }, + { 0xba8e, 0x0000 }, + { 0xba8f, 0x0000 }, + { 0xba90, 0x0000 }, + { 0xba91, 0x0000 }, + { 0xba92, 0x0000 }, + { 0xba93, 0x0000 }, + { 0xba94, 0x0000 }, + { 0xba95, 0x0000 }, + { 0xba96, 0x0000 }, + { 0xba97, 0x0000 }, + { 0xba98, 0x0000 }, + { 0xba99, 0x0000 }, + { 0xba9a, 0x0000 }, + { 0xba9b, 0x0000 }, + { 0xba9c, 0x0000 }, + { 0xba9d, 0x0000 }, + { 0xba9e, 0x0000 }, + { 0xba9f, 0x0000 }, + { 0xbaa0, 0x0000 }, + { 0xbaa1, 0x0000 }, + { 0xbaa2, 0x0000 }, + { 0xbaa3, 0x0000 }, + { 0xbaa4, 0x0000 }, + { 0xbaa5, 0x0000 }, + { 0xbaa6, 0x0000 }, + { 0xbaa7, 0x0000 }, + { 0xbaa8, 0x0000 }, + { 0xbaa9, 0x0000 }, + { 0xbaaa, 0x0000 }, + { 0xbaab, 0x0000 }, + { 0xbaac, 0x0000 }, + { 0xbaad, 0x0000 }, + { 0xbaae, 0x0000 }, + { 0xbaaf, 0x0000 }, + { 0xbab0, 0x0000 }, + { 0xbab1, 0x0000 }, + { 0xbab2, 0x0000 }, + { 0xbab3, 0x0000 }, + { 0xbab4, 0x0000 }, + { 0xbab5, 0x0000 }, + { 0xbab6, 0x0000 }, + { 0xbab7, 0x0000 }, + { 0xbab8, 0x0000 }, + { 0xbab9, 0x0000 }, + { 0xbaba, 0x0000 }, + { 0xbabb, 0x0000 }, + { 0xbabc, 0x0000 }, + { 0xbabd, 0x0000 }, + { 0xbabe, 0x0000 }, + { 0xbabf, 0x0000 }, + { 0xbac0, 0x0000 }, + { 0xbac1, 0x0000 }, + { 0xbac2, 0x0000 }, + { 0xbac3, 0x0000 }, + { 0xbac4, 0x0000 }, + { 0xbac5, 0x0000 }, + { 0xbac6, 0x0000 }, + { 0xbac7, 0x0000 }, + { 0xbac8, 0x0000 }, + { 0xbac9, 0x0000 }, + { 0xbaca, 0x0000 }, + { 0xbacb, 0x0000 }, + { 0xbacc, 0x0000 }, + { 0xbacd, 0x0000 }, + { 0xbace, 0x0000 }, + { 0xbacf, 0x0000 }, + { 0xbad0, 0x0000 }, + { 0xbad1, 0x0000 }, + { 0xbad2, 0x0000 }, + { 0xbad3, 0x0000 }, + { 0xbad4, 0x0000 }, + { 0xbad5, 0x0000 }, + { 0xbad6, 0x0000 }, + { 0xbad7, 0x0000 }, + { 0xbad8, 0x0000 }, + { 0xbad9, 0x0000 }, + { 0xbada, 0x0000 }, + { 0xbadb, 0x0000 }, + { 0xbadc, 0x0000 }, + { 0xbadd, 0x0000 }, + { 0xbade, 0x0000 }, + { 0xbadf, 0x0000 }, + { 0xbae0, 0x0000 }, + { 0xbae1, 0x0000 }, + { 0xbae2, 0x0000 }, + { 0xbae3, 0x0000 }, + { 0xbae4, 0x0000 }, + { 0xbae5, 0x0000 }, + { 0xbae6, 0x0000 }, + { 0xbae7, 0x0000 }, + { 0xbae8, 0x0000 }, + { 0xbae9, 0x0000 }, + { 0xbaea, 0x0000 }, + { 0xbaeb, 0x0000 }, + { 0xbaec, 0x0000 }, + { 0xbaed, 0x0000 }, + { 0xbaee, 0x0000 }, + { 0xbaef, 0x0000 }, + { 0xbaf0, 0x0000 }, + { 0xbaf1, 0x0000 }, + { 0xbaf2, 0x0000 }, + { 0xbaf3, 0x0000 }, + { 0xbaf4, 0x0000 }, + { 0xbaf5, 0x0000 }, + { 0xbaf6, 0x0000 }, + { 0xbaf7, 0x0000 }, + { 0xbaf8, 0x0000 }, + { 0xbaf9, 0x0000 }, + { 0xbafa, 0x0000 }, + { 0xbafb, 0x0000 }, + { 0xbafc, 0x0000 }, + { 0xbafd, 0x0000 }, + { 0xbafe, 0x0000 }, + { 0xbaff, 0x0000 }, + { 0xbb00, 0x0000 }, + { 0xbb01, 0x0000 }, + { 0xbb02, 0x0000 }, + { 0xbb03, 0x0000 }, + { 0xbb04, 0x0000 }, + { 0xbb05, 0x0000 }, + { 0xbb06, 0x0000 }, + { 0xbb07, 0x0000 }, + { 0xbb08, 0x0000 }, + { 0xbb09, 0x0000 }, + { 0xbb0a, 0x0000 }, + { 0xbb0b, 0x0000 }, + { 0xbb0c, 0x0000 }, + { 0xbb0d, 0x0000 }, + { 0xbb0e, 0x0000 }, + { 0xbb0f, 0x0000 }, + { 0xbb10, 0x0000 }, + { 0xbb11, 0x0000 }, + { 0xbb12, 0x0000 }, + { 0xbb13, 0x0000 }, + { 0xbb14, 0x0000 }, + { 0xbb15, 0x0000 }, + { 0xbb16, 0x0000 }, + { 0xbb17, 0x0000 }, + { 0xbb18, 0x0000 }, + { 0xbb19, 0x0000 }, + { 0xbb1a, 0x0000 }, + { 0xbb1b, 0x0000 }, + { 0xbb1c, 0x0000 }, + { 0xbb1d, 0x0000 }, + { 0xbb1e, 0x0000 }, + { 0xbb1f, 0x0000 }, + { 0xbb20, 0x0000 }, + { 0xbb21, 0x0000 }, + { 0xbb22, 0x0000 }, + { 0xbb23, 0x0000 }, + { 0xbb24, 0x0000 }, + { 0xbb25, 0x0000 }, + { 0xbb26, 0x0000 }, + { 0xbb27, 0x0000 }, + { 0xbb28, 0x0000 }, + { 0xbb29, 0x0000 }, + { 0xbb2a, 0x0000 }, + { 0xbb2b, 0x0000 }, + { 0xbb2c, 0x0000 }, + { 0xbb2d, 0x0000 }, + { 0xbb2e, 0x0000 }, + { 0xbb2f, 0x0000 }, + { 0xbb30, 0x0000 }, + { 0xbb31, 0x0000 }, + { 0xbb32, 0x0000 }, + { 0xbb33, 0x0000 }, + { 0xbb34, 0x0000 }, + { 0xbb35, 0x0000 }, + { 0xbb36, 0x0000 }, + { 0xbb37, 0x0000 }, + { 0xbb38, 0x0000 }, + { 0xbb39, 0x0000 }, + { 0xbb3a, 0x0000 }, + { 0xbb3b, 0x0000 }, + { 0xbb3c, 0x0000 }, + { 0xbb3d, 0x0000 }, + { 0xbb3e, 0x0000 }, + { 0xbb3f, 0x0000 }, + { 0xbb40, 0x0000 }, + { 0xbb41, 0x0000 }, + { 0xbb42, 0x0000 }, + { 0xbb43, 0x0000 }, + { 0xbb44, 0x0000 }, + { 0xbb45, 0x0000 }, + { 0xbb46, 0x0000 }, + { 0xbb47, 0x0000 }, + { 0xbb48, 0x0000 }, + { 0xbb49, 0x0000 }, + { 0xbb4a, 0x0000 }, + { 0xbb4b, 0x0000 }, + { 0xbb4c, 0x0000 }, + { 0xbb4d, 0x0000 }, + { 0xbb4e, 0x0000 }, + { 0xbb4f, 0x0000 }, + { 0xbb50, 0x0000 }, + { 0xbb51, 0x0000 }, + { 0xbb52, 0x0000 }, + { 0xbb53, 0x0000 }, + { 0xbb54, 0x0000 }, + { 0xbb55, 0x0000 }, + { 0xbb56, 0x0000 }, + { 0xbb57, 0x0000 }, + { 0xbb58, 0x0000 }, + { 0xbb59, 0x0000 }, + { 0xbb5a, 0x0000 }, + { 0xbb5b, 0x0000 }, + { 0xbb5c, 0x0000 }, + { 0xbb5d, 0x0000 }, + { 0xbb5e, 0x0000 }, + { 0xbb5f, 0x0000 }, + { 0xbb60, 0x0000 }, + { 0xbb61, 0x0000 }, + { 0xbb62, 0x0000 }, + { 0xbb63, 0x0000 }, + { 0xbb64, 0x0000 }, + { 0xbb65, 0x0000 }, + { 0xbb66, 0x0000 }, + { 0xbb67, 0x0000 }, + { 0xbb68, 0x0000 }, + { 0xbb69, 0x0000 }, + { 0xbb6a, 0x0000 }, + { 0xbb6b, 0x0000 }, + { 0xbb6c, 0x0000 }, + { 0xbb6d, 0x0000 }, + { 0xbb6e, 0x0000 }, + { 0xbb6f, 0x0000 }, + { 0xbb70, 0x0000 }, + { 0xbb71, 0x0000 }, + { 0xbb72, 0x0000 }, + { 0xbb73, 0x0000 }, + { 0xbb74, 0x0000 }, + { 0xbb75, 0x0000 }, + { 0xbb76, 0x0000 }, + { 0xbb77, 0x0000 }, + { 0xbb78, 0x0000 }, + { 0xbb79, 0x0000 }, + { 0xbb7a, 0x0000 }, + { 0xbb7b, 0x0000 }, + { 0xbb7c, 0x0000 }, + { 0xbb7d, 0x0000 }, + { 0xbb7e, 0x0000 }, + { 0xbb7f, 0x0000 }, + { 0xbb80, 0x0000 }, + { 0xbb81, 0x0000 }, + { 0xbb82, 0x0000 }, + { 0xbb83, 0x0000 }, + { 0xbb84, 0x0000 }, + { 0xbb85, 0x0000 }, + { 0xbb86, 0x0000 }, + { 0xbb87, 0x0000 }, + { 0xbb88, 0x0000 }, + { 0xbb89, 0x0000 }, + { 0xbb8a, 0x0000 }, + { 0xbb8b, 0x0000 }, + { 0xbb8c, 0x0000 }, + { 0xbb8d, 0x0000 }, + { 0xbb8e, 0x0000 }, + { 0xbb8f, 0x0000 }, + { 0xbb90, 0x0000 }, + { 0xbb91, 0x0000 }, + { 0xbb92, 0x0000 }, + { 0xbb93, 0x0000 }, + { 0xbb94, 0x0000 }, + { 0xbb95, 0x0000 }, + { 0xbb96, 0x0000 }, + { 0xbb97, 0x0000 }, + { 0xbb98, 0x0000 }, + { 0xbb99, 0x0000 }, + { 0xbb9a, 0x0000 }, + { 0xbb9b, 0x0000 }, + { 0xbb9c, 0x0000 }, + { 0xbb9d, 0x0000 }, + { 0xbb9e, 0x0000 }, + { 0xbb9f, 0x0000 }, + { 0xbba0, 0x0000 }, + { 0xbba1, 0x0000 }, + { 0xbba2, 0x0000 }, + { 0xbba3, 0x0000 }, + { 0xbba4, 0x0000 }, + { 0xbba5, 0x0000 }, + { 0xbba6, 0x0000 }, + { 0xbba7, 0x0000 }, + { 0xbba8, 0x0000 }, + { 0xbba9, 0x0000 }, + { 0xbbaa, 0x0000 }, + { 0xbbab, 0x0000 }, + { 0xbbac, 0x0000 }, + { 0xbbad, 0x0000 }, + { 0xbbae, 0x0000 }, + { 0xbbaf, 0x0000 }, + { 0xbbb0, 0x0000 }, + { 0xbbb1, 0x0000 }, + { 0xbbb2, 0x0000 }, + { 0xbbb3, 0x0000 }, + { 0xbbb4, 0x0000 }, + { 0xbbb5, 0x0000 }, + { 0xbbb6, 0x0000 }, + { 0xbbb7, 0x0000 }, + { 0xbbb8, 0x0000 }, + { 0xbbb9, 0x0000 }, + { 0xbbba, 0x0000 }, + { 0xbbbb, 0x0000 }, + { 0xbbbc, 0x0000 }, + { 0xbbbd, 0x0000 }, + { 0xbbbe, 0x0000 }, + { 0xbbbf, 0x0000 }, + { 0xbbc0, 0x0000 }, + { 0xbbc1, 0x0000 }, + { 0xbbc2, 0x0000 }, + { 0xbbc3, 0x0000 }, + { 0xbbc4, 0x0000 }, + { 0xbbc5, 0x0000 }, + { 0xbbc6, 0x0000 }, + { 0xbbc7, 0x0000 }, + { 0xbbc8, 0x0000 }, + { 0xbbc9, 0x0000 }, + { 0xbbca, 0x0000 }, + { 0xbbcb, 0x0000 }, + { 0xbbcc, 0x0000 }, + { 0xbbcd, 0x0000 }, + { 0xbbce, 0x0000 }, + { 0xbbcf, 0x0000 }, + { 0xbbd0, 0x0000 }, + { 0xbbd1, 0x0000 }, + { 0xbbd2, 0x0000 }, + { 0xbbd3, 0x0000 }, + { 0xbbd4, 0x0000 }, + { 0xbbd5, 0x0000 }, + { 0xbbd6, 0x0000 }, + { 0xbbd7, 0x0000 }, + { 0xbbd8, 0x0000 }, + { 0xbbd9, 0x0000 }, + { 0xbbda, 0x0000 }, + { 0xbbdb, 0x0000 }, + { 0xbbdc, 0x0000 }, + { 0xbbdd, 0x0000 }, + { 0xbbde, 0x0000 }, + { 0xbbdf, 0x0000 }, + { 0xbbe0, 0x0000 }, + { 0xbbe1, 0x0000 }, + { 0xbbe2, 0x0000 }, + { 0xbbe3, 0x0000 }, + { 0xbbe4, 0x0000 }, + { 0xbbe5, 0x0000 }, + { 0xbbe6, 0x0000 }, + { 0xbbe7, 0x0000 }, + { 0xbbe8, 0x0000 }, + { 0xbbe9, 0x0000 }, + { 0xbbea, 0x0000 }, + { 0xbbeb, 0x0000 }, + { 0xbbec, 0x0000 }, + { 0xbbed, 0x0000 }, + { 0xbbee, 0x0000 }, + { 0xbbef, 0x0000 }, + { 0xbbf0, 0x0000 }, + { 0xbbf1, 0x0000 }, + { 0xbbf2, 0x0000 }, + { 0xbbf3, 0x0000 }, + { 0xbbf4, 0x0000 }, + { 0xbbf5, 0x0000 }, + { 0xbbf6, 0x0000 }, + { 0xbbf7, 0x0000 }, + { 0xbbf8, 0x0000 }, + { 0xbbf9, 0x0000 }, + { 0xbbfa, 0x0000 }, + { 0xbbfb, 0x0000 }, + { 0xbbfc, 0x0000 }, + { 0xbbfd, 0x0000 }, + { 0xbbfe, 0x0000 }, + { 0xbbff, 0x0000 }, + { 0xbc00, 0x0000 }, + { 0xbc01, 0x0000 }, + { 0xbc02, 0x0000 }, + { 0xbc03, 0x0000 }, + { 0xbc04, 0x0000 }, + { 0xbc05, 0x0000 }, + { 0xbc06, 0x0000 }, + { 0xbc07, 0x0000 }, + { 0xbc08, 0x0000 }, + { 0xbc09, 0x0000 }, + { 0xbc0a, 0x0000 }, + { 0xbc0b, 0x0000 }, + { 0xbc0c, 0x0000 }, + { 0xbc0d, 0x0000 }, + { 0xbc0e, 0x0000 }, + { 0xbc0f, 0x0000 }, + { 0xbc10, 0x0000 }, + { 0xbc11, 0x0000 }, + { 0xbc12, 0x0000 }, + { 0xbc13, 0x0000 }, + { 0xbc14, 0x0000 }, + { 0xbc15, 0x0000 }, + { 0xbc16, 0x0000 }, + { 0xbc17, 0x0000 }, + { 0xbc18, 0x0000 }, + { 0xbc19, 0x0000 }, + { 0xbc1a, 0x0000 }, + { 0xbc1b, 0x0000 }, + { 0xbc1c, 0x0000 }, + { 0xbc1d, 0x0000 }, + { 0xbc1e, 0x0000 }, + { 0xbc1f, 0x0000 }, + { 0xbc20, 0x0000 }, + { 0xbc21, 0x0000 }, + { 0xbc22, 0x0000 }, + { 0xbc23, 0x0000 }, + { 0xbc24, 0x0000 }, + { 0xbc25, 0x0000 }, + { 0xbc26, 0x0000 }, + { 0xbc27, 0x0000 }, + { 0xbc28, 0x0000 }, + { 0xbc29, 0x0000 }, + { 0xbc2a, 0x0000 }, + { 0xbc2b, 0x0000 }, + { 0xbc2c, 0x0000 }, + { 0xbc2d, 0x0000 }, + { 0xbc2e, 0x0000 }, + { 0xbc2f, 0x0000 }, + { 0xbc30, 0x0000 }, + { 0xbc31, 0x0000 }, + { 0xbc32, 0x0000 }, + { 0xbc33, 0x0000 }, + { 0xbc34, 0x0000 }, + { 0xbc35, 0x0000 }, + { 0xbc36, 0x0000 }, + { 0xbc37, 0x0000 }, + { 0xbc38, 0x0000 }, + { 0xbc39, 0x0000 }, + { 0xbc3a, 0x0000 }, + { 0xbc3b, 0x0000 }, + { 0xbc3c, 0x0000 }, + { 0xbc3d, 0x0000 }, + { 0xbc3e, 0x0000 }, + { 0xbc3f, 0x0000 }, + { 0xbc40, 0x0000 }, + { 0xbc41, 0x0000 }, + { 0xbc42, 0x0000 }, + { 0xbc43, 0x0000 }, + { 0xbc44, 0x0000 }, + { 0xbc45, 0x0000 }, + { 0xbc46, 0x0000 }, + { 0xbc47, 0x0000 }, + { 0xbc48, 0x0000 }, + { 0xbc49, 0x0000 }, + { 0xbc4a, 0x0000 }, + { 0xbc4b, 0x0000 }, + { 0xbc4c, 0x0000 }, + { 0xbc4d, 0x0000 }, + { 0xbc4e, 0x0000 }, + { 0xbc4f, 0x0000 }, + { 0xbc50, 0x0000 }, + { 0xbc51, 0x0000 }, + { 0xbc52, 0x0000 }, + { 0xbc53, 0x0000 }, + { 0xbc54, 0x0000 }, + { 0xbc55, 0x0000 }, + { 0xbc56, 0x0000 }, + { 0xbc57, 0x0000 }, + { 0xbc58, 0x0000 }, + { 0xbc59, 0x0000 }, + { 0xbc5a, 0x0000 }, + { 0xbc5b, 0x0000 }, + { 0xbc5c, 0x0000 }, + { 0xbc5d, 0x0000 }, + { 0xbc5e, 0x0000 }, + { 0xbc5f, 0x0000 }, + { 0xbc60, 0x0000 }, + { 0xbc61, 0x0000 }, + { 0xbc62, 0x0000 }, + { 0xbc63, 0x0000 }, + { 0xbc64, 0x0000 }, + { 0xbc65, 0x0000 }, + { 0xbc66, 0x0000 }, + { 0xbc67, 0x0000 }, + { 0xbc68, 0x0000 }, + { 0xbc69, 0x0000 }, + { 0xbc6a, 0x0000 }, + { 0xbc6b, 0x0000 }, + { 0xbc6c, 0x0000 }, + { 0xbc6d, 0x0000 }, + { 0xbc6e, 0x0000 }, + { 0xbc6f, 0x0000 }, + { 0xbc70, 0x0000 }, + { 0xbc71, 0x0000 }, + { 0xbc72, 0x0000 }, + { 0xbc73, 0x0000 }, + { 0xbc74, 0x0000 }, + { 0xbc75, 0x0000 }, + { 0xbc76, 0x0000 }, + { 0xbc77, 0x0000 }, + { 0xbc78, 0x0000 }, + { 0xbc79, 0x0000 }, + { 0xbc7a, 0x0000 }, + { 0xbc7b, 0x0000 }, + { 0xbc7c, 0x0000 }, + { 0xbc7d, 0x0000 }, + { 0xbc7e, 0x0000 }, + { 0xbc7f, 0x0000 }, + { 0xbc80, 0x0000 }, + { 0xbc81, 0x0000 }, + { 0xbc82, 0x0000 }, + { 0xbc83, 0x0000 }, + { 0xbc84, 0x0000 }, + { 0xbc85, 0x0000 }, + { 0xbc86, 0x0000 }, + { 0xbc87, 0x0000 }, + { 0xbc88, 0x0000 }, + { 0xbc89, 0x0000 }, + { 0xbc8a, 0x0000 }, + { 0xbc8b, 0x0000 }, + { 0xbc8c, 0x0000 }, + { 0xbc8d, 0x0000 }, + { 0xbc8e, 0x0000 }, + { 0xbc8f, 0x0000 }, + { 0xbc90, 0x0000 }, + { 0xbc91, 0x0000 }, + { 0xbc92, 0x0000 }, + { 0xbc93, 0x0000 }, + { 0xbc94, 0x0000 }, + { 0xbc95, 0x0000 }, + { 0xbc96, 0x0000 }, + { 0xbc97, 0x0000 }, + { 0xbc98, 0x0000 }, + { 0xbc99, 0x0000 }, + { 0xbc9a, 0x0000 }, + { 0xbc9b, 0x0000 }, + { 0xbc9c, 0x0000 }, + { 0xbc9d, 0x0000 }, + { 0xbc9e, 0x0000 }, + { 0xbc9f, 0x0000 }, + { 0xbca0, 0x0000 }, + { 0xbca1, 0x0000 }, + { 0xbca2, 0x0000 }, + { 0xbca3, 0x0000 }, + { 0xbca4, 0x0000 }, + { 0xbca5, 0x0000 }, + { 0xbca6, 0x0000 }, + { 0xbca7, 0x0000 }, + { 0xbca8, 0x0000 }, + { 0xbca9, 0x0000 }, + { 0xbcaa, 0x0000 }, + { 0xbcab, 0x0000 }, + { 0xbcac, 0x0000 }, + { 0xbcad, 0x0000 }, + { 0xbcae, 0x0000 }, + { 0xbcaf, 0x0000 }, + { 0xbcb0, 0x0000 }, + { 0xbcb1, 0x0000 }, + { 0xbcb2, 0x0000 }, + { 0xbcb3, 0x0000 }, + { 0xbcb4, 0x0000 }, + { 0xbcb5, 0x0000 }, + { 0xbcb6, 0x0000 }, + { 0xbcb7, 0x0000 }, + { 0xbcb8, 0x0000 }, + { 0xbcb9, 0x0000 }, + { 0xbcba, 0x0000 }, + { 0xbcbb, 0x0000 }, + { 0xbcbc, 0x0000 }, + { 0xbcbd, 0x0000 }, + { 0xbcbe, 0x0000 }, + { 0xbcbf, 0x0000 }, + { 0xbcc0, 0x0000 }, + { 0xbcc1, 0x0000 }, + { 0xbcc2, 0x0000 }, + { 0xbcc3, 0x0000 }, + { 0xbcc4, 0x0000 }, + { 0xbcc5, 0x0000 }, + { 0xbcc6, 0x0000 }, + { 0xbcc7, 0x0000 }, + { 0xbcc8, 0x0000 }, + { 0xbcc9, 0x0000 }, + { 0xbcca, 0x0000 }, + { 0xbccb, 0x0000 }, + { 0xbccc, 0x0000 }, + { 0xbccd, 0x0000 }, + { 0xbcce, 0x0000 }, + { 0xbccf, 0x0000 }, + { 0xbcd0, 0x0000 }, + { 0xbcd1, 0x0000 }, + { 0xbcd2, 0x0000 }, + { 0xbcd3, 0x0000 }, + { 0xbcd4, 0x0000 }, + { 0xbcd5, 0x0000 }, + { 0xbcd6, 0x0000 }, + { 0xbcd7, 0x0000 }, + { 0xbcd8, 0x0000 }, + { 0xbcd9, 0x0000 }, + { 0xbcda, 0x0000 }, + { 0xbcdb, 0x0000 }, + { 0xbcdc, 0x0000 }, + { 0xbcdd, 0x0000 }, + { 0xbcde, 0x0000 }, + { 0xbcdf, 0x0000 }, + { 0xbce0, 0x0000 }, + { 0xbce1, 0x0000 }, + { 0xbce2, 0x0000 }, + { 0xbce3, 0x0000 }, + { 0xbce4, 0x0000 }, + { 0xbce5, 0x0000 }, + { 0xbce6, 0x0000 }, + { 0xbce7, 0x0000 }, + { 0xbce8, 0x0000 }, + { 0xbce9, 0x0000 }, + { 0xbcea, 0x0000 }, + { 0xbceb, 0x0000 }, + { 0xbcec, 0x0000 }, + { 0xbced, 0x0000 }, + { 0xbcee, 0x0000 }, + { 0xbcef, 0x0000 }, + { 0xbcf0, 0x0000 }, + { 0xbcf1, 0x0000 }, + { 0xbcf2, 0x0000 }, + { 0xbcf3, 0x0000 }, + { 0xbcf4, 0x0000 }, + { 0xbcf5, 0x0000 }, + { 0xbcf6, 0x0000 }, + { 0xbcf7, 0x0000 }, + { 0xbcf8, 0x0000 }, + { 0xbcf9, 0x0000 }, + { 0xbcfa, 0x0000 }, + { 0xbcfb, 0x0000 }, + { 0xbcfc, 0x0000 }, + { 0xbcfd, 0x0000 }, + { 0xbcfe, 0x0000 }, + { 0xbcff, 0x0000 }, + { 0xbd00, 0x0000 }, + { 0xbd01, 0x0000 }, + { 0xbd02, 0x0000 }, + { 0xbd03, 0x0000 }, + { 0xbd04, 0x0000 }, + { 0xbd05, 0x0000 }, + { 0xbd06, 0x0000 }, + { 0xbd07, 0x0000 }, + { 0xbd08, 0x0000 }, + { 0xbd09, 0x0000 }, + { 0xbd0a, 0x0000 }, + { 0xbd0b, 0x0000 }, + { 0xbd0c, 0x0000 }, + { 0xbd0d, 0x0000 }, + { 0xbd0e, 0x0000 }, + { 0xbd0f, 0x0000 }, + { 0xbd10, 0x0000 }, + { 0xbd11, 0x0000 }, + { 0xbd12, 0x0000 }, + { 0xbd13, 0x0000 }, + { 0xbd14, 0x0000 }, + { 0xbd15, 0x0000 }, + { 0xbd16, 0x0000 }, + { 0xbd17, 0x0000 }, + { 0xbd18, 0x0000 }, + { 0xbd19, 0x0000 }, + { 0xbd1a, 0x0000 }, + { 0xbd1b, 0x0000 }, + { 0xbd1c, 0x0000 }, + { 0xbd1d, 0x0000 }, + { 0xbd1e, 0x0000 }, + { 0xbd1f, 0x0000 }, + { 0xbd20, 0x0000 }, + { 0xbd21, 0x0000 }, + { 0xbd22, 0x0000 }, + { 0xbd23, 0x0000 }, + { 0xbd24, 0x0000 }, + { 0xbd25, 0x0000 }, + { 0xbd26, 0x0000 }, + { 0xbd27, 0x0000 }, + { 0xbd28, 0x0000 }, + { 0xbd29, 0x0000 }, + { 0xbd2a, 0x0000 }, + { 0xbd2b, 0x0000 }, + { 0xbd2c, 0x0000 }, + { 0xbd2d, 0x0000 }, + { 0xbd2e, 0x0000 }, + { 0xbd2f, 0x0000 }, + { 0xbd30, 0x0000 }, + { 0xbd31, 0x0000 }, + { 0xbd32, 0x0000 }, + { 0xbd33, 0x0000 }, + { 0xbd34, 0x0000 }, + { 0xbd35, 0x0000 }, + { 0xbd36, 0x0000 }, + { 0xbd37, 0x0000 }, + { 0xbd38, 0x0000 }, + { 0xbd39, 0x0000 }, + { 0xbd3a, 0x0000 }, + { 0xbd3b, 0x0000 }, + { 0xbd3c, 0x0000 }, + { 0xbd3d, 0x0000 }, + { 0xbd3e, 0x0000 }, + { 0xbd3f, 0x0000 }, + { 0xbd40, 0x0000 }, + { 0xbd41, 0x0000 }, + { 0xbd42, 0x0000 }, + { 0xbd43, 0x0000 }, + { 0xbd44, 0x0000 }, + { 0xbd45, 0x0000 }, + { 0xbd46, 0x0000 }, + { 0xbd47, 0x0000 }, + { 0xbd48, 0x0000 }, + { 0xbd49, 0x0000 }, + { 0xbd4a, 0x0000 }, + { 0xbd4b, 0x0000 }, + { 0xbd4c, 0x0000 }, + { 0xbd4d, 0x0000 }, + { 0xbd4e, 0x0000 }, + { 0xbd4f, 0x0000 }, + { 0xbd50, 0x0000 }, + { 0xbd51, 0x0000 }, + { 0xbd52, 0x0000 }, + { 0xbd53, 0x0000 }, + { 0xbd54, 0x0000 }, + { 0xbd55, 0x0000 }, + { 0xbd56, 0x0000 }, + { 0xbd57, 0x0000 }, + { 0xbd58, 0x0000 }, + { 0xbd59, 0x0000 }, + { 0xbd5a, 0x0000 }, + { 0xbd5b, 0x0000 }, + { 0xbd5c, 0x0000 }, + { 0xbd5d, 0x0000 }, + { 0xbd5e, 0x0000 }, + { 0xbd5f, 0x0000 }, + { 0xbd60, 0x0000 }, + { 0xbd61, 0x0000 }, + { 0xbd62, 0x0000 }, + { 0xbd63, 0x0000 }, + { 0xbd64, 0x0000 }, + { 0xbd65, 0x0000 }, + { 0xbd66, 0x0000 }, + { 0xbd67, 0x0000 }, + { 0xbd68, 0x0000 }, + { 0xbd69, 0x0000 }, + { 0xbd6a, 0x0000 }, + { 0xbd6b, 0x0000 }, + { 0xbd6c, 0x0000 }, + { 0xbd6d, 0x0000 }, + { 0xbd6e, 0x0000 }, + { 0xbd6f, 0x0000 }, + { 0xbd70, 0x0000 }, + { 0xbd71, 0x0000 }, + { 0xbd72, 0x0000 }, + { 0xbd73, 0x0000 }, + { 0xbd74, 0x0000 }, + { 0xbd75, 0x0000 }, + { 0xbd76, 0x0000 }, + { 0xbd77, 0x0000 }, + { 0xbd78, 0x0000 }, + { 0xbd79, 0x0000 }, + { 0xbd7a, 0x0000 }, + { 0xbd7b, 0x0000 }, + { 0xbd7c, 0x0000 }, + { 0xbd7d, 0x0000 }, + { 0xbd7e, 0x0000 }, + { 0xbd7f, 0x0000 }, + { 0xbd80, 0x0000 }, + { 0xbd81, 0x0000 }, + { 0xbd82, 0x0000 }, + { 0xbd83, 0x0000 }, + { 0xbd84, 0x0000 }, + { 0xbd85, 0x0000 }, + { 0xbd86, 0x0000 }, + { 0xbd87, 0x0000 }, + { 0xbd88, 0x0000 }, + { 0xbd89, 0x0000 }, + { 0xbd8a, 0x0000 }, + { 0xbd8b, 0x0000 }, + { 0xbd8c, 0x0000 }, + { 0xbd8d, 0x0000 }, + { 0xbd8e, 0x0000 }, + { 0xbd8f, 0x0000 }, + { 0xbd90, 0x0000 }, + { 0xbd91, 0x0000 }, + { 0xbd92, 0x0000 }, + { 0xbd93, 0x0000 }, + { 0xbd94, 0x0000 }, + { 0xbd95, 0x0000 }, + { 0xbd96, 0x0000 }, + { 0xbd97, 0x0000 }, + { 0xbd98, 0x0000 }, + { 0xbd99, 0x0000 }, + { 0xbd9a, 0x0000 }, + { 0xbd9b, 0x0000 }, + { 0xbd9c, 0x0000 }, + { 0xbd9d, 0x0000 }, + { 0xbd9e, 0x0000 }, + { 0xbd9f, 0x0000 }, + { 0xbda0, 0x0000 }, + { 0xbda1, 0x0000 }, + { 0xbda2, 0x0000 }, + { 0xbda3, 0x0000 }, + { 0xbda4, 0x0000 }, + { 0xbda5, 0x0000 }, + { 0xbda6, 0x0000 }, + { 0xbda7, 0x0000 }, + { 0xbda8, 0x0000 }, + { 0xbda9, 0x0000 }, + { 0xbdaa, 0x0000 }, + { 0xbdab, 0x0000 }, + { 0xbdac, 0x0000 }, + { 0xbdad, 0x0000 }, + { 0xbdae, 0x0000 }, + { 0xbdaf, 0x0000 }, + { 0xbdb0, 0x0000 }, + { 0xbdb1, 0x0000 }, + { 0xbdb2, 0x0000 }, + { 0xbdb3, 0x0000 }, + { 0xbdb4, 0x0000 }, + { 0xbdb5, 0x0000 }, + { 0xbdb6, 0x0000 }, + { 0xbdb7, 0x0000 }, + { 0xbdb8, 0x0000 }, + { 0xbdb9, 0x0000 }, + { 0xbdba, 0x0000 }, + { 0xbdbb, 0x0000 }, + { 0xbdbc, 0x0000 }, + { 0xbdbd, 0x0000 }, + { 0xbdbe, 0x0000 }, + { 0xbdbf, 0x0000 }, + { 0xbdc0, 0x0000 }, + { 0xbdc1, 0x0000 }, + { 0xbdc2, 0x0000 }, + { 0xbdc3, 0x0000 }, + { 0xbdc4, 0x0000 }, + { 0xbdc5, 0x0000 }, + { 0xbdc6, 0x0000 }, + { 0xbdc7, 0x0000 }, + { 0xbdc8, 0x0000 }, + { 0xbdc9, 0x0000 }, + { 0xbdca, 0x0000 }, + { 0xbdcb, 0x0000 }, + { 0xbdcc, 0x0000 }, + { 0xbdcd, 0x0000 }, + { 0xbdce, 0x0000 }, + { 0xbdcf, 0x0000 }, + { 0xbdd0, 0x0000 }, + { 0xbdd1, 0x0000 }, + { 0xbdd2, 0x0000 }, + { 0xbdd3, 0x0000 }, + { 0xbdd4, 0x0000 }, + { 0xbdd5, 0x0000 }, + { 0xbdd6, 0x0000 }, + { 0xbdd7, 0x0000 }, + { 0xbdd8, 0x0000 }, + { 0xbdd9, 0x0000 }, + { 0xbdda, 0x0000 }, + { 0xbddb, 0x0000 }, + { 0xbddc, 0x0000 }, + { 0xbddd, 0x0000 }, + { 0xbdde, 0x0000 }, + { 0xbddf, 0x0000 }, + { 0xbde0, 0x0000 }, + { 0xbde1, 0x0000 }, + { 0xbde2, 0x0000 }, + { 0xbde3, 0x0000 }, + { 0xbde4, 0x0000 }, + { 0xbde5, 0x0000 }, + { 0xbde6, 0x0000 }, + { 0xbde7, 0x0000 }, + { 0xbde8, 0x0000 }, + { 0xbde9, 0x0000 }, + { 0xbdea, 0x0000 }, + { 0xbdeb, 0x0000 }, + { 0xbdec, 0x0000 }, + { 0xbded, 0x0000 }, + { 0xbdee, 0x0000 }, + { 0xbdef, 0x0000 }, + { 0xbdf0, 0x0000 }, + { 0xbdf1, 0x0000 }, + { 0xbdf2, 0x0000 }, + { 0xbdf3, 0x0000 }, + { 0xbdf4, 0x0000 }, + { 0xbdf5, 0x0000 }, + { 0xbdf6, 0x0000 }, + { 0xbdf7, 0x0000 }, + { 0xbdf8, 0x0000 }, + { 0xbdf9, 0x0000 }, + { 0xbdfa, 0x0000 }, + { 0xbdfb, 0x0000 }, + { 0xbdfc, 0x0000 }, + { 0xbdfd, 0x0000 }, + { 0xbdfe, 0x0000 }, + { 0xbdff, 0x0000 }, + { 0xbe00, 0x0000 }, + { 0xbe01, 0x0000 }, + { 0xbe02, 0x0000 }, + { 0xbe03, 0x0000 }, + { 0xbe04, 0x0000 }, + { 0xbe05, 0x0000 }, + { 0xbe06, 0x0000 }, + { 0xbe07, 0x0000 }, + { 0xbe08, 0x0000 }, + { 0xbe09, 0x0000 }, + { 0xbe0a, 0x0000 }, + { 0xbe0b, 0x0000 }, + { 0xbe0c, 0x0000 }, + { 0xbe0d, 0x0000 }, + { 0xbe0e, 0x0000 }, + { 0xbe0f, 0x0000 }, + { 0xbe10, 0x0000 }, + { 0xbe11, 0x0000 }, + { 0xbe12, 0x0000 }, + { 0xbe13, 0x0000 }, + { 0xbe14, 0x0000 }, + { 0xbe15, 0x0000 }, + { 0xbe16, 0x0000 }, + { 0xbe17, 0x0000 }, + { 0xbe18, 0x0000 }, + { 0xbe19, 0x0000 }, + { 0xbe1a, 0x0000 }, + { 0xbe1b, 0x0000 }, + { 0xbe1c, 0x0000 }, + { 0xbe1d, 0x0000 }, + { 0xbe1e, 0x0000 }, + { 0xbe1f, 0x0000 }, + { 0xbe20, 0x0000 }, + { 0xbe21, 0x0000 }, + { 0xbe22, 0x0000 }, + { 0xbe23, 0x0000 }, + { 0xbe24, 0x0000 }, + { 0xbe25, 0x0000 }, + { 0xbe26, 0x0000 }, + { 0xbe27, 0x0000 }, + { 0xbe28, 0x0000 }, + { 0xbe29, 0x0000 }, + { 0xbe2a, 0x0000 }, + { 0xbe2b, 0x0000 }, + { 0xbe2c, 0x0000 }, + { 0xbe2d, 0x0000 }, + { 0xbe2e, 0x0000 }, + { 0xbe2f, 0x0000 }, + { 0xbe30, 0x0000 }, + { 0xbe31, 0x0000 }, + { 0xbe32, 0x0000 }, + { 0xbe33, 0x0000 }, + { 0xbe34, 0x0000 }, + { 0xbe35, 0x0000 }, + { 0xbe36, 0x0000 }, + { 0xbe37, 0x0000 }, + { 0xbe38, 0x0000 }, + { 0xbe39, 0x0000 }, + { 0xbe3a, 0x0000 }, + { 0xbe3b, 0x0000 }, + { 0xbe3c, 0x0000 }, + { 0xbe3d, 0x0000 }, + { 0xbe3e, 0x0000 }, + { 0xbe3f, 0x0000 }, + { 0xbe40, 0x0000 }, + { 0xbe41, 0x0000 }, + { 0xbe42, 0x0000 }, + { 0xbe43, 0x0000 }, + { 0xbe44, 0x0000 }, + { 0xbe45, 0x0000 }, + { 0xbe46, 0x0000 }, + { 0xbe47, 0x0000 }, + { 0xbe48, 0x0000 }, + { 0xbe49, 0x0000 }, + { 0xbe4a, 0x0000 }, + { 0xbe4b, 0x0000 }, + { 0xbe4c, 0x0000 }, + { 0xbe4d, 0x0000 }, + { 0xbe4e, 0x0000 }, + { 0xbe4f, 0x0000 }, + { 0xbe50, 0x0000 }, + { 0xbe51, 0x0000 }, + { 0xbe52, 0x0000 }, + { 0xbe53, 0x0000 }, + { 0xbe54, 0x0000 }, + { 0xbe55, 0x0000 }, + { 0xbe56, 0x0000 }, + { 0xbe57, 0x0000 }, + { 0xbe58, 0x0000 }, + { 0xbe59, 0x0000 }, + { 0xbe5a, 0x0000 }, + { 0xbe5b, 0x0000 }, + { 0xbe5c, 0x0000 }, + { 0xbe5d, 0x0000 }, + { 0xbe5e, 0x0000 }, + { 0xbe5f, 0x0000 }, + { 0xbe60, 0x0000 }, + { 0xbe61, 0x0000 }, + { 0xbe62, 0x0000 }, + { 0xbe63, 0x0000 }, + { 0xbe64, 0x0000 }, + { 0xbe65, 0x0000 }, + { 0xbe66, 0x0000 }, + { 0xbe67, 0x0000 }, + { 0xbe68, 0x0000 }, + { 0xbe69, 0x0000 }, + { 0xbe6a, 0x0000 }, + { 0xbe6b, 0x0000 }, + { 0xbe6c, 0x0000 }, + { 0xbe6d, 0x0000 }, + { 0xbe6e, 0x0000 }, + { 0xbe6f, 0x0000 }, + { 0xbe70, 0x0000 }, + { 0xbe71, 0x0000 }, + { 0xbe72, 0x0000 }, + { 0xbe73, 0x0000 }, + { 0xbe74, 0x0000 }, + { 0xbe75, 0x0000 }, + { 0xbe76, 0x0000 }, + { 0xbe77, 0x0000 }, + { 0xbe78, 0x0000 }, + { 0xbe79, 0x0000 }, + { 0xbe7a, 0x0000 }, + { 0xbe7b, 0x0000 }, + { 0xbe7c, 0x0000 }, + { 0xbe7d, 0x0000 }, + { 0xbe7e, 0x0000 }, + { 0xbe7f, 0x0000 }, + { 0xbe80, 0x0000 }, + { 0xbe81, 0x0000 }, + { 0xbe82, 0x0000 }, + { 0xbe83, 0x0000 }, + { 0xbe84, 0x0000 }, + { 0xbe85, 0x0000 }, + { 0xbe86, 0x0000 }, + { 0xbe87, 0x0000 }, + { 0xbe88, 0x0000 }, + { 0xbe89, 0x0000 }, + { 0xbe8a, 0x0000 }, + { 0xbe8b, 0x0000 }, + { 0xbe8c, 0x0000 }, + { 0xbe8d, 0x0000 }, + { 0xbe8e, 0x0000 }, + { 0xbe8f, 0x0000 }, + { 0xbe90, 0x0000 }, + { 0xbe91, 0x0000 }, + { 0xbe92, 0x0000 }, + { 0xbe93, 0x0000 }, + { 0xbe94, 0x0000 }, + { 0xbe95, 0x0000 }, + { 0xbe96, 0x0000 }, + { 0xbe97, 0x0000 }, + { 0xbe98, 0x0000 }, + { 0xbe99, 0x0000 }, + { 0xbe9a, 0x0000 }, + { 0xbe9b, 0x0000 }, + { 0xbe9c, 0x0000 }, + { 0xbe9d, 0x0000 }, + { 0xbe9e, 0x0000 }, + { 0xbe9f, 0x0000 }, + { 0xbea0, 0x0000 }, + { 0xbea1, 0x0000 }, + { 0xbea2, 0x0000 }, + { 0xbea3, 0x0000 }, + { 0xbea4, 0x0000 }, + { 0xbea5, 0x0000 }, + { 0xbea6, 0x0000 }, + { 0xbea7, 0x0000 }, + { 0xbea8, 0x0000 }, + { 0xbea9, 0x0000 }, + { 0xbeaa, 0x0000 }, + { 0xbeab, 0x0000 }, + { 0xbeac, 0x0000 }, + { 0xbead, 0x0000 }, + { 0xbeae, 0x0000 }, + { 0xbeaf, 0x0000 }, + { 0xbeb0, 0x0000 }, + { 0xbeb1, 0x0000 }, + { 0xbeb2, 0x0000 }, + { 0xbeb3, 0x0000 }, + { 0xbeb4, 0x0000 }, + { 0xbeb5, 0x0000 }, + { 0xbeb6, 0x0000 }, + { 0xbeb7, 0x0000 }, + { 0xbeb8, 0x0000 }, + { 0xbeb9, 0x0000 }, + { 0xbeba, 0x0000 }, + { 0xbebb, 0x0000 }, + { 0xbebc, 0x0000 }, + { 0xbebd, 0x0000 }, + { 0xbebe, 0x0000 }, + { 0xbebf, 0x0000 }, + { 0xbec0, 0x0000 }, + { 0xbec1, 0x0000 }, + { 0xbec2, 0x0000 }, + { 0xbec3, 0x0000 }, + { 0xbec4, 0x0000 }, + { 0xbec5, 0x0000 }, + { 0xbec6, 0x0000 }, + { 0xbec7, 0x0000 }, + { 0xbec8, 0x0000 }, + { 0xbec9, 0x0000 }, + { 0xbeca, 0x0000 }, + { 0xbecb, 0x0000 }, + { 0xbecc, 0x0000 }, + { 0xbecd, 0x0000 }, + { 0xbece, 0x0000 }, + { 0xbecf, 0x0000 }, + { 0xbed0, 0x0000 }, + { 0xbed1, 0x0000 }, + { 0xbed2, 0x0000 }, + { 0xbed3, 0x0000 }, + { 0xbed4, 0x0000 }, + { 0xbed5, 0x0000 }, + { 0xbed6, 0x0000 }, + { 0xbed7, 0x0000 }, + { 0xbed8, 0x0000 }, + { 0xbed9, 0x0000 }, + { 0xbeda, 0x0000 }, + { 0xbedb, 0x0000 }, + { 0xbedc, 0x0000 }, + { 0xbedd, 0x0000 }, + { 0xbede, 0x0000 }, + { 0xbedf, 0x0000 }, + { 0xbee0, 0x0000 }, + { 0xbee1, 0x0000 }, + { 0xbee2, 0x0000 }, + { 0xbee3, 0x0000 }, + { 0xbee4, 0x0000 }, + { 0xbee5, 0x0000 }, + { 0xbee6, 0x0000 }, + { 0xbee7, 0x0000 }, + { 0xbee8, 0x0000 }, + { 0xbee9, 0x0000 }, + { 0xbeea, 0x0000 }, + { 0xbeeb, 0x0000 }, + { 0xbeec, 0x0000 }, + { 0xbeed, 0x0000 }, + { 0xbeee, 0x0000 }, + { 0xbeef, 0x0000 }, + { 0xbef0, 0x0000 }, + { 0xbef1, 0x0000 }, + { 0xbef2, 0x0000 }, + { 0xbef3, 0x0000 }, + { 0xbef4, 0x0000 }, + { 0xbef5, 0x0000 }, + { 0xbef6, 0x0000 }, + { 0xbef7, 0x0000 }, + { 0xbef8, 0x0000 }, + { 0xbef9, 0x0000 }, + { 0xbefa, 0x0000 }, + { 0xbefb, 0x0000 }, + { 0xbefc, 0x0000 }, + { 0xbefd, 0x0000 }, + { 0xbefe, 0x0000 }, + { 0xbeff, 0x0000 }, + { 0xbf00, 0x0000 }, + { 0xbf01, 0x0000 }, + { 0xbf02, 0x0000 }, + { 0xbf03, 0x0000 }, + { 0xbf04, 0x0000 }, + { 0xbf05, 0x0000 }, + { 0xbf06, 0x0000 }, + { 0xbf07, 0x0000 }, + { 0xbf08, 0x0000 }, + { 0xbf09, 0x0000 }, + { 0xbf0a, 0x0000 }, + { 0xbf0b, 0x0000 }, + { 0xbf0c, 0x0000 }, + { 0xbf0d, 0x0000 }, + { 0xbf0e, 0x0000 }, + { 0xbf0f, 0x0000 }, + { 0xbf10, 0x0000 }, + { 0xbf11, 0x0000 }, + { 0xbf12, 0x0000 }, + { 0xbf13, 0x0000 }, + { 0xbf14, 0x0000 }, + { 0xbf15, 0x0000 }, + { 0xbf16, 0x0000 }, + { 0xbf17, 0x0000 }, + { 0xbf18, 0x0000 }, + { 0xbf19, 0x0000 }, + { 0xbf1a, 0x0000 }, + { 0xbf1b, 0x0000 }, + { 0xbf1c, 0x0000 }, + { 0xbf1d, 0x0000 }, + { 0xbf1e, 0x0000 }, + { 0xbf1f, 0x0000 }, + { 0xbf20, 0x0000 }, + { 0xbf21, 0x0000 }, + { 0xbf22, 0x0000 }, + { 0xbf23, 0x0000 }, + { 0xbf24, 0x0000 }, + { 0xbf25, 0x0000 }, + { 0xbf26, 0x0000 }, + { 0xbf27, 0x0000 }, + { 0xbf28, 0x0000 }, + { 0xbf29, 0x0000 }, + { 0xbf2a, 0x0000 }, + { 0xbf2b, 0x0000 }, + { 0xbf2c, 0x0000 }, + { 0xbf2d, 0x0000 }, + { 0xbf2e, 0x0000 }, + { 0xbf2f, 0x0000 }, + { 0xbf30, 0x0000 }, + { 0xbf31, 0x0000 }, + { 0xbf32, 0x0000 }, + { 0xbf33, 0x0000 }, + { 0xbf34, 0x0000 }, + { 0xbf35, 0x0000 }, + { 0xbf36, 0x0000 }, + { 0xbf37, 0x0000 }, + { 0xbf38, 0x0000 }, + { 0xbf39, 0x0000 }, + { 0xbf3a, 0x0000 }, + { 0xbf3b, 0x0000 }, + { 0xbf3c, 0x0000 }, + { 0xbf3d, 0x0000 }, + { 0xbf3e, 0x0000 }, + { 0xbf3f, 0x0000 }, + { 0xbf40, 0x0000 }, + { 0xbf41, 0x0000 }, + { 0xbf42, 0x0000 }, + { 0xbf43, 0x0000 }, + { 0xbf44, 0x0000 }, + { 0xbf45, 0x0000 }, + { 0xbf46, 0x0000 }, + { 0xbf47, 0x0000 }, + { 0xbf48, 0x0000 }, + { 0xbf49, 0x0000 }, + { 0xbf4a, 0x0000 }, + { 0xbf4b, 0x0000 }, + { 0xbf4c, 0x0000 }, + { 0xbf4d, 0x0000 }, + { 0xbf4e, 0x0000 }, + { 0xbf4f, 0x0000 }, + { 0xbf50, 0x0000 }, + { 0xbf51, 0x0000 }, + { 0xbf52, 0x0000 }, + { 0xbf53, 0x0000 }, + { 0xbf54, 0x0000 }, + { 0xbf55, 0x0000 }, + { 0xbf56, 0x0000 }, + { 0xbf57, 0x0000 }, + { 0xbf58, 0x0000 }, + { 0xbf59, 0x0000 }, + { 0xbf5a, 0x0000 }, + { 0xbf5b, 0x0000 }, + { 0xbf5c, 0x0000 }, + { 0xbf5d, 0x0000 }, + { 0xbf5e, 0x0000 }, + { 0xbf5f, 0x0000 }, + { 0xbf60, 0x0000 }, + { 0xbf61, 0x0000 }, + { 0xbf62, 0x0000 }, + { 0xbf63, 0x0000 }, + { 0xbf64, 0x0000 }, + { 0xbf65, 0x0000 }, + { 0xbf66, 0x0000 }, + { 0xbf67, 0x0000 }, + { 0xbf68, 0x0000 }, + { 0xbf69, 0x0000 }, + { 0xbf6a, 0x0000 }, + { 0xbf6b, 0x0000 }, + { 0xbf6c, 0x0000 }, + { 0xbf6d, 0x0000 }, + { 0xbf6e, 0x0000 }, + { 0xbf6f, 0x0000 }, + { 0xbf70, 0x0000 }, + { 0xbf71, 0x0000 }, + { 0xbf72, 0x0000 }, + { 0xbf73, 0x0000 }, + { 0xbf74, 0x0000 }, + { 0xbf75, 0x0000 }, + { 0xbf76, 0x0000 }, + { 0xbf77, 0x0000 }, + { 0xbf78, 0x0000 }, + { 0xbf79, 0x0000 }, + { 0xbf7a, 0x0000 }, + { 0xbf7b, 0x0000 }, + { 0xbf7c, 0x0000 }, + { 0xbf7d, 0x0000 }, + { 0xbf7e, 0x0000 }, + { 0xbf7f, 0x0000 }, + { 0xbf80, 0x0000 }, + { 0xbf81, 0x0000 }, + { 0xbf82, 0x0000 }, + { 0xbf83, 0x0000 }, + { 0xbf84, 0x0000 }, + { 0xbf85, 0x0000 }, + { 0xbf86, 0x0000 }, + { 0xbf87, 0x0000 }, + { 0xbf88, 0x0000 }, + { 0xbf89, 0x0000 }, + { 0xbf8a, 0x0000 }, + { 0xbf8b, 0x0000 }, + { 0xbf8c, 0x0000 }, + { 0xbf8d, 0x0000 }, + { 0xbf8e, 0x0000 }, + { 0xbf8f, 0x0000 }, + { 0xbf90, 0x0000 }, + { 0xbf91, 0x0000 }, + { 0xbf92, 0x0000 }, + { 0xbf93, 0x0000 }, + { 0xbf94, 0x0000 }, + { 0xbf95, 0x0000 }, + { 0xbf96, 0x0000 }, + { 0xbf97, 0x0000 }, + { 0xbf98, 0x0000 }, + { 0xbf99, 0x0000 }, + { 0xbf9a, 0x0000 }, + { 0xbf9b, 0x0000 }, + { 0xbf9c, 0x0000 }, + { 0xbf9d, 0x0000 }, + { 0xbf9e, 0x0000 }, + { 0xbf9f, 0x0000 }, + { 0xbfa0, 0x0000 }, + { 0xbfa1, 0x0000 }, + { 0xbfa2, 0x0000 }, + { 0xbfa3, 0x0000 }, + { 0xbfa4, 0x0000 }, + { 0xbfa5, 0x0000 }, + { 0xbfa6, 0x0000 }, + { 0xbfa7, 0x0000 }, + { 0xbfa8, 0x0000 }, + { 0xbfa9, 0x0000 }, + { 0xbfaa, 0x0000 }, + { 0xbfab, 0x0000 }, + { 0xbfac, 0x0000 }, + { 0xbfad, 0x0000 }, + { 0xbfae, 0x0000 }, + { 0xbfaf, 0x0000 }, + { 0xbfb0, 0x0000 }, + { 0xbfb1, 0x0000 }, + { 0xbfb2, 0x0000 }, + { 0xbfb3, 0x0000 }, + { 0xbfb4, 0x0000 }, + { 0xbfb5, 0x0000 }, + { 0xbfb6, 0x0000 }, + { 0xbfb7, 0x0000 }, + { 0xbfb8, 0x0000 }, + { 0xbfb9, 0x0000 }, + { 0xbfba, 0x0000 }, + { 0xbfbb, 0x0000 }, + { 0xbfbc, 0x0000 }, + { 0xbfbd, 0x0000 }, + { 0xbfbe, 0x0000 }, + { 0xbfbf, 0x0000 }, + { 0xbfc0, 0x0000 }, + { 0xbfc1, 0x0000 }, + { 0xbfc2, 0x0000 }, + { 0xbfc3, 0x0000 }, + { 0xbfc4, 0x0000 }, + { 0xbfc5, 0x0000 }, + { 0xbfc6, 0x0000 }, + { 0xbfc7, 0x0000 }, + { 0xbfc8, 0x0000 }, + { 0xbfc9, 0x0000 }, + { 0xbfca, 0x0000 }, + { 0xbfcb, 0x0000 }, + { 0xbfcc, 0x0000 }, + { 0xbfcd, 0x0000 }, + { 0xbfce, 0x0000 }, + { 0xbfcf, 0x0000 }, + { 0xbfd0, 0x0000 }, + { 0xbfd1, 0x0000 }, + { 0xbfd2, 0x0000 }, + { 0xbfd3, 0x0000 }, + { 0xbfd4, 0x0000 }, + { 0xbfd5, 0x0000 }, + { 0xbfd6, 0x0000 }, + { 0xbfd7, 0x0000 }, + { 0xbfd8, 0x0000 }, + { 0xbfd9, 0x0000 }, + { 0xbfda, 0x0000 }, + { 0xbfdb, 0x0000 }, + { 0xbfdc, 0x0000 }, + { 0xbfdd, 0x0000 }, + { 0xbfde, 0x0000 }, + { 0xbfdf, 0x0000 }, + { 0xbfe0, 0x0000 }, + { 0xbfe1, 0x0000 }, + { 0xbfe2, 0x0000 }, + { 0xbfe3, 0x0000 }, + { 0xbfe4, 0x0000 }, + { 0xbfe5, 0x0000 }, + { 0xbfe6, 0x0000 }, + { 0xbfe7, 0x0000 }, + { 0xbfe8, 0x0000 }, + { 0xbfe9, 0x0000 }, + { 0xbfea, 0x0000 }, + { 0xbfeb, 0x0000 }, + { 0xbfec, 0x0000 }, + { 0xbfed, 0x0000 }, + { 0xbfee, 0x0000 }, + { 0xbfef, 0x0000 }, + { 0xbff0, 0x0000 }, + { 0xbff1, 0x0000 }, + { 0xbff2, 0x0000 }, + { 0xbff3, 0x0000 }, + { 0xbff4, 0x0000 }, + { 0xbff5, 0x0000 }, + { 0xbff6, 0x0000 }, + { 0xbff7, 0x0000 }, + { 0xbff8, 0x0000 }, + { 0xbff9, 0x0000 }, + { 0xbffa, 0x0000 }, + { 0xbffb, 0x0000 }, + { 0xbffc, 0x0000 }, + { 0xbffd, 0x0000 }, + { 0xbffe, 0x0000 }, + { 0xbfff, 0x0000 }, + { 0xc000, 0x0000 }, + { 0xc001, 0x0000 }, + { 0xc002, 0x0000 }, + { 0xc003, 0x0000 }, + { 0xc004, 0x0000 }, + { 0xc005, 0x0000 }, + { 0xc006, 0x0000 }, + { 0xc007, 0x0000 }, + { 0xc008, 0x0000 }, + { 0xc009, 0x0000 }, + { 0xc00a, 0x0000 }, + { 0xc00b, 0x0000 }, + { 0xc00c, 0x0000 }, + { 0xc00d, 0x0000 }, + { 0xc00e, 0x0000 }, + { 0xc00f, 0x0000 }, + { 0xc010, 0x0000 }, + { 0xc011, 0x0000 }, + { 0xc012, 0x0000 }, + { 0xc013, 0x0000 }, + { 0xc014, 0x0000 }, + { 0xc015, 0x0000 }, + { 0xc016, 0x0000 }, + { 0xc017, 0x0000 }, + { 0xc018, 0x0000 }, + { 0xc019, 0x0000 }, + { 0xc01a, 0x0000 }, + { 0xc01b, 0x0000 }, + { 0xc01c, 0x0000 }, + { 0xc01d, 0x0000 }, + { 0xc01e, 0x0000 }, + { 0xc01f, 0x0000 }, + { 0xc020, 0x0000 }, + { 0xc021, 0x0000 }, + { 0xc022, 0x0000 }, + { 0xc023, 0x0000 }, + { 0xc024, 0x0000 }, + { 0xc025, 0x0000 }, + { 0xc026, 0x0000 }, + { 0xc027, 0x0000 }, + { 0xc028, 0x0000 }, + { 0xc029, 0x0000 }, + { 0xc02a, 0x0000 }, + { 0xc02b, 0x0000 }, + { 0xc02c, 0x0000 }, + { 0xc02d, 0x0000 }, + { 0xc02e, 0x0000 }, + { 0xc02f, 0x0000 }, + { 0xc030, 0x0000 }, + { 0xc031, 0x0000 }, + { 0xc032, 0x0000 }, + { 0xc033, 0x0000 }, + { 0xc034, 0x0000 }, + { 0xc035, 0x0000 }, + { 0xc036, 0x0000 }, + { 0xc037, 0x0000 }, + { 0xc038, 0x0000 }, + { 0xc039, 0x0000 }, + { 0xc03a, 0x0000 }, + { 0xc03b, 0x0000 }, + { 0xc03c, 0x0000 }, + { 0xc03d, 0x0000 }, + { 0xc03e, 0x0000 }, + { 0xc03f, 0x0000 }, + { 0xc040, 0x0000 }, + { 0xc041, 0x0000 }, + { 0xc042, 0x0000 }, + { 0xc043, 0x0000 }, + { 0xc044, 0x0000 }, + { 0xc045, 0x0000 }, + { 0xc046, 0x0000 }, + { 0xc047, 0x0000 }, + { 0xc048, 0x0000 }, + { 0xc049, 0x0000 }, + { 0xc04a, 0x0000 }, + { 0xc04b, 0x0000 }, + { 0xc04c, 0x0000 }, + { 0xc04d, 0x0000 }, + { 0xc04e, 0x0000 }, + { 0xc04f, 0x0000 }, + { 0xc050, 0x0000 }, + { 0xc051, 0x0000 }, + { 0xc052, 0x0000 }, + { 0xc053, 0x0000 }, + { 0xc054, 0x0000 }, + { 0xc055, 0x0000 }, + { 0xc056, 0x0000 }, + { 0xc057, 0x0000 }, + { 0xc058, 0x0000 }, + { 0xc059, 0x0000 }, + { 0xc05a, 0x0000 }, + { 0xc05b, 0x0000 }, + { 0xc05c, 0x0000 }, + { 0xc05d, 0x0000 }, + { 0xc05e, 0x0000 }, + { 0xc05f, 0x0000 }, + { 0xc060, 0x0000 }, + { 0xc061, 0x0000 }, + { 0xc062, 0x0000 }, + { 0xc063, 0x0000 }, + { 0xc064, 0x0000 }, + { 0xc065, 0x0000 }, + { 0xc066, 0x0000 }, + { 0xc067, 0x0000 }, + { 0xc068, 0x0000 }, + { 0xc069, 0x0000 }, + { 0xc06a, 0x0000 }, + { 0xc06b, 0x0000 }, + { 0xc06c, 0x0000 }, + { 0xc06d, 0x0000 }, + { 0xc06e, 0x0000 }, + { 0xc06f, 0x0000 }, + { 0xc070, 0x0000 }, + { 0xc071, 0x0000 }, + { 0xc072, 0x0000 }, + { 0xc073, 0x0000 }, + { 0xc074, 0x0000 }, + { 0xc075, 0x0000 }, + { 0xc076, 0x0000 }, + { 0xc077, 0x0000 }, + { 0xc078, 0x0000 }, + { 0xc079, 0x0000 }, + { 0xc07a, 0x0000 }, + { 0xc07b, 0x0000 }, + { 0xc07c, 0x0000 }, + { 0xc07d, 0x0000 }, + { 0xc07e, 0x0000 }, + { 0xc07f, 0x0000 }, + { 0xc080, 0x0000 }, + { 0xc081, 0x0000 }, + { 0xc082, 0x0000 }, + { 0xc083, 0x0000 }, + { 0xc084, 0x0000 }, + { 0xc085, 0x0000 }, + { 0xc086, 0x0000 }, + { 0xc087, 0x0000 }, + { 0xc088, 0x0000 }, + { 0xc089, 0x0000 }, + { 0xc08a, 0x0000 }, + { 0xc08b, 0x0000 }, + { 0xc08c, 0x0000 }, + { 0xc08d, 0x0000 }, + { 0xc08e, 0x0000 }, + { 0xc08f, 0x0000 }, + { 0xc090, 0x0000 }, + { 0xc091, 0x0000 }, + { 0xc092, 0x0000 }, + { 0xc093, 0x0000 }, + { 0xc094, 0x0000 }, + { 0xc095, 0x0000 }, + { 0xc096, 0x0000 }, + { 0xc097, 0x0000 }, + { 0xc098, 0x0000 }, + { 0xc099, 0x0000 }, + { 0xc09a, 0x0000 }, + { 0xc09b, 0x0000 }, + { 0xc09c, 0x0000 }, + { 0xc09d, 0x0000 }, + { 0xc09e, 0x0000 }, + { 0xc09f, 0x0000 }, + { 0xc0a0, 0x0000 }, + { 0xc0a1, 0x0000 }, + { 0xc0a2, 0x0000 }, + { 0xc0a3, 0x0000 }, + { 0xc0a4, 0x0000 }, + { 0xc0a5, 0x0000 }, + { 0xc0a6, 0x0000 }, + { 0xc0a7, 0x0000 }, + { 0xc0a8, 0x0000 }, + { 0xc0a9, 0x0000 }, + { 0xc0aa, 0x0000 }, + { 0xc0ab, 0x0000 }, + { 0xc0ac, 0x0000 }, + { 0xc0ad, 0x0000 }, + { 0xc0ae, 0x0000 }, + { 0xc0af, 0x0000 }, + { 0xc0b0, 0x0000 }, + { 0xc0b1, 0x0000 }, + { 0xc0b2, 0x0000 }, + { 0xc0b3, 0x0000 }, + { 0xc0b4, 0x0000 }, + { 0xc0b5, 0x0000 }, + { 0xc0b6, 0x0000 }, + { 0xc0b7, 0x0000 }, + { 0xc0b8, 0x0000 }, + { 0xc0b9, 0x0000 }, + { 0xc0ba, 0x0000 }, + { 0xc0bb, 0x0000 }, + { 0xc0bc, 0x0000 }, + { 0xc0bd, 0x0000 }, + { 0xc0be, 0x0000 }, + { 0xc0bf, 0x0000 }, + { 0xc0c0, 0x0000 }, + { 0xc0c1, 0x0000 }, + { 0xc0c2, 0x0000 }, + { 0xc0c3, 0x0000 }, + { 0xc0c4, 0x0000 }, + { 0xc0c5, 0x0000 }, + { 0xc0c6, 0x0000 }, + { 0xc0c7, 0x0000 }, + { 0xc0c8, 0x0000 }, + { 0xc0c9, 0x0000 }, + { 0xc0ca, 0x0000 }, + { 0xc0cb, 0x0000 }, + { 0xc0cc, 0x0000 }, + { 0xc0cd, 0x0000 }, + { 0xc0ce, 0x0000 }, + { 0xc0cf, 0x0000 }, + { 0xc0d0, 0x0000 }, + { 0xc0d1, 0x0000 }, + { 0xc0d2, 0x0000 }, + { 0xc0d3, 0x0000 }, + { 0xc0d4, 0x0000 }, + { 0xc0d5, 0x0000 }, + { 0xc0d6, 0x0000 }, + { 0xc0d7, 0x0000 }, + { 0xc0d8, 0x0000 }, + { 0xc0d9, 0x0000 }, + { 0xc0da, 0x0000 }, + { 0xc0db, 0x0000 }, + { 0xc0dc, 0x0000 }, + { 0xc0dd, 0x0000 }, + { 0xc0de, 0x0000 }, + { 0xc0df, 0x0000 }, + { 0xc0e0, 0x0000 }, + { 0xc0e1, 0x0000 }, + { 0xc0e2, 0x0000 }, + { 0xc0e3, 0x0000 }, + { 0xc0e4, 0x0000 }, + { 0xc0e5, 0x0000 }, + { 0xc0e6, 0x0000 }, + { 0xc0e7, 0x0000 }, + { 0xc0e8, 0x0000 }, + { 0xc0e9, 0x0000 }, + { 0xc0ea, 0x0000 }, + { 0xc0eb, 0x0000 }, + { 0xc0ec, 0x0000 }, + { 0xc0ed, 0x0000 }, + { 0xc0ee, 0x0000 }, + { 0xc0ef, 0x0000 }, + { 0xc0f0, 0x0000 }, + { 0xc0f1, 0x0000 }, + { 0xc0f2, 0x0000 }, + { 0xc0f3, 0x0000 }, + { 0xc0f4, 0x0000 }, + { 0xc0f5, 0x0000 }, + { 0xc0f6, 0x0000 }, + { 0xc0f7, 0x0000 }, + { 0xc0f8, 0x0000 }, + { 0xc0f9, 0x0000 }, + { 0xc0fa, 0x0000 }, + { 0xc0fb, 0x0000 }, + { 0xc0fc, 0x0000 }, + { 0xc0fd, 0x0000 }, + { 0xc0fe, 0x0000 }, + { 0xc0ff, 0x0000 }, + { 0xc100, 0x0000 }, + { 0xc101, 0x0000 }, + { 0xc102, 0x0000 }, + { 0xc103, 0x0000 }, + { 0xc104, 0x0000 }, + { 0xc105, 0x0000 }, + { 0xc106, 0x0000 }, + { 0xc107, 0x0000 }, + { 0xc108, 0x0000 }, + { 0xc109, 0x0000 }, + { 0xc10a, 0x0000 }, + { 0xc10b, 0x0000 }, + { 0xc10c, 0x0000 }, + { 0xc10d, 0x0000 }, + { 0xc10e, 0x0000 }, + { 0xc10f, 0x0000 }, + { 0xc110, 0x0000 }, + { 0xc111, 0x0000 }, + { 0xc112, 0x0000 }, + { 0xc113, 0x0000 }, + { 0xc114, 0x0000 }, + { 0xc115, 0x0000 }, + { 0xc116, 0x0000 }, + { 0xc117, 0x0000 }, + { 0xc118, 0x0000 }, + { 0xc119, 0x0000 }, + { 0xc11a, 0x0000 }, + { 0xc11b, 0x0000 }, + { 0xc11c, 0x0000 }, + { 0xc11d, 0x0000 }, + { 0xc11e, 0x0000 }, + { 0xc11f, 0x0000 }, + { 0xc120, 0x0000 }, + { 0xc121, 0x0000 }, + { 0xc122, 0x0000 }, + { 0xc123, 0x0000 }, + { 0xc124, 0x0000 }, + { 0xc125, 0x0000 }, + { 0xc126, 0x0000 }, + { 0xc127, 0x0000 }, + { 0xc128, 0x0000 }, + { 0xc129, 0x0000 }, + { 0xc12a, 0x0000 }, + { 0xc12b, 0x0000 }, + { 0xc12c, 0x0000 }, + { 0xc12d, 0x0000 }, + { 0xc12e, 0x0000 }, + { 0xc12f, 0x0000 }, + { 0xc130, 0x0000 }, + { 0xc131, 0x0000 }, + { 0xc132, 0x0000 }, + { 0xc133, 0x0000 }, + { 0xc134, 0x0000 }, + { 0xc135, 0x0000 }, + { 0xc136, 0x0000 }, + { 0xc137, 0x0000 }, + { 0xc138, 0x0000 }, + { 0xc139, 0x0000 }, + { 0xc13a, 0x0000 }, + { 0xc13b, 0x0000 }, + { 0xc13c, 0x0000 }, + { 0xc13d, 0x0000 }, + { 0xc13e, 0x0000 }, + { 0xc13f, 0x0000 }, + { 0xc140, 0x0000 }, + { 0xc141, 0x0000 }, + { 0xc142, 0x0000 }, + { 0xc143, 0x0000 }, + { 0xc144, 0x0000 }, + { 0xc145, 0x0000 }, + { 0xc146, 0x0000 }, + { 0xc147, 0x0000 }, + { 0xc148, 0x0000 }, + { 0xc149, 0x0000 }, + { 0xc14a, 0x0000 }, + { 0xc14b, 0x0000 }, + { 0xc14c, 0x0000 }, + { 0xc14d, 0x0000 }, + { 0xc14e, 0x0000 }, + { 0xc14f, 0x0000 }, + { 0xc150, 0x0000 }, + { 0xc151, 0x0000 }, + { 0xc152, 0x0000 }, + { 0xc153, 0x0000 }, + { 0xc154, 0x0000 }, + { 0xc155, 0x0000 }, + { 0xc156, 0x0000 }, + { 0xc157, 0x0000 }, + { 0xc158, 0x0000 }, + { 0xc159, 0x0000 }, + { 0xc15a, 0x0000 }, + { 0xc15b, 0x0000 }, + { 0xc15c, 0x0000 }, + { 0xc15d, 0x0000 }, + { 0xc15e, 0x0000 }, + { 0xc15f, 0x0000 }, + { 0xc160, 0x0000 }, + { 0xc161, 0x0000 }, + { 0xc162, 0x0000 }, + { 0xc163, 0x0000 }, + { 0xc164, 0x0000 }, + { 0xc165, 0x0000 }, + { 0xc166, 0x0000 }, + { 0xc167, 0x0000 }, + { 0xc168, 0x0000 }, + { 0xc169, 0x0000 }, + { 0xc16a, 0x0000 }, + { 0xc16b, 0x0000 }, + { 0xc16c, 0x0000 }, + { 0xc16d, 0x0000 }, + { 0xc16e, 0x0000 }, + { 0xc16f, 0x0000 }, + { 0xc170, 0x0000 }, + { 0xc171, 0x0000 }, + { 0xc172, 0x0000 }, + { 0xc173, 0x0000 }, + { 0xc174, 0x0000 }, + { 0xc175, 0x0000 }, + { 0xc176, 0x0000 }, + { 0xc177, 0x0000 }, + { 0xc178, 0x0000 }, + { 0xc179, 0x0000 }, + { 0xc17a, 0x0000 }, + { 0xc17b, 0x0000 }, + { 0xc17c, 0x0000 }, + { 0xc17d, 0x0000 }, + { 0xc17e, 0x0000 }, + { 0xc17f, 0x0000 }, + { 0xc180, 0x0000 }, + { 0xc181, 0x0000 }, + { 0xc182, 0x0000 }, + { 0xc183, 0x0000 }, + { 0xc184, 0x0000 }, + { 0xc185, 0x0000 }, + { 0xc186, 0x0000 }, + { 0xc187, 0x0000 }, + { 0xc188, 0x0000 }, + { 0xc189, 0x0000 }, + { 0xc18a, 0x0000 }, + { 0xc18b, 0x0000 }, + { 0xc18c, 0x0000 }, + { 0xc18d, 0x0000 }, + { 0xc18e, 0x0000 }, + { 0xc18f, 0x0000 }, + { 0xc190, 0x0000 }, + { 0xc191, 0x0000 }, + { 0xc192, 0x0000 }, + { 0xc193, 0x0000 }, + { 0xc194, 0x0000 }, + { 0xc195, 0x0000 }, + { 0xc196, 0x0000 }, + { 0xc197, 0x0000 }, + { 0xc198, 0x0000 }, + { 0xc199, 0x0000 }, + { 0xc19a, 0x0000 }, + { 0xc19b, 0x0000 }, + { 0xc19c, 0x0000 }, + { 0xc19d, 0x0000 }, + { 0xc19e, 0x0000 }, + { 0xc19f, 0x0000 }, + { 0xc1a0, 0x0000 }, + { 0xc1a1, 0x0000 }, + { 0xc1a2, 0x0000 }, + { 0xc1a3, 0x0000 }, + { 0xc1a4, 0x0000 }, + { 0xc1a5, 0x0000 }, + { 0xc1a6, 0x0000 }, + { 0xc1a7, 0x0000 }, + { 0xc1a8, 0x0000 }, + { 0xc1a9, 0x0000 }, + { 0xc1aa, 0x0000 }, + { 0xc1ab, 0x0000 }, + { 0xc1ac, 0x0000 }, + { 0xc1ad, 0x0000 }, + { 0xc1ae, 0x0000 }, + { 0xc1af, 0x0000 }, + { 0xc1b0, 0x0000 }, + { 0xc1b1, 0x0000 }, + { 0xc1b2, 0x0000 }, + { 0xc1b3, 0x0000 }, + { 0xc1b4, 0x0000 }, + { 0xc1b5, 0x0000 }, + { 0xc1b6, 0x0000 }, + { 0xc1b7, 0x0000 }, + { 0xc1b8, 0x0000 }, + { 0xc1b9, 0x0000 }, + { 0xc1ba, 0x0000 }, + { 0xc1bb, 0x0000 }, + { 0xc1bc, 0x0000 }, + { 0xc1bd, 0x0000 }, + { 0xc1be, 0x0000 }, + { 0xc1bf, 0x0000 }, + { 0xc1c0, 0x0000 }, + { 0xc1c1, 0x0000 }, + { 0xc1c2, 0x0000 }, + { 0xc1c3, 0x0000 }, + { 0xc1c4, 0x0000 }, + { 0xc1c5, 0x0000 }, + { 0xc1c6, 0x0000 }, + { 0xc1c7, 0x0000 }, + { 0xc1c8, 0x0000 }, + { 0xc1c9, 0x0000 }, + { 0xc1ca, 0x0000 }, + { 0xc1cb, 0x0000 }, + { 0xc1cc, 0x0000 }, + { 0xc1cd, 0x0000 }, + { 0xc1ce, 0x0000 }, + { 0xc1cf, 0x0000 }, + { 0xc1d0, 0x0000 }, + { 0xc1d1, 0x0000 }, + { 0xc1d2, 0x0000 }, + { 0xc1d3, 0x0000 }, + { 0xc1d4, 0x0000 }, + { 0xc1d5, 0x0000 }, + { 0xc1d6, 0x0000 }, + { 0xc1d7, 0x0000 }, + { 0xc1d8, 0x0000 }, + { 0xc1d9, 0x0000 }, + { 0xc1da, 0x0000 }, + { 0xc1db, 0x0000 }, + { 0xc1dc, 0x0000 }, + { 0xc1dd, 0x0000 }, + { 0xc1de, 0x0000 }, + { 0xc1df, 0x0000 }, + { 0xc1e0, 0x0000 }, + { 0xc1e1, 0x0000 }, + { 0xc1e2, 0x0000 }, + { 0xc1e3, 0x0000 }, + { 0xc1e4, 0x0000 }, + { 0xc1e5, 0x0000 }, + { 0xc1e6, 0x0000 }, + { 0xc1e7, 0x0000 }, + { 0xc1e8, 0x0000 }, + { 0xc1e9, 0x0000 }, + { 0xc1ea, 0x0000 }, + { 0xc1eb, 0x0000 }, + { 0xc1ec, 0x0000 }, + { 0xc1ed, 0x0000 }, + { 0xc1ee, 0x0000 }, + { 0xc1ef, 0x0000 }, + { 0xc1f0, 0x0000 }, + { 0xc1f1, 0x0000 }, + { 0xc1f2, 0x0000 }, + { 0xc1f3, 0x0000 }, + { 0xc1f4, 0x0000 }, + { 0xc1f5, 0x0000 }, + { 0xc1f6, 0x0000 }, + { 0xc1f7, 0x0000 }, + { 0xc1f8, 0x0000 }, + { 0xc1f9, 0x0000 }, + { 0xc1fa, 0x0000 }, + { 0xc1fb, 0x0000 }, + { 0xc1fc, 0x0000 }, + { 0xc1fd, 0x0000 }, + { 0xc1fe, 0x0000 }, + { 0xc1ff, 0x0000 }, + { 0xc200, 0x0000 }, + { 0xc201, 0x0000 }, + { 0xc202, 0x0000 }, + { 0xc203, 0x0000 }, + { 0xc204, 0x0000 }, + { 0xc205, 0x0000 }, + { 0xc206, 0x0000 }, + { 0xc207, 0x0000 }, + { 0xc208, 0x0000 }, + { 0xc209, 0x0000 }, + { 0xc20a, 0x0000 }, + { 0xc20b, 0x0000 }, + { 0xc20c, 0x0000 }, + { 0xc20d, 0x0000 }, + { 0xc20e, 0x0000 }, + { 0xc20f, 0x0000 }, + { 0xc210, 0x0000 }, + { 0xc211, 0x0000 }, + { 0xc212, 0x0000 }, + { 0xc213, 0x0000 }, + { 0xc214, 0x0000 }, + { 0xc215, 0x0000 }, + { 0xc216, 0x0000 }, + { 0xc217, 0x0000 }, + { 0xc218, 0x0000 }, + { 0xc219, 0x0000 }, + { 0xc21a, 0x0000 }, + { 0xc21b, 0x0000 }, + { 0xc21c, 0x0000 }, + { 0xc21d, 0x0000 }, + { 0xc21e, 0x0000 }, + { 0xc21f, 0x0000 }, + { 0xc220, 0x0000 }, + { 0xc221, 0x0000 }, + { 0xc222, 0x0000 }, + { 0xc223, 0x0000 }, + { 0xc224, 0x0000 }, + { 0xc225, 0x0000 }, + { 0xc226, 0x0000 }, + { 0xc227, 0x0000 }, + { 0xc228, 0x0000 }, + { 0xc229, 0x0000 }, + { 0xc22a, 0x0000 }, + { 0xc22b, 0x0000 }, + { 0xc22c, 0x0000 }, + { 0xc22d, 0x0000 }, + { 0xc22e, 0x0000 }, + { 0xc22f, 0x0000 }, + { 0xc230, 0x0000 }, + { 0xc231, 0x0000 }, + { 0xc232, 0x0000 }, + { 0xc233, 0x0000 }, + { 0xc234, 0x0000 }, + { 0xc235, 0x0000 }, + { 0xc236, 0x0000 }, + { 0xc237, 0x0000 }, + { 0xc238, 0x0000 }, + { 0xc239, 0x0000 }, + { 0xc23a, 0x0000 }, + { 0xc23b, 0x0000 }, + { 0xc23c, 0x0000 }, + { 0xc23d, 0x0000 }, + { 0xc23e, 0x0000 }, + { 0xc23f, 0x0000 }, + { 0xc240, 0x0000 }, + { 0xc241, 0x0000 }, + { 0xc242, 0x0000 }, + { 0xc243, 0x0000 }, + { 0xc244, 0x0000 }, + { 0xc245, 0x0000 }, + { 0xc246, 0x0000 }, + { 0xc247, 0x0000 }, + { 0xc248, 0x0000 }, + { 0xc249, 0x0000 }, + { 0xc24a, 0x0000 }, + { 0xc24b, 0x0000 }, + { 0xc24c, 0x0000 }, + { 0xc24d, 0x0000 }, + { 0xc24e, 0x0000 }, + { 0xc24f, 0x0000 }, + { 0xc250, 0x0000 }, + { 0xc251, 0x0000 }, + { 0xc252, 0x0000 }, + { 0xc253, 0x0000 }, + { 0xc254, 0x0000 }, + { 0xc255, 0x0000 }, + { 0xc256, 0x0000 }, + { 0xc257, 0x0000 }, + { 0xc258, 0x0000 }, + { 0xc259, 0x0000 }, + { 0xc25a, 0x0000 }, + { 0xc25b, 0x0000 }, + { 0xc25c, 0x0000 }, + { 0xc25d, 0x0000 }, + { 0xc25e, 0x0000 }, + { 0xc25f, 0x0000 }, + { 0xc260, 0x0000 }, + { 0xc261, 0x0000 }, + { 0xc262, 0x0000 }, + { 0xc263, 0x0000 }, + { 0xc264, 0x0000 }, + { 0xc265, 0x0000 }, + { 0xc266, 0x0000 }, + { 0xc267, 0x0000 }, + { 0xc268, 0x0000 }, + { 0xc269, 0x0000 }, + { 0xc26a, 0x0000 }, + { 0xc26b, 0x0000 }, + { 0xc26c, 0x0000 }, + { 0xc26d, 0x0000 }, + { 0xc26e, 0x0000 }, + { 0xc26f, 0x0000 }, + { 0xc270, 0x0000 }, + { 0xc271, 0x0000 }, + { 0xc272, 0x0000 }, + { 0xc273, 0x0000 }, + { 0xc274, 0x0000 }, + { 0xc275, 0x0000 }, + { 0xc276, 0x0000 }, + { 0xc277, 0x0000 }, + { 0xc278, 0x0000 }, + { 0xc279, 0x0000 }, + { 0xc27a, 0x0000 }, + { 0xc27b, 0x0000 }, + { 0xc27c, 0x0000 }, + { 0xc27d, 0x0000 }, + { 0xc27e, 0x0000 }, + { 0xc27f, 0x0000 }, + { 0xc280, 0x0000 }, + { 0xc281, 0x0000 }, + { 0xc282, 0x0000 }, + { 0xc283, 0x0000 }, + { 0xc284, 0x0000 }, + { 0xc285, 0x0000 }, + { 0xc286, 0x0000 }, + { 0xc287, 0x0000 }, + { 0xc288, 0x0000 }, + { 0xc289, 0x0000 }, + { 0xc28a, 0x0000 }, + { 0xc28b, 0x0000 }, + { 0xc28c, 0x0000 }, + { 0xc28d, 0x0000 }, + { 0xc28e, 0x0000 }, + { 0xc28f, 0x0000 }, + { 0xc290, 0x0000 }, + { 0xc291, 0x0000 }, + { 0xc292, 0x0000 }, + { 0xc293, 0x0000 }, + { 0xc294, 0x0000 }, + { 0xc295, 0x0000 }, + { 0xc296, 0x0000 }, + { 0xc297, 0x0000 }, + { 0xc298, 0x0000 }, + { 0xc299, 0x0000 }, + { 0xc29a, 0x0000 }, + { 0xc29b, 0x0000 }, + { 0xc29c, 0x0000 }, + { 0xc29d, 0x0000 }, + { 0xc29e, 0x0000 }, + { 0xc29f, 0x0000 }, + { 0xc2a0, 0x0000 }, + { 0xc2a1, 0x0000 }, + { 0xc2a2, 0x0000 }, + { 0xc2a3, 0x0000 }, + { 0xc2a4, 0x0000 }, + { 0xc2a5, 0x0000 }, + { 0xc2a6, 0x0000 }, + { 0xc2a7, 0x0000 }, + { 0xc2a8, 0x0000 }, + { 0xc2a9, 0x0000 }, + { 0xc2aa, 0x0000 }, + { 0xc2ab, 0x0000 }, + { 0xc2ac, 0x0000 }, + { 0xc2ad, 0x0000 }, + { 0xc2ae, 0x0000 }, + { 0xc2af, 0x0000 }, + { 0xc2b0, 0x0000 }, + { 0xc2b1, 0x0000 }, + { 0xc2b2, 0x0000 }, + { 0xc2b3, 0x0000 }, + { 0xc2b4, 0x0000 }, + { 0xc2b5, 0x0000 }, + { 0xc2b6, 0x0000 }, + { 0xc2b7, 0x0000 }, + { 0xc2b8, 0x0000 }, + { 0xc2b9, 0x0000 }, + { 0xc2ba, 0x0000 }, + { 0xc2bb, 0x0000 }, + { 0xc2bc, 0x0000 }, + { 0xc2bd, 0x0000 }, + { 0xc2be, 0x0000 }, + { 0xc2bf, 0x0000 }, + { 0xc2c0, 0x0000 }, + { 0xc2c1, 0x0000 }, + { 0xc2c2, 0x0000 }, + { 0xc2c3, 0x0000 }, + { 0xc2c4, 0x0000 }, + { 0xc2c5, 0x0000 }, + { 0xc2c6, 0x0000 }, + { 0xc2c7, 0x0000 }, + { 0xc2c8, 0x0000 }, + { 0xc2c9, 0x0000 }, + { 0xc2ca, 0x0000 }, + { 0xc2cb, 0x0000 }, + { 0xc2cc, 0x0000 }, + { 0xc2cd, 0x0000 }, + { 0xc2ce, 0x0000 }, + { 0xc2cf, 0x0000 }, + { 0xc2d0, 0x0000 }, + { 0xc2d1, 0x0000 }, + { 0xc2d2, 0x0000 }, + { 0xc2d3, 0x0000 }, + { 0xc2d4, 0x0000 }, + { 0xc2d5, 0x0000 }, + { 0xc2d6, 0x0000 }, + { 0xc2d7, 0x0000 }, + { 0xc2d8, 0x0000 }, + { 0xc2d9, 0x0000 }, + { 0xc2da, 0x0000 }, + { 0xc2db, 0x0000 }, + { 0xc2dc, 0x0000 }, + { 0xc2dd, 0x0000 }, + { 0xc2de, 0x0000 }, + { 0xc2df, 0x0000 }, + { 0xc2e0, 0x0000 }, + { 0xc2e1, 0x0000 }, + { 0xc2e2, 0x0000 }, + { 0xc2e3, 0x0000 }, + { 0xc2e4, 0x0000 }, + { 0xc2e5, 0x0000 }, + { 0xc2e6, 0x0000 }, + { 0xc2e7, 0x0000 }, + { 0xc2e8, 0x0000 }, + { 0xc2e9, 0x0000 }, + { 0xc2ea, 0x0000 }, + { 0xc2eb, 0x0000 }, + { 0xc2ec, 0x0000 }, + { 0xc2ed, 0x0000 }, + { 0xc2ee, 0x0000 }, + { 0xc2ef, 0x0000 }, + { 0xc2f0, 0x0000 }, + { 0xc2f1, 0x0000 }, + { 0xc2f2, 0x0000 }, + { 0xc2f3, 0x0000 }, + { 0xc2f4, 0x0000 }, + { 0xc2f5, 0x0000 }, + { 0xc2f6, 0x0000 }, + { 0xc2f7, 0x0000 }, + { 0xc2f8, 0x0000 }, + { 0xc2f9, 0x0000 }, + { 0xc2fa, 0x0000 }, + { 0xc2fb, 0x0000 }, + { 0xc2fc, 0x0000 }, + { 0xc2fd, 0x0000 }, + { 0xc2fe, 0x0000 }, + { 0xc2ff, 0x0000 }, + { 0xc300, 0x0000 }, + { 0xc301, 0x0000 }, + { 0xc302, 0x0000 }, + { 0xc303, 0x0000 }, + { 0xc304, 0x0000 }, + { 0xc305, 0x0000 }, + { 0xc306, 0x0000 }, + { 0xc307, 0x0000 }, + { 0xc308, 0x0000 }, + { 0xc309, 0x0000 }, + { 0xc30a, 0x0000 }, + { 0xc30b, 0x0000 }, + { 0xc30c, 0x0000 }, + { 0xc30d, 0x0000 }, + { 0xc30e, 0x0000 }, + { 0xc30f, 0x0000 }, + { 0xc310, 0x0000 }, + { 0xc311, 0x0000 }, + { 0xc312, 0x0000 }, + { 0xc313, 0x0000 }, + { 0xc314, 0x0000 }, + { 0xc315, 0x0000 }, + { 0xc316, 0x0000 }, + { 0xc317, 0x0000 }, + { 0xc318, 0x0000 }, + { 0xc319, 0x0000 }, + { 0xc31a, 0x0000 }, + { 0xc31b, 0x0000 }, + { 0xc31c, 0x0000 }, + { 0xc31d, 0x0000 }, + { 0xc31e, 0x0000 }, + { 0xc31f, 0x0000 }, + { 0xc320, 0x0000 }, + { 0xc321, 0x0000 }, + { 0xc322, 0x0000 }, + { 0xc323, 0x0000 }, + { 0xc324, 0x0000 }, + { 0xc325, 0x0000 }, + { 0xc326, 0x0000 }, + { 0xc327, 0x0000 }, + { 0xc328, 0x0000 }, + { 0xc329, 0x0000 }, + { 0xc32a, 0x0000 }, + { 0xc32b, 0x0000 }, + { 0xc32c, 0x0000 }, + { 0xc32d, 0x0000 }, + { 0xc32e, 0x0000 }, + { 0xc32f, 0x0000 }, + { 0xc330, 0x0000 }, + { 0xc331, 0x0000 }, + { 0xc332, 0x0000 }, + { 0xc333, 0x0000 }, + { 0xc334, 0x0000 }, + { 0xc335, 0x0000 }, + { 0xc336, 0x0000 }, + { 0xc337, 0x0000 }, + { 0xc338, 0x0000 }, + { 0xc339, 0x0000 }, + { 0xc33a, 0x0000 }, + { 0xc33b, 0x0000 }, + { 0xc33c, 0x0000 }, + { 0xc33d, 0x0000 }, + { 0xc33e, 0x0000 }, + { 0xc33f, 0x0000 }, + { 0xc340, 0x0000 }, + { 0xc341, 0x0000 }, + { 0xc342, 0x0000 }, + { 0xc343, 0x0000 }, + { 0xc344, 0x0000 }, + { 0xc345, 0x0000 }, + { 0xc346, 0x0000 }, + { 0xc347, 0x0000 }, + { 0xc348, 0x0000 }, + { 0xc349, 0x0000 }, + { 0xc34a, 0x0000 }, + { 0xc34b, 0x0000 }, + { 0xc34c, 0x0000 }, + { 0xc34d, 0x0000 }, + { 0xc34e, 0x0000 }, + { 0xc34f, 0x0000 }, + { 0xc350, 0x0000 }, + { 0xc351, 0x0000 }, + { 0xc352, 0x0000 }, + { 0xc353, 0x0000 }, + { 0xc354, 0x0000 }, + { 0xc355, 0x0000 }, + { 0xc356, 0x0000 }, + { 0xc357, 0x0000 }, + { 0xc358, 0x0000 }, + { 0xc359, 0x0000 }, + { 0xc35a, 0x0000 }, + { 0xc35b, 0x0000 }, + { 0xc35c, 0x0000 }, + { 0xc35d, 0x0000 }, + { 0xc35e, 0x0000 }, + { 0xc35f, 0x0000 }, + { 0xc360, 0x0000 }, + { 0xc361, 0x0000 }, + { 0xc362, 0x0000 }, + { 0xc363, 0x0000 }, + { 0xc364, 0x0000 }, + { 0xc365, 0x0000 }, + { 0xc366, 0x0000 }, + { 0xc367, 0x0000 }, + { 0xc368, 0x0000 }, + { 0xc369, 0x0000 }, + { 0xc36a, 0x0000 }, + { 0xc36b, 0x0000 }, + { 0xc36c, 0x0000 }, + { 0xc36d, 0x0000 }, + { 0xc36e, 0x0000 }, + { 0xc36f, 0x0000 }, + { 0xc370, 0x0000 }, + { 0xc371, 0x0000 }, + { 0xc372, 0x0000 }, + { 0xc373, 0x0000 }, + { 0xc374, 0x0000 }, + { 0xc375, 0x0000 }, + { 0xc376, 0x0000 }, + { 0xc377, 0x0000 }, + { 0xc378, 0x0000 }, + { 0xc379, 0x0000 }, + { 0xc37a, 0x0000 }, + { 0xc37b, 0x0000 }, + { 0xc37c, 0x0000 }, + { 0xc37d, 0x0000 }, + { 0xc37e, 0x0000 }, + { 0xc37f, 0x0000 }, + { 0xc380, 0x0000 }, + { 0xc381, 0x0000 }, + { 0xc382, 0x0000 }, + { 0xc383, 0x0000 }, + { 0xc384, 0x0000 }, + { 0xc385, 0x0000 }, + { 0xc386, 0x0000 }, + { 0xc387, 0x0000 }, + { 0xc388, 0x0000 }, + { 0xc389, 0x0000 }, + { 0xc38a, 0x0000 }, + { 0xc38b, 0x0000 }, + { 0xc38c, 0x0000 }, + { 0xc38d, 0x0000 }, + { 0xc38e, 0x0000 }, + { 0xc38f, 0x0000 }, + { 0xc390, 0x0000 }, + { 0xc391, 0x0000 }, + { 0xc392, 0x0000 }, + { 0xc393, 0x0000 }, + { 0xc394, 0x0000 }, + { 0xc395, 0x0000 }, + { 0xc396, 0x0000 }, + { 0xc397, 0x0000 }, + { 0xc398, 0x0000 }, + { 0xc399, 0x0000 }, + { 0xc39a, 0x0000 }, + { 0xc39b, 0x0000 }, + { 0xc39c, 0x0000 }, + { 0xc39d, 0x0000 }, + { 0xc39e, 0x0000 }, + { 0xc39f, 0x0000 }, + { 0xc3a0, 0x0000 }, + { 0xc3a1, 0x0000 }, + { 0xc3a2, 0x0000 }, + { 0xc3a3, 0x0000 }, + { 0xc3a4, 0x0000 }, + { 0xc3a5, 0x0000 }, + { 0xc3a6, 0x0000 }, + { 0xc3a7, 0x0000 }, + { 0xc3a8, 0x0000 }, + { 0xc3a9, 0x0000 }, + { 0xc3aa, 0x0000 }, + { 0xc3ab, 0x0000 }, + { 0xc3ac, 0x0000 }, + { 0xc3ad, 0x0000 }, + { 0xc3ae, 0x0000 }, + { 0xc3af, 0x0000 }, + { 0xc3b0, 0x0000 }, + { 0xc3b1, 0x0000 }, + { 0xc3b2, 0x0000 }, + { 0xc3b3, 0x0000 }, + { 0xc3b4, 0x0000 }, + { 0xc3b5, 0x0000 }, + { 0xc3b6, 0x0000 }, + { 0xc3b7, 0x0000 }, + { 0xc3b8, 0x0000 }, + { 0xc3b9, 0x0000 }, + { 0xc3ba, 0x0000 }, + { 0xc3bb, 0x0000 }, + { 0xc3bc, 0x0000 }, + { 0xc3bd, 0x0000 }, + { 0xc3be, 0x0000 }, + { 0xc3bf, 0x0000 }, + { 0xc3c0, 0x0000 }, + { 0xc3c1, 0x0000 }, + { 0xc3c2, 0x0000 }, + { 0xc3c3, 0x0000 }, + { 0xc3c4, 0x0000 }, + { 0xc3c5, 0x0000 }, + { 0xc3c6, 0x0000 }, + { 0xc3c7, 0x0000 }, + { 0xc3c8, 0x0000 }, + { 0xc3c9, 0x0000 }, + { 0xc3ca, 0x0000 }, + { 0xc3cb, 0x0000 }, + { 0xc3cc, 0x0000 }, + { 0xc3cd, 0x0000 }, + { 0xc3ce, 0x0000 }, + { 0xc3cf, 0x0000 }, + { 0xc3d0, 0x0000 }, + { 0xc3d1, 0x0000 }, + { 0xc3d2, 0x0000 }, + { 0xc3d3, 0x0000 }, + { 0xc3d4, 0x0000 }, + { 0xc3d5, 0x0000 }, + { 0xc3d6, 0x0000 }, + { 0xc3d7, 0x0000 }, + { 0xc3d8, 0x0000 }, + { 0xc3d9, 0x0000 }, + { 0xc3da, 0x0000 }, + { 0xc3db, 0x0000 }, + { 0xc3dc, 0x0000 }, + { 0xc3dd, 0x0000 }, + { 0xc3de, 0x0000 }, + { 0xc3df, 0x0000 }, + { 0xc3e0, 0x0000 }, + { 0xc3e1, 0x0000 }, + { 0xc3e2, 0x0000 }, + { 0xc3e3, 0x0000 }, + { 0xc3e4, 0x0000 }, + { 0xc3e5, 0x0000 }, + { 0xc3e6, 0x0000 }, + { 0xc3e7, 0x0000 }, + { 0xc3e8, 0x0000 }, + { 0xc3e9, 0x0000 }, + { 0xc3ea, 0x0000 }, + { 0xc3eb, 0x0000 }, + { 0xc3ec, 0x0000 }, + { 0xc3ed, 0x0000 }, + { 0xc3ee, 0x0000 }, + { 0xc3ef, 0x0000 }, + { 0xc3f0, 0x0000 }, + { 0xc3f1, 0x0000 }, + { 0xc3f2, 0x0000 }, + { 0xc3f3, 0x0000 }, + { 0xc3f4, 0x0000 }, + { 0xc3f5, 0x0000 }, + { 0xc3f6, 0x0000 }, + { 0xc3f7, 0x0000 }, + { 0xc3f8, 0x0000 }, + { 0xc3f9, 0x0000 }, + { 0xc3fa, 0x0000 }, + { 0xc3fb, 0x0000 }, + { 0xc3fc, 0x0000 }, + { 0xc3fd, 0x0000 }, + { 0xc3fe, 0x0000 }, + { 0xc3ff, 0x0000 }, + { 0xc400, 0x0000 }, + { 0xc401, 0x0000 }, + { 0xc402, 0x0000 }, + { 0xc403, 0x0000 }, + { 0xc404, 0x0000 }, + { 0xc405, 0x0000 }, + { 0xc406, 0x0000 }, + { 0xc407, 0x0000 }, + { 0xc408, 0x0000 }, + { 0xc409, 0x0000 }, + { 0xc40a, 0x0000 }, + { 0xc40b, 0x0000 }, + { 0xc40c, 0x0000 }, + { 0xc40d, 0x0000 }, + { 0xc40e, 0x0000 }, + { 0xc40f, 0x0000 }, + { 0xc410, 0x0000 }, + { 0xc411, 0x0000 }, + { 0xc412, 0x0000 }, + { 0xc413, 0x0000 }, + { 0xc414, 0x0000 }, + { 0xc415, 0x0000 }, + { 0xc416, 0x0000 }, + { 0xc417, 0x0000 }, + { 0xc418, 0x0000 }, + { 0xc419, 0x0000 }, + { 0xc41a, 0x0000 }, + { 0xc41b, 0x0000 }, + { 0xc41c, 0x0000 }, + { 0xc41d, 0x0000 }, + { 0xc41e, 0x0000 }, + { 0xc41f, 0x0000 }, + { 0xc420, 0x0000 }, + { 0xc421, 0x0000 }, + { 0xc422, 0x0000 }, + { 0xc423, 0x0000 }, + { 0xc424, 0x0000 }, + { 0xc425, 0x0000 }, + { 0xc426, 0x0000 }, + { 0xc427, 0x0000 }, + { 0xc428, 0x0000 }, + { 0xc429, 0x0000 }, + { 0xc42a, 0x0000 }, + { 0xc42b, 0x0000 }, + { 0xc42c, 0x0000 }, + { 0xc42d, 0x0000 }, + { 0xc42e, 0x0000 }, + { 0xc42f, 0x0000 }, + { 0xc430, 0x0000 }, + { 0xc431, 0x0000 }, + { 0xc432, 0x0000 }, + { 0xc433, 0x0000 }, + { 0xc434, 0x0000 }, + { 0xc435, 0x0000 }, + { 0xc436, 0x0000 }, + { 0xc437, 0x0000 }, + { 0xc438, 0x0000 }, + { 0xc439, 0x0000 }, + { 0xc43a, 0x0000 }, + { 0xc43b, 0x0000 }, + { 0xc43c, 0x0000 }, + { 0xc43d, 0x0000 }, + { 0xc43e, 0x0000 }, + { 0xc43f, 0x0000 }, + { 0xc440, 0x0000 }, + { 0xc441, 0x0000 }, + { 0xc442, 0x0000 }, + { 0xc443, 0x0000 }, + { 0xc444, 0x0000 }, + { 0xc445, 0x0000 }, + { 0xc446, 0x0000 }, + { 0xc447, 0x0000 }, + { 0xc448, 0x0000 }, + { 0xc449, 0x0000 }, + { 0xc44a, 0x0000 }, + { 0xc44b, 0x0000 }, + { 0xc44c, 0x0000 }, + { 0xc44d, 0x0000 }, + { 0xc44e, 0x0000 }, + { 0xc44f, 0x0000 }, + { 0xc450, 0x0000 }, + { 0xc451, 0x0000 }, + { 0xc452, 0x0000 }, + { 0xc453, 0x0000 }, + { 0xc454, 0x0000 }, + { 0xc455, 0x0000 }, + { 0xc456, 0x0000 }, + { 0xc457, 0x0000 }, + { 0xc458, 0x0000 }, + { 0xc459, 0x0000 }, + { 0xc45a, 0x0000 }, + { 0xc45b, 0x0000 }, + { 0xc45c, 0x0000 }, + { 0xc45d, 0x0000 }, + { 0xc45e, 0x0000 }, + { 0xc45f, 0x0000 }, + { 0xc460, 0x0000 }, + { 0xc461, 0x0000 }, + { 0xc462, 0x0000 }, + { 0xc463, 0x0000 }, + { 0xc464, 0x0000 }, + { 0xc465, 0x0000 }, + { 0xc466, 0x0000 }, + { 0xc467, 0x0000 }, + { 0xc468, 0x0000 }, + { 0xc469, 0x0000 }, + { 0xc46a, 0x0000 }, + { 0xc46b, 0x0000 }, + { 0xc46c, 0x0000 }, + { 0xc46d, 0x0000 }, + { 0xc46e, 0x0000 }, + { 0xc46f, 0x0000 }, + { 0xc470, 0x0000 }, + { 0xc471, 0x0000 }, + { 0xc472, 0x0000 }, + { 0xc473, 0x0000 }, + { 0xc474, 0x0000 }, + { 0xc475, 0x0000 }, + { 0xc476, 0x0000 }, + { 0xc477, 0x0000 }, + { 0xc478, 0x0000 }, + { 0xc479, 0x0000 }, + { 0xc47a, 0x0000 }, + { 0xc47b, 0x0000 }, + { 0xc47c, 0x0000 }, + { 0xc47d, 0x0000 }, + { 0xc47e, 0x0000 }, + { 0xc47f, 0x0000 }, + { 0xc480, 0x0000 }, + { 0xc481, 0x0000 }, + { 0xc482, 0x0000 }, + { 0xc483, 0x0000 }, + { 0xc484, 0x0000 }, + { 0xc485, 0x0000 }, + { 0xc486, 0x0000 }, + { 0xc487, 0x0000 }, + { 0xc488, 0x0000 }, + { 0xc489, 0x0000 }, + { 0xc48a, 0x0000 }, + { 0xc48b, 0x0000 }, + { 0xc48c, 0x0000 }, + { 0xc48d, 0x0000 }, + { 0xc48e, 0x0000 }, + { 0xc48f, 0x0000 }, + { 0xc490, 0x0000 }, + { 0xc491, 0x0000 }, + { 0xc492, 0x0000 }, + { 0xc493, 0x0000 }, + { 0xc494, 0x0000 }, + { 0xc495, 0x0000 }, + { 0xc496, 0x0000 }, + { 0xc497, 0x0000 }, + { 0xc498, 0x0000 }, + { 0xc499, 0x0000 }, + { 0xc49a, 0x0000 }, + { 0xc49b, 0x0000 }, + { 0xc49c, 0x0000 }, + { 0xc49d, 0x0000 }, + { 0xc49e, 0x0000 }, + { 0xc49f, 0x0000 }, + { 0xc4a0, 0x0000 }, + { 0xc4a1, 0x0000 }, + { 0xc4a2, 0x0000 }, + { 0xc4a3, 0x0000 }, + { 0xc4a4, 0x0000 }, + { 0xc4a5, 0x0000 }, + { 0xc4a6, 0x0000 }, + { 0xc4a7, 0x0000 }, + { 0xc4a8, 0x0000 }, + { 0xc4a9, 0x0000 }, + { 0xc4aa, 0x0000 }, + { 0xc4ab, 0x0000 }, + { 0xc4ac, 0x0000 }, + { 0xc4ad, 0x0000 }, + { 0xc4ae, 0x0000 }, + { 0xc4af, 0x0000 }, + { 0xc4b0, 0x0000 }, + { 0xc4b1, 0x0000 }, + { 0xc4b2, 0x0000 }, + { 0xc4b3, 0x0000 }, + { 0xc4b4, 0x0000 }, + { 0xc4b5, 0x0000 }, + { 0xc4b6, 0x0000 }, + { 0xc4b7, 0x0000 }, + { 0xc4b8, 0x0000 }, + { 0xc4b9, 0x0000 }, + { 0xc4ba, 0x0000 }, + { 0xc4bb, 0x0000 }, + { 0xc4bc, 0x0000 }, + { 0xc4bd, 0x0000 }, + { 0xc4be, 0x0000 }, + { 0xc4bf, 0x0000 }, + { 0xc4c0, 0x0000 }, + { 0xc4c1, 0x0000 }, + { 0xc4c2, 0x0000 }, + { 0xc4c3, 0x0000 }, + { 0xc4c4, 0x0000 }, + { 0xc4c5, 0x0000 }, + { 0xc4c6, 0x0000 }, + { 0xc4c7, 0x0000 }, + { 0xc4c8, 0x0000 }, + { 0xc4c9, 0x0000 }, + { 0xc4ca, 0x0000 }, + { 0xc4cb, 0x0000 }, + { 0xc4cc, 0x0000 }, + { 0xc4cd, 0x0000 }, + { 0xc4ce, 0x0000 }, + { 0xc4cf, 0x0000 }, + { 0xc4d0, 0x0000 }, + { 0xc4d1, 0x0000 }, + { 0xc4d2, 0x0000 }, + { 0xc4d3, 0x0000 }, + { 0xc4d4, 0x0000 }, + { 0xc4d5, 0x0000 }, + { 0xc4d6, 0x0000 }, + { 0xc4d7, 0x0000 }, + { 0xc4d8, 0x0000 }, + { 0xc4d9, 0x0000 }, + { 0xc4da, 0x0000 }, + { 0xc4db, 0x0000 }, + { 0xc4dc, 0x0000 }, + { 0xc4dd, 0x0000 }, + { 0xc4de, 0x0000 }, + { 0xc4df, 0x0000 }, + { 0xc4e0, 0x0000 }, + { 0xc4e1, 0x0000 }, + { 0xc4e2, 0x0000 }, + { 0xc4e3, 0x0000 }, + { 0xc4e4, 0x0000 }, + { 0xc4e5, 0x0000 }, + { 0xc4e6, 0x0000 }, + { 0xc4e7, 0x0000 }, + { 0xc4e8, 0x0000 }, + { 0xc4e9, 0x0000 }, + { 0xc4ea, 0x0000 }, + { 0xc4eb, 0x0000 }, + { 0xc4ec, 0x0000 }, + { 0xc4ed, 0x0000 }, + { 0xc4ee, 0x0000 }, + { 0xc4ef, 0x0000 }, + { 0xc4f0, 0x0000 }, + { 0xc4f1, 0x0000 }, + { 0xc4f2, 0x0000 }, + { 0xc4f3, 0x0000 }, + { 0xc4f4, 0x0000 }, + { 0xc4f5, 0x0000 }, + { 0xc4f6, 0x0000 }, + { 0xc4f7, 0x0000 }, + { 0xc4f8, 0x0000 }, + { 0xc4f9, 0x0000 }, + { 0xc4fa, 0x0000 }, + { 0xc4fb, 0x0000 }, + { 0xc4fc, 0x0000 }, + { 0xc4fd, 0x0000 }, + { 0xc4fe, 0x0000 }, + { 0xc4ff, 0x0000 }, + { 0xc500, 0x0000 }, + { 0xc501, 0x0000 }, + { 0xc502, 0x0000 }, + { 0xc503, 0x0000 }, + { 0xc504, 0x0000 }, + { 0xc505, 0x0000 }, + { 0xc506, 0x0000 }, + { 0xc507, 0x0000 }, + { 0xc508, 0x0000 }, + { 0xc509, 0x0000 }, + { 0xc50a, 0x0000 }, + { 0xc50b, 0x0000 }, + { 0xc50c, 0x0000 }, + { 0xc50d, 0x0000 }, + { 0xc50e, 0x0000 }, + { 0xc50f, 0x0000 }, + { 0xc510, 0x0000 }, + { 0xc511, 0x0000 }, + { 0xc512, 0x0000 }, + { 0xc513, 0x0000 }, + { 0xc514, 0x0000 }, + { 0xc515, 0x0000 }, + { 0xc516, 0x0000 }, + { 0xc517, 0x0000 }, + { 0xc518, 0x0000 }, + { 0xc519, 0x0000 }, + { 0xc51a, 0x0000 }, + { 0xc51b, 0x0000 }, + { 0xc51c, 0x0000 }, + { 0xc51d, 0x0000 }, + { 0xc51e, 0x0000 }, + { 0xc51f, 0x0000 }, + { 0xc520, 0x0000 }, + { 0xc521, 0x0000 }, + { 0xc522, 0x0000 }, + { 0xc523, 0x0000 }, + { 0xc524, 0x0000 }, + { 0xc525, 0x0000 }, + { 0xc526, 0x0000 }, + { 0xc527, 0x0000 }, + { 0xc528, 0x0000 }, + { 0xc529, 0x0000 }, + { 0xc52a, 0x0000 }, + { 0xc52b, 0x0000 }, + { 0xc52c, 0x0000 }, + { 0xc52d, 0x0000 }, + { 0xc52e, 0x0000 }, + { 0xc52f, 0x0000 }, + { 0xc530, 0x0000 }, + { 0xc531, 0x0000 }, + { 0xc532, 0x0000 }, + { 0xc533, 0x0000 }, + { 0xc534, 0x0000 }, + { 0xc535, 0x0000 }, + { 0xc536, 0x0000 }, + { 0xc537, 0x0000 }, + { 0xc538, 0x0000 }, + { 0xc539, 0x0000 }, + { 0xc53a, 0x0000 }, + { 0xc53b, 0x0000 }, + { 0xc53c, 0x0000 }, + { 0xc53d, 0x0000 }, + { 0xc53e, 0x0000 }, + { 0xc53f, 0x0000 }, + { 0xc540, 0x0000 }, + { 0xc541, 0x0000 }, + { 0xc542, 0x0000 }, + { 0xc543, 0x0000 }, + { 0xc544, 0x0000 }, + { 0xc545, 0x0000 }, + { 0xc546, 0x0000 }, + { 0xc547, 0x0000 }, + { 0xc548, 0x0000 }, + { 0xc549, 0x0000 }, + { 0xc54a, 0x0000 }, + { 0xc54b, 0x0000 }, + { 0xc54c, 0x0000 }, + { 0xc54d, 0x0000 }, + { 0xc54e, 0x0000 }, + { 0xc54f, 0x0000 }, + { 0xc550, 0x0000 }, + { 0xc551, 0x0000 }, + { 0xc552, 0x0000 }, + { 0xc553, 0x0000 }, + { 0xc554, 0x0000 }, + { 0xc555, 0x0000 }, + { 0xc556, 0x0000 }, + { 0xc557, 0x0000 }, + { 0xc558, 0x0000 }, + { 0xc559, 0x0000 }, + { 0xc55a, 0x0000 }, + { 0xc55b, 0x0000 }, + { 0xc55c, 0x0000 }, + { 0xc55d, 0x0000 }, + { 0xc55e, 0x0000 }, + { 0xc55f, 0x0000 }, + { 0xc560, 0x0000 }, + { 0xc561, 0x0000 }, + { 0xc562, 0x0000 }, + { 0xc563, 0x0000 }, + { 0xc564, 0x0000 }, + { 0xc565, 0x0000 }, + { 0xc566, 0x0000 }, + { 0xc567, 0x0000 }, + { 0xc568, 0x0000 }, + { 0xc569, 0x0000 }, + { 0xc56a, 0x0000 }, + { 0xc56b, 0x0000 }, + { 0xc56c, 0x0000 }, + { 0xc56d, 0x0000 }, + { 0xc56e, 0x0000 }, + { 0xc56f, 0x0000 }, + { 0xc570, 0x0000 }, + { 0xc571, 0x0000 }, + { 0xc572, 0x0000 }, + { 0xc573, 0x0000 }, + { 0xc574, 0x0000 }, + { 0xc575, 0x0000 }, + { 0xc576, 0x0000 }, + { 0xc577, 0x0000 }, + { 0xc578, 0x0000 }, + { 0xc579, 0x0000 }, + { 0xc57a, 0x0000 }, + { 0xc57b, 0x0000 }, + { 0xc57c, 0x0000 }, + { 0xc57d, 0x0000 }, + { 0xc57e, 0x0000 }, + { 0xc57f, 0x0000 }, + { 0xc580, 0x0000 }, + { 0xc581, 0x0000 }, + { 0xc582, 0x0000 }, + { 0xc583, 0x0000 }, + { 0xc584, 0x0000 }, + { 0xc585, 0x0000 }, + { 0xc586, 0x0000 }, + { 0xc587, 0x0000 }, + { 0xc588, 0x0000 }, + { 0xc589, 0x0000 }, + { 0xc58a, 0x0000 }, + { 0xc58b, 0x0000 }, + { 0xc58c, 0x0000 }, + { 0xc58d, 0x0000 }, + { 0xc58e, 0x0000 }, + { 0xc58f, 0x0000 }, + { 0xc590, 0x0000 }, + { 0xc591, 0x0000 }, + { 0xc592, 0x0000 }, + { 0xc593, 0x0000 }, + { 0xc594, 0x0000 }, + { 0xc595, 0x0000 }, + { 0xc596, 0x0000 }, + { 0xc597, 0x0000 }, + { 0xc598, 0x0000 }, + { 0xc599, 0x0000 }, + { 0xc59a, 0x0000 }, + { 0xc59b, 0x0000 }, + { 0xc59c, 0x0000 }, + { 0xc59d, 0x0000 }, + { 0xc59e, 0x0000 }, + { 0xc59f, 0x0000 }, + { 0xc5a0, 0x0000 }, + { 0xc5a1, 0x0000 }, + { 0xc5a2, 0x0000 }, + { 0xc5a3, 0x0000 }, + { 0xc5a4, 0x0000 }, + { 0xc5a5, 0x0000 }, + { 0xc5a6, 0x0000 }, + { 0xc5a7, 0x0000 }, + { 0xc5a8, 0x0000 }, + { 0xc5a9, 0x0000 }, + { 0xc5aa, 0x0000 }, + { 0xc5ab, 0x0000 }, + { 0xc5ac, 0x0000 }, + { 0xc5ad, 0x0000 }, + { 0xc5ae, 0x0000 }, + { 0xc5af, 0x0000 }, + { 0xc5b0, 0x0000 }, + { 0xc5b1, 0x0000 }, + { 0xc5b2, 0x0000 }, + { 0xc5b3, 0x0000 }, + { 0xc5b4, 0x0000 }, + { 0xc5b5, 0x0000 }, + { 0xc5b6, 0x0000 }, + { 0xc5b7, 0x0000 }, + { 0xc5b8, 0x0000 }, + { 0xc5b9, 0x0000 }, + { 0xc5ba, 0x0000 }, + { 0xc5bb, 0x0000 }, + { 0xc5bc, 0x0000 }, + { 0xc5bd, 0x0000 }, + { 0xc5be, 0x0000 }, + { 0xc5bf, 0x0000 }, + { 0xc5c0, 0x0000 }, + { 0xc5c1, 0x0000 }, + { 0xc5c2, 0x0000 }, + { 0xc5c3, 0x0000 }, + { 0xc5c4, 0x0000 }, + { 0xc5c5, 0x0000 }, + { 0xc5c6, 0x0000 }, + { 0xc5c7, 0x0000 }, + { 0xc5c8, 0x0000 }, + { 0xc5c9, 0x0000 }, + { 0xc5ca, 0x0000 }, + { 0xc5cb, 0x0000 }, + { 0xc5cc, 0x0000 }, + { 0xc5cd, 0x0000 }, + { 0xc5ce, 0x0000 }, + { 0xc5cf, 0x0000 }, + { 0xc5d0, 0x0000 }, + { 0xc5d1, 0x0000 }, + { 0xc5d2, 0x0000 }, + { 0xc5d3, 0x0000 }, + { 0xc5d4, 0x0000 }, + { 0xc5d5, 0x0000 }, + { 0xc5d6, 0x0000 }, + { 0xc5d7, 0x0000 }, + { 0xc5d8, 0x0000 }, + { 0xc5d9, 0x0000 }, + { 0xc5da, 0x0000 }, + { 0xc5db, 0x0000 }, + { 0xc5dc, 0x0000 }, + { 0xc5dd, 0x0000 }, + { 0xc5de, 0x0000 }, + { 0xc5df, 0x0000 }, + { 0xc5e0, 0x0000 }, + { 0xc5e1, 0x0000 }, + { 0xc5e2, 0x0000 }, + { 0xc5e3, 0x0000 }, + { 0xc5e4, 0x0000 }, + { 0xc5e5, 0x0000 }, + { 0xc5e6, 0x0000 }, + { 0xc5e7, 0x0000 }, + { 0xc5e8, 0x0000 }, + { 0xc5e9, 0x0000 }, + { 0xc5ea, 0x0000 }, + { 0xc5eb, 0x0000 }, + { 0xc5ec, 0x0000 }, + { 0xc5ed, 0x0000 }, + { 0xc5ee, 0x0000 }, + { 0xc5ef, 0x0000 }, + { 0xc5f0, 0x0000 }, + { 0xc5f1, 0x0000 }, + { 0xc5f2, 0x0000 }, + { 0xc5f3, 0x0000 }, + { 0xc5f4, 0x0000 }, + { 0xc5f5, 0x0000 }, + { 0xc5f6, 0x0000 }, + { 0xc5f7, 0x0000 }, + { 0xc5f8, 0x0000 }, + { 0xc5f9, 0x0000 }, + { 0xc5fa, 0x0000 }, + { 0xc5fb, 0x0000 }, + { 0xc5fc, 0x0000 }, + { 0xc5fd, 0x0000 }, + { 0xc5fe, 0x0000 }, + { 0xc5ff, 0x0000 }, + { 0xc600, 0x0000 }, + { 0xc601, 0x0000 }, + { 0xc602, 0x0000 }, + { 0xc603, 0x0000 }, + { 0xc604, 0x0000 }, + { 0xc605, 0x0000 }, + { 0xc606, 0x0000 }, + { 0xc607, 0x0000 }, + { 0xc608, 0x0000 }, + { 0xc609, 0x0000 }, + { 0xc60a, 0x0000 }, + { 0xc60b, 0x0000 }, + { 0xc60c, 0x0000 }, + { 0xc60d, 0x0000 }, + { 0xc60e, 0x0000 }, + { 0xc60f, 0x0000 }, + { 0xc610, 0x0000 }, + { 0xc611, 0x0000 }, + { 0xc612, 0x0000 }, + { 0xc613, 0x0000 }, + { 0xc614, 0x0000 }, + { 0xc615, 0x0000 }, + { 0xc616, 0x0000 }, + { 0xc617, 0x0000 }, + { 0xc618, 0x0000 }, + { 0xc619, 0x0000 }, + { 0xc61a, 0x0000 }, + { 0xc61b, 0x0000 }, + { 0xc61c, 0x0000 }, + { 0xc61d, 0x0000 }, + { 0xc61e, 0x0000 }, + { 0xc61f, 0x0000 }, + { 0xc620, 0x0000 }, + { 0xc621, 0x0000 }, + { 0xc622, 0x0000 }, + { 0xc623, 0x0000 }, + { 0xc624, 0x0000 }, + { 0xc625, 0x0000 }, + { 0xc626, 0x0000 }, + { 0xc627, 0x0000 }, + { 0xc628, 0x0000 }, + { 0xc629, 0x0000 }, + { 0xc62a, 0x0000 }, + { 0xc62b, 0x0000 }, + { 0xc62c, 0x0000 }, + { 0xc62d, 0x0000 }, + { 0xc62e, 0x0000 }, + { 0xc62f, 0x0000 }, + { 0xc630, 0x0000 }, + { 0xc631, 0x0000 }, + { 0xc632, 0x0000 }, + { 0xc633, 0x0000 }, + { 0xc634, 0x0000 }, + { 0xc635, 0x0000 }, + { 0xc636, 0x0000 }, + { 0xc637, 0x0000 }, + { 0xc638, 0x0000 }, + { 0xc639, 0x0000 }, + { 0xc63a, 0x0000 }, + { 0xc63b, 0x0000 }, + { 0xc63c, 0x0000 }, + { 0xc63d, 0x0000 }, + { 0xc63e, 0x0000 }, + { 0xc63f, 0x0000 }, + { 0xc640, 0x0000 }, + { 0xc641, 0x0000 }, + { 0xc642, 0x0000 }, + { 0xc643, 0x0000 }, + { 0xc644, 0x0000 }, + { 0xc645, 0x0000 }, + { 0xc646, 0x0000 }, + { 0xc647, 0x0000 }, + { 0xc648, 0x0000 }, + { 0xc649, 0x0000 }, + { 0xc64a, 0x0000 }, + { 0xc64b, 0x0000 }, + { 0xc64c, 0x0000 }, + { 0xc64d, 0x0000 }, + { 0xc64e, 0x0000 }, + { 0xc64f, 0x0000 }, + { 0xc650, 0x0000 }, + { 0xc651, 0x0000 }, + { 0xc652, 0x0000 }, + { 0xc653, 0x0000 }, + { 0xc654, 0x0000 }, + { 0xc655, 0x0000 }, + { 0xc656, 0x0000 }, + { 0xc657, 0x0000 }, + { 0xc658, 0x0000 }, + { 0xc659, 0x0000 }, + { 0xc65a, 0x0000 }, + { 0xc65b, 0x0000 }, + { 0xc65c, 0x0000 }, + { 0xc65d, 0x0000 }, + { 0xc65e, 0x0000 }, + { 0xc65f, 0x0000 }, + { 0xc660, 0x0000 }, + { 0xc661, 0x0000 }, + { 0xc662, 0x0000 }, + { 0xc663, 0x0000 }, + { 0xc664, 0x0000 }, + { 0xc665, 0x0000 }, + { 0xc666, 0x0000 }, + { 0xc667, 0x0000 }, + { 0xc668, 0x0000 }, + { 0xc669, 0x0000 }, + { 0xc66a, 0x0000 }, + { 0xc66b, 0x0000 }, + { 0xc66c, 0x0000 }, + { 0xc66d, 0x0000 }, + { 0xc66e, 0x0000 }, + { 0xc66f, 0x0000 }, + { 0xc670, 0x0000 }, + { 0xc671, 0x0000 }, + { 0xc672, 0x0000 }, + { 0xc673, 0x0000 }, + { 0xc674, 0x0000 }, + { 0xc675, 0x0000 }, + { 0xc676, 0x0000 }, + { 0xc677, 0x0000 }, + { 0xc678, 0x0000 }, + { 0xc679, 0x0000 }, + { 0xc67a, 0x0000 }, + { 0xc67b, 0x0000 }, + { 0xc67c, 0x0000 }, + { 0xc67d, 0x0000 }, + { 0xc67e, 0x0000 }, + { 0xc67f, 0x0000 }, + { 0xc680, 0x0000 }, + { 0xc681, 0x0000 }, + { 0xc682, 0x0000 }, + { 0xc683, 0x0000 }, + { 0xc684, 0x0000 }, + { 0xc685, 0x0000 }, + { 0xc686, 0x0000 }, + { 0xc687, 0x0000 }, + { 0xc688, 0x0000 }, + { 0xc689, 0x0000 }, + { 0xc68a, 0x0000 }, + { 0xc68b, 0x0000 }, + { 0xc68c, 0x0000 }, + { 0xc68d, 0x0000 }, + { 0xc68e, 0x0000 }, + { 0xc68f, 0x0000 }, + { 0xc690, 0x0000 }, + { 0xc691, 0x0000 }, + { 0xc692, 0x0000 }, + { 0xc693, 0x0000 }, + { 0xc694, 0x0000 }, + { 0xc695, 0x0000 }, + { 0xc696, 0x0000 }, + { 0xc697, 0x0000 }, + { 0xc698, 0x0000 }, + { 0xc699, 0x0000 }, + { 0xc69a, 0x0000 }, + { 0xc69b, 0x0000 }, + { 0xc69c, 0x0000 }, + { 0xc69d, 0x0000 }, + { 0xc69e, 0x0000 }, + { 0xc69f, 0x0000 }, + { 0xc6a0, 0x0000 }, + { 0xc6a1, 0x0000 }, + { 0xc6a2, 0x0000 }, + { 0xc6a3, 0x0000 }, + { 0xc6a4, 0x0000 }, + { 0xc6a5, 0x0000 }, + { 0xc6a6, 0x0000 }, + { 0xc6a7, 0x0000 }, + { 0xc6a8, 0x0000 }, + { 0xc6a9, 0x0000 }, + { 0xc6aa, 0x0000 }, + { 0xc6ab, 0x0000 }, + { 0xc6ac, 0x0000 }, + { 0xc6ad, 0x0000 }, + { 0xc6ae, 0x0000 }, + { 0xc6af, 0x0000 }, + { 0xc6b0, 0x0000 }, + { 0xc6b1, 0x0000 }, + { 0xc6b2, 0x0000 }, + { 0xc6b3, 0x0000 }, + { 0xc6b4, 0x0000 }, + { 0xc6b5, 0x0000 }, + { 0xc6b6, 0x0000 }, + { 0xc6b7, 0x0000 }, + { 0xc6b8, 0x0000 }, + { 0xc6b9, 0x0000 }, + { 0xc6ba, 0x0000 }, + { 0xc6bb, 0x0000 }, + { 0xc6bc, 0x0000 }, + { 0xc6bd, 0x0000 }, + { 0xc6be, 0x0000 }, + { 0xc6bf, 0x0000 }, + { 0xc6c0, 0x0000 }, + { 0xc6c1, 0x0000 }, + { 0xc6c2, 0x0000 }, + { 0xc6c3, 0x0000 }, + { 0xc6c4, 0x0000 }, + { 0xc6c5, 0x0000 }, + { 0xc6c6, 0x0000 }, + { 0xc6c7, 0x0000 }, + { 0xc6c8, 0x0000 }, + { 0xc6c9, 0x0000 }, + { 0xc6ca, 0x0000 }, + { 0xc6cb, 0x0000 }, + { 0xc6cc, 0x0000 }, + { 0xc6cd, 0x0000 }, + { 0xc6ce, 0x0000 }, + { 0xc6cf, 0x0000 }, + { 0xc6d0, 0x0000 }, + { 0xc6d1, 0x0000 }, + { 0xc6d2, 0x0000 }, + { 0xc6d3, 0x0000 }, + { 0xc6d4, 0x0000 }, + { 0xc6d5, 0x0000 }, + { 0xc6d6, 0x0000 }, + { 0xc6d7, 0x0000 }, + { 0xc6d8, 0x0000 }, + { 0xc6d9, 0x0000 }, + { 0xc6da, 0x0000 }, + { 0xc6db, 0x0000 }, + { 0xc6dc, 0x0000 }, + { 0xc6dd, 0x0000 }, + { 0xc6de, 0x0000 }, + { 0xc6df, 0x0000 }, + { 0xc6e0, 0x0000 }, + { 0xc6e1, 0x0000 }, + { 0xc6e2, 0x0000 }, + { 0xc6e3, 0x0000 }, + { 0xc6e4, 0x0000 }, + { 0xc6e5, 0x0000 }, + { 0xc6e6, 0x0000 }, + { 0xc6e7, 0x0000 }, + { 0xc6e8, 0x0000 }, + { 0xc6e9, 0x0000 }, + { 0xc6ea, 0x0000 }, + { 0xc6eb, 0x0000 }, + { 0xc6ec, 0x0000 }, + { 0xc6ed, 0x0000 }, + { 0xc6ee, 0x0000 }, + { 0xc6ef, 0x0000 }, + { 0xc6f0, 0x0000 }, + { 0xc6f1, 0x0000 }, + { 0xc6f2, 0x0000 }, + { 0xc6f3, 0x0000 }, + { 0xc6f4, 0x0000 }, + { 0xc6f5, 0x0000 }, + { 0xc6f6, 0x0000 }, + { 0xc6f7, 0x0000 }, + { 0xc6f8, 0x0000 }, + { 0xc6f9, 0x0000 }, + { 0xc6fa, 0x0000 }, + { 0xc6fb, 0x0000 }, + { 0xc6fc, 0x0000 }, + { 0xc6fd, 0x0000 }, + { 0xc6fe, 0x0000 }, + { 0xc6ff, 0x0000 }, + { 0xc700, 0x0000 }, + { 0xc701, 0x0000 }, + { 0xc702, 0x0000 }, + { 0xc703, 0x0000 }, + { 0xc704, 0x0000 }, + { 0xc705, 0x0000 }, + { 0xc706, 0x0000 }, + { 0xc707, 0x0000 }, + { 0xc708, 0x0000 }, + { 0xc709, 0x0000 }, + { 0xc70a, 0x0000 }, + { 0xc70b, 0x0000 }, + { 0xc70c, 0x0000 }, + { 0xc70d, 0x0000 }, + { 0xc70e, 0x0000 }, + { 0xc70f, 0x0000 }, + { 0xc710, 0x0000 }, + { 0xc711, 0x0000 }, + { 0xc712, 0x0000 }, + { 0xc713, 0x0000 }, + { 0xc714, 0x0000 }, + { 0xc715, 0x0000 }, + { 0xc716, 0x0000 }, + { 0xc717, 0x0000 }, + { 0xc718, 0x0000 }, + { 0xc719, 0x0000 }, + { 0xc71a, 0x0000 }, + { 0xc71b, 0x0000 }, + { 0xc71c, 0x0000 }, + { 0xc71d, 0x0000 }, + { 0xc71e, 0x0000 }, + { 0xc71f, 0x0000 }, + { 0xc720, 0x0000 }, + { 0xc721, 0x0000 }, + { 0xc722, 0x0000 }, + { 0xc723, 0x0000 }, + { 0xc724, 0x0000 }, + { 0xc725, 0x0000 }, + { 0xc726, 0x0000 }, + { 0xc727, 0x0000 }, + { 0xc728, 0x0000 }, + { 0xc729, 0x0000 }, + { 0xc72a, 0x0000 }, + { 0xc72b, 0x0000 }, + { 0xc72c, 0x0000 }, + { 0xc72d, 0x0000 }, + { 0xc72e, 0x0000 }, + { 0xc72f, 0x0000 }, + { 0xc730, 0x0000 }, + { 0xc731, 0x0000 }, + { 0xc732, 0x0000 }, + { 0xc733, 0x0000 }, + { 0xc734, 0x0000 }, + { 0xc735, 0x0000 }, + { 0xc736, 0x0000 }, + { 0xc737, 0x0000 }, + { 0xc738, 0x0000 }, + { 0xc739, 0x0000 }, + { 0xc73a, 0x0000 }, + { 0xc73b, 0x0000 }, + { 0xc73c, 0x0000 }, + { 0xc73d, 0x0000 }, + { 0xc73e, 0x0000 }, + { 0xc73f, 0x0000 }, + { 0xc740, 0x0000 }, + { 0xc741, 0x0000 }, + { 0xc742, 0x0000 }, + { 0xc743, 0x0000 }, + { 0xc744, 0x0000 }, + { 0xc745, 0x0000 }, + { 0xc746, 0x0000 }, + { 0xc747, 0x0000 }, + { 0xc748, 0x0000 }, + { 0xc749, 0x0000 }, + { 0xc74a, 0x0000 }, + { 0xc74b, 0x0000 }, + { 0xc74c, 0x0000 }, + { 0xc74d, 0x0000 }, + { 0xc74e, 0x0000 }, + { 0xc74f, 0x0000 }, + { 0xc750, 0x0000 }, + { 0xc751, 0x0000 }, + { 0xc752, 0x0000 }, + { 0xc753, 0x0000 }, + { 0xc754, 0x0000 }, + { 0xc755, 0x0000 }, + { 0xc756, 0x0000 }, + { 0xc757, 0x0000 }, + { 0xc758, 0x0000 }, + { 0xc759, 0x0000 }, + { 0xc75a, 0x0000 }, + { 0xc75b, 0x0000 }, + { 0xc75c, 0x0000 }, + { 0xc75d, 0x0000 }, + { 0xc75e, 0x0000 }, + { 0xc75f, 0x0000 }, + { 0xc760, 0x0000 }, + { 0xc761, 0x0000 }, + { 0xc762, 0x0000 }, + { 0xc763, 0x0000 }, + { 0xc764, 0x0000 }, + { 0xc765, 0x0000 }, + { 0xc766, 0x0000 }, + { 0xc767, 0x0000 }, + { 0xc768, 0x0000 }, + { 0xc769, 0x0000 }, + { 0xc76a, 0x0000 }, + { 0xc76b, 0x0000 }, + { 0xc76c, 0x0000 }, + { 0xc76d, 0x0000 }, + { 0xc76e, 0x0000 }, + { 0xc76f, 0x0000 }, + { 0xc770, 0x0000 }, + { 0xc771, 0x0000 }, + { 0xc772, 0x0000 }, + { 0xc773, 0x0000 }, + { 0xc774, 0x0000 }, + { 0xc775, 0x0000 }, + { 0xc776, 0x0000 }, + { 0xc777, 0x0000 }, + { 0xc778, 0x0000 }, + { 0xc779, 0x0000 }, + { 0xc77a, 0x0000 }, + { 0xc77b, 0x0000 }, + { 0xc77c, 0x0000 }, + { 0xc77d, 0x0000 }, + { 0xc77e, 0x0000 }, + { 0xc77f, 0x0000 }, + { 0xc780, 0x0000 }, + { 0xc781, 0x0000 }, + { 0xc782, 0x0000 }, + { 0xc783, 0x0000 }, + { 0xc784, 0x0000 }, + { 0xc785, 0x0000 }, + { 0xc786, 0x0000 }, + { 0xc787, 0x0000 }, + { 0xc788, 0x0000 }, + { 0xc789, 0x0000 }, + { 0xc78a, 0x0000 }, + { 0xc78b, 0x0000 }, + { 0xc78c, 0x0000 }, + { 0xc78d, 0x0000 }, + { 0xc78e, 0x0000 }, + { 0xc78f, 0x0000 }, + { 0xc790, 0x0000 }, + { 0xc791, 0x0000 }, + { 0xc792, 0x0000 }, + { 0xc793, 0x0000 }, + { 0xc794, 0x0000 }, + { 0xc795, 0x0000 }, + { 0xc796, 0x0000 }, + { 0xc797, 0x0000 }, + { 0xc798, 0x0000 }, + { 0xc799, 0x0000 }, + { 0xc79a, 0x0000 }, + { 0xc79b, 0x0000 }, + { 0xc79c, 0x0000 }, + { 0xc79d, 0x0000 }, + { 0xc79e, 0x0000 }, + { 0xc79f, 0x0000 }, + { 0xc7a0, 0x0000 }, + { 0xc7a1, 0x0000 }, + { 0xc7a2, 0x0000 }, + { 0xc7a3, 0x0000 }, + { 0xc7a4, 0x0000 }, + { 0xc7a5, 0x0000 }, + { 0xc7a6, 0x0000 }, + { 0xc7a7, 0x0000 }, + { 0xc7a8, 0x0000 }, + { 0xc7a9, 0x0000 }, + { 0xc7aa, 0x0000 }, + { 0xc7ab, 0x0000 }, + { 0xc7ac, 0x0000 }, + { 0xc7ad, 0x0000 }, + { 0xc7ae, 0x0000 }, + { 0xc7af, 0x0000 }, + { 0xc7b0, 0x0000 }, + { 0xc7b1, 0x0000 }, + { 0xc7b2, 0x0000 }, + { 0xc7b3, 0x0000 }, + { 0xc7b4, 0x0000 }, + { 0xc7b5, 0x0000 }, + { 0xc7b6, 0x0000 }, + { 0xc7b7, 0x0000 }, + { 0xc7b8, 0x0000 }, + { 0xc7b9, 0x0000 }, + { 0xc7ba, 0x0000 }, + { 0xc7bb, 0x0000 }, + { 0xc7bc, 0x0000 }, + { 0xc7bd, 0x0000 }, + { 0xc7be, 0x0000 }, + { 0xc7bf, 0x0000 }, + { 0xc7c0, 0x0000 }, + { 0xc7c1, 0x0000 }, + { 0xc7c2, 0x0000 }, + { 0xc7c3, 0x0000 }, + { 0xc7c4, 0x0000 }, + { 0xc7c5, 0x0000 }, + { 0xc7c6, 0x0000 }, + { 0xc7c7, 0x0000 }, + { 0xc7c8, 0x0000 }, + { 0xc7c9, 0x0000 }, + { 0xc7ca, 0x0000 }, + { 0xc7cb, 0x0000 }, + { 0xc7cc, 0x0000 }, + { 0xc7cd, 0x0000 }, + { 0xc7ce, 0x0000 }, + { 0xc7cf, 0x0000 }, + { 0xc7d0, 0x0000 }, + { 0xc7d1, 0x0000 }, + { 0xc7d2, 0x0000 }, + { 0xc7d3, 0x0000 }, + { 0xc7d4, 0x0000 }, + { 0xc7d5, 0x0000 }, + { 0xc7d6, 0x0000 }, + { 0xc7d7, 0x0000 }, + { 0xc7d8, 0x0000 }, + { 0xc7d9, 0x0000 }, + { 0xc7da, 0x0000 }, + { 0xc7db, 0x0000 }, + { 0xc7dc, 0x0000 }, + { 0xc7dd, 0x0000 }, + { 0xc7de, 0x0000 }, + { 0xc7df, 0x0000 }, + { 0xc7e0, 0x0000 }, + { 0xc7e1, 0x0000 }, + { 0xc7e2, 0x0000 }, + { 0xc7e3, 0x0000 }, + { 0xc7e4, 0x0000 }, + { 0xc7e5, 0x0000 }, + { 0xc7e6, 0x0000 }, + { 0xc7e7, 0x0000 }, + { 0xc7e8, 0x0000 }, + { 0xc7e9, 0x0000 }, + { 0xc7ea, 0x0000 }, + { 0xc7eb, 0x0000 }, + { 0xc7ec, 0x0000 }, + { 0xc7ed, 0x0000 }, + { 0xc7ee, 0x0000 }, + { 0xc7ef, 0x0000 }, + { 0xc7f0, 0x0000 }, + { 0xc7f1, 0x0000 }, + { 0xc7f2, 0x0000 }, + { 0xc7f3, 0x0000 }, + { 0xc7f4, 0x0000 }, + { 0xc7f5, 0x0000 }, + { 0xc7f6, 0x0000 }, + { 0xc7f7, 0x0000 }, + { 0xc7f8, 0x0000 }, + { 0xc7f9, 0x0000 }, + { 0xc7fa, 0x0000 }, + { 0xc7fb, 0x0000 }, + { 0xc7fc, 0x0000 }, + { 0xc7fd, 0x0000 }, + { 0xc7fe, 0x0000 }, + { 0xc7ff, 0x0000 }, + { 0xc800, 0x0000 }, + { 0xc801, 0x0000 }, + { 0xc802, 0x0000 }, + { 0xc803, 0x0000 }, + { 0xc804, 0x0000 }, + { 0xc805, 0x0000 }, + { 0xc806, 0x0000 }, + { 0xc807, 0x0000 }, + { 0xc808, 0x0000 }, + { 0xc809, 0x0000 }, + { 0xc80a, 0x0000 }, + { 0xc80b, 0x0000 }, + { 0xc80c, 0x0000 }, + { 0xc80d, 0x0000 }, + { 0xc80e, 0x0000 }, + { 0xc80f, 0x0000 }, + { 0xc810, 0x0000 }, + { 0xc811, 0x0000 }, + { 0xc812, 0x0000 }, + { 0xc813, 0x0000 }, + { 0xc814, 0x0000 }, + { 0xc815, 0x0000 }, + { 0xc816, 0x0000 }, + { 0xc817, 0x0000 }, + { 0xc818, 0x0000 }, + { 0xc819, 0x0000 }, + { 0xc81a, 0x0000 }, + { 0xc81b, 0x0000 }, + { 0xc81c, 0x0000 }, + { 0xc81d, 0x0000 }, + { 0xc81e, 0x0000 }, + { 0xc81f, 0x0000 }, + { 0xc820, 0x0000 }, + { 0xc821, 0x0000 }, + { 0xc822, 0x0000 }, + { 0xc823, 0x0000 }, + { 0xc824, 0x0000 }, + { 0xc825, 0x0000 }, + { 0xc826, 0x0000 }, + { 0xc827, 0x0000 }, + { 0xc828, 0x0000 }, + { 0xc829, 0x0000 }, + { 0xc82a, 0x0000 }, + { 0xc82b, 0x0000 }, + { 0xc82c, 0x0000 }, + { 0xc82d, 0x0000 }, + { 0xc82e, 0x0000 }, + { 0xc82f, 0x0000 }, + { 0xc830, 0x0000 }, + { 0xc831, 0x0000 }, + { 0xc832, 0x0000 }, + { 0xc833, 0x0000 }, + { 0xc834, 0x0000 }, + { 0xc835, 0x0000 }, + { 0xc836, 0x0000 }, + { 0xc837, 0x0000 }, + { 0xc838, 0x0000 }, + { 0xc839, 0x0000 }, + { 0xc83a, 0x0000 }, + { 0xc83b, 0x0000 }, + { 0xc83c, 0x0000 }, + { 0xc83d, 0x0000 }, + { 0xc83e, 0x0000 }, + { 0xc83f, 0x0000 }, + { 0xc840, 0x0000 }, + { 0xc841, 0x0000 }, + { 0xc842, 0x0000 }, + { 0xc843, 0x0000 }, + { 0xc844, 0x0000 }, + { 0xc845, 0x0000 }, + { 0xc846, 0x0000 }, + { 0xc847, 0x0000 }, + { 0xc848, 0x0000 }, + { 0xc849, 0x0000 }, + { 0xc84a, 0x0000 }, + { 0xc84b, 0x0000 }, + { 0xc84c, 0x0000 }, + { 0xc84d, 0x0000 }, + { 0xc84e, 0x0000 }, + { 0xc84f, 0x0000 }, + { 0xc850, 0x0000 }, + { 0xc851, 0x0000 }, + { 0xc852, 0x0000 }, + { 0xc853, 0x0000 }, + { 0xc854, 0x0000 }, + { 0xc855, 0x0000 }, + { 0xc856, 0x0000 }, + { 0xc857, 0x0000 }, + { 0xc858, 0x0000 }, + { 0xc859, 0x0000 }, + { 0xc85a, 0x0000 }, + { 0xc85b, 0x0000 }, + { 0xc85c, 0x0000 }, + { 0xc85d, 0x0000 }, + { 0xc85e, 0x0000 }, + { 0xc85f, 0x0000 }, + { 0xc860, 0x0000 }, + { 0xc861, 0x0000 }, + { 0xc862, 0x0000 }, + { 0xc863, 0x0000 }, + { 0xc864, 0x0000 }, + { 0xc865, 0x0000 }, + { 0xc866, 0x0000 }, + { 0xc867, 0x0000 }, + { 0xc868, 0x0000 }, + { 0xc869, 0x0000 }, + { 0xc86a, 0x0000 }, + { 0xc86b, 0x0000 }, + { 0xc86c, 0x0000 }, + { 0xc86d, 0x0000 }, + { 0xc86e, 0x0000 }, + { 0xc86f, 0x0000 }, + { 0xc870, 0x0000 }, + { 0xc871, 0x0000 }, + { 0xc872, 0x0000 }, + { 0xc873, 0x0000 }, + { 0xc874, 0x0000 }, + { 0xc875, 0x0000 }, + { 0xc876, 0x0000 }, + { 0xc877, 0x0000 }, + { 0xc878, 0x0000 }, + { 0xc879, 0x0000 }, + { 0xc87a, 0x0000 }, + { 0xc87b, 0x0000 }, + { 0xc87c, 0x0000 }, + { 0xc87d, 0x0000 }, + { 0xc87e, 0x0000 }, + { 0xc87f, 0x0000 }, + { 0xc880, 0x0000 }, + { 0xc881, 0x0000 }, + { 0xc882, 0x0000 }, + { 0xc883, 0x0000 }, + { 0xc884, 0x0000 }, + { 0xc885, 0x0000 }, + { 0xc886, 0x0000 }, + { 0xc887, 0x0000 }, + { 0xc888, 0x0000 }, + { 0xc889, 0x0000 }, + { 0xc88a, 0x0000 }, + { 0xc88b, 0x0000 }, + { 0xc88c, 0x0000 }, + { 0xc88d, 0x0000 }, + { 0xc88e, 0x0000 }, + { 0xc88f, 0x0000 }, + { 0xc890, 0x0000 }, + { 0xc891, 0x0000 }, + { 0xc892, 0x0000 }, + { 0xc893, 0x0000 }, + { 0xc894, 0x0000 }, + { 0xc895, 0x0000 }, + { 0xc896, 0x0000 }, + { 0xc897, 0x0000 }, + { 0xc898, 0x0000 }, + { 0xc899, 0x0000 }, + { 0xc89a, 0x0000 }, + { 0xc89b, 0x0000 }, + { 0xc89c, 0x0000 }, + { 0xc89d, 0x0000 }, + { 0xc89e, 0x0000 }, + { 0xc89f, 0x0000 }, + { 0xc8a0, 0x0000 }, + { 0xc8a1, 0x0000 }, + { 0xc8a2, 0x0000 }, + { 0xc8a3, 0x0000 }, + { 0xc8a4, 0x0000 }, + { 0xc8a5, 0x0000 }, + { 0xc8a6, 0x0000 }, + { 0xc8a7, 0x0000 }, + { 0xc8a8, 0x0000 }, + { 0xc8a9, 0x0000 }, + { 0xc8aa, 0x0000 }, + { 0xc8ab, 0x0000 }, + { 0xc8ac, 0x0000 }, + { 0xc8ad, 0x0000 }, + { 0xc8ae, 0x0000 }, + { 0xc8af, 0x0000 }, + { 0xc8b0, 0x0000 }, + { 0xc8b1, 0x0000 }, + { 0xc8b2, 0x0000 }, + { 0xc8b3, 0x0000 }, + { 0xc8b4, 0x0000 }, + { 0xc8b5, 0x0000 }, + { 0xc8b6, 0x0000 }, + { 0xc8b7, 0x0000 }, + { 0xc8b8, 0x0000 }, + { 0xc8b9, 0x0000 }, + { 0xc8ba, 0x0000 }, + { 0xc8bb, 0x0000 }, + { 0xc8bc, 0x0000 }, + { 0xc8bd, 0x0000 }, + { 0xc8be, 0x0000 }, + { 0xc8bf, 0x0000 }, + { 0xc8c0, 0x0000 }, + { 0xc8c1, 0x0000 }, + { 0xc8c2, 0x0000 }, + { 0xc8c3, 0x0000 }, + { 0xc8c4, 0x0000 }, + { 0xc8c5, 0x0000 }, + { 0xc8c6, 0x0000 }, + { 0xc8c7, 0x0000 }, + { 0xc8c8, 0x0000 }, + { 0xc8c9, 0x0000 }, + { 0xc8ca, 0x0000 }, + { 0xc8cb, 0x0000 }, + { 0xc8cc, 0x0000 }, + { 0xc8cd, 0x0000 }, + { 0xc8ce, 0x0000 }, + { 0xc8cf, 0x0000 }, + { 0xc8d0, 0x0000 }, + { 0xc8d1, 0x0000 }, + { 0xc8d2, 0x0000 }, + { 0xc8d3, 0x0000 }, + { 0xc8d4, 0x0000 }, + { 0xc8d5, 0x0000 }, + { 0xc8d6, 0x0000 }, + { 0xc8d7, 0x0000 }, + { 0xc8d8, 0x0000 }, + { 0xc8d9, 0x0000 }, + { 0xc8da, 0x0000 }, + { 0xc8db, 0x0000 }, + { 0xc8dc, 0x0000 }, + { 0xc8dd, 0x0000 }, + { 0xc8de, 0x0000 }, + { 0xc8df, 0x0000 }, + { 0xc8e0, 0x0000 }, + { 0xc8e1, 0x0000 }, + { 0xc8e2, 0x0000 }, + { 0xc8e3, 0x0000 }, + { 0xc8e4, 0x0000 }, + { 0xc8e5, 0x0000 }, + { 0xc8e6, 0x0000 }, + { 0xc8e7, 0x0000 }, + { 0xc8e8, 0x0000 }, + { 0xc8e9, 0x0000 }, + { 0xc8ea, 0x0000 }, + { 0xc8eb, 0x0000 }, + { 0xc8ec, 0x0000 }, + { 0xc8ed, 0x0000 }, + { 0xc8ee, 0x0000 }, + { 0xc8ef, 0x0000 }, + { 0xc8f0, 0x0000 }, + { 0xc8f1, 0x0000 }, + { 0xc8f2, 0x0000 }, + { 0xc8f3, 0x0000 }, + { 0xc8f4, 0x0000 }, + { 0xc8f5, 0x0000 }, + { 0xc8f6, 0x0000 }, + { 0xc8f7, 0x0000 }, + { 0xc8f8, 0x0000 }, + { 0xc8f9, 0x0000 }, + { 0xc8fa, 0x0000 }, + { 0xc8fb, 0x0000 }, + { 0xc8fc, 0x0000 }, + { 0xc8fd, 0x0000 }, + { 0xc8fe, 0x0000 }, + { 0xc8ff, 0x0000 }, + { 0xc900, 0x0000 }, + { 0xc901, 0x0000 }, + { 0xc902, 0x0000 }, + { 0xc903, 0x0000 }, + { 0xc904, 0x0000 }, + { 0xc905, 0x0000 }, + { 0xc906, 0x0000 }, + { 0xc907, 0x0000 }, + { 0xc908, 0x0000 }, + { 0xc909, 0x0000 }, + { 0xc90a, 0x0000 }, + { 0xc90b, 0x0000 }, + { 0xc90c, 0x0000 }, + { 0xc90d, 0x0000 }, + { 0xc90e, 0x0000 }, + { 0xc90f, 0x0000 }, + { 0xc910, 0x0000 }, + { 0xc911, 0x0000 }, + { 0xc912, 0x0000 }, + { 0xc913, 0x0000 }, + { 0xc914, 0x0000 }, + { 0xc915, 0x0000 }, + { 0xc916, 0x0000 }, + { 0xc917, 0x0000 }, + { 0xc918, 0x0000 }, + { 0xc919, 0x0000 }, + { 0xc91a, 0x0000 }, + { 0xc91b, 0x0000 }, + { 0xc91c, 0x0000 }, + { 0xc91d, 0x0000 }, + { 0xc91e, 0x0000 }, + { 0xc91f, 0x0000 }, + { 0xc920, 0x0000 }, + { 0xc921, 0x0000 }, + { 0xc922, 0x0000 }, + { 0xc923, 0x0000 }, + { 0xc924, 0x0000 }, + { 0xc925, 0x0000 }, + { 0xc926, 0x0000 }, + { 0xc927, 0x0000 }, + { 0xc928, 0x0000 }, + { 0xc929, 0x0000 }, + { 0xc92a, 0x0000 }, + { 0xc92b, 0x0000 }, + { 0xc92c, 0x0000 }, + { 0xc92d, 0x0000 }, + { 0xc92e, 0x0000 }, + { 0xc92f, 0x0000 }, + { 0xc930, 0x0000 }, + { 0xc931, 0x0000 }, + { 0xc932, 0x0000 }, + { 0xc933, 0x0000 }, + { 0xc934, 0x0000 }, + { 0xc935, 0x0000 }, + { 0xc936, 0x0000 }, + { 0xc937, 0x0000 }, + { 0xc938, 0x0000 }, + { 0xc939, 0x0000 }, + { 0xc93a, 0x0000 }, + { 0xc93b, 0x0000 }, + { 0xc93c, 0x0000 }, + { 0xc93d, 0x0000 }, + { 0xc93e, 0x0000 }, + { 0xc93f, 0x0000 }, + { 0xc940, 0x0000 }, + { 0xc941, 0x0000 }, + { 0xc942, 0x0000 }, + { 0xc943, 0x0000 }, + { 0xc944, 0x0000 }, + { 0xc945, 0x0000 }, + { 0xc946, 0x0000 }, + { 0xc947, 0x0000 }, + { 0xc948, 0x0000 }, + { 0xc949, 0x0000 }, + { 0xc94a, 0x0000 }, + { 0xc94b, 0x0000 }, + { 0xc94c, 0x0000 }, + { 0xc94d, 0x0000 }, + { 0xc94e, 0x0000 }, + { 0xc94f, 0x0000 }, + { 0xc950, 0x0000 }, + { 0xc951, 0x0000 }, + { 0xc952, 0x0000 }, + { 0xc953, 0x0000 }, + { 0xc954, 0x0000 }, + { 0xc955, 0x0000 }, + { 0xc956, 0x0000 }, + { 0xc957, 0x0000 }, + { 0xc958, 0x0000 }, + { 0xc959, 0x0000 }, + { 0xc95a, 0x0000 }, + { 0xc95b, 0x0000 }, + { 0xc95c, 0x0000 }, + { 0xc95d, 0x0000 }, + { 0xc95e, 0x0000 }, + { 0xc95f, 0x0000 }, + { 0xc960, 0x0000 }, + { 0xc961, 0x0000 }, + { 0xc962, 0x0000 }, + { 0xc963, 0x0000 }, + { 0xc964, 0x0000 }, + { 0xc965, 0x0000 }, + { 0xc966, 0x0000 }, + { 0xc967, 0x0000 }, + { 0xc968, 0x0000 }, + { 0xc969, 0x0000 }, + { 0xc96a, 0x0000 }, + { 0xc96b, 0x0000 }, + { 0xc96c, 0x0000 }, + { 0xc96d, 0x0000 }, + { 0xc96e, 0x0000 }, + { 0xc96f, 0x0000 }, + { 0xc970, 0x0000 }, + { 0xc971, 0x0000 }, + { 0xc972, 0x0000 }, + { 0xc973, 0x0000 }, + { 0xc974, 0x0000 }, + { 0xc975, 0x0000 }, + { 0xc976, 0x0000 }, + { 0xc977, 0x0000 }, + { 0xc978, 0x0000 }, + { 0xc979, 0x0000 }, + { 0xc97a, 0x0000 }, + { 0xc97b, 0x0000 }, + { 0xc97c, 0x0000 }, + { 0xc97d, 0x0000 }, + { 0xc97e, 0x0000 }, + { 0xc97f, 0x0000 }, + { 0xc980, 0x0000 }, + { 0xc981, 0x0000 }, + { 0xc982, 0x0000 }, + { 0xc983, 0x0000 }, + { 0xc984, 0x0000 }, + { 0xc985, 0x0000 }, + { 0xc986, 0x0000 }, + { 0xc987, 0x0000 }, + { 0xc988, 0x0000 }, + { 0xc989, 0x0000 }, + { 0xc98a, 0x0000 }, + { 0xc98b, 0x0000 }, + { 0xc98c, 0x0000 }, + { 0xc98d, 0x0000 }, + { 0xc98e, 0x0000 }, + { 0xc98f, 0x0000 }, + { 0xc990, 0x0000 }, + { 0xc991, 0x0000 }, + { 0xc992, 0x0000 }, + { 0xc993, 0x0000 }, + { 0xc994, 0x0000 }, + { 0xc995, 0x0000 }, + { 0xc996, 0x0000 }, + { 0xc997, 0x0000 }, + { 0xc998, 0x0000 }, + { 0xc999, 0x0000 }, + { 0xc99a, 0x0000 }, + { 0xc99b, 0x0000 }, + { 0xc99c, 0x0000 }, + { 0xc99d, 0x0000 }, + { 0xc99e, 0x0000 }, + { 0xc99f, 0x0000 }, + { 0xc9a0, 0x0000 }, + { 0xc9a1, 0x0000 }, + { 0xc9a2, 0x0000 }, + { 0xc9a3, 0x0000 }, + { 0xc9a4, 0x0000 }, + { 0xc9a5, 0x0000 }, + { 0xc9a6, 0x0000 }, + { 0xc9a7, 0x0000 }, + { 0xc9a8, 0x0000 }, + { 0xc9a9, 0x0000 }, + { 0xc9aa, 0x0000 }, + { 0xc9ab, 0x0000 }, + { 0xc9ac, 0x0000 }, + { 0xc9ad, 0x0000 }, + { 0xc9ae, 0x0000 }, + { 0xc9af, 0x0000 }, + { 0xc9b0, 0x0000 }, + { 0xc9b1, 0x0000 }, + { 0xc9b2, 0x0000 }, + { 0xc9b3, 0x0000 }, + { 0xc9b4, 0x0000 }, + { 0xc9b5, 0x0000 }, + { 0xc9b6, 0x0000 }, + { 0xc9b7, 0x0000 }, + { 0xc9b8, 0x0000 }, + { 0xc9b9, 0x0000 }, + { 0xc9ba, 0x0000 }, + { 0xc9bb, 0x0000 }, + { 0xc9bc, 0x0000 }, + { 0xc9bd, 0x0000 }, + { 0xc9be, 0x0000 }, + { 0xc9bf, 0x0000 }, + { 0xc9c0, 0x0000 }, + { 0xc9c1, 0x0000 }, + { 0xc9c2, 0x0000 }, + { 0xc9c3, 0x0000 }, + { 0xc9c4, 0x0000 }, + { 0xc9c5, 0x0000 }, + { 0xc9c6, 0x0000 }, + { 0xc9c7, 0x0000 }, + { 0xc9c8, 0x0000 }, + { 0xc9c9, 0x0000 }, + { 0xc9ca, 0x0000 }, + { 0xc9cb, 0x0000 }, + { 0xc9cc, 0x0000 }, + { 0xc9cd, 0x0000 }, + { 0xc9ce, 0x0000 }, + { 0xc9cf, 0x0000 }, + { 0xc9d0, 0x0000 }, + { 0xc9d1, 0x0000 }, + { 0xc9d2, 0x0000 }, + { 0xc9d3, 0x0000 }, + { 0xc9d4, 0x0000 }, + { 0xc9d5, 0x0000 }, + { 0xc9d6, 0x0000 }, + { 0xc9d7, 0x0000 }, + { 0xc9d8, 0x0000 }, + { 0xc9d9, 0x0000 }, + { 0xc9da, 0x0000 }, + { 0xc9db, 0x0000 }, + { 0xc9dc, 0x0000 }, + { 0xc9dd, 0x0000 }, + { 0xc9de, 0x0000 }, + { 0xc9df, 0x0000 }, + { 0xc9e0, 0x0000 }, + { 0xc9e1, 0x0000 }, + { 0xc9e2, 0x0000 }, + { 0xc9e3, 0x0000 }, + { 0xc9e4, 0x0000 }, + { 0xc9e5, 0x0000 }, + { 0xc9e6, 0x0000 }, + { 0xc9e7, 0x0000 }, + { 0xc9e8, 0x0000 }, + { 0xc9e9, 0x0000 }, + { 0xc9ea, 0x0000 }, + { 0xc9eb, 0x0000 }, + { 0xc9ec, 0x0000 }, + { 0xc9ed, 0x0000 }, + { 0xc9ee, 0x0000 }, + { 0xc9ef, 0x0000 }, + { 0xc9f0, 0x0000 }, + { 0xc9f1, 0x0000 }, + { 0xc9f2, 0x0000 }, + { 0xc9f3, 0x0000 }, + { 0xc9f4, 0x0000 }, + { 0xc9f5, 0x0000 }, + { 0xc9f6, 0x0000 }, + { 0xc9f7, 0x0000 }, + { 0xc9f8, 0x0000 }, + { 0xc9f9, 0x0000 }, + { 0xc9fa, 0x0000 }, + { 0xc9fb, 0x0000 }, + { 0xc9fc, 0x0000 }, + { 0xc9fd, 0x0000 }, + { 0xc9fe, 0x0000 }, + { 0xc9ff, 0x0000 }, + { 0xca00, 0x0000 }, + { 0xca01, 0x0000 }, + { 0xca02, 0x0000 }, + { 0xca03, 0x0000 }, + { 0xca04, 0x0000 }, + { 0xca05, 0x0000 }, + { 0xca06, 0x0000 }, + { 0xca07, 0x0000 }, + { 0xca08, 0x0000 }, + { 0xca09, 0x0000 }, + { 0xca0a, 0x0000 }, + { 0xca0b, 0x0000 }, + { 0xca0c, 0x0000 }, + { 0xca0d, 0x0000 }, + { 0xca0e, 0x0000 }, + { 0xca0f, 0x0000 }, + { 0xca10, 0x0000 }, + { 0xca11, 0x0000 }, + { 0xca12, 0x0000 }, + { 0xca13, 0x0000 }, + { 0xca14, 0x0000 }, + { 0xca15, 0x0000 }, + { 0xca16, 0x0000 }, + { 0xca17, 0x0000 }, + { 0xca18, 0x0000 }, + { 0xca19, 0x0000 }, + { 0xca1a, 0x0000 }, + { 0xca1b, 0x0000 }, + { 0xca1c, 0x0000 }, + { 0xca1d, 0x0000 }, + { 0xca1e, 0x0000 }, + { 0xca1f, 0x0000 }, + { 0xca20, 0x0000 }, + { 0xca21, 0x0000 }, + { 0xca22, 0x0000 }, + { 0xca23, 0x0000 }, + { 0xca24, 0x0000 }, + { 0xca25, 0x0000 }, + { 0xca26, 0x0000 }, + { 0xca27, 0x0000 }, + { 0xca28, 0x0000 }, + { 0xca29, 0x0000 }, + { 0xca2a, 0x0000 }, + { 0xca2b, 0x0000 }, + { 0xca2c, 0x0000 }, + { 0xca2d, 0x0000 }, + { 0xca2e, 0x0000 }, + { 0xca2f, 0x0000 }, + { 0xca30, 0x0000 }, + { 0xca31, 0x0000 }, + { 0xca32, 0x0000 }, + { 0xca33, 0x0000 }, + { 0xca34, 0x0000 }, + { 0xca35, 0x0000 }, + { 0xca36, 0x0000 }, + { 0xca37, 0x0000 }, + { 0xca38, 0x0000 }, + { 0xca39, 0x0000 }, + { 0xca3a, 0x0000 }, + { 0xca3b, 0x0000 }, + { 0xca3c, 0x0000 }, + { 0xca3d, 0x0000 }, + { 0xca3e, 0x0000 }, + { 0xca3f, 0x0000 }, + { 0xca40, 0x0000 }, + { 0xca41, 0x0000 }, + { 0xca42, 0x0000 }, + { 0xca43, 0x0000 }, + { 0xca44, 0x0000 }, + { 0xca45, 0x0000 }, + { 0xca46, 0x0000 }, + { 0xca47, 0x0000 }, + { 0xca48, 0x0000 }, + { 0xca49, 0x0000 }, + { 0xca4a, 0x0000 }, + { 0xca4b, 0x0000 }, + { 0xca4c, 0x0000 }, + { 0xca4d, 0x0000 }, + { 0xca4e, 0x0000 }, + { 0xca4f, 0x0000 }, + { 0xca50, 0x0000 }, + { 0xca51, 0x0000 }, + { 0xca52, 0x0000 }, + { 0xca53, 0x0000 }, + { 0xca54, 0x0000 }, + { 0xca55, 0x0000 }, + { 0xca56, 0x0000 }, + { 0xca57, 0x0000 }, + { 0xca58, 0x0000 }, + { 0xca59, 0x0000 }, + { 0xca5a, 0x0000 }, + { 0xca5b, 0x0000 }, + { 0xca5c, 0x0000 }, + { 0xca5d, 0x0000 }, + { 0xca5e, 0x0000 }, + { 0xca5f, 0x0000 }, + { 0xca60, 0x0000 }, + { 0xca61, 0x0000 }, + { 0xca62, 0x0000 }, + { 0xca63, 0x0000 }, + { 0xca64, 0x0000 }, + { 0xca65, 0x0000 }, + { 0xca66, 0x0000 }, + { 0xca67, 0x0000 }, + { 0xca68, 0x0000 }, + { 0xca69, 0x0000 }, + { 0xca6a, 0x0000 }, + { 0xca6b, 0x0000 }, + { 0xca6c, 0x0000 }, + { 0xca6d, 0x0000 }, + { 0xca6e, 0x0000 }, + { 0xca6f, 0x0000 }, + { 0xca70, 0x0000 }, + { 0xca71, 0x0000 }, + { 0xca72, 0x0000 }, + { 0xca73, 0x0000 }, + { 0xca74, 0x0000 }, + { 0xca75, 0x0000 }, + { 0xca76, 0x0000 }, + { 0xca77, 0x0000 }, + { 0xca78, 0x0000 }, + { 0xca79, 0x0000 }, + { 0xca7a, 0x0000 }, + { 0xca7b, 0x0000 }, + { 0xca7c, 0x0000 }, + { 0xca7d, 0x0000 }, + { 0xca7e, 0x0000 }, + { 0xca7f, 0x0000 }, + { 0xca80, 0x0000 }, + { 0xca81, 0x0000 }, + { 0xca82, 0x0000 }, + { 0xca83, 0x0000 }, + { 0xca84, 0x0000 }, + { 0xca85, 0x0000 }, + { 0xca86, 0x0000 }, + { 0xca87, 0x0000 }, + { 0xca88, 0x0000 }, + { 0xca89, 0x0000 }, + { 0xca8a, 0x0000 }, + { 0xca8b, 0x0000 }, + { 0xca8c, 0x0000 }, + { 0xca8d, 0x0000 }, + { 0xca8e, 0x0000 }, + { 0xca8f, 0x0000 }, + { 0xca90, 0x0000 }, + { 0xca91, 0x0000 }, + { 0xca92, 0x0000 }, + { 0xca93, 0x0000 }, + { 0xca94, 0x0000 }, + { 0xca95, 0x0000 }, + { 0xca96, 0x0000 }, + { 0xca97, 0x0000 }, + { 0xca98, 0x0000 }, + { 0xca99, 0x0000 }, + { 0xca9a, 0x0000 }, + { 0xca9b, 0x0000 }, + { 0xca9c, 0x0000 }, + { 0xca9d, 0x0000 }, + { 0xca9e, 0x0000 }, + { 0xca9f, 0x0000 }, + { 0xcaa0, 0x0000 }, + { 0xcaa1, 0x0000 }, + { 0xcaa2, 0x0000 }, + { 0xcaa3, 0x0000 }, + { 0xcaa4, 0x0000 }, + { 0xcaa5, 0x0000 }, + { 0xcaa6, 0x0000 }, + { 0xcaa7, 0x0000 }, + { 0xcaa8, 0x0000 }, + { 0xcaa9, 0x0000 }, + { 0xcaaa, 0x0000 }, + { 0xcaab, 0x0000 }, + { 0xcaac, 0x0000 }, + { 0xcaad, 0x0000 }, + { 0xcaae, 0x0000 }, + { 0xcaaf, 0x0000 }, + { 0xcab0, 0x0000 }, + { 0xcab1, 0x0000 }, + { 0xcab2, 0x0000 }, + { 0xcab3, 0x0000 }, + { 0xcab4, 0x0000 }, + { 0xcab5, 0x0000 }, + { 0xcab6, 0x0000 }, + { 0xcab7, 0x0000 }, + { 0xcab8, 0x0000 }, + { 0xcab9, 0x0000 }, + { 0xcaba, 0x0000 }, + { 0xcabb, 0x0000 }, + { 0xcabc, 0x0000 }, + { 0xcabd, 0x0000 }, + { 0xcabe, 0x0000 }, + { 0xcabf, 0x0000 }, + { 0xcac0, 0x0000 }, + { 0xcac1, 0x0000 }, + { 0xcac2, 0x0000 }, + { 0xcac3, 0x0000 }, + { 0xcac4, 0x0000 }, + { 0xcac5, 0x0000 }, + { 0xcac6, 0x0000 }, + { 0xcac7, 0x0000 }, + { 0xcac8, 0x0000 }, + { 0xcac9, 0x0000 }, + { 0xcaca, 0x0000 }, + { 0xcacb, 0x0000 }, + { 0xcacc, 0x0000 }, + { 0xcacd, 0x0000 }, + { 0xcace, 0x0000 }, + { 0xcacf, 0x0000 }, + { 0xcad0, 0x0000 }, + { 0xcad1, 0x0000 }, + { 0xcad2, 0x0000 }, + { 0xcad3, 0x0000 }, + { 0xcad4, 0x0000 }, + { 0xcad5, 0x0000 }, + { 0xcad6, 0x0000 }, + { 0xcad7, 0x0000 }, + { 0xcad8, 0x0000 }, + { 0xcad9, 0x0000 }, + { 0xcada, 0x0000 }, + { 0xcadb, 0x0000 }, + { 0xcadc, 0x0000 }, + { 0xcadd, 0x0000 }, + { 0xcade, 0x0000 }, + { 0xcadf, 0x0000 }, + { 0xcae0, 0x0000 }, + { 0xcae1, 0x0000 }, + { 0xcae2, 0x0000 }, + { 0xcae3, 0x0000 }, + { 0xcae4, 0x0000 }, + { 0xcae5, 0x0000 }, + { 0xcae6, 0x0000 }, + { 0xcae7, 0x0000 }, + { 0xcae8, 0x0000 }, + { 0xcae9, 0x0000 }, + { 0xcaea, 0x0000 }, + { 0xcaeb, 0x0000 }, + { 0xcaec, 0x0000 }, + { 0xcaed, 0x0000 }, + { 0xcaee, 0x0000 }, + { 0xcaef, 0x0000 }, + { 0xcaf0, 0x0000 }, + { 0xcaf1, 0x0000 }, + { 0xcaf2, 0x0000 }, + { 0xcaf3, 0x0000 }, + { 0xcaf4, 0x0000 }, + { 0xcaf5, 0x0000 }, + { 0xcaf6, 0x0000 }, + { 0xcaf7, 0x0000 }, + { 0xcaf8, 0x0000 }, + { 0xcaf9, 0x0000 }, + { 0xcafa, 0x0000 }, + { 0xcafb, 0x0000 }, + { 0xcafc, 0x0000 }, + { 0xcafd, 0x0000 }, + { 0xcafe, 0x0000 }, + { 0xcaff, 0x0000 }, + { 0xcb00, 0x0000 }, + { 0xcb01, 0x0000 }, + { 0xcb02, 0x0000 }, + { 0xcb03, 0x0000 }, + { 0xcb04, 0x0000 }, + { 0xcb05, 0x0000 }, + { 0xcb06, 0x0000 }, + { 0xcb07, 0x0000 }, + { 0xcb08, 0x0000 }, + { 0xcb09, 0x0000 }, + { 0xcb0a, 0x0000 }, + { 0xcb0b, 0x0000 }, + { 0xcb0c, 0x0000 }, + { 0xcb0d, 0x0000 }, + { 0xcb0e, 0x0000 }, + { 0xcb0f, 0x0000 }, + { 0xcb10, 0x0000 }, + { 0xcb11, 0x0000 }, + { 0xcb12, 0x0000 }, + { 0xcb13, 0x0000 }, + { 0xcb14, 0x0000 }, + { 0xcb15, 0x0000 }, + { 0xcb16, 0x0000 }, + { 0xcb17, 0x0000 }, + { 0xcb18, 0x0000 }, + { 0xcb19, 0x0000 }, + { 0xcb1a, 0x0000 }, + { 0xcb1b, 0x0000 }, + { 0xcb1c, 0x0000 }, + { 0xcb1d, 0x0000 }, + { 0xcb1e, 0x0000 }, + { 0xcb1f, 0x0000 }, + { 0xcb20, 0x0000 }, + { 0xcb21, 0x0000 }, + { 0xcb22, 0x0000 }, + { 0xcb23, 0x0000 }, + { 0xcb24, 0x0000 }, + { 0xcb25, 0x0000 }, + { 0xcb26, 0x0000 }, + { 0xcb27, 0x0000 }, + { 0xcb28, 0x0000 }, + { 0xcb29, 0x0000 }, + { 0xcb2a, 0x0000 }, + { 0xcb2b, 0x0000 }, + { 0xcb2c, 0x0000 }, + { 0xcb2d, 0x0000 }, + { 0xcb2e, 0x0000 }, + { 0xcb2f, 0x0000 }, + { 0xcb30, 0x0000 }, + { 0xcb31, 0x0000 }, + { 0xcb32, 0x0000 }, + { 0xcb33, 0x0000 }, + { 0xcb34, 0x0000 }, + { 0xcb35, 0x0000 }, + { 0xcb36, 0x0000 }, + { 0xcb37, 0x0000 }, + { 0xcb38, 0x0000 }, + { 0xcb39, 0x0000 }, + { 0xcb3a, 0x0000 }, + { 0xcb3b, 0x0000 }, + { 0xcb3c, 0x0000 }, + { 0xcb3d, 0x0000 }, + { 0xcb3e, 0x0000 }, + { 0xcb3f, 0x0000 }, + { 0xcb40, 0x0000 }, + { 0xcb41, 0x0000 }, + { 0xcb42, 0x0000 }, + { 0xcb43, 0x0000 }, + { 0xcb44, 0x0000 }, + { 0xcb45, 0x0000 }, + { 0xcb46, 0x0000 }, + { 0xcb47, 0x0000 }, + { 0xcb48, 0x0000 }, + { 0xcb49, 0x0000 }, + { 0xcb4a, 0x0000 }, + { 0xcb4b, 0x0000 }, + { 0xcb4c, 0x0000 }, + { 0xcb4d, 0x0000 }, + { 0xcb4e, 0x0000 }, + { 0xcb4f, 0x0000 }, + { 0xcb50, 0x0000 }, + { 0xcb51, 0x0000 }, + { 0xcb52, 0x0000 }, + { 0xcb53, 0x0000 }, + { 0xcb54, 0x0000 }, + { 0xcb55, 0x0000 }, + { 0xcb56, 0x0000 }, + { 0xcb57, 0x0000 }, + { 0xcb58, 0x0000 }, + { 0xcb59, 0x0000 }, + { 0xcb5a, 0x0000 }, + { 0xcb5b, 0x0000 }, + { 0xcb5c, 0x0000 }, + { 0xcb5d, 0x0000 }, + { 0xcb5e, 0x0000 }, + { 0xcb5f, 0x0000 }, + { 0xcb60, 0x0000 }, + { 0xcb61, 0x0000 }, + { 0xcb62, 0x0000 }, + { 0xcb63, 0x0000 }, + { 0xcb64, 0x0000 }, + { 0xcb65, 0x0000 }, + { 0xcb66, 0x0000 }, + { 0xcb67, 0x0000 }, + { 0xcb68, 0x0000 }, + { 0xcb69, 0x0000 }, + { 0xcb6a, 0x0000 }, + { 0xcb6b, 0x0000 }, + { 0xcb6c, 0x0000 }, + { 0xcb6d, 0x0000 }, + { 0xcb6e, 0x0000 }, + { 0xcb6f, 0x0000 }, + { 0xcb70, 0x0000 }, + { 0xcb71, 0x0000 }, + { 0xcb72, 0x0000 }, + { 0xcb73, 0x0000 }, + { 0xcb74, 0x0000 }, + { 0xcb75, 0x0000 }, + { 0xcb76, 0x0000 }, + { 0xcb77, 0x0000 }, + { 0xcb78, 0x0000 }, + { 0xcb79, 0x0000 }, + { 0xcb7a, 0x0000 }, + { 0xcb7b, 0x0000 }, + { 0xcb7c, 0x0000 }, + { 0xcb7d, 0x0000 }, + { 0xcb7e, 0x0000 }, + { 0xcb7f, 0x0000 }, + { 0xcb80, 0x0000 }, + { 0xcb81, 0x0000 }, + { 0xcb82, 0x0000 }, + { 0xcb83, 0x0000 }, + { 0xcb84, 0x0000 }, + { 0xcb85, 0x0000 }, + { 0xcb86, 0x0000 }, + { 0xcb87, 0x0000 }, + { 0xcb88, 0x0000 }, + { 0xcb89, 0x0000 }, + { 0xcb8a, 0x0000 }, + { 0xcb8b, 0x0000 }, + { 0xcb8c, 0x0000 }, + { 0xcb8d, 0x0000 }, + { 0xcb8e, 0x0000 }, + { 0xcb8f, 0x0000 }, + { 0xcb90, 0x0000 }, + { 0xcb91, 0x0000 }, + { 0xcb92, 0x0000 }, + { 0xcb93, 0x0000 }, + { 0xcb94, 0x0000 }, + { 0xcb95, 0x0000 }, + { 0xcb96, 0x0000 }, + { 0xcb97, 0x0000 }, + { 0xcb98, 0x0000 }, + { 0xcb99, 0x0000 }, + { 0xcb9a, 0x0000 }, + { 0xcb9b, 0x0000 }, + { 0xcb9c, 0x0000 }, + { 0xcb9d, 0x0000 }, + { 0xcb9e, 0x0000 }, + { 0xcb9f, 0x0000 }, + { 0xcba0, 0x0000 }, + { 0xcba1, 0x0000 }, + { 0xcba2, 0x0000 }, + { 0xcba3, 0x0000 }, + { 0xcba4, 0x0000 }, + { 0xcba5, 0x0000 }, + { 0xcba6, 0x0000 }, + { 0xcba7, 0x0000 }, + { 0xcba8, 0x0000 }, + { 0xcba9, 0x0000 }, + { 0xcbaa, 0x0000 }, + { 0xcbab, 0x0000 }, + { 0xcbac, 0x0000 }, + { 0xcbad, 0x0000 }, + { 0xcbae, 0x0000 }, + { 0xcbaf, 0x0000 }, + { 0xcbb0, 0x0000 }, + { 0xcbb1, 0x0000 }, + { 0xcbb2, 0x0000 }, + { 0xcbb3, 0x0000 }, + { 0xcbb4, 0x0000 }, + { 0xcbb5, 0x0000 }, + { 0xcbb6, 0x0000 }, + { 0xcbb7, 0x0000 }, + { 0xcbb8, 0x0000 }, + { 0xcbb9, 0x0000 }, + { 0xcbba, 0x0000 }, + { 0xcbbb, 0x0000 }, + { 0xcbbc, 0x0000 }, + { 0xcbbd, 0x0000 }, + { 0xcbbe, 0x0000 }, + { 0xcbbf, 0x0000 }, + { 0xcbc0, 0x0000 }, + { 0xcbc1, 0x0000 }, + { 0xcbc2, 0x0000 }, + { 0xcbc3, 0x0000 }, + { 0xcbc4, 0x0000 }, + { 0xcbc5, 0x0000 }, + { 0xcbc6, 0x0000 }, + { 0xcbc7, 0x0000 }, + { 0xcbc8, 0x0000 }, + { 0xcbc9, 0x0000 }, + { 0xcbca, 0x0000 }, + { 0xcbcb, 0x0000 }, + { 0xcbcc, 0x0000 }, + { 0xcbcd, 0x0000 }, + { 0xcbce, 0x0000 }, + { 0xcbcf, 0x0000 }, + { 0xcbd0, 0x0000 }, + { 0xcbd1, 0x0000 }, + { 0xcbd2, 0x0000 }, + { 0xcbd3, 0x0000 }, + { 0xcbd4, 0x0000 }, + { 0xcbd5, 0x0000 }, + { 0xcbd6, 0x0000 }, + { 0xcbd7, 0x0000 }, + { 0xcbd8, 0x0000 }, + { 0xcbd9, 0x0000 }, + { 0xcbda, 0x0000 }, + { 0xcbdb, 0x0000 }, + { 0xcbdc, 0x0000 }, + { 0xcbdd, 0x0000 }, + { 0xcbde, 0x0000 }, + { 0xcbdf, 0x0000 }, + { 0xcbe0, 0x0000 }, + { 0xcbe1, 0x0000 }, + { 0xcbe2, 0x0000 }, + { 0xcbe3, 0x0000 }, + { 0xcbe4, 0x0000 }, + { 0xcbe5, 0x0000 }, + { 0xcbe6, 0x0000 }, + { 0xcbe7, 0x0000 }, + { 0xcbe8, 0x0000 }, + { 0xcbe9, 0x0000 }, + { 0xcbea, 0x0000 }, + { 0xcbeb, 0x0000 }, + { 0xcbec, 0x0000 }, + { 0xcbed, 0x0000 }, + { 0xcbee, 0x0000 }, + { 0xcbef, 0x0000 }, + { 0xcbf0, 0x0000 }, + { 0xcbf1, 0x0000 }, + { 0xcbf2, 0x0000 }, + { 0xcbf3, 0x0000 }, + { 0xcbf4, 0x0000 }, + { 0xcbf5, 0x0000 }, + { 0xcbf6, 0x0000 }, + { 0xcbf7, 0x0000 }, + { 0xcbf8, 0x0000 }, + { 0xcbf9, 0x0000 }, + { 0xcbfa, 0x0000 }, + { 0xcbfb, 0x0000 }, + { 0xcbfc, 0x0000 }, + { 0xcbfd, 0x0000 }, + { 0xcbfe, 0x0000 }, + { 0xcbff, 0x0000 }, + { 0xcc00, 0x0000 }, + { 0xcc01, 0x0000 }, + { 0xcc02, 0x0000 }, + { 0xcc03, 0x0000 }, + { 0xcc04, 0x0000 }, + { 0xcc05, 0x0000 }, + { 0xcc06, 0x0000 }, + { 0xcc07, 0x0000 }, + { 0xcc08, 0x0000 }, + { 0xcc09, 0x0000 }, + { 0xcc0a, 0x0000 }, + { 0xcc0b, 0x0000 }, + { 0xcc0c, 0x0000 }, + { 0xcc0d, 0x0000 }, + { 0xcc0e, 0x0000 }, + { 0xcc0f, 0x0000 }, + { 0xcc10, 0x0000 }, + { 0xcc11, 0x0000 }, + { 0xcc12, 0x0000 }, + { 0xcc13, 0x0000 }, + { 0xcc14, 0x0000 }, + { 0xcc15, 0x0000 }, + { 0xcc16, 0x0000 }, + { 0xcc17, 0x0000 }, + { 0xcc18, 0x0000 }, + { 0xcc19, 0x0000 }, + { 0xcc1a, 0x0000 }, + { 0xcc1b, 0x0000 }, + { 0xcc1c, 0x0000 }, + { 0xcc1d, 0x0000 }, + { 0xcc1e, 0x0000 }, + { 0xcc1f, 0x0000 }, + { 0xcc20, 0x0000 }, + { 0xcc21, 0x0000 }, + { 0xcc22, 0x0000 }, + { 0xcc23, 0x0000 }, + { 0xcc24, 0x0000 }, + { 0xcc25, 0x0000 }, + { 0xcc26, 0x0000 }, + { 0xcc27, 0x0000 }, + { 0xcc28, 0x0000 }, + { 0xcc29, 0x0000 }, + { 0xcc2a, 0x0000 }, + { 0xcc2b, 0x0000 }, + { 0xcc2c, 0x0000 }, + { 0xcc2d, 0x0000 }, + { 0xcc2e, 0x0000 }, + { 0xcc2f, 0x0000 }, + { 0xcc30, 0x0000 }, + { 0xcc31, 0x0000 }, + { 0xcc32, 0x0000 }, + { 0xcc33, 0x0000 }, + { 0xcc34, 0x0000 }, + { 0xcc35, 0x0000 }, + { 0xcc36, 0x0000 }, + { 0xcc37, 0x0000 }, + { 0xcc38, 0x0000 }, + { 0xcc39, 0x0000 }, + { 0xcc3a, 0x0000 }, + { 0xcc3b, 0x0000 }, + { 0xcc3c, 0x0000 }, + { 0xcc3d, 0x0000 }, + { 0xcc3e, 0x0000 }, + { 0xcc3f, 0x0000 }, + { 0xcc40, 0x0000 }, + { 0xcc41, 0x0000 }, + { 0xcc42, 0x0000 }, + { 0xcc43, 0x0000 }, + { 0xcc44, 0x0000 }, + { 0xcc45, 0x0000 }, + { 0xcc46, 0x0000 }, + { 0xcc47, 0x0000 }, + { 0xcc48, 0x0000 }, + { 0xcc49, 0x0000 }, + { 0xcc4a, 0x0000 }, + { 0xcc4b, 0x0000 }, + { 0xcc4c, 0x0000 }, + { 0xcc4d, 0x0000 }, + { 0xcc4e, 0x0000 }, + { 0xcc4f, 0x0000 }, + { 0xcc50, 0x0000 }, + { 0xcc51, 0x0000 }, + { 0xcc52, 0x0000 }, + { 0xcc53, 0x0000 }, + { 0xcc54, 0x0000 }, + { 0xcc55, 0x0000 }, + { 0xcc56, 0x0000 }, + { 0xcc57, 0x0000 }, + { 0xcc58, 0x0000 }, + { 0xcc59, 0x0000 }, + { 0xcc5a, 0x0000 }, + { 0xcc5b, 0x0000 }, + { 0xcc5c, 0x0000 }, + { 0xcc5d, 0x0000 }, + { 0xcc5e, 0x0000 }, + { 0xcc5f, 0x0000 }, + { 0xcc60, 0x0000 }, + { 0xcc61, 0x0000 }, + { 0xcc62, 0x0000 }, + { 0xcc63, 0x0000 }, + { 0xcc64, 0x0000 }, + { 0xcc65, 0x0000 }, + { 0xcc66, 0x0000 }, + { 0xcc67, 0x0000 }, + { 0xcc68, 0x0000 }, + { 0xcc69, 0x0000 }, + { 0xcc6a, 0x0000 }, + { 0xcc6b, 0x0000 }, + { 0xcc6c, 0x0000 }, + { 0xcc6d, 0x0000 }, + { 0xcc6e, 0x0000 }, + { 0xcc6f, 0x0000 }, + { 0xcc70, 0x0000 }, + { 0xcc71, 0x0000 }, + { 0xcc72, 0x0000 }, + { 0xcc73, 0x0000 }, + { 0xcc74, 0x0000 }, + { 0xcc75, 0x0000 }, + { 0xcc76, 0x0000 }, + { 0xcc77, 0x0000 }, + { 0xcc78, 0x0000 }, + { 0xcc79, 0x0000 }, + { 0xcc7a, 0x0000 }, + { 0xcc7b, 0x0000 }, + { 0xcc7c, 0x0000 }, + { 0xcc7d, 0x0000 }, + { 0xcc7e, 0x0000 }, + { 0xcc7f, 0x0000 }, + { 0xcc80, 0x0000 }, + { 0xcc81, 0x0000 }, + { 0xcc82, 0x0000 }, + { 0xcc83, 0x0000 }, + { 0xcc84, 0x0000 }, + { 0xcc85, 0x0000 }, + { 0xcc86, 0x0000 }, + { 0xcc87, 0x0000 }, + { 0xcc88, 0x0000 }, + { 0xcc89, 0x0000 }, + { 0xcc8a, 0x0000 }, + { 0xcc8b, 0x0000 }, + { 0xcc8c, 0x0000 }, + { 0xcc8d, 0x0000 }, + { 0xcc8e, 0x0000 }, + { 0xcc8f, 0x0000 }, + { 0xcc90, 0x0000 }, + { 0xcc91, 0x0000 }, + { 0xcc92, 0x0000 }, + { 0xcc93, 0x0000 }, + { 0xcc94, 0x0000 }, + { 0xcc95, 0x0000 }, + { 0xcc96, 0x0000 }, + { 0xcc97, 0x0000 }, + { 0xcc98, 0x0000 }, + { 0xcc99, 0x0000 }, + { 0xcc9a, 0x0000 }, + { 0xcc9b, 0x0000 }, + { 0xcc9c, 0x0000 }, + { 0xcc9d, 0x0000 }, + { 0xcc9e, 0x0000 }, + { 0xcc9f, 0x0000 }, + { 0xcca0, 0x0000 }, + { 0xcca1, 0x0000 }, + { 0xcca2, 0x0000 }, + { 0xcca3, 0x0000 }, + { 0xcca4, 0x0000 }, + { 0xcca5, 0x0000 }, + { 0xcca6, 0x0000 }, + { 0xcca7, 0x0000 }, + { 0xcca8, 0x0000 }, + { 0xcca9, 0x0000 }, + { 0xccaa, 0x0000 }, + { 0xccab, 0x0000 }, + { 0xccac, 0x0000 }, + { 0xccad, 0x0000 }, + { 0xccae, 0x0000 }, + { 0xccaf, 0x0000 }, + { 0xccb0, 0x0000 }, + { 0xccb1, 0x0000 }, + { 0xccb2, 0x0000 }, + { 0xccb3, 0x0000 }, + { 0xccb4, 0x0000 }, + { 0xccb5, 0x0000 }, + { 0xccb6, 0x0000 }, + { 0xccb7, 0x0000 }, + { 0xccb8, 0x0000 }, + { 0xccb9, 0x0000 }, + { 0xccba, 0x0000 }, + { 0xccbb, 0x0000 }, + { 0xccbc, 0x0000 }, + { 0xccbd, 0x0000 }, + { 0xccbe, 0x0000 }, + { 0xccbf, 0x0000 }, + { 0xccc0, 0x0000 }, + { 0xccc1, 0x0000 }, + { 0xccc2, 0x0000 }, + { 0xccc3, 0x0000 }, + { 0xccc4, 0x0000 }, + { 0xccc5, 0x0000 }, + { 0xccc6, 0x0000 }, + { 0xccc7, 0x0000 }, + { 0xccc8, 0x0000 }, + { 0xccc9, 0x0000 }, + { 0xccca, 0x0000 }, + { 0xcccb, 0x0000 }, + { 0xcccc, 0x0000 }, + { 0xcccd, 0x0000 }, + { 0xccce, 0x0000 }, + { 0xcccf, 0x0000 }, + { 0xccd0, 0x0000 }, + { 0xccd1, 0x0000 }, + { 0xccd2, 0x0000 }, + { 0xccd3, 0x0000 }, + { 0xccd4, 0x0000 }, + { 0xccd5, 0x0000 }, + { 0xccd6, 0x0000 }, + { 0xccd7, 0x0000 }, + { 0xccd8, 0x0000 }, + { 0xccd9, 0x0000 }, + { 0xccda, 0x0000 }, + { 0xccdb, 0x0000 }, + { 0xccdc, 0x0000 }, + { 0xccdd, 0x0000 }, + { 0xccde, 0x0000 }, + { 0xccdf, 0x0000 }, + { 0xcce0, 0x0000 }, + { 0xcce1, 0x0000 }, + { 0xcce2, 0x0000 }, + { 0xcce3, 0x0000 }, + { 0xcce4, 0x0000 }, + { 0xcce5, 0x0000 }, + { 0xcce6, 0x0000 }, + { 0xcce7, 0x0000 }, + { 0xcce8, 0x0000 }, + { 0xcce9, 0x0000 }, + { 0xccea, 0x0000 }, + { 0xcceb, 0x0000 }, + { 0xccec, 0x0000 }, + { 0xcced, 0x0000 }, + { 0xccee, 0x0000 }, + { 0xccef, 0x0000 }, + { 0xccf0, 0x0000 }, + { 0xccf1, 0x0000 }, + { 0xccf2, 0x0000 }, + { 0xccf3, 0x0000 }, + { 0xccf4, 0x0000 }, + { 0xccf5, 0x0000 }, + { 0xccf6, 0x0000 }, + { 0xccf7, 0x0000 }, + { 0xccf8, 0x0000 }, + { 0xccf9, 0x0000 }, + { 0xccfa, 0x0000 }, + { 0xccfb, 0x0000 }, + { 0xccfc, 0x0000 }, + { 0xccfd, 0x0000 }, + { 0xccfe, 0x0000 }, + { 0xccff, 0x0000 }, + { 0xcd00, 0x0000 }, + { 0xcd01, 0x0000 }, + { 0xcd02, 0x0000 }, + { 0xcd03, 0x0000 }, + { 0xcd04, 0x0000 }, + { 0xcd05, 0x0000 }, + { 0xcd06, 0x0000 }, + { 0xcd07, 0x0000 }, + { 0xcd08, 0x0000 }, + { 0xcd09, 0x0000 }, + { 0xcd0a, 0x0000 }, + { 0xcd0b, 0x0000 }, + { 0xcd0c, 0x0000 }, + { 0xcd0d, 0x0000 }, + { 0xcd0e, 0x0000 }, + { 0xcd0f, 0x0000 }, + { 0xcd10, 0x0000 }, + { 0xcd11, 0x0000 }, + { 0xcd12, 0x0000 }, + { 0xcd13, 0x0000 }, + { 0xcd14, 0x0000 }, + { 0xcd15, 0x0000 }, + { 0xcd16, 0x0000 }, + { 0xcd17, 0x0000 }, + { 0xcd18, 0x0000 }, + { 0xcd19, 0x0000 }, + { 0xcd1a, 0x0000 }, + { 0xcd1b, 0x0000 }, + { 0xcd1c, 0x0000 }, + { 0xcd1d, 0x0000 }, + { 0xcd1e, 0x0000 }, + { 0xcd1f, 0x0000 }, + { 0xcd20, 0x0000 }, + { 0xcd21, 0x0000 }, + { 0xcd22, 0x0000 }, + { 0xcd23, 0x0000 }, + { 0xcd24, 0x0000 }, + { 0xcd25, 0x0000 }, + { 0xcd26, 0x0000 }, + { 0xcd27, 0x0000 }, + { 0xcd28, 0x0000 }, + { 0xcd29, 0x0000 }, + { 0xcd2a, 0x0000 }, + { 0xcd2b, 0x0000 }, + { 0xcd2c, 0x0000 }, + { 0xcd2d, 0x0000 }, + { 0xcd2e, 0x0000 }, + { 0xcd2f, 0x0000 }, + { 0xcd30, 0x0000 }, + { 0xcd31, 0x0000 }, + { 0xcd32, 0x0000 }, + { 0xcd33, 0x0000 }, + { 0xcd34, 0x0000 }, + { 0xcd35, 0x0000 }, + { 0xcd36, 0x0000 }, + { 0xcd37, 0x0000 }, + { 0xcd38, 0x0000 }, + { 0xcd39, 0x0000 }, + { 0xcd3a, 0x0000 }, + { 0xcd3b, 0x0000 }, + { 0xcd3c, 0x0000 }, + { 0xcd3d, 0x0000 }, + { 0xcd3e, 0x0000 }, + { 0xcd3f, 0x0000 }, + { 0xcd40, 0x0000 }, + { 0xcd41, 0x0000 }, + { 0xcd42, 0x0000 }, + { 0xcd43, 0x0000 }, + { 0xcd44, 0x0000 }, + { 0xcd45, 0x0000 }, + { 0xcd46, 0x0000 }, + { 0xcd47, 0x0000 }, + { 0xcd48, 0x0000 }, + { 0xcd49, 0x0000 }, + { 0xcd4a, 0x0000 }, + { 0xcd4b, 0x0000 }, + { 0xcd4c, 0x0000 }, + { 0xcd4d, 0x0000 }, + { 0xcd4e, 0x0000 }, + { 0xcd4f, 0x0000 }, + { 0xcd50, 0x0000 }, + { 0xcd51, 0x0000 }, + { 0xcd52, 0x0000 }, + { 0xcd53, 0x0000 }, + { 0xcd54, 0x0000 }, + { 0xcd55, 0x0000 }, + { 0xcd56, 0x0000 }, + { 0xcd57, 0x0000 }, + { 0xcd58, 0x0000 }, + { 0xcd59, 0x0000 }, + { 0xcd5a, 0x0000 }, + { 0xcd5b, 0x0000 }, + { 0xcd5c, 0x0000 }, + { 0xcd5d, 0x0000 }, + { 0xcd5e, 0x0000 }, + { 0xcd5f, 0x0000 }, + { 0xcd60, 0x0000 }, + { 0xcd61, 0x0000 }, + { 0xcd62, 0x0000 }, + { 0xcd63, 0x0000 }, + { 0xcd64, 0x0000 }, + { 0xcd65, 0x0000 }, + { 0xcd66, 0x0000 }, + { 0xcd67, 0x0000 }, + { 0xcd68, 0x0000 }, + { 0xcd69, 0x0000 }, + { 0xcd6a, 0x0000 }, + { 0xcd6b, 0x0000 }, + { 0xcd6c, 0x0000 }, + { 0xcd6d, 0x0000 }, + { 0xcd6e, 0x0000 }, + { 0xcd6f, 0x0000 }, + { 0xcd70, 0x0000 }, + { 0xcd71, 0x0000 }, + { 0xcd72, 0x0000 }, + { 0xcd73, 0x0000 }, + { 0xcd74, 0x0000 }, + { 0xcd75, 0x0000 }, + { 0xcd76, 0x0000 }, + { 0xcd77, 0x0000 }, + { 0xcd78, 0x0000 }, + { 0xcd79, 0x0000 }, + { 0xcd7a, 0x0000 }, + { 0xcd7b, 0x0000 }, + { 0xcd7c, 0x0000 }, + { 0xcd7d, 0x0000 }, + { 0xcd7e, 0x0000 }, + { 0xcd7f, 0x0000 }, + { 0xcd80, 0x0000 }, + { 0xcd81, 0x0000 }, + { 0xcd82, 0x0000 }, + { 0xcd83, 0x0000 }, + { 0xcd84, 0x0000 }, + { 0xcd85, 0x0000 }, + { 0xcd86, 0x0000 }, + { 0xcd87, 0x0000 }, + { 0xcd88, 0x0000 }, + { 0xcd89, 0x0000 }, + { 0xcd8a, 0x0000 }, + { 0xcd8b, 0x0000 }, + { 0xcd8c, 0x0000 }, + { 0xcd8d, 0x0000 }, + { 0xcd8e, 0x0000 }, + { 0xcd8f, 0x0000 }, + { 0xcd90, 0x0000 }, + { 0xcd91, 0x0000 }, + { 0xcd92, 0x0000 }, + { 0xcd93, 0x0000 }, + { 0xcd94, 0x0000 }, + { 0xcd95, 0x0000 }, + { 0xcd96, 0x0000 }, + { 0xcd97, 0x0000 }, + { 0xcd98, 0x0000 }, + { 0xcd99, 0x0000 }, + { 0xcd9a, 0x0000 }, + { 0xcd9b, 0x0000 }, + { 0xcd9c, 0x0000 }, + { 0xcd9d, 0x0000 }, + { 0xcd9e, 0x0000 }, + { 0xcd9f, 0x0000 }, + { 0xcda0, 0x0000 }, + { 0xcda1, 0x0000 }, + { 0xcda2, 0x0000 }, + { 0xcda3, 0x0000 }, + { 0xcda4, 0x0000 }, + { 0xcda5, 0x0000 }, + { 0xcda6, 0x0000 }, + { 0xcda7, 0x0000 }, + { 0xcda8, 0x0000 }, + { 0xcda9, 0x0000 }, + { 0xcdaa, 0x0000 }, + { 0xcdab, 0x0000 }, + { 0xcdac, 0x0000 }, + { 0xcdad, 0x0000 }, + { 0xcdae, 0x0000 }, + { 0xcdaf, 0x0000 }, + { 0xcdb0, 0x0000 }, + { 0xcdb1, 0x0000 }, + { 0xcdb2, 0x0000 }, + { 0xcdb3, 0x0000 }, + { 0xcdb4, 0x0000 }, + { 0xcdb5, 0x0000 }, + { 0xcdb6, 0x0000 }, + { 0xcdb7, 0x0000 }, + { 0xcdb8, 0x0000 }, + { 0xcdb9, 0x0000 }, + { 0xcdba, 0x0000 }, + { 0xcdbb, 0x0000 }, + { 0xcdbc, 0x0000 }, + { 0xcdbd, 0x0000 }, + { 0xcdbe, 0x0000 }, + { 0xcdbf, 0x0000 }, + { 0xcdc0, 0x0000 }, + { 0xcdc1, 0x0000 }, + { 0xcdc2, 0x0000 }, + { 0xcdc3, 0x0000 }, + { 0xcdc4, 0x0000 }, + { 0xcdc5, 0x0000 }, + { 0xcdc6, 0x0000 }, + { 0xcdc7, 0x0000 }, + { 0xcdc8, 0x0000 }, + { 0xcdc9, 0x0000 }, + { 0xcdca, 0x0000 }, + { 0xcdcb, 0x0000 }, + { 0xcdcc, 0x0000 }, + { 0xcdcd, 0x0000 }, + { 0xcdce, 0x0000 }, + { 0xcdcf, 0x0000 }, + { 0xcdd0, 0x0000 }, + { 0xcdd1, 0x0000 }, + { 0xcdd2, 0x0000 }, + { 0xcdd3, 0x0000 }, + { 0xcdd4, 0x0000 }, + { 0xcdd5, 0x0000 }, + { 0xcdd6, 0x0000 }, + { 0xcdd7, 0x0000 }, + { 0xcdd8, 0x0000 }, + { 0xcdd9, 0x0000 }, + { 0xcdda, 0x0000 }, + { 0xcddb, 0x0000 }, + { 0xcddc, 0x0000 }, + { 0xcddd, 0x0000 }, + { 0xcdde, 0x0000 }, + { 0xcddf, 0x0000 }, + { 0xcde0, 0x0000 }, + { 0xcde1, 0x0000 }, + { 0xcde2, 0x0000 }, + { 0xcde3, 0x0000 }, + { 0xcde4, 0x0000 }, + { 0xcde5, 0x0000 }, + { 0xcde6, 0x0000 }, + { 0xcde7, 0x0000 }, + { 0xcde8, 0x0000 }, + { 0xcde9, 0x0000 }, + { 0xcdea, 0x0000 }, + { 0xcdeb, 0x0000 }, + { 0xcdec, 0x0000 }, + { 0xcded, 0x0000 }, + { 0xcdee, 0x0000 }, + { 0xcdef, 0x0000 }, + { 0xcdf0, 0x0000 }, + { 0xcdf1, 0x0000 }, + { 0xcdf2, 0x0000 }, + { 0xcdf3, 0x0000 }, + { 0xcdf4, 0x0000 }, + { 0xcdf5, 0x0000 }, + { 0xcdf6, 0x0000 }, + { 0xcdf7, 0x0000 }, + { 0xcdf8, 0x0000 }, + { 0xcdf9, 0x0000 }, + { 0xcdfa, 0x0000 }, + { 0xcdfb, 0x0000 }, + { 0xcdfc, 0x0000 }, + { 0xcdfd, 0x0000 }, + { 0xcdfe, 0x0000 }, + { 0xcdff, 0x0000 }, + { 0xce00, 0x0000 }, + { 0xce01, 0x0000 }, + { 0xce02, 0x0000 }, + { 0xce03, 0x0000 }, + { 0xce04, 0x0000 }, + { 0xce05, 0x0000 }, + { 0xce06, 0x0000 }, + { 0xce07, 0x0000 }, + { 0xce08, 0x0000 }, + { 0xce09, 0x0000 }, + { 0xce0a, 0x0000 }, + { 0xce0b, 0x0000 }, + { 0xce0c, 0x0000 }, + { 0xce0d, 0x0000 }, + { 0xce0e, 0x0000 }, + { 0xce0f, 0x0000 }, + { 0xce10, 0x0000 }, + { 0xce11, 0x0000 }, + { 0xce12, 0x0000 }, + { 0xce13, 0x0000 }, + { 0xce14, 0x0000 }, + { 0xce15, 0x0000 }, + { 0xce16, 0x0000 }, + { 0xce17, 0x0000 }, + { 0xce18, 0x0000 }, + { 0xce19, 0x0000 }, + { 0xce1a, 0x0000 }, + { 0xce1b, 0x0000 }, + { 0xce1c, 0x0000 }, + { 0xce1d, 0x0000 }, + { 0xce1e, 0x0000 }, + { 0xce1f, 0x0000 }, + { 0xce20, 0x0000 }, + { 0xce21, 0x0000 }, + { 0xce22, 0x0000 }, + { 0xce23, 0x0000 }, + { 0xce24, 0x0000 }, + { 0xce25, 0x0000 }, + { 0xce26, 0x0000 }, + { 0xce27, 0x0000 }, + { 0xce28, 0x0000 }, + { 0xce29, 0x0000 }, + { 0xce2a, 0x0000 }, + { 0xce2b, 0x0000 }, + { 0xce2c, 0x0000 }, + { 0xce2d, 0x0000 }, + { 0xce2e, 0x0000 }, + { 0xce2f, 0x0000 }, + { 0xce30, 0x0000 }, + { 0xce31, 0x0000 }, + { 0xce32, 0x0000 }, + { 0xce33, 0x0000 }, + { 0xce34, 0x0000 }, + { 0xce35, 0x0000 }, + { 0xce36, 0x0000 }, + { 0xce37, 0x0000 }, + { 0xce38, 0x0000 }, + { 0xce39, 0x0000 }, + { 0xce3a, 0x0000 }, + { 0xce3b, 0x0000 }, + { 0xce3c, 0x0000 }, + { 0xce3d, 0x0000 }, + { 0xce3e, 0x0000 }, + { 0xce3f, 0x0000 }, + { 0xce40, 0x0000 }, + { 0xce41, 0x0000 }, + { 0xce42, 0x0000 }, + { 0xce43, 0x0000 }, + { 0xce44, 0x0000 }, + { 0xce45, 0x0000 }, + { 0xce46, 0x0000 }, + { 0xce47, 0x0000 }, + { 0xce48, 0x0000 }, + { 0xce49, 0x0000 }, + { 0xce4a, 0x0000 }, + { 0xce4b, 0x0000 }, + { 0xce4c, 0x0000 }, + { 0xce4d, 0x0000 }, + { 0xce4e, 0x0000 }, + { 0xce4f, 0x0000 }, + { 0xce50, 0x0000 }, + { 0xce51, 0x0000 }, + { 0xce52, 0x0000 }, + { 0xce53, 0x0000 }, + { 0xce54, 0x0000 }, + { 0xce55, 0x0000 }, + { 0xce56, 0x0000 }, + { 0xce57, 0x0000 }, + { 0xce58, 0x0000 }, + { 0xce59, 0x0000 }, + { 0xce5a, 0x0000 }, + { 0xce5b, 0x0000 }, + { 0xce5c, 0x0000 }, + { 0xce5d, 0x0000 }, + { 0xce5e, 0x0000 }, + { 0xce5f, 0x0000 }, + { 0xce60, 0x0000 }, + { 0xce61, 0x0000 }, + { 0xce62, 0x0000 }, + { 0xce63, 0x0000 }, + { 0xce64, 0x0000 }, + { 0xce65, 0x0000 }, + { 0xce66, 0x0000 }, + { 0xce67, 0x0000 }, + { 0xce68, 0x0000 }, + { 0xce69, 0x0000 }, + { 0xce6a, 0x0000 }, + { 0xce6b, 0x0000 }, + { 0xce6c, 0x0000 }, + { 0xce6d, 0x0000 }, + { 0xce6e, 0x0000 }, + { 0xce6f, 0x0000 }, + { 0xce70, 0x0000 }, + { 0xce71, 0x0000 }, + { 0xce72, 0x0000 }, + { 0xce73, 0x0000 }, + { 0xce74, 0x0000 }, + { 0xce75, 0x0000 }, + { 0xce76, 0x0000 }, + { 0xce77, 0x0000 }, + { 0xce78, 0x0000 }, + { 0xce79, 0x0000 }, + { 0xce7a, 0x0000 }, + { 0xce7b, 0x0000 }, + { 0xce7c, 0x0000 }, + { 0xce7d, 0x0000 }, + { 0xce7e, 0x0000 }, + { 0xce7f, 0x0000 }, + { 0xce80, 0x0000 }, + { 0xce81, 0x0000 }, + { 0xce82, 0x0000 }, + { 0xce83, 0x0000 }, + { 0xce84, 0x0000 }, + { 0xce85, 0x0000 }, + { 0xce86, 0x0000 }, + { 0xce87, 0x0000 }, + { 0xce88, 0x0000 }, + { 0xce89, 0x0000 }, + { 0xce8a, 0x0000 }, + { 0xce8b, 0x0000 }, + { 0xce8c, 0x0000 }, + { 0xce8d, 0x0000 }, + { 0xce8e, 0x0000 }, + { 0xce8f, 0x0000 }, + { 0xce90, 0x0000 }, + { 0xce91, 0x0000 }, + { 0xce92, 0x0000 }, + { 0xce93, 0x0000 }, + { 0xce94, 0x0000 }, + { 0xce95, 0x0000 }, + { 0xce96, 0x0000 }, + { 0xce97, 0x0000 }, + { 0xce98, 0x0000 }, + { 0xce99, 0x0000 }, + { 0xce9a, 0x0000 }, + { 0xce9b, 0x0000 }, + { 0xce9c, 0x0000 }, + { 0xce9d, 0x0000 }, + { 0xce9e, 0x0000 }, + { 0xce9f, 0x0000 }, + { 0xcea0, 0x0000 }, + { 0xcea1, 0x0000 }, + { 0xcea2, 0x0000 }, + { 0xcea3, 0x0000 }, + { 0xcea4, 0x0000 }, + { 0xcea5, 0x0000 }, + { 0xcea6, 0x0000 }, + { 0xcea7, 0x0000 }, + { 0xcea8, 0x0000 }, + { 0xcea9, 0x0000 }, + { 0xceaa, 0x0000 }, + { 0xceab, 0x0000 }, + { 0xceac, 0x0000 }, + { 0xcead, 0x0000 }, + { 0xceae, 0x0000 }, + { 0xceaf, 0x0000 }, + { 0xceb0, 0x0000 }, + { 0xceb1, 0x0000 }, + { 0xceb2, 0x0000 }, + { 0xceb3, 0x0000 }, + { 0xceb4, 0x0000 }, + { 0xceb5, 0x0000 }, + { 0xceb6, 0x0000 }, + { 0xceb7, 0x0000 }, + { 0xceb8, 0x0000 }, + { 0xceb9, 0x0000 }, + { 0xceba, 0x0000 }, + { 0xcebb, 0x0000 }, + { 0xcebc, 0x0000 }, + { 0xcebd, 0x0000 }, + { 0xcebe, 0x0000 }, + { 0xcebf, 0x0000 }, + { 0xcec0, 0x0000 }, + { 0xcec1, 0x0000 }, + { 0xcec2, 0x0000 }, + { 0xcec3, 0x0000 }, + { 0xcec4, 0x0000 }, + { 0xcec5, 0x0000 }, + { 0xcec6, 0x0000 }, + { 0xcec7, 0x0000 }, + { 0xcec8, 0x0000 }, + { 0xcec9, 0x0000 }, + { 0xceca, 0x0000 }, + { 0xcecb, 0x0000 }, + { 0xcecc, 0x0000 }, + { 0xcecd, 0x0000 }, + { 0xcece, 0x0000 }, + { 0xcecf, 0x0000 }, + { 0xced0, 0x0000 }, + { 0xced1, 0x0000 }, + { 0xced2, 0x0000 }, + { 0xced3, 0x0000 }, + { 0xced4, 0x0000 }, + { 0xced5, 0x0000 }, + { 0xced6, 0x0000 }, + { 0xced7, 0x0000 }, + { 0xced8, 0x0000 }, + { 0xced9, 0x0000 }, + { 0xceda, 0x0000 }, + { 0xcedb, 0x0000 }, + { 0xcedc, 0x0000 }, + { 0xcedd, 0x0000 }, + { 0xcede, 0x0000 }, + { 0xcedf, 0x0000 }, + { 0xcee0, 0x0000 }, + { 0xcee1, 0x0000 }, + { 0xcee2, 0x0000 }, + { 0xcee3, 0x0000 }, + { 0xcee4, 0x0000 }, + { 0xcee5, 0x0000 }, + { 0xcee6, 0x0000 }, + { 0xcee7, 0x0000 }, + { 0xcee8, 0x0000 }, + { 0xcee9, 0x0000 }, + { 0xceea, 0x0000 }, + { 0xceeb, 0x0000 }, + { 0xceec, 0x0000 }, + { 0xceed, 0x0000 }, + { 0xceee, 0x0000 }, + { 0xceef, 0x0000 }, + { 0xcef0, 0x0000 }, + { 0xcef1, 0x0000 }, + { 0xcef2, 0x0000 }, + { 0xcef3, 0x0000 }, + { 0xcef4, 0x0000 }, + { 0xcef5, 0x0000 }, + { 0xcef6, 0x0000 }, + { 0xcef7, 0x0000 }, + { 0xcef8, 0x0000 }, + { 0xcef9, 0x0000 }, + { 0xcefa, 0x0000 }, + { 0xcefb, 0x0000 }, + { 0xcefc, 0x0000 }, + { 0xcefd, 0x0000 }, + { 0xcefe, 0x0000 }, + { 0xceff, 0x0000 }, + { 0xcf00, 0x0000 }, + { 0xcf01, 0x0000 }, + { 0xcf02, 0x0000 }, + { 0xcf03, 0x0000 }, + { 0xcf04, 0x0000 }, + { 0xcf05, 0x0000 }, + { 0xcf06, 0x0000 }, + { 0xcf07, 0x0000 }, + { 0xcf08, 0x0000 }, + { 0xcf09, 0x0000 }, + { 0xcf0a, 0x0000 }, + { 0xcf0b, 0x0000 }, + { 0xcf0c, 0x0000 }, + { 0xcf0d, 0x0000 }, + { 0xcf0e, 0x0000 }, + { 0xcf0f, 0x0000 }, + { 0xcf10, 0x0000 }, + { 0xcf11, 0x0000 }, + { 0xcf12, 0x0000 }, + { 0xcf13, 0x0000 }, + { 0xcf14, 0x0000 }, + { 0xcf15, 0x0000 }, + { 0xcf16, 0x0000 }, + { 0xcf17, 0x0000 }, + { 0xcf18, 0x0000 }, + { 0xcf19, 0x0000 }, + { 0xcf1a, 0x0000 }, + { 0xcf1b, 0x0000 }, + { 0xcf1c, 0x0000 }, + { 0xcf1d, 0x0000 }, + { 0xcf1e, 0x0000 }, + { 0xcf1f, 0x0000 }, + { 0xcf20, 0x0000 }, + { 0xcf21, 0x0000 }, + { 0xcf22, 0x0000 }, + { 0xcf23, 0x0000 }, + { 0xcf24, 0x0000 }, + { 0xcf25, 0x0000 }, + { 0xcf26, 0x0000 }, + { 0xcf27, 0x0000 }, + { 0xcf28, 0x0000 }, + { 0xcf29, 0x0000 }, + { 0xcf2a, 0x0000 }, + { 0xcf2b, 0x0000 }, + { 0xcf2c, 0x0000 }, + { 0xcf2d, 0x0000 }, + { 0xcf2e, 0x0000 }, + { 0xcf2f, 0x0000 }, + { 0xcf30, 0x0000 }, + { 0xcf31, 0x0000 }, + { 0xcf32, 0x0000 }, + { 0xcf33, 0x0000 }, + { 0xcf34, 0x0000 }, + { 0xcf35, 0x0000 }, + { 0xcf36, 0x0000 }, + { 0xcf37, 0x0000 }, + { 0xcf38, 0x0000 }, + { 0xcf39, 0x0000 }, + { 0xcf3a, 0x0000 }, + { 0xcf3b, 0x0000 }, + { 0xcf3c, 0x0000 }, + { 0xcf3d, 0x0000 }, + { 0xcf3e, 0x0000 }, + { 0xcf3f, 0x0000 }, + { 0xcf40, 0x0000 }, + { 0xcf41, 0x0000 }, + { 0xcf42, 0x0000 }, + { 0xcf43, 0x0000 }, + { 0xcf44, 0x0000 }, + { 0xcf45, 0x0000 }, + { 0xcf46, 0x0000 }, + { 0xcf47, 0x0000 }, + { 0xcf48, 0x0000 }, + { 0xcf49, 0x0000 }, + { 0xcf4a, 0x0000 }, + { 0xcf4b, 0x0000 }, + { 0xcf4c, 0x0000 }, + { 0xcf4d, 0x0000 }, + { 0xcf4e, 0x0000 }, + { 0xcf4f, 0x0000 }, + { 0xcf50, 0x0000 }, + { 0xcf51, 0x0000 }, + { 0xcf52, 0x0000 }, + { 0xcf53, 0x0000 }, + { 0xcf54, 0x0000 }, + { 0xcf55, 0x0000 }, + { 0xcf56, 0x0000 }, + { 0xcf57, 0x0000 }, + { 0xcf58, 0x0000 }, + { 0xcf59, 0x0000 }, + { 0xcf5a, 0x0000 }, + { 0xcf5b, 0x0000 }, + { 0xcf5c, 0x0000 }, + { 0xcf5d, 0x0000 }, + { 0xcf5e, 0x0000 }, + { 0xcf5f, 0x0000 }, + { 0xcf60, 0x0000 }, + { 0xcf61, 0x0000 }, + { 0xcf62, 0x0000 }, + { 0xcf63, 0x0000 }, + { 0xcf64, 0x0000 }, + { 0xcf65, 0x0000 }, + { 0xcf66, 0x0000 }, + { 0xcf67, 0x0000 }, + { 0xcf68, 0x0000 }, + { 0xcf69, 0x0000 }, + { 0xcf6a, 0x0000 }, + { 0xcf6b, 0x0000 }, + { 0xcf6c, 0x0000 }, + { 0xcf6d, 0x0000 }, + { 0xcf6e, 0x0000 }, + { 0xcf6f, 0x0000 }, + { 0xcf70, 0x0000 }, + { 0xcf71, 0x0000 }, + { 0xcf72, 0x0000 }, + { 0xcf73, 0x0000 }, + { 0xcf74, 0x0000 }, + { 0xcf75, 0x0000 }, + { 0xcf76, 0x0000 }, + { 0xcf77, 0x0000 }, + { 0xcf78, 0x0000 }, + { 0xcf79, 0x0000 }, + { 0xcf7a, 0x0000 }, + { 0xcf7b, 0x0000 }, + { 0xcf7c, 0x0000 }, + { 0xcf7d, 0x0000 }, + { 0xcf7e, 0x0000 }, + { 0xcf7f, 0x0000 }, + { 0xcf80, 0x0000 }, + { 0xcf81, 0x0000 }, + { 0xcf82, 0x0000 }, + { 0xcf83, 0x0000 }, + { 0xcf84, 0x0000 }, + { 0xcf85, 0x0000 }, + { 0xcf86, 0x0000 }, + { 0xcf87, 0x0000 }, + { 0xcf88, 0x0000 }, + { 0xcf89, 0x0000 }, + { 0xcf8a, 0x0000 }, + { 0xcf8b, 0x0000 }, + { 0xcf8c, 0x0000 }, + { 0xcf8d, 0x0000 }, + { 0xcf8e, 0x0000 }, + { 0xcf8f, 0x0000 }, + { 0xcf90, 0x0000 }, + { 0xcf91, 0x0000 }, + { 0xcf92, 0x0000 }, + { 0xcf93, 0x0000 }, + { 0xcf94, 0x0000 }, + { 0xcf95, 0x0000 }, + { 0xcf96, 0x0000 }, + { 0xcf97, 0x0000 }, + { 0xcf98, 0x0000 }, + { 0xcf99, 0x0000 }, + { 0xcf9a, 0x0000 }, + { 0xcf9b, 0x0000 }, + { 0xcf9c, 0x0000 }, + { 0xcf9d, 0x0000 }, + { 0xcf9e, 0x0000 }, + { 0xcf9f, 0x0000 }, + { 0xcfa0, 0x0000 }, + { 0xcfa1, 0x0000 }, + { 0xcfa2, 0x0000 }, + { 0xcfa3, 0x0000 }, + { 0xcfa4, 0x0000 }, + { 0xcfa5, 0x0000 }, + { 0xcfa6, 0x0000 }, + { 0xcfa7, 0x0000 }, + { 0xcfa8, 0x0000 }, + { 0xcfa9, 0x0000 }, + { 0xcfaa, 0x0000 }, + { 0xcfab, 0x0000 }, + { 0xcfac, 0x0000 }, + { 0xcfad, 0x0000 }, + { 0xcfae, 0x0000 }, + { 0xcfaf, 0x0000 }, + { 0xcfb0, 0x0000 }, + { 0xcfb1, 0x0000 }, + { 0xcfb2, 0x0000 }, + { 0xcfb3, 0x0000 }, + { 0xcfb4, 0x0000 }, + { 0xcfb5, 0x0000 }, + { 0xcfb6, 0x0000 }, + { 0xcfb7, 0x0000 }, + { 0xcfb8, 0x0000 }, + { 0xcfb9, 0x0000 }, + { 0xcfba, 0x0000 }, + { 0xcfbb, 0x0000 }, + { 0xcfbc, 0x0000 }, + { 0xcfbd, 0x0000 }, + { 0xcfbe, 0x0000 }, + { 0xcfbf, 0x0000 }, + { 0xcfc0, 0x0000 }, + { 0xcfc1, 0x0000 }, + { 0xcfc2, 0x0000 }, + { 0xcfc3, 0x0000 }, + { 0xcfc4, 0x0000 }, + { 0xcfc5, 0x0000 }, + { 0xcfc6, 0x0000 }, + { 0xcfc7, 0x0000 }, + { 0xcfc8, 0x0000 }, + { 0xcfc9, 0x0000 }, + { 0xcfca, 0x0000 }, + { 0xcfcb, 0x0000 }, + { 0xcfcc, 0x0000 }, + { 0xcfcd, 0x0000 }, + { 0xcfce, 0x0000 }, + { 0xcfcf, 0x0000 }, + { 0xcfd0, 0x0000 }, + { 0xcfd1, 0x0000 }, + { 0xcfd2, 0x0000 }, + { 0xcfd3, 0x0000 }, + { 0xcfd4, 0x0000 }, + { 0xcfd5, 0x0000 }, + { 0xcfd6, 0x0000 }, + { 0xcfd7, 0x0000 }, + { 0xcfd8, 0x0000 }, + { 0xcfd9, 0x0000 }, + { 0xcfda, 0x0000 }, + { 0xcfdb, 0x0000 }, + { 0xcfdc, 0x0000 }, + { 0xcfdd, 0x0000 }, + { 0xcfde, 0x0000 }, + { 0xcfdf, 0x0000 }, + { 0xcfe0, 0x0000 }, + { 0xcfe1, 0x0000 }, + { 0xcfe2, 0x0000 }, + { 0xcfe3, 0x0000 }, + { 0xcfe4, 0x0000 }, + { 0xcfe5, 0x0000 }, + { 0xcfe6, 0x0000 }, + { 0xcfe7, 0x0000 }, + { 0xcfe8, 0x0000 }, + { 0xcfe9, 0x0000 }, + { 0xcfea, 0x0000 }, + { 0xcfeb, 0x0000 }, + { 0xcfec, 0x0000 }, + { 0xcfed, 0x0000 }, + { 0xcfee, 0x0000 }, + { 0xcfef, 0x0000 }, + { 0xcff0, 0x0000 }, + { 0xcff1, 0x0000 }, + { 0xcff2, 0x0000 }, + { 0xcff3, 0x0000 }, + { 0xcff4, 0x0000 }, + { 0xcff5, 0x0000 }, + { 0xcff6, 0x0000 }, + { 0xcff7, 0x0000 }, + { 0xcff8, 0x0000 }, + { 0xcff9, 0x0000 }, + { 0xcffa, 0x0000 }, + { 0xcffb, 0x0000 }, + { 0xcffc, 0x0000 }, + { 0xcffd, 0x0000 }, + { 0xcffe, 0x0000 }, + { 0xcfff, 0x0000 }, + { 0xd000, 0x0000 }, + { 0xd001, 0x0000 }, + { 0xd002, 0x0000 }, + { 0xd003, 0x0000 }, + { 0xd004, 0x0000 }, + { 0xd005, 0x0000 }, + { 0xd006, 0x0000 }, + { 0xd007, 0x0000 }, + { 0xd008, 0x0000 }, + { 0xd009, 0x0000 }, + { 0xd00a, 0x0000 }, + { 0xd00b, 0x0000 }, + { 0xd00c, 0x0000 }, + { 0xd00d, 0x0000 }, + { 0xd00e, 0x0000 }, + { 0xd00f, 0x0000 }, + { 0xd010, 0x0000 }, + { 0xd011, 0x0000 }, + { 0xd012, 0x0000 }, + { 0xd013, 0x0000 }, + { 0xd014, 0x0000 }, + { 0xd015, 0x0000 }, + { 0xd016, 0x0000 }, + { 0xd017, 0x0000 }, + { 0xd018, 0x0000 }, + { 0xd019, 0x0000 }, + { 0xd01a, 0x0000 }, + { 0xd01b, 0x0000 }, + { 0xd01c, 0x0000 }, + { 0xd01d, 0x0000 }, + { 0xd01e, 0x0000 }, + { 0xd01f, 0x0000 }, + { 0xd020, 0x0000 }, + { 0xd021, 0x0000 }, + { 0xd022, 0x0000 }, + { 0xd023, 0x0000 }, + { 0xd024, 0x0000 }, + { 0xd025, 0x0000 }, + { 0xd026, 0x0000 }, + { 0xd027, 0x0000 }, + { 0xd028, 0x0000 }, + { 0xd029, 0x0000 }, + { 0xd02a, 0x0000 }, + { 0xd02b, 0x0000 }, + { 0xd02c, 0x0000 }, + { 0xd02d, 0x0000 }, + { 0xd02e, 0x0000 }, + { 0xd02f, 0x0000 }, + { 0xd030, 0x0000 }, + { 0xd031, 0x0000 }, + { 0xd032, 0x0000 }, + { 0xd033, 0x0000 }, + { 0xd034, 0x0000 }, + { 0xd035, 0x0000 }, + { 0xd036, 0x0000 }, + { 0xd037, 0x0000 }, + { 0xd038, 0x0000 }, + { 0xd039, 0x0000 }, + { 0xd03a, 0x0000 }, + { 0xd03b, 0x0000 }, + { 0xd03c, 0x0000 }, + { 0xd03d, 0x0000 }, + { 0xd03e, 0x0000 }, + { 0xd03f, 0x0000 }, + { 0xd040, 0x0000 }, + { 0xd041, 0x0000 }, + { 0xd042, 0x0000 }, + { 0xd043, 0x0000 }, + { 0xd044, 0x0000 }, + { 0xd045, 0x0000 }, + { 0xd046, 0x0000 }, + { 0xd047, 0x0000 }, + { 0xd048, 0x0000 }, + { 0xd049, 0x0000 }, + { 0xd04a, 0x0000 }, + { 0xd04b, 0x0000 }, + { 0xd04c, 0x0000 }, + { 0xd04d, 0x0000 }, + { 0xd04e, 0x0000 }, + { 0xd04f, 0x0000 }, + { 0xd050, 0x0000 }, + { 0xd051, 0x0000 }, + { 0xd052, 0x0000 }, + { 0xd053, 0x0000 }, + { 0xd054, 0x0000 }, + { 0xd055, 0x0000 }, + { 0xd056, 0x0000 }, + { 0xd057, 0x0000 }, + { 0xd058, 0x0000 }, + { 0xd059, 0x0000 }, + { 0xd05a, 0x0000 }, + { 0xd05b, 0x0000 }, + { 0xd05c, 0x0000 }, + { 0xd05d, 0x0000 }, + { 0xd05e, 0x0000 }, + { 0xd05f, 0x0000 }, + { 0xd060, 0x0000 }, + { 0xd061, 0x0000 }, + { 0xd062, 0x0000 }, + { 0xd063, 0x0000 }, + { 0xd064, 0x0000 }, + { 0xd065, 0x0000 }, + { 0xd066, 0x0000 }, + { 0xd067, 0x0000 }, + { 0xd068, 0x0000 }, + { 0xd069, 0x0000 }, + { 0xd06a, 0x0000 }, + { 0xd06b, 0x0000 }, + { 0xd06c, 0x0000 }, + { 0xd06d, 0x0000 }, + { 0xd06e, 0x0000 }, + { 0xd06f, 0x0000 }, + { 0xd070, 0x0000 }, + { 0xd071, 0x0000 }, + { 0xd072, 0x0000 }, + { 0xd073, 0x0000 }, + { 0xd074, 0x0000 }, + { 0xd075, 0x0000 }, + { 0xd076, 0x0000 }, + { 0xd077, 0x0000 }, + { 0xd078, 0x0000 }, + { 0xd079, 0x0000 }, + { 0xd07a, 0x0000 }, + { 0xd07b, 0x0000 }, + { 0xd07c, 0x0000 }, + { 0xd07d, 0x0000 }, + { 0xd07e, 0x0000 }, + { 0xd07f, 0x0000 }, + { 0xd080, 0x0000 }, + { 0xd081, 0x0000 }, + { 0xd082, 0x0000 }, + { 0xd083, 0x0000 }, + { 0xd084, 0x0000 }, + { 0xd085, 0x0000 }, + { 0xd086, 0x0000 }, + { 0xd087, 0x0000 }, + { 0xd088, 0x0000 }, + { 0xd089, 0x0000 }, + { 0xd08a, 0x0000 }, + { 0xd08b, 0x0000 }, + { 0xd08c, 0x0000 }, + { 0xd08d, 0x0000 }, + { 0xd08e, 0x0000 }, + { 0xd08f, 0x0000 }, + { 0xd090, 0x0000 }, + { 0xd091, 0x0000 }, + { 0xd092, 0x0000 }, + { 0xd093, 0x0000 }, + { 0xd094, 0x0000 }, + { 0xd095, 0x0000 }, + { 0xd096, 0x0000 }, + { 0xd097, 0x0000 }, + { 0xd098, 0x0000 }, + { 0xd099, 0x0000 }, + { 0xd09a, 0x0000 }, + { 0xd09b, 0x0000 }, + { 0xd09c, 0x0000 }, + { 0xd09d, 0x0000 }, + { 0xd09e, 0x0000 }, + { 0xd09f, 0x0000 }, + { 0xd0a0, 0x0000 }, + { 0xd0a1, 0x0000 }, + { 0xd0a2, 0x0000 }, + { 0xd0a3, 0x0000 }, + { 0xd0a4, 0x0000 }, + { 0xd0a5, 0x0000 }, + { 0xd0a6, 0x0000 }, + { 0xd0a7, 0x0000 }, + { 0xd0a8, 0x0000 }, + { 0xd0a9, 0x0000 }, + { 0xd0aa, 0x0000 }, + { 0xd0ab, 0x0000 }, + { 0xd0ac, 0x0000 }, + { 0xd0ad, 0x0000 }, + { 0xd0ae, 0x0000 }, + { 0xd0af, 0x0000 }, + { 0xd0b0, 0x0000 }, + { 0xd0b1, 0x0000 }, + { 0xd0b2, 0x0000 }, + { 0xd0b3, 0x0000 }, + { 0xd0b4, 0x0000 }, + { 0xd0b5, 0x0000 }, + { 0xd0b6, 0x0000 }, + { 0xd0b7, 0x0000 }, + { 0xd0b8, 0x0000 }, + { 0xd0b9, 0x0000 }, + { 0xd0ba, 0x0000 }, + { 0xd0bb, 0x0000 }, + { 0xd0bc, 0x0000 }, + { 0xd0bd, 0x0000 }, + { 0xd0be, 0x0000 }, + { 0xd0bf, 0x0000 }, + { 0xd0c0, 0x0000 }, + { 0xd0c1, 0x0000 }, + { 0xd0c2, 0x0000 }, + { 0xd0c3, 0x0000 }, + { 0xd0c4, 0x0000 }, + { 0xd0c5, 0x0000 }, + { 0xd0c6, 0x0000 }, + { 0xd0c7, 0x0000 }, + { 0xd0c8, 0x0000 }, + { 0xd0c9, 0x0000 }, + { 0xd0ca, 0x0000 }, + { 0xd0cb, 0x0000 }, + { 0xd0cc, 0x0000 }, + { 0xd0cd, 0x0000 }, + { 0xd0ce, 0x0000 }, + { 0xd0cf, 0x0000 }, + { 0xd0d0, 0x0000 }, + { 0xd0d1, 0x0000 }, + { 0xd0d2, 0x0000 }, + { 0xd0d3, 0x0000 }, + { 0xd0d4, 0x0000 }, + { 0xd0d5, 0x0000 }, + { 0xd0d6, 0x0000 }, + { 0xd0d7, 0x0000 }, + { 0xd0d8, 0x0000 }, + { 0xd0d9, 0x0000 }, + { 0xd0da, 0x0000 }, + { 0xd0db, 0x0000 }, + { 0xd0dc, 0x0000 }, + { 0xd0dd, 0x0000 }, + { 0xd0de, 0x0000 }, + { 0xd0df, 0x0000 }, + { 0xd0e0, 0x0000 }, + { 0xd0e1, 0x0000 }, + { 0xd0e2, 0x0000 }, + { 0xd0e3, 0x0000 }, + { 0xd0e4, 0x0000 }, + { 0xd0e5, 0x0000 }, + { 0xd0e6, 0x0000 }, + { 0xd0e7, 0x0000 }, + { 0xd0e8, 0x0000 }, + { 0xd0e9, 0x0000 }, + { 0xd0ea, 0x0000 }, + { 0xd0eb, 0x0000 }, + { 0xd0ec, 0x0000 }, + { 0xd0ed, 0x0000 }, + { 0xd0ee, 0x0000 }, + { 0xd0ef, 0x0000 }, + { 0xd0f0, 0x0000 }, + { 0xd0f1, 0x0000 }, + { 0xd0f2, 0x0000 }, + { 0xd0f3, 0x0000 }, + { 0xd0f4, 0x0000 }, + { 0xd0f5, 0x0000 }, + { 0xd0f6, 0x0000 }, + { 0xd0f7, 0x0000 }, + { 0xd0f8, 0x0000 }, + { 0xd0f9, 0x0000 }, + { 0xd0fa, 0x0000 }, + { 0xd0fb, 0x0000 }, + { 0xd0fc, 0x0000 }, + { 0xd0fd, 0x0000 }, + { 0xd0fe, 0x0000 }, + { 0xd0ff, 0x0000 }, + { 0xd100, 0x0000 }, + { 0xd101, 0x0000 }, + { 0xd102, 0x0000 }, + { 0xd103, 0x0000 }, + { 0xd104, 0x0000 }, + { 0xd105, 0x0000 }, + { 0xd106, 0x0000 }, + { 0xd107, 0x0000 }, + { 0xd108, 0x0000 }, + { 0xd109, 0x0000 }, + { 0xd10a, 0x0000 }, + { 0xd10b, 0x0000 }, + { 0xd10c, 0x0000 }, + { 0xd10d, 0x0000 }, + { 0xd10e, 0x0000 }, + { 0xd10f, 0x0000 }, + { 0xd110, 0x0000 }, + { 0xd111, 0x0000 }, + { 0xd112, 0x0000 }, + { 0xd113, 0x0000 }, + { 0xd114, 0x0000 }, + { 0xd115, 0x0000 }, + { 0xd116, 0x0000 }, + { 0xd117, 0x0000 }, + { 0xd118, 0x0000 }, + { 0xd119, 0x0000 }, + { 0xd11a, 0x0000 }, + { 0xd11b, 0x0000 }, + { 0xd11c, 0x0000 }, + { 0xd11d, 0x0000 }, + { 0xd11e, 0x0000 }, + { 0xd11f, 0x0000 }, + { 0xd120, 0x0000 }, + { 0xd121, 0x0000 }, + { 0xd122, 0x0000 }, + { 0xd123, 0x0000 }, + { 0xd124, 0x0000 }, + { 0xd125, 0x0000 }, + { 0xd126, 0x0000 }, + { 0xd127, 0x0000 }, + { 0xd128, 0x0000 }, + { 0xd129, 0x0000 }, + { 0xd12a, 0x0000 }, + { 0xd12b, 0x0000 }, + { 0xd12c, 0x0000 }, + { 0xd12d, 0x0000 }, + { 0xd12e, 0x0000 }, + { 0xd12f, 0x0000 }, + { 0xd130, 0x0000 }, + { 0xd131, 0x0000 }, + { 0xd132, 0x0000 }, + { 0xd133, 0x0000 }, + { 0xd134, 0x0000 }, + { 0xd135, 0x0000 }, + { 0xd136, 0x0000 }, + { 0xd137, 0x0000 }, + { 0xd138, 0x0000 }, + { 0xd139, 0x0000 }, + { 0xd13a, 0x0000 }, + { 0xd13b, 0x0000 }, + { 0xd13c, 0x0000 }, + { 0xd13d, 0x0000 }, + { 0xd13e, 0x0000 }, + { 0xd13f, 0x0000 }, + { 0xd140, 0x0000 }, + { 0xd141, 0x0000 }, + { 0xd142, 0x0000 }, + { 0xd143, 0x0000 }, + { 0xd144, 0x0000 }, + { 0xd145, 0x0000 }, + { 0xd146, 0x0000 }, + { 0xd147, 0x0000 }, + { 0xd148, 0x0000 }, + { 0xd149, 0x0000 }, + { 0xd14a, 0x0000 }, + { 0xd14b, 0x0000 }, + { 0xd14c, 0x0000 }, + { 0xd14d, 0x0000 }, + { 0xd14e, 0x0000 }, + { 0xd14f, 0x0000 }, + { 0xd150, 0x0000 }, + { 0xd151, 0x0000 }, + { 0xd152, 0x0000 }, + { 0xd153, 0x0000 }, + { 0xd154, 0x0000 }, + { 0xd155, 0x0000 }, + { 0xd156, 0x0000 }, + { 0xd157, 0x0000 }, + { 0xd158, 0x0000 }, + { 0xd159, 0x0000 }, + { 0xd15a, 0x0000 }, + { 0xd15b, 0x0000 }, + { 0xd15c, 0x0000 }, + { 0xd15d, 0x0000 }, + { 0xd15e, 0x0000 }, + { 0xd15f, 0x0000 }, + { 0xd160, 0x0000 }, + { 0xd161, 0x0000 }, + { 0xd162, 0x0000 }, + { 0xd163, 0x0000 }, + { 0xd164, 0x0000 }, + { 0xd165, 0x0000 }, + { 0xd166, 0x0000 }, + { 0xd167, 0x0000 }, + { 0xd168, 0x0000 }, + { 0xd169, 0x0000 }, + { 0xd16a, 0x0000 }, + { 0xd16b, 0x0000 }, + { 0xd16c, 0x0000 }, + { 0xd16d, 0x0000 }, + { 0xd16e, 0x0000 }, + { 0xd16f, 0x0000 }, + { 0xd170, 0x0000 }, + { 0xd171, 0x0000 }, + { 0xd172, 0x0000 }, + { 0xd173, 0x0000 }, + { 0xd174, 0x0000 }, + { 0xd175, 0x0000 }, + { 0xd176, 0x0000 }, + { 0xd177, 0x0000 }, + { 0xd178, 0x0000 }, + { 0xd179, 0x0000 }, + { 0xd17a, 0x0000 }, + { 0xd17b, 0x0000 }, + { 0xd17c, 0x0000 }, + { 0xd17d, 0x0000 }, + { 0xd17e, 0x0000 }, + { 0xd17f, 0x0000 }, + { 0xd180, 0x0000 }, + { 0xd181, 0x0000 }, + { 0xd182, 0x0000 }, + { 0xd183, 0x0000 }, + { 0xd184, 0x0000 }, + { 0xd185, 0x0000 }, + { 0xd186, 0x0000 }, + { 0xd187, 0x0000 }, + { 0xd188, 0x0000 }, + { 0xd189, 0x0000 }, + { 0xd18a, 0x0000 }, + { 0xd18b, 0x0000 }, + { 0xd18c, 0x0000 }, + { 0xd18d, 0x0000 }, + { 0xd18e, 0x0000 }, + { 0xd18f, 0x0000 }, + { 0xd190, 0x0000 }, + { 0xd191, 0x0000 }, + { 0xd192, 0x0000 }, + { 0xd193, 0x0000 }, + { 0xd194, 0x0000 }, + { 0xd195, 0x0000 }, + { 0xd196, 0x0000 }, + { 0xd197, 0x0000 }, + { 0xd198, 0x0000 }, + { 0xd199, 0x0000 }, + { 0xd19a, 0x0000 }, + { 0xd19b, 0x0000 }, + { 0xd19c, 0x0000 }, + { 0xd19d, 0x0000 }, + { 0xd19e, 0x0000 }, + { 0xd19f, 0x0000 }, + { 0xd1a0, 0x0000 }, + { 0xd1a1, 0x0000 }, + { 0xd1a2, 0x0000 }, + { 0xd1a3, 0x0000 }, + { 0xd1a4, 0x0000 }, + { 0xd1a5, 0x0000 }, + { 0xd1a6, 0x0000 }, + { 0xd1a7, 0x0000 }, + { 0xd1a8, 0x0000 }, + { 0xd1a9, 0x0000 }, + { 0xd1aa, 0x0000 }, + { 0xd1ab, 0x0000 }, + { 0xd1ac, 0x0000 }, + { 0xd1ad, 0x0000 }, + { 0xd1ae, 0x0000 }, + { 0xd1af, 0x0000 }, + { 0xd1b0, 0x0000 }, + { 0xd1b1, 0x0000 }, + { 0xd1b2, 0x0000 }, + { 0xd1b3, 0x0000 }, + { 0xd1b4, 0x0000 }, + { 0xd1b5, 0x0000 }, + { 0xd1b6, 0x0000 }, + { 0xd1b7, 0x0000 }, + { 0xd1b8, 0x0000 }, + { 0xd1b9, 0x0000 }, + { 0xd1ba, 0x0000 }, + { 0xd1bb, 0x0000 }, + { 0xd1bc, 0x0000 }, + { 0xd1bd, 0x0000 }, + { 0xd1be, 0x0000 }, + { 0xd1bf, 0x0000 }, + { 0xd1c0, 0x0000 }, + { 0xd1c1, 0x0000 }, + { 0xd1c2, 0x0000 }, + { 0xd1c3, 0x0000 }, + { 0xd1c4, 0x0000 }, + { 0xd1c5, 0x0000 }, + { 0xd1c6, 0x0000 }, + { 0xd1c7, 0x0000 }, + { 0xd1c8, 0x0000 }, + { 0xd1c9, 0x0000 }, + { 0xd1ca, 0x0000 }, + { 0xd1cb, 0x0000 }, + { 0xd1cc, 0x0000 }, + { 0xd1cd, 0x0000 }, + { 0xd1ce, 0x0000 }, + { 0xd1cf, 0x0000 }, + { 0xd1d0, 0x0000 }, + { 0xd1d1, 0x0000 }, + { 0xd1d2, 0x0000 }, + { 0xd1d3, 0x0000 }, + { 0xd1d4, 0x0000 }, + { 0xd1d5, 0x0000 }, + { 0xd1d6, 0x0000 }, + { 0xd1d7, 0x0000 }, + { 0xd1d8, 0x0000 }, + { 0xd1d9, 0x0000 }, + { 0xd1da, 0x0000 }, + { 0xd1db, 0x0000 }, + { 0xd1dc, 0x0000 }, + { 0xd1dd, 0x0000 }, + { 0xd1de, 0x0000 }, + { 0xd1df, 0x0000 }, + { 0xd1e0, 0x0000 }, + { 0xd1e1, 0x0000 }, + { 0xd1e2, 0x0000 }, + { 0xd1e3, 0x0000 }, + { 0xd1e4, 0x0000 }, + { 0xd1e5, 0x0000 }, + { 0xd1e6, 0x0000 }, + { 0xd1e7, 0x0000 }, + { 0xd1e8, 0x0000 }, + { 0xd1e9, 0x0000 }, + { 0xd1ea, 0x0000 }, + { 0xd1eb, 0x0000 }, + { 0xd1ec, 0x0000 }, + { 0xd1ed, 0x0000 }, + { 0xd1ee, 0x0000 }, + { 0xd1ef, 0x0000 }, + { 0xd1f0, 0x0000 }, + { 0xd1f1, 0x0000 }, + { 0xd1f2, 0x0000 }, + { 0xd1f3, 0x0000 }, + { 0xd1f4, 0x0000 }, + { 0xd1f5, 0x0000 }, + { 0xd1f6, 0x0000 }, + { 0xd1f7, 0x0000 }, + { 0xd1f8, 0x0000 }, + { 0xd1f9, 0x0000 }, + { 0xd1fa, 0x0000 }, + { 0xd1fb, 0x0000 }, + { 0xd1fc, 0x0000 }, + { 0xd1fd, 0x0000 }, + { 0xd1fe, 0x0000 }, + { 0xd1ff, 0x0000 }, + { 0xd200, 0x0000 }, + { 0xd201, 0x0000 }, + { 0xd202, 0x0000 }, + { 0xd203, 0x0000 }, + { 0xd204, 0x0000 }, + { 0xd205, 0x0000 }, + { 0xd206, 0x0000 }, + { 0xd207, 0x0000 }, + { 0xd208, 0x0000 }, + { 0xd209, 0x0000 }, + { 0xd20a, 0x0000 }, + { 0xd20b, 0x0000 }, + { 0xd20c, 0x0000 }, + { 0xd20d, 0x0000 }, + { 0xd20e, 0x0000 }, + { 0xd20f, 0x0000 }, + { 0xd210, 0x0000 }, + { 0xd211, 0x0000 }, + { 0xd212, 0x0000 }, + { 0xd213, 0x0000 }, + { 0xd214, 0x0000 }, + { 0xd215, 0x0000 }, + { 0xd216, 0x0000 }, + { 0xd217, 0x0000 }, + { 0xd218, 0x0000 }, + { 0xd219, 0x0000 }, + { 0xd21a, 0x0000 }, + { 0xd21b, 0x0000 }, + { 0xd21c, 0x0000 }, + { 0xd21d, 0x0000 }, + { 0xd21e, 0x0000 }, + { 0xd21f, 0x0000 }, + { 0xd220, 0x0000 }, + { 0xd221, 0x0000 }, + { 0xd222, 0x0000 }, + { 0xd223, 0x0000 }, + { 0xd224, 0x0000 }, + { 0xd225, 0x0000 }, + { 0xd226, 0x0000 }, + { 0xd227, 0x0000 }, + { 0xd228, 0x0000 }, + { 0xd229, 0x0000 }, + { 0xd22a, 0x0000 }, + { 0xd22b, 0x0000 }, + { 0xd22c, 0x0000 }, + { 0xd22d, 0x0000 }, + { 0xd22e, 0x0000 }, + { 0xd22f, 0x0000 }, + { 0xd230, 0x0000 }, + { 0xd231, 0x0000 }, + { 0xd232, 0x0000 }, + { 0xd233, 0x0000 }, + { 0xd234, 0x0000 }, + { 0xd235, 0x0000 }, + { 0xd236, 0x0000 }, + { 0xd237, 0x0000 }, + { 0xd238, 0x0000 }, + { 0xd239, 0x0000 }, + { 0xd23a, 0x0000 }, + { 0xd23b, 0x0000 }, + { 0xd23c, 0x0000 }, + { 0xd23d, 0x0000 }, + { 0xd23e, 0x0000 }, + { 0xd23f, 0x0000 }, + { 0xd240, 0x0000 }, + { 0xd241, 0x0000 }, + { 0xd242, 0x0000 }, + { 0xd243, 0x0000 }, + { 0xd244, 0x0000 }, + { 0xd245, 0x0000 }, + { 0xd246, 0x0000 }, + { 0xd247, 0x0000 }, + { 0xd248, 0x0000 }, + { 0xd249, 0x0000 }, + { 0xd24a, 0x0000 }, + { 0xd24b, 0x0000 }, + { 0xd24c, 0x0000 }, + { 0xd24d, 0x0000 }, + { 0xd24e, 0x0000 }, + { 0xd24f, 0x0000 }, + { 0xd250, 0x0000 }, + { 0xd251, 0x0000 }, + { 0xd252, 0x0000 }, + { 0xd253, 0x0000 }, + { 0xd254, 0x0000 }, + { 0xd255, 0x0000 }, + { 0xd256, 0x0000 }, + { 0xd257, 0x0000 }, + { 0xd258, 0x0000 }, + { 0xd259, 0x0000 }, + { 0xd25a, 0x0000 }, + { 0xd25b, 0x0000 }, + { 0xd25c, 0x0000 }, + { 0xd25d, 0x0000 }, + { 0xd25e, 0x0000 }, + { 0xd25f, 0x0000 }, + { 0xd260, 0x0000 }, + { 0xd261, 0x0000 }, + { 0xd262, 0x0000 }, + { 0xd263, 0x0000 }, + { 0xd264, 0x0000 }, + { 0xd265, 0x0000 }, + { 0xd266, 0x0000 }, + { 0xd267, 0x0000 }, + { 0xd268, 0x0000 }, + { 0xd269, 0x0000 }, + { 0xd26a, 0x0000 }, + { 0xd26b, 0x0000 }, + { 0xd26c, 0x0000 }, + { 0xd26d, 0x0000 }, + { 0xd26e, 0x0000 }, + { 0xd26f, 0x0000 }, + { 0xd270, 0x0000 }, + { 0xd271, 0x0000 }, + { 0xd272, 0x0000 }, + { 0xd273, 0x0000 }, + { 0xd274, 0x0000 }, + { 0xd275, 0x0000 }, + { 0xd276, 0x0000 }, + { 0xd277, 0x0000 }, + { 0xd278, 0x0000 }, + { 0xd279, 0x0000 }, + { 0xd27a, 0x0000 }, + { 0xd27b, 0x0000 }, + { 0xd27c, 0x0000 }, + { 0xd27d, 0x0000 }, + { 0xd27e, 0x0000 }, + { 0xd27f, 0x0000 }, + { 0xd280, 0x0000 }, + { 0xd281, 0x0000 }, + { 0xd282, 0x0000 }, + { 0xd283, 0x0000 }, + { 0xd284, 0x0000 }, + { 0xd285, 0x0000 }, + { 0xd286, 0x0000 }, + { 0xd287, 0x0000 }, + { 0xd288, 0x0000 }, + { 0xd289, 0x0000 }, + { 0xd28a, 0x0000 }, + { 0xd28b, 0x0000 }, + { 0xd28c, 0x0000 }, + { 0xd28d, 0x0000 }, + { 0xd28e, 0x0000 }, + { 0xd28f, 0x0000 }, + { 0xd290, 0x0000 }, + { 0xd291, 0x0000 }, + { 0xd292, 0x0000 }, + { 0xd293, 0x0000 }, + { 0xd294, 0x0000 }, + { 0xd295, 0x0000 }, + { 0xd296, 0x0000 }, + { 0xd297, 0x0000 }, + { 0xd298, 0x0000 }, + { 0xd299, 0x0000 }, + { 0xd29a, 0x0000 }, + { 0xd29b, 0x0000 }, + { 0xd29c, 0x0000 }, + { 0xd29d, 0x0000 }, + { 0xd29e, 0x0000 }, + { 0xd29f, 0x0000 }, + { 0xd2a0, 0x0000 }, + { 0xd2a1, 0x0000 }, + { 0xd2a2, 0x0000 }, + { 0xd2a3, 0x0000 }, + { 0xd2a4, 0x0000 }, + { 0xd2a5, 0x0000 }, + { 0xd2a6, 0x0000 }, + { 0xd2a7, 0x0000 }, + { 0xd2a8, 0x0000 }, + { 0xd2a9, 0x0000 }, + { 0xd2aa, 0x0000 }, + { 0xd2ab, 0x0000 }, + { 0xd2ac, 0x0000 }, + { 0xd2ad, 0x0000 }, + { 0xd2ae, 0x0000 }, + { 0xd2af, 0x0000 }, + { 0xd2b0, 0x0000 }, + { 0xd2b1, 0x0000 }, + { 0xd2b2, 0x0000 }, + { 0xd2b3, 0x0000 }, + { 0xd2b4, 0x0000 }, + { 0xd2b5, 0x0000 }, + { 0xd2b6, 0x0000 }, + { 0xd2b7, 0x0000 }, + { 0xd2b8, 0x0000 }, + { 0xd2b9, 0x0000 }, + { 0xd2ba, 0x0000 }, + { 0xd2bb, 0x0000 }, + { 0xd2bc, 0x0000 }, + { 0xd2bd, 0x0000 }, + { 0xd2be, 0x0000 }, + { 0xd2bf, 0x0000 }, + { 0xd2c0, 0x0000 }, + { 0xd2c1, 0x0000 }, + { 0xd2c2, 0x0000 }, + { 0xd2c3, 0x0000 }, + { 0xd2c4, 0x0000 }, + { 0xd2c5, 0x0000 }, + { 0xd2c6, 0x0000 }, + { 0xd2c7, 0x0000 }, + { 0xd2c8, 0x0000 }, + { 0xd2c9, 0x0000 }, + { 0xd2ca, 0x0000 }, + { 0xd2cb, 0x0000 }, + { 0xd2cc, 0x0000 }, + { 0xd2cd, 0x0000 }, + { 0xd2ce, 0x0000 }, + { 0xd2cf, 0x0000 }, + { 0xd2d0, 0x0000 }, + { 0xd2d1, 0x0000 }, + { 0xd2d2, 0x0000 }, + { 0xd2d3, 0x0000 }, + { 0xd2d4, 0x0000 }, + { 0xd2d5, 0x0000 }, + { 0xd2d6, 0x0000 }, + { 0xd2d7, 0x0000 }, + { 0xd2d8, 0x0000 }, + { 0xd2d9, 0x0000 }, + { 0xd2da, 0x0000 }, + { 0xd2db, 0x0000 }, + { 0xd2dc, 0x0000 }, + { 0xd2dd, 0x0000 }, + { 0xd2de, 0x0000 }, + { 0xd2df, 0x0000 }, + { 0xd2e0, 0x0000 }, + { 0xd2e1, 0x0000 }, + { 0xd2e2, 0x0000 }, + { 0xd2e3, 0x0000 }, + { 0xd2e4, 0x0000 }, + { 0xd2e5, 0x0000 }, + { 0xd2e6, 0x0000 }, + { 0xd2e7, 0x0000 }, + { 0xd2e8, 0x0000 }, + { 0xd2e9, 0x0000 }, + { 0xd2ea, 0x0000 }, + { 0xd2eb, 0x0000 }, + { 0xd2ec, 0x0000 }, + { 0xd2ed, 0x0000 }, + { 0xd2ee, 0x0000 }, + { 0xd2ef, 0x0000 }, + { 0xd2f0, 0x0000 }, + { 0xd2f1, 0x0000 }, + { 0xd2f2, 0x0000 }, + { 0xd2f3, 0x0000 }, + { 0xd2f4, 0x0000 }, + { 0xd2f5, 0x0000 }, + { 0xd2f6, 0x0000 }, + { 0xd2f7, 0x0000 }, + { 0xd2f8, 0x0000 }, + { 0xd2f9, 0x0000 }, + { 0xd2fa, 0x0000 }, + { 0xd2fb, 0x0000 }, + { 0xd2fc, 0x0000 }, + { 0xd2fd, 0x0000 }, + { 0xd2fe, 0x0000 }, + { 0xd2ff, 0x0000 }, + { 0xd300, 0x0000 }, + { 0xd301, 0x0000 }, + { 0xd302, 0x0000 }, + { 0xd303, 0x0000 }, + { 0xd304, 0x0000 }, + { 0xd305, 0x0000 }, + { 0xd306, 0x0000 }, + { 0xd307, 0x0000 }, + { 0xd308, 0x0000 }, + { 0xd309, 0x0000 }, + { 0xd30a, 0x0000 }, + { 0xd30b, 0x0000 }, + { 0xd30c, 0x0000 }, + { 0xd30d, 0x0000 }, + { 0xd30e, 0x0000 }, + { 0xd30f, 0x0000 }, + { 0xd310, 0x0000 }, + { 0xd311, 0x0000 }, + { 0xd312, 0x0000 }, + { 0xd313, 0x0000 }, + { 0xd314, 0x0000 }, + { 0xd315, 0x0000 }, + { 0xd316, 0x0000 }, + { 0xd317, 0x0000 }, + { 0xd318, 0x0000 }, + { 0xd319, 0x0000 }, + { 0xd31a, 0x0000 }, + { 0xd31b, 0x0000 }, + { 0xd31c, 0x0000 }, + { 0xd31d, 0x0000 }, + { 0xd31e, 0x0000 }, + { 0xd31f, 0x0000 }, + { 0xd320, 0x0000 }, + { 0xd321, 0x0000 }, + { 0xd322, 0x0000 }, + { 0xd323, 0x0000 }, + { 0xd324, 0x0000 }, + { 0xd325, 0x0000 }, + { 0xd326, 0x0000 }, + { 0xd327, 0x0000 }, + { 0xd328, 0x0000 }, + { 0xd329, 0x0000 }, + { 0xd32a, 0x0000 }, + { 0xd32b, 0x0000 }, + { 0xd32c, 0x0000 }, + { 0xd32d, 0x0000 }, + { 0xd32e, 0x0000 }, + { 0xd32f, 0x0000 }, + { 0xd330, 0x0000 }, + { 0xd331, 0x0000 }, + { 0xd332, 0x0000 }, + { 0xd333, 0x0000 }, + { 0xd334, 0x0000 }, + { 0xd335, 0x0000 }, + { 0xd336, 0x0000 }, + { 0xd337, 0x0000 }, + { 0xd338, 0x0000 }, + { 0xd339, 0x0000 }, + { 0xd33a, 0x0000 }, + { 0xd33b, 0x0000 }, + { 0xd33c, 0x0000 }, + { 0xd33d, 0x0000 }, + { 0xd33e, 0x0000 }, + { 0xd33f, 0x0000 }, + { 0xd340, 0x0000 }, + { 0xd341, 0x0000 }, + { 0xd342, 0x0000 }, + { 0xd343, 0x0000 }, + { 0xd344, 0x0000 }, + { 0xd345, 0x0000 }, + { 0xd346, 0x0000 }, + { 0xd347, 0x0000 }, + { 0xd348, 0x0000 }, + { 0xd349, 0x0000 }, + { 0xd34a, 0x0000 }, + { 0xd34b, 0x0000 }, + { 0xd34c, 0x0000 }, + { 0xd34d, 0x0000 }, + { 0xd34e, 0x0000 }, + { 0xd34f, 0x0000 }, + { 0xd350, 0x0000 }, + { 0xd351, 0x0000 }, + { 0xd352, 0x0000 }, + { 0xd353, 0x0000 }, + { 0xd354, 0x0000 }, + { 0xd355, 0x0000 }, + { 0xd356, 0x0000 }, + { 0xd357, 0x0000 }, + { 0xd358, 0x0000 }, + { 0xd359, 0x0000 }, + { 0xd35a, 0x0000 }, + { 0xd35b, 0x0000 }, + { 0xd35c, 0x0000 }, + { 0xd35d, 0x0000 }, + { 0xd35e, 0x0000 }, + { 0xd35f, 0x0000 }, + { 0xd360, 0x0000 }, + { 0xd361, 0x0000 }, + { 0xd362, 0x0000 }, + { 0xd363, 0x0000 }, + { 0xd364, 0x0000 }, + { 0xd365, 0x0000 }, + { 0xd366, 0x0000 }, + { 0xd367, 0x0000 }, + { 0xd368, 0x0000 }, + { 0xd369, 0x0000 }, + { 0xd36a, 0x0000 }, + { 0xd36b, 0x0000 }, + { 0xd36c, 0x0000 }, + { 0xd36d, 0x0000 }, + { 0xd36e, 0x0000 }, + { 0xd36f, 0x0000 }, + { 0xd370, 0x0000 }, + { 0xd371, 0x0000 }, + { 0xd372, 0x0000 }, + { 0xd373, 0x0000 }, + { 0xd374, 0x0000 }, + { 0xd375, 0x0000 }, + { 0xd376, 0x0000 }, + { 0xd377, 0x0000 }, + { 0xd378, 0x0000 }, + { 0xd379, 0x0000 }, + { 0xd37a, 0x0000 }, + { 0xd37b, 0x0000 }, + { 0xd37c, 0x0000 }, + { 0xd37d, 0x0000 }, + { 0xd37e, 0x0000 }, + { 0xd37f, 0x0000 }, + { 0xd380, 0x0000 }, + { 0xd381, 0x0000 }, + { 0xd382, 0x0000 }, + { 0xd383, 0x0000 }, + { 0xd384, 0x0000 }, + { 0xd385, 0x0000 }, + { 0xd386, 0x0000 }, + { 0xd387, 0x0000 }, + { 0xd388, 0x0000 }, + { 0xd389, 0x0000 }, + { 0xd38a, 0x0000 }, + { 0xd38b, 0x0000 }, + { 0xd38c, 0x0000 }, + { 0xd38d, 0x0000 }, + { 0xd38e, 0x0000 }, + { 0xd38f, 0x0000 }, + { 0xd390, 0x0000 }, + { 0xd391, 0x0000 }, + { 0xd392, 0x0000 }, + { 0xd393, 0x0000 }, + { 0xd394, 0x0000 }, + { 0xd395, 0x0000 }, + { 0xd396, 0x0000 }, + { 0xd397, 0x0000 }, + { 0xd398, 0x0000 }, + { 0xd399, 0x0000 }, + { 0xd39a, 0x0000 }, + { 0xd39b, 0x0000 }, + { 0xd39c, 0x0000 }, + { 0xd39d, 0x0000 }, + { 0xd39e, 0x0000 }, + { 0xd39f, 0x0000 }, + { 0xd3a0, 0x0000 }, + { 0xd3a1, 0x0000 }, + { 0xd3a2, 0x0000 }, + { 0xd3a3, 0x0000 }, + { 0xd3a4, 0x0000 }, + { 0xd3a5, 0x0000 }, + { 0xd3a6, 0x0000 }, + { 0xd3a7, 0x0000 }, + { 0xd3a8, 0x0000 }, + { 0xd3a9, 0x0000 }, + { 0xd3aa, 0x0000 }, + { 0xd3ab, 0x0000 }, + { 0xd3ac, 0x0000 }, + { 0xd3ad, 0x0000 }, + { 0xd3ae, 0x0000 }, + { 0xd3af, 0x0000 }, + { 0xd3b0, 0x0000 }, + { 0xd3b1, 0x0000 }, + { 0xd3b2, 0x0000 }, + { 0xd3b3, 0x0000 }, + { 0xd3b4, 0x0000 }, + { 0xd3b5, 0x0000 }, + { 0xd3b6, 0x0000 }, + { 0xd3b7, 0x0000 }, + { 0xd3b8, 0x0000 }, + { 0xd3b9, 0x0000 }, + { 0xd3ba, 0x0000 }, + { 0xd3bb, 0x0000 }, + { 0xd3bc, 0x0000 }, + { 0xd3bd, 0x0000 }, + { 0xd3be, 0x0000 }, + { 0xd3bf, 0x0000 }, + { 0xd3c0, 0x0000 }, + { 0xd3c1, 0x0000 }, + { 0xd3c2, 0x0000 }, + { 0xd3c3, 0x0000 }, + { 0xd3c4, 0x0000 }, + { 0xd3c5, 0x0000 }, + { 0xd3c6, 0x0000 }, + { 0xd3c7, 0x0000 }, + { 0xd3c8, 0x0000 }, + { 0xd3c9, 0x0000 }, + { 0xd3ca, 0x0000 }, + { 0xd3cb, 0x0000 }, + { 0xd3cc, 0x0000 }, + { 0xd3cd, 0x0000 }, + { 0xd3ce, 0x0000 }, + { 0xd3cf, 0x0000 }, + { 0xd3d0, 0x0000 }, + { 0xd3d1, 0x0000 }, + { 0xd3d2, 0x0000 }, + { 0xd3d3, 0x0000 }, + { 0xd3d4, 0x0000 }, + { 0xd3d5, 0x0000 }, + { 0xd3d6, 0x0000 }, + { 0xd3d7, 0x0000 }, + { 0xd3d8, 0x0000 }, + { 0xd3d9, 0x0000 }, + { 0xd3da, 0x0000 }, + { 0xd3db, 0x0000 }, + { 0xd3dc, 0x0000 }, + { 0xd3dd, 0x0000 }, + { 0xd3de, 0x0000 }, + { 0xd3df, 0x0000 }, + { 0xd3e0, 0x0000 }, + { 0xd3e1, 0x0000 }, + { 0xd3e2, 0x0000 }, + { 0xd3e3, 0x0000 }, + { 0xd3e4, 0x0000 }, + { 0xd3e5, 0x0000 }, + { 0xd3e6, 0x0000 }, + { 0xd3e7, 0x0000 }, + { 0xd3e8, 0x0000 }, + { 0xd3e9, 0x0000 }, + { 0xd3ea, 0x0000 }, + { 0xd3eb, 0x0000 }, + { 0xd3ec, 0x0000 }, + { 0xd3ed, 0x0000 }, + { 0xd3ee, 0x0000 }, + { 0xd3ef, 0x0000 }, + { 0xd3f0, 0x0000 }, + { 0xd3f1, 0x0000 }, + { 0xd3f2, 0x0000 }, + { 0xd3f3, 0x0000 }, + { 0xd3f4, 0x0000 }, + { 0xd3f5, 0x0000 }, + { 0xd3f6, 0x0000 }, + { 0xd3f7, 0x0000 }, + { 0xd3f8, 0x0000 }, + { 0xd3f9, 0x0000 }, + { 0xd3fa, 0x0000 }, + { 0xd3fb, 0x0000 }, + { 0xd3fc, 0x0000 }, + { 0xd3fd, 0x0000 }, + { 0xd3fe, 0x0000 }, + { 0xd3ff, 0x0000 }, + { 0xd400, 0x0000 }, + { 0xd401, 0x0000 }, + { 0xd402, 0x0000 }, + { 0xd403, 0x0000 }, + { 0xd404, 0x0000 }, + { 0xd405, 0x0000 }, + { 0xd406, 0x0000 }, + { 0xd407, 0x0000 }, + { 0xd408, 0x0000 }, + { 0xd409, 0x0000 }, + { 0xd40a, 0x0000 }, + { 0xd40b, 0x0000 }, + { 0xd40c, 0x0000 }, + { 0xd40d, 0x0000 }, + { 0xd40e, 0x0000 }, + { 0xd40f, 0x0000 }, + { 0xd410, 0x0000 }, + { 0xd411, 0x0000 }, + { 0xd412, 0x0000 }, + { 0xd413, 0x0000 }, + { 0xd414, 0x0000 }, + { 0xd415, 0x0000 }, + { 0xd416, 0x0000 }, + { 0xd417, 0x0000 }, + { 0xd418, 0x0000 }, + { 0xd419, 0x0000 }, + { 0xd41a, 0x0000 }, + { 0xd41b, 0x0000 }, + { 0xd41c, 0x0000 }, + { 0xd41d, 0x0000 }, + { 0xd41e, 0x0000 }, + { 0xd41f, 0x0000 }, + { 0xd420, 0x0000 }, + { 0xd421, 0x0000 }, + { 0xd422, 0x0000 }, + { 0xd423, 0x0000 }, + { 0xd424, 0x0000 }, + { 0xd425, 0x0000 }, + { 0xd426, 0x0000 }, + { 0xd427, 0x0000 }, + { 0xd428, 0x0000 }, + { 0xd429, 0x0000 }, + { 0xd42a, 0x0000 }, + { 0xd42b, 0x0000 }, + { 0xd42c, 0x0000 }, + { 0xd42d, 0x0000 }, + { 0xd42e, 0x0000 }, + { 0xd42f, 0x0000 }, + { 0xd430, 0x0000 }, + { 0xd431, 0x0000 }, + { 0xd432, 0x0000 }, + { 0xd433, 0x0000 }, + { 0xd434, 0x0000 }, + { 0xd435, 0x0000 }, + { 0xd436, 0x0000 }, + { 0xd437, 0x0000 }, + { 0xd438, 0x0000 }, + { 0xd439, 0x0000 }, + { 0xd43a, 0x0000 }, + { 0xd43b, 0x0000 }, + { 0xd43c, 0x0000 }, + { 0xd43d, 0x0000 }, + { 0xd43e, 0x0000 }, + { 0xd43f, 0x0000 }, + { 0xd440, 0x0000 }, + { 0xd441, 0x0000 }, + { 0xd442, 0x0000 }, + { 0xd443, 0x0000 }, + { 0xd444, 0x0000 }, + { 0xd445, 0x0000 }, + { 0xd446, 0x0000 }, + { 0xd447, 0x0000 }, + { 0xd448, 0x0000 }, + { 0xd449, 0x0000 }, + { 0xd44a, 0x0000 }, + { 0xd44b, 0x0000 }, + { 0xd44c, 0x0000 }, + { 0xd44d, 0x0000 }, + { 0xd44e, 0x0000 }, + { 0xd44f, 0x0000 }, + { 0xd450, 0x0000 }, + { 0xd451, 0x0000 }, + { 0xd452, 0x0000 }, + { 0xd453, 0x0000 }, + { 0xd454, 0x0000 }, + { 0xd455, 0x0000 }, + { 0xd456, 0x0000 }, + { 0xd457, 0x0000 }, + { 0xd458, 0x0000 }, + { 0xd459, 0x0000 }, + { 0xd45a, 0x0000 }, + { 0xd45b, 0x0000 }, + { 0xd45c, 0x0000 }, + { 0xd45d, 0x0000 }, + { 0xd45e, 0x0000 }, + { 0xd45f, 0x0000 }, + { 0xd460, 0x0000 }, + { 0xd461, 0x0000 }, + { 0xd462, 0x0000 }, + { 0xd463, 0x0000 }, + { 0xd464, 0x0000 }, + { 0xd465, 0x0000 }, + { 0xd466, 0x0000 }, + { 0xd467, 0x0000 }, + { 0xd468, 0x0000 }, + { 0xd469, 0x0000 }, + { 0xd46a, 0x0000 }, + { 0xd46b, 0x0000 }, + { 0xd46c, 0x0000 }, + { 0xd46d, 0x0000 }, + { 0xd46e, 0x0000 }, + { 0xd46f, 0x0000 }, + { 0xd470, 0x0000 }, + { 0xd471, 0x0000 }, + { 0xd472, 0x0000 }, + { 0xd473, 0x0000 }, + { 0xd474, 0x0000 }, + { 0xd475, 0x0000 }, + { 0xd476, 0x0000 }, + { 0xd477, 0x0000 }, + { 0xd478, 0x0000 }, + { 0xd479, 0x0000 }, + { 0xd47a, 0x0000 }, + { 0xd47b, 0x0000 }, + { 0xd47c, 0x0000 }, + { 0xd47d, 0x0000 }, + { 0xd47e, 0x0000 }, + { 0xd47f, 0x0000 }, + { 0xd480, 0x0000 }, + { 0xd481, 0x0000 }, + { 0xd482, 0x0000 }, + { 0xd483, 0x0000 }, + { 0xd484, 0x0000 }, + { 0xd485, 0x0000 }, + { 0xd486, 0x0000 }, + { 0xd487, 0x0000 }, + { 0xd488, 0x0000 }, + { 0xd489, 0x0000 }, + { 0xd48a, 0x0000 }, + { 0xd48b, 0x0000 }, + { 0xd48c, 0x0000 }, + { 0xd48d, 0x0000 }, + { 0xd48e, 0x0000 }, + { 0xd48f, 0x0000 }, + { 0xd490, 0x0000 }, + { 0xd491, 0x0000 }, + { 0xd492, 0x0000 }, + { 0xd493, 0x0000 }, + { 0xd494, 0x0000 }, + { 0xd495, 0x0000 }, + { 0xd496, 0x0000 }, + { 0xd497, 0x0000 }, + { 0xd498, 0x0000 }, + { 0xd499, 0x0000 }, + { 0xd49a, 0x0000 }, + { 0xd49b, 0x0000 }, + { 0xd49c, 0x0000 }, + { 0xd49d, 0x0000 }, + { 0xd49e, 0x0000 }, + { 0xd49f, 0x0000 }, + { 0xd4a0, 0x0000 }, + { 0xd4a1, 0x0000 }, + { 0xd4a2, 0x0000 }, + { 0xd4a3, 0x0000 }, + { 0xd4a4, 0x0000 }, + { 0xd4a5, 0x0000 }, + { 0xd4a6, 0x0000 }, + { 0xd4a7, 0x0000 }, + { 0xd4a8, 0x0000 }, + { 0xd4a9, 0x0000 }, + { 0xd4aa, 0x0000 }, + { 0xd4ab, 0x0000 }, + { 0xd4ac, 0x0000 }, + { 0xd4ad, 0x0000 }, + { 0xd4ae, 0x0000 }, + { 0xd4af, 0x0000 }, + { 0xd4b0, 0x0000 }, + { 0xd4b1, 0x0000 }, + { 0xd4b2, 0x0000 }, + { 0xd4b3, 0x0000 }, + { 0xd4b4, 0x0000 }, + { 0xd4b5, 0x0000 }, + { 0xd4b6, 0x0000 }, + { 0xd4b7, 0x0000 }, + { 0xd4b8, 0x0000 }, + { 0xd4b9, 0x0000 }, + { 0xd4ba, 0x0000 }, + { 0xd4bb, 0x0000 }, + { 0xd4bc, 0x0000 }, + { 0xd4bd, 0x0000 }, + { 0xd4be, 0x0000 }, + { 0xd4bf, 0x0000 }, + { 0xd4c0, 0x0000 }, + { 0xd4c1, 0x0000 }, + { 0xd4c2, 0x0000 }, + { 0xd4c3, 0x0000 }, + { 0xd4c4, 0x0000 }, + { 0xd4c5, 0x0000 }, + { 0xd4c6, 0x0000 }, + { 0xd4c7, 0x0000 }, + { 0xd4c8, 0x0000 }, + { 0xd4c9, 0x0000 }, + { 0xd4ca, 0x0000 }, + { 0xd4cb, 0x0000 }, + { 0xd4cc, 0x0000 }, + { 0xd4cd, 0x0000 }, + { 0xd4ce, 0x0000 }, + { 0xd4cf, 0x0000 }, + { 0xd4d0, 0x0000 }, + { 0xd4d1, 0x0000 }, + { 0xd4d2, 0x0000 }, + { 0xd4d3, 0x0000 }, + { 0xd4d4, 0x0000 }, + { 0xd4d5, 0x0000 }, + { 0xd4d6, 0x0000 }, + { 0xd4d7, 0x0000 }, + { 0xd4d8, 0x0000 }, + { 0xd4d9, 0x0000 }, + { 0xd4da, 0x0000 }, + { 0xd4db, 0x0000 }, + { 0xd4dc, 0x0000 }, + { 0xd4dd, 0x0000 }, + { 0xd4de, 0x0000 }, + { 0xd4df, 0x0000 }, + { 0xd4e0, 0x0000 }, + { 0xd4e1, 0x0000 }, + { 0xd4e2, 0x0000 }, + { 0xd4e3, 0x0000 }, + { 0xd4e4, 0x0000 }, + { 0xd4e5, 0x0000 }, + { 0xd4e6, 0x0000 }, + { 0xd4e7, 0x0000 }, + { 0xd4e8, 0x0000 }, + { 0xd4e9, 0x0000 }, + { 0xd4ea, 0x0000 }, + { 0xd4eb, 0x0000 }, + { 0xd4ec, 0x0000 }, + { 0xd4ed, 0x0000 }, + { 0xd4ee, 0x0000 }, + { 0xd4ef, 0x0000 }, + { 0xd4f0, 0x0000 }, + { 0xd4f1, 0x0000 }, + { 0xd4f2, 0x0000 }, + { 0xd4f3, 0x0000 }, + { 0xd4f4, 0x0000 }, + { 0xd4f5, 0x0000 }, + { 0xd4f6, 0x0000 }, + { 0xd4f7, 0x0000 }, + { 0xd4f8, 0x0000 }, + { 0xd4f9, 0x0000 }, + { 0xd4fa, 0x0000 }, + { 0xd4fb, 0x0000 }, + { 0xd4fc, 0x0000 }, + { 0xd4fd, 0x0000 }, + { 0xd4fe, 0x0000 }, + { 0xd4ff, 0x0000 }, + { 0xd500, 0x0000 }, + { 0xd501, 0x0000 }, + { 0xd502, 0x0000 }, + { 0xd503, 0x0000 }, + { 0xd504, 0x0000 }, + { 0xd505, 0x0000 }, + { 0xd506, 0x0000 }, + { 0xd507, 0x0000 }, + { 0xd508, 0x0000 }, + { 0xd509, 0x0000 }, + { 0xd50a, 0x0000 }, + { 0xd50b, 0x0000 }, + { 0xd50c, 0x0000 }, + { 0xd50d, 0x0000 }, + { 0xd50e, 0x0000 }, + { 0xd50f, 0x0000 }, + { 0xd510, 0x0000 }, + { 0xd511, 0x0000 }, + { 0xd512, 0x0000 }, + { 0xd513, 0x0000 }, + { 0xd514, 0x0000 }, + { 0xd515, 0x0000 }, + { 0xd516, 0x0000 }, + { 0xd517, 0x0000 }, + { 0xd518, 0x0000 }, + { 0xd519, 0x0000 }, + { 0xd51a, 0x0000 }, + { 0xd51b, 0x0000 }, + { 0xd51c, 0x0000 }, + { 0xd51d, 0x0000 }, + { 0xd51e, 0x0000 }, + { 0xd51f, 0x0000 }, + { 0xd520, 0x0000 }, + { 0xd521, 0x0000 }, + { 0xd522, 0x0000 }, + { 0xd523, 0x0000 }, + { 0xd524, 0x0000 }, + { 0xd525, 0x0000 }, + { 0xd526, 0x0000 }, + { 0xd527, 0x0000 }, + { 0xd528, 0x0000 }, + { 0xd529, 0x0000 }, + { 0xd52a, 0x0000 }, + { 0xd52b, 0x0000 }, + { 0xd52c, 0x0000 }, + { 0xd52d, 0x0000 }, + { 0xd52e, 0x0000 }, + { 0xd52f, 0x0000 }, + { 0xd530, 0x0000 }, + { 0xd531, 0x0000 }, + { 0xd532, 0x0000 }, + { 0xd533, 0x0000 }, + { 0xd534, 0x0000 }, + { 0xd535, 0x0000 }, + { 0xd536, 0x0000 }, + { 0xd537, 0x0000 }, + { 0xd538, 0x0000 }, + { 0xd539, 0x0000 }, + { 0xd53a, 0x0000 }, + { 0xd53b, 0x0000 }, + { 0xd53c, 0x0000 }, + { 0xd53d, 0x0000 }, + { 0xd53e, 0x0000 }, + { 0xd53f, 0x0000 }, + { 0xd540, 0x0000 }, + { 0xd541, 0x0000 }, + { 0xd542, 0x0000 }, + { 0xd543, 0x0000 }, + { 0xd544, 0x0000 }, + { 0xd545, 0x0000 }, + { 0xd546, 0x0000 }, + { 0xd547, 0x0000 }, + { 0xd548, 0x0000 }, + { 0xd549, 0x0000 }, + { 0xd54a, 0x0000 }, + { 0xd54b, 0x0000 }, + { 0xd54c, 0x0000 }, + { 0xd54d, 0x0000 }, + { 0xd54e, 0x0000 }, + { 0xd54f, 0x0000 }, + { 0xd550, 0x0000 }, + { 0xd551, 0x0000 }, + { 0xd552, 0x0000 }, + { 0xd553, 0x0000 }, + { 0xd554, 0x0000 }, + { 0xd555, 0x0000 }, + { 0xd556, 0x0000 }, + { 0xd557, 0x0000 }, + { 0xd558, 0x0000 }, + { 0xd559, 0x0000 }, + { 0xd55a, 0x0000 }, + { 0xd55b, 0x0000 }, + { 0xd55c, 0x0000 }, + { 0xd55d, 0x0000 }, + { 0xd55e, 0x0000 }, + { 0xd55f, 0x0000 }, + { 0xd560, 0x0000 }, + { 0xd561, 0x0000 }, + { 0xd562, 0x0000 }, + { 0xd563, 0x0000 }, + { 0xd564, 0x0000 }, + { 0xd565, 0x0000 }, + { 0xd566, 0x0000 }, + { 0xd567, 0x0000 }, + { 0xd568, 0x0000 }, + { 0xd569, 0x0000 }, + { 0xd56a, 0x0000 }, + { 0xd56b, 0x0000 }, + { 0xd56c, 0x0000 }, + { 0xd56d, 0x0000 }, + { 0xd56e, 0x0000 }, + { 0xd56f, 0x0000 }, + { 0xd570, 0x0000 }, + { 0xd571, 0x0000 }, + { 0xd572, 0x0000 }, + { 0xd573, 0x0000 }, + { 0xd574, 0x0000 }, + { 0xd575, 0x0000 }, + { 0xd576, 0x0000 }, + { 0xd577, 0x0000 }, + { 0xd578, 0x0000 }, + { 0xd579, 0x0000 }, + { 0xd57a, 0x0000 }, + { 0xd57b, 0x0000 }, + { 0xd57c, 0x0000 }, + { 0xd57d, 0x0000 }, + { 0xd57e, 0x0000 }, + { 0xd57f, 0x0000 }, + { 0xd580, 0x0000 }, + { 0xd581, 0x0000 }, + { 0xd582, 0x0000 }, + { 0xd583, 0x0000 }, + { 0xd584, 0x0000 }, + { 0xd585, 0x0000 }, + { 0xd586, 0x0000 }, + { 0xd587, 0x0000 }, + { 0xd588, 0x0000 }, + { 0xd589, 0x0000 }, + { 0xd58a, 0x0000 }, + { 0xd58b, 0x0000 }, + { 0xd58c, 0x0000 }, + { 0xd58d, 0x0000 }, + { 0xd58e, 0x0000 }, + { 0xd58f, 0x0000 }, + { 0xd590, 0x0000 }, + { 0xd591, 0x0000 }, + { 0xd592, 0x0000 }, + { 0xd593, 0x0000 }, + { 0xd594, 0x0000 }, + { 0xd595, 0x0000 }, + { 0xd596, 0x0000 }, + { 0xd597, 0x0000 }, + { 0xd598, 0x0000 }, + { 0xd599, 0x0000 }, + { 0xd59a, 0x0000 }, + { 0xd59b, 0x0000 }, + { 0xd59c, 0x0000 }, + { 0xd59d, 0x0000 }, + { 0xd59e, 0x0000 }, + { 0xd59f, 0x0000 }, + { 0xd5a0, 0x0000 }, + { 0xd5a1, 0x0000 }, + { 0xd5a2, 0x0000 }, + { 0xd5a3, 0x0000 }, + { 0xd5a4, 0x0000 }, + { 0xd5a5, 0x0000 }, + { 0xd5a6, 0x0000 }, + { 0xd5a7, 0x0000 }, + { 0xd5a8, 0x0000 }, + { 0xd5a9, 0x0000 }, + { 0xd5aa, 0x0000 }, + { 0xd5ab, 0x0000 }, + { 0xd5ac, 0x0000 }, + { 0xd5ad, 0x0000 }, + { 0xd5ae, 0x0000 }, + { 0xd5af, 0x0000 }, + { 0xd5b0, 0x0000 }, + { 0xd5b1, 0x0000 }, + { 0xd5b2, 0x0000 }, + { 0xd5b3, 0x0000 }, + { 0xd5b4, 0x0000 }, + { 0xd5b5, 0x0000 }, + { 0xd5b6, 0x0000 }, + { 0xd5b7, 0x0000 }, + { 0xd5b8, 0x0000 }, + { 0xd5b9, 0x0000 }, + { 0xd5ba, 0x0000 }, + { 0xd5bb, 0x0000 }, + { 0xd5bc, 0x0000 }, + { 0xd5bd, 0x0000 }, + { 0xd5be, 0x0000 }, + { 0xd5bf, 0x0000 }, + { 0xd5c0, 0x0000 }, + { 0xd5c1, 0x0000 }, + { 0xd5c2, 0x0000 }, + { 0xd5c3, 0x0000 }, + { 0xd5c4, 0x0000 }, + { 0xd5c5, 0x0000 }, + { 0xd5c6, 0x0000 }, + { 0xd5c7, 0x0000 }, + { 0xd5c8, 0x0000 }, + { 0xd5c9, 0x0000 }, + { 0xd5ca, 0x0000 }, + { 0xd5cb, 0x0000 }, + { 0xd5cc, 0x0000 }, + { 0xd5cd, 0x0000 }, + { 0xd5ce, 0x0000 }, + { 0xd5cf, 0x0000 }, + { 0xd5d0, 0x0000 }, + { 0xd5d1, 0x0000 }, + { 0xd5d2, 0x0000 }, + { 0xd5d3, 0x0000 }, + { 0xd5d4, 0x0000 }, + { 0xd5d5, 0x0000 }, + { 0xd5d6, 0x0000 }, + { 0xd5d7, 0x0000 }, + { 0xd5d8, 0x0000 }, + { 0xd5d9, 0x0000 }, + { 0xd5da, 0x0000 }, + { 0xd5db, 0x0000 }, + { 0xd5dc, 0x0000 }, + { 0xd5dd, 0x0000 }, + { 0xd5de, 0x0000 }, + { 0xd5df, 0x0000 }, + { 0xd5e0, 0x0000 }, + { 0xd5e1, 0x0000 }, + { 0xd5e2, 0x0000 }, + { 0xd5e3, 0x0000 }, + { 0xd5e4, 0x0000 }, + { 0xd5e5, 0x0000 }, + { 0xd5e6, 0x0000 }, + { 0xd5e7, 0x0000 }, + { 0xd5e8, 0x0000 }, + { 0xd5e9, 0x0000 }, + { 0xd5ea, 0x0000 }, + { 0xd5eb, 0x0000 }, + { 0xd5ec, 0x0000 }, + { 0xd5ed, 0x0000 }, + { 0xd5ee, 0x0000 }, + { 0xd5ef, 0x0000 }, + { 0xd5f0, 0x0000 }, + { 0xd5f1, 0x0000 }, + { 0xd5f2, 0x0000 }, + { 0xd5f3, 0x0000 }, + { 0xd5f4, 0x0000 }, + { 0xd5f5, 0x0000 }, + { 0xd5f6, 0x0000 }, + { 0xd5f7, 0x0000 }, + { 0xd5f8, 0x0000 }, + { 0xd5f9, 0x0000 }, + { 0xd5fa, 0x0000 }, + { 0xd5fb, 0x0000 }, + { 0xd5fc, 0x0000 }, + { 0xd5fd, 0x0000 }, + { 0xd5fe, 0x0000 }, + { 0xd5ff, 0x0000 }, + { 0xd600, 0x0000 }, + { 0xd601, 0x0000 }, + { 0xd602, 0x0000 }, + { 0xd603, 0x0000 }, + { 0xd604, 0x0000 }, + { 0xd605, 0x0000 }, + { 0xd606, 0x0000 }, + { 0xd607, 0x0000 }, + { 0xd608, 0x0000 }, + { 0xd609, 0x0000 }, + { 0xd60a, 0x0000 }, + { 0xd60b, 0x0000 }, + { 0xd60c, 0x0000 }, + { 0xd60d, 0x0000 }, + { 0xd60e, 0x0000 }, + { 0xd60f, 0x0000 }, + { 0xd610, 0x0000 }, + { 0xd611, 0x0000 }, + { 0xd612, 0x0000 }, + { 0xd613, 0x0000 }, + { 0xd614, 0x0000 }, + { 0xd615, 0x0000 }, + { 0xd616, 0x0000 }, + { 0xd617, 0x0000 }, + { 0xd618, 0x0000 }, + { 0xd619, 0x0000 }, + { 0xd61a, 0x0000 }, + { 0xd61b, 0x0000 }, + { 0xd61c, 0x0000 }, + { 0xd61d, 0x0000 }, + { 0xd61e, 0x0000 }, + { 0xd61f, 0x0000 }, + { 0xd620, 0x0000 }, + { 0xd621, 0x0000 }, + { 0xd622, 0x0000 }, + { 0xd623, 0x0000 }, + { 0xd624, 0x0000 }, + { 0xd625, 0x0000 }, + { 0xd626, 0x0000 }, + { 0xd627, 0x0000 }, + { 0xd628, 0x0000 }, + { 0xd629, 0x0000 }, + { 0xd62a, 0x0000 }, + { 0xd62b, 0x0000 }, + { 0xd62c, 0x0000 }, + { 0xd62d, 0x0000 }, + { 0xd62e, 0x0000 }, + { 0xd62f, 0x0000 }, + { 0xd630, 0x0000 }, + { 0xd631, 0x0000 }, + { 0xd632, 0x0000 }, + { 0xd633, 0x0000 }, + { 0xd634, 0x0000 }, + { 0xd635, 0x0000 }, + { 0xd636, 0x0000 }, + { 0xd637, 0x0000 }, + { 0xd638, 0x0000 }, + { 0xd639, 0x0000 }, + { 0xd63a, 0x0000 }, + { 0xd63b, 0x0000 }, + { 0xd63c, 0x0000 }, + { 0xd63d, 0x0000 }, + { 0xd63e, 0x0000 }, + { 0xd63f, 0x0000 }, + { 0xd640, 0x0000 }, + { 0xd641, 0x0000 }, + { 0xd642, 0x0000 }, + { 0xd643, 0x0000 }, + { 0xd644, 0x0000 }, + { 0xd645, 0x0000 }, + { 0xd646, 0x0000 }, + { 0xd647, 0x0000 }, + { 0xd648, 0x0000 }, + { 0xd649, 0x0000 }, + { 0xd64a, 0x0000 }, + { 0xd64b, 0x0000 }, + { 0xd64c, 0x0000 }, + { 0xd64d, 0x0000 }, + { 0xd64e, 0x0000 }, + { 0xd64f, 0x0000 }, + { 0xd650, 0x0000 }, + { 0xd651, 0x0000 }, + { 0xd652, 0x0000 }, + { 0xd653, 0x0000 }, + { 0xd654, 0x0000 }, + { 0xd655, 0x0000 }, + { 0xd656, 0x0000 }, + { 0xd657, 0x0000 }, + { 0xd658, 0x0000 }, + { 0xd659, 0x0000 }, + { 0xd65a, 0x0000 }, + { 0xd65b, 0x0000 }, + { 0xd65c, 0x0000 }, + { 0xd65d, 0x0000 }, + { 0xd65e, 0x0000 }, + { 0xd65f, 0x0000 }, + { 0xd660, 0x0000 }, + { 0xd661, 0x0000 }, + { 0xd662, 0x0000 }, + { 0xd663, 0x0000 }, + { 0xd664, 0x0000 }, + { 0xd665, 0x0000 }, + { 0xd666, 0x0000 }, + { 0xd667, 0x0000 }, + { 0xd668, 0x0000 }, + { 0xd669, 0x0000 }, + { 0xd66a, 0x0000 }, + { 0xd66b, 0x0000 }, + { 0xd66c, 0x0000 }, + { 0xd66d, 0x0000 }, + { 0xd66e, 0x0000 }, + { 0xd66f, 0x0000 }, + { 0xd670, 0x0000 }, + { 0xd671, 0x0000 }, + { 0xd672, 0x0000 }, + { 0xd673, 0x0000 }, + { 0xd674, 0x0000 }, + { 0xd675, 0x0000 }, + { 0xd676, 0x0000 }, + { 0xd677, 0x0000 }, + { 0xd678, 0x0000 }, + { 0xd679, 0x0000 }, + { 0xd67a, 0x0000 }, + { 0xd67b, 0x0000 }, + { 0xd67c, 0x0000 }, + { 0xd67d, 0x0000 }, + { 0xd67e, 0x0000 }, + { 0xd67f, 0x0000 }, + { 0xd680, 0x0000 }, + { 0xd681, 0x0000 }, + { 0xd682, 0x0000 }, + { 0xd683, 0x0000 }, + { 0xd684, 0x0000 }, + { 0xd685, 0x0000 }, + { 0xd686, 0x0000 }, + { 0xd687, 0x0000 }, + { 0xd688, 0x0000 }, + { 0xd689, 0x0000 }, + { 0xd68a, 0x0000 }, + { 0xd68b, 0x0000 }, + { 0xd68c, 0x0000 }, + { 0xd68d, 0x0000 }, + { 0xd68e, 0x0000 }, + { 0xd68f, 0x0000 }, + { 0xd690, 0x0000 }, + { 0xd691, 0x0000 }, + { 0xd692, 0x0000 }, + { 0xd693, 0x0000 }, + { 0xd694, 0x0000 }, + { 0xd695, 0x0000 }, + { 0xd696, 0x0000 }, + { 0xd697, 0x0000 }, + { 0xd698, 0x0000 }, + { 0xd699, 0x0000 }, + { 0xd69a, 0x0000 }, + { 0xd69b, 0x0000 }, + { 0xd69c, 0x0000 }, + { 0xd69d, 0x0000 }, + { 0xd69e, 0x0000 }, + { 0xd69f, 0x0000 }, + { 0xd6a0, 0x0000 }, + { 0xd6a1, 0x0000 }, + { 0xd6a2, 0x0000 }, + { 0xd6a3, 0x0000 }, + { 0xd6a4, 0x0000 }, + { 0xd6a5, 0x0000 }, + { 0xd6a6, 0x0000 }, + { 0xd6a7, 0x0000 }, + { 0xd6a8, 0x0000 }, + { 0xd6a9, 0x0000 }, + { 0xd6aa, 0x0000 }, + { 0xd6ab, 0x0000 }, + { 0xd6ac, 0x0000 }, + { 0xd6ad, 0x0000 }, + { 0xd6ae, 0x0000 }, + { 0xd6af, 0x0000 }, + { 0xd6b0, 0x0000 }, + { 0xd6b1, 0x0000 }, + { 0xd6b2, 0x0000 }, + { 0xd6b3, 0x0000 }, + { 0xd6b4, 0x0000 }, + { 0xd6b5, 0x0000 }, + { 0xd6b6, 0x0000 }, + { 0xd6b7, 0x0000 }, + { 0xd6b8, 0x0000 }, + { 0xd6b9, 0x0000 }, + { 0xd6ba, 0x0000 }, + { 0xd6bb, 0x0000 }, + { 0xd6bc, 0x0000 }, + { 0xd6bd, 0x0000 }, + { 0xd6be, 0x0000 }, + { 0xd6bf, 0x0000 }, + { 0xd6c0, 0x0000 }, + { 0xd6c1, 0x0000 }, + { 0xd6c2, 0x0000 }, + { 0xd6c3, 0x0000 }, + { 0xd6c4, 0x0000 }, + { 0xd6c5, 0x0000 }, + { 0xd6c6, 0x0000 }, + { 0xd6c7, 0x0000 }, + { 0xd6c8, 0x0000 }, + { 0xd6c9, 0x0000 }, + { 0xd6ca, 0x0000 }, + { 0xd6cb, 0x0000 }, + { 0xd6cc, 0x0000 }, + { 0xd6cd, 0x0000 }, + { 0xd6ce, 0x0000 }, + { 0xd6cf, 0x0000 }, + { 0xd6d0, 0x0000 }, + { 0xd6d1, 0x0000 }, + { 0xd6d2, 0x0000 }, + { 0xd6d3, 0x0000 }, + { 0xd6d4, 0x0000 }, + { 0xd6d5, 0x0000 }, + { 0xd6d6, 0x0000 }, + { 0xd6d7, 0x0000 }, + { 0xd6d8, 0x0000 }, + { 0xd6d9, 0x0000 }, + { 0xd6da, 0x0000 }, + { 0xd6db, 0x0000 }, + { 0xd6dc, 0x0000 }, + { 0xd6dd, 0x0000 }, + { 0xd6de, 0x0000 }, + { 0xd6df, 0x0000 }, + { 0xd6e0, 0x0000 }, + { 0xd6e1, 0x0000 }, + { 0xd6e2, 0x0000 }, + { 0xd6e3, 0x0000 }, + { 0xd6e4, 0x0000 }, + { 0xd6e5, 0x0000 }, + { 0xd6e6, 0x0000 }, + { 0xd6e7, 0x0000 }, + { 0xd6e8, 0x0000 }, + { 0xd6e9, 0x0000 }, + { 0xd6ea, 0x0000 }, + { 0xd6eb, 0x0000 }, + { 0xd6ec, 0x0000 }, + { 0xd6ed, 0x0000 }, + { 0xd6ee, 0x0000 }, + { 0xd6ef, 0x0000 }, + { 0xd6f0, 0x0000 }, + { 0xd6f1, 0x0000 }, + { 0xd6f2, 0x0000 }, + { 0xd6f3, 0x0000 }, + { 0xd6f4, 0x0000 }, + { 0xd6f5, 0x0000 }, + { 0xd6f6, 0x0000 }, + { 0xd6f7, 0x0000 }, + { 0xd6f8, 0x0000 }, + { 0xd6f9, 0x0000 }, + { 0xd6fa, 0x0000 }, + { 0xd6fb, 0x0000 }, + { 0xd6fc, 0x0000 }, + { 0xd6fd, 0x0000 }, + { 0xd6fe, 0x0000 }, + { 0xd6ff, 0x0000 }, + { 0xd700, 0x0000 }, + { 0xd701, 0x0000 }, + { 0xd702, 0x0000 }, + { 0xd703, 0x0000 }, + { 0xd704, 0x0000 }, + { 0xd705, 0x0000 }, + { 0xd706, 0x0000 }, + { 0xd707, 0x0000 }, + { 0xd708, 0x0000 }, + { 0xd709, 0x0000 }, + { 0xd70a, 0x0000 }, + { 0xd70b, 0x0000 }, + { 0xd70c, 0x0000 }, + { 0xd70d, 0x0000 }, + { 0xd70e, 0x0000 }, + { 0xd70f, 0x0000 }, + { 0xd710, 0x0000 }, + { 0xd711, 0x0000 }, + { 0xd712, 0x0000 }, + { 0xd713, 0x0000 }, + { 0xd714, 0x0000 }, + { 0xd715, 0x0000 }, + { 0xd716, 0x0000 }, + { 0xd717, 0x0000 }, + { 0xd718, 0x0000 }, + { 0xd719, 0x0000 }, + { 0xd71a, 0x0000 }, + { 0xd71b, 0x0000 }, + { 0xd71c, 0x0000 }, + { 0xd71d, 0x0000 }, + { 0xd71e, 0x0000 }, + { 0xd71f, 0x0000 }, + { 0xd720, 0x0000 }, + { 0xd721, 0x0000 }, + { 0xd722, 0x0000 }, + { 0xd723, 0x0000 }, + { 0xd724, 0x0000 }, + { 0xd725, 0x0000 }, + { 0xd726, 0x0000 }, + { 0xd727, 0x0000 }, + { 0xd728, 0x0000 }, + { 0xd729, 0x0000 }, + { 0xd72a, 0x0000 }, + { 0xd72b, 0x0000 }, + { 0xd72c, 0x0000 }, + { 0xd72d, 0x0000 }, + { 0xd72e, 0x0000 }, + { 0xd72f, 0x0000 }, + { 0xd730, 0x0000 }, + { 0xd731, 0x0000 }, + { 0xd732, 0x0000 }, + { 0xd733, 0x0000 }, + { 0xd734, 0x0000 }, + { 0xd735, 0x0000 }, + { 0xd736, 0x0000 }, + { 0xd737, 0x0000 }, + { 0xd738, 0x0000 }, + { 0xd739, 0x0000 }, + { 0xd73a, 0x0000 }, + { 0xd73b, 0x0000 }, + { 0xd73c, 0x0000 }, + { 0xd73d, 0x0000 }, + { 0xd73e, 0x0000 }, + { 0xd73f, 0x0000 }, + { 0xd740, 0x0000 }, + { 0xd741, 0x0000 }, + { 0xd742, 0x0000 }, + { 0xd743, 0x0000 }, + { 0xd744, 0x0000 }, + { 0xd745, 0x0000 }, + { 0xd746, 0x0000 }, + { 0xd747, 0x0000 }, + { 0xd748, 0x0000 }, + { 0xd749, 0x0000 }, + { 0xd74a, 0x0000 }, + { 0xd74b, 0x0000 }, + { 0xd74c, 0x0000 }, + { 0xd74d, 0x0000 }, + { 0xd74e, 0x0000 }, + { 0xd74f, 0x0000 }, + { 0xd750, 0x0000 }, + { 0xd751, 0x0000 }, + { 0xd752, 0x0000 }, + { 0xd753, 0x0000 }, + { 0xd754, 0x0000 }, + { 0xd755, 0x0000 }, + { 0xd756, 0x0000 }, + { 0xd757, 0x0000 }, + { 0xd758, 0x0000 }, + { 0xd759, 0x0000 }, + { 0xd75a, 0x0000 }, + { 0xd75b, 0x0000 }, + { 0xd75c, 0x0000 }, + { 0xd75d, 0x0000 }, + { 0xd75e, 0x0000 }, + { 0xd75f, 0x0000 }, + { 0xd760, 0x0000 }, + { 0xd761, 0x0000 }, + { 0xd762, 0x0000 }, + { 0xd763, 0x0000 }, + { 0xd764, 0x0000 }, + { 0xd765, 0x0000 }, + { 0xd766, 0x0000 }, + { 0xd767, 0x0000 }, + { 0xd768, 0x0000 }, + { 0xd769, 0x0000 }, + { 0xd76a, 0x0000 }, + { 0xd76b, 0x0000 }, + { 0xd76c, 0x0000 }, + { 0xd76d, 0x0000 }, + { 0xd76e, 0x0000 }, + { 0xd76f, 0x0000 }, + { 0xd770, 0x0000 }, + { 0xd771, 0x0000 }, + { 0xd772, 0x0000 }, + { 0xd773, 0x0000 }, + { 0xd774, 0x0000 }, + { 0xd775, 0x0000 }, + { 0xd776, 0x0000 }, + { 0xd777, 0x0000 }, + { 0xd778, 0x0000 }, + { 0xd779, 0x0000 }, + { 0xd77a, 0x0000 }, + { 0xd77b, 0x0000 }, + { 0xd77c, 0x0000 }, + { 0xd77d, 0x0000 }, + { 0xd77e, 0x0000 }, + { 0xd77f, 0x0000 }, + { 0xd780, 0x0000 }, + { 0xd781, 0x0000 }, + { 0xd782, 0x0000 }, + { 0xd783, 0x0000 }, + { 0xd784, 0x0000 }, + { 0xd785, 0x0000 }, + { 0xd786, 0x0000 }, + { 0xd787, 0x0000 }, + { 0xd788, 0x0000 }, + { 0xd789, 0x0000 }, + { 0xd78a, 0x0000 }, + { 0xd78b, 0x0000 }, + { 0xd78c, 0x0000 }, + { 0xd78d, 0x0000 }, + { 0xd78e, 0x0000 }, + { 0xd78f, 0x0000 }, + { 0xd790, 0x0000 }, + { 0xd791, 0x0000 }, + { 0xd792, 0x0000 }, + { 0xd793, 0x0000 }, + { 0xd794, 0x0000 }, + { 0xd795, 0x0000 }, + { 0xd796, 0x0000 }, + { 0xd797, 0x0000 }, + { 0xd798, 0x0000 }, + { 0xd799, 0x0000 }, + { 0xd79a, 0x0000 }, + { 0xd79b, 0x0000 }, + { 0xd79c, 0x0000 }, + { 0xd79d, 0x0000 }, + { 0xd79e, 0x0000 }, + { 0xd79f, 0x0000 }, + { 0xd7a0, 0x0000 }, + { 0xd7a1, 0x0000 }, + { 0xd7a2, 0x0000 }, + { 0xd7a3, 0x0000 }, + { 0xd7a4, 0x0000 }, + { 0xd7a5, 0x0000 }, + { 0xd7a6, 0x0000 }, + { 0xd7a7, 0x0000 }, + { 0xd7a8, 0x0000 }, + { 0xd7a9, 0x0000 }, + { 0xd7aa, 0x0000 }, + { 0xd7ab, 0x0000 }, + { 0xd7ac, 0x0000 }, + { 0xd7ad, 0x0000 }, + { 0xd7ae, 0x0000 }, + { 0xd7af, 0x0000 }, + { 0xd7b0, 0x0000 }, + { 0xd7b1, 0x0000 }, + { 0xd7b2, 0x0000 }, + { 0xd7b3, 0x0000 }, + { 0xd7b4, 0x0000 }, + { 0xd7b5, 0x0000 }, + { 0xd7b6, 0x0000 }, + { 0xd7b7, 0x0000 }, + { 0xd7b8, 0x0000 }, + { 0xd7b9, 0x0000 }, + { 0xd7ba, 0x0000 }, + { 0xd7bb, 0x0000 }, + { 0xd7bc, 0x0000 }, + { 0xd7bd, 0x0000 }, + { 0xd7be, 0x0000 }, + { 0xd7bf, 0x0000 }, + { 0xd7c0, 0x0000 }, + { 0xd7c1, 0x0000 }, + { 0xd7c2, 0x0000 }, + { 0xd7c3, 0x0000 }, + { 0xd7c4, 0x0000 }, + { 0xd7c5, 0x0000 }, + { 0xd7c6, 0x0000 }, + { 0xd7c7, 0x0000 }, + { 0xd7c8, 0x0000 }, + { 0xd7c9, 0x0000 }, + { 0xd7ca, 0x0000 }, + { 0xd7cb, 0x0000 }, + { 0xd7cc, 0x0000 }, + { 0xd7cd, 0x0000 }, + { 0xd7ce, 0x0000 }, + { 0xd7cf, 0x0000 }, + { 0xd7d0, 0x0000 }, + { 0xd7d1, 0x0000 }, + { 0xd7d2, 0x0000 }, + { 0xd7d3, 0x0000 }, + { 0xd7d4, 0x0000 }, + { 0xd7d5, 0x0000 }, + { 0xd7d6, 0x0000 }, + { 0xd7d7, 0x0000 }, + { 0xd7d8, 0x0000 }, + { 0xd7d9, 0x0000 }, + { 0xd7da, 0x0000 }, + { 0xd7db, 0x0000 }, + { 0xd7dc, 0x0000 }, + { 0xd7dd, 0x0000 }, + { 0xd7de, 0x0000 }, + { 0xd7df, 0x0000 }, + { 0xd7e0, 0x0000 }, + { 0xd7e1, 0x0000 }, + { 0xd7e2, 0x0000 }, + { 0xd7e3, 0x0000 }, + { 0xd7e4, 0x0000 }, + { 0xd7e5, 0x0000 }, + { 0xd7e6, 0x0000 }, + { 0xd7e7, 0x0000 }, + { 0xd7e8, 0x0000 }, + { 0xd7e9, 0x0000 }, + { 0xd7ea, 0x0000 }, + { 0xd7eb, 0x0000 }, + { 0xd7ec, 0x0000 }, + { 0xd7ed, 0x0000 }, + { 0xd7ee, 0x0000 }, + { 0xd7ef, 0x0000 }, + { 0xd7f0, 0x0000 }, + { 0xd7f1, 0x0000 }, + { 0xd7f2, 0x0000 }, + { 0xd7f3, 0x0000 }, + { 0xd7f4, 0x0000 }, + { 0xd7f5, 0x0000 }, + { 0xd7f6, 0x0000 }, + { 0xd7f7, 0x0000 }, + { 0xd7f8, 0x0000 }, + { 0xd7f9, 0x0000 }, + { 0xd7fa, 0x0000 }, + { 0xd7fb, 0x0000 }, + { 0xd7fc, 0x0000 }, + { 0xd7fd, 0x0000 }, + { 0xd7fe, 0x0000 }, + { 0xd7ff, 0x0000 }, + { 0xd800, 0x0000 }, + { 0xd801, 0x0000 }, + { 0xd802, 0x0000 }, + { 0xd803, 0x0000 }, + { 0xd804, 0x0000 }, + { 0xd805, 0x0000 }, + { 0xd806, 0x0000 }, + { 0xd807, 0x0000 }, + { 0xd808, 0x0000 }, + { 0xd809, 0x0000 }, + { 0xd80a, 0x0000 }, + { 0xd80b, 0x0000 }, + { 0xd80c, 0x0000 }, + { 0xd80d, 0x0000 }, + { 0xd80e, 0x0000 }, + { 0xd80f, 0x0000 }, + { 0xd810, 0x0000 }, + { 0xd811, 0x0000 }, + { 0xd812, 0x0000 }, + { 0xd813, 0x0000 }, + { 0xd814, 0x0000 }, + { 0xd815, 0x0000 }, + { 0xd816, 0x0000 }, + { 0xd817, 0x0000 }, + { 0xd818, 0x0000 }, + { 0xd819, 0x0000 }, + { 0xd81a, 0x0000 }, + { 0xd81b, 0x0000 }, + { 0xd81c, 0x0000 }, + { 0xd81d, 0x0000 }, + { 0xd81e, 0x0000 }, + { 0xd81f, 0x0000 }, + { 0xd820, 0x0000 }, + { 0xd821, 0x0000 }, + { 0xd822, 0x0000 }, + { 0xd823, 0x0000 }, + { 0xd824, 0x0000 }, + { 0xd825, 0x0000 }, + { 0xd826, 0x0000 }, + { 0xd827, 0x0000 }, + { 0xd828, 0x0000 }, + { 0xd829, 0x0000 }, + { 0xd82a, 0x0000 }, + { 0xd82b, 0x0000 }, + { 0xd82c, 0x0000 }, + { 0xd82d, 0x0000 }, + { 0xd82e, 0x0000 }, + { 0xd82f, 0x0000 }, + { 0xd830, 0x0000 }, + { 0xd831, 0x0000 }, + { 0xd832, 0x0000 }, + { 0xd833, 0x0000 }, + { 0xd834, 0x0000 }, + { 0xd835, 0x0000 }, + { 0xd836, 0x0000 }, + { 0xd837, 0x0000 }, + { 0xd838, 0x0000 }, + { 0xd839, 0x0000 }, + { 0xd83a, 0x0000 }, + { 0xd83b, 0x0000 }, + { 0xd83c, 0x0000 }, + { 0xd83d, 0x0000 }, + { 0xd83e, 0x0000 }, + { 0xd83f, 0x0000 }, + { 0xd840, 0x0000 }, + { 0xd841, 0x0000 }, + { 0xd842, 0x0000 }, + { 0xd843, 0x0000 }, + { 0xd844, 0x0000 }, + { 0xd845, 0x0000 }, + { 0xd846, 0x0000 }, + { 0xd847, 0x0000 }, + { 0xd848, 0x0000 }, + { 0xd849, 0x0000 }, + { 0xd84a, 0x0000 }, + { 0xd84b, 0x0000 }, + { 0xd84c, 0x0000 }, + { 0xd84d, 0x0000 }, + { 0xd84e, 0x0000 }, + { 0xd84f, 0x0000 }, + { 0xd850, 0x0000 }, + { 0xd851, 0x0000 }, + { 0xd852, 0x0000 }, + { 0xd853, 0x0000 }, + { 0xd854, 0x0000 }, + { 0xd855, 0x0000 }, + { 0xd856, 0x0000 }, + { 0xd857, 0x0000 }, + { 0xd858, 0x0000 }, + { 0xd859, 0x0000 }, + { 0xd85a, 0x0000 }, + { 0xd85b, 0x0000 }, + { 0xd85c, 0x0000 }, + { 0xd85d, 0x0000 }, + { 0xd85e, 0x0000 }, + { 0xd85f, 0x0000 }, + { 0xd860, 0x0000 }, + { 0xd861, 0x0000 }, + { 0xd862, 0x0000 }, + { 0xd863, 0x0000 }, + { 0xd864, 0x0000 }, + { 0xd865, 0x0000 }, + { 0xd866, 0x0000 }, + { 0xd867, 0x0000 }, + { 0xd868, 0x0000 }, + { 0xd869, 0x0000 }, + { 0xd86a, 0x0000 }, + { 0xd86b, 0x0000 }, + { 0xd86c, 0x0000 }, + { 0xd86d, 0x0000 }, + { 0xd86e, 0x0000 }, + { 0xd86f, 0x0000 }, + { 0xd870, 0x0000 }, + { 0xd871, 0x0000 }, + { 0xd872, 0x0000 }, + { 0xd873, 0x0000 }, + { 0xd874, 0x0000 }, + { 0xd875, 0x0000 }, + { 0xd876, 0x0000 }, + { 0xd877, 0x0000 }, + { 0xd878, 0x0000 }, + { 0xd879, 0x0000 }, + { 0xd87a, 0x0000 }, + { 0xd87b, 0x0000 }, + { 0xd87c, 0x0000 }, + { 0xd87d, 0x0000 }, + { 0xd87e, 0x0000 }, + { 0xd87f, 0x0000 }, + { 0xd880, 0x0000 }, + { 0xd881, 0x0000 }, + { 0xd882, 0x0000 }, + { 0xd883, 0x0000 }, + { 0xd884, 0x0000 }, + { 0xd885, 0x0000 }, + { 0xd886, 0x0000 }, + { 0xd887, 0x0000 }, + { 0xd888, 0x0000 }, + { 0xd889, 0x0000 }, + { 0xd88a, 0x0000 }, + { 0xd88b, 0x0000 }, + { 0xd88c, 0x0000 }, + { 0xd88d, 0x0000 }, + { 0xd88e, 0x0000 }, + { 0xd88f, 0x0000 }, + { 0xd890, 0x0000 }, + { 0xd891, 0x0000 }, + { 0xd892, 0x0000 }, + { 0xd893, 0x0000 }, + { 0xd894, 0x0000 }, + { 0xd895, 0x0000 }, + { 0xd896, 0x0000 }, + { 0xd897, 0x0000 }, + { 0xd898, 0x0000 }, + { 0xd899, 0x0000 }, + { 0xd89a, 0x0000 }, + { 0xd89b, 0x0000 }, + { 0xd89c, 0x0000 }, + { 0xd89d, 0x0000 }, + { 0xd89e, 0x0000 }, + { 0xd89f, 0x0000 }, + { 0xd8a0, 0x0000 }, + { 0xd8a1, 0x0000 }, + { 0xd8a2, 0x0000 }, + { 0xd8a3, 0x0000 }, + { 0xd8a4, 0x0000 }, + { 0xd8a5, 0x0000 }, + { 0xd8a6, 0x0000 }, + { 0xd8a7, 0x0000 }, + { 0xd8a8, 0x0000 }, + { 0xd8a9, 0x0000 }, + { 0xd8aa, 0x0000 }, + { 0xd8ab, 0x0000 }, + { 0xd8ac, 0x0000 }, + { 0xd8ad, 0x0000 }, + { 0xd8ae, 0x0000 }, + { 0xd8af, 0x0000 }, + { 0xd8b0, 0x0000 }, + { 0xd8b1, 0x0000 }, + { 0xd8b2, 0x0000 }, + { 0xd8b3, 0x0000 }, + { 0xd8b4, 0x0000 }, + { 0xd8b5, 0x0000 }, + { 0xd8b6, 0x0000 }, + { 0xd8b7, 0x0000 }, + { 0xd8b8, 0x0000 }, + { 0xd8b9, 0x0000 }, + { 0xd8ba, 0x0000 }, + { 0xd8bb, 0x0000 }, + { 0xd8bc, 0x0000 }, + { 0xd8bd, 0x0000 }, + { 0xd8be, 0x0000 }, + { 0xd8bf, 0x0000 }, + { 0xd8c0, 0x0000 }, + { 0xd8c1, 0x0000 }, + { 0xd8c2, 0x0000 }, + { 0xd8c3, 0x0000 }, + { 0xd8c4, 0x0000 }, + { 0xd8c5, 0x0000 }, + { 0xd8c6, 0x0000 }, + { 0xd8c7, 0x0000 }, + { 0xd8c8, 0x0000 }, + { 0xd8c9, 0x0000 }, + { 0xd8ca, 0x0000 }, + { 0xd8cb, 0x0000 }, + { 0xd8cc, 0x0000 }, + { 0xd8cd, 0x0000 }, + { 0xd8ce, 0x0000 }, + { 0xd8cf, 0x0000 }, + { 0xd8d0, 0x0000 }, + { 0xd8d1, 0x0000 }, + { 0xd8d2, 0x0000 }, + { 0xd8d3, 0x0000 }, + { 0xd8d4, 0x0000 }, + { 0xd8d5, 0x0000 }, + { 0xd8d6, 0x0000 }, + { 0xd8d7, 0x0000 }, + { 0xd8d8, 0x0000 }, + { 0xd8d9, 0x0000 }, + { 0xd8da, 0x0000 }, + { 0xd8db, 0x0000 }, + { 0xd8dc, 0x0000 }, + { 0xd8dd, 0x0000 }, + { 0xd8de, 0x0000 }, + { 0xd8df, 0x0000 }, + { 0xd8e0, 0x0000 }, + { 0xd8e1, 0x0000 }, + { 0xd8e2, 0x0000 }, + { 0xd8e3, 0x0000 }, + { 0xd8e4, 0x0000 }, + { 0xd8e5, 0x0000 }, + { 0xd8e6, 0x0000 }, + { 0xd8e7, 0x0000 }, + { 0xd8e8, 0x0000 }, + { 0xd8e9, 0x0000 }, + { 0xd8ea, 0x0000 }, + { 0xd8eb, 0x0000 }, + { 0xd8ec, 0x0000 }, + { 0xd8ed, 0x0000 }, + { 0xd8ee, 0x0000 }, + { 0xd8ef, 0x0000 }, + { 0xd8f0, 0x0000 }, + { 0xd8f1, 0x0000 }, + { 0xd8f2, 0x0000 }, + { 0xd8f3, 0x0000 }, + { 0xd8f4, 0x0000 }, + { 0xd8f5, 0x0000 }, + { 0xd8f6, 0x0000 }, + { 0xd8f7, 0x0000 }, + { 0xd8f8, 0x0000 }, + { 0xd8f9, 0x0000 }, + { 0xd8fa, 0x0000 }, + { 0xd8fb, 0x0000 }, + { 0xd8fc, 0x0000 }, + { 0xd8fd, 0x0000 }, + { 0xd8fe, 0x0000 }, + { 0xd8ff, 0x0000 }, + { 0xd900, 0x0000 }, + { 0xd901, 0x0000 }, + { 0xd902, 0x0000 }, + { 0xd903, 0x0000 }, + { 0xd904, 0x0000 }, + { 0xd905, 0x0000 }, + { 0xd906, 0x0000 }, + { 0xd907, 0x0000 }, + { 0xd908, 0x0000 }, + { 0xd909, 0x0000 }, + { 0xd90a, 0x0000 }, + { 0xd90b, 0x0000 }, + { 0xd90c, 0x0000 }, + { 0xd90d, 0x0000 }, + { 0xd90e, 0x0000 }, + { 0xd90f, 0x0000 }, + { 0xd910, 0x0000 }, + { 0xd911, 0x0000 }, + { 0xd912, 0x0000 }, + { 0xd913, 0x0000 }, + { 0xd914, 0x0000 }, + { 0xd915, 0x0000 }, + { 0xd916, 0x0000 }, + { 0xd917, 0x0000 }, + { 0xd918, 0x0000 }, + { 0xd919, 0x0000 }, + { 0xd91a, 0x0000 }, + { 0xd91b, 0x0000 }, + { 0xd91c, 0x0000 }, + { 0xd91d, 0x0000 }, + { 0xd91e, 0x0000 }, + { 0xd91f, 0x0000 }, + { 0xd920, 0x0000 }, + { 0xd921, 0x0000 }, + { 0xd922, 0x0000 }, + { 0xd923, 0x0000 }, + { 0xd924, 0x0000 }, + { 0xd925, 0x0000 }, + { 0xd926, 0x0000 }, + { 0xd927, 0x0000 }, + { 0xd928, 0x0000 }, + { 0xd929, 0x0000 }, + { 0xd92a, 0x0000 }, + { 0xd92b, 0x0000 }, + { 0xd92c, 0x0000 }, + { 0xd92d, 0x0000 }, + { 0xd92e, 0x0000 }, + { 0xd92f, 0x0000 }, + { 0xd930, 0x0000 }, + { 0xd931, 0x0000 }, + { 0xd932, 0x0000 }, + { 0xd933, 0x0000 }, + { 0xd934, 0x0000 }, + { 0xd935, 0x0000 }, + { 0xd936, 0x0000 }, + { 0xd937, 0x0000 }, + { 0xd938, 0x0000 }, + { 0xd939, 0x0000 }, + { 0xd93a, 0x0000 }, + { 0xd93b, 0x0000 }, + { 0xd93c, 0x0000 }, + { 0xd93d, 0x0000 }, + { 0xd93e, 0x0000 }, + { 0xd93f, 0x0000 }, + { 0xd940, 0x0000 }, + { 0xd941, 0x0000 }, + { 0xd942, 0x0000 }, + { 0xd943, 0x0000 }, + { 0xd944, 0x0000 }, + { 0xd945, 0x0000 }, + { 0xd946, 0x0000 }, + { 0xd947, 0x0000 }, + { 0xd948, 0x0000 }, + { 0xd949, 0x0000 }, + { 0xd94a, 0x0000 }, + { 0xd94b, 0x0000 }, + { 0xd94c, 0x0000 }, + { 0xd94d, 0x0000 }, + { 0xd94e, 0x0000 }, + { 0xd94f, 0x0000 }, + { 0xd950, 0x0000 }, + { 0xd951, 0x0000 }, + { 0xd952, 0x0000 }, + { 0xd953, 0x0000 }, + { 0xd954, 0x0000 }, + { 0xd955, 0x0000 }, + { 0xd956, 0x0000 }, + { 0xd957, 0x0000 }, + { 0xd958, 0x0000 }, + { 0xd959, 0x0000 }, + { 0xd95a, 0x0000 }, + { 0xd95b, 0x0000 }, + { 0xd95c, 0x0000 }, + { 0xd95d, 0x0000 }, + { 0xd95e, 0x0000 }, + { 0xd95f, 0x0000 }, + { 0xd960, 0x0000 }, + { 0xd961, 0x0000 }, + { 0xd962, 0x0000 }, + { 0xd963, 0x0000 }, + { 0xd964, 0x0000 }, + { 0xd965, 0x0000 }, + { 0xd966, 0x0000 }, + { 0xd967, 0x0000 }, + { 0xd968, 0x0000 }, + { 0xd969, 0x0000 }, + { 0xd96a, 0x0000 }, + { 0xd96b, 0x0000 }, + { 0xd96c, 0x0000 }, + { 0xd96d, 0x0000 }, + { 0xd96e, 0x0000 }, + { 0xd96f, 0x0000 }, + { 0xd970, 0x0000 }, + { 0xd971, 0x0000 }, + { 0xd972, 0x0000 }, + { 0xd973, 0x0000 }, + { 0xd974, 0x0000 }, + { 0xd975, 0x0000 }, + { 0xd976, 0x0000 }, + { 0xd977, 0x0000 }, + { 0xd978, 0x0000 }, + { 0xd979, 0x0000 }, + { 0xd97a, 0x0000 }, + { 0xd97b, 0x0000 }, + { 0xd97c, 0x0000 }, + { 0xd97d, 0x0000 }, + { 0xd97e, 0x0000 }, + { 0xd97f, 0x0000 }, + { 0xd980, 0x0000 }, + { 0xd981, 0x0000 }, + { 0xd982, 0x0000 }, + { 0xd983, 0x0000 }, + { 0xd984, 0x0000 }, + { 0xd985, 0x0000 }, + { 0xd986, 0x0000 }, + { 0xd987, 0x0000 }, + { 0xd988, 0x0000 }, + { 0xd989, 0x0000 }, + { 0xd98a, 0x0000 }, + { 0xd98b, 0x0000 }, + { 0xd98c, 0x0000 }, + { 0xd98d, 0x0000 }, + { 0xd98e, 0x0000 }, + { 0xd98f, 0x0000 }, + { 0xd990, 0x0000 }, + { 0xd991, 0x0000 }, + { 0xd992, 0x0000 }, + { 0xd993, 0x0000 }, + { 0xd994, 0x0000 }, + { 0xd995, 0x0000 }, + { 0xd996, 0x0000 }, + { 0xd997, 0x0000 }, + { 0xd998, 0x0000 }, + { 0xd999, 0x0000 }, + { 0xd99a, 0x0000 }, + { 0xd99b, 0x0000 }, + { 0xd99c, 0x0000 }, + { 0xd99d, 0x0000 }, + { 0xd99e, 0x0000 }, + { 0xd99f, 0x0000 }, + { 0xd9a0, 0x0000 }, + { 0xd9a1, 0x0000 }, + { 0xd9a2, 0x0000 }, + { 0xd9a3, 0x0000 }, + { 0xd9a4, 0x0000 }, + { 0xd9a5, 0x0000 }, + { 0xd9a6, 0x0000 }, + { 0xd9a7, 0x0000 }, + { 0xd9a8, 0x0000 }, + { 0xd9a9, 0x0000 }, + { 0xd9aa, 0x0000 }, + { 0xd9ab, 0x0000 }, + { 0xd9ac, 0x0000 }, + { 0xd9ad, 0x0000 }, + { 0xd9ae, 0x0000 }, + { 0xd9af, 0x0000 }, + { 0xd9b0, 0x0000 }, + { 0xd9b1, 0x0000 }, + { 0xd9b2, 0x0000 }, + { 0xd9b3, 0x0000 }, + { 0xd9b4, 0x0000 }, + { 0xd9b5, 0x0000 }, + { 0xd9b6, 0x0000 }, + { 0xd9b7, 0x0000 }, + { 0xd9b8, 0x0000 }, + { 0xd9b9, 0x0000 }, + { 0xd9ba, 0x0000 }, + { 0xd9bb, 0x0000 }, + { 0xd9bc, 0x0000 }, + { 0xd9bd, 0x0000 }, + { 0xd9be, 0x0000 }, + { 0xd9bf, 0x0000 }, + { 0xd9c0, 0x0000 }, + { 0xd9c1, 0x0000 }, + { 0xd9c2, 0x0000 }, + { 0xd9c3, 0x0000 }, + { 0xd9c4, 0x0000 }, + { 0xd9c5, 0x0000 }, + { 0xd9c6, 0x0000 }, + { 0xd9c7, 0x0000 }, + { 0xd9c8, 0x0000 }, + { 0xd9c9, 0x0000 }, + { 0xd9ca, 0x0000 }, + { 0xd9cb, 0x0000 }, + { 0xd9cc, 0x0000 }, + { 0xd9cd, 0x0000 }, + { 0xd9ce, 0x0000 }, + { 0xd9cf, 0x0000 }, + { 0xd9d0, 0x0000 }, + { 0xd9d1, 0x0000 }, + { 0xd9d2, 0x0000 }, + { 0xd9d3, 0x0000 }, + { 0xd9d4, 0x0000 }, + { 0xd9d5, 0x0000 }, + { 0xd9d6, 0x0000 }, + { 0xd9d7, 0x0000 }, + { 0xd9d8, 0x0000 }, + { 0xd9d9, 0x0000 }, + { 0xd9da, 0x0000 }, + { 0xd9db, 0x0000 }, + { 0xd9dc, 0x0000 }, + { 0xd9dd, 0x0000 }, + { 0xd9de, 0x0000 }, + { 0xd9df, 0x0000 }, + { 0xd9e0, 0x0000 }, + { 0xd9e1, 0x0000 }, + { 0xd9e2, 0x0000 }, + { 0xd9e3, 0x0000 }, + { 0xd9e4, 0x0000 }, + { 0xd9e5, 0x0000 }, + { 0xd9e6, 0x0000 }, + { 0xd9e7, 0x0000 }, + { 0xd9e8, 0x0000 }, + { 0xd9e9, 0x0000 }, + { 0xd9ea, 0x0000 }, + { 0xd9eb, 0x0000 }, + { 0xd9ec, 0x0000 }, + { 0xd9ed, 0x0000 }, + { 0xd9ee, 0x0000 }, + { 0xd9ef, 0x0000 }, + { 0xd9f0, 0x0000 }, + { 0xd9f1, 0x0000 }, + { 0xd9f2, 0x0000 }, + { 0xd9f3, 0x0000 }, + { 0xd9f4, 0x0000 }, + { 0xd9f5, 0x0000 }, + { 0xd9f6, 0x0000 }, + { 0xd9f7, 0x0000 }, + { 0xd9f8, 0x0000 }, + { 0xd9f9, 0x0000 }, + { 0xd9fa, 0x0000 }, + { 0xd9fb, 0x0000 }, + { 0xd9fc, 0x0000 }, + { 0xd9fd, 0x0000 }, + { 0xd9fe, 0x0000 }, + { 0xd9ff, 0x0000 }, + { 0xda00, 0x0000 }, + { 0xda01, 0x0000 }, + { 0xda02, 0x0000 }, + { 0xda03, 0x0000 }, + { 0xda04, 0x0000 }, + { 0xda05, 0x0000 }, + { 0xda06, 0x0000 }, + { 0xda07, 0x0000 }, + { 0xda08, 0x0000 }, + { 0xda09, 0x0000 }, + { 0xda0a, 0x0000 }, + { 0xda0b, 0x0000 }, + { 0xda0c, 0x0000 }, + { 0xda0d, 0x0000 }, + { 0xda0e, 0x0000 }, + { 0xda0f, 0x0000 }, + { 0xda10, 0x0000 }, + { 0xda11, 0x0000 }, + { 0xda12, 0x0000 }, + { 0xda13, 0x0000 }, + { 0xda14, 0x0000 }, + { 0xda15, 0x0000 }, + { 0xda16, 0x0000 }, + { 0xda17, 0x0000 }, + { 0xda18, 0x0000 }, + { 0xda19, 0x0000 }, + { 0xda1a, 0x0000 }, + { 0xda1b, 0x0000 }, + { 0xda1c, 0x0000 }, + { 0xda1d, 0x0000 }, + { 0xda1e, 0x0000 }, + { 0xda1f, 0x0000 }, + { 0xda20, 0x0000 }, + { 0xda21, 0x0000 }, + { 0xda22, 0x0000 }, + { 0xda23, 0x0000 }, + { 0xda24, 0x0000 }, + { 0xda25, 0x0000 }, + { 0xda26, 0x0000 }, + { 0xda27, 0x0000 }, + { 0xda28, 0x0000 }, + { 0xda29, 0x0000 }, + { 0xda2a, 0x0000 }, + { 0xda2b, 0x0000 }, + { 0xda2c, 0x0000 }, + { 0xda2d, 0x0000 }, + { 0xda2e, 0x0000 }, + { 0xda2f, 0x0000 }, + { 0xda30, 0x0000 }, + { 0xda31, 0x0000 }, + { 0xda32, 0x0000 }, + { 0xda33, 0x0000 }, + { 0xda34, 0x0000 }, + { 0xda35, 0x0000 }, + { 0xda36, 0x0000 }, + { 0xda37, 0x0000 }, + { 0xda38, 0x0000 }, + { 0xda39, 0x0000 }, + { 0xda3a, 0x0000 }, + { 0xda3b, 0x0000 }, + { 0xda3c, 0x0000 }, + { 0xda3d, 0x0000 }, + { 0xda3e, 0x0000 }, + { 0xda3f, 0x0000 }, + { 0xda40, 0x0000 }, + { 0xda41, 0x0000 }, + { 0xda42, 0x0000 }, + { 0xda43, 0x0000 }, + { 0xda44, 0x0000 }, + { 0xda45, 0x0000 }, + { 0xda46, 0x0000 }, + { 0xda47, 0x0000 }, + { 0xda48, 0x0000 }, + { 0xda49, 0x0000 }, + { 0xda4a, 0x0000 }, + { 0xda4b, 0x0000 }, + { 0xda4c, 0x0000 }, + { 0xda4d, 0x0000 }, + { 0xda4e, 0x0000 }, + { 0xda4f, 0x0000 }, + { 0xda50, 0x0000 }, + { 0xda51, 0x0000 }, + { 0xda52, 0x0000 }, + { 0xda53, 0x0000 }, + { 0xda54, 0x0000 }, + { 0xda55, 0x0000 }, + { 0xda56, 0x0000 }, + { 0xda57, 0x0000 }, + { 0xda58, 0x0000 }, + { 0xda59, 0x0000 }, + { 0xda5a, 0x0000 }, + { 0xda5b, 0x0000 }, + { 0xda5c, 0x0000 }, + { 0xda5d, 0x0000 }, + { 0xda5e, 0x0000 }, + { 0xda5f, 0x0000 }, + { 0xda60, 0x0000 }, + { 0xda61, 0x0000 }, + { 0xda62, 0x0000 }, + { 0xda63, 0x0000 }, + { 0xda64, 0x0000 }, + { 0xda65, 0x0000 }, + { 0xda66, 0x0000 }, + { 0xda67, 0x0000 }, + { 0xda68, 0x0000 }, + { 0xda69, 0x0000 }, + { 0xda6a, 0x0000 }, + { 0xda6b, 0x0000 }, + { 0xda6c, 0x0000 }, + { 0xda6d, 0x0000 }, + { 0xda6e, 0x0000 }, + { 0xda6f, 0x0000 }, + { 0xda70, 0x0000 }, + { 0xda71, 0x0000 }, + { 0xda72, 0x0000 }, + { 0xda73, 0x0000 }, + { 0xda74, 0x0000 }, + { 0xda75, 0x0000 }, + { 0xda76, 0x0000 }, + { 0xda77, 0x0000 }, + { 0xda78, 0x0000 }, + { 0xda79, 0x0000 }, + { 0xda7a, 0x0000 }, + { 0xda7b, 0x0000 }, + { 0xda7c, 0x0000 }, + { 0xda7d, 0x0000 }, + { 0xda7e, 0x0000 }, + { 0xda7f, 0x0000 }, + { 0xda80, 0x0000 }, + { 0xda81, 0x0000 }, + { 0xda82, 0x0000 }, + { 0xda83, 0x0000 }, + { 0xda84, 0x0000 }, + { 0xda85, 0x0000 }, + { 0xda86, 0x0000 }, + { 0xda87, 0x0000 }, + { 0xda88, 0x0000 }, + { 0xda89, 0x0000 }, + { 0xda8a, 0x0000 }, + { 0xda8b, 0x0000 }, + { 0xda8c, 0x0000 }, + { 0xda8d, 0x0000 }, + { 0xda8e, 0x0000 }, + { 0xda8f, 0x0000 }, + { 0xda90, 0x0000 }, + { 0xda91, 0x0000 }, + { 0xda92, 0x0000 }, + { 0xda93, 0x0000 }, + { 0xda94, 0x0000 }, + { 0xda95, 0x0000 }, + { 0xda96, 0x0000 }, + { 0xda97, 0x0000 }, + { 0xda98, 0x0000 }, + { 0xda99, 0x0000 }, + { 0xda9a, 0x0000 }, + { 0xda9b, 0x0000 }, + { 0xda9c, 0x0000 }, + { 0xda9d, 0x0000 }, + { 0xda9e, 0x0000 }, + { 0xda9f, 0x0000 }, + { 0xdaa0, 0x0000 }, + { 0xdaa1, 0x0000 }, + { 0xdaa2, 0x0000 }, + { 0xdaa3, 0x0000 }, + { 0xdaa4, 0x0000 }, + { 0xdaa5, 0x0000 }, + { 0xdaa6, 0x0000 }, + { 0xdaa7, 0x0000 }, + { 0xdaa8, 0x0000 }, + { 0xdaa9, 0x0000 }, + { 0xdaaa, 0x0000 }, + { 0xdaab, 0x0000 }, + { 0xdaac, 0x0000 }, + { 0xdaad, 0x0000 }, + { 0xdaae, 0x0000 }, + { 0xdaaf, 0x0000 }, + { 0xdab0, 0x0000 }, + { 0xdab1, 0x0000 }, + { 0xdab2, 0x0000 }, + { 0xdab3, 0x0000 }, + { 0xdab4, 0x0000 }, + { 0xdab5, 0x0000 }, + { 0xdab6, 0x0000 }, + { 0xdab7, 0x0000 }, + { 0xdab8, 0x0000 }, + { 0xdab9, 0x0000 }, + { 0xdaba, 0x0000 }, + { 0xdabb, 0x0000 }, + { 0xdabc, 0x0000 }, + { 0xdabd, 0x0000 }, + { 0xdabe, 0x0000 }, + { 0xdabf, 0x0000 }, + { 0xdac0, 0x0000 }, + { 0xdac1, 0x0000 }, + { 0xdac2, 0x0000 }, + { 0xdac3, 0x0000 }, + { 0xdac4, 0x0000 }, + { 0xdac5, 0x0000 }, + { 0xdac6, 0x0000 }, + { 0xdac7, 0x0000 }, + { 0xdac8, 0x0000 }, + { 0xdac9, 0x0000 }, + { 0xdaca, 0x0000 }, + { 0xdacb, 0x0000 }, + { 0xdacc, 0x0000 }, + { 0xdacd, 0x0000 }, + { 0xdace, 0x0000 }, + { 0xdacf, 0x0000 }, + { 0xdad0, 0x0000 }, + { 0xdad1, 0x0000 }, + { 0xdad2, 0x0000 }, + { 0xdad3, 0x0000 }, + { 0xdad4, 0x0000 }, + { 0xdad5, 0x0000 }, + { 0xdad6, 0x0000 }, + { 0xdad7, 0x0000 }, + { 0xdad8, 0x0000 }, + { 0xdad9, 0x0000 }, + { 0xdada, 0x0000 }, + { 0xdadb, 0x0000 }, + { 0xdadc, 0x0000 }, + { 0xdadd, 0x0000 }, + { 0xdade, 0x0000 }, + { 0xdadf, 0x0000 }, + { 0xdae0, 0x0000 }, + { 0xdae1, 0x0000 }, + { 0xdae2, 0x0000 }, + { 0xdae3, 0x0000 }, + { 0xdae4, 0x0000 }, + { 0xdae5, 0x0000 }, + { 0xdae6, 0x0000 }, + { 0xdae7, 0x0000 }, + { 0xdae8, 0x0000 }, + { 0xdae9, 0x0000 }, + { 0xdaea, 0x0000 }, + { 0xdaeb, 0x0000 }, + { 0xdaec, 0x0000 }, + { 0xdaed, 0x0000 }, + { 0xdaee, 0x0000 }, + { 0xdaef, 0x0000 }, + { 0xdaf0, 0x0000 }, + { 0xdaf1, 0x0000 }, + { 0xdaf2, 0x0000 }, + { 0xdaf3, 0x0000 }, + { 0xdaf4, 0x0000 }, + { 0xdaf5, 0x0000 }, + { 0xdaf6, 0x0000 }, + { 0xdaf7, 0x0000 }, + { 0xdaf8, 0x0000 }, + { 0xdaf9, 0x0000 }, + { 0xdafa, 0x0000 }, + { 0xdafb, 0x0000 }, + { 0xdafc, 0x0000 }, + { 0xdafd, 0x0000 }, + { 0xdafe, 0x0000 }, + { 0xdaff, 0x0000 }, + { 0xdb00, 0x0000 }, + { 0xdb01, 0x0000 }, + { 0xdb02, 0x0000 }, + { 0xdb03, 0x0000 }, + { 0xdb04, 0x0000 }, + { 0xdb05, 0x0000 }, + { 0xdb06, 0x0000 }, + { 0xdb07, 0x0000 }, + { 0xdb08, 0x0000 }, + { 0xdb09, 0x0000 }, + { 0xdb0a, 0x0000 }, + { 0xdb0b, 0x0000 }, + { 0xdb0c, 0x0000 }, + { 0xdb0d, 0x0000 }, + { 0xdb0e, 0x0000 }, + { 0xdb0f, 0x0000 }, + { 0xdb10, 0x0000 }, + { 0xdb11, 0x0000 }, + { 0xdb12, 0x0000 }, + { 0xdb13, 0x0000 }, + { 0xdb14, 0x0000 }, + { 0xdb15, 0x0000 }, + { 0xdb16, 0x0000 }, + { 0xdb17, 0x0000 }, + { 0xdb18, 0x0000 }, + { 0xdb19, 0x0000 }, + { 0xdb1a, 0x0000 }, + { 0xdb1b, 0x0000 }, + { 0xdb1c, 0x0000 }, + { 0xdb1d, 0x0000 }, + { 0xdb1e, 0x0000 }, + { 0xdb1f, 0x0000 }, + { 0xdb20, 0x0000 }, + { 0xdb21, 0x0000 }, + { 0xdb22, 0x0000 }, + { 0xdb23, 0x0000 }, + { 0xdb24, 0x0000 }, + { 0xdb25, 0x0000 }, + { 0xdb26, 0x0000 }, + { 0xdb27, 0x0000 }, + { 0xdb28, 0x0000 }, + { 0xdb29, 0x0000 }, + { 0xdb2a, 0x0000 }, + { 0xdb2b, 0x0000 }, + { 0xdb2c, 0x0000 }, + { 0xdb2d, 0x0000 }, + { 0xdb2e, 0x0000 }, + { 0xdb2f, 0x0000 }, + { 0xdb30, 0x0000 }, + { 0xdb31, 0x0000 }, + { 0xdb32, 0x0000 }, + { 0xdb33, 0x0000 }, + { 0xdb34, 0x0000 }, + { 0xdb35, 0x0000 }, + { 0xdb36, 0x0000 }, + { 0xdb37, 0x0000 }, + { 0xdb38, 0x0000 }, + { 0xdb39, 0x0000 }, + { 0xdb3a, 0x0000 }, + { 0xdb3b, 0x0000 }, + { 0xdb3c, 0x0000 }, + { 0xdb3d, 0x0000 }, + { 0xdb3e, 0x0000 }, + { 0xdb3f, 0x0000 }, + { 0xdb40, 0x0000 }, + { 0xdb41, 0x0000 }, + { 0xdb42, 0x0000 }, + { 0xdb43, 0x0000 }, + { 0xdb44, 0x0000 }, + { 0xdb45, 0x0000 }, + { 0xdb46, 0x0000 }, + { 0xdb47, 0x0000 }, + { 0xdb48, 0x0000 }, + { 0xdb49, 0x0000 }, + { 0xdb4a, 0x0000 }, + { 0xdb4b, 0x0000 }, + { 0xdb4c, 0x0000 }, + { 0xdb4d, 0x0000 }, + { 0xdb4e, 0x0000 }, + { 0xdb4f, 0x0000 }, + { 0xdb50, 0x0000 }, + { 0xdb51, 0x0000 }, + { 0xdb52, 0x0000 }, + { 0xdb53, 0x0000 }, + { 0xdb54, 0x0000 }, + { 0xdb55, 0x0000 }, + { 0xdb56, 0x0000 }, + { 0xdb57, 0x0000 }, + { 0xdb58, 0x0000 }, + { 0xdb59, 0x0000 }, + { 0xdb5a, 0x0000 }, + { 0xdb5b, 0x0000 }, + { 0xdb5c, 0x0000 }, + { 0xdb5d, 0x0000 }, + { 0xdb5e, 0x0000 }, + { 0xdb5f, 0x0000 }, + { 0xdb60, 0x0000 }, + { 0xdb61, 0x0000 }, + { 0xdb62, 0x0000 }, + { 0xdb63, 0x0000 }, + { 0xdb64, 0x0000 }, + { 0xdb65, 0x0000 }, + { 0xdb66, 0x0000 }, + { 0xdb67, 0x0000 }, + { 0xdb68, 0x0000 }, + { 0xdb69, 0x0000 }, + { 0xdb6a, 0x0000 }, + { 0xdb6b, 0x0000 }, + { 0xdb6c, 0x0000 }, + { 0xdb6d, 0x0000 }, + { 0xdb6e, 0x0000 }, + { 0xdb6f, 0x0000 }, + { 0xdb70, 0x0000 }, + { 0xdb71, 0x0000 }, + { 0xdb72, 0x0000 }, + { 0xdb73, 0x0000 }, + { 0xdb74, 0x0000 }, + { 0xdb75, 0x0000 }, + { 0xdb76, 0x0000 }, + { 0xdb77, 0x0000 }, + { 0xdb78, 0x0000 }, + { 0xdb79, 0x0000 }, + { 0xdb7a, 0x0000 }, + { 0xdb7b, 0x0000 }, + { 0xdb7c, 0x0000 }, + { 0xdb7d, 0x0000 }, + { 0xdb7e, 0x0000 }, + { 0xdb7f, 0x0000 }, + { 0xdb80, 0x0000 }, + { 0xdb81, 0x0000 }, + { 0xdb82, 0x0000 }, + { 0xdb83, 0x0000 }, + { 0xdb84, 0x0000 }, + { 0xdb85, 0x0000 }, + { 0xdb86, 0x0000 }, + { 0xdb87, 0x0000 }, + { 0xdb88, 0x0000 }, + { 0xdb89, 0x0000 }, + { 0xdb8a, 0x0000 }, + { 0xdb8b, 0x0000 }, + { 0xdb8c, 0x0000 }, + { 0xdb8d, 0x0000 }, + { 0xdb8e, 0x0000 }, + { 0xdb8f, 0x0000 }, + { 0xdb90, 0x0000 }, + { 0xdb91, 0x0000 }, + { 0xdb92, 0x0000 }, + { 0xdb93, 0x0000 }, + { 0xdb94, 0x0000 }, + { 0xdb95, 0x0000 }, + { 0xdb96, 0x0000 }, + { 0xdb97, 0x0000 }, + { 0xdb98, 0x0000 }, + { 0xdb99, 0x0000 }, + { 0xdb9a, 0x0000 }, + { 0xdb9b, 0x0000 }, + { 0xdb9c, 0x0000 }, + { 0xdb9d, 0x0000 }, + { 0xdb9e, 0x0000 }, + { 0xdb9f, 0x0000 }, + { 0xdba0, 0x0000 }, + { 0xdba1, 0x0000 }, + { 0xdba2, 0x0000 }, + { 0xdba3, 0x0000 }, + { 0xdba4, 0x0000 }, + { 0xdba5, 0x0000 }, + { 0xdba6, 0x0000 }, + { 0xdba7, 0x0000 }, + { 0xdba8, 0x0000 }, + { 0xdba9, 0x0000 }, + { 0xdbaa, 0x0000 }, + { 0xdbab, 0x0000 }, + { 0xdbac, 0x0000 }, + { 0xdbad, 0x0000 }, + { 0xdbae, 0x0000 }, + { 0xdbaf, 0x0000 }, + { 0xdbb0, 0x0000 }, + { 0xdbb1, 0x0000 }, + { 0xdbb2, 0x0000 }, + { 0xdbb3, 0x0000 }, + { 0xdbb4, 0x0000 }, + { 0xdbb5, 0x0000 }, + { 0xdbb6, 0x0000 }, + { 0xdbb7, 0x0000 }, + { 0xdbb8, 0x0000 }, + { 0xdbb9, 0x0000 }, + { 0xdbba, 0x0000 }, + { 0xdbbb, 0x0000 }, + { 0xdbbc, 0x0000 }, + { 0xdbbd, 0x0000 }, + { 0xdbbe, 0x0000 }, + { 0xdbbf, 0x0000 }, + { 0xdbc0, 0x0000 }, + { 0xdbc1, 0x0000 }, + { 0xdbc2, 0x0000 }, + { 0xdbc3, 0x0000 }, + { 0xdbc4, 0x0000 }, + { 0xdbc5, 0x0000 }, + { 0xdbc6, 0x0000 }, + { 0xdbc7, 0x0000 }, + { 0xdbc8, 0x0000 }, + { 0xdbc9, 0x0000 }, + { 0xdbca, 0x0000 }, + { 0xdbcb, 0x0000 }, + { 0xdbcc, 0x0000 }, + { 0xdbcd, 0x0000 }, + { 0xdbce, 0x0000 }, + { 0xdbcf, 0x0000 }, + { 0xdbd0, 0x0000 }, + { 0xdbd1, 0x0000 }, + { 0xdbd2, 0x0000 }, + { 0xdbd3, 0x0000 }, + { 0xdbd4, 0x0000 }, + { 0xdbd5, 0x0000 }, + { 0xdbd6, 0x0000 }, + { 0xdbd7, 0x0000 }, + { 0xdbd8, 0x0000 }, + { 0xdbd9, 0x0000 }, + { 0xdbda, 0x0000 }, + { 0xdbdb, 0x0000 }, + { 0xdbdc, 0x0000 }, + { 0xdbdd, 0x0000 }, + { 0xdbde, 0x0000 }, + { 0xdbdf, 0x0000 }, + { 0xdbe0, 0x0000 }, + { 0xdbe1, 0x0000 }, + { 0xdbe2, 0x0000 }, + { 0xdbe3, 0x0000 }, + { 0xdbe4, 0x0000 }, + { 0xdbe5, 0x0000 }, + { 0xdbe6, 0x0000 }, + { 0xdbe7, 0x0000 }, + { 0xdbe8, 0x0000 }, + { 0xdbe9, 0x0000 }, + { 0xdbea, 0x0000 }, + { 0xdbeb, 0x0000 }, + { 0xdbec, 0x0000 }, + { 0xdbed, 0x0000 }, + { 0xdbee, 0x0000 }, + { 0xdbef, 0x0000 }, + { 0xdbf0, 0x0000 }, + { 0xdbf1, 0x0000 }, + { 0xdbf2, 0x0000 }, + { 0xdbf3, 0x0000 }, + { 0xdbf4, 0x0000 }, + { 0xdbf5, 0x0000 }, + { 0xdbf6, 0x0000 }, + { 0xdbf7, 0x0000 }, + { 0xdbf8, 0x0000 }, + { 0xdbf9, 0x0000 }, + { 0xdbfa, 0x0000 }, + { 0xdbfb, 0x0000 }, + { 0xdbfc, 0x0000 }, + { 0xdbfd, 0x0000 }, + { 0xdbfe, 0x0000 }, + { 0xdbff, 0x0000 }, + { 0xdc00, 0x0000 }, + { 0xdc01, 0x0000 }, + { 0xdc02, 0x0000 }, + { 0xdc03, 0x0000 }, + { 0xdc04, 0x0000 }, + { 0xdc05, 0x0000 }, + { 0xdc06, 0x0000 }, + { 0xdc07, 0x0000 }, + { 0xdc08, 0x0000 }, + { 0xdc09, 0x0000 }, + { 0xdc0a, 0x0000 }, + { 0xdc0b, 0x0000 }, + { 0xdc0c, 0x0000 }, + { 0xdc0d, 0x0000 }, + { 0xdc0e, 0x0000 }, + { 0xdc0f, 0x0000 }, + { 0xdc10, 0x0000 }, + { 0xdc11, 0x0000 }, + { 0xdc12, 0x0000 }, + { 0xdc13, 0x0000 }, + { 0xdc14, 0x0000 }, + { 0xdc15, 0x0000 }, + { 0xdc16, 0x0000 }, + { 0xdc17, 0x0000 }, + { 0xdc18, 0x0000 }, + { 0xdc19, 0x0000 }, + { 0xdc1a, 0x0000 }, + { 0xdc1b, 0x0000 }, + { 0xdc1c, 0x0000 }, + { 0xdc1d, 0x0000 }, + { 0xdc1e, 0x0000 }, + { 0xdc1f, 0x0000 }, + { 0xdc20, 0x0000 }, + { 0xdc21, 0x0000 }, + { 0xdc22, 0x0000 }, + { 0xdc23, 0x0000 }, + { 0xdc24, 0x0000 }, + { 0xdc25, 0x0000 }, + { 0xdc26, 0x0000 }, + { 0xdc27, 0x0000 }, + { 0xdc28, 0x0000 }, + { 0xdc29, 0x0000 }, + { 0xdc2a, 0x0000 }, + { 0xdc2b, 0x0000 }, + { 0xdc2c, 0x0000 }, + { 0xdc2d, 0x0000 }, + { 0xdc2e, 0x0000 }, + { 0xdc2f, 0x0000 }, + { 0xdc30, 0x0000 }, + { 0xdc31, 0x0000 }, + { 0xdc32, 0x0000 }, + { 0xdc33, 0x0000 }, + { 0xdc34, 0x0000 }, + { 0xdc35, 0x0000 }, + { 0xdc36, 0x0000 }, + { 0xdc37, 0x0000 }, + { 0xdc38, 0x0000 }, + { 0xdc39, 0x0000 }, + { 0xdc3a, 0x0000 }, + { 0xdc3b, 0x0000 }, + { 0xdc3c, 0x0000 }, + { 0xdc3d, 0x0000 }, + { 0xdc3e, 0x0000 }, + { 0xdc3f, 0x0000 }, + { 0xdc40, 0x0000 }, + { 0xdc41, 0x0000 }, + { 0xdc42, 0x0000 }, + { 0xdc43, 0x0000 }, + { 0xdc44, 0x0000 }, + { 0xdc45, 0x0000 }, + { 0xdc46, 0x0000 }, + { 0xdc47, 0x0000 }, + { 0xdc48, 0x0000 }, + { 0xdc49, 0x0000 }, + { 0xdc4a, 0x0000 }, + { 0xdc4b, 0x0000 }, + { 0xdc4c, 0x0000 }, + { 0xdc4d, 0x0000 }, + { 0xdc4e, 0x0000 }, + { 0xdc4f, 0x0000 }, + { 0xdc50, 0x0000 }, + { 0xdc51, 0x0000 }, + { 0xdc52, 0x0000 }, + { 0xdc53, 0x0000 }, + { 0xdc54, 0x0000 }, + { 0xdc55, 0x0000 }, + { 0xdc56, 0x0000 }, + { 0xdc57, 0x0000 }, + { 0xdc58, 0x0000 }, + { 0xdc59, 0x0000 }, + { 0xdc5a, 0x0000 }, + { 0xdc5b, 0x0000 }, + { 0xdc5c, 0x0000 }, + { 0xdc5d, 0x0000 }, + { 0xdc5e, 0x0000 }, + { 0xdc5f, 0x0000 }, + { 0xdc60, 0x0000 }, + { 0xdc61, 0x0000 }, + { 0xdc62, 0x0000 }, + { 0xdc63, 0x0000 }, + { 0xdc64, 0x0000 }, + { 0xdc65, 0x0000 }, + { 0xdc66, 0x0000 }, + { 0xdc67, 0x0000 }, + { 0xdc68, 0x0000 }, + { 0xdc69, 0x0000 }, + { 0xdc6a, 0x0000 }, + { 0xdc6b, 0x0000 }, + { 0xdc6c, 0x0000 }, + { 0xdc6d, 0x0000 }, + { 0xdc6e, 0x0000 }, + { 0xdc6f, 0x0000 }, + { 0xdc70, 0x0000 }, + { 0xdc71, 0x0000 }, + { 0xdc72, 0x0000 }, + { 0xdc73, 0x0000 }, + { 0xdc74, 0x0000 }, + { 0xdc75, 0x0000 }, + { 0xdc76, 0x0000 }, + { 0xdc77, 0x0000 }, + { 0xdc78, 0x0000 }, + { 0xdc79, 0x0000 }, + { 0xdc7a, 0x0000 }, + { 0xdc7b, 0x0000 }, + { 0xdc7c, 0x0000 }, + { 0xdc7d, 0x0000 }, + { 0xdc7e, 0x0000 }, + { 0xdc7f, 0x0000 }, + { 0xdc80, 0x0000 }, + { 0xdc81, 0x0000 }, + { 0xdc82, 0x0000 }, + { 0xdc83, 0x0000 }, + { 0xdc84, 0x0000 }, + { 0xdc85, 0x0000 }, + { 0xdc86, 0x0000 }, + { 0xdc87, 0x0000 }, + { 0xdc88, 0x0000 }, + { 0xdc89, 0x0000 }, + { 0xdc8a, 0x0000 }, + { 0xdc8b, 0x0000 }, + { 0xdc8c, 0x0000 }, + { 0xdc8d, 0x0000 }, + { 0xdc8e, 0x0000 }, + { 0xdc8f, 0x0000 }, + { 0xdc90, 0x0000 }, + { 0xdc91, 0x0000 }, + { 0xdc92, 0x0000 }, + { 0xdc93, 0x0000 }, + { 0xdc94, 0x0000 }, + { 0xdc95, 0x0000 }, + { 0xdc96, 0x0000 }, + { 0xdc97, 0x0000 }, + { 0xdc98, 0x0000 }, + { 0xdc99, 0x0000 }, + { 0xdc9a, 0x0000 }, + { 0xdc9b, 0x0000 }, + { 0xdc9c, 0x0000 }, + { 0xdc9d, 0x0000 }, + { 0xdc9e, 0x0000 }, + { 0xdc9f, 0x0000 }, + { 0xdca0, 0x0000 }, + { 0xdca1, 0x0000 }, + { 0xdca2, 0x0000 }, + { 0xdca3, 0x0000 }, + { 0xdca4, 0x0000 }, + { 0xdca5, 0x0000 }, + { 0xdca6, 0x0000 }, + { 0xdca7, 0x0000 }, + { 0xdca8, 0x0000 }, + { 0xdca9, 0x0000 }, + { 0xdcaa, 0x0000 }, + { 0xdcab, 0x0000 }, + { 0xdcac, 0x0000 }, + { 0xdcad, 0x0000 }, + { 0xdcae, 0x0000 }, + { 0xdcaf, 0x0000 }, + { 0xdcb0, 0x0000 }, + { 0xdcb1, 0x0000 }, + { 0xdcb2, 0x0000 }, + { 0xdcb3, 0x0000 }, + { 0xdcb4, 0x0000 }, + { 0xdcb5, 0x0000 }, + { 0xdcb6, 0x0000 }, + { 0xdcb7, 0x0000 }, + { 0xdcb8, 0x0000 }, + { 0xdcb9, 0x0000 }, + { 0xdcba, 0x0000 }, + { 0xdcbb, 0x0000 }, + { 0xdcbc, 0x0000 }, + { 0xdcbd, 0x0000 }, + { 0xdcbe, 0x0000 }, + { 0xdcbf, 0x0000 }, + { 0xdcc0, 0x0000 }, + { 0xdcc1, 0x0000 }, + { 0xdcc2, 0x0000 }, + { 0xdcc3, 0x0000 }, + { 0xdcc4, 0x0000 }, + { 0xdcc5, 0x0000 }, + { 0xdcc6, 0x0000 }, + { 0xdcc7, 0x0000 }, + { 0xdcc8, 0x0000 }, + { 0xdcc9, 0x0000 }, + { 0xdcca, 0x0000 }, + { 0xdccb, 0x0000 }, + { 0xdccc, 0x0000 }, + { 0xdccd, 0x0000 }, + { 0xdcce, 0x0000 }, + { 0xdccf, 0x0000 }, + { 0xdcd0, 0x0000 }, + { 0xdcd1, 0x0000 }, + { 0xdcd2, 0x0000 }, + { 0xdcd3, 0x0000 }, + { 0xdcd4, 0x0000 }, + { 0xdcd5, 0x0000 }, + { 0xdcd6, 0x0000 }, + { 0xdcd7, 0x0000 }, + { 0xdcd8, 0x0000 }, + { 0xdcd9, 0x0000 }, + { 0xdcda, 0x0000 }, + { 0xdcdb, 0x0000 }, + { 0xdcdc, 0x0000 }, + { 0xdcdd, 0x0000 }, + { 0xdcde, 0x0000 }, + { 0xdcdf, 0x0000 }, + { 0xdce0, 0x0000 }, + { 0xdce1, 0x0000 }, + { 0xdce2, 0x0000 }, + { 0xdce3, 0x0000 }, + { 0xdce4, 0x0000 }, + { 0xdce5, 0x0000 }, + { 0xdce6, 0x0000 }, + { 0xdce7, 0x0000 }, + { 0xdce8, 0x0000 }, + { 0xdce9, 0x0000 }, + { 0xdcea, 0x0000 }, + { 0xdceb, 0x0000 }, + { 0xdcec, 0x0000 }, + { 0xdced, 0x0000 }, + { 0xdcee, 0x0000 }, + { 0xdcef, 0x0000 }, + { 0xdcf0, 0x0000 }, + { 0xdcf1, 0x0000 }, + { 0xdcf2, 0x0000 }, + { 0xdcf3, 0x0000 }, + { 0xdcf4, 0x0000 }, + { 0xdcf5, 0x0000 }, + { 0xdcf6, 0x0000 }, + { 0xdcf7, 0x0000 }, + { 0xdcf8, 0x0000 }, + { 0xdcf9, 0x0000 }, + { 0xdcfa, 0x0000 }, + { 0xdcfb, 0x0000 }, + { 0xdcfc, 0x0000 }, + { 0xdcfd, 0x0000 }, + { 0xdcfe, 0x0000 }, + { 0xdcff, 0x0000 }, + { 0xdd00, 0x0000 }, + { 0xdd01, 0x0000 }, + { 0xdd02, 0x0000 }, + { 0xdd03, 0x0000 }, + { 0xdd04, 0x0000 }, + { 0xdd05, 0x0000 }, + { 0xdd06, 0x0000 }, + { 0xdd07, 0x0000 }, + { 0xdd08, 0x0000 }, + { 0xdd09, 0x0000 }, + { 0xdd0a, 0x0000 }, + { 0xdd0b, 0x0000 }, + { 0xdd0c, 0x0000 }, + { 0xdd0d, 0x0000 }, + { 0xdd0e, 0x0000 }, + { 0xdd0f, 0x0000 }, + { 0xdd10, 0x0000 }, + { 0xdd11, 0x0000 }, + { 0xdd12, 0x0000 }, + { 0xdd13, 0x0000 }, + { 0xdd14, 0x0000 }, + { 0xdd15, 0x0000 }, + { 0xdd16, 0x0000 }, + { 0xdd17, 0x0000 }, + { 0xdd18, 0x0000 }, + { 0xdd19, 0x0000 }, + { 0xdd1a, 0x0000 }, + { 0xdd1b, 0x0000 }, + { 0xdd1c, 0x0000 }, + { 0xdd1d, 0x0000 }, + { 0xdd1e, 0x0000 }, + { 0xdd1f, 0x0000 }, + { 0xdd20, 0x0000 }, + { 0xdd21, 0x0000 }, + { 0xdd22, 0x0000 }, + { 0xdd23, 0x0000 }, + { 0xdd24, 0x0000 }, + { 0xdd25, 0x0000 }, + { 0xdd26, 0x0000 }, + { 0xdd27, 0x0000 }, + { 0xdd28, 0x0000 }, + { 0xdd29, 0x0000 }, + { 0xdd2a, 0x0000 }, + { 0xdd2b, 0x0000 }, + { 0xdd2c, 0x0000 }, + { 0xdd2d, 0x0000 }, + { 0xdd2e, 0x0000 }, + { 0xdd2f, 0x0000 }, + { 0xdd30, 0x0000 }, + { 0xdd31, 0x0000 }, + { 0xdd32, 0x0000 }, + { 0xdd33, 0x0000 }, + { 0xdd34, 0x0000 }, + { 0xdd35, 0x0000 }, + { 0xdd36, 0x0000 }, + { 0xdd37, 0x0000 }, + { 0xdd38, 0x0000 }, + { 0xdd39, 0x0000 }, + { 0xdd3a, 0x0000 }, + { 0xdd3b, 0x0000 }, + { 0xdd3c, 0x0000 }, + { 0xdd3d, 0x0000 }, + { 0xdd3e, 0x0000 }, + { 0xdd3f, 0x0000 }, + { 0xdd40, 0x0000 }, + { 0xdd41, 0x0000 }, + { 0xdd42, 0x0000 }, + { 0xdd43, 0x0000 }, + { 0xdd44, 0x0000 }, + { 0xdd45, 0x0000 }, + { 0xdd46, 0x0000 }, + { 0xdd47, 0x0000 }, + { 0xdd48, 0x0000 }, + { 0xdd49, 0x0000 }, + { 0xdd4a, 0x0000 }, + { 0xdd4b, 0x0000 }, + { 0xdd4c, 0x0000 }, + { 0xdd4d, 0x0000 }, + { 0xdd4e, 0x0000 }, + { 0xdd4f, 0x0000 }, + { 0xdd50, 0x0000 }, + { 0xdd51, 0x0000 }, + { 0xdd52, 0x0000 }, + { 0xdd53, 0x0000 }, + { 0xdd54, 0x0000 }, + { 0xdd55, 0x0000 }, + { 0xdd56, 0x0000 }, + { 0xdd57, 0x0000 }, + { 0xdd58, 0x0000 }, + { 0xdd59, 0x0000 }, + { 0xdd5a, 0x0000 }, + { 0xdd5b, 0x0000 }, + { 0xdd5c, 0x0000 }, + { 0xdd5d, 0x0000 }, + { 0xdd5e, 0x0000 }, + { 0xdd5f, 0x0000 }, + { 0xdd60, 0x0000 }, + { 0xdd61, 0x0000 }, + { 0xdd62, 0x0000 }, + { 0xdd63, 0x0000 }, + { 0xdd64, 0x0000 }, + { 0xdd65, 0x0000 }, + { 0xdd66, 0x0000 }, + { 0xdd67, 0x0000 }, + { 0xdd68, 0x0000 }, + { 0xdd69, 0x0000 }, + { 0xdd6a, 0x0000 }, + { 0xdd6b, 0x0000 }, + { 0xdd6c, 0x0000 }, + { 0xdd6d, 0x0000 }, + { 0xdd6e, 0x0000 }, + { 0xdd6f, 0x0000 }, + { 0xdd70, 0x0000 }, + { 0xdd71, 0x0000 }, + { 0xdd72, 0x0000 }, + { 0xdd73, 0x0000 }, + { 0xdd74, 0x0000 }, + { 0xdd75, 0x0000 }, + { 0xdd76, 0x0000 }, + { 0xdd77, 0x0000 }, + { 0xdd78, 0x0000 }, + { 0xdd79, 0x0000 }, + { 0xdd7a, 0x0000 }, + { 0xdd7b, 0x0000 }, + { 0xdd7c, 0x0000 }, + { 0xdd7d, 0x0000 }, + { 0xdd7e, 0x0000 }, + { 0xdd7f, 0x0000 }, + { 0xdd80, 0x0000 }, + { 0xdd81, 0x0000 }, + { 0xdd82, 0x0000 }, + { 0xdd83, 0x0000 }, + { 0xdd84, 0x0000 }, + { 0xdd85, 0x0000 }, + { 0xdd86, 0x0000 }, + { 0xdd87, 0x0000 }, + { 0xdd88, 0x0000 }, + { 0xdd89, 0x0000 }, + { 0xdd8a, 0x0000 }, + { 0xdd8b, 0x0000 }, + { 0xdd8c, 0x0000 }, + { 0xdd8d, 0x0000 }, + { 0xdd8e, 0x0000 }, + { 0xdd8f, 0x0000 }, + { 0xdd90, 0x0000 }, + { 0xdd91, 0x0000 }, + { 0xdd92, 0x0000 }, + { 0xdd93, 0x0000 }, + { 0xdd94, 0x0000 }, + { 0xdd95, 0x0000 }, + { 0xdd96, 0x0000 }, + { 0xdd97, 0x0000 }, + { 0xdd98, 0x0000 }, + { 0xdd99, 0x0000 }, + { 0xdd9a, 0x0000 }, + { 0xdd9b, 0x0000 }, + { 0xdd9c, 0x0000 }, + { 0xdd9d, 0x0000 }, + { 0xdd9e, 0x0000 }, + { 0xdd9f, 0x0000 }, + { 0xdda0, 0x0000 }, + { 0xdda1, 0x0000 }, + { 0xdda2, 0x0000 }, + { 0xdda3, 0x0000 }, + { 0xdda4, 0x0000 }, + { 0xdda5, 0x0000 }, + { 0xdda6, 0x0000 }, + { 0xdda7, 0x0000 }, + { 0xdda8, 0x0000 }, + { 0xdda9, 0x0000 }, + { 0xddaa, 0x0000 }, + { 0xddab, 0x0000 }, + { 0xddac, 0x0000 }, + { 0xddad, 0x0000 }, + { 0xddae, 0x0000 }, + { 0xddaf, 0x0000 }, + { 0xddb0, 0x0000 }, + { 0xddb1, 0x0000 }, + { 0xddb2, 0x0000 }, + { 0xddb3, 0x0000 }, + { 0xddb4, 0x0000 }, + { 0xddb5, 0x0000 }, + { 0xddb6, 0x0000 }, + { 0xddb7, 0x0000 }, + { 0xddb8, 0x0000 }, + { 0xddb9, 0x0000 }, + { 0xddba, 0x0000 }, + { 0xddbb, 0x0000 }, + { 0xddbc, 0x0000 }, + { 0xddbd, 0x0000 }, + { 0xddbe, 0x0000 }, + { 0xddbf, 0x0000 }, + { 0xddc0, 0x0000 }, + { 0xddc1, 0x0000 }, + { 0xddc2, 0x0000 }, + { 0xddc3, 0x0000 }, + { 0xddc4, 0x0000 }, + { 0xddc5, 0x0000 }, + { 0xddc6, 0x0000 }, + { 0xddc7, 0x0000 }, + { 0xddc8, 0x0000 }, + { 0xddc9, 0x0000 }, + { 0xddca, 0x0000 }, + { 0xddcb, 0x0000 }, + { 0xddcc, 0x0000 }, + { 0xddcd, 0x0000 }, + { 0xddce, 0x0000 }, + { 0xddcf, 0x0000 }, + { 0xddd0, 0x0000 }, + { 0xddd1, 0x0000 }, + { 0xddd2, 0x0000 }, + { 0xddd3, 0x0000 }, + { 0xddd4, 0x0000 }, + { 0xddd5, 0x0000 }, + { 0xddd6, 0x0000 }, + { 0xddd7, 0x0000 }, + { 0xddd8, 0x0000 }, + { 0xddd9, 0x0000 }, + { 0xddda, 0x0000 }, + { 0xdddb, 0x0000 }, + { 0xdddc, 0x0000 }, + { 0xdddd, 0x0000 }, + { 0xddde, 0x0000 }, + { 0xdddf, 0x0000 }, + { 0xdde0, 0x0000 }, + { 0xdde1, 0x0000 }, + { 0xdde2, 0x0000 }, + { 0xdde3, 0x0000 }, + { 0xdde4, 0x0000 }, + { 0xdde5, 0x0000 }, + { 0xdde6, 0x0000 }, + { 0xdde7, 0x0000 }, + { 0xdde8, 0x0000 }, + { 0xdde9, 0x0000 }, + { 0xddea, 0x0000 }, + { 0xddeb, 0x0000 }, + { 0xddec, 0x0000 }, + { 0xdded, 0x0000 }, + { 0xddee, 0x0000 }, + { 0xddef, 0x0000 }, + { 0xddf0, 0x0000 }, + { 0xddf1, 0x0000 }, + { 0xddf2, 0x0000 }, + { 0xddf3, 0x0000 }, + { 0xddf4, 0x0000 }, + { 0xddf5, 0x0000 }, + { 0xddf6, 0x0000 }, + { 0xddf7, 0x0000 }, + { 0xddf8, 0x0000 }, + { 0xddf9, 0x0000 }, + { 0xddfa, 0x0000 }, + { 0xddfb, 0x0000 }, + { 0xddfc, 0x0000 }, + { 0xddfd, 0x0000 }, + { 0xddfe, 0x0000 }, + { 0xddff, 0x0000 }, + { 0xde00, 0x0000 }, + { 0xde01, 0x0000 }, + { 0xde02, 0x0000 }, + { 0xde03, 0x0000 }, + { 0xde04, 0x0000 }, + { 0xde05, 0x0000 }, + { 0xde06, 0x0000 }, + { 0xde07, 0x0000 }, + { 0xde08, 0x0000 }, + { 0xde09, 0x0000 }, + { 0xde0a, 0x0000 }, + { 0xde0b, 0x0000 }, + { 0xde0c, 0x0000 }, + { 0xde0d, 0x0000 }, + { 0xde0e, 0x0000 }, + { 0xde0f, 0x0000 }, + { 0xde10, 0x0000 }, + { 0xde11, 0x0000 }, + { 0xde12, 0x0000 }, + { 0xde13, 0x0000 }, + { 0xde14, 0x0000 }, + { 0xde15, 0x0000 }, + { 0xde16, 0x0000 }, + { 0xde17, 0x0000 }, + { 0xde18, 0x0000 }, + { 0xde19, 0x0000 }, + { 0xde1a, 0x0000 }, + { 0xde1b, 0x0000 }, + { 0xde1c, 0x0000 }, + { 0xde1d, 0x0000 }, + { 0xde1e, 0x0000 }, + { 0xde1f, 0x0000 }, + { 0xde20, 0x0000 }, + { 0xde21, 0x0000 }, + { 0xde22, 0x0000 }, + { 0xde23, 0x0000 }, + { 0xde24, 0x0000 }, + { 0xde25, 0x0000 }, + { 0xde26, 0x0000 }, + { 0xde27, 0x0000 }, + { 0xde28, 0x0000 }, + { 0xde29, 0x0000 }, + { 0xde2a, 0x0000 }, + { 0xde2b, 0x0000 }, + { 0xde2c, 0x0000 }, + { 0xde2d, 0x0000 }, + { 0xde2e, 0x0000 }, + { 0xde2f, 0x0000 }, + { 0xde30, 0x0000 }, + { 0xde31, 0x0000 }, + { 0xde32, 0x0000 }, + { 0xde33, 0x0000 }, + { 0xde34, 0x0000 }, + { 0xde35, 0x0000 }, + { 0xde36, 0x0000 }, + { 0xde37, 0x0000 }, + { 0xde38, 0x0000 }, + { 0xde39, 0x0000 }, + { 0xde3a, 0x0000 }, + { 0xde3b, 0x0000 }, + { 0xde3c, 0x0000 }, + { 0xde3d, 0x0000 }, + { 0xde3e, 0x0000 }, + { 0xde3f, 0x0000 }, + { 0xde40, 0x0000 }, + { 0xde41, 0x0000 }, + { 0xde42, 0x0000 }, + { 0xde43, 0x0000 }, + { 0xde44, 0x0000 }, + { 0xde45, 0x0000 }, + { 0xde46, 0x0000 }, + { 0xde47, 0x0000 }, + { 0xde48, 0x0000 }, + { 0xde49, 0x0000 }, + { 0xde4a, 0x0000 }, + { 0xde4b, 0x0000 }, + { 0xde4c, 0x0000 }, + { 0xde4d, 0x0000 }, + { 0xde4e, 0x0000 }, + { 0xde4f, 0x0000 }, + { 0xde50, 0x0000 }, + { 0xde51, 0x0000 }, + { 0xde52, 0x0000 }, + { 0xde53, 0x0000 }, + { 0xde54, 0x0000 }, + { 0xde55, 0x0000 }, + { 0xde56, 0x0000 }, + { 0xde57, 0x0000 }, + { 0xde58, 0x0000 }, + { 0xde59, 0x0000 }, + { 0xde5a, 0x0000 }, + { 0xde5b, 0x0000 }, + { 0xde5c, 0x0000 }, + { 0xde5d, 0x0000 }, + { 0xde5e, 0x0000 }, + { 0xde5f, 0x0000 }, + { 0xde60, 0x0000 }, + { 0xde61, 0x0000 }, + { 0xde62, 0x0000 }, + { 0xde63, 0x0000 }, + { 0xde64, 0x0000 }, + { 0xde65, 0x0000 }, + { 0xde66, 0x0000 }, + { 0xde67, 0x0000 }, + { 0xde68, 0x0000 }, + { 0xde69, 0x0000 }, + { 0xde6a, 0x0000 }, + { 0xde6b, 0x0000 }, + { 0xde6c, 0x0000 }, + { 0xde6d, 0x0000 }, + { 0xde6e, 0x0000 }, + { 0xde6f, 0x0000 }, + { 0xde70, 0x0000 }, + { 0xde71, 0x0000 }, + { 0xde72, 0x0000 }, + { 0xde73, 0x0000 }, + { 0xde74, 0x0000 }, + { 0xde75, 0x0000 }, + { 0xde76, 0x0000 }, + { 0xde77, 0x0000 }, + { 0xde78, 0x0000 }, + { 0xde79, 0x0000 }, + { 0xde7a, 0x0000 }, + { 0xde7b, 0x0000 }, + { 0xde7c, 0x0000 }, + { 0xde7d, 0x0000 }, + { 0xde7e, 0x0000 }, + { 0xde7f, 0x0000 }, + { 0xde80, 0x0000 }, + { 0xde81, 0x0000 }, + { 0xde82, 0x0000 }, + { 0xde83, 0x0000 }, + { 0xde84, 0x0000 }, + { 0xde85, 0x0000 }, + { 0xde86, 0x0000 }, + { 0xde87, 0x0000 }, + { 0xde88, 0x0000 }, + { 0xde89, 0x0000 }, + { 0xde8a, 0x0000 }, + { 0xde8b, 0x0000 }, + { 0xde8c, 0x0000 }, + { 0xde8d, 0x0000 }, + { 0xde8e, 0x0000 }, + { 0xde8f, 0x0000 }, + { 0xde90, 0x0000 }, + { 0xde91, 0x0000 }, + { 0xde92, 0x0000 }, + { 0xde93, 0x0000 }, + { 0xde94, 0x0000 }, + { 0xde95, 0x0000 }, + { 0xde96, 0x0000 }, + { 0xde97, 0x0000 }, + { 0xde98, 0x0000 }, + { 0xde99, 0x0000 }, + { 0xde9a, 0x0000 }, + { 0xde9b, 0x0000 }, + { 0xde9c, 0x0000 }, + { 0xde9d, 0x0000 }, + { 0xde9e, 0x0000 }, + { 0xde9f, 0x0000 }, + { 0xdea0, 0x0000 }, + { 0xdea1, 0x0000 }, + { 0xdea2, 0x0000 }, + { 0xdea3, 0x0000 }, + { 0xdea4, 0x0000 }, + { 0xdea5, 0x0000 }, + { 0xdea6, 0x0000 }, + { 0xdea7, 0x0000 }, + { 0xdea8, 0x0000 }, + { 0xdea9, 0x0000 }, + { 0xdeaa, 0x0000 }, + { 0xdeab, 0x0000 }, + { 0xdeac, 0x0000 }, + { 0xdead, 0x0000 }, + { 0xdeae, 0x0000 }, + { 0xdeaf, 0x0000 }, + { 0xdeb0, 0x0000 }, + { 0xdeb1, 0x0000 }, + { 0xdeb2, 0x0000 }, + { 0xdeb3, 0x0000 }, + { 0xdeb4, 0x0000 }, + { 0xdeb5, 0x0000 }, + { 0xdeb6, 0x0000 }, + { 0xdeb7, 0x0000 }, + { 0xdeb8, 0x0000 }, + { 0xdeb9, 0x0000 }, + { 0xdeba, 0x0000 }, + { 0xdebb, 0x0000 }, + { 0xdebc, 0x0000 }, + { 0xdebd, 0x0000 }, + { 0xdebe, 0x0000 }, + { 0xdebf, 0x0000 }, + { 0xdec0, 0x0000 }, + { 0xdec1, 0x0000 }, + { 0xdec2, 0x0000 }, + { 0xdec3, 0x0000 }, + { 0xdec4, 0x0000 }, + { 0xdec5, 0x0000 }, + { 0xdec6, 0x0000 }, + { 0xdec7, 0x0000 }, + { 0xdec8, 0x0000 }, + { 0xdec9, 0x0000 }, + { 0xdeca, 0x0000 }, + { 0xdecb, 0x0000 }, + { 0xdecc, 0x0000 }, + { 0xdecd, 0x0000 }, + { 0xdece, 0x0000 }, + { 0xdecf, 0x0000 }, + { 0xded0, 0x0000 }, + { 0xded1, 0x0000 }, + { 0xded2, 0x0000 }, + { 0xded3, 0x0000 }, + { 0xded4, 0x0000 }, + { 0xded5, 0x0000 }, + { 0xded6, 0x0000 }, + { 0xded7, 0x0000 }, + { 0xded8, 0x0000 }, + { 0xded9, 0x0000 }, + { 0xdeda, 0x0000 }, + { 0xdedb, 0x0000 }, + { 0xdedc, 0x0000 }, + { 0xdedd, 0x0000 }, + { 0xdede, 0x0000 }, + { 0xdedf, 0x0000 }, + { 0xdee0, 0x0000 }, + { 0xdee1, 0x0000 }, + { 0xdee2, 0x0000 }, + { 0xdee3, 0x0000 }, + { 0xdee4, 0x0000 }, + { 0xdee5, 0x0000 }, + { 0xdee6, 0x0000 }, + { 0xdee7, 0x0000 }, + { 0xdee8, 0x0000 }, + { 0xdee9, 0x0000 }, + { 0xdeea, 0x0000 }, + { 0xdeeb, 0x0000 }, + { 0xdeec, 0x0000 }, + { 0xdeed, 0x0000 }, + { 0xdeee, 0x0000 }, + { 0xdeef, 0x0000 }, + { 0xdef0, 0x0000 }, + { 0xdef1, 0x0000 }, + { 0xdef2, 0x0000 }, + { 0xdef3, 0x0000 }, + { 0xdef4, 0x0000 }, + { 0xdef5, 0x0000 }, + { 0xdef6, 0x0000 }, + { 0xdef7, 0x0000 }, + { 0xdef8, 0x0000 }, + { 0xdef9, 0x0000 }, + { 0xdefa, 0x0000 }, + { 0xdefb, 0x0000 }, + { 0xdefc, 0x0000 }, + { 0xdefd, 0x0000 }, + { 0xdefe, 0x0000 }, + { 0xdeff, 0x0000 }, + { 0xdf00, 0x0000 }, + { 0xdf01, 0x0000 }, + { 0xdf02, 0x0000 }, + { 0xdf03, 0x0000 }, + { 0xdf04, 0x0000 }, + { 0xdf05, 0x0000 }, + { 0xdf06, 0x0000 }, + { 0xdf07, 0x0000 }, + { 0xdf08, 0x0000 }, + { 0xdf09, 0x0000 }, + { 0xdf0a, 0x0000 }, + { 0xdf0b, 0x0000 }, + { 0xdf0c, 0x0000 }, + { 0xdf0d, 0x0000 }, + { 0xdf0e, 0x0000 }, + { 0xdf0f, 0x0000 }, + { 0xdf10, 0x0000 }, + { 0xdf11, 0x0000 }, + { 0xdf12, 0x0000 }, + { 0xdf13, 0x0000 }, + { 0xdf14, 0x0000 }, + { 0xdf15, 0x0000 }, + { 0xdf16, 0x0000 }, + { 0xdf17, 0x0000 }, + { 0xdf18, 0x0000 }, + { 0xdf19, 0x0000 }, + { 0xdf1a, 0x0000 }, + { 0xdf1b, 0x0000 }, + { 0xdf1c, 0x0000 }, + { 0xdf1d, 0x0000 }, + { 0xdf1e, 0x0000 }, + { 0xdf1f, 0x0000 }, + { 0xdf20, 0x0000 }, + { 0xdf21, 0x0000 }, + { 0xdf22, 0x0000 }, + { 0xdf23, 0x0000 }, + { 0xdf24, 0x0000 }, + { 0xdf25, 0x0000 }, + { 0xdf26, 0x0000 }, + { 0xdf27, 0x0000 }, + { 0xdf28, 0x0000 }, + { 0xdf29, 0x0000 }, + { 0xdf2a, 0x0000 }, + { 0xdf2b, 0x0000 }, + { 0xdf2c, 0x0000 }, + { 0xdf2d, 0x0000 }, + { 0xdf2e, 0x0000 }, + { 0xdf2f, 0x0000 }, + { 0xdf30, 0x0000 }, + { 0xdf31, 0x0000 }, + { 0xdf32, 0x0000 }, + { 0xdf33, 0x0000 }, + { 0xdf34, 0x0000 }, + { 0xdf35, 0x0000 }, + { 0xdf36, 0x0000 }, + { 0xdf37, 0x0000 }, + { 0xdf38, 0x0000 }, + { 0xdf39, 0x0000 }, + { 0xdf3a, 0x0000 }, + { 0xdf3b, 0x0000 }, + { 0xdf3c, 0x0000 }, + { 0xdf3d, 0x0000 }, + { 0xdf3e, 0x0000 }, + { 0xdf3f, 0x0000 }, + { 0xdf40, 0x0000 }, + { 0xdf41, 0x0000 }, + { 0xdf42, 0x0000 }, + { 0xdf43, 0x0000 }, + { 0xdf44, 0x0000 }, + { 0xdf45, 0x0000 }, + { 0xdf46, 0x0000 }, + { 0xdf47, 0x0000 }, + { 0xdf48, 0x0000 }, + { 0xdf49, 0x0000 }, + { 0xdf4a, 0x0000 }, + { 0xdf4b, 0x0000 }, + { 0xdf4c, 0x0000 }, + { 0xdf4d, 0x0000 }, + { 0xdf4e, 0x0000 }, + { 0xdf4f, 0x0000 }, + { 0xdf50, 0x0000 }, + { 0xdf51, 0x0000 }, + { 0xdf52, 0x0000 }, + { 0xdf53, 0x0000 }, + { 0xdf54, 0x0000 }, + { 0xdf55, 0x0000 }, + { 0xdf56, 0x0000 }, + { 0xdf57, 0x0000 }, + { 0xdf58, 0x0000 }, + { 0xdf59, 0x0000 }, + { 0xdf5a, 0x0000 }, + { 0xdf5b, 0x0000 }, + { 0xdf5c, 0x0000 }, + { 0xdf5d, 0x0000 }, + { 0xdf5e, 0x0000 }, + { 0xdf5f, 0x0000 }, + { 0xdf60, 0x0000 }, + { 0xdf61, 0x0000 }, + { 0xdf62, 0x0000 }, + { 0xdf63, 0x0000 }, + { 0xdf64, 0x0000 }, + { 0xdf65, 0x0000 }, + { 0xdf66, 0x0000 }, + { 0xdf67, 0x0000 }, + { 0xdf68, 0x0000 }, + { 0xdf69, 0x0000 }, + { 0xdf6a, 0x0000 }, + { 0xdf6b, 0x0000 }, + { 0xdf6c, 0x0000 }, + { 0xdf6d, 0x0000 }, + { 0xdf6e, 0x0000 }, + { 0xdf6f, 0x0000 }, + { 0xdf70, 0x0000 }, + { 0xdf71, 0x0000 }, + { 0xdf72, 0x0000 }, + { 0xdf73, 0x0000 }, + { 0xdf74, 0x0000 }, + { 0xdf75, 0x0000 }, + { 0xdf76, 0x0000 }, + { 0xdf77, 0x0000 }, + { 0xdf78, 0x0000 }, + { 0xdf79, 0x0000 }, + { 0xdf7a, 0x0000 }, + { 0xdf7b, 0x0000 }, + { 0xdf7c, 0x0000 }, + { 0xdf7d, 0x0000 }, + { 0xdf7e, 0x0000 }, + { 0xdf7f, 0x0000 }, + { 0xdf80, 0x0000 }, + { 0xdf81, 0x0000 }, + { 0xdf82, 0x0000 }, + { 0xdf83, 0x0000 }, + { 0xdf84, 0x0000 }, + { 0xdf85, 0x0000 }, + { 0xdf86, 0x0000 }, + { 0xdf87, 0x0000 }, + { 0xdf88, 0x0000 }, + { 0xdf89, 0x0000 }, + { 0xdf8a, 0x0000 }, + { 0xdf8b, 0x0000 }, + { 0xdf8c, 0x0000 }, + { 0xdf8d, 0x0000 }, + { 0xdf8e, 0x0000 }, + { 0xdf8f, 0x0000 }, + { 0xdf90, 0x0000 }, + { 0xdf91, 0x0000 }, + { 0xdf92, 0x0000 }, + { 0xdf93, 0x0000 }, + { 0xdf94, 0x0000 }, + { 0xdf95, 0x0000 }, + { 0xdf96, 0x0000 }, + { 0xdf97, 0x0000 }, + { 0xdf98, 0x0000 }, + { 0xdf99, 0x0000 }, + { 0xdf9a, 0x0000 }, + { 0xdf9b, 0x0000 }, + { 0xdf9c, 0x0000 }, + { 0xdf9d, 0x0000 }, + { 0xdf9e, 0x0000 }, + { 0xdf9f, 0x0000 }, + { 0xdfa0, 0x0000 }, + { 0xdfa1, 0x0000 }, + { 0xdfa2, 0x0000 }, + { 0xdfa3, 0x0000 }, + { 0xdfa4, 0x0000 }, + { 0xdfa5, 0x0000 }, + { 0xdfa6, 0x0000 }, + { 0xdfa7, 0x0000 }, + { 0xdfa8, 0x0000 }, + { 0xdfa9, 0x0000 }, + { 0xdfaa, 0x0000 }, + { 0xdfab, 0x0000 }, + { 0xdfac, 0x0000 }, + { 0xdfad, 0x0000 }, + { 0xdfae, 0x0000 }, + { 0xdfaf, 0x0000 }, + { 0xdfb0, 0x0000 }, + { 0xdfb1, 0x0000 }, + { 0xdfb2, 0x0000 }, + { 0xdfb3, 0x0000 }, + { 0xdfb4, 0x0000 }, + { 0xdfb5, 0x0000 }, + { 0xdfb6, 0x0000 }, + { 0xdfb7, 0x0000 }, + { 0xdfb8, 0x0000 }, + { 0xdfb9, 0x0000 }, + { 0xdfba, 0x0000 }, + { 0xdfbb, 0x0000 }, + { 0xdfbc, 0x0000 }, + { 0xdfbd, 0x0000 }, + { 0xdfbe, 0x0000 }, + { 0xdfbf, 0x0000 }, + { 0xdfc0, 0x0000 }, + { 0xdfc1, 0x0000 }, + { 0xdfc2, 0x0000 }, + { 0xdfc3, 0x0000 }, + { 0xdfc4, 0x0000 }, + { 0xdfc5, 0x0000 }, + { 0xdfc6, 0x0000 }, + { 0xdfc7, 0x0000 }, + { 0xdfc8, 0x0000 }, + { 0xdfc9, 0x0000 }, + { 0xdfca, 0x0000 }, + { 0xdfcb, 0x0000 }, + { 0xdfcc, 0x0000 }, + { 0xdfcd, 0x0000 }, + { 0xdfce, 0x0000 }, + { 0xdfcf, 0x0000 }, + { 0xdfd0, 0x0000 }, + { 0xdfd1, 0x0000 }, + { 0xdfd2, 0x0000 }, + { 0xdfd3, 0x0000 }, + { 0xdfd4, 0x0000 }, + { 0xdfd5, 0x0000 }, + { 0xdfd6, 0x0000 }, + { 0xdfd7, 0x0000 }, + { 0xdfd8, 0x0000 }, + { 0xdfd9, 0x0000 }, + { 0xdfda, 0x0000 }, + { 0xdfdb, 0x0000 }, + { 0xdfdc, 0x0000 }, + { 0xdfdd, 0x0000 }, + { 0xdfde, 0x0000 }, + { 0xdfdf, 0x0000 }, + { 0xdfe0, 0x0000 }, + { 0xdfe1, 0x0000 }, + { 0xdfe2, 0x0000 }, + { 0xdfe3, 0x0000 }, + { 0xdfe4, 0x0000 }, + { 0xdfe5, 0x0000 }, + { 0xdfe6, 0x0000 }, + { 0xdfe7, 0x0000 }, + { 0xdfe8, 0x0000 }, + { 0xdfe9, 0x0000 }, + { 0xdfea, 0x0000 }, + { 0xdfeb, 0x0000 }, + { 0xdfec, 0x0000 }, + { 0xdfed, 0x0000 }, + { 0xdfee, 0x0000 }, + { 0xdfef, 0x0000 }, + { 0xdff0, 0x0000 }, + { 0xdff1, 0x0000 }, + { 0xdff2, 0x0000 }, + { 0xdff3, 0x0000 }, + { 0xdff4, 0x0000 }, + { 0xdff5, 0x0000 }, + { 0xdff6, 0x0000 }, + { 0xdff7, 0x0000 }, + { 0xdff8, 0x0000 }, + { 0xdff9, 0x0000 }, + { 0xdffa, 0x0000 }, + { 0xdffb, 0x0000 }, + { 0xdffc, 0x0000 }, + { 0xdffd, 0x0000 }, + { 0xdffe, 0x0000 }, + { 0xdfff, 0x0000 }, + { 0xe000, 0x0000 }, + { 0xe001, 0x0000 }, + { 0xe002, 0x0000 }, + { 0xe003, 0x0000 }, + { 0xe004, 0x0000 }, + { 0xe005, 0x0000 }, + { 0xe006, 0x0000 }, + { 0xe007, 0x0000 }, + { 0xe008, 0x0000 }, + { 0xe009, 0x0000 }, + { 0xe00a, 0x0000 }, + { 0xe00b, 0x0000 }, + { 0xe00c, 0x0000 }, + { 0xe00d, 0x0000 }, + { 0xe00e, 0x0000 }, + { 0xe00f, 0x0000 }, + { 0xe010, 0x0000 }, + { 0xe011, 0x0000 }, + { 0xe012, 0x0000 }, + { 0xe013, 0x0000 }, + { 0xe014, 0x0000 }, + { 0xe015, 0x0000 }, + { 0xe016, 0x0000 }, + { 0xe017, 0x0000 }, + { 0xe018, 0x0000 }, + { 0xe019, 0x0000 }, + { 0xe01a, 0x0000 }, + { 0xe01b, 0x0000 }, + { 0xe01c, 0x0000 }, + { 0xe01d, 0x0000 }, + { 0xe01e, 0x0000 }, + { 0xe01f, 0x0000 }, + { 0xe020, 0x0000 }, + { 0xe021, 0x0000 }, + { 0xe022, 0x0000 }, + { 0xe023, 0x0000 }, + { 0xe024, 0x0000 }, + { 0xe025, 0x0000 }, + { 0xe026, 0x0000 }, + { 0xe027, 0x0000 }, + { 0xe028, 0x0000 }, + { 0xe029, 0x0000 }, + { 0xe02a, 0x0000 }, + { 0xe02b, 0x0000 }, + { 0xe02c, 0x0000 }, + { 0xe02d, 0x0000 }, + { 0xe02e, 0x0000 }, + { 0xe02f, 0x0000 }, + { 0xe030, 0x0000 }, + { 0xe031, 0x0000 }, + { 0xe032, 0x0000 }, + { 0xe033, 0x0000 }, + { 0xe034, 0x0000 }, + { 0xe035, 0x0000 }, + { 0xe036, 0x0000 }, + { 0xe037, 0x0000 }, + { 0xe038, 0x0000 }, + { 0xe039, 0x0000 }, + { 0xe03a, 0x0000 }, + { 0xe03b, 0x0000 }, + { 0xe03c, 0x0000 }, + { 0xe03d, 0x0000 }, + { 0xe03e, 0x0000 }, + { 0xe03f, 0x0000 }, + { 0xe040, 0x0000 }, + { 0xe041, 0x0000 }, + { 0xe042, 0x0000 }, + { 0xe043, 0x0000 }, + { 0xe044, 0x0000 }, + { 0xe045, 0x0000 }, + { 0xe046, 0x0000 }, + { 0xe047, 0x0000 }, + { 0xe048, 0x0000 }, + { 0xe049, 0x0000 }, + { 0xe04a, 0x0000 }, + { 0xe04b, 0x0000 }, + { 0xe04c, 0x0000 }, + { 0xe04d, 0x0000 }, + { 0xe04e, 0x0000 }, + { 0xe04f, 0x0000 }, + { 0xe050, 0x0000 }, + { 0xe051, 0x0000 }, + { 0xe052, 0x0000 }, + { 0xe053, 0x0000 }, + { 0xe054, 0x0000 }, + { 0xe055, 0x0000 }, + { 0xe056, 0x0000 }, + { 0xe057, 0x0000 }, + { 0xe058, 0x0000 }, + { 0xe059, 0x0000 }, + { 0xe05a, 0x0000 }, + { 0xe05b, 0x0000 }, + { 0xe05c, 0x0000 }, + { 0xe05d, 0x0000 }, + { 0xe05e, 0x0000 }, + { 0xe05f, 0x0000 }, + { 0xe060, 0x0000 }, + { 0xe061, 0x0000 }, + { 0xe062, 0x0000 }, + { 0xe063, 0x0000 }, + { 0xe064, 0x0000 }, + { 0xe065, 0x0000 }, + { 0xe066, 0x0000 }, + { 0xe067, 0x0000 }, + { 0xe068, 0x0000 }, + { 0xe069, 0x0000 }, + { 0xe06a, 0x0000 }, + { 0xe06b, 0x0000 }, + { 0xe06c, 0x0000 }, + { 0xe06d, 0x0000 }, + { 0xe06e, 0x0000 }, + { 0xe06f, 0x0000 }, + { 0xe070, 0x0000 }, + { 0xe071, 0x0000 }, + { 0xe072, 0x0000 }, + { 0xe073, 0x0000 }, + { 0xe074, 0x0000 }, + { 0xe075, 0x0000 }, + { 0xe076, 0x0000 }, + { 0xe077, 0x0000 }, + { 0xe078, 0x0000 }, + { 0xe079, 0x0000 }, + { 0xe07a, 0x0000 }, + { 0xe07b, 0x0000 }, + { 0xe07c, 0x0000 }, + { 0xe07d, 0x0000 }, + { 0xe07e, 0x0000 }, + { 0xe07f, 0x0000 }, + { 0xe080, 0x0000 }, + { 0xe081, 0x0000 }, + { 0xe082, 0x0000 }, + { 0xe083, 0x0000 }, + { 0xe084, 0x0000 }, + { 0xe085, 0x0000 }, + { 0xe086, 0x0000 }, + { 0xe087, 0x0000 }, + { 0xe088, 0x0000 }, + { 0xe089, 0x0000 }, + { 0xe08a, 0x0000 }, + { 0xe08b, 0x0000 }, + { 0xe08c, 0x0000 }, + { 0xe08d, 0x0000 }, + { 0xe08e, 0x0000 }, + { 0xe08f, 0x0000 }, + { 0xe090, 0x0000 }, + { 0xe091, 0x0000 }, + { 0xe092, 0x0000 }, + { 0xe093, 0x0000 }, + { 0xe094, 0x0000 }, + { 0xe095, 0x0000 }, + { 0xe096, 0x0000 }, + { 0xe097, 0x0000 }, + { 0xe098, 0x0000 }, + { 0xe099, 0x0000 }, + { 0xe09a, 0x0000 }, + { 0xe09b, 0x0000 }, + { 0xe09c, 0x0000 }, + { 0xe09d, 0x0000 }, + { 0xe09e, 0x0000 }, + { 0xe09f, 0x0000 }, + { 0xe0a0, 0x0000 }, + { 0xe0a1, 0x0000 }, + { 0xe0a2, 0x0000 }, + { 0xe0a3, 0x0000 }, + { 0xe0a4, 0x0000 }, + { 0xe0a5, 0x0000 }, + { 0xe0a6, 0x0000 }, + { 0xe0a7, 0x0000 }, + { 0xe0a8, 0x0000 }, + { 0xe0a9, 0x0000 }, + { 0xe0aa, 0x0000 }, + { 0xe0ab, 0x0000 }, + { 0xe0ac, 0x0000 }, + { 0xe0ad, 0x0000 }, + { 0xe0ae, 0x0000 }, + { 0xe0af, 0x0000 }, + { 0xe0b0, 0x0000 }, + { 0xe0b1, 0x0000 }, + { 0xe0b2, 0x0000 }, + { 0xe0b3, 0x0000 }, + { 0xe0b4, 0x0000 }, + { 0xe0b5, 0x0000 }, + { 0xe0b6, 0x0000 }, + { 0xe0b7, 0x0000 }, + { 0xe0b8, 0x0000 }, + { 0xe0b9, 0x0000 }, + { 0xe0ba, 0x0000 }, + { 0xe0bb, 0x0000 }, + { 0xe0bc, 0x0000 }, + { 0xe0bd, 0x0000 }, + { 0xe0be, 0x0000 }, + { 0xe0bf, 0x0000 }, + { 0xe0c0, 0x0000 }, + { 0xe0c1, 0x0000 }, + { 0xe0c2, 0x0000 }, + { 0xe0c3, 0x0000 }, + { 0xe0c4, 0x0000 }, + { 0xe0c5, 0x0000 }, + { 0xe0c6, 0x0000 }, + { 0xe0c7, 0x0000 }, + { 0xe0c8, 0x0000 }, + { 0xe0c9, 0x0000 }, + { 0xe0ca, 0x0000 }, + { 0xe0cb, 0x0000 }, + { 0xe0cc, 0x0000 }, + { 0xe0cd, 0x0000 }, + { 0xe0ce, 0x0000 }, + { 0xe0cf, 0x0000 }, + { 0xe0d0, 0x0000 }, + { 0xe0d1, 0x0000 }, + { 0xe0d2, 0x0000 }, + { 0xe0d3, 0x0000 }, + { 0xe0d4, 0x0000 }, + { 0xe0d5, 0x0000 }, + { 0xe0d6, 0x0000 }, + { 0xe0d7, 0x0000 }, + { 0xe0d8, 0x0000 }, + { 0xe0d9, 0x0000 }, + { 0xe0da, 0x0000 }, + { 0xe0db, 0x0000 }, + { 0xe0dc, 0x0000 }, + { 0xe0dd, 0x0000 }, + { 0xe0de, 0x0000 }, + { 0xe0df, 0x0000 }, + { 0xe0e0, 0x0000 }, + { 0xe0e1, 0x0000 }, + { 0xe0e2, 0x0000 }, + { 0xe0e3, 0x0000 }, + { 0xe0e4, 0x0000 }, + { 0xe0e5, 0x0000 }, + { 0xe0e6, 0x0000 }, + { 0xe0e7, 0x0000 }, + { 0xe0e8, 0x0000 }, + { 0xe0e9, 0x0000 }, + { 0xe0ea, 0x0000 }, + { 0xe0eb, 0x0000 }, + { 0xe0ec, 0x0000 }, + { 0xe0ed, 0x0000 }, + { 0xe0ee, 0x0000 }, + { 0xe0ef, 0x0000 }, + { 0xe0f0, 0x0000 }, + { 0xe0f1, 0x0000 }, + { 0xe0f2, 0x0000 }, + { 0xe0f3, 0x0000 }, + { 0xe0f4, 0x0000 }, + { 0xe0f5, 0x0000 }, + { 0xe0f6, 0x0000 }, + { 0xe0f7, 0x0000 }, + { 0xe0f8, 0x0000 }, + { 0xe0f9, 0x0000 }, + { 0xe0fa, 0x0000 }, + { 0xe0fb, 0x0000 }, + { 0xe0fc, 0x0000 }, + { 0xe0fd, 0x0000 }, + { 0xe0fe, 0x0000 }, + { 0xe0ff, 0x0000 }, + { 0xe100, 0x0000 }, + { 0xe101, 0x0000 }, + { 0xe102, 0x0000 }, + { 0xe103, 0x0000 }, + { 0xe104, 0x0000 }, + { 0xe105, 0x0000 }, + { 0xe106, 0x0000 }, + { 0xe107, 0x0000 }, + { 0xe108, 0x0000 }, + { 0xe109, 0x0000 }, + { 0xe10a, 0x0000 }, + { 0xe10b, 0x0000 }, + { 0xe10c, 0x0000 }, + { 0xe10d, 0x0000 }, + { 0xe10e, 0x0000 }, + { 0xe10f, 0x0000 }, + { 0xe110, 0x0000 }, + { 0xe111, 0x0000 }, + { 0xe112, 0x0000 }, + { 0xe113, 0x0000 }, + { 0xe114, 0x0000 }, + { 0xe115, 0x0000 }, + { 0xe116, 0x0000 }, + { 0xe117, 0x0000 }, + { 0xe118, 0x0000 }, + { 0xe119, 0x0000 }, + { 0xe11a, 0x0000 }, + { 0xe11b, 0x0000 }, + { 0xe11c, 0x0000 }, + { 0xe11d, 0x0000 }, + { 0xe11e, 0x0000 }, + { 0xe11f, 0x0000 }, + { 0xe120, 0x0000 }, + { 0xe121, 0x0000 }, + { 0xe122, 0x0000 }, + { 0xe123, 0x0000 }, + { 0xe124, 0x0000 }, + { 0xe125, 0x0000 }, + { 0xe126, 0x0000 }, + { 0xe127, 0x0000 }, + { 0xe128, 0x0000 }, + { 0xe129, 0x0000 }, + { 0xe12a, 0x0000 }, + { 0xe12b, 0x0000 }, + { 0xe12c, 0x0000 }, + { 0xe12d, 0x0000 }, + { 0xe12e, 0x0000 }, + { 0xe12f, 0x0000 }, + { 0xe130, 0x0000 }, + { 0xe131, 0x0000 }, + { 0xe132, 0x0000 }, + { 0xe133, 0x0000 }, + { 0xe134, 0x0000 }, + { 0xe135, 0x0000 }, + { 0xe136, 0x0000 }, + { 0xe137, 0x0000 }, + { 0xe138, 0x0000 }, + { 0xe139, 0x0000 }, + { 0xe13a, 0x0000 }, + { 0xe13b, 0x0000 }, + { 0xe13c, 0x0000 }, + { 0xe13d, 0x0000 }, + { 0xe13e, 0x0000 }, + { 0xe13f, 0x0000 }, + { 0xe140, 0x0000 }, + { 0xe141, 0x0000 }, + { 0xe142, 0x0000 }, + { 0xe143, 0x0000 }, + { 0xe144, 0x0000 }, + { 0xe145, 0x0000 }, + { 0xe146, 0x0000 }, + { 0xe147, 0x0000 }, + { 0xe148, 0x0000 }, + { 0xe149, 0x0000 }, + { 0xe14a, 0x0000 }, + { 0xe14b, 0x0000 }, + { 0xe14c, 0x0000 }, + { 0xe14d, 0x0000 }, + { 0xe14e, 0x0000 }, + { 0xe14f, 0x0000 }, + { 0xe150, 0x0000 }, + { 0xe151, 0x0000 }, + { 0xe152, 0x0000 }, + { 0xe153, 0x0000 }, + { 0xe154, 0x0000 }, + { 0xe155, 0x0000 }, + { 0xe156, 0x0000 }, + { 0xe157, 0x0000 }, + { 0xe158, 0x0000 }, + { 0xe159, 0x0000 }, + { 0xe15a, 0x0000 }, + { 0xe15b, 0x0000 }, + { 0xe15c, 0x0000 }, + { 0xe15d, 0x0000 }, + { 0xe15e, 0x0000 }, + { 0xe15f, 0x0000 }, + { 0xe160, 0x0000 }, + { 0xe161, 0x0000 }, + { 0xe162, 0x0000 }, + { 0xe163, 0x0000 }, + { 0xe164, 0x0000 }, + { 0xe165, 0x0000 }, + { 0xe166, 0x0000 }, + { 0xe167, 0x0000 }, + { 0xe168, 0x0000 }, + { 0xe169, 0x0000 }, + { 0xe16a, 0x0000 }, + { 0xe16b, 0x0000 }, + { 0xe16c, 0x0000 }, + { 0xe16d, 0x0000 }, + { 0xe16e, 0x0000 }, + { 0xe16f, 0x0000 }, + { 0xe170, 0x0000 }, + { 0xe171, 0x0000 }, + { 0xe172, 0x0000 }, + { 0xe173, 0x0000 }, + { 0xe174, 0x0000 }, + { 0xe175, 0x0000 }, + { 0xe176, 0x0000 }, + { 0xe177, 0x0000 }, + { 0xe178, 0x0000 }, + { 0xe179, 0x0000 }, + { 0xe17a, 0x0000 }, + { 0xe17b, 0x0000 }, + { 0xe17c, 0x0000 }, + { 0xe17d, 0x0000 }, + { 0xe17e, 0x0000 }, + { 0xe17f, 0x0000 }, + { 0xe180, 0x0000 }, + { 0xe181, 0x0000 }, + { 0xe182, 0x0000 }, + { 0xe183, 0x0000 }, + { 0xe184, 0x0000 }, + { 0xe185, 0x0000 }, + { 0xe186, 0x0000 }, + { 0xe187, 0x0000 }, + { 0xe188, 0x0000 }, + { 0xe189, 0x0000 }, + { 0xe18a, 0x0000 }, + { 0xe18b, 0x0000 }, + { 0xe18c, 0x0000 }, + { 0xe18d, 0x0000 }, + { 0xe18e, 0x0000 }, + { 0xe18f, 0x0000 }, + { 0xe190, 0x0000 }, + { 0xe191, 0x0000 }, + { 0xe192, 0x0000 }, + { 0xe193, 0x0000 }, + { 0xe194, 0x0000 }, + { 0xe195, 0x0000 }, + { 0xe196, 0x0000 }, + { 0xe197, 0x0000 }, + { 0xe198, 0x0000 }, + { 0xe199, 0x0000 }, + { 0xe19a, 0x0000 }, + { 0xe19b, 0x0000 }, + { 0xe19c, 0x0000 }, + { 0xe19d, 0x0000 }, + { 0xe19e, 0x0000 }, + { 0xe19f, 0x0000 }, + { 0xe1a0, 0x0000 }, + { 0xe1a1, 0x0000 }, + { 0xe1a2, 0x0000 }, + { 0xe1a3, 0x0000 }, + { 0xe1a4, 0x0000 }, + { 0xe1a5, 0x0000 }, + { 0xe1a6, 0x0000 }, + { 0xe1a7, 0x0000 }, + { 0xe1a8, 0x0000 }, + { 0xe1a9, 0x0000 }, + { 0xe1aa, 0x0000 }, + { 0xe1ab, 0x0000 }, + { 0xe1ac, 0x0000 }, + { 0xe1ad, 0x0000 }, + { 0xe1ae, 0x0000 }, + { 0xe1af, 0x0000 }, + { 0xe1b0, 0x0000 }, + { 0xe1b1, 0x0000 }, + { 0xe1b2, 0x0000 }, + { 0xe1b3, 0x0000 }, + { 0xe1b4, 0x0000 }, + { 0xe1b5, 0x0000 }, + { 0xe1b6, 0x0000 }, + { 0xe1b7, 0x0000 }, + { 0xe1b8, 0x0000 }, + { 0xe1b9, 0x0000 }, + { 0xe1ba, 0x0000 }, + { 0xe1bb, 0x0000 }, + { 0xe1bc, 0x0000 }, + { 0xe1bd, 0x0000 }, + { 0xe1be, 0x0000 }, + { 0xe1bf, 0x0000 }, + { 0xe1c0, 0x0000 }, + { 0xe1c1, 0x0000 }, + { 0xe1c2, 0x0000 }, + { 0xe1c3, 0x0000 }, + { 0xe1c4, 0x0000 }, + { 0xe1c5, 0x0000 }, + { 0xe1c6, 0x0000 }, + { 0xe1c7, 0x0000 }, + { 0xe1c8, 0x0000 }, + { 0xe1c9, 0x0000 }, + { 0xe1ca, 0x0000 }, + { 0xe1cb, 0x0000 }, + { 0xe1cc, 0x0000 }, + { 0xe1cd, 0x0000 }, + { 0xe1ce, 0x0000 }, + { 0xe1cf, 0x0000 }, + { 0xe1d0, 0x0000 }, + { 0xe1d1, 0x0000 }, + { 0xe1d2, 0x0000 }, + { 0xe1d3, 0x0000 }, + { 0xe1d4, 0x0000 }, + { 0xe1d5, 0x0000 }, + { 0xe1d6, 0x0000 }, + { 0xe1d7, 0x0000 }, + { 0xe1d8, 0x0000 }, + { 0xe1d9, 0x0000 }, + { 0xe1da, 0x0000 }, + { 0xe1db, 0x0000 }, + { 0xe1dc, 0x0000 }, + { 0xe1dd, 0x0000 }, + { 0xe1de, 0x0000 }, + { 0xe1df, 0x0000 }, + { 0xe1e0, 0x0000 }, + { 0xe1e1, 0x0000 }, + { 0xe1e2, 0x0000 }, + { 0xe1e3, 0x0000 }, + { 0xe1e4, 0x0000 }, + { 0xe1e5, 0x0000 }, + { 0xe1e6, 0x0000 }, + { 0xe1e7, 0x0000 }, + { 0xe1e8, 0x0000 }, + { 0xe1e9, 0x0000 }, + { 0xe1ea, 0x0000 }, + { 0xe1eb, 0x0000 }, + { 0xe1ec, 0x0000 }, + { 0xe1ed, 0x0000 }, + { 0xe1ee, 0x0000 }, + { 0xe1ef, 0x0000 }, + { 0xe1f0, 0x0000 }, + { 0xe1f1, 0x0000 }, + { 0xe1f2, 0x0000 }, + { 0xe1f3, 0x0000 }, + { 0xe1f4, 0x0000 }, + { 0xe1f5, 0x0000 }, + { 0xe1f6, 0x0000 }, + { 0xe1f7, 0x0000 }, + { 0xe1f8, 0x0000 }, + { 0xe1f9, 0x0000 }, + { 0xe1fa, 0x0000 }, + { 0xe1fb, 0x0000 }, + { 0xe1fc, 0x0000 }, + { 0xe1fd, 0x0000 }, + { 0xe1fe, 0x0000 }, + { 0xe1ff, 0x0000 }, + { 0xe200, 0x0000 }, + { 0xe201, 0x0000 }, + { 0xe202, 0x0000 }, + { 0xe203, 0x0000 }, + { 0xe204, 0x0000 }, + { 0xe205, 0x0000 }, + { 0xe206, 0x0000 }, + { 0xe207, 0x0000 }, + { 0xe208, 0x0000 }, + { 0xe209, 0x0000 }, + { 0xe20a, 0x0000 }, + { 0xe20b, 0x0000 }, + { 0xe20c, 0x0000 }, + { 0xe20d, 0x0000 }, + { 0xe20e, 0x0000 }, + { 0xe20f, 0x0000 }, + { 0xe210, 0x0000 }, + { 0xe211, 0x0000 }, + { 0xe212, 0x0000 }, + { 0xe213, 0x0000 }, + { 0xe214, 0x0000 }, + { 0xe215, 0x0000 }, + { 0xe216, 0x0000 }, + { 0xe217, 0x0000 }, + { 0xe218, 0x0000 }, + { 0xe219, 0x0000 }, + { 0xe21a, 0x0000 }, + { 0xe21b, 0x0000 }, + { 0xe21c, 0x0000 }, + { 0xe21d, 0x0000 }, + { 0xe21e, 0x0000 }, + { 0xe21f, 0x0000 }, + { 0xe220, 0x0000 }, + { 0xe221, 0x0000 }, + { 0xe222, 0x0000 }, + { 0xe223, 0x0000 }, + { 0xe224, 0x0000 }, + { 0xe225, 0x0000 }, + { 0xe226, 0x0000 }, + { 0xe227, 0x0000 }, + { 0xe228, 0x0000 }, + { 0xe229, 0x0000 }, + { 0xe22a, 0x0000 }, + { 0xe22b, 0x0000 }, + { 0xe22c, 0x0000 }, + { 0xe22d, 0x0000 }, + { 0xe22e, 0x0000 }, + { 0xe22f, 0x0000 }, + { 0xe230, 0x0000 }, + { 0xe231, 0x0000 }, + { 0xe232, 0x0000 }, + { 0xe233, 0x0000 }, + { 0xe234, 0x0000 }, + { 0xe235, 0x0000 }, + { 0xe236, 0x0000 }, + { 0xe237, 0x0000 }, + { 0xe238, 0x0000 }, + { 0xe239, 0x0000 }, + { 0xe23a, 0x0000 }, + { 0xe23b, 0x0000 }, + { 0xe23c, 0x0000 }, + { 0xe23d, 0x0000 }, + { 0xe23e, 0x0000 }, + { 0xe23f, 0x0000 }, + { 0xe240, 0x0000 }, + { 0xe241, 0x0000 }, + { 0xe242, 0x0000 }, + { 0xe243, 0x0000 }, + { 0xe244, 0x0000 }, + { 0xe245, 0x0000 }, + { 0xe246, 0x0000 }, + { 0xe247, 0x0000 }, + { 0xe248, 0x0000 }, + { 0xe249, 0x0000 }, + { 0xe24a, 0x0000 }, + { 0xe24b, 0x0000 }, + { 0xe24c, 0x0000 }, + { 0xe24d, 0x0000 }, + { 0xe24e, 0x0000 }, + { 0xe24f, 0x0000 }, + { 0xe250, 0x0000 }, + { 0xe251, 0x0000 }, + { 0xe252, 0x0000 }, + { 0xe253, 0x0000 }, + { 0xe254, 0x0000 }, + { 0xe255, 0x0000 }, + { 0xe256, 0x0000 }, + { 0xe257, 0x0000 }, + { 0xe258, 0x0000 }, + { 0xe259, 0x0000 }, + { 0xe25a, 0x0000 }, + { 0xe25b, 0x0000 }, + { 0xe25c, 0x0000 }, + { 0xe25d, 0x0000 }, + { 0xe25e, 0x0000 }, + { 0xe25f, 0x0000 }, + { 0xe260, 0x0000 }, + { 0xe261, 0x0000 }, + { 0xe262, 0x0000 }, + { 0xe263, 0x0000 }, + { 0xe264, 0x0000 }, + { 0xe265, 0x0000 }, + { 0xe266, 0x0000 }, + { 0xe267, 0x0000 }, + { 0xe268, 0x0000 }, + { 0xe269, 0x0000 }, + { 0xe26a, 0x0000 }, + { 0xe26b, 0x0000 }, + { 0xe26c, 0x0000 }, + { 0xe26d, 0x0000 }, + { 0xe26e, 0x0000 }, + { 0xe26f, 0x0000 }, + { 0xe270, 0x0000 }, + { 0xe271, 0x0000 }, + { 0xe272, 0x0000 }, + { 0xe273, 0x0000 }, + { 0xe274, 0x0000 }, + { 0xe275, 0x0000 }, + { 0xe276, 0x0000 }, + { 0xe277, 0x0000 }, + { 0xe278, 0x0000 }, + { 0xe279, 0x0000 }, + { 0xe27a, 0x0000 }, + { 0xe27b, 0x0000 }, + { 0xe27c, 0x0000 }, + { 0xe27d, 0x0000 }, + { 0xe27e, 0x0000 }, + { 0xe27f, 0x0000 }, + { 0xe280, 0x0000 }, + { 0xe281, 0x0000 }, + { 0xe282, 0x0000 }, + { 0xe283, 0x0000 }, + { 0xe284, 0x0000 }, + { 0xe285, 0x0000 }, + { 0xe286, 0x0000 }, + { 0xe287, 0x0000 }, + { 0xe288, 0x0000 }, + { 0xe289, 0x0000 }, + { 0xe28a, 0x0000 }, + { 0xe28b, 0x0000 }, + { 0xe28c, 0x0000 }, + { 0xe28d, 0x0000 }, + { 0xe28e, 0x0000 }, + { 0xe28f, 0x0000 }, + { 0xe290, 0x0000 }, + { 0xe291, 0x0000 }, + { 0xe292, 0x0000 }, + { 0xe293, 0x0000 }, + { 0xe294, 0x0000 }, + { 0xe295, 0x0000 }, + { 0xe296, 0x0000 }, + { 0xe297, 0x0000 }, + { 0xe298, 0x0000 }, + { 0xe299, 0x0000 }, + { 0xe29a, 0x0000 }, + { 0xe29b, 0x0000 }, + { 0xe29c, 0x0000 }, + { 0xe29d, 0x0000 }, + { 0xe29e, 0x0000 }, + { 0xe29f, 0x0000 }, + { 0xe2a0, 0x0000 }, + { 0xe2a1, 0x0000 }, + { 0xe2a2, 0x0000 }, + { 0xe2a3, 0x0000 }, + { 0xe2a4, 0x0000 }, + { 0xe2a5, 0x0000 }, + { 0xe2a6, 0x0000 }, + { 0xe2a7, 0x0000 }, + { 0xe2a8, 0x0000 }, + { 0xe2a9, 0x0000 }, + { 0xe2aa, 0x0000 }, + { 0xe2ab, 0x0000 }, + { 0xe2ac, 0x0000 }, + { 0xe2ad, 0x0000 }, + { 0xe2ae, 0x0000 }, + { 0xe2af, 0x0000 }, + { 0xe2b0, 0x0000 }, + { 0xe2b1, 0x0000 }, + { 0xe2b2, 0x0000 }, + { 0xe2b3, 0x0000 }, + { 0xe2b4, 0x0000 }, + { 0xe2b5, 0x0000 }, + { 0xe2b6, 0x0000 }, + { 0xe2b7, 0x0000 }, + { 0xe2b8, 0x0000 }, + { 0xe2b9, 0x0000 }, + { 0xe2ba, 0x0000 }, + { 0xe2bb, 0x0000 }, + { 0xe2bc, 0x0000 }, + { 0xe2bd, 0x0000 }, + { 0xe2be, 0x0000 }, + { 0xe2bf, 0x0000 }, + { 0xe2c0, 0x0000 }, + { 0xe2c1, 0x0000 }, + { 0xe2c2, 0x0000 }, + { 0xe2c3, 0x0000 }, + { 0xe2c4, 0x0000 }, + { 0xe2c5, 0x0000 }, + { 0xe2c6, 0x0000 }, + { 0xe2c7, 0x0000 }, + { 0xe2c8, 0x0000 }, + { 0xe2c9, 0x0000 }, + { 0xe2ca, 0x0000 }, + { 0xe2cb, 0x0000 }, + { 0xe2cc, 0x0000 }, + { 0xe2cd, 0x0000 }, + { 0xe2ce, 0x0000 }, + { 0xe2cf, 0x0000 }, + { 0xe2d0, 0x0000 }, + { 0xe2d1, 0x0000 }, + { 0xe2d2, 0x0000 }, + { 0xe2d3, 0x0000 }, + { 0xe2d4, 0x0000 }, + { 0xe2d5, 0x0000 }, + { 0xe2d6, 0x0000 }, + { 0xe2d7, 0x0000 }, + { 0xe2d8, 0x0000 }, + { 0xe2d9, 0x0000 }, + { 0xe2da, 0x0000 }, + { 0xe2db, 0x0000 }, + { 0xe2dc, 0x0000 }, + { 0xe2dd, 0x0000 }, + { 0xe2de, 0x0000 }, + { 0xe2df, 0x0000 }, + { 0xe2e0, 0x0000 }, + { 0xe2e1, 0x0000 }, + { 0xe2e2, 0x0000 }, + { 0xe2e3, 0x0000 }, + { 0xe2e4, 0x0000 }, + { 0xe2e5, 0x0000 }, + { 0xe2e6, 0x0000 }, + { 0xe2e7, 0x0000 }, + { 0xe2e8, 0x0000 }, + { 0xe2e9, 0x0000 }, + { 0xe2ea, 0x0000 }, + { 0xe2eb, 0x0000 }, + { 0xe2ec, 0x0000 }, + { 0xe2ed, 0x0000 }, + { 0xe2ee, 0x0000 }, + { 0xe2ef, 0x0000 }, + { 0xe2f0, 0x0000 }, + { 0xe2f1, 0x0000 }, + { 0xe2f2, 0x0000 }, + { 0xe2f3, 0x0000 }, + { 0xe2f4, 0x0000 }, + { 0xe2f5, 0x0000 }, + { 0xe2f6, 0x0000 }, + { 0xe2f7, 0x0000 }, + { 0xe2f8, 0x0000 }, + { 0xe2f9, 0x0000 }, + { 0xe2fa, 0x0000 }, + { 0xe2fb, 0x0000 }, + { 0xe2fc, 0x0000 }, + { 0xe2fd, 0x0000 }, + { 0xe2fe, 0x0000 }, + { 0xe2ff, 0x0000 }, + { 0xe300, 0x0000 }, + { 0xe301, 0x0000 }, + { 0xe302, 0x0000 }, + { 0xe303, 0x0000 }, + { 0xe304, 0x0000 }, + { 0xe305, 0x0000 }, + { 0xe306, 0x0000 }, + { 0xe307, 0x0000 }, + { 0xe308, 0x0000 }, + { 0xe309, 0x0000 }, + { 0xe30a, 0x0000 }, + { 0xe30b, 0x0000 }, + { 0xe30c, 0x0000 }, + { 0xe30d, 0x0000 }, + { 0xe30e, 0x0000 }, + { 0xe30f, 0x0000 }, + { 0xe310, 0x0000 }, + { 0xe311, 0x0000 }, + { 0xe312, 0x0000 }, + { 0xe313, 0x0000 }, + { 0xe314, 0x0000 }, + { 0xe315, 0x0000 }, + { 0xe316, 0x0000 }, + { 0xe317, 0x0000 }, + { 0xe318, 0x0000 }, + { 0xe319, 0x0000 }, + { 0xe31a, 0x0000 }, + { 0xe31b, 0x0000 }, + { 0xe31c, 0x0000 }, + { 0xe31d, 0x0000 }, + { 0xe31e, 0x0000 }, + { 0xe31f, 0x0000 }, + { 0xe320, 0x0000 }, + { 0xe321, 0x0000 }, + { 0xe322, 0x0000 }, + { 0xe323, 0x0000 }, + { 0xe324, 0x0000 }, + { 0xe325, 0x0000 }, + { 0xe326, 0x0000 }, + { 0xe327, 0x0000 }, + { 0xe328, 0x0000 }, + { 0xe329, 0x0000 }, + { 0xe32a, 0x0000 }, + { 0xe32b, 0x0000 }, + { 0xe32c, 0x0000 }, + { 0xe32d, 0x0000 }, + { 0xe32e, 0x0000 }, + { 0xe32f, 0x0000 }, + { 0xe330, 0x0000 }, + { 0xe331, 0x0000 }, + { 0xe332, 0x0000 }, + { 0xe333, 0x0000 }, + { 0xe334, 0x0000 }, + { 0xe335, 0x0000 }, + { 0xe336, 0x0000 }, + { 0xe337, 0x0000 }, + { 0xe338, 0x0000 }, + { 0xe339, 0x0000 }, + { 0xe33a, 0x0000 }, + { 0xe33b, 0x0000 }, + { 0xe33c, 0x0000 }, + { 0xe33d, 0x0000 }, + { 0xe33e, 0x0000 }, + { 0xe33f, 0x0000 }, + { 0xe340, 0x0000 }, + { 0xe341, 0x0000 }, + { 0xe342, 0x0000 }, + { 0xe343, 0x0000 }, + { 0xe344, 0x0000 }, + { 0xe345, 0x0000 }, + { 0xe346, 0x0000 }, + { 0xe347, 0x0000 }, + { 0xe348, 0x0000 }, + { 0xe349, 0x0000 }, + { 0xe34a, 0x0000 }, + { 0xe34b, 0x0000 }, + { 0xe34c, 0x0000 }, + { 0xe34d, 0x0000 }, + { 0xe34e, 0x0000 }, + { 0xe34f, 0x0000 }, + { 0xe350, 0x0000 }, + { 0xe351, 0x0000 }, + { 0xe352, 0x0000 }, + { 0xe353, 0x0000 }, + { 0xe354, 0x0000 }, + { 0xe355, 0x0000 }, + { 0xe356, 0x0000 }, + { 0xe357, 0x0000 }, + { 0xe358, 0x0000 }, + { 0xe359, 0x0000 }, + { 0xe35a, 0x0000 }, + { 0xe35b, 0x0000 }, + { 0xe35c, 0x0000 }, + { 0xe35d, 0x0000 }, + { 0xe35e, 0x0000 }, + { 0xe35f, 0x0000 }, + { 0xe360, 0x0000 }, + { 0xe361, 0x0000 }, + { 0xe362, 0x0000 }, + { 0xe363, 0x0000 }, + { 0xe364, 0x0000 }, + { 0xe365, 0x0000 }, + { 0xe366, 0x0000 }, + { 0xe367, 0x0000 }, + { 0xe368, 0x0000 }, + { 0xe369, 0x0000 }, + { 0xe36a, 0x0000 }, + { 0xe36b, 0x0000 }, + { 0xe36c, 0x0000 }, + { 0xe36d, 0x0000 }, + { 0xe36e, 0x0000 }, + { 0xe36f, 0x0000 }, + { 0xe370, 0x0000 }, + { 0xe371, 0x0000 }, + { 0xe372, 0x0000 }, + { 0xe373, 0x0000 }, + { 0xe374, 0x0000 }, + { 0xe375, 0x0000 }, + { 0xe376, 0x0000 }, + { 0xe377, 0x0000 }, + { 0xe378, 0x0000 }, + { 0xe379, 0x0000 }, + { 0xe37a, 0x0000 }, + { 0xe37b, 0x0000 }, + { 0xe37c, 0x0000 }, + { 0xe37d, 0x0000 }, + { 0xe37e, 0x0000 }, + { 0xe37f, 0x0000 }, + { 0xe380, 0x0000 }, + { 0xe381, 0x0000 }, + { 0xe382, 0x0000 }, + { 0xe383, 0x0000 }, + { 0xe384, 0x0000 }, + { 0xe385, 0x0000 }, + { 0xe386, 0x0000 }, + { 0xe387, 0x0000 }, + { 0xe388, 0x0000 }, + { 0xe389, 0x0000 }, + { 0xe38a, 0x0000 }, + { 0xe38b, 0x0000 }, + { 0xe38c, 0x0000 }, + { 0xe38d, 0x0000 }, + { 0xe38e, 0x0000 }, + { 0xe38f, 0x0000 }, + { 0xe390, 0x0000 }, + { 0xe391, 0x0000 }, + { 0xe392, 0x0000 }, + { 0xe393, 0x0000 }, + { 0xe394, 0x0000 }, + { 0xe395, 0x0000 }, + { 0xe396, 0x0000 }, + { 0xe397, 0x0000 }, + { 0xe398, 0x0000 }, + { 0xe399, 0x0000 }, + { 0xe39a, 0x0000 }, + { 0xe39b, 0x0000 }, + { 0xe39c, 0x0000 }, + { 0xe39d, 0x0000 }, + { 0xe39e, 0x0000 }, + { 0xe39f, 0x0000 }, + { 0xe3a0, 0x0000 }, + { 0xe3a1, 0x0000 }, + { 0xe3a2, 0x0000 }, + { 0xe3a3, 0x0000 }, + { 0xe3a4, 0x0000 }, + { 0xe3a5, 0x0000 }, + { 0xe3a6, 0x0000 }, + { 0xe3a7, 0x0000 }, + { 0xe3a8, 0x0000 }, + { 0xe3a9, 0x0000 }, + { 0xe3aa, 0x0000 }, + { 0xe3ab, 0x0000 }, + { 0xe3ac, 0x0000 }, + { 0xe3ad, 0x0000 }, + { 0xe3ae, 0x0000 }, + { 0xe3af, 0x0000 }, + { 0xe3b0, 0x0000 }, + { 0xe3b1, 0x0000 }, + { 0xe3b2, 0x0000 }, + { 0xe3b3, 0x0000 }, + { 0xe3b4, 0x0000 }, + { 0xe3b5, 0x0000 }, + { 0xe3b6, 0x0000 }, + { 0xe3b7, 0x0000 }, + { 0xe3b8, 0x0000 }, + { 0xe3b9, 0x0000 }, + { 0xe3ba, 0x0000 }, + { 0xe3bb, 0x0000 }, + { 0xe3bc, 0x0000 }, + { 0xe3bd, 0x0000 }, + { 0xe3be, 0x0000 }, + { 0xe3bf, 0x0000 }, + { 0xe3c0, 0x0000 }, + { 0xe3c1, 0x0000 }, + { 0xe3c2, 0x0000 }, + { 0xe3c3, 0x0000 }, + { 0xe3c4, 0x0000 }, + { 0xe3c5, 0x0000 }, + { 0xe3c6, 0x0000 }, + { 0xe3c7, 0x0000 }, + { 0xe3c8, 0x0000 }, + { 0xe3c9, 0x0000 }, + { 0xe3ca, 0x0000 }, + { 0xe3cb, 0x0000 }, + { 0xe3cc, 0x0000 }, + { 0xe3cd, 0x0000 }, + { 0xe3ce, 0x0000 }, + { 0xe3cf, 0x0000 }, + { 0xe3d0, 0x0000 }, + { 0xe3d1, 0x0000 }, + { 0xe3d2, 0x0000 }, + { 0xe3d3, 0x0000 }, + { 0xe3d4, 0x0000 }, + { 0xe3d5, 0x0000 }, + { 0xe3d6, 0x0000 }, + { 0xe3d7, 0x0000 }, + { 0xe3d8, 0x0000 }, + { 0xe3d9, 0x0000 }, + { 0xe3da, 0x0000 }, + { 0xe3db, 0x0000 }, + { 0xe3dc, 0x0000 }, + { 0xe3dd, 0x0000 }, + { 0xe3de, 0x0000 }, + { 0xe3df, 0x0000 }, + { 0xe3e0, 0x0000 }, + { 0xe3e1, 0x0000 }, + { 0xe3e2, 0x0000 }, + { 0xe3e3, 0x0000 }, + { 0xe3e4, 0x0000 }, + { 0xe3e5, 0x0000 }, + { 0xe3e6, 0x0000 }, + { 0xe3e7, 0x0000 }, + { 0xe3e8, 0x0000 }, + { 0xe3e9, 0x0000 }, + { 0xe3ea, 0x0000 }, + { 0xe3eb, 0x0000 }, + { 0xe3ec, 0x0000 }, + { 0xe3ed, 0x0000 }, + { 0xe3ee, 0x0000 }, + { 0xe3ef, 0x0000 }, + { 0xe3f0, 0x0000 }, + { 0xe3f1, 0x0000 }, + { 0xe3f2, 0x0000 }, + { 0xe3f3, 0x0000 }, + { 0xe3f4, 0x0000 }, + { 0xe3f5, 0x0000 }, + { 0xe3f6, 0x0000 }, + { 0xe3f7, 0x0000 }, + { 0xe3f8, 0x0000 }, + { 0xe3f9, 0x0000 }, + { 0xe3fa, 0x0000 }, + { 0xe3fb, 0x0000 }, + { 0xe3fc, 0x0000 }, + { 0xe3fd, 0x0000 }, + { 0xe3fe, 0x0000 }, + { 0xe3ff, 0x0000 }, + { 0xe400, 0x0000 }, + { 0xe401, 0x0000 }, + { 0xe402, 0x0000 }, + { 0xe403, 0x0000 }, + { 0xe404, 0x0000 }, + { 0xe405, 0x0000 }, + { 0xe406, 0x0000 }, + { 0xe407, 0x0000 }, + { 0xe408, 0x0000 }, + { 0xe409, 0x0000 }, + { 0xe40a, 0x0000 }, + { 0xe40b, 0x0000 }, + { 0xe40c, 0x0000 }, + { 0xe40d, 0x0000 }, + { 0xe40e, 0x0000 }, + { 0xe40f, 0x0000 }, + { 0xe410, 0x0000 }, + { 0xe411, 0x0000 }, + { 0xe412, 0x0000 }, + { 0xe413, 0x0000 }, + { 0xe414, 0x0000 }, + { 0xe415, 0x0000 }, + { 0xe416, 0x0000 }, + { 0xe417, 0x0000 }, + { 0xe418, 0x0000 }, + { 0xe419, 0x0000 }, + { 0xe41a, 0x0000 }, + { 0xe41b, 0x0000 }, + { 0xe41c, 0x0000 }, + { 0xe41d, 0x0000 }, + { 0xe41e, 0x0000 }, + { 0xe41f, 0x0000 }, + { 0xe420, 0x0000 }, + { 0xe421, 0x0000 }, + { 0xe422, 0x0000 }, + { 0xe423, 0x0000 }, + { 0xe424, 0x0000 }, + { 0xe425, 0x0000 }, + { 0xe426, 0x0000 }, + { 0xe427, 0x0000 }, + { 0xe428, 0x0000 }, + { 0xe429, 0x0000 }, + { 0xe42a, 0x0000 }, + { 0xe42b, 0x0000 }, + { 0xe42c, 0x0000 }, + { 0xe42d, 0x0000 }, + { 0xe42e, 0x0000 }, + { 0xe42f, 0x0000 }, + { 0xe430, 0x0000 }, + { 0xe431, 0x0000 }, + { 0xe432, 0x0000 }, + { 0xe433, 0x0000 }, + { 0xe434, 0x0000 }, + { 0xe435, 0x0000 }, + { 0xe436, 0x0000 }, + { 0xe437, 0x0000 }, + { 0xe438, 0x0000 }, + { 0xe439, 0x0000 }, + { 0xe43a, 0x0000 }, + { 0xe43b, 0x0000 }, + { 0xe43c, 0x0000 }, + { 0xe43d, 0x0000 }, + { 0xe43e, 0x0000 }, + { 0xe43f, 0x0000 }, + { 0xe440, 0x0000 }, + { 0xe441, 0x0000 }, + { 0xe442, 0x0000 }, + { 0xe443, 0x0000 }, + { 0xe444, 0x0000 }, + { 0xe445, 0x0000 }, + { 0xe446, 0x0000 }, + { 0xe447, 0x0000 }, + { 0xe448, 0x0000 }, + { 0xe449, 0x0000 }, + { 0xe44a, 0x0000 }, + { 0xe44b, 0x0000 }, + { 0xe44c, 0x0000 }, + { 0xe44d, 0x0000 }, + { 0xe44e, 0x0000 }, + { 0xe44f, 0x0000 }, + { 0xe450, 0x0000 }, + { 0xe451, 0x0000 }, + { 0xe452, 0x0000 }, + { 0xe453, 0x0000 }, + { 0xe454, 0x0000 }, + { 0xe455, 0x0000 }, + { 0xe456, 0x0000 }, + { 0xe457, 0x0000 }, + { 0xe458, 0x0000 }, + { 0xe459, 0x0000 }, + { 0xe45a, 0x0000 }, + { 0xe45b, 0x0000 }, + { 0xe45c, 0x0000 }, + { 0xe45d, 0x0000 }, + { 0xe45e, 0x0000 }, + { 0xe45f, 0x0000 }, + { 0xe460, 0x0000 }, + { 0xe461, 0x0000 }, + { 0xe462, 0x0000 }, + { 0xe463, 0x0000 }, + { 0xe464, 0x0000 }, + { 0xe465, 0x0000 }, + { 0xe466, 0x0000 }, + { 0xe467, 0x0000 }, + { 0xe468, 0x0000 }, + { 0xe469, 0x0000 }, + { 0xe46a, 0x0000 }, + { 0xe46b, 0x0000 }, + { 0xe46c, 0x0000 }, + { 0xe46d, 0x0000 }, + { 0xe46e, 0x0000 }, + { 0xe46f, 0x0000 }, + { 0xe470, 0x0000 }, + { 0xe471, 0x0000 }, + { 0xe472, 0x0000 }, + { 0xe473, 0x0000 }, + { 0xe474, 0x0000 }, + { 0xe475, 0x0000 }, + { 0xe476, 0x0000 }, + { 0xe477, 0x0000 }, + { 0xe478, 0x0000 }, + { 0xe479, 0x0000 }, + { 0xe47a, 0x0000 }, + { 0xe47b, 0x0000 }, + { 0xe47c, 0x0000 }, + { 0xe47d, 0x0000 }, + { 0xe47e, 0x0000 }, + { 0xe47f, 0x0000 }, + { 0xe480, 0x0000 }, + { 0xe481, 0x0000 }, + { 0xe482, 0x0000 }, + { 0xe483, 0x0000 }, + { 0xe484, 0x0000 }, + { 0xe485, 0x0000 }, + { 0xe486, 0x0000 }, + { 0xe487, 0x0000 }, + { 0xe488, 0x0000 }, + { 0xe489, 0x0000 }, + { 0xe48a, 0x0000 }, + { 0xe48b, 0x0000 }, + { 0xe48c, 0x0000 }, + { 0xe48d, 0x0000 }, + { 0xe48e, 0x0000 }, + { 0xe48f, 0x0000 }, + { 0xe490, 0x0000 }, + { 0xe491, 0x0000 }, + { 0xe492, 0x0000 }, + { 0xe493, 0x0000 }, + { 0xe494, 0x0000 }, + { 0xe495, 0x0000 }, + { 0xe496, 0x0000 }, + { 0xe497, 0x0000 }, + { 0xe498, 0x0000 }, + { 0xe499, 0x0000 }, + { 0xe49a, 0x0000 }, + { 0xe49b, 0x0000 }, + { 0xe49c, 0x0000 }, + { 0xe49d, 0x0000 }, + { 0xe49e, 0x0000 }, + { 0xe49f, 0x0000 }, + { 0xe4a0, 0x0000 }, + { 0xe4a1, 0x0000 }, + { 0xe4a2, 0x0000 }, + { 0xe4a3, 0x0000 }, + { 0xe4a4, 0x0000 }, + { 0xe4a5, 0x0000 }, + { 0xe4a6, 0x0000 }, + { 0xe4a7, 0x0000 }, + { 0xe4a8, 0x0000 }, + { 0xe4a9, 0x0000 }, + { 0xe4aa, 0x0000 }, + { 0xe4ab, 0x0000 }, + { 0xe4ac, 0x0000 }, + { 0xe4ad, 0x0000 }, + { 0xe4ae, 0x0000 }, + { 0xe4af, 0x0000 }, + { 0xe4b0, 0x0000 }, + { 0xe4b1, 0x0000 }, + { 0xe4b2, 0x0000 }, + { 0xe4b3, 0x0000 }, + { 0xe4b4, 0x0000 }, + { 0xe4b5, 0x0000 }, + { 0xe4b6, 0x0000 }, + { 0xe4b7, 0x0000 }, + { 0xe4b8, 0x0000 }, + { 0xe4b9, 0x0000 }, + { 0xe4ba, 0x0000 }, + { 0xe4bb, 0x0000 }, + { 0xe4bc, 0x0000 }, + { 0xe4bd, 0x0000 }, + { 0xe4be, 0x0000 }, + { 0xe4bf, 0x0000 }, + { 0xe4c0, 0x0000 }, + { 0xe4c1, 0x0000 }, + { 0xe4c2, 0x0000 }, + { 0xe4c3, 0x0000 }, + { 0xe4c4, 0x0000 }, + { 0xe4c5, 0x0000 }, + { 0xe4c6, 0x0000 }, + { 0xe4c7, 0x0000 }, + { 0xe4c8, 0x0000 }, + { 0xe4c9, 0x0000 }, + { 0xe4ca, 0x0000 }, + { 0xe4cb, 0x0000 }, + { 0xe4cc, 0x0000 }, + { 0xe4cd, 0x0000 }, + { 0xe4ce, 0x0000 }, + { 0xe4cf, 0x0000 }, + { 0xe4d0, 0x0000 }, + { 0xe4d1, 0x0000 }, + { 0xe4d2, 0x0000 }, + { 0xe4d3, 0x0000 }, + { 0xe4d4, 0x0000 }, + { 0xe4d5, 0x0000 }, + { 0xe4d6, 0x0000 }, + { 0xe4d7, 0x0000 }, + { 0xe4d8, 0x0000 }, + { 0xe4d9, 0x0000 }, + { 0xe4da, 0x0000 }, + { 0xe4db, 0x0000 }, + { 0xe4dc, 0x0000 }, + { 0xe4dd, 0x0000 }, + { 0xe4de, 0x0000 }, + { 0xe4df, 0x0000 }, + { 0xe4e0, 0x0000 }, + { 0xe4e1, 0x0000 }, + { 0xe4e2, 0x0000 }, + { 0xe4e3, 0x0000 }, + { 0xe4e4, 0x0000 }, + { 0xe4e5, 0x0000 }, + { 0xe4e6, 0x0000 }, + { 0xe4e7, 0x0000 }, + { 0xe4e8, 0x0000 }, + { 0xe4e9, 0x0000 }, + { 0xe4ea, 0x0000 }, + { 0xe4eb, 0x0000 }, + { 0xe4ec, 0x0000 }, + { 0xe4ed, 0x0000 }, + { 0xe4ee, 0x0000 }, + { 0xe4ef, 0x0000 }, + { 0xe4f0, 0x0000 }, + { 0xe4f1, 0x0000 }, + { 0xe4f2, 0x0000 }, + { 0xe4f3, 0x0000 }, + { 0xe4f4, 0x0000 }, + { 0xe4f5, 0x0000 }, + { 0xe4f6, 0x0000 }, + { 0xe4f7, 0x0000 }, + { 0xe4f8, 0x0000 }, + { 0xe4f9, 0x0000 }, + { 0xe4fa, 0x0000 }, + { 0xe4fb, 0x0000 }, + { 0xe4fc, 0x0000 }, + { 0xe4fd, 0x0000 }, + { 0xe4fe, 0x0000 }, + { 0xe4ff, 0x0000 }, + { 0xe500, 0x0000 }, + { 0xe501, 0x0000 }, + { 0xe502, 0x0000 }, + { 0xe503, 0x0000 }, + { 0xe504, 0x0000 }, + { 0xe505, 0x0000 }, + { 0xe506, 0x0000 }, + { 0xe507, 0x0000 }, + { 0xe508, 0x0000 }, + { 0xe509, 0x0000 }, + { 0xe50a, 0x0000 }, + { 0xe50b, 0x0000 }, + { 0xe50c, 0x0000 }, + { 0xe50d, 0x0000 }, + { 0xe50e, 0x0000 }, + { 0xe50f, 0x0000 }, + { 0xe510, 0x0000 }, + { 0xe511, 0x0000 }, + { 0xe512, 0x0000 }, + { 0xe513, 0x0000 }, + { 0xe514, 0x0000 }, + { 0xe515, 0x0000 }, + { 0xe516, 0x0000 }, + { 0xe517, 0x0000 }, + { 0xe518, 0x0000 }, + { 0xe519, 0x0000 }, + { 0xe51a, 0x0000 }, + { 0xe51b, 0x0000 }, + { 0xe51c, 0x0000 }, + { 0xe51d, 0x0000 }, + { 0xe51e, 0x0000 }, + { 0xe51f, 0x0000 }, + { 0xe520, 0x0000 }, + { 0xe521, 0x0000 }, + { 0xe522, 0x0000 }, + { 0xe523, 0x0000 }, + { 0xe524, 0x0000 }, + { 0xe525, 0x0000 }, + { 0xe526, 0x0000 }, + { 0xe527, 0x0000 }, + { 0xe528, 0x0000 }, + { 0xe529, 0x0000 }, + { 0xe52a, 0x0000 }, + { 0xe52b, 0x0000 }, + { 0xe52c, 0x0000 }, + { 0xe52d, 0x0000 }, + { 0xe52e, 0x0000 }, + { 0xe52f, 0x0000 }, + { 0xe530, 0x0000 }, + { 0xe531, 0x0000 }, + { 0xe532, 0x0000 }, + { 0xe533, 0x0000 }, + { 0xe534, 0x0000 }, + { 0xe535, 0x0000 }, + { 0xe536, 0x0000 }, + { 0xe537, 0x0000 }, + { 0xe538, 0x0000 }, + { 0xe539, 0x0000 }, + { 0xe53a, 0x0000 }, + { 0xe53b, 0x0000 }, + { 0xe53c, 0x0000 }, + { 0xe53d, 0x0000 }, + { 0xe53e, 0x0000 }, + { 0xe53f, 0x0000 }, + { 0xe540, 0x0000 }, + { 0xe541, 0x0000 }, + { 0xe542, 0x0000 }, + { 0xe543, 0x0000 }, + { 0xe544, 0x0000 }, + { 0xe545, 0x0000 }, + { 0xe546, 0x0000 }, + { 0xe547, 0x0000 }, + { 0xe548, 0x0000 }, + { 0xe549, 0x0000 }, + { 0xe54a, 0x0000 }, + { 0xe54b, 0x0000 }, + { 0xe54c, 0x0000 }, + { 0xe54d, 0x0000 }, + { 0xe54e, 0x0000 }, + { 0xe54f, 0x0000 }, + { 0xe550, 0x0000 }, + { 0xe551, 0x0000 }, + { 0xe552, 0x0000 }, + { 0xe553, 0x0000 }, + { 0xe554, 0x0000 }, + { 0xe555, 0x0000 }, + { 0xe556, 0x0000 }, + { 0xe557, 0x0000 }, + { 0xe558, 0x0000 }, + { 0xe559, 0x0000 }, + { 0xe55a, 0x0000 }, + { 0xe55b, 0x0000 }, + { 0xe55c, 0x0000 }, + { 0xe55d, 0x0000 }, + { 0xe55e, 0x0000 }, + { 0xe55f, 0x0000 }, + { 0xe560, 0x0000 }, + { 0xe561, 0x0000 }, + { 0xe562, 0x0000 }, + { 0xe563, 0x0000 }, + { 0xe564, 0x0000 }, + { 0xe565, 0x0000 }, + { 0xe566, 0x0000 }, + { 0xe567, 0x0000 }, + { 0xe568, 0x0000 }, + { 0xe569, 0x0000 }, + { 0xe56a, 0x0000 }, + { 0xe56b, 0x0000 }, + { 0xe56c, 0x0000 }, + { 0xe56d, 0x0000 }, + { 0xe56e, 0x0000 }, + { 0xe56f, 0x0000 }, + { 0xe570, 0x0000 }, + { 0xe571, 0x0000 }, + { 0xe572, 0x0000 }, + { 0xe573, 0x0000 }, + { 0xe574, 0x0000 }, + { 0xe575, 0x0000 }, + { 0xe576, 0x0000 }, + { 0xe577, 0x0000 }, + { 0xe578, 0x0000 }, + { 0xe579, 0x0000 }, + { 0xe57a, 0x0000 }, + { 0xe57b, 0x0000 }, + { 0xe57c, 0x0000 }, + { 0xe57d, 0x0000 }, + { 0xe57e, 0x0000 }, + { 0xe57f, 0x0000 }, + { 0xe580, 0x0000 }, + { 0xe581, 0x0000 }, + { 0xe582, 0x0000 }, + { 0xe583, 0x0000 }, + { 0xe584, 0x0000 }, + { 0xe585, 0x0000 }, + { 0xe586, 0x0000 }, + { 0xe587, 0x0000 }, + { 0xe588, 0x0000 }, + { 0xe589, 0x0000 }, + { 0xe58a, 0x0000 }, + { 0xe58b, 0x0000 }, + { 0xe58c, 0x0000 }, + { 0xe58d, 0x0000 }, + { 0xe58e, 0x0000 }, + { 0xe58f, 0x0000 }, + { 0xe590, 0x0000 }, + { 0xe591, 0x0000 }, + { 0xe592, 0x0000 }, + { 0xe593, 0x0000 }, + { 0xe594, 0x0000 }, + { 0xe595, 0x0000 }, + { 0xe596, 0x0000 }, + { 0xe597, 0x0000 }, + { 0xe598, 0x0000 }, + { 0xe599, 0x0000 }, + { 0xe59a, 0x0000 }, + { 0xe59b, 0x0000 }, + { 0xe59c, 0x0000 }, + { 0xe59d, 0x0000 }, + { 0xe59e, 0x0000 }, + { 0xe59f, 0x0000 }, + { 0xe5a0, 0x0000 }, + { 0xe5a1, 0x0000 }, + { 0xe5a2, 0x0000 }, + { 0xe5a3, 0x0000 }, + { 0xe5a4, 0x0000 }, + { 0xe5a5, 0x0000 }, + { 0xe5a6, 0x0000 }, + { 0xe5a7, 0x0000 }, + { 0xe5a8, 0x0000 }, + { 0xe5a9, 0x0000 }, + { 0xe5aa, 0x0000 }, + { 0xe5ab, 0x0000 }, + { 0xe5ac, 0x0000 }, + { 0xe5ad, 0x0000 }, + { 0xe5ae, 0x0000 }, + { 0xe5af, 0x0000 }, + { 0xe5b0, 0x0000 }, + { 0xe5b1, 0x0000 }, + { 0xe5b2, 0x0000 }, + { 0xe5b3, 0x0000 }, + { 0xe5b4, 0x0000 }, + { 0xe5b5, 0x0000 }, + { 0xe5b6, 0x0000 }, + { 0xe5b7, 0x0000 }, + { 0xe5b8, 0x0000 }, + { 0xe5b9, 0x0000 }, + { 0xe5ba, 0x0000 }, + { 0xe5bb, 0x0000 }, + { 0xe5bc, 0x0000 }, + { 0xe5bd, 0x0000 }, + { 0xe5be, 0x0000 }, + { 0xe5bf, 0x0000 }, + { 0xe5c0, 0x0000 }, + { 0xe5c1, 0x0000 }, + { 0xe5c2, 0x0000 }, + { 0xe5c3, 0x0000 }, + { 0xe5c4, 0x0000 }, + { 0xe5c5, 0x0000 }, + { 0xe5c6, 0x0000 }, + { 0xe5c7, 0x0000 }, + { 0xe5c8, 0x0000 }, + { 0xe5c9, 0x0000 }, + { 0xe5ca, 0x0000 }, + { 0xe5cb, 0x0000 }, + { 0xe5cc, 0x0000 }, + { 0xe5cd, 0x0000 }, + { 0xe5ce, 0x0000 }, + { 0xe5cf, 0x0000 }, + { 0xe5d0, 0x0000 }, + { 0xe5d1, 0x0000 }, + { 0xe5d2, 0x0000 }, + { 0xe5d3, 0x0000 }, + { 0xe5d4, 0x0000 }, + { 0xe5d5, 0x0000 }, + { 0xe5d6, 0x0000 }, + { 0xe5d7, 0x0000 }, + { 0xe5d8, 0x0000 }, + { 0xe5d9, 0x0000 }, + { 0xe5da, 0x0000 }, + { 0xe5db, 0x0000 }, + { 0xe5dc, 0x0000 }, + { 0xe5dd, 0x0000 }, + { 0xe5de, 0x0000 }, + { 0xe5df, 0x0000 }, + { 0xe5e0, 0x0000 }, + { 0xe5e1, 0x0000 }, + { 0xe5e2, 0x0000 }, + { 0xe5e3, 0x0000 }, + { 0xe5e4, 0x0000 }, + { 0xe5e5, 0x0000 }, + { 0xe5e6, 0x0000 }, + { 0xe5e7, 0x0000 }, + { 0xe5e8, 0x0000 }, + { 0xe5e9, 0x0000 }, + { 0xe5ea, 0x0000 }, + { 0xe5eb, 0x0000 }, + { 0xe5ec, 0x0000 }, + { 0xe5ed, 0x0000 }, + { 0xe5ee, 0x0000 }, + { 0xe5ef, 0x0000 }, + { 0xe5f0, 0x0000 }, + { 0xe5f1, 0x0000 }, + { 0xe5f2, 0x0000 }, + { 0xe5f3, 0x0000 }, + { 0xe5f4, 0x0000 }, + { 0xe5f5, 0x0000 }, + { 0xe5f6, 0x0000 }, + { 0xe5f7, 0x0000 }, + { 0xe5f8, 0x0000 }, + { 0xe5f9, 0x0000 }, + { 0xe5fa, 0x0000 }, + { 0xe5fb, 0x0000 }, + { 0xe5fc, 0x0000 }, + { 0xe5fd, 0x0000 }, + { 0xe5fe, 0x0000 }, + { 0xe5ff, 0x0000 }, + { 0xe600, 0x0000 }, + { 0xe601, 0x0000 }, + { 0xe602, 0x0000 }, + { 0xe603, 0x0000 }, + { 0xe604, 0x0000 }, + { 0xe605, 0x0000 }, + { 0xe606, 0x0000 }, + { 0xe607, 0x0000 }, + { 0xe608, 0x0000 }, + { 0xe609, 0x0000 }, + { 0xe60a, 0x0000 }, + { 0xe60b, 0x0000 }, + { 0xe60c, 0x0000 }, + { 0xe60d, 0x0000 }, + { 0xe60e, 0x0000 }, + { 0xe60f, 0x0000 }, + { 0xe610, 0x0000 }, + { 0xe611, 0x0000 }, + { 0xe612, 0x0000 }, + { 0xe613, 0x0000 }, + { 0xe614, 0x0000 }, + { 0xe615, 0x0000 }, + { 0xe616, 0x0000 }, + { 0xe617, 0x0000 }, + { 0xe618, 0x0000 }, + { 0xe619, 0x0000 }, + { 0xe61a, 0x0000 }, + { 0xe61b, 0x0000 }, + { 0xe61c, 0x0000 }, + { 0xe61d, 0x0000 }, + { 0xe61e, 0x0000 }, + { 0xe61f, 0x0000 }, + { 0xe620, 0x0000 }, + { 0xe621, 0x0000 }, + { 0xe622, 0x0000 }, + { 0xe623, 0x0000 }, + { 0xe624, 0x0000 }, + { 0xe625, 0x0000 }, + { 0xe626, 0x0000 }, + { 0xe627, 0x0000 }, + { 0xe628, 0x0000 }, + { 0xe629, 0x0000 }, + { 0xe62a, 0x0000 }, + { 0xe62b, 0x0000 }, + { 0xe62c, 0x0000 }, + { 0xe62d, 0x0000 }, + { 0xe62e, 0x0000 }, + { 0xe62f, 0x0000 }, + { 0xe630, 0x0000 }, + { 0xe631, 0x0000 }, + { 0xe632, 0x0000 }, + { 0xe633, 0x0000 }, + { 0xe634, 0x0000 }, + { 0xe635, 0x0000 }, + { 0xe636, 0x0000 }, + { 0xe637, 0x0000 }, + { 0xe638, 0x0000 }, + { 0xe639, 0x0000 }, + { 0xe63a, 0x0000 }, + { 0xe63b, 0x0000 }, + { 0xe63c, 0x0000 }, + { 0xe63d, 0x0000 }, + { 0xe63e, 0x0000 }, + { 0xe63f, 0x0000 }, + { 0xe640, 0x0000 }, + { 0xe641, 0x0000 }, + { 0xe642, 0x0000 }, + { 0xe643, 0x0000 }, + { 0xe644, 0x0000 }, + { 0xe645, 0x0000 }, + { 0xe646, 0x0000 }, + { 0xe647, 0x0000 }, + { 0xe648, 0x0000 }, + { 0xe649, 0x0000 }, + { 0xe64a, 0x0000 }, + { 0xe64b, 0x0000 }, + { 0xe64c, 0x0000 }, + { 0xe64d, 0x0000 }, + { 0xe64e, 0x0000 }, + { 0xe64f, 0x0000 }, + { 0xe650, 0x0000 }, + { 0xe651, 0x0000 }, + { 0xe652, 0x0000 }, + { 0xe653, 0x0000 }, + { 0xe654, 0x0000 }, + { 0xe655, 0x0000 }, + { 0xe656, 0x0000 }, + { 0xe657, 0x0000 }, + { 0xe658, 0x0000 }, + { 0xe659, 0x0000 }, + { 0xe65a, 0x0000 }, + { 0xe65b, 0x0000 }, + { 0xe65c, 0x0000 }, + { 0xe65d, 0x0000 }, + { 0xe65e, 0x0000 }, + { 0xe65f, 0x0000 }, + { 0xe660, 0x0000 }, + { 0xe661, 0x0000 }, + { 0xe662, 0x0000 }, + { 0xe663, 0x0000 }, + { 0xe664, 0x0000 }, + { 0xe665, 0x0000 }, + { 0xe666, 0x0000 }, + { 0xe667, 0x0000 }, + { 0xe668, 0x0000 }, + { 0xe669, 0x0000 }, + { 0xe66a, 0x0000 }, + { 0xe66b, 0x0000 }, + { 0xe66c, 0x0000 }, + { 0xe66d, 0x0000 }, + { 0xe66e, 0x0000 }, + { 0xe66f, 0x0000 }, + { 0xe670, 0x0000 }, + { 0xe671, 0x0000 }, + { 0xe672, 0x0000 }, + { 0xe673, 0x0000 }, + { 0xe674, 0x0000 }, + { 0xe675, 0x0000 }, + { 0xe676, 0x0000 }, + { 0xe677, 0x0000 }, + { 0xe678, 0x0000 }, + { 0xe679, 0x0000 }, + { 0xe67a, 0x0000 }, + { 0xe67b, 0x0000 }, + { 0xe67c, 0x0000 }, + { 0xe67d, 0x0000 }, + { 0xe67e, 0x0000 }, + { 0xe67f, 0x0000 }, + { 0xe680, 0x0000 }, + { 0xe681, 0x0000 }, + { 0xe682, 0x0000 }, + { 0xe683, 0x0000 }, + { 0xe684, 0x0000 }, + { 0xe685, 0x0000 }, + { 0xe686, 0x0000 }, + { 0xe687, 0x0000 }, + { 0xe688, 0x0000 }, + { 0xe689, 0x0000 }, + { 0xe68a, 0x0000 }, + { 0xe68b, 0x0000 }, + { 0xe68c, 0x0000 }, + { 0xe68d, 0x0000 }, + { 0xe68e, 0x0000 }, + { 0xe68f, 0x0000 }, + { 0xe690, 0x0000 }, + { 0xe691, 0x0000 }, + { 0xe692, 0x0000 }, + { 0xe693, 0x0000 }, + { 0xe694, 0x0000 }, + { 0xe695, 0x0000 }, + { 0xe696, 0x0000 }, + { 0xe697, 0x0000 }, + { 0xe698, 0x0000 }, + { 0xe699, 0x0000 }, + { 0xe69a, 0x0000 }, + { 0xe69b, 0x0000 }, + { 0xe69c, 0x0000 }, + { 0xe69d, 0x0000 }, + { 0xe69e, 0x0000 }, + { 0xe69f, 0x0000 }, + { 0xe6a0, 0x0000 }, + { 0xe6a1, 0x0000 }, + { 0xe6a2, 0x0000 }, + { 0xe6a3, 0x0000 }, + { 0xe6a4, 0x0000 }, + { 0xe6a5, 0x0000 }, + { 0xe6a6, 0x0000 }, + { 0xe6a7, 0x0000 }, + { 0xe6a8, 0x0000 }, + { 0xe6a9, 0x0000 }, + { 0xe6aa, 0x0000 }, + { 0xe6ab, 0x0000 }, + { 0xe6ac, 0x0000 }, + { 0xe6ad, 0x0000 }, + { 0xe6ae, 0x0000 }, + { 0xe6af, 0x0000 }, + { 0xe6b0, 0x0000 }, + { 0xe6b1, 0x0000 }, + { 0xe6b2, 0x0000 }, + { 0xe6b3, 0x0000 }, + { 0xe6b4, 0x0000 }, + { 0xe6b5, 0x0000 }, + { 0xe6b6, 0x0000 }, + { 0xe6b7, 0x0000 }, + { 0xe6b8, 0x0000 }, + { 0xe6b9, 0x0000 }, + { 0xe6ba, 0x0000 }, + { 0xe6bb, 0x0000 }, + { 0xe6bc, 0x0000 }, + { 0xe6bd, 0x0000 }, + { 0xe6be, 0x0000 }, + { 0xe6bf, 0x0000 }, + { 0xe6c0, 0x0000 }, + { 0xe6c1, 0x0000 }, + { 0xe6c2, 0x0000 }, + { 0xe6c3, 0x0000 }, + { 0xe6c4, 0x0000 }, + { 0xe6c5, 0x0000 }, + { 0xe6c6, 0x0000 }, + { 0xe6c7, 0x0000 }, + { 0xe6c8, 0x0000 }, + { 0xe6c9, 0x0000 }, + { 0xe6ca, 0x0000 }, + { 0xe6cb, 0x0000 }, + { 0xe6cc, 0x0000 }, + { 0xe6cd, 0x0000 }, + { 0xe6ce, 0x0000 }, + { 0xe6cf, 0x0000 }, + { 0xe6d0, 0x0000 }, + { 0xe6d1, 0x0000 }, + { 0xe6d2, 0x0000 }, + { 0xe6d3, 0x0000 }, + { 0xe6d4, 0x0000 }, + { 0xe6d5, 0x0000 }, + { 0xe6d6, 0x0000 }, + { 0xe6d7, 0x0000 }, + { 0xe6d8, 0x0000 }, + { 0xe6d9, 0x0000 }, + { 0xe6da, 0x0000 }, + { 0xe6db, 0x0000 }, + { 0xe6dc, 0x0000 }, + { 0xe6dd, 0x0000 }, + { 0xe6de, 0x0000 }, + { 0xe6df, 0x0000 }, + { 0xe6e0, 0x0000 }, + { 0xe6e1, 0x0000 }, + { 0xe6e2, 0x0000 }, + { 0xe6e3, 0x0000 }, + { 0xe6e4, 0x0000 }, + { 0xe6e5, 0x0000 }, + { 0xe6e6, 0x0000 }, + { 0xe6e7, 0x0000 }, + { 0xe6e8, 0x0000 }, + { 0xe6e9, 0x0000 }, + { 0xe6ea, 0x0000 }, + { 0xe6eb, 0x0000 }, + { 0xe6ec, 0x0000 }, + { 0xe6ed, 0x0000 }, + { 0xe6ee, 0x0000 }, + { 0xe6ef, 0x0000 }, + { 0xe6f0, 0x0000 }, + { 0xe6f1, 0x0000 }, + { 0xe6f2, 0x0000 }, + { 0xe6f3, 0x0000 }, + { 0xe6f4, 0x0000 }, + { 0xe6f5, 0x0000 }, + { 0xe6f6, 0x0000 }, + { 0xe6f7, 0x0000 }, + { 0xe6f8, 0x0000 }, + { 0xe6f9, 0x0000 }, + { 0xe6fa, 0x0000 }, + { 0xe6fb, 0x0000 }, + { 0xe6fc, 0x0000 }, + { 0xe6fd, 0x0000 }, + { 0xe6fe, 0x0000 }, + { 0xe6ff, 0x0000 }, + { 0xe700, 0x0000 }, + { 0xe701, 0x0000 }, + { 0xe702, 0x0000 }, + { 0xe703, 0x0000 }, + { 0xe704, 0x0000 }, + { 0xe705, 0x0000 }, + { 0xe706, 0x0000 }, + { 0xe707, 0x0000 }, + { 0xe708, 0x0000 }, + { 0xe709, 0x0000 }, + { 0xe70a, 0x0000 }, + { 0xe70b, 0x0000 }, + { 0xe70c, 0x0000 }, + { 0xe70d, 0x0000 }, + { 0xe70e, 0x0000 }, + { 0xe70f, 0x0000 }, + { 0xe710, 0x0000 }, + { 0xe711, 0x0000 }, + { 0xe712, 0x0000 }, + { 0xe713, 0x0000 }, + { 0xe714, 0x0000 }, + { 0xe715, 0x0000 }, + { 0xe716, 0x0000 }, + { 0xe717, 0x0000 }, + { 0xe718, 0x0000 }, + { 0xe719, 0x0000 }, + { 0xe71a, 0x0000 }, + { 0xe71b, 0x0000 }, + { 0xe71c, 0x0000 }, + { 0xe71d, 0x0000 }, + { 0xe71e, 0x0000 }, + { 0xe71f, 0x0000 }, + { 0xe720, 0x0000 }, + { 0xe721, 0x0000 }, + { 0xe722, 0x0000 }, + { 0xe723, 0x0000 }, + { 0xe724, 0x0000 }, + { 0xe725, 0x0000 }, + { 0xe726, 0x0000 }, + { 0xe727, 0x0000 }, + { 0xe728, 0x0000 }, + { 0xe729, 0x0000 }, + { 0xe72a, 0x0000 }, + { 0xe72b, 0x0000 }, + { 0xe72c, 0x0000 }, + { 0xe72d, 0x0000 }, + { 0xe72e, 0x0000 }, + { 0xe72f, 0x0000 }, + { 0xe730, 0x0000 }, + { 0xe731, 0x0000 }, + { 0xe732, 0x0000 }, + { 0xe733, 0x0000 }, + { 0xe734, 0x0000 }, + { 0xe735, 0x0000 }, + { 0xe736, 0x0000 }, + { 0xe737, 0x0000 }, + { 0xe738, 0x0000 }, + { 0xe739, 0x0000 }, + { 0xe73a, 0x0000 }, + { 0xe73b, 0x0000 }, + { 0xe73c, 0x0000 }, + { 0xe73d, 0x0000 }, + { 0xe73e, 0x0000 }, + { 0xe73f, 0x0000 }, + { 0xe740, 0x0000 }, + { 0xe741, 0x0000 }, + { 0xe742, 0x0000 }, + { 0xe743, 0x0000 }, + { 0xe744, 0x0000 }, + { 0xe745, 0x0000 }, + { 0xe746, 0x0000 }, + { 0xe747, 0x0000 }, + { 0xe748, 0x0000 }, + { 0xe749, 0x0000 }, + { 0xe74a, 0x0000 }, + { 0xe74b, 0x0000 }, + { 0xe74c, 0x0000 }, + { 0xe74d, 0x0000 }, + { 0xe74e, 0x0000 }, + { 0xe74f, 0x0000 }, + { 0xe750, 0x0000 }, + { 0xe751, 0x0000 }, + { 0xe752, 0x0000 }, + { 0xe753, 0x0000 }, + { 0xe754, 0x0000 }, + { 0xe755, 0x0000 }, + { 0xe756, 0x0000 }, + { 0xe757, 0x0000 }, + { 0xe758, 0x0000 }, + { 0xe759, 0x0000 }, + { 0xe75a, 0x0000 }, + { 0xe75b, 0x0000 }, + { 0xe75c, 0x0000 }, + { 0xe75d, 0x0000 }, + { 0xe75e, 0x0000 }, + { 0xe75f, 0x0000 }, + { 0xe760, 0x0000 }, + { 0xe761, 0x0000 }, + { 0xe762, 0x0000 }, + { 0xe763, 0x0000 }, + { 0xe764, 0x0000 }, + { 0xe765, 0x0000 }, + { 0xe766, 0x0000 }, + { 0xe767, 0x0000 }, + { 0xe768, 0x0000 }, + { 0xe769, 0x0000 }, + { 0xe76a, 0x0000 }, + { 0xe76b, 0x0000 }, + { 0xe76c, 0x0000 }, + { 0xe76d, 0x0000 }, + { 0xe76e, 0x0000 }, + { 0xe76f, 0x0000 }, + { 0xe770, 0x0000 }, + { 0xe771, 0x0000 }, + { 0xe772, 0x0000 }, + { 0xe773, 0x0000 }, + { 0xe774, 0x0000 }, + { 0xe775, 0x0000 }, + { 0xe776, 0x0000 }, + { 0xe777, 0x0000 }, + { 0xe778, 0x0000 }, + { 0xe779, 0x0000 }, + { 0xe77a, 0x0000 }, + { 0xe77b, 0x0000 }, + { 0xe77c, 0x0000 }, + { 0xe77d, 0x0000 }, + { 0xe77e, 0x0000 }, + { 0xe77f, 0x0000 }, + { 0xe780, 0x0000 }, + { 0xe781, 0x0000 }, + { 0xe782, 0x0000 }, + { 0xe783, 0x0000 }, + { 0xe784, 0x0000 }, + { 0xe785, 0x0000 }, + { 0xe786, 0x0000 }, + { 0xe787, 0x0000 }, + { 0xe788, 0x0000 }, + { 0xe789, 0x0000 }, + { 0xe78a, 0x0000 }, + { 0xe78b, 0x0000 }, + { 0xe78c, 0x0000 }, + { 0xe78d, 0x0000 }, + { 0xe78e, 0x0000 }, + { 0xe78f, 0x0000 }, + { 0xe790, 0x0000 }, + { 0xe791, 0x0000 }, + { 0xe792, 0x0000 }, + { 0xe793, 0x0000 }, + { 0xe794, 0x0000 }, + { 0xe795, 0x0000 }, + { 0xe796, 0x0000 }, + { 0xe797, 0x0000 }, + { 0xe798, 0x0000 }, + { 0xe799, 0x0000 }, + { 0xe79a, 0x0000 }, + { 0xe79b, 0x0000 }, + { 0xe79c, 0x0000 }, + { 0xe79d, 0x0000 }, + { 0xe79e, 0x0000 }, + { 0xe79f, 0x0000 }, + { 0xe7a0, 0x0000 }, + { 0xe7a1, 0x0000 }, + { 0xe7a2, 0x0000 }, + { 0xe7a3, 0x0000 }, + { 0xe7a4, 0x0000 }, + { 0xe7a5, 0x0000 }, + { 0xe7a6, 0x0000 }, + { 0xe7a7, 0x0000 }, + { 0xe7a8, 0x0000 }, + { 0xe7a9, 0x0000 }, + { 0xe7aa, 0x0000 }, + { 0xe7ab, 0x0000 }, + { 0xe7ac, 0x0000 }, + { 0xe7ad, 0x0000 }, + { 0xe7ae, 0x0000 }, + { 0xe7af, 0x0000 }, + { 0xe7b0, 0x0000 }, + { 0xe7b1, 0x0000 }, + { 0xe7b2, 0x0000 }, + { 0xe7b3, 0x0000 }, + { 0xe7b4, 0x0000 }, + { 0xe7b5, 0x0000 }, + { 0xe7b6, 0x0000 }, + { 0xe7b7, 0x0000 }, + { 0xe7b8, 0x0000 }, + { 0xe7b9, 0x0000 }, + { 0xe7ba, 0x0000 }, + { 0xe7bb, 0x0000 }, + { 0xe7bc, 0x0000 }, + { 0xe7bd, 0x0000 }, + { 0xe7be, 0x0000 }, + { 0xe7bf, 0x0000 }, + { 0xe7c0, 0x0000 }, + { 0xe7c1, 0x0000 }, + { 0xe7c2, 0x0000 }, + { 0xe7c3, 0x0000 }, + { 0xe7c4, 0x0000 }, + { 0xe7c5, 0x0000 }, + { 0xe7c6, 0x0000 }, + { 0xe7c7, 0x0000 }, + { 0xe7c8, 0x0000 }, + { 0xe7c9, 0x0000 }, + { 0xe7ca, 0x0000 }, + { 0xe7cb, 0x0000 }, + { 0xe7cc, 0x0000 }, + { 0xe7cd, 0x0000 }, + { 0xe7ce, 0x0000 }, + { 0xe7cf, 0x0000 }, + { 0xe7d0, 0x0000 }, + { 0xe7d1, 0x0000 }, + { 0xe7d2, 0x0000 }, + { 0xe7d3, 0x0000 }, + { 0xe7d4, 0x0000 }, + { 0xe7d5, 0x0000 }, + { 0xe7d6, 0x0000 }, + { 0xe7d7, 0x0000 }, + { 0xe7d8, 0x0000 }, + { 0xe7d9, 0x0000 }, + { 0xe7da, 0x0000 }, + { 0xe7db, 0x0000 }, + { 0xe7dc, 0x0000 }, + { 0xe7dd, 0x0000 }, + { 0xe7de, 0x0000 }, + { 0xe7df, 0x0000 }, + { 0xe7e0, 0x0000 }, + { 0xe7e1, 0x0000 }, + { 0xe7e2, 0x0000 }, + { 0xe7e3, 0x0000 }, + { 0xe7e4, 0x0000 }, + { 0xe7e5, 0x0000 }, + { 0xe7e6, 0x0000 }, + { 0xe7e7, 0x0000 }, + { 0xe7e8, 0x0000 }, + { 0xe7e9, 0x0000 }, + { 0xe7ea, 0x0000 }, + { 0xe7eb, 0x0000 }, + { 0xe7ec, 0x0000 }, + { 0xe7ed, 0x0000 }, + { 0xe7ee, 0x0000 }, + { 0xe7ef, 0x0000 }, + { 0xe7f0, 0x0000 }, + { 0xe7f1, 0x0000 }, + { 0xe7f2, 0x0000 }, + { 0xe7f3, 0x0000 }, + { 0xe7f4, 0x0000 }, + { 0xe7f5, 0x0000 }, + { 0xe7f6, 0x0000 }, + { 0xe7f7, 0x0000 }, + { 0xe7f8, 0x0000 }, + { 0xe7f9, 0x0000 }, + { 0xe7fa, 0x0000 }, + { 0xe7fb, 0x0000 }, + { 0xe7fc, 0x0000 }, + { 0xe7fd, 0x0000 }, + { 0xe7fe, 0x0000 }, + { 0xe7ff, 0x0000 }, + { 0xe800, 0x0000 }, + { 0xe801, 0x0000 }, + { 0xe802, 0x0000 }, + { 0xe803, 0x0000 }, + { 0xe804, 0x0000 }, + { 0xe805, 0x0000 }, + { 0xe806, 0x0000 }, + { 0xe807, 0x0000 }, + { 0xe808, 0x0000 }, + { 0xe809, 0x0000 }, + { 0xe80a, 0x0000 }, + { 0xe80b, 0x0000 }, + { 0xe80c, 0x0000 }, + { 0xe80d, 0x0000 }, + { 0xe80e, 0x0000 }, + { 0xe80f, 0x0000 }, + { 0xe810, 0x0000 }, + { 0xe811, 0x0000 }, + { 0xe812, 0x0000 }, + { 0xe813, 0x0000 }, + { 0xe814, 0x0000 }, + { 0xe815, 0x0000 }, + { 0xe816, 0x0000 }, + { 0xe817, 0x0000 }, + { 0xe818, 0x0000 }, + { 0xe819, 0x0000 }, + { 0xe81a, 0x0000 }, + { 0xe81b, 0x0000 }, + { 0xe81c, 0x0000 }, + { 0xe81d, 0x0000 }, + { 0xe81e, 0x0000 }, + { 0xe81f, 0x0000 }, + { 0xe820, 0x0000 }, + { 0xe821, 0x0000 }, + { 0xe822, 0x0000 }, + { 0xe823, 0x0000 }, + { 0xe824, 0x0000 }, + { 0xe825, 0x0000 }, + { 0xe826, 0x0000 }, + { 0xe827, 0x0000 }, + { 0xe828, 0x0000 }, + { 0xe829, 0x0000 }, + { 0xe82a, 0x0000 }, + { 0xe82b, 0x0000 }, + { 0xe82c, 0x0000 }, + { 0xe82d, 0x0000 }, + { 0xe82e, 0x0000 }, + { 0xe82f, 0x0000 }, + { 0xe830, 0x0000 }, + { 0xe831, 0x0000 }, + { 0xe832, 0x0000 }, + { 0xe833, 0x0000 }, + { 0xe834, 0x0000 }, + { 0xe835, 0x0000 }, + { 0xe836, 0x0000 }, + { 0xe837, 0x0000 }, + { 0xe838, 0x0000 }, + { 0xe839, 0x0000 }, + { 0xe83a, 0x0000 }, + { 0xe83b, 0x0000 }, + { 0xe83c, 0x0000 }, + { 0xe83d, 0x0000 }, + { 0xe83e, 0x0000 }, + { 0xe83f, 0x0000 }, + { 0xe840, 0x0000 }, + { 0xe841, 0x0000 }, + { 0xe842, 0x0000 }, + { 0xe843, 0x0000 }, + { 0xe844, 0x0000 }, + { 0xe845, 0x0000 }, + { 0xe846, 0x0000 }, + { 0xe847, 0x0000 }, + { 0xe848, 0x0000 }, + { 0xe849, 0x0000 }, + { 0xe84a, 0x0000 }, + { 0xe84b, 0x0000 }, + { 0xe84c, 0x0000 }, + { 0xe84d, 0x0000 }, + { 0xe84e, 0x0000 }, + { 0xe84f, 0x0000 }, + { 0xe850, 0x0000 }, + { 0xe851, 0x0000 }, + { 0xe852, 0x0000 }, + { 0xe853, 0x0000 }, + { 0xe854, 0x0000 }, + { 0xe855, 0x0000 }, + { 0xe856, 0x0000 }, + { 0xe857, 0x0000 }, + { 0xe858, 0x0000 }, + { 0xe859, 0x0000 }, + { 0xe85a, 0x0000 }, + { 0xe85b, 0x0000 }, + { 0xe85c, 0x0000 }, + { 0xe85d, 0x0000 }, + { 0xe85e, 0x0000 }, + { 0xe85f, 0x0000 }, + { 0xe860, 0x0000 }, + { 0xe861, 0x0000 }, + { 0xe862, 0x0000 }, + { 0xe863, 0x0000 }, + { 0xe864, 0x0000 }, + { 0xe865, 0x0000 }, + { 0xe866, 0x0000 }, + { 0xe867, 0x0000 }, + { 0xe868, 0x0000 }, + { 0xe869, 0x0000 }, + { 0xe86a, 0x0000 }, + { 0xe86b, 0x0000 }, + { 0xe86c, 0x0000 }, + { 0xe86d, 0x0000 }, + { 0xe86e, 0x0000 }, + { 0xe86f, 0x0000 }, + { 0xe870, 0x0000 }, + { 0xe871, 0x0000 }, + { 0xe872, 0x0000 }, + { 0xe873, 0x0000 }, + { 0xe874, 0x0000 }, + { 0xe875, 0x0000 }, + { 0xe876, 0x0000 }, + { 0xe877, 0x0000 }, + { 0xe878, 0x0000 }, + { 0xe879, 0x0000 }, + { 0xe87a, 0x0000 }, + { 0xe87b, 0x0000 }, + { 0xe87c, 0x0000 }, + { 0xe87d, 0x0000 }, + { 0xe87e, 0x0000 }, + { 0xe87f, 0x0000 }, + { 0xe880, 0x0000 }, + { 0xe881, 0x0000 }, + { 0xe882, 0x0000 }, + { 0xe883, 0x0000 }, + { 0xe884, 0x0000 }, + { 0xe885, 0x0000 }, + { 0xe886, 0x0000 }, + { 0xe887, 0x0000 }, + { 0xe888, 0x0000 }, + { 0xe889, 0x0000 }, + { 0xe88a, 0x0000 }, + { 0xe88b, 0x0000 }, + { 0xe88c, 0x0000 }, + { 0xe88d, 0x0000 }, + { 0xe88e, 0x0000 }, + { 0xe88f, 0x0000 }, + { 0xe890, 0x0000 }, + { 0xe891, 0x0000 }, + { 0xe892, 0x0000 }, + { 0xe893, 0x0000 }, + { 0xe894, 0x0000 }, + { 0xe895, 0x0000 }, + { 0xe896, 0x0000 }, + { 0xe897, 0x0000 }, + { 0xe898, 0x0000 }, + { 0xe899, 0x0000 }, + { 0xe89a, 0x0000 }, + { 0xe89b, 0x0000 }, + { 0xe89c, 0x0000 }, + { 0xe89d, 0x0000 }, + { 0xe89e, 0x0000 }, + { 0xe89f, 0x0000 }, + { 0xe8a0, 0x0000 }, + { 0xe8a1, 0x0000 }, + { 0xe8a2, 0x0000 }, + { 0xe8a3, 0x0000 }, + { 0xe8a4, 0x0000 }, + { 0xe8a5, 0x0000 }, + { 0xe8a6, 0x0000 }, + { 0xe8a7, 0x0000 }, + { 0xe8a8, 0x0000 }, + { 0xe8a9, 0x0000 }, + { 0xe8aa, 0x0000 }, + { 0xe8ab, 0x0000 }, + { 0xe8ac, 0x0000 }, + { 0xe8ad, 0x0000 }, + { 0xe8ae, 0x0000 }, + { 0xe8af, 0x0000 }, + { 0xe8b0, 0x0000 }, + { 0xe8b1, 0x0000 }, + { 0xe8b2, 0x0000 }, + { 0xe8b3, 0x0000 }, + { 0xe8b4, 0x0000 }, + { 0xe8b5, 0x0000 }, + { 0xe8b6, 0x0000 }, + { 0xe8b7, 0x0000 }, + { 0xe8b8, 0x0000 }, + { 0xe8b9, 0x0000 }, + { 0xe8ba, 0x0000 }, + { 0xe8bb, 0x0000 }, + { 0xe8bc, 0x0000 }, + { 0xe8bd, 0x0000 }, + { 0xe8be, 0x0000 }, + { 0xe8bf, 0x0000 }, + { 0xe8c0, 0x0000 }, + { 0xe8c1, 0x0000 }, + { 0xe8c2, 0x0000 }, + { 0xe8c3, 0x0000 }, + { 0xe8c4, 0x0000 }, + { 0xe8c5, 0x0000 }, + { 0xe8c6, 0x0000 }, + { 0xe8c7, 0x0000 }, + { 0xe8c8, 0x0000 }, + { 0xe8c9, 0x0000 }, + { 0xe8ca, 0x0000 }, + { 0xe8cb, 0x0000 }, + { 0xe8cc, 0x0000 }, + { 0xe8cd, 0x0000 }, + { 0xe8ce, 0x0000 }, + { 0xe8cf, 0x0000 }, + { 0xe8d0, 0x0000 }, + { 0xe8d1, 0x0000 }, + { 0xe8d2, 0x0000 }, + { 0xe8d3, 0x0000 }, + { 0xe8d4, 0x0000 }, + { 0xe8d5, 0x0000 }, + { 0xe8d6, 0x0000 }, + { 0xe8d7, 0x0000 }, + { 0xe8d8, 0x0000 }, + { 0xe8d9, 0x0000 }, + { 0xe8da, 0x0000 }, + { 0xe8db, 0x0000 }, + { 0xe8dc, 0x0000 }, + { 0xe8dd, 0x0000 }, + { 0xe8de, 0x0000 }, + { 0xe8df, 0x0000 }, + { 0xe8e0, 0x0000 }, + { 0xe8e1, 0x0000 }, + { 0xe8e2, 0x0000 }, + { 0xe8e3, 0x0000 }, + { 0xe8e4, 0x0000 }, + { 0xe8e5, 0x0000 }, + { 0xe8e6, 0x0000 }, + { 0xe8e7, 0x0000 }, + { 0xe8e8, 0x0000 }, + { 0xe8e9, 0x0000 }, + { 0xe8ea, 0x0000 }, + { 0xe8eb, 0x0000 }, + { 0xe8ec, 0x0000 }, + { 0xe8ed, 0x0000 }, + { 0xe8ee, 0x0000 }, + { 0xe8ef, 0x0000 }, + { 0xe8f0, 0x0000 }, + { 0xe8f1, 0x0000 }, + { 0xe8f2, 0x0000 }, + { 0xe8f3, 0x0000 }, + { 0xe8f4, 0x0000 }, + { 0xe8f5, 0x0000 }, + { 0xe8f6, 0x0000 }, + { 0xe8f7, 0x0000 }, + { 0xe8f8, 0x0000 }, + { 0xe8f9, 0x0000 }, + { 0xe8fa, 0x0000 }, + { 0xe8fb, 0x0000 }, + { 0xe8fc, 0x0000 }, + { 0xe8fd, 0x0000 }, + { 0xe8fe, 0x0000 }, + { 0xe8ff, 0x0000 }, + { 0xe900, 0x0000 }, + { 0xe901, 0x0000 }, + { 0xe902, 0x0000 }, + { 0xe903, 0x0000 }, + { 0xe904, 0x0000 }, + { 0xe905, 0x0000 }, + { 0xe906, 0x0000 }, + { 0xe907, 0x0000 }, + { 0xe908, 0x0000 }, + { 0xe909, 0x0000 }, + { 0xe90a, 0x0000 }, + { 0xe90b, 0x0000 }, + { 0xe90c, 0x0000 }, + { 0xe90d, 0x0000 }, + { 0xe90e, 0x0000 }, + { 0xe90f, 0x0000 }, + { 0xe910, 0x0000 }, + { 0xe911, 0x0000 }, + { 0xe912, 0x0000 }, + { 0xe913, 0x0000 }, + { 0xe914, 0x0000 }, + { 0xe915, 0x0000 }, + { 0xe916, 0x0000 }, + { 0xe917, 0x0000 }, + { 0xe918, 0x0000 }, + { 0xe919, 0x0000 }, + { 0xe91a, 0x0000 }, + { 0xe91b, 0x0000 }, + { 0xe91c, 0x0000 }, + { 0xe91d, 0x0000 }, + { 0xe91e, 0x0000 }, + { 0xe91f, 0x0000 }, + { 0xe920, 0x0000 }, + { 0xe921, 0x0000 }, + { 0xe922, 0x0000 }, + { 0xe923, 0x0000 }, + { 0xe924, 0x0000 }, + { 0xe925, 0x0000 }, + { 0xe926, 0x0000 }, + { 0xe927, 0x0000 }, + { 0xe928, 0x0000 }, + { 0xe929, 0x0000 }, + { 0xe92a, 0x0000 }, + { 0xe92b, 0x0000 }, + { 0xe92c, 0x0000 }, + { 0xe92d, 0x0000 }, + { 0xe92e, 0x0000 }, + { 0xe92f, 0x0000 }, + { 0xe930, 0x0000 }, + { 0xe931, 0x0000 }, + { 0xe932, 0x0000 }, + { 0xe933, 0x0000 }, + { 0xe934, 0x0000 }, + { 0xe935, 0x0000 }, + { 0xe936, 0x0000 }, + { 0xe937, 0x0000 }, + { 0xe938, 0x0000 }, + { 0xe939, 0x0000 }, + { 0xe93a, 0x0000 }, + { 0xe93b, 0x0000 }, + { 0xe93c, 0x0000 }, + { 0xe93d, 0x0000 }, + { 0xe93e, 0x0000 }, + { 0xe93f, 0x0000 }, + { 0xe940, 0x0000 }, + { 0xe941, 0x0000 }, + { 0xe942, 0x0000 }, + { 0xe943, 0x0000 }, + { 0xe944, 0x0000 }, + { 0xe945, 0x0000 }, + { 0xe946, 0x0000 }, + { 0xe947, 0x0000 }, + { 0xe948, 0x0000 }, + { 0xe949, 0x0000 }, + { 0xe94a, 0x0000 }, + { 0xe94b, 0x0000 }, + { 0xe94c, 0x0000 }, + { 0xe94d, 0x0000 }, + { 0xe94e, 0x0000 }, + { 0xe94f, 0x0000 }, + { 0xe950, 0x0000 }, + { 0xe951, 0x0000 }, + { 0xe952, 0x0000 }, + { 0xe953, 0x0000 }, + { 0xe954, 0x0000 }, + { 0xe955, 0x0000 }, + { 0xe956, 0x0000 }, + { 0xe957, 0x0000 }, + { 0xe958, 0x0000 }, + { 0xe959, 0x0000 }, + { 0xe95a, 0x0000 }, + { 0xe95b, 0x0000 }, + { 0xe95c, 0x0000 }, + { 0xe95d, 0x0000 }, + { 0xe95e, 0x0000 }, + { 0xe95f, 0x0000 }, + { 0xe960, 0x0000 }, + { 0xe961, 0x0000 }, + { 0xe962, 0x0000 }, + { 0xe963, 0x0000 }, + { 0xe964, 0x0000 }, + { 0xe965, 0x0000 }, + { 0xe966, 0x0000 }, + { 0xe967, 0x0000 }, + { 0xe968, 0x0000 }, + { 0xe969, 0x0000 }, + { 0xe96a, 0x0000 }, + { 0xe96b, 0x0000 }, + { 0xe96c, 0x0000 }, + { 0xe96d, 0x0000 }, + { 0xe96e, 0x0000 }, + { 0xe96f, 0x0000 }, + { 0xe970, 0x0000 }, + { 0xe971, 0x0000 }, + { 0xe972, 0x0000 }, + { 0xe973, 0x0000 }, + { 0xe974, 0x0000 }, + { 0xe975, 0x0000 }, + { 0xe976, 0x0000 }, + { 0xe977, 0x0000 }, + { 0xe978, 0x0000 }, + { 0xe979, 0x0000 }, + { 0xe97a, 0x0000 }, + { 0xe97b, 0x0000 }, + { 0xe97c, 0x0000 }, + { 0xe97d, 0x0000 }, + { 0xe97e, 0x0000 }, + { 0xe97f, 0x0000 }, + { 0xe980, 0x0000 }, + { 0xe981, 0x0000 }, + { 0xe982, 0x0000 }, + { 0xe983, 0x0000 }, + { 0xe984, 0x0000 }, + { 0xe985, 0x0000 }, + { 0xe986, 0x0000 }, + { 0xe987, 0x0000 }, + { 0xe988, 0x0000 }, + { 0xe989, 0x0000 }, + { 0xe98a, 0x0000 }, + { 0xe98b, 0x0000 }, + { 0xe98c, 0x0000 }, + { 0xe98d, 0x0000 }, + { 0xe98e, 0x0000 }, + { 0xe98f, 0x0000 }, + { 0xe990, 0x0000 }, + { 0xe991, 0x0000 }, + { 0xe992, 0x0000 }, + { 0xe993, 0x0000 }, + { 0xe994, 0x0000 }, + { 0xe995, 0x0000 }, + { 0xe996, 0x0000 }, + { 0xe997, 0x0000 }, + { 0xe998, 0x0000 }, + { 0xe999, 0x0000 }, + { 0xe99a, 0x0000 }, + { 0xe99b, 0x0000 }, + { 0xe99c, 0x0000 }, + { 0xe99d, 0x0000 }, + { 0xe99e, 0x0000 }, + { 0xe99f, 0x0000 }, + { 0xe9a0, 0x0000 }, + { 0xe9a1, 0x0000 }, + { 0xe9a2, 0x0000 }, + { 0xe9a3, 0x0000 }, + { 0xe9a4, 0x0000 }, + { 0xe9a5, 0x0000 }, + { 0xe9a6, 0x0000 }, + { 0xe9a7, 0x0000 }, + { 0xe9a8, 0x0000 }, + { 0xe9a9, 0x0000 }, + { 0xe9aa, 0x0000 }, + { 0xe9ab, 0x0000 }, + { 0xe9ac, 0x0000 }, + { 0xe9ad, 0x0000 }, + { 0xe9ae, 0x0000 }, + { 0xe9af, 0x0000 }, + { 0xe9b0, 0x0000 }, + { 0xe9b1, 0x0000 }, + { 0xe9b2, 0x0000 }, + { 0xe9b3, 0x0000 }, + { 0xe9b4, 0x0000 }, + { 0xe9b5, 0x0000 }, + { 0xe9b6, 0x0000 }, + { 0xe9b7, 0x0000 }, + { 0xe9b8, 0x0000 }, + { 0xe9b9, 0x0000 }, + { 0xe9ba, 0x0000 }, + { 0xe9bb, 0x0000 }, + { 0xe9bc, 0x0000 }, + { 0xe9bd, 0x0000 }, + { 0xe9be, 0x0000 }, + { 0xe9bf, 0x0000 }, + { 0xe9c0, 0x0000 }, + { 0xe9c1, 0x0000 }, + { 0xe9c2, 0x0000 }, + { 0xe9c3, 0x0000 }, + { 0xe9c4, 0x0000 }, + { 0xe9c5, 0x0000 }, + { 0xe9c6, 0x0000 }, + { 0xe9c7, 0x0000 }, + { 0xe9c8, 0x0000 }, + { 0xe9c9, 0x0000 }, + { 0xe9ca, 0x0000 }, + { 0xe9cb, 0x0000 }, + { 0xe9cc, 0x0000 }, + { 0xe9cd, 0x0000 }, + { 0xe9ce, 0x0000 }, + { 0xe9cf, 0x0000 }, + { 0xe9d0, 0x0000 }, + { 0xe9d1, 0x0000 }, + { 0xe9d2, 0x0000 }, + { 0xe9d3, 0x0000 }, + { 0xe9d4, 0x0000 }, + { 0xe9d5, 0x0000 }, + { 0xe9d6, 0x0000 }, + { 0xe9d7, 0x0000 }, + { 0xe9d8, 0x0000 }, + { 0xe9d9, 0x0000 }, + { 0xe9da, 0x0000 }, + { 0xe9db, 0x0000 }, + { 0xe9dc, 0x0000 }, + { 0xe9dd, 0x0000 }, + { 0xe9de, 0x0000 }, + { 0xe9df, 0x0000 }, + { 0xe9e0, 0x0000 }, + { 0xe9e1, 0x0000 }, + { 0xe9e2, 0x0000 }, + { 0xe9e3, 0x0000 }, + { 0xe9e4, 0x0000 }, + { 0xe9e5, 0x0000 }, + { 0xe9e6, 0x0000 }, + { 0xe9e7, 0x0000 }, + { 0xe9e8, 0x0000 }, + { 0xe9e9, 0x0000 }, + { 0xe9ea, 0x0000 }, + { 0xe9eb, 0x0000 }, + { 0xe9ec, 0x0000 }, + { 0xe9ed, 0x0000 }, + { 0xe9ee, 0x0000 }, + { 0xe9ef, 0x0000 }, + { 0xe9f0, 0x0000 }, + { 0xe9f1, 0x0000 }, + { 0xe9f2, 0x0000 }, + { 0xe9f3, 0x0000 }, + { 0xe9f4, 0x0000 }, + { 0xe9f5, 0x0000 }, + { 0xe9f6, 0x0000 }, + { 0xe9f7, 0x0000 }, + { 0xe9f8, 0x0000 }, + { 0xe9f9, 0x0000 }, + { 0xe9fa, 0x0000 }, + { 0xe9fb, 0x0000 }, + { 0xe9fc, 0x0000 }, + { 0xe9fd, 0x0000 }, + { 0xe9fe, 0x0000 }, + { 0xe9ff, 0x0000 }, + { 0xea00, 0x0000 }, + { 0xea01, 0x0000 }, + { 0xea02, 0x0000 }, + { 0xea03, 0x0000 }, + { 0xea04, 0x0000 }, + { 0xea05, 0x0000 }, + { 0xea06, 0x0000 }, + { 0xea07, 0x0000 }, + { 0xea08, 0x0000 }, + { 0xea09, 0x0000 }, + { 0xea0a, 0x0000 }, + { 0xea0b, 0x0000 }, + { 0xea0c, 0x0000 }, + { 0xea0d, 0x0000 }, + { 0xea0e, 0x0000 }, + { 0xea0f, 0x0000 }, + { 0xea10, 0x0000 }, + { 0xea11, 0x0000 }, + { 0xea12, 0x0000 }, + { 0xea13, 0x0000 }, + { 0xea14, 0x0000 }, + { 0xea15, 0x0000 }, + { 0xea16, 0x0000 }, + { 0xea17, 0x0000 }, + { 0xea18, 0x0000 }, + { 0xea19, 0x0000 }, + { 0xea1a, 0x0000 }, + { 0xea1b, 0x0000 }, + { 0xea1c, 0x0000 }, + { 0xea1d, 0x0000 }, + { 0xea1e, 0x0000 }, + { 0xea1f, 0x0000 }, + { 0xea20, 0x0000 }, + { 0xea21, 0x0000 }, + { 0xea22, 0x0000 }, + { 0xea23, 0x0000 }, + { 0xea24, 0x0000 }, + { 0xea25, 0x0000 }, + { 0xea26, 0x0000 }, + { 0xea27, 0x0000 }, + { 0xea28, 0x0000 }, + { 0xea29, 0x0000 }, + { 0xea2a, 0x0000 }, + { 0xea2b, 0x0000 }, + { 0xea2c, 0x0000 }, + { 0xea2d, 0x0000 }, + { 0xea2e, 0x0000 }, + { 0xea2f, 0x0000 }, + { 0xea30, 0x0000 }, + { 0xea31, 0x0000 }, + { 0xea32, 0x0000 }, + { 0xea33, 0x0000 }, + { 0xea34, 0x0000 }, + { 0xea35, 0x0000 }, + { 0xea36, 0x0000 }, + { 0xea37, 0x0000 }, + { 0xea38, 0x0000 }, + { 0xea39, 0x0000 }, + { 0xea3a, 0x0000 }, + { 0xea3b, 0x0000 }, + { 0xea3c, 0x0000 }, + { 0xea3d, 0x0000 }, + { 0xea3e, 0x0000 }, + { 0xea3f, 0x0000 }, + { 0xea40, 0x0000 }, + { 0xea41, 0x0000 }, + { 0xea42, 0x0000 }, + { 0xea43, 0x0000 }, + { 0xea44, 0x0000 }, + { 0xea45, 0x0000 }, + { 0xea46, 0x0000 }, + { 0xea47, 0x0000 }, + { 0xea48, 0x0000 }, + { 0xea49, 0x0000 }, + { 0xea4a, 0x0000 }, + { 0xea4b, 0x0000 }, + { 0xea4c, 0x0000 }, + { 0xea4d, 0x0000 }, + { 0xea4e, 0x0000 }, + { 0xea4f, 0x0000 }, + { 0xea50, 0x0000 }, + { 0xea51, 0x0000 }, + { 0xea52, 0x0000 }, + { 0xea53, 0x0000 }, + { 0xea54, 0x0000 }, + { 0xea55, 0x0000 }, + { 0xea56, 0x0000 }, + { 0xea57, 0x0000 }, + { 0xea58, 0x0000 }, + { 0xea59, 0x0000 }, + { 0xea5a, 0x0000 }, + { 0xea5b, 0x0000 }, + { 0xea5c, 0x0000 }, + { 0xea5d, 0x0000 }, + { 0xea5e, 0x0000 }, + { 0xea5f, 0x0000 }, + { 0xea60, 0x0000 }, + { 0xea61, 0x0000 }, + { 0xea62, 0x0000 }, + { 0xea63, 0x0000 }, + { 0xea64, 0x0000 }, + { 0xea65, 0x0000 }, + { 0xea66, 0x0000 }, + { 0xea67, 0x0000 }, + { 0xea68, 0x0000 }, + { 0xea69, 0x0000 }, + { 0xea6a, 0x0000 }, + { 0xea6b, 0x0000 }, + { 0xea6c, 0x0000 }, + { 0xea6d, 0x0000 }, + { 0xea6e, 0x0000 }, + { 0xea6f, 0x0000 }, + { 0xea70, 0x0000 }, + { 0xea71, 0x0000 }, + { 0xea72, 0x0000 }, + { 0xea73, 0x0000 }, + { 0xea74, 0x0000 }, + { 0xea75, 0x0000 }, + { 0xea76, 0x0000 }, + { 0xea77, 0x0000 }, + { 0xea78, 0x0000 }, + { 0xea79, 0x0000 }, + { 0xea7a, 0x0000 }, + { 0xea7b, 0x0000 }, + { 0xea7c, 0x0000 }, + { 0xea7d, 0x0000 }, + { 0xea7e, 0x0000 }, + { 0xea7f, 0x0000 }, + { 0xea80, 0x0000 }, + { 0xea81, 0x0000 }, + { 0xea82, 0x0000 }, + { 0xea83, 0x0000 }, + { 0xea84, 0x0000 }, + { 0xea85, 0x0000 }, + { 0xea86, 0x0000 }, + { 0xea87, 0x0000 }, + { 0xea88, 0x0000 }, + { 0xea89, 0x0000 }, + { 0xea8a, 0x0000 }, + { 0xea8b, 0x0000 }, + { 0xea8c, 0x0000 }, + { 0xea8d, 0x0000 }, + { 0xea8e, 0x0000 }, + { 0xea8f, 0x0000 }, + { 0xea90, 0x0000 }, + { 0xea91, 0x0000 }, + { 0xea92, 0x0000 }, + { 0xea93, 0x0000 }, + { 0xea94, 0x0000 }, + { 0xea95, 0x0000 }, + { 0xea96, 0x0000 }, + { 0xea97, 0x0000 }, + { 0xea98, 0x0000 }, + { 0xea99, 0x0000 }, + { 0xea9a, 0x0000 }, + { 0xea9b, 0x0000 }, + { 0xea9c, 0x0000 }, + { 0xea9d, 0x0000 }, + { 0xea9e, 0x0000 }, + { 0xea9f, 0x0000 }, + { 0xeaa0, 0x0000 }, + { 0xeaa1, 0x0000 }, + { 0xeaa2, 0x0000 }, + { 0xeaa3, 0x0000 }, + { 0xeaa4, 0x0000 }, + { 0xeaa5, 0x0000 }, + { 0xeaa6, 0x0000 }, + { 0xeaa7, 0x0000 }, + { 0xeaa8, 0x0000 }, + { 0xeaa9, 0x0000 }, + { 0xeaaa, 0x0000 }, + { 0xeaab, 0x0000 }, + { 0xeaac, 0x0000 }, + { 0xeaad, 0x0000 }, + { 0xeaae, 0x0000 }, + { 0xeaaf, 0x0000 }, + { 0xeab0, 0x0000 }, + { 0xeab1, 0x0000 }, + { 0xeab2, 0x0000 }, + { 0xeab3, 0x0000 }, + { 0xeab4, 0x0000 }, + { 0xeab5, 0x0000 }, + { 0xeab6, 0x0000 }, + { 0xeab7, 0x0000 }, + { 0xeab8, 0x0000 }, + { 0xeab9, 0x0000 }, + { 0xeaba, 0x0000 }, + { 0xeabb, 0x0000 }, + { 0xeabc, 0x0000 }, + { 0xeabd, 0x0000 }, + { 0xeabe, 0x0000 }, + { 0xeabf, 0x0000 }, + { 0xeac0, 0x0000 }, + { 0xeac1, 0x0000 }, + { 0xeac2, 0x0000 }, + { 0xeac3, 0x0000 }, + { 0xeac4, 0x0000 }, + { 0xeac5, 0x0000 }, + { 0xeac6, 0x0000 }, + { 0xeac7, 0x0000 }, + { 0xeac8, 0x0000 }, + { 0xeac9, 0x0000 }, + { 0xeaca, 0x0000 }, + { 0xeacb, 0x0000 }, + { 0xeacc, 0x0000 }, + { 0xeacd, 0x0000 }, + { 0xeace, 0x0000 }, + { 0xeacf, 0x0000 }, + { 0xead0, 0x0000 }, + { 0xead1, 0x0000 }, + { 0xead2, 0x0000 }, + { 0xead3, 0x0000 }, + { 0xead4, 0x0000 }, + { 0xead5, 0x0000 }, + { 0xead6, 0x0000 }, + { 0xead7, 0x0000 }, + { 0xead8, 0x0000 }, + { 0xead9, 0x0000 }, + { 0xeada, 0x0000 }, + { 0xeadb, 0x0000 }, + { 0xeadc, 0x0000 }, + { 0xeadd, 0x0000 }, + { 0xeade, 0x0000 }, + { 0xeadf, 0x0000 }, + { 0xeae0, 0x0000 }, + { 0xeae1, 0x0000 }, + { 0xeae2, 0x0000 }, + { 0xeae3, 0x0000 }, + { 0xeae4, 0x0000 }, + { 0xeae5, 0x0000 }, + { 0xeae6, 0x0000 }, + { 0xeae7, 0x0000 }, + { 0xeae8, 0x0000 }, + { 0xeae9, 0x0000 }, + { 0xeaea, 0x0000 }, + { 0xeaeb, 0x0000 }, + { 0xeaec, 0x0000 }, + { 0xeaed, 0x0000 }, + { 0xeaee, 0x0000 }, + { 0xeaef, 0x0000 }, + { 0xeaf0, 0x0000 }, + { 0xeaf1, 0x0000 }, + { 0xeaf2, 0x0000 }, + { 0xeaf3, 0x0000 }, + { 0xeaf4, 0x0000 }, + { 0xeaf5, 0x0000 }, + { 0xeaf6, 0x0000 }, + { 0xeaf7, 0x0000 }, + { 0xeaf8, 0x0000 }, + { 0xeaf9, 0x0000 }, + { 0xeafa, 0x0000 }, + { 0xeafb, 0x0000 }, + { 0xeafc, 0x0000 }, + { 0xeafd, 0x0000 }, + { 0xeafe, 0x0000 }, + { 0xeaff, 0x0000 }, + { 0xeb00, 0x0000 }, + { 0xeb01, 0x0000 }, + { 0xeb02, 0x0000 }, + { 0xeb03, 0x0000 }, + { 0xeb04, 0x0000 }, + { 0xeb05, 0x0000 }, + { 0xeb06, 0x0000 }, + { 0xeb07, 0x0000 }, + { 0xeb08, 0x0000 }, + { 0xeb09, 0x0000 }, + { 0xeb0a, 0x0000 }, + { 0xeb0b, 0x0000 }, + { 0xeb0c, 0x0000 }, + { 0xeb0d, 0x0000 }, + { 0xeb0e, 0x0000 }, + { 0xeb0f, 0x0000 }, + { 0xeb10, 0x0000 }, + { 0xeb11, 0x0000 }, + { 0xeb12, 0x0000 }, + { 0xeb13, 0x0000 }, + { 0xeb14, 0x0000 }, + { 0xeb15, 0x0000 }, + { 0xeb16, 0x0000 }, + { 0xeb17, 0x0000 }, + { 0xeb18, 0x0000 }, + { 0xeb19, 0x0000 }, + { 0xeb1a, 0x0000 }, + { 0xeb1b, 0x0000 }, + { 0xeb1c, 0x0000 }, + { 0xeb1d, 0x0000 }, + { 0xeb1e, 0x0000 }, + { 0xeb1f, 0x0000 }, + { 0xeb20, 0x0000 }, + { 0xeb21, 0x0000 }, + { 0xeb22, 0x0000 }, + { 0xeb23, 0x0000 }, + { 0xeb24, 0x0000 }, + { 0xeb25, 0x0000 }, + { 0xeb26, 0x0000 }, + { 0xeb27, 0x0000 }, + { 0xeb28, 0x0000 }, + { 0xeb29, 0x0000 }, + { 0xeb2a, 0x0000 }, + { 0xeb2b, 0x0000 }, + { 0xeb2c, 0x0000 }, + { 0xeb2d, 0x0000 }, + { 0xeb2e, 0x0000 }, + { 0xeb2f, 0x0000 }, + { 0xeb30, 0x0000 }, + { 0xeb31, 0x0000 }, + { 0xeb32, 0x0000 }, + { 0xeb33, 0x0000 }, + { 0xeb34, 0x0000 }, + { 0xeb35, 0x0000 }, + { 0xeb36, 0x0000 }, + { 0xeb37, 0x0000 }, + { 0xeb38, 0x0000 }, + { 0xeb39, 0x0000 }, + { 0xeb3a, 0x0000 }, + { 0xeb3b, 0x0000 }, + { 0xeb3c, 0x0000 }, + { 0xeb3d, 0x0000 }, + { 0xeb3e, 0x0000 }, + { 0xeb3f, 0x0000 }, + { 0xeb40, 0x0000 }, + { 0xeb41, 0x0000 }, + { 0xeb42, 0x0000 }, + { 0xeb43, 0x0000 }, + { 0xeb44, 0x0000 }, + { 0xeb45, 0x0000 }, + { 0xeb46, 0x0000 }, + { 0xeb47, 0x0000 }, + { 0xeb48, 0x0000 }, + { 0xeb49, 0x0000 }, + { 0xeb4a, 0x0000 }, + { 0xeb4b, 0x0000 }, + { 0xeb4c, 0x0000 }, + { 0xeb4d, 0x0000 }, + { 0xeb4e, 0x0000 }, + { 0xeb4f, 0x0000 }, + { 0xeb50, 0x0000 }, + { 0xeb51, 0x0000 }, + { 0xeb52, 0x0000 }, + { 0xeb53, 0x0000 }, + { 0xeb54, 0x0000 }, + { 0xeb55, 0x0000 }, + { 0xeb56, 0x0000 }, + { 0xeb57, 0x0000 }, + { 0xeb58, 0x0000 }, + { 0xeb59, 0x0000 }, + { 0xeb5a, 0x0000 }, + { 0xeb5b, 0x0000 }, + { 0xeb5c, 0x0000 }, + { 0xeb5d, 0x0000 }, + { 0xeb5e, 0x0000 }, + { 0xeb5f, 0x0000 }, + { 0xeb60, 0x0000 }, + { 0xeb61, 0x0000 }, + { 0xeb62, 0x0000 }, + { 0xeb63, 0x0000 }, + { 0xeb64, 0x0000 }, + { 0xeb65, 0x0000 }, + { 0xeb66, 0x0000 }, + { 0xeb67, 0x0000 }, + { 0xeb68, 0x0000 }, + { 0xeb69, 0x0000 }, + { 0xeb6a, 0x0000 }, + { 0xeb6b, 0x0000 }, + { 0xeb6c, 0x0000 }, + { 0xeb6d, 0x0000 }, + { 0xeb6e, 0x0000 }, + { 0xeb6f, 0x0000 }, + { 0xeb70, 0x0000 }, + { 0xeb71, 0x0000 }, + { 0xeb72, 0x0000 }, + { 0xeb73, 0x0000 }, + { 0xeb74, 0x0000 }, + { 0xeb75, 0x0000 }, + { 0xeb76, 0x0000 }, + { 0xeb77, 0x0000 }, + { 0xeb78, 0x0000 }, + { 0xeb79, 0x0000 }, + { 0xeb7a, 0x0000 }, + { 0xeb7b, 0x0000 }, + { 0xeb7c, 0x0000 }, + { 0xeb7d, 0x0000 }, + { 0xeb7e, 0x0000 }, + { 0xeb7f, 0x0000 }, + { 0xeb80, 0x0000 }, + { 0xeb81, 0x0000 }, + { 0xeb82, 0x0000 }, + { 0xeb83, 0x0000 }, + { 0xeb84, 0x0000 }, + { 0xeb85, 0x0000 }, + { 0xeb86, 0x0000 }, + { 0xeb87, 0x0000 }, + { 0xeb88, 0x0000 }, + { 0xeb89, 0x0000 }, + { 0xeb8a, 0x0000 }, + { 0xeb8b, 0x0000 }, + { 0xeb8c, 0x0000 }, + { 0xeb8d, 0x0000 }, + { 0xeb8e, 0x0000 }, + { 0xeb8f, 0x0000 }, + { 0xeb90, 0x0000 }, + { 0xeb91, 0x0000 }, + { 0xeb92, 0x0000 }, + { 0xeb93, 0x0000 }, + { 0xeb94, 0x0000 }, + { 0xeb95, 0x0000 }, + { 0xeb96, 0x0000 }, + { 0xeb97, 0x0000 }, + { 0xeb98, 0x0000 }, + { 0xeb99, 0x0000 }, + { 0xeb9a, 0x0000 }, + { 0xeb9b, 0x0000 }, + { 0xeb9c, 0x0000 }, + { 0xeb9d, 0x0000 }, + { 0xeb9e, 0x0000 }, + { 0xeb9f, 0x0000 }, + { 0xeba0, 0x0000 }, + { 0xeba1, 0x0000 }, + { 0xeba2, 0x0000 }, + { 0xeba3, 0x0000 }, + { 0xeba4, 0x0000 }, + { 0xeba5, 0x0000 }, + { 0xeba6, 0x0000 }, + { 0xeba7, 0x0000 }, + { 0xeba8, 0x0000 }, + { 0xeba9, 0x0000 }, + { 0xebaa, 0x0000 }, + { 0xebab, 0x0000 }, + { 0xebac, 0x0000 }, + { 0xebad, 0x0000 }, + { 0xebae, 0x0000 }, + { 0xebaf, 0x0000 }, + { 0xebb0, 0x0000 }, + { 0xebb1, 0x0000 }, + { 0xebb2, 0x0000 }, + { 0xebb3, 0x0000 }, + { 0xebb4, 0x0000 }, + { 0xebb5, 0x0000 }, + { 0xebb6, 0x0000 }, + { 0xebb7, 0x0000 }, + { 0xebb8, 0x0000 }, + { 0xebb9, 0x0000 }, + { 0xebba, 0x0000 }, + { 0xebbb, 0x0000 }, + { 0xebbc, 0x0000 }, + { 0xebbd, 0x0000 }, + { 0xebbe, 0x0000 }, + { 0xebbf, 0x0000 }, + { 0xebc0, 0x0000 }, + { 0xebc1, 0x0000 }, + { 0xebc2, 0x0000 }, + { 0xebc3, 0x0000 }, + { 0xebc4, 0x0000 }, + { 0xebc5, 0x0000 }, + { 0xebc6, 0x0000 }, + { 0xebc7, 0x0000 }, + { 0xebc8, 0x0000 }, + { 0xebc9, 0x0000 }, + { 0xebca, 0x0000 }, + { 0xebcb, 0x0000 }, + { 0xebcc, 0x0000 }, + { 0xebcd, 0x0000 }, + { 0xebce, 0x0000 }, + { 0xebcf, 0x0000 }, + { 0xebd0, 0x0000 }, + { 0xebd1, 0x0000 }, + { 0xebd2, 0x0000 }, + { 0xebd3, 0x0000 }, + { 0xebd4, 0x0000 }, + { 0xebd5, 0x0000 }, + { 0xebd6, 0x0000 }, + { 0xebd7, 0x0000 }, + { 0xebd8, 0x0000 }, + { 0xebd9, 0x0000 }, + { 0xebda, 0x0000 }, + { 0xebdb, 0x0000 }, + { 0xebdc, 0x0000 }, + { 0xebdd, 0x0000 }, + { 0xebde, 0x0000 }, + { 0xebdf, 0x0000 }, + { 0xebe0, 0x0000 }, + { 0xebe1, 0x0000 }, + { 0xebe2, 0x0000 }, + { 0xebe3, 0x0000 }, + { 0xebe4, 0x0000 }, + { 0xebe5, 0x0000 }, + { 0xebe6, 0x0000 }, + { 0xebe7, 0x0000 }, + { 0xebe8, 0x0000 }, + { 0xebe9, 0x0000 }, + { 0xebea, 0x0000 }, + { 0xebeb, 0x0000 }, + { 0xebec, 0x0000 }, + { 0xebed, 0x0000 }, + { 0xebee, 0x0000 }, + { 0xebef, 0x0000 }, + { 0xebf0, 0x0000 }, + { 0xebf1, 0x0000 }, + { 0xebf2, 0x0000 }, + { 0xebf3, 0x0000 }, + { 0xebf4, 0x0000 }, + { 0xebf5, 0x0000 }, + { 0xebf6, 0x0000 }, + { 0xebf7, 0x0000 }, + { 0xebf8, 0x0000 }, + { 0xebf9, 0x0000 }, + { 0xebfa, 0x0000 }, + { 0xebfb, 0x0000 }, + { 0xebfc, 0x0000 }, + { 0xebfd, 0x0000 }, + { 0xebfe, 0x0000 }, + { 0xebff, 0x0000 }, + { 0xec00, 0x0000 }, + { 0xec01, 0x0000 }, + { 0xec02, 0x0000 }, + { 0xec03, 0x0000 }, + { 0xec04, 0x0000 }, + { 0xec05, 0x0000 }, + { 0xec06, 0x0000 }, + { 0xec07, 0x0000 }, + { 0xec08, 0x0000 }, + { 0xec09, 0x0000 }, + { 0xec0a, 0x0000 }, + { 0xec0b, 0x0000 }, + { 0xec0c, 0x0000 }, + { 0xec0d, 0x0000 }, + { 0xec0e, 0x0000 }, + { 0xec0f, 0x0000 }, + { 0xec10, 0x0000 }, + { 0xec11, 0x0000 }, + { 0xec12, 0x0000 }, + { 0xec13, 0x0000 }, + { 0xec14, 0x0000 }, + { 0xec15, 0x0000 }, + { 0xec16, 0x0000 }, + { 0xec17, 0x0000 }, + { 0xec18, 0x0000 }, + { 0xec19, 0x0000 }, + { 0xec1a, 0x0000 }, + { 0xec1b, 0x0000 }, + { 0xec1c, 0x0000 }, + { 0xec1d, 0x0000 }, + { 0xec1e, 0x0000 }, + { 0xec1f, 0x0000 }, + { 0xec20, 0x0000 }, + { 0xec21, 0x0000 }, + { 0xec22, 0x0000 }, + { 0xec23, 0x0000 }, + { 0xec24, 0x0000 }, + { 0xec25, 0x0000 }, + { 0xec26, 0x0000 }, + { 0xec27, 0x0000 }, + { 0xec28, 0x0000 }, + { 0xec29, 0x0000 }, + { 0xec2a, 0x0000 }, + { 0xec2b, 0x0000 }, + { 0xec2c, 0x0000 }, + { 0xec2d, 0x0000 }, + { 0xec2e, 0x0000 }, + { 0xec2f, 0x0000 }, + { 0xec30, 0x0000 }, + { 0xec31, 0x0000 }, + { 0xec32, 0x0000 }, + { 0xec33, 0x0000 }, + { 0xec34, 0x0000 }, + { 0xec35, 0x0000 }, + { 0xec36, 0x0000 }, + { 0xec37, 0x0000 }, + { 0xec38, 0x0000 }, + { 0xec39, 0x0000 }, + { 0xec3a, 0x0000 }, + { 0xec3b, 0x0000 }, + { 0xec3c, 0x0000 }, + { 0xec3d, 0x0000 }, + { 0xec3e, 0x0000 }, + { 0xec3f, 0x0000 }, + { 0xec40, 0x0000 }, + { 0xec41, 0x0000 }, + { 0xec42, 0x0000 }, + { 0xec43, 0x0000 }, + { 0xec44, 0x0000 }, + { 0xec45, 0x0000 }, + { 0xec46, 0x0000 }, + { 0xec47, 0x0000 }, + { 0xec48, 0x0000 }, + { 0xec49, 0x0000 }, + { 0xec4a, 0x0000 }, + { 0xec4b, 0x0000 }, + { 0xec4c, 0x0000 }, + { 0xec4d, 0x0000 }, + { 0xec4e, 0x0000 }, + { 0xec4f, 0x0000 }, + { 0xec50, 0x0000 }, + { 0xec51, 0x0000 }, + { 0xec52, 0x0000 }, + { 0xec53, 0x0000 }, + { 0xec54, 0x0000 }, + { 0xec55, 0x0000 }, + { 0xec56, 0x0000 }, + { 0xec57, 0x0000 }, + { 0xec58, 0x0000 }, + { 0xec59, 0x0000 }, + { 0xec5a, 0x0000 }, + { 0xec5b, 0x0000 }, + { 0xec5c, 0x0000 }, + { 0xec5d, 0x0000 }, + { 0xec5e, 0x0000 }, + { 0xec5f, 0x0000 }, + { 0xec60, 0x0000 }, + { 0xec61, 0x0000 }, + { 0xec62, 0x0000 }, + { 0xec63, 0x0000 }, + { 0xec64, 0x0000 }, + { 0xec65, 0x0000 }, + { 0xec66, 0x0000 }, + { 0xec67, 0x0000 }, + { 0xec68, 0x0000 }, + { 0xec69, 0x0000 }, + { 0xec6a, 0x0000 }, + { 0xec6b, 0x0000 }, + { 0xec6c, 0x0000 }, + { 0xec6d, 0x0000 }, + { 0xec6e, 0x0000 }, + { 0xec6f, 0x0000 }, + { 0xec70, 0x0000 }, + { 0xec71, 0x0000 }, + { 0xec72, 0x0000 }, + { 0xec73, 0x0000 }, + { 0xec74, 0x0000 }, + { 0xec75, 0x0000 }, + { 0xec76, 0x0000 }, + { 0xec77, 0x0000 }, + { 0xec78, 0x0000 }, + { 0xec79, 0x0000 }, + { 0xec7a, 0x0000 }, + { 0xec7b, 0x0000 }, + { 0xec7c, 0x0000 }, + { 0xec7d, 0x0000 }, + { 0xec7e, 0x0000 }, + { 0xec7f, 0x0000 }, + { 0xec80, 0x0000 }, + { 0xec81, 0x0000 }, + { 0xec82, 0x0000 }, + { 0xec83, 0x0000 }, + { 0xec84, 0x0000 }, + { 0xec85, 0x0000 }, + { 0xec86, 0x0000 }, + { 0xec87, 0x0000 }, + { 0xec88, 0x0000 }, + { 0xec89, 0x0000 }, + { 0xec8a, 0x0000 }, + { 0xec8b, 0x0000 }, + { 0xec8c, 0x0000 }, + { 0xec8d, 0x0000 }, + { 0xec8e, 0x0000 }, + { 0xec8f, 0x0000 }, + { 0xec90, 0x0000 }, + { 0xec91, 0x0000 }, + { 0xec92, 0x0000 }, + { 0xec93, 0x0000 }, + { 0xec94, 0x0000 }, + { 0xec95, 0x0000 }, + { 0xec96, 0x0000 }, + { 0xec97, 0x0000 }, + { 0xec98, 0x0000 }, + { 0xec99, 0x0000 }, + { 0xec9a, 0x0000 }, + { 0xec9b, 0x0000 }, + { 0xec9c, 0x0000 }, + { 0xec9d, 0x0000 }, + { 0xec9e, 0x0000 }, + { 0xec9f, 0x0000 }, + { 0xeca0, 0x0000 }, + { 0xeca1, 0x0000 }, + { 0xeca2, 0x0000 }, + { 0xeca3, 0x0000 }, + { 0xeca4, 0x0000 }, + { 0xeca5, 0x0000 }, + { 0xeca6, 0x0000 }, + { 0xeca7, 0x0000 }, + { 0xeca8, 0x0000 }, + { 0xeca9, 0x0000 }, + { 0xecaa, 0x0000 }, + { 0xecab, 0x0000 }, + { 0xecac, 0x0000 }, + { 0xecad, 0x0000 }, + { 0xecae, 0x0000 }, + { 0xecaf, 0x0000 }, + { 0xecb0, 0x0000 }, + { 0xecb1, 0x0000 }, + { 0xecb2, 0x0000 }, + { 0xecb3, 0x0000 }, + { 0xecb4, 0x0000 }, + { 0xecb5, 0x0000 }, + { 0xecb6, 0x0000 }, + { 0xecb7, 0x0000 }, + { 0xecb8, 0x0000 }, + { 0xecb9, 0x0000 }, + { 0xecba, 0x0000 }, + { 0xecbb, 0x0000 }, + { 0xecbc, 0x0000 }, + { 0xecbd, 0x0000 }, + { 0xecbe, 0x0000 }, + { 0xecbf, 0x0000 }, + { 0xecc0, 0x0000 }, + { 0xecc1, 0x0000 }, + { 0xecc2, 0x0000 }, + { 0xecc3, 0x0000 }, + { 0xecc4, 0x0000 }, + { 0xecc5, 0x0000 }, + { 0xecc6, 0x0000 }, + { 0xecc7, 0x0000 }, + { 0xecc8, 0x0000 }, + { 0xecc9, 0x0000 }, + { 0xecca, 0x0000 }, + { 0xeccb, 0x0000 }, + { 0xeccc, 0x0000 }, + { 0xeccd, 0x0000 }, + { 0xecce, 0x0000 }, + { 0xeccf, 0x0000 }, + { 0xecd0, 0x0000 }, + { 0xecd1, 0x0000 }, + { 0xecd2, 0x0000 }, + { 0xecd3, 0x0000 }, + { 0xecd4, 0x0000 }, + { 0xecd5, 0x0000 }, + { 0xecd6, 0x0000 }, + { 0xecd7, 0x0000 }, + { 0xecd8, 0x0000 }, + { 0xecd9, 0x0000 }, + { 0xecda, 0x0000 }, + { 0xecdb, 0x0000 }, + { 0xecdc, 0x0000 }, + { 0xecdd, 0x0000 }, + { 0xecde, 0x0000 }, + { 0xecdf, 0x0000 }, + { 0xece0, 0x0000 }, + { 0xece1, 0x0000 }, + { 0xece2, 0x0000 }, + { 0xece3, 0x0000 }, + { 0xece4, 0x0000 }, + { 0xece5, 0x0000 }, + { 0xece6, 0x0000 }, + { 0xece7, 0x0000 }, + { 0xece8, 0x0000 }, + { 0xece9, 0x0000 }, + { 0xecea, 0x0000 }, + { 0xeceb, 0x0000 }, + { 0xecec, 0x0000 }, + { 0xeced, 0x0000 }, + { 0xecee, 0x0000 }, + { 0xecef, 0x0000 }, + { 0xecf0, 0x0000 }, + { 0xecf1, 0x0000 }, + { 0xecf2, 0x0000 }, + { 0xecf3, 0x0000 }, + { 0xecf4, 0x0000 }, + { 0xecf5, 0x0000 }, + { 0xecf6, 0x0000 }, + { 0xecf7, 0x0000 }, + { 0xecf8, 0x0000 }, + { 0xecf9, 0x0000 }, + { 0xecfa, 0x0000 }, + { 0xecfb, 0x0000 }, + { 0xecfc, 0x0000 }, + { 0xecfd, 0x0000 }, + { 0xecfe, 0x0000 }, + { 0xecff, 0x0000 }, + { 0xed00, 0x0000 }, + { 0xed01, 0x0000 }, + { 0xed02, 0x0000 }, + { 0xed03, 0x0000 }, + { 0xed04, 0x0000 }, + { 0xed05, 0x0000 }, + { 0xed06, 0x0000 }, + { 0xed07, 0x0000 }, + { 0xed08, 0x0000 }, + { 0xed09, 0x0000 }, + { 0xed0a, 0x0000 }, + { 0xed0b, 0x0000 }, + { 0xed0c, 0x0000 }, + { 0xed0d, 0x0000 }, + { 0xed0e, 0x0000 }, + { 0xed0f, 0x0000 }, + { 0xed10, 0x0000 }, + { 0xed11, 0x0000 }, + { 0xed12, 0x0000 }, + { 0xed13, 0x0000 }, + { 0xed14, 0x0000 }, + { 0xed15, 0x0000 }, + { 0xed16, 0x0000 }, + { 0xed17, 0x0000 }, + { 0xed18, 0x0000 }, + { 0xed19, 0x0000 }, + { 0xed1a, 0x0000 }, + { 0xed1b, 0x0000 }, + { 0xed1c, 0x0000 }, + { 0xed1d, 0x0000 }, + { 0xed1e, 0x0000 }, + { 0xed1f, 0x0000 }, + { 0xed20, 0x0000 }, + { 0xed21, 0x0000 }, + { 0xed22, 0x0000 }, + { 0xed23, 0x0000 }, + { 0xed24, 0x0000 }, + { 0xed25, 0x0000 }, + { 0xed26, 0x0000 }, + { 0xed27, 0x0000 }, + { 0xed28, 0x0000 }, + { 0xed29, 0x0000 }, + { 0xed2a, 0x0000 }, + { 0xed2b, 0x0000 }, + { 0xed2c, 0x0000 }, + { 0xed2d, 0x0000 }, + { 0xed2e, 0x0000 }, + { 0xed2f, 0x0000 }, + { 0xed30, 0x0000 }, + { 0xed31, 0x0000 }, + { 0xed32, 0x0000 }, + { 0xed33, 0x0000 }, + { 0xed34, 0x0000 }, + { 0xed35, 0x0000 }, + { 0xed36, 0x0000 }, + { 0xed37, 0x0000 }, + { 0xed38, 0x0000 }, + { 0xed39, 0x0000 }, + { 0xed3a, 0x0000 }, + { 0xed3b, 0x0000 }, + { 0xed3c, 0x0000 }, + { 0xed3d, 0x0000 }, + { 0xed3e, 0x0000 }, + { 0xed3f, 0x0000 }, + { 0xed40, 0x0000 }, + { 0xed41, 0x0000 }, + { 0xed42, 0x0000 }, + { 0xed43, 0x0000 }, + { 0xed44, 0x0000 }, + { 0xed45, 0x0000 }, + { 0xed46, 0x0000 }, + { 0xed47, 0x0000 }, + { 0xed48, 0x0000 }, + { 0xed49, 0x0000 }, + { 0xed4a, 0x0000 }, + { 0xed4b, 0x0000 }, + { 0xed4c, 0x0000 }, + { 0xed4d, 0x0000 }, + { 0xed4e, 0x0000 }, + { 0xed4f, 0x0000 }, + { 0xed50, 0x0000 }, + { 0xed51, 0x0000 }, + { 0xed52, 0x0000 }, + { 0xed53, 0x0000 }, + { 0xed54, 0x0000 }, + { 0xed55, 0x0000 }, + { 0xed56, 0x0000 }, + { 0xed57, 0x0000 }, + { 0xed58, 0x0000 }, + { 0xed59, 0x0000 }, + { 0xed5a, 0x0000 }, + { 0xed5b, 0x0000 }, + { 0xed5c, 0x0000 }, + { 0xed5d, 0x0000 }, + { 0xed5e, 0x0000 }, + { 0xed5f, 0x0000 }, + { 0xed60, 0x0000 }, + { 0xed61, 0x0000 }, + { 0xed62, 0x0000 }, + { 0xed63, 0x0000 }, + { 0xed64, 0x0000 }, + { 0xed65, 0x0000 }, + { 0xed66, 0x0000 }, + { 0xed67, 0x0000 }, + { 0xed68, 0x0000 }, + { 0xed69, 0x0000 }, + { 0xed6a, 0x0000 }, + { 0xed6b, 0x0000 }, + { 0xed6c, 0x0000 }, + { 0xed6d, 0x0000 }, + { 0xed6e, 0x0000 }, + { 0xed6f, 0x0000 }, + { 0xed70, 0x0000 }, + { 0xed71, 0x0000 }, + { 0xed72, 0x0000 }, + { 0xed73, 0x0000 }, + { 0xed74, 0x0000 }, + { 0xed75, 0x0000 }, + { 0xed76, 0x0000 }, + { 0xed77, 0x0000 }, + { 0xed78, 0x0000 }, + { 0xed79, 0x0000 }, + { 0xed7a, 0x0000 }, + { 0xed7b, 0x0000 }, + { 0xed7c, 0x0000 }, + { 0xed7d, 0x0000 }, + { 0xed7e, 0x0000 }, + { 0xed7f, 0x0000 }, + { 0xed80, 0x0000 }, + { 0xed81, 0x0000 }, + { 0xed82, 0x0000 }, + { 0xed83, 0x0000 }, + { 0xed84, 0x0000 }, + { 0xed85, 0x0000 }, + { 0xed86, 0x0000 }, + { 0xed87, 0x0000 }, + { 0xed88, 0x0000 }, + { 0xed89, 0x0000 }, + { 0xed8a, 0x0000 }, + { 0xed8b, 0x0000 }, + { 0xed8c, 0x0000 }, + { 0xed8d, 0x0000 }, + { 0xed8e, 0x0000 }, + { 0xed8f, 0x0000 }, + { 0xed90, 0x0000 }, + { 0xed91, 0x0000 }, + { 0xed92, 0x0000 }, + { 0xed93, 0x0000 }, + { 0xed94, 0x0000 }, + { 0xed95, 0x0000 }, + { 0xed96, 0x0000 }, + { 0xed97, 0x0000 }, + { 0xed98, 0x0000 }, + { 0xed99, 0x0000 }, + { 0xed9a, 0x0000 }, + { 0xed9b, 0x0000 }, + { 0xed9c, 0x0000 }, + { 0xed9d, 0x0000 }, + { 0xed9e, 0x0000 }, + { 0xed9f, 0x0000 }, + { 0xeda0, 0x0000 }, + { 0xeda1, 0x0000 }, + { 0xeda2, 0x0000 }, + { 0xeda3, 0x0000 }, + { 0xeda4, 0x0000 }, + { 0xeda5, 0x0000 }, + { 0xeda6, 0x0000 }, + { 0xeda7, 0x0000 }, + { 0xeda8, 0x0000 }, + { 0xeda9, 0x0000 }, + { 0xedaa, 0x0000 }, + { 0xedab, 0x0000 }, + { 0xedac, 0x0000 }, + { 0xedad, 0x0000 }, + { 0xedae, 0x0000 }, + { 0xedaf, 0x0000 }, + { 0xedb0, 0x0000 }, + { 0xedb1, 0x0000 }, + { 0xedb2, 0x0000 }, + { 0xedb3, 0x0000 }, + { 0xedb4, 0x0000 }, + { 0xedb5, 0x0000 }, + { 0xedb6, 0x0000 }, + { 0xedb7, 0x0000 }, + { 0xedb8, 0x0000 }, + { 0xedb9, 0x0000 }, + { 0xedba, 0x0000 }, + { 0xedbb, 0x0000 }, + { 0xedbc, 0x0000 }, + { 0xedbd, 0x0000 }, + { 0xedbe, 0x0000 }, + { 0xedbf, 0x0000 }, + { 0xedc0, 0x0000 }, + { 0xedc1, 0x0000 }, + { 0xedc2, 0x0000 }, + { 0xedc3, 0x0000 }, + { 0xedc4, 0x0000 }, + { 0xedc5, 0x0000 }, + { 0xedc6, 0x0000 }, + { 0xedc7, 0x0000 }, + { 0xedc8, 0x0000 }, + { 0xedc9, 0x0000 }, + { 0xedca, 0x0000 }, + { 0xedcb, 0x0000 }, + { 0xedcc, 0x0000 }, + { 0xedcd, 0x0000 }, + { 0xedce, 0x0000 }, + { 0xedcf, 0x0000 }, + { 0xedd0, 0x0000 }, + { 0xedd1, 0x0000 }, + { 0xedd2, 0x0000 }, + { 0xedd3, 0x0000 }, + { 0xedd4, 0x0000 }, + { 0xedd5, 0x0000 }, + { 0xedd6, 0x0000 }, + { 0xedd7, 0x0000 }, + { 0xedd8, 0x0000 }, + { 0xedd9, 0x0000 }, + { 0xedda, 0x0000 }, + { 0xeddb, 0x0000 }, + { 0xeddc, 0x0000 }, + { 0xeddd, 0x0000 }, + { 0xedde, 0x0000 }, + { 0xeddf, 0x0000 }, + { 0xede0, 0x0000 }, + { 0xede1, 0x0000 }, + { 0xede2, 0x0000 }, + { 0xede3, 0x0000 }, + { 0xede4, 0x0000 }, + { 0xede5, 0x0000 }, + { 0xede6, 0x0000 }, + { 0xede7, 0x0000 }, + { 0xede8, 0x0000 }, + { 0xede9, 0x0000 }, + { 0xedea, 0x0000 }, + { 0xedeb, 0x0000 }, + { 0xedec, 0x0000 }, + { 0xeded, 0x0000 }, + { 0xedee, 0x0000 }, + { 0xedef, 0x0000 }, + { 0xedf0, 0x0000 }, + { 0xedf1, 0x0000 }, + { 0xedf2, 0x0000 }, + { 0xedf3, 0x0000 }, + { 0xedf4, 0x0000 }, + { 0xedf5, 0x0000 }, + { 0xedf6, 0x0000 }, + { 0xedf7, 0x0000 }, + { 0xedf8, 0x0000 }, + { 0xedf9, 0x0000 }, + { 0xedfa, 0x0000 }, + { 0xedfb, 0x0000 }, + { 0xedfc, 0x0000 }, + { 0xedfd, 0x0000 }, + { 0xedfe, 0x0000 }, + { 0xedff, 0x0000 }, + { 0xee00, 0x0000 }, + { 0xee01, 0x0000 }, + { 0xee02, 0x0000 }, + { 0xee03, 0x0000 }, + { 0xee04, 0x0000 }, + { 0xee05, 0x0000 }, + { 0xee06, 0x0000 }, + { 0xee07, 0x0000 }, + { 0xee08, 0x0000 }, + { 0xee09, 0x0000 }, + { 0xee0a, 0x0000 }, + { 0xee0b, 0x0000 }, + { 0xee0c, 0x0000 }, + { 0xee0d, 0x0000 }, + { 0xee0e, 0x0000 }, + { 0xee0f, 0x0000 }, + { 0xee10, 0x0000 }, + { 0xee11, 0x0000 }, + { 0xee12, 0x0000 }, + { 0xee13, 0x0000 }, + { 0xee14, 0x0000 }, + { 0xee15, 0x0000 }, + { 0xee16, 0x0000 }, + { 0xee17, 0x0000 }, + { 0xee18, 0x0000 }, + { 0xee19, 0x0000 }, + { 0xee1a, 0x0000 }, + { 0xee1b, 0x0000 }, + { 0xee1c, 0x0000 }, + { 0xee1d, 0x0000 }, + { 0xee1e, 0x0000 }, + { 0xee1f, 0x0000 }, + { 0xee20, 0x0000 }, + { 0xee21, 0x0000 }, + { 0xee22, 0x0000 }, + { 0xee23, 0x0000 }, + { 0xee24, 0x0000 }, + { 0xee25, 0x0000 }, + { 0xee26, 0x0000 }, + { 0xee27, 0x0000 }, + { 0xee28, 0x0000 }, + { 0xee29, 0x0000 }, + { 0xee2a, 0x0000 }, + { 0xee2b, 0x0000 }, + { 0xee2c, 0x0000 }, + { 0xee2d, 0x0000 }, + { 0xee2e, 0x0000 }, + { 0xee2f, 0x0000 }, + { 0xee30, 0x0000 }, + { 0xee31, 0x0000 }, + { 0xee32, 0x0000 }, + { 0xee33, 0x0000 }, + { 0xee34, 0x0000 }, + { 0xee35, 0x0000 }, + { 0xee36, 0x0000 }, + { 0xee37, 0x0000 }, + { 0xee38, 0x0000 }, + { 0xee39, 0x0000 }, + { 0xee3a, 0x0000 }, + { 0xee3b, 0x0000 }, + { 0xee3c, 0x0000 }, + { 0xee3d, 0x0000 }, + { 0xee3e, 0x0000 }, + { 0xee3f, 0x0000 }, + { 0xee40, 0x0000 }, + { 0xee41, 0x0000 }, + { 0xee42, 0x0000 }, + { 0xee43, 0x0000 }, + { 0xee44, 0x0000 }, + { 0xee45, 0x0000 }, + { 0xee46, 0x0000 }, + { 0xee47, 0x0000 }, + { 0xee48, 0x0000 }, + { 0xee49, 0x0000 }, + { 0xee4a, 0x0000 }, + { 0xee4b, 0x0000 }, + { 0xee4c, 0x0000 }, + { 0xee4d, 0x0000 }, + { 0xee4e, 0x0000 }, + { 0xee4f, 0x0000 }, + { 0xee50, 0x0000 }, + { 0xee51, 0x0000 }, + { 0xee52, 0x0000 }, + { 0xee53, 0x0000 }, + { 0xee54, 0x0000 }, + { 0xee55, 0x0000 }, + { 0xee56, 0x0000 }, + { 0xee57, 0x0000 }, + { 0xee58, 0x0000 }, + { 0xee59, 0x0000 }, + { 0xee5a, 0x0000 }, + { 0xee5b, 0x0000 }, + { 0xee5c, 0x0000 }, + { 0xee5d, 0x0000 }, + { 0xee5e, 0x0000 }, + { 0xee5f, 0x0000 }, + { 0xee60, 0x0000 }, + { 0xee61, 0x0000 }, + { 0xee62, 0x0000 }, + { 0xee63, 0x0000 }, + { 0xee64, 0x0000 }, + { 0xee65, 0x0000 }, + { 0xee66, 0x0000 }, + { 0xee67, 0x0000 }, + { 0xee68, 0x0000 }, + { 0xee69, 0x0000 }, + { 0xee6a, 0x0000 }, + { 0xee6b, 0x0000 }, + { 0xee6c, 0x0000 }, + { 0xee6d, 0x0000 }, + { 0xee6e, 0x0000 }, + { 0xee6f, 0x0000 }, + { 0xee70, 0x0000 }, + { 0xee71, 0x0000 }, + { 0xee72, 0x0000 }, + { 0xee73, 0x0000 }, + { 0xee74, 0x0000 }, + { 0xee75, 0x0000 }, + { 0xee76, 0x0000 }, + { 0xee77, 0x0000 }, + { 0xee78, 0x0000 }, + { 0xee79, 0x0000 }, + { 0xee7a, 0x0000 }, + { 0xee7b, 0x0000 }, + { 0xee7c, 0x0000 }, + { 0xee7d, 0x0000 }, + { 0xee7e, 0x0000 }, + { 0xee7f, 0x0000 }, + { 0xee80, 0x0000 }, + { 0xee81, 0x0000 }, + { 0xee82, 0x0000 }, + { 0xee83, 0x0000 }, + { 0xee84, 0x0000 }, + { 0xee85, 0x0000 }, + { 0xee86, 0x0000 }, + { 0xee87, 0x0000 }, + { 0xee88, 0x0000 }, + { 0xee89, 0x0000 }, + { 0xee8a, 0x0000 }, + { 0xee8b, 0x0000 }, + { 0xee8c, 0x0000 }, + { 0xee8d, 0x0000 }, + { 0xee8e, 0x0000 }, + { 0xee8f, 0x0000 }, + { 0xee90, 0x0000 }, + { 0xee91, 0x0000 }, + { 0xee92, 0x0000 }, + { 0xee93, 0x0000 }, + { 0xee94, 0x0000 }, + { 0xee95, 0x0000 }, + { 0xee96, 0x0000 }, + { 0xee97, 0x0000 }, + { 0xee98, 0x0000 }, + { 0xee99, 0x0000 }, + { 0xee9a, 0x0000 }, + { 0xee9b, 0x0000 }, + { 0xee9c, 0x0000 }, + { 0xee9d, 0x0000 }, + { 0xee9e, 0x0000 }, + { 0xee9f, 0x0000 }, + { 0xeea0, 0x0000 }, + { 0xeea1, 0x0000 }, + { 0xeea2, 0x0000 }, + { 0xeea3, 0x0000 }, + { 0xeea4, 0x0000 }, + { 0xeea5, 0x0000 }, + { 0xeea6, 0x0000 }, + { 0xeea7, 0x0000 }, + { 0xeea8, 0x0000 }, + { 0xeea9, 0x0000 }, + { 0xeeaa, 0x0000 }, + { 0xeeab, 0x0000 }, + { 0xeeac, 0x0000 }, + { 0xeead, 0x0000 }, + { 0xeeae, 0x0000 }, + { 0xeeaf, 0x0000 }, + { 0xeeb0, 0x0000 }, + { 0xeeb1, 0x0000 }, + { 0xeeb2, 0x0000 }, + { 0xeeb3, 0x0000 }, + { 0xeeb4, 0x0000 }, + { 0xeeb5, 0x0000 }, + { 0xeeb6, 0x0000 }, + { 0xeeb7, 0x0000 }, + { 0xeeb8, 0x0000 }, + { 0xeeb9, 0x0000 }, + { 0xeeba, 0x0000 }, + { 0xeebb, 0x0000 }, + { 0xeebc, 0x0000 }, + { 0xeebd, 0x0000 }, + { 0xeebe, 0x0000 }, + { 0xeebf, 0x0000 }, + { 0xeec0, 0x0000 }, + { 0xeec1, 0x0000 }, + { 0xeec2, 0x0000 }, + { 0xeec3, 0x0000 }, + { 0xeec4, 0x0000 }, + { 0xeec5, 0x0000 }, + { 0xeec6, 0x0000 }, + { 0xeec7, 0x0000 }, + { 0xeec8, 0x0000 }, + { 0xeec9, 0x0000 }, + { 0xeeca, 0x0000 }, + { 0xeecb, 0x0000 }, + { 0xeecc, 0x0000 }, + { 0xeecd, 0x0000 }, + { 0xeece, 0x0000 }, + { 0xeecf, 0x0000 }, + { 0xeed0, 0x0000 }, + { 0xeed1, 0x0000 }, + { 0xeed2, 0x0000 }, + { 0xeed3, 0x0000 }, + { 0xeed4, 0x0000 }, + { 0xeed5, 0x0000 }, + { 0xeed6, 0x0000 }, + { 0xeed7, 0x0000 }, + { 0xeed8, 0x0000 }, + { 0xeed9, 0x0000 }, + { 0xeeda, 0x0000 }, + { 0xeedb, 0x0000 }, + { 0xeedc, 0x0000 }, + { 0xeedd, 0x0000 }, + { 0xeede, 0x0000 }, + { 0xeedf, 0x0000 }, + { 0xeee0, 0x0000 }, + { 0xeee1, 0x0000 }, + { 0xeee2, 0x0000 }, + { 0xeee3, 0x0000 }, + { 0xeee4, 0x0000 }, + { 0xeee5, 0x0000 }, + { 0xeee6, 0x0000 }, + { 0xeee7, 0x0000 }, + { 0xeee8, 0x0000 }, + { 0xeee9, 0x0000 }, + { 0xeeea, 0x0000 }, + { 0xeeeb, 0x0000 }, + { 0xeeec, 0x0000 }, + { 0xeeed, 0x0000 }, + { 0xeeee, 0x0000 }, + { 0xeeef, 0x0000 }, + { 0xeef0, 0x0000 }, + { 0xeef1, 0x0000 }, + { 0xeef2, 0x0000 }, + { 0xeef3, 0x0000 }, + { 0xeef4, 0x0000 }, + { 0xeef5, 0x0000 }, + { 0xeef6, 0x0000 }, + { 0xeef7, 0x0000 }, + { 0xeef8, 0x0000 }, + { 0xeef9, 0x0000 }, + { 0xeefa, 0x0000 }, + { 0xeefb, 0x0000 }, + { 0xeefc, 0x0000 }, + { 0xeefd, 0x0000 }, + { 0xeefe, 0x0000 }, + { 0xeeff, 0x0000 }, + { 0xef00, 0x0000 }, + { 0xef01, 0x0000 }, + { 0xef02, 0x0000 }, + { 0xef03, 0x0000 }, + { 0xef04, 0x0000 }, + { 0xef05, 0x0000 }, + { 0xef06, 0x0000 }, + { 0xef07, 0x0000 }, + { 0xef08, 0x0000 }, + { 0xef09, 0x0000 }, + { 0xef0a, 0x0000 }, + { 0xef0b, 0x0000 }, + { 0xef0c, 0x0000 }, + { 0xef0d, 0x0000 }, + { 0xef0e, 0x0000 }, + { 0xef0f, 0x0000 }, + { 0xef10, 0x0000 }, + { 0xef11, 0x0000 }, + { 0xef12, 0x0000 }, + { 0xef13, 0x0000 }, + { 0xef14, 0x0000 }, + { 0xef15, 0x0000 }, + { 0xef16, 0x0000 }, + { 0xef17, 0x0000 }, + { 0xef18, 0x0000 }, + { 0xef19, 0x0000 }, + { 0xef1a, 0x0000 }, + { 0xef1b, 0x0000 }, + { 0xef1c, 0x0000 }, + { 0xef1d, 0x0000 }, + { 0xef1e, 0x0000 }, + { 0xef1f, 0x0000 }, + { 0xef20, 0x0000 }, + { 0xef21, 0x0000 }, + { 0xef22, 0x0000 }, + { 0xef23, 0x0000 }, + { 0xef24, 0x0000 }, + { 0xef25, 0x0000 }, + { 0xef26, 0x0000 }, + { 0xef27, 0x0000 }, + { 0xef28, 0x0000 }, + { 0xef29, 0x0000 }, + { 0xef2a, 0x0000 }, + { 0xef2b, 0x0000 }, + { 0xef2c, 0x0000 }, + { 0xef2d, 0x0000 }, + { 0xef2e, 0x0000 }, + { 0xef2f, 0x0000 }, + { 0xef30, 0x0000 }, + { 0xef31, 0x0000 }, + { 0xef32, 0x0000 }, + { 0xef33, 0x0000 }, + { 0xef34, 0x0000 }, + { 0xef35, 0x0000 }, + { 0xef36, 0x0000 }, + { 0xef37, 0x0000 }, + { 0xef38, 0x0000 }, + { 0xef39, 0x0000 }, + { 0xef3a, 0x0000 }, + { 0xef3b, 0x0000 }, + { 0xef3c, 0x0000 }, + { 0xef3d, 0x0000 }, + { 0xef3e, 0x0000 }, + { 0xef3f, 0x0000 }, + { 0xef40, 0x0000 }, + { 0xef41, 0x0000 }, + { 0xef42, 0x0000 }, + { 0xef43, 0x0000 }, + { 0xef44, 0x0000 }, + { 0xef45, 0x0000 }, + { 0xef46, 0x0000 }, + { 0xef47, 0x0000 }, + { 0xef48, 0x0000 }, + { 0xef49, 0x0000 }, + { 0xef4a, 0x0000 }, + { 0xef4b, 0x0000 }, + { 0xef4c, 0x0000 }, + { 0xef4d, 0x0000 }, + { 0xef4e, 0x0000 }, + { 0xef4f, 0x0000 }, + { 0xef50, 0x0000 }, + { 0xef51, 0x0000 }, + { 0xef52, 0x0000 }, + { 0xef53, 0x0000 }, + { 0xef54, 0x0000 }, + { 0xef55, 0x0000 }, + { 0xef56, 0x0000 }, + { 0xef57, 0x0000 }, + { 0xef58, 0x0000 }, + { 0xef59, 0x0000 }, + { 0xef5a, 0x0000 }, + { 0xef5b, 0x0000 }, + { 0xef5c, 0x0000 }, + { 0xef5d, 0x0000 }, + { 0xef5e, 0x0000 }, + { 0xef5f, 0x0000 }, + { 0xef60, 0x0000 }, + { 0xef61, 0x0000 }, + { 0xef62, 0x0000 }, + { 0xef63, 0x0000 }, + { 0xef64, 0x0000 }, + { 0xef65, 0x0000 }, + { 0xef66, 0x0000 }, + { 0xef67, 0x0000 }, + { 0xef68, 0x0000 }, + { 0xef69, 0x0000 }, + { 0xef6a, 0x0000 }, + { 0xef6b, 0x0000 }, + { 0xef6c, 0x0000 }, + { 0xef6d, 0x0000 }, + { 0xef6e, 0x0000 }, + { 0xef6f, 0x0000 }, + { 0xef70, 0x0000 }, + { 0xef71, 0x0000 }, + { 0xef72, 0x0000 }, + { 0xef73, 0x0000 }, + { 0xef74, 0x0000 }, + { 0xef75, 0x0000 }, + { 0xef76, 0x0000 }, + { 0xef77, 0x0000 }, + { 0xef78, 0x0000 }, + { 0xef79, 0x0000 }, + { 0xef7a, 0x0000 }, + { 0xef7b, 0x0000 }, + { 0xef7c, 0x0000 }, + { 0xef7d, 0x0000 }, + { 0xef7e, 0x0000 }, + { 0xef7f, 0x0000 }, + { 0xef80, 0x0000 }, + { 0xef81, 0x0000 }, + { 0xef82, 0x0000 }, + { 0xef83, 0x0000 }, + { 0xef84, 0x0000 }, + { 0xef85, 0x0000 }, + { 0xef86, 0x0000 }, + { 0xef87, 0x0000 }, + { 0xef88, 0x0000 }, + { 0xef89, 0x0000 }, + { 0xef8a, 0x0000 }, + { 0xef8b, 0x0000 }, + { 0xef8c, 0x0000 }, + { 0xef8d, 0x0000 }, + { 0xef8e, 0x0000 }, + { 0xef8f, 0x0000 }, + { 0xef90, 0x0000 }, + { 0xef91, 0x0000 }, + { 0xef92, 0x0000 }, + { 0xef93, 0x0000 }, + { 0xef94, 0x0000 }, + { 0xef95, 0x0000 }, + { 0xef96, 0x0000 }, + { 0xef97, 0x0000 }, + { 0xef98, 0x0000 }, + { 0xef99, 0x0000 }, + { 0xef9a, 0x0000 }, + { 0xef9b, 0x0000 }, + { 0xef9c, 0x0000 }, + { 0xef9d, 0x0000 }, + { 0xef9e, 0x0000 }, + { 0xef9f, 0x0000 }, + { 0xefa0, 0x0000 }, + { 0xefa1, 0x0000 }, + { 0xefa2, 0x0000 }, + { 0xefa3, 0x0000 }, + { 0xefa4, 0x0000 }, + { 0xefa5, 0x0000 }, + { 0xefa6, 0x0000 }, + { 0xefa7, 0x0000 }, + { 0xefa8, 0x0000 }, + { 0xefa9, 0x0000 }, + { 0xefaa, 0x0000 }, + { 0xefab, 0x0000 }, + { 0xefac, 0x0000 }, + { 0xefad, 0x0000 }, + { 0xefae, 0x0000 }, + { 0xefaf, 0x0000 }, + { 0xefb0, 0x0000 }, + { 0xefb1, 0x0000 }, + { 0xefb2, 0x0000 }, + { 0xefb3, 0x0000 }, + { 0xefb4, 0x0000 }, + { 0xefb5, 0x0000 }, + { 0xefb6, 0x0000 }, + { 0xefb7, 0x0000 }, + { 0xefb8, 0x0000 }, + { 0xefb9, 0x0000 }, + { 0xefba, 0x0000 }, + { 0xefbb, 0x0000 }, + { 0xefbc, 0x0000 }, + { 0xefbd, 0x0000 }, + { 0xefbe, 0x0000 }, + { 0xefbf, 0x0000 }, + { 0xefc0, 0x0000 }, + { 0xefc1, 0x0000 }, + { 0xefc2, 0x0000 }, + { 0xefc3, 0x0000 }, + { 0xefc4, 0x0000 }, + { 0xefc5, 0x0000 }, + { 0xefc6, 0x0000 }, + { 0xefc7, 0x0000 }, + { 0xefc8, 0x0000 }, + { 0xefc9, 0x0000 }, + { 0xefca, 0x0000 }, + { 0xefcb, 0x0000 }, + { 0xefcc, 0x0000 }, + { 0xefcd, 0x0000 }, + { 0xefce, 0x0000 }, + { 0xefcf, 0x0000 }, + { 0xefd0, 0x0000 }, + { 0xefd1, 0x0000 }, + { 0xefd2, 0x0000 }, + { 0xefd3, 0x0000 }, + { 0xefd4, 0x0000 }, + { 0xefd5, 0x0000 }, + { 0xefd6, 0x0000 }, + { 0xefd7, 0x0000 }, + { 0xefd8, 0x0000 }, + { 0xefd9, 0x0000 }, + { 0xefda, 0x0000 }, + { 0xefdb, 0x0000 }, + { 0xefdc, 0x0000 }, + { 0xefdd, 0x0000 }, + { 0xefde, 0x0000 }, + { 0xefdf, 0x0000 }, + { 0xefe0, 0x0000 }, + { 0xefe1, 0x0000 }, + { 0xefe2, 0x0000 }, + { 0xefe3, 0x0000 }, + { 0xefe4, 0x0000 }, + { 0xefe5, 0x0000 }, + { 0xefe6, 0x0000 }, + { 0xefe7, 0x0000 }, + { 0xefe8, 0x0000 }, + { 0xefe9, 0x0000 }, + { 0xefea, 0x0000 }, + { 0xefeb, 0x0000 }, + { 0xefec, 0x0000 }, + { 0xefed, 0x0000 }, + { 0xefee, 0x0000 }, + { 0xefef, 0x0000 }, + { 0xeff0, 0x0000 }, + { 0xeff1, 0x0000 }, + { 0xeff2, 0x0000 }, + { 0xeff3, 0x0000 }, + { 0xeff4, 0x0000 }, + { 0xeff5, 0x0000 }, + { 0xeff6, 0x0000 }, + { 0xeff7, 0x0000 }, + { 0xeff8, 0x0000 }, + { 0xeff9, 0x0000 }, + { 0xeffa, 0x0000 }, + { 0xeffb, 0x0000 }, + { 0xeffc, 0x0000 }, + { 0xeffd, 0x0000 }, + { 0xeffe, 0x0000 }, + { 0xefff, 0x0000 }, + { 0xf000, 0x0000 }, + { 0xf001, 0x0000 }, + { 0xf002, 0x0000 }, + { 0xf003, 0x0000 }, + { 0xf004, 0x0000 }, + { 0xf005, 0x0000 }, + { 0xf006, 0x0000 }, + { 0xf007, 0x0000 }, + { 0xf008, 0x0000 }, + { 0xf009, 0x0000 }, + { 0xf00a, 0x0000 }, + { 0xf00b, 0x0000 }, + { 0xf00c, 0x0000 }, + { 0xf00d, 0x0000 }, + { 0xf00e, 0x0000 }, + { 0xf00f, 0x0000 }, + { 0xf010, 0x0000 }, + { 0xf011, 0x0000 }, + { 0xf012, 0x0000 }, + { 0xf013, 0x0000 }, + { 0xf014, 0x0000 }, + { 0xf015, 0x0000 }, + { 0xf016, 0x0000 }, + { 0xf017, 0x0000 }, + { 0xf018, 0x0000 }, + { 0xf019, 0x0000 }, + { 0xf01a, 0x0000 }, + { 0xf01b, 0x0000 }, + { 0xf01c, 0x0000 }, + { 0xf01d, 0x0000 }, + { 0xf01e, 0x0000 }, + { 0xf01f, 0x0000 }, + { 0xf020, 0x0000 }, + { 0xf021, 0x0000 }, + { 0xf022, 0x0000 }, + { 0xf023, 0x0000 }, + { 0xf024, 0x0000 }, + { 0xf025, 0x0000 }, + { 0xf026, 0x0000 }, + { 0xf027, 0x0000 }, + { 0xf028, 0x0000 }, + { 0xf029, 0x0000 }, + { 0xf02a, 0x0000 }, + { 0xf02b, 0x0000 }, + { 0xf02c, 0x0000 }, + { 0xf02d, 0x0000 }, + { 0xf02e, 0x0000 }, + { 0xf02f, 0x0000 }, + { 0xf030, 0x0000 }, + { 0xf031, 0x0000 }, + { 0xf032, 0x0000 }, + { 0xf033, 0x0000 }, + { 0xf034, 0x0000 }, + { 0xf035, 0x0000 }, + { 0xf036, 0x0000 }, + { 0xf037, 0x0000 }, + { 0xf038, 0x0000 }, + { 0xf039, 0x0000 }, + { 0xf03a, 0x0000 }, + { 0xf03b, 0x0000 }, + { 0xf03c, 0x0000 }, + { 0xf03d, 0x0000 }, + { 0xf03e, 0x0000 }, + { 0xf03f, 0x0000 }, + { 0xf040, 0x0000 }, + { 0xf041, 0x0000 }, + { 0xf042, 0x0000 }, + { 0xf043, 0x0000 }, + { 0xf044, 0x0000 }, + { 0xf045, 0x0000 }, + { 0xf046, 0x0000 }, + { 0xf047, 0x0000 }, + { 0xf048, 0x0000 }, + { 0xf049, 0x0000 }, + { 0xf04a, 0x0000 }, + { 0xf04b, 0x0000 }, + { 0xf04c, 0x0000 }, + { 0xf04d, 0x0000 }, + { 0xf04e, 0x0000 }, + { 0xf04f, 0x0000 }, + { 0xf050, 0x0000 }, + { 0xf051, 0x0000 }, + { 0xf052, 0x0000 }, + { 0xf053, 0x0000 }, + { 0xf054, 0x0000 }, + { 0xf055, 0x0000 }, + { 0xf056, 0x0000 }, + { 0xf057, 0x0000 }, + { 0xf058, 0x0000 }, + { 0xf059, 0x0000 }, + { 0xf05a, 0x0000 }, + { 0xf05b, 0x0000 }, + { 0xf05c, 0x0000 }, + { 0xf05d, 0x0000 }, + { 0xf05e, 0x0000 }, + { 0xf05f, 0x0000 }, + { 0xf060, 0x0000 }, + { 0xf061, 0x0000 }, + { 0xf062, 0x0000 }, + { 0xf063, 0x0000 }, + { 0xf064, 0x0000 }, + { 0xf065, 0x0000 }, + { 0xf066, 0x0000 }, + { 0xf067, 0x0000 }, + { 0xf068, 0x0000 }, + { 0xf069, 0x0000 }, + { 0xf06a, 0x0000 }, + { 0xf06b, 0x0000 }, + { 0xf06c, 0x0000 }, + { 0xf06d, 0x0000 }, + { 0xf06e, 0x0000 }, + { 0xf06f, 0x0000 }, + { 0xf070, 0x0000 }, + { 0xf071, 0x0000 }, + { 0xf072, 0x0000 }, + { 0xf073, 0x0000 }, + { 0xf074, 0x0000 }, + { 0xf075, 0x0000 }, + { 0xf076, 0x0000 }, + { 0xf077, 0x0000 }, + { 0xf078, 0x0000 }, + { 0xf079, 0x0000 }, + { 0xf07a, 0x0000 }, + { 0xf07b, 0x0000 }, + { 0xf07c, 0x0000 }, + { 0xf07d, 0x0000 }, + { 0xf07e, 0x0000 }, + { 0xf07f, 0x0000 }, + { 0xf080, 0x0000 }, + { 0xf081, 0x0000 }, + { 0xf082, 0x0000 }, + { 0xf083, 0x0000 }, + { 0xf084, 0x0000 }, + { 0xf085, 0x0000 }, + { 0xf086, 0x0000 }, + { 0xf087, 0x0000 }, + { 0xf088, 0x0000 }, + { 0xf089, 0x0000 }, + { 0xf08a, 0x0000 }, + { 0xf08b, 0x0000 }, + { 0xf08c, 0x0000 }, + { 0xf08d, 0x0000 }, + { 0xf08e, 0x0000 }, + { 0xf08f, 0x0000 }, + { 0xf090, 0x0000 }, + { 0xf091, 0x0000 }, + { 0xf092, 0x0000 }, + { 0xf093, 0x0000 }, + { 0xf094, 0x0000 }, + { 0xf095, 0x0000 }, + { 0xf096, 0x0000 }, + { 0xf097, 0x0000 }, + { 0xf098, 0x0000 }, + { 0xf099, 0x0000 }, + { 0xf09a, 0x0000 }, + { 0xf09b, 0x0000 }, + { 0xf09c, 0x0000 }, + { 0xf09d, 0x0000 }, + { 0xf09e, 0x0000 }, + { 0xf09f, 0x0000 }, + { 0xf0a0, 0x0000 }, + { 0xf0a1, 0x0000 }, + { 0xf0a2, 0x0000 }, + { 0xf0a3, 0x0000 }, + { 0xf0a4, 0x0000 }, + { 0xf0a5, 0x0000 }, + { 0xf0a6, 0x0000 }, + { 0xf0a7, 0x0000 }, + { 0xf0a8, 0x0000 }, + { 0xf0a9, 0x0000 }, + { 0xf0aa, 0x0000 }, + { 0xf0ab, 0x0000 }, + { 0xf0ac, 0x0000 }, + { 0xf0ad, 0x0000 }, + { 0xf0ae, 0x0000 }, + { 0xf0af, 0x0000 }, + { 0xf0b0, 0x0000 }, + { 0xf0b1, 0x0000 }, + { 0xf0b2, 0x0000 }, + { 0xf0b3, 0x0000 }, + { 0xf0b4, 0x0000 }, + { 0xf0b5, 0x0000 }, + { 0xf0b6, 0x0000 }, + { 0xf0b7, 0x0000 }, + { 0xf0b8, 0x0000 }, + { 0xf0b9, 0x0000 }, + { 0xf0ba, 0x0000 }, + { 0xf0bb, 0x0000 }, + { 0xf0bc, 0x0000 }, + { 0xf0bd, 0x0000 }, + { 0xf0be, 0x0000 }, + { 0xf0bf, 0x0000 }, + { 0xf0c0, 0x0000 }, + { 0xf0c1, 0x0000 }, + { 0xf0c2, 0x0000 }, + { 0xf0c3, 0x0000 }, + { 0xf0c4, 0x0000 }, + { 0xf0c5, 0x0000 }, + { 0xf0c6, 0x0000 }, + { 0xf0c7, 0x0000 }, + { 0xf0c8, 0x0000 }, + { 0xf0c9, 0x0000 }, + { 0xf0ca, 0x0000 }, + { 0xf0cb, 0x0000 }, + { 0xf0cc, 0x0000 }, + { 0xf0cd, 0x0000 }, + { 0xf0ce, 0x0000 }, + { 0xf0cf, 0x0000 }, + { 0xf0d0, 0x0000 }, + { 0xf0d1, 0x0000 }, + { 0xf0d2, 0x0000 }, + { 0xf0d3, 0x0000 }, + { 0xf0d4, 0x0000 }, + { 0xf0d5, 0x0000 }, + { 0xf0d6, 0x0000 }, + { 0xf0d7, 0x0000 }, + { 0xf0d8, 0x0000 }, + { 0xf0d9, 0x0000 }, + { 0xf0da, 0x0000 }, + { 0xf0db, 0x0000 }, + { 0xf0dc, 0x0000 }, + { 0xf0dd, 0x0000 }, + { 0xf0de, 0x0000 }, + { 0xf0df, 0x0000 }, + { 0xf0e0, 0x0000 }, + { 0xf0e1, 0x0000 }, + { 0xf0e2, 0x0000 }, + { 0xf0e3, 0x0000 }, + { 0xf0e4, 0x0000 }, + { 0xf0e5, 0x0000 }, + { 0xf0e6, 0x0000 }, + { 0xf0e7, 0x0000 }, + { 0xf0e8, 0x0000 }, + { 0xf0e9, 0x0000 }, + { 0xf0ea, 0x0000 }, + { 0xf0eb, 0x0000 }, + { 0xf0ec, 0x0000 }, + { 0xf0ed, 0x0000 }, + { 0xf0ee, 0x0000 }, + { 0xf0ef, 0x0000 }, + { 0xf0f0, 0x0000 }, + { 0xf0f1, 0x0000 }, + { 0xf0f2, 0x0000 }, + { 0xf0f3, 0x0000 }, + { 0xf0f4, 0x0000 }, + { 0xf0f5, 0x0000 }, + { 0xf0f6, 0x0000 }, + { 0xf0f7, 0x0000 }, + { 0xf0f8, 0x0000 }, + { 0xf0f9, 0x0000 }, + { 0xf0fa, 0x0000 }, + { 0xf0fb, 0x0000 }, + { 0xf0fc, 0x0000 }, + { 0xf0fd, 0x0000 }, + { 0xf0fe, 0x0000 }, + { 0xf0ff, 0x0000 }, + { 0xf100, 0x0000 }, + { 0xf101, 0x0000 }, + { 0xf102, 0x0000 }, + { 0xf103, 0x0000 }, + { 0xf104, 0x0000 }, + { 0xf105, 0x0000 }, + { 0xf106, 0x0000 }, + { 0xf107, 0x0000 }, + { 0xf108, 0x0000 }, + { 0xf109, 0x0000 }, + { 0xf10a, 0x0000 }, + { 0xf10b, 0x0000 }, + { 0xf10c, 0x0000 }, + { 0xf10d, 0x0000 }, + { 0xf10e, 0x0000 }, + { 0xf10f, 0x0000 }, + { 0xf110, 0x0000 }, + { 0xf111, 0x0000 }, + { 0xf112, 0x0000 }, + { 0xf113, 0x0000 }, + { 0xf114, 0x0000 }, + { 0xf115, 0x0000 }, + { 0xf116, 0x0000 }, + { 0xf117, 0x0000 }, + { 0xf118, 0x0000 }, + { 0xf119, 0x0000 }, + { 0xf11a, 0x0000 }, + { 0xf11b, 0x0000 }, + { 0xf11c, 0x0000 }, + { 0xf11d, 0x0000 }, + { 0xf11e, 0x0000 }, + { 0xf11f, 0x0000 }, + { 0xf120, 0x0000 }, + { 0xf121, 0x0000 }, + { 0xf122, 0x0000 }, + { 0xf123, 0x0000 }, + { 0xf124, 0x0000 }, + { 0xf125, 0x0000 }, + { 0xf126, 0x0000 }, + { 0xf127, 0x0000 }, + { 0xf128, 0x0000 }, + { 0xf129, 0x0000 }, + { 0xf12a, 0x0000 }, + { 0xf12b, 0x0000 }, + { 0xf12c, 0x0000 }, + { 0xf12d, 0x0000 }, + { 0xf12e, 0x0000 }, + { 0xf12f, 0x0000 }, + { 0xf130, 0x0000 }, + { 0xf131, 0x0000 }, + { 0xf132, 0x0000 }, + { 0xf133, 0x0000 }, + { 0xf134, 0x0000 }, + { 0xf135, 0x0000 }, + { 0xf136, 0x0000 }, + { 0xf137, 0x0000 }, + { 0xf138, 0x0000 }, + { 0xf139, 0x0000 }, + { 0xf13a, 0x0000 }, + { 0xf13b, 0x0000 }, + { 0xf13c, 0x0000 }, + { 0xf13d, 0x0000 }, + { 0xf13e, 0x0000 }, + { 0xf13f, 0x0000 }, + { 0xf140, 0x0000 }, + { 0xf141, 0x0000 }, + { 0xf142, 0x0000 }, + { 0xf143, 0x0000 }, + { 0xf144, 0x0000 }, + { 0xf145, 0x0000 }, + { 0xf146, 0x0000 }, + { 0xf147, 0x0000 }, + { 0xf148, 0x0000 }, + { 0xf149, 0x0000 }, + { 0xf14a, 0x0000 }, + { 0xf14b, 0x0000 }, + { 0xf14c, 0x0000 }, + { 0xf14d, 0x0000 }, + { 0xf14e, 0x0000 }, + { 0xf14f, 0x0000 }, + { 0xf150, 0x0000 }, + { 0xf151, 0x0000 }, + { 0xf152, 0x0000 }, + { 0xf153, 0x0000 }, + { 0xf154, 0x0000 }, + { 0xf155, 0x0000 }, + { 0xf156, 0x0000 }, + { 0xf157, 0x0000 }, + { 0xf158, 0x0000 }, + { 0xf159, 0x0000 }, + { 0xf15a, 0x0000 }, + { 0xf15b, 0x0000 }, + { 0xf15c, 0x0000 }, + { 0xf15d, 0x0000 }, + { 0xf15e, 0x0000 }, + { 0xf15f, 0x0000 }, + { 0xf160, 0x0000 }, + { 0xf161, 0x0000 }, + { 0xf162, 0x0000 }, + { 0xf163, 0x0000 }, + { 0xf164, 0x0000 }, + { 0xf165, 0x0000 }, + { 0xf166, 0x0000 }, + { 0xf167, 0x0000 }, + { 0xf168, 0x0000 }, + { 0xf169, 0x0000 }, + { 0xf16a, 0x0000 }, + { 0xf16b, 0x0000 }, + { 0xf16c, 0x0000 }, + { 0xf16d, 0x0000 }, + { 0xf16e, 0x0000 }, + { 0xf16f, 0x0000 }, + { 0xf170, 0x0000 }, + { 0xf171, 0x0000 }, + { 0xf172, 0x0000 }, + { 0xf173, 0x0000 }, + { 0xf174, 0x0000 }, + { 0xf175, 0x0000 }, + { 0xf176, 0x0000 }, + { 0xf177, 0x0000 }, + { 0xf178, 0x0000 }, + { 0xf179, 0x0000 }, + { 0xf17a, 0x0000 }, + { 0xf17b, 0x0000 }, + { 0xf17c, 0x0000 }, + { 0xf17d, 0x0000 }, + { 0xf17e, 0x0000 }, + { 0xf17f, 0x0000 }, + { 0xf180, 0x0000 }, + { 0xf181, 0x0000 }, + { 0xf182, 0x0000 }, + { 0xf183, 0x0000 }, + { 0xf184, 0x0000 }, + { 0xf185, 0x0000 }, + { 0xf186, 0x0000 }, + { 0xf187, 0x0000 }, + { 0xf188, 0x0000 }, + { 0xf189, 0x0000 }, + { 0xf18a, 0x0000 }, + { 0xf18b, 0x0000 }, + { 0xf18c, 0x0000 }, + { 0xf18d, 0x0000 }, + { 0xf18e, 0x0000 }, + { 0xf18f, 0x0000 }, + { 0xf190, 0x0000 }, + { 0xf191, 0x0000 }, + { 0xf192, 0x0000 }, + { 0xf193, 0x0000 }, + { 0xf194, 0x0000 }, + { 0xf195, 0x0000 }, + { 0xf196, 0x0000 }, + { 0xf197, 0x0000 }, + { 0xf198, 0x0000 }, + { 0xf199, 0x0000 }, + { 0xf19a, 0x0000 }, + { 0xf19b, 0x0000 }, + { 0xf19c, 0x0000 }, + { 0xf19d, 0x0000 }, + { 0xf19e, 0x0000 }, + { 0xf19f, 0x0000 }, + { 0xf1a0, 0x0000 }, + { 0xf1a1, 0x0000 }, + { 0xf1a2, 0x0000 }, + { 0xf1a3, 0x0000 }, + { 0xf1a4, 0x0000 }, + { 0xf1a5, 0x0000 }, + { 0xf1a6, 0x0000 }, + { 0xf1a7, 0x0000 }, + { 0xf1a8, 0x0000 }, + { 0xf1a9, 0x0000 }, + { 0xf1aa, 0x0000 }, + { 0xf1ab, 0x0000 }, + { 0xf1ac, 0x0000 }, + { 0xf1ad, 0x0000 }, + { 0xf1ae, 0x0000 }, + { 0xf1af, 0x0000 }, + { 0xf1b0, 0x0000 }, + { 0xf1b1, 0x0000 }, + { 0xf1b2, 0x0000 }, + { 0xf1b3, 0x0000 }, + { 0xf1b4, 0x0000 }, + { 0xf1b5, 0x0000 }, + { 0xf1b6, 0x0000 }, + { 0xf1b7, 0x0000 }, + { 0xf1b8, 0x0000 }, + { 0xf1b9, 0x0000 }, + { 0xf1ba, 0x0000 }, + { 0xf1bb, 0x0000 }, + { 0xf1bc, 0x0000 }, + { 0xf1bd, 0x0000 }, + { 0xf1be, 0x0000 }, + { 0xf1bf, 0x0000 }, + { 0xf1c0, 0x0000 }, + { 0xf1c1, 0x0000 }, + { 0xf1c2, 0x0000 }, + { 0xf1c3, 0x0000 }, + { 0xf1c4, 0x0000 }, + { 0xf1c5, 0x0000 }, + { 0xf1c6, 0x0000 }, + { 0xf1c7, 0x0000 }, + { 0xf1c8, 0x0000 }, + { 0xf1c9, 0x0000 }, + { 0xf1ca, 0x0000 }, + { 0xf1cb, 0x0000 }, + { 0xf1cc, 0x0000 }, + { 0xf1cd, 0x0000 }, + { 0xf1ce, 0x0000 }, + { 0xf1cf, 0x0000 }, + { 0xf1d0, 0x0000 }, + { 0xf1d1, 0x0000 }, + { 0xf1d2, 0x0000 }, + { 0xf1d3, 0x0000 }, + { 0xf1d4, 0x0000 }, + { 0xf1d5, 0x0000 }, + { 0xf1d6, 0x0000 }, + { 0xf1d7, 0x0000 }, + { 0xf1d8, 0x0000 }, + { 0xf1d9, 0x0000 }, + { 0xf1da, 0x0000 }, + { 0xf1db, 0x0000 }, + { 0xf1dc, 0x0000 }, + { 0xf1dd, 0x0000 }, + { 0xf1de, 0x0000 }, + { 0xf1df, 0x0000 }, + { 0xf1e0, 0x0000 }, + { 0xf1e1, 0x0000 }, + { 0xf1e2, 0x0000 }, + { 0xf1e3, 0x0000 }, + { 0xf1e4, 0x0000 }, + { 0xf1e5, 0x0000 }, + { 0xf1e6, 0x0000 }, + { 0xf1e7, 0x0000 }, + { 0xf1e8, 0x0000 }, + { 0xf1e9, 0x0000 }, + { 0xf1ea, 0x0000 }, + { 0xf1eb, 0x0000 }, + { 0xf1ec, 0x0000 }, + { 0xf1ed, 0x0000 }, + { 0xf1ee, 0x0000 }, + { 0xf1ef, 0x0000 }, + { 0xf1f0, 0x0000 }, + { 0xf1f1, 0x0000 }, + { 0xf1f2, 0x0000 }, + { 0xf1f3, 0x0000 }, + { 0xf1f4, 0x0000 }, + { 0xf1f5, 0x0000 }, + { 0xf1f6, 0x0000 }, + { 0xf1f7, 0x0000 }, + { 0xf1f8, 0x0000 }, + { 0xf1f9, 0x0000 }, + { 0xf1fa, 0x0000 }, + { 0xf1fb, 0x0000 }, + { 0xf1fc, 0x0000 }, + { 0xf1fd, 0x0000 }, + { 0xf1fe, 0x0000 }, + { 0xf1ff, 0x0000 }, + { 0xf200, 0x0000 }, + { 0xf201, 0x0000 }, + { 0xf202, 0x0000 }, + { 0xf203, 0x0000 }, + { 0xf204, 0x0000 }, + { 0xf205, 0x0000 }, + { 0xf206, 0x0000 }, + { 0xf207, 0x0000 }, + { 0xf208, 0x0000 }, + { 0xf209, 0x0000 }, + { 0xf20a, 0x0000 }, + { 0xf20b, 0x0000 }, + { 0xf20c, 0x0000 }, + { 0xf20d, 0x0000 }, + { 0xf20e, 0x0000 }, + { 0xf20f, 0x0000 }, + { 0xf210, 0x0000 }, + { 0xf211, 0x0000 }, + { 0xf212, 0x0000 }, + { 0xf213, 0x0000 }, + { 0xf214, 0x0000 }, + { 0xf215, 0x0000 }, + { 0xf216, 0x0000 }, + { 0xf217, 0x0000 }, + { 0xf218, 0x0000 }, + { 0xf219, 0x0000 }, + { 0xf21a, 0x0000 }, + { 0xf21b, 0x0000 }, + { 0xf21c, 0x0000 }, + { 0xf21d, 0x0000 }, + { 0xf21e, 0x0000 }, + { 0xf21f, 0x0000 }, + { 0xf220, 0x0000 }, + { 0xf221, 0x0000 }, + { 0xf222, 0x0000 }, + { 0xf223, 0x0000 }, + { 0xf224, 0x0000 }, + { 0xf225, 0x0000 }, + { 0xf226, 0x0000 }, + { 0xf227, 0x0000 }, + { 0xf228, 0x0000 }, + { 0xf229, 0x0000 }, + { 0xf22a, 0x0000 }, + { 0xf22b, 0x0000 }, + { 0xf22c, 0x0000 }, + { 0xf22d, 0x0000 }, + { 0xf22e, 0x0000 }, + { 0xf22f, 0x0000 }, + { 0xf230, 0x0000 }, + { 0xf231, 0x0000 }, + { 0xf232, 0x0000 }, + { 0xf233, 0x0000 }, + { 0xf234, 0x0000 }, + { 0xf235, 0x0000 }, + { 0xf236, 0x0000 }, + { 0xf237, 0x0000 }, + { 0xf238, 0x0000 }, + { 0xf239, 0x0000 }, + { 0xf23a, 0x0000 }, + { 0xf23b, 0x0000 }, + { 0xf23c, 0x0000 }, + { 0xf23d, 0x0000 }, + { 0xf23e, 0x0000 }, + { 0xf23f, 0x0000 }, + { 0xf240, 0x0000 }, + { 0xf241, 0x0000 }, + { 0xf242, 0x0000 }, + { 0xf243, 0x0000 }, + { 0xf244, 0x0000 }, + { 0xf245, 0x0000 }, + { 0xf246, 0x0000 }, + { 0xf247, 0x0000 }, + { 0xf248, 0x0000 }, + { 0xf249, 0x0000 }, + { 0xf24a, 0x0000 }, + { 0xf24b, 0x0000 }, + { 0xf24c, 0x0000 }, + { 0xf24d, 0x0000 }, + { 0xf24e, 0x0000 }, + { 0xf24f, 0x0000 }, + { 0xf250, 0x0000 }, + { 0xf251, 0x0000 }, + { 0xf252, 0x0000 }, + { 0xf253, 0x0000 }, + { 0xf254, 0x0000 }, + { 0xf255, 0x0000 }, + { 0xf256, 0x0000 }, + { 0xf257, 0x0000 }, + { 0xf258, 0x0000 }, + { 0xf259, 0x0000 }, + { 0xf25a, 0x0000 }, + { 0xf25b, 0x0000 }, + { 0xf25c, 0x0000 }, + { 0xf25d, 0x0000 }, + { 0xf25e, 0x0000 }, + { 0xf25f, 0x0000 }, + { 0xf260, 0x0000 }, + { 0xf261, 0x0000 }, + { 0xf262, 0x0000 }, + { 0xf263, 0x0000 }, + { 0xf264, 0x0000 }, + { 0xf265, 0x0000 }, + { 0xf266, 0x0000 }, + { 0xf267, 0x0000 }, + { 0xf268, 0x0000 }, + { 0xf269, 0x0000 }, + { 0xf26a, 0x0000 }, + { 0xf26b, 0x0000 }, + { 0xf26c, 0x0000 }, + { 0xf26d, 0x0000 }, + { 0xf26e, 0x0000 }, + { 0xf26f, 0x0000 }, + { 0xf270, 0x0000 }, + { 0xf271, 0x0000 }, + { 0xf272, 0x0000 }, + { 0xf273, 0x0000 }, + { 0xf274, 0x0000 }, + { 0xf275, 0x0000 }, + { 0xf276, 0x0000 }, + { 0xf277, 0x0000 }, + { 0xf278, 0x0000 }, + { 0xf279, 0x0000 }, + { 0xf27a, 0x0000 }, + { 0xf27b, 0x0000 }, + { 0xf27c, 0x0000 }, + { 0xf27d, 0x0000 }, + { 0xf27e, 0x0000 }, + { 0xf27f, 0x0000 }, + { 0xf280, 0x0000 }, + { 0xf281, 0x0000 }, + { 0xf282, 0x0000 }, + { 0xf283, 0x0000 }, + { 0xf284, 0x0000 }, + { 0xf285, 0x0000 }, + { 0xf286, 0x0000 }, + { 0xf287, 0x0000 }, + { 0xf288, 0x0000 }, + { 0xf289, 0x0000 }, + { 0xf28a, 0x0000 }, + { 0xf28b, 0x0000 }, + { 0xf28c, 0x0000 }, + { 0xf28d, 0x0000 }, + { 0xf28e, 0x0000 }, + { 0xf28f, 0x0000 }, + { 0xf290, 0x0000 }, + { 0xf291, 0x0000 }, + { 0xf292, 0x0000 }, + { 0xf293, 0x0000 }, + { 0xf294, 0x0000 }, + { 0xf295, 0x0000 }, + { 0xf296, 0x0000 }, + { 0xf297, 0x0000 }, + { 0xf298, 0x0000 }, + { 0xf299, 0x0000 }, + { 0xf29a, 0x0000 }, + { 0xf29b, 0x0000 }, + { 0xf29c, 0x0000 }, + { 0xf29d, 0x0000 }, + { 0xf29e, 0x0000 }, + { 0xf29f, 0x0000 }, + { 0xf2a0, 0x0000 }, + { 0xf2a1, 0x0000 }, + { 0xf2a2, 0x0000 }, + { 0xf2a3, 0x0000 }, + { 0xf2a4, 0x0000 }, + { 0xf2a5, 0x0000 }, + { 0xf2a6, 0x0000 }, + { 0xf2a7, 0x0000 }, + { 0xf2a8, 0x0000 }, + { 0xf2a9, 0x0000 }, + { 0xf2aa, 0x0000 }, + { 0xf2ab, 0x0000 }, + { 0xf2ac, 0x0000 }, + { 0xf2ad, 0x0000 }, + { 0xf2ae, 0x0000 }, + { 0xf2af, 0x0000 }, + { 0xf2b0, 0x0000 }, + { 0xf2b1, 0x0000 }, + { 0xf2b2, 0x0000 }, + { 0xf2b3, 0x0000 }, + { 0xf2b4, 0x0000 }, + { 0xf2b5, 0x0000 }, + { 0xf2b6, 0x0000 }, + { 0xf2b7, 0x0000 }, + { 0xf2b8, 0x0000 }, + { 0xf2b9, 0x0000 }, + { 0xf2ba, 0x0000 }, + { 0xf2bb, 0x0000 }, + { 0xf2bc, 0x0000 }, + { 0xf2bd, 0x0000 }, + { 0xf2be, 0x0000 }, + { 0xf2bf, 0x0000 }, + { 0xf2c0, 0x0000 }, + { 0xf2c1, 0x0000 }, + { 0xf2c2, 0x0000 }, + { 0xf2c3, 0x0000 }, + { 0xf2c4, 0x0000 }, + { 0xf2c5, 0x0000 }, + { 0xf2c6, 0x0000 }, + { 0xf2c7, 0x0000 }, + { 0xf2c8, 0x0000 }, + { 0xf2c9, 0x0000 }, + { 0xf2ca, 0x0000 }, + { 0xf2cb, 0x0000 }, + { 0xf2cc, 0x0000 }, + { 0xf2cd, 0x0000 }, + { 0xf2ce, 0x0000 }, + { 0xf2cf, 0x0000 }, + { 0xf2d0, 0x0000 }, + { 0xf2d1, 0x0000 }, + { 0xf2d2, 0x0000 }, + { 0xf2d3, 0x0000 }, + { 0xf2d4, 0x0000 }, + { 0xf2d5, 0x0000 }, + { 0xf2d6, 0x0000 }, + { 0xf2d7, 0x0000 }, + { 0xf2d8, 0x0000 }, + { 0xf2d9, 0x0000 }, + { 0xf2da, 0x0000 }, + { 0xf2db, 0x0000 }, + { 0xf2dc, 0x0000 }, + { 0xf2dd, 0x0000 }, + { 0xf2de, 0x0000 }, + { 0xf2df, 0x0000 }, + { 0xf2e0, 0x0000 }, + { 0xf2e1, 0x0000 }, + { 0xf2e2, 0x0000 }, + { 0xf2e3, 0x0000 }, + { 0xf2e4, 0x0000 }, + { 0xf2e5, 0x0000 }, + { 0xf2e6, 0x0000 }, + { 0xf2e7, 0x0000 }, + { 0xf2e8, 0x0000 }, + { 0xf2e9, 0x0000 }, + { 0xf2ea, 0x0000 }, + { 0xf2eb, 0x0000 }, + { 0xf2ec, 0x0000 }, + { 0xf2ed, 0x0000 }, + { 0xf2ee, 0x0000 }, + { 0xf2ef, 0x0000 }, + { 0xf2f0, 0x0000 }, + { 0xf2f1, 0x0000 }, + { 0xf2f2, 0x0000 }, + { 0xf2f3, 0x0000 }, + { 0xf2f4, 0x0000 }, + { 0xf2f5, 0x0000 }, + { 0xf2f6, 0x0000 }, + { 0xf2f7, 0x0000 }, + { 0xf2f8, 0x0000 }, + { 0xf2f9, 0x0000 }, + { 0xf2fa, 0x0000 }, + { 0xf2fb, 0x0000 }, + { 0xf2fc, 0x0000 }, + { 0xf2fd, 0x0000 }, + { 0xf2fe, 0x0000 }, + { 0xf2ff, 0x0000 }, + { 0xf300, 0x0000 }, + { 0xf301, 0x0000 }, + { 0xf302, 0x0000 }, + { 0xf303, 0x0000 }, + { 0xf304, 0x0000 }, + { 0xf305, 0x0000 }, + { 0xf306, 0x0000 }, + { 0xf307, 0x0000 }, + { 0xf308, 0x0000 }, + { 0xf309, 0x0000 }, + { 0xf30a, 0x0000 }, + { 0xf30b, 0x0000 }, + { 0xf30c, 0x0000 }, + { 0xf30d, 0x0000 }, + { 0xf30e, 0x0000 }, + { 0xf30f, 0x0000 }, + { 0xf310, 0x0000 }, + { 0xf311, 0x0000 }, + { 0xf312, 0x0000 }, + { 0xf313, 0x0000 }, + { 0xf314, 0x0000 }, + { 0xf315, 0x0000 }, + { 0xf316, 0x0000 }, + { 0xf317, 0x0000 }, + { 0xf318, 0x0000 }, + { 0xf319, 0x0000 }, + { 0xf31a, 0x0000 }, + { 0xf31b, 0x0000 }, + { 0xf31c, 0x0000 }, + { 0xf31d, 0x0000 }, + { 0xf31e, 0x0000 }, + { 0xf31f, 0x0000 }, + { 0xf320, 0x0000 }, + { 0xf321, 0x0000 }, + { 0xf322, 0x0000 }, + { 0xf323, 0x0000 }, + { 0xf324, 0x0000 }, + { 0xf325, 0x0000 }, + { 0xf326, 0x0000 }, + { 0xf327, 0x0000 }, + { 0xf328, 0x0000 }, + { 0xf329, 0x0000 }, + { 0xf32a, 0x0000 }, + { 0xf32b, 0x0000 }, + { 0xf32c, 0x0000 }, + { 0xf32d, 0x0000 }, + { 0xf32e, 0x0000 }, + { 0xf32f, 0x0000 }, + { 0xf330, 0x0000 }, + { 0xf331, 0x0000 }, + { 0xf332, 0x0000 }, + { 0xf333, 0x0000 }, + { 0xf334, 0x0000 }, + { 0xf335, 0x0000 }, + { 0xf336, 0x0000 }, + { 0xf337, 0x0000 }, + { 0xf338, 0x0000 }, + { 0xf339, 0x0000 }, + { 0xf33a, 0x0000 }, + { 0xf33b, 0x0000 }, + { 0xf33c, 0x0000 }, + { 0xf33d, 0x0000 }, + { 0xf33e, 0x0000 }, + { 0xf33f, 0x0000 }, + { 0xf340, 0x0000 }, + { 0xf341, 0x0000 }, + { 0xf342, 0x0000 }, + { 0xf343, 0x0000 }, + { 0xf344, 0x0000 }, + { 0xf345, 0x0000 }, + { 0xf346, 0x0000 }, + { 0xf347, 0x0000 }, + { 0xf348, 0x0000 }, + { 0xf349, 0x0000 }, + { 0xf34a, 0x0000 }, + { 0xf34b, 0x0000 }, + { 0xf34c, 0x0000 }, + { 0xf34d, 0x0000 }, + { 0xf34e, 0x0000 }, + { 0xf34f, 0x0000 }, + { 0xf350, 0x0000 }, + { 0xf351, 0x0000 }, + { 0xf352, 0x0000 }, + { 0xf353, 0x0000 }, + { 0xf354, 0x0000 }, + { 0xf355, 0x0000 }, + { 0xf356, 0x0000 }, + { 0xf357, 0x0000 }, + { 0xf358, 0x0000 }, + { 0xf359, 0x0000 }, + { 0xf35a, 0x0000 }, + { 0xf35b, 0x0000 }, + { 0xf35c, 0x0000 }, + { 0xf35d, 0x0000 }, + { 0xf35e, 0x0000 }, + { 0xf35f, 0x0000 }, + { 0xf360, 0x0000 }, + { 0xf361, 0x0000 }, + { 0xf362, 0x0000 }, + { 0xf363, 0x0000 }, + { 0xf364, 0x0000 }, + { 0xf365, 0x0000 }, + { 0xf366, 0x0000 }, + { 0xf367, 0x0000 }, + { 0xf368, 0x0000 }, + { 0xf369, 0x0000 }, + { 0xf36a, 0x0000 }, + { 0xf36b, 0x0000 }, + { 0xf36c, 0x0000 }, + { 0xf36d, 0x0000 }, + { 0xf36e, 0x0000 }, + { 0xf36f, 0x0000 }, + { 0xf370, 0x0000 }, + { 0xf371, 0x0000 }, + { 0xf372, 0x0000 }, + { 0xf373, 0x0000 }, + { 0xf374, 0x0000 }, + { 0xf375, 0x0000 }, + { 0xf376, 0x0000 }, + { 0xf377, 0x0000 }, + { 0xf378, 0x0000 }, + { 0xf379, 0x0000 }, + { 0xf37a, 0x0000 }, + { 0xf37b, 0x0000 }, + { 0xf37c, 0x0000 }, + { 0xf37d, 0x0000 }, + { 0xf37e, 0x0000 }, + { 0xf37f, 0x0000 }, + { 0xf380, 0x0000 }, + { 0xf381, 0x0000 }, + { 0xf382, 0x0000 }, + { 0xf383, 0x0000 }, + { 0xf384, 0x0000 }, + { 0xf385, 0x0000 }, + { 0xf386, 0x0000 }, + { 0xf387, 0x0000 }, + { 0xf388, 0x0000 }, + { 0xf389, 0x0000 }, + { 0xf38a, 0x0000 }, + { 0xf38b, 0x0000 }, + { 0xf38c, 0x0000 }, + { 0xf38d, 0x0000 }, + { 0xf38e, 0x0000 }, + { 0xf38f, 0x0000 }, + { 0xf390, 0x0000 }, + { 0xf391, 0x0000 }, + { 0xf392, 0x0000 }, + { 0xf393, 0x0000 }, + { 0xf394, 0x0000 }, + { 0xf395, 0x0000 }, + { 0xf396, 0x0000 }, + { 0xf397, 0x0000 }, + { 0xf398, 0x0000 }, + { 0xf399, 0x0000 }, + { 0xf39a, 0x0000 }, + { 0xf39b, 0x0000 }, + { 0xf39c, 0x0000 }, + { 0xf39d, 0x0000 }, + { 0xf39e, 0x0000 }, + { 0xf39f, 0x0000 }, + { 0xf3a0, 0x0000 }, + { 0xf3a1, 0x0000 }, + { 0xf3a2, 0x0000 }, + { 0xf3a3, 0x0000 }, + { 0xf3a4, 0x0000 }, + { 0xf3a5, 0x0000 }, + { 0xf3a6, 0x0000 }, + { 0xf3a7, 0x0000 }, + { 0xf3a8, 0x0000 }, + { 0xf3a9, 0x0000 }, + { 0xf3aa, 0x0000 }, + { 0xf3ab, 0x0000 }, + { 0xf3ac, 0x0000 }, + { 0xf3ad, 0x0000 }, + { 0xf3ae, 0x0000 }, + { 0xf3af, 0x0000 }, + { 0xf3b0, 0x0000 }, + { 0xf3b1, 0x0000 }, + { 0xf3b2, 0x0000 }, + { 0xf3b3, 0x0000 }, + { 0xf3b4, 0x0000 }, + { 0xf3b5, 0x0000 }, + { 0xf3b6, 0x0000 }, + { 0xf3b7, 0x0000 }, + { 0xf3b8, 0x0000 }, + { 0xf3b9, 0x0000 }, + { 0xf3ba, 0x0000 }, + { 0xf3bb, 0x0000 }, + { 0xf3bc, 0x0000 }, + { 0xf3bd, 0x0000 }, + { 0xf3be, 0x0000 }, + { 0xf3bf, 0x0000 }, + { 0xf3c0, 0x0000 }, + { 0xf3c1, 0x0000 }, + { 0xf3c2, 0x0000 }, + { 0xf3c3, 0x0000 }, + { 0xf3c4, 0x0000 }, + { 0xf3c5, 0x0000 }, + { 0xf3c6, 0x0000 }, + { 0xf3c7, 0x0000 }, + { 0xf3c8, 0x0000 }, + { 0xf3c9, 0x0000 }, + { 0xf3ca, 0x0000 }, + { 0xf3cb, 0x0000 }, + { 0xf3cc, 0x0000 }, + { 0xf3cd, 0x0000 }, + { 0xf3ce, 0x0000 }, + { 0xf3cf, 0x0000 }, + { 0xf3d0, 0x0000 }, + { 0xf3d1, 0x0000 }, + { 0xf3d2, 0x0000 }, + { 0xf3d3, 0x0000 }, + { 0xf3d4, 0x0000 }, + { 0xf3d5, 0x0000 }, + { 0xf3d6, 0x0000 }, + { 0xf3d7, 0x0000 }, + { 0xf3d8, 0x0000 }, + { 0xf3d9, 0x0000 }, + { 0xf3da, 0x0000 }, + { 0xf3db, 0x0000 }, + { 0xf3dc, 0x0000 }, + { 0xf3dd, 0x0000 }, + { 0xf3de, 0x0000 }, + { 0xf3df, 0x0000 }, + { 0xf3e0, 0x0000 }, + { 0xf3e1, 0x0000 }, + { 0xf3e2, 0x0000 }, + { 0xf3e3, 0x0000 }, + { 0xf3e4, 0x0000 }, + { 0xf3e5, 0x0000 }, + { 0xf3e6, 0x0000 }, + { 0xf3e7, 0x0000 }, + { 0xf3e8, 0x0000 }, + { 0xf3e9, 0x0000 }, + { 0xf3ea, 0x0000 }, + { 0xf3eb, 0x0000 }, + { 0xf3ec, 0x0000 }, + { 0xf3ed, 0x0000 }, + { 0xf3ee, 0x0000 }, + { 0xf3ef, 0x0000 }, + { 0xf3f0, 0x0000 }, + { 0xf3f1, 0x0000 }, + { 0xf3f2, 0x0000 }, + { 0xf3f3, 0x0000 }, + { 0xf3f4, 0x0000 }, + { 0xf3f5, 0x0000 }, + { 0xf3f6, 0x0000 }, + { 0xf3f7, 0x0000 }, + { 0xf3f8, 0x0000 }, + { 0xf3f9, 0x0000 }, + { 0xf3fa, 0x0000 }, + { 0xf3fb, 0x0000 }, + { 0xf3fc, 0x0000 }, + { 0xf3fd, 0x0000 }, + { 0xf3fe, 0x0000 }, + { 0xf3ff, 0x0000 }, + { 0xf400, 0x0000 }, + { 0xf401, 0x0000 }, + { 0xf402, 0x0000 }, + { 0xf403, 0x0000 }, + { 0xf404, 0x0000 }, + { 0xf405, 0x0000 }, + { 0xf406, 0x0000 }, + { 0xf407, 0x0000 }, + { 0xf408, 0x0000 }, + { 0xf409, 0x0000 }, + { 0xf40a, 0x0000 }, + { 0xf40b, 0x0000 }, + { 0xf40c, 0x0000 }, + { 0xf40d, 0x0000 }, + { 0xf40e, 0x0000 }, + { 0xf40f, 0x0000 }, + { 0xf410, 0x0000 }, + { 0xf411, 0x0000 }, + { 0xf412, 0x0000 }, + { 0xf413, 0x0000 }, + { 0xf414, 0x0000 }, + { 0xf415, 0x0000 }, + { 0xf416, 0x0000 }, + { 0xf417, 0x0000 }, + { 0xf418, 0x0000 }, + { 0xf419, 0x0000 }, + { 0xf41a, 0x0000 }, + { 0xf41b, 0x0000 }, + { 0xf41c, 0x0000 }, + { 0xf41d, 0x0000 }, + { 0xf41e, 0x0000 }, + { 0xf41f, 0x0000 }, + { 0xf420, 0x0000 }, + { 0xf421, 0x0000 }, + { 0xf422, 0x0000 }, + { 0xf423, 0x0000 }, + { 0xf424, 0x0000 }, + { 0xf425, 0x0000 }, + { 0xf426, 0x0000 }, + { 0xf427, 0x0000 }, + { 0xf428, 0x0000 }, + { 0xf429, 0x0000 }, + { 0xf42a, 0x0000 }, + { 0xf42b, 0x0000 }, + { 0xf42c, 0x0000 }, + { 0xf42d, 0x0000 }, + { 0xf42e, 0x0000 }, + { 0xf42f, 0x0000 }, + { 0xf430, 0x0000 }, + { 0xf431, 0x0000 }, + { 0xf432, 0x0000 }, + { 0xf433, 0x0000 }, + { 0xf434, 0x0000 }, + { 0xf435, 0x0000 }, + { 0xf436, 0x0000 }, + { 0xf437, 0x0000 }, + { 0xf438, 0x0000 }, + { 0xf439, 0x0000 }, + { 0xf43a, 0x0000 }, + { 0xf43b, 0x0000 }, + { 0xf43c, 0x0000 }, + { 0xf43d, 0x0000 }, + { 0xf43e, 0x0000 }, + { 0xf43f, 0x0000 }, + { 0xf440, 0x0000 }, + { 0xf441, 0x0000 }, + { 0xf442, 0x0000 }, + { 0xf443, 0x0000 }, + { 0xf444, 0x0000 }, + { 0xf445, 0x0000 }, + { 0xf446, 0x0000 }, + { 0xf447, 0x0000 }, + { 0xf448, 0x0000 }, + { 0xf449, 0x0000 }, + { 0xf44a, 0x0000 }, + { 0xf44b, 0x0000 }, + { 0xf44c, 0x0000 }, + { 0xf44d, 0x0000 }, + { 0xf44e, 0x0000 }, + { 0xf44f, 0x0000 }, + { 0xf450, 0x0000 }, + { 0xf451, 0x0000 }, + { 0xf452, 0x0000 }, + { 0xf453, 0x0000 }, + { 0xf454, 0x0000 }, + { 0xf455, 0x0000 }, + { 0xf456, 0x0000 }, + { 0xf457, 0x0000 }, + { 0xf458, 0x0000 }, + { 0xf459, 0x0000 }, + { 0xf45a, 0x0000 }, + { 0xf45b, 0x0000 }, + { 0xf45c, 0x0000 }, + { 0xf45d, 0x0000 }, + { 0xf45e, 0x0000 }, + { 0xf45f, 0x0000 }, + { 0xf460, 0x0000 }, + { 0xf461, 0x0000 }, + { 0xf462, 0x0000 }, + { 0xf463, 0x0000 }, + { 0xf464, 0x0000 }, + { 0xf465, 0x0000 }, + { 0xf466, 0x0000 }, + { 0xf467, 0x0000 }, + { 0xf468, 0x0000 }, + { 0xf469, 0x0000 }, + { 0xf46a, 0x0000 }, + { 0xf46b, 0x0000 }, + { 0xf46c, 0x0000 }, + { 0xf46d, 0x0000 }, + { 0xf46e, 0x0000 }, + { 0xf46f, 0x0000 }, + { 0xf470, 0x0000 }, + { 0xf471, 0x0000 }, + { 0xf472, 0x0000 }, + { 0xf473, 0x0000 }, + { 0xf474, 0x0000 }, + { 0xf475, 0x0000 }, + { 0xf476, 0x0000 }, + { 0xf477, 0x0000 }, + { 0xf478, 0x0000 }, + { 0xf479, 0x0000 }, + { 0xf47a, 0x0000 }, + { 0xf47b, 0x0000 }, + { 0xf47c, 0x0000 }, + { 0xf47d, 0x0000 }, + { 0xf47e, 0x0000 }, + { 0xf47f, 0x0000 }, + { 0xf480, 0x0000 }, + { 0xf481, 0x0000 }, + { 0xf482, 0x0000 }, + { 0xf483, 0x0000 }, + { 0xf484, 0x0000 }, + { 0xf485, 0x0000 }, + { 0xf486, 0x0000 }, + { 0xf487, 0x0000 }, + { 0xf488, 0x0000 }, + { 0xf489, 0x0000 }, + { 0xf48a, 0x0000 }, + { 0xf48b, 0x0000 }, + { 0xf48c, 0x0000 }, + { 0xf48d, 0x0000 }, + { 0xf48e, 0x0000 }, + { 0xf48f, 0x0000 }, + { 0xf490, 0x0000 }, + { 0xf491, 0x0000 }, + { 0xf492, 0x0000 }, + { 0xf493, 0x0000 }, + { 0xf494, 0x0000 }, + { 0xf495, 0x0000 }, + { 0xf496, 0x0000 }, + { 0xf497, 0x0000 }, + { 0xf498, 0x0000 }, + { 0xf499, 0x0000 }, + { 0xf49a, 0x0000 }, + { 0xf49b, 0x0000 }, + { 0xf49c, 0x0000 }, + { 0xf49d, 0x0000 }, + { 0xf49e, 0x0000 }, + { 0xf49f, 0x0000 }, + { 0xf4a0, 0x0000 }, + { 0xf4a1, 0x0000 }, + { 0xf4a2, 0x0000 }, + { 0xf4a3, 0x0000 }, + { 0xf4a4, 0x0000 }, + { 0xf4a5, 0x0000 }, + { 0xf4a6, 0x0000 }, + { 0xf4a7, 0x0000 }, + { 0xf4a8, 0x0000 }, + { 0xf4a9, 0x0000 }, + { 0xf4aa, 0x0000 }, + { 0xf4ab, 0x0000 }, + { 0xf4ac, 0x0000 }, + { 0xf4ad, 0x0000 }, + { 0xf4ae, 0x0000 }, + { 0xf4af, 0x0000 }, + { 0xf4b0, 0x0000 }, + { 0xf4b1, 0x0000 }, + { 0xf4b2, 0x0000 }, + { 0xf4b3, 0x0000 }, + { 0xf4b4, 0x0000 }, + { 0xf4b5, 0x0000 }, + { 0xf4b6, 0x0000 }, + { 0xf4b7, 0x0000 }, + { 0xf4b8, 0x0000 }, + { 0xf4b9, 0x0000 }, + { 0xf4ba, 0x0000 }, + { 0xf4bb, 0x0000 }, + { 0xf4bc, 0x0000 }, + { 0xf4bd, 0x0000 }, + { 0xf4be, 0x0000 }, + { 0xf4bf, 0x0000 }, + { 0xf4c0, 0x0000 }, + { 0xf4c1, 0x0000 }, + { 0xf4c2, 0x0000 }, + { 0xf4c3, 0x0000 }, + { 0xf4c4, 0x0000 }, + { 0xf4c5, 0x0000 }, + { 0xf4c6, 0x0000 }, + { 0xf4c7, 0x0000 }, + { 0xf4c8, 0x0000 }, + { 0xf4c9, 0x0000 }, + { 0xf4ca, 0x0000 }, + { 0xf4cb, 0x0000 }, + { 0xf4cc, 0x0000 }, + { 0xf4cd, 0x0000 }, + { 0xf4ce, 0x0000 }, + { 0xf4cf, 0x0000 }, + { 0xf4d0, 0x0000 }, + { 0xf4d1, 0x0000 }, + { 0xf4d2, 0x0000 }, + { 0xf4d3, 0x0000 }, + { 0xf4d4, 0x0000 }, + { 0xf4d5, 0x0000 }, + { 0xf4d6, 0x0000 }, + { 0xf4d7, 0x0000 }, + { 0xf4d8, 0x0000 }, + { 0xf4d9, 0x0000 }, + { 0xf4da, 0x0000 }, + { 0xf4db, 0x0000 }, + { 0xf4dc, 0x0000 }, + { 0xf4dd, 0x0000 }, + { 0xf4de, 0x0000 }, + { 0xf4df, 0x0000 }, + { 0xf4e0, 0x0000 }, + { 0xf4e1, 0x0000 }, + { 0xf4e2, 0x0000 }, + { 0xf4e3, 0x0000 }, + { 0xf4e4, 0x0000 }, + { 0xf4e5, 0x0000 }, + { 0xf4e6, 0x0000 }, + { 0xf4e7, 0x0000 }, + { 0xf4e8, 0x0000 }, + { 0xf4e9, 0x0000 }, + { 0xf4ea, 0x0000 }, + { 0xf4eb, 0x0000 }, + { 0xf4ec, 0x0000 }, + { 0xf4ed, 0x0000 }, + { 0xf4ee, 0x0000 }, + { 0xf4ef, 0x0000 }, + { 0xf4f0, 0x0000 }, + { 0xf4f1, 0x0000 }, + { 0xf4f2, 0x0000 }, + { 0xf4f3, 0x0000 }, + { 0xf4f4, 0x0000 }, + { 0xf4f5, 0x0000 }, + { 0xf4f6, 0x0000 }, + { 0xf4f7, 0x0000 }, + { 0xf4f8, 0x0000 }, + { 0xf4f9, 0x0000 }, + { 0xf4fa, 0x0000 }, + { 0xf4fb, 0x0000 }, + { 0xf4fc, 0x0000 }, + { 0xf4fd, 0x0000 }, + { 0xf4fe, 0x0000 }, + { 0xf4ff, 0x0000 }, + { 0xf500, 0x0000 }, + { 0xf501, 0x0000 }, + { 0xf502, 0x0000 }, + { 0xf503, 0x0000 }, + { 0xf504, 0x0000 }, + { 0xf505, 0x0000 }, + { 0xf506, 0x0000 }, + { 0xf507, 0x0000 }, + { 0xf508, 0x0000 }, + { 0xf509, 0x0000 }, + { 0xf50a, 0x0000 }, + { 0xf50b, 0x0000 }, + { 0xf50c, 0x0000 }, + { 0xf50d, 0x0000 }, + { 0xf50e, 0x0000 }, + { 0xf50f, 0x0000 }, + { 0xf510, 0x0000 }, + { 0xf511, 0x0000 }, + { 0xf512, 0x0000 }, + { 0xf513, 0x0000 }, + { 0xf514, 0x0000 }, + { 0xf515, 0x0000 }, + { 0xf516, 0x0000 }, + { 0xf517, 0x0000 }, + { 0xf518, 0x0000 }, + { 0xf519, 0x0000 }, + { 0xf51a, 0x0000 }, + { 0xf51b, 0x0000 }, + { 0xf51c, 0x0000 }, + { 0xf51d, 0x0000 }, + { 0xf51e, 0x0000 }, + { 0xf51f, 0x0000 }, + { 0xf520, 0x0000 }, + { 0xf521, 0x0000 }, + { 0xf522, 0x0000 }, + { 0xf523, 0x0000 }, + { 0xf524, 0x0000 }, + { 0xf525, 0x0000 }, + { 0xf526, 0x0000 }, + { 0xf527, 0x0000 }, + { 0xf528, 0x0000 }, + { 0xf529, 0x0000 }, + { 0xf52a, 0x0000 }, + { 0xf52b, 0x0000 }, + { 0xf52c, 0x0000 }, + { 0xf52d, 0x0000 }, + { 0xf52e, 0x0000 }, + { 0xf52f, 0x0000 }, + { 0xf530, 0x0000 }, + { 0xf531, 0x0000 }, + { 0xf532, 0x0000 }, + { 0xf533, 0x0000 }, + { 0xf534, 0x0000 }, + { 0xf535, 0x0000 }, + { 0xf536, 0x0000 }, + { 0xf537, 0x0000 }, + { 0xf538, 0x0000 }, + { 0xf539, 0x0000 }, + { 0xf53a, 0x0000 }, + { 0xf53b, 0x0000 }, + { 0xf53c, 0x0000 }, + { 0xf53d, 0x0000 }, + { 0xf53e, 0x0000 }, + { 0xf53f, 0x0000 }, + { 0xf540, 0x0000 }, + { 0xf541, 0x0000 }, + { 0xf542, 0x0000 }, + { 0xf543, 0x0000 }, + { 0xf544, 0x0000 }, + { 0xf545, 0x0000 }, + { 0xf546, 0x0000 }, + { 0xf547, 0x0000 }, + { 0xf548, 0x0000 }, + { 0xf549, 0x0000 }, + { 0xf54a, 0x0000 }, + { 0xf54b, 0x0000 }, + { 0xf54c, 0x0000 }, + { 0xf54d, 0x0000 }, + { 0xf54e, 0x0000 }, + { 0xf54f, 0x0000 }, + { 0xf550, 0x0000 }, + { 0xf551, 0x0000 }, + { 0xf552, 0x0000 }, + { 0xf553, 0x0000 }, + { 0xf554, 0x0000 }, + { 0xf555, 0x0000 }, + { 0xf556, 0x0000 }, + { 0xf557, 0x0000 }, + { 0xf558, 0x0000 }, + { 0xf559, 0x0000 }, + { 0xf55a, 0x0000 }, + { 0xf55b, 0x0000 }, + { 0xf55c, 0x0000 }, + { 0xf55d, 0x0000 }, + { 0xf55e, 0x0000 }, + { 0xf55f, 0x0000 }, + { 0xf560, 0x0000 }, + { 0xf561, 0x0000 }, + { 0xf562, 0x0000 }, + { 0xf563, 0x0000 }, + { 0xf564, 0x0000 }, + { 0xf565, 0x0000 }, + { 0xf566, 0x0000 }, + { 0xf567, 0x0000 }, + { 0xf568, 0x0000 }, + { 0xf569, 0x0000 }, + { 0xf56a, 0x0000 }, + { 0xf56b, 0x0000 }, + { 0xf56c, 0x0000 }, + { 0xf56d, 0x0000 }, + { 0xf56e, 0x0000 }, + { 0xf56f, 0x0000 }, + { 0xf570, 0x0000 }, + { 0xf571, 0x0000 }, + { 0xf572, 0x0000 }, + { 0xf573, 0x0000 }, + { 0xf574, 0x0000 }, + { 0xf575, 0x0000 }, + { 0xf576, 0x0000 }, + { 0xf577, 0x0000 }, + { 0xf578, 0x0000 }, + { 0xf579, 0x0000 }, + { 0xf57a, 0x0000 }, + { 0xf57b, 0x0000 }, + { 0xf57c, 0x0000 }, + { 0xf57d, 0x0000 }, + { 0xf57e, 0x0000 }, + { 0xf57f, 0x0000 }, + { 0xf580, 0x0000 }, + { 0xf581, 0x0000 }, + { 0xf582, 0x0000 }, + { 0xf583, 0x0000 }, + { 0xf584, 0x0000 }, + { 0xf585, 0x0000 }, + { 0xf586, 0x0000 }, + { 0xf587, 0x0000 }, + { 0xf588, 0x0000 }, + { 0xf589, 0x0000 }, + { 0xf58a, 0x0000 }, + { 0xf58b, 0x0000 }, + { 0xf58c, 0x0000 }, + { 0xf58d, 0x0000 }, + { 0xf58e, 0x0000 }, + { 0xf58f, 0x0000 }, + { 0xf590, 0x0000 }, + { 0xf591, 0x0000 }, + { 0xf592, 0x0000 }, + { 0xf593, 0x0000 }, + { 0xf594, 0x0000 }, + { 0xf595, 0x0000 }, + { 0xf596, 0x0000 }, + { 0xf597, 0x0000 }, + { 0xf598, 0x0000 }, + { 0xf599, 0x0000 }, + { 0xf59a, 0x0000 }, + { 0xf59b, 0x0000 }, + { 0xf59c, 0x0000 }, + { 0xf59d, 0x0000 }, + { 0xf59e, 0x0000 }, + { 0xf59f, 0x0000 }, + { 0xf5a0, 0x0000 }, + { 0xf5a1, 0x0000 }, + { 0xf5a2, 0x0000 }, + { 0xf5a3, 0x0000 }, + { 0xf5a4, 0x0000 }, + { 0xf5a5, 0x0000 }, + { 0xf5a6, 0x0000 }, + { 0xf5a7, 0x0000 }, + { 0xf5a8, 0x0000 }, + { 0xf5a9, 0x0000 }, + { 0xf5aa, 0x0000 }, + { 0xf5ab, 0x0000 }, + { 0xf5ac, 0x0000 }, + { 0xf5ad, 0x0000 }, + { 0xf5ae, 0x0000 }, + { 0xf5af, 0x0000 }, + { 0xf5b0, 0x0000 }, + { 0xf5b1, 0x0000 }, + { 0xf5b2, 0x0000 }, + { 0xf5b3, 0x0000 }, + { 0xf5b4, 0x0000 }, + { 0xf5b5, 0x0000 }, + { 0xf5b6, 0x0000 }, + { 0xf5b7, 0x0000 }, + { 0xf5b8, 0x0000 }, + { 0xf5b9, 0x0000 }, + { 0xf5ba, 0x0000 }, + { 0xf5bb, 0x0000 }, + { 0xf5bc, 0x0000 }, + { 0xf5bd, 0x0000 }, + { 0xf5be, 0x0000 }, + { 0xf5bf, 0x0000 }, + { 0xf5c0, 0x0000 }, + { 0xf5c1, 0x0000 }, + { 0xf5c2, 0x0000 }, + { 0xf5c3, 0x0000 }, + { 0xf5c4, 0x0000 }, + { 0xf5c5, 0x0000 }, + { 0xf5c6, 0x0000 }, + { 0xf5c7, 0x0000 }, + { 0xf5c8, 0x0000 }, + { 0xf5c9, 0x0000 }, + { 0xf5ca, 0x0000 }, + { 0xf5cb, 0x0000 }, + { 0xf5cc, 0x0000 }, + { 0xf5cd, 0x0000 }, + { 0xf5ce, 0x0000 }, + { 0xf5cf, 0x0000 }, + { 0xf5d0, 0x0000 }, + { 0xf5d1, 0x0000 }, + { 0xf5d2, 0x0000 }, + { 0xf5d3, 0x0000 }, + { 0xf5d4, 0x0000 }, + { 0xf5d5, 0x0000 }, + { 0xf5d6, 0x0000 }, + { 0xf5d7, 0x0000 }, + { 0xf5d8, 0x0000 }, + { 0xf5d9, 0x0000 }, + { 0xf5da, 0x0000 }, + { 0xf5db, 0x0000 }, + { 0xf5dc, 0x0000 }, + { 0xf5dd, 0x0000 }, + { 0xf5de, 0x0000 }, + { 0xf5df, 0x0000 }, + { 0xf5e0, 0x0000 }, + { 0xf5e1, 0x0000 }, + { 0xf5e2, 0x0000 }, + { 0xf5e3, 0x0000 }, + { 0xf5e4, 0x0000 }, + { 0xf5e5, 0x0000 }, + { 0xf5e6, 0x0000 }, + { 0xf5e7, 0x0000 }, + { 0xf5e8, 0x0000 }, + { 0xf5e9, 0x0000 }, + { 0xf5ea, 0x0000 }, + { 0xf5eb, 0x0000 }, + { 0xf5ec, 0x0000 }, + { 0xf5ed, 0x0000 }, + { 0xf5ee, 0x0000 }, + { 0xf5ef, 0x0000 }, + { 0xf5f0, 0x0000 }, + { 0xf5f1, 0x0000 }, + { 0xf5f2, 0x0000 }, + { 0xf5f3, 0x0000 }, + { 0xf5f4, 0x0000 }, + { 0xf5f5, 0x0000 }, + { 0xf5f6, 0x0000 }, + { 0xf5f7, 0x0000 }, + { 0xf5f8, 0x0000 }, + { 0xf5f9, 0x0000 }, + { 0xf5fa, 0x0000 }, + { 0xf5fb, 0x0000 }, + { 0xf5fc, 0x0000 }, + { 0xf5fd, 0x0000 }, + { 0xf5fe, 0x0000 }, + { 0xf5ff, 0x0000 }, + { 0xf600, 0x0000 }, + { 0xf601, 0x0000 }, + { 0xf602, 0x0000 }, + { 0xf603, 0x0000 }, + { 0xf604, 0x0000 }, + { 0xf605, 0x0000 }, + { 0xf606, 0x0000 }, + { 0xf607, 0x0000 }, + { 0xf608, 0x0000 }, + { 0xf609, 0x0000 }, + { 0xf60a, 0x0000 }, + { 0xf60b, 0x0000 }, + { 0xf60c, 0x0000 }, + { 0xf60d, 0x0000 }, + { 0xf60e, 0x0000 }, + { 0xf60f, 0x0000 }, + { 0xf610, 0x0000 }, + { 0xf611, 0x0000 }, + { 0xf612, 0x0000 }, + { 0xf613, 0x0000 }, + { 0xf614, 0x0000 }, + { 0xf615, 0x0000 }, + { 0xf616, 0x0000 }, + { 0xf617, 0x0000 }, + { 0xf618, 0x0000 }, + { 0xf619, 0x0000 }, + { 0xf61a, 0x0000 }, + { 0xf61b, 0x0000 }, + { 0xf61c, 0x0000 }, + { 0xf61d, 0x0000 }, + { 0xf61e, 0x0000 }, + { 0xf61f, 0x0000 }, + { 0xf620, 0x0000 }, + { 0xf621, 0x0000 }, + { 0xf622, 0x0000 }, + { 0xf623, 0x0000 }, + { 0xf624, 0x0000 }, + { 0xf625, 0x0000 }, + { 0xf626, 0x0000 }, + { 0xf627, 0x0000 }, + { 0xf628, 0x0000 }, + { 0xf629, 0x0000 }, + { 0xf62a, 0x0000 }, + { 0xf62b, 0x0000 }, + { 0xf62c, 0x0000 }, + { 0xf62d, 0x0000 }, + { 0xf62e, 0x0000 }, + { 0xf62f, 0x0000 }, + { 0xf630, 0x0000 }, + { 0xf631, 0x0000 }, + { 0xf632, 0x0000 }, + { 0xf633, 0x0000 }, + { 0xf634, 0x0000 }, + { 0xf635, 0x0000 }, + { 0xf636, 0x0000 }, + { 0xf637, 0x0000 }, + { 0xf638, 0x0000 }, + { 0xf639, 0x0000 }, + { 0xf63a, 0x0000 }, + { 0xf63b, 0x0000 }, + { 0xf63c, 0x0000 }, + { 0xf63d, 0x0000 }, + { 0xf63e, 0x0000 }, + { 0xf63f, 0x0000 }, + { 0xf640, 0x0000 }, + { 0xf641, 0x0000 }, + { 0xf642, 0x0000 }, + { 0xf643, 0x0000 }, + { 0xf644, 0x0000 }, + { 0xf645, 0x0000 }, + { 0xf646, 0x0000 }, + { 0xf647, 0x0000 }, + { 0xf648, 0x0000 }, + { 0xf649, 0x0000 }, + { 0xf64a, 0x0000 }, + { 0xf64b, 0x0000 }, + { 0xf64c, 0x0000 }, + { 0xf64d, 0x0000 }, + { 0xf64e, 0x0000 }, + { 0xf64f, 0x0000 }, + { 0xf650, 0x0000 }, + { 0xf651, 0x0000 }, + { 0xf652, 0x0000 }, + { 0xf653, 0x0000 }, + { 0xf654, 0x0000 }, + { 0xf655, 0x0000 }, + { 0xf656, 0x0000 }, + { 0xf657, 0x0000 }, + { 0xf658, 0x0000 }, + { 0xf659, 0x0000 }, + { 0xf65a, 0x0000 }, + { 0xf65b, 0x0000 }, + { 0xf65c, 0x0000 }, + { 0xf65d, 0x0000 }, + { 0xf65e, 0x0000 }, + { 0xf65f, 0x0000 }, + { 0xf660, 0x0000 }, + { 0xf661, 0x0000 }, + { 0xf662, 0x0000 }, + { 0xf663, 0x0000 }, + { 0xf664, 0x0000 }, + { 0xf665, 0x0000 }, + { 0xf666, 0x0000 }, + { 0xf667, 0x0000 }, + { 0xf668, 0x0000 }, + { 0xf669, 0x0000 }, + { 0xf66a, 0x0000 }, + { 0xf66b, 0x0000 }, + { 0xf66c, 0x0000 }, + { 0xf66d, 0x0000 }, + { 0xf66e, 0x0000 }, + { 0xf66f, 0x0000 }, + { 0xf670, 0x0000 }, + { 0xf671, 0x0000 }, + { 0xf672, 0x0000 }, + { 0xf673, 0x0000 }, + { 0xf674, 0x0000 }, + { 0xf675, 0x0000 }, + { 0xf676, 0x0000 }, + { 0xf677, 0x0000 }, + { 0xf678, 0x0000 }, + { 0xf679, 0x0000 }, + { 0xf67a, 0x0000 }, + { 0xf67b, 0x0000 }, + { 0xf67c, 0x0000 }, + { 0xf67d, 0x0000 }, + { 0xf67e, 0x0000 }, + { 0xf67f, 0x0000 }, + { 0xf680, 0x0000 }, + { 0xf681, 0x0000 }, + { 0xf682, 0x0000 }, + { 0xf683, 0x0000 }, + { 0xf684, 0x0000 }, + { 0xf685, 0x0000 }, + { 0xf686, 0x0000 }, + { 0xf687, 0x0000 }, + { 0xf688, 0x0000 }, + { 0xf689, 0x0000 }, + { 0xf68a, 0x0000 }, + { 0xf68b, 0x0000 }, + { 0xf68c, 0x0000 }, + { 0xf68d, 0x0000 }, + { 0xf68e, 0x0000 }, + { 0xf68f, 0x0000 }, + { 0xf690, 0x0000 }, + { 0xf691, 0x0000 }, + { 0xf692, 0x0000 }, + { 0xf693, 0x0000 }, + { 0xf694, 0x0000 }, + { 0xf695, 0x0000 }, + { 0xf696, 0x0000 }, + { 0xf697, 0x0000 }, + { 0xf698, 0x0000 }, + { 0xf699, 0x0000 }, + { 0xf69a, 0x0000 }, + { 0xf69b, 0x0000 }, + { 0xf69c, 0x0000 }, + { 0xf69d, 0x0000 }, + { 0xf69e, 0x0000 }, + { 0xf69f, 0x0000 }, + { 0xf6a0, 0x0000 }, + { 0xf6a1, 0x0000 }, + { 0xf6a2, 0x0000 }, + { 0xf6a3, 0x0000 }, + { 0xf6a4, 0x0000 }, + { 0xf6a5, 0x0000 }, + { 0xf6a6, 0x0000 }, + { 0xf6a7, 0x0000 }, + { 0xf6a8, 0x0000 }, + { 0xf6a9, 0x0000 }, + { 0xf6aa, 0x0000 }, + { 0xf6ab, 0x0000 }, + { 0xf6ac, 0x0000 }, + { 0xf6ad, 0x0000 }, + { 0xf6ae, 0x0000 }, + { 0xf6af, 0x0000 }, + { 0xf6b0, 0x0000 }, + { 0xf6b1, 0x0000 }, + { 0xf6b2, 0x0000 }, + { 0xf6b3, 0x0000 }, + { 0xf6b4, 0x0000 }, + { 0xf6b5, 0x0000 }, + { 0xf6b6, 0x0000 }, + { 0xf6b7, 0x0000 }, + { 0xf6b8, 0x0000 }, + { 0xf6b9, 0x0000 }, + { 0xf6ba, 0x0000 }, + { 0xf6bb, 0x0000 }, + { 0xf6bc, 0x0000 }, + { 0xf6bd, 0x0000 }, + { 0xf6be, 0x0000 }, + { 0xf6bf, 0x0000 }, + { 0xf6c0, 0x0000 }, + { 0xf6c1, 0x0000 }, + { 0xf6c2, 0x0000 }, + { 0xf6c3, 0x0000 }, + { 0xf6c4, 0x0000 }, + { 0xf6c5, 0x0000 }, + { 0xf6c6, 0x0000 }, + { 0xf6c7, 0x0000 }, + { 0xf6c8, 0x0000 }, + { 0xf6c9, 0x0000 }, + { 0xf6ca, 0x0000 }, + { 0xf6cb, 0x0000 }, + { 0xf6cc, 0x0000 }, + { 0xf6cd, 0x0000 }, + { 0xf6ce, 0x0000 }, + { 0xf6cf, 0x0000 }, + { 0xf6d0, 0x0000 }, + { 0xf6d1, 0x0000 }, + { 0xf6d2, 0x0000 }, + { 0xf6d3, 0x0000 }, + { 0xf6d4, 0x0000 }, + { 0xf6d5, 0x0000 }, + { 0xf6d6, 0x0000 }, + { 0xf6d7, 0x0000 }, + { 0xf6d8, 0x0000 }, + { 0xf6d9, 0x0000 }, + { 0xf6da, 0x0000 }, + { 0xf6db, 0x0000 }, + { 0xf6dc, 0x0000 }, + { 0xf6dd, 0x0000 }, + { 0xf6de, 0x0000 }, + { 0xf6df, 0x0000 }, + { 0xf6e0, 0x0000 }, + { 0xf6e1, 0x0000 }, + { 0xf6e2, 0x0000 }, + { 0xf6e3, 0x0000 }, + { 0xf6e4, 0x0000 }, + { 0xf6e5, 0x0000 }, + { 0xf6e6, 0x0000 }, + { 0xf6e7, 0x0000 }, + { 0xf6e8, 0x0000 }, + { 0xf6e9, 0x0000 }, + { 0xf6ea, 0x0000 }, + { 0xf6eb, 0x0000 }, + { 0xf6ec, 0x0000 }, + { 0xf6ed, 0x0000 }, + { 0xf6ee, 0x0000 }, + { 0xf6ef, 0x0000 }, + { 0xf6f0, 0x0000 }, + { 0xf6f1, 0x0000 }, + { 0xf6f2, 0x0000 }, + { 0xf6f3, 0x0000 }, + { 0xf6f4, 0x0000 }, + { 0xf6f5, 0x0000 }, + { 0xf6f6, 0x0000 }, + { 0xf6f7, 0x0000 }, + { 0xf6f8, 0x0000 }, + { 0xf6f9, 0x0000 }, + { 0xf6fa, 0x0000 }, + { 0xf6fb, 0x0000 }, + { 0xf6fc, 0x0000 }, + { 0xf6fd, 0x0000 }, + { 0xf6fe, 0x0000 }, + { 0xf6ff, 0x0000 }, + { 0xf700, 0x0000 }, + { 0xf701, 0x0000 }, + { 0xf702, 0x0000 }, + { 0xf703, 0x0000 }, + { 0xf704, 0x0000 }, + { 0xf705, 0x0000 }, + { 0xf706, 0x0000 }, + { 0xf707, 0x0000 }, + { 0xf708, 0x0000 }, + { 0xf709, 0x0000 }, + { 0xf70a, 0x0000 }, + { 0xf70b, 0x0000 }, + { 0xf70c, 0x0000 }, + { 0xf70d, 0x0000 }, + { 0xf70e, 0x0000 }, + { 0xf70f, 0x0000 }, + { 0xf710, 0x0000 }, + { 0xf711, 0x0000 }, + { 0xf712, 0x0000 }, + { 0xf713, 0x0000 }, + { 0xf714, 0x0000 }, + { 0xf715, 0x0000 }, + { 0xf716, 0x0000 }, + { 0xf717, 0x0000 }, + { 0xf718, 0x0000 }, + { 0xf719, 0x0000 }, + { 0xf71a, 0x0000 }, + { 0xf71b, 0x0000 }, + { 0xf71c, 0x0000 }, + { 0xf71d, 0x0000 }, + { 0xf71e, 0x0000 }, + { 0xf71f, 0x0000 }, + { 0xf720, 0x0000 }, + { 0xf721, 0x0000 }, + { 0xf722, 0x0000 }, + { 0xf723, 0x0000 }, + { 0xf724, 0x0000 }, + { 0xf725, 0x0000 }, + { 0xf726, 0x0000 }, + { 0xf727, 0x0000 }, + { 0xf728, 0x0000 }, + { 0xf729, 0x0000 }, + { 0xf72a, 0x0000 }, + { 0xf72b, 0x0000 }, + { 0xf72c, 0x0000 }, + { 0xf72d, 0x0000 }, + { 0xf72e, 0x0000 }, + { 0xf72f, 0x0000 }, + { 0xf730, 0x0000 }, + { 0xf731, 0x0000 }, + { 0xf732, 0x0000 }, + { 0xf733, 0x0000 }, + { 0xf734, 0x0000 }, + { 0xf735, 0x0000 }, + { 0xf736, 0x0000 }, + { 0xf737, 0x0000 }, + { 0xf738, 0x0000 }, + { 0xf739, 0x0000 }, + { 0xf73a, 0x0000 }, + { 0xf73b, 0x0000 }, + { 0xf73c, 0x0000 }, + { 0xf73d, 0x0000 }, + { 0xf73e, 0x0000 }, + { 0xf73f, 0x0000 }, + { 0xf740, 0x0000 }, + { 0xf741, 0x0000 }, + { 0xf742, 0x0000 }, + { 0xf743, 0x0000 }, + { 0xf744, 0x0000 }, + { 0xf745, 0x0000 }, + { 0xf746, 0x0000 }, + { 0xf747, 0x0000 }, + { 0xf748, 0x0000 }, + { 0xf749, 0x0000 }, + { 0xf74a, 0x0000 }, + { 0xf74b, 0x0000 }, + { 0xf74c, 0x0000 }, + { 0xf74d, 0x0000 }, + { 0xf74e, 0x0000 }, + { 0xf74f, 0x0000 }, + { 0xf750, 0x0000 }, + { 0xf751, 0x0000 }, + { 0xf752, 0x0000 }, + { 0xf753, 0x0000 }, + { 0xf754, 0x0000 }, + { 0xf755, 0x0000 }, + { 0xf756, 0x0000 }, + { 0xf757, 0x0000 }, + { 0xf758, 0x0000 }, + { 0xf759, 0x0000 }, + { 0xf75a, 0x0000 }, + { 0xf75b, 0x0000 }, + { 0xf75c, 0x0000 }, + { 0xf75d, 0x0000 }, + { 0xf75e, 0x0000 }, + { 0xf75f, 0x0000 }, + { 0xf760, 0x0000 }, + { 0xf761, 0x0000 }, + { 0xf762, 0x0000 }, + { 0xf763, 0x0000 }, + { 0xf764, 0x0000 }, + { 0xf765, 0x0000 }, + { 0xf766, 0x0000 }, + { 0xf767, 0x0000 }, + { 0xf768, 0x0000 }, + { 0xf769, 0x0000 }, + { 0xf76a, 0x0000 }, + { 0xf76b, 0x0000 }, + { 0xf76c, 0x0000 }, + { 0xf76d, 0x0000 }, + { 0xf76e, 0x0000 }, + { 0xf76f, 0x0000 }, + { 0xf770, 0x0000 }, + { 0xf771, 0x0000 }, + { 0xf772, 0x0000 }, + { 0xf773, 0x0000 }, + { 0xf774, 0x0000 }, + { 0xf775, 0x0000 }, + { 0xf776, 0x0000 }, + { 0xf777, 0x0000 }, + { 0xf778, 0x0000 }, + { 0xf779, 0x0000 }, + { 0xf77a, 0x0000 }, + { 0xf77b, 0x0000 }, + { 0xf77c, 0x0000 }, + { 0xf77d, 0x0000 }, + { 0xf77e, 0x0000 }, + { 0xf77f, 0x0000 }, + { 0xf780, 0x0000 }, + { 0xf781, 0x0000 }, + { 0xf782, 0x0000 }, + { 0xf783, 0x0000 }, + { 0xf784, 0x0000 }, + { 0xf785, 0x0000 }, + { 0xf786, 0x0000 }, + { 0xf787, 0x0000 }, + { 0xf788, 0x0000 }, + { 0xf789, 0x0000 }, + { 0xf78a, 0x0000 }, + { 0xf78b, 0x0000 }, + { 0xf78c, 0x0000 }, + { 0xf78d, 0x0000 }, + { 0xf78e, 0x0000 }, + { 0xf78f, 0x0000 }, + { 0xf790, 0x0000 }, + { 0xf791, 0x0000 }, + { 0xf792, 0x0000 }, + { 0xf793, 0x0000 }, + { 0xf794, 0x0000 }, + { 0xf795, 0x0000 }, + { 0xf796, 0x0000 }, + { 0xf797, 0x0000 }, + { 0xf798, 0x0000 }, + { 0xf799, 0x0000 }, + { 0xf79a, 0x0000 }, + { 0xf79b, 0x0000 }, + { 0xf79c, 0x0000 }, + { 0xf79d, 0x0000 }, + { 0xf79e, 0x0000 }, + { 0xf79f, 0x0000 }, + { 0xf7a0, 0x0000 }, + { 0xf7a1, 0x0000 }, + { 0xf7a2, 0x0000 }, + { 0xf7a3, 0x0000 }, + { 0xf7a4, 0x0000 }, + { 0xf7a5, 0x0000 }, + { 0xf7a6, 0x0000 }, + { 0xf7a7, 0x0000 }, + { 0xf7a8, 0x0000 }, + { 0xf7a9, 0x0000 }, + { 0xf7aa, 0x0000 }, + { 0xf7ab, 0x0000 }, + { 0xf7ac, 0x0000 }, + { 0xf7ad, 0x0000 }, + { 0xf7ae, 0x0000 }, + { 0xf7af, 0x0000 }, + { 0xf7b0, 0x0000 }, + { 0xf7b1, 0x0000 }, + { 0xf7b2, 0x0000 }, + { 0xf7b3, 0x0000 }, + { 0xf7b4, 0x0000 }, + { 0xf7b5, 0x0000 }, + { 0xf7b6, 0x0000 }, + { 0xf7b7, 0x0000 }, + { 0xf7b8, 0x0000 }, + { 0xf7b9, 0x0000 }, + { 0xf7ba, 0x0000 }, + { 0xf7bb, 0x0000 }, + { 0xf7bc, 0x0000 }, + { 0xf7bd, 0x0000 }, + { 0xf7be, 0x0000 }, + { 0xf7bf, 0x0000 }, + { 0xf7c0, 0x0000 }, + { 0xf7c1, 0x0000 }, + { 0xf7c2, 0x0000 }, + { 0xf7c3, 0x0000 }, + { 0xf7c4, 0x0000 }, + { 0xf7c5, 0x0000 }, + { 0xf7c6, 0x0000 }, + { 0xf7c7, 0x0000 }, + { 0xf7c8, 0x0000 }, + { 0xf7c9, 0x0000 }, + { 0xf7ca, 0x0000 }, + { 0xf7cb, 0x0000 }, + { 0xf7cc, 0x0000 }, + { 0xf7cd, 0x0000 }, + { 0xf7ce, 0x0000 }, + { 0xf7cf, 0x0000 }, + { 0xf7d0, 0x0000 }, + { 0xf7d1, 0x0000 }, + { 0xf7d2, 0x0000 }, + { 0xf7d3, 0x0000 }, + { 0xf7d4, 0x0000 }, + { 0xf7d5, 0x0000 }, + { 0xf7d6, 0x0000 }, + { 0xf7d7, 0x0000 }, + { 0xf7d8, 0x0000 }, + { 0xf7d9, 0x0000 }, + { 0xf7da, 0x0000 }, + { 0xf7db, 0x0000 }, + { 0xf7dc, 0x0000 }, + { 0xf7dd, 0x0000 }, + { 0xf7de, 0x0000 }, + { 0xf7df, 0x0000 }, + { 0xf7e0, 0x0000 }, + { 0xf7e1, 0x0000 }, + { 0xf7e2, 0x0000 }, + { 0xf7e3, 0x0000 }, + { 0xf7e4, 0x0000 }, + { 0xf7e5, 0x0000 }, + { 0xf7e6, 0x0000 }, + { 0xf7e7, 0x0000 }, + { 0xf7e8, 0x0000 }, + { 0xf7e9, 0x0000 }, + { 0xf7ea, 0x0000 }, + { 0xf7eb, 0x0000 }, + { 0xf7ec, 0x0000 }, + { 0xf7ed, 0x0000 }, + { 0xf7ee, 0x0000 }, + { 0xf7ef, 0x0000 }, + { 0xf7f0, 0x0000 }, + { 0xf7f1, 0x0000 }, + { 0xf7f2, 0x0000 }, + { 0xf7f3, 0x0000 }, + { 0xf7f4, 0x0000 }, + { 0xf7f5, 0x0000 }, + { 0xf7f6, 0x0000 }, + { 0xf7f7, 0x0000 }, + { 0xf7f8, 0x0000 }, + { 0xf7f9, 0x0000 }, + { 0xf7fa, 0x0000 }, + { 0xf7fb, 0x0000 }, + { 0xf7fc, 0x0000 }, + { 0xf7fd, 0x0000 }, + { 0xf7fe, 0x0000 }, + { 0xf7ff, 0x0000 }, + { 0xf800, 0x0000 }, + { 0xf801, 0x0000 }, + { 0xf802, 0x0000 }, + { 0xf803, 0x0000 }, + { 0xf804, 0x0000 }, + { 0xf805, 0x0000 }, + { 0xf806, 0x0000 }, + { 0xf807, 0x0000 }, + { 0xf808, 0x0000 }, + { 0xf809, 0x0000 }, + { 0xf80a, 0x0000 }, + { 0xf80b, 0x0000 }, + { 0xf80c, 0x0000 }, + { 0xf80d, 0x0000 }, + { 0xf80e, 0x0000 }, + { 0xf80f, 0x0000 }, + { 0xf810, 0x0000 }, + { 0xf811, 0x0000 }, + { 0xf812, 0x0000 }, + { 0xf813, 0x0000 }, + { 0xf814, 0x0000 }, + { 0xf815, 0x0000 }, + { 0xf816, 0x0000 }, + { 0xf817, 0x0000 }, + { 0xf818, 0x0000 }, + { 0xf819, 0x0000 }, + { 0xf81a, 0x0000 }, + { 0xf81b, 0x0000 }, + { 0xf81c, 0x0000 }, + { 0xf81d, 0x0000 }, + { 0xf81e, 0x0000 }, + { 0xf81f, 0x0000 }, + { 0xf820, 0x0000 }, + { 0xf821, 0x0000 }, + { 0xf822, 0x0000 }, + { 0xf823, 0x0000 }, + { 0xf824, 0x0000 }, + { 0xf825, 0x0000 }, + { 0xf826, 0x0000 }, + { 0xf827, 0x0000 }, + { 0xf828, 0x0000 }, + { 0xf829, 0x0000 }, + { 0xf82a, 0x0000 }, + { 0xf82b, 0x0000 }, + { 0xf82c, 0x0000 }, + { 0xf82d, 0x0000 }, + { 0xf82e, 0x0000 }, + { 0xf82f, 0x0000 }, + { 0xf830, 0x0000 }, + { 0xf831, 0x0000 }, + { 0xf832, 0x0000 }, + { 0xf833, 0x0000 }, + { 0xf834, 0x0000 }, + { 0xf835, 0x0000 }, + { 0xf836, 0x0000 }, + { 0xf837, 0x0000 }, + { 0xf838, 0x0000 }, + { 0xf839, 0x0000 }, + { 0xf83a, 0x0000 }, + { 0xf83b, 0x0000 }, + { 0xf83c, 0x0000 }, + { 0xf83d, 0x0000 }, + { 0xf83e, 0x0000 }, + { 0xf83f, 0x0000 }, + { 0xf840, 0x0000 }, + { 0xf841, 0x0000 }, + { 0xf842, 0x0000 }, + { 0xf843, 0x0000 }, + { 0xf844, 0x0000 }, + { 0xf845, 0x0000 }, + { 0xf846, 0x0000 }, + { 0xf847, 0x0000 }, + { 0xf848, 0x0000 }, + { 0xf849, 0x0000 }, + { 0xf84a, 0x0000 }, + { 0xf84b, 0x0000 }, + { 0xf84c, 0x0000 }, + { 0xf84d, 0x0000 }, + { 0xf84e, 0x0000 }, + { 0xf84f, 0x0000 }, + { 0xf850, 0x0000 }, + { 0xf851, 0x0000 }, + { 0xf852, 0x0000 }, + { 0xf853, 0x0000 }, + { 0xf854, 0x0000 }, + { 0xf855, 0x0000 }, + { 0xf856, 0x0000 }, + { 0xf857, 0x0000 }, + { 0xf858, 0x0000 }, + { 0xf859, 0x0000 }, + { 0xf85a, 0x0000 }, + { 0xf85b, 0x0000 }, + { 0xf85c, 0x0000 }, + { 0xf85d, 0x0000 }, + { 0xf85e, 0x0000 }, + { 0xf85f, 0x0000 }, + { 0xf860, 0x0000 }, + { 0xf861, 0x0000 }, + { 0xf862, 0x0000 }, + { 0xf863, 0x0000 }, + { 0xf864, 0x0000 }, + { 0xf865, 0x0000 }, + { 0xf866, 0x0000 }, + { 0xf867, 0x0000 }, + { 0xf868, 0x0000 }, + { 0xf869, 0x0000 }, + { 0xf86a, 0x0000 }, + { 0xf86b, 0x0000 }, + { 0xf86c, 0x0000 }, + { 0xf86d, 0x0000 }, + { 0xf86e, 0x0000 }, + { 0xf86f, 0x0000 }, + { 0xf870, 0x0000 }, + { 0xf871, 0x0000 }, + { 0xf872, 0x0000 }, + { 0xf873, 0x0000 }, + { 0xf874, 0x0000 }, + { 0xf875, 0x0000 }, + { 0xf876, 0x0000 }, + { 0xf877, 0x0000 }, + { 0xf878, 0x0000 }, + { 0xf879, 0x0000 }, + { 0xf87a, 0x0000 }, + { 0xf87b, 0x0000 }, + { 0xf87c, 0x0000 }, + { 0xf87d, 0x0000 }, + { 0xf87e, 0x0000 }, + { 0xf87f, 0x0000 }, + { 0xf880, 0x0000 }, + { 0xf881, 0x0000 }, + { 0xf882, 0x0000 }, + { 0xf883, 0x0000 }, + { 0xf884, 0x0000 }, + { 0xf885, 0x0000 }, + { 0xf886, 0x0000 }, + { 0xf887, 0x0000 }, + { 0xf888, 0x0000 }, + { 0xf889, 0x0000 }, + { 0xf88a, 0x0000 }, + { 0xf88b, 0x0000 }, + { 0xf88c, 0x0000 }, + { 0xf88d, 0x0000 }, + { 0xf88e, 0x0000 }, + { 0xf88f, 0x0000 }, + { 0xf890, 0x0000 }, + { 0xf891, 0x0000 }, + { 0xf892, 0x0000 }, + { 0xf893, 0x0000 }, + { 0xf894, 0x0000 }, + { 0xf895, 0x0000 }, + { 0xf896, 0x0000 }, + { 0xf897, 0x0000 }, + { 0xf898, 0x0000 }, + { 0xf899, 0x0000 }, + { 0xf89a, 0x0000 }, + { 0xf89b, 0x0000 }, + { 0xf89c, 0x0000 }, + { 0xf89d, 0x0000 }, + { 0xf89e, 0x0000 }, + { 0xf89f, 0x0000 }, + { 0xf8a0, 0x0000 }, + { 0xf8a1, 0x0000 }, + { 0xf8a2, 0x0000 }, + { 0xf8a3, 0x0000 }, + { 0xf8a4, 0x0000 }, + { 0xf8a5, 0x0000 }, + { 0xf8a6, 0x0000 }, + { 0xf8a7, 0x0000 }, + { 0xf8a8, 0x0000 }, + { 0xf8a9, 0x0000 }, + { 0xf8aa, 0x0000 }, + { 0xf8ab, 0x0000 }, + { 0xf8ac, 0x0000 }, + { 0xf8ad, 0x0000 }, + { 0xf8ae, 0x0000 }, + { 0xf8af, 0x0000 }, + { 0xf8b0, 0x0000 }, + { 0xf8b1, 0x0000 }, + { 0xf8b2, 0x0000 }, + { 0xf8b3, 0x0000 }, + { 0xf8b4, 0x0000 }, + { 0xf8b5, 0x0000 }, + { 0xf8b6, 0x0000 }, + { 0xf8b7, 0x0000 }, + { 0xf8b8, 0x0000 }, + { 0xf8b9, 0x0000 }, + { 0xf8ba, 0x0000 }, + { 0xf8bb, 0x0000 }, + { 0xf8bc, 0x0000 }, + { 0xf8bd, 0x0000 }, + { 0xf8be, 0x0000 }, + { 0xf8bf, 0x0000 }, + { 0xf8c0, 0x0000 }, + { 0xf8c1, 0x0000 }, + { 0xf8c2, 0x0000 }, + { 0xf8c3, 0x0000 }, + { 0xf8c4, 0x0000 }, + { 0xf8c5, 0x0000 }, + { 0xf8c6, 0x0000 }, + { 0xf8c7, 0x0000 }, + { 0xf8c8, 0x0000 }, + { 0xf8c9, 0x0000 }, + { 0xf8ca, 0x0000 }, + { 0xf8cb, 0x0000 }, + { 0xf8cc, 0x0000 }, + { 0xf8cd, 0x0000 }, + { 0xf8ce, 0x0000 }, + { 0xf8cf, 0x0000 }, + { 0xf8d0, 0x0000 }, + { 0xf8d1, 0x0000 }, + { 0xf8d2, 0x0000 }, + { 0xf8d3, 0x0000 }, + { 0xf8d4, 0x0000 }, + { 0xf8d5, 0x0000 }, + { 0xf8d6, 0x0000 }, + { 0xf8d7, 0x0000 }, + { 0xf8d8, 0x0000 }, + { 0xf8d9, 0x0000 }, + { 0xf8da, 0x0000 }, + { 0xf8db, 0x0000 }, + { 0xf8dc, 0x0000 }, + { 0xf8dd, 0x0000 }, + { 0xf8de, 0x0000 }, + { 0xf8df, 0x0000 }, + { 0xf8e0, 0x0000 }, + { 0xf8e1, 0x0000 }, + { 0xf8e2, 0x0000 }, + { 0xf8e3, 0x0000 }, + { 0xf8e4, 0x0000 }, + { 0xf8e5, 0x0000 }, + { 0xf8e6, 0x0000 }, + { 0xf8e7, 0x0000 }, + { 0xf8e8, 0x0000 }, + { 0xf8e9, 0x0000 }, + { 0xf8ea, 0x0000 }, + { 0xf8eb, 0x0000 }, + { 0xf8ec, 0x0000 }, + { 0xf8ed, 0x0000 }, + { 0xf8ee, 0x0000 }, + { 0xf8ef, 0x0000 }, + { 0xf8f0, 0x0000 }, + { 0xf8f1, 0x0000 }, + { 0xf8f2, 0x0000 }, + { 0xf8f3, 0x0000 }, + { 0xf8f4, 0x0000 }, + { 0xf8f5, 0x0000 }, + { 0xf8f6, 0x0000 }, + { 0xf8f7, 0x0000 }, + { 0xf8f8, 0x0000 }, + { 0xf8f9, 0x0000 }, + { 0xf8fa, 0x0000 }, + { 0xf8fb, 0x0000 }, + { 0xf8fc, 0x0000 }, + { 0xf8fd, 0x0000 }, + { 0xf8fe, 0x0000 }, + { 0xf8ff, 0x0000 }, + { 0xf900, 0x0000 }, + { 0xf901, 0x0000 }, + { 0xf902, 0x0000 }, + { 0xf903, 0x0000 }, + { 0xf904, 0x0000 }, + { 0xf905, 0x0000 }, + { 0xf906, 0x0000 }, + { 0xf907, 0x0000 }, + { 0xf908, 0x0000 }, + { 0xf909, 0x0000 }, + { 0xf90a, 0x0000 }, + { 0xf90b, 0x0000 }, + { 0xf90c, 0x0000 }, + { 0xf90d, 0x0000 }, + { 0xf90e, 0x0000 }, + { 0xf90f, 0x0000 }, + { 0xf910, 0x0000 }, + { 0xf911, 0x0000 }, + { 0xf912, 0x0000 }, + { 0xf913, 0x0000 }, + { 0xf914, 0x0000 }, + { 0xf915, 0x0000 }, + { 0xf916, 0x0000 }, + { 0xf917, 0x0000 }, + { 0xf918, 0x0000 }, + { 0xf919, 0x0000 }, + { 0xf91a, 0x0000 }, + { 0xf91b, 0x0000 }, + { 0xf91c, 0x0000 }, + { 0xf91d, 0x0000 }, + { 0xf91e, 0x0000 }, + { 0xf91f, 0x0000 }, + { 0xf920, 0x0000 }, + { 0xf921, 0x0000 }, + { 0xf922, 0x0000 }, + { 0xf923, 0x0000 }, + { 0xf924, 0x0000 }, + { 0xf925, 0x0000 }, + { 0xf926, 0x0000 }, + { 0xf927, 0x0000 }, + { 0xf928, 0x0000 }, + { 0xf929, 0x0000 }, + { 0xf92a, 0x0000 }, + { 0xf92b, 0x0000 }, + { 0xf92c, 0x0000 }, + { 0xf92d, 0x0000 }, + { 0xf92e, 0x0000 }, + { 0xf92f, 0x0000 }, + { 0xf930, 0x0000 }, + { 0xf931, 0x0000 }, + { 0xf932, 0x0000 }, + { 0xf933, 0x0000 }, + { 0xf934, 0x0000 }, + { 0xf935, 0x0000 }, + { 0xf936, 0x0000 }, + { 0xf937, 0x0000 }, + { 0xf938, 0x0000 }, + { 0xf939, 0x0000 }, + { 0xf93a, 0x0000 }, + { 0xf93b, 0x0000 }, + { 0xf93c, 0x0000 }, + { 0xf93d, 0x0000 }, + { 0xf93e, 0x0000 }, + { 0xf93f, 0x0000 }, + { 0xf940, 0x0000 }, + { 0xf941, 0x0000 }, + { 0xf942, 0x0000 }, + { 0xf943, 0x0000 }, + { 0xf944, 0x0000 }, + { 0xf945, 0x0000 }, + { 0xf946, 0x0000 }, + { 0xf947, 0x0000 }, + { 0xf948, 0x0000 }, + { 0xf949, 0x0000 }, + { 0xf94a, 0x0000 }, + { 0xf94b, 0x0000 }, + { 0xf94c, 0x0000 }, + { 0xf94d, 0x0000 }, + { 0xf94e, 0x0000 }, + { 0xf94f, 0x0000 }, + { 0xf950, 0x0000 }, + { 0xf951, 0x0000 }, + { 0xf952, 0x0000 }, + { 0xf953, 0x0000 }, + { 0xf954, 0x0000 }, + { 0xf955, 0x0000 }, + { 0xf956, 0x0000 }, + { 0xf957, 0x0000 }, + { 0xf958, 0x0000 }, + { 0xf959, 0x0000 }, + { 0xf95a, 0x0000 }, + { 0xf95b, 0x0000 }, + { 0xf95c, 0x0000 }, + { 0xf95d, 0x0000 }, + { 0xf95e, 0x0000 }, + { 0xf95f, 0x0000 }, + { 0xf960, 0x0000 }, + { 0xf961, 0x0000 }, + { 0xf962, 0x0000 }, + { 0xf963, 0x0000 }, + { 0xf964, 0x0000 }, + { 0xf965, 0x0000 }, + { 0xf966, 0x0000 }, + { 0xf967, 0x0000 }, + { 0xf968, 0x0000 }, + { 0xf969, 0x0000 }, + { 0xf96a, 0x0000 }, + { 0xf96b, 0x0000 }, + { 0xf96c, 0x0000 }, + { 0xf96d, 0x0000 }, + { 0xf96e, 0x0000 }, + { 0xf96f, 0x0000 }, + { 0xf970, 0x0000 }, + { 0xf971, 0x0000 }, + { 0xf972, 0x0000 }, + { 0xf973, 0x0000 }, + { 0xf974, 0x0000 }, + { 0xf975, 0x0000 }, + { 0xf976, 0x0000 }, + { 0xf977, 0x0000 }, + { 0xf978, 0x0000 }, + { 0xf979, 0x0000 }, + { 0xf97a, 0x0000 }, + { 0xf97b, 0x0000 }, + { 0xf97c, 0x0000 }, + { 0xf97d, 0x0000 }, + { 0xf97e, 0x0000 }, + { 0xf97f, 0x0000 }, + { 0xf980, 0x0000 }, + { 0xf981, 0x0000 }, + { 0xf982, 0x0000 }, + { 0xf983, 0x0000 }, + { 0xf984, 0x0000 }, + { 0xf985, 0x0000 }, + { 0xf986, 0x0000 }, + { 0xf987, 0x0000 }, + { 0xf988, 0x0000 }, + { 0xf989, 0x0000 }, + { 0xf98a, 0x0000 }, + { 0xf98b, 0x0000 }, + { 0xf98c, 0x0000 }, + { 0xf98d, 0x0000 }, + { 0xf98e, 0x0000 }, + { 0xf98f, 0x0000 }, + { 0xf990, 0x0000 }, + { 0xf991, 0x0000 }, + { 0xf992, 0x0000 }, + { 0xf993, 0x0000 }, + { 0xf994, 0x0000 }, + { 0xf995, 0x0000 }, + { 0xf996, 0x0000 }, + { 0xf997, 0x0000 }, + { 0xf998, 0x0000 }, + { 0xf999, 0x0000 }, + { 0xf99a, 0x0000 }, + { 0xf99b, 0x0000 }, + { 0xf99c, 0x0000 }, + { 0xf99d, 0x0000 }, + { 0xf99e, 0x0000 }, + { 0xf99f, 0x0000 }, + { 0xf9a0, 0x0000 }, + { 0xf9a1, 0x0000 }, + { 0xf9a2, 0x0000 }, + { 0xf9a3, 0x0000 }, + { 0xf9a4, 0x0000 }, + { 0xf9a5, 0x0000 }, + { 0xf9a6, 0x0000 }, + { 0xf9a7, 0x0000 }, + { 0xf9a8, 0x0000 }, + { 0xf9a9, 0x0000 }, + { 0xf9aa, 0x0000 }, + { 0xf9ab, 0x0000 }, + { 0xf9ac, 0x0000 }, + { 0xf9ad, 0x0000 }, + { 0xf9ae, 0x0000 }, + { 0xf9af, 0x0000 }, + { 0xf9b0, 0x0000 }, + { 0xf9b1, 0x0000 }, + { 0xf9b2, 0x0000 }, + { 0xf9b3, 0x0000 }, + { 0xf9b4, 0x0000 }, + { 0xf9b5, 0x0000 }, + { 0xf9b6, 0x0000 }, + { 0xf9b7, 0x0000 }, + { 0xf9b8, 0x0000 }, + { 0xf9b9, 0x0000 }, + { 0xf9ba, 0x0000 }, + { 0xf9bb, 0x0000 }, + { 0xf9bc, 0x0000 }, + { 0xf9bd, 0x0000 }, + { 0xf9be, 0x0000 }, + { 0xf9bf, 0x0000 }, + { 0xf9c0, 0x0000 }, + { 0xf9c1, 0x0000 }, + { 0xf9c2, 0x0000 }, + { 0xf9c3, 0x0000 }, + { 0xf9c4, 0x0000 }, + { 0xf9c5, 0x0000 }, + { 0xf9c6, 0x0000 }, + { 0xf9c7, 0x0000 }, + { 0xf9c8, 0x0000 }, + { 0xf9c9, 0x0000 }, + { 0xf9ca, 0x0000 }, + { 0xf9cb, 0x0000 }, + { 0xf9cc, 0x0000 }, + { 0xf9cd, 0x0000 }, + { 0xf9ce, 0x0000 }, + { 0xf9cf, 0x0000 }, + { 0xf9d0, 0x0000 }, + { 0xf9d1, 0x0000 }, + { 0xf9d2, 0x0000 }, + { 0xf9d3, 0x0000 }, + { 0xf9d4, 0x0000 }, + { 0xf9d5, 0x0000 }, + { 0xf9d6, 0x0000 }, + { 0xf9d7, 0x0000 }, + { 0xf9d8, 0x0000 }, + { 0xf9d9, 0x0000 }, + { 0xf9da, 0x0000 }, + { 0xf9db, 0x0000 }, + { 0xf9dc, 0x0000 }, + { 0xf9dd, 0x0000 }, + { 0xf9de, 0x0000 }, + { 0xf9df, 0x0000 }, + { 0xf9e0, 0x0000 }, + { 0xf9e1, 0x0000 }, + { 0xf9e2, 0x0000 }, + { 0xf9e3, 0x0000 }, + { 0xf9e4, 0x0000 }, + { 0xf9e5, 0x0000 }, + { 0xf9e6, 0x0000 }, + { 0xf9e7, 0x0000 }, + { 0xf9e8, 0x0000 }, + { 0xf9e9, 0x0000 }, + { 0xf9ea, 0x0000 }, + { 0xf9eb, 0x0000 }, + { 0xf9ec, 0x0000 }, + { 0xf9ed, 0x0000 }, + { 0xf9ee, 0x0000 }, + { 0xf9ef, 0x0000 }, + { 0xf9f0, 0x0000 }, + { 0xf9f1, 0x0000 }, + { 0xf9f2, 0x0000 }, + { 0xf9f3, 0x0000 }, + { 0xf9f4, 0x0000 }, + { 0xf9f5, 0x0000 }, + { 0xf9f6, 0x0000 }, + { 0xf9f7, 0x0000 }, + { 0xf9f8, 0x0000 }, + { 0xf9f9, 0x0000 }, + { 0xf9fa, 0x0000 }, + { 0xf9fb, 0x0000 }, + { 0xf9fc, 0x0000 }, + { 0xf9fd, 0x0000 }, + { 0xf9fe, 0x0000 }, + { 0xf9ff, 0x0000 }, + { 0xfa00, 0x0000 }, + { 0xfa01, 0x0000 }, + { 0xfa02, 0x0000 }, + { 0xfa03, 0x0000 }, + { 0xfa04, 0x0000 }, + { 0xfa05, 0x0000 }, + { 0xfa06, 0x0000 }, + { 0xfa07, 0x0000 }, + { 0xfa08, 0x0000 }, + { 0xfa09, 0x0000 }, + { 0xfa0a, 0x0000 }, + { 0xfa0b, 0x0000 }, + { 0xfa0c, 0x0000 }, + { 0xfa0d, 0x0000 }, + { 0xfa0e, 0x0000 }, + { 0xfa0f, 0x0000 }, + { 0xfa10, 0x0000 }, + { 0xfa11, 0x0000 }, + { 0xfa12, 0x0000 }, + { 0xfa13, 0x0000 }, + { 0xfa14, 0x0000 }, + { 0xfa15, 0x0000 }, + { 0xfa16, 0x0000 }, + { 0xfa17, 0x0000 }, + { 0xfa18, 0x0000 }, + { 0xfa19, 0x0000 }, + { 0xfa1a, 0x0000 }, + { 0xfa1b, 0x0000 }, + { 0xfa1c, 0x0000 }, + { 0xfa1d, 0x0000 }, + { 0xfa1e, 0x0000 }, + { 0xfa1f, 0x0000 }, + { 0xfa20, 0x0000 }, + { 0xfa21, 0x0000 }, + { 0xfa22, 0x0000 }, + { 0xfa23, 0x0000 }, + { 0xfa24, 0x0000 }, + { 0xfa25, 0x0000 }, + { 0xfa26, 0x0000 }, + { 0xfa27, 0x0000 }, + { 0xfa28, 0x0000 }, + { 0xfa29, 0x0000 }, + { 0xfa2a, 0x0000 }, + { 0xfa2b, 0x0000 }, + { 0xfa2c, 0x0000 }, + { 0xfa2d, 0x0000 }, + { 0xfa2e, 0x0000 }, + { 0xfa2f, 0x0000 }, + { 0xfa30, 0x0000 }, + { 0xfa31, 0x0000 }, + { 0xfa32, 0x0000 }, + { 0xfa33, 0x0000 }, + { 0xfa34, 0x0000 }, + { 0xfa35, 0x0000 }, + { 0xfa36, 0x0000 }, + { 0xfa37, 0x0000 }, + { 0xfa38, 0x0000 }, + { 0xfa39, 0x0000 }, + { 0xfa3a, 0x0000 }, + { 0xfa3b, 0x0000 }, + { 0xfa3c, 0x0000 }, + { 0xfa3d, 0x0000 }, + { 0xfa3e, 0x0000 }, + { 0xfa3f, 0x0000 }, + { 0xfa40, 0x0000 }, + { 0xfa41, 0x0000 }, + { 0xfa42, 0x0000 }, + { 0xfa43, 0x0000 }, + { 0xfa44, 0x0000 }, + { 0xfa45, 0x0000 }, + { 0xfa46, 0x0000 }, + { 0xfa47, 0x0000 }, + { 0xfa48, 0x0000 }, + { 0xfa49, 0x0000 }, + { 0xfa4a, 0x0000 }, + { 0xfa4b, 0x0000 }, + { 0xfa4c, 0x0000 }, + { 0xfa4d, 0x0000 }, + { 0xfa4e, 0x0000 }, + { 0xfa4f, 0x0000 }, + { 0xfa50, 0x0000 }, + { 0xfa51, 0x0000 }, + { 0xfa52, 0x0000 }, + { 0xfa53, 0x0000 }, + { 0xfa54, 0x0000 }, + { 0xfa55, 0x0000 }, + { 0xfa56, 0x0000 }, + { 0xfa57, 0x0000 }, + { 0xfa58, 0x0000 }, + { 0xfa59, 0x0000 }, + { 0xfa5a, 0x0000 }, + { 0xfa5b, 0x0000 }, + { 0xfa5c, 0x0000 }, + { 0xfa5d, 0x0000 }, + { 0xfa5e, 0x0000 }, + { 0xfa5f, 0x0000 }, + { 0xfa60, 0x0000 }, + { 0xfa61, 0x0000 }, + { 0xfa62, 0x0000 }, + { 0xfa63, 0x0000 }, + { 0xfa64, 0x0000 }, + { 0xfa65, 0x0000 }, + { 0xfa66, 0x0000 }, + { 0xfa67, 0x0000 }, + { 0xfa68, 0x0000 }, + { 0xfa69, 0x0000 }, + { 0xfa6a, 0x0000 }, + { 0xfa6b, 0x0000 }, + { 0xfa6c, 0x0000 }, + { 0xfa6d, 0x0000 }, + { 0xfa6e, 0x0000 }, + { 0xfa6f, 0x0000 }, + { 0xfa70, 0x0000 }, + { 0xfa71, 0x0000 }, + { 0xfa72, 0x0000 }, + { 0xfa73, 0x0000 }, + { 0xfa74, 0x0000 }, + { 0xfa75, 0x0000 }, + { 0xfa76, 0x0000 }, + { 0xfa77, 0x0000 }, + { 0xfa78, 0x0000 }, + { 0xfa79, 0x0000 }, + { 0xfa7a, 0x0000 }, + { 0xfa7b, 0x0000 }, + { 0xfa7c, 0x0000 }, + { 0xfa7d, 0x0000 }, + { 0xfa7e, 0x0000 }, + { 0xfa7f, 0x0000 }, + { 0xfa80, 0x0000 }, + { 0xfa81, 0x0000 }, + { 0xfa82, 0x0000 }, + { 0xfa83, 0x0000 }, + { 0xfa84, 0x0000 }, + { 0xfa85, 0x0000 }, + { 0xfa86, 0x0000 }, + { 0xfa87, 0x0000 }, + { 0xfa88, 0x0000 }, + { 0xfa89, 0x0000 }, + { 0xfa8a, 0x0000 }, + { 0xfa8b, 0x0000 }, + { 0xfa8c, 0x0000 }, + { 0xfa8d, 0x0000 }, + { 0xfa8e, 0x0000 }, + { 0xfa8f, 0x0000 }, + { 0xfa90, 0x0000 }, + { 0xfa91, 0x0000 }, + { 0xfa92, 0x0000 }, + { 0xfa93, 0x0000 }, + { 0xfa94, 0x0000 }, + { 0xfa95, 0x0000 }, + { 0xfa96, 0x0000 }, + { 0xfa97, 0x0000 }, + { 0xfa98, 0x0000 }, + { 0xfa99, 0x0000 }, + { 0xfa9a, 0x0000 }, + { 0xfa9b, 0x0000 }, + { 0xfa9c, 0x0000 }, + { 0xfa9d, 0x0000 }, + { 0xfa9e, 0x0000 }, + { 0xfa9f, 0x0000 }, + { 0xfaa0, 0x0000 }, + { 0xfaa1, 0x0000 }, + { 0xfaa2, 0x0000 }, + { 0xfaa3, 0x0000 }, + { 0xfaa4, 0x0000 }, + { 0xfaa5, 0x0000 }, + { 0xfaa6, 0x0000 }, + { 0xfaa7, 0x0000 }, + { 0xfaa8, 0x0000 }, + { 0xfaa9, 0x0000 }, + { 0xfaaa, 0x0000 }, + { 0xfaab, 0x0000 }, + { 0xfaac, 0x0000 }, + { 0xfaad, 0x0000 }, + { 0xfaae, 0x0000 }, + { 0xfaaf, 0x0000 }, + { 0xfab0, 0x0000 }, + { 0xfab1, 0x0000 }, + { 0xfab2, 0x0000 }, + { 0xfab3, 0x0000 }, + { 0xfab4, 0x0000 }, + { 0xfab5, 0x0000 }, + { 0xfab6, 0x0000 }, + { 0xfab7, 0x0000 }, + { 0xfab8, 0x0000 }, + { 0xfab9, 0x0000 }, + { 0xfaba, 0x0000 }, + { 0xfabb, 0x0000 }, + { 0xfabc, 0x0000 }, + { 0xfabd, 0x0000 }, + { 0xfabe, 0x0000 }, + { 0xfabf, 0x0000 }, + { 0xfac0, 0x0000 }, + { 0xfac1, 0x0000 }, + { 0xfac2, 0x0000 }, + { 0xfac3, 0x0000 }, + { 0xfac4, 0x0000 }, + { 0xfac5, 0x0000 }, + { 0xfac6, 0x0000 }, + { 0xfac7, 0x0000 }, + { 0xfac8, 0x0000 }, + { 0xfac9, 0x0000 }, + { 0xfaca, 0x0000 }, + { 0xfacb, 0x0000 }, + { 0xfacc, 0x0000 }, + { 0xfacd, 0x0000 }, + { 0xface, 0x0000 }, + { 0xfacf, 0x0000 }, + { 0xfad0, 0x0000 }, + { 0xfad1, 0x0000 }, + { 0xfad2, 0x0000 }, + { 0xfad3, 0x0000 }, + { 0xfad4, 0x0000 }, + { 0xfad5, 0x0000 }, + { 0xfad6, 0x0000 }, + { 0xfad7, 0x0000 }, + { 0xfad8, 0x0000 }, + { 0xfad9, 0x0000 }, + { 0xfada, 0x0000 }, + { 0xfadb, 0x0000 }, + { 0xfadc, 0x0000 }, + { 0xfadd, 0x0000 }, + { 0xfade, 0x0000 }, + { 0xfadf, 0x0000 }, + { 0xfae0, 0x0000 }, + { 0xfae1, 0x0000 }, + { 0xfae2, 0x0000 }, + { 0xfae3, 0x0000 }, + { 0xfae4, 0x0000 }, + { 0xfae5, 0x0000 }, + { 0xfae6, 0x0000 }, + { 0xfae7, 0x0000 }, + { 0xfae8, 0x0000 }, + { 0xfae9, 0x0000 }, + { 0xfaea, 0x0000 }, + { 0xfaeb, 0x0000 }, + { 0xfaec, 0x0000 }, + { 0xfaed, 0x0000 }, + { 0xfaee, 0x0000 }, + { 0xfaef, 0x0000 }, + { 0xfaf0, 0x0000 }, + { 0xfaf1, 0x0000 }, + { 0xfaf2, 0x0000 }, + { 0xfaf3, 0x0000 }, + { 0xfaf4, 0x0000 }, + { 0xfaf5, 0x0000 }, + { 0xfaf6, 0x0000 }, + { 0xfaf7, 0x0000 }, + { 0xfaf8, 0x0000 }, + { 0xfaf9, 0x0000 }, + { 0xfafa, 0x0000 }, + { 0xfafb, 0x0000 }, + { 0xfafc, 0x0000 }, + { 0xfafd, 0x0000 }, + { 0xfafe, 0x0000 }, + { 0xfaff, 0x0000 }, + { 0xfb00, 0x0000 }, + { 0xfb01, 0x0000 }, + { 0xfb02, 0x0000 }, + { 0xfb03, 0x0000 }, + { 0xfb04, 0x0000 }, + { 0xfb05, 0x0000 }, + { 0xfb06, 0x0000 }, + { 0xfb07, 0x0000 }, + { 0xfb08, 0x0000 }, + { 0xfb09, 0x0000 }, + { 0xfb0a, 0x0000 }, + { 0xfb0b, 0x0000 }, + { 0xfb0c, 0x0000 }, + { 0xfb0d, 0x0000 }, + { 0xfb0e, 0x0000 }, + { 0xfb0f, 0x0000 }, + { 0xfb10, 0x0000 }, + { 0xfb11, 0x0000 }, + { 0xfb12, 0x0000 }, + { 0xfb13, 0x0000 }, + { 0xfb14, 0x0000 }, + { 0xfb15, 0x0000 }, + { 0xfb16, 0x0000 }, + { 0xfb17, 0x0000 }, + { 0xfb18, 0x0000 }, + { 0xfb19, 0x0000 }, + { 0xfb1a, 0x0000 }, + { 0xfb1b, 0x0000 }, + { 0xfb1c, 0x0000 }, + { 0xfb1d, 0x0000 }, + { 0xfb1e, 0x0000 }, + { 0xfb1f, 0x0000 }, + { 0xfb20, 0x0000 }, + { 0xfb21, 0x0000 }, + { 0xfb22, 0x0000 }, + { 0xfb23, 0x0000 }, + { 0xfb24, 0x0000 }, + { 0xfb25, 0x0000 }, + { 0xfb26, 0x0000 }, + { 0xfb27, 0x0000 }, + { 0xfb28, 0x0000 }, + { 0xfb29, 0x0000 }, + { 0xfb2a, 0x0000 }, + { 0xfb2b, 0x0000 }, + { 0xfb2c, 0x0000 }, + { 0xfb2d, 0x0000 }, + { 0xfb2e, 0x0000 }, + { 0xfb2f, 0x0000 }, + { 0xfb30, 0x0000 }, + { 0xfb31, 0x0000 }, + { 0xfb32, 0x0000 }, + { 0xfb33, 0x0000 }, + { 0xfb34, 0x0000 }, + { 0xfb35, 0x0000 }, + { 0xfb36, 0x0000 }, + { 0xfb37, 0x0000 }, + { 0xfb38, 0x0000 }, + { 0xfb39, 0x0000 }, + { 0xfb3a, 0x0000 }, + { 0xfb3b, 0x0000 }, + { 0xfb3c, 0x0000 }, + { 0xfb3d, 0x0000 }, + { 0xfb3e, 0x0000 }, + { 0xfb3f, 0x0000 }, + { 0xfb40, 0x0000 }, + { 0xfb41, 0x0000 }, + { 0xfb42, 0x0000 }, + { 0xfb43, 0x0000 }, + { 0xfb44, 0x0000 }, + { 0xfb45, 0x0000 }, + { 0xfb46, 0x0000 }, + { 0xfb47, 0x0000 }, + { 0xfb48, 0x0000 }, + { 0xfb49, 0x0000 }, + { 0xfb4a, 0x0000 }, + { 0xfb4b, 0x0000 }, + { 0xfb4c, 0x0000 }, + { 0xfb4d, 0x0000 }, + { 0xfb4e, 0x0000 }, + { 0xfb4f, 0x0000 }, + { 0xfb50, 0x0000 }, + { 0xfb51, 0x0000 }, + { 0xfb52, 0x0000 }, + { 0xfb53, 0x0000 }, + { 0xfb54, 0x0000 }, + { 0xfb55, 0x0000 }, + { 0xfb56, 0x0000 }, + { 0xfb57, 0x0000 }, + { 0xfb58, 0x0000 }, + { 0xfb59, 0x0000 }, + { 0xfb5a, 0x0000 }, + { 0xfb5b, 0x0000 }, + { 0xfb5c, 0x0000 }, + { 0xfb5d, 0x0000 }, + { 0xfb5e, 0x0000 }, + { 0xfb5f, 0x0000 }, + { 0xfb60, 0x0000 }, + { 0xfb61, 0x0000 }, + { 0xfb62, 0x0000 }, + { 0xfb63, 0x0000 }, + { 0xfb64, 0x0000 }, + { 0xfb65, 0x0000 }, + { 0xfb66, 0x0000 }, + { 0xfb67, 0x0000 }, + { 0xfb68, 0x0000 }, + { 0xfb69, 0x0000 }, + { 0xfb6a, 0x0000 }, + { 0xfb6b, 0x0000 }, + { 0xfb6c, 0x0000 }, + { 0xfb6d, 0x0000 }, + { 0xfb6e, 0x0000 }, + { 0xfb6f, 0x0000 }, + { 0xfb70, 0x0000 }, + { 0xfb71, 0x0000 }, + { 0xfb72, 0x0000 }, + { 0xfb73, 0x0000 }, + { 0xfb74, 0x0000 }, + { 0xfb75, 0x0000 }, + { 0xfb76, 0x0000 }, + { 0xfb77, 0x0000 }, + { 0xfb78, 0x0000 }, + { 0xfb79, 0x0000 }, + { 0xfb7a, 0x0000 }, + { 0xfb7b, 0x0000 }, + { 0xfb7c, 0x0000 }, + { 0xfb7d, 0x0000 }, + { 0xfb7e, 0x0000 }, + { 0xfb7f, 0x0000 }, + { 0xfb80, 0x0000 }, + { 0xfb81, 0x0000 }, + { 0xfb82, 0x0000 }, + { 0xfb83, 0x0000 }, + { 0xfb84, 0x0000 }, + { 0xfb85, 0x0000 }, + { 0xfb86, 0x0000 }, + { 0xfb87, 0x0000 }, + { 0xfb88, 0x0000 }, + { 0xfb89, 0x0000 }, + { 0xfb8a, 0x0000 }, + { 0xfb8b, 0x0000 }, + { 0xfb8c, 0x0000 }, + { 0xfb8d, 0x0000 }, + { 0xfb8e, 0x0000 }, + { 0xfb8f, 0x0000 }, + { 0xfb90, 0x0000 }, + { 0xfb91, 0x0000 }, + { 0xfb92, 0x0000 }, + { 0xfb93, 0x0000 }, + { 0xfb94, 0x0000 }, + { 0xfb95, 0x0000 }, + { 0xfb96, 0x0000 }, + { 0xfb97, 0x0000 }, + { 0xfb98, 0x0000 }, + { 0xfb99, 0x0000 }, + { 0xfb9a, 0x0000 }, + { 0xfb9b, 0x0000 }, + { 0xfb9c, 0x0000 }, + { 0xfb9d, 0x0000 }, + { 0xfb9e, 0x0000 }, + { 0xfb9f, 0x0000 }, + { 0xfba0, 0x0000 }, + { 0xfba1, 0x0000 }, + { 0xfba2, 0x0000 }, + { 0xfba3, 0x0000 }, + { 0xfba4, 0x0000 }, + { 0xfba5, 0x0000 }, + { 0xfba6, 0x0000 }, + { 0xfba7, 0x0000 }, + { 0xfba8, 0x0000 }, + { 0xfba9, 0x0000 }, + { 0xfbaa, 0x0000 }, + { 0xfbab, 0x0000 }, + { 0xfbac, 0x0000 }, + { 0xfbad, 0x0000 }, + { 0xfbae, 0x0000 }, + { 0xfbaf, 0x0000 }, + { 0xfbb0, 0x0000 }, + { 0xfbb1, 0x0000 }, + { 0xfbb2, 0x0000 }, + { 0xfbb3, 0x0000 }, + { 0xfbb4, 0x0000 }, + { 0xfbb5, 0x0000 }, + { 0xfbb6, 0x0000 }, + { 0xfbb7, 0x0000 }, + { 0xfbb8, 0x0000 }, + { 0xfbb9, 0x0000 }, + { 0xfbba, 0x0000 }, + { 0xfbbb, 0x0000 }, + { 0xfbbc, 0x0000 }, + { 0xfbbd, 0x0000 }, + { 0xfbbe, 0x0000 }, + { 0xfbbf, 0x0000 }, + { 0xfbc0, 0x0000 }, + { 0xfbc1, 0x0000 }, + { 0xfbc2, 0x0000 }, + { 0xfbc3, 0x0000 }, + { 0xfbc4, 0x0000 }, + { 0xfbc5, 0x0000 }, + { 0xfbc6, 0x0000 }, + { 0xfbc7, 0x0000 }, + { 0xfbc8, 0x0000 }, + { 0xfbc9, 0x0000 }, + { 0xfbca, 0x0000 }, + { 0xfbcb, 0x0000 }, + { 0xfbcc, 0x0000 }, + { 0xfbcd, 0x0000 }, + { 0xfbce, 0x0000 }, + { 0xfbcf, 0x0000 }, + { 0xfbd0, 0x0000 }, + { 0xfbd1, 0x0000 }, + { 0xfbd2, 0x0000 }, + { 0xfbd3, 0x0000 }, + { 0xfbd4, 0x0000 }, + { 0xfbd5, 0x0000 }, + { 0xfbd6, 0x0000 }, + { 0xfbd7, 0x0000 }, + { 0xfbd8, 0x0000 }, + { 0xfbd9, 0x0000 }, + { 0xfbda, 0x0000 }, + { 0xfbdb, 0x0000 }, + { 0xfbdc, 0x0000 }, + { 0xfbdd, 0x0000 }, + { 0xfbde, 0x0000 }, + { 0xfbdf, 0x0000 }, + { 0xfbe0, 0x0000 }, + { 0xfbe1, 0x0000 }, + { 0xfbe2, 0x0000 }, + { 0xfbe3, 0x0000 }, + { 0xfbe4, 0x0000 }, + { 0xfbe5, 0x0000 }, + { 0xfbe6, 0x0000 }, + { 0xfbe7, 0x0000 }, + { 0xfbe8, 0x0000 }, + { 0xfbe9, 0x0000 }, + { 0xfbea, 0x0000 }, + { 0xfbeb, 0x0000 }, + { 0xfbec, 0x0000 }, + { 0xfbed, 0x0000 }, + { 0xfbee, 0x0000 }, + { 0xfbef, 0x0000 }, + { 0xfbf0, 0x0000 }, + { 0xfbf1, 0x0000 }, + { 0xfbf2, 0x0000 }, + { 0xfbf3, 0x0000 }, + { 0xfbf4, 0x0000 }, + { 0xfbf5, 0x0000 }, + { 0xfbf6, 0x0000 }, + { 0xfbf7, 0x0000 }, + { 0xfbf8, 0x0000 }, + { 0xfbf9, 0x0000 }, + { 0xfbfa, 0x0000 }, + { 0xfbfb, 0x0000 }, + { 0xfbfc, 0x0000 }, + { 0xfbfd, 0x0000 }, + { 0xfbfe, 0x0000 }, + { 0xfbff, 0x0000 }, + { 0xfc00, 0x0000 }, + { 0xfc01, 0x0000 }, + { 0xfc02, 0x0000 }, + { 0xfc03, 0x0000 }, + { 0xfc04, 0x0000 }, + { 0xfc05, 0x0000 }, + { 0xfc06, 0x0000 }, + { 0xfc07, 0x0000 }, + { 0xfc08, 0x0000 }, + { 0xfc09, 0x0000 }, + { 0xfc0a, 0x0000 }, + { 0xfc0b, 0x0000 }, + { 0xfc0c, 0x0000 }, + { 0xfc0d, 0x0000 }, + { 0xfc0e, 0x0000 }, + { 0xfc0f, 0x0000 }, + { 0xfc10, 0x0000 }, + { 0xfc11, 0x0000 }, + { 0xfc12, 0x0000 }, + { 0xfc13, 0x0000 }, + { 0xfc14, 0x0000 }, + { 0xfc15, 0x0000 }, + { 0xfc16, 0x0000 }, + { 0xfc17, 0x0000 }, + { 0xfc18, 0x0000 }, + { 0xfc19, 0x0000 }, + { 0xfc1a, 0x0000 }, + { 0xfc1b, 0x0000 }, + { 0xfc1c, 0x0000 }, + { 0xfc1d, 0x0000 }, + { 0xfc1e, 0x0000 }, + { 0xfc1f, 0x0000 }, + { 0xfc20, 0x0000 }, + { 0xfc21, 0x0000 }, + { 0xfc22, 0x0000 }, + { 0xfc23, 0x0000 }, + { 0xfc24, 0x0000 }, + { 0xfc25, 0x0000 }, + { 0xfc26, 0x0000 }, + { 0xfc27, 0x0000 }, + { 0xfc28, 0x0000 }, + { 0xfc29, 0x0000 }, + { 0xfc2a, 0x0000 }, + { 0xfc2b, 0x0000 }, + { 0xfc2c, 0x0000 }, + { 0xfc2d, 0x0000 }, + { 0xfc2e, 0x0000 }, + { 0xfc2f, 0x0000 }, + { 0xfc30, 0x0000 }, + { 0xfc31, 0x0000 }, + { 0xfc32, 0x0000 }, + { 0xfc33, 0x0000 }, + { 0xfc34, 0x0000 }, + { 0xfc35, 0x0000 }, + { 0xfc36, 0x0000 }, + { 0xfc37, 0x0000 }, + { 0xfc38, 0x0000 }, + { 0xfc39, 0x0000 }, + { 0xfc3a, 0x0000 }, + { 0xfc3b, 0x0000 }, + { 0xfc3c, 0x0000 }, + { 0xfc3d, 0x0000 }, + { 0xfc3e, 0x0000 }, + { 0xfc3f, 0x0000 }, + { 0xfc40, 0x0000 }, + { 0xfc41, 0x0000 }, + { 0xfc42, 0x0000 }, + { 0xfc43, 0x0000 }, + { 0xfc44, 0x0000 }, + { 0xfc45, 0x0000 }, + { 0xfc46, 0x0000 }, + { 0xfc47, 0x0000 }, + { 0xfc48, 0x0000 }, + { 0xfc49, 0x0000 }, + { 0xfc4a, 0x0000 }, + { 0xfc4b, 0x0000 }, + { 0xfc4c, 0x0000 }, + { 0xfc4d, 0x0000 }, + { 0xfc4e, 0x0000 }, + { 0xfc4f, 0x0000 }, + { 0xfc50, 0x0000 }, + { 0xfc51, 0x0000 }, + { 0xfc52, 0x0000 }, + { 0xfc53, 0x0000 }, + { 0xfc54, 0x0000 }, + { 0xfc55, 0x0000 }, + { 0xfc56, 0x0000 }, + { 0xfc57, 0x0000 }, + { 0xfc58, 0x0000 }, + { 0xfc59, 0x0000 }, + { 0xfc5a, 0x0000 }, + { 0xfc5b, 0x0000 }, + { 0xfc5c, 0x0000 }, + { 0xfc5d, 0x0000 }, + { 0xfc5e, 0x0000 }, + { 0xfc5f, 0x0000 }, + { 0xfc60, 0x0000 }, + { 0xfc61, 0x0000 }, + { 0xfc62, 0x0000 }, + { 0xfc63, 0x0000 }, + { 0xfc64, 0x0000 }, + { 0xfc65, 0x0000 }, + { 0xfc66, 0x0000 }, + { 0xfc67, 0x0000 }, + { 0xfc68, 0x0000 }, + { 0xfc69, 0x0000 }, + { 0xfc6a, 0x0000 }, + { 0xfc6b, 0x0000 }, + { 0xfc6c, 0x0000 }, + { 0xfc6d, 0x0000 }, + { 0xfc6e, 0x0000 }, + { 0xfc6f, 0x0000 }, + { 0xfc70, 0x0000 }, + { 0xfc71, 0x0000 }, + { 0xfc72, 0x0000 }, + { 0xfc73, 0x0000 }, + { 0xfc74, 0x0000 }, + { 0xfc75, 0x0000 }, + { 0xfc76, 0x0000 }, + { 0xfc77, 0x0000 }, + { 0xfc78, 0x0000 }, + { 0xfc79, 0x0000 }, + { 0xfc7a, 0x0000 }, + { 0xfc7b, 0x0000 }, + { 0xfc7c, 0x0000 }, + { 0xfc7d, 0x0000 }, + { 0xfc7e, 0x0000 }, + { 0xfc7f, 0x0000 }, + { 0xfc80, 0x0000 }, + { 0xfc81, 0x0000 }, + { 0xfc82, 0x0000 }, + { 0xfc83, 0x0000 }, + { 0xfc84, 0x0000 }, + { 0xfc85, 0x0000 }, + { 0xfc86, 0x0000 }, + { 0xfc87, 0x0000 }, + { 0xfc88, 0x0000 }, + { 0xfc89, 0x0000 }, + { 0xfc8a, 0x0000 }, + { 0xfc8b, 0x0000 }, + { 0xfc8c, 0x0000 }, + { 0xfc8d, 0x0000 }, + { 0xfc8e, 0x0000 }, + { 0xfc8f, 0x0000 }, + { 0xfc90, 0x0000 }, + { 0xfc91, 0x0000 }, + { 0xfc92, 0x0000 }, + { 0xfc93, 0x0000 }, + { 0xfc94, 0x0000 }, + { 0xfc95, 0x0000 }, + { 0xfc96, 0x0000 }, + { 0xfc97, 0x0000 }, + { 0xfc98, 0x0000 }, + { 0xfc99, 0x0000 }, + { 0xfc9a, 0x0000 }, + { 0xfc9b, 0x0000 }, + { 0xfc9c, 0x0000 }, + { 0xfc9d, 0x0000 }, + { 0xfc9e, 0x0000 }, + { 0xfc9f, 0x0000 }, + { 0xfca0, 0x0000 }, + { 0xfca1, 0x0000 }, + { 0xfca2, 0x0000 }, + { 0xfca3, 0x0000 }, + { 0xfca4, 0x0000 }, + { 0xfca5, 0x0000 }, + { 0xfca6, 0x0000 }, + { 0xfca7, 0x0000 }, + { 0xfca8, 0x0000 }, + { 0xfca9, 0x0000 }, + { 0xfcaa, 0x0000 }, + { 0xfcab, 0x0000 }, + { 0xfcac, 0x0000 }, + { 0xfcad, 0x0000 }, + { 0xfcae, 0x0000 }, + { 0xfcaf, 0x0000 }, + { 0xfcb0, 0x0000 }, + { 0xfcb1, 0x0000 }, + { 0xfcb2, 0x0000 }, + { 0xfcb3, 0x0000 }, + { 0xfcb4, 0x0000 }, + { 0xfcb5, 0x0000 }, + { 0xfcb6, 0x0000 }, + { 0xfcb7, 0x0000 }, + { 0xfcb8, 0x0000 }, + { 0xfcb9, 0x0000 }, + { 0xfcba, 0x0000 }, + { 0xfcbb, 0x0000 }, + { 0xfcbc, 0x0000 }, + { 0xfcbd, 0x0000 }, + { 0xfcbe, 0x0000 }, + { 0xfcbf, 0x0000 }, + { 0xfcc0, 0x0000 }, + { 0xfcc1, 0x0000 }, + { 0xfcc2, 0x0000 }, + { 0xfcc3, 0x0000 }, + { 0xfcc4, 0x0000 }, + { 0xfcc5, 0x0000 }, + { 0xfcc6, 0x0000 }, + { 0xfcc7, 0x0000 }, + { 0xfcc8, 0x0000 }, + { 0xfcc9, 0x0000 }, + { 0xfcca, 0x0000 }, + { 0xfccb, 0x0000 }, + { 0xfccc, 0x0000 }, + { 0xfccd, 0x0000 }, + { 0xfcce, 0x0000 }, + { 0xfccf, 0x0000 }, + { 0xfcd0, 0x0000 }, + { 0xfcd1, 0x0000 }, + { 0xfcd2, 0x0000 }, + { 0xfcd3, 0x0000 }, + { 0xfcd4, 0x0000 }, + { 0xfcd5, 0x0000 }, + { 0xfcd6, 0x0000 }, + { 0xfcd7, 0x0000 }, + { 0xfcd8, 0x0000 }, + { 0xfcd9, 0x0000 }, + { 0xfcda, 0x0000 }, + { 0xfcdb, 0x0000 }, + { 0xfcdc, 0x0000 }, + { 0xfcdd, 0x0000 }, + { 0xfcde, 0x0000 }, + { 0xfcdf, 0x0000 }, + { 0xfce0, 0x0000 }, + { 0xfce1, 0x0000 }, + { 0xfce2, 0x0000 }, + { 0xfce3, 0x0000 }, + { 0xfce4, 0x0000 }, + { 0xfce5, 0x0000 }, + { 0xfce6, 0x0000 }, + { 0xfce7, 0x0000 }, + { 0xfce8, 0x0000 }, + { 0xfce9, 0x0000 }, + { 0xfcea, 0x0000 }, + { 0xfceb, 0x0000 }, + { 0xfcec, 0x0000 }, + { 0xfced, 0x0000 }, + { 0xfcee, 0x0000 }, + { 0xfcef, 0x0000 }, + { 0xfcf0, 0x0000 }, + { 0xfcf1, 0x0000 }, + { 0xfcf2, 0x0000 }, + { 0xfcf3, 0x0000 }, + { 0xfcf4, 0x0000 }, + { 0xfcf5, 0x0000 }, + { 0xfcf6, 0x0000 }, + { 0xfcf7, 0x0000 }, + { 0xfcf8, 0x0000 }, + { 0xfcf9, 0x0000 }, + { 0xfcfa, 0x0000 }, + { 0xfcfb, 0x0000 }, + { 0xfcfc, 0x0000 }, + { 0xfcfd, 0x0000 }, + { 0xfcfe, 0x0000 }, + { 0xfcff, 0x0000 }, + { 0xfd00, 0x0000 }, + { 0xfd01, 0x0000 }, + { 0xfd02, 0x0000 }, + { 0xfd03, 0x0000 }, + { 0xfd04, 0x0000 }, + { 0xfd05, 0x0000 }, + { 0xfd06, 0x0000 }, + { 0xfd07, 0x0000 }, + { 0xfd08, 0x0000 }, + { 0xfd09, 0x0000 }, + { 0xfd0a, 0x0000 }, + { 0xfd0b, 0x0000 }, + { 0xfd0c, 0x0000 }, + { 0xfd0d, 0x0000 }, + { 0xfd0e, 0x0000 }, + { 0xfd0f, 0x0000 }, + { 0xfd10, 0x0000 }, + { 0xfd11, 0x0000 }, + { 0xfd12, 0x0000 }, + { 0xfd13, 0x0000 }, + { 0xfd14, 0x0000 }, + { 0xfd15, 0x0000 }, + { 0xfd16, 0x0000 }, + { 0xfd17, 0x0000 }, + { 0xfd18, 0x0000 }, + { 0xfd19, 0x0000 }, + { 0xfd1a, 0x0000 }, + { 0xfd1b, 0x0000 }, + { 0xfd1c, 0x0000 }, + { 0xfd1d, 0x0000 }, + { 0xfd1e, 0x0000 }, + { 0xfd1f, 0x0000 }, + { 0xfd20, 0x0000 }, + { 0xfd21, 0x0000 }, + { 0xfd22, 0x0000 }, + { 0xfd23, 0x0000 }, + { 0xfd24, 0x0000 }, + { 0xfd25, 0x0000 }, + { 0xfd26, 0x0000 }, + { 0xfd27, 0x0000 }, + { 0xfd28, 0x0000 }, + { 0xfd29, 0x0000 }, + { 0xfd2a, 0x0000 }, + { 0xfd2b, 0x0000 }, + { 0xfd2c, 0x0000 }, + { 0xfd2d, 0x0000 }, + { 0xfd2e, 0x0000 }, + { 0xfd2f, 0x0000 }, + { 0xfd30, 0x0000 }, + { 0xfd31, 0x0000 }, + { 0xfd32, 0x0000 }, + { 0xfd33, 0x0000 }, + { 0xfd34, 0x0000 }, + { 0xfd35, 0x0000 }, + { 0xfd36, 0x0000 }, + { 0xfd37, 0x0000 }, + { 0xfd38, 0x0000 }, + { 0xfd39, 0x0000 }, + { 0xfd3a, 0x0000 }, + { 0xfd3b, 0x0000 }, + { 0xfd3c, 0x0000 }, + { 0xfd3d, 0x0000 }, + { 0xfd3e, 0x0000 }, + { 0xfd3f, 0x0000 }, + { 0xfd40, 0x0000 }, + { 0xfd41, 0x0000 }, + { 0xfd42, 0x0000 }, + { 0xfd43, 0x0000 }, + { 0xfd44, 0x0000 }, + { 0xfd45, 0x0000 }, + { 0xfd46, 0x0000 }, + { 0xfd47, 0x0000 }, + { 0xfd48, 0x0000 }, + { 0xfd49, 0x0000 }, + { 0xfd4a, 0x0000 }, + { 0xfd4b, 0x0000 }, + { 0xfd4c, 0x0000 }, + { 0xfd4d, 0x0000 }, + { 0xfd4e, 0x0000 }, + { 0xfd4f, 0x0000 }, + { 0xfd50, 0x0000 }, + { 0xfd51, 0x0000 }, + { 0xfd52, 0x0000 }, + { 0xfd53, 0x0000 }, + { 0xfd54, 0x0000 }, + { 0xfd55, 0x0000 }, + { 0xfd56, 0x0000 }, + { 0xfd57, 0x0000 }, + { 0xfd58, 0x0000 }, + { 0xfd59, 0x0000 }, + { 0xfd5a, 0x0000 }, + { 0xfd5b, 0x0000 }, + { 0xfd5c, 0x0000 }, + { 0xfd5d, 0x0000 }, + { 0xfd5e, 0x0000 }, + { 0xfd5f, 0x0000 }, + { 0xfd60, 0x0000 }, + { 0xfd61, 0x0000 }, + { 0xfd62, 0x0000 }, + { 0xfd63, 0x0000 }, + { 0xfd64, 0x0000 }, + { 0xfd65, 0x0000 }, + { 0xfd66, 0x0000 }, + { 0xfd67, 0x0000 }, + { 0xfd68, 0x0000 }, + { 0xfd69, 0x0000 }, + { 0xfd6a, 0x0000 }, + { 0xfd6b, 0x0000 }, + { 0xfd6c, 0x0000 }, + { 0xfd6d, 0x0000 }, + { 0xfd6e, 0x0000 }, + { 0xfd6f, 0x0000 }, + { 0xfd70, 0x0000 }, + { 0xfd71, 0x0000 }, + { 0xfd72, 0x0000 }, + { 0xfd73, 0x0000 }, + { 0xfd74, 0x0000 }, + { 0xfd75, 0x0000 }, + { 0xfd76, 0x0000 }, + { 0xfd77, 0x0000 }, + { 0xfd78, 0x0000 }, + { 0xfd79, 0x0000 }, + { 0xfd7a, 0x0000 }, + { 0xfd7b, 0x0000 }, + { 0xfd7c, 0x0000 }, + { 0xfd7d, 0x0000 }, + { 0xfd7e, 0x0000 }, + { 0xfd7f, 0x0000 }, + { 0xfd80, 0x0000 }, + { 0xfd81, 0x0000 }, + { 0xfd82, 0x0000 }, + { 0xfd83, 0x0000 }, + { 0xfd84, 0x0000 }, + { 0xfd85, 0x0000 }, + { 0xfd86, 0x0000 }, + { 0xfd87, 0x0000 }, + { 0xfd88, 0x0000 }, + { 0xfd89, 0x0000 }, + { 0xfd8a, 0x0000 }, + { 0xfd8b, 0x0000 }, + { 0xfd8c, 0x0000 }, + { 0xfd8d, 0x0000 }, + { 0xfd8e, 0x0000 }, + { 0xfd8f, 0x0000 }, + { 0xfd90, 0x0000 }, + { 0xfd91, 0x0000 }, + { 0xfd92, 0x0000 }, + { 0xfd93, 0x0000 }, + { 0xfd94, 0x0000 }, + { 0xfd95, 0x0000 }, + { 0xfd96, 0x0000 }, + { 0xfd97, 0x0000 }, + { 0xfd98, 0x0000 }, + { 0xfd99, 0x0000 }, + { 0xfd9a, 0x0000 }, + { 0xfd9b, 0x0000 }, + { 0xfd9c, 0x0000 }, + { 0xfd9d, 0x0000 }, + { 0xfd9e, 0x0000 }, + { 0xfd9f, 0x0000 }, + { 0xfda0, 0x0000 }, + { 0xfda1, 0x0000 }, + { 0xfda2, 0x0000 }, + { 0xfda3, 0x0000 }, + { 0xfda4, 0x0000 }, + { 0xfda5, 0x0000 }, + { 0xfda6, 0x0000 }, + { 0xfda7, 0x0000 }, + { 0xfda8, 0x0000 }, + { 0xfda9, 0x0000 }, + { 0xfdaa, 0x0000 }, + { 0xfdab, 0x0000 }, + { 0xfdac, 0x0000 }, + { 0xfdad, 0x0000 }, + { 0xfdae, 0x0000 }, + { 0xfdaf, 0x0000 }, + { 0xfdb0, 0x0000 }, + { 0xfdb1, 0x0000 }, + { 0xfdb2, 0x0000 }, + { 0xfdb3, 0x0000 }, + { 0xfdb4, 0x0000 }, + { 0xfdb5, 0x0000 }, + { 0xfdb6, 0x0000 }, + { 0xfdb7, 0x0000 }, + { 0xfdb8, 0x0000 }, + { 0xfdb9, 0x0000 }, + { 0xfdba, 0x0000 }, + { 0xfdbb, 0x0000 }, + { 0xfdbc, 0x0000 }, + { 0xfdbd, 0x0000 }, + { 0xfdbe, 0x0000 }, + { 0xfdbf, 0x0000 }, + { 0xfdc0, 0x0000 }, + { 0xfdc1, 0x0000 }, + { 0xfdc2, 0x0000 }, + { 0xfdc3, 0x0000 }, + { 0xfdc4, 0x0000 }, + { 0xfdc5, 0x0000 }, + { 0xfdc6, 0x0000 }, + { 0xfdc7, 0x0000 }, + { 0xfdc8, 0x0000 }, + { 0xfdc9, 0x0000 }, + { 0xfdca, 0x0000 }, + { 0xfdcb, 0x0000 }, + { 0xfdcc, 0x0000 }, + { 0xfdcd, 0x0000 }, + { 0xfdce, 0x0000 }, + { 0xfdcf, 0x0000 }, + { 0xfdd0, 0x0000 }, + { 0xfdd1, 0x0000 }, + { 0xfdd2, 0x0000 }, + { 0xfdd3, 0x0000 }, + { 0xfdd4, 0x0000 }, + { 0xfdd5, 0x0000 }, + { 0xfdd6, 0x0000 }, + { 0xfdd7, 0x0000 }, + { 0xfdd8, 0x0000 }, + { 0xfdd9, 0x0000 }, + { 0xfdda, 0x0000 }, + { 0xfddb, 0x0000 }, + { 0xfddc, 0x0000 }, + { 0xfddd, 0x0000 }, + { 0xfdde, 0x0000 }, + { 0xfddf, 0x0000 }, + { 0xfde0, 0x0000 }, + { 0xfde1, 0x0000 }, + { 0xfde2, 0x0000 }, + { 0xfde3, 0x0000 }, + { 0xfde4, 0x0000 }, + { 0xfde5, 0x0000 }, + { 0xfde6, 0x0000 }, + { 0xfde7, 0x0000 }, + { 0xfde8, 0x0000 }, + { 0xfde9, 0x0000 }, + { 0xfdea, 0x0000 }, + { 0xfdeb, 0x0000 }, + { 0xfdec, 0x0000 }, + { 0xfded, 0x0000 }, + { 0xfdee, 0x0000 }, + { 0xfdef, 0x0000 }, + { 0xfdf0, 0x0000 }, + { 0xfdf1, 0x0000 }, + { 0xfdf2, 0x0000 }, + { 0xfdf3, 0x0000 }, + { 0xfdf4, 0x0000 }, + { 0xfdf5, 0x0000 }, + { 0xfdf6, 0x0000 }, + { 0xfdf7, 0x0000 }, + { 0xfdf8, 0x0000 }, + { 0xfdf9, 0x0000 }, + { 0xfdfa, 0x0000 }, + { 0xfdfb, 0x0000 }, + { 0xfdfc, 0x0000 }, + { 0xfdfd, 0x0000 }, + { 0xfdfe, 0x0000 }, + { 0xfdff, 0x0000 }, + { 0xfe00, 0x0000 }, + { 0xfe01, 0x0000 }, + { 0xfe02, 0x0000 }, + { 0xfe03, 0x0000 }, + { 0xfe04, 0x0000 }, + { 0xfe05, 0x0000 }, + { 0xfe06, 0x0000 }, + { 0xfe07, 0x0000 }, + { 0xfe08, 0x0000 }, + { 0xfe09, 0x0000 }, + { 0xfe0a, 0x0000 }, + { 0xfe0b, 0x0000 }, + { 0xfe0c, 0x0000 }, + { 0xfe0d, 0x0000 }, + { 0xfe0e, 0x0000 }, + { 0xfe0f, 0x0000 }, + { 0xfe10, 0x0000 }, + { 0xfe11, 0x0000 }, + { 0xfe12, 0x0000 }, + { 0xfe13, 0x0000 }, + { 0xfe14, 0x0000 }, + { 0xfe15, 0x0000 }, + { 0xfe16, 0x0000 }, + { 0xfe17, 0x0000 }, + { 0xfe18, 0x0000 }, + { 0xfe19, 0x0000 }, + { 0xfe1a, 0x0000 }, + { 0xfe1b, 0x0000 }, + { 0xfe1c, 0x0000 }, + { 0xfe1d, 0x0000 }, + { 0xfe1e, 0x0000 }, + { 0xfe1f, 0x0000 }, + { 0xfe20, 0x0000 }, + { 0xfe21, 0x0000 }, + { 0xfe22, 0x0000 }, + { 0xfe23, 0x0000 }, + { 0xfe24, 0x0000 }, + { 0xfe25, 0x0000 }, + { 0xfe26, 0x0000 }, + { 0xfe27, 0x0000 }, + { 0xfe28, 0x0000 }, + { 0xfe29, 0x0000 }, + { 0xfe2a, 0x0000 }, + { 0xfe2b, 0x0000 }, + { 0xfe2c, 0x0000 }, + { 0xfe2d, 0x0000 }, + { 0xfe2e, 0x0000 }, + { 0xfe2f, 0x0000 }, + { 0xfe30, 0x0000 }, + { 0xfe31, 0x0000 }, + { 0xfe32, 0x0000 }, + { 0xfe33, 0x0000 }, + { 0xfe34, 0x0000 }, + { 0xfe35, 0x0000 }, + { 0xfe36, 0x0000 }, + { 0xfe37, 0x0000 }, + { 0xfe38, 0x0000 }, + { 0xfe39, 0x0000 }, + { 0xfe3a, 0x0000 }, + { 0xfe3b, 0x0000 }, + { 0xfe3c, 0x0000 }, + { 0xfe3d, 0x0000 }, + { 0xfe3e, 0x0000 }, + { 0xfe3f, 0x0000 }, + { 0xfe40, 0x0000 }, + { 0xfe41, 0x0000 }, + { 0xfe42, 0x0000 }, + { 0xfe43, 0x0000 }, + { 0xfe44, 0x0000 }, + { 0xfe45, 0x0000 }, + { 0xfe46, 0x0000 }, + { 0xfe47, 0x0000 }, + { 0xfe48, 0x0000 }, + { 0xfe49, 0x0000 }, + { 0xfe4a, 0x0000 }, + { 0xfe4b, 0x0000 }, + { 0xfe4c, 0x0000 }, + { 0xfe4d, 0x0000 }, + { 0xfe4e, 0x0000 }, + { 0xfe4f, 0x0000 }, + { 0xfe50, 0x0000 }, + { 0xfe51, 0x0000 }, + { 0xfe52, 0x0000 }, + { 0xfe53, 0x0000 }, + { 0xfe54, 0x0000 }, + { 0xfe55, 0x0000 }, + { 0xfe56, 0x0000 }, + { 0xfe57, 0x0000 }, + { 0xfe58, 0x0000 }, + { 0xfe59, 0x0000 }, + { 0xfe5a, 0x0000 }, + { 0xfe5b, 0x0000 }, + { 0xfe5c, 0x0000 }, + { 0xfe5d, 0x0000 }, + { 0xfe5e, 0x0000 }, + { 0xfe5f, 0x0000 }, + { 0xfe60, 0x0000 }, + { 0xfe61, 0x0000 }, + { 0xfe62, 0x0000 }, + { 0xfe63, 0x0000 }, + { 0xfe64, 0x0000 }, + { 0xfe65, 0x0000 }, + { 0xfe66, 0x0000 }, + { 0xfe67, 0x0000 }, + { 0xfe68, 0x0000 }, + { 0xfe69, 0x0000 }, + { 0xfe6a, 0x0000 }, + { 0xfe6b, 0x0000 }, + { 0xfe6c, 0x0000 }, + { 0xfe6d, 0x0000 }, + { 0xfe6e, 0x0000 }, + { 0xfe6f, 0x0000 }, + { 0xfe70, 0x0000 }, + { 0xfe71, 0x0000 }, + { 0xfe72, 0x0000 }, + { 0xfe73, 0x0000 }, + { 0xfe74, 0x0000 }, + { 0xfe75, 0x0000 }, + { 0xfe76, 0x0000 }, + { 0xfe77, 0x0000 }, + { 0xfe78, 0x0000 }, + { 0xfe79, 0x0000 }, + { 0xfe7a, 0x0000 }, + { 0xfe7b, 0x0000 }, + { 0xfe7c, 0x0000 }, + { 0xfe7d, 0x0000 }, + { 0xfe7e, 0x0000 }, + { 0xfe7f, 0x0000 }, + { 0xfe80, 0x0000 }, + { 0xfe81, 0x0000 }, + { 0xfe82, 0x0000 }, + { 0xfe83, 0x0000 }, + { 0xfe84, 0x0000 }, + { 0xfe85, 0x0000 }, + { 0xfe86, 0x0000 }, + { 0xfe87, 0x0000 }, + { 0xfe88, 0x0000 }, + { 0xfe89, 0x0000 }, + { 0xfe8a, 0x0000 }, + { 0xfe8b, 0x0000 }, + { 0xfe8c, 0x0000 }, + { 0xfe8d, 0x0000 }, + { 0xfe8e, 0x0000 }, + { 0xfe8f, 0x0000 }, + { 0xfe90, 0x0000 }, + { 0xfe91, 0x0000 }, + { 0xfe92, 0x0000 }, + { 0xfe93, 0x0000 }, + { 0xfe94, 0x0000 }, + { 0xfe95, 0x0000 }, + { 0xfe96, 0x0000 }, + { 0xfe97, 0x0000 }, + { 0xfe98, 0x0000 }, + { 0xfe99, 0x0000 }, + { 0xfe9a, 0x0000 }, + { 0xfe9b, 0x0000 }, + { 0xfe9c, 0x0000 }, + { 0xfe9d, 0x0000 }, + { 0xfe9e, 0x0000 }, + { 0xfe9f, 0x0000 }, + { 0xfea0, 0x0000 }, + { 0xfea1, 0x0000 }, + { 0xfea2, 0x0000 }, + { 0xfea3, 0x0000 }, + { 0xfea4, 0x0000 }, + { 0xfea5, 0x0000 }, + { 0xfea6, 0x0000 }, + { 0xfea7, 0x0000 }, + { 0xfea8, 0x0000 }, + { 0xfea9, 0x0000 }, + { 0xfeaa, 0x0000 }, + { 0xfeab, 0x0000 }, + { 0xfeac, 0x0000 }, + { 0xfead, 0x0000 }, + { 0xfeae, 0x0000 }, + { 0xfeaf, 0x0000 }, + { 0xfeb0, 0x0000 }, + { 0xfeb1, 0x0000 }, + { 0xfeb2, 0x0000 }, + { 0xfeb3, 0x0000 }, + { 0xfeb4, 0x0000 }, + { 0xfeb5, 0x0000 }, + { 0xfeb6, 0x0000 }, + { 0xfeb7, 0x0000 }, + { 0xfeb8, 0x0000 }, + { 0xfeb9, 0x0000 }, + { 0xfeba, 0x0000 }, + { 0xfebb, 0x0000 }, + { 0xfebc, 0x0000 }, + { 0xfebd, 0x0000 }, + { 0xfebe, 0x0000 }, + { 0xfebf, 0x0000 }, + { 0xfec0, 0x0000 }, + { 0xfec1, 0x0000 }, + { 0xfec2, 0x0000 }, + { 0xfec3, 0x0000 }, + { 0xfec4, 0x0000 }, + { 0xfec5, 0x0000 }, + { 0xfec6, 0x0000 }, + { 0xfec7, 0x0000 }, + { 0xfec8, 0x0000 }, + { 0xfec9, 0x0000 }, + { 0xfeca, 0x0000 }, + { 0xfecb, 0x0000 }, + { 0xfecc, 0x0000 }, + { 0xfecd, 0x0000 }, + { 0xfece, 0x0000 }, + { 0xfecf, 0x0000 }, + { 0xfed0, 0x0000 }, + { 0xfed1, 0x0000 }, + { 0xfed2, 0x0000 }, + { 0xfed3, 0x0000 }, + { 0xfed4, 0x0000 }, + { 0xfed5, 0x0000 }, + { 0xfed6, 0x0000 }, + { 0xfed7, 0x0000 }, + { 0xfed8, 0x0000 }, + { 0xfed9, 0x0000 }, + { 0xfeda, 0x0000 }, + { 0xfedb, 0x0000 }, + { 0xfedc, 0x0000 }, + { 0xfedd, 0x0000 }, + { 0xfede, 0x0000 }, + { 0xfedf, 0x0000 }, + { 0xfee0, 0x0000 }, + { 0xfee1, 0x0000 }, + { 0xfee2, 0x0000 }, + { 0xfee3, 0x0000 }, + { 0xfee4, 0x0000 }, + { 0xfee5, 0x0000 }, + { 0xfee6, 0x0000 }, + { 0xfee7, 0x0000 }, + { 0xfee8, 0x0000 }, + { 0xfee9, 0x0000 }, + { 0xfeea, 0x0000 }, + { 0xfeeb, 0x0000 }, + { 0xfeec, 0x0000 }, + { 0xfeed, 0x0000 }, + { 0xfeee, 0x0000 }, + { 0xfeef, 0x0000 }, + { 0xfef0, 0x0000 }, + { 0xfef1, 0x0000 }, + { 0xfef2, 0x0000 }, + { 0xfef3, 0x0000 }, + { 0xfef4, 0x0000 }, + { 0xfef5, 0x0000 }, + { 0xfef6, 0x0000 }, + { 0xfef7, 0x0000 }, + { 0xfef8, 0x0000 }, + { 0xfef9, 0x0000 }, + { 0xfefa, 0x0000 }, + { 0xfefb, 0x0000 }, + { 0xfefc, 0x0000 }, + { 0xfefd, 0x0000 }, + { 0xfefe, 0x0000 }, + { 0xfeff, 0x0000 }, + { 0xff00, 0x0000 }, + { 0xff01, 0x0000 }, + { 0xff02, 0x0000 }, + { 0xff03, 0x0000 }, + { 0xff04, 0x0000 }, + { 0xff05, 0x0000 }, + { 0xff06, 0x0000 }, + { 0xff07, 0x0000 }, + { 0xff08, 0x0000 }, + { 0xff09, 0x0000 }, + { 0xff0a, 0x0000 }, + { 0xff0b, 0x0000 }, + { 0xff0c, 0x0000 }, + { 0xff0d, 0x0000 }, + { 0xff0e, 0x0000 }, + { 0xff0f, 0x0000 }, + { 0xff10, 0x0000 }, + { 0xff11, 0x0000 }, + { 0xff12, 0x0000 }, + { 0xff13, 0x0000 }, + { 0xff14, 0x0000 }, + { 0xff15, 0x0000 }, + { 0xff16, 0x0000 }, + { 0xff17, 0x0000 }, + { 0xff18, 0x0000 }, + { 0xff19, 0x0000 }, + { 0xff1a, 0x0000 }, + { 0xff1b, 0x0000 }, + { 0xff1c, 0x0000 }, + { 0xff1d, 0x0000 }, + { 0xff1e, 0x0000 }, + { 0xff1f, 0x0000 }, + { 0xff20, 0x0000 }, + { 0xff21, 0x0000 }, + { 0xff22, 0x0000 }, + { 0xff23, 0x0000 }, + { 0xff24, 0x0000 }, + { 0xff25, 0x0000 }, + { 0xff26, 0x0000 }, + { 0xff27, 0x0000 }, + { 0xff28, 0x0000 }, + { 0xff29, 0x0000 }, + { 0xff2a, 0x0000 }, + { 0xff2b, 0x0000 }, + { 0xff2c, 0x0000 }, + { 0xff2d, 0x0000 }, + { 0xff2e, 0x0000 }, + { 0xff2f, 0x0000 }, + { 0xff30, 0x0000 }, + { 0xff31, 0x0000 }, + { 0xff32, 0x0000 }, + { 0xff33, 0x0000 }, + { 0xff34, 0x0000 }, + { 0xff35, 0x0000 }, + { 0xff36, 0x0000 }, + { 0xff37, 0x0000 }, + { 0xff38, 0x0000 }, + { 0xff39, 0x0000 }, + { 0xff3a, 0x0000 }, + { 0xff3b, 0x0000 }, + { 0xff3c, 0x0000 }, + { 0xff3d, 0x0000 }, + { 0xff3e, 0x0000 }, + { 0xff3f, 0x0000 }, + { 0xff40, 0x0000 }, + { 0xff41, 0x0000 }, + { 0xff42, 0x0000 }, + { 0xff43, 0x0000 }, + { 0xff44, 0x0000 }, + { 0xff45, 0x0000 }, + { 0xff46, 0x0000 }, + { 0xff47, 0x0000 }, + { 0xff48, 0x0000 }, + { 0xff49, 0x0000 }, + { 0xff4a, 0x0000 }, + { 0xff4b, 0x0000 }, + { 0xff4c, 0x0000 }, + { 0xff4d, 0x0000 }, + { 0xff4e, 0x0000 }, + { 0xff4f, 0x0000 }, + { 0xff50, 0x0000 }, + { 0xff51, 0x0000 }, + { 0xff52, 0x0000 }, + { 0xff53, 0x0000 }, + { 0xff54, 0x0000 }, + { 0xff55, 0x0000 }, + { 0xff56, 0x0000 }, + { 0xff57, 0x0000 }, + { 0xff58, 0x0000 }, + { 0xff59, 0x0000 }, + { 0xff5a, 0x0000 }, + { 0xff5b, 0x0000 }, + { 0xff5c, 0x0000 }, + { 0xff5d, 0x0000 }, + { 0xff5e, 0x0000 }, + { 0xff5f, 0x0000 }, + { 0xff60, 0x0000 }, + { 0xff61, 0x0000 }, + { 0xff62, 0x0000 }, + { 0xff63, 0x0000 }, + { 0xff64, 0x0000 }, + { 0xff65, 0x0000 }, + { 0xff66, 0x0000 }, + { 0xff67, 0x0000 }, + { 0xff68, 0x0000 }, + { 0xff69, 0x0000 }, + { 0xff6a, 0x0000 }, + { 0xff6b, 0x0000 }, + { 0xff6c, 0x0000 }, + { 0xff6d, 0x0000 }, + { 0xff6e, 0x0000 }, + { 0xff6f, 0x0000 }, + { 0xff70, 0x0000 }, + { 0xff71, 0x0000 }, + { 0xff72, 0x0000 }, + { 0xff73, 0x0000 }, + { 0xff74, 0x0000 }, + { 0xff75, 0x0000 }, + { 0xff76, 0x0000 }, + { 0xff77, 0x0000 }, + { 0xff78, 0x0000 }, + { 0xff79, 0x0000 }, + { 0xff7a, 0x0000 }, + { 0xff7b, 0x0000 }, + { 0xff7c, 0x0000 }, + { 0xff7d, 0x0000 }, + { 0xff7e, 0x0000 }, + { 0xff7f, 0x0000 }, + { 0xff80, 0x0000 }, + { 0xff81, 0x0000 }, + { 0xff82, 0x0000 }, + { 0xff83, 0x0000 }, + { 0xff84, 0x0000 }, + { 0xff85, 0x0000 }, + { 0xff86, 0x0000 }, + { 0xff87, 0x0000 }, + { 0xff88, 0x0000 }, + { 0xff89, 0x0000 }, + { 0xff8a, 0x0000 }, + { 0xff8b, 0x0000 }, + { 0xff8c, 0x0000 }, + { 0xff8d, 0x0000 }, + { 0xff8e, 0x0000 }, + { 0xff8f, 0x0000 }, + { 0xff90, 0x0000 }, + { 0xff91, 0x0000 }, + { 0xff92, 0x0000 }, + { 0xff93, 0x0000 }, + { 0xff94, 0x0000 }, + { 0xff95, 0x0000 }, + { 0xff96, 0x0000 }, + { 0xff97, 0x0000 }, + { 0xff98, 0x0000 }, + { 0xff99, 0x0000 }, + { 0xff9a, 0x0000 }, + { 0xff9b, 0x0000 }, + { 0xff9c, 0x0000 }, + { 0xff9d, 0x0000 }, + { 0xff9e, 0x0000 }, + { 0xff9f, 0x0000 }, + { 0xffa0, 0x0000 }, + { 0xffa1, 0x0000 }, + { 0xffa2, 0x0000 }, + { 0xffa3, 0x0000 }, + { 0xffa4, 0x0000 }, + { 0xffa5, 0x0000 }, + { 0xffa6, 0x0000 }, + { 0xffa7, 0x0000 }, + { 0xffa8, 0x0000 }, + { 0xffa9, 0x0000 }, + { 0xffaa, 0x0000 }, + { 0xffab, 0x0000 }, + { 0xffac, 0x0000 }, + { 0xffad, 0x0000 }, + { 0xffae, 0x0000 }, + { 0xffaf, 0x0000 }, + { 0xffb0, 0x0000 }, + { 0xffb1, 0x0000 }, + { 0xffb2, 0x0000 }, + { 0xffb3, 0x0000 }, + { 0xffb4, 0x0000 }, + { 0xffb5, 0x0000 }, + { 0xffb6, 0x0000 }, + { 0xffb7, 0x0000 }, + { 0xffb8, 0x0000 }, + { 0xffb9, 0x0000 }, + { 0xffba, 0x0000 }, + { 0xffbb, 0x0000 }, + { 0xffbc, 0x0000 }, + { 0xffbd, 0x0000 }, + { 0xffbe, 0x0000 }, + { 0xffbf, 0x0000 }, + { 0xffc0, 0x0000 }, + { 0xffc1, 0x0000 }, + { 0xffc2, 0x0000 }, + { 0xffc3, 0x0000 }, + { 0xffc4, 0x0000 }, + { 0xffc5, 0x0000 }, + { 0xffc6, 0x0000 }, + { 0xffc7, 0x0000 }, + { 0xffc8, 0x0000 }, + { 0xffc9, 0x0000 }, + { 0xffca, 0x0000 }, + { 0xffcb, 0x0000 }, + { 0xffcc, 0x0000 }, + { 0xffcd, 0x0000 }, + { 0xffce, 0x0000 }, + { 0xffcf, 0x0000 }, + { 0xffd0, 0x0000 }, + { 0xffd1, 0x0000 }, + { 0xffd2, 0x0000 }, + { 0xffd3, 0x0000 }, + { 0xffd4, 0x0000 }, + { 0xffd5, 0x0000 }, + { 0xffd6, 0x0000 }, + { 0xffd7, 0x0000 }, + { 0xffd8, 0x0000 }, + { 0xffd9, 0x0000 }, + { 0xffda, 0x0000 }, + { 0xffdb, 0x0000 }, + { 0xffdc, 0x0000 }, + { 0xffdd, 0x0000 }, + { 0xffde, 0x0000 }, + { 0xffdf, 0x0000 }, + { 0xffe0, 0x0000 }, + { 0xffe1, 0x0000 }, + { 0xffe2, 0x0000 }, + { 0xffe3, 0x0000 }, + { 0xffe4, 0x0000 }, + { 0xffe5, 0x0000 }, + { 0xffe6, 0x0000 }, + { 0xffe7, 0x0000 }, + { 0xffe8, 0x0000 }, + { 0xffe9, 0x0000 }, + { 0xffea, 0x0000 }, + { 0xffeb, 0x0000 }, + { 0xffec, 0x0000 }, + { 0xffed, 0x0000 }, + { 0xffee, 0x0000 }, + { 0xffef, 0x0000 }, + { 0xfff0, 0x0000 }, + { 0xfff1, 0x0000 }, + { 0xfff2, 0x0000 }, + { 0xfff3, 0x0000 }, + { 0xfff4, 0x0000 }, + { 0xfff5, 0x0000 }, + { 0xfff6, 0x0000 }, + { 0xfff7, 0x0000 }, + { 0xfff8, 0x0000 }, + { 0xfff9, 0x0000 }, + { 0xfffa, 0x0000 }, + { 0xfffb, 0x0000 }, + { 0xfffc, 0x0000 }, + { 0xfffd, 0x0000 }, + { 0xfffe, 0x0000 }, + { 0xffff, 0x0000 }, + +}; + + +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c new file mode 100644 index 000000000000..8cb67827a8db --- /dev/null +++ b/sound/soc/codecs/rt700.c @@ -0,0 +1,1600 @@ +/* + * rt700.c -- rt700 ALSA SoC audio driver + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * ALC700 ASoC Codec Driver based Intel Dummy SdW codec driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt700.h" + +struct sdw_stream_data { + int stream_tag; +}; + +struct hda_cmd { + u16 vid; + u8 nid; + u16 payload; +}; + +static struct hda_cmd hda_dump_list[] = { + /* vid, nid, payload */ + {0xf00, 0x00, 0x00}, /* Vendor ID */ +#if 0 + {0xf00, 0x00, 0x02}, /* Revision ID */ + {0xf00, 0x00, 0x04}, /* Subordinate Node Count */ + {0xf00, 0x01, 0x04}, /* Subordinate Node Count */ + {0xf00, 0x01, 0x05}, /* Function Group Type */ + {0xf00, 0x01, 0x08}, /* Audio Function Capabilities */ + {0xf00, 0x00, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x01, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x02, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x03, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x04, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x05, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x06, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x07, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x08, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x09, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x0a, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x27, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x22, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x23, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x24, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x25, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x14, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x15, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x17, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x18, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x29, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x19, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1a, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1b, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x16, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1d, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x21, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1e, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1f, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x12, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x13, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x20, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x01, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x02, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x03, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x04, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x05, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x06, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x07, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x08, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x09, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x0a, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x27, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x01, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x02, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x03, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x04, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x05, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x06, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x07, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x08, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x09, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x0a, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x27, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x12, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x13, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x14, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x15, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x16, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x17, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x18, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x19, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1a, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1b, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1d, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1e, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1f, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x21, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x29, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x07, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x08, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x09, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x12, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x13, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x18, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x19, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x1a, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x1b, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x22, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x23, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x24, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x25, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x29, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x02, 0x12}, /* Output Amplifiers */ + {0xf00, 0x03, 0x12}, /* Output Amplifiers */ + {0xf00, 0x14, 0x12}, /* Output Amplifiers */ + {0xf00, 0x15, 0x12}, /* Output Amplifiers */ + {0xf00, 0x16, 0x12}, /* Output Amplifiers */ + {0xf00, 0x17, 0x12}, /* Output Amplifiers */ + {0xf00, 0x1a, 0x12}, /* Output Amplifiers */ + {0xf00, 0x1b, 0x12}, /* Output Amplifiers */ + {0xf00, 0x21, 0x12}, /* Output Amplifiers */ + {0xf00, 0x07, 0x0e}, /* Connect List Length */ + {0xf00, 0x08, 0x0e}, /* Connect List Length */ + {0xf00, 0x09, 0x0e}, /* Connect List Length */ + {0xf00, 0x0a, 0x0e}, /* Connect List Length */ + {0xf00, 0x27, 0x0e}, /* Connect List Length */ + {0xf00, 0x22, 0x0e}, /* Connect List Length */ + {0xf00, 0x23, 0x0e}, /* Connect List Length */ + {0xf00, 0x24, 0x0e}, /* Connect List Length */ + {0xf00, 0x25, 0x0e}, /* Connect List Length */ + {0xf00, 0x14, 0x0e}, /* Connect List Length */ + {0xf00, 0x15, 0x0e}, /* Connect List Length */ + {0xf00, 0x16, 0x0e}, /* Connect List Length */ + {0xf00, 0x17, 0x0e}, /* Connect List Length */ + {0xf00, 0x18, 0x0e}, /* Connect List Length */ + {0xf00, 0x19, 0x0e}, /* Connect List Length */ + {0xf00, 0x1a, 0x0e}, /* Connect List Length */ + {0xf00, 0x1b, 0x0e}, /* Connect List Length */ + {0xf00, 0x1d, 0x0e}, /* Connect List Length */ + {0xf00, 0x21, 0x0e}, /* Connect List Length */ + {0xf00, 0x1e, 0x0e}, /* Connect List Length */ + {0xf00, 0x1f, 0x0e}, /* Connect List Length */ + {0xf00, 0x12, 0x0e}, /* Connect List Length */ + {0xf00, 0x13, 0x0e}, /* Connect List Length */ + {0xf00, 0x29, 0x0e}, /* Connect List Length */ + {0xf00, 0x01, 0x0f}, /* Supported Power States */ + {0xf00, 0x02, 0x0f}, /* Supported Power States */ + {0xf00, 0x03, 0x0f}, /* Supported Power States */ + {0xf00, 0x04, 0x0f}, /* Supported Power States */ + {0xf00, 0x05, 0x0f}, /* Supported Power States */ + {0xf00, 0x06, 0x0f}, /* Supported Power States */ + {0xf00, 0x07, 0x0f}, /* Supported Power States */ + {0xf00, 0x08, 0x0f}, /* Supported Power States */ + {0xf00, 0x09, 0x0f}, /* Supported Power States */ + {0xf00, 0x0a, 0x0f}, /* Supported Power States */ + {0xf00, 0x27, 0x0f}, /* Supported Power States */ + {0xf00, 0x22, 0x0f}, /* Supported Power States */ + {0xf00, 0x23, 0x0f}, /* Supported Power States */ + {0xf00, 0x24, 0x0f}, /* Supported Power States */ + {0xf00, 0x21, 0x0f}, /* Supported Power States */ + {0xf00, 0x12, 0x0f}, /* Supported Power States */ + {0xf00, 0x13, 0x0f}, /* Supported Power States */ + {0xf00, 0x14, 0x0f}, /* Supported Power States */ + {0xf00, 0x15, 0x0f}, /* Supported Power States */ + {0xf00, 0x16, 0x0f}, /* Supported Power States */ + {0xf00, 0x17, 0x0f}, /* Supported Power States */ + {0xf00, 0x18, 0x0f}, /* Supported Power States */ + {0xf00, 0x19, 0x0f}, /* Supported Power States */ + {0xf00, 0x1a, 0x0f}, /* Supported Power States */ + {0xf00, 0x1b, 0x0f}, /* Supported Power States */ + {0xf00, 0x1d, 0x0f}, /* Supported Power States */ + {0xf00, 0x1e, 0x0f}, /* Supported Power States */ + {0xf00, 0x1f, 0x0f}, /* Supported Power States */ + {0xf00, 0x29, 0x0f}, /* Supported Power States */ + {0xf00, 0x0b, 0x0f}, /* Supported Power States */ + {0xf00, 0x0c, 0x0f}, /* Supported Power States */ + {0xf00, 0x0d, 0x0f}, /* Supported Power States */ + {0xf00, 0x0e, 0x0f}, /* Supported Power States */ + {0xf00, 0x0f, 0x0f}, /* Supported Power States */ + {0xf00, 0x10, 0x0f}, /* Supported Power States */ + {0xf00, 0x11, 0x0f}, /* Supported Power States */ + {0xf00, 0x1c, 0x0f}, /* Supported Power States */ + {0xf00, 0x20, 0x0f}, /* Supported Power States */ + {0xf00, 0x26, 0x0f}, /* Supported Power States */ + {0xf00, 0x28, 0x0f}, /* Supported Power States */ + {0xf00, 0x20, 0x10}, /* Processing Capabilities */ + {0xf00, 0x53, 0x10}, /* Processing Capabilities */ + {0xf00, 0x54, 0x10}, /* Processing Capabilities */ + {0xf00, 0x55, 0x10}, /* Processing Capabilities */ + {0xf00, 0x56, 0x10}, /* Processing Capabilities */ + {0xf00, 0x57, 0x10}, /* Processing Capabilities */ + {0xf00, 0x58, 0x10}, /* Processing Capabilities */ + {0xf00, 0x5b, 0x10}, /* Processing Capabilities */ + {0xf00, 0x60, 0x10}, /* Processing Capabilities */ + {0xf00, 0x01, 0x11}, /* GPIO Capabilities */ + {0xf00, 0x00, 0x16}, /* Statically Switchable BCLK Clock Frequency */ + {0xf00, 0x00, 0x17}, /* Interface Type Capability */ +#endif + {0xf01, 0x14, 0x00}, /* Connection Select Control */ + {0xf01, 0x15, 0x00}, /* Connection Select Control */ + {0xf01, 0x16, 0x00}, /* Connection Select Control */ + {0xf01, 0x1b, 0x00}, /* Connection Select Control */ + {0xf01, 0x21, 0x00}, /* Connection Select Control */ + {0xf01, 0x22, 0x00}, /* Connection Select Control */ + {0xf01, 0x23, 0x00}, /* Connection Select Control */ + {0xf01, 0x24, 0x00}, /* Connection Select Control */ + {0xf01, 0x25, 0x00}, /* Connection Select Control */ + {0xf02, 0x07, 0x00}, /* Connection List Entry */ + {0xf02, 0x08, 0x00}, /* Connection List Entry */ + {0xf02, 0x09, 0x00}, /* Connection List Entry */ + {0xf02, 0x0a, 0x00}, /* Connection List Entry */ + {0xf02, 0x14, 0x00}, /* Connection List Entry */ + {0xf02, 0x15, 0x00}, /* Connection List Entry */ + {0xf02, 0x16, 0x00}, /* Connection List Entry */ + {0xf02, 0x1b, 0x00}, /* Connection List Entry */ + {0xf02, 0x21, 0x00}, /* Connection List Entry */ + {0xf02, 0x1e, 0x00}, /* Connection List Entry */ + {0xf02, 0x21, 0x00}, /* Connection List Entry */ + {0xf02, 0x23, 0x00}, /* Connection List Entry */ + {0xf02, 0x24, 0x00}, /* Connection List Entry */ + {0xf02, 0x25, 0x00}, /* Connection List Entry */ + {0xd00, 0x20, 0x00}, /* Coefficient Index */ + {0xd00, 0x53, 0x00}, /* Coefficient Index */ + {0xd00, 0x54, 0x00}, /* Coefficient Index */ + {0xd00, 0x56, 0x00}, /* Coefficient Index */ + {0xd00, 0x57, 0x00}, /* Coefficient Index */ + {0xd00, 0x58, 0x00}, /* Coefficient Index */ + {0xc00, 0x20, 0x00}, /* Processing Coefficient */ + {0xc00, 0x53, 0x00}, /* Processing Coefficient */ + {0xc00, 0x54, 0x00}, /* Processing Coefficient */ + {0xc00, 0x56, 0x00}, /* Processing Coefficient */ + {0xc00, 0x57, 0x00}, /* Processing Coefficient */ + {0xc00, 0x58, 0x00}, /* Processing Coefficient */ + {0xb00, 0x02, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x02, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x03, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x03, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x07, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x07, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x08, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x08, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x09, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x09, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x12, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x12, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x13, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x13, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x19, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x19, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x1a, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x1a, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x14, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x14, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x15, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x15, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x16, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x16, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x17, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x17, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x21, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x21, 0xa000}, /* Amplifier Gain */ + {0xa00, 0x02, 0x0000}, /* Converter Format */ + {0xa00, 0x03, 0x0000}, /* Converter Format */ + {0xa00, 0x04, 0x0000}, /* Converter Format */ + {0xa00, 0x05, 0x0000}, /* Converter Format */ + {0xa00, 0x06, 0x0000}, /* Converter Format */ + {0xa00, 0x07, 0x0000}, /* Converter Format */ + {0xa00, 0x08, 0x0000}, /* Converter Format */ + {0xa00, 0x09, 0x0000}, /* Converter Format */ + {0xa00, 0x0a, 0x0000}, /* Converter Format */ + {0xa00, 0x27, 0x0000}, /* Converter Format */ + {0xf05, 0x01, 0x00}, /* Power State */ + {0xf05, 0x02, 0x00}, /* Power State */ + {0xf05, 0x03, 0x00}, /* Power State */ + {0xf05, 0x04, 0x00}, /* Power State */ + {0xf05, 0x05, 0x00}, /* Power State */ + {0xf05, 0x06, 0x00}, /* Power State */ + {0xf05, 0x07, 0x00}, /* Power State */ + {0xf05, 0x08, 0x00}, /* Power State */ + {0xf05, 0x09, 0x00}, /* Power State */ + {0xf05, 0x0a, 0x00}, /* Power State */ + {0xf05, 0x12, 0x00}, /* Power State */ + {0xf05, 0x13, 0x00}, /* Power State */ + {0xf05, 0x14, 0x00}, /* Power State */ + {0xf05, 0x15, 0x00}, /* Power State */ + {0xf05, 0x16, 0x00}, /* Power State */ + {0xf05, 0x17, 0x00}, /* Power State */ + {0xf05, 0x18, 0x00}, /* Power State */ + {0xf05, 0x19, 0x00}, /* Power State */ + {0xf05, 0x1a, 0x00}, /* Power State */ + {0xf05, 0x1b, 0x00}, /* Power State */ + {0xf05, 0x1d, 0x00}, /* Power State */ + {0xf05, 0x1e, 0x00}, /* Power State */ + {0xf05, 0x1f, 0x00}, /* Power State */ + {0xf05, 0x21, 0x00}, /* Power State */ + {0xf05, 0x27, 0x00}, /* Power State */ + {0xf05, 0x29, 0x00}, /* Power State */ + {0xf06, 0x02, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x03, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x04, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x05, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x06, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x07, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x08, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x09, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x0a, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x27, 0x00}, /* Converter Stream, Channel */ + {0xf07, 0x12, 0x00}, /* Pin Widget Control */ + {0xf07, 0x13, 0x00}, /* Pin Widget Control */ + {0xf07, 0x14, 0x00}, /* Pin Widget Control */ + {0xf07, 0x15, 0x00}, /* Pin Widget Control */ + {0xf07, 0x16, 0x00}, /* Pin Widget Control */ + {0xf07, 0x17, 0x00}, /* Pin Widget Control */ + {0xf07, 0x18, 0x00}, /* Pin Widget Control */ + {0xf07, 0x19, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1a, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1b, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1d, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1e, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1f, 0x00}, /* Pin Widget Control */ + {0xf07, 0x21, 0x00}, /* Pin Widget Control */ + {0xf07, 0x29, 0x00}, /* Pin Widget Control */ + {0xf0c, 0x14, 0x00}, /* EAPD Enable */ + {0xf0c, 0x15, 0x00}, /* EAPD Enable */ + {0xf0c, 0x16, 0x00}, /* EAPD Enable */ + {0xf0c, 0x17, 0x00}, /* EAPD Enable */ + {0xf0c, 0x1b, 0x00}, /* EAPD Enable */ + {0xf0c, 0x21, 0x00}, /* EAPD Enable */ + {0xf08, 0x01, 0x00}, /* Unsolicited Response */ + {0xf08, 0x15, 0x00}, /* Unsolicited Response */ + {0xf08, 0x16, 0x00}, /* Unsolicited Response */ + {0xf08, 0x17, 0x00}, /* Unsolicited Response */ + {0xf08, 0x18, 0x00}, /* Unsolicited Response */ + {0xf08, 0x19, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1a, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1b, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1e, 0x00}, /* Unsolicited Response */ + {0xf08, 0x21, 0x00}, /* Unsolicited Response */ + {0xf08, 0x55, 0x00}, /* Unsolicited Response */ + {0xf08, 0x60, 0x00}, /* Unsolicited Response */ + {0xf09, 0x60, 0x00}, /* Pin Sense */ + {0xf09, 0x15, 0x00}, /* Pin Sense */ + {0xf09, 0x16, 0x00}, /* Pin Sense */ + {0xf09, 0x17, 0x00}, /* Pin Sense */ + {0xf09, 0x18, 0x00}, /* Pin Sense */ + {0xf09, 0x19, 0x00}, /* Pin Sense */ + {0xf09, 0x1a, 0x00}, /* Pin Sense */ + {0xf09, 0x1b, 0x00}, /* Pin Sense */ + {0xf09, 0x1e, 0x00}, /* Pin Sense */ + {0xf09, 0x1f, 0x00}, /* Pin Sense */ + {0xf09, 0x29, 0x00}, /* Pin Sense */ + {0xf0a, 0x01, 0x00}, /* BEEP Generator */ + {0xf20, 0x01, 0x00}, /* Subsystem ID */ + {0xf21, 0x01, 0x00}, /* Subsystem ID */ + {0xf22, 0x01, 0x00}, /* Subsystem ID */ + {0xf23, 0x01, 0x00}, /* Subsystem ID */ + {0xf1c, 0x12, 0x00}, /* Configuration Default */ + {0xf1c, 0x13, 0x00}, /* Configuration Default */ + {0xf1c, 0x14, 0x00}, /* Configuration Default */ + {0xf1c, 0x15, 0x00}, /* Configuration Default */ + {0xf1c, 0x16, 0x00}, /* Configuration Default */ + {0xf1c, 0x17, 0x00}, /* Configuration Default */ + {0xf1c, 0x18, 0x00}, /* Configuration Default */ + {0xf1c, 0x19, 0x00}, /* Configuration Default */ + {0xf1c, 0x1a, 0x00}, /* Configuration Default */ + {0xf1c, 0x1b, 0x00}, /* Configuration Default */ + {0xf1c, 0x1d, 0x00}, /* Configuration Default */ + {0xf1c, 0x1e, 0x00}, /* Configuration Default */ + {0xf1c, 0x1f, 0x00}, /* Configuration Default */ + {0xf1c, 0x21, 0x00}, /* Configuration Default */ + {0xf1c, 0x29, 0x00}, /* Configuration Default */ + {0xf1d, 0x12, 0x00}, /* Configuration Default */ + {0xf1d, 0x13, 0x00}, /* Configuration Default */ + {0xf1d, 0x14, 0x00}, /* Configuration Default */ + {0xf1d, 0x15, 0x00}, /* Configuration Default */ + {0xf1d, 0x16, 0x00}, /* Configuration Default */ + {0xf1d, 0x17, 0x00}, /* Configuration Default */ + {0xf1d, 0x18, 0x00}, /* Configuration Default */ + {0xf1d, 0x19, 0x00}, /* Configuration Default */ + {0xf1d, 0x1a, 0x00}, /* Configuration Default */ + {0xf1d, 0x1b, 0x00}, /* Configuration Default */ + {0xf1d, 0x1d, 0x00}, /* Configuration Default */ + {0xf1d, 0x1e, 0x00}, /* Configuration Default */ + {0xf1d, 0x1f, 0x00}, /* Configuration Default */ + {0xf1d, 0x21, 0x00}, /* Configuration Default */ + {0xf1d, 0x29, 0x00}, /* Configuration Default */ + {0xf1e, 0x12, 0x00}, /* Configuration Default */ + {0xf1e, 0x13, 0x00}, /* Configuration Default */ + {0xf1e, 0x14, 0x00}, /* Configuration Default */ + {0xf1e, 0x15, 0x00}, /* Configuration Default */ + {0xf1e, 0x16, 0x00}, /* Configuration Default */ + {0xf1e, 0x17, 0x00}, /* Configuration Default */ + {0xf1e, 0x18, 0x00}, /* Configuration Default */ + {0xf1e, 0x19, 0x00}, /* Configuration Default */ + {0xf1e, 0x1a, 0x00}, /* Configuration Default */ + {0xf1e, 0x1b, 0x00}, /* Configuration Default */ + {0xf1e, 0x1d, 0x00}, /* Configuration Default */ + {0xf1e, 0x1e, 0x00}, /* Configuration Default */ + {0xf1e, 0x1f, 0x00}, /* Configuration Default */ + {0xf1e, 0x21, 0x00}, /* Configuration Default */ + {0xf1e, 0x29, 0x00}, /* Configuration Default */ + {0xf1f, 0x12, 0x00}, /* Configuration Default */ + {0xf1f, 0x13, 0x00}, /* Configuration Default */ + {0xf1f, 0x14, 0x00}, /* Configuration Default */ + {0xf1f, 0x15, 0x00}, /* Configuration Default */ + {0xf1f, 0x16, 0x00}, /* Configuration Default */ + {0xf1f, 0x17, 0x00}, /* Configuration Default */ + {0xf1f, 0x18, 0x00}, /* Configuration Default */ + {0xf1f, 0x19, 0x00}, /* Configuration Default */ + {0xf1f, 0x1a, 0x00}, /* Configuration Default */ + {0xf1f, 0x1b, 0x00}, /* Configuration Default */ + {0xf1f, 0x1d, 0x00}, /* Configuration Default */ + {0xf1f, 0x1e, 0x00}, /* Configuration Default */ + {0xf1f, 0x1f, 0x00}, /* Configuration Default */ + {0xf1f, 0x21, 0x00}, /* Configuration Default */ + {0xf1f, 0x29, 0x00}, /* Configuration Default */ + {0xf0d, 0x06, 0x00}, /* Digital Converter */ + {0xf0d, 0x0a, 0x00}, /* Digital Converter */ + {0xf15, 0x01, 0x00}, /* GPIO Data */ + {0xf16, 0x01, 0x00}, /* GPIO Enable Mask */ + {0xf16, 0x20, 0x00}, /* GPIO Enable Mask */ + {0xf17, 0x01, 0x00}, /* GPIO Direction */ + {0xf17, 0x20, 0x00}, /* GPIO Direction */ + {0xf19, 0x01, 0x00}, /* GPIO Unsolicited Response Enable Mask */ + {0xf19, 0x20, 0x00}, /* GPIO Unsolicited Response Enable Mask */ + {0xf37, 0x01, 0x00}, /* Current BCLK Frequency */ +}; +#define RT700_HDA_DUMP_LEN ARRAY_SIZE(hda_dump_list) + +static int rt700_index_write(struct regmap *regmap, + unsigned int reg, unsigned int value) +{ + int ret; + unsigned int val_h, val_l; + + val_h = (reg >> 8) & 0xff; + val_l = reg & 0xff; + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + val_h = (value >> 8) & 0xff; + val_l = value & 0xff; + ret = regmap_write(regmap, RT700_PRIV_DATA_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_DATA_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + return 0; + +err: + return ret; +} + +static int rt700_index_read(struct regmap *regmap, + unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int val_h, val_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + + val_h = (reg >> 8) & 0xff; + val_l = reg & 0xff; + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + val_h = 0; + val_l = 0; + ret = regmap_write(regmap, RT700_PRIV_DATA_R_H, val_h); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_DATA_R_L, val_l); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + regmap_read(regmap, + RT700_READ_HDA_3, &sdw_data_3); + regmap_read(regmap, + RT700_READ_HDA_2, &sdw_data_2); + regmap_read(regmap, + RT700_READ_HDA_1, &sdw_data_1); + regmap_read(regmap, + RT700_READ_HDA_0, &sdw_data_0); + *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + return 0; + +err: + return ret; +} + +static int rt700_hda_read(struct regmap *regmap, unsigned int vid, + unsigned int nid, unsigned int pid, unsigned int *value) +{ + int ret; + unsigned int val_h, val_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int sdw_addr_h, sdw_addr_l; + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + + if (vid & 0x800) { /* get command */ + hda_to_sdw(nid, vid, pid, + &sdw_addr_h, &sdw_data_1, &sdw_addr_l, &sdw_data_0); + + pr_debug("write %04x %02x\n", sdw_addr_h, sdw_data_1); + regmap_write(regmap, sdw_addr_h, sdw_data_1); + if (sdw_addr_l) { + pr_debug("write %04x %02x", sdw_addr_l, sdw_data_0); + regmap_write(regmap, sdw_addr_l, sdw_data_0); + } + regmap_read(regmap, + RT700_READ_HDA_3, &sdw_data_3); + regmap_read(regmap, + RT700_READ_HDA_2, &sdw_data_2); + regmap_read(regmap, + RT700_READ_HDA_1, &sdw_data_1); + regmap_read(regmap, + RT700_READ_HDA_0, &sdw_data_0); + pr_debug("(%03x %02x %04x) = %02x%02x%02x%02x\n", + vid, nid, pid, sdw_data_3, + sdw_data_2, sdw_data_1, sdw_data_0); + } else { + pr_err("%s: it is not a get verb\n", __func__); + } + *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + + return 0; +} + +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic) +{ + unsigned int buf; + + rt700_index_read(rt700->regmap, 0x82, &buf); + *hp = buf & 0x10; + if (*hp) { + *mic = buf & 0x40; + } else { + *mic = false; + } + + /* Clear IRQ */ + rt700_index_read(rt700->regmap, 0x10, &buf); + buf = buf | 0x1000; + rt700_index_write(rt700->regmap, 0x10, buf); + + rt700_index_read(rt700->regmap, 0x19, &buf); + buf = buf | 0x0100; + rt700_index_write(rt700->regmap, 0x19, buf); + + + return 0; +} +EXPORT_SYMBOL(rt700_jack_detect); + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_l; + unsigned int read_ll, read_rl; + + + /* Can't use update bit function, so read the original value first */ + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + + + /* Now set value */ + addr_h = mc->reg; + addr_l = mc->rreg; + + /*pr_debug("%s val = %d, %d\n", ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]);*/ + pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + /* L Channel */ + val_h = (1 << mc->shift) | (1 << 5); + + if (mc->invert) { + /* for mute */ + val_l = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_l |= read_ll; + } else { + /* for gain */ + val_l = ((ucontrol->value.integer.value[0]) & mc->max); + /* keep mute status */ + read_ll = read_ll & 0x80; + val_l |= read_ll; + } + + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_l); + pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + + /* R Channel */ + val_h = (1 << mc->shift) | (1 << 4); + + if (mc->invert) { + /* for mute */ + val_l = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_l |= read_rl; + } else { + /* for gain */ + val_l = ((ucontrol->value.integer.value[1]) & mc->max); + /* keep mute status */ + read_rl = read_rl & 0x80; + val_l |= read_rl; + } + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_l); + pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + + return 0; +} + +static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +static const struct snd_kcontrol_new rt700_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", RT700_SET_GAIN_DAC1_H, + RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT700_SET_GAIN_AMIC_H, + RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT("Speaker Playback Switch", RT700_SET_GAIN_SPK_H, + RT700_SET_GAIN_SPK_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("Headphone Playback Switch", RT700_SET_GAIN_HP_H, + RT700_SET_GAIN_HP_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), +}; + +static int rt700_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, val; + + /* nid = e->reg, vid = 0xf01 */ + reg = RT700_VERB_GET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, 0x0); + pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); + val = snd_soc_component_read32(component, RT700_READ_HDA_0); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val); + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt700_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2, change, reg; + struct snd_soc_dapm_update update; + + if (item[0] >= e->items) + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + pr_debug("%s val=%x e->reg=%x item[0]=%d\n", + __func__, val, e->reg, item[0]); + + reg = RT700_VERB_GET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, 0x0); + pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); + val2 = snd_soc_component_read32(component, RT700_READ_HDA_0); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val2); + if (val == val2) + change = 0; + else + change = 1; + + pr_debug("change=%d\n", change); + + if (change) { + reg = RT700_VERB_SET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, val); + pr_debug("%s write %04x %02x\n", __func__, reg, val); + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = 0xff; + update.val = val; + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, &update); + } + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc22_enum, RT700_MIXER_IN1, 0, adc_mux_text); + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc23_enum, RT700_MIXER_IN2, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt700_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, + rt700_mux_get, rt700_mux_put); + +static const char * const out_mux_text[] = { + "Front", + "Surround", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt700_hp_enum, RT700_HP_OUT, 0, out_mux_text); + +static const struct snd_kcontrol_new rt700_hp_mux = + SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_DAC("DAC Front", NULL, RT700_SET_STREAMID_DAC1, 4, 0), + SND_SOC_DAPM_DAC("DAC Surround", NULL, RT700_SET_STREAMID_DAC2, 4, 0), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux), + SND_SOC_DAPM_PGA("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, RT700_SET_STREAMID_ADC1, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, RT700_SET_STREAMID_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt700_audio_map[] = { + {"DAC Front", NULL, "DP1RX"}, + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"HPO Mux", "Front", "DAC Front"}, + {"HPO Mux", "Surround", "DAC Surround"}, + {"HP", NULL, "HPO Mux"}, + {"SPK PGA", NULL, "DAC Front"}, + {"SPK", NULL, "SPK PGA"}, +}; + +static int rt700_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { + snd_soc_component_write(component, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_rt700 = { + .set_bias_level = rt700_set_bias_level, + .controls = rt700_snd_controls, + .num_controls = ARRAY_SIZE(rt700_snd_controls), + .dapm_widgets = rt700_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets), + .dapm_routes = rt700_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), +}; + +static int rt700_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream_tag) +{ + struct sdw_stream_data *stream_data; + + stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL); + if (!stream_data) + return -ENOMEM; + stream_data->stream_tag = stream_tag; + snd_soc_dai_set_dma_data(dai, substream, stream_data); + return 0; +} + +static int rt700_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream_data; + + stream_data = snd_soc_dai_get_dma_data(dai, substream); + kfree(stream_data); + return 0; +} + + + +static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + int retval; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + struct sdw_stream_data *stream; + int port; + int num_channels; + int upscale_factor = 1; + unsigned int val = 0; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!rt700->sdw) + return 0; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_IN; + port = 1; + } else { + direction = SDW_DATA_DIR_OUT; + port = 2; + } + switch (dai->id) { + case RT700_AIF1: + break; + case RT700_AIF2: + port += 2; + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.frame_rate *= upscale_factor; + stream_config.channel_count = params_channels(params); + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + retval = sdw_config_stream(rt700->sdw->mstr, + rt700->sdw, &stream_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return retval; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = port; + num_channels = params_channels(params); + port_cfg.ch_mask = (1 << (num_channels)) - 1; + retval = sdw_config_port(rt700->sdw->mstr, rt700->sdw, + &port_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */ + case 44100: + snd_soc_component_write(component, RT700_DAC_FORMAT_H, 0x40); + snd_soc_component_write(component, RT700_ADC_FORMAT_H, 0x40); + break; + case 48000: + snd_soc_component_write(component, RT700_DAC_FORMAT_H, 0x0); + snd_soc_component_write(component, RT700_ADC_FORMAT_H, 0x0); + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "format val = 0x%x\n", val); + + snd_soc_component_write(component, RT700_DAC_FORMAT_L, val); + snd_soc_component_write(component, RT700_ADC_FORMAT_L, val); + + return retval; +} + +int rt700_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = snd_soc_dai_get_dma_data(dai, + substream); + if (!rt700->sdw) + return 0; + sdw_release_stream(rt700->sdw->mstr, rt700->sdw, stream->stream_tag); + return 0; +} + +#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt700_ops = { + .hw_params = rt700_pcm_hw_params, + .hw_free = rt700_pcm_hw_free, + .program_stream_tag = rt700_program_stream_tag, + .remove_stream_tag = rt700_remove_stream_tag, +}; + +static struct snd_soc_dai_driver rt700_dai[] = { + { + .name = "rt700-aif1", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +static ssize_t rt700_index_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i, cnt = 0; + + /* index */ + for (i = 0; i <= 0xa0; i++) { + rt700_index_read(rt700->regmap, i, &sdw_data_0); + cnt += snprintf(buf + cnt, 12, + "%02x = %04x\n", i, sdw_data_0); + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + return cnt; +} + +static ssize_t rt700_index_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; + unsigned int index_reg, index_val; + int i; + + pr_debug("register \"%s\" count=%zu\n", buf, count); + for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ + if (*(buf + i) <= '9' && *(buf + i) >= '0') + index_reg = (index_reg << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + index_reg = (index_reg << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + index_reg = (index_reg << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + index_val = (index_val << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + index_val = (index_val << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + index_val = (index_val << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + pr_debug("index_reg=0x%x index_val=0x%x\n", + index_reg, index_val); + + rt700_index_write(rt700->regmap, index_reg, index_val); + + return count; +} + +static DEVICE_ATTR(index_reg, 0664, rt700_index_cmd_show, rt700_index_cmd_store); + +static ssize_t rt700_hda_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i, cnt = 0; + unsigned int value; + + pr_debug("%s cnt=%d RT700_HDA_DUMP_LEN=%d PAGE_SIZE=%d\n", + __func__, cnt, RT700_HDA_DUMP_LEN, PAGE_SIZE); + for (i = 0; i < RT700_HDA_DUMP_LEN; i++) { + pr_debug("%s i=%d", __func__, i); + if (cnt + 25 >= PAGE_SIZE) + break; + rt700->dbg_nid = hda_dump_list[i].nid; + rt700->dbg_vid = hda_dump_list[i].vid; + rt700->dbg_payload = hda_dump_list[i].payload; + rt700_hda_read(rt700->regmap, rt700->dbg_vid, + rt700->dbg_nid, rt700->dbg_payload, &value); + + cnt += snprintf(buf + cnt, 25, + "%03x %02x %04x=%x\n", + rt700->dbg_vid, rt700->dbg_nid, + rt700->dbg_payload, value); + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + return cnt; +} + +static ssize_t rt700_hda_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i; + + pr_debug("register \"%s\" count=%zu\n", buf, count); + for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + if (rt700->dbg_vid < 0xf) + rt700->dbg_vid = rt700->dbg_vid << 8; + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + pr_debug("dbg_nid=0x%x dbg_vid=0x%x dbg_payload=0x%x\n", + rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload); + + hda_to_sdw(rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, + &sdw_addr_h, &sdw_data_h, &sdw_addr_l, &sdw_data_l); + + regmap_write(rt700->regmap, sdw_addr_h, sdw_data_h); + if (!sdw_addr_l) + regmap_write(rt700->regmap, sdw_addr_l, sdw_data_l); + + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + if (rt700->dbg_vid & 0x800) { /* get command */ + regmap_read(rt700->regmap, RT700_READ_HDA_3, &sdw_data_3); + regmap_read(rt700->regmap, RT700_READ_HDA_2, &sdw_data_2); + regmap_read(rt700->regmap, RT700_READ_HDA_1, &sdw_data_1); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &sdw_data_0); + pr_info("read (%02x %03x %04x) = %02x%02x%02x%02x\n", + rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, + sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0); + } + + /* Enable Jack Detection */ + regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + return count; +} + +static DEVICE_ATTR(hda_reg, 0664, rt700_hda_cmd_show, rt700_hda_cmd_store); + +/* Bus clock frequency */ +#define RT700_CLK_FREQ_9600000HZ 9600000 +#define RT700_CLK_FREQ_12000000HZ 12000000 +#define RT700_CLK_FREQ_6000000HZ 6000000 +#define RT700_CLK_FREQ_4800000HZ 4800000 +#define RT700_CLK_FREQ_2400000HZ 2400000 +#define RT700_CLK_FREQ_12288000HZ 12288000 + + +static int rt700_clock_config(struct device *dev, struct alc700 *alc700) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + int value, read_value1, read_value2; + + switch(alc700->params->bus_clk_freq) { + case RT700_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT700_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT700_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT700_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT700_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT700_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + regmap_write(rt700->regmap, 0xe0, value); + regmap_write(rt700->regmap, 0xf0, value); + + return 0; +} + +int rt700_probe(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt700_priv *rt700; + struct alc700 *alc700 = dev_get_drvdata(dev); + int ret; + unsigned int value; + + rt700 = devm_kzalloc(dev, sizeof(struct rt700_priv), + GFP_KERNEL); + if (!rt700) + return -ENOMEM; + + dev_set_drvdata(dev, rt700); + + rt700->regmap = regmap; + rt700->sdw = slave; + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); + dev_info(&slave->dev, "%s\n", __func__); + + /* Set Tx route */ + /* Filter 02: index 91[13:12] 07[3] */ + /* Filter 03: index 5f[15:14] 07[4] */ + /* DAC (02) -> Front -> SPK (14)*/ + /* DAC (03) -> Surr -> HP (14)*/ + + /* Set Rx route */ + /* Rx_09: index 91[8] */ + /* Rx_08: index 91[6] */ + /* Mic2 (19) -> Mux (22) -> ADC (09) */ + //regmap_write(rt700->regmap, 0x3122, 0x0); /* Mic2 (19) -> Mux (22)*/ + + /* Assign stream ID */ + /* do it in dapm widget + regmap_write(rt700->regmap, RT700_SET_STREAMID_DAC1, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_DAC2, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_ADC2, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_ADC1, 0x10); + */ + + /* Set Pin Widget */ + regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40); + regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40); + //regmap_write(rt700->regmap, 0x3c14, 0x02); /* 14 70c 02 */ + regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20); + /* + regmap_write(rt700->regmap, RT700_SET_PIN_LINE1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_LINE2, 0x20); + */ + + /* Set Configuration Default */ + //regmap_write(rt700->regmap, 0x4f12, 0x00); + regmap_write(rt700->regmap, 0x4f12, 0x91); + regmap_write(rt700->regmap, 0x4e12, 0xd6); + regmap_write(rt700->regmap, 0x4d12, 0x11); + regmap_write(rt700->regmap, 0x4c12, 0x20); + regmap_write(rt700->regmap, 0x4f13, 0x91); + regmap_write(rt700->regmap, 0x4e13, 0xd6); + regmap_write(rt700->regmap, 0x4d13, 0x11); + regmap_write(rt700->regmap, 0x4c13, 0x21); + + regmap_write(rt700->regmap, 0x4f19, 0x02); + regmap_write(rt700->regmap, 0x4e19, 0xa1); + regmap_write(rt700->regmap, 0x4d19, 0x90); + regmap_write(rt700->regmap, 0x4c19, 0x80); + + /* Enable Line2 */ + regmap_write(rt700->regmap, 0x371b, 0x40); + regmap_write(rt700->regmap, 0x731b, 0xb0); + regmap_write(rt700->regmap, 0x839b, 0x00); + + /* Set index */ + rt700_index_write(rt700->regmap, 0x4a, 0x201b); + //rt700_index_write(rt700->regmap, 0x38, 0x4921); + + /* get the setting registers for debug + pr_debug("%s get the setting registers\n", __func__); + rt700_hda_read(rt700->regmap, 0xf07, 0x21, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x14, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x12, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x13, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x19, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x1a, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x1b, 0, &value); + rt700_hda_read(rt700->regmap, 0xf0c, 0x14, 0, &value); + */ + ret = rt700_clock_config(dev, alc700); + + /* Enable Jack Detection */ + regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + ret = device_create_file(&slave->dev, &dev_attr_index_reg); + if (ret != 0) { + dev_err(&slave->dev, + "Failed to create index_reg sysfs files: %d\n", ret); + return ret; + } + + ret = device_create_file(&slave->dev, &dev_attr_hda_reg); + if (ret != 0) { + dev_err(&slave->dev, + "Failed to create hda_reg sysfs files: %d\n", ret); + return ret; + } + + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + pm_runtime_enable(&slave->dev); + pm_runtime_put_sync_autosuspend(&slave->dev); + return ret; +} +EXPORT_SYMBOL(rt700_probe); + +int rt700_remove(struct device *dev) +{ + + dev_info(dev, "Removing\n"); + + return 0; +} +EXPORT_SYMBOL(rt700_remove); + +#ifdef CONFIG_PM +static int rt700_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int rt700_runtime_resume(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + int ret; + int timeout = 0; + + if(rt700->sdw) { + ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, + rt700->sdw); + if (ret < 0) + return ret; + } + + return 0; +} +#endif + +const struct dev_pm_ops rt700_runtime_pm = { + SET_RUNTIME_PM_OPS(rt700_runtime_suspend, rt700_runtime_resume, + NULL) +}; +EXPORT_SYMBOL(rt700_runtime_pm); + +MODULE_DESCRIPTION("ASoC rt700 driver"); +MODULE_DESCRIPTION("ASoC rt700 driver SDW"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h new file mode 100644 index 000000000000..3ad8b84f60f7 --- /dev/null +++ b/sound/soc/codecs/rt700.h @@ -0,0 +1,161 @@ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __RT700_H__ +#define __RT700_H__ + +#include + +extern const struct dev_pm_ops rt700_runtime_pm; + +struct rt700_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct gpio_desc *reset_gpio; + u32 sclk; + u8 hpout_load; + u8 hpout_clamp; + struct sdw_slv *sdw; + int dbg_nid; + int dbg_vid; + int dbg_payload; +}; + +struct alc700 { + struct sdw_slv *sdw; + struct sdw_bus_params *params; +}; + +/* NID */ +#define RT700_AUDIO_FUNCTION_GROUP 0x01 +#define RT700_DAC_OUT1 0x02 +#define RT700_DAC_OUT2 0x03 +#define RT700_ADC_IN1 0x09 +#define RT700_ADC_IN2 0x08 +#define RT700_DMIC1 0x12 +#define RT700_DMIC2 0x13 +#define RT700_SPK_OUT 0x14 +#define RT700_MIC2 0x19 +#define RT700_LINE1 0x1a +#define RT700_LINE2 0x1b +#define RT700_BEEP 0x1d +#define RT700_SPDIF 0x1e +#define RT700_VENDOR_REGISTERS 0x20 +#define RT700_HP_OUT 0x21 +#define RT700_MIXER_IN1 0x22 +#define RT700_MIXER_IN2 0x23 +#define RT700_INLINE_CMD 0x55 + +/* Verb */ +#define RT700_VERB_SET_CONNECT_SEL 0x3100 +#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT700_VERB_GET_CONNECT_SEL 0xb100 +#define RT700_VERB_SET_POWER_STATE 0x3500 +#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT700_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT700_SET_AMP_GAIN_MUTE_L 0x8380 + +#define RT700_READ_HDA_3 0x2012 +#define RT700_READ_HDA_2 0x2013 +#define RT700_READ_HDA_1 0x2014 +#define RT700_READ_HDA_0 0x2015 +#define RT700_PRIV_INDEX_W_H 0x7520 +#define RT700_PRIV_INDEX_W_L 0x85a0 +#define RT700_PRIV_DATA_W_H 0x7420 +#define RT700_PRIV_DATA_W_L 0x84a0 +#define RT700_PRIV_INDEX_R_H 0x9d20 +#define RT700_PRIV_INDEX_R_L 0xada0 +#define RT700_PRIV_DATA_R_H 0x9c20 +#define RT700_PRIV_DATA_R_L 0xaca0 +#define RT700_DAC_FORMAT_H 0x7203 +#define RT700_DAC_FORMAT_L 0x8283 +#define RT700_ADC_FORMAT_H 0x7209 +#define RT700_ADC_FORMAT_L 0x8289 +#define RT700_SET_AUDIO_POWER_STATE\ + (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP) +#define RT700_SET_PIN_DMIC1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1) +#define RT700_SET_PIN_DMIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2) +#define RT700_SET_PIN_SPK\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT) +#define RT700_SET_PIN_HP\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT) +#define RT700_SET_PIN_MIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2) +#define RT700_SET_PIN_LINE1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1) +#define RT700_SET_PIN_LINE2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2) +#define RT700_SET_MIC2_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2) +#define RT700_SET_HP_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT) +#define RT700_SET_STREAMID_DAC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1) +#define RT700_SET_STREAMID_DAC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2) +#define RT700_SET_STREAMID_ADC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1) +#define RT700_SET_STREAMID_ADC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2) +#define RT700_SET_GAIN_DAC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1) +#define RT700_SET_GAIN_DAC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1) +#define RT700_SET_GAIN_ADC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC2_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2) +#define RT700_SET_GAIN_ADC2_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2) +#define RT700_SET_GAIN_AMIC_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2) +#define RT700_SET_GAIN_AMIC_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2) +#define RT700_SET_GAIN_HP_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT) +#define RT700_SET_GAIN_HP_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT) +#define RT700_SET_GAIN_SPK_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT) +#define RT700_SET_GAIN_SPK_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT) +#define RT700_SET_EAPD_SPK\ + (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT) + +#define RT700_EAPD_HIGH 0x2 +#define RT700_EAPD_LOW 0x0 +#define RT700_MUTE_SFT 7 +#define RT700_DIR_IN_SFT 6 +#define RT700_DIR_OUT_SFT 7 + +enum { + RT700_AIF1, + RT700_AIF2, + RT700_AIFS, +}; + +int rt700_probe(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave); +int rt700_remove(struct device *dev); +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l); +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic); +#endif /* __RT700_H__ */ From 28427704646b82a297094cc049a706294c6c3c2c Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Tue, 28 Jun 2016 15:48:56 +0530 Subject: [PATCH 0092/1276] ASoC:Intel: Add support for ALC700 machine driver Change-Id: Idcec9e08faaedbb92f714548d9fea15af4de6b91 Signed-off-by: Hardik Shah Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt700.c | 319 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 162 +++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt700.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ca140a77d992..e6b065fa1dff 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -306,6 +306,17 @@ config SND_SOC_INTEL_CNL_CS42L42_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_RT700_MACH + tristate "Cannonlake with RT700 SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT700 + select SND_SOC_DMIC + help + This adds support for ASoC RT700 codec SDW machine driver. This will + create an alsa sound card for RT700. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_CNL_SVFPGA_MACH tristate "Cannonlake with SVFPGA PDM SDW mode" depends on MFD_INTEL_LPSS && I2C && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 4f2b9b38edd0..c5e0ff065610 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o +snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -49,4 +50,5 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c new file mode 100644 index 000000000000..e0b93e571fb9 --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -0,0 +1,319 @@ +/* + * cnl_rt700.c - ASOC Machine driver for Intel cnl_rt700 platform + * with ALC700 SoundWire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_rt700_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route cnl_rt700_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Speaker", NULL, "SPK" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "DP1 Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + { "SDW Tx10", NULL, "sdw_codec1_out"}, + + { "sdw_codec0_in", NULL, "SDW Rx" }, + { "SDW Rx", NULL, "DP2 Capture" }, + {"sdw_codec2_in", NULL, "SDW Rx10"}, + {"SDW Rx10", NULL, "DP4 Capture"}, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; + +static const struct snd_kcontrol_new cnl_rt700_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + + +static int cnl_rt700_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_rt700_controls, + ARRAY_SIZE(cnl_rt700_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_rt700_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_rt700_ops = { + .startup = cnl_rt700_startup, +}; + +static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_rt700_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_rt700_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_capture = 1, + .ops = &cnl_rt700_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_rt700_ops, + }, + + { + .name = "SDW0-Codec", + .cpu_dai_name = "SDW Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .codec_dai_name = "rt700-aif1", + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "SDW1-Codec", + .cpu_dai_name = "SDW10 Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .codec_dai_name = "rt700-aif2", + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_rt700 = { + .name = "cnl_rt700-audio", + .dai_link = cnl_rt700_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt700_msic_dailink), + .dapm_widgets = cnl_rt700_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt700_widgets), + .dapm_routes = cnl_rt700_map, + .num_dapm_routes = ARRAY_SIZE(cnl_rt700_map), +}; + + +static int snd_cnl_rt700_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_rt700_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_rt700.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_rt700, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_rt700); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_rt700); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_rt700_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_rt700_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt700" }, + { .name = "icl_rt700" }, + { } +}; + +static struct platform_driver snd_cnl_rt700_mc_driver = { + .driver = { + .name = "cnl_rt700", + }, + .probe = snd_cnl_rt700_mc_probe, + .remove = snd_cnl_rt700_mc_remove, + .id_table = cnl_board_ids +}; + +static int snd_cnl_rt700_driver_init(void) +{ + return platform_driver_register(&snd_cnl_rt700_mc_driver); +} +module_init(snd_cnl_rt700_driver_init); + +static void snd_cnl_rt700_driver_exit(void) +{ + platform_driver_unregister(&snd_cnl_rt700_mc_driver); +} +module_exit(snd_cnl_rt700_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt700"); +MODULE_ALIAS("platform:icl_rt700"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index feb4ab5fc9b4..19ee283f9586 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -499,12 +499,17 @@ static int skl_find_machine(struct skl *skl, void *driver_data) struct snd_soc_acpi_mach *mach = driver_data; struct skl_machine_pdata *pdata; + if (IS_ENABLED(CONFIG_SND_SOC_RT700) || + IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)) + goto out; + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; } +out: skl->mach = mach; skl->fw_name = mach->fw_filename; pdata = mach->pdata; @@ -1053,6 +1058,163 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +static struct snd_soc_acpi_mach sst_skl_devdata[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, + {} +}; + +static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, + {} +}; + +static struct snd_soc_acpi_mach sst_kbl_devdata[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data + }, + + {} +}; + +static struct snd_soc_acpi_mach sst_glk_devdata[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, + {} +}; + +static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#else + { + .drv_name = "cnl_rt700", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#endif + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ From 4c10b7446d7b9516e05596eb07c6dcb1b501700f Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 26 Jun 2017 12:12:48 +0530 Subject: [PATCH 0093/1276] ASoC: Intel: CNL: Add new BE dai for ALC701 HS plyaback Change-Id: I64ef09b6147472ce8a78e5a689413b8f4384132c Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-pcm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 3ac58520240a..09899a05975f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1342,6 +1342,28 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW10 Pin", + .id = SDW_BE_DAI_ID_MSTR0, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW Tx10", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW Rx10", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, { /* Currently adding 1 capture pin, for PDM ideally it * should be coming from CLT based on endpoints to be supported From 1adad0d585c9d846e8207f39c20d6ea4c4eeede0 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 19 Sep 2016 13:59:36 +0530 Subject: [PATCH 0094/1276] ASoC: Intel: CNL: Enable sdw interrupt during D0 Change-Id: I3f34bbbf273da5a0a9a15df319b95178e8b41fa0 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 18be70b87728..a124dabc353a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -288,6 +288,7 @@ static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) if (core_id == SKL_DSP_CORE0_ID) { /* enable interrupt */ cnl_ipc_int_enable(ctx); + cnl_sdw_int_enable(ctx, true); cnl_ipc_op_int_enable(ctx); cnl->boot_complete = false; From 53b19e788594307dac2959f35b76f94c1a33c366 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Sun, 8 Nov 2015 19:12:28 +0530 Subject: [PATCH 0095/1276] ASoC: Intel: Skylake:fix for large get config api This patch adds support for get large config API when need to send the tx parameter to retrieve one or more configuration parameters from specified module instance. Change-Id: I3db4398e52e63176cb25ec37ff06db2b8f73f72b Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-messages.c | 2 +- sound/soc/intel/skylake/skl-sst-ipc.c | 26 ++++++++++++++++++-------- sound/soc/intel/skylake/skl-sst-ipc.h | 3 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index fa8ef710900c..13d1898da7fb 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1538,5 +1538,5 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, msg.param_data_size = size; msg.large_param_id = param_id; - return skl_ipc_get_large_config(&ctx->ipc, &msg, params); + return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, 0); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index b83a3076a1e3..7b6ce92c9d0e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1002,12 +1002,13 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, - struct skl_ipc_large_config_msg *msg, u32 *param) + struct skl_ipc_large_config_msg *msg, u32 *param, + u32 *txparam, u32 size) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); int ret = 0; - size_t sz_remaining, rx_size, data_offset; + size_t sz_remaining, rx_size, data_offset, inbox_sz; header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); @@ -1022,16 +1023,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, sz_remaining = msg->param_data_size; data_offset = 0; + inbox_sz = ipc->dsp->mailbox.in_size; + + if (msg->param_data_size >= inbox_sz) + header.extension |= IPC_FINAL_BLOCK(0); while (sz_remaining != 0) { - rx_size = sz_remaining > SKL_ADSP_W1_SZ - ? SKL_ADSP_W1_SZ : sz_remaining; + rx_size = sz_remaining > inbox_sz + ? inbox_sz : sz_remaining; if (rx_size == sz_remaining) header.extension |= IPC_FINAL_BLOCK(1); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, - ((char *)param) + data_offset, - msg->param_data_size); + dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__, + header.primary, header.extension); + dev_dbg(ipc->dev, "receiving offset: %#x, size: %#x\n", + (unsigned)data_offset, (unsigned)rx_size); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, + ((char *)txparam), size, ((char *)param) + data_offset, + rx_size); if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); @@ -1044,7 +1054,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, header.extension &= IPC_INITIAL_BLOCK_CLEAR; header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; /* fill the fields */ - header.extension |= IPC_INITIAL_BLOCK(1); + header.extension |= IPC_INITIAL_BLOCK(0); header.extension |= IPC_DATA_OFFSET_SZ(data_offset); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0437e4cf1261..e969ac964daa 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -244,7 +244,8 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, - struct skl_ipc_large_config_msg *msg, u32 *param); + struct skl_ipc_large_config_msg *msg, u32 *param, + u32 *txparam, u32 size); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait); From d53db4600af4bf5870f885ee1e3de8e948200dc0 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Wed, 25 Nov 2015 22:52:40 +0530 Subject: [PATCH 0096/1276] ASoC: Intel: Skylake: generic IPC message support Debugfs provides an interface to send generic ipc message. This feature can be used to send any IPC command by passing it as a binary blob to the interface. Change-Id: Ic712c303a8e4559e2628e7507f16828913860a26 Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-debug.c | 157 ++++++++++++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 8 ++ sound/soc/intel/skylake/skl.h | 1 + 3 files changed, 165 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index f9db7ea363ef..23a5251e383b 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -24,10 +24,14 @@ #include "../common/sst-dsp-priv.h" #include "skl-nhlt.h" -#define MOD_BUF PAGE_SIZE +#define MOD_BUF (2 * PAGE_SIZE) #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 #define MAX_SSP 4 +#define MAX_SZ 1025 +#define IPC_MOD_LARGE_CONFIG_GET 3 +#define IPC_MOD_LARGE_CONFIG_SET 4 +#define MOD_BUF1 (3 * PAGE_SIZE) struct nhlt_blob { size_t size; @@ -44,6 +48,7 @@ struct skl_debug { u8 fw_read_buff[FW_REG_BUF]; struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; + u32 ipc_data[MAX_SZ]; }; struct nhlt_specific_cfg @@ -126,6 +131,155 @@ static const struct file_operations nhlt_fops = { .llseek = default_llseek, }; +static ssize_t mod_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char *state; + char *buf1; + int ret; + unsigned int ofs = 0; + + if (d->ipc_data[0] == 0) { + state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); + } + + state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; + buf1 = kzalloc(MOD_BUF1, GFP_KERNEL); + if (!buf1) + return -ENOMEM; + + ret = snprintf(buf1, MOD_BUF1, + "%s\nLARGE PARAM DATA\n", state); + + for (ofs = 0 ; ofs < d->ipc_data[0] ; ofs += 16) { + ret += snprintf(buf1 + ret, MOD_BUF1 - ret, "0x%.4x : ", ofs); + hex_dump_to_buffer(&(d->ipc_data[1]) + ofs, 16, 16, 4, + buf1 + ret, MOD_BUF1 - ret, 0); + ret += strlen(buf1 + ret); + if (MOD_BUF1 - ret > 0) + buf1[ret++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); + kfree(buf1); + return ret; + +} + +static ssize_t mod_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + struct mod_set_get *mod_set_get; + char *buf; + int retval, type; + ssize_t written; + u32 size, mbsz; + u32 *large_data; + int large_param_size; + + struct skl_sst *ctx = d->skl->skl_sst; + struct skl_ipc_large_config_msg msg; + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + + buf = kzalloc(MOD_BUF, GFP_KERNEL); + written = simple_write_to_buffer(buf, MOD_BUF, ppos, + user_buf, count); + size = written; + print_hex_dump(KERN_DEBUG, "buf :", DUMP_PREFIX_OFFSET, 8, 4, + buf, size, false); + + mod_set_get = (struct mod_set_get *)buf; + header.primary = mod_set_get->primary; + header.extension = mod_set_get->extension; + + mbsz = mod_set_get->size - (sizeof(u32)*2); + print_hex_dump(KERN_DEBUG, "header mailbox:", DUMP_PREFIX_OFFSET, 8, 4, + mod_set_get->mailbx, size-12, false); + type = ((0x1f000000) & (mod_set_get->primary))>>24; + + switch (type) { + + case IPC_MOD_LARGE_CONFIG_GET: + msg.module_id = (header.primary) & 0x0000ffff; + msg.instance_id = ((header.primary) & 0x00ff0000)>>16; + msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; + msg.param_data_size = (header.extension) & 0x000fffff; + large_param_size = msg.param_data_size; + + large_data = kzalloc(large_param_size, GFP_KERNEL); + if (!large_data) + return -ENOMEM; + + if (mbsz) + retval = skl_ipc_get_large_config(&ctx->ipc, &msg, + large_data, &(mod_set_get->mailbx[0]), mbsz); + else + retval = skl_ipc_get_large_config(&ctx->ipc, + &msg, large_data, NULL, 0); + + d->ipc_data[0] = msg.param_data_size; + memcpy(&d->ipc_data[1], large_data, msg.param_data_size); + kfree(large_data); + break; + + case IPC_MOD_LARGE_CONFIG_SET: + d->ipc_data[0] = 0; + msg.module_id = (header.primary) & 0x0000ffff; + msg.instance_id = ((header.primary) & 0x00ff0000)>>16; + msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; + msg.param_data_size = (header.extension) & 0x000fffff; + + retval = skl_ipc_set_large_config(&ctx->ipc, &msg, + (u32 *)(&mod_set_get->mailbx)); + d->ipc_data[0] = 0; + break; + + default: + if (mbsz) + retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, + mod_set_get->mailbx, mbsz, NULL, 0); + + else + retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, + NULL, 0, NULL, 0); + + d->ipc_data[0] = 0; + break; + + } + if (retval) + d->skl->mod_set_get_status = 1; + else + d->skl->mod_set_get_status = 0; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + kfree(buf); + return written; +} + +static const struct file_operations set_get_ctrl_fops = { + .open = simple_open, + .read = mod_control_read, + .write = mod_control_write, + .llseek = default_llseek, +}; + +static int skl_init_mod_set_get(struct skl_debug *d) +{ + if (!debugfs_create_file("set_get_ctrl", 0644, d->modules, d, + &set_get_ctrl_fops)) { + dev_err(d->dev, "module set get ctrl debugfs init failed\n"); + return -EIO; + } + return 0; +} + static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, int max_pin, ssize_t size, bool direction) { @@ -437,6 +591,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) } skl_init_nhlt(d); + skl_init_mod_set_get(d); return d; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 94152704e22e..c6158d37f557 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -506,6 +506,14 @@ static inline struct skl *get_skl_ctx(struct device *dev) } struct skl_probe_config; + +struct mod_set_get { + u32 size; + u32 primary; + u32 extension; + u32 mailbx[1024]; +}; + int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4056ea3de714..4c7cb272a029 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -104,6 +104,7 @@ struct skl { struct skl_fw_config cfg; struct snd_soc_acpi_mach *mach; bool nhlt_override; + bool mod_set_get_status; }; #define skl_to_bus(s) (&(s)->hbus) From ee3c0fd2477057ecc2aef3bb8f3b7d75ff99a314 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Thu, 26 Nov 2015 01:28:34 +0530 Subject: [PATCH 0097/1276] ASoC: Intel: Skylake:Add support to get fw configuration Debugfs gives the support to get the fw configuration depending on base fw property. Change-Id: Ib9bd67928939fdfc9443d9641a71a506e1ac22cb Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-debug.c | 206 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 2 + sound/soc/intel/skylake/skl-topology.h | 26 ++++ 3 files changed, 234 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 23a5251e383b..ff6d8356cb4f 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -33,6 +33,19 @@ #define IPC_MOD_LARGE_CONFIG_SET 4 #define MOD_BUF1 (3 * PAGE_SIZE) +#define DEFAULT_SZ 100 +#define DEFAULT_ID 0XFF +#define ADSP_PROPERTIES_SZ 0x64 +#define ADSP_RESOURCE_STATE_SZ 0x18 +#define FIRMWARE_CONFIG_SZ 0x14c +#define HARDWARE_CONFIG_SZ 0x84 +#define MODULES_INFO_SZ 0xa70 +#define PIPELINE_LIST_INFO_SZ 0xc +#define SCHEDULERS_INFO_SZ 0x34 +#define GATEWAYS_INFO_SZ 0x4e4 +#define MEMORY_STATE_INFO_SZ 0x1000 +#define POWER_STATE_INFO_SZ 0x1000 + struct nhlt_blob { size_t size; struct nhlt_specific_cfg *cfg; @@ -49,6 +62,7 @@ struct skl_debug { struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; u32 ipc_data[MAX_SZ]; + struct fw_ipc_data fw_ipc_data; }; struct nhlt_specific_cfg @@ -551,6 +565,196 @@ static int skl_init_nhlt(struct skl_debug *d) return 0; } +static ssize_t adsp_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct skl_debug *d = file->private_data; + char *buf1; + ssize_t ret; + unsigned int data, ofs = 0; + int replysz = 0; + + mutex_lock(&d->fw_ipc_data.mutex); + replysz = d->fw_ipc_data.replysz; + data = d->fw_ipc_data.adsp_id; + + buf1 = kzalloc(MOD_BUF1, GFP_ATOMIC); + if (!buf1) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -ENOMEM; + } + + ret = snprintf(buf1, MOD_BUF1, + "\nADSP_PROP ID %x\n", data); + for (ofs = 0 ; ofs < replysz ; ofs += 16) { + ret += snprintf(buf1 + ret, MOD_BUF1 - ret, + "0x%.4x : ", ofs); + hex_dump_to_buffer((u8 *)(&(d->fw_ipc_data.mailbx[0])) + ofs, + 16, 16, 4, + buf1 + ret, MOD_BUF1 - ret, 0); + ret += strlen(buf1 + ret); + if (MOD_BUF1 - ret > 0) + buf1[ret++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(buf1); + + return ret; +} + +static ssize_t adsp_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[8]; + int err, replysz; + unsigned int dsp_property; + u32 *ipc_data; + struct skl_sst *ctx = d->skl->skl_sst; + struct skl_ipc_large_config_msg msg; + char id[8]; + u32 tx_data; + int j = 0, bufsize, tx_param = 0, tx_param_id; + int len = min(count, (sizeof(buf)-1)); + + mutex_lock(&d->fw_ipc_data.mutex); + if (copy_from_user(buf, user_buf, len)) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -EFAULT; + } + + buf[len] = '\0'; + bufsize = strlen(buf); + + while (buf[j] != '\0') { + if (buf[j] == ',') { + strncpy(id, &buf[j+1], (bufsize-j)); + buf[j] = '\0'; + tx_param = 1; + } else + j++; + } + + err = kstrtouint(buf, 10, &dsp_property); + + if ((dsp_property == DMA_CONTROL) || (dsp_property == ENABLE_LOGS)) { + dev_err(d->dev, "invalid input !! not readable\n"); + mutex_unlock(&d->fw_ipc_data.mutex); + return -EINVAL; + } + + if (tx_param == 1) { + err = kstrtouint(id, 10, &tx_param_id); + tx_data = (tx_param_id << 8) | dsp_property; + } + + ipc_data = kzalloc(DSP_BUF, GFP_ATOMIC); + if (!ipc_data) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -ENOMEM; + } + + switch (dsp_property) { + + case ADSP_PROPERTIES: + replysz = ADSP_PROPERTIES_SZ; + break; + + case ADSP_RESOURCE_STATE: + replysz = ADSP_RESOURCE_STATE_SZ; + break; + + case FIRMWARE_CONFIG: + replysz = FIRMWARE_CONFIG_SZ; + break; + + case HARDWARE_CONFIG: + replysz = HARDWARE_CONFIG_SZ; + break; + + case MODULES_INFO: + replysz = MODULES_INFO_SZ; + break; + + case PIPELINE_LIST_INFO: + replysz = PIPELINE_LIST_INFO_SZ; + break; + + case SCHEDULERS_INFO: + replysz = SCHEDULERS_INFO_SZ; + break; + + case GATEWAYS_INFO: + replysz = GATEWAYS_INFO_SZ; + break; + + case MEMORY_STATE_INFO: + replysz = MEMORY_STATE_INFO_SZ; + break; + + case POWER_STATE_INFO: + replysz = POWER_STATE_INFO_SZ; + break; + + default: + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(ipc_data); + return -EINVAL; + } + + msg.module_id = 0x0; + msg.instance_id = 0x0; + msg.large_param_id = dsp_property; + msg.param_data_size = replysz; + + if (tx_param == 1) + skl_ipc_get_large_config(&ctx->ipc, &msg, + ipc_data, &tx_data, sizeof(u32)); + else + skl_ipc_get_large_config(&ctx->ipc, &msg, + ipc_data, NULL, 0); + + memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); + + memcpy(&d->fw_ipc_data.mailbx[0], ipc_data, replysz); + + d->fw_ipc_data.adsp_id = dsp_property; + + d->fw_ipc_data.replysz = replysz; + + /* Userspace has been fiddling around behindthe kernel's back*/ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(ipc_data); + + return len; +} + +static const struct file_operations ssp_cntrl_adsp_fops = { + .open = simple_open, + .read = adsp_control_read, + .write = adsp_control_write, + .llseek = default_llseek, +}; + +static int skl_init_adsp(struct skl_debug *d) +{ + if (!debugfs_create_file("adsp_prop_ctrl", 0644, d->fs, d, + &ssp_cntrl_adsp_fops)) { + dev_err(d->dev, "adsp control debugfs init failed\n"); + return -EIO; + } + + memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); + d->fw_ipc_data.replysz = DEFAULT_SZ; + d->fw_ipc_data.adsp_id = DEFAULT_ID; + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -559,6 +763,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) if (!d) return NULL; + mutex_init(&d->fw_ipc_data.mutex); /* create the debugfs dir with platform component's debugfs as parent */ d->fs = debugfs_create_dir("dsp", skl->component->debugfs_root); @@ -591,6 +796,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) } skl_init_nhlt(d); + skl_init_adsp(d); skl_init_mod_set_get(d); return d; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 33de8c939bc4..767c6edddc3a 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -127,6 +127,8 @@ struct skl_lib_info; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) +#define DSP_BUF PAGE_SIZE + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index c6158d37f557..b8a1d8b90fe1 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -32,6 +32,7 @@ #define MAX_TS_GROUPS 8 #define MAX_DMIC_TS_GROUPS 4 #define MAX_FIXED_DMIC_PARAMS_SIZE 727 +#define MAX_ADSP_SZ 1024 /* Maximum number of coefficients up down mixer module */ #define UP_DOWN_MIXER_MAX_COEFF 8 @@ -514,6 +515,31 @@ struct mod_set_get { u32 mailbx[1024]; }; +enum base_fw_run_time_param { + ADSP_PROPERTIES = 0, + ADSP_RESOURCE_STATE = 1, + NOTIFICATION_MASK = 3, + ASTATE_TABLE = 4, + DMA_CONTROL = 5, + ENABLE_LOGS = 6, + FIRMWARE_CONFIG = 7, + HARDWARE_CONFIG = 8, + MODULES_INFO = 9, + PIPELINE_LIST_INFO = 10, + PIPELINE_PROPS = 11, + SCHEDULERS_INFO = 12, + GATEWAYS_INFO = 13, + MEMORY_STATE_INFO = 14, + POWER_STATE_INFO = 15 +}; + +struct fw_ipc_data { + u32 replysz; + u32 adsp_id; + u32 mailbx[MAX_ADSP_SZ]; + struct mutex mutex; +}; + int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, From 3e35ed7ff0c62b20855d78c2f2afd89fdc03f70d Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 24 Oct 2016 16:23:20 +0530 Subject: [PATCH 0098/1276] [WORKAROUND] Register masters only if RT700 is selected in config This info should come from BIOS settings. Right now it is not available, so using WA to avoid registering sdw masters if used in I2S mode. Change-Id: Id5cad0a8e53507481b65a56e942b4eedc8f0a108 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a124dabc353a..a81e1b032a96 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,6 +509,7 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_RT700) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, void __iomem *mmio_base, int irq) { @@ -655,6 +656,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, cnl_sdw_int_enable(dsp->dsp, 1); return 0; } +#endif static void skl_unregister_sdw_masters(struct skl_sst *ctx) { @@ -723,11 +725,13 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_RT700) ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); if (ret) { dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); return ret; } +#endif return 0; } From 194415d4632dc4c520945259723acefef00db840 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 25 Oct 2016 13:03:36 +0530 Subject: [PATCH 0099/1276] [WORKAROUND] Add #if for platform/codec name in cnl_rt700 machine This is done to ensure same machine driver works for both FPGA as well as RVP. Change-Id: I969ea974cdc02a802a576e23746cfdc4f4d9a7d5 --- sound/soc/intel/boards/cnl_rt700.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index e0b93e571fb9..d49c3f9de10e 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -155,6 +155,14 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "sdw-slave0-10:02:5d:07:01:00"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; +#endif + static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "Bxtn Audio Port", @@ -162,7 +170,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "System Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .init = cnl_rt700_init, .ignore_suspend = 1, .nonatomic = 1, @@ -177,7 +185,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "Reference Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, @@ -190,7 +198,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "Deepbuffer Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .dpcm_playback = 1, .ignore_suspend = 1, .nonatomic = 1, @@ -201,8 +209,8 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", - .platform_name = "0000:02:18.0", - .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .platform_name = pname, + .codec_name = cname, .codec_dai_name = "rt700-aif1", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, @@ -213,8 +221,8 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "SDW1-Codec", .cpu_dai_name = "SDW10 Pin", - .platform_name = "0000:02:18.0", - .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .platform_name = pname, + .codec_name = cname, .codec_dai_name = "rt700-aif2", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, @@ -227,7 +235,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = "0000:02:18.0", + .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, From 4ee03a728ca68982fb31e0514bb05930596ce490 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 25 Oct 2016 13:04:39 +0530 Subject: [PATCH 0100/1276] [REVERTME] ASoC: Intel: CNL: Change BE id to SDW MSTR1 Change-Id: I88e650630da9f65b222838190d14899ac28627dc Signed-off-by: Shreyas NC --- sound/soc/intel/skylake/skl-pcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 09899a05975f..ac1eef7d2b41 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1325,7 +1325,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW Pin", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx", @@ -1347,7 +1351,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW10 Pin", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx10", From 88d02294f35a41579ad29990e527ef8dcdd8f9e8 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:30:20 +0530 Subject: [PATCH 0101/1276] ASoC: Intel: CNL: Update dsp ops API to take direction as input parameter This patch removes hardcoding of audio stream direction inside dsp ops. Instead it is passed as input parameter of dsp ops. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/bxt-sst.c | 28 +++++++++++++++++--------- sound/soc/intel/skylake/cnl-sst.c | 15 +++++++++----- sound/soc/intel/skylake/skl-messages.c | 20 +++++++++--------- sound/soc/intel/skylake/skl-sst-dsp.h | 9 ++++----- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 924c0dfa6ad7..2b169b40e6c4 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -69,7 +69,8 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) goto load_library_failed; stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, - stripped_fw.size, &dmab); + stripped_fw.size, &dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "Lib prepare DMA err: %x\n", stream_tag); @@ -80,14 +81,17 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) dma_id = stream_tag - 1; memcpy(dmab.area, stripped_fw.data, stripped_fw.size); - ctx->dsp_ops.trigger(ctx->dev, true, stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i, true); if (ret < 0) dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", linfo[i].name, ret); - ctx->dsp_ops.trigger(ctx->dev, false, stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); } return ret; @@ -107,7 +111,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, { int stream_tag, ret; - stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", stream_tag); @@ -169,7 +174,9 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, return ret; base_fw_load_failed: - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); return ret; @@ -179,12 +186,15 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) { int ret; - ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); - ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); return ret; } diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a81e1b032a96..784d92df7ac3 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -127,7 +127,8 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) if (ret < 0) return ret; - stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag); return stream_tag; @@ -178,7 +179,8 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) return 0; base_fw_load_failed: - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); cnl_fpga_free_imr(ctx); @@ -189,13 +191,16 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) { int ret; - ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK, CNL_FW_INIT, CNL_BASEFW_TIMEOUT, "firmware boot"); - ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); return ret; } diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 13d1898da7fb..6fcd68c7bd1e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -127,7 +127,9 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, } static int skl_dsp_prepare(struct device *dev, unsigned int format, - unsigned int size, struct snd_dma_buffer *dmab) + unsigned int size, + struct snd_dma_buffer *dmab, + int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_ext_stream *estream; @@ -139,7 +141,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, return -ENODEV; memset(&substream, 0, sizeof(substream)); - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + + substream.stream = direction; estream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); @@ -158,7 +161,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, return stream->stream_tag; } -static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) +static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag, + int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; @@ -166,8 +170,7 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) if (!bus) return -ENODEV; - stream = snd_hdac_get_stream(bus, - SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + stream = snd_hdac_get_stream(bus, direction, stream_tag); if (!stream) return -EINVAL; @@ -176,8 +179,8 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) return 0; } -static int skl_dsp_cleanup(struct device *dev, - struct snd_dma_buffer *dmab, int stream_tag) +static int skl_dsp_cleanup(struct device *dev, struct snd_dma_buffer *dmab, + int stream_tag, int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; @@ -186,8 +189,7 @@ static int skl_dsp_cleanup(struct device *dev, if (!bus) return -ENODEV; - stream = snd_hdac_get_stream(bus, - SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + stream = snd_hdac_get_stream(bus, direction, stream_tag); if (!stream) return -EINVAL; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 767c6edddc3a..2a3ad2d43448 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -157,7 +157,6 @@ struct skl_dsp_fw_ops { unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); - }; struct skl_dsp_loader_ops { @@ -169,11 +168,11 @@ struct skl_dsp_loader_ops { struct snd_dma_buffer *dmab); int (*prepare)(struct device *dev, unsigned int format, unsigned int byte_size, - struct snd_dma_buffer *bufp); - int (*trigger)(struct device *dev, bool start, int stream_tag); - + struct snd_dma_buffer *bufp, int direction); + int (*trigger)(struct device *dev, bool start, int stream_tag, + int direction); int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab, - int stream_tag); + int stream_tag, int direction); }; #define MAX_INSTANCE_BUFF 2 From 289e88adeed0e046178641308ebc514fb1a56551 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:48:07 +0530 Subject: [PATCH 0102/1276] ASoC: Intel: CNL: Platform driver implementation for SoundWire BRA feature This patch adds APIs requied for Soundwire BRA feature. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/skl-messages.c | 742 ++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 14 + 2 files changed, 754 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 6fcd68c7bd1e..28d990450463 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "skl-sst-dsp.h" #include "cnl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -29,6 +30,9 @@ #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "skl-topology.h" +#include +#include +#include static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) @@ -272,6 +276,739 @@ static const struct skl_dsp_ops dsp_ops[] = { }, }; +static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + if (enable) { + + /* Run CP Pipeline */ + ret = skl_run_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX run pipeline failed: 0x%x\n", ret); + goto error; + } + + /* Run PB Pipeline */ + ret = skl_run_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX run pipeline failed: 0x%x\n", ret); + goto error; + } + + } else { + + /* Stop playback pipeline */ + ret = skl_stop_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX stop pipeline failed: 0x%x\n", ret); + goto error; + } + + /* Stop capture pipeline */ + ret = skl_stop_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX stop pipeline failed: 0x%x\n", ret); + goto error; + } + } + +error: + return ret; +} + +static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + struct skl_pipe *host_cpr_pipe = NULL; + struct skl_pipe_params host_cpr_params; + struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; + int ret; + + /* Playback pipeline */ + host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); + if (!host_cpr_pipe) { + ret = -ENOMEM; + goto error; + } + + bra_data->pb_pipe = host_cpr_pipe; + + host_cpr_pipe->p_params = &host_cpr_params; + host_cpr_cfg.pipe = host_cpr_pipe; + + host_cpr_pipe->ppl_id = 1; + host_cpr_pipe->pipe_priority = 0; + host_cpr_pipe->conn_type = 0; + host_cpr_pipe->memory_pages = 2; + + ret = skl_create_pipeline(ctx, host_cpr_cfg.pipe); + if (ret < 0) + goto error; + + host_cpr_params.host_dma_id = (bra_data->pb_stream_tag - 1); + host_cpr_params.link_dma_id = 0; + host_cpr_params.ch = 1; + host_cpr_params.s_freq = 96000; + host_cpr_params.s_fmt = 32; + host_cpr_params.linktype = 0; + host_cpr_params.stream = 0; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + host_cpr_cfg.id.module_id = 3; +#else + host_cpr_cfg.id.module_id = 4; +#endif + host_cpr_cfg.id.instance_id = 1; + host_cpr_cfg.mcps = 100000; + host_cpr_cfg.mem_pages = 0; + host_cpr_cfg.ibs = 384; + host_cpr_cfg.obs = 384; + host_cpr_cfg.core_id = 0; + host_cpr_cfg.max_in_queue = 1; + host_cpr_cfg.max_out_queue = 1; + host_cpr_cfg.is_loadable = 0; + host_cpr_cfg.domain = 0; + host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg.dma_buffer_size = 2; + host_cpr_cfg.pdi_type = 0; + host_cpr_cfg.converter = 0; + host_cpr_cfg.vbus_id = 0; + host_cpr_cfg.sdw_agg_enable = 0; + host_cpr_cfg.formats_config.caps_size = 0; + + host_cpr_cfg.in_fmt[0].channels = 1; + host_cpr_cfg.in_fmt[0].s_freq = 96000; + host_cpr_cfg.in_fmt[0].bit_depth = 32; + host_cpr_cfg.in_fmt[0].valid_bit_depth = 24; + host_cpr_cfg.in_fmt[0].ch_cfg = 0; + host_cpr_cfg.in_fmt[0].interleaving_style = 0; + host_cpr_cfg.in_fmt[0].sample_type = 0; + host_cpr_cfg.in_fmt[0].ch_map = 0xFFFFFFF1; + + host_cpr_cfg.out_fmt[0].channels = 1; + host_cpr_cfg.out_fmt[0].s_freq = 96000; + host_cpr_cfg.out_fmt[0].bit_depth = 32; + host_cpr_cfg.out_fmt[0].valid_bit_depth = 24; + host_cpr_cfg.out_fmt[0].ch_cfg = 0; + host_cpr_cfg.out_fmt[0].interleaving_style = 0; + host_cpr_cfg.out_fmt[0].sample_type = 0; + host_cpr_cfg.out_fmt[0].ch_map = 0xFFFFFFF1; + + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, + sizeof(host_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, + sizeof(host_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_in_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_in_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_in_pin[0].in_use = false; + host_cpr_cfg.m_in_pin[0].is_dynamic = true; + host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg.m_out_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_out_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_out_pin[0].in_use = false; + host_cpr_cfg.m_out_pin[0].is_dynamic = true; + host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(&link_cpr_cfg, &host_cpr_cfg, + sizeof(struct skl_module_cfg)); + + link_cpr_cfg.id.instance_id = 2; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.sdw_stream_num = 0x3; +#else + link_cpr_cfg.sdw_stream_num = 0x13; +#endif + link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, + sizeof(link_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, + sizeof(link_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_in_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_in_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_in_pin[0].in_use = false; + link_cpr_cfg.m_in_pin[0].is_dynamic = true; + link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.m_out_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_out_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_out_pin[0].in_use = false; + link_cpr_cfg.m_out_pin[0].is_dynamic = true; + link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + GFP_KERNEL); + if (!link_cpr_cfg.formats_config.caps) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.formats_config.caps[0] = 0x0; + link_cpr_cfg.formats_config.caps[1] = 0x1; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.formats_config.caps[2] = 0x1003; +#else + link_cpr_cfg.formats_config.caps[2] = 0x1013; +#endif + link_cpr_cfg.formats_config.caps[3] = 0x0; + + /* Init PB CPR1 module */ + ret = skl_init_module(ctx, &host_cpr_cfg); + if (ret < 0) + goto error; + + /* Init PB CPR2 module */ + ret = skl_init_module(ctx, &link_cpr_cfg); + if (ret < 0) + goto error; + + /* Bind PB CPR1 and CPR2 module */ + ret = skl_bind_modules(ctx, &host_cpr_cfg, &link_cpr_cfg); + if (ret < 0) + goto error; + +error: + /* Free up all memory allocated */ + kfree(host_cpr_cfg.m_in_pin); + kfree(host_cpr_cfg.m_out_pin); + kfree(link_cpr_cfg.m_in_pin); + kfree(link_cpr_cfg.m_out_pin); + kfree(link_cpr_cfg.formats_config.caps); + + return ret; +} + +static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + struct skl_pipe *link_cpr_pipe = NULL; + struct skl_pipe_params link_cpr_params; + struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; + int ret; + + /* Capture Pipeline */ + link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); + if (!link_cpr_pipe) { + ret = -ENOMEM; + goto error; + } + + bra_data->cp_pipe = link_cpr_pipe; + link_cpr_pipe->p_params = &link_cpr_params; + link_cpr_cfg.pipe = link_cpr_pipe; + + link_cpr_pipe->ppl_id = 2; + link_cpr_pipe->pipe_priority = 0; + link_cpr_pipe->conn_type = 0; + link_cpr_pipe->memory_pages = 2; + + /* Create Capture Pipeline */ + ret = skl_create_pipeline(ctx, link_cpr_cfg.pipe); + if (ret < 0) + goto error; + + link_cpr_params.host_dma_id = 0; + link_cpr_params.link_dma_id = 0; + link_cpr_params.ch = 6; + link_cpr_params.s_freq = 48000; + link_cpr_params.s_fmt = 32; + link_cpr_params.linktype = 0; + link_cpr_params.stream = 0; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.id.module_id = 3; +#else + link_cpr_cfg.id.module_id = 4; +#endif + link_cpr_cfg.id.instance_id = 3; + link_cpr_cfg.mcps = 100000; + link_cpr_cfg.mem_pages = 0; + link_cpr_cfg.ibs = 1152; + link_cpr_cfg.obs = 1152; + link_cpr_cfg.core_id = 0; + link_cpr_cfg.max_in_queue = 1; + link_cpr_cfg.max_out_queue = 1; + link_cpr_cfg.is_loadable = 0; + link_cpr_cfg.domain = 0; + link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.sdw_stream_num = 0x4; +#else + link_cpr_cfg.sdw_stream_num = 0x14; +#endif + link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + + link_cpr_cfg.formats_config.caps_size = 0; + link_cpr_cfg.dma_buffer_size = 2; + link_cpr_cfg.pdi_type = 0; + link_cpr_cfg.converter = 0; + link_cpr_cfg.vbus_id = 0; + link_cpr_cfg.sdw_agg_enable = 0; + link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + GFP_KERNEL); + if (!link_cpr_cfg.formats_config.caps) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.formats_config.caps[0] = 0x0; + link_cpr_cfg.formats_config.caps[1] = 0x1; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.formats_config.caps[2] = 0x1104; +#else + link_cpr_cfg.formats_config.caps[2] = 0x1114; +#endif + link_cpr_cfg.formats_config.caps[3] = 0x1; + + link_cpr_cfg.in_fmt[0].channels = 6; + link_cpr_cfg.in_fmt[0].s_freq = 48000; + link_cpr_cfg.in_fmt[0].bit_depth = 32; + link_cpr_cfg.in_fmt[0].valid_bit_depth = 24; + link_cpr_cfg.in_fmt[0].ch_cfg = 8; + link_cpr_cfg.in_fmt[0].interleaving_style = 0; + link_cpr_cfg.in_fmt[0].sample_type = 0; + link_cpr_cfg.in_fmt[0].ch_map = 0xFF657120; + + link_cpr_cfg.out_fmt[0].channels = 6; + link_cpr_cfg.out_fmt[0].s_freq = 48000; + link_cpr_cfg.out_fmt[0].bit_depth = 32; + link_cpr_cfg.out_fmt[0].valid_bit_depth = 24; + link_cpr_cfg.out_fmt[0].ch_cfg = 8; + link_cpr_cfg.out_fmt[0].interleaving_style = 0; + link_cpr_cfg.out_fmt[0].sample_type = 0; + link_cpr_cfg.out_fmt[0].ch_map = 0xFF657120; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, + sizeof(link_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, + sizeof(link_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_in_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_in_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_in_pin[0].in_use = false; + link_cpr_cfg.m_in_pin[0].is_dynamic = true; + link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.m_out_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_out_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_out_pin[0].in_use = false; + link_cpr_cfg.m_out_pin[0].is_dynamic = true; + link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(&host_cpr_cfg, &link_cpr_cfg, + sizeof(struct skl_module_cfg)); + + host_cpr_cfg.id.instance_id = 4; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); + host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, + sizeof(host_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, + sizeof(host_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_in_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_in_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_in_pin[0].in_use = false; + host_cpr_cfg.m_in_pin[0].is_dynamic = true; + host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg.m_out_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_out_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_out_pin[0].in_use = false; + host_cpr_cfg.m_out_pin[0].is_dynamic = true; + host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + /* Init CP CPR1 module */ + ret = skl_init_module(ctx, &link_cpr_cfg); + if (ret < 0) + goto error; + + /* Init CP CPR2 module */ + ret = skl_init_module(ctx, &host_cpr_cfg); + if (ret < 0) + goto error; + + /* Bind CP CPR1 and CPR2 module */ + ret = skl_bind_modules(ctx, &link_cpr_cfg, &host_cpr_cfg); + if (ret < 0) + goto error; + + +error: + /* Free up all memory allocated */ + kfree(link_cpr_cfg.formats_config.caps); + kfree(link_cpr_cfg.m_in_pin); + kfree(link_cpr_cfg.m_out_pin); + kfree(host_cpr_cfg.m_in_pin); + kfree(host_cpr_cfg.m_out_pin); + + return ret; +} + +static int cnl_sdw_bra_pipe_setup(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + /* + * This function creates TX and TX pipelines for BRA transfers. + * TODO: Currently the pipelines are created manually. All the + * values needs to be received from XML based on the configuration + * used. + */ + + if (enable) { + + /* Create playback pipeline */ + ret = cnl_sdw_bra_pipe_cfg_pb(ctx, mstr_num); + if (ret < 0) + goto error; + + /* Create capture pipeline */ + ret = cnl_sdw_bra_pipe_cfg_cp(ctx, mstr_num); + if (ret < 0) + goto error; + } else { + + /* Delete playback pipeline */ + ret = skl_delete_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) + goto error; + + /* Delete capture pipeline */ + ret = skl_delete_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) + goto error; + } + + if (enable) + return 0; +error: + /* Free up all memory allocated */ + kfree(bra_data->pb_pipe); + kfree(bra_data->cp_pipe); + + return ret; +} + +static int cnl_sdw_bra_dma_trigger(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct sst_dsp *dsp_ctx = ctx->dsp; + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + if (enable) { + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, true, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX DMA trigger failed: 0x%x\n", ret); + goto bra_dma_failed; + } + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, true, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX DMA trigger failed: 0x%x\n", ret); + goto bra_dma_failed; + } + + } else { + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, false, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX DMA trigger stop failed: 0x%x\n", ret); + goto bra_dma_failed; + } + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, false, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX DMA trigger stop failed: 0x%x\n", ret); + goto bra_dma_failed; + } + } + + if (enable) + return 0; + +bra_dma_failed: + + /* Free up resources */ + dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->pb_dmab, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->cp_dmab, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + + return ret; +} + + +static int cnl_sdw_bra_dma_setup(struct skl_sst *ctx, bool enable, + struct bra_info *info) +{ + struct sst_dsp *dsp_ctx = ctx->dsp; + struct bra_conf *bra_data = &ctx->bra_pipe_data[info->mstr_num]; + struct snd_dma_buffer *pb_dmab = &bra_data->pb_dmab; + struct snd_dma_buffer *cp_dmab = &bra_data->cp_dmab; + u32 pb_pages = 0, cp_pages = 0; + int pb_block_size = info->tx_block_size; + int cp_block_size = info->rx_block_size; + int ret = 0; + + /* + * TODO: In future below approach can be replaced by component + * framework + */ + if (enable) { + + /* + * Take below number for BRA DMA format + * Format = (32 * 2 = 64) = 0x40 Size = 0x80 + */ + + /* Prepare TX Host DMA */ + bra_data->pb_stream_tag = dsp_ctx->dsp_ops.prepare(dsp_ctx->dev, + 0x40, pb_block_size, + pb_dmab, + SNDRV_PCM_STREAM_PLAYBACK); + if (bra_data->pb_stream_tag <= 0) { + dev_err(dsp_ctx->dev, "BRA: PB DMA prepare failed: 0x%x\n", + bra_data->pb_stream_tag); + ret = -EINVAL; + goto bra_dma_failed; + } + + pb_pages = (pb_block_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + set_memory_uc((unsigned long) pb_dmab->area, pb_pages); + memcpy(pb_dmab->area, info->tx_ptr, pb_block_size); + + /* Prepare RX Host DMA */ + bra_data->cp_stream_tag = dsp_ctx->dsp_ops.prepare(dsp_ctx->dev, + 0x40, cp_block_size, + cp_dmab, + SNDRV_PCM_STREAM_CAPTURE); + if (bra_data->cp_stream_tag <= 0) { + dev_err(dsp_ctx->dev, "BRA: CP DMA prepare failed: 0x%x\n", + bra_data->cp_stream_tag); + ret = -EINVAL; + goto bra_dma_failed; + } + + cp_pages = (cp_block_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + set_memory_uc((unsigned long) cp_dmab->area, cp_pages); + + } else { + + ret = dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->pb_dmab, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + goto bra_dma_failed; + + ret = dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->cp_dmab, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) + goto bra_dma_failed; + + } + +bra_dma_failed: + + return ret; +} + +static int cnl_sdw_bra_setup(void *context, bool enable, + struct bra_info *info) +{ + struct skl_sst *ctx = context; + int ret; + + if (enable) { + + /* Setup Host DMA */ + ret = cnl_sdw_bra_dma_setup(ctx, true, info); + if (ret < 0) + goto error; + + /* Create Pipeline */ + ret = cnl_sdw_bra_pipe_setup(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + } else { + + /* De-setup Host DMA */ + ret = cnl_sdw_bra_dma_setup(ctx, false, info); + if (ret < 0) + goto error; + + /* Delete Pipeline */ + ret = cnl_sdw_bra_pipe_setup(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + + } + +error: + return ret; +} + + +static int cnl_sdw_bra_xfer(void *context, bool enable, + struct bra_info *info) +{ + + struct skl_sst *ctx = context; + struct bra_conf *bra_data = &ctx->bra_pipe_data[info->mstr_num]; + struct snd_dma_buffer *cp_dmab = &bra_data->cp_dmab; + int ret; + + if (enable) { + + /* + * TODO: Need to check on how to check on RX buffer + * completion. Approaches can be used: + * 1. Check any of LPIB, SPIB or DPIB register for + * xfer completion. + * 2. Add Interrupt of completion (IOC) for RX DMA buffer. + * This needs to adds changes in common infrastructure code + * only for BRA feature. + * Currenly we are just sleeping for 100 ms and copying + * data to appropriate RX buffer. + */ + + /* Trigger Host DMA */ + ret = cnl_sdw_bra_dma_trigger(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + /* Trigger Pipeline */ + ret = cnl_sdw_bra_pipe_trigger(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + + /* Sleep for 100 ms */ + msleep(100); + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA CP DMA BUFFER DUMP RCVD:", DUMP_PREFIX_OFFSET, 8, 4, + cp_dmab->area, cp_dmab->bytes, false); + + /* Copy data in RX buffer */ + memcpy(info->rx_ptr, cp_dmab->area, info->rx_block_size); + + } else { + + /* Stop Host DMA */ + ret = cnl_sdw_bra_dma_trigger(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + + /* Stop Pipeline */ + ret = cnl_sdw_bra_pipe_trigger(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + } + +error: + return ret; +} + + +struct cnl_bra_operation cnl_sdw_bra_ops = { + .bra_platform_setup = cnl_sdw_bra_setup, + .bra_platform_xfer = cnl_sdw_bra_xfer, +}; + + const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) { int i; @@ -498,10 +1235,11 @@ static void skl_set_base_module_format(struct skl_sst *ctx, base_cfg->audio_fmt.bit_depth = format->bit_depth; base_cfg->audio_fmt.valid_bit_depth = format->valid_bit_depth; base_cfg->audio_fmt.ch_cfg = format->ch_cfg; + base_cfg->audio_fmt.sample_type = format->sample_type; - dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n", + dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x sample_type:%x\n", format->bit_depth, format->valid_bit_depth, - format->ch_cfg); + format->ch_cfg, format->sample_type); base_cfg->audio_fmt.channel_map = format->ch_map; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index e969ac964daa..0cde541f900c 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -17,6 +17,7 @@ #define __SKL_IPC_H #include +#include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" @@ -105,6 +106,15 @@ struct skl_probe_config { struct extractor_data eprobe[NO_OF_EXTRACTOR]; }; +struct bra_conf { + struct snd_dma_buffer pb_dmab; + struct snd_dma_buffer cp_dmab; + int pb_stream_tag; + int cp_stream_tag; + struct skl_pipe *pb_pipe; + struct skl_pipe *cp_pipe; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -156,7 +166,11 @@ struct skl_sst { int num_sdw_controllers; /* Array of sdw masters */ struct sdw_master *mstr; + struct skl_probe_config probe_config; + + /* BRA configuration data */ + struct bra_conf *bra_pipe_data; }; struct skl_ipc_init_instance_msg { From af82c73ba5db8f098ae9c204f364fb4b1f3a9182 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:51:00 +0530 Subject: [PATCH 0103/1276] ASoC: Intel: CNL: Register BRA ops in init. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/bxt-sst.c | 2 +- sound/soc/intel/skylake/cnl-sst-dsp.h | 2 +- sound/soc/intel/skylake/cnl-sst.c | 19 ++++++++++++++++--- sound/soc/intel/skylake/skl-messages.c | 5 ++--- sound/soc/intel/skylake/skl-sst-dsp.h | 8 ++++---- sound/soc/intel/skylake/skl-sst.c | 3 ++- sound/soc/intel/skylake/skl.h | 6 +++--- 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 2b169b40e6c4..6f9b24c8c211 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -566,7 +566,7 @@ static struct sst_dsp_device skl_dev = { int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp) + struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 323cebc4e389..5f0653b36308 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -118,7 +118,7 @@ void cnl_ipc_free(struct sst_generic_ipc *ipc); int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + struct skl_sst **dsp, void *ptr); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx); void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 784d92df7ac3..ae3c6aa5557e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -516,13 +516,14 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) #if IS_ENABLED(CONFIG_SND_SOC_RT700) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, - void __iomem *mmio_base, int irq) + void __iomem *mmio_base, int irq, void *ptr) { struct sdw_master_capabilities *m_cap, *map_data; struct sdw_mstr_dp0_capabilities *dp0_cap; struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; struct cnl_sdw_data *p_data; + struct cnl_bra_operation *p_ptr = ptr; int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ #if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) @@ -538,11 +539,18 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dsp->num_sdw_controllers = 0; } dsp->mstr = master; + + dsp->bra_pipe_data = devm_kzalloc(dev, + (sizeof(*dsp->bra_pipe_data) * + dsp->num_sdw_controllers), + GFP_KERNEL); + /* TODO This should come from ACPI */ for (i = 0; i < dsp->num_sdw_controllers; i++) { p_data = devm_kzalloc(dev, sizeof(*p_data), GFP_KERNEL); if (!p_data) return -ENOMEM; + /* PCI Device is parent of the SoundWire master device */ /* TODO: All these hardcoding should come from ACPI */ master[i].dev.parent = dev; @@ -651,6 +659,11 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, p_data->alh_base = mmio_base + CNL_ALH_BASE; p_data->inst_id = i; p_data->irq = irq; + + p_data->bra_data = kzalloc((sizeof(struct cnl_sdw_bra_cfg)), + GFP_KERNEL); + p_data->bra_data->drv_data = dsp; + p_data->bra_data->bra_ops = p_ptr; ret = sdw_add_master_controller(&master[i]); if (ret) { dev_err(dev, "Failed to register soundwire master\n"); @@ -676,7 +689,7 @@ static void skl_unregister_sdw_masters(struct skl_sst *ctx) int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp) + struct skl_sst **dsp, void *ptr) { struct skl_sst *cnl; struct sst_dsp *sst; @@ -731,7 +744,7 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, } #if IS_ENABLED(CONFIG_SND_SOC_RT700) - ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); + ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq, ptr); if (ret) { dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); return ret; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 28d990450463..bb7f24286d30 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1049,9 +1049,8 @@ int skl_init_dsp(struct skl *skl) } loader_ops = ops->loader_ops(); - ret = ops->init(bus->dev, mmio_base, irq, - skl->fw_name, loader_ops, - &skl->skl_sst); + ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, + &skl->skl_sst, &cnl_sdw_bra_ops); if (ret < 0) goto unmap_mmio; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 2a3ad2d43448..e874e75a15fc 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -230,11 +230,11 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr); int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 5951bbdf1f1a..277e972eb858 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -528,7 +528,8 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4c7cb272a029..2bafc7d40cec 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -125,10 +125,10 @@ struct skl_dsp_ops { int id; unsigned int num_cores; struct skl_dsp_loader_ops (*loader_ops)(void); - int (*init)(struct device *dev, void __iomem *mmio_base, - int irq, const char *fw_name, + int (*init)(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops loader_ops, - struct skl_sst **skl_sst); + struct skl_sst **skl_sst, void *ptr); int (*init_fw)(struct device *dev, struct skl_sst *ctx); void (*cleanup)(struct device *dev, struct skl_sst *ctx); }; From 6e7bf332a2f544805121541f2513ad5be0669597 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Thu, 27 Oct 2016 12:04:26 +0530 Subject: [PATCH 0104/1276] ASoC: rt700: codec changes for BRA feature This patch create sysfs entry to test BRA feature. sysfs entry created: /sys/bus/soundwire/devices//bra_trigger On reading this sysfs entry, codec driver will initiate BRA read request of 36 bytes to bus driver. On successful packet transfer, bus driver will return SUCCESS with 36 bytes copied in buffer provided by codec driver. Signed-off-by: Sanyog Kale --- sound/soc/codecs/rt700.c | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 8cb67827a8db..773e77bc7ed6 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1432,6 +1432,70 @@ static int rt700_clock_config(struct device *dev, struct alc700 *alc700) return 0; } +static int rt700_create_bra_block(struct sdw_slv *slave) +{ + struct sdw_bra_block bra_block; + u8 *value; + int ret = 0; + + /* Fill bra data structure */ + bra_block.slave_addr = slave->slv_addr->slv_number; + bra_block.cmd = 0; /* Read command */ + bra_block.num_bytes = 36; /* 36 bytes */ + bra_block.reg_offset = 0x0000; + + value = kzalloc(bra_block.num_bytes, GFP_KERNEL); + if (!value) + return -ENOMEM; + + /* Memset with some fixed pattern */ + memset(value, 0xAB, bra_block.num_bytes); + bra_block.values = value; + + pr_info("SDW: BRA: slv_addr:%d, cmd:%d, num_bytes:%d, reg_offset:0x%x\n", + bra_block.slave_addr, + bra_block.cmd, + bra_block.num_bytes, + bra_block.reg_offset); + + print_hex_dump(KERN_DEBUG, "SDW: BRA: CODEC BUFFER:", DUMP_PREFIX_OFFSET, 8, 4, + bra_block.values, bra_block.num_bytes, false); + + + ret = sdw_slave_xfer_bra_block(slave->mstr, &bra_block); + if (ret) { + dev_err(&slave->dev, "SDW: BRA transfer failed\n"); + kfree(bra_block.values); + return ret; + } + + print_hex_dump(KERN_DEBUG, "SDW: BRA: CODEC BUFFER RCVD:", DUMP_PREFIX_OFFSET, 8, 4, + bra_block.values, bra_block.num_bytes, false); + + pr_info("SDW: BRA: Transfer successful\n"); + + /* Free up memory */ + kfree(bra_block.values); + + return ret; +} + +static ssize_t rt700_bra_trigger(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + + rt700_create_bra_block(rt700->sdw); + + pm_runtime_put_sync(dev); + + return 0; +} +static DEVICE_ATTR(bra_trigger, 0444, rt700_bra_trigger, NULL); + int rt700_probe(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) { @@ -1548,6 +1612,15 @@ int rt700_probe(struct device *dev, struct regmap *regmap, return ret; } + /* create sysfs entry */ + ret = device_create_file(dev, &dev_attr_bra_trigger); + if (ret < 0) + return ret; + + pm_runtime_get_sync(&rt700->sdw->mstr->dev); + pm_runtime_mark_last_busy(&rt700->sdw->mstr->dev); + pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); pm_runtime_enable(&slave->dev); From 320da75bdcd5eac784f4e0b2582df7fdc220217a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 6 Dec 2006 11:28:06 +0530 Subject: [PATCH 0105/1276] ASoC: Intel: Add support for Icelake IDs Icelake is next gen SoC, so add the IDs for Icelake Change-Id: I1210d2ea5c1d19137cd0829bd0b86a13a8fcd4f Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 8 ++++++++ sound/soc/intel/skylake/skl.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bb7f24286d30..dde1aeb11153 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -274,6 +274,14 @@ static const struct skl_dsp_ops dsp_ops[] = { .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup }, + { + .id = 0x34c8, + .num_cores = 4, + .loader_ops = bxt_get_loader_ops, + .init = cnl_sst_dsp_init, + .init_fw = cnl_sst_init_fw, + .cleanup = cnl_sst_dsp_cleanup + }, }; static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 19ee283f9586..e4eb58c7a747 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1215,6 +1215,11 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; +static struct sst_acpi_mach sst_icl_devdata[] = { + { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ @@ -1232,6 +1237,9 @@ static const struct pci_device_id skl_ids[] = { /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + /* ICL */ + { PCI_DEVICE(0x8086, 0x34c8), + .driver_data = (unsigned long)&sst_icl_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 461d851ce54ab2e829ecdcc99681f009d7f8e576 Mon Sep 17 00:00:00 2001 From: Jaikrishna Nemallapudi Date: Wed, 14 Dec 2016 17:12:45 +0530 Subject: [PATCH 0106/1276] ASoC: Intel: Fix build warning for unused variables This patch initialize uninitialized variables reported as build warnings. Change-Id: Idaa8c3d5992a9d29d21646d653a2db57e8bbbd2a Signed-off-by: Jaikrishna Nemallapudi Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar Reviewed-by: Prusty, Subhransu S Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-compress.c | 4 +--- sound/soc/intel/skylake/skl-compress.h | 5 +++-- sound/soc/intel/skylake/skl-debug.c | 5 +++++ sound/soc/intel/skylake/skl-probe.c | 5 ++++- sound/soc/intel/skylake/skl.c | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index c8b26e80b974..a0b33e198722 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -25,6 +25,7 @@ #include #include #include "skl.h" +#include "skl-compress.h" inline struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) { @@ -61,12 +62,9 @@ void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, int skl_compr_malloc_pages(struct snd_compr_stream *substream, struct hdac_ext_bus *ebus, size_t size) { - struct snd_compr_runtime *runtime; struct snd_dma_buffer *dmab = NULL; struct skl *skl = ebus_to_skl(ebus); - runtime = substream->runtime; - dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); if (!dmab) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index 9fcf6c38f5b8..2db347369c0c 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -18,7 +18,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ - +#ifndef __SKL_COMPRESS_H__ +#define __SKL_COMPRESS_H__ inline struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); @@ -32,4 +33,4 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, int skl_compr_free_pages(struct snd_compr_stream *substream); int skl_substream_free_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream); - +#endif /* __SKL_COMPRESS_H__*/ diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index ff6d8356cb4f..8a20f86544d2 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -639,6 +639,8 @@ static ssize_t adsp_control_write(struct file *file, } err = kstrtouint(buf, 10, &dsp_property); + if (err) + return -EINVAL; if ((dsp_property == DMA_CONTROL) || (dsp_property == ENABLE_LOGS)) { dev_err(d->dev, "invalid input !! not readable\n"); @@ -648,6 +650,9 @@ static ssize_t adsp_control_write(struct file *file, if (tx_param == 1) { err = kstrtouint(id, 10, &tx_param_id); + if (err) + return -EINVAL; + tx_data = (tx_param_id << 8) | dsp_property; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 9c4d96ecc762..b4f5fe4220cf 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -23,10 +23,13 @@ #include #include #include +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "skl.h" #include "skl-topology.h" #include "skl-sst-ipc.h" #include "skl-compress.h" +#include "skl-probe.h" #define USE_SPIB 0 @@ -199,7 +202,7 @@ int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_bus *bus = ebus_to_hbus(ebus); - u64 new_spib_pos; + u64 __maybe_unused new_spib_pos; struct snd_compr_runtime *runtime = substream->runtime; u64 spib_pos = div64_u64(runtime->total_bytes_available, runtime->buffer_size); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e4eb58c7a747..41f0d77f470b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -366,7 +366,7 @@ static int skl_resume(struct device *dev) struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); struct hdac_ext_link *hlink = NULL; - int ret; + int ret = 0; /* Turned OFF in HDMI codec driver after codec reconfiguration */ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { From eb7456d5af7e5ed6ac671cf9f0aa707821fb5a5b Mon Sep 17 00:00:00 2001 From: Jaikrishna Nemallapudi Date: Wed, 22 Mar 2017 19:16:23 +0530 Subject: [PATCH 0107/1276] ASoC: Intel: Fix Compilation issues for probe compress APIs Move the inline to header file. Fixes: 202dcc0("ASoC: Intel: Add Probe compress APIs") Signed-off-by: Jaikrishna Nemallapudi Cc: Divya Prakash Cc: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-compress.c | 5 ----- sound/soc/intel/skylake/skl-compress.h | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index a0b33e198722..67f2b78812b2 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -26,11 +26,6 @@ #include #include "skl.h" #include "skl-compress.h" -inline -struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) -{ - return stream->runtime->private_data; -} struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) { diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index 2db347369c0c..abfff2d27f14 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -20,8 +20,6 @@ */ #ifndef __SKL_COMPRESS_H__ #define __SKL_COMPRESS_H__ -inline -struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, struct snd_dma_buffer *bufp, size_t size); @@ -33,4 +31,9 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, int skl_compr_free_pages(struct snd_compr_stream *substream); int skl_substream_free_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream); +static inline struct hdac_ext_stream +*get_hdac_ext_compr_stream(struct snd_compr_stream *stream) +{ + return stream->runtime->private_data; +} #endif /* __SKL_COMPRESS_H__*/ From da039c8236b25bc1942927c8e47b00ccef002a7f Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Tue, 13 Dec 2016 11:17:20 +0530 Subject: [PATCH 0108/1276] ASoC: Intel: Kconfig and Makefile changes for SoundWire This patch selects SoundWire Bus and SoundWire Master driver when SND_SOC_INTEL_SKYLAKE config is selected. Change-Id: I8f59f930a2c0089663e7976e354a72c78be8e03b Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Nemallapudi, JaikrishnaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/Kconfig | 2 ++ sound/soc/intel/skylake/Makefile | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 2f56dbfea444..487e4445f6a0 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -113,6 +113,8 @@ config SND_SOC_INTEL_SKYLAKE select SND_SOC_INTEL_SST select SND_SOC_COMPRESS select SND_SOC_ACPI_INTEL_MATCH + select SDW + select SDW_CNL help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 7b8cf119aeae..f36c0bb6e6c2 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -13,9 +13,7 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ skl-sst-utils.o skl-fwlog.o -ifdef CONFIG_SDW - snd-soc-skl-ipc-objs += cnl-acpi.o -endif + snd-soc-skl-ipc-objs += cnl-acpi.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o From e23b29cf270029ff0ef96797b10404878f80bb43 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 23 Nov 2016 19:05:46 +0530 Subject: [PATCH 0109/1276] ASoC: Intel: Boards: Add CNL RT274 I2S machine driver Add the CNL I2S machine driver using Realtek ALC274 codec in I2S mode. Change-Id: Ife808f52d69e73a8156130c446a3ab0602fff63d Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 385 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 2 +- 4 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e6b065fa1dff..69e1081966a8 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -328,6 +328,17 @@ config SND_SOC_INTEL_CNL_SVFPGA_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "Cannonlake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Cannonlake platform + with RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index c5e0ff065610..72dfb2ccdb61 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o @@ -49,6 +50,7 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 000000000000..1b788f8e59a2 --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,385 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +static struct snd_soc_jack cnl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, +}; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static unsigned int rates_supported[] = { + 48000, + 32000, + 24000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list rate_constraints = { + .count = ARRAY_SIZE(rates_supported), + .list = rates_supported, +}; + +static int cnl_fe_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &rate_constraints); +} + +static struct snd_soc_ops cnl_fe_ops = { + .startup = cnl_fe_startup, +}; + +static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#define CNL_FREQ_OUT 19200000 + +static int rt274_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret, ratio = 100; + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ratio * params_rate(params), CNL_FREQ_OUT); + if (ret != 0) { + dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops rt274_ops = { + .hw_params = rt274_hw_params, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { + { + .name = "CNL Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + .ops = &rt274_ops, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_msic_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), +}; + +static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static struct platform_driver snd_cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_cnl_rt274_mc_probe, +}; + +module_platform_driver(snd_cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 41f0d77f470b..e3965a15e7d2 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1215,7 +1215,7 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; -static struct sst_acpi_mach sst_icl_devdata[] = { +static struct snd_soc_acpi_mach sst_icl_devdata[] = { { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, {} }; From 55d9beaf0000a41694114cc317fe64c78909f943 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Fri, 15 Dec 2006 12:19:15 +0530 Subject: [PATCH 0110/1276] ASoC: Intel: Modify Icelake machine id to use RT274 Icelake features rt274 codec. Hence updating to use the right machine driver Change-Id: Ia60530a67b17b682e9a265150b53e1f2fa7095e2 Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e3965a15e7d2..879be20b361a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,7 +1216,12 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { }; static struct snd_soc_acpi_mach sst_icl_devdata[] = { - { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, + { + .id = "dummy", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, {} }; From ee6fa97cc4124fc9b4500d30c358aac8c02ce209 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Wed, 3 Jan 2007 09:21:03 +0530 Subject: [PATCH 0111/1276] ASoC: Intel: board: Add id_table in cnl_rt274 ICL and CNL use the same machine driver with rt274 codec. Hence added id_table to facilitate this. Change-Id: I7484f774c1fc2b1c3c779f0f084585fbf02966a2 Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 1b788f8e59a2..20058f9c7fd1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -370,12 +370,19 @@ static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); } +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt274" }, + { .name = "icl_rt274" }, + { } +}; + static struct platform_driver snd_cnl_rt274_driver = { .driver = { .name = "cnl_rt274", .pm = &snd_soc_pm_ops, }, .probe = snd_cnl_rt274_mc_probe, + .id_table = cnl_board_ids, }; module_platform_driver(snd_cnl_rt274_driver); @@ -383,3 +390,4 @@ module_platform_driver(snd_cnl_rt274_driver); MODULE_AUTHOR("Guneshwor Singh "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cnl_rt274"); +MODULE_ALIAS("platform:icl_rt274"); From f66cba9b6795a2937b14b774916515d10ff98d42 Mon Sep 17 00:00:00 2001 From: Mousami Jana Date: Mon, 19 Dec 2016 12:00:13 +0530 Subject: [PATCH 0112/1276] ASoC: Intel: Skylake: Support for all rates from 8K to 192K Add support for all sample rates in FE pipelines which are required for IVI usecases. Platform capabilities have been changed to support all rates via SNDRV_PCM_RATE_KNOT. Supported rates are validated against a constraint list. The list contains the rates supported by the DSP FW's rate conversion algorithm. Change-Id: I28f275e0f0a9f2a497d8d0a9ab9b2a5b2b067e46 Signed-off-by: Mousumi Jana Signed-off-by: Yadav, PramodX K Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 37 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ac1eef7d2b41..a6901c223652 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -50,9 +50,8 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_8000, + SNDRV_PCM_FMTBIT_S24_LE, /* TODO Add constraints to other machine drivers */ + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 48000, .channels_min = 1, @@ -124,6 +123,31 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) return HDAC_EXT_STREAM_TYPE_COUPLED; } +static unsigned int rates[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 128000, + 176400, + 192000, +}; + +static struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + + /* * check if the stream opened is marked as ignore_suspend by machine, if so * then enable suspend_active refcount @@ -246,6 +270,11 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, skl_set_pcm_constrains(bus, runtime); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_rates); + + /* * disable WALLCLOCK timestamps for capture streams * until we figure out how to handle digital inputs @@ -974,7 +1003,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "System Playback", .channels_min = HDA_MONO, .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 32, From 5b19ba75b23334a083fd72833d670ebfde4e648d Mon Sep 17 00:00:00 2001 From: bardliao Date: Tue, 10 Jan 2017 10:54:11 +0530 Subject: [PATCH 0113/1276] ASoc: rt700: Fix for first playback and capture no audio issue This patch fixes no audio issue observed with first playback and capture after booting the platform with power disconnect/connect. In case for playback the issue was randomly observed and in case of capture, the issue was always reproducible. Change-Id: I0d91afa26a83a25295ab0b8faa1221caa32c80dc Signed-off-by: bardliao Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700.c | 171 ++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 64 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 773e77bc7ed6..400f7bbc6245 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -623,16 +623,41 @@ int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic) } EXPORT_SYMBOL(rt700_jack_detect); +static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, r_val); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *r_val); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, l_val); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *l_val); +} + /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); - unsigned int addr_h, addr_l, val_h, val_l; + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; unsigned int read_ll, read_rl; + int i; /* Can't use update bit function, so read the original value first */ @@ -642,76 +667,107 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, val_h = 0x80; else /* input */ val_h = 0x0; - /* R Channel */ - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); - - /* L Channel */ - val_h |= 0x20; - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); /* Now set value */ addr_h = mc->reg; addr_l = mc->rreg; - /*pr_debug("%s val = %d, %d\n", ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]);*/ pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]); - /* L Channel */ - val_h = (1 << mc->shift) | (1 << 5); + ucontrol->value.integer.value[1]); + /* L Channel */ if (mc->invert) { /* for mute */ - val_l = (mc->max - ucontrol->value.integer.value[0]) << 7; + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; /* keep gain */ read_ll = read_ll & 0x7f; - val_l |= read_ll; + val_ll |= read_ll; } else { /* for gain */ - val_l = ((ucontrol->value.integer.value[0]) & mc->max); + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; /* keep mute status */ read_ll = read_ll & 0x80; - val_l |= read_ll; + val_ll |= read_ll; } - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, val_l); - pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); - /* R Channel */ - val_h = (1 << mc->shift) | (1 << 4); - if (mc->invert) { + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); /* for mute */ - val_l = (mc->max - ucontrol->value.integer.value[1]) << 7; + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; /* keep gain */ read_rl = read_rl & 0x7f; - val_l |= read_rl; + val_lr |= read_rl; } else { /* for gain */ - val_l = ((ucontrol->value.integer.value[1]) & mc->max); + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; /* keep mute status */ read_rl = read_rl & 0x80; - val_l |= read_rl; + val_lr |= read_rl; } - val_h = (1 << mc->shift) | (1 << 4); - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, val_l); - pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + pr_debug("%s i=%d\n", __func__, i); + addr_h = mc->reg; + addr_l = mc->rreg; + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_ll); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_ll); + + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_ll); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_ll); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_lr); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_lr); + + } + /* check result */ + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) { + pr_debug("write command successful\n"); + break; + } + + pr_warn("write command unsuccessful, retry\n"); + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); return 0; } @@ -731,22 +787,8 @@ static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, val_h = 0x80; else /* input */ val_h = 0x0; - /* R Channel */ - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); - /* L Channel */ - val_h |= 0x20; - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); if (mc->invert) { /* for mute status */ @@ -957,10 +999,14 @@ static int rt700_set_bias_level(struct snd_soc_component *component, { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt700_priv *rt700 = snd_soc_codec_get_drvdata(codec); + unsigned int sdw_data_0; + pr_debug("%s level=%d\n", __func__, level); switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { + pm_runtime_get_sync(&rt700->sdw->mstr->dev); snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); } @@ -969,6 +1015,7 @@ static int rt700_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_STANDBY: snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); break; default: @@ -1379,12 +1426,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0); } - /* Enable Jack Detection */ - regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); - regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); - rt700_index_write(rt700->regmap, 0x10, 0x2420); - rt700_index_write(rt700->regmap, 0x19, 0x2e11); - return count; } @@ -1518,6 +1559,9 @@ int rt700_probe(struct device *dev, struct regmap *regmap, &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); dev_info(&slave->dev, "%s\n", __func__); + /* Enable clock before setting */ + pm_runtime_get_sync(&rt700->sdw->mstr->dev); + /* Set Tx route */ /* Filter 02: index 91[13:12] 07[3] */ /* Filter 03: index 5f[15:14] 07[4] */ @@ -1617,7 +1661,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, if (ret < 0) return ret; - pm_runtime_get_sync(&rt700->sdw->mstr->dev); pm_runtime_mark_last_busy(&rt700->sdw->mstr->dev); pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); From 10b32fdb233e55231f94bf2e55c2648441264cf2 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 6 Jan 2017 16:06:23 +0530 Subject: [PATCH 0114/1276] ASoC: Intel: Add SoundWire aggregation support This patch adds aggregation support on Master 1 and Master 2 under CONFIG_SND_SOC_SDW_AGGM1M2 config. It also removes Maxim FPGA codec aggregation related changes. Change-Id: I3ae6755543c4992a2dcddb4ab86ca3c503a9bd36 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja --- sound/soc/intel/skylake/cnl-sst.c | 12 ++++-------- sound/soc/intel/skylake/skl-sdw-pcm.c | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index ae3c6aa5557e..790af2489d0e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -526,11 +526,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_bra_operation *p_ptr = ptr; int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ -#if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) - dsp->num_sdw_controllers = 3; -#else dsp->num_sdw_controllers = 4; -#endif master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); @@ -636,18 +632,18 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, switch (i) { case 0: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; -#ifdef CONFIG_SND_SOC_MXFPGA - master[i].link_sync_mask = 0x1; -#endif break; case 1: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 master[i].link_sync_mask = 0x2; #endif break; case 2: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + master[i].link_sync_mask = 0x4; +#endif break; case 3: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_3_BASE; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 564602c0ee12..2c5ba970e34c 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -47,7 +47,7 @@ struct sdw_dma_data { int mstr_nr; }; -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 static char uuid_playback[] = "Agg_p"; static char uuid_capture[] = "Agg_c"; #endif @@ -102,7 +102,7 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) uuid = uuid_playback; else From 3c8faaa67b4715accc0f121294c8be55b08235bd Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Fri, 26 Feb 2016 20:00:57 +0530 Subject: [PATCH 0115/1276] ASoC: Intel: Skylake: Avoid resume capablity for capture streams DMA resume capability is not supported by the HW. Hence this patch avoids resume capablity for capture streams. Change-Id: If3f44facdd746677d8b1021759df996a09b0c024 Signed-off-by: R, Dharageswari Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Prodduvaka, Leoni Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a6901c223652..a7b8df50aa9b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -536,7 +536,11 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - if (!w->ignore_suspend) { + /* + * DMA resume capablity is not attempted for capture stream + * as it is not supported by HW + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* * enable DMA Resume enable bit for the stream, set the * dpib & lpib position to resume before starting the From 0eae5822175f69ed524f6bc891b18f359c4af5ad Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 18 Jan 2017 15:53:24 +0530 Subject: [PATCH 0116/1276] ASoC: Intel: Skylake: Support all I2S ports with all possible capabilities This patch adds all possible capabilities that the platform supports. This is the superset of the capabilities that all machines support. Below are the platform capabilities supported: Rates = Support all rates via SNDRV_PCM_RATE_KNOT BE Sample size = S16_LE, S24_LE, S32_LE FE Sample size = S16_LE, S24_LE, S32_LE, FLOAT_LE BE Num Channels = 1 to 8 FE Num Channels = 1 to 8 Change-Id: I975d3d1599505a1a4592160377b0d0ede1548c54 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 129 ++++++++++++++++-------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a7b8df50aa9b..4701d9e09647 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -35,6 +35,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 #define HDA_QUAD 4 +#define HDA_8_CH 8 static const struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -50,10 +51,11 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S24_LE, /* TODO Add constraints to other machine drivers */ + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, .channels_min = 1, .channels_max = 8, .buffer_bytes_max = AZX_MAX_BUF_SIZE, @@ -1006,20 +1008,19 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .playback = { .stream_name = "System Playback", .channels_min = HDA_MONO, - .channels_max = HDA_STEREO, + .channels_max = HDA_8_CH, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE, .sig_bits = 32, }, .capture = { .stream_name = "System Capture", .channels_min = HDA_MONO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_KNOT | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE, .sig_bits = 32, }, }, @@ -1172,17 +1173,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp0 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp0 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1190,17 +1193,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp1 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp1 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1208,17 +1213,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp2 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp2 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1226,17 +1233,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp3 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp3 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1244,17 +1253,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp4 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp4 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1262,17 +1273,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp5 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp5 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { From f8a4a56ab5f64729291b985f1e56eed4650e9be2 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Fri, 10 Feb 2017 20:22:42 +0530 Subject: [PATCH 0117/1276] ASoC: Intel: Skylake: Add platform DAI for deepbuffer capture This patch adds platform DAI for deepbuffer capture Change-Id: I2278766ac028e4801ec96a26c28b40c6e5c0e477 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4701d9e09647..1da485053967 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1073,6 +1073,13 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, + .capture = { + .stream_name = "Deepbuffer Capture", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, }, { .name = "Compress Probe0 Pin", From 8f50546d25ae5577dd0127d4aa4f116270893120 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Sun, 13 Mar 2016 22:22:47 +0530 Subject: [PATCH 0118/1276] ASoC: Intel: board: Enable deepbuffer capture in cnl_rt274 Change-Id: I42a782d7689a90b814214420bc3a3ea8e2ad224e Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 20058f9c7fd1..eebde84e7700 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -226,6 +226,7 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .codec_dai_name = "snd-soc-dummy-dai", .platform_name = pname, .dpcm_playback = 1, + .dpcm_capture = 1, .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, From aa6e3912dfbb08a3f40e150e7ceeb9f6bb6f72ae Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Fri, 13 Jan 2017 10:49:09 +0530 Subject: [PATCH 0119/1276] ASoC: Intel: Add Icelake machine id to use RT700 Icelake supports RT700 SoundWire codec as well, hence updating the machine id list. Change-Id: I2ff08cac04d4affcdae92a36fc4b74f352cb624c Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 879be20b361a..9bcd376ec445 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,12 +1216,22 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { }; static struct snd_soc_acpi_mach sst_icl_devdata[] = { +#if IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "dummy", + .drv_name = "icl_rt700", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, +#else { .id = "dummy", .drv_name = "icl_rt274", .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &cnl_pdata, }, + +#endif {} }; From e74d8ee9ddd29c5cdd16c95c6c26c2cce9261f98 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 11 Jan 2017 18:43:31 +0530 Subject: [PATCH 0120/1276] ASoC: Intel: Add Icelake machine id to use WM8281 Icelake supports WM8281 codec as well, hence updating the machine id list. Change-Id: I7f18b9cc11d06f2d3c535a611b5bd2894b9ab2ad Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 9bcd376ec445..dbff367fb3d0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1223,6 +1223,13 @@ static struct snd_soc_acpi_mach sst_icl_devdata[] = { .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &cnl_pdata, }, +#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) + { + .id = "dummy", + .drv_name = "icl_wm8281", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, #else { .id = "dummy", From f33099ce11d74bc1fd10a4d4aafcc80fa51c56c2 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 15 Feb 2017 06:04:04 +0530 Subject: [PATCH 0121/1276] ASoC: Intel: Skylake: Fix library name length Topology framework allows a maximum string length of 44 bytes. So, fix the library name length to 44 bytes as well. Change-Id: I3df2bb5d6130bc96200565aa59757b9c8eb0119e Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0cde541f900c..6c037d2c8a3c 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -72,7 +72,7 @@ struct skl_d0i3_data { struct delayed_work work; }; -#define SKL_LIB_NAME_LENGTH 128 +#define SKL_LIB_NAME_LENGTH 44 #define SKL_MAX_LIB 16 struct skl_lib_info { From b22a2f4e3f3473d2948a3156753d3a848bf5aafe Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Mon, 23 Jan 2017 05:43:58 +0530 Subject: [PATCH 0122/1276] ASoC: Intel: Skylake: Update SDW BRA interface Change SoundWire BRA interfaces to accommodate updated driver module config structures. Change-Id: I7e2099846389fe106196568a4eb7406385a26099 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 199 ++++++++++++++++--------- 1 file changed, 131 insertions(+), 68 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index dde1aeb11153..495f82fc5d65 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -334,7 +334,24 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, struct skl_pipe *host_cpr_pipe = NULL; struct skl_pipe_params host_cpr_params; struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; + struct skl_module host_cpr_mod, link_cpr_mod; int ret; + struct skl_module_fmt *in_fmt, *out_fmt; + u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, + 74, 148, 60, 31, 162, 232, 47, 157, 218 }; + + link_cpr_cfg.module = &link_cpr_mod; + host_cpr_cfg.module = &host_cpr_mod; + + /* + * To get the pvt id, UUID of the module config is + * necessary. Hence hardocde this to the UUID fof copier + * module + */ + memcpy(&host_cpr_cfg.guid, &guid, 16); + memcpy(&link_cpr_cfg.guid, &guid, 16); + in_fmt = &host_cpr_cfg.module->formats[0].inputs[0].fmt; + out_fmt = &host_cpr_cfg.module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -343,6 +360,10 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, goto error; } + host_cpr_cfg.fmt_idx = 0; + host_cpr_cfg.res_idx = 0; + link_cpr_cfg.fmt_idx = 0; + link_cpr_cfg.res_idx = 0; bra_data->pb_pipe = host_cpr_pipe; host_cpr_pipe->p_params = &host_cpr_params; @@ -371,54 +392,59 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg.id.module_id = 4; #endif host_cpr_cfg.id.instance_id = 1; - host_cpr_cfg.mcps = 100000; - host_cpr_cfg.mem_pages = 0; - host_cpr_cfg.ibs = 384; - host_cpr_cfg.obs = 384; + host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); + if (host_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + + host_cpr_cfg.module->resources[0].cps = 100000; + host_cpr_cfg.module->resources[0].is_pages = 0; + host_cpr_cfg.module->resources[0].ibs = 384; + host_cpr_cfg.module->resources[0].obs = 384; host_cpr_cfg.core_id = 0; - host_cpr_cfg.max_in_queue = 1; - host_cpr_cfg.max_out_queue = 1; - host_cpr_cfg.is_loadable = 0; + host_cpr_cfg.module->max_input_pins = 1; + host_cpr_cfg.module->max_output_pins = 1; + host_cpr_cfg.module->loadable = 0; host_cpr_cfg.domain = 0; host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.dma_buffer_size = 2; + host_cpr_cfg.module->resources[0].dma_buffer_size = 2; host_cpr_cfg.pdi_type = 0; host_cpr_cfg.converter = 0; host_cpr_cfg.vbus_id = 0; host_cpr_cfg.sdw_agg_enable = 0; host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.in_fmt[0].channels = 1; - host_cpr_cfg.in_fmt[0].s_freq = 96000; - host_cpr_cfg.in_fmt[0].bit_depth = 32; - host_cpr_cfg.in_fmt[0].valid_bit_depth = 24; - host_cpr_cfg.in_fmt[0].ch_cfg = 0; - host_cpr_cfg.in_fmt[0].interleaving_style = 0; - host_cpr_cfg.in_fmt[0].sample_type = 0; - host_cpr_cfg.in_fmt[0].ch_map = 0xFFFFFFF1; - - host_cpr_cfg.out_fmt[0].channels = 1; - host_cpr_cfg.out_fmt[0].s_freq = 96000; - host_cpr_cfg.out_fmt[0].bit_depth = 32; - host_cpr_cfg.out_fmt[0].valid_bit_depth = 24; - host_cpr_cfg.out_fmt[0].ch_cfg = 0; - host_cpr_cfg.out_fmt[0].interleaving_style = 0; - host_cpr_cfg.out_fmt[0].sample_type = 0; - host_cpr_cfg.out_fmt[0].ch_map = 0xFFFFFFF1; - - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, - sizeof(host_cpr_cfg.m_in_pin), + in_fmt->channels = 1; + in_fmt->s_freq = 96000; + in_fmt->bit_depth = 32; + in_fmt->valid_bit_depth = 24; + in_fmt->ch_cfg = 0; + in_fmt->interleaving_style = 0; + in_fmt->sample_type = 0; + in_fmt->ch_map = 0xFFFFFFF1; + + out_fmt->channels = 1; + out_fmt->s_freq = 96000; + out_fmt->bit_depth = 32; + out_fmt->valid_bit_depth = 24; + out_fmt->ch_cfg = 0; + out_fmt->interleaving_style = 0; + out_fmt->sample_type = 0; + out_fmt->ch_map = 0xFFFFFFF1; + + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, + sizeof(*host_cpr_cfg.m_in_pin), GFP_KERNEL); if (!host_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, - sizeof(host_cpr_cfg.m_out_pin), + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, + sizeof(*host_cpr_cfg.m_out_pin), GFP_KERNEL); if (!host_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -445,6 +471,11 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, sizeof(struct skl_module_cfg)); link_cpr_cfg.id.instance_id = 2; + link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); + if (link_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x3; @@ -453,16 +484,16 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, #endif link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, - sizeof(link_cpr_cfg.m_in_pin), + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, + sizeof(*link_cpr_cfg.m_in_pin), GFP_KERNEL); if (!link_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, - sizeof(link_cpr_cfg.m_out_pin), + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, + sizeof(*link_cpr_cfg.m_out_pin), GFP_KERNEL); if (!link_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -534,8 +565,26 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *link_cpr_pipe = NULL; struct skl_pipe_params link_cpr_params; + struct skl_module host_cpr_mod, link_cpr_mod; struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; int ret; + struct skl_module_fmt *in_fmt, *out_fmt; + u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, + 74, 148, 60, 31, 162, 232, 47, 157, 218 }; + + link_cpr_cfg.module = &link_cpr_mod; + host_cpr_cfg.module = &host_cpr_mod; + + + /* + * To get the pvt id, UUID of the module config is + * necessary. Hence hardocde this to the UUID fof copier + * module + */ + memcpy(&host_cpr_cfg.guid, &guid, 16); + memcpy(&link_cpr_cfg.guid, &guid, 16); + in_fmt = &link_cpr_cfg.module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg.module->formats[0].outputs[0].fmt; /* Capture Pipeline */ link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -544,6 +593,10 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, goto error; } + link_cpr_cfg.fmt_idx = 0; + link_cpr_cfg.res_idx = 0; + host_cpr_cfg.fmt_idx = 0; + host_cpr_cfg.res_idx = 0; bra_data->cp_pipe = link_cpr_pipe; link_cpr_pipe->p_params = &link_cpr_params; link_cpr_cfg.pipe = link_cpr_pipe; @@ -572,14 +625,19 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.id.module_id = 4; #endif link_cpr_cfg.id.instance_id = 3; - link_cpr_cfg.mcps = 100000; - link_cpr_cfg.mem_pages = 0; - link_cpr_cfg.ibs = 1152; - link_cpr_cfg.obs = 1152; + link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); + if (link_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + + link_cpr_cfg.module->resources[0].cps = 100000; + link_cpr_cfg.module->resources[0].is_pages = 0; + link_cpr_cfg.module->resources[0].ibs = 1152; + link_cpr_cfg.module->resources[0].obs = 1152; link_cpr_cfg.core_id = 0; - link_cpr_cfg.max_in_queue = 1; - link_cpr_cfg.max_out_queue = 1; - link_cpr_cfg.is_loadable = 0; + link_cpr_cfg.module->max_input_pins = 1; + link_cpr_cfg.module->max_output_pins = 1; + link_cpr_cfg.module->loadable = 0; link_cpr_cfg.domain = 0; link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; link_cpr_cfg.dev_type = SKL_DEVICE_SDW; @@ -591,7 +649,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; link_cpr_cfg.formats_config.caps_size = 0; - link_cpr_cfg.dma_buffer_size = 2; + link_cpr_cfg.module->resources[0].dma_buffer_size = 2; link_cpr_cfg.pdi_type = 0; link_cpr_cfg.converter = 0; link_cpr_cfg.vbus_id = 0; @@ -613,34 +671,34 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, #endif link_cpr_cfg.formats_config.caps[3] = 0x1; - link_cpr_cfg.in_fmt[0].channels = 6; - link_cpr_cfg.in_fmt[0].s_freq = 48000; - link_cpr_cfg.in_fmt[0].bit_depth = 32; - link_cpr_cfg.in_fmt[0].valid_bit_depth = 24; - link_cpr_cfg.in_fmt[0].ch_cfg = 8; - link_cpr_cfg.in_fmt[0].interleaving_style = 0; - link_cpr_cfg.in_fmt[0].sample_type = 0; - link_cpr_cfg.in_fmt[0].ch_map = 0xFF657120; - - link_cpr_cfg.out_fmt[0].channels = 6; - link_cpr_cfg.out_fmt[0].s_freq = 48000; - link_cpr_cfg.out_fmt[0].bit_depth = 32; - link_cpr_cfg.out_fmt[0].valid_bit_depth = 24; - link_cpr_cfg.out_fmt[0].ch_cfg = 8; - link_cpr_cfg.out_fmt[0].interleaving_style = 0; - link_cpr_cfg.out_fmt[0].sample_type = 0; - link_cpr_cfg.out_fmt[0].ch_map = 0xFF657120; - - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, - sizeof(link_cpr_cfg.m_in_pin), + in_fmt->channels = 6; + in_fmt->s_freq = 48000; + in_fmt->bit_depth = 32; + in_fmt->valid_bit_depth = 24; + in_fmt->ch_cfg = 8; + in_fmt->interleaving_style = 0; + in_fmt->sample_type = 0; + in_fmt->ch_map = 0xFF657120; + + out_fmt->channels = 6; + out_fmt->s_freq = 48000; + out_fmt->bit_depth = 32; + out_fmt->valid_bit_depth = 24; + out_fmt->ch_cfg = 8; + out_fmt->interleaving_style = 0; + out_fmt->sample_type = 0; + out_fmt->ch_map = 0xFF657120; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, + sizeof(*link_cpr_cfg.m_in_pin), GFP_KERNEL); if (!link_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, - sizeof(link_cpr_cfg.m_out_pin), + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, + sizeof(*link_cpr_cfg.m_out_pin), GFP_KERNEL); if (!link_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -667,20 +725,25 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, sizeof(struct skl_module_cfg)); host_cpr_cfg.id.instance_id = 4; + host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); + if (host_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, - sizeof(host_cpr_cfg.m_in_pin), + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, + sizeof(*host_cpr_cfg.m_in_pin), GFP_KERNEL); if (!host_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, - sizeof(host_cpr_cfg.m_out_pin), + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, + sizeof(*host_cpr_cfg.m_out_pin), GFP_KERNEL); if (!host_cpr_cfg.m_out_pin) { ret = -ENOMEM; From efd8027db71b04f25629556ed95b875aa2eea310 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 6 Mar 2017 09:47:50 +0530 Subject: [PATCH 0123/1276] ASoC: Intel: Skylake: Split dais and add flag for dynamic dais Since FE dais can come from topology, split the FE dais from existing dai array and use module param to decide to register them during probe. Updated commit message: Split dai change is already merged in #c3ae22e39db79 ("ASoC: Intel: Skylake: Add flag to check to register FE dais from topology"). With this patch only the remaining the dais are added. Change-Id: I9f5d3d89e070b65800ada57746df21d1f6754e78 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 118 +++++++++++++++--------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1da485053967..7ac209ddd1eb 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -962,46 +962,6 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { }; static struct snd_soc_dai_driver skl_fe_dai[] = { -{ - .name = "TraceBuffer0 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer0 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer1 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer1 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer2 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer2 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer3 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer3 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -1081,24 +1041,6 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, -{ - .name = "Compress Probe0 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_probe_compr_ops, - .playback = { - .stream_name = "Probe Playback", - .channels_min = HDA_MONO, - }, -}, -{ - .name = "Compress Probe1 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_probe_compr_ops, - .capture = { - .stream_name = "Probe Capture", - .channels_min = HDA_MONO, - }, -}, { .name = "LowLatency Pin", .ops = &skl_pcm_dai_ops, @@ -1173,7 +1115,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { }, }; -/* BE CPU Dais */ +/* BE cpu dais and compress dais*/ static struct snd_soc_dai_driver skl_platform_dai[] = { { .name = "SSP0 Pin", @@ -1485,6 +1427,64 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, +{ + .name = "TraceBuffer0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer0 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer1 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer2 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer2 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer3 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer3 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "Compress Probe0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .playback = { + .stream_name = "Probe Playback", + .channels_min = HDA_MONO, + }, +}, +{ + .name = "Compress Probe1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .capture = { + .stream_name = "Probe Capture", + .channels_min = HDA_MONO, + }, +}, }; int skl_dai_load(struct snd_soc_component *cmp, int index, From ce6063be0ae2014933bf52afd74eb21fff4320a4 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 6 Mar 2017 09:56:10 +0530 Subject: [PATCH 0124/1276] ASoC: Intel: Skylake: Add component ops for dai load Since FE dais can come from topology, add component ops for the same. Change-Id: I868be6943a69d0dafc6fb04b91f70be576318400 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Nc, Shreyas Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b8a1d8b90fe1..d17809b337ed 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -617,4 +617,7 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai); #endif From 53927220311197c7fa890690df8ad01a02dc9d7d Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:09:37 +0530 Subject: [PATCH 0125/1276] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt274 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: I0fb07c74450bf55415323539e383ef39ed3ff4c4 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 74 ++++-------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index eebde84e7700..21b40b895629 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -124,30 +124,6 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static unsigned int rates_supported[] = { - 48000, - 32000, - 24000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list rate_constraints = { - .count = ARRAY_SIZE(rates_supported), - .list = rates_supported, -}; - -static int cnl_fe_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &rate_constraints); -} - -static struct snd_soc_ops cnl_fe_ops = { - .startup = cnl_fe_startup, -}; - static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -204,46 +180,6 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { - { - .name = "CNL Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -352,6 +288,15 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -363,6 +308,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 8474f70661ae2f257d910bd142268b277c1af494 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:20:59 +0530 Subject: [PATCH 0126/1276] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt700 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: Ia2dcaeebd785f79c87f3032c0ad39939bb31cee6 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt700.c | 93 ++++-------------------------- 1 file changed, 12 insertions(+), 81 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index d49c3f9de10e..740062a4108a 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -80,45 +80,6 @@ static const struct snd_kcontrol_new cnl_rt700_controls[] = { }; -static int cnl_rt700_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_card *card = runtime->card; - - pr_info("Entry %s\n", __func__); - card->dapm.idle_bias_off = true; - - ret = snd_soc_add_card_controls(card, cnl_rt700_controls, - ARRAY_SIZE(cnl_rt700_controls)); - if (ret) { - pr_err("unable to add card controls\n"); - return ret; - } - return 0; -} - -static unsigned int rates_48000[] = { - 48000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - -static int cnl_rt700_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); -} - -static struct snd_soc_ops cnl_rt700_ops = { - .startup = cnl_rt700_startup, -}; - static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -164,48 +125,6 @@ static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { - { - .name = "Bxtn Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .init = cnl_rt700_init, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_rt700_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &cnl_rt700_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_rt700_ops, - }, - { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", @@ -243,6 +162,15 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl_rt700 = { .name = "cnl_rt700-audio", @@ -252,6 +180,9 @@ static struct snd_soc_card snd_soc_card_cnl_rt700 = { .num_dapm_widgets = ARRAY_SIZE(cnl_rt700_widgets), .dapm_routes = cnl_rt700_map, .num_dapm_routes = ARRAY_SIZE(cnl_rt700_map), + .add_dai_link = cnl_add_dai_link, + .controls = cnl_rt700_controls, + .num_controls = ARRAY_SIZE(cnl_rt700_controls), }; From 05a69172d136acff50380b9a4c12bb3373fcbb6e Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 17 Feb 2017 11:58:27 +0530 Subject: [PATCH 0127/1276] ASoC: Intel: Update device type entry for SoundWire device SoundWire device type is updated as SoundWire PCM and SoundWire PDM type. This information will be used to assign SoundWire stream type (PCM or PDM). Change-Id: Ide861544b6f175153431cc1e411591f9a45e44e4 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 3 ++- sound/soc/intel/skylake/skl-messages.c | 7 ++++--- sound/soc/intel/skylake/skl-topology.c | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index e1a7771f4873..c0145433d05e 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -108,7 +108,8 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_SDW = 0x6, + SKL_DEVICE_SDW_PCM = 0x6, + SKL_DEVICE_SDW_PDM = 0x7, SKL_DEVICE_NONE }; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 495f82fc5d65..b4b2d1dd0ee2 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -476,7 +476,7 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, if (link_cpr_cfg.id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x3; #else @@ -640,7 +640,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.module->loadable = 0; link_cpr_cfg.domain = 0; link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x4; #else @@ -1392,7 +1392,8 @@ static u32 skl_get_node_id(struct skl_sst *ctx, SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; - case SKL_DEVICE_SDW: + case SKL_DEVICE_SDW_PCM: + case SKL_DEVICE_SDW_PDM: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_SDW_LINK_OUTPUT_CLASS : diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2e4a894f58a4..629f420b58de 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2120,7 +2120,8 @@ static u8 skl_tplg_be_link_type(int dev_type) case SKL_DEVICE_HDALINK: ret = NHLT_LINK_HDA; break; - case SKL_DEVICE_SDW: + case SKL_DEVICE_SDW_PCM: + case SKL_DEVICE_SDW_PDM: ret = NHLT_LINK_SDW; break; default: From 16964eb1c5619a8a76aadda02438bdfbffc88dc9 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 17 Feb 2017 12:08:49 +0530 Subject: [PATCH 0128/1276] ASoC: Intel: Skylake: Use device type to determine SoundWire stream type To determine SoundWire stream type, device type information is used instead of pdi type. Change-Id: I98ba5b7141b1a6b865f38697510fb9439dd4816c Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-sdw-pcm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 2c5ba970e34c..6b8c4dba5b01 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -90,9 +90,10 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, ret = -ENOMEM; goto alloc_failed; } - if (m_cfg->pdi_type == SKL_PDI_PCM) + + if (m_cfg->dev_type == SKL_DEVICE_SDW_PCM) dma->stream_type = CNL_SDW_PDI_TYPE_PCM; - else if (m_cfg->pdi_type == SKL_PDI_PDM) + else if (m_cfg->dev_type == SKL_DEVICE_SDW_PDM) dma->stream_type = CNL_SDW_PDI_TYPE_PDM; else { dev_err(dai->dev, "Stream type not known\n"); From 30681e2b178e8c76a282e604a512bd52ed9a4c6b Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Tue, 21 Feb 2017 15:51:38 +0530 Subject: [PATCH 0129/1276] ASoC: Intel: Remove pdi_type support from topology The pdi_type tag used for SoundWire device type is not used, so removing support from driver. Change-Id: I77a71c02cc0b2b51edce24aa667549e24752d095 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 6 ------ sound/soc/intel/skylake/skl-messages.c | 2 -- sound/soc/intel/skylake/skl-topology.h | 1 - 3 files changed, 9 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index c0145433d05e..fc4e4324f94b 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -113,12 +113,6 @@ enum skl_dev_type { SKL_DEVICE_NONE }; -enum skl_pdi_type { - SKL_PDI_PCM = 0, - SKL_PDI_PDM = 1, - SKL_PDI_INVALID = 2 -}; - /** * enum skl_interleaving - interleaving style * diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b4b2d1dd0ee2..79451737be9e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -411,7 +411,6 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; host_cpr_cfg.formats_config.caps_size = 0; host_cpr_cfg.module->resources[0].dma_buffer_size = 2; - host_cpr_cfg.pdi_type = 0; host_cpr_cfg.converter = 0; host_cpr_cfg.vbus_id = 0; host_cpr_cfg.sdw_agg_enable = 0; @@ -650,7 +649,6 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.formats_config.caps_size = 0; link_cpr_cfg.module->resources[0].dma_buffer_size = 2; - link_cpr_cfg.pdi_type = 0; link_cpr_cfg.converter = 0; link_cpr_cfg.vbus_id = 0; link_cpr_cfg.sdw_agg_enable = 0; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index d17809b337ed..00265245beee 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -442,7 +442,6 @@ struct skl_module_cfg { u32 mem_pages; enum d0i3_capability d0i3_caps; u32 dma_buffer_size; /* in milli seconds */ - u8 pdi_type; u32 sdw_stream_num; bool sdw_agg_enable; struct skl_sdw_aggregation sdw_agg; From c4a0016923303e171110d692afc0b8ea86af0f6e Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 8 Mar 2017 03:08:18 +0530 Subject: [PATCH 0130/1276] ASoC: Intel: Skylake: Define tokens for aggregation To support aggregation, define tokens for aggregation id, masters participating in aggregation, link id and channel mask. Change-Id: Ib7e3f5a3aec4d8a6e2dec1b1f045c8078a3ea958 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar --- include/uapi/sound/snd_sst_tokens.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 8ba0112e5336..5d3d81af0c30 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -232,6 +232,16 @@ * from source specified by clock source. * * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry + * %SKL_TKN_U32_AGG_NUM_MASTERS: + * Number of aggregated masters + * + * %SKL_TKN_U32_AGG_LINK_ID: Aggregated master's instance id + * + * %SKL_TKN_U32_AGG_CH_MASK: Represents channels driven by the master + * + * %SKL_TKN_U32_AGG_ID: Aggregation id is a non zero identifier to + * indicate if this endpoint is participating + * in aggregation. * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest @@ -324,7 +334,13 @@ enum SKL_TKNS { SKL_TKN_U32_ASTATE_COUNT, SKL_TKN_U32_ASTATE_KCPS, SKL_TKN_U32_ASTATE_CLK_SRC, - SKL_TKN_MAX = SKL_TKN_U32_ASTATE_CLK_SRC, + + SKL_TKN_U32_AGG_NUM_MASTERS, + SKL_TKN_U32_AGG_LINK_ID, + SKL_TKN_U32_AGG_CH_MASK, + SKL_TKN_U32_AGG_ID, + + SKL_TKN_MAX = SKL_TKN_U32_AGG_ID, }; #endif From 184d511505a30480cb52b8a9bca7c80c91fbb420 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 8 Mar 2017 03:09:33 +0530 Subject: [PATCH 0131/1276] ASoC: Intel: Skylake: Parse tokens to support aggregation To support aggregation, we need to parse the aggregation related tokens like channel mask, number of masters participating in aggregation and link id. So, add parsing logic for the same. Change-Id: I167a5023bcf7e7bd319ecd249a7a4e7c051ff2c2 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-topology.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 629f420b58de..711629143aa3 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2720,7 +2720,7 @@ static int skl_tplg_get_token(struct device *dev, int tkn_count = 0; int ret; static int is_pipe_exists; - static int pin_index, dir, conf_idx; + static int pin_index, dir, conf_idx, agg_id; struct skl_module_iface *iface = NULL; struct skl_module_res *res = NULL; int res_idx = mconfig->res_idx; @@ -2939,6 +2939,23 @@ static int skl_tplg_get_token(struct device *dev, break; + case SKL_TKN_U32_AGG_LINK_ID: + agg_id = tkn_elem->value; + if (agg_id > SDW_MAX_MASTERS) + return -EINVAL; + break; + + case SKL_TKN_U32_AGG_NUM_MASTERS: + mconfig->sdw_agg.num_masters = tkn_elem->value; + mconfig->sdw_agg_enable = (tkn_elem->value > 1) + ? true : false; + break; + + case SKL_TKN_U32_AGG_CH_MASK: + mconfig->sdw_agg.agg_data[agg_id].ch_mask = + tkn_elem->value; + break; + case SKL_TKN_U32_DMA_BUF_SIZE: mconfig->dma_buffer_size = tkn_elem->value; break; From 51771d06fd29955a75414c791139971ad3e6750d Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:41:55 +0530 Subject: [PATCH 0132/1276] ASoC: Intel: CNL: Add DAIs for SDW Aggregation This patch adds DAI to enable Aggregation feature for playback and capture on SoundWire Master 1 and Master 2 with RT700 codec. It also makes changes in channel and format configurations for both playback and capture in DAIs. Change-Id: I252733c39e2e81a2aa8c2e4c44a9416a02aafab2 Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7ac209ddd1eb..265992ec11ff 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1354,17 +1354,17 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx10", - .channels_min = HDA_STEREO, + .channels_min = HDA_MONO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .stream_name = "SDW Rx10", - .channels_min = HDA_STEREO, + .channels_min = HDA_MONO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, { @@ -1405,6 +1405,32 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +{ + /* + * Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW2 Pin", + .id = SDW_BE_DAI_ID_MSTR2, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW2 Tx", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "SDW2 Rx", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + +}, +#endif { /* Currently adding 1 playback and 1 capture pin, ideally it * should be coming from CLT based on endpoints to be supported From bfe16d1ee3dec1c45213544b46fceead4b1bfc65 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 6 Jan 2017 16:12:13 +0530 Subject: [PATCH 0133/1276] ASoC: Intel: Kconfig changes for SoundWire aggregation support This patch adds SoundWire aggregation config. Change-Id: I7e5ab3d7079454dfde16b98caec818dd21e47c99 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Nc, Shreyas Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 487e4445f6a0..9d2be10e484c 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -132,6 +132,11 @@ config SND_SOC_INTEL_CNL_FPGA Select Y if you are using FPGA. If unsure select "N". +config SND_SOC_SDW_AGGM1M2 + bool "Enable SoundWire aggregation on Master 1 and Master 2" + help + Say Y to enable SoundWire aggregation on Master1 and Master2. + endif ## SND_SOC_INTEL_SST_TOPLEVEL From d3efaa4545a3f891a59b27ae39429c6ddc402d63 Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:32:35 +0530 Subject: [PATCH 0134/1276] ASoC: rt700: codec changes for SDW Aggregation Add stream aggregation changes to codec driver. Playback and capture can be performed using two SoundWire master controllers and 2 RT700 codecs each connected to one master controller. Change-Id: I07d22afaa0e7dd4fbabe59adfe9a72b3f1e91852 Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Babu, Ramesh Reviewed-by: D M, Karthik Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700-sdw.c | 14 ++- sound/soc/codecs/rt700.c | 187 ++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt700.h | 3 +- 3 files changed, 197 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index a2533edfa48a..1c935e3a05e2 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -252,7 +252,11 @@ static int rt700_register_sdw_capabilties(struct sdw_slv *sdw, dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; dpn_cap->imp_def_intr_mask = 0; /* bit 0: Test Fail */ +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + dpn_cap->min_ch_num = 1; +#else dpn_cap->min_ch_num = 2; +#endif dpn_cap->max_ch_num = 2; dpn_cap->num_ch_supported = 0; dpn_cap->ch_supported = NULL; @@ -319,7 +323,7 @@ static int rt700_sdw_probe(struct sdw_slv *sdw, ret = sdw_slave_get_bus_params(sdw, alc700_priv->params); if (ret) return -EFAULT; - return rt700_probe(&sdw->dev, regmap, sdw); + return rt700_probe(&sdw->dev, regmap, sdw, sdw_id->driver_data); } static int rt700_sdw_remove(struct sdw_slv *sdw) @@ -344,7 +348,15 @@ static const struct sdw_slv_id rt700_id[] = { {"15:02:5d:07:01:00", 0}, {"16:02:5d:07:01:00", 0}, {"17:02:5d:07:01:00", 0}, +#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 {"10:02:5d:07:00:01", 0}, +#else + {"10:02:5d:07:00:01", 1}, + {"10:02:5d:07:01:02", 2}, + {"10:02:5d:07:01:03", 3}, +#endif +#endif {} }; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 400f7bbc6245..8fc9c00e15b8 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -849,6 +849,39 @@ static const struct snd_kcontrol_new rt700_snd_controls[] = { rt700_set_amp_gain_get, rt700_set_amp_gain_put), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_kcontrol_new rt700_2_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front_2 Playback Volume", RT700_SET_GAIN_DAC1_H, + RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08_2 Capture Switch", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09_2 Capture Switch", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08_2 Capture Volume", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09_2 Capture Volume", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC_2 Volume", RT700_SET_GAIN_AMIC_H, + RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT("Speaker Playback_2 Switch", RT700_SET_GAIN_SPK_H, + RT700_SET_GAIN_SPK_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("Headphone Playback_2 Switch", RT700_SET_GAIN_HP_H, + RT700_SET_GAIN_HP_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), +}; +#endif + static int rt700_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -922,12 +955,29 @@ static const char * const adc_mux_text[] = { "DMIC", }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const char * const adc_mux_2_text[] = { + "MIC2_2", + "LINE1_2", + "LINE2_2", + "DMIC", +}; +#endif + static const SOC_ENUM_SINGLE_DECL( rt700_adc22_enum, RT700_MIXER_IN1, 0, adc_mux_text); static const SOC_ENUM_SINGLE_DECL( rt700_adc23_enum, RT700_MIXER_IN2, 0, adc_mux_text); +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const SOC_ENUM_SINGLE_DECL( + rt700_adc22_2_enum, RT700_MIXER_IN1, 0, adc_mux_2_text); + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc23_2_enum, RT700_MIXER_IN2, 0, adc_mux_2_text); +#endif + static const struct snd_kcontrol_new rt700_adc22_mux = SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, rt700_mux_get, rt700_mux_put); @@ -935,6 +985,15 @@ static const struct snd_kcontrol_new rt700_adc22_mux = static const struct snd_kcontrol_new rt700_adc23_mux = SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, rt700_mux_get, rt700_mux_put); +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_kcontrol_new rt700_adc22_mux_2 = + SOC_DAPM_ENUM_EXT("ADC 22 Mux_2", rt700_adc22_2_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux_2 = + SOC_DAPM_ENUM_EXT("ADC 23 Mux_2", rt700_adc23_2_enum, + rt700_mux_get, rt700_mux_put); +#endif static const char * const out_mux_text[] = { "Front", @@ -972,6 +1031,32 @@ static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_dapm_widget rt700_2_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP_2"), + SND_SOC_DAPM_OUTPUT("SPK_2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC2_2"), + SND_SOC_DAPM_INPUT("MIC2_2"), + SND_SOC_DAPM_INPUT("LINE1_2"), + SND_SOC_DAPM_INPUT("LINE2_2"), + SND_SOC_DAPM_DAC("DAC Front_2", NULL, RT700_SET_STREAMID_DAC1, 4, 0), + SND_SOC_DAPM_DAC("DAC Surround_2", NULL, RT700_SET_STREAMID_DAC2, 4, 0), + SND_SOC_DAPM_MUX("HPO Mux_2", SND_SOC_NOPM, 0, 0, &rt700_hp_mux), + SND_SOC_DAPM_PGA("SPK PGA_2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC 09_2", NULL, RT700_SET_STREAMID_ADC1, 4, 0), + SND_SOC_DAPM_ADC("ADC 08_2", NULL, RT700_SET_STREAMID_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux_2", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux_2), + SND_SOC_DAPM_MUX("ADC 23 Mux_2", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux_2), + SND_SOC_DAPM_AIF_IN("DP1RX_2", "DP1 Playback2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX_2", "DP3 Playback2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX_2", "DP2 Capture2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX_2", "DP4 Capture2", 0, SND_SOC_NOPM, 0, 0), +}; +#endif + static const struct snd_soc_dapm_route rt700_audio_map[] = { {"DAC Front", NULL, "DP1RX"}, {"DAC Surround", NULL, "DP3RX"}, @@ -994,6 +1079,30 @@ static const struct snd_soc_dapm_route rt700_audio_map[] = { {"SPK", NULL, "SPK PGA"}, }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_dapm_route rt700_2_audio_map[] = { + {"DAC Front_2", NULL, "DP1RX_2"}, + {"DAC Surround_2", NULL, "DP3RX_2"}, + {"DP2TX_2", NULL, "ADC 09_2"}, + {"DP4TX_2", NULL, "ADC 08_2"}, + {"ADC 09_2", NULL, "ADC 22 Mux_2"}, + {"ADC 08_2", NULL, "ADC 23 Mux_2"}, + {"ADC 22 Mux_2", "DMIC", "DMIC1_2"}, + {"ADC 22 Mux_2", "LINE1_2", "LINE1_2"}, + {"ADC 22 Mux_2", "LINE2_2", "LINE2_2"}, + {"ADC 22 Mux_2", "MIC2_2", "MIC2_2"}, + {"ADC 23 Mux_2", "DMIC", "DMIC2_2"}, + {"ADC 23 Mux_2", "LINE1_2", "LINE1_2"}, + {"ADC 23 Mux_2", "LINE2_2", "LINE2_2"}, + {"ADC 23 Mux_2", "MIC2_2", "MIC2_2"}, + {"HPO Mux_2", "Front", "DAC Front_2"}, + {"HPO Mux_2", "Surround", "DAC Surround_2"}, + {"HP_2", NULL, "HPO Mux_2"}, + {"SPK PGA_2", NULL, "DAC Front_2"}, + {"SPK_2", NULL, "SPK PGA_2"}, +}; +#endif + static int rt700_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -1035,6 +1144,18 @@ static const struct snd_soc_component_driver soc_component_dev_rt700 = { .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_component_driver soc_component_dev_rt700_2 = { + .set_bias_level = rt700_set_bias_level, + .controls = rt700_2_snd_controls, + .num_controls = ARRAY_SIZE(rt700_2_snd_controls), + .dapm_widgets = rt700_2_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_2_dapm_widgets), + .dapm_routes = rt700_2_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_2_audio_map), +}; +#endif + static int rt700_program_stream_tag(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, int stream_tag) { @@ -1210,14 +1331,14 @@ static struct snd_soc_dai_driver rt700_dai[] = { .id = RT700_AIF1, .playback = { .stream_name = "DP1 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, }, .capture = { .stream_name = "DP2 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, @@ -1229,14 +1350,56 @@ static struct snd_soc_dai_driver rt700_dai[] = { .id = RT700_AIF2, .playback = { .stream_name = "DP3 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, }, .capture = { .stream_name = "DP4 Capture", - .channels_min = 2, + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static struct snd_soc_dai_driver rt700_2_dai[] = { + { + .name = "rt700-aif1_2", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2_2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture2", + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, @@ -1244,6 +1407,7 @@ static struct snd_soc_dai_driver rt700_dai[] = { .ops = &rt700_ops, }, }; +#endif static ssize_t rt700_index_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1538,7 +1702,8 @@ static ssize_t rt700_bra_trigger(struct device *dev, static DEVICE_ATTR(bra_trigger, 0444, rt700_bra_trigger, NULL); int rt700_probe(struct device *dev, struct regmap *regmap, - struct sdw_slave *slave) + struct sdw_slv *slave, + kernel_ulong_t driver_data) { struct rt700_priv *rt700; struct alc700 *alc700 = dev_get_drvdata(dev); @@ -1555,8 +1720,20 @@ int rt700_probe(struct device *dev, struct regmap *regmap, rt700->regmap = regmap; rt700->sdw = slave; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 ret = devm_snd_soc_register_component(dev, &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); +#else + if (driver_data == 1) { + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700, + rt700_dai, ARRAY_SIZE(rt700_dai)); + } else if (driver_data == 2) { + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700_2, + rt700_2_dai, ARRAY_SIZE(rt700_2_dai)); + } +#endif dev_info(&slave->dev, "%s\n", __func__); /* Enable clock before setting */ diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 3ad8b84f60f7..bcfb86340112 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -152,7 +152,8 @@ enum { }; int rt700_probe(struct device *dev, struct regmap *regmap, - struct sdw_slave *slave); + struct sdw_slv *slave, + kernel_ulong_t driver_data); int rt700_remove(struct device *dev); int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, unsigned int *sdw_addr_h, unsigned int *sdw_data_h, From ed800c88e267b42ed690ac356a417e37886108fc Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:38:56 +0530 Subject: [PATCH 0135/1276] ASoC: Intel: Boards: Add SDW Aggregation changes This patch makes machine driver changes to enable Aggregation feature for playback and capture on SoundWire Master 1 and Master 2 with RT700 codec Change-Id: I51b2c3de040621a2dd1989ad2e2ca1e4f70b748f Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: D M, Karthik Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt700.c | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index 740062a4108a..ee8fe3934571 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -44,13 +44,24 @@ struct cnl_rt700_mc_private { int bt_mode; }; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), }; +#else +static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_HP("Headphones_2", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("AMIC_2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; +#endif +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_soc_dapm_route cnl_rt700_map[] = { /*Headphones*/ { "Headphones", NULL, "HP" }, @@ -72,12 +83,46 @@ static const struct snd_soc_dapm_route cnl_rt700_map[] = { {"dmic01_hifi", NULL, "DMIC01 Rx"}, }; +#else +static const struct snd_soc_dapm_route cnl_rt700_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Headphones_2", NULL, "HP_2" }, + { "MIC2", NULL, "AMIC" }, + { "MIC2_2", NULL, "AMIC_2" }, + + /* SWM map link the SWM outs to codec AIF */ + { "DP1 Playback", NULL, "SDW Tx10"}, + { "SDW Tx10", NULL, "sdw_codec0_out"}, + { "DP1 Playback2", NULL, "SDW2 Tx"}, + { "SDW2 Tx", NULL, "sdw_codec0_out"}, + + { "sdw_codec0_in", NULL, "SDW Rx10" }, + { "SDW Rx10", NULL, "DP2 Capture" }, + {"sdw_codec0_in", NULL, "SDW2 Rx"}, + {"SDW2 Rx", NULL, "DP2 Capture2"}, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; +#endif +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_kcontrol_new cnl_rt700_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("AMIC"), SOC_DAPM_PIN_SWITCH("Speaker"), }; +#else +static const struct snd_kcontrol_new cnl_rt700_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Headphones_2"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("AMIC_2"), +}; +#endif static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, @@ -93,7 +138,12 @@ static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); slot_width = 24; rate->min = rate->max = 48000; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + channels->min = 1; + channels->max = 2; +#else channels->min = channels->max = 2; +#endif snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); @@ -125,6 +175,7 @@ static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", @@ -137,18 +188,37 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, +#endif { .name = "SDW1-Codec", .cpu_dai_name = "SDW10 Pin", .platform_name = pname, .codec_name = cname, +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .codec_dai_name = "rt700-aif2", +#else + .codec_dai_name = "rt700-aif1", +#endif + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + { + .name = "SDW2-Codec", + .cpu_dai_name = "SDW2 Pin", + .platform_name = pname, + .codec_name = "sdw-slave2-10:02:5d:07:01:02", + .codec_dai_name = "rt700-aif1_2", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, .no_pcm = 1, .dpcm_playback = 1, .dpcm_capture = 1, }, +#endif { .name = "dmic01", .cpu_dai_name = "DMIC01 Pin", From 0071924767f26f68e15a940f723c46f7678d6b53 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 16 Mar 2017 21:49:33 +0530 Subject: [PATCH 0136/1276] ASoC: Intel: Change sst_ipc_tx_message_wait api to return valid data Since the firmware returns the rx_bytes as a part of ipc response the api parameter is changed to pointer to accommodate the change. Change-Id: I7d5ae8bfaa1e7514fe91b03e2a4e9113956c984a Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/baytrail/sst-baytrail-ipc.c | 7 +++-- sound/soc/intel/common/sst-ipc.c | 35 +++++++++++++++------ sound/soc/intel/common/sst-ipc.h | 3 +- sound/soc/intel/haswell/sst-haswell-ipc.c | 28 ++++++++++------- sound/soc/intel/skylake/skl-debug.c | 16 ++++++---- sound/soc/intel/skylake/skl-messages.c | 3 +- sound/soc/intel/skylake/skl-sst-ipc.c | 34 ++++++++++---------- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 8 files changed, 78 insertions(+), 50 deletions(-) diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index 260447da32b8..403dac3a514b 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -420,13 +420,14 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) struct sst_byt_alloc_response *reply = &stream->reply; u64 header; int ret; + size_t rx_bytes = sizeof(*reply); header = sst_byt_header(IPC_IA_ALLOC_STREAM, sizeof(*str_req) + sizeof(u32), true, stream->str_id); ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, sizeof(*str_req), - reply, sizeof(*reply)); + reply, &rx_bytes); if (ret < 0) { dev_err(byt->dev, "ipc: error stream commit failed\n"); return ret; @@ -448,7 +449,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) goto out; header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); - ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(byt->dev, "ipc: free stream %d failed\n", stream->str_id); @@ -473,7 +474,7 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, header = sst_byt_header(type, 0, false, stream_id); if (wait) return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, - 0, NULL, 0); + 0, NULL, NULL); else return sst_ipc_tx_message_nowait(&byt->ipc, header, NULL, 0); diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index dcff13802c00..f4b09503518e 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -52,7 +52,7 @@ static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc) } static int tx_wait_done(struct sst_generic_ipc *ipc, - struct ipc_message *msg, void *rx_data) + struct ipc_message *msg, void *rx_data, size_t *rx_bytes) { unsigned long flags; int ret; @@ -71,11 +71,21 @@ static int tx_wait_done(struct sst_generic_ipc *ipc, } else { /* copy the data returned from DSP */ - if (msg->rx_size) + if ((rx_bytes != NULL) && + (msg->rx_size > *rx_bytes)) { + dev_err(ipc->dev, "rx size is more than expected\n"); + ret = -EINVAL; + goto err; + } + + if (msg->rx_size) { + if (rx_bytes != NULL) + *rx_bytes = msg->rx_size; memcpy(rx_data, msg->rx_data, msg->rx_size); + } ret = msg->errno; } - +err: list_add_tail(&msg->list, &ipc->empty_list); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); return ret; @@ -83,7 +93,7 @@ static int tx_wait_done(struct sst_generic_ipc *ipc, static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, - size_t rx_bytes, int wait) + size_t *rx_bytes, int wait) { struct ipc_message *msg; unsigned long flags; @@ -98,7 +108,12 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, msg->header = header; msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; + + if (!rx_bytes) + msg->rx_size = 0; + else + msg->rx_size = *rx_bytes; + msg->wait = wait; msg->errno = 0; msg->pending = false; @@ -112,7 +127,8 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); if (wait) - return tx_wait_done(ipc, msg, rx_data); + return tx_wait_done(ipc, msg, rx_data, + rx_bytes); else return 0; } @@ -183,7 +199,8 @@ static void ipc_tx_msgs(struct work_struct *work) } int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) + void *tx_data, size_t tx_bytes, void *rx_data, + size_t *rx_bytes) { int ret; @@ -211,7 +228,7 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes) { return ipc_tx_message(ipc, header, tx_data, tx_bytes, - NULL, 0, 0); + NULL, NULL, 0); } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); @@ -219,7 +236,7 @@ int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) { return ipc_tx_message(ipc, header, tx_data, tx_bytes, - rx_data, rx_bytes, 1); + rx_data, &rx_bytes, 1); } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm); diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 7ed42a640ad6..4cfa9e37ac28 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -75,7 +75,8 @@ struct sst_generic_ipc { }; int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + void *tx_data, size_t tx_bytes, void *rx_data, + size_t *rx_bytes); int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index d33bdaf92c57..557f7800f373 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -826,10 +826,11 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw, struct sst_hsw_ipc_fw_version *version) { int ret; + size_t rx_bytes = sizeof(*version); ret = sst_ipc_tx_message_wait(&hsw->ipc, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), - NULL, 0, version, sizeof(*version)); + NULL, 0, version, &rx_bytes); if (ret < 0) dev_err(hsw->dev, "error: get version failed\n"); @@ -893,7 +894,7 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, } ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req, - sizeof(*req), NULL, 0); + sizeof(*req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: set stream volume failed\n"); return ret; @@ -959,7 +960,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, req.target_volume = volume; ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req, - sizeof(req), NULL, 0); + sizeof(req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: set mixer volume failed\n"); return ret; @@ -1018,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req, - sizeof(stream->free_req), NULL, 0); + sizeof(stream->free_req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: free stream %d failed\n", stream->free_req.stream_id); @@ -1194,6 +1195,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply; u32 header; int ret; + size_t rx_bytes = sizeof(*reply); if (!stream) { dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n"); @@ -1210,7 +1212,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req, - sizeof(*str_req), reply, sizeof(*reply)); + sizeof(*str_req), reply, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "error: stream commit failed\n"); return ret; @@ -1253,6 +1255,7 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) struct sst_hsw_ipc_stream_info_reply *reply; u32 header; int ret; + size_t rx_bytes = sizeof(*reply); reply = &hsw->mixer_info; header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO); @@ -1260,7 +1263,7 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) trace_ipc_request("get global mixer info", 0); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, - reply, sizeof(*reply)); + reply, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "error: get stream info failed\n"); return ret; @@ -1282,7 +1285,7 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, if (wait) return sst_ipc_tx_message_wait(&hsw->ipc, header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); else return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0); } @@ -1412,7 +1415,7 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config, - sizeof(config), NULL, 0); + sizeof(config), NULL, NULL); if (ret < 0) dev_err(hsw->dev, "error: set device formats failed\n"); @@ -1426,6 +1429,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, { u32 header, state_; int ret, item; + size_t rx_bytes = sizeof(*dx); header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); state_ = state; @@ -1433,7 +1437,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, trace_ipc_request("PM enter Dx state", state); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_, - sizeof(state_), dx, sizeof(*dx)); + sizeof(state_), dx, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); return ret; @@ -1948,7 +1952,7 @@ int sst_hsw_module_enable(struct sst_hsw *hsw, config.map.module_entries[0].entry_point); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, - &config, sizeof(config), NULL, 0); + &config, sizeof(config), NULL, NULL); if (ret < 0) dev_err(dev, "ipc: module enable failed - %d\n", ret); else @@ -1986,7 +1990,7 @@ int sst_hsw_module_disable(struct sst_hsw *hsw, IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | IPC_MODULE_ID(module_id); - ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, NULL); if (ret < 0) dev_err(dev, "module disable failed - %d\n", ret); else @@ -2039,7 +2043,7 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw, parameter->data_size = param_size; ret = sst_ipc_tx_message_wait(&hsw->ipc, header, - parameter, transfer_parameter_size , NULL, 0); + parameter, transfer_parameter_size, NULL, NULL); if (ret < 0) dev_err(dev, "ipc: module set parameter failed - %d\n", ret); diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8a20f86544d2..5cc274a8d531 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -231,10 +231,12 @@ static ssize_t mod_control_write(struct file *file, if (mbsz) retval = skl_ipc_get_large_config(&ctx->ipc, &msg, - large_data, &(mod_set_get->mailbx[0]), mbsz); + large_data, &(mod_set_get->mailbx[0]), + mbsz, NULL); else retval = skl_ipc_get_large_config(&ctx->ipc, - &msg, large_data, NULL, 0); + &msg, large_data, NULL, + 0, NULL); d->ipc_data[0] = msg.param_data_size; memcpy(&d->ipc_data[1], large_data, msg.param_data_size); @@ -256,11 +258,11 @@ static ssize_t mod_control_write(struct file *file, default: if (mbsz) retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, - mod_set_get->mailbx, mbsz, NULL, 0); + mod_set_get->mailbx, mbsz, NULL, NULL); else retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); d->ipc_data[0] = 0; break; @@ -717,10 +719,12 @@ static ssize_t adsp_control_write(struct file *file, if (tx_param == 1) skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, &tx_data, sizeof(u32)); + ipc_data, &tx_data, + sizeof(u32), NULL); else skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, NULL, 0); + ipc_data, NULL, + 0, NULL); memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 79451737be9e..4a0ad2916388 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2347,5 +2347,6 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, msg.param_data_size = size; msg.large_param_id = param_id; - return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, 0); + return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, + 0, NULL); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 7b6ce92c9d0e..bf9f97bb36de 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -676,7 +676,7 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, header.extension = IPC_PPL_LP_MODE(lp_mode); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret); return ret; @@ -698,7 +698,7 @@ int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) header.primary |= IPC_INSTANCE_ID(instance_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret); return ret; @@ -722,7 +722,7 @@ int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc, header.primary |= IPC_PPL_STATE(state); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret); return ret; @@ -745,7 +745,7 @@ skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id) header.extension = IPC_DMA_ID(dma_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret); return ret; @@ -767,7 +767,7 @@ int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) header.primary |= IPC_INSTANCE_ID(instance_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret); return ret; @@ -793,7 +793,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - dx, sizeof(*dx), NULL, 0); + dx, sizeof(*dx), NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); return ret; @@ -819,7 +819,7 @@ int skl_ipc_delete_instance(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, - msg->param_data_size, NULL, 0); + msg->param_data_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: delete instance failed\n"); @@ -857,7 +857,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data, - msg->param_data_size, NULL, 0); + msg->param_data_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: init instance failed\n"); @@ -889,7 +889,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary, header.extension); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: bind/unbind failed\n"); return ret; @@ -917,8 +917,8 @@ int skl_ipc_load_modules(struct sst_generic_ipc *ipc, header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS); header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); - ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, data, - (sizeof(u16) * module_cnt)); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, + (sizeof(u16) * module_cnt), NULL, NULL); if (ret < 0) dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret); @@ -939,7 +939,7 @@ int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt, header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, - (sizeof(u16) * module_cnt), NULL, 0); + (sizeof(u16) * module_cnt), NULL, NULL); if (ret < 0) dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret); @@ -980,7 +980,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, (unsigned)data_offset, (unsigned)tx_size); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, ((char *)param) + data_offset, - tx_size, NULL, 0); + tx_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set large config fail, err: %d\n", ret); @@ -1003,7 +1003,7 @@ EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param, - u32 *txparam, u32 size) + u32 *txparam, u32 tx_bytes, size_t *rx_bytes) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); @@ -1040,8 +1040,8 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, (unsigned)data_offset, (unsigned)rx_size); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - ((char *)txparam), size, ((char *)param) + data_offset, - rx_size); + ((char *)txparam), tx_bytes, + ((char *)param) + data_offset, &rx_size); if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); @@ -1077,7 +1077,7 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, if (wait) ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); else ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, NULL, 0); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 6c037d2c8a3c..bc02740e5525 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -259,7 +259,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param, - u32 *txparam, u32 size); + u32 *txparam, u32 tx_bytes, size_t *rx_bytes); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait); From e9e0071cb5a77ecdf23bfcc9333082dd01ad0cbb Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 16 Mar 2017 21:50:51 +0530 Subject: [PATCH 0137/1276] ASoC: Intel: Skylake: Extract the receive response size returned by the FW The driver uses the rx_bytes value returned from firmware to copy the response of the ipc. Change-Id: I76b78c01ef83b28d6328de66249af84b4c99700f Signed-off-by: Dharageswari R Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index bf9f97bb36de..b3591c8de471 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -475,6 +475,10 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, if (reply == IPC_GLB_REPLY_SUCCESS) { dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary); /* copy the rx data from the mailbox */ + if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary) == + IPC_MOD_LARGE_CONFIG_GET) + msg->rx_size = header.extension & + IPC_DATA_OFFSET_SZ_MASK; sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size); switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { case IPC_GLB_LOAD_MULTIPLE_MODS: @@ -1039,15 +1043,27 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "receiving offset: %#x, size: %#x\n", (unsigned)data_offset, (unsigned)rx_size); + if (rx_bytes != NULL) + *rx_bytes = rx_size; + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, ((char *)txparam), tx_bytes, - ((char *)param) + data_offset, &rx_size); + ((char *)param) + data_offset, rx_bytes); + if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); return ret; } + /* exit as this is the final block */ + if (header.extension | (0 << IPC_FINAL_BLOCK_SHIFT)) + break; + + if (rx_bytes != NULL) + rx_size = *rx_bytes; + sz_remaining -= rx_size; + data_offset = msg->param_data_size - sz_remaining; /* clear the fields */ From 181909ac86e7ff95efd438dbb6b880ffe4abc66f Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 7 Feb 2017 18:42:40 +0530 Subject: [PATCH 0138/1276] ASoC: Intel: Skylake: Querying FW CONFIG information FW Config information is queried from the firmware. This information is useful to set the behavior of the driver. [Ex: Memory reclaim] Change-Id: Idba891a1db4f61bdcce26120974409ffc484770c Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.h | 18 +++++++++++++++++ sound/soc/intel/skylake/skl-sst-utils.c | 26 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 18 ----------------- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index e874e75a15fc..ea6e57e6899f 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -262,4 +262,5 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, unsigned int hdr_offset, int index); void skl_release_library(struct skl_lib_info *linfo, int lib_count); +int skl_get_firmware_configuration(struct sst_dsp *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc02740e5525..074014668f0a 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -39,6 +39,24 @@ enum skl_ipc_pipeline_state { PPL_RESTORED = 7 }; +enum base_fw_run_time_param { + ADSP_PROPERTIES = 0, + ADSP_RESOURCE_STATE = 1, + NOTIFICATION_MASK = 3, + ASTATE_TABLE = 4, + DMA_CONTROL = 5, + ENABLE_LOGS = 6, + FIRMWARE_CONFIG = 7, + HARDWARE_CONFIG = 8, + MODULES_INFO = 9, + PIPELINE_LIST_INFO = 10, + PIPELINE_PROPS = 11, + SCHEDULERS_INFO = 12, + GATEWAYS_INFO = 13, + MEMORY_STATE_INFO = 14, + POWER_STATE_INFO = 15 +}; + struct skl_ipc_dxstate_info { u32 core_mask; u32 dx_mask; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 2ae405617876..59fb24bc0adc 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -446,3 +446,29 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count) } } } + +int skl_get_firmware_configuration(struct sst_dsp *ctx) +{ + struct skl_ipc_large_config_msg msg; + struct skl_sst *skl = ctx->thread_context; + u8 *ipc_data; + int ret = 0; + size_t rx_bytes; + + ipc_data = kzalloc(DSP_BUF, GFP_KERNEL); + if (!ipc_data) + return -ENOMEM; + + msg.module_id = 0; + msg.instance_id = 0; + msg.large_param_id = FIRMWARE_CONFIG; + msg.param_data_size = DSP_BUF; + + ret = skl_ipc_get_large_config(&skl->ipc, &msg, + (u32 *)ipc_data, NULL, 0, &rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to get fw configuration !!!\n"); + + kfree(ipc_data); + return ret; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 00265245beee..676a769374ae 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -514,24 +514,6 @@ struct mod_set_get { u32 mailbx[1024]; }; -enum base_fw_run_time_param { - ADSP_PROPERTIES = 0, - ADSP_RESOURCE_STATE = 1, - NOTIFICATION_MASK = 3, - ASTATE_TABLE = 4, - DMA_CONTROL = 5, - ENABLE_LOGS = 6, - FIRMWARE_CONFIG = 7, - HARDWARE_CONFIG = 8, - MODULES_INFO = 9, - PIPELINE_LIST_INFO = 10, - PIPELINE_PROPS = 11, - SCHEDULERS_INFO = 12, - GATEWAYS_INFO = 13, - MEMORY_STATE_INFO = 14, - POWER_STATE_INFO = 15 -}; - struct fw_ipc_data { u32 replysz; u32 adsp_id; From 5c39adbbf8d22f5a6188a0140750fe06321f7621 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Mon, 6 Feb 2017 22:16:04 +0530 Subject: [PATCH 0139/1276] ASoC: Intel: Skylake: Parse the fw property The value returned by the FW CONFIG IPC is in the form of a TLV. It contains information about fw version, memory reclaim, mailbox size etc., Parse the fw property, it could be used during runtime operations. Change-Id: Ie366c59b8ba103f38e8a8e67e1aa679123e77c05 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 5 + sound/soc/intel/skylake/skl-sst-dsp.h | 32 ++++ sound/soc/intel/skylake/skl-sst-ipc.h | 58 +++++++ sound/soc/intel/skylake/skl-sst-utils.c | 195 +++++++++++++++++++++++- 4 files changed, 289 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 4a0ad2916388..e0d6f74a8fd5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1158,6 +1158,8 @@ int skl_free_dsp(struct skl *skl) { struct hdac_bus *bus = skl_to_bus(skl); struct skl_sst *ctx = skl->skl_sst; + struct skl_fw_property_info fw_property = skl->skl_sst->fw_property; + struct skl_scheduler_config sch_config = fw_property.scheduler_config; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(bus, false); @@ -1170,6 +1172,9 @@ int skl_free_dsp(struct skl *skl) if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); + kfree(fw_property.dma_config); + kfree(sch_config.sys_tick_cfg); + return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ea6e57e6899f..df37765858c4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -127,8 +127,40 @@ struct skl_lib_info; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) +/* Header size is in number of bytes */ +#define SKL_TLV_HEADER_SIZE 8 +struct skl_tlv_message { + u32 type; + u32 length; + char data[0]; +} __packed; + #define DSP_BUF PAGE_SIZE +enum skl_fw_info_type { + SKL_FW_VERSION = 0, + SKL_MEMORY_RECLAIMED, + SKL_SLOW_CLOCK_FREQ_HZ, + SKL_FAST_CLOCK_FREQ_HZ, + SKL_DMA_BUFFER_CONFIG, + SKL_ALH_SUPPORT_LEVEL, + SKL_IPC_DL_MAILBOX_BYTES, + SKL_IPC_UL_MAILBOX_BYTES, + SKL_TRACE_LOG_BYTES, + SKL_MAX_PPL_COUNT, + SKL_MAX_ASTATE_COUNT, + SKL_MAX_MODULE_PIN_COUNT, + SKL_MODULES_COUNT, + SKL_MAX_MOD_INST_COUNT, + SKL_MAX_LL_TASKS_PER_PRI_COUNT, + SKL_LL_PRI_COUNT, + SKL_MAX_DP_TASKS_COUNT, + SKL_MAX_LIBS_COUNT, + SKL_SCHEDULER_CONFIG, + SKL_XTAL_FREQ_HZ, + SKL_CLOCKS_CONFIG, +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 074014668f0a..7700b5c54934 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -133,6 +133,61 @@ struct bra_conf { struct skl_pipe *cp_pipe; }; +struct skl_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + +struct skl_dma_buff_config { + u32 min_size_bytes; + u32 max_size_bytes; +}; + +enum skl_alh_support_level { + ALH_NO_SUPPORT = 0x00000, + ALH_CAVS_1_8_CNL = 0x10000, +}; + +struct skl_clk_config { + u32 clock_source; + u32 clock_param_mask; +}; + +struct skl_scheduler_config { + u32 sys_tick_multiplier; + u32 sys_tick_divider; + u32 sys_tick_source; + u32 sys_tick_cfg_length; + u32 *sys_tick_cfg; +}; + +struct skl_fw_property_info { + struct skl_fw_version version; + u32 memory_reclaimed; + u32 slow_clock_freq_hz; + u32 fast_clock_freq_hz; + enum skl_alh_support_level alh_support; + u32 ipc_dl_mailbox_bytes; + u32 ipc_ul_mailbox_bytes; + u32 trace_log_bytes; + u32 max_ppl_count; + u32 max_astate_count; + u32 max_module_pin_count; + u32 modules_count; + u32 max_mod_inst_count; + u32 max_ll_tasks_per_pri_count; + u32 ll_pri_count; + u32 max_dp_tasks_count; + u32 max_libs_count; + u32 xtal_freq_hz; + struct skl_clk_config clk_config; + struct skl_scheduler_config scheduler_config; + u32 num_dma_cfg; + struct skl_dma_buff_config *dma_config; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -189,6 +244,9 @@ struct skl_sst { /* BRA configuration data */ struct bra_conf *bra_pipe_data; + + /* firmware configuration information */ + struct skl_fw_property_info fw_property; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 59fb24bc0adc..2bd5565d5f80 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -446,6 +446,192 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count) } } } +static int skl_fill_sch_cfg( + struct skl_fw_property_info *fw_property, + u32 *src) +{ + struct skl_scheduler_config *sch_config = + &(fw_property->scheduler_config); + + sch_config->sys_tick_multiplier = *src; + sch_config->sys_tick_divider = *(src + 1); + sch_config->sys_tick_source = *(src + 2); + sch_config->sys_tick_cfg_length = *(src + 3); + + if (sch_config->sys_tick_cfg_length > 0) { + sch_config->sys_tick_cfg = + kcalloc(sch_config->sys_tick_cfg_length, + sizeof(*sch_config->sys_tick_cfg), + GFP_KERNEL); + + if (!sch_config->sys_tick_cfg) + return -ENOMEM; + + memcpy(sch_config->sys_tick_cfg, + src + 4, + sch_config->sys_tick_cfg_length * + sizeof(*sch_config->sys_tick_cfg)); + } + return 0; +} + +static int skl_fill_dma_cfg(struct skl_tlv_message *message, + struct skl_fw_property_info *fw_property, u32 *src) +{ + struct skl_dma_buff_config dma_buff_cfg; + + fw_property->num_dma_cfg = message->length / + sizeof(dma_buff_cfg); + + if (fw_property->num_dma_cfg > 0) { + fw_property->dma_config = + kcalloc(fw_property->num_dma_cfg, + sizeof(dma_buff_cfg), + GFP_KERNEL); + + if (!fw_property->dma_config) + return -ENOMEM; + + memcpy(fw_property->dma_config, src, + message->length); + } + return 0; +} + +static int skl_parse_fw_config_info(struct sst_dsp *ctx, + u8 *src, int limit) +{ + struct skl_tlv_message *message; + int offset = 0, shift, ret = 0; + u32 *value; + struct skl_sst *skl = ctx->thread_context; + struct skl_fw_property_info *fw_property = &skl->fw_property; + enum skl_fw_info_type type; + struct skl_scheduler_config *sch_config = + &fw_property->scheduler_config; + + while (offset < limit) { + + message = (struct skl_tlv_message *)src; + if (message == NULL) + break; + + /* Skip TLV header to read value */ + src += sizeof(*message); + + value = (u32 *)src; + type = message->type; + + switch (type) { + case SKL_FW_VERSION: + memcpy(&fw_property->version, value, + sizeof(fw_property->version)); + break; + + case SKL_MEMORY_RECLAIMED: + fw_property->memory_reclaimed = *value; + break; + + case SKL_SLOW_CLOCK_FREQ_HZ: + fw_property->slow_clock_freq_hz = *value; + break; + + case SKL_FAST_CLOCK_FREQ_HZ: + fw_property->fast_clock_freq_hz = *value; + break; + + case SKL_DMA_BUFFER_CONFIG: + ret = skl_fill_dma_cfg(message, fw_property, value); + if (ret < 0) + goto err; + break; + + case SKL_ALH_SUPPORT_LEVEL: + fw_property->alh_support = *value; + break; + + case SKL_IPC_DL_MAILBOX_BYTES: + fw_property->ipc_dl_mailbox_bytes = *value; + break; + + case SKL_IPC_UL_MAILBOX_BYTES: + fw_property->ipc_ul_mailbox_bytes = *value; + break; + + case SKL_TRACE_LOG_BYTES: + fw_property->trace_log_bytes = *value; + break; + + case SKL_MAX_PPL_COUNT: + fw_property->max_ppl_count = *value; + break; + + case SKL_MAX_ASTATE_COUNT: + fw_property->max_astate_count = *value; + break; + + case SKL_MAX_MODULE_PIN_COUNT: + fw_property->max_module_pin_count = *value; + break; + + case SKL_MODULES_COUNT: + fw_property->modules_count = *value; + break; + + case SKL_MAX_MOD_INST_COUNT: + fw_property->max_mod_inst_count = *value; + break; + + case SKL_MAX_LL_TASKS_PER_PRI_COUNT: + fw_property->max_ll_tasks_per_pri_count = *value; + break; + + case SKL_LL_PRI_COUNT: + fw_property->ll_pri_count = *value; + break; + + case SKL_MAX_DP_TASKS_COUNT: + fw_property->max_dp_tasks_count = *value; + break; + + case SKL_MAX_LIBS_COUNT: + fw_property->max_libs_count = *value; + break; + + case SKL_SCHEDULER_CONFIG: + ret = skl_fill_sch_cfg(fw_property, value); + if (ret < 0) + goto err; + break; + + case SKL_XTAL_FREQ_HZ: + fw_property->xtal_freq_hz = *value; + break; + + case SKL_CLOCKS_CONFIG: + memcpy(&(fw_property->clk_config), value, + message->length); + break; + + default: + dev_err(ctx->dev, "Invalid fw info type:%d !!\n", + type); + break; + } + + shift = message->length + sizeof(*message); + offset += shift; + /* skip over to next tlv data */ + src += message->length; + } +err: + if (ret < 0) { + kfree(fw_property->dma_config); + kfree(sch_config->sys_tick_cfg); + } + + return ret; +} int skl_get_firmware_configuration(struct sst_dsp *ctx) { @@ -466,9 +652,16 @@ int skl_get_firmware_configuration(struct sst_dsp *ctx) ret = skl_ipc_get_large_config(&skl->ipc, &msg, (u32 *)ipc_data, NULL, 0, &rx_bytes); - if (ret < 0) + if (ret < 0) { dev_err(ctx->dev, "failed to get fw configuration !!!\n"); + goto err; + } + + ret = skl_parse_fw_config_info(ctx, ipc_data, rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to parse configuration !!!\n"); +err: kfree(ipc_data); return ret; } From a58ced6733646f0bd1f2436aaaf947d7de69dca1 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Mon, 6 Feb 2017 22:24:18 +0530 Subject: [PATCH 0140/1276] ASoC: Intel: Skylake: Check for memory reclaim bit Memory reclaim bit indicates whether legacy DMA memory is managed by FW. It would be set by firmware if DMA is to be used. Check for the memory reclaimed bit during the probe. If the bit is not set then fail the probe. Change-Id: I8a926ea2b4f86b1f7d66749d2e9809f23308c51c Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/cnl-sst.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 790af2489d0e..d5e2c32b3f83 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -209,8 +209,10 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) { struct firmware stripped_fw; struct skl_sst *cnl = ctx->thread_context; + struct skl_fw_property_info fw_property; int ret; + fw_property.memory_reclaimed = -1; if (!ctx->fw) { ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { @@ -255,6 +257,21 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) cnl->fw_loaded = true; + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "fwconfig ipc failed !\n"); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + + fw_property = cnl->fw_property; + if (fw_property.memory_reclaimed <= 0) { + dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", + fw_property.memory_reclaimed); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + return 0; cnl_load_base_firmware_failed: From 9f68b12b859200729196d21cb645551a31c30fd9 Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Thu, 11 Feb 2016 20:02:14 +0530 Subject: [PATCH 0141/1276] ASoC: Intel: Skylake: Better handling of stream interrupts There are storm of interrupts while audio is running. It seems like we have level triggered irq for audio. We need to disable the source of IRQ and re-enable them after we handle them. So here we are disabling them in main irq handler and re-enable them in bottom half. Change-Id: I482dacb83f61229783bfe0c61721b73f47ee4703 Tracked-On: Signed-off-by: Jayachandran B Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index dbff367fb3d0..d3fd3dd5f882 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -216,16 +216,18 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; u32 status; + u32 mask, int_enable; + int ret = IRQ_NONE; if (!pm_runtime_active(bus->dev)) - return IRQ_NONE; + return ret; spin_lock(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); if (status == 0 || status == 0xffffffff) { spin_unlock(&bus->reg_lock); - return IRQ_NONE; + return ret; } /* clear rirb int */ @@ -236,20 +238,41 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); } + mask = (0x1 << ebus->num_streams) - 1; + + status = snd_hdac_chip_readl(bus, INTSTS); + status &= mask; + if (status) { + /* Disable stream interrupts; Re-enable in bottom half */ + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); + ret = IRQ_WAKE_THREAD; + } else + ret = IRQ_HANDLED; + spin_unlock(&bus->reg_lock); + return ret; - return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; } static irqreturn_t skl_threaded_handler(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; u32 status; + u32 int_enable; + u32 mask; + unsigned long flags; status = snd_hdac_chip_readl(bus, INTSTS); snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update); + /* Re-enable stream interrupts */ + mask = (0x1 << ebus->num_streams) - 1; + spin_lock_irqsave(&bus->reg_lock, flags); + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); + spin_unlock_irqrestore(&bus->reg_lock, flags); return IRQ_HANDLED; } From ed60e7fbb9eb6dd193f6170a9d38e18fc325ccfd Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 6 Dec 2016 15:22:43 +0530 Subject: [PATCH 0142/1276] [REVERTME] ASoC: Intel: Skylake: Set DUM bit in EM2 register HW recommends that we set DUM bit so that DPIB write request will occur every frame regardless whether DPIB has changed or not. Change-Id: I84933645bef2a22ce4758b29e6f618f2ac37a8e9 Signed-off-by: Senthilnathan Veppur Signed-off-by: Dharageswari R Signed-off-by: GuruprasadX Pawse Signed-off-by: Hardik T Shah Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Kp, Jeeja Tested-by: Sangaraju, KarthikeyaX Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 29 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 1 + 2 files changed, 30 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index d3fd3dd5f882..97e8367eed30 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -201,6 +201,33 @@ static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) hstr->curr_pos += no_of_bytes; } +/* + * skl_dum_set - Set the DUM bit in EM2 register to fix the IP bug + * of incorrect postion reporting for capture stream. + */ +static void skl_dum_set(struct hdac_ext_bus *ebus) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + u32 reg; + u8 val; + + /* + * For the DUM bit to be set, CRST needs to be out of reset state + */ + val = snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET; + if (!val) { + skl_enable_miscbdcge(bus->dev, false); + snd_hdac_bus_exit_link_reset(bus); + skl_enable_miscbdcge(bus->dev, true); + } + /* + * Set the DUM bit in EM2 register to fix the IP bug of incorrect + * postion reporting for capture stream. + */ + reg = snd_hdac_chip_readl(bus, VS_EM2); + snd_hdac_chip_writel(bus, VS_EM2, (reg | AZX_EM2_DUM_MASK)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -927,6 +954,8 @@ static int skl_first_init(struct hdac_bus *bus) /* initialize chip */ skl_init_pci(skl); + skl_dum_set(ebus); + return skl_init_chip(bus, true); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 2bafc7d40cec..10a414d77cb2 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -44,6 +44,7 @@ #define DMA_CLK_CONTROLS 1 #define DMA_TRANSMITION_START 2 #define DMA_TRANSMITION_STOP 3 +#define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) From 34da7c51226753003797a5d13e97bcbaa45125a2 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Wed, 15 Feb 2017 17:04:40 +0530 Subject: [PATCH 0143/1276] ASoC: Intel: Skylake: Add D0i3 support for Icelake platform The driver needs two DSP callback, one to set D0i0 (active) and D0i3 (low-power) states. Add these callbacks in icelake dsp ops. Change-Id: Ibd077dd4bef1a617a9d86f2c2e639f47ce6b5f25 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/bxt-sst.c | 6 +++--- sound/soc/intel/skylake/cnl-sst.c | 7 +++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 6f9b24c8c211..c1f6d8b6ab79 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -302,7 +302,7 @@ static int bxt_d0i3_target_state(struct sst_dsp *ctx) return SKL_DSP_D0I3_NONE; } -static void bxt_set_dsp_D0i3(struct work_struct *work) +void bxt_set_dsp_D0i3(struct work_struct *work) { int ret; struct skl_ipc_d0ix_msg msg; @@ -347,7 +347,7 @@ static void bxt_set_dsp_D0i3(struct work_struct *work) skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; } -static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) +int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) { struct skl_sst *skl = ctx->thread_context; struct skl_d0i3_data *d0i3 = &skl->d0i3; @@ -364,7 +364,7 @@ static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) return 0; } -static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) +int bxt_set_dsp_D0i0(struct sst_dsp *ctx) { int ret; struct skl_ipc_d0ix_msg msg; diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index d5e2c32b3f83..5ee7b350eb7f 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -389,6 +389,8 @@ static unsigned int cnl_get_errno(struct sst_dsp *ctx) static const struct skl_dsp_fw_ops cnl_fw_ops = { .set_state_D0 = cnl_set_dsp_D0, .set_state_D3 = cnl_set_dsp_D3, + .set_state_D0i3 = bxt_schedule_dsp_D0i3, + .set_state_D0i0 = bxt_set_dsp_D0i0, .load_fw = cnl_load_base_firmware, .get_fw_errcode = cnl_get_errno, .load_library = bxt_load_library, @@ -743,9 +745,14 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + /* set the D0i3 check */ + cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); + INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); + cnl->d0i3.state = SKL_DSP_D0I3_NONE; + ret = skl_dsp_acquire_irq(sst); if (ret < 0) return ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index df37765858c4..58c0c13c15aa 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -295,4 +295,10 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); + +int bxt_set_dsp_D0i0(struct sst_dsp *ctx); + +int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); + +void bxt_set_dsp_D0i3(struct work_struct *work); #endif /*__SKL_SST_DSP_H__*/ From 241403c0a4f0750726b5111d889e26b90a5fc00d Mon Sep 17 00:00:00 2001 From: Praveen Diwakar Date: Wed, 8 Mar 2017 20:12:57 +0530 Subject: [PATCH 0144/1276] ASoC: Intel: Skylake: Audio format mismatch detection This patch detects mismatch in audio format for source and destination modules. It prints warning to inform a mismatch occured in given path. Change-Id: Ic9fe0bbde2e2487d3ec25cbd3723ebaab81b395a Signed-off-by: Praveen Diwakar Signed-off-by: Rahul Patil Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index e0d6f74a8fd5..f4ebdce7446d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2086,6 +2086,70 @@ static void fill_pin_params(struct skl_audio_data_format *pin_fmt, #define CPR_SINK_FMT_PARAM_ID 2 +static struct +skl_module_fmt *skl_get_pin_format(struct skl_module_cfg *mconfig, + u8 pin_direction, u8 pin_idx) +{ + struct skl_module *module = mconfig->module; + int fmt_idx = mconfig->fmt_idx; + struct skl_module_iface *intf; + struct skl_module_fmt *pin_fmt; + + intf = &module->formats[fmt_idx]; + + if (pin_direction == SKL_INPUT_PIN) + pin_fmt = &intf->inputs[pin_idx].fmt; + else + pin_fmt = &intf->outputs[pin_idx].fmt; + + return pin_fmt; +} + +/* + * This function checks for source module and destination module format + * mismatch + */ +static void skl_module_format_mismatch_detection(struct skl_sst *ctx, + struct skl_module_cfg *src_mcfg, + struct skl_module_cfg *dst_mcfg, + int src_index, int dst_index) +{ + struct skl_module_fmt *src_fmt, *dst_fmt; + + src_fmt = skl_get_pin_format(src_mcfg, SKL_OUTPUT_PIN, src_index); + dst_fmt = skl_get_pin_format(dst_mcfg, SKL_INPUT_PIN, dst_index); + + if(memcmp(src_fmt, dst_fmt, sizeof(*src_fmt))) { + dev_warn(ctx->dev, "#### src and dst format mismatch: ####\n"); + dev_warn(ctx->dev, "pipe=%d src module_id=%d src instance_id=%d\n", + src_mcfg->pipe->ppl_id, + src_mcfg->id.module_id, + src_mcfg->id.pvt_id); + + dev_warn(ctx->dev, "pipe=%d dst module_id=%d dst instance_id=%d\n", + dst_mcfg->pipe->ppl_id, + dst_mcfg->id.module_id, + dst_mcfg->id.pvt_id); + + dev_warn(ctx->dev, "channels: src=%d dst=%d\n", + src_fmt->channels, dst_fmt->channels); + dev_warn(ctx->dev, "s_freq: src=%d dst=%d\n", + src_fmt->s_freq, dst_fmt->s_freq); + dev_warn(ctx->dev, "bit_depth: src=%d dst=%d\n", + src_fmt->bit_depth, dst_fmt->bit_depth); + dev_warn(ctx->dev, "valid_bit_depth: src=%d dst=%d\n", + src_fmt->valid_bit_depth, dst_fmt->valid_bit_depth); + dev_warn(ctx->dev, "ch_cfg: src=%d dst=%d\n", + src_fmt->ch_cfg, dst_fmt->ch_cfg); + dev_warn(ctx->dev, "interleaving_style: src=%d dst=%d\n", + src_fmt->interleaving_style, dst_fmt->interleaving_style); + dev_warn(ctx->dev, "sample_type: src=%d dst=%d\n", + src_fmt->sample_type, dst_fmt->sample_type); + dev_warn(ctx->dev, "ch_map: src=%d dst=%d\n", + src_fmt->ch_map, dst_fmt->ch_map); + } +} + /* * Once a module is instantiated it need to be 'bind' with other modules in * the pipeline. For binding we need to find the module pins which are bind @@ -2152,6 +2216,9 @@ int skl_bind_modules(struct skl_sst *ctx, dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n", msg.src_queue, msg.dst_queue); + skl_module_format_mismatch_detection(ctx, src_mcfg, dst_mcfg, + src_index, dst_index); + msg.module_id = src_mcfg->id.module_id; msg.instance_id = src_mcfg->id.pvt_id; msg.dst_module_id = dst_mcfg->id.module_id; From 6244bd50489a94033ad498b83344390963a64b36 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Mon, 30 Jan 2017 20:12:52 +0530 Subject: [PATCH 0145/1276] ASoC: Intel: Skylake: add sysfs files for firmware modules This patch adds sysfs files for firmware modules. Below is the structure of sysfs files created: /sys/bus/pci/devices//dsp/modules/ |---id: module id |---hash: module hash |---loaded: module state loaded/unloaded Change-Id: Ia097a3cc1409a33b2a82b1d1cdc634fb4b0eee90 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Prodduvaka, Leoni Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 1 + sound/soc/intel/skylake/skl-pcm.c | 3 + sound/soc/intel/skylake/skl-sst-dsp.h | 7 + sound/soc/intel/skylake/skl-sst-ipc.h | 3 + sound/soc/intel/skylake/skl-sst-utils.c | 222 +++++++++++++++++++++++- 5 files changed, 235 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f4ebdce7446d..54bd83eb55b5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1164,6 +1164,7 @@ int skl_free_dsp(struct skl *skl) /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(bus, false); + skl_module_sysfs_exit(skl->skl_sst); ctx->dsp_ops->cleanup(bus->dev, ctx); kfree(ctx->cores.state); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 265992ec11ff..361b85bfa792 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1954,6 +1954,9 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) } skl_get_probe_widget(component, skl); + + /* create sysfs to list modules downloaded by driver */ + skl_module_sysfs_init(skl->skl_sst, &component->dev->kobj); } pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 58c0c13c15aa..7be2cdeb85b6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -137,6 +137,8 @@ struct skl_tlv_message { #define DSP_BUF PAGE_SIZE +#define DEFAULT_HASH_SHA256_LEN 32 + enum skl_fw_info_type { SKL_FW_VERSION = 0, SKL_MEMORY_RECLAIMED, @@ -218,6 +220,7 @@ struct uuid_module { int *instance_id; struct list_head list; + u8 hash[DEFAULT_HASH_SHA256_LEN]; }; struct skl_load_module_info { @@ -301,4 +304,8 @@ int bxt_set_dsp_D0i0(struct sst_dsp *ctx); int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); void bxt_set_dsp_D0i3(struct work_struct *work); + +int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *fw_modules_kobj); + +void skl_module_sysfs_exit(struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 7700b5c54934..f3b8d7bc68ec 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -247,6 +247,9 @@ struct skl_sst { /* firmware configuration information */ struct skl_fw_property_info fw_property; + + /* sysfs for module info */ + struct skl_sysfs_tree *sysfs_tree; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 2bd5565d5f80..0eaa88e6ae61 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -23,11 +23,35 @@ #define UUID_STR_SIZE 37 -#define DEFAULT_HASH_SHA256_LEN 32 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 +#define UUID_ATTR_RO(_name) \ + struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) + +struct skl_sysfs_tree { + struct kobject *dsp_kobj; + struct kobject *modules_kobj; + struct skl_sysfs_module **mod_obj; +}; + +struct skl_sysfs_module { + struct kobject kobj; + struct uuid_module *uuid_mod; + struct list_head *module_list; + int fw_ops_load_mod; +}; + +struct uuid_attribute { + struct attribute attr; + ssize_t (*show)(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf); + ssize_t (*store)(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, const char *buf, + size_t count); +}; + struct UUID { u8 id[16]; }; @@ -319,6 +343,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, ret = -ENOMEM; goto free_uuid_list; } + memcpy(&module->hash, mod_entry->hash1, sizeof(module->hash)); list_add_tail(&module->list, &skl->uuid_list); @@ -665,3 +690,198 @@ int skl_get_firmware_configuration(struct sst_dsp *ctx) kfree(ipc_data); return ret; } + +static ssize_t uuid_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uuid_attribute *uuid_attr = + container_of(attr, struct uuid_attribute, attr); + struct skl_sysfs_module *modinfo_obj = + container_of(kobj, struct skl_sysfs_module, kobj); + + if (uuid_attr->show) + return uuid_attr->show(modinfo_obj, uuid_attr, buf); + + return 0; +} + +static const struct sysfs_ops uuid_sysfs_ops = { + .show = uuid_attr_show, +}; + +static void uuid_release(struct kobject *kobj) +{ + struct skl_sysfs_module *modinfo_obj = + container_of(kobj, struct skl_sysfs_module, kobj); + + kfree(modinfo_obj); +} + +static struct kobj_type uuid_ktype = { + .release = uuid_release, + .sysfs_ops = &uuid_sysfs_ops, +}; + +static ssize_t loaded_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + struct skl_module_table *module_list; + + if ((!modinfo_obj->fw_ops_load_mod) || + (modinfo_obj->fw_ops_load_mod && + !modinfo_obj->uuid_mod->is_loadable)) + return sprintf(buf, "%d\n", true); + + if (list_empty(modinfo_obj->module_list)) + return sprintf(buf, "%d\n", false); + + list_for_each_entry(module_list, modinfo_obj->module_list, list) { + if (module_list->mod_info->mod_id + == modinfo_obj->uuid_mod->id) + return sprintf(buf, "%d\n", module_list->usage_cnt); + } + + return sprintf(buf, "%d\n", false); +} + +static ssize_t hash_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + int ret = 0; + int i; + + for (i = 0; i < DEFAULT_HASH_SHA256_LEN; i++) + ret += sprintf(buf + ret, "%d ", + modinfo_obj->uuid_mod->hash[i]); + ret += sprintf(buf + ret, "\n"); + + return ret; +} + + +static ssize_t id_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", modinfo_obj->uuid_mod->id); +} + +static UUID_ATTR_RO(loaded); +static UUID_ATTR_RO(hash); +static UUID_ATTR_RO(id); + +static struct attribute *modules_attrs[] = { + &uuid_attr_loaded.attr, + &uuid_attr_hash.attr, + &uuid_attr_id.attr, + NULL, +}; + +static const struct attribute_group uuid_group = { + .attrs = modules_attrs, +}; + +static void free_uuid_node(struct kobject *kobj, + const struct attribute_group *group) +{ + if (kobj) { + sysfs_remove_group(kobj, group); + kobject_put(kobj); + } +} + +void skl_module_sysfs_exit(struct skl_sst *ctx) +{ + struct skl_sysfs_tree *tree = ctx->sysfs_tree; + struct skl_sysfs_module **m; + + if (!tree) + return; + + if (tree->mod_obj) { + for (m = tree->mod_obj; *m; m++) + free_uuid_node(&(*m)->kobj, &uuid_group); + kfree(tree->mod_obj); + } + + if (tree->modules_kobj) + kobject_put(tree->modules_kobj); + + if (tree->dsp_kobj) + kobject_put(tree->dsp_kobj); + + kfree(tree); + ctx->sysfs_tree = NULL; +} +EXPORT_SYMBOL_GPL(skl_module_sysfs_exit); + +int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) +{ + struct uuid_module *module; + struct skl_sysfs_module *modinfo_obj; + char *uuid_name; + int count = 0; + int max_mod = 0; + int ret = 0; + + if (list_empty(&ctx->uuid_list)) + return 0; + + ctx->sysfs_tree = kzalloc(sizeof(*ctx->sysfs_tree), GFP_KERNEL); + if (!ctx->sysfs_tree) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + ctx->sysfs_tree->dsp_kobj = kobject_create_and_add("dsp", kobj); + if (!ctx->sysfs_tree->dsp_kobj) + goto err_sysfs_exit; + + ctx->sysfs_tree->modules_kobj = kobject_create_and_add("modules", + ctx->sysfs_tree->dsp_kobj); + if (!ctx->sysfs_tree->modules_kobj) + goto err_sysfs_exit; + + list_for_each_entry(module, &ctx->uuid_list, list) + max_mod++; + + ctx->sysfs_tree->mod_obj = kcalloc(max_mod + 1, + sizeof(*ctx->sysfs_tree->mod_obj), GFP_KERNEL); + if (!ctx->sysfs_tree->mod_obj) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + list_for_each_entry(module, &ctx->uuid_list, list) { + modinfo_obj = kzalloc(sizeof(*modinfo_obj), GFP_KERNEL); + if (!modinfo_obj) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + uuid_name = kasprintf(GFP_KERNEL, "%pUL", &module->uuid); + ret = kobject_init_and_add(&modinfo_obj->kobj, &uuid_ktype, + ctx->sysfs_tree->modules_kobj, uuid_name); + if (ret < 0) + goto err_sysfs_exit; + + ret = sysfs_create_group(&modinfo_obj->kobj, &uuid_group); + if (ret < 0) + goto err_sysfs_exit; + + modinfo_obj->uuid_mod = module; + modinfo_obj->module_list = &ctx->dsp->module_list; + modinfo_obj->fw_ops_load_mod = + (ctx->dsp->fw_ops.load_mod == NULL) ? 0 : 1; + + ctx->sysfs_tree->mod_obj[count] = modinfo_obj; + count++; + } + + return 0; + +err_sysfs_exit: + skl_module_sysfs_exit(ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_module_sysfs_init); From 7c008deb5a1c700c171b0b7126476c31c984938d Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 27 Apr 2017 14:32:17 +0530 Subject: [PATCH 0146/1276] ASoC: Intel: Skylake: Append SDW device to device type enum Append the SDW device type enum to the device type enum instead of putting it in between. Change-Id: Idc6e7a19569bfcb672047311767804177ceb5c4d Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index fc4e4324f94b..e5656fc0ca02 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -108,9 +108,10 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_SDW_PCM = 0x6, - SKL_DEVICE_SDW_PDM = 0x7, - SKL_DEVICE_NONE + SKL_DEVICE_NONE = 0x6, + SKL_DEVICE_SDW_PCM = 0x7, + SKL_DEVICE_SDW_PDM = 0x8, + SKL_DEVICE_MAX = SKL_DEVICE_SDW_PDM, }; /** From 546280552bb2cee92182310b41df9df2fb868ee9 Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Wed, 27 Jan 2016 09:30:16 +0530 Subject: [PATCH 0147/1276] ASoC: Intel: Skylake: Debugfs for core power handling This Debugfs allows to test core power handling. To turn on a core: echo wake n > core_power To turn off a core: echo sleep n > core_power where, n is the core id (0 ... num cores on audio IP - 1) Note that when core 0 is turned on/off using this debug i/f, pm_runtime_get_sync/pm_runtime_put is called. Hence this debug i/f can be used for runtime PM unit tests without having to run usecases to invoke runtime PM. Change-Id: Id63fc95d99ed6ed78eccfba134f204fdd2f07629 Signed-off-by: Jayachandran B Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 5cc274a8d531..2041e3387c03 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -764,6 +766,73 @@ static int skl_init_adsp(struct skl_debug *d) return 0; } +static ssize_t core_power_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + struct skl_sst *skl_ctx = d->skl->skl_sst; + struct sst_dsp *ctx = skl_ctx->dsp; + char buf[16]; + int len = min(count, (sizeof(buf) - 1)); + unsigned int core_id; + char *ptr; + int wake; + int err; + + + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = 0; + + /* + * The buffer content should be "wake n" or "sleep n", + * where n is the core id + */ + ptr = strnstr(buf, "wake", len); + if (ptr) { + ptr = ptr + 5; + wake = 1; + } else { + ptr = strnstr(buf, "sleep", len); + if (ptr) { + ptr = ptr + 6; + wake = 0; + } else + return -EINVAL; + } + + err = kstrtouint(ptr, 10, &core_id); + if (err) { + dev_err(d->dev, "%s: Debugfs kstrtouint returned error = %d\n", + __func__, err); + return err; + } + + dev_info(d->dev, "Debugfs: %s %d\n", wake ? "wake" : "sleep", core_id); + + if (wake) { + if (core_id == SKL_DSP_CORE0_ID) + pm_runtime_get_sync(d->dev); + else + skl_dsp_get_core(ctx, core_id); + } else { + if (core_id == SKL_DSP_CORE0_ID) + pm_runtime_put_sync(d->dev); + else + skl_dsp_put_core(ctx, core_id); + } + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + return len; +} +static const struct file_operations core_power_fops = { + .open = simple_open, + .write = core_power_write, + .llseek = default_llseek, +}; + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -797,6 +866,12 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) goto err; } + if (!debugfs_create_file("core_power", 0644, d->fs, d, + &core_power_fops)) { + dev_err(d->dev, "core power debugfs init failed\n"); + goto err; + } + /* now create the NHLT dir */ d->nhlt = debugfs_create_dir("nhlt", d->fs); if (IS_ERR(d->nhlt) || !d->nhlt) { From 162f0f09287fe03ae50ca9f386487b97d75b5e23 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 25 Feb 2016 12:06:18 +0530 Subject: [PATCH 0148/1276] ASoC: Intel: Skylake: DebugFs changes to suit FDK Following changes have been made in the generic ipc debugfs interface to better suit the FDK implementation 1. Return IO error on write operation if the ipc fails. 2. IPC read operations are exposed in binary format Change-Id: If0254fdb91030c917e0c0501089214d0654d39c7 Signed-off-by: Pardha Saradhi K Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 67 +++++++---------------------- 1 file changed, 16 insertions(+), 51 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 2041e3387c03..587fcac48ec0 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -151,37 +151,11 @@ static ssize_t mod_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct skl_debug *d = file->private_data; - char *state; - char *buf1; - int ret; - unsigned int ofs = 0; - - if (d->ipc_data[0] == 0) { - state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; - return simple_read_from_buffer(user_buf, count, ppos, - state, strlen(state)); - } - - state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; - buf1 = kzalloc(MOD_BUF1, GFP_KERNEL); - if (!buf1) - return -ENOMEM; - - ret = snprintf(buf1, MOD_BUF1, - "%s\nLARGE PARAM DATA\n", state); - - for (ofs = 0 ; ofs < d->ipc_data[0] ; ofs += 16) { - ret += snprintf(buf1 + ret, MOD_BUF1 - ret, "0x%.4x : ", ofs); - hex_dump_to_buffer(&(d->ipc_data[1]) + ofs, 16, 16, 4, - buf1 + ret, MOD_BUF1 - ret, 0); - ret += strlen(buf1 + ret); - if (MOD_BUF1 - ret > 0) - buf1[ret++] = '\n'; - } + const u32 param_data_size = d->ipc_data[0]; + const u32 *param_data = &d->ipc_data[1]; - ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); - kfree(buf1); - return ret; + return simple_read_from_buffer(user_buf, count, ppos, + param_data, param_data_size); } @@ -194,14 +168,14 @@ static ssize_t mod_control_write(struct file *file, int retval, type; ssize_t written; u32 size, mbsz; - u32 *large_data; - int large_param_size; struct skl_sst *ctx = d->skl->skl_sst; struct skl_ipc_large_config_msg msg; struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); + d->ipc_data[0] = 0; + buf = kzalloc(MOD_BUF, GFP_KERNEL); written = simple_write_to_buffer(buf, MOD_BUF, ppos, user_buf, count); @@ -225,28 +199,22 @@ static ssize_t mod_control_write(struct file *file, msg.instance_id = ((header.primary) & 0x00ff0000)>>16; msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; msg.param_data_size = (header.extension) & 0x000fffff; - large_param_size = msg.param_data_size; - - large_data = kzalloc(large_param_size, GFP_KERNEL); - if (!large_data) - return -ENOMEM; if (mbsz) retval = skl_ipc_get_large_config(&ctx->ipc, &msg, - large_data, &(mod_set_get->mailbx[0]), - mbsz, NULL); + &d->ipc_data[1], + &mod_set_get->mailbx[0], + mbsz, NULL); else retval = skl_ipc_get_large_config(&ctx->ipc, - &msg, large_data, NULL, - 0, NULL); - - d->ipc_data[0] = msg.param_data_size; - memcpy(&d->ipc_data[1], large_data, msg.param_data_size); - kfree(large_data); + &msg, + &d->ipc_data[1], + NULL, 0, NULL); + if (retval == 0) + d->ipc_data[0] = msg.param_data_size; break; case IPC_MOD_LARGE_CONFIG_SET: - d->ipc_data[0] = 0; msg.module_id = (header.primary) & 0x0000ffff; msg.instance_id = ((header.primary) & 0x00ff0000)>>16; msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; @@ -254,7 +222,6 @@ static ssize_t mod_control_write(struct file *file, retval = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)(&mod_set_get->mailbx)); - d->ipc_data[0] = 0; break; default: @@ -266,14 +233,12 @@ static ssize_t mod_control_write(struct file *file, retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, NULL, 0, NULL, NULL); - d->ipc_data[0] = 0; break; } + if (retval) - d->skl->mod_set_get_status = 1; - else - d->skl->mod_set_get_status = 0; + return -EIO; /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); From 24b205261a32be534cba89ebf282445fbf98dfaa Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 24 Feb 2016 17:04:30 +0530 Subject: [PATCH 0149/1276] ASoC: Intel: Skylake: Support Pipeline Properties IPC Add support to query te properties of any pipeline, that is running in te ADSP. This ipc involves sending the pipe id for which properties is sought for. This IPC is supported in a TLV format and so the associated changes in the framework are done. Typical syntax from a debugfs standpoint is echo 11, > adsp_prop_ctrl Change-Id: I69f0c5b0a6bbe93587ba6981b0f76e423fb97be8 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 587fcac48ec0..8f6d11d49dd6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -34,6 +34,8 @@ #define IPC_MOD_LARGE_CONFIG_GET 3 #define IPC_MOD_LARGE_CONFIG_SET 4 #define MOD_BUF1 (3 * PAGE_SIZE) +#define MAX_TLV_PAYLOAD_SIZE 4088 +#define EXTENDED_PARAMS_SZ 2 #define DEFAULT_SZ 100 #define DEFAULT_ID 0XFF @@ -43,6 +45,7 @@ #define HARDWARE_CONFIG_SZ 0x84 #define MODULES_INFO_SZ 0xa70 #define PIPELINE_LIST_INFO_SZ 0xc +#define PIPELINE_PROPS_SZ 0x60 #define SCHEDULERS_INFO_SZ 0x34 #define GATEWAYS_INFO_SZ 0x4e4 #define MEMORY_STATE_INFO_SZ 0x1000 @@ -585,7 +588,7 @@ static ssize_t adsp_control_write(struct file *file, struct skl_sst *ctx = d->skl->skl_sst; struct skl_ipc_large_config_msg msg; char id[8]; - u32 tx_data; + u32 tx_data[EXTENDED_PARAMS_SZ]; int j = 0, bufsize, tx_param = 0, tx_param_id; int len = min(count, (sizeof(buf)-1)); @@ -622,7 +625,8 @@ static ssize_t adsp_control_write(struct file *file, if (err) return -EINVAL; - tx_data = (tx_param_id << 8) | dsp_property; + tx_data[0] = (tx_param_id << 8) | dsp_property; + tx_data[1] = MAX_TLV_PAYLOAD_SIZE; } ipc_data = kzalloc(DSP_BUF, GFP_ATOMIC); @@ -657,6 +661,10 @@ static ssize_t adsp_control_write(struct file *file, replysz = PIPELINE_LIST_INFO_SZ; break; + case PIPELINE_PROPS: + replysz = PIPELINE_PROPS_SZ; + break; + case SCHEDULERS_INFO: replysz = SCHEDULERS_INFO_SZ; break; @@ -681,13 +689,18 @@ static ssize_t adsp_control_write(struct file *file, msg.module_id = 0x0; msg.instance_id = 0x0; - msg.large_param_id = dsp_property; + + if (tx_param == 1) + msg.large_param_id = 0xFF; + else + msg.large_param_id = dsp_property; + msg.param_data_size = replysz; if (tx_param == 1) skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, &tx_data, - sizeof(u32), NULL); + ipc_data, tx_data, + EXTENDED_PARAMS_SZ*sizeof(u32), NULL); else skl_ipc_get_large_config(&ctx->ipc, &msg, ipc_data, NULL, From 7bffd586b20313d6618c341b0a8ba9395217d431 Mon Sep 17 00:00:00 2001 From: Senthilnathan Veppur Date: Wed, 23 Mar 2016 16:46:08 +0530 Subject: [PATCH 0150/1276] ASoC: Intel: Skylake: Add check for buffer overflow Return error if source buffer would overflow in strncpy. Change-Id: I8637aa3fed40dd5f042cb041cc5a447506bc15d2 Signed-off-by: Senthilnathan Veppur Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8f6d11d49dd6..6dc4ef3d7e8e 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -603,6 +603,10 @@ static ssize_t adsp_control_write(struct file *file, while (buf[j] != '\0') { if (buf[j] == ',') { + if ((bufsize-j) > sizeof(id)) { + dev_err(d->dev, "ID buffer overflow\n"); + return -EINVAL; + } strncpy(id, &buf[j+1], (bufsize-j)); buf[j] = '\0'; tx_param = 1; From 5e4f0805b02e7db40db5546a22a9d8e975318eb9 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Fri, 9 Oct 2015 01:31:44 +0530 Subject: [PATCH 0151/1276] ASoC: Intel: Skylake: Increase the SSP count in debugFS Some of Broxton SKUs has 6 SSP ports, hence support them in debugFS. Change-Id: I0b8fdf9feed0a4484789f750134d7734e805e5bc Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 6dc4ef3d7e8e..07b3309bf4ee 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -29,7 +29,7 @@ #define MOD_BUF (2 * PAGE_SIZE) #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 -#define MAX_SSP 4 +#define MAX_SSP 6 #define MAX_SZ 1025 #define IPC_MOD_LARGE_CONFIG_GET 3 #define IPC_MOD_LARGE_CONFIG_SET 4 From a29022e3727bdd96efb26bf4d47b86c68c00483a Mon Sep 17 00:00:00 2001 From: "Mallikarjun, chippalkatti" Date: Mon, 24 Apr 2017 14:43:23 +0530 Subject: [PATCH 0152/1276] ASoC: Intel: CNL: Retrieve module id from GUID This patch retrieves module id from GUID for copier module used for SoundWire BRA feature. Change-Id: Ib9453f929e4b0280535a2151851ac04c5098c806 Signed-off-by: Mallikarjun, chippalkatti Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: audio_build Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-messages.c | 14 ++++---------- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-topology.c | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 54bd83eb55b5..2c0cf7a96128 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -385,12 +385,9 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_params.s_fmt = 32; host_cpr_params.linktype = 0; host_cpr_params.stream = 0; + host_cpr_cfg.id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg.guid); -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - host_cpr_cfg.id.module_id = 3; -#else - host_cpr_cfg.id.module_id = 4; -#endif host_cpr_cfg.id.instance_id = 1; host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); @@ -617,12 +614,9 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; + host_cpr_cfg.id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg.guid); -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.id.module_id = 3; -#else - link_cpr_cfg.id.module_id = 4; -#endif link_cpr_cfg.id.instance_id = 3; link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 7be2cdeb85b6..402fb875272c 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -278,6 +278,7 @@ int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_co int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, unsigned int offset, int index); +int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid_mod); int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id); int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id); int skl_get_pvt_instance_id_map(struct skl_sst *ctx, diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 711629143aa3..2dfb454f7007 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1040,7 +1040,7 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, return 0; } -static int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) +int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) { struct uuid_module *module; @@ -1051,6 +1051,7 @@ static int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) return -EINVAL; } +EXPORT_SYMBOL_GPL(skl_get_module_id); static int skl_tplg_find_moduleid_from_uuid(struct skl *skl, const struct snd_kcontrol_new *k) From b26dea9f10d8b206487a6e68b95e2fa6e765bbaa Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 24 Mar 2017 03:40:36 +0530 Subject: [PATCH 0153/1276] ASoc: rt700: Remove runtime get and put from set bias Earlier runtime get and put was added while setting bias level to make sure the clock to the codec is ON. Since clock will be ON while setting bias level, get and put can be removed from set bias level API. Change-Id: Ic8fe49a5dfda5a2f1d5bf49db8a669e0297d10b1 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 8fc9c00e15b8..c88aa16a4280 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1115,7 +1115,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { - pm_runtime_get_sync(&rt700->sdw->mstr->dev); snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); } @@ -1124,7 +1123,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_STANDBY: snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); break; default: From f0f3997ee9b8c9ad9802cc0d38bb440468c58bb7 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 12 Apr 2017 16:07:43 +0530 Subject: [PATCH 0154/1276] ASoC: Intel: board: Add SSP0 codec-codec link Change-Id: I111f0405411125a8176aa6e9c8295f72059ab2e2 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 21b40b895629..3615c79e5bc5 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -60,6 +60,14 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -286,6 +294,20 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, + /* codec-codec link */ + { + .name = "CNL SSP0-Loop Port", + .stream_name = "CNL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, }; static int From bec80a313170de6873db6e1de887228365d003c6 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 4 Apr 2017 08:34:51 +0530 Subject: [PATCH 0155/1276] ASoC: Intel: board: Move cnl_rt274 clock setting to supply widget During BE-BE loop, codec clocks were not set as it was a part of dai link ops hw_params and no sound was heard due to this reason when use cases involve BE-BE loop. So, move codec clock setting as a part of supply widget and define routes appropriately. Also use macro to define BE rate fixup and use it for both fixup as well as clock computation. Change-Id: Id5a08d2bd6024a61b601dbbe70ad99a52149da5e Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Kale, Sanyog R Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 89 +++++++++++++++++++----------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 3615c79e5bc5..4b434dc82b9f 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,6 +35,56 @@ #include "../../codecs/rt274.h" +#define CNL_FREQ_OUT 19200000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + CNL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + CNL_BE_FIXUP_RATE * ratio, + CNL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + static struct snd_soc_jack cnl_headset; /* Headset jack detection DAPM pins */ @@ -58,6 +108,9 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_pcm_stream dai_params_codec = { @@ -102,6 +155,9 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"ssp0 Rx", NULL, "AIF1 Capture"}, {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -140,7 +196,7 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = 48000; + rate->min = rate->max = CNL_BE_FIXUP_RATE; channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -149,36 +205,6 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -#define CNL_FREQ_OUT 19200000 - -static int rt274_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret, ratio = 100; - - snd_soc_dai_set_bclk_ratio(codec_dai, ratio); - - ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, - ratio * params_rate(params), CNL_FREQ_OUT); - if (ret != 0) { - dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); - if (ret < 0) - dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); - - return ret; -} - -static struct snd_soc_ops rt274_ops = { - .hw_params = rt274_hw_params, -}; - #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; @@ -268,7 +294,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = cnl_rt274_init, - .ops = &rt274_ops, }, { .name = "SSP1-Codec", From 0c8e1750d1fcd15474c5030d42497bdaa5436394 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 21 Apr 2017 12:21:17 +0530 Subject: [PATCH 0156/1276] SoundWire: Select default frame shape based on platform This patch selects default frame shape in SoundWire controller init based on FPGA/NONFPGA platform. Change-Id: I07cb3f578367ef3afb4ea1e0db905d562d134f8f Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Tested-by: Avati, Santosh Kumar --- drivers/sdw/sdw_cnl_priv.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 504df88d681a..b7f44e1f9d6f 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -28,12 +28,13 @@ #define SDW_CNL_CMD_WORD_LEN 4 #define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 #define SDW_CNL_DEFAULT_CLK_DIVIDER 0 -#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x48 #else #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x176F +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 #endif #define SDW_CNL_PORT_REG_OFFSET 0x80 From 1fa59ca922dde506b8b9dbe99a9e375f0cf2df67 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 23:02:20 +0530 Subject: [PATCH 0157/1276] ASoC: rt700: Added support for ICL FPGA SDW Aggregation Adding RT700 codec slave id connected to Master 1 used for aggregation and the master and slave id combination more general across different platforms Change-Id: If2552c5b56a7508179263791ccd996703ccf64c7 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700-sdw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 1c935e3a05e2..8a7fbc5a40c7 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -348,15 +348,10 @@ static const struct sdw_slv_id rt700_id[] = { {"15:02:5d:07:01:00", 0}, {"16:02:5d:07:01:00", 0}, {"17:02:5d:07:01:00", 0}, -#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA -#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 - {"10:02:5d:07:00:01", 0}, -#else {"10:02:5d:07:00:01", 1}, + {"10:02:5d:07:01:01", 1}, {"10:02:5d:07:01:02", 2}, {"10:02:5d:07:01:03", 3}, -#endif -#endif {} }; From a7fb2396f6a2bde9aacf5bf8a7aee1688a1fca8e Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 23:02:32 +0530 Subject: [PATCH 0158/1276] ASoC: Intel: Boards: Add support for ICL FPGA SDW Aggregation Aggregation is performed on ICL FPGA with Master 1 and Master2 hence added slave id Change-Id: Ic6506769242c099582b7435ca2bd338d3a7ff919 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kp, Jeeja Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Jayanti, Satya Charitardha Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt700.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index ee8fe3934571..dcb02b08a91c 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -168,8 +168,12 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const char cname[] = "sdw-slave0-10:02:5d:07:01:00"; #else +static const char cname[] = "sdw-slave1-10:02:5d:07:01:01"; +#endif +#else static const char pname[] = "0000:00:1f.3"; static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif From 4023bfe9fd9fc10e5dd76b75162b89bd77b4a90f Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 22:57:51 +0530 Subject: [PATCH 0159/1276] ASoC: Intel: CNL: Add support for ICL FPGA SDW Aggregation Added Master 1 as the skl_platform_dai id for icl fpga sdw aggregation Change-Id: Ia21d008dddad0ca0928b7903d6760e0da0433b04 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Jayanti, Satya Charitardha Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 361b85bfa792..c1ddf5ce1852 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1321,7 +1321,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW Pin", #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif #else .id = SDW_BE_DAI_ID_MSTR1, #endif @@ -1347,7 +1351,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW10 Pin", #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif #else .id = SDW_BE_DAI_ID_MSTR1, #endif From 6e0a9f17550f1ede9c0618c203c9b604caa5ab97 Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Wed, 28 Jun 2017 16:42:00 +0530 Subject: [PATCH 0160/1276] ASoC: Intel: Skylake: Add support for getting hw config from DSP This patch gets hw config from DSP by sending hw config ipc. Signed-off-by: Diwakar, Praveen --- sound/soc/intel/skylake/skl-sst-dsp.h | 14 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 41 +++++++++ sound/soc/intel/skylake/skl-sst-utils.c | 111 ++++++++++++++++++++++++ 3 files changed, 166 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 402fb875272c..baec42299419 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -163,6 +163,19 @@ enum skl_fw_info_type { SKL_CLOCKS_CONFIG, }; +enum skl_hw_info_type { + SKL_CAVS_VERSION = 0, + SKL_DSP_CORES, + SKL_MEM_PAGE_TYPES, + SKL_TOTAL_PHYS_MEM_PAGES, + SKL_I2S_CAPS, + SKL_GPDMA_CAPS, + SKL_GATEWAY_COUNT, + SKL_HB_EBB_COUNT, + SKL_LP_EBB_COUNT, + SKL_EBB_SIZE_BYTES, +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -299,6 +312,7 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); +int skl_get_hardware_configuration(struct sst_dsp *ctx); int bxt_set_dsp_D0i0(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f3b8d7bc68ec..43d3eedad780 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -188,6 +188,44 @@ struct skl_fw_property_info { struct skl_dma_buff_config *dma_config; }; +enum skl_cavs_version { + CAVS_VER_NA = 0x0, + CAVS_VER_1_5 = 0x1005, + CAVS_VER_1_8 = 0x1008 +}; + +enum skl_i2s_version { + I2S_VER_15_SKYLAKE = 0x00000, + I2S_VER_15_BROXTON = 0x10000, + I2S_VER_15_BROXTON_P = 0x20000 +}; + +struct skl_i2s_capabilities { + enum skl_i2s_version version; + u32 controller_count; + u32 *controller_base_addr; +}; + +struct skl_gpdma_capabilities { + u32 lp_ctrl_count; + u32 *lp_ch_count; + u32 hp_ctrl_count; + u32 *hp_ch_count; +}; + +struct skl_hw_property_info { + enum skl_cavs_version cavs_version; + u32 dsp_cores; + u32 mem_page_bytes; + u32 total_phys_mem_pages; + struct skl_i2s_capabilities i2s_caps; + struct skl_gpdma_capabilities gpdma_caps; + u32 gateway_count; + u32 hb_ebb_count; + u32 lp_ebb_count; + u32 ebb_size_bytes; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -248,6 +286,9 @@ struct skl_sst { /* firmware configuration information */ struct skl_fw_property_info fw_property; + /* hardware configuration information */ + struct skl_hw_property_info hw_property; + /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; }; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 0eaa88e6ae61..7288893ce61a 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -359,6 +359,117 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, return ret; } +static int skl_parse_hw_config_info(struct sst_dsp *ctx, u8 *src, int limit) +{ + struct skl_tlv_message *message; + int offset = 0, shift; + u32 *value; + struct skl_sst *skl = ctx->thread_context; + struct skl_hw_property_info *hw_property = &skl->hw_property; + enum skl_hw_info_type type; + + while (offset < limit) { + + message = (struct skl_tlv_message *)src; + if (message == NULL) + break; + + /* Skip TLV header to read value */ + src += sizeof(struct skl_tlv_message); + + value = (u32 *)src; + type = message->type; + + switch (type) { + case SKL_CAVS_VERSION: + hw_property->cavs_version = *value; + break; + + case SKL_DSP_CORES: + hw_property->dsp_cores = *value; + break; + + case SKL_MEM_PAGE_TYPES: + hw_property->mem_page_bytes = *value; + break; + + case SKL_TOTAL_PHYS_MEM_PAGES: + hw_property->total_phys_mem_pages = *value; + break; + + case SKL_I2S_CAPS: + memcpy(&hw_property->i2s_caps, value, + sizeof(hw_property->i2s_caps)); + break; + + case SKL_GPDMA_CAPS: + memcpy(&hw_property->gpdma_caps, value, + sizeof(hw_property->gpdma_caps)); + break; + + case SKL_GATEWAY_COUNT: + hw_property->gateway_count = *value; + break; + + case SKL_HB_EBB_COUNT: + hw_property->hb_ebb_count = *value; + break; + + case SKL_LP_EBB_COUNT: + hw_property->lp_ebb_count = *value; + break; + + case SKL_EBB_SIZE_BYTES: + hw_property->ebb_size_bytes = *value; + break; + + default: + dev_err(ctx->dev, "Invalid hw info type:%d \n", type); + break; + } + + shift = message->length + sizeof(*message); + offset += shift; + /* skip over to next tlv data */ + src += message->length; + } + + return 0; +} + +int skl_get_hardware_configuration(struct sst_dsp *ctx) +{ + struct skl_ipc_large_config_msg msg; + struct skl_sst *skl = ctx->thread_context; + u8 *ipc_data; + int ret = 0; + size_t rx_bytes; + + ipc_data = kzalloc(DSP_BUF, GFP_KERNEL); + if (!ipc_data) + return -ENOMEM; + + msg.module_id = 0; + msg.instance_id = 0; + msg.large_param_id = HARDWARE_CONFIG; + msg.param_data_size = DSP_BUF; + + ret = skl_ipc_get_large_config(&skl->ipc, &msg, + (u32 *)ipc_data, NULL, 0, &rx_bytes); + if (ret < 0) { + dev_err(ctx->dev, "failed to get hw configuration !!!\n"); + goto err; + } + + ret = skl_parse_hw_config_info(ctx, ipc_data, rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to parse configuration !!!\n"); + +err: + kfree(ipc_data); + return ret; +} + void skl_freeup_uuid_list(struct skl_sst *ctx) { struct uuid_module *uuid, *_uuid; From 368dead68e5f28e38685583731309f5e8f43131f Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Wed, 28 Jun 2017 16:49:23 +0530 Subject: [PATCH 0161/1276] ASoC: Intel: Skylake: Get dsp core count from hw config ipc Instead of getting dsp core count from hard coded value, use hw config ipc to reterive the same. Signed-off-by: Diwakar, Praveen --- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 5ee7b350eb7f..9441a724ea59 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -272,6 +272,16 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) goto cnl_load_base_firmware_failed; } + ret = skl_get_hardware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "hwconfig ipc failed !\n"); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + + /* Update dsp core count retrieved from hw config IPC */ + cnl->cores.count = cnl->hw_property.dsp_cores; + return 0; cnl_load_base_firmware_failed: From 1380c42c7b3ba6f7db9c5118efc33fc7e0ca4942 Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Wed, 3 May 2017 01:20:41 +0530 Subject: [PATCH 0162/1276] ASoC: Intel: Skylake: Add user notification event for pipe creation/deletion This patch provides kernel event generation on pipeline creation/deletion and also update the debugfs file with event timestamp. Userspace gets the event for each pipe creation and deletion and can read the event timestamp from the debugfs. Change-Id: I6e015e5f3e5285ecad215a37dfe286a4e3dc3435 Signed-off-by: Guneshwor Singh Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-debug.c | 80 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-messages.c | 2 + sound/soc/intel/skylake/skl.h | 4 ++ 3 files changed, 86 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 07b3309bf4ee..f3c5e29afaa6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -56,6 +56,11 @@ struct nhlt_blob { struct nhlt_specific_cfg *cfg; }; +struct skl_pipe_event_data { + long event_time; + int event_type; +}; + struct skl_debug { struct skl *skl; struct device *dev; @@ -68,6 +73,7 @@ struct skl_debug { struct nhlt_blob dmic_blob; u32 ipc_data[MAX_SZ]; struct fw_ipc_data fw_ipc_data; + struct skl_pipe_event_data data; }; struct nhlt_specific_cfg @@ -815,9 +821,76 @@ static const struct file_operations core_power_fops = { .llseek = default_llseek, }; +void skl_dbg_event(struct skl_sst *ctx, int type) +{ + int retval; + struct timeval pipe_event_tv; + struct skl *skl = get_skl_ctx(ctx->dev); + struct kobject *kobj; + + kobj = &skl->component->dev->kobj; + + if (type == SKL_PIPE_CREATED) + /* pipe creation event */ + retval = kobject_uevent(kobj, KOBJ_ADD); + else if (type == SKL_PIPE_INVALID) + /* pipe deletion event */ + retval = kobject_uevent(kobj, KOBJ_REMOVE); + else + return; + + if (retval < 0) { + dev_err(ctx->dev, + "pipeline uevent failed, ret = %d\n", retval); + return; + } + + do_gettimeofday(&pipe_event_tv); + + skl->debugfs->data.event_time = pipe_event_tv.tv_usec; + skl->debugfs->data.event_type = type; +} + +static ssize_t skl_dbg_event_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[32]; + char pipe_state[24]; + int retval; + + if (d->data.event_type) + strcpy(pipe_state, "SKL_PIPE_CREATED"); + else + strcpy(pipe_state, "SKL_PIPE_INVALID"); + + retval = snprintf(buf, sizeof(buf), "%s - %ld\n", + pipe_state, d->data.event_time); + + return simple_read_from_buffer(user_buf, count, ppos, buf, retval); +} + +static const struct file_operations skl_dbg_event_fops = { + .open = simple_open, + .read = skl_dbg_event_read, + .llseek = default_llseek, +}; + +static int skl_init_dbg_event(struct skl_debug *d) +{ + if (!debugfs_create_file("dbg_event", 0644, d->fs, d, + &skl_dbg_event_fops)) { + dev_err(d->dev, "dbg_event debugfs file creation failed\n"); + return -EIO; + } + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; + int ret; d = devm_kzalloc(&skl->pci->dev, sizeof(*d), GFP_KERNEL); if (!d) @@ -865,6 +938,13 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) skl_init_adsp(d); skl_init_mod_set_get(d); + ret = skl_init_dbg_event(d); + if (ret < 0) { + dev_err(&skl->pci->dev, + "dbg_event debugfs init failed, ret = %d\n", ret); + goto err; + } + return d; err: diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 2c0cf7a96128..31384afe42b3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2265,6 +2265,7 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_CREATED; + skl_dbg_event(ctx, pipe->state); return 0; } @@ -2303,6 +2304,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_INVALID; + skl_dbg_event(ctx, pipe->state); return ret; } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 10a414d77cb2..66f34ceae74e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -170,6 +170,7 @@ void skl_debug_init_module(struct skl_debug *d, struct nhlt_specific_cfg *skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, u8 stream); +void skl_dbg_event(struct skl_sst *ctx, int type); #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -185,6 +186,9 @@ static inline struct nhlt_specific_cfg { return NULL; } +void skl_dbg_event(struct skl_sst *ctx, int type) +{ +} #endif #endif /* __SOUND_SOC_SKL_H */ From b1b918abc0123bed1b4b76a42e794c9a604d8b24 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 18 May 2017 21:56:06 +0530 Subject: [PATCH 0163/1276] ASoC: Intel: Extract the "nhlt-version" from DSDT table This patch extracts the "nhlt-version" from the DSDT table present at /sys/firmware/acpi/tables/DSDT. Change-Id: Icf20d440ff8a2e9e5f1ae1aacd1f1e0991235672 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 15 +++++++++++++++ sound/soc/intel/skylake/skl-nhlt.h | 6 ++++++ sound/soc/intel/skylake/skl.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 01a050cf8775..b2873dd8cdf3 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -28,6 +28,21 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); +int skl_get_nhlt_version(struct device *dev) +{ + const char *version; + int ret; + + ret = device_property_read_string(dev, "nhlt-version", &version); + if (!ret) { + if (!strncmp(version, "1.8-0", strlen("1.8-0"))) + return VERSION_1; + else + return VERSION_INVALID; + } + /* if reading fails, assume we are on older platforms */ + return VERSION_0; +} struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h index fc17da503b4d..62550a75a9dc 100644 --- a/sound/soc/intel/skylake/skl-nhlt.h +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -59,6 +59,12 @@ enum nhlt_device_type { NHLT_DEVICE_INVALID }; +enum nhlt_version_type { + VERSION_INVALID = -1, + VERSION_0, + VERSION_1, +}; + struct nhlt_specific_cfg { u32 size; u8 caps[0]; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 66f34ceae74e..01d54513b029 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -82,6 +82,7 @@ struct skl { struct snd_soc_component *component; struct snd_soc_dai_driver *dais; + unsigned int nhlt_version; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -137,6 +138,7 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); +int skl_get_nhlt_version(struct device *dev); struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, From c4f0e146f47700f2da08dd674ff8f8b6ef1423dd Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 17 May 2017 12:05:30 +0530 Subject: [PATCH 0164/1276] ASoC: Intel: Skylake: Increase the max number of entries for resources, path configs and formats To support multi-format playback/capture, 0.7 xml has more number of entries for module resources, path configs and formats. This patch increases the max limit for module resources to 32, path config entries to 32 and module formats to 64. Although this is not a permanent solution. Right solution is to dynamically calculate the entries instead of hard coding. Change-Id: If052e6b95a69b9ed47f08ebe284383c4d2e8e81f Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: audio_build Reviewed-by: Babu, Ramesh Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: R, Dharageswari Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 676a769374ae..adf35fa0d75c 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -47,9 +47,9 @@ #define SKL_OUTPUT_PIN 0 #define SKL_INPUT_PIN 1 -#define SKL_MAX_PATH_CONFIGS 8 +#define SKL_MAX_PATH_CONFIGS 32 #define SKL_MAX_MODULES_IN_PIPE 8 -#define SKL_MAX_MODULE_FORMATS 32 +#define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 enum skl_channel_index { From 3efd96041a8ee2cf7c9163af7f0829daa8100a53 Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Tue, 25 Apr 2017 06:24:38 +0530 Subject: [PATCH 0165/1276] ASoC: Intel: Skylake: Add single module support in a given pipeline Single pipeline module will be modelled as PGA leaf. Decision to model it as PGA instead of mixer is taken because there may be pipeline with single module which has TLV controls associated with it. New handler for single module PGA event has been assigned. DAPM graph connectivity of single module will come from Pathconnector. Change-Id: Ie34420fb970553df5f370a66cd7bf817d6cbc0d2 Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prodduvaka, Leoni Reviewed-by: S, Pavan K Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 136 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 4 + 2 files changed, 140 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2dfb454f7007..ed5c6d585e33 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -60,6 +60,9 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { #define CHECK_HW_PARAMS(ch, freq, bps, prm_ch, prm_freq, prm_bps) \ ((ch == prm_ch) && (bps == prm_bps) && (freq == prm_freq)) +static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, + struct skl *skl); + void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) { struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; @@ -885,6 +888,17 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl_sst *ctx = skl->skl_sst; struct skl_module_deferred_bind *modules; + if (mconfig->pipe->state >= SKL_PIPE_CREATED) + return 0; + + /* + * This will check for single module in source pipeline. If single + * module pipeline exists then its going to create source pipeline + * first. This will handle/satisfy source-to-sink pipeline creation + * scenario for single module in any stream + */ + skl_init_single_module_pipe(w, skl); + ret = skl_tplg_get_pipe_config(skl, mconfig); if (ret < 0) return ret; @@ -947,6 +961,54 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +/* + * This function returns pipe order in given stream + */ +static int skl_get_pipe_order(struct skl_module_cfg *mcfg) +{ + struct skl_pipe *pipe = mcfg->pipe; + + switch (pipe->conn_type) { + case SKL_PIPE_CONN_TYPE_FE: + if (pipe->direction == SNDRV_PCM_STREAM_CAPTURE) + return SKL_LAST_PIPE; + else if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) + return SKL_FIRST_PIPE; + break; + case SKL_PIPE_CONN_TYPE_BE: + if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) + return SKL_LAST_PIPE; + else if (pipe->direction == SNDRV_PCM_STREAM_CAPTURE) + return SKL_FIRST_PIPE; + break; + } + return SKL_INTERMEDIATE_PIPE; +} + +/* + * This function checks for single module source pipeline. If found any then + * it will initialize source pipeline and its module + */ +static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *src_w = NULL; + struct skl_module_cfg *mcfg; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + src_w = p->source; + + if ((src_w->priv != NULL) && is_skl_dsp_widget_type(src_w, skl->skl_sst->dev)) { + mcfg = src_w->priv; + if ((list_is_singular(&mcfg->pipe->w_list)) && + (src_w->power_check(src_w))) + skl_tplg_mixer_dapm_pre_pmu_event(src_w, skl); + } + skl_init_single_module_pipe(src_w, skl); + } +} + static int skl_fill_sink_instance_id(struct skl_sst *ctx, u32 *params, int size, struct skl_module_cfg *mcfg) { @@ -1570,6 +1632,43 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } + +/* + * In modelling, we assumed that all single module will be PGA leaf. Have + * added new event flag POST_PMU. PRE_PMU is going to handle dynamic connection + * i.e (dynamic FE or BE connection to already running stream). POST_PMU will + * handle the pipeline binding and running from sink to source. POST_PMD + * will handle the cleanup of single module pipe. + */ +static int skl_tplg_pga_single_module_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_module_cfg *mcfg = w->priv; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + if ((skl_get_pipe_order(mcfg) == SKL_LAST_PIPE) && (ret == 0)) + ret = skl_tplg_mixer_dapm_post_pmu_event(w, skl); + return ret; + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + ret = skl_tplg_pga_dapm_post_pmd_event(w, skl); + if (ret >= 0) + ret = skl_tplg_mixer_dapm_post_pmd_event(w, skl); + return ret; + } + + return 0; +} + int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3979,6 +4078,40 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, return 0; } +/* + * This function updates the event flag and fucntiona handler for single module + */ +static void skl_update_single_module_event(struct skl *skl, + struct skl_pipe *pipe) +{ + struct skl_module_cfg *mcfg; + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mcfg = w->priv; + + if (list_is_singular(&pipe->w_list)) { + + /* + * If module pipe order is last then we dont need + * POST_PMU, as POST_PMU bind/run sink to source. + * For last pipe order there is no sink pipelne. + */ + if (skl_get_pipe_order(mcfg) == SKL_LAST_PIPE) + w->event_flags = SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD; + else + w->event_flags = SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD; + + w->event = skl_tplg_pga_single_module_event; + } + } +} + static struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, @@ -4093,5 +4226,8 @@ int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) list_for_each_entry(ppl, &skl->ppl_list, node) skl_tplg_set_pipe_type(skl, ppl->pipe); + list_for_each_entry(ppl, &skl->ppl_list, node) + skl_update_single_module_event(skl, ppl->pipe); + return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index adf35fa0d75c..0c6b5c6b9c99 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -28,6 +28,10 @@ #include #include "skl.h" +#define SKL_FIRST_PIPE 0 +#define SKL_LAST_PIPE 1 +#define SKL_INTERMEDIATE_PIPE 2 + #define BITS_PER_BYTE 8 #define MAX_TS_GROUPS 8 #define MAX_DMIC_TS_GROUPS 4 From 59532d46ac3ef923e21503458c875e2edf186d89 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 30 May 2017 19:37:19 +0530 Subject: [PATCH 0166/1276] ASoC: Intel: Skylake: Fix incorrect parsing of pipe tokens To avoid parsing of pipe related tokens multiple times for a case where the pipe has more than one module, a logic was added to parse these tokens only once. But, the existing logic would parse these tokens only if there are more than one module in the pipe. So, for a pipe with single module, the logic is insufficient. So, fix it by updating the flag accordingly. Change-Id: Ie183f14eaf98f21cf87691e0e681b77342706b37 Signed-off-by: Shreyas NC Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prodduvaka, Leoni Reviewed-by: S, Pavan K Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index ed5c6d585e33..6e7a192cc68c 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2935,6 +2935,7 @@ static int skl_tplg_get_token(struct device *dev, } return is_pipe_exists; } + is_pipe_exists = 0; break; @@ -2948,7 +2949,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PMODE: case SKL_TKN_U32_PIPE_DIRECTION: case SKL_TKN_U32_NUM_CONFIGS: - if (is_pipe_exists) { + if (!is_pipe_exists) { ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, tkn_elem->token, tkn_elem->value); if (ret < 0) From 542a0b8ca1bb9a938b6691d3e00b347a27196eac Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Tue, 6 Jun 2017 21:04:21 +0530 Subject: [PATCH 0167/1276] ASoC: Intel: Skylake: Create SSP BE dais dynamically This patch creates BE SSP dai dynamically, by getting SSP link information from NHLT. Change-Id: I2b6e45125a3fbd1e7f155efe86b5fb1a983c0f41 Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 36 ++++++ sound/soc/intel/skylake/skl-pcm.c | 195 ++++++++++------------------- sound/soc/intel/skylake/skl.h | 7 ++ 3 files changed, 111 insertions(+), 127 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b2873dd8cdf3..cf3d38136289 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -464,3 +464,39 @@ void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks) epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); } } + +static bool is_vbus_id_exist(struct skl *skl, int vbus_id) +{ + bool ret = false; + int i; + + for (i = 0; i < skl->nhlt->endpoint_count; i++) { + if (vbus_id == skl->grp_cnt.vbus_id[i]) + return true; + } + return ret; +} + +/* + * This function gets endpoint count and vbus_id for the specific link type + * passed as parameter. + */ +void skl_nhlt_get_ep_cnt(struct skl *skl, int link_type) +{ + struct nhlt_endpoint *epnt = (struct nhlt_endpoint *) skl->nhlt->desc; + int i; + + skl->grp_cnt.cnt = 0; + memset(skl->grp_cnt.vbus_id, 0xff, + (sizeof(int) * skl->nhlt->endpoint_count)); + + for (i = 0; i < skl->nhlt->endpoint_count; i++) { + + if (epnt->linktype == link_type) { + if (!is_vbus_id_exist(skl, epnt->virtual_bus_id)) + skl->grp_cnt.vbus_id[skl->grp_cnt.cnt++] = + epnt->virtual_bus_id; + } + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } +} diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index c1ddf5ce1852..96c912bafc87 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1117,126 +1117,6 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { /* BE cpu dais and compress dais*/ static struct snd_soc_dai_driver skl_platform_dai[] = { -{ - .name = "SSP0 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp0 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp0 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP1 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp1 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp1 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP2 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp2 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp2 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP3 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp3 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp3 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP4 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp4 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp4 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP5 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp5 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp5 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, { .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, @@ -1994,40 +1874,101 @@ static const struct snd_soc_component_driver skl_component = { .num_controls = ARRAY_SIZE(skl_controls), }; +static struct snd_soc_dai_driver ssp_dai_info = { + .ops = &skl_be_ssp_dai_ops, + .playback = { + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + int skl_platform_register(struct device *dev) { int ret; - struct snd_soc_dai_driver *dais; - int num_dais = ARRAY_SIZE(skl_platform_dai); struct hdac_bus *bus = dev_get_drvdata(dev); struct skl *skl = bus_to_skl(bus); + struct snd_soc_dai_driver *dais; + int num_dais = ARRAY_SIZE(skl_platform_dai); + int total_dais; + int i, index; INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); skl->dais = kmemdup(skl_platform_dai, sizeof(skl_platform_dai), GFP_KERNEL); + skl->grp_cnt.vbus_id = devm_kcalloc(dev, skl->nhlt->endpoint_count, + sizeof(int), GFP_KERNEL); + if (!skl->grp_cnt.vbus_id) + return -ENOMEM; + + skl_nhlt_get_ep_cnt(skl, NHLT_LINK_SSP); + + total_dais = num_dais + skl->grp_cnt.cnt; + + skl->dais = devm_kcalloc(dev, total_dais, sizeof(*dais), GFP_KERNEL); + if (!skl->dais) { ret = -ENOMEM; goto err; } + memcpy(skl->dais, skl_platform_dai, sizeof(skl_platform_dai)); + + for (i = 0; i < skl->grp_cnt.cnt; i++) { + index = num_dais + i; + + memcpy(&skl->dais[index], &ssp_dai_info, sizeof(ssp_dai_info)); + + skl->dais[index].name = kasprintf(GFP_KERNEL, "SSP%d Pin", + skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].name) + return -ENOMEM; + + skl->dais[index].playback.stream_name = kasprintf(GFP_KERNEL, + "ssp%d Tx", skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].playback.stream_name) { + kfree(skl->dais[index].name); + return -ENOMEM; + } + + skl->dais[index].capture.stream_name = kasprintf(GFP_KERNEL, + "ssp%d Rx", skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].capture.stream_name) { + kfree(skl->dais[index].name); + kfree(skl->dais[index].playback.stream_name); + return -ENOMEM; + } + } + if (!skl->use_tplg_pcm) { - dais = krealloc(skl->dais, sizeof(skl_fe_dai) + - sizeof(skl_platform_dai), GFP_KERNEL); + total_dais += ARRAY_SIZE(skl_fe_dai); + dais = krealloc(skl->dais, (total_dais * sizeof(*dais)), + GFP_KERNEL); if (!dais) { ret = -ENOMEM; goto err; } skl->dais = dais; - memcpy(&skl->dais[ARRAY_SIZE(skl_platform_dai)], skl_fe_dai, + memcpy(&skl->dais[num_dais + skl->grp_cnt.cnt], skl_fe_dai, sizeof(skl_fe_dai)); - num_dais += ARRAY_SIZE(skl_fe_dai); + + num_dais = total_dais; } ret = devm_snd_soc_register_component(dev, &skl_component, - skl->dais, num_dais); + skl->dais, total_dais); if (ret) dev_err(dev, "soc component registration failed %d\n", ret); err: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 01d54513b029..2883d86d56fe 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -71,6 +71,11 @@ struct skl_fw_config { struct skl_astate_config *astate_cfg; }; +struct ep_group_cnt { + int cnt; + int *vbus_id; +}; + struct skl { struct hdac_bus hbus; struct pci_dev *pci; @@ -107,6 +112,7 @@ struct skl { struct snd_soc_acpi_mach *mach; bool nhlt_override; bool mod_set_get_status; + struct ep_group_cnt grp_cnt; }; #define skl_to_bus(s) (&(s)->hbus) @@ -139,6 +145,7 @@ int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); int skl_get_nhlt_version(struct device *dev); +void skl_nhlt_get_ep_cnt(struct skl *skl, int link_type); struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, From 69413918db403ce72df6acd644825292c0b31b62 Mon Sep 17 00:00:00 2001 From: "Paul, Subhankar" Date: Wed, 31 Aug 2016 03:24:12 +0530 Subject: [PATCH 0168/1276] ASoC: Intel: Skylake: Adding support for set system time to aDSP In order to calculate the logging timestamps, firmware need the IA timestamps reference. A new IPC "SYSTEM TIME" has been added to pass UTC time to firmware. Also enable log skl_log_state_msg structure for enable log IPC has been modified according to latest firmware interface specification document. Change-Id: Ibcfb185c01c70b9b8e5a716849b9c935327594d3 Signed-off-by: Paul, Subhankar Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 32 +++++++++++++++++ sound/soc/intel/skylake/skl-pcm.c | 6 ++++ sound/soc/intel/skylake/skl-sst-ipc.h | 49 +++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 31384afe42b3..dc648b7db934 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -71,6 +71,8 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 +#define FW_LOGGING_AGING_TIMER_PERIOD 100 +#define FW_LOG_FIFO_FULL_TIMER_PERIOD 100 /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) @@ -79,6 +81,9 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) struct skl_ipc_large_config_msg msg = {0}; int ret = 0; + log_msg.aging_timer_period = FW_LOGGING_AGING_TIMER_PERIOD; + log_msg.fifo_full_timer_period = FW_LOG_FIFO_FULL_TIMER_PERIOD; + log_msg.core_mask = (1 << core); log_msg.logs_core[core].enable = enable; log_msg.logs_core[core].priority = ipc->dsp->trace_wind.log_priority; @@ -91,6 +96,33 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) return ret; } +#define SYSTEM_TIME 20 + +/* set system time to DSP via IPC */ +int skl_dsp_set_system_time(struct skl_sst *skl_sst) +{ + struct sst_generic_ipc *ipc = &skl_sst->ipc; + struct SystemTime sys_time_msg; + struct skl_ipc_large_config_msg msg = {0}; + struct timeval tv; + u64 sys_time; + u64 mask = 0x00000000FFFFFFFF; + int ret; + + do_gettimeofday(&tv); + + /* DSP firmware expects UTC time in micro seconds */ + sys_time = tv.tv_sec*1000*1000 + tv.tv_usec; + sys_time_msg.val_l = sys_time & mask; + sys_time_msg.val_u = (sys_time & (~mask)) >> 32; + + msg.large_param_id = SYSTEM_TIME; + msg.param_data_size = sizeof(sys_time_msg); + + ret = skl_ipc_set_large_config(ipc, &msg, (u32 *)&sys_time_msg); + return ret; +} + #define NOTIFICATION_PARAM_ID 3 #define NOTIFICATION_MASK 0xf diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 96c912bafc87..ac9f67c8b7e4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -847,6 +847,12 @@ static int skl_trace_compr_set_params(struct snd_compr_stream *stream, return ret; } + ret = skl_dsp_set_system_time(skl_sst); + if (ret < 0) { + dev_err(sst->dev, "Set system time to dsp firmware failed: %d\n", ret); + return ret; + } + skl_dsp_get_log_buff(sst, core); sst->trace_wind.flags |= BIT(core); ret = skl_dsp_enable_logging(ipc, core, 1); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 43d3eedad780..bc9b27884120 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -27,6 +27,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 +#define FW_REG_SZ 1024 enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, @@ -332,11 +333,56 @@ struct skl_log_state { }; struct skl_log_state_msg { + uint32_t aging_timer_period; + uint32_t fifo_full_timer_period; + u32 core_mask; struct skl_log_state logs_core[2]; }; -#define SKL_IPC_BOOT_MSECS 3000 +struct SystemTime { + uint32_t val_l; + uint32_t val_u; +}; + +struct fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +} __packed; + +struct sw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +} __packed; + +struct skl_dsp_core_dump { + u16 type0; + u16 length0; + u32 crash_dump_ver; + u16 bus_dev_id; + u16 cavs_hw_version; + struct fw_version fw_ver; + struct sw_version sw_ver; + u16 type2; + u16 length2; + u32 fwreg[FW_REG_SZ]; +} __packed; + +struct skl_module_notify { + u32 unique_id; + u32 event_id; + u32 event_data_size; + u32 event_data[0]; +} __packed; + +/* Timeout values in milliseconds for response from FW */ +#define SKL_IPC_BOOT_MSECS 3000 +#define SKL_IPC_LOAD_LIB_TIMEOUT 3000 +#define SKL_IPC_DEFAULT_TIMEOUT 300 #define SKL_IPC_D3_MASK 0 #define SKL_IPC_D0_MASK 3 @@ -390,6 +436,7 @@ int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable); +int skl_dsp_set_system_time(struct skl_sst *skl_sst); void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); From 86bd85e5ce7509948bb2c3c1d7147fecbf4c07a8 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Sat, 3 Sep 2016 02:56:03 +0530 Subject: [PATCH 0169/1276] ASoC: Intel: Skylake: Update FW Trace logs feature to new interface FW trace logs feature has been changed according to a new implementation from the firmware. 1. Core Mask has been updated to support 4 cores 2. Read and Write pointers of the trace buffer are moved to the base of the Trace window of the FW This patch contains the related driver changes Change-Id: Ie2336b5df91bfe291bb871c00726779ac77c0472 Signed-off-by: Mousumi Jana Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index b3591c8de471..1c53ba3c6172 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -53,8 +53,8 @@ #define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ << IPC_MSG_DIR_SHIFT) /* Global Notification Message */ -#define IPC_GLB_NOTIFY_CORE_SHIFT 15 -#define IPC_GLB_NOTIFY_CORE_MASK 0x1 +#define IPC_GLB_NOTIFY_CORE_SHIFT 12 +#define IPC_GLB_NOTIFY_CORE_MASK 0xF #define IPC_GLB_NOTIFY_CORE_ID(x) (((x) >> IPC_GLB_NOTIFY_CORE_SHIFT) \ & IPC_GLB_NOTIFY_CORE_MASK) #define IPC_GLB_NOTIFY_TYPE_SHIFT 16 @@ -356,8 +356,9 @@ static void skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) { int core, size; - u32 *ptr, avail; + u32 *ptr; u8 *base; + u32 write, read; #if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) core = 0; @@ -383,11 +384,21 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) base = (u8 *)sst->trace_wind.addr; /* move to the source dsp tracing window */ base += (core * size); - ptr = (u32 *)sst->trace_wind.dsp_wps[core]; - avail = *ptr; - if (avail < size/2) - base += size/2; - skl_dsp_write_log(sst, (void __iomem *)base, core, size/2); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + if (write > read) { + skl_dsp_write_log(sst, (void __iomem *)(base + 8 + read), + core, (write - read)); + /* read pointer */ + ptr[0] += write - read; + } else { + skl_dsp_write_log(sst, (void __iomem *) (base + 8 + read), + core, size - read); + skl_dsp_write_log(sst, (void __iomem *) (base + 8), + core, write); + ptr[0] = write; + } skl_dsp_put_log_buff(sst, core); } From 43596d7da90f54d5de1ee9d0113ed0b681ca2c5d Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Mon, 7 Nov 2016 13:38:52 +0530 Subject: [PATCH 0170/1276] ASoC: Intel: Skylake: Fix FW logging data corruption As per the new logging scheme introduced in latest firmware, the log buffer read/write pointers are located at the base of the log buffer for each core. Hence, while reading log data during buffer wrap around, the data count needs to be decremented to skip these pointers. This patch fixes the issue. Change-Id: I2fc52125823cc0e317eb56d217b95ad56589df28 Signed-off-by: Pardha Saradhi K Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Sangaraju, KarthikeyaX Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 1c53ba3c6172..508382d52e04 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -394,7 +394,7 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) ptr[0] += write - read; } else { skl_dsp_write_log(sst, (void __iomem *) (base + 8 + read), - core, size - read); + core, size - 8 - read); skl_dsp_write_log(sst, (void __iomem *) (base + 8), core, write); ptr[0] = write; From 683503f4a0b5858b6f4f38cf7d245278d2269256 Mon Sep 17 00:00:00 2001 From: "Schweikhardt, Markus" Date: Tue, 9 May 2017 23:00:20 +0530 Subject: [PATCH 0171/1276] ASoC: Intel: Board: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532 Change-Id: Ieee7ba1fc2dab6fbe43836b65def88c81360d48f Signed-off-by: Mohit Sinha Signed-off-by: Markus Schweikhardt Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 209 +++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 69e1081966a8..d8b639c426fa 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -339,6 +339,16 @@ config SND_SOC_INTEL_CNL_RT274_MACH with RT274 I2S audio codec. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "Broxton with TDF8532 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_TDF8532 + help + This adds support for ASoC machine driver for Broxton IVI GP MRB + platforms with TDF8532 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 72dfb2ccdb61..dcd898c0b4a2 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 000000000000..027060b17322 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,209 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From 1943b3a5e35b4b2ca583aa17c35565a76f3e3a24 Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Mon, 8 May 2017 21:19:09 +0530 Subject: [PATCH 0172/1276] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 377 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 485 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b002d79d910e..006be9a90286 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -163,6 +163,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -1015,6 +1016,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5aed74a73acf..b9ec39dfc960 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -179,6 +179,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -443,6 +444,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 000000000000..7a3cca073845 --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,377 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_component_driver soc_component_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 000000000000..6e3f2c147eac --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#ifndef __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From d2ee70d7d464c83bc6ab7d022c2456b6f194a5d8 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 8 Jun 2017 20:37:58 +0530 Subject: [PATCH 0173/1276] ASoC: Intel: Skylake: Added support for creating BXTP GPMRB machine with TDF8532 codec GPMRB board has TDF8532 codec on board, so added the machine name. Change-Id: Icdcb03e1068b11de12740ba9b9c4e7e83050aab0 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Babu, Ramesh Reviewed-by: Shaik, Kareem M Reviewed-by: Kale, Sanyog R Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: B, Jayachandran Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 97e8367eed30..c1fa7f6aa821 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1182,6 +1182,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, {} }; From 4720592f3166687d5408ed6c3011b81f78ee287a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 15 Jun 2017 15:08:06 +0530 Subject: [PATCH 0174/1276] ASoC: Intel: boards: Remove SSP1-codec dai link from cnl_rt274 machine Since NHLT does not have SSP1 endpoint, remove it from the dai link definitions Change-Id: I7b08f43d21eeff9decb5722e3af4f142f800b3f7 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: audio_build Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 4b434dc82b9f..ebfe74132da5 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -295,21 +295,9 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, - { - .name = "SSP1-Codec", - .id = 2, - .cpu_dai_name = "SSP1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .be_hw_params_fixup = cnl_be_fixup, - .ignore_suspend = 1, - .no_pcm = 1, - .dpcm_playback = 1, - }, { .name = "dmic01", - .id = 3, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", From a63c202f0033e67033866ec05890019698e64f7f Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 29 Jun 2017 11:57:13 +0530 Subject: [PATCH 0175/1276] [WORKAROUND] FIX Kconfigs to build compile with all platforms Change-Id: I4e29037b8c4d2068c244b30668133ae243cb957b Signed-off-by: Anamika Lal --- drivers/sdw/Kconfig | 5 +++-- sound/soc/codecs/Kconfig | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 1b7e2cc2ebc3..660188bd2c02 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,17 +1,18 @@ menuconfig SDW tristate "SoundWire bus support" depends on CRC8 + depends on X86 help SoundWire interface is typically used for transporting data related to audio functions. menuconfig SDW_CNL tristate "Intel SoundWire master controller support" - depends on SDW + depends on SDW && X86 help Intel SoundWire master controller driver menuconfig SDW_MAXIM_SLAVE bool "SoundWire Slave for the Intel CNL FPGA" - depends on SDW + depends on SDW && X86 help SoundWire Slave on FPGA platform for Intel CNL IP Mostly N for all the cases other than CNL Slave FPGA diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 006be9a90286..934bd6e4c53f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -81,7 +81,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES7134 select SND_SOC_ES7241 select SND_SOC_GTM601 - select SND_SOC_HDAC_HDMI select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -235,7 +234,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - select SND_SOC_SVFPGA_I2C if I2C help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -474,13 +472,13 @@ config SND_SOC_SVFPGA config SND_SOC_SVFPGA_SDW tristate "Intel SVFPGA Codec - SDW" - depends on SDW + depends on SDW && X86 select SND_SOC_SVFPGA select REGMAP_SDW config SND_SOC_SVFPGA_I2C tristate "Intel SVFPGA Codec - I2C" - depends on I2C + depends on I2C && X86 select SND_SOC_SVFPGA config SND_SOC_CS42L51 @@ -628,6 +626,7 @@ config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' config SND_SOC_HDAC_HDMI + depends on X86 && ACPI tristate select SND_HDA_EXT_CORE select SND_PCM_ELD From 1c66575ad3c7e6d5d261c5e104f9b3fa16508e0e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Mon, 22 Feb 2016 17:16:57 +0530 Subject: [PATCH 0176/1276] ASoC: Intel: Boards: FW logging DAI-links for BXT-P Added two DAI links, one for each DSP core, for FW logging Change-Id: I94f3d7be8c5263082deb7e1a995f599dcc174921 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Shaik, Kareem M Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_rt298.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 27308337ab12..a06f50464f54 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -428,6 +428,28 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Back End DAI links */ { /* SSP5 - Codec */ From 9f2a96589e6b89e99d03df70a5cbf482c2703658 Mon Sep 17 00:00:00 2001 From: Pramod Kumar Yadav Date: Tue, 26 Apr 2016 15:49:45 +0530 Subject: [PATCH 0177/1276] ASoC: Intel: Board: DAI links for probe in APL machine driver Added two DAI link, one for each playback & capture, for probe Change-Id: I4acd2e9421a96a1bd6938b8e5c8644a739c856a0 Signed-off-by: Pramod Yadav Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_rt298.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index a06f50464f54..9d40c403ac5c 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -428,6 +428,27 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, + /* Probe DAI links */ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Trace Buffer DAI links */ { .name = "Bxt Trace Buffer0", From 68d39a72b2122a33f5edac77f7d39d2f93a65f7a Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Mon, 11 Jul 2016 18:29:27 +0530 Subject: [PATCH 0178/1276] ASoC: Intel: Skylake: Probe sequence changes based on state transition PROBE STATE TRANSITIONS: This patch implements below state sequence in setting-up and tearing down the extractor/injector probe point. EXTRACTOR: Default state: SKL_PROBE_STATE_EXT_NONE 1. Probe module instantiation and probe point connections State: SKL_PROBE_STATE_EXT_CONNECTED 2. Probe point disconnection State: SKL_PROBE_STATE_EXT_NONE Note: Extractor does not have separate attach/detach DMA step INJECTOR: Default state: SKL_PROBE_STATE_INJ_NONE 1. Probe module instantiation & Injection DMA attachment State: SKL_PROBE_STATE_INJ_DMA_ATTACHED 2. Probe point connection State: SKL_PROBE_STATE_INJ_CONNECTED 3. Probe point disconnection State: SKL_PROBE_STATE_INJ_DISCONNECTED 4. Injection DMA detachment State: SKL_PROBE_STATE_INJ_NONE Change-Id: I4ceb720d9dfae82c8877db1c971715956382852d Signed-off-by: Pawse, GuruprasadX Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 22 +- sound/soc/intel/skylake/skl-messages.c | 66 +++- sound/soc/intel/skylake/skl-pcm.c | 14 +- sound/soc/intel/skylake/skl-probe.c | 104 +++++- sound/soc/intel/skylake/skl-sst-ipc.h | 26 +- sound/soc/intel/skylake/skl-topology.c | 408 +++++++++++++++++------- sound/soc/intel/skylake/skl-topology.h | 24 +- 7 files changed, 495 insertions(+), 169 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index e5656fc0ca02..2dceadfbc1f5 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -151,17 +151,33 @@ enum skl_module_param_type { SKL_PARAM_BIND }; -enum skl_probe_connect_type { - SKL_PROBE_CONNECT = 3, +enum skl_probe_param_id_type { + SKL_PROBE_INJECT_DMA_ATTACH = 1, + SKL_PROBE_INJECT_DMA_DETACH, + SKL_PROBE_CONNECT, SKL_PROBE_DISCONNECT }; -enum skl_probe_direction { +enum skl_probe_purpose { SKL_PROBE_EXTRACT = 0, SKL_PROBE_INJECT, SKL_PROBE_INJECT_REEXTRACT }; +/* Injector probe states */ +enum skl_probe_state_inj { + SKL_PROBE_STATE_INJ_NONE = 1, + SKL_PROBE_STATE_INJ_DMA_ATTACHED, + SKL_PROBE_STATE_INJ_CONNECTED, + SKL_PROBE_STATE_INJ_DISCONNECTED +}; + +/* Extractor probe states */ +enum skl_probe_state_ext { + SKL_PROBE_STATE_EXT_NONE = 1, + SKL_PROBE_STATE_EXT_CONNECTED +}; + struct skl_dfw_sdw_aggdata { u32 alh_stream_num; u32 channel_mask; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index dc648b7db934..b52f1c08d0a5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2008,33 +2008,83 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg src_module->m_state, dst_module->m_state); } -int skl_disconnect_probe_point(struct skl_sst *ctx, +int skl_probe_point_disconnect_ext(struct skl_sst *ctx, struct snd_soc_dapm_widget *w) { struct skl_ipc_large_config_msg msg; struct skl_probe_config *pconfig = &ctx->probe_config; struct skl_module_cfg *mcfg; - int probe_point[8] = {0}; + u32 probe_point[NO_OF_EXTRACTOR] = {0}; + int store_prb_pt_index[NO_OF_EXTRACTOR] = {0}; int n = 0, i; + int ret = 0; int no_of_extractor = pconfig->no_extractor; - dev_dbg(ctx->dev, "Disconnecting probe\n"); + dev_dbg(ctx->dev, "Disconnecting extractor probe points\n"); mcfg = w->priv; msg.module_id = mcfg->id.module_id; msg.instance_id = mcfg->id.instance_id; msg.large_param_id = SKL_PROBE_DISCONNECT; for (i = 0; i < no_of_extractor; i++) { - if (pconfig->eprobe[i].set) { - probe_point[n] = pconfig->eprobe[i].id; - pconfig->eprobe[i].set = -1; + if (pconfig->eprobe[i].state == SKL_PROBE_STATE_EXT_CONNECTED) { + probe_point[n] = pconfig->eprobe[i].probe_point_id; + store_prb_pt_index[i] = 1; n++; } } + if (n == 0) + return ret; msg.param_data_size = n * sizeof(u32); - return skl_ipc_set_large_config(&ctx->ipc, &msg, - probe_point); + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, probe_point); + if (ret < 0) + return -EINVAL; + + for (i = 0; i < pconfig->no_extractor; i++) { + if (store_prb_pt_index[i]) { + pconfig->eprobe[i].state = SKL_PROBE_STATE_EXT_NONE; + dev_dbg(ctx->dev, "eprobe[%d].state %d\n", + i, pconfig->eprobe[i].state); + } + } + + return ret; +} + +int skl_probe_point_disconnect_inj(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index) +{ + struct skl_ipc_large_config_msg msg; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_module_cfg *mcfg; + u32 probe_point = 0; + int ret = 0; + + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_CONNECTED) { + dev_dbg(ctx->dev, "Disconnecting injector probe point\n"); + mcfg = w->priv; + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.large_param_id = SKL_PROBE_DISCONNECT; + probe_point = pconfig->iprobe[index].probe_point_id; + msg.param_data_size = sizeof(u32); + + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, &probe_point); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DISCONNECTED; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", + index, pconfig->iprobe[index].state); + } + + return ret; + } /* * On module freeup, we need to unbind the module with modules diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ac9f67c8b7e4..1a722d2fa87e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1774,6 +1774,7 @@ static int skl_get_probe_widget(struct snd_soc_component *component, { struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; struct snd_soc_dapm_widget *w; + int i; list_for_each_entry(w, &component->card->widgets, list) { if (is_skl_dsp_widget_type(w, skl->skl_sst->dev) && @@ -1786,9 +1787,16 @@ static int skl_get_probe_widget(struct snd_soc_component *component, } } - pconfig->probe_count = 0; - pconfig->no_injector = 6; - pconfig->no_extractor = 8; + pconfig->i_refc = 0; + pconfig->e_refc = 0; + pconfig->no_injector = NO_OF_INJECTOR; + pconfig->no_extractor = NO_OF_EXTRACTOR; + + for (i = 0; i < pconfig->no_injector; i++) + pconfig->iprobe[i].state = SKL_PROBE_STATE_INJ_NONE; + + for (i = 0; i < pconfig->no_extractor; i++) + pconfig->eprobe[i].state = SKL_PROBE_STATE_EXT_NONE; return 0; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index b4f5fe4220cf..9ccd19d32ef3 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -40,6 +40,37 @@ */ #define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 +/* + * ======================== + * PROBE STATE TRANSITIONS: + * ======================== + * Below gives the steps involved in setting-up and tearing down the + * extractor/injector probe point and the corresponding state to which + * it transition after each step. + * + * EXTRACTOR: + * Default state: SKL_PROBE_STATE_EXT_NONE + * 1. Probe module instantiation and probe point connections + * (can connect multiple probe points) + * State: SKL_PROBE_STATE_EXT_CONNECTED + * --> State where the stream is running. + * 2. Probe point disconnection + * State: SKL_PROBE_STATE_EXT_NONE + * Note: Extractor does not have separate attach/detach DMA step + * + * INJECTOR: + * Default state: SKL_PROBE_STATE_INJ_NONE + * 1. Probe module instantiation & Injection DMA attachment + * State: SKL_PROBE_STATE_INJ_DMA_ATTACHED + * 2. Probe point connection + * State: SKL_PROBE_STATE_INJ_CONNECTED + * --> State where the stream is running. + * 3. Probe point disconnection + * State: SKL_PROBE_STATE_INJ_DISCONNECTED + * 4. Injection DMA detachment + * State: SKL_PROBE_STATE_INJ_NONE + */ + static int set_injector_stream(struct hdac_ext_stream *stream, struct snd_soc_dai *dai) { @@ -50,9 +81,9 @@ static int set_injector_stream(struct hdac_ext_stream *stream, */ struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - int i; + int i = skl_probe_get_index(dai, pconfig); - if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + if (i != -1) { pconfig->iprobe[i].stream = stream; pconfig->iprobe[i].dma_id = hdac_stream(stream)->stream_tag - 1; @@ -71,7 +102,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); - if (!pconfig->probe_count) { + if ((pconfig->i_refc + pconfig->e_refc) == 0) { pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, @@ -118,8 +149,15 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, int ret, dma_id; unsigned int format_val = 0; int err; + int index; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + ret = skl_substream_alloc_compr_pages(ebus, substream, runtime->fragments*runtime->fragment_size); if (ret < 0) @@ -128,11 +166,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); - if (hdac_stream(stream)->prepared) { - dev_dbg(dai->dev, "already stream is prepared - returning\n"); - return 0; - } - snd_hdac_stream_reset(hdac_stream(stream)); err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); @@ -148,17 +181,25 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, hdac_stream(stream)->prepared = 1; /* Initialize probe module only the first time */ - if (!pconfig->probe_count) { - + if ((pconfig->i_refc + pconfig->e_refc) == 0) { ret = skl_init_probe_module(skl->skl_sst, mconfig); if (ret < 0) return ret; } - if (substream->direction == SND_COMPRESS_PLAYBACK) - skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); + if (substream->direction == SND_COMPRESS_PLAYBACK) { + index = skl_probe_get_index(dai, pconfig); + if (index < 0) + return -EINVAL; + + ret = skl_probe_attach_inj_dma(pconfig->w, skl->skl_sst, index); + if (ret < 0) + return -EINVAL; - pconfig->probe_count++; + pconfig->i_refc++; + } else { + pconfig->e_refc++; + } #if USE_SPIB snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); @@ -173,15 +214,43 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - int ret; + struct skl_module_cfg *mconfig = pconfig->w->priv; + int ret = 0; + int index; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); #if USE_SPIB snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); #endif + if ((pconfig->i_refc + pconfig->e_refc) == 0) + goto probe_uninit; + + if (substream->direction == SND_COMPRESS_PLAYBACK) { + index = skl_probe_get_index(dai, pconfig); + if (index < 0) + return -EINVAL; + + ret = skl_probe_point_disconnect_inj(skl->skl_sst, + pconfig->w, index); + if (ret < 0) + return -EINVAL; + + ret = skl_probe_detach_inj_dma(skl->skl_sst, pconfig->w, index); + if (ret < 0) + return -EINVAL; + + pconfig->i_refc--; + } else if (substream->direction == SND_COMPRESS_CAPTURE) { + ret = skl_probe_point_disconnect_ext(skl->skl_sst, pconfig->w); + if (ret < 0) + return -EINVAL; + + pconfig->e_refc--; + } - if (!--pconfig->probe_count) { - skl_disconnect_probe_point(skl->skl_sst, pconfig->w); +probe_uninit: + if (((pconfig->i_refc + pconfig->e_refc) == 0) + && mconfig->m_state == SKL_MODULE_INIT_DONE) { ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); if (ret < 0) return ret; @@ -344,7 +413,8 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, /* FW starts probe module soon after its params are set. * So to avoid xruns, start DMA first and then set probe params. */ - ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + ret = skl_probe_point_set_config(pconfig->w, skl->skl_sst, + substream->direction, dai); if (ret < 0) return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc9b27884120..d6866bc15469 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -20,6 +20,7 @@ #include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" +#include "skl-tplg-interface.h" struct sst_dsp; struct skl_sst; @@ -100,21 +101,36 @@ struct skl_lib_info { }; struct injector_data { - int set; - int id; + /* connect or disconnect */ + u8 operation; + /* Specifies EXTRACTOR or INJECTOR or INJECT_REEXTRACT */ + u32 purpose; + /* Injector probe param */ + u32 probe_point_id; struct hdac_ext_stream *stream; int dma_id; int dma_buf_size; + enum skl_probe_state_inj state; }; struct extractor_data { - int set; - int id; + /* Probe connect or disconnect */ + u8 operation; + /* Specifies EXTRACTOR or INJECTOR or INJECT_REEXTRACT */ + u32 purpose; + /* Extractor probe param */ + u32 probe_point_id; + enum skl_probe_state_ext state; }; struct skl_probe_config { struct snd_soc_dapm_widget *w; - int probe_count; + /* Number of extractor DMA's used */ + int e_refc; + + /* Number of injector DMA's used */ + int i_refc; + int edma_id; int edma_type; int edma_buffsize; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6e7a192cc68c..4bcd184033e8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -495,7 +495,7 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, skl_dump_mconfig(ctx, m_cfg); } -int skl_get_probe_index(struct snd_soc_dai *dai, +int skl_probe_get_index(struct snd_soc_dai *dai, struct skl_probe_config *pconfig) { int i, ret = -1; @@ -509,62 +509,124 @@ int skl_get_probe_index(struct snd_soc_dai *dai, return ret; } -int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, - struct skl_sst *ctx, struct snd_soc_dai *dai) +int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int index) { - int i, ret; + int ret = -EINVAL; + struct skl_module_cfg *mconfig = w->priv; - struct skl_attach_probe_dma ad; + struct skl_probe_attach_inj_dma ad; struct skl_probe_config *pconfig = &ctx->probe_config; - if ((i = skl_get_probe_index(dai, pconfig)) != -1) { - ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_NONE) { + dev_dbg(ctx->dev, "Attaching injector DMA\n"); + ad.node_id.node.vindex = pconfig->iprobe[index].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; + + ret = skl_set_module_params(ctx, (void *)&ad, + sizeof(struct skl_probe_attach_inj_dma), + SKL_PROBE_INJECT_DMA_ATTACH, mconfig); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DMA_ATTACHED; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); } ret = skl_set_module_params(ctx, (u32 *)&ad, - sizeof(struct skl_attach_probe_dma), 1, mconfig); + sizeof(struct skl_probe_attach_inj_dma), + 1, mconfig); return ret; } -int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, +int skl_probe_detach_inj_dma(struct skl_sst *ctx, struct snd_soc_dapm_widget *w, + int index) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_ipc_large_config_msg msg; + union skl_connector_node_id node_id; + int ret = -EINVAL; + + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_DISCONNECTED) { + dev_dbg(ctx->dev, "Detaching injector DMA\n"); + node_id.node.vindex = pconfig->iprobe[index].dma_id; + node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + node_id.node.rsvd = 0; + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.large_param_id = SKL_PROBE_INJECT_DMA_DETACH; + msg.param_data_size = sizeof(union skl_connector_node_id); + + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, + (u32 *)&node_id); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_NONE; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); + } + return ret; +} + + +int skl_probe_point_set_config(struct snd_soc_dapm_widget *w, struct skl_sst *ctx, int direction, struct snd_soc_dai *dai) { - int i, ret = 0, n = 0; + int i, ret = -EIO, n = 0; struct skl_module_cfg *mconfig = w->priv; const struct snd_kcontrol_new *k; - struct soc_bytes_ext *sb; - struct skl_probe_data *bc; struct skl_probe_config *pconfig = &ctx->probe_config; struct probe_pt_param prb_pt_param[8] = {{0}}; + int store_prb_pt_index[8] = {0}; if (direction == SND_COMPRESS_PLAYBACK) { /* only one injector point can be set at a time*/ - n = skl_get_probe_index(dai, pconfig); + n = skl_probe_get_index(dai, pconfig); if (n < 0) return -EINVAL; k = &w->kcontrol_news[pconfig->no_extractor + n]; - - if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - sb = (void *) k->private_value; - bc = (struct skl_probe_data *)sb->dobj.private; - pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", - bc->is_ext_inj, bc->params, bc->is_connect); - if (!(bc->is_ext_inj == SKL_PROBE_INJECT || - bc->is_ext_inj == SKL_PROBE_INJECT_REEXTRACT)) + dev_dbg(dai->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + pconfig->iprobe[n].operation, pconfig->iprobe[n].purpose, + pconfig->iprobe[n].probe_point_id); + + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + && (pconfig->iprobe[n].state == + SKL_PROBE_STATE_INJ_DMA_ATTACHED) + && (pconfig->iprobe[n].operation == + SKL_PROBE_CONNECT) + && (pconfig->iprobe[n].purpose == + SKL_PROBE_INJECT || + pconfig->iprobe[n].purpose == + SKL_PROBE_INJECT_REEXTRACT)) { + + prb_pt_param[0].params = + pconfig->iprobe[n].probe_point_id; + prb_pt_param[0].connection = pconfig->iprobe[n].purpose; + prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; + ret = skl_set_module_params(ctx, (void *)prb_pt_param, + sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_dbg(dai->dev, "failed to set injector probe point\n"); return -EINVAL; + } - prb_pt_param[0].params = (int)bc->params; - prb_pt_param[0].connection = bc->is_ext_inj; - prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; - ret = skl_set_module_params(ctx, (void *)prb_pt_param, sizeof(struct probe_pt_param), - bc->is_connect, mconfig); + pconfig->iprobe[n].state = + SKL_PROBE_STATE_INJ_CONNECTED; + dev_dbg(dai->dev, "iprobe[%d].state %d\n", n, + pconfig->iprobe[n].state); } } else if (direction == SND_COMPRESS_CAPTURE) { @@ -572,27 +634,50 @@ int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, /*multiple extractor points can be set simultaneously*/ for (i = 0; i < pconfig->no_extractor; i++) { k = &w->kcontrol_news[i]; - if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - sb = (void *) k->private_value; - bc = (struct skl_probe_data *)sb->dobj.private; - - pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", - bc->is_ext_inj, bc->params, bc->is_connect); - if (bc->is_ext_inj == SKL_PROBE_EXTRACT && - pconfig->eprobe[i].set == 1) { - pr_debug("Retrieving the exractor params \n"); - prb_pt_param[n].params = (int)bc->params; - prb_pt_param[n].connection = bc->is_ext_inj; - prb_pt_param[n].node_id = -1; - n++; - } + dev_dbg(dai->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + pconfig->eprobe[i].operation, + pconfig->eprobe[i].purpose, + pconfig->eprobe[i].probe_point_id); + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + && (pconfig->eprobe[i].state == + SKL_PROBE_STATE_EXT_NONE) + && (pconfig->eprobe[i].operation == + SKL_PROBE_CONNECT) + && (pconfig->eprobe[i].purpose == + SKL_PROBE_EXTRACT || + pconfig->eprobe[i].purpose == + SKL_PROBE_INJECT_REEXTRACT)) { + + dev_dbg(dai->dev, "Retrieving the exractor params\n"); + prb_pt_param[n].params = + pconfig->eprobe[i].probe_point_id; + prb_pt_param[n].connection = + pconfig->eprobe[i].purpose; + prb_pt_param[n].node_id = -1; + store_prb_pt_index[i] = 1; + n++; } } - if (n > 0) + if (n > 0) { ret = skl_set_module_params(ctx, (void *)prb_pt_param, n * sizeof(struct probe_pt_param), SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_dbg(dai->dev, "failed to set extractor probe point\n"); + return -EINVAL; + } + } + + for (i = 0; i < pconfig->no_extractor; i++) { + if (store_prb_pt_index[i]) { + pconfig->eprobe[i].state = + SKL_PROBE_STATE_EXT_CONNECTED; + dev_dbg(dai->dev, "eprobe[%d].state %d\n", + n, pconfig->eprobe[i].state); + } + } + } return ret; } @@ -1906,64 +1991,85 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, memcpy(pipe->p_params, params, sizeof(*params)); } } -static int skl_cache_probe_param(struct snd_kcontrol *kctl, - struct skl_probe_data *ap, struct skl_sst *ctx) + +static int skl_probe_set_tlv_ext(struct snd_kcontrol *kcontrol) { - struct skl_probe_config *pconfig = &ctx->probe_config; - union skl_connector_node_id node_id = {-1}; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct probe_pt_param connect_point; + int disconnect_point; + int ret = 0; int index = -1, i; char buf[20], pos[10]; - if (ap->is_ext_inj == SKL_PROBE_EXTRACT) { - /* From the control ID get the extractor index */ - for (i = 0; i < pconfig->no_extractor; i++) { - strcpy(buf, "Extractor"); - snprintf(pos, 4, "%d", i); - if (strstr(kctl->id.name, strcat(buf, pos))) { - index = i; - break; - } - } - if (index < 0) - return -EINVAL; + for (i = 0; i < pconfig->no_extractor; i++) { + strcpy(buf, "Extractor"); + snprintf(pos, 4, "%d", i); + if (strstr(kcontrol->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + if (index < 0) + return -EINVAL; - pr_debug("Setting extractor probe index %d\n", index); - memcpy(&ap->node_id, &node_id, sizeof(u32)); - pconfig->eprobe[index].id = ap->params; - if (ap->is_connect == SKL_PROBE_CONNECT) - pconfig->eprobe[index].set = 1; - else if (ap->is_connect == SKL_PROBE_DISCONNECT) - pconfig->eprobe[index].set = -1; + if ((ap->operation == SKL_PROBE_CONNECT) && + (pconfig->eprobe[index].state == SKL_PROBE_STATE_EXT_NONE)) { + /* cache extractor params */ + pconfig->eprobe[index].operation = ap->operation; + pconfig->eprobe[index].purpose = ap->purpose; + pconfig->eprobe[index].probe_point_id = ap->probe_point_id; - } else { - /* From the control ID get the injector index */ - for (i = 0; i < pconfig->no_injector; i++) { - strcpy(buf, "Injector"); - snprintf(pos, 4, "%d", i); - if (strstr(kctl->id.name, strcat(buf, pos))) { - index = i; - break; + /* Below check ensures that atleast one extractor stream is in + * progress in which case the driver can send the CONNECT IPC + */ + if (pconfig->e_refc > 0) { + memcpy(&connect_point.params, &ap->probe_point_id, + sizeof(u32)); + connect_point.connection = ap->purpose; + connect_point.node_id = -1; + ret = skl_set_module_params(skl->skl_sst, + (void *)&connect_point, + sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to connect extractor probe point\n"); + return -EINVAL; } + pconfig->eprobe[index].state = + SKL_PROBE_STATE_EXT_CONNECTED; + dev_dbg(dapm->dev, "eprobe[%d].state %d\n", index, + pconfig->eprobe[index].state); } - - if (index < 0) + } else if ((ap->operation == SKL_PROBE_DISCONNECT) && + (pconfig->eprobe[index].state == + SKL_PROBE_STATE_EXT_CONNECTED) && + (pconfig->e_refc > 0)) { + disconnect_point = (int)ap->probe_point_id; + ret = skl_set_module_params(skl->skl_sst, + (void *)&disconnect_point, sizeof(disconnect_point), + SKL_PROBE_DISCONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to disconnect extractor probe point\n"); return -EINVAL; + } + pconfig->eprobe[index].state = SKL_PROBE_STATE_EXT_NONE; + dev_dbg(dapm->dev, "eprobe[%d].state %d\n", index, + pconfig->eprobe[index].state); + } else + ret = -EINVAL; - pconfig->iprobe[index].id = ap->params; - node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; - node_id.node.vindex = pconfig->iprobe[index].dma_id; - memcpy(&ap->node_id, &node_id, sizeof(u32)); - if (ap->is_connect == SKL_PROBE_CONNECT) - pconfig->iprobe[index].set = 1; - else if (ap->is_connect == SKL_PROBE_DISCONNECT) - pconfig->iprobe[index].set = -1; - } - return 0; + return ret; } -static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, - const unsigned int __user *data, unsigned int size) +static int skl_probe_set_tlv_inj(struct snd_kcontrol *kcontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); @@ -1973,64 +2079,118 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; struct skl *skl = get_skl_ctx(dapm->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - struct probe_pt_param connect_point; int disconnect_point; + int ret = 0; + int index = -1, i; + char buf[20], pos[10]; + + for (i = 0; i < pconfig->no_injector; i++) { + strcpy(buf, "Injector"); + snprintf(pos, 4, "%d", i); + if (strstr(kcontrol->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + if (index < 0) + return -EINVAL; + + if ((ap->operation == SKL_PROBE_CONNECT) && + (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_NONE)) { + /* cache injector params */ + pconfig->iprobe[index].operation = ap->operation; + pconfig->iprobe[index].purpose = ap->purpose; + pconfig->iprobe[index].probe_point_id = ap->probe_point_id; + } else if ((ap->operation == SKL_PROBE_DISCONNECT) && + + (pconfig->iprobe[index].state == + SKL_PROBE_STATE_INJ_CONNECTED) && + (pconfig->i_refc > 0)) { + disconnect_point = (int)ap->probe_point_id; + ret = skl_set_module_params(skl->skl_sst, + (void *)&disconnect_point, + sizeof(disconnect_point), + SKL_PROBE_DISCONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to disconnect injector probe point\n"); + return -EINVAL; + } + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DISCONNECTED; + dev_dbg(dapm->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); + } else + ret = -EINVAL; + + return ret; +} + +static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, + const unsigned int __user *data, unsigned int size) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; void *offset; - int ret; + int ret = -EIO, ret1; - dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); + dev_dbg(dapm->dev, "In %s control=%s\n", __func__, kcontrol->id.name); dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); if (data) { offset = (unsigned char *)data; offset += 2 * sizeof(u32); /* To skip TLV heeader */ - if (copy_from_user(&ap->is_connect, - offset, sizeof(ap->is_connect))) + if (copy_from_user(&ap->operation, + offset, sizeof(ap->operation))) return -EIO; - offset += sizeof(ap->is_connect); - if (copy_from_user(&ap->is_ext_inj, - offset, sizeof(ap->is_ext_inj))) + offset += sizeof(ap->operation); + if (copy_from_user(&ap->purpose, + offset, sizeof(ap->purpose))) return -EIO; - offset += sizeof(ap->is_ext_inj); - if (copy_from_user(&ap->params, - offset, sizeof(ap->params))) + offset += sizeof(ap->purpose); + if (copy_from_user(&ap->probe_point_id, + offset, sizeof(ap->probe_point_id))) return -EIO; - dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", - ap->is_connect, ap->is_ext_inj, ap->params); + dev_dbg(dapm->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + ap->operation, ap->purpose, ap->probe_point_id); - ret = skl_cache_probe_param(kcontrol, ap, skl->skl_sst); - if (ret < 0) - return -EINVAL; - - if (pconfig->probe_count) { - /* In the case of extraction, additional probe points can be set when - * the stream is in progress and the driver can immediately send the - * connect IPC. But in the case of injector, for each probe point - * connection a new stream with the DAI number corresponding to that - * control has to be opened. Hence below check ensures that the - * connect IPC is sent only in case of extractor. - */ - if ((ap->is_connect == SKL_PROBE_CONNECT) - && (ap->is_ext_inj == SKL_PROBE_EXTRACT)) { + /* In the case of extraction, additional probe points can + * be set when the stream is in progress and the driver can + * immediately send the connect IPC. But in the case of + * injector, for each probe point connection a new stream with + * the DAI number corresponding to that control has to be + * opened. Hence below implementation ensures that the connect + * IPC is sent only in case of extractor. + */ + switch (ap->purpose) { + case SKL_PROBE_EXTRACT: + ret = skl_probe_set_tlv_ext(kcontrol); + break; - memcpy(&connect_point.params, &ap->params, sizeof(u32)); - connect_point.connection = ap->is_ext_inj; - memcpy(&connect_point.node_id, (&ap->node_id), sizeof(u32)); - return skl_set_module_params(skl->skl_sst, (void *)&connect_point, - sizeof(struct probe_pt_param), ap->is_connect, mconfig); + case SKL_PROBE_INJECT: + ret = skl_probe_set_tlv_inj(kcontrol); + break; - } else if (ap->is_connect == SKL_PROBE_DISCONNECT) { + case SKL_PROBE_INJECT_REEXTRACT: + /* Injector and extractor control will be set one by one + * for Inject_Reextract + */ + ret = skl_probe_set_tlv_ext(kcontrol); + ret1 = skl_probe_set_tlv_inj(kcontrol); + if (ret == 0 || ret1 == 0) + ret = 0; + else + ret = -EINVAL; + break; - disconnect_point = (int)ap->params; - return skl_set_module_params(skl->skl_sst, (void *)&disconnect_point, - sizeof(disconnect_point), ap->is_connect, mconfig); - } + default: + ret = -EINVAL; } } - return 0; + return ret; } /* diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 0c6b5c6b9c99..02256d70f60f 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -468,13 +468,15 @@ struct skl_algo_data { }; struct skl_probe_data { - u8 is_connect; - u32 is_ext_inj; - u32 params; + /* connect or disconnect */ + u8 operation; + /* extractor or injector or inject-reextract */ + u32 purpose; + u32 probe_point_id; u32 node_id; } __packed; -struct skl_attach_probe_dma { +struct skl_probe_attach_inj_dma { union skl_connector_node_id node_id; u32 dma_buff_size; } __packed; @@ -559,12 +561,14 @@ int skl_init_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_con int skl_uninit_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); -int skl_get_probe_index(struct snd_soc_dai *dai, +int skl_probe_get_index(struct snd_soc_dai *dai, struct skl_probe_config *pconfig); -int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, - struct skl_sst *ctx, struct snd_soc_dai *dai); -int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, +int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int index); +int skl_probe_detach_inj_dma(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index); +int skl_probe_point_set_config(struct snd_soc_dapm_widget *w, struct skl_sst *ctx, int direction, struct snd_soc_dai *dai); int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, @@ -575,8 +579,10 @@ int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); -int skl_disconnect_probe_point(struct skl_sst *ctx, +int skl_probe_point_disconnect_ext(struct skl_sst *ctx, struct snd_soc_dapm_widget *w); +int skl_probe_point_disconnect_inj(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index); int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, From bdcec9987b0eb37b576e3fc6d2586bdfec5338cf Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 2 Aug 2016 17:00:12 +0530 Subject: [PATCH 0179/1276] ASoC: Intel: Skylake: Probe DMA release for extractor 1. Extractor DMA is to be assigned when the first probe stream (irrespective of whether it is injector or extractor) is opened. But if the first probe stream is injector, we get injector's substream pointer and we do not have the right substream pointer for extractor. The existing code passed injector's substream while assigning extractor DMA.This patch sets NULL for substream while assigning the DMA for extractor and sets the correct substream pointer later when open is indeed for extractor. 2. DMA reset for extractor should not be done after probe module is initialized. The existing code reset DMA in hw_params. This can result in DMA reset after probe module init when injector and extractor are started one after the other. 3. Extractor DMA is assigned in compr_open for the first probe stream irrespective of whether it is injector or extractor. So DMA release for extractor should be done in the case where a injector probe alone was started and stopped without starting any extractor. This patch moves the DMA reset from hw params to immediately after DMA assignment in open call back for both injector and extractor. Change-Id: I2604796d81e2e6da5acd3977774887a0b2e14559 Signed-off-by: Pawse, GuruprasadX Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: audio_build Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-probe.c | 48 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 9ccd19d32ef3..b563dc38dce8 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -105,29 +105,51 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, if ((pconfig->i_refc + pconfig->e_refc) == 0) { pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; + /* + * Extractor DMA is to be assigned when the first probe + * stream(irrespective of whether it is injector or extractor) + * is opened. But if the first probe stream is injector, we + * get injector's substream pointer and we do not have the + * right substream pointer for extractor. So, pass NULL for + * substream while assigning the DMA for extractor and set the + * correct substream pointer later when open is indeed for + * extractor. + */ pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, - substream, + NULL, SND_COMPRESS_CAPTURE); - if (!pconfig->estream) + if (!pconfig->estream) { + dev_err(dai->dev, "Failed to assign extractor stream\n"); return -EINVAL; + } pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; + snd_hdac_stream_reset(hdac_stream(pconfig->estream)); } if (substream->direction == SND_COMPRESS_PLAYBACK) { stream = hdac_ext_host_stream_compr_assign(ebus, substream, SND_COMPRESS_PLAYBACK); + if (stream == NULL) { + if ((pconfig->i_refc + pconfig->e_refc) == 0) + snd_hdac_ext_stream_release(pconfig->estream, + HDAC_EXT_STREAM_TYPE_HOST); + + dev_err(dai->dev, "Failed to assign injector stream\n"); + return -EBUSY; + } set_injector_stream(stream, dai); runtime->private_data = stream; + snd_hdac_stream_reset(hdac_stream(stream)); } else if (substream->direction == SND_COMPRESS_CAPTURE) { stream = pconfig->estream; runtime->private_data = pconfig->estream; - } - - if (stream == NULL) { - dev_err(dai->dev, "stream = NULL\n"); - return -EBUSY; + /* + * Open is indeed for extractor. So, set the correct substream + * pointer now. + */ + stream->hstream.stream = substream; } hdac_stream(stream)->curr_pos = 0; @@ -166,7 +188,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); - snd_hdac_stream_reset(hdac_stream(stream)); err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); if (err < 0) @@ -254,6 +275,16 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); if (ret < 0) return ret; + + /* + * Extractor DMA is assigned in compr_open for the first probe stream + * irrespective of whether it is injector or extractor. + * So DMA release for extractor should be done in the case where + * a injector probe alone was started and stopped without + * starting any extractor. + */ + if (substream->direction == SND_COMPRESS_PLAYBACK) + snd_hdac_ext_stream_release(pconfig->estream, HDAC_EXT_STREAM_TYPE_HOST); } snd_hdac_stream_cleanup(hdac_stream(stream)); @@ -261,6 +292,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + /* Release the particular injector/extractor stream getting closed */ snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); return 0; From 0e0849ce48ee4b5e970708ede558ed78fff4cdf9 Mon Sep 17 00:00:00 2001 From: "S, Pavan K" Date: Fri, 30 Jun 2017 20:52:37 +0530 Subject: [PATCH 0180/1276] ASoC: Intel: Multiple I/O PCM format support for pipe If a pipe supports multiple input/output formats, kcontrol is created and selection of pipe input and output configuration is done based on control set. If more than one configuration is supported, then this patch allows user to select configuration of choice using amixer settings. Change-Id: Ie977d9857507a13aade10a1175994ecabcceed0c Signed-off-by: S, Pavan K Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 1 + sound/soc/intel/skylake/skl-topology.c | 103 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + 3 files changed, 105 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 2dceadfbc1f5..89844fb10aa5 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -19,6 +19,7 @@ #define SKL_CONTROL_TYPE_BYTE_TLV 0x100 #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 #define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 +#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 4bcd184033e8..a1f88c3e2aa4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -60,6 +60,14 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { #define CHECK_HW_PARAMS(ch, freq, bps, prm_ch, prm_freq, prm_bps) \ ((ch == prm_ch) && (bps == prm_bps) && (freq == prm_freq)) +#define GET_PIPE(ppl, skl, node, pipe_id, pipe) \ + do { list_for_each_entry(ppl, &skl->ppl_list, node) { \ + if (ppl->pipe->ppl_id == pipe_id) { \ + pipe = ppl->pipe; \ + break; } \ + } \ + } while (0) + static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, struct skl *skl); @@ -895,6 +903,35 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, return ret; } +static bool is_skl_tplg_multi_fmt(struct skl *skl, struct skl_pipe *pipe) +{ + int i; + struct skl_pipe_fmt *cur_fmt; + struct skl_pipe_fmt *next_fmt; + + if (pipe->conn_type == SKL_PIPE_CONN_TYPE_FE && + pipe->nr_cfgs > 1) { + for (i = 0; i < pipe->nr_cfgs-1; i++) { + if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) { + cur_fmt = &pipe->configs[i].out_fmt; + next_fmt = &pipe->configs[i+1].out_fmt; + } else { + cur_fmt = &pipe->configs[i].in_fmt; + next_fmt = &pipe->configs[i+1].in_fmt; + } + if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq, + cur_fmt->bps, + next_fmt->channels, + next_fmt->freq, next_fmt->bps)) + return true; + } + } else if (pipe->nr_cfgs > 1) { + return true; + } + + return false; +} + /* * Here, we select pipe format based on the pipe type and pipe * direction to determine the current config index for the pipeline. @@ -912,12 +949,21 @@ skl_tplg_get_pipe_config(struct skl *skl, struct skl_module_cfg *mconfig) struct skl_pipe_fmt *fmt = NULL; bool in_fmt = false; int i; + bool ret; if (pipe->nr_cfgs == 0) { pipe->cur_config_idx = 0; return 0; } + ret = is_skl_tplg_multi_fmt(skl, pipe); + if (ret) { + pipe->cur_config_idx = pipe->pipe_config_idx; + pipe->memory_pages = pconfig->mem_pages; + dev_dbg(ctx->dev, "found pipe config idx:%d\n", + pipe->cur_config_idx); + return 0; + } if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE) { dev_dbg(ctx->dev, "No conn_type detected, take 0th config\n"); pipe->cur_config_idx = 0; @@ -1778,6 +1824,58 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, return 0; } +static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; + struct skl_pipe *pipe = NULL; + u32 *pipe_id; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + if (!ec) + return -EINVAL; + + pipe_id = ec->dobj.private; + GET_PIPE(ppl, skl, node, *pipe_id, pipe); + if (!pipe) + return -EIO; + + ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx; + + return 0; +} + +static int skl_tplg_multi_config_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; + struct skl_pipe *pipe = NULL; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + u32 *pipe_id; + + if (!ec) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] > ec->items) + return -EINVAL; + + pipe_id = ec->dobj.private; + GET_PIPE(ppl, skl, node, *pipe_id, pipe); + if (!pipe) + return -EIO; + + pipe->pipe_config_idx = ucontrol->value.enumerated.item[0]; + + return 0; +} + + static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) { @@ -2571,6 +2669,11 @@ static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { .get = skl_tplg_mic_control_get, .put = skl_tplg_mic_control_set, }, + { + .id = SKL_CONTROL_TYPE_MULTI_IO_SELECT, + .get = skl_tplg_multi_config_get, + .put = skl_tplg_multi_config_set, + }, }; static int skl_tplg_fill_pipe_cfg(struct device *dev, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 02256d70f60f..07d041fc8b72 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -342,6 +342,7 @@ struct skl_pipe { struct skl_path_config configs[SKL_MAX_PATH_CONFIGS]; struct list_head w_list; bool passthru; + u32 pipe_config_idx; }; enum skl_module_state { From 471433a8e7d3a7f99730c682d4c792a09f7dd483 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Thu, 15 Jun 2017 13:25:09 +0530 Subject: [PATCH 0181/1276] ASoC: Intel: Skylake: Parse manifest data to fill DMA control parameters DMA control parameters are required in order to initialize or modify DMA gateway configuration in ADSP Firmware. These parameters are kept in the manifest data blocks and driver should read these values from this manifest. This patch parses manifest private data blocks and fill DMA control configuration structure in driver accordingly. Change-Id: Icb01a78c1869181681c7d82f49069dc666be4444 Signed-off-by: Kareem,Shaik --- include/uapi/sound/snd_sst_tokens.h | 11 +++- sound/soc/intel/skylake/skl-topology.c | 85 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl.h | 21 +++++++ 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 5d3d81af0c30..7c0149476820 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -243,6 +243,12 @@ * indicate if this endpoint is participating * in aggregation. * + * %SKL_TKN_U32_DMACTRL_CFG_IDX: + * Config index to fill up DMA control params + * + * %SKL_TKN_U32_DMACTRL_CFG_SIZE: + * Size information of DMA control params + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -339,8 +345,9 @@ enum SKL_TKNS { SKL_TKN_U32_AGG_LINK_ID, SKL_TKN_U32_AGG_CH_MASK, SKL_TKN_U32_AGG_ID, - - SKL_TKN_MAX = SKL_TKN_U32_AGG_ID, + SKL_TKN_U32_DMACTRL_CFG_IDX, + SKL_TKN_U32_DMACTRL_CFG_SIZE, + SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, }; #endif diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a1f88c3e2aa4..cd5640eac05a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3925,6 +3925,63 @@ static int skl_tplg_get_str_tkn(struct device *dev, return tkn_count; } +static int skl_tplg_mfest_fill_dmactrl(struct device *dev, + struct skl_dmactrl_config *dmactrl_cfg, + struct snd_soc_tplg_vendor_value_elem *tkn_elem) +{ + + u32 cfg_idx = dmactrl_cfg->idx; + struct skl_dmctrl_hdr *hdr = &dmactrl_cfg->hdr[cfg_idx]; + + switch (tkn_elem->token) { + case SKL_TKN_U32_FMT_CH: + hdr->ch = tkn_elem->value; + break; + + case SKL_TKN_U32_FMT_FREQ: + hdr->freq = tkn_elem->value; + break; + + case SKL_TKN_U32_FMT_BIT_DEPTH: + hdr->fmt = tkn_elem->value; + break; + + case SKL_TKN_U32_PIPE_DIRECTION: + hdr->direction = tkn_elem->value; + break; + + case SKL_TKN_U8_TIME_SLOT: + hdr->tdm_slot = tkn_elem->value; + break; + + case SKL_TKN_U32_VBUS_ID: + hdr->vbus_id = tkn_elem->value; + break; + + case SKL_TKN_U32_DMACTRL_CFG_IDX: + dmactrl_cfg->idx = tkn_elem->value; + break; + + case SKL_TKN_U32_DMACTRL_CFG_SIZE: + if (tkn_elem->value && !hdr->data) { + hdr->data = devm_kzalloc(dev, + tkn_elem->value, GFP_KERNEL); + if (!hdr->data) + return -ENOMEM; + hdr->data_size = tkn_elem->value; + } else { + hdr->data_size = 0; + dev_err(dev, "Invalid dmactrl info \n"); + } + break; + default: + dev_err(dev, "Invalid token %d\n", tkn_elem->token); + return -EINVAL; + } + + return 0; +} + static int skl_tplg_manifest_fill_fmt(struct device *dev, struct skl_module_iface *fmt, struct snd_soc_tplg_vendor_value_elem *tkn_elem, @@ -4163,8 +4220,17 @@ static int skl_tplg_get_int_tkn(struct device *dev, case SKL_TKN_U32_FMT_SAMPLE_TYPE: case SKL_TKN_U32_FMT_CH_MAP: case SKL_TKN_MM_U32_INTF_PIN_ID: - ret = skl_tplg_manifest_fill_fmt(dev, fmt, tkn_elem, - dir, pin_idx); + case SKL_TKN_U32_PIPE_DIRECTION: + case SKL_TKN_U8_TIME_SLOT: + case SKL_TKN_U32_VBUS_ID: + case SKL_TKN_U32_DMACTRL_CFG_IDX: + case SKL_TKN_U32_DMACTRL_CFG_SIZE: + if (skl->modules) + ret = skl_tplg_manifest_fill_fmt(dev, fmt, tkn_elem, + dir, pin_idx); + else + ret = skl_tplg_mfest_fill_dmactrl(dev, &skl->cfg.dmactrl_cfg, + tkn_elem); if (ret < 0) return ret; break; @@ -4267,8 +4333,9 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, { struct snd_soc_tplg_vendor_array *array; int num_blocks, block_size = 0, block_type, off = 0; + struct skl_dmctrl_hdr *dmactrl_hdr; + int cfg_idx, ret; char *data; - int ret; /* Read the NUM_DATA_BLOCKS descriptor */ array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data; @@ -4313,7 +4380,17 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, --num_blocks; } else { - return -EINVAL; + cfg_idx = skl->cfg.dmactrl_cfg.idx; + if (cfg_idx < SKL_MAX_DMACTRL) { + dmactrl_hdr = &skl->cfg.dmactrl_cfg.hdr[cfg_idx]; + if (dmactrl_hdr->data && (dmactrl_hdr->data_size == block_size)) + memcpy(dmactrl_hdr->data, data, block_size); + } else { + dev_err(dev, "error block_idx value exceeding %d\n", cfg_idx); + return -EINVAL; + } + ret = block_size; + --num_blocks; } off += ret; } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 2883d86d56fe..51cc193c3353 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -47,6 +47,8 @@ #define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) +#define SKL_MAX_DMA_CFG 24 +#define SKL_MAX_DMACTRL 7 struct skl_dsp_resource { u32 max_mcps; @@ -67,7 +69,26 @@ struct skl_astate_config { struct skl_astate_param astate_table[0]; }; +struct skl_dmctrl_hdr { + u32 vbus_id; + u32 freq; + u32 tdm_slot; + u32 fmt; + u32 direction; + u32 ch; + u32 data_size; + u32 *data; +} __packed; + +struct skl_dmactrl_config { + u32 type; + u32 size; + u32 idx; + struct skl_dmctrl_hdr hdr[SKL_MAX_DMACTRL]; +} __packed; + struct skl_fw_config { + struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From 8ef90a67f3b6496f3673faa65b1b26b574a3138c Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Thu, 15 Jun 2017 13:40:04 +0530 Subject: [PATCH 0182/1276] ASoC: Intel: Skylake: Add support for always on CLK configuration For some platforms it is required that ADSP generate BCLK, Frame_sync and MCLK regardless of whether audio stream is active or not. Clock generation is controlled by ADSP Firmware, so driver can configure that by sending DMA control IPC. The configuration for clock is prepared using DMA control manifest data. This patch prepares DMA control IPC by extracting specific ACPI NHLT blob using DMA control manifest data and appending Firmware gateway configuration to NHLT blob. Firmware Gateway configuration is available in DMA control manifest data. Finally DMA control IPC is sent to ADSP after firmware download is completed and ADSP enters D0 state. Change-Id: I65b090931c5ccaf1189c700975a1da6a772a44d8 Signed-off-by: Kareem,Shaik --- sound/soc/intel/skylake/skl-messages.c | 92 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-nhlt.c | 20 ++++++ sound/soc/intel/skylake/skl-pcm.c | 4 ++ sound/soc/intel/skylake/skl-topology.c | 11 +-- sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl.h | 3 + 6 files changed, 123 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b52f1c08d0a5..1fd9a3afe9a9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1169,6 +1169,10 @@ int skl_init_dsp(struct skl *skl) dev_dbg(bus->dev, "dsp registration status=%d\n", ret); + /* Set DMA clock controls */ + ret = skl_dsp_set_dma_clk_controls(skl->skl_sst); + if (ret < 0) + return ret; return 0; free_core_state: @@ -1291,6 +1295,9 @@ int skl_resume_dsp(struct skl *skl) skl->cfg.astate_cfg); } return ret; + + /* Set DMA clock controls */ + return skl_dsp_set_dma_clk_controls(skl->skl_sst); } enum skl_bitdepth skl_get_bit_depth(int params) @@ -1544,10 +1551,95 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); kfree(dma_ctrl); + return err; } EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control); +static u32 skl_prepare_i2s_node_id(u32 instance, u8 dev_type, + u32 dir, u32 time_slot) +{ + union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; + + node_id.node.dma_type = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? + SKL_DMA_I2S_LINK_OUTPUT_CLASS : + SKL_DMA_I2S_LINK_INPUT_CLASS; + ssp_node.dma_node.time_slot_index = time_slot; + ssp_node.dma_node.i2s_instance = instance; + node_id.node.vindex = ssp_node.val; + + return node_id.val; +} + +int skl_dsp_set_dma_clk_controls(struct skl_sst *ctx) +{ + struct nhlt_specific_cfg *cfg = NULL; + struct skl *skl = get_skl_ctx(ctx->dev); + struct skl_dmactrl_config *dmactrl_cfg = &skl->cfg.dmactrl_cfg; + struct skl_dmctrl_hdr *hdr; + u8 *dma_ctrl_config; + void *i2s_config = NULL; + u32 i2s_config_size, node_id; + int i, ret = 0; + + if (!skl->cfg.dmactrl_cfg.size) + return 0; + + for (i = 0; i < SKL_MAX_DMACTRL; i++) { + hdr = &dmactrl_cfg->hdr[i]; + + /* get nhlt specific config info */ + cfg = skl_get_nhlt_specific_cfg(skl, hdr->vbus_id, + NHLT_LINK_SSP, hdr->fmt, + hdr->ch, hdr->freq, + hdr->direction, NHLT_DEVICE_I2S); + + if (cfg && hdr->data_size) { + print_hex_dump(KERN_DEBUG, "NHLT blob Info:", + DUMP_PREFIX_OFFSET, 8, 4, + cfg->caps, cfg->size, false); + + i2s_config_size = cfg->size + hdr->data_size; + i2s_config = kzalloc(i2s_config_size, GFP_KERNEL); + if (!i2s_config) + return -ENOMEM; + + /* copy blob */ + memcpy(i2s_config, cfg->caps, cfg->size); + + /* copy additional dma controls informatioin */ + dma_ctrl_config = (u8 *)i2s_config + cfg->size; + memcpy(dma_ctrl_config, hdr->data, hdr->data_size); + + print_hex_dump(KERN_DEBUG, "Blob + DMA Control Info:", + DUMP_PREFIX_OFFSET, 8, 4, + i2s_config, i2s_config_size, false); + + /* get node id */ + node_id = skl_prepare_i2s_node_id(hdr->vbus_id, + SKL_DEVICE_I2S, + hdr->direction, + hdr->tdm_slot); + + ret = skl_dsp_set_dma_control(ctx, (u32 *)i2s_config, + i2s_config_size, node_id); + + kfree(i2s_config); + + if (ret < 0) + return ret; + + } else { + dev_err(ctx->dev, "Failed to get NHLT config: vbusi_id=%d ch=%d fmt=%d s_rate=%d\n", + hdr->vbus_id, hdr->ch, hdr->fmt, hdr->freq); + return -EIO; + } + } + + return 0; +} + static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index cf3d38136289..742b0cb0dd15 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -149,6 +149,26 @@ static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, return false; } +struct nhlt_specific_cfg * +skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, + u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type) +{ + struct nhlt_specific_cfg *cfg = NULL; + struct hdac_ext_bus *ebus = &skl->ebus; + + /* update the blob based on virtual bus_id*/ + if (!skl->nhlt_override) { + dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); + cfg = skl_get_ep_blob(skl, instance, link_type, s_fmt, + num_ch, s_rate, dir, dev_type); + } else { + dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from Debugfs!!\n"); + cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, instance, dir); + } + + return cfg; +} + struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1a722d2fa87e..7fda668d9fc9 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1845,6 +1845,10 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) dev_err(component->dev, "Failed to boot first fw: %d\n", ret); return ret; } + + /* Set DMA clock controls */ + skl_dsp_set_dma_clk_controls(skl->skl_sst); + skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; skl_dsp_enable_notification(skl->skl_sst, false); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index cd5640eac05a..24c08dca8bd5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2548,18 +2548,13 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, mconfig->formats_config.caps = (u32 *) sdw_cfg; return 0; } + /* update the blob based on virtual bus_id*/ - if (!skl->nhlt_override) { - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + cfg = skl_get_nhlt_specific_cfg(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, params->s_freq, params->stream, dev_type); - } else { - dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); - cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, - link_type, mconfig->vbus_id, - params->stream); - } + if (cfg) { mconfig->formats_config.caps_size = cfg->size; mconfig->formats_config.caps = (u32 *) &cfg->caps; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 07d041fc8b72..8dbe73ee3d59 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -530,6 +530,7 @@ struct fw_ipc_data { int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); +int skl_dsp_set_dma_clk_controls(struct skl_sst *ctx); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, u32 caps_size, u32 node_id); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 51cc193c3353..318b3c54c44e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -172,6 +172,9 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn, u8 dev_type); +struct nhlt_specific_cfg * +skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, + u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type); int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); From 23dac881dde0abc553146304ab127c31f7edf52e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:13:49 +0530 Subject: [PATCH 0183/1276] ASoC: Intel: bxtn: Initialize fw tracing window for bxt Initializing the tracing window for the platform along with the firmware write pointers Change-Id: Ibb735215c6bd0af8abc0e1146b28b4961277665b Signed-off-by: Panwar, Ashish Reviewed-by: Babu, Ramesh Signed-off-by: Mohit Sinha --- sound/soc/intel/skylake/bxt-sst.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index c1f6d8b6ab79..9c8d923fe358 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -21,7 +21,7 @@ #include #include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" +#include "skl-fwlog.h" #include "skl-sst-ipc.h" #define BXT_BASEFW_TIMEOUT 3000 @@ -32,6 +32,13 @@ #define BXT_ROM_INIT 0x5 #define BXT_ADSP_SRAM0_BASE 0x80000 +/* Trace Buffer Window */ +#define BXT_ADSP_SRAM2_BASE 0x0C0000 +#define BXT_ADSP_W2_SIZE 0x2000 +#define BXT_ADSP_WP_DSP0 (BXT_ADSP_SRAM0_BASE+0x30) +#define BXT_ADSP_WP_DSP1 (BXT_ADSP_SRAM0_BASE+0x34) +#define BXT_ADSP_NR_DSP 2 + /* Firmware status window */ #define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE #define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) @@ -570,6 +577,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, { struct skl_sst *skl; struct sst_dsp *sst; + u32 dsp_wp[] = {BXT_ADSP_WP_DSP0, BXT_ADSP_WP_DSP1}; int ret; ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); @@ -590,6 +598,12 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + ret = skl_dsp_init_trace_window(sst, dsp_wp, BXT_ADSP_SRAM2_BASE, + BXT_ADSP_W2_SIZE, BXT_ADSP_NR_DSP); + if (ret) { + dev_err(dev, "FW tracing init failed : %x", ret); + return ret; + } ret = skl_ipc_init(dev, skl); if (ret) { From e340f5bc1a3c0ada3dc2b62c6e8d8ab8229de3b4 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:10:32 +0530 Subject: [PATCH 0184/1276] ASoC: Intel: Board: DAI links for probe in GPMRB machine driver Added two DAI links for probe playback and capture Change-Id: I0bf364eba3b6a2b779625a6fd1b664c2530a1ab2 Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 027060b17322..1e2b8be00127 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -78,6 +78,27 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From f6411ebe488bfd772479262062bea727098a2546 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:21:19 +0530 Subject: [PATCH 0185/1276] ASoC: Intel: Boards: Add FW logging DAI-links for GPMRB Add two FW logging DAI for each DSP core Change-Id: Ic825ecb4afbbcacabda6b74e2e5f2969fc722a1f Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 1e2b8be00127..325b59adaf1c 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -99,6 +99,27 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .init = NULL, .nonatomic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From 4f4add8fe50d5e1d078920fc18f02e7a91fa3370 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 25 Feb 2016 11:13:35 +0530 Subject: [PATCH 0186/1276] ASoC: Intel: Skylake: Send correct size in ipc header for large config To query information from FW we use "Large Config Get" message which accept arguments in the form of extended params. So we need to send a TX message of fixed size (8 bytes) to retrieve a large reply. Hence, IPC header should reflect correct size of TX message for these messages. Change-Id: Ib055c879d6e9dc00e8c861ab25ea9d9080e98732 Signed-off-by: Pardha Saradhi K Reviewed-by: Babu, Ramesh Signed-off-by: Mohit Sinha --- sound/soc/intel/skylake/skl-sst-ipc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 508382d52e04..14802ab7ea20 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1031,7 +1031,11 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); header.primary |= IPC_MOD_ID(msg->module_id); - header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + if (!tx_bytes) + header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + else + header.extension = IPC_DATA_OFFSET_SZ(tx_bytes); + header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); header.extension |= IPC_FINAL_BLOCK(1); header.extension |= IPC_INITIAL_BLOCK(1); From ae8f58868440f829baeab6dfda197494de1acafc Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Sun, 18 Jun 2017 09:52:40 +0530 Subject: [PATCH 0187/1276] ASoC: Intel: board: Add support for HDMI in cnl_rt274 To enable HDMI/iDisp, corresponding BE DAI links are defined in the machine driver. FE links will come topology with an assumption that the dai link name will consist the string "HDMI". This assumption is made to distinguish other dai links from hdmi dai links. Special handling is needed for hdmi dai links because hdmi jack is mapped for each hdmi BE dai link with the corresponding pcm device. And since FE links come from topology, they can come in any order. Hence the need to keep a track of pcm_count. Change-Id: I6fbdfbdb61d5fa58691cfe84abbd859209ccfce5 Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_rt274.c | 129 ++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index ebfe74132da5..e9e5959e256a 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -33,11 +33,27 @@ #include #include +#include "../../codecs/hdac_hdmi.h" #include "../../codecs/rt274.h" #define CNL_FREQ_OUT 19200000 #define CNL_BE_FIXUP_RATE 48000 #define RT274_CODEC_DAI "rt274-aif1" +#define CNL_NAME_SIZE 32 +#define CNL_MAX_HDMI 3 + +static struct snd_soc_jack cnl_hdmi[CNL_MAX_HDMI]; + +struct cnl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct cnl_rt274_private { + struct list_head hdmi_pcm_list; + int pcm_count; +}; static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, const char *dai_name) @@ -158,6 +174,13 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"Headphone Jack", NULL, "Platform Clock"}, {"MIC", NULL, "Platform Clock"}, + + {"hifi1", NULL, "iDisp1 Tx"}, + {"iDisp1 Tx", NULL, "iDisp1_out"}, + {"hifi2", NULL, "iDisp2 Tx"}, + {"iDisp2 Tx", NULL, "iDisp2_out"}, + {"hifi3", NULL, "iDisp3 Tx"}, + {"iDisp3 Tx", NULL, "iDisp3_out"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -307,6 +330,36 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, /* codec-codec link */ { .name = "CNL SSP0-Loop Port", @@ -326,10 +379,67 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { static int cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { - link->platform_name = pname; - link->nonatomic = 1; + struct cnl_rt274_private *ctx = snd_soc_card_get_drvdata(card); + char hdmi_dai_name[CNL_NAME_SIZE]; + struct cnl_hdmi_pcm *pcm; + + link->platform_name = pname; + link->nonatomic = 1; + + /* Assuming HDMI dai link will consist the string "HDMI" */ + if (strstr(link->name, "HDMI")) { + static int i = 1; /* hdmi codec dai name starts from index 1 */ + + pcm = devm_kzalloc(card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; - return 0; + snprintf(hdmi_dai_name, sizeof(hdmi_dai_name), "intel-hdmi-hifi%d", i++); + pcm->codec_dai = cnl_get_codec_dai(card, hdmi_dai_name); + if (!pcm->codec_dai) + return -EINVAL; + + pcm->device = ctx->pcm_count; + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + } + ctx->pcm_count++; + + return 0; +} + +static int cnl_card_late_probe(struct snd_soc_card *card) +{ + struct cnl_rt274_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[CNL_NAME_SIZE]; + struct cnl_hdmi_pcm *pcm; + int err, i = 0; + + if (list_empty(&ctx->hdmi_pcm_list)) + return 0; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &cnl_hdmi[i], + NULL, 0); + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, + pcm->device, &cnl_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); } /* SoC card */ @@ -344,11 +454,24 @@ static struct snd_soc_card snd_soc_card_cnl = { .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), .add_dai_link = cnl_add_dai_link, + .fully_routed = true, + .late_probe = cnl_card_late_probe, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) { + struct cnl_rt274_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->pcm_count = ARRAY_SIZE(cnl_rt274_msic_dailink); + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + snd_soc_card_cnl.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl, ctx); + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); } From 19b2f9b9adc9db1eff812a451810c6b19bc20462 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Tue, 4 Jul 2017 18:39:18 +0530 Subject: [PATCH 0188/1276] ASoC: Intel: Skylake: Support for DSP exception record dump In the cases where the DSP encounters an exception during its execution, the record is stored in the FW registers window, aligned towards the end. This data is read by the driver and is passed to the user space using the linux coredump framework The record contains data on a per core basis is dumped to the userspace in scenario - when DSP sends an EXCEPTION_CAUGHT IPC Change-Id: I1d2ac3bca545db7fe5d13c1a0d6ab850da4d7984 Signed-off-by: Mousumi Jana Signed-off-by: Giribabu Gogineni --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 17 +++- sound/soc/intel/skylake/skl-sst-ipc.h | 13 --- sound/soc/intel/skylake/skl-sst-utils.c | 114 +++++++++++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 1 + 5 files changed, 131 insertions(+), 15 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 9d2be10e484c..c08d87821c9f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -115,6 +115,7 @@ config SND_SOC_INTEL_SKYLAKE select SND_SOC_ACPI_INTEL_MATCH select SDW select SDW_CNL + select WANT_DEV_COREDUMP help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 14802ab7ea20..cfdf2ce3733b 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -21,6 +21,7 @@ #include "skl-sst-ipc.h" #include "skl-fwlog.h" #include "sound/hdaudio_ext.h" +#include "skl-topology.h" #define IPC_IXC_STATUS_BITS 24 @@ -271,7 +272,9 @@ enum skl_ipc_notification_type { IPC_GLB_NOTIFY_RESOURCE_EVENT = 5, IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6, IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, - IPC_GLB_NOTIFY_FW_READY = 8 + IPC_GLB_NOTIFY_FW_READY = 8, + IPC_GLB_NOTIFY_FW_AUD_CLASS_RESULT = 9, + IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10 }; /* Module Message Types */ @@ -406,6 +409,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc); + int ret; if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { switch (IPC_GLB_NOTIFY_TYPE(header.primary)) { @@ -440,6 +444,17 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, skl->enable_miscbdcge(ipc->dev, false); skl->miscbdcg_disabled = true; break; + case IPC_GLB_NOTIFY_EXCEPTION_CAUGHT: + dev_err(ipc->dev, "*****Exception Detected **********\n"); + /* hexdump of the fw core exception record reg */ + ret = skl_dsp_crash_dump_read(skl); + if (ret < 0) { + dev_err(ipc->dev, + "dsp crash dump read fail:%d\n", ret); + return ret; + } + break; + default: dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index d6866bc15469..ad437be40334 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -375,19 +375,6 @@ struct sw_version { u16 build; } __packed; -struct skl_dsp_core_dump { - u16 type0; - u16 length0; - u32 crash_dump_ver; - u16 bus_dev_id; - u16 cavs_hw_version; - struct fw_version fw_ver; - struct sw_version sw_ver; - u16 type2; - u16 length2; - u32 fwreg[FW_REG_SZ]; -} __packed; - struct skl_module_notify { u32 unique_id; u32 event_id; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 7288893ce61a..8f4e3074de17 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "skl-sst-dsp.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -23,7 +25,11 @@ #define UUID_STR_SIZE 37 - +#define TYPE0_EXCEPTION 0 +#define TYPE1_EXCEPTION 1 +#define TYPE2_EXCEPTION 2 +#define MAX_CRASH_DATA_TYPES 3 +#define CRASH_DUMP_VERSION 0x1 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 @@ -126,6 +132,31 @@ struct skl_ext_manifest_hdr { u32 entries; }; +struct adsp_crash_hdr { + u16 type; + u16 length; + char data[0]; +} __packed; + +struct adsp_type0_crash_data { + u32 crash_dump_ver; + u16 bus_dev_id; + u16 cavs_hw_version; + struct fw_version fw_ver; + struct sw_version sw_ver; +} __packed; + +struct adsp_type1_crash_data { + u32 mod_uuid[4]; + u32 hash[2]; + u16 mod_id; + u16 rsvd; +} __packed; + +struct adsp_type2_crash_data { + u32 fwreg[FW_REG_SZ]; +} __packed; + static int skl_get_pvtid_map(struct uuid_module *module, int instance_id) { int pvt_id; @@ -261,6 +292,87 @@ int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id) } EXPORT_SYMBOL_GPL(skl_put_pvt_id); + + +int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) +{ + int num_mod = 0, size_core_dump, sz_ext_dump = 0, idx = 0; + struct uuid_module *module, *module1; + void *coredump, *ext_core_dump; + void *fw_reg_addr, *offset; + struct pci_dev *pci = to_pci_dev(ctx->dsp->dev); + u16 length0, length1, length2; + struct adsp_crash_hdr *crash_data_hdr; + struct adsp_type0_crash_data *type0_data; + struct adsp_type1_crash_data *type1_data; + struct adsp_type2_crash_data *type2_data; + + if (list_empty(&ctx->uuid_list)) + dev_info(ctx->dev, "Module list is empty\n"); + + list_for_each_entry(module1, &ctx->uuid_list, list) { + num_mod++; + } + + /* Length representing in DWORD */ + length0 = sizeof(*type0_data) / sizeof(u32); + length1 = (num_mod * sizeof(*type1_data)) / sizeof(u32); + length2 = sizeof(*type2_data) / sizeof(u32); + + /* type1 data size is calculated based on number of modules */ + size_core_dump = (MAX_CRASH_DATA_TYPES * sizeof(*crash_data_hdr)) + + sizeof(*type0_data) + (num_mod * sizeof(*type1_data)) + + sizeof(*type2_data); + + coredump = vzalloc(size_core_dump); + if (!coredump) + return -ENOMEM; + + offset = coredump; + + /* Fill type0 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE0_EXCEPTION; + crash_data_hdr->length = length0; + offset += sizeof(*crash_data_hdr); + type0_data = (struct adsp_type0_crash_data *) offset; + type0_data->crash_dump_ver = CRASH_DUMP_VERSION; + type0_data->bus_dev_id = pci->device; + offset += sizeof(*type0_data); + + /* Fill type1 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE1_EXCEPTION; + crash_data_hdr->length = length1; + offset += sizeof(*crash_data_hdr); + type1_data = (struct adsp_type1_crash_data *) offset; + list_for_each_entry(module, &ctx->uuid_list, list) { + memcpy(type1_data->mod_uuid, &(module->uuid), + (sizeof(type1_data->mod_uuid))); + memcpy(type1_data->hash, &(module->hash), + (sizeof(type1_data->hash))); + memcpy(&type1_data->mod_id, &(module->id), + (sizeof(type1_data->mod_id))); + type1_data++; + } + offset += (num_mod * sizeof(*type1_data)); + + /* Fill type2 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE2_EXCEPTION; + crash_data_hdr->length = length2; + offset += sizeof(*crash_data_hdr); + type2_data = (struct adsp_type2_crash_data *) offset; + fw_reg_addr = (void __force*)(ctx->dsp->mailbox.in_base - + ctx->dsp->addr.w0_stat_sz); + memcpy_fromio(type2_data->fwreg, (const void __iomem *)fw_reg_addr, + sizeof(*type2_data)); + + dev_coredumpv(ctx->dsp->dev, coredump, + size_core_dump, GFP_KERNEL); + return 0; +} + /* * Parse the firmware binary to get the UUID, module id * and loadable flags diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8dbe73ee3d59..b3a15ed76122 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -613,4 +613,5 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); +int skl_dsp_crash_dump_read(struct skl_sst *ctx); #endif From f7f53de6d8e19c82cf5dd3e328e03d6747b6eef3 Mon Sep 17 00:00:00 2001 From: Dronamraju Santosh P K Date: Tue, 25 Jul 2017 08:51:27 +0530 Subject: [PATCH 0189/1276] ASoC: Intel: board: Separate out icl_rt274 from cnl_rt274 Since HDMI is enabled on cnl_rt274 machine driver and HDMI codec is not available with ICL FPGA, another machine driver for ICL is created without HDMI. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d308 Signed-off-by: Dronamraju Santosh P K Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 2 - sound/soc/intel/boards/icl_rt274.c | 373 +++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/boards/icl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d8b639c426fa..e655978d3418 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -340,6 +340,17 @@ config SND_SOC_INTEL_CNL_RT274_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_ICL_RT274_MACH + tristate "Icelake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Icelake platform with + RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_BXT_TDF8532_MACH tristate "Broxton with TDF8532 I2S mode" depends on MFD_INTEL_LPSS && I2C && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index dcd898c0b4a2..239051cc6f22 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -24,6 +24,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl-rt274-objs := cnl_rt274.o +snd-soc-icl-rt274-objs := icl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o @@ -53,6 +54,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o +obj-$(CONFIG_SND_SOC_INTEL_ICL_RT274_MACH) += snd-soc-icl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index e9e5959e256a..07a876f4f3b2 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -477,7 +477,6 @@ static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) static const struct platform_device_id cnl_board_ids[] = { { .name = "cnl_rt274" }, - { .name = "icl_rt274" }, { } }; @@ -495,4 +494,3 @@ module_platform_driver(snd_cnl_rt274_driver); MODULE_AUTHOR("Guneshwor Singh "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cnl_rt274"); -MODULE_ALIAS("platform:icl_rt274"); diff --git a/sound/soc/intel/boards/icl_rt274.c b/sound/soc/intel/boards/icl_rt274.c new file mode 100644 index 000000000000..f4d855766b81 --- /dev/null +++ b/sound/soc/intel/boards/icl_rt274.c @@ -0,0 +1,373 @@ +/* + * icl_rt274.c - ASOC Machine driver for ICL + * + * Copyright (C) 2016 Intel Corp + * Author: Dronamraju Santosh Pavan Kumar + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +#define ICL_FREQ_OUT 19200000 +#define ICL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *icl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int icl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = icl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + ICL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ICL_BE_FIXUP_RATE * ratio, + ICL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + +static struct snd_soc_jack icl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin icl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new icl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget icl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + icl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int icl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route icl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, +}; + +static int icl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &icl_headset, + icl_headset_pins, ARRAY_SIZE(icl_headset_pins)); + + if (ret) + return ret; + + snd_soc_component_set_jack(component, &icl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static int icl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = ICL_BE_FIXUP_RATE; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link icl_rt274_msic_dailink[] = { + /* Trace Buffer DAI links */ + { + .name = "ICL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "ICL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "ICL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = icl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = icl_rt274_init, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = icl_dmic_fixup, + }, + /* codec-codec link */ + { + .name = "ICL SSP0-Loop Port", + .stream_name = "ICL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, +}; + +static int +icl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_icl = { + .name = "icl-audio", + .dai_link = icl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(icl_rt274_msic_dailink), + .dapm_widgets = icl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(icl_rt274_widgets), + .dapm_routes = icl_map, + .num_dapm_routes = ARRAY_SIZE(icl_map), + .controls = icl_controls, + .num_controls = ARRAY_SIZE(icl_controls), + .add_dai_link = icl_add_dai_link, +}; + +static int snd_icl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_icl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_icl); +} + +static const struct platform_device_id icl_board_ids[] = { + { .name = "icl_rt274" }, + { } +}; + +static struct platform_driver snd_icl_rt274_driver = { + .driver = { + .name = "icl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_icl_rt274_mc_probe, + .id_table = icl_board_ids, +}; + +module_platform_driver(snd_icl_rt274_driver); + +MODULE_AUTHOR("Dronamraju Santosh pavan Kumar "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:icl_rt274"); From 43afd109451444e3b7f76ce456b492316367ff30 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Fri, 4 Aug 2017 16:06:57 +0530 Subject: [PATCH 0190/1276] ASoC: Intel: Skylake: Removed duplicate IPC call for Probe Injector DMA Removed duplicate IPC call for attaching DMA for Probe Injector. Change-Id: I12d8bd73ba5203a697cdbe1caee0747eb16344b1 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Babu, Ramesh Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 24c08dca8bd5..29be5b7da3ba 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -544,9 +544,6 @@ int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, pconfig->iprobe[index].state); } - ret = skl_set_module_params(ctx, (u32 *)&ad, - sizeof(struct skl_probe_attach_inj_dma), - 1, mconfig); return ret; } From 080f07935f8f45a6a65aaf0fe517155bfff42d80 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Thu, 20 Jul 2017 16:39:00 +0530 Subject: [PATCH 0191/1276] ASoC: Intel: Skylake: Notify topology changes Some events like pipeline start, pipeline delete, DSP D0/D3 need to be notified to the user in order to convey a change in the topology. Support for notifying such events has been add using kcontrol. This kcontrol reports time at which the last change occurred in the topology. Change-Id: I3745a5a6d7034cb95bea13ba47f8d6eaf76f5a43 Signed-off-by: Giribabu Gogineni Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 16 ++++++ sound/soc/intel/skylake/bxt-sst.c | 10 ++++ sound/soc/intel/skylake/skl-messages.c | 8 +++ sound/soc/intel/skylake/skl-pcm.c | 10 ++++ sound/soc/intel/skylake/skl-sst-dsp.h | 16 ++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 6 +++ sound/soc/intel/skylake/skl-sst-utils.c | 19 +++++++ sound/soc/intel/skylake/skl-topology.c | 72 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 3 ++ 9 files changed, 160 insertions(+) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 7c0149476820..0f74eeb85995 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -350,4 +350,20 @@ enum SKL_TKNS { SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, }; +/* + * Topology change notification events along with time at which + * the change occurred in topology. + */ +enum skl_event_type { + SKL_TPLG_CHG_NOTIFY_PIPELINE_START = 1, + SKL_TPLG_CHG_NOTIFY_PIPELINE_DELETE, + SKL_TPLG_CHG_NOTIFY_DSP_D0, + SKL_TPLG_CHG_NOTIFY_DSP_D3, +}; + +struct skl_tcn_events { + enum skl_event_type type; + struct timeval tv; +}; + #endif diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 9c8d923fe358..c7f7c1529354 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -498,6 +498,11 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) } skl->cores.state[core_id] = SKL_DSP_RUNNING; + ret = skl_notify_tplg_change(skl, SKL_TPLG_CHG_NOTIFY_DSP_D0); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event D0 failed\n"); + return 0; err: if (core_id == SKL_DSP_CORE0_ID) @@ -544,6 +549,11 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) return ret; } skl->cores.state[core_id] = SKL_DSP_RESET; + ret = skl_notify_tplg_change(skl, SKL_TPLG_CHG_NOTIFY_DSP_D3); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event D3 failed\n"); + return 0; } diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 1fd9a3afe9a9..8560cd65e77c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2479,6 +2479,10 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) pipe->state = SKL_PIPE_INVALID; skl_dbg_event(ctx, pipe->state); + ret = skl_notify_tplg_change(ctx, SKL_TPLG_CHG_NOTIFY_PIPELINE_DELETE); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event delete pipe failed\n"); return ret; } @@ -2514,6 +2518,10 @@ int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_STARTED; + ret = skl_notify_tplg_change(ctx, SKL_TPLG_CHG_NOTIFY_PIPELINE_START); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event run pipe failed\n"); return 0; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7fda668d9fc9..8209c954b7ad 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -967,6 +967,10 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { .shutdown = skl_sdw_shutdown, }; +struct skl_dsp_notify_ops cb_ops = { + .notify_cb = skl_dsp_cb_event, +}; + static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", @@ -1821,6 +1825,8 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } + skl->component = component; + /* load the firmwares, since all is set */ ops = skl_get_dsp_ops(skl->pci->device); if (!ops) @@ -1851,6 +1857,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; + skl->skl_sst->notify_ops = cb_ops; skl_dsp_enable_notification(skl->skl_sst, false); if (skl->cfg.astate_cfg != NULL) { @@ -1879,6 +1886,9 @@ static const struct soc_enum dsp_log_enum = static struct snd_kcontrol_new skl_controls[] = { SOC_ENUM_EXT("DSP Log Level", dsp_log_enum, skl_tplg_dsp_log_get, skl_tplg_dsp_log_set), + SND_SOC_BYTES_TLV("Topology Change Notification", + sizeof(struct skl_tcn_events), skl_tplg_change_notification_get, + NULL), }; static const struct snd_soc_component_driver skl_component = { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index baec42299419..6736c89c6520 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "skl-sst-cldma.h" struct sst_dsp; @@ -236,6 +237,17 @@ struct uuid_module { u8 hash[DEFAULT_HASH_SHA256_LEN]; }; +struct skl_notify_data { + u32 type; + u32 length; + struct skl_tcn_events tcn_data; +}; + +struct skl_dsp_notify_ops { + int (*notify_cb)(struct skl_sst *skl, unsigned int event, + struct skl_notify_data *notify_data); +}; + struct skl_load_module_info { u16 mod_id; const struct firmware *fw; @@ -323,4 +335,8 @@ void bxt_set_dsp_D0i3(struct work_struct *work); int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *fw_modules_kobj); void skl_module_sysfs_exit(struct skl_sst *ctx); + +int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, + struct skl_notify_data *notify_data); + #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ad437be40334..bc083b0a0d4f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -29,6 +29,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 #define FW_REG_SZ 1024 +#define SKL_TPLG_CHG_NOTIFY 3 enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, @@ -283,6 +284,8 @@ struct skl_sst { /* Callback to update D0i3C register */ void (*update_d0i3c)(struct device *dev, bool enable); + struct skl_dsp_notify_ops notify_ops; + struct skl_d0i3_data d0i3; const struct skl_dsp_ops *dsp_ops; @@ -308,6 +311,8 @@ struct skl_sst { /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; + + struct snd_kcontrol *kcontrol; }; struct skl_ipc_init_instance_msg { @@ -457,4 +462,5 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header); void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); +int skl_notify_tplg_change(struct skl_sst *ctx, int type); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 8f4e3074de17..f20b842af4e4 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -881,6 +881,25 @@ static int skl_parse_fw_config_info(struct sst_dsp *ctx, return ret; } +int skl_notify_tplg_change(struct skl_sst *ctx, int type) +{ + struct skl_notify_data *notify_data; + + notify_data = kzalloc(sizeof(*notify_data), GFP_KERNEL); + if (!notify_data) + return -ENOMEM; + + notify_data->type = 0xFF; + notify_data->length = sizeof(struct skl_tcn_events); + notify_data->tcn_data.type = type; + do_gettimeofday(&(notify_data->tcn_data.tv)); + ctx->notify_ops.notify_cb(ctx, SKL_TPLG_CHG_NOTIFY, notify_data); + kfree(notify_data); + + return 0; +} +EXPORT_SYMBOL_GPL(skl_notify_tplg_change); + int skl_get_firmware_configuration(struct sst_dsp *ctx) { struct skl_ipc_large_config_msg msg; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 29be5b7da3ba..373dbced8efa 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2642,6 +2642,78 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } +/* + * Get the events along with data stored in notify_data and pass + * to kcontrol private data. + */ +int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, + struct skl_notify_data *notify_data) +{ + struct snd_soc_card *card; + struct soc_bytes_ext *sb; + struct skl *skl = get_skl_ctx(ctx->dev); + struct snd_soc_component *component = skl->component; + struct skl_module_notify *m_notification = NULL; + struct skl_algo_data *bc; + u8 param_length; + + switch (event) { + case SKL_TPLG_CHG_NOTIFY: + card = component->card; + + if (!ctx->kcontrol) { + ctx->kcontrol = snd_soc_card_get_kcontrol(card, + "Topology Change Notification"); + if (!ctx->kcontrol) { + dev_dbg(ctx->dev, + "NOTIFICATION Controls not found\n"); + return -EINVAL; + } + } + + sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + if (!sb->dobj.private) { + sb->dobj.private = devm_kzalloc(ctx->dev, + sizeof(*notify_data), GFP_KERNEL); + if (!sb->dobj.private) + return -ENOMEM; + } + + memcpy(sb->dobj.private, notify_data, sizeof(*notify_data)); + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, + &ctx->kcontrol->id); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * Get last topology change events like pipeline start, pipeline delete, + * DSP D0/D3 and notify to user along with time at which last change occurred + * in topology. + */ +int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size) +{ + struct skl_notify_data *notify_data; + struct soc_bytes_ext *sb = + (struct soc_bytes_ext *)kcontrol->private_value; + + if (sb->dobj.private) { + notify_data = (struct skl_notify_data *)sb->dobj.private; + if (copy_to_user(data, notify_data, sizeof(*notify_data))) + return -EFAULT; + /* Clear the data after copy to user as per requirement */ + memset(notify_data, 0, sizeof(*notify_data)); + } + + return 0; +} + static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { {SKL_MIXER_EVENT, skl_tplg_mixer_event}, {SKL_VMIXER_EVENT, skl_tplg_mixer_event}, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b3a15ed76122..ce4069607dda 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -614,4 +614,7 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); int skl_dsp_crash_dump_read(struct skl_sst *ctx); + +int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size); #endif From 98943ca9170419fe8efa6bac90d855714a2b15ee Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 10 Aug 2017 12:28:20 +0530 Subject: [PATCH 0192/1276] ASoC: rt700: Remove unused variables. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d313 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/rt700.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index c88aa16a4280..03bab55537b6 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -557,8 +557,6 @@ static int rt700_index_read(struct regmap *regmap, static int rt700_hda_read(struct regmap *regmap, unsigned int vid, unsigned int nid, unsigned int pid, unsigned int *value) { - int ret; - unsigned int val_h, val_l; unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; unsigned int sdw_addr_h, sdw_addr_l; @@ -1108,8 +1106,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct rt700_priv *rt700 = snd_soc_codec_get_drvdata(codec); - unsigned int sdw_data_0; pr_debug("%s level=%d\n", __func__, level); switch (level) { @@ -1411,8 +1407,7 @@ static ssize_t rt700_index_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l; - unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int sdw_data_0; int i, cnt = 0; /* index */ @@ -1433,8 +1428,7 @@ static ssize_t rt700_index_cmd_store(struct device *dev, const char *buf, size_t count) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; - unsigned int index_reg, index_val; + unsigned int index_reg = 0, index_val = 0; int i; pr_debug("register \"%s\" count=%zu\n", buf, count); @@ -1480,8 +1474,6 @@ static ssize_t rt700_hda_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l; - unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; int i, cnt = 0; unsigned int value; @@ -1605,9 +1597,9 @@ static DEVICE_ATTR(hda_reg, 0664, rt700_hda_cmd_show, rt700_hda_cmd_store); static int rt700_clock_config(struct device *dev, struct alc700 *alc700) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - int value, read_value1, read_value2; + int value; - switch(alc700->params->bus_clk_freq) { + switch (alc700->params->bus_clk_freq) { case RT700_CLK_FREQ_12000000HZ: value = 0x0; break; @@ -1706,7 +1698,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, struct rt700_priv *rt700; struct alc700 *alc700 = dev_get_drvdata(dev); int ret; - unsigned int value; rt700 = devm_kzalloc(dev, sizeof(struct rt700_priv), GFP_KERNEL); @@ -1868,7 +1859,7 @@ static int rt700_runtime_resume(struct device *dev) int ret; int timeout = 0; - if(rt700->sdw) { + if (rt700->sdw) { ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, rt700->sdw); if (ret < 0) From 50cd66d03dfc83c4da85f1230bed09dff973ded9 Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 10 Aug 2017 12:24:43 +0530 Subject: [PATCH 0193/1276] ASoC: rt700: Remove prints used for debugging. While integrating rt700 codec driver prints were added for debugging purpose. Hence removing them. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d312 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/rt700-sdw.c | 3 -- sound/soc/codecs/rt700.c | 74 ++---------------------------------- 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 8a7fbc5a40c7..62f7e2989ca8 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -364,11 +364,8 @@ static int rt700_sdw_handle_impl_def_interrupts(struct sdw_slv *swdev, struct rt700_priv *rt700 = dev_get_drvdata(&swdev->dev); bool hp, mic; - pr_debug("%s control_port_stat=%x port0_stat=%x\n", __func__, - intr_status->control_port_stat, intr_status->port0_stat); if (intr_status->control_port_stat & 0x4) { rt700_jack_detect(rt700, &hp, &mic); - pr_info("%s hp=%d mic=%d\n", __func__, hp, mic); } return 0; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 03bab55537b6..a654c092b8c5 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -569,12 +569,10 @@ static int rt700_hda_read(struct regmap *regmap, unsigned int vid, hda_to_sdw(nid, vid, pid, &sdw_addr_h, &sdw_data_1, &sdw_addr_l, &sdw_data_0); - pr_debug("write %04x %02x\n", sdw_addr_h, sdw_data_1); regmap_write(regmap, sdw_addr_h, sdw_data_1); - if (sdw_addr_l) { - pr_debug("write %04x %02x", sdw_addr_l, sdw_data_0); + if (sdw_addr_l) regmap_write(regmap, sdw_addr_l, sdw_data_0); - } + regmap_read(regmap, RT700_READ_HDA_3, &sdw_data_3); regmap_read(regmap, @@ -583,11 +581,6 @@ static int rt700_hda_read(struct regmap *regmap, unsigned int vid, RT700_READ_HDA_1, &sdw_data_1); regmap_read(regmap, RT700_READ_HDA_0, &sdw_data_0); - pr_debug("(%03x %02x %04x) = %02x%02x%02x%02x\n", - vid, nid, pid, sdw_data_3, - sdw_data_2, sdw_data_1, sdw_data_0); - } else { - pr_err("%s: it is not a get verb\n", __func__); } *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); @@ -627,20 +620,14 @@ static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, { /* R Channel */ regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); regmap_read(rt700->regmap, RT700_READ_HDA_0, r_val); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *r_val); /* L Channel */ val_h |= 0x20; regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); regmap_read(rt700->regmap, RT700_READ_HDA_0, l_val); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *l_val); } /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ @@ -672,9 +659,6 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, addr_h = mc->reg; addr_l = mc->rreg; - pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]); - /* L Channel */ if (mc->invert) { /* for mute */ @@ -712,38 +696,23 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, } for (i = 0; i < 3; i++) { /* retry 3 times at most */ - pr_debug("%s i=%d\n", __func__, i); addr_h = mc->reg; addr_l = mc->rreg; if (val_ll == val_lr) { /* Set both L/R channels at the same time */ val_h = (1 << mc->shift) | (3 << 4); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_ll); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_ll); - } else { /* Lch*/ val_h = (1 << mc->shift) | (1 << 5); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_ll); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_ll); /* Rch */ val_h = (1 << mc->shift) | (1 << 4); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_lr); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_lr); - } /* check result */ addr_h = (mc->reg + 0x2000) | 0x800; @@ -755,12 +724,8 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); - if (read_rl == val_lr && read_ll == val_ll) { - pr_debug("write command successful\n"); + if (read_rl == val_lr && read_ll == val_ll) break; - } - - pr_warn("write command unsuccessful, retry\n"); } if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) @@ -891,9 +856,7 @@ static int rt700_mux_get(struct snd_kcontrol *kcontrol, /* nid = e->reg, vid = 0xf01 */ reg = RT700_VERB_GET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, 0x0); - pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); val = snd_soc_component_read32(component, RT700_READ_HDA_0); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val); ucontrol->value.enumerated.item[0] = val; return 0; @@ -916,25 +879,18 @@ static int rt700_mux_put(struct snd_kcontrol *kcontrol, /* Verb ID = 0x701h, nid = e->reg */ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - pr_debug("%s val=%x e->reg=%x item[0]=%d\n", - __func__, val, e->reg, item[0]); reg = RT700_VERB_GET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, 0x0); - pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); val2 = snd_soc_component_read32(component, RT700_READ_HDA_0); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val2); if (val == val2) change = 0; else change = 1; - pr_debug("change=%d\n", change); - if (change) { reg = RT700_VERB_SET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, val); - pr_debug("%s write %04x %02x\n", __func__, reg, val); update.kcontrol = kcontrol; update.reg = e->reg; update.mask = 0xff; @@ -1107,7 +1063,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - pr_debug("%s level=%d\n", __func__, level); switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { @@ -1431,7 +1386,6 @@ static ssize_t rt700_index_cmd_store(struct device *dev, unsigned int index_reg = 0, index_val = 0; int i; - pr_debug("register \"%s\" count=%zu\n", buf, count); for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ if (*(buf + i) <= '9' && *(buf + i) >= '0') index_reg = (index_reg << 4) | @@ -1460,9 +1414,6 @@ static ssize_t rt700_index_cmd_store(struct device *dev, break; } - pr_debug("index_reg=0x%x index_val=0x%x\n", - index_reg, index_val); - rt700_index_write(rt700->regmap, index_reg, index_val); return count; @@ -1477,10 +1428,7 @@ static ssize_t rt700_hda_cmd_show(struct device *dev, int i, cnt = 0; unsigned int value; - pr_debug("%s cnt=%d RT700_HDA_DUMP_LEN=%d PAGE_SIZE=%d\n", - __func__, cnt, RT700_HDA_DUMP_LEN, PAGE_SIZE); for (i = 0; i < RT700_HDA_DUMP_LEN; i++) { - pr_debug("%s i=%d", __func__, i); if (cnt + 25 >= PAGE_SIZE) break; rt700->dbg_nid = hda_dump_list[i].nid; @@ -1510,7 +1458,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; int i; - pr_debug("register \"%s\" count=%zu\n", buf, count); for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ if (*(buf + i) <= '9' && *(buf + i) >= '0') rt700->dbg_nid = (rt700->dbg_nid << 4) | @@ -1555,8 +1502,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, else break; } - pr_debug("dbg_nid=0x%x dbg_vid=0x%x dbg_payload=0x%x\n", - rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload); hda_to_sdw(rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, &sdw_addr_h, &sdw_data_h, &sdw_addr_l, &sdw_data_l); @@ -1784,19 +1729,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, /* Set index */ rt700_index_write(rt700->regmap, 0x4a, 0x201b); - //rt700_index_write(rt700->regmap, 0x38, 0x4921); - - /* get the setting registers for debug - pr_debug("%s get the setting registers\n", __func__); - rt700_hda_read(rt700->regmap, 0xf07, 0x21, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x14, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x12, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x13, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x19, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x1a, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x1b, 0, &value); - rt700_hda_read(rt700->regmap, 0xf0c, 0x14, 0, &value); - */ ret = rt700_clock_config(dev, alc700); /* Enable Jack Detection */ From 48708e26b88b278c754ad9b61aa447cd2c750118 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 Aug 2017 16:36:28 +0530 Subject: [PATCH 0194/1276] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 7a3cca073845..e723ffebed0f 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From f73bae289945e5dfbd964974ecda74449be17b5b Mon Sep 17 00:00:00 2001 From: Dronamraju Santosh Pavan Kumar Date: Wed, 16 Aug 2017 17:04:49 +0530 Subject: [PATCH 0195/1276] ASoC: Intel: CNL: Remove larger frame size warnings from cnl_sdw_bra_pipe_cfg_pb and cnl_sdw_bra_pipe_cfg_cp functions. Below warning message observed due to static allocation of struct skl_module_cfg and struct skl_module in above mentioned functions: warning: the frame size of 85664 bytes is larger than 2048 bytes To avoid this warning memory is allocated dynamically. Change-Id: I62beb19219b70640a4e7391604b2f3884897e7d4 Signed-off-by: Dronamraju Santosh Pavan Kumar Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 485 ++++++++++++++----------- 1 file changed, 270 insertions(+), 215 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8560cd65e77c..9d413b816437 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -365,25 +365,49 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *host_cpr_pipe = NULL; struct skl_pipe_params host_cpr_params; - struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; - struct skl_module host_cpr_mod, link_cpr_mod; + struct skl_module_cfg *host_cpr_cfg = NULL, *link_cpr_cfg = NULL; + struct skl_module *host_cpr_mod = NULL, *link_cpr_mod = NULL; int ret; struct skl_module_fmt *in_fmt, *out_fmt; u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218 }; - link_cpr_cfg.module = &link_cpr_mod; - host_cpr_cfg.module = &host_cpr_mod; + host_cpr_cfg = kzalloc(sizeof(*host_cpr_cfg), GFP_KERNEL); + if (!host_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg = kzalloc(sizeof(*link_cpr_cfg), GFP_KERNEL); + if (!link_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_mod = kzalloc(sizeof(*host_cpr_mod), GFP_KERNEL); + if (!host_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_mod = kzalloc(sizeof(*link_cpr_mod), GFP_KERNEL); + if (!link_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg->module = link_cpr_mod; + host_cpr_cfg->module = host_cpr_mod; /* * To get the pvt id, UUID of the module config is * necessary. Hence hardocde this to the UUID fof copier * module */ - memcpy(&host_cpr_cfg.guid, &guid, 16); - memcpy(&link_cpr_cfg.guid, &guid, 16); - in_fmt = &host_cpr_cfg.module->formats[0].inputs[0].fmt; - out_fmt = &host_cpr_cfg.module->formats[0].outputs[0].fmt; + memcpy(host_cpr_cfg->guid, &guid, 16); + memcpy(link_cpr_cfg->guid, &guid, 16); + in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -392,21 +416,21 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, goto error; } - host_cpr_cfg.fmt_idx = 0; - host_cpr_cfg.res_idx = 0; - link_cpr_cfg.fmt_idx = 0; - link_cpr_cfg.res_idx = 0; + host_cpr_cfg->fmt_idx = 0; + host_cpr_cfg->res_idx = 0; + link_cpr_cfg->fmt_idx = 0; + link_cpr_cfg->res_idx = 0; bra_data->pb_pipe = host_cpr_pipe; host_cpr_pipe->p_params = &host_cpr_params; - host_cpr_cfg.pipe = host_cpr_pipe; + host_cpr_cfg->pipe = host_cpr_pipe; host_cpr_pipe->ppl_id = 1; host_cpr_pipe->pipe_priority = 0; host_cpr_pipe->conn_type = 0; host_cpr_pipe->memory_pages = 2; - ret = skl_create_pipeline(ctx, host_cpr_cfg.pipe); + ret = skl_create_pipeline(ctx, host_cpr_cfg->pipe); if (ret < 0) goto error; @@ -417,33 +441,33 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_params.s_fmt = 32; host_cpr_params.linktype = 0; host_cpr_params.stream = 0; - host_cpr_cfg.id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg.guid); + host_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg->guid); - host_cpr_cfg.id.instance_id = 1; - host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); - if (host_cpr_cfg.id.pvt_id < 0) + host_cpr_cfg->id.instance_id = 1; + host_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg->guid, host_cpr_cfg->id.instance_id); + if (host_cpr_cfg->id.pvt_id < 0) return -EINVAL; - host_cpr_cfg.module->resources[0].cps = 100000; - host_cpr_cfg.module->resources[0].is_pages = 0; - host_cpr_cfg.module->resources[0].ibs = 384; - host_cpr_cfg.module->resources[0].obs = 384; - host_cpr_cfg.core_id = 0; - host_cpr_cfg.module->max_input_pins = 1; - host_cpr_cfg.module->max_output_pins = 1; - host_cpr_cfg.module->loadable = 0; - host_cpr_cfg.domain = 0; - host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; - host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; - host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.module->resources[0].dma_buffer_size = 2; - host_cpr_cfg.converter = 0; - host_cpr_cfg.vbus_id = 0; - host_cpr_cfg.sdw_agg_enable = 0; - host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg->module->resources[0].cps = 100000; + host_cpr_cfg->module->resources[0].is_pages = 0; + host_cpr_cfg->module->resources[0].ibs = 384; + host_cpr_cfg->module->resources[0].obs = 384; + host_cpr_cfg->core_id = 0; + host_cpr_cfg->module->max_input_pins = 1; + host_cpr_cfg->module->max_output_pins = 1; + host_cpr_cfg->module->loadable = 0; + host_cpr_cfg->domain = 0; + host_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; + host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; + host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->module->resources[0].dma_buffer_size = 2; + host_cpr_cfg->converter = 0; + host_cpr_cfg->vbus_id = 0; + host_cpr_cfg->sdw_agg_enable = 0; + host_cpr_cfg->formats_config.caps_size = 0; in_fmt->channels = 1; in_fmt->s_freq = 96000; @@ -463,126 +487,130 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, out_fmt->sample_type = 0; out_fmt->ch_map = 0xFFFFFFF1; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, - sizeof(*host_cpr_cfg.m_in_pin), + host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, + sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_in_pin) { + if (!host_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, - sizeof(*host_cpr_cfg.m_out_pin), + host_cpr_cfg->m_out_pin = kcalloc(host_cpr_cfg->module->max_output_pins, + sizeof(*host_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_out_pin) { + if (!host_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_in_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_in_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_in_pin[0].in_use = false; - host_cpr_cfg.m_in_pin[0].is_dynamic = true; - host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - host_cpr_cfg.m_out_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_out_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_out_pin[0].in_use = false; - host_cpr_cfg.m_out_pin[0].is_dynamic = true; - host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - memcpy(&link_cpr_cfg, &host_cpr_cfg, + host_cpr_cfg->m_in_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_in_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_in_pin[0].in_use = false; + host_cpr_cfg->m_in_pin[0].is_dynamic = true; + host_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg->m_out_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_out_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_out_pin[0].in_use = false; + host_cpr_cfg->m_out_pin[0].is_dynamic = true; + host_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(link_cpr_cfg, host_cpr_cfg, sizeof(struct skl_module_cfg)); - link_cpr_cfg.id.instance_id = 2; - link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); - if (link_cpr_cfg.id.pvt_id < 0) + link_cpr_cfg->id.instance_id = 2; + link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg->guid, link_cpr_cfg->id.instance_id); + if (link_cpr_cfg->id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; + link_cpr_cfg->dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.sdw_stream_num = 0x3; + link_cpr_cfg->sdw_stream_num = 0x3; #else - link_cpr_cfg.sdw_stream_num = 0x13; + link_cpr_cfg->sdw_stream_num = 0x13; #endif - link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + link_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, - sizeof(*link_cpr_cfg.m_in_pin), + link_cpr_cfg->m_in_pin = kcalloc(link_cpr_cfg->module->max_input_pins, + sizeof(*link_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_in_pin) { + if (!link_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, - sizeof(*link_cpr_cfg.m_out_pin), + link_cpr_cfg->m_out_pin = kcalloc(link_cpr_cfg->module->max_output_pins, + sizeof(*link_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_out_pin) { + if (!link_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_in_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_in_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_in_pin[0].in_use = false; - link_cpr_cfg.m_in_pin[0].is_dynamic = true; - link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.m_out_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_out_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_out_pin[0].in_use = false; - link_cpr_cfg.m_out_pin[0].is_dynamic = true; - link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + link_cpr_cfg->m_in_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_in_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_in_pin[0].in_use = false; + link_cpr_cfg->m_in_pin[0].is_dynamic = true; + link_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->m_out_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_out_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_out_pin[0].in_use = false; + link_cpr_cfg->m_out_pin[0].is_dynamic = true; + link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), GFP_KERNEL); - if (!link_cpr_cfg.formats_config.caps) { + if (!link_cpr_cfg->formats_config.caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg.formats_config.caps[0] = 0x0; - link_cpr_cfg.formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config.caps[0] = 0x0; + link_cpr_cfg->formats_config.caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.formats_config.caps[2] = 0x1003; + link_cpr_cfg->formats_config.caps[2] = 0x1003; #else - link_cpr_cfg.formats_config.caps[2] = 0x1013; + link_cpr_cfg->formats_config.caps[2] = 0x1013; #endif - link_cpr_cfg.formats_config.caps[3] = 0x0; + link_cpr_cfg->formats_config.caps[3] = 0x0; /* Init PB CPR1 module */ - ret = skl_init_module(ctx, &host_cpr_cfg); + ret = skl_init_module(ctx, host_cpr_cfg); if (ret < 0) goto error; /* Init PB CPR2 module */ - ret = skl_init_module(ctx, &link_cpr_cfg); + ret = skl_init_module(ctx, link_cpr_cfg); if (ret < 0) goto error; /* Bind PB CPR1 and CPR2 module */ - ret = skl_bind_modules(ctx, &host_cpr_cfg, &link_cpr_cfg); + ret = skl_bind_modules(ctx, host_cpr_cfg, link_cpr_cfg); if (ret < 0) goto error; error: /* Free up all memory allocated */ - kfree(host_cpr_cfg.m_in_pin); - kfree(host_cpr_cfg.m_out_pin); - kfree(link_cpr_cfg.m_in_pin); - kfree(link_cpr_cfg.m_out_pin); - kfree(link_cpr_cfg.formats_config.caps); + kfree(host_cpr_cfg->m_in_pin); + kfree(host_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg->m_in_pin); + kfree(link_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg->formats_config.caps); + kfree(host_cpr_cfg); + kfree(link_cpr_cfg); + kfree(host_cpr_mod); + kfree(link_cpr_mod); return ret; } @@ -593,15 +621,39 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *link_cpr_pipe = NULL; struct skl_pipe_params link_cpr_params; - struct skl_module host_cpr_mod, link_cpr_mod; - struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; + struct skl_module *host_cpr_mod = NULL, *link_cpr_mod = NULL; + struct skl_module_cfg *link_cpr_cfg = NULL, *host_cpr_cfg = NULL; int ret; struct skl_module_fmt *in_fmt, *out_fmt; u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218 }; - link_cpr_cfg.module = &link_cpr_mod; - host_cpr_cfg.module = &host_cpr_mod; + link_cpr_cfg = kzalloc(sizeof(*link_cpr_cfg), GFP_KERNEL); + if (!link_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg = kzalloc(sizeof(*host_cpr_cfg), GFP_KERNEL); + if (!host_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_mod = kzalloc(sizeof(*host_cpr_mod), GFP_KERNEL); + if (!host_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_mod = kzalloc(sizeof(*link_cpr_mod), GFP_KERNEL); + if (!link_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg->module = link_cpr_mod; + host_cpr_cfg->module = host_cpr_mod; /* @@ -609,10 +661,10 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, * necessary. Hence hardocde this to the UUID fof copier * module */ - memcpy(&host_cpr_cfg.guid, &guid, 16); - memcpy(&link_cpr_cfg.guid, &guid, 16); - in_fmt = &link_cpr_cfg.module->formats[0].inputs[0].fmt; - out_fmt = &link_cpr_cfg.module->formats[0].outputs[0].fmt; + memcpy(host_cpr_cfg->guid, &guid, 16); + memcpy(link_cpr_cfg->guid, &guid, 16); + in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; /* Capture Pipeline */ link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -621,13 +673,13 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, goto error; } - link_cpr_cfg.fmt_idx = 0; - link_cpr_cfg.res_idx = 0; - host_cpr_cfg.fmt_idx = 0; - host_cpr_cfg.res_idx = 0; + link_cpr_cfg->fmt_idx = 0; + link_cpr_cfg->res_idx = 0; + host_cpr_cfg->fmt_idx = 0; + host_cpr_cfg->res_idx = 0; bra_data->cp_pipe = link_cpr_pipe; link_cpr_pipe->p_params = &link_cpr_params; - link_cpr_cfg.pipe = link_cpr_pipe; + link_cpr_cfg->pipe = link_cpr_pipe; link_cpr_pipe->ppl_id = 2; link_cpr_pipe->pipe_priority = 0; @@ -635,7 +687,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_pipe->memory_pages = 2; /* Create Capture Pipeline */ - ret = skl_create_pipeline(ctx, link_cpr_cfg.pipe); + ret = skl_create_pipeline(ctx, link_cpr_cfg->pipe); if (ret < 0) goto error; @@ -646,54 +698,54 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; - host_cpr_cfg.id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg.guid); + host_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg->guid); - link_cpr_cfg.id.instance_id = 3; - link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); - if (link_cpr_cfg.id.pvt_id < 0) + link_cpr_cfg->id.instance_id = 3; + link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg->guid, link_cpr_cfg->id.instance_id); + if (link_cpr_cfg->id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.module->resources[0].cps = 100000; - link_cpr_cfg.module->resources[0].is_pages = 0; - link_cpr_cfg.module->resources[0].ibs = 1152; - link_cpr_cfg.module->resources[0].obs = 1152; - link_cpr_cfg.core_id = 0; - link_cpr_cfg.module->max_input_pins = 1; - link_cpr_cfg.module->max_output_pins = 1; - link_cpr_cfg.module->loadable = 0; - link_cpr_cfg.domain = 0; - link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; + link_cpr_cfg->module->resources[0].cps = 100000; + link_cpr_cfg->module->resources[0].is_pages = 0; + link_cpr_cfg->module->resources[0].ibs = 1152; + link_cpr_cfg->module->resources[0].obs = 1152; + link_cpr_cfg->core_id = 0; + link_cpr_cfg->module->max_input_pins = 1; + link_cpr_cfg->module->max_output_pins = 1; + link_cpr_cfg->module->loadable = 0; + link_cpr_cfg->domain = 0; + link_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; + link_cpr_cfg->dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.sdw_stream_num = 0x4; + link_cpr_cfg->sdw_stream_num = 0x4; #else - link_cpr_cfg.sdw_stream_num = 0x14; + link_cpr_cfg->sdw_stream_num = 0x14; #endif - link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; - - link_cpr_cfg.formats_config.caps_size = 0; - link_cpr_cfg.module->resources[0].dma_buffer_size = 2; - link_cpr_cfg.converter = 0; - link_cpr_cfg.vbus_id = 0; - link_cpr_cfg.sdw_agg_enable = 0; - link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + link_cpr_cfg->hw_conn_type = SKL_CONN_SINK; + + link_cpr_cfg->formats_config.caps_size = 0; + link_cpr_cfg->module->resources[0].dma_buffer_size = 2; + link_cpr_cfg->converter = 0; + link_cpr_cfg->vbus_id = 0; + link_cpr_cfg->sdw_agg_enable = 0; + link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), GFP_KERNEL); - if (!link_cpr_cfg.formats_config.caps) { + if (!link_cpr_cfg->formats_config.caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg.formats_config.caps[0] = 0x0; - link_cpr_cfg.formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config.caps[0] = 0x0; + link_cpr_cfg->formats_config.caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.formats_config.caps[2] = 0x1104; + link_cpr_cfg->formats_config.caps[2] = 0x1104; #else - link_cpr_cfg.formats_config.caps[2] = 0x1114; + link_cpr_cfg->formats_config.caps[2] = 0x1114; #endif - link_cpr_cfg.formats_config.caps[3] = 0x1; + link_cpr_cfg->formats_config.caps[3] = 0x1; in_fmt->channels = 6; in_fmt->s_freq = 48000; @@ -713,107 +765,110 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, out_fmt->sample_type = 0; out_fmt->ch_map = 0xFF657120; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, - sizeof(*link_cpr_cfg.m_in_pin), + link_cpr_cfg->m_in_pin = kcalloc(link_cpr_cfg->module->max_input_pins, + sizeof(*link_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_in_pin) { + if (!link_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, - sizeof(*link_cpr_cfg.m_out_pin), + link_cpr_cfg->m_out_pin = kcalloc(link_cpr_cfg->module->max_output_pins, + sizeof(*link_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_out_pin) { + if (!link_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_in_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_in_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_in_pin[0].in_use = false; - link_cpr_cfg.m_in_pin[0].is_dynamic = true; - link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.m_out_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_out_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_out_pin[0].in_use = false; - link_cpr_cfg.m_out_pin[0].is_dynamic = true; - link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - memcpy(&host_cpr_cfg, &link_cpr_cfg, + link_cpr_cfg->m_in_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_in_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_in_pin[0].in_use = false; + link_cpr_cfg->m_in_pin[0].is_dynamic = true; + link_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->m_out_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_out_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_out_pin[0].in_use = false; + link_cpr_cfg->m_out_pin[0].is_dynamic = true; + link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(host_cpr_cfg, link_cpr_cfg, sizeof(struct skl_module_cfg)); - host_cpr_cfg.id.instance_id = 4; - host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); - if (host_cpr_cfg.id.pvt_id < 0) + host_cpr_cfg->id.instance_id = 4; + host_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg->guid, host_cpr_cfg->id.instance_id); + if (host_cpr_cfg->id.pvt_id < 0) return -EINVAL; - host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; - host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg->hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); - host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, - sizeof(*host_cpr_cfg.m_in_pin), + host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, + sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_in_pin) { + if (!host_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, - sizeof(*host_cpr_cfg.m_out_pin), + host_cpr_cfg->m_out_pin = kcalloc(host_cpr_cfg->module->max_output_pins, + sizeof(*host_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_out_pin) { + if (!host_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_in_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_in_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_in_pin[0].in_use = false; - host_cpr_cfg.m_in_pin[0].is_dynamic = true; - host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - host_cpr_cfg.m_out_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_out_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_out_pin[0].in_use = false; - host_cpr_cfg.m_out_pin[0].is_dynamic = true; - host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + host_cpr_cfg->m_in_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_in_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_in_pin[0].in_use = false; + host_cpr_cfg->m_in_pin[0].is_dynamic = true; + host_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg->m_out_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_out_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_out_pin[0].in_use = false; + host_cpr_cfg->m_out_pin[0].is_dynamic = true; + host_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; /* Init CP CPR1 module */ - ret = skl_init_module(ctx, &link_cpr_cfg); + ret = skl_init_module(ctx, link_cpr_cfg); if (ret < 0) goto error; /* Init CP CPR2 module */ - ret = skl_init_module(ctx, &host_cpr_cfg); + ret = skl_init_module(ctx, host_cpr_cfg); if (ret < 0) goto error; /* Bind CP CPR1 and CPR2 module */ - ret = skl_bind_modules(ctx, &link_cpr_cfg, &host_cpr_cfg); + ret = skl_bind_modules(ctx, link_cpr_cfg, host_cpr_cfg); if (ret < 0) goto error; error: /* Free up all memory allocated */ - kfree(link_cpr_cfg.formats_config.caps); - kfree(link_cpr_cfg.m_in_pin); - kfree(link_cpr_cfg.m_out_pin); - kfree(host_cpr_cfg.m_in_pin); - kfree(host_cpr_cfg.m_out_pin); - + kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->m_in_pin); + kfree(link_cpr_cfg->m_out_pin); + kfree(host_cpr_cfg->m_in_pin); + kfree(host_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg); + kfree(host_cpr_cfg); + kfree(host_cpr_mod); + kfree(link_cpr_mod); return ret; } From e437a7068738955e10655cdbd047555552aabbc4 Mon Sep 17 00:00:00 2001 From: "Paul, Subhankar" Date: Tue, 8 Aug 2017 22:46:45 +0530 Subject: [PATCH 0196/1276] ASoC: Intel: Skylake: Add support for module notifications Firmware modules can send asynchronous notification to driver with event data as payload. Add support for notifying user of such asynchronous notifications from firmware modules by adding kcontrols. These kcontrols have the module event data that needs to be sent to user. Change-Id: If204e275a9613c769cf00fe632e45b174bd2fa2f Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Paul, Subhankar Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-messages.c | 2 + sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 58 +++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 9 +++ sound/soc/intel/skylake/skl-topology.c | 107 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 8 ++ sound/soc/intel/skylake/skl.c | 2 + 7 files changed, 186 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 9d413b816437..ea500be81329 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1224,6 +1224,8 @@ int skl_init_dsp(struct skl *skl) dev_dbg(bus->dev, "dsp registration status=%d\n", ret); + INIT_LIST_HEAD(&skl->skl_sst->notify_kctls); + /* Set DMA clock controls */ ret = skl_dsp_set_dma_clk_controls(skl->skl_sst); if (ret < 0) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 6736c89c6520..207bf3fe8ffd 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -241,6 +241,7 @@ struct skl_notify_data { u32 type; u32 length; struct skl_tcn_events tcn_data; + char data[0]; }; struct skl_dsp_notify_ops { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index cfdf2ce3733b..5a8766acc1e2 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -198,6 +198,8 @@ #define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ << IPC_D0IX_STREAMING_SHIFT) +/* Offset to get the event data for module notification */ +#define MOD_DATA_OFFSET 12 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -274,7 +276,8 @@ enum skl_ipc_notification_type { IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, IPC_GLB_NOTIFY_FW_READY = 8, IPC_GLB_NOTIFY_FW_AUD_CLASS_RESULT = 9, - IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10 + IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10, + IPC_GLB_MODULE_NOTIFICATION = 12 }; /* Module Message Types */ @@ -355,6 +358,51 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, } +static int skl_process_module_notification(struct skl_sst *skl) +{ + struct skl_notify_data *notify_data; + struct skl_module_notify mod_notif; + u32 notify_data_sz; + char *module_data; + + dev_dbg(skl->dev, "***** Module Notification ******\n"); + /* read module notification structure from mailbox */ + sst_dsp_inbox_read(skl->dsp, &mod_notif, + sizeof(struct skl_module_notify)); + + notify_data_sz = sizeof(mod_notif) + mod_notif.event_data_size; + notify_data = kzalloc((sizeof(*notify_data) + notify_data_sz), + GFP_KERNEL); + + if (!notify_data) + return -ENOMEM; + + /* read the complete notification message */ + sst_dsp_inbox_read(skl->dsp, notify_data->data, notify_data_sz); + + notify_data->length = notify_data_sz; + notify_data->type = 0xFF; + + /* Module notification data to console */ + dev_dbg(skl->dev, "Module Id = %#x\n", + (mod_notif.unique_id >> 16)); + dev_dbg(skl->dev, "Instanse Id = %#x\n", + (mod_notif.unique_id & 0x0000FFFF)); + dev_dbg(skl->dev, "Data Size = %d bytes\n", + mod_notif.event_data_size); + + module_data = notify_data->data; + + print_hex_dump(KERN_DEBUG, "DATA: ", MOD_DATA_OFFSET, 8, 4, + module_data, notify_data->length, false); + + skl->notify_ops.notify_cb(skl, IPC_GLB_MODULE_NOTIFICATION, + notify_data); + kfree(notify_data); + + return 0; +} + static void skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) { @@ -455,6 +503,14 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, } break; + case IPC_GLB_MODULE_NOTIFICATION: + ret = skl_process_module_notification(skl); + if (ret < 0) { + dev_err(ipc->dev, + "Module Notification read fail:%d\n", ret); + return ret; + } + break; default: dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc083b0a0d4f..5db66e257887 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -29,6 +29,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 #define FW_REG_SZ 1024 +#define SKL_EVENT_GLB_MODULE_NOTIFICATION 12 #define SKL_TPLG_CHG_NOTIFY 3 enum skl_ipc_pipeline_state { @@ -244,6 +245,12 @@ struct skl_hw_property_info { u32 ebb_size_bytes; }; +struct skl_notify_kctrl_info { + struct list_head list; + u32 notify_id; + struct snd_kcontrol *notify_kctl; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -313,6 +320,8 @@ struct skl_sst { struct skl_sysfs_tree *sysfs_tree; struct snd_kcontrol *kcontrol; + + struct list_head notify_kctls; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 373dbced8efa..df06e446d25a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2642,6 +2642,96 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } +/* + * This function searches notification kcontrol list present in skl_sst + * context against unique notify_id and returns kcontrol pointer if match + * found. + */ +struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, + u32 notify_id) +{ + struct skl_notify_kctrl_info *kctl_info; + + list_for_each_entry(kctl_info, &skl->notify_kctls, list) { + if (notify_id == kctl_info->notify_id) + return kctl_info->notify_kctl; + } + return NULL; +} + +/* + * This function creates notification kcontrol list by searching control + * list present in snd_card context. It compares kcontrol name with specific + * string "notify params" to get notification kcontrols and add it up to the + * notification list present in skl_sst context. + * NOTE: To use module notification feature, new kcontrol named "notify" should + * be added in topology XML for that particular module. + */ +int skl_create_notify_kctl_list(struct skl_sst *skl_sst, + struct snd_card *card) +{ + struct snd_kcontrol *kctl; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_notify_kctrl_info *info; + u32 size = sizeof(*info); + + list_for_each_entry(kctl, &card->controls, list) { + if (strnstr(kctl->id.name, "notify params", + strlen(kctl->id.name))) { + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + w = snd_soc_dapm_kcontrol_widget(kctl); + mconfig = w->priv; + + /* Module ID (MS word) + Module Instance ID (LS word) */ + info->notify_id = ((mconfig->id.module_id << 16) | + (mconfig->id.instance_id)); + info->notify_kctl = kctl; + + list_add_tail(&info->list, &skl_sst->notify_kctls); + } + } + return 0; +} + +/* + * This function deletes notification kcontrol list from skl_sst + * context. + */ +void skl_delete_notify_kctl_list(struct skl_sst *skl_sst) +{ + struct skl_notify_kctrl_info *info, *tmp; + + list_for_each_entry_safe(info, tmp, &skl_sst->notify_kctls, list) { + list_del(&info->list); + kfree(info); + } +} + +/* + * This function creates notification kcontrol list on first module + * notification from firmware. It also search notification kcontrol + * list against unique notify_id sent from firmware and returns the + * corresponding kcontrol pointer. + */ +struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, + struct snd_card *card, u32 notify_id) +{ + struct snd_kcontrol *kctl = NULL; + + if (list_empty(&skl->notify_kctls)) + skl_create_notify_kctl_list(skl, card); + + kctl = skl_search_notify_kctl(skl, notify_id); + + return kctl; +} + + + /* * Get the events along with data stored in notify_data and pass * to kcontrol private data. @@ -2683,7 +2773,24 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); break; + case SKL_EVENT_GLB_MODULE_NOTIFICATION: + m_notification = (struct skl_module_notify *)notify_data->data; + card = component->card; + ctx->kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, + m_notification->unique_id); + if (!ctx->kcontrol) { + dev_dbg(ctx->dev, "Module notify control not found\n"); + return -EINVAL; + } + sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + param_length = sizeof(struct skl_notify_data) + + notify_data->length; + memcpy(bc->params, (char *)notify_data, param_length); + snd_ctl_notify(card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); + break; default: return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index ce4069607dda..4c09a0896e42 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -617,4 +617,12 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx); int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); +struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, + u32 notify_id); +int skl_create_notify_kctl_list(struct skl_sst *skl_sst, + struct snd_card *card); +void skl_delete_notify_kctl_list(struct skl_sst *skl_sst); +struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, + struct snd_card *card, u32 notify_id); + #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index c1fa7f6aa821..a848eae708c8 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -37,6 +37,7 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-topology.h" /* * initialize the PCI registers @@ -1091,6 +1092,7 @@ static void skl_remove(struct pci_dev *pci) struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); + skl_delete_notify_kctl_list(skl->skl_sst); release_firmware(skl->tplg); pm_runtime_get_noresume(&pci->dev); From 4ea21efc1903ac2373d1289a0dd241f4195d27f5 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Wed, 30 Aug 2017 16:46:40 +0530 Subject: [PATCH 0197/1276] ASoC: Intel: Board: Add pm_ops to fix suspend/resume issue Audio playback not resumed after it is suspended. Add snd_soc_pm_ops to execute power management operation. Change-Id: I84ccf6a0ac7e35c1f79971ee59555f24024d4309 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: H S, Vijay Reviewed-by: Kp, Jeeja Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 325b59adaf1c..c7b7fe3f9ed7 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -240,6 +240,7 @@ static struct platform_driver broxton_tdf8532_audio = { .remove = broxton_tdf8532_audio_remove, .driver = { .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, }, }; From 6fd6165effa5debca47c9dcd0a6170b742fdad22 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:56:44 +0530 Subject: [PATCH 0198/1276] ASoC: rt700: Remove unused variable Compiler throws warning for unused variable, so remove it. Signed-off-by: Guneshwor Singh --- sound/soc/codecs/rt700.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index a654c092b8c5..952140d5fbbe 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1789,7 +1789,6 @@ static int rt700_runtime_resume(struct device *dev) { struct rt700_priv *rt700 = dev_get_drvdata(dev); int ret; - int timeout = 0; if (rt700->sdw) { ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, From fd89582716c60f7d0c7af404e9ac5b7ba5d87b17 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:58:03 +0530 Subject: [PATCH 0199/1276] ASoC: Intel: board: Remove unused function cnl_dmic_fixup cnl_dmic_fixup is never used, remove it to shun compiler warning. Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_cs42l42.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c index 879aa4e552f3..bf5885b59a74 100644 --- a/sound/soc/intel/boards/cnl_cs42l42.c +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -162,16 +162,6 @@ static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 2; - - return 0; -} - struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { { .name = "Bxtn Audio Port", From 643778b722825be12dbf676044f9ac4dfa9af004 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:59:36 +0530 Subject: [PATCH 0200/1276] ASoC: Intel: Skylake: Move prototype to appropriate header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skl_dsp_crash_dump_read is defined in skl-sst-ipc.c and but skl-topology.h in which its prototype lives, is not included in skl-sst-ipc.c. So move the prototype to skl-sst-ipc.h so as to avoid compiler warning below: warning: no previous prototype for ‘skl_dsp_crash_dump_read’ [-Wmissing-prototypes] Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 5db66e257887..30ba9a9e708d 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -472,4 +472,6 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); int skl_notify_tplg_change(struct skl_sst *ctx, int type); +int skl_dsp_crash_dump_read(struct skl_sst *ctx); + #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 4c09a0896e42..12639e4bba76 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -613,8 +613,6 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); -int skl_dsp_crash_dump_read(struct skl_sst *ctx); - int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, From 3a0c01a1662acea67dfa08ba6b271c3c79fc3584 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 18 Sep 2017 11:03:25 +0530 Subject: [PATCH 0201/1276] ASoC: Intel: cnl: Move d0i[03] ops after cnl_load_base_firmware Since ipc's are sent during cnl_load_base_firmware and there's no need to check d0i0 state at the time of firmware load, move the ops assignments after loading firmware. Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 9441a724ea59..c99b2c931202 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -755,14 +755,9 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } - /* set the D0i3 check */ - cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); - INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); - cnl->d0i3.state = SKL_DSP_D0I3_NONE; - ret = skl_dsp_acquire_irq(sst); if (ret < 0) return ret; @@ -773,6 +768,12 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + /* set the D0i3 check */ + cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; + + INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); + cnl->d0i3.state = SKL_DSP_D0I3_NONE; + #if IS_ENABLED(CONFIG_SND_SOC_RT700) ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq, ptr); if (ret) { From 9c6d83ad35229b14fc6965d709b4fe4897936fcd Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Wed, 30 Aug 2017 20:52:51 +0530 Subject: [PATCH 0202/1276] ASoC: Intel: Skylake: Support for Probe DMA Buffer size This patch removes hard coding of buffer size for Probe Module. For probe, DMA Buffer size is calculated as- 2 * [Probe Hdr + ((SRate/1000)*(ch)*(sch_time_ms)*(bytes per sample)) + Probe Footer] where, Probe Header and footer are 24 and 8 bytes respectively. Hence this value is topology specific and is to be used from XML as dma_buffer_size value for Probe Module. Change-Id: I2f1d388d5e4f77731f7fa3753eac4550bd5ae57b Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 5 +++++ sound/soc/intel/skylake/skl-probe.c | 8 -------- sound/soc/intel/skylake/skl-topology.c | 15 +-------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ea500be81329..6cf06fa8293a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1782,8 +1782,13 @@ static void skl_setup_probe_gateway_cfg(struct skl_sst *ctx, struct skl_probe_cfg *probe_cfg) { union skl_connector_node_id node_id = {0}; + struct skl_module_res *res; struct skl_probe_config *pconfig = &ctx->probe_config; + res = &mconfig->module->resources[mconfig->res_idx]; + + pconfig->edma_buffsize = res->dma_buffer_size; + node_id.node.dma_type = pconfig->edma_type; node_id.node.vindex = pconfig->edma_id; probe_cfg->prb_cfg.dma_buffer_size = pconfig->edma_buffsize; diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index b563dc38dce8..0716bf62f92e 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -33,13 +33,6 @@ #define USE_SPIB 0 -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 - /* * ======================== * PROBE STATE TRANSITIONS: @@ -103,7 +96,6 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); if ((pconfig->i_refc + pconfig->e_refc) == 0) { - pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; /* * Extractor DMA is to be assigned when the first probe diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index df06e446d25a..4a7b7d3c67ec 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -37,12 +37,6 @@ #define SKL_FMT_FIXUP_MASK (1 << 2) #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4) -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 static const int mic_mono_list[] = { 0, 1, 2, 3, @@ -109,13 +103,6 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) } } -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 - /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type @@ -531,7 +518,7 @@ int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, ad.node_id.node.vindex = pconfig->iprobe[index].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; - ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; + ad.dma_buff_size = pconfig->edma_buffsize; ret = skl_set_module_params(ctx, (void *)&ad, sizeof(struct skl_probe_attach_inj_dma), From af8bc0241fcddbf9f7d3ed8d9db0279e53594641 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Mon, 21 Aug 2017 22:03:10 +0530 Subject: [PATCH 0203/1276] ASoC: Intel: Skylake: Add a separate module type for ASRC algo ASRC algorithm has the same interfaces like the SRC algorithm. This patch distinguishes both of them with similar names so as to enable using them in the same topology One parameter called mode has been added to ASRC init structure whose value is dependent on stream direction. Recommendation from the FW team is to set asrc mode = 2 for capture and asrc mode = 1 for playback for better signal quality Change-Id: I26961f8601bbaba71ebd3944438ebfa294774037 Signed-off-by: Sameer Sharma Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Sinha, Mohit Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 3 ++- sound/soc/intel/skylake/skl-messages.c | 13 +++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 89844fb10aa5..a3f0890c75bd 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -81,7 +81,8 @@ enum skl_module_type { SKL_MODULE_TYPE_BASE_OUTFMT, SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, - SKL_MODULE_TYPE_PROBE + SKL_MODULE_TYPE_PROBE, + SKL_MODULE_TYPE_ASRC }; enum skl_core_affinity { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 6cf06fa8293a..d0d60d740441 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -34,6 +34,9 @@ #include #include +#define ASRC_MODE_UPLINK 2 +#define ASRC_MODE_DOWNLINK 1 + static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) { @@ -1736,6 +1739,14 @@ static void skl_set_src_format(struct skl_sst *ctx, (struct skl_base_cfg *)src_mconfig); src_mconfig->src_cfg = fmt->s_freq; + + if (mconfig->m_type == SKL_MODULE_TYPE_ASRC) { + if (mconfig->pipe->p_params->stream == + SNDRV_PCM_STREAM_PLAYBACK) + src_mconfig->mode = ASRC_MODE_DOWNLINK; + else + src_mconfig->mode = ASRC_MODE_UPLINK; + } } /* @@ -1862,6 +1873,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, return sizeof(struct skl_probe_cfg); case SKL_MODULE_TYPE_SRCINT: + case SKL_MODULE_TYPE_ASRC: return sizeof(struct skl_src_module_cfg); case SKL_MODULE_TYPE_UPDWMIX: @@ -1920,6 +1932,7 @@ static int skl_set_module_format(struct skl_sst *ctx, break; case SKL_MODULE_TYPE_SRCINT: + case SKL_MODULE_TYPE_ASRC: skl_set_src_format(ctx, module_config, *param_data); break; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 12639e4bba76..f9ac42cdc64d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -167,6 +167,7 @@ struct skl_cpr_pin_fmt { struct skl_src_module_cfg { struct skl_base_cfg base_cfg; enum skl_s_freq src_cfg; + u32 mode; } __packed; struct notification_mask { From 8bde2e903953cee7ef28d029176cc6d5ef01ba3f Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 5 Sep 2017 19:57:00 +0530 Subject: [PATCH 0204/1276] ASoC: Intel: Skylake: Add support for DMA Buffer configuration DMA buffer configuration is to manage the HW Buffers at gateway copiers. This information configured from topology and sent as an IPC to the ADSP after firmware download and subsequent D0/D3 cycles. If the topology doesn't have DMA buffer configuration, no IPC would be sent and the HW buffer organization lies with the FW. Change-Id: I7d031b7a5c76c5b4943007b216f2ac515435c0fc Signed-off-by: Gogineni, GiribabuX Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 20 +++++++++++++++++++- sound/soc/intel/skylake/bxt-sst.c | 3 --- sound/soc/intel/skylake/skl-messages.c | 6 +++++- sound/soc/intel/skylake/skl-pcm.c | 6 ++++++ sound/soc/intel/skylake/skl-sst-ipc.c | 20 ++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.c | 22 ++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 16 +++++++++++++++- 8 files changed, 89 insertions(+), 6 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 0f74eeb85995..b29d07b018ea 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -249,6 +249,19 @@ * %SKL_TKN_U32_DMACTRL_CFG_SIZE: * Size information of DMA control params * + * %SKL_TKN_U32_DMA_IDX Config index to fill up DMA config info + * from the manifest. + * + * %SKL_TKN_U32_DMA_TYPE: Types of FW configs + * DMA_CONFIG, SCHEDULER_CONFIG. + * + * %SKL_TKN_U32_DMA_SIZE: DMA buffer Size + * + * %SKL_TKN_U32_DMA_MAX_SIZE: Maximum DMA buffer size + * + * %SKL_TKN_U32_DMA_MIN_SIZE: Minimum DMA buffer size + * + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -347,7 +360,12 @@ enum SKL_TKNS { SKL_TKN_U32_AGG_ID, SKL_TKN_U32_DMACTRL_CFG_IDX, SKL_TKN_U32_DMACTRL_CFG_SIZE, - SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, + SKL_TKN_U32_DMA_IDX, + SKL_TKN_U32_DMA_TYPE, + SKL_TKN_U32_DMA_SIZE, + SKL_TKN_U32_DMA_MAX_SIZE, + SKL_TKN_U32_DMA_MIN_SIZE, + SKL_TKN_MAX = SKL_TKN_U32_DMA_MIN_SIZE, }; /* diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index c7f7c1529354..406d278555f0 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -45,9 +45,6 @@ #define BXT_ADSP_SRAM1_BASE 0xA0000 -#define BXT_INSTANCE_ID 0 -#define BXT_BASE_FW_MODULE_ID 0 - #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 /* Delay before scheduling D0i3 entry */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d0d60d740441..02b3aad155f1 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1354,7 +1354,11 @@ int skl_resume_dsp(struct skl *skl) skl_dsp_set_astate_cfg(skl->skl_sst, skl->cfg.astate_cfg->count, skl->cfg.astate_cfg); } - return ret; + + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, BXT_INSTANCE_ID, + BXT_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); /* Set DMA clock controls */ return skl_dsp_set_dma_clk_controls(skl->skl_sst); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 8209c954b7ad..889fd8680629 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1852,6 +1852,12 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, + BXT_INSTANCE_ID, BXT_BASE_FW_MODULE_ID, + (u32 *)(&skl->cfg.dmacfg)); + /* Set DMA clock controls */ skl_dsp_set_dma_clk_controls(skl->skl_sst); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5a8766acc1e2..d7c75da0625f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -200,6 +200,7 @@ /* Offset to get the event data for module notification */ #define MOD_DATA_OFFSET 12 +#define SET_LARGE_CFG_FW_CONFIG 7 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -1164,6 +1165,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); +void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, + u16 module_id, u32 *data) +{ + struct skl_ipc_large_config_msg msg = {0}; + u32 size_offset = 1; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.large_param_id = SET_LARGE_CFG_FW_CONFIG; + /* size of total message = size of payload + size of headers*/ + msg.param_data_size = data[size_offset] + (2 * sizeof(u32)); + + ret = skl_ipc_set_large_config(ipc, &msg, data); + if (ret < 0) + dev_err(ipc->dev, "ipc: set dma config failed, err %d\n", ret); +} +EXPORT_SYMBOL_GPL(skl_ipc_set_dma_cfg); + int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 30ba9a9e708d..76e83d216e49 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -474,4 +474,6 @@ void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, int skl_notify_tplg_change(struct skl_sst *ctx, int type); int skl_dsp_crash_dump_read(struct skl_sst *ctx); +void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, + u16 module_id, u32 *data); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 4a7b7d3c67ec..1a40c66e1c58 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4232,6 +4232,7 @@ static int skl_tplg_get_int_tkn(struct device *dev, { int tkn_count = 0, ret, size; static int mod_idx, res_val_idx, intf_val_idx, dir, pin_idx; + static int dma_cfg_idx; struct skl_module_res *res = NULL; struct skl_module_iface *fmt = NULL; struct skl_module *mod = NULL; @@ -4307,6 +4308,27 @@ static int skl_tplg_get_int_tkn(struct device *dev, case SKL_TKN_U32_ASTATE_CLK_SRC: astate_table[astate_cfg_idx].clk_src = tkn_elem->value; + + case SKL_TKN_U32_DMA_TYPE: + skl->cfg.dmacfg.type = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_SIZE: + skl->cfg.dmacfg.size = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_IDX: + dma_cfg_idx = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_MIN_SIZE: + skl->cfg.dmacfg.dma_cfg[dma_cfg_idx].min_size = + tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_MAX_SIZE: + skl->cfg.dmacfg.dma_cfg[dma_cfg_idx].max_size = + tkn_elem->value; break; case SKL_TKN_U8_IN_PIN_TYPE: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 318b3c54c44e..c8a2c27d971d 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -47,8 +47,10 @@ #define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) -#define SKL_MAX_DMA_CFG 24 #define SKL_MAX_DMACTRL 7 +#define SKL_MAX_DMA_CFG 24 +#define BXT_INSTANCE_ID 0 +#define BXT_BASE_FW_MODULE_ID 0 struct skl_dsp_resource { u32 max_mcps; @@ -69,6 +71,17 @@ struct skl_astate_config { struct skl_astate_param astate_table[0]; }; +struct skl_dma_config { + u32 min_size; + u32 max_size; +} __packed; + +struct skl_dma_buff_cfg { + u32 type; + u32 size; + struct skl_dma_config dma_cfg[SKL_MAX_DMA_CFG]; +} __packed; + struct skl_dmctrl_hdr { u32 vbus_id; u32 freq; @@ -88,6 +101,7 @@ struct skl_dmactrl_config { } __packed; struct skl_fw_config { + struct skl_dma_buff_cfg dmacfg; struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From d2d5b70927bf3fc31f9c5400baf23158727a9996 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Tue, 12 Sep 2017 17:18:44 +0530 Subject: [PATCH 0205/1276] ASoC: Intel: Set all I2S ports to slave mode after DSP power up in BXTP During DSP power up sequences, the I2S ports default to Master mode. This drives frame sync and bit clock high and may cause distortion issues on peripherals in some boards. To prevent this, the ports should be set slave mode before the DSP boot. Change-Id: Id8f96989d35674acad89f7080f58e7682bcd81dc Signed-off-by: Sameer Sharma Signed-off-by: Mousumi Jana Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/bxt-sst.c | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 406d278555f0..dd5453daa562 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -32,6 +32,15 @@ #define BXT_ROM_INIT 0x5 #define BXT_ADSP_SRAM0_BASE 0x80000 +/* BXT SSP/I2S Registers */ +#define I2S_SSC1_REG_OFF BIT(2) +#define SET_SLAVE_MASK GENMASK(25, 24) + +/*BXT I2S Clock Gating*/ +#define BXT_DSP_CLK_CTL 0x378 +#define BXT_DISABLE_4_SSP_CLK_GT GENMASK(21, 18) +#define BXT_DISABLE_ALL_SSP_CLK_GT GENMASK(23, 18) + /* Trace Buffer Window */ #define BXT_ADSP_SRAM2_BASE 0x0C0000 #define BXT_ADSP_W2_SIZE 0x2000 @@ -52,6 +61,36 @@ #define BXT_FW_ROM_INIT_RETRY 3 +#define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) + +#define BXTP_NUM_I2S_PORTS 6 + +static void bxt_set_ssp_slave(struct sst_dsp *ctx) +{ + u32 mask, i2s_base_addr; + int i; + + if (BXTP_NUM_I2S_PORTS == 4) + mask = BXT_DISABLE_4_SSP_CLK_GT; + else + mask = BXT_DISABLE_ALL_SSP_CLK_GT; + + /* disable clock gating on all SSPs */ + sst_dsp_shim_update_bits_unlocked(ctx, + BXT_DSP_CLK_CTL, mask, mask); + + /* set all SSPs to slave */ + i2s_base_addr = GET_SSP_BASE(BXTP_NUM_I2S_PORTS); + for (i = 0; i < BXTP_NUM_I2S_PORTS; i++) { + sst_dsp_shim_update_bits_unlocked(ctx, + (i2s_base_addr + (i * 0x1000) + I2S_SSC1_REG_OFF), + SET_SLAVE_MASK, SET_SLAVE_MASK); + } + + /* re-enable clock gating */ + sst_dsp_shim_update_bits_unlocked(ctx, BXT_DSP_CLK_CTL, mask, 0); +} + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); @@ -134,6 +173,9 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, goto base_fw_load_failed; } + /* DSP is powered up, set all SSPs to slave mode */ + bxt_set_ssp_slave(ctx); + /* Step 2: Purge FW request */ sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9))); @@ -448,6 +490,9 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) if (core_id == SKL_DSP_CORE0_ID) { + /* set all SSPs to slave mode */ + bxt_set_ssp_slave(ctx); + /* * Enable interrupt after SPA is set and before * DSP is unstalled From e651aba925a6ee7fc58dcea650871743fb7378b1 Mon Sep 17 00:00:00 2001 From: Imed BEN ROMDHANE Date: Tue, 12 Sep 2017 20:44:07 +0530 Subject: [PATCH 0206/1276] ASoC: Intel: Skylake: Return default sampling rate for Trace compress devices. Defining sampling rate for getting FW logs from FwLogging/Trace compress devices. As user space expects a valid sample rate but existing API compress_get_hpointer returns an error if sampling rate is 0. Change-Id: Ie4eea4850c6cfca9e24bdf3af881b9afd96d2a50 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Gogineni, GiribabuX Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 889fd8680629..4679bf9f4543 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -877,6 +877,8 @@ static int skl_trace_compr_tstamp(struct snd_compr_stream *stream, return -EINVAL; tstamp->copied_total = skl_dsp_log_avail(sst, core); + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(cpu_dai->driver->capture.rates); + return 0; } @@ -1359,6 +1361,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer0 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1369,6 +1374,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer1 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1379,6 +1387,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer2 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1389,6 +1400,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer3 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { From f5b96383dc87c9dfa8b49da68df2f37e5877802d Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Mon, 4 Sep 2017 23:31:17 +0530 Subject: [PATCH 0207/1276] ASoC: Intel: Board: Add fixup for 32 bit masking Fixup function does the masking of the format to set the SSP2 to 32 bit Change-Id: I1c5f20ce1244f9c3a47a47342d46184fdd718290 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/bxt_tdf8532.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index c7b7fe3f9ed7..27361e8f72d3 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -76,6 +76,18 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { { "ssp3 Tx", NULL, "Modem_ssp3_out"}, }; +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { /* Probe DAI links*/ @@ -158,6 +170,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_capture = 1, .dpcm_playback = 1, .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, }, { /* SSP3 - Modem */ From 768262b9560c1dcd92f08869b73504d5e2ddaff2 Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Tue, 25 Jul 2017 17:49:58 +0530 Subject: [PATCH 0208/1276] ASoC: Intel: Skylake: Add support for GAIN module Gain module is represented by three entities such as Ramp Duration, Ramp Type and Volume controlled by three kcontrols. These kcontrols are added with the corresponding get and put handlers. A Large IPC message is sent to the DSP when any of these controls are set and if widget is in powered on state else these values are cached in driver and sent during init of gain module. Multichannel support for volume control is implemented by user defined kcontrol info method to set number of channels to what we are getting from mconfig out channels. Change-Id: I2473d636fb673f7a73118d7b5018f07127def69f Signed-off-by: Giribabu Gogineni Signed-off-by: Dharageswari.R Reviewed-on: Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 13 +- sound/soc/intel/skylake/skl-messages.c | 29 ++ sound/soc/intel/skylake/skl-topology.c | 467 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 13 + 4 files changed, 521 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index a3f0890c75bd..5e284a4b6ce0 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -20,6 +20,9 @@ #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 #define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 #define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 +#define SKL_CONTROL_TYPE_VOLUME 0x104 +#define SKL_CONTROL_TYPE_RAMP_DURATION 0x105 +#define SKL_CONTROL_TYPE_RAMP_TYPE 0x106 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 @@ -82,7 +85,8 @@ enum skl_module_type { SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, SKL_MODULE_TYPE_PROBE, - SKL_MODULE_TYPE_ASRC + SKL_MODULE_TYPE_ASRC, + SKL_MODULE_TYPE_GAIN }; enum skl_core_affinity { @@ -193,6 +197,13 @@ struct skl_dfw_algo_data { char params[0]; } __packed; +struct skl_gain_config { + u32 channel_id; + u32 target_volume; + u32 ramp_type; + u64 ramp_duration; +} __packed; + enum skl_tkn_dir { SKL_DIR_IN, SKL_DIR_OUT diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 02b3aad155f1..ba60269255f6 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -36,6 +36,7 @@ #define ASRC_MODE_UPLINK 2 #define ASRC_MODE_DOWNLINK 1 +#define SKL_ENABLE_ALL_CHANNELS 0xffffffff static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) @@ -1726,6 +1727,22 @@ static void skl_setup_out_format(struct skl_sst *ctx, out_fmt->number_of_channels, format->s_freq, format->bit_depth); } +static int skl_set_gain_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_gain_module_config *gain_mconfig) +{ + struct skl_gain_data *gain_fmt = mconfig->gain_data; + + skl_set_base_module_format(ctx, mconfig, + (struct skl_base_cfg *)gain_mconfig); + gain_mconfig->gain_cfg.channel_id = SKL_ENABLE_ALL_CHANNELS; + gain_mconfig->gain_cfg.target_volume = gain_fmt->volume[0]; + gain_mconfig->gain_cfg.ramp_type = gain_fmt->ramp_type; + gain_mconfig->gain_cfg.ramp_duration = gain_fmt->ramp_duration; + + return 0; +} + /* * DSP needs SRC module for frequency conversion, SRC takes base module * configuration and the target frequency as extra parameter passed as src @@ -1866,6 +1883,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, struct skl_module_cfg *mconfig) { u16 param_size; + struct skl_module_iface *m_intf; switch (mconfig->m_type) { case SKL_MODULE_TYPE_COPIER: @@ -1893,6 +1911,13 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, case SKL_MODULE_TYPE_KPB: return sizeof(struct skl_base_outfmt_cfg); + case SKL_MODULE_TYPE_GAIN: + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + param_size = sizeof(struct skl_base_cfg); + param_size += sizeof(struct skl_gain_config) + * m_intf->outputs[0].fmt.channels; + return param_size; + default: /* * return only base cfg when no specific module type is @@ -1954,6 +1979,10 @@ static int skl_set_module_format(struct skl_sst *ctx, skl_set_base_outfmt_format(ctx, module_config, *param_data); break; + case SKL_MODULE_TYPE_GAIN: + skl_set_gain_format(ctx, module_config, *param_data); + break; + default: skl_set_base_module_format(ctx, module_config, *param_data); break; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1a40c66e1c58..451b28a21585 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -32,6 +32,8 @@ #include "../common/sst-dsp-priv.h" #include "skl-fwlog.h" +#define SKL_CURVE_NONE 0 +#define SKL_MAX_GAIN 0x7FFFFFFF #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) #define SKL_FMT_FIXUP_MASK (1 << 2) @@ -62,6 +64,257 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { } \ } while (0) +/* + * The following table provides the gain in linear scale corresponding to + * gain in dB scale in the range of -144 dB to 0 dB with 0.1 dB resolution. + * The real number linear gain is scaled by 0x7FFFFFFFF to convert it to a + * 32 bit integer as required by FW. + * linear_gain[i] = 0 for i = 0 ; (Mapped as mute) + * = 0x7FFFFFFF*Round(10^(-144+0.1*i)/20) for i = 1 ... 1440 + */ +static u32 linear_gain[] = { +0x00000000, 0x00000089, 0x0000008B, 0x0000008C, 0x0000008E, 0x00000090, +0x00000091, 0x00000093, 0x00000095, 0x00000096, 0x00000098, 0x0000009A, +0x0000009C, 0x0000009D, 0x0000009F, 0x000000A1, 0x000000A3, 0x000000A5, +0x000000A7, 0x000000A9, 0x000000AB, 0x000000AD, 0x000000AF, 0x000000B1, +0x000000B3, 0x000000B5, 0x000000B7, 0x000000B9, 0x000000BB, 0x000000BD, +0x000000BF, 0x000000C2, 0x000000C4, 0x000000C6, 0x000000C8, 0x000000CB, +0x000000CD, 0x000000CF, 0x000000D2, 0x000000D4, 0x000000D7, 0x000000D9, +0x000000DC, 0x000000DE, 0x000000E1, 0x000000E3, 0x000000E6, 0x000000E9, +0x000000EB, 0x000000EE, 0x000000F1, 0x000000F4, 0x000000F7, 0x000000F9, +0x000000FC, 0x000000FF, 0x00000102, 0x00000105, 0x00000108, 0x0000010B, +0x0000010E, 0x00000111, 0x00000115, 0x00000118, 0x0000011B, 0x0000011E, +0x00000122, 0x00000125, 0x00000128, 0x0000012C, 0x0000012F, 0x00000133, +0x00000136, 0x0000013A, 0x0000013E, 0x00000141, 0x00000145, 0x00000149, +0x0000014D, 0x00000150, 0x00000154, 0x00000158, 0x0000015C, 0x00000160, +0x00000164, 0x00000169, 0x0000016D, 0x00000171, 0x00000175, 0x0000017A, +0x0000017E, 0x00000182, 0x00000187, 0x0000018B, 0x00000190, 0x00000195, +0x00000199, 0x0000019E, 0x000001A3, 0x000001A8, 0x000001AC, 0x000001B1, +0x000001B6, 0x000001BC, 0x000001C1, 0x000001C6, 0x000001CB, 0x000001D0, +0x000001D6, 0x000001DB, 0x000001E1, 0x000001E6, 0x000001EC, 0x000001F2, +0x000001F7, 0x000001FD, 0x00000203, 0x00000209, 0x0000020F, 0x00000215, +0x0000021B, 0x00000222, 0x00000228, 0x0000022E, 0x00000235, 0x0000023B, +0x00000242, 0x00000249, 0x0000024F, 0x00000256, 0x0000025D, 0x00000264, +0x0000026B, 0x00000273, 0x0000027A, 0x00000281, 0x00000289, 0x00000290, +0x00000298, 0x0000029F, 0x000002A7, 0x000002AF, 0x000002B7, 0x000002BF, +0x000002C7, 0x000002CF, 0x000002D8, 0x000002E0, 0x000002E9, 0x000002F1, +0x000002FA, 0x00000303, 0x0000030C, 0x00000315, 0x0000031E, 0x00000327, +0x00000330, 0x0000033A, 0x00000343, 0x0000034D, 0x00000357, 0x00000361, +0x0000036B, 0x00000375, 0x0000037F, 0x0000038A, 0x00000394, 0x0000039F, +0x000003A9, 0x000003B4, 0x000003BF, 0x000003CA, 0x000003D6, 0x000003E1, +0x000003EC, 0x000003F8, 0x00000404, 0x00000410, 0x0000041C, 0x00000428, +0x00000434, 0x00000441, 0x0000044D, 0x0000045A, 0x00000467, 0x00000474, +0x00000481, 0x0000048F, 0x0000049C, 0x000004AA, 0x000004B8, 0x000004C6, +0x000004D4, 0x000004E2, 0x000004F1, 0x000004FF, 0x0000050E, 0x0000051D, +0x0000052C, 0x0000053B, 0x0000054B, 0x0000055B, 0x0000056B, 0x0000057B, +0x0000058B, 0x0000059B, 0x000005AC, 0x000005BD, 0x000005CE, 0x000005DF, +0x000005F0, 0x00000602, 0x00000614, 0x00000626, 0x00000638, 0x0000064A, +0x0000065D, 0x00000670, 0x00000683, 0x00000696, 0x000006AA, 0x000006BE, +0x000006D2, 0x000006E6, 0x000006FA, 0x0000070F, 0x00000724, 0x00000739, +0x0000074E, 0x00000764, 0x0000077A, 0x00000790, 0x000007A7, 0x000007BD, +0x000007D4, 0x000007EB, 0x00000803, 0x0000081B, 0x00000833, 0x0000084B, +0x00000863, 0x0000087C, 0x00000896, 0x000008AF, 0x000008C9, 0x000008E3, +0x000008FD, 0x00000918, 0x00000933, 0x0000094E, 0x0000096A, 0x00000985, +0x000009A2, 0x000009BE, 0x000009DB, 0x000009F8, 0x00000A16, 0x00000A34, +0x00000A52, 0x00000A71, 0x00000A90, 0x00000AAF, 0x00000ACE, 0x00000AEF, +0x00000B0F, 0x00000B30, 0x00000B51, 0x00000B72, 0x00000B94, 0x00000BB7, +0x00000BD9, 0x00000BFD, 0x00000C20, 0x00000C44, 0x00000C68, 0x00000C8D, +0x00000CB2, 0x00000CD8, 0x00000CFE, 0x00000D25, 0x00000D4C, 0x00000D73, +0x00000D9B, 0x00000DC3, 0x00000DEC, 0x00000E15, 0x00000E3F, 0x00000E69, +0x00000E94, 0x00000EBF, 0x00000EEB, 0x00000F17, 0x00000F44, 0x00000F71, +0x00000F9F, 0x00000FCD, 0x00000FFC, 0x0000102B, 0x0000105B, 0x0000108C, +0x000010BD, 0x000010EE, 0x00001121, 0x00001153, 0x00001187, 0x000011BB, +0x000011EF, 0x00001224, 0x0000125A, 0x00001291, 0x000012C8, 0x000012FF, +0x00001338, 0x00001371, 0x000013AA, 0x000013E4, 0x0000141F, 0x0000145B, +0x00001497, 0x000014D4, 0x00001512, 0x00001551, 0x00001590, 0x000015D0, +0x00001610, 0x00001652, 0x00001694, 0x000016D7, 0x0000171B, 0x0000175F, +0x000017A4, 0x000017EB, 0x00001831, 0x00001879, 0x000018C2, 0x0000190B, +0x00001955, 0x000019A0, 0x000019EC, 0x00001A39, 0x00001A87, 0x00001AD6, +0x00001B25, 0x00001B76, 0x00001BC7, 0x00001C19, 0x00001C6D, 0x00001CC1, +0x00001D16, 0x00001D6C, 0x00001DC4, 0x00001E1C, 0x00001E75, 0x00001ECF, +0x00001F2B, 0x00001F87, 0x00001FE5, 0x00002043, 0x000020A3, 0x00002103, +0x00002165, 0x000021C8, 0x0000222C, 0x00002292, 0x000022F8, 0x00002360, +0x000023C9, 0x00002433, 0x0000249E, 0x0000250B, 0x00002578, 0x000025E8, +0x00002658, 0x000026CA, 0x0000273D, 0x000027B1, 0x00002827, 0x0000289E, +0x00002916, 0x00002990, 0x00002A0B, 0x00002A88, 0x00002B06, 0x00002B85, +0x00002C06, 0x00002C89, 0x00002D0D, 0x00002D92, 0x00002E19, 0x00002EA2, +0x00002F2C, 0x00002FB8, 0x00003045, 0x000030D5, 0x00003165, 0x000031F8, +0x0000328C, 0x00003322, 0x000033B9, 0x00003453, 0x000034EE, 0x0000358B, +0x00003629, 0x000036CA, 0x0000376C, 0x00003811, 0x000038B7, 0x0000395F, +0x00003A09, 0x00003AB5, 0x00003B63, 0x00003C13, 0x00003CC5, 0x00003D79, +0x00003E30, 0x00003EE8, 0x00003FA2, 0x0000405F, 0x0000411E, 0x000041DF, +0x000042A2, 0x00004368, 0x0000442F, 0x000044FA, 0x000045C6, 0x00004695, +0x00004766, 0x0000483A, 0x00004910, 0x000049E8, 0x00004AC3, 0x00004BA1, +0x00004C81, 0x00004D64, 0x00004E49, 0x00004F32, 0x0000501C, 0x0000510A, +0x000051FA, 0x000052ED, 0x000053E3, 0x000054DC, 0x000055D7, 0x000056D6, +0x000057D7, 0x000058DB, 0x000059E3, 0x00005AED, 0x00005BFB, 0x00005D0B, +0x00005E1F, 0x00005F36, 0x00006050, 0x0000616E, 0x0000628F, 0x000063B3, +0x000064DA, 0x00006605, 0x00006734, 0x00006866, 0x0000699B, 0x00006AD4, +0x00006C11, 0x00006D51, 0x00006E95, 0x00006FDD, 0x00007129, 0x00007278, +0x000073CC, 0x00007523, 0x0000767E, 0x000077DD, 0x00007941, 0x00007AA8, +0x00007C14, 0x00007D83, 0x00007EF7, 0x00008070, 0x000081ED, 0x0000836E, +0x000084F3, 0x0000867D, 0x0000880C, 0x0000899F, 0x00008B37, 0x00008CD4, +0x00008E76, 0x0000901C, 0x000091C7, 0x00009377, 0x0000952C, 0x000096E6, +0x000098A6, 0x00009A6A, 0x00009C34, 0x00009E03, 0x00009FD7, 0x0000A1B1, +0x0000A391, 0x0000A575, 0x0000A760, 0x0000A950, 0x0000AB46, 0x0000AD42, +0x0000AF43, 0x0000B14B, 0x0000B358, 0x0000B56C, 0x0000B786, 0x0000B9A6, +0x0000BBCC, 0x0000BDF9, 0x0000C02C, 0x0000C266, 0x0000C4A6, 0x0000C6ED, +0x0000C93B, 0x0000CB8F, 0x0000CDEA, 0x0000D04D, 0x0000D2B6, 0x0000D527, +0x0000D79F, 0x0000DA1E, 0x0000DCA5, 0x0000DF33, 0x0000E1C8, 0x0000E466, +0x0000E70B, 0x0000E9B7, 0x0000EC6C, 0x0000EF29, 0x0000F1EE, 0x0000F4BB, +0x0000F791, 0x0000FA6F, 0x0000FD55, 0x00010044, 0x0001033C, 0x0001063C, +0x00010945, 0x00010C58, 0x00010F73, 0x00011298, 0x000115C6, 0x000118FD, +0x00011C3E, 0x00011F89, 0x000122DD, 0x0001263B, 0x000129A4, 0x00012D16, +0x00013092, 0x00013419, 0x000137AB, 0x00013B46, 0x00013EED, 0x0001429E, +0x0001465B, 0x00014A22, 0x00014DF5, 0x000151D3, 0x000155BC, 0x000159B1, +0x00015DB2, 0x000161BF, 0x000165D7, 0x000169FC, 0x00016E2D, 0x0001726B, +0x000176B5, 0x00017B0B, 0x00017F6F, 0x000183E0, 0x0001885D, 0x00018CE8, +0x00019181, 0x00019627, 0x00019ADB, 0x00019F9D, 0x0001A46D, 0x0001A94B, +0x0001AE38, 0x0001B333, 0x0001B83E, 0x0001BD57, 0x0001C27F, 0x0001C7B6, +0x0001CCFD, 0x0001D254, 0x0001D7BA, 0x0001DD30, 0x0001E2B7, 0x0001E84E, +0x0001EDF5, 0x0001F3AD, 0x0001F977, 0x0001FF51, 0x0002053D, 0x00020B3A, +0x00021149, 0x0002176A, 0x00021D9D, 0x000223E3, 0x00022A3B, 0x000230A6, +0x00023724, 0x00023DB5, 0x0002445A, 0x00024B12, 0x000251DE, 0x000258BF, +0x00025FB3, 0x000266BD, 0x00026DDB, 0x0002750F, 0x00027C57, 0x000283B6, +0x00028B2A, 0x000292B4, 0x00029A55, 0x0002A20C, 0x0002A9DA, 0x0002B1BF, +0x0002B9BC, 0x0002C1D0, 0x0002C9FD, 0x0002D241, 0x0002DA9E, 0x0002E314, +0x0002EBA3, 0x0002F44B, 0x0002FD0D, 0x000305E9, 0x00030EDF, 0x000317F0, +0x0003211B, 0x00032A62, 0x000333C4, 0x00033D42, 0x000346DC, 0x00035093, +0x00035A66, 0x00036457, 0x00036E65, 0x00037891, 0x000382DB, 0x00038D44, +0x000397CB, 0x0003A271, 0x0003AD38, 0x0003B81E, 0x0003C324, 0x0003CE4B, +0x0003D993, 0x0003E4FD, 0x0003F088, 0x0003FC36, 0x00040806, 0x000413F9, +0x00042010, 0x00042C4B, 0x000438A9, 0x0004452D, 0x000451D5, 0x00045EA4, +0x00046B98, 0x000478B2, 0x000485F3, 0x0004935C, 0x0004A0EC, 0x0004AEA5, +0x0004BC86, 0x0004CA90, 0x0004D8C4, 0x0004E722, 0x0004F5AB, 0x0005045F, +0x0005133E, 0x00052249, 0x00053181, 0x000540E6, 0x00055079, 0x0005603A, +0x0005702A, 0x00058048, 0x00059097, 0x0005A116, 0x0005B1C6, 0x0005C2A7, +0x0005D3BB, 0x0005E501, 0x0005F67A, 0x00060827, 0x00061A08, 0x00062C1F, +0x00063E6B, 0x000650ED, 0x000663A6, 0x00067697, 0x000689BF, 0x00069D21, +0x0006B0BC, 0x0006C491, 0x0006D8A1, 0x0006ECEC, 0x00070174, 0x00071638, +0x00072B3A, 0x0007407A, 0x000755FA, 0x00076BB9, 0x000781B8, 0x000797F9, +0x0007AE7B, 0x0007C541, 0x0007DC49, 0x0007F397, 0x00080B29, 0x00082301, +0x00083B20, 0x00085386, 0x00086C34, 0x0008852C, 0x00089E6E, 0x0008B7FA, +0x0008D1D3, 0x0008EBF8, 0x0009066A, 0x0009212B, 0x00093C3B, 0x0009579C, +0x0009734D, 0x00098F51, 0x0009ABA7, 0x0009C852, 0x0009E552, 0x000A02A7, +0x000A2054, 0x000A3E58, 0x000A5CB6, 0x000A7B6D, 0x000A9A80, 0x000AB9EF, +0x000AD9BB, 0x000AF9E5, 0x000B1A6E, 0x000B3B58, 0x000B5CA4, 0x000B7E52, +0x000BA064, 0x000BC2DB, 0x000BE5B8, 0x000C08FD, 0x000C2CAA, 0x000C50C1, +0x000C7543, 0x000C9A31, 0x000CBF8C, 0x000CE556, 0x000D0B91, 0x000D323C, +0x000D595A, 0x000D80ED, 0x000DA8F4, 0x000DD172, 0x000DFA69, 0x000E23D8, +0x000E4DC3, 0x000E7829, 0x000EA30E, 0x000ECE71, 0x000EFA55, 0x000F26BC, +0x000F53A6, 0x000F8115, 0x000FAF0A, 0x000FDD88, 0x00100C90, 0x00103C23, +0x00106C43, 0x00109CF2, 0x0010CE31, 0x00110003, 0x00113267, 0x00116562, +0x001198F3, 0x0011CD1D, 0x001201E2, 0x00123743, 0x00126D43, 0x0012A3E2, +0x0012DB24, 0x00131309, 0x00134B94, 0x001384C7, 0x0013BEA3, 0x0013F92B, +0x00143460, 0x00147044, 0x0014ACDB, 0x0014EA24, 0x00152824, 0x001566DB, +0x0015A64C, 0x0015E67A, 0x00162765, 0x00166911, 0x0016AB80, 0x0016EEB3, +0x001732AE, 0x00177772, 0x0017BD02, 0x00180361, 0x00184A90, 0x00189292, +0x0018DB69, 0x00192518, 0x00196FA2, 0x0019BB09, 0x001A074F, 0x001A5477, +0x001AA284, 0x001AF179, 0x001B4157, 0x001B9222, 0x001BE3DD, 0x001C368A, +0x001C8A2C, 0x001CDEC6, 0x001D345B, 0x001D8AED, 0x001DE280, 0x001E3B17, +0x001E94B4, 0x001EEF5B, 0x001F4B0F, 0x001FA7D2, 0x002005A9, 0x00206496, +0x0020C49C, 0x002125BE, 0x00218801, 0x0021EB67, 0x00224FF3, 0x0022B5AA, +0x00231C8E, 0x002384A3, 0x0023EDED, 0x0024586F, 0x0024C42C, 0x00253129, +0x00259F69, 0x00260EF0, 0x00267FC1, 0x0026F1E1, 0x00276553, 0x0027DA1C, +0x0028503E, 0x0028C7BF, 0x002940A2, 0x0029BAEB, 0x002A369F, 0x002AB3C1, +0x002B3257, 0x002BB263, 0x002C33EC, 0x002CB6F4, 0x002D3B81, 0x002DC196, +0x002E4939, 0x002ED26E, 0x002F5D3A, 0x002FE9A2, 0x003077A9, 0x00310756, +0x003198AC, 0x00322BB1, 0x0032C06A, 0x003356DC, 0x0033EF0C, 0x003488FF, +0x003524BB, 0x0035C244, 0x003661A0, 0x003702D4, 0x0037A5E6, 0x00384ADC, +0x0038F1BB, 0x00399A88, 0x003A454A, 0x003AF206, 0x003BA0C2, 0x003C5184, +0x003D0452, 0x003DB932, 0x003E702A, 0x003F2940, 0x003FE47B, 0x0040A1E2, +0x00416179, 0x00422349, 0x0042E757, 0x0043ADAA, 0x00447649, 0x0045413B, +0x00460E87, 0x0046DE33, 0x0047B046, 0x004884C9, 0x00495BC1, 0x004A3537, +0x004B1131, 0x004BEFB7, 0x004CD0D1, 0x004DB486, 0x004E9ADE, 0x004F83E1, +0x00506F97, 0x00515E08, 0x00524F3B, 0x00534339, 0x00543A0B, 0x005533B8, +0x00563049, 0x00572FC8, 0x0058323B, 0x005937AD, 0x005A4025, 0x005B4BAE, +0x005C5A4F, 0x005D6C13, 0x005E8102, 0x005F9927, 0x0060B48A, 0x0061D334, +0x0062F531, 0x00641A89, 0x00654347, 0x00666F74, 0x00679F1C, 0x0068D247, +0x006A0901, 0x006B4354, 0x006C814B, 0x006DC2F0, 0x006F084F, 0x00705172, +0x00719E65, 0x0072EF33, 0x007443E8, 0x00759C8E, 0x0076F932, 0x007859DF, +0x0079BEA2, 0x007B2787, 0x007C9499, 0x007E05E6, 0x007F7B79, 0x0080F560, +0x008273A6, 0x0083F65A, 0x00857D89, 0x0087093F, 0x0088998A, 0x008A2E77, +0x008BC815, 0x008D6672, 0x008F099A, 0x0090B19D, 0x00925E89, 0x0094106D, +0x0095C756, 0x00978355, 0x00994478, 0x009B0ACE, 0x009CD667, 0x009EA752, +0x00A07DA0, 0x00A25960, 0x00A43AA2, 0x00A62177, 0x00A80DEE, 0x00AA001A, +0x00ABF80A, 0x00ADF5D1, 0x00AFF97E, 0x00B20324, 0x00B412D4, 0x00B628A1, +0x00B8449C, 0x00BA66D8, 0x00BC8F67, 0x00BEBE5B, 0x00C0F3C9, 0x00C32FC3, +0x00C5725D, 0x00C7BBA9, 0x00CA0BBD, 0x00CC62AC, 0x00CEC08A, 0x00D1256C, +0x00D39167, 0x00D60490, 0x00D87EFC, 0x00DB00C0, 0x00DD89F3, 0x00E01AAB, +0x00E2B2FD, 0x00E55300, 0x00E7FACC, 0x00EAAA77, 0x00ED6218, 0x00F021C7, +0x00F2E99C, 0x00F5B9B0, 0x00F89219, 0x00FB72F2, 0x00FE5C54, 0x01014E57, +0x01044915, 0x01074CA8, 0x010A592A, 0x010D6EB6, 0x01108D67, 0x0113B557, +0x0116E6A2, 0x011A2164, 0x011D65B9, 0x0120B3BC, 0x01240B8C, 0x01276D45, +0x012AD904, 0x012E4EE7, 0x0131CF0B, 0x01355991, 0x0138EE96, 0x013C8E39, +0x0140389A, 0x0143EDD8, 0x0147AE14, 0x014B796F, 0x014F500A, 0x01533205, +0x01571F82, 0x015B18A5, 0x015F1D8E, 0x01632E61, 0x01674B42, 0x016B7454, +0x016FA9BB, 0x0173EB9C, 0x01783A1B, 0x017C955F, 0x0180FD8D, 0x018572CB, +0x0189F540, 0x018E8513, 0x0193226D, 0x0197CD74, 0x019C8651, 0x01A14D2E, +0x01A62234, 0x01AB058D, 0x01AFF764, 0x01B4F7E3, 0x01BA0735, 0x01BF2588, +0x01C45306, 0x01C98FDE, 0x01CEDC3D, 0x01D43850, 0x01D9A447, 0x01DF2050, +0x01E4AC9B, 0x01EA4958, 0x01EFF6B8, 0x01F5B4ED, 0x01FB8428, 0x0201649B, +0x0207567A, 0x020D59F9, 0x02136F4B, 0x021996A5, 0x021FD03D, 0x02261C4A, +0x022C7B01, 0x0232EC9A, 0x0239714D, 0x02400952, 0x0246B4E4, 0x024D743B, +0x02544792, 0x025B2F26, 0x02622B31, 0x02693BF0, 0x027061A1, 0x02779C82, +0x027EECD2, 0x028652D0, 0x028DCEBC, 0x029560D8, 0x029D0964, 0x02A4C8A5, +0x02AC9EDD, 0x02B48C50, 0x02BC9142, 0x02C4ADFB, 0x02CCE2BF, 0x02D52FD7, +0x02DD958A, 0x02E61422, 0x02EEABE8, 0x02F75D27, 0x0300282A, 0x03090D3F, +0x03120CB1, 0x031B26CF, 0x03245BE9, 0x032DAC4D, 0x0337184E, 0x0340A03D, +0x034A446D, 0x03540531, 0x035DE2DF, 0x0367DDCC, 0x0371F64E, 0x037C2CBD, +0x03868173, 0x0390F4C8, 0x039B8719, 0x03A638BF, 0x03B10A19, 0x03BBFB84, +0x03C70D60, 0x03D2400C, 0x03DD93E9, 0x03E9095B, 0x03F4A0C5, 0x04005A8B, +0x040C3714, 0x041836C5, 0x04245A09, 0x0430A147, 0x043D0CEB, 0x04499D60, +0x04565314, 0x04632E76, 0x04702FF4, 0x047D57FF, 0x048AA70B, 0x04981D8B, +0x04A5BBF3, 0x04B382B9, 0x04C17257, 0x04CF8B44, 0x04DDCDFB, 0x04EC3AF8, +0x04FAD2B9, 0x050995BB, 0x05188480, 0x05279F89, 0x0536E758, 0x05465C74, +0x0555FF62, 0x0565D0AB, 0x0575D0D6, 0x05860070, 0x05966005, 0x05A6F023, +0x05B7B15B, 0x05C8A43D, 0x05D9C95D, 0x05EB2150, 0x05FCACAD, 0x060E6C0B, +0x06206006, 0x06328938, 0x0644E841, 0x06577DBE, 0x066A4A53, 0x067D4EA2, +0x06908B50, 0x06A40104, 0x06B7B068, 0x06CB9A26, 0x06DFBEEC, 0x06F41F68, +0x0708BC4C, 0x071D964A, 0x0732AE18, 0x0748046D, 0x075D9A02, 0x07736F92, +0x078985DC, 0x079FDD9F, 0x07B6779E, 0x07CD549C, 0x07E47560, 0x07FBDAB4, +0x08138562, 0x082B7638, 0x0843AE06, 0x085C2D9E, 0x0874F5D6, 0x088E0783, +0x08A76381, 0x08C10AAC, 0x08DAFDE2, 0x08F53E04, 0x090FCBF7, 0x092AA8A2, +0x0945D4EE, 0x096151C6, 0x097D201A, 0x099940DB, 0x09B5B4FE, 0x09D27D79, +0x09EF9B47, 0x0A0D0F64, 0x0A2ADAD1, 0x0A48FE91, 0x0A677BA8, 0x0A865320, +0x0AA58606, 0x0AC51567, 0x0AE50256, 0x0B054DE8, 0x0B25F937, 0x0B47055D, +0x0B68737A, 0x0B8A44AF, 0x0BAC7A24, 0x0BCF1501, 0x0BF21673, 0x0C157FA9, +0x0C3951D8, 0x0C5D8E36, 0x0C8235FF, 0x0CA74A70, 0x0CCCCCCD, 0x0CF2BE5A, +0x0D192061, 0x0D3FF430, 0x0D673B17, 0x0D8EF66D, 0x0DB7278B, 0x0DDFCFCC, +0x0E08F094, 0x0E328B46, 0x0E5CA14C, 0x0E873415, 0x0EB24511, 0x0EDDD5B7, +0x0F09E781, 0x0F367BEE, 0x0F639481, 0x0F9132C3, 0x0FBF583F, 0x0FEE0686, +0x101D3F2D, 0x104D03D0, 0x107D560D, 0x10AE3787, 0x10DFA9E7, 0x1111AEDB, +0x11444815, 0x1177774D, 0x11AB3E3F, 0x11DF9EAE, 0x12149A60, 0x124A3321, +0x12806AC3, 0x12B7431D, 0x12EEBE0C, 0x1326DD70, 0x135FA333, 0x13991141, +0x13D3298C, 0x140DEE0E, 0x144960C5, 0x148583B6, 0x14C258EA, 0x14FFE273, +0x153E2266, 0x157D1AE2, 0x15BCCE07, 0x15FD3E01, 0x163E6CFE, 0x16805D35, +0x16C310E3, 0x17068A4B, 0x174ACBB8, 0x178FD779, 0x17D5AFE8, 0x181C5762, +0x1863D04D, 0x18AC1D17, 0x18F54033, 0x193F3C1D, 0x198A1357, 0x19D5C86C, +0x1A225DED, 0x1A6FD673, 0x1ABE349F, 0x1B0D7B1B, 0x1B5DAC97, 0x1BAECBCA, +0x1C00DB77, 0x1C53DE66, 0x1CA7D768, 0x1CFCC956, 0x1D52B712, 0x1DA9A387, +0x1E0191A9, 0x1E5A8471, 0x1EB47EE7, 0x1F0F8416, 0x1F6B9715, 0x1FC8BB06, +0x2026F30F, 0x20864265, 0x20E6AC43, 0x214833EE, 0x21AADCB6, 0x220EA9F4, +0x22739F0A, 0x22D9BF65, 0x23410E7E, 0x23A98FD5, 0x241346F6, 0x247E3777, +0x24EA64F9, 0x2557D328, 0x25C685BB, 0x26368073, 0x26A7C71D, 0x271A5D91, +0x278E47B3, 0x28038970, 0x287A26C4, 0x28F223B6, 0x296B8457, 0x29E64CC5, +0x2A62812C, 0x2AE025C3, 0x2B5F3ECC, 0x2BDFD098, 0x2C61DF84, 0x2CE56FF9, +0x2D6A866F, 0x2DF12769, 0x2E795779, 0x2F031B3E, 0x2F8E7765, 0x301B70A8, +0x30AA0BCF, 0x313A4DB3, 0x31CC3B37, 0x325FD94F, 0x32F52CFF, 0x338C3B56, +0x34250975, 0x34BF9C8B, 0x355BF9D8, 0x35FA26A9, 0x369A285D, 0x373C0461, +0x37DFC033, 0x38856163, 0x392CED8E, 0x39D66A63, 0x3A81DDA4, 0x3B2F4D22, +0x3BDEBEBF, 0x3C90386F, 0x3D43C038, 0x3DF95C32, 0x3EB11285, 0x3F6AE96F, +0x4026E73C, 0x40E5124F, 0x41A5711B, 0x42680A28, 0x432CE40F, 0x43F4057E, +0x44BD7539, 0x45893A13, 0x46575AF8, 0x4727DEE6, 0x47FACCF0, 0x48D02C3F, +0x49A8040F, 0x4A825BB5, 0x4B5F3A99, 0x4C3EA838, 0x4D20AC29, 0x4E054E17, +0x4EEC95C3, 0x4FD68B07, 0x50C335D3, 0x51B29E2F, 0x52A4CC3A, 0x5399C82D, +0x54919A57, 0x558C4B22, 0x5689E30E, 0x578A6AB7, 0x588DEAD1, 0x59946C2A, +0x5A9DF7AB, 0x5BAA9656, 0x5CBA514A, 0x5DCD31BD, 0x5EE34105, 0x5FFC8890, +0x611911E9, 0x6238E6BA, 0x635C10C5, 0x648299EC, 0x65AC8C2E, 0x66D9F1A7, +0x680AD491, 0x693F3F45, 0x6A773C39, 0x6BB2D603, 0x6CF2175A, 0x6E350B13, +0x6F7BBC23, 0x70C6359F, 0x721482BF, 0x7366AEDB, 0x74BCC56B, 0x7616D20D, +0x7774E07D, 0x78D6FC9E, 0x7A3D3271, 0x7BA78E21, 0x7D161BF7, 0x7E88E865, +0x7FFFFFFF}; + static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, struct skl *skl); @@ -1808,6 +2061,191 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, return 0; } +static int skl_tplg_send_gain_ipc(struct snd_soc_dapm_context *dapm, + struct skl_module_cfg *mconfig) +{ + struct skl_gain_config *gain_cfg; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_module_iface *m_intf; + int num_channel, i, ret = 0; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + num_channel = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + + gain_cfg = kzalloc(sizeof(*gain_cfg), GFP_KERNEL); + if (!gain_cfg) + return -ENOMEM; + + gain_cfg->ramp_type = mconfig->gain_data->ramp_type; + gain_cfg->ramp_duration = mconfig->gain_data->ramp_duration; + for (i = 0; i < num_channel; i++) { + gain_cfg->channel_id = i; + gain_cfg->target_volume = mconfig->gain_data->volume[i]; + ret = skl_set_module_params(skl->skl_sst, (u32 *)gain_cfg, + sizeof(*gain_cfg), 0, mconfig); + if (ret < 0) { + dev_err(dapm->dev, + "set gain for channel:%d failed\n", i); + break; + } + } + kfree(gain_cfg); + + return ret; +} + +static int skl_tplg_ramp_duration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + + ucontrol->value.integer.value[0] = mconfig->gain_data->ramp_duration; + + return 0; +} + +static int skl_tplg_ramp_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + + ucontrol->value.integer.value[0] = mconfig->gain_data->ramp_type; + + return 0; +} + +static int skl_tplg_get_linear_toindex(int val) +{ + int i, index = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(linear_gain); i++) { + if (val == linear_gain[i]) { + index = i; + break; + } + } + + return index; +} + +static int skl_tplg_volume_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc; + struct snd_soc_dapm_widget *w; + struct skl_module_iface *m_intf; + struct skl_module_cfg *mconfig; + + mc = (struct soc_mixer_control *)kcontrol->private_value; + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = m_intf->outputs[0].fmt.channels; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} + +static int skl_tplg_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct skl_module_iface *m_intf; + int i, max_channels; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + max_channels = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + for (i = 0; i < max_channels; i++) + ucontrol->value.integer.value[i] = + skl_tplg_get_linear_toindex( + mconfig->gain_data->volume[i]); + + return 0; +} + +static int skl_tplg_ramp_duration_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret = 0; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + mconfig->gain_data->ramp_duration = ucontrol->value.integer.value[0]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + return ret; +} + +static int skl_tplg_ramp_type_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret = 0; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + mconfig->gain_data->ramp_type = ucontrol->value.integer.value[0]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + + return ret; +} + +static int skl_tplg_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_module_iface *m_intf; + int ret = 0, i, max_channels; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + max_channels = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + + for (i = 0; i < max_channels; i++) + if (ucontrol->value.integer.value[i] >= + ARRAY_SIZE(linear_gain)) { + dev_err(dapm->dev, + "Volume requested is out of range!!!\n"); + return -EINVAL; + } + + for (i = 0; i < max_channels; i++) + mconfig->gain_data->volume[i] = + linear_gain[ucontrol->value.integer.value[i]]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + + return ret; +} + static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2832,6 +3270,22 @@ static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { .get = skl_tplg_multi_config_get, .put = skl_tplg_multi_config_set, }, + { + .id = SKL_CONTROL_TYPE_VOLUME, + .info = skl_tplg_volume_ctl_info, + .get = skl_tplg_volume_get, + .put = skl_tplg_volume_set, + }, + { + .id = SKL_CONTROL_TYPE_RAMP_DURATION, + .get = skl_tplg_ramp_duration_get, + .put = skl_tplg_ramp_duration_set, + }, + { + .id = SKL_CONTROL_TYPE_RAMP_TYPE, + .get = skl_tplg_ramp_type_get, + .put = skl_tplg_ramp_type_set, + }, }; static int skl_tplg_fill_pipe_cfg(struct device *dev, @@ -3922,6 +4376,19 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, if (ret < 0) return ret; + if (mconfig->m_type == SKL_MODULE_TYPE_GAIN) { + mconfig->gain_data = devm_kzalloc(bus->dev, + sizeof(*mconfig->gain_data), GFP_KERNEL); + + if (!mconfig->gain_data) + return -ENOMEM; + + mconfig->gain_data->ramp_duration = 0; + mconfig->gain_data->ramp_type = SKL_CURVE_NONE; + for (i = 0; i < MAX_NUM_CHANNELS; i++) + mconfig->gain_data->volume[i] = SKL_MAX_GAIN; + } + skl_debug_init_module(skl->debugfs, w, mconfig); bind_event: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index f9ac42cdc64d..cc23a18fb2b4 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -55,6 +55,7 @@ #define SKL_MAX_MODULES_IN_PIPE 8 #define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 +#define MAX_NUM_CHANNELS 8 enum skl_channel_index { SKL_CHANNEL_LEFT = 0, @@ -258,6 +259,11 @@ struct skl_kpb_params { } u; }; +struct skl_gain_module_config { + struct skl_base_cfg mconf; + struct skl_gain_config gain_cfg; +}; + struct skl_module_inst_id { uuid_le mod_uuid; int module_id; @@ -415,6 +421,12 @@ struct skl_sdw_aggregation { struct skl_sdw_agg_data agg_data[4]; }; +struct skl_gain_data { + u64 ramp_duration; + u32 ramp_type; + u32 volume[MAX_NUM_CHANNELS]; +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -459,6 +471,7 @@ struct skl_module_cfg { struct skl_pipe *pipe; struct skl_specific_cfg formats_config; struct skl_pipe_mcfg mod_cfg[SKL_MAX_MODULES_IN_PIPE]; + struct skl_gain_data *gain_data; }; struct skl_algo_data { From b3edb01e1ef8c8cbd4aabdb86da4a49d4680b330 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:55 +0530 Subject: [PATCH 0209/1276] ASoC: Intel: Skylake: Fix codec_dai NULL pointer dereferening Pointer 'codec_dai' returned from call to cnl_get_codec_dai() can be NULL. Check for the valid pointer before dereferencing. Change-Id: I783b6220e32a9b8bf7655b92df7a4b034175a509 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 07a876f4f3b2..5570474806ab 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -76,6 +76,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, int ret = 0, ratio = 100; struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; /* Codec needs clock for Jack detection and button press */ ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, From b20d26a715bfee47d45eff4d24fe7e2fbe0a2701 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:56 +0530 Subject: [PATCH 0210/1276] ASoC: Intel: Skylake: Check for word_length_buffer allcation failure word_length_buffer buffer allocation can fail. Make sure to return -ENOMEM on word_length_buffer allocation failure Change-Id: Idf31300cadd6f014f729cae09e981b4459694dd9 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/cnl-sst.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c99b2c931202..813cd73545e4 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -638,6 +638,8 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dpn_cap->word_length_buffer = kzalloc(((sizeof(unsigned int)) * dpn_cap->num_word_length), GFP_KERNEL); + if (!dpn_cap->word_length_buffer) + return -ENOMEM; for (k = 0; k < dpn_cap->num_word_length; k++) dpn_cap->word_length_buffer[k] = wl = wl + 8; wl = 0; From 82ed25065a04dca6a2fac1e6a7df4340f70e3c1f Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:57 +0530 Subject: [PATCH 0211/1276] ASoC: Intel: Skylake: Fix cnl_sdw_startup() error path In cnl_sdw_startup function, error -EINVAL is returned after invalid stream type is detected without freeing the previously allocated sdw_dma_data memory. Add the missing error handling path to avoid memory leak. Change-Id: Iacdc9608ad65075d68ff46e0410c49c8358491cf Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sdw-pcm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 6b8c4dba5b01..955c952cc4bb 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -97,7 +97,8 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->stream_type = CNL_SDW_PDI_TYPE_PDM; else { dev_err(dai->dev, "Stream type not known\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma; } dma->mstr = mstr; dma->mstr_nr = sdw_ctrl_nr; @@ -113,13 +114,13 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); ret = -EINVAL; - goto alloc_stream_tag_failed; + goto free_dma; } ret = snd_soc_dai_program_stream_tag(substream, dai, dma->stream_tag); dma->stream_state = STREAM_STATE_ALLOC_STREAM_TAG; return 0; -alloc_stream_tag_failed: +free_dma: kfree(dma); alloc_failed: sdw_put_master(mstr); From 1a08abdcd22ab0b62301bfd2894ffab5ac016bf9 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:58 +0530 Subject: [PATCH 0212/1276] ASoC: Intel: Skylake: Fix error handling in cnl_sdw_hw_params() There are bunch of error paths where allocated resources are not freed before returning. Add the missing error handling to free up the allocated resources. Change-Id: I2b7b3e901d6878f951823661cfc32f03167c32fa Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sdw-pcm.c | 34 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 955c952cc4bb..fb3186a5a35f 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -141,6 +141,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; int i, upscale_factor = 16; + int nr_port; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -168,13 +169,14 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, if (!dma->port) return -ENOMEM; - for (i = 0; i < dma->nr_ports; i++) { + for (nr_port = 0; nr_port < dma->nr_ports; nr_port++) { /* Dynamically alloc port and PDI streams for this DAI */ - dma->port[i] = cnl_sdw_alloc_port(dma->mstr, channels, + dma->port[nr_port] = cnl_sdw_alloc_port(dma->mstr, channels, direction, dma->stream_type); - if (!dma->port[i]) { + if (!dma->port[nr_port]) { dev_err(dai->dev, "Unable to allocate port\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma_port; } } @@ -182,7 +184,8 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); if (!m_cfg) { dev_err(dai->dev, "BE Copier not found\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma_port; } if (!m_cfg->sdw_agg_enable) @@ -192,7 +195,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dma->port[0]->pdi_stream->sdw_pdi_num; ret = skl_tplg_be_update_params(dai, &p_params); if (ret) - return ret; + goto free_dma_port; stream_config.frame_rate = params_rate(params); @@ -216,13 +219,14 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure the stream\n"); - return ret; + goto free_dma_port; } port_cfg = kcalloc(dma->nr_ports, sizeof(struct sdw_port_cfg), GFP_KERNEL); - if (!port_cfg) - return -ENOMEM; - + if (!port_cfg) { + ret = -ENOMEM; + goto free_dma_port; + } port_config.num_ports = dma->nr_ports; port_config.port_cfg = port_cfg; @@ -238,10 +242,18 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure port\n"); - return ret; + goto free_port_cfg; } dma->stream_state = STREAM_STATE_CONFIG_STREAM; return 0; + +free_port_cfg: + kfree(port_cfg); +free_dma_port: + while (nr_port--) + cnl_sdw_free_port(dma->mstr, dma->port[nr_port]->port_num); + kfree(dma->port); + return ret; } int cnl_sdw_hw_free(struct snd_pcm_substream *substream, From 104f29001edbf0312dbd75185be495116483c1f7 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Mon, 18 Sep 2017 16:03:41 +0530 Subject: [PATCH 0213/1276] ASoC: Intel: Skylake: Check for NHLT ACPI header signature Due to buggy BIOS acpi_evaluate_dsm() may not return the correct NHLT table, so check the NHLT table header signature before accessing it. Change-Id: I525cc504f1cf71d969bdcc2120b8a280dedeb4f5 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 742b0cb0dd15..284e765eebad 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -28,6 +28,8 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); +#define NHLT_ACPI_HEADER_SIG "NHLT" + int skl_get_nhlt_version(struct device *dev) { const char *version; From 02d3a2f8f1de6cf9db8c34212257f26f170d0107 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Thu, 6 Oct 2016 16:23:09 +0530 Subject: [PATCH 0214/1276] ASoC: Intel: Skylake: Fix Max DSP MCPS value Currently Max MCPS is specified as 30M, but it should be 350M. However actual MCPS is 400M, since module CPS are simulated on HW and it's peak MCPS is taken into count instead of average cycle count. So it is limited to 350M to avoid MCPS overshooting. Change-Id: I40f1a5b8ec66611da52cbfc9365b045015133270 Signed-off-by: Kareem Shaik Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sinha, Mohit Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 451b28a21585..1931d4ed73f5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -5166,7 +5166,7 @@ static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) } /* This will be read from topology manifest, currently defined here */ -#define SKL_MAX_MCPS 30000000 +#define SKL_MAX_MCPS 350000000 #define SKL_FW_MAX_MEM 1000000 /* From 592668afed80b7d1d2eb56473bc6c28b44f58d73 Mon Sep 17 00:00:00 2001 From: "Mallikarjun, chippalkatti" Date: Mon, 19 Jun 2017 09:33:05 +0530 Subject: [PATCH 0215/1276] ASoC: Intel: Skylake: Fix bug in module id retrieval for link copier This was a copy paste mistake introduced in commit e3bd94ea9644 ("ASoC: Intel: CNL: Retrieve module id from GUID").So, fix it. Change-Id: Iab9bcf9433f0e2441bf88a886a762b93f5bbdf31 Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: R, Dharageswari Reviewed-by: audio_build Reviewed-by: Sinha, Mohit Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ba60269255f6..891e24842be3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -702,8 +702,8 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; - host_cpr_cfg->id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg->guid); + link_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)link_cpr_cfg->guid); link_cpr_cfg->id.instance_id = 3; link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, From 45ce3687cee14c15e237accb20ba8c963af0bca0 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 11 Oct 2017 10:41:43 +0530 Subject: [PATCH 0216/1276] ASoC: Intel: Skylake: Fix incorrect in_fmt and out_fmt pointers in BRA playback pipeline Fix the in_fmt/out_fmt pointers for host copier of playback pipeline. Change-Id: I8a6c66ec5ebc291ae968ebca779d702cc4de105e Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Sinha, Mohit Reviewed-by: Kale, Sanyog R Reviewed-by: H S, Vijay Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 891e24842be3..84acb0630373 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -410,8 +410,8 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, */ memcpy(host_cpr_cfg->guid, &guid, 16); memcpy(link_cpr_cfg->guid, &guid, 16); - in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; - out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; + in_fmt = &host_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &host_cpr_cfg->module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); From 31db4d738ee6a716241436f6fc5e20f9f41e8afb Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 26 Oct 2017 14:52:16 +0530 Subject: [PATCH 0217/1276] ASoC: Intel: Skylake: Avoid global kcontrol pointer for event handling The audio driver registers multiple kcontrols to represent multiple events like topology change or async notifications. However, using a single global variable to cache and to point to these several kcontrols in skl_dsp_cb_event() resulted in incorrect pointer being accessed. Hence use a local variable to point to the kcontrol for the current event handled by skl_dsp_cb_event() Change-Id: I6230650153abbe59fe54e70d81718702f05d6d77 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 -- sound/soc/intel/skylake/skl-topology.c | 29 +++++++++++++------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 76e83d216e49..4fa372c1bf38 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -319,8 +319,6 @@ struct skl_sst { /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; - struct snd_kcontrol *kcontrol; - struct list_head notify_kctls; }; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1931d4ed73f5..514b316ddd47 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3169,6 +3169,7 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, struct skl *skl = get_skl_ctx(ctx->dev); struct snd_soc_component *component = skl->component; struct skl_module_notify *m_notification = NULL; + struct snd_kcontrol *kcontrol; struct skl_algo_data *bc; u8 param_length; @@ -3176,17 +3177,15 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, case SKL_TPLG_CHG_NOTIFY: card = component->card; - if (!ctx->kcontrol) { - ctx->kcontrol = snd_soc_card_get_kcontrol(card, - "Topology Change Notification"); - if (!ctx->kcontrol) { - dev_dbg(ctx->dev, - "NOTIFICATION Controls not found\n"); - return -EINVAL; - } + kcontrol = snd_soc_card_get_kcontrol(card, + "Topology Change Notification"); + if (!kcontrol) { + dev_warn(ctx->dev, + "NOTIFICATION Controls not found\n"); + return -EINVAL; } - sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + sb = (struct soc_bytes_ext *)kcontrol->private_value; if (!sb->dobj.private) { sb->dobj.private = devm_kzalloc(ctx->dev, sizeof(*notify_data), GFP_KERNEL); @@ -3196,25 +3195,25 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, memcpy(sb->dobj.private, notify_data, sizeof(*notify_data)); snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, - &ctx->kcontrol->id); + &kcontrol->id); break; case SKL_EVENT_GLB_MODULE_NOTIFICATION: m_notification = (struct skl_module_notify *)notify_data->data; card = component->card; - ctx->kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, + kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, m_notification->unique_id); - if (!ctx->kcontrol) { - dev_dbg(ctx->dev, "Module notify control not found\n"); + if (!kcontrol) { + dev_warn(ctx->dev, "Module notify control not found\n"); return -EINVAL; } - sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + sb = (struct soc_bytes_ext *)kcontrol->private_value; bc = (struct skl_algo_data *)sb->dobj.private; param_length = sizeof(struct skl_notify_data) + notify_data->length; memcpy(bc->params, (char *)notify_data, param_length); snd_ctl_notify(card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); + SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); break; default: return -EINVAL; From 5fdd0e35ddb97e03629af118621a080b43d9b01d Mon Sep 17 00:00:00 2001 From: Kareem Shaik Date: Tue, 31 Oct 2017 15:44:52 +0530 Subject: [PATCH 0218/1276] ASoC: Intel: Skylake: Return default sampling rate for Probe compress devices. Defining sampling rate for getting Probe data from probe compress devices. As user space expects a valid sample rate but existing API compress_get_hpointer returns an error if sampling rate is 0. Change-Id: I0482c96fea4583a93b952d7484a4234388d6a6f0 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Tewani, Pradeep D Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 6 ++++++ sound/soc/intel/skylake/skl-probe.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4679bf9f4543..8702f0c3e17e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1412,6 +1412,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "Probe Playback", .channels_min = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1421,6 +1424,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .capture = { .stream_name = "Probe Capture", .channels_min = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, }; diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 0716bf62f92e..4a22c75c552b 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -323,6 +323,11 @@ int skl_probe_compr_tstamp(struct snd_compr_stream *stream, tstamp->copied_total = hstream->hstream.curr_pos; + if (stream->direction == SND_COMPRESS_PLAYBACK) + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(dai->driver->playback.rates); + else + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(dai->driver->capture.rates); + return 0; } From 77f5beb105d16d2a2aa4e984d4a0ddeabf6c1f24 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Fri, 27 Oct 2017 19:30:32 +0530 Subject: [PATCH 0219/1276] ASoC: Intel: Skylake: Add ULL machine driver entry This patch adds ULL machine driver name to driver data. Change-Id: Ib6e47b14f7e7c8a24a3bdad4034a39008de7f3be Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Tewani, Pradeep D Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Reviewed-by: Kale, Sanyog R Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index a848eae708c8..ba614c96254b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1189,6 +1189,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", }, + { + .id = "INT34C3", + .drv_name = "bxt_ivi_ull", + .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + }, {} }; From 164b919abe72dd3025c093eef5739f5765178552 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Fri, 27 Oct 2017 18:50:54 +0530 Subject: [PATCH 0220/1276] ASoC: Intel: Board: Add BXTP MRB ULL machine driver This is the machine driver for Ultra Low latency(ULL) topology on MRB using dummy codec in I2S mode. Change-Id: Ica5c6515ccf99660efcea79d6ff67f3946e9b0d8 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: audio_build Reviewed-by: Tewani, Pradeep D Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_ivi_ull.c | 330 +++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_ivi_ull.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e655978d3418..45b1d5a03ed0 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -360,6 +360,15 @@ config SND_SOC_INTEL_BXT_TDF8532_MACH platforms with TDF8532 I2S audio codec. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_BXT_ULL_MACH + tristate "Broxton ULL reference for GP MRB platform" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton ULL GP MRB + platform. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 239051cc6f22..9a77052f0469 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -28,6 +28,7 @@ snd-soc-icl-rt274-objs := icl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o +snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -58,3 +59,4 @@ obj-$(CONFIG_SND_SOC_INTEL_ICL_RT274_MACH) += snd-soc-icl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o diff --git a/sound/soc/intel/boards/bxt_ivi_ull.c b/sound/soc/intel/boards/bxt_ivi_ull.c new file mode 100644 index 000000000000..22ae8ab30a1d --- /dev/null +++ b/sound/soc/intel/boards/bxt_ivi_ull.c @@ -0,0 +1,330 @@ +/* + * Intel Broxton-P I2S ULL Machine Driver + * + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_pcm_stream media1_out_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 3, + .channels_max = 3, +}; + +static const struct snd_soc_pcm_stream codec1_in_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 6, + .channels_max = 6, +}; + +static const struct snd_soc_pcm_stream codec0_in_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("DummySpeaker1", NULL), + SND_SOC_DAPM_SPK("DummySpeaker2", NULL), + SND_SOC_DAPM_SPK("DummySpeaker3", NULL), + SND_SOC_DAPM_SPK("DummySpeaker4", NULL), + SND_SOC_DAPM_MIC("DummyMIC0", NULL), + SND_SOC_DAPM_MIC("DummyMIC2", NULL), + SND_SOC_DAPM_MIC("DummyMIC4", NULL), +}; + +static const struct snd_soc_dapm_route bxtp_ull_map[] = { + {"8ch_pt_in3", NULL, "ssp0 Rx" }, + {"ssp0 Rx", NULL, "Dummy Capture" }, + {"Dummy Capture", NULL, "DummyMIC0"}, + + {"DummySpeaker2", NULL, "Dummy Playback2"}, + {"Dummy Playback2", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "8ch_pt_out2"}, + + {"DummySpeaker1", NULL, "Dummy Playback1"}, + {"Dummy Playback1", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "8ch_pt_out3"}, + + {"8ch_pt_in2", NULL, "ssp2 Rx" }, + {"ssp2 Rx", NULL, "Dummy Capture2" }, + {"Dummy Capture2", NULL, "DummyMIC2"}, + + {"DummySpeaker4", NULL, "Dummy Playback4"}, + {"Dummy Playback4", NULL, "ssp4 Tx"}, + {"ssp4 Tx", NULL, "8ch_pt_out"}, + + {"8ch_pt_in", NULL, "ssp4 Rx" }, + {"ssp4 Rx", NULL, "Dummy Capture4" }, + {"Dummy Capture4", NULL, "DummyMIC4"}, + + /* (ANC) Codec1_in - Loop pipe */ + { "codec1_in", NULL, "ssp0-b Rx" }, + { "ssp0-b Rx", NULL, "Dummy Capture" }, + + /* Codec0_in - Loop pipe */ + { "codec0_in", NULL, "ssp2-b Rx" }, + { "ssp2-b Rx", NULL, "Dummy Capture2" }, + + /* Media1_out Loop Path */ + {"DummySpeaker3", NULL, "Dummy Playback3"}, + { "Dummy Playback3", NULL, "ssp1-b Tx"}, + { "ssp1-b Tx", NULL, "media1_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link bxtp_ull_dais[] = { + { + .name = "Bxt Audio Port 3", + .stream_name = "Stereo-16K SSP4", + .cpu_dai_name = "System Pin 3", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "Bxt Audio Port 4", + .stream_name = "5-ch SSP1", + .cpu_dai_name = "System Pin 4", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + { + .name = "Bxt Audio Port 5", + .stream_name = "SSP2 Stream", + .cpu_dai_name = "System Pin 5", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "Bxt Audio Port 6", + .stream_name = "8-Ch SSP0", + .cpu_dai_name = "System Pin 6", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + /* Probe DAI Links */ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* CODEC<->CODEC link */ + { + .name = "Bxtn SSP0 Port", + .stream_name = "Bxtn SSP0", + .cpu_dai_name = "SSP0-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &codec1_in_params, + .dsp_loopback = true, + }, + + { + .name = "Bxtn SSP2 port", + .stream_name = "Bxtn SSP2", + .cpu_dai_name = "SSP2-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &codec0_in_params, + .dsp_loopback = true, + }, + + { + .name = "Bxtn SSP1 port", + .stream_name = "Bxtn SSP2", + .cpu_dai_name = "SSP1-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &media1_out_params, + .dsp_loopback = true, + }, + + /* Back End DAI links */ + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .cpu_dai_name = "SSP4 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for ULL Dummy Codec*/ +static struct snd_soc_card bxtp_ull = { + .name = "bxtp-ull", + .owner = THIS_MODULE, + .dai_link = bxtp_ull_dais, + .num_links = ARRAY_SIZE(bxtp_ull_dais), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = bxtp_ull_map, + .num_dapm_routes = ARRAY_SIZE(bxtp_ull_map), + .fully_routed = false, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + bxtp_ull.dev = &pdev->dev; + return snd_soc_register_card(&bxtp_ull); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&bxtp_ull); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_ull", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(broxton_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton ULL Machine"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxtp_i2s_ull"); From 71d8d25cd6df99574c1179760e449c90200cfdb2 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Thu, 26 Oct 2017 02:56:25 +0530 Subject: [PATCH 0221/1276] ASoC: Intel: Skylake: Add support to configure ADSP Scheduler ADSP scheduler can be configured to run either from 1. Internal Timer 2. DMA completion interrupts from SSP/DMIC This patch adds support to send this information to the ADSP during every D0/D3 cycle as a Large Config Set IPC. In addition to providing the source, there is a provision to alter the LL task period to values less than 1ms. Change-Id: Id856ef66a0930c6dceab804f8081c7449527476b Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Prusty, Subhransu S Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 29 ++++++++++++++++- sound/soc/intel/skylake/skl-messages.c | 9 ++---- sound/soc/intel/skylake/skl-pcm.c | 16 +++++----- sound/soc/intel/skylake/skl-sst-ipc.c | 6 ++-- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.c | 44 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl.h | 11 +++++++ 8 files changed, 99 insertions(+), 19 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index b29d07b018ea..ba4806d5ef17 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -261,6 +261,25 @@ * * %SKL_TKN_U32_DMA_MIN_SIZE: Minimum DMA buffer size * + * %SKL_TKN_U32_SCH_TYPE: Types of FW configs: SCHEDULER_CONFIG + * + * %SKL_TKN_U32_SCH_SIZE: Scheduler config size + * + * %SKL_TKN_U32_SCH_SYS_TICK_MUL: + * System tick multiplier + * + * %SKL_TKN_U32_SCH_SYS_TICK_DIV: + * System tick divider + * + * %SKL_TKN_U32_SCH_SYS_TICK_LL_SRC: + * Low Latency interrupt source + * + * %SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN: + * Config length + * + * %SKL_TKN_U32_SCH_SYS_TICK_CFG: + * Config contain capture on which SSP to + * active the FW * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest @@ -365,7 +384,15 @@ enum SKL_TKNS { SKL_TKN_U32_DMA_SIZE, SKL_TKN_U32_DMA_MAX_SIZE, SKL_TKN_U32_DMA_MIN_SIZE, - SKL_TKN_MAX = SKL_TKN_U32_DMA_MIN_SIZE, + + SKL_TKN_U32_SCH_TYPE, + SKL_TKN_U32_SCH_SIZE, + SKL_TKN_U32_SCH_SYS_TICK_MUL, + SKL_TKN_U32_SCH_SYS_TICK_DIV, + SKL_TKN_U32_SCH_SYS_TICK_LL_SRC, + SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN, + SKL_TKN_U32_SCH_SYS_TICK_CFG, + SKL_TKN_MAX = SKL_TKN_U32_SCH_SYS_TICK_CFG, }; /* diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 84acb0630373..b97255b5aae0 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1356,13 +1356,10 @@ int skl_resume_dsp(struct skl *skl) skl->cfg.astate_cfg); } - /* Set DMA buffer configuration */ - if (skl->cfg.dmacfg.size) - skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, BXT_INSTANCE_ID, - BXT_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); + /* Set the FW config info from topology */ + skl_tplg_fw_cfg_set(skl); - /* Set DMA clock controls */ - return skl_dsp_set_dma_clk_controls(skl->skl_sst); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 8702f0c3e17e..cd036f5fe515 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1872,14 +1872,14 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } - /* Set DMA buffer configuration */ - if (skl->cfg.dmacfg.size) - skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, - BXT_INSTANCE_ID, BXT_BASE_FW_MODULE_ID, - (u32 *)(&skl->cfg.dmacfg)); - - /* Set DMA clock controls */ - skl_dsp_set_dma_clk_controls(skl->skl_sst); + if (skl->cfg.astate_cfg != NULL) { + skl_dsp_set_astate_cfg(skl->skl_sst, + skl->cfg.astate_cfg->count, + skl->cfg.astate_cfg); + } + + /* Set the FW config info from topology */ + skl_tplg_fw_cfg_set(skl); skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index d7c75da0625f..13917d54dc32 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1165,7 +1165,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); -void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, +void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data) { struct skl_ipc_large_config_msg msg = {0}; @@ -1180,9 +1180,9 @@ void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, ret = skl_ipc_set_large_config(ipc, &msg, data); if (ret < 0) - dev_err(ipc->dev, "ipc: set dma config failed, err %d\n", ret); + dev_err(ipc->dev, "ipc: set fw config failed, err %d\n", ret); } -EXPORT_SYMBOL_GPL(skl_ipc_set_dma_cfg); +EXPORT_SYMBOL_GPL(skl_ipc_set_fw_cfg); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 4fa372c1bf38..4b3c7e283030 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -472,6 +472,6 @@ void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, int skl_notify_tplg_change(struct skl_sst *ctx, int type); int skl_dsp_crash_dump_read(struct skl_sst *ctx); -void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, +void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 514b316ddd47..22c1795d4624 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4797,6 +4797,34 @@ static int skl_tplg_get_int_tkn(struct device *dev, tkn_elem->value; break; + case SKL_TKN_U32_SCH_TYPE: + skl->cfg.sch_cfg.type = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SIZE: + skl->cfg.sch_cfg.length = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_MUL: + skl->cfg.sch_cfg.sys_tick_mul = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_DIV: + skl->cfg.sch_cfg.sys_tick_div = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_LL_SRC: + skl->cfg.sch_cfg.sys_tick_ll_src = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN: + skl->cfg.sch_cfg.sys_tick_cfg_len = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_CFG: + skl->cfg.sch_cfg.sys_tick_cfg = tkn_elem->value; + break; + case SKL_TKN_U8_IN_PIN_TYPE: case SKL_TKN_U8_OUT_PIN_TYPE: case SKL_TKN_U8_IN_QUEUE_COUNT: @@ -5141,6 +5169,22 @@ static int skl_tplg_create_pipe_widget_list(struct snd_soc_component *component) return 0; } +void skl_tplg_fw_cfg_set(struct skl *skl) +{ + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_fw_cfg(&skl->skl_sst->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); + + /* set scheduler config if available */ + if (skl->cfg.sch_cfg.length) + skl_ipc_set_fw_cfg(&skl->skl_sst->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.sch_cfg)); + + /* Set DMA clock controls */ + skl_dsp_set_dma_clk_controls(skl->skl_sst); +} + static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) { struct skl_pipe_module *w_module; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cc23a18fb2b4..cd1827e20832 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -636,5 +636,6 @@ int skl_create_notify_kctl_list(struct skl_sst *skl_sst, void skl_delete_notify_kctl_list(struct skl_sst *skl_sst); struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, struct snd_card *card, u32 notify_id); +void skl_tplg_fw_cfg_set(struct skl *skl); #endif diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index c8a2c27d971d..b75dc47331f6 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -82,6 +82,16 @@ struct skl_dma_buff_cfg { struct skl_dma_config dma_cfg[SKL_MAX_DMA_CFG]; } __packed; +struct skl_sch_config { + u32 type; + u32 length; + u32 sys_tick_mul; + u32 sys_tick_div; + u32 sys_tick_ll_src; + u32 sys_tick_cfg_len; + u32 sys_tick_cfg; +}; + struct skl_dmctrl_hdr { u32 vbus_id; u32 freq; @@ -102,6 +112,7 @@ struct skl_dmactrl_config { struct skl_fw_config { struct skl_dma_buff_cfg dmacfg; + struct skl_sch_config sch_cfg; struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From 1f9e68ccb7eac82e9321df357da6a44380559568 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Nov 2017 20:11:42 +0530 Subject: [PATCH 0222/1276] WA: Disable irq in rt274 Signed-off-by: Subhransu S. Prusty --- sound/soc/codecs/rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index c5c3ee4d3182..6b03996c410e 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1179,6 +1179,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c, regmap_write(rt274->regmap, RT274_UNSOLICITED_HP_OUT, 0x81); regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82); +#if 0 if (rt274->i2c->irq) { ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); @@ -1188,6 +1189,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c, return ret; } } +#endif ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt274, From 1ab7b2a187b908109d928029e3d2b1a515588948 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 20 Sep 2017 14:29:38 +0530 Subject: [PATCH 0223/1276] ASoC: Intel: Skylake: Replace modulus operator with div_u64_rem. Linking errors are observed when compiled for 32 bit architecture as shown below: sound/soc/intel/skylake/skl.o: In function `skl_stream_update': skl.c:(.text+0x213): undefined reference to `__umoddi3' sound/soc/intel/skylake/skl-probe.o: In function `skl_probe_compr_copy': skl-probe.c:(.text+0x3f7): undefined reference to `__umoddi3' skl-probe.c:(.text+0x459): undefined reference to `__umoddi3' The error for __umoddi3 is observed due to usage of modulus operator on u64 variable which is unsupported for i386. To fix this, use div_u64_rem instead. Change-Id: I3ebba0c28ac50215dc4efff7356a31ba4db930f6 Signed-off-by: Guneshwor Singh Signed-off-by: Dronamraju, Santosh Pavan KumarX Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Shaik, Kareem M Reviewed-by: Tewani, Pradeep D Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-probe.c | 11 ++++++----- sound/soc/intel/skylake/skl.c | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 4a22c75c552b..2d9d0ea6c907 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -335,7 +335,8 @@ int skl_probe_compr_tstamp(struct snd_compr_stream *stream, int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, size_t count, struct snd_soc_dai *dai) { - int offset = 0, availcount = 0, retval = 0, copy; + int availcount = 0, retval = 0, copy; + unsigned int offset = 0; void *dstn; /* * If userspace happens to issue a copy with count > ring buffer size, @@ -345,8 +346,8 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, count = stream->runtime->buffer_size; if (stream->direction == SND_COMPRESS_CAPTURE) { - offset = stream->runtime->total_bytes_transferred % - stream->runtime->buffer_size; + div_u64_rem(stream->runtime->total_bytes_transferred, + stream->runtime->buffer_size, &offset); dstn = stream->runtime->dma_area + offset; availcount = (stream->runtime->buffer_size - offset); if (count > availcount) { @@ -366,8 +367,8 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, } else if (stream->direction == SND_COMPRESS_PLAYBACK) { - offset = stream->runtime->total_bytes_available % - stream->runtime->buffer_size; + div_u64_rem(stream->runtime->total_bytes_available, + stream->runtime->buffer_size, &offset); dstn = stream->runtime->dma_area + offset; if (count < stream->runtime->buffer_size - offset) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ba614c96254b..691496cecaa0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -189,9 +189,11 @@ void skl_update_d0i3c(struct device *dev, bool enable) static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) { - int pos, prev_pos, no_of_bytes; + int pos, no_of_bytes; + unsigned int prev_pos; - prev_pos = hstr->curr_pos % hstr->stream->runtime->buffer_size; + div_u64_rem(hstr->curr_pos, + hstr->stream->runtime->buffer_size, &prev_pos); pos = snd_hdac_stream_get_pos_posbuf(hstr); if (pos < prev_pos) From b7d52f2c928e4ec09d987e4ad8bf1f7a08b8ffeb Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Thu, 16 Nov 2017 21:35:07 +0530 Subject: [PATCH 0224/1276] ASoC: Intel: Skylake: Poll on ADSPCS.CSTALL bit to confirm stall state change. ADSPCS.CSTALL bit should be set/cleared in order to stall/ un-stall the DSP core. Since the stall bit(state) transition need not happen instantaneously, the driver has to poll on the bit until the correct state transition occurs or the polling times out indicating a failure. Such a polling was not done in the current code. As a result an operation on the core failed because it was attempted without waiting for the core to be un-stalled. Change-Id: I268c89e031acf8a9bb5220ba621e4bf2d99ae745 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Pawse, GuruprasadX Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 71e31ad0bb3f..4b16e0002cd4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -160,11 +160,24 @@ is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask) static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask) { + int ret; + /* stall core */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CSTALL_MASK(core_mask), SKL_ADSPCS_CSTALL_MASK(core_mask)); + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), + SKL_ADSPCS_CSTALL_MASK(core_mask), + SKL_DSP_PU_TO, + "Stall Core"); + + if (ret < 0) + return ret; + /* set reset state */ return skl_dsp_core_set_reset_state(ctx, core_mask); } @@ -183,6 +196,16 @@ int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CSTALL_MASK(core_mask), 0); + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), + 0, + SKL_DSP_PU_TO, + "Unstall Core"); + if (ret < 0) + return ret; + if (!is_skl_dsp_core_enable(ctx, core_mask)) { skl_dsp_reset_core(ctx, core_mask); dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", From 796ed1c842085de77b9aa7cf1205f2c757712063 Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Thu, 16 Nov 2017 23:01:21 +0530 Subject: [PATCH 0225/1276] ASoC: Intel: Skylake: Add delay during DSP core start 1msec delay is required in power-up sequence of DSP core. Power-up sequence for DSP core is as follows: 1) power up core 2) unreset core 3) unstall core 4) send IPC ~1msec wait is required between unstall core and before sending IPC to ensure proper propagation of signals. Change-Id: Ie7c4b47d4b4ce6f869178a5fd9940f6af84b13d2 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: audio_build Reviewed-by: B, Jayachandran Reviewed-by: Prusty, Subhransu S Reviewed-by: Pawse, GuruprasadX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 4b16e0002cd4..b34f7b73e24e 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -17,6 +17,7 @@ */ #include +#include #include "../common/sst-dsp.h" #include "../common/sst-ipc.h" #include "../common/sst-dsp-priv.h" @@ -206,6 +207,9 @@ int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) if (ret < 0) return ret; + /* delay to ensure proper signal propagation after unreset/unstall */ + usleep_range(1000, 1500); + if (!is_skl_dsp_core_enable(ctx, core_mask)) { skl_dsp_reset_core(ctx, core_mask); dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", From af2ce25631640c509d9b474718e226291d148637 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 18 Jul 2017 22:56:08 +0530 Subject: [PATCH 0226/1276] ALSA: hda: Make sure DMA is stopped by reading back the RUN bit As per HW recommendation, after clearing the RUN bit, software must read a 0 from the RUN bit, before modifying related control registers or re-starting the DMA engine. Change-Id: Ied7c3534dde57fab7dc3eea809811933ced555b8 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 3c0c7c353566..2ddda76ba91d 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -71,8 +71,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start); */ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) { + int timeout; + unsigned char val; + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); + + timeout = 300; + do { + udelay(3); + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_DMA_START; + if (!val) + break; + } while (--timeout); + + if (!timeout) + dev_err(azx_dev->bus->dev, "unable to stop the stream\n"); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ azx_dev->running = false; } From 67325753946f393c2955689ad80379422dbad5f0 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Fri, 21 Jul 2017 02:08:12 +0530 Subject: [PATCH 0227/1276] ALSA: hda: Make sure DMA is started by reading back the RUN bit As per HW recommendation, after setting the RUN bit, software must read a 1 from the RUN bit, before modifying related control registers/re-starting the DMA engine. Change-Id: I5e81791a3d210a579f61d2345c4d3a29cd4c7b08 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 2ddda76ba91d..00954efaa483 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -49,6 +49,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) { struct hdac_bus *bus = azx_dev->bus; + int timeout; + unsigned char val; trace_snd_hdac_stream_start(bus, azx_dev); @@ -61,6 +63,21 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) /* set DMA start and interrupt mask */ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); + + timeout = 300; + do { + udelay(3); + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; + if (val) + break; + } while(--timeout); + + if (!timeout) { + dev_err(azx_dev->bus->dev, "unable to start the stream\n"); + azx_dev->running = false; + return; + } + azx_dev->running = true; } EXPORT_SYMBOL_GPL(snd_hdac_stream_start); From f3e1bda9e2ffdfb9826eab1a9bdb4a56e1427692 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 19 Jul 2017 00:41:43 +0530 Subject: [PATCH 0228/1276] ALSA: hda: Log HDA Hardware related errors Detect the timeout while modifying HDA DMA related Registers for stream reset and print them to console for user information Change-Id: Id2441bee1fd6083cd11c4725af2d36cb90e9bf92 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 00954efaa483..0fdb6dd7f316 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -147,6 +147,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) if (val) break; } while (--timeout); + + if (!timeout) + dev_err(azx_dev->bus->dev, "timeout on stream reset entry\n"); + val &= ~SD_CTL_STREAM_RESET; snd_hdac_stream_writeb(azx_dev, SD_CTL, val); udelay(3); @@ -160,6 +164,9 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) break; } while (--timeout); + if (!timeout) + dev_err(azx_dev->bus->dev, "timeout on stream reset exit\n"); + /* reset first position - may not be synced with hw at this time */ if (azx_dev->posbuf) *azx_dev->posbuf = 0; From 4c4799c8c3abfa661f6c278d9dfceb4d3fd0cbe6 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 17 Oct 2017 23:01:00 +0530 Subject: [PATCH 0229/1276] ALSA: hda: check if stream is stopped in snd_hdac_stream_clear Check if the DMA Channel is already stopped. There is no need to stop it again if stopped. Change-Id: Ia4632675638726b65dc3b9e2aca4f6c36773f757 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 0fdb6dd7f316..67d672192606 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -91,6 +91,11 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) int timeout; unsigned char val; + /* check if the DMA is already stopped */ + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; + if (!val) + return; + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); From ea88ffd445e72182197e058aa686f2f086bdeb57 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Sat, 18 Nov 2017 03:34:50 +0530 Subject: [PATCH 0230/1276] ASoC: Intel: Skylake: Support multiple format configs A module can have two kinds of set params, as per topology requirements. For example, one pre-init and one post-init. But currently, there is support for just one type, as the format_config. This patch extends the format_configs to 4, so as to be able to support pre-init, post-init and post-bind type of set params, for the same module, simultaneously. Change-Id: I5cdf6a921db6e9ffcabda2ec601795564c8f53d2 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 6 +- sound/soc/intel/skylake/skl-messages.c | 68 ++++++++-------- sound/soc/intel/skylake/skl-topology.c | 108 ++++++++++++++++--------- sound/soc/intel/skylake/skl-topology.h | 4 +- 4 files changed, 113 insertions(+), 73 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index ba4806d5ef17..c04dd0418173 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -281,6 +281,8 @@ * Config contain capture on which SSP to * active the FW * + * %SKL_TKN_U32_FMT_CFG_IDX: Format config index + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -392,7 +394,9 @@ enum SKL_TKNS { SKL_TKN_U32_SCH_SYS_TICK_LL_SRC, SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN, SKL_TKN_U32_SCH_SYS_TICK_CFG, - SKL_TKN_MAX = SKL_TKN_U32_SCH_SYS_TICK_CFG, + + SKL_TKN_U32_FMT_CFG_IDX, + SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, }; /* diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b97255b5aae0..c6d9cb386de3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -466,12 +466,12 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; host_cpr_cfg->module->resources[0].dma_buffer_size = 2; host_cpr_cfg->converter = 0; host_cpr_cfg->vbus_id = 0; host_cpr_cfg->sdw_agg_enable = 0; - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; in_fmt->channels = 1; in_fmt->s_freq = 96000; @@ -572,22 +572,23 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, link_cpr_cfg->m_out_pin[0].is_dynamic = true; link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; - link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), - GFP_KERNEL); - if (!link_cpr_cfg->formats_config.caps) { + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = + (sizeof(u32) * 4); + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps = + kzalloc((sizeof(u32) * 4), GFP_KERNEL); + if (!link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg->formats_config.caps[0] = 0x0; - link_cpr_cfg->formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[0] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg->formats_config.caps[2] = 0x1003; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1003; #else - link_cpr_cfg->formats_config.caps[2] = 0x1013; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1013; #endif - link_cpr_cfg->formats_config.caps[3] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[3] = 0x0; /* Init PB CPR1 module */ ret = skl_init_module(ctx, host_cpr_cfg); @@ -610,7 +611,7 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, kfree(host_cpr_cfg->m_out_pin); kfree(link_cpr_cfg->m_in_pin); kfree(link_cpr_cfg->m_out_pin); - kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps); kfree(host_cpr_cfg); kfree(link_cpr_cfg); kfree(host_cpr_mod); @@ -729,27 +730,28 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, #endif link_cpr_cfg->hw_conn_type = SKL_CONN_SINK; - link_cpr_cfg->formats_config.caps_size = 0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; link_cpr_cfg->module->resources[0].dma_buffer_size = 2; link_cpr_cfg->converter = 0; link_cpr_cfg->vbus_id = 0; link_cpr_cfg->sdw_agg_enable = 0; - link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), - GFP_KERNEL); - if (!link_cpr_cfg->formats_config.caps) { + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = + (sizeof(u32) * 4); + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps = + kzalloc((sizeof(u32) * 4), GFP_KERNEL); + if (!link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg->formats_config.caps[0] = 0x0; - link_cpr_cfg->formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[0] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg->formats_config.caps[2] = 0x1104; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1104; #else - link_cpr_cfg->formats_config.caps[2] = 0x1114; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1114; #endif - link_cpr_cfg->formats_config.caps[3] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[3] = 0x1; in_fmt->channels = 6; in_fmt->s_freq = 48000; @@ -813,7 +815,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg->hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); @@ -864,7 +866,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, error: /* Free up all memory allocated */ - kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps); kfree(link_cpr_cfg->m_in_pin); kfree(link_cpr_cfg->m_out_pin); kfree(host_cpr_cfg->m_in_pin); @@ -1427,15 +1429,15 @@ static void skl_set_base_module_format(struct skl_sst *ctx, static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, struct skl_cpr_cfg *cpr_mconfig) { - if (mconfig->formats_config.caps_size == 0) + if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0) return; memcpy(cpr_mconfig->gtw_cfg.config_data, - mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + mconfig->formats_config[SKL_PARAM_INIT].caps, + mconfig->formats_config[SKL_PARAM_INIT].caps_size); cpr_mconfig->gtw_cfg.config_length = - (mconfig->formats_config.caps_size) / 4; + (mconfig->formats_config[SKL_PARAM_INIT].caps_size) / 4; } #define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF @@ -1848,12 +1850,12 @@ static void skl_set_algo_format(struct skl_sst *ctx, skl_set_base_module_format(ctx, mconfig, base_cfg); - if (mconfig->formats_config.caps_size == 0) + if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0) return; memcpy(algo_mcfg->params, - mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + mconfig->formats_config[SKL_PARAM_INIT].caps, + mconfig->formats_config[SKL_PARAM_INIT].caps_size); } @@ -1885,7 +1887,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, switch (mconfig->m_type) { case SKL_MODULE_TYPE_COPIER: param_size = sizeof(struct skl_cpr_cfg); - param_size += mconfig->formats_config.caps_size; + param_size += mconfig->formats_config[SKL_PARAM_INIT].caps_size; return param_size; case SKL_MODULE_TYPE_PROBE: @@ -1900,7 +1902,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, case SKL_MODULE_TYPE_ALGO: param_size = sizeof(struct skl_base_cfg); - param_size += mconfig->formats_config.caps_size; + param_size += mconfig->formats_config[SKL_PARAM_INIT].caps_size; return param_size; case SKL_MODULE_TYPE_BASE_OUTFMT: diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 22c1795d4624..8026abf99ae8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -664,7 +664,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, struct skl_module_iface *m_iface = &m_cfg->module->formats[fmt_idx]; /* check if we already have blob */ - if (m_cfg->formats_config.caps_size > 0) + if (m_cfg->formats_config[SKL_PARAM_INIT].caps_size > 0) return 0; dev_dbg(ctx->dev, "Applying default cfg blob\n"); @@ -700,8 +700,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, s_fmt, ch, s_freq, dir, dev_type); if (cfg) { - m_cfg->formats_config.caps_size = cfg->size; - m_cfg->formats_config.caps = (u32 *) &cfg->caps; + m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; + m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *) &cfg->caps; } else { dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n", m_cfg->vbus_id, link_type, dir); @@ -942,9 +942,10 @@ int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, struct skl_algo_data *bc; struct skl_specific_cfg *sp_cfg; - if (mconfig->formats_config.caps_size > 0 && - mconfig->formats_config.set_params == SKL_PARAM_SET) { - sp_cfg = &mconfig->formats_config; + if (mconfig->formats_config[SKL_PARAM_SET].caps_size > 0 && + mconfig->formats_config[SKL_PARAM_SET].set_params == + SKL_PARAM_SET) { + sp_cfg = &mconfig->formats_config[SKL_PARAM_SET]; ret = skl_set_module_params(ctx, sp_cfg->caps, sp_cfg->caps_size, sp_cfg->param_id, mconfig); @@ -994,8 +995,10 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w) if (bc->set_params != SKL_PARAM_INIT) continue; - mconfig->formats_config.caps = (u32 *)bc->params; - mconfig->formats_config.caps_size = bc->size; + mconfig->formats_config[SKL_PARAM_INIT].caps = + (u32 *)bc->params; + mconfig->formats_config[SKL_PARAM_INIT].caps_size = + bc->size; break; } @@ -1432,9 +1435,10 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, return 0; } - if (mconfig->formats_config.caps_size > 0 && - mconfig->formats_config.set_params == SKL_PARAM_BIND) { - sp_cfg = &mconfig->formats_config; + if (mconfig->formats_config[SKL_PARAM_BIND].caps_size > 0 && + mconfig->formats_config[SKL_PARAM_BIND].set_params == + SKL_PARAM_BIND) { + sp_cfg = &mconfig->formats_config[SKL_PARAM_BIND]; ret = skl_set_module_params(ctx, sp_cfg->caps, sp_cfg->caps_size, sp_cfg->param_id, mconfig); @@ -2390,7 +2394,8 @@ static int skl_tplg_mic_control_get(struct snd_kcontrol *kcontrol, static int skl_fill_mic_sel_params(struct skl_module_cfg *mconfig, struct skl_mic_sel_config *mic_cfg, struct device *dev) { - struct skl_specific_cfg *sp_cfg = &mconfig->formats_config; + struct skl_specific_cfg *sp_cfg = + &mconfig->formats_config[SKL_PARAM_INIT]; sp_cfg->caps_size = sizeof(struct skl_mic_sel_config); sp_cfg->set_params = SKL_PARAM_SET; @@ -2951,8 +2956,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, GFP_KERNEL); if (!sdw_cfg) return -ENOMEM; - mconfig->formats_config.caps_size = (((sizeof(u32)) * - (mconfig->sdw_agg.num_masters) * 2) + mconfig->formats_config[SKL_PARAM_INIT].caps_size = + (((sizeof(u32)) * (mconfig->sdw_agg.num_masters) * 2) + (2 * (sizeof(u32)))); sdw_cfg->count = mconfig->sdw_agg.num_masters; @@ -2967,7 +2972,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, } } sdw_cfg->count = mconfig->sdw_agg.num_masters; - mconfig->formats_config.caps = (u32 *) sdw_cfg; + mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *) sdw_cfg; return 0; } @@ -2978,8 +2983,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_type); if (cfg) { - mconfig->formats_config.caps_size = cfg->size; - mconfig->formats_config.caps = (u32 *) &cfg->caps; + mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; + mconfig->formats_config[SKL_PARAM_INIT].caps = + (u32 *) &cfg->caps; } else { dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", mconfig->vbus_id, link_type, @@ -3892,19 +3898,26 @@ static int skl_tplg_get_token(struct device *dev, break; + case SKL_TKN_U32_FMT_CFG_IDX: + if (tkn_elem->value > SKL_MAX_PARAMS_TYPES) + return -EINVAL; + + mconfig->fmt_cfg_idx = tkn_elem->value; + break; + case SKL_TKN_U32_CAPS_SIZE: - mconfig->formats_config.caps_size = + mconfig->formats_config[mconfig->fmt_cfg_idx].caps_size = tkn_elem->value; break; case SKL_TKN_U32_CAPS_SET_PARAMS: - mconfig->formats_config.set_params = + mconfig->formats_config[mconfig->fmt_cfg_idx].set_params = tkn_elem->value; break; case SKL_TKN_U32_CAPS_PARAMS_ID: - mconfig->formats_config.param_id = + mconfig->formats_config[mconfig->fmt_cfg_idx].param_id = tkn_elem->value; break; @@ -4135,6 +4148,7 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, struct skl_dfw_v4_module *dfw = (struct skl_dfw_v4_module *)tplg_w->priv.data; int ret; + int idx = mconfig->fmt_cfg_idx; dev_dbg(dev, "Parsing Skylake v4 widget topology data\n"); @@ -4168,7 +4182,7 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, mconfig->dev_type = dfw->dev_type; mconfig->hw_conn_type = dfw->hw_conn_type; mconfig->time_slot = dfw->time_slot; - mconfig->formats_config.caps_size = dfw->caps.caps_size; + mconfig->formats_config[idx].caps_size = dfw->caps.caps_size; mconfig->m_in_pin = devm_kcalloc(dev, MAX_IN_QUEUE, sizeof(*mconfig->m_in_pin), @@ -4189,21 +4203,40 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, dfw->is_dynamic_out_pin, mconfig->module->max_output_pins); - if (mconfig->formats_config.caps_size) { - mconfig->formats_config.set_params = dfw->caps.set_params; - mconfig->formats_config.param_id = dfw->caps.param_id; - mconfig->formats_config.caps = - devm_kzalloc(dev, mconfig->formats_config.caps_size, + if (mconfig->formats_config[idx].caps_size) { + mconfig->formats_config[idx].set_params = dfw->caps.set_params; + mconfig->formats_config[idx].param_id = dfw->caps.param_id; + mconfig->formats_config[idx].caps = + devm_kzalloc(dev, mconfig->formats_config[idx].caps_size, GFP_KERNEL); - if (!mconfig->formats_config.caps) + if (!mconfig->formats_config[idx].caps) return -ENOMEM; - memcpy(mconfig->formats_config.caps, dfw->caps.caps, + memcpy(mconfig->formats_config[idx].caps, dfw->caps.caps, dfw->caps.caps_size); } return 0; } +static int skl_tplg_get_caps_data(struct device *dev, char *data, + struct skl_module_cfg *mconfig) +{ + int idx; + + idx = mconfig->fmt_cfg_idx; + if (mconfig->formats_config[idx].caps_size > 0) { + mconfig->formats_config[idx].caps = (u32 *)devm_kzalloc(dev, + mconfig->formats_config[idx].caps_size, + GFP_KERNEL); + if (mconfig->formats_config[idx].caps == NULL) + return -ENOMEM; + memcpy(mconfig->formats_config[idx].caps, data, + mconfig->formats_config[idx].caps_size); + } + + return mconfig->formats_config[idx].caps_size; +} + /* * Parse the private data for the token and corresponding value. * The private data can have multiple data blocks. So, a data block @@ -4264,18 +4297,14 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, if (block_type == SKL_TYPE_TUPLE) { ret = skl_tplg_get_tokens(dev, data, skl, mconfig, block_size); - - if (ret < 0) - return ret; - - --num_blocks; } else { - if (mconfig->formats_config.caps_size > 0) - memcpy(mconfig->formats_config.caps, data, - mconfig->formats_config.caps_size); - --num_blocks; - ret = mconfig->formats_config.caps_size; + ret = skl_tplg_get_caps_data(dev, data, mconfig); } + + if (ret < 0) + return ret; + + --num_blocks; off += ret; } @@ -4370,6 +4399,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, */ mconfig->id.module_id = -1; + /* To provide backward compatibility, set default as SKL_PARAM_INIT */ + mconfig->fmt_cfg_idx = SKL_PARAM_INIT; + /* Parse private data for tuples */ ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig); if (ret < 0) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cd1827e20832..97b9614f57af 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -56,6 +56,7 @@ #define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 #define MAX_NUM_CHANNELS 8 +#define SKL_MAX_PARAMS_TYPES 4 enum skl_channel_index { SKL_CHANNEL_LEFT = 0, @@ -433,6 +434,7 @@ struct skl_module_cfg { struct skl_module *module; int res_idx; int fmt_idx; + int fmt_cfg_idx; u8 domain; bool homogenous_inputs; bool homogenous_outputs; @@ -469,7 +471,7 @@ struct skl_module_cfg { enum skl_hw_conn_type hw_conn_type; enum skl_module_state m_state; struct skl_pipe *pipe; - struct skl_specific_cfg formats_config; + struct skl_specific_cfg formats_config[SKL_MAX_PARAMS_TYPES]; struct skl_pipe_mcfg mod_cfg[SKL_MAX_MODULES_IN_PIPE]; struct skl_gain_data *gain_data; }; From e88ed734b9c4831ce3e0a3cf47a44bf0b33e6591 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 20 Sep 2017 14:26:09 +0530 Subject: [PATCH 0231/1276] SoundWire: Fix CRC8 dependency CRC8 should be selected when SDW is selected. So use 'select' instead of 'depends' in Kconfig. Change-Id: Ia5f7c07750574cee1f6308bf884bd3b5441aedb3 Signed-off-by: Guneshwor Singh Signed-off-by: Dronamraju, Santosh Pavan KumarX Reviewed-on: Reviewed-by: Prabhu, PuneethX Reviewed-by: Kale, Sanyog R Reviewed-by: Pawse, GuruprasadX Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- drivers/sdw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 660188bd2c02..7e5a57f1f6d2 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,6 +1,6 @@ menuconfig SDW tristate "SoundWire bus support" - depends on CRC8 + select CRC8 depends on X86 help SoundWire interface is typically used for transporting data From 82acf71c15815c3ea47684ca8aa43fc1d267bd2a Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Mon, 4 Dec 2017 15:05:59 +0530 Subject: [PATCH 0232/1276] soundwire: Change programming sequence for BRA Currently, the register programming (PDI, ALH, DP_CTRL) is performed before FW pipeline creation. The ALH ownership is set to host as part of first SoundWire gateway configuration. Due to this changes in FW, the first instance of BRA fails. This patch changes the sequence and performs register programming after FW pipeline creation. Change-Id: Ifbff05f9033e88541406b42d7d5a5370e1bccdcb Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Prabhu, PuneethX Reviewed-by: Pawse, GuruprasadX Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- drivers/sdw/sdw_cnl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 9f8e77c20699..95eb7a8fab2b 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1900,9 +1900,6 @@ static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, /* Fill master number in bra info data structure */ info.mstr_num = mstr->nr; - /* PDI Configuration (ON) */ - cnl_sdw_bra_pdi_config(mstr, true); - /* Prepare TX buffer */ ret = cnl_sdw_bra_data_ops(mstr, block, &info); if (ret < 0) { @@ -1917,6 +1914,9 @@ static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, goto out; } + /* PDI Configuration (ON) */ + cnl_sdw_bra_pdi_config(mstr, true); + /* Trigger START host DMA and pipeline */ ret = ops->bra_platform_xfer(data->bra_data->drv_data, true, &info); if (ret < 0) { From d9b0f467711582ee485c6c047264c5854d83e3cc Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 2 Jan 2018 23:53:39 +0530 Subject: [PATCH 0233/1276] [WORKAROUND] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index e723ffebed0f..a5e2a028338c 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 7bdbd10886bbf47767889ed4393f201e942b6984 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Wed, 16 Aug 2017 12:26:31 +0530 Subject: [PATCH 0234/1276] SoundWire: Perform clock exit by setting clock stop clear As per HW sequence, clock stop clear bit in mcp_control register should be set and wait for bit to be cleared in order to exit Master from clock stop. Hence adding the support. Change-Id: I3491c74a9969e4ce112ed6afc5eb366e1cc6737a Signed-off-by: Sanyog Kale Signed-off-by: Paul, Subhankar --- drivers/sdw/sdw_cnl.c | 55 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 95eb7a8fab2b..c754edbe6564 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -485,7 +485,7 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) int mcp_config, mcp_control, sync_reg, mcp_clockctrl; volatile int sync_update = 0; int timeout = 10; /* Try 10 times before timing out */ - int ret = 0; + int ret = 0, mask; /* Power up the link controller */ ret = sdw_power_up_link(sdw); @@ -498,6 +498,56 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); + /* write to MCP Control register to enable block wakeup */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + mask = (MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + mcp_control &= ~mask; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + do { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) + break; + + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + + /* Write the MCP Control register to exit from clock stop */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + mask = (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); + mcp_control |= mask; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + /* Reset timeout */ + timeout = 10; + + /* Wait for clock stop exit bit to be self cleared */ + do { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + + /* Read once again to confirm */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) { + dev_dbg(&sdw->mstr->dev, "SDW ctrl %d exit clock stop success\n", + data->inst_id); + } else { + dev_err(&sdw->mstr->dev, + "Failed exit from clock stop SDW ctrl %d\n", + data->inst_id); + return -EIO; + } + /* Set SyncPRD period */ sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); @@ -506,6 +556,9 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + /* Reset timeout */ + timeout = 10; + do { sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); if ((sync_update & CNL_SYNC_SYNCCPU_MASK) == 0) From 693a392e9f23a13b786a7958ee44ba0137517a84 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:03:56 +0530 Subject: [PATCH 0235/1276] ASoC: Intel: Skylake: Add API to reset private instance id of modules When the Audio DSP becomes unresponsive, DSP will be reset and firmware will be downloaded again. As a consequence of this driver resources need to be reinitialized with its default values. With this patch an API is added to reset private/dynamic instance id of the modules. This will be used during recovery of the Audio DSP Change-Id: Id4e7c4fca80d3dd97b823853cfd476d3d82dd116 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Prakash, Divya1 Reviewed-by: Kale, Sanyog R Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-utils.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 207bf3fe8ffd..807f5b7db0ae 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -307,6 +307,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid_mod); int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id); int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id); +void skl_reset_instance_id(struct skl_sst *ctx); int skl_get_pvt_instance_id_map(struct skl_sst *ctx, int module_id, int instance_id); void skl_freeup_uuid_list(struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index f20b842af4e4..8f4056ebf670 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -292,7 +292,21 @@ int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id) } EXPORT_SYMBOL_GPL(skl_put_pvt_id); +void skl_reset_instance_id(struct skl_sst *ctx) +{ + struct uuid_module *module; + int size, i; + + list_for_each_entry(module, &ctx->uuid_list, list) { + for (i = 0; i < MAX_INSTANCE_BUFF; i++) + module->pvt_id[i] = 0; + + size = sizeof(int) * module->max_instance; + memset(module->instance_id, -1, size); + } +} +EXPORT_SYMBOL_GPL(skl_reset_instance_id); int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) { From 6c1b6c968df3605e60a6b116713952ea24249ba3 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:06:05 +0530 Subject: [PATCH 0236/1276] ASoC: Intel: Skylake: Add an API to reset the usage count of the DSP cores Add an interface to reset the usage count of ADSP cores. This is needed in situations where the DSP needs to restart afresh, after a crash Change-Id: I56fcd89a9055b99671f0d4229f19aa0fd3340d48 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.c | 12 ++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 1 + 2 files changed, 13 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index b34f7b73e24e..0b1cc44c901d 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -54,6 +54,18 @@ void skl_dsp_init_core_state(struct sst_dsp *ctx) } } +void skl_dsp_reset_core_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + int i; + + for (i = 0; i < skl->cores.count; i++) { + skl->cores.state[i] = SKL_DSP_RESET; + skl->cores.usage_count[i] = 0; + } +} +EXPORT_SYMBOL_GPL(skl_dsp_reset_core_state); + /* Get the mask for all enabled cores */ unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx) { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 807f5b7db0ae..ef9bf4a4a1b7 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -273,6 +273,7 @@ bool is_skl_dsp_running(struct sst_dsp *ctx); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); void skl_dsp_init_core_state(struct sst_dsp *ctx); +void skl_dsp_reset_core_state(struct sst_dsp *ctx); int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask); int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask); int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask); From 2bdcbeded8d395ce2143d17499342b8296a08118 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:07:11 +0530 Subject: [PATCH 0237/1276] ASoC: Intel: Skylake: Fix the is_dsp_running() to return core0 state DSP state is mainly dependent on the core0 state. This patches fixes the is_dsp_running callback to return dsp state based on core0 state. Change-Id: I60297162b6512b2092886f4ae8cbcd286bafdf09 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Periyasamy, SriramX Reviewed-by: Prakash, Divya1 Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-dsp-priv.h | 1 - sound/soc/intel/skylake/skl-sst-dsp.c | 11 +++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 8e80c6f177f8..196bb7d7ebf0 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -355,7 +355,6 @@ struct sst_dsp { /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; - int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 0b1cc44c901d..485c8b8c38a1 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -28,13 +28,6 @@ #define SKL_DSP_PD_TO 50 #define SKL_DSP_RESET_TO 50 -void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) -{ - mutex_lock(&ctx->mutex); - ctx->sst_state = state; - mutex_unlock(&ctx->mutex); -} - /* * Initialize core power state and usage count. To be called after * successful first boot. Hence core 0 will be running and other cores @@ -504,6 +497,8 @@ EXPORT_SYMBOL_GPL(skl_dsp_free); bool is_skl_dsp_running(struct sst_dsp *ctx) { - return (ctx->sst_state == SKL_DSP_RUNNING); + struct skl_sst *skl_sst = ctx->thread_context; + + return (skl_sst->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RESET); } EXPORT_SYMBOL_GPL(is_skl_dsp_running); From 24ab0be5a7d8338e5738a86cdc119025be320372 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:17:03 +0530 Subject: [PATCH 0238/1276] ASoC: Intel: Skylake: Avoid sending IPCs during the crash recovery Stream is put to suspend as a part of firmware crash recovery. This in turn invokes the PCM trigger suspend as well as DAPM pre/post PMD events, where driver sends IPCs to DSP. However, DSP can't respond to IPCs during recovery. This patch ensures no IPCs are sent during the recovery process. Change-Id: If06d57563d3e24898ce86e33b3427ca24aa12804 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-ipc.c | 4 ++++ sound/soc/intel/skylake/skl-topology.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index f4b09503518e..03896bde44d3 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -98,6 +98,10 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, struct ipc_message *msg; unsigned long flags; + if (ipc->dsp->is_recovery) { + dev_dbg(ipc->dev, "Recovery in progress..\n"); + return 0; + } spin_lock_irqsave(&ipc->dsp->spinlock, flags); msg = msg_get_empty(ipc); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 8026abf99ae8..c09f03c9da4d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1972,9 +1972,13 @@ static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, return skl_tplg_mixer_dapm_post_pmu_event(w, skl); case SND_SOC_DAPM_PRE_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); case SND_SOC_DAPM_POST_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_mixer_dapm_post_pmd_event(w, skl); } @@ -1999,6 +2003,8 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return skl_tplg_pga_dapm_pre_pmu_event(w, skl); case SND_SOC_DAPM_POST_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_pga_dapm_post_pmd_event(w, skl); } From c6ad2dc0b35881ccdff04dbe869456e77131a7a1 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 04:48:41 +0530 Subject: [PATCH 0239/1276] [REVERTME] ASoC: Intel: CNL: Fix for the firmware redownload failure on ICL There is a bug in hda uncaching mechanism due to which seecond time firmware download was failing in the ICL platform. In order to get around this, flush_cache_range() is used as WA to maintain the integrity of firmware. This patch will be reverted, once the proper fix is available Change-Id: I2efdfe82dc302e8a1b623c905da2ea08084d8074 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/cnl-sst.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 813cd73545e4..ea7bec1ad073 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -137,6 +138,7 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) ctx->dsp_ops.stream_tag = stream_tag; memcpy(ctx->dmab.area, fwdata, fwsize); + clflush_cache_range(ctx->dmab.area, fwsize); /* purge FW request */ sst_dsp_shim_write(ctx, CNL_ADSP_REG_HIPCIDR, CNL_ADSP_REG_HIPCIDR_BUSY | (CNL_IPC_PURGE | From 71074153449a8065c003de117de59e9dbd58b222 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 03:03:35 +0530 Subject: [PATCH 0240/1276] ASoC: Intel: SKL: Implement the timer to trigger firmware crash recovery This patch implements timer to trigger firmware crash recovery when there is no period elapsed for the period boundary of a stream. Change-Id: I500d0307f5367e30bf28b37f356e2f63d648c5ff Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-messages.c | 33 +++++++++++++++++ sound/soc/intel/skylake/skl-pcm.c | 51 +++++++++++++++++++++++++- sound/soc/intel/skylake/skl.c | 28 +++++++++++++- sound/soc/intel/skylake/skl.h | 16 ++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index c6d9cb386de3..5fb1093cdfa2 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "skl-sst-dsp.h" #include "cnl-sst-dsp.h" @@ -363,6 +364,38 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, return ret; } +void skl_trigger_recovery(struct work_struct *work) +{ + struct skl_monitor *monitor_dsp = container_of(work, + struct skl_monitor, mwork); + struct skl *skl = container_of(monitor_dsp, + struct skl, monitor_dsp); + const struct skl_dsp_ops *ops; + + ops = skl_get_dsp_ops(skl->pci->device); + + if (ops->do_recovery) + ops->do_recovery(skl); + return; + +} + +void skl_timer_cb(struct timer_list *t) +{ + struct skl *skl = from_timer(skl, t, monitor_dsp.timer); + struct skl_sst *ctx = skl->skl_sst; + const struct skl_dsp_ops *ops; + + ops = skl_get_dsp_ops(skl->pci->device); + ctx->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RESET; + + if (ops->do_recovery) { + schedule_work(&skl->monitor_dsp.mwork); + del_timer(&skl->monitor_dsp.timer); + } + +} + static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, unsigned int mstr_num) { diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index cd036f5fe515..bee7c5ea7fd1 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "skl.h" @@ -519,12 +520,19 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct skl *skl = get_skl_ctx(dai->dev); + struct skl_monitor *monitor = &skl->monitor_dsp; struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dapm_widget *w; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_stream *azx_dev; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + u32 interval; + int i; +#endif + bool is_running = false; int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); @@ -554,7 +562,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); if (runtime->no_rewinds) - snd_hdac_ext_stream_set_spib(ebus, + snd_hdac_ext_stream_set_spib(bus, stream, stream->spib); } case SNDRV_PCM_TRIGGER_START: @@ -568,6 +576,25 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ret = skl_decoupled_trigger(substream, cmd); if (ret < 0) return ret; + /* + * Period elapsed interrupts with multiple streams are not + * consistent on FPGA. However, it works without any issues on + * RVP. So, using the default max value for FPGA + */ +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + /* + * To be on the safer side, restricting the minimal interval to + * 10ms + */ + interval = SKL_MIN_TIME_INTERVAL + + ((2 * runtime->period_size * 1000) / + runtime->rate); + monitor->intervals[hdac_stream(stream)->index] = interval; + if (interval > monitor->interval) + monitor->interval = interval; +#else + monitor->interval = SKL_MAX_TIME_INTERVAL; +#endif return skl_run_pipe(ctx, mconfig->pipe); break; @@ -595,6 +622,26 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, hdac_stream(stream)); snd_hdac_ext_stream_decouple(bus, stream, false); } + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->running) { + is_running = true; + break; + } + } + monitor->intervals[hdac_stream(stream)->index] = 0; + if (!is_running) + del_timer(&skl->monitor_dsp.timer); +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + else { + interval = SKL_MIN_TIME_INTERVAL; + for (i = 0; i < bus->num_streams; i++) { + if (monitor->intervals[i] > interval) + interval = monitor->intervals[i]; + } + monitor->interval = interval; + } +#endif break; default: diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 691496cecaa0..7532c11ee1f0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -21,6 +21,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -35,6 +36,8 @@ #include #include #include "skl.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-topology.h" @@ -245,6 +248,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; + struct skl *skl = bus_to_skl(ebus); u32 status; u32 mask, int_enable; int ret = IRQ_NONE; @@ -268,7 +272,7 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); } - mask = (0x1 << ebus->num_streams) - 1; + mask = (0x1 << bus->num_streams) - 1; status = snd_hdac_chip_readl(bus, INTSTS); status &= mask; @@ -276,6 +280,8 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) /* Disable stream interrupts; Re-enable in bottom half */ int_enable = snd_hdac_chip_readl(bus, INTCTL); snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); + mod_timer(&skl->monitor_dsp.timer, jiffies + + msecs_to_jiffies(skl->monitor_dsp.interval)); ret = IRQ_WAKE_THREAD; } else ret = IRQ_HANDLED; @@ -858,6 +864,22 @@ static void skl_probe_work(struct work_struct *work) err = snd_hdac_display_power(bus, false); } +static int skl_init_recovery(struct skl *skl) +{ + struct skl_monitor *monitor = &skl->monitor_dsp; + + INIT_WORK(&monitor->mwork, skl_trigger_recovery); + monitor->interval = SKL_MIN_TIME_INTERVAL; + + monitor->intervals = devm_kzalloc(&skl->pci->dev, + skl->ebus.num_streams * sizeof(u32), + GFP_KERNEL); + if (!monitor->intervals) + return -ENOMEM; + timer_setup(&monitor->timer, skl_timer_cb, 0); + return 0; +} + /* * constructor */ @@ -981,6 +1003,10 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + err = skl_init_recovery(skl); + if (err < 0) + return err; + skl->pci_id = pci->device; device_disable_async_suspend(bus->dev); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index b75dc47331f6..01053c4cdf6e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "skl-nhlt.h" #include "skl-ssp-clk.h" @@ -52,6 +53,9 @@ #define BXT_INSTANCE_ID 0 #define BXT_BASE_FW_MODULE_ID 0 +#define SKL_MAX_TIME_INTERVAL 1000 +#define SKL_MIN_TIME_INTERVAL 10 + struct skl_dsp_resource { u32 max_mcps; u32 max_mem; @@ -122,6 +126,14 @@ struct ep_group_cnt { int *vbus_id; }; +/* For crash recovery */ +struct skl_monitor { + struct work_struct mwork; + struct timer_list timer; + u32 interval; + u32 *intervals; +}; + struct skl { struct hdac_bus hbus; struct pci_dev *pci; @@ -137,6 +149,7 @@ struct skl { struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + struct skl_monitor monitor_dsp; struct skl_dsp_resource resource; struct list_head ppl_list; struct list_head bind_list; @@ -185,6 +198,7 @@ struct skl_dsp_ops { struct skl_sst **skl_sst, void *ptr); int (*init_fw)(struct device *dev, struct skl_sst *ctx); void (*cleanup)(struct device *dev, struct skl_sst *ctx); + void (*do_recovery)(struct skl *skl); }; int skl_platform_unregister(struct device *dev); @@ -218,6 +232,8 @@ struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, u32 caps_size, u32 node_id); +void skl_timer_cb(struct timer_list *t); +void skl_trigger_recovery(struct work_struct *work); struct skl_module_cfg; #ifdef CONFIG_DEBUG_FS From f59167c7e8a2da47ee11aedebc164de370333146 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Thu, 28 Dec 2017 08:31:45 +0530 Subject: [PATCH 0241/1276] ASoC: Intel: Skylake: Implement recovery for cAVS platforms This patch implements the Audio dsp crash recovery for cAVS platforms for single and multiple streams. As a part of recovery, the firmware needs to be re-downloaded which requires the DMA channel 0. The patch does the necessary changes to reuse the DMA channel 0 for firmware download Change-Id: Icb09bca1525759d45a7739b42aa4404556087922 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/bxt-sst.c | 3 ++ sound/soc/intel/skylake/cnl-sst.c | 7 ++++ sound/soc/intel/skylake/skl-messages.c | 54 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-pcm.c | 2 + sound/soc/intel/skylake/skl-sst-dsp.h | 2 + sound/soc/intel/skylake/skl-topology.c | 1 + sound/soc/intel/skylake/skl-topology.h | 2 + sound/soc/intel/skylake/skl.c | 3 ++ 9 files changed, 71 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 196bb7d7ebf0..b9935fdd0910 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -355,6 +355,7 @@ struct sst_dsp { /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; + bool is_recovery; struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index dd5453daa562..2eb57d75f1b1 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -680,6 +680,9 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx) int ret; struct sst_dsp *sst = ctx->dsp; + if (sst->is_recovery) + skl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed: %x\n", ret); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index ea7bec1ad073..df42b2157b21 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -795,6 +795,13 @@ int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) struct sst_dsp *sst = ctx->dsp; int ret; + if (sst->is_recovery) { + cnl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = cnl_load_base_firmware(sst); + if (ret < 0) + return ret; + } + skl_dsp_init_core_state(sst); if (ctx->lib_count > 1) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 5fb1093cdfa2..bc8d3afcf7d1 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -293,7 +293,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x3198, @@ -301,7 +302,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x9dc8, @@ -309,7 +311,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x34c8, @@ -317,7 +320,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, }; @@ -364,6 +368,48 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, return ret; } +void skl_do_recovery(struct skl *skl) +{ + struct snd_soc_component *soc_component = skl->component; + const struct skl_dsp_ops *ops; + struct snd_soc_card *card; + struct hdac_stream *azx_dev; + struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct snd_pcm_substream *substream = NULL; + struct hdac_ext_stream *stream; + + skl->skl_sst->dsp->is_recovery = true; + skl_dsp_reset_core_state(skl->skl_sst->dsp); + card = soc_component->card; + snd_soc_suspend(card->dev); + skl_cleanup_resources(skl); + skl_reset_instance_id(skl->skl_sst); + + /* Free up DMA channel 0 for firmware re-download */ + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->stream_tag == 1 && + azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (azx_dev->opened) { + substream = azx_dev->substream; + stream = stream_to_hdac_ext_stream(azx_dev); + snd_hdac_ext_stream_release(stream, + skl_get_host_stream_type(ebus)); + } + break; + } + } + ops = skl_get_dsp_ops(skl->pci->device); + if (ops->init_fw(soc_component->dev, skl->skl_sst) < 0) + dev_err(skl->skl_sst->dev, "Recovery failed\n"); + if (substream != NULL) { + stream = snd_hdac_ext_stream_assign(ebus, substream, + skl_get_host_stream_type(ebus)); + } + snd_soc_resume(card->dev); + skl->skl_sst->dsp->is_recovery = false; +} + void skl_trigger_recovery(struct work_struct *work) { struct skl_monitor *monitor_dsp = container_of(work, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bee7c5ea7fd1..6da6b492b70b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -189,6 +189,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) params->host_dma_id + 1); if (!hstream) return -EINVAL; + hstream->substream = params->substream; stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); @@ -373,6 +374,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, p_params.host_dma_id = dma_id; p_params.stream = substream->stream; p_params.format = params_format(params); + p_params.substream = substream; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) p_params.host_bps = dai->driver->playback.sig_bits; else diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ef9bf4a4a1b7..dc793d503115 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -22,6 +22,7 @@ #include #include #include "skl-sst-cldma.h" +#include "skl.h" struct sst_dsp; struct skl_sst; @@ -270,6 +271,7 @@ struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); int skl_dsp_acquire_irq(struct sst_dsp *sst); bool is_skl_dsp_running(struct sst_dsp *ctx); +void skl_do_recovery(struct skl *skl); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); void skl_dsp_init_core_state(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c09f03c9da4d..b91c4ea88002 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2517,6 +2517,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, pipe->p_params->s_freq = params->s_freq; pipe->p_params->stream = params->stream; pipe->p_params->format = params->format; + pipe->p_params->substream = params->substream; } else { memcpy(pipe->p_params, params, sizeof(*params)); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 97b9614f57af..e86b84e9868d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -317,6 +317,7 @@ struct skl_pipe_params { int stream; unsigned int host_bps; unsigned int link_bps; + struct snd_pcm_substream *substream; }; struct skl_pipe_fmt { @@ -616,6 +617,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus); int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 7532c11ee1f0..29e6c5a22f51 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -389,6 +389,9 @@ static int skl_suspend(struct device *dev) struct skl *skl = bus_to_skl(bus); int ret = 0; + if (skl->skl_sst->dsp->is_recovery) + return -EBUSY; + /* * Do not suspend if streams which are marked ignore suspend are * running, we need to save the state for these and continue From 99b34b3e0aeb72f9de24404c8c913bd142efdd93 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Mon, 5 Feb 2018 20:12:17 +0530 Subject: [PATCH 0242/1276] ASoC: Intel: Skylake: Update gain interface structure This patch updates gain structure alignment as per firmware interface requirement. Change-Id: I67d509ec8aaff2f9f02d1ad830f03dca7fa50465 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- include/uapi/sound/skl-tplg-interface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 5e284a4b6ce0..7fbd306fca68 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -201,6 +201,7 @@ struct skl_gain_config { u32 channel_id; u32 target_volume; u32 ramp_type; + u32 reserved; u64 ramp_duration; } __packed; From 61ceb23de49d83fd4964fa4c9517037c6977095e Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 8 Mar 2018 00:35:50 +0530 Subject: [PATCH 0243/1276] ASoC: Intel: Skylake: Read extended crash dump info from DSP When DSP encounters an exception, besides providing basic info about the crash in the FW REGS section, an extended info is written in the log buffer, on a per core basis. This information is usually related to the module's stack that helps in identifying the reason for the Exception to occur. Audio driver needs to read this info from the log buffers and append it to the crash dump file. Change-Id: I0ae67e510f7627317b10445cdf3c2c927beaca4f Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-ipc.c | 9 ++- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-sst-utils.c | 102 +++++++++++++++++++++++- 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 13917d54dc32..e36160b1173f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -202,6 +202,9 @@ #define MOD_DATA_OFFSET 12 #define SET_LARGE_CFG_FW_CONFIG 7 +#define DSP_EXCEP_CORE_MASK 0x3 +#define DSP_EXCEP_STACK_SIZE_SHIFT 2 + enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, IPC_MOD_MSG = 1 @@ -494,9 +497,11 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, skl->miscbdcg_disabled = true; break; case IPC_GLB_NOTIFY_EXCEPTION_CAUGHT: - dev_err(ipc->dev, "*****Exception Detected **********\n"); + dev_err(ipc->dev, "*****Exception Detected on core id: %d \n",(header.extension & DSP_EXCEP_CORE_MASK)); + dev_err(ipc->dev, "Exception Stack size is %d\n", (header.extension >> DSP_EXCEP_STACK_SIZE_SHIFT)); /* hexdump of the fw core exception record reg */ - ret = skl_dsp_crash_dump_read(skl); + ret = skl_dsp_crash_dump_read(skl, + (header.extension >> DSP_EXCEP_STACK_SIZE_SHIFT)); if (ret < 0) { dev_err(ipc->dev, "dsp crash dump read fail:%d\n", ret); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 4b3c7e283030..36e699a1765b 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -470,7 +470,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); int skl_notify_tplg_change(struct skl_sst *ctx, int type); -int skl_dsp_crash_dump_read(struct skl_sst *ctx); +int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size); void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 8f4056ebf670..a420d702f689 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -32,6 +32,7 @@ #define CRASH_DUMP_VERSION 0x1 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 +#define MAX_DSP_EXCEPTION_STACK_SIZE (64*1024) #define UUID_ATTR_RO(_name) \ struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) @@ -308,6 +309,73 @@ void skl_reset_instance_id(struct skl_sst *ctx) } EXPORT_SYMBOL_GPL(skl_reset_instance_id); +/* This function checks tha available data on the core id + * passed as an argument and returns the bytes available + */ +static int skl_check_ext_excep_data_avail(struct skl_sst *ctx, int idx) +{ + u32 size = ctx->dsp->trace_wind.size/ctx->dsp->trace_wind.nr_dsp; + u8 *base = (u8 __force*)ctx->dsp->trace_wind.addr; + u32 read, write; + u32 *ptr; + + /* move to the source dsp tracing window */ + base += (idx * size); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + + if (write == read) + return 0; + else if (write > read) + return (write - read); + else + return (size - 8 - read + write); +} + +/* Function to read the extended DSP crash information from the + * log buffer memory window, on per core basis. + * Data is read into the buffer passed as *ext_core_dump. + * number of bytes read is updated in the sz_ext_dump + */ +static void skl_read_ext_exception_data(struct skl_sst *ctx, int idx, + void *ext_core_dump, int *sz_ext_dump) +{ + u32 size = ctx->dsp->trace_wind.size/ctx->dsp->trace_wind.nr_dsp; + u8 *base = (u8 __force*)ctx->dsp->trace_wind.addr; + u32 read, write; + int offset = *sz_ext_dump; + u32 *ptr; + + /* move to the current core's tracing window */ + base += (idx * size); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + if (write > read) { + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *)(base + 8 + read), + (write - read)); + *sz_ext_dump = offset + write - read; + /* advance read pointer */ + ptr[0] += write - read; + } else { + /* wrap around condition - copy till the end */ + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *)(base + 8 + read), + (size - 8 - read)); + *sz_ext_dump = offset + size - 8 - read; + offset = *sz_ext_dump; + + /* copy from the beginnning */ + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *) (base + 8), write); + *sz_ext_dump = offset + write; + /* update the read pointer */ + ptr[0] = write; + } +} + int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) { int num_mod = 0, size_core_dump, sz_ext_dump = 0, idx = 0; @@ -320,6 +388,7 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) struct adsp_type0_crash_data *type0_data; struct adsp_type1_crash_data *type1_data; struct adsp_type2_crash_data *type2_data; + struct sst_dsp *sst = ctx->dsp; if (list_empty(&ctx->uuid_list)) dev_info(ctx->dev, "Module list is empty\n"); @@ -328,6 +397,21 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) num_mod++; } + if(stack_size) + ext_core_dump = vzalloc(stack_size); + else + ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE); + if (!ext_core_dump) { + dev_err(ctx->dsp->dev, "failed to allocate memory for FW Stack\n"); + return -ENOMEM; + } + for (idx = 0; idx < sst->trace_wind.nr_dsp; idx++) { + while(skl_check_ext_excep_data_avail(ctx, idx)) { + skl_read_ext_exception_data(ctx, idx, + ext_core_dump, &sz_ext_dump); + } + } + /* Length representing in DWORD */ length0 = sizeof(*type0_data) / sizeof(u32); length1 = (num_mod * sizeof(*type1_data)) / sizeof(u32); @@ -336,11 +420,14 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) /* type1 data size is calculated based on number of modules */ size_core_dump = (MAX_CRASH_DATA_TYPES * sizeof(*crash_data_hdr)) + sizeof(*type0_data) + (num_mod * sizeof(*type1_data)) + - sizeof(*type2_data); + sizeof(*type2_data) + sz_ext_dump; - coredump = vzalloc(size_core_dump); - if (!coredump) + coredump = vzalloc(size_core_dump + sz_ext_dump); + if (!coredump){ + dev_err(ctx->dsp->dev, "failed to allocate memory \n"); + vfree(ext_core_dump); return -ENOMEM; + } offset = coredump; @@ -382,8 +469,15 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) memcpy_fromio(type2_data->fwreg, (const void __iomem *)fw_reg_addr, sizeof(*type2_data)); + if (sz_ext_dump) { + offset = coredump + size_core_dump; + memcpy(offset, ext_core_dump, sz_ext_dump); + } + + vfree(ext_core_dump); + dev_coredumpv(ctx->dsp->dev, coredump, - size_core_dump, GFP_KERNEL); + size_core_dump + sz_ext_dump, GFP_KERNEL); return 0; } From c749921351c2fcb5b87a0b7a33d5c1457333afba Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 8 Mar 2018 00:45:37 +0530 Subject: [PATCH 0244/1276] ASoC: Intel: Skylake: Fix issues in ADSP Extended Crash Dump feature Extended crash data along with Header and footer tags is written to the log buffers by the ADSP - FW owns the write pointer and driver manages the read pointer. It has been observed that complete info is not getting flushed out to the dump file. Hence the dump logic has been altered to check for completeness with respect to stack size as returned by the ADSP. Change-Id: Ieebef84a454af2f8510272a9156269d7ccdb567c Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Shaik, Kareem M Reviewed-by: Kp, Jeeja Reviewed-by: Tewani, Pradeep D Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-utils.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index a420d702f689..668e7d020a9c 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -34,6 +34,9 @@ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 #define MAX_DSP_EXCEPTION_STACK_SIZE (64*1024) +/* FW adds headers and trailing patters to extended crash data */ +#define EXTRA_BYTES 256 + #define UUID_ATTR_RO(_name) \ struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) @@ -352,6 +355,11 @@ static void skl_read_ext_exception_data(struct skl_sst *ctx, int idx, ptr = (u32 *) base; read = ptr[0]; write = ptr[1]; + + /* in case of read = write, just return */ + if (read == write) + return; + if (write > read) { memcpy_fromio((ext_core_dump + offset), (const void __iomem *)(base + 8 + read), @@ -398,17 +406,19 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) } if(stack_size) - ext_core_dump = vzalloc(stack_size); + ext_core_dump = vzalloc(stack_size + EXTRA_BYTES); else - ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE); + ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE + EXTRA_BYTES); if (!ext_core_dump) { dev_err(ctx->dsp->dev, "failed to allocate memory for FW Stack\n"); return -ENOMEM; } for (idx = 0; idx < sst->trace_wind.nr_dsp; idx++) { - while(skl_check_ext_excep_data_avail(ctx, idx)) { - skl_read_ext_exception_data(ctx, idx, + if(skl_check_ext_excep_data_avail(ctx, idx) != 0) { + while(sz_ext_dump < stack_size) { + skl_read_ext_exception_data(ctx, idx, ext_core_dump, &sz_ext_dump); + } } } From 87cab30c1c66aa387f4d0df5ef3c4d129579240c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 17 Jan 2018 09:56:04 +0530 Subject: [PATCH 0245/1276] ASoC: Intel: kconfig: Make drivers build on x86 only The drivers are designed to build and run for X86 arch only. The current depends on ACPI was okay earlier but now we have ACPI on non X86 systems too so add explicit X86 dependency. Change-Id: I3ef91b9799b7593c2c75d07e7f63bf0bf7d9113d Signed-off-by: Vinod Koul Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c08d87821c9f..e6334dda6237 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -49,7 +49,7 @@ config SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_HASWELL tristate "Haswell/Broadwell Platforms" depends on SND_DMA_SGBUF - depends on DMADEVICES && ACPI + depends on DMADEVICES && ACPI && X86 select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && X86 && SND_SST_ATOM_HIFI2_PLATFORM=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -106,7 +106,7 @@ config SND_SOC_INTEL_SKYLAKE_SSP_CLK config SND_SOC_INTEL_SKYLAKE tristate "SKL/BXT/KBL/GLK/CNL... Platforms" - depends on PCI && ACPI + depends on PCI && ACPI && X86 select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY From 638406f5c6303e1c27dc813b1f754112d484f885 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Mon, 2 Apr 2018 23:37:54 +0530 Subject: [PATCH 0246/1276] ASoC: tdf8532: Fix Audio memory leakage at boot time Fix kmemleak issue in tdf8532_get_state function by relasing the local allocated memory before exiting the function. kmemleak backtrace: unreferenced object 0xffff880270cabd40 (size 32): comm "alsa_aplay", pid 2409, jiffies 4294673205 (age 91.856s) hex dump (first 32 bytes): 02 00 03 80 80 01 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x250 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0x148/0x14d [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x100/0x350 [] dpcm_fe_dai_do_trigger+0x13a/0x200 [] dpcm_fe_dai_trigger+0x38/0x40 [] snd_pcm_do_start+0x2a/0x30 [] snd_pcm_action_single+0x3b/0x90 [] snd_pcm_action+0xe1/0x110 [] snd_pcm_common_ioctl1+0x318/0xc90 [] snd_pcm_playback_ioctl1+0x19f/0x250 [] snd_pcm_playback_ioctl+0x27/0x40 [] do_vfs_ioctl+0xa8/0x620 Change-Id: I8621e17997022274509554139097d849b211f384 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Periyasamy, SriramX Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index a5e2a028338c..68decd023a9d 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -165,6 +165,8 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, *status_repl = (struct get_dev_status_repl *) repl_buff; out: + if (repl_buff) + kfree(repl_buff); return ret; } From a216f80767bd5c081bdc69f3d90306c710f7d2d0 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Thu, 29 Mar 2018 21:40:15 +0530 Subject: [PATCH 0247/1276] ASoC: Intel: Skylake: Fix Audio memory leakage at boot time Fix kmemleak issues in skl_module_sysfs_init functions by, instead of allocating memory, we use local variable of type "char array" and snprintf to write uuid. kmemleak backtrace: unreferenced object 0xffff880267946e00 (size 64): comm "insmod", pid 2340, jiffies 4294672904 (age 92.133s) hex dump (first 32 bytes): 36 36 42 34 34 30 32 44 2d 42 34 36 38 2d 34 32 66B4402D-B468-42 46 32 2d 38 31 41 37 2d 42 33 37 31 32 31 38 36 F2-81A7-B3712186 backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc_track_caller+0x124/0x200 [] kvasprintf+0x66/0xd0 [] kasprintf+0x4e/0x70 [] skl_module_sysfs_init+0x13a/0x260 [snd_soc_skl_ipc] [] skl_platform_soc_probe+0x246/0x480 [snd_soc_skl] [] snd_soc_platform_drv_probe+0x1c/0x20 [] soc_probe_component+0x227/0x3c0 [] snd_soc_register_card+0x687/0xf00 [] broxton_tdf8532_audio_probe+0x3a/0x3d [snd_soc_sst_bxt_tdf8532] [] platform_drv_probe+0x3e/0xa0 [] driver_probe_device+0xef/0x410 [] __driver_attach+0xa7/0xf0 [] bus_for_each_dev+0x70/0xc0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x1c7/0x270 Change-Id: Ib326d4400c0d7ac6cd4ad36a2dd006663837cee7 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-utils.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 668e7d020a9c..7c867426b39b 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -1178,10 +1178,11 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) { struct uuid_module *module; struct skl_sysfs_module *modinfo_obj; - char *uuid_name; int count = 0; int max_mod = 0; int ret = 0; + unsigned int uuid_size = sizeof(module->uuid); + char uuid_name[uuid_size]; if (list_empty(&ctx->uuid_list)) return 0; @@ -1218,7 +1219,7 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) goto err_sysfs_exit; } - uuid_name = kasprintf(GFP_KERNEL, "%pUL", &module->uuid); + snprintf(uuid_name, sizeof(uuid_name), "%pUL", &module->uuid); ret = kobject_init_and_add(&modinfo_obj->kobj, &uuid_ktype, ctx->sysfs_tree->modules_kobj, uuid_name); if (ret < 0) @@ -1240,7 +1241,7 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) return 0; err_sysfs_exit: - skl_module_sysfs_exit(ctx); + skl_module_sysfs_exit(ctx); return ret; } From ad1db317c7e538fb92870086d3618b7c649563b6 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 10 Apr 2018 23:46:48 +0530 Subject: [PATCH 0248/1276] ASoC: Intel: Skylake: Add support to notify resource event ADSP notifies to driver in case of any resource events that occur while executing a usecase. These are notification IPCs that belong to a class called RESOURCE_EVENT. This patch displays such notifications to the console via debug messages. Change-Id: I7faaf31fab692ac77eefd91a5ed6e4d36c92b228 Signed-off-by: Giribabu Gogineni Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Prabhu, PuneethX Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-ipc.c | 125 +++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index e36160b1173f..6efc9502d06e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -204,6 +204,7 @@ #define DSP_EXCEP_CORE_MASK 0x3 #define DSP_EXCEP_STACK_SIZE_SHIFT 2 +#define SKL_FW_RSRCE_EVNT_DATA_SZ 6 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -232,6 +233,28 @@ enum skl_ipc_glb_type { IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ }; +/* Resource Event Types */ +enum skl_ipc_resource_event_type { + SKL_BUDGET_VIOLATION = 0, + SKL_MIXER_UNDERRUN = 1, + SKL_STREAM_DATA_SEGMENT = 2, + SKL_PROCESS_DATA_ERR = 3, + SKL_STACK_OVERFLOW = 4, + SKL_BUFFERING_MODE_CHANGED = 5, + SKL_GATEWAY_UNDERRUN = 6, + SKL_GATEWAY_OVERRUN = 7, + SKL_EDF_DOMAIN_UNSTABLE = 8, + SKL_WCLK_SAMPLE_COUNT = 9, + SKL_GATEWAY_HIGH_THRESHOLD = 10, + SKL_GATEWAY_LOW_THRESHOLD = 11, + SKL_I2S_BCE_DETECTED = 12, + SKL_I2S_CLK_STATE_CHANGED = 13, + SKL_I2S_SINK_MODE_CHANGED = 14, + SKL_I2S_SOURCE_MODE_CHANGED = 15, + SKL_SRE_DRIFT_TOO_HIGH = 16, + SKL_INVALID_RESORUCE_EVENT_TYPE = 17 +}; + enum skl_ipc_glb_reply { IPC_GLB_REPLY_SUCCESS = 0, @@ -298,6 +321,13 @@ enum skl_ipc_module_msg { IPC_MOD_DELETE_INSTANCE = 11 }; +struct skl_event_notify { + u32 resource_type; + u32 resource_id; + u32 event_type; + u32 event_data[SKL_FW_RSRCE_EVNT_DATA_SZ]; +} __packed; + void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size) { @@ -457,6 +487,98 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) skl_dsp_put_log_buff(sst, core); } +static void +skl_parse_resource_event(struct skl_sst *skl, struct skl_ipc_header header) +{ + struct skl_event_notify notify; + struct sst_dsp *sst = skl->dsp; + + /* read the message contents from mailbox */ + sst_dsp_inbox_read(sst, ¬ify, sizeof(struct skl_event_notify)); + + /* notify user about the event type */ + switch (notify.event_type) { + + case SKL_BUDGET_VIOLATION: + dev_err(sst->dev, "MCPS Budget Violation: %x\n", + header.primary); + break; + case SKL_MIXER_UNDERRUN: + dev_err(sst->dev, "Mixer Underrun Detected: %x\n", + header.primary); + break; + case SKL_STREAM_DATA_SEGMENT: + dev_err(sst->dev, "Stream Data Segment: %x\n", + header.primary); + break; + case SKL_PROCESS_DATA_ERR: + dev_err(sst->dev, "Process Data Error: %x\n", + header.primary); + break; + case SKL_STACK_OVERFLOW: + dev_err(sst->dev, "Stack Overflow: %x\n", + header.primary); + break; + case SKL_BUFFERING_MODE_CHANGED: + dev_err(sst->dev, "Buffering Mode Changed: %x\n", + header.primary); + break; + case SKL_GATEWAY_UNDERRUN: + dev_err(sst->dev, "Gateway Underrun Detected: %x\n", + header.primary); + break; + case SKL_GATEWAY_OVERRUN: + dev_err(sst->dev, "Gateway Overrun Detected: %x\n", + header.primary); + break; + case SKL_WCLK_SAMPLE_COUNT: + dev_err(sst->dev, + "FW Wclk and Sample count Notif Detected: %x\n", + header.primary); + break; + case SKL_GATEWAY_HIGH_THRESHOLD: + dev_err(sst->dev, "IPC gateway reached high threshold: %x\n", + header.primary); + break; + case SKL_GATEWAY_LOW_THRESHOLD: + dev_err(sst->dev, "IPC gateway reached low threshold: %x\n", + header.primary); + break; + case SKL_I2S_BCE_DETECTED: + dev_err(sst->dev, "Bit Count Error detected on I2S port: %x\n", + header.primary); + break; + case SKL_I2S_CLK_STATE_CHANGED: + dev_err(sst->dev, "Clock detected/loss on I2S port: %x\n", + header.primary); + break; + case SKL_I2S_SINK_MODE_CHANGED: + dev_err(sst->dev, "I2S Sink started/stopped dropping \ + data in non-blk mode: %x\n", header.primary); + break; + case SKL_I2S_SOURCE_MODE_CHANGED: + dev_err(sst->dev, "I2S Source started/stopped generating 0's \ + in non-blk mode: %x\n", header.primary); + break; + case SKL_SRE_DRIFT_TOO_HIGH: + dev_err(sst->dev, + "Frequency drift exceeded limit in SRE: %x\n", + header.primary); + break; + case SKL_INVALID_RESORUCE_EVENT_TYPE: + dev_err(sst->dev, "Invalid type: %x\n", header.primary); + break; + default: + dev_err(sst->dev, "ipc: Unhandled resource event=%x", + header.primary); + break; + } + + print_hex_dump(KERN_DEBUG, "Params:", + DUMP_PREFIX_OFFSET, 8, 4, + ¬ify, sizeof(struct skl_event_notify), false); +} + int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { @@ -471,8 +593,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, break; case IPC_GLB_NOTIFY_RESOURCE_EVENT: - dev_err(ipc->dev, "MCPS Budget Violation: %x\n", - header.primary); + skl_parse_resource_event(skl, header); break; case IPC_GLB_NOTIFY_FW_READY: From 58abbd28b47d79d156f92e31cbc661ad679ff2b1 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Fri, 1 Dec 2017 00:46:21 +0530 Subject: [PATCH 0249/1276] ASoC: Intel: BXT: Retry FW download sequence In FW download failure case, the recommended solution is to repeat complete download sequence in multiple iterations.FW download sequence is: 1. ROM initialization 2. BaseFW download During FW download failure, repeat the entire download sequence in three iteration and give-up. Change-Id: Ib29d3b0208c91d959ca1df0fc963e3e9f1d2b994 Tracked-On: Signed-off-by: Kareem,Shaik Reviewed-on: Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Sinha, Mohit Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/bxt-sst.c | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 2eb57d75f1b1..47b2a24f84dc 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -59,7 +59,7 @@ /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 -#define BXT_FW_ROM_INIT_RETRY 3 +#define BXT_FW_INIT_RETRY 3 #define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) @@ -270,30 +270,38 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) stripped_fw.size = ctx->fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); - - for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) { + for (i = 0; i < BXT_FW_INIT_RETRY; i++) { ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); + if (ret < 0) { + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + dev_err(ctx->dev, "Itertion %d Core En/ROM load fail:%d\n", i,ret); + continue; + } + dev_dbg(ctx->dev, "Itertion %d ROM load Success:%d,%d\n", i,ret); + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Itertion %d Transfer firmware failed %d\n", i,ret); + dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + continue; + } + dev_dbg(ctx->dev, "Itertion %d FW transfer Success:%d,%d\n", i,ret); + if (ret == 0) break; } - if (ret < 0) { - dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", - sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), - sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - - dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); + if (ret < 0) { + dev_err(ctx->dev, "Firmware download failed\n"); goto sst_load_base_firmware_failed; - } - - ret = sst_transfer_fw_host_dma(ctx); - if (ret < 0) { - dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); - dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", - sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), - sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - - skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); } else { dev_dbg(ctx->dev, "Firmware download successful\n"); ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, From 4b5936458291d3f09d35cc1111c881c773ed8678 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 13 Dec 2017 10:40:30 +0530 Subject: [PATCH 0250/1276] ASoC: Intel: Skylake: Check for pointer validity. kzalloc allocation can fail. Return -ENOMEM if allocation fails. Change-Id: Idc3c1c05e2797332b94372f401794c4d9c5ad7c0 Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/skl-debug.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index f3c5e29afaa6..30507fa0208c 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -186,6 +186,9 @@ static ssize_t mod_control_write(struct file *file, d->ipc_data[0] = 0; buf = kzalloc(MOD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + written = simple_write_to_buffer(buf, MOD_BUF, ppos, user_buf, count); size = written; From b3e9dbcffdbc3b806184fc7133c68ae4e217c2f0 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Fri, 23 Feb 2018 15:47:19 +0530 Subject: [PATCH 0251/1276] ASoC: Intel: Skylake: Fix logs_core array size 2 extra trace buffer dais were added for CNL but logs_core array size had not been updated to accommodate this. Fix the logs_core array size to correct value. Fixes: ccc6166da92f ("ASoC: Intel: Skylake: Add trace buffer dais for CNL") Change-Id: I2ec363e22605d60ec886fdea20a600c9583a4b4e Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 36e699a1765b..436e0365aa76 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -365,7 +365,7 @@ struct skl_log_state_msg { uint32_t fifo_full_timer_period; u32 core_mask; - struct skl_log_state logs_core[2]; + struct skl_log_state logs_core[4]; }; struct SystemTime { From 003a00a7ea687e16b8df653b511c599e6c34e7d4 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 28 Feb 2018 15:38:13 +0530 Subject: [PATCH 0252/1276] ASoC: Intel: Skylake: Replace strcpy with strlcpy Replace strcpy with strlcpy, as it avoids a possible buffer overflow. Change-Id: I3f612640979d919af513ff2c79d4d0207bb62f5a Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/cnl-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-acpi.c b/sound/soc/intel/skylake/cnl-acpi.c index 1bee574f2ab8..bda94640d966 100644 --- a/sound/soc/intel/skylake/cnl-acpi.c +++ b/sound/soc/intel/skylake/cnl-acpi.c @@ -129,7 +129,7 @@ int cnl_sdw_get_master_caps(struct device *dev, acpi_string path_sdw_ctrl = {"SCD"}; char path[SDW_PATH_CTRL_MAX]; - strcpy(path, path_sdw_ctrl); + strlcpy(path, path_sdw_ctrl, sizeof(path)); sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_ctrl, path); if (!m_cap) { dev_err(dev, "SoundWire controller mapping failed...\n"); From 581e1861c0a521b8ad0549cdf14666b32c375d00 Mon Sep 17 00:00:00 2001 From: Shahina Shaik Date: Mon, 30 Apr 2018 18:47:02 +0530 Subject: [PATCH 0253/1276] ASoC: Intel: Boards: Replace codec to component in RT274 machine driver As the framework is changed in kernel 4.17 version, replace codec variable with component and use component specific function to set jack. Change-Id: Id6d1cda7968a5d524a3210f1b38221214c2bb67d Signed-off-by: Shahina Shaik --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 5570474806ab..890e474ce597 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -188,7 +188,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_component *component = runtime->codec_dai->component; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; @@ -199,7 +199,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From d721502235098b3fd4af338ff76923a6bbe8293e Mon Sep 17 00:00:00 2001 From: Zhang Yanmin Date: Thu, 12 Apr 2018 16:42:28 +0530 Subject: [PATCH 0254/1276] ASoC: Intel: Skylake: Set dsp cores off during shutdown When the cores.usage_count is equal to 0, driver puts dsp cores to sleep. The issue happens when cores.usage_count is not equal to 0 and dsp core remains ON even when the system goes to shutdown. Removing the dependency of usage_count by forcing to disable the dsp core. Change-Id: I4d1c925dd9521c9eda2188e20eb262abf81e7b49 Signed-off-by: Mohit Sinha Signed-off-by: Zhang Yanmin Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Periyasamy, SriramX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.c | 1 + sound/soc/intel/skylake/skl.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 485c8b8c38a1..0f5e497e6f93 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -310,6 +310,7 @@ int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask) return ret; } +EXPORT_SYMBOL(skl_dsp_disable_core); int skl_dsp_boot(struct sst_dsp *ctx) { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 29e6c5a22f51..a0d41441a7a4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1110,6 +1110,15 @@ static void skl_shutdown(struct pci_dev *pci) return; snd_hdac_ext_stop_streams(bus); + /* While doing the warm reboot testing, some times dsp core is on + * when system goes to shutdown. When cores.usage_count is + * equal to zero then driver puts the dsp core to zero. On few + * warm reboots cores.usage_count is not equal to zero and dsp + * core is ON even system goes to shutdown. Force the dsp cores + * off without checking the usage_count. + */ + skl_dsp_disable_core(skl->skl_sst->dsp, SKL_DSP_CORE0_ID); + list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); From 97bf1f01a842422efaf1d31ff133d822adc99ce6 Mon Sep 17 00:00:00 2001 From: Zhang Yanmin Date: Thu, 12 Apr 2018 17:05:23 +0530 Subject: [PATCH 0255/1276] ASoC: Intel: Disable dsp core in skl_shutdown skl_shutdown requires to put dsp cores quiescent else leads to the issue when PMC timeout while waiting for IP SIDE_POK_STS and PRIM_POK_STS deassertions Change-Id: I6c654e5afeb9267b0887a70722fce9f4afa8a1d9 Signed-off-by: Mohit Sinha Signed-off-by: Zhang Yanmin Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Periyasamy, SriramX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/hda/ext/hdac_ext_stream.c | 1 - sound/soc/intel/skylake/skl.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 0ede36f51e2e..492f85841ac0 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -489,7 +489,6 @@ void snd_hdac_ext_stop_streams(struct hdac_bus *bus) if (bus->chip_init) { list_for_each_entry(stream, &bus->stream_list, list) snd_hdac_stream_stop(stream); - snd_hdac_bus_stop_chip(bus); } } EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index a0d41441a7a4..99f8cdf0cc07 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1110,6 +1110,8 @@ static void skl_shutdown(struct pci_dev *pci) return; snd_hdac_ext_stop_streams(bus); + snd_hdac_ext_bus_link_power_down_all(bus); + skl_dsp_sleep(skl->skl_sst->dsp); /* While doing the warm reboot testing, some times dsp core is on * when system goes to shutdown. When cores.usage_count is * equal to zero then driver puts the dsp core to zero. On few From e322a1aee3f275ea3ac6dcec4aee5326cd810cb8 Mon Sep 17 00:00:00 2001 From: xiao jin Date: Thu, 12 Apr 2018 17:32:04 +0530 Subject: [PATCH 0256/1276] ASoC: soc-pcm: Fix FE and BE race when accessing substream->runtime After start of FE and BE, FE might close without triggering STOP, and substream->runtime gets freed. However, BE remains at START state and BE's substream->runtime still points to the freed runtime. Later if FE gets opened/started again, and triggers STOP, then skl_platform_pcm_trigger accesses the freed old runtime data. Fix is by assigning be_substream->runtime in dpcm_be_dai_startup when BE's state is START. Change-Id: If1fd0464a3c6c2a3e22c8b2af7ccc68c801e0e80 Signed-off-by: Mohit Sinha Signed-off-by: xiao jin Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/soc-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e8b98bfd4cf1..afcb2cfaf551 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1620,6 +1620,8 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) if (be->dpcm[stream].users++ != 0) continue; + be_substream->runtime = be->dpcm[stream].runtime; + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue; @@ -1627,7 +1629,6 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: open %s BE %s\n", stream ? "capture" : "playback", be->dai_link->name); - be_substream->runtime = be->dpcm[stream].runtime; err = soc_pcm_open(be_substream); if (err < 0) { dev_err(be->dev, "ASoC: BE open failed %d\n", err); From 590c8eff068c00b23dd999922f06a78d6d9a4b1c Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Tue, 15 May 2018 23:41:08 +0530 Subject: [PATCH 0257/1276] Revert "ASoC: tdf8532: Fix Audio memory leakage at boot time" This reverts commit c7083ded28e697dadc4cf0ef838220260e4a10f4. Change-Id: I77b671edc5c02c2ddab7cc834ee8626845df7489 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Tewani, Pradeep D Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 68decd023a9d..a5e2a028338c 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -165,8 +165,6 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, *status_repl = (struct get_dev_status_repl *) repl_buff; out: - if (repl_buff) - kfree(repl_buff); return ret; } From f6854e11bf374acbb3cb326334284e91af8b4ca4 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:24:01 +0800 Subject: [PATCH 0258/1276] ASoC: tdf8532: fix memleak in tdf8532_wait_state Fix kmemleak issue in tdf8532_wait_state function by releasing the memory getting allocated continuosly in instance of get_dev_status_repl i.e. status_repl before exiting the function. kernel memory leakage in audio stack/kmemleak backtrace: unreferenced object 0xffff88006227cc20 (size 32): comm "irq/25-snd_soc_", pid 2302, jiffies 4294679082 (age 5506.010s) hex dump (first 32 bytes): 02 00 03 80 80 02 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x260 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0xab/0x15a [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x18d/0x350 Change-Id: I550897d6b1efbd5ebbe15ab47038adf99581a82f Tracked-On: Signed-off-by: Liu Changcheng Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Gogineni, GiribabuX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index a5e2a028338c..dd4cebec5cf9 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -174,29 +174,31 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; struct get_dev_status_repl *status_repl = NULL; + u8 cur_state = STATE_NONE; struct device *dev = &(dev_data->i2c->dev); do { ret = tdf8532_get_state(dev_data, &status_repl); if (ret < 0) goto out; - + cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", DUMP_PREFIX_NONE, 32, 1, status_repl, 6, false); + + kfree(status_repl); + status_repl = NULL; } while (time_before(jiffies, timeout_point) - && status_repl->state != req_state); + && cur_state != req_state); - if (status_repl->state == req_state) + if (cur_state == req_state) return 0; +out: ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - status_repl->state, req_state, ret); - -out: - kfree(status_repl); + cur_state, req_state, ret); return ret; } From 7bb7ce6935140b6335b558e1cc44adb0040f8f96 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:11:42 +0800 Subject: [PATCH 0259/1276] ASoC: tdf8532: right free allocated space in case of error 1. Check allocated space before using it. 2. The repl_buff parameter in tdf8523_single_read is used to store the read data from i2c interface. When the data isn't right read, the pre-allocate space should be freed and the content of repl_buff should be set as NULL in case of being wrong used by the caller. 3. In the wrong case i.e. ret != len, return -EINVAL Change-Id: I3d0e12a9fcb6516716efc92eb734a0248ab3fb28 Tracked-On: Signed-off-by: Liu Changcheng Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index dd4cebec5cf9..86b7430f4c88 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -107,11 +107,11 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, return ret; } -static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, +static int tdf8532_single_read(struct tdf8532_priv *dev_data, char **repl_buff) { int ret; - uint8_t len; + int len; struct device *dev = &(dev_data->i2c->dev); @@ -126,6 +126,10 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, len = ret + HEADER_SIZE; *repl_buff = kzalloc(len, GFP_KERNEL); + if (*repl_buff == NULL) { + ret = -ENOMEM; + goto out; + } ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); @@ -136,6 +140,8 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, dev_err(dev, "i2c recv packet returned: %d (expected: %d)\n", ret, len); + + ret = -EINVAL; goto out_free; } @@ -143,7 +149,7 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, out_free: kfree(*repl_buff); - repl_buff = NULL; + *repl_buff = NULL; out: return ret; } From 38174f80fdb9808df3f265c8f0fb448418a075fe Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Thu, 23 Nov 2017 20:52:05 +0530 Subject: [PATCH 0260/1276] ASoC: Intel: Skylake: Add kabylake R machine driver entry This patch adds acpi entry for kabylake R I2S machine driver, which makes use of ALC298 codec. Change-Id: Ie61f3c3e2759cd3a1b1380870307654e0d773ce7 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 99f8cdf0cc07..5a442ee5af64 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1242,7 +1242,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) + .drv_name = "kblr_alc298s_i2s", +#else .drv_name = "kbl_alc286s_i2s", +#endif .fw_filename = "intel/dsp_fw_kbl.bin", }, { From 7ef7cd5a22f40396128e63113378e87c6b1c8991 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 31 Jan 2018 12:31:27 +0530 Subject: [PATCH 0261/1276] ASoC: Intel: Boards: Add machine driver for Kabylake R This patch adds I2S machine driver for Kabylake R platform which makes use of ALC298 codec. Change-Id: I46b931b4f6f1c144c82bce8d03c3dafa635ac3d1 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 13 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/kblr_rt298.c | 549 ++++++++++++++++++++++++++++ 3 files changed, 564 insertions(+) create mode 100644 sound/soc/intel/boards/kblr_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 45b1d5a03ed0..44eac7c32e9b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -369,6 +369,19 @@ config SND_SOC_INTEL_BXT_ULL_MACH platform. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_KBLR_RT298_MACH + tristate "ASoC Audio driver for KBL-R with RT298 I2S mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT298 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Kabylake-R platform + with RT298 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 9a77052f0469..f255f7ab0960 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -29,6 +29,7 @@ snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o +snd-soc-kblr_rt298-objs := kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -60,3 +61,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o +obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o diff --git a/sound/soc/intel/boards/kblr_rt298.c b/sound/soc/intel/boards/kblr_rt298.c new file mode 100644 index 000000000000..1007e0239bb9 --- /dev/null +++ b/sound/soc/intel/boards/kblr_rt298.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2017-18 Intel Corporation + +/* + * kblr_rt298.c -Intel Kabylake-R I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt298.h" +#include "../../codecs/hdac_hdmi.h" + +static struct snd_soc_jack kabylake_headset; +static struct snd_soc_jack kabylake_hdmi[3]; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_rt298_private { + struct list_head hdmi_pcm_list; +}; + +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_DB_PB, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin kabylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new kabylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget kabylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), +}; + +static const struct snd_soc_dapm_route kabylake_rt298_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack detect */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMic", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + +}; + +static int kabylake_rt298_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static int kabylake_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *codec = rtd->codec_dai->component; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &kabylake_headset, + kabylake_headset_pins, ARRAY_SIZE(kabylake_headset_pins)); + + if (ret) + return ret; + + rt298_mic_detect(codec, &kabylake_headset); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + + return 0; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_rt298_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * on this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt298_fe_ops = { + .startup = kbl_fe_startup, +}; + +static int kabylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static int kabylake_rt298_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops kabylake_rt298_ops = { + .hw_params = kabylake_rt298_hw_params, +}; + +static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static const unsigned int channels_dmic[] = { + 2, 4, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int kabylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops kabylake_dmic_ops = { + .startup = kabylake_dmic_startup, +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_rt298_dais[] = { + /* Front End DAI links */ + [SKL_DPCM_AUDIO_PB] = { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = kabylake_rt298_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .ops = &kabylake_rt298_fe_ops, + }, + [SKL_DPCM_AUDIO_DB_PB] = { + .name = "Skl Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .ops = &kabylake_rt298_fe_ops, + + }, + [SKL_DPCM_AUDIO_CP] = { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + .ops = &kabylake_rt298_fe_ops, + }, + [SKL_DPCM_AUDIO_REF_CP] = { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_DMIC_CP] = { + .name = "Skl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt298-aif1", + .init = kabylake_rt298_codec_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp0_fixup, + .ops = &kabylake_rt298_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = kabylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 2, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 3, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 4, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +#define NAME_SIZE 32 +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_rt298_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + struct snd_soc_component *codec = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + codec = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &kabylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &kabylake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!codec) + return -EINVAL; + + return hdac_hdmi_jack_port_init(codec, &card->dapm); +} + +/* kabylake audio machine driver for SPT + RT298S */ +static struct snd_soc_card kabylake_rt298 = { + .name = "kabylake-rt298", + .owner = THIS_MODULE, + .dai_link = kabylake_rt298_dais, + .num_links = ARRAY_SIZE(kabylake_rt298_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_rt298_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_rt298_map), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_rt298_private *ctx; + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_rt298.dev = &pdev->dev; + snd_soc_card_set_drvdata(&kabylake_rt298, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &kabylake_rt298); +} + +static const struct platform_device_id kbl_board_ids[] = { + { .name = "kblr_alc298s_i2s" }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kblr_alc298s_i2s", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, + +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah "); +MODULE_AUTHOR("Puneeth Prabhu "); +MODULE_DESCRIPTION("Intel SST Audio for Kabylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kblr_alc298s_i2s"); From 8060b855d42a841e799d7c973f26b15c793c454d Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Fri, 12 Jan 2018 15:50:50 +0530 Subject: [PATCH 0262/1276] ASoC: rt298: Set jack combo for kabylake R This patch adds DMI information of Kabylake R to force_combo_jack_table[]. Change-Id: Ibf29d8eb5d3ded179aa76a31da5fbf8ad5d99d36 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Pawse, GuruprasadX Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/rt298.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 06cdba4edfe2..47f7d14f1413 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1169,6 +1169,13 @@ static const struct dmi_system_id force_combo_jack_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake") } }, + { + .ident = "Intel Kabylake R RVP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, { } }; From 4d3120dd58908088ec923ca34dba365093c711b5 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 2 May 2018 12:55:39 +0530 Subject: [PATCH 0263/1276] ASoC: Intel: Boards: Add machine driver for RSE topology This patch adds I2S machine driver for IVI RSE topology which uses dummy codec. Change-Id: I8152c3ec7d6057f341412a0a8179283dab247fa2 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 10 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_rse_rt298.c | 359 ++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_rse_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 44eac7c32e9b..c807cc465c47 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -382,6 +382,16 @@ config SND_SOC_INTEL_KBLR_RT298_MACH with RT298 I2S audio codec. Say Y if you have such a device. If unsure select "N". + +config SND_SOC_INTEL_BXTP_IVI_RSE_MACH + tristate "ASoC Audio driver for BXTP IVI RSE with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI Rear Seat Unit. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f255f7ab0960..683737d503dc 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -30,6 +30,7 @@ snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o +snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -62,3 +63,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o diff --git a/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c b/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c new file mode 100644 index 000000000000..25917c4a39d2 --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_rse_rt298.c -Intel RSE I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + /* BE connections */ + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec2_out"}, + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec3_out"}, + { "hdmi_ssp0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "Dummy Capture" }, + /* Test connections */ + { "Dummy Playback", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "TestSSP3_out"}, + { "TestSSP3_in", NULL, "ssp3 Rx" }, + { "ssp3 Rx", NULL, "Dummy Capture" }, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = 0x40900500; + u32 gpio_value2 = 0x44000600; + + gpio_addr = (void *)ioremap_nocache(0xd0c40610, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp1_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000400; + + gpio_addr = (void *)ioremap_nocache(0xd0c40660, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x20, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000A00; + u32 gpio_value2 = 0x44000800; + + gpio_addr = (void *)ioremap_nocache(0xd0c705A0, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int bxtp_ssp3_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000800; + u32 gpio_value2 = 0x44000802; + + gpio_addr = (void *)ioremap_nocache(0xd0c40638, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int broxton_ssp1_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 48k, 2 Channel */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP1 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int broxton_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 44k, stereo */ + rate->min = rate->max = 44100; + channels->min = channels->max = 2; + + /* set SSP2 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int broxton_ssp4_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 44k, stereo */ + rate->min = rate->max = 44100; + channels->min = channels->max = 2; + + /* set SSP4 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + /* Back End DAI links */ + { .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp0_gpio_init, + .dpcm_capture = 1, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = broxton_ssp1_fixup, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp1_gpio_init, + .dpcm_playback = 1, + }, + { + .name = "SSP2-Codec", + .id = 3, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = broxton_ssp2_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + }, + { + .name = "SSP3-Codec", + .id = 4, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp3_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "SSP4-Codec", + .id = 5, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = broxton_ssp4_fixup, + .dpcm_playback = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-rse", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .controls = NULL, + .num_controls = 0, + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + return snd_soc_register_card(&broxton_rt298); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_rse_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Ramesh Babu "); +MODULE_AUTHOR("Senthilnathan Veppur "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_rse_i2s"); From 10d38d892a5a7fa9424558e53ce45a99752ea17d Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Mon, 7 May 2018 17:22:32 +0530 Subject: [PATCH 0264/1276] ASoC: Intel: Boards: Add machine driver for HU topology This patch adds I2S machine driver for IVI HU topology which uses dummy codec. Change-Id: Iff3afa704d23bc7a479265bf1139db5af579b258 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_hu_rt298.c | 250 +++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_hu_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c807cc465c47..d98f82cf4a3c 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -392,6 +392,15 @@ config SND_SOC_INTEL_BXTP_IVI_RSE_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_HU_MACH + tristate "ASoC Audio driver for BXTP IVI HU with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI Head Unit. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 683737d503dc..949db4b0438d 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -31,6 +31,7 @@ snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o +snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -64,3 +65,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o diff --git a/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c b/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c new file mode 100644 index 000000000000..ce467721305a --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_hu_rt298.c -Intel HU I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define DEF_BT_RATE_INBDEX 0x0 + +struct bxtp_ivi_hu_prv { + int srate; +}; + +static unsigned int ivi_hu_bt_rates[] = { + 8000, + 16000, +}; + +/* sound card controls */ +static const char * const bt_rate[] = {"8K", "16K"}; + +static const struct soc_enum btrate_enum = + SOC_ENUM_SINGLE_EXT(2, bt_rate); + +static int bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = drv->srate; + return 0; +} + +static int bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + if (ucontrol->value.integer.value[0] == drv->srate) + return 0; + + drv->srate = ucontrol->value.integer.value[0]; + return 0; + +} + +static const struct snd_kcontrol_new hu_snd_controls[] = { + + SOC_ENUM_EXT("BT Rate", btrate_enum, + bt_sample_rate_get, bt_sample_rate_put), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "bt_ssp0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "Dummy Capture" }, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = 0x40900500; + u32 gpio_value2 = 0x44000600; + + gpio_addr = (void *)ioremap_nocache(0xd0c40610, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int broxton_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_soc_card *card = rtd->card; + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + /* SSP0 operates with a BT Transceiver */ + rate->min = rate->max = ivi_hu_bt_rates[drv->srate]; + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec - for HDMI MCH */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp0_fixup, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-hu", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = hu_snd_controls, + .num_controls = ARRAY_SIZE(hu_snd_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + int ret_val; + struct bxtp_ivi_hu_prv *drv; + + broxton_rt298.dev = &pdev->dev; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->srate = DEF_BT_RATE_INBDEX; + snd_soc_card_set_drvdata(&broxton_rt298, drv); + + ret_val = snd_soc_register_card(&broxton_rt298); + if (ret_val) { + dev_dbg(&pdev->dev, "snd_soc_register_card failed %d\n", + ret_val); + return ret_val; + } + + platform_set_drvdata(pdev, &broxton_rt298); + + return ret_val; +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_hu_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Ramesh Babu "); +MODULE_AUTHOR("Senthilnathan Veppur "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton-P IVI HU"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_hu_i2s"); From f816b353484cf2b8858454bdc38e27e0535fb7c3 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 3 Jan 2018 19:52:29 +0530 Subject: [PATCH 0265/1276] ASoC: Intel: Boards: Add a machine driver for BXT-P IVI M3 This patch adds support for a machine driver for Validation purposes only for M3 topology in IVI, that supports - 1. 2 bluetooth streams for playback and capture 2. 2 streams out of a Master SSP with 8 channels (PB & CP) 3. 2 more streams out of a Slave SSP with 8 channels (PB & CP) whose clock and frame are tied to above Master SSP for synchronous playback of 16 channel data. Dummy Codec driver is used for reference. Related Changes for Makefile and Kconfig have been added. Change-Id: Ibd8562678890accd21adfd89f521502fc7e88bf9 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Prabhu, PuneethX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_m3.c | 327 +++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_m3.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d98f82cf4a3c..b38c4550fa2d 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -401,6 +401,15 @@ config SND_SOC_INTEL_BXTP_IVI_HU_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_M3_MACH + tristate "ASoC Audio driver for BXTP IVI M3 with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI M3 topology. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 949db4b0438d..7bba4ac5ab44 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -32,6 +32,7 @@ snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o +snd-soc-bxtp_ivi_m3-objs := bxtp_ivi_m3.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -66,3 +67,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) += snd-soc-bxtp_ivi_m3.o diff --git a/sound/soc/intel/boards/bxtp_ivi_m3.c b/sound/soc/intel/boards/bxtp_ivi_m3.c new file mode 100644 index 000000000000..5dcafb979a49 --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_m3.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_m3.c -Intel BXTP-IVI M3 I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define SSP0_GPIO_BASE 0xd0c40610 +#define SSP0_GPIO_VALUE1 0x40900500 +#define SSP0_GPIO_VALUE2 0x44000600 +#define SSP1_GPIO_BASE 0xd0c40660 +#define SSP1_GPIO_VALUE1 0x44000400 +#define SSP4_GPIO_BASE 0xd0c705A0 +#define SSP4_GPIO_VALUE1 0x44000A00 +#define SSP4_GPIO_VALUE2 0x44000800 +#define SSP5_GPIO_BASE 0xd0c70580 +#define SSP5_GPIO_VALUE 0x44000800 + +#define DEF_BT_RATE_INBDEX 0x0 + +struct bxtp_ivi_gen_prv { + int srate; +}; + +static unsigned int ivi_gen_bt_rates[] = { + 8000, + 16000, +}; + +/* sound card controls */ +static const char * const bt_rate[] = {"8K", "16K"}; + +static const struct soc_enum btrate_enum = + SOC_ENUM_SINGLE_EXT(2, bt_rate); + +static int bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = drv->srate; + return 0; +} + +static int bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + if (ucontrol->value.integer.value[0] == drv->srate) + return 0; + + drv->srate = ucontrol->value.integer.value[0]; + return 0; + +} +static const struct snd_kcontrol_new gen_snd_controls[] = { + + SOC_ENUM_EXT("BT Rate", btrate_enum, + bt_sample_rate_get, bt_sample_rate_put), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + /* BE connections */ + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "codec0_in", NULL, "ssp4 Rx" }, + { "ssp4 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "codec1_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "Dummy Capture"}, + + { "hdmi_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec4_out"}, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP0_GPIO_VALUE1; + u32 gpio_value2 = SSP0_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP0_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP4_GPIO_VALUE1; + u32 gpio_value2 = SSP4_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP4_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int broxton_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_soc_card *card = rtd->card; + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + + /* The ADSP will covert the FE rate to 8k,16k mono */ + rate->min = rate->max = ivi_gen_bt_rates[drv->srate]; + channels->min = channels->max = 2; + return 0; + +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp2_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-m3", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = gen_snd_controls, + .num_controls = ARRAY_SIZE(gen_snd_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + int ret_val; + struct bxtp_ivi_gen_prv *drv; + + broxton_rt298.dev = &pdev->dev; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->srate = DEF_BT_RATE_INBDEX; + snd_soc_card_set_drvdata(&broxton_rt298, drv); + ret_val=snd_soc_register_card(&broxton_rt298); + + if (ret_val) { + dev_dbg(&pdev->dev, "snd_soc_register_card failed %d\n", + ret_val); + return ret_val; + } + + platform_set_drvdata(pdev, &broxton_rt298); + + return ret_val; +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_m3_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Mousumi Jana "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_m3_i2s"); From 8ab67f3f04353b3c7c6c9538b5cffa2e1b1289c1 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Fri, 11 May 2018 11:32:53 +0530 Subject: [PATCH 0266/1276] ASoC: Intel: Boards: Add machine driver for generic topology This patch adds I2S machine driver for IVI generic topology which uses dummy codec. Generic topology supports 6 streams of playback and 6 streams of capture each stream is of 48K sampling rate, 8 channels and 32 bit depth. Change-Id: I6bc7b38553f90a3281a8c46b5cdbd7504713302e Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_generic.c | 366 ++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_generic.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b38c4550fa2d..069ae85c555b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -410,6 +410,15 @@ config SND_SOC_INTEL_BXTP_IVI_M3_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH + tristate "ASoC Audio driver for BXTP IVI GENERIC with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI generic topology. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 7bba4ac5ab44..f69831624273 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -33,6 +33,7 @@ snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o snd-soc-bxtp_ivi_m3-objs := bxtp_ivi_m3.o +snd-soc-bxtp_ivi_generic-objs := bxtp_ivi_generic.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -68,3 +69,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) += snd-soc-bxtp_ivi_m3.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) += snd-soc-bxtp_ivi_generic.o diff --git a/sound/soc/intel/boards/bxtp_ivi_generic.c b/sound/soc/intel/boards/bxtp_ivi_generic.c new file mode 100644 index 000000000000..e256a4bf173b --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_generic.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_generic.c -Intel generic I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define SSP0_GPIO_BASE 0xd0c40610 +#define SSP0_GPIO_VALUE1 0x40900500 +#define SSP0_GPIO_VALUE2 0x44000600 +#define SSP1_GPIO_BASE 0xd0c40660 +#define SSP1_GPIO_VALUE1 0x44000400 +#define SSP4_GPIO_BASE 0xd0c705A0 +#define SSP4_GPIO_VALUE1 0x44000A00 +#define SSP4_GPIO_VALUE2 0x44000800 +#define SSP5_GPIO_BASE 0xd0c70580 +#define SSP5_GPIO_VALUE 0x44000800 + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + /* DAPM route for IVI generic topology */ + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + + /* BE connections */ + { "Dummy Playback", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "codec3_out"}, + { "codec3_in", NULL, "ssp5 Rx" }, + { "ssp5 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "codec0_in", NULL, "ssp4 Rx" }, + { "ssp4 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "codec5_out"}, + { "codec5_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "codec1_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec2_out"}, + { "codec2_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "Dummy Capture"}, + + { "hdmi_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec4_out"}, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP0_GPIO_VALUE1; + u32 gpio_value2 = SSP0_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP0_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp1_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP1_GPIO_VALUE1; + + gpio_addr = (void *)ioremap_nocache(SSP1_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x20, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP4_GPIO_VALUE1; + u32 gpio_value2 = SSP4_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP4_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int bxtp_ssp5_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP5_GPIO_VALUE; + + gpio_addr = (void *)ioremap_nocache(SSP5_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp3_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000800; + u32 gpio_value2 = 0x44000802; + + gpio_addr = (void *)ioremap_nocache(0xd0c40638, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp1_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP3 - Codec */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp3_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP5 - Codec */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp5_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-generic", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = NULL, + .num_controls = 0, + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + return snd_soc_register_card(&broxton_rt298); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_generic_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Puneeth Prabhu "); +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Mousumi Jana "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_generic_i2s"); From 162823f91add67b55b9b1483e876872f097bc91c Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Mon, 7 May 2018 16:38:11 +0530 Subject: [PATCH 0267/1276] ASoC: Intel: Skylake: Add RSE, HU, M3 and generic machine driver entry This patch adds driver names of RSE, HU, M3 and generic machine drivers to driver data. Also adds the name of the FW file that has to be loaded when IVI machine drivers are enabled. Change-Id: I39eafec466e83a56ee7ada22b6b47927e7533ece Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5a442ee5af64..293a1b98b8db 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,8 +1216,25 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) + .drv_name = "bxt_ivi_rse_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) + .drv_name = "bxt_ivi_hu_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) + .drv_name = "bxt_ivi_m3_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .drv_name = "bxt_ivi_generic_i2s", +#else .drv_name = "bxt_alc298s_i2s", +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .fw_filename = "intel/ADSPFW.bin" +#else .fw_filename = "intel/dsp_fw_bxtn.bin", +#endif }, { .id = "DLGS7219", From dfcf239c0cfc55f6c8170dacb39a30b4ecf4f69b Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Wed, 23 May 2018 21:17:13 +0530 Subject: [PATCH 0268/1276] ASoC: Intel: Skylake: Resolve load DMA control config issue Audio driver doesn't load dmactrl_cfg after adding it in topology xml. There is no value assigned to dmactrl_cfg.size when dmactrl_cfg is parsed from topology xml by codec. But it checks for dmactrl_cfg.size when dmactrl_cfg is send to DSP. This Patch resolve this issue. Change-Id: I7caac5281cd1a6151ca7874f3b74865d91347065 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Shaik, Kareem M Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b91c4ea88002..c80ad4558ba6 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4632,6 +4632,7 @@ static int skl_tplg_mfest_fill_dmactrl(struct device *dev, if (!hdr->data) return -ENOMEM; hdr->data_size = tkn_elem->value; + dmactrl_cfg->size = hdr->data_size; } else { hdr->data_size = 0; dev_err(dev, "Invalid dmactrl info \n"); From 0fb263581498e0de17670c44965a6f1bd019012d Mon Sep 17 00:00:00 2001 From: Kareem Shaik Date: Wed, 29 Nov 2017 15:38:20 +0530 Subject: [PATCH 0269/1276] ASoC: Intel: Skylake: Improve BXT-P machine driver selection based on configuration Use configuration option to select the correct machine driver defined under same machine id. Otherwise second machine driver is not loading without a configuration check. Change-Id: I419125f133f6b684916e81f26d0e5e685414a5af Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 293a1b98b8db..3a11cef06e6b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1243,16 +1243,19 @@ IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) { .id = "INT34C3", .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", }, +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) { .id = "INT34C3", .drv_name = "bxt_ivi_ull", .fw_filename = "intel/dsp_fw_ull_bxtn.bin", }, +#endif {} }; From 0be27edd20c450f991afc94adaa7d9aa2dc277c1 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 30 Mar 2017 22:57:48 +0530 Subject: [PATCH 0270/1276] ASoC: Intel: common: Provide an interface to send IPCs directly This patch adds support for an IPC Tx function that can be called directly rather than adding new Tx messages to the Queue. Hence the new logic to send IPCs will be - If the DSP is not busy, send IPCs directly. Else add to the queue Change-Id: I9d58872a051c9d704c60cf25a2e3b69c3b580818 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Koul, Vinod Tested-by: Tr, HarishkumarX Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-ipc.c | 11 +++++++-- sound/soc/intel/common/sst-ipc.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 34 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 03896bde44d3..0037e21f0b69 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -127,8 +127,15 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); list_add_tail(&msg->list, &ipc->tx_list); - schedule_work(&ipc->kwork); - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + if ((ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) || + (ipc->ops.direct_tx_msg == NULL)) { + schedule_work(&ipc->kwork); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + } else { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + ipc->ops.direct_tx_msg(ipc); + } if (wait) return tx_wait_done(ipc, msg, rx_data, diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 4cfa9e37ac28..eae3fa7d1aef 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -47,6 +47,7 @@ struct sst_generic_ipc; struct sst_plat_ipc_ops { void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *); + void (*direct_tx_msg)(struct sst_generic_ipc *); void (*shim_dbg)(struct sst_generic_ipc *, const char *); void (*tx_data_copy)(struct ipc_message *, char *, size_t); u64 (*reply_msg_match)(u64 header, u64 *mask); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 6efc9502d06e..9370c474c618 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -343,6 +343,39 @@ static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp) return (hipci & SKL_ADSP_REG_HIPCI_BUSY); } +static void skl_ipc_tx_msgs_direct(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + if (list_empty(&ipc->tx_list) || ipc->pending) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy, we will TX messages after IRQ. + * also postpone if we are in the middle of procesing completion irq*/ + if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { + dev_dbg(ipc->dev, "skl_ipc_tx_msgs_direct dsp busy\n"); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); + list_move(&msg->list, &ipc->rx_list); + + dev_dbg(ipc->dev, "skl_ipc_tx_msgs_direct sending message, header - %#.16lx\n", + (unsigned long)msg->header); + print_hex_dump_debug("Params:", DUMP_PREFIX_OFFSET, 8, 4, + msg->tx_data, msg->tx_size, false); + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); +} + /* Lock to be held by caller */ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) { @@ -854,6 +887,7 @@ int skl_ipc_init(struct device *dev, struct skl_sst *skl) ipc->ops.tx_msg = skl_ipc_tx_msg; ipc->ops.tx_data_copy = skl_ipc_tx_data_copy; + ipc->ops.direct_tx_msg = skl_ipc_tx_msgs_direct; ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy; return 0; From 690d4230940299fee572ac9525f55462465e1302 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 14 Feb 2018 06:39:45 +0530 Subject: [PATCH 0271/1276] ASoC: Intel: BXT: Remove compile warnings Remove compile warnings from the function bxt_load_base_firmware Change-Id: I88b13e9dc045ecc57b989a96e83c7da1683b0e3b Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Kp, Jeeja Tested-by: Tr, HarishkumarX Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/bxt-sst.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 47b2a24f84dc..bac77f8fc0d4 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -277,14 +277,14 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - dev_err(ctx->dev, "Itertion %d Core En/ROM load fail:%d\n", i,ret); + dev_err(ctx->dev, "Iteration %d Core En/ROM load fail:%d\n", i, ret); continue; } - dev_dbg(ctx->dev, "Itertion %d ROM load Success:%d,%d\n", i,ret); + dev_dbg(ctx->dev, "Iteration %d ROM load Success:%d\n", i, ret); ret = sst_transfer_fw_host_dma(ctx); if (ret < 0) { - dev_err(ctx->dev, "Itertion %d Transfer firmware failed %d\n", i,ret); + dev_err(ctx->dev, "Iteration %d Transfer firmware failed %d\n", i, ret); dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); @@ -293,7 +293,7 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); continue; } - dev_dbg(ctx->dev, "Itertion %d FW transfer Success:%d,%d\n", i,ret); + dev_dbg(ctx->dev, "Iteration %d FW transfer Success:%d\n", i, ret); if (ret == 0) break; From 33e21b77b7281b7fa9bdd5207fb6949f2720d809 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Tue, 15 May 2018 12:01:07 +0530 Subject: [PATCH 0272/1276] ASoC: topology: Increase SND_SOC_TPLG_NUM_TEXTS to 32 ITT supports maximum of 32 path configs for a path. But, kernel supports upto 16 path configs. So, increase SND_SOC_TPLG_NUM_TEXTS to 32 to support 32 path configs for a path. Change-Id: Idc3bb893829e5a7a58a001fe28773257f96c11e1 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- include/uapi/sound/asoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index a74ca232f1fc..5b1411bf79c5 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -88,7 +88,7 @@ #define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ /* string sizes */ -#define SND_SOC_TPLG_NUM_TEXTS 16 +#define SND_SOC_TPLG_NUM_TEXTS 32 /* ABI version */ #define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ From 2668058661956788b895720d63e6d22161a08389 Mon Sep 17 00:00:00 2001 From: Sameer Sharma Date: Thu, 7 Jun 2018 17:28:42 +0530 Subject: [PATCH 0273/1276] ASoC: Intel: board: Add support for FE dynamic dai for ULL FE dai links now come from the topology, so removing them from machine driver. Change-Id: I2ac610902496beecb225fc44e57be28d05fb98a7 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Prabhu, PuneethX Reviewed-by: Periyasamy, SriramX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/bxt_ivi_ull.c | 54 ---------------------------- 1 file changed, 54 deletions(-) diff --git a/sound/soc/intel/boards/bxt_ivi_ull.c b/sound/soc/intel/boards/bxt_ivi_ull.c index 22ae8ab30a1d..fe9e28598bbe 100644 --- a/sound/soc/intel/boards/bxt_ivi_ull.c +++ b/sound/soc/intel/boards/bxt_ivi_ull.c @@ -95,60 +95,6 @@ static const struct snd_soc_dapm_route bxtp_ull_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link bxtp_ull_dais[] = { - { - .name = "Bxt Audio Port 3", - .stream_name = "Stereo-16K SSP4", - .cpu_dai_name = "System Pin 3", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, - { - .name = "Bxt Audio Port 4", - .stream_name = "5-ch SSP1", - .cpu_dai_name = "System Pin 4", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - }, - { - .name = "Bxt Audio Port 5", - .stream_name = "SSP2 Stream", - .cpu_dai_name = "System Pin 5", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, - { - .name = "Bxt Audio Port 6", - .stream_name = "8-Ch SSP0", - .cpu_dai_name = "System Pin 6", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_capture = 1, - }, /* Probe DAI Links */ { .name = "Bxt Compress Probe playback", From aa48b11e990c04edcee7920acc9332bfd93da285 Mon Sep 17 00:00:00 2001 From: Sameer Sharma Date: Mon, 18 Jun 2018 19:54:54 +0530 Subject: [PATCH 0274/1276] ASoC: Intel: Skylake: Enable use_tplg_pcm flag for BXTP platform Enabling use_tplg_pcm flag to check whether FE dais will be registered from topology during dai driver component registration for BXTP platform Change-Id: I60766088a56922e61c8010b0a587452db3db42d2 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Prabhu, PuneethX Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 3a11cef06e6b..199cbfe1d00d 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1188,6 +1188,10 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; +static struct skl_machine_pdata bxt_pdata = { + .use_tplg_pcm = true, +}; + static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", @@ -1248,12 +1252,14 @@ IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) .id = "INT34C3", .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", + .pdata = &bxt_pdata, }, #elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) { .id = "INT34C3", .drv_name = "bxt_ivi_ull", .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + .pdata = &bxt_pdata, }, #endif {} From bdf35d6da224e3f184c5ee19f21dfac94ac8551c Mon Sep 17 00:00:00 2001 From: "Lewandowski, Gustaw" Date: Wed, 11 Jul 2018 13:52:39 +0200 Subject: [PATCH 0275/1276] ASoC: Intel: Fix race condition in IPC rx list Since there are multiple IPCs being sent in a short span of time, there is a possibility of more than one message being on the Rx list after receiving response from firmware. In such cases, when the first notification of interrupt from firmware is received, driver retrieves the message from the Rx list but does not delete it from the list till the next lock. In the meantime, when another interrupt is received from the firmware, driver is reading the previous message again since the previous message has not been removed from the list. Change-Id: I3d85cce7b0e9632a73e286d7d42a2627c1431d6e Reviewed-on: Tested-by: Lewandowski, Gustaw Reviewed-by: Wasko, Michal --- sound/soc/intel/skylake/skl-sst-ipc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 9370c474c618..0e7b5ce64b9e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -420,6 +420,7 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, msg = list_first_entry(&ipc->rx_list, struct ipc_message, list); + list_del(&msg->list); out: return msg; @@ -707,8 +708,8 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, spin_lock_irqsave(&ipc->dsp->spinlock, flags); msg = skl_ipc_reply_get_msg(ipc, *ipc_header); - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); if (msg == NULL) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); dev_dbg(ipc->dev, "ipc: rx list is empty\n"); return; } @@ -753,8 +754,6 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, } } - spin_lock_irqsave(&ipc->dsp->spinlock, flags); - list_del(&msg->list); sst_ipc_tx_msg_reply_complete(ipc, msg); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); } From 245b329e0937ec45bb09ad0484efbccd788f2f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Thu, 12 Jul 2018 12:55:26 +0200 Subject: [PATCH 0276/1276] ASoC: Intel: Skylake: pipeline needs to be reset before disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5666ca1fca1ca80e9b431138470f4d9693ff011b Signed-off-by: Amadeusz SÅ‚awiÅ„ski Reviewed-on: Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/intel/skylake/skl-messages.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bc8d3afcf7d1..c01875a2a61d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2632,7 +2632,9 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) /* * A pipeline needs to be deleted on cleanup. If a pipeline is running, then - * pause the pipeline first and then delete it + * pause the pipeline first and then delete it. There is also case in which + * pipeline needs to be reset before deletion, so always reset as it doesn't + * change anything in other cases. * The pipe delete is done by sending delete pipeline IPC. DSP will stop the * DMA engines and releases resources */ @@ -2642,6 +2644,10 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + /* If pipe was not created in FW, do not try to delete it */ + if (pipe->state < SKL_PIPE_CREATED) + return 0; + /* If pipe is started, do stop the pipe in FW. */ if (pipe->state >= SKL_PIPE_STARTED) { ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); @@ -2653,9 +2659,14 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) pipe->state = SKL_PIPE_PAUSED; } - /* If pipe was not created in FW, do not try to delete it */ - if (pipe->state < SKL_PIPE_CREATED) - return 0; + /* reset pipe state before deletion */ + ret = skl_set_pipe_state(ctx, pipe, PPL_RESET); + if (ret < 0) { + dev_err(ctx->dev, "Failed to reset pipe ret=%d\n", ret); + return ret; + } + + pipe->state = SKL_PIPE_RESET; ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); if (ret < 0) { From 9132291ae7fe177b0116ed5f1d1721a1cfd55f0b Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Tue, 24 Jul 2018 16:30:03 +0200 Subject: [PATCH 0277/1276] Revert "ASoC: topology: Increase SND_SOC_TPLG_NUM_TEXTS to 32" This reverts commit 640a66de27627b1f18270497d8e16fec7c9afb58. Since correspondig changes to alsa-lib is not properly upstreamed we should not introduce such change without ensure backward compatibility. Change-Id: I512b63b0e4dc7bbd889503a560c373c9e396326e Reviewed-on: Reviewed-by: Slawinski, AmadeuszX Reviewed-by: Bozek, DominikX Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- include/uapi/sound/asoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 5b1411bf79c5..a74ca232f1fc 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -88,7 +88,7 @@ #define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ /* string sizes */ -#define SND_SOC_TPLG_NUM_TEXTS 32 +#define SND_SOC_TPLG_NUM_TEXTS 16 /* ABI version */ #define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ From 9fe75e536cf33c2dfa05bf19b318ec00b9741097 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 17 Jul 2018 17:06:34 +0200 Subject: [PATCH 0278/1276] ASoC: Intel: Allow for firmware load retry. Due to unconditional initial timeouts, firmware may fail to load during its initialization. This issue cannot be resolved on driver side but has to be accounted for nonetheless. Default firmware load retry count is set to 3. Change-Id: Idd12483e07633d1f809cdc4363fb51f5217233ad Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Slawinski, AmadeuszX Reviewed-by: Lewandowski, Gustaw Tested-by: Lewandowski, Gustaw --- sound/soc/intel/common/sst-dsp.h | 7 +++++ sound/soc/intel/skylake/cnl-sst.c | 48 ++++++++++++++++++------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 859f0de00339..92150322496c 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -174,6 +174,13 @@ #define SST_PMCS 0x84 #define SST_PMCS_PS_MASK 0x3 +/* + * Number of times to retry firmware load before driver commits failure. + * This is to account for initial timeouts, e.g., from ROM init during + * FW load procedure when the former fails to receive imr from CSE. + */ +#define SST_FW_INIT_RETRY 3 + struct sst_dsp; /* diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index df42b2157b21..19398e200cb0 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -212,49 +212,56 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) struct firmware stripped_fw; struct skl_sst *cnl = ctx->thread_context; struct skl_fw_property_info fw_property; - int ret; + int ret, i; fw_property.memory_reclaimed = -1; if (!ctx->fw) { ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "request firmware failed: %d\n", ret); - goto cnl_load_base_firmware_failed; + return ret; } } /* parse uuids if first boot */ if (cnl->is_first_boot) { - ret = snd_skl_parse_uuids(ctx, ctx->fw, - CNL_ADSP_FW_HDR_OFFSET, 0); + ret = snd_skl_parse_uuids(ctx, ctx->fw, CNL_ADSP_FW_HDR_OFFSET, 0); if (ret < 0) - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } stripped_fw.data = ctx->fw->data; stripped_fw.size = ctx->fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); - ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); - if (ret < 0) { - dev_err(ctx->dev, "prepare firmware failed: %d\n", ret); - goto cnl_load_base_firmware_failed; - } + ret = -ENOEXEC; + for (i = 0; i < SST_FW_INIT_RETRY && ret < 0; i++) { + ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); + if (ret < 0) { + dev_dbg(ctx->dev, "prepare firmware failed: %d\n", ret); + continue; + } - ret = sst_transfer_fw_host_dma(ctx); - if (ret < 0) { - dev_err(ctx->dev, "transfer firmware failed: %d\n", ret); - cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); - goto cnl_load_base_firmware_failed; + dev_dbg(ctx->dev, "ROM loaded successfully on iteration %d.\n", i); + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "transfer firmware failed: %d\n", ret); + cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + } } + if (ret < 0) + goto load_base_firmware_failed; + dev_dbg(ctx->dev, "Firmware download successful.\n"); + ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete, msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); if (ret == 0) { dev_err(ctx->dev, "FW ready timed-out\n"); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } cnl->fw_loaded = true; @@ -263,7 +270,7 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) if (ret < 0) { dev_err(ctx->dev, "fwconfig ipc failed !\n"); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } fw_property = cnl->fw_property; @@ -271,14 +278,14 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", fw_property.memory_reclaimed); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } ret = skl_get_hardware_configuration(ctx); if (ret < 0) { dev_err(ctx->dev, "hwconfig ipc failed !\n"); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } /* Update dsp core count retrieved from hw config IPC */ @@ -286,7 +293,8 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) return 0; -cnl_load_base_firmware_failed: +load_base_firmware_failed: + dev_err(ctx->dev, "Firmware load failed: %d.\n", ret); release_firmware(ctx->fw); ctx->fw = NULL; From 658b9d3545e9607657d3b796e6363f2eec831ccc Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Thu, 19 Jul 2018 11:59:01 +0530 Subject: [PATCH 0279/1276] Revert "ASoC: core: Do not return for dummy codec in soc_probe_component" This reverts commit 88088a3e6a6c379b069241176f5c4f1c8573f85c. As an impact of above commit, dummy widget gets added to the widget list which leads to creation of DAI link between dummy widget and the FE DAI widget. As a result, skl_tplg_fe_get_cpr_module() returns an invalid module config pointer which leads to crash. Reverting the above commit will avoid adding a dummy widget to the widget list. Change-Id: I3d41fc776806185d9040dae695dbc258989be863 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/soc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f41ff22e1317..2ecf3442c965 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1242,10 +1242,10 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; int ret; -#if 0 + if (!strcmp(component->name, "snd-soc-dummy")) return 0; -#endif + if (component->card) { if (component->card != card) { dev_err(component->dev, From 94af5fc6b802add9432010681886a5b6fc8426ea Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 13 Aug 2018 15:29:19 +0200 Subject: [PATCH 0280/1276] ASoC: Intel: Skylake: Align with v4.18-rc1 linux kernel base. File skl-tplg-interface.h no longer present, relocated to uapi. Removal of duplicate declaration for skl_dai_load within skl-topology.h. Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 436e0365aa76..ee32aaa68490 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -20,7 +20,7 @@ #include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" -#include "skl-tplg-interface.h" +#include struct sst_dsp; struct skl_sst; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index e86b84e9868d..c0ef5e78dbfa 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -629,8 +629,6 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, From 533fe92d744d974d5cfc4bfc8328f7c952eb0c04 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 11:38:49 +0200 Subject: [PATCH 0281/1276] ASoC: Intel: Move bxt machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to bxt-specific data only. Change-Id: I6780bcd1506709757e673130ffc0ce6d0b26709f Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-bxt-match.c | 33 +++++++++++ sound/soc/intel/skylake/skl.c | 57 ------------------- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d3..77cbd98d3e22 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -8,17 +8,39 @@ #include #include +#include "../skylake/skl.h" static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; +static struct skl_machine_pdata bxt_pdata = { + .use_tplg_pcm = true, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) + .drv_name = "bxt_ivi_rse_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) + .drv_name = "bxt_ivi_hu_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) + .drv_name = "bxt_ivi_m3_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .drv_name = "bxt_ivi_generic_i2s", +#else .drv_name = "bxt_alc298s_i2s", +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .fw_filename = "intel/ADSPFW.bin" +#else .fw_filename = "intel/dsp_fw_bxtn.bin", +#endif }, { .id = "DLGS7219", @@ -44,13 +66,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", .asoc_plat_name = "0000:00:0e.0", }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) + { + .id = "INT34C3", + .drv_name = "bxt_ivi_ull", + .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + .pdata = &bxt_pdata, + }, +#else { .id = "INT34C3", .drv_name = "bxt_tdf8532", + .fw_filename = "intel/dsp_fw_bxtn.bin", .sof_fw_filename = "intel/sof-apl.ri", .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", .asoc_plat_name = "0000:00:0e.0", + .pdata = &bxt_pdata, }, +#endif {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 199cbfe1d00d..6dc99d4961ae 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1164,11 +1164,6 @@ static struct snd_soc_acpi_codecs kbl_codecs = { .codecs = {"10508825"} }; -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - static struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} @@ -1188,10 +1183,6 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct skl_machine_pdata bxt_pdata = { - .use_tplg_pcm = true, -}; - static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", @@ -1217,54 +1208,6 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) - .drv_name = "bxt_ivi_rse_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) - .drv_name = "bxt_ivi_hu_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) - .drv_name = "bxt_ivi_m3_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) - .drv_name = "bxt_ivi_generic_i2s", -#else - .drv_name = "bxt_alc298s_i2s", -#endif -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) - .fw_filename = "intel/ADSPFW.bin" -#else - .fw_filename = "intel/dsp_fw_bxtn.bin", -#endif - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) - { - .id = "INT34C3", - .drv_name = "bxt_tdf8532", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .pdata = &bxt_pdata, - }, -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) - { - .id = "INT34C3", - .drv_name = "bxt_ivi_ull", - .fw_filename = "intel/dsp_fw_ull_bxtn.bin", - .pdata = &bxt_pdata, - }, -#endif - {} -}; - static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", From d87dbbd58c3684eda1bdf70abdd9c5050d506dce Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:02:26 +0200 Subject: [PATCH 0282/1276] ASoC: Intel: Move kbl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to kbl-specific data only. Change-Id: I25846b83d6c791c7c7dae25766a06beddd686277 Depends-On: I6780bcd1506709757e673130ffc0ce6d0b26709f Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-kbl-match.c | 4 + sound/soc/intel/skylake/skl.c | 79 ------------------- 2 files changed, 4 insertions(+), 79 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index 0ee173ca437d..750a7f851a2c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -35,7 +35,11 @@ static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) + .drv_name = "kblr_alc298s_i2s", +#else .drv_name = "kbl_alc286s_i2s", +#endif .fw_filename = "intel/dsp_fw_kbl.bin", }, { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 6dc99d4961ae..f661ffc4a33a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1159,26 +1159,6 @@ static struct snd_soc_acpi_codecs skl_codecs = { .codecs = {"10508825"} }; -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; @@ -1208,65 +1188,6 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) - .drv_name = "kblr_alc298s_i2s", -#else - .drv_name = "kbl_alc286s_i2s", -#endif - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", From 573e9a5ada4b13f8c502c14dfc0812ee665a7197 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:25:29 +0200 Subject: [PATCH 0283/1276] ASoC: Intel: Move skl machine drv tables to common directory. Align with machine drv tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to skl-specific data only. Change-Id: Ia7c823611f6e0f14a94447399cdcafe9aeb5e51e Depends-On: I25846b83d6c791c7c7dae25766a06beddd686277 Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- sound/soc/intel/skylake/skl.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f661ffc4a33a..815fa5541444 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1154,40 +1154,10 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", From a417f45348b5a615b71e00c4232c72a672ce550e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:15:45 +0200 Subject: [PATCH 0284/1276] ASoC: Intel: Move glk machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to glk-specific data only. Change-Id: I3c2c0ee4eeffddb495dfd765788b9cc861e7741e Depends-On: Ia7c823611f6e0f14a94447399cdcafe9aeb5e51e Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- sound/soc/intel/skylake/skl.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 815fa5541444..eb26714a128a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1158,15 +1158,6 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { #if !IS_ENABLED(CONFIG_SND_SOC_RT700) { From b1c500cebeb78a28886a952d0bb0dfca5597c733 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:41:13 +0200 Subject: [PATCH 0285/1276] ASoC: Intel: Move icl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to icl-specific data only. Change-Id: I67ead17edb1bc469894a053e7d85b67409eeb2e6 Depends-On: I3c2c0ee4eeffddb495dfd765788b9cc861e7741e Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- .../intel/common/soc-acpi-intel-icl-match.c | 46 +++++++++++++++++++ sound/soc/intel/skylake/skl.c | 29 +----------- 4 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index bb1d24b703fb..54096616fbec 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -24,5 +24,6 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 915a34cdc8ac..fd52419f1c81 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 000000000000..94a7e5603102 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { +#if IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "dummy", + .drv_name = "icl_rt700", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, +#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) + { + .id = "dummy", + .drv_name = "icl_wm8281", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, +#else + { + .id = "dummy", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, + +#endif + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index eb26714a128a..efa24a9e58fe 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1176,33 +1176,6 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_icl_devdata[] = { -#if IS_ENABLED(CONFIG_SND_SOC_RT700) - { - .id = "dummy", - .drv_name = "icl_rt700", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, -#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) - { - .id = "dummy", - .drv_name = "icl_wm8281", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, -#else - { - .id = "dummy", - .drv_name = "icl_rt274", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, - -#endif - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ @@ -1222,7 +1195,7 @@ static const struct pci_device_id skl_ids[] = { .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, /* ICL */ { PCI_DEVICE(0x8086, 0x34c8), - .driver_data = (unsigned long)&sst_icl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_icl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 2a2a069b4f7d3a0ee1cd5112785e5457f3a43a80 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:34:29 +0200 Subject: [PATCH 0286/1276] ASoC: Intel: Move cnl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to cnl-specific data only. Change-Id: I9bccf434c9a1d7949e88d0ab363e28c1bd193e75 Depends-On: I67ead17edb1bc469894a053e7d85b67409eeb2e6 Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-cnl-match.c | 8 +++++++ sound/soc/intel/skylake/skl.c | 22 ------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937..9abc557b7b54 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -15,6 +15,7 @@ static struct skl_machine_pdata cnl_pdata = { }; struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_RT700) { .id = "INT34C2", .drv_name = "cnl_rt274", @@ -24,6 +25,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", .asoc_plat_name = "0000:00:1f.3", }, +#else + { + .drv_name = "cnl_rt700", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#endif {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index efa24a9e58fe..31d214a0db85 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1154,28 +1154,6 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { -#if !IS_ENABLED(CONFIG_SND_SOC_RT700) - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, -#else - { - .drv_name = "cnl_rt700", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, -#endif - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ From 7cd26ab7d5591d7bf339ee28e5735b67b4d4781a Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 4 Jan 2018 19:40:48 +0530 Subject: [PATCH 0287/1276] ASoC: Intel: Skylake: validate the downloaded firmware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print the firmware version downloaded and verify if it is equal or greater than the minimum supported version for the existing driver. Report error and exit driver initialization if downloaded firmware is not compatible with current driver. Change-Id: I6e0f046d88123c9c216ac9a1c9054f37661e1bfe Signed-off-by: Prakash, Divya1 Signed-off-by: Mohit Sinha Signed-off-by: Pardha Saradhi K Signed-off-by: Amadeusz SÅ‚awiÅ„ski Reviewed-on: Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/intel/skylake/bxt-sst.c | 17 ++++++++++++++-- sound/soc/intel/skylake/cnl-sst-dsp.h | 2 +- sound/soc/intel/skylake/cnl-sst.c | 11 ++++++++-- sound/soc/intel/skylake/skl-messages.c | 22 +++++++++++--------- sound/soc/intel/skylake/skl-sst-dsp.h | 27 +++++++++++++++++++++++-- sound/soc/intel/skylake/skl-sst-ipc.h | 7 ------- sound/soc/intel/skylake/skl-sst-utils.c | 25 +++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst.c | 18 +++++++++++++++-- sound/soc/intel/skylake/skl.h | 14 ------------- 9 files changed, 104 insertions(+), 39 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index bac77f8fc0d4..76266d3945e7 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -311,6 +311,16 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; } else { + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "FW version query failed\n"); + goto sst_load_base_firmware_failed; + } + + ret = skl_validate_fw_version(skl); + if (ret < 0) + goto sst_load_base_firmware_failed; + ret = 0; skl->fw_loaded = true; } @@ -632,21 +642,24 @@ static struct sst_dsp_device skl_dev = { }; int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {BXT_ADSP_WP_DSP0, BXT_ADSP_WP_DSP1}; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; + skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->fw_ops = bxt_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 5f0653b36308..7bc24ee5eecc 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -117,7 +117,7 @@ bool cnl_ipc_int_status(struct sst_dsp *ctx); void cnl_ipc_free(struct sst_generic_ipc *ipc); int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx); void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 19398e200cb0..52565423c83e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -273,6 +273,10 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) goto load_base_firmware_failed; } + ret = skl_validate_fw_version(cnl); + if (ret < 0) + goto load_base_firmware_failed; + fw_property = cnl->fw_property; if (fw_property.memory_reclaimed <= 0) { dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", @@ -725,22 +729,25 @@ static void skl_unregister_sdw_masters(struct skl_sst *ctx) } int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *cnl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {CNL_ADSP_WP_DSP0, CNL_ADSP_WP_DSP1, CNL_ADSP_WP_DSP2, CNL_ADSP_WP_DSP3}; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &cnl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } cnl = *dsp; + cnl->dsp_ops = dsp_ops; sst = cnl->dsp; sst->fw_ops = cnl_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index c01875a2a61d..f66ae9d702bc 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -277,7 +277,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup + .cleanup = skl_sst_dsp_cleanup, + .min_fw_ver = {9, 21, 0, 3173} }, { .id = 0x9d71, @@ -285,7 +286,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup + .cleanup = skl_sst_dsp_cleanup, + .min_fw_ver = {9, 21, 0, 3173} }, { .id = 0x5a98, @@ -294,7 +296,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {9, 22, 1, 3132} }, { .id = 0x3198, @@ -303,7 +306,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {9, 22, 1, 3366} }, { .id = 0x9dc8, @@ -312,7 +316,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {10, 23, 0, 1233} }, { .id = 0x34c8, @@ -321,7 +326,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {10, 23, 0, 1233} }, }; @@ -1283,14 +1289,12 @@ int skl_init_dsp(struct skl *skl) goto unmap_mmio; } - loader_ops = ops->loader_ops(); - ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, + ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, ops, &skl->skl_sst, &cnl_sdw_bra_ops); if (ret < 0) goto unmap_mmio; - skl->skl_sst->dsp_ops = ops; cores = &skl->skl_sst->cores; cores->count = ops->num_cores; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index dc793d503115..fa3a47183916 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -178,6 +178,13 @@ enum skl_hw_info_type { SKL_EBB_SIZE_BYTES, }; +struct skl_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -193,6 +200,19 @@ enum skl_dsp_d0i3_states { SKL_DSP_D0I3_STREAMING = 1, }; +struct skl_dsp_ops { + int id; + unsigned int num_cores; + struct skl_dsp_loader_ops (*loader_ops)(void); + struct skl_fw_version min_fw_ver; + int (*init)(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, + struct skl_sst **skl_sst, void *ptr); + int (*init_fw)(struct device *dev, struct skl_sst *ctx); + void (*cleanup)(struct device *dev, struct skl_sst *ctx); + void (*do_recovery)(struct skl *skl); +}; + struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ @@ -294,10 +314,10 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); @@ -331,6 +351,8 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); int skl_get_hardware_configuration(struct sst_dsp *ctx); +int skl_validate_fw_version(struct skl_sst *skl); + int bxt_set_dsp_D0i0(struct sst_dsp *ctx); int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); @@ -344,4 +366,5 @@ void skl_module_sysfs_exit(struct skl_sst *ctx); int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, struct skl_notify_data *notify_data); +const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ee32aaa68490..4eef4a386996 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -152,13 +152,6 @@ struct bra_conf { struct skl_pipe *cp_pipe; }; -struct skl_fw_version { - u16 major; - u16 minor; - u16 hotfix; - u16 build; -}; - struct skl_dma_buff_config { u32 min_size_bytes; u32 max_size_bytes; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 7c867426b39b..a495cea49b05 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -1246,3 +1246,28 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) return ret; } EXPORT_SYMBOL_GPL(skl_module_sysfs_init); + +int skl_validate_fw_version(struct skl_sst *skl) +{ + struct skl_fw_version *fw_version = &skl->fw_property.version; + const struct skl_dsp_ops *ops = skl->dsp_ops; + + dev_info(skl->dev, "ADSP FW Version: %d.%d.%d.%d\n", + fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build); + + + if (ops->min_fw_ver.major == fw_version->major && + ops->min_fw_ver.minor == fw_version->minor && + ops->min_fw_ver.hotfix == fw_version->hotfix && + ops->min_fw_ver.build <= fw_version->build) + return 0; + + dev_err(skl->dev, "Incorrect ADSP FW version = %d.%d.%d.%d, minimum supported FW version = %d.%d.%d.%d\n", + fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build, + ops->min_fw_ver.major, ops->min_fw_ver.minor, + ops->min_fw_ver.hotfix, ops->min_fw_ver.build); + + return -EINVAL; +} diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 277e972eb858..77cf3f2ba95f 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -152,6 +152,17 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto transfer_firmware_failed; } + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "FW version query failed\n"); + goto skl_load_base_firmware_failed; + } + + ret = skl_validate_fw_version(skl); + if (ret < 0) { + ret = -EIO; + goto skl_load_base_firmware_failed; + } dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl->fw_loaded = true; } @@ -528,20 +539,23 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; + skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 01053c4cdf6e..d00a8b19d054 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -188,19 +188,6 @@ struct skl_machine_pdata { bool use_tplg_pcm; /* use dais and dai links from topology */ }; -struct skl_dsp_ops { - int id; - unsigned int num_cores; - struct skl_dsp_loader_ops (*loader_ops)(void); - int (*init)(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, - struct skl_dsp_loader_ops loader_ops, - struct skl_sst **skl_sst, void *ptr); - int (*init_fw)(struct device *dev, struct skl_sst *ctx); - void (*cleanup)(struct device *dev, struct skl_sst *ctx); - void (*do_recovery)(struct skl *skl); -}; - int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); @@ -223,7 +210,6 @@ int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); -const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); void skl_update_d0i3c(struct device *dev, bool enable); int skl_nhlt_create_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl); From 0ed4b532a20f225fae879c0efd9790d6697e147f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 3 Sep 2018 14:42:21 +0200 Subject: [PATCH 0288/1276] ASoC: Intel: Replace hdac_ext_bus usage with hdac_bus. This patch updates CoE against following change: ALSA: hdac: Remove usage of struct hdac_ext_bus and use hda_bus instead. This patch also fixes NULL dereference panic within soc_dai_hw_params introduced by lastedsoc-framework changes for CoE. Change-Id: I06600e84714863ec37f551ed5373830dec10c29c Signed-off-by: Cezary Rojewski --- include/sound/hdaudio_ext.h | 2 +- sound/hda/ext/hdac_ext_stream.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-compress.c | 17 ++++++++--------- sound/soc/intel/skylake/skl-compress.h | 6 +++--- sound/soc/intel/skylake/skl-messages.c | 10 ++++------ sound/soc/intel/skylake/skl-nhlt.c | 6 +++--- sound/soc/intel/skylake/skl-pcm.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-probe.c | 26 ++++++++++++-------------- sound/soc/intel/skylake/skl-topology.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-topology.h | 4 ++-- sound/soc/intel/skylake/skl.c | 17 ++++++++++------- sound/soc/soc-pcm.c | 2 +- 12 files changed, 71 insertions(+), 73 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 62181677f009..5ebf57fa778d 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -90,7 +90,7 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); struct hdac_ext_stream * -hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_compr_assign(struct hdac_bus *bus, struct snd_compr_stream *substream, int direction); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 492f85841ac0..fa7b80fcba9d 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -552,21 +552,21 @@ int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); + struct hdac_ext_stream * -hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_compr_assign(struct hdac_bus *bus, struct snd_compr_stream *substream, int direction) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -575,19 +575,19 @@ hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, if (!stream->opened) { if (!hstream->decoupled) - snd_hdac_ext_stream_decouple(ebus, + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; res->hstream.stream = substream; - spin_unlock_irq(&hbus->reg_lock); - dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + spin_unlock_irq(&bus->reg_lock); + dev_dbg(bus->dev, "Stream tag = %d, index = %d\n", res->hstream.stream_tag, res->hstream.index); } return res; diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index 67f2b78812b2..d346b08ad044 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -27,13 +27,12 @@ #include "skl.h" #include "skl-compress.h" -struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) +struct hdac_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) { struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); struct hdac_stream *hstream = hdac_stream(stream); - struct hdac_bus *bus = hstream->bus; - return hbus_to_ebus(bus); + return hstream->bus; } void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, @@ -55,10 +54,10 @@ void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, } int skl_compr_malloc_pages(struct snd_compr_stream *substream, - struct hdac_ext_bus *ebus, size_t size) + struct hdac_bus *bus, size_t size) { struct snd_dma_buffer *dmab = NULL; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); if (!dmab) @@ -69,7 +68,7 @@ int skl_compr_malloc_pages(struct snd_compr_stream *substream, if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, size, dmab) < 0) { - dev_err(ebus_to_hbus(ebus)->dev, + dev_err(bus->dev, "Error in snd_dma_alloc_pages\n"); kfree(dmab); return -ENOMEM; @@ -79,7 +78,7 @@ int skl_compr_malloc_pages(struct snd_compr_stream *substream, return 1; } -int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, +int skl_substream_alloc_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream, size_t size) { @@ -90,10 +89,10 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; - ret = skl_compr_malloc_pages(substream, ebus, size); + ret = skl_compr_malloc_pages(substream, bus, size); if (ret < 0) return ret; - ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); return ret; } diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index abfff2d27f14..4b7f9d58e513 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -20,12 +20,12 @@ */ #ifndef __SKL_COMPRESS_H__ #define __SKL_COMPRESS_H__ -struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); +struct hdac_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, struct snd_dma_buffer *bufp, size_t size); int skl_compr_malloc_pages(struct snd_compr_stream *substream, - struct hdac_ext_bus *ebus, size_t size); -int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct hdac_bus *bus, size_t size); +int skl_substream_alloc_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream, size_t size); int skl_compr_free_pages(struct snd_compr_stream *substream); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f66ae9d702bc..74833f0cddfb 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -380,8 +380,7 @@ void skl_do_recovery(struct skl *skl) const struct skl_dsp_ops *ops; struct snd_soc_card *card; struct hdac_stream *azx_dev; - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct snd_pcm_substream *substream = NULL; struct hdac_ext_stream *stream; @@ -400,7 +399,7 @@ void skl_do_recovery(struct skl *skl) substream = azx_dev->substream; stream = stream_to_hdac_ext_stream(azx_dev); snd_hdac_ext_stream_release(stream, - skl_get_host_stream_type(ebus)); + skl_get_host_stream_type(bus)); } break; } @@ -409,8 +408,8 @@ void skl_do_recovery(struct skl *skl) if (ops->init_fw(soc_component->dev, skl->skl_sst) < 0) dev_err(skl->skl_sst->dev, "Recovery failed\n"); if (substream != NULL) { - stream = snd_hdac_ext_stream_assign(ebus, substream, - skl_get_host_stream_type(ebus)); + stream = snd_hdac_ext_stream_assign(bus, substream, + skl_get_host_stream_type(bus)); } snd_soc_resume(card->dev); skl->skl_sst->dsp->is_recovery = false; @@ -1266,7 +1265,6 @@ int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_bus *bus = skl_to_bus(skl); - struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; struct skl_dsp_cores *cores; diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 284e765eebad..73d6c9841d91 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -156,15 +156,15 @@ skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type) { struct nhlt_specific_cfg *cfg = NULL; - struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = &skl->hbus; /* update the blob based on virtual bus_id*/ if (!skl->nhlt_override) { - dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); + dev_warn(bus->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); cfg = skl_get_ep_blob(skl, instance, link_type, s_fmt, num_ch, s_rate, dir, dev_type); } else { - dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from Debugfs!!\n"); + dev_warn(bus->dev, "Querying NHLT blob from Debugfs!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, instance, dir); } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 6da6b492b70b..fe4cd70700f7 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -95,7 +95,7 @@ static int skl_substream_alloc_pages(struct hdac_bus *bus, ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) return ret; - ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); return ret; } @@ -118,7 +118,7 @@ static void skl_set_pcm_constrains(struct hdac_bus *bus, 20, 178000000); } -static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) { if (bus->ppcap) return HDAC_EXT_STREAM_TYPE_HOST; @@ -212,7 +212,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) runtime = hdac_stream(stream)->substream->runtime; /* enable SPIB if no_rewinds flag is set */ if (runtime->no_rewinds) - snd_hdac_ext_stream_spbcap_enable(ebus, 1, hstream->index); + snd_hdac_ext_stream_spbcap_enable(bus, 1, hstream->index); hdac_stream(stream)->prepared = 1; @@ -442,8 +442,8 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (runtime->no_rewinds) { - snd_hdac_ext_stream_set_spib(ebus, stream, 0); - snd_hdac_ext_stream_spbcap_enable(ebus, 0, hstream->index); + snd_hdac_ext_stream_set_spib(bus, stream, 0); + snd_hdac_ext_stream_spbcap_enable(bus, 0, hstream->index); } if (mconfig) { ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); @@ -863,8 +863,8 @@ static struct skl_sst *skl_get_sst_compr(struct snd_compr_stream *stream) { struct snd_soc_pcm_runtime *rtd = stream->private_data; struct snd_soc_dai *dai = rtd->cpu_dai; - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); + struct skl *skl = bus_to_skl(bus); struct skl_sst *sst = skl->skl_sst; return sst; @@ -1589,7 +1589,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, /* update SPIB register with appl position */ static int skl_platform_ack(struct snd_pcm_substream *substream) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *estream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; ssize_t appl_pos, buf_size; @@ -1606,7 +1606,7 @@ static int skl_platform_ack(struct snd_pcm_substream *substream) /* Allowable value for SPIB is 1 byte to max buffer size */ spib = (spib == 0) ? buf_size : spib; - snd_hdac_ext_stream_set_spib(ebus, estream, spib); + snd_hdac_ext_stream_set_spib(bus, estream, spib); return 0; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 2d9d0ea6c907..166ffa1e0d3e 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -87,7 +87,7 @@ static int set_injector_stream(struct hdac_ext_stream *stream, int skl_probe_compr_open(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = NULL; struct snd_compr_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); @@ -107,7 +107,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, * correct substream pointer later when open is indeed for * extractor. */ - pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, + pconfig->estream = hdac_ext_host_stream_compr_assign(bus, NULL, SND_COMPRESS_CAPTURE); if (!pconfig->estream) { @@ -120,7 +120,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, } if (substream->direction == SND_COMPRESS_PLAYBACK) { - stream = hdac_ext_host_stream_compr_assign(ebus, substream, + stream = hdac_ext_host_stream_compr_assign(bus, substream, SND_COMPRESS_PLAYBACK); if (stream == NULL) { if ((pconfig->i_refc + pconfig->e_refc) == 0) @@ -154,7 +154,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); struct snd_compr_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); @@ -172,7 +172,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, return 0; } - ret = skl_substream_alloc_compr_pages(ebus, substream, + ret = skl_substream_alloc_compr_pages(bus, substream, runtime->fragments*runtime->fragment_size); if (ret < 0) return ret; @@ -215,7 +215,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, } #if USE_SPIB - snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); + snd_hdac_ext_stream_spbcap_enable(bus, 1, hdac_stream(stream)->index); #endif return 0; } @@ -224,7 +224,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; struct skl_module_cfg *mconfig = pconfig->w->priv; @@ -233,7 +233,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); #if USE_SPIB - snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); + snd_hdac_ext_stream_spbcap_enable(bus, 0, hdac_stream(stream)->index); #endif if ((pconfig->i_refc + pconfig->e_refc) == 0) goto probe_uninit; @@ -282,7 +282,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; - skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + skl_substream_free_compr_pages(bus, substream); /* Release the particular injector/extractor stream getting closed */ snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); @@ -293,8 +293,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); u64 __maybe_unused new_spib_pos; struct snd_compr_runtime *runtime = substream->runtime; u64 spib_pos = div64_u64(runtime->total_bytes_available, @@ -387,7 +386,7 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, #if USE_SPIB spib_pos = (offset + retval)%stream->runtime->dma_bytes; - snd_hdac_ext_stream_set_spib(ebus, estream, spib_pos); + snd_hdac_ext_stream_set_spib(bus, estream, spib_pos); #endif return retval; @@ -397,8 +396,7 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = get_bus_compr_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_compr_ctx(substream); struct hdac_ext_stream *stream; struct hdac_stream *hstr; int start; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c80ad4558ba6..9b6e3bfc464d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2051,8 +2051,8 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); ucontrol->value.integer.value[0] = get_dsp_log_priority(skl); @@ -2063,8 +2063,8 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); update_dsp_log_priority(ucontrol->value.integer.value[0], skl); @@ -2260,8 +2260,8 @@ static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; struct skl_pipe *pipe = NULL; u32 *pipe_id; @@ -2284,8 +2284,8 @@ static int skl_tplg_multi_config_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; struct skl_pipe *pipe = NULL; struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; @@ -4378,7 +4378,7 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - int ret; + int i, ret; struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index c0ef5e78dbfa..da3c8d19033b 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -553,7 +553,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_component *component, - struct hdac_bus *ebus); + struct hdac_bus *bus); struct skl_module_cfg *skl_tplg_fe_get_cpr_module( struct snd_soc_dai *dai, int stream); int skl_tplg_update_pipe_params(struct device *dev, @@ -617,7 +617,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus); +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus); int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 31d214a0db85..8d7305133424 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -42,6 +42,10 @@ #include "skl-sst-ipc.h" #include "skl-topology.h" +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static struct skl_machine_pdata skl_dmic_data; +#endif + /* * initialize the PCI registers */ @@ -211,9 +215,8 @@ static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) * skl_dum_set - Set the DUM bit in EM2 register to fix the IP bug * of incorrect postion reporting for capture stream. */ -static void skl_dum_set(struct hdac_ext_bus *ebus) +static void skl_dum_set(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); u32 reg; u8 val; @@ -248,7 +251,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; - struct skl *skl = bus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); u32 status; u32 mask, int_enable; int ret = IRQ_NONE; @@ -304,7 +307,7 @@ static irqreturn_t skl_threaded_handler(int irq, void *dev_id) snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update); /* Re-enable stream interrupts */ - mask = (0x1 << ebus->num_streams) - 1; + mask = (0x1 << bus->num_streams) - 1; spin_lock_irqsave(&bus->reg_lock, flags); int_enable = snd_hdac_chip_readl(bus, INTCTL); snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); @@ -875,7 +878,7 @@ static int skl_init_recovery(struct skl *skl) monitor->interval = SKL_MIN_TIME_INTERVAL; monitor->intervals = devm_kzalloc(&skl->pci->dev, - skl->ebus.num_streams * sizeof(u32), + skl->hbus.num_streams * sizeof(u32), GFP_KERNEL); if (!monitor->intervals) return -ENOMEM; @@ -982,7 +985,7 @@ static int skl_first_init(struct hdac_bus *bus) /* initialize chip */ skl_init_pci(skl); - skl_dum_set(ebus); + skl_dum_set(bus); return skl_init_chip(bus, true); } @@ -1043,7 +1046,7 @@ static int skl_probe(struct pci_dev *pci, nhlt_continue: #endif - pci_set_drvdata(skl->pci, ebus); + pci_set_drvdata(skl->pci, bus); #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index afcb2cfaf551..2a71d8e04f5b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -863,7 +863,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, int ret; /* perform any topology hw_params fixups before DAI */ - if (rtd->dai_link->be_hw_params_fixup) { + if (rtd && rtd->dai_link->be_hw_params_fixup) { ret = rtd->dai_link->be_hw_params_fixup(rtd, params); if (ret < 0) { dev_err(rtd->dev, From 7facf6265344d0633437510da8d6a59f0b3f7215 Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Fri, 21 Sep 2018 16:09:53 +0200 Subject: [PATCH 0289/1276] REVERTME: Fix no audio output after resume from S3 Ignoring returned code from tdf8532_stop_play allows continue playback after S3. Tracked-On: Change-Id: Ied077210ce6508a79b12eedc0ac22ea86d5b643a Signed-off-by: Gustaw Lewandowski Reviewed-on: Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/codecs/tdf8532.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 86b7430f4c88..f306b597b42d 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -268,7 +268,9 @@ static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - ret = tdf8532_stop_play(tdf8532); + /* WA on unexpected codec down during S3 + SNDRV_PCM_TRIGGER_STOP fails so skip set ret */ + tdf8532_stop_play(tdf8532); break; } From 8e80b41c069714ed08a2903adea9b60579f5aae4 Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Mon, 17 Sep 2018 11:27:02 +0200 Subject: [PATCH 0290/1276] REVERTME: Increase tdf8532 timeout and number of retries for audio ROM This is WA for delays in ROM init and ACK timeout for tdf8532 codec. New values at this patch are set to very high so this patch should be reverted after RC will be know or new reasonable values be established. Tracked-On: Change-Id: I4e8145a8711ef9ff737a80028f9e22d9a991f4b4 Signed-off-by: Gustaw Lewandowski Reviewed-on: Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/codecs/tdf8532.h | 2 +- sound/soc/intel/skylake/bxt-sst.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h index 6e3f2c147eac..39353a04b3ba 100644 --- a/sound/soc/codecs/tdf8532.h +++ b/sound/soc/codecs/tdf8532.h @@ -16,7 +16,7 @@ #ifndef __TDF8532_H_ #define __TDF8532_H_ -#define ACK_TIMEOUT 300 +#define ACK_TIMEOUT 10000 #define CHNL_MAX 5 diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 76266d3945e7..f66aae4c229f 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -59,7 +59,7 @@ /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 -#define BXT_FW_INIT_RETRY 3 +#define BXT_FW_INIT_RETRY 100 #define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) From 53cc3b890c544971a21aeadc526a843f3b446284 Mon Sep 17 00:00:00 2001 From: "he, bo" Date: Sat, 29 Sep 2018 14:18:12 +0800 Subject: [PATCH 0291/1276] fix the invalid double free in tdf8532 i2c read error tdf8532_single_read() is defined as return uint8_t, the error ret is defined as int, when it run into the error case, the tdf8532_single_read() won't return the error, it will cause the double free. Change-Id: I6be1438472f5f2c553e80703f761bd28d0e16d3e Tracked-On: Signed-off-by: he, bo Reviewed-on: Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/codecs/tdf8532.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index f306b597b42d..02e63f95512b 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -83,7 +83,7 @@ static int __tdf8532_single_write(struct tdf8532_priv *dev_data, } -static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, +static int tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout) { uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; From ac0f9ae9d9b4fa8b732d19110a0461115b9cc637 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Thu, 4 Oct 2018 12:17:27 +0200 Subject: [PATCH 0292/1276] ASoC: Intel: Skylake: Restore static SSP5 BE declaration. Skylake driver declares SSP Pin BEs dynamically, based on NHLT table data. Number of endpoints extracted from said table, present for gp-mrb, differs from one expected by topology. In consequence, SSP5 Pin cpu dai is not being registered, thus routing for TestPin FE fails. Static SSP5 Pin BE declaration is restored in order to make TestPin functional again. Change-Id: I6180117e4bf37c3fe09068ababf855e9b1d2902d Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/skl-pcm.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index fe4cd70700f7..2aedbc266832 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1178,6 +1178,29 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { /* BE cpu dais and compress dais*/ static struct snd_soc_dai_driver skl_platform_dai[] = { +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) +{ + .name = "SSP5 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp5 Tx", + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "ssp5 Rx", + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +#endif { .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, From 670314c73d338e451f188d7ca308f1f831ce9b05 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 19 Oct 2018 10:59:29 +0200 Subject: [PATCH 0293/1276] Revert "ASoC: Intel: Skylake: validate the downloaded firmware" This reverts commit 5421814ae83588a501df54d5d4bde39b059756fa. Change-Id: I58ead4ce312cf073c54178f1f91369f711f21cc3 Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/bxt-sst.c | 17 ++-------------- sound/soc/intel/skylake/cnl-sst-dsp.h | 2 +- sound/soc/intel/skylake/cnl-sst.c | 11 ++-------- sound/soc/intel/skylake/skl-messages.c | 23 +++++++++------------ sound/soc/intel/skylake/skl-sst-dsp.h | 27 ++----------------------- sound/soc/intel/skylake/skl-sst-ipc.h | 7 +++++++ sound/soc/intel/skylake/skl-sst-utils.c | 25 ----------------------- sound/soc/intel/skylake/skl-sst.c | 18 ++--------------- sound/soc/intel/skylake/skl.h | 14 +++++++++++++ 9 files changed, 40 insertions(+), 104 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index f66aae4c229f..cd7fbabef8ee 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -311,16 +311,6 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; } else { - ret = skl_get_firmware_configuration(ctx); - if (ret < 0) { - dev_err(ctx->dev, "FW version query failed\n"); - goto sst_load_base_firmware_failed; - } - - ret = skl_validate_fw_version(skl); - if (ret < 0) - goto sst_load_base_firmware_failed; - ret = 0; skl->fw_loaded = true; } @@ -642,24 +632,21 @@ static struct sst_dsp_device skl_dev = { }; int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; - struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {BXT_ADSP_WP_DSP0, BXT_ADSP_WP_DSP1}; int ret; - loader_ops = dsp_ops->loader_ops(); - ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); + ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; - skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->fw_ops = bxt_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 7bc24ee5eecc..5f0653b36308 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -117,7 +117,7 @@ bool cnl_ipc_int_status(struct sst_dsp *ctx); void cnl_ipc_free(struct sst_generic_ipc *ipc); int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx); void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 52565423c83e..19398e200cb0 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -273,10 +273,6 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) goto load_base_firmware_failed; } - ret = skl_validate_fw_version(cnl); - if (ret < 0) - goto load_base_firmware_failed; - fw_property = cnl->fw_property; if (fw_property.memory_reclaimed <= 0) { dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", @@ -729,25 +725,22 @@ static void skl_unregister_sdw_masters(struct skl_sst *ctx) } int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *cnl; struct sst_dsp *sst; - struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {CNL_ADSP_WP_DSP0, CNL_ADSP_WP_DSP1, CNL_ADSP_WP_DSP2, CNL_ADSP_WP_DSP3}; int ret; - loader_ops = dsp_ops->loader_ops(); - ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &cnl_dev); + ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } cnl = *dsp; - cnl->dsp_ops = dsp_ops; sst = cnl->dsp; sst->fw_ops = cnl_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 74833f0cddfb..a1d9c842646a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -277,8 +277,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup, - .min_fw_ver = {9, 21, 0, 3173} + .cleanup = skl_sst_dsp_cleanup }, { .id = 0x9d71, @@ -286,8 +285,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup, - .min_fw_ver = {9, 21, 0, 3173} + .cleanup = skl_sst_dsp_cleanup }, { .id = 0x5a98, @@ -296,8 +294,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery, - .min_fw_ver = {9, 22, 1, 3132} + .do_recovery = skl_do_recovery }, { .id = 0x3198, @@ -306,8 +303,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery, - .min_fw_ver = {9, 22, 1, 3366} + .do_recovery = skl_do_recovery }, { .id = 0x9dc8, @@ -316,8 +312,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery, - .min_fw_ver = {10, 23, 0, 1233} + .do_recovery = skl_do_recovery }, { .id = 0x34c8, @@ -326,8 +321,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery, - .min_fw_ver = {10, 23, 0, 1233} + .do_recovery = skl_do_recovery }, }; @@ -1265,6 +1259,7 @@ int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_bus *bus = skl_to_bus(skl); + struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; struct skl_dsp_cores *cores; @@ -1287,12 +1282,14 @@ int skl_init_dsp(struct skl *skl) goto unmap_mmio; } - ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, ops, + loader_ops = ops->loader_ops(); + ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, &skl->skl_sst, &cnl_sdw_bra_ops); if (ret < 0) goto unmap_mmio; + skl->skl_sst->dsp_ops = ops; cores = &skl->skl_sst->cores; cores->count = ops->num_cores; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index fa3a47183916..dc793d503115 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -178,13 +178,6 @@ enum skl_hw_info_type { SKL_EBB_SIZE_BYTES, }; -struct skl_fw_version { - u16 major; - u16 minor; - u16 hotfix; - u16 build; -}; - /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -200,19 +193,6 @@ enum skl_dsp_d0i3_states { SKL_DSP_D0I3_STREAMING = 1, }; -struct skl_dsp_ops { - int id; - unsigned int num_cores; - struct skl_dsp_loader_ops (*loader_ops)(void); - struct skl_fw_version min_fw_ver; - int (*init)(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, - struct skl_sst **skl_sst, void *ptr); - int (*init_fw)(struct device *dev, struct skl_sst *ctx); - void (*cleanup)(struct device *dev, struct skl_sst *ctx); - void (*do_recovery)(struct skl *skl); -}; - struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ @@ -314,10 +294,10 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr); int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); @@ -351,8 +331,6 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); int skl_get_hardware_configuration(struct sst_dsp *ctx); -int skl_validate_fw_version(struct skl_sst *skl); - int bxt_set_dsp_D0i0(struct sst_dsp *ctx); int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); @@ -366,5 +344,4 @@ void skl_module_sysfs_exit(struct skl_sst *ctx); int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, struct skl_notify_data *notify_data); -const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 4eef4a386996..ee32aaa68490 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -152,6 +152,13 @@ struct bra_conf { struct skl_pipe *cp_pipe; }; +struct skl_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + struct skl_dma_buff_config { u32 min_size_bytes; u32 max_size_bytes; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index a495cea49b05..7c867426b39b 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -1246,28 +1246,3 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) return ret; } EXPORT_SYMBOL_GPL(skl_module_sysfs_init); - -int skl_validate_fw_version(struct skl_sst *skl) -{ - struct skl_fw_version *fw_version = &skl->fw_property.version; - const struct skl_dsp_ops *ops = skl->dsp_ops; - - dev_info(skl->dev, "ADSP FW Version: %d.%d.%d.%d\n", - fw_version->major, fw_version->minor, - fw_version->hotfix, fw_version->build); - - - if (ops->min_fw_ver.major == fw_version->major && - ops->min_fw_ver.minor == fw_version->minor && - ops->min_fw_ver.hotfix == fw_version->hotfix && - ops->min_fw_ver.build <= fw_version->build) - return 0; - - dev_err(skl->dev, "Incorrect ADSP FW version = %d.%d.%d.%d, minimum supported FW version = %d.%d.%d.%d\n", - fw_version->major, fw_version->minor, - fw_version->hotfix, fw_version->build, - ops->min_fw_ver.major, ops->min_fw_ver.minor, - ops->min_fw_ver.hotfix, ops->min_fw_ver.build); - - return -EINVAL; -} diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 77cf3f2ba95f..277e972eb858 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -152,17 +152,6 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto transfer_firmware_failed; } - ret = skl_get_firmware_configuration(ctx); - if (ret < 0) { - dev_err(ctx->dev, "FW version query failed\n"); - goto skl_load_base_firmware_failed; - } - - ret = skl_validate_fw_version(skl); - if (ret < 0) { - ret = -EIO; - goto skl_load_base_firmware_failed; - } dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl->fw_loaded = true; } @@ -539,23 +528,20 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, const struct skl_dsp_ops *dsp_ops, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; - struct skl_dsp_loader_ops loader_ops; int ret; - loader_ops = dsp_ops->loader_ops(); - ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); + ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; - skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index d00a8b19d054..01053c4cdf6e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -188,6 +188,19 @@ struct skl_machine_pdata { bool use_tplg_pcm; /* use dais and dai links from topology */ }; +struct skl_dsp_ops { + int id; + unsigned int num_cores; + struct skl_dsp_loader_ops (*loader_ops)(void); + int (*init)(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, + struct skl_dsp_loader_ops loader_ops, + struct skl_sst **skl_sst, void *ptr); + int (*init_fw)(struct device *dev, struct skl_sst *ctx); + void (*cleanup)(struct device *dev, struct skl_sst *ctx); + void (*do_recovery)(struct skl *skl); +}; + int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); @@ -210,6 +223,7 @@ int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); +const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); void skl_update_d0i3c(struct device *dev, bool enable); int skl_nhlt_create_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl); From ca6e6f4c372d50068d2ecfbfb1d8e16dbbe2c0bc Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 22 Nov 2017 17:28:42 +0530 Subject: [PATCH 0294/1276] ASoC: Intel: BXT: Print version details of ADSP Base FW Print the major, minor, hotfix and build number of the ADSP FW binary that is loaded with the current software. Change-Id: I38fd347ffefb4eca857227b34d84bf90fc3a102f Signed-off-by: Pardha Saradhi K (cherry picked from commit eec2300236d4e0a86a6229716bb622d4e4df738d) Signed-off-by: Tomasz Medrek Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/skl-sst-utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 7c867426b39b..02583e42f687 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -526,6 +526,10 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, adsp_hdr = (struct adsp_fw_hdr *)(buf + offset); + dev_info(ctx->dev, "ADSP FW Version: %d.%d.%d.%d\n", + adsp_hdr->major, adsp_hdr->minor, + adsp_hdr->hotfix, adsp_hdr->build); + /* check 1st module entry is in file */ safe_file += adsp_hdr->len + sizeof(*mod_entry); if (stripped_fw.size <= safe_file) { From a006329ac4baa24102655d58837851e07d384af1 Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Fri, 12 Oct 2018 10:24:41 +0200 Subject: [PATCH 0295/1276] ASoC: tdf8532: add crit sections Stop of playback caused that entire tdf8532 codec has been put into IDLE state despite current capture activity. Change-Id: I3756094e182cd44c225ba8224a840fc8c435625c Signed-off-by: Gustaw Lewandowski --- sound/soc/codecs/tdf8532.c | 93 ++++++++++++++++++++++---------------- sound/soc/codecs/tdf8532.h | 1 + 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 02e63f95512b..9fc73b69611c 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -21,8 +21,11 @@ #include #include #include +#include #include "tdf8532.h" +static DEFINE_MUTEX(tdf8532_lock); + static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, va_list valist, u8 *payload) { @@ -210,11 +213,12 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, static int tdf8532_start_play(struct tdf8532_priv *tdf8532) { - int ret; + int ret = 0; + mutex_lock(&tdf8532_lock); ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); if (ret < 0) - return ret; + goto out; ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, CHNL_MASK(tdf8532->channels)); @@ -222,22 +226,22 @@ static int tdf8532_start_play(struct tdf8532_priv *tdf8532) if (ret >= 0) ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); +out: + if (ret >= 0) + tdf8532->powered = 1; + mutex_unlock(&tdf8532_lock); return ret; } - static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) { - int ret; - + int ret = 0; ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, CHNL_MASK(tdf8532->channels)); if (ret < 0) goto out; - ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - if (ret < 0) - goto out; + tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); if (ret < 0) @@ -249,30 +253,17 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) return ret; } - -static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, +static int tdf8532_dai_trigger_pb(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { int ret = 0; struct snd_soc_component *component = dai->component; struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + dev_dbg(component->dev, "%s: cmd:%d substream:%d\n", __func__, cmd, + substream->stream); - dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: + if (cmd == SNDRV_PCM_TRIGGER_START && !tdf8532->powered) ret = tdf8532_start_play(tdf8532); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - /* WA on unexpected codec down during S3 - SNDRV_PCM_TRIGGER_STOP fails so skip set ret */ - tdf8532_stop_play(tdf8532); - break; - } return ret; } @@ -281,25 +272,48 @@ static int tdf8532_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + int ret; - dev_dbg(component->dev, "%s\n", __func__); - - if (mute) - return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, - CHNL_MASK(CHNL_MAX)); - else - return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, - CHNL_MASK(CHNL_MAX)); + dev_dbg(component->dev, "%s mute:%d\n", __func__, mute); + mutex_lock(&tdf8532_lock); + ret = tdf8532_amp_write(tdf8532, (mute)?SET_CHNL_MUTE:SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); + mutex_unlock(&tdf8532_lock); + return ret; } -static const struct snd_soc_dai_ops tdf8532_dai_ops = { - .trigger = tdf8532_dai_trigger, +static const struct snd_soc_dai_ops tdf8532_dai_ops_pb = { + .trigger = tdf8532_dai_trigger_pb, .digital_mute = tdf8532_mute, }; -static struct snd_soc_component_driver soc_component_tdf8532; +static int tdf8532_resume(struct snd_soc_component *component) +{ + int ret; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + u8 cur_state = STATE_NONE; + struct get_dev_status_repl *status_repl = NULL; + + dev_dbg(component->dev, "%s\n", __func__); + mutex_lock(&tdf8532_lock); + ret = tdf8532_get_state(tdf8532, &status_repl); + mutex_unlock(&tdf8532_lock); + if (ret < 0) + goto out; + cur_state = status_repl->state; + dev_dbg(component->dev, "%s cur_state:%d\n", __func__, cur_state); + if (cur_state < STATE_PLAY) + tdf8532_start_play(tdf8532); +out: + + return 0; +} + +static const struct snd_soc_component_driver soc_component_tdf8532 = { + .resume = tdf8532_resume, +}; -static struct snd_soc_dai_driver tdf8532_dai[] = { +static struct snd_soc_dai_driver tdf8532_dai_pb[] = { { .name = "tdf8532-hifi", .playback = { @@ -309,7 +323,7 @@ static struct snd_soc_dai_driver tdf8532_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .ops = &tdf8532_dai_ops, + .ops = &tdf8532_dai_ops_pb, } }; @@ -332,11 +346,12 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; + dev_data->powered = 0; i2c_set_clientdata(i2c, dev_data); ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, - tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + tdf8532_dai_pb, ARRAY_SIZE(tdf8532_dai_pb)); if (ret != 0) { dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); goto out; diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h index 39353a04b3ba..0f3540707580 100644 --- a/sound/soc/codecs/tdf8532.h +++ b/sound/soc/codecs/tdf8532.h @@ -96,6 +96,7 @@ struct tdf8532_priv { struct i2c_client *i2c; u8 channels; u8 pkt_id; + u8 powered:1; }; #endif From 45720a74851bb589cb4248736e51b911e2c66ec3 Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Sat, 13 Oct 2018 13:49:20 +0200 Subject: [PATCH 0296/1276] ASoC: Intel: bxtp: BXT_FW_INIT_RETRY set to 10 Change-Id: Ife347075f7708f7213451b7a21b19f6720630a12 Signed-off-by: Gustaw Lewandowski --- sound/soc/intel/skylake/bxt-sst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index cd7fbabef8ee..cde519de05dd 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -59,7 +59,7 @@ /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 -#define BXT_FW_INIT_RETRY 100 +#define BXT_FW_INIT_RETRY 10 #define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) From 0509180a8c2dff512b5dd383a4cfe13aed6a7203 Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Wed, 17 Oct 2018 12:14:39 +0200 Subject: [PATCH 0297/1276] ASoC: Intel: Skylake: Fix for lockup in sst_ipc_tx_message_wait Change-Id: I25540f4255813a4eb83a12a7495c4cb84afcb741 Signed-off-by: Michal Wasko Signed-off-by: Gustaw Lewandowski --- sound/soc/intel/skylake/skl-sst-ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 0e7b5ce64b9e..9f2621d7a535 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -708,8 +708,8 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, spin_lock_irqsave(&ipc->dsp->spinlock, flags); msg = skl_ipc_reply_get_msg(ipc, *ipc_header); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); if (msg == NULL) { - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); dev_dbg(ipc->dev, "ipc: rx list is empty\n"); return; } @@ -753,7 +753,7 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, } } - + spin_lock_irqsave(&ipc->dsp->spinlock, flags); sst_ipc_tx_msg_reply_complete(ipc, msg); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); } From 8cf33f1d9afce94e07bee0591e9f1b30af1761e1 Mon Sep 17 00:00:00 2001 From: Michal Wasko Date: Fri, 12 Oct 2018 14:23:43 +0200 Subject: [PATCH 0298/1276] ASoC: Intel: Skylake: Set DUM bit in EM2 register on skl resume Issue fix the IP bug of incorrect position reporting for capture stream Change-Id: I4b6c4ae28937ad4ee5df9d878b278efcfacc2695 Signed-off-by: Michal Wasko --- sound/soc/intel/skylake/skl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 8d7305133424..73b70cea74d7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -466,6 +466,7 @@ static int skl_resume(struct device *dev) snd_hdac_bus_init_cmd_io(bus); } else { ret = _skl_resume(bus); + skl_dum_set(bus); /* turn off the links which are off before suspend */ list_for_each_entry(hlink, &bus->hlink_list, list) { From b06de4340be575f50f0ba3cb5a182b18ad34bb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 18 Nov 2013 20:46:48 -0800 Subject: [PATCH 0299/1276] trusty: Add trusty driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit includes: Add arm64 support includes: Add trusty_fast_call64 api on 64 bit systems. includes: move probe to subsys_initcall Child devices of trusty like FIQ-based debuggers and watchdogs may want to probe early, move trusty from module init to subsys init to allow it and its children to probe earlier. includes: Retry std_calls on SM_ERR_BUSY If the trusty spinlock is held, or if the strex fails for another reason, trusty returns SM_ERR_BUSY. Add retry code to handle this. Without this retry code, std_calls can fail. If the previous smc call had returned SM_ERR_INTERRUPTED, this failure would cause the driver to get out of sync with trusty. All later calls would then fail with SM_ERR_INTERLEAVED_SMC. Change-Id: Idc0bbe78b557bc5d95dbec448e4085e3ab9111b4 Signed-off-by: Arve HjønnevÃ¥g --- .../devicetree/bindings/trusty/trusty-smc.txt | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/trusty/Kconfig | 11 + drivers/trusty/Makefile | 5 + drivers/trusty/trusty.c | 258 ++++++++++++++++++ include/linux/trusty/sm_err.h | 39 +++ include/linux/trusty/smcall.h | 75 +++++ include/linux/trusty/trusty.h | 46 ++++ 9 files changed, 443 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-smc.txt create mode 100644 drivers/trusty/Kconfig create mode 100644 drivers/trusty/Makefile create mode 100644 drivers/trusty/trusty.c create mode 100644 include/linux/trusty/sm_err.h create mode 100644 include/linux/trusty/smcall.h create mode 100644 include/linux/trusty/trusty.h diff --git a/Documentation/devicetree/bindings/trusty/trusty-smc.txt b/Documentation/devicetree/bindings/trusty/trusty-smc.txt new file mode 100644 index 000000000000..1b39ad317c67 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-smc.txt @@ -0,0 +1,6 @@ +Trusty smc interface + +Trusty is running in secure mode on the same (arm) cpu(s) as the current os. + +Required properties: +- compatible: "android,trusty-smc-v1" diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..ca0515d53928 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -79,6 +79,8 @@ source "drivers/hwmon/Kconfig" source "drivers/thermal/Kconfig" +source "drivers/trusty/Kconfig" + source "drivers/watchdog/Kconfig" source "drivers/ssb/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..372a5c27dcb1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_W1) += w1/ obj-y += power/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_THERMAL) += thermal/ +obj-$(CONFIG_TRUSTY) += trusty/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig new file mode 100644 index 000000000000..f577ae8acad3 --- /dev/null +++ b/drivers/trusty/Kconfig @@ -0,0 +1,11 @@ +# +# Trusty +# + +menu "Trusty" + +config TRUSTY + tristate "Trusty" + default n + +endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile new file mode 100644 index 000000000000..1d77805d7dd6 --- /dev/null +++ b/drivers/trusty/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for trusty components +# + +obj-$(CONFIG_TRUSTY) += trusty.o diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c new file mode 100644 index 000000000000..59ecf60fc050 --- /dev/null +++ b/drivers/trusty/trusty.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_state { + struct mutex smc_lock; +}; + +#ifdef CONFIG_ARM64 +#define SMC_ARG0 "x0" +#define SMC_ARG1 "x1" +#define SMC_ARG2 "x2" +#define SMC_ARG3 "x3" +#define SMC_ARCH_EXTENSION "" +#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ + "x12","x13","x14","x15","x16","x17" +#else +#define SMC_ARG0 "r0" +#define SMC_ARG1 "r1" +#define SMC_ARG2 "r2" +#define SMC_ARG3 "r3" +#define SMC_ARCH_EXTENSION ".arch_extension sec\n" +#define SMC_REGISTERS_TRASHED "ip" +#endif + +static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +{ + register ulong _r0 asm(SMC_ARG0) = r0; + register ulong _r1 asm(SMC_ARG1) = r1; + register ulong _r2 asm(SMC_ARG2) = r2; + register ulong _r3 asm(SMC_ARG3) = r3; + + asm volatile( + __asmeq("%0", SMC_ARG0) + __asmeq("%1", SMC_ARG1) + __asmeq("%2", SMC_ARG2) + __asmeq("%3", SMC_ARG3) + __asmeq("%4", SMC_ARG0) + __asmeq("%5", SMC_ARG1) + __asmeq("%6", SMC_ARG2) + __asmeq("%7", SMC_ARG3) + SMC_ARCH_EXTENSION + "smc #0" /* switch to secure world */ + : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) + : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) + : SMC_REGISTERS_TRASHED); + return _r0; +} + +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +EXPORT_SYMBOL(trusty_fast_call32); + +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(!SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +#endif + +static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int retry = 5; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx)\n", + __func__, smcnr, a0, a1, a2); + while (true) { + ret = smc(smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY || !retry) + break; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", + __func__, smcnr, a0, a1, a2); + retry--; + } + + return ret; +} + +static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int sleep_time = 1; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + while (true) { + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY) + break; + + if (sleep_time == 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy\n", + __func__, smcnr, a0, a1, a2); + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, wait %d ms\n", + __func__, smcnr, a0, a1, a2, sleep_time); + + msleep(sleep_time); + if (sleep_time < 1000) + sleep_time <<= 1; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) retry\n", + __func__, smcnr, a0, a1, a2); + } + + if (sleep_time > 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) busy cleared\n", + __func__, smcnr, a0, a1, a2); + + return ret; +} + +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + int ret; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + mutex_lock(&s->smc_lock); + + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n", + __func__, smcnr, a0, a1, a2); + + ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); + while (ret == SM_ERR_INTERRUPTED) { + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", + __func__, smcnr, a0, a1, a2); + ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); + } + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", + __func__, smcnr, a0, a1, a2, ret); + + WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); + + mutex_unlock(&s->smc_lock); + + return ret; +} +EXPORT_SYMBOL(trusty_std_call32); + +static int trusty_remove_child(struct device *dev, void *data) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int trusty_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_state *s; + struct device_node *node = pdev->dev.of_node; + + if (!node) { + dev_err(&pdev->dev, "of_node required\n"); + return -EINVAL; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto err_allocate_state; + } + mutex_init(&s->smc_lock); + platform_set_drvdata(pdev, s); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add children: %d\n", ret); + goto err_add_children; + } + + return 0; + +err_add_children: + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); +err_allocate_state: + return ret; +} + +static int trusty_remove(struct platform_device *pdev) +{ + struct trusty_state *s = platform_get_drvdata(pdev); + + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); + return 0; +} + +static const struct of_device_id trusty_of_match[] = { + { .compatible = "android,trusty-smc-v1", }, + {}, +}; + +static struct platform_driver trusty_driver = { + .probe = trusty_probe, + .remove = trusty_remove, + .driver = { + .name = "trusty", + .owner = THIS_MODULE, + .of_match_table = trusty_of_match, + }, +}; + +static int __init trusty_driver_init(void) +{ + return platform_driver_register(&trusty_driver); +} + +static void __exit trusty_driver_exit(void) +{ + platform_driver_unregister(&trusty_driver); +} + +subsys_initcall(trusty_driver_init); +module_exit(trusty_driver_exit); diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h new file mode 100644 index 000000000000..4ee67589ce63 --- /dev/null +++ b/include/linux/trusty/sm_err.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SM_ERR_H +#define __LINUX_TRUSTY_SM_ERR_H + +/* Errors from the secure monitor */ +#define SM_ERR_UNDEFINED_SMC 0xFFFFFFFF /* Unknown SMC (defined by ARM DEN 0028A(0.9.0) */ +#define SM_ERR_INVALID_PARAMETERS -2 +#define SM_ERR_INTERRUPTED -3 /* Got interrupted. Call back with restart SMC */ +#define SM_ERR_UNEXPECTED_RESTART -4 /* Got an restart SMC when we didn't expect it */ +#define SM_ERR_BUSY -5 /* Temporarily busy. Call back with original args */ +#define SM_ERR_INTERLEAVED_SMC -6 /* Got a trusted_service SMC when a restart SMC is required */ +#define SM_ERR_INTERNAL_FAILURE -7 /* Unknown error */ +#define SM_ERR_NOT_SUPPORTED -8 +#define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ +#define SM_ERR_END_OF_INPUT -10 +#define SM_ERR_PANIC -11 /* Secure OS crashed */ + +#endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h new file mode 100644 index 000000000000..278a4b256fbc --- /dev/null +++ b/include/linux/trusty/smcall.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013-2014 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SMCALL_H +#define __LINUX_TRUSTY_SMCALL_H + +#define SMC_NUM_ENTITIES 64 +#define SMC_NUM_ARGS 4 +#define SMC_NUM_PARAMS (SMC_NUM_ARGS - 1) + +#define SMC_IS_FASTCALL(smc_nr) ((smc_nr) & 0x80000000) +#define SMC_IS_SMC64(smc_nr) ((smc_nr) & 0x40000000) +#define SMC_ENTITY(smc_nr) (((smc_nr) & 0x3F000000) >> 24) +#define SMC_FUNCTION(smc_nr) ((smc_nr) & 0x0000FFFF) + +#define SMC_NR(entity, fn, fastcall, smc64) ((((fastcall) & 0x1) << 31) | \ + (((smc64) & 0x1) << 30) | \ + (((entity) & 0x3F) << 24) | \ + ((fn) & 0xFFFF) \ + ) + +#define SMC_FASTCALL_NR(entity, fn) SMC_NR((entity), (fn), 1, 0) +#define SMC_STDCALL_NR(entity, fn) SMC_NR((entity), (fn), 0, 0) +#define SMC_FASTCALL64_NR(entity, fn) SMC_NR((entity), (fn), 1, 1) +#define SMC_STDCALL64_NR(entity, fn) SMC_NR((entity), (fn), 0, 1) + +#define SMC_ENTITY_ARCH 0 /* ARM Architecture calls */ +#define SMC_ENTITY_CPU 1 /* CPU Service calls */ +#define SMC_ENTITY_SIP 2 /* SIP Service calls */ +#define SMC_ENTITY_OEM 3 /* OEM Service calls */ +#define SMC_ENTITY_STD 4 /* Standard Service calls */ +#define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ +#define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ +#define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ + +/* FC = Fast call, SC = Standard call */ +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) + +/* + * Return from secure os to non-secure os with return value in r1 + */ +#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) + +#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) + +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) + +#endif /* __LINUX_TRUSTY_SMCALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h new file mode 100644 index 000000000000..30d4300ba301 --- /dev/null +++ b/include/linux/trusty/trusty.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_TRUSTY_TRUSTY_H +#define __LINUX_TRUSTY_TRUSTY_H + +#include +#include + +#ifdef CONFIG_TRUSTY +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2); +#endif +#else +static inline s32 trusty_std_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +static inline s32 trusty_fast_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#ifdef CONFIG_64BIT +static inline s64 trusty_fast_call64(struct device *dev, + u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#endif +#endif + +#endif From e2eb6019da2f2412a3da62c378896ba171689d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 5 May 2016 15:43:44 -0700 Subject: [PATCH 0300/1276] trusty: Add notifier before and after every smc call. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Called with local interrupts disabled. Change-Id: I5d2b15ce0fee29f067d8403a6f7127046fc185e9 Signed-off-by: Arve HjønnevÃ¥g --- drivers/trusty/trusty.c | 26 ++++++++++++++++++++++++++ include/linux/trusty/trusty.h | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 59ecf60fc050..7efcff89610c 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,6 +26,7 @@ struct trusty_state { struct mutex smc_lock; + struct atomic_notifier_head notifier; }; #ifdef CONFIG_ARM64 @@ -123,7 +124,14 @@ static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { + local_irq_disable(); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_enable(); + if ((int)ret != SM_ERR_BUSY) break; @@ -178,6 +186,23 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) } EXPORT_SYMBOL(trusty_std_call32); +int trusty_call_notifier_register(struct device *dev, struct notifier_block *n) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return atomic_notifier_chain_register(&s->notifier, n); +} +EXPORT_SYMBOL(trusty_call_notifier_register); + +int trusty_call_notifier_unregister(struct device *dev, + struct notifier_block *n) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return atomic_notifier_chain_unregister(&s->notifier, n); +} +EXPORT_SYMBOL(trusty_call_notifier_unregister); + static int trusty_remove_child(struct device *dev, void *data) { platform_device_unregister(to_platform_device(dev)); @@ -201,6 +226,7 @@ static int trusty_probe(struct platform_device *pdev) goto err_allocate_state; } mutex_init(&s->smc_lock); + ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); platform_set_drvdata(pdev, s); ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 30d4300ba301..ce00c1d46a5e 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -43,4 +43,14 @@ static inline s64 trusty_fast_call64(struct device *dev, #endif #endif +struct notifier_block; +enum { + TRUSTY_CALL_PREPARE, + TRUSTY_CALL_RETURNED, +}; +int trusty_call_notifier_register(struct device *dev, + struct notifier_block *n); +int trusty_call_notifier_unregister(struct device *dev, + struct notifier_block *n); + #endif From d01a220f07cf2ce2b440cdf35c6fa92f5c0a0900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 9 Oct 2014 21:24:17 -0700 Subject: [PATCH 0301/1276] trusty: Get version string from trusty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print trusty version to kernel log on startup, and export it in sysfs. Change-Id: Ib8e3d856ed9cf86a71d334f5ab753af1ec8a8bd3 Signed-off-by: Arve HjønnevÃ¥g --- drivers/trusty/trusty.c | 66 +++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 1 + include/linux/trusty/trusty.h | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7efcff89610c..16c595bf5e29 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; + char *version_str; }; #ifdef CONFIG_ARM64 @@ -209,6 +211,60 @@ static int trusty_remove_child(struct device *dev, void *data) return 0; } +ssize_t trusty_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return scnprintf(buf, PAGE_SIZE, "%s\n", s->version_str); +} + +DEVICE_ATTR(trusty_version, S_IRUSR, trusty_version_show, NULL); + +const char *trusty_version_str_get(struct device *dev) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return s->version_str; +} +EXPORT_SYMBOL(trusty_version_str_get); + +static void trusty_init_version(struct trusty_state *s, struct device *dev) +{ + int ret; + int i; + int version_str_len; + + ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, -1, 0, 0); + if (ret <= 0) + goto err_get_size; + + version_str_len = ret; + + s->version_str = kmalloc(version_str_len + 1, GFP_KERNEL); + for (i = 0; i < version_str_len; i++) { + ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, i, 0, 0); + if (ret < 0) + goto err_get_char; + s->version_str[i] = ret; + } + s->version_str[i] = '\0'; + + dev_info(dev, "trusty version: %s\n", s->version_str); + + ret = device_create_file(dev, &dev_attr_trusty_version); + if (ret) + goto err_create_file; + return; + +err_create_file: +err_get_char: + kfree(s->version_str); + s->version_str = NULL; +err_get_size: + dev_err(dev, "failed to get version: %d\n", ret); +} + static int trusty_probe(struct platform_device *pdev) { int ret; @@ -229,6 +285,8 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); platform_set_drvdata(pdev, s); + trusty_init_version(s, &pdev->dev); + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to add children: %d\n", ret); @@ -238,6 +296,10 @@ static int trusty_probe(struct platform_device *pdev) return 0; err_add_children: + if (s->version_str) { + device_remove_file(&pdev->dev, &dev_attr_trusty_version); + kfree(s->version_str); + } device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); kfree(s); @@ -251,6 +313,10 @@ static int trusty_remove(struct platform_device *pdev) device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); + if (s->version_str) { + device_remove_file(&pdev->dev, &dev_attr_trusty_version); + kfree(s->version_str); + } kfree(s); return 0; } diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 278a4b256fbc..4344683f6c61 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -71,5 +71,6 @@ #define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) +#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) #endif /* __LINUX_TRUSTY_SMCALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index ce00c1d46a5e..abb77f1db74d 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -52,5 +52,5 @@ int trusty_call_notifier_register(struct device *dev, struct notifier_block *n); int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); - +const char *trusty_version_str_get(struct device *dev); #endif From 9ded1c8e40fb6a9a0acafe733f4d8544e11b47b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 18 Nov 2013 20:52:55 -0800 Subject: [PATCH 0302/1276] trusty: Add interrupt support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get list of interrupts from secure mode and register handlers for them. When an interrupt triggers, disable the interrupt and schedule a work function. The work functions then masks interrupts at the cpu, reenables the interrupt and calls into secure mode. Edge triggered interrupts are not supported. Change-Id: I6df62e791140f0f2a8b5718b30edd86cca3dde5b Signed-off-by: Arve HjønnevÃ¥g --- .../devicetree/bindings/trusty/trusty-irq.txt | 8 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-irq.c | 536 ++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-irq.txt create mode 100644 drivers/trusty/trusty-irq.c diff --git a/Documentation/devicetree/bindings/trusty/trusty-irq.txt b/Documentation/devicetree/bindings/trusty/trusty-irq.txt new file mode 100644 index 000000000000..85fe1f1c7458 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-irq.txt @@ -0,0 +1,8 @@ +Trusty irq interface + +Trusty requires non-secure irqs to be forwarded to the secure OS. + +Required properties: +- compatible: "android,trusty-irq-v1" + +Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 1d77805d7dd6..89acb6f7868a 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_TRUSTY) += trusty.o +obj-$(CONFIG_TRUSTY) += trusty-irq.o diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c new file mode 100644 index 000000000000..ae9535af77dd --- /dev/null +++ b/drivers/trusty/trusty-irq.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_irq { + struct trusty_irq_state *is; + struct hlist_node node; + unsigned int irq; + bool percpu; + bool enable; + struct trusty_irq __percpu *percpu_ptr; +}; + +struct trusty_irq_work { + struct trusty_irq_state *is; + struct work_struct work; +}; + +struct trusty_irq_irqset { + struct hlist_head pending; + struct hlist_head inactive; +}; + +struct trusty_irq_state { + struct device *dev; + struct device *trusty_dev; + struct trusty_irq_work __percpu *irq_work; + struct trusty_irq_irqset normal_irqs; + spinlock_t normal_irqs_lock; + struct trusty_irq_irqset __percpu *percpu_irqs; + struct notifier_block trusty_call_notifier; + struct notifier_block cpu_notifier; +}; + +static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset, + bool percpu) +{ + struct hlist_node *n; + struct trusty_irq *trusty_irq; + + hlist_for_each_entry_safe(trusty_irq, n, &irqset->pending, node) { + dev_dbg(is->dev, + "%s: enable pending irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, percpu, smp_processor_id()); + if (percpu) + enable_percpu_irq(trusty_irq->irq, 0); + else + enable_irq(trusty_irq->irq); + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->inactive); + } +} + +static void trusty_irq_enable_irqset(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset) +{ + struct trusty_irq *trusty_irq; + + hlist_for_each_entry(trusty_irq, &irqset->inactive, node) { + if (trusty_irq->enable) { + dev_warn(is->dev, + "%s: percpu irq %d already enabled, cpu %d\n", + __func__, trusty_irq->irq, smp_processor_id()); + continue; + } + dev_dbg(is->dev, "%s: enable percpu irq %d, cpu %d\n", + __func__, trusty_irq->irq, smp_processor_id()); + enable_percpu_irq(trusty_irq->irq, 0); + trusty_irq->enable = true; + } +} + +static void trusty_irq_disable_irqset(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset) +{ + struct hlist_node *n; + struct trusty_irq *trusty_irq; + + hlist_for_each_entry(trusty_irq, &irqset->inactive, node) { + if (!trusty_irq->enable) { + dev_warn(is->dev, + "irq %d already disabled, percpu %d, cpu %d\n", + trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + continue; + } + dev_dbg(is->dev, "%s: disable irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + trusty_irq->enable = false; + if (trusty_irq->percpu) + disable_percpu_irq(trusty_irq->irq); + else + disable_irq_nosync(trusty_irq->irq); + } + hlist_for_each_entry_safe(trusty_irq, n, &irqset->pending, node) { + if (!trusty_irq->enable) { + dev_warn(is->dev, + "pending irq %d already disabled, percpu %d, cpu %d\n", + trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + } + dev_dbg(is->dev, + "%s: disable pending irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + trusty_irq->enable = false; + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->inactive); + } +} + +static int trusty_irq_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_irq_state *is; + + BUG_ON(!irqs_disabled()); + + if (action != TRUSTY_CALL_PREPARE) + return NOTIFY_DONE; + + is = container_of(nb, struct trusty_irq_state, trusty_call_notifier); + + spin_lock(&is->normal_irqs_lock); + trusty_irq_enable_pending_irqs(is, &is->normal_irqs, false); + spin_unlock(&is->normal_irqs_lock); + trusty_irq_enable_pending_irqs(is, this_cpu_ptr(is->percpu_irqs), true); + + return NOTIFY_OK; +} + + +static void trusty_irq_work_func(struct work_struct *work) +{ + int ret; + struct trusty_irq_state *is = + container_of(work, struct trusty_irq_work, work)->is; + + dev_dbg(is->dev, "%s\n", __func__); + + ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + if (ret != 0) + dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); + + dev_dbg(is->dev, "%s: done\n", __func__); +} + +irqreturn_t trusty_irq_handler(int irq, void *data) +{ + struct trusty_irq *trusty_irq = data; + struct trusty_irq_state *is = trusty_irq->is; + struct trusty_irq_work *trusty_irq_work = this_cpu_ptr(is->irq_work); + struct trusty_irq_irqset *irqset; + + dev_dbg(is->dev, "%s: irq %d, percpu %d, cpu %d, enable %d\n", + __func__, irq, trusty_irq->irq, smp_processor_id(), + trusty_irq->enable); + + if (trusty_irq->percpu) { + disable_percpu_irq(irq); + irqset = this_cpu_ptr(is->percpu_irqs); + } else { + disable_irq_nosync(irq); + irqset = &is->normal_irqs; + } + + spin_lock(&is->normal_irqs_lock); + if (trusty_irq->enable) { + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->pending); + } + spin_unlock(&is->normal_irqs_lock); + + schedule_work_on(raw_smp_processor_id(), &trusty_irq_work->work); + + dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); + + return IRQ_HANDLED; +} + +static void trusty_irq_cpu_up(void *info) +{ + unsigned long irq_flags; + struct trusty_irq_state *is = info; + + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); + + local_irq_save(irq_flags); + trusty_irq_enable_irqset(is, this_cpu_ptr(is->percpu_irqs)); + local_irq_restore(irq_flags); +} + +static void trusty_irq_cpu_down(void *info) +{ + unsigned long irq_flags; + struct trusty_irq_state *is = info; + + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); + + local_irq_save(irq_flags); + trusty_irq_disable_irqset(is, this_cpu_ptr(is->percpu_irqs)); + local_irq_restore(irq_flags); +} + +static int trusty_irq_cpu_notify(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + struct trusty_irq_state *is; + + is = container_of(nb, struct trusty_irq_state, cpu_notifier); + + dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + trusty_irq_cpu_up(is); + break; + case CPU_DYING: + trusty_irq_cpu_down(is); + break; + } + + return NOTIFY_OK; +} + +static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) +{ + int ret; + unsigned long irq_flags; + struct trusty_irq *trusty_irq; + + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + + trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); + if (!trusty_irq) + return -ENOMEM; + + trusty_irq->is = is; + trusty_irq->irq = irq; + trusty_irq->enable = true; + + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + hlist_add_head(&trusty_irq->node, &is->normal_irqs.inactive); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + + ret = request_irq(irq, trusty_irq_handler, IRQF_NO_THREAD, + "trusty", trusty_irq); + if (ret) { + dev_err(is->dev, "request_irq failed %d\n", ret); + goto err_request_irq; + } + return 0; + +err_request_irq: + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + hlist_del(&trusty_irq->node); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + kfree(trusty_irq); + return ret; +} + +static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int irq) +{ + int ret; + unsigned int cpu; + struct trusty_irq __percpu *trusty_irq_handler_data; + + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + + trusty_irq_handler_data = alloc_percpu(struct trusty_irq); + if (!trusty_irq_handler_data) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + struct trusty_irq *trusty_irq; + struct trusty_irq_irqset *irqset; + + trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); + irqset = per_cpu_ptr(is->percpu_irqs, cpu); + + trusty_irq->is = is; + hlist_add_head(&trusty_irq->node, &irqset->inactive); + trusty_irq->irq = irq; + trusty_irq->percpu = true; + trusty_irq->percpu_ptr = trusty_irq_handler_data; + } + + ret = request_percpu_irq(irq, trusty_irq_handler, "trusty", + trusty_irq_handler_data); + if (ret) { + dev_err(is->dev, "request_percpu_irq failed %d\n", ret); + goto err_request_percpu_irq; + } + + return 0; + +err_request_percpu_irq: + for_each_possible_cpu(cpu) { + struct trusty_irq *trusty_irq; + + trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); + hlist_del(&trusty_irq->node); + } + + free_percpu(trusty_irq_handler_data); + return ret; +} + +static int trusty_smc_get_next_irq(struct trusty_irq_state *is, + unsigned long min_irq, bool per_cpu) +{ + return trusty_fast_call32(is->trusty_dev, SMC_FC_GET_NEXT_IRQ, + min_irq, per_cpu, 0); +} + +static int trusty_irq_init_one(struct trusty_irq_state *is, + int irq, bool per_cpu) +{ + int ret; + + irq = trusty_smc_get_next_irq(is, irq, per_cpu); + if (irq < 0) + return irq; + + if (per_cpu) + ret = trusty_irq_init_per_cpu_irq(is, irq); + else + ret = trusty_irq_init_normal_irq(is, irq); + + if (ret) { + dev_warn(is->dev, + "failed to initialize irq %d, irq will be ignored\n", + irq); + } + + return irq + 1; +} + +static void trusty_irq_free_irqs(struct trusty_irq_state *is) +{ + struct trusty_irq *irq; + struct hlist_node *n; + unsigned int cpu; + + hlist_for_each_entry_safe(irq, n, &is->normal_irqs.inactive, node) { + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq->irq); + free_irq(irq->irq, irq); + hlist_del(&irq->node); + kfree(irq); + } + hlist_for_each_entry_safe(irq, n, + &this_cpu_ptr(is->percpu_irqs)->inactive, + node) { + struct trusty_irq __percpu *trusty_irq_handler_data; + + dev_dbg(is->dev, "%s: percpu irq %d\n", __func__, irq->irq); + trusty_irq_handler_data = irq->percpu_ptr; + free_percpu_irq(irq->irq, trusty_irq_handler_data); + for_each_possible_cpu(cpu) { + struct trusty_irq *irq_tmp; + + irq_tmp = per_cpu_ptr(trusty_irq_handler_data, cpu); + hlist_del(&irq_tmp->node); + } + free_percpu(trusty_irq_handler_data); + } +} + +static int trusty_irq_probe(struct platform_device *pdev) +{ + int ret; + int irq; + unsigned int cpu; + unsigned long irq_flags; + struct trusty_irq_state *is; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + is = kzalloc(sizeof(*is), GFP_KERNEL); + if (!is) { + ret = -ENOMEM; + goto err_alloc_is; + } + + is->dev = &pdev->dev; + is->trusty_dev = is->dev->parent; + is->irq_work = alloc_percpu(struct trusty_irq_work); + if (!is->irq_work) { + ret = -ENOMEM; + goto err_alloc_irq_work; + } + spin_lock_init(&is->normal_irqs_lock); + is->percpu_irqs = alloc_percpu(struct trusty_irq_irqset); + if (!is->percpu_irqs) { + ret = -ENOMEM; + goto err_alloc_pending_percpu_irqs; + } + + platform_set_drvdata(pdev, is); + + is->trusty_call_notifier.notifier_call = trusty_irq_call_notify; + ret = trusty_call_notifier_register(is->trusty_dev, + &is->trusty_call_notifier); + if (ret) { + dev_err(&pdev->dev, + "failed to register trusty call notifier\n"); + goto err_trusty_call_notifier_register; + } + + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + trusty_irq_work->is = is; + INIT_WORK(&trusty_irq_work->work, trusty_irq_work_func); + } + + for (irq = 0; irq >= 0;) + irq = trusty_irq_init_one(is, irq, true); + for (irq = 0; irq >= 0;) + irq = trusty_irq_init_one(is, irq, false); + + is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; + ret = register_hotcpu_notifier(&is->cpu_notifier); + if (ret) { + dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); + goto err_register_hotcpu_notifier; + } + ret = on_each_cpu(trusty_irq_cpu_up, is, 0); + if (ret) { + dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); + goto err_on_each_cpu; + } + + return 0; + +err_on_each_cpu: + unregister_hotcpu_notifier(&is->cpu_notifier); + on_each_cpu(trusty_irq_cpu_down, is, 1); +err_register_hotcpu_notifier: + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + trusty_irq_disable_irqset(is, &is->normal_irqs); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + trusty_irq_free_irqs(is); + trusty_call_notifier_unregister(is->trusty_dev, + &is->trusty_call_notifier); +err_trusty_call_notifier_register: + free_percpu(is->percpu_irqs); +err_alloc_pending_percpu_irqs: + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + flush_work(&trusty_irq_work->work); + } + free_percpu(is->irq_work); +err_alloc_irq_work: + kfree(is); +err_alloc_is: + return ret; +} + +static int trusty_irq_remove(struct platform_device *pdev) +{ + int ret; + unsigned int cpu; + unsigned long irq_flags; + struct trusty_irq_state *is = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s\n", __func__); + + unregister_hotcpu_notifier(&is->cpu_notifier); + ret = on_each_cpu(trusty_irq_cpu_down, is, 1); + if (ret) + dev_err(&pdev->dev, "on_each_cpu failed %d\n", ret); + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + trusty_irq_disable_irqset(is, &is->normal_irqs); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + + trusty_irq_free_irqs(is); + + trusty_call_notifier_unregister(is->trusty_dev, + &is->trusty_call_notifier); + free_percpu(is->percpu_irqs); + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + flush_work(&trusty_irq_work->work); + } + free_percpu(is->irq_work); + kfree(is); + + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-irq-v1", }, + {}, +}; + +static struct platform_driver trusty_irq_driver = { + .probe = trusty_irq_probe, + .remove = trusty_irq_remove, + .driver = { + .name = "trusty-irq", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_irq_driver); From ada75a9a4e647402160ab53aa38be3ce9b38f4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 26 Nov 2013 20:18:35 -0800 Subject: [PATCH 0303/1276] trusty: Add fiq support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register a custom fiq-return handler with the fiq-glue so the monitor mode can atomically re-enable the fiq and return to the last mode. Change-Id: I0016d67edccea096d7f189e223ac73cc20f79ac9 Signed-off-by: Arve HjønnevÃ¥g --- .../bindings/trusty/trusty-fiq-debugger.txt | 8 ++ .../devicetree/bindings/trusty/trusty-fiq.txt | 8 ++ drivers/trusty/Kconfig | 12 +++ drivers/trusty/Makefile | 2 + drivers/trusty/trusty-fiq-arm.c | 42 +++++++++ drivers/trusty/trusty-fiq.c | 85 +++++++++++++++++++ drivers/trusty/trusty-fiq.h | 16 ++++ 7 files changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt create mode 100644 Documentation/devicetree/bindings/trusty/trusty-fiq.txt create mode 100644 drivers/trusty/trusty-fiq-arm.c create mode 100644 drivers/trusty/trusty-fiq.c create mode 100644 drivers/trusty/trusty-fiq.h diff --git a/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt b/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt new file mode 100644 index 000000000000..18329d39487e --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt @@ -0,0 +1,8 @@ +Trusty fiq debugger interface + +Provides a single fiq for the fiq debugger. + +Required properties: +- compatible: compatible = "android,trusty-fiq-v1-*"; where * is a serial port. + +Must be a child of the node that provides fiq support ("android,trusty-fiq-v1"). diff --git a/Documentation/devicetree/bindings/trusty/trusty-fiq.txt b/Documentation/devicetree/bindings/trusty/trusty-fiq.txt new file mode 100644 index 000000000000..de810b955bc9 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-fiq.txt @@ -0,0 +1,8 @@ +Trusty fiq interface + +Trusty provides fiq emulation. + +Required properties: +- compatible: "android,trusty-fiq-v1" + +Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index f577ae8acad3..3c725e29b399 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -8,4 +8,16 @@ config TRUSTY tristate "Trusty" default n +config TRUSTY_FIQ + tristate + depends on TRUSTY + +config TRUSTY_FIQ_ARM + tristate + depends on TRUSTY + depends on ARM + select FIQ_GLUE + select TRUSTY_FIQ + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 89acb6f7868a..a01c82485eb6 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_TRUSTY) += trusty.o obj-$(CONFIG_TRUSTY) += trusty-irq.o +obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o +obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o diff --git a/drivers/trusty/trusty-fiq-arm.c b/drivers/trusty/trusty-fiq-arm.c new file mode 100644 index 000000000000..8c62a00bbc44 --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include "trusty-fiq.h" + +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) + +static void __naked trusty_fiq_return(void) +{ + asm volatile( + ".arch_extension sec\n" + "mov r12, r0\n" + "ldr r0, =" STRINGIFY(SMC_FC_FIQ_EXIT) "\n" + "smc #0"); +} + +int trusty_fiq_arch_probe(struct platform_device *pdev) +{ + return fiq_glue_set_return_handler(trusty_fiq_return); +} + +void trusty_fiq_arch_remove(struct platform_device *pdev) +{ + fiq_glue_clear_return_handler(trusty_fiq_return); +} diff --git a/drivers/trusty/trusty-fiq.c b/drivers/trusty/trusty-fiq.c new file mode 100644 index 000000000000..1a031c67ea72 --- /dev/null +++ b/drivers/trusty/trusty-fiq.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "trusty-fiq.h" + +static int trusty_fiq_remove_child(struct device *dev, void *data) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int trusty_fiq_probe(struct platform_device *pdev) +{ + int ret; + + ret = trusty_fiq_arch_probe(pdev); + if (ret) + goto err_set_fiq_return; + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add children: %d\n", ret); + goto err_add_children; + } + + return 0; + +err_add_children: + device_for_each_child(&pdev->dev, NULL, trusty_fiq_remove_child); + trusty_fiq_arch_remove(pdev); +err_set_fiq_return: + return ret; +} + +static int trusty_fiq_remove(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, trusty_fiq_remove_child); + trusty_fiq_arch_remove(pdev); + return 0; +} + +static const struct of_device_id trusty_fiq_of_match[] = { + { .compatible = "android,trusty-fiq-v1", }, + {}, +}; + +static struct platform_driver trusty_fiq_driver = { + .probe = trusty_fiq_probe, + .remove = trusty_fiq_remove, + .driver = { + .name = "trusty-fiq", + .owner = THIS_MODULE, + .of_match_table = trusty_fiq_of_match, + }, +}; + +static int __init trusty_fiq_driver_init(void) +{ + return platform_driver_register(&trusty_fiq_driver); +} + +static void __exit trusty_fiq_driver_exit(void) +{ + platform_driver_unregister(&trusty_fiq_driver); +} + +subsys_initcall(trusty_fiq_driver_init); +module_exit(trusty_fiq_driver_exit); diff --git a/drivers/trusty/trusty-fiq.h b/drivers/trusty/trusty-fiq.h new file mode 100644 index 000000000000..d4ae9a9635f3 --- /dev/null +++ b/drivers/trusty/trusty-fiq.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +int trusty_fiq_arch_probe(struct platform_device *pdev); +void trusty_fiq_arch_remove(struct platform_device *pdev); From 7474a6628333833e35c79ce1e664b3b6b6079186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 14 Apr 2014 17:18:40 -0700 Subject: [PATCH 0304/1276] trusty: arm64 fiq support Change-Id: I907fbaa2b9d1697b204dad6c16d9027ef3bb0a58 --- drivers/trusty/Kconfig | 8 ++ drivers/trusty/Makefile | 1 + drivers/trusty/trusty-fiq-arm64-glue.S | 54 ++++++++++ drivers/trusty/trusty-fiq-arm64.c | 140 +++++++++++++++++++++++++ include/linux/trusty/smcall.h | 4 + 5 files changed, 207 insertions(+) create mode 100644 drivers/trusty/trusty-fiq-arm64-glue.S create mode 100644 drivers/trusty/trusty-fiq-arm64.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 3c725e29b399..fc1061deb876 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -20,4 +20,12 @@ config TRUSTY_FIQ_ARM select TRUSTY_FIQ default y +config TRUSTY_FIQ_ARM64 + tristate + depends on TRUSTY + depends on ARM64 + select FIQ_GLUE + select TRUSTY_FIQ + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index a01c82485eb6..e162a4061e14 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_TRUSTY) += trusty.o obj-$(CONFIG_TRUSTY) += trusty-irq.o obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o +obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o diff --git a/drivers/trusty/trusty-fiq-arm64-glue.S b/drivers/trusty/trusty-fiq-arm64-glue.S new file mode 100644 index 000000000000..6994b3a94fc3 --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm64-glue.S @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +.macro push reg1,reg2,remregs:vararg + .ifnb \remregs + push \remregs + .endif + stp \reg1, \reg2, [sp, #-16]! +.endm + +.macro pop reg1,reg2,remregs:vararg + ldp \reg1, \reg2, [sp], #16 + .ifnb \remregs + pop \remregs + .endif +.endm + +ENTRY(trusty_fiq_glue_arm64) + sub sp, sp, #S_FRAME_SIZE - S_LR + push x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, \ + x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, \ + x26, x27, x28, x29 + ldr x0, =SMC_FC64_GET_FIQ_REGS + smc #0 + stp x0, x1, [sp, #S_PC] /* original pc, cpsr */ + tst x1, PSR_MODE_MASK + csel x2, x2, x3, eq /* sp el0, sp el1 */ + stp x30, x2, [sp, #S_LR] /* lr, original sp */ + mov x0, sp + mov x1, x3 + bl trusty_fiq_handler + pop x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, \ + x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, \ + x26, x27, x28, x29 + ldr x30, [sp], #S_FRAME_SIZE - S_LR /* load LR and restore SP */ + ldr x0, =SMC_FC_FIQ_EXIT + smc #0 + b . /* should not get here */ diff --git a/drivers/trusty/trusty-fiq-arm64.c b/drivers/trusty/trusty-fiq-arm64.c new file mode 100644 index 000000000000..df05a98f235d --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm64.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include + +#include "trusty-fiq.h" + +extern void trusty_fiq_glue_arm64(void); + +static struct device *trusty_dev; +static DEFINE_PER_CPU(void *, fiq_stack); +static struct fiq_glue_handler *current_handler; +static DEFINE_MUTEX(fiq_glue_lock); + +void trusty_fiq_handler(struct pt_regs *regs, void *svc_sp) +{ + current_handler->fiq(current_handler, regs, svc_sp); +} + +static void smp_nop_call(void *info) +{ + /* If this call is reached, the fiq handler is not currently running */ +} + +static void fiq_glue_clear_handler(void) +{ + int cpu; + int ret; + void *stack; + + for_each_possible_cpu(cpu) { + stack = per_cpu(fiq_stack, cpu); + if (!stack) + continue; + + ret = trusty_fast_call64(trusty_dev, SMC_FC64_SET_FIQ_HANDLER, + cpu, 0, 0); + if (ret) { + pr_err("%s: SMC_FC_SET_FIQ_HANDLER(%d, 0, 0) failed 0x%x, skip free stack\n", + __func__, cpu, ret); + continue; + } + + per_cpu(fiq_stack, cpu) = NULL; + smp_call_function_single(cpu, smp_nop_call, NULL, true); + free_pages((unsigned long)stack, THREAD_SIZE_ORDER); + } +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + int cpu; + void *stack; + unsigned long irqflags; + + if (!handler || !handler->fiq) + return -EINVAL; + + mutex_lock(&fiq_glue_lock); + + if (!trusty_dev) { + ret = -ENODEV; + goto err_no_trusty; + } + if (current_handler) { + ret = -EBUSY; + goto err_busy; + } + + current_handler = handler; + + for_each_possible_cpu(cpu) { + stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); + if (WARN_ON(!stack)) { + ret = -ENOMEM; + goto err_alloc_fiq_stack; + } + per_cpu(fiq_stack, cpu) = stack; + stack += THREAD_START_SP; + + local_irq_save(irqflags); + ret = trusty_fast_call64(trusty_dev, SMC_FC64_SET_FIQ_HANDLER, + cpu, (uintptr_t)trusty_fiq_glue_arm64, + (uintptr_t)stack); + local_irq_restore(irqflags); + if (ret) { + pr_err("%s: SMC_FC_SET_FIQ_HANDLER(%d, %p, %p) failed 0x%x\n", + __func__, cpu, trusty_fiq_glue_arm64, + stack, ret); + ret = -EINVAL; + goto err_set_fiq_handler; + } + } + + mutex_unlock(&fiq_glue_lock); + return 0; + +err_set_fiq_handler: +err_alloc_fiq_stack: + fiq_glue_clear_handler(); +err_busy: +err_no_trusty: + mutex_unlock(&fiq_glue_lock); + return ret; +} + +int trusty_fiq_arch_probe(struct platform_device *pdev) +{ + mutex_lock(&fiq_glue_lock); + trusty_dev = pdev->dev.parent; + mutex_unlock(&fiq_glue_lock); + + return 0; +} + +void trusty_fiq_arch_remove(struct platform_device *pdev) +{ + mutex_lock(&fiq_glue_lock); + fiq_glue_clear_handler(); + trusty_dev = NULL; + mutex_unlock(&fiq_glue_lock); +} diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 4344683f6c61..e8704974d3e3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -66,6 +66,10 @@ #define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) #define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) #define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4) + +#define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5) +#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6) #define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) #define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) From 5bc3b98394f1c91cbab8c8f6c939daee2282e3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 2 May 2014 19:15:44 -0700 Subject: [PATCH 0305/1276] trusty: fiq-arm64: Allow multiple fiq handlers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If multiple fiq handlers are reqistered call them all. There is currently no api to remove handlers. Change-Id: I1d4bd936081d690ea6f1ec0c041f43a5f7717733 Signed-off-by: Arve HjønnevÃ¥g --- drivers/trusty/trusty-fiq-arm64.c | 76 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/trusty/trusty-fiq-arm64.c b/drivers/trusty/trusty-fiq-arm64.c index df05a98f235d..8b9a40887587 100644 --- a/drivers/trusty/trusty-fiq-arm64.c +++ b/drivers/trusty/trusty-fiq-arm64.c @@ -26,12 +26,19 @@ extern void trusty_fiq_glue_arm64(void); static struct device *trusty_dev; static DEFINE_PER_CPU(void *, fiq_stack); -static struct fiq_glue_handler *current_handler; +static struct fiq_glue_handler *fiq_handlers; static DEFINE_MUTEX(fiq_glue_lock); void trusty_fiq_handler(struct pt_regs *regs, void *svc_sp) { - current_handler->fiq(current_handler, regs, svc_sp); + struct fiq_glue_handler *handler; + + for (handler = ACCESS_ONCE(fiq_handlers); handler; + handler = ACCESS_ONCE(handler->next)) { + /* Barrier paired with smp_wmb in fiq_glue_register_handler */ + smp_read_barrier_depends(); + handler->fiq(handler, regs, svc_sp); + } } static void smp_nop_call(void *info) @@ -64,29 +71,13 @@ static void fiq_glue_clear_handler(void) } } -int fiq_glue_register_handler(struct fiq_glue_handler *handler) +static int fiq_glue_set_handler(void) { int ret; int cpu; void *stack; unsigned long irqflags; - if (!handler || !handler->fiq) - return -EINVAL; - - mutex_lock(&fiq_glue_lock); - - if (!trusty_dev) { - ret = -ENODEV; - goto err_no_trusty; - } - if (current_handler) { - ret = -EBUSY; - goto err_busy; - } - - current_handler = handler; - for_each_possible_cpu(cpu) { stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); if (WARN_ON(!stack)) { @@ -109,16 +100,57 @@ int fiq_glue_register_handler(struct fiq_glue_handler *handler) goto err_set_fiq_handler; } } + return 0; + +err_alloc_fiq_stack: +err_set_fiq_handler: + fiq_glue_clear_handler(); + return ret; +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + + if (!handler || !handler->fiq) { + ret = -EINVAL; + goto err_bad_arg; + } + + mutex_lock(&fiq_glue_lock); + + if (!trusty_dev) { + ret = -ENODEV; + goto err_no_trusty; + } + + handler->next = fiq_handlers; + /* + * Write barrier paired with smp_read_barrier_depends in + * trusty_fiq_handler. Make sure next pointer is updated before + * fiq_handlers so trusty_fiq_handler does not see an uninitialized + * value and terminate early or crash. + */ + smp_wmb(); + fiq_handlers = handler; + + smp_call_function(smp_nop_call, NULL, true); + + if (!handler->next) { + ret = fiq_glue_set_handler(); + if (ret) + goto err_set_fiq_handler; + } mutex_unlock(&fiq_glue_lock); return 0; err_set_fiq_handler: -err_alloc_fiq_stack: - fiq_glue_clear_handler(); -err_busy: + fiq_handlers = handler->next; err_no_trusty: mutex_unlock(&fiq_glue_lock); +err_bad_arg: + pr_err("%s: failed, %d\n", __func__, ret); return ret; } From a53f21ecbfbc3b4a75d2562e27fb266c72529c1e Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Thu, 5 May 2016 14:42:41 -0700 Subject: [PATCH 0306/1276] trusty: Add trusty logging driver. This driver is the consumer side of a ringbuffer of log data that the secure operating system dumps prints into. Trusty printfs will be dumped into the kernel log after smc calls and during panics. Change-Id: Iadc939b60940330e8fe02a52f3e397da7833c2fa --- drivers/trusty/Kconfig | 5 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-log.c | 274 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty-log.h | 22 +++ include/linux/trusty/smcall.h | 1 + 5 files changed, 303 insertions(+) create mode 100644 drivers/trusty/trusty-log.c create mode 100644 drivers/trusty/trusty-log.h diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index fc1061deb876..ea75813254c0 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -28,4 +28,9 @@ config TRUSTY_FIQ_ARM64 select TRUSTY_FIQ default y +config TRUSTY_LOG + tristate + depends on TRUSTY + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index e162a4061e14..641ee2a6e830 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_TRUSTY) += trusty-irq.o obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o +obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c new file mode 100644 index 000000000000..e8dcced2ff1d --- /dev/null +++ b/drivers/trusty/trusty-log.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trusty-log.h" + +#define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) +#define TRUSTY_LINE_BUFFER_SIZE 256 + +struct trusty_log_state { + struct device *dev; + struct device *trusty_dev; + + /* + * This lock is here to ensure only one consumer will read + * from the log ring buffer at a time. + */ + spinlock_t lock; + struct log_rb *log; + uint32_t get; + + struct page *log_pages; + + struct notifier_block call_notifier; + struct notifier_block panic_notifier; + char line_buffer[TRUSTY_LINE_BUFFER_SIZE]; +}; + +static int log_read_line(struct trusty_log_state *s, int put, int get) +{ + struct log_rb *log = s->log; + int i; + char c = '\0'; + size_t max_to_read = min((size_t)(put - get), + sizeof(s->line_buffer) - 1); + size_t mask = log->sz - 1; + + for (i = 0; i < max_to_read && c != '\n';) + s->line_buffer[i++] = c = log->data[get++ & mask]; + s->line_buffer[i] = '\0'; + + return i; +} + +static void trusty_dump_logs(struct trusty_log_state *s) +{ + struct log_rb *log = s->log; + uint32_t get, put, alloc; + int read_chars; + + BUG_ON(!is_power_of_2(log->sz)); + + /* + * For this ring buffer, at any given point, alloc >= put >= get. + * The producer side of the buffer is not locked, so the put and alloc + * pointers must be read in a defined order (put before alloc) so + * that the above condition is maintained. A read barrier is needed + * to make sure the hardware and compiler keep the reads ordered. + */ + get = s->get; + while ((put = log->put) != get) { + /* Make sure that the read of put occurs before the read of log data */ + rmb(); + + /* Read a line from the log */ + read_chars = log_read_line(s, put, get); + + /* Force the loads from log_read_line to complete. */ + rmb(); + alloc = log->alloc; + + /* + * Discard the line that was just read if the data could + * have been corrupted by the producer. + */ + if (alloc - get > log->sz) { + pr_err("trusty: log overflow."); + get = alloc - log->sz; + continue; + } + pr_info("trusty: %s", s->line_buffer); + get += read_chars; + } + s->get = get; +} + +static int trusty_log_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_log_state *s; + unsigned long flags; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + s = container_of(nb, struct trusty_log_state, call_notifier); + spin_lock_irqsave(&s->lock, flags); + trusty_dump_logs(s); + spin_unlock_irqrestore(&s->lock, flags); + return NOTIFY_OK; +} + +static int trusty_log_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_log_state *s; + + /* + * Don't grab the spin lock to hold up the panic notifier, even + * though this is racy. + */ + s = container_of(nb, struct trusty_log_state, panic_notifier); + pr_info("trusty-log panic notifier - trusty version %s", + trusty_version_str_get(s->trusty_dev)); + trusty_dump_logs(s); + return NOTIFY_OK; +} + +static bool trusty_supports_logging(struct device *device) +{ + int result; + + result = trusty_std_call32(device, SMC_SC_SHARED_LOG_VERSION, + TRUSTY_LOG_API_VERSION, 0, 0); + if (result == SM_ERR_UNDEFINED_SMC) { + pr_info("trusty-log not supported on secure side.\n"); + return false; + } else if (result < 0) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_VERSION) failed: %d\n", + result); + return false; + } + + if (result == TRUSTY_LOG_API_VERSION) { + return true; + } else { + pr_info("trusty-log unsupported api version: %d, supported: %d\n", + result, TRUSTY_LOG_API_VERSION); + return false; + } +} + +static int trusty_log_probe(struct platform_device *pdev) +{ + struct trusty_log_state *s; + int result; + phys_addr_t pa; + + dev_dbg(&pdev->dev, "%s\n", __func__); + if (!trusty_supports_logging(pdev->dev.parent)) { + return -ENXIO; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + result = -ENOMEM; + goto error_alloc_state; + } + + spin_lock_init(&s->lock); + s->dev = &pdev->dev; + s->trusty_dev = s->dev->parent; + s->get = 0; + s->log_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order(TRUSTY_LOG_SIZE)); + if (!s->log_pages) { + result = -ENOMEM; + goto error_alloc_log; + } + s->log = page_address(s->log_pages); + + pa = page_to_phys(s->log_pages); + result = trusty_std_call32(s->trusty_dev, + SMC_SC_SHARED_LOG_ADD, + (u32)(pa), (u32)(pa >> 32), + TRUSTY_LOG_SIZE); + if (result < 0) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_ADD) failed: %d %pa\n", + result, &pa); + goto error_std_call; + } + + s->call_notifier.notifier_call = trusty_log_call_notify; + result = trusty_call_notifier_register(s->trusty_dev, + &s->call_notifier); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register trusty call notifier\n"); + goto error_call_notifier; + } + + s->panic_notifier.notifier_call = trusty_log_panic_notify; + result = atomic_notifier_chain_register(&panic_notifier_list, + &s->panic_notifier); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register panic notifier\n"); + goto error_panic_notifier; + } + platform_set_drvdata(pdev, s); + + return 0; + +error_panic_notifier: + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); +error_call_notifier: + trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, + (u32)pa, (u32)(pa >> 32), 0); +error_std_call: + __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); +error_alloc_log: + kfree(s); +error_alloc_state: + return result; +} + +static int trusty_log_remove(struct platform_device *pdev) +{ + int result; + struct trusty_log_state *s = platform_get_drvdata(pdev); + phys_addr_t pa = page_to_phys(s->log_pages); + + dev_dbg(&pdev->dev, "%s\n", __func__); + + atomic_notifier_chain_unregister(&panic_notifier_list, + &s->panic_notifier); + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); + + result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, + (u32)pa, (u32)(pa >> 32), 0); + if (result) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n", + result); + } + __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); + kfree(s); + + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-log-v1", }, + {}, +}; + +static struct platform_driver trusty_log_driver = { + .probe = trusty_log_probe, + .remove = trusty_log_remove, + .driver = { + .name = "trusty-log", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_log_driver); diff --git a/drivers/trusty/trusty-log.h b/drivers/trusty/trusty-log.h new file mode 100644 index 000000000000..09f60213e1f6 --- /dev/null +++ b/drivers/trusty/trusty-log.h @@ -0,0 +1,22 @@ +#ifndef _TRUSTY_LOG_H_ +#define _TRUSTY_LOG_H_ + +/* + * Ring buffer that supports one secure producer thread and one + * linux side consumer thread. + */ +struct log_rb { + volatile uint32_t alloc; + volatile uint32_t put; + uint32_t sz; + volatile char data[0]; +} __packed; + +#define SMC_SC_SHARED_LOG_VERSION SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 0) +#define SMC_SC_SHARED_LOG_ADD SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 1) +#define SMC_SC_SHARED_LOG_RM SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 2) + +#define TRUSTY_LOG_API_VERSION 1 + +#endif + diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index e8704974d3e3..aaad5cee6143 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -51,6 +51,7 @@ #define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ #define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ #define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_LOGGING 51 /* Used for secure -> nonsecure logging */ #define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ /* FC = Fast call, SC = Standard call */ From ea7cd4be59dff208228d1ad6e25509d5c977336b Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Thu, 26 Mar 2015 19:31:25 -0700 Subject: [PATCH 0307/1276] trusty: add couple non-secure memory related helper routines trusty_encode_page_info - encodes page physical address, memory type and other attributes into architecture specific structure that can be parsed by secure side. trusty_call32_mem_buf - can be used by drivers to make specified smc call with physicaly contigues memory buffer as an argument. Memory buffer info in retrieved by trusty_encode_page_info and along with buffer size is encoded into series of 32-bit smc call parameters. Change-Id: I79aadca85e2329bb89469b4c8f183cf0752f7641 Signed-off-by: Michael Ryleev --- drivers/trusty/Makefile | 1 + drivers/trusty/trusty-mem.c | 134 ++++++++++++++++++++++++++++++++++ include/linux/trusty/trusty.h | 15 ++++ 3 files changed, 150 insertions(+) create mode 100644 drivers/trusty/trusty-mem.c diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 641ee2a6e830..e527a237cb5d 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o +obj-$(CONFIG_TRUSTY) += trusty-mem.o diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c new file mode 100644 index 000000000000..c55ace25beed --- /dev/null +++ b/drivers/trusty/trusty-mem.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +static int get_mem_attr(struct page *page, pgprot_t pgprot) +{ +#if defined(CONFIG_ARM64) + uint64_t mair; + uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; + + asm ("mrs %0, mair_el1\n" : "=&r" (mair)); + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM_LPAE) + uint32_t mair; + uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); + + if (attr_index >= 4) { + attr_index -= 4; + asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); + } else { + asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); + } + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM) + /* check memory type */ + switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { + case L_PTE_MT_WRITEALLOC: + /* Normal: write back write allocate */ + return 0xFF; + + case L_PTE_MT_BUFFERABLE: + /* Normal: non-cacheble */ + return 0x44; + + case L_PTE_MT_WRITEBACK: + /* Normal: writeback, read allocate */ + return 0xEE; + + case L_PTE_MT_WRITETHROUGH: + /* Normal: write through */ + return 0xAA; + + case L_PTE_MT_UNCACHED: + /* strongly ordered */ + return 0x00; + + case L_PTE_MT_DEV_SHARED: + case L_PTE_MT_DEV_NONSHARED: + /* device */ + return 0x04; + + default: + return -EINVAL; + } +#else + return 0; +#endif +} + +int trusty_encode_page_info(struct ns_mem_page_info *inf, + struct page *page, pgprot_t pgprot) +{ + int mem_attr; + uint64_t pte; + + if (!inf || !page) + return -EINVAL; + + /* get physical address */ + pte = (uint64_t) page_to_phys(page); + + /* get memory attributes */ + mem_attr = get_mem_attr(page, pgprot); + if (mem_attr < 0) + return mem_attr; + + /* add other attributes */ +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) + pte |= pgprot_val(pgprot); +#elif defined(CONFIG_ARM) + if (pgprot_val(pgprot) & L_PTE_USER) + pte |= (1 << 6); + if (pgprot_val(pgprot) & L_PTE_RDONLY) + pte |= (1 << 7); + if (pgprot_val(pgprot) & L_PTE_SHARED) + pte |= (3 << 8); /* inner sharable */ +#endif + + inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); + return 0; +} + +int trusty_call32_mem_buf(struct device *dev, u32 smcnr, + struct page *page, u32 size, + pgprot_t pgprot) +{ + int ret; + struct ns_mem_page_info pg_inf; + + if (!dev || !page) + return -EINVAL; + + ret = trusty_encode_page_info(&pg_inf, page, pgprot); + if (ret) + return ret; + + if (SMC_IS_FASTCALL(smcnr)) { + return trusty_fast_call32(dev, smcnr, + (u32)pg_inf.attr, + (u32)(pg_inf.attr >> 32), size); + } else { + return trusty_std_call32(dev, smcnr, + (u32)pg_inf.attr, + (u32)(pg_inf.attr >> 32), size); + } +} + diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index abb77f1db74d..d084d9d68a7b 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -16,6 +16,9 @@ #include #include +#include +#include + #ifdef CONFIG_TRUSTY s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); @@ -53,4 +56,16 @@ int trusty_call_notifier_register(struct device *dev, int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); const char *trusty_version_str_get(struct device *dev); + +struct ns_mem_page_info { + uint64_t attr; +}; + +int trusty_encode_page_info(struct ns_mem_page_info *inf, + struct page *page, pgprot_t pgprot); + +int trusty_call32_mem_buf(struct device *dev, u32 smcnr, + struct page *page, u32 size, + pgprot_t pgprot); + #endif From 8556a8896c865d24213aac7bf3b5b8de946c4755 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Mon, 30 Mar 2015 12:43:59 -0700 Subject: [PATCH 0308/1276] trusty: add trusty virtio driver Trusty virtio driver is responsible for management an interaction with virtio devices exposed by Trusty. During initialization, this driver makes an smc call to retrieve Trusty virtio device descriptor from secure side, parses it then instantiates and configures the specified set of virtio devices. Change-Id: I3bac25d861db55a0f1408a4344ff5f8e53a75d44 Signed-off-by: Michael Ryleev --- drivers/trusty/Kconfig | 6 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-virtio.c | 697 +++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 8 + 4 files changed, 712 insertions(+) create mode 100644 drivers/trusty/trusty-virtio.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index ea75813254c0..2255c0a9a815 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -33,4 +33,10 @@ config TRUSTY_LOG depends on TRUSTY default y +config TRUSTY_VIRTIO + tristate "Trusty virtio support" + depends on TRUSTY + select VIRTIO + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index e527a237cb5d..beb89a87f115 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o +obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c new file mode 100644 index 000000000000..fabbf29bffcc --- /dev/null +++ b/drivers/trusty/trusty-virtio.c @@ -0,0 +1,697 @@ +/* + * Trusty Virtio driver + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define RSC_DESCR_VER 1 + +struct trusty_vdev; + +struct trusty_ctx { + struct device *dev; + void *shared_va; + size_t shared_sz; + struct work_struct check_vqs; + struct work_struct kick_vqs; + struct notifier_block call_notifier; + struct list_head vdev_list; + struct mutex mlock; /* protects vdev_list */ +}; + +struct trusty_vring { + void *vaddr; + phys_addr_t paddr; + size_t size; + uint align; + uint elem_num; + u32 notifyid; + atomic_t needs_kick; + struct fw_rsc_vdev_vring *vr_descr; + struct virtqueue *vq; + struct trusty_vdev *tvdev; +}; + +struct trusty_vdev { + struct list_head node; + struct virtio_device vdev; + struct trusty_ctx *tctx; + u32 notifyid; + uint config_len; + void *config; + struct fw_rsc_vdev *vdev_descr; + uint vring_num; + struct trusty_vring vrings[0]; +}; + +#define vdev_to_tvdev(vd) container_of((vd), struct trusty_vdev, vdev) + +static void check_all_vqs(struct work_struct *work) +{ + uint i; + struct trusty_ctx *tctx = container_of(work, struct trusty_ctx, + check_vqs); + struct trusty_vdev *tvdev; + + list_for_each_entry(tvdev, &tctx->vdev_list, node) { + for (i = 0; i < tvdev->vring_num; i++) + vring_interrupt(0, tvdev->vrings[i].vq); + } +} + +static int trusty_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_ctx *tctx; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + tctx = container_of(nb, struct trusty_ctx, call_notifier); + schedule_work(&tctx->check_vqs); + + return NOTIFY_OK; +} + +static void kick_vq(struct trusty_ctx *tctx, + struct trusty_vdev *tvdev, + struct trusty_vring *tvr) +{ + int ret; + + dev_dbg(tctx->dev, "%s: vdev_id=%d: vq_id=%d\n", + __func__, tvdev->notifyid, tvr->notifyid); + + ret = trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_KICK_VQ, + tvdev->notifyid, tvr->notifyid, 0); + if (ret) { + dev_err(tctx->dev, "vq notify (%d, %d) returned %d\n", + tvdev->notifyid, tvr->notifyid, ret); + } +} + +static void kick_vqs(struct work_struct *work) +{ + uint i; + struct trusty_vdev *tvdev; + struct trusty_ctx *tctx = container_of(work, struct trusty_ctx, + kick_vqs); + mutex_lock(&tctx->mlock); + list_for_each_entry(tvdev, &tctx->vdev_list, node) { + for (i = 0; i < tvdev->vring_num; i++) { + struct trusty_vring *tvr = &tvdev->vrings[i]; + if (atomic_xchg(&tvr->needs_kick, 0)) + kick_vq(tctx, tvdev, tvr); + } + } + mutex_unlock(&tctx->mlock); +} + +static bool trusty_virtio_notify(struct virtqueue *vq) +{ + struct trusty_vring *tvr = vq->priv; + struct trusty_vdev *tvdev = tvr->tvdev; + struct trusty_ctx *tctx = tvdev->tctx; + + atomic_set(&tvr->needs_kick, 1); + schedule_work(&tctx->kick_vqs); + + return true; +} + +static int trusty_load_device_descr(struct trusty_ctx *tctx, + void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, + SMC_SC_VIRTIO_GET_DESCR, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret < 0) { + dev_err(tctx->dev, "%s: virtio get descr returned (%d)\n", + __func__, ret); + return -ENODEV; + } + return ret; +} + +static void trusty_virtio_stop(struct trusty_ctx *tctx, void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, SMC_SC_VIRTIO_STOP, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret) { + dev_err(tctx->dev, "%s: virtio done returned (%d)\n", + __func__, ret); + return; + } +} + +static int trusty_virtio_start(struct trusty_ctx *tctx, + void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, SMC_SC_VIRTIO_START, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret) { + dev_err(tctx->dev, "%s: virtio start returned (%d)\n", + __func__, ret); + return -ENODEV; + } + return 0; +} + +static void trusty_virtio_reset(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + struct trusty_ctx *tctx = tvdev->tctx; + + dev_dbg(&vdev->dev, "reset vdev_id=%d\n", tvdev->notifyid); + trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_RESET, + tvdev->notifyid, 0, 0); +} + +static u64 trusty_virtio_get_features(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + return tvdev->vdev_descr->dfeatures; +} + +static int trusty_virtio_finalize_features(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + + /* Make sure we don't have any features > 32 bits! */ + BUG_ON((u32)vdev->features != vdev->features); + + tvdev->vdev_descr->gfeatures = vdev->features; + return 0; +} + +static void trusty_virtio_get_config(struct virtio_device *vdev, + unsigned offset, void *buf, + unsigned len) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + + dev_dbg(&vdev->dev, "%s: %d bytes @ offset %d\n", + __func__, len, offset); + + if (tvdev->config) { + if (offset + len <= tvdev->config_len) + memcpy(buf, tvdev->config + offset, len); + } +} + +static void trusty_virtio_set_config(struct virtio_device *vdev, + unsigned offset, const void *buf, + unsigned len) +{ + dev_dbg(&vdev->dev, "%s\n", __func__); +} + +static u8 trusty_virtio_get_status(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + return tvdev->vdev_descr->status; +} + +static void trusty_virtio_set_status(struct virtio_device *vdev, u8 status) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + tvdev->vdev_descr->status = status; +} + +static void _del_vqs(struct virtio_device *vdev) +{ + uint i; + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + struct trusty_vring *tvr = &tvdev->vrings[0]; + + for (i = 0; i < tvdev->vring_num; i++, tvr++) { + /* delete vq */ + if (tvr->vq) { + vring_del_virtqueue(tvr->vq); + tvr->vq = NULL; + } + /* delete vring */ + if (tvr->vaddr) { + free_pages_exact(tvr->vaddr, tvr->size); + tvr->vaddr = NULL; + } + } +} + +static void trusty_virtio_del_vqs(struct virtio_device *vdev) +{ + dev_dbg(&vdev->dev, "%s\n", __func__); + _del_vqs(vdev); +} + + +static struct virtqueue *_find_vq(struct virtio_device *vdev, + unsigned id, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct trusty_vring *tvr; + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + phys_addr_t pa; + + if (!name) + return ERR_PTR(-EINVAL); + + if (id >= tvdev->vring_num) + return ERR_PTR(-EINVAL); + + tvr = &tvdev->vrings[id]; + + /* actual size of vring (in bytes) */ + tvr->size = PAGE_ALIGN(vring_size(tvr->elem_num, tvr->align)); + + /* allocate memory for the vring. */ + tvr->vaddr = alloc_pages_exact(tvr->size, GFP_KERNEL | __GFP_ZERO); + if (!tvr->vaddr) { + dev_err(&vdev->dev, "vring alloc failed\n"); + return ERR_PTR(-ENOMEM); + } + + pa = virt_to_phys(tvr->vaddr); + /* save vring address to shared structure */ + tvr->vr_descr->da = (u32)pa; + /* da field is only 32 bit wide. Use previously unused 'reserved' field + * to store top 32 bits of 64-bit address + */ + tvr->vr_descr->reserved = (u32)(pa >> 32); + + dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", + id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); + + tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, + vdev, true, tvr->vaddr, + trusty_virtio_notify, callback, name); + if (!tvr->vq) { + dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", + name); + goto err_new_virtqueue; + } + + tvr->vq->priv = tvr; + + return tvr->vq; + +err_new_virtqueue: + free_pages_exact(tvr->vaddr, tvr->size); + tvr->vaddr = NULL; + return ERR_PTR(-ENOMEM); +} + +static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + uint i; + int ret; + + for (i = 0; i < nvqs; i++) { + vqs[i] = _find_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + _del_vqs(vdev); + return ret; + } + } + return 0; +} + +static const char *trusty_virtio_bus_name(struct virtio_device *vdev) +{ + return "trusty-virtio"; +} + +/* The ops structure which hooks everything together. */ +static const struct virtio_config_ops trusty_virtio_config_ops = { + .get_features = trusty_virtio_get_features, + .finalize_features = trusty_virtio_finalize_features, + .get = trusty_virtio_get_config, + .set = trusty_virtio_set_config, + .get_status = trusty_virtio_get_status, + .set_status = trusty_virtio_set_status, + .reset = trusty_virtio_reset, + .find_vqs = trusty_virtio_find_vqs, + .del_vqs = trusty_virtio_del_vqs, + .bus_name = trusty_virtio_bus_name, +}; + +static int trusty_virtio_add_device(struct trusty_ctx *tctx, + struct fw_rsc_vdev *vdev_descr, + struct fw_rsc_vdev_vring *vr_descr, + void *config) +{ + int i, ret; + struct trusty_vdev *tvdev; + + tvdev = kzalloc(sizeof(struct trusty_vdev) + + vdev_descr->num_of_vrings * sizeof(struct trusty_vring), + GFP_KERNEL); + if (!tvdev) { + dev_err(tctx->dev, "Failed to allocate VDEV\n"); + return -ENOMEM; + } + + /* setup vdev */ + tvdev->tctx = tctx; + tvdev->vdev.dev.parent = tctx->dev; + tvdev->vdev.id.device = vdev_descr->id; + tvdev->vdev.config = &trusty_virtio_config_ops; + tvdev->vdev_descr = vdev_descr; + tvdev->notifyid = vdev_descr->notifyid; + + /* setup config */ + tvdev->config = config; + tvdev->config_len = vdev_descr->config_len; + + /* setup vrings and vdev resource */ + tvdev->vring_num = vdev_descr->num_of_vrings; + + for (i = 0; i < tvdev->vring_num; i++, vr_descr++) { + struct trusty_vring *tvr = &tvdev->vrings[i]; + tvr->tvdev = tvdev; + tvr->vr_descr = vr_descr; + tvr->align = vr_descr->align; + tvr->elem_num = vr_descr->num; + tvr->notifyid = vr_descr->notifyid; + } + + /* register device */ + ret = register_virtio_device(&tvdev->vdev); + if (ret) { + dev_err(tctx->dev, + "Failed (%d) to register device dev type %u\n", + ret, vdev_descr->id); + goto err_register; + } + + /* add it to tracking list */ + list_add_tail(&tvdev->node, &tctx->vdev_list); + + return 0; + +err_register: + kfree(tvdev); + return ret; +} + +static int trusty_parse_device_descr(struct trusty_ctx *tctx, + void *descr_va, size_t descr_sz) +{ + u32 i; + struct resource_table *descr = descr_va; + + if (descr_sz < sizeof(*descr)) { + dev_err(tctx->dev, "descr table is too small (0x%x)\n", + (int)descr_sz); + return -ENODEV; + } + + if (descr->ver != RSC_DESCR_VER) { + dev_err(tctx->dev, "unexpected descr ver (0x%x)\n", + (int)descr->ver); + return -ENODEV; + } + + if (descr_sz < (sizeof(*descr) + descr->num * sizeof(u32))) { + dev_err(tctx->dev, "descr table is too small (0x%x)\n", + (int)descr->ver); + return -ENODEV; + } + + for (i = 0; i < descr->num; i++) { + struct fw_rsc_hdr *hdr; + struct fw_rsc_vdev *vd; + struct fw_rsc_vdev_vring *vr; + void *cfg; + size_t vd_sz; + + u32 offset = descr->offset[i]; + + if (offset >= descr_sz) { + dev_err(tctx->dev, "offset is out of bounds (%u)\n", + (uint)offset); + return -ENODEV; + } + + /* check space for rsc header */ + if ((descr_sz - offset) < sizeof(struct fw_rsc_hdr)) { + dev_err(tctx->dev, "no space for rsc header (%u)\n", + (uint)offset); + return -ENODEV; + } + hdr = (struct fw_rsc_hdr *)((u8 *)descr + offset); + offset += sizeof(struct fw_rsc_hdr); + + /* check type */ + if (hdr->type != RSC_VDEV) { + dev_err(tctx->dev, "unsupported rsc type (%u)\n", + (uint)hdr->type); + continue; + } + + /* got vdev: check space for vdev */ + if ((descr_sz - offset) < sizeof(struct fw_rsc_vdev)) { + dev_err(tctx->dev, "no space for vdev descr (%u)\n", + (uint)offset); + return -ENODEV; + } + vd = (struct fw_rsc_vdev *)((u8 *)descr + offset); + + /* check space for vrings and config area */ + vd_sz = sizeof(struct fw_rsc_vdev) + + vd->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + + vd->config_len; + + if ((descr_sz - offset) < vd_sz) { + dev_err(tctx->dev, "no space for vdev (%u)\n", + (uint)offset); + return -ENODEV; + } + vr = (struct fw_rsc_vdev_vring *)vd->vring; + cfg = (void *)(vr + vd->num_of_vrings); + + trusty_virtio_add_device(tctx, vd, vr, cfg); + } + + return 0; +} + +static void _remove_devices_locked(struct trusty_ctx *tctx) +{ + struct trusty_vdev *tvdev, *next; + + list_for_each_entry_safe(tvdev, next, &tctx->vdev_list, node) { + list_del(&tvdev->node); + unregister_virtio_device(&tvdev->vdev); + kfree(tvdev); + } +} + +static void trusty_virtio_remove_devices(struct trusty_ctx *tctx) +{ + mutex_lock(&tctx->mlock); + _remove_devices_locked(tctx); + mutex_unlock(&tctx->mlock); +} + +static int trusty_virtio_add_devices(struct trusty_ctx *tctx) +{ + int ret; + void *descr_va; + size_t descr_sz; + size_t descr_buf_sz; + + /* allocate buffer to load device descriptor into */ + descr_buf_sz = PAGE_SIZE; + descr_va = alloc_pages_exact(descr_buf_sz, GFP_KERNEL | __GFP_ZERO); + if (!descr_va) { + dev_err(tctx->dev, "Failed to allocate shared area\n"); + return -ENOMEM; + } + + /* load device descriptors */ + ret = trusty_load_device_descr(tctx, descr_va, descr_buf_sz); + if (ret < 0) { + dev_err(tctx->dev, "failed (%d) to load device descr\n", ret); + goto err_load_descr; + } + + descr_sz = (size_t)ret; + + mutex_lock(&tctx->mlock); + + /* parse device descriptor and add virtio devices */ + ret = trusty_parse_device_descr(tctx, descr_va, descr_sz); + if (ret) { + dev_err(tctx->dev, "failed (%d) to parse device descr\n", ret); + goto err_parse_descr; + } + + /* register call notifier */ + ret = trusty_call_notifier_register(tctx->dev->parent, + &tctx->call_notifier); + if (ret) { + dev_err(tctx->dev, "%s: failed (%d) to register notifier\n", + __func__, ret); + goto err_register_notifier; + } + + /* start virtio */ + ret = trusty_virtio_start(tctx, descr_va, descr_sz); + if (ret) { + dev_err(tctx->dev, "failed (%d) to start virtio\n", ret); + goto err_start_virtio; + } + + /* attach shared area */ + tctx->shared_va = descr_va; + tctx->shared_sz = descr_buf_sz; + + mutex_unlock(&tctx->mlock); + + return 0; + +err_start_virtio: + trusty_call_notifier_unregister(tctx->dev->parent, + &tctx->call_notifier); + cancel_work_sync(&tctx->check_vqs); +err_register_notifier: +err_parse_descr: + _remove_devices_locked(tctx); + mutex_unlock(&tctx->mlock); + cancel_work_sync(&tctx->kick_vqs); + trusty_virtio_stop(tctx, descr_va, descr_sz); +err_load_descr: + free_pages_exact(descr_va, descr_buf_sz); + return ret; +} + +static int trusty_virtio_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_ctx *tctx; + + dev_info(&pdev->dev, "initializing\n"); + + tctx = kzalloc(sizeof(*tctx), GFP_KERNEL); + if (!tctx) { + dev_err(&pdev->dev, "Failed to allocate context\n"); + return -ENOMEM; + } + + tctx->dev = &pdev->dev; + tctx->call_notifier.notifier_call = trusty_call_notify; + mutex_init(&tctx->mlock); + INIT_LIST_HEAD(&tctx->vdev_list); + INIT_WORK(&tctx->check_vqs, check_all_vqs); + INIT_WORK(&tctx->kick_vqs, kick_vqs); + platform_set_drvdata(pdev, tctx); + + ret = trusty_virtio_add_devices(tctx); + if (ret) { + dev_err(&pdev->dev, "Failed to add virtio devices\n"); + goto err_add_devices; + } + + dev_info(&pdev->dev, "initializing done\n"); + return 0; + +err_add_devices: + kfree(tctx); + return ret; +} + +static int trusty_virtio_remove(struct platform_device *pdev) +{ + struct trusty_ctx *tctx = platform_get_drvdata(pdev); + + dev_err(&pdev->dev, "removing\n"); + + /* unregister call notifier and wait until workqueue is done */ + trusty_call_notifier_unregister(tctx->dev->parent, + &tctx->call_notifier); + cancel_work_sync(&tctx->check_vqs); + + /* remove virtio devices */ + trusty_virtio_remove_devices(tctx); + cancel_work_sync(&tctx->kick_vqs); + + /* notify remote that shared area goes away */ + trusty_virtio_stop(tctx, tctx->shared_va, tctx->shared_sz); + + /* free shared area */ + free_pages_exact(tctx->shared_va, tctx->shared_sz); + + /* free context */ + kfree(tctx); + return 0; +} + +static const struct of_device_id trusty_of_match[] = { + { + .compatible = "android,trusty-virtio-v1", + }, +}; + +MODULE_DEVICE_TABLE(of, trusty_of_match); + +static struct platform_driver trusty_virtio_driver = { + .probe = trusty_virtio_probe, + .remove = trusty_virtio_remove, + .driver = { + .name = "trusty-virtio", + .owner = THIS_MODULE, + .of_match_table = trusty_of_match, + }, +}; + +module_platform_driver(trusty_virtio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty virtio driver"); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index aaad5cee6143..a2be2e3579f3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -78,4 +78,12 @@ #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) #define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +/* TRUSTED_OS entity calls */ +#define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) +#define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) +#define SMC_SC_VIRTIO_STOP SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 22) + +#define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23) +#define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) + #endif /* __LINUX_TRUSTY_SMCALL_H */ From ec64affa2ad6c32b61cf99a4fe7df3ea98328202 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Wed, 7 Jan 2015 15:47:37 -0800 Subject: [PATCH 0309/1276] trusty: add trusty-ipc driver Trusty IPC driver provides message passing interface between non-secure side (Linux) and secure side running Trusty. It is handling a set of trusty IPC virtio devices instantiated and configured by trusty-virtio driver based on device description retrieved from secure side. Change-Id: I7249384380850dfb8795c0e0d5e39dfb907400c6 Signed-off-by: Michael Ryleev --- drivers/trusty/Kconfig | 10 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-ipc.c | 1672 +++++++++++++++++++++++++++++ include/linux/trusty/trusty_ipc.h | 88 ++ 4 files changed, 1771 insertions(+) create mode 100644 drivers/trusty/trusty-ipc.c create mode 100644 include/linux/trusty/trusty_ipc.h diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 2255c0a9a815..052cd8e91ab0 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -39,4 +39,14 @@ config TRUSTY_VIRTIO select VIRTIO default y +config TRUSTY_VIRTIO_IPC + tristate "Trusty Virtio IPC driver" + depends on TRUSTY_VIRTIO + default y + help + This module adds support for communications with Trusty Services + + If you choose to build a module, it'll be called trusty-ipc. + Say N if unsure. + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index beb89a87f115..9ca451e50dee 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o +obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c new file mode 100644 index 000000000000..06e026344e67 --- /dev/null +++ b/drivers/trusty/trusty-ipc.c @@ -0,0 +1,1672 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define MAX_DEVICES 4 + +#define REPLY_TIMEOUT 5000 +#define TXBUF_TIMEOUT 15000 + +#define MAX_SRV_NAME_LEN 256 +#define MAX_DEV_NAME_LEN 32 + +#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE +#define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE + +#define TIPC_CTRL_ADDR 53 +#define TIPC_ANY_ADDR 0xFFFFFFFF + +#define TIPC_MIN_LOCAL_ADDR 1024 + +#define TIPC_IOC_MAGIC 'r' +#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *) +#if defined(CONFIG_COMPAT) +#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, \ + compat_uptr_t) +#endif + +struct tipc_virtio_dev; + +struct tipc_dev_config { + u32 msg_buf_max_size; + u32 msg_buf_alignment; + char dev_name[MAX_DEV_NAME_LEN]; +} __packed; + +struct tipc_msg_hdr { + u32 src; + u32 dst; + u32 reserved; + u16 len; + u16 flags; + u8 data[0]; +} __packed; + +enum tipc_ctrl_msg_types { + TIPC_CTRL_MSGTYPE_GO_ONLINE = 1, + TIPC_CTRL_MSGTYPE_GO_OFFLINE, + TIPC_CTRL_MSGTYPE_CONN_REQ, + TIPC_CTRL_MSGTYPE_CONN_RSP, + TIPC_CTRL_MSGTYPE_DISC_REQ, +}; + +struct tipc_ctrl_msg { + u32 type; + u32 body_len; + u8 body[0]; +} __packed; + +struct tipc_conn_req_body { + char name[MAX_SRV_NAME_LEN]; +} __packed; + +struct tipc_conn_rsp_body { + u32 target; + u32 status; + u32 remote; + u32 max_msg_size; + u32 max_msg_cnt; +} __packed; + +struct tipc_disc_req_body { + u32 target; +} __packed; + +struct tipc_cdev_node { + struct cdev cdev; + struct device *dev; + unsigned int minor; +}; + +enum tipc_device_state { + VDS_OFFLINE = 0, + VDS_ONLINE, + VDS_DEAD, +}; + +struct tipc_virtio_dev { + struct kref refcount; + struct mutex lock; /* protects access to this device */ + struct virtio_device *vdev; + struct virtqueue *rxvq; + struct virtqueue *txvq; + uint msg_buf_cnt; + uint msg_buf_max_cnt; + size_t msg_buf_max_sz; + uint free_msg_buf_cnt; + struct list_head free_buf_list; + wait_queue_head_t sendq; + struct idr addr_idr; + enum tipc_device_state state; + struct tipc_cdev_node cdev_node; + char cdev_name[MAX_DEV_NAME_LEN]; +}; + +enum tipc_chan_state { + TIPC_DISCONNECTED = 0, + TIPC_CONNECTING, + TIPC_CONNECTED, + TIPC_STALE, +}; + +struct tipc_chan { + struct mutex lock; /* protects channel state */ + struct kref refcount; + enum tipc_chan_state state; + struct tipc_virtio_dev *vds; + const struct tipc_chan_ops *ops; + void *ops_arg; + u32 remote; + u32 local; + u32 max_msg_size; + u32 max_msg_cnt; + char srv_name[MAX_SRV_NAME_LEN]; +}; + +static struct class *tipc_class; +static unsigned int tipc_major; + +struct virtio_device *default_vdev; + +static DEFINE_IDR(tipc_devices); +static DEFINE_MUTEX(tipc_devices_lock); + +static int _match_any(int id, void *p, void *data) +{ + return id; +} + +static int _match_data(int id, void *p, void *data) +{ + return (p == data); +} + +static void *_alloc_shareable_mem(size_t sz, phys_addr_t *ppa, gfp_t gfp) +{ + return alloc_pages_exact(sz, gfp); +} + +static void _free_shareable_mem(size_t sz, void *va, phys_addr_t pa) +{ + free_pages_exact(va, sz); +} + +static struct tipc_msg_buf *_alloc_msg_buf(size_t sz) +{ + struct tipc_msg_buf *mb; + + /* allocate tracking structure */ + mb = kzalloc(sizeof(struct tipc_msg_buf), GFP_KERNEL); + if (!mb) + return NULL; + + /* allocate buffer that can be shared with secure world */ + mb->buf_va = _alloc_shareable_mem(sz, &mb->buf_pa, GFP_KERNEL); + if (!mb->buf_va) + goto err_alloc; + + mb->buf_sz = sz; + + return mb; + +err_alloc: + kfree(mb); + return NULL; +} + +static void _free_msg_buf(struct tipc_msg_buf *mb) +{ + _free_shareable_mem(mb->buf_sz, mb->buf_va, mb->buf_pa); + kfree(mb); +} + +static void _free_msg_buf_list(struct list_head *list) +{ + struct tipc_msg_buf *mb = NULL; + + mb = list_first_entry_or_null(list, struct tipc_msg_buf, node); + while (mb) { + list_del(&mb->node); + _free_msg_buf(mb); + mb = list_first_entry_or_null(list, struct tipc_msg_buf, node); + } +} + +static inline void mb_reset(struct tipc_msg_buf *mb) +{ + mb->wpos = 0; + mb->rpos = 0; +} + +static void _free_chan(struct kref *kref) +{ + struct tipc_chan *ch = container_of(kref, struct tipc_chan, refcount); + kfree(ch); +} + +static void _free_vds(struct kref *kref) +{ + struct tipc_virtio_dev *vds = + container_of(kref, struct tipc_virtio_dev, refcount); + kfree(vds); +} + +static struct tipc_msg_buf *vds_alloc_msg_buf(struct tipc_virtio_dev *vds) +{ + return _alloc_msg_buf(vds->msg_buf_max_sz); +} + +static void vds_free_msg_buf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + _free_msg_buf(mb); +} + +static bool _put_txbuf_locked(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + list_add_tail(&mb->node, &vds->free_buf_list); + return vds->free_msg_buf_cnt++ == 0; +} + +static struct tipc_msg_buf *_get_txbuf_locked(struct tipc_virtio_dev *vds) +{ + struct tipc_msg_buf *mb; + + if (vds->state != VDS_ONLINE) + return ERR_PTR(-ENODEV); + + if (vds->free_msg_buf_cnt) { + /* take it out of free list */ + mb = list_first_entry(&vds->free_buf_list, + struct tipc_msg_buf, node); + list_del(&mb->node); + vds->free_msg_buf_cnt--; + } else { + if (vds->msg_buf_cnt >= vds->msg_buf_max_cnt) + return ERR_PTR(-EAGAIN); + + /* try to allocate it */ + mb = _alloc_msg_buf(vds->msg_buf_max_sz); + if (!mb) + return ERR_PTR(-ENOMEM); + + vds->msg_buf_cnt++; + } + return mb; +} + +static struct tipc_msg_buf *_vds_get_txbuf(struct tipc_virtio_dev *vds) +{ + struct tipc_msg_buf *mb; + + mutex_lock(&vds->lock); + mb = _get_txbuf_locked(vds); + mutex_unlock(&vds->lock); + + return mb; +} + +static void vds_put_txbuf(struct tipc_virtio_dev *vds, struct tipc_msg_buf *mb) +{ + if (!vds) + return; + + mutex_lock(&vds->lock); + _put_txbuf_locked(vds, mb); + wake_up_interruptible(&vds->sendq); + mutex_unlock(&vds->lock); +} + +static struct tipc_msg_buf *vds_get_txbuf(struct tipc_virtio_dev *vds, + long timeout) +{ + struct tipc_msg_buf *mb; + + if (!vds) + return ERR_PTR(-EINVAL); + + mb = _vds_get_txbuf(vds); + + if ((PTR_ERR(mb) == -EAGAIN) && timeout) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + timeout = msecs_to_jiffies(timeout); + add_wait_queue(&vds->sendq, &wait); + for (;;) { + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); + if (!timeout) { + mb = ERR_PTR(-ETIMEDOUT); + break; + } + + if (signal_pending(current)) { + mb = ERR_PTR(-ERESTARTSYS); + break; + } + + mb = _vds_get_txbuf(vds); + if (PTR_ERR(mb) != -EAGAIN) + break; + } + remove_wait_queue(&vds->sendq, &wait); + } + + if (IS_ERR(mb)) + return mb; + + BUG_ON(!mb); + + /* reset and reserve space for message header */ + mb_reset(mb); + mb_put_data(mb, sizeof(struct tipc_msg_hdr)); + + return mb; +} + +static int vds_queue_txbuf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + int err; + struct scatterlist sg; + bool need_notify = false; + + if (!vds) + return -EINVAL; + + mutex_lock(&vds->lock); + if (vds->state == VDS_ONLINE) { + sg_init_one(&sg, mb->buf_va, mb->wpos); + err = virtqueue_add_outbuf(vds->txvq, &sg, 1, mb, GFP_KERNEL); + need_notify = virtqueue_kick_prepare(vds->txvq); + } else { + err = -ENODEV; + } + mutex_unlock(&vds->lock); + + if (need_notify) + virtqueue_notify(vds->txvq); + + return err; +} + +static int vds_add_channel(struct tipc_virtio_dev *vds, + struct tipc_chan *chan) +{ + int ret; + + mutex_lock(&vds->lock); + if (vds->state == VDS_ONLINE) { + ret = idr_alloc(&vds->addr_idr, chan, + TIPC_MIN_LOCAL_ADDR, TIPC_ANY_ADDR - 1, + GFP_KERNEL); + if (ret > 0) { + chan->local = ret; + kref_get(&chan->refcount); + ret = 0; + } + } else { + ret = -EINVAL; + } + mutex_unlock(&vds->lock); + + return ret; +} + +static void vds_del_channel(struct tipc_virtio_dev *vds, + struct tipc_chan *chan) +{ + mutex_lock(&vds->lock); + if (chan->local) { + idr_remove(&vds->addr_idr, chan->local); + chan->local = 0; + chan->remote = 0; + kref_put(&chan->refcount, _free_chan); + } + mutex_unlock(&vds->lock); +} + +static struct tipc_chan *vds_lookup_channel(struct tipc_virtio_dev *vds, + u32 addr) +{ + int id; + struct tipc_chan *chan = NULL; + + mutex_lock(&vds->lock); + if (addr == TIPC_ANY_ADDR) { + id = idr_for_each(&vds->addr_idr, _match_any, NULL); + if (id > 0) + chan = idr_find(&vds->addr_idr, id); + } else { + chan = idr_find(&vds->addr_idr, addr); + } + if (chan) + kref_get(&chan->refcount); + mutex_unlock(&vds->lock); + + return chan; +} + +static struct tipc_chan *vds_create_channel(struct tipc_virtio_dev *vds, + const struct tipc_chan_ops *ops, + void *ops_arg) +{ + int ret; + struct tipc_chan *chan = NULL; + + if (!vds) + return ERR_PTR(-ENOENT); + + if (!ops) + return ERR_PTR(-EINVAL); + + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return ERR_PTR(-ENOMEM); + + kref_get(&vds->refcount); + chan->vds = vds; + chan->ops = ops; + chan->ops_arg = ops_arg; + mutex_init(&chan->lock); + kref_init(&chan->refcount); + chan->state = TIPC_DISCONNECTED; + + ret = vds_add_channel(vds, chan); + if (ret) { + kfree(chan); + kref_put(&vds->refcount, _free_vds); + return ERR_PTR(ret); + } + + return chan; +} + +static void fill_msg_hdr(struct tipc_msg_buf *mb, u32 src, u32 dst) +{ + struct tipc_msg_hdr *hdr = mb_get_data(mb, sizeof(*hdr)); + + hdr->src = src; + hdr->dst = dst; + hdr->len = mb_avail_data(mb); + hdr->flags = 0; + hdr->reserved = 0; +} + +/*****************************************************************************/ + +struct tipc_chan *tipc_create_channel(struct device *dev, + const struct tipc_chan_ops *ops, + void *ops_arg) +{ + struct virtio_device *vd; + struct tipc_chan *chan; + struct tipc_virtio_dev *vds; + + mutex_lock(&tipc_devices_lock); + if (dev) { + vd = container_of(dev, struct virtio_device, dev); + } else { + vd = default_vdev; + if (!vd) { + mutex_unlock(&tipc_devices_lock); + return ERR_PTR(-ENOENT); + } + } + vds = vd->priv; + kref_get(&vds->refcount); + mutex_unlock(&tipc_devices_lock); + + chan = vds_create_channel(vds, ops, ops_arg); + kref_put(&vds->refcount, _free_vds); + return chan; +} +EXPORT_SYMBOL(tipc_create_channel); + +struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan) +{ + return vds_alloc_msg_buf(chan->vds); +} +EXPORT_SYMBOL(tipc_chan_get_rxbuf); + +void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + vds_free_msg_buf(chan->vds, mb); +} +EXPORT_SYMBOL(tipc_chan_put_rxbuf); + +struct tipc_msg_buf *tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, + long timeout) +{ + return vds_get_txbuf(chan->vds, timeout); +} +EXPORT_SYMBOL(tipc_chan_get_txbuf_timeout); + +void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + vds_put_txbuf(chan->vds, mb); +} +EXPORT_SYMBOL(tipc_chan_put_txbuf); + +int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + int err; + + mutex_lock(&chan->lock); + switch (chan->state) { + case TIPC_CONNECTED: + fill_msg_hdr(mb, chan->local, chan->remote); + err = vds_queue_txbuf(chan->vds, mb); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } + break; + case TIPC_DISCONNECTED: + case TIPC_CONNECTING: + err = -ENOTCONN; + break; + case TIPC_STALE: + err = -ESHUTDOWN; + break; + default: + err = -EBADFD; + pr_err("%s: unexpected channel state %d\n", + __func__, chan->state); + } + mutex_unlock(&chan->lock); + return err; +} +EXPORT_SYMBOL(tipc_chan_queue_msg); + + +int tipc_chan_connect(struct tipc_chan *chan, const char *name) +{ + int err; + struct tipc_ctrl_msg *msg; + struct tipc_conn_req_body *body; + struct tipc_msg_buf *txbuf; + + txbuf = vds_get_txbuf(chan->vds, TXBUF_TIMEOUT); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + /* reserve space for connection request control message */ + msg = mb_put_data(txbuf, sizeof(*msg) + sizeof(*body)); + body = (struct tipc_conn_req_body *)msg->body; + + /* fill message */ + msg->type = TIPC_CTRL_MSGTYPE_CONN_REQ; + msg->body_len = sizeof(*body); + + strncpy(body->name, name, sizeof(body->name)); + body->name[sizeof(body->name)-1] = '\0'; + + mutex_lock(&chan->lock); + switch (chan->state) { + case TIPC_DISCONNECTED: + /* save service name we are connecting to */ + strcpy(chan->srv_name, body->name); + + fill_msg_hdr(txbuf, chan->local, TIPC_CTRL_ADDR); + err = vds_queue_txbuf(chan->vds, txbuf); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } else { + chan->state = TIPC_CONNECTING; + txbuf = NULL; /* prevents discarding buffer */ + } + break; + case TIPC_CONNECTED: + case TIPC_CONNECTING: + /* check if we are trying to connect to the same service */ + if (strcmp(chan->srv_name, body->name) == 0) + err = 0; + else + if (chan->state == TIPC_CONNECTING) + err = -EALREADY; /* in progress */ + else + err = -EISCONN; /* already connected */ + break; + + case TIPC_STALE: + err = -ESHUTDOWN; + break; + default: + err = -EBADFD; + pr_err("%s: unexpected channel state %d\n", + __func__, chan->state); + break; + } + mutex_unlock(&chan->lock); + + if (txbuf) + tipc_chan_put_txbuf(chan, txbuf); /* discard it */ + + return err; +} +EXPORT_SYMBOL(tipc_chan_connect); + +int tipc_chan_shutdown(struct tipc_chan *chan) +{ + int err; + struct tipc_ctrl_msg *msg; + struct tipc_disc_req_body *body; + struct tipc_msg_buf *txbuf = NULL; + + /* get tx buffer */ + txbuf = vds_get_txbuf(chan->vds, TXBUF_TIMEOUT); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTED || chan->state == TIPC_CONNECTING) { + /* reserve space for disconnect request control message */ + msg = mb_put_data(txbuf, sizeof(*msg) + sizeof(*body)); + body = (struct tipc_disc_req_body *)msg->body; + + msg->type = TIPC_CTRL_MSGTYPE_DISC_REQ; + msg->body_len = sizeof(*body); + body->target = chan->remote; + + fill_msg_hdr(txbuf, chan->local, TIPC_CTRL_ADDR); + err = vds_queue_txbuf(chan->vds, txbuf); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } + } else { + err = -ENOTCONN; + } + chan->state = TIPC_STALE; + mutex_unlock(&chan->lock); + + if (err) { + /* release buffer */ + tipc_chan_put_txbuf(chan, txbuf); + } + + return err; +} +EXPORT_SYMBOL(tipc_chan_shutdown); + +void tipc_chan_destroy(struct tipc_chan *chan) +{ + mutex_lock(&chan->lock); + if (chan->vds) { + vds_del_channel(chan->vds, chan); + kref_put(&chan->vds->refcount, _free_vds); + chan->vds = NULL; + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); +} +EXPORT_SYMBOL(tipc_chan_destroy); + +/***************************************************************************/ + +struct tipc_dn_chan { + int state; + struct mutex lock; /* protects rx_msg_queue list and channel state */ + struct tipc_chan *chan; + wait_queue_head_t readq; + struct completion reply_comp; + struct list_head rx_msg_queue; +}; + +static int dn_wait_for_reply(struct tipc_dn_chan *dn, int timeout) +{ + int ret; + + ret = wait_for_completion_interruptible_timeout(&dn->reply_comp, + msecs_to_jiffies(timeout)); + if (ret < 0) + return ret; + + mutex_lock(&dn->lock); + if (!ret) { + /* no reply from remote */ + dn->state = TIPC_STALE; + ret = -ETIMEDOUT; + } else { + /* got reply */ + if (dn->state == TIPC_CONNECTED) + ret = 0; + else if (dn->state == TIPC_DISCONNECTED) + if (!list_empty(&dn->rx_msg_queue)) + ret = 0; + else + ret = -ENOTCONN; + else + ret = -EIO; + } + mutex_unlock(&dn->lock); + + return ret; +} + +struct tipc_msg_buf *dn_handle_msg(void *data, struct tipc_msg_buf *rxbuf) +{ + struct tipc_dn_chan *dn = data; + struct tipc_msg_buf *newbuf = rxbuf; + + mutex_lock(&dn->lock); + if (dn->state == TIPC_CONNECTED) { + /* get new buffer */ + newbuf = tipc_chan_get_rxbuf(dn->chan); + if (newbuf) { + /* queue an old buffer and return a new one */ + list_add_tail(&rxbuf->node, &dn->rx_msg_queue); + wake_up_interruptible(&dn->readq); + } else { + /* + * return an old buffer effectively discarding + * incoming message + */ + pr_err("%s: discard incoming message\n", __func__); + newbuf = rxbuf; + } + } + mutex_unlock(&dn->lock); + + return newbuf; +} + +static void dn_connected(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + dn->state = TIPC_CONNECTED; + + /* complete all pending */ + complete(&dn->reply_comp); + + mutex_unlock(&dn->lock); +} + +static void dn_disconnected(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + dn->state = TIPC_DISCONNECTED; + + /* complete all pending */ + complete(&dn->reply_comp); + + /* wakeup all readers */ + wake_up_interruptible_all(&dn->readq); + + mutex_unlock(&dn->lock); +} + +static void dn_shutdown(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + + /* set state to STALE */ + dn->state = TIPC_STALE; + + /* complete all pending */ + complete(&dn->reply_comp); + + /* wakeup all readers */ + wake_up_interruptible_all(&dn->readq); + + mutex_unlock(&dn->lock); +} + +static void dn_handle_event(void *data, int event) +{ + struct tipc_dn_chan *dn = data; + + switch (event) { + case TIPC_CHANNEL_SHUTDOWN: + dn_shutdown(dn); + break; + + case TIPC_CHANNEL_DISCONNECTED: + dn_disconnected(dn); + break; + + case TIPC_CHANNEL_CONNECTED: + dn_connected(dn); + break; + + default: + pr_err("%s: unhandled event %d\n", __func__, event); + break; + } +} + +static struct tipc_chan_ops _dn_ops = { + .handle_msg = dn_handle_msg, + .handle_event = dn_handle_event, +}; + +#define cdev_to_cdn(c) container_of((c), struct tipc_cdev_node, cdev) +#define cdn_to_vds(cdn) container_of((cdn), struct tipc_virtio_dev, cdev_node) + +static struct tipc_virtio_dev *_dn_lookup_vds(struct tipc_cdev_node *cdn) +{ + int ret; + struct tipc_virtio_dev *vds = NULL; + + mutex_lock(&tipc_devices_lock); + ret = idr_for_each(&tipc_devices, _match_data, cdn); + if (ret) { + vds = cdn_to_vds(cdn); + kref_get(&vds->refcount); + } + mutex_unlock(&tipc_devices_lock); + return vds; +} + +static int tipc_open(struct inode *inode, struct file *filp) +{ + int ret; + struct tipc_virtio_dev *vds; + struct tipc_dn_chan *dn; + struct tipc_cdev_node *cdn = cdev_to_cdn(inode->i_cdev); + + vds = _dn_lookup_vds(cdn); + if (!vds) { + ret = -ENOENT; + goto err_vds_lookup; + } + + dn = kzalloc(sizeof(*dn), GFP_KERNEL); + if (!dn) { + ret = -ENOMEM; + goto err_alloc_chan; + } + + mutex_init(&dn->lock); + init_waitqueue_head(&dn->readq); + init_completion(&dn->reply_comp); + INIT_LIST_HEAD(&dn->rx_msg_queue); + + dn->state = TIPC_DISCONNECTED; + + dn->chan = vds_create_channel(vds, &_dn_ops, dn); + if (IS_ERR(dn->chan)) { + ret = PTR_ERR(dn->chan); + goto err_create_chan; + } + + filp->private_data = dn; + kref_put(&vds->refcount, _free_vds); + return 0; + +err_create_chan: + kfree(dn); +err_alloc_chan: + kref_put(&vds->refcount, _free_vds); +err_vds_lookup: + return ret; +} + + +static int dn_connect_ioctl(struct tipc_dn_chan *dn, char __user *usr_name) +{ + int err; + char name[MAX_SRV_NAME_LEN]; + + /* copy in service name from user space */ + err = strncpy_from_user(name, usr_name, sizeof(name)); + if (err < 0) { + pr_err("%s: copy_from_user (%p) failed (%d)\n", + __func__, usr_name, err); + return err; + } + name[sizeof(name)-1] = '\0'; + + /* send connect request */ + err = tipc_chan_connect(dn->chan, name); + if (err) + return err; + + /* and wait for reply */ + return dn_wait_for_reply(dn, REPLY_TIMEOUT); +} + +static long tipc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret; + struct tipc_dn_chan *dn = filp->private_data; + + if (_IOC_TYPE(cmd) != TIPC_IOC_MAGIC) + return -EINVAL; + + switch (cmd) { + case TIPC_IOC_CONNECT: + ret = dn_connect_ioctl(dn, (char __user *)arg); + break; + default: + pr_warn("%s: Unhandled ioctl cmd: 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } + return ret; +} + +#if defined(CONFIG_COMPAT) +static long tipc_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret; + struct tipc_dn_chan *dn = filp->private_data; + void __user *user_req = compat_ptr(arg); + + if (_IOC_TYPE(cmd) != TIPC_IOC_MAGIC) + return -EINVAL; + + switch (cmd) { + case TIPC_IOC_CONNECT_COMPAT: + ret = dn_connect_ioctl(dn, user_req); + break; + default: + pr_warn("%s: Unhandled ioctl cmd: 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } + return ret; +} +#endif + +static inline bool _got_rx(struct tipc_dn_chan *dn) +{ + if (dn->state != TIPC_CONNECTED) + return true; + + if (!list_empty(&dn->rx_msg_queue)) + return true; + + return false; +} + +static ssize_t tipc_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + size_t len; + struct tipc_msg_buf *mb; + struct file *filp = iocb->ki_filp; + struct tipc_dn_chan *dn = filp->private_data; + + mutex_lock(&dn->lock); + + while (list_empty(&dn->rx_msg_queue)) { + if (dn->state != TIPC_CONNECTED) { + if (dn->state == TIPC_CONNECTING) + ret = -ENOTCONN; + else if (dn->state == TIPC_DISCONNECTED) + ret = -ENOTCONN; + else if (dn->state == TIPC_STALE) + ret = -ESHUTDOWN; + else + ret = -EBADFD; + goto out; + } + + mutex_unlock(&dn->lock); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(dn->readq, _got_rx(dn))) + return -ERESTARTSYS; + + mutex_lock(&dn->lock); + } + + mb = list_first_entry(&dn->rx_msg_queue, struct tipc_msg_buf, node); + + len = mb_avail_data(mb); + if (len > iov_iter_count(iter)) { + ret = -EMSGSIZE; + goto out; + } + + if (copy_to_iter(mb_get_data(mb, len), len, iter) != len) { + ret = -EFAULT; + goto out; + } + + ret = len; + list_del(&mb->node); + tipc_chan_put_rxbuf(dn->chan, mb); + +out: + mutex_unlock(&dn->lock); + return ret; +} + +static ssize_t tipc_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + size_t len; + long timeout = TXBUF_TIMEOUT; + struct tipc_msg_buf *txbuf = NULL; + struct file *filp = iocb->ki_filp; + struct tipc_dn_chan *dn = filp->private_data; + + if (filp->f_flags & O_NONBLOCK) + timeout = 0; + + txbuf = tipc_chan_get_txbuf_timeout(dn->chan, timeout); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + /* message length */ + len = iov_iter_count(iter); + + /* check available space */ + if (len > mb_avail_space(txbuf)) { + ret = -EMSGSIZE; + goto err_out; + } + + /* copy in message data */ + if (copy_from_iter(mb_put_data(txbuf, len), len, iter) != len) { + ret = -EFAULT; + goto err_out; + } + + /* queue message */ + ret = tipc_chan_queue_msg(dn->chan, txbuf); + if (ret) + goto err_out; + + return len; + +err_out: + tipc_chan_put_txbuf(dn->chan, txbuf); + return ret; +} + +static unsigned int tipc_poll(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + struct tipc_dn_chan *dn = filp->private_data; + + mutex_lock(&dn->lock); + + poll_wait(filp, &dn->readq, wait); + + /* Writes always succeed for now */ + mask |= POLLOUT | POLLWRNORM; + + if (!list_empty(&dn->rx_msg_queue)) + mask |= POLLIN | POLLRDNORM; + + if (dn->state != TIPC_CONNECTED) + mask |= POLLERR; + + mutex_unlock(&dn->lock); + return mask; +} + + +static int tipc_release(struct inode *inode, struct file *filp) +{ + struct tipc_dn_chan *dn = filp->private_data; + + dn_shutdown(dn); + + /* free all pending buffers */ + _free_msg_buf_list(&dn->rx_msg_queue); + + /* shutdown channel */ + tipc_chan_shutdown(dn->chan); + + /* and destroy it */ + tipc_chan_destroy(dn->chan); + + kfree(dn); + + return 0; +} + +static const struct file_operations tipc_fops = { + .open = tipc_open, + .release = tipc_release, + .unlocked_ioctl = tipc_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = tipc_compat_ioctl, +#endif + .read_iter = tipc_read_iter, + .write_iter = tipc_write_iter, + .poll = tipc_poll, + .owner = THIS_MODULE, +}; + +/*****************************************************************************/ + +static void chan_trigger_event(struct tipc_chan *chan, int event) +{ + if (!event) + return; + + chan->ops->handle_event(chan->ops_arg, event); +} + +static void _cleanup_vq(struct virtqueue *vq) +{ + struct tipc_msg_buf *mb; + + while ((mb = virtqueue_detach_unused_buf(vq)) != NULL) + _free_msg_buf(mb); +} + +static int _create_cdev_node(struct device *parent, + struct tipc_cdev_node *cdn, + const char *name) +{ + int ret; + dev_t devt; + + if (!name) { + dev_dbg(parent, "%s: cdev name has to be provided\n", + __func__); + return -EINVAL; + } + + /* allocate minor */ + ret = idr_alloc(&tipc_devices, cdn, 0, MAX_DEVICES-1, GFP_KERNEL); + if (ret < 0) { + dev_dbg(parent, "%s: failed (%d) to get id\n", + __func__, ret); + return ret; + } + + cdn->minor = ret; + cdev_init(&cdn->cdev, &tipc_fops); + cdn->cdev.owner = THIS_MODULE; + + /* Add character device */ + devt = MKDEV(tipc_major, cdn->minor); + ret = cdev_add(&cdn->cdev, devt, 1); + if (ret) { + dev_dbg(parent, "%s: cdev_add failed (%d)\n", + __func__, ret); + goto err_add_cdev; + } + + /* Create a device node */ + cdn->dev = device_create(tipc_class, parent, + devt, NULL, "trusty-ipc-%s", name); + if (IS_ERR(cdn->dev)) { + ret = PTR_ERR(cdn->dev); + dev_dbg(parent, "%s: device_create failed: %d\n", + __func__, ret); + goto err_device_create; + } + + return 0; + +err_device_create: + cdn->dev = NULL; + cdev_del(&cdn->cdev); +err_add_cdev: + idr_remove(&tipc_devices, cdn->minor); + return ret; +} + +static void create_cdev_node(struct tipc_virtio_dev *vds, + struct tipc_cdev_node *cdn) +{ + int err; + + mutex_lock(&tipc_devices_lock); + + if (!default_vdev) { + kref_get(&vds->refcount); + default_vdev = vds->vdev; + } + + if (vds->cdev_name[0] && !cdn->dev) { + kref_get(&vds->refcount); + err = _create_cdev_node(&vds->vdev->dev, cdn, vds->cdev_name); + if (err) { + dev_err(&vds->vdev->dev, + "failed (%d) to create cdev node\n", err); + kref_put(&vds->refcount, _free_vds); + } + } + mutex_unlock(&tipc_devices_lock); +} + +static void destroy_cdev_node(struct tipc_virtio_dev *vds, + struct tipc_cdev_node *cdn) +{ + mutex_lock(&tipc_devices_lock); + if (cdn->dev) { + device_destroy(tipc_class, MKDEV(tipc_major, cdn->minor)); + cdev_del(&cdn->cdev); + idr_remove(&tipc_devices, cdn->minor); + cdn->dev = NULL; + kref_put(&vds->refcount, _free_vds); + } + + if (default_vdev == vds->vdev) { + default_vdev = NULL; + kref_put(&vds->refcount, _free_vds); + } + + mutex_unlock(&tipc_devices_lock); +} + +static void _go_online(struct tipc_virtio_dev *vds) +{ + mutex_lock(&vds->lock); + if (vds->state == VDS_OFFLINE) + vds->state = VDS_ONLINE; + mutex_unlock(&vds->lock); + + create_cdev_node(vds, &vds->cdev_node); + + dev_info(&vds->vdev->dev, "is online\n"); +} + +static void _go_offline(struct tipc_virtio_dev *vds) +{ + struct tipc_chan *chan; + + /* change state to OFFLINE */ + mutex_lock(&vds->lock); + if (vds->state != VDS_ONLINE) { + mutex_unlock(&vds->lock); + return; + } + vds->state = VDS_OFFLINE; + mutex_unlock(&vds->lock); + + /* wakeup all waiters */ + wake_up_interruptible_all(&vds->sendq); + + /* shutdown all channels */ + while ((chan = vds_lookup_channel(vds, TIPC_ANY_ADDR))) { + mutex_lock(&chan->lock); + chan->state = TIPC_STALE; + chan->remote = 0; + chan_trigger_event(chan, TIPC_CHANNEL_SHUTDOWN); + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } + + /* shutdown device node */ + destroy_cdev_node(vds, &vds->cdev_node); + + dev_info(&vds->vdev->dev, "is offline\n"); +} + +static void _handle_conn_rsp(struct tipc_virtio_dev *vds, + struct tipc_conn_rsp_body *rsp, size_t len) +{ + struct tipc_chan *chan; + + if (sizeof(*rsp) != len) { + dev_err(&vds->vdev->dev, "%s: Invalid response length %zd\n", + __func__, len); + return; + } + + dev_dbg(&vds->vdev->dev, + "%s: connection response: for addr 0x%x: " + "status %d remote addr 0x%x\n", + __func__, rsp->target, rsp->status, rsp->remote); + + /* Lookup channel */ + chan = vds_lookup_channel(vds, rsp->target); + if (chan) { + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTING) { + if (!rsp->status) { + chan->state = TIPC_CONNECTED; + chan->remote = rsp->remote; + chan->max_msg_cnt = rsp->max_msg_cnt; + chan->max_msg_size = rsp->max_msg_size; + chan_trigger_event(chan, + TIPC_CHANNEL_CONNECTED); + } else { + chan->state = TIPC_DISCONNECTED; + chan->remote = 0; + chan_trigger_event(chan, + TIPC_CHANNEL_DISCONNECTED); + } + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } +} + +static void _handle_disc_req(struct tipc_virtio_dev *vds, + struct tipc_disc_req_body *req, size_t len) +{ + struct tipc_chan *chan; + + if (sizeof(*req) != len) { + dev_err(&vds->vdev->dev, "%s: Invalid request length %zd\n", + __func__, len); + return; + } + + dev_dbg(&vds->vdev->dev, "%s: disconnect request: for addr 0x%x\n", + __func__, req->target); + + chan = vds_lookup_channel(vds, req->target); + if (chan) { + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTED || + chan->state == TIPC_CONNECTING) { + chan->state = TIPC_DISCONNECTED; + chan->remote = 0; + chan_trigger_event(chan, TIPC_CHANNEL_DISCONNECTED); + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } +} + +static void _handle_ctrl_msg(struct tipc_virtio_dev *vds, + void *data, int len, u32 src) +{ + struct tipc_ctrl_msg *msg = data; + + if ((len < sizeof(*msg)) || (sizeof(*msg) + msg->body_len != len)) { + dev_err(&vds->vdev->dev, + "%s: Invalid message length ( %d vs. %d)\n", + __func__, (int)(sizeof(*msg) + msg->body_len), len); + return; + } + + dev_dbg(&vds->vdev->dev, + "%s: Incoming ctrl message: src 0x%x type %d len %d\n", + __func__, src, msg->type, msg->body_len); + + switch (msg->type) { + case TIPC_CTRL_MSGTYPE_GO_ONLINE: + _go_online(vds); + break; + + case TIPC_CTRL_MSGTYPE_GO_OFFLINE: + _go_offline(vds); + break; + + case TIPC_CTRL_MSGTYPE_CONN_RSP: + _handle_conn_rsp(vds, (struct tipc_conn_rsp_body *)msg->body, + msg->body_len); + break; + + case TIPC_CTRL_MSGTYPE_DISC_REQ: + _handle_disc_req(vds, (struct tipc_disc_req_body *)msg->body, + msg->body_len); + break; + + default: + dev_warn(&vds->vdev->dev, + "%s: Unexpected message type: %d\n", + __func__, msg->type); + } +} + +static int _handle_rxbuf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *rxbuf, size_t rxlen) +{ + int err; + struct scatterlist sg; + struct tipc_msg_hdr *msg; + struct device *dev = &vds->vdev->dev; + + /* message sanity check */ + if (rxlen > rxbuf->buf_sz) { + dev_warn(dev, "inbound msg is too big: %zd\n", rxlen); + goto drop_it; + } + + if (rxlen < sizeof(*msg)) { + dev_warn(dev, "inbound msg is too short: %zd\n", rxlen); + goto drop_it; + } + + /* reset buffer and put data */ + mb_reset(rxbuf); + mb_put_data(rxbuf, rxlen); + + /* get message header */ + msg = mb_get_data(rxbuf, sizeof(*msg)); + if (mb_avail_data(rxbuf) != msg->len) { + dev_warn(dev, "inbound msg length mismatch: (%d vs. %d)\n", + (uint) mb_avail_data(rxbuf), (uint)msg->len); + goto drop_it; + } + + dev_dbg(dev, "From: %d, To: %d, Len: %d, Flags: 0x%x, Reserved: %d\n", + msg->src, msg->dst, msg->len, msg->flags, msg->reserved); + + /* message directed to control endpoint is a special case */ + if (msg->dst == TIPC_CTRL_ADDR) { + _handle_ctrl_msg(vds, msg->data, msg->len, msg->src); + } else { + struct tipc_chan *chan = NULL; + /* Lookup channel */ + chan = vds_lookup_channel(vds, msg->dst); + if (chan) { + /* handle it */ + rxbuf = chan->ops->handle_msg(chan->ops_arg, rxbuf); + BUG_ON(!rxbuf); + kref_put(&chan->refcount, _free_chan); + } + } + +drop_it: + /* add the buffer back to the virtqueue */ + sg_init_one(&sg, rxbuf->buf_va, rxbuf->buf_sz); + err = virtqueue_add_inbuf(vds->rxvq, &sg, 1, rxbuf, GFP_KERNEL); + if (err < 0) { + dev_err(dev, "failed to add a virtqueue buffer: %d\n", err); + return err; + } + + return 0; +} + +static void _rxvq_cb(struct virtqueue *rxvq) +{ + unsigned int len; + struct tipc_msg_buf *mb; + unsigned int msg_cnt = 0; + struct tipc_virtio_dev *vds = rxvq->vdev->priv; + + while ((mb = virtqueue_get_buf(rxvq, &len)) != NULL) { + if (_handle_rxbuf(vds, mb, len)) + break; + msg_cnt++; + } + + /* tell the other size that we added rx buffers */ + if (msg_cnt) + virtqueue_kick(rxvq); +} + +static void _txvq_cb(struct virtqueue *txvq) +{ + unsigned int len; + struct tipc_msg_buf *mb; + bool need_wakeup = false; + struct tipc_virtio_dev *vds = txvq->vdev->priv; + + dev_dbg(&txvq->vdev->dev, "%s\n", __func__); + + /* detach all buffers */ + mutex_lock(&vds->lock); + while ((mb = virtqueue_get_buf(txvq, &len)) != NULL) + need_wakeup |= _put_txbuf_locked(vds, mb); + mutex_unlock(&vds->lock); + + if (need_wakeup) { + /* wake up potential senders waiting for a tx buffer */ + wake_up_interruptible_all(&vds->sendq); + } +} + +static int tipc_virtio_probe(struct virtio_device *vdev) +{ + int err, i; + struct tipc_virtio_dev *vds; + struct tipc_dev_config config; + struct virtqueue *vqs[2]; + vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; + const char *vq_names[] = { "rx", "tx" }; + + dev_dbg(&vdev->dev, "%s:\n", __func__); + + vds = kzalloc(sizeof(*vds), GFP_KERNEL); + if (!vds) + return -ENOMEM; + + vds->vdev = vdev; + + mutex_init(&vds->lock); + kref_init(&vds->refcount); + init_waitqueue_head(&vds->sendq); + INIT_LIST_HEAD(&vds->free_buf_list); + idr_init(&vds->addr_idr); + + /* set default max message size and alignment */ + memset(&config, 0, sizeof(config)); + config.msg_buf_max_size = DEFAULT_MSG_BUF_SIZE; + config.msg_buf_alignment = DEFAULT_MSG_BUF_ALIGN; + + /* get configuration if present */ + vdev->config->get(vdev, 0, &config, sizeof(config)); + + /* copy dev name */ + strncpy(vds->cdev_name, config.dev_name, sizeof(vds->cdev_name)); + vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; + + /* find tx virtqueues (rx and tx and in this order) */ + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); + if (err) + goto err_find_vqs; + + vds->rxvq = vqs[0]; + vds->txvq = vqs[1]; + + /* save max buffer size and count */ + vds->msg_buf_max_sz = config.msg_buf_max_size; + vds->msg_buf_max_cnt = virtqueue_get_vring_size(vds->txvq); + + /* set up the receive buffers */ + for (i = 0; i < virtqueue_get_vring_size(vds->rxvq); i++) { + struct scatterlist sg; + struct tipc_msg_buf *rxbuf; + + rxbuf = _alloc_msg_buf(vds->msg_buf_max_sz); + if (!rxbuf) { + dev_err(&vdev->dev, "failed to allocate rx buffer\n"); + err = -ENOMEM; + goto err_free_rx_buffers; + } + + sg_init_one(&sg, rxbuf->buf_va, rxbuf->buf_sz); + err = virtqueue_add_inbuf(vds->rxvq, &sg, 1, rxbuf, GFP_KERNEL); + WARN_ON(err); /* sanity check; this can't really happen */ + } + + vdev->priv = vds; + vds->state = VDS_OFFLINE; + + dev_dbg(&vdev->dev, "%s: done\n", __func__); + return 0; + +err_free_rx_buffers: + _cleanup_vq(vds->rxvq); +err_find_vqs: + kref_put(&vds->refcount, _free_vds); + return err; +} + +static void tipc_virtio_remove(struct virtio_device *vdev) +{ + struct tipc_virtio_dev *vds = vdev->priv; + + _go_offline(vds); + + mutex_lock(&vds->lock); + vds->state = VDS_DEAD; + vds->vdev = NULL; + mutex_unlock(&vds->lock); + + vdev->config->reset(vdev); + + idr_destroy(&vds->addr_idr); + + _cleanup_vq(vds->rxvq); + _cleanup_vq(vds->txvq); + _free_msg_buf_list(&vds->free_buf_list); + + vdev->config->del_vqs(vds->vdev); + + kref_put(&vds->refcount, _free_vds); +} + +static struct virtio_device_id tipc_virtio_id_table[] = { + { VIRTIO_ID_TRUSTY_IPC, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + 0, +}; + +static struct virtio_driver virtio_tipc_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = tipc_virtio_id_table, + .probe = tipc_virtio_probe, + .remove = tipc_virtio_remove, +}; + +static int __init tipc_init(void) +{ + int ret; + dev_t dev; + + ret = alloc_chrdev_region(&dev, 0, MAX_DEVICES, KBUILD_MODNAME); + if (ret) { + pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret); + return ret; + } + + tipc_major = MAJOR(dev); + tipc_class = class_create(THIS_MODULE, KBUILD_MODNAME); + if (IS_ERR(tipc_class)) { + ret = PTR_ERR(tipc_class); + pr_err("%s: class_create failed: %d\n", __func__, ret); + goto err_class_create; + } + + ret = register_virtio_driver(&virtio_tipc_driver); + if (ret) { + pr_err("failed to register virtio driver: %d\n", ret); + goto err_register_virtio_drv; + } + + return 0; + +err_register_virtio_drv: + class_destroy(tipc_class); + +err_class_create: + unregister_chrdev_region(dev, MAX_DEVICES); + return ret; +} + +static void __exit tipc_exit(void) +{ + unregister_virtio_driver(&virtio_tipc_driver); + class_destroy(tipc_class); + unregister_chrdev_region(MKDEV(tipc_major, 0), MAX_DEVICES); +} + +/* We need to init this early */ +subsys_initcall(tipc_init); +module_exit(tipc_exit); + +MODULE_DEVICE_TABLE(tipc, tipc_virtio_id_table); +MODULE_DESCRIPTION("Trusty IPC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/trusty/trusty_ipc.h b/include/linux/trusty/trusty_ipc.h new file mode 100644 index 000000000000..4ca15938a854 --- /dev/null +++ b/include/linux/trusty/trusty_ipc.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_TRUSTY_TRUSTY_IPC_H +#define __LINUX_TRUSTY_TRUSTY_IPC_H + +struct tipc_chan; + +struct tipc_msg_buf { + void *buf_va; + phys_addr_t buf_pa; + size_t buf_sz; + size_t wpos; + size_t rpos; + struct list_head node; +}; + +enum tipc_chan_event { + TIPC_CHANNEL_CONNECTED = 1, + TIPC_CHANNEL_DISCONNECTED, + TIPC_CHANNEL_SHUTDOWN, +}; + +struct tipc_chan_ops { + void (*handle_event)(void *cb_arg, int event); + struct tipc_msg_buf *(*handle_msg)(void *cb_arg, + struct tipc_msg_buf *mb); +}; + +struct tipc_chan *tipc_create_channel(struct device *dev, + const struct tipc_chan_ops *ops, + void *cb_arg); + +int tipc_chan_connect(struct tipc_chan *chan, const char *port); + +int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +int tipc_chan_shutdown(struct tipc_chan *chan); + +void tipc_chan_destroy(struct tipc_chan *chan); + +struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan); + +void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +struct tipc_msg_buf * +tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout); + +void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +static inline size_t mb_avail_space(struct tipc_msg_buf *mb) +{ + return mb->buf_sz - mb->wpos; +} + +static inline size_t mb_avail_data(struct tipc_msg_buf *mb) +{ + return mb->wpos - mb->rpos; +} + +static inline void *mb_put_data(struct tipc_msg_buf *mb, size_t len) +{ + void *pos = (u8 *)mb->buf_va + mb->wpos; + BUG_ON(mb->wpos + len > mb->buf_sz); + mb->wpos += len; + return pos; +} + +static inline void *mb_get_data(struct tipc_msg_buf *mb, size_t len) +{ + void *pos = (u8 *)mb->buf_va + mb->rpos; + BUG_ON(mb->rpos + len > mb->wpos); + mb->rpos += len; + return pos; +} + +#endif /* __LINUX_TRUSTY_TRUSTY_IPC_H */ + From f67eb79e7f06643a2661d8eeb74340422c56e34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Jun 2015 19:34:28 -0700 Subject: [PATCH 0310/1276] trusty: Select api version Select api version in probe, and store it in trusty_state. This enables new return codes from trusty, and will later be used to enable a nop stdcall that does not take smc_lock. Change-Id: I8011325265da818725ef65f094bf820402878eb5 --- drivers/trusty/trusty.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 19 +++++++++++++++++++ include/linux/trusty/trusty.h | 1 + 3 files changed, 55 insertions(+) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 16c595bf5e29..fcdbba518797 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -29,6 +29,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; char *version_str; + u32 api_version; }; #ifdef CONFIG_ARM64 @@ -265,6 +266,35 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) dev_err(dev, "failed to get version: %d\n", ret); } +u32 trusty_get_api_version(struct device *dev) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return s->api_version; +} +EXPORT_SYMBOL(trusty_get_api_version); + +static int trusty_init_api_version(struct trusty_state *s, struct device *dev) +{ + u32 api_version; + api_version = trusty_fast_call32(dev, SMC_FC_API_VERSION, + TRUSTY_API_VERSION_CURRENT, 0, 0); + if (api_version == SM_ERR_UNDEFINED_SMC) + api_version = 0; + + if (api_version > TRUSTY_API_VERSION_CURRENT) { + dev_err(dev, "unsupported api version %u > %u\n", + api_version, TRUSTY_API_VERSION_CURRENT); + return -EINVAL; + } + + dev_info(dev, "selected api version: %u (requested %u)\n", + api_version, TRUSTY_API_VERSION_CURRENT); + s->api_version = api_version; + + return 0; +} + static int trusty_probe(struct platform_device *pdev) { int ret; @@ -287,6 +317,10 @@ static int trusty_probe(struct platform_device *pdev) trusty_init_version(s, &pdev->dev); + ret = trusty_init_api_version(s, &pdev->dev); + if (ret < 0) + goto err_api_version; + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to add children: %d\n", ret); @@ -296,6 +330,7 @@ static int trusty_probe(struct platform_device *pdev) return 0; err_add_children: +err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index a2be2e3579f3..cdb4eccd7bc3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -78,6 +78,25 @@ #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) #define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +/** + * SMC_FC_API_VERSION - Find and select supported API version. + * + * @r1: Version supported by client. + * + * Returns version supported by trusty. + * + * If multiple versions are supported, the client should start by calling + * SMC_FC_API_VERSION with the largest version it supports. Trusty will then + * return a version it supports. If the client does not support the version + * returned by trusty and the version returned is less than the version + * requested, repeat the call with the largest supported version less than the + * last returned version. + * + * This call must be made before any calls that are affected by the api version. + */ +#define TRUSTY_API_VERSION_CURRENT (0) +#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index d084d9d68a7b..24fe2101a528 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -56,6 +56,7 @@ int trusty_call_notifier_register(struct device *dev, int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); const char *trusty_version_str_get(struct device *dev); +u32 trusty_get_api_version(struct device *dev); struct ns_mem_page_info { uint64_t attr; From 792de288f76a592cb0445bde7cc119b3bcd6702d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Jun 2015 19:51:54 -0700 Subject: [PATCH 0311/1276] trusty: Handle fiqs without calling notifier and enabling interrupts Change-Id: I9c147376bd1596f4ecd1e932b30140c87410c860 --- drivers/trusty/trusty.c | 2 ++ include/linux/trusty/sm_err.h | 1 + include/linux/trusty/smcall.h | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index fcdbba518797..4b5d3552720b 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -108,6 +108,8 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, __func__, smcnr, a0, a1, a2); while (true) { ret = smc(smcnr, a0, a1, a2); + while ((s32)ret == SM_ERR_FIQ_INTERRUPTED) + ret = smc(SMC_SC_RESTART_FIQ, 0, 0, 0); if ((int)ret != SM_ERR_BUSY || !retry) break; diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h index 4ee67589ce63..7de09b46fddb 100644 --- a/include/linux/trusty/sm_err.h +++ b/include/linux/trusty/sm_err.h @@ -35,5 +35,6 @@ #define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ #define SM_ERR_END_OF_INPUT -10 #define SM_ERR_PANIC -11 /* Secure OS crashed */ +#define SM_ERR_FIQ_INTERRUPTED -12 /* Got interrupted by FIQ. Call back with SMC_SC_RESTART_FIQ on same CPU */ #endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index cdb4eccd7bc3..7d8950a8890e 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -58,6 +58,18 @@ #define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) #define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +/** + * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq + * + * No arguments, no return value. + * + * Re-enter trusty after returning to ns to process an fiq. Must be called iff + * trusty returns SM_ERR_FIQ_INTERRUPTED. + * + * Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later. + */ +#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) + /* * Return from secure os to non-secure os with return value in r1 */ @@ -94,7 +106,8 @@ * * This call must be made before any calls that are affected by the api version. */ -#define TRUSTY_API_VERSION_CURRENT (0) +#define TRUSTY_API_VERSION_RESTART_FIQ (1) +#define TRUSTY_API_VERSION_CURRENT (1) #define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ From 899f1679f8faf829516d9681ac46ef40d17d8650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 23 Jan 2015 17:55:48 -0800 Subject: [PATCH 0312/1276] trusty: Add smp support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an unlocked nop call to allow multiple cpus to enter trusty. Other standard calls are still serialized to avoid return codes getting mixed up. A new return code is used to indicate that the standard call is running on another cpu. Change-Id: I0eecb88fb28989e3f4942659d109eee8863f3227 Signed-off-by: Arve HjønnevÃ¥g --- drivers/trusty/trusty-irq.c | 31 ++++++++++++++++++++++++++++--- drivers/trusty/trusty.c | 27 ++++++++++++++++++++++++--- include/linux/trusty/sm_err.h | 3 +++ include/linux/trusty/smcall.h | 17 +++++++++++++++-- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index ae9535af77dd..1f14f7f48bed 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -154,7 +154,7 @@ static int trusty_irq_call_notify(struct notifier_block *nb, } -static void trusty_irq_work_func(struct work_struct *work) +static void trusty_irq_work_func_locked_nop(struct work_struct *work) { int ret; struct trusty_irq_state *is = @@ -162,8 +162,27 @@ static void trusty_irq_work_func(struct work_struct *work) dev_dbg(is->dev, "%s\n", __func__); - ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + ret = trusty_std_call32(is->trusty_dev, SMC_SC_LOCKED_NOP, 0, 0, 0); if (ret != 0) + dev_err(is->dev, "%s: SMC_SC_LOCKED_NOP failed %d", + __func__, ret); + + dev_dbg(is->dev, "%s: done\n", __func__); +} + +static void trusty_irq_work_func(struct work_struct *work) +{ + int ret; + struct trusty_irq_state *is = + container_of(work, struct trusty_irq_work, work)->is; + + dev_dbg(is->dev, "%s\n", __func__); + + do { + ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + } while (ret == SM_ERR_NOP_INTERRUPTED); + + if (ret != SM_ERR_NOP_DONE) dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); dev_dbg(is->dev, "%s: done\n", __func__); @@ -397,6 +416,7 @@ static int trusty_irq_probe(struct platform_device *pdev) unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is; + work_func_t work_func; dev_dbg(&pdev->dev, "%s\n", __func__); @@ -431,12 +451,17 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_trusty_call_notifier_register; } + if (trusty_get_api_version(is->trusty_dev) < TRUSTY_API_VERSION_SMP) + work_func = trusty_irq_work_func_locked_nop; + else + work_func = trusty_irq_work_func; + for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); trusty_irq_work->is = is; - INIT_WORK(&trusty_irq_work->work, trusty_irq_work_func); + INIT_WORK(&trusty_irq_work->work, work_func); } for (irq = 0; irq >= 0;) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4b5d3552720b..2a7aeb4725c5 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -28,6 +28,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; + struct completion cpu_idle_completion; char *version_str; u32 api_version; }; @@ -161,6 +162,17 @@ static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, return ret; } +static void trusty_std_call_cpu_idle(struct trusty_state *s) +{ + int ret; + + ret = wait_for_completion_timeout(&s->cpu_idle_completion, HZ * 10); + if (!ret) { + pr_warn("%s: timed out waiting for cpu idle to clear, retry anyway\n", + __func__); + } +} + s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { int ret; @@ -169,15 +181,20 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) BUG_ON(SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); - mutex_lock(&s->smc_lock); + if (smcnr != SMC_SC_NOP) { + mutex_lock(&s->smc_lock); + reinit_completion(&s->cpu_idle_completion); + } dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n", __func__, smcnr, a0, a1, a2); ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); - while (ret == SM_ERR_INTERRUPTED) { + while (ret == SM_ERR_INTERRUPTED || ret == SM_ERR_CPU_IDLE) { dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", __func__, smcnr, a0, a1, a2); + if (ret == SM_ERR_CPU_IDLE) + trusty_std_call_cpu_idle(s); ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); } dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", @@ -185,7 +202,10 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); - mutex_unlock(&s->smc_lock); + if (smcnr == SMC_SC_NOP) + complete(&s->cpu_idle_completion); + else + mutex_unlock(&s->smc_lock); return ret; } @@ -315,6 +335,7 @@ static int trusty_probe(struct platform_device *pdev) } mutex_init(&s->smc_lock); ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); + init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); trusty_init_version(s, &pdev->dev); diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h index 7de09b46fddb..32ee08e499c3 100644 --- a/include/linux/trusty/sm_err.h +++ b/include/linux/trusty/sm_err.h @@ -36,5 +36,8 @@ #define SM_ERR_END_OF_INPUT -10 #define SM_ERR_PANIC -11 /* Secure OS crashed */ #define SM_ERR_FIQ_INTERRUPTED -12 /* Got interrupted by FIQ. Call back with SMC_SC_RESTART_FIQ on same CPU */ +#define SM_ERR_CPU_IDLE -13 /* SMC call waiting for another CPU */ +#define SM_ERR_NOP_INTERRUPTED -14 /* Got interrupted. Call back with new SMC_SC_NOP */ +#define SM_ERR_NOP_DONE -15 /* Cpu idle after SMC_SC_NOP (not an error) */ #endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 7d8950a8890e..2e43803d9333 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -56,7 +56,7 @@ /* FC = Fast call, SC = Standard call */ #define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) /** * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq @@ -70,6 +70,18 @@ */ #define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +/** + * SMC_SC_NOP - Enter trusty to run pending work. + * + * No arguments. + * + * Returns SM_ERR_NOP_INTERRUPTED or SM_ERR_NOP_DONE. + * If SM_ERR_NOP_INTERRUPTED is returned, the call must be repeated. + * + * Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later. + */ +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + /* * Return from secure os to non-secure os with return value in r1 */ @@ -107,7 +119,8 @@ * This call must be made before any calls that are affected by the api version. */ #define TRUSTY_API_VERSION_RESTART_FIQ (1) -#define TRUSTY_API_VERSION_CURRENT (1) +#define TRUSTY_API_VERSION_SMP (2) +#define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ From 7b2864841dda72bfa863d83800400bf0742abfd9 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Fri, 26 Jun 2015 13:47:02 -0700 Subject: [PATCH 0313/1276] trusty-irq: Add support for secure interrupt mapping Trusty TEE is using flat IRQ space to identify its interrupts which does not match to IRQ domain model introduced on the Linux side. This CL adds support for optional "interrupt-templates" and "interrupt-ranges" properties that can be used to define correspondence between secure and non-secure IRQ IDs. Change-Id: Idb298760f2f21f0b8507eafa72600cca7ab8ac64 Signed-off-by: Michael Ryleev --- .../devicetree/bindings/trusty/trusty-irq.txt | 59 ++++++++++ drivers/trusty/trusty-irq.c | 106 +++++++++++++++++- 2 files changed, 161 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/trusty/trusty-irq.txt b/Documentation/devicetree/bindings/trusty/trusty-irq.txt index 85fe1f1c7458..5aefeb8e536f 100644 --- a/Documentation/devicetree/bindings/trusty/trusty-irq.txt +++ b/Documentation/devicetree/bindings/trusty/trusty-irq.txt @@ -5,4 +5,63 @@ Trusty requires non-secure irqs to be forwarded to the secure OS. Required properties: - compatible: "android,trusty-irq-v1" +Optional properties: + +- interrupt-templates: is an optional property that works together + with "interrupt-ranges" to specify secure side to kernel IRQs mapping. + + It is a list of entries, each one of which defines a group of interrupts + having common properties, and has the following format: + < phandle irq_id_pos [templ_data]> + phandle - phandle of interrupt controller this template is for + irq_id_pos - the position of irq id in interrupt specifier array + for interrupt controller referenced by phandle. + templ_data - is an array of u32 values (could be empty) in the same + format as interrupt specifier for interrupt controller + referenced by phandle but with omitted irq id field. + +- interrupt-ranges: list of entries that specifies secure side to kernel + IRQs mapping. + + Each entry in the "interrupt-ranges" list has the following format: + + beg - first entry in this range + end - last entry in this range + templ_idx - index of entry in "interrupt-templates" property + that must be used as a template for all interrupts + in this range + +Example: +{ + gic: interrupt-controller@50041000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + interrupt-controller; + ... + }; + ... + IPI: interrupt-controller { + compatible = "android,CustomIPI"; + #interrupt-cells = <1>; + interrupt-controller; + }; + ... + trusty { + compatible = "android,trusty-smc-v1"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + + irq { + compatible = "android,trusty-irq-v1"; + interrupt-templates = <&IPI 0>, + <&gic 1 GIC_PPI 0>, + <&gic 1 GIC_SPI 0>; + interrupt-ranges = < 0 15 0>, + <16 31 1>, + <32 223 2>; + }; + } +} + Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 1f14f7f48bed..8d6e8afb2a2f 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -15,8 +15,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -266,13 +268,101 @@ static int trusty_irq_cpu_notify(struct notifier_block *nb, return NOTIFY_OK; } -static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) +static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) { int ret; + int index; + u32 irq_pos; + u32 templ_idx; + u32 range_base; + u32 range_end; + struct of_phandle_args oirq; + + /* check if "interrupt-ranges" property is present */ + if (!of_find_property(is->dev->of_node, "interrupt-ranges", NULL)) { + /* fallback to old behavior to be backward compatible with + * systems that do not need IRQ domains. + */ + return irq; + } + + /* find irq range */ + for (index = 0;; index += 3) { + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index, &range_base); + if (ret) + return ret; + + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index + 1, &range_end); + if (ret) + return ret; + + if (irq >= range_base && irq <= range_end) + break; + } + + /* read the rest of range entry: template index and irq_pos */ + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index + 2, &templ_idx); + if (ret) + return ret; + + /* read irq template */ + ret = of_parse_phandle_with_args(is->dev->of_node, + "interrupt-templates", + "#interrupt-cells", + templ_idx, &oirq); + if (ret) + return ret; + + WARN_ON(!oirq.np); + WARN_ON(!oirq.args_count); + + /* + * An IRQ template is a non empty array of u32 values describing group + * of interrupts having common properties. The u32 entry with index + * zero contains the position of irq_id in interrupt specifier array + * followed by data representing interrupt specifier array with irq id + * field omitted, so to convert irq template to interrupt specifier + * array we have to move down one slot the first irq_pos entries and + * replace the resulting gap with real irq id. + */ + irq_pos = oirq.args[0]; + + if (irq_pos >= oirq.args_count) { + dev_err(is->dev, "irq pos is out of range: %d\n", irq_pos); + return -EINVAL; + } + + for (index = 1; index <= irq_pos; index++) + oirq.args[index - 1] = oirq.args[index]; + + oirq.args[irq_pos] = irq - range_base; + + ret = irq_create_of_mapping(&oirq); + + return (!ret) ? -EINVAL : ret; +} + +static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) +{ + int ret; + int irq; unsigned long irq_flags; struct trusty_irq *trusty_irq; - dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); + + irq = trusty_irq_create_irq_mapping(is, tirq); + if (irq < 0) { + dev_err(is->dev, + "trusty_irq_create_irq_mapping failed (%d)\n", irq); + return irq; + } trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); if (!trusty_irq) @@ -302,13 +392,21 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) return ret; } -static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int irq) +static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) { int ret; + int irq; unsigned int cpu; struct trusty_irq __percpu *trusty_irq_handler_data; - dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); + + irq = trusty_irq_create_irq_mapping(is, tirq); + if (irq <= 0) { + dev_err(is->dev, + "trusty_irq_create_irq_mapping failed (%d)\n", irq); + return irq; + } trusty_irq_handler_data = alloc_percpu(struct trusty_irq); if (!trusty_irq_handler_data) From 8842bc7568e9c370903535127269db49de7e96d3 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 13 May 2016 10:36:16 +0800 Subject: [PATCH 0314/1276] Modify the static analysis errors for google's trusty driver patches. Totally 15 patches for Google's trusty driver are ported into kernel/glv. This patch will fix all of the static analysis errors for the 15 patches from google. Change-Id: I38c604cc010f1e93fda6a06d1f9410ab578656df Signed-off-by: weideng --- drivers/trusty/trusty.c | 4 ++-- include/linux/trusty/smcall.h | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 2a7aeb4725c5..6fcd5481ac88 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -39,8 +39,8 @@ struct trusty_state { #define SMC_ARG2 "x2" #define SMC_ARG3 "x3" #define SMC_ARCH_EXTENSION "" -#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ - "x12","x13","x14","x15","x16","x17" +#define SMC_REGISTERS_TRASHED "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", \ + "x12", "x13", "x14", "x15", "x16", "x17" #else #define SMC_ARG0 "r0" #define SMC_ARG1 "r1" diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 2e43803d9333..1160890a3d90 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -55,8 +55,8 @@ #define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ /* FC = Fast call, SC = Standard call */ -#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 1) /** * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq @@ -68,7 +68,7 @@ * * Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later. */ -#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 2) /** * SMC_SC_NOP - Enter trusty to run pending work. @@ -80,27 +80,27 @@ * * Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later. */ -#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_SC_NOP SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 3) /* * Return from secure os to non-secure os with return value in r1 */ -#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_NS_RETURN SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) -#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) -#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) -#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4) +#define SMC_FC_RESERVED SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 4) #define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5) -#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6) +#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 6) -#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) -#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 8) -#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) -#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 9) +#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 10) /** * SMC_FC_API_VERSION - Find and select supported API version. @@ -121,7 +121,7 @@ #define TRUSTY_API_VERSION_RESTART_FIQ (1) #define TRUSTY_API_VERSION_SMP (2) #define TRUSTY_API_VERSION_CURRENT (2) -#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) +#define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) From 592282f4f656df9ec98464e9522d7e34d92226d2 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 28 Oct 2016 13:46:37 +0800 Subject: [PATCH 0315/1276] Modify Google's trusty drivers so as to support Intel platform Previously, Google's trusty drivers can just work on ARM platform. With this patch, the trusty drivers can then support Intel platform so as to implement IPC functionality between android and lk. This patch is implemented by Intel, and it has been verified by tipc-test32 test cases which are also provided by Google. Change-Id: I7076ee23eb1eb8f1102feca4b299b34873f7f861 Author: chunmei Signed-off-by: kwang13 Signed-off-by: chunmei Signed-off-by: weideng --- drivers/trusty/Kconfig | 2 +- drivers/trusty/trusty-ipc.c | 9 +- drivers/trusty/trusty-irq.c | 102 ++++++++++++-- drivers/trusty/trusty-log.c | 2 + drivers/trusty/trusty-mem.c | 115 ++++++++-------- drivers/trusty/trusty-virtio.c | 15 ++- drivers/trusty/trusty.c | 236 +++++++++++++++++++++++++-------- include/linux/trusty/trusty.h | 2 +- 8 files changed, 348 insertions(+), 135 deletions(-) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 052cd8e91ab0..0b6b88e3a718 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -29,7 +29,7 @@ config TRUSTY_FIQ_ARM64 default y config TRUSTY_LOG - tristate + tristate "Trusty Log support" depends on TRUSTY default y diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 06e026344e67..7d66e9f74220 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -33,6 +33,8 @@ #define MAX_DEVICES 4 +#define VIRTIO_ID_TRUSTY_IPC 13 /* virtio trusty ipc */ + #define REPLY_TIMEOUT 5000 #define TXBUF_TIMEOUT 15000 @@ -172,7 +174,10 @@ static int _match_data(int id, void *p, void *data) static void *_alloc_shareable_mem(size_t sz, phys_addr_t *ppa, gfp_t gfp) { - return alloc_pages_exact(sz, gfp); + void *buf_va; + buf_va = alloc_pages_exact(sz, gfp); + *ppa = virt_to_phys(buf_va); + return buf_va; } static void _free_shareable_mem(size_t sz, void *va, phys_addr_t pa) @@ -1597,7 +1602,7 @@ static void tipc_virtio_remove(struct virtio_device *vdev) _cleanup_vq(vds->txvq); _free_msg_buf_list(&vds->free_buf_list); - vdev->config->del_vqs(vds->vdev); + vdev->config->del_vqs(vdev); kref_put(&vds->refcount, _free_vds); } diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 8d6e8afb2a2f..b325bff33774 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -12,6 +12,7 @@ * */ +#include #include #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,6 +59,24 @@ struct trusty_irq_state { struct notifier_block cpu_notifier; }; +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + +#define TRUSTY_VMCALL_IRQ_DONE 0x74727506 +static inline void irq_register_done(void) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_IRQ_DONE) + ); +} + static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) @@ -201,6 +222,8 @@ irqreturn_t trusty_irq_handler(int irq, void *data) __func__, irq, trusty_irq->irq, smp_processor_id(), trusty_irq->enable); + set_pending_intr_to_lk(irq+0x30); + if (trusty_irq->percpu) { disable_percpu_irq(irq); irqset = this_cpu_ptr(is->percpu_irqs); @@ -348,6 +371,39 @@ static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) return (!ret) ? -EINVAL : ret; } +static inline void trusty_irq_unmask(struct irq_data *data) +{ + return; +} + +static inline void trusty_irq_mask(struct irq_data *data) +{ + return; +} + +static void trusty_irq_enable(struct irq_data *data) +{ + return; +} + +static void trusty_irq_disable(struct irq_data *data) +{ + return; +} + +void trusty_irq_eoi(struct irq_data *data) +{ + return; +} +static struct irq_chip trusty_irq_chip = { + .name = "TRUSY-IRQ", + .irq_mask = trusty_irq_mask, + .irq_unmask = trusty_irq_unmask, + .irq_enable = trusty_irq_enable, + .irq_disable = trusty_irq_disable, + .irq_eoi = trusty_irq_eoi, +}; + static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) { int ret; @@ -357,12 +413,7 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); - irq = trusty_irq_create_irq_mapping(is, tirq); - if (irq < 0) { - dev_err(is->dev, - "trusty_irq_create_irq_mapping failed (%d)\n", irq); - return irq; - } + irq = tirq; trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); if (!trusty_irq) @@ -376,8 +427,17 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) hlist_add_head(&trusty_irq->node, &is->normal_irqs.inactive); spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + ret = irq_alloc_desc_at(irq, 0); + if (ret >= 0) + irq_set_chip_and_handler_name(irq, &trusty_irq_chip, handle_edge_irq, "trusty-irq"); + else if (ret != -EEXIST) { + dev_err(is->dev, "can't allocate irq desc %d\n", ret); + goto err_request_irq; + } + ret = request_irq(irq, trusty_irq_handler, IRQF_NO_THREAD, - "trusty", trusty_irq); + "trusty-irq", trusty_irq); + if (ret) { dev_err(is->dev, "request_irq failed %d\n", ret); goto err_request_irq; @@ -416,6 +476,8 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) struct trusty_irq *trusty_irq; struct trusty_irq_irqset *irqset; + if (cpu >= 32) + return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); irqset = per_cpu_ptr(is->percpu_irqs, cpu); @@ -439,6 +501,8 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) for_each_possible_cpu(cpu) { struct trusty_irq *trusty_irq; + if (cpu >= 32) + return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); hlist_del(&trusty_irq->node); } @@ -462,11 +526,11 @@ static int trusty_irq_init_one(struct trusty_irq_state *is, irq = trusty_smc_get_next_irq(is, irq, per_cpu); if (irq < 0) return irq; - + dev_info(is->dev, "irq from lk = %d\n", irq); if (per_cpu) - ret = trusty_irq_init_per_cpu_irq(is, irq); + ret = trusty_irq_init_per_cpu_irq(is, irq-0x30); else - ret = trusty_irq_init_normal_irq(is, irq); + ret = trusty_irq_init_normal_irq(is, irq-0x30); if (ret) { dev_warn(is->dev, @@ -481,7 +545,6 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) { struct trusty_irq *irq; struct hlist_node *n; - unsigned int cpu; hlist_for_each_entry_safe(irq, n, &is->normal_irqs.inactive, node) { dev_dbg(is->dev, "%s: irq %d\n", __func__, irq->irq); @@ -489,6 +552,7 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) hlist_del(&irq->node); kfree(irq); } +/* hlist_for_each_entry_safe(irq, n, &this_cpu_ptr(is->percpu_irqs)->inactive, node) { @@ -504,7 +568,7 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) hlist_del(&irq_tmp->node); } free_percpu(trusty_irq_handler_data); - } + } */ } static int trusty_irq_probe(struct platform_device *pdev) @@ -557,16 +621,18 @@ static int trusty_irq_probe(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); trusty_irq_work->is = is; INIT_WORK(&trusty_irq_work->work, work_func); } - for (irq = 0; irq >= 0;) - irq = trusty_irq_init_one(is, irq, true); for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); + irq_register_done(); + is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; ret = register_hotcpu_notifier(&is->cpu_notifier); if (ret) { @@ -597,6 +663,8 @@ static int trusty_irq_probe(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); flush_work(&trusty_irq_work->work); } @@ -632,6 +700,8 @@ static int trusty_irq_remove(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); flush_work(&trusty_irq_work->work); } @@ -657,3 +727,7 @@ static struct platform_driver trusty_irq_driver = { }; module_platform_driver(trusty_irq_driver); + + +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index e8dcced2ff1d..112287cd4739 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. * */ +#include #include #include #include @@ -272,3 +273,4 @@ static struct platform_driver trusty_log_driver = { }; module_platform_driver(trusty_log_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c index c55ace25beed..1317ec734315 100644 --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -11,66 +11,68 @@ * GNU General Public License for more details. * */ - +#include #include #include #include #include +#include + +/* Normal memory */ +#define NS_MAIR_NORMAL_CACHED_WB_RWA 0xFF /* inner and outer write back read/write allocate */ +#define NS_MAIR_NORMAL_CACHED_WT_RA 0xAA /* inner and outer write through read allocate */ +#define NS_MAIR_NORMAL_CACHED_WB_RA 0xEE /* inner and outer wriet back, read allocate */ +#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ static int get_mem_attr(struct page *page, pgprot_t pgprot) { -#if defined(CONFIG_ARM64) - uint64_t mair; - uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; - - asm ("mrs %0, mair_el1\n" : "=&r" (mair)); - return (mair >> (attr_index * 8)) & 0xff; - -#elif defined(CONFIG_ARM_LPAE) - uint32_t mair; - uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); - - if (attr_index >= 4) { - attr_index -= 4; - asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); - } else { - asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + /* The porting to CHT kernel (3.14.55) is in the #else clause. + ** For BXT kernel (4.1.0), the function get_page_memtype() is static. + ** + ** The orignal google code (for arm) getst the cache states and page + ** flags from input parameter "pgprot", which is not prefered in x86. + ** In x86, both cache states and page flags should be got from input + ** parameter "page". But, since current caller of trusty_call32_mem_buf() + ** always allocate memory in kernel heap, it is also ok to use hardcode + ** here. + ** + ** The memory allocated in kernel heap should be CACHED. The reason to + ** return UNCACHED here is to pass the check in LK sm_decode_ns_memory_attr() + ** with SMP, which only allow UNCACHED. + */ + return NS_MAIR_NORMAL_UNCACHED; +#else + unsigned long type; + int ret_mem_attr = 0; + + type = get_page_memtype(page); + /* + * -1 from get_page_memtype() implies RAM page is in its + * default state and not reserved, and hence of type WB + */ + if (type == -1) { + type = _PAGE_CACHE_MODE_WB; } - return (mair >> (attr_index * 8)) & 0xff; - -#elif defined(CONFIG_ARM) - /* check memory type */ - switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { - case L_PTE_MT_WRITEALLOC: - /* Normal: write back write allocate */ - return 0xFF; - - case L_PTE_MT_BUFFERABLE: - /* Normal: non-cacheble */ - return 0x44; - - case L_PTE_MT_WRITEBACK: - /* Normal: writeback, read allocate */ - return 0xEE; - - case L_PTE_MT_WRITETHROUGH: - /* Normal: write through */ - return 0xAA; - - case L_PTE_MT_UNCACHED: - /* strongly ordered */ - return 0x00; - - case L_PTE_MT_DEV_SHARED: - case L_PTE_MT_DEV_NONSHARED: - /* device */ - return 0x04; + switch (type) { + case _PAGE_CACHE_MODE_UC_MINUS: + /* uncacheable */ + ret_mem_attr = NS_MAIR_NORMAL_UNCACHED; + break; + case _PAGE_CACHE_MODE_WB: + /* writeback */ + ret_mem_attr = NS_MAIR_NORMAL_CACHED_WB_RWA; + break; + case _PAGE_CACHE_MODE_WC: + /* write combined */ + ret_mem_attr = NS_MAIR_NORMAL_UNCACHED; + break; default: - return -EINVAL; + printk(KERN_ERR "%s(): invalid type: 0x%x\n", __func__, type); + ret_mem_attr = -EINVAL; } -#else - return 0; + return ret_mem_attr; #endif } @@ -90,18 +92,10 @@ int trusty_encode_page_info(struct ns_mem_page_info *inf, mem_attr = get_mem_attr(page, pgprot); if (mem_attr < 0) return mem_attr; - - /* add other attributes */ -#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) - pte |= pgprot_val(pgprot); -#elif defined(CONFIG_ARM) - if (pgprot_val(pgprot) & L_PTE_USER) + if (pgprot_val(pgprot) & _PAGE_USER) pte |= (1 << 6); - if (pgprot_val(pgprot) & L_PTE_RDONLY) + if (!(pgprot_val(pgprot) & _PAGE_RW)) pte |= (1 << 7); - if (pgprot_val(pgprot) & L_PTE_SHARED) - pte |= (3 << 8); /* inner sharable */ -#endif inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); return 0; @@ -131,4 +125,5 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, (u32)(pg_inf.attr >> 32), size); } } - +EXPORT_SYMBOL(trusty_call32_mem_buf); +MODULE_LICENSE("GPL"); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index fabbf29bffcc..f00c4ece03bf 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -206,22 +206,23 @@ static void trusty_virtio_reset(struct virtio_device *vdev) dev_dbg(&vdev->dev, "reset vdev_id=%d\n", tvdev->notifyid); trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_RESET, tvdev->notifyid, 0, 0); + vdev->config->set_status(vdev, 0); } static u64 trusty_virtio_get_features(struct virtio_device *vdev) { struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); - return tvdev->vdev_descr->dfeatures; + return ((u64)tvdev->vdev_descr->dfeatures) & 0x00000000FFFFFFFFULL; } static int trusty_virtio_finalize_features(struct virtio_device *vdev) { struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); - + /* Make sure we don't have any features > 32 bits! */ BUG_ON((u32)vdev->features != vdev->features); - tvdev->vdev_descr->gfeatures = vdev->features; + tvdev->vdev_descr->gfeatures = (u32)(vdev->features); return 0; } @@ -381,6 +382,12 @@ static const struct virtio_config_ops trusty_virtio_config_ops = { .bus_name = trusty_virtio_bus_name, }; +void virtio_vdev_release(struct device *dev) +{ + dev_dbg(dev, "%s() is called\n", __func__); + return; +} + static int trusty_virtio_add_device(struct trusty_ctx *tctx, struct fw_rsc_vdev *vdev_descr, struct fw_rsc_vdev_vring *vr_descr, @@ -400,6 +407,7 @@ static int trusty_virtio_add_device(struct trusty_ctx *tctx, /* setup vdev */ tvdev->tctx = tctx; tvdev->vdev.dev.parent = tctx->dev; + tvdev->vdev.dev.release = virtio_vdev_release; tvdev->vdev.id.device = vdev_descr->id; tvdev->vdev.config = &trusty_virtio_config_ops; tvdev->vdev_descr = vdev_descr; @@ -677,6 +685,7 @@ static const struct of_device_id trusty_of_match[] = { { .compatible = "android,trusty-virtio-v1", }, + {}, }; MODULE_DEVICE_TABLE(of, trusty_of_match); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 6fcd5481ac88..12a90224eb27 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -12,7 +12,6 @@ * */ -#include #include #include #include @@ -20,11 +19,14 @@ #include #include #include +#include #include #include #include #include +#define TRUSTY_VMCALL_SMC 0x74727500 + struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; @@ -33,56 +35,57 @@ struct trusty_state { u32 api_version; }; -#ifdef CONFIG_ARM64 -#define SMC_ARG0 "x0" -#define SMC_ARG1 "x1" -#define SMC_ARG2 "x2" -#define SMC_ARG3 "x3" -#define SMC_ARCH_EXTENSION "" -#define SMC_REGISTERS_TRASHED "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", \ - "x12", "x13", "x14", "x15", "x16", "x17" -#else -#define SMC_ARG0 "r0" -#define SMC_ARG1 "r1" -#define SMC_ARG2 "r2" -#define SMC_ARG3 "r3" -#define SMC_ARCH_EXTENSION ".arch_extension sec\n" -#define SMC_REGISTERS_TRASHED "ip" -#endif +struct trusty_smc_interface { + struct device *dev; + ulong args[5]; +}; static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { - register ulong _r0 asm(SMC_ARG0) = r0; - register ulong _r1 asm(SMC_ARG1) = r1; - register ulong _r2 asm(SMC_ARG2) = r2; - register ulong _r3 asm(SMC_ARG3) = r3; - - asm volatile( - __asmeq("%0", SMC_ARG0) - __asmeq("%1", SMC_ARG1) - __asmeq("%2", SMC_ARG2) - __asmeq("%3", SMC_ARG3) - __asmeq("%4", SMC_ARG0) - __asmeq("%5", SMC_ARG1) - __asmeq("%6", SMC_ARG2) - __asmeq("%7", SMC_ARG3) - SMC_ARCH_EXTENSION - "smc #0" /* switch to secure world */ - : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) - : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) - : SMC_REGISTERS_TRASHED); - return _r0; + __asm__ __volatile__( + "vmcall; \n" + :"=D"(r0) + :"a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } -s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +static void trusty_fast_call32_remote(void *args) { + struct trusty_smc_interface *p_args = args; + struct device *dev = p_args->dev; + ulong smcnr = p_args->args[0]; + ulong a0 = p_args->args[1]; + ulong a1 = p_args->args[2]; + ulong a2 = p_args->args[3]; struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); BUG_ON(!s); BUG_ON(!SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); - return smc(smcnr, a0, a1, a2); + p_args->args[4] = smc(smcnr, a0, a1, a2); +} + +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + int cpu = 0; + int ret = 0; + struct trusty_smc_interface s; + s.dev = dev; + s.args[0] = smcnr; + s.args[1] = a0; + s.args[2] = a1; + s.args[3] = a2; + s.args[4] = 0; + + ret = smp_call_function_single(cpu, trusty_fast_call32_remote, (void *)&s, 1); + + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + + return s.args[4]; } EXPORT_SYMBOL(trusty_fast_call32); @@ -122,21 +125,59 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, return ret; } +static void trusty_std_call_inner_wrapper_remote(void *args) +{ + struct trusty_smc_interface *p_args = args; + struct device *dev = p_args->dev; + ulong smcnr = p_args->args[0]; + ulong a0 = p_args->args[1]; + ulong a1 = p_args->args[2]; + ulong a2 = p_args->args[3]; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + ulong ret; + unsigned long flags; + + local_irq_save(flags); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_restore(flags); + + p_args->args[4] = ret; +} + +static ulong trusty_std_call_inner_wrapper(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + int cpu = 0; + int ret = 0; + struct trusty_smc_interface s; + s.dev = dev; + s.args[0] = smcnr; + s.args[1] = a0; + s.args[2] = a1; + s.args[3] = a2; + s.args[4] = 0; + + ret = smp_call_function_single(cpu, trusty_std_call_inner_wrapper_remote, (void *)&s, 1); + + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + + return s.args[4]; +} + static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, ulong a0, ulong a1, ulong a2) { ulong ret; int sleep_time = 1; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { - local_irq_disable(); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, - NULL); - ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, - NULL); - local_irq_enable(); + ret = trusty_std_call_inner_wrapper(dev, smcnr, a0, a1, a2); if ((int)ret != SM_ERR_BUSY) break; @@ -173,6 +214,9 @@ static void trusty_std_call_cpu_idle(struct trusty_state *s) } } +/* must set CONFIG_DEBUG_ATOMIC_SLEEP=n +** otherwise mutex_lock() will fail and crash +*/ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { int ret; @@ -230,6 +274,7 @@ EXPORT_SYMBOL(trusty_call_notifier_unregister); static int trusty_remove_child(struct device *dev, void *data) { + dev_dbg(dev, "%s() is called()\n", __func__); platform_device_unregister(to_platform_device(dev)); return 0; } @@ -265,6 +310,8 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) version_str_len = ret; s->version_str = kmalloc(version_str_len + 1, GFP_KERNEL); + if (!s->version_str) + goto err_get_size; for (i = 0; i < version_str_len; i++) { ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, i, 0, 0); if (ret < 0) @@ -344,15 +391,8 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; - ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to add children: %d\n", ret); - goto err_add_children; - } - return 0; -err_add_children: err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); @@ -369,6 +409,8 @@ static int trusty_remove(struct platform_device *pdev) { struct trusty_state *s = platform_get_drvdata(pdev); + dev_dbg(&(pdev->dev), "%s() is called\n", __func__); + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); if (s->version_str) { @@ -394,15 +436,101 @@ static struct platform_driver trusty_driver = { }, }; +void trusty_dev_release(struct device *dev) +{ + dev_dbg(dev, "%s() is called()\n", __func__); + return; +} + +static struct device_node trusty_irq_node = { + .name = "trusty-irq", + .sibling = NULL, +}; + +static struct device_node trusty_virtio_node = { + .name = "trusty-virtio", + .sibling = &trusty_irq_node, +}; + +static struct device_node trusty_log_node = { + .name = "trusty-log", + .sibling = &trusty_virtio_node, +}; + + +static struct device_node trusty_node = { + .name = "trusty", + .child = &trusty_log_node, +}; + +static struct platform_device trusty_platform_dev = { + .name = "trusty", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .of_node = &trusty_node, + }, +}; +static struct platform_device trusty_platform_dev_log = { + .name = "trusty-log", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_log_node, + }, +}; + +static struct platform_device trusty_platform_dev_virtio = { + .name = "trusty-virtio", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_virtio_node, + }, +}; + +static struct platform_device trusty_platform_dev_irq = { + .name = "trusty-irq", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_irq_node, + }, +}; + +static struct platform_device *trusty_devices[] __initdata = { + &trusty_platform_dev, + &trusty_platform_dev_log, + &trusty_platform_dev_virtio, + &trusty_platform_dev_irq +}; static int __init trusty_driver_init(void) { + int ret = 0; + + ret = platform_add_devices(trusty_devices, ARRAY_SIZE(trusty_devices)); + if (ret) { + printk(KERN_ERR "%s(): platform_add_devices() failed, ret %d\n", __func__, ret); + return ret; + } return platform_driver_register(&trusty_driver); } static void __exit trusty_driver_exit(void) { platform_driver_unregister(&trusty_driver); + platform_device_unregister(&trusty_platform_dev); } subsys_initcall(trusty_driver_init); module_exit(trusty_driver_exit); + +MODULE_LICENSE("GPL"); + diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 24fe2101a528..74598389c308 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -20,7 +20,7 @@ #include -#ifdef CONFIG_TRUSTY +#if IS_ENABLED(CONFIG_TRUSTY) s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); #ifdef CONFIG_64BIT From cd4e0ff3cc5a0845d99c3b2287ab72dcd569dda7 Mon Sep 17 00:00:00 2001 From: weideng Date: Mon, 20 Jun 2016 14:19:52 +0800 Subject: [PATCH 0316/1276] Fix the issue for tipc test case closer1 If the server channel accept the connection and immediately close the server channel, the client channel will receive one CONN_RSP message and then immediately one DISCONN_REQ message. At this time, channel status will maintain in CONNECTED status for one short time and dn_wait_for_reply() cannot capture the channel status. This is the reason why closer1 will fail. This patch will add one pulse variable to capture channel CONNECTED status. And it can work well for both UP and SMP mode. Change-Id: I4aac5af714daa67d3095093907c0b9f26af4d76c Signed-off-by: weideng --- drivers/trusty/trusty-ipc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 7d66e9f74220..d6765f1d4510 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -38,6 +38,9 @@ #define REPLY_TIMEOUT 5000 #define TXBUF_TIMEOUT 15000 +#define PULSE_ACTIVE 1 +#define PULSE_DEACTIVE 0 + #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 @@ -705,6 +708,7 @@ EXPORT_SYMBOL(tipc_chan_destroy); /***************************************************************************/ struct tipc_dn_chan { + int pulse; int state; struct mutex lock; /* protects rx_msg_queue list and channel state */ struct tipc_chan *chan; @@ -729,9 +733,10 @@ static int dn_wait_for_reply(struct tipc_dn_chan *dn, int timeout) ret = -ETIMEDOUT; } else { /* got reply */ - if (dn->state == TIPC_CONNECTED) + if (dn->pulse == PULSE_ACTIVE) { + dn->pulse = PULSE_DEACTIVE; ret = 0; - else if (dn->state == TIPC_DISCONNECTED) + } else if (dn->state == TIPC_DISCONNECTED) if (!list_empty(&dn->rx_msg_queue)) ret = 0; else @@ -775,6 +780,7 @@ static void dn_connected(struct tipc_dn_chan *dn) { mutex_lock(&dn->lock); dn->state = TIPC_CONNECTED; + dn->pulse = PULSE_ACTIVE; /* complete all pending */ complete(&dn->reply_comp); @@ -883,6 +889,7 @@ static int tipc_open(struct inode *inode, struct file *filp) INIT_LIST_HEAD(&dn->rx_msg_queue); dn->state = TIPC_DISCONNECTED; + dn->pulse = PULSE_DEACTIVE; dn->chan = vds_create_channel(vds, &_dn_ops, dn); if (IS_ERR(dn->chan)) { From 11d8b28f962947d80574b656a6f638f83e003c64 Mon Sep 17 00:00:00 2001 From: "Zhu, Bing" Date: Fri, 15 Jul 2016 13:24:42 +0800 Subject: [PATCH 0317/1276] trusty: implement trusty OS timer proxy for performance enhancement Previously VMX timer causes 14 times of vmexit/vmresume switches every 10ms and VMX timer stops when processor enters C3+ sleep state. With linux proxiedtimer implementation, we can reduces vmexit/vmresume switches down to 4. But a drawback is that Trusty OS has no timer during the boot time (before Linux kernel bringup), because Trusty OS also intends to be used to provide services for bootloader, like GVB and FRP(factor reset protection). We plan to solve it in other ways, e.g. taking control of lapic timer before Linux kernel boot. Change-Id: I4baa827ecca51fcca5315a1e973a7533553073a0 Signed-off-by: Zhu, Bing Signed-off-by: Feng, Wang Signed-off-by: weideng Tracked-On: --- drivers/trusty/trusty-irq.c | 2 - drivers/trusty/trusty.c | 87 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index b325bff33774..2c2a792a3636 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -631,8 +631,6 @@ static int trusty_irq_probe(struct platform_device *pdev) for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); - irq_register_done(); - is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; ret = register_hotcpu_notifier(&is->cpu_notifier); if (ret) { diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 12a90224eb27..8daf817634d8 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,11 +26,23 @@ #include #define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ +#define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ + +enum lktimer_mode { + ONESHOT_TIMER, + PERIODICAL_TIMER, +}; struct trusty_state { + struct device *dev; struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; + struct timer_list timer; + struct work_struct timer_work; + enum lktimer_mode timer_mode; + unsigned long timer_interval; char *version_str; u32 api_version; }; @@ -40,6 +52,72 @@ struct trusty_smc_interface { ulong args[5]; }; +static void trusty_lktimer_work_func(struct work_struct *work) +{ + int ret; + unsigned int vector; + struct trusty_state *s = + container_of(work, struct trusty_state, timer_work); + + dev_dbg(s->dev, "%s\n", __func__); + + /* need vector number only for the first time */ + vector = TRUSTY_LKTIMER_VECTOR; + + do { + ret = trusty_std_call32(s->dev, SMC_SC_NOP, vector, 0, 0); + vector = 0; + } while (ret == SM_ERR_NOP_INTERRUPTED); + + if (ret != SM_ERR_NOP_DONE) + dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); + + dev_notice_once(s->dev, "LK OS proxy timer works\n"); +} + +static void trusty_lktimer_func(unsigned long data) +{ + struct trusty_state *s = (struct trusty_state *)data; + + /* binding it physical CPU0 only because trusty OS runs on it */ + schedule_work_on(0, &s->timer_work); + + /* reactivate the timer again in periodic mode */ + if (s->timer_mode == PERIODICAL_TIMER) + mod_timer(&s->timer, + jiffies + msecs_to_jiffies(s->timer_interval)); +} + +static void trusty_init_lktimer(struct trusty_state *s) +{ + INIT_WORK(&s->timer_work, trusty_lktimer_work_func); + setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); +} + +/* note that this function is not thread-safe */ +static void trusty_configure_lktimer(struct trusty_state *s, + enum lktimer_mode mode, unsigned long interval) +{ + if (mode != ONESHOT_TIMER && mode != PERIODICAL_TIMER) { + pr_err("%s: invalid timer mode: %d\n", __func__, mode); + return; + } + + s->timer_mode = mode; + s->timer_interval = interval; + mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); +} + +/* + * this should be called when removing trusty dev and + * when LK/Trusty crashes, to disable proxy timer. + */ +static void trusty_del_lktimer(struct trusty_state *s) +{ + del_timer_sync(&s->timer); + flush_work(&s->timer_work); +} + static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( @@ -246,6 +324,9 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); + if (ret == SM_ERR_PANIC) + trusty_del_lktimer(s); + if (smcnr == SMC_SC_NOP) complete(&s->cpu_idle_completion); else @@ -384,6 +465,7 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); + s->dev = &pdev->dev; trusty_init_version(s, &pdev->dev); @@ -391,6 +473,10 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; + trusty_init_lktimer(s); + trusty_configure_lktimer(s, + PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); + return 0; err_api_version: @@ -417,6 +503,7 @@ static int trusty_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); } + trusty_del_lktimer(s); kfree(s); return 0; } From 72fd093860bbc79563798fb93195857cea29fc25 Mon Sep 17 00:00:00 2001 From: "Deng, Wei A" Date: Wed, 16 Nov 2016 16:31:43 +0800 Subject: [PATCH 0318/1276] Replace CPU_STARTING/CPU_DYING with CPU_UP_PREPARE/CPU_DEAD CPU_STARTING and CPU_DYING notifier are removed from kernel 4.9. Add this patch to replace them with CPU_UP_PREPARE/CPU_DEAD. Change-Id: I1f48e7a8598dc684e70c8e4bc678723cbb1a0353 Signed-off-by: Deng, Wei A --- drivers/trusty/trusty-irq.c | 4 ++-- drivers/trusty/trusty-virtio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 2c2a792a3636..aeb0918dc572 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -280,10 +280,10 @@ static int trusty_irq_cpu_notify(struct notifier_block *nb, dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); switch (action & ~CPU_TASKS_FROZEN) { - case CPU_STARTING: + case CPU_UP_PREPARE: trusty_irq_cpu_up(is); break; - case CPU_DYING: + case CPU_DEAD: trusty_irq_cpu_down(is); break; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index f00c4ece03bf..a48f4f9884a8 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -320,7 +320,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, /* da field is only 32 bit wide. Use previously unused 'reserved' field * to store top 32 bits of 64-bit address */ - tvr->vr_descr->reserved = (u32)(pa >> 32); + tvr->vr_descr->pa = (u32)(pa >> 32); dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); From 7c8ade3f7611885506edbb4d2b1b54b9b04f4dcb Mon Sep 17 00:00:00 2001 From: Dwane Pottratz Date: Thu, 17 Nov 2016 12:53:31 -0800 Subject: [PATCH 0319/1276] trusty: fix incompatible-pointer-types incompatible-pointer-types found in function trusty_virtio_find_vps drivers/trusty/trusty-virtio.c:380:14: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .find_vqs = trusty_virtio_find_vqs, Change-Id: Idfd949f9ca20b46537db135621bfe17ad1178d36 Signed-off-by: Dwane Pottratz --- drivers/trusty/trusty-virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index a48f4f9884a8..eb4c0d31e249 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -347,7 +347,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char *names[]) + const char * const names[]) { uint i; int ret; From f271fabd56bc4413e6c507cb634692e28921ff78 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Mon, 12 Dec 2016 14:18:25 +0000 Subject: [PATCH 0320/1276] trusty: move async works off system workqueue Trusty async works might be very CPU intensive, move all Trusty works to separate workqueues. Change-Id: I78a906bc0963beea9b20ad8d8599a31b34546376 Signed-off-by: Michael Ryleev Signed-off-by: weideng Reviewed-by: mark gross --- drivers/trusty/trusty-irq.c | 12 +++++++++++- drivers/trusty/trusty-virtio.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index aeb0918dc572..5a74d75ce820 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -57,6 +57,7 @@ struct trusty_irq_state { struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; struct notifier_block cpu_notifier; + struct workqueue_struct *wq; }; #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 @@ -239,7 +240,7 @@ irqreturn_t trusty_irq_handler(int irq, void *data) } spin_unlock(&is->normal_irqs_lock); - schedule_work_on(raw_smp_processor_id(), &trusty_irq_work->work); + queue_work_on(raw_smp_processor_id(), is->wq, &trusty_irq_work->work); dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); @@ -588,6 +589,12 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_alloc_is; } + is->wq = alloc_workqueue("trusty-irq-wq", WQ_CPU_INTENSIVE, 0); + if (!is->wq) { + ret = -ENOMEM; + goto err_alloc_wq; + } + is->dev = &pdev->dev; is->trusty_dev = is->dev->parent; is->irq_work = alloc_percpu(struct trusty_irq_work); @@ -668,6 +675,8 @@ static int trusty_irq_probe(struct platform_device *pdev) } free_percpu(is->irq_work); err_alloc_irq_work: + destroy_workqueue(is->wq); +err_alloc_wq: kfree(is); err_alloc_is: return ret; @@ -704,6 +713,7 @@ static int trusty_irq_remove(struct platform_device *pdev) flush_work(&trusty_irq_work->work); } free_percpu(is->irq_work); + destroy_workqueue(is->wq); kfree(is); return 0; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index eb4c0d31e249..eaeb020e98f4 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -46,6 +46,8 @@ struct trusty_ctx { struct notifier_block call_notifier; struct list_head vdev_list; struct mutex mlock; /* protects vdev_list */ + struct workqueue_struct *kick_wq; + struct workqueue_struct *check_wq; }; struct trusty_vring { @@ -97,7 +99,7 @@ static int trusty_call_notify(struct notifier_block *nb, return NOTIFY_DONE; tctx = container_of(nb, struct trusty_ctx, call_notifier); - schedule_work(&tctx->check_vqs); + queue_work(tctx->check_wq, &tctx->check_vqs); return NOTIFY_OK; } @@ -143,7 +145,7 @@ static bool trusty_virtio_notify(struct virtqueue *vq) struct trusty_ctx *tctx = tvdev->tctx; atomic_set(&tvr->needs_kick, 1); - schedule_work(&tctx->kick_vqs); + queue_work(tctx->kick_wq, &tctx->kick_vqs); return true; } @@ -641,6 +643,21 @@ static int trusty_virtio_probe(struct platform_device *pdev) INIT_WORK(&tctx->kick_vqs, kick_vqs); platform_set_drvdata(pdev, tctx); + tctx->check_wq = alloc_workqueue("trusty-check-wq", WQ_UNBOUND, 0); + if (!tctx->check_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-check-wq\n"); + goto err_create_check_wq; + } + + tctx->kick_wq = alloc_workqueue("trusty-kick-wq", + WQ_UNBOUND | WQ_CPU_INTENSIVE, 0); + if (!tctx->kick_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-kick-wq\n"); + goto err_create_kick_wq; + } + ret = trusty_virtio_add_devices(tctx); if (ret) { dev_err(&pdev->dev, "Failed to add virtio devices\n"); @@ -651,6 +668,10 @@ static int trusty_virtio_probe(struct platform_device *pdev) return 0; err_add_devices: + destroy_workqueue(tctx->kick_wq); +err_create_kick_wq: + destroy_workqueue(tctx->check_wq); +err_create_check_wq: kfree(tctx); return ret; } @@ -670,6 +691,10 @@ static int trusty_virtio_remove(struct platform_device *pdev) trusty_virtio_remove_devices(tctx); cancel_work_sync(&tctx->kick_vqs); + /* destroy workqueues */ + destroy_workqueue(tctx->kick_wq); + destroy_workqueue(tctx->check_wq); + /* notify remote that shared area goes away */ trusty_virtio_stop(tctx, tctx->shared_va, tctx->shared_sz); From c33e09fda738f16e521bd43727f667ee3c605576 Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Thu, 8 Dec 2016 04:58:55 +0000 Subject: [PATCH 0321/1276] trusty: print out "Built: " in kernel directly. do this instead of get them from trusty which can save 28 times vmexit/vmresume switch, so we can reduce some boot time Change-Id: I196d506f606a77c1abe9a87d4d48dc18e40ca6bc Tracked-On: Signed-off-by: Feng, Wang Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8daf817634d8..cfef965402c4 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -401,7 +401,7 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) } s->version_str[i] = '\0'; - dev_info(dev, "trusty version: %s\n", s->version_str); + dev_info(dev, "trusty version: Built: %s\n", s->version_str); ret = device_create_file(dev, &dev_attr_trusty_version); if (ret) From 25010e549f552254e606fe49c745be8127920ad8 Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Thu, 8 Dec 2016 05:14:48 +0000 Subject: [PATCH 0322/1276] trusty: Popup warning when LK timer interrupt is not as expected LK timer interrupt vector 0x31 should map to irq 1, if not LK timer interrupt is not work as expected Change-Id: I4936bf3dd1d9a21e6913d8d3c4353568eb67c2b2 Tracked-On: Signed-off-by: Feng, Wang Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty-irq.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 5a74d75ce820..6c510a65e784 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -29,6 +29,9 @@ #include #include +#define IRQ_VECTOR_OFFSET 0x30 +#define IRQ_FOR_LK_TIMER 1 + struct trusty_irq { struct trusty_irq_state *is; struct hlist_node node; @@ -223,7 +226,9 @@ irqreturn_t trusty_irq_handler(int irq, void *data) __func__, irq, trusty_irq->irq, smp_processor_id(), trusty_irq->enable); - set_pending_intr_to_lk(irq+0x30); + WARN_ON(irq != IRQ_FOR_LK_TIMER); + + set_pending_intr_to_lk(irq+IRQ_VECTOR_OFFSET); if (trusty_irq->percpu) { disable_percpu_irq(irq); @@ -528,10 +533,13 @@ static int trusty_irq_init_one(struct trusty_irq_state *is, if (irq < 0) return irq; dev_info(is->dev, "irq from lk = %d\n", irq); + + WARN_ON(irq-IRQ_VECTOR_OFFSET != IRQ_FOR_LK_TIMER); + if (per_cpu) - ret = trusty_irq_init_per_cpu_irq(is, irq-0x30); + ret = trusty_irq_init_per_cpu_irq(is, irq-IRQ_VECTOR_OFFSET); else - ret = trusty_irq_init_normal_irq(is, irq-0x30); + ret = trusty_irq_init_normal_irq(is, irq-IRQ_VECTOR_OFFSET); if (ret) { dev_warn(is->dev, From ebd2caa97dbfb2335562b79abb995b7f421e04e4 Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Fri, 9 Dec 2016 05:32:20 +0000 Subject: [PATCH 0323/1276] trusty-log: Add vmm panic notifier for vmm deadloop dumping register a new vmcall TRUSTY_VMCALL_DUMP_INIT. Change-Id: Icee169358f30c64da44894dc5816ce5f3020fc70 Tracked-On: Signed-off-by: syan10 Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty-log.c | 107 ++++++++++++++++++++++++++++++++++++ drivers/trusty/trusty-log.h | 22 ++++++++ 2 files changed, 129 insertions(+) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 112287cd4739..a066481c4f1d 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -26,6 +26,8 @@ #define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) #define TRUSTY_LINE_BUFFER_SIZE 256 +static uint64_t g_vmm_debug_buf; + struct trusty_log_state { struct device *dev; struct device *trusty_dev; @@ -135,6 +137,72 @@ static int trusty_log_panic_notify(struct notifier_block *nb, return NOTIFY_OK; } +static void trusty_vmm_dump_header(struct deadloop_dump *dump) +{ + struct dump_header *header; + + if (!dump) + return; + + header = &(dump->header); + pr_info("VMM version = %s\n", header->vmm_version); + pr_info("Signature = %s\n", header->signature); + pr_info("Error_info = %s\n", header->error_info); + pr_info("Cpuid = %d\n", header->cpuid); +} + +static void trusty_vmm_dump_data(struct deadloop_dump *dump) +{ + struct dump_data *dump_data; + int i; + + if (!dump) + return; + + dump_data = &(dump->data); + + for (i = 0; i < dump_data->length; i++) + pr_info("%c", dump_data->data[i]); +} + +static int trusty_vmm_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct deadloop_dump *dump_info; + + if (g_vmm_debug_buf) { + dump_info = (struct deadloop_dump *)g_vmm_debug_buf; + + if (dump_info->is_valid) { + pr_info("trusty-vmm panic start!\n"); + trusty_vmm_dump_header(dump_info); + trusty_vmm_dump_data(dump_info); + pr_info("trusty-vmm panic dump end!\n"); + } + } + + return NOTIFY_OK; +} + +static struct notifier_block trusty_vmm_panic_nb = { + .notifier_call = trusty_vmm_panic_notify, + .priority = 0, +}; + +#define TRUSTY_VMCALL_DUMP_INIT 0x74727507 +static int trusty_vmm_dump_init(void *gva) +{ + int ret = -1; + + __asm__ __volatile__( + "vmcall" + : "=a"(ret) + : "a"(TRUSTY_VMCALL_DUMP_INIT), "D"(gva) + ); + + return ret; +} + static bool trusty_supports_logging(struct device *device) { int result; @@ -164,6 +232,7 @@ static int trusty_log_probe(struct platform_device *pdev) struct trusty_log_state *s; int result; phys_addr_t pa; + struct deadloop_dump *dump; dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_supports_logging(pdev->dev.parent)) { @@ -216,10 +285,45 @@ static int trusty_log_probe(struct platform_device *pdev) "failed to register panic notifier\n"); goto error_panic_notifier; } + + /* allocate debug buffer for vmm panic dump */ + g_vmm_debug_buf = get_zeroed_page(GFP_KERNEL); + if (!g_vmm_debug_buf) { + result = -ENOMEM; + goto error_alloc_vmm; + } + + dump = (struct deadloop_dump *)g_vmm_debug_buf; + dump->version_of_this_struct = VMM_DUMP_VERSION; + dump->size_of_this_struct = sizeof(struct deadloop_dump); + dump->is_valid = false; + + /* shared the buffer to vmm by VMCALL */ + result = trusty_vmm_dump_init(dump); + if (result < 0) { + dev_err(&pdev->dev, + "failed to share the dump buffer to VMM\n"); + goto error_vmm_panic_notifier; + } + + /* register the panic notifier for vmm */ + result = atomic_notifier_chain_register(&panic_notifier_list, + &trusty_vmm_panic_nb); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register vmm panic notifier\n"); + goto error_vmm_panic_notifier; + } + platform_set_drvdata(pdev, s); return 0; +error_vmm_panic_notifier: + free_page(g_vmm_debug_buf); +error_alloc_vmm: + atomic_notifier_chain_unregister(&panic_notifier_list, + &s->panic_notifier); error_panic_notifier: trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); error_call_notifier: @@ -241,6 +345,8 @@ static int trusty_log_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); + atomic_notifier_chain_unregister(&panic_notifier_list, + &trusty_vmm_panic_nb); atomic_notifier_chain_unregister(&panic_notifier_list, &s->panic_notifier); trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); @@ -253,6 +359,7 @@ static int trusty_log_remove(struct platform_device *pdev) } __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); kfree(s); + free_page(g_vmm_debug_buf); return 0; } diff --git a/drivers/trusty/trusty-log.h b/drivers/trusty/trusty-log.h index 09f60213e1f6..587bc7aaa145 100644 --- a/drivers/trusty/trusty-log.h +++ b/drivers/trusty/trusty-log.h @@ -18,5 +18,27 @@ struct log_rb { #define TRUSTY_LOG_API_VERSION 1 +#define VMM_DUMP_VERSION 1 + +struct dump_data { + uint32_t length; + uint8_t data[0]; +} __packed; + +struct dump_header { + uint8_t vmm_version[64]; /* version of the vmm */ + uint8_t signature[16]; /* signature for the dump structure */ + uint8_t error_info[32]; /* filename:linenum */ + uint16_t cpuid; +} __packed; + +struct deadloop_dump { + uint16_t size_of_this_struct; + uint16_t version_of_this_struct; + uint32_t is_valid; + struct dump_header header; + struct dump_data data; +} __packed; + #endif From 2f3cb8dfaa635bc44706d97e346d9bfe82285a11 Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Fri, 9 Dec 2016 05:33:22 +0000 Subject: [PATCH 0324/1276] trusty: fix rcu_preempt soft lockup crash issue since we'll run a long TEE/Trusty task, e.g generate 3K RSA key pair, the previous API not meet the requirement of "must be fast and non-blocking for smp_call_function_single()", we replace smp_call_function_single() with work_on_cpu() to bind cpu #0, which can fix the rcu_preempt softup crash issue. Change-Id: I63225c16be50b1ff21accb2ae51114d377c45059 Signed-off-by: Zhu, Bing Signed-off-by: Yan, shaopu Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty.c | 101 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index cfef965402c4..679c5a9a7acf 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -203,59 +203,22 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, return ret; } -static void trusty_std_call_inner_wrapper_remote(void *args) -{ - struct trusty_smc_interface *p_args = args; - struct device *dev = p_args->dev; - ulong smcnr = p_args->args[0]; - ulong a0 = p_args->args[1]; - ulong a1 = p_args->args[2]; - ulong a2 = p_args->args[3]; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); - ulong ret; - unsigned long flags; - - local_irq_save(flags); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, - NULL); - ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, - NULL); - local_irq_restore(flags); - - p_args->args[4] = ret; -} - -static ulong trusty_std_call_inner_wrapper(struct device *dev, ulong smcnr, - ulong a0, ulong a1, ulong a2) -{ - int cpu = 0; - int ret = 0; - struct trusty_smc_interface s; - s.dev = dev; - s.args[0] = smcnr; - s.args[1] = a0; - s.args[2] = a1; - s.args[3] = a2; - s.args[4] = 0; - - ret = smp_call_function_single(cpu, trusty_std_call_inner_wrapper_remote, (void *)&s, 1); - - if (ret) { - pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); - } - - return s.args[4]; -} - static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, ulong a0, ulong a1, ulong a2) { ulong ret; int sleep_time = 1; + unsigned long flags; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { - ret = trusty_std_call_inner_wrapper(dev, smcnr, a0, a1, a2); + local_irq_save(flags); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_restore(flags); if ((int)ret != SM_ERR_BUSY) break; @@ -292,13 +255,33 @@ static void trusty_std_call_cpu_idle(struct trusty_state *s) } } -/* must set CONFIG_DEBUG_ATOMIC_SLEEP=n -** otherwise mutex_lock() will fail and crash -*/ -s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) + +struct trusty_std_call32_args { + struct device *dev; + u32 smcnr; + u32 a0; + u32 a1; + u32 a2; +}; + +static long trusty_std_call32_work(void *args) { int ret; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + struct device *dev; + u32 smcnr, a0, a1, a2; + struct trusty_state *s; + struct trusty_std_call32_args *work_args; + + BUG_ON(!args); + + work_args = args; + dev = work_args->dev; + s = platform_get_drvdata(to_platform_device(dev)); + + smcnr = work_args->smcnr; + a0 = work_args->a0; + a1 = work_args->a1; + a2 = work_args->a2; BUG_ON(SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); @@ -334,6 +317,22 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) return ret; } + +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + const int cpu = 0; + struct trusty_std_call32_args args = { + .dev = dev, + .smcnr = smcnr, + .a0 = a0, + .a1 = a1, + .a2 = a2, + }; + + /* bind cpu 0 for now since trusty OS is running on physical cpu #0*/ + return work_on_cpu(cpu, trusty_std_call32_work, (void *) &args); +} + EXPORT_SYMBOL(trusty_std_call32); int trusty_call_notifier_register(struct device *dev, struct notifier_block *n) From 89c9fadd71eb2d06532eaa67b4dcfddf6bc1c798 Mon Sep 17 00:00:00 2001 From: "Yan, Xiangyang" Date: Wed, 11 Jan 2017 01:26:27 +0000 Subject: [PATCH 0325/1276] trusty: Add VMM PANIC dump data. 1. Increase the alloced size of dump data field to 4 page; 2. Kick off '\r' character in dump data which is outputted from mon_vsprintf_s() in evmm code; Change-Id: I255d97c2a7e898c8d4e1f15777ddd7f7c11af2b0 Tracked-On: Signed-off-by: Yan, Xiangyang Reviewed-by: Gross, Mark --- drivers/trusty/trusty-log.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index a066481c4f1d..8091a596a5e3 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -154,15 +154,28 @@ static void trusty_vmm_dump_header(struct deadloop_dump *dump) static void trusty_vmm_dump_data(struct deadloop_dump *dump) { struct dump_data *dump_data; - int i; + char *p, *pstr; if (!dump) return; dump_data = &(dump->data); - for (i = 0; i < dump_data->length; i++) - pr_info("%c", dump_data->data[i]); + pstr = (char *)dump_data->data; + for (p = pstr; p < ((char *)dump_data->data + dump_data->length); p++) { + if (*p == '\r') { + *p = 0x00; + } else if (*p == '\n') { + *p = 0x00; + pr_info("%s\n", pstr); + pstr = (char *)(p + 1); + } + } + /* dump the characters in the last line */ + if ((pstr - (char *)(dump_data->data)) < dump_data->length) { + *p = 0x00; + pr_info("%s\n", pstr); + } } static int trusty_vmm_panic_notify(struct notifier_block *nb, @@ -287,7 +300,7 @@ static int trusty_log_probe(struct platform_device *pdev) } /* allocate debug buffer for vmm panic dump */ - g_vmm_debug_buf = get_zeroed_page(GFP_KERNEL); + g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); if (!g_vmm_debug_buf) { result = -ENOMEM; goto error_alloc_vmm; From d8885cbd96cda7bb5e8b056fb4985c17630eb745 Mon Sep 17 00:00:00 2001 From: weideng Date: Tue, 28 Mar 2017 01:40:53 +0000 Subject: [PATCH 0326/1276] Modify Trusty drivers so as to compatible with Kernel 4.11 Cpu_hotplug_register/unregister APIs are removed from Kernel 4.11. Add this patch to fix these issues for kernel change. Change-Id: I0ecafaff20128dd53f80fbdc357918ef69a36da7 Signed-off-by: weideng --- drivers/trusty/trusty-ipc.c | 3 +- drivers/trusty/trusty-irq.c | 96 +++++++++++++++++++--------------- drivers/trusty/trusty-virtio.c | 3 +- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index d6765f1d4510..363b0239310a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1549,7 +1550,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL); if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 6c510a65e784..363b302dec0a 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,10 +59,13 @@ struct trusty_irq_state { spinlock_t normal_irqs_lock; struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; - struct notifier_block cpu_notifier; + /* CPU hotplug instances for online */ + struct hlist_node node; struct workqueue_struct *wq; }; +static enum cpuhp_state trusty_irq_online; + #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 static inline void set_pending_intr_to_lk(uint8_t vector) { @@ -252,49 +255,30 @@ irqreturn_t trusty_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static void trusty_irq_cpu_up(void *info) +static int trusty_irq_cpu_up(unsigned int cpu, struct hlist_node *node) { unsigned long irq_flags; - struct trusty_irq_state *is = info; + struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); trusty_irq_enable_irqset(is, this_cpu_ptr(is->percpu_irqs)); local_irq_restore(irq_flags); + return 0; } -static void trusty_irq_cpu_down(void *info) +static int trusty_irq_cpu_down(unsigned int cpu, struct hlist_node *node) { unsigned long irq_flags; - struct trusty_irq_state *is = info; + struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); trusty_irq_disable_irqset(is, this_cpu_ptr(is->percpu_irqs)); local_irq_restore(irq_flags); -} - -static int trusty_irq_cpu_notify(struct notifier_block *nb, - unsigned long action, void *hcpu) -{ - struct trusty_irq_state *is; - - is = container_of(nb, struct trusty_irq_state, cpu_notifier); - - dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_UP_PREPARE: - trusty_irq_cpu_up(is); - break; - case CPU_DEAD: - trusty_irq_cpu_down(is); - break; - } - - return NOTIFY_OK; + return 0; } static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) @@ -580,6 +564,20 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) } */ } +static int trusty_irq_cpu_notif_add(struct trusty_irq_state *is) +{ + int ret; + + ret = cpuhp_state_add_instance(trusty_irq_online, &is->node); + + return ret; +} + +static void trusty_irq_cpu_notif_remove(struct trusty_irq_state *is) +{ + cpuhp_state_remove_instance(trusty_irq_online, &is->node); +} + static int trusty_irq_probe(struct platform_device *pdev) { int ret; @@ -646,23 +644,14 @@ static int trusty_irq_probe(struct platform_device *pdev) for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); - is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; - ret = register_hotcpu_notifier(&is->cpu_notifier); + ret = trusty_irq_cpu_notif_add(is); if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; } - ret = on_each_cpu(trusty_irq_cpu_up, is, 0); - if (ret) { - dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); - goto err_on_each_cpu; - } return 0; -err_on_each_cpu: - unregister_hotcpu_notifier(&is->cpu_notifier); - on_each_cpu(trusty_irq_cpu_down, is, 1); err_register_hotcpu_notifier: spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); trusty_irq_disable_irqset(is, &is->normal_irqs); @@ -692,17 +681,13 @@ static int trusty_irq_probe(struct platform_device *pdev) static int trusty_irq_remove(struct platform_device *pdev) { - int ret; unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "%s\n", __func__); - unregister_hotcpu_notifier(&is->cpu_notifier); - ret = on_each_cpu(trusty_irq_cpu_down, is, 1); - if (ret) - dev_err(&pdev->dev, "on_each_cpu failed %d\n", ret); + trusty_irq_cpu_notif_remove(is); spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); trusty_irq_disable_irqset(is, &is->normal_irqs); spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); @@ -742,8 +727,35 @@ static struct platform_driver trusty_irq_driver = { }, }; -module_platform_driver(trusty_irq_driver); +static int __init trusty_irq_driver_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "x86/trustyirq:online", + trusty_irq_cpu_up, trusty_irq_cpu_down); + if (ret < 0) + goto out; + trusty_irq_online = ret; + + ret = platform_driver_register(&trusty_irq_driver); + if (ret) + goto err_dead; + + return 0; +err_dead: + cpuhp_remove_multi_state(trusty_irq_online); +out: + return ret; +} + +static void __exit trusty_irq_driver_exit(void) +{ + cpuhp_remove_multi_state(trusty_irq_online); + platform_driver_unregister(&trusty_irq_driver); +} +module_init(trusty_irq_driver_init); +module_exit(trusty_irq_driver_exit); MODULE_LICENSE("GPL v2"); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index eaeb020e98f4..3d1a9aabef83 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -349,7 +349,8 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { uint i; int ret; From 47bc72b2dcc64f1924dba10d0ef2d4c251368eea Mon Sep 17 00:00:00 2001 From: yingbinx Date: Wed, 22 Feb 2017 14:28:03 +0800 Subject: [PATCH 0327/1276] Limit to output trusty/lk log on debug version Modified trusty_dump_log() to only output lk side's log on debug version. This is to avoid the condition that tipc drivers will print lots of info/log from lk side at one time to serial console on release version. Details may reference Change-Id: I28681a97a037d08a97d13b8314ab05f4f13b2309 Tracked-On: Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-log.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 8091a596a5e3..4200e901d925 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -63,7 +63,7 @@ static int log_read_line(struct trusty_log_state *s, int put, int get) return i; } -static void trusty_dump_logs(struct trusty_log_state *s) +static void trusty_dump_logs(struct trusty_log_state *s, bool dump_panic_log) { struct log_rb *log = s->log; uint32_t get, put, alloc; @@ -99,7 +99,10 @@ static void trusty_dump_logs(struct trusty_log_state *s) get = alloc - log->sz; continue; } - pr_info("trusty: %s", s->line_buffer); + + if (dump_panic_log) + pr_info("trusty: %s", s->line_buffer); + get += read_chars; } s->get = get; @@ -116,7 +119,11 @@ static int trusty_log_call_notify(struct notifier_block *nb, s = container_of(nb, struct trusty_log_state, call_notifier); spin_lock_irqsave(&s->lock, flags); - trusty_dump_logs(s); +#ifdef CONFIG_DEBUG_INFO + trusty_dump_logs(s, true); +#else + trusty_dump_logs(s, false); +#endif spin_unlock_irqrestore(&s->lock, flags); return NOTIFY_OK; } @@ -133,7 +140,7 @@ static int trusty_log_panic_notify(struct notifier_block *nb, s = container_of(nb, struct trusty_log_state, panic_notifier); pr_info("trusty-log panic notifier - trusty version %s", trusty_version_str_get(s->trusty_dev)); - trusty_dump_logs(s); + trusty_dump_logs(s, true); return NOTIFY_OK; } From f5c6b46628377572eaa24867d13de63d1dfb49b6 Mon Sep 17 00:00:00 2001 From: "Yan, Xiangyang" Date: Tue, 21 Mar 2017 13:31:33 +0800 Subject: [PATCH 0328/1276] trusty-ipc:tipc_msg_hdr structure: support large message transfer len field type of tipc_msg_hdr structure is u16 which will only handle message length of less than 64K. Change it to u32 to support larger message. Change-Id: I9f08d699842723224a10242d19165fa748a8c8b4 Tracked-On: Signed-off-by: Yan, Xiangyang Reviewed-on: --- drivers/trusty/trusty-ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 363b0239310a..44843eb811bd 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -71,9 +71,9 @@ struct tipc_dev_config { struct tipc_msg_hdr { u32 src; u32 dst; - u32 reserved; - u16 len; + u32 len; u16 flags; + u16 reserved; u8 data[0]; } __packed; From ecba7638024151ff089e6c647ec3fb2980d045b5 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Mon, 13 Mar 2017 13:22:21 +0800 Subject: [PATCH 0329/1276] trusty-ipc: change DEFAULT_MSG_BUF_SIZE to 68K after hw-backed keymaster enabled, the cts cases of testLargeMsgKat and testLongMsgKat both will failed due to the default CHUNK size(64K) is exceed the channel buffer size in trusty keymaster which use the 4K as default. In order to fix the failed cases, we will enlarge the default channel buffer size to 68K. Change-Id: I2bfb0174430962c6e66c08033be958aaffeca515 Tracked-On: Signed-off-by: Yan, Shaopu Reviewed-on: --- drivers/trusty/trusty-ipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 44843eb811bd..9d6f6bf94f97 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -45,7 +45,8 @@ #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 -#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE +#define DEFAULT_MSG_BUF_SIZE (68*1024) + #define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE #define TIPC_CTRL_ADDR 53 From e73b298ddf395010b04a965beaf24da3842e5a90 Mon Sep 17 00:00:00 2001 From: yingbinx Date: Mon, 27 Mar 2017 12:24:04 +0800 Subject: [PATCH 0330/1276] check CPUID while probe trusty drivers. Trusty ipc drivers only work when eVmm is alive. So when probe the trusty drivers, we need to call CPUID to check if eVmm is already existed. Change-Id: I295785b0510729aa2e9d212b243d7c242370389f Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-ipc.c | 7 +++++++ drivers/trusty/trusty-irq.c | 6 ++++++ drivers/trusty/trusty-log.c | 6 ++++++ drivers/trusty/trusty-virtio.c | 6 ++++++ drivers/trusty/trusty.c | 6 ++++++ include/linux/trusty/trusty.h | 16 ++++++++++++++++ 6 files changed, 47 insertions(+) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 9d6f6bf94f97..a05c5f957146 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -31,6 +31,7 @@ #include #include +#include #define MAX_DEVICES 4 @@ -1524,6 +1525,12 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; + err = trusty_check_cpuid(); + if (err < 0) { + dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&vdev->dev, "%s:\n", __func__); vds = kzalloc(sizeof(*vds), GFP_KERNEL); diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 363b302dec0a..afdea66c23c2 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -587,6 +587,12 @@ static int trusty_irq_probe(struct platform_device *pdev) struct trusty_irq_state *is; work_func_t work_func; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); is = kzalloc(sizeof(*is), GFP_KERNEL); diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 4200e901d925..c977d33ccde5 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -254,6 +254,12 @@ static int trusty_log_probe(struct platform_device *pdev) phys_addr_t pa; struct deadloop_dump *dump; + result = trusty_check_cpuid(); + if (result < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_supports_logging(pdev->dev.parent)) { return -ENXIO; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 3d1a9aabef83..2ce818cef175 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -628,6 +628,12 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_info(&pdev->dev, "initializing\n"); tctx = kzalloc(sizeof(*tctx), GFP_KERNEL); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 679c5a9a7acf..93c73882b00c 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -450,6 +450,12 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + if (!node) { dev_err(&pdev->dev, "of_node required\n"); return -EINVAL; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 74598389c308..7dc2dad40daa 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -69,4 +69,20 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, struct page *page, u32 size, pgprot_t pgprot); +/* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ +#define EVMM_RUNNING_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ +#define EVMM_RUNNING_SIGNATURE_MON 0x4D4D5645 /* "XMON", ecx */ + +static inline int trusty_check_cpuid(void) +{ + u32 eax, ebx, ecx, edx; + + cpuid(3, &eax, &ebx, &ecx, &edx); + if ((ecx != EVMM_RUNNING_SIGNATURE_MON) || + (edx != EVMM_RUNNING_SIGNATURE_CORP)) { + return -EINVAL; + } + + return 0; +} #endif From 5c1ff04ec173cbc5ea71b38aac1f42df55cee4cb Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Thu, 18 May 2017 16:31:32 +0800 Subject: [PATCH 0331/1276] Fix the compile error when update 4.12 virtio API updated on 4.12, trusty need update accordingly. Change-Id: I6ef8a63a23d19cbce1471f9f3bc6e8a38002ad25 Tracked-On: --- drivers/trusty/trusty-ipc.c | 2 +- drivers/trusty/trusty-virtio.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index a05c5f957146..68f677f91c21 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1558,7 +1558,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL); + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL, NULL); if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 2ce818cef175..2368c10f1b7b 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -328,7 +328,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, - vdev, true, tvr->vaddr, + vdev, true, true, tvr->vaddr, trusty_virtio_notify, callback, name); if (!tvr->vq) { dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", @@ -350,6 +350,7 @@ static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + const bool *ctx, struct irq_affinity *desc) { uint i; From b572e1da73b3ef2bddb1ae6efe549f1318172887 Mon Sep 17 00:00:00 2001 From: yingbinx Date: Tue, 9 May 2017 13:45:06 +0800 Subject: [PATCH 0332/1276] trusty: Fix the warnings for eywa building Several warnings are generated while we build for eywa with ARCH i386. The patch is to fix the warnings. Tested by tipc test cases and CTS, all are pass. Change-Id: I2710dd94dfb635f12f5b482a894891bcf725f6be Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-log.c | 10 +++++++--- drivers/trusty/trusty-virtio.c | 2 +- include/linux/trusty/trusty.h | 7 +++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index c977d33ccde5..0f00d0074fc9 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -26,7 +26,11 @@ #define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) #define TRUSTY_LINE_BUFFER_SIZE 256 +#ifdef CONFIG_64BIT static uint64_t g_vmm_debug_buf; +#else +static uint32_t g_vmm_debug_buf; +#endif struct trusty_log_state { struct device *dev; @@ -286,7 +290,7 @@ static int trusty_log_probe(struct platform_device *pdev) pa = page_to_phys(s->log_pages); result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_ADD, - (u32)(pa), (u32)(pa >> 32), + (u32)(pa), (u32)HIULINT(pa), TRUSTY_LOG_SIZE); if (result < 0) { pr_err("trusty std call (SMC_SC_SHARED_LOG_ADD) failed: %d %pa\n", @@ -354,7 +358,7 @@ static int trusty_log_probe(struct platform_device *pdev) trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); error_call_notifier: trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, - (u32)pa, (u32)(pa >> 32), 0); + (u32)pa, (u32)HIULINT(pa), 0); error_std_call: __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); error_alloc_log: @@ -378,7 +382,7 @@ static int trusty_log_remove(struct platform_device *pdev) trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, - (u32)pa, (u32)(pa >> 32), 0); + (u32)pa, (u32)HIULINT(pa), 0); if (result) { pr_err("trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n", result); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 2368c10f1b7b..6cb1ec762efe 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -322,7 +322,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, /* da field is only 32 bit wide. Use previously unused 'reserved' field * to store top 32 bits of 64-bit address */ - tvr->vr_descr->pa = (u32)(pa >> 32); + tvr->vr_descr->pa = (u32)HIULINT(pa); dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 7dc2dad40daa..f7b0a14c9a1d 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,4 +85,11 @@ static inline int trusty_check_cpuid(void) return 0; } + +/* High 32 bits of unsigned 64-bit integer*/ +#ifdef CONFIG_64BIT +#define HIULINT(x) ((x) >> 32) +#else +#define HIULINT(x) 0 +#endif #endif From 2fd66ce91216f754fcb5a04257888fb0c5d0ae33 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 6 Jun 2017 00:15:01 +0000 Subject: [PATCH 0333/1276] trusty: Enable dynamic timer Enable the dynamic timer support for Trusty scheduling. Besides periodic timer, Trusty now supports both dynamic timer and periodic timer. Proxy timer drives the Trusty OS scheduling in fixed periodical intervals. Dynamic timer is similar to tickless mode which will not schedule if Trusty OS is idle. This patch will consult Trusty OS for the timer mode to use and enable the specified timer to drive Trusty scheduling. Change-Id: Ic972c40d768cb59a8326842c698fafbe45af906c Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty.c | 91 ++++++++++++++++++++++++++++++++--- include/linux/trusty/smcall.h | 17 +++++++ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 93c73882b00c..647031dacb4e 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -25,9 +25,10 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_VMCALL_SMC 0x74727500 #define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ #define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ +#define TRUSTY_STOP_TIMER 0xFFFFFFFF enum lktimer_mode { ONESHOT_TIMER, @@ -52,6 +53,12 @@ struct trusty_smc_interface { ulong args[5]; }; +static struct timer_list *lk_timer; + +static ulong (*smc_func)(ulong r0, ulong r1, ulong r2, ulong r3); +static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3); +static ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3); + static void trusty_lktimer_work_func(struct work_struct *work) { int ret; @@ -72,7 +79,7 @@ static void trusty_lktimer_work_func(struct work_struct *work) if (ret != SM_ERR_NOP_DONE) dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - dev_notice_once(s->dev, "LK OS proxy timer works\n"); + dev_notice_once(s->dev, "LK OS timer works\n"); } static void trusty_lktimer_func(unsigned long data) @@ -92,6 +99,7 @@ static void trusty_init_lktimer(struct trusty_state *s) { INIT_WORK(&s->timer_work, trusty_lktimer_work_func); setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); + lk_timer = &s->timer; } /* note that this function is not thread-safe */ @@ -108,6 +116,39 @@ static void trusty_configure_lktimer(struct trusty_state *s, mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); } +static void trusty_init_smc_function(void) +{ + smc_func = smc_periodic_timer; +} + +static void trusty_set_timer_mode(struct trusty_state *s, struct device *dev) +{ + int ret; + + ret = trusty_fast_call32(dev, SMC_FC_TIMER_MODE, 0, 0, 0); + + if (ret == 0) { + smc_func = smc_dynamic_timer; + } else { + smc_func = smc_periodic_timer; + /* + * If bit 31 set indicates periodic timer is used + * bit 15:0 indicates interval + */ + if ((ret & 0x80000000) && (ret & 0x0FFFF)) { + trusty_configure_lktimer(s, + PERIODICAL_TIMER, + ret & 0x0FFFF); + } else { + /* set periodical timer with default interval */ + trusty_configure_lktimer(s, + PERIODICAL_TIMER, + TRUSTY_LKTIMER_INTERVAL); + } + } + +} + /* * this should be called when removing trusty dev and * when LK/Trusty crashes, to disable proxy timer. @@ -119,12 +160,45 @@ static void trusty_del_lktimer(struct trusty_state *s) } static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +{ + return smc_func(r0, r1, r2, r3); +} + +static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( "vmcall; \n" - :"=D"(r0) - :"a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) ); + + if (((r0 == SM_ERR_NOP_INTERRUPTED) || + (r0 == SM_ERR_INTERRUPTED)) && + (r1 != 0)) { + struct trusty_state *s; + + if (lk_timer != NULL) { + s = container_of(lk_timer, struct trusty_state, timer); + if (r1 != TRUSTY_STOP_TIMER) + trusty_configure_lktimer(s, ONESHOT_TIMER, r1); + else + trusty_configure_lktimer(s, ONESHOT_TIMER, 0); + } else { + pr_err("Trusty timer has not been initialized yet!\n"); + } + } + + return r0; +} + +static inline ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3) +{ + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } @@ -472,19 +546,20 @@ static int trusty_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); s->dev = &pdev->dev; + trusty_init_smc_function(); + trusty_init_lktimer(s); + trusty_set_timer_mode(s, &pdev->dev); + trusty_init_version(s, &pdev->dev); ret = trusty_init_api_version(s, &pdev->dev); if (ret < 0) goto err_api_version; - trusty_init_lktimer(s); - trusty_configure_lktimer(s, - PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); - return 0; err_api_version: + trusty_del_lktimer(s); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 1160890a3d90..974b7b3e753d 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -123,6 +123,23 @@ #define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) +/** + * SMC_FC_TIMER_MODE - Find and set timer mode + * Returns timer mode from trusty. + * + * Return value stands for: + * Bit 31 : + * If this bit is set, trusty uses periodic timer, Android trusty driver + * injects timer interrupt to trusty with specified interval. + * If this bit is clear, trusty uses dynamic timer, Android trusty + * driver injects timer interrupt to trusty on demand. + * Bit 15:0 : + * If bit 31 is set, Android trusty driver injects timer interrupt to + * trusty with interval specified by this field in milliseconds. + * If bit 31 is clear, this field is ignored. + */ +#define SMC_FC_TIMER_MODE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) From 2be2a87f64f1b9421511304fb46e92f3a545faef Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Wed, 28 Jun 2017 06:26:15 +0000 Subject: [PATCH 0334/1276] check vmm signature for vmm dump Change-Id: Ibc0e1ebf561b0b4278bb5f2d92d173685810aa22 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-ipc.c | 2 +- drivers/trusty/trusty-irq.c | 2 +- drivers/trusty/trusty-log.c | 55 ++++++++++++++++++---------------- drivers/trusty/trusty-virtio.c | 2 +- drivers/trusty/trusty.c | 2 +- include/linux/trusty/trusty.h | 14 +++++---- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 68f677f91c21..93003b45eb32 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1525,7 +1525,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; - err = trusty_check_cpuid(); + err = trusty_check_cpuid(NULL); if (err < 0) { dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index afdea66c23c2..d17162c6a85e 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -587,7 +587,7 @@ static int trusty_irq_probe(struct platform_device *pdev) struct trusty_irq_state *is; work_func_t work_func; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 0f00d0074fc9..c5a85ccaf222 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -255,10 +255,11 @@ static int trusty_log_probe(struct platform_device *pdev) { struct trusty_log_state *s; int result; + u32 vmm_signature; phys_addr_t pa; struct deadloop_dump *dump; - result = trusty_check_cpuid(); + result = trusty_check_cpuid(&vmm_signature); if (result < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; @@ -316,33 +317,35 @@ static int trusty_log_probe(struct platform_device *pdev) goto error_panic_notifier; } - /* allocate debug buffer for vmm panic dump */ - g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); - if (!g_vmm_debug_buf) { - result = -ENOMEM; - goto error_alloc_vmm; - } - - dump = (struct deadloop_dump *)g_vmm_debug_buf; - dump->version_of_this_struct = VMM_DUMP_VERSION; - dump->size_of_this_struct = sizeof(struct deadloop_dump); - dump->is_valid = false; + if(vmm_signature == EVMM_SIGNATURE_VMM) { + /* allocate debug buffer for vmm panic dump */ + g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); + if (!g_vmm_debug_buf) { + result = -ENOMEM; + goto error_alloc_vmm; + } - /* shared the buffer to vmm by VMCALL */ - result = trusty_vmm_dump_init(dump); - if (result < 0) { - dev_err(&pdev->dev, - "failed to share the dump buffer to VMM\n"); - goto error_vmm_panic_notifier; - } + dump = (struct deadloop_dump *)g_vmm_debug_buf; + dump->version_of_this_struct = VMM_DUMP_VERSION; + dump->size_of_this_struct = sizeof(struct deadloop_dump); + dump->is_valid = false; + + /* shared the buffer to vmm by VMCALL */ + result = trusty_vmm_dump_init(dump); + if (result < 0) { + dev_err(&pdev->dev, + "failed to share the dump buffer to VMM\n"); + goto error_vmm_panic_notifier; + } - /* register the panic notifier for vmm */ - result = atomic_notifier_chain_register(&panic_notifier_list, - &trusty_vmm_panic_nb); - if (result < 0) { - dev_err(&pdev->dev, - "failed to register vmm panic notifier\n"); - goto error_vmm_panic_notifier; + /* register the panic notifier for vmm */ + result = atomic_notifier_chain_register(&panic_notifier_list, + &trusty_vmm_panic_nb); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register vmm panic notifier\n"); + goto error_vmm_panic_notifier; + } } platform_set_drvdata(pdev, s); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 6cb1ec762efe..6bbf80ce7d7f 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -629,7 +629,7 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 647031dacb4e..8e7e715d7018 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -524,7 +524,7 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index f7b0a14c9a1d..aba204b9ff3a 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -70,19 +70,23 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, pgprot_t pgprot); /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ -#define EVMM_RUNNING_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ -#define EVMM_RUNNING_SIGNATURE_MON 0x4D4D5645 /* "XMON", ecx */ +#define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ +#define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ -static inline int trusty_check_cpuid(void) +static inline int trusty_check_cpuid(u32 *vmm_signature) { u32 eax, ebx, ecx, edx; cpuid(3, &eax, &ebx, &ecx, &edx); - if ((ecx != EVMM_RUNNING_SIGNATURE_MON) || - (edx != EVMM_RUNNING_SIGNATURE_CORP)) { + if ((ecx != EVMM_SIGNATURE_VMM) || + (edx != EVMM_SIGNATURE_CORP)) { return -EINVAL; } + if(vmm_signature) { + *vmm_signature = ecx; + } + return 0; } From a759ad442f9b476d177dc51f93eda16f99206fdd Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:15:01 +0000 Subject: [PATCH 0335/1276] Revert "[BXT][DYNAMIC TIMER] Enable dynamic timer" This reverts commit 56dae7b0c686eeaf2ff604497ab940328124f611. Change-Id: I3603787890e6de43acc5f895034237e7b0c5f954 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty.c | 88 +++-------------------------------- include/linux/trusty/smcall.h | 17 ------- 2 files changed, 7 insertions(+), 98 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8e7e715d7018..2fc1b232fee3 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -25,10 +25,9 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_VMCALL_SMC 0x74727500 #define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ #define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ -#define TRUSTY_STOP_TIMER 0xFFFFFFFF enum lktimer_mode { ONESHOT_TIMER, @@ -53,12 +52,6 @@ struct trusty_smc_interface { ulong args[5]; }; -static struct timer_list *lk_timer; - -static ulong (*smc_func)(ulong r0, ulong r1, ulong r2, ulong r3); -static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3); -static ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3); - static void trusty_lktimer_work_func(struct work_struct *work) { int ret; @@ -79,7 +72,7 @@ static void trusty_lktimer_work_func(struct work_struct *work) if (ret != SM_ERR_NOP_DONE) dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - dev_notice_once(s->dev, "LK OS timer works\n"); + dev_notice_once(s->dev, "LK OS proxy timer works\n"); } static void trusty_lktimer_func(unsigned long data) @@ -99,7 +92,6 @@ static void trusty_init_lktimer(struct trusty_state *s) { INIT_WORK(&s->timer_work, trusty_lktimer_work_func); setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); - lk_timer = &s->timer; } /* note that this function is not thread-safe */ @@ -116,39 +108,6 @@ static void trusty_configure_lktimer(struct trusty_state *s, mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); } -static void trusty_init_smc_function(void) -{ - smc_func = smc_periodic_timer; -} - -static void trusty_set_timer_mode(struct trusty_state *s, struct device *dev) -{ - int ret; - - ret = trusty_fast_call32(dev, SMC_FC_TIMER_MODE, 0, 0, 0); - - if (ret == 0) { - smc_func = smc_dynamic_timer; - } else { - smc_func = smc_periodic_timer; - /* - * If bit 31 set indicates periodic timer is used - * bit 15:0 indicates interval - */ - if ((ret & 0x80000000) && (ret & 0x0FFFF)) { - trusty_configure_lktimer(s, - PERIODICAL_TIMER, - ret & 0x0FFFF); - } else { - /* set periodical timer with default interval */ - trusty_configure_lktimer(s, - PERIODICAL_TIMER, - TRUSTY_LKTIMER_INTERVAL); - } - } - -} - /* * this should be called when removing trusty dev and * when LK/Trusty crashes, to disable proxy timer. @@ -160,45 +119,12 @@ static void trusty_del_lktimer(struct trusty_state *s) } static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) -{ - return smc_func(r0, r1, r2, r3); -} - -static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( "vmcall; \n" - : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "=D"(r0) : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) ); - - if (((r0 == SM_ERR_NOP_INTERRUPTED) || - (r0 == SM_ERR_INTERRUPTED)) && - (r1 != 0)) { - struct trusty_state *s; - - if (lk_timer != NULL) { - s = container_of(lk_timer, struct trusty_state, timer); - if (r1 != TRUSTY_STOP_TIMER) - trusty_configure_lktimer(s, ONESHOT_TIMER, r1); - else - trusty_configure_lktimer(s, ONESHOT_TIMER, 0); - } else { - pr_err("Trusty timer has not been initialized yet!\n"); - } - } - - return r0; -} - -static inline ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3) -{ - __asm__ __volatile__( - "vmcall; \n" - : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) - : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - ); - return r0; } @@ -546,16 +472,16 @@ static int trusty_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); s->dev = &pdev->dev; - trusty_init_smc_function(); - trusty_init_lktimer(s); - trusty_set_timer_mode(s, &pdev->dev); - trusty_init_version(s, &pdev->dev); ret = trusty_init_api_version(s, &pdev->dev); if (ret < 0) goto err_api_version; + trusty_init_lktimer(s); + trusty_configure_lktimer(s, + PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); + return 0; err_api_version: diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 974b7b3e753d..1160890a3d90 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -123,23 +123,6 @@ #define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) -/** - * SMC_FC_TIMER_MODE - Find and set timer mode - * Returns timer mode from trusty. - * - * Return value stands for: - * Bit 31 : - * If this bit is set, trusty uses periodic timer, Android trusty driver - * injects timer interrupt to trusty with specified interval. - * If this bit is clear, trusty uses dynamic timer, Android trusty - * driver injects timer interrupt to trusty on demand. - * Bit 15:0 : - * If bit 31 is set, Android trusty driver injects timer interrupt to - * trusty with interval specified by this field in milliseconds. - * If bit 31 is clear, this field is ignored. - */ -#define SMC_FC_TIMER_MODE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) - /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) From 89d71b58a2e4afdd9eeae254a93f209ad34fa704 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:19:21 +0000 Subject: [PATCH 0336/1276] Revert "trusty: implement trusty OS timer proxy for performance enhancement" This reverts commit 3e30c8c0a0b5928bc11fa44571563635a9b1e0a8. Change-Id: I16e64b07a9ddfd50f44ab85ed0aa27925c8ac8a2 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty-irq.c | 2 + drivers/trusty/trusty.c | 88 ------------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index d17162c6a85e..e60068b50e04 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -651,6 +651,8 @@ static int trusty_irq_probe(struct platform_device *pdev) irq = trusty_irq_init_one(is, irq, false); ret = trusty_irq_cpu_notif_add(is); + irq_register_done(); + if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 2fc1b232fee3..7e55453ae5f5 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,23 +26,11 @@ #include #define TRUSTY_VMCALL_SMC 0x74727500 -#define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ -#define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ - -enum lktimer_mode { - ONESHOT_TIMER, - PERIODICAL_TIMER, -}; struct trusty_state { - struct device *dev; struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; - struct timer_list timer; - struct work_struct timer_work; - enum lktimer_mode timer_mode; - unsigned long timer_interval; char *version_str; u32 api_version; }; @@ -52,72 +40,6 @@ struct trusty_smc_interface { ulong args[5]; }; -static void trusty_lktimer_work_func(struct work_struct *work) -{ - int ret; - unsigned int vector; - struct trusty_state *s = - container_of(work, struct trusty_state, timer_work); - - dev_dbg(s->dev, "%s\n", __func__); - - /* need vector number only for the first time */ - vector = TRUSTY_LKTIMER_VECTOR; - - do { - ret = trusty_std_call32(s->dev, SMC_SC_NOP, vector, 0, 0); - vector = 0; - } while (ret == SM_ERR_NOP_INTERRUPTED); - - if (ret != SM_ERR_NOP_DONE) - dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - - dev_notice_once(s->dev, "LK OS proxy timer works\n"); -} - -static void trusty_lktimer_func(unsigned long data) -{ - struct trusty_state *s = (struct trusty_state *)data; - - /* binding it physical CPU0 only because trusty OS runs on it */ - schedule_work_on(0, &s->timer_work); - - /* reactivate the timer again in periodic mode */ - if (s->timer_mode == PERIODICAL_TIMER) - mod_timer(&s->timer, - jiffies + msecs_to_jiffies(s->timer_interval)); -} - -static void trusty_init_lktimer(struct trusty_state *s) -{ - INIT_WORK(&s->timer_work, trusty_lktimer_work_func); - setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); -} - -/* note that this function is not thread-safe */ -static void trusty_configure_lktimer(struct trusty_state *s, - enum lktimer_mode mode, unsigned long interval) -{ - if (mode != ONESHOT_TIMER && mode != PERIODICAL_TIMER) { - pr_err("%s: invalid timer mode: %d\n", __func__, mode); - return; - } - - s->timer_mode = mode; - s->timer_interval = interval; - mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); -} - -/* - * this should be called when removing trusty dev and - * when LK/Trusty crashes, to disable proxy timer. - */ -static void trusty_del_lktimer(struct trusty_state *s) -{ - del_timer_sync(&s->timer); - flush_work(&s->timer_work); -} - static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( @@ -307,9 +229,6 @@ static long trusty_std_call32_work(void *args) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); - if (ret == SM_ERR_PANIC) - trusty_del_lktimer(s); - if (smcnr == SMC_SC_NOP) complete(&s->cpu_idle_completion); else @@ -470,7 +389,6 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); - s->dev = &pdev->dev; trusty_init_version(s, &pdev->dev); @@ -478,14 +396,9 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; - trusty_init_lktimer(s); - trusty_configure_lktimer(s, - PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); - return 0; err_api_version: - trusty_del_lktimer(s); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); @@ -509,7 +422,6 @@ static int trusty_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); } - trusty_del_lktimer(s); kfree(s); return 0; } From ce7bc340df3431e0a91d2f5c1ef9ff14f1a93f47 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 11 Jul 2017 04:42:49 +0000 Subject: [PATCH 0337/1276] trusty: add support for parameterized NOP ops Parameterized NOPs are introduced by Trusty secure side to facilitate better SMP concurrency. They are effectively NOP calls with parameters that will be routed to appropriate handlers on secure side which can be executed concurrently on multiple CPUs. Parameterized NOPs are represented by trusty_nop structure that has to be initialized by calling trusty_nop_init call. This patch creates queue for such items, adds per CPU work queue to invoke them and adds API to enqueue and dequeue them. Change-Id: I7cf32bfdf07727e7d9b0d955ddfb3bf1b52e3a46 Signed-off-by: Zhong,Fangjian Author: Michael Ryleev --- drivers/trusty/trusty-irq.c | 96 +------------------ drivers/trusty/trusty.c | 169 ++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 3 +- include/linux/trusty/trusty.h | 17 ++++ 4 files changed, 189 insertions(+), 96 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index e60068b50e04..5b4686f4f85f 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -41,11 +41,6 @@ struct trusty_irq { struct trusty_irq __percpu *percpu_ptr; }; -struct trusty_irq_work { - struct trusty_irq_state *is; - struct work_struct work; -}; - struct trusty_irq_irqset { struct hlist_head pending; struct hlist_head inactive; @@ -54,14 +49,12 @@ struct trusty_irq_irqset { struct trusty_irq_state { struct device *dev; struct device *trusty_dev; - struct trusty_irq_work __percpu *irq_work; struct trusty_irq_irqset normal_irqs; spinlock_t normal_irqs_lock; struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; /* CPU hotplug instances for online */ struct hlist_node node; - struct workqueue_struct *wq; }; static enum cpuhp_state trusty_irq_online; @@ -183,46 +176,10 @@ static int trusty_irq_call_notify(struct notifier_block *nb, return NOTIFY_OK; } - -static void trusty_irq_work_func_locked_nop(struct work_struct *work) -{ - int ret; - struct trusty_irq_state *is = - container_of(work, struct trusty_irq_work, work)->is; - - dev_dbg(is->dev, "%s\n", __func__); - - ret = trusty_std_call32(is->trusty_dev, SMC_SC_LOCKED_NOP, 0, 0, 0); - if (ret != 0) - dev_err(is->dev, "%s: SMC_SC_LOCKED_NOP failed %d", - __func__, ret); - - dev_dbg(is->dev, "%s: done\n", __func__); -} - -static void trusty_irq_work_func(struct work_struct *work) -{ - int ret; - struct trusty_irq_state *is = - container_of(work, struct trusty_irq_work, work)->is; - - dev_dbg(is->dev, "%s\n", __func__); - - do { - ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); - } while (ret == SM_ERR_NOP_INTERRUPTED); - - if (ret != SM_ERR_NOP_DONE) - dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - - dev_dbg(is->dev, "%s: done\n", __func__); -} - irqreturn_t trusty_irq_handler(int irq, void *data) { struct trusty_irq *trusty_irq = data; struct trusty_irq_state *is = trusty_irq->is; - struct trusty_irq_work *trusty_irq_work = this_cpu_ptr(is->irq_work); struct trusty_irq_irqset *irqset; dev_dbg(is->dev, "%s: irq %d, percpu %d, cpu %d, enable %d\n", @@ -248,7 +205,7 @@ irqreturn_t trusty_irq_handler(int irq, void *data) } spin_unlock(&is->normal_irqs_lock); - queue_work_on(raw_smp_processor_id(), is->wq, &trusty_irq_work->work); + trusty_enqueue_nop(is->trusty_dev, NULL); dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); @@ -582,10 +539,8 @@ static int trusty_irq_probe(struct platform_device *pdev) { int ret; int irq; - unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is; - work_func_t work_func; ret = trusty_check_cpuid(NULL); if (ret < 0) { @@ -601,19 +556,8 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_alloc_is; } - is->wq = alloc_workqueue("trusty-irq-wq", WQ_CPU_INTENSIVE, 0); - if (!is->wq) { - ret = -ENOMEM; - goto err_alloc_wq; - } - is->dev = &pdev->dev; is->trusty_dev = is->dev->parent; - is->irq_work = alloc_percpu(struct trusty_irq_work); - if (!is->irq_work) { - ret = -ENOMEM; - goto err_alloc_irq_work; - } spin_lock_init(&is->normal_irqs_lock); is->percpu_irqs = alloc_percpu(struct trusty_irq_irqset); if (!is->percpu_irqs) { @@ -632,21 +576,6 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_trusty_call_notifier_register; } - if (trusty_get_api_version(is->trusty_dev) < TRUSTY_API_VERSION_SMP) - work_func = trusty_irq_work_func_locked_nop; - else - work_func = trusty_irq_work_func; - - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - trusty_irq_work->is = is; - INIT_WORK(&trusty_irq_work->work, work_func); - } - for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); @@ -670,18 +599,6 @@ static int trusty_irq_probe(struct platform_device *pdev) err_trusty_call_notifier_register: free_percpu(is->percpu_irqs); err_alloc_pending_percpu_irqs: - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - flush_work(&trusty_irq_work->work); - } - free_percpu(is->irq_work); -err_alloc_irq_work: - destroy_workqueue(is->wq); -err_alloc_wq: kfree(is); err_alloc_is: return ret; @@ -689,7 +606,6 @@ static int trusty_irq_probe(struct platform_device *pdev) static int trusty_irq_remove(struct platform_device *pdev) { - unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is = platform_get_drvdata(pdev); @@ -705,16 +621,6 @@ static int trusty_irq_remove(struct platform_device *pdev) trusty_call_notifier_unregister(is->trusty_dev, &is->trusty_call_notifier); free_percpu(is->percpu_irqs); - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - flush_work(&trusty_irq_work->work); - } - free_percpu(is->irq_work); - destroy_workqueue(is->wq); kfree(is); return 0; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7e55453ae5f5..4aa4a89799dc 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,12 +27,24 @@ #define TRUSTY_VMCALL_SMC 0x74727500 +struct trusty_state; + +struct trusty_work { + struct trusty_state *ts; + struct work_struct work; +}; + struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; char *version_str; u32 api_version; + struct device *dev; + struct workqueue_struct *nop_wq; + struct trusty_work __percpu *nop_works; + struct list_head nop_queue; + spinlock_t nop_lock; /* protects nop_queue */ }; struct trusty_smc_interface { @@ -363,9 +375,116 @@ static int trusty_init_api_version(struct trusty_state *s, struct device *dev) return 0; } +static bool dequeue_nop(struct trusty_state *s, u32 *args) +{ + unsigned long flags; + struct trusty_nop *nop = NULL; + + spin_lock_irqsave(&s->nop_lock, flags); + if (!list_empty(&s->nop_queue)) { + nop = list_first_entry(&s->nop_queue, + struct trusty_nop, node); + list_del_init(&nop->node); + args[0] = nop->args[0]; + args[1] = nop->args[1]; + args[2] = nop->args[2]; + } else { + args[0] = 0; + args[1] = 0; + args[2] = 0; + } + spin_unlock_irqrestore(&s->nop_lock, flags); + return nop; +} + +static void locked_nop_work_func(struct work_struct *work) +{ + int ret; + struct trusty_work *tw = container_of(work, struct trusty_work, work); + struct trusty_state *s = tw->ts; + + dev_dbg(s->dev, "%s\n", __func__); + + ret = trusty_std_call32(s->dev, SMC_SC_LOCKED_NOP, 0, 0, 0); + if (ret != 0) + dev_err(s->dev, "%s: SMC_SC_LOCKED_NOP failed %d", + __func__, ret); + dev_dbg(s->dev, "%s: done\n", __func__); +} + +static void nop_work_func(struct work_struct *work) +{ + int ret; + bool next; + u32 args[3]; + struct trusty_work *tw = container_of(work, struct trusty_work, work); + struct trusty_state *s = tw->ts; + + dev_dbg(s->dev, "%s:\n", __func__); + + dequeue_nop(s, args); + do { + dev_dbg(s->dev, "%s: %x %x %x\n", + __func__, args[0], args[1], args[2]); + + ret = trusty_std_call32(s->dev, SMC_SC_NOP, + args[0], args[1], args[2]); + + next = dequeue_nop(s, args); + + if (ret == SM_ERR_NOP_INTERRUPTED) + next = true; + else if (ret != SM_ERR_NOP_DONE) + dev_err(s->dev, "%s: SMC_SC_NOP failed %d", + __func__, ret); + } while (next); + + dev_dbg(s->dev, "%s: done\n", __func__); +} + +void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) +{ + unsigned long flags; + struct trusty_work *tw; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + preempt_disable(); + tw = this_cpu_ptr(s->nop_works); + if (nop) { + WARN_ON(s->api_version < TRUSTY_API_VERSION_SMP_NOP); + + spin_lock_irqsave(&s->nop_lock, flags); + if (list_empty(&nop->node)) + list_add_tail(&nop->node, &s->nop_queue); + spin_unlock_irqrestore(&s->nop_lock, flags); + } + queue_work(s->nop_wq, &tw->work); + preempt_enable(); +} +EXPORT_SYMBOL(trusty_enqueue_nop); + +void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop) +{ + unsigned long flags; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + if (WARN_ON(!nop)) + return; + + spin_lock_irqsave(&s->nop_lock, flags); + if (!list_empty(&nop->node)) + list_del_init(&nop->node); + spin_unlock_irqrestore(&s->nop_lock, flags); +} +EXPORT_SYMBOL(trusty_dequeue_nop); + + + static int trusty_probe(struct platform_device *pdev) { int ret; + unsigned int cpu; + work_func_t work_func; struct trusty_state *s; struct device_node *node = pdev->dev.of_node; @@ -385,6 +504,11 @@ static int trusty_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_allocate_state; } + + s->dev = &pdev->dev; + spin_lock_init(&s->nop_lock); + INIT_LIST_HEAD(&s->nop_queue); + mutex_init(&s->smc_lock); ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); @@ -396,8 +520,43 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; + s->nop_wq = alloc_workqueue("trusty-nop-wq", WQ_CPU_INTENSIVE, 0); + if (!s->nop_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-nop-wq\n"); + goto err_create_nop_wq; + } + + s->nop_works = alloc_percpu(struct trusty_work); + if (!s->nop_works) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Failed to allocate works\n"); + goto err_alloc_works; + } + + if (s->api_version < TRUSTY_API_VERSION_SMP) + work_func = locked_nop_work_func; + else + work_func = nop_work_func; + + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + tw->ts = s; + INIT_WORK(&tw->work, work_func); + } + return 0; +err_alloc_works: + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + flush_work(&tw->work); + } + free_percpu(s->nop_works); + destroy_workqueue(s->nop_wq); +err_create_nop_wq: err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); @@ -412,11 +571,21 @@ static int trusty_probe(struct platform_device *pdev) static int trusty_remove(struct platform_device *pdev) { + unsigned int cpu; struct trusty_state *s = platform_get_drvdata(pdev); dev_dbg(&(pdev->dev), "%s() is called\n", __func__); device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + flush_work(&tw->work); + } + free_percpu(s->nop_works); + destroy_workqueue(s->nop_wq); + mutex_destroy(&s->smc_lock); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 1160890a3d90..fc98b3e5b2e7 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -120,7 +120,8 @@ */ #define TRUSTY_API_VERSION_RESTART_FIQ (1) #define TRUSTY_API_VERSION_SMP (2) -#define TRUSTY_API_VERSION_CURRENT (2) +#define TRUSTY_API_VERSION_SMP_NOP (3) +#define TRUSTY_API_VERSION_CURRENT (3) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index aba204b9ff3a..eaa833bdea73 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -69,6 +69,23 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, struct page *page, u32 size, pgprot_t pgprot); +struct trusty_nop { + struct list_head node; + u32 args[3]; +}; + +static inline void trusty_nop_init(struct trusty_nop *nop, + u32 arg0, u32 arg1, u32 arg2) { + INIT_LIST_HEAD(&nop->node); + nop->args[0] = arg0; + nop->args[1] = arg1; + nop->args[2] = arg2; +} + +void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); +void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); + + /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ #define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ #define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ From ce00a83f16fe6bf5dac6d1d86ef7cf6e4964f415 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:44:59 +0000 Subject: [PATCH 0338/1276] trusty: switch to use version 3 of TRUSTY_API Version 3 of Trusty API adds support for new command (SMC_NC_VDEV_KICK_VQ) that can be used to notify virtqueue that new item is available. This command is a parameterized NOP, it has to be queued using trusty_enqueue_nop API and as such can be executed concurrently on multiple CPUs. Change-Id: I9ba615e70b59e0689a47fa6eae0a6d9ba6033841 Signed-off-by: Zhong,Fangjian Author: Michael Ryleev --- drivers/trusty/trusty-virtio.c | 18 +++++++++++++++--- include/linux/trusty/smcall.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 6bbf80ce7d7f..b2418d7da5e1 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -60,7 +60,8 @@ struct trusty_vring { atomic_t needs_kick; struct fw_rsc_vdev_vring *vr_descr; struct virtqueue *vq; - struct trusty_vdev *tvdev; + struct trusty_vdev *tvdev; + struct trusty_nop kick_nop; }; struct trusty_vdev { @@ -144,8 +145,14 @@ static bool trusty_virtio_notify(struct virtqueue *vq) struct trusty_vdev *tvdev = tvr->tvdev; struct trusty_ctx *tctx = tvdev->tctx; - atomic_set(&tvr->needs_kick, 1); - queue_work(tctx->kick_wq, &tctx->kick_vqs); + u32 api_ver = trusty_get_api_version(tctx->dev->parent); + + if (api_ver < TRUSTY_API_VERSION_SMP_NOP) { + atomic_set(&tvr->needs_kick, 1); + queue_work(tctx->kick_wq, &tctx->kick_vqs); + } else { + trusty_enqueue_nop(tctx->dev->parent, &tvr->kick_nop); + } return true; } @@ -269,6 +276,9 @@ static void _del_vqs(struct virtio_device *vdev) struct trusty_vring *tvr = &tvdev->vrings[0]; for (i = 0; i < tvdev->vring_num; i++, tvr++) { + /* dequeue kick_nop */ + trusty_dequeue_nop(tvdev->tctx->dev->parent, &tvr->kick_nop); + /* delete vq */ if (tvr->vq) { vring_del_virtqueue(tvr->vq); @@ -431,6 +441,8 @@ static int trusty_virtio_add_device(struct trusty_ctx *tctx, tvr->align = vr_descr->align; tvr->elem_num = vr_descr->num; tvr->notifyid = vr_descr->notifyid; + trusty_nop_init(&tvr->kick_nop, SMC_NC_VDEV_KICK_VQ, + tvdev->notifyid, tvr->notifyid); } /* register device */ diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index fc98b3e5b2e7..037b3fa4429e 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -131,5 +131,6 @@ #define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23) #define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) +#define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25) #endif /* __LINUX_TRUSTY_SMCALL_H */ From 766b32ca30fc9a548847c83e5be8f51fb5ea281c Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 11 Jul 2017 05:03:20 +0000 Subject: [PATCH 0339/1276] trusty: add support for SM Wall object SM Wall is a shared memory buffer established between secure and non-secure side that allows for secure side to publish in efficient manner certain state that non-secure side might acts. This patch adds support for such buffer in a generic way, an API to setup such buffer with secure side and an API to locate it's content based on well object known id's. Change-Id: Ibc4d43bdb7f47e803939461ece2ed848fda5738d Signed-off-by: Zhong,Fangjian Author: Michael Ryleev Author: Zhong,Fangjian --- drivers/trusty/Makefile | 1 + drivers/trusty/trusty-irq.c | 20 ---- drivers/trusty/trusty-wall.c | 199 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty.c | 22 +++- include/linux/trusty/smcall.h | 21 +++- include/linux/trusty/smwall.h | 90 +++++++++++++++ include/linux/trusty/trusty.h | 13 +++ 7 files changed, 343 insertions(+), 23 deletions(-) create mode 100644 drivers/trusty/trusty-wall.c create mode 100644 include/linux/trusty/smwall.h diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 9ca451e50dee..c1afb140ee00 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o +obj-$(CONFIG_TRUSTY) += trusty-wall.o diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 5b4686f4f85f..eda0bff48c40 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,24 +59,6 @@ struct trusty_irq_state { static enum cpuhp_state trusty_irq_online; -#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 -static inline void set_pending_intr_to_lk(uint8_t vector) -{ - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) - ); -} - -#define TRUSTY_VMCALL_IRQ_DONE 0x74727506 -static inline void irq_register_done(void) -{ - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_IRQ_DONE) - ); -} - static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) @@ -580,8 +562,6 @@ static int trusty_irq_probe(struct platform_device *pdev) irq = trusty_irq_init_one(is, irq, false); ret = trusty_irq_cpu_notif_add(is); - irq_register_done(); - if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c new file mode 100644 index 000000000000..3c33d724b3fa --- /dev/null +++ b/drivers/trusty/trusty-wall.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + + +void *trusty_wall_base(struct device *dev) +{ + struct trusty_wall_dev_state *s; + + s = platform_get_drvdata(to_platform_device(dev)); + + if (NULL == s) + return NULL; + + return s->va; +} +EXPORT_SYMBOL(trusty_wall_base); + +void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, + u32 item_id, size_t exp_sz) +{ + uint i; + struct sm_wall_toc *toc; + struct sm_wall_toc_item *item; + struct trusty_wall_dev_state *s; + + s = platform_get_drvdata(to_platform_device(dev)); + + if (!s->va) { + dev_dbg(s->dev, "No smwall buffer is set\n"); + return NULL; + } + + toc = (struct sm_wall_toc *)s->va; + if (toc->version != SM_WALL_TOC_VER) { + dev_err(s->dev, "Unexpected toc version: %d\n", toc->version); + return NULL; + } + + if (cpu >= toc->cpu_num) { + dev_err(s->dev, "Unsupported cpu (%d) requested\n", cpu); + return NULL; + } + + item = (struct sm_wall_toc_item *)((uintptr_t)toc + + toc->per_cpu_toc_offset); + for (i = 0; i < toc->per_cpu_num_items; i++, item++) { + if (item->id != item_id) + continue; + + if (item->size != exp_sz) { + dev_err(s->dev, + "Size mismatch (%zd vs. %zd) for item_id %d\n", + (size_t)item->size, exp_sz, item_id); + return NULL; + } + + return s->va + toc->per_cpu_base_offset + + cpu * toc->per_cpu_region_size + item->offset; + } + return NULL; +} +EXPORT_SYMBOL(trusty_wall_per_cpu_item_ptr); + +static int trusty_wall_setup(struct trusty_wall_dev_state *s) +{ + int ret; + void *va; + size_t sz; + + /* check if wall feature is supported by Trusted OS */ + ret = trusty_fast_call32(s->trusty_dev, SMC_FC_GET_WALL_SIZE, 0, 0, 0); + if (ret == SM_ERR_UNDEFINED_SMC || ret == SM_ERR_NOT_SUPPORTED) { + /* wall is not supported */ + dev_notice(s->dev, "smwall: is not supported by Trusted OS\n"); + return 0; + } else if (ret < 0) { + dev_err(s->dev, "smwall: failed (%d) to query buffer size\n", + ret); + return ret; + } else if (ret == 0) { + dev_notice(s->dev, "smwall: zero-sized buffer requested\n"); + return 0; + } + sz = (size_t)ret; + + /* allocate memory for shared buffer */ + va = alloc_pages_exact(sz, GFP_KERNEL | __GFP_ZERO); + if (!va) { + dev_err(s->dev, "smwall: failed to allocate buffer\n"); + return -ENOMEM; + } + + /* call into Trusted OS to setup wall */ + ret = trusty_call32_mem_buf(s->trusty_dev, SMC_SC_SETUP_WALL, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret < 0) { + dev_err(s->dev, "smwall: TEE returned (%d)\n", ret); + free_pages_exact(va, sz); + return -ENODEV; + } + + dev_info(s->dev, "smwall: initialized %zu bytes\n", sz); + + s->va = va; + s->sz = sz; + + return 0; +} + +static void trusty_wall_destroy(struct trusty_wall_dev_state *s) +{ + int ret; + + ret = trusty_std_call32(s->trusty_dev, SMC_SC_DESTROY_WALL, 0, 0, 0); + if (ret) { + /** + * It should never happen, but if it happens, it is + * unsafe to free buffer so we have to leak memory + */ + dev_err(s->dev, "Failed (%d) to destroy the wall buffer\n", + ret); + } else { + free_pages_exact(s->va, s->sz); + } +} + +static int trusty_wall_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_wall_dev_state *s; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->dev = &pdev->dev; + s->trusty_dev = s->dev->parent; + platform_set_drvdata(pdev, s); + + ret = trusty_wall_setup(s); + if (ret < 0) { + dev_warn(s->dev, "Failed (%d) to setup the wall\n", ret); + kfree(s); + return ret; + } + + return 0; +} + +static int trusty_wall_remove(struct platform_device *pdev) +{ + struct trusty_wall_dev_state *s = platform_get_drvdata(pdev); + + trusty_wall_destroy(s); + + return 0; +} + +static const struct of_device_id trusty_wall_of_match[] = { + { .compatible = "android, trusty-wall-v1", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, trusty_wall_of_match); + +static struct platform_driver trusty_wall_driver = { + .probe = trusty_wall_probe, + .remove = trusty_wall_remove, + .driver = { + .name = "trusty-wall", + .owner = THIS_MODULE, + .of_match_table = trusty_wall_of_match, + }, +}; + +module_platform_driver(trusty_wall_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty smwall driver"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4aa4a89799dc..0b3e75823be1 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -610,12 +611,17 @@ static struct platform_driver trusty_driver = { }, }; -void trusty_dev_release(struct device *dev) +void trusty_dev_release(struct device *dev) { dev_dbg(dev, "%s() is called()\n", __func__); return; } +static struct device_node trusty_wall_node = { + .name = "trusty-wall", + .sibling = NULL, +}; + static struct device_node trusty_irq_node = { .name = "trusty-irq", .sibling = NULL, @@ -679,11 +685,23 @@ static struct platform_device trusty_platform_dev_irq = { }, }; +static struct platform_device trusty_platform_dev_wall = { + .name = "trusty-wall", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_wall_node, + }, +}; + static struct platform_device *trusty_devices[] __initdata = { &trusty_platform_dev, &trusty_platform_dev_log, &trusty_platform_dev_virtio, - &trusty_platform_dev_irq + &trusty_platform_dev_irq, + &trusty_platform_dev_wall }; static int __init trusty_driver_init(void) { diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 037b3fa4429e..ee5dda2560b6 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 Google Inc. All rights reserved + * Copyright (c) 2013-2016 Google Inc. All rights reserved * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files @@ -124,6 +124,25 @@ #define TRUSTY_API_VERSION_CURRENT (3) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) +/* + * SM Wall is a shared memory buffer established between secure and non-secure + * side that allows for secure side to publish certain state that non-secure + * side might acts on. One known example is a state of per CPU timer on + * platforms that require migration to broadcast timer in deep idle states. + * + * SMC_FC_GET_WALL_SIZE - retrieves the size of memory buffer that will be + * required to setup the SM Wall object. + * + * SMC_SC_SETUP_WALL - specifies location, size and attributes of memory buffer + * allocated by non-secure side to setup the SM Wall object. + * + * SMC_SC_DESTROY_WALL - notifies secure side that previously specifies SM Wall + * object should be released usually as part of normal shutdown sequence. + */ +#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_SC_SETUP_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_SC_DESTROY_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 13) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) diff --git a/include/linux/trusty/smwall.h b/include/linux/trusty/smwall.h new file mode 100644 index 000000000000..370d8b32f26a --- /dev/null +++ b/include/linux/trusty/smwall.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SMWALL_H +#define __LINUX_TRUSTY_SMWALL_H + +/** + * DOC: Introduction + * + * SM Wall buffer is formatted by secure side to contain the location of + * objects it exports: + * + * In general it starts with sm_wall_toc header struct followed + * by array of sm_wall_toc_item objects describing location of + * individual objects within SM Wall buffer. + */ + +/* current version of TOC structure */ +#define SM_WALL_TOC_VER 1 + +/** + * struct sm_wall_toc_item - describes individual table of content item + * @id: item id + * @offset: item offset relative to appropriate base. For global items + * it is relative to SM wall buffer base address. For per cpu item, this is an + * offset within each individual per cpu region. + * @size: item size + * @reserved: reserved: must be set to zero + */ +struct sm_wall_toc_item { + u32 id; + u32 offset; + u32 size; + u32 reserved; +}; + +/** + * struct sm_wall_toc - describes sm_wall table of content structure + * @version: current toc structure version + * @cpu_num: number of cpus supported + * @per_cpu_toc_offset: offset of the start of per_cpu item table relative to + * SM wall buffer base address. + * @per_cpu_num_items: number of per cpu toc items located at position + * specified by @per_cpu_toc_offset. + * @per_cpu_base_offset: offset of the start of a sequence of per cpu data + * regions (@cpu_num total) relative to SM wall buffer + * base address. + * @per_cpu_region_size: size of each per cpu data region. + * @global_toc_offset: offset of the start of global item table relative to + * SM wall buffer base address. + * @global_num_items: number of items in global item table + */ +struct sm_wall_toc { + u32 version; + u32 cpu_num; + u32 per_cpu_toc_offset; + u32 per_cpu_num_items; + u32 per_cpu_base_offset; + u32 per_cpu_region_size; + u32 global_toc_offset; + u32 global_num_items; +}; + +struct trusty_wall_dev_state { + struct device *dev; + struct device *trusty_dev; + void *va; + size_t sz; +}; + +#endif /* __LINUX_TRUSTY_SMWALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index eaa833bdea73..029b0986566f 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,6 +85,19 @@ static inline void trusty_nop_init(struct trusty_nop *nop, void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + +void trusty_update_wall_info(struct device *dev, void *va, size_t sz); +void *trusty_wall_base(struct device *dev); +void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, + u32 item_id, size_t exp_sz); /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ #define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ From 217f5f5166d4769fae3dc6501c7d7b1ee65671e1 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 05:09:10 +0000 Subject: [PATCH 0340/1276] trusty: add support for trusty backup timer On some platforms, in certain cpu idle modes, Trusty might lose the state of secure timer that it is using for work scheduling. In such cases, non-secure side would typically migrate such timers to alternative implementations that does not lose their state.Ideally, secure side should have similar mechanizm, but it might not be always feasible due to hardware limitations. This patch introduces a generic workaround for this issue but adding backup non-secure timers that is used to kick cpus out of deep idle modes when appropriate. Change-Id: I7ce18d45db67cc650f7875395451da7a2ed1ab2d Signed-off-by: Zhong,Fangjian Author: Michael Ryleev Author: Zhong,Fangjian --- drivers/trusty/Kconfig | 13 +++ drivers/trusty/Makefile | 1 + drivers/trusty/trusty-timer.c | 166 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty.c | 19 +++- include/linux/trusty/smwall.h | 14 +++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 drivers/trusty/trusty-timer.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 0b6b88e3a718..7b58db5e9a21 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -49,4 +49,17 @@ config TRUSTY_VIRTIO_IPC If you choose to build a module, it'll be called trusty-ipc. Say N if unsure. +config TRUSTY_BACKUP_TIMER + tristate "Trusty backup timer" + depends on TRUSTY + default y + help + This module adds support for Trusty backup timer. Trusty backup + timer might be required on platforms that might loose state of + secure timer in deep idle state. + + If you choose to build a module, it'll be called trusty-timer. + Say N if unsure. + + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index c1afb140ee00..69a78688f1b0 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o obj-$(CONFIG_TRUSTY) += trusty-wall.o +obj-$(CONFIG_TRUSTY_BACKUP_TIMER) += trusty-timer.o diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c new file mode 100644 index 000000000000..0998e027984b --- /dev/null +++ b/drivers/trusty/trusty-timer.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_timer { + struct sec_timer_state *sts; + struct hrtimer tm; +}; + +struct trusty_timer_dev_state { + struct device *dev; + struct device *smwall_dev; + struct device *trusty_dev; + struct notifier_block call_notifier; + struct trusty_timer timer; +}; + +static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) +{ + struct trusty_timer_dev_state *s; + + s = container_of(tm, struct trusty_timer_dev_state, timer.tm); + + set_pending_intr_to_lk(0x31); + trusty_enqueue_nop(s->trusty_dev, NULL); + + return HRTIMER_NORESTART; +} + +static int trusty_timer_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_timer *tt; + struct sec_timer_state *sts; + struct trusty_timer_dev_state *s; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + s = container_of(nb, struct trusty_timer_dev_state, call_notifier); + + /* this notifier is executed in non-preemptible context */ + tt = &s->timer; + sts = tt->sts; + + if (sts->tv_ns > sts->cv_ns) { + hrtimer_cancel(&tt->tm); + } else if (sts->cv_ns > sts->tv_ns) { + /* need to set/reset timer */ + hrtimer_start(&tt->tm, ns_to_ktime(sts->cv_ns - sts->tv_ns), + HRTIMER_MODE_REL_PINNED); + } + + sts->cv_ns = 0ULL; + sts->tv_ns = 0ULL; + + return NOTIFY_OK; +} + +static int trusty_timer_probe(struct platform_device *pdev) +{ + int ret; + unsigned int cpu; + struct trusty_timer_dev_state *s; + struct trusty_timer *tt; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + if (!trusty_wall_base(pdev->dev.parent)) { + dev_notice(&pdev->dev, "smwall: is not setup by parent\n"); + return -ENODEV; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->dev = &pdev->dev; + s->smwall_dev = s->dev->parent; + s->trusty_dev = s->smwall_dev->parent; + platform_set_drvdata(pdev, s); + + tt = &s->timer; + + hrtimer_init(&tt->tm, CLOCK_BOOTTIME, HRTIMER_MODE_REL_PINNED); + tt->tm.function = trusty_timer_cb; + tt->sts = + trusty_wall_per_cpu_item_ptr(s->smwall_dev, 0, + SM_WALL_PER_CPU_SEC_TIMER_ID, + sizeof(*tt->sts)); + WARN_ON(!tt->sts); + + + /* register notifier */ + s->call_notifier.notifier_call = trusty_timer_call_notify; + ret = trusty_call_notifier_register(s->trusty_dev, &s->call_notifier); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register call notifier\n"); + kfree(s); + return ret; + } + + dev_info(s->dev, "initialized\n"); + + return 0; + +} + +static int trusty_timer_remove(struct platform_device *pdev) +{ + unsigned int cpu; + struct trusty_timer_dev_state *s = platform_get_drvdata(pdev); + struct trusty_timer *tt; + + + dev_dbg(&pdev->dev, "%s\n", __func__); + + /* unregister notifier */ + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); + + tt = &s->timer; + hrtimer_cancel(&tt->tm); + + /* free state */ + kfree(s); + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-timer-v1", }, + {}, +}; + +static struct platform_driver trusty_timer_driver = { + .probe = trusty_timer_probe, + .remove = trusty_timer_remove, + .driver = { + .name = "trusty-timer", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_timer_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty timer driver"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 0b3e75823be1..1568849e4501 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -617,6 +617,11 @@ void trusty_dev_release(struct device *dev) return; } +static struct device_node trusty_timer_node = { + .name = "trusty-timer", + .sibling = NULL, +}; + static struct device_node trusty_wall_node = { .name = "trusty-wall", .sibling = NULL, @@ -696,12 +701,24 @@ static struct platform_device trusty_platform_dev_wall = { }, }; +static struct platform_device trusty_platform_dev_timer = { + .name = "trusty-timer", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev_wall.dev, + .of_node = &trusty_timer_node, + }, +}; + static struct platform_device *trusty_devices[] __initdata = { &trusty_platform_dev, &trusty_platform_dev_log, &trusty_platform_dev_virtio, &trusty_platform_dev_irq, - &trusty_platform_dev_wall + &trusty_platform_dev_wall, + &trusty_platform_dev_timer }; static int __init trusty_driver_init(void) { diff --git a/include/linux/trusty/smwall.h b/include/linux/trusty/smwall.h index 370d8b32f26a..66368de8c137 100644 --- a/include/linux/trusty/smwall.h +++ b/include/linux/trusty/smwall.h @@ -87,4 +87,18 @@ struct trusty_wall_dev_state { size_t sz; }; +/* ID's of well known wall objects */ +#define SM_WALL_PER_CPU_SEC_TIMER_ID 1 + +/** + * struct sec_timer_state - structure to hold secute timer state + * @tv_ns: If non-zero this field contains snapshot of timers + * current time (ns). + * @cv_ns: next timer event configured (ns) + */ +struct sec_timer_state { + u64 tv_ns; + u64 cv_ns; +}; + #endif /* __LINUX_TRUSTY_SMWALL_H */ From 21dc8c19cc62ec96f956c004774c8674d3bbc776 Mon Sep 17 00:00:00 2001 From: yingbinx Date: Thu, 10 Aug 2017 06:57:08 +0000 Subject: [PATCH 0341/1276] trusty kernel driver code refine Merge the code refine change form kerenl 4.4 code. Use define CONFIG_X86 to sperate IA code and ARM code. Use NR_CPUS to replace the hardcode value. Inprove the debug log. Change-Id: I11075d594e6b119913cc38d79fe5fa3032ca254e Tracked-On: Signed-off-by: yingbinx Signed-off-by: Sheng, W --- drivers/trusty/trusty-irq.c | 4 +- drivers/trusty/trusty-log.c | 6 ++- drivers/trusty/trusty-mem.c | 71 ++++++++++++++++++++++++++++++++++- drivers/trusty/trusty.c | 4 +- include/linux/trusty/trusty.h | 2 + 5 files changed, 80 insertions(+), 7 deletions(-) mode change 100644 => 100755 drivers/trusty/trusty-log.c mode change 100644 => 100755 drivers/trusty/trusty-mem.c mode change 100644 => 100755 drivers/trusty/trusty.c diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index eda0bff48c40..b576729ec868 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -405,7 +405,7 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) struct trusty_irq *trusty_irq; struct trusty_irq_irqset *irqset; - if (cpu >= 32) + if (cpu >= NR_CPUS) return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); irqset = per_cpu_ptr(is->percpu_irqs, cpu); @@ -430,7 +430,7 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) for_each_possible_cpu(cpu) { struct trusty_irq *trusty_irq; - if (cpu >= 32) + if (cpu >= NR_CPUS) return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); hlist_del(&trusty_irq->node); diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c old mode 100644 new mode 100755 index c5a85ccaf222..b58715cc2ef3 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -156,10 +156,12 @@ static void trusty_vmm_dump_header(struct deadloop_dump *dump) return; header = &(dump->header); + pr_info("-----------VMM PANIC HEADER-----------\n"); pr_info("VMM version = %s\n", header->vmm_version); pr_info("Signature = %s\n", header->signature); pr_info("Error_info = %s\n", header->error_info); pr_info("Cpuid = %d\n", header->cpuid); + pr_info("-----------END OF VMM PANIC HEADER-----------\n"); } static void trusty_vmm_dump_data(struct deadloop_dump *dump) @@ -172,6 +174,7 @@ static void trusty_vmm_dump_data(struct deadloop_dump *dump) dump_data = &(dump->data); + pr_info("-----------VMM PANIC DATA INFO-----------\n"); pstr = (char *)dump_data->data; for (p = pstr; p < ((char *)dump_data->data + dump_data->length); p++) { if (*p == '\r') { @@ -187,12 +190,13 @@ static void trusty_vmm_dump_data(struct deadloop_dump *dump) *p = 0x00; pr_info("%s\n", pstr); } + pr_info("-----------END OF VMM PANIC DATA INFO-----------\n"); } static int trusty_vmm_panic_notify(struct notifier_block *nb, unsigned long action, void *data) { - struct deadloop_dump *dump_info; + struct deadloop_dump *dump_info = NULL; if (g_vmm_debug_buf) { dump_info = (struct deadloop_dump *)g_vmm_debug_buf; diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c old mode 100644 new mode 100755 index 1317ec734315..fc299e348581 --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -26,7 +26,58 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#if defined(CONFIG_ARM64) + uint64_t mair; + uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; + + asm ("mrs %0, mair_el1\n" : "=&r" (mair)); + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM_LPAE) + uint32_t mair; + uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); + + if (attr_index >= 4) { + attr_index -= 4; + asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); + } else { + asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); + } + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM) + /* check memory type */ + switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { + case L_PTE_MT_WRITEALLOC: + /* Normal: write back write allocate */ + return 0xFF; + + case L_PTE_MT_BUFFERABLE: + /* Normal: non-cacheble */ + return 0x44; + + case L_PTE_MT_WRITEBACK: + /* Normal: writeback, read allocate */ + return 0xEE; + + case L_PTE_MT_WRITETHROUGH: + /* Normal: write through */ + return 0xAA; + + case L_PTE_MT_UNCACHED: + /* strongly ordered */ + return 0x00; + + case L_PTE_MT_DEV_SHARED: + case L_PTE_MT_DEV_NONSHARED: + /* device */ + return 0x04; + + default: + return -EINVAL; + } +#elif defined(CONFIG_X86) + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* The porting to CHT kernel (3.14.55) is in the #else clause. ** For BXT kernel (4.1.0), the function get_page_memtype() is static. ** @@ -42,7 +93,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ** with SMP, which only allow UNCACHED. */ return NS_MAIR_NORMAL_UNCACHED; -#else + #else unsigned long type; int ret_mem_attr = 0; @@ -73,6 +124,9 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ret_mem_attr = -EINVAL; } return ret_mem_attr; + #endif +#else + return 0; #endif } @@ -92,10 +146,23 @@ int trusty_encode_page_info(struct ns_mem_page_info *inf, mem_attr = get_mem_attr(page, pgprot); if (mem_attr < 0) return mem_attr; + + /* add other attributes */ +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) + pte |= pgprot_val(pgprot); +#elif defined(CONFIG_ARM) + if (pgprot_val(pgprot) & L_PTE_USER) + pte |= (1 << 6); + if (pgprot_val(pgprot) & L_PTE_RDONLY) + pte |= (1 << 7); + if (pgprot_val(pgprot) & L_PTE_SHARED) + pte |= (3 << 8); /* inner sharable */ +#elif defined(CONFIG_X86) if (pgprot_val(pgprot) & _PAGE_USER) pte |= (1 << 6); if (!(pgprot_val(pgprot) & _PAGE_RW)) pte |= (1 << 7); +#endif inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); return 0; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c old mode 100644 new mode 100755 index 1568849e4501..d4eeb40e2b60 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -209,7 +209,7 @@ static long trusty_std_call32_work(void *args) BUG_ON(!args); - work_args = args; + work_args = (struct trusty_std_call32_args *)args; dev = work_args->dev; s = platform_get_drvdata(to_platform_device(dev)); @@ -332,7 +332,7 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) } s->version_str[i] = '\0'; - dev_info(dev, "trusty version: Built: %s\n", s->version_str); + dev_info(dev, "trusty version: %s\n", s->version_str); ret = device_create_file(dev, &dev_attr_trusty_version); if (ret) diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 029b0986566f..1e9b4559d1b6 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -88,10 +88,12 @@ void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 static inline void set_pending_intr_to_lk(uint8_t vector) { +#ifdef CONFIG_X86 __asm__ __volatile__( "vmcall" ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) ); +#endif } void trusty_update_wall_info(struct device *dev, void *va, size_t sz); From c108b9a2e08450e8a36bf31ab464b84b7a1b1985 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 21 Apr 2017 00:52:03 +0000 Subject: [PATCH 0342/1276] Change Trusty Kconfig to build for X86 Arch only Currently Trusty only works on x86, so the module should never build for other archs except x86. Add this patch to add 'depends' part on drivers/trusty/Kconfig to disable them. Change-Id: Ic18f351696a9c1c31d57621a4af3e8993cc73de5 Signed-off-by: weideng --- drivers/trusty/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 7b58db5e9a21..7d26922ed84c 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -6,6 +6,7 @@ menu "Trusty" config TRUSTY tristate "Trusty" + depends on X86 default n config TRUSTY_FIQ From 4ac23c997abc3a9289d2aefc25b0eb21af9727e6 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Mon, 6 Nov 2017 12:35:31 +0800 Subject: [PATCH 0343/1276] trusty: Add null check pointer before deference Add null check before deference pointer. Change-Id: Icc9d61e17e3ecadfa1bd7fc252cf5e3d7aabb636 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-irq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index b576729ec868..868a31c01f19 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -199,6 +199,9 @@ static int trusty_irq_cpu_up(unsigned int cpu, struct hlist_node *node) unsigned long irq_flags; struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); + if(is == NULL) + return 0; + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); @@ -212,6 +215,9 @@ static int trusty_irq_cpu_down(unsigned int cpu, struct hlist_node *node) unsigned long irq_flags; struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); + if(is == NULL) + return 0; + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); From 001cee4feb457398f5644659029223e1f6ac3670 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Tue, 21 Nov 2017 14:13:27 +0800 Subject: [PATCH 0344/1276] trusty: Check if eVmm is available before init driver eVmm not available in recovery mode, there is a kernel panic if trusty driver initialize without check it. Change-Id: I21f788ab7186cddbc0b0d1a10f7896b5523d257a Tracked-On: --- drivers/trusty/trusty-timer.c | 8 ++++++-- drivers/trusty/trusty-wall.c | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 0998e027984b..e88dc5f4cdf8 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -79,10 +79,15 @@ static int trusty_timer_call_notify(struct notifier_block *nb, static int trusty_timer_probe(struct platform_device *pdev) { int ret; - unsigned int cpu; struct trusty_timer_dev_state *s; struct trusty_timer *tt; + ret = trusty_check_cpuid(NULL); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_wall_base(pdev->dev.parent)) { @@ -127,7 +132,6 @@ static int trusty_timer_probe(struct platform_device *pdev) static int trusty_timer_remove(struct platform_device *pdev) { - unsigned int cpu; struct trusty_timer_dev_state *s = platform_get_drvdata(pdev); struct trusty_timer *tt; diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 3c33d724b3fa..64368480c309 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -147,6 +147,12 @@ static int trusty_wall_probe(struct platform_device *pdev) int ret; struct trusty_wall_dev_state *s; + ret = trusty_check_cpuid(NULL); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); s = kzalloc(sizeof(*s), GFP_KERNEL); From db471240d3fb390f5db473781aaf93a4a39fa595 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 12 Dec 2017 15:16:21 +0800 Subject: [PATCH 0345/1276] trusty: Update Trusty timer solution 1. Add new customized SMC calls 2. Move send pending interrupt implementation to trusty-irq.c 3. Invokes new added standard SMC call to inject timer interrupt to secure side Change-Id: I6c9a94c8ff50f42b58abd2e2b2dd5efd26c126e2 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty-irq.c | 9 +++++++++ drivers/trusty/trusty-timer.c | 38 +++++++++++++++++++++++++++++++++-- include/linux/trusty/trusty.h | 11 ---------- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 868a31c01f19..04df531bf9d0 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,6 +59,15 @@ struct trusty_irq_state { static enum cpuhp_state trusty_irq_online; +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index e88dc5f4cdf8..43e43265c3c6 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -24,6 +24,7 @@ struct trusty_timer { struct sec_timer_state *sts; struct hrtimer tm; + struct work_struct work; }; struct trusty_timer_dev_state { @@ -32,16 +33,33 @@ struct trusty_timer_dev_state { struct device *trusty_dev; struct notifier_block call_notifier; struct trusty_timer timer; + struct workqueue_struct *workqueue; }; +/* Max entity defined as SMC_NUM_ENTITIES(64) */ +#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ + +#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) + +static void timer_work_func(struct work_struct *work) +{ + int ret; + struct trusty_timer_dev_state *s; + + s = container_of(work, struct trusty_timer_dev_state, timer.work); + + ret = trusty_std_call32(s->trusty_dev, SMC_SC_LK_TIMER, 0, 0, 0); + if (ret != 0) + dev_err(s->dev, "%s failed %d\n", __func__, ret); +} + static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) { struct trusty_timer_dev_state *s; s = container_of(tm, struct trusty_timer_dev_state, timer.tm); - set_pending_intr_to_lk(0x31); - trusty_enqueue_nop(s->trusty_dev, NULL); + queue_work(s->workqueue, &s->timer.work); return HRTIMER_NORESTART; } @@ -114,6 +132,12 @@ static int trusty_timer_probe(struct platform_device *pdev) sizeof(*tt->sts)); WARN_ON(!tt->sts); + s->workqueue = alloc_workqueue("trusty-timer-wq", WQ_CPU_INTENSIVE, 0); + if (!s->workqueue) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to allocate work queue\n"); + goto err_allocate_work_queue; + } /* register notifier */ s->call_notifier.notifier_call = trusty_timer_call_notify; @@ -124,10 +148,18 @@ static int trusty_timer_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&s->timer.work, timer_work_func); + dev_info(s->dev, "initialized\n"); return 0; +err_register_call_notifier: + destroy_workqueue(s->workqueue); +err_allocate_work_queue: + kfree(s); + return ret; + } static int trusty_timer_remove(struct platform_device *pdev) @@ -144,6 +176,8 @@ static int trusty_timer_remove(struct platform_device *pdev) tt = &s->timer; hrtimer_cancel(&tt->tm); + flush_work(&tt->work); + destroy_workqueue(s->workqueue); /* free state */ kfree(s); return 0; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 1e9b4559d1b6..3189c7ec967c 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,17 +85,6 @@ static inline void trusty_nop_init(struct trusty_nop *nop, void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); -#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 -static inline void set_pending_intr_to_lk(uint8_t vector) -{ -#ifdef CONFIG_X86 - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) - ); -#endif -} - void trusty_update_wall_info(struct device *dev, void *va, size_t sz); void *trusty_wall_base(struct device *dev); void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, From 185cabb30ae5b923a07b8d8dd31503b9d3a7347e Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 2 Feb 2018 13:12:40 +0800 Subject: [PATCH 0346/1276] trusty: detect vmm when load trusty driver Use hypervisor_cpuid_base() to detect VMM which support Trusty. Currently, there are 2 hypervisors support trusty: CWP and EVMM. Use different hypercall to implement SMC for EVMM and CWP. Change-Id: I45a9c69862c785aba3d2911ca439b5e3d8cf0cf6 Signed-off-by: Qi, Yadong Tracked-On: --- drivers/trusty/trusty-ipc.c | 4 +-- drivers/trusty/trusty-irq.c | 4 +-- drivers/trusty/trusty-log.c | 10 +++---- drivers/trusty/trusty-timer.c | 4 +-- drivers/trusty/trusty-virtio.c | 4 +-- drivers/trusty/trusty-wall.c | 4 +-- drivers/trusty/trusty.c | 52 +++++++++++++++++++++++++++------- include/linux/trusty/trusty.h | 33 +++++++++++---------- 8 files changed, 74 insertions(+), 41 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 93003b45eb32..a2bc3fcba29a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1525,9 +1525,9 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; - err = trusty_check_cpuid(NULL); + err = trusty_detect_vmm(); if (err < 0) { - dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&vdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 04df531bf9d0..af2af6ee37ba 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -539,9 +539,9 @@ static int trusty_irq_probe(struct platform_device *pdev) unsigned long irq_flags; struct trusty_irq_state *is; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index b58715cc2ef3..d2446a1f34c9 100755 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -259,13 +259,13 @@ static int trusty_log_probe(struct platform_device *pdev) { struct trusty_log_state *s; int result; - u32 vmm_signature; + int vmm_id; phys_addr_t pa; struct deadloop_dump *dump; - result = trusty_check_cpuid(&vmm_signature); - if (result < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + vmm_id = trusty_detect_vmm(); + if (vmm_id < 0) { + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } @@ -321,7 +321,7 @@ static int trusty_log_probe(struct platform_device *pdev) goto error_panic_notifier; } - if(vmm_signature == EVMM_SIGNATURE_VMM) { + if(vmm_id == VMM_ID_EVMM) { /* allocate debug buffer for vmm panic dump */ g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); if (!g_vmm_debug_buf) { diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 43e43265c3c6..5d4466d4e157 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -100,9 +100,9 @@ static int trusty_timer_probe(struct platform_device *pdev) struct trusty_timer_dev_state *s; struct trusty_timer *tt; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index b2418d7da5e1..743a4789772f 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -641,9 +641,9 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 64368480c309..2345f56a6405 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -147,9 +147,9 @@ static int trusty_wall_probe(struct platform_device *pdev) int ret; struct trusty_wall_dev_state *s; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index d4eeb40e2b60..98c866487a3e 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,7 +26,8 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define EVMM_SMC_HC_ID 0x74727500 +#define CWP_SMC_HC_ID 0x80000071 struct trusty_state; @@ -53,13 +54,28 @@ struct trusty_smc_interface { ulong args[5]; }; -static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +static ulong (*smc)(ulong, ulong, ulong, ulong); + +#define asm_smc_vmcall(smc_id, rdi, rsi, rdx, rbx) \ +do { \ + __asm__ __volatile__( \ + "vmcall; \n" \ + : "=D"(rdi) \ + : "r"(smc_id), "D"(rdi), "S"(rsi), "d"(rdx), "b"(rbx) \ + ); \ +} while (0) + +static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) { - __asm__ __volatile__( - "vmcall; \n" - : "=D"(r0) - : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - ); + register unsigned long smc_id asm("rax") = EVMM_SMC_HC_ID; + asm_smc_vmcall(smc_id, r0, r1, r2, r3); + return r0; +} + +static inline ulong smc_cwp(ulong r0, ulong r1, ulong r2, ulong r3) +{ + register unsigned long smc_id asm("r8") = CWP_SMC_HC_ID; + asm_smc_vmcall(smc_id, r0, r1, r2, r3); return r0; } @@ -443,6 +459,19 @@ static void nop_work_func(struct work_struct *work) dev_dbg(s->dev, "%s: done\n", __func__); } +static void trusty_init_smc(int vmm_id) +{ + if (vmm_id == VMM_ID_EVMM) { + smc = smc_evmm; + } else if (vmm_id == VMM_ID_CWP) { + smc = smc_cwp; + } else { + pr_err("%s: No smc supports VMM[%d](sig:%s)!", + __func__, vmm_id, vmm_signature[vmm_id]); + BUG(); + } +} + void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) { unsigned long flags; @@ -479,8 +508,6 @@ void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop) } EXPORT_SYMBOL(trusty_dequeue_nop); - - static int trusty_probe(struct platform_device *pdev) { int ret; @@ -489,11 +516,14 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } + dev_dbg(&pdev->dev, "Detected VMM: sig=%s\n", vmm_signature[ret]); + + trusty_init_smc(ret); if (!node) { dev_err(&pdev->dev, "of_node required\n"); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 3189c7ec967c..48e1ea716889 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -18,6 +18,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_TRUSTY) @@ -90,25 +91,27 @@ void *trusty_wall_base(struct device *dev); void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, u32 item_id, size_t exp_sz); -/* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ -#define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ -#define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ - -static inline int trusty_check_cpuid(u32 *vmm_signature) -{ - u32 eax, ebx, ecx, edx; +enum { + VMM_ID_EVMM = 0, + VMM_ID_CWP, + VMM_SUPPORTED_NUM +}; - cpuid(3, &eax, &ebx, &ecx, &edx); - if ((ecx != EVMM_SIGNATURE_VMM) || - (edx != EVMM_SIGNATURE_CORP)) { - return -EINVAL; - } +static const char *vmm_signature[] = { + [VMM_ID_EVMM] = "EVMMEVMMEVMM", + [VMM_ID_CWP] = "CWPCWPCWP\0\0" +}; - if(vmm_signature) { - *vmm_signature = ecx; +/* Detect VMM and return vmm_id */ +static inline int trusty_detect_vmm(void) +{ + int i; + for (i = 0; i < VMM_SUPPORTED_NUM; i++) { + if (hypervisor_cpuid_base(vmm_signature[i], 0)) + return i; } - return 0; + return -EINVAL; } /* High 32 bits of unsigned 64-bit integer*/ From a3a97252e71f7ca090ace8923f0149636e1d8d41 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Wed, 14 Feb 2018 15:14:20 +0800 Subject: [PATCH 0347/1276] Remove unused label to depress compile warning As title, remove a unused label to depress compile wrning Change-Id: I8a6daa1d85b9a95ec9a475ef39990e74c84e89e9 --- drivers/trusty/trusty-timer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 5d4466d4e157..18e315c25067 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -154,7 +154,6 @@ static int trusty_timer_probe(struct platform_device *pdev) return 0; -err_register_call_notifier: destroy_workqueue(s->workqueue); err_allocate_work_queue: kfree(s); From 36417d03e69ccea8c0e7139a24ceace686a23664 Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 23 Feb 2018 14:12:07 +0800 Subject: [PATCH 0348/1276] trusty: Update dependency of trusty module Trusty is supported only for x86_64 arch. Modify Kconfig to make it depends on x86_64. Change-Id: Ia52a8ba05f2de3d423e070a53e7368901b20ada7 Signed-off-by: Qi, Yadong --- drivers/trusty/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 7d26922ed84c..a230dad0434d 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -6,7 +6,7 @@ menu "Trusty" config TRUSTY tristate "Trusty" - depends on X86 + depends on X86_64 default n config TRUSTY_FIQ From 899b9a1faef27d688b01d4366f999c208032b5eb Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Mon, 26 Feb 2018 09:48:06 +0800 Subject: [PATCH 0349/1276] trusty: Rename CWP with ACRN The CWP hypervisor has been renamed to ACRN. Change-Id: I23bcff44954110fbc20148fd3266ac48864a3a1f Signed-off-by: Qi, Yadong --- drivers/trusty/trusty.c | 10 +++++----- include/linux/trusty/trusty.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 98c866487a3e..e253ee498ab5 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,7 +27,7 @@ #include #define EVMM_SMC_HC_ID 0x74727500 -#define CWP_SMC_HC_ID 0x80000071 +#define ACRN_SMC_HC_ID 0x80000071 struct trusty_state; @@ -72,9 +72,9 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) return r0; } -static inline ulong smc_cwp(ulong r0, ulong r1, ulong r2, ulong r3) +static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { - register unsigned long smc_id asm("r8") = CWP_SMC_HC_ID; + register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; asm_smc_vmcall(smc_id, r0, r1, r2, r3); return r0; } @@ -463,8 +463,8 @@ static void trusty_init_smc(int vmm_id) { if (vmm_id == VMM_ID_EVMM) { smc = smc_evmm; - } else if (vmm_id == VMM_ID_CWP) { - smc = smc_cwp; + } else if (vmm_id == VMM_ID_ACRN) { + smc = smc_acrn; } else { pr_err("%s: No smc supports VMM[%d](sig:%s)!", __func__, vmm_id, vmm_signature[vmm_id]); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 48e1ea716889..546e6db03498 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -93,13 +93,13 @@ void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, enum { VMM_ID_EVMM = 0, - VMM_ID_CWP, + VMM_ID_ACRN, VMM_SUPPORTED_NUM }; static const char *vmm_signature[] = { [VMM_ID_EVMM] = "EVMMEVMMEVMM", - [VMM_ID_CWP] = "CWPCWPCWP\0\0" + [VMM_ID_ACRN] = "ACRNACRNACRN" }; /* Detect VMM and return vmm_id */ From c22dec3fb6a46a32d1e7efe6ea553ca0b94b0fb3 Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 16 Mar 2018 15:42:07 +0800 Subject: [PATCH 0350/1276] trusty: add RAX into clobber list of inline asm for ACRN The RAX regiser will be modified when do "vmcall" for ACRN hypervisor. So the RAX register should to be listed in asm clobber list to inform compiler aware of such changes. Change-Id: I298c056c109e974d2a391ba7b3e8dfbb7f25ed4f Signed-off-by: Qi, Yadong --- drivers/trusty/trusty.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index e253ee498ab5..4d33f269851d 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -56,26 +56,28 @@ struct trusty_smc_interface { static ulong (*smc)(ulong, ulong, ulong, ulong); -#define asm_smc_vmcall(smc_id, rdi, rsi, rdx, rbx) \ -do { \ - __asm__ __volatile__( \ - "vmcall; \n" \ - : "=D"(rdi) \ - : "r"(smc_id), "D"(rdi), "S"(rsi), "d"(rdx), "b"(rbx) \ - ); \ -} while (0) - static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("rax") = EVMM_SMC_HC_ID; - asm_smc_vmcall(smc_id, r0, r1, r2, r3); + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0) + : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; - asm_smc_vmcall(smc_id, r0, r1, r2, r3); + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0) + : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + : "rax" + ); + return r0; } From 800e814afd9e2897e8fbfd23cdd5ba90dc8b0984 Mon Sep 17 00:00:00 2001 From: "Ding,XinX" Date: Wed, 21 Mar 2018 11:09:27 +0800 Subject: [PATCH 0351/1276] trusty: Update macro SMC_FC_GET_WALL_SIZE from 12 to 20 Keep this macro synced with that of Trusty OS because we rebased trusty OS with Google's and this smc id was increased. Change-Id: I09d68971de6d8f3d099525c21f99fe7ed2fdcb9d Signed-off-by: Ding,XinX --- include/linux/trusty/smcall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index ee5dda2560b6..3ab2f688cb33 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -139,7 +139,7 @@ * SMC_SC_DESTROY_WALL - notifies secure side that previously specifies SM Wall * object should be released usually as part of normal shutdown sequence. */ -#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 20) #define SMC_SC_SETUP_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) #define SMC_SC_DESTROY_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 13) From 637a11bbc9b8d167a9c956f8b8e2bea0471eefd8 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Wed, 7 Mar 2018 15:45:48 +0800 Subject: [PATCH 0352/1276] unify trusty driver Keep One Trusty driver accross different version kernel as we have One Trusty OS Change-Id: Ie81201bb543ffdf6050bfab7560bd275f3a92eb0 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-ipc.c | 7 +++++++ drivers/trusty/trusty-mem.c | 6 +++--- drivers/trusty/trusty-virtio.c | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) mode change 100755 => 100644 drivers/trusty/trusty-mem.c diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index a2bc3fcba29a..7df0972ddd05 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -21,7 +21,10 @@ #include #include #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include +#endif #include #include #include @@ -1558,7 +1561,11 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL, NULL); +#else + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); +#endif if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c old mode 100755 new mode 100644 index fc299e348581..470df8823d3a --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -77,7 +77,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) return -EINVAL; } #elif defined(CONFIG_X86) - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* The porting to CHT kernel (3.14.55) is in the #else clause. ** For BXT kernel (4.1.0), the function get_page_memtype() is static. ** @@ -93,7 +93,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ** with SMP, which only allow UNCACHED. */ return NS_MAIR_NORMAL_UNCACHED; - #else +#else unsigned long type; int ret_mem_attr = 0; @@ -124,7 +124,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ret_mem_attr = -EINVAL; } return ret_mem_attr; - #endif +#endif #else return 0; #endif diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 743a4789772f..66b4ee7caf0d 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -337,9 +338,15 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, vdev, true, true, tvr->vaddr, trusty_virtio_notify, callback, name); +#else + tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, + vdev, true, tvr->vaddr, + trusty_virtio_notify, callback, name); +#endif if (!tvr->vq) { dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", name); @@ -356,12 +363,19 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, return ERR_PTR(-ENOMEM); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], const bool *ctx, struct irq_affinity *desc) +#else +static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char * const names[]) +#endif { uint i; int ret; From 9527cf8870d91c2138ea1e4a3456133c0bcc6189 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Thu, 12 Apr 2018 09:06:04 +0800 Subject: [PATCH 0353/1276] Revert "trusty-ipc: change DEFAULT_MSG_BUF_SIZE to 68K" This reverts commit f3e776a486937859e6cd67ab558544544fae7004. Signed-off-by: Yan, Shaopu --- drivers/trusty/trusty-ipc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 7df0972ddd05..f0b6b1bb444a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -49,8 +49,7 @@ #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 -#define DEFAULT_MSG_BUF_SIZE (68*1024) - +#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE #define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE #define TIPC_CTRL_ADDR 53 From 03b151704fa844b017217787333b444e7b7cd0fb Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 17 Jul 2018 21:16:53 +0800 Subject: [PATCH 0354/1276] refine work queue in trusty driver Change-Id: I049497485f87d2c90e23be11893696513602800b Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-timer.c | 7 +------ drivers/trusty/trusty-virtio.c | 4 ++-- drivers/trusty/trusty.c | 9 ++++++--- include/linux/trusty/smcall.h | 3 +++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 18e315c25067..6783a30b4a11 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -36,11 +36,6 @@ struct trusty_timer_dev_state { struct workqueue_struct *workqueue; }; -/* Max entity defined as SMC_NUM_ENTITIES(64) */ -#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ - -#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) - static void timer_work_func(struct work_struct *work) { int ret; @@ -59,7 +54,7 @@ static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) s = container_of(tm, struct trusty_timer_dev_state, timer.tm); - queue_work(s->workqueue, &s->timer.work); + queue_work_on(0, s->workqueue, &s->timer.work); return HRTIMER_NORESTART; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 66b4ee7caf0d..df066dda80d3 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -150,7 +150,7 @@ static bool trusty_virtio_notify(struct virtqueue *vq) if (api_ver < TRUSTY_API_VERSION_SMP_NOP) { atomic_set(&tvr->needs_kick, 1); - queue_work(tctx->kick_wq, &tctx->kick_vqs); + queue_work_on(0, tctx->kick_wq, &tctx->kick_vqs); } else { trusty_enqueue_nop(tctx->dev->parent, &tvr->kick_nop); } @@ -685,7 +685,7 @@ static int trusty_virtio_probe(struct platform_device *pdev) } tctx->kick_wq = alloc_workqueue("trusty-kick-wq", - WQ_UNBOUND | WQ_CPU_INTENSIVE, 0); + WQ_CPU_INTENSIVE, 0); if (!tctx->kick_wq) { ret = -ENODEV; dev_err(&pdev->dev, "Failed create trusty-kick-wq\n"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4d33f269851d..8f80f9b84772 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -270,7 +270,6 @@ static long trusty_std_call32_work(void *args) s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { - const int cpu = 0; struct trusty_std_call32_args args = { .dev = dev, .smcnr = smcnr, @@ -280,7 +279,11 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) }; /* bind cpu 0 for now since trusty OS is running on physical cpu #0*/ - return work_on_cpu(cpu, trusty_std_call32_work, (void *) &args); + if((smcnr == SMC_SC_VDEV_KICK_VQ) || (smcnr == SMC_SC_LK_TIMER) + || (smcnr == SMC_SC_LOCKED_NOP) || (smcnr == SMC_SC_NOP)) + return trusty_std_call32_work((void *) &args); + else + return work_on_cpu(0, trusty_std_call32_work, (void *) &args); } EXPORT_SYMBOL(trusty_std_call32); @@ -490,7 +493,7 @@ void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) list_add_tail(&nop->node, &s->nop_queue); spin_unlock_irqrestore(&s->nop_lock, flags); } - queue_work(s->nop_wq, &tw->work); + queue_work_on(0, s->nop_wq, &tw->work); preempt_enable(); } EXPORT_SYMBOL(trusty_enqueue_nop); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 3ab2f688cb33..55d25dddc4a8 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -152,4 +152,7 @@ #define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) #define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25) +/* Max entity defined as SMC_NUM_ENTITIES(64) */ +#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ +#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) #endif /* __LINUX_TRUSTY_SMCALL_H */ From 3f48e5f34fa75fb9654c0c6e3c3123a506bd0372 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 17 Jul 2018 15:01:38 +0800 Subject: [PATCH 0355/1276] register suspend callback Save secure world context by the hyercall Change-Id: I21ad1569c12f9b8dda66ab47beab273d4b3791cb Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty.c | 42 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8f80f9b84772..7bff133a4610 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,7 +27,8 @@ #include #define EVMM_SMC_HC_ID 0x74727500 -#define ACRN_SMC_HC_ID 0x80000071 +#define ACRN_HC_SWITCH_WORLD 0x80000071 +#define ACRN_HC_SAVE_SWORLD_CONTEXT 0x80000072 struct trusty_state; @@ -70,7 +71,7 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { - register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; + register unsigned long smc_id asm("r8") = ACRN_HC_SWITCH_WORLD; __asm__ __volatile__( "vmcall; \n" : "=D"(r0) @@ -81,6 +82,20 @@ static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) return r0; } +static void acrn_save_sworld_context(void *arg) +{ + long *save_ret = arg; + register signed long result asm("rax"); + register unsigned long hc_id asm("r8") = ACRN_HC_SAVE_SWORLD_CONTEXT; + __asm__ __volatile__( + "vmcall; \n" + : "=r"(result) + : "r"(hc_id) + ); + + *save_ret = result; +} + static void trusty_fast_call32_remote(void *args) { struct trusty_smc_interface *p_args = args; @@ -631,6 +646,24 @@ static int trusty_remove(struct platform_device *pdev) return 0; } +static int trusty_suspend(struct platform_device *pdev, pm_message_t state) +{ + dev_info(&pdev->dev, "%s() is called\n", __func__); + long ret = 0, save_ret = 0; + int cpu = 0; + + ret = smp_call_function_single(cpu, acrn_save_sworld_context, (void *)&save_ret, 1); + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + if(save_ret < 0) { + dev_err(&pdev->dev, "%s(): failed to save world context!\n", __func__); + return -EPERM; + } + + return 0; +} + static const struct of_device_id trusty_of_match[] = { { .compatible = "android,trusty-smc-v1", }, {}, @@ -764,6 +797,11 @@ static int __init trusty_driver_init(void) printk(KERN_ERR "%s(): platform_add_devices() failed, ret %d\n", __func__, ret); return ret; } + + if(trusty_detect_vmm() == VMM_ID_ACRN) { + trusty_driver.suspend = trusty_suspend; + } + return platform_driver_register(&trusty_driver); } From 1ddd60f8e64761d4fd604af1ab975de3a649c6f5 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Fri, 10 Aug 2018 15:00:04 +0800 Subject: [PATCH 0356/1276] Fix compile warning from ISO90 and output format Fix the warning of mixed declarations and code which are forbidded in ISO90, and update 'long' output format. Change-Id: I96e6e4152151f1b26d5d2243974cc85bd7fc5bdd --- drivers/trusty/trusty.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7bff133a4610..f37a1a58dce8 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -648,13 +648,14 @@ static int trusty_remove(struct platform_device *pdev) static int trusty_suspend(struct platform_device *pdev, pm_message_t state) { - dev_info(&pdev->dev, "%s() is called\n", __func__); long ret = 0, save_ret = 0; int cpu = 0; + dev_info(&pdev->dev, "%s() is called\n", __func__); + ret = smp_call_function_single(cpu, acrn_save_sworld_context, (void *)&save_ret, 1); if (ret) { - pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + pr_err("%s: smp_call_function_single failed: %ld\n", __func__, ret); } if(save_ret < 0) { dev_err(&pdev->dev, "%s(): failed to save world context!\n", __func__); From 939df1ebc13938a1b1b7a2310bedcfb53b9e4377 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 16 Aug 2018 18:04:44 +0800 Subject: [PATCH 0357/1276] check return value of hypercall exit from probe if acrn does not enable trusty Change-Id: I99271cd96c6df46e141b4e57a2af378119a1c25c Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index f37a1a58dce8..881924f88e4f 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -72,13 +72,18 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("r8") = ACRN_HC_SWITCH_WORLD; + register signed long ret asm("rax"); __asm__ __volatile__( "vmcall; \n" - : "=D"(r0) + : "=D"(r0), "=r"(ret) : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - : "rax" ); + if(ret < 0) { + pr_err("trusty: %s: hypercall failed: %ld\n", __func__, ret); + r0 = (ulong)SM_ERR_NOT_SUPPORTED; + } + return r0; } From 656cf0ec26780bbc429986060785893db6f23003 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Mon, 27 Aug 2018 15:15:51 +0800 Subject: [PATCH 0358/1276] Fix compilation errors when rebase to v4.19-rc1. Include header file of_platform.h when update kernel to v4.19-rc1. Change-Id: I732913061fed8ab14edddb40544df370e19edc54 --- drivers/trusty/trusty-log.c | 4 ++++ drivers/trusty/trusty-timer.c | 4 ++++ drivers/trusty/trusty-wall.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index d2446a1f34c9..48883439dce2 100755 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -12,6 +12,10 @@ * */ #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 6783a30b4a11..ca6ea5799eeb 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -15,6 +15,10 @@ #include #include #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 2345f56a6405..812ac2a3ea98 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -13,6 +13,10 @@ * */ #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include From 898cce6005d22ed83ace8e158a8b8604426a9871 Mon Sep 17 00:00:00 2001 From: "Pottratz, Dwane" Date: Fri, 27 Jan 2017 10:18:38 -0800 Subject: [PATCH 0359/1276] greybus: Remove android make file Remove the andorid make file. With multiple kernel repos in the android build system the make file will cause a redefine and fail Signed-off-by: Pottratz, Dwane --- drivers/staging/greybus/tools/Android.mk | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 drivers/staging/greybus/tools/Android.mk diff --git a/drivers/staging/greybus/tools/Android.mk b/drivers/staging/greybus/tools/Android.mk deleted file mode 100644 index fdadbf611757..000000000000 --- a/drivers/staging/greybus/tools/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= loopback_test.c -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := gb_loopback_test - -include $(BUILD_EXECUTABLE) - From e0bd44ca27e84f9650bf7e2b2ef748c24455138e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:07 +0200 Subject: [PATCH 0360/1276] x86/mm/init32: Mark text and rodata RO in one go The sequence of marking text and rodata read-only in 32bit init is: set_ro(text); kernel_set_to_readonly = 1; set_ro(rodata); When kernel_set_to_readonly is 1 it enables the protection mechanism in CPA for the read only regions. With the upcoming checks for existing mappings this consequently triggers the warning about an existing mapping being incorrect vs. static protections because rodata has not been converted yet. There is no technical reason to split the two, so just combine the RO protection to convert text and rodata in one go. Convert the printks to pr_info while at it. Reported-by: kernel test robot Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.731701535@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/init_32.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 979e0a02cbe1..142c7d9f89cc 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -923,34 +923,19 @@ static void mark_nxdata_nx(void) void mark_rodata_ro(void) { unsigned long start = PFN_ALIGN(_text); - unsigned long size = PFN_ALIGN(_etext) - start; + unsigned long size = (unsigned long)__end_rodata - start; set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Write protecting the kernel text: %luk\n", + pr_info("Write protecting kernel text and read-only data: %luk\n", size >> 10); kernel_set_to_readonly = 1; #ifdef CONFIG_CPA_DEBUG - printk(KERN_INFO "Testing CPA: Reverting %lx-%lx\n", - start, start+size); - set_pages_rw(virt_to_page(start), size>>PAGE_SHIFT); - - printk(KERN_INFO "Testing CPA: write protecting again\n"); - set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT); -#endif - - start += size; - size = (unsigned long)__end_rodata - start; - set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", - size >> 10); - -#ifdef CONFIG_CPA_DEBUG - printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, start + size); + pr_info("Testing CPA: Reverting %lx-%lx\n", start, start + size); set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Testing CPA: write protecting again\n"); + pr_info("Testing CPA: write protecting again\n"); set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); #endif mark_nxdata_nx(); From 0f4921d88918be4253902d3ae664c6ddcf8faf51 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:08 +0200 Subject: [PATCH 0361/1276] x86/mm/cpa: Split, rename and clean up try_preserve_large_page() Avoid the extra variable and gotos by splitting the function into the actual algorithm and a callable function which contains the lock protection. Rename it to should_split_large_page() while at it so the return values make actually sense. Clean up the code flow, comments and general whitespace damage while at it. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.830507216@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 121 ++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 51a5a69ecac9..36f5d711ddbe 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -421,18 +421,18 @@ pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address, */ pte_t *lookup_address(unsigned long address, unsigned int *level) { - return lookup_address_in_pgd(pgd_offset_k(address), address, level); + return lookup_address_in_pgd(pgd_offset_k(address), address, level); } EXPORT_SYMBOL_GPL(lookup_address); static pte_t *_lookup_address_cpa(struct cpa_data *cpa, unsigned long address, unsigned int *level) { - if (cpa->pgd) + if (cpa->pgd) return lookup_address_in_pgd(cpa->pgd + pgd_index(address), address, level); - return lookup_address(address, level); + return lookup_address(address, level); } /* @@ -549,27 +549,22 @@ static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) return prot; } -static int -try_preserve_large_page(pte_t *kpte, unsigned long address, - struct cpa_data *cpa) +static int __should_split_large_page(pte_t *kpte, unsigned long address, + struct cpa_data *cpa) { - unsigned long nextpage_addr, numpages, pmask, psize, addr, pfn, old_pfn; - pte_t new_pte, old_pte, *tmp; + unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; pgprot_t old_prot, new_prot, req_prot; - int i, do_split = 1; + pte_t new_pte, old_pte, *tmp; enum pg_level level; + int i; - if (cpa->force_split) - return 1; - - spin_lock(&pgd_lock); /* * Check for races, another CPU might have split this page * up already: */ tmp = _lookup_address_cpa(cpa, address, &level); if (tmp != kpte) - goto out_unlock; + return 1; switch (level) { case PG_LEVEL_2M: @@ -581,8 +576,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, old_pfn = pud_pfn(*(pud_t *)kpte); break; default: - do_split = -EINVAL; - goto out_unlock; + return -EINVAL; } psize = page_level_size(level); @@ -592,8 +586,8 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, * Calculate the number of pages, which fit into this large * page starting at address: */ - nextpage_addr = (address + psize) & pmask; - numpages = (nextpage_addr - address) >> PAGE_SHIFT; + lpaddr = (address + psize) & pmask; + numpages = (lpaddr - address) >> PAGE_SHIFT; if (numpages < cpa->numpages) cpa->numpages = numpages; @@ -620,57 +614,62 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, pgprot_val(req_prot) |= _PAGE_PSE; /* - * old_pfn points to the large page base pfn. So we need - * to add the offset of the virtual address: + * old_pfn points to the large page base pfn. So we need to add the + * offset of the virtual address: */ pfn = old_pfn + ((address & (psize - 1)) >> PAGE_SHIFT); cpa->pfn = pfn; - new_prot = static_protections(req_prot, address, pfn); + /* + * Calculate the large page base address and the number of 4K pages + * in the large page + */ + lpaddr = address & pmask; + numpages = psize >> PAGE_SHIFT; /* - * We need to check the full range, whether - * static_protection() requires a different pgprot for one of - * the pages in the range we try to preserve: + * Make sure that the requested pgprot does not violate the static + * protections. Check the full large page whether one of the pages + * in it results in a different pgprot than the first one of the + * requested range. If yes, then the page needs to be split. */ - addr = address & pmask; + new_prot = static_protections(req_prot, address, pfn); pfn = old_pfn; - for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) { + for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { pgprot_t chk_prot = static_protections(req_prot, addr, pfn); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) - goto out_unlock; + return 1; } - /* - * If there are no changes, return. maxpages has been updated - * above: - */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) { - do_split = 0; - goto out_unlock; - } + /* If there are no changes, return. */ + if (pgprot_val(new_prot) == pgprot_val(old_prot)) + return 0; /* - * We need to change the attributes. Check, whether we can - * change the large page in one go. We request a split, when - * the address is not aligned and the number of pages is - * smaller than the number of pages in the large page. Note - * that we limited the number of possible pages already to - * the number of pages in the large page. + * Verify that the address is aligned and the number of pages + * covers the full page. */ - if (address == (address & pmask) && cpa->numpages == (psize >> PAGE_SHIFT)) { - /* - * The address is aligned and the number of pages - * covers the full page. - */ - new_pte = pfn_pte(old_pfn, new_prot); - __set_pmd_pte(kpte, address, new_pte); - cpa->flags |= CPA_FLUSHTLB; - do_split = 0; - } + if (address != lpaddr || cpa->numpages != numpages) + return 1; + + /* All checks passed. Update the large page mapping. */ + new_pte = pfn_pte(old_pfn, new_prot); + __set_pmd_pte(kpte, address, new_pte); + cpa->flags |= CPA_FLUSHTLB; + return 0; +} + +static int should_split_large_page(pte_t *kpte, unsigned long address, + struct cpa_data *cpa) +{ + int do_split; + + if (cpa->force_split) + return 1; -out_unlock: + spin_lock(&pgd_lock); + do_split = __should_split_large_page(kpte, address, cpa); spin_unlock(&pgd_lock); return do_split; @@ -1273,7 +1272,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) * Check, whether we can keep the large page intact * and just change the pte: */ - do_split = try_preserve_large_page(kpte, address, cpa); + do_split = should_split_large_page(kpte, address, cpa); /* * When the range fits into the existing large page, * return. cp->numpages and cpa->tlbflush have been updated in @@ -1288,23 +1287,23 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) err = split_large_page(cpa, kpte, address); if (!err) { /* - * Do a global flush tlb after splitting the large page - * and before we do the actual change page attribute in the PTE. - * - * With out this, we violate the TLB application note, that says - * "The TLBs may contain both ordinary and large-page + * Do a global flush tlb after splitting the large page + * and before we do the actual change page attribute in the PTE. + * + * With out this, we violate the TLB application note, that says + * "The TLBs may contain both ordinary and large-page * translations for a 4-KByte range of linear addresses. This * may occur if software modifies the paging structures so that * the page size used for the address range changes. If the two * translations differ with respect to page frame or attributes * (e.g., permissions), processor behavior is undefined and may * be implementation-specific." - * - * We do this global tlb flush inside the cpa_lock, so that we + * + * We do this global tlb flush inside the cpa_lock, so that we * don't allow any other cpu, with stale tlb entries change the * page attribute in parallel, that also falls into the * just split large page entry. - */ + */ flush_tlb_all(); goto repeat; } From fbfdbce31f6a045c88e67a4801a56792bb816538 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:09 +0200 Subject: [PATCH 0362/1276] x86/mm/cpa: Rework static_protections() static_protections() is pretty unreadable. Split it up into separate checks for each protection area. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.913005317@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 162 +++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 64 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 36f5d711ddbe..9731aeeebe71 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -286,84 +286,118 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache, } } +#ifdef CONFIG_PCI_BIOS /* - * Certain areas of memory on x86 require very specific protection flags, - * for example the BIOS area or kernel text. Callers don't always get this - * right (again, ioremap() on BIOS memory is not uncommon) so this function - * checks and fixes these known static required protection bits. + * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS + * based config access (CONFIG_PCI_GOBIOS) support. */ -static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, - unsigned long pfn) -{ - pgprot_t forbidden = __pgprot(0); +#define BIOS_PFN PFN_DOWN(BIOS_BEGIN) +#define BIOS_PFN_END PFN_DOWN(BIOS_END) - /* - * The BIOS area between 640k and 1Mb needs to be executable for - * PCI BIOS based config access (CONFIG_PCI_GOBIOS) support. - */ -#ifdef CONFIG_PCI_BIOS - if (pcibios_enabled && within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT)) - pgprot_val(forbidden) |= _PAGE_NX; +static pgprotval_t protect_pci_bios(unsigned long pfn) +{ + if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END)) + return _PAGE_NX; + return 0; +} +#else +static pgprotval_t protect_pci_bios(unsigned long pfn) +{ + return 0; +} #endif - /* - * The kernel text needs to be executable for obvious reasons - * Does not cover __inittext since that is gone later on. On - * 64bit we do not enforce !NX on the low mapping - */ - if (within(address, (unsigned long)_text, (unsigned long)_etext)) - pgprot_val(forbidden) |= _PAGE_NX; +/* + * The .rodata section needs to be read-only. Using the pfn catches all + * aliases. This also includes __ro_after_init, so do not enforce until + * kernel_set_to_readonly is true. + */ +static pgprotval_t protect_rodata(unsigned long pfn) +{ + unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT; + unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT; - /* - * The .rodata section needs to be read-only. Using the pfn - * catches all aliases. This also includes __ro_after_init, - * so do not enforce until kernel_set_to_readonly is true. - */ - if (kernel_set_to_readonly && - within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT, - __pa_symbol(__end_rodata) >> PAGE_SHIFT)) - pgprot_val(forbidden) |= _PAGE_RW; + if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn)) + return _PAGE_RW; + return 0; +} + +/* + * Protect kernel text against becoming non executable by forbidding + * _PAGE_NX. This protects only the high kernel mapping (_text -> _etext) + * out of which the kernel actually executes. Do not protect the low + * mapping. + * + * This does not cover __inittext since that is gone after boot. + */ +static pgprotval_t protect_kernel_text(unsigned long address) +{ + if (within(address, (unsigned long)_text, (unsigned long)_etext)) + return _PAGE_NX; + return 0; +} #if defined(CONFIG_X86_64) +/* + * Once the kernel maps the text as RO (kernel_set_to_readonly is set), + * kernel text mappings for the large page aligned text, rodata sections + * will be always read-only. For the kernel identity mappings covering the + * holes caused by this alignment can be anything that user asks. + * + * This will preserve the large page mappings for kernel text/data at no + * extra cost. + */ +static pgprotval_t protect_kernel_text_ro(unsigned long address) +{ + unsigned long end = (unsigned long)__end_rodata_hpage_align; + unsigned long start = (unsigned long)_text; + unsigned int level; + + if (!kernel_set_to_readonly || !within(address, start, end)) + return 0; /* - * Once the kernel maps the text as RO (kernel_set_to_readonly is set), - * kernel text mappings for the large page aligned text, rodata sections - * will be always read-only. For the kernel identity mappings covering - * the holes caused by this alignment can be anything that user asks. + * Don't enforce the !RW mapping for the kernel text mapping, if + * the current mapping is already using small page mapping. No + * need to work hard to preserve large page mappings in this case. * - * This will preserve the large page mappings for kernel text/data - * at no extra cost. + * This also fixes the Linux Xen paravirt guest boot failure caused + * by unexpected read-only mappings for kernel identity + * mappings. In this paravirt guest case, the kernel text mapping + * and the kernel identity mapping share the same page-table pages, + * so the protections for kernel text and identity mappings have to + * be the same. */ - if (kernel_set_to_readonly && - within(address, (unsigned long)_text, - (unsigned long)__end_rodata_hpage_align)) { - unsigned int level; - - /* - * Don't enforce the !RW mapping for the kernel text mapping, - * if the current mapping is already using small page mapping. - * No need to work hard to preserve large page mappings in this - * case. - * - * This also fixes the Linux Xen paravirt guest boot failure - * (because of unexpected read-only mappings for kernel identity - * mappings). In this paravirt guest case, the kernel text - * mapping and the kernel identity mapping share the same - * page-table pages. Thus we can't really use different - * protections for the kernel text and identity mappings. Also, - * these shared mappings are made of small page mappings. - * Thus this don't enforce !RW mapping for small page kernel - * text mapping logic will help Linux Xen parvirt guest boot - * as well. - */ - if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) - pgprot_val(forbidden) |= _PAGE_RW; - } + if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) + return _PAGE_RW; + return 0; +} +#else +static pgprotval_t protect_kernel_text_ro(unsigned long address) +{ + return 0; +} #endif - prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); +/* + * Certain areas of memory on x86 require very specific protection flags, + * for example the BIOS area or kernel text. Callers don't always get this + * right (again, ioremap() on BIOS memory is not uncommon) so this function + * checks and fixes these known static required protection bits. + */ +static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, + unsigned long pfn) +{ + pgprotval_t forbidden; + + /* Operate on the virtual address */ + forbidden = protect_kernel_text(address); + forbidden |= protect_kernel_text_ro(address); - return prot; + /* Check the PFN directly */ + forbidden |= protect_pci_bios(pfn); + forbidden |= protect_rodata(pfn); + + return __pgprot(pgprot_val(prot) & ~forbidden); } /* From c73b6d859bd510a753598eb4a1541a6f4358fa55 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:10 +0200 Subject: [PATCH 0363/1276] x86/mm/cpa: Allow range check for static protections Checking static protections only page by page is slow especially for huge pages. To allow quick checks over a complete range, add the ability to do that. Make the checks inclusive so the ranges can be directly used for debug output later. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.995734490@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 9731aeeebe71..61ff27848452 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -286,22 +286,29 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache, } } +static bool overlaps(unsigned long r1_start, unsigned long r1_end, + unsigned long r2_start, unsigned long r2_end) +{ + return (r1_start <= r2_end && r1_end >= r2_start) || + (r2_start <= r1_end && r2_end >= r1_start); +} + #ifdef CONFIG_PCI_BIOS /* * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS * based config access (CONFIG_PCI_GOBIOS) support. */ #define BIOS_PFN PFN_DOWN(BIOS_BEGIN) -#define BIOS_PFN_END PFN_DOWN(BIOS_END) +#define BIOS_PFN_END PFN_DOWN(BIOS_END - 1) -static pgprotval_t protect_pci_bios(unsigned long pfn) +static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn) { - if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END)) + if (pcibios_enabled && overlaps(spfn, epfn, BIOS_PFN, BIOS_PFN_END)) return _PAGE_NX; return 0; } #else -static pgprotval_t protect_pci_bios(unsigned long pfn) +static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn) { return 0; } @@ -312,12 +319,17 @@ static pgprotval_t protect_pci_bios(unsigned long pfn) * aliases. This also includes __ro_after_init, so do not enforce until * kernel_set_to_readonly is true. */ -static pgprotval_t protect_rodata(unsigned long pfn) +static pgprotval_t protect_rodata(unsigned long spfn, unsigned long epfn) { - unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT; - unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT; + unsigned long epfn_ro, spfn_ro = PFN_DOWN(__pa_symbol(__start_rodata)); + + /* + * Note: __end_rodata is at page aligned and not inclusive, so + * subtract 1 to get the last enforced PFN in the rodata area. + */ + epfn_ro = PFN_DOWN(__pa_symbol(__end_rodata)) - 1; - if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn)) + if (kernel_set_to_readonly && overlaps(spfn, epfn, spfn_ro, epfn_ro)) return _PAGE_RW; return 0; } @@ -330,9 +342,12 @@ static pgprotval_t protect_rodata(unsigned long pfn) * * This does not cover __inittext since that is gone after boot. */ -static pgprotval_t protect_kernel_text(unsigned long address) +static pgprotval_t protect_kernel_text(unsigned long start, unsigned long end) { - if (within(address, (unsigned long)_text, (unsigned long)_etext)) + unsigned long t_end = (unsigned long)_etext - 1; + unsigned long t_start = (unsigned long)_text; + + if (overlaps(start, end, t_start, t_end)) return _PAGE_NX; return 0; } @@ -347,13 +362,14 @@ static pgprotval_t protect_kernel_text(unsigned long address) * This will preserve the large page mappings for kernel text/data at no * extra cost. */ -static pgprotval_t protect_kernel_text_ro(unsigned long address) +static pgprotval_t protect_kernel_text_ro(unsigned long start, + unsigned long end) { - unsigned long end = (unsigned long)__end_rodata_hpage_align; - unsigned long start = (unsigned long)_text; + unsigned long t_end = (unsigned long)__end_rodata_hpage_align - 1; + unsigned long t_start = (unsigned long)_text; unsigned int level; - if (!kernel_set_to_readonly || !within(address, start, end)) + if (!kernel_set_to_readonly || !overlaps(start, end, t_start, t_end)) return 0; /* * Don't enforce the !RW mapping for the kernel text mapping, if @@ -367,12 +383,13 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address) * so the protections for kernel text and identity mappings have to * be the same. */ - if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) + if (lookup_address(start, &level) && (level != PG_LEVEL_4K)) return _PAGE_RW; return 0; } #else -static pgprotval_t protect_kernel_text_ro(unsigned long address) +static pgprotval_t protect_kernel_text_ro(unsigned long start, + unsigned long end) { return 0; } @@ -384,18 +401,20 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address) * right (again, ioremap() on BIOS memory is not uncommon) so this function * checks and fixes these known static required protection bits. */ -static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, - unsigned long pfn) +static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, + unsigned long pfn, unsigned long npg) { pgprotval_t forbidden; + unsigned long end; /* Operate on the virtual address */ - forbidden = protect_kernel_text(address); - forbidden |= protect_kernel_text_ro(address); + end = start + npg * PAGE_SIZE - 1; + forbidden = protect_kernel_text(start, end); + forbidden |= protect_kernel_text_ro(start, end); /* Check the PFN directly */ - forbidden |= protect_pci_bios(pfn); - forbidden |= protect_rodata(pfn); + forbidden |= protect_pci_bios(pfn, pfn + npg - 1); + forbidden |= protect_rodata(pfn, pfn + npg - 1); return __pgprot(pgprot_val(prot) & ~forbidden); } @@ -667,10 +686,10 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, * in it results in a different pgprot than the first one of the * requested range. If yes, then the page needs to be split. */ - new_prot = static_protections(req_prot, address, pfn); + new_prot = static_protections(req_prot, address, pfn, 1); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn); + pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -1280,7 +1299,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); - new_prot = static_protections(new_prot, address, pfn); + new_prot = static_protections(new_prot, address, pfn, 1); new_prot = pgprot_clear_protnone_bits(new_prot); From 6fe60544d740cb4fec572f8945333c10d4f863ca Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:11 +0200 Subject: [PATCH 0364/1276] x86/mm/cpa: Add debug mechanism The whole static protection magic is silently fixing up anything which is handed in. That's just wrong. The offending call sites need to be fixed. Add a debug mechanism which emits a warning if a requested mapping needs to be fixed up. The DETECT debug mechanism is really not meant to be enabled except for developers, so limit the output hard to the protection fixups. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.078998733@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 61 +++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 61ff27848452..1f2519055b30 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -42,6 +42,13 @@ struct cpa_data { struct page **pages; }; +enum cpa_warn { + CPA_PROTECT, + CPA_DETECT, +}; + +static const int cpa_warn_level = CPA_PROTECT; + /* * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings) * using cpa_lock. So that we don't allow any other cpu, with stale large tlb @@ -395,6 +402,28 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, } #endif +static inline bool conflicts(pgprot_t prot, pgprotval_t val) +{ + return (pgprot_val(prot) & ~val) != pgprot_val(prot); +} + +static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val, + unsigned long start, unsigned long end, + unsigned long pfn, const char *txt) +{ + static const char *lvltxt[] = { + [CPA_PROTECT] = "protect", + [CPA_DETECT] = "detect", + }; + + if (warnlvl > cpa_warn_level || !conflicts(prot, val)) + return; + + pr_warn("CPA %8s %10s: 0x%016lx - 0x%016lx PFN %lx req %016llx prevent %016llx\n", + lvltxt[warnlvl], txt, start, end, pfn, (unsigned long long)pgprot_val(prot), + (unsigned long long)val); +} + /* * Certain areas of memory on x86 require very specific protection flags, * for example the BIOS area or kernel text. Callers don't always get this @@ -402,19 +431,31 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, * checks and fixes these known static required protection bits. */ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, - unsigned long pfn, unsigned long npg) + unsigned long pfn, unsigned long npg, + int warnlvl) { - pgprotval_t forbidden; + pgprotval_t forbidden, res; unsigned long end; /* Operate on the virtual address */ end = start + npg * PAGE_SIZE - 1; - forbidden = protect_kernel_text(start, end); - forbidden |= protect_kernel_text_ro(start, end); + + res = protect_kernel_text(start, end); + check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX"); + forbidden = res; + + res = protect_kernel_text_ro(start, end); + check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO"); + forbidden |= res; /* Check the PFN directly */ - forbidden |= protect_pci_bios(pfn, pfn + npg - 1); - forbidden |= protect_rodata(pfn, pfn + npg - 1); + res = protect_pci_bios(pfn, pfn + npg - 1); + check_conflict(warnlvl, prot, res, start, end, pfn, "PCIBIOS NX"); + forbidden |= res; + + res = protect_rodata(pfn, pfn + npg - 1); + check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO"); + forbidden |= res; return __pgprot(pgprot_val(prot) & ~forbidden); } @@ -686,10 +727,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, * in it results in a different pgprot than the first one of the * requested range. If yes, then the page needs to be split. */ - new_prot = static_protections(req_prot, address, pfn, 1); + new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1); + pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, + CPA_DETECT); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -1299,7 +1341,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); - new_prot = static_protections(new_prot, address, pfn, 1); + new_prot = static_protections(new_prot, address, pfn, 1, + CPA_PROTECT); new_prot = pgprot_clear_protnone_bits(new_prot); From 157ea07d5141c76e6f422dfdbcbc6745a2879123 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:12 +0200 Subject: [PATCH 0365/1276] x86/mm/cpa: Add large page preservation statistics The large page preservation mechanism is just magic and provides no information at all. Add optional statistic output in debugfs so the magic can be evaluated. Defaults is off. Output: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800770 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.160867778@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/Kconfig | 8 ++++ arch/x86/mm/pageattr.c | 99 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1a0be022f91d..65728bb1182c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1491,6 +1491,14 @@ config X86_DIRECT_GBPAGES supports them), so don't confuse the user by printing that we have them enabled. +config X86_CPA_STATISTICS + bool "Enable statistic for Change Page Attribute" + depends on DEBUG_FS + ---help--- + Expose statistics about the Change Page Attribute mechanims, which + helps to determine the effectivness of preserving large and huge + page mappings when mapping protections are changed. + config ARCH_HAS_MEM_ENCRYPT def_bool y diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 1f2519055b30..cdf52eb86f3a 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -101,6 +101,95 @@ void arch_report_meminfo(struct seq_file *m) static inline void split_page_count(int level) { } #endif +#ifdef CONFIG_X86_CPA_STATISTICS + +static unsigned long cpa_1g_checked; +static unsigned long cpa_1g_sameprot; +static unsigned long cpa_1g_preserved; +static unsigned long cpa_2m_checked; +static unsigned long cpa_2m_sameprot; +static unsigned long cpa_2m_preserved; +static unsigned long cpa_4k_checked; +static unsigned long cpa_4k_install; + +static inline void cpa_inc_1g_checked(void) +{ + cpa_1g_checked++; +} + +static inline void cpa_inc_2m_checked(void) +{ + cpa_2m_checked++; +} + +static inline void cpa_inc_4k_checked(void) +{ + cpa_4k_checked++; +} + +static inline void cpa_inc_4k_install(void) +{ + cpa_4k_install++; +} + +static inline void cpa_inc_lp_sameprot(int level) +{ + if (level == PG_LEVEL_1G) + cpa_1g_sameprot++; + else + cpa_2m_sameprot++; +} + +static inline void cpa_inc_lp_preserved(int level) +{ + if (level == PG_LEVEL_1G) + cpa_1g_preserved++; + else + cpa_2m_preserved++; +} + +static int cpastats_show(struct seq_file *m, void *p) +{ + seq_printf(m, "1G pages checked: %16lu\n", cpa_1g_checked); + seq_printf(m, "1G pages sameprot: %16lu\n", cpa_1g_sameprot); + seq_printf(m, "1G pages preserved: %16lu\n", cpa_1g_preserved); + seq_printf(m, "2M pages checked: %16lu\n", cpa_2m_checked); + seq_printf(m, "2M pages sameprot: %16lu\n", cpa_2m_sameprot); + seq_printf(m, "2M pages preserved: %16lu\n", cpa_2m_preserved); + seq_printf(m, "4K pages checked: %16lu\n", cpa_4k_checked); + seq_printf(m, "4K pages set-checked: %16lu\n", cpa_4k_install); + return 0; +} + +static int cpastats_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpastats_show, NULL); +} + +static const struct file_operations cpastats_fops = { + .open = cpastats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init cpa_stats_init(void) +{ + debugfs_create_file("cpa_stats", S_IRUSR, arch_debugfs_dir, NULL, + &cpastats_fops); + return 0; +} +late_initcall(cpa_stats_init); +#else +static inline void cpa_inc_1g_checked(void) { } +static inline void cpa_inc_2m_checked(void) { } +static inline void cpa_inc_4k_checked(void) { } +static inline void cpa_inc_4k_install(void) { } +static inline void cpa_inc_lp_sameprot(int level) { } +static inline void cpa_inc_lp_preserved(int level) { } +#endif + + static inline int within(unsigned long addr, unsigned long start, unsigned long end) { @@ -664,10 +753,12 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, case PG_LEVEL_2M: old_prot = pmd_pgprot(*(pmd_t *)kpte); old_pfn = pmd_pfn(*(pmd_t *)kpte); + cpa_inc_2m_checked(); break; case PG_LEVEL_1G: old_prot = pud_pgprot(*(pud_t *)kpte); old_pfn = pud_pfn(*(pud_t *)kpte); + cpa_inc_1g_checked(); break; default: return -EINVAL; @@ -732,14 +823,16 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, CPA_DETECT); - + cpa_inc_4k_checked(); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; } /* If there are no changes, return. */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) + if (pgprot_val(new_prot) == pgprot_val(old_prot)) { + cpa_inc_lp_sameprot(level); return 0; + } /* * Verify that the address is aligned and the number of pages @@ -752,6 +845,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); cpa->flags |= CPA_FLUSHTLB; + cpa_inc_lp_preserved(level); return 0; } @@ -1341,6 +1435,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); + cpa_inc_4k_install(); new_prot = static_protections(new_prot, address, pfn, 1, CPA_PROTECT); From 34ae4bfeab1a8c3177f950d9344f6337e5f18f4b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:13 +0200 Subject: [PATCH 0366/1276] x86/mm/cpa: Avoid static protection checks on unmap If the new pgprot has the PRESENT bit cleared, then conflicts vs. RW/NX are completely irrelevant. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800770 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800709 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.245849757@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index cdf52eb86f3a..8f9083eb21ac 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -526,6 +526,13 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, pgprotval_t forbidden, res; unsigned long end; + /* + * There is no point in checking RW/NX conflicts when the requested + * mapping is setting the page !PRESENT. + */ + if (!(pgprot_val(prot) & _PAGE_PRESENT)) + return prot; + /* Operate on the virtual address */ end = start + npg * PAGE_SIZE - 1; From ce7a6ada29e9969153030a9f00217f8f67061d9b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:14 +0200 Subject: [PATCH 0367/1276] x86/mm/cpa: Add sanity check for existing mappings With the range check it is possible to do a quick verification that the current mapping is correct vs. the static protection areas. In case a incorrect mapping is detected a warning is emitted and the large page is split up. If the large page is a 2M page, then the split code is forced to check the static protections for the PTE entries to fix up the incorrectness. For 1G pages this can't be done easily because that would require to either find the offending 2M areas before the split or afterwards. For now just warn about that case and revisit it when reported. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.331408643@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 77 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 8f9083eb21ac..19781b0ab4b4 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -37,12 +37,14 @@ struct cpa_data { unsigned long numpages; int flags; unsigned long pfn; - unsigned force_split : 1; + unsigned force_split : 1, + force_static_prot : 1; int curpage; struct page **pages; }; enum cpa_warn { + CPA_CONFLICT, CPA_PROTECT, CPA_DETECT, }; @@ -501,6 +503,7 @@ static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val, unsigned long pfn, const char *txt) { static const char *lvltxt[] = { + [CPA_CONFLICT] = "conflict", [CPA_PROTECT] = "protect", [CPA_DETECT] = "detect", }; @@ -743,7 +746,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; - pgprot_t old_prot, new_prot, req_prot; + pgprot_t old_prot, new_prot, req_prot, chk_prot; pte_t new_pte, old_pte, *tmp; enum pg_level level; int i; @@ -819,6 +822,23 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, lpaddr = address & pmask; numpages = psize >> PAGE_SHIFT; + /* + * Sanity check that the existing mapping is correct versus the static + * protections. static_protections() guards against !PRESENT, so no + * extra conditional required here. + */ + chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages, + CPA_CONFLICT); + + if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) { + /* + * Split the large page and tell the split code to + * enforce static protections. + */ + cpa->force_static_prot = 1; + return 1; + } + /* * Make sure that the requested pgprot does not violate the static * protections. Check the full large page whether one of the pages @@ -828,8 +848,8 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, - CPA_DETECT); + chk_prot = static_protections(req_prot, addr, pfn, 1, + CPA_DETECT); cpa_inc_4k_checked(); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -871,15 +891,50 @@ static int should_split_large_page(pte_t *kpte, unsigned long address, return do_split; } +static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn, + pgprot_t ref_prot, unsigned long address, + unsigned long size) +{ + unsigned int npg = PFN_DOWN(size); + pgprot_t prot; + + /* + * If should_split_large_page() discovered an inconsistent mapping, + * remove the invalid protection in the split mapping. + */ + if (!cpa->force_static_prot) + goto set; + + prot = static_protections(ref_prot, address, pfn, npg, CPA_PROTECT); + + if (pgprot_val(prot) == pgprot_val(ref_prot)) + goto set; + + /* + * If this is splitting a PMD, fix it up. PUD splits cannot be + * fixed trivially as that would require to rescan the newly + * installed PMD mappings after returning from split_large_page() + * so an eventual further split can allocate the necessary PTE + * pages. Warn for now and revisit it in case this actually + * happens. + */ + if (size == PAGE_SIZE) + ref_prot = prot; + else + pr_warn_once("CPA: Cannot fixup static protections for PUD split\n"); +set: + set_pte(pte, pfn_pte(pfn, ref_prot)); +} + static int __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, struct page *base) { + unsigned long lpaddr, lpinc, ref_pfn, pfn, pfninc = 1; pte_t *pbase = (pte_t *)page_address(base); - unsigned long ref_pfn, pfn, pfninc = 1; unsigned int i, level; - pte_t *tmp; pgprot_t ref_prot; + pte_t *tmp; spin_lock(&pgd_lock); /* @@ -902,15 +957,17 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * PAT bit to correct position. */ ref_prot = pgprot_large_2_4k(ref_prot); - ref_pfn = pmd_pfn(*(pmd_t *)kpte); + lpaddr = address & PMD_MASK; + lpinc = PAGE_SIZE; break; case PG_LEVEL_1G: ref_prot = pud_pgprot(*(pud_t *)kpte); ref_pfn = pud_pfn(*(pud_t *)kpte); pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; - + lpaddr = address & PUD_MASK; + lpinc = PMD_SIZE; /* * Clear the PSE flags if the PRESENT flag is not set * otherwise pmd_present/pmd_huge will return true @@ -931,8 +988,8 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * Get the target pfn from the original entry: */ pfn = ref_pfn; - for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) - set_pte(&pbase[i], pfn_pte(pfn, ref_prot)); + for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc, lpaddr += lpinc) + split_set_pte(cpa, pbase + i, pfn, ref_prot, lpaddr, lpinc); if (virt_addr_valid(address)) { unsigned long pfn = PFN_DOWN(__pa(address)); From ed95b7ab62420a5696881dcc94cc56e99cf04680 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:15 +0200 Subject: [PATCH 0368/1276] x86/mm/cpa: Optimize same protection check When the existing mapping is correct and the new requested page protections are the same as the existing ones, then further checks can be omitted and the large page can be preserved. The slow path 4k wise check will not come up with a different result. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800709 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 560642 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.424477581@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 19781b0ab4b4..5160334f9095 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -839,6 +839,20 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, return 1; } + /* + * Optimization: If the requested pgprot is the same as the current + * pgprot, then the large page can be preserved and no updates are + * required independent of alignment and length of the requested + * range. The above already established that the current pgprot is + * correct, which in consequence makes the requested pgprot correct + * as well if it is the same. The static protection scan below will + * not come to a different conclusion. + */ + if (pgprot_val(req_prot) == pgprot_val(old_prot)) { + cpa_inc_lp_sameprot(level); + return 0; + } + /* * Make sure that the requested pgprot does not violate the static * protections. Check the full large page whether one of the pages From 7daa247d4ca88405d678b88b6ea0a91f3f80a973 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:16 +0200 Subject: [PATCH 0369/1276] x86/mm/cpa: Do the range check early To avoid excessive 4k wise checks in the common case do a quick check first whether the requested new page protections conflict with a static protection area in the large page. If there is no conflict then the decision whether to preserve or to split the page can be made immediately. If the requested range covers the full large page, preserve it. Otherwise split it up. No point in doing a slow crawl in 4k steps. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 560642 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 541 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 514 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.507259989@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 5160334f9095..bbc5eb5cbc9d 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -854,10 +854,28 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, } /* - * Make sure that the requested pgprot does not violate the static - * protections. Check the full large page whether one of the pages - * in it results in a different pgprot than the first one of the - * requested range. If yes, then the page needs to be split. + * Optimization: Check whether the requested pgprot is conflicting + * with a static protection requirement in the large page. If not, + * then checking whether the requested range is fully covering the + * large page can be done right here. + */ + new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, + CPA_DETECT); + + if (pgprot_val(req_prot) == pgprot_val(new_prot)) { + if (address != lpaddr || cpa->numpages != numpages) + return 1; + goto setlp; + } + + /* + * Slow path. The full large page check above established that the + * requested pgprot cannot be applied to the full large page due to + * conflicting requirements of static protection regions. It might + * turn out that the whole requested range is covered by the + * modified protection of the first 4k segment at @address. This + * might result in the ability to preserve the large page + * nevertheless. */ new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; @@ -882,6 +900,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, if (address != lpaddr || cpa->numpages != numpages) return 1; +setlp: /* All checks passed. Update the large page mapping. */ new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); From bb1e569b89ee3b9f5128d5eb6ac15e5a41b084cd Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:17 +0200 Subject: [PATCH 0370/1276] x86/mm/cpa: Avoid the 4k pages check completely The extra loop which tries hard to preserve large pages in case of conflicts with static protection regions turns out to be not preserving anything, at least not in the experiments which have been conducted. There might be corner cases in which the code would be able to preserve a large page oaccsionally, but it's really not worth the extra code and the cycles wasted in the common case. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 541 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 514 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.589642503@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 64 +++++++++++------------------------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index bbc5eb5cbc9d..4e55ded01be5 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -111,7 +111,6 @@ static unsigned long cpa_1g_preserved; static unsigned long cpa_2m_checked; static unsigned long cpa_2m_sameprot; static unsigned long cpa_2m_preserved; -static unsigned long cpa_4k_checked; static unsigned long cpa_4k_install; static inline void cpa_inc_1g_checked(void) @@ -124,11 +123,6 @@ static inline void cpa_inc_2m_checked(void) cpa_2m_checked++; } -static inline void cpa_inc_4k_checked(void) -{ - cpa_4k_checked++; -} - static inline void cpa_inc_4k_install(void) { cpa_4k_install++; @@ -158,7 +152,6 @@ static int cpastats_show(struct seq_file *m, void *p) seq_printf(m, "2M pages checked: %16lu\n", cpa_2m_checked); seq_printf(m, "2M pages sameprot: %16lu\n", cpa_2m_sameprot); seq_printf(m, "2M pages preserved: %16lu\n", cpa_2m_preserved); - seq_printf(m, "4K pages checked: %16lu\n", cpa_4k_checked); seq_printf(m, "4K pages set-checked: %16lu\n", cpa_4k_install); return 0; } @@ -185,7 +178,6 @@ late_initcall(cpa_stats_init); #else static inline void cpa_inc_1g_checked(void) { } static inline void cpa_inc_2m_checked(void) { } -static inline void cpa_inc_4k_checked(void) { } static inline void cpa_inc_4k_install(void) { } static inline void cpa_inc_lp_sameprot(int level) { } static inline void cpa_inc_lp_preserved(int level) { } @@ -745,11 +737,10 @@ static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) static int __should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { - unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; + unsigned long numpages, pmask, psize, lpaddr, pfn, old_pfn; pgprot_t old_prot, new_prot, req_prot, chk_prot; pte_t new_pte, old_pte, *tmp; enum pg_level level; - int i; /* * Check for races, another CPU might have split this page @@ -854,53 +845,30 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, } /* - * Optimization: Check whether the requested pgprot is conflicting - * with a static protection requirement in the large page. If not, - * then checking whether the requested range is fully covering the - * large page can be done right here. + * If the requested range does not cover the full page, split it up */ - new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, - CPA_DETECT); - - if (pgprot_val(req_prot) == pgprot_val(new_prot)) { - if (address != lpaddr || cpa->numpages != numpages) - return 1; - goto setlp; - } + if (address != lpaddr || cpa->numpages != numpages) + return 1; /* - * Slow path. The full large page check above established that the - * requested pgprot cannot be applied to the full large page due to - * conflicting requirements of static protection regions. It might - * turn out that the whole requested range is covered by the - * modified protection of the first 4k segment at @address. This - * might result in the ability to preserve the large page - * nevertheless. + * Check whether the requested pgprot is conflicting with a static + * protection requirement in the large page. */ - new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); - pfn = old_pfn; - for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - chk_prot = static_protections(req_prot, addr, pfn, 1, - CPA_DETECT); - cpa_inc_4k_checked(); - if (pgprot_val(chk_prot) != pgprot_val(new_prot)) - return 1; - } - - /* If there are no changes, return. */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) { - cpa_inc_lp_sameprot(level); - return 0; - } + new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, + CPA_DETECT); /* - * Verify that the address is aligned and the number of pages - * covers the full page. + * If there is a conflict, split the large page. + * + * There used to be a 4k wise evaluation trying really hard to + * preserve the large pages, but experimentation has shown, that this + * does not help at all. There might be corner cases which would + * preserve one large page occasionally, but it's really not worth the + * extra code and cycles for the common case. */ - if (address != lpaddr || cpa->numpages != numpages) + if (pgprot_val(req_prot) != pgprot_val(new_prot)) return 1; -setlp: /* All checks passed. Update the large page mapping. */ new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); From 008df4900c3f6f6f4a8380bcd0a4ebf7d083d462 Mon Sep 17 00:00:00 2001 From: "G Jaya Kumaran, Vineetha" Date: Thu, 24 May 2018 18:45:28 +0800 Subject: [PATCH 0371/1276] Integration of CBC line discipline kernel module This patch enables the Carrier Board Communication (CBC) kernel module, which is implemented as a Linux kernel line discipline attached to a UART device. The CBC protocol defines a number of different channels which are provided as Linux character device nodes. Change-Id: Ic2a72229df9e9a80e306f2f44da37b1ac8539af3 Signed-off-by: G Jaya Kumaran, Vineetha --- drivers/tty/Kconfig | 3 + drivers/tty/Makefile | 1 + drivers/tty/cbc/Kconfig | 22 + drivers/tty/cbc/Makefile | 14 + drivers/tty/cbc/cbc_core.c | 307 +++++++++ drivers/tty/cbc/cbc_core.h | 44 ++ drivers/tty/cbc/cbc_core_public.h | 28 + drivers/tty/cbc/cbc_device.c | 66 ++ drivers/tty/cbc/cbc_device.h | 107 ++++ drivers/tty/cbc/cbc_device_manager.c | 879 ++++++++++++++++++++++++++ drivers/tty/cbc/cbc_device_manager.h | 66 ++ drivers/tty/cbc/cbc_link_checksum.c | 59 ++ drivers/tty/cbc/cbc_link_checksum.h | 44 ++ drivers/tty/cbc/cbc_link_layer.c | 484 ++++++++++++++ drivers/tty/cbc/cbc_link_layer.h | 38 ++ drivers/tty/cbc/cbc_memory.c | 153 +++++ drivers/tty/cbc/cbc_memory.h | 146 +++++ drivers/tty/cbc/cbc_mux_multiplexer.c | 145 +++++ drivers/tty/cbc/cbc_mux_multiplexer.h | 62 ++ drivers/tty/cbc/cbc_types.h | 137 ++++ include/cbc/cbc-core.h | 24 + include/uapi/linux/cbc/cbc-core.h | 24 + include/uapi/linux/major.h | 2 + include/uapi/linux/tty.h | 5 +- 24 files changed, 2858 insertions(+), 2 deletions(-) create mode 100644 drivers/tty/cbc/Kconfig create mode 100644 drivers/tty/cbc/Makefile create mode 100644 drivers/tty/cbc/cbc_core.c create mode 100644 drivers/tty/cbc/cbc_core.h create mode 100644 drivers/tty/cbc/cbc_core_public.h create mode 100644 drivers/tty/cbc/cbc_device.c create mode 100644 drivers/tty/cbc/cbc_device.h create mode 100644 drivers/tty/cbc/cbc_device_manager.c create mode 100644 drivers/tty/cbc/cbc_device_manager.h create mode 100644 drivers/tty/cbc/cbc_link_checksum.c create mode 100644 drivers/tty/cbc/cbc_link_checksum.h create mode 100644 drivers/tty/cbc/cbc_link_layer.c create mode 100644 drivers/tty/cbc/cbc_link_layer.h create mode 100644 drivers/tty/cbc/cbc_memory.c create mode 100644 drivers/tty/cbc/cbc_memory.h create mode 100644 drivers/tty/cbc/cbc_mux_multiplexer.c create mode 100644 drivers/tty/cbc/cbc_mux_multiplexer.h create mode 100644 drivers/tty/cbc/cbc_types.h create mode 100644 include/cbc/cbc-core.h create mode 100644 include/uapi/linux/cbc/cbc-core.h diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 0840d27381ea..7210714592ac 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -441,4 +441,7 @@ config VCC depends on SUN_LDOMS help Support for Sun logical domain consoles. + +source "drivers/tty/cbc/Kconfig" + endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index c72cafdf32b4..4ae6744406d4 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -33,5 +33,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o obj-$(CONFIG_VCC) += vcc.o +obj-$(CONFIG_CBC_LDISC) += cbc/ obj-y += ipwireless/ diff --git a/drivers/tty/cbc/Kconfig b/drivers/tty/cbc/Kconfig new file mode 100644 index 000000000000..c9cde7fa43ca --- /dev/null +++ b/drivers/tty/cbc/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CBC (CarrierBoardCommunication) subsystem configuration +# + +config CBC_LDISC + tristate "CBC (Carrier Board Communication) line discipline" + depends on TTY + help + The CBC driver implements a line discipline supporting + the proprietary CBC (Carrier Board Communication) protocol. + + The CBC protocol is a serial line protocol with multiplexing + intended to be used in automotive IVI platforms for multi- + plexed communication between a vehicle IOC and CPU. It is + designed to transport small data packets (up to 64 bytes) + and features a transport protocol to transport larger data + chunks over a point to point connection. + + When initialised the driver presents a number of channels as + character devices. + diff --git a/drivers/tty/cbc/Makefile b/drivers/tty/cbc/Makefile new file mode 100644 index 000000000000..c0517817f605 --- /dev/null +++ b/drivers/tty/cbc/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 + + +cbc-ldisc-y := cbc_core.o +cbc-ldisc-y += cbc_device.o +cbc-ldisc-y += cbc_device_manager.o +cbc-ldisc-y += cbc_link_checksum.o +cbc-ldisc-y += cbc_link_layer.o +cbc-ldisc-y += cbc_memory.o +cbc-ldisc-y += cbc_mux_multiplexer.o + +obj-$(CONFIG_CBC_LDISC) += cbc-ldisc.o + +ccflags-y := -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -fstack-protector \ No newline at end of file diff --git a/drivers/tty/cbc/cbc_core.c b/drivers/tty/cbc/cbc_core.c new file mode 100644 index 000000000000..d9d15fa224ae --- /dev/null +++ b/drivers/tty/cbc/cbc_core.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cbc_core.h" +#include "cbc_types.h" +#include "cbc_device_manager.h" +#include "cbc_link_layer.h" +#include "cbc_mux_multiplexer.h" + +MODULE_AUTHOR("Gerhard Bocksch. gerhard.bocksch@intel.com"); +MODULE_DESCRIPTION("CBC serial protocol demultiplexer"); +MODULE_LICENSE("GPL"); + +static int granularity = 4; +module_param(granularity, int, 0644); +MODULE_PARM_DESC(granularity, + "The granularity of the CBC messages. default: 4"); + +MODULE_ALIAS_LDISC(N_CBCCORE); + +/* Marker to mark the cbc_core as valid. */ +#define CBC_MAGIC 0xAFFEAFFE + +static struct cbc_struct cbc_core = { .magic = CBC_MAGIC }; + +/* + * cbc_core_configure_channel - Set the priority and receive + * callback for a CBC channel. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for channel. + * @data: Data associated with channel. + * @receive: Receive callback associated with channel. + */ +void cbc_core_configure_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)) +{ + cbc_mux_configure_data_channel(channel_idx, priority, data, receive); +} +EXPORT_SYMBOL_GPL(cbc_core_configure_channel); + +/* + * cbc_core_send_data - Send to a CBC channel. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for channel. + * @data: Data associated with channel. + * @receive: Receive function associated with channel. + */ +void cbc_core_send_data(const u32 channel_idx, const u16 length, + const u8 * const buffer) +{ + cbc_manager_transmit_data(channel_idx, length, buffer); +} +EXPORT_SYMBOL_GPL(cbc_core_send_data); + +/* + * cbc_ldisc_open - Open the CBC connection to the IOC. + * @tty: Handle to tty. + * + * This function is called by the tty module when the + * line discipline is requested. It allocates the memory pool and creates the + * CBC devices. + * Called in process context serialized from other ldisc calls. + * + * Return: 0 on success error otherwise. + */ +static int cbc_ldisc_open(struct tty_struct *tty) +{ + struct cbc_struct *cbc; + int err; + + pr_debug("cbc-ldisc open.\n"); + mutex_lock(&cbc_core.ldisc_mutex); + + if (WARN_ON(tty->ops->write == NULL)) { + pr_err("cbc-ldisc open write not supported.\n"); + err = -EOPNOTSUPP; + goto err_exit; + } + + cbc = tty->disc_data; + + err = -EEXIST; + /* First make sure we're not already connected. */ + if (WARN_ON(cbc && cbc->magic == CBC_MAGIC)) { + pr_err( + "cbc-ldisc CBC line discipline already open or CBC magic wrong.\n"); + goto err_exit; + } + + cbc = &cbc_core; + + cbc->tty = tty; + tty->disc_data = cbc; + tty->receive_room = 65536; + + /* Create class cbc-core. This will appear as /sys/class/cbc* */ + cbc->cbc_class = class_create(THIS_MODULE, "cbc"); + + if (WARN_ON(!cbc->cbc_class)) { + pr_err("cbc-ldisc open could not create cbc class.\n"); + goto err_exit; + } + + /* Create memory pool */ + cbc->memory_pool = cbc_memory_pool_create( + CBC_QUEUE_LENGTH * CBC_CHANNEL_MAX_NUMBER); + if (WARN_ON(!cbc->memory_pool)) { + pr_err("failed to create memory pool.\n"); + goto err_exit; + } + + /* Initialise on every open, so we start with sequence-counters at 0.*/ + cbc_link_layer_init(cbc->memory_pool); + + /* Register devices here. */ + err = cbc_register_devices(cbc->cbc_class, cbc->memory_pool); + if (err != 0) { + pr_err("register devices failed\n"); + goto err_exit; + } + + cbc_link_layer_set_frame_granularity(granularity); + + /* tty layer expects 0 on success */ + mutex_unlock(&cbc_core.ldisc_mutex); + return 0; + +err_exit: mutex_unlock(&cbc_core.ldisc_mutex); + return err; +} + +/* + * cbc_ldisc_close - Close down the CBC communication. + * @tty: Handle to tty + * + * Unregister CBC devices and destroy CBC class. + */ +static void cbc_ldisc_close(struct tty_struct *tty) +{ + struct cbc_struct *cbc; + + mutex_lock(&cbc_core.ldisc_mutex); + + cbc = (struct cbc_struct *) tty->disc_data; + pr_debug("cbc-ldisc close\n"); + + if (cbc && cbc->magic == CBC_MAGIC && cbc->tty == tty) { + cbc_unregister_devices(cbc->cbc_class); + class_destroy(cbc->cbc_class); + if (WARN_ON(!cbc_memory_pool_try_free(cbc->memory_pool))) + pr_err("could not free memory pool.\n"); + tty->disc_data = NULL; + cbc->tty = NULL; + } else { + WARN_ON(1); + pr_err("ldisc close with wrong CBC magic.\n"); + } + + mutex_unlock(&cbc_core.ldisc_mutex); +} + +/* + * Close line discipline on ldisc hangup. + * @tty: Handle to tty + */ +static int cbc_ldisc_hangup(struct tty_struct *tty) +{ + cbc_ldisc_close(tty); + return 0; +} + +/* + * cbc_ldisc_receive_buf - .receive call for a line discipline. + * @tty: Handle to tty + * @cp: Received data buffer. + * @fp: Not used + * @count: Amount of data received. + * + * Attempts to read a single CBC frame at a time. Cycles round + * until all data has been processed. + */ +static void cbc_ldisc_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct cbc_struct *cbc; + u8 accepted_bytes; /* per cbc_serial_receive call */ + unsigned int accepted_bytes_sum = 0; + + mutex_lock(&cbc_core.ldisc_mutex); + cbc = (struct cbc_struct *) tty->disc_data; + + if (cbc && (cbc->magic == CBC_MAGIC) && (cbc->tty == tty)) { + do { + u8 chunksize = 254; + + if ((count - accepted_bytes_sum) < 255) + chunksize = count - accepted_bytes_sum; + accepted_bytes = cbc_core_on_receive_cbc_serial_data( + chunksize, cp + accepted_bytes_sum); + accepted_bytes_sum += accepted_bytes; + cbc_link_layer_rx_handler(); + } while (accepted_bytes_sum != count); + } + mutex_unlock(&cbc_core.ldisc_mutex); +} + +/* + * Called from ldisc when write is possible. Not used. + */ +static void cbc_ldisc_write_wakeup(struct tty_struct *tty) +{ + (void) tty; +} + +/* Called to send messages from channel specific device to the IOC */ +enum cbc_error target_specific_send_cbc_uart_data(u16 length, + const u8 *raw_buffer) +{ + mutex_lock(&cbc_core.ldisc_mutex); + if (cbc_core.tty) { + set_bit(TTY_DO_WRITE_WAKEUP, &cbc_core.tty->flags); + + cbc_core.tty->ops->write(cbc_core.tty, raw_buffer, length); + } + mutex_unlock(&cbc_core.ldisc_mutex); + + return CBC_OK; +} + +static struct tty_ldisc_ops cbc_ldisc = { + .owner = THIS_MODULE, + .name = "cbc-ldisc", + .magic = TTY_LDISC_MAGIC, + .open = cbc_ldisc_open, + .close = cbc_ldisc_close, + .hangup = cbc_ldisc_hangup, + .receive_buf = cbc_ldisc_receive_buf, + .write_wakeup = cbc_ldisc_write_wakeup +}; + +/* + * cbc_init - Module init call. + * Registers this module as TTY line discipline. + */ +static int __init cbc_init(void) +{ + int status; + + pr_debug("cbc-ldisc init\n"); + mutex_init(&cbc_core.ldisc_mutex); + mutex_lock(&cbc_core.ldisc_mutex); + + cbc_kmod_devices_init(); + + /* Fill in line discipline, and register it */ + status = tty_register_ldisc(N_CBCCORE, &cbc_ldisc); + if (status) + pr_err("cbc-ldisc: can't register line discipline\n"); + + mutex_unlock(&cbc_core.ldisc_mutex); + return 0; +} + +/* + * cbc_exit - Module exit call. + * + * De-registers itself as line discipline. + */ +static void __exit cbc_exit(void) +{ + int i; + + mutex_lock(&cbc_core.ldisc_mutex); + + pr_debug("cbc-ldisc Exit\n"); + + i = tty_unregister_ldisc(N_CBCCORE); + if (i) + pr_err("cbc-ldisc: can't unregister ldisc (err %d).\n", i); + + mutex_unlock(&cbc_core.ldisc_mutex); +} + +module_init(cbc_init); +module_exit(cbc_exit); diff --git a/drivers/tty/cbc/cbc_core.h b/drivers/tty/cbc/cbc_core.h new file mode 100644 index 000000000000..71e07a3f7765 --- /dev/null +++ b/drivers/tty/cbc/cbc_core.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_CORE_MOD_H_ +#define CBC_CORE_MOD_H_ + +#include +#include +#include +#include +#include + +#include "cbc_memory.h" + +#define CBC_IOCTL_MAGIC 0xf4 + +/* + * struct cbc_struct - + * + * @magic: Marker to mark the cbc_core as valid. + * @tty: tty associated with the CBC driver. + * @class: CBC device class + * @memory_pool: Memory pool of CBC buffer allocated for used by CBC driver. + * @ldisc_mutex: Mutex to avoid unloading while accessing the driver + */ +struct cbc_struct { + int magic; + struct tty_struct *tty; + struct class *cbc_class; + struct cbc_memory_pool *memory_pool; + struct mutex ldisc_mutex; +}; + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/drivers/tty/cbc/cbc_core_public.h b/drivers/tty/cbc/cbc_core_public.h new file mode 100644 index 000000000000..85928f6efc68 --- /dev/null +++ b/drivers/tty/cbc/cbc_core_public.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_CORE_PUBLIC_H +#define CBC_CORE_PUBLIC_H + +#include "cbc_types.h" + +/* CBC version. */ +#define CBC_VERSION_ID 1 + +/* The following function needs to be implemented on CM/IOC. */ +enum cbc_error target_specific_send_cbc_uart_data(u16 length, + const u8 *raw_buffer); + + +#endif /* CBC_CORE_PUBLIC_H */ + diff --git a/drivers/tty/cbc/cbc_device.c b/drivers/tty/cbc/cbc_device.c new file mode 100644 index 000000000000..1933a8527015 --- /dev/null +++ b/drivers/tty/cbc/cbc_device.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include + +#include "cbc_device.h" + + +void cbc_device_init(struct cbc_device_data *cd) +{ + if (cd) + INIT_LIST_HEAD(&cd->open_files_head); +} + +void cbc_file_init(struct cbc_file_data *file) +{ + if (file) { + cbc_buffer_queue_init(&file->queue); + init_waitqueue_head(&file->wq_read); + INIT_LIST_HEAD(&file->list); + } +} + +void cbc_file_enqueue(struct cbc_file_data *fd, struct cbc_buffer *buffer) +{ + if (fd) { + if (cbc_buffer_queue_enqueue(&fd->queue, buffer)) { + cbc_buffer_increment_ref(buffer); + wake_up_interruptible(&fd->wq_read); + } + } +} + +struct cbc_buffer *cbc_file_dequeue(struct cbc_file_data *fd) +{ + struct cbc_buffer *buffer = NULL; + + if (fd) + buffer = cbc_buffer_queue_dequeue(&fd->queue); + + if (buffer && atomic_read(&buffer->refcount) == 0) { + buffer = NULL; + pr_err("cbc-core: De-queueing an already freed buffer\n"); + } + + return buffer; +} + +int cbc_file_queue_empty(struct cbc_file_data *fd) +{ + if (fd) + return (fd->queue.write == fd->queue.read); + + return 1; +} + diff --git a/drivers/tty/cbc/cbc_device.h b/drivers/tty/cbc/cbc_device.h new file mode 100644 index 000000000000..deb0cd922316 --- /dev/null +++ b/drivers/tty/cbc/cbc_device.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_DEVICE_H_ +#define CBC_DEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "cbc_types.h" +#include "cbc_memory.h" + +enum cbc_device_type { + CBC_DEVICE_TYPE_DEFAULT, + CBC_DEVICE_TYPE_RAW, + CBC_DEVICE_TYPE_HIDDEN, + CBC_DEVICE_TYPE_DEBUG +}; + +/* + * struct cbc_device_data -Data for a single channel e.g. /dev/cbc-pmt. + * @device_name: Device name. + * @device_type: CBC device type. + * See c:type 'enum cbc_device_type '. + * @device: Pointer to device struct. + * @open_files_head: Linked list of open files for this device. + * + * Configuration for a given CBC device. It will be stored in the device private + * data. + */ +struct cbc_device_data { + char *device_name; + enum cbc_device_type device_type; + struct device *device; + struct list_head open_files_head; +}; + +/* + * struct cbc_file_data - Data for a CBC device file . + * @queue: CBC buffer queue for this device. + * @wq_read: wait_queue_head_t fused for waking device on events. + * @cbc_device: Device data for this device. + * @list: list_head. + * + */ +struct cbc_file_data { + struct cbc_buffer_queue queue; + wait_queue_head_t wq_read; + struct cbc_device_data *cbc_device; + struct list_head list; +}; + +/* + * cbc_device_init - Initialise CBC device data + * @cd: pointer to CBC device data. + * + * Initialises device's list_head. + */ +void cbc_device_init(struct cbc_device_data *cd); + +/* + * cbc_file_init - Initialise CBC file data + * @file: pointer to CBC file data. + * + * Initialises device file's CBC queue, wait_queue_head_t and list_head. + */ +void cbc_file_init(struct cbc_file_data *file); + +/* + * cbc_file_enqueue -Add CBC buffer to queue. + * @fd: CBC device file data. + * buffer: Pointer to CBC buffer. + * Increases reference count on buffer. + */ +void cbc_file_enqueue(struct cbc_file_data *fd, struct cbc_buffer *buffer); + +/* + * cbc_file_dequeue - Remove buffer from head of queue. + * @fd: CBC device file data. + * + * Does not decrease reference count. + */ +struct cbc_buffer *cbc_file_dequeue(struct cbc_file_data *fd); + +/* + * cbc_file_queue_empty - Is CBC queue empty? + */ +int cbc_file_queue_empty(struct cbc_file_data *fd); + +#endif /* CBC_DEVICE_H_ */ diff --git a/drivers/tty/cbc/cbc_device_manager.c b/drivers/tty/cbc/cbc_device_manager.c new file mode 100644 index 000000000000..0e74183d9828 --- /dev/null +++ b/drivers/tty/cbc/cbc_device_manager.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include "cbc_core.h" +#include "cbc_device.h" +#include "cbc_device_manager.h" +#include "cbc_memory.h" +#include "cbc_mux_multiplexer.h" +#include "cbc_types.h" + +static int major; /* Default to dynamic major */ +module_param(major, int, 0); +MODULE_PARM_DESC(major, "Major device number"); + +/* + * Minor start number. + * This has to be 0 to allow a lookup of the device structs in an array. + */ +#define CBC_MINOR 0 +/* Device-name/driver-name when registering the devices in the linux kernel.*/ +#define DEVICE_NAME "cbc-core" + +/* Max number of open files per cbc channel */ +#define MAX_OPEN_FILES 6 + +static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer); + +static int cbc_device_open(struct inode *, struct file *); +static int cbc_device_release(struct inode *, struct file *); +static ssize_t cbc_device_read(struct file *, char __user *, size_t, + loff_t *); +static ssize_t cbc_device_write(struct file *, const char __user *, size_t, + loff_t *); +static unsigned int cbc_device_poll(struct file *file, poll_table *wait); +static long cbc_device_ioctl(struct file *, unsigned int, unsigned long); + +static const struct file_operations cbc_dev_file_operations = { + .owner = THIS_MODULE, + .open = cbc_device_open, + .release = cbc_device_release, + .read = cbc_device_read, + .write = cbc_device_write, + .poll = cbc_device_poll, + .unlocked_ioctl = cbc_device_ioctl +}; + +struct cbc_device_manager { + struct cdev cdev; + struct cbc_device_data channels[CBC_CHANNEL_MAX_NUMBER]; + struct mutex send_lock; + struct cbc_memory_pool *cbc_memory; +}; + +/* Currently, only one CBC per kernel supported.*/ +static struct cbc_device_manager cbc_device_mgr_configuration = { + .channels[CBC_CHANNEL_PMT].device_type = + CBC_DEVICE_TYPE_HIDDEN, + .channels[CBC_CHANNEL_PMT].device_name = + "cbc-pmt", + + .channels[CBC_CHANNEL_LIFECYCLE].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_LIFECYCLE].device_name = + "cbc-lifecycle", + + .channels[CBC_CHANNEL_SIGNALS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_SIGNALS].device_name = + "cbc-signals", + + .channels[CBC_CHANNEL_EARLY_SIGNALS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_EARLY_SIGNALS].device_name = + "cbc-early-signals", + + .channels[CBC_CHANNEL_DIAGNOSIS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_DIAGNOSIS].device_name = + "cbc-diagnosis", + + .channels[CBC_CHANNEL_DLT].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_DLT].device_name = + "cbc-dlt", + + .channels[CBC_CHANNEL_LINDA].device_type = + CBC_DEVICE_TYPE_HIDDEN, + .channels[CBC_CHANNEL_LINDA].device_name = + "cbc-linda", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_0].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_0].device_name = + "cbc-raw0", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_1].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_1].device_name = + "cbc-raw1", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_2].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_2].device_name = + "cbc-raw2", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_3].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_3].device_name = + "cbc-raw3", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_4].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_4].device_name = + "cbc-raw4", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_5].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_5].device_name = + "cbc-raw5", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_6].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_6].device_name = + "cbc-raw6", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_7].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_7].device_name = + "cbc-raw7", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_8].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_8].device_name = + "cbc-raw8", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_9].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_9].device_name = + "cbc-raw9", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_10].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_10].device_name = + "cbc-raw10", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_11].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_11].device_name = + "cbc-raw11", + + .channels[CBC_CHANNEL_DEBUG_OUT].device_type = + CBC_DEVICE_TYPE_DEBUG, + .channels[CBC_CHANNEL_DEBUG_OUT].device_name = + "cbc-debug-out", + + .channels[CBC_CHANNEL_DEBUG_IN].device_type = + CBC_DEVICE_TYPE_DEBUG, + .channels[CBC_CHANNEL_DEBUG_IN].device_name = + "cbc-debug-in", +}; + +static struct cbc_mux_channel_configuration cbc_mux_config; + +/* + * priority_show - Retrieve device attribute priority. + * @dev: device (i.e /dev/cbc*) + * @attr: Priority attribute + * @buf: Buffer to write to. + * + * Every channel (entry in /dev) has a priority. + * This can be set/read by a ioctl or in the sysfs. + */ +static ssize_t priority_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cbc_device_data *chn_data = + (struct cbc_device_data *) dev_get_drvdata(dev); + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + int prio = cbc_mux_multiplexer_get_priority(idx); + + pr_debug("cbc-core: read priority %i for channel: %i\n", prio, idx); + return scnprintf(buf, PAGE_SIZE, "%i\n", prio); +} + +/* + * priority_store - Store device attribute priority. + * @dev: device (i.e /dev/cbc*) + * @attr: Priority attribute + * @buf: Buffer to write to. + * + * Every channel (entry in /dev) has a priority. + * This can be set/read by a ioctl or in the sysfs. + */ +static ssize_t priority_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cbc_device_data *chn_data = + (struct cbc_device_data *) dev_get_drvdata(dev); + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + u8 tmp = 0; + int res = kstrtou8(buf, 0, &tmp); + + if ((res == 0) && (tmp < 8)) { + pr_debug("cbc-core: write priority %i to channel %i\n", tmp, + idx); + cbc_mux_multiplexer_set_priority(idx, tmp); + } + return count; +} +static DEVICE_ATTR_RW(priority); + +/* + * cbc_device_open - Open CBC char device. Implementation of .open. + * @inode:Pointer to inode object for this device. + * @file: Pointer to file object for this device. + * + * Return 0 if device opened successfully, Linux error code otherwise. + */ +static int cbc_device_open(struct inode *inode, struct file *file) +{ + struct cbc_device_data *device_data = + &cbc_device_mgr_configuration.channels[MINOR( + inode->i_rdev)]; + int ret = 0; + u32 num_open_files = 0; + struct cbc_file_data *file_data = kmalloc(sizeof(struct cbc_file_data), + GFP_KERNEL); + + if (!device_data) + ret = -EIO; + + if (!file_data) + ret = -ENOMEM; + + if (ret == 0) { + pr_debug("cbc_core: device_open: %d.%d %s\n", + MAJOR(inode->i_rdev), MINOR(inode->i_rdev), + device_data->device_name); + + if (MINOR(inode->i_rdev) >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc-core: invalid cbc channel number.\n"); + ret = -ENODEV; + } + } + + if (ret == 0) { + struct list_head *tmp; + + list_for_each(tmp, &device_data->open_files_head) + num_open_files++; + + if (num_open_files > MAX_OPEN_FILES) + ret = -EBUSY; + } + + if (ret == 0) { + cbc_file_init(file_data); + file_data->cbc_device = device_data; + list_add(&file_data->list, &device_data->open_files_head); + file->private_data = file_data; + } else { + kfree(file_data); + } + + return ret; +} + +/* + * cbc_device_release - Release char device. Implementation of .release + * @inode:Pointer to inode object for this device. + * @file: Pointer to file object for this device. + */ +static int cbc_device_release(struct inode *inode, struct file *file) +{ + u32 dev_idx = MINOR(inode->i_rdev); + struct cbc_file_data *file_data = file->private_data; + + if (file_data) { + list_del(&file_data->list); + + pr_debug("cbc-core: device_release: %d.%d %s\n", + MAJOR(inode->i_rdev), dev_idx, + file_data->cbc_device->device_name); + + while (!cbc_file_queue_empty(file_data)) + cbc_buffer_release(cbc_file_dequeue(file_data)); + + kfree(file_data); + file->private_data = NULL; + } + return 0; +} + +/* + * cbc_device_read - CBC device read. Implementation of .read. + * @file: Pointer to file object for this device. + * @user_buffer: Pointer to buffer containing data to be read. + * @length:Length of buffer. + * @offset: Offset into buffer. + */ +static ssize_t cbc_device_read(struct file *file, char __user *user_buffer, + size_t length, loff_t *offset) +{ + struct cbc_file_data *f = (struct cbc_file_data *) file->private_data; + s32 ret = 0; + + if (!f) + ret = -EIO; + + if (ret == 0) { + while (cbc_file_queue_empty(f) && (ret == 0)) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + return ret; + } + ret = wait_event_interruptible(f->wq_read, + !(cbc_file_queue_empty(f))); + if ((ret != 0) && (ret != -ERESTARTSYS)) { + /* + * ERESTARTSYS happens when a file is polled + * while shutting down the ldisc. + * This is not an error. + */ + pr_err("cbc-core: fifo_read: woke up with error %d.\n", + ret); + ret = -EIO; + } + } + } + + if (ret == 0) { + if (!cbc_file_queue_empty(f)) { + struct cbc_buffer *cbc_buffer; + + cbc_buffer = cbc_file_dequeue(f); + + if (cbc_buffer) { + u32 offset = CBC_HEADER_SIZE; + u16 data_length = cbc_buffer->payload_length; + + if (f->cbc_device->device_type == + CBC_DEVICE_TYPE_RAW) { + offset = CBC_HEADER_SIZE + + CBC_RAWHEADER_SIZE; + data_length = data_length - + CBC_RAWHEADER_SIZE; + } else if (f->cbc_device->device_type == + CBC_DEVICE_TYPE_DEBUG) { + offset = 0; + data_length = cbc_buffer->frame_length; + } + + if (data_length <= length) { + ret = copy_to_user( + (void __user *) user_buffer, + &cbc_buffer->data[offset], + data_length); + if (ret == 0) { + ret = data_length; + } else { + pr_err( + "cbc-core: device_read %u bytes copy to user failed.\n", + data_length); + } + } else { + pr_err( + "cbc-core: device_read, buffer too small for %u bytes.\n", + data_length); + ret = -EINVAL; + } + cbc_buffer_release(cbc_buffer); + } else { + pr_err("cbc-core: dequeued a null-buffer.\n"); + } + + } else { + pr_err("cbc-core: queue empty after response to wait.\n"); + } + + } + return ret; +} + +/* + * cbc_device_write - Write data to char device. Implementation of .write. + * @file: Pointer to file object for this device. + * @user_buffer: Pointer to buffer containing data to be read. + * @length:Length of buffer. + * @offset: Offset into buffer. + */ +static ssize_t cbc_device_write(struct file *file, + const char __user *user_buffer, size_t length, loff_t *offset) +{ + int n = 0; + struct cbc_file_data *file_data = + (struct cbc_file_data *) file->private_data; + struct cbc_device_data *chn_data = file_data->cbc_device; + + struct cbc_buffer *cbc_buffer = cbc_memory_pool_get_buffer( + cbc_device_mgr_configuration.cbc_memory); + int ret = 0; + u32 payload_offset = CBC_HEADER_SIZE; + u32 additional_header_size = 0; + + u32 tmp = (u32) length; + + if (!cbc_buffer) { + pr_err("cbc-core: Out of memory.\n"); + ret = -ENOMEM; + } + + if (ret == 0) { + if (chn_data == NULL) { + pr_err("cbc-core: Channel data is NULL.\n"); + ret = -EINVAL; + } + } + + if (ret == 0) { + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) { + payload_offset = CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE; + additional_header_size = CBC_RAWHEADER_SIZE; + } else if (chn_data->device_type == CBC_DEVICE_TYPE_DEBUG) { + ret = -EINVAL; /* debug channels do not support write */ + } + } + + if (length + payload_offset > CBC_BUFFER_SIZE) { + pr_err( + "cbc-core: Device_write %u bytes not possible,maximum buffer size exceeded.\n", + tmp); + ret = -EINVAL; + } + + if (ret == 0) { + if (user_buffer == NULL) { + pr_err("cbc-core: Device_write buffer is NULL.\n"); + ret = -EINVAL; + } + } + + if (ret == 0) { + if (copy_from_user(&cbc_buffer->data[payload_offset], + (void __user *) user_buffer, length) == 0) { + int idx = chn_data - + &cbc_device_mgr_configuration.channels[0]; + + n = length; + cbc_buffer->payload_length = length + + additional_header_size; + cbc_manager_transmit_buffer(idx, cbc_buffer); + } + } + + cbc_buffer_release(cbc_buffer); + + if (ret == 0) + ret = n; + return ret; +} + +/* + * cbc_mux_configure_data_channel - Configure the specified channel. + * @channel_idx: Channel identifier. + * @data: Data associated with this channel. + * @receive: Data receive function for this channel. + * + * Other kernel modules may wish to use the CBC line discipline. + * They can potentially define their own configurations for the CBC channels. + */ +void cbc_mux_configure_data_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)) +{ + if (channel_idx < CBC_CHANNEL_MAX_NUMBER) { + struct cbc_mux_channel *list = + &cbc_mux_config.cbc_mux_channel_list[channel_idx]; + list->data = data; + list->priority = priority; + list->buffer_receive = NULL; + list->data_receive = receive; + } +} + + +/* + * cbc_register_devices - Register the CBC channels as Linux character devices. + * @cbc_class: CBC device class. + * @memory: CBC memory pool allocated for CBC buffers. + * + * Return: 0 on success or Linux error code. + */ +int cbc_register_devices(struct class *cbc_class, + struct cbc_memory_pool *memory) +{ + int ret = 0; + int i; + dev_t devid; + + struct cbc_device_manager *cbc = &cbc_device_mgr_configuration; + + cbc->cbc_memory = memory; + + /* Set up the devices after the line discipline is opened. */ + if (major) { + devid = MKDEV(major, 0); + ret = register_chrdev_region(devid, CBC_CHANNEL_MAX_NUMBER, + DEVICE_NAME); + } else { + ret = alloc_chrdev_region(&devid, 0, CBC_CHANNEL_MAX_NUMBER, + DEVICE_NAME); + major = MAJOR(devid); + } + + if (ret < 0) + pr_err("cbc-core: ldisc open register chrdev region failed.\n"); + + if (ret == 0) { + cdev_init(&cbc->cdev, &cbc_dev_file_operations); + cbc->cdev.owner = THIS_MODULE; + cbc->cdev.ops = &cbc_dev_file_operations; + ret = cdev_add(&cbc->cdev, MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + if (ret < 0) { + unregister_chrdev_region(MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + pr_err("cbc-core: ldisc open add cdev failed\n"); + } + } + + if (ret == 0) { + for (i = 0; i < CBC_CHANNEL_MAX_NUMBER; i++) { + cbc_device_init(&cbc->channels[i]); + + if (cbc->channels[i].device_type != + CBC_DEVICE_TYPE_HIDDEN) { + /* + * Create the devices. + * These will appear in /sys/class/cbc and + * if udev is running, /dev + */ + cbc->channels[i].device = device_create( + cbc_class, NULL, + MKDEV(major, i), NULL, + cbc->channels[i].device_name, + i); + + /* Add the attribute */ + ret = device_create_file( + cbc->channels[i].device, + &dev_attr_priority); + + /* Set private data to point to the fifo */ + dev_set_drvdata(cbc->channels[i].device, + &cbc->channels[i]); + } else { + cbc->channels[i].device = NULL; + } + } + } + + if (ret != 0) + cbc_unregister_devices(cbc_class); + + return ret; +} + +/* + * cbc_unregister_devices - Remove CBC devices. + * @cbc_class: CBC Device class. + * + * Remove CBC device files and unregisters chrdev region. + */ +void cbc_unregister_devices(struct class *cbc_class) +{ + int i; + struct cbc_device_manager *cbc = &cbc_device_mgr_configuration; + + /* Remove the /dev/cbc* devices */ + cdev_del(&cbc->cdev); + + for (i = 0; i < CBC_CHANNEL_MAX_NUMBER; i++) { + if (cbc->channels[i].device != NULL) { + device_remove_file(cbc->channels[i].device, + &dev_attr_priority); + device_destroy(cbc_class, MKDEV(major, i)); + } + } + + /* + * Also destroys all class attribute files, + * because they are ref. counted. + */ + unregister_chrdev_region(MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + + cbc->cbc_memory = NULL; +} + +/* + * cbc_manager_transmit_data - Transmit data on specified channel. + * @channel_idx: Channel identifier. + * @length: Length of data. + * @buffer: Pointer to data buffer. + * + * If a CBC buffer is available from the memory pool, and the channel + * is valid, the supplied data is copied into a CBC buffer and transmitted. + */ +void cbc_manager_transmit_data(const u32 channel_idx, const u16 length, + const u8 * const buffer) +{ + struct cbc_buffer *cbc_buffer = cbc_memory_pool_get_buffer( + cbc_device_mgr_configuration.cbc_memory); + struct cbc_device_data *chn_data; + u32 offset = CBC_HEADER_SIZE; + u32 copy_length = length; + + if (channel_idx >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc_mux_transmit_data(): Invalid cbc channel idx.\n"); + return; + } + + chn_data = &cbc_device_mgr_configuration.channels[channel_idx]; + + if (!cbc_buffer) + return; + + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) + offset = CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE; + + if (length + offset > CBC_MAX_TOTAL_FRAME_SIZE) + copy_length = CBC_MAX_TOTAL_FRAME_SIZE - offset; + + memcpy(&cbc_buffer->data[offset], buffer, copy_length); + + cbc_manager_transmit_buffer(channel_idx, cbc_buffer); + cbc_buffer_release(cbc_buffer); +} + +/* + * cbc_manager_transmit_buffer - Transmits CBC buffer on specified channel. + * @channel_idx: Channel identifier. + * @buffer: CBC buffer to transmit. + */ +void cbc_manager_transmit_buffer(const u32 channel_idx, + struct cbc_buffer *buffer) +{ + if (channel_idx >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc_mux_transmit_data(): Invalid cbc channel idx.\n"); + } else { + enum cbc_error res = CBC_OK; + struct cbc_device_data *chn_data = + &cbc_device_mgr_configuration.channels[channel_idx]; + + mutex_lock(&cbc_device_mgr_configuration.send_lock); + + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) { + /* + * Room for raw header is already reserved in buffer. + * Calculate raw header data length without raw + * header. + */ + u32 real_payload_size = buffer->payload_length - + CBC_RAWHEADER_SIZE; + + buffer->data[CBC_HEADER_SIZE] = + CBC_RAW_CHANNEL_DIRECT_TRANSPORT; + buffer->data[CBC_HEADER_SIZE + 1] = + (u8) (real_payload_size & 0xFF); + buffer->data[CBC_HEADER_SIZE + 2] = + (u8) ((real_payload_size >> 8) & 0xFFU); + } + + res = cbc_mux_multiplexer_transmit_buffer(channel_idx, buffer); + mutex_unlock(&cbc_device_mgr_configuration.send_lock); + if (res != CBC_OK) + pr_err("Error transmitting frame %u.\n", res); + } + /* Buffer is released in the calling cbc_device_write() */ +} + +/* + * cbc_device_poll - Set up polling based on current status of queue. + * @file: Handle to cbc_device_data. + * @wait: Pointer to poll_table. + * + * Return: Updated poll mask. + */ +static unsigned int cbc_device_poll(struct file *file, poll_table *wait) +{ + struct cbc_file_data *f = (struct cbc_file_data *) file->private_data; + unsigned int mask = 0; + + poll_wait(file, &f->wq_read, wait); + + if (!cbc_file_queue_empty(f)) + mask |= (POLLIN | POLLRDNORM); + + if (!(f->queue.read + CBC_QUEUE_LENGTH == f->queue.write)) + mask |= (POLLOUT | POLLWRNORM); + + return mask; +} + +/* + * cbc_device_ioctl - Handle CBC channel ioctl call. + * @file: Handle to cbc_device_data. + * @cmd: ioctl command. + * @arg: argument associated with the command. + * + * The flag field and priority can get get/set for a CBC device (channel). + * + * Return: 0 if command successfully handled, Linux error otherwise. + */ +static long cbc_device_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int tmp; + struct cbc_file_data *file_data = + (struct cbc_file_data *) file->private_data; + struct cbc_device_data *chn_data = file_data->cbc_device; + + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + + switch (cmd) { + case CBC_PRIORITY_GET: + tmp = cbc_mux_multiplexer_get_priority(idx); + if (copy_to_user((void __user *) arg, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; + + case CBC_PRIORITY_SET: + if (copy_from_user(&tmp, (void __user *) arg, sizeof(tmp))) + return -EFAULT; + cbc_mux_multiplexer_set_priority(idx, tmp); + return 0; + + default: + return -EINVAL; + } + +} + +/* + * get_default_priority - Get default priority for specified channel. + * @channel_id: channel identifier. + * + * Return: Priority for specified channel. + */ +static u8 get_default_priority(enum cbc_channel_enumeration channel_id) +{ + u8 result = 1; + + switch (channel_id) { + case CBC_CHANNEL_PMT: + case CBC_CHANNEL_LIFECYCLE: + case CBC_CHANNEL_DLT: + case CBC_CHANNEL_LINDA: + result = 6; + break; + + case CBC_CHANNEL_DIAGNOSIS: + result = 2; + break; + + default: + result = 3; + break; + } + return result; +} + +/* + * demuxed_receive - Handle a CBC buffer received over UART. + * @void_data: CBC device data. + * @cbc_buffer: CBC buffer received over UART. + * + * Checks if there is valid data. Determines the frame type and + * adds to buffer queue. + */ +static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) +{ + struct cbc_device_data *device_data = + (struct cbc_device_data *) void_data; + struct list_head *current_item; + struct cbc_file_data *current_file_data; + + if (device_data && cbc_buffer + && cbc_buffer->frame_length > + CBC_HEADER_SIZE + CBC_CHECKSUM_SIZE) { + /* Payload_length includes raw_header */ + u16 payload_length = cbc_buffer->frame_length - + (CBC_HEADER_SIZE + CBC_CHECKSUM_SIZE); + + if (device_data->device_type == CBC_DEVICE_TYPE_RAW) { + if (cbc_buffer->frame_length > + (CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE + + CBC_CHECKSUM_SIZE)) { + u16 raw_length; + + raw_length = cbc_buffer->data[4]; + raw_length |= (cbc_buffer->data[5] << 8); + + if (raw_length + CBC_RAWHEADER_SIZE > + payload_length) { + pr_err( + "raw length (%i) is longer than payload length (%i)\n", + raw_length, payload_length); + /* Payload_length already set + * to max value + */ + } else { + payload_length = raw_length + + CBC_RAWHEADER_SIZE; + } + } else { + pr_err("cbc-core: Frame to short for a raw frame\n"); + } + cbc_buffer->payload_length = payload_length; + } else if (device_data->device_type == + CBC_DEVICE_TYPE_DEFAULT) { + cbc_buffer->payload_length = payload_length; + } + /* else, do not touch payload_length in a debug-channel */ + + /* Enqueue */ + for (current_item = device_data->open_files_head.next + ; current_item != &device_data->open_files_head; current_item = + current_item->next) { + + current_file_data = list_entry(current_item, + struct cbc_file_data, list); + /* File_enqueue increases ref. count. */ + cbc_file_enqueue(current_file_data, cbc_buffer); + } + } else { + pr_err("cbc-core: (<- IOC) dev_receive data is null\n"); + } +} + +/* + * cbc_kmod_devices_init - Configure CBC multiplexer. + * + * Configures multiplexer channel list and configures the multiplexer using + * this list. Initialises mutex lock for multiplexer. + */ +void cbc_kmod_devices_init(void) +{ + /* + * Set up the multiplexer channel list and use it to configure the + * multiplexer. + */ + u32 i = 0; + + for (; i < CBC_CHANNEL_MAX_NUMBER; i++) { + cbc_mux_config.cbc_mux_channel_list[i].buffer_receive = + demuxed_receive; + cbc_mux_config.cbc_mux_channel_list[i].data_receive = NULL; + cbc_mux_config.cbc_mux_channel_list[i].data = + &cbc_device_mgr_configuration.channels[i]; + cbc_mux_config.cbc_mux_channel_list[i].priority = + get_default_priority(i); + } + + cbc_mux_multiplexer_setup(&cbc_mux_config); + mutex_init(&cbc_device_mgr_configuration.send_lock); +} diff --git a/drivers/tty/cbc/cbc_device_manager.h b/drivers/tty/cbc/cbc_device_manager.h new file mode 100644 index 000000000000..a6f9b45da1aa --- /dev/null +++ b/drivers/tty/cbc/cbc_device_manager.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_DEVICE_MANAGER_H +#define CBC_DEVICE_MANAGER_H + +#include "cbc_memory.h" + +#include +#include + +void cbc_kmod_devices_init(void); + +int cbc_register_devices(struct class *cbc_class, + struct cbc_memory_pool *memory); + +void cbc_unregister_devices(struct class *cbc_class); + +/* + * cbc_mux_configure_data_channel - Configure channels + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for this channel + * @data: Channel data + * @receive: Receive data function associated with this channel. + * Channels can only be configured after cbc_kmod_devices_init(). + * This will overwrite the settings for the devices. + * The device will be created anyway, to allow the cbc_socket_server to + * work without a requirement for handling missing devices. + */ +void cbc_mux_configure_data_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)); + +/* + * cbc_manager_transmit_data - Transmit data to IOC. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @length: Length of data + * @buffer: The data + * + * This is the version provided as a kernel symbol. + */ +void cbc_manager_transmit_data(const u32 channel_idx, const u16 length, + const u8 * const buffer); + +/* + * cbc_manager_transmit_data - Transmit data to IOC. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @buffer: The data + * + * This is the internal version, without memcpy. + */ +void cbc_manager_transmit_buffer(const u32 channel_idx, + struct cbc_buffer *buffer); + +#endif /* CBC_DEVICE_MANAGER_H */ diff --git a/drivers/tty/cbc/cbc_link_checksum.c b/drivers/tty/cbc/cbc_link_checksum.c new file mode 100644 index 000000000000..69f0ca74c3c8 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_checksum.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "cbc_types.h" +#include "cbc_link_checksum.h" + +enum cbc_error cbc_checksum_calculate(u8 length, + u8 const * const payload_data, u8 *checksum) +{ + u8 result = 0; /* Holds result of calculation */ + u8 counter = 0; + + /* Parameter validation */ + if (length == 0U) + return CBC_ERROR_PARAMETER_INCORRECT; + + if ((payload_data == NULL) || (checksum == NULL)) + return CBC_ERROR_NULL_POINTER_SUPPLIED; + + /* Perform calculation */ + do { + result += (u8) ((0x100 - *(payload_data + counter++)) & 0xFFU); + } while (counter != length); + + *checksum = result; + + return CBC_OK; +} + +enum cbc_error cbc_checksum_check(u8 length, u8 const * const payload_data, + u8 checksum, u8 *expected_checksum) +{ + u8 calculated_checksum = 0U; + enum cbc_error result = CBC_OK; + + enum cbc_error calc_result = cbc_checksum_calculate(length, + payload_data, &calculated_checksum); + + if ((calc_result == CBC_OK) && + (checksum == calculated_checksum)) + result = CBC_OK; + else + result = CBC_ERROR_CHECKSUM_MISMATCH; + + if (expected_checksum != NULL) + *expected_checksum = calculated_checksum; + + return result; +} diff --git a/drivers/tty/cbc/cbc_link_checksum.h b/drivers/tty/cbc/cbc_link_checksum.h new file mode 100644 index 000000000000..9f8f50f2cbd7 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_checksum.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _CBC_LINK_CHECKSUM_H_ +#define _CBC_LINK_CHECKSUM_H_ + +#include "cbc_types.h" + +/* + * cbc_checksum_calculate - Calculate checksum. + * @length: data length + * @payload_data:The data buffer. + * @checksum: Pointer to checksum. + * + * Based on summation of inverted individual byte values. + * + * Return: cbc_error if checksum cannot be generated. + */ + +enum cbc_error cbc_checksum_calculate(u8 length, + u8 const * const payload_data, u8 *checksum); + +/* + * Check checksum is valid for current data. + * @length: data length + * @payload_data: The data buffer. + * @checksum: Checksum value to check + * @expected_checksum: Expected checksum. + * + * Return: cbc_error if checksum is invalid. + */ +enum cbc_error cbc_checksum_check(u8 length, u8 const * const payload_data, + u8 checksum, u8 *expected_checksum); + +#endif /*_CBC_LINK_CHECKSUM_H_ */ diff --git a/drivers/tty/cbc/cbc_link_layer.c b/drivers/tty/cbc/cbc_link_layer.c new file mode 100644 index 000000000000..a55cd8ce7add --- /dev/null +++ b/drivers/tty/cbc/cbc_link_layer.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include "cbc_core_public.h" +#include "cbc_link_checksum.h" +#include "cbc_link_layer.h" +#include "cbc_memory.h" +#include "cbc_mux_multiplexer.h" + +#define CBC_MAX_RING_BUFFER_SIZE 256 + +/* + * struct cbc_queue_control - Structure holding the queue control data. + * @next_element: Next element in received data buffer. + * @current_element: Current element in received data buffer. + * + * Handles the current (and next) position in the received data circular buffer. + */ +struct cbc_queue_control { + u8 next_element; + u8 current_element; +}; + +static struct cbc_memory_pool *memory_pool; + +static u8 rx_cvh_ring_message[CBC_MAX_RING_BUFFER_SIZE]; +static struct cbc_queue_control cvh_rx_queue_control; +static u8 number_of_bytes_expected; +static u8 number_of_bytes_skipped; +static u8 ignore_all_skipped_bytes = 1; +static u8 last_rx_frame_valid = 1; +static u8 rx_sequence_counter; + +/* Transmitted data queue. */ +static struct cbc_buffer_queue tx_queue; +static u8 tx_sequence_counter; /* Sequence counter value for next tx frame */ +static u8 cbc_frame_granularity; +static struct mutex transmit_frame_mutex; + +static void cbc_link_layer_transmit_frame(void); +static void cbc_link_release_rx_data(u8 bytes_to_free); + +/* + * calculate_total_frame_length - Calculates the total length of a frame. + * @buffer: Pointer to CBC buffer. + * + * Used for outgoing frames. Calculates the total length of a frame + * depending on its payload_length. Total length is stored in + * cbc_buffer->frame_length. + */ +static void calculate_total_frame_length(struct cbc_buffer *buffer); + +/* + * cbc_link_release_rx_data - Release the specified number of bytes from the + * internal rx buffer. + * @bytes_to_free: Number of bytes to be released. + */ +static void cbc_link_release_rx_data(u8 bytes_to_free) +{ + cvh_rx_queue_control.current_element += bytes_to_free; +} + +static void calculate_total_frame_length(struct cbc_buffer *buffer) +{ + u8 frame_length_in_bytes; + + if (!buffer) + return; + + frame_length_in_bytes = buffer->payload_length + CBC_HEADER_SIZE + + CBC_CHECKSUM_SIZE; + + /* Adjust frame_length to granularity */ + if ((frame_length_in_bytes % cbc_frame_granularity) != 0) + frame_length_in_bytes += cbc_frame_granularity - + (frame_length_in_bytes % + cbc_frame_granularity); + + buffer->frame_length = frame_length_in_bytes; +} + +/* + * cbc_link_layer_transmit_frame - Transmits a frame over a UART. + */ +static void cbc_link_layer_transmit_frame(void) +{ + u8 total_len; + u8 checksum = 0U; + s32 mutex_lock_result = -1; + u32 frame_transmission_counter = CBC_MAX_FRAME_TRANSMISSION_NUMBER; + struct cbc_buffer *buffer = NULL; + + mutex_lock_result = mutex_lock_interruptible(&transmit_frame_mutex); + if (mutex_lock_result != 0) { + pr_err("cbc-core: Could not lock the transmit_frame_mutex\n"); + return; + } + + /* If queue is not empty. */ + if (tx_queue.read != tx_queue.write) + buffer = cbc_buffer_queue_dequeue(&tx_queue); + + while (buffer && frame_transmission_counter) { + total_len = buffer->frame_length; + frame_transmission_counter--; + + /* Reset sequence counter bits first */ + buffer->data[1U] &= ~CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Qdd sequence counter */ + buffer->data[1U] |= tx_sequence_counter; + + /* + * Qdd checksum, subtract 1, as the checksum field + * itself cannot be included in calculation + */ + cbc_checksum_calculate(total_len - 1U, buffer->data, + &checksum); + buffer->data[total_len - 1U] = checksum; + + /* Try to send the frame */ + if (target_specific_send_cbc_uart_data(total_len, + buffer->data) != CBC_OK) { + /* Not sent, release anyway */ + pr_debug("cbc-core: Could not send packet.\n"); + + } else { + /* + * Data was transmitted, so increase the + * sequence counter for next frame + */ + tx_sequence_counter = (tx_sequence_counter + 1U) + & CBC_SEQUENCE_COUNTER_WIDTH_MASK; + } /* else */ + cbc_buffer_release(buffer); + buffer = NULL; + + /* If queue is not empty. */ + if (tx_queue.read != tx_queue.write) + buffer = cbc_buffer_queue_dequeue(&tx_queue); + } + + mutex_unlock(&transmit_frame_mutex); +} + +/* + * cbc_link_layer_get_stored_serial_data - Get stored serial data. + * @out_buf:Pointer to buffer populated with serial data. + * @max_length: Maximum amount of data to be retrieved. + * + * Populates out_buf and returns number of bytes read. Stop reading if the + * maximum length is reached. + * + * Return: The amount of data read. + */ +static u8 cbc_link_layer_get_stored_serial_data(u8 *out_buf, + u8 const max_length) +{ + u8 index8 = 0; + u8 curr = cvh_rx_queue_control.current_element; + u8 next = cvh_rx_queue_control.next_element; + + while (((curr + index8) & 0xFFU) != next) { + out_buf[index8] = rx_cvh_ring_message[(curr + index8) & 0xFFU]; + index8++; + /* Avoid memory overflow of target array */ + if (index8 >= max_length) + break; + } + return index8; +} + +/* + * cbc_link_layer_set_frame_granularity - Set the CBC frame granularity. + * @granularity: Supported values are 4, 8, 16 and 32 bytes. + * + * Return: CBC error, OK or incorrect parameter (if invalid granularity + * supplied). + */ +enum cbc_error cbc_link_layer_set_frame_granularity(u8 granularity) +{ + if ((granularity == 4) || (granularity == 8) || (granularity == 16) || + (granularity == 32U)) { + cbc_frame_granularity = granularity; + return CBC_OK; + } else { + return CBC_ERROR_PARAMETER_INCORRECT; + } /* else */ +} + +/* + * cbc_link_layer_init - Initialize link layer. + * + * This function shall be called once during startup. + * It shall be the first function of this file to be called. + */ +void cbc_link_layer_init(struct cbc_memory_pool *memory) +{ + cvh_rx_queue_control.next_element = 0; + cvh_rx_queue_control.current_element = 0; + + memory_pool = memory; + + number_of_bytes_expected = 0; + number_of_bytes_skipped = 0; + ignore_all_skipped_bytes = 1; + last_rx_frame_valid = 1; + rx_sequence_counter = 0; + + cbc_buffer_queue_init(&tx_queue); + tx_sequence_counter = 0; + + cbc_frame_granularity = 4; + + mutex_init(&transmit_frame_mutex); +} + +/* + * cbc_core_on_receive_cbc_serial_data - Called on reception of data on UART. + * @length: Maximum size of data to retrieve in one go. + * @rx_buf: Pointer to buffer to populate. + * + * This function is called on reception of serial data. It extracts single CBC + * frames from the received data. If incomplete frames are received, it waits + * for more data. Buffers are added to a circular buffer rx_cvh_ring_message. + * + * Return number of bytes retrieved. + */ +u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf) +{ + u8 number_of_bytes_accepted = 0; + u8 next_try_element = 0; + + while (length != 0) { + next_try_element = + (u8) ((cvh_rx_queue_control.next_element + 1U) + % CBC_MAX_RING_BUFFER_SIZE); + if (next_try_element != cvh_rx_queue_control.current_element) { + rx_cvh_ring_message[cvh_rx_queue_control.next_element] = + *rx_buf; + + cvh_rx_queue_control.next_element = next_try_element; + rx_buf++; /* next byte */ + length--; + number_of_bytes_accepted++; + } else { + /* Buffer is full, do not store additional bytes */ + return number_of_bytes_accepted; + } /* else */ + } /* while */ + return number_of_bytes_accepted; +} + +static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) +{ + u8 expected_checksum = 0U; + u8 checksum = 0U; + + checksum = rx_cvh_frame[frame_length - 1U]; + /* Check checksum is valid. */ + if (cbc_checksum_check(frame_length - 1U, + rx_cvh_frame, checksum, + &expected_checksum) + != CBC_OK) { + pr_err("cbc-core: Received CBC frame contains an invalid checksum\n"); + pr_err("cbc-core: found 0x%x expected 0x%x. frame discarded (length: %i), try to realign.\n", + checksum, + expected_checksum, + frame_length); + cbc_link_release_rx_data(1U); + number_of_bytes_skipped = 1; + last_rx_frame_valid = 0; + } else { + /* check the sequence counter */ + if ((rx_cvh_frame[1] + & CBC_SEQUENCE_COUNTER_WIDTH_MASK) + != rx_sequence_counter) { + pr_err("cbc-core: Found unexpected Rx sequence counter %i, expected %i\n", + rx_cvh_frame[1] + & 0x3, + rx_sequence_counter); + + /* + * Reset the sequence counter + * to the received value. + * + */ + rx_sequence_counter = + rx_cvh_frame[1] + & CBC_SEQUENCE_COUNTER_WIDTH_MASK; + } + } + +} + +/* + * cbc_link_layer_rx_handler - Process data received on UART. + * + * Processes received serial data and parses for complete frames + */ +void cbc_link_layer_rx_handler(void) +{ + u8 bytes_avail = 0U; + u8 service_layer_frame_length = 0U; + u8 frame_length = 0U; + + struct cbc_buffer *buffer; + u8 *rx_cvh_frame; + + buffer = cbc_memory_pool_get_buffer(memory_pool); + + if (!buffer) { + pr_err("cbc-core: Out of memory.\n"); + return; + } + + bytes_avail = cbc_link_layer_get_stored_serial_data( + buffer->data, CBC_BUFFER_SIZE); + rx_cvh_frame = buffer->data; + + /* Wait for at least one frame (minimum size) */ + while ((bytes_avail >= 8U) && + (bytes_avail >= number_of_bytes_expected)) { + /* Check for start of frame */ + if (rx_cvh_frame[0] == CBC_SOF) { + /* Log skipped bytes if necessary */ + if (number_of_bytes_skipped > 0U) { + if (ignore_all_skipped_bytes == 0U) + pr_err("Skipped %d bytes.\n", + number_of_bytes_skipped); + + number_of_bytes_skipped = 0U; + ignore_all_skipped_bytes = 1U; + } + + service_layer_frame_length = (rx_cvh_frame[1] >> + CBC_FRAME_LENGTH_SHIFT) & + CBC_FRAME_LENGTH_WIDTH_MASK; + + frame_length = (service_layer_frame_length + 2U) * 4U; + + if (frame_length > CBC_MAX_TOTAL_FRAME_SIZE) { + pr_err("cbc: Received frame has illegal length (%u bytes).Frame discarded, try to realign.\n", + frame_length); + cbc_link_release_rx_data(1U); + number_of_bytes_skipped = 1U; + last_rx_frame_valid = 0U; + } else if (bytes_avail >= frame_length) { + /* ok */ + _cbc_link_layer_checksum(rx_cvh_frame, + frame_length); + + /* Increment seq. counter. */ + rx_sequence_counter++; + rx_sequence_counter &= + CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Forward frame to Mux. layer. */ + buffer->frame_length = frame_length; + cbc_mux_multiplexer_process_rx_buffer( + buffer); + cbc_link_release_rx_data(frame_length); + + last_rx_frame_valid = 1; + /* else */ + number_of_bytes_expected = 0; + } else { + /* + * Wait for missing bytes to arrive, + * leave and try again. + */ + number_of_bytes_expected = frame_length; + } /* else */ + } else { + if (!(last_rx_frame_valid && (rx_cvh_frame[0] == + CBC_INTER_FRAME_FILL_BYTE))) + ignore_all_skipped_bytes = 0; + + /* + * No alignment found, + * skip current byte and try next one. + */ + cbc_link_release_rx_data(1U); + number_of_bytes_expected = 0; + ++number_of_bytes_skipped; + } /* else */ + + /* Process rx_buffer increases ref count, + * so always release here. + */ + cbc_buffer_release(buffer); + buffer = cbc_memory_pool_get_buffer(memory_pool); + + if (!buffer) { + pr_err("cbc-core: Out of memory.\n"); + rx_cvh_frame = NULL; + return; + } + bytes_avail = + cbc_link_layer_get_stored_serial_data( + buffer->data, CBC_BUFFER_SIZE); + rx_cvh_frame = buffer->data; + } /* while */ + + cbc_buffer_release(buffer); + +} + +/* + * cbc_link_layer_tx_handler - Triggers pending data transmission. + */ +enum cbc_error cbc_link_layer_tx_handler(void) +{ + cbc_link_layer_transmit_frame(); + + return CBC_OK; +} + +/* + * cbc_link_layer_assemble_buffer_for_transmission - Add frame to queue for + * transmission. + * @mux: CBC channel frame is associated with. + * @priority: Priority for frame. + * @buffer: Frame data. + * + * Generate a CBC frame from supplied data. + * Fills in CBC header details (adds start of frame identifier, frame length + * and channel. Also adds padding. Buffer is added to queue and transmission + * is triggered. + * + * Return CBC error code (CBC_OK if frame assembled successfully). + */ +enum cbc_error cbc_link_layer_assemble_buffer_for_transmission(u8 mux, + u8 priority, struct cbc_buffer *buffer) +{ + u8 frame_length_in_bytes; + enum cbc_error result = CBC_OK; + u32 i; + + if (!buffer) + return CBC_ERROR_NULL_POINTER_SUPPLIED; + + calculate_total_frame_length(buffer); + frame_length_in_bytes = buffer->frame_length; + + /* Fill in padding */ + for (i = buffer->payload_length + CBC_HEADER_SIZE; + i < frame_length_in_bytes; i++) + buffer->data[i] = 0xFF; + + /* Fill in cbc header. */ + buffer->data[0] = CBC_SOF; /* set start of frame byte */ + buffer->data[1] = ((((frame_length_in_bytes - 4U - 1U) / 4U) & + CBC_FRAME_LENGTH_WIDTH_MASK) << + CBC_FRAME_LENGTH_SHIFT); + buffer->data[2] = ((mux & CBC_MULTIPLEXER_WIDTH_MASK) << + CBC_MULTIPLEXER_SHIFT) | + (priority & CBC_PRIORITY_WIDTH_MASK); + + /* + * If transmission is done in a different thread, + * check for queue full first. + */ + cbc_buffer_queue_enqueue(&tx_queue, buffer); + cbc_buffer_increment_ref(buffer); + + /* Trigger transmission */ + cbc_link_layer_transmit_frame(); + + return result; +} diff --git a/drivers/tty/cbc/cbc_link_layer.h b/drivers/tty/cbc/cbc_link_layer.h new file mode 100644 index 000000000000..ba70a99b04e6 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_layer.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_LINK_LAYER_H_ +#define CBC_LINK_LAYER_H_ + +#include "cbc_types.h" +#include "cbc_memory.h" + +void cbc_link_layer_init(struct cbc_memory_pool *memory); + +enum cbc_error cbc_link_layer_set_frame_granularity(u8 granularity); + +enum cbc_error cbc_link_layer_tx_handler(void); + +void cbc_link_layer_rx_handler(void); + +u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf); + +enum cbc_error cbc_link_layer_assemble_frame_for_transmission(u8 mux, + u8 priority, u8 service_frame_length, + u8 const * const raw_buffer); + +enum cbc_error cbc_link_layer_assemble_buffer_for_transmission(u8 mux, + u8 priority, struct cbc_buffer *buffer); + +#endif /*CBC_LINK_LAYER_H_ */ + diff --git a/drivers/tty/cbc/cbc_memory.c b/drivers/tty/cbc/cbc_memory.c new file mode 100644 index 000000000000..69cf65e8901a --- /dev/null +++ b/drivers/tty/cbc/cbc_memory.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include + +#include "cbc_memory.h" + +bool cbc_memory_pool_try_free(struct cbc_memory_pool *pool) +{ + u32 i = 0; + bool allfree = true; + int tmp; + + if (!pool) + return 0; + + mutex_lock(&pool->lock); + + for (i = 0; i < (pool->num_blocks); i++) { + tmp = atomic_read(&pool->pool[i].refcount); + if (tmp > 0) { + pr_err("Buffer %i was not freed. (%i refs)\n", + i, tmp); + allfree = false; + } + } + + mutex_unlock(&pool->lock); + + if (allfree) + kfree(pool); + + return allfree; +} + +struct cbc_memory_pool *cbc_memory_pool_create(const u16 num_blocks) +{ + size_t size; + struct cbc_memory_pool *new_pool; + u32 i = 0; + + /* Check we have a valid queue length before we go any further. */ + BUILD_BUG_ON(CBC_QUEUE_LENGTH & (CBC_QUEUE_LENGTH - 1)); + + size = sizeof(struct cbc_memory_pool) + + (sizeof(struct cbc_buffer) * num_blocks); + + new_pool = kmalloc(size, GFP_KERNEL); + new_pool->num_blocks = num_blocks; + mutex_init(&new_pool->lock); + + for (i = 0; i < num_blocks; i++) + atomic_set(&new_pool->pool[i].refcount, 0); + + return new_pool; +} + +struct cbc_buffer *cbc_memory_pool_get_buffer(struct cbc_memory_pool *pool) +{ + u32 i = 0; + struct cbc_buffer *buffer = NULL; + int tmp; + + if (pool) { + mutex_lock(&pool->lock); + + for (; i < pool->num_blocks; i++) { + tmp = atomic_read(&pool->pool[i].refcount); + if (tmp == 0) { + atomic_inc_and_test(&pool->pool[i].refcount); + buffer = &pool->pool[i]; + buffer->payload_length = 0; + buffer->frame_length = 0; + break; + } + } + mutex_unlock(&pool->lock); + } + return buffer; +} + +void cbc_buffer_release(struct cbc_buffer *buffer) +{ + int tmp; + + if (!buffer) + return; + + atomic_read(&buffer->refcount); + + tmp = atomic_dec_return(&buffer->refcount); + if (tmp == 0) + memset(buffer->data, 0xCD, CBC_BUFFER_SIZE); + +} + +void cbc_buffer_increment_ref(struct cbc_buffer *buffer) +{ + if (buffer) + atomic_inc(&buffer->refcount); +} + +void cbc_buffer_queue_init(struct cbc_buffer_queue *queue) +{ + queue->write = 0; + queue->read = 0; +} + +int cbc_buffer_queue_enqueue(struct cbc_buffer_queue *queue, + struct cbc_buffer *buffer) +{ + if (!queue || !buffer) + return 0; + + if (queue->read + CBC_QUEUE_LENGTH == queue->write) { + pr_err("cbc buffer queue full\n"); + return 0; + } + + queue->queue[queue->write & CBC_QUEUE_BM] = buffer; + queue->write++; + return 1; +} + +struct cbc_buffer *cbc_buffer_queue_dequeue(struct cbc_buffer_queue *queue) +{ + struct cbc_buffer *buffer = NULL; + + if (!queue) + return buffer; + + if (queue->read == queue->write) { + pr_err("cbc buffer queue: dequeue while empty.\n"); + return buffer; + } + + buffer = queue->queue[queue->read & CBC_QUEUE_BM]; + queue->queue[queue->read & CBC_QUEUE_BM] = NULL; + queue->read++; + + return buffer; +} + diff --git a/drivers/tty/cbc/cbc_memory.h b/drivers/tty/cbc/cbc_memory.h new file mode 100644 index 000000000000..d6e34fcd48ca --- /dev/null +++ b/drivers/tty/cbc/cbc_memory.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_MEMORY_H_ +#define CBC_MEMORY_H_ + +#include +#include +#include + +#include "cbc_types.h" + +#define CBC_BUFFER_SIZE CBC_MAX_TOTAL_FRAME_SIZE + +/* + * struct cbc_buffer - Represents a single CBC frame buffer. + * @frame_length: Total length including headers and checksum. + * @payload_length: Length of payload without fill-bytes, including raw- + * header if present. + * @refcount: Reference count incremented/decremented when queuing + * de-queueing the buffer. + * @data: Contents of buffer. + */ +struct cbc_buffer { + u16 frame_length; + u16 payload_length; + atomic_t refcount; + u8 data[CBC_BUFFER_SIZE]; +}; + +/* + * struct cbc_memory_pool - Memory pool for cbc_buffer. + * @num_blocks: Number of blocks allocated (CBC queue length * maximum number of + * channels. + * lock: Mutex to lock memory operations. + * pool: The actual pool of CBC buffers. + * + * The cbc_memory_pool holds a number of cbc_buffers with reference counting. + */ +struct cbc_memory_pool { + u16 num_blocks; + struct mutex lock; + struct cbc_buffer pool[0]; +}; + +/* CBC queue length has to be a power of 2 */ +#define CBC_QUEUE_LENGTH 16 +#define CBC_QUEUE_BM (CBC_QUEUE_LENGTH - 1) + +/* + * cbc_buffer_queue - Circular buffer for cbc_buffer pointers. + * @queue: The queue of CBC buffer pointers. + * @write: Head of queue. + * @read: Tail of queue. + * + * Reference count handling is not done by this queue. + */ +struct cbc_buffer_queue { + struct cbc_buffer *queue[CBC_QUEUE_LENGTH]; + u8 write; + u8 read; +}; + +/* cbc_memory_pool_create - Create memory pool of CBC buffers. + * @num_blocks: Size of memory pool to create (based on queue size and number + * maximum of channels). + * + * Use kmalloc to create a new cbc_memory_pool with given number of cbc_buffers. + */ +struct cbc_memory_pool *cbc_memory_pool_create(const u16 num_blocks); + +/* + * cbc_memory_pool_try_free - Frees the pool if no buffer is in use. + * @pool: Pointer to memory pool. + * + * Ensure no new buffers are requested while calling this. + * + * Return: True if pool has been freed, false if not. + */ +bool cbc_memory_pool_try_free(struct cbc_memory_pool *pool); + +/* + * cbc_memory_pool_get_buffer - Returns a free CBC buffer if available. + * @pool: Pointer to memory pool. + * + * Return: Pointer to buffer is one is available, null otherwise. + */ +struct cbc_buffer *cbc_memory_pool_get_buffer(struct cbc_memory_pool *pool); + +/* + * cbc_buffer_release - Release CBC buffer (if not is use elsewhere). + * @buffer: Buffer to release. + * + * Decreases the reference count. A reference count of 0 marks this buffer as + * free. + */ +void cbc_buffer_release(struct cbc_buffer *buffer); + +/* + * cbc_buffer_increment_ref - Increases the reference count for a CBC buffer. + * @buffer: Buffer to increment ref count for. + */ +void cbc_buffer_increment_ref(struct cbc_buffer *buffer); + +/* + * cbc_buffer_queue_init - Initializes a cbc_buffer_queue. + * @queue: CBC buffer queue to initialise. + * + * Initialises head and tail. + */ +void cbc_buffer_queue_init(struct cbc_buffer_queue *queue); + +/* + * cbc_buffer_queue_enqueue - Add CBC buffer to a queue. + * @queue: CBC buffer queue. + * @buffer: Buffer to add. + * + * Enqueues a buffer into the queue. If the queue is full, + * the buffer will not be enqueued without any error. + * does not do reference count handling. + */ +int cbc_buffer_queue_enqueue(struct cbc_buffer_queue *queue, + struct cbc_buffer *buffer); + +/* + * cbc_buffer_queue_dequeue - Remove buffer from queue if not in use + * elsewhere. + * @queue: CBC buffer queue + * @buffer: Buffer to dequeue + * + * Dequeues a buffer. If queue is empty, null is returned. + * Does not do reference count handling + */ +struct cbc_buffer *cbc_buffer_queue_dequeue(struct cbc_buffer_queue *queue); + +#endif /* CBC_DEVICE_H_ */ diff --git a/drivers/tty/cbc/cbc_mux_multiplexer.c b/drivers/tty/cbc/cbc_mux_multiplexer.c new file mode 100644 index 000000000000..4439e34f5142 --- /dev/null +++ b/drivers/tty/cbc/cbc_mux_multiplexer.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "cbc_link_layer.h" +#include "cbc_mux_multiplexer.h" + +static struct cbc_mux_channel_configuration *cbc_mux_configuration; + +void cbc_mux_multiplexer_setup(struct cbc_mux_channel_configuration *config) +{ + cbc_mux_configuration = config; +} + +/* + * cbc_mux_multiplexer_process_rx_buffer - Processes a received buffer. + * @cbc_buffer: CBC buffer to process. + * + * If the frame is valid, and the channel is valid, the contents of the CBC + * buffer are passed to the channel's receive function. + */ +void cbc_mux_multiplexer_process_rx_buffer(struct cbc_buffer *cbc_buffer) +{ + u8 mux_idx = 0U; + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (!cbc_buffer || cbc_buffer->frame_length < CBC_HEADER_SIZE) + return; + + mux_idx = (u8) ((cbc_buffer->data[2] >> CBC_MULTIPLEXER_SHIFT) + & CBC_MULTIPLEXER_WIDTH_MASK) & 0xFFU; + + if (config) { + struct cbc_mux_channel *channel; + + channel = &config->cbc_mux_channel_list[mux_idx]; + + if (channel) { + if (channel->buffer_receive) { + channel->buffer_receive(channel->data, + cbc_buffer); + } else if (channel->data_receive) { + channel->data_receive(channel->data, + cbc_buffer->payload_length, + &cbc_buffer->data[CBC_HEADER_SIZE]); + cbc_buffer_release(cbc_buffer); + } + } + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_IN]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + } +} + +/* + * cbc_mux_multiplexer_transmit_buffer - Send a buffer. + * @channel_idx: Channel identifier. + * @buffer: CBC buffer to transmit. + * + * Assembles CBC buffer for transmission. + * + * Return: CBC Error. + */ +enum cbc_error cbc_mux_multiplexer_transmit_buffer( + enum cbc_channel_enumeration channel_idx, + struct cbc_buffer *cbc_buffer) +{ + enum cbc_error result = CBC_OK; + struct cbc_mux_channel *channel; + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + + /* + * Transmit will release the buffer, make sure a reference is held. + * until it is enqueued in the debug device. + */ + cbc_buffer_increment_ref(cbc_buffer); + + if (config) { + cbc_link_layer_assemble_buffer_for_transmission( + (u8) channel_idx, + config->cbc_mux_channel_list[channel_idx].priority, + cbc_buffer); + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_OUT]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + } + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_OUT]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + + cbc_buffer_release(cbc_buffer); + + return result; +} + +/* + * cbc_mux_multiplexer_set_priority - Set priority for specified channel. + * multiplexer. + * @channel_num: Channel ID. + * @new_priority: UNew priority value. + */ + +void cbc_mux_multiplexer_set_priority(u32 channel_num, u8 new_priority) +{ + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (config && (channel_num < CBC_CHANNEL_MAX_NUMBER)) + config->cbc_mux_channel_list[channel_num].priority = + new_priority; +} + +/* + * cbc_mux_multiplexer_get_priority - Get priority for specified channel. + * @channel_num: Channel ID. + * + * Return: Priority for this channel. + */ +u8 cbc_mux_multiplexer_get_priority(u32 channel_num) +{ + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (config && (channel_num < CBC_CHANNEL_MAX_NUMBER)) + return config->cbc_mux_channel_list[channel_num].priority; + return 0; +} diff --git a/drivers/tty/cbc/cbc_mux_multiplexer.h b/drivers/tty/cbc/cbc_mux_multiplexer.h new file mode 100644 index 000000000000..aa24777d6a13 --- /dev/null +++ b/drivers/tty/cbc/cbc_mux_multiplexer.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_MUX_MULTIPLEXER_H_ +#define CBC_MUX_MULTIPLEXER_H_ + +#include "cbc_types.h" +#include "cbc_memory.h" + +struct cbc_mux_channel { + void (*buffer_receive)(void *data, struct cbc_buffer *cbc_buffer); + void (*data_receive)(void *data, const u16 length, + const u8 * const buffer); + void *data; + u8 priority; +}; + +/* + * Channel configuration struct. + * + * Priorities can be set via ioctl or in sysfs. + * Recommended values for the priorities: + * - CBC_CHANNEL_PMT: 6 + * - CBC_CHANNEL_SYSTEM_CONTROL: 6 + * - CBC_CHANNEL_PROCESS_DATA: 3 + * - CBC_CHANNEL_DIAGNOSTICS: 3 + * - CBC_CHANNEL_SW_TRANSFER: 2 + * - CBC_CHANNEL_DEBUG: 6 + * - CBC_CHANNEL_LINDA: 6 + * - default: 3 + * + */ +struct cbc_mux_channel_configuration { + struct cbc_mux_channel cbc_mux_channel_list[CBC_CHANNEL_MAX_NUMBER]; +}; + +/* Pass configuration to Multiplexer. */ +void cbc_mux_multiplexer_setup(struct cbc_mux_channel_configuration *config); + +/* Process buffer received over UARYT via link layer. */ +void cbc_mux_multiplexer_process_rx_buffer(struct cbc_buffer *cbc_buffer); + +enum cbc_error cbc_mux_multiplexer_transmit_buffer( + enum cbc_channel_enumeration channel, + struct cbc_buffer *cbc_buffer); + +void cbc_mux_multiplexer_set_priority(u32 channel_num, u8 new_priority); + +u8 cbc_mux_multiplexer_get_priority(u32 channel_num); + +#endif /*CBC_MUX_MULTIPLEXER_H_ */ + diff --git a/drivers/tty/cbc/cbc_types.h b/drivers/tty/cbc/cbc_types.h new file mode 100644 index 000000000000..28d6877ff36e --- /dev/null +++ b/drivers/tty/cbc/cbc_types.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_TYPES_H_ +#define CBC_TYPES_H_ + +#include + + +/* Start of frame indicator. */ +#define CBC_SOF 0x05 +/* Fill byte allowed between CBC frames */ +#define CBC_INTER_FRAME_FILL_BYTE 0xFF + +/*Width bit mask for priority in Mux. layer */ +#define CBC_PRIORITY_WIDTH_MASK GENMASK(2, 0) +/* Width bit mask for multiplexer in Mux layer */ +#define CBC_MULTIPLEXER_WIDTH_MASK GENMASK(4, 0) +/* Shift multiplexer in Mux layer */ +#define CBC_MULTIPLEXER_SHIFT 0x03 + +/* Width bit mask for sequence counter in link layer */ +#define CBC_SEQUENCE_COUNTER_WIDTH_MASK GENMASK(1, 0) +/* Width bit mask for frame length link layer */ +#define CBC_FRAME_LENGTH_WIDTH_MASK GENMASK(4, 0) +/* Maximum possible frame size that can be specified + * with the IAS_CBC_FRAME_LENGTH_WIDTH_MASK + */ +#define CBC_MAX_POSSIBLE_FRAME_SIZE ((CBC_FRAME_LENGTH_WIDTH_MASK + 2) * 4) +/* Frame shift length in link layer */ +#define CBC_FRAME_LENGTH_SHIFT 0x02 + +#define CBC_HEADER_SIZE 0x03 +#define CBC_RAWHEADER_SIZE 0x03 +#define CBC_CHECKSUM_SIZE 1 + +/* + * Maximum size of a CBC frame. This includes the + * IAS_CBC_MAX_SERVICE_FRAME_SIZE, 4 bytes of CBC protocol + * overhead and up to 28 additional bytes for padding to + * 32 byte granularity. + */ +#define CBC_MAX_TOTAL_FRAME_SIZE 96 + +/* Enumeration of supported CBC channels */ +enum cbc_channel_enumeration { + CBC_CHANNEL_PMT = 0, + CBC_CHANNEL_LIFECYCLE = 1, + CBC_CHANNEL_SIGNALS = 2, + CBC_CHANNEL_EARLY_SIGNALS = 3, + CBC_CHANNEL_DIAGNOSIS = 4, + CBC_CHANNEL_DLT = 5, + CBC_CHANNEL_LINDA = 6, + CBC_CHANNEL_OEM_RAW_CHANNEL_0 = 7, + CBC_CHANNEL_OEM_RAW_CHANNEL_1 = 8, + CBC_CHANNEL_OEM_RAW_CHANNEL_2 = 9, + CBC_CHANNEL_OEM_RAW_CHANNEL_3 = 10, + CBC_CHANNEL_OEM_RAW_CHANNEL_4 = 11, + CBC_CHANNEL_OEM_RAW_CHANNEL_5 = 12, + CBC_CHANNEL_OEM_RAW_CHANNEL_6 = 13, + CBC_CHANNEL_OEM_RAW_CHANNEL_7 = 14, + CBC_CHANNEL_OEM_RAW_CHANNEL_8 = 15, + CBC_CHANNEL_OEM_RAW_CHANNEL_9 = 16, + CBC_CHANNEL_OEM_RAW_CHANNEL_10 = 17, + CBC_CHANNEL_OEM_RAW_CHANNEL_11 = 18, + CBC_CHANNEL_DEBUG_OUT = 19, + CBC_CHANNEL_DEBUG_IN = 20, + CBC_CHANNEL_MAX_NUMBER = 21 +}; + +/* + * CBC load monitoring (transmit/receive throughput, errors etc.) can + * be compiled in using the following define. + */ + +/* Enumeration containing available errors */ +enum cbc_error { + CBC_OK = 0, + CBC_ERROR_QUEUE_UNINITIALIZED = 1, + CBC_ERROR_QUEUE_FULL = 2, + CBC_ERROR_QUEUE_EMPTY = 3, + CBC_ERROR_PARAMETER_INCORRECT = 4, + CBC_ERROR_NULL_POINTER_SUPPLIED = 5, + CBC_ERROR_CHECKSUM_MISMATCH = 6, + CBC_ERROR_UNKNOWN_CHANNEL = 7, + CBC_ERROR_OUT_OF_QUEUE_MEMORY = 8, + CBC_ERROR_NO_DATA_IN_QUEUE_MEMORY = 9, + CBC_ERROR_NOT_PROCESSED = 10, + CBC_ERROR_TP_FRAME_NOT_SUPPORTED = 11, + CBC_ERROR_TP_FRAME_NOT_EXPECTED = 12, + CBC_ERROR_BUSY_TRY_AGAIN = 13, + CBC_ERROR_TEC = 14, + CBC_ERROR_UNKNOWN_PERIPHERAL_ID = 15, + CBC_ERROR_HW_NO_WRITE_ACCESS = 16, + CBC_ERROR_HW_NO_READ_ACCESS = 17, + CBC_ERROR_NOT_IMPLEMENTED = 18, + CBC_ERROR_GENERAL_ERROR = 19, + CBC_ERROR_UDP_GET_ADR = 20, + CBC_ERROR_UDP_OPEN_SOCKET = 21, + CBC_ERROR_UDP_CONNECTION_REFUSED = 22, + CBC_ERROR_UDP_CLOSE_INVALID_ID = 23, + CBC_ERROR_UDP_CLOSE_ERR = 24, + CBC_ERROR_DTC_LIST_EMPTY = 25, + CBC_ERROR_INCORRECT_VERSION = 26, + CBC_ERROR_POWER_SUPPLY_ERROR = 27, + CBC_ERROR_PARAMETER_INVALID = 28, + E_CBC_ERROR_NOT_INITIALIZED = 29, + CBC_ERROR_NUMBER_OF_ERRORS = 30, + CBC_ERROR_CUSTOMER_IMPLEMENTATION_MISSING = 31 +}; + +/* + * Maximum number of CBC frames transmitted in each cyclic call of the CBC + * core. + */ +# define CBC_MAX_FRAME_TRANSMISSION_NUMBER 50 + +/* + * Enumeration indicating whether raw channel uses protocol or handles raw + * data. + */ +enum cbc_service_raw_channel_svc { + CBC_RAW_CHANNEL_USE_TRANSPORT_PROTOCOL = 1, + CBC_RAW_CHANNEL_DIRECT_TRANSPORT = 2 +} +; +#endif /* CBC_TYPES_H_ */ diff --git a/include/cbc/cbc-core.h b/include/cbc/cbc-core.h new file mode 100644 index 000000000000..4bcdf52d84eb --- /dev/null +++ b/include/cbc/cbc-core.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _UAPI_CBC_CORE_H_ +#define _UAPI_CBC_CORE_H_ + +#include + +/* Get/set the priority of this channel */ +#define CBC_PRIORITY_GET _IOR(CBC_IOCTL_MAGIC, 2, int) +#define CBC_PRIORITY_SET _IOW(CBC_IOCTL_MAGIC, 3, int) + + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/include/uapi/linux/cbc/cbc-core.h b/include/uapi/linux/cbc/cbc-core.h new file mode 100644 index 000000000000..4bcdf52d84eb --- /dev/null +++ b/include/uapi/linux/cbc/cbc-core.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _UAPI_CBC_CORE_H_ +#define _UAPI_CBC_CORE_H_ + +#include + +/* Get/set the priority of this channel */ +#define CBC_PRIORITY_GET _IOR(CBC_IOCTL_MAGIC, 2, int) +#define CBC_PRIORITY_SET _IOW(CBC_IOCTL_MAGIC, 3, int) + + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/include/uapi/linux/major.h b/include/uapi/linux/major.h index 7e5fa8e15c43..e08767b96a45 100644 --- a/include/uapi/linux/major.h +++ b/include/uapi/linux/major.h @@ -135,6 +135,8 @@ #define ATARAID_MAJOR 114 +#define CBC_CORE_MAJOR 115 + #define SCSI_DISK8_MAJOR 128 #define SCSI_DISK9_MAJOR 129 #define SCSI_DISK10_MAJOR 130 diff --git a/include/uapi/linux/tty.h b/include/uapi/linux/tty.h index 376cccf397be..20217970ebfd 100644 --- a/include/uapi/linux/tty.h +++ b/include/uapi/linux/tty.h @@ -6,7 +6,7 @@ * 'tty.h' defines some structures used by tty_io.c and some defines. */ -#define NR_LDISCS 30 +#define NR_LDISCS 31 /* line disciplines */ #define N_TTY 0 @@ -37,6 +37,7 @@ #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ #define N_NCI 25 /* NFC NCI UART */ #define N_SPEAKUP 26 /* Speakup communication with synths */ -#define N_NULL 27 /* Null ldisc used for error handling */ +#define N_CBCCORE 27 /* cbc protocol */ +#define N_NULL 28 /* Null ldisc used for error handling */ #endif /* _UAPI_LINUX_TTY_H */ From a01378510b915827720ad92ea90ae35f681766c5 Mon Sep 17 00:00:00 2001 From: "G Jaya Kumaran, Vineetha" Date: Fri, 25 May 2018 14:24:11 +0800 Subject: [PATCH 0372/1276] cbc: Avoid rx sequence counter mismatch warnings The receive sequence counter should only be incremented if the checksum passes, to avoid unnecessary spawning of receive sequence mismatch warnings. Change-Id: I8a595dd9365693e257e6f621c4ef26f0834488b9 Signed-off-by: G Jaya Kumaran, Vineetha --- drivers/tty/cbc/cbc_link_layer.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/tty/cbc/cbc_link_layer.c b/drivers/tty/cbc/cbc_link_layer.c index a55cd8ce7add..bd747aac250f 100644 --- a/drivers/tty/cbc/cbc_link_layer.c +++ b/drivers/tty/cbc/cbc_link_layer.c @@ -264,7 +264,8 @@ u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf) return number_of_bytes_accepted; } -static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) +static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length, + struct cbc_buffer *buffer) { u8 expected_checksum = 0U; u8 checksum = 0U; @@ -302,6 +303,17 @@ static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) rx_cvh_frame[1] & CBC_SEQUENCE_COUNTER_WIDTH_MASK; } + + /* Increment seq. counter. */ + rx_sequence_counter++; + rx_sequence_counter &= + CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Forward frame to Mux. layer. */ + buffer->frame_length = frame_length; + cbc_mux_multiplexer_process_rx_buffer(buffer); + cbc_link_release_rx_data(frame_length); + last_rx_frame_valid = 1; } } @@ -361,21 +373,7 @@ void cbc_link_layer_rx_handler(void) } else if (bytes_avail >= frame_length) { /* ok */ _cbc_link_layer_checksum(rx_cvh_frame, - frame_length); - - /* Increment seq. counter. */ - rx_sequence_counter++; - rx_sequence_counter &= - CBC_SEQUENCE_COUNTER_WIDTH_MASK; - - /* Forward frame to Mux. layer. */ - buffer->frame_length = frame_length; - cbc_mux_multiplexer_process_rx_buffer( - buffer); - cbc_link_release_rx_data(frame_length); - - last_rx_frame_valid = 1; - /* else */ + frame_length, buffer); number_of_bytes_expected = 0; } else { /* From c0600dc02243ce249dc72342f85c6b6065ac562c Mon Sep 17 00:00:00 2001 From: padmarao edapalapati Date: Thu, 6 Sep 2018 10:38:22 +0530 Subject: [PATCH 0373/1276] Fix for cbc kernel driver crash during warm reboot Added a Sync mechanism with spinlocks between demuxed_receive and cbc_device_open, cbc_device_release Change-Id: Id5c71eb59bb390a0b39b7bc66d5536d9eecf607f Signed-off-by: padmarao edapalapati --- drivers/tty/cbc/cbc_device.c | 4 +++- drivers/tty/cbc/cbc_device.h | 3 +++ drivers/tty/cbc/cbc_device_manager.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/tty/cbc/cbc_device.c b/drivers/tty/cbc/cbc_device.c index 1933a8527015..23728c04dd6e 100644 --- a/drivers/tty/cbc/cbc_device.c +++ b/drivers/tty/cbc/cbc_device.c @@ -18,8 +18,10 @@ void cbc_device_init(struct cbc_device_data *cd) { - if (cd) + if (cd) { + spin_lock_init(&cd->cbc_device_lock); INIT_LIST_HEAD(&cd->open_files_head); + } } void cbc_file_init(struct cbc_file_data *file) diff --git a/drivers/tty/cbc/cbc_device.h b/drivers/tty/cbc/cbc_device.h index deb0cd922316..09c806716557 100644 --- a/drivers/tty/cbc/cbc_device.h +++ b/drivers/tty/cbc/cbc_device.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "cbc_types.h" @@ -49,6 +50,8 @@ struct cbc_device_data { char *device_name; enum cbc_device_type device_type; struct device *device; + /* lock to sync demuxed_receive with cbc_device_release and open */ + spinlock_t cbc_device_lock; struct list_head open_files_head; }; diff --git a/drivers/tty/cbc/cbc_device_manager.c b/drivers/tty/cbc/cbc_device_manager.c index 0e74183d9828..2c8c1cf966ca 100644 --- a/drivers/tty/cbc/cbc_device_manager.c +++ b/drivers/tty/cbc/cbc_device_manager.c @@ -234,6 +234,7 @@ static int cbc_device_open(struct inode *inode, struct file *file) inode->i_rdev)]; int ret = 0; u32 num_open_files = 0; + unsigned long flags; struct cbc_file_data *file_data = kmalloc(sizeof(struct cbc_file_data), GFP_KERNEL); @@ -267,7 +268,9 @@ static int cbc_device_open(struct inode *inode, struct file *file) if (ret == 0) { cbc_file_init(file_data); file_data->cbc_device = device_data; + spin_lock_irqsave(&device_data->cbc_device_lock, flags); list_add(&file_data->list, &device_data->open_files_head); + spin_unlock_irqrestore(&device_data->cbc_device_lock, flags); file->private_data = file_data; } else { kfree(file_data); @@ -285,9 +288,14 @@ static int cbc_device_release(struct inode *inode, struct file *file) { u32 dev_idx = MINOR(inode->i_rdev); struct cbc_file_data *file_data = file->private_data; + unsigned long flags; if (file_data) { + spin_lock_irqsave( + &file_data->cbc_device->cbc_device_lock, flags); list_del(&file_data->list); + spin_unlock_irqrestore( + &file_data->cbc_device->cbc_device_lock, flags); pr_debug("cbc-core: device_release: %d.%d %s\n", MAJOR(inode->i_rdev), dev_idx, @@ -299,6 +307,7 @@ static int cbc_device_release(struct inode *inode, struct file *file) kfree(file_data); file->private_data = NULL; } + return 0; } @@ -796,6 +805,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) (struct cbc_device_data *) void_data; struct list_head *current_item; struct cbc_file_data *current_file_data; + unsigned long flags; if (device_data && cbc_buffer && cbc_buffer->frame_length > @@ -836,6 +846,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) /* else, do not touch payload_length in a debug-channel */ /* Enqueue */ + spin_lock_irqsave(&device_data->cbc_device_lock, flags); for (current_item = device_data->open_files_head.next ; current_item != &device_data->open_files_head; current_item = current_item->next) { @@ -845,6 +856,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) /* File_enqueue increases ref. count. */ cbc_file_enqueue(current_file_data, cbc_buffer); } + spin_unlock_irqrestore(&device_data->cbc_device_lock, flags); } else { pr_err("cbc-core: (<- IOC) dev_receive data is null\n"); } From 7cc1c2973325014d6367b4ecc2318744139009c7 Mon Sep 17 00:00:00 2001 From: "Fan, Yugang" Date: Mon, 17 Sep 2018 09:50:43 +0800 Subject: [PATCH 0374/1276] drm/i915: Sysfs interface to get GFX shmem usage stats per process There is a requirement of a new interface for achieving below functionalities: 1) Need to provide Client based detailed information about the distribution of Graphics memory 2) Need to provide an interface which can provide info about the sharing of Graphics buffers between the clients. The client based interface would also aid in debugging of memory usage/consumption by each client & debug memleak related issues. With this new interface, 1) In case of memleak scenarios, we can easily zero in on the culprit client which is unexpectedly holding on the Graphics buffers for an inordinate amount of time. 2) We can get an estimate of the instantaneous memory footprint of every Graphics client. 3) We can now trace all the processes sharing a particular Graphics buffer. By means of this patch we try to provide a sysfs interface to achieve the mentioned functionalities. There are two files created in sysfs: 'i915_gem_meminfo' will provide summary of the graphics resources used by each graphics client. 'i915_gem_objinfo' will provide detailed view of each object created by individual clients. Rebased for kernel v4.19 1A (Fan Yugang) 1) Removes unuseful shmem_inode_info->info_lock per Jeremy's optimization. 2) Removes unuseful object info for each pid for performance optimization. 3) Adds CONFIG_DRM_I915_MEMTRACK which depends on CONFIG_DRM_I915_CAPTURE_ERROR to pass kernel config test. Change-Id: If9705d001b922de3570693b2ad39125babd8e860 Signed-off-by: Sourab Gupta Sgined-off-by: Deepak S Signed-off-by: Hu Beiyuan Signed-off-by: Mingwei Wang Signed-off-by: Harish Krupo Signed-off-by: Jeremy Compostella Signed-off-by: Yugang Fan --- drivers/gpu/drm/drm_file.c | 4 + drivers/gpu/drm/drm_internal.h | 5 + drivers/gpu/drm/i915/Kconfig | 10 + drivers/gpu/drm/i915/i915_drv.c | 7 + drivers/gpu/drm/i915/i915_drv.h | 45 ++ drivers/gpu/drm/i915/i915_gem.c | 954 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_object.h | 8 + drivers/gpu/drm/i915/i915_gpu_error.c | 25 +- drivers/gpu/drm/i915/i915_gpu_error.h | 7 + drivers/gpu/drm/i915/i915_sysfs.c | 311 ++++++++ 10 files changed, 1375 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index ffa8dc35515f..d47119e045e8 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -46,6 +46,10 @@ /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +EXPORT_SYMBOL(drm_global_mutex); +#endif + /** * DOC: file operations * diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 40179c5fc6b8..d9848ae2219e 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -48,6 +48,11 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct dma_buf *dma_buf); /* drm_drv.c */ + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ +#endif + struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor); diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 33a458b7f1fc..858c578179d9 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -84,6 +84,16 @@ config DRM_I915_COMPRESS_ERROR If in doubt, say "Y". +config DRM_I915_MEMTRACK + bool "Enable shmem usage status track" + depends on DRM_I915_CAPTURE_ERROR + default y + help + This option enables shmem usage status track of system summary and + each process. + + If in doubt, say "N". + config DRM_I915_USERPTR bool "Always enable userptr support" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f8cfd16be534..d8ec9962c94e 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1516,6 +1516,10 @@ static void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) i915_gem_release(dev, file); mutex_unlock(&dev->struct_mutex); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + kfree(file_priv->process_name); +#endif + kfree(file_priv); } @@ -2874,6 +2878,9 @@ static struct drm_driver driver = { .lastclose = i915_driver_lastclose, .postclose = i915_driver_postclose, +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + .gem_open_object = i915_gem_open_object, +#endif .gem_close_object = i915_gem_close_object, .gem_free_object_unlocked = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4aca5344863d..c2791ebd3fca 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -78,6 +78,9 @@ #include "i915_scheduler.h" #include "i915_timeline.h" #include "i915_vma.h" +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include "i915_gpu_error.h" +#endif #include "intel_gvt.h" @@ -333,6 +336,11 @@ struct drm_i915_file_private { struct drm_i915_private *dev_priv; struct drm_file *file; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + char *process_name; + struct pid *tgid; +#endif + struct { spinlock_t lock; struct list_head request_list; @@ -351,6 +359,10 @@ struct drm_i915_file_private { unsigned int bsd_engine; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct bin_attribute *obj_attr; +#endif + /* * Every context ban increments per client ban score. Also * hangs in short succession increments ban score. If ban threshold @@ -996,6 +1008,10 @@ struct i915_gem_mm { spinlock_t object_stat_lock; u64 object_memory; u32 object_count; + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + size_t phys_mem_total; +#endif }; #define I915_IDLE_ENGINES_TIMEOUT (200) /* in ms */ @@ -1666,6 +1682,10 @@ struct drm_i915_private { bool preserve_bios_swizzle; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct kobject memtrack_kobj; +#endif + /* overlay */ struct intel_overlay *overlay; @@ -2898,6 +2918,11 @@ i915_gem_object_create(struct drm_i915_private *dev_priv, u64 size); struct drm_i915_gem_object * i915_gem_object_create_from_data(struct drm_i915_private *dev_priv, const void *data, size_t size); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_gem_open_object(struct drm_gem_object *gem, struct drm_file *file); +#endif + void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file); void i915_gem_free_object(struct drm_gem_object *obj); @@ -3319,6 +3344,19 @@ u32 i915_gem_fence_size(struct drm_i915_private *dev_priv, u32 size, u32 i915_gem_fence_alignment(struct drm_i915_private *dev_priv, u32 size, unsigned int tiling, unsigned int stride); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_get_pid_cmdline(struct task_struct *task, char *buffer); +int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj); +void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj); +int i915_obj_insert_virt_addr(struct drm_i915_gem_object *obj, + unsigned long addr, bool is_map_gtt, + bool is_mutex_locked); +int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev); +int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid); +#endif + /* i915_debugfs.c */ #ifdef CONFIG_DEBUG_FS int i915_debugfs_register(struct drm_i915_private *dev_priv); @@ -3358,6 +3396,13 @@ extern int i915_restore_state(struct drm_i915_private *dev_priv); void i915_setup_sysfs(struct drm_i915_private *dev_priv); void i915_teardown_sysfs(struct drm_i915_private *dev_priv); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_gem_create_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file); +void i915_gem_remove_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file); +#endif + /* intel_lpe_audio.c */ int intel_lpe_audio_init(struct drm_i915_private *dev_priv); void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fcc73a6ab503..4b31093239b2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -47,8 +47,852 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include +#include +#include +#include "../drm_internal.h" +#endif + static void i915_gem_flush_free_objects(struct drm_i915_private *i915); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +struct per_file_obj_mem_info { + int num_obj; + int num_obj_shared; + int num_obj_private; + int num_obj_gtt_bound; + int num_obj_purged; + int num_obj_purgeable; + int num_obj_allocated; + int num_obj_fault_mappable; + int num_obj_stolen; + size_t gtt_space_allocated_shared; + size_t gtt_space_allocated_priv; + size_t phys_space_allocated_shared; + size_t phys_space_allocated_priv; + size_t phys_space_purgeable; + size_t phys_space_shared_proportion; + size_t fault_mappable_size; + size_t stolen_space_allocated; + char *process_name; +}; + +struct name_entry { + struct list_head head; + struct drm_hash_item hash_item; +}; + +struct pid_stat_entry { + struct list_head head; + struct list_head namefree; + struct drm_open_hash namelist; + struct per_file_obj_mem_info stats; + struct pid *tgid; + int pid_num; +}; + +struct drm_i915_obj_virt_addr { + struct list_head head; + unsigned long user_virt_addr; +}; + +struct drm_i915_obj_pid_info { + struct list_head head; + pid_t tgid; + int open_handle_count; + struct list_head virt_addr_head; +}; + +struct get_obj_stats_buf { + struct pid_stat_entry *entry; + struct drm_i915_error_state_buf *m; +}; + +#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__) +#define err_puts(e, s) i915_error_puts(e, s) + +static const char *get_tiling_flag(struct drm_i915_gem_object *obj) +{ + switch (i915_gem_object_get_tiling(obj)) { + default: + case I915_TILING_NONE: return " "; + case I915_TILING_X: return "X"; + case I915_TILING_Y: return "Y"; + } +} + +/* + * If this mmput call is the last one, it will tear down the mmaps of the + * process and calls drm_gem_vm_close(), which leads deadlock on i915 mutex. + * Instead, asynchronously schedule mmput function here, to avoid recursive + * calls to acquire i915_mutex. + */ +static void async_mmput_func(void *data, async_cookie_t cookie) +{ + struct mm_struct *mm = data; + mmput(mm); +} + +static void async_mmput(struct mm_struct *mm) +{ + async_schedule(async_mmput_func, mm); +} + +int i915_get_pid_cmdline(struct task_struct *task, char *buffer) +{ + int res = 0; + unsigned int len; + struct mm_struct *mm = get_task_mm(task); + + if (!mm) + goto out; + if (!mm->arg_end) + goto out_mm; + + len = mm->arg_end - mm->arg_start; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + + res = access_process_vm(task, mm->arg_start, buffer, len, 0); + if (res < 0) { + async_mmput(mm); + return res; + } + + if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) + buffer[res-1] = '\0'; +out_mm: + async_mmput(mm); +out: + return 0; +} + +static int i915_obj_get_shmem_pages_alloced(struct drm_i915_gem_object *obj) +{ + if (obj->base.filp) { + struct inode *inode = file_inode(obj->base.filp); + + if (!inode) + return 0; + return inode->i_mapping->nrpages; + } + return 0; +} + +int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj) +{ + int found = 0; + struct drm_i915_obj_pid_info *entry; + pid_t current_tgid = task_tgid_nr(current); + + mutex_lock(&obj->base.dev->struct_mutex); + + list_for_each_entry(entry, &obj->pid_info, head) { + if (entry->tgid == current_tgid) { + entry->open_handle_count++; + found = 1; + break; + } + } + if (found == 0) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + DRM_ERROR("alloc failed\n"); + mutex_unlock(&obj->base.dev->struct_mutex); + return -ENOMEM; + } + entry->tgid = current_tgid; + entry->open_handle_count = 1; + INIT_LIST_HEAD(&entry->virt_addr_head); + list_add_tail(&entry->head, &obj->pid_info); + } + + mutex_unlock(&obj->base.dev->struct_mutex); + return 0; +} + +void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj) +{ + struct drm_i915_obj_pid_info *pid_entry, *pid_next; + struct drm_i915_obj_virt_addr *virt_entry, *virt_next; + + list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) { + list_for_each_entry_safe(virt_entry, + virt_next, + &pid_entry->virt_addr_head, + head) { + list_del(&virt_entry->head); + kfree(virt_entry); + } + list_del(&pid_entry->head); + kfree(pid_entry); + } +} + + int +i915_obj_insert_virt_addr(struct drm_i915_gem_object *obj, + unsigned long addr, + bool is_map_gtt, + bool is_mutex_locked) +{ + struct drm_i915_obj_pid_info *pid_entry; + pid_t current_tgid = task_tgid_nr(current); + int ret = 0, found = 0; + + if (is_map_gtt) + addr |= 1; + + if (!is_mutex_locked) { + ret = i915_mutex_lock_interruptible(obj->base.dev); + if (ret) + return ret; + } + + list_for_each_entry(pid_entry, &obj->pid_info, head) { + if (pid_entry->tgid == current_tgid) { + struct drm_i915_obj_virt_addr *virt_entry, *new_entry; + + list_for_each_entry(virt_entry, + &pid_entry->virt_addr_head, + head) { + if (virt_entry->user_virt_addr == addr) { + found = 1; + break; + } + } + if (found) + break; + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (new_entry == NULL) { + DRM_ERROR("alloc failed\n"); + ret = -ENOMEM; + goto out; + } + new_entry->user_virt_addr = addr; + list_add_tail(&new_entry->head, + &pid_entry->virt_addr_head); + break; + } + } + +out: + if (!is_mutex_locked) + mutex_unlock(&obj->base.dev->struct_mutex); + + return ret; +} + +static int i915_obj_virt_addr_is_invalid(struct drm_gem_object *obj, + struct pid *tgid, unsigned long addr) +{ + struct task_struct *task; + struct mm_struct *mm; + struct vm_area_struct *vma; + int locked, ret = 0; + + task = get_pid_task(tgid, PIDTYPE_PID); + if (task == NULL) { + DRM_DEBUG("null task for tgid=%d\n", pid_nr(tgid)); + return -EINVAL; + } + + mm = get_task_mm(task); + if (mm == NULL) { + DRM_DEBUG("null mm for tgid=%d\n", pid_nr(tgid)); + ret = -EINVAL; + goto out_task; + } + + locked = down_read_trylock(&mm->mmap_sem); + if (!locked) + goto out_mm; + + vma = find_vma(mm, addr); + if (vma) { + if (addr & 1) { /* mmap_gtt case */ + if (vma->vm_pgoff*PAGE_SIZE == (unsigned long) + drm_vma_node_offset_addr(&obj->vma_node)) + ret = 0; + else + ret = -EINVAL; + } else { /* mmap case */ + if (vma->vm_file == obj->filp) + ret = 0; + else + ret = -EINVAL; + } + } else + ret = -EINVAL; + + up_read(&mm->mmap_sem); + +out_mm: + async_mmput(mm); +out_task: + put_task_struct(task); + return ret; +} + +static void i915_obj_pidarray_validate(struct drm_gem_object *gem_obj) +{ + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + struct drm_device *dev = gem_obj->dev; + struct drm_i915_obj_virt_addr *virt_entry, *virt_next; + struct drm_i915_obj_pid_info *pid_entry, *pid_next; + struct drm_file *file; + struct drm_i915_file_private *file_priv; + struct pid *tgid; + int pid_num, present; + + /* + * Run a sanity check on pid_array. All entries in pid_array should + * be subset of the the drm filelist pid entries. + */ + list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) { + if (pid_next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + break; + } + + present = 0; + list_for_each_entry(file, &dev->filelist, lhead) { + file_priv = file->driver_priv; + tgid = file_priv->tgid; + pid_num = pid_nr(tgid); + + if (pid_num == pid_entry->tgid) { + present = 1; + break; + } + } + if (present == 0) { + DRM_DEBUG("stale_tgid=%d\n", pid_entry->tgid); + list_for_each_entry_safe(virt_entry, virt_next, + &pid_entry->virt_addr_head, + head) { + list_del(&virt_entry->head); + kfree(virt_entry); + } + list_del(&pid_entry->head); + kfree(pid_entry); + } else { + /* Validate the virtual address list */ + struct task_struct *task = + get_pid_task(tgid, PIDTYPE_PID); + if (task == NULL) + continue; + + list_for_each_entry_safe(virt_entry, virt_next, + &pid_entry->virt_addr_head, + head) { + if (i915_obj_virt_addr_is_invalid(gem_obj, tgid, + virt_entry->user_virt_addr)) { + DRM_DEBUG("stale_addr=%ld\n", + virt_entry->user_virt_addr); + list_del(&virt_entry->head); + kfree(virt_entry); + } + } + put_task_struct(task); + } + } +} + +static int i915_obj_find_insert_in_hash(struct drm_i915_gem_object *obj, + struct pid_stat_entry *pid_entry, + bool *found) +{ + struct drm_hash_item *hash_item; + int ret; + + ret = drm_ht_find_item(&pid_entry->namelist, + (unsigned long)&obj->base, &hash_item); + /* Not found, insert in hash */ + if (ret) { + struct name_entry *entry = + kzalloc(sizeof(*entry), GFP_NOWAIT); + if (entry == NULL) { + DRM_ERROR("alloc failed\n"); + return -ENOMEM; + } + entry->hash_item.key = (unsigned long)&obj->base; + drm_ht_insert_item(&pid_entry->namelist, + &entry->hash_item); + list_add_tail(&entry->head, &pid_entry->namefree); + *found = false; + } else + *found = true; + + return 0; +} + +static int i915_obj_shared_count(struct drm_i915_gem_object *obj, + struct pid_stat_entry *pid_entry, + bool *discard) +{ + struct drm_i915_obj_pid_info *pid_info_entry; + int ret, obj_shared_count = 0; + + /* + * The object can be shared among different processes by either flink + * or dma-buf mechanism, leading to shared count more than 1. For the + * objects not shared , return the shared count as 1. + * In case of shared dma-buf objects, there's a possibility that these + * may be external to i915. Detect this condition through + * 'import_attach' field. + */ + if (!obj->base.name && !obj->base.dma_buf) + return 1; + else if(obj->base.import_attach) { + /* not our GEM obj */ + *discard = true; + return 0; + } + + ret = i915_obj_find_insert_in_hash(obj, pid_entry, discard); + if (ret) + return ret; + + list_for_each_entry(pid_info_entry, &obj->pid_info, head) + obj_shared_count++; + + if (WARN_ON(obj_shared_count == 0)) + return -EINVAL; + + return obj_shared_count; +} + + static int +i915_describe_obj(struct get_obj_stats_buf *obj_stat_buf, + struct drm_i915_gem_object *obj) +{ + struct pid_stat_entry *pid_entry = obj_stat_buf->entry; + struct per_file_obj_mem_info *stats = &pid_entry->stats; + int obj_shared_count = 0; + + bool discard = false; + + obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard); + if (obj_shared_count < 0) + return obj_shared_count; + + if (!discard && !obj->stolen && + (obj->mm.madv != __I915_MADV_PURGED) && + (i915_obj_get_shmem_pages_alloced(obj) != 0)) { + if (obj_shared_count > 1) + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + else + stats->phys_space_allocated_priv += + obj->base.size; + } + + return 0; +} + + static int +i915_drm_gem_obj_info(int id, void *ptr, void *data) +{ + struct drm_i915_gem_object *obj = ptr; + struct get_obj_stats_buf *obj_stat_buf = data; + + if (obj->pid_info.next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + return 0; + } + + return i915_describe_obj(obj_stat_buf, obj); +} + +bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o) +{ + struct i915_vma *vma; + + list_for_each_entry(vma, &o->vma_list, obj_link) + if (drm_mm_node_allocated(&vma->node)) + return true; + + return false; +} + + static int +i915_drm_gem_object_per_file_summary(int id, void *ptr, void *data) +{ + struct pid_stat_entry *pid_entry = data; + struct drm_i915_gem_object *obj = ptr; + struct per_file_obj_mem_info *stats = &pid_entry->stats; + int obj_shared_count = 0; + bool discard = false; + + if (obj->pid_info.next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + return 0; + } + + i915_obj_pidarray_validate(&obj->base); + + stats->num_obj++; + + obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard); + if (obj_shared_count < 0) + return obj_shared_count; + + if (discard) + return 0; + + if (obj_shared_count > 1) + stats->num_obj_shared++; + else + stats->num_obj_private++; + + if (i915_gem_obj_bound_any(obj)) { + stats->num_obj_gtt_bound++; + if (obj_shared_count > 1) + stats->gtt_space_allocated_shared += obj->base.size; + else + stats->gtt_space_allocated_priv += obj->base.size; + } + + if (obj->stolen) { + stats->num_obj_stolen++; + stats->stolen_space_allocated += obj->base.size; + } else if (obj->mm.madv == __I915_MADV_PURGED) { + stats->num_obj_purged++; + } else if (obj->mm.madv == I915_MADV_DONTNEED) { + stats->num_obj_purgeable++; + stats->num_obj_allocated++; + if (i915_obj_get_shmem_pages_alloced(obj) != 0) { + stats->phys_space_purgeable += obj->base.size; + if (obj_shared_count > 1) { + stats->phys_space_allocated_shared += + obj->base.size; + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + } else + stats->phys_space_allocated_priv += + obj->base.size; + } else + WARN_ON(1); + } else if (i915_obj_get_shmem_pages_alloced(obj) != 0) { + stats->num_obj_allocated++; + if (obj_shared_count > 1) { + stats->phys_space_allocated_shared += + obj->base.size; + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + } + else + stats->phys_space_allocated_priv += obj->base.size; + } + return 0; +} + + static int +__i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev) +{ + struct drm_file *file; + struct drm_i915_private *dev_priv = dev->dev_private; + + struct name_entry *entry, *next; + struct pid_stat_entry *pid_entry, *temp_entry; + struct pid_stat_entry *new_pid_entry, *new_temp_entry; + struct list_head per_pid_stats, sorted_pid_stats; + int ret = 0; + size_t total_shared_prop_space = 0, total_priv_space = 0; + + INIT_LIST_HEAD(&per_pid_stats); + INIT_LIST_HEAD(&sorted_pid_stats); + + err_puts(m, + "\n\n pid Total Shared Priv Purgeable Alloced SharedPHYsize SharedPHYprop PrivPHYsize PurgeablePHYsize process\n"); + + list_for_each_entry(file, &dev->filelist, lhead) { + struct pid *tgid; + struct drm_i915_file_private *file_priv = file->driver_priv; + int pid_num, found = 0; + + tgid = file_priv->tgid; + pid_num = pid_nr(tgid); + + list_for_each_entry(pid_entry, &per_pid_stats, head) { + if (pid_entry->pid_num == pid_num) { + found = 1; + break; + } + } + + if (!found) { + struct pid_stat_entry *new_entry = + kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (new_entry == NULL) { + DRM_ERROR("alloc failed\n"); + ret = -ENOMEM; + break; + } + new_entry->tgid = tgid; + new_entry->pid_num = pid_num; + ret = drm_ht_create(&new_entry->namelist, + DRM_MAGIC_HASH_ORDER); + if (ret) { + kfree(new_entry); + break; + } + + list_add_tail(&new_entry->head, &per_pid_stats); + INIT_LIST_HEAD(&new_entry->namefree); + new_entry->stats.process_name = file_priv->process_name; + pid_entry = new_entry; + } + + spin_lock(&file->table_lock); + ret = idr_for_each(&file->object_idr, + &i915_drm_gem_object_per_file_summary, pid_entry); + spin_unlock(&file->table_lock); + if (ret) + break; + } + + list_for_each_entry_safe(pid_entry, temp_entry, &per_pid_stats, head) { + if (list_empty(&sorted_pid_stats)) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, &sorted_pid_stats); + continue; + } + + list_for_each_entry_safe(new_pid_entry, new_temp_entry, + &sorted_pid_stats, head) { + int prev_space = + pid_entry->stats.phys_space_shared_proportion + + pid_entry->stats.phys_space_allocated_priv; + int new_space = + new_pid_entry-> + stats.phys_space_shared_proportion + + new_pid_entry->stats.phys_space_allocated_priv; + if (prev_space > new_space) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, + &new_pid_entry->head); + break; + } + if (list_is_last(&new_pid_entry->head, + &sorted_pid_stats)) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, + &sorted_pid_stats); + } + } + } + + list_for_each_entry_safe(pid_entry, temp_entry, + &sorted_pid_stats, head) { + struct task_struct *task = get_pid_task(pid_entry->tgid, + PIDTYPE_PID); + err_printf(m, + "%5d %6d %6d %6d %9d %8d %14zdK %14zdK %14zdK %14zdK %s", + pid_entry->pid_num, + pid_entry->stats.num_obj, + pid_entry->stats.num_obj_shared, + pid_entry->stats.num_obj_private, + pid_entry->stats.num_obj_purgeable, + pid_entry->stats.num_obj_allocated, + pid_entry->stats.phys_space_allocated_shared/1024, + pid_entry->stats.phys_space_shared_proportion/1024, + pid_entry->stats.phys_space_allocated_priv/1024, + pid_entry->stats.phys_space_purgeable/1024, + pid_entry->stats.process_name); + + if (task == NULL) + err_puts(m, "*\n"); + else + err_puts(m, "\n"); + + total_shared_prop_space += + pid_entry->stats.phys_space_shared_proportion/1024; + total_priv_space += + pid_entry->stats.phys_space_allocated_priv/1024; + list_del(&pid_entry->head); + + list_for_each_entry_safe(entry, next, + &pid_entry->namefree, head) { + list_del(&entry->head); + drm_ht_remove_item(&pid_entry->namelist, + &entry->hash_item); + kfree(entry); + } + drm_ht_remove(&pid_entry->namelist); + kfree(pid_entry); + if (task) + put_task_struct(task); + } + + err_puts(m, + "\t\t\t\t\t\t\t\t--------------\t-------------\t--------\n"); + err_printf(m, + "\t\t\t\t\t\t\t\t%13zdK\t%12zdK\tTotal\n", + total_shared_prop_space, total_priv_space); + + err_printf(m, "\nTotal used GFX Shmem Physical space %8zdK\n", + dev_priv->mm.phys_mem_total/1024); + + if (ret) + return ret; + if (m->bytes == 0 && m->err) + return m->err; + + return 0; +} + +#define NUM_SPACES 100 +#define INITIAL_SPACES_STR(x) #x +#define SPACES_STR(x) INITIAL_SPACES_STR(x) + + static int +__i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid) +{ + struct drm_file *file; + struct drm_i915_file_private *file_priv_reqd = NULL; + int bytes_copy, ret = 0; + struct pid_stat_entry pid_entry; + struct name_entry *entry, *next; + + pid_entry.stats.phys_space_shared_proportion = 0; + pid_entry.stats.phys_space_allocated_priv = 0; + pid_entry.tgid = tgid; + pid_entry.pid_num = pid_nr(tgid); + ret = drm_ht_create(&pid_entry.namelist, DRM_MAGIC_HASH_ORDER); + if (ret) + return ret; + + INIT_LIST_HEAD(&pid_entry.namefree); + + /* + * Fill up initial few bytes with spaces, to insert summary data later + * on + */ + err_printf(m, "%"SPACES_STR(NUM_SPACES)"s\n", " "); + + list_for_each_entry(file, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv = file->driver_priv; + struct get_obj_stats_buf obj_stat_buf; + + obj_stat_buf.entry = &pid_entry; + obj_stat_buf.m = m; + + if (file_priv->tgid != tgid) + continue; + + file_priv_reqd = file_priv; + spin_lock(&file->table_lock); + ret = idr_for_each(&file->object_idr, + &i915_drm_gem_obj_info, &obj_stat_buf); + spin_unlock(&file->table_lock); + if (ret) + break; + } + + if (file_priv_reqd) { + int space_remaining; + + /* Reset the bytes counter to buffer beginning */ + bytes_copy = m->bytes; + m->bytes = 0; + + err_printf(m, "\n PID GfxMem Process\n"); + err_printf(m, "%5d %8zdK ", pid_nr(file_priv_reqd->tgid), + (pid_entry.stats.phys_space_shared_proportion + + pid_entry.stats.phys_space_allocated_priv)/1024); + + space_remaining = NUM_SPACES - m->bytes - 1; + if (strlen(file_priv_reqd->process_name) > space_remaining) + file_priv_reqd->process_name[space_remaining] = '\0'; + + err_printf(m, "%s\n", file_priv_reqd->process_name); + + /* Reinstate the previous saved value of bytes counter */ + m->bytes = bytes_copy; + } else + WARN(1, "drm file corresponding to tgid:%d not found\n", + pid_nr(tgid)); + + list_for_each_entry_safe(entry, next, + &pid_entry.namefree, head) { + list_del(&entry->head); + drm_ht_remove_item(&pid_entry.namelist, + &entry->hash_item); + kfree(entry); + } + drm_ht_remove(&pid_entry.namelist); + + if (ret) + return ret; + if (m->bytes == 0 && m->err) + return m->err; + return 0; +} + +int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev) +{ + int ret = 0; + + /* + * Protect the access to global drm resources such as filelist. Protect + * against their removal under our noses, while in use. + */ + mutex_lock(&drm_global_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + mutex_unlock(&drm_global_mutex); + return ret; + } + + ret = __i915_get_drm_clients_info(m, dev); + + mutex_unlock(&dev->struct_mutex); + mutex_unlock(&drm_global_mutex); + + return ret; +} + +int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = __i915_gem_get_obj_info(m, dev, tgid); + + mutex_unlock(&dev->struct_mutex); + + return ret; +} +#endif + static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj) { if (obj->cache_dirty) @@ -1856,6 +2700,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_mmap *args = data; struct drm_i915_gem_object *obj; unsigned long addr; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + int ret; +#endif if (args->flags & ~(I915_MMAP_WC)) return -EINVAL; @@ -1901,6 +2748,12 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, if (IS_ERR((void *)addr)) return addr; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + ret = i915_obj_insert_virt_addr(obj, addr, false, false); + if (ret) + return ret; +#endif + args->addr_ptr = (uint64_t) addr; return 0; @@ -2103,6 +2956,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf) (ggtt->gmadr.start + vma->node.start) >> PAGE_SHIFT, min_t(u64, vma->size, area->vm_end - area->vm_start), &ggtt->iomap); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + ret = i915_obj_insert_virt_addr(obj, (unsigned long)area->vm_start, true, true); +#endif if (ret) goto err_fence; @@ -2360,6 +3216,19 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) shmem_truncate_range(file_inode(obj->base.filp), 0, (loff_t)-1); obj->mm.madv = __I915_MADV_PURGED; obj->mm.pages = ERR_PTR(-EFAULT); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Mark the object as not having backing pages, as physical space + * returned back to kernel + */ + if (obj->has_backing_pages == 1) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total -= obj->base.size; + obj->has_backing_pages = 0; + } +#endif } /* Try to discard unwanted pages */ @@ -2655,6 +3524,14 @@ static int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj, st); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (obj->has_backing_pages == 0) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total += obj->base.size; + obj->has_backing_pages = 1; + } +#endif __i915_gem_object_set_pages(obj, st, sg_page_sizes); return 0; @@ -4699,6 +5576,15 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN); mutex_init(&obj->mm.get_page.lock); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Mark the object as not having backing pages, as no allocation + * for it yet + */ + obj->has_backing_pages = 0; + INIT_LIST_HEAD(&obj->pid_info); +#endif + i915_gem_info_add_obj(to_i915(obj->base.dev), obj->base.size); } @@ -4832,6 +5718,17 @@ static bool discard_backing_storage(struct drm_i915_gem_object *obj) return atomic_long_read(&obj->base.filp->f_count) == 1; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int +i915_gem_open_object(struct drm_gem_object *gem_obj, + struct drm_file *file_priv) +{ + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + + return i915_gem_obj_insert_pid(obj); +} +#endif + static void __i915_gem_free_objects(struct drm_i915_private *i915, struct llist_node *freed) { @@ -4885,6 +5782,16 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915, if (obj->base.import_attach) drm_prime_gem_destroy(&obj->base, NULL); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (!obj->stolen && (obj->has_backing_pages == 1)) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total -= obj->base.size; + obj->has_backing_pages = 0; + } + i915_gem_obj_remove_all_pids(obj); +#endif + reservation_object_fini(&obj->__builtin_resv); drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(i915, obj->base.size); @@ -5837,6 +6744,11 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file) struct drm_i915_file_private *file_priv = file->driver_priv; struct i915_request *request; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + i915_gem_remove_sysfs_file_entry(dev, file); + put_pid(file_priv->tgid); +#endif + /* Clean up our request list when the client is going away, so that * later retire_requests won't dereference our soon-to-be-gone * file_priv. @@ -5862,15 +6774,57 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file) file_priv->dev_priv = i915; file_priv->file = file; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + rcu_read_lock(); + file_priv->tgid = get_pid(find_vpid(task_tgid_nr(current))); + rcu_read_unlock(); + + file_priv->process_name = kzalloc(PAGE_SIZE, GFP_ATOMIC); + if (!file_priv->process_name) { + ret = -ENOMEM; + goto out_free_file; + } + + ret = i915_get_pid_cmdline(current, file_priv->process_name); + if (ret) + goto out_free_name; +#endif + spin_lock_init(&file_priv->mm.lock); INIT_LIST_HEAD(&file_priv->mm.request_list); file_priv->bsd_engine = -1; file_priv->hang_timestamp = jiffies; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + intel_runtime_pm_get(i915); +#endif + ret = i915_gem_context_open(i915, file); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (ret) { + intel_runtime_pm_put(i915); + goto out_free_name; + } + intel_runtime_pm_put(i915); + + ret = i915_gem_create_sysfs_file_entry(&i915->drm, file); + if (ret) { + i915_gem_context_close(file); + goto out_free_name; + } + + return 0; + +out_free_name: + kfree(file_priv->process_name); +out_free_file: + put_pid(file_priv->tgid); + kfree(file_priv); +#else if (ret) kfree(file_priv); +#endif return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h index 83e5e01fa9ea..338709b6640e 100644 --- a/drivers/gpu/drm/i915/i915_gem_object.h +++ b/drivers/gpu/drm/i915/i915_gem_object.h @@ -147,6 +147,10 @@ struct drm_i915_gem_object { #define I915_BO_CACHE_COHERENT_FOR_WRITE BIT(1) unsigned int cache_dirty:1; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + unsigned int has_backing_pages:1; +#endif + /** * @read_domains: Read memory domains. * @@ -278,6 +282,10 @@ struct drm_i915_gem_object { void *gvt_info; }; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct list_head pid_info; +#endif + /** for phys allocated objects */ struct drm_dma_handle *phys_handle; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index a262a64f5625..a803449498f8 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -91,6 +91,13 @@ static bool __i915_error_ok(struct drm_i915_error_state_buf *e) return true; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +bool i915_error_ok(struct drm_i915_error_state_buf *e) +{ + return __i915_error_ok(e); +} +#endif + static bool __i915_error_seek(struct drm_i915_error_state_buf *e, unsigned len) { @@ -162,7 +169,7 @@ static void i915_error_vprintf(struct drm_i915_error_state_buf *e, __i915_error_advance(e, len); } -static void i915_error_puts(struct drm_i915_error_state_buf *e, +void i915_error_puts(struct drm_i915_error_state_buf *e, const char *str) { unsigned len; @@ -871,6 +878,22 @@ int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf, return 0; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_obj_state_buf_init(struct drm_i915_error_state_buf *ebuf, + size_t count) +{ + memset(ebuf, 0, sizeof(*ebuf)); + + ebuf->buf = kmalloc(count, GFP_KERNEL); + + if (ebuf->buf == NULL) + return -ENOMEM; + + ebuf->size = count; + return 0; +} +#endif + static void i915_error_object_free(struct drm_i915_error_object *obj) { int page; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h index 8710fb18ed74..821bed7bd375 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.h +++ b/drivers/gpu/drm/i915/i915_gpu_error.h @@ -307,6 +307,13 @@ struct drm_i915_error_state_buf { }; #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +void i915_error_puts(struct drm_i915_error_state_buf *e, + const char *str); +bool i915_error_ok(struct drm_i915_error_state_buf *e); +int i915_obj_state_buf_init(struct drm_i915_error_state_buf *eb, + size_t count); +#endif __printf(2, 3) void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...); diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index e5e6f6bb2b05..4ff644202743 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -32,6 +32,10 @@ #include "intel_drv.h" #include "i915_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include "../drm_internal.h" +#endif + static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev) { struct drm_minor *minor = dev_get_drvdata(kdev); @@ -571,6 +575,284 @@ static void i915_teardown_error_capture(struct device *kdev) { sysfs_remove_bin_file(&kdev->kobj, &error_state_attr); } + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#define dev_to_drm_minor(d) dev_get_drvdata((d)) + +static ssize_t i915_gem_clients_state_read(struct file *filp, + struct kobject *memtrack_kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct kobject *kobj = memtrack_kobj->parent; + struct device *kdev = container_of(kobj, struct device, kobj); + struct drm_minor *minor = dev_to_drm_minor(kdev); + struct drm_device *dev = minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_error_state_buf error_str; + ssize_t ret_count = 0; + int ret; + + ret = i915_error_state_buf_init(&error_str, dev_priv, count, off); + if (ret) + return ret; + + ret = i915_get_drm_clients_info(&error_str, dev); + if (ret) + goto out; + + ret_count = count < error_str.bytes ? count : error_str.bytes; + + memcpy(buf, error_str.buf, ret_count); +out: + i915_error_state_buf_release(&error_str); + + return ret ?: ret_count; +} + +#define GEM_OBJ_STAT_BUF_SIZE (4*1024) /* 4KB */ +#define GEM_OBJ_STAT_BUF_SIZE_MAX (1024*1024) /* 1MB */ + +struct i915_gem_file_attr_priv { + char tgid_str[16]; + struct pid *tgid; + struct drm_i915_error_state_buf buf; +}; + +static ssize_t i915_gem_read_objects(struct file *filp, + struct kobject *memtrack_kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct kobject *kobj = memtrack_kobj->parent; + struct device *kdev = container_of(kobj, struct device, kobj); + struct drm_minor *minor = dev_to_drm_minor(kdev); + struct drm_device *dev = minor->dev; + struct i915_gem_file_attr_priv *attr_priv; + struct pid *tgid; + ssize_t ret_count = 0; + long bytes_available; + int ret = 0, buf_size = GEM_OBJ_STAT_BUF_SIZE; + unsigned long timeout = msecs_to_jiffies(500) + 1; + + /* + * There may arise a scenario where syfs file entry is being removed, + * and may race against sysfs read. Sysfs file remove function would + * have taken the drm_global_mutex and would wait for read to finish, + * which is again waiting to acquire drm_global_mutex, leading to + * deadlock. To avoid this, use mutex_trylock here with a timeout. + */ + while (!mutex_trylock(&drm_global_mutex) && --timeout) + schedule_timeout_killable(1); + if (timeout == 0) { + DRM_DEBUG_DRIVER("Unable to acquire drm global mutex.\n"); + return -EBUSY; + } + + if (!attr || !attr->private) { + ret = -EINVAL; + DRM_ERROR("attr | attr->private pointer is NULL\n"); + goto out; + } + attr_priv = attr->private; + tgid = attr_priv->tgid; + + if (off && !attr_priv->buf.buf) { + ret = -EINVAL; + DRM_ERROR( + "Buf not allocated during read with non-zero offset\n"); + goto out; + } + + if (off == 0) { +retry: + if (!attr_priv->buf.buf) { + ret = i915_obj_state_buf_init(&attr_priv->buf, + buf_size); + if (ret) { + DRM_ERROR( + "obj state buf init failed. buf_size=%d\n", + buf_size); + goto out; + } + } else { + /* Reset the buf parameters before filling data */ + attr_priv->buf.pos = 0; + attr_priv->buf.bytes = 0; + } + + /* Read the gfx device stats */ + ret = i915_gem_get_obj_info(&attr_priv->buf, dev, tgid); + if (ret) + goto out; + + ret = i915_error_ok(&attr_priv->buf); + if (ret) { + ret = 0; + goto copy_data; + } + if (buf_size >= GEM_OBJ_STAT_BUF_SIZE_MAX) { + DRM_DEBUG_DRIVER("obj stat buf size limit reached\n"); + ret = -ENOMEM; + goto out; + } else { + /* Try to reallocate buf of larger size */ + i915_error_state_buf_release(&attr_priv->buf); + buf_size *= 2; + + ret = i915_obj_state_buf_init(&attr_priv->buf, + buf_size); + if (ret) { + DRM_ERROR( + "obj stat buf init failed. buf_size=%d\n", + buf_size); + goto out; + } + goto retry; + } + } +copy_data: + + bytes_available = (long)attr_priv->buf.bytes - (long)off; + + if (bytes_available > 0) { + ret_count = count < bytes_available ? count : bytes_available; + memcpy(buf, attr_priv->buf.buf + off, ret_count); + } else + ret_count = 0; + +out: + mutex_unlock(&drm_global_mutex); + + return ret ?: ret_count; +} + +int i915_gem_create_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_gem_file_attr_priv *attr_priv; + struct bin_attribute *obj_attr; + struct drm_file *file_local; + int ret; + + /* + * Check for multiple drm files having same tgid. If found, copy the + * bin attribute into the new file priv. Otherwise allocate a new + * copy of bin attribute, and create its corresponding sysfs file. + */ + mutex_lock(&dev->struct_mutex); + list_for_each_entry(file_local, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv_local = + file_local->driver_priv; + + if (file_priv->tgid == file_priv_local->tgid) { + file_priv->obj_attr = file_priv_local->obj_attr; + mutex_unlock(&dev->struct_mutex); + return 0; + } + } + mutex_unlock(&dev->struct_mutex); + + obj_attr = kzalloc(sizeof(*obj_attr), GFP_KERNEL); + if (!obj_attr) { + DRM_ERROR("Alloc failed. Out of memory\n"); + ret = -ENOMEM; + goto out; + } + + attr_priv = kzalloc(sizeof(*attr_priv), GFP_KERNEL); + if (!attr_priv) { + DRM_ERROR("Alloc failed. Out of memory\n"); + ret = -ENOMEM; + goto out_obj_attr; + } + + snprintf(attr_priv->tgid_str, 16, "%d", task_tgid_nr(current)); + obj_attr->attr.name = attr_priv->tgid_str; + obj_attr->attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + obj_attr->size = 0; + obj_attr->read = i915_gem_read_objects; + + attr_priv->tgid = file_priv->tgid; + obj_attr->private = attr_priv; + + ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj, + obj_attr); + if (ret) { + DRM_ERROR( + "sysfs tgid file setup failed. tgid=%d, process:%s, ret:%d\n", + pid_nr(file_priv->tgid), file_priv->process_name, ret); + + goto out_attr_priv; + } + + file_priv->obj_attr = obj_attr; + return 0; + +out_attr_priv: + kfree(attr_priv); +out_obj_attr: + kfree(obj_attr); +out: + return ret; +} + +void i915_gem_remove_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_file *file_local; + int open_count = 1; + + /* + * The current drm file instance is already removed from filelist at + * this point. + * Check if this particular drm file being removed is the last one for + * that particular tgid, and no other instances for this tgid exist in + * the filelist. If so, remove the corresponding sysfs file entry also. + */ + list_for_each_entry(file_local, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv_local = + file_local->driver_priv; + + if (pid_nr(file_priv->tgid) == pid_nr(file_priv_local->tgid)) + open_count++; + } + + if (open_count == 1) { + struct i915_gem_file_attr_priv *attr_priv; + + if (WARN_ON(file_priv->obj_attr == NULL)) + return; + attr_priv = file_priv->obj_attr->private; + + sysfs_remove_bin_file(&dev_priv->memtrack_kobj, + file_priv->obj_attr); + + i915_error_state_buf_release(&attr_priv->buf); + kfree(file_priv->obj_attr->private); + kfree(file_priv->obj_attr); + } +} + +static struct bin_attribute i915_gem_client_state_attr = { + .attr.name = "i915_gem_meminfo", + .attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, + .size = 0, + .read = i915_gem_clients_state_read, +}; + +static struct attribute *memtrack_kobj_attrs[] = {NULL}; + +static struct kobj_type memtrack_kobj_type = { + .release = NULL, + .sysfs_ops = NULL, + .default_attrs = memtrack_kobj_attrs, +}; +#endif #else static void i915_setup_error_capture(struct device *kdev) {} static void i915_teardown_error_capture(struct device *kdev) {} @@ -623,6 +905,28 @@ void i915_setup_sysfs(struct drm_i915_private *dev_priv) DRM_ERROR("RPS sysfs setup failed\n"); i915_setup_error_capture(kdev); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Create the gfx_memtrack directory for memtrack sysfs files + */ + ret = kobject_init_and_add( + &dev_priv->memtrack_kobj, &memtrack_kobj_type, + &kdev->kobj, "gfx_memtrack"); + if (unlikely(ret != 0)) { + DRM_ERROR( + "i915 sysfs setup memtrack directory failed\n" + ); + kobject_put(&dev_priv->memtrack_kobj); + } else { + ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj, + &i915_gem_client_state_attr); + if (ret) + DRM_ERROR( + "i915_gem_client_state sysfs setup failed\n" + ); + } +#endif } void i915_teardown_sysfs(struct drm_i915_private *dev_priv) @@ -641,4 +945,11 @@ void i915_teardown_sysfs(struct drm_i915_private *dev_priv) sysfs_unmerge_group(&kdev->kobj, &rc6_attr_group); sysfs_unmerge_group(&kdev->kobj, &rc6p_attr_group); #endif + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + sysfs_remove_bin_file(&dev_priv->memtrack_kobj, + &i915_gem_client_state_attr); + kobject_del(&dev_priv->memtrack_kobj); + kobject_put(&dev_priv->memtrack_kobj); +#endif } From 8fe804082e352f1b647698895e0250365b5891c7 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 19 Sep 2018 14:17:45 +0800 Subject: [PATCH 0375/1276] drm/i915: Async work for hdcp authentication Each HDCP authentication, could take upto 5.1Sec, based on the downstream HDCP topology. Hence to avoid this much delay in the atomic_commit path, this patch schedules the HDCP authentication into a asynchronous work. Change-Id: I951f20d9db082c80c4323495c4cf290a6eed1238 Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdcp.c | 36 +++++++++++++++++----------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2951096bca0..9507d9086691 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15925,6 +15925,7 @@ static void intel_hpd_poll_fini(struct drm_device *dev) if (connector->hdcp_shim) { cancel_delayed_work_sync(&connector->hdcp_check_work); cancel_work_sync(&connector->hdcp_prop_work); + cancel_work_sync(&connector->hdcp_enable_work); } } drm_connector_list_iter_end(&conn_iter); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc61e96754f..25807569ec34 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -418,6 +418,7 @@ struct intel_connector { uint64_t hdcp_value; /* protected by hdcp_mutex */ struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; + struct work_struct hdcp_enable_work; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 0cc6a861bcf8..ea938b9146b0 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -679,8 +679,14 @@ static int _intel_hdcp_enable(struct intel_connector *connector) for (i = 0; i < tries; i++) { ret = intel_hdcp_auth(conn_to_dig_port(connector), connector->hdcp_shim); - if (!ret) + if (!ret) { + connector->hdcp_value = + DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&connector->hdcp_prop_work); + schedule_delayed_work(&connector->hdcp_check_work, + DRM_HDCP_CHECK_PERIOD_MS); return 0; + } DRM_DEBUG_KMS("HDCP Auth failure (%d)\n", ret); @@ -692,6 +698,17 @@ static int _intel_hdcp_enable(struct intel_connector *connector) return ret; } +static void intel_hdcp_enable_work(struct work_struct *work) +{ + struct intel_connector *connector = container_of(work, + struct intel_connector, + hdcp_enable_work); + + mutex_lock(&connector->hdcp_mutex); + _intel_hdcp_enable(connector); + mutex_unlock(&connector->hdcp_mutex); +} + static void intel_hdcp_check_work(struct work_struct *work) { struct intel_connector *connector = container_of(to_delayed_work(work), @@ -748,29 +765,20 @@ int intel_hdcp_init(struct intel_connector *connector, mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); INIT_WORK(&connector->hdcp_prop_work, intel_hdcp_prop_work); + INIT_WORK(&connector->hdcp_enable_work, intel_hdcp_enable_work); return 0; } int intel_hdcp_enable(struct intel_connector *connector) { - int ret; - if (!connector->hdcp_shim) return -ENOENT; mutex_lock(&connector->hdcp_mutex); - - ret = _intel_hdcp_enable(connector); - if (ret) - goto out; - - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; - schedule_work(&connector->hdcp_prop_work); - schedule_delayed_work(&connector->hdcp_check_work, - DRM_HDCP_CHECK_PERIOD_MS); -out: + schedule_work(&connector->hdcp_enable_work); mutex_unlock(&connector->hdcp_mutex); - return ret; + + return 0; } int intel_hdcp_disable(struct intel_connector *connector) From a03ddd161523ca66e36988a9b917c6135198e084 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 19 Sep 2018 14:18:50 +0800 Subject: [PATCH 0376/1276] drm/i915: Commit CP without modeset This patch commits the content protection change of a connector, without crtc modeset. This will give better user experience. Daniel vetter and Ville has mentioned that ville is developing some alternate approach for this kind of connector related commits. Till then it is preferred to have this solution in place. Change-Id: I95fd78f0c45851fda1453ba2d94ee634ee29bf2f Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_ddi.c | 7 ------ drivers/gpu/drm/i915/intel_display.c | 10 +++++++++ drivers/gpu/drm/i915/intel_drv.h | 7 ++++-- drivers/gpu/drm/i915/intel_hdcp.c | 32 ++++++++++++++++++++++++---- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c9af34861d9e..4c81548df7fa 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3009,11 +3009,6 @@ static void intel_enable_ddi(struct intel_encoder *encoder, intel_enable_ddi_hdmi(encoder, crtc_state, conn_state); else intel_enable_ddi_dp(encoder, crtc_state, conn_state); - - /* Enable hdcp if it's desired */ - if (conn_state->content_protection == - DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(to_intel_connector(conn_state->connector)); } static void intel_disable_ddi_dp(struct intel_encoder *encoder, @@ -3053,8 +3048,6 @@ static void intel_disable_ddi(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_hdcp_disable(to_intel_connector(old_conn_state->connector)); - if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI)) intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state); else diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9507d9086691..b3c0e61bf612 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12580,6 +12580,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_connector_state *old_conn_state, *new_conn_state; + struct drm_connector *connector; struct drm_crtc *crtc; struct intel_crtc_state *intel_cstate; u64 put_domains[I915_MAX_PIPES] = {}; @@ -12677,9 +12679,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) } } + for_each_oldnew_connector_in_state(state, connector, old_conn_state, + new_conn_state, i) + intel_hdcp_atomic_pre_commit(connector, old_conn_state, + new_conn_state); + /* Now enable the clocks, plane, pipe, and connectors that we set up. */ dev_priv->display.update_crtcs(state); + for_each_new_connector_in_state(state, connector, new_conn_state, i) + intel_hdcp_atomic_commit(connector, new_conn_state); + /* FIXME: We should call drm_atomic_helper_commit_hw_done() here * already, but still need the state for the delayed optimization. To * fix this: diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25807569ec34..f20ce3b3545a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1917,10 +1917,13 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con void intel_hdcp_atomic_check(struct drm_connector *connector, struct drm_connector_state *old_state, struct drm_connector_state *new_state); +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, + struct drm_connector_state *old_state, + struct drm_connector_state *new_state); +void intel_hdcp_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *new_state); int intel_hdcp_init(struct intel_connector *connector, const struct intel_hdcp_shim *hdcp_shim); -int intel_hdcp_enable(struct intel_connector *connector); -int intel_hdcp_disable(struct intel_connector *connector); int intel_hdcp_check_link(struct intel_connector *connector); bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port); diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index ea938b9146b0..7f87667753b3 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -806,7 +806,6 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, { uint64_t old_cp = old_state->content_protection; uint64_t new_cp = new_state->content_protection; - struct drm_crtc_state *crtc_state; if (!new_state->crtc) { /* @@ -827,10 +826,35 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) return; +} + +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, + struct drm_connector_state *old_state, + struct drm_connector_state *new_state) +{ + uint64_t old_cp = old_state->content_protection; + uint64_t new_cp = new_state->content_protection; + + /* + * Disable HDCP if the connector is becoming disabled, or if requested + * via the property. + */ + if ((!new_state->crtc && + old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) || + (new_state->crtc && + old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED && + new_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) + intel_hdcp_disable(to_intel_connector(connector)); +} + +void intel_hdcp_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *new_state) +{ + uint64_t new_cp = new_state->content_protection; - crtc_state = drm_atomic_get_new_crtc_state(new_state->state, - new_state->crtc); - crtc_state->mode_changed = true; + /* Enable hdcp if it's desired */ + if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) + intel_hdcp_enable(to_intel_connector(connector)); } /* Implements Part 3 of the HDCP authorization procedure */ From bfabd057831e38da856979a027c06b27a6b16af3 Mon Sep 17 00:00:00 2001 From: Badiuzzaman Iskhandar Date: Tue, 9 Oct 2018 15:51:18 +0800 Subject: [PATCH 0377/1276] MUST_REBASE [IOTG]: drm/i915: Allow late GuC/HuC loading This patch allows for the GuC and HuC to be loaded until the first device is open (instead of during the driver initialisation). Delaying the fw load prevents a load failure when the partition that the firmware is supposed to be on has not been mounted yet, as is the case in Android. During i915 init, if huc/guc enabled, the huc/guc fw sizes need to be known. As we defer the fw fetch later, the fw sizes can't be determined. ggtt_pin_bias relies on the fw size as well. MUST_REBASE: The hw_late code is part of a solution for loading firmware of a file system that has not been mounted yet. Only a problem on Android. This patch is a rewrote from similar patch in 4.11 Change-Id: Ic4f414deed9961d73c25434b3d8698ae9fbb425c Tracked-On: Signed-off-by: Badiuzzaman Iskhandar --- drivers/gpu/drm/i915/i915_drv.h | 3 ++ drivers/gpu/drm/i915/i915_gem.c | 48 ++++++++++++++++++++----- drivers/gpu/drm/i915/i915_gem_context.c | 44 +++++++++++++++++++---- drivers/gpu/drm/i915/intel_wopcm.c | 7 ++++ 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c2791ebd3fca..bf711b1e13eb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2137,6 +2137,8 @@ struct drm_i915_private { struct i915_pmu pmu; + bool contexts_ready; /* for deferred initialization */ + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. @@ -3174,6 +3176,7 @@ void i915_gem_reset_engine(struct intel_engine_cs *engine, void i915_gem_init_mmio(struct drm_i915_private *i915); int __must_check i915_gem_init(struct drm_i915_private *dev_priv); int __must_check i915_gem_init_hw(struct drm_i915_private *dev_priv); +int __must_check i915_gem_init_hw_late(struct drm_i915_private *dev_priv); void i915_gem_init_swizzling(struct drm_i915_private *dev_priv); void i915_gem_fini(struct drm_i915_private *dev_priv); void i915_gem_cleanup_engines(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4b31093239b2..b3e4923129a3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -6161,6 +6161,28 @@ static int __i915_gem_restart_engines(void *data) return 0; } +int i915_gem_init_hw_late(struct drm_i915_private *dev_priv) +{ + int ret; + + /* + * Place for things that can be delayed until the first context + * is open. For example, fw loading in android. + */ + + /* fetch firmware */ + intel_uc_init_misc(dev_priv); + + /* Load fw. We can't enable contexts until all firmware is loaded */ + ret = intel_uc_init_hw(dev_priv); + if (ret) { + DRM_ERROR("Late init: enabling uc failed (%d)\n", ret); + return ret; + } + + return 0; +} + int i915_gem_init_hw(struct drm_i915_private *dev_priv) { int ret; @@ -6219,11 +6241,17 @@ int i915_gem_init_hw(struct drm_i915_private *dev_priv) goto out; } - /* We can't enable contexts until all firmware is loaded */ - ret = intel_uc_init_hw(dev_priv); - if (ret) { - DRM_ERROR("Enabling uc failed (%d)\n", ret); - goto out; + /* + * Don't call i915_gem_init_hw_late() the very first time (during + * driver load); it will get called during first open instead. + * It should only be called on subsequent (re-initialization) passes. + */ + if (dev_priv->contexts_ready) { + ret = i915_gem_init_hw_late(dev_priv); + if (ret) + goto out; + } else { + DRM_DEBUG_DRIVER("Deferring late initialization\n"); } intel_mocs_init_l3cc_table(dev_priv); @@ -6386,9 +6414,13 @@ int i915_gem_init(struct drm_i915_private *dev_priv) if (ret) return ret; - ret = intel_uc_init_misc(dev_priv); - if (ret) - return ret; + /* + * ANDROID: fetch fw during drm_open instead + * due to filesystem is not up yet during driver init + * ret = intel_uc_init_misc(dev_priv); + * if (ret) + * return ret; + */ ret = intel_wopcm_init(&dev_priv->wopcm); if (ret) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index b10770cfccd2..e703779a503e 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -548,23 +548,55 @@ static int context_idr_cleanup(int id, void *p, void *data) return 0; } +int i915_gem_context_first_open(struct drm_i915_private *dev_priv) +{ + int ret; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + DRM_DEBUG_DRIVER("Late initialization starting\n"); + + intel_runtime_pm_get(dev_priv); + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + ret = i915_gem_init_hw_late(dev_priv); + if (ret == 0) + dev_priv->contexts_ready = true; + else + DRM_ERROR("Late initialization failed: %d\n", ret); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + + return ret; +} + int i915_gem_context_open(struct drm_i915_private *i915, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; struct i915_gem_context *ctx; + int ret = 0; idr_init(&file_priv->context_idr); mutex_lock(&i915->drm.struct_mutex); - ctx = i915_gem_create_context(i915, file_priv); - mutex_unlock(&i915->drm.struct_mutex); - if (IS_ERR(ctx)) { - idr_destroy(&file_priv->context_idr); - return PTR_ERR(ctx); + + if (!(i915->contexts_ready)) + ret = i915_gem_context_first_open(i915); + + if (ret == 0) { + ctx = i915_gem_create_context(i915, file_priv); + if (IS_ERR(ctx)) + ret = PTR_ERR(ctx); + + GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); } - GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); + mutex_unlock(&i915->drm.struct_mutex); + + if (ret) + idr_destroy(&file_priv->context_idr); return 0; } diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c index 74bf76f3fddc..5ff877ed7b12 100644 --- a/drivers/gpu/drm/i915/intel_wopcm.c +++ b/drivers/gpu/drm/i915/intel_wopcm.c @@ -207,6 +207,13 @@ int intel_wopcm_init(struct intel_wopcm *wopcm) wopcm->guc.base = guc_wopcm_base; wopcm->guc.size = guc_wopcm_size; + /* + * In deferred fw loading, we defer the intel_guc_init which will + * initialize the guc.ggtt_pin_bias. As it relies on wopcm size, + * set the ggtt_pin_bias after wopcm initialization + */ + i915->guc.ggtt_pin_bias = i915->wopcm.size - i915->wopcm.guc.base; + return 0; } From 2892d94880059d3bd1481d08b0e79d50ff52c2f2 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Tue, 9 Oct 2018 19:02:37 +0800 Subject: [PATCH 0378/1276] drm/i915: Passing the intel_connector to HDCP auth Reference of Intel_connector is needed at generic authentication flow for upcoming revocation check and downstream topology info gathering etc. As a preparation for storing the downstream topology information at intel_connector and accessig SRM revocated ID list from intel_connector, we are passing the intel_connector reference to hdcp authentication. Change-Id: I2cb3608ed1d3b408e3531fe15687a01f78d716b0 Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_hdcp.c | 143 ++++++++++++------------------ 1 file changed, 57 insertions(+), 86 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 7f87667753b3..a03a745544e2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -179,16 +179,60 @@ bool intel_hdcp_is_ksv_valid(u8 *ksv) return true; } +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) +{ + return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); +} + +/* Implements Part 2 of the HDCP authorization procedure */ static -int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim, - u8 *ksv_fifo, u8 num_downstream, u8 *bstatus) +int intel_hdcp_auth_downstream(struct intel_connector *connector) { - struct drm_i915_private *dev_priv; + struct intel_digital_port *intel_dig_port = + conn_to_dig_port(connector); + const struct intel_hdcp_shim *shim = connector->hdcp_shim; + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); u32 vprime, sha_text, sha_leftovers, rep_ctl; + u8 bstatus[2], num_downstream, *ksv_fifo; int ret, i, j, sha_idx; - dev_priv = intel_dig_port->base.base.dev->dev_private; + if(intel_dig_port == NULL) + return EINVAL; + + ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim); + if (ret) { + DRM_ERROR("KSV list failed to become ready (%d)\n", ret); + return ret; + } + + ret = shim->read_bstatus(intel_dig_port, bstatus); + if (ret) + return ret; + + if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) || + DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) { + DRM_ERROR("Max Topology Limit Exceeded\n"); + return -EPERM; + } + + /* + * When repeater reports 0 device count, HDCP1.4 spec allows disabling + * the HDCP encryption. That implies that repeater can't have its own + * display. As there is no consumption of encrypted content in the + * repeater with 0 downstream devices, we are failing the + * authentication. + */ + num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]); + if (num_downstream == 0) + return -EINVAL; + + ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL); + if (!ksv_fifo) + return -ENOMEM; + + ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo); + if (ret) + return ret; /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { @@ -394,79 +438,12 @@ int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port, return 0; } -/* Implements Part 2 of the HDCP authorization procedure */ -static -int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim) -{ - u8 bstatus[2], num_downstream, *ksv_fifo; - int ret, i, tries = 3; - - ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim); - if (ret) { - DRM_ERROR("KSV list failed to become ready (%d)\n", ret); - return ret; - } - - ret = shim->read_bstatus(intel_dig_port, bstatus); - if (ret) - return ret; - - if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) || - DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) { - DRM_ERROR("Max Topology Limit Exceeded\n"); - return -EPERM; - } - - /* - * When repeater reports 0 device count, HDCP1.4 spec allows disabling - * the HDCP encryption. That implies that repeater can't have its own - * display. As there is no consumption of encrypted content in the - * repeater with 0 downstream devices, we are failing the - * authentication. - */ - num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]); - if (num_downstream == 0) - return -EINVAL; - - ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream, GFP_KERNEL); - if (!ksv_fifo) - return -ENOMEM; - - ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo); - if (ret) - goto err; - - /* - * When V prime mismatches, DP Spec mandates re-read of - * V prime atleast twice. - */ - for (i = 0; i < tries; i++) { - ret = intel_hdcp_validate_v_prime(intel_dig_port, shim, - ksv_fifo, num_downstream, - bstatus); - if (!ret) - break; - } - - if (i == tries) { - DRM_ERROR("V Prime validation failed.(%d)\n", ret); - goto err; - } - - DRM_DEBUG_KMS("HDCP is enabled (%d downstream devices)\n", - num_downstream); - ret = 0; -err: - kfree(ksv_fifo); - return ret; -} - /* Implements Part 1 of the HDCP authorization procedure */ -static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim) +static int intel_hdcp_auth(struct intel_connector *connector) { - struct drm_i915_private *dev_priv; + struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector); + const struct intel_hdcp_shim *shim = connector->hdcp_shim; + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); enum port port; unsigned long r0_prime_gen_start; int ret, i, tries = 2; @@ -484,7 +461,8 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, } ri; bool repeater_present, hdcp_capable; - dev_priv = intel_dig_port->base.base.dev->dev_private; + if(intel_dig_port == NULL) + return EINVAL; port = intel_dig_port->base.port; @@ -612,18 +590,12 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, */ if (repeater_present) - return intel_hdcp_auth_downstream(intel_dig_port, shim); + return intel_hdcp_auth_downstream(connector); DRM_DEBUG_KMS("HDCP is enabled (no repeater present)\n"); return 0; } -static -struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) -{ - return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); -} - static int _intel_hdcp_disable(struct intel_connector *connector) { struct drm_i915_private *dev_priv = connector->base.dev->dev_private; @@ -677,8 +649,7 @@ static int _intel_hdcp_enable(struct intel_connector *connector) /* Incase of authentication failures, HDCP spec expects reauth. */ for (i = 0; i < tries; i++) { - ret = intel_hdcp_auth(conn_to_dig_port(connector), - connector->hdcp_shim); + ret = intel_hdcp_auth(connector); if (!ret) { connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; From 9cf6c391a188a5ff6c4b51cc988f0d0e24d86841 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 13 Dec 2017 23:23:08 +0530 Subject: [PATCH 0379/1276] drm: Add CP downstream_info property This patch adds a optional CP downstream info blob property to the connectors. This enables the Userspace to read the information of HDCP authenticated downstream topology. Driver will updated this blob with all downstream information at the end of the authentication. In case userspace configures this platform as repeater, then this information is needed for the authentication with upstream HDCP transmitter. Change-Id: Ic04109af286c0ab30f8a2ae3585700a7218e43fe Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/drm_atomic.c | 8 ++-- drivers/gpu/drm/drm_connector.c | 69 +++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 19 +++++++-- include/uapi/drm/drm_mode.h | 28 +++++++++++++ 4 files changed, 115 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 281cf9cbb44c..c52be07dbac7 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1511,11 +1511,9 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->scaling_mode; } else if (property == connector->content_protection_property) { *val = state->content_protection; - } else if (property == config->writeback_fb_id_property) { - /* Writeback framebuffer is one-shot, write and forget */ - *val = 0; - } else if (property == config->writeback_out_fence_ptr_property) { - *val = 0; + } else if (property == connector->cp_downstream_property) { + *val = connector->cp_downstream_blob_ptr ? + connector->cp_downstream_blob_ptr->base.id : 0; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 6011d769d50b..8b21a017edaa 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -241,6 +241,7 @@ int drm_connector_init(struct drm_device *dev, INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); connector->edid_blob_ptr = NULL; + connector->cp_downstream_blob_ptr = NULL; connector->status = connector_status_unknown; connector->display_info.panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; @@ -1346,6 +1347,42 @@ int drm_connector_attach_content_protection_property( } EXPORT_SYMBOL(drm_connector_attach_content_protection_property); +/** + * drm_connector_attach_cp_downstream_property - attach cp downstream + * property + * + * @connector: connector to attach cp downstream property on. + * + * This is used to add support for content protection downstream info on + * select connectors. when Intel platform is configured as repeater, + * this downstream info is used by userspace, to complete the repeater + * authentication of HDCP specification with upstream HDCP transmitter. + * + * The cp downstream will be set to &drm_connector_state.cp_downstream + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_cp_downstream_property( + struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "CP_Downstream_Info", 0); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&connector->base, prop, 0); + + connector->cp_downstream_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_cp_downstream_property); + /** * drm_mode_create_aspect_ratio_property - create aspect ratio property * @dev: DRM device @@ -1578,6 +1615,38 @@ void drm_connector_set_link_status_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_connector_set_link_status_property); +/** + * drm_mode_connector_update_cp_downstream_property - update the cp_downstream + * property of a connector + * @connector: drm connector + * @cp_downstream_info: new value of the cp_downstream property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's cp_downstream property. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_connector_update_cp_downstream_property( + struct drm_connector *connector, + const struct cp_downstream_info *info) +{ + struct drm_device *dev = connector->dev; + int ret; + + if (!info) + return -EINVAL; + + ret = drm_property_replace_global_blob(dev, + &connector->cp_downstream_blob_ptr, + sizeof(struct cp_downstream_info), + info, + &connector->base, + connector->cp_downstream_property); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_update_cp_downstream_property); + /** * drm_connector_init_panel_orientation_property - * initialize the connecters panel_orientation property diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 97ea41dc678f..9fd96c96d598 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -915,6 +915,13 @@ struct drm_connector { */ struct drm_property *content_protection_property; + /** + * @cp_downstream_property: DRM BLOB property for content + * protection downstream information. + */ + struct drm_property *cp_downstream_property; + struct drm_property_blob *cp_downstream_blob_ptr; + /** * @path_blob_ptr: * @@ -1184,20 +1191,24 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, u32 scaling_mode_mask); int drm_connector_attach_content_protection_property( struct drm_connector *connector); +int drm_connector_attach_cp_downstream_property( + struct drm_connector *connector); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); int drm_mode_create_content_type_property(struct drm_device *dev); void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, const struct drm_connector_state *conn_state); - int drm_mode_create_suggested_offset_properties(struct drm_device *dev); int drm_connector_set_path_property(struct drm_connector *connector, - const char *path); + const char *path); int drm_connector_set_tile_property(struct drm_connector *connector); int drm_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid); + const struct edid *edid); void drm_connector_set_link_status_property(struct drm_connector *connector, - uint64_t link_status); + uint64_t link_status); +int drm_mode_connector_update_cp_downstream_property( + struct drm_connector *connector, + const struct cp_downstream_info *info); int drm_connector_init_panel_orientation_property( struct drm_connector *connector, int width, int height); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 8d67243952f4..926d80323b55 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -209,6 +209,34 @@ extern "C" { #define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 #define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 +#define DRM_MODE_HDCP_KSV_LEN 5 +#define DRM_MODE_HDCP_MAX_DEVICE_CNT 127 + +struct cp_downstream_info { + + /* KSV of immediate HDCP Sink. In Little-Endian Format. */ + char bksv[DRM_MODE_HDCP_KSV_LEN]; + + /* Whether Immediate HDCP sink is a repeater? */ + bool is_repeater; + + /* Depth received from immediate downstream repeater */ + __u8 depth; + + /* Device count received from immediate downstream repeater */ + __u32 device_count; + + /* + * Max buffer required to hold ksv list received from immediate + * repeater. In this array first device_count * DRM_MODE_HDCP_KSV_LEN + * will hold the valid ksv bytes. + * If authentication specification is + * HDCP1.4 - each KSV's Bytes will be in Little-Endian format. + * HDCP2.2 - each KSV's Bytes will be in Big-Endian format. + */ + char ksv_list[DRM_MODE_HDCP_KSV_LEN * DRM_MODE_HDCP_MAX_DEVICE_CNT]; +}; + struct drm_mode_modeinfo { __u32 clock; __u16 hdisplay; From ebae0d663419fcabf3f05779863832b6d7d9c300 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Fri, 12 Oct 2018 19:39:37 +0800 Subject: [PATCH 0380/1276] drm: Add CP System Renewability Msg Property This patch adds a drm blob property to the selected connector. This property will be used to pass the SRM Blob ID from userspace to kernel. Revocated ksv list from SRM Table will be used by the kernel in the HDCP authentication. Change-Id: Ie2a09d3834386d8ba401762e1c717ea336a1193a Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/drm_atomic.c | 4 ++++ drivers/gpu/drm/drm_connector.c | 35 +++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 13 ++++++++++++ 3 files changed, 52 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c52be07dbac7..4dac6f035c61 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1421,6 +1421,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, return set_out_fence_for_connector(state->state, connector, fence_ptr); + } else if (property == connector->cp_srm_property) { + state->cp_srm_blob_id = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1511,6 +1513,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->scaling_mode; } else if (property == connector->content_protection_property) { *val = state->content_protection; + } else if (property == connector->cp_srm_property) { + *val = state->cp_srm_blob_id; } else if (property == connector->cp_downstream_property) { *val = connector->cp_downstream_blob_ptr ? connector->cp_downstream_blob_ptr->base.id : 0; diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 8b21a017edaa..60858b76374c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1383,6 +1383,41 @@ int drm_connector_attach_cp_downstream_property( } EXPORT_SYMBOL(drm_connector_attach_cp_downstream_property); +/** + * drm_connector_attach_cp_srm_property - attach cp srm + * property + * + * @connector: connector to attach cp srm property on. + * + * This is used to add support for sending the SRM table from userspace to + * kernel on selected connectors. Protected content provider will provide + * the system renewability Message(SRM) to userspace before requesting for + * HDCP on a port. Hence if a Port supports content protection (mostly HDCP) + * then this property will be attached to receive the SRM for revocation check + * of the ksvs. + * + * The srm blob id will be set to &drm_connector_state.cp_srm_blob_id + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_cp_srm_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CP_SRM", 0); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&connector->base, prop, 0); + connector->cp_srm_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_cp_srm_property); + + /** * drm_mode_create_aspect_ratio_property - create aspect ratio property * @dev: DRM device diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 9fd96c96d598..dc5f4d217bc0 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -460,6 +460,12 @@ struct drm_connector_state { * drm_writeback_signal_completion() */ struct drm_writeback_job *writeback_job; + + /** + * @cp_srm_blob_id: Connector property to pass the SRM table for content + * protection. This is most commonly used for HDCP. + */ + unsigned int cp_srm_blob_id; }; /** @@ -922,6 +928,12 @@ struct drm_connector { struct drm_property *cp_downstream_property; struct drm_property_blob *cp_downstream_blob_ptr; + /** + * @cp_srm_property: DRM BLOB property for content + * protection SRM information. + */ + struct drm_property *cp_srm_property; + /** * @path_blob_ptr: * @@ -1193,6 +1205,7 @@ int drm_connector_attach_content_protection_property( struct drm_connector *connector); int drm_connector_attach_cp_downstream_property( struct drm_connector *connector); +int drm_connector_attach_cp_srm_property(struct drm_connector *connector); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); int drm_mode_create_content_type_property(struct drm_device *dev); void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, From f30047634cf3e4f677ea3f21877554a3db7ddada Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 20 Dec 2017 21:47:01 +0530 Subject: [PATCH 0381/1276] drm/i915: Add HDCP SRM Blob parsing This patch adds a drm blob property to selected connectors. And also adds capability to parse the new srm blob passed through cp_srm_property. The revocated KSV list and their counts are stored in the intel_connector. This list should be used for revocation check of BKSVs in first stage HDCP authentication and for revocation check of ksv_fifo in second stage authentication. Signed-off-by: Ramalingam C --- drivers/gpu/drm/i915/intel_drv.h | 5 + drivers/gpu/drm/i915/intel_hdcp.c | 146 ++++++++++++++++++++++++++++++ include/drm/drm_hdcp.h | 14 +++ 3 files changed, 165 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index f20ce3b3545a..d4b307b531ec 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -419,6 +419,11 @@ struct intel_connector { struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; struct work_struct hdcp_enable_work; + + /* list of Revocated KSVs and their count from SRM blob Parsing */ + unsigned int revocated_ksv_cnt; + u8 *revocated_ksv_list; + u32 srm_blob_id; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index a03a745544e2..ca2c73469cb2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "intel_drv.h" #include "i915_reg.h" @@ -732,6 +733,10 @@ int intel_hdcp_init(struct intel_connector *connector, if (ret) return ret; + ret = drm_connector_attach_cp_srm_property(&connector->base); + if (ret) + return ret; + connector->hdcp_shim = hdcp_shim; mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); @@ -818,11 +823,152 @@ void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, intel_hdcp_disable(to_intel_connector(connector)); } +static u32 intel_hdcp_get_revocated_ksv_count(u8 *buf, u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz; + + do { + vrl_ksv_cnt = *buf; + ksv_count += vrl_ksv_cnt; + + vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1; + buf += vrl_sz; + parsed_bytes += vrl_sz; + } while (parsed_bytes < vrls_length); + + return ksv_count; +} + +static u32 intel_hdcp_get_revocated_ksvs(u8 *ksv_list, const u8 *buf, + u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0; + u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0; + + do { + vrl_ksv_cnt = *buf; + vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN; + + buf++; + + DRM_INFO("vrl: %d, Revoked KSVs: %d\n", vrl_idx++, + vrl_ksv_cnt); + memcpy(ksv_list, buf, vrl_ksv_sz); + + ksv_count += vrl_ksv_cnt; + ksv_list += vrl_ksv_sz; + buf += vrl_ksv_sz; + + parsed_bytes += (vrl_ksv_sz + 1); + } while (parsed_bytes < vrls_length); + + return ksv_count; +} + +static int intel_hdcp_parse_srm(struct drm_connector *connector, + struct drm_property_blob *blob) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct cp_srm_header *header; + u32 vrl_length, ksv_count; + u8 *buf; + + if (blob->length < (sizeof(struct cp_srm_header) + + DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length\n"); + return -EINVAL; + } + + header = (struct cp_srm_header *)blob->data; + + DRM_INFO("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", + header->spec_indicator.srm_id, + __swab16(header->srm_version), + header->srm_gen_no); + + WARN_ON(header->spec_indicator.reserved_hi || + header->spec_indicator.reserved_lo); + + if (header->spec_indicator.srm_id != DRM_HDCP_1_4_SRM_ID) { + DRM_ERROR("Invalid srm_id\n"); + return -EINVAL; + } + + buf = blob->data + sizeof(*header); + + vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2)); + + if (blob->length < (sizeof(struct cp_srm_header) + vrl_length) || + vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length or vrl length\n"); + return -EINVAL; + } + + /* Length of the all vrls combined */ + vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE); + + if (!vrl_length) { + DRM_DEBUG("No vrl found\n"); + return -EINVAL; + } + + buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE; + + + ksv_count = intel_hdcp_get_revocated_ksv_count(buf, vrl_length); + if (!ksv_count) { + DRM_INFO("Revocated KSV count is 0\n"); + return 0; + } + + kfree(intel_connector->revocated_ksv_list); + intel_connector->revocated_ksv_list = kzalloc(ksv_count * + DRM_HDCP_KSV_LEN, GFP_KERNEL); + if (!intel_connector->revocated_ksv_list) { + DRM_ERROR("Out of Memory\n"); + return -ENOMEM; + } + + if (intel_hdcp_get_revocated_ksvs(intel_connector->revocated_ksv_list, + buf, vrl_length) != ksv_count) { + intel_connector->revocated_ksv_cnt = 0; + kfree(intel_connector->revocated_ksv_list); + return -EINVAL; + } + + intel_connector->revocated_ksv_cnt = ksv_count; + return 0; +} + +static void intel_hdcp_update_srm(struct drm_connector *connector, + u32 srm_blob_id) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_property_blob *blob; + + blob = drm_property_lookup_blob(connector->dev, srm_blob_id); + if (!blob || !blob->data) + return; + + if (!intel_hdcp_parse_srm(connector, blob)) + intel_connector->srm_blob_id = srm_blob_id; + + drm_property_blob_put(blob); +} + void intel_hdcp_atomic_commit(struct drm_connector *connector, struct drm_connector_state *new_state) { + struct intel_connector *intel_connector = to_intel_connector(connector); uint64_t new_cp = new_state->content_protection; + if (new_state->cp_srm_blob_id && + new_state->cp_srm_blob_id != intel_connector->srm_blob_id) + intel_hdcp_update_srm(connector, new_state->cp_srm_blob_id); + /* Enable hdcp if it's desired */ if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(connector)); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 98e63d870139..f17eb2910bdb 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -38,4 +38,18 @@ #define DRM_HDCP_DDC_BSTATUS 0x41 #define DRM_HDCP_DDC_KSV_FIFO 0x43 +#define DRM_HDCP_1_4_SRM_ID 0x8 +#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_1_4_DCP_SIG_SIZE 40 + +struct cp_srm_header { + struct { + uint8_t reserved_hi:4; + uint8_t srm_id:4; + uint8_t reserved_lo; + } spec_indicator; + uint16_t srm_version; + uint8_t srm_gen_no; +} __packed; + #endif From f0e0770b53847da4367e2c0ae758cf1d14388657 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Thu, 21 Dec 2017 13:55:41 +0530 Subject: [PATCH 0382/1276] drm/i915: Add revocation check on Ksvs KSV list revocated by DCP LLC is provided as SRM Blob to kernel. Which is parsed and stored in intel_connector->revocated_ksv_list. This patch adds the revocation check for BKSV and KSV_FIFO in case of repeater. Signed-off-by: Ramalingam C --- drivers/gpu/drm/i915/intel_hdcp.c | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index ca2c73469cb2..20f78b572d1b 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -185,6 +185,45 @@ struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); } +static inline void intel_hdcp_print_ksv(u8 *ksv) +{ + DRM_DEBUG_KMS("\t%#04x, %#04x, %#04x, %#04x, %#04x\n", *ksv, + *(ksv + 1), *(ksv + 2), *(ksv + 3), *(ksv + 4)); +} + +/* Check if any of the KSV is revocated by DCP LLC through SRM table */ +static inline bool intel_hdcp_ksvs_revocated(struct intel_connector *connector, + u8 *ksvs, u32 ksv_count) +{ + u32 rev_ksv_cnt = connector->revocated_ksv_cnt; + u8 *rev_ksv_list = connector->revocated_ksv_list; + u32 cnt, i, j; + + /* If the Revocated ksv list is empty */ + if (!rev_ksv_cnt || !rev_ksv_list) + return false; + + for (cnt = 0; cnt < ksv_count; cnt++) { + rev_ksv_list = connector->revocated_ksv_list; + for (i = 0; i < rev_ksv_cnt; i++) { + for (j = 0; j < DRM_HDCP_KSV_LEN; j++) + if (*(ksvs + j) != *(rev_ksv_list + j)) { + break; + } else if (j == (DRM_HDCP_KSV_LEN - 1)) { + DRM_DEBUG_KMS("Revocated KSV is "); + intel_hdcp_print_ksv(ksvs); + return true; + } + /* Move the offset to next KSV in the revocated list */ + rev_ksv_list += DRM_HDCP_KSV_LEN; + } + + /* Iterate to next ksv_offset */ + ksvs += DRM_HDCP_KSV_LEN; + } + return false; +} + /* Implements Part 2 of the HDCP authorization procedure */ static int intel_hdcp_auth_downstream(struct intel_connector *connector) @@ -235,6 +274,11 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) if (ret) return ret; + if (intel_hdcp_ksvs_revocated(connector, ksv_fifo, num_downstream)) { + DRM_ERROR("Revocated Ksv(s) in ksv_fifo\n"); + return -EPERM; + } + /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { ret = shim->read_v_prime_part(intel_dig_port, i, &vprime); @@ -519,6 +563,11 @@ static int intel_hdcp_auth(struct intel_connector *connector) return -ENODEV; } + if (intel_hdcp_ksvs_revocated(connector, bksv.shim, 1)) { + DRM_ERROR("BKSV is revocated\n"); + return -EPERM; + } + I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]); I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]); From b0cb94088de18b9d7488aa2f1793b22b4c04dd95 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Tue, 16 Oct 2018 11:38:41 +0800 Subject: [PATCH 0383/1276] i915: Add cp_downstream property Implements drm blob property cp_downstream_info property on HDCP capable connectors. Downstream topology info is gathered across authentication stages and stored in intel_conenctor. When HDCP authentication is successful, new blob with latest downstream topology information is updated to cp_downstream_info property. Change-Id: I4646ce3c1e971573bab815e655c2bb66da170a40 Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_drv.h | 3 + drivers/gpu/drm/i915/intel_hdcp.c | 119 +++++++++++++++++++++--------- include/drm/drm_hdcp.h | 1 + 3 files changed, 88 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d4b307b531ec..d734030c2ac1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -424,6 +424,9 @@ struct intel_connector { unsigned int revocated_ksv_cnt; u8 *revocated_ksv_list; u32 srm_blob_id; + + /* Downstream info like, depth, device_count, bksv and ksv_list etc */ + struct cp_downstream_info *downstream_info; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 20f78b572d1b..d9869b64a3f1 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -234,25 +234,28 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) struct drm_i915_private *dev_priv = to_i915(connector->base.dev); u32 vprime, sha_text, sha_leftovers, rep_ctl; u8 bstatus[2], num_downstream, *ksv_fifo; - int ret, i, j, sha_idx; + int ret = 0, i, j, sha_idx; - if(intel_dig_port == NULL) - return EINVAL; + if(intel_dig_port == NULL) { + ret = -EINVAL; + goto out; + } ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim); if (ret) { DRM_ERROR("KSV list failed to become ready (%d)\n", ret); - return ret; + goto out; } ret = shim->read_bstatus(intel_dig_port, bstatus); if (ret) - return ret; + goto out; if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) || DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) { DRM_ERROR("Max Topology Limit Exceeded\n"); - return -EPERM; + ret = -EPERM; + goto out; } /* @@ -263,27 +266,38 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) * authentication. */ num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]); - if (num_downstream == 0) - return -EINVAL; + if (num_downstream == 0) { + ret = -EINVAL; + goto out; + } + + connector->downstream_info->device_count = num_downstream; + connector->downstream_info->depth = DRM_HDCP_DEPTH(bstatus[1]); ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL); - if (!ksv_fifo) - return -ENOMEM; + if (!ksv_fifo) { + ret = -ENOMEM; + goto out; + } ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo); if (ret) - return ret; + goto kfree_out; if (intel_hdcp_ksvs_revocated(connector, ksv_fifo, num_downstream)) { DRM_ERROR("Revocated Ksv(s) in ksv_fifo\n"); - return -EPERM; + ret = -EPERM; + goto kfree_out; } + memcpy(connector->downstream_info->ksv_list, ksv_fifo, + num_downstream * DRM_HDCP_KSV_LEN); + /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { ret = shim->read_v_prime_part(intel_dig_port, i, &vprime); if (ret) - return ret; + goto kfree_out; I915_WRITE(HDCP_SHA_V_PRIME(i), vprime); } @@ -313,7 +327,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; /* Programming guide writes this every 64 bytes */ sha_idx += sizeof(sha_text); @@ -336,7 +350,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; sha_leftovers = 0; sha_text = 0; sha_idx += sizeof(sha_text); @@ -354,21 +368,21 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) ret = intel_write_sha_text(dev_priv, bstatus[0] << 8 | bstatus[1]); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 32 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_0); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 16 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_16); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); } else if (sha_leftovers == 1) { @@ -379,21 +393,21 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) sha_text = (sha_text & 0xffffff00) >> 8; ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 32 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_0); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 24 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_8); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); } else if (sha_leftovers == 2) { @@ -402,7 +416,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) sha_text |= bstatus[0] << 24 | bstatus[1] << 16; ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 64 bits of M0 */ @@ -410,7 +424,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) for (i = 0; i < 2; i++) { ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); } } else if (sha_leftovers == 3) { @@ -419,33 +433,34 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) sha_text |= bstatus[0] << 24; ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 8 bits of text, 24 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_8); ret = intel_write_sha_text(dev_priv, bstatus[1]); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 32 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_0); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); /* Write 8 bits of M0 */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_24); ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); } else { DRM_DEBUG_KMS("Invalid number of leftovers %d\n", sha_leftovers); - return -EINVAL; + ret = -EINVAL; + goto kfree_out; } I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_TEXT_32); @@ -453,7 +468,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) while ((sha_idx % 64) < (64 - sizeof(sha_text))) { ret = intel_write_sha_text(dev_priv, 0); if (ret < 0) - return ret; + goto kfree_out; sha_idx += sizeof(sha_text); } @@ -465,7 +480,7 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) sha_text = (num_downstream * 5 + 10) * 8; ret = intel_write_sha_text(dev_priv, sha_text); if (ret < 0) - return ret; + goto kfree_out; /* Tell the HW we're done with the hash and wait for it to ACK */ I915_WRITE(HDCP_REP_CTL, rep_ctl | HDCP_SHA1_COMPLETE_HASH); @@ -473,14 +488,19 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) HDCP_SHA1_COMPLETE, HDCP_SHA1_COMPLETE, 1)) { DRM_DEBUG_KMS("Timed out waiting for SHA1 complete\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto kfree_out; } if (!(I915_READ(HDCP_REP_CTL) & HDCP_SHA1_V_MATCH)) { DRM_DEBUG_KMS("SHA-1 mismatch, HDCP failed\n"); - return -ENXIO; + ret = -ENXIO; + goto kfree_out; } - return 0; +kfree_out: + kfree(ksv_fifo); +out: + return ret; } /* Implements Part 1 of the HDCP authorization procedure */ @@ -568,15 +588,20 @@ static int intel_hdcp_auth(struct intel_connector *connector) return -EPERM; } + memcpy(connector->downstream_info->bksv, bksv.shim, + DRM_MODE_HDCP_KSV_LEN); + I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]); I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]); ret = shim->repeater_present(intel_dig_port, &repeater_present); if (ret) return ret; - if (repeater_present) + if (repeater_present) { I915_WRITE(HDCP_REP_CTL, intel_hdcp_get_repeater_ctl(intel_dig_port)); + connector->downstream_info->is_repeater = true; + } ret = shim->toggle_signalling(intel_dig_port, true); if (ret) @@ -669,6 +694,9 @@ static int _intel_hdcp_disable(struct intel_connector *connector) return ret; } + memset(connector->downstream_info, 0, + sizeof(struct cp_downstream_info)); + DRM_DEBUG_KMS("HDCP is disabled\n"); return 0; } @@ -715,6 +743,9 @@ static int _intel_hdcp_enable(struct intel_connector *connector) _intel_hdcp_disable(connector); } + memset(connector->downstream_info, 0, + sizeof(struct cp_downstream_info)); + DRM_ERROR("HDCP authentication failed (%d tries/%d)\n", tries, ret); return ret; } @@ -724,9 +755,18 @@ static void intel_hdcp_enable_work(struct work_struct *work) struct intel_connector *connector = container_of(work, struct intel_connector, hdcp_enable_work); + int ret; mutex_lock(&connector->hdcp_mutex); - _intel_hdcp_enable(connector); + ret = _intel_hdcp_enable(connector); + if (!ret) { + ret = drm_mode_connector_update_cp_downstream_property( + &connector->base, + connector->downstream_info); + if (ret) + DRM_ERROR("Downstream_property update failed.%d\n", + ret); + } mutex_unlock(&connector->hdcp_mutex); } @@ -786,6 +826,15 @@ int intel_hdcp_init(struct intel_connector *connector, if (ret) return ret; + ret = drm_connector_attach_cp_downstream_property(&connector->base); + if (ret) + return ret; + + connector->downstream_info = kzalloc(sizeof(struct cp_downstream_info), + GFP_KERNEL); + if (!connector->downstream_info) + return -ENOMEM; + connector->hdcp_shim = hdcp_shim; mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index f17eb2910bdb..8f5831d4b483 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -20,6 +20,7 @@ #define DRM_HDCP_V_PRIME_PART_LEN 4 #define DRM_HDCP_V_PRIME_NUM_PARTS 5 #define DRM_HDCP_NUM_DOWNSTREAM(x) (x & 0x7f) +#define DRM_HDCP_DEPTH(x) (x & 0x7) #define DRM_HDCP_MAX_CASCADE_EXCEEDED(x) (x & BIT(3)) #define DRM_HDCP_MAX_DEVICE_EXCEEDED(x) (x & BIT(7)) From 419b6f963d67c9e4ba596a2e9ce98a901a579d64 Mon Sep 17 00:00:00 2001 From: Badiuzzaman Iskhandar Date: Tue, 9 Oct 2018 15:31:22 +0800 Subject: [PATCH 0384/1276] REVERTME [IOTG]: drm/i915/: Add GuC v9.29 and HuC v1.07 firmware size In deferred firmware loading, the value of the firmware sizes were not known during i915 init. This will cause the i915 driver init to fail. Add the firmware size as workaround to enable huc/guc loading Change-Id: Id20318a9553e473e768ced036e24c14efd925ae2 Tracked-On: Signed-off-by: Badiuzzaman Iskhandar --- drivers/gpu/drm/i915/intel_wopcm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c index 5ff877ed7b12..10d1ea189775 100644 --- a/drivers/gpu/drm/i915/intel_wopcm.c +++ b/drivers/gpu/drm/i915/intel_wopcm.c @@ -63,6 +63,9 @@ #define GEN9_GUC_FW_RESERVED (128 * 1024) #define GEN9_GUC_WOPCM_OFFSET (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED) +#define GEN9_GUC_9_29_SIZE ((142 * 1024) + 768) +#define GEN9_HUC_1_07_SIZE ((150 * 1024) + 576) + /** * intel_wopcm_init_early() - Early initialization of the WOPCM. * @wopcm: pointer to intel_wopcm. @@ -155,8 +158,8 @@ static inline int check_hw_restriction(struct drm_i915_private *i915, int intel_wopcm_init(struct intel_wopcm *wopcm) { struct drm_i915_private *i915 = wopcm_to_i915(wopcm); - u32 guc_fw_size = intel_uc_fw_get_upload_size(&i915->guc.fw); - u32 huc_fw_size = intel_uc_fw_get_upload_size(&i915->huc.fw); + u32 guc_fw_size = GEN9_GUC_9_29_SIZE; + u32 huc_fw_size = GEN9_HUC_1_07_SIZE; u32 ctx_rsvd = context_reserved_size(i915); u32 guc_wopcm_base; u32 guc_wopcm_size; From 47993c29f975c73672c4533c0c4b05701976739a Mon Sep 17 00:00:00 2001 From: "Fan, Yugang" Date: Wed, 17 Oct 2018 10:39:24 +0800 Subject: [PATCH 0385/1276] drm/i915: Add interface to set ctm post offset In Android a 4x4 color transform matrix is provided to set color correction and color inversion, a post offset vector is also in the matrix. As drm only provide 3x3 CTM for color transform, the interface to set post offset is needed. So exposes it as CRTC property named as "CTM_POST_OFFSET". Rebased for kernel v4.19. Change-Id: I06ede732ef3f82c07fbee1b7e7e853cdfc01ad70 Signed-off-by: Gao Chun Signed-off-by: Yugang Fan --- drivers/gpu/drm/drm_atomic.c | 10 ++++++++++ drivers/gpu/drm/drm_atomic_helper.c | 4 ++++ drivers/gpu/drm/drm_color_mgmt.c | 15 ++++++++++++++- drivers/gpu/drm/drm_fb_helper.c | 1 + drivers/gpu/drm/drm_mode_config.c | 7 +++++++ drivers/gpu/drm/i915/intel_color.c | 29 +++++++++++++++++++++++++---- include/drm/drm_crtc.h | 9 +++++++++ include/drm/drm_mode_config.h | 5 +++++ include/uapi/drm/drm_mode.h | 7 +++++++ 9 files changed, 82 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 4dac6f035c61..b1fcc77e2add 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -574,6 +574,14 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, &replaced); state->color_mgmt_changed |= replaced; return ret; + } else if (property == config->ctm_post_offset_property) { + ret = drm_atomic_replace_property_blob_from_id(dev, + &state->ctm_post_offset, + val, + sizeof(struct drm_color_ctm_post_offset), -1, + &replaced); + state->color_mgmt_changed |= replaced; + return ret; } else if (property == config->gamma_lut_property) { ret = drm_atomic_replace_property_blob_from_id(dev, &state->gamma_lut, @@ -636,6 +644,8 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, *val = (state->degamma_lut) ? state->degamma_lut->base.id : 0; else if (property == config->ctm_property) *val = (state->ctm) ? state->ctm->base.id : 0; + else if (property == config->ctm_post_offset_property) + *val = (state->ctm_post_offset) ? state->ctm_post_offset->base.id : 0; else if (property == config->gamma_lut_property) *val = (state->gamma_lut) ? state->gamma_lut->base.id : 0; else if (property == config->prop_out_fence_ptr) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1bb4c318bdd4..85afc6813686 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3469,6 +3469,8 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, drm_property_blob_get(state->degamma_lut); if (state->ctm) drm_property_blob_get(state->ctm); + if (state->ctm_post_offset) + drm_property_blob_get(state->ctm_post_offset); if (state->gamma_lut) drm_property_blob_get(state->gamma_lut); state->mode_changed = false; @@ -3538,6 +3540,7 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) drm_property_blob_put(state->mode_blob); drm_property_blob_put(state->degamma_lut); drm_property_blob_put(state->ctm); + drm_property_blob_put(state->ctm_post_offset); drm_property_blob_put(state->gamma_lut); } EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); @@ -3934,6 +3937,7 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, /* Reset DEGAMMA_LUT and CTM properties. */ replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); + replaced |= drm_property_replace_blob(&crtc_state->ctm_post_offset, NULL); replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); crtc_state->color_mgmt_changed |= replaced; diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index b97e2de2c029..8fc8197059bd 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -64,6 +64,16 @@ * boot-up state too. Drivers can access the blob for the color conversion * matrix through &drm_crtc_state.ctm. * + * “CTM_POST_OFFSETâ€: + * Blob property to set post offset vector used to convert colors after + * applying ctm. The data is interpreted as a struct + * &drm_color_ctm_post_offset. + * + * Setting this to NULL (blob property value set to 0) means a pass-thru + * vector should be used. This is generally the driver boot-up state too. + * Drivers can access the blob for post offset vector through + * &drm_crtc_state.ctm_post_offset. + * * “GAMMA_LUTâ€: * Blob property to set the gamma lookup table (LUT) mapping pixel data * after the transformation matrix to data sent to the connector. The @@ -162,9 +172,12 @@ void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, degamma_lut_size); } - if (has_ctm) + if (has_ctm){ drm_object_attach_property(&crtc->base, config->ctm_property, 0); + drm_object_attach_property(&crtc->base, + config->ctm_post_offset_property, 0); + } if (gamma_lut_size) { drm_object_attach_property(&crtc->base, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 9628dd617826..a2be7e1d3f16 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1440,6 +1440,7 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); + replaced |= drm_property_replace_blob(&crtc_state->ctm_post_offset, NULL); replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, gamma_lut); crtc_state->color_mgmt_changed |= replaced; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 21e353bd3948..1d6ea4bb4838 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -332,6 +332,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.ctm_property = prop; + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB, + "CTM_POST_OFFSET", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.ctm_post_offset_property = prop; + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "GAMMA_LUT", 0); diff --git a/drivers/gpu/drm/i915/intel_color.c b/drivers/gpu/drm/i915/intel_color.c index c6a7beabd58d..8d41c8f7042a 100644 --- a/drivers/gpu/drm/i915/intel_color.c +++ b/drivers/gpu/drm/i915/intel_color.c @@ -230,13 +230,34 @@ static void ilk_load_csc_matrix(struct drm_crtc_state *crtc_state) if (INTEL_GEN(dev_priv) > 6) { uint16_t postoff = 0; + uint16_t postoff_red = 0; + uint16_t postoff_green = 0; + uint16_t postoff_blue = 0; - if (limited_color_range) + if (limited_color_range){ postoff = (16 * (1 << 12) / 255) & 0x1fff; + postoff_red = postoff; + postoff_green = postoff; + postoff_blue = postoff; + } + + if (crtc_state->ctm_post_offset) { + struct drm_color_ctm_post_offset *ctm_post_offset = + (struct drm_color_ctm_post_offset *)crtc_state->ctm_post_offset->data; + + /* Convert to U0.12 format. */ + postoff_red = ctm_post_offset->red >> 4; + postoff_green = ctm_post_offset->green >> 4; + postoff_blue = ctm_post_offset->blue >> 4; + + postoff_red = clamp_val(postoff_red, postoff, 0xfff); + postoff_green = clamp_val(postoff_green, postoff, 0xfff); + postoff_blue = clamp_val(postoff_blue, postoff, 0xfff); + } - I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff); - I915_WRITE(PIPE_CSC_POSTOFF_ME(pipe), postoff); - I915_WRITE(PIPE_CSC_POSTOFF_LO(pipe), postoff); + I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff_red); + I915_WRITE(PIPE_CSC_POSTOFF_ME(pipe), postoff_green); + I915_WRITE(PIPE_CSC_POSTOFF_LO(pipe), postoff_blue); I915_WRITE(PIPE_CSC_MODE(pipe), 0); } else { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 92e7fc7f05a4..5e4f8d83181d 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -265,6 +265,15 @@ struct drm_crtc_state { */ struct drm_property_blob *ctm; + /** + * @ctm_post_offset: + * + * Post offset used to convert colors after applying ctm. See + * drm_crtc_enable_color_mgmt().The blob (if not NULL) is a &struct + * drm_color_ctm_post_offset. + */ + struct drm_property_blob *ctm_post_offset; + /** * @gamma_lut: * diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index a0b202e1d69a..f606d2d2cbe1 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -747,6 +747,11 @@ struct drm_mode_config { * degamma LUT. */ struct drm_property *ctm_property; + /** + * @ctm_post_offset_property: Optional CRTC property to set the + * post offset used to convert colors after applying ctm. + */ + struct drm_property *ctm_post_offset_property; /** * @gamma_lut_property: Optional CRTC property to set the LUT used to * convert the colors, after the CTM matrix, to the gamma space of the diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 926d80323b55..6e9e2d66a83b 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -647,6 +647,13 @@ struct drm_color_ctm { __u64 matrix[9]; }; +struct drm_color_ctm_post_offset { + /* Data is U0.16 fixed point format. */ + __u16 red; + __u16 green; + __u16 blue; +}; + struct drm_color_lut { /* * Data is U0.16 fixed point format. From 3ad399d9cb99237dd5b32c7b254acc66c8c890fe Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 27 Sep 2017 17:18:48 -0700 Subject: [PATCH 0386/1276] ANDROID: add script to fetch android kernel config fragments The Android kernel config fragments now live in a separate repository. To prevent others from having to search for this location, add a script to fetch and unpack the fragments. Update .gitignore to include these fragments. Change-Id: If2d4a59b86e4573b0a9b3190025dfe4191870b46 Signed-off-by: Steve Muckle --- .gitignore | 3 +++ kernel/configs/android-fetch-configs.sh | 4 ++++ 2 files changed, 7 insertions(+) create mode 100755 kernel/configs/android-fetch-configs.sh diff --git a/.gitignore b/.gitignore index 97ba6b79834c..c62842976bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,6 @@ all.config # Kdevelop4 *.kdev4 + +# fetched Android config fragments +kernel/configs/android-*.cfg diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh new file mode 100755 index 000000000000..a5b56d4908dc --- /dev/null +++ b/kernel/configs/android-fetch-configs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv + From e0126cad42f9df08e73e40ba9f36629a5dc0102c Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 8 Nov 2013 15:24:19 -0800 Subject: [PATCH 0387/1276] ANDROID: arm: Fix dtb list when DTB_IMAGE_NAMES is empty In the 3.10 kernel, dtb-y is not defined in Makefile.boot anymore but in dts/Makefile, so it needs to be included too. Change-Id: I6d6fccf933709bcb6220ce8f12b4b9e2a7c40d63 Signed-off-by: Benoit Goby --- arch/arm/boot/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index a3af4dc08c3e..19c14972eb0d 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -16,6 +16,7 @@ OBJCOPYFLAGS :=-O binary -R .comment -S ifneq ($(MACHINE),) include $(MACHINE)/Makefile.boot endif +include $(srctree)/arch/arm/boot/dts/Makefile # Note: the following conditions must always be true: # ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET) From a1e8a3ba6e6a09abcc26b5ad4f389136d5ab4d53 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 25 Mar 2013 15:04:41 -0700 Subject: [PATCH 0388/1276] ANDROID: arm: add config option to build zImage/dtb combo Allows a defconfig to set a default dtb to concatenate with a zImage to create a zImage-dtb. Signed-off-by: Erik Gilling Change-Id: I34b643b1c49228fbae88a56e46c93c478089620d --- arch/arm/Kconfig | 15 +++++++++++++++ arch/arm/Makefile | 2 ++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e8cd55a5b04c..4c6ef59610c0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1839,6 +1839,21 @@ config DEPRECATED_PARAM_STRUCT This was deprecated in 2001 and announced to live on for 5 years. Some old boot loaders still use this way. +config BUILD_ARM_APPENDED_DTB_IMAGE + bool "Build a concatenated zImage/dtb by default" + depends on OF + help + Enabling this option will cause a concatenated zImage and DTB to + be built by default (instead of a standalone zImage.) The image + will built in arch/arm/boot/zImage-dtb. + +config BUILD_ARM_APPENDED_DTB_IMAGE_NAME + string "Default dtb name" + depends on BUILD_ARM_APPENDED_DTB_IMAGE + help + name of the dtb to append when building a concatenated + zImage/dtb. + # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. config ZBOOT_ROM_TEXT diff --git a/arch/arm/Makefile b/arch/arm/Makefile index d1516f85f25d..5235f58d9bba 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -303,6 +303,8 @@ libs-y := arch/arm/lib/ $(libs-y) boot := arch/arm/boot ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := $(boot)/xipImage +else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) +KBUILD_IMAGE := $(boot)/zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) else KBUILD_IMAGE := $(boot)/zImage endif From 2efbc3fc1d0fff94abe95f3b16770fa64519bd64 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 17 Apr 2013 16:58:36 -0700 Subject: [PATCH 0389/1276] ANDROID: arm: convert build of appended dtb zImage to list of dtbs Allow CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES to specify a space separated list of dtbs to append to the zImage, and name the resulting file zImage-dtb Change-Id: Ied5d0bafbd1d01fc1f109c15c4283de7029903c9 Signed-off-by: Colin Cross --- arch/arm/Kconfig | 14 +++++++------- arch/arm/Makefile | 5 ++++- arch/arm/boot/.gitignore | 1 + arch/arm/boot/Makefile | 12 ++++++++++++ scripts/Makefile.lib | 19 ++++++++++++++++++- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4c6ef59610c0..1d67c443f9be 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1843,16 +1843,16 @@ config BUILD_ARM_APPENDED_DTB_IMAGE bool "Build a concatenated zImage/dtb by default" depends on OF help - Enabling this option will cause a concatenated zImage and DTB to - be built by default (instead of a standalone zImage.) The image - will built in arch/arm/boot/zImage-dtb. + Enabling this option will cause a concatenated zImage and list of + DTBs to be built by default (instead of a standalone zImage.) + The image will built in arch/arm/boot/zImage-dtb -config BUILD_ARM_APPENDED_DTB_IMAGE_NAME - string "Default dtb name" +config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES + string "Default dtb names" depends on BUILD_ARM_APPENDED_DTB_IMAGE help - name of the dtb to append when building a concatenated - zImage/dtb. + Space separated list of names of dtbs to append when + building a concatenated zImage-dtb. # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 5235f58d9bba..7574964b83b9 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -304,7 +304,7 @@ boot := arch/arm/boot ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := $(boot)/xipImage else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := $(boot)/zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) +KBUILD_IMAGE := $(boot)/zImage-dtb else KBUILD_IMAGE := $(boot)/zImage endif @@ -358,6 +358,9 @@ ifeq ($(CONFIG_VDSO),y) $(Q)$(MAKE) $(build)=arch/arm/vdso $@ endif +zImage-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ + # We use MRPROPER_FILES and CLEAN_FILES now archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore index ce1c5ff746e7..025d8aaf013d 100644 --- a/arch/arm/boot/.gitignore +++ b/arch/arm/boot/.gitignore @@ -3,3 +3,4 @@ zImage xipImage bootpImage uImage +zImage-dtb diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 19c14972eb0d..3e3199ac1820 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -30,6 +30,14 @@ export ZRELADDR INITRD_PHYS PARAMS_PHYS targets := Image zImage xipImage bootpImage uImage +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) + ifeq ($(CONFIG_XIP_KERNEL),y) cmd_deflate_xip_data = $(CONFIG_SHELL) -c \ @@ -67,6 +75,10 @@ $(obj)/compressed/vmlinux: $(obj)/Image FORCE $(obj)/zImage: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy) +$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE + $(call if_changed,cat) + @echo ' Kernel: $@ is ready' + endif ifneq ($(LOADADDR),) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 61e596650ed3..22667fe49a22 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -57,8 +57,19 @@ real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) real-obj-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m))),$($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m)),$(m))) # DTB +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif + +targets += dtbs dtbs_install +targets += $(DTB_LIST) + +extra-y += $(DTB_LIST) + # If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built -extra-y += $(dtb-y) extra-$(CONFIG_OF_ALL_DTBS) += $(dtb-) # Add subdir path @@ -294,6 +305,12 @@ $(obj)/%.dtb: $(src)/%.dts $(DTC) FORCE dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) +# cat +# --------------------------------------------------------------------------- +# Concatentate multiple files together +quiet_cmd_cat = CAT $@ +cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false) + # Bzip2 # --------------------------------------------------------------------------- From 74070ec005ace629d17ad7017411625a81c25cbe Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Mon, 17 Mar 2014 13:44:01 -0700 Subject: [PATCH 0390/1276] ANDROID: arm64: add option to build Image.gz/dtb combo Allows a defconfig to set a list of dtbs to concatenate with an Image.gz to create a Image.gz-dtb. Includes 8adb162 arm64: Fix correct dtb clean-files location Change-Id: I0b462322d5c970f1fdf37baffece7ad058099f4a Signed-off-by: Alex Ray --- arch/arm64/Kconfig | 15 +++++++++++++++ arch/arm64/Makefile | 8 ++++++++ arch/arm64/boot/.gitignore | 1 + arch/arm64/boot/Makefile | 13 +++++++++++++ arch/arm64/boot/dts/Makefile | 14 ++++++++++++++ 5 files changed, 51 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1b1a0e95c751..db39f2721e57 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1280,6 +1280,21 @@ config DMI However, even with this option, the resultant kernel should continue to boot on existing non-UEFI platforms. +config BUILD_ARM64_APPENDED_DTB_IMAGE + bool "Build a concatenated Image.gz/dtb by default" + depends on OF + help + Enabling this option will cause a concatenated Image.gz and list of + DTBs to be built by default (instead of a standalone Image.gz.) + The image will built in arch/arm64/boot/Image.gz-dtb + +config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES + string "Default dtb names" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + help + Space separated list of names of dtbs to append when + building a concatenated Image.gz-dtb. + endmenu config COMPAT diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 106039d25e2f..33d3d02976f2 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -112,7 +112,12 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make boot := arch/arm64/boot +ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) +KBUILD_IMAGE := $(boot)/Image.gz-dtb +else KBUILD_IMAGE := $(boot)/Image.gz +endif + KBUILD_DTBS := dtbs all: Image.gz $(KBUILD_DTBS) @@ -138,6 +143,9 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts +Image.gz-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + PHONY += vdso_install vdso_install: $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso $@ diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore index 8dab0bb6ae66..eb3551131b1e 100644 --- a/arch/arm64/boot/.gitignore +++ b/arch/arm64/boot/.gitignore @@ -1,2 +1,3 @@ Image Image.gz +Image.gz-dtb diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 1f012c506434..5bb65a9e5d13 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -14,10 +14,20 @@ # Based on the ia64 boot/Makefile. # +include $(srctree)/arch/arm64/boot/dts/Makefile + OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S targets := Image Image.gz +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) + $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy) @@ -36,6 +46,9 @@ $(obj)/Image.lzma: $(obj)/Image FORCE $(obj)/Image.lzo: $(obj)/Image FORCE $(call if_changed,lzo) +$(obj)/Image.gz-dtb: $(obj)/Image.gz $(DTB_OBJS) FORCE + $(call if_changed,cat) + install: $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \ $(obj)/Image System.map "$(INSTALL_PATH)" diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 4690364d584b..ce19f2766412 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -26,3 +26,17 @@ subdir-y += synaptics subdir-y += ti subdir-y += xilinx subdir-y += zte + +targets += dtbs + +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +targets += $(DTB_LIST) + +dtbs: $(addprefix $(obj)/, $(DTB_LIST)) + +clean-files := dts/*.dtb *.dtb From 4db09bc028e5e2e6b1860cd6a69b45b2fa58b35e Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Fri, 22 Apr 2016 17:23:29 +0800 Subject: [PATCH 0391/1276] ANDROID: arm64: add option to build Image-dtb Some bootloaders couldn't decompress Image.gz-dtb. Change-Id: I698cd0c4ee6894e8d0655d88f3ecf4826c28a645 Signed-off-by: Haojian Zhuang Signed-off-by: John Stultz Signed-off-by: Dmitry Shmidt [AmitP: Folded following android-4.9 commit changes into this patch 56b70ac2447f ("ANDROID: ARM64: Ignore Image-dtb from git point of view")] Signed-off-by: Amit Pundir --- arch/arm64/Makefile | 2 +- arch/arm64/boot/.gitignore | 1 + arch/arm64/boot/Makefile | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 33d3d02976f2..89d3ca230b26 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -143,7 +143,7 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts -Image.gz-dtb: vmlinux scripts dtbs +Image-dtb Image.gz-dtb: vmlinux scripts dtbs $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ PHONY += vdso_install diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore index eb3551131b1e..34e35209fc2e 100644 --- a/arch/arm64/boot/.gitignore +++ b/arch/arm64/boot/.gitignore @@ -1,3 +1,4 @@ Image +Image-dtb Image.gz Image.gz-dtb diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 5bb65a9e5d13..2c8cb864315e 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -34,6 +34,9 @@ $(obj)/Image: vmlinux FORCE $(obj)/Image.bz2: $(obj)/Image FORCE $(call if_changed,bzip2) +$(obj)/Image-dtb: $(obj)/Image $(DTB_OBJS) FORCE + $(call if_changed,cat) + $(obj)/Image.gz: $(obj)/Image FORCE $(call if_changed,gzip) From 9c9c9eefa18f585415b1d73e7783eaadb4989852 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 28 Mar 2017 13:30:18 -0700 Subject: [PATCH 0392/1276] ANDROID: arm64: Allow to choose appended kernel image By default appended kernel image is Image.gz-dtb. New config option BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME allows to choose between Image.gz-dtb and Image-dtb. Change-Id: I1c71b85136f1beeb61782e4646820718c1ccd7e4 Signed-off-by: Dmitry Shmidt [AmitP: Add BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME as one of the default build targets to align with upstream commit 06995804b57 ("arm64: Use full path in KBUILD_IMAGE definition")] Signed-off-by: Amit Pundir --- arch/arm64/Kconfig | 20 ++++++++++++++++++++ arch/arm64/Makefile | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index db39f2721e57..15d659d62db1 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1288,6 +1288,26 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE DTBs to be built by default (instead of a standalone Image.gz.) The image will built in arch/arm64/boot/Image.gz-dtb +choice + prompt "Appended DTB Kernel Image name" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + help + Enabling this option will cause a specific kernel image Image or + Image.gz to be used for final image creation. + The image will built in arch/arm64/boot/IMAGE-NAME-dtb + + config IMG_GZ_DTB + bool "Image.gz-dtb" + config IMG_DTB + bool "Image-dtb" +endchoice + +config BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME + string + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + default "Image.gz-dtb" if IMG_GZ_DTB + default "Image-dtb" if IMG_DTB + config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES string "Default dtb names" depends on BUILD_ARM64_APPENDED_DTB_IMAGE diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 89d3ca230b26..2dbca8c11498 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -113,14 +113,14 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make boot := arch/arm64/boot ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := $(boot)/Image.gz-dtb +KBUILD_IMAGE := $(boot)/$(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) else KBUILD_IMAGE := $(boot)/Image.gz endif KBUILD_DTBS := dtbs -all: Image.gz $(KBUILD_DTBS) +all: Image.gz $(KBUILD_DTBS) $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) Image: vmlinux From 5c6c193d2a1580ed1a53906f706242f0f6bd1a2d Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 6 Jun 2017 17:04:42 -0700 Subject: [PATCH 0393/1276] ANDROID: binder: add support for RT prio inheritance. Adds support for SCHED_BATCH/SCHED_FIFO/SCHED_RR priority inheritance. Change-Id: I71f356e476be2933713a0ecfa2cc31aa141e2dc6 Signed-off-by: Martijn Coenen [AmitP: Include for struct sched_param] Signed-off-by: Amit Pundir --- drivers/android/binder.c | 164 ++++++++++++++++++++++++++++++++------- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d58763b6b009..fe2d92e28105 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -73,6 +73,7 @@ #include #include +#include #include @@ -462,6 +463,22 @@ enum binder_deferred_state { BINDER_DEFERRED_RELEASE = 0x04, }; +/** + * struct binder_priority - scheduler policy and priority + * @sched_policy scheduler policy + * @prio [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT + * + * The binder driver supports inheriting the following scheduler policies: + * SCHED_NORMAL + * SCHED_BATCH + * SCHED_FIFO + * SCHED_RR + */ +struct binder_priority { + unsigned int sched_policy; + int prio; +}; + /** * struct binder_proc - binder process bookkeeping * @proc_node: element for binder_procs list @@ -540,7 +557,7 @@ struct binder_proc { int requested_threads; int requested_threads_started; int tmp_ref; - long default_priority; + struct binder_priority default_priority; struct dentry *debugfs_entry; struct binder_alloc alloc; struct binder_context *context; @@ -625,8 +642,8 @@ struct binder_transaction { struct binder_buffer *buffer; unsigned int code; unsigned int flags; - long priority; - long saved_priority; + struct binder_priority priority; + struct binder_priority saved_priority; kuid_t sender_euid; /** * @lock: protects @from, @to_proc, and @to_thread @@ -1107,22 +1124,93 @@ static void binder_wakeup_proc_ilocked(struct binder_proc *proc) binder_wakeup_thread_ilocked(proc, thread, /* sync = */false); } -static void binder_set_nice(long nice) +static bool is_rt_policy(int policy) +{ + return policy == SCHED_FIFO || policy == SCHED_RR; +} + +static bool is_fair_policy(int policy) +{ + return policy == SCHED_NORMAL || policy == SCHED_BATCH; +} + +static bool binder_supported_policy(int policy) +{ + return is_fair_policy(policy) || is_rt_policy(policy); +} + +static int to_userspace_prio(int policy, int kernel_priority) +{ + if (is_fair_policy(policy)) + return PRIO_TO_NICE(kernel_priority); + else + return MAX_USER_RT_PRIO - 1 - kernel_priority; +} + +static int to_kernel_prio(int policy, int user_priority) +{ + if (is_fair_policy(policy)) + return NICE_TO_PRIO(user_priority); + else + return MAX_USER_RT_PRIO - 1 - user_priority; +} + +static void binder_set_priority(struct task_struct *task, + struct binder_priority desired) { - long min_nice; + int priority; /* user-space prio value */ + bool has_cap_nice; + unsigned int policy = desired.sched_policy; - if (can_nice(current, nice)) { - set_user_nice(current, nice); + if (task->policy == policy && task->normal_prio == desired.prio) return; + + has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE); + + priority = to_userspace_prio(policy, desired.prio); + + if (is_rt_policy(policy) && !has_cap_nice) { + long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO); + + if (max_rtprio == 0) { + policy = SCHED_NORMAL; + priority = MIN_NICE; + } else if (priority > max_rtprio) { + priority = max_rtprio; + } } - min_nice = rlimit_to_nice(rlimit(RLIMIT_NICE)); - binder_debug(BINDER_DEBUG_PRIORITY_CAP, - "%d: nice value %ld not allowed use %ld instead\n", - current->pid, nice, min_nice); - set_user_nice(current, min_nice); - if (min_nice <= MAX_NICE) - return; - binder_user_error("%d RLIMIT_NICE not set\n", current->pid); + + if (is_fair_policy(policy) && !has_cap_nice) { + long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE)); + + if (min_nice > MAX_NICE) { + binder_user_error("%d RLIMIT_NICE not set\n", + task->pid); + return; + } else if (priority < min_nice) { + priority = min_nice; + } + } + + if (policy != desired.sched_policy || + to_kernel_prio(policy, priority) != desired.prio) + binder_debug(BINDER_DEBUG_PRIORITY_CAP, + "%d: priority %d not allowed, using %d instead\n", + task->pid, desired.prio, + to_kernel_prio(policy, priority)); + + /* Set the actual priority */ + if (task->policy != policy || is_rt_policy(policy)) { + struct sched_param params; + + params.sched_priority = is_rt_policy(policy) ? priority : 0; + + sched_setscheduler_nocheck(task, + policy | SCHED_RESET_ON_FORK, + ¶ms); + } + if (is_fair_policy(policy)) + set_user_nice(task, priority); } static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, @@ -1207,7 +1295,8 @@ static struct binder_node *binder_init_node_ilocked( node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; - node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->min_priority = NICE_TO_PRIO( + flags & FLAT_BINDER_FLAG_PRIORITY_MASK); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); @@ -2780,7 +2869,7 @@ static void binder_transaction(struct binder_proc *proc, } thread->transaction_stack = in_reply_to->to_parent; binder_inner_proc_unlock(proc); - binder_set_nice(in_reply_to->saved_priority); + binder_set_priority(current, in_reply_to->saved_priority); target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; @@ -2953,7 +3042,15 @@ static void binder_transaction(struct binder_proc *proc, t->to_thread = target_thread; t->code = tr->code; t->flags = tr->flags; - t->priority = task_nice(current); + if (!(t->flags & TF_ONE_WAY) && + binder_supported_policy(current->policy)) { + /* Inherit supported policies for synchronous transactions */ + t->priority.sched_policy = current->policy; + t->priority.prio = current->normal_prio; + } else { + /* Otherwise, fall back to the default priority */ + t->priority = target_proc->default_priority; + } trace_binder_transaction(reply, t, target_node); @@ -3865,7 +3962,7 @@ static int binder_thread_read(struct binder_proc *proc, wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } - binder_set_nice(proc->default_priority); + binder_set_priority(current, proc->default_priority); } if (non_block) { @@ -4080,16 +4177,17 @@ static int binder_thread_read(struct binder_proc *proc, BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; + struct binder_priority prio = t->priority; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; - t->saved_priority = task_nice(current); - if (t->priority < target_node->min_priority && - !(t->flags & TF_ONE_WAY)) - binder_set_nice(t->priority); - else if (!(t->flags & TF_ONE_WAY) || - t->saved_priority > target_node->min_priority) - binder_set_nice(target_node->min_priority); + t->saved_priority.sched_policy = current->policy; + t->saved_priority.prio = current->normal_prio; + if (target_node->min_priority < t->priority.prio) { + prio.sched_policy = SCHED_NORMAL; + prio.prio = target_node->min_priority; + } + binder_set_priority(current, prio); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; @@ -4767,7 +4865,14 @@ static int binder_open(struct inode *nodp, struct file *filp) proc->tsk = current->group_leader; mutex_init(&proc->files_lock); INIT_LIST_HEAD(&proc->todo); - proc->default_priority = task_nice(current); + if (binder_supported_policy(current->policy)) { + proc->default_priority.sched_policy = current->policy; + proc->default_priority.prio = current->normal_prio; + } else { + proc->default_priority.sched_policy = SCHED_NORMAL; + proc->default_priority.prio = NICE_TO_PRIO(0); + } + binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; @@ -5061,13 +5166,14 @@ static void print_binder_transaction_ilocked(struct seq_file *m, spin_lock(&t->lock); to_proc = t->to_proc; seq_printf(m, - "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld r%d", + "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %d:%d r%d", prefix, t->debug_id, t, t->from ? t->from->proc->pid : 0, t->from ? t->from->pid : 0, to_proc ? to_proc->pid : 0, t->to_thread ? t->to_thread->pid : 0, - t->code, t->flags, t->priority, t->need_reply); + t->code, t->flags, t->priority.sched_policy, + t->priority.prio, t->need_reply); spin_unlock(&t->lock); if (proc != to_proc) { From b4d4493cdda22ba6357a1538649410e916e5904a Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 7 Jun 2017 09:29:14 -0700 Subject: [PATCH 0394/1276] ANDROID: binder: add min sched_policy to node. This change adds flags to flat_binder_object.flags to allow indicating a minimum scheduling policy for the node. It also clarifies the valid value range for the priority bits in the flags. Internally, we use the priority map that the kernel uses, e.g. [0..99] for real-time policies and [100..139] for the SCHED_NORMAL/SCHED_BATCH policies. Bug: 34461621 Bug: 37293077 Change-Id: I12438deecb53df432da18c6fc77460768ae726d2 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 26 ++++++++++++++---- include/uapi/linux/android/binder.h | 41 ++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index fe2d92e28105..446d5b30bb9a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -351,6 +351,8 @@ struct binder_error { * and by @lock) * @has_async_transaction: async transaction to node in progress * (protected by @lock) + * @sched_policy: minimum scheduling policy for node + * (invariant after initialized) * @accept_fds: file descriptor operations supported for node * (invariant after initialized) * @min_priority: minimum scheduling priority @@ -390,6 +392,7 @@ struct binder_node { /* * invariant after initialization */ + u8 sched_policy:2; u8 accept_fds:1; u8 min_priority; }; @@ -1263,6 +1266,7 @@ static struct binder_node *binder_init_node_ilocked( binder_uintptr_t ptr = fp ? fp->binder : 0; binder_uintptr_t cookie = fp ? fp->cookie : 0; __u32 flags = fp ? fp->flags : 0; + s8 priority; assert_spin_locked(&proc->inner_lock); @@ -1295,8 +1299,10 @@ static struct binder_node *binder_init_node_ilocked( node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; - node->min_priority = NICE_TO_PRIO( - flags & FLAT_BINDER_FLAG_PRIORITY_MASK); + priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; + node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); @@ -4183,8 +4189,17 @@ static int binder_thread_read(struct binder_proc *proc, tr.cookie = target_node->cookie; t->saved_priority.sched_policy = current->policy; t->saved_priority.prio = current->normal_prio; - if (target_node->min_priority < t->priority.prio) { - prio.sched_policy = SCHED_NORMAL; + if (target_node->min_priority < t->priority.prio || + (target_node->min_priority == t->priority.prio && + target_node->sched_policy == SCHED_FIFO)) { + /* + * In case the minimum priority on the node is + * higher (lower value), use that priority. If + * the priority is the same, but the node uses + * SCHED_FIFO, prefer SCHED_FIFO, since it can + * run unbounded, unlike SCHED_RR. + */ + prio.sched_policy = target_node->sched_policy; prio.prio = target_node->min_priority; } binder_set_priority(current, prio); @@ -5291,8 +5306,9 @@ static void print_binder_node_nilocked(struct seq_file *m, hlist_for_each_entry(ref, &node->refs, node_entry) count++; - seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d tr %d", + seq_printf(m, " node %d: u%016llx c%016llx pri %d:%d hs %d hw %d ls %d lw %d is %d iw %d tr %d", node->debug_id, (u64)node->ptr, (u64)node->cookie, + node->sched_policy, node->min_priority, node->has_strong_ref, node->has_weak_ref, node->local_strong_refs, node->local_weak_refs, node->internal_strong_refs, count, node->tmp_refs); diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index bfaec6903b8b..3fe109467ee3 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -38,9 +38,48 @@ enum { BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), }; -enum { +/** + * enum flat_binder_object_shifts: shift values for flat_binder_object_flags + * @FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: shift for getting scheduler policy. + * + */ +enum flat_binder_object_shifts { + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9, +}; + +/** + * enum flat_binder_object_flags - flags for use in flat_binder_object.flags + */ +enum flat_binder_object_flags { + /** + * @FLAT_BINDER_FLAG_PRIORITY_MASK: bit-mask for min scheduler priority + * + * These bits can be used to set the minimum scheduler priority + * at which transactions into this node should run. Valid values + * in these bits depend on the scheduler policy encoded in + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK. + * + * For SCHED_NORMAL/SCHED_BATCH, the valid range is between [-20..19] + * For SCHED_FIFO/SCHED_RR, the value can run between [1..99] + */ FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + /** + * @FLAT_BINDER_FLAG_ACCEPTS_FDS: whether the node accepts fds. + */ FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, + /** + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK: bit-mask for scheduling policy + * + * These two bits can be used to set the min scheduling policy at which + * transactions on this node should run. These match the UAPI + * scheduler policy values, eg: + * 00b: SCHED_NORMAL + * 01b: SCHED_FIFO + * 10b: SCHED_RR + * 11b: SCHED_BATCH + */ + FLAT_BINDER_FLAG_SCHED_POLICY_MASK = + 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT, }; #ifdef BINDER_IPC_32BIT From 7957a4f93c02220e5bce34eddd91da096a243591 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 7 Jun 2017 10:02:12 -0700 Subject: [PATCH 0395/1276] ANDROID: binder: improve priority inheritance. By raising the priority of a thread selected for a transaction *before* we wake it up. Delay restoring the priority when doing a reply until after we wake-up the process receiving the reply. Change-Id: Ic332e4e0ed7d2d3ca6ab1034da4629c9eadd3405 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 74 ++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 446d5b30bb9a..fe7ec579e489 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -610,6 +610,7 @@ enum { * @is_dead: thread is dead and awaiting free * when outstanding transactions are cleaned up * (protected by @proc->inner_lock) + * @task: struct task_struct for this thread * * Bookkeeping structure for binder threads. */ @@ -629,6 +630,7 @@ struct binder_thread { struct binder_stats stats; atomic_t tmp_ref; bool is_dead; + struct task_struct *task; }; struct binder_transaction { @@ -647,6 +649,7 @@ struct binder_transaction { unsigned int flags; struct binder_priority priority; struct binder_priority saved_priority; + bool set_priority_called; kuid_t sender_euid; /** * @lock: protects @from, @to_proc, and @to_thread @@ -1216,6 +1219,38 @@ static void binder_set_priority(struct task_struct *task, set_user_nice(task, priority); } +static void binder_transaction_priority(struct task_struct *task, + struct binder_transaction *t, + struct binder_priority node_prio) +{ + struct binder_priority desired_prio; + + if (t->set_priority_called) + return; + + t->set_priority_called = true; + t->saved_priority.sched_policy = task->policy; + t->saved_priority.prio = task->normal_prio; + + desired_prio.prio = t->priority.prio; + desired_prio.sched_policy = t->priority.sched_policy; + + if (node_prio.prio < t->priority.prio || + (node_prio.prio == t->priority.prio && + node_prio.sched_policy == SCHED_FIFO)) { + /* + * In case the minimum priority on the node is + * higher (lower value), use that priority. If + * the priority is the same, but the node uses + * SCHED_FIFO, prefer SCHED_FIFO, since it can + * run unbounded, unlike SCHED_RR. + */ + desired_prio = node_prio; + } + + binder_set_priority(task, desired_prio); +} + static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, binder_uintptr_t ptr) { @@ -2728,11 +2763,15 @@ static bool binder_proc_transaction(struct binder_transaction *t, struct binder_thread *thread) { struct binder_node *node = t->buffer->target_node; + struct binder_priority node_prio; bool oneway = !!(t->flags & TF_ONE_WAY); bool pending_async = false; BUG_ON(!node); binder_node_lock(node); + node_prio.prio = node->min_priority; + node_prio.sched_policy = node->sched_policy; + if (oneway) { BUG_ON(thread); if (node->has_async_transaction) { @@ -2753,12 +2792,14 @@ static bool binder_proc_transaction(struct binder_transaction *t, if (!thread && !pending_async) thread = binder_select_thread_ilocked(proc); - if (thread) + if (thread) { + binder_transaction_priority(thread->task, t, node_prio); binder_enqueue_thread_work_ilocked(thread, &t->work); - else if (!pending_async) + } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); - else + } else { binder_enqueue_work_ilocked(&t->work, &node->async_todo); + } if (!pending_async) binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); @@ -2875,7 +2916,6 @@ static void binder_transaction(struct binder_proc *proc, } thread->transaction_stack = in_reply_to->to_parent; binder_inner_proc_unlock(proc); - binder_set_priority(current, in_reply_to->saved_priority); target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; @@ -3285,6 +3325,7 @@ static void binder_transaction(struct binder_proc *proc, binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); + binder_set_priority(current, in_reply_to->saved_priority); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); @@ -3388,6 +3429,7 @@ static void binder_transaction(struct binder_proc *proc, BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { + binder_set_priority(current, in_reply_to->saved_priority); thread->return_error.cmd = BR_TRANSACTION_COMPLETE; binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); @@ -4183,26 +4225,13 @@ static int binder_thread_read(struct binder_proc *proc, BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; - struct binder_priority prio = t->priority; + struct binder_priority node_prio; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; - t->saved_priority.sched_policy = current->policy; - t->saved_priority.prio = current->normal_prio; - if (target_node->min_priority < t->priority.prio || - (target_node->min_priority == t->priority.prio && - target_node->sched_policy == SCHED_FIFO)) { - /* - * In case the minimum priority on the node is - * higher (lower value), use that priority. If - * the priority is the same, but the node uses - * SCHED_FIFO, prefer SCHED_FIFO, since it can - * run unbounded, unlike SCHED_RR. - */ - prio.sched_policy = target_node->sched_policy; - prio.prio = target_node->min_priority; - } - binder_set_priority(current, prio); + node_prio.sched_policy = target_node->sched_policy; + node_prio.prio = target_node->min_priority; + binder_transaction_priority(current, t, node_prio); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; @@ -4380,6 +4409,8 @@ static struct binder_thread *binder_get_thread_ilocked( binder_stats_created(BINDER_STAT_THREAD); thread->proc = proc; thread->pid = current->pid; + get_task_struct(current); + thread->task = current; atomic_set(&thread->tmp_ref, 0); init_waitqueue_head(&thread->wait); INIT_LIST_HEAD(&thread->todo); @@ -4430,6 +4461,7 @@ static void binder_free_thread(struct binder_thread *thread) BUG_ON(!list_empty(&thread->todo)); binder_stats_deleted(BINDER_STAT_THREAD); binder_proc_dec_tmpref(thread->proc); + put_task_struct(thread->task); kfree(thread); } From b00e803b3cd9bd9436f80711fb60bc08f7ac5140 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 23 Jun 2017 10:13:43 -0700 Subject: [PATCH 0396/1276] ANDROID: binder: add RT inheritance flag to node. Allows a binder node to specify whether it wants to inherit real-time scheduling policy from a caller. Change-Id: I375b6094bf441c19f19cba06d5a6be02cd07d714 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 22 +++++++++++++++++----- include/uapi/linux/android/binder.h | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index fe7ec579e489..205863a07b89 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -357,6 +357,8 @@ struct binder_error { * (invariant after initialized) * @min_priority: minimum scheduling priority * (invariant after initialized) + * @inherit_rt: inherit RT scheduling policy from caller + * (invariant after initialized) * @async_todo: list of async work items * (protected by @proc->inner_lock) * @@ -393,6 +395,7 @@ struct binder_node { * invariant after initialization */ u8 sched_policy:2; + u8 inherit_rt:1; u8 accept_fds:1; u8 min_priority; }; @@ -1221,7 +1224,8 @@ static void binder_set_priority(struct task_struct *task, static void binder_transaction_priority(struct task_struct *task, struct binder_transaction *t, - struct binder_priority node_prio) + struct binder_priority node_prio, + bool inherit_rt) { struct binder_priority desired_prio; @@ -1232,8 +1236,13 @@ static void binder_transaction_priority(struct task_struct *task, t->saved_priority.sched_policy = task->policy; t->saved_priority.prio = task->normal_prio; - desired_prio.prio = t->priority.prio; - desired_prio.sched_policy = t->priority.sched_policy; + if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { + desired_prio.prio = NICE_TO_PRIO(0); + desired_prio.sched_policy = SCHED_NORMAL; + } else { + desired_prio.prio = t->priority.prio; + desired_prio.sched_policy = t->priority.sched_policy; + } if (node_prio.prio < t->priority.prio || (node_prio.prio == t->priority.prio && @@ -1339,6 +1348,7 @@ static struct binder_node *binder_init_node_ilocked( FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); @@ -2793,7 +2803,8 @@ static bool binder_proc_transaction(struct binder_transaction *t, thread = binder_select_thread_ilocked(proc); if (thread) { - binder_transaction_priority(thread->task, t, node_prio); + binder_transaction_priority(thread->task, t, node_prio, + node->inherit_rt); binder_enqueue_thread_work_ilocked(thread, &t->work); } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); @@ -4231,7 +4242,8 @@ static int binder_thread_read(struct binder_proc *proc, tr.cookie = target_node->cookie; node_prio.sched_policy = target_node->sched_policy; node_prio.prio = target_node->min_priority; - binder_transaction_priority(current, t, node_prio); + binder_transaction_priority(current, t, node_prio, + target_node->inherit_rt); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 3fe109467ee3..b4723e36b6cf 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -80,6 +80,14 @@ enum flat_binder_object_flags { */ FLAT_BINDER_FLAG_SCHED_POLICY_MASK = 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT, + + /** + * @FLAT_BINDER_FLAG_INHERIT_RT: whether the node inherits RT policy + * + * Only when set, calls into this node will inherit a real-time + * scheduling policy from the caller (for synchronous transactions). + */ + FLAT_BINDER_FLAG_INHERIT_RT = 0x800, }; #ifdef BINDER_IPC_32BIT From 5b1cc76c43d0567efa80757d4defccc65b1d4dd9 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 26 May 2017 10:48:56 -0700 Subject: [PATCH 0397/1276] ANDROID: binder: don't check prio permissions on restore. Because we have disabled RT priority inheritance for the regular binder domain, the following can happen: 1) thread A (prio 98) calls into thread B 2) because RT prio inheritance is disabled, thread B runs at the lowest nice (prio 100) instead 3) thread B calls back into A; A will run at prio 100 for the duration of the transaction 4) When thread A is done with the call from B, we will try to restore the prio back to 98. But, we fail because the process doesn't hold CAP_SYS_NICE, neither is RLIMIT_RT_PRIO set. While the proper fix going forward will be to correctly apply CAP_SYS_NICE or RLIMIT_RT_PRIO, for now it seems reasonable to not check permissions on the restore path. Change-Id: Ibede5960c9b7bb786271c001e405de50be64d944 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 205863a07b89..ba75a05b37fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1164,8 +1164,9 @@ static int to_kernel_prio(int policy, int user_priority) return MAX_USER_RT_PRIO - 1 - user_priority; } -static void binder_set_priority(struct task_struct *task, - struct binder_priority desired) +static void binder_do_set_priority(struct task_struct *task, + struct binder_priority desired, + bool verify) { int priority; /* user-space prio value */ bool has_cap_nice; @@ -1178,7 +1179,7 @@ static void binder_set_priority(struct task_struct *task, priority = to_userspace_prio(policy, desired.prio); - if (is_rt_policy(policy) && !has_cap_nice) { + if (verify && is_rt_policy(policy) && !has_cap_nice) { long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO); if (max_rtprio == 0) { @@ -1189,7 +1190,7 @@ static void binder_set_priority(struct task_struct *task, } } - if (is_fair_policy(policy) && !has_cap_nice) { + if (verify && is_fair_policy(policy) && !has_cap_nice) { long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE)); if (min_nice > MAX_NICE) { @@ -1222,6 +1223,18 @@ static void binder_set_priority(struct task_struct *task, set_user_nice(task, priority); } +static void binder_set_priority(struct task_struct *task, + struct binder_priority desired) +{ + binder_do_set_priority(task, desired, /* verify = */ true); +} + +static void binder_restore_priority(struct task_struct *task, + struct binder_priority desired) +{ + binder_do_set_priority(task, desired, /* verify = */ false); +} + static void binder_transaction_priority(struct task_struct *task, struct binder_transaction *t, struct binder_priority node_prio, @@ -3336,7 +3349,7 @@ static void binder_transaction(struct binder_proc *proc, binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); - binder_set_priority(current, in_reply_to->saved_priority); + binder_restore_priority(current, in_reply_to->saved_priority); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); @@ -3440,7 +3453,7 @@ static void binder_transaction(struct binder_proc *proc, BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { - binder_set_priority(current, in_reply_to->saved_priority); + binder_restore_priority(current, in_reply_to->saved_priority); thread->return_error.cmd = BR_TRANSACTION_COMPLETE; binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); @@ -4021,7 +4034,7 @@ static int binder_thread_read(struct binder_proc *proc, wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } - binder_set_priority(current, proc->default_priority); + binder_restore_priority(current, proc->default_priority); } if (non_block) { From be32d2bec12b1a14d9cb7c2177cb88cf6f85b09e Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 8 May 2017 09:33:22 -0700 Subject: [PATCH 0398/1276] ANDROID: binder: Add tracing for binder priority inheritance. Bug: 34461621 Change-Id: I5ebb1c0c49fd42a89ee250a1d70221f767c82c7c Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 4 ++++ drivers/android/binder_trace.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ba75a05b37fb..67746f6ce6fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1209,6 +1209,10 @@ static void binder_do_set_priority(struct task_struct *task, task->pid, desired.prio, to_kernel_prio(policy, priority)); + trace_binder_set_priority(task->tgid, task->pid, task->normal_prio, + to_kernel_prio(policy, priority), + desired.prio); + /* Set the actual priority */ if (task->policy != policy || is_rt_policy(policy)) { struct sched_param params; diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 588eb3ec3507..7d8beb77f532 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -85,6 +85,30 @@ DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done); +TRACE_EVENT(binder_set_priority, + TP_PROTO(int proc, int thread, unsigned int old_prio, + unsigned int desired_prio, unsigned int new_prio), + TP_ARGS(proc, thread, old_prio, new_prio, desired_prio), + + TP_STRUCT__entry( + __field(int, proc) + __field(int, thread) + __field(unsigned int, old_prio) + __field(unsigned int, new_prio) + __field(unsigned int, desired_prio) + ), + TP_fast_assign( + __entry->proc = proc; + __entry->thread = thread; + __entry->old_prio = old_prio; + __entry->new_prio = new_prio; + __entry->desired_prio = desired_prio; + ), + TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d", + __entry->proc, __entry->thread, __entry->old_prio, + __entry->new_prio, __entry->desired_prio) +); + TRACE_EVENT(binder_wait_for_work, TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), TP_ARGS(proc_work, transaction_stack, thread_todo), From c847b48f8cda6027a6b72f3df2f464c660fa4dcc Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 27 Sep 2017 15:12:25 +0800 Subject: [PATCH 0399/1276] ANDROID: binder: init desired_prio.sched_policy before use it In function binder_transaction_priority(), we access desired_prio before initialzing it. This patch fix this. Change-Id: I9d14d50f9a128010476a65b52631630899a44633 Signed-off-by: Ganesh Mahendran --- drivers/android/binder.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 67746f6ce6fb..95ad121fefc9 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1244,7 +1244,7 @@ static void binder_transaction_priority(struct task_struct *task, struct binder_priority node_prio, bool inherit_rt) { - struct binder_priority desired_prio; + struct binder_priority desired_prio = t->priority; if (t->set_priority_called) return; @@ -1256,9 +1256,6 @@ static void binder_transaction_priority(struct task_struct *task, if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { desired_prio.prio = NICE_TO_PRIO(0); desired_prio.sched_policy = SCHED_NORMAL; - } else { - desired_prio.prio = t->priority.prio; - desired_prio.sched_policy = t->priority.sched_policy; } if (node_prio.prio < t->priority.prio || From d0b7e3150813ee7818bfb183b266a7b7392c6f7d Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 26 Sep 2017 17:56:25 +0800 Subject: [PATCH 0400/1276] ANDROID: binder: fix node sched policy calculation We should use FLAT_BINDER_FLAG_SCHED_POLICY_MASK as the mask to calculate sched policy. Change-Id: Ic252fd7c68495830690130d792802c02f99fc8fc Signed-off-by: Ganesh Mahendran --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 95ad121fefc9..0d475a17089b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1358,7 +1358,7 @@ static struct binder_node *binder_init_node_ilocked( node->cookie = cookie; node->work.type = BINDER_WORK_NODE; priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); From e01aae2b7e17e54b112a930270518afa4d038563 Mon Sep 17 00:00:00 2001 From: Thierry Strudel Date: Tue, 14 Jun 2016 17:46:44 -0700 Subject: [PATCH 0401/1276] ANDROID: cpu: send KOBJ_ONLINE event when enabling cpus In case some sysfs nodes needs to be labeled with a different label than sysfs then user needs to be notified when a core is brought back online. Signed-off-by: Thierry Strudel Bug: 29359497 Change-Id: I0395c86e01cd49c348fda8f93087d26f88557c91 --- kernel/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/cpu.c b/kernel/cpu.c index 0097acec1c71..02a45dd96835 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1211,6 +1211,7 @@ void __weak arch_enable_nonboot_cpus_end(void) void enable_nonboot_cpus(void) { int cpu, error; + struct device *cpu_device; /* Allow everyone to use the CPU hotplug again */ cpu_maps_update_begin(); @@ -1228,6 +1229,12 @@ void enable_nonboot_cpus(void) trace_suspend_resume(TPS("CPU_ON"), cpu, false); if (!error) { pr_info("CPU%d is up\n", cpu); + cpu_device = get_cpu_device(cpu); + if (!cpu_device) + pr_err("%s: failed to get cpu%d device\n", + __func__, cpu); + else + kobject_uevent(&cpu_device->kobj, KOBJ_ONLINE); continue; } pr_warn("Error taking CPU%d up: %d\n", cpu, error); From f6c2694943bd29e43c32c75e01a47979b5be6a89 Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Tue, 6 Sep 2016 15:16:25 -0700 Subject: [PATCH 0402/1276] ANDROID: cpuset: Make cpusets restore on hotplug This deliberately changes the behavior of the per-cpuset cpus file to not be effected by hotplug. When a cpu is offlined, it will be removed from the cpuset/cpus file. When a cpu is onlined, if the cpuset originally requested that that cpu was part of the cpuset, that cpu will be restored to the cpuset. The cpus files still have to be hierachical, but the ranges no longer have to be out of the currently online cpus, just the physically present cpus. Change-Id: I22cdf33e7d312117bcefba1aeb0125e1ada289a9 Signed-off-by: Dmitry Shmidt [AmitP: Refactored original changes to align with upstream commit 201af4c0fab0 ("cgroup: move cgroup files under kernel/cgroup/")] Signed-off-by: Amit Pundir --- kernel/cgroup/cpuset.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 266f10cb7222..beccc2633cf4 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -103,6 +103,7 @@ struct cpuset { /* user-configured CPUs and Memory Nodes allow to tasks */ cpumask_var_t cpus_allowed; + cpumask_var_t cpus_requested; nodemask_t mems_allowed; /* effective CPUs and Memory Nodes allow to tasks */ @@ -412,7 +413,7 @@ static void cpuset_update_task_spread_flag(struct cpuset *cs, static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q) { - return cpumask_subset(p->cpus_allowed, q->cpus_allowed) && + return cpumask_subset(p->cpus_requested, q->cpus_requested) && nodes_subset(p->mems_allowed, q->mems_allowed) && is_cpu_exclusive(p) <= is_cpu_exclusive(q) && is_mem_exclusive(p) <= is_mem_exclusive(q); @@ -511,7 +512,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial) cpuset_for_each_child(c, css, par) { if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && c != cur && - cpumask_intersects(trial->cpus_allowed, c->cpus_allowed)) + cpumask_intersects(trial->cpus_requested, c->cpus_requested)) goto out; if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) && c != cur && @@ -972,17 +973,18 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, if (!*buf) { cpumask_clear(trialcs->cpus_allowed); } else { - retval = cpulist_parse(buf, trialcs->cpus_allowed); + retval = cpulist_parse(buf, trialcs->cpus_requested); if (retval < 0) return retval; - if (!cpumask_subset(trialcs->cpus_allowed, - top_cpuset.cpus_allowed)) + if (!cpumask_subset(trialcs->cpus_requested, cpu_present_mask)) return -EINVAL; + + cpumask_and(trialcs->cpus_allowed, trialcs->cpus_requested, cpu_active_mask); } /* Nothing to do if the cpus didn't change */ - if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed)) + if (cpumask_equal(cs->cpus_requested, trialcs->cpus_requested)) return 0; retval = validate_change(cs, trialcs); @@ -991,6 +993,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, spin_lock_irq(&callback_lock); cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed); + cpumask_copy(cs->cpus_requested, trialcs->cpus_requested); spin_unlock_irq(&callback_lock); /* use trialcs->cpus_allowed as a temp variable */ @@ -1759,7 +1762,7 @@ static int cpuset_common_seq_show(struct seq_file *sf, void *v) switch (type) { case FILE_CPULIST: - seq_printf(sf, "%*pbl\n", cpumask_pr_args(cs->cpus_allowed)); + seq_printf(sf, "%*pbl\n", cpumask_pr_args(cs->cpus_requested)); break; case FILE_MEMLIST: seq_printf(sf, "%*pbl\n", nodemask_pr_args(&cs->mems_allowed)); @@ -1949,11 +1952,14 @@ cpuset_css_alloc(struct cgroup_subsys_state *parent_css) return ERR_PTR(-ENOMEM); if (!alloc_cpumask_var(&cs->cpus_allowed, GFP_KERNEL)) goto free_cs; + if (!alloc_cpumask_var(&cs->cpus_requested, GFP_KERNEL)) + goto free_allowed; if (!alloc_cpumask_var(&cs->effective_cpus, GFP_KERNEL)) - goto free_cpus; + goto free_requested; set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); cpumask_clear(cs->cpus_allowed); + cpumask_clear(cs->cpus_requested); nodes_clear(cs->mems_allowed); cpumask_clear(cs->effective_cpus); nodes_clear(cs->effective_mems); @@ -1962,7 +1968,9 @@ cpuset_css_alloc(struct cgroup_subsys_state *parent_css) return &cs->css; -free_cpus: +free_requested: + free_cpumask_var(cs->cpus_requested); +free_allowed: free_cpumask_var(cs->cpus_allowed); free_cs: kfree(cs); @@ -2025,6 +2033,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) cs->mems_allowed = parent->mems_allowed; cs->effective_mems = parent->mems_allowed; cpumask_copy(cs->cpus_allowed, parent->cpus_allowed); + cpumask_copy(cs->cpus_requested, parent->cpus_requested); cpumask_copy(cs->effective_cpus, parent->cpus_allowed); spin_unlock_irq(&callback_lock); out_unlock: @@ -2059,6 +2068,7 @@ static void cpuset_css_free(struct cgroup_subsys_state *css) free_cpumask_var(cs->effective_cpus); free_cpumask_var(cs->cpus_allowed); + free_cpumask_var(cs->cpus_requested); kfree(cs); } @@ -2121,8 +2131,10 @@ int __init cpuset_init(void) BUG_ON(!alloc_cpumask_var(&top_cpuset.cpus_allowed, GFP_KERNEL)); BUG_ON(!alloc_cpumask_var(&top_cpuset.effective_cpus, GFP_KERNEL)); + BUG_ON(!alloc_cpumask_var(&top_cpuset.cpus_requested, GFP_KERNEL)); cpumask_setall(top_cpuset.cpus_allowed); + cpumask_setall(top_cpuset.cpus_requested); nodes_setall(top_cpuset.mems_allowed); cpumask_setall(top_cpuset.effective_cpus); nodes_setall(top_cpuset.effective_mems); @@ -2255,7 +2267,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs) goto retry; } - cpumask_and(&new_cpus, cs->cpus_allowed, parent_cs(cs)->effective_cpus); + cpumask_and(&new_cpus, cs->cpus_requested, parent_cs(cs)->effective_cpus); nodes_and(new_mems, cs->mems_allowed, parent_cs(cs)->effective_mems); cpus_updated = !cpumask_equal(&new_cpus, cs->effective_cpus); From e687086c21c88af2911328cf9ced8c73c74fb534 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Oct 2016 16:14:16 -0700 Subject: [PATCH 0403/1276] CHROMIUM: cgroups: relax permissions on moving tasks between cgroups Android expects system_server to be able to move tasks between different cgroups/cpusets, but does not want to be running as root. Let's relax permission check so that processes can move other tasks if they have CAP_SYS_NICE in the affected task's user namespace. BUG=b:31790445,chromium:647994 TEST=Boot android container, examine logcat Change-Id: Ia919c66ab6ed6a6daf7c4cf67feb38b13b1ad09b Signed-off-by: Dmitry Torokhov Reviewed-on: https://chromium-review.googlesource.com/394927 Reviewed-by: Ricky Zhou [AmitP: Refactored original changes to align with upstream commit 201af4c0fab0 ("cgroup: move cgroup files under kernel/cgroup/")] Signed-off-by: Amit Pundir --- kernel/cgroup/cgroup-v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 51063e7a93c2..0a39b26d6e4d 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -541,7 +541,8 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of, tcred = get_task_cred(task); if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && !uid_eq(cred->euid, tcred->uid) && - !uid_eq(cred->euid, tcred->suid)) + !uid_eq(cred->euid, tcred->suid) && + !ns_capable(tcred->user_ns, CAP_SYS_NICE)) ret = -EACCES; put_cred(tcred); if (ret) From 7f6e009ca2189fe01438254d73d1ffda0ac6a997 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 30 Mar 2016 14:10:13 -0700 Subject: [PATCH 0404/1276] ANDROID: dm: verity-fec: add sysfs attribute fec/corrected Add a sysfs entry that allows user space to determine whether dm-verity has come across correctable errors on the underlying block device. Bug: 22655252 Bug: 27928374 Change-Id: I80547a2aa944af2fb9ffde002650482877ade31b Signed-off-by: Sami Tolvanen (cherry picked from commit 7911fad5f0a2cf5afc2215657219a21e6630e001) [AmitP: Folded following android-4.9 commit changes into this patch 3278f53e4658 ("ANDROID: dm verity fec: add missing release from fec_ktype")] Signed-off-by: Amit Pundir --- drivers/md/dm-verity-fec.c | 46 +++++++++++++++++++++++++++++++++++++- drivers/md/dm-verity-fec.h | 4 ++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 684af08d0747..8306ee0b2d0c 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -11,6 +11,7 @@ #include "dm-verity-fec.h" #include +#include #define DM_MSG_PREFIX "verity-fec" @@ -175,9 +176,11 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio, if (r < 0 && neras) DMERR_LIMIT("%s: FEC %llu: failed to correct: %d", v->data_dev->name, (unsigned long long)rsb, r); - else if (r > 0) + else if (r > 0) { DMWARN_LIMIT("%s: FEC %llu: corrected %d errors", v->data_dev->name, (unsigned long long)rsb, r); + atomic_add_unless(&v->fec->corrected, 1, INT_MAX); + } return r; } @@ -545,6 +548,7 @@ unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz, void verity_fec_dtr(struct dm_verity *v) { struct dm_verity_fec *f = v->fec; + struct kobject *kobj = &f->kobj_holder.kobj; if (!verity_fec_is_enabled(v)) goto out; @@ -561,6 +565,12 @@ void verity_fec_dtr(struct dm_verity *v) if (f->dev) dm_put_device(v->ti, f->dev); + + if (kobj->state_initialized) { + kobject_put(kobj); + wait_for_completion(dm_get_completion_from_kobject(kobj)); + } + out: kfree(f); v->fec = NULL; @@ -649,6 +659,28 @@ int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, return 0; } +static ssize_t corrected_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct dm_verity_fec *f = container_of(kobj, struct dm_verity_fec, + kobj_holder.kobj); + + return sprintf(buf, "%d\n", atomic_read(&f->corrected)); +} + +static struct kobj_attribute attr_corrected = __ATTR_RO(corrected); + +static struct attribute *fec_attrs[] = { + &attr_corrected.attr, + NULL +}; + +static struct kobj_type fec_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = fec_attrs, + .release = dm_kobject_release +}; + /* * Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr. */ @@ -672,8 +704,10 @@ int verity_fec_ctr_alloc(struct dm_verity *v) */ int verity_fec_ctr(struct dm_verity *v) { + int r; struct dm_verity_fec *f = v->fec; struct dm_target *ti = v->ti; + struct mapped_device *md = dm_table_get_md(ti->table); u64 hash_blocks; int ret; @@ -682,6 +716,16 @@ int verity_fec_ctr(struct dm_verity *v) return 0; } + /* Create a kobject and sysfs attributes */ + init_completion(&f->kobj_holder.completion); + + r = kobject_init_and_add(&f->kobj_holder.kobj, &fec_ktype, + &disk_to_dev(dm_disk(md))->kobj, "%s", "fec"); + if (r) { + ti->error = "Cannot create kobject"; + return r; + } + /* * FEC is computed over data blocks, possible metadata, and * hash blocks. In other words, FEC covers total of fec_blocks diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index 6ad803b2b36c..93af41777b4f 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -12,6 +12,8 @@ #ifndef DM_VERITY_FEC_H #define DM_VERITY_FEC_H +#include "dm.h" +#include "dm-core.h" #include "dm-verity.h" #include @@ -51,6 +53,8 @@ struct dm_verity_fec { mempool_t extra_pool; /* mempool for extra buffers */ mempool_t output_pool; /* mempool for output */ struct kmem_cache *cache; /* cache for buffers */ + atomic_t corrected; /* corrected errors */ + struct dm_kobject_holder kobj_holder; /* for sysfs attributes */ }; /* per-bio data */ From 387fb913112c3422fb9c6ea0a17d65c9765cfbf7 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Wed, 9 Jun 2010 17:47:38 -0500 Subject: [PATCH 0405/1276] CHROMIUM: dm: boot time specification of dm= This is a wrap-up of three patches pending upstream approval. I'm bundling them because they are interdependent, and it'll be easier to drop it on rebase later. 1. dm: allow a dm-fs-style device to be shared via dm-ioctl Integrates feedback from Alisdair, Mike, and Kiyoshi. Two main changes occur here: - One function is added which allows for a programmatically created mapped device to be inserted into the dm-ioctl hash table. This binds the device to a name and, optional, uuid which is needed by udev and allows for userspace management of the mapped device. - dm_table_complete() was extended to handle all of the final functional changes required for the table to be operational once called. 2. init: boot to device-mapper targets without an initr* Add a dm= kernel parameter modeled after the md= parameter from do_mounts_md. It allows for device-mapper targets to be configured at boot time for use early in the boot process (as the root device or otherwise). It also replaces /dev/XXX calls with major:minor opportunistically. The format is dm="name uuid ro,table line 1,table line 2,...". The parser expects the comma to be safe to use as a newline substitute but, otherwise, uses the normal separator of space. Some attempt has been made to make it forgiving of additional spaces (using skip_spaces()). A mapped device created during boot will be assigned a minor of 0 and may be access via /dev/dm-0. An example dm-linear root with no uuid may look like: root=/dev/dm-0 dm="lroot none ro, 0 4096 linear /dev/ubdb 0, 4096 4096 linear /dv/ubdc 0" Once udev is started, /dev/dm-0 will become /dev/mapper/lroot. Older upstream threads: http://marc.info/?l=dm-devel&m=127429492521964&w=2 http://marc.info/?l=dm-devel&m=127429499422096&w=2 http://marc.info/?l=dm-devel&m=127429493922000&w=2 Latest upstream threads: https://patchwork.kernel.org/patch/104859/ https://patchwork.kernel.org/patch/104860/ https://patchwork.kernel.org/patch/104861/ Bug: 27175947 Signed-off-by: Will Drewry Review URL: http://codereview.chromium.org/2020011 Change-Id: I92bd53432a11241228d2e5ac89a3b20d19b05a31 [AmitP: Refactored the original changes based on upstream changes, commit e52347bd66f6 ("Documentation/admin-guide: split the kernel parameter list to a separate file")] Signed-off-by: Amit Pundir --- .../admin-guide/kernel-parameters.txt | 3 + Documentation/device-mapper/boot.txt | 42 ++ drivers/md/dm-ioctl.c | 39 ++ drivers/md/dm-table.c | 1 + include/linux/device-mapper.h | 6 + init/Makefile | 1 + init/do_mounts.c | 1 + init/do_mounts.h | 10 + init/do_mounts_dm.c | 410 ++++++++++++++++++ 9 files changed, 513 insertions(+) create mode 100644 Documentation/device-mapper/boot.txt create mode 100644 init/do_mounts_dm.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..331195d19b32 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -880,6 +880,9 @@ dis_ucode_ldr [X86] Disable the microcode loader. + dm= [DM] Allows early creation of a device-mapper device. + See Documentation/device-mapper/boot.txt. + dma_debug=off If the kernel is compiled with DMA_API_DEBUG support, this option disables the debugging code at boot. diff --git a/Documentation/device-mapper/boot.txt b/Documentation/device-mapper/boot.txt new file mode 100644 index 000000000000..adcaad5e5e32 --- /dev/null +++ b/Documentation/device-mapper/boot.txt @@ -0,0 +1,42 @@ +Boot time creation of mapped devices +=================================== + +It is possible to configure a device mapper device to act as the root +device for your system in two ways. + +The first is to build an initial ramdisk which boots to a minimal +userspace which configures the device, then pivot_root(8) in to it. + +For simple device mapper configurations, it is possible to boot directly +using the following kernel command line: + +dm=" ,table line 1,...,table line n" + +name = the name to associate with the device + after boot, udev, if used, will use that name to label + the device node. +uuid = may be 'none' or the UUID desired for the device. +ro = may be "ro" or "rw". If "ro", the device and device table will be + marked read-only. + +Each table line may be as normal when using the dmsetup tool except for +two variations: +1. Any use of commas will be interpreted as a newline +2. Quotation marks cannot be escaped and cannot be used without + terminating the dm= argument. + +Unless renamed by udev, the device node created will be dm-0 as the +first minor number for the device-mapper is used during early creation. + +Example +======= + +- Booting to a linear array made up of user-mode linux block devices: + + dm="lroot none 0, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" \ + root=/dev/dm-0 + +Will boot to a rw dm-linear target of 8192 sectors split across two +block devices identified by their major:minor numbers. After boot, udev +will rename this target to /dev/mapper/lroot (depending on the rules). +No uuid was assigned. diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index b810ea77e6b1..350aa7a7a591 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1993,6 +1993,45 @@ void dm_interface_exit(void) dm_hash_exit(); } + +/** + * dm_ioctl_export - Permanently export a mapped device via the ioctl interface + * @md: Pointer to mapped_device + * @name: Buffer (size DM_NAME_LEN) for name + * @uuid: Buffer (size DM_UUID_LEN) for uuid or NULL if not desired + */ +int dm_ioctl_export(struct mapped_device *md, const char *name, + const char *uuid) +{ + int r = 0; + struct hash_cell *hc; + + if (!md) { + r = -ENXIO; + goto out; + } + + /* The name and uuid can only be set once. */ + mutex_lock(&dm_hash_cells_mutex); + hc = dm_get_mdptr(md); + mutex_unlock(&dm_hash_cells_mutex); + if (hc) { + DMERR("%s: already exported", dm_device_name(md)); + r = -ENXIO; + goto out; + } + + r = dm_hash_insert(name, uuid, md); + if (r) { + DMERR("%s: could not bind to '%s'", dm_device_name(md), name); + goto out; + } + + /* Let udev know we've changed. */ + dm_kobject_uevent(md, KOBJ_CHANGE, dm_get_event_nr(md)); +out: + return r; +} /** * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers * @md: Pointer to mapped_device diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 3d0e2c198f06..485626d5b9d7 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 6fb0808e87c8..8d1ec55b43d1 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -397,6 +397,12 @@ void dm_put(struct mapped_device *md); void dm_set_mdptr(struct mapped_device *md, void *ptr); void *dm_get_mdptr(struct mapped_device *md); +/* + * Export the device via the ioctl interface (uses mdptr). + */ +int dm_ioctl_export(struct mapped_device *md, const char *name, + const char *uuid); + /* * A device can still be used while suspended, but I/O is deferred. */ diff --git a/init/Makefile b/init/Makefile index a3e5ce2bcf08..f814f0ff5974 100644 --- a/init/Makefile +++ b/init/Makefile @@ -19,6 +19,7 @@ mounts-y := do_mounts.o mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o +mounts-$(CONFIG_BLK_DEV_DM) += do_mounts_dm.o # dependencies on generated files need to be listed explicitly $(obj)/version.o: include/generated/compile.h diff --git a/init/do_mounts.c b/init/do_mounts.c index e1c9afa9d8c9..d707f12be6e7 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -555,6 +555,7 @@ void __init prepare_namespace(void) wait_for_device_probe(); md_run_setup(); + dm_run_setup(); if (saved_root_name[0]) { root_device_name = saved_root_name; diff --git a/init/do_mounts.h b/init/do_mounts.h index 0bb0806de4ce..0f57528ea324 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -61,3 +61,13 @@ void md_run_setup(void); static inline void md_run_setup(void) {} #endif + +#ifdef CONFIG_BLK_DEV_DM + +void dm_run_setup(void); + +#else + +static inline void dm_run_setup(void) {} + +#endif diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c new file mode 100644 index 000000000000..0fd3411533f3 --- /dev/null +++ b/init/do_mounts_dm.c @@ -0,0 +1,410 @@ +/* do_mounts_dm.c + * Copyright (C) 2010 The Chromium OS Authors + * All Rights Reserved. + * Based on do_mounts_md.c + * + * This file is released under the GPL. + */ +#include +#include +#include + +#include "do_mounts.h" + +#define DM_MAX_NAME 32 +#define DM_MAX_UUID 129 +#define DM_NO_UUID "none" + +#define DM_MSG_PREFIX "init" + +/* Separators used for parsing the dm= argument. */ +#define DM_FIELD_SEP ' ' +#define DM_LINE_SEP ',' + +/* + * When the device-mapper and any targets are compiled into the kernel + * (not a module), one target may be created and used as the root device at + * boot time with the parameters given with the boot line dm=... + * The code for that is here. + */ + +struct dm_setup_target { + sector_t begin; + sector_t length; + char *type; + char *params; + /* simple singly linked list */ + struct dm_setup_target *next; +}; + +static struct { + int minor; + int ro; + char name[DM_MAX_NAME]; + char uuid[DM_MAX_UUID]; + char *targets; + struct dm_setup_target *target; + int target_count; +} dm_setup_args __initdata; + +static __initdata int dm_early_setup; + +static size_t __init get_dm_option(char *str, char **next, char sep) +{ + size_t len = 0; + char *endp = NULL; + + if (!str) + return 0; + + endp = strchr(str, sep); + if (!endp) { /* act like strchrnul */ + len = strlen(str); + endp = str + len; + } else { + len = endp - str; + } + + if (endp == str) + return 0; + + if (!next) + return len; + + if (*endp == 0) { + /* Don't advance past the nul. */ + *next = endp; + } else { + *next = endp + 1; + } + return len; +} + +static int __init dm_setup_args_init(void) +{ + dm_setup_args.minor = 0; + dm_setup_args.ro = 0; + dm_setup_args.target = NULL; + dm_setup_args.target_count = 0; + return 0; +} + +static int __init dm_setup_cleanup(void) +{ + struct dm_setup_target *target = dm_setup_args.target; + struct dm_setup_target *old_target = NULL; + while (target) { + kfree(target->type); + kfree(target->params); + old_target = target; + target = target->next; + kfree(old_target); + dm_setup_args.target_count--; + } + BUG_ON(dm_setup_args.target_count); + return 0; +} + +static char * __init dm_setup_parse_device_args(char *str) +{ + char *next = NULL; + size_t len = 0; + + /* Grab the logical name of the device to be exported to udev */ + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len) { + DMERR("failed to parse device name"); + goto parse_fail; + } + len = min(len + 1, sizeof(dm_setup_args.name)); + strlcpy(dm_setup_args.name, str, len); /* includes nul */ + str = skip_spaces(next); + + /* Grab the UUID value or "none" */ + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len) { + DMERR("failed to parse device uuid"); + goto parse_fail; + } + len = min(len + 1, sizeof(dm_setup_args.uuid)); + strlcpy(dm_setup_args.uuid, str, len); + str = skip_spaces(next); + + /* Determine if the table/device will be read only or read-write */ + if (!strncmp("ro,", str, 3)) { + dm_setup_args.ro = 1; + } else if (!strncmp("rw,", str, 3)) { + dm_setup_args.ro = 0; + } else { + DMERR("failed to parse table mode"); + goto parse_fail; + } + str = skip_spaces(str + 3); + + return str; + +parse_fail: + return NULL; +} + +static void __init dm_substitute_devices(char *str, size_t str_len) +{ + char *candidate = str; + char *candidate_end = str; + char old_char; + size_t len = 0; + dev_t dev; + + if (str_len < 3) + return; + + while (str && *str) { + candidate = strchr(str, '/'); + if (!candidate) + break; + + /* Avoid embedded slashes */ + if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { + str = strchr(candidate, DM_FIELD_SEP); + continue; + } + + len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); + str = skip_spaces(candidate_end); + if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ + continue; + + /* Temporarily terminate with a nul */ + candidate_end--; + old_char = *candidate_end; + *candidate_end = '\0'; + + DMDEBUG("converting candidate device '%s' to dev_t", candidate); + /* Use the boot-time specific device naming */ + dev = name_to_dev_t(candidate); + *candidate_end = old_char; + + DMDEBUG(" -> %u", dev); + /* No suitable replacement found */ + if (!dev) + continue; + + /* Rewrite the /dev/path as a major:minor */ + len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); + if (!len) { + DMERR("error substituting device major/minor."); + break; + } + candidate += len; + /* Pad out with spaces (fixing our nul) */ + while (candidate < candidate_end) + *(candidate++) = DM_FIELD_SEP; + } +} + +static int __init dm_setup_parse_targets(char *str) +{ + char *next = NULL; + size_t len = 0; + struct dm_setup_target **target = NULL; + + /* Targets are defined as per the table format but with a + * comma as a newline separator. */ + target = &dm_setup_args.target; + while (str && *str) { + *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); + if (!*target) { + DMERR("failed to allocate memory for target %d", + dm_setup_args.target_count); + goto parse_fail; + } + dm_setup_args.target_count++; + + (*target)->begin = simple_strtoull(str, &next, 10); + if (!next || *next != DM_FIELD_SEP) { + DMERR("failed to parse starting sector for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next + 1); + + (*target)->length = simple_strtoull(str, &next, 10); + if (!next || *next != DM_FIELD_SEP) { + DMERR("failed to parse length for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next + 1); + + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len || + !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { + DMERR("failed to parse type for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next); + + len = get_dm_option(str, &next, DM_LINE_SEP); + if (!len || + !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { + DMERR("failed to parse params for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next); + + /* Before moving on, walk through the copied target and + * attempt to replace all /dev/xxx with the major:minor number. + * It may not be possible to resolve them traditionally at + * boot-time. */ + dm_substitute_devices((*target)->params, len); + + target = &((*target)->next); + } + DMDEBUG("parsed %d targets", dm_setup_args.target_count); + + return 0; + +parse_fail: + return 1; +} + +/* + * Parse the command-line parameters given our kernel, but do not + * actually try to invoke the DM device now; that is handled by + * dm_setup_drive after the low-level disk drivers have initialised. + * dm format is as follows: + * dm="name uuid fmode,[table line 1],[table line 2],..." + * May be used with root=/dev/dm-0 as it always uses the first dm minor. + */ + +static int __init dm_setup(char *str) +{ + dm_setup_args_init(); + + str = dm_setup_parse_device_args(str); + if (!str) { + DMDEBUG("str is NULL"); + goto parse_fail; + } + + /* Target parsing is delayed until we have dynamic memory */ + dm_setup_args.targets = str; + + printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", + dm_setup_args.name, dm_setup_args.minor); + + dm_early_setup = 1; + return 1; + +parse_fail: + printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + return 0; +} + + +static void __init dm_setup_drive(void) +{ + struct mapped_device *md = NULL; + struct dm_table *table = NULL; + struct dm_setup_target *target; + char *uuid = dm_setup_args.uuid; + fmode_t fmode = FMODE_READ; + + /* Finish parsing the targets. */ + if (dm_setup_parse_targets(dm_setup_args.targets)) + goto parse_fail; + + if (dm_create(dm_setup_args.minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* In addition to flagging the table below, the disk must be + * set explicitly ro/rw. */ + set_disk_ro(dm_disk(md), dm_setup_args.ro); + + if (!dm_setup_args.ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } + + target = dm_setup_args.target; + while (target) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, target->type, + target->params); + if (dm_table_add_target(table, target->type, target->begin, + target->length, target->params)) { + DMDEBUG("failed to add the target to the table"); + goto add_target_fail; + } + target = target->next; + } + + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; + } + + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } + + /* Bind the table to the device. This is the only way to associate + * md->map with the table and set the disk capacity directly. */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } + + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } + + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { + DMDEBUG("failed to export device with given name and uuid"); + goto export_fail; + } + printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + + dm_setup_cleanup(); + return; + +export_fail: +resume_fail: +table_bind_fail: +suspend_fail: +table_complete_fail: +add_target_fail: + dm_table_put(table); +dm_table_create_fail: + dm_put(md); +dm_create_fail: + dm_setup_cleanup(); +parse_fail: + printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", + dm_setup_args.minor, dm_setup_args.name); +} + +__setup("dm=", dm_setup); + +void __init dm_run_setup(void) +{ + if (!dm_early_setup) + return; + printk(KERN_INFO "dm: attempting early device configuration.\n"); + dm_setup_drive(); +} From ec31e3f1ed6807c654beecbc3a29abf059d6339e Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 8 Feb 2016 16:47:41 -0800 Subject: [PATCH 0406/1276] ANDROID: dm: do_mounts_dm: Rebase on top of 4.9 1. "dm: optimize use SRCU and RCU" removes the use of dm_table_put. 2. "dm: remove request-based logic from make_request_fn wrapper" necessitates calling dm_setup_md_queue or else the request_queue's make_request_fn pointer ends being unset. [ 7.711600] Internal error: Oops - bad mode: 0 [#1] PREEMPT SMP [ 7.717519] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G W 4.1.15-02273-gb057d16-dirty #33 [ 7.726559] Hardware name: HiKey Development Board (DT) [ 7.731779] task: ffffffc005f8acc0 ti: ffffffc005f8c000 task.ti: ffffffc005f8c000 [ 7.739257] PC is at 0x0 [ 7.741787] LR is at generic_make_request+0x8c/0x108 .... [ 9.082931] Call trace: [ 9.085372] [< (null)>] (null) [ 9.090074] [] submit_bio+0x98/0x1e0 [ 9.095212] [] _submit_bh+0x120/0x1f0 [ 9.096165] cfg80211: Calling CRDA to update world regulatory domain [ 9.106781] [] __bread_gfp+0x94/0x114 [ 9.112004] [] ext4_fill_super+0x18c/0x2d64 [ 9.117750] [] mount_bdev+0x194/0x1c0 [ 9.122973] [] ext4_mount+0x14/0x1c [ 9.128021] [] mount_fs+0x3c/0x194 [ 9.132985] [] vfs_kern_mount+0x4c/0x134 [ 9.138467] [] do_mount+0x204/0xbbc [ 9.143514] [] SyS_mount+0x94/0xe8 [ 9.148479] [] mount_block_root+0x120/0x24c [ 9.154222] [] mount_root+0x110/0x12c [ 9.159443] [] prepare_namespace+0x170/0x1b8 [ 9.165273] [] kernel_init_freeable+0x23c/0x260 [ 9.171365] [] kernel_init+0x10/0x118 [ 9.176589] Code: bad PC value [ 9.179807] ---[ end trace 75e1bc52ba364d13 ]--- Bug: 27175947 Signed-off-by: Badhri Jagan Sridharan Change-Id: I952d86fd1475f0825f9be1386e3497b36127abd0 --- init/do_mounts_dm.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index 0fd3411533f3..0fe9c5f7d5e9 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -10,6 +10,7 @@ #include #include "do_mounts.h" +#include "../drivers/md/dm.h" #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 @@ -333,6 +334,7 @@ static void __init dm_setup_drive(void) goto dm_table_create_fail; } + dm_lock_md_type(md); target = dm_setup_args.target; while (target) { DMINFO("adding target '%llu %llu %s %s'", @@ -352,6 +354,17 @@ static void __init dm_setup_drive(void) goto table_complete_fail; } + if (dm_get_md_type(md) == DM_TYPE_NONE) { + dm_set_md_type(md, dm_table_get_type(table)); + if (dm_setup_md_queue(md, table)) { + DMWARN("unable to set up device queue for new table."); + goto setup_md_queue_fail; + } + } else if (dm_get_md_type(md) != dm_table_get_type(table)) { + DMWARN("can't change device type after initial table load."); + goto setup_md_queue_fail; + } + /* Suspend the device so that we can bind it to the table. */ if (dm_suspend(md, 0)) { DMDEBUG("failed to suspend the device pre-bind"); @@ -380,6 +393,7 @@ static void __init dm_setup_drive(void) } printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + dm_unlock_md_type(md); dm_setup_cleanup(); return; @@ -387,9 +401,10 @@ static void __init dm_setup_drive(void) resume_fail: table_bind_fail: suspend_fail: +setup_md_queue_fail: table_complete_fail: add_target_fail: - dm_table_put(table); + dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: From 763b9c35d8def8e919199476001ba1ad976c9072 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 2 May 2016 17:29:28 +0200 Subject: [PATCH 0407/1276] ANDROID: dm: do_mounts_dm: fix dm_substitute_devices() When candidate is the last parameter, candidate_end points to the '\0' character and not the DM_FIELD_SEP character. In such a situation, we should not move the candidate_end pointer one character backward. Signed-off-by: Jeremy Compostella --- init/do_mounts_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index 0fe9c5f7d5e9..a557c5ee00a7 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -176,7 +176,8 @@ static void __init dm_substitute_devices(char *str, size_t str_len) continue; /* Temporarily terminate with a nul */ - candidate_end--; + if (*candidate_end) + candidate_end--; old_char = *candidate_end; *candidate_end = '\0'; From 551c005c8a20176533fc45c70f1ca37f94b406d5 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 19 May 2017 17:20:18 -0400 Subject: [PATCH 0408/1276] ANDROID: dm: do_mounts_dm: Update init/do_mounts_dm.c to the latest ChromiumOS version. This is needed for AVB. Bug: None Test: Compiles. Change-Id: I45b5d435652ab66ec07420ab17f2c7889f7e4d95 Signed-off-by: David Zeuthen --- drivers/md/dm.h | 2 - include/linux/device-mapper.h | 7 + init/do_mounts_dm.c | 556 ++++++++++++++++++---------------- 3 files changed, 307 insertions(+), 258 deletions(-) diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 114a81b27c37..d8db76afa622 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -80,8 +80,6 @@ void dm_set_md_type(struct mapped_device *md, enum dm_queue_mode type); enum dm_queue_mode dm_get_md_type(struct mapped_device *md); struct target_type *dm_get_immutable_target_type(struct mapped_device *md); -int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t); - /* * To check the return value from dm_table_find_target(). */ diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 8d1ec55b43d1..b7b047709918 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -432,6 +432,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq); struct queue_limits *dm_get_queue_limits(struct mapped_device *md); +void dm_lock_md_type(struct mapped_device *md); +void dm_unlock_md_type(struct mapped_device *md); +void dm_set_md_type(struct mapped_device *md, unsigned type); +unsigned dm_get_md_type(struct mapped_device *md); +int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t); +unsigned dm_table_get_type(struct dm_table *t); + /* * Geometry functions. */ diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index a557c5ee00a7..af84b01ccfbc 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -5,13 +5,17 @@ * * This file is released under the GPL. */ +#include +#include #include #include #include +#include #include "do_mounts.h" -#include "../drivers/md/dm.h" +#define DM_MAX_DEVICES 256 +#define DM_MAX_TARGETS 256 #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 #define DM_NO_UUID "none" @@ -19,14 +23,47 @@ #define DM_MSG_PREFIX "init" /* Separators used for parsing the dm= argument. */ -#define DM_FIELD_SEP ' ' -#define DM_LINE_SEP ',' +#define DM_FIELD_SEP " " +#define DM_LINE_SEP "," +#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP /* * When the device-mapper and any targets are compiled into the kernel - * (not a module), one target may be created and used as the root device at - * boot time with the parameters given with the boot line dm=... - * The code for that is here. + * (not a module), one or more device-mappers may be created and used + * as the root device at boot time with the parameters given with the + * boot line dm=... + * + * Multiple device-mappers can be stacked specifing the number of + * devices. A device can have multiple targets if the the number of + * targets is specified. + * + * TODO(taysom:defect 32847) + * In the future, the field will be mandatory. + * + * ::= [] + + * ::= "," + + * ::= [] + * ::= "," + * ::= "ro" | "rw" + * ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" + * ::= "verity" | "bootcache" | ... + * + * Example: + * 2 vboot none ro 1, + * 0 1768000 bootcache + * device=aa55b119-2a47-8c45-946a-5ac57765011f+1 + * signature=76e9be054b15884a9fa85973e9cb274c93afadb6 + * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, + * vroot none ro 1, + * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 + * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 + * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe + * + * Notes: + * 1. uuid is a label for the device and we set it to "none". + * 2. The field will be optional initially and assumed to be 1. + * Once all the scripts that set these fields have been set, it will + * be made mandatory. */ struct dm_setup_target { @@ -38,381 +75,388 @@ struct dm_setup_target { struct dm_setup_target *next; }; -static struct { +struct dm_device { int minor; int ro; char name[DM_MAX_NAME]; char uuid[DM_MAX_UUID]; - char *targets; + unsigned long num_targets; struct dm_setup_target *target; int target_count; + struct dm_device *next; +}; + +struct dm_option { + char *start; + char *next; + size_t len; + char delim; +}; + +static struct { + unsigned long num_devices; + char *str; } dm_setup_args __initdata; static __initdata int dm_early_setup; -static size_t __init get_dm_option(char *str, char **next, char sep) +static int __init get_dm_option(struct dm_option *opt, const char *accept) { - size_t len = 0; - char *endp = NULL; + char *str = opt->next; + char *endp; if (!str) return 0; - endp = strchr(str, sep); + str = skip_spaces(str); + opt->start = str; + endp = strpbrk(str, accept); if (!endp) { /* act like strchrnul */ - len = strlen(str); - endp = str + len; + opt->len = strlen(str); + endp = str + opt->len; } else { - len = endp - str; + opt->len = endp - str; } - - if (endp == str) - return 0; - - if (!next) - return len; - + opt->delim = *endp; if (*endp == 0) { /* Don't advance past the nul. */ - *next = endp; + opt->next = endp; } else { - *next = endp + 1; + opt->next = endp + 1; } - return len; -} - -static int __init dm_setup_args_init(void) -{ - dm_setup_args.minor = 0; - dm_setup_args.ro = 0; - dm_setup_args.target = NULL; - dm_setup_args.target_count = 0; - return 0; + return opt->len != 0; } -static int __init dm_setup_cleanup(void) +static int __init dm_setup_cleanup(struct dm_device *devices) { - struct dm_setup_target *target = dm_setup_args.target; - struct dm_setup_target *old_target = NULL; - while (target) { - kfree(target->type); - kfree(target->params); - old_target = target; - target = target->next; - kfree(old_target); - dm_setup_args.target_count--; + struct dm_device *dev = devices; + + while (dev) { + struct dm_device *old_dev = dev; + struct dm_setup_target *target = dev->target; + while (target) { + struct dm_setup_target *old_target = target; + kfree(target->type); + kfree(target->params); + target = target->next; + kfree(old_target); + dev->target_count--; + } + BUG_ON(dev->target_count); + dev = dev->next; + kfree(old_dev); } - BUG_ON(dm_setup_args.target_count); return 0; } -static char * __init dm_setup_parse_device_args(char *str) +static char * __init dm_parse_device(struct dm_device *dev, char *str) { - char *next = NULL; - size_t len = 0; + struct dm_option opt; + size_t len; /* Grab the logical name of the device to be exported to udev */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device name"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.name)); - strlcpy(dm_setup_args.name, str, len); /* includes nul */ - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->name)); + strlcpy(dev->name, opt.start, len); /* includes nul */ /* Grab the UUID value or "none" */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device uuid"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.uuid)); - strlcpy(dm_setup_args.uuid, str, len); - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->uuid)); + strlcpy(dev->uuid, opt.start, len); /* Determine if the table/device will be read only or read-write */ - if (!strncmp("ro,", str, 3)) { - dm_setup_args.ro = 1; - } else if (!strncmp("rw,", str, 3)) { - dm_setup_args.ro = 0; + get_dm_option(&opt, DM_ANY_SEP); + if (!strncmp("ro", opt.start, opt.len)) { + dev->ro = 1; + } else if (!strncmp("rw", opt.start, opt.len)) { + dev->ro = 0; } else { DMERR("failed to parse table mode"); goto parse_fail; } - str = skip_spaces(str + 3); - return str; + /* Optional number field */ + /* XXX: The field will be mandatory in the next round */ + if (opt.delim == DM_FIELD_SEP[0]) { + if (!get_dm_option(&opt, DM_LINE_SEP)) + return NULL; + dev->num_targets = simple_strtoul(opt.start, NULL, 10); + } else { + dev->num_targets = 1; + } + if (dev->num_targets > DM_MAX_TARGETS) { + DMERR("too many targets %lu > %d", + dev->num_targets, DM_MAX_TARGETS); + } + return opt.next; parse_fail: return NULL; } -static void __init dm_substitute_devices(char *str, size_t str_len) +static char * __init dm_parse_targets(struct dm_device *dev, char *str) { - char *candidate = str; - char *candidate_end = str; - char old_char; - size_t len = 0; - dev_t dev; - - if (str_len < 3) - return; - - while (str && *str) { - candidate = strchr(str, '/'); - if (!candidate) - break; - - /* Avoid embedded slashes */ - if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { - str = strchr(candidate, DM_FIELD_SEP); - continue; - } - - len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); - str = skip_spaces(candidate_end); - if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ - continue; - - /* Temporarily terminate with a nul */ - if (*candidate_end) - candidate_end--; - old_char = *candidate_end; - *candidate_end = '\0'; - - DMDEBUG("converting candidate device '%s' to dev_t", candidate); - /* Use the boot-time specific device naming */ - dev = name_to_dev_t(candidate); - *candidate_end = old_char; - - DMDEBUG(" -> %u", dev); - /* No suitable replacement found */ - if (!dev) - continue; - - /* Rewrite the /dev/path as a major:minor */ - len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); - if (!len) { - DMERR("error substituting device major/minor."); - break; - } - candidate += len; - /* Pad out with spaces (fixing our nul) */ - while (candidate < candidate_end) - *(candidate++) = DM_FIELD_SEP; - } -} - -static int __init dm_setup_parse_targets(char *str) -{ - char *next = NULL; - size_t len = 0; - struct dm_setup_target **target = NULL; + struct dm_option opt; + struct dm_setup_target **target = &dev->target; + unsigned long num_targets = dev->num_targets; + unsigned long i; /* Targets are defined as per the table format but with a * comma as a newline separator. */ - target = &dm_setup_args.target; - while (str && *str) { + opt.next = str; + for (i = 0; i < num_targets; i++) { *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); if (!*target) { - DMERR("failed to allocate memory for target %d", - dm_setup_args.target_count); + DMERR("failed to allocate memory for target %s<%ld>", + dev->name, i); goto parse_fail; } - dm_setup_args.target_count++; + dev->target_count++; - (*target)->begin = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse starting sector for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse starting sector" + " for target %s<%ld>", dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); + (*target)->begin = simple_strtoull(opt.start, NULL, 10); - (*target)->length = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse length for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse length for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); - - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len || - !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse type for target %d", - dm_setup_args.target_count - 1); + (*target)->length = simple_strtoull(opt.start, NULL, 10); + + if (get_dm_option(&opt, DM_FIELD_SEP)) + (*target)->type = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->type)) { + DMERR("failed to parse type for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - len = get_dm_option(str, &next, DM_LINE_SEP); - if (!len || - !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse params for target %d", - dm_setup_args.target_count - 1); + if (get_dm_option(&opt, DM_LINE_SEP)) + (*target)->params = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->params)) { + DMERR("failed to parse params for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - /* Before moving on, walk through the copied target and - * attempt to replace all /dev/xxx with the major:minor number. - * It may not be possible to resolve them traditionally at - * boot-time. */ - dm_substitute_devices((*target)->params, len); - target = &((*target)->next); } - DMDEBUG("parsed %d targets", dm_setup_args.target_count); + DMDEBUG("parsed %d targets", dev->target_count); - return 0; + return opt.next; parse_fail: - return 1; + return NULL; +} + +static struct dm_device * __init dm_parse_args(void) +{ + struct dm_device *devices = NULL; + struct dm_device **tail = &devices; + struct dm_device *dev; + char *str = dm_setup_args.str; + unsigned long num_devices = dm_setup_args.num_devices; + unsigned long i; + + if (!str) + return NULL; + for (i = 0; i < num_devices; i++) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + DMERR("failed to allocated memory for dev"); + goto error; + } + *tail = dev; + tail = &dev->next; + /* + * devices are given minor numbers 0 - n-1 + * in the order they are found in the arg + * string. + */ + dev->minor = i; + str = dm_parse_device(dev, str); + if (!str) /* NULL indicates error in parsing, bail */ + goto error; + + str = dm_parse_targets(dev, str); + if (!str) + goto error; + } + return devices; +error: + dm_setup_cleanup(devices); + return NULL; } /* * Parse the command-line parameters given our kernel, but do not * actually try to invoke the DM device now; that is handled by - * dm_setup_drive after the low-level disk drivers have initialised. - * dm format is as follows: - * dm="name uuid fmode,[table line 1],[table line 2],..." - * May be used with root=/dev/dm-0 as it always uses the first dm minor. + * dm_setup_drives after the low-level disk drivers have initialised. + * dm format is described at the top of the file. + * + * Because dm minor numbers are assigned in assending order starting with 0, + * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1, + * and so forth. */ - static int __init dm_setup(char *str) { - dm_setup_args_init(); + struct dm_option opt; + unsigned long num_devices; - str = dm_setup_parse_device_args(str); if (!str) { DMDEBUG("str is NULL"); goto parse_fail; } - - /* Target parsing is delayed until we have dynamic memory */ - dm_setup_args.targets = str; - - printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", - dm_setup_args.name, dm_setup_args.minor); - + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) + goto parse_fail; + if (isdigit(opt.start[0])) { /* XXX: Optional number field */ + num_devices = simple_strtoul(opt.start, NULL, 10); + str = opt.next; + } else { + num_devices = 1; + /* Don't advance str */ + } + if (num_devices > DM_MAX_DEVICES) { + DMDEBUG("too many devices %lu > %d", + num_devices, DM_MAX_DEVICES); + } + dm_setup_args.str = str; + dm_setup_args.num_devices = num_devices; + DMINFO("will configure %lu devices", num_devices); dm_early_setup = 1; return 1; parse_fail: - printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + DMWARN("Invalid arguments supplied to dm=."); return 0; } - -static void __init dm_setup_drive(void) +static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; struct dm_table *table = NULL; struct dm_setup_target *target; - char *uuid = dm_setup_args.uuid; + struct dm_device *dev; + char *uuid; fmode_t fmode = FMODE_READ; + struct dm_device *devices; - /* Finish parsing the targets. */ - if (dm_setup_parse_targets(dm_setup_args.targets)) - goto parse_fail; - - if (dm_create(dm_setup_args.minor, &md)) { - DMDEBUG("failed to create the device"); - goto dm_create_fail; - } - DMDEBUG("created device '%s'", dm_device_name(md)); - - /* In addition to flagging the table below, the disk must be - * set explicitly ro/rw. */ - set_disk_ro(dm_disk(md), dm_setup_args.ro); + devices = dm_parse_args(); - if (!dm_setup_args.ro) - fmode |= FMODE_WRITE; - if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { - DMDEBUG("failed to create the table"); - goto dm_table_create_fail; - } + for (dev = devices; dev; dev = dev->next) { + if (dm_create(dev->minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* + * In addition to flagging the table below, the disk must be + * set explicitly ro/rw. + */ + set_disk_ro(dm_disk(md), dev->ro); + + if (!dev->ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dev->target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } - dm_lock_md_type(md); - target = dm_setup_args.target; - while (target) { - DMINFO("adding target '%llu %llu %s %s'", - (unsigned long long) target->begin, - (unsigned long long) target->length, target->type, - target->params); - if (dm_table_add_target(table, target->type, target->begin, - target->length, target->params)) { - DMDEBUG("failed to add the target to the table"); - goto add_target_fail; + dm_lock_md_type(md); + + for (target = dev->target; target; target = target->next) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, + target->type, target->params); + if (dm_table_add_target(table, target->type, + target->begin, + target->length, + target->params)) { + DMDEBUG("failed to add the target" + " to the table"); + goto add_target_fail; + } + } + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; } - target = target->next; - } - if (dm_table_complete(table)) { - DMDEBUG("failed to complete the table"); - goto table_complete_fail; - } + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } - if (dm_get_md_type(md) == DM_TYPE_NONE) { + /* Initial table load: acquire type of table. */ dm_set_md_type(md, dm_table_get_type(table)); + + /* Setup md->queue to reflect md's type. */ if (dm_setup_md_queue(md, table)) { DMWARN("unable to set up device queue for new table."); goto setup_md_queue_fail; } - } else if (dm_get_md_type(md) != dm_table_get_type(table)) { - DMWARN("can't change device type after initial table load."); - goto setup_md_queue_fail; - } - - /* Suspend the device so that we can bind it to the table. */ - if (dm_suspend(md, 0)) { - DMDEBUG("failed to suspend the device pre-bind"); - goto suspend_fail; - } - /* Bind the table to the device. This is the only way to associate - * md->map with the table and set the disk capacity directly. */ - if (dm_swap_table(md, table)) { /* should return NULL. */ - DMDEBUG("failed to bind the device to the table"); - goto table_bind_fail; - } + /* + * Bind the table to the device. This is the only way + * to associate md->map with the table and set the disk + * capacity directly. + */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } - /* Finally, resume and the device should be ready. */ - if (dm_resume(md)) { - DMDEBUG("failed to resume the device"); - goto resume_fail; - } + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } - /* Export the dm device via the ioctl interface */ - if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) - uuid = NULL; - if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { - DMDEBUG("failed to export device with given name and uuid"); - goto export_fail; - } - printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dev->uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dev->name, uuid)) { + DMDEBUG("failed to export device with given" + " name and uuid"); + goto export_fail; + } - dm_unlock_md_type(md); - dm_setup_cleanup(); + dm_unlock_md_type(md); + + DMINFO("dm-%d is ready", dev->minor); + } + dm_setup_cleanup(devices); return; export_fail: resume_fail: table_bind_fail: -suspend_fail: setup_md_queue_fail: +suspend_fail: table_complete_fail: add_target_fail: dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: - dm_setup_cleanup(); -parse_fail: - printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", - dm_setup_args.minor, dm_setup_args.name); + DMWARN("starting dm-%d (%s) failed", + dev->minor, dev->name); + dm_setup_cleanup(devices); } __setup("dm=", dm_setup); @@ -421,6 +465,6 @@ void __init dm_run_setup(void) { if (!dm_early_setup) return; - printk(KERN_INFO "dm: attempting early device configuration.\n"); - dm_setup_drive(); + DMINFO("attempting early device configuration."); + dm_setup_drives(); } From d503ea8e3ac1c088ccf4d9616b4845a919396067 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 14 Dec 2015 20:09:39 -0800 Subject: [PATCH 0409/1276] ANDROID: dm: android-verity: Add android verity target This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked to the actual data blocks in the block device. The signature of the metadata contents are verified against the key included in the system keyring. Upon success, the underlying verity target is setup. BUG: 27175947 Change-Id: I7e99644a0960ac8279f02c0158ed20999510ea97 Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch 56f6a6b2b1cd ("ANDROID: dm-android-verity: Rebase on top of 4.1")] Signed-off-by: Amit Pundir --- drivers/md/Kconfig | 16 + drivers/md/Makefile | 4 + drivers/md/dm-android-verity.c | 771 +++++++++++++++++++++++++++++++++ drivers/md/dm-android-verity.h | 92 ++++ drivers/md/dm-verity-target.c | 12 +- drivers/md/dm-verity.h | 12 + 6 files changed, 901 insertions(+), 6 deletions(-) create mode 100644 drivers/md/dm-android-verity.c create mode 100644 drivers/md/dm-android-verity.h diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 8b8c123cae66..7601e36a9ee0 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -559,4 +559,20 @@ config DM_ZONED If unsure, say N. +config DM_ANDROID_VERITY + bool "Android verity target support" + depends on DM_VERITY + depends on X509_CERTIFICATE_PARSER + depends on SYSTEM_TRUSTED_KEYRING + depends on PUBLIC_KEY_ALGO_RSA + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + ---help--- + This device-mapper target is virtually a VERITY target. This + target is setup by reading the metadata contents piggybacked + to the actual data blocks in the block device. The signature + of the metadata contents are verified against the key included + in the system keyring. Upon success, the underlying verity + target is setup. endif # MD diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 822f4e8753bc..dab38ff6f542 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -76,3 +76,7 @@ endif ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif + +ifeq ($(CONFIG_DM_ANDROID_VERITY),y) +dm-verity-objs += dm-android-verity.o +endif diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c new file mode 100644 index 000000000000..aeb5045830d9 --- /dev/null +++ b/drivers/md/dm-android-verity.c @@ -0,0 +1,771 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dm-verity.h" +#include "dm-android-verity.h" + +static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; +static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; + +static int __init verified_boot_state_param(char *line) +{ + strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate)); + return 1; +} + +__setup("androidboot.verifiedbootstate=", verified_boot_state_param); + +static int __init verity_mode_param(char *line) +{ + strlcpy(veritymode, line, sizeof(veritymode)); + return 1; +} + +__setup("androidboot.veritymode=", verity_mode_param); + +static int table_extract_mpi_array(struct public_key_signature *pks, + const void *data, size_t len) +{ + MPI mpi = mpi_read_raw_data(data, len); + + if (!mpi) { + DMERR("Error while allocating mpi array"); + return -ENOMEM; + } + + pks->mpi[0] = mpi; + pks->nr_mpi = 1; + return 0; +} + +static struct public_key_signature *table_make_digest( + enum hash_algo hash, + const void *table, + unsigned long table_len) +{ + struct public_key_signature *pks = NULL; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + int ret; + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return ERR_CAST(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of out + * context data and the digest output buffer on the end of that. + */ + ret = -ENOMEM; + pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); + if (!pks) + goto error; + + pks->pkey_hash_algo = hash; + pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; + pks->digest_size = digest_size; + + desc = (struct shash_desc *)(pks + 1); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_finup(desc, table, table_len, pks->digest); + if (ret < 0) + goto error; + + crypto_free_shash(tfm); + return pks; + +error: + kfree(pks); + crypto_free_shash(tfm); + return ERR_PTR(ret); +} + +static int read_block_dev(struct bio_read *payload, struct block_device *bdev, + sector_t offset, int length) +{ + struct bio *bio; + int err = 0, i; + + payload->number_of_pages = DIV_ROUND_UP(length, PAGE_SIZE); + + bio = bio_alloc(GFP_KERNEL, payload->number_of_pages); + if (!bio) { + DMERR("Error while allocating bio"); + return -ENOMEM; + } + + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = offset; + + payload->page_io = kzalloc(sizeof(struct page *) * + payload->number_of_pages, GFP_KERNEL); + if (!payload->page_io) { + DMERR("page_io array alloc failed"); + err = -ENOMEM; + goto free_bio; + } + + for (i = 0; i < payload->number_of_pages; i++) { + payload->page_io[i] = alloc_page(GFP_KERNEL); + if (!payload->page_io[i]) { + DMERR("alloc_page failed"); + err = -ENOMEM; + goto free_pages; + } + if (!bio_add_page(bio, payload->page_io[i], PAGE_SIZE, 0)) { + DMERR("bio_add_page error"); + err = -EIO; + goto free_pages; + } + } + + if (!submit_bio_wait(READ, bio)) + /* success */ + goto free_bio; + DMERR("bio read failed"); + err = -EIO; + +free_pages: + for (i = 0; i < payload->number_of_pages; i++) + if (payload->page_io[i]) + __free_page(payload->page_io[i]); + kfree(payload->page_io); +free_bio: + bio_put(bio); + return err; +} + +static inline u64 fec_div_round_up(u64 x, u64 y) +{ + u64 remainder; + + return div64_u64_rem(x, y, &remainder) + + (remainder > 0 ? 1 : 0); +} + +static inline void populate_fec_metadata(struct fec_header *header, + struct fec_ecc_metadata *ecc) +{ + ecc->blocks = fec_div_round_up(le64_to_cpu(header->inp_size), + FEC_BLOCK_SIZE); + ecc->roots = le32_to_cpu(header->roots); + ecc->start = le64_to_cpu(header->inp_size); +} + +static inline int validate_fec_header(struct fec_header *header, u64 offset) +{ + /* move offset to make the sanity check work for backup header + * as well. */ + offset -= offset % FEC_BLOCK_SIZE; + if (le32_to_cpu(header->magic) != FEC_MAGIC || + le32_to_cpu(header->version) != FEC_VERSION || + le32_to_cpu(header->size) != sizeof(struct fec_header) || + le32_to_cpu(header->roots) == 0 || + le32_to_cpu(header->roots) >= FEC_RSM || + offset < le32_to_cpu(header->fec_size) || + offset - le32_to_cpu(header->fec_size) != + le64_to_cpu(header->inp_size)) + return -EINVAL; + + return 0; +} + +static int extract_fec_header(dev_t dev, struct fec_header *fec, + struct fec_ecc_metadata *ecc) +{ + u64 device_size; + struct bio_read payload; + int i, err = 0; + struct block_device *bdev; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + + if (IS_ERR(bdev)) { + DMERR("bdev get error"); + return PTR_ERR(bdev); + } + + device_size = i_size_read(bdev->bd_inode); + + /* fec metadata size is a power of 2 and PAGE_SIZE + * is a power of 2 as well. + */ + BUG_ON(FEC_BLOCK_SIZE > PAGE_SIZE); + /* 512 byte sector alignment */ + BUG_ON(((device_size - FEC_BLOCK_SIZE) % (1 << SECTOR_SHIFT)) != 0); + + err = read_block_dev(&payload, bdev, (device_size - + FEC_BLOCK_SIZE) / (1 << SECTOR_SHIFT), FEC_BLOCK_SIZE); + if (err) { + DMERR("Error while reading verity metadata"); + goto error; + } + + BUG_ON(sizeof(struct fec_header) > PAGE_SIZE); + memcpy(fec, page_address(payload.page_io[0]), + sizeof(*fec)); + + ecc->valid = true; + if (validate_fec_header(fec, device_size - FEC_BLOCK_SIZE)) { + /* Try the backup header */ + memcpy(fec, page_address(payload.page_io[0]) + FEC_BLOCK_SIZE + - sizeof(*fec) , + sizeof(*fec)); + if (validate_fec_header(fec, device_size - + sizeof(struct fec_header))) + ecc->valid = false; + } + + if (ecc->valid) + populate_fec_metadata(fec, ecc); + + for (i = 0; i < payload.number_of_pages; i++) + __free_page(payload.page_io[i]); + kfree(payload.page_io); + +error: + blkdev_put(bdev, FMODE_READ); + return err; +} +static void find_metadata_offset(struct fec_header *fec, + struct block_device *bdev, u64 *metadata_offset) +{ + u64 device_size; + + device_size = i_size_read(bdev->bd_inode); + + if (le32_to_cpu(fec->magic) == FEC_MAGIC) + *metadata_offset = le64_to_cpu(fec->inp_size) - + VERITY_METADATA_SIZE; + else + *metadata_offset = device_size - VERITY_METADATA_SIZE; +} + +static struct android_metadata *extract_metadata(dev_t dev, + struct fec_header *fec) +{ + struct block_device *bdev; + struct android_metadata_header *header; + struct android_metadata *uninitialized_var(metadata); + int i; + u32 table_length, copy_length, offset; + u64 metadata_offset; + struct bio_read payload; + int err = 0; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + + if (IS_ERR(bdev)) { + DMERR("blkdev_get_by_dev failed"); + return ERR_CAST(bdev); + } + + find_metadata_offset(fec, bdev, &metadata_offset); + + /* Verity metadata size is a power of 2 and PAGE_SIZE + * is a power of 2 as well. + * PAGE_SIZE is also a multiple of 512 bytes. + */ + if (VERITY_METADATA_SIZE > PAGE_SIZE) + BUG_ON(VERITY_METADATA_SIZE % PAGE_SIZE != 0); + /* 512 byte sector alignment */ + BUG_ON(metadata_offset % (1 << SECTOR_SHIFT) != 0); + + err = read_block_dev(&payload, bdev, metadata_offset / + (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE); + if (err) { + DMERR("Error while reading verity metadata"); + metadata = ERR_PTR(err); + goto blkdev_release; + } + + header = kzalloc(sizeof(*header), GFP_KERNEL); + if (!header) { + DMERR("kzalloc failed for header"); + err = -ENOMEM; + goto free_payload; + } + + memcpy(header, page_address(payload.page_io[0]), + sizeof(*header)); + + DMINFO("bio magic_number:%u protocol_version:%d table_length:%u", + le32_to_cpu(header->magic_number), + le32_to_cpu(header->protocol_version), + le32_to_cpu(header->table_length)); + + metadata = kzalloc(sizeof(*metadata), GFP_KERNEL); + if (!metadata) { + DMERR("kzalloc for metadata failed"); + err = -ENOMEM; + goto free_header; + } + + metadata->header = header; + table_length = le32_to_cpu(header->table_length); + + if (table_length == 0 || + table_length > (VERITY_METADATA_SIZE - + sizeof(struct android_metadata_header))) + goto free_metadata; + + metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL); + + if (!metadata->verity_table) { + DMERR("kzalloc verity_table failed"); + err = -ENOMEM; + goto free_metadata; + } + + if (sizeof(struct android_metadata_header) + + table_length <= PAGE_SIZE) { + memcpy(metadata->verity_table, page_address(payload.page_io[0]) + + sizeof(struct android_metadata_header), + table_length); + } else { + copy_length = PAGE_SIZE - + sizeof(struct android_metadata_header); + memcpy(metadata->verity_table, page_address(payload.page_io[0]) + + sizeof(struct android_metadata_header), + copy_length); + table_length -= copy_length; + offset = copy_length; + i = 1; + while (table_length != 0) { + if (table_length > PAGE_SIZE) { + memcpy(metadata->verity_table + offset, + page_address(payload.page_io[i]), + PAGE_SIZE); + offset += PAGE_SIZE; + table_length -= PAGE_SIZE; + } else { + memcpy(metadata->verity_table + offset, + page_address(payload.page_io[i]), + table_length); + table_length = 0; + } + i++; + } + } + metadata->verity_table[table_length] = '\0'; + + goto free_payload; + +free_metadata: + kfree(metadata); +free_header: + kfree(header); + metadata = ERR_PTR(err); +free_payload: + for (i = 0; i < payload.number_of_pages; i++) + if (payload.page_io[i]) + __free_page(payload.page_io[i]); + kfree(payload.page_io); + + DMINFO("verity_table: %s", metadata->verity_table); +blkdev_release: + blkdev_put(bdev, FMODE_READ); + return metadata; +} + +/* helper functions to extract properties from dts */ +const char *find_dt_value(const char *name) +{ + struct device_node *firmware; + const char *value; + + firmware = of_find_node_by_path("/firmware/android"); + if (!firmware) + return NULL; + value = of_get_property(firmware, name, NULL); + of_node_put(firmware); + + return value; +} + +static bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + static const char verified_boot_prop[] = "verifiedbootstate"; + const char *value; + + value = find_dt_value(verified_boot_prop); + if (!value) + value = verifiedbootstate; + + return !strncmp(value, unlocked, sizeof(unlocked) - 1); +} + +static int verity_mode(void) +{ + static const char enforcing[] = "enforcing"; + static const char verified_mode_prop[] = "veritymode"; + const char *value; + + value = find_dt_value(verified_mode_prop); + if (!value) + value = veritymode; + if (!strncmp(value, enforcing, sizeof(enforcing) - 1)) + return DM_VERITY_MODE_RESTART; + + return DM_VERITY_MODE_EIO; +} + +static int verify_header(struct android_metadata_header *header) +{ + int retval = -EINVAL; + + if (is_unlocked() && le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE) { + retval = VERITY_STATE_DISABLE; + return retval; + } + + if (!(le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_NUMBER) || + (le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE)) { + DMERR("Incorrect magic number"); + return retval; + } + + if (le32_to_cpu(header->protocol_version) != + VERITY_METADATA_VERSION) { + DMERR("Unsupported version %u", + le32_to_cpu(header->protocol_version)); + return retval; + } + + return 0; +} + +static int verify_verity_signature(char *key_id, + struct android_metadata *metadata) +{ + key_ref_t key_ref; + struct key *key; + struct public_key_signature *pks = NULL; + int retval = -EINVAL; + + key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1), + &key_type_asymmetric, key_id); + + if (IS_ERR(key_ref)) { + DMERR("keyring: key not found"); + return -ENOKEY; + } + + key = key_ref_to_ptr(key_ref); + + pks = table_make_digest(HASH_ALGO_SHA256, + (const void *)metadata->verity_table, + le32_to_cpu(metadata->header->table_length)); + + if (IS_ERR(pks)) { + DMERR("hashing failed"); + goto error; + } + + retval = table_extract_mpi_array(pks, &metadata->header->signature[0], + RSANUMBYTES); + if (retval < 0) { + DMERR("Error extracting mpi %d", retval); + goto error; + } + + retval = verify_signature(key, pks); + mpi_free(pks->rsa.s); +error: + kfree(pks); + key_put(key); + + return retval; +} + +static void handle_error(void) +{ + int mode = verity_mode(); + if (mode == DM_VERITY_MODE_RESTART) { + DMERR("triggering restart"); + kernel_restart("dm-verity device corrupted"); + } else { + DMERR("Mounting verity root failed"); + } +} + +static inline bool test_mult_overflow(sector_t a, u32 b) +{ + sector_t r = (sector_t)~0ULL; + + sector_div(r, b); + return a > r; +} + +/* + * Target parameters: + * Key id of the public key in the system keyring. + * Verity metadata's signature would be verified against + * this. If the key id contains spaces, replace them + * with '#'. + * The block device for which dm-verity is being setup. + */ +static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) +{ + dev_t uninitialized_var(dev); + struct android_metadata *uninitialized_var(metadata); + int err = 0, i, mode; + char *key_id, *table_ptr, dummy, + *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; + /* One for specifying number of opt args and one for mode */ + sector_t data_sectors; + u32 data_block_size; + unsigned int major, minor, + no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; + struct fec_header uninitialized_var(fec); + struct fec_ecc_metadata uninitialized_var(ecc); + char buf[FEC_ARG_LENGTH], *buf_ptr; + unsigned long long tmpll; + + if (argc != 2) { + DMERR("Incorrect number of arguments"); + handle_error(); + return -EINVAL; + } + + /* should come as one of the arguments for the verity target */ + key_id = argv[0]; + strreplace(argv[0], '#', ' '); + + if (sscanf(argv[1], "%u:%u%c", &major, &minor, &dummy) == 2) { + dev = MKDEV(major, minor); + if (MAJOR(dev) != major || MINOR(dev) != minor) { + DMERR("Incorrect bdev major minor number"); + handle_error(); + return -EOVERFLOW; + } + } + + DMINFO("key:%s dev:%s", argv[0], argv[1]); + + if (extract_fec_header(dev, &fec, &ecc)) { + DMERR("Error while extracting fec header"); + handle_error(); + return -EINVAL; + } + + metadata = extract_metadata(dev, &fec); + + if (IS_ERR(metadata)) { + DMERR("Error while extracting metadata"); + handle_error(); + return -EINVAL; + } + + err = verify_header(metadata->header); + + if (err == VERITY_STATE_DISABLE) { + DMERR("Mounting root with verity disabled"); + return -EINVAL; + } else if (err) { + DMERR("Verity header handle error"); + handle_error(); + goto free_metadata; + } + + err = verify_verity_signature(key_id, metadata); + + if (err) { + DMERR("Signature verification failed"); + handle_error(); + goto free_metadata; + } else + DMINFO("Signature verification success"); + + table_ptr = metadata->verity_table; + + for (i = 0; i < VERITY_TABLE_ARGS; i++) { + verity_table_args[i] = strsep(&table_ptr, " "); + if (verity_table_args[i] == NULL) + break; + } + + if (i != VERITY_TABLE_ARGS) { + DMERR("Verity table not in the expected format"); + err = -EINVAL; + handle_error(); + goto free_metadata; + } + + if (sscanf(verity_table_args[5], "%llu%c", &tmpll, &dummy) + != 1) { + DMERR("Verity table not in the expected format"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + if (tmpll > ULONG_MAX) { + DMERR(" too large. Forgot to turn on CONFIG_LBDAF?"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + data_sectors = tmpll; + + if (sscanf(verity_table_args[3], "%u%c", &data_block_size, &dummy) + != 1) { + DMERR("Verity table not in the expected format"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + if (test_mult_overflow(data_sectors, data_block_size >> + SECTOR_SHIFT)) { + DMERR("data_sectors too large"); + handle_error(); + err = -EOVERFLOW; + goto free_metadata; + } + + data_sectors *= data_block_size >> SECTOR_SHIFT; + DMINFO("Data sectors %llu", (unsigned long long)data_sectors); + + /* update target length */ + ti->len = data_sectors; + + /*substitute data_dev and hash_dev*/ + verity_table_args[1] = argv[1]; + verity_table_args[2] = argv[1]; + + mode = verity_mode(); + + if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) { + if (mode) { + err = snprintf(buf, FEC_ARG_LENGTH, + "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, + 1 + VERITY_TABLE_OPT_FEC_ARGS, + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING, + argv[1], ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); + } else { + err = snprintf(buf, FEC_ARG_LENGTH, + "%u " VERITY_TABLE_OPT_FEC_FORMAT, + VERITY_TABLE_OPT_FEC_ARGS, argv[1], + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, ecc.roots); + } + } else if (mode) { + err = snprintf(buf, FEC_ARG_LENGTH, + "2 " VERITY_TABLE_OPT_IGNZERO " %s", + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING); + } else { + err = snprintf(buf, FEC_ARG_LENGTH, "1 %s", + "ignore_zero_blocks"); + } + + if (err < 0 || err >= FEC_ARG_LENGTH) + goto free_metadata; + + buf_ptr = buf; + + for (i = VERITY_TABLE_ARGS; i < (VERITY_TABLE_ARGS + + VERITY_TABLE_OPT_FEC_ARGS + 2); i++) { + verity_table_args[i] = strsep(&buf_ptr, " "); + if (verity_table_args[i] == NULL) { + no_of_args = i; + break; + } + } + + err = verity_ctr(ti, no_of_args, verity_table_args); + +free_metadata: + kfree(metadata->header); + kfree(metadata->verity_table); + kfree(metadata); + return err; +} + +static struct target_type android_verity_target = { + .name = "android-verity", + .version = {1, 0, 0}, + .module = THIS_MODULE, + .ctr = android_verity_ctr, + .dtr = verity_dtr, + .map = verity_map, + .status = verity_status, + .ioctl = verity_ioctl, + .merge = verity_merge, + .iterate_devices = verity_iterate_devices, + .io_hints = verity_io_hints, +}; + +static int __init dm_android_verity_init(void) +{ + int r; + + r = dm_register_target(&android_verity_target); + if (r < 0) + DMERR("register failed %d", r); + + return r; +} + +static void __exit dm_android_verity_exit(void) +{ + dm_unregister_target(&android_verity_target); +} + +module_init(dm_android_verity_init); +module_exit(dm_android_verity_exit); diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h new file mode 100644 index 000000000000..11477ffd2243 --- /dev/null +++ b/drivers/md/dm-android-verity.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef DM_ANDROID_VERITY_H +#define DM_ANDROID_VERITY_H + +#include + +#define RSANUMBYTES 256 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 +#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 +#define VERITY_METADATA_VERSION 0 +#define VERITY_STATE_DISABLE 1 +#define DATA_BLOCK_SIZE (4 * 1024) +#define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE) +#define VERITY_TABLE_ARGS 10 +#define VERITY_COMMANDLINE_PARAM_LENGTH 20 + +#define FEC_MAGIC 0xFECFECFE +#define FEC_BLOCK_SIZE (4 * 1024) +#define FEC_VERSION 0 +#define FEC_RSM 255 +#define FEC_ARG_LENGTH 300 + +#define VERITY_TABLE_OPT_RESTART "restart_on_corruption" +#define VERITY_TABLE_OPT_LOGGING "ignore_corruption" +#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks" + +#define VERITY_TABLE_OPT_FEC_FORMAT \ + "use_fec_from_device %s fec_start %llu fec_blocks %llu fec_roots %u ignore_zero_blocks" +#define VERITY_TABLE_OPT_FEC_ARGS 9 + +#define VERITY_DEBUG 0 + +#define DM_MSG_PREFIX "android-verity" +/* + * There can be two formats. + * if fec is present + * + * if fec is not present + * + */ +/* TODO: rearrange structure to reduce memory holes + * depends on userspace change. + */ +struct fec_header { + __le32 magic; + __le32 version; + __le32 size; + __le32 roots; + __le32 fec_size; + __le64 inp_size; + u8 hash[SHA256_DIGEST_SIZE]; +}; + +struct android_metadata_header { + __le32 magic_number; + __le32 protocol_version; + char signature[RSANUMBYTES]; + __le32 table_length; +}; + +struct android_metadata { + struct android_metadata_header *header; + char *verity_table; +}; + +struct fec_ecc_metadata { + bool valid; + u32 roots; + u64 blocks; + u64 rounds; + u64 start; +}; + +struct bio_read { + struct page **page_io; + int number_of_pages; +}; + +#endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index fc65f0dedf7f..fc7895c6dd5d 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -630,7 +630,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io) * Bio map function. It allocates dm_verity_io structure and bio vector and * fills them. Then it issues prefetches and the I/O. */ -static int verity_map(struct dm_target *ti, struct bio *bio) +int verity_map(struct dm_target *ti, struct bio *bio) { struct dm_verity *v = ti->private; struct dm_verity_io *io; @@ -675,7 +675,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio) /* * Status: V (valid) or C (corruption found) */ -static void verity_status(struct dm_target *ti, status_type_t type, +void verity_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct dm_verity *v = ti->private; @@ -751,7 +751,7 @@ static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev return 0; } -static int verity_iterate_devices(struct dm_target *ti, +int verity_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct dm_verity *v = ti->private; @@ -759,7 +759,7 @@ static int verity_iterate_devices(struct dm_target *ti, return fn(ti, v->data_dev, v->data_start, ti->len, data); } -static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) +void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) { struct dm_verity *v = ti->private; @@ -772,7 +772,7 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) blk_limits_io_min(limits, limits->logical_block_size); } -static void verity_dtr(struct dm_target *ti) +void verity_dtr(struct dm_target *ti) { struct dm_verity *v = ti->private; @@ -927,7 +927,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) * * Hex string or "-" if no salt. */ -static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) +int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) { struct dm_verity *v; struct dm_arg_set as; diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 3441c10b840c..b07e2e5bd952 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -127,4 +127,16 @@ extern int verity_hash(struct dm_verity *v, struct ahash_request *req, extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, sector_t block, u8 *digest, bool *is_zero); +extern void verity_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen); +extern int verity_ioctl(struct dm_target *ti, unsigned cmd, + unsigned long arg); +extern int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); +extern int verity_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data); +extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); +extern void verity_dtr(struct dm_target *ti); +extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); +extern int verity_map(struct dm_target *ti, struct bio *bio); #endif /* DM_VERITY_H */ From e62d56662018a9eb7df018d62f72637979b148e1 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 21 Mar 2016 10:55:23 -0700 Subject: [PATCH 0410/1276] ANDROID: dm: android-verity: Mounting root as linear device when verity disabled This CL makes android-verity target to be added as linear dm device if when bootloader is unlocked and verity is disabled. Bug: 27175947 Change-Id: Ic41ca4b8908fb2777263799cf3a3e25934d70f18 Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch 7e70218c2699 ("ANDROID: dm: Minor cleanup") 67584ff8412b ("ANDROID: dm: rename dm-linear methods for dm-android-verity")] Signed-off-by: Amit Pundir --- drivers/md/dm-android-verity.c | 128 +++++++++++++++++++++++++++------ drivers/md/dm-android-verity.h | 17 +++++ drivers/md/dm-linear.c | 24 +++---- 3 files changed, 135 insertions(+), 34 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index aeb5045830d9..b7e059595f75 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -43,6 +44,25 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; +static bool target_added; +static bool verity_enabled = true; +struct dentry *debug_dir; +static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv); + +static struct target_type android_verity_target = { + .name = "android-verity", + .version = {1, 0, 0}, + .module = THIS_MODULE, + .ctr = android_verity_ctr, + .dtr = verity_dtr, + .map = verity_map, + .status = verity_status, + .ioctl = verity_ioctl, + .merge = verity_merge, + .iterate_devices = verity_iterate_devices, + .io_hints = verity_io_hints, +}; + static int __init verified_boot_state_param(char *line) { strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate)); @@ -549,6 +569,32 @@ static inline bool test_mult_overflow(sector_t a, u32 b) return a > r; } +static int add_as_linear_device(struct dm_target *ti, char *dev) +{ + /*Move to linear mapping defines*/ + char *linear_table_args[DM_LINEAR_ARGS] = {dev, + DM_LINEAR_TARGET_OFFSET}; + int err = 0; + + android_verity_target.dtr = dm_linear_dtr, + android_verity_target.map = dm_linear_map, + android_verity_target.status = dm_linear_status, + android_verity_target.ioctl = dm_linear_ioctl, + android_verity_target.merge = dm_linear_merge, + android_verity_target.iterate_devices = dm_linear_iterate_devices, + android_verity_target.io_hints = NULL; + + err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); + + if (!err) { + DMINFO("Added android-verity as a linear target"); + target_added = true; + } else + DMERR("Failed to add android-verity as linear target"); + + return err; +} + /* * Target parameters: * Key id of the public key in the system keyring. @@ -613,21 +659,27 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) if (err == VERITY_STATE_DISABLE) { DMERR("Mounting root with verity disabled"); - return -EINVAL; + verity_enabled = false; + /* we would still have to parse the args to figure out + * the data blocks size. Or may be could map the entire + * partition similar to mounting the device. + */ } else if (err) { DMERR("Verity header handle error"); handle_error(); goto free_metadata; } - err = verify_verity_signature(key_id, metadata); + if (!verity_enabled) { + err = verify_verity_signature(key_id, metadata); - if (err) { - DMERR("Signature verification failed"); - handle_error(); - goto free_metadata; - } else - DMINFO("Signature verification success"); + if (err) { + DMERR("Signature verification failed"); + handle_error(); + goto free_metadata; + } else + DMINFO("Signature verification success"); + } table_ptr = metadata->verity_table; @@ -683,6 +735,12 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* update target length */ ti->len = data_sectors; + /* Setup linear target and free */ + if (!verity_enabled) { + err = add_as_linear_device(ti, argv[1]); + goto free_metadata; + } + /*substitute data_dev and hash_dev*/ verity_table_args[1] = argv[1]; verity_table_args[2] = argv[1]; @@ -730,6 +788,13 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = verity_ctr(ti, no_of_args, verity_table_args); + if (err) + DMERR("android-verity failed to mount as verity target"); + else { + target_added = true; + DMINFO("android-verity mounted as verity target"); + } + free_metadata: kfree(metadata->header); kfree(metadata->verity_table); @@ -737,33 +802,52 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return err; } -static struct target_type android_verity_target = { - .name = "android-verity", - .version = {1, 0, 0}, - .module = THIS_MODULE, - .ctr = android_verity_ctr, - .dtr = verity_dtr, - .map = verity_map, - .status = verity_status, - .ioctl = verity_ioctl, - .merge = verity_merge, - .iterate_devices = verity_iterate_devices, - .io_hints = verity_io_hints, -}; - static int __init dm_android_verity_init(void) { int r; + struct dentry *file; r = dm_register_target(&android_verity_target); if (r < 0) DMERR("register failed %d", r); + /* Tracks the status of the last added target */ + debug_dir = debugfs_create_dir("android_verity", NULL); + + if (IS_ERR_OR_NULL(debug_dir)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + goto end; + } + + file = debugfs_create_bool("target_added", S_IRUGO, debug_dir, + (u32 *)&target_added); + + if (IS_ERR_OR_NULL(file)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + debugfs_remove_recursive(debug_dir); + goto end; + } + + file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir, + (u32 *)&verity_enabled); + + if (IS_ERR_OR_NULL(file)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + debugfs_remove_recursive(debug_dir); + } + +end: return r; } static void __exit dm_android_verity_exit(void) { + if (!IS_ERR_OR_NULL(debug_dir)) + debugfs_remove_recursive(debug_dir); + dm_unregister_target(&android_verity_target); } diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 11477ffd2243..efb796524896 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -44,6 +44,10 @@ #define VERITY_DEBUG 0 #define DM_MSG_PREFIX "android-verity" + +#define DM_LINEAR_ARGS 2 +#define DM_LINEAR_TARGET_OFFSET "0" + /* * There can be two formats. * if fec is present @@ -89,4 +93,17 @@ struct bio_read { int number_of_pages; }; +extern struct target_type linear_target; + +extern void dm_linear_dtr(struct dm_target *ti); +extern int dm_linear_map(struct dm_target *ti, struct bio *bio); +extern void dm_linear_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen); +extern int dm_linear_ioctl(struct dm_target *ti, unsigned int cmd, + unsigned long arg); +extern int dm_linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); +extern int dm_linear_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data); +extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); #endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 2f7c44a006c4..33ad42c56b7b 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -26,7 +26,7 @@ struct linear_c { /* * Construct a linear mapping: */ -static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) +int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct linear_c *lc; unsigned long long tmp; @@ -70,7 +70,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) return ret; } -static void linear_dtr(struct dm_target *ti) +void dm_linear_dtr(struct dm_target *ti) { struct linear_c *lc = (struct linear_c *) ti->private; @@ -95,7 +95,7 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio) linear_map_sector(ti, bio->bi_iter.bi_sector); } -static int linear_map(struct dm_target *ti, struct bio *bio) +int dm_linear_map(struct dm_target *ti, struct bio *bio) { linear_map_bio(ti, bio); @@ -115,7 +115,7 @@ static int linear_end_io(struct dm_target *ti, struct bio *bio, } #endif -static void linear_status(struct dm_target *ti, status_type_t type, +void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct linear_c *lc = (struct linear_c *) ti->private; @@ -132,7 +132,7 @@ static void linear_status(struct dm_target *ti, status_type_t type, } } -static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +static int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct linear_c *lc = (struct linear_c *) ti->private; struct dm_dev *dev = lc->dev; @@ -148,7 +148,7 @@ static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev return 0; } -static int linear_iterate_devices(struct dm_target *ti, +int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct linear_c *lc = ti->private; @@ -217,12 +217,12 @@ static struct target_type linear_target = { .features = DM_TARGET_PASSES_INTEGRITY, #endif .module = THIS_MODULE, - .ctr = linear_ctr, - .dtr = linear_dtr, - .map = linear_map, - .status = linear_status, - .prepare_ioctl = linear_prepare_ioctl, - .iterate_devices = linear_iterate_devices, + .ctr = dm_linear_ctr, + .dtr = dm_linear_dtr, + .map = dm_linear_map, + .status = dm_linear_status, + .prepare_ioctl = dm_linear_prepare_ioctl, + .iterate_devices = dm_linear_iterate_devices, .direct_access = linear_dax_direct_access, .dax_copy_from_iter = linear_dax_copy_from_iter, .dax_copy_to_iter = linear_dax_copy_to_iter, From 4ef66b5cc55d95042c9a295ec446618a5a601741 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 15 Apr 2016 13:32:54 +0200 Subject: [PATCH 0411/1276] ANDROID: dm: android-verity: use name_to_dev_t This patch makes android_verity_ctr() parse its block device string parameter with name_to_dev_t(). It allows the use of less hardware related block device reference like PARTUUID for instance. Change-Id: Idb84453e70cc11abd5ef3a0adfbb16f8b5feaf07 Signed-off-by: Jeremy Compostella --- drivers/md/dm-android-verity.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index b7e059595f75..9c26cbb5f179 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -613,8 +613,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* One for specifying number of opt args and one for mode */ sector_t data_sectors; u32 data_block_size; - unsigned int major, minor, - no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; + unsigned int no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; struct fec_header uninitialized_var(fec); struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; @@ -630,13 +629,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) key_id = argv[0]; strreplace(argv[0], '#', ' '); - if (sscanf(argv[1], "%u:%u%c", &major, &minor, &dummy) == 2) { - dev = MKDEV(major, minor); - if (MAJOR(dev) != major || MINOR(dev) != minor) { - DMERR("Incorrect bdev major minor number"); - handle_error(); - return -EOVERFLOW; - } + dev = name_to_dev_t(argv[1]); + if (!dev) { + DMERR("no dev found for %s", argv[1]); + handle_error(); + return -EINVAL; } DMINFO("key:%s dev:%s", argv[0], argv[1]); From 3c687674be31f4eaada70e8432599e138ae3e13f Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 20 May 2016 16:44:19 -0700 Subject: [PATCH 0412/1276] ANDROID: dm: android-verity: fix signature verification flag The bug was that the signature verification was only happening when verity was disabled. It should always happen when verity is enabled. Signed-off-by: Badhri Jagan Sridharan Change-Id: I2d9354e240d36ea06fc68c2d18d8e87b823a4c2f (cherry picked from commit 5364b5ca0b1a12a58283b51408e43fc36d4e4fe7) --- drivers/md/dm-android-verity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 9c26cbb5f179..00275a986d03 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -667,7 +667,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) goto free_metadata; } - if (!verity_enabled) { + if (verity_enabled) { err = verify_verity_signature(key_id, metadata); if (err) { From 50cb85d259af05dca914dc09312cefa3433247f0 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 20 May 2016 16:45:45 -0700 Subject: [PATCH 0413/1276] ANDROID: dm: android-verity: use default verity public key If the dm-android-verity target does not provide a default key try using the default public key from the system keyring. The defualt verity keyid is passed as a kernel command line argument veritykeyid=. The order of the dm-android-verity params have been reversed to facilitate the change. Old format example: dm="system none ro,0 1 android-verity Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f /dev/mmcblk0p43" New formats supported: dm="system none ro,0 1 android-verity /dev/mmcblk0p43 Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f" (or) dm="system none ro,0 1 android-verity /dev/mmcblk0p43" when veritykeyid= is set in the kernel command line. BUG: 28384658 Signed-off-by: Badhri Jagan Sridharan Change-Id: I506c89b053d835ab579e703eef2bc1f8487250de (cherry picked from commit c5c74d0327729f35b576564976885596c6d0e7fb) --- drivers/md/dm-android-verity.c | 67 ++++++++++++++++++++++++---------- drivers/md/dm-android-verity.h | 16 ++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 00275a986d03..097fb2b1de89 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -43,6 +43,7 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; +static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH]; static bool target_added; static bool verity_enabled = true; @@ -79,6 +80,19 @@ static int __init verity_mode_param(char *line) __setup("androidboot.veritymode=", verity_mode_param); +static int __init verity_keyid_param(char *line) +{ + strlcpy(veritykeyid, line, sizeof(veritykeyid)); + return 1; +} + +__setup("veritykeyid=", verity_keyid_param); + +static inline bool default_verity_key_id(void) +{ + return veritykeyid[0] != '\0'; +} + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -608,7 +622,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *uninitialized_var(metadata); int err = 0, i, mode; - char *key_id, *table_ptr, dummy, + char *key_id, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; @@ -619,24 +633,34 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - if (argc != 2) { + if (argc == 1) { + /* Use the default keyid */ + if (default_verity_key_id()) + key_id = veritykeyid; + else { + DMERR("veritykeyid= is not set"); + handle_error(); + return -EINVAL; + } + } else if (argc == 2) + key_id = argv[1]; + else { DMERR("Incorrect number of arguments"); handle_error(); return -EINVAL; } - /* should come as one of the arguments for the verity target */ - key_id = argv[0]; - strreplace(argv[0], '#', ' '); + strreplace(key_id, '#', ' '); + target_device = argv[0]; - dev = name_to_dev_t(argv[1]); + dev = name_to_dev_t(target_device); if (!dev) { - DMERR("no dev found for %s", argv[1]); + DMERR("no dev found for %s", target_device); handle_error(); return -EINVAL; } - DMINFO("key:%s dev:%s", argv[0], argv[1]); + DMINFO("key:%s dev:%s", key_id, target_device); if (extract_fec_header(dev, &fec, &ecc)) { DMERR("Error while extracting fec header"); @@ -734,30 +758,33 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* Setup linear target and free */ if (!verity_enabled) { - err = add_as_linear_device(ti, argv[1]); + err = add_as_linear_device(ti, target_device); goto free_metadata; } /*substitute data_dev and hash_dev*/ - verity_table_args[1] = argv[1]; - verity_table_args[2] = argv[1]; + verity_table_args[1] = target_device; + verity_table_args[2] = target_device; mode = verity_mode(); if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) { if (mode) { err = snprintf(buf, FEC_ARG_LENGTH, - "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, - 1 + VERITY_TABLE_OPT_FEC_ARGS, - mode == DM_VERITY_MODE_RESTART ? - VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING, - argv[1], ecc.start / FEC_BLOCK_SIZE, ecc.blocks, - ecc.roots); + "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, + 1 + VERITY_TABLE_OPT_FEC_ARGS, + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : + VERITY_TABLE_OPT_LOGGING, + target_device, + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); } else { err = snprintf(buf, FEC_ARG_LENGTH, - "%u " VERITY_TABLE_OPT_FEC_FORMAT, - VERITY_TABLE_OPT_FEC_ARGS, argv[1], - ecc.start / FEC_BLOCK_SIZE, ecc.blocks, ecc.roots); + "%u " VERITY_TABLE_OPT_FEC_FORMAT, + VERITY_TABLE_OPT_FEC_ARGS, target_device, + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); } } else if (mode) { err = snprintf(buf, FEC_ARG_LENGTH, diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index efb796524896..43655ee0f813 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -27,6 +27,22 @@ #define VERITY_TABLE_ARGS 10 #define VERITY_COMMANDLINE_PARAM_LENGTH 20 +/* + * : is the format for the identifier. + * subject can either be the Common Name(CN) + Organization Name(O) or + * just the CN if the it is prefixed with O + * From https://tools.ietf.org/html/rfc5280#appendix-A + * ub-organization-name-length INTEGER ::= 64 + * ub-common-name-length INTEGER ::= 64 + * + * http://lxr.free-electrons.com/source/crypto/asymmetric_keys/x509_cert_parser.c?v=3.9#L278 + * ctx->o_size + 2 + ctx->cn_size + 1 + * + 41 characters for ":" and sha1 id + * 64 + 2 + 64 + 1 + 1 + 40 (172) + * setting VERITY_DEFAULT_KEY_ID_LENGTH to 200 characters. + */ +#define VERITY_DEFAULT_KEY_ID_LENGTH 200 + #define FEC_MAGIC 0xFECFECFE #define FEC_BLOCK_SIZE (4 * 1024) #define FEC_VERSION 0 From 72ad7a11fa2cd13fbe001efde12b9385f85f4820 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 17 Jun 2016 18:54:35 -0700 Subject: [PATCH 0414/1276] ANDROID: dm: android-verity: mount as linear target if eng build eng builds dont have verity enabled i.e it does even have verity metadata appended to the parition. Therefore add rootdev as linear device and map the entire partition if build variant is "eng". (Cherry-picked based on https://partner-android-review.git.corp.google.com/#/c/618690/) BUG: 29276559 Signed-off-by: Badhri Jagan Sridharan Change-Id: I8f5c2289b842b820ca04f5773525e5449bb3f355 --- drivers/md/dm-android-verity.c | 62 +++++++++++++++++++++++++++++++--- drivers/md/dm-android-verity.h | 1 + 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 097fb2b1de89..e1a8e284e7e4 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -44,6 +44,7 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH]; +static char buildvariant[BUILD_VARIANT]; static bool target_added; static bool verity_enabled = true; @@ -88,11 +89,26 @@ static int __init verity_keyid_param(char *line) __setup("veritykeyid=", verity_keyid_param); +static int __init verity_buildvariant(char *line) +{ + strlcpy(buildvariant, line, sizeof(buildvariant)); + return 1; +} + +__setup("buildvariant=", verity_buildvariant); + static inline bool default_verity_key_id(void) { return veritykeyid[0] != '\0'; } +static inline bool is_eng(void) +{ + static const char typeeng[] = "eng"; + + return !strncmp(buildvariant, typeeng, sizeof(typeeng)); +} + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -262,7 +278,7 @@ static int extract_fec_header(dev_t dev, struct fec_header *fec, bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); - if (IS_ERR(bdev)) { + if (IS_ERR_OR_NULL(bdev)) { DMERR("bdev get error"); return PTR_ERR(bdev); } @@ -323,6 +339,24 @@ static void find_metadata_offset(struct fec_header *fec, *metadata_offset = device_size - VERITY_METADATA_SIZE; } +static int find_size(dev_t dev, u64 *device_size) +{ + struct block_device *bdev; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + if (IS_ERR_OR_NULL(bdev)) { + DMERR("blkdev_get_by_dev failed"); + return PTR_ERR(bdev); + } + + *device_size = i_size_read(bdev->bd_inode); + *device_size >>= SECTOR_SHIFT; + + DMINFO("blkdev size in sectors: %llu", *device_size); + blkdev_put(bdev, FMODE_READ); + return 0; +} + static struct android_metadata *extract_metadata(dev_t dev, struct fec_header *fec) { @@ -337,7 +371,7 @@ static struct android_metadata *extract_metadata(dev_t dev, bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); - if (IS_ERR(bdev)) { + if (IS_ERR_OR_NULL(bdev)) { DMERR("blkdev_get_by_dev failed"); return ERR_CAST(bdev); } @@ -632,12 +666,13 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; + u64 device_size; if (argc == 1) { /* Use the default keyid */ if (default_verity_key_id()) key_id = veritykeyid; - else { + else if (!is_eng()) { DMERR("veritykeyid= is not set"); handle_error(); return -EINVAL; @@ -650,7 +685,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - strreplace(key_id, '#', ' '); target_device = argv[0]; dev = name_to_dev_t(target_device); @@ -660,6 +694,26 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } + if (is_eng()) { + err = find_size(dev, &device_size); + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; + } + + strreplace(key_id, '#', ' '); + DMINFO("key:%s dev:%s", key_id, target_device); if (extract_fec_header(dev, &fec, &ecc)) { diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 43655ee0f813..782e1c815c67 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -26,6 +26,7 @@ #define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE) #define VERITY_TABLE_ARGS 10 #define VERITY_COMMANDLINE_PARAM_LENGTH 20 +#define BUILD_VARIANT 20 /* * : is the format for the identifier. From 03f4a7d5be315dd3d4c2c77c95b295d402c5aedf Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 27 Jun 2016 16:25:55 -0700 Subject: [PATCH 0415/1276] ANDROID: dm: android-verity: allow adb disable-verity only in userdebug adb disable-verity was allowed when the phone is in the unlocked state. Since the driver is now aware of the build variant, honor "adb disable-verity" only in userdebug builds. (Cherry-picked from https://partner-android-review.git.corp.google.com/#/c/622117) BUG: 29276559 Signed-off-by: Badhri Jagan Sridharan Change-Id: I7ce9f38d8c7a62361392c5a8ccebb288f8a3a2ea --- drivers/md/dm-android-verity.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index e1a8e284e7e4..999e75bf2ba0 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -109,6 +109,14 @@ static inline bool is_eng(void) return !strncmp(buildvariant, typeeng, sizeof(typeeng)); } +static inline bool is_userdebug(void) +{ + static const char typeuserdebug[] = "userdebug"; + + return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); +} + + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -499,19 +507,6 @@ const char *find_dt_value(const char *name) return value; } -static bool is_unlocked(void) -{ - static const char unlocked[] = "orange"; - static const char verified_boot_prop[] = "verifiedbootstate"; - const char *value; - - value = find_dt_value(verified_boot_prop); - if (!value) - value = verifiedbootstate; - - return !strncmp(value, unlocked, sizeof(unlocked) - 1); -} - static int verity_mode(void) { static const char enforcing[] = "enforcing"; @@ -531,7 +526,7 @@ static int verify_header(struct android_metadata_header *header) { int retval = -EINVAL; - if (is_unlocked() && le32_to_cpu(header->magic_number) == + if (is_userdebug() && le32_to_cpu(header->magic_number) == VERITY_METADATA_MAGIC_DISABLE) { retval = VERITY_STATE_DISABLE; return retval; From 856bbbf91913034cc4a5d34c9f1d2b9ec1c2b7b9 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 6 Jul 2016 17:16:19 -0700 Subject: [PATCH 0416/1276] ANDROID: dm: android-verity: Verify header before fetching table Move header validation logic before reading the verity_table as an invalid header implies the table is invalid as well. (Cherry-picked from: https://partner-android-review.git.corp.google.com/#/c/625203) BUG: 29940612 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib34d25c0854202f3e70df0a6d0ef1d96f0250c8e --- drivers/md/dm-android-verity.c | 140 +++++++++++++++++---------------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 999e75bf2ba0..1f4eb099209d 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -365,12 +365,38 @@ static int find_size(dev_t dev, u64 *device_size) return 0; } -static struct android_metadata *extract_metadata(dev_t dev, - struct fec_header *fec) +static int verify_header(struct android_metadata_header *header) +{ + int retval = -EINVAL; + + if (is_userdebug() && le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE) + return VERITY_STATE_DISABLE; + + if (!(le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_NUMBER) || + (le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE)) { + DMERR("Incorrect magic number"); + return retval; + } + + if (le32_to_cpu(header->protocol_version) != + VERITY_METADATA_VERSION) { + DMERR("Unsupported version %u", + le32_to_cpu(header->protocol_version)); + return retval; + } + + return 0; +} + +static int extract_metadata(dev_t dev, struct fec_header *fec, + struct android_metadata **metadata, + bool *verity_enabled) { struct block_device *bdev; struct android_metadata_header *header; - struct android_metadata *uninitialized_var(metadata); int i; u32 table_length, copy_length, offset; u64 metadata_offset; @@ -381,7 +407,7 @@ static struct android_metadata *extract_metadata(dev_t dev, if (IS_ERR_OR_NULL(bdev)) { DMERR("blkdev_get_by_dev failed"); - return ERR_CAST(bdev); + return -ENODEV; } find_metadata_offset(fec, bdev, &metadata_offset); @@ -399,7 +425,6 @@ static struct android_metadata *extract_metadata(dev_t dev, (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE); if (err) { DMERR("Error while reading verity metadata"); - metadata = ERR_PTR(err); goto blkdev_release; } @@ -418,24 +443,42 @@ static struct android_metadata *extract_metadata(dev_t dev, le32_to_cpu(header->protocol_version), le32_to_cpu(header->table_length)); - metadata = kzalloc(sizeof(*metadata), GFP_KERNEL); - if (!metadata) { + err = verify_header(header); + + if (err == VERITY_STATE_DISABLE) { + DMERR("Mounting root with verity disabled"); + *verity_enabled = false; + /* we would still have to read the metadata to figure out + * the data blocks size. Or may be could map the entire + * partition similar to mounting the device. + * + * Reset error as well as the verity_enabled flag is changed. + */ + err = 0; + } else if (err) + goto free_header; + + *metadata = kzalloc(sizeof(**metadata), GFP_KERNEL); + if (!*metadata) { DMERR("kzalloc for metadata failed"); err = -ENOMEM; goto free_header; } - metadata->header = header; + (*metadata)->header = header; table_length = le32_to_cpu(header->table_length); if (table_length == 0 || table_length > (VERITY_METADATA_SIZE - - sizeof(struct android_metadata_header))) + sizeof(struct android_metadata_header))) { + DMERR("table_length too long"); + err = -EINVAL; goto free_metadata; + } - metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL); + (*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL); - if (!metadata->verity_table) { + if (!(*metadata)->verity_table) { DMERR("kzalloc verity_table failed"); err = -ENOMEM; goto free_metadata; @@ -443,13 +486,15 @@ static struct android_metadata *extract_metadata(dev_t dev, if (sizeof(struct android_metadata_header) + table_length <= PAGE_SIZE) { - memcpy(metadata->verity_table, page_address(payload.page_io[0]) + memcpy((*metadata)->verity_table, + page_address(payload.page_io[0]) + sizeof(struct android_metadata_header), table_length); } else { copy_length = PAGE_SIZE - sizeof(struct android_metadata_header); - memcpy(metadata->verity_table, page_address(payload.page_io[0]) + memcpy((*metadata)->verity_table, + page_address(payload.page_io[0]) + sizeof(struct android_metadata_header), copy_length); table_length -= copy_length; @@ -457,13 +502,13 @@ static struct android_metadata *extract_metadata(dev_t dev, i = 1; while (table_length != 0) { if (table_length > PAGE_SIZE) { - memcpy(metadata->verity_table + offset, + memcpy((*metadata)->verity_table + offset, page_address(payload.page_io[i]), PAGE_SIZE); offset += PAGE_SIZE; table_length -= PAGE_SIZE; } else { - memcpy(metadata->verity_table + offset, + memcpy((*metadata)->verity_table + offset, page_address(payload.page_io[i]), table_length); table_length = 0; @@ -471,25 +516,23 @@ static struct android_metadata *extract_metadata(dev_t dev, i++; } } - metadata->verity_table[table_length] = '\0'; + (*metadata)->verity_table[table_length] = '\0'; + DMINFO("verity_table: %s", (*metadata)->verity_table); goto free_payload; free_metadata: - kfree(metadata); + kfree(*metadata); free_header: kfree(header); - metadata = ERR_PTR(err); free_payload: for (i = 0; i < payload.number_of_pages; i++) if (payload.page_io[i]) __free_page(payload.page_io[i]); kfree(payload.page_io); - - DMINFO("verity_table: %s", metadata->verity_table); blkdev_release: blkdev_put(bdev, FMODE_READ); - return metadata; + return err; } /* helper functions to extract properties from dts */ @@ -522,34 +565,6 @@ static int verity_mode(void) return DM_VERITY_MODE_EIO; } -static int verify_header(struct android_metadata_header *header) -{ - int retval = -EINVAL; - - if (is_userdebug() && le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_DISABLE) { - retval = VERITY_STATE_DISABLE; - return retval; - } - - if (!(le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_NUMBER) || - (le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_DISABLE)) { - DMERR("Incorrect magic number"); - return retval; - } - - if (le32_to_cpu(header->protocol_version) != - VERITY_METADATA_VERSION) { - DMERR("Unsupported version %u", - le32_to_cpu(header->protocol_version)); - return retval; - } - - return 0; -} - static int verify_verity_signature(char *key_id, struct android_metadata *metadata) { @@ -649,7 +664,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) { dev_t uninitialized_var(dev); - struct android_metadata *uninitialized_var(metadata); + struct android_metadata *metadata = NULL; int err = 0, i, mode; char *key_id, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; @@ -717,26 +732,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - metadata = extract_metadata(dev, &fec); + err = extract_metadata(dev, &fec, &metadata, &verity_enabled); - if (IS_ERR(metadata)) { + if (err) { DMERR("Error while extracting metadata"); handle_error(); - return -EINVAL; - } - - err = verify_header(metadata->header); - - if (err == VERITY_STATE_DISABLE) { - DMERR("Mounting root with verity disabled"); - verity_enabled = false; - /* we would still have to parse the args to figure out - * the data blocks size. Or may be could map the entire - * partition similar to mounting the device. - */ - } else if (err) { - DMERR("Verity header handle error"); - handle_error(); goto free_metadata; } @@ -869,8 +869,10 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) } free_metadata: - kfree(metadata->header); - kfree(metadata->verity_table); + if (metadata) { + kfree(metadata->header); + kfree(metadata->verity_table); + } kfree(metadata); return err; } From bc554844d07cab7d6fd52e1ce6f7ff9bfb1dea57 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 10 May 2016 13:10:20 +0200 Subject: [PATCH 0417/1276] ANDROID: dm: android-verity: pack the fec_header structure The fec_header structure is generated build time and stored on disk. The fec_header might be build on a 64 bits machine while it is read per a 32 bits device or the other way around. In such situations, the fec_header fields are not aligned as expected by the device and it fails to read the fec_header structure. This patch makes the fec_header packed. Change-Id: Idb84453e70cc11abd5ef3a0adfbb16f8b5feaf06 Signed-off-by: Jeremy Compostella --- drivers/md/dm-android-verity.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 782e1c815c67..f43b02fbb475 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -72,9 +72,6 @@ * if fec is not present * */ -/* TODO: rearrange structure to reduce memory holes - * depends on userspace change. - */ struct fec_header { __le32 magic; __le32 version; @@ -83,7 +80,7 @@ struct fec_header { __le32 fec_size; __le64 inp_size; u8 hash[SHA256_DIGEST_SIZE]; -}; +} __attribute__((packed)); struct android_metadata_header { __le32 magic_number; From 1813e0fa9c1aa859c07258e2e0eff68d1caddc20 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 9 Aug 2016 12:47:37 -0700 Subject: [PATCH 0418/1276] ANDROID: dm: android-verity: adopt changes made to dm callbacks v4.4 introduced changes to the callbacks used for dm-linear and dm-verity-target targets. Move to those headers in dm-android-verity. Verified on hikey while having BOARD_USES_RECOVERY_AS_BOOT := true BOARD_BUILD_SYSTEM_ROOT_IMAGE := true BUG: 27339727 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ic64950c3b55f0a6eaa570bcedc2ace83bbf3005e --- drivers/md/dm-android-verity.c | 12 +++++------- drivers/md/dm-android-verity.h | 5 +---- drivers/md/dm-linear.c | 2 +- drivers/md/dm-verity-target.c | 2 +- drivers/md/dm-verity.h | 5 +---- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 1f4eb099209d..15ce2a81c1f4 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -59,8 +59,7 @@ static struct target_type android_verity_target = { .dtr = verity_dtr, .map = verity_map, .status = verity_status, - .ioctl = verity_ioctl, - .merge = verity_merge, + .prepare_ioctl = verity_prepare_ioctl, .iterate_devices = verity_iterate_devices, .io_hints = verity_io_hints, }; @@ -637,8 +636,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dtr = dm_linear_dtr, android_verity_target.map = dm_linear_map, android_verity_target.status = dm_linear_status, - android_verity_target.ioctl = dm_linear_ioctl, - android_verity_target.merge = dm_linear_merge, + android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl, android_verity_target.iterate_devices = dm_linear_iterate_devices, android_verity_target.io_hints = NULL; @@ -676,7 +674,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 device_size; + u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -896,7 +894,7 @@ static int __init dm_android_verity_init(void) } file = debugfs_create_bool("target_added", S_IRUGO, debug_dir, - (u32 *)&target_added); + &target_added); if (IS_ERR_OR_NULL(file)) { DMERR("Cannot create android_verity debugfs directory: %ld", @@ -906,7 +904,7 @@ static int __init dm_android_verity_init(void) } file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir, - (u32 *)&verity_enabled); + &verity_enabled); if (IS_ERR_OR_NULL(file)) { DMERR("Cannot create android_verity debugfs directory: %ld", diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index f43b02fbb475..07e22bc8955c 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -113,10 +113,7 @@ extern void dm_linear_dtr(struct dm_target *ti); extern int dm_linear_map(struct dm_target *ti, struct bio *bio); extern void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); -extern int dm_linear_ioctl(struct dm_target *ti, unsigned int cmd, - unsigned long arg); -extern int dm_linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, - struct bio_vec *biovec, int max_size); +extern int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 33ad42c56b7b..0a3f7ed82b47 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -132,7 +132,7 @@ void dm_linear_status(struct dm_target *ti, status_type_t type, } } -static int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct linear_c *lc = (struct linear_c *) ti->private; struct dm_dev *dev = lc->dev; diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index fc7895c6dd5d..58fbf52fe38e 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -739,7 +739,7 @@ void verity_status(struct dm_target *ti, status_type_t type, } } -static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct dm_verity *v = ti->private; diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index b07e2e5bd952..0f634e49f63b 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -129,10 +129,7 @@ extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, extern void verity_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); -extern int verity_ioctl(struct dm_target *ti, unsigned cmd, - unsigned long arg); -extern int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm, - struct bio_vec *biovec, int max_size); +extern int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int verity_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); From a7d4179027474fd18d9d02820b4892c5ee7b2432 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 27 Sep 2016 13:48:29 -0700 Subject: [PATCH 0419/1276] ANDROID: dm: android-verity: Remove fec_header location constraint This CL removes the mandate of the fec_header being located right after the ECC data. (Cherry-picked from https://android-review.googlesource.com/#/c/280401) Bug: 28865197 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ie04c8cf2dd755f54d02dbdc4e734a13d6f6507b5 --- drivers/md/dm-android-verity.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 15ce2a81c1f4..bb6c1285e499 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -266,10 +266,7 @@ static inline int validate_fec_header(struct fec_header *header, u64 offset) le32_to_cpu(header->version) != FEC_VERSION || le32_to_cpu(header->size) != sizeof(struct fec_header) || le32_to_cpu(header->roots) == 0 || - le32_to_cpu(header->roots) >= FEC_RSM || - offset < le32_to_cpu(header->fec_size) || - offset - le32_to_cpu(header->fec_size) != - le64_to_cpu(header->inp_size)) + le32_to_cpu(header->roots) >= FEC_RSM) return -EINVAL; return 0; From 052b681640672b2c91b13dd6807dd32391ec1dd0 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 13 Jan 2017 11:05:00 -0800 Subject: [PATCH 0420/1276] ANDROID: dm: android-verity: rebase for 4.9 Export the direct_access method of dm_linear target for dm-android-verity target. Signed-off-by: Badhri Jagan Sridharan Change-Id: I46556d882305e5194352946264cbc9c06e5038e4 [AmitP: Rebased the changes for v4.14] Signed-off-by: Amit Pundir --- drivers/md/dm-android-verity.c | 5 +++++ drivers/md/dm-android-verity.h | 13 +++++++++++++ drivers/md/dm-linear.c | 25 ++++++++++++++----------- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index bb6c1285e499..19e27e796008 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -633,8 +633,13 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dtr = dm_linear_dtr, android_verity_target.map = dm_linear_map, android_verity_target.status = dm_linear_status, +#ifdef CONFIG_BLK_DEV_ZONED + android_verity_target.end_io = dm_linear_end_io, +#endif android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl, android_verity_target.iterate_devices = dm_linear_iterate_devices, + android_verity_target.direct_access = dm_linear_dax_direct_access, + android_verity_target.dax_copy_from_iter = dm_linear_dax_copy_from_iter, android_verity_target.io_hints = NULL; err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 07e22bc8955c..ce4eb95c2f6e 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -111,10 +111,23 @@ extern struct target_type linear_target; extern void dm_linear_dtr(struct dm_target *ti); extern int dm_linear_map(struct dm_target *ti, struct bio *bio); +#ifdef CONFIG_BLK_DEV_ZONED +extern int dm_linear_end_io(struct dm_target *ti, struct bio *bio, + blk_status_t *error); +#endif extern void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); extern int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); +#if IS_ENABLED(CONFIG_DAX_DRIVER) +extern long dm_linear_dax_direct_access(struct dm_target *ti, sector_t sector, + void **kaddr, pfn_t *pfn, long size); +extern size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, + void *addr, size_t bytes, struct iov_iter *i); +#else +#define dm_linear_dax_direct_access NULL +#define dm_linear_dax_copy_from_iter NULL +#endif #endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 0a3f7ed82b47..391537b9dfd9 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -103,7 +103,7 @@ int dm_linear_map(struct dm_target *ti, struct bio *bio) } #ifdef CONFIG_BLK_DEV_ZONED -static int linear_end_io(struct dm_target *ti, struct bio *bio, +int dm_linear_end_io(struct dm_target *ti, struct bio *bio, blk_status_t *error) { struct linear_c *lc = ti->private; @@ -113,6 +113,7 @@ static int linear_end_io(struct dm_target *ti, struct bio *bio, return DM_ENDIO_DONE; } +EXPORT_SYMBOL_GPL(dm_linear_end_io); #endif void dm_linear_status(struct dm_target *ti, status_type_t type, @@ -157,7 +158,7 @@ int dm_linear_iterate_devices(struct dm_target *ti, } #if IS_ENABLED(CONFIG_DAX_DRIVER) -static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, +long dm_linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, long nr_pages, void **kaddr, pfn_t *pfn) { long ret; @@ -172,8 +173,9 @@ static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, return ret; return dax_direct_access(dax_dev, pgoff, nr_pages, kaddr, pfn); } +EXPORT_SYMBOL_GPL(dm_linear_dax_direct_access); -static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, +size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i) { struct linear_c *lc = ti->private; @@ -186,8 +188,9 @@ static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, return 0; return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i); } +EXPORT_SYMBOL_GPL(dm_linear_dax_copy_from_iter); -static size_t linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, +static size_t dm_linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i) { struct linear_c *lc = ti->private; @@ -202,16 +205,16 @@ static size_t linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, } #else -#define linear_dax_direct_access NULL -#define linear_dax_copy_from_iter NULL -#define linear_dax_copy_to_iter NULL +#define dm_linear_dax_direct_access NULL +#define dm_linear_dax_copy_from_iter NULL +#define dm_linear_dax_copy_to_iter NULL #endif static struct target_type linear_target = { .name = "linear", .version = {1, 4, 0}, #ifdef CONFIG_BLK_DEV_ZONED - .end_io = linear_end_io, + .end_io = dm_linear_end_io, .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_ZONED_HM, #else .features = DM_TARGET_PASSES_INTEGRITY, @@ -223,9 +226,9 @@ static struct target_type linear_target = { .status = dm_linear_status, .prepare_ioctl = dm_linear_prepare_ioctl, .iterate_devices = dm_linear_iterate_devices, - .direct_access = linear_dax_direct_access, - .dax_copy_from_iter = linear_dax_copy_from_iter, - .dax_copy_to_iter = linear_dax_copy_to_iter, + .direct_access = dm_linear_dax_direct_access, + .dax_copy_from_iter = dm_linear_dax_copy_from_iter, + .dax_copy_to_iter = dm_linear_dax_copy_to_iter, }; int __init dm_linear_init(void) From 9fe00b3cd7db7cd3fee9ebb4029329298c2ff7b1 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 14 Nov 2016 09:48:02 -0800 Subject: [PATCH 0421/1276] ANDROID: dm: android-verity: fix table_make_digest() error handling If table_make_digest() fails, verify_verity_signature() would try to pass the returned ERR_PTR() to kfree(). This fixes the smatch error: drivers/md/dm-android-verity.c:601 verify_verity_signature() error: 'pks' dereferencing possible ERR_PTR() Change-Id: I9b9b7764b538cb4a5f94337660e9b0f149b139be Signed-off-by: Greg Hackmann --- drivers/md/dm-android-verity.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 19e27e796008..3963e08a57c0 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -585,6 +585,8 @@ static int verify_verity_signature(char *key_id, if (IS_ERR(pks)) { DMERR("hashing failed"); + retval = PTR_ERR(pks); + pks = NULL; goto error; } From e16f7c8107871e4bb13150f0a1cb2b1e1d3c096c Mon Sep 17 00:00:00 2001 From: Bowgo Tsai Date: Thu, 2 Mar 2017 18:54:15 +0800 Subject: [PATCH 0422/1276] ANDROID: dm: android-verity: allow disable dm-verity for Treble VTS To start Treble VTS test, a single AOSP system.img will be flashed onto the device. The size of AOSP system.img might be different than the system partition size on device, making locating verity metadata fail (at the last fixed size of the partition). This change allows disabling dm-verity on system partition when the device is unlocked (orange device state) with invalid metadata. BUG: 35603549 Test: boot device with a different-sized system.img, checks verity is not enabled via: "adb shell getprop | grep partition.system.verified" Change-Id: Ide78dca4eefde4ab019e4b202d3f590dcb1bb506 Signed-off-by: Bowgo Tsai --- drivers/md/dm-android-verity.c | 53 ++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 3963e08a57c0..714e1ba73697 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -115,6 +115,12 @@ static inline bool is_userdebug(void) return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); } +static inline bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + + return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); +} static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) @@ -655,6 +661,28 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) return err; } +static int create_linear_device(struct dm_target *ti, dev_t dev, + char *target_device) +{ + u64 device_size = 0; + int err = find_size(dev, &device_size); + + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; +} + /* * Target parameters: * Key id of the public key in the system keyring. @@ -678,7 +706,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -706,23 +733,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - if (is_eng()) { - err = find_size(dev, &device_size); - if (err) { - DMERR("error finding bdev size"); - handle_error(); - return err; - } - - ti->len = device_size; - err = add_as_linear_device(ti, target_device); - if (err) { - handle_error(); - return err; - } - verity_enabled = false; - return 0; - } + if (is_eng()) + return create_linear_device(ti, dev, target_device); strreplace(key_id, '#', ' '); @@ -737,6 +749,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = extract_metadata(dev, &fec, &metadata, &verity_enabled); if (err) { + /* Allow invalid metadata when the device is unlocked */ + if (is_unlocked()) { + DMWARN("Allow invalid metadata when unlocked"); + return create_linear_device(ti, dev, target_device); + } DMERR("Error while extracting metadata"); handle_error(); goto free_metadata; From e91d392dd6a40703e26638179bfb5b9eb9e3c7f4 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 3 Jun 2016 13:16:59 -0700 Subject: [PATCH 0423/1276] ANDROID: dm: android-verity: mark dev as rw for linear target Mark as rw when adding as linear target to allow changes to the underlying filesystem through adb disable verity and adb remount. (Cherry-picked from https://partner-android-review.googlesource.com/#/c/613573/ 79a3032bb62da65a5d724eb70c8bdc662945d475) BUG: 28845874 Signed-off-by: Badhri Jagan Sridharan Change-Id: If41e9cad8e0f054f4778c09a6e2f0cb8af6fddaf --- drivers/md/dm-android-verity.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 714e1ba73697..cfb592e43e5f 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -650,6 +650,8 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dax_copy_from_iter = dm_linear_dax_copy_from_iter, android_verity_target.io_hints = NULL; + set_disk_ro(dm_disk(dm_table_get_md(ti->table)), 0); + err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); if (!err) { From a3fc693d824750f732e075e9dde6c95fc20db93a Mon Sep 17 00:00:00 2001 From: Keun-young Park Date: Mon, 14 Nov 2016 18:25:15 -0800 Subject: [PATCH 0424/1276] ANDROID: dm: verity: add minimum prefetch size - For device like eMMC, it gives better performance to read more hash blocks at a time. - For android, set it to default 128. For other devices, set it to 1 which is the same as now. - saved boot-up time by 300ms in tested device bug: 32246564 Change-Id: Ibc0401a0cddba64b862a80445844b4e595213621 Cc: Sami Tolvanen Signed-off-by: Keun-young Park --- drivers/md/Kconfig | 16 ++++++++++++++++ drivers/md/dm-verity-target.c | 9 ++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 7601e36a9ee0..44d56f41660f 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -479,6 +479,21 @@ config DM_VERITY If unsure, say N. +config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + bool "Prefetch size 128" + +config DM_VERITY_HASH_PREFETCH_MIN_SIZE + int "Verity hash prefetch minimum size" + depends on DM_VERITY + range 1 4096 + default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + default 1 + ---help--- + This sets minimum number of hash blocks to prefetch for dm-verity. + For devices like eMMC, having larger prefetch size like 128 can improve + performance with increased memory consumption for keeping more hashes + in RAM. + config DM_VERITY_FEC bool "Verity forward error correction support" depends on DM_VERITY @@ -568,6 +583,7 @@ config DM_ANDROID_VERITY depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 58fbf52fe38e..427cabe675a8 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -580,6 +580,7 @@ static void verity_prefetch_io(struct work_struct *work) container_of(work, struct dm_verity_prefetch_work, work); struct dm_verity *v = pw->v; int i; + sector_t prefetch_size; for (i = v->levels - 2; i >= 0; i--) { sector_t hash_block_start; @@ -602,8 +603,14 @@ static void verity_prefetch_io(struct work_struct *work) hash_block_end = v->hash_blocks - 1; } no_prefetch_cluster: + // for emmc, it is more efficient to send bigger read + prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE, + hash_block_end - hash_block_start + 1); + if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) { + prefetch_size = hash_block_end - hash_block_start + 1; + } dm_bufio_prefetch(v->bufio, hash_block_start, - hash_block_end - hash_block_start + 1); + prefetch_size); } kfree(pw); From d29f148afa8c5f8ce5652136367c95b39ecf4760 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:16 +0000 Subject: [PATCH 0425/1276] ANDROID: fs: epoll: use freezable blocking call Avoid waking up every thread sleeping in an epoll_wait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Change-Id: I848d08d28c89302fd42bbbdfa76489a474ab27bf Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/eventpoll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 42bbe6824b4b..779b74160280 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1816,7 +1817,8 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, } spin_unlock_irq(&ep->wq.lock); - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!freezable_schedule_hrtimeout_range(to, slack, + HRTIMER_MODE_ABS)) timed_out = 1; spin_lock_irq(&ep->wq.lock); From 871cfeca1eb9d0d3c1481625604053d0b73cef61 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:32:59 -0800 Subject: [PATCH 0426/1276] ANDROID: fs: sched: add a counter to track fsync Change-Id: I6c138de5b2332eea70f57e098134d1d141247b3f Signed-off-by: Jin Qian [AmitP: Refactored changes to align with changes from upstream commit 9a07000400c8 ("sched/headers: Move CONFIG_TASK_XACCT bits from to ")] Signed-off-by: Amit Pundir --- fs/sync.c | 3 ++- include/linux/sched/xacct.h | 9 +++++++++ include/linux/task_io_accounting.h | 2 ++ include/linux/task_io_accounting_ops.h | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/sync.c b/fs/sync.c index b54e0541ad89..055daab8652a 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -220,6 +220,7 @@ static int do_fsync(unsigned int fd, int datasync) if (f.file) { ret = vfs_fsync(f.file, datasync); fdput(f); + inc_syscfs(current); } return ret; } diff --git a/include/linux/sched/xacct.h b/include/linux/sched/xacct.h index c078f0a94cec..9544c9d9d534 100644 --- a/include/linux/sched/xacct.h +++ b/include/linux/sched/xacct.h @@ -28,6 +28,11 @@ static inline void inc_syscw(struct task_struct *tsk) { tsk->ioac.syscw++; } + +static inline void inc_syscfs(struct task_struct *tsk) +{ + tsk->ioac.syscfs++; +} #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { @@ -44,6 +49,10 @@ static inline void inc_syscr(struct task_struct *tsk) static inline void inc_syscw(struct task_struct *tsk) { } + +static inline void inc_syscfs(struct task_struct *tsk) +{ +} #endif #endif /* _LINUX_SCHED_XACCT_H */ diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 6f6acce064de..bb26108ca23c 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -19,6 +19,8 @@ struct task_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; + /* # of fsync syscalls */ + u64 syscfs; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index bb5498bcdd96..733ab62ae141 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -97,6 +97,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, dst->wchar += src->wchar; dst->syscr += src->syscr; dst->syscw += src->syscw; + dst->syscfs += src->syscfs; } #else static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, From a01366e388022e4341aecc770b69072dfbc9299e Mon Sep 17 00:00:00 2001 From: Mohan Srinivasan Date: Wed, 14 Dec 2016 16:39:51 -0800 Subject: [PATCH 0427/1276] ANDROID: fs: FS tracepoints to track IO. Adds tracepoints in ext4/f2fs/mpage to track readpages/buffered write()s. This allows us to track files that are being read/written to PIDs. (Merged from android4.4-common). Signed-off-by: Mohan Srinivasan --- fs/ext4/inline.c | 6 ++ fs/ext4/inode.c | 28 ++++++++ fs/ext4/readpage.c | 41 +++++++++-- fs/f2fs/data.c | 21 ++++++ fs/f2fs/inline.c | 11 +++ fs/mpage.c | 30 ++++++++ include/trace/events/android_fs.h | 31 +++++++++ include/trace/events/android_fs_template.h | 79 ++++++++++++++++++++++ 8 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 include/trace/events/android_fs.h create mode 100644 include/trace/events/android_fs_template.h diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 7b4736022761..09c8c3fd8491 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -12,6 +12,7 @@ #include "ext4.h" #include "xattr.h" #include "truncate.h" +#include #define EXT4_XATTR_SYSTEM_DATA "data" #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) @@ -505,6 +506,9 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) return -EAGAIN; } + trace_android_fs_dataread_start(inode, page_offset(page), PAGE_SIZE, + current->pid, current->comm); + /* * Current inline data can only exist in the 1st page, * So for all the other pages, just set them uptodate. @@ -516,6 +520,8 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) SetPageUptodate(page); } + trace_android_fs_dataread_end(inode, page_offset(page), PAGE_SIZE); + up_read(&EXT4_I(inode)->xattr_sem); unlock_page(page); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d767e993591d..2f98c77e6d2a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -47,6 +47,7 @@ #include "truncate.h" #include +#include #define MPAGE_DA_EXTENT_TAIL 0x01 @@ -1253,6 +1254,8 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_ext4_write_begin(inode, pos, len, flags); /* * Reserve one block more for addition to orphan list in case @@ -1391,6 +1394,7 @@ static int ext4_write_end(struct file *file, int i_size_changed = 0; int inline_data = ext4_has_inline_data(inode); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_write_end(inode, pos, len, copied); if (inline_data) { ret = ext4_write_inline_data_end(inode, pos, len, @@ -1496,6 +1500,7 @@ static int ext4_journalled_write_end(struct file *file, int size_changed = 0; int inline_data = ext4_has_inline_data(inode); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_journalled_write_end(inode, pos, len, copied); from = pos & (PAGE_SIZE - 1); to = from + len; @@ -3024,6 +3029,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, len, flags, pagep, fsdata); } *fsdata = (void *)0; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_ext4_da_write_begin(inode, pos, len, flags); if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { @@ -3142,6 +3149,7 @@ static int ext4_da_write_end(struct file *file, return ext4_write_end(file, mapping, pos, len, copied, page, fsdata); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_da_write_end(inode, pos, len, copied); start = pos & (PAGE_SIZE - 1); end = start + copied - 1; @@ -3847,6 +3855,7 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) size_t count = iov_iter_count(iter); loff_t offset = iocb->ki_pos; ssize_t ret; + int rw = iov_iter_rw(iter); #ifdef CONFIG_EXT4_FS_ENCRYPTION if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) @@ -3863,12 +3872,31 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) if (ext4_has_inline_data(inode)) return 0; + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_start(inode, offset, count, + current->pid, + current->comm); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, + current->comm); + trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); if (iov_iter_rw(iter) == READ) ret = ext4_direct_IO_read(iocb, iter); else ret = ext4_direct_IO_write(iocb, iter); trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret); + + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + return ret; } diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index f461d75ac049..44538c6c1131 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -46,6 +46,7 @@ #include #include "ext4.h" +#include static inline bool ext4_bio_encrypted(struct bio *bio) { @@ -56,6 +57,17 @@ static inline bool ext4_bio_encrypted(struct bio *bio) #endif } +static void +ext4_trace_read_completion(struct bio *bio) +{ + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); +} + /* * I/O completion handler for multipage BIOs. * @@ -73,6 +85,9 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_start_enabled()) + ext4_trace_read_completion(bio); + if (ext4_bio_encrypted(bio)) { if (bio->bi_status) { fscrypt_release_ctx(bio->bi_private); @@ -96,6 +111,24 @@ static void mpage_end_io(struct bio *bio) bio_put(bio); } +static void +ext4_submit_bio_read(struct bio *bio) +{ + if (trace_android_fs_dataread_start_enabled()) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + current->comm); + } + } + submit_bio(bio); +} + int ext4_mpage_readpages(struct address_space *mapping, struct list_head *pages, struct page *page, unsigned nr_pages, bool is_readahead) @@ -236,7 +269,7 @@ int ext4_mpage_readpages(struct address_space *mapping, */ if (bio && (last_block_in_bio != blocks[0] - 1)) { submit_and_realloc: - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } if (bio == NULL) { @@ -270,14 +303,14 @@ int ext4_mpage_readpages(struct address_space *mapping, if (((map.m_flags & EXT4_MAP_BOUNDARY) && (relative_block == map.m_len)) || (first_hole != blocks_per_page)) { - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } else last_block_in_bio = blocks[blocks_per_page - 1]; goto next_page; confused: if (bio) { - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } if (!PageUptodate(page)) @@ -290,6 +323,6 @@ int ext4_mpage_readpages(struct address_space *mapping, } BUG_ON(pages && !list_empty(pages)); if (bio) - submit_bio(bio); + ext4_submit_bio_read(bio); return 0; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 382c1ef9a9e4..1bd0f006288d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -27,6 +27,7 @@ #include "segment.h" #include "trace.h" #include +#include #define NUM_PREALLOC_POST_READ_CTXS 128 @@ -2330,6 +2331,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, block_t blkaddr = NULL_ADDR; int err = 0; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_f2fs_write_begin(inode, pos, len, flags); if ((f2fs_is_atomic_file(inode) && @@ -2429,6 +2432,7 @@ static int f2fs_write_end(struct file *file, { struct inode *inode = page->mapping->host; + trace_android_fs_datawrite_end(inode, pos, len); trace_f2fs_write_end(inode, pos, len, copied); /* @@ -2496,6 +2500,16 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) trace_f2fs_direct_IO_enter(inode, offset, count, rw); + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_start(inode, offset, + count, current->pid, + current->comm); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, current->comm); + if (rw == WRITE && whint_mode == WHINT_MODE_OFF) iocb->ki_hint = WRITE_LIFE_NOT_SET; @@ -2524,6 +2538,13 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) } out: + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); return err; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 115dc219344b..fede695f722e 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -13,6 +13,7 @@ #include "f2fs.h" #include "node.h" +#include bool f2fs_may_inline_data(struct inode *inode) { @@ -86,14 +87,22 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) { struct page *ipage; + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + current->comm); + ipage = f2fs_get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return PTR_ERR(ipage); } if (!f2fs_has_inline_data(inode)) { f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); return -EAGAIN; } @@ -105,6 +114,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) if (!PageUptodate(page)) SetPageUptodate(page); f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return 0; } diff --git a/fs/mpage.c b/fs/mpage.c index c820dc9bebab..57468ecab5a6 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -32,6 +32,14 @@ #include #include "internal.h" +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_end); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_end); + /* * I/O completion handler for multipage BIOs. * @@ -49,6 +57,16 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_end_enabled() && + (bio_data_dir(bio) == READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); + } + bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; page_endio(page, bio_op(bio), @@ -60,6 +78,18 @@ static void mpage_end_io(struct bio *bio) static struct bio *mpage_bio_submit(int op, int op_flags, struct bio *bio) { + if (trace_android_fs_dataread_start_enabled() && (op == REQ_OP_READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + current->comm); + } + } bio->bi_end_io = mpage_end_io; bio_set_op_attrs(bio, op, op_flags); guard_bio_eod(op, bio); diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h new file mode 100644 index 000000000000..531da433a7bc --- /dev/null +++ b/include/trace/events/android_fs.h @@ -0,0 +1,31 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM android_fs + +#if !defined(_TRACE_ANDROID_FS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_H + +#include +#include + +DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +#endif /* _TRACE_ANDROID_FS_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h new file mode 100644 index 000000000000..618988b047c1 --- /dev/null +++ b/include/trace/events/android_fs_template.h @@ -0,0 +1,79 @@ +#if !defined(_TRACE_ANDROID_FS_TEMPLATE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_TEMPLATE_H + +#include + +DECLARE_EVENT_CLASS(android_fs_data_start_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command), + TP_STRUCT__entry( + __array(char, path, MAX_FILTER_STR_VAL); + __field(char *, pathname); + __field(loff_t, offset); + __field(int, bytes); + __field(loff_t, i_size); + __string(cmdline, command); + __field(pid_t, pid); + __field(ino_t, ino); + ), + TP_fast_assign( + { + struct dentry *d; + + /* + * Grab a reference to the inode here because + * d_obtain_alias() will either drop the inode + * reference if it locates an existing dentry + * or transfer the reference to the new dentry + * created. In our case, the file is still open, + * so the dentry is guaranteed to exist (connected), + * so d_obtain_alias() drops the reference we + * grabbed here. + */ + ihold(inode); + d = d_obtain_alias(inode); + if (!IS_ERR(d)) { + __entry->pathname = dentry_path(d, + __entry->path, + MAX_FILTER_STR_VAL); + dput(d); + } else + __entry->pathname = ERR_PTR(-EINVAL); + __entry->offset = offset; + __entry->bytes = bytes; + __entry->i_size = i_size_read(inode); + __assign_str(cmdline, command); + __entry->pid = pid; + __entry->ino = inode->i_ino; + } + ), + TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s," + " pid %d, i_size %llu, ino %lu", + (IS_ERR(__entry->pathname) ? "ERROR" : __entry->pathname), + __entry->offset, __entry->bytes, __get_str(cmdline), + __entry->pid, __entry->i_size, + (unsigned long) __entry->ino) +); + +DECLARE_EVENT_CLASS(android_fs_data_end_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes), + TP_STRUCT__entry( + __field(ino_t, ino); + __field(loff_t, offset); + __field(int, bytes); + ), + TP_fast_assign( + { + __entry->ino = inode->i_ino; + __entry->offset = offset; + __entry->bytes = bytes; + } + ), + TP_printk("ino %lu, offset %llu, bytes %d", + (unsigned long) __entry->ino, + __entry->offset, __entry->bytes) +); + +#endif /* _TRACE_ANDROID_FS_TEMPLATE_H */ From 63066f4acf92f994a55cb2bd1e0b63b11c877412 Mon Sep 17 00:00:00 2001 From: Mohan Srinivasan Date: Fri, 10 Feb 2017 14:26:23 -0800 Subject: [PATCH 0428/1276] ANDROID: fs: Refactor FS readpage/write tracepoints. Refactor the fs readpage/write tracepoints to move the inode->path lookup outside the tracepoint code, and pass a pointer to the path into the tracepoint code instead. This is necessary because the tracepoint code runs non-preemptible. Thanks to Trilok Soni for catching this in 4.4. Signed-off-by: Mohan Srinivasan [AmitP: Folded following android-4.9 commit changes into this patch a5c4dbb05ab7 ("ANDROID: Replace spaces by '_' for some android filesystem tracepoints.")] Signed-off-by: Amit Pundir --- fs/ext4/inline.c | 12 +++++- fs/ext4/inode.c | 45 +++++++++++++++++----- fs/ext4/readpage.c | 6 +++ fs/f2fs/data.c | 33 +++++++++++++--- fs/f2fs/inline.c | 13 +++++-- fs/mpage.c | 6 +++ include/trace/events/android_fs.h | 44 ++++++++++++++++++--- include/trace/events/android_fs_template.h | 37 ++++++------------ 8 files changed, 145 insertions(+), 51 deletions(-) diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 09c8c3fd8491..b75a7cf79784 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -506,8 +506,16 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) return -EAGAIN; } - trace_android_fs_dataread_start(inode, page_offset(page), PAGE_SIZE, - current->pid, current->comm); + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } /* * Current inline data can only exist in the 1st page, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2f98c77e6d2a..aaf56a88e173 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1254,8 +1254,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_ext4_write_begin(inode, pos, len, flags); /* * Reserve one block more for addition to orphan list in case @@ -3029,8 +3037,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, len, flags, pagep, fsdata); } *fsdata = (void *)0; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, + path, current->comm); + } trace_ext4_da_write_begin(inode, pos, len, flags); if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { @@ -3873,16 +3889,27 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) return 0; if (trace_android_fs_dataread_start_enabled() && - (rw == READ)) + (rw == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_dataread_start(inode, offset, count, - current->pid, + current->pid, path, current->comm); + } if (trace_android_fs_datawrite_start_enabled() && - (rw == WRITE)) + (rw == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_datawrite_start(inode, offset, count, - current->pid, + current->pid, path, current->comm); - + } trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); if (iov_iter_rw(iter) == READ) ret = ext4_direct_IO_read(iocb, iter); diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 44538c6c1131..aa1b9e181f31 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -118,11 +118,17 @@ ext4_submit_bio_read(struct bio *bio) struct page *first_page = bio->bi_io_vec[0].bv_page; if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); trace_android_fs_dataread_start( first_page->mapping->host, page_offset(first_page), bio->bi_iter.bi_size, current->pid, + path, current->comm); } } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1bd0f006288d..dde566164ef0 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2331,8 +2331,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, block_t blkaddr = NULL_ADDR; int err = 0; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_f2fs_write_begin(inode, pos, len, flags); if ((f2fs_is_atomic_file(inode) && @@ -2501,14 +2509,27 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) trace_f2fs_direct_IO_enter(inode, offset, count, rw); if (trace_android_fs_dataread_start_enabled() && - (rw == READ)) + (rw == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_dataread_start(inode, offset, - count, current->pid, + count, current->pid, path, current->comm); + } if (trace_android_fs_datawrite_start_enabled() && - (rw == WRITE)) + (rw == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_datawrite_start(inode, offset, count, - current->pid, current->comm); + current->pid, path, + current->comm); + } if (rw == WRITE && whint_mode == WHINT_MODE_OFF) iocb->ki_hint = WRITE_LIFE_NOT_SET; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index fede695f722e..df71d26192b8 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -87,9 +87,16 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) { struct page *ipage; - trace_android_fs_dataread_start(inode, page_offset(page), - PAGE_SIZE, current->pid, - current->comm); + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } ipage = f2fs_get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { diff --git a/fs/mpage.c b/fs/mpage.c index 57468ecab5a6..a5265828a2f2 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -82,11 +82,17 @@ static struct bio *mpage_bio_submit(int op, int op_flags, struct bio *bio) struct page *first_page = bio->bi_io_vec[0].bv_page; if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); trace_android_fs_dataread_start( first_page->mapping->host, page_offset(first_page), bio->bi_iter.bi_size, current->pid, + path, current->comm); } } diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h index 531da433a7bc..49509533d3fa 100644 --- a/include/trace/events/android_fs.h +++ b/include/trace/events/android_fs.h @@ -9,8 +9,8 @@ DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command)); + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, TP_PROTO(struct inode *inode, loff_t offset, int bytes), @@ -18,14 +18,48 @@ DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command)); + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end, TP_PROTO(struct inode *inode, loff_t offset, int bytes), - TP_ARGS(inode, offset, bytes)); + TP_ARGS(inode, offset, bytes)); #endif /* _TRACE_ANDROID_FS_H */ /* This part must be outside protection */ #include + +#ifndef ANDROID_FSTRACE_GET_PATHNAME +#define ANDROID_FSTRACE_GET_PATHNAME + +/* Sizes an on-stack array, so careful if sizing this up ! */ +#define MAX_TRACE_PATHBUF_LEN 256 + +static inline char * +android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode) +{ + char *path; + struct dentry *d; + + /* + * d_obtain_alias() will either iput() if it locates an existing + * dentry or transfer the reference to the new dentry created. + * So get an extra reference here. + */ + ihold(inode); + d = d_obtain_alias(inode); + if (likely(!IS_ERR(d))) { + path = dentry_path_raw(d, buf, buflen); + if (unlikely(IS_ERR(path))) { + strcpy(buf, "ERROR"); + path = buf; + } + dput(d); + } else { + strcpy(buf, "ERROR"); + path = buf; + } + return path; +} +#endif diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h index 618988b047c1..b23d17b56c63 100644 --- a/include/trace/events/android_fs_template.h +++ b/include/trace/events/android_fs_template.h @@ -5,11 +5,10 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command), + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command), TP_STRUCT__entry( - __array(char, path, MAX_FILTER_STR_VAL); - __field(char *, pathname); + __string(pathbuf, pathname); __field(loff_t, offset); __field(int, bytes); __field(loff_t, i_size); @@ -19,40 +18,26 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template, ), TP_fast_assign( { - struct dentry *d; - /* - * Grab a reference to the inode here because - * d_obtain_alias() will either drop the inode - * reference if it locates an existing dentry - * or transfer the reference to the new dentry - * created. In our case, the file is still open, - * so the dentry is guaranteed to exist (connected), - * so d_obtain_alias() drops the reference we - * grabbed here. + * Replace the spaces in filenames and cmdlines + * because this screws up the tooling that parses + * the traces. */ - ihold(inode); - d = d_obtain_alias(inode); - if (!IS_ERR(d)) { - __entry->pathname = dentry_path(d, - __entry->path, - MAX_FILTER_STR_VAL); - dput(d); - } else - __entry->pathname = ERR_PTR(-EINVAL); + __assign_str(pathbuf, pathname); + (void)strreplace(__get_str(pathbuf), ' ', '_'); __entry->offset = offset; __entry->bytes = bytes; __entry->i_size = i_size_read(inode); __assign_str(cmdline, command); + (void)strreplace(__get_str(cmdline), ' ', '_'); __entry->pid = pid; __entry->ino = inode->i_ino; } ), TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s," " pid %d, i_size %llu, ino %lu", - (IS_ERR(__entry->pathname) ? "ERROR" : __entry->pathname), - __entry->offset, __entry->bytes, __get_str(cmdline), - __entry->pid, __entry->i_size, + __get_str(pathbuf), __entry->offset, __entry->bytes, + __get_str(cmdline), __entry->pid, __entry->i_size, (unsigned long) __entry->ino) ); From 6188fa01742cc41381f04b22b6a82d84a14a9d4c Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 7 Oct 2016 16:20:47 -0700 Subject: [PATCH 0429/1276] ANDROID: goldfish: add ranchu defconfigs Change-Id: I73ef1b132b6203ae921a1e1d4850eaadf58f8926 [AmitP: Folded following android-4.9 commit changes into this patch b821439faf75 ("ANDROID: arm64: rename ranchu defconfig to ranchu64") 1a03fc05a569 ("ANDROID: goldfish: enable CONFIG_INET_DIAG_DESTROY") 2bed6160b367 ("ANDROID: goldfish: disable GOLDFISH_SYNC") ec6a764367a4 ("ANDROID: goldfish_sync: update defconfig for 4.9-compatible version")] Signed-off-by: Amit Pundir --- arch/arm/configs/ranchu_defconfig | 314 +++++++++++++++++ arch/arm64/configs/ranchu64_defconfig | 310 +++++++++++++++++ arch/x86/configs/i386_ranchu_defconfig | 422 +++++++++++++++++++++++ arch/x86/configs/x86_64_ranchu_defconfig | 417 ++++++++++++++++++++++ 4 files changed, 1463 insertions(+) create mode 100644 arch/arm/configs/ranchu_defconfig create mode 100644 arch/arm64/configs/ranchu64_defconfig create mode 100644 arch/x86/configs/i386_ranchu_defconfig create mode 100644 arch/x86/configs/x86_64_ranchu_defconfig diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig new file mode 100644 index 000000000000..461a85a02764 --- /dev/null +++ b/arch/arm/configs/ranchu_defconfig @@ -0,0 +1,314 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_ARCH_MMAP_RND_BITS=16 +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_VIRT=y +CONFIG_ARM_KERNMEM_PERMS=y +CONFIG_SMP=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_KSM=y +CONFIG_SECCOMP=y +CONFIG_CMDLINE="console=ttyAMA0" +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_INET_ESP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_FB=y +CONFIG_FB_GOLDFISH=y +CONFIG_FB_SIMPLE=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PL031=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_VIRTUALIZATION=y diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig new file mode 100644 index 000000000000..51c3bfc8658c --- /dev/null +++ b/arch/arm64/configs/ranchu64_defconfig @@ -0,0 +1,310 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_MMAP_RND_BITS=24 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_VEXPRESS=y +CONFIG_NR_CPUS=4 +CONFIG_PREEMPT=y +CONFIG_KSM=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_CMDLINE="console=ttyAMA0" +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_INET_ESP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_TTL=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_SMC91X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TABLET=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +# CONFIG_HW_RANDOM is not set +CONFIG_BATTERY_GOLDFISH=y +# CONFIG_HWMON is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_FB=y +CONFIG_FB_GOLDFISH=y +CONFIG_FB_SIMPLE=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +# CONFIG_USB_SUPPORT is not set +CONFIG_RTC_CLASS=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_FTRACE is not set +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_DEBUG_RODATA=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig new file mode 100644 index 000000000000..18d3675d28f0 --- /dev/null +++ b/arch/x86/configs/i386_ranchu_defconfig @@ -0,0 +1,422 @@ +# CONFIG_64BIT is not set +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_ARCH_MMAP_RND_BITS=16 +CONFIG_PARTITION_ADVANCED=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_SGI_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_SMP=y +CONFIG_X86_BIGSMP=y +CONFIG_MCORE2=y +CONFIG_X86_GENERIC=y +CONFIG_HPET_TIMER=y +CONFIG_NR_CPUS=512 +CONFIG_PREEMPT=y +# CONFIG_X86_MCE is not set +CONFIG_X86_REBOOTFIXUPS=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_CMA=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_HZ_100=y +CONFIG_PHYSICAL_START=0x100000 +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIEASPM is not set +CONFIG_PCCARD=y +CONFIG_YENTA=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_ISCSI_ATTRS=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_ATA_PIIX=y +CONFIG_PATA_AMD=y +CONFIG_PATA_OLDPIIX=y +CONFIG_PATA_SCH=y +CONFIG_PATA_MPIIX=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_BNX2=y +CONFIG_TIGON3=y +CONFIG_NET_TULIP=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_SKY2=y +CONFIG_NE2K_PCI=y +CONFIG_FORCEDETH=y +CONFIG_8139TOO=y +# CONFIG_8139TOO_PIO is not set +CONFIG_R8169=y +CONFIG_FDDI=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_NVRAM=y +CONFIG_I2C_I801=y +CONFIG_BATTERY_GOLDFISH=y +CONFIG_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_AGP=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_DRM=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_EFI=y +CONFIG_FB_GOLDFISH=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_DMADEVICES=y +CONFIG_VIRTIO_PCI=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SYNC_FILE=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_SND_HDA_INTEL=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_GOLDFISH_SYNC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ISCSI_IBFT_FIND=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=2048 +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SCHED_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_PKCS7_TEST_KEY=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_CRC_T10DIF=y diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig new file mode 100644 index 000000000000..7eff3002db18 --- /dev/null +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -0,0 +1,417 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_ARCH_MMAP_RND_BITS=32 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_PARTITION_ADVANCED=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_SGI_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_SMP=y +CONFIG_MCORE2=y +CONFIG_MAXSMP=y +CONFIG_PREEMPT=y +# CONFIG_X86_MCE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_CMA=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_HZ_100=y +CONFIG_PHYSICAL_START=0x100000 +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIEASPM is not set +CONFIG_PCCARD=y +CONFIG_YENTA=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_IA32_EMULATION=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DMA_CMA=y +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_ISCSI_ATTRS=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_ATA_PIIX=y +CONFIG_PATA_AMD=y +CONFIG_PATA_OLDPIIX=y +CONFIG_PATA_SCH=y +CONFIG_PATA_MPIIX=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_BNX2=y +CONFIG_TIGON3=y +CONFIG_NET_TULIP=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_SKY2=y +CONFIG_NE2K_PCI=y +CONFIG_FORCEDETH=y +CONFIG_8139TOO=y +# CONFIG_8139TOO_PIO is not set +CONFIG_R8169=y +CONFIG_FDDI=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_NVRAM=y +CONFIG_I2C_I801=y +CONFIG_BATTERY_GOLDFISH=y +CONFIG_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_AGP=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_DRM=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_EFI=y +CONFIG_FB_GOLDFISH=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_DMADEVICES=y +CONFIG_VIRTIO_PCI=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SYNC_FILE=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_SND_HDA_INTEL=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_GOLDFISH_SYNC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ISCSI_IBFT_FIND=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SCHED_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_PKCS7_TEST_KEY=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_CRC_T10DIF=y From 5c4530e05bbe135a646ef38938416695d6d7508a Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 12 Sep 2016 15:51:35 -0700 Subject: [PATCH 0430/1276] ANDROID: build: add build server configs for goldfish Change-Id: Icd7a8d44df2b09394be5c6230c64ecb374cae236 [AmitP: Folded following android-4.9 commit changes into this patch d7d2efab84d5 ("ANDROID: build: fix build config kernel_dir")] Signed-off-by: Amit Pundir --- build.config.goldfish.arm | 12 ++++++++++++ build.config.goldfish.arm64 | 12 ++++++++++++ build.config.goldfish.mips | 11 +++++++++++ build.config.goldfish.mips64 | 11 +++++++++++ build.config.goldfish.x86 | 12 ++++++++++++ build.config.goldfish.x86_64 | 12 ++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 build.config.goldfish.arm create mode 100644 build.config.goldfish.arm64 create mode 100644 build.config.goldfish.mips create mode 100644 build.config.goldfish.mips64 create mode 100644 build.config.goldfish.x86 create mode 100644 build.config.goldfish.x86_64 diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm new file mode 100644 index 000000000000..866da9361b71 --- /dev/null +++ b/build.config.goldfish.arm @@ -0,0 +1,12 @@ +ARCH=arm +BRANCH=android-4.4 +CROSS_COMPILE=arm-linux-androidkernel- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin +FILES=" +arch/arm/boot/zImage +vmlinux +System.map +" diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 new file mode 100644 index 000000000000..9c963cf4a3d8 --- /dev/null +++ b/build.config.goldfish.arm64 @@ -0,0 +1,12 @@ +ARCH=arm64 +BRANCH=android-4.4 +CROSS_COMPILE=aarch64-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin +FILES=" +arch/arm64/boot/Image +vmlinux +System.map +" diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips new file mode 100644 index 000000000000..8af53d2c2940 --- /dev/null +++ b/build.config.goldfish.mips @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 new file mode 100644 index 000000000000..2a33d36dc4c8 --- /dev/null +++ b/build.config.goldfish.mips64 @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 new file mode 100644 index 000000000000..f86253f58d4d --- /dev/null +++ b/build.config.goldfish.x86 @@ -0,0 +1,12 @@ +ARCH=x86 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=i386_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 new file mode 100644 index 000000000000..e1738861ec5c --- /dev/null +++ b/build.config.goldfish.x86_64 @@ -0,0 +1,12 @@ +ARCH=x86_64 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=x86_64_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" From 9d3e5312821d3d7d8300a04b265399bd3dc907ea Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Mon, 6 Jul 2015 16:50:33 -0700 Subject: [PATCH 0431/1276] ANDROID: initramfs: Add skip_initramfs command line option Add a skip_initramfs option to allow choosing whether to boot using the initramfs or not at runtime. Change-Id: If30428fa748c1d4d3d7b9d97c1f781de5e4558c3 Signed-off-by: Rom Lemarchand --- include/linux/initramfs.h | 32 ++++++++++++++++++++++++++++++++ init/Makefile | 3 --- init/initramfs.c | 19 ++++++++++++++++++- init/noinitramfs.c | 9 ++++++++- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 include/linux/initramfs.h diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h new file mode 100644 index 000000000000..fc7da63b125b --- /dev/null +++ b/include/linux/initramfs.h @@ -0,0 +1,32 @@ +/* + * include/linux/initramfs.h + * + * Copyright (C) 2015, Google + * Rom Lemarchand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_INITRAMFS_H +#define _LINUX_INITRAMFS_H + +#include + +#if IS_BUILTIN(CONFIG_BLK_DEV_INITRD) + +int __init default_rootfs(void); + +#endif + +#endif /* _LINUX_INITRAMFS_H */ diff --git a/init/Makefile b/init/Makefile index f814f0ff5974..1bac4381de9a 100644 --- a/init/Makefile +++ b/init/Makefile @@ -6,11 +6,8 @@ ccflags-y := -fno-function-sections -fno-data-sections obj-y := main.o version.o mounts.o -ifneq ($(CONFIG_BLK_DEV_INITRD),y) obj-y += noinitramfs.o -else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o -endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o obj-y += init_task.o diff --git a/init/initramfs.c b/init/initramfs.c index 640557788026..cc21c6cfd20f 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -10,6 +10,7 @@ #include #include #include +#include static ssize_t __init xwrite(int fd, const char *p, size_t count) { @@ -597,10 +598,26 @@ static void __init clean_rootfs(void) } #endif +static int __initdata do_skip_initramfs; + +static int __init skip_initramfs_param(char *str) +{ + if (*str) + return 0; + do_skip_initramfs = 1; + return 1; +} +__setup("skip_initramfs", skip_initramfs_param); + static int __init populate_rootfs(void) { + char *err; + + if (do_skip_initramfs) + return default_rootfs(); + /* Load the built in initramfs */ - char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); + err = unpack_to_rootfs(__initramfs_start, __initramfs_size); if (err) panic("%s", err); /* Failed to decompress INTERNAL initramfs */ /* If available load the bootloader supplied initrd */ diff --git a/init/noinitramfs.c b/init/noinitramfs.c index f4bad8436c93..39e0b4f7b48b 100644 --- a/init/noinitramfs.c +++ b/init/noinitramfs.c @@ -21,11 +21,16 @@ #include #include #include +#include +#include /* * Create a simple rootfs that is similar to the default initramfs */ -static int __init default_rootfs(void) +#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) +static +#endif +int __init default_rootfs(void) { int err; @@ -49,4 +54,6 @@ static int __init default_rootfs(void) printk(KERN_WARNING "Failed to create a rootfs\n"); return err; } +#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) rootfs_initcall(default_rootfs); +#endif From 9e1cea01bafaf4dbf915bfa218bdcd56153bf95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 15 Oct 2008 18:23:47 -0700 Subject: [PATCH 0432/1276] ANDROID: input: Generic GPIO Input devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports keyboard matrixces, direct inputs, direct outputs and axes connected to gpios. Change-Id: I5e921e6e3a1cc169316ee3b665f4cc21b5735114 Signed-off-by: Arve HjønnevÃ¥g Signed-off-by: Nick Pelly [AmitP: Use ktime directly to align with upstream commit 2456e8553544 ("ktime: Get rid of the union")] Signed-off-by: Amit Pundir --- drivers/input/misc/Kconfig | 5 + drivers/input/misc/Makefile | 1 + drivers/input/misc/gpio_axis.c | 192 ++++++++++++++ drivers/input/misc/gpio_event.c | 249 +++++++++++++++++ drivers/input/misc/gpio_input.c | 376 ++++++++++++++++++++++++++ drivers/input/misc/gpio_matrix.c | 441 +++++++++++++++++++++++++++++++ drivers/input/misc/gpio_output.c | 97 +++++++ include/linux/gpio_event.h | 170 ++++++++++++ 8 files changed, 1531 insertions(+) create mode 100644 drivers/input/misc/gpio_axis.c create mode 100644 drivers/input/misc/gpio_event.c create mode 100644 drivers/input/misc/gpio_input.c create mode 100644 drivers/input/misc/gpio_matrix.c create mode 100644 drivers/input/misc/gpio_output.c create mode 100644 include/linux/gpio_event.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2be9bc5..9e0232c517d0 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -521,6 +521,11 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1ff68f..02e9edcde799 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c new file mode 100644 index 000000000000..0acf4a576f53 --- /dev/null +++ b/drivers/input/misc/gpio_axis.c @@ -0,0 +1,192 @@ +/* drivers/input/misc/gpio_axis.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +struct gpio_axis_state { + struct gpio_event_input_devs *input_devs; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_devs->dev[ai->dev], + ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_devs->dev[ai->dev], + ai->type, ai->code, pos); + } + input_sync(as->input_devs->dev[ai->dev]); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_devs = input_devs; + as->info = ai; + if (ai->dev >= input_devs->count) { + pr_err("gpio_event_axis: bad device index %d >= %d " + "for %d:%d\n", ai->dev, input_devs->count, + ai->type, ai->code); + ret = -EINVAL; + goto err_bad_device_index; + } + + input_set_capability(input_devs->dev[ai->dev], + ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_devs->dev[ai->dev], ai->code, + 0, ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } +err_bad_device_index: + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c new file mode 100644 index 000000000000..d4e5b4dfe19f --- /dev/null +++ b/drivers/input/misc/gpio_event.c @@ -0,0 +1,249 @@ +/* drivers/input/misc/gpio_event.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_event { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_platform_data *info; + struct early_suspend early_suspend; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int devnr; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (devnr = 0; devnr < ip->input_devs->count; devnr++) + if (ip->input_devs->dev[devnr] == dev) + break; + if (devnr == ip->input_devs->count) { + pr_err("gpio_input_event: unknown device %p\n", dev); + return -EIO; + } + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_devs, *ii, + &ip->state[i], + devnr, type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) + continue; + ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) + continue; + (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void gpio_event_suspend(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + ip->info->power(ip->info, 0); +} + +void gpio_event_resume(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} +#endif + +static int gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct gpio_event_platform_data *event_info; + int dev_count = 1; + int i; + int registered = 0; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if ((!event_info->name && !event_info->names[0]) || + !event_info->info || !event_info->info_count) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + if (!event_info->name) + while (event_info->names[dev_count]) + dev_count++; + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count + + sizeof(*ip->input_devs) + + sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + ip->input_devs = (void*)&ip->state[event_info->info_count]; + platform_set_drvdata(pdev, ip); + + for (i = 0; i < dev_count; i++) { + struct input_dev *input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: " + "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + input_dev->name = event_info->name ? + event_info->name : event_info->names[i]; + input_dev->event = gpio_input_event; + ip->input_devs->dev[i] = input_dev; + } + ip->input_devs->count = dev_count; + ip->info = event_info; + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ip->early_suspend.suspend = gpio_event_suspend; + ip->early_suspend.resume = gpio_event_resume; + register_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 1); + } + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + for (i = 0; i < dev_count; i++) { + err = input_register_device(ip->input_devs->dev[i]); + if (err) { + pr_err("gpio_event_probe: Unable to register %s " + "input device\n", ip->input_devs->dev[i]->name); + goto err_input_register_device_failed; + } + registered++; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < registered; i++) + input_unregister_device(ip->input_devs->dev[i]); + for (i = dev_count - 1; i >= registered; i--) { + input_free_device(ip->input_devs->dev[i]); +err_input_dev_alloc_failed: + ; + } + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + int i; + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < ip->input_devs->count; i++) + input_unregister_device(ip->input_devs->dev[i]); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +module_platform_driver(gpio_event_driver); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 000000000000..3122c19466a4 --- /dev/null +++ b/drivers/input/misc/gpio_input.c @@ -0,0 +1,376 @@ +/* drivers/input/misc/gpio_input.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wake_lock wake_lock; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + bool sync_needed; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + sync_needed = false; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + sync_needed = true; + } + if (sync_needed) { + for (i = 0; i < ds->input_devs->count; i++) + input_sync(ds->input_devs->dev[i]); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + wake_unlock(&ds->wake_lock); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + wake_lock(&ds->wake_lock); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq_nosync(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + input_sync(ds->input_devs->dev[key_entry->dev]); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + if (ds->info->info.no_suspend) { + err = enable_irq_wake(irq); + if (err) { + pr_err("gpio_event_input_request_irqs: " + "enable_irq_wake failed for input %d, " + "irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_enable_irq_wake_failed; + } + } + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); +err_enable_irq_wake_failed: + free_irq(irq, &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_devs = input_devs; + ds->info = di; + wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + int dev = di->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_input_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + di->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " + "mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + int irq = gpio_to_irq(di->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); + free_irq(irq, &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + wake_lock_destroy(&ds->wake_lock); + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c new file mode 100644 index 000000000000..eaa9e89d473a --- /dev/null +++ b/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,441 @@ +/* drivers/input/misc/gpio_matrix.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_kp { + struct gpio_event_input_devs *input_devs; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned int disabled_irq:1; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_devs->dev[dev], keycode, pressed); + } + } +} + +static void report_sync(struct gpio_kp *kp) +{ + int i; + + for (i = 0; i < kp->input_devs->count; i++) + input_sync(kp->input_devs->dev[i]); +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + report_sync(kp); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) { + /* ignore interrupt while registering the handler */ + kp->disabled_irq = 1; + disable_irq_nosync(irq_in); + return IRQ_HANDLED; + } + + for (i = 0; i < mi->ninputs; i++) + disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = enable_irq_wake(irq); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + if (kp->disabled_irq) { + kp->disabled_irq = 0; + enable_irq(irq); + } + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_devs = input_devs; + kp->keypad_info = mi; + for (i = 0; i < key_count; i++) { + unsigned short keyentry = mi->keymap[i]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + if (dev >= input_devs->count) { + pr_err("gpiomatrix: bad device index %d >= " + "%d for key code %d\n", + dev, input_devs->count, keycode); + err = -EINVAL; + goto err_bad_keymap; + } + if (keycode && keycode <= KEY_MAX) + input_set_capability(input_devs->dev[dev], + EV_KEY, keycode); + } + + for (i = 0; i < mi->noutputs; i++) { + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_output_gpio_configure_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " + "%s%s in %s mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } +err_bad_keymap: + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c new file mode 100644 index 000000000000..2aac2fad0a17 --- /dev/null +++ b/drivers/input/misc/gpio_output.c @@ -0,0 +1,97 @@ +/* drivers/input/misc/gpio_output.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +int gpio_event_output_event( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + + for (i = 0; i < oi->keymap_size; i++) { + int dev = oi->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_output_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + oi->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], oi->type, + oi->keymap[i].code); + } + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + return ret; +} + diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h new file mode 100644 index 000000000000..2613fc5e4a93 --- /dev/null +++ b/include/linux/gpio_event.h @@ -0,0 +1,170 @@ +/* include/linux/gpio_event.h + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_GPIO_EVENT_H +#define _LINUX_GPIO_EVENT_H + +#include + +struct gpio_event_input_devs { + int count; + struct input_dev *dev[]; +}; +enum { + GPIO_EVENT_FUNC_UNINIT = 0x0, + GPIO_EVENT_FUNC_INIT = 0x1, + GPIO_EVENT_FUNC_SUSPEND = 0x2, + GPIO_EVENT_FUNC_RESUME = 0x3, +}; +struct gpio_event_info { + int (*func)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, int func); + int (*event)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value); /* out events */ + bool no_suspend; +}; + +struct gpio_event_platform_data { + const char *name; + struct gpio_event_info **info; + size_t info_count; + int (*power)(const struct gpio_event_platform_data *pdata, bool on); + const char *names[]; /* If name is NULL, names contain a NULL */ + /* terminated list of input devices to create */ +}; + +#define GPIO_EVENT_DEV_NAME "gpio-event" + +/* Key matrix */ + +enum gpio_event_matrix_flags { + /* unset: drive active output low, set: drive active output high */ + GPIOKPF_ACTIVE_HIGH = 1U << 0, + GPIOKPF_DEBOUNCE = 1U << 1, + GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, + GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | + GPIOKPF_DEBOUNCE, + GPIOKPF_DRIVE_INACTIVE = 1U << 3, + GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, + GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, + GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, + GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, +}; + +#define MATRIX_CODE_BITS (10) +#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) +#define MATRIX_KEY(dev, code) \ + (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) + +extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_matrix_info { + /* initialize to gpio_event_matrix_func */ + struct gpio_event_info info; + /* size must be ninputs * noutputs */ + const unsigned short *keymap; + unsigned int *input_gpios; + unsigned int *output_gpios; + unsigned int ninputs; + unsigned int noutputs; + /* time to wait before reading inputs after driving each output */ + ktime_t settle_time; + /* time to wait before scanning the keypad a second time */ + ktime_t debounce_delay; + ktime_t poll_time; + unsigned flags; +}; + +/* Directly connected inputs and outputs */ + +enum gpio_event_direct_flags { + GPIOEDF_ACTIVE_HIGH = 1U << 0, +/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ +/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ + GPIOEDF_PRINT_KEYS = 1U << 8, + GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, + GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, +}; + +struct gpio_event_direct_entry { + uint32_t gpio:16; + uint32_t code:10; + uint32_t dev:6; +}; + +/* inputs */ +extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_input_info { + /* initialize to gpio_event_input_func */ + struct gpio_event_info info; + ktime_t debounce_time; + ktime_t poll_time; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + +/* outputs */ +extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, + unsigned int dev, unsigned int type, + unsigned int code, int value); +struct gpio_event_output_info { + /* initialize to gpio_event_output_func and gpio_event_output_event */ + struct gpio_event_info info; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + + +/* axes */ + +enum gpio_event_axis_flags { + GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, + GPIOEAF_PRINT_RAW = 1U << 17, + GPIOEAF_PRINT_EVENT = 1U << 18, +}; + +extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_axis_info { + /* initialize to gpio_event_axis_func */ + struct gpio_event_info info; + uint8_t count; /* number of gpios for this axis */ + uint8_t dev; /* device index when using multiple input devices */ + uint8_t type; /* EV_REL or EV_ABS */ + uint16_t code; + uint16_t decoded_size; + uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); + uint32_t *gpio; + uint32_t flags; +}; +#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map +#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map +uint16_t gpio_axis_4bit_gray_map( + struct gpio_event_axis_info *info, uint16_t in); +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in); + +#endif From 45818d534d428e1010d47c15928ed8ee19abdb1f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 20:26:28 -0800 Subject: [PATCH 0433/1276] ANDROID: input: gpio_event: remove early suspend Remove the early suspend handler. Leave the suspend functions for now, they should eventually get called through a userspace interface.x Change-Id: I67f9dafe32fe32577bab93c42b95824db96c215c Signed-off-by: Colin Cross --- drivers/input/misc/gpio_event.c | 39 ++++++++------------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c index d4e5b4dfe19f..90f07eba3ce9 100644 --- a/drivers/input/misc/gpio_event.c +++ b/drivers/input/misc/gpio_event.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -24,7 +23,6 @@ struct gpio_event { struct gpio_event_input_devs *input_devs; const struct gpio_event_platform_data *info; - struct early_suspend early_suspend; void *state[0]; }; @@ -101,23 +99,19 @@ static int gpio_event_call_all_func(struct gpio_event *ip, int func) return ret; } -#ifdef CONFIG_HAS_EARLYSUSPEND -void gpio_event_suspend(struct early_suspend *h) +static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); - ip->info->power(ip->info, 0); + if (ip->info->power) + ip->info->power(ip->info, 0); } -void gpio_event_resume(struct early_suspend *h) +static void __maybe_unused gpio_event_resume(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); - ip->info->power(ip->info, 1); + if (ip->info->power) + ip->info->power(ip->info, 1); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); } -#endif static int gpio_event_probe(struct platform_device *pdev) { @@ -169,15 +163,8 @@ static int gpio_event_probe(struct platform_device *pdev) } ip->input_devs->count = dev_count; ip->info = event_info; - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; - ip->early_suspend.suspend = gpio_event_suspend; - ip->early_suspend.resume = gpio_event_resume; - register_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 1); - } err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); if (err) @@ -198,12 +185,8 @@ static int gpio_event_probe(struct platform_device *pdev) err_input_register_device_failed: gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); err_call_all_func_failed: - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < registered; i++) input_unregister_device(ip->input_devs->dev[i]); for (i = dev_count - 1; i >= registered; i--) { @@ -222,12 +205,8 @@ static int gpio_event_remove(struct platform_device *pdev) int i; gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); - if (ip->info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (ip->info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < ip->input_devs->count; i++) input_unregister_device(ip->input_devs->dev[i]); kfree(ip); From e8eed0ebbb18862f33790aef3358b832ecd8790b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 19 Jun 2012 21:06:47 -0700 Subject: [PATCH 0434/1276] ANDROID: input: gpio_input: convert from wakelocks to wakeup sources And add device names to wakeup source names Change-Id: Ia5f2723319a2e749f00d6ec7d846edff6af6d5c2 Signed-off-by: Todd Poynor --- drivers/input/misc/gpio_input.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c index 3122c19466a4..5875d739c550 100644 --- a/drivers/input/misc/gpio_input.c +++ b/drivers/input/misc/gpio_input.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include enum { DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ @@ -45,7 +45,7 @@ struct gpio_input_state { int use_irq; int debounce_count; spinlock_t irq_lock; - struct wake_lock wake_lock; + struct wakeup_source *ws; struct gpio_key_state key_state[0]; }; @@ -153,7 +153,7 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) else if (!ds->use_irq) hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); else - wake_unlock(&ds->wake_lock); + __pm_relax(ds->ws); spin_unlock_irqrestore(&ds->irq_lock, irqflags); @@ -179,7 +179,7 @@ static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) if (ks->debounce & DEBOUNCE_WAIT_IRQ) { ks->debounce = DEBOUNCE_UNKNOWN; if (ds->debounce_count++ == 0) { - wake_lock(&ds->wake_lock); + __pm_stay_awake(ds->ws); hrtimer_start( &ds->timer, ds->info->debounce_time, HRTIMER_MODE_REL); @@ -262,6 +262,7 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, unsigned long irqflags; struct gpio_event_input_info *di; struct gpio_input_state *ds = *data; + char *wlname; di = container_of(info, struct gpio_event_input_info, info); @@ -297,7 +298,19 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ds->debounce_count = di->keymap_size; ds->input_devs = input_devs; ds->info = di; - wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", + input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : ""); + + ds->ws = wakeup_source_register(wlname); + kfree(wlname); + if (!ds->ws) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate wakeup source\n"); + goto err_ws_failed; + } + spin_lock_init(&ds->irq_lock); for (i = 0; i < di->keymap_size; i++) { @@ -369,7 +382,8 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ; } err_bad_keymap: - wake_lock_destroy(&ds->wake_lock); + wakeup_source_unregister(ds->ws); +err_ws_failed: kfree(ds); err_ds_alloc_failed: return ret; From d25e67010b693c6eeaa81dfc77fdd289b9bc74cf Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 12 Jan 2017 12:34:22 -0800 Subject: [PATCH 0435/1276] ANDROID: input: gpio_matrix: Remove wakelock.h dependencies Change-Id: I228bcdebf28f5c67765002043d3f919718827316 Signed-off-by: Dmitry Shmidt --- drivers/input/misc/gpio_matrix.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c index eaa9e89d473a..08769dd88f56 100644 --- a/drivers/input/misc/gpio_matrix.c +++ b/drivers/input/misc/gpio_matrix.c @@ -19,13 +19,12 @@ #include #include #include -#include struct gpio_kp { struct gpio_event_input_devs *input_devs; struct gpio_event_matrix_info *keypad_info; struct hrtimer timer; - struct wake_lock wake_lock; + struct wakeup_source wake_src; int current_output; unsigned int use_irq:1; unsigned int key_state_changed:1; @@ -215,7 +214,7 @@ static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) } for (in = 0; in < mi->ninputs; in++) enable_irq(gpio_to_irq(mi->input_gpios[in])); - wake_unlock(&kp->wake_lock); + __pm_relax(&kp->wake_src); return HRTIMER_NORESTART; } @@ -242,7 +241,7 @@ static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) else gpio_direction_input(mi->output_gpios[i]); } - wake_lock(&kp->wake_lock); + __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return IRQ_HANDLED; } @@ -396,7 +395,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); kp->timer.function = gpio_keypad_timer_func; - wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + wakeup_source_init(&kp->wake_src, "gpio_kp"); err = gpio_keypad_request_irqs(kp); kp->use_irq = err == 0; @@ -406,7 +405,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, kp->use_irq ? "interrupt" : "polling"); if (kp->use_irq) - wake_lock(&kp->wake_lock); + __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return 0; @@ -420,7 +419,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, free_irq(gpio_to_irq(mi->input_gpios[i]), kp); hrtimer_cancel(&kp->timer); - wake_lock_destroy(&kp->wake_lock); + wakeup_source_trash(&kp->wake_src); for (i = mi->noutputs - 1; i >= 0; i--) { err_gpio_direction_input_failed: gpio_free(mi->input_gpios[i]); From 30ff6f35d3ccd1f4e6026afa0f3f4274325fef1b Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Thu, 5 Feb 2015 16:07:59 -0800 Subject: [PATCH 0436/1276] ANDROID: kbuild: make it possible to specify the module output dir Make modinst_dir user-defined on the command line. This allows to do things like: make MODLIB=output/ modinst_dir=. modules_install to ensure all the .ko are in the output/ directory. Change-Id: I2bc007eea27ee744d35289e26e4a8ac43ba04151 Signed-off-by: Rom Lemarchand --- scripts/Makefile.modinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index ff5ca9817a85..8ff0669b3d0d 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -30,7 +30,7 @@ quiet_cmd_modules_install = INSTALL $@ INSTALL_MOD_DIR ?= extra ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) -modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) +modinst_dir ?= $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) $(modules): $(call cmd,modules_install,$(MODLIB)/$(modinst_dir)) From 9eddc97c2e2b261c48a423a34a2f04c2a9b7c9dc Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 5 Jan 2016 17:36:31 +0530 Subject: [PATCH 0437/1276] ANDROID: kbuild: Makefile.clean: make Kbuild and Makefile optional AOSP commit b13ce9f4aa6f "ARM64: add option to build Image.gz/dtb combo" broke archclean / mrproper build targets and we run into: ---------- ./scripts/Makefile.clean:14: arch/arm64/boot/amd/Makefile: No such file or directory make[2]: *** No rule to make target `arch/arm64/boot/amd/Makefile'. Stop. make[1]: *** [arch/arm64/boot/amd] Error 2 make: *** [archclean] Error 2 ---------- This patch skip the missing Kbuild/Makefile reporting error. It does the job (i.e cleanup dts/*/*.dtb and do not spit out missing file error messages as well). Signed-off-by: Amit Pundir --- scripts/Makefile.clean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean index 0b80e3207b20..b20bce9235b9 100644 --- a/scripts/Makefile.clean +++ b/scripts/Makefile.clean @@ -12,7 +12,7 @@ include scripts/Kbuild.include # The filename Kbuild has precedence over Makefile kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) -include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) +-include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) # Figure out what we need to build from the various variables # ========================================================================== From ffe0479296dcb5c1f3c0990c2c2af4cff6f226f8 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Fri, 21 Apr 2017 14:04:28 -0700 Subject: [PATCH 0438/1276] CHROMIUM: kbuild: clang: Disable the 'duplicate-decl-specifier' warning clang generates plenty of these warnings in different parts of the code. They are mostly caused by container_of() and other macros which declare a "const *" variable for their internal use which triggers a "duplicate 'const' specifier" warning if the is already const qualified. Change-Id: I3ad9d33e31b7b40f926554eed2afeea1ebb7e961 Wording-mostly-from: Michael Davidson Signed-off-by: Matthias Kaehlcke --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 69fa5c0310d8..c07bb28f05ae 100644 --- a/Makefile +++ b/Makefile @@ -702,6 +702,7 @@ KBUILD_CPPFLAGS += $(call cc-option,-Qunused-arguments,) KBUILD_CFLAGS += $(call cc-disable-warning, format-invalid-specifier) KBUILD_CFLAGS += $(call cc-disable-warning, gnu) KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) +KBUILD_CFLAGS += $(call cc-disable-warning, duplicate-decl-specifier) # Quiet clang warning: comparison of unsigned expression < 0 is always false KBUILD_CFLAGS += $(call cc-disable-warning, tautological-compare) # CLANG uses a _MergedGlobals as optimization, but this breaks modpost, as the From e26aca4cbfb18e94c3c4384efc2fa6790df2ae1c Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 25 Oct 2016 13:59:59 -0700 Subject: [PATCH 0439/1276] ANDROID: Kbuild, LLVMLinux: allow overriding clang target triple Android has an unusual setup where the kernel needs to target [arch]-linux-gnu to avoid Android userspace-specific flags and optimizations, but AOSP doesn't ship a matching binutils. Add a new variable CLANG_TRIPLE which can override the "-target" triple used to compile the kernel, while using a different CROSS_COMPILE to pick the binutils/gcc installation. For Android you'd do something like: export CLANG_TRIPLE=aarch64-linux-gnu- export CROSS_COMPILE=aarch64-linux-android- If you don't need something like this, leave CLANG_TRIPLE unset and it will default to CROSS_COMPILE. Change-Id: I85d63599c6ab8ed458071cdf9197d85b1f7f150b Signed-off-by: Greg Hackmann --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c07bb28f05ae..e95542b02752 100644 --- a/Makefile +++ b/Makefile @@ -482,7 +482,8 @@ endif ifeq ($(cc-name),clang) ifneq ($(CROSS_COMPILE),) -CLANG_TARGET := --target=$(notdir $(CROSS_COMPILE:%-=%)) +CLANG_TRIPLE ?= $(CROSS_COMPILE) +CLANG_TARGET := --target=$(notdir $(CLANG_TRIPLE:%-=%)) GCC_TOOLCHAIN_DIR := $(dir $(shell which $(LD))) CLANG_PREFIX := --prefix=$(GCC_TOOLCHAIN_DIR) GCC_TOOLCHAIN := $(realpath $(GCC_TOOLCHAIN_DIR)/..) From 954f5eb5cd1d70a7ced861d9476208a771879097 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 27 Oct 2015 16:42:08 -0700 Subject: [PATCH 0440/1276] ANDROID: mm: add a field to store names for private anonymous memory Userspace processes often have multiple allocators that each do anonymous mmaps to get memory. When examining memory usage of individual processes or systems as a whole, it is useful to be able to break down the various heaps that were allocated by each layer and examine their size, RSS, and physical memory usage. This patch adds a user pointer to the shared union in vm_area_struct that points to a null terminated string inside the user process containing a name for the vma. vmas that point to the same address will be merged, but vmas that point to equivalent strings at different addresses will not be merged. Userspace can set the name for a region of memory by calling prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name); Setting the name to NULL clears it. The names of named anonymous vmas are shown in /proc/pid/maps as [anon:] and in /proc/pid/smaps in a new "Name" field that is only present for named vmas. If the userspace pointer is no longer valid all or part of the name will be replaced with "". The idea to store a userspace pointer to reduce the complexity within mm (at the expense of the complexity of reading /proc/pid/mem) came from Dave Hansen. This results in no runtime overhead in the mm subsystem other than comparing the anon_name pointers when considering vma merging. The pointer is stored in a union with fieds that are only used on file-backed mappings, so it does not increase memory usage. Includes fix from Jed Davis for typo in prctl_set_vma_anon_name, which could attempt to set the name across two vmas at the same time due to a typo, which might corrupt the vma list. Fix it to use tmp instead of end to limit the name setting to a single vma at a time. Change-Id: I9aa7b6b5ef536cd780599ba4e2fba8ceebe8b59f Signed-off-by: Dmitry Shmidt [AmitP: Fix get_user_pages_remote() call to align with upstream commit 5b56d49fc31d ("mm: add locked parameter to get_user_pages_remote()")] Signed-off-by: Amit Pundir --- Documentation/filesystems/proc.txt | 6 ++ fs/proc/task_mmu.c | 64 +++++++++++- fs/userfaultfd.c | 9 +- include/linux/mm.h | 2 +- include/linux/mm_types.h | 24 ++++- include/uapi/linux/prctl.h | 3 + kernel/sys.c | 152 +++++++++++++++++++++++++++++ mm/madvise.c | 2 +- mm/mempolicy.c | 3 +- mm/mlock.c | 2 +- mm/mmap.c | 39 +++++--- mm/mprotect.c | 2 +- 12 files changed, 280 insertions(+), 28 deletions(-) diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 22b4b00dee31..02ba2136a358 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -398,6 +398,8 @@ is not associated with a file: [stack] = the stack of the main process [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -426,6 +428,7 @@ KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 0 kB VmFlags: rd ex mr mw me dw +Name: name from userspace the first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -498,6 +501,9 @@ Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may be vanished or the reverse -- new added. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 5ea1d64cb0b4..2c57d817876c 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -123,6 +123,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv) } #endif +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) +{ + const char __user *name = vma_get_anon_name(vma); + struct mm_struct *mm = vma->vm_mm; + + unsigned long page_start_vaddr; + unsigned long page_offset; + unsigned long num_pages; + unsigned long max_len = NAME_MAX; + int i; + + page_start_vaddr = (unsigned long)name & PAGE_MASK; + page_offset = (unsigned long)name - page_start_vaddr; + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); + + seq_puts(m, "[anon:"); + + for (i = 0; i < num_pages; i++) { + int len; + int write_len; + const char *kaddr; + long pages_pinned; + struct page *page; + + pages_pinned = get_user_pages_remote(current, mm, + page_start_vaddr, 1, 0, &page, NULL, NULL); + if (pages_pinned < 1) { + seq_puts(m, "]"); + return; + } + + kaddr = (const char *)kmap(page); + len = min(max_len, PAGE_SIZE - page_offset); + write_len = strnlen(kaddr + page_offset, len); + seq_write(m, kaddr + page_offset, write_len); + kunmap(page); + put_page(page); + + /* if strnlen hit a null terminator then we're done */ + if (write_len != len) + break; + + max_len -= len; + page_offset = 0; + page_start_vaddr += PAGE_SIZE; + } + + seq_putc(m, ']'); +} + static void vma_stop(struct proc_maps_private *priv) { struct mm_struct *mm = priv->mm; @@ -344,8 +394,15 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) goto done; } - if (is_stack(vma)) + if (is_stack(vma)) { name = "[stack]"; + goto done; + } + + if (vma_get_anon_name(vma)) { + seq_pad(m, ' '); + seq_print_vma_name(m, vma); + } } done: @@ -780,6 +837,11 @@ static int show_smap(struct seq_file *m, void *v) smap_gather_stats(vma, &mss); show_map_vma(m, vma); + if (vma_get_anon_name(vma)) { + seq_puts(m, "Name: "); + seq_print_vma_name(m, vma); + seq_putc(m, '\n'); + } SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start); SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma)); diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index bfa0ec69f924..f4a021d5ec83 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -890,7 +890,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file) new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - NULL_VM_UFFD_CTX); + NULL_VM_UFFD_CTX, + vma_get_anon_name(vma)); if (prev) vma = prev; else @@ -1423,7 +1424,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, prev = vma_merge(mm, prev, start, vma_end, new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - ((struct vm_userfaultfd_ctx){ ctx })); + ((struct vm_userfaultfd_ctx){ ctx }), + vma_get_anon_name(vma)); if (prev) { vma = prev; goto next; @@ -1581,7 +1583,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, prev = vma_merge(mm, prev, start, vma_end, new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - NULL_VM_UFFD_CTX); + NULL_VM_UFFD_CTX, + vma_get_anon_name(vma)); if (prev) { vma = prev; goto next; diff --git a/include/linux/mm.h b/include/linux/mm.h index 0416a7204be3..5c5f2184228f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2227,7 +2227,7 @@ static inline int vma_adjust(struct vm_area_struct *vma, unsigned long start, extern struct vm_area_struct *vma_merge(struct mm_struct *, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, - struct mempolicy *, struct vm_userfaultfd_ctx); + struct mempolicy *, struct vm_userfaultfd_ctx, const char __user *); extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); extern int __split_vma(struct mm_struct *, struct vm_area_struct *, unsigned long addr, int new_below); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 5ed8f6292a53..7dfd40dd8ddc 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -290,11 +290,18 @@ struct vm_area_struct { /* * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree. + * + * For private anonymous mappings, a pointer to a null terminated string + * in the user process containing the name given to the vma, or NULL + * if unnamed. */ - struct { - struct rb_node rb; - unsigned long rb_subtree_last; - } shared; + union { + struct { + struct rb_node rb; + unsigned long rb_subtree_last; + } shared; + const char __user *anon_name; + }; /* * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma @@ -649,4 +656,13 @@ typedef struct { unsigned long val; } swp_entry_t; +/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ +static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) +{ + if (vma->vm_file) + return NULL; + + return vma->anon_name; +} + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index c0d7ea0bf5b6..851c0032e14b 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -219,4 +219,7 @@ struct prctl_mm_map { # define PR_SPEC_DISABLE (1UL << 2) # define PR_SPEC_FORCE_DISABLE (1UL << 3) +#define PR_SET_VMA 0x53564d41 +# define PR_SET_VMA_ANON_NAME 0 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 123bd73046ec..e7e277ff559c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -2258,6 +2260,153 @@ int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which, return -EINVAL; } +#ifdef CONFIG_MMU +static int prctl_update_vma_anon_name(struct vm_area_struct *vma, + struct vm_area_struct **prev, + unsigned long start, unsigned long end, + const char __user *name_addr) +{ + struct mm_struct *mm = vma->vm_mm; + int error = 0; + pgoff_t pgoff; + + if (name_addr == vma_get_anon_name(vma)) { + *prev = vma; + goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma), + vma->vm_userfaultfd_ctx, name_addr); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; + + if (start != vma->vm_start) { + error = split_vma(mm, vma, start, 1); + if (error) + goto out; + } + + if (end != vma->vm_end) { + error = split_vma(mm, vma, end, 0); + if (error) + goto out; + } + +success: + if (!vma->vm_file) + vma->anon_name = name_addr; + +out: + if (error == -ENOMEM) + error = -EAGAIN; + return error; +} + +static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, + unsigned long arg) +{ + unsigned long tmp; + struct vm_area_struct *vma, *prev; + int unmapped_error = 0; + int error = -EINVAL; + + /* + * If the interval [start,end) covers some unmapped address + * ranges, just ignore them, but return -ENOMEM at the end. + * - this matches the handling in madvise. + */ + vma = find_vma_prev(current->mm, start, &prev); + if (vma && start > vma->vm_start) + prev = vma; + + for (;;) { + /* Still start < end. */ + error = -ENOMEM; + if (!vma) + return error; + + /* Here start < (end|vma->vm_end). */ + if (start < vma->vm_start) { + unmapped_error = -ENOMEM; + start = vma->vm_start; + if (start >= end) + return error; + } + + /* Here vma->vm_start <= start < (end|vma->vm_end) */ + tmp = vma->vm_end; + if (end < tmp) + tmp = end; + + /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ + error = prctl_update_vma_anon_name(vma, &prev, start, tmp, + (const char __user *)arg); + if (error) + return error; + start = tmp; + if (prev && start < prev->vm_end) + start = prev->vm_end; + error = unmapped_error; + if (start >= end) + return error; + if (prev) + vma = prev->vm_next; + else /* madvise_remove dropped mmap_sem */ + vma = find_vma(current->mm, start); + } +} + +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + struct mm_struct *mm = current->mm; + int error; + unsigned long len; + unsigned long end; + + if (start & ~PAGE_MASK) + return -EINVAL; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; + + /* Check to see whether len was rounded up from small -ve to zero */ + if (len_in && !len) + return -EINVAL; + + end = start + len; + if (end < start) + return -EINVAL; + + if (end == start) + return 0; + + down_write(&mm->mmap_sem); + + switch (opt) { + case PR_SET_VMA_ANON_NAME: + error = prctl_set_vma_anon_name(start, end, arg); + break; + default: + error = -EINVAL; + } + + up_write(&mm->mmap_sem); + + return error; +} +#else /* CONFIG_MMU */ +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + return -EINVAL; +} +#endif + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -2476,6 +2625,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return -EINVAL; error = arch_prctl_spec_ctrl_set(me, arg2, arg3); break; + case PR_SET_VMA: + error = prctl_set_vma(arg2, arg3, arg4, arg5); + break; default: error = -EINVAL; break; diff --git a/mm/madvise.c b/mm/madvise.c index 71d21df2a3f3..899b19e38aee 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -138,7 +138,7 @@ static long madvise_behavior(struct vm_area_struct *vma, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index da858f794eb6..9625e14b37b4 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -705,7 +705,8 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, ((vmstart - vma->vm_start) >> PAGE_SHIFT); prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, - new_pol, vma->vm_userfaultfd_ctx); + new_pol, vma->vm_userfaultfd_ctx, + vma_get_anon_name(vma)); if (prev) { vma = prev; next = vma->vm_next; diff --git a/mm/mlock.c b/mm/mlock.c index 41cc47e28ad6..9fbe2af06cff 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -535,7 +535,7 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mmap.c b/mm/mmap.c index f7cd9cb966c0..510369eb8847 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -982,7 +982,8 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, */ static inline int is_mergeable_vma(struct vm_area_struct *vma, struct file *file, unsigned long vm_flags, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { /* * VM_SOFTDIRTY should not prevent from VMA merging, if we @@ -1000,6 +1001,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, return 0; if (!is_mergeable_vm_userfaultfd_ctx(vma, vm_userfaultfd_ctx)) return 0; + if (vma_get_anon_name(vma) != anon_name) + return 0; return 1; } @@ -1032,9 +1035,10 @@ static int can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) && + if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { if (vma->vm_pgoff == vm_pgoff) return 1; @@ -1053,9 +1057,10 @@ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) && + if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; vm_pglen = vma_pages(vma); @@ -1066,9 +1071,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, } /* - * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out - * whether that can be merged with its predecessor or its successor. - * Or both (it neatly fills a hole). + * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), + * figure out whether that can be merged with its predecessor or its + * successor. Or both (it neatly fills a hole). * * In most cases - when called for mmap, brk or mremap - [addr,end) is * certain not to be mapped by the time vma_merge is called; but when @@ -1110,7 +1115,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, unsigned long end, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t pgoff, struct mempolicy *policy, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { pgoff_t pglen = (end - addr) >> PAGE_SHIFT; struct vm_area_struct *area, *next; @@ -1143,7 +1149,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, mpol_equal(vma_policy(prev), policy) && can_vma_merge_after(prev, vm_flags, anon_vma, file, pgoff, - vm_userfaultfd_ctx)) { + vm_userfaultfd_ctx, + anon_name)) { /* * OK, it can. Can we now merge in the successor as well? */ @@ -1152,7 +1159,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, can_vma_merge_before(next, vm_flags, anon_vma, file, pgoff+pglen, - vm_userfaultfd_ctx) && + vm_userfaultfd_ctx, + anon_name) && is_mergeable_anon_vma(prev->anon_vma, next->anon_vma, NULL)) { /* cases 1, 6 */ @@ -1175,7 +1183,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, mpol_equal(policy, vma_policy(next)) && can_vma_merge_before(next, vm_flags, anon_vma, file, pgoff+pglen, - vm_userfaultfd_ctx)) { + vm_userfaultfd_ctx, + anon_name)) { if (prev && addr < prev->vm_end) /* case 4 */ err = __vma_adjust(prev, prev->vm_start, addr, prev->vm_pgoff, NULL, next); @@ -1720,7 +1729,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, * Can we just expand an old mapping? */ vma = vma_merge(mm, prev, addr, addr + len, vm_flags, - NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX); + NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX, NULL); if (vma) goto out; @@ -2973,7 +2982,7 @@ static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long fla /* Can we just expand an old private anonymous mapping? */ vma = vma_merge(mm, prev, addr, addr + len, flags, - NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX); + NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX, NULL); if (vma) goto out; @@ -3171,7 +3180,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, return NULL; /* should never get here */ new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (new_vma) { /* * Source vma may have been merged into new_vma diff --git a/mm/mprotect.c b/mm/mprotect.c index 6d331620b9e5..58f591daf578 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -398,7 +398,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *pprev = vma_merge(mm, *pprev, start, end, newflags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*pprev) { vma = *pprev; VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY); From 98e5c3216112628ba5a00b601bc817a0cae7d797 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 7 Oct 2010 14:39:16 -0700 Subject: [PATCH 0441/1276] ANDROID: mmc: core: Add "ignore mmc pm notify" functionality Change-Id: I20821a82831b07ca037973d5d92e832372c6b583 Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/host.c | 6 ++++-- include/linux/mmc/pm.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index f57f5de54206..f3dc49fa078d 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -441,7 +441,8 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); - mmc_register_pm_notifier(host); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_register_pm_notifier(host); return 0; } @@ -458,7 +459,8 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - mmc_unregister_pm_notifier(host); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_unregister_pm_notifier(host); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h index 4a139204c20c..6e2d6a135c7e 100644 --- a/include/linux/mmc/pm.h +++ b/include/linux/mmc/pm.h @@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ #endif /* LINUX_MMC_PM_H */ From df914ef8cfade9a8b2b0f515371b8f60a46f300d Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:35:44 -0400 Subject: [PATCH 0442/1276] ANDROID: net: Paranoid network. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_ANDROID_PARANOID_NETWORK, require specific uids/gids to instantiate network sockets. Signed-off-by: Robert Love paranoid networking: Use in_egroup_p() to check group membership The previous group_search() caused trouble for partners with module builds. in_egroup_p() is also cleaner. Signed-off-by: Nick Pelly Fix 2.6.29 build. Signed-off-by: Arve HjønnevÃ¥g net: Fix compilation of the IPv6 module Fix compilation of the IPv6 module -- current->euid does not exist anymore, current_euid() is what needs to be used. Signed-off-by: Steinar H. Gunderson net: bluetooth: Remove the AID_NET_BT* gid numbers Removed bluetooth checks for AID_NET_BT and AID_NET_BT_ADMIN which are not useful anymore. This is in preparation for getting rid of all the AID_* gids. Change-Id: I879d7181f07532784499ef152288d12a03ab6354 Signed-off-by: JP Abgrall [AmitP: Folded following android-4.9 commit changes into this patch a2624d7b9d73 ("ANDROID: Add android_aid.h")] Signed-off-by: Amit Pundir --- include/linux/android_aid.h | 25 +++++++++++++++++++++++++ net/Kconfig | 6 ++++++ net/bluetooth/af_bluetooth.c | 29 +++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 32 +++++++++++++++++++++++++++++++- net/ipv6/af_inet6.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 include/linux/android_aid.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h new file mode 100644 index 000000000000..dc66530e5fc7 --- /dev/null +++ b/include/linux/android_aid.h @@ -0,0 +1,25 @@ +/* include/linux/android_aid.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_ANDROID_AID_H +#define _LINUX_ANDROID_AID_H + +/* AIDs that the kernel treats differently */ +#define AID_OBSOLETE_000 KGIDT_INIT(3001) /* was NET_BT_ADMIN */ +#define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ +#define AID_INET KGIDT_INIT(3003) +#define AID_NET_RAW KGIDT_INIT(3004) + +#endif diff --git a/net/Kconfig b/net/Kconfig index 228dfa382eec..4d84cee12ab7 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -92,6 +92,12 @@ source "net/netlabel/Kconfig" endif # if INET +config ANDROID_PARANOID_NETWORK + bool "Only allow certain groups to create sockets" + default y + help + none + config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index deacc52d7ff1..6fa61b875b69 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -108,11 +108,40 @@ void bt_sock_unregister(int proto) } EXPORT_SYMBOL(bt_sock_unregister); +#ifdef CONFIG_PARANOID_NETWORK +static inline int current_has_bt_admin(void) +{ + return !current_euid(); +} + +static inline int current_has_bt(void) +{ + return current_has_bt_admin(); +} +# else +static inline int current_has_bt_admin(void) +{ + return 1; +} + +static inline int current_has_bt(void) +{ + return 1; +} +#endif + static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) { int err; + if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || + proto == BTPROTO_L2CAP) { + if (!current_has_bt()) + return -EPERM; + } else if (!current_has_bt_admin()) + return -EPERM; + if (net != &init_net) return -EAFNOSUPPORT; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 1fbe2f815474..ef1275fcc0b8 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -123,6 +123,10 @@ #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* The inetsw table contains everything that inet_create needs to * build a new socket. */ @@ -240,6 +244,29 @@ int inet_listen(struct socket *sock, int backlog) } EXPORT_SYMBOL(inet_listen); +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + /* * Create an inet socket. */ @@ -258,6 +285,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -307,7 +337,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 9a4261e50272..e55548e56843 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -66,6 +66,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + #include "ip6_offload.h" MODULE_AUTHOR("Cast of dozens"); @@ -107,6 +111,29 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -122,6 +149,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + /* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; @@ -169,7 +199,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From 3d76291d7143fb90e9a5025b02d39ebaec43af13 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 19 Jun 2009 07:15:05 +0800 Subject: [PATCH 0443/1276] ANDROID: net: paranoid: security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable(). Signed-off-by: Chia-chi Yeh --- include/linux/android_aid.h | 1 + security/commoncap.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index dc66530e5fc7..3d7a5ead1200 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -21,5 +21,6 @@ #define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) +#define AID_NET_ADMIN KGIDT_INIT(3005) #endif diff --git a/security/commoncap.c b/security/commoncap.c index 2e489d6a3ac8..ce07c5bc359b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -31,6 +31,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. @@ -73,6 +77,11 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 0; + if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) + return 0; + /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target * user namespace's parents. From ddde0de4b4e3b84ef4d54e7b4def0ca663069e6e Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Tue, 30 Jun 2009 11:23:04 +0800 Subject: [PATCH 0444/1276] ANDROID: net: paranoid: Replace AID_NET_RAW checks with capable(CAP_NET_RAW). Signed-off-by: Chia-chi Yeh --- net/ipv4/af_inet.c | 36 +++++++++++------------------------- net/ipv6/af_inet6.c | 36 +++++++++++------------------------- 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ef1275fcc0b8..32615ca1dd6f 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -125,6 +125,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif /* The inetsw table contains everything that inet_create needs to @@ -244,29 +254,6 @@ int inet_listen(struct socket *sock, int backlog) } EXPORT_SYMBOL(inet_listen); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - /* * Create an inet socket. */ @@ -336,8 +323,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e55548e56843..6c330ed494b5 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -68,6 +68,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif #include "ip6_offload.h" @@ -111,29 +121,6 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -198,8 +185,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From 19484f170f8e5b8a3e4174f75c879d6a5b784087 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Jul 2011 15:32:57 -0700 Subject: [PATCH 0445/1276] ANDROID: net: paranoid: Only NET_ADMIN is allowed to fully control TUN interfaces. Signed-off-by: Chia-chi Yeh --- drivers/net/tun.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 50e9cc19023a..467b51b1570b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2840,6 +2840,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int ret; bool do_notify = false; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { + return -EPERM; + } +#endif + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || (_IOC_TYPE(cmd) == SOCK_IOC_TYPE && cmd != SIOCGSKNS)) { if (copy_from_user(&ifr, argp, ifreq_len)) From 9c15b6a45896b4fa113f4aa0f5e2ba742f7e861f Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 26 Mar 2012 16:54:15 +0530 Subject: [PATCH 0446/1276] ANDROID: net: paranoid: security: Add proper checks for Android specific capability checks Commit b641072 ("security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable().") introduces additional checks for AID_NET_xxx macros. Since the header file including those macros are conditionally included, the checks should also be conditionally executed. Change-Id: Iaec5208d5b95a46b1ac3f2db8449c661e803fa5b Signed-off-by: Tushar Behera Signed-off-by: Andrey Konovalov --- security/commoncap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/commoncap.c b/security/commoncap.c index ce07c5bc359b..5973c420c1f5 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -77,10 +77,12 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0; +#endif /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target From 51ffefefdc83afb3edbdf5f7e915cf0fc73b518d Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 25 Aug 2017 16:41:26 -0700 Subject: [PATCH 0447/1276] ANDROID: net: paranoid: commoncap: Begin to warn users of implicit PARANOID_NETWORK capability grants CAP_NET_ADMIN and CAP_NET_RAW are implicity granted to the "special" Android groups net_admin and net_raw. This is a byproduct of the init system not being able to specify capabilities back in the day, but has now been resolved and .rc files can explictly specify the capabilities to be granted to a service. Thus, we should start to remove this implict capability grant, and the first step is to warn when a process doesn't have explicit capablity but is a member of the implicitly granted group, when that capability is checked. This will allow for the PARANOID_NETWORK checks in commoncap.c to be totally removed in a future kernel. Change-Id: I6dac90e23608b6dba14a8f2049ba29ae56cb7ae4 Signed-off-by: John Stultz --- security/commoncap.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index 5973c420c1f5..ccc992794004 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -58,7 +58,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) } /** - * cap_capable - Determine whether a task has a particular effective capability + * __cap_capable - Determine whether a task has a particular effective capability * @cred: The credentials to use * @ns: The user namespace in which we need the capability * @cap: The capability to check for @@ -72,18 +72,11 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) * cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, +int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { struct user_namespace *ns = targ_ns; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 0; - if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) - return 0; -#endif - /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target * user namespace's parents. @@ -117,6 +110,27 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, /* We never get here */ } +int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, + int cap, int audit) +{ + int ret = __cap_capable(cred, targ_ns, cap, audit); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (ret != 0 && cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) { + printk("Process %s granted CAP_NET_RAW from Android group net_raw.\n", current->comm); + printk(" Please update the .rc file to explictly set 'capabilities NET_RAW'\n"); + printk(" Implicit grants are deprecated and will be removed in the future.\n"); + return 0; + } + if (ret != 0 && cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) { + printk("Process %s granted CAP_NET_ADMIN from Android group net_admin.\n", current->comm); + printk(" Please update the .rc file to explictly set 'capabilities NET_ADMIN'\n"); + printk(" Implicit grants are deprecated and will be removed in the future.\n"); + return 0; + } +#endif + return ret; +} /** * cap_settime - Determine whether the current process may set the system clock * @ts: The time to set From d72dda28aaf10c1be915641f2222cbbbd39f564e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 13:03:12 +0900 Subject: [PATCH 0448/1276] ANDROID: net: ip-sysctl: Document tcp_fwmark_accept This documentation patch is the part of original tcp_fwmark_accept implementation, Change-Id: I26bc1eceefd2c588d73b921865ab70e4645ade57 ("net: support marking accepting TCP sockets"). The implementation since then got upstreamed, 84f39b08d786 ("net: support marking accepting TCP sockets"), without this documentation part. Change-Id: I26bc1eceefd2c588d73b921865ab70e4645ade57 Signed-off-by: Lorenzo Colitti [AmitP: Refactored the commit log. Fixes: 84f39b08d786 ("net: support marking accepting TCP sockets") Signed-off-by: Amit Pundir --- Documentation/networking/ip-sysctl.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 960de8fe3f40..f234dbe3c3f1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -630,6 +630,16 @@ tcp_fastopen_blackhole_timeout_sec - INTEGER 0 to disable the blackhole detection. By default, it is set to 1hr. +tcp_fwmark_accept - BOOLEAN + If set, incoming connections to listening sockets that do not have a + socket mark will set the mark of the accepting socket to the fwmark of + the incoming SYN packet. This will cause all packets on that connection + (starting from the first SYNACK) to be sent with that fwmark. The + listening socket's mark is unchanged. Listening sockets that already + have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are + unaffected. + Default: 0 + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 127. Default value From 2e0627b312e38f19eff8360812234e95fca90490 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Thu, 31 Jul 2008 11:12:44 -0400 Subject: [PATCH 0449/1276] ANDROID: net: ipv4: sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size Add a family of knobs to /sys/kernel/ipv4 for controlling the TCP window size: tcp_wmem_min tcp_wmem_def tcp_wmem_max tcp_rmem_min tcp_rmem_def tcp_rmem_max This six values mirror the sysctl knobs in /proc/sys/net/ipv4/tcp_wmem and /proc/sys/net/ipv4/tcp_rmem. Sysfs, unlike sysctl, allows us to set and manage the files' permissions and owners. Signed-off-by: Robert Love --- net/ipv4/Makefile | 1 + net/ipv4/sysfs_net_ipv4.c | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 net/ipv4/sysfs_net_ipv4.c diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7446b98661d8..433e35abbcc5 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_BPFILTER) += bpfilter/ obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o +obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c new file mode 100644 index 000000000000..0cbbf10026a6 --- /dev/null +++ b/net/ipv4/sysfs_net_ipv4.c @@ -0,0 +1,88 @@ +/* + * net/ipv4/sysfs_net_ipv4.c + * + * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) + * + * Copyright (C) 2008 Google, Inc. + * + * Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define CREATE_IPV4_FILE(_name, _var) \ +static ssize_t _name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", _var); \ +} \ +static ssize_t _name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int val, ret; \ + ret = sscanf(buf, "%d", &val); \ + if (ret != 1) \ + return -EINVAL; \ + if (val < 0) \ + return -EINVAL; \ + _var = val; \ + return count; \ +} \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); + +CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); + +static struct attribute *ipv4_attrs[] = { + &tcp_wmem_min_attr.attr, + &tcp_wmem_def_attr.attr, + &tcp_wmem_max_attr.attr, + &tcp_rmem_min_attr.attr, + &tcp_rmem_def_attr.attr, + &tcp_rmem_max_attr.attr, + NULL +}; + +static struct attribute_group ipv4_attr_group = { + .attrs = ipv4_attrs, +}; + +static __init int sysfs_ipv4_init(void) +{ + struct kobject *ipv4_kobject; + int ret; + + ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); + if (!ipv4_kobject) + return -ENOMEM; + + ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); + if (ret) { + kobject_put(ipv4_kobject); + return ret; + } + + return 0; +} + +subsys_initcall(sysfs_ipv4_init); From 89d2959605d6c853286d6b06626f4648cdcf782b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Feb 2014 18:40:10 -0800 Subject: [PATCH 0450/1276] ANDROID: net: ipv4: tcp: add a sysctl to config the tcp_default_init_rwnd The default initial rwnd is hardcoded to 10. Now we allow it to be controlled via /proc/sys/net/ipv4/tcp_default_init_rwnd which limits the values from 3 to 100 This is somewhat needed because ipv6 routes are autoconfigured by the kernel. See "An Argument for Increasing TCP's Initial Congestion Window" in https://developers.google.com/speed/articles/tcp_initcwnd_paper.pdf Change-Id: I386b2a9d62de0ebe05c1ebe1b4bd91b314af5c54 Signed-off-by: JP Abgrall Conflicts: net/ipv4/sysctl_net_ipv4.c net/ipv4/tcp_input.c [AmitP: Folded following android-4.9 commit changes into this patch 3823c8b26e6e ("ANDROID: tcp: fix tcp_default_init_rwnd() for 4.1")] Signed-off-by: Amit Pundir --- include/net/tcp.h | 2 ++ net/ipv4/sysctl_net_ipv4.c | 22 ++++++++++++++++++++++ net/ipv4/tcp_input.c | 1 + net/ipv4/tcp_output.c | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 770917d0caa7..87ed93f14c52 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -248,6 +248,8 @@ extern long sysctl_tcp_mem[3]; #define TCP_RACK_STATIC_REO_WND 0x2 /* Use static RACK reo wnd */ #define TCP_RACK_NO_DUPTHRESH 0x4 /* Do not use DUPACK threshold in RACK */ +extern int sysctl_tcp_default_init_rwnd; + extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; extern unsigned long tcp_memory_pressure; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 891ed2f91467..bad5a38de987 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -219,6 +219,21 @@ static int ipv4_fwd_update_priority(struct ctl_table *table, int write, return ret; } +/* Validate changes from /proc interface. */ +static int proc_tcp_default_init_rwnd(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + int old_value = *(int *)ctl->data; + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + int new_value = *(int *)ctl->data; + + if (write && ret == 0 && (new_value < 3 || new_value > 100)) + *(int *)ctl->data = old_value; + + return ret; +} + static int proc_tcp_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -526,6 +541,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0444, .proc_handler = proc_tcp_available_ulp, }, + { + .procname = "tcp_default_init_rwnd", + .data = &sysctl_tcp_default_init_rwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_default_init_rwnd + }, { .procname = "icmp_msgs_per_sec", .data = &sysctl_icmp_msgs_per_sec, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 47e08c1b5bc3..88db2d6f70aa 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,6 +81,7 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; +int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 597dbd749f05..20abda1b6ff1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -187,7 +187,7 @@ u32 tcp_default_init_rwnd(u32 mss) * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ - u32 init_rwnd = TCP_INIT_CWND * 2; + u32 init_rwnd = sysctl_tcp_default_init_rwnd; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); From b89d8013a1312ab9a0b45d71baf45d7c2ef31d50 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 19:35:41 +0900 Subject: [PATCH 0451/1276] ANDROID: net: ipv6: autoconf routes into per-device tables Currently, IPv6 router discovery always puts routes into RT6_TABLE_MAIN. This causes problems for connection managers that want to support multiple simultaneous network connections and want control over which one is used by default (e.g., wifi and wired). To work around this connection managers typically take the routes they prefer and copy them to static routes with low metrics in the main table. This puts the burden on the connection manager to watch netlink to see if the routes have changed, delete the routes when their lifetime expires, etc. Instead, this patch adds a per-interface sysctl to have the kernel put autoconf routes into different tables. This allows each interface to have its own autoconf table, and choosing the default interface (or using different interfaces at the same time for different types of traffic) can be done using appropriate ip rules. The sysctl behaves as follows: - = 0: default. Put routes into RT6_TABLE_MAIN as before. - > 0: manual. Put routes into the specified table. - < 0: automatic. Add the absolute value of the sysctl to the device's ifindex, and use that table. The automatic mode is most useful in conjunction with net.ipv6.conf.default.accept_ra_rt_table. A connection manager or distribution could set it to, say, -100 on boot, and thereafter just use IP rules. Change-Id: I82d16e3737d9cdfa6489e649e247894d0d60cbb1 Signed-off-by: Lorenzo Colitti [AmitP: Refactored original changes to align with the changes introduced by upstream commits 830218c1add1 ("net: ipv6: Fix processing of RAs in presence of VRF"), 8d1c802b2815 ("net/ipv6: Flip FIB entries to fib6_info"). Also folded following android-4.9 commit changes into this patch be65fb01da4d ("ANDROID: net: ipv6: remove unused variable ifindex in")] Signed-off-by: Amit Pundir --- include/linux/ipv6.h | 1 + include/net/addrconf.h | 2 ++ include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 39 ++++++++++++++++++++++++++-- net/ipv6/route.c | 53 ++++++++++----------------------------- 5 files changed, 54 insertions(+), 42 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 8415bf1a9776..4a0fc3bce67b 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -42,6 +42,7 @@ struct ipv6_devconf { __s32 accept_ra_rt_info_max_plen; #endif #endif + __s32 accept_ra_rt_table; __s32 proxy_ndp; __s32 accept_source_route; __s32 accept_ra_from_local; diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 6def0351bcc3..cf1076601732 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -296,6 +296,8 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao); +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table); + /* * anycast prototypes (anycast.c) */ diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 9c0f4a92bcff..f2cef9c68424 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -166,6 +166,7 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_ACCEPT_RA_RT_TABLE, DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_SUPPRESS_FRAG_NDISC, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4e81ff2f4588..5f9e0e096a36 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -220,6 +220,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -274,6 +275,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -2310,6 +2312,31 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad ipv6_regen_rndid(idev); } +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table) { + /* Determines into what table to put autoconf PIO/RIO/default routes + * learned on this device. + * + * - If 0, use the same table for every device. This puts routes into + * one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route + * (but note that these three are currently all equal to + * RT6_TABLE_MAIN). + * - If > 0, use the specified table. + * - If < 0, put routes into table dev->ifindex + (-rt_table). + */ + struct inet6_dev *idev = in6_dev_get(dev); + u32 table; + int sysctl = idev->cnf.accept_ra_rt_table; + if (sysctl == 0) { + table = default_table; + } else if (sysctl > 0) { + table = (u32) sysctl; + } else { + table = (unsigned) dev->ifindex + (-sysctl); + } + in6_dev_put(idev); + return table; +} + /* * Add prefix route. */ @@ -2320,7 +2347,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric, u32 flags, gfp_t gfp_flags) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX), .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_expires = expires, @@ -2354,7 +2381,7 @@ static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, struct fib6_node *fn; struct fib6_info *rt = NULL; struct fib6_table *table; - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX); table = fib6_get_table(dev_net(dev), tb_id); if (!table) @@ -5170,6 +5197,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif + array[DEVCONF_ACCEPT_RA_RT_TABLE] = cnf->accept_ra_rt_table; array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD @@ -6346,6 +6374,13 @@ static const struct ctl_table addrconf_sysctl[] = { }, #endif #endif + { + .procname = "accept_ra_rt_table", + .data = &ipv6_devconf.accept_ra_rt_table, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "proxy_ndp", .data = &ipv6_devconf.proxy_ndp, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index abcb5ae77319..cb5507d6d439 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3468,8 +3468,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net, const struct in6_addr *gwaddr, struct net_device *dev) { - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; - int ifindex = dev->ifindex; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO); struct fib6_node *fn; struct fib6_info *rt = NULL; struct fib6_table *table; @@ -3484,7 +3483,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net, goto out; for_each_fib6_node_rt_rcu(fn) { - if (rt->fib6_nh.nh_dev->ifindex != ifindex) + if (rt->fib6_nh.nh_dev->ifindex != dev->ifindex) continue; if ((rt->fib6_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) continue; @@ -3518,7 +3517,7 @@ static struct fib6_info *rt6_add_route_info(struct net *net, .fc_nlinfo.nl_net = net, }; - cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, + cfg.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO), cfg.fc_dst = *prefix; cfg.fc_gateway = *gwaddr; @@ -3536,7 +3535,7 @@ struct fib6_info *rt6_get_dflt_router(struct net *net, const struct in6_addr *addr, struct net_device *dev) { - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_MAIN); struct fib6_info *rt; struct fib6_table *table; @@ -3563,7 +3562,7 @@ struct fib6_info *rt6_add_dflt_router(struct net *net, unsigned int pref) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_DFLT), .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | @@ -3588,47 +3587,21 @@ struct fib6_info *rt6_add_dflt_router(struct net *net, return rt6_get_dflt_router(net, gwaddr, dev); } -static void __rt6_purge_dflt_routers(struct net *net, - struct fib6_table *table) +int rt6_addrconf_purge(struct fib6_info *rt, void *arg) { - struct fib6_info *rt; - -restart: - rcu_read_lock(); - for_each_fib6_node_rt_rcu(&table->tb6_root) { - struct net_device *dev = fib6_info_nh_dev(rt); - struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; + struct net_device *dev = fib6_info_nh_dev(rt); + struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; - if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && - (!idev || idev->cnf.accept_ra != 2) && - fib6_info_hold_safe(rt)) { - rcu_read_unlock(); - ip6_del_rt(net, rt); - goto restart; - } - } - rcu_read_unlock(); + if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!idev || idev->cnf.accept_ra != 2)) + return -1; - table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; + return 0; } void rt6_purge_dflt_routers(struct net *net) { - struct fib6_table *table; - struct hlist_head *head; - unsigned int h; - - rcu_read_lock(); - - for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { - head = &net->ipv6.fib_table_hash[h]; - hlist_for_each_entry_rcu(table, head, tb6_hlist) { - if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) - __rt6_purge_dflt_routers(net, table); - } - } - - rcu_read_unlock(); + fib6_clean_all(net, rt6_addrconf_purge, NULL); } static void rtmsg_to_fib6_config(struct net *net, From 0092a68e015a05fa4fd74cca73279541e72fb31f Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 4 Dec 2008 17:37:05 -0800 Subject: [PATCH 0452/1276] ANDROID: net: rfkill: Introduce CONFIG_RFKILL_PM and use instead of CONFIG_PM to power down Some platforms do not want to power down rfkill devices on suspend. Change-Id: I62a11630521c636d54a4a02ab9037a43435925f5 Signed-off-by: Nick Pelly [AmitP: Folded following android-4.9 commit changes into this patch faad2b874fea ("rfkill: fix unused function warning")] Signed-off-by: Amit Pundir --- net/rfkill/Kconfig | 5 +++++ net/rfkill/core.c | 11 +++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 060600b03fad..7c33c8bb2cd9 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -10,6 +10,11 @@ menuconfig RFKILL To compile this driver as a module, choose M here: the module will be called rfkill. +config RFKILL_PM + bool "Power off on suspend" + depends on RFKILL && PM + default y + # LED trigger support config RFKILL_LEDS bool diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 1355f5ca8d22..f0a061c11a40 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -870,8 +870,7 @@ void rfkill_resume_polling(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_resume_polling); -#ifdef CONFIG_PM_SLEEP -static int rfkill_suspend(struct device *dev) +static __maybe_unused int rfkill_suspend(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); @@ -881,7 +880,7 @@ static int rfkill_suspend(struct device *dev) return 0; } -static int rfkill_resume(struct device *dev) +static __maybe_unused int rfkill_resume(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); bool cur; @@ -901,17 +900,13 @@ static int rfkill_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume); -#define RFKILL_PM_OPS (&rfkill_pm_ops) -#else -#define RFKILL_PM_OPS NULL -#endif static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_groups = rfkill_dev_groups, .dev_uevent = rfkill_dev_uevent, - .pm = RFKILL_PM_OPS, + .pm = IS_ENABLED(CONFIG_RFKILL_PM) ? &rfkill_pm_ops : NULL, }; bool rfkill_blocked(struct rfkill *rfkill) From b62666b9c6e627b0fa49229c22a8277f4fd2c465 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 24 Jun 2014 09:36:50 -0700 Subject: [PATCH 0453/1276] ANDROID: net: wireless: Decrease scan entry expiration to avoid stall results Change-Id: I0e23ce45d78d7c17633670973f49943a5ed6032d Signed-off-by: Dmitry Shmidt --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d0e7472dd9fd..3391c14ee3a1 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -71,7 +71,7 @@ module_param(bss_entries_limit, int, 0644); MODULE_PARM_DESC(bss_entries_limit, "limit to number of scan BSS entries (per wiphy, default 1000)"); -#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { From 80c4a94f6b22d3723282c5d2ae30c07b7fd315b1 Mon Sep 17 00:00:00 2001 From: Jimmy Perchet Date: Mon, 9 May 2016 10:32:04 -0700 Subject: [PATCH 0454/1276] ANDROID: net: wireless: wlcore: Disable filtering in AP role When you configure (set it up) a STA interface, the driver install a multicast filter. This is normal behavior, when one application subscribe to multicast address the filter is updated. When Access Point interface is configured, there is no filter installation and the "filter update" path is disabled in the driver. The problem happens when you switch an interface from STA type to AP type. The filter is installed but there are no means to update it. Change-Id: Ied22323af831575303abd548574918baa9852dd0 Signed-off-by: Dmitry Shmidt Signed-off-by: Amit Pundir --- drivers/net/wireless/ti/wlcore/init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 58898b99d3f7..145e10a8be55 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -549,6 +549,11 @@ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; + /* Disable filtering */ + ret = wl1271_acx_group_address_tbl(wl, wlvif, false, NULL, 0); + if (ret < 0) + return ret; + ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); if (ret < 0) return ret; From b4133fd8f7027c370d32c85ddd00ad0838cf8505 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 May 2017 23:54:04 +0900 Subject: [PATCH 0455/1276] ANDROID: net: xfrm: make PF_KEY SHA256 use RFC-compliant truncation. When using the PF_KEY interface, SHA-256 hashes are hardcoded to use 96-bit truncation. This is a violation of RFC4868, which specifies 128-bit truncation, but will not be fixed upstream due to backwards compatibility concerns and because the PF_KEY interface is deprecated in favour of netlink XFRM (which allows the app to specify an arbitrary truncation length). Change the hardcoded truncation length from 96 to 128 so that PF_KEY apps such as racoon will work with standards-compliant VPN servers. Bug: 34114242 Change-Id: Ie46bff4b6358f18117d0be241171d677d31d33f7 Signed-off-by: Lorenzo Colitti --- net/xfrm/xfrm_algo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 44ac85fe2bc9..d0ca0dbf494e 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -241,7 +241,7 @@ static struct xfrm_algo_desc aalg_list[] = { .uinfo = { .auth = { - .icv_truncbits = 96, + .icv_truncbits = 128, .icv_fullbits = 256, } }, From 9c6474bf6219757e356218a34866281b10c21eff Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 21 Jun 2011 11:14:49 -0700 Subject: [PATCH 0456/1276] ANDROID: netfilter: xt_quota2: adding the original quota2 from xtables-addons The original xt_quota in the kernel is plain broken: - counts quota at a per CPU level (was written back when ubiquitous SMP was just a dream) - provides no way to count across IPV4/IPV6. This patch is the original unaltered code from: http://sourceforge.net/projects/xtables-addons at commit e84391ce665cef046967f796dd91026851d6bbf3 Change-Id: I19d49858840effee9ecf6cff03c23b45a97efdeb Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_quota2.h | 25 +++ net/netfilter/xt_quota2.c | 274 ++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 include/linux/netfilter/xt_quota2.h create mode 100644 net/netfilter/xt_quota2.c diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h new file mode 100644 index 000000000000..eadc6903314e --- /dev/null +++ b/include/linux/netfilter/xt_quota2.h @@ -0,0 +1,25 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 1 << 0, + XT_QUOTA_GROW = 1 << 1, + XT_QUOTA_PACKET = 1 << 2, + XT_QUOTA_NO_CHANGE = 1 << 3, + XT_QUOTA_MASK = 0x0F, +}; + +struct xt_quota_counter; + +struct xt_quota_mtinfo2 { + char name[15]; + u_int8_t flags; + + /* Comparison-invariant */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_counter *master __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c new file mode 100644 index 000000000000..4857008f1eb0 --- /dev/null +++ b/net/netfilter/xt_quota2.c @@ -0,0 +1,274 @@ +/* + * xt_quota2 - enhanced xt_quota that can count upwards and in packets + * as a minimal accounting match. + * by Jan Engelhardt , 2008 + * + * Originally based on xt_quota.c: + * netfilter module to enforce network quotas + * Sam Johnston + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either + * version 2 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include "xt_quota2.h" +#include "compat_xtables.h" + +/** + * @lock: lock to protect quota writers from each other + */ +struct xt_quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; + struct proc_dir_entry *procfs_entry; +}; + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + +static struct proc_dir_entry *proc_xt_quota; +static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; +static unsigned int quota_list_uid = 0; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +static int quota_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + struct xt_quota_counter *e = data; + int ret; + + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_counter *e = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + spin_lock_bh(&e->lock); + e->quota = simple_strtoull(buf, NULL, 0); + spin_unlock_bh(&e->lock); + return size; +} + +static struct xt_quota_counter * +q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) +{ + struct xt_quota_counter *e; + unsigned int size; + + /* Do not need all the procfs things for anonymous counters. */ + size = anon ? offsetof(typeof(*e), list) : sizeof(*e); + e = kmalloc(size, GFP_KERNEL); + if (e == NULL) + return NULL; + + e->quota = q->quota; + spin_lock_init(&e->lock); + if (!anon) { + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strncpy(e->name, q->name, sizeof(e->name)); + } + return e; +} + +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct xt_quota_counter * +q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct xt_quota_counter *e; + + if (*q->name == '\0') + return q2_new_counter(q, true); + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + return e; + } + + e = q2_new_counter(q, false); + if (e == NULL) + goto out; + + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + if (p == NULL || IS_ERR(p)) + goto out; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + p->owner = THIS_MODULE; +#endif + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + list_add_tail(&e->list, &counter_list); + spin_unlock_bh(&counter_list_lock); + return e; + + out: + spin_unlock_bh(&counter_list_lock); + kfree(e); + return NULL; +} + +static int quota_mt2_check(const struct xt_mtchk_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + + if (q->flags & ~XT_QUOTA_MASK) + return -EINVAL; + + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '.' || strchr(q->name, '/') != NULL) { + printk(KERN_ERR "xt_quota.3: illegal name\n"); + return -EINVAL; + } + + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); + return -ENOMEM; + } + + return 0; +} + +static void quota_mt2_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + struct xt_quota_counter *e = q->master; + + if (*q->name == '\0') { + kfree(e); + return; + } + + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + remove_proc_entry(e->name, proc_xt_quota); + spin_unlock_bh(&counter_list_lock); + kfree(e); +} + +static bool +quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; + struct xt_quota_counter *e = q->master; + bool ret = q->flags & XT_QUOTA_INVERT; + + spin_lock_bh(&e->lock); + if (q->flags & XT_QUOTA_GROW) { + /* + * While no_change is pointless in "grow" mode, we will + * implement it here simply to have a consistent behavior. + */ + if (!(q->flags & XT_QUOTA_NO_CHANGE)) { + e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + q->quota = e->quota; + } + ret = true; + } else { + if (e->quota >= skb->len) { + if (!(q->flags & XT_QUOTA_NO_CHANGE)) + e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + ret = !ret; + } else { + /* we do not allow even small packets from now on */ + e->quota = 0; + } + q->quota = e->quota; + } + spin_unlock_bh(&e->lock); + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV4, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + + proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net__proc_net); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net__proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); From 4d33aa3058718b3be98292c63b73592373c6567b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH 0457/1276] ANDROID: netfilter: xt_quota2: fixup the quota2, and enable. The xt_quota2 came from http://sourceforge.net/projects/xtables-addons/develop It needed tweaking for it to compile within the kernel tree. Fixed kmalloc() and create_proc_entry() invocations within a non-interruptible context. Removed useless copying of current quota back to the iptable's struct matchinfo: - those are per CPU: they will change randomly based on which cpu gets to update the value. - they prevent matching a rule: e.g. -A chain -m quota2 --name q1 --quota 123 can't be followed by -D chain -m quota2 --name q1 --quota 123 as the 123 will be compared to the struct matchinfo's quota member. Use the NETLINK NETLINK_NFLOG family to log a single message when the quota limit is reached. It uses the same packet type as ipt_ULOG, but - never copies skb data, - uses 112 as the event number (ULOG's +1) It doesn't log if the module param "event_num" is 0. Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492 Signed-off-by: JP Abgrall [AmitP: Fix quota2_log() call and use ktime directly to align with upstream commits 2456e8553544 ("ktime: Get rid of the union") and 613dbd95723a ("netfilter: x_tables: move hook state into xt_action_param structure"). Also folded following android-4.9 commit changes into this patch eb6aba2a14b9 ("ANDROID: netfilter: xt_quota2: 3.10 fixes.") Parts of 85a2eb5b48fc ("ANDROID: netfilter: xt_qtaguid: 64-bit warning fixes") 89f9044e826c ("ANDROID: net: kuid/kguid build fixes") 60d4c172c5cd ("ANDROID: netfilter: xt_quota2: make quota2_log work well")] Signed-off-by: Amit Pundir --- net/netfilter/Kconfig | 23 +++++ net/netfilter/Makefile | 1 + net/netfilter/xt_quota2.c | 211 ++++++++++++++++++++++++++++++-------- 3 files changed, 193 insertions(+), 42 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f61c306de1d0..4bde51a5902e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1460,6 +1460,29 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 16895e045b66..9c87ed55eba7 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -191,6 +191,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb0..24b774263aa6 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -12,14 +12,37 @@ * version 2 of the License, as published by the Free Software Foundation. */ #include +#include #include #include #include #include +#include #include -#include "xt_quota2.h" -#include "compat_xtables.h" +#include + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* For compatibility, these definitions are copied from the + * deprecated header file */ +#define ULOG_MAC_LEN 80 +#define ULOG_PREFIX_LEN 32 + +/* Format of the ULOG packets passed through netlink */ +typedef struct ulog_packet_msg { + unsigned long mark; + long timestamp_sec; + long timestamp_usec; + unsigned int hook; + char indev_name[IFNAMSIZ]; + char outdev_name[IFNAMSIZ]; + size_t data_len; + char prefix[ULOG_PREFIX_LEN]; + unsigned char mac_len; + unsigned char mac[ULOG_MAC_LEN]; + unsigned char payload[0]; +} ulog_packet_msg_t; +#endif /** * @lock: lock to protect quota writers from each other @@ -33,33 +56,105 @@ struct xt_quota_counter { struct proc_dir_entry *procfs_entry; }; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + static LIST_HEAD(counter_list); static DEFINE_SPINLOCK(counter_list_lock); static struct proc_dir_entry *proc_xt_quota; static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; -static unsigned int quota_list_uid = 0; -static unsigned int quota_list_gid = 0; +static kuid_t quota_list_uid = KUIDT_INIT(0); +static kgid_t quota_list_gid = KGIDT_INIT(0); module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); -module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); -module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); -static int quota_proc_read(char *page, char **start, off_t offset, - int count, int *eof, void *data) +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) { - struct xt_quota_counter *e = data; - int ret; + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm), 0); + if (!nlh) { + pr_err("xt_quota2: nlmsg_put failed\n"); + kfree_skb(log_skb); + return; + } + pm = nlmsg_data(nlh); + if (skb->tstamp == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + +static ssize_t quota_proc_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; spin_lock_bh(&e->lock); - ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota); spin_unlock_bh(&e->lock); - return ret; + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int quota_proc_write(struct file *file, const char __user *input, - unsigned long size, void *data) +static ssize_t quota_proc_write(struct file *file, const char __user *input, + size_t size, loff_t *ppos) { - struct xt_quota_counter *e = data; + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); char buf[sizeof("18446744073709551616")]; if (size > sizeof(buf)) @@ -74,6 +169,12 @@ static int quota_proc_write(struct file *file, const char __user *input, return size; } +static const struct file_operations q2_counter_fops = { + .read = quota_proc_read, + .write = quota_proc_write, + .llseek = default_llseek, +}; + static struct xt_quota_counter * q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) { @@ -91,7 +192,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) if (!anon) { INIT_LIST_HEAD(&e->list); atomic_set(&e->ref, 1); - strncpy(e->name, q->name, sizeof(e->name)); + strlcpy(e->name, q->name, sizeof(e->name)); } return e; } @@ -104,42 +205,52 @@ static struct xt_quota_counter * q2_get_counter(const struct xt_quota_mtinfo2 *q) { struct proc_dir_entry *p; - struct xt_quota_counter *e; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; if (*q->name == '\0') return q2_new_counter(q, true); + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + spin_lock_bh(&counter_list_lock); list_for_each_entry(e, &counter_list, list) if (strcmp(e->name, q->name) == 0) { atomic_inc(&e->ref); spin_unlock_bh(&counter_list_lock); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); return e; } + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); + list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ + spin_unlock_bh(&counter_list_lock); - e = q2_new_counter(q, false); - if (e == NULL) - goto out; + /* create_proc_entry() is not spin_lock happy */ + p = e->procfs_entry = proc_create_data(e->name, quota_list_perms, + proc_xt_quota, &q2_counter_fops, e); - p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, - proc_xt_quota); - if (p == NULL || IS_ERR(p)) + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); goto out; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) - p->owner = THIS_MODULE; -#endif - p->data = e; - p->read_proc = quota_proc_read; - p->write_proc = quota_proc_write; - p->uid = quota_list_uid; - p->gid = quota_list_gid; - list_add_tail(&e->list, &counter_list); - spin_unlock_bh(&counter_list_lock); + } + proc_set_user(p, quota_list_uid, quota_list_gid); return e; out: - spin_unlock_bh(&counter_list_lock); kfree(e); return NULL; } @@ -148,6 +259,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) { struct xt_quota_mtinfo2 *q = par->matchinfo; + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + if (q->flags & ~XT_QUOTA_MASK) return -EINVAL; @@ -203,7 +316,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) */ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; - q->quota = e->quota; } ret = true; } else { @@ -212,10 +324,17 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(xt_hooknum(par), + skb, + xt_in(par), + xt_out(par), + q->name); + } /* we do not allow even small packets from now on */ e->quota = 0; } - q->quota = e->quota; } spin_unlock_bh(&e->lock); return ret; @@ -228,7 +347,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV4, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -238,7 +357,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV6, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -247,21 +366,29 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; + pr_debug("xt_quota2: init()"); + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); + if (!nflognl) + return -ENOMEM; +#endif - proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); if (proc_xt_quota == NULL) return -EACCES; ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); if (ret < 0) - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); return ret; } static void __exit quota_mt2_exit(void) { xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); } module_init(quota_mt2_init); From 88f0bae7f568caea6034ca222ea20e0bc2cf249e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 26 Apr 2012 23:28:35 -0700 Subject: [PATCH 0458/1276] ANDROID: netfilter: xt_IDLETIMER: Add new netlink msg type Send notifications when the label becomes active after an idle period. Send netlink message notifications in addition to sysfs notifications. Using a uevent with subsystem=xt_idletimer INTERFACE=... STATE={active,inactive} This is backport from common android-3.0 commit: beb914e987cbbd368988d2b94a6661cb907c4d5a with uevent support instead of a new netlink message type. Change-Id: I31677ef00c94b5f82c8457e5bf9e5e584c23c523 Signed-off-by: Ashish Sharma Signed-off-by: JP Abgrall --- include/uapi/linux/netfilter/xt_IDLETIMER.h | 8 +++ net/netfilter/xt_IDLETIMER.c | 78 +++++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h index 3c586a19baea..c82a1c1d53ec 100644 --- a/include/uapi/linux/netfilter/xt_IDLETIMER.h +++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h @@ -5,6 +5,7 @@ * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and forward-ported to 2.6.34 @@ -33,12 +34,19 @@ #include #define MAX_IDLETIMER_LABEL_SIZE 28 +#define NLMSG_MAX_SIZE 64 + +#define NL_EVENT_TYPE_INACTIVE 0 +#define NL_EVENT_TYPE_ACTIVE 1 struct idletimer_tg_info { __u32 timeout; char label[MAX_IDLETIMER_LABEL_SIZE]; + /* Use netlink messages for notification in addition to sysfs */ + __u8 send_nl_msg; + /* for kernel module internal use only */ struct idletimer_tg *timer __attribute__((aligned(8))); }; diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 5ee859193783..c76e5d4b088a 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -5,6 +5,7 @@ * After timer expires a kevent will be sent. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and reworked for upstream inclusion @@ -38,8 +39,10 @@ #include #include #include +#include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -56,6 +59,8 @@ struct idletimer_tg { struct idletimer_tg_attr attr; unsigned int refcnt; + bool send_nl_msg; + bool active; }; static LIST_HEAD(idletimer_tg_list); @@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; +static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +{ + char iface_msg[NLMSG_MAX_SIZE]; + char state_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, NULL }; + int res; + + res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", + iface); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", + timer->active ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); + return; + + +} + static struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) { @@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, { struct idletimer_tg *timer; unsigned long expires = 0; + unsigned long now = jiffies; mutex_lock(&list_mutex); @@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, mutex_unlock(&list_mutex); - if (time_after(expires, jiffies)) + if (time_after(expires, now)) return sprintf(buf, "%u\n", - jiffies_to_msecs(expires - jiffies) / 1000); + jiffies_to_msecs(expires - now) / 1000); - return sprintf(buf, "0\n"); + if (timer->send_nl_msg) + return sprintf(buf, "0 %d\n", + jiffies_to_msecs(now - expires) / 1000); + else + return sprintf(buf, "0\n"); } static void idletimer_tg_work(struct work_struct *work) @@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) work); sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); + + if (timer->send_nl_msg) + notify_netlink_uevent(timer->attr.attr.name, timer); } static void idletimer_tg_expired(struct timer_list *t) @@ -113,6 +152,7 @@ static void idletimer_tg_expired(struct timer_list *t) pr_debug("timer %s expired\n", timer->attr.attr.name); + timer->active = false; schedule_work(&timer->work); } @@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) timer_setup(&info->timer->timer, idletimer_tg_expired, 0); info->timer->refcnt = 1; + info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; + info->timer->active = true; INIT_WORK(&info->timer->work, idletimer_tg_work); @@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, const struct xt_action_param *par) { const struct idletimer_tg_info *info = par->targinfo; + unsigned long now = jiffies; pr_debug("resetting timer %s, timeout period %u\n", info->label, info->timeout); BUG_ON(!info->timer); + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", + info->label, info->timer->timer.expires, now); + } + + /* TODO: Avoid modifying timers on each packet */ mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); return XT_CONTINUE; } @@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; + unsigned long now = jiffies; - pr_debug("checkentry targinfo%s\n", info->label); + pr_debug("checkentry targinfo %s\n", info->label); if (info->timeout == 0) { pr_debug("timeout value is zero\n"); @@ -207,8 +260,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + info->timer->timer.expires, now); + } + mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); @@ -222,6 +283,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -244,7 +306,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) kfree(info->timer); } else { pr_debug("decreased refcnt of timer %s to %u\n", - info->label, info->timer->refcnt); + info->label, info->timer->refcnt); } mutex_unlock(&list_mutex); @@ -252,6 +314,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) static struct xt_target idletimer_tg __read_mostly = { .name = "IDLETIMER", + .revision = 1, .family = NFPROTO_UNSPEC, .target = idletimer_tg_target, .targetsize = sizeof(struct idletimer_tg_info), @@ -318,3 +381,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); From ee0b238fada50a8196afa964b7b6cb3f9b7f7ef9 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 25 Mar 2014 16:43:28 -0700 Subject: [PATCH 0459/1276] ANDROID: netfilter: xt_IDLETIMER: time-stamp and suspend/resume handling. Message notifications contains an additional timestamp field in nano seconds. The expiry time for the timers are modified during suspend/resume. If timer was supposed to expire while the system is suspended then a notification is sent when it resumes with the timestamp of the scheduled expiry. Removes the race condition for multiple work scheduled. Bug: 13247811 Change-Id: I752c5b00225fe7085482819f975cc0eb5af89bff Signed-off-by: Ruchi Kandoi [AmitP: Folded following android-4.9 commit changes into this patch 13e257eaa624 ("nf: Remove compilation error caused by e8430cbed3ef15fdb1ac26cfd020e010aa5f1c35")] Signed-off-by: Amit Pundir --- net/netfilter/xt_IDLETIMER.c | 170 +++++++++++++++++++++++++++++++---- 1 file changed, 152 insertions(+), 18 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index c76e5d4b088a..25a021a9d8ef 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -42,6 +42,11 @@ #include #include #include +#include +#include +#include +#include +#include #include struct idletimer_tg_attr { @@ -58,22 +63,65 @@ struct idletimer_tg { struct kobject *kobj; struct idletimer_tg_attr attr; + struct timespec delayed_timer_trigger; + struct timespec last_modified_timer; + struct timespec last_suspend_time; + struct notifier_block pm_nb; + + int timeout; unsigned int refcnt; + bool work_pending; bool send_nl_msg; bool active; }; static LIST_HEAD(idletimer_tg_list); static DEFINE_MUTEX(list_mutex); +static DEFINE_SPINLOCK(timestamp_lock); static struct kobject *idletimer_tg_kobj; +static bool check_for_delayed_trigger(struct idletimer_tg *timer, + struct timespec *ts) +{ + bool state; + struct timespec temp; + spin_lock_bh(×tamp_lock); + timer->work_pending = false; + if ((ts->tv_sec - timer->last_modified_timer.tv_sec) > timer->timeout || + timer->delayed_timer_trigger.tv_sec != 0) { + state = false; + temp.tv_sec = timer->timeout; + temp.tv_nsec = 0; + if (timer->delayed_timer_trigger.tv_sec != 0) { + temp = timespec_add(timer->delayed_timer_trigger, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + timer->delayed_timer_trigger.tv_sec = 0; + timer->work_pending = true; + schedule_work(&timer->work); + } else { + temp = timespec_add(timer->last_modified_timer, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + } + } else { + state = timer->active; + } + spin_unlock_bh(×tamp_lock); + return state; +} + static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) { char iface_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, NULL }; + char timestamp_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; int res; + struct timespec ts; + uint64_t time_ns; + bool state; res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", iface); @@ -81,12 +129,24 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); return; } + + get_monotonic_boottime(&ts); + state = check_for_delayed_trigger(timer, &ts); res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", - timer->active ? "active" : "inactive"); + state ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { pr_err("message too long (%d)", res); return; } + + time_ns = timespec_to_ns(&ts); + res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); + if (NLMSG_MAX_SIZE <= res) { + timestamp_msg[0] = '\0'; + pr_err("message too long (%d)", res); + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -151,9 +211,55 @@ static void idletimer_tg_expired(struct timer_list *t) struct idletimer_tg *timer = from_timer(timer, t, timer); pr_debug("timer %s expired\n", timer->attr.attr.name); - + spin_lock_bh(×tamp_lock); timer->active = false; + timer->work_pending = true; schedule_work(&timer->work); + spin_unlock_bh(×tamp_lock); +} + +static int idletimer_resume(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct timespec ts; + unsigned long time_diff, now = jiffies; + struct idletimer_tg *timer = container_of(notifier, + struct idletimer_tg, pm_nb); + if (!timer) + return NOTIFY_DONE; + switch (pm_event) { + case PM_SUSPEND_PREPARE: + get_monotonic_boottime(&timer->last_suspend_time); + break; + case PM_POST_SUSPEND: + spin_lock_bh(×tamp_lock); + if (!timer->active) { + spin_unlock_bh(×tamp_lock); + break; + } + /* since jiffies are not updated when suspended now represents + * the time it would have suspended */ + if (time_after(timer->timer.expires, now)) { + get_monotonic_boottime(&ts); + ts = timespec_sub(ts, timer->last_suspend_time); + time_diff = timespec_to_jiffies(&ts); + if (timer->timer.expires > (time_diff + now)) { + mod_timer_pending(&timer->timer, + (timer->timer.expires - time_diff)); + } else { + del_timer(&timer->timer); + timer->timer.expires = 0; + timer->active = false; + timer->work_pending = true; + schedule_work(&timer->work); + } + } + spin_unlock_bh(×tamp_lock); + break; + default: + break; + } + return NOTIFY_DONE; } static int idletimer_tg_create(struct idletimer_tg_info *info) @@ -187,6 +293,18 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->refcnt = 1; info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; info->timer->active = true; + info->timer->timeout = info->timeout; + + info->timer->delayed_timer_trigger.tv_sec = 0; + info->timer->delayed_timer_trigger.tv_nsec = 0; + info->timer->work_pending = false; + get_monotonic_boottime(&info->timer->last_modified_timer); + + info->timer->pm_nb.notifier_call = idletimer_resume; + ret = register_pm_notifier(&info->timer->pm_nb); + if (ret) + printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n", + __func__, ret); INIT_WORK(&info->timer->work, idletimer_tg_work); @@ -203,6 +321,34 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) return ret; } +static void reset_timer(const struct idletimer_tg_info *info) +{ + unsigned long now = jiffies; + struct idletimer_tg *timer = info->timer; + bool timer_prev; + + spin_lock_bh(×tamp_lock); + timer_prev = timer->active; + timer->active = true; + /* timer_prev is used to guard overflow problem in time_before*/ + if (!timer_prev || time_before(timer->timer.expires, now)) { + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + timer->timer.expires, now); + /* checks if there is a pending inactive notification*/ + if (timer->work_pending) + timer->delayed_timer_trigger = timer->last_modified_timer; + else { + timer->work_pending = true; + schedule_work(&timer->work); + } + } + + get_monotonic_boottime(&timer->last_modified_timer); + mod_timer(&timer->timer, + msecs_to_jiffies(info->timeout * 1000) + now); + spin_unlock_bh(×tamp_lock); +} + /* * The actual xt_tables plugin. */ @@ -226,9 +372,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + now); - + reset_timer(info); return XT_CONTINUE; } @@ -236,7 +380,6 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; - unsigned long now = jiffies; pr_debug("checkentry targinfo %s\n", info->label); @@ -260,17 +403,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; - info->timer->active = true; - - if (time_before(info->timer->timer.expires, now)) { - schedule_work(&info->timer->work); - pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", - info->timer->timer.expires, now); - } - - mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + now); - + reset_timer(info); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { @@ -302,6 +435,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) del_timer_sync(&info->timer->timer); cancel_work_sync(&info->timer->work); sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); + unregister_pm_notifier(&info->timer->pm_nb); kfree(info->timer->attr.attr.name); kfree(info->timer); } else { From 728c058a495e697734dae53c5164dc9abed93e07 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 23 Apr 2015 12:09:09 -0700 Subject: [PATCH 0460/1276] ANDROID: netfilter: xt_IDLETIMER: Adds the uid field in the msg Message notifications contains an additional uid field. This field represents the uid that was responsible for waking the radio. And hence it is present only in notifications stating that the radio is now active. Change-Id: I18fc73eada512e370d7ab24fc9f890845037b729 Signed-off-by: Ruchi Kandoi Bug: 20264396 [AmitP: Folded following android-4.9 commit changes into this patch 22ea73dee036 ("nf: IDLETIMER: Fix broken uid field in the msg")] Signed-off-by: Amit Pundir --- net/netfilter/xt_IDLETIMER.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 25a021a9d8ef..b8ea93603b83 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -48,6 +48,7 @@ #include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -73,6 +74,7 @@ struct idletimer_tg { bool work_pending; bool send_nl_msg; bool active; + uid_t uid; }; static LIST_HEAD(idletimer_tg_list); @@ -117,7 +119,8 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) char iface_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; char timestamp_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; + char uid_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, uid_msg, NULL }; int res; struct timespec ts; uint64_t time_ns; @@ -140,6 +143,16 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) return; } + if (state) { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID=%u", timer->uid); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } else { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID="); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } + time_ns = timespec_to_ns(&ts); res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); if (NLMSG_MAX_SIZE <= res) { @@ -147,7 +160,8 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); } - pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + pr_debug("putting nlmsg: <%s> <%s> <%s> <%s>\n", iface_msg, state_msg, + timestamp_msg, uid_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -298,6 +312,7 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->delayed_timer_trigger.tv_sec = 0; info->timer->delayed_timer_trigger.tv_nsec = 0; info->timer->work_pending = false; + info->timer->uid = 0; get_monotonic_boottime(&info->timer->last_modified_timer); info->timer->pm_nb.notifier_call = idletimer_resume; @@ -321,7 +336,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) return ret; } -static void reset_timer(const struct idletimer_tg_info *info) +static void reset_timer(const struct idletimer_tg_info *info, + struct sk_buff *skb) { unsigned long now = jiffies; struct idletimer_tg *timer = info->timer; @@ -334,6 +350,13 @@ static void reset_timer(const struct idletimer_tg_info *info) if (!timer_prev || time_before(timer->timer.expires, now)) { pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", timer->timer.expires, now); + + /* Stores the uid resposible for waking up the radio */ + if (skb && (skb->sk)) { + timer->uid = from_kuid_munged(current_user_ns(), + sock_i_uid(skb->sk)); + } + /* checks if there is a pending inactive notification*/ if (timer->work_pending) timer->delayed_timer_trigger = timer->last_modified_timer; @@ -372,7 +395,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - reset_timer(info); + reset_timer(info, skb); return XT_CONTINUE; } @@ -403,7 +426,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; - reset_timer(info); + reset_timer(info, NULL); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { From 5ebea489d44cf578311e2c075e443b729c932076 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Thu, 10 Nov 2016 19:36:15 -0700 Subject: [PATCH 0461/1276] ANDROID: netfilter: xt_IDLETIMER: Fix use after free condition during work schedule_work(&timer->work) appears to be called after cancel_work_sync(&info->timer->work) is completed. Work can be scheduled from the PM_POST_SUSPEND notification event even after cancel_work_sync is called. Call stack -004|notify_netlink_uevent( | [X19] timer = 0xFFFFFFC0A5DFC780 -> ( | ... | [NSD:0xFFFFFFC0A5DFC800] kobj = 0x6B6B6B6B6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC868] timeout = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC86C] refcnt = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC870] work_pending = 0x6B, | [NSD:0xFFFFFFC0A5DFC871] send_nl_msg = 0x6B, | [NSD:0xFFFFFFC0A5DFC872] active = 0x6B, | [NSD:0xFFFFFFC0A5DFC874] uid = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC878] suspend_time_valid = 0x6B)) -005|idletimer_tg_work( -006|__read_once_size(inline) -006|static_key_count(inline) -006|static_key_false(inline) -006|trace_workqueue_execute_end(inline) -006|process_one_work( -007|worker_thread( -008|kthread( -009|ret_from_fork(asm) ---|end of frame Force any pending idletimer_tg_work() to complete before freeing the associated work struct and after unregistering to the pm_notifier callback. Change-Id: I4c5f0a1c142f7d698c092cf7bcafdb0f9fbaa9c1 Signed-off-by: Subash Abhinov Kasiviswanathan --- net/netfilter/xt_IDLETIMER.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index b8ea93603b83..a385487811ad 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -456,9 +456,9 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) list_del(&info->timer->entry); del_timer_sync(&info->timer->timer); - cancel_work_sync(&info->timer->work); sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); unregister_pm_notifier(&info->timer->pm_nb); + cancel_work_sync(&info->timer->work); kfree(info->timer->attr.attr.name); kfree(info->timer); } else { From 5ab69d7ba2c57ad1534d339e7a05df71aad32910 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Wed, 2 Nov 2016 11:56:40 -0600 Subject: [PATCH 0462/1276] ANDROID: netfilter: xt_IDLETIMER: Use fullsock when querying uid sock_i_uid() acquires the sk_callback_lock which does not exist for sockets in TCP_NEW_SYN_RECV state. This results in errors showing up as spinlock bad magic. Fix this by looking for the full sock as suggested by Eric. Callstack for reference - -003|rwlock_bug -004|arch_read_lock -004|do_raw_read_lock -005|raw_read_lock_bh -006|sock_i_uid -007|from_kuid_munged(inline) -007|reset_timer -008|idletimer_tg_target -009|ipt_do_table -010|iptable_mangle_hook -011|nf_iterate -012|nf_hook_slow -013|NF_HOOK_COND(inline) -013|ip_output -014|ip_local_out -015|ip_build_and_send_pkt -016|tcp_v4_send_synack -017|atomic_sub_return(inline) -017|reqsk_put(inline) -017|tcp_conn_request -018|tcp_v4_conn_request -019|tcp_rcv_state_process -020|tcp_v4_do_rcv -021|tcp_v4_rcv -022|ip_local_deliver_finish -023|NF_HOOK_THRESH(inline) -023|NF_HOOK(inline) -023|ip_local_deliver -024|ip_rcv_finish -025|NF_HOOK_THRESH(inline) -025|NF_HOOK(inline) -025|ip_rcv -026|deliver_skb(inline) -026|deliver_ptype_list_skb(inline) -026|__netif_receive_skb_core -027|__netif_receive_skb -028|netif_receive_skb_internal -029|netif_receive_skb Change-Id: Ic8f3a3d2d7af31434d1163b03971994e2125d552 Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Eric Dumazet --- net/netfilter/xt_IDLETIMER.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index a385487811ad..673860af4934 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -49,6 +49,7 @@ #include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -354,7 +355,7 @@ static void reset_timer(const struct idletimer_tg_info *info, /* Stores the uid resposible for waking up the radio */ if (skb && (skb->sk)) { timer->uid = from_kuid_munged(current_user_ns(), - sock_i_uid(skb->sk)); + sock_i_uid(skb_to_full_sk(skb))); } /* checks if there is a pending inactive notification*/ From cfdd57b01d5b9220a7cf98a465a28e592ed4201b Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Thu, 17 Aug 2017 13:58:40 -0700 Subject: [PATCH 0463/1276] ANDROID: NFC: st21nfca: Fix memory OOB and leak issues in connectivity events handler Overflow on memcpy is possible in kernel driver for st21nfca's NFC HCI layer when handling connectivity events if aid_len or params_len are bigger than the buffer size. Memory leak is possible when parameter tag is invalid. Bug: 62679581 Signed-off-by: Suren Baghdasaryan --- drivers/nfc/st21nfca/se.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index 4bed9e842db3..acdce231e227 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -322,23 +322,33 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, * AID 81 5 to 16 * PARAMETERS 82 0 to 255 */ - if (skb->len < NFC_MIN_AID_LENGTH + 2 && + if (skb->len < NFC_MIN_AID_LENGTH + 2 || skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) return -EPROTO; + /* + * Buffer should have enough space for at least + * two tag fields + two length fields + aid_len (skb->data[1]) + */ + if (skb->len < skb->data[1] + 4) + return -EPROTO; + transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, skb->len - 2, GFP_KERNEL); transaction->aid_len = skb->data[1]; memcpy(transaction->aid, &skb->data[2], transaction->aid_len); + transaction->params_len = skb->data[transaction->aid_len + 3]; - /* Check next byte is PARAMETERS tag (82) */ + /* Check next byte is PARAMETERS tag (82) and the length field */ if (skb->data[transaction->aid_len + 2] != - NFC_EVT_TRANSACTION_PARAMS_TAG) + NFC_EVT_TRANSACTION_PARAMS_TAG || + skb->len < transaction->aid_len + transaction->params_len + 4) { + devm_kfree(dev, transaction); return -EPROTO; + } - transaction->params_len = skb->data[transaction->aid_len + 3]; memcpy(transaction->params, skb->data + transaction->aid_len + 4, transaction->params_len); From ac76f9cfd0d951391597d0fac987c7e89499b526 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Thu, 2 Feb 2012 22:58:28 -0800 Subject: [PATCH 0464/1276] ANDROID: of: Support CONFIG_CMDLINE_EXTEND config option The old logic assumes CMDLINE_FROM_BOOTLOADER vs. CMDLINE_FORCE and ignores CMDLINE_EXTEND. Here's the old logic: - CONFIG_CMDLINE_FORCE=true CONFIG_CMDLINE - dt bootargs=non-empty: dt bootargs - dt bootargs=empty, @data is non-empty string @data is left unchanged - dt bootargs=empty, @data is empty string CONFIG_CMDLINE (or "" if that's not defined) The new logic is now documented in of_fdt.h and is copied here for reference: - CONFIG_CMDLINE_FORCE=true CONFIG_CMDLINE - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string @data + dt bootargs (even if dt bootargs are empty) - CONFIG_CMDLINE_EXTEND=true, @data is empty string CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty) - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty: dt bootargs - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string @data is left unchanged - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string CONFIG_CMDLINE (or "" if that's not defined) Signed-off-by: Doug Anderson CC: devicetree-discuss@lists.ozlabs.org CC: Grant Likely CC: Benjamin Herrenschmidt CC: Rob Herring Change-Id: I40ace250847f813358125dfcaa8998fd32cf7ea3 Signed-off-by: Colin Cross [AmitP: Folded following android-4.9 commit changes into this patch e820270abb5d ("ANDROID: of: fix CONFIG_CMDLINE_EXTEND") 9a4a74055444 ("ANDROID: of: Fix build warnings")] Signed-off-by: Amit Pundir --- drivers/of/fdt.c | 74 ++++++++++++++++++++++++++++-------------- include/linux/of_fdt.h | 21 ++++++++++++ 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 800ad252cf9c..2aa4261d3e8f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -1072,42 +1072,66 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, return 0; } +/* + * Convert configs to something easy to use in C code + */ +#if defined(CONFIG_CMDLINE_FORCE) +static const int overwrite_incoming_cmdline = 1; +static const int read_dt_cmdline; +static const int concat_cmdline; +#elif defined(CONFIG_CMDLINE_EXTEND) +static const int overwrite_incoming_cmdline; +static const int read_dt_cmdline = 1; +static const int concat_cmdline = 1; +#else /* CMDLINE_FROM_BOOTLOADER */ +static const int overwrite_incoming_cmdline; +static const int read_dt_cmdline = 1; +static const int concat_cmdline; +#endif + +#ifdef CONFIG_CMDLINE +static const char *config_cmdline = CONFIG_CMDLINE; +#else +static const char *config_cmdline = ""; +#endif + int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { - int l; - const char *p; + int l = 0; + const char *p = NULL; + char *cmdline = data; pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); - if (depth != 1 || !data || + if (depth != 1 || !cmdline || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; early_init_dt_check_for_initrd(node); - /* Retrieve command line */ - p = of_get_flat_dt_prop(node, "bootargs", &l); - if (p != NULL && l > 0) - strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); - - /* - * CONFIG_CMDLINE is meant to be a default in case nothing else - * managed to set the command line, unless CONFIG_CMDLINE_FORCE - * is set in which case we override whatever was found earlier. - */ -#ifdef CONFIG_CMDLINE -#if defined(CONFIG_CMDLINE_EXTEND) - strlcat(data, " ", COMMAND_LINE_SIZE); - strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#elif defined(CONFIG_CMDLINE_FORCE) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#else - /* No arguments from boot loader, use kernel's cmdl*/ - if (!((char *)data)[0]) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#endif -#endif /* CONFIG_CMDLINE */ + /* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */ + if (overwrite_incoming_cmdline || !cmdline[0]) + strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE); + + /* Retrieve command line unless forcing */ + if (read_dt_cmdline) + p = of_get_flat_dt_prop(node, "bootargs", &l); + + if (p != NULL && l > 0) { + if (concat_cmdline) { + int cmdline_len; + int copy_len; + strlcat(cmdline, " ", COMMAND_LINE_SIZE); + cmdline_len = strlen(cmdline); + copy_len = COMMAND_LINE_SIZE - cmdline_len - 1; + copy_len = min((int)l, copy_len); + strncpy(cmdline + cmdline_len, p, copy_len); + cmdline[cmdline_len + copy_len] = '\0'; + } else { + strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE)); + } + } pr_debug("Command line is: %s\n", (char*)data); diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index b9cd9ebdf9b9..587c147a2436 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -69,6 +69,27 @@ extern unsigned long of_get_flat_dt_root(void); extern int of_get_flat_dt_size(void); extern uint32_t of_get_flat_dt_phandle(unsigned long node); +/* + * early_init_dt_scan_chosen - scan the device tree for ramdisk and bootargs + * + * The boot arguments will be placed into the memory pointed to by @data. + * That memory should be COMMAND_LINE_SIZE big and initialized to be a valid + * (possibly empty) string. Logic for what will be in @data after this + * function finishes: + * + * - CONFIG_CMDLINE_FORCE=true + * CONFIG_CMDLINE + * - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string + * @data + dt bootargs (even if dt bootargs are empty) + * - CONFIG_CMDLINE_EXTEND=true, @data is empty string + * CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty) + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty: + * dt bootargs + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string + * @data is left unchanged + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string + * CONFIG_CMDLINE (or "" if that's not defined) + */ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, From b9eb81cb630303d682cbe866aaf93d604059f34f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:02:15 -0700 Subject: [PATCH 0465/1276] ANDROID: arm64: copy CONFIG_CMDLINE_EXTEND from ARM Copy the config choice for CONFIG_CMDLINE_EXTEND from arch/arm/Kconfig, including CONFIG_CMDLINE_FROM_BOOTLOADER as the default. These will be used by drivers/of/fdt.c. Change-Id: I8416038498ddf8fc1e99ab06109825eb1492aa7f Signed-off-by: Colin Cross --- arch/arm64/Kconfig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 15d659d62db1..da5bc2c9392e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1239,6 +1239,23 @@ config CMDLINE entering them here. As a minimum, you should specify the the root device (e.g. root=/dev/nfs). +choice + prompt "Kernel command line type" if CMDLINE != "" + default CMDLINE_FROM_BOOTLOADER + +config CMDLINE_FROM_BOOTLOADER + bool "Use bootloader kernel arguments if available" + help + Uses the command-line options passed by the boot loader. If + the boot loader doesn't provide any, the default kernel command + string provided in CMDLINE will be used. + +config CMDLINE_EXTEND + bool "Extend bootloader kernel arguments" + help + The command-line arguments provided by the boot loader will be + appended to the default kernel command string. + config CMDLINE_FORCE bool "Always use the default kernel command string" help @@ -1246,6 +1263,7 @@ config CMDLINE_FORCE loader passes other arguments to the kernel. This is useful if you cannot or don't want to change the command-line options your boot loader passes to the kernel. +endchoice config EFI_STUB bool From f68ca1c734fc1f2ca9e65d7d302ecab6f9d4dda8 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 19 Feb 2014 15:30:47 -0800 Subject: [PATCH 0466/1276] ANDROID: power: wakeup_reason: add an API to log wakeup reasons Add API log_wakeup_reason() and expose it to userspace via sysfs path /sys/kernel/wakeup_reasons/last_resume_reason Change-Id: I81addaf420f1338255c5d0638b0d244a99d777d1 Signed-off-by: Ruchi Kandoi [AmitP: Folded following android-4.9 commit changes into this patch 1135122a192a ("ANDROID: POWER: fix compile warnings in log_wakeup_reason") b4e6247778b0 ("ANDROID: Power: Changes the permission to read only for sysfs file /sys/kernel/wakeup_reasons/last_resume_reason") e13dbc7c69cd ("ANDROID: power: wakeup_reason: rename irq_count to irqcount")] Signed-off-by: Amit Pundir --- include/linux/wakeup_reason.h | 23 ++++++ kernel/power/Makefile | 2 + kernel/power/wakeup_reason.c | 132 ++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 include/linux/wakeup_reason.h create mode 100644 kernel/power/wakeup_reason.c diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h new file mode 100644 index 000000000000..7ce50f0debc4 --- /dev/null +++ b/include/linux/wakeup_reason.h @@ -0,0 +1,23 @@ +/* + * include/linux/wakeup_reason.h + * + * Logs the reason which caused the kernel to resume + * from the suspend mode. + * + * Copyright (C) 2014 Google, Inc. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_WAKEUP_REASON_H +#define _LINUX_WAKEUP_REASON_H + +void log_wakeup_reason(int irq); + +#endif /* _LINUX_WAKEUP_REASON_H */ diff --git a/kernel/power/Makefile b/kernel/power/Makefile index a3f79f0eef36..5c1743d4d8ef 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -15,3 +15,5 @@ obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o + +obj-$(CONFIG_SUSPEND) += wakeup_reason.o diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c new file mode 100644 index 000000000000..1491bc402b05 --- /dev/null +++ b/kernel/power/wakeup_reason.c @@ -0,0 +1,132 @@ +/* + * kernel/power/wakeup_reason.c + * + * Logs the reasons which caused the kernel to resume from + * the suspend mode. + * + * Copyright (C) 2014 Google, Inc. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_WAKEUP_REASON_IRQS 32 +static int irq_list[MAX_WAKEUP_REASON_IRQS]; +static int irqcount; +static struct kobject *wakeup_reason; +static spinlock_t resume_reason_lock; + +static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int irq_no, buf_offset = 0; + struct irq_desc *desc; + spin_lock(&resume_reason_lock); + for (irq_no = 0; irq_no < irqcount; irq_no++) { + desc = irq_to_desc(irq_list[irq_no]); + if (desc && desc->action && desc->action->name) + buf_offset += sprintf(buf + buf_offset, "%d %s\n", + irq_list[irq_no], desc->action->name); + else + buf_offset += sprintf(buf + buf_offset, "%d\n", + irq_list[irq_no]); + } + spin_unlock(&resume_reason_lock); + return buf_offset; +} + +static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason); + +static struct attribute *attrs[] = { + &resume_reason.attr, + NULL, +}; +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +/* + * logs all the wake up reasons to the kernel + * stores the irqs to expose them to the userspace via sysfs + */ +void log_wakeup_reason(int irq) +{ + struct irq_desc *desc; + desc = irq_to_desc(irq); + if (desc && desc->action && desc->action->name) + printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq, + desc->action->name); + else + printk(KERN_INFO "Resume caused by IRQ %d\n", irq); + + spin_lock(&resume_reason_lock); + irq_list[irqcount++] = irq; + spin_unlock(&resume_reason_lock); +} + +/* Detects a suspend and clears all the previous wake up reasons*/ +static int wakeup_reason_pm_event(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + switch (pm_event) { + case PM_SUSPEND_PREPARE: + spin_lock(&resume_reason_lock); + irqcount = 0; + spin_unlock(&resume_reason_lock); + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block wakeup_reason_pm_notifier_block = { + .notifier_call = wakeup_reason_pm_event, +}; + +/* Initializes the sysfs parameter + * registers the pm_event notifier + */ +int __init wakeup_reason_init(void) +{ + int retval; + spin_lock_init(&resume_reason_lock); + retval = register_pm_notifier(&wakeup_reason_pm_notifier_block); + if (retval) + printk(KERN_WARNING "[%s] failed to register PM notifier %d\n", + __func__, retval); + + wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj); + if (!wakeup_reason) { + printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n", + __func__); + return 1; + } + retval = sysfs_create_group(wakeup_reason, &attr_group); + if (retval) { + kobject_put(wakeup_reason); + printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n", + __func__, retval); + } + return 0; +} + +late_initcall(wakeup_reason_init); From 39d7c7fe91c0d7268b683a290e932314c72ed6b3 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 7 Mar 2014 12:54:30 -0800 Subject: [PATCH 0467/1276] ANDROID: power: wakeup_reason: Add guard condition for maximum wakeup reasons Ensure the array for the wakeup reason IRQs does not overflow. Change-Id: Iddc57a3aeb1888f39d4e7b004164611803a4d37c Signed-off-by: Ruchi Kandoi (cherry picked from commit b5ea40cdfcf38296535f931a7e5e7bf47b6fad7f) --- kernel/power/wakeup_reason.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 1491bc402b05..187e4e9105fb 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -78,6 +78,13 @@ void log_wakeup_reason(int irq) printk(KERN_INFO "Resume caused by IRQ %d\n", irq); spin_lock(&resume_reason_lock); + if (irqcount == MAX_WAKEUP_REASON_IRQS) { + spin_unlock(&resume_reason_lock); + printk(KERN_WARNING "Resume caused by more than %d IRQs\n", + MAX_WAKEUP_REASON_IRQS); + return; + } + irq_list[irqcount++] = irq; spin_unlock(&resume_reason_lock); } From 0730434bdf492b648af3b5f6b22f29080d0726ae Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 14 Oct 2014 17:43:21 -0700 Subject: [PATCH 0468/1276] ANDROID: power: wakeup_reason: Avoids bogus error messages for the suspend aborts. Avoids printing bogus error message "tasks refusing to freeze", in cases where pending wakeup source caused the suspend abort. Signed-off-by: Ruchi Kandoi Change-Id: I913ad290f501b31cd536d039834c8d24c6f16928 --- kernel/power/process.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/kernel/power/process.c b/kernel/power/process.c index 7381d49a44db..d76e61606f51 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -85,26 +85,27 @@ static int try_to_freeze_tasks(bool user_only) elapsed = ktime_sub(end, start); elapsed_msecs = ktime_to_ms(elapsed); - if (todo) { + if (wakeup) { pr_cont("\n"); - pr_err("Freezing of tasks %s after %d.%03d seconds " - "(%d tasks refusing to freeze, wq_busy=%d):\n", - wakeup ? "aborted" : "failed", + pr_err("Freezing of tasks aborted after %d.%03d seconds", + elapsed_msecs / 1000, elapsed_msecs % 1000); + } else if (todo) { + pr_cont("\n"); + pr_err("Freezing of tasks failed after %d.%03d seconds" + " (%d tasks refusing to freeze, wq_busy=%d):\n", elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (wq_busy) show_workqueue_state(); - if (!wakeup) { - read_lock(&tasklist_lock); - for_each_process_thread(g, p) { - if (p != current && !freezer_should_skip(p) - && freezing(p) && !frozen(p)) - sched_show_task(p); - } - read_unlock(&tasklist_lock); + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + if (p != current && !freezer_should_skip(p) + && freezing(p) && !frozen(p)) + sched_show_task(p); } + read_unlock(&tasklist_lock); } else { pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, elapsed_msecs % 1000); From 4e42dceae54ea81bd28012e8124e5a2e51be18e1 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 29 Oct 2014 10:36:27 -0700 Subject: [PATCH 0469/1276] ANDROID: power: wakeup_reason: Adds functionality to log the last suspend abort reason. Extends the last_resume_reason to log suspend abort reason. The abort reasons will have "Abort:" appended at the start to distinguish itself from the resume reason. Signed-off-by: Ruchi Kandoi Change-Id: I3207f1844e3d87c706dfc298fb10e1c648814c5f [AmitP: Folded following android-4.9 changes into this patch 00a83e61b4fc ("ANDROID: Make suspend abort reason logging depend on CONFIG_PM_SLEEP") 9d17e24b036e ("ANDROID: wakeup_reason: use vsnprintf instead of snsprintf for vargs.") 7961972600ba ("ANDROID: power: Provide dummy log_suspend_abort_reason() if SUSPEND is disabled")] Signed-off-by: Amit Pundir --- drivers/base/power/main.c | 5 +++++ drivers/base/power/wakeup.c | 16 +++++++++++++ drivers/base/syscore.c | 3 +++ include/linux/suspend.h | 1 + include/linux/wakeup_reason.h | 7 ++++++ kernel/power/process.c | 9 ++++++++ kernel/power/suspend.c | 20 +++++++++++++++-- kernel/power/wakeup_reason.c | 42 ++++++++++++++++++++++++++++------- 8 files changed, 93 insertions(+), 10 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a690fd400260..ee0d68f61cfb 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "../base.h" #include "power.h" @@ -1706,6 +1707,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; const char *info = NULL; int error = 0; + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; DECLARE_DPM_WATCHDOG_ON_STACK(wd); TRACE_DEVICE(dev); @@ -1729,6 +1731,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (pm_wakeup_pending()) { dev->power.direct_complete = false; + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); async_error = -EBUSY; goto Complete; } diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 5fa1898755a3..a4ebcf37c612 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -803,6 +803,22 @@ void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard) } EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); +void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) +{ + struct wakeup_source *ws; + int len = 0; + rcu_read_lock(); + len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->active) { + len += snprintf(pending_wakeup_source + len, max, + "%s ", ws->name); + } + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); + void pm_print_active_wakeup_sources(void) { struct wakeup_source *ws; diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index 6e076f359dcc..996573ffa58e 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -10,6 +10,7 @@ #include #include #include +#include static LIST_HEAD(syscore_ops_list); static DEFINE_MUTEX(syscore_ops_lock); @@ -74,6 +75,8 @@ int syscore_suspend(void) return 0; err_out: + log_suspend_abort_reason("System core suspend callback %pF failed", + ops->suspend); pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); list_for_each_entry_continue(ops, &syscore_ops_list, node) diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 3f529ad9a9d2..71c6110bd551 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -446,6 +446,7 @@ extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); extern void pm_wakep_autosleep_enabled(bool set); extern void pm_print_active_wakeup_sources(void); +extern void pm_get_active_wakeup_sources(char *pending_sources, size_t max); extern void lock_system_sleep(void); extern void unlock_system_sleep(void); diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h index 7ce50f0debc4..9fbe209c7177 100644 --- a/include/linux/wakeup_reason.h +++ b/include/linux/wakeup_reason.h @@ -18,6 +18,13 @@ #ifndef _LINUX_WAKEUP_REASON_H #define _LINUX_WAKEUP_REASON_H +#define MAX_SUSPEND_ABORT_LEN 256 + void log_wakeup_reason(int irq); +#ifdef CONFIG_SUSPEND +void log_suspend_abort_reason(const char *fmt, ...); +#else +static inline void log_suspend_abort_reason(const char *fmt, ...) { } +#endif #endif /* _LINUX_WAKEUP_REASON_H */ diff --git a/kernel/power/process.c b/kernel/power/process.c index d76e61606f51..c366e3d34a07 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -22,6 +22,7 @@ #include #include #include +#include /* * Timeout for stopping processes @@ -38,6 +39,9 @@ static int try_to_freeze_tasks(bool user_only) unsigned int elapsed_msecs; bool wakeup = false; int sleep_usecs = USEC_PER_MSEC; +#ifdef CONFIG_PM_SLEEP + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; +#endif start = ktime_get_boottime(); @@ -67,6 +71,11 @@ static int try_to_freeze_tasks(bool user_only) break; if (pm_wakeup_pending()) { +#ifdef CONFIG_PM_SLEEP + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); +#endif wakeup = true; break; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 0bd595a0b610..36cb7edb9126 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "power.h" @@ -396,7 +397,8 @@ void __weak arch_suspend_enable_irqs(void) */ static int suspend_enter(suspend_state_t state, bool *wakeup) { - int error; + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; + int error, last_dev; error = platform_suspend_prepare(state); if (error) @@ -404,7 +406,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) error = dpm_suspend_late(PMSG_SUSPEND); if (error) { + last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; + last_dev %= REC_FAILED_NUM; pr_err("late suspend of devices failed\n"); + log_suspend_abort_reason("%s device failed to power down", + suspend_stats.failed_devs[last_dev]); goto Platform_finish; } error = platform_suspend_prepare_late(state); @@ -418,7 +424,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) error = dpm_suspend_noirq(PMSG_SUSPEND); if (error) { + last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; + last_dev %= REC_FAILED_NUM; pr_err("noirq suspend of devices failed\n"); + log_suspend_abort_reason("noirq suspend of %s device failed", + suspend_stats.failed_devs[last_dev]); goto Platform_early_resume; } error = platform_suspend_prepare_noirq(state); @@ -429,8 +439,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) goto Platform_wake; error = disable_nonboot_cpus(); - if (error || suspend_test(TEST_CPUS)) + if (error || suspend_test(TEST_CPUS)) { + log_suspend_abort_reason("Disabling non-boot cpus failed"); goto Enable_cpus; + } arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); @@ -447,6 +459,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) trace_suspend_resume(TPS("machine_suspend"), state, false); } else if (*wakeup) { + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); error = -EBUSY; } syscore_resume(); @@ -498,6 +513,7 @@ int suspend_devices_and_enter(suspend_state_t state) error = dpm_suspend_start(PMSG_SUSPEND); if (error) { pr_err("Some devices failed to suspend, or early wake event detected\n"); + log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected"); goto Recover_platform; } suspend_test_finish("suspend devices"); diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 187e4e9105fb..5944dfe78191 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -31,6 +31,8 @@ #define MAX_WAKEUP_REASON_IRQS 32 static int irq_list[MAX_WAKEUP_REASON_IRQS]; static int irqcount; +static bool suspend_abort; +static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; @@ -40,14 +42,18 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu int irq_no, buf_offset = 0; struct irq_desc *desc; spin_lock(&resume_reason_lock); - for (irq_no = 0; irq_no < irqcount; irq_no++) { - desc = irq_to_desc(irq_list[irq_no]); - if (desc && desc->action && desc->action->name) - buf_offset += sprintf(buf + buf_offset, "%d %s\n", - irq_list[irq_no], desc->action->name); - else - buf_offset += sprintf(buf + buf_offset, "%d\n", - irq_list[irq_no]); + if (suspend_abort) { + buf_offset = sprintf(buf, "Abort: %s", abort_reason); + } else { + for (irq_no = 0; irq_no < irqcount; irq_no++) { + desc = irq_to_desc(irq_list[irq_no]); + if (desc && desc->action && desc->action->name) + buf_offset += sprintf(buf + buf_offset, "%d %s\n", + irq_list[irq_no], desc->action->name); + else + buf_offset += sprintf(buf + buf_offset, "%d\n", + irq_list[irq_no]); + } } spin_unlock(&resume_reason_lock); return buf_offset; @@ -89,6 +95,25 @@ void log_wakeup_reason(int irq) spin_unlock(&resume_reason_lock); } +void log_suspend_abort_reason(const char *fmt, ...) +{ + va_list args; + + spin_lock(&resume_reason_lock); + + //Suspend abort reason has already been logged. + if (suspend_abort) { + spin_unlock(&resume_reason_lock); + return; + } + + suspend_abort = true; + va_start(args, fmt); + vsnprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args); + va_end(args); + spin_unlock(&resume_reason_lock); +} + /* Detects a suspend and clears all the previous wake up reasons*/ static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) @@ -97,6 +122,7 @@ static int wakeup_reason_pm_event(struct notifier_block *notifier, case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; + suspend_abort = false; spin_unlock(&resume_reason_lock); break; default: From f21313b70ac73dafcbacacd054243d6509594b68 Mon Sep 17 00:00:00 2001 From: jinqian Date: Wed, 25 Mar 2015 16:18:44 -0700 Subject: [PATCH 0470/1276] ANDROID: power: wakeup_reason: Report suspend times from last_suspend_time This node epxorts two values separated by space. From left to right: 1. time spent in suspend/resume process 2. time spent sleep in suspend state Change-Id: I2cb9a9408a5fd12166aaec11b935a0fd6a408c63 --- .../ABI/testing/sysfs-kernel-wakeup_reasons | 16 +++++++++ kernel/power/wakeup_reason.c | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-kernel-wakeup_reasons diff --git a/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons new file mode 100644 index 000000000000..acb19b91c192 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons @@ -0,0 +1,16 @@ +What: /sys/kernel/wakeup_reasons/last_resume_reason +Date: February 2014 +Contact: Ruchi Kandoi +Description: + The /sys/kernel/wakeup_reasons/last_resume_reason is + used to report wakeup reasons after system exited suspend. + +What: /sys/kernel/wakeup_reasons/last_suspend_time +Date: March 2015 +Contact: jinqian +Description: + The /sys/kernel/wakeup_reasons/last_suspend_time is + used to report time spent in last suspend cycle. It contains + two numbers (in seconds) separated by space. First number is + the time spent in suspend and resume processes. Second number + is the time spent in sleep state. \ No newline at end of file diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 5944dfe78191..8480e32b41cb 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -36,6 +36,11 @@ static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; +static struct timespec last_xtime; /* wall time before last suspend */ +static struct timespec curr_xtime; /* wall time after last suspend */ +static struct timespec last_stime; /* total_sleep_time before last suspend */ +static struct timespec curr_stime; /* total_sleep_time after last suspend */ + static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -59,10 +64,32 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu return buf_offset; } +static ssize_t last_suspend_time_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct timespec sleep_time; + struct timespec total_time; + struct timespec suspend_resume_time; + + sleep_time = timespec_sub(curr_stime, last_stime); + total_time = timespec_sub(curr_xtime, last_xtime); + suspend_resume_time = timespec_sub(total_time, sleep_time); + + /* + * suspend_resume_time is calculated from sleep_time. Userspace would + * always need both. Export them in pair here. + */ + return sprintf(buf, "%lu.%09lu %lu.%09lu\n", + suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec, + sleep_time.tv_sec, sleep_time.tv_nsec); +} + static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason); +static struct kobj_attribute suspend_time = __ATTR_RO(last_suspend_time); static struct attribute *attrs[] = { &resume_reason.attr, + &suspend_time.attr, NULL, }; static struct attribute_group attr_group = { @@ -118,12 +145,21 @@ void log_suspend_abort_reason(const char *fmt, ...) static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { + struct timespec xtom; /* wall_to_monotonic, ignored */ + switch (pm_event) { case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; suspend_abort = false; spin_unlock(&resume_reason_lock); + + get_xtime_and_monotonic_and_sleep_offset(&last_xtime, &xtom, + &last_stime); + break; + case PM_POST_SUSPEND: + get_xtime_and_monotonic_and_sleep_offset(&curr_xtime, &xtom, + &curr_stime); break; default: break; From f97ec34442ac5ec2f399762d8814a7df6530b225 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 14 Apr 2015 02:38:20 +0530 Subject: [PATCH 0471/1276] ANDROID: power: wakeup_reason: fix suspend time reporting Suspend time reporting Change-Id: I2cb9a9408a5fd12166aaec11b935a0fd6a408c63 (Power: Report suspend times from last_suspend_time), is broken on 3.16+ kernels because get_xtime_and_monotonic_and_sleep_offset() hrtimer helper routine is removed from kernel timekeeping. The replacement helper routines ktime_get_update_offsets_{tick,now}() are private to core kernel timekeeping so we can't use them, hence using ktime_get() and ktime_get_boottime() instead and sampling the time twice. Idea is to use Monotonic boottime offset to calculate total time spent in last suspend state and CLOCK_MONOTONIC to calculate time spent in last suspend-resume process. Signed-off-by: Amit Pundir --- kernel/power/wakeup_reason.c | 41 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 8480e32b41cb..ddbcf15ec667 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -36,10 +36,10 @@ static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; -static struct timespec last_xtime; /* wall time before last suspend */ -static struct timespec curr_xtime; /* wall time after last suspend */ -static struct timespec last_stime; /* total_sleep_time before last suspend */ -static struct timespec curr_stime; /* total_sleep_time after last suspend */ +static ktime_t last_monotime; /* monotonic time before last suspend */ +static ktime_t curr_monotime; /* monotonic time after last suspend */ +static ktime_t last_stime; /* monotonic boottime offset before last suspend */ +static ktime_t curr_stime; /* monotonic boottime offset after last suspend */ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -71,14 +71,22 @@ static ssize_t last_suspend_time_show(struct kobject *kobj, struct timespec total_time; struct timespec suspend_resume_time; - sleep_time = timespec_sub(curr_stime, last_stime); - total_time = timespec_sub(curr_xtime, last_xtime); - suspend_resume_time = timespec_sub(total_time, sleep_time); + /* + * total_time is calculated from monotonic bootoffsets because + * unlike CLOCK_MONOTONIC it include the time spent in suspend state. + */ + total_time = ktime_to_timespec(ktime_sub(curr_stime, last_stime)); /* - * suspend_resume_time is calculated from sleep_time. Userspace would - * always need both. Export them in pair here. + * suspend_resume_time is calculated as monotonic (CLOCK_MONOTONIC) + * time interval before entering suspend and post suspend. */ + suspend_resume_time = ktime_to_timespec(ktime_sub(curr_monotime, last_monotime)); + + /* sleep_time = total_time - suspend_resume_time */ + sleep_time = timespec_sub(total_time, suspend_resume_time); + + /* Export suspend_resume_time and sleep_time in pair here. */ return sprintf(buf, "%lu.%09lu %lu.%09lu\n", suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec, sleep_time.tv_sec, sleep_time.tv_nsec); @@ -145,21 +153,22 @@ void log_suspend_abort_reason(const char *fmt, ...) static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { - struct timespec xtom; /* wall_to_monotonic, ignored */ - switch (pm_event) { case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; suspend_abort = false; spin_unlock(&resume_reason_lock); - - get_xtime_and_monotonic_and_sleep_offset(&last_xtime, &xtom, - &last_stime); + /* monotonic time since boot */ + last_monotime = ktime_get(); + /* monotonic time since boot including the time spent in suspend */ + last_stime = ktime_get_boottime(); break; case PM_POST_SUSPEND: - get_xtime_and_monotonic_and_sleep_offset(&curr_xtime, &xtom, - &curr_stime); + /* monotonic time since boot */ + curr_monotime = ktime_get(); + /* monotonic time since boot including the time spent in suspend */ + curr_stime = ktime_get_boottime(); break; default: break; From cd92df73e5049bd0af97d9c1b179ade0344c2d8d Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 8 Apr 2015 15:42:29 -0700 Subject: [PATCH 0472/1276] ANDROID: power: wakeup: Add last wake up source logging for suspend abort reason. There is a possibility that a wakeup source event is received after the device prepares to suspend which might cause the suspend to abort. This patch adds the functionality of reporting the last active wakeup source which is currently not active but caused the suspend to abort reason via the /sys/kernel/power/last_wakeup_reason file. Change-Id: I1760d462f497b33e425f5565cb6cff5973932ec3 Signed-off-by: Ruchi Kandoi --- drivers/base/power/wakeup.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index a4ebcf37c612..4110de9fe190 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "power.h" @@ -805,16 +806,31 @@ EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) { - struct wakeup_source *ws; + struct wakeup_source *ws, *last_active_ws = NULL; int len = 0; + bool active = false; + rcu_read_lock(); - len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->active) { - len += snprintf(pending_wakeup_source + len, max, + if (!active) + len += scnprintf(pending_wakeup_source, max, + "Pending Wakeup Sources: "); + len += scnprintf(pending_wakeup_source + len, max - len, "%s ", ws->name); + active = true; + } else if (!active && + (!last_active_ws || + ktime_to_ns(ws->last_time) > + ktime_to_ns(last_active_ws->last_time))) { + last_active_ws = ws; } } + if (!active && last_active_ws) { + scnprintf(pending_wakeup_source, max, + "Last active Wakeup Source: %s", + last_active_ws->name); + } rcu_read_unlock(); } EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); From 546b6ae3c087ddea20fcd8208f72a6d4d33c65a8 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 10 Nov 2015 10:53:55 -0800 Subject: [PATCH 0473/1276] ANDROID: power: wakeup: Add the guard condition for len in pm_get_active_wakeup_sources Check if the len is not greater than maximum to prevent buffer overflow. Signed-off-by: Ruchi Kandoi Change-Id: I575b0a72bb5448b68353408d71fa8b83420c9088 --- drivers/base/power/wakeup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 4110de9fe190..e75512dd9733 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -812,7 +812,7 @@ void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { - if (ws->active) { + if (ws->active && len < max) { if (!active) len += scnprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); From 6c6a4a362bebea1bce261408106bc3a5828c0730 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Sun, 29 May 2016 14:22:32 -0700 Subject: [PATCH 0474/1276] ANDROID: security,perf: Allow further restriction of perf_event_open When kernel.perf_event_open is set to 3 (or greater), disallow all access to performance events by users without CAP_SYS_ADMIN. Add a Kconfig symbol CONFIG_SECURITY_PERF_EVENTS_RESTRICT that makes this value the default. This is based on a similar feature in grsecurity (CONFIG_GRKERNSEC_PERF_HARDEN). This version doesn't include making the variable read-only. It also allows enabling further restriction at run-time regardless of whether the default is changed. https://lkml.org/lkml/2016/1/11/587 Signed-off-by: Ben Hutchings Bug: 29054680 Change-Id: Iff5bff4fc1042e85866df9faa01bce8d04335ab8 --- Documentation/sysctl/kernel.txt | 4 +++- include/linux/perf_event.h | 5 +++++ kernel/events/core.c | 8 ++++++++ security/Kconfig | 9 +++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 37a679501ddc..0e4a8129e86f 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -693,7 +693,8 @@ allowed to execute. perf_event_paranoid: Controls use of the performance events system by unprivileged -users (without CAP_SYS_ADMIN). The default value is 2. +users (without CAP_SYS_ADMIN). The default value is 3 if +CONFIG_SECURITY_PERF_EVENTS_RESTRICT is set, or 2 otherwise. -1: Allow use of (almost) all events by all users Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK @@ -701,6 +702,7 @@ users (without CAP_SYS_ADMIN). The default value is 2. Disallow raw tracepoint access by users without CAP_SYS_ADMIN >=1: Disallow CPU event access by users without CAP_SYS_ADMIN >=2: Disallow kernel profiling by users without CAP_SYS_ADMIN +>=3: Disallow all event access by users without CAP_SYS_ADMIN ============================================================== diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 53c500f0ca79..15c236b8aba3 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1179,6 +1179,11 @@ extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, int perf_event_max_stack_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +static inline bool perf_paranoid_any(void) +{ + return sysctl_perf_event_paranoid > 2; +} + static inline bool perf_paranoid_tracepoint_raw(void) { return sysctl_perf_event_paranoid > -1; diff --git a/kernel/events/core.c b/kernel/events/core.c index 5a97f34bc14c..a4a4fc1e1586 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -397,8 +397,13 @@ static cpumask_var_t perf_online_mask; * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv + * 3 - disallow all unpriv perf event use */ +#ifdef CONFIG_SECURITY_PERF_EVENTS_RESTRICT +int sysctl_perf_event_paranoid __read_mostly = 3; +#else int sysctl_perf_event_paranoid __read_mostly = 2; +#endif /* Minimum for 512 kiB + 1 user control page */ int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ @@ -10410,6 +10415,9 @@ SYSCALL_DEFINE5(perf_event_open, if (flags & ~PERF_FLAG_ALL) return -EINVAL; + if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + err = perf_copy_attr(attr_uptr, &attr); if (err) return err; diff --git a/security/Kconfig b/security/Kconfig index d9aa521b5206..6c379cd0df1e 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -18,6 +18,15 @@ config SECURITY_DMESG_RESTRICT If you are unsure how to answer this question, answer N. +config SECURITY_PERF_EVENTS_RESTRICT + bool "Restrict unprivileged use of performance events" + depends on PERF_EVENTS + help + If you say Y here, the kernel.perf_event_paranoid sysctl + will be set to 3 by default, and no unprivileged use of the + perf_event_open syscall will be permitted unless it is + changed. + config SECURITY bool "Enable different security models" depends on SYSFS From caa3c58efdccf1a87a562cc818f877d137cc4b4d Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Fri, 2 Oct 2015 00:39:53 -0700 Subject: [PATCH 0475/1276] ANDROID: trace: sched: add sched blocked tracepoint which dumps out context of sleep. Decare war on uninterruptible sleep. Add a tracepoint which walks the kernel stack and dumps the first non-scheduler function called before the scheduler is invoked. Change-Id: I19e965d5206329360a92cbfe2afcc8c30f65c229 Signed-off-by: Riley Andrews --- include/trace/events/sched.h | 26 +++++++++++++++++++++++++- kernel/sched/fair.c | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 0be866c91f62..4a68273b29d5 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -226,7 +226,7 @@ DECLARE_EVENT_CLASS(sched_process_template, DEFINE_EVENT(sched_process_template, sched_process_free, TP_PROTO(struct task_struct *p), TP_ARGS(p)); - + /* * Tracepoint for a task exiting: @@ -380,6 +380,30 @@ DEFINE_EVENT(sched_stat_template, sched_stat_blocked, TP_PROTO(struct task_struct *tsk, u64 delay), TP_ARGS(tsk, delay)); +/* + * Tracepoint for recording the cause of uninterruptible sleep. + */ +TRACE_EVENT(sched_blocked_reason, + + TP_PROTO(struct task_struct *tsk), + + TP_ARGS(tsk), + + TP_STRUCT__entry( + __field( pid_t, pid ) + __field( void*, caller ) + __field( bool, io_wait ) + ), + + TP_fast_assign( + __entry->pid = tsk->pid; + __entry->caller = (void*)get_wchan(tsk); + __entry->io_wait = tsk->in_iowait; + ), + + TP_printk("pid=%d iowait=%d caller=%pS", __entry->pid, __entry->io_wait, __entry->caller) +); + /* * Tracepoint for accounting runtime (time the task is executing * on a CPU). diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 908c9cdae2f0..725488a1f628 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -941,6 +941,7 @@ update_stats_enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) } trace_sched_stat_blocked(tsk, delta); + trace_sched_blocked_reason(tsk); /* * Blocking time is in units of nanosecs, so shift by From 97456bc36e225bf3d219f214cca854680d3ac786 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:39:37 -0800 Subject: [PATCH 0476/1276] ANDROID: usb: gadget: f_accessory: Add Android Accessory function USB accessory mode allows users to connect USB host hardware specifically designed for Android-powered devices. The accessories must adhere to the Android accessory protocol outlined in the http://accessories.android.com documentation. This allows Android devices that cannot act as a USB host to still interact with USB hardware. When an Android device is in USB accessory mode, the attached Android USB accessory acts as the host, provides power to the USB bus, and enumerates connected devices. Change-Id: I67964b50d278f3c0471d47efbb7b0973a3502681 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch ceb2f0aac624 ("ANDROID: usb: gadget: accessory: Fix section mismatch") Parts of e27543931009 ("ANDROID: usb: gadget: Fixes and hacks to make android usb gadget compile on 3.8") 1b07ec751563 ("ANDROID: drivers: usb: gadget: 64-bit related type fixes")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 798 +++++++++++++++++++++++++++++++ include/linux/usb/f_accessory.h | 83 ++++ 2 files changed, 881 insertions(+) create mode 100644 drivers/usb/gadget/f_accessory.c create mode 100644 include/linux/usb/f_accessory.h diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 000000000000..72e9f0b1b57c --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c @@ -0,0 +1,798 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define ACC_STRING_SIZE 256 + +#define PROTOCOL_VERSION 1 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +struct acc_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + /* set to 1 when we connect */ + int online:1; + /* Set to 1 when we disconnect. + * Not cleared until our file is closed. + */ + int disconnected:1; + + /* strings sent by the host */ + char manufacturer[ACC_STRING_SIZE]; + char model[ACC_STRING_SIZE]; + char description[ACC_STRING_SIZE]; + char version[ACC_STRING_SIZE]; + char uri[ACC_STRING_SIZE]; + char serial[ACC_STRING_SIZE]; + + /* for acc_complete_set_string */ + int string_index; + + /* set to 1 if we have a pending start request */ + int start_requested; + + /* synchronize access to our device file */ + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + struct delayed_work work; +}; + +static struct usb_interface_descriptor acc_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_endpoint_descriptor acc_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_fullspeed_in_desc, + (struct usb_descriptor_header *) &acc_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_highspeed_in_desc, + (struct usb_descriptor_header *) &acc_highspeed_out_desc, + NULL, +}; + +static struct usb_string acc_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", + { }, /* end of list */ +}; + +static struct usb_gadget_strings acc_string_table = { + .language = 0x0409, /* en-US */ + .strings = acc_string_defs, +}; + +static struct usb_gadget_strings *acc_strings[] = { + &acc_string_table, + NULL, +}; + +/* temporary variable used between acc_open() and acc_gadget_bind() */ +static struct acc_dev *_acc_dev; + +static inline struct acc_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct acc_dev, function); +} + +static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void acc_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/* add a request to the tail of a list */ +static void req_put(struct acc_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void acc_set_disconnected(struct acc_dev *dev) +{ + dev->online = 0; + dev->disconnected = 1; +} + +static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + if (req->status != 0) + acc_set_disconnected(dev); + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + dev->rx_done = 1; + if (req->status != 0) + acc_set_disconnected(dev); + + wake_up(&dev->read_wq); +} + +static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = ep->driver_data; + char *string_dest = NULL; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_string, err %d\n", req->status); + return; + } + + switch (dev->string_index) { + case ACCESSORY_STRING_MANUFACTURER: + string_dest = dev->manufacturer; + break; + case ACCESSORY_STRING_MODEL: + string_dest = dev->model; + break; + case ACCESSORY_STRING_DESCRIPTION: + string_dest = dev->description; + break; + case ACCESSORY_STRING_VERSION: + string_dest = dev->version; + break; + case ACCESSORY_STRING_URI: + string_dest = dev->uri; + break; + case ACCESSORY_STRING_SERIAL: + string_dest = dev->serial; + break; + } + if (string_dest) { + unsigned long flags; + + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + + spin_lock_irqsave(&dev->lock, flags); + memcpy(string_dest, req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { + pr_err("unknown accessory string index %d\n", + dev->string_index); + } +} + +static int create_bulk_endpoints(struct acc_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_out; + dev->rx_req[i] = req; + } + + return 0; + +fail: + printk(KERN_ERR "acc_bind() could not allocate requests\n"); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + return -1; +} + +static ssize_t acc_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req; + ssize_t r = count; + unsigned xfer; + int ret = 0; + + pr_debug("acc_read(%zu)\n", count); + + if (dev->disconnected) + return -ENODEV; + + if (count > BULK_BUFFER_SIZE) + count = BULK_BUFFER_SIZE; + + /* we will block until we're online */ + pr_debug("acc_read: waiting for online\n"); + ret = wait_event_interruptible(dev->read_wq, dev->online); + if (ret < 0) { + r = ret; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->online) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + pr_debug("rx %p %u\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + pr_debug("acc_read returning %zd\n", r); + return r; +} + +static ssize_t acc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req = 0; + ssize_t r = count; + unsigned xfer; + int ret; + + pr_debug("acc_write(%zu)\n", count); + + if (!dev->online || dev->disconnected) + return -ENODEV; + + while (count > 0) { + if (!dev->online) { + pr_debug("acc_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + pr_debug("acc_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + pr_debug("acc_write returning %zd\n", r); + return r; +} + +static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct acc_dev *dev = fp->private_data; + char *src = NULL; + int ret; + + switch (code) { + case ACCESSORY_GET_STRING_MANUFACTURER: + src = dev->manufacturer; + break; + case ACCESSORY_GET_STRING_MODEL: + src = dev->model; + break; + case ACCESSORY_GET_STRING_DESCRIPTION: + src = dev->description; + break; + case ACCESSORY_GET_STRING_VERSION: + src = dev->version; + break; + case ACCESSORY_GET_STRING_URI: + src = dev->uri; + break; + case ACCESSORY_GET_STRING_SERIAL: + src = dev->serial; + break; + case ACCESSORY_IS_START_REQUESTED: + return dev->start_requested; + } + if (!src) + return -EINVAL; + + ret = strlen(src) + 1; + if (copy_to_user((void __user *)value, src, ret)) + ret = -EFAULT; + return ret; +} + +static int acc_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_open\n"); + if (atomic_xchg(&_acc_dev->open_excl, 1)) + return -EBUSY; + + _acc_dev->disconnected = 0; + fp->private_data = _acc_dev; + return 0; +} + +static int acc_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_release\n"); + + WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); + _acc_dev->disconnected = 0; + return 0; +} + +/* file operations for /dev/acc_usb */ +static const struct file_operations acc_fops = { + .owner = THIS_MODULE, + .read = acc_read, + .write = acc_write, + .unlocked_ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, +}; + +static struct miscdevice acc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "usb_accessory", + .fops = &acc_fops, +}; + + +static int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct acc_dev *dev = _acc_dev; + int value = -EOPNOTSUPP; + u8 b_requestType = ctrl->bRequestType; + u8 b_request = ctrl->bRequest; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + +/* + printk(KERN_INFO "acc_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + b_requestType, b_request, + w_value, w_index, w_length); +*/ + + if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_START) { + dev->start_requested = 1; + schedule_delayed_work( + &dev->work, msecs_to_jiffies(10)); + value = 0; + } else if (b_request == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } + } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_GET_PROTOCOL) { + *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; + value = sizeof(u16); + + /* clear any string left over from a previous session */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); + memset(dev->serial, 0, sizeof(dev->serial)); + dev->start_requested = 0; + } + } + + if (value >= 0) { + cdev->req->zero = 0; + cdev->req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "%s setup response queue error\n", + __func__); + } + + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct acc_dev *dev = func_to_dev(f); + int id; + int ret; + + DBG(cdev, "acc_function_bind dev: %p\n", dev); + + dev->start_requested = 0; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + acc_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, + &acc_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acc_highspeed_in_desc.bEndpointAddress = + acc_fullspeed_in_desc.bEndpointAddress; + acc_highspeed_out_desc.bEndpointAddress = + acc_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +acc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); +} + +static void acc_work(struct work_struct *data) +{ + char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); +} + +static int acc_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void acc_function_disable(struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "acc_function_disable\n"); + acc_set_disconnected(dev); + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int acc_bind_config(struct usb_configuration *c) +{ + struct acc_dev *dev = _acc_dev; + int ret; + + printk(KERN_INFO "acc_bind_config\n"); + + /* allocate a string ID for our interface */ + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int acc_setup(void) +{ + struct acc_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + atomic_set(&dev->open_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_DELAYED_WORK(&dev->work, acc_work); + + /* _acc_dev must be set before calling usb_gadget_register_driver */ + _acc_dev = dev; + + ret = misc_register(&acc_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + return ret; +} + +static void acc_cleanup(void) +{ + misc_deregister(&acc_device); + kfree(_acc_dev); + _acc_dev = NULL; +} diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h new file mode 100644 index 000000000000..5b2dcf9728e1 --- /dev/null +++ b/include/linux/usb/f_accessory.h @@ -0,0 +1,83 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_F_ACCESSORY_H +#define __LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version (currently 1) + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) + +#endif /* __LINUX_USB_F_ACCESSORY_H */ From 9d5891d516e205e81d1ab7809c65af46c7d778d8 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:00:40 -0700 Subject: [PATCH 0477/1276] ANDROID: usb: gadget: f_accessory: Add ACCESSORY_SET_AUDIO_MODE control request and ioctl The control request will be used by the host to enable/disable USB audio and the ioctl will be used by userspace to read the audio mode Change-Id: I81c38611b588451e80eacdccc417ca6e11c60cab Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 11 ++++++++++- include/linux/usb/f_accessory.h | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 72e9f0b1b57c..216d669e6344 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -40,7 +40,7 @@ #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 -#define PROTOCOL_VERSION 1 +#define PROTOCOL_VERSION 2 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -78,6 +78,8 @@ struct acc_dev { /* set to 1 if we have a pending start request */ int start_requested; + int audio_mode; + /* synchronize access to our device file */ atomic_t open_excl; @@ -512,6 +514,8 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) break; case ACCESSORY_IS_START_REQUESTED: return dev->start_requested; + case ACCESSORY_GET_AUDIO_MODE: + return dev->audio_mode; } if (!src) return -EINVAL; @@ -588,6 +592,10 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, cdev->gadget->ep0->driver_data = dev; cdev->req->complete = acc_complete_set_string; value = w_length; + } else if (b_request == ACCESSORY_SET_AUDIO_MODE && + w_index == 0 && w_length == 0) { + dev->audio_mode = w_value; + value = 0; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -602,6 +610,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, memset(dev->uri, 0, sizeof(dev->uri)); memset(dev->serial, 0, sizeof(dev->serial)); dev->start_requested = 0; + dev->audio_mode = 0; } } diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 5b2dcf9728e1..ddb2fd0e88fb 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -36,13 +36,15 @@ #define ACCESSORY_STRING_URI 4 #define ACCESSORY_STRING_SERIAL 5 -/* Control request for retrieving device's protocol version (currently 1) +/* Control request for retrieving device's protocol version * * requestType: USB_DIR_IN | USB_TYPE_VENDOR * request: ACCESSORY_GET_PROTOCOL * value: 0 * index: 0 * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -70,6 +72,17 @@ */ #define ACCESSORY_START 53 +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + /* ioctls for retrieving strings set by the host */ #define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) #define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) @@ -79,5 +92,7 @@ #define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) /* returns 1 if there is a start request pending */ #define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) #endif /* __LINUX_USB_F_ACCESSORY_H */ From dc66cfce9622bf4e750c956159505544cf0bdf4a Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 26 Mar 2012 11:03:55 -0700 Subject: [PATCH 0478/1276] ANDROID: usb: gadget: f_accessory: Add support for HID input devices Change-Id: I4f1452db32508382df52acdc47c0eb395ae328c7 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch 4ce5e656ae74 ("ANDROID: usb: gadget: accessory: Fix section mismatch (again)")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 389 ++++++++++++++++++++++++++++++- include/linux/usb/f_accessory.h | 50 +++- 2 files changed, 431 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 216d669e6344..73c6b2072d73 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include #include @@ -49,6 +51,20 @@ #define TX_REQ_MAX 4 #define RX_REQ_MAX 2 +struct acc_hid_dev { + struct list_head list; + struct hid_device *hid; + struct acc_dev *dev; + /* accessory defined ID */ + int id; + /* HID report descriptor */ + u8 *report_desc; + /* length of HID report descriptor */ + int report_desc_len; + /* number of bytes of report_desc we have received so far */ + int report_desc_offset; +}; + struct acc_dev { struct usb_function function; struct usb_composite_dev *cdev; @@ -89,7 +105,21 @@ struct acc_dev { wait_queue_head_t write_wq; struct usb_request *rx_req[RX_REQ_MAX]; int rx_done; - struct delayed_work work; + + /* delayed work for handling ACCESSORY_START */ + struct delayed_work start_work; + + /* worker for registering and unregistering hid devices */ + struct work_struct hid_work; + + /* list of active HID devices */ + struct list_head hid_list; + + /* list of new HID devices to register */ + struct list_head new_hid_list; + + /* list of dead HID devices to unregister */ + struct list_head dead_hid_list; }; static struct usb_interface_descriptor acc_interface_desc = { @@ -298,6 +328,160 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) } } +static void acc_complete_set_hid_report_desc(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + struct acc_dev *dev = hid->dev; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_hid_report_desc, err %d\n", + req->status); + return; + } + + memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); + hid->report_desc_offset += length; + if (hid->report_desc_offset == hid->report_desc_len) { + /* After we have received the entire report descriptor + * we schedule work to initialize the HID device + */ + schedule_work(&dev->hid_work); + } +} + +static void acc_complete_send_hid_event(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_send_hid_event, err %d\n", req->status); + return; + } + + hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); +} + +static int acc_hid_parse(struct hid_device *hid) +{ + struct acc_hid_dev *hdev = hid->driver_data; + + hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); + return 0; +} + +static int acc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_stop(struct hid_device *hid) +{ +} + +static int acc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver acc_hid_ll_driver = { + .parse = acc_hid_parse, + .start = acc_hid_start, + .stop = acc_hid_stop, + .open = acc_hid_open, + .close = acc_hid_close, +}; + +static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, + int id, int desc_len) +{ + struct acc_hid_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); + if (!hdev) + return NULL; + hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); + if (!hdev->report_desc) { + kfree(hdev); + return NULL; + } + hdev->dev = dev; + hdev->id = id; + hdev->report_desc_len = desc_len; + + return hdev; +} + +static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) +{ + struct acc_hid_dev *hid; + + list_for_each_entry(hid, list, list) { + if (hid->id == id) + return hid; + } + return NULL; +} + +static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + /* report descriptor length must be > 0 */ + if (desc_length <= 0) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + /* replace HID if one already exists with this ID */ + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (hid) + list_move(&hid->list, &dev->dead_hid_list); + + hid = acc_hid_new(dev, id, desc_length); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + list_add(&hid->list, &dev->new_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + /* schedule work to register the HID device */ + schedule_work(&dev->hid_work); + return 0; +} + +static int acc_unregister_hid(struct acc_dev *dev, int id) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + + list_move(&hid->list, &dev->dead_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); + return 0; +} + static int create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) @@ -355,7 +539,7 @@ static int create_bulk_endpoints(struct acc_dev *dev, return 0; fail: - printk(KERN_ERR "acc_bind() could not allocate requests\n"); + pr_err("acc_bind() could not allocate requests\n"); while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -546,7 +730,7 @@ static int acc_release(struct inode *ip, struct file *fp) return 0; } -/* file operations for /dev/acc_usb */ +/* file operations for /dev/usb_accessory */ static const struct file_operations acc_fops = { .owner = THIS_MODULE, .read = acc_read, @@ -556,23 +740,47 @@ static const struct file_operations acc_fops = { .release = acc_release, }; +static int acc_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) + return ret; + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + static struct miscdevice acc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "usb_accessory", .fops = &acc_fops, }; +static const struct hid_device_id acc_hid_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver acc_hid_driver = { + .name = "USB accessory", + .id_table = acc_hid_table, + .probe = acc_hid_probe, +}; static int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; int value = -EOPNOTSUPP; + struct acc_hid_dev *hid; + int offset; u8 b_requestType = ctrl->bRequestType; u8 b_request = ctrl->bRequest; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; /* printk(KERN_INFO "acc_ctrlrequest " @@ -585,7 +793,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_START) { dev->start_requested = 1; schedule_delayed_work( - &dev->work, msecs_to_jiffies(10)); + &dev->start_work, msecs_to_jiffies(10)); value = 0; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; @@ -596,6 +804,38 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, w_index == 0 && w_length == 0) { dev->audio_mode = w_value; value = 0; + } else if (b_request == ACCESSORY_REGISTER_HID) { + value = acc_register_hid(dev, w_value, w_index); + } else if (b_request == ACCESSORY_UNREGISTER_HID) { + value = acc_unregister_hid(dev, w_value); + } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->new_hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + offset = w_index; + if (offset != hid->report_desc_offset + || offset + w_length > hid->report_desc_len) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_set_hid_report_desc; + value = w_length; + } else if (b_request == ACCESSORY_SEND_HID_EVENT) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_send_hid_event; + value = w_length; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -623,6 +863,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, __func__); } +err: if (value == -EOPNOTSUPP) VDBG(cdev, "unknown class-specific control req " @@ -642,6 +883,10 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + ret = hid_register_driver(&acc_hid_driver); + if (ret) + return ret; + dev->start_requested = 0; /* allocate interface ID(s) */ @@ -670,6 +915,36 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static void +kill_all_hid_devices(struct acc_dev *dev) +{ + struct acc_hid_dev *hid; + struct list_head *entry, *temp; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_safe(entry, temp, &dev->hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); +} + +static void +acc_hid_unbind(struct acc_dev *dev) +{ + hid_unregister_driver(&acc_hid_driver); + kill_all_hid_devices(dev); +} + static void acc_function_unbind(struct usb_configuration *c, struct usb_function *f) { @@ -681,14 +956,104 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) acc_request_free(dev->rx_req[i], dev->ep_out); + + acc_hid_unbind(dev); } -static void acc_work(struct work_struct *data) +static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } +static int acc_hid_init(struct acc_hid_dev *hdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &acc_hid_ll_driver; + hid->dev.parent = acc_device.this_device; + + hid->bus = BUS_USB; + hid->vendor = HID_ANY_ID; + hid->product = HID_ANY_ID; + hid->driver_data = hdev; + ret = hid_add_device(hid); + if (ret) { + pr_err("can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + hdev->hid = hid; + return 0; +} + +static void acc_hid_delete(struct acc_hid_dev *hid) +{ + kfree(hid->report_desc); + kfree(hid); +} + +static void acc_hid_work(struct work_struct *data) +{ + struct acc_dev *dev = _acc_dev; + struct list_head *entry, *temp; + struct acc_hid_dev *hid; + struct list_head new_list, dead_list; + unsigned long flags; + + INIT_LIST_HEAD(&new_list); + + spin_lock_irqsave(&dev->lock, flags); + + /* copy hids that are ready for initialization to new_list */ + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (hid->report_desc_offset == hid->report_desc_len) + list_move(&hid->list, &new_list); + } + + if (list_empty(&dev->dead_hid_list)) { + INIT_LIST_HEAD(&dead_list); + } else { + /* move all of dev->dead_hid_list to dead_list */ + dead_list.prev = dev->dead_hid_list.prev; + dead_list.next = dev->dead_hid_list.next; + dead_list.next->prev = &dead_list; + dead_list.prev->next = &dead_list; + INIT_LIST_HEAD(&dev->dead_hid_list); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + /* register new HID devices */ + list_for_each_safe(entry, temp, &new_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (acc_hid_init(hid)) { + pr_err("can't add HID device %p\n", hid); + acc_hid_delete(hid); + } else { + spin_lock_irqsave(&dev->lock, flags); + list_move(&hid->list, &dev->hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + } + } + + /* remove dead HID devices */ + list_for_each_safe(entry, temp, &dead_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + if (hid->hid) + hid_destroy_device(hid->hid); + acc_hid_delete(hid); + } +} + static int acc_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { @@ -782,7 +1147,11 @@ static int acc_setup(void) init_waitqueue_head(&dev->write_wq); atomic_set(&dev->open_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); - INIT_DELAYED_WORK(&dev->work, acc_work); + INIT_LIST_HEAD(&dev->hid_list); + INIT_LIST_HEAD(&dev->new_hid_list); + INIT_LIST_HEAD(&dev->dead_hid_list); + INIT_DELAYED_WORK(&dev->start_work, acc_start_work); + INIT_WORK(&dev->hid_work, acc_hid_work); /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; @@ -795,10 +1164,16 @@ static int acc_setup(void) err: kfree(dev); - printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + pr_err("USB accessory gadget driver failed to initialize\n"); return ret; } +static void acc_disconnect(void) +{ + /* unregister all HID devices if USB is disconnected */ + kill_all_hid_devices(_acc_dev); +} + static void acc_cleanup(void) { misc_deregister(&acc_device); diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index ddb2fd0e88fb..61ebe0aabc5b 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -44,7 +44,7 @@ * index: 0 * data version number (16 bits little endian) * 1 for original accessory support - * 2 adds device to host audio support + * 2 adds HID and device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -72,6 +72,54 @@ */ #define ACCESSORY_START 53 +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + /* Control request for setting the audio mode. * * requestType: USB_DIR_OUT | USB_TYPE_VENDOR From 5f1ac9c2871b22eb12eaec09bb37002c9787a5ee Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 13:08:39 -0800 Subject: [PATCH 0479/1276] ANDROID: usb: gadget: f_accessory: move userspace interface to uapi Move the entire contents of linux/usb/f_accessory.h header to uapi, it only contains a userspace interface. Change-Id: Ieb5547da449588ae554988a201c0e6b4e3afc531 Signed-off-by: Colin Cross --- include/linux/usb/f_accessory.h | 125 +---------------------- include/uapi/linux/usb/f_accessory.h | 146 +++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 124 deletions(-) create mode 100644 include/uapi/linux/usb/f_accessory.h diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 61ebe0aabc5b..ebe3c4d59309 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -18,129 +18,6 @@ #ifndef __LINUX_USB_F_ACCESSORY_H #define __LINUX_USB_F_ACCESSORY_H -/* Use Google Vendor ID when in accessory mode */ -#define USB_ACCESSORY_VENDOR_ID 0x18D1 - - -/* Product ID to use when in accessory mode */ -#define USB_ACCESSORY_PRODUCT_ID 0x2D00 - -/* Product ID to use when in accessory mode and adb is enabled */ -#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 - -/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ -#define ACCESSORY_STRING_MANUFACTURER 0 -#define ACCESSORY_STRING_MODEL 1 -#define ACCESSORY_STRING_DESCRIPTION 2 -#define ACCESSORY_STRING_VERSION 3 -#define ACCESSORY_STRING_URI 4 -#define ACCESSORY_STRING_SERIAL 5 - -/* Control request for retrieving device's protocol version - * - * requestType: USB_DIR_IN | USB_TYPE_VENDOR - * request: ACCESSORY_GET_PROTOCOL - * value: 0 - * index: 0 - * data version number (16 bits little endian) - * 1 for original accessory support - * 2 adds HID and device to host audio support - */ -#define ACCESSORY_GET_PROTOCOL 51 - -/* Control request for host to send a string to the device - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SEND_STRING - * value: 0 - * index: string ID - * data zero terminated UTF8 string - * - * The device can later retrieve these strings via the - * ACCESSORY_GET_STRING_* ioctls - */ -#define ACCESSORY_SEND_STRING 52 - -/* Control request for starting device in accessory mode. - * The host sends this after setting all its strings to the device. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_START - * value: 0 - * index: 0 - * data none - */ -#define ACCESSORY_START 53 - -/* Control request for registering a HID device. - * Upon registering, a unique ID is sent by the accessory in the - * value parameter. This ID will be used for future commands for - * the device - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_REGISTER_HID_DEVICE - * value: Accessory assigned ID for the HID device - * index: total length of the HID report descriptor - * data none - */ -#define ACCESSORY_REGISTER_HID 54 - -/* Control request for unregistering a HID device. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_REGISTER_HID - * value: Accessory assigned ID for the HID device - * index: 0 - * data none - */ -#define ACCESSORY_UNREGISTER_HID 55 - -/* Control request for sending the HID report descriptor. - * If the HID descriptor is longer than the endpoint zero max packet size, - * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC - * commands. The data for the descriptor must be sent sequentially - * if multiple packets are needed. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SET_HID_REPORT_DESC - * value: Accessory assigned ID for the HID device - * index: offset of data in descriptor - * (needed when HID descriptor is too big for one packet) - * data the HID report descriptor - */ -#define ACCESSORY_SET_HID_REPORT_DESC 56 - -/* Control request for sending HID events. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SEND_HID_EVENT - * value: Accessory assigned ID for the HID device - * index: 0 - * data the HID report for the event - */ -#define ACCESSORY_SEND_HID_EVENT 57 - -/* Control request for setting the audio mode. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SET_AUDIO_MODE - * value: 0 - no audio - * 1 - device to host, 44100 16-bit stereo PCM - * index: 0 - * data none - */ -#define ACCESSORY_SET_AUDIO_MODE 58 - -/* ioctls for retrieving strings set by the host */ -#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) -#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) -#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) -#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) -#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) -#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) -/* returns 1 if there is a start request pending */ -#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) -/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ -#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) +#include #endif /* __LINUX_USB_F_ACCESSORY_H */ diff --git a/include/uapi/linux/usb/f_accessory.h b/include/uapi/linux/usb/f_accessory.h new file mode 100644 index 000000000000..0baeb7d0d74c --- /dev/null +++ b/include/uapi/linux/usb/f_accessory.h @@ -0,0 +1,146 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H +#define _UAPI_LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds HID and device to host audio support + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) + +#endif /* _UAPI_LINUX_USB_F_ACCESSORY_H */ From 9a6241722cd8526ad83495f4779b5e3b800dc235 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:07:44 +0800 Subject: [PATCH 0480/1276] ANDROID: usb: gadget: f_accessory: Enabled Zero Length Packet (ZLP) for acc_write Accessory connected to Android Device requires Zero Length Packet (ZLP) to be written when data transferred out from the Android device are multiples of wMaxPacketSize (64bytes (Full-Speed) / 512bytes (High-Speed)) to end the transfer. Change-Id: Ib2c2c0ab98ef9afa10e74a720142deca5c0ed476 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_accessory.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 73c6b2072d73..f8490c034107 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -640,10 +640,17 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, break; } - if (count > BULK_BUFFER_SIZE) + if (count > BULK_BUFFER_SIZE) { xfer = BULK_BUFFER_SIZE; - else + /* ZLP, They will be more TX requests so not yet. */ + req->zero = 0; + } else { xfer = count; + /* If the data length is a multple of the + * maxpacket size then send a zero length packet(ZLP). + */ + req->zero = ((xfer % dev->ep_in->maxpacket) == 0); + } if (copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; From 31a0ecd5a82584c2b076b32e196842bbb2a7116e Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 16 Jan 2015 05:41:10 +0530 Subject: [PATCH 0481/1276] ANDROID: usb: gadget: f_accessory: check for accessory device before disconnecting HIDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While disabling ConfigFS Android gadget, android_disconnect() calls kill_all_hid_devices(), if CONFIG_USB_CONFIGFS_F_ACC is enabled, to free the registered HIDs without checking whether the USB accessory device really exist or not. If USB accessory device doesn't exist then we run into following kernel panic: ----8<---- [  136.724761] Unable to handle kernel NULL pointer dereference at virtual address 00000064 [  136.724809] pgd = c0204000 [  136.731924] [00000064] *pgd=00000000 [  136.737830] Internal error: Oops: 5 [#1] SMP ARM [  136.738108] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.18.0-rc4-00400-gf75300e-dirty #76 [  136.742788] task: c0fb19d8 ti: c0fa4000 task.ti: c0fa4000 [  136.750890] PC is at _raw_spin_lock_irqsave+0x24/0x60 [  136.756246] LR is at kill_all_hid_devices+0x24/0x114 ---->8---- This patch adds a test to check if USB Accessory device exists before freeing HIDs. Change-Id: Ie229feaf0de3f4f7a151fcaa9a994e34e15ff73b Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index f8490c034107..9ffe017bf1cf 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -929,6 +929,10 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; + /* do nothing if usb accessory device doesn't exist */ + if (!dev) + return; + spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); From 77db15bf50432d2c56f0b3b19bc5d4c1c6350c46 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:01:08 -0700 Subject: [PATCH 0482/1276] ANDROID: usb: gadget: f_audio_source: New gadget driver for audio output This driver presents a standard USB audio class interface to the host and an ALSA PCM device to userspace Change-Id: If16b14a5ff27045f9cb2daaf1ae9195c5eeab7d0 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch Parts of e27543931009 ("ANDROID: usb: gadget: Fixes and hacks to make android usb gadget compile on 3.8") i6d9285e2574a ("ANDROID: usb: gadget: f_audio_source:replace deprecated API")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_audio_source.c | 828 ++++++++++++++++++++++++++++ 1 file changed, 828 insertions(+) create mode 100644 drivers/usb/gadget/f_audio_source.c diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c new file mode 100644 index 000000000000..54f1699613bf --- /dev/null +++ b/drivers/usb/gadget/f_audio_source.c @@ -0,0 +1,828 @@ +/* + * Gadget Function Driver for USB audio source device + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#define SAMPLE_RATE 44100 +#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) + +#define IN_EP_MAX_PACKET_SIZE 384 + +/* Number of requests to allocate */ +#define IN_EP_REQ_COUNT 4 + +#define AUDIO_AC_INTERFACE 0 +#define AUDIO_AS_INTERFACE 1 +#define AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ + + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ + + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = AUDIO_AC_INTERFACE, + [1] = AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 4, /* poll 1 per millisecond */ +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 1, /* poll 1 per millisecond */ +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&hs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct snd_pcm_hardware audio_hw_info = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = SAMPLE_RATE, + .rate_max = SAMPLE_RATE, + + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +/*-------------------------------------------------------------------------*/ + +struct audio_source_config { + int card; + int device; +}; + +struct audio_dev { + struct usb_function func; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + struct list_head idle_reqs; + struct usb_ep *in_ep; + + spinlock_t lock; + + /* beginning, end and current position in our buffer */ + void *buffer_start; + void *buffer_end; + void *buffer_pos; + + /* byte size of a "period" */ + unsigned int period; + /* bytes sent since last call to snd_pcm_period_elapsed */ + unsigned int period_offset; + /* time we started playing */ + ktime_t start_time; + /* number of frames sent since start_time */ + s64 frames_sent; +}; + +static inline struct audio_dev *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + req->length = buffer_size; + return req; +} + +static void audio_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static void audio_req_put(struct audio_dev *audio, struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + list_add_tail(&req->list, &audio->idle_reqs); + spin_unlock_irqrestore(&audio->lock, flags); +} + +static struct usb_request *audio_req_get(struct audio_dev *audio) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&audio->lock, flags); + if (list_empty(&audio->idle_reqs)) { + req = 0; + } else { + req = list_first_entry(&audio->idle_reqs, struct usb_request, + list); + list_del(&req->list); + } + spin_unlock_irqrestore(&audio->lock, flags); + return req; +} + +/* send the appropriate number of packets to match our bitrate */ +static void audio_send(struct audio_dev *audio) +{ + struct snd_pcm_runtime *runtime; + struct usb_request *req; + int length, length1, length2, ret; + s64 msecs; + s64 frames; + ktime_t now; + + /* audio->substream will be null if we have been closed */ + if (!audio->substream) + return; + /* audio->buffer_pos will be null if we have been stopped */ + if (!audio->buffer_pos) + return; + + runtime = audio->substream->runtime; + + /* compute number of frames to send */ + now = ktime_get(); + msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); + do_div(msecs, 1000000); + frames = msecs * SAMPLE_RATE; + do_div(frames, 1000); + + /* Readjust our frames_sent if we fall too far behind. + * If we get too far behind it is better to drop some frames than + * to keep sending data too fast in an attempt to catch up. + */ + if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) + audio->frames_sent = frames - FRAMES_PER_MSEC; + + frames -= audio->frames_sent; + + /* We need to send something to keep the pipeline going */ + if (frames <= 0) + frames = FRAMES_PER_MSEC; + + while (frames > 0) { + req = audio_req_get(audio); + if (!req) + break; + + length = frames_to_bytes(runtime, frames); + if (length > IN_EP_MAX_PACKET_SIZE) + length = IN_EP_MAX_PACKET_SIZE; + + if (audio->buffer_pos + length > audio->buffer_end) + length1 = audio->buffer_end - audio->buffer_pos; + else + length1 = length; + memcpy(req->buf, audio->buffer_pos, length1); + if (length1 < length) { + /* Wrap around and copy remaining length + * at beginning of buffer. + */ + length2 = length - length1; + memcpy(req->buf + length1, audio->buffer_start, + length2); + audio->buffer_pos = audio->buffer_start + length2; + } else { + audio->buffer_pos += length1; + if (audio->buffer_pos >= audio->buffer_end) + audio->buffer_pos = audio->buffer_start; + } + + req->length = length; + ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("usb_ep_queue failed ret: %d\n", ret); + audio_req_put(audio, req); + break; + } + + frames -= bytes_to_frames(runtime, length); + audio->frames_sent += bytes_to_frames(runtime, length); + } +} + +static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* nothing to do here */ +} + +static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct audio_dev *audio = req->context; + + pr_debug("audio_data_complete req->status %d req->actual %d\n", + req->status, req->actual); + + audio_req_put(audio, req); + + if (!audio->buffer_start || req->status) + return; + + audio->period_offset += req->actual; + if (audio->period_offset >= audio->period) { + snd_pcm_period_elapsed(audio->substream); + audio->period_offset = 0; + } + audio_send(audio); +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + case UAC_SET_MIN: + case UAC_SET_MAX: + case UAC_SET_RES: + value = len; + break; + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 *buf = cdev->req->buf; + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + /* return our sample rate */ + buf[0] = (u8)SAMPLE_RATE; + buf[1] = (u8)(SAMPLE_RATE >> 8); + buf[2] = (u8)(SAMPLE_RATE >> 16); + value = 3; + break; + default: + break; + } + } + + return value; +} + +static int +audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + req->complete = audio_control_complete; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + pr_err("audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); + if (ret) + return ret; + + usb_ep_enable(audio->in_ep); + return 0; +} + +static void audio_disable(struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_disable\n"); + usb_ep_disable(audio->in_ep); +} + +/*-------------------------------------------------------------------------*/ + +static void audio_build_desc(struct audio_dev *audio) +{ + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = 2; + as_type_i_desc.bNrChannels = 2; + + /* Set sample rates */ + rate = SAMPLE_RATE; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); +} + +/* audio function driver setup/binding */ +static int +audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct audio_dev *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + struct usb_request *req; + int i; + + audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate our endpoint */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + ep->driver_data = audio; /* claim */ + + if (gadget_is_dualspeed(c->cdev->gadget)) + hs_as_in_ep_desc.bEndpointAddress = + fs_as_in_ep_desc.bEndpointAddress; + + f->fs_descriptors = fs_audio_desc; + f->hs_descriptors = hs_audio_desc; + + for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { + req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); + if (req) { + req->context = audio; + req->complete = audio_data_complete; + audio_req_put(audio, req); + } else + status = -ENOMEM; + } + +fail: + return status; +} + +static void +audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_request *req; + + while ((req = audio_req_get(audio))) + audio_request_free(req, audio->in_ep); + + snd_card_free_when_closed(audio->card); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; +} + +static void audio_pcm_playback_start(struct audio_dev *audio) +{ + audio->start_time = ktime_get(); + audio->frames_sent = 0; + audio_send(audio); +} + +static void audio_pcm_playback_stop(struct audio_dev *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->buffer_start = 0; + audio->buffer_end = 0; + audio->buffer_pos = 0; + spin_unlock_irqrestore(&audio->lock, flags); +} + +static int audio_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = substream->private_data; + + runtime->private_data = audio; + runtime->hw = audio_hw_info; + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_max = 2; + + audio->substream = substream; + return 0; +} + +static int audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct audio_dev *audio = substream->private_data; + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; + spin_unlock_irqrestore(&audio->lock, flags); + + return 0; +} + +static int audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (rate != SAMPLE_RATE) + return -EINVAL; + if (channels != 2) + return -EINVAL; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + + audio->period = snd_pcm_lib_period_bytes(substream); + audio->period_offset = 0; + audio->buffer_start = runtime->dma_area; + audio->buffer_end = audio->buffer_start + + snd_pcm_lib_buffer_bytes(substream); + audio->buffer_pos = audio->buffer_start; + + return 0; +} + +static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + ssize_t bytes = audio->buffer_pos - audio->buffer_start; + + /* return offset of next frame to fill in our buffer */ + return bytes_to_frames(runtime, bytes); +} + +static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct audio_dev *audio = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + audio_pcm_playback_start(audio); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + audio_pcm_playback_stop(audio); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + +static struct snd_pcm_ops audio_playback_ops = { + .open = audio_pcm_open, + .close = audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = audio_pcm_hw_params, + .hw_free = audio_pcm_hw_free, + .prepare = audio_pcm_prepare, + .trigger = audio_pcm_playback_trigger, + .pointer = audio_pcm_pointer, +}; + +int audio_source_bind_config(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + config->card = -1; + config->device = -1; + + audio = &_audio_dev; + + err = snd_card_new(&c->cdev->gadget->dev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + return err; + + err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); + if (err) + goto pcm_fail; + + pcm->private_data = audio; + pcm->info_flags = 0; + audio->pcm = pcm; + + strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); + + strlcpy(card->driver, "audio_source", sizeof(card->driver)); + strlcpy(card->shortname, card->driver, sizeof(card->shortname)); + strlcpy(card->longname, "USB accessory audio source", + sizeof(card->longname)); + + err = snd_card_register(card); + if (err) + goto register_fail; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + config->card = pcm->card->number; + config->device = pcm->device; + audio->card = card; + return 0; + +add_fail: +register_fail: +pcm_fail: + snd_card_free(audio->card); + return err; +} From 2095c953d894e7f30eb81af913d5a9d5e9907962 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Tue, 1 Jul 2014 18:17:20 +0800 Subject: [PATCH 0483/1276] ANDROID: usb: gadget: f_audio_source: change max ISO packet size Re-applying from https://gitorious.org/shr/linux/commit/eb4c9d2db894c3492c0a848581bd4f6790f93d5f Most USB-AUDIO devices are limited to 256 byte for max iso buffer size. If a IN_EP_MAX_PACKET_SIZE is bigger than a USB-AUDIO device's max iso buffer size, it will cause noise. This patch will prevent this case as possibe by reducing packet size. When using 44.1khz, 2ch, 16bit audio data, if max packet size is bigger than 176 bytes, it's no problem. Credits to: Iliyan Malchev Change-Id: Ic2a1c19ea65d5fb42bf12926b51b255b465d7215 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 54f1699613bf..904b1ef6658f 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -24,7 +24,7 @@ #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 384 +#define IN_EP_MAX_PACKET_SIZE 256 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 From 8671b3e536381e62d15f45e3b6eeb21cf6a88189 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:14:01 +0800 Subject: [PATCH 0484/1276] ANDROID: usb: gadget: f_audio_source: Fixed USB Audio Class Interface Descriptor Fixed Android Issue #56549. When both Vendor Class and Audio Class are activated for AOA 2.0, the baInterfaceNr of the AudioControl Interface Descriptor points to wrong interface numbers. They should be pointing to Audio Control Device and Audio Streaming interfaces. Replaced baInterfaceNr with the correct value. Change-Id: Iaa083f3d97c1f0fc9481bf87852b2b51278a6351 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 904b1ef6658f..4b9786b48950 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -580,12 +580,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; ac_interface_desc.bInterfaceNumber = status; + /* AUDIO_AC_INTERFACE */ + ac_header_desc.baInterfaceNr[0] = status; + status = usb_interface_id(c, f); if (status < 0) goto fail; as_interface_alt_0_desc.bInterfaceNumber = status; as_interface_alt_1_desc.bInterfaceNumber = status; + /* AUDIO_AS_INTERFACE */ + ac_header_desc.baInterfaceNr[1] = status; + status = -ENODEV; /* allocate our endpoint */ From b81b4efb2cd1e23ee1512b1986fa50232cb48346 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 16:42:27 -0800 Subject: [PATCH 0485/1276] ANDROID: usb: gadget: configfs: Add usb_function ptr to fi struct Add a pointer to the usb_function inside the usb_function_instance structure to service functions specific setup requests even before the function gets added to the usb_gadget Signed-off-by: Badhri Jagan Sridharan Change-Id: I6f457006f6c5516cc6986ec2acdf5b1ecf259d0c --- include/linux/usb/composite.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 8675e145ea8b..af4396cc4ea8 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -587,6 +587,7 @@ struct usb_function_instance { struct config_group group; struct list_head cfs_list; struct usb_function_driver *fd; + struct usb_function *f; int (*set_inst_name)(struct usb_function_instance *inst, const char *name); void (*free_func_inst)(struct usb_function_instance *inst); From 41f5b6ffd3335831aef97ddabc4b395e49a793dd Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 10:44:47 -0800 Subject: [PATCH 0486/1276] ANDROID: usb: gadget: configfs: Add Uevent to notify userspace Android userspace UsbDeviceManager relies on the uevents generated by the composition driver to generate user notifications. This CL adds uevents to be generated whenever USB changes its state i.e. connected, disconnected, configured. This CL also intercepts the setup requests from the usb_core anb routes it to the specific usb function if required. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib3d3a78255a532f7449dac286f776c2966caf8c1 [AmitP: Folded following android-4.9 commit changes into this patch 9214c899f730 ("ANDROID: usb: gadget: configfs: handle gadget reset request for android")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 8 ++ drivers/usb/gadget/configfs.c | 155 +++++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..d92762d134b4 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -368,6 +368,14 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_UEVENT + bool "Uevent notification of Gadget state" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when the gadget + state changes. The gadget can be in any of the following + three states: "CONNECTED/DISCONNECTED/CONFIGURED" + config USB_CONFIGFS_F_UAC1 bool "Audio Class 1.0" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 025129942894..0c67c7302405 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -10,6 +10,20 @@ #include "u_f.h" #include "u_os_desc.h" +#ifdef CONFIG_USB_CONFIGFS_UEVENT +#include +#include +#include +#include "u_fs.h" + +#ifdef CONFIG_USB_CONFIGFS_F_ACC +extern int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl); +void acc_disconnect(void); +#endif +static struct class *android_class; +#endif + int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { @@ -61,6 +75,12 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + bool connected; + bool sw_connected; + struct work_struct work; + struct device *dev; +#endif }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -266,7 +286,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, mutex_lock(&gi->lock); - if (!strlen(name)) { + if (!strlen(name) || strcmp(name, "none") == 0) { ret = unregister_gadget(gi); if (ret) goto err; @@ -1372,6 +1392,57 @@ static int configfs_composite_bind(struct usb_gadget *gadget, return ret; } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static void android_work(struct work_struct *data) +{ + struct gadget_info *gi = container_of(data, struct gadget_info, work); + struct usb_composite_dev *cdev = &gi->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + /* 0-connected 1-configured 2-disconnected*/ + bool status[3] = { false, false, false }; + unsigned long flags; + bool uevent_sent = false; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + status[1] = true; + + if (gi->connected != gi->sw_connected) { + if (gi->connected) + status[0] = true; + else + status[2] = true; + gi->sw_connected = gi->connected; + } + spin_unlock_irqrestore(&cdev->lock, flags); + + if (status[0]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + pr_info("%s: sent uevent %s\n", __func__, connected[0]); + uevent_sent = true; + } + + if (status[1]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + pr_info("%s: sent uevent %s\n", __func__, configured[0]); + uevent_sent = true; + } + + if (status[2]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); + uevent_sent = true; + } + + if (!uevent_sent) { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + gi->connected, gi->sw_connected, cdev->config); + } +} +#endif + static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; @@ -1391,14 +1462,79 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static int android_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *c) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + int value = -EOPNOTSUPP; + struct usb_function_instance *fi; + + spin_lock_irqsave(&cdev->lock, flags); + if (!gi->connected) { + gi->connected = 1; + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + list_for_each_entry(fi, &gi->available_func, cfs_list) { + if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { + value = fi->f->setup(fi->f, c); + if (value >= 0) + break; + } + } + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + if (value < 0) + value = acc_ctrlrequest(cdev, c); +#endif + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + acc_disconnect(); +#endif + gi->connected = 0; + schedule_work(&gi->work); + composite_disconnect(gadget); +} +#endif + static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - +#ifdef CONFIG_USB_CONFIGFS_UEVENT + .setup = android_setup, + .reset = android_disconnect, + .disconnect = android_disconnect, +#else .setup = composite_setup, .reset = composite_disconnect, .disconnect = composite_disconnect, - +#endif .suspend = composite_suspend, .resume = composite_resume, @@ -1458,6 +1594,12 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + INIT_WORK(&gi->work, android_work); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); +#endif + if (!gi->composite.gadget_driver.function) goto err; @@ -1509,6 +1651,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); ret = configfs_register_subsystem(&gadget_subsys); + +#ifdef CONFIG_USB_CONFIGFS_UEVENT + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); +#endif + return ret; } module_init(gadget_cfs_init); From 5d5c64b62f6f456070ca5d6243b9dfc2de1263cc Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:15:19 -0700 Subject: [PATCH 0487/1276] ANDROID: usb: gadget: configfs: Add function devices to the parent Added create_function_device to create child function devices for USB gadget functions. Android UsbDeviceManager relies on communicating to the devices created by the gadget functions to implement functions such as audio_source. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0df9ad86ac32d8cdacdea164e9fed49891b45fc2 --- drivers/usb/gadget/configfs.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 0c67c7302405..37eb312d7498 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -14,7 +14,6 @@ #include #include #include -#include "u_fs.h" #ifdef CONFIG_USB_CONFIGFS_F_ACC extern int acc_ctrlrequest(struct usb_composite_dev *cdev, @@ -22,6 +21,18 @@ extern int acc_ctrlrequest(struct usb_composite_dev *cdev, void acc_disconnect(void); #endif static struct class *android_class; +static struct device *android_device; +static int index; + +struct device *create_function_device(char *name) +{ + if (android_device && !IS_ERR(android_device)) + return device_create(android_class, android_device, + MKDEV(0, index++), NULL, name); + else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(create_function_device); #endif int check_user_usb_string(const char *name, @@ -1419,19 +1430,22 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; } @@ -1596,8 +1610,10 @@ static struct config_group *gadgets_make( #ifdef CONFIG_USB_CONFIGFS_UEVENT INIT_WORK(&gi->work, android_work); - gi->dev = device_create(android_class, NULL, + android_device = device_create(android_class, NULL, MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + goto err; #endif if (!gi->composite.gadget_driver.function) @@ -1612,6 +1628,9 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { config_item_put(item); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + device_destroy(android_device->class, android_device->devt); +#endif } static struct configfs_group_operations gadgets_ops = { @@ -1665,5 +1684,10 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + if (!IS_ERR(android_class)) + class_destroy(android_class); +#endif + } module_exit(gadget_cfs_exit); From 5e2784d0496fec2e3d86353bfdbeae0d825e10ff Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 14 Jul 2015 15:46:11 -0700 Subject: [PATCH 0488/1276] ANDROID: usb: gadget: configfs: Add "state" attribute to android_device Added a device attribute to android_device to determine USB_GADGET's state Signed-off-by: Badhri Jagan Sridharan Change-Id: I17f8903120df96bf2f4bf441940b53a87b818230 --- drivers/usb/gadget/configfs.c | 66 ++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 37eb312d7498..68f67ce607da 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1560,11 +1560,49 @@ static const struct usb_gadget_driver configfs_driver_template = { .match_existing_only = 1, }; +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct gadget_info *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!dev) + goto out; + + cdev = &dev->cdev; + + if (!cdev) + goto out; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + state = "CONFIGURED"; + else if (dev->connected) + state = "CONNECTED"; + spin_unlock_irqrestore(&cdev->lock, flags); +out: + return sprintf(buf, "%s\n", state); +} + +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *android_usb_attributes[] = { + &dev_attr_state, + NULL +}; +#endif + static struct config_group *gadgets_make( struct config_group *group, const char *name) { struct gadget_info *gi; + struct device_attribute **attrs; + struct device_attribute *attr; + int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1614,12 +1652,31 @@ static struct config_group *gadgets_make( MKDEV(0, 0), NULL, "android0"); if (IS_ERR(android_device)) goto err; + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + err = device_create_file(android_device, attr); + if (err) + goto err1; + } #endif if (!gi->composite.gadget_driver.function) - goto err; + goto err1; return &gi->group; + +err1: +#ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + + device_destroy(android_device->class, + android_device->devt); +#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1627,8 +1684,15 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { + struct device_attribute **attrs; + struct device_attribute *attr; + config_item_put(item); + #ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); device_destroy(android_device->class, android_device->devt); #endif } From a924be3652004768a328156be070be80c09a9a17 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 9 Aug 2015 15:12:50 -0700 Subject: [PATCH 0489/1276] ANDROID: usb: gadget: configfs: Add device attribute to determine gadget state Android frameworks (UsbDeviceManager) relies on gadget state exported through device attributes. This CL adds the device attribute to export USB gadget state. Change-Id: Id0391810d75b58c579610fbec6e37ab22f28886d Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch Parts of e45c769fa7af ("ANDROID: usb: gadget: cleanup: fix unused variable and function warnings") Signed-off-by: Amit Pundir --- drivers/usb/gadget/configfs.c | 91 ++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 68f67ce607da..59a97eba1aa8 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1593,6 +1593,54 @@ static struct device_attribute *android_usb_attributes[] = { &dev_attr_state, NULL }; + +static int android_device_create(struct gadget_info *gi) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + INIT_WORK(&gi->work, android_work); + android_device = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + return PTR_ERR(android_device); + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + int err; + + err = device_create_file(android_device, attr); + if (err) { + device_destroy(android_device->class, + android_device->devt); + return err; + } + } + + return 0; +} + +static void android_device_destroy(void) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + device_destroy(android_device->class, android_device->devt); +} +#else +static inline int android_device_create(struct gadget_info *gi) +{ + return 0; +} + +static inline void android_device_destroy(void) +{ +} #endif static struct config_group *gadgets_make( @@ -1600,9 +1648,6 @@ static struct config_group *gadgets_make( const char *name) { struct gadget_info *gi; - struct device_attribute **attrs; - struct device_attribute *attr; - int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1646,37 +1691,14 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; -#ifdef CONFIG_USB_CONFIGFS_UEVENT - INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) + if (!gi->composite.gadget_driver.function) goto err; - dev_set_drvdata(android_device, gi); - - attrs = android_usb_attributes; - while ((attr = *attrs++)) { - err = device_create_file(android_device, attr); - if (err) - goto err1; - } -#endif - - if (!gi->composite.gadget_driver.function) - goto err1; + if (android_device_create(gi) < 0) + goto err; return &gi->group; -err1: -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - - device_destroy(android_device->class, - android_device->devt); -#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1684,17 +1706,8 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { - struct device_attribute **attrs; - struct device_attribute *attr; - config_item_put(item); - -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); -#endif + android_device_destroy(); } static struct configfs_group_operations gadgets_ops = { From 5c899c9fd75d1ad1be54fbac38ecee9ae4dd7ed4 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 14 Aug 2017 12:45:38 +0200 Subject: [PATCH 0490/1276] ANDROID: usb: gadget: configfs: fix null ptr in android_disconnect There's a race between usb_gadget_udc_stop() which is likely to set the gadget driver to NULL in the udc driver and this drivers gadget disconnect fn which likely checks for the gadget driver to a null ptr. It happens that unbind (doing set_gadget_data(NULL)) is called before the gadget driver is set to NULL and the udc driver calls disconnect fn which results in cdev being a null ptr. As a workaround we check cdev in android_disconnect() to prevent the following panic: Unable to handle kernel NULL pointer dereference at virtual address 000000a8 pgd = ffffff800940a000 [000000a8] *pgd=00000000be1fe003, *pud=00000000be1fe003, *pmd=0000000000000000 Internal error: Oops: 96000046 [#1] PREEMPT SMP CPU: 7 PID: 1134 Comm: kworker/u16:3 Tainted: G S 4.9.41-g75cd2a0231ea-dirty #4 Hardware name: HiKey960 (DT) Workqueue: events_power_efficient event_work task: ffffffc0b5f4f000 task.stack: ffffffc0b5b94000 PC is at android_disconnect+0x54/0xa4 LR is at android_disconnect+0x54/0xa4 pc : [] lr : [] pstate: 80000185 sp : ffffffc0b5b97bf0 x29: ffffffc0b5b97bf0 x28: 0000000000000003 x27: ffffffc0b5181c54 x26: ffffffc0b5181c68 x25: ffffff8008dc1000 x24: ffffffc0b5181d70 x23: ffffff8008dc18a0 x22: ffffffc0b5f5a018 x21: ffffffc0b5894ad8 x20: 0000000000000000 x19: ffffff8008ddaec8 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 00000000007c9ccd x13: 0000000000000000 x12: 0000000000000000 x11: 0000000000000001 x10: 0000000000000001 x9 : ffffff800930f1a8 x8 : ffffff800932a133 x7 : 0000000000000000 x6 : 0000000000000000 x5 : ffffffc0b5b97a50 x4 : ffffffc0be19f090 x3 : 0000000000000000 x2 : ffffff80091ca000 x1 : 000000000000002f x0 : 000000000000002f This happened on a hikey960 with the following backtrace: [] android_disconnect+0x54/0xa4 [] dwc3_disconnect_gadget.part.19+0x114.888119] [] dwc3_gadget_suspend+0x6c/0x70 [] dwc3_suspend_device+0x58/0xa0 [] dwc3_otg_work+0x214/0x474 [] event_work+0x3bc/0x5ac [] process_one_work+0x14c/0x43c [] worker_thread+0x5c/0x438 [] kthread+0xec/0x100 [] ret_from_fork+0x10/0x50 dwc3_otg_work tries to handle a switch from host to device mode and therefore calls disconnect on the gadget driver. To reproduce the issue it is enaugh to enable tethering (rndis gadget), unplug and plug in again the usb connector which causes the change from device to host and back to device mode. Signed-off-by: Danilo Krummrich --- drivers/usb/gadget/configfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 59a97eba1aa8..b0148a449306 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1523,6 +1523,18 @@ static void android_disconnect(struct usb_gadget *gadget) struct usb_composite_dev *cdev = get_gadget_data(gadget); struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + /* FIXME: There's a race between usb_gadget_udc_stop() which is likely + * to set the gadget driver to NULL in the udc driver and this drivers + * gadget disconnect fn which likely checks for the gadget driver to + * be a null ptr. It happens that unbind (doing set_gadget_data(NULL)) + * is called before the gadget driver is set to NULL and the udc driver + * calls disconnect fn which results in cdev being a null ptr. + */ + if (cdev == NULL) { + WARN(1, "%s: gadget driver already disconnected\n", __func__); + return; + } + /* accessory HID support can be active while the accessory function is not actually enabled, so we need to inform it when we are disconnected. From ddfd0c4070c16e1d8708bf8a1cfcdb2b3aecc34a Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 13:51:28 -0800 Subject: [PATCH 0491/1276] ANDROID: usb: gadget: f_audio_source: Move to USB_FUNCTION API This patch adds support to use audio_source gadget function through DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: I1fc6c9ea07105ae4eb785eebd3bb925bfdd8bc6b [AmitP: Folded following android-4.9 commit changes into this patch 95106e3d2071 ("ANDROID: usb: gadget: build audio_source function only if SND is enabled") 4de9e33e5046 ("ANDROID: usb: gadget: audio_source: fix comparison of distinct pointer types") Parts of 051584e76d12 ("ANDROID: usb: gadget: function: cleanup: Add blank line after declaration")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 12 ++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_audio_source.c | 242 ++++++++++++++++++++++++++-- 3 files changed, 245 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d92762d134b4..a306bb3bef85 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -215,6 +215,9 @@ config USB_F_PRINTER config USB_F_TCM tristate +config USB_F_AUDIO_SRC + tristate + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -368,6 +371,15 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_AUDIO_SRC + bool "Audio Source gadget" + depends on USB_CONFIGFS + depends on SND + select SND_PCM + select USB_F_AUDIO_SRC + help + USB gadget Audio Source support + config USB_CONFIGFS_UEVENT bool "Uevent notification of Gadget state" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 130dad7130b6..f019157a0908 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,3 +11,5 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 4b9786b48950..61b47b820a90 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -21,6 +21,13 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) @@ -32,6 +39,7 @@ #define AUDIO_AC_INTERFACE 0 #define AUDIO_AS_INTERFACE 1 #define AUDIO_NUM_INTERFACES 2 +#define MAX_INST_NAME_LEN 40 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -259,6 +267,7 @@ struct audio_dev { ktime_t start_time; /* number of frames sent since start_time */ s64 frames_sent; + struct audio_source_config *config; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -268,9 +277,40 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f) /*-------------------------------------------------------------------------*/ +struct audio_source_instance { + struct usb_function_instance func_inst; + const char *name; + struct audio_source_config *config; + struct device *audio_device; +}; + +static void audio_source_attr_release(struct config_item *item); + +static struct configfs_item_operations audio_source_item_ops = { + .release = audio_source_attr_release, +}; + +static struct config_item_type audio_source_func_type = { + .ct_item_ops = &audio_source_item_ops, + .ct_owner = THIS_MODULE, +}; + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +/*--------------------------------------------------------------------------*/ + static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -338,10 +378,9 @@ static void audio_send(struct audio_dev *audio) /* compute number of frames to send */ now = ktime_get(); - msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); - do_div(msecs, 1000000); - frames = msecs * SAMPLE_RATE; - do_div(frames, 1000); + msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)), + 1000000); + frames = div_s64((msecs * SAMPLE_RATE), 1000); /* Readjust our frames_sent if we fall too far behind. * If we get too far behind it is better to drop some frames than @@ -561,6 +600,13 @@ static void audio_build_desc(struct audio_dev *audio) memcpy(sam_freq, &rate, 3); } + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config); +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi); + + /* audio function driver setup/binding */ static int audio_bind(struct usb_configuration *c, struct usb_function *f) @@ -571,6 +617,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct usb_request *req; int i; + int err; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + err = snd_card_setup(c, config); + if (err) + return err; + } audio_build_desc(audio); @@ -636,6 +694,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio->pcm = NULL; audio->substream = NULL; audio->in_ep = NULL; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + config->card = -1; + config->device = -1; + } } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -779,8 +847,6 @@ int audio_source_bind_config(struct usb_configuration *c, struct audio_source_config *config) { struct audio_dev *audio; - struct snd_card *card; - struct snd_pcm *pcm; int err; config->card = -1; @@ -788,6 +854,31 @@ int audio_source_bind_config(struct usb_configuration *c, audio = &_audio_dev; + err = snd_card_setup(c, config); + if (err) + return err; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + return 0; + +add_fail: + snd_card_free(audio->card); + return err; +} + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + audio = &_audio_dev; + err = snd_card_new(&c->cdev->gadget->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); @@ -817,18 +908,147 @@ int audio_source_bind_config(struct usb_configuration *c, if (err) goto register_fail; - err = usb_add_function(c, &audio->func); - if (err) - goto add_fail; - config->card = pcm->card->number; config->device = pcm->device; audio->card = card; return 0; -add_fail: register_fail: pcm_fail: snd_card_free(audio->card); return err; } + +static struct audio_source_instance *to_audio_source_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), struct audio_source_instance, + func_inst.group); +} + +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi) +{ + return container_of(fi, struct audio_source_instance, func_inst); +} + +static void audio_source_attr_release(struct config_item *item) +{ + struct audio_source_instance *fi_audio = to_audio_source_instance(item); + + usb_put_function_instance(&fi_audio->func_inst); +} + +static int audio_source_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct audio_source_instance *fi_audio; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_audio = to_fi_audio_source(fi); + fi_audio->name = ptr; + + return 0; +} + +static void audio_source_free_inst(struct usb_function_instance *fi) +{ + struct audio_source_instance *fi_audio; + + fi_audio = to_fi_audio_source(fi); + device_destroy(fi_audio->audio_device->class, + fi_audio->audio_device->devt); + kfree(fi_audio->name); + kfree(fi_audio->config); +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audio_source_instance *fi_audio = dev_get_drvdata(dev); + struct audio_source_config *config = fi_audio->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +struct device *create_function_device(char *name); + +static struct usb_function_instance *audio_source_alloc_inst(void) +{ + struct audio_source_instance *fi_audio; + struct device_attribute **attrs; + struct device_attribute *attr; + struct device *dev; + void *err_ptr; + int err = 0; + + fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); + if (!fi_audio) + return ERR_PTR(-ENOMEM); + + fi_audio->func_inst.set_inst_name = audio_source_set_inst_name; + fi_audio->func_inst.free_func_inst = audio_source_free_inst; + + fi_audio->config = kzalloc(sizeof(struct audio_source_config), + GFP_KERNEL); + if (!fi_audio->config) { + err_ptr = ERR_PTR(-ENOMEM); + goto fail_audio; + } + + config_group_init_type_name(&fi_audio->func_inst.group, "", + &audio_source_func_type); + dev = create_function_device("f_audio_source"); + + if (IS_ERR(dev)) { + err_ptr = dev; + goto fail_audio_config; + } + + fi_audio->config->card = -1; + fi_audio->config->device = -1; + fi_audio->audio_device = dev; + + attrs = audio_source_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + err_ptr = ERR_PTR(-EINVAL); + goto fail_device; + } + } + + dev_set_drvdata(dev, fi_audio); + _audio_dev.config = fi_audio->config; + + return &fi_audio->func_inst; + +fail_device: + device_destroy(dev->class, dev->devt); +fail_audio_config: + kfree(fi_audio->config); +fail_audio: + kfree(fi_audio); + return err_ptr; + +} + +static struct usb_function *audio_source_alloc(struct usb_function_instance *fi) +{ + return &_audio_dev.func; +} + +DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst, + audio_source_alloc); +MODULE_LICENSE("GPL"); From a3ab81aaa19e74d8e7779fb040a96243a4ca8637 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:49:55 -0700 Subject: [PATCH 0492/1276] ANDROID: usb: gadget: f_audio_source: Move gadget functions code 3.18 kernel has reorganized drivers/usb/gadget directory. Moving gadget functions drivers from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: I1eab0190f8d42e3be1b4e91ad3bc3a2dc853b0ef [AmitP: Refactored the original changes after squashing of related patches] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Makefile | 2 -- drivers/usb/gadget/function/Makefile | 2 ++ drivers/usb/gadget/{ => function}/f_audio_source.c | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename drivers/usb/gadget/{ => function}/f_audio_source.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index f019157a0908..130dad7130b6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,5 +11,3 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ -usb_f_audio_source-y := f_audio_source.o -obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..42a0b6ef39da 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -50,3 +50,5 @@ usb_f_printer-y := f_printer.o obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c similarity index 100% rename from drivers/usb/gadget/f_audio_source.c rename to drivers/usb/gadget/function/f_audio_source.c From af98f36edbe20f26067e4a8c9bd929127324a97b Mon Sep 17 00:00:00 2001 From: Konrad Leszczynski Date: Wed, 24 Feb 2016 10:47:12 +0000 Subject: [PATCH 0493/1276] ANDROID: usb: gadget: f_audio_source: disable the CPU C-states upon playback Due to the issue with the isoc transfers being interrupted by the CPU going into the idle state, the C-states will be disabled for the playback time. Change-Id: If4e52673606923d7e33a1d1dbe0192b8ad24f78c Signed-off-by: Konrad Leszczynski Signed-off-by: Russ Weight --- drivers/usb/gadget/function/f_audio_source.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 61b47b820a90..3cf60609a83e 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -268,6 +269,8 @@ struct audio_dev { /* number of frames sent since start_time */ s64 frames_sent; struct audio_source_config *config; + /* for creating and issuing QoS requests */ + struct pm_qos_request pm_qos; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -735,6 +738,10 @@ static int audio_pcm_open(struct snd_pcm_substream *substream) runtime->hw.channels_max = 2; audio->substream = substream; + + /* Add the QoS request and set the latency to 0 */ + pm_qos_add_request(&audio->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + return 0; } @@ -744,6 +751,10 @@ static int audio_pcm_close(struct snd_pcm_substream *substream) unsigned long flags; spin_lock_irqsave(&audio->lock, flags); + + /* Remove the QoS request */ + pm_qos_remove_request(&audio->pm_qos); + audio->substream = NULL; spin_unlock_irqrestore(&audio->lock, flags); From 580721fa6cbc767ac9d3c315a6377c8aad4f6a11 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 17:21:22 -0800 Subject: [PATCH 0494/1276] ANDROID: usb: gadget: f_accessory: Migrate to USB_FUNCTION API This patch adds support to use Android accessory gadget function through the DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib352752d5bc905fa1df9049b53eabf1294930db7 [AmitP: Folded following android-4.9 commit changes into this patch Parts of e45c769fa7af ("ANDROID: usb: gadget: cleanup: fix unused variable and function warnings") a0a752add9b5 ("ANDROID: usb: gadget: f_accessory: remove duplicate endpoint alloc") Parts of 051584e76d12 ("ANDROID: usb: gadget: function: cleanup: Add blank line after declaration")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 12 +- drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/f_accessory.c | 188 ++++++++++++++++++++++++------- 3 files changed, 161 insertions(+), 42 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a306bb3bef85..812800c255ec 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -218,6 +218,9 @@ config USB_F_TCM config USB_F_AUDIO_SRC tristate +config USB_F_ACC + tristate + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -371,9 +374,16 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_ACC + bool "Accessory gadget" + depends on USB_CONFIGFS + select USB_F_ACC + help + USB gadget Accessory support + config USB_CONFIGFS_F_AUDIO_SRC bool "Audio Source gadget" - depends on USB_CONFIGFS + depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC depends on SND select SND_PCM select USB_F_AUDIO_SRC diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 130dad7130b6..92dd916df04d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,4 +10,7 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o + obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 9ffe017bf1cf..ef1ffcf88cdc 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -39,6 +39,10 @@ #include #include +#include +#include + +#define MAX_INST_NAME_LEN 40 #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 @@ -194,6 +198,11 @@ static struct usb_gadget_strings *acc_strings[] = { /* temporary variable used between acc_open() and acc_gadget_bind() */ static struct acc_dev *_acc_dev; +struct acc_instance { + struct usb_function_instance func_inst; + const char *name; +}; + static inline struct acc_dev *func_to_dev(struct usb_function *f) { return container_of(f, struct acc_dev, function); @@ -202,6 +211,7 @@ static inline struct acc_dev *func_to_dev(struct usb_function *f) static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -511,15 +521,6 @@ static int create_bulk_endpoints(struct acc_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - /* now allocate requests for our endpoints */ for (i = 0; i < TX_REQ_MAX; i++) { req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); @@ -775,7 +776,7 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; -static int acc_ctrlrequest(struct usb_composite_dev *cdev, +int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; @@ -879,9 +880,11 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, w_value, w_index, w_length); return value; } +EXPORT_SYMBOL_GPL(acc_ctrlrequest); static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) +__acc_function_bind(struct usb_configuration *c, + struct usb_function *f, bool configfs) { struct usb_composite_dev *cdev = c->cdev; struct acc_dev *dev = func_to_dev(f); @@ -890,6 +893,16 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + if (configfs) { + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + dev->cdev = c->cdev; + } ret = hid_register_driver(&acc_hid_driver); if (ret) return ret; @@ -922,6 +935,12 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static int +acc_function_bind_configfs(struct usb_configuration *c, + struct usb_function *f) { + return __acc_function_bind(c, f, true); +} + static void kill_all_hid_devices(struct acc_dev *dev) { @@ -974,6 +993,7 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } @@ -1115,35 +1135,6 @@ static void acc_function_disable(struct usb_function *f) VDBG(cdev, "%s disabled\n", dev->function.name); } -static int acc_bind_config(struct usb_configuration *c) -{ - struct acc_dev *dev = _acc_dev; - int ret; - - printk(KERN_INFO "acc_bind_config\n"); - - /* allocate a string ID for our interface */ - if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - acc_string_defs[INTERFACE_STRING_INDEX].id = ret; - acc_interface_desc.iInterface = ret; - } - - dev->cdev = c->cdev; - dev->function.name = "accessory"; - dev->function.strings = acc_strings, - dev->function.fs_descriptors = fs_acc_descs; - dev->function.hs_descriptors = hs_acc_descs; - dev->function.bind = acc_function_bind; - dev->function.unbind = acc_function_unbind; - dev->function.set_alt = acc_function_set_alt; - dev->function.disable = acc_function_disable; - - return usb_add_function(c, &dev->function); -} - static int acc_setup(void) { struct acc_dev *dev; @@ -1179,11 +1170,12 @@ static int acc_setup(void) return ret; } -static void acc_disconnect(void) +void acc_disconnect(void) { /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); } +EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { @@ -1191,3 +1183,117 @@ static void acc_cleanup(void) kfree(_acc_dev); _acc_dev = NULL; } +static struct acc_instance *to_acc_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct acc_instance, + func_inst.group); +} + +static void acc_attr_release(struct config_item *item) +{ + struct acc_instance *fi_acc = to_acc_instance(item); + + usb_put_function_instance(&fi_acc->func_inst); +} + +static struct configfs_item_operations acc_item_ops = { + .release = acc_attr_release, +}; + +static struct config_item_type acc_func_type = { + .ct_item_ops = &acc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct acc_instance *to_fi_acc(struct usb_function_instance *fi) +{ + return container_of(fi, struct acc_instance, func_inst); +} + +static int acc_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct acc_instance *fi_acc; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_acc = to_fi_acc(fi); + fi_acc->name = ptr; + return 0; +} + +static void acc_free_inst(struct usb_function_instance *fi) +{ + struct acc_instance *fi_acc; + + fi_acc = to_fi_acc(fi); + kfree(fi_acc->name); + acc_cleanup(); +} + +static struct usb_function_instance *acc_alloc_inst(void) +{ + struct acc_instance *fi_acc; + struct acc_dev *dev; + int err; + + fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); + if (!fi_acc) + return ERR_PTR(-ENOMEM); + fi_acc->func_inst.set_inst_name = acc_set_inst_name; + fi_acc->func_inst.free_func_inst = acc_free_inst; + + err = acc_setup(); + if (err) { + kfree(fi_acc); + pr_err("Error setting ACCESSORY\n"); + return ERR_PTR(err); + } + + config_group_init_type_name(&fi_acc->func_inst.group, + "", &acc_func_type); + dev = _acc_dev; + return &fi_acc->func_inst; +} + +static void acc_free(struct usb_function *f) +{ +/*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +int acc_ctrlrequest_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { + if (f->config != NULL && f->config->cdev != NULL) + return acc_ctrlrequest(f->config->cdev, ctrl); + else + return -1; +} + +static struct usb_function *acc_alloc(struct usb_function_instance *fi) +{ + struct acc_dev *dev = _acc_dev; + + pr_info("acc_alloc\n"); + + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind_configfs; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + dev->function.free_func = acc_free; + dev->function.setup = acc_ctrlrequest_configfs; + + return &dev->function; +} +DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc); +MODULE_LICENSE("GPL"); From 675047ee68e93d0acbfdb6bc2d23d62ea225fd99 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 30 Mar 2015 15:32:22 -0700 Subject: [PATCH 0495/1276] ANDROID: usb: gadget: f_accessory: Move gadget functions code 3.18 kernel has reorganized drivers/usb/gadget directory. Moving accessory gadget driver from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: If73c6df0537c4b1f51338ed3b0db817e51f06b4a [AmitP: Refactored the original changes after squashing of related patches] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Makefile | 3 --- drivers/usb/gadget/function/Makefile | 2 ++ drivers/usb/gadget/{ => function}/f_accessory.c | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename drivers/usb/gadget/{ => function}/f_accessory.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 92dd916df04d..130dad7130b6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,7 +10,4 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -usb_f_accessory-y := f_accessory.o -obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o - obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 42a0b6ef39da..d7d5673d8343 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -52,3 +52,5 @@ usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o usb_f_audio_source-y := f_audio_source.o obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c similarity index 100% rename from drivers/usb/gadget/f_accessory.c rename to drivers/usb/gadget/function/f_accessory.c From ebc98ac5a22f5b1e8a161216af461ce19b378758 Mon Sep 17 00:00:00 2001 From: keunyoung Date: Wed, 29 Jan 2014 12:41:50 -0800 Subject: [PATCH 0496/1276] ANDROID: usb: gadget: f_accessory: fix false disconnect due to a signal sent to the reading process - In the current implementation, when a signal is sent to the reading process, read is cancelled by calling usb_ep_dequeue, which lead into calling acc_complete_out with ECONNRESET, but the current logic treats it as disconnection, which makes the device inaccessible until cable is actually disconnected. - The fix calls disconnect only when ESHUTDOWN error is passed. - If data has already arrived while trying cancelling, the data is marked as available, and it will be read out on the next read. This is necessary as USB bulk is assumed to guarantee no data loss. Signed-off-by: keunyoung --- drivers/usb/gadget/function/f_accessory.c | 32 +++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index ef1ffcf88cdc..efb22a3ffa3f 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -271,8 +271,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_in set disconnected"); acc_set_disconnected(dev); + } req_put(dev, &dev->tx_idle, req); @@ -284,8 +286,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = _acc_dev; dev->rx_done = 1; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_out set disconnected"); acc_set_disconnected(dev); + } wake_up(&dev->read_wq); } @@ -559,8 +563,10 @@ static ssize_t acc_read(struct file *fp, char __user *buf, pr_debug("acc_read(%zu)\n", count); - if (dev->disconnected) + if (dev->disconnected) { + pr_debug("acc_read disconnected"); return -ENODEV; + } if (count > BULK_BUFFER_SIZE) count = BULK_BUFFER_SIZE; @@ -573,6 +579,12 @@ static ssize_t acc_read(struct file *fp, char __user *buf, goto done; } + if (dev->rx_done) { + // last req cancelled. try to get it. + req = dev->rx_req[0]; + goto copy_data; + } + requeue_req: /* queue a request */ req = dev->rx_req[0]; @@ -590,9 +602,17 @@ static ssize_t acc_read(struct file *fp, char __user *buf, ret = wait_event_interruptible(dev->read_wq, dev->rx_done); if (ret < 0) { r = ret; - usb_ep_dequeue(dev->ep_out, req); + ret = usb_ep_dequeue(dev->ep_out, req); + if (ret != 0) { + // cancel failed. There can be a data already received. + // it will be retrieved in the next read. + pr_debug("acc_read: cancelling failed %d", ret); + } goto done; } + +copy_data: + dev->rx_done = 0; if (dev->online) { /* If we got a 0-len packet, throw it back and try again. */ if (req->actual == 0) @@ -622,8 +642,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, pr_debug("acc_write(%zu)\n", count); - if (!dev->online || dev->disconnected) + if (!dev->online || dev->disconnected) { + pr_debug("acc_write disconnected or not online"); return -ENODEV; + } while (count > 0) { if (!dev->online) { From 7f407172fb289075dd43def3b4d56374d1ee46bb Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Fri, 12 Aug 2016 20:38:10 -0400 Subject: [PATCH 0497/1276] ANDROID: usb: gadget: f_accessory: Fix for UsbAccessory clean unbind. Reapplying fix by Darren Whobrey (Change 69674) Fixes issues: 20545, 59667 and 61390. With prior version of f_accessory.c, UsbAccessories would not unbind cleanly when application is closed or i/o stopped while the usb cable is still connected. The accessory gadget driver would be left in an invalid state which was not reset on subsequent binding or opening. A reboot was necessary to clear. In some phones this issues causes the phone to reboot upon unplugging the USB cable. Main problem was that acc_disconnect was being called on I/O error which reset disconnected and online. Minor fix required to properly track setting and unsetting of disconnected and online flags. Also added urb Q wakeup's on unbind to help unblock waiting threads. Tested on Nexus 7 grouper. Expected behaviour now observed: closing accessory causes blocked i/o to interrupt with IOException. Accessory can be restarted following closing of file handle and re-opening. This is a generic fix that applies to all devices. Change-Id: I4e08b326730dd3a2820c863124cee10f7cb5501e Signed-off-by: Darren Whobrey Signed-off-by: Anson Jacob --- drivers/usb/gadget/function/f_accessory.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index efb22a3ffa3f..e0a677a44763 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -77,9 +77,13 @@ struct acc_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; - /* set to 1 when we connect */ + /* online indicates state of function_set_alt & function_unbind + * set to 1 when we connect + */ int online:1; - /* Set to 1 when we disconnect. + + /* disconnected indicates state of open & release + * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; @@ -263,7 +267,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) static void acc_set_disconnected(struct acc_dev *dev) { - dev->online = 0; dev->disconnected = 1; } @@ -756,7 +759,10 @@ static int acc_release(struct inode *ip, struct file *fp) printk(KERN_INFO "acc_release\n"); WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); - _acc_dev->disconnected = 0; + /* indicate that we are disconnected + * still could be online so don't touch online flag + */ + _acc_dev->disconnected = 1; return 0; } @@ -1004,6 +1010,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + dev->online = 0; /* clear online flag */ + wake_up(&dev->read_wq); /* unblock reads on closure */ + wake_up(&dev->write_wq); /* likewise for writes */ + while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1135,6 +1145,7 @@ static int acc_function_set_alt(struct usb_function *f, } dev->online = 1; + dev->disconnected = 0; /* if online then not disconnected */ /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -1147,7 +1158,8 @@ static void acc_function_disable(struct usb_function *f) struct usb_composite_dev *cdev = dev->cdev; DBG(cdev, "acc_function_disable\n"); - acc_set_disconnected(dev); + acc_set_disconnected(dev); /* this now only sets disconnected */ + dev->online = 0; /* so now need to clear online flag here too */ usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); From 71c6dc5ffdab31a8f74aae2e5827406c19530d4a Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 21 Jul 2017 14:58:16 -0700 Subject: [PATCH 0498/1276] ANDROID: usb: gadget: f_accessory: assign no-op request complete callbacks The req->complete seems to presist the callback pointer for the control requests. This causes the serial of the accessory to be overridden when an accessory function specific out control request is issued right after the ACCESSORY_SEND_STRING control request. Therefore, assign a no-op req complete function when nothing needs to be done once the request is completed. Signed-off-by: Badhri Jagan Sridharan Bug: 63867169 Change-Id: I78c1602e9a044b8718b270b8a068cf5afc83f984 --- drivers/usb/gadget/function/f_accessory.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index e0a677a44763..b22b77c8aef4 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -804,6 +804,14 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; +static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req) +{ + /* + * Default no-op function when nothing needs to be done for the + * setup request + */ +} + int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { @@ -831,6 +839,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, schedule_delayed_work( &dev->start_work, msecs_to_jiffies(10)); value = 0; + cdev->req->complete = acc_complete_setup_noop; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; cdev->gadget->ep0->driver_data = dev; @@ -839,10 +848,13 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, } else if (b_request == ACCESSORY_SET_AUDIO_MODE && w_index == 0 && w_length == 0) { dev->audio_mode = w_value; + cdev->req->complete = acc_complete_setup_noop; value = 0; } else if (b_request == ACCESSORY_REGISTER_HID) { + cdev->req->complete = acc_complete_setup_noop; value = acc_register_hid(dev, w_value, w_index); } else if (b_request == ACCESSORY_UNREGISTER_HID) { + cdev->req->complete = acc_complete_setup_noop; value = acc_unregister_hid(dev, w_value); } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { spin_lock_irqsave(&dev->lock, flags); @@ -877,7 +889,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_GET_PROTOCOL) { *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; value = sizeof(u16); - + cdev->req->complete = acc_complete_setup_noop; /* clear any string left over from a previous session */ memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); memset(dev->model, 0, sizeof(dev->model)); From 754bf27382ed0b27ef2b439a68e10191b5f0e070 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 2 Sep 2015 22:49:10 -0700 Subject: [PATCH 0499/1276] ANDROID: usb: gadget: f_midi: create F_midi device Android frameworks relies on the alsa config reported by the f_midi device. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0695e00b166fd953f50acea93802245b0d5a5240 --- drivers/usb/gadget/function/f_midi.c | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 46af0aa07e2e..2a86b3cf313f 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1216,6 +1216,65 @@ static void f_midi_free_inst(struct usb_function_instance *f) } } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +extern struct device *create_function_device(char *name); +static ssize_t alsa_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_function_instance *fi_midi = dev_get_drvdata(dev); + struct f_midi *midi; + + if (!fi_midi->f) + dev_warn(dev, "f_midi: function not set\n"); + + if (fi_midi && fi_midi->f) { + midi = func_to_midi(fi_midi->f); + if (midi->rmidi && midi->rmidi->card) + return sprintf(buf, "%d %d\n", + midi->rmidi->card->number, midi->rmidi->device); + } + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", -1, -1); +} + +static DEVICE_ATTR(alsa, S_IRUGO, alsa_show, NULL); + +static struct device_attribute *alsa_function_attributes[] = { + &dev_attr_alsa, + NULL +}; + +static int create_alsa_device(struct usb_function_instance *fi) +{ + struct device *dev; + struct device_attribute **attrs; + struct device_attribute *attr; + int err = 0; + + dev = create_function_device("f_midi"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + attrs = alsa_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + device_destroy(dev->class, dev->devt); + return -EINVAL; + } + } + dev_set_drvdata(dev, fi); + return 0; +} +#else +static int create_alsa_device(struct usb_function_instance *fi) +{ + return 0; +} +#endif + static struct usb_function_instance *f_midi_alloc_inst(void) { struct f_midi_opts *opts; @@ -1234,6 +1293,11 @@ static struct usb_function_instance *f_midi_alloc_inst(void) opts->out_ports = 1; opts->refcnt = 1; + if (create_alsa_device(&opts->func_inst)) { + kfree(opts); + return ERR_PTR(-ENODEV); + } + config_group_init_type_name(&opts->func_inst.group, "", &midi_func_type); @@ -1341,6 +1405,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->func.disable = f_midi_disable; midi->func.free_func = f_midi_free; + fi->f = &midi->func; return &midi->func; setup_fail: From b99861330bff36f993d827262acb2078c04a1b54 Mon Sep 17 00:00:00 2001 From: Winter Wang Date: Fri, 20 May 2016 11:05:00 +0800 Subject: [PATCH 0500/1276] ANDROID: usb: gadget: f_midi: set fi->f to NULL when free f_midi function fi->f is set in f_midi's alloc_func, need to clean this to NULL in free_func, otherwise on ConfigFS's function switch, midi->usb_function it self is freed, fi->f will be a wild pointer and run into below kernel panic: --------------- [ 58.950628] Unable to handle kernel paging request at virtual address 63697664 [ 58.957869] pgd = c0004000 [ 58.960583] [63697664] *pgd=00000000 [ 58.964185] Internal error: Oops: 80000005 [#1] PREEMPT SMP ARM [ 58.970111] Modules linked in: [ 58.973191] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.1.15-03504-g34c857c-dirty #89 [ 58.981024] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [ 58.987557] task: c110bd70 ti: c1100000 task.ti: c1100000 [ 58.992962] PC is at 0x63697664 [ 58.996120] LR is at android_setup+0x78/0x138 <..snip..> [ 60.044980] 1fc0: ffffffff ffffffff c1000684 00000000 00000000 c108ecd0 c11f7294 c11039c0 [ 60.053181] 1fe0: c108eccc c110d148 1000406a 412fc09a 00000000 1000807c 00000000 00000000 [ 60.061420] [] (android_setup) from [] (udc_irq+0x758/0x1034) [ 60.068951] [] (udc_irq) from [] (handle_irq_event_percpu+0x50/0x254) [ 60.077165] [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [ 60.086072] [] (handle_irq_event) from [] (handle_fasteoi_irq+0xe0/0x198) [ 60.094630] [] (handle_fasteoi_irq) from [] (generic_handle_irq+0x2c/0x3c) [ 60.103271] [] (generic_handle_irq) from [] (__handle_domain_irq+0x7c/0xec) [ 60.112000] [] (__handle_domain_irq) from [] (gic_handle_irq+0x24/0x5c) -------------- Signed-off-by: Winter Wang --- drivers/usb/gadget/function/f_midi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 2a86b3cf313f..4713a1c7f622 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1318,6 +1318,7 @@ static void f_midi_free(struct usb_function *f) kfifo_free(&midi->in_req_fifo); kfree(midi); free = true; + opts->func_inst.f = NULL; } mutex_unlock(&opts->lock); From a830751a338e0f82e8f8bf14c05baad7aeb37aae Mon Sep 17 00:00:00 2001 From: Mark Kuo Date: Mon, 11 Jan 2016 17:49:16 +0800 Subject: [PATCH 0501/1276] CHROMIUM: usb: gadget: f_audio_source: add .free_func callback When userspace unbinds gadget functions through configfs, the .free_func() callback is always invoked. (in config_usb_cfg_unlink()) Implement it as a no-op to avoid the following crash: [ 68.125679] configfs-gadget gadget: unbind function 'accessory'/ffffffc0720bf000 [ 68.133202] configfs-gadget gadget: unbind function 'audio_source'/ffffffc0012ca3c0 [ 68.142668] tegra-xudc 700d0000.usb-device: ep 0 disabled [ 68.148186] Bad mode in Synchronous Abort handler detected, code 0x86000006 [ 68.155144] CPU: 2 PID: 1 Comm: init Tainted: G U W 3.18.0-09419-g87296c3-dirty #561 [ 68.163743] Hardware name: Google Tegra210 Smaug Rev 1,3+ (DT) [ 68.169566] task: ffffffc0bc8d0000 ti: ffffffc0bc8bc000 task.ti: ffffffc0bc8bc000 [ 68.177039] PC is at 0x0 [ 68.179577] LR is at usb_put_function+0x14/0x1c .... BUG=chrome-os-partner:49140 TEST="setprop sys.usb.config accessory,audio_source" on A44 and then switch back to default: "setprop sys.usb.config mtp,adb", no crash will be seen. Change-Id: I5b6141964aab861e86e3afb139ded02d4d122dab Signed-off-by: Mark Kuo Reviewed-on: https://chromium-review.googlesource.com/321013 Commit-Ready: Andrew Bresticker Tested-by: Andrew Bresticker Reviewed-by: Andrew Bresticker --- drivers/usb/gadget/function/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 3cf60609a83e..8124af33b738 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -586,6 +586,11 @@ static void audio_disable(struct usb_function *f) usb_ep_disable(audio->in_ep); } +static void audio_free_func(struct usb_function *f) +{ + /* no-op */ +} + /*-------------------------------------------------------------------------*/ static void audio_build_desc(struct audio_dev *audio) @@ -838,6 +843,7 @@ static struct audio_dev _audio_dev = { .set_alt = audio_set_alt, .setup = audio_setup, .disable = audio_disable, + .free_func = audio_free_func, }, .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), From b2bedaa5c7dfc2de62d8a006f4a4bb850e22e330 Mon Sep 17 00:00:00 2001 From: Mark Kuo Date: Mon, 11 Jan 2016 19:07:12 +0800 Subject: [PATCH 0502/1276] CHROMIUM: usb: gadget: f_accessory: add .raw_request callback After this upstream commit: 3c86726cfe38952f0366f86acfbbb025813ec1c2, .raw_request is mandatory in hid_ll_driver structure, hence add an empty raw_request() function. BUG=chrome-os-partner:49140 TEST=none Change-Id: Idd0bbe6960aad2c557376e4a24827d7e1df8e023 Signed-off-by: Mark Kuo Reviewed-on: https://chromium-review.googlesource.com/321038 Commit-Ready: Andrew Bresticker Tested-by: Andrew Bresticker Reviewed-by: Andrew Bresticker --- drivers/usb/gadget/function/f_accessory.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index b22b77c8aef4..7aa2656a2328 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -408,12 +408,19 @@ static void acc_hid_close(struct hid_device *hid) { } +static int acc_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, int reqtype) +{ + return 0; +} + static struct hid_ll_driver acc_hid_ll_driver = { .parse = acc_hid_parse, .start = acc_hid_start, .stop = acc_hid_stop, .open = acc_hid_open, .close = acc_hid_close, + .raw_request = acc_hid_raw_request, }; static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, From f7d3f1a5e9c2d3e651c4d304cebf32897b366d56 Mon Sep 17 00:00:00 2001 From: jinqian Date: Wed, 11 Mar 2015 10:44:50 -0700 Subject: [PATCH 0503/1276] ANDROID: uid_cputime: Adds accounting for the cputimes per uid. Adds proc files /proc/uid_cputime/show_uid_stat and /proc/uid_cputime/remove_uid_range. show_uid_stat lists the total utime and stime for the active as well as terminated processes for each of the uids. Writing a range of uids to remove_uid_range will delete the accounting for all the uids within that range. Change-Id: I21d9210379da730b33ddc1a0ea663c8c9d2ac15b [AmitP: Refactored the original patch because upstream commit 605dc2b31a2a ("tsacct: Convert obsolete cputime type to nsecs") made cputime_t type obsolete, so use u64 nanoseconds directly instead. Also folded following android-4.9 changes into this patch 48a9906c0fd8 ("ANDROID: proc: uid_cputime: create uids from kuids") 453ac31cab34 ("ANDROID: proc: uid_cputime: fix show_uid_stat permission")] Signed-off-by: Amit Pundir --- drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/uid_cputime.c | 237 +++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 drivers/misc/uid_cputime.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3726eacdf65d..9cb0c65577ec 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -513,6 +513,12 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB +config UID_CPUTIME + bool "Per-UID cpu time statistics" + depends on PROFILING + help + Per UID based cpu time statistics exported to /proc/uid_cputime + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af22bbc3d00c..d3dd7eb910d2 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ +obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c new file mode 100644 index 000000000000..e0f0b5cb0baa --- /dev/null +++ b/drivers/misc/uid_cputime.c @@ -0,0 +1,237 @@ +/* drivers/misc/uid_cputime.c + * + * Copyright (C) 2014 - 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UID_HASH_BITS 10 +DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); + +static DEFINE_MUTEX(uid_lock); +static struct proc_dir_entry *parent; + +struct uid_entry { + uid_t uid; + u64 utime; + u64 stime; + u64 active_utime; + u64 active_stime; + struct hlist_node hash; +}; + +static struct uid_entry *find_uid_entry(uid_t uid) +{ + struct uid_entry *uid_entry; + hash_for_each_possible(hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +static struct uid_entry *find_or_register_uid(uid_t uid) +{ + struct uid_entry *uid_entry; + + uid_entry = find_uid_entry(uid); + if (uid_entry) + return uid_entry; + + uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + + hash_add(hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static int uid_stat_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct task_struct *task; + u64 utime; + u64 stime; + unsigned long bkt; + + mutex_lock(&uid_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + uid_entry->active_stime = 0; + uid_entry->active_utime = 0; + } + + read_lock(&tasklist_lock); + for_each_process(task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) { + read_unlock(&tasklist_lock); + mutex_unlock(&uid_lock); + pr_err("%s: failed to find the uid_entry for uid %d\n", + __func__, from_kuid_munged(current_user_ns(), + task_uid(task))); + return -ENOMEM; + } + task_cputime_adjusted(task, &utime, &stime); + uid_entry->active_utime += utime; + uid_entry->active_stime += stime; + } + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + u64 total_utime = uid_entry->utime + + uid_entry->active_utime; + u64 total_stime = uid_entry->stime + + uid_entry->active_stime; + seq_printf(m, "%d: %u %u\n", uid_entry->uid, + cputime_to_usecs(total_utime), + cputime_to_usecs(total_stime)); + } + + mutex_unlock(&uid_lock); + return 0; +} + +static int uid_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_stat_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_stat_fops = { + .open = uid_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_remove_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_remove_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + char uids[128]; + char *start_uid, *end_uid = NULL; + long int uid_start = 0, uid_end = 0; + + if (count >= sizeof(uids)) + count = sizeof(uids) - 1; + + if (copy_from_user(uids, buffer, count)) + return -EFAULT; + + uids[count] = '\0'; + end_uid = uids; + start_uid = strsep(&end_uid, "-"); + + if (!start_uid || !end_uid) + return -EINVAL; + + if (kstrtol(start_uid, 10, &uid_start) != 0 || + kstrtol(end_uid, 10, &uid_end) != 0) { + return -EINVAL; + } + + mutex_lock(&uid_lock); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(hash_table, uid_entry, tmp, + hash, uid_start) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } + } + + mutex_unlock(&uid_lock); + return count; +} + +static const struct file_operations uid_remove_fops = { + .open = uid_remove_open, + .release = single_release, + .write = uid_remove_write, +}; + +static int process_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct task_struct *task = v; + struct uid_entry *uid_entry; + u64 utime, stime; + uid_t uid; + + if (!task) + return NOTIFY_OK; + + mutex_lock(&uid_lock); + uid = from_kuid_munged(current_user_ns(), task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + pr_err("%s: failed to find uid %d\n", __func__, uid); + goto exit; + } + + task_cputime_adjusted(task, &utime, &stime); + uid_entry->utime += utime; + uid_entry->stime += stime; + +exit: + mutex_unlock(&uid_lock); + return NOTIFY_OK; +} + +static struct notifier_block process_notifier_block = { + .notifier_call = process_notifier, +}; + +static int __init proc_uid_cputime_init(void) +{ + hash_init(hash_table); + + parent = proc_mkdir("uid_cputime", NULL); + if (!parent) { + pr_err("%s: failed to create proc entry\n", __func__); + return -ENOMEM; + } + + proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, + NULL); + + proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, + NULL); + + profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); + + return 0; +} + +early_initcall(proc_uid_cputime_init); From da3687fbc3935c6e98f695041700faaf2e2fa6f6 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 13 Jul 2015 18:16:55 -0700 Subject: [PATCH 0504/1276] ANDROID: uid_cputime: fix cputime overflow Converting cputime_t to usec caused overflow when the value is greater than 1 hour. Use msec and convert to unsigned long long to support bigger range. Bug: 22461683 Change-Id: I853fe3e8e7dbf0d3e2cc5c6f9688a5a6e1f1fb3e Signed-off-by: Jin Qian [AmitP: Refactored the original changes because cputime_to_jiffies() is obsolete and removed in upstream commit f22d6df0b23e ("sched/cputime: Remove jiffies based cputime")] Signed-off-by: Amit Pundir --- drivers/misc/uid_cputime.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index e0f0b5cb0baa..0df8d70b4e12 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -108,9 +108,8 @@ static int uid_stat_show(struct seq_file *m, void *v) uid_entry->active_utime; u64 total_stime = uid_entry->stime + uid_entry->active_stime; - seq_printf(m, "%d: %u %u\n", uid_entry->uid, - cputime_to_usecs(total_utime), - cputime_to_usecs(total_stime)); + seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, + ktime_to_ms(total_utime), ktime_to_ms(total_stime)); } mutex_unlock(&uid_lock); From 71e3eb512d0030a60d9968984048347464e5ea2d Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 31 Jul 2015 10:17:54 -0700 Subject: [PATCH 0505/1276] ANDROID: uid_cputime: Iterates over all the threads instead of processes. Bug: 22833116 Change-Id: I775a18f61bd2f4df2bec23d01bd49421d0969f87 Signed-off-by: Ruchi Kandoi --- drivers/misc/uid_cputime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 0df8d70b4e12..07c8cca0b172 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -73,7 +73,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) static int uid_stat_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; - struct task_struct *task; + struct task_struct *task, *temp; u64 utime; u64 stime; unsigned long bkt; @@ -86,7 +86,7 @@ static int uid_stat_show(struct seq_file *m, void *v) } read_lock(&tasklist_lock); - for_each_process(task) { + do_each_thread(temp, task) { uid_entry = find_or_register_uid(from_kuid_munged( current_user_ns(), task_uid(task))); if (!uid_entry) { @@ -100,7 +100,7 @@ static int uid_stat_show(struct seq_file *m, void *v) task_cputime_adjusted(task, &utime, &stime); uid_entry->active_utime += utime; uid_entry->active_stime += stime; - } + } while_each_thread(temp, task); read_unlock(&tasklist_lock); hash_for_each(hash_table, bkt, uid_entry, hash) { From c65f0080c3007492beddfbb3467e069d2a90f59f Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 23 Oct 2015 17:49:11 -0700 Subject: [PATCH 0506/1276] ANDROID: uid_cputime: Check for the range while removing range of UIDs. Checking if the uid_entry->uid matches the uid intended to be removed will prevent deleting unwanted uid_entry. Type cast the key for the hashtable to the same size, as when they were inserted. This will make sure that we can find the uid_entry we want. Bug: 25195548 Change-Id: I567942123cfb20e4b61ad624da19ec4cc84642c1 Signed-off: Ruchi kandoi --- drivers/misc/uid_cputime.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 07c8cca0b172..869d7c67d81b 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -159,14 +159,15 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } - mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { hash_for_each_possible_safe(hash_table, uid_entry, tmp, - hash, uid_start) { - hash_del(&uid_entry->hash); - kfree(uid_entry); + hash, (uid_t)uid_start) { + if (uid_start == uid_entry->uid) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } } } From 95536418d4086caccfc0039e59d6279485c0e661 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:10:35 -0800 Subject: [PATCH 0507/1276] ANDROID: uid_cputime: add per-uid IO usage accounting IO usages are accounted in foreground and background buckets. For each uid, io usage is calculated in two steps. delta = current total of all uid tasks - previus total current bucket += delta Bucket is determined by current uid stat. Userspace writes to /proc/uid_procstat/set when uid stat is updated. /proc/uid_io/stats shows IO usage in this format. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: Ib8bebda53e7a56f45ea3eb0ec9a3153d44188102 --- drivers/misc/uid_cputime.c | 236 ++++++++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 16 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 869d7c67d81b..6a61c8fb0642 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -30,7 +30,24 @@ DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); static DEFINE_MUTEX(uid_lock); -static struct proc_dir_entry *parent; +static struct proc_dir_entry *cpu_parent; +static struct proc_dir_entry *io_parent; +static struct proc_dir_entry *proc_parent; + +struct io_stats { + u64 read_bytes; + u64 write_bytes; + u64 rchar; + u64 wchar; +}; + +#define UID_STATE_FOREGROUND 0 +#define UID_STATE_BACKGROUND 1 +#define UID_STATE_BUCKET_SIZE 2 + +#define UID_STATE_TOTAL_CURR 2 +#define UID_STATE_TOTAL_LAST 3 +#define UID_STATE_SIZE 4 struct uid_entry { uid_t uid; @@ -38,6 +55,8 @@ struct uid_entry { u64 stime; u64 active_utime; u64 active_stime; + int state; + struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; }; @@ -70,7 +89,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return uid_entry; } -static int uid_stat_show(struct seq_file *m, void *v) +static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; @@ -116,13 +135,13 @@ static int uid_stat_show(struct seq_file *m, void *v) return 0; } -static int uid_stat_open(struct inode *inode, struct file *file) +static int uid_cputime_open(struct inode *inode, struct file *file) { - return single_open(file, uid_stat_show, PDE_DATA(inode)); + return single_open(file, uid_cputime_show, PDE_DATA(inode)); } -static const struct file_operations uid_stat_fops = { - .open = uid_stat_open, +static const struct file_operations uid_cputime_fops = { + .open = uid_cputime_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -181,6 +200,162 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static void add_uid_io_curr_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + + io_curr->read_bytes += task->ioac.read_bytes; + io_curr->write_bytes += + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->rchar += task->ioac.rchar; + io_curr->wchar += task->ioac.wchar; +} + +static void clean_uid_io_last_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_last->read_bytes -= task->ioac.read_bytes; + io_last->write_bytes -= + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->rchar -= task->ioac.rchar; + io_last->wchar -= task->ioac.wchar; +} + +static void update_io_stats_locked(void) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + unsigned long bkt; + + BUG_ON(!mutex_is_locked(&uid_lock)); + + hash_for_each(hash_table, bkt, uid_entry, hash) + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + } +} + +static int uid_io_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + unsigned long bkt; + + mutex_lock(&uid_lock); + + update_io_stats_locked(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + } + + mutex_unlock(&uid_lock); + + return 0; +} + +static int uid_io_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_io_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_io_fops = { + .open = uid_io_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_procstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_procstat_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + uid_t uid; + int argc, state; + char input[128]; + + if (count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + + argc = sscanf(input, "%u %d", &uid, &state); + if (argc != 2) + return -EINVAL; + + if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) + return -EINVAL; + + mutex_lock(&uid_lock); + + uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->state == state) { + mutex_unlock(&uid_lock); + return -EINVAL; + } + + update_io_stats_locked(); + + uid_entry->state = state; + + mutex_unlock(&uid_lock); + + return count; +} + +static const struct file_operations uid_procstat_fops = { + .open = uid_procstat_open, + .release = single_release, + .write = uid_procstat_write, +}; + static int process_notifier(struct notifier_block *self, unsigned long cmd, void *v) { @@ -204,6 +379,9 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; + update_io_stats_locked(); + clean_uid_io_last_stats(uid_entry, task); + exit: mutex_unlock(&uid_lock); return NOTIFY_OK; @@ -213,25 +391,51 @@ static struct notifier_block process_notifier_block = { .notifier_call = process_notifier, }; -static int __init proc_uid_cputime_init(void) +static int __init proc_uid_sys_stats_init(void) { hash_init(hash_table); - parent = proc_mkdir("uid_cputime", NULL); - if (!parent) { - pr_err("%s: failed to create proc entry\n", __func__); - return -ENOMEM; + cpu_parent = proc_mkdir("uid_cputime", NULL); + if (!cpu_parent) { + pr_err("%s: failed to create uid_cputime proc entry\n", + __func__); + goto err; } - proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, - NULL); + proc_create_data("remove_uid_range", 0222, cpu_parent, + &uid_remove_fops, NULL); + proc_create_data("show_uid_stat", 0444, cpu_parent, + &uid_cputime_fops, NULL); - proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, - NULL); + io_parent = proc_mkdir("uid_io", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_io proc entry\n", + __func__); + goto err; + } + + proc_create_data("stats", 0444, io_parent, + &uid_io_fops, NULL); + + proc_parent = proc_mkdir("uid_procstat", NULL); + if (!proc_parent) { + pr_err("%s: failed to create uid_procstat proc entry\n", + __func__); + goto err; + } + + proc_create_data("set", 0222, proc_parent, + &uid_procstat_fops, NULL); profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); return 0; + +err: + remove_proc_subtree("uid_cputime", NULL); + remove_proc_subtree("uid_io", NULL); + remove_proc_subtree("uid_procstat", NULL); + return -ENOMEM; } -early_initcall(proc_uid_cputime_init); +early_initcall(proc_uid_sys_stats_init); From 215d9740c7e082ad5439836a386c5f239a57ece1 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:11:07 -0800 Subject: [PATCH 0508/1276] ANDROID: uid_sys_stats: rename uid_cputime.c to uid_sys_stats.c This module tracks cputime and io stats. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: I9ee7d9e915431e0bb714b36b5a2282e1fdcc7342 --- drivers/misc/Kconfig | 6 ++++-- drivers/misc/Makefile | 2 +- drivers/misc/{uid_cputime.c => uid_sys_stats.c} | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename drivers/misc/{uid_cputime.c => uid_sys_stats.c} (100%) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9cb0c65577ec..ca1cbe5ec2e7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -513,11 +513,13 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB -config UID_CPUTIME - bool "Per-UID cpu time statistics" +config UID_SYS_STATS + bool "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime + Per UID based io statistics exported to /proc/uid_io + Per UID based procstat control in /proc/uid_procstat source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d3dd7eb910d2..fca6aae030f0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,4 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ -obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o +obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_sys_stats.c similarity index 100% rename from drivers/misc/uid_cputime.c rename to drivers/misc/uid_sys_stats.c From e003a91d8f42718fe9b3b0cdbbf765581e757ce3 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 17 Jan 2017 17:26:07 -0800 Subject: [PATCH 0509/1276] ANDROID: uid_sys_stats: allow writing same state Signed-off-by: Jin Qian Bug: 34360629 Change-Id: Ia748351e07910b1febe54f0484ca1be58c4eb9c7 --- drivers/misc/uid_sys_stats.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6a61c8fb0642..c93045cab241 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -336,11 +336,16 @@ static ssize_t uid_procstat_write(struct file *file, mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); - if (!uid_entry || uid_entry->state == state) { + if (!uid_entry) { mutex_unlock(&uid_lock); return -EINVAL; } + if (uid_entry->state == state) { + mutex_unlock(&uid_lock); + return count; + } + update_io_stats_locked(); uid_entry->state = state; From 89b984bf2efd16b4b3ca8520f11b18cbd90be397 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 28 Feb 2017 15:09:42 -0800 Subject: [PATCH 0510/1276] ANDROID: uid_sys_stats: fix negative write bytes. A task can cancel writes made by other tasks. In rare cases, cancelled_write_bytes is larger than write_bytes if the task itself didn't make any write. This doesn't affect total size but may cause confusion when looking at IO usage on individual tasks. Bug: 35851986 Change-Id: If6cb549aeef9e248e18d804293401bb2b91918ca Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index c93045cab241..dcf82a1e83bc 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -200,14 +200,21 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + static void add_uid_io_curr_stats(struct uid_entry *uid_entry, struct task_struct *task) { struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; } @@ -218,8 +225,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; } From bf0e62ac9d69b9687ab0e4657d85d9933671a07a Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:39:43 -0800 Subject: [PATCH 0511/1276] ANDROID: uid_sys_stats: account for fsync syscalls Change-Id: Ie888d8a0f4ec7a27dea86dc4afba8e6fd4203488 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index dcf82a1e83bc..8764077a6e58 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -39,6 +39,7 @@ struct io_stats { u64 write_bytes; u64 rchar; u64 wchar; + u64 fsync; }; #define UID_STATE_FOREGROUND 0 @@ -217,6 +218,7 @@ static void add_uid_io_curr_stats(struct uid_entry *uid_entry, io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; + io_curr->fsync += task->ioac.syscfs; } static void clean_uid_io_last_stats(struct uid_entry *uid_entry, @@ -228,6 +230,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; + io_last->fsync -= task->ioac.syscfs; } static void update_io_stats_locked(void) @@ -264,11 +267,13 @@ static void update_io_stats_locked(void) io_curr->write_bytes - io_last->write_bytes; io_bucket->rchar += io_curr->rchar - io_last->rchar; io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; io_last->read_bytes = io_curr->read_bytes; io_last->write_bytes = io_curr->write_bytes; io_last->rchar = io_curr->rchar; io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; } } @@ -282,7 +287,7 @@ static int uid_io_show(struct seq_file *m, void *v) update_io_stats_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { - seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", uid_entry->uid, uid_entry->io[UID_STATE_FOREGROUND].rchar, uid_entry->io[UID_STATE_FOREGROUND].wchar, @@ -291,7 +296,9 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].rchar, uid_entry->io[UID_STATE_BACKGROUND].wchar, uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); } mutex_unlock(&uid_lock); From bb5ee21cc4dd6d55acb86c9abdf3be8881e88dce Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 13 Mar 2017 12:22:21 -0700 Subject: [PATCH 0512/1276] ANDROID: uid_sys_stats: change to use rt_mutex We see this happens multiple times in heavy workload in systrace and AMS stuck in uid_lock. Running process: Process 953 Running thread: android.ui State: Uninterruptible Sleep Start: 1,025.628 ms Duration: 27,955.949 ms On CPU: Running instead: system_server Args: {kernel callsite when blocked:: "uid_procstat_write+0xb8/0x144"} Changing to rt_mutex can mitigate the priority inversion Bug: 34991231 Bug: 34193533 Test: on marlin Change-Id: I28eb3971331cea60b1075740c792ab87d103262c Signed-off-by: Wei Wang --- drivers/misc/uid_sys_stats.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 8764077a6e58..2f4572fe8fd1 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -21,15 +21,17 @@ #include #include #include +#include #include #include #include #include + #define UID_HASH_BITS 10 DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); -static DEFINE_MUTEX(uid_lock); +static DEFINE_RT_MUTEX(uid_lock); static struct proc_dir_entry *cpu_parent; static struct proc_dir_entry *io_parent; static struct proc_dir_entry *proc_parent; @@ -98,7 +100,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) u64 stime; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); hash_for_each(hash_table, bkt, uid_entry, hash) { uid_entry->active_stime = 0; @@ -111,7 +113,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) current_user_ns(), task_uid(task))); if (!uid_entry) { read_unlock(&tasklist_lock); - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, from_kuid_munged(current_user_ns(), task_uid(task))); @@ -132,7 +134,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) ktime_to_ms(total_utime), ktime_to_ms(total_stime)); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -179,7 +181,7 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { hash_for_each_possible_safe(hash_table, uid_entry, tmp, @@ -191,7 +193,7 @@ static ssize_t uid_remove_write(struct file *file, } } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -240,7 +242,7 @@ static void update_io_stats_locked(void) struct io_stats *io_bucket, *io_curr, *io_last; unsigned long bkt; - BUG_ON(!mutex_is_locked(&uid_lock)); + BUG_ON(!rt_mutex_is_locked(&uid_lock)); hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, @@ -282,7 +284,7 @@ static int uid_io_show(struct seq_file *m, void *v) struct uid_entry *uid_entry; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); update_io_stats_locked(); @@ -301,7 +303,7 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].fsync); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -346,16 +348,16 @@ static ssize_t uid_procstat_write(struct file *file, if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) return -EINVAL; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); if (!uid_entry) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return -EINVAL; } if (uid_entry->state == state) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -363,7 +365,7 @@ static ssize_t uid_procstat_write(struct file *file, uid_entry->state = state; - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -385,7 +387,7 @@ static int process_notifier(struct notifier_block *self, if (!task) return NOTIFY_OK; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid = from_kuid_munged(current_user_ns(), task_uid(task)); uid_entry = find_or_register_uid(uid); if (!uid_entry) { @@ -401,7 +403,7 @@ static int process_notifier(struct notifier_block *self, clean_uid_io_last_stats(uid_entry, task); exit: - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return NOTIFY_OK; } From 9297d5a160c7ac674f499eca93d6d83f142b50d6 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 13 Apr 2017 17:07:58 -0700 Subject: [PATCH 0513/1276] ANDROID: uid_sys_stats: reduce update_io_stats overhead Replaced read_lock with rcu_read_lock to reduce time that preemption is disabled. Added a function to update io stats for specific uid and moved hash table lookup, user_namespace out of loops. Bug: 37319300 Change-Id: I2b81b5cd3b6399b40d08c3c14b42cad044556970 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 61 ++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 2f4572fe8fd1..c9ee4f0cd4e4 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -235,28 +235,28 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->fsync -= task->ioac.syscfs; } -static void update_io_stats_locked(void) +static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; - - BUG_ON(!rt_mutex_is_locked(&uid_lock)); + uid_t uid; hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; add_uid_io_curr_stats(uid_entry, task); } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { io_bucket = &uid_entry->io[uid_entry->state]; @@ -279,6 +279,47 @@ static void update_io_stats_locked(void) } } +static void update_io_stats_uid_locked(uid_t target_uid) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); + + uid_entry = find_or_register_uid(target_uid); + if (!uid_entry) + return; + + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + rcu_read_lock(); + do_each_thread(temp, task) { + if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + rcu_read_unlock(); + + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; +} + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -286,7 +327,7 @@ static int uid_io_show(struct seq_file *m, void *v) rt_mutex_lock(&uid_lock); - update_io_stats_locked(); + update_io_stats_all_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", @@ -361,7 +402,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_locked(); + update_io_stats_uid_locked(uid); uid_entry->state = state; @@ -399,7 +440,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_locked(); + update_io_stats_uid_locked(uid); clean_uid_io_last_stats(uid_entry, task); exit: From c8212458e3a51cd6e7c8c9ca356396eefefabb8c Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 25 Apr 2017 18:07:43 +0800 Subject: [PATCH 0514/1276] ANDROID: uid_sys_stats: fix access of task_uid(task) struct task_struct *task should be proteced by tasklist_lock. Change-Id: Iefcd13442a9b9d855a2bbcde9fd838a4132fee58 Signed-off-by: Ganesh Mahendran (cherry picked from commit 90d78776c4a0e13fb7ee5bd0787f04a1730631a6) --- drivers/misc/uid_sys_stats.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index c9ee4f0cd4e4..6934380014c0 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -96,9 +96,11 @@ static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; + struct user_namespace *user_ns = current_user_ns(); u64 utime; u64 stime; unsigned long bkt; + uid_t uid; rt_mutex_lock(&uid_lock); @@ -109,14 +111,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); + __func__, uid); return -ENOMEM; } task_cputime_adjusted(task, &utime, &stime); From 89402d07fe9179aed0c6003f88ca4290eee88c5b Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 22 May 2017 12:08:06 -0700 Subject: [PATCH 0515/1276] ANDROID: uid_sys_stats: defer io stats calulation for dead tasks Store sum of dead task io stats in uid_entry and defer uid io calulation until next uid proc stat change or dumpsys. Bug: 37754877 Change-Id: I970f010a4c841c5ca26d0efc7e027414c3c952e0 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 107 ++++++++++++++--------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6934380014c0..79ad83529f33 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -50,7 +50,8 @@ struct io_stats { #define UID_STATE_TOTAL_CURR 2 #define UID_STATE_TOTAL_LAST 3 -#define UID_STATE_SIZE 4 +#define UID_STATE_DEAD_TASKS 4 +#define UID_STATE_SIZE 5 struct uid_entry { uid_t uid; @@ -212,35 +213,44 @@ static u64 compute_write_bytes(struct task_struct *task) return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; } -static void add_uid_io_curr_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void add_uid_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) { - struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + struct io_stats *io_slot = &uid_entry->io[slot]; - io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += compute_write_bytes(task); - io_curr->rchar += task->ioac.rchar; - io_curr->wchar += task->ioac.wchar; - io_curr->fsync += task->ioac.syscfs; + io_slot->read_bytes += task->ioac.read_bytes; + io_slot->write_bytes += compute_write_bytes(task); + io_slot->rchar += task->ioac.rchar; + io_slot->wchar += task->ioac.wchar; + io_slot->fsync += task->ioac.syscfs; } -static void clean_uid_io_last_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) { - struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= compute_write_bytes(task); - io_last->rchar -= task->ioac.rchar; - io_last->wchar -= task->ioac.wchar; - io_last->fsync -= task->ioac.syscfs; + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); } static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; uid_t uid; @@ -255,70 +265,38 @@ static void update_io_stats_all_locked(void) uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } } -static void update_io_stats_uid_locked(uid_t target_uid) +static void update_io_stats_uid_locked(struct uid_entry *uid_entry) { - struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); - uid_entry = find_or_register_uid(target_uid); - if (!uid_entry) - return; - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); rcu_read_lock(); do_each_thread(temp, task) { - if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } static int uid_io_show(struct seq_file *m, void *v) @@ -403,7 +381,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_uid_locked(uid); + update_io_stats_uid_locked(uid_entry); uid_entry->state = state; @@ -441,8 +419,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_uid_locked(uid); - clean_uid_io_last_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS); exit: rt_mutex_unlock(&uid_lock); From 6cbf3939bae2156c7f210f3b01329e3e0ac73a1d Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 24 May 2017 10:28:27 +0800 Subject: [PATCH 0516/1276] ANDROID: uid_sys_stats: Kconfig: add depends for UID_SYS_STATS uid_io depends on TASK_XACCT and TASK_IO_ACCOUNTING. So add depends in Kconfig before compiling code. Change-Id: Ie6bf57ec7c2eceffadf4da0fc2aca001ce10c36e Signed-off-by: Ganesh Mahendran --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ca1cbe5ec2e7..d36249491167 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -515,7 +515,7 @@ config MISC_RTSX config UID_SYS_STATS bool "Per-UID statistics" - depends on PROFILING + depends on PROFILING && TASK_XACCT && TASK_IO_ACCOUNTING help Per UID based cpu time statistics exported to /proc/uid_cputime Per UID based io statistics exported to /proc/uid_io From 6dc5d8173a8cdd0ae3d6417d4f9008be2c3087dc Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Thu, 25 May 2017 15:20:29 +0800 Subject: [PATCH 0517/1276] ANDROID: uid_sys_stats: check previous uid_entry before call find_or_register_uid Theads in a process are stored in list struct task_struct->thread_group, so it will be visited continiously in below loop: do_each_thread(temp, task) { ... } while_each_thread(temp, task); I add some log in the loop, we can see below information: [ 65.033561] uid 1000, uid_entry ffffffc0f2761600 [ 65.033567] uid 1000, uid_entry ffffffc0f2761600 [ 65.033574] uid 1000, uid_entry ffffffc0f2761600 [ 65.033581] uid 1000, uid_entry ffffffc0f2761600 [ 65.033588] uid 1000, uid_entry ffffffc0f2761600 [ 65.033595] uid 1000, uid_entry ffffffc0f2761600 [ 65.033602] uid 1000, uid_entry ffffffc0f2761600 [ 65.033609] uid 1000, uid_entry ffffffc0f2761600 [ 65.033615] uid 1000, uid_entry ffffffc0f2761600 [ 65.033622] uid 1000, uid_entry ffffffc0f2761600 [ 65.033629] uid 1000, uid_entry ffffffc0f2761600 [ 65.033637] uid 1000, uid_entry ffffffc0f2761600 [ 65.033644] uid 1000, uid_entry ffffffc0f2761600 [ 65.033651] uid 1000, uid_entry ffffffc0f2761600 [ 65.033658] uid 1000, uid_entry ffffffc0f2761600 [ 65.033665] uid 1000, uid_entry ffffffc0f2761600 [ 65.033672] uid 1000, uid_entry ffffffc0f2761600 [ 65.033680] uid 1000, uid_entry ffffffc0f2761600 [ 65.033687] uid 1000, uid_entry ffffffc0f2761600 [ 65.033694] uid 1000, uid_entry ffffffc0f2761600 [ 65.033701] uid 1000, uid_entry ffffffc0f2761600 [ 65.033708] uid 1000, uid_entry ffffffc0f2761600 [ 65.033715] uid 1000, uid_entry ffffffc0f2761600 [ 65.033722] uid 1000, uid_entry ffffffc0f2761600 [ 65.033729] uid 1000, uid_entry ffffffc0f2761600 [ 65.033736] uid 1000, uid_entry ffffffc0f2761600 [ 65.033743] uid 1000, uid_entry ffffffc0f2761600 [ 65.033750] uid 1000, uid_entry ffffffc0f2761600 [ 65.033757] uid 1000, uid_entry ffffffc0f2761600 [ 65.033763] uid 1000, uid_entry ffffffc0f2761600 [ 65.033770] uid 1000, uid_entry ffffffc0f2761600 [ 65.033777] uid 1000, uid_entry ffffffc0f2761600 [ 65.033784] uid 1000, uid_entry ffffffc0f2761600 [ 65.033791] uid 1000, uid_entry ffffffc0f2761600 [ 65.033798] uid 1000, uid_entry ffffffc0f2761600 So we can check the previous uid_entry before calling find_or_register_uid to save time. Change-Id: I05ec1a1405a80c0a620cb4b4b2f6483dbfde7829 Signed-off-by: Ganesh Mahendran --- drivers/misc/uid_sys_stats.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 79ad83529f33..6d58eb0120f6 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -95,7 +95,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) static int uid_cputime_show(struct seq_file *m, void *v) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; struct user_namespace *user_ns = current_user_ns(); u64 utime; @@ -113,7 +113,8 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); @@ -249,7 +250,7 @@ static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, static void update_io_stats_all_locked(void) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; @@ -262,7 +263,8 @@ static void update_io_stats_all_locked(void) rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); From 0ca2ece8f7ec1b3e296687777dc9203bb3f21a6f Mon Sep 17 00:00:00 2001 From: Yang Jin Date: Wed, 26 Jul 2017 12:52:22 -0700 Subject: [PATCH 0518/1276] ANDROID: uid_sys_stats: log task io with a debug flag Add a hashmap inside each uid_entry to keep track of task name and io. Task full name is a combination of thread and process name. Bug: 63739275 Change-Id: I30083b757eaef8c61e55a213a883ce8d0c9cf2b1 Signed-off-by: Yang Jin [AmitP: Folded following android-4.9 commit changes into this patch 0ea205a6a224 ("ANDROID: uid_sys_stats: Fix implicit declaration of get_cmdline()")] Signed-off-by: Amit Pundir --- drivers/misc/Kconfig | 7 + drivers/misc/uid_sys_stats.c | 304 ++++++++++++++++++++++++++++++----- 2 files changed, 267 insertions(+), 44 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d36249491167..241e3d9ee7aa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -521,6 +521,13 @@ config UID_SYS_STATS Per UID based io statistics exported to /proc/uid_io Per UID based procstat control in /proc/uid_procstat +config UID_SYS_STATS_DEBUG + bool "Per-TASK statistics" + depends on UID_SYS_STATS + default n + help + Per TASK based io statistics exported to /proc/uid_io + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6d58eb0120f6..7069b0b064a5 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,15 @@ struct io_stats { #define UID_STATE_DEAD_TASKS 4 #define UID_STATE_SIZE 5 +#define MAX_TASK_COMM_LEN 256 + +struct task_entry { + char comm[MAX_TASK_COMM_LEN]; + pid_t pid; + struct io_stats io[UID_STATE_SIZE]; + struct hlist_node hash; +}; + struct uid_entry { uid_t uid; u64 utime; @@ -62,8 +72,231 @@ struct uid_entry { int state; struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; +#ifdef CONFIG_UID_SYS_STATS_DEBUG + DECLARE_HASHTABLE(task_entries, UID_HASH_BITS); +#endif }; +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + +static void compute_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) +{ + /* tasks could switch to another uid group, but its io_last in the + * previous uid group could still be positive. + * therefore before each update, do an overflow check first + */ + int64_t delta; + + delta = io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->read_bytes += delta > 0 ? delta : 0; + delta = io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->write_bytes += delta > 0 ? delta : 0; + delta = io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->rchar += delta > 0 ? delta : 0; + delta = io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->wchar += delta > 0 ? delta : 0; + delta = io_curr->fsync + io_dead->fsync - io_last->fsync; + io_bucket->fsync += delta > 0 ? delta : 0; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); +} + +#ifdef CONFIG_UID_SYS_STATS_DEBUG +static void get_full_task_comm(struct task_entry *task_entry, + struct task_struct *task) +{ + int i = 0, offset = 0, len = 0; + /* save one byte for terminating null character */ + int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1; + char buf[unused_len]; + struct mm_struct *mm = task->mm; + + /* fill the first TASK_COMM_LEN bytes with thread name */ + get_task_comm(task_entry->comm, task); + i = strlen(task_entry->comm); + while (i < TASK_COMM_LEN) + task_entry->comm[i++] = ' '; + + /* next the executable file name */ + if (mm) { + down_read(&mm->mmap_sem); + if (mm->exe_file) { + char *pathname = d_path(&mm->exe_file->f_path, buf, + unused_len); + + if (!IS_ERR(pathname)) { + len = strlcpy(task_entry->comm + i, pathname, + unused_len); + i += len; + task_entry->comm[i++] = ' '; + unused_len--; + } + } + up_read(&mm->mmap_sem); + } + unused_len -= len; + + /* fill the rest with command line argument + * replace each null or new line character + * between args in argv with whitespace */ + len = get_cmdline(task, buf, unused_len); + while (offset < len) { + if (buf[offset] != '\0' && buf[offset] != '\n') + task_entry->comm[i++] = buf[offset]; + else + task_entry->comm[i++] = ' '; + offset++; + } + + /* get rid of trailing whitespaces in case when arg is memset to + * zero before being reset in userspace + */ + while (task_entry->comm[i-1] == ' ') + i--; + task_entry->comm[i] = '\0'; +} + +static struct task_entry *find_task_entry(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + + hash_for_each_possible(uid_entry->task_entries, task_entry, hash, + task->pid) { + if (task->pid == task_entry->pid) { + /* if thread name changed, update the entire command */ + int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN) + - task_entry->comm; + + if (strncmp(task_entry->comm, task->comm, len)) + get_full_task_comm(task_entry, task); + return task_entry; + } + } + return NULL; +} + +static struct task_entry *find_or_register_task(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + pid_t pid = task->pid; + + task_entry = find_task_entry(uid_entry, task); + if (task_entry) + return task_entry; + + task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC); + if (!task_entry) + return NULL; + + get_full_task_comm(task_entry, task); + + task_entry->pid = pid; + hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid); + + return task_entry; +} + +static void remove_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + struct hlist_node *tmp_task; + + hash_for_each_safe(uid_entry->task_entries, bkt_task, + tmp_task, task_entry, hash) { + hash_del(&task_entry->hash); + kfree(task_entry); + } +} + +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + } +} + +static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) +{ + struct task_entry *task_entry = find_or_register_task(uid_entry, task); + struct io_stats *task_io_slot = &task_entry->io[slot]; + + task_io_slot->read_bytes += task->ioac.read_bytes; + task_io_slot->write_bytes += compute_write_bytes(task); + task_io_slot->rchar += task->ioac.rchar; + task_io_slot->wchar += task->ioac.wchar; + task_io_slot->fsync += task->ioac.syscfs; +} + +static void compute_io_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + compute_io_bucket_stats(&task_entry->io[uid_entry->state], + &task_entry->io[UID_STATE_TOTAL_CURR], + &task_entry->io[UID_STATE_TOTAL_LAST], + &task_entry->io[UID_STATE_DEAD_TASKS]); + } +} + +static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + /* Separated by comma because space exists in task comm */ + seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", + task_entry->comm, + (unsigned long)task_entry->pid, + task_entry->io[UID_STATE_FOREGROUND].rchar, + task_entry->io[UID_STATE_FOREGROUND].wchar, + task_entry->io[UID_STATE_FOREGROUND].read_bytes, + task_entry->io[UID_STATE_FOREGROUND].write_bytes, + task_entry->io[UID_STATE_BACKGROUND].rchar, + task_entry->io[UID_STATE_BACKGROUND].wchar, + task_entry->io[UID_STATE_BACKGROUND].read_bytes, + task_entry->io[UID_STATE_BACKGROUND].write_bytes, + task_entry->io[UID_STATE_FOREGROUND].fsync, + task_entry->io[UID_STATE_BACKGROUND].fsync); + } +} +#else +static void remove_uid_tasks(struct uid_entry *uid_entry) {}; +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {}; +static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) {}; +static void compute_io_uid_tasks(struct uid_entry *uid_entry) {}; +static void show_io_uid_tasks(struct seq_file *m, + struct uid_entry *uid_entry) {} +#endif + static struct uid_entry *find_uid_entry(uid_t uid) { struct uid_entry *uid_entry; @@ -87,7 +320,9 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return NULL; uid_entry->uid = uid; - +#ifdef CONFIG_UID_SYS_STATS_DEBUG + hash_init(uid_entry->task_entries); +#endif hash_add(hash_table, &uid_entry->hash, uid); return uid_entry; @@ -190,6 +425,7 @@ static ssize_t uid_remove_write(struct file *file, hash_for_each_possible_safe(hash_table, uid_entry, tmp, hash, (uid_t)uid_start) { if (uid_start == uid_entry->uid) { + remove_uid_tasks(uid_entry); hash_del(&uid_entry->hash); kfree(uid_entry); } @@ -206,13 +442,6 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; -static u64 compute_write_bytes(struct task_struct *task) -{ - if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) - return 0; - - return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; -} static void add_uid_io_stats(struct uid_entry *uid_entry, struct task_struct *task, int slot) @@ -224,28 +453,8 @@ static void add_uid_io_stats(struct uid_entry *uid_entry, io_slot->rchar += task->ioac.rchar; io_slot->wchar += task->ioac.wchar; io_slot->fsync += task->ioac.syscfs; -} -static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, - struct io_stats *io_curr, - struct io_stats *io_last, - struct io_stats *io_dead) -{ - io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - - io_last->read_bytes; - io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; - - memset(io_dead, 0, sizeof(struct io_stats)); + add_uid_tasks_io_stats(uid_entry, task, slot); } static void update_io_stats_all_locked(void) @@ -256,9 +465,11 @@ static void update_io_stats_all_locked(void) unsigned long bkt; uid_t uid; - hash_for_each(hash_table, bkt, uid_entry, hash) + hash_for_each(hash_table, bkt, uid_entry, hash) { memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); + } rcu_read_lock(); do_each_thread(temp, task) { @@ -272,10 +483,11 @@ static void update_io_stats_all_locked(void) rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } } @@ -286,6 +498,7 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); rcu_read_lock(); do_each_thread(temp, task) { @@ -295,12 +508,14 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) } while_each_thread(temp, task); rcu_read_unlock(); - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -312,21 +527,22 @@ static int uid_io_show(struct seq_file *m, void *v) hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", - uid_entry->uid, - uid_entry->io[UID_STATE_FOREGROUND].rchar, - uid_entry->io[UID_STATE_FOREGROUND].wchar, - uid_entry->io[UID_STATE_FOREGROUND].read_bytes, - uid_entry->io[UID_STATE_FOREGROUND].write_bytes, - uid_entry->io[UID_STATE_BACKGROUND].rchar, - uid_entry->io[UID_STATE_BACKGROUND].wchar, - uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes, - uid_entry->io[UID_STATE_FOREGROUND].fsync, - uid_entry->io[UID_STATE_BACKGROUND].fsync); + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); + + show_io_uid_tasks(m, uid_entry); } rt_mutex_unlock(&uid_lock); - return 0; } From 720ff9673228f62a41da5d25fe9465e3b9c51b65 Mon Sep 17 00:00:00 2001 From: Nick Bray Date: Thu, 30 Nov 2017 15:49:54 -0800 Subject: [PATCH 0519/1276] ANDROID: initramfs: call free_initrd() when skipping init Memory allocated for initrd would not be reclaimed if initializing ramfs was skipped. Bug: 69901741 Test: "grep MemTotal /proc/meminfo" increases by a few MB on an Android device with a/b boot. Change-Id: Ifbe094d303ed12cfd6de6aa004a8a19137a2f58a Signed-off-by: Nick Bray --- init/initramfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/init/initramfs.c b/init/initramfs.c index cc21c6cfd20f..c18b91dec432 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -613,8 +613,11 @@ static int __init populate_rootfs(void) { char *err; - if (do_skip_initramfs) + if (do_skip_initramfs) { + if (initrd_start) + free_initrd(); return default_rootfs(); + } /* Load the built in initramfs */ err = unpack_to_rootfs(__initramfs_start, __initramfs_size); From 7885f36a594e2d53cc2b1a2986b2bb2833890887 Mon Sep 17 00:00:00 2001 From: Artem Borisov Date: Sat, 13 Jan 2018 18:03:40 +0300 Subject: [PATCH 0520/1276] ANDROID: uid_sys_stats: fix the comment It is not uid_cputime.c anymore. Change-Id: I7effc2a449c1f9cba9d86a7b122a9c05fc266405 Signed-off-by: Artem Borisov --- drivers/misc/uid_sys_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 7069b0b064a5..9f5b6d1dde0a 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -1,4 +1,4 @@ -/* drivers/misc/uid_cputime.c +/* drivers/misc/uid_sys_stats.c * * Copyright (C) 2014 - 2015 Google, Inc. * From e44f05987d2fae8d6434e0ad890629fb4aa2b544 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 24 Jan 2018 13:59:50 -0800 Subject: [PATCH 0521/1276] ANDROID: Fix script to fetch android kernel config fragments for 4.14 Change-Id: I6036d0cc9d4f28f0df0e8d3267034b34ec276a08 Signed-off-by: Dmitry Shmidt --- kernel/configs/android-fetch-configs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh index a5b56d4908dc..2dcd2981e1be 100755 --- a/kernel/configs/android-fetch-configs.sh +++ b/kernel/configs/android-fetch-configs.sh @@ -1,4 +1,4 @@ #!/bin/sh -curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv +curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.14.tar.gz | tar xzv From 903cea7ab0b298109005c3ca2288b8912ab05127 Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:23:50 -0700 Subject: [PATCH 0522/1276] ANDROID: Included sdcardfs source code for kernel 3.0 Only included the source code as is for kernel 3.0. Following patches take care of porting this file system to version 3.10. Change-Id: I09e76db77cd98a059053ba5b6fd88572a4b75b5b Signed-off-by: Daniel Campello --- fs/Kconfig | 1 + fs/Makefile | 5 +- fs/sdcardfs/Kconfig | 18 + fs/sdcardfs/Makefile | 7 + fs/sdcardfs/dentry.c | 182 ++++++++ fs/sdcardfs/derived_perm.c | 290 ++++++++++++ fs/sdcardfs/file.c | 357 +++++++++++++++ fs/sdcardfs/hashtable.h | 190 ++++++++ fs/sdcardfs/inode.c | 886 +++++++++++++++++++++++++++++++++++++ fs/sdcardfs/lookup.c | 386 ++++++++++++++++ fs/sdcardfs/main.c | 425 ++++++++++++++++++ fs/sdcardfs/mmap.c | 82 ++++ fs/sdcardfs/multiuser.h | 37 ++ fs/sdcardfs/packagelist.c | 458 +++++++++++++++++++ fs/sdcardfs/sdcardfs.h | 493 +++++++++++++++++++++ fs/sdcardfs/strtok.h | 75 ++++ fs/sdcardfs/super.c | 229 ++++++++++ include/uapi/linux/magic.h | 2 + 18 files changed, 4121 insertions(+), 2 deletions(-) create mode 100644 fs/sdcardfs/Kconfig create mode 100644 fs/sdcardfs/Makefile create mode 100644 fs/sdcardfs/dentry.c create mode 100644 fs/sdcardfs/derived_perm.c create mode 100644 fs/sdcardfs/file.c create mode 100644 fs/sdcardfs/hashtable.h create mode 100644 fs/sdcardfs/inode.c create mode 100644 fs/sdcardfs/lookup.c create mode 100644 fs/sdcardfs/main.c create mode 100644 fs/sdcardfs/mmap.c create mode 100644 fs/sdcardfs/multiuser.h create mode 100644 fs/sdcardfs/packagelist.c create mode 100644 fs/sdcardfs/sdcardfs.h create mode 100644 fs/sdcardfs/strtok.h create mode 100644 fs/sdcardfs/super.c diff --git a/fs/Kconfig b/fs/Kconfig index ac474a61be37..ffbd9a4356ed 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -234,6 +234,7 @@ source "fs/orangefs/Kconfig" source "fs/adfs/Kconfig" source "fs/affs/Kconfig" source "fs/ecryptfs/Kconfig" +source "fs/sdcardfs/Kconfig" source "fs/hfs/Kconfig" source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 293733f61594..57744e192335 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -4,7 +4,7 @@ # # 14 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. -# +# obj-y := open.o read_write.o file_table.o super.o \ char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ @@ -62,7 +62,7 @@ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o obj-$(CONFIG_DLM) += dlm/ - + # Do not add any filesystems before this line obj-$(CONFIG_FSCACHE) += fscache/ obj-$(CONFIG_REISERFS_FS) += reiserfs/ @@ -84,6 +84,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ +obj-$(CONFIG_SDCARD_FS) += sdcardfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig new file mode 100644 index 000000000000..657f4958e8d6 --- /dev/null +++ b/fs/sdcardfs/Kconfig @@ -0,0 +1,18 @@ +config SDCARD_FS + tristate "sdcard file system" + depends on EXPERIMENTAL + default n + help + Sdcardfs is based on Wrapfs file system. + +config SDCARD_FS_FADV_NOACTIVE + bool "sdcardfs fadvise noactive support" + depends on FADV_NOACTIVE + default y + help + Sdcardfs supports fadvise noactive mode. + +config SDCARD_FS_CI_SEARCH + tristate "sdcardfs case-insensitive search support" + depends on SDCARD_FS + default y diff --git a/fs/sdcardfs/Makefile b/fs/sdcardfs/Makefile new file mode 100644 index 000000000000..b84fbb2b45a4 --- /dev/null +++ b/fs/sdcardfs/Makefile @@ -0,0 +1,7 @@ +SDCARDFS_VERSION="0.1" + +EXTRA_CFLAGS += -DSDCARDFS_VERSION=\"$(SDCARDFS_VERSION)\" + +obj-$(CONFIG_SDCARD_FS) += sdcardfs.o + +sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c new file mode 100644 index 000000000000..4572a5403bb2 --- /dev/null +++ b/fs/sdcardfs/dentry.c @@ -0,0 +1,182 @@ +/* + * fs/sdcardfs/dentry.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "linux/ctype.h" + +/* + * returns: -ERRNO if error (returned to user) + * 0: tell VFS to invalidate dentry + * 1: dentry is valid + */ +static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int err = 1; + struct path parent_lower_path, lower_path; + struct dentry *parent_dentry = NULL; + struct dentry *parent_lower_dentry = NULL; + struct dentry *lower_cur_parent_dentry = NULL; + struct dentry *lower_dentry = NULL; + + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + spin_lock(&dentry->d_lock); + if (IS_ROOT(dentry)) { + spin_unlock(&dentry->d_lock); + return 1; + } + spin_unlock(&dentry->d_lock); + + /* check uninitialized obb_dentry and + * whether the base obbpath has been changed or not */ + if (is_obbpath_invalid(dentry)) { + d_drop(dentry); + return 0; + } + + parent_dentry = dget_parent(dentry); + sdcardfs_get_lower_path(parent_dentry, &parent_lower_path); + sdcardfs_get_real_lower(dentry, &lower_path); + parent_lower_dentry = parent_lower_path.dentry; + lower_dentry = lower_path.dentry; + lower_cur_parent_dentry = dget_parent(lower_dentry); + + spin_lock(&lower_dentry->d_lock); + if (d_unhashed(lower_dentry)) { + spin_unlock(&lower_dentry->d_lock); + d_drop(dentry); + err = 0; + goto out; + } + spin_unlock(&lower_dentry->d_lock); + + if (parent_lower_dentry != lower_cur_parent_dentry) { + d_drop(dentry); + err = 0; + goto out; + } + + if (dentry < lower_dentry) { + spin_lock(&dentry->d_lock); + spin_lock(&lower_dentry->d_lock); + } else { + spin_lock(&lower_dentry->d_lock); + spin_lock(&dentry->d_lock); + } + + if (dentry->d_name.len != lower_dentry->d_name.len) { + __d_drop(dentry); + err = 0; + } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, + dentry->d_name.len) != 0) { + __d_drop(dentry); + err = 0; + } + + if (dentry < lower_dentry) { + spin_unlock(&lower_dentry->d_lock); + spin_unlock(&dentry->d_lock); + } else { + spin_unlock(&dentry->d_lock); + spin_unlock(&lower_dentry->d_lock); + } + +out: + dput(parent_dentry); + dput(lower_cur_parent_dentry); + sdcardfs_put_lower_path(parent_dentry, &parent_lower_path); + sdcardfs_put_real_lower(dentry, &lower_path); + return err; +} + +static void sdcardfs_d_release(struct dentry *dentry) +{ + /* release and reset the lower paths */ + if(has_graft_path(dentry)) { + sdcardfs_put_reset_orig_path(dentry); + } + sdcardfs_put_reset_lower_path(dentry); + free_dentry_private_data(dentry); + return; +} + +static int sdcardfs_hash_ci(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + /* + * This function is copy of vfat_hashi. + * FIXME Should we support national language? + * Refer to vfat_hashi() + * struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; + */ + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + //len = vfat_striptail_len(qstr); + len = qstr->len; + + hash = init_name_hash(); + while (len--) + //hash = partial_name_hash(nls_tolower(t, *name++), hash); + hash = partial_name_hash(tolower(*name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case insensitive compare of two vfat names. + */ +static int sdcardfs_cmp_ci(const struct dentry *parent, + const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + /* This function is copy of vfat_cmpi */ + // FIXME Should we support national language? + //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; + //unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + /* + alen = vfat_striptail_len(name); + blen = __vfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + */ + if (name->len == len) { + if (strncasecmp(name->name, str, len) == 0) + return 0; + } + return 1; +} + +const struct dentry_operations sdcardfs_ci_dops = { + .d_revalidate = sdcardfs_d_revalidate, + .d_release = sdcardfs_d_release, + .d_hash = sdcardfs_hash_ci, + .d_compare = sdcardfs_cmp_ci, +}; + diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c new file mode 100644 index 000000000000..00c33a471dcc --- /dev/null +++ b/fs/sdcardfs/derived_perm.c @@ -0,0 +1,290 @@ +/* + * fs/sdcardfs/derived_perm.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* copy derived state from parent inode */ +static void inherit_derived_state(struct inode *parent, struct inode *child) +{ + struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); + struct sdcardfs_inode_info *ci = SDCARDFS_I(child); + + ci->perm = PERM_INHERIT; + ci->userid = pi->userid; + ci->d_uid = pi->d_uid; + ci->d_gid = pi->d_gid; + ci->d_mode = pi->d_mode; +} + +/* helper function for derived state */ +void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, gid_t gid, mode_t mode) +{ + struct sdcardfs_inode_info *info = SDCARDFS_I(inode); + + info->perm = perm; + info->userid = userid; + info->d_uid = uid; + info->d_gid = gid; + info->d_mode = mode; +} + +void get_derived_permission(struct dentry *parent, struct dentry *dentry) +{ + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + appid_t appid; + + /* By default, each inode inherits from its parent. + * the properties are maintained on its private fields + * because the inode attributes will be modified with that of + * its lower inode. + * The derived state will be updated on the last + * stage of each system call by fix_derived_permission(inode). + */ + + inherit_derived_state(parent->d_inode, dentry->d_inode); + + //printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name, + // dentry->d_name.name, parent_info->perm); + + if (sbi->options.derive == DERIVE_NONE) { + return; + } + + /* Derive custom permissions based on parent and current node */ + switch (parent_info->perm) { + case PERM_INHERIT: + /* Already inherited above */ + break; + case PERM_LEGACY_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + info->d_mode = 00770; + if (!strcasecmp(dentry->d_name.name, "Android")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->d_mode = 00771; + } else if (sbi->options.split_perms) { + if (!strcasecmp(dentry->d_name.name, "DCIM") + || !strcasecmp(dentry->d_name.name, "Pictures")) { + info->d_gid = AID_SDCARD_PICS; + } else if (!strcasecmp(dentry->d_name.name, "Alarms") + || !strcasecmp(dentry->d_name.name, "Movies") + || !strcasecmp(dentry->d_name.name, "Music") + || !strcasecmp(dentry->d_name.name, "Notifications") + || !strcasecmp(dentry->d_name.name, "Podcasts") + || !strcasecmp(dentry->d_name.name, "Ringtones")) { + info->d_gid = AID_SDCARD_AV; + } + } + break; + case PERM_ANDROID: + if (!strcasecmp(dentry->d_name.name, "data")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; + info->d_mode = 00771; + } else if (!strcasecmp(dentry->d_name.name, "obb")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->d_mode = 00771; + // FIXME : this feature will be implemented later. + /* Single OBB directory is always shared */ + } else if (!strcasecmp(dentry->d_name.name, "user")) { + /* User directories must only be accessible to system, protected + * by sdcard_all. Zygote will bind mount the appropriate user- + * specific path. */ + info->perm = PERM_ANDROID_USER; + info->d_gid = AID_SDCARD_ALL; + info->d_mode = 00770; + } + break; + /* same policy will be applied on PERM_ANDROID_DATA + * and PERM_ANDROID_OBB */ + case PERM_ANDROID_DATA: + case PERM_ANDROID_OBB: + appid = get_appid(sbi->pkgl_id, dentry->d_name.name); + if (appid != 0) { + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + } + info->d_mode = 00770; + break; + case PERM_ANDROID_USER: + /* Root of a secondary user */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + info->d_gid = AID_SDCARD_R; + info->d_mode = 00771; + break; + } +} + +/* main function for updating derived permission */ +inline void update_derived_permission(struct dentry *dentry) +{ + struct dentry *parent; + + if(!dentry || !dentry->d_inode) { + printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + return; + } + /* FIXME: + * 1. need to check whether the dentry is updated or not + * 2. remove the root dentry update + */ + if(IS_ROOT(dentry)) { + //setup_default_pre_root_state(dentry->d_inode); + } else { + parent = dget_parent(dentry); + if(parent) { + get_derived_permission(parent, dentry); + dput(parent); + } + } + fix_derived_permission(dentry->d_inode); +} + +int need_graft_path(struct dentry *dentry) +{ + int ret = 0; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + if(parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + + /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ + if(!(sbi->options.derive == DERIVE_UNIFIED + && parent_info->userid == 0)) { + ret = 1; + } + } + dput(parent); + return ret; +} + +int is_obbpath_invalid(struct dentry *dent) +{ + int ret = 0; + struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); + char *path_buf, *obbpath_s; + + /* check the base obbpath has been changed. + * this routine can check an uninitialized obb dentry as well. + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + spin_lock(&di->lock); + if(di->orig_path.dentry) { + if(!di->lower_path.dentry) { + ret = 1; + } else { + path_get(&di->lower_path); + //lower_parent = lock_parent(lower_path->dentry); + + path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); + if(!path_buf) { + ret = 1; + printk(KERN_ERR "sdcardfs: " + "fail to allocate path_buf in %s.\n", __func__); + } else { + obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); + if (d_unhashed(di->lower_path.dentry) || + strcasecmp(sbi->obbpath_s, obbpath_s)) { + ret = 1; + } + kfree(path_buf); + } + + //unlock_dir(lower_parent); + path_put(&di->lower_path); + } + } + spin_unlock(&di->lock); + return ret; +} + +int is_base_obbpath(struct dentry *dentry) +{ + int ret = 0; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + spin_lock(&SDCARDFS_D(dentry)->lock); + /* DERIVED_LEGACY */ + if(parent_info->perm == PERM_LEGACY_PRE_ROOT && + !strcasecmp(dentry->d_name.name, "obb")) { + ret = 1; + } + /* DERIVED_UNIFIED :/Android/obb is the base obbpath */ + else if (parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + if((sbi->options.derive == DERIVE_UNIFIED + && parent_info->userid == 0)) { + ret = 1; + } + } + spin_unlock(&SDCARDFS_D(dentry)->lock); + dput(parent); + return ret; +} + +/* The lower_path will be stored to the dentry's orig_path + * and the base obbpath will be copyed to the lower_path variable. + * if an error returned, there's no change in the lower_path + * returns: -ERRNO if error (0: no error) */ +int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) +{ + int err = 0; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct path obbpath; + + /* A local obb dentry must have its own orig_path to support rmdir + * and mkdir of itself. Usually, we expect that the sbi->obbpath + * is avaiable on this stage. */ + sdcardfs_set_orig_path(dentry, lower_path); + + err = kern_path(sbi->obbpath_s, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); + + if(!err) { + /* the obbpath base has been found */ + printk(KERN_INFO "sdcardfs: " + "the sbi->obbpath is found\n"); + pathcpy(lower_path, &obbpath); + } else { + /* if the sbi->obbpath is not available, we can optionally + * setup the lower_path with its orig_path. + * but, the current implementation just returns an error + * because the sdcard daemon also regards this case as + * a lookup fail. */ + printk(KERN_INFO "sdcardfs: " + "the sbi->obbpath is not available\n"); + } + return err; +} + + diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c new file mode 100644 index 000000000000..bcacb947c874 --- /dev/null +++ b/fs/sdcardfs/file.c @@ -0,0 +1,357 @@ +/* + * fs/sdcardfs/file.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE +#include +#endif + +static ssize_t sdcardfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE + struct backing_dev_info *bdi; +#endif + + lower_file = sdcardfs_lower_file(file); + +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE + if (file->f_mode & FMODE_NOACTIVE) { + if (!(lower_file->f_mode & FMODE_NOACTIVE)) { + bdi = lower_file->f_mapping->backing_dev_info; + lower_file->f_ra.ra_pages = bdi->ra_pages * 2; + spin_lock(&lower_file->f_lock); + lower_file->f_mode |= FMODE_NOACTIVE; + spin_unlock(&lower_file->f_lock); + } + } +#endif + + err = vfs_read(lower_file, buf, count, ppos); + /* update our inode atime upon a successful lower read */ + if (err >= 0) + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + + return err; +} + +static ssize_t sdcardfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int err = 0; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + + /* check disk space */ + if (!check_min_free_space(dentry, count, 0)) { + printk(KERN_INFO "No minimum free space.\n"); + return -ENOSPC; + } + + lower_file = sdcardfs_lower_file(file); + err = vfs_write(lower_file, buf, count, ppos); + /* update our inode times+sizes upon a successful lower write */ + if (err >= 0) { + fsstack_copy_inode_size(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_times(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + } + + return err; +} + +static int sdcardfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + + lower_file = sdcardfs_lower_file(file); + + lower_file->f_pos = file->f_pos; + err = vfs_readdir(lower_file, filldir, dirent); + file->f_pos = lower_file->f_pos; + if (err >= 0) /* copy the atime */ + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + return err; +} + +static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->unlocked_ioctl) + err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + +out: + return err; +} + +#ifdef CONFIG_COMPAT +static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->compat_ioctl) + err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + +out: + return err; +} +#endif + +static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err = 0; + bool willwrite; + struct file *lower_file; + const struct vm_operations_struct *saved_vm_ops = NULL; + + /* this might be deferred to mmap's writepage */ + willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); + + /* + * File systems which do not implement ->writepage may use + * generic_file_readonly_mmap as their ->mmap op. If you call + * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. + * But we cannot call the lower ->mmap op, so we can't tell that + * writeable mappings won't work. Therefore, our only choice is to + * check if the lower file system supports the ->writepage, and if + * not, return EINVAL (the same error that + * generic_file_readonly_mmap returns in that case). + */ + lower_file = sdcardfs_lower_file(file); + if (willwrite && !lower_file->f_mapping->a_ops->writepage) { + err = -EINVAL; + printk(KERN_ERR "sdcardfs: lower file system does not " + "support writeable mmap\n"); + goto out; + } + + /* + * find and save lower vm_ops. + * + * XXX: the VFS should have a cleaner way of finding the lower vm_ops + */ + if (!SDCARDFS_F(file)->lower_vm_ops) { + err = lower_file->f_op->mmap(lower_file, vma); + if (err) { + printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + goto out; + } + saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ + err = do_munmap(current->mm, vma->vm_start, + vma->vm_end - vma->vm_start); + if (err) { + printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); + goto out; + } + } + + /* + * Next 3 lines are all I need from generic_file_mmap. I definitely + * don't want its test for ->readpage which returns -ENOEXEC. + */ + file_accessed(file); + vma->vm_ops = &sdcardfs_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; + + file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ + if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ + SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; + +out: + return err; +} + +static int sdcardfs_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct file *lower_file = NULL; + struct path lower_path; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + int has_rw; + + /* don't open unhashed/deleted files */ + if (d_unhashed(dentry)) { + err = -ENOENT; + goto out_err; + } + + has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, + open_flags_to_access_mode(file->f_flags), has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_err; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred); + + file->private_data = + kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); + if (!SDCARDFS_F(file)) { + err = -ENOMEM; + goto out_revert_cred; + } + + /* open lower object and link sdcardfs's file struct to lower's */ + sdcardfs_get_lower_path(file->f_path.dentry, &lower_path); + lower_file = dentry_open(lower_path.dentry, lower_path.mnt, + file->f_flags, current_cred()); + if (IS_ERR(lower_file)) { + err = PTR_ERR(lower_file); + lower_file = sdcardfs_lower_file(file); + if (lower_file) { + sdcardfs_set_lower_file(file, NULL); + fput(lower_file); /* fput calls dput for lower_dentry */ + } + } else { + sdcardfs_set_lower_file(file, lower_file); + } + + if (err) + kfree(SDCARDFS_F(file)); + else { + fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode)); + fix_derived_permission(inode); + } + +out_revert_cred: + REVERT_CRED(saved_cred); +out_err: + dput(parent); + return err; +} + +static int sdcardfs_flush(struct file *file, fl_owner_t id) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = sdcardfs_lower_file(file); + if (lower_file && lower_file->f_op && lower_file->f_op->flush) + err = lower_file->f_op->flush(lower_file, id); + + return err; +} + +/* release all lower object references & free the file info structure */ +static int sdcardfs_file_release(struct inode *inode, struct file *file) +{ + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (lower_file) { + sdcardfs_set_lower_file(file, NULL); + fput(lower_file); + } + + kfree(SDCARDFS_F(file)); + return 0; +} + +static int +sdcardfs_fsync(struct file *file, int datasync) +{ + int err; + struct file *lower_file; + struct path lower_path; + struct dentry *dentry = file->f_path.dentry; + + lower_file = sdcardfs_lower_file(file); + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_fsync(lower_file, datasync); + sdcardfs_put_lower_path(dentry, &lower_path); + + return err; +} + +static int sdcardfs_fasync(int fd, struct file *file, int flag) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = sdcardfs_lower_file(file); + if (lower_file->f_op && lower_file->f_op->fasync) + err = lower_file->f_op->fasync(fd, lower_file, flag); + + return err; +} + +const struct file_operations sdcardfs_main_fops = { + .llseek = generic_file_llseek, + .read = sdcardfs_read, + .write = sdcardfs_write, + .unlocked_ioctl = sdcardfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sdcardfs_compat_ioctl, +#endif + .mmap = sdcardfs_mmap, + .open = sdcardfs_open, + .flush = sdcardfs_flush, + .release = sdcardfs_file_release, + .fsync = sdcardfs_fsync, + .fasync = sdcardfs_fasync, +}; + +/* trimmed directory options */ +const struct file_operations sdcardfs_dir_fops = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = sdcardfs_readdir, + .unlocked_ioctl = sdcardfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sdcardfs_compat_ioctl, +#endif + .open = sdcardfs_open, + .release = sdcardfs_file_release, + .flush = sdcardfs_flush, + .fsync = sdcardfs_fsync, + .fasync = sdcardfs_fasync, +}; diff --git a/fs/sdcardfs/hashtable.h b/fs/sdcardfs/hashtable.h new file mode 100644 index 000000000000..1e770f3df148 --- /dev/null +++ b/fs/sdcardfs/hashtable.h @@ -0,0 +1,190 @@ +/* + * Statically sized hash table implementation + * (C) 2012 Sasha Levin + */ + +#ifndef _LINUX_HASHTABLE_H +#define _LINUX_HASHTABLE_H + +#include +#include +#include +#include +#include + +#define DEFINE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DECLARE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] + +#define HASH_SIZE(name) (ARRAY_SIZE(name)) +#define HASH_BITS(name) ilog2(HASH_SIZE(name)) + +/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ +#define hash_min(val, bits) \ + (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) + +static inline void __hash_init(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + INIT_HLIST_HEAD(&ht[i]); +} + +/** + * hash_init - initialize a hash table + * @hashtable: hashtable to be initialized + * + * Calculates the size of the hashtable from the given parameter, otherwise + * same as hash_init_size. + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_add - add an object to a hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add(hashtable, node, key) \ + hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_add_rcu - add an object to a rcu enabled hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add_rcu(hashtable, node, key) \ + hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_hashed - check whether an object is in any hashtable + * @node: the &struct hlist_node of the object to be checked + */ +static inline bool hash_hashed(struct hlist_node *node) +{ + return !hlist_unhashed(node); +} + +static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + if (!hlist_empty(&ht[i])) + return false; + + return true; +} + +/** + * hash_empty - check whether a hashtable is empty + * @hashtable: hashtable to check + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_del - remove an object from a hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del(struct hlist_node *node) +{ + hlist_del_init(node); +} + +/** + * hash_del_rcu - remove an object from a rcu enabled hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del_rcu(struct hlist_node *node) +{ + hlist_del_init_rcu(node); +} + +/** + * hash_for_each - iterate over a hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each(name, bkt, obj, member, pos) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry(obj, pos, &name[bkt], member) + +/** + * hash_for_each_rcu - iterate over a rcu enabled hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_rcu(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_rcu(obj, &name[bkt], member) + +/** + * hash_for_each_safe - iterate over a hashtable safe against removal of + * hash entry + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @tmp: a &struct used for temporary storage + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_safe(name, bkt, tmp, obj, member, pos) \ + for ((bkt) = 0, obj = NULL; (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_safe(obj, pos, tmp, &name[bkt], member) + +/** + * hash_for_each_possible - iterate over all possible objects hashing to the + * same bucket + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible(name, obj, member, key, pos) \ + hlist_for_each_entry(obj, pos, &name[hash_min(key, HASH_BITS(name))], member) + +/** + * hash_for_each_possible_rcu - iterate over all possible objects hashing to the + * same bucket in an rcu enabled hashtable + * in a rcu enabled hashtable + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_rcu(name, obj, member, key) \ + hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\ + member) + +/** + * hash_for_each_possible_safe - iterate over all possible objects hashing to the + * same bucket safe against removals + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @tmp: a &struct used for temporary storage + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ + hlist_for_each_entry_safe(obj, tmp,\ + &name[hash_min(key, HASH_BITS(name))], member) + + +#endif diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c new file mode 100644 index 000000000000..e8ed04250ed1 --- /dev/null +++ b/fs/sdcardfs/inode.c @@ -0,0 +1,886 @@ +/* + * fs/sdcardfs/inode.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) +{ + struct cred * cred; + const struct cred * old_cred; + + cred = prepare_creds(); + if (!cred) + return NULL; + + cred->fsuid = sbi->options.fs_low_uid; + cred->fsgid = sbi->options.fs_low_gid; + + old_cred = override_creds(cred); + + return old_cred; +} + +/* Do not directly use this function, use REVERT_CRED() instead. */ +void revert_fsids(const struct cred * old_cred) +{ + const struct cred * cur_cred; + + cur_cred = current->cred; + revert_creds(old_cred); + put_cred(cur_cred); +} + +static int sdcardfs_create(struct inode *dir, struct dentry *dentry, + int mode, struct nameidata *nd) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path, saved_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + + pathcpy(&saved_path, &nd->path); + pathcpy(&nd->path, &lower_path); + + /* set last 16bytes of mode field to 0664 */ + mode = (mode & S_IFMT) | 00664; + err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); + + pathcpy(&nd->path, &saved_path); + if (err) + goto out; + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int err; + struct path lower_old_path, lower_new_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + file_size_save = i_size_read(old_dentry->d_inode); + sdcardfs_get_lower_path(old_dentry, &lower_old_path); + sdcardfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_dir_dentry = lock_parent(lower_new_dentry); + + err = mnt_want_write(lower_new_path.mnt); + if (err) + goto out_unlock; + + err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + if (err || !lower_new_dentry->d_inode) + goto out; + + err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); + old_dentry->d_inode->i_nlink = + sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink; + i_size_write(new_dentry->d_inode, file_size_save); +out: + mnt_drop_write(lower_new_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + sdcardfs_put_lower_path(old_dentry, &lower_old_path); + sdcardfs_put_lower_path(new_dentry, &lower_new_path); + REVERT_CRED(); + return err; +} +#endif + +static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + struct dentry *lower_dentry; + struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); + struct dentry *lower_dir_dentry; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + dget(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_unlink(lower_dir_inode, lower_dentry); + + /* + * Note: unlinking on top of NFS can cause silly-renamed files. + * Trying to delete such files results in EBUSY from NFS + * below. Silly-renamed files will get deleted by NFS later on, so + * we just need to detect them here and treat such EBUSY errors as + * if the upper file was successfully deleted. + */ + if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) + err = 0; + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_dir_inode); + fsstack_copy_inode_size(dir, lower_dir_inode); + dentry->d_inode->i_nlink = + sdcardfs_lower_inode(dentry->d_inode)->i_nlink; + dentry->d_inode->i_ctime = dir->i_ctime; + d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + dput(lower_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); + if (err) + goto out; + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + +static int touch(char *abs_path, mode_t mode) { + struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { + if (PTR_ERR(filp) == -EEXIST) { + return 0; + } + else { + printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + abs_path, PTR_ERR(filp)); + return PTR_ERR(filp); + } + } + filp_close(filp, current->files); + return 0; +} + +static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err = 0; + int make_nomedia_in_obb = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); + char *page_buf; + char *nomedia_dir_name; + char *nomedia_fullpath; + int fullpath_namelen; + int touch_err = 0; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + /* check disk space */ + if (!check_min_free_space(dentry, 0, 1)) { + printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + err = -ENOSPC; + goto out_revert; + } + + /* the lower_dentry is negative here */ + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + + /* set last 16bytes of mode field to 0775 */ + mode = (mode & S_IFMT) | 00775; + err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); + + if (err) + goto out; + + /* if it is a local obb dentry, setup it with the base obbpath */ + if(need_graft_path(dentry)) { + + err = setup_obb_dentry(dentry, &lower_path); + if(err) { + /* if the sbi->obbpath is not available, the lower_path won't be + * changed by setup_obb_dentry() but the lower path is saved to + * its orig_path. this dentry will be revalidated later. + * but now, the lower_path should be NULL */ + sdcardfs_put_reset_lower_path(dentry); + + /* the newly created lower path which saved to its orig_path or + * the lower_path is the base obbpath. + * therefore, an additional path_get is required */ + path_get(&lower_path); + } else + make_nomedia_in_obb = 1; + } + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + /* update number of links on parent directory */ + dir->i_nlink = sdcardfs_lower_inode(dir)->i_nlink; + + if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) + && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) + make_nomedia_in_obb = 1; + + /* When creating /Android/data and /Android/obb, mark them as .nomedia */ + if (make_nomedia_in_obb || + ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + + page_buf = (char *)__get_free_page(GFP_KERNEL); + if (!page_buf) { + printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); + goto out; + } + + nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); + if (IS_ERR(nomedia_dir_name)) { + free_page((unsigned long)page_buf); + printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); + goto out; + } + + fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; + fullpath_namelen += strlen("/.nomedia"); + nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); + if (!nomedia_fullpath) { + free_page((unsigned long)page_buf); + printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); + goto out; + } + + strcpy(nomedia_fullpath, nomedia_dir_name); + free_page((unsigned long)page_buf); + strcat(nomedia_fullpath, "/.nomedia"); + touch_err = touch(nomedia_fullpath, 0664); + if (touch_err) { + printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", + nomedia_fullpath, touch_err); + kfree(nomedia_fullpath); + goto out; + } + kfree(nomedia_fullpath); + } +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); +out_revert: + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + int err; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + //char *path_s = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry + * the dentry on the original path should be deleted. */ + sdcardfs_get_real_lower(dentry, &lower_path); + + lower_dentry = lower_path.dentry; + lower_dir_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + if (err) + goto out; + + d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ + if (dentry->d_inode) + clear_nlink(dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + sdcardfs_put_real_lower(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + if (err) + goto out; + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + +/* + * The locking rules in sdcardfs_rename are complex. We could use a simpler + * superblock-level name-space lock for renames and copy-ups. + */ +static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int err = 0; + struct dentry *lower_old_dentry = NULL; + struct dentry *lower_new_dentry = NULL; + struct dentry *lower_old_dir_dentry = NULL; + struct dentry *lower_new_dir_dentry = NULL; + struct dentry *trap = NULL; + struct dentry *new_parent = NULL; + struct path lower_old_path, lower_new_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name, + sbi->options.derive, 1, has_rw) || + !check_caller_access_to_name(new_dir, new_dentry->d_name.name, + sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " new_dentry: %s, task:%s\n", + __func__, new_dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); + + sdcardfs_get_real_lower(old_dentry, &lower_old_path); + sdcardfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + + trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + /* source should not be ancestor of target */ + if (trap == lower_old_dentry) { + err = -EINVAL; + goto out; + } + /* target should not be ancestor of source */ + if (trap == lower_new_dentry) { + err = -ENOTEMPTY; + goto out; + } + + err = mnt_want_write(lower_old_path.mnt); + if (err) + goto out; + err = mnt_want_write(lower_new_path.mnt); + if (err) + goto out_drop_old_write; + + err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); + if (err) + goto out_err; + + /* Copy attrs from lower dir, but i_uid/i_gid */ + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); + fix_derived_permission(new_dir); + if (new_dir != old_dir) { + fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); + fix_derived_permission(old_dir); + /* update the derived permission of the old_dentry + * with its new parent + */ + new_parent = dget_parent(new_dentry); + if(new_parent) { + if(old_dentry->d_inode) { + get_derived_permission(new_parent, old_dentry); + fix_derived_permission(old_dentry->d_inode); + } + dput(new_parent); + } + } + +out_err: + mnt_drop_write(lower_new_path.mnt); +out_drop_old_write: + mnt_drop_write(lower_old_path.mnt); +out: + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_old_dir_dentry); + dput(lower_new_dir_dentry); + sdcardfs_put_real_lower(old_dentry, &lower_old_path); + sdcardfs_put_lower_path(new_dentry, &lower_new_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) +{ + int err; + struct dentry *lower_dentry; + struct path lower_path; + /* XXX readlink does not requires overriding credential */ + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + + err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + buf, bufsiz); + if (err < 0) + goto out; + fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); + +out: + sdcardfs_put_lower_path(dentry, &lower_path); + return err; +} +#endif + +#if 0 +static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, err; + mm_segment_t old_fs; + + /* This is freed by the put_link method assuming a successful call. */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + buf = ERR_PTR(-ENOMEM); + goto out; + } + + /* read the symlink, and then we will follow it */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sdcardfs_readlink(dentry, buf, len); + set_fs(old_fs); + if (err < 0) { + kfree(buf); + buf = ERR_PTR(err); + } else { + buf[err] = '\0'; + } +out: + nd_set_link(nd, buf); + return NULL; +} +#endif + +#if 0 +/* this @nd *IS* still used */ +static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + char *buf = nd_get_link(nd); + if (!IS_ERR(buf)) /* free the char* */ + kfree(buf); +} +#endif + +static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags) +{ + int err; + + if (flags & IPERM_FLAG_RCU) + return -ECHILD; + + /* + * Permission check on sdcardfs inode. + * Calling process should have AID_SDCARD_RW permission + */ + err = generic_permission(inode, mask, 0, inode->i_op->check_acl); + + /* XXX + * Original sdcardfs code calls inode_permission(lower_inode,.. ) + * for checking inode permission. But doing such things here seems + * duplicated work, because the functions called after this func, + * such as vfs_create, vfs_unlink, vfs_rename, and etc, + * does exactly same thing, i.e., they calls inode_permission(). + * So we just let they do the things. + * If there are any security hole, just uncomment following if block. + */ +#if 0 + if (!err) { + /* + * Permission check on lower_inode(=EXT4). + * we check it with AID_MEDIA_RW permission + */ + struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); + + lower_inode = sdcardfs_lower_inode(inode); + err = inode_permission(lower_inode, mask); + + REVERT_CRED(); + } +#endif + return err; + +} + +static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct dentry *parent; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + parent = dget_parent(dentry); + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 0, 0)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + dput(parent); + return -EACCES; + } + dput(parent); + + inode = dentry->d_inode; + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + /* if the dentry has been moved from other location + * so, on this stage, its derived permission must be + * rechecked from its private field. + */ + fix_derived_permission(inode); + + generic_fillattr(inode, stat); + sdcardfs_put_lower_path(dentry, &lower_path); + return 0; +} + +static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct iattr lower_ia; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct dentry *parent; + int has_rw; + + inode = dentry->d_inode; + + /* + * Check if user has permission to change inode. We don't check if + * this user can change the lower inode: that should happen when + * calling notify_change on the lower inode. + */ + err = inode_change_ok(inode, ia); + + /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ + if (!err) { + /* check the Android group ID */ + has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + parent = dget_parent(dentry); + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + } + dput(parent); + } + + if (err) + goto out_err; + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + if (ia->ia_valid & ATTR_FILE) + lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); + + lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE); + + /* + * If shrinking, first truncate upper level to cancel writing dirty + * pages beyond the new eof; and also if its' maxbytes is more + * limiting (fail with -EFBIG before making any change to the lower + * level). There is no need to vmtruncate the upper level + * afterwards in the other cases: we fsstack_copy_inode_size from + * the lower level. + */ + if (current->mm) + down_write(¤t->mm->mmap_sem); + if (ia->ia_valid & ATTR_SIZE) { + err = inode_newsize_ok(inode, ia->ia_size); + if (err) { + if (current->mm) + up_write(¤t->mm->mmap_sem); + goto out; + } + truncate_setsize(inode, ia->ia_size); + } + + /* + * mode change is for clearing setuid/setgid bits. Allow lower fs + * to interpret this in its own way. + */ + if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + lower_ia.ia_valid &= ~ATTR_MODE; + + /* notify the (possibly copied-up) lower inode */ + /* + * Note: we use lower_dentry->d_inode, because lower_inode may be + * unlinked (no inode->i_sb and i_ino==0. This happens if someone + * tries to open(), unlink(), then ftruncate() a file. + */ + mutex_lock(&lower_dentry->d_inode->i_mutex); + err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ + mutex_unlock(&lower_dentry->d_inode->i_mutex); + if (current->mm) + up_write(¤t->mm->mmap_sem); + if (err) + goto out; + + /* get attributes from the lower inode */ + fsstack_copy_attr_all(inode, lower_inode); + /* update derived permission of the upper inode */ + fix_derived_permission(inode); + + /* + * Not running fsstack_copy_inode_size(inode, lower_inode), because + * VFS should update our inode size, and notify_change on + * lower_inode should update its size. + */ + +out: + sdcardfs_put_lower_path(dentry, &lower_path); +out_err: + return err; +} + +const struct inode_operations sdcardfs_symlink_iops = { + .permission = sdcardfs_permission, + .setattr = sdcardfs_setattr, + /* XXX Following operations are implemented, + * but FUSE(sdcard) or FAT does not support them + * These methods are *NOT* perfectly tested. + .readlink = sdcardfs_readlink, + .follow_link = sdcardfs_follow_link, + .put_link = sdcardfs_put_link, + */ +}; + +const struct inode_operations sdcardfs_dir_iops = { + .create = sdcardfs_create, + .lookup = sdcardfs_lookup, + .permission = sdcardfs_permission, + .unlink = sdcardfs_unlink, + .mkdir = sdcardfs_mkdir, + .rmdir = sdcardfs_rmdir, + .rename = sdcardfs_rename, + .setattr = sdcardfs_setattr, + .getattr = sdcardfs_getattr, + /* XXX Following operations are implemented, + * but FUSE(sdcard) or FAT does not support them + * These methods are *NOT* perfectly tested. + .symlink = sdcardfs_symlink, + .link = sdcardfs_link, + .mknod = sdcardfs_mknod, + */ +}; + +const struct inode_operations sdcardfs_main_iops = { + .permission = sdcardfs_permission, + .setattr = sdcardfs_setattr, + .getattr = sdcardfs_getattr, +}; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c new file mode 100644 index 000000000000..c0b12375b1bf --- /dev/null +++ b/fs/sdcardfs/lookup.c @@ -0,0 +1,386 @@ +/* + * fs/sdcardfs/lookup.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "linux/delay.h" + +/* The dentry cache is just so we have properly sized dentries */ +static struct kmem_cache *sdcardfs_dentry_cachep; + +int sdcardfs_init_dentry_cache(void) +{ + sdcardfs_dentry_cachep = + kmem_cache_create("sdcardfs_dentry", + sizeof(struct sdcardfs_dentry_info), + 0, SLAB_RECLAIM_ACCOUNT, NULL); + + return sdcardfs_dentry_cachep ? 0 : -ENOMEM; +} + +void sdcardfs_destroy_dentry_cache(void) +{ + if (sdcardfs_dentry_cachep) + kmem_cache_destroy(sdcardfs_dentry_cachep); +} + +void free_dentry_private_data(struct dentry *dentry) +{ + if (!dentry || !dentry->d_fsdata) + return; + kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); + dentry->d_fsdata = NULL; +} + +/* allocate new dentry private data */ +int new_dentry_private_data(struct dentry *dentry) +{ + struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry); + + /* use zalloc to init dentry_info.lower_path */ + info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC); + if (!info) + return -ENOMEM; + + spin_lock_init(&info->lock); + dentry->d_fsdata = info; + + return 0; +} + +static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode) +{ + struct inode *current_lower_inode = sdcardfs_lower_inode(inode); + if (current_lower_inode == (struct inode *)candidate_lower_inode) + return 1; /* found a match */ + else + return 0; /* no match */ +} + +static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) +{ + /* we do actual inode initialization in sdcardfs_iget */ + return 0; +} + +static struct inode *sdcardfs_iget(struct super_block *sb, + struct inode *lower_inode) +{ + struct sdcardfs_inode_info *info; + struct inode *inode; /* the new inode to return */ + int err; + + inode = iget5_locked(sb, /* our superblock */ + /* + * hashval: we use inode number, but we can + * also use "(unsigned long)lower_inode" + * instead. + */ + lower_inode->i_ino, /* hashval */ + sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_set, /* inode init function */ + lower_inode); /* data passed to test+set fxns */ + if (!inode) { + err = -EACCES; + iput(lower_inode); + return ERR_PTR(err); + } + /* if found a cached inode, then just return it */ + if (!(inode->i_state & I_NEW)) + return inode; + + /* initialize new inode */ + info = SDCARDFS_I(inode); + + inode->i_ino = lower_inode->i_ino; + if (!igrab(lower_inode)) { + err = -ESTALE; + return ERR_PTR(err); + } + sdcardfs_set_lower_inode(inode, lower_inode); + + inode->i_version++; + + /* use different set of inode ops for symlinks & directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &sdcardfs_dir_iops; + else if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &sdcardfs_symlink_iops; + else + inode->i_op = &sdcardfs_main_iops; + + /* use different set of file ops for directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &sdcardfs_dir_fops; + else + inode->i_fop = &sdcardfs_main_fops; + + inode->i_mapping->a_ops = &sdcardfs_aops; + + inode->i_atime.tv_sec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = 0; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = 0; + inode->i_ctime.tv_nsec = 0; + + /* properly initialize special inodes */ + if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || + S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) + init_special_inode(inode, lower_inode->i_mode, + lower_inode->i_rdev); + + /* all well, copy inode attributes */ + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + + fix_derived_permission(inode); + + unlock_new_inode(inode); + return inode; +} + +/* + * Connect a sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path) +{ + int err = 0; + struct inode *inode; + struct inode *lower_inode; + struct super_block *lower_sb; + + lower_inode = lower_path->dentry->d_inode; + lower_sb = sdcardfs_lower_super(sb); + + /* check that the lower file system didn't cross a mount point */ + if (lower_inode->i_sb != lower_sb) { + err = -EXDEV; + goto out; + } + + /* + * We allocate our new inode below by calling sdcardfs_iget, + * which will initialize some of the new inode's fields + */ + + /* inherit lower inode number for sdcardfs's inode */ + inode = sdcardfs_iget(sb, lower_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + update_derived_permission(dentry); +out: + return err; +} + +/* + * Main driver function for sdcardfs's lookup. + * + * Returns: NULL (ok), ERR_PTR if an error occurred. + * Fills in lower_parent_path with on success. + */ +static struct dentry *__sdcardfs_lookup(struct dentry *dentry, + struct nameidata *nd, struct path *lower_parent_path) +{ + int err = 0; + struct vfsmount *lower_dir_mnt; + struct dentry *lower_dir_dentry = NULL; + struct dentry *lower_dentry; + const char *name; + struct nameidata lower_nd; + struct path lower_path; + struct qstr this; + struct sdcardfs_sb_info *sbi; + + sbi = SDCARDFS_SB(dentry->d_sb); + /* must initialize dentry operations */ + d_set_d_op(dentry, &sdcardfs_ci_dops); + + if (IS_ROOT(dentry)) + goto out; + + name = dentry->d_name.name; + + /* now start the actual lookup procedure */ + lower_dir_dentry = lower_parent_path->dentry; + lower_dir_mnt = lower_parent_path->mnt; + + /* Use vfs_path_lookup to check if the dentry exists or not */ + if (sbi->options.lower_fs == LOWER_FS_EXT4) { + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, + LOOKUP_CASE_INSENSITIVE, &lower_nd); + } else if (sbi->options.lower_fs == LOWER_FS_FAT) { + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + &lower_nd); + } + + /* no error: handle positive dentries */ + if (!err) { + /* check if the dentry is an obb dentry + * if true, the lower_inode must be replaced with + * the inode of the graft path */ + + if(need_graft_path(dentry)) { + + /* setup_obb_dentry() + * The lower_path will be stored to the dentry's orig_path + * and the base obbpath will be copyed to the lower_path variable. + * if an error returned, there's no change in the lower_path + * returns: -ERRNO if error (0: no error) */ + err = setup_obb_dentry(dentry, &lower_nd.path); + + if(err) { + /* if the sbi->obbpath is not available, we can optionally + * setup the lower_path with its orig_path. + * but, the current implementation just returns an error + * because the sdcard daemon also regards this case as + * a lookup fail. */ + printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + sdcardfs_put_reset_orig_path(dentry); + goto out; + } + } + + sdcardfs_set_lower_path(dentry, &lower_nd.path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_nd.path); + if (err) /* path_put underlying path on error */ + sdcardfs_put_reset_lower_path(dentry); + goto out; + } + + /* + * We don't consider ENOENT an error, and we want to return a + * negative dentry. + */ + if (err && err != -ENOENT) + goto out; + + /* instatiate a new negative dentry */ + this.name = name; + this.len = strlen(name); + this.hash = full_name_hash(this.name, this.len); + lower_dentry = d_lookup(lower_dir_dentry, &this); + if (lower_dentry) + goto setup_lower; + + lower_dentry = d_alloc(lower_dir_dentry, &this); + if (!lower_dentry) { + err = -ENOMEM; + goto out; + } + d_add(lower_dentry, NULL); /* instantiate and hash */ + +setup_lower: + lower_path.dentry = lower_dentry; + lower_path.mnt = mntget(lower_dir_mnt); + sdcardfs_set_lower_path(dentry, &lower_path); + + /* + * If the intent is to create a file, then don't return an error, so + * the VFS will continue the process of making this negative dentry + * into a positive one. + */ + if (nd) { + if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) + err = 0; + } else + err = 0; + +out: + return ERR_PTR(err); +} + +/* + * On success: + * fills dentry object appropriate values and returns NULL. + * On fail (== error) + * returns error ptr + * + * @dir : Parent inode. It is locked (dir->i_mutex) + * @dentry : Target dentry to lookup. we should set each of fields. + * (dentry->d_name is initialized already) + * @nd : nameidata of parent inode + */ +struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *ret = NULL, *parent; + struct path lower_parent_path; + int err = 0; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + parent = dget_parent(dentry); + + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 0, 0)) { + ret = ERR_PTR(-EACCES); + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + goto out_err; + } + + /* save current_cred and override it */ + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(parent, &lower_parent_path); + + /* allocate dentry private data. We free it in ->d_release */ + err = new_dentry_private_data(dentry); + if (err) { + ret = ERR_PTR(err); + goto out; + } + + ret = __sdcardfs_lookup(dentry, nd, &lower_parent_path); + if (IS_ERR(ret)) + { + goto out; + } + if (ret) + dentry = ret; + if (dentry->d_inode) { + fsstack_copy_attr_times(dentry->d_inode, + sdcardfs_lower_inode(dentry->d_inode)); + /* get drived permission */ + get_derived_permission(parent, dentry); + fix_derived_permission(dentry->d_inode); + } + /* update parent directory's atime */ + fsstack_copy_attr_atime(parent->d_inode, + sdcardfs_lower_inode(parent->d_inode)); + +out: + sdcardfs_put_lower_path(parent, &lower_parent_path); + REVERT_CRED(saved_cred); +out_err: + dput(parent); + return ret; +} diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c new file mode 100644 index 000000000000..1fdceffec72c --- /dev/null +++ b/fs/sdcardfs/main.c @@ -0,0 +1,425 @@ +/* + * fs/sdcardfs/main.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include +#include +#include + +enum { + Opt_uid, + Opt_gid, + Opt_wgid, + Opt_debug, + Opt_split, + Opt_derive, + Opt_lower_fs, + Opt_reserved_mb, + Opt_err, +}; + +static const match_table_t sdcardfs_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_wgid, "wgid=%u"}, + {Opt_debug, "debug"}, + {Opt_split, "split"}, + {Opt_derive, "derive=%s"}, + {Opt_lower_fs, "lower_fs=%s"}, + {Opt_reserved_mb, "reserved_mb=%u"}, + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *sb, char *options, int silent, + int *debug, struct sdcardfs_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *string_option; + + /* by default, we use AID_MEDIA_RW as uid, gid */ + opts->fs_low_uid = AID_MEDIA_RW; + opts->fs_low_gid = AID_MEDIA_RW; + /* by default, we use AID_SDCARD_RW as write_gid */ + opts->write_gid = AID_SDCARD_RW; + /* default permission policy + * (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */ + opts->derive = DERIVE_NONE; + opts->split_perms = 0; + /* by default, we use LOWER_FS_EXT4 as lower fs type */ + opts->lower_fs = LOWER_FS_EXT4; + /* by default, 0MB is reserved */ + opts->reserved_mb = 0; + + *debug = 0; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sdcardfs_tokens, args); + + switch (token) { + case Opt_debug: + *debug = 1; + break; + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_low_uid = option; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_low_gid = option; + break; + case Opt_wgid: + if (match_int(&args[0], &option)) + return 0; + opts->write_gid = option; + break; + case Opt_split: + opts->split_perms=1; + break; + case Opt_derive: + string_option = match_strdup(&args[0]); + if (!strcmp("none", string_option)) { + opts->derive = DERIVE_NONE; + } else if (!strcmp("legacy", string_option)) { + opts->derive = DERIVE_LEGACY; + } else if (!strcmp("unified", string_option)) { + opts->derive = DERIVE_UNIFIED; + } else { + kfree(string_option); + goto invalid_option; + } + kfree(string_option); + break; + case Opt_lower_fs: + string_option = match_strdup(&args[0]); + if (!strcmp("ext4", string_option)) { + opts->lower_fs = LOWER_FS_EXT4; + } else if (!strcmp("fat", string_option)) { + opts->lower_fs = LOWER_FS_FAT; + } else { + kfree(string_option); + goto invalid_option; + } + kfree(string_option); + break; + case Opt_reserved_mb: + if (match_int(&args[0], &option)) + return 0; + opts->reserved_mb = option; + break; + /* unknown option */ + default: +invalid_option: + if (!silent) { + printk( KERN_ERR "Unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + + if (*debug) { + printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); + printk( KERN_INFO "sdcardfs : options - uid:%d\n", + opts->fs_low_uid); + printk( KERN_INFO "sdcardfs : options - gid:%d\n", + opts->fs_low_gid); + } + + return 0; +} + +/* + * our custom d_alloc_root work-alike + * + * we can't use d_alloc_root if we want to use our own interpose function + * unchanged, so we simply call our own "fake" d_alloc_root + */ +static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) +{ + struct dentry *ret = NULL; + + if (sb) { + static const struct qstr name = { + .name = "/", + .len = 1 + }; + + ret = d_alloc(NULL, &name); + if (ret) { + d_set_d_op(ret, &sdcardfs_ci_dops); + ret->d_sb = sb; + ret->d_parent = ret; + } + } + return ret; +} + +/* + * There is no need to lock the sdcardfs_super_info's rwsem as there is no + * way anyone can have a reference to the superblock at this point in time. + */ +static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, + void *raw_data, int silent) +{ + int err = 0; + int debug; + struct super_block *lower_sb; + struct path lower_path; + struct sdcardfs_sb_info *sb_info; + void *pkgl_id; + + printk(KERN_INFO "sdcardfs version 2.0\n"); + + if (!dev_name) { + printk(KERN_ERR + "sdcardfs: read_super: missing dev_name argument\n"); + err = -EINVAL; + goto out; + } + + printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); + printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); + + /* parse lower path */ + err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + &lower_path); + if (err) { + printk(KERN_ERR "sdcardfs: error accessing " + "lower directory '%s'\n", dev_name); + goto out; + } + + /* allocate superblock private data */ + sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); + if (!SDCARDFS_SB(sb)) { + printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + err = -ENOMEM; + goto out_free; + } + + sb_info = sb->s_fs_info; + + /* parse options */ + err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); + if (err) { + printk(KERN_ERR "sdcardfs: invalid options\n"); + goto out_freesbi; + } + + if (sb_info->options.derive != DERIVE_NONE) { + pkgl_id = packagelist_create(sb_info->options.write_gid); + if(IS_ERR(pkgl_id)) + goto out_freesbi; + else + sb_info->pkgl_id = pkgl_id; + } + + /* set the lower superblock field of upper superblock */ + lower_sb = lower_path.dentry->d_sb; + atomic_inc(&lower_sb->s_active); + sdcardfs_set_lower_super(sb, lower_sb); + + /* inherit maxbytes from lower file system */ + sb->s_maxbytes = lower_sb->s_maxbytes; + + /* + * Our c/m/atime granularity is 1 ns because we may stack on file + * systems whose granularity is as good. + */ + sb->s_time_gran = 1; + + sb->s_magic = SDCARDFS_SUPER_MAGIC; + sb->s_op = &sdcardfs_sops; + + /* see comment next to the definition of sdcardfs_d_alloc_root */ + sb->s_root = sdcardfs_d_alloc_root(sb); + if (!sb->s_root) { + err = -ENOMEM; + goto out_sput; + } + + /* link the upper and lower dentries */ + sb->s_root->d_fsdata = NULL; + err = new_dentry_private_data(sb->s_root); + if (err) + goto out_freeroot; + + /* set the lower dentries for s_root */ + sdcardfs_set_lower_path(sb->s_root, &lower_path); + + /* call interpose to create the upper level inode */ + err = sdcardfs_interpose(sb->s_root, sb, &lower_path); + if (!err) { + /* setup permission policy */ + switch(sb_info->options.derive) { + case DERIVE_NONE: + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); + sb_info->obbpath_s = NULL; + break; + case DERIVE_LEGACY: + /* Legacy behavior used to support internal multiuser layout which + * places user_id at the top directory level, with the actual roots + * just below that. Shared OBB path is also at top level. */ + setup_derived_state(sb->s_root->d_inode, + PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + /* initialize the obbpath string and lookup the path + * sb_info->obb_path will be deactivated by path_put + * on sdcardfs_put_super */ + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + err = prepare_dir(sb_info->obbpath_s, + sb_info->options.fs_low_uid, + sb_info->options.fs_low_gid, 00755); + if(err) + printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", + __func__,__LINE__, sb_info->obbpath_s); + break; + case DERIVE_UNIFIED: + /* Unified multiuser layout which places secondary user_id under + * /Android/user and shared OBB path under /Android/obb. */ + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); + break; + } + fix_derived_permission(sb->s_root->d_inode); + + if (!silent) + printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + dev_name, lower_sb->s_type->name); + goto out; + } + /* else error: fall through */ + + free_dentry_private_data(sb->s_root); +out_freeroot: + dput(sb->s_root); +out_sput: + /* drop refs we took earlier */ + atomic_dec(&lower_sb->s_active); + packagelist_destroy(sb_info->pkgl_id); +out_freesbi: + kfree(SDCARDFS_SB(sb)); + sb->s_fs_info = NULL; +out_free: + path_put(&lower_path); + +out: + return err; +} + +/* A feature which supports mount_nodev() with options */ +static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + int (*fill_super)(struct super_block *, const char *, void *, int)) + +{ + int error; + struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); + + if (IS_ERR(s)) + return ERR_CAST(s); + + s->s_flags = flags; + + error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); + if (error) { + deactivate_locked_super(s); + return ERR_PTR(error); + } + s->s_flags |= MS_ACTIVE; + return dget(s->s_root); +} + +struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + /* + * dev_name is a lower_path_name, + * raw_data is a option string. + */ + return mount_nodev_with_options(fs_type, flags, dev_name, + raw_data, sdcardfs_read_super); +} + +static struct file_system_type sdcardfs_fs_type = { + .owner = THIS_MODULE, + .name = SDCARDFS_NAME, + .mount = sdcardfs_mount, + .kill_sb = generic_shutdown_super, + .fs_flags = FS_REVAL_DOT, +}; + +static int __init init_sdcardfs_fs(void) +{ + int err; + + pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n"); + + err = sdcardfs_init_inode_cache(); + if (err) + goto out; + err = sdcardfs_init_dentry_cache(); + if (err) + goto out; + err = packagelist_init(); + if (err) + goto out; + err = register_filesystem(&sdcardfs_fs_type); +out: + if (err) { + sdcardfs_destroy_inode_cache(); + sdcardfs_destroy_dentry_cache(); + packagelist_exit(); + } + return err; +} + +static void __exit exit_sdcardfs_fs(void) +{ + sdcardfs_destroy_inode_cache(); + sdcardfs_destroy_dentry_cache(); + packagelist_exit(); + unregister_filesystem(&sdcardfs_fs_type); + pr_info("Completed sdcardfs module unload\n"); +} + +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" + " (http://www.fsl.cs.sunysb.edu/)"); +MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION + " (http://wrapfs.filesystems.org/)"); +MODULE_LICENSE("GPL"); + +module_init(init_sdcardfs_fs); +module_exit(exit_sdcardfs_fs); diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c new file mode 100644 index 000000000000..c807d7f18f8b --- /dev/null +++ b/fs/sdcardfs/mmap.c @@ -0,0 +1,82 @@ +/* + * fs/sdcardfs/mmap.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + int err; + struct file *file, *lower_file; + const struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + + lower_file = sdcardfs_lower_file(file); + /* + * XXX: vm_ops->fault may be called in parallel. Because we have to + * resort to temporarily changing the vma->vm_file to point to the + * lower file, a concurrent invocation of sdcardfs_fault could see a + * different value. In this workaround, we keep a different copy of + * the vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. A + * better fix would be to change the calling semantics of ->fault to + * take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + err = lower_vm_ops->fault(&lower_vma, vmf); + return err; +} + +static ssize_t sdcardfs_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, + unsigned long nr_segs) +{ + /* + * This function returns zero on purpose in order to support direct IO. + * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. + * + * However, this function won't be called by certain file operations + * including generic fs functions. * reads and writes are delivered to + * the lower file systems and the direct IOs will be handled by them. + * + * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), + * swap_writepage invokes this function directly. + */ + printk(KERN_INFO "%s, operation is not supported\n", __func__); + return 0; +} + +/* + * XXX: the default address_space_ops for sdcardfs is empty. We cannot set + * our inode->i_mapping->a_ops to NULL because too many code paths expect + * the a_ops vector to be non-NULL. + */ +const struct address_space_operations sdcardfs_aops = { + /* empty on purpose */ + .direct_IO = sdcardfs_direct_IO, +}; + +const struct vm_operations_struct sdcardfs_vm_ops = { + .fault = sdcardfs_fault, +}; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h new file mode 100644 index 000000000000..923ba101dfa9 --- /dev/null +++ b/fs/sdcardfs/multiuser.h @@ -0,0 +1,37 @@ +/* + * fs/sdcardfs/multiuser.h + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#define MULTIUSER_APP_PER_USER_RANGE 100000 + +typedef uid_t userid_t; +typedef uid_t appid_t; + +static inline userid_t multiuser_get_user_id(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +static inline appid_t multiuser_get_app_id(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} + +static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +} + diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c new file mode 100644 index 000000000000..c786d8f92203 --- /dev/null +++ b/fs/sdcardfs/packagelist.c @@ -0,0 +1,458 @@ +/* + * fs/sdcardfs/packagelist.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "strtok.h" +#include "hashtable.h" +#include +#include +#include +#include + +#define STRING_BUF_SIZE (512) + +struct hashtable_entry { + struct hlist_node hlist; + void *key; + int value; +}; + +struct packagelist_data { + DECLARE_HASHTABLE(package_to_appid,8); + DECLARE_HASHTABLE(appid_with_rw,7); + struct mutex hashtable_lock; + struct task_struct *thread_id; + gid_t write_gid; + char *strtok_last; + char read_buf[STRING_BUF_SIZE]; + char event_buf[STRING_BUF_SIZE]; + char app_name_buf[STRING_BUF_SIZE]; + char gids_buf[STRING_BUF_SIZE]; +}; + +static struct kmem_cache *hashtable_entry_cachep; + +/* Path to system-provided mapping of package name to appIds */ +static const char* const kpackageslist_file = "/data/system/packages.list"; +/* Supplementary groups to execute with */ +static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; + +static unsigned int str_hash(void *key) { + int i; + unsigned int h = strlen(key); + char *data = (char *)key; + + for (i = 0; i < strlen(key); i++) { + h = h * 31 + *data; + data++; + } + return h; +} + +static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid, h_n) + if (appid == hash_cur->key) + return 1; + return 0; +} + +/* Return if the calling UID holds sdcard_rw. */ +int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + appid_t appid; + int ret; + + /* No additional permissions enforcement */ + if (derive == DERIVE_NONE) { + return 1; + } + + appid = multiuser_get_app_id(current_fsuid()); + mutex_lock(&pkgl_dat->hashtable_lock); + ret = contain_appid_key(pkgl_dat, (void *)appid); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; +} + +appid_t get_appid(void *pkgl_id, const char *app_name) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + unsigned int hash = str_hash((void *)app_name); + appid_t ret_id; + + //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); + mutex_lock(&pkgl_dat->hashtable_lock); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); + if (!strcasecmp(app_name, hash_cur->key)) { + ret_id = (appid_t)hash_cur->value; + mutex_unlock(&pkgl_dat->hashtable_lock); + //printk(KERN_INFO "=> app_id: %d\n", (int)ret_id); + return ret_id; + } + } + mutex_unlock(&pkgl_dat->hashtable_lock); + //printk(KERN_INFO "=> app_id: %d\n", 0); + return 0; +} + +/* Kernel has already enforced everything we returned through + * derive_permissions_locked(), so this is used to lock down access + * even further, such as enforcing that apps hold sdcard_rw. */ +int check_caller_access_to_name(struct inode *parent_node, const char* name, + derive_t derive, int w_ok, int has_rw) { + + /* Always block security-sensitive files at root */ + if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { + if (!strcasecmp(name, "autorun.inf") + || !strcasecmp(name, ".android_secure") + || !strcasecmp(name, "android_secure")) { + return 0; + } + } + + /* No additional permissions enforcement */ + if (derive == DERIVE_NONE) { + return 1; + } + + /* Root always has access; access for any other UIDs should always + * be controlled through packages.list. */ + if (current_fsuid() == 0) { + return 1; + } + + /* If asking to write, verify that caller either owns the + * parent or holds sdcard_rw. */ + if (w_ok) { + if (parent_node && + (current_fsuid() == SDCARDFS_I(parent_node)->d_uid)) { + return 1; + } + return has_rw; + } + + /* No extra permissions to enforce */ + return 1; +} + +/* This function is used when file opening. The open flags must be + * checked before calling check_caller_access_to_name() */ +int open_flags_to_access_mode(int open_flags) { + if((open_flags & O_ACCMODE) == O_RDONLY) { + return 0; /* R_OK */ + } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + return 1; /* W_OK */ + } else { + /* Probably O_RDRW, but treat as default to be safe */ + return 1; /* R_OK | W_OK */ + } +} + +static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + struct hlist_node *h_n; + unsigned int hash = str_hash(key); + + //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + if (!strcasecmp(key, hash_cur->key)) { + hash_cur->value = value; + return 0; + } + } + new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->key = kstrdup(key, GFP_KERNEL); + new_entry->value = value; + hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash); + return 0; +} + +static void remove_str_to_int(struct hashtable_entry *h_entry) { + //printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value); + kfree(h_entry->key); + kmem_cache_free(hashtable_entry_cachep, h_entry); +} + +static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + struct hlist_node *h_n; + + //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, + (unsigned int)key, h_n) { + if (key == hash_cur->key) { + hash_cur->value = value; + return 0; + } + } + new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->key = key; + new_entry->value = value; + hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, + (unsigned int)new_entry->key); + return 0; +} + +static void remove_int_to_null(struct hashtable_entry *h_entry) { + //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value); + kmem_cache_free(hashtable_entry_cachep, h_entry); +} + +static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + struct hlist_node *h_t; + int i; + + hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist, h_n) + remove_str_to_int(hash_cur); + hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist, h_n) + remove_int_to_null(hash_cur); + + hash_init(pkgl_dat->package_to_appid); + hash_init(pkgl_dat->appid_with_rw); +} + +static int read_package_list(struct packagelist_data *pkgl_dat) { + int ret; + int fd; + int read_amount; + + printk(KERN_INFO "sdcardfs: read_package_list\n"); + + mutex_lock(&pkgl_dat->hashtable_lock); + + remove_all_hashentrys(pkgl_dat); + + fd = sys_open(kpackageslist_file, O_RDONLY, 0); + if (fd < 0) { + printk(KERN_ERR "sdcardfs: failed to open package list\n"); + mutex_unlock(&pkgl_dat->hashtable_lock); + return fd; + } + + while ((read_amount = sys_read(fd, pkgl_dat->read_buf, + sizeof(pkgl_dat->read_buf))) > 0) { + int appid; + char *token; + int one_line_len = 0; + int additional_read; + unsigned long ret_gid; + + while (one_line_len < read_amount) { + if (pkgl_dat->read_buf[one_line_len] == '\n') { + one_line_len++; + break; + } + one_line_len++; + } + additional_read = read_amount - one_line_len; + if (additional_read > 0) + sys_lseek(fd, -additional_read, SEEK_CUR); + + if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s", + pkgl_dat->app_name_buf, &appid, + pkgl_dat->gids_buf) == 3) { + ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); + if (ret) { + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; + } + + token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last); + while (token != NULL) { + if (!kstrtoul(token, 10, &ret_gid) && + (ret_gid == pkgl_dat->write_gid)) { + ret = insert_int_to_null(pkgl_dat, (void *)appid, 1); + if (ret) { + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; + } + break; + } + token = strtok_r(NULL, ",", &pkgl_dat->strtok_last); + } + } + } + + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return 0; +} + +static int packagelist_reader(void *thread_data) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data; + struct inotify_event *event; + bool active = false; + int event_pos; + int event_size; + int res = 0; + int nfd; + + allow_signal(SIGINT); + + nfd = sys_inotify_init(); + if (nfd < 0) { + printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd); + return nfd; + } + + while (!kthread_should_stop()) { + if (signal_pending(current)) { + ssleep(1); + continue; + } + + if (!active) { + res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF); + if (res < 0) { + if (res == -ENOENT || res == -EACCES) { + /* Framework may not have created yet, sleep and retry */ + printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n"); + ssleep(2); + printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n"); + continue; + } else { + printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res); + goto interruptable_sleep; + } + } + /* Watch above will tell us about any future changes, so + * read the current state. */ + res = read_package_list(pkgl_dat); + if (res) { + printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res); + goto interruptable_sleep; + } + active = true; + } + + event_pos = 0; + res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf)); + if (res < (int) sizeof(*event)) { + if (res == -EINTR) + continue; + printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res); + goto interruptable_sleep; + } + + while (res >= (int) sizeof(*event)) { + event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos); + + printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask); + if ((event->mask & IN_IGNORED) == IN_IGNORED) { + /* Previously watched file was deleted, probably due to move + * that swapped in new data; re-arm the watch and read. */ + active = false; + } + + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + continue; + +interruptable_sleep: + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + flush_signals(current); + sys_close(nfd); + return res; +} + +void * packagelist_create(gid_t write_gid) +{ + struct packagelist_data *pkgl_dat; + struct task_struct *packagelist_thread; + + pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); + if (!pkgl_dat) { + printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_init(&pkgl_dat->hashtable_lock); + hash_init(pkgl_dat->package_to_appid); + hash_init(pkgl_dat->appid_with_rw); + pkgl_dat->write_gid = write_gid; + + packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld"); + if (IS_ERR(packagelist_thread)) { + printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + kfree(pkgl_dat); + return packagelist_thread; + } + pkgl_dat->thread_id = packagelist_thread; + + printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n", + (int)pkgl_dat->thread_id->pid); + + return (void *)pkgl_dat; +} + +void packagelist_destroy(void *pkgl_id) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + pid_t pkgl_pid = pkgl_dat->thread_id->pid; + + force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id); + kthread_stop(pkgl_dat->thread_id); + remove_all_hashentrys(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid); + kfree(pkgl_dat); +} + +int packagelist_init(void) +{ + hashtable_entry_cachep = + kmem_cache_create("packagelist_hashtable_entry", + sizeof(struct hashtable_entry), 0, 0, NULL); + if (!hashtable_entry_cachep) { + printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + return -ENOMEM; + } + + return 0; +} + +void packagelist_exit(void) +{ + if (hashtable_entry_cachep) + kmem_cache_destroy(hashtable_entry_cachep); +} + + diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h new file mode 100644 index 000000000000..90f8b24e4a52 --- /dev/null +++ b/fs/sdcardfs/sdcardfs.h @@ -0,0 +1,493 @@ +/* + * fs/sdcardfs/sdcardfs.h + * + * The sdcardfs v2.0 + * This file system replaces the sdcard daemon on Android + * On version 2.0, some of the daemon functions have been ported + * to support the multi-user concepts of Android 4.4 + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#ifndef _SDCARDFS_H_ +#define _SDCARDFS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multiuser.h" + +/* the file system name */ +#define SDCARDFS_NAME "sdcardfs" + +/* sdcardfs root inode number */ +#define SDCARDFS_ROOT_INO 1 + +/* useful for tracking code reachability */ +#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) + +#define SDCARDFS_DIRENT_SIZE 256 + +/* temporary static uid settings for development */ +#define AID_ROOT 0 /* uid for accessing /mnt/sdcard & extSdcard */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ + +#define AID_SDCARD_RW 1015 /* external storage write access */ +#define AID_SDCARD_R 1028 /* external storage read access */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ + +#define AID_PACKAGE_INFO 1027 + +#define fix_derived_permission(x) \ + do { \ + (x)->i_uid = SDCARDFS_I(x)->d_uid; \ + (x)->i_gid = SDCARDFS_I(x)->d_gid; \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ + } while (0) + +/* OVERRIDE_CRED() and REVERT_CRED() + * OVERRID_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. + * REVERT_CRED() + * restore original task->cred->fsuid/fsgid. + * These two macro should be used in pair, and OVERRIDE_CRED() should be + * placed at the beginning of a function, right after variable declaration. + */ +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \ + saved_cred = override_fsids(sdcardfs_sbi); \ + if (!saved_cred) { return -ENOMEM; } + +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \ + saved_cred = override_fsids(sdcardfs_sbi); \ + if (!saved_cred) { return ERR_PTR(-ENOMEM); } + +#define REVERT_CRED(saved_cred) revert_fsids(saved_cred) + +#define DEBUG_CRED() \ + printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ + __FUNCTION__, __LINE__, \ + (int)current->cred->fsuid, \ + (int)current->cred->fsgid); + +/* Android 4.4 support */ + +/* Permission mode for a specific node. Controls how file permissions + * are derived for children nodes. */ +typedef enum { + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. */ + PERM_LEGACY_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/user" */ + PERM_ANDROID_USER, +} perm_t; + +/* Permissions structure to derive */ +typedef enum { + DERIVE_NONE, + DERIVE_LEGACY, + DERIVE_UNIFIED, +} derive_t; + +typedef enum { + LOWER_FS_EXT4, + LOWER_FS_FAT, +} lower_fs_t; + +struct sdcardfs_sb_info; +struct sdcardfs_mount_options; + +/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi); +/* Do not directly use this function, use REVERT_CRED() instead. */ +void revert_fsids(const struct cred * old_cred); + +/* operations vectors defined in specific files */ +extern const struct file_operations sdcardfs_main_fops; +extern const struct file_operations sdcardfs_dir_fops; +extern const struct inode_operations sdcardfs_main_iops; +extern const struct inode_operations sdcardfs_dir_iops; +extern const struct inode_operations sdcardfs_symlink_iops; +extern const struct super_operations sdcardfs_sops; +extern const struct dentry_operations sdcardfs_ci_dops; +extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops; +extern const struct vm_operations_struct sdcardfs_vm_ops; + +extern int sdcardfs_init_inode_cache(void); +extern void sdcardfs_destroy_inode_cache(void); +extern int sdcardfs_init_dentry_cache(void); +extern void sdcardfs_destroy_dentry_cache(void); +extern int new_dentry_private_data(struct dentry *dentry); +extern void free_dentry_private_data(struct dentry *dentry); +extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd); +extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path); + +/* file private data */ +struct sdcardfs_file_info { + struct file *lower_file; + const struct vm_operations_struct *lower_vm_ops; +}; + +/* sdcardfs inode data in memory */ +struct sdcardfs_inode_info { + struct inode *lower_inode; + /* state derived based on current position in hierachy + * caution: d_mode does not include file types + */ + perm_t perm; + userid_t userid; + uid_t d_uid; + gid_t d_gid; + mode_t d_mode; + + struct inode vfs_inode; +}; + +/* sdcardfs dentry data in memory */ +struct sdcardfs_dentry_info { + spinlock_t lock; /* protects lower_path */ + struct path lower_path; + struct path orig_path; +}; + +struct sdcardfs_mount_options { + uid_t fs_low_uid; + gid_t fs_low_gid; + gid_t write_gid; + int split_perms; + derive_t derive; + lower_fs_t lower_fs; + unsigned int reserved_mb; +}; + +/* sdcardfs super-block data in memory */ +struct sdcardfs_sb_info { + struct super_block *lower_sb; + /* derived perm policy : some of options have been added + * to sdcardfs_mount_options (Android 4.4 support) */ + struct sdcardfs_mount_options options; + spinlock_t lock; /* protects obbpath */ + char *obbpath_s; + struct path obbpath; + void *pkgl_id; +}; + +/* + * inode to private data + * + * Since we use containers and the struct inode is _inside_ the + * sdcardfs_inode_info structure, SDCARDFS_I will always (given a non-NULL + * inode pointer), return a valid non-NULL pointer. + */ +static inline struct sdcardfs_inode_info *SDCARDFS_I(const struct inode *inode) +{ + return container_of(inode, struct sdcardfs_inode_info, vfs_inode); +} + +/* dentry to private data */ +#define SDCARDFS_D(dent) ((struct sdcardfs_dentry_info *)(dent)->d_fsdata) + +/* superblock to private data */ +#define SDCARDFS_SB(super) ((struct sdcardfs_sb_info *)(super)->s_fs_info) + +/* file to private Data */ +#define SDCARDFS_F(file) ((struct sdcardfs_file_info *)((file)->private_data)) + +/* file to lower file */ +static inline struct file *sdcardfs_lower_file(const struct file *f) +{ + return SDCARDFS_F(f)->lower_file; +} + +static inline void sdcardfs_set_lower_file(struct file *f, struct file *val) +{ + SDCARDFS_F(f)->lower_file = val; +} + +/* inode to lower inode. */ +static inline struct inode *sdcardfs_lower_inode(const struct inode *i) +{ + return SDCARDFS_I(i)->lower_inode; +} + +static inline void sdcardfs_set_lower_inode(struct inode *i, struct inode *val) +{ + SDCARDFS_I(i)->lower_inode = val; +} + +/* superblock to lower superblock */ +static inline struct super_block *sdcardfs_lower_super( + const struct super_block *sb) +{ + return SDCARDFS_SB(sb)->lower_sb; +} + +static inline void sdcardfs_set_lower_super(struct super_block *sb, + struct super_block *val) +{ + SDCARDFS_SB(sb)->lower_sb = val; +} + +/* path based (dentry/mnt) macros */ +static inline void pathcpy(struct path *dst, const struct path *src) +{ + dst->dentry = src->dentry; + dst->mnt = src->mnt; +} + +/* sdcardfs_get_pname functions calls path_get() + * therefore, the caller must call "proper" path_put functions + */ +#define SDCARDFS_DENT_FUNC(pname) \ +static inline void sdcardfs_get_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + pathcpy(pname, &SDCARDFS_D(dent)->pname); \ + path_get(pname); \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_put_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + path_put(pname); \ + return; \ +} \ +static inline void sdcardfs_set_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + pathcpy(&SDCARDFS_D(dent)->pname, pname); \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_reset_##pname(const struct dentry *dent) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + SDCARDFS_D(dent)->pname.dentry = NULL; \ + SDCARDFS_D(dent)->pname.mnt = NULL; \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ +{ \ + struct path pname; \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + if(SDCARDFS_D(dent)->pname.dentry) { \ + pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ + SDCARDFS_D(dent)->pname.dentry = NULL; \ + SDCARDFS_D(dent)->pname.mnt = NULL; \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + path_put(&pname); \ + } else \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} + +SDCARDFS_DENT_FUNC(lower_path) +SDCARDFS_DENT_FUNC(orig_path) + +static inline int has_graft_path(const struct dentry *dent) +{ + int ret = 0; + + spin_lock(&SDCARDFS_D(dent)->lock); + if (SDCARDFS_D(dent)->orig_path.dentry != NULL) + ret = 1; + spin_unlock(&SDCARDFS_D(dent)->lock); + + return ret; +} + +static inline void sdcardfs_get_real_lower(const struct dentry *dent, + struct path *real_lower) +{ + /* in case of a local obb dentry + * the orig_path should be returned + */ + if(has_graft_path(dent)) + sdcardfs_get_orig_path(dent, real_lower); + else + sdcardfs_get_lower_path(dent, real_lower); +} + +static inline void sdcardfs_put_real_lower(const struct dentry *dent, + struct path *real_lower) +{ + if(has_graft_path(dent)) + sdcardfs_put_orig_path(dent, real_lower); + else + sdcardfs_put_lower_path(dent, real_lower); +} + +/* for packagelist.c */ +extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive); +extern appid_t get_appid(void *pkgl_id, const char *app_name); +extern int check_caller_access_to_name(struct inode *parent_node, const char* name, + derive_t derive, int w_ok, int has_rw); +extern int open_flags_to_access_mode(int open_flags); +extern void * packagelist_create(gid_t write_gid); +extern void packagelist_destroy(void *pkgl_id); +extern int packagelist_init(void); +extern void packagelist_exit(void); + +/* for derived_perm.c */ +extern void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, gid_t gid, mode_t mode); +extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); +extern void update_derived_permission(struct dentry *dentry); +extern int need_graft_path(struct dentry *dentry); +extern int is_base_obbpath(struct dentry *dentry); +extern int is_obbpath_invalid(struct dentry *dentry); +extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); + +/* locking helpers */ +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + return dir; +} + +static inline void unlock_dir(struct dentry *dir) +{ + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); +} + +static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode) +{ + int err; + struct dentry *dent; + struct iattr attrs; + struct nameidata nd; + + err = kern_path_parent(path_s, &nd); + if (err) { + if (err == -EEXIST) + err = 0; + goto out; + } + + dent = lookup_create(&nd, 1); + if (IS_ERR(dent)) { + err = PTR_ERR(dent); + if (err == -EEXIST) + err = 0; + goto out_unlock; + } + + err = vfs_mkdir(nd.path.dentry->d_inode, dent, mode); + if (err) { + if (err == -EEXIST) + err = 0; + goto out_dput; + } + + attrs.ia_uid = uid; + attrs.ia_gid = gid; + attrs.ia_valid = ATTR_UID | ATTR_GID; + mutex_lock(&dent->d_inode->i_mutex); + notify_change(dent, &attrs); + mutex_unlock(&dent->d_inode->i_mutex); + +out_dput: + dput(dent); + +out_unlock: + /* parent dentry locked by lookup_create */ + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + path_put(&nd.path); + +out: + return err; +} + +/* + * Return 1, if a disk has enough free space, otherwise 0. + * We assume that any files can not be overwritten. + */ +static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir) +{ + int err; + struct path lower_path; + struct kstatfs statfs; + u64 avail; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + if (sbi->options.reserved_mb) { + /* Get fs stat of lower filesystem. */ + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_statfs(&lower_path, &statfs); + sdcardfs_put_lower_path(dentry, &lower_path); + + if (unlikely(err)) + return 0; + + /* Invalid statfs informations. */ + if (unlikely(statfs.f_bsize == 0)) + return 0; + + /* if you are checking directory, set size to f_bsize. */ + if (unlikely(dir)) + size = statfs.f_bsize; + + /* available size */ + avail = statfs.f_bavail * statfs.f_bsize; + + /* not enough space */ + if ((u64)size > avail) + return 0; + + /* enough space */ + if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024)) + return 1; + + return 0; + } else + return 1; +} + +#endif /* not _SDCARDFS_H_ */ diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h new file mode 100644 index 000000000000..50ab25aa0bc4 --- /dev/null +++ b/fs/sdcardfs/strtok.h @@ -0,0 +1,75 @@ +/* + * fs/sdcardfs/strtok.h + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +static char * +strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + /* if (s == NULL && (s = *last) == NULL) + return NULL; */ + if (s == NULL) { + s = *last; + if (s == NULL) + return NULL; + } + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + sc = *spanp++; + if (sc == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return tok; + } + } while (sc != 0); + } + + /* NOTREACHED */ +} + diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c new file mode 100644 index 000000000000..1d206c82dfdf --- /dev/null +++ b/fs/sdcardfs/super.c @@ -0,0 +1,229 @@ +/* + * fs/sdcardfs/super.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* + * The inode cache is used with alloc_inode for both our inode info and the + * vfs inode. + */ +static struct kmem_cache *sdcardfs_inode_cachep; + +/* final actions when unmounting a file system */ +static void sdcardfs_put_super(struct super_block *sb) +{ + struct sdcardfs_sb_info *spd; + struct super_block *s; + + spd = SDCARDFS_SB(sb); + if (!spd) + return; + + if(spd->obbpath_s) { + kfree(spd->obbpath_s); + path_put(&spd->obbpath); + } + + /* decrement lower super references */ + s = sdcardfs_lower_super(sb); + sdcardfs_set_lower_super(sb, NULL); + atomic_dec(&s->s_active); + + if(spd->pkgl_id) + packagelist_destroy(spd->pkgl_id); + + kfree(spd); + sb->s_fs_info = NULL; +} + +static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + struct path lower_path; + u32 min_blocks; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_statfs(&lower_path, buf); + sdcardfs_put_lower_path(dentry, &lower_path); + + if (sbi->options.reserved_mb) { + /* Invalid statfs informations. */ + if (buf->f_bsize == 0) { + printk(KERN_ERR "Returned block size is zero.\n"); + return -EINVAL; + } + + min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize); + buf->f_blocks -= min_blocks; + + if (buf->f_bavail > min_blocks) + buf->f_bavail -= min_blocks; + else + buf->f_bavail = 0; + + /* Make reserved blocks invisiable to media storage */ + buf->f_bfree = buf->f_bavail; + } + + /* set return buf to our f/s to avoid confusing user-level utils */ + buf->f_type = SDCARDFS_SUPER_MAGIC; + + return err; +} + +/* + * @flags: numeric mount options + * @options: mount options string + */ +static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { + printk(KERN_ERR + "sdcardfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + + return err; +} + +/* + * Called by iput() when the inode reference count reached zero + * and the inode is not hashed anywhere. Used to clear anything + * that needs to be, before the inode is completely destroyed and put + * on the inode free list. + */ +static void sdcardfs_evict_inode(struct inode *inode) +{ + struct inode *lower_inode; + + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + /* + * Decrement a reference to a lower_inode, which was incremented + * by our read_inode when it was created initially. + */ + lower_inode = sdcardfs_lower_inode(inode); + sdcardfs_set_lower_inode(inode, NULL); + iput(lower_inode); +} + +static struct inode *sdcardfs_alloc_inode(struct super_block *sb) +{ + struct sdcardfs_inode_info *i; + + i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL); + if (!i) + return NULL; + + /* memset everything up to the inode to 0 */ + memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode)); + + i->vfs_inode.i_version = 1; + return &i->vfs_inode; +} + +static void sdcardfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); +} + +/* sdcardfs inode cache constructor */ +static void init_once(void *obj) +{ + struct sdcardfs_inode_info *i = obj; + + inode_init_once(&i->vfs_inode); +} + +int sdcardfs_init_inode_cache(void) +{ + int err = 0; + + sdcardfs_inode_cachep = + kmem_cache_create("sdcardfs_inode_cache", + sizeof(struct sdcardfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT, init_once); + if (!sdcardfs_inode_cachep) + err = -ENOMEM; + return err; +} + +/* sdcardfs inode cache destructor */ +void sdcardfs_destroy_inode_cache(void) +{ + if (sdcardfs_inode_cachep) + kmem_cache_destroy(sdcardfs_inode_cachep); +} + +/* + * Used only in nfs, to kill any pending RPC tasks, so that subsequent + * code can actually succeed and won't leave tasks that need handling. + */ +static void sdcardfs_umount_begin(struct super_block *sb) +{ + struct super_block *lower_sb; + + lower_sb = sdcardfs_lower_super(sb); + if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_sb); +} + +static int sdcardfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(mnt->mnt_sb); + struct sdcardfs_mount_options *opts = &sbi->options; + + if (opts->fs_low_uid != 0) + seq_printf(m, ",uid=%u", opts->fs_low_uid); + if (opts->fs_low_gid != 0) + seq_printf(m, ",gid=%u", opts->fs_low_gid); + + if (opts->derive == DERIVE_NONE) + seq_printf(m, ",derive=none"); + else if (opts->derive == DERIVE_LEGACY) + seq_printf(m, ",derive=legacy"); + else if (opts->derive == DERIVE_UNIFIED) + seq_printf(m, ",derive=unified"); + + if (opts->reserved_mb != 0) + seq_printf(m, ",reserved=%uMB", opts->reserved_mb); + + return 0; +}; + +const struct super_operations sdcardfs_sops = { + .put_super = sdcardfs_put_super, + .statfs = sdcardfs_statfs, + .remount_fs = sdcardfs_remount_fs, + .evict_inode = sdcardfs_evict_inode, + .umount_begin = sdcardfs_umount_begin, + .show_options = sdcardfs_show_options, + .alloc_inode = sdcardfs_alloc_inode, + .destroy_inode = sdcardfs_destroy_inode, + .drop_inode = generic_delete_inode, +}; diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 1a6fee974116..6f7b217c44ac 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -56,6 +56,8 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" +#define SDCARDFS_SUPER_MAGIC 0xb550ca10 + #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb #define CGROUP2_SUPER_MAGIC 0x63677270 From a4f94187521ab7db2fce588e185757dfbe5f3e31 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 11 Feb 2016 16:44:15 -0800 Subject: [PATCH 0523/1276] ANDROID: vfs: add d_canonical_path for stacked filesystem support Inotify does not currently know when a filesystem is acting as a wrapper around another fs. This means that inotify watchers will miss any modifications to the base file, as well as any made in a separate stacked fs that points to the same file. d_canonical_path solves this problem by allowing the fs to map a dentry to a path in the lower fs. Inotify can use it to find the appropriate place to watch to be informed of all changes to a file. Change-Id: I09563baffad1711a045e45c1bd0bd8713c2cc0b6 Signed-off-by: Daniel Rosenberg --- fs/notify/inotify/inotify_user.c | 15 +++++++++++++-- include/linux/dcache.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ac6978d3208c..15f5655df87d 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -699,6 +699,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, struct fsnotify_group *group; struct inode *inode; struct path path; + struct path alteredpath; + struct path *canonical_path = &path; struct fd f; int ret; unsigned flags = 0; @@ -742,13 +744,22 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, if (ret) goto fput_and_out; + /* support stacked filesystems */ + if(path.dentry && path.dentry->d_op) { + if (path.dentry->d_op->d_canonical_path) { + path.dentry->d_op->d_canonical_path(&path, &alteredpath); + canonical_path = &alteredpath; + path_put(&path); + } + } + /* inode held in place by reference to path; group by fget on fd */ - inode = path.dentry->d_inode; + inode = canonical_path->dentry->d_inode; group = f.file->private_data; /* create/update an inode mark */ ret = inotify_update_watch(group, inode, mask); - path_put(&path); + path_put(canonical_path); fput_and_out: fdput(f); return ret; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index ef4b70f64f33..2dc6915d7abf 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -146,6 +146,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *); + void (*d_canonical_path)(const struct path *, struct path *); } ____cacheline_aligned; /* From 9234efb0005a02b5411ba20aaac93212415d0e24 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 15:29:51 -0700 Subject: [PATCH 0524/1276] ANDROID: mnt: Add filesystem private data to mount points This starts to add private data associated directly to mount points. The intent is to give filesystems a sense of where they have come from, as a means of letting a filesystem take different actions based on this information. Change-Id: Ie769d7b3bb2f5972afe05c1bf16cf88c91647ab2 Signed-off-by: Daniel Rosenberg --- fs/namespace.c | 26 +++++++++++++++++++++++++- fs/pnode.c | 29 +++++++++++++++++++++++++++++ fs/pnode.h | 1 + include/linux/fs.h | 3 +++ include/linux/mount.h | 1 + 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 99186556f8d3..cd4fb0e7e241 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -200,6 +200,7 @@ static struct mount *alloc_vfsmnt(const char *name) mnt->mnt_count = 1; mnt->mnt_writers = 0; #endif + mnt->mnt.data = NULL; INIT_HLIST_NODE(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); @@ -552,6 +553,7 @@ int sb_prepare_remount_readonly(struct super_block *sb) static void free_vfsmnt(struct mount *mnt) { + kfree(mnt->mnt.data); kfree_const(mnt->mnt_devname); #ifdef CONFIG_SMP free_percpu(mnt->mnt_pcp); @@ -955,6 +957,14 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (!mnt) return ERR_PTR(-ENOMEM); + if (type->alloc_mnt_data) { + mnt->mnt.data = type->alloc_mnt_data(); + if (!mnt->mnt.data) { + mnt_free_id(mnt); + free_vfsmnt(mnt); + return ERR_PTR(-ENOMEM); + } + } if (flags & SB_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; @@ -1002,6 +1012,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, if (!mnt) return ERR_PTR(-ENOMEM); + if (sb->s_op->clone_mnt_data) { + mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data); + if (!mnt->mnt.data) { + err = -ENOMEM; + goto out_free; + } + } + if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE)) mnt->mnt_group_id = 0; /* not a peer of original */ else @@ -2274,8 +2292,14 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, err = change_mount_flags(path->mnt, ms_flags); else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) err = -EPERM; - else + else { err = do_remount_sb(sb, sb_flags, data, 0); + namespace_lock(); + lock_mount_hash(); + propagate_remount(mnt); + unlock_mount_hash(); + namespace_unlock(); + } if (!err) { lock_mount_hash(); mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; diff --git a/fs/pnode.c b/fs/pnode.c index 53d411a371ce..386884dd6d97 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -607,3 +607,32 @@ int propagate_umount(struct list_head *list) return 0; } + +/* + * Iterates over all slaves, and slaves of slaves. + */ +static struct mount *next_descendent(struct mount *root, struct mount *cur) +{ + if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list)) + return first_slave(cur); + do { + if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list) + return next_slave(cur); + cur = cur->mnt_master; + } while (cur != root); + return NULL; +} + +void propagate_remount(struct mount *mnt) +{ + struct mount *m = mnt; + struct super_block *sb = mnt->mnt.mnt_sb; + + if (sb->s_op->copy_mnt_data) { + m = next_descendent(mnt, m); + while (m) { + sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data); + m = next_descendent(mnt, m); + } + } +} diff --git a/fs/pnode.h b/fs/pnode.h index dc87e65becd2..a9a6576540ad 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -44,6 +44,7 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *, int propagate_umount(struct list_head *); int propagate_mount_busy(struct mount *, int); void propagate_mount_unlock(struct mount *); +void propagate_remount(struct mount *); void mnt_release_group_id(struct mount *); int get_dominating_id(struct mount *mnt, const struct path *root); unsigned int mnt_get_count(struct mount *mnt); diff --git a/include/linux/fs.h b/include/linux/fs.h index 897eae8faee1..e30ab9dcaab4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1858,6 +1858,8 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); + void *(*clone_mnt_data) (void *); + void (*copy_mnt_data) (void *, void *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); @@ -2120,6 +2122,7 @@ struct file_system_type { #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); + void *(*alloc_mnt_data) (void); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; diff --git a/include/linux/mount.h b/include/linux/mount.h index 45b1f56c6c2f..1ff21c19b0b9 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -68,6 +68,7 @@ struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ int mnt_flags; + void *data; } __randomize_layout; struct file; /* forward dec */ From c82036997e96a521d89cf7b73efa3431802a5eda Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 22 Apr 2016 00:00:48 -0700 Subject: [PATCH 0525/1276] ANDROID: fuse: Add support for d_canonical_path Allows FUSE to report to inotify that it is acting as a layered filesystem. The userspace component returns a string representing the location of the underlying file. If the string cannot be resolved into a path, the top level path is returned instead. bug: 23904372 Change-Id: Iabdca0bbedfbff59e9c820c58636a68ef9683d9f Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 5 +++++ fs/fuse/dir.c | 46 +++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 3 +++ include/uapi/linux/fuse.h | 1 + 4 files changed, 55 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 11ea2c4a38ab..9fc81f1f0e7e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1900,6 +1901,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, cs->move_pages = 0; err = copy_out_args(cs, &req->out, nbytes); + if (req->in.h.opcode == FUSE_CANONICAL_PATH) { + req->out.h.error = kern_path((char *)req->out.args[0].value, 0, + req->canonical_path); + } fuse_copy_finish(cs); spin_lock(&fpq->lock); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0979609d6eba..6c407b930e36 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -262,6 +262,50 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) goto out; } +/* + * Get the canonical path. Since we must translate to a path, this must be done + * in the context of the userspace daemon, however, the userspace daemon cannot + * look up paths on its own. Instead, we handle the lookup as a special case + * inside of the write request. + */ +static void fuse_dentry_canonical_path(const struct path *path, struct path *canonical_path) { + struct inode *inode = path->dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + int err; + char *path_name; + + req = fuse_get_req(fc, 1); + err = PTR_ERR(req); + if (IS_ERR(req)) + goto default_path; + + path_name = (char*)__get_free_page(GFP_KERNEL); + if (!path_name) { + fuse_put_request(fc, req); + goto default_path; + } + + req->in.h.opcode = FUSE_CANONICAL_PATH; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 0; + req->out.numargs = 1; + req->out.args[0].size = PATH_MAX; + req->out.args[0].value = path_name; + req->canonical_path = canonical_path; + req->out.argvar = 1; + fuse_request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + free_page((unsigned long)path_name); + if (!err) + return; +default_path: + canonical_path->dentry = path->dentry; + canonical_path->mnt = path->mnt; + path_get(canonical_path); +} + static int invalid_nodeid(u64 nodeid) { return !nodeid || nodeid == FUSE_ROOT_ID; @@ -284,11 +328,13 @@ const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, + .d_canonical_path = fuse_dentry_canonical_path, }; const struct dentry_operations fuse_root_dentry_operations = { .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, + .d_canonical_path = fuse_dentry_canonical_path, }; int fuse_valid_type(int m) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index f78e9614bb5f..a110175e0075 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -371,6 +371,9 @@ struct fuse_req { /** Inode used in the request or NULL */ struct inode *inode; + /** Path used for completing d_canonical_path */ + struct path *canonical_path; + /** AIO control block */ struct fuse_io_priv *io; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 92fa24c24c92..fbb318d7dc89 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -381,6 +381,7 @@ enum fuse_opcode { FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, + FUSE_CANONICAL_PATH= 2016, /* CUSE specific operations */ CUSE_INIT = 4096, From 0d7ae9ff1796bc47139b4c7d936123fc97171591 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 15:58:22 -0700 Subject: [PATCH 0526/1276] ANDROID: vfs: Allow filesystems to access their private mount data Now we pass the vfsmount when mounting and remounting. This allows the filesystem to actually set up the mount specific data, although we can't quite do anything with it yet. show_options is expanded to include data that lives with the mount. To avoid changing existing filesystems, these have been added as new vfs functions. Change-Id: If80670bfad9f287abb8ac22457e1b034c9697097 Signed-off-by: Daniel Rosenberg --- fs/internal.h | 4 +++- fs/namespace.c | 4 ++-- fs/proc_namespace.c | 8 ++++++-- fs/super.c | 28 +++++++++++++++++++++++----- include/linux/fs.h | 4 ++++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index d410186bc369..a1ac9427243f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -100,9 +100,11 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); * super.c */ extern int do_remount_sb(struct super_block *, int, void *, int); +extern int do_remount_sb2(struct vfsmount *, struct super_block *, int, + void *, int); extern bool trylock_super(struct super_block *sb); extern struct dentry *mount_fs(struct file_system_type *, - int, const char *, void *); + int, const char *, struct vfsmount *, void *); extern struct super_block *user_get_super(dev_t); /* diff --git a/fs/namespace.c b/fs/namespace.c index cd4fb0e7e241..b4976060aed4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -968,7 +968,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (flags & SB_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; - root = mount_fs(type, flags, name, data); + root = mount_fs(type, flags, name, &mnt->mnt, data); if (IS_ERR(root)) { mnt_free_id(mnt); free_vfsmnt(mnt); @@ -2293,7 +2293,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) err = -EPERM; else { - err = do_remount_sb(sb, sb_flags, data, 0); + err = do_remount_sb2(path->mnt, sb, sb_flags, data, 0); namespace_lock(); lock_mount_hash(); propagate_remount(mnt); diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index e16fb8f2049e..bd07f0f4f06b 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -121,7 +121,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) if (err) goto out; show_mnt_opts(m, mnt); - if (sb->s_op->show_options) + if (sb->s_op->show_options2) + err = sb->s_op->show_options2(mnt, m, mnt_path.dentry); + else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt_path.dentry); seq_puts(m, " 0 0\n"); out: @@ -183,7 +185,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) err = show_sb_opts(m, sb); if (err) goto out; - if (sb->s_op->show_options) + if (sb->s_op->show_options2) { + err = sb->s_op->show_options2(mnt, m, mnt->mnt_root); + } else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt->mnt_root); seq_putc(m, '\n'); out: diff --git a/fs/super.c b/fs/super.c index f3a8c008e164..b24987b45dad 100644 --- a/fs/super.c +++ b/fs/super.c @@ -834,7 +834,8 @@ struct super_block *user_get_super(dev_t dev) } /** - * do_remount_sb - asks filesystem to change mount options. + * do_remount_sb2 - asks filesystem to change mount options. + * @mnt: mount we are looking at * @sb: superblock in question * @sb_flags: revised superblock flags * @data: the rest of options @@ -842,7 +843,7 @@ struct super_block *user_get_super(dev_t dev) * * Alters the mount options of a mounted file system. */ -int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) +int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int sb_flags, void *data, int force) { int retval; int remount_ro; @@ -884,7 +885,16 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) } } - if (sb->s_op->remount_fs) { + if (mnt && sb->s_op->remount_fs2) { + retval = sb->s_op->remount_fs2(mnt, sb, &sb_flags, data); + if (retval) { + if (!force) + goto cancel_readonly; + /* If forced remount, go ahead despite any errors */ + WARN(1, "forced remount of a %s fs returned %i\n", + sb->s_type->name, retval); + } + } else if (sb->s_op->remount_fs) { retval = sb->s_op->remount_fs(sb, &sb_flags, data); if (retval) { if (!force) @@ -916,6 +926,11 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) return retval; } +int do_remount_sb(struct super_block *sb, int flags, void *data, int force) +{ + return do_remount_sb2(NULL, sb, flags, data, force); +} + static void do_emergency_remount_callback(struct super_block *sb) { down_write(&sb->s_umount); @@ -1241,7 +1256,7 @@ struct dentry *mount_single(struct file_system_type *fs_type, EXPORT_SYMBOL(mount_single); struct dentry * -mount_fs(struct file_system_type *type, int flags, const char *name, void *data) +mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data) { struct dentry *root; struct super_block *sb; @@ -1258,7 +1273,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) goto out_free_secdata; } - root = type->mount(type, flags, name, data); + if (type->mount2) + root = type->mount2(mnt, type, flags, name, data); + else + root = type->mount(type, flags, name, data); if (IS_ERR(root)) { error = PTR_ERR(root); goto out_free_secdata; diff --git a/include/linux/fs.h b/include/linux/fs.h index e30ab9dcaab4..e013cfb206d8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1858,11 +1858,13 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); + int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *); void *(*clone_mnt_data) (void *); void (*copy_mnt_data) (void *, void *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); + int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *); int (*show_devname)(struct seq_file *, struct dentry *); int (*show_path)(struct seq_file *, struct dentry *); int (*show_stats)(struct seq_file *, struct dentry *); @@ -2122,6 +2124,8 @@ struct file_system_type { #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); + struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int, + const char *, void *); void *(*alloc_mnt_data) (void); void (*kill_sb) (struct super_block *); struct module *owner; From 9e3954870c7ba66765e3bab001acb83ee32106cc Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:33:11 -0700 Subject: [PATCH 0527/1276] ANDROID: vfs: Add setattr2 for filesystems with per mount permissions This allows filesystems to use their mount private data to influence the permssions they use in setattr2. It has been separated into a new call to avoid disrupting current setattr users. Change-Id: I19959038309284448f1b7f232d579674ef546385 Signed-off-by: Daniel Rosenberg --- fs/attr.c | 12 ++++++++++-- fs/coredump.c | 2 +- fs/inode.c | 6 +++--- fs/namei.c | 2 +- fs/open.c | 21 ++++++++++++++------- fs/utimes.c | 2 +- include/linux/fs.h | 6 +++++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index d22e8187477f..e55ed6e817d3 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -223,7 +223,7 @@ EXPORT_SYMBOL(setattr_copy); * the file open for write, as there can be no conflicting delegation in * that case. */ -int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) { struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; @@ -330,7 +330,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de if (error) return error; - if (inode->i_op->setattr) + if (mnt && inode->i_op->setattr2) + error = inode->i_op->setattr2(mnt, dentry, attr); + else if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else error = simple_setattr(dentry, attr); @@ -343,4 +345,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de return error; } +EXPORT_SYMBOL(notify_change2); + +int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +{ + return notify_change2(NULL, dentry, attr, delegated_inode); +} EXPORT_SYMBOL(notify_change); diff --git a/fs/coredump.c b/fs/coredump.c index 1e2c87acac9b..d9b3ba086d92 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -742,7 +742,7 @@ void do_coredump(const siginfo_t *siginfo) goto close_fail; if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) goto close_fail; - if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file)) + if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file)) goto close_fail; } diff --git a/fs/inode.c b/fs/inode.c index 42f6d25f32a5..790552ed0c62 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1794,7 +1794,7 @@ int dentry_needs_remove_privs(struct dentry *dentry) return mask; } -static int __remove_privs(struct dentry *dentry, int kill) +static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill) { struct iattr newattrs; @@ -1803,7 +1803,7 @@ static int __remove_privs(struct dentry *dentry, int kill) * Note we call this on write, so notify_change will not * encounter any conflicting delegations: */ - return notify_change(dentry, &newattrs, NULL); + return notify_change2(mnt, dentry, &newattrs, NULL); } /* @@ -1825,7 +1825,7 @@ int file_remove_privs(struct file *file) if (kill < 0) return kill; if (kill) - error = __remove_privs(dentry, kill); + error = __remove_privs(file->f_path.mnt, dentry, kill); if (!error) inode_has_no_xattr(inode); diff --git a/fs/namei.c b/fs/namei.c index 0cab6494978c..be445b3c28a0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3005,7 +3005,7 @@ static int handle_truncate(struct file *filp) if (!error) error = security_path_truncate(path); if (!error) { - error = do_truncate(path->dentry, 0, + error = do_truncate2(path->mnt, path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, filp); } diff --git a/fs/open.c b/fs/open.c index 0285ce7dbd51..a642ec16944b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -34,8 +34,8 @@ #include "internal.h" -int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, - struct file *filp) +int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length, + unsigned int time_attrs, struct file *filp) { int ret; struct iattr newattrs; @@ -60,10 +60,15 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, inode_lock(dentry->d_inode); /* Note any delegations or leases have already been broken: */ - ret = notify_change(dentry, &newattrs, NULL); + ret = notify_change2(mnt, dentry, &newattrs, NULL); inode_unlock(dentry->d_inode); return ret; } +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) +{ + return do_truncate2(NULL, dentry, length, time_attrs, filp); +} long vfs_truncate(const struct path *path, loff_t length) { @@ -106,7 +111,7 @@ long vfs_truncate(const struct path *path, loff_t length) if (!error) error = security_path_truncate(path); if (!error) - error = do_truncate(path->dentry, length, 0, NULL); + error = do_truncate2(mnt, path->dentry, length, 0, NULL); put_write_and_out: put_write_access(inode); @@ -155,6 +160,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode *inode; struct dentry *dentry; + struct vfsmount *mnt; struct fd f; int error; @@ -171,6 +177,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) small = 0; dentry = f.file->f_path.dentry; + mnt = f.file->f_path.mnt; inode = dentry->d_inode; error = -EINVAL; if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE)) @@ -191,7 +198,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (!error) error = security_path_truncate(&f.file->f_path); if (!error) - error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); + error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); sb_end_write(inode->i_sb); out_putf: fdput(f); @@ -538,7 +545,7 @@ static int chmod_common(const struct path *path, umode_t mode) goto out_unlock; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); out_unlock: inode_unlock(inode); if (delegated_inode) { @@ -629,7 +636,7 @@ static int chown_common(const struct path *path, uid_t user, gid_t group) inode_lock(inode); error = security_path_chown(path, uid, gid); if (!error) - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); inode_unlock(inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/fs/utimes.c b/fs/utimes.c index 69d4b6ba1bfb..1039ef7378a5 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -88,7 +88,7 @@ static int utimes_common(const struct path *path, struct timespec64 *times) } retry_deleg: inode_lock(inode); - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); inode_unlock(inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index e013cfb206d8..3ccb51f23689 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1784,7 +1784,8 @@ struct inode_operations { int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*setattr) (struct dentry *, struct iattr *); - int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); + int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *); + int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); @@ -2443,6 +2444,8 @@ struct filename { extern long vfs_truncate(const struct path *, loff_t); extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, struct file *filp); +extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start, + unsigned int time_attrs, struct file *filp); extern int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len); extern long do_sys_open(int dfd, const char __user *filename, int flags, @@ -2752,6 +2755,7 @@ extern void emergency_remount(void); extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct iattr *, struct inode **); +extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **); extern int inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int); extern int __check_sticky(struct inode *dir, struct inode *inode); From 82b2f3accea2a56aa86abcf10d58ca9feab8b65d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:27:45 -0700 Subject: [PATCH 0528/1276] ANDROID: vfs: Add permission2 for filesystems with per mount permissions This allows filesystems to use their mount private data to influence the permssions they return in permission2. It has been separated into a new call to avoid disrupting current permission users. Change-Id: I9d416e3b8b6eca84ef3e336bd2af89ddd51df6ca Signed-off-by: Daniel Rosenberg [AmitP: Minor refactoring of original patch to align with changes from the following upstream commit 4bfd054ae11e ("fs: fold __inode_permission() into inode_permission()"). Also introduce vfs_mkobj2(), because do_create() moved from using vfs_create() to vfs_mkobj() eecec19d9e70 ("mqueue: switch to vfs_mkobj(), quit abusing ->d_fsdata") do_create() is dropped/cleaned-up upstream so a minor refactoring there as well. 066cc813e94a ("do_mq_open(): move all work prior to dentry_open() into a helper")] Signed-off-by: Amit Pundir --- fs/attr.c | 2 +- fs/exec.c | 2 +- fs/namei.c | 185 ++++++++++++++++++++--------- fs/notify/fanotify/fanotify_user.c | 2 +- fs/notify/inotify/inotify_user.c | 2 +- fs/open.c | 15 ++- include/linux/fs.h | 13 ++ include/linux/namei.h | 1 + ipc/mqueue.c | 14 +-- security/inode.c | 2 +- 10 files changed, 167 insertions(+), 71 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index e55ed6e817d3..bea2f7cfd52c 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -247,7 +247,7 @@ int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * return -EPERM; if (!inode_owner_or_capable(inode)) { - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) return error; } diff --git a/fs/exec.c b/fs/exec.c index 1ebf6e5a521d..c7e3417a10ae 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1308,7 +1308,7 @@ EXPORT_SYMBOL(flush_old_exec); void would_dump(struct linux_binprm *bprm, struct file *file) { struct inode *inode = file_inode(file); - if (inode_permission(inode, MAY_READ) < 0) { + if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) { struct user_namespace *old, *user_ns; bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; diff --git a/fs/namei.c b/fs/namei.c index be445b3c28a0..b463413ec56e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -379,9 +379,11 @@ EXPORT_SYMBOL(generic_permission); * flag in inode->i_opflags, that says "this has not special * permission function, use the fast case". */ -static inline int do_inode_permission(struct inode *inode, int mask) +static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask) { if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) { + if (likely(mnt && inode->i_op->permission2)) + return inode->i_op->permission2(mnt, inode, mask); if (likely(inode->i_op->permission)) return inode->i_op->permission(inode, mask); @@ -414,7 +416,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) } /** - * inode_permission - Check for access rights to a given inode + * inode_permission2 - Check for access rights to a given inode + * @mnt: * @inode: Inode to check permission on * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * @@ -424,7 +427,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) * * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. */ -int inode_permission(struct inode *inode, int mask) +int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask) { int retval; @@ -448,7 +451,7 @@ int inode_permission(struct inode *inode, int mask) return -EACCES; } - retval = do_inode_permission(inode, mask); + retval = do_inode_permission(mnt, inode, mask); if (retval) return retval; @@ -456,7 +459,14 @@ int inode_permission(struct inode *inode, int mask) if (retval) return retval; - return security_inode_permission(inode, mask); + retval = security_inode_permission(inode, mask); + return retval; +} +EXPORT_SYMBOL(inode_permission2); + +int inode_permission(struct inode *inode, int mask) +{ + return inode_permission2(NULL, inode, mask); } EXPORT_SYMBOL(inode_permission); @@ -1693,13 +1703,13 @@ static struct dentry *lookup_slow(const struct qstr *name, static inline int may_lookup(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { - int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); + int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK); if (err != -ECHILD) return err; if (unlazy_walk(nd)) return -ECHILD; } - return inode_permission(nd->inode, MAY_EXEC); + return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC); } static inline int handle_dots(struct nameidata *nd, int type) @@ -2455,8 +2465,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, - int len, struct qstr *this) +static int lookup_one_len_common(const char *name, struct vfsmount *mnt, + struct dentry *base, int len, struct qstr *this) { this->name = name; this->len = len; @@ -2484,7 +2494,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(base->d_inode, MAY_EXEC); + return inode_permission2(mnt, base->d_inode, MAY_EXEC); } /** @@ -2508,7 +2518,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, NULL, base, len, &this); if (err) return ERR_PTR(err); @@ -2527,7 +2537,7 @@ EXPORT_SYMBOL(try_lookup_one_len); * * The caller must hold base->i_mutex. */ -struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len) { struct dentry *dentry; struct qstr this; @@ -2535,13 +2545,19 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, mnt, base, len, &this); if (err) return ERR_PTR(err); dentry = lookup_dcache(&this, base, 0); return dentry ? dentry : __lookup_slow(&this, base, 0); } +EXPORT_SYMBOL(lookup_one_len2); + +struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +{ + return lookup_one_len2(name, NULL, base, len); +} EXPORT_SYMBOL(lookup_one_len); /** @@ -2563,7 +2579,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, NULL, base, len, &this); if (err) return ERR_PTR(err); @@ -2787,7 +2803,7 @@ EXPORT_SYMBOL(__check_sticky); * 11. We don't allow removal of NFS sillyrenamed files; it's handled by * nfs_async_unlink(). */ -static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) +static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir) { struct inode *inode = d_backing_inode(victim); int error; @@ -2804,7 +2820,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) @@ -2836,7 +2852,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) * 4. We should have write and exec permissions on dir * 5. We can't do it if dir is immutable (done in permission()) */ -static inline int may_create(struct inode *dir, struct dentry *child) +static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child) { struct user_namespace *s_user_ns; audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); @@ -2848,7 +2864,7 @@ static inline int may_create(struct inode *dir, struct dentry *child) if (!kuid_has_mapping(s_user_ns, current_fsuid()) || !kgid_has_mapping(s_user_ns, current_fsgid())) return -EOVERFLOW; - return inode_permission(dir, MAY_WRITE | MAY_EXEC); + return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); } /* @@ -2895,10 +2911,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) } EXPORT_SYMBOL(unlock_rename); -int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool want_excl) +int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, + umode_t mode, bool want_excl) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -2914,14 +2930,21 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_create2); + +int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl) +{ + return vfs_create2(NULL, dir, dentry, mode, want_excl); +} EXPORT_SYMBOL(vfs_create); -int vfs_mkobj(struct dentry *dentry, umode_t mode, +int vfs_mkobj2(struct vfsmount *mnt, struct dentry *dentry, umode_t mode, int (*f)(struct dentry *, umode_t, void *), void *arg) { struct inode *dir = dentry->d_parent->d_inode; - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -2935,6 +2958,15 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode, fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mkobj2); + + +int vfs_mkobj(struct dentry *dentry, umode_t mode, + int (*f)(struct dentry *, umode_t, void *), + void *arg) +{ + return vfs_mkobj2(NULL, dentry, mode, f, arg); +} EXPORT_SYMBOL(vfs_mkobj); bool may_open_dev(const struct path *path) @@ -2946,6 +2978,7 @@ bool may_open_dev(const struct path *path) static int may_open(const struct path *path, int acc_mode, int flag) { struct dentry *dentry = path->dentry; + struct vfsmount *mnt = path->mnt; struct inode *inode = dentry->d_inode; int error; @@ -2970,7 +3003,7 @@ static int may_open(const struct path *path, int acc_mode, int flag) break; } - error = inode_permission(inode, MAY_OPEN | acc_mode); + error = inode_permission2(mnt, inode, MAY_OPEN | acc_mode); if (error) return error; @@ -3032,7 +3065,7 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m !kgid_has_mapping(s_user_ns, current_fsgid())) return -EOVERFLOW; - error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); + error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); if (error) return error; @@ -3440,7 +3473,8 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag) int error; /* we want directory to be writable */ - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(ERR_PTR(-EOPNOTSUPP), dir, + MAY_WRITE | MAY_EXEC); if (error) goto out_err; error = -EOPNOTSUPP; @@ -3694,9 +3728,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname, } EXPORT_SYMBOL(user_path_create); -int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -3721,6 +3755,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mknod2); + +int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +{ + return vfs_mknod2(NULL, dir, dentry, mode, dev); +} EXPORT_SYMBOL(vfs_mknod); static int may_mknod(umode_t mode) @@ -3763,12 +3803,12 @@ long do_mknodat(int dfd, const char __user *filename, umode_t mode, goto out; switch (mode & S_IFMT) { case 0: case S_IFREG: - error = vfs_create(path.dentry->d_inode,dentry,mode,true); + error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true); if (!error) ima_post_path_mknod(dentry); break; case S_IFCHR: case S_IFBLK: - error = vfs_mknod(path.dentry->d_inode,dentry,mode, + error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode, new_decode_dev(dev)); break; case S_IFIFO: case S_IFSOCK: @@ -3795,9 +3835,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return do_mknodat(AT_FDCWD, filename, mode, dev); } -int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); unsigned max_links = dir->i_sb->s_max_links; if (error) @@ -3819,6 +3859,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) fsnotify_mkdir(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mkdir2); + +int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + return vfs_mkdir2(NULL, dir, dentry, mode); +} EXPORT_SYMBOL(vfs_mkdir); long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) @@ -3837,7 +3883,7 @@ long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) mode &= ~current_umask(); error = security_path_mkdir(&path, dentry, mode); if (!error) - error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -3856,9 +3902,9 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) return do_mkdirat(AT_FDCWD, pathname, mode); } -int vfs_rmdir(struct inode *dir, struct dentry *dentry) +int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry) { - int error = may_delete(dir, dentry, 1); + int error = may_delete(mnt, dir, dentry, 1); if (error) return error; @@ -3893,6 +3939,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) d_delete(dentry); return error; } +int vfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + return vfs_rmdir2(NULL, dir, dentry); +} EXPORT_SYMBOL(vfs_rmdir); long do_rmdir(int dfd, const char __user *pathname) @@ -3938,7 +3988,7 @@ long do_rmdir(int dfd, const char __user *pathname) error = security_path_rmdir(&path, dentry); if (error) goto exit3; - error = vfs_rmdir(path.dentry->d_inode, dentry); + error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry); exit3: dput(dentry); exit2: @@ -3977,10 +4027,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) { struct inode *target = dentry->d_inode; - int error = may_delete(dir, dentry, 0); + int error = may_delete(mnt, dir, dentry, 0); if (error) return error; @@ -4015,6 +4065,12 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate return error; } +EXPORT_SYMBOL(vfs_unlink2); + +int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +{ + return vfs_unlink2(NULL, dir, dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_unlink); /* @@ -4060,7 +4116,7 @@ long do_unlinkat(int dfd, struct filename *name) error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode); exit2: dput(dentry); } @@ -4110,9 +4166,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) return do_unlinkat(AT_FDCWD, getname(pathname)); } -int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -4129,6 +4185,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_symlink2); + +int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +{ + return vfs_symlink2(NULL, dir, dentry, oldname); +} EXPORT_SYMBOL(vfs_symlink); long do_symlinkat(const char __user *oldname, int newdfd, @@ -4151,7 +4213,7 @@ long do_symlinkat(const char __user *oldname, int newdfd, error = security_path_symlink(&path, dentry, from->name); if (!error) - error = vfs_symlink(path.dentry->d_inode, dentry, from->name); + error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4192,7 +4254,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) { struct inode *inode = old_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; @@ -4201,7 +4263,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + error = may_create(mnt, dir, new_dentry); if (error) return error; @@ -4251,6 +4313,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de fsnotify_link(dir, inode, new_dentry); return error; } +EXPORT_SYMBOL(vfs_link2); + +int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +{ + return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_link); /* @@ -4306,7 +4374,7 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); + error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); out_dput: done_path_create(&new_path, new_dentry); if (delegated_inode) { @@ -4388,7 +4456,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname * ->i_mutex on parents, which works but leads to some truly excessive * locking]. */ -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +int vfs_rename2(struct vfsmount *mnt, + struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct inode **delegated_inode, unsigned int flags) { @@ -4403,19 +4472,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (source == target) return 0; - error = may_delete(old_dir, old_dentry, is_dir); + error = may_delete(mnt, old_dir, old_dentry, is_dir); if (error) return error; if (!target) { - error = may_create(new_dir, new_dentry); + error = may_create(mnt, new_dir, new_dentry); } else { new_is_dir = d_is_dir(new_dentry); if (!(flags & RENAME_EXCHANGE)) - error = may_delete(new_dir, new_dentry, is_dir); + error = may_delete(mnt, new_dir, new_dentry, is_dir); else - error = may_delete(new_dir, new_dentry, new_is_dir); + error = may_delete(mnt, new_dir, new_dentry, new_is_dir); } if (error) return error; @@ -4429,12 +4498,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (new_dir != old_dir) { if (is_dir) { - error = inode_permission(source, MAY_WRITE); + error = inode_permission2(mnt, source, MAY_WRITE); if (error) return error; } if ((flags & RENAME_EXCHANGE) && new_is_dir) { - error = inode_permission(target, MAY_WRITE); + error = inode_permission2(mnt, target, MAY_WRITE); if (error) return error; } @@ -4511,6 +4580,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, return error; } +EXPORT_SYMBOL(vfs_rename2); + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + struct inode **delegated_inode, unsigned int flags) +{ + return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags); +} EXPORT_SYMBOL(vfs_rename); static int do_renameat2(int olddfd, const char __user *oldname, int newdfd, @@ -4624,7 +4701,7 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd, &new_path, new_dentry, flags); if (error) goto exit5; - error = vfs_rename(old_path.dentry->d_inode, old_dentry, + error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode, flags); exit5: @@ -4675,7 +4752,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna int vfs_whiteout(struct inode *dir, struct dentry *dentry) { - int error = may_create(dir, dentry); + int error = may_create(NULL, dir, dentry); if (error) return error; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 69054886915b..06c57294f231 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -490,7 +490,7 @@ static int fanotify_find_path(int dfd, const char __user *filename, } /* you can only watch an inode if you have read permissions on it */ - ret = inode_permission(path->dentry->d_inode, MAY_READ); + ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (ret) path_put(path); out: diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 15f5655df87d..f9a95fb1008f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -350,7 +350,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns if (error) return error; /* you can only watch an inode if you have read permissions on it */ - error = inode_permission(path->dentry->d_inode, MAY_READ); + error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (error) path_put(path); return error; diff --git a/fs/open.c b/fs/open.c index a642ec16944b..f862e1c9141c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -73,9 +73,11 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, long vfs_truncate(const struct path *path, loff_t length) { struct inode *inode; + struct vfsmount *mnt; long error; inode = path->dentry->d_inode; + mnt = path->mnt; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ if (S_ISDIR(inode->i_mode)) @@ -87,7 +89,7 @@ long vfs_truncate(const struct path *path, loff_t length) if (error) goto out; - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; @@ -357,6 +359,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) struct cred *override_cred; struct path path; struct inode *inode; + struct vfsmount *mnt; int res; unsigned int lookup_flags = LOOKUP_FOLLOW; @@ -387,6 +390,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) goto out; inode = d_backing_inode(path.dentry); + mnt = path.mnt; if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -398,7 +402,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) goto out_path_release; } - res = inode_permission(inode, mode | MAY_ACCESS); + res = inode_permission2(mnt, inode, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; @@ -447,7 +451,7 @@ int ksys_chdir(const char __user *filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -481,7 +485,8 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) if (!d_can_lookup(f.file->f_path.dentry)) goto out_putf; - error = inode_permission(file_inode(f.file), MAY_EXEC | MAY_CHDIR); + error = inode_permission2(f.file->f_path.mnt, file_inode(f.file), + MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &f.file->f_path); out_putf: @@ -500,7 +505,7 @@ int ksys_chroot(const char __user *filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3ccb51f23689..b12dc3fcf0cd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1625,13 +1625,21 @@ extern bool inode_owner_or_capable(const struct inode *inode); * VFS helper functions.. */ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); +extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); +extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); +extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); +extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *); extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **); extern int vfs_rmdir(struct inode *, struct dentry *); +extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); +extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); +extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); extern int vfs_whiteout(struct inode *, struct dentry *); extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, @@ -1640,6 +1648,9 @@ extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int vfs_mkobj(struct dentry *, umode_t, int (*f)(struct dentry *, umode_t, void *), void *); +int vfs_mkobj2(struct vfsmount *, struct dentry *, umode_t, + int (*f)(struct dentry *, umode_t, void *), + void *); extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); @@ -1770,6 +1781,7 @@ struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); int (*permission) (struct inode *, int); + int (*permission2) (struct vfsmount *, struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); @@ -2757,6 +2769,7 @@ extern sector_t bmap(struct inode *, sector_t); extern int notify_change(struct dentry *, struct iattr *, struct inode **); extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **); extern int inode_permission(struct inode *, int); +extern int inode_permission2(struct vfsmount *, struct inode *, int); extern int generic_permission(struct inode *, int); extern int __check_sticky(struct inode *dir, struct inode *inode); diff --git a/include/linux/namei.h b/include/linux/namei.h index a78606e8e3df..788f3cbc12b7 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -83,6 +83,7 @@ extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern int follow_down_one(struct path *); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index c0d58f390c3b..37f7c7c0a1eb 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -714,7 +714,7 @@ static void remove_notification(struct mqueue_inode_info *info) info->notify_user_ns = NULL; } -static int prepare_open(struct dentry *dentry, int oflag, int ro, +static int prepare_open(struct vfsmount *mnt, struct dentry *dentry, int oflag, int ro, umode_t mode, struct filename *name, struct mq_attr *attr) { @@ -728,7 +728,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, if (ro) return ro; audit_inode_parent_hidden(name, dentry->d_parent); - return vfs_mkobj(dentry, mode & ~current_umask(), + return vfs_mkobj2(mnt, dentry, mode & ~current_umask(), mqueue_create_attr, attr); } /* it already existed */ @@ -738,7 +738,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return -EINVAL; acc = oflag2acc[oflag & O_ACCMODE]; - return inode_permission(d_inode(dentry), acc); + return inode_permission2(mnt, d_inode(dentry), acc); } static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, @@ -762,13 +762,13 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, ro = mnt_want_write(mnt); /* we'll drop it in any case */ inode_lock(d_inode(root)); - path.dentry = lookup_one_len(name->name, root, strlen(name->name)); + path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name)); if (IS_ERR(path.dentry)) { error = PTR_ERR(path.dentry); goto out_putfd; } path.mnt = mntget(mnt); - error = prepare_open(path.dentry, oflag, ro, mode, name, attr); + error = prepare_open(path.mnt, path.dentry, oflag, ro, mode, name, attr); if (!error) { struct file *file = dentry_open(&path, oflag, current_cred()); if (!IS_ERR(file)) @@ -818,7 +818,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) if (err) goto out_name; inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT); - dentry = lookup_one_len(name->name, mnt->mnt_root, + dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root, strlen(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); @@ -830,7 +830,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) err = -ENOENT; } else { ihold(inode); - err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL); + err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL); } dput(dentry); diff --git a/security/inode.c b/security/inode.c index 8dd9ca8848e4..bf2810936dfb 100644 --- a/security/inode.c +++ b/security/inode.c @@ -122,7 +122,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, dir = d_inode(parent); inode_lock(dir); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len2(name, mount, parent, strlen(name)); if (IS_ERR(dentry)) goto out; From 9992eb8b9a1e4a367fedcf54901e35d7e2ca469f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 14:34:38 -0800 Subject: [PATCH 0529/1276] ANDROID: xattr: Pass EOPNOTSUPP to permission2 The permission call for xattr operations happens regardless of whether or not the xattr functions are implemented. The xattr functions currently don't have support for permission2. Passing EOPNOTSUPP as the mount point in xattr_permission allows us to return EOPNOTSUPP early in permission2, if the filesystem supports it. Change-Id: I9d07e4cd633cf40af60450ffbff7ac5c1b4e8c2c Signed-off-by: Daniel Rosenberg Bug: 35848445 --- fs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xattr.c b/fs/xattr.c index 0d6a6a4af861..2b10376568f1 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -130,7 +130,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) return -EPERM; } - return inode_permission(inode, mask); + return inode_permission2(ERR_PTR(-EOPNOTSUPP), inode, mask); } int From 612a725e3d975e653fa0a6c0b203959ea8efd531 Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:27:37 -0700 Subject: [PATCH 0530/1276] ANDROID: Port of sdcardfs to 4.4 Change-Id: I25b99ecf214e72ebf6a57ec3085972542a8d7951 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/Kconfig | 1 - fs/sdcardfs/dentry.c | 9 +- fs/sdcardfs/file.c | 47 ++++--- fs/sdcardfs/hashtable.h | 190 -------------------------- fs/sdcardfs/inode.c | 280 +++++++++++++++----------------------- fs/sdcardfs/lookup.c | 25 ++-- fs/sdcardfs/main.c | 113 ++++++++------- fs/sdcardfs/mmap.c | 5 +- fs/sdcardfs/packagelist.c | 39 +++--- fs/sdcardfs/sdcardfs.h | 41 +++--- fs/sdcardfs/super.c | 6 +- include/linux/namei.h | 2 + 12 files changed, 252 insertions(+), 506 deletions(-) delete mode 100644 fs/sdcardfs/hashtable.h diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index 657f4958e8d6..d995f3eaae6d 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -1,6 +1,5 @@ config SDCARD_FS tristate "sdcard file system" - depends on EXPERIMENTAL default n help Sdcardfs is based on Wrapfs file system. diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 4572a5403bb2..dbbcfd091fc7 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -26,7 +26,7 @@ * 0: tell VFS to invalidate dentry * 1: dentry is valid */ -static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) { int err = 1; struct path parent_lower_path, lower_path; @@ -35,7 +35,7 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) struct dentry *lower_cur_parent_dentry = NULL; struct dentry *lower_dentry = NULL; - if (nd && nd->flags & LOOKUP_RCU) + if (flags & LOOKUP_RCU) return -ECHILD; spin_lock(&dentry->d_lock); @@ -119,7 +119,7 @@ static void sdcardfs_d_release(struct dentry *dentry) } static int sdcardfs_hash_ci(const struct dentry *dentry, - const struct inode *inode, struct qstr *qstr) + struct qstr *qstr) { /* * This function is copy of vfat_hashi. @@ -148,8 +148,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, * Case insensitive compare of two vfat names. */ static int sdcardfs_cmp_ci(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { /* This function is copy of vfat_cmpi */ diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index bcacb947c874..f9c5eaafc619 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -50,8 +50,8 @@ static ssize_t sdcardfs_read(struct file *file, char __user *buf, err = vfs_read(lower_file, buf, count, ppos); /* update our inode atime upon a successful lower read */ if (err >= 0) - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), + file_inode(lower_file)); return err; } @@ -59,7 +59,7 @@ static ssize_t sdcardfs_read(struct file *file, char __user *buf, static ssize_t sdcardfs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - int err = 0; + int err; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; @@ -73,29 +73,29 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, err = vfs_write(lower_file, buf, count, ppos); /* update our inode times+sizes upon a successful lower write */ if (err >= 0) { - fsstack_copy_inode_size(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - fsstack_copy_attr_times(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_inode_size(d_inode(dentry), + file_inode(lower_file)); + fsstack_copy_attr_times(d_inode(dentry), + file_inode(lower_file)); } return err; } -static int sdcardfs_readdir(struct file *file, void *dirent, filldir_t filldir) +static int sdcardfs_readdir(struct file *file, struct dir_context *ctx) { - int err = 0; + int err; struct file *lower_file = NULL; struct dentry *dentry = file->f_path.dentry; lower_file = sdcardfs_lower_file(file); lower_file->f_pos = file->f_pos; - err = vfs_readdir(lower_file, filldir, dirent); + err = iterate_dir(lower_file, ctx); file->f_pos = lower_file->f_pos; if (err >= 0) /* copy the atime */ - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), + file_inode(lower_file)); return err; } @@ -191,7 +191,6 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) */ file_accessed(file); vma->vm_ops = &sdcardfs_vm_ops; - vma->vm_flags |= VM_CAN_NONLINEAR; file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ @@ -242,8 +241,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) /* open lower object and link sdcardfs's file struct to lower's */ sdcardfs_get_lower_path(file->f_path.dentry, &lower_path); - lower_file = dentry_open(lower_path.dentry, lower_path.mnt, - file->f_flags, current_cred()); + lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); + path_put(&lower_path); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); lower_file = sdcardfs_lower_file(file); @@ -275,8 +274,10 @@ static int sdcardfs_flush(struct file *file, fl_owner_t id) struct file *lower_file = NULL; lower_file = sdcardfs_lower_file(file); - if (lower_file && lower_file->f_op && lower_file->f_op->flush) + if (lower_file && lower_file->f_op && lower_file->f_op->flush) { + filemap_write_and_wait(file->f_mapping); err = lower_file->f_op->flush(lower_file, id); + } return err; } @@ -296,19 +297,23 @@ static int sdcardfs_file_release(struct inode *inode, struct file *file) return 0; } -static int -sdcardfs_fsync(struct file *file, int datasync) +static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end, + int datasync) { int err; struct file *lower_file; struct path lower_path; struct dentry *dentry = file->f_path.dentry; + err = __generic_file_fsync(file, start, end, datasync); + if (err) + goto out; + lower_file = sdcardfs_lower_file(file); sdcardfs_get_lower_path(dentry, &lower_path); - err = vfs_fsync(lower_file, datasync); + err = vfs_fsync_range(lower_file, start, end, datasync); sdcardfs_put_lower_path(dentry, &lower_path); - +out: return err; } @@ -344,7 +349,7 @@ const struct file_operations sdcardfs_main_fops = { const struct file_operations sdcardfs_dir_fops = { .llseek = generic_file_llseek, .read = generic_read_dir, - .readdir = sdcardfs_readdir, + .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sdcardfs_compat_ioctl, diff --git a/fs/sdcardfs/hashtable.h b/fs/sdcardfs/hashtable.h deleted file mode 100644 index 1e770f3df148..000000000000 --- a/fs/sdcardfs/hashtable.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Statically sized hash table implementation - * (C) 2012 Sasha Levin - */ - -#ifndef _LINUX_HASHTABLE_H -#define _LINUX_HASHTABLE_H - -#include -#include -#include -#include -#include - -#define DEFINE_HASHTABLE(name, bits) \ - struct hlist_head name[1 << (bits)] = \ - { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } - -#define DECLARE_HASHTABLE(name, bits) \ - struct hlist_head name[1 << (bits)] - -#define HASH_SIZE(name) (ARRAY_SIZE(name)) -#define HASH_BITS(name) ilog2(HASH_SIZE(name)) - -/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ -#define hash_min(val, bits) \ - (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) - -static inline void __hash_init(struct hlist_head *ht, unsigned int sz) -{ - unsigned int i; - - for (i = 0; i < sz; i++) - INIT_HLIST_HEAD(&ht[i]); -} - -/** - * hash_init - initialize a hash table - * @hashtable: hashtable to be initialized - * - * Calculates the size of the hashtable from the given parameter, otherwise - * same as hash_init_size. - * - * This has to be a macro since HASH_BITS() will not work on pointers since - * it calculates the size during preprocessing. - */ -#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) - -/** - * hash_add - add an object to a hashtable - * @hashtable: hashtable to add to - * @node: the &struct hlist_node of the object to be added - * @key: the key of the object to be added - */ -#define hash_add(hashtable, node, key) \ - hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) - -/** - * hash_add_rcu - add an object to a rcu enabled hashtable - * @hashtable: hashtable to add to - * @node: the &struct hlist_node of the object to be added - * @key: the key of the object to be added - */ -#define hash_add_rcu(hashtable, node, key) \ - hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) - -/** - * hash_hashed - check whether an object is in any hashtable - * @node: the &struct hlist_node of the object to be checked - */ -static inline bool hash_hashed(struct hlist_node *node) -{ - return !hlist_unhashed(node); -} - -static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) -{ - unsigned int i; - - for (i = 0; i < sz; i++) - if (!hlist_empty(&ht[i])) - return false; - - return true; -} - -/** - * hash_empty - check whether a hashtable is empty - * @hashtable: hashtable to check - * - * This has to be a macro since HASH_BITS() will not work on pointers since - * it calculates the size during preprocessing. - */ -#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) - -/** - * hash_del - remove an object from a hashtable - * @node: &struct hlist_node of the object to remove - */ -static inline void hash_del(struct hlist_node *node) -{ - hlist_del_init(node); -} - -/** - * hash_del_rcu - remove an object from a rcu enabled hashtable - * @node: &struct hlist_node of the object to remove - */ -static inline void hash_del_rcu(struct hlist_node *node) -{ - hlist_del_init_rcu(node); -} - -/** - * hash_for_each - iterate over a hashtable - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each(name, bkt, obj, member, pos) \ - for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry(obj, pos, &name[bkt], member) - -/** - * hash_for_each_rcu - iterate over a rcu enabled hashtable - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each_rcu(name, bkt, obj, member) \ - for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry_rcu(obj, &name[bkt], member) - -/** - * hash_for_each_safe - iterate over a hashtable safe against removal of - * hash entry - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @tmp: a &struct used for temporary storage - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each_safe(name, bkt, tmp, obj, member, pos) \ - for ((bkt) = 0, obj = NULL; (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry_safe(obj, pos, tmp, &name[bkt], member) - -/** - * hash_for_each_possible - iterate over all possible objects hashing to the - * same bucket - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible(name, obj, member, key, pos) \ - hlist_for_each_entry(obj, pos, &name[hash_min(key, HASH_BITS(name))], member) - -/** - * hash_for_each_possible_rcu - iterate over all possible objects hashing to the - * same bucket in an rcu enabled hashtable - * in a rcu enabled hashtable - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible_rcu(name, obj, member, key) \ - hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\ - member) - -/** - * hash_for_each_possible_safe - iterate over all possible objects hashing to the - * same bucket safe against removals - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @tmp: a &struct used for temporary storage - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ - hlist_for_each_entry_safe(obj, tmp,\ - &name[hash_min(key, HASH_BITS(name))], member) - - -#endif diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index e8ed04250ed1..75c622bac2f5 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -30,8 +30,8 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) if (!cred) return NULL; - cred->fsuid = sbi->options.fs_low_uid; - cred->fsgid = sbi->options.fs_low_gid; + cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); + cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); old_cred = override_creds(cred); @@ -49,12 +49,12 @@ void revert_fsids(const struct cred * old_cred) } static int sdcardfs_create(struct inode *dir, struct dentry *dentry, - int mode, struct nameidata *nd) + umode_t mode, bool want_excl) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; - struct path lower_path, saved_path; + struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; @@ -74,18 +74,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - - pathcpy(&saved_path, &nd->path); - pathcpy(&nd->path, &lower_path); - /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; - err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); - - pathcpy(&nd->path, &saved_path); + err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -93,11 +84,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -118,33 +107,27 @@ static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - file_size_save = i_size_read(old_dentry->d_inode); + file_size_save = i_size_read(d_inode(old_dentry)); sdcardfs_get_lower_path(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_dir_dentry = lock_parent(lower_new_dentry); - err = mnt_want_write(lower_new_path.mnt); - if (err) - goto out_unlock; - - err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); - if (err || !lower_new_dentry->d_inode) + err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), + lower_new_dentry, NULL); + if (err || !d_inode(lower_new_dentry)) goto out; err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); if (err) goto out; - fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); - old_dentry->d_inode->i_nlink = - sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink; - i_size_write(new_dentry->d_inode, file_size_save); + fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); + fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); + set_nlink(d_inode(old_dentry), + sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); + i_size_write(d_inode(new_dentry), file_size_save); out: - mnt_drop_write(lower_new_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); sdcardfs_put_lower_path(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); @@ -180,10 +163,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_unlink(lower_dir_inode, lower_dentry); + err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); /* * Note: unlinking on top of NFS can cause silly-renamed files. @@ -198,13 +178,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) goto out; fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_inode_size(dir, lower_dir_inode); - dentry->d_inode->i_nlink = - sdcardfs_lower_inode(dentry->d_inode)->i_nlink; - dentry->d_inode->i_ctime = dir->i_ctime; + set_nlink(d_inode(dentry), + sdcardfs_lower_inode(d_inode(dentry))->i_nlink); + d_inode(dentry)->i_ctime = dir->i_ctime; d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); @@ -217,7 +195,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; @@ -228,21 +206,16 @@ static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); + err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); if (err) goto out; err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(); @@ -266,9 +239,9 @@ static int touch(char *abs_path, mode_t mode) { return 0; } -static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { - int err = 0; + int err; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; @@ -306,13 +279,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; - err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); + err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); if (err) goto out; @@ -341,9 +310,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ - dir->i_nlink = sdcardfs_lower_inode(dir)->i_nlink; + set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) @@ -388,8 +357,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) kfree(nomedia_fullpath); } out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: @@ -427,23 +394,18 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) lower_dentry = lower_path.dentry; lower_dir_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); if (err) goto out; d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ - if (dentry->d_inode) - clear_nlink(dentry->d_inode); - fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); - dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + if (d_inode(dentry)) + clear_nlink(d_inode(dentry)); + fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); + fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry)); + set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -452,10 +414,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } #if 0 -static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, +static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; @@ -466,10 +428,7 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); if (err) goto out; @@ -477,11 +436,9 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(); @@ -541,43 +498,33 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - err = mnt_want_write(lower_old_path.mnt); + err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, + d_inode(lower_new_dir_dentry), lower_new_dentry, + NULL, 0); if (err) goto out; - err = mnt_want_write(lower_new_path.mnt); - if (err) - goto out_drop_old_write; - - err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); - if (err) - goto out_err; /* Copy attrs from lower dir, but i_uid/i_gid */ - fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); - fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_attr_all(new_dir, d_inode(lower_new_dir_dentry)); + fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry)); fix_derived_permission(new_dir); if (new_dir != old_dir) { - fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); - fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); + fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); fix_derived_permission(old_dir); /* update the derived permission of the old_dentry * with its new parent */ new_parent = dget_parent(new_dentry); if(new_parent) { - if(old_dentry->d_inode) { + if(d_inode(old_dentry)) { get_derived_permission(new_parent, old_dentry); - fix_derived_permission(old_dentry->d_inode); + fix_derived_permission(d_inode(old_dentry)); } dput(new_parent); } } -out_err: - mnt_drop_write(lower_new_path.mnt); -out_drop_old_write: - mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -599,17 +546,17 @@ static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; - if (!lower_dentry->d_inode->i_op || - !lower_dentry->d_inode->i_op->readlink) { + if (!d_inode(lower_dentry)->i_op || + !d_inode(lower_dentry)->i_op->readlink) { err = -EINVAL; goto out; } - err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + err = d_inode(lower_dentry)->i_op->readlink(lower_dentry, buf, bufsiz); if (err < 0) goto out; - fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), d_inode(lower_dentry)); out: sdcardfs_put_lower_path(dentry, &lower_path); @@ -618,7 +565,7 @@ static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz #endif #if 0 -static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) { char *buf; int len = PAGE_SIZE, err; @@ -628,7 +575,7 @@ static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) buf = kmalloc(len, GFP_KERNEL); if (!buf) { buf = ERR_PTR(-ENOMEM); - goto out; + return buf; } /* read the symlink, and then we will follow it */ @@ -642,35 +589,19 @@ static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) } else { buf[err] = '\0'; } -out: - nd_set_link(nd, buf); - return NULL; -} -#endif - -#if 0 -/* this @nd *IS* still used */ -static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *buf = nd_get_link(nd); - if (!IS_ERR(buf)) /* free the char* */ - kfree(buf); + return *cookie = buf; } #endif -static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags) +static int sdcardfs_permission(struct inode *inode, int mask) { int err; - if (flags & IPERM_FLAG_RCU) - return -ECHILD; - /* * Permission check on sdcardfs inode. * Calling process should have AID_SDCARD_RW permission */ - err = generic_permission(inode, mask, 0, inode->i_op->check_acl); + err = generic_permission(inode, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) @@ -700,49 +631,9 @@ static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags } -static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) -{ - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; - struct path lower_path; - struct dentry *parent; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); - - parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, 0, 0)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); - dput(parent); - return -EACCES; - } - dput(parent); - - inode = dentry->d_inode; - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - fsstack_copy_attr_all(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - /* if the dentry has been moved from other location - * so, on this stage, its derived permission must be - * rechecked from its private field. - */ - fix_derived_permission(inode); - - generic_fillattr(inode, stat); - sdcardfs_put_lower_path(dentry, &lower_path); - return 0; -} - static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) { - int err = 0; + int err; struct dentry *lower_dentry; struct inode *inode; struct inode *lower_inode; @@ -752,7 +643,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct dentry *parent; int has_rw; - inode = dentry->d_inode; + inode = d_inode(dentry); /* * Check if user has permission to change inode. We don't check if @@ -766,7 +657,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* check the Android group ID */ has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -819,13 +710,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* notify the (possibly copied-up) lower inode */ /* - * Note: we use lower_dentry->d_inode, because lower_inode may be + * Note: we use d_inode(lower_dentry), because lower_inode may be * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ - mutex_lock(&lower_dentry->d_inode->i_mutex); - err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ - mutex_unlock(&lower_dentry->d_inode->i_mutex); + mutex_lock(&d_inode(lower_dentry)->i_mutex); + err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ + NULL); + mutex_unlock(&d_inode(lower_dentry)->i_mutex); if (current->mm) up_write(¤t->mm->mmap_sem); if (err) @@ -848,6 +740,46 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) return err; } +static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct dentry *parent; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + parent = dget_parent(dentry); + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, + sbi->options.derive, 0, 0)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + dput(parent); + return -EACCES; + } + dput(parent); + + inode = d_inode(dentry); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + /* if the dentry has been moved from other location + * so, on this stage, its derived permission must be + * rechecked from its private field. + */ + fix_derived_permission(inode); + + generic_fillattr(inode, stat); + sdcardfs_put_lower_path(dentry, &lower_path); + return 0; +} + const struct inode_operations sdcardfs_symlink_iops = { .permission = sdcardfs_permission, .setattr = sdcardfs_setattr, @@ -856,14 +788,16 @@ const struct inode_operations sdcardfs_symlink_iops = { * These methods are *NOT* perfectly tested. .readlink = sdcardfs_readlink, .follow_link = sdcardfs_follow_link, - .put_link = sdcardfs_put_link, + .put_link = kfree_put_link, */ }; const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, +#if 0 .permission = sdcardfs_permission, +#endif .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index c0b12375b1bf..a4b94df99f32 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -79,8 +79,7 @@ static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) return 0; } -static struct inode *sdcardfs_iget(struct super_block *sb, - struct inode *lower_inode) +struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) { struct sdcardfs_inode_info *info; struct inode *inode; /* the new inode to return */ @@ -206,14 +205,13 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, * Fills in lower_parent_path with on success. */ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, - struct nameidata *nd, struct path *lower_parent_path) + unsigned int flags, struct path *lower_parent_path) { int err = 0; struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; const char *name; - struct nameidata lower_nd; struct path lower_path; struct qstr this; struct sdcardfs_sb_info *sbi; @@ -234,10 +232,10 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* Use vfs_path_lookup to check if the dentry exists or not */ if (sbi->options.lower_fs == LOWER_FS_EXT4) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, - LOOKUP_CASE_INSENSITIVE, &lower_nd); + LOOKUP_CASE_INSENSITIVE, &lower_path); } else if (sbi->options.lower_fs == LOWER_FS_FAT) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, - &lower_nd); + &lower_path); } /* no error: handle positive dentries */ @@ -253,7 +251,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path * returns: -ERRNO if error (0: no error) */ - err = setup_obb_dentry(dentry, &lower_nd.path); + err = setup_obb_dentry(dentry, &lower_path); if(err) { /* if the sbi->obbpath is not available, we can optionally @@ -267,8 +265,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } - sdcardfs_set_lower_path(dentry, &lower_nd.path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_nd.path); + sdcardfs_set_lower_path(dentry, &lower_path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path); if (err) /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); goto out; @@ -306,10 +304,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * the VFS will continue the process of making this negative dentry * into a positive one. */ - if (nd) { - if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) - err = 0; - } else + if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) err = 0; out: @@ -328,7 +323,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * @nd : nameidata of parent inode */ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) + unsigned int flags) { struct dentry *ret = NULL, *parent; struct path lower_parent_path; @@ -359,7 +354,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, nd, &lower_parent_path); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path); if (IS_ERR(ret)) { goto out; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 1fdceffec72c..9d04ae8ceb46 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -156,6 +156,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; } +#if 0 /* * our custom d_alloc_root work-alike * @@ -181,6 +182,7 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) } return ret; } +#endif /* * There is no need to lock the sdcardfs_super_info's rwsem as there is no @@ -195,6 +197,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, struct path lower_path; struct sdcardfs_sb_info *sb_info; void *pkgl_id; + struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -259,12 +262,18 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb->s_magic = SDCARDFS_SUPER_MAGIC; sb->s_op = &sdcardfs_sops; - /* see comment next to the definition of sdcardfs_d_alloc_root */ - sb->s_root = sdcardfs_d_alloc_root(sb); + /* get a new inode and allocate our root dentry */ + inode = sdcardfs_iget(sb, lower_path.dentry->d_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out_sput; + } + sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto out_sput; + goto out_iput; } + d_set_d_op(sb->s_root, &sdcardfs_ci_dops); /* link the upper and lower dentries */ sb->s_root->d_fsdata = NULL; @@ -275,56 +284,60 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, /* set the lower dentries for s_root */ sdcardfs_set_lower_path(sb->s_root, &lower_path); - /* call interpose to create the upper level inode */ - err = sdcardfs_interpose(sb->s_root, sb, &lower_path); - if (!err) { - /* setup permission policy */ - switch(sb_info->options.derive) { - case DERIVE_NONE: - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); - sb_info->obbpath_s = NULL; - break; - case DERIVE_LEGACY: - /* Legacy behavior used to support internal multiuser layout which - * places user_id at the top directory level, with the actual roots - * just below that. Shared OBB path is also at top level. */ - setup_derived_state(sb->s_root->d_inode, - PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - /* initialize the obbpath string and lookup the path - * sb_info->obb_path will be deactivated by path_put - * on sdcardfs_put_super */ - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755); - if(err) - printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", - __func__,__LINE__, sb_info->obbpath_s); - break; - case DERIVE_UNIFIED: - /* Unified multiuser layout which places secondary user_id under - * /Android/user and shared OBB path under /Android/obb. */ - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); - break; - } - fix_derived_permission(sb->s_root->d_inode); + /* + * No need to call interpose because we already have a positive + * dentry, which was instantiated by d_make_root. Just need to + * d_rehash it. + */ + d_rehash(sb->s_root); - if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", - dev_name, lower_sb->s_type->name); - goto out; + /* setup permission policy */ + switch(sb_info->options.derive) { + case DERIVE_NONE: + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); + sb_info->obbpath_s = NULL; + break; + case DERIVE_LEGACY: + /* Legacy behavior used to support internal multiuser layout which + * places user_id at the top directory level, with the actual roots + * just below that. Shared OBB path is also at top level. */ + setup_derived_state(sb->s_root->d_inode, + PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + /* initialize the obbpath string and lookup the path + * sb_info->obb_path will be deactivated by path_put + * on sdcardfs_put_super */ + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + err = prepare_dir(sb_info->obbpath_s, + sb_info->options.fs_low_uid, + sb_info->options.fs_low_gid, 00755); + if(err) + printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", + __func__,__LINE__, sb_info->obbpath_s); + break; + case DERIVE_UNIFIED: + /* Unified multiuser layout which places secondary user_id under + * /Android/user and shared OBB path under /Android/obb. */ + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); + break; } - /* else error: fall through */ + fix_derived_permission(sb->s_root->d_inode); + + if (!silent) + printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + dev_name, lower_sb->s_type->name); + goto out; /* all is well */ - free_dentry_private_data(sb->s_root); + /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); +out_iput: + iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); @@ -346,7 +359,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, { int error; - struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); + struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); if (IS_ERR(s)) return ERR_CAST(s); @@ -378,7 +391,7 @@ static struct file_system_type sdcardfs_fs_type = { .name = SDCARDFS_NAME, .mount = sdcardfs_mount, .kill_sb = generic_shutdown_super, - .fs_flags = FS_REVAL_DOT, + .fs_flags = 0, }; static int __init init_sdcardfs_fs(void) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index c807d7f18f8b..e21f64675a80 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,9 +48,8 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } -static ssize_t sdcardfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) +static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, + struct iov_iter *iter, loff_t pos) { /* * This function returns zero on purpose in order to support direct IO. diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index c786d8f92203..d7ba8d4a423e 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,7 +20,7 @@ #include "sdcardfs.h" #include "strtok.h" -#include "hashtable.h" +#include #include #include #include @@ -29,8 +29,8 @@ #define STRING_BUF_SIZE (512) struct hashtable_entry { - struct hlist_node hlist; - void *key; + struct hlist_node hlist; + void *key; int value; }; @@ -67,12 +67,12 @@ static unsigned int str_hash(void *key) { } static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { - struct hashtable_entry *hash_cur; - struct hlist_node *h_n; + struct hashtable_entry *hash_cur; + + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid) - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid, h_n) - if (appid == hash_cur->key) - return 1; + if (appid == hash_cur->key) + return 1; return 0; } @@ -87,7 +87,7 @@ int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { return 1; } - appid = multiuser_get_app_id(current_fsuid()); + appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); mutex_lock(&pkgl_dat->hashtable_lock); ret = contain_appid_key(pkgl_dat, (void *)appid); mutex_unlock(&pkgl_dat->hashtable_lock); @@ -98,13 +98,12 @@ appid_t get_appid(void *pkgl_id, const char *app_name) { struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; struct hashtable_entry *hash_cur; - struct hlist_node *h_n; unsigned int hash = str_hash((void *)app_name); appid_t ret_id; //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); if (!strcasecmp(app_name, hash_cur->key)) { ret_id = (appid_t)hash_cur->value; @@ -140,7 +139,7 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ - if (current_fsuid() == 0) { + if (from_kuid(&init_user_ns, current_fsuid()) == 0) { return 1; } @@ -148,7 +147,8 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, * parent or holds sdcard_rw. */ if (w_ok) { if (parent_node && - (current_fsuid() == SDCARDFS_I(parent_node)->d_uid)) { + (from_kuid(&init_user_ns, current_fsuid()) == + SDCARDFS_I(parent_node)->d_uid)) { return 1; } return has_rw; @@ -174,11 +174,10 @@ int open_flags_to_access_mode(int open_flags) { static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - struct hlist_node *h_n; unsigned int hash = str_hash(key); //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_cur->value = value; return 0; @@ -202,11 +201,10 @@ static void remove_str_to_int(struct hashtable_entry *h_entry) { static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - struct hlist_node *h_n; //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, - (unsigned int)key, h_n) { + (unsigned int)key) { if (key == hash_cur->key) { hash_cur->value = value; return 0; @@ -230,14 +228,13 @@ static void remove_int_to_null(struct hashtable_entry *h_entry) { static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) { struct hashtable_entry *hash_cur; - struct hlist_node *h_n; struct hlist_node *h_t; int i; - hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist, h_n) + hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) remove_str_to_int(hash_cur); - hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist, h_n) - remove_int_to_null(hash_cur); + hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist) + remove_int_to_null(hash_cur); hash_init(pkgl_dat->package_to_appid); hash_init(pkgl_dat->appid_with_rw); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 90f8b24e4a52..51f6c7912584 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -69,8 +69,8 @@ #define fix_derived_permission(x) \ do { \ - (x)->i_uid = SDCARDFS_I(x)->d_uid; \ - (x)->i_gid = SDCARDFS_I(x)->d_gid; \ + (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ + (x)->i_gid = make_kgid(&init_user_ns, SDCARDFS_I(x)->d_gid); \ (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ } while (0) @@ -159,7 +159,9 @@ extern void sdcardfs_destroy_dentry_cache(void); extern int new_dentry_private_data(struct dentry *dentry); extern void free_dentry_private_data(struct dentry *dentry); extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd); + unsigned int flags); +extern struct inode *sdcardfs_iget(struct super_block *sb, + struct inode *lower_inode); extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, struct path *lower_path); @@ -387,13 +389,13 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); return dir; } static inline void unlock_dir(struct dentry *dir) { - mutex_unlock(&dir->d_inode->i_mutex); + mutex_unlock(&d_inode(dir)->i_mutex); dput(dir); } @@ -402,16 +404,9 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m int err; struct dentry *dent; struct iattr attrs; - struct nameidata nd; + struct path parent; - err = kern_path_parent(path_s, &nd); - if (err) { - if (err == -EEXIST) - err = 0; - goto out; - } - - dent = lookup_create(&nd, 1); + dent = kern_path_locked(path_s, &parent); if (IS_ERR(dent)) { err = PTR_ERR(dent); if (err == -EEXIST) @@ -419,29 +414,27 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m goto out_unlock; } - err = vfs_mkdir(nd.path.dentry->d_inode, dent, mode); + err = vfs_mkdir(d_inode(parent.dentry), dent, mode); if (err) { if (err == -EEXIST) err = 0; goto out_dput; } - attrs.ia_uid = uid; - attrs.ia_gid = gid; + attrs.ia_uid = make_kuid(&init_user_ns, uid); + attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; - mutex_lock(&dent->d_inode->i_mutex); - notify_change(dent, &attrs); - mutex_unlock(&dent->d_inode->i_mutex); + mutex_lock(&d_inode(dent)->i_mutex); + notify_change(dent, &attrs, NULL); + mutex_unlock(&d_inode(dent)->i_mutex); out_dput: dput(dent); out_unlock: /* parent dentry locked by lookup_create */ - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - path_put(&nd.path); - -out: + mutex_unlock(&d_inode(parent.dentry)->i_mutex); + path_put(&parent); return err; } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 1d206c82dfdf..f153ce1b8cf3 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -122,7 +122,7 @@ static void sdcardfs_evict_inode(struct inode *inode) struct inode *lower_inode; truncate_inode_pages(&inode->i_data, 0); - end_writeback(inode); + clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented * by our read_inode when it was created initially. @@ -193,9 +193,9 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct seq_file *m, struct vfsmount *mnt) +static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) { - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(mnt->mnt_sb); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; if (opts->fs_low_uid != 0) diff --git a/include/linux/namei.h b/include/linux/namei.h index 788f3cbc12b7..5f0abcf0aff4 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -80,6 +80,8 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *, extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct path *); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); From e4187c55208b3ef3ef8706f182f3aad16b22689d Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:33:46 -0700 Subject: [PATCH 0531/1276] ANDROID: Changed type-casting in packagelist management Fixed existing type-casting in packagelist management code. All warnings at compile time were taken care of. Change-Id: I1ea97786d1d1325f31b9f09ae966af1f896a2af5 Signed-off-by: Daniel Campello --- fs/sdcardfs/packagelist.c | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d7ba8d4a423e..f11591da141d 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -31,7 +31,7 @@ struct hashtable_entry { struct hlist_node hlist; void *key; - int value; + unsigned int value; }; struct packagelist_data { @@ -54,7 +54,7 @@ static const char* const kpackageslist_file = "/data/system/packages.list"; /* Supplementary groups to execute with */ static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; -static unsigned int str_hash(void *key) { +static unsigned int str_hash(const char *key) { int i; unsigned int h = strlen(key); char *data = (char *)key; @@ -66,13 +66,13 @@ static unsigned int str_hash(void *key) { return h; } -static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { +static int contain_appid_key(struct packagelist_data *pkgl_dat, unsigned int appid) { struct hashtable_entry *hash_cur; - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid) - - if (appid == hash_cur->key) + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, appid) + if ((void *)(uintptr_t)appid == hash_cur->key) return 1; + return 0; } @@ -89,7 +89,7 @@ int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); mutex_lock(&pkgl_dat->hashtable_lock); - ret = contain_appid_key(pkgl_dat, (void *)appid); + ret = contain_appid_key(pkgl_dat, appid); mutex_unlock(&pkgl_dat->hashtable_lock); return ret; } @@ -98,7 +98,7 @@ appid_t get_appid(void *pkgl_id, const char *app_name) { struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; struct hashtable_entry *hash_cur; - unsigned int hash = str_hash((void *)app_name); + unsigned int hash = str_hash(app_name); appid_t ret_id; //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); @@ -171,7 +171,9 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { +static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, + unsigned int value) +{ struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); @@ -198,14 +200,15 @@ static void remove_str_to_int(struct hashtable_entry *h_entry) { kmem_cache_free(hashtable_entry_cachep, h_entry); } -static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { +static int insert_int_to_null(struct packagelist_data *pkgl_dat, unsigned int key, + unsigned int value) +{ struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, - (unsigned int)key) { - if (key == hash_cur->key) { + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, key) { + if ((void *)(uintptr_t)key == hash_cur->key) { hash_cur->value = value; return 0; } @@ -213,10 +216,9 @@ static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); if (!new_entry) return -ENOMEM; - new_entry->key = key; + new_entry->key = (void *)(uintptr_t)key; new_entry->value = value; - hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, - (unsigned int)new_entry->key); + hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, key); return 0; } @@ -260,7 +262,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { while ((read_amount = sys_read(fd, pkgl_dat->read_buf, sizeof(pkgl_dat->read_buf))) > 0) { - int appid; + unsigned int appid; char *token; int one_line_len = 0; int additional_read; @@ -277,7 +279,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { if (additional_read > 0) sys_lseek(fd, -additional_read, SEEK_CUR); - if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s", + if (sscanf(pkgl_dat->read_buf, "%s %u %*d %*s %*s %s", pkgl_dat->app_name_buf, &appid, pkgl_dat->gids_buf) == 3) { ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); @@ -291,7 +293,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { while (token != NULL) { if (!kstrtoul(token, 10, &ret_gid) && (ret_gid == pkgl_dat->write_gid)) { - ret = insert_int_to_null(pkgl_dat, (void *)appid, 1); + ret = insert_int_to_null(pkgl_dat, appid, 1); if (ret) { sys_close(fd); mutex_unlock(&pkgl_dat->hashtable_lock); From cf76072a5cd8fd6c11b2cc1cb2ed6541db16c1b8 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 3 Feb 2016 21:08:21 -0800 Subject: [PATCH 0532/1276] ANDROID: sdcardfs: Bring up to date with Android M permissions: In M, the workings of sdcardfs were changed significantly. This brings sdcardfs into line with the changes. Change-Id: I10e91a84a884c838feef7aa26c0a2b21f02e052e --- fs/sdcardfs/Kconfig | 1 + fs/sdcardfs/derived_perm.c | 119 ++++----- fs/sdcardfs/file.c | 10 +- fs/sdcardfs/inode.c | 78 +++--- fs/sdcardfs/lookup.c | 40 +-- fs/sdcardfs/main.c | 141 +++++------ fs/sdcardfs/packagelist.c | 504 ++++++++++++++++++------------------- fs/sdcardfs/sdcardfs.h | 134 ++++++---- fs/sdcardfs/strtok.h | 75 ------ fs/sdcardfs/super.c | 11 +- 10 files changed, 501 insertions(+), 612 deletions(-) delete mode 100644 fs/sdcardfs/strtok.h diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index d995f3eaae6d..ab25f88ebb37 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -1,5 +1,6 @@ config SDCARD_FS tristate "sdcard file system" + depends on CONFIGFS_FS default n help Sdcardfs is based on Wrapfs file system. diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 00c33a471dcc..128b3e56851f 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -29,24 +29,23 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->perm = PERM_INHERIT; ci->userid = pi->userid; ci->d_uid = pi->d_uid; - ci->d_gid = pi->d_gid; - ci->d_mode = pi->d_mode; + ci->under_android = pi->under_android; } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, gid_t gid, mode_t mode) + userid_t userid, uid_t uid, bool under_android) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->perm = perm; info->userid = userid; info->d_uid = uid; - info->d_gid = gid; - info->d_mode = mode; + info->under_android = under_android; } -void get_derived_permission(struct dentry *parent, struct dentry *dentry) +/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); @@ -63,86 +62,68 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) inherit_derived_state(parent->d_inode, dentry->d_inode); - //printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name, - // dentry->d_name.name, parent_info->perm); - - if (sbi->options.derive == DERIVE_NONE) { - return; - } - /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { case PERM_INHERIT: /* Already inherited above */ break; - case PERM_LEGACY_PRE_ROOT: + case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); break; case PERM_ROOT: /* Assume masked off by default. */ - info->d_mode = 00770; - if (!strcasecmp(dentry->d_name.name, "Android")) { + if (!strcasecmp(newdentry->d_name.name, "Android")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; - info->d_mode = 00771; - } else if (sbi->options.split_perms) { - if (!strcasecmp(dentry->d_name.name, "DCIM") - || !strcasecmp(dentry->d_name.name, "Pictures")) { - info->d_gid = AID_SDCARD_PICS; - } else if (!strcasecmp(dentry->d_name.name, "Alarms") - || !strcasecmp(dentry->d_name.name, "Movies") - || !strcasecmp(dentry->d_name.name, "Music") - || !strcasecmp(dentry->d_name.name, "Notifications") - || !strcasecmp(dentry->d_name.name, "Podcasts") - || !strcasecmp(dentry->d_name.name, "Ringtones")) { - info->d_gid = AID_SDCARD_AV; - } + info->under_android = true; } break; case PERM_ANDROID: - if (!strcasecmp(dentry->d_name.name, "data")) { + if (!strcasecmp(newdentry->d_name.name, "data")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; - info->d_mode = 00771; - } else if (!strcasecmp(dentry->d_name.name, "obb")) { + } else if (!strcasecmp(newdentry->d_name.name, "obb")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; - info->d_mode = 00771; - // FIXME : this feature will be implemented later. /* Single OBB directory is always shared */ - } else if (!strcasecmp(dentry->d_name.name, "user")) { - /* User directories must only be accessible to system, protected - * by sdcard_all. Zygote will bind mount the appropriate user- - * specific path. */ - info->perm = PERM_ANDROID_USER; - info->d_gid = AID_SDCARD_ALL; - info->d_mode = 00770; + } else if (!strcasecmp(newdentry->d_name.name, "media")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; } break; - /* same policy will be applied on PERM_ANDROID_DATA - * and PERM_ANDROID_OBB */ case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: - appid = get_appid(sbi->pkgl_id, dentry->d_name.name); + case PERM_ANDROID_MEDIA: + appid = get_appid(sbi->pkgl_id, newdentry->d_name.name); if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } - info->d_mode = 00770; - break; - case PERM_ANDROID_USER: - /* Root of a secondary user */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); - info->d_gid = AID_SDCARD_R; - info->d_mode = 00771; break; } } +void get_derived_permission(struct dentry *parent, struct dentry *dentry) +{ + get_derived_permission_new(parent, dentry, dentry); +} + +void get_derive_permissions_recursive(struct dentry *parent) { + struct dentry *dentry; + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + if (dentry && dentry->d_inode) { + mutex_lock(&dentry->d_inode->i_mutex); + get_derived_permission(parent, dentry); + fix_derived_permission(dentry->d_inode); + get_derive_permissions_recursive(dentry); + mutex_unlock(&dentry->d_inode->i_mutex); + } + } +} + /* main function for updating derived permission */ -inline void update_derived_permission(struct dentry *dentry) +inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; @@ -154,6 +135,7 @@ inline void update_derived_permission(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ + mutex_lock(&dentry->d_inode->i_mutex); if(IS_ROOT(dentry)) { //setup_default_pre_root_state(dentry->d_inode); } else { @@ -164,6 +146,7 @@ inline void update_derived_permission(struct dentry *dentry) } } fix_derived_permission(dentry->d_inode); + mutex_unlock(&dentry->d_inode->i_mutex); } int need_graft_path(struct dentry *dentry) @@ -177,7 +160,7 @@ int need_graft_path(struct dentry *dentry) !strcasecmp(dentry->d_name.name, "obb")) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.derive == DERIVE_UNIFIED + if(!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -207,8 +190,7 @@ int is_obbpath_invalid(struct dentry *dent) path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if(!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: " - "fail to allocate path_buf in %s.\n", __func__); + printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -234,21 +216,16 @@ int is_base_obbpath(struct dentry *dentry) struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); spin_lock(&SDCARDFS_D(dentry)->lock); - /* DERIVED_LEGACY */ - if(parent_info->perm == PERM_LEGACY_PRE_ROOT && - !strcasecmp(dentry->d_name.name, "obb")) { - ret = 1; - } - /* DERIVED_UNIFIED :/Android/obb is the base obbpath */ - else if (parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { - if((sbi->options.derive == DERIVE_UNIFIED - && parent_info->userid == 0)) { + if (sbi->options.multiuser) { + if(parent_info->perm == PERM_PRE_ROOT && + !strcasecmp(dentry->d_name.name, "obb")) { ret = 1; } + } else if (parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + ret = 1; } spin_unlock(&SDCARDFS_D(dentry)->lock); - dput(parent); return ret; } @@ -272,8 +249,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) if(!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: " - "the sbi->obbpath is found\n"); + printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally @@ -281,8 +257,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * but, the current implementation just returns an error * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: " - "the sbi->obbpath is not available\n"); + printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index f9c5eaafc619..c249fa982d3c 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -209,7 +209,6 @@ static int sdcardfs_open(struct inode *inode, struct file *file) struct dentry *parent = dget_parent(dentry); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw; /* don't open unhashed/deleted files */ if (d_unhashed(dentry)) { @@ -217,11 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, - open_flags_to_access_mode(file->f_flags), has_rw)) { + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -257,8 +252,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); else { - fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode)); - fix_derived_permission(inode); + sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); } out_revert_cred: diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 75c622bac2f5..2528da0d3ae1 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -55,11 +55,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -80,7 +78,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); @@ -143,11 +141,9 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -255,8 +251,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int fullpath_namelen; int touch_err = 0; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -293,19 +288,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if(err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to - * its orig_path. this dentry will be revalidated later. + * its orig_path. this dentry will be revalidated later. * but now, the lower_path should be NULL */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required */ path_get(&lower_path); } else make_nomedia_in_obb = 1; } - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); if (err) goto out; @@ -314,7 +309,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); - if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) + if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -371,12 +366,9 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; int err; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - //char *path_s = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -461,14 +453,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *trap = NULL; struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name, - sbi->options.derive, 1, has_rw) || - !check_caller_access_to_name(new_dir, new_dentry->d_name.name, - sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || + !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); @@ -505,26 +493,31 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; /* Copy attrs from lower dir, but i_uid/i_gid */ - fsstack_copy_attr_all(new_dir, d_inode(lower_new_dir_dentry)); + sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry)); fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry)); - fix_derived_permission(new_dir); + if (new_dir != old_dir) { - fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); + sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); - fix_derived_permission(old_dir); + /* update the derived permission of the old_dentry * with its new parent */ new_parent = dget_parent(new_dentry); if(new_parent) { if(d_inode(old_dentry)) { - get_derived_permission(new_parent, old_dentry); - fix_derived_permission(d_inode(old_dentry)); + update_derived_permission_lock(old_dentry); } dput(new_parent); } } - + /* At this point, not all dentry information has been moved, so + * we pass along new_dentry for the name.*/ + mutex_lock(&d_inode(old_dentry)->i_mutex); + get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); + fix_derived_permission(d_inode(old_dentry)); + get_derive_permissions_recursive(old_dentry); + mutex_unlock(&d_inode(old_dentry)->i_mutex); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -639,9 +632,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct inode *lower_inode; struct path lower_path; struct iattr lower_ia; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct dentry *parent; - int has_rw; inode = d_inode(dentry); @@ -655,10 +646,8 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ if (!err) { /* check the Android group ID */ - has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, - sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -723,10 +712,8 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (err) goto out; - /* get attributes from the lower inode */ - fsstack_copy_attr_all(inode, lower_inode); - /* update derived permission of the upper inode */ - fix_derived_permission(inode); + /* get attributes from the lower inode and update derived permissions */ + sdcardfs_copy_and_fix_attrs(inode, lower_inode); /* * Not running fsstack_copy_inode_size(inode, lower_inode), because @@ -748,11 +735,9 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *lower_inode; struct path lower_path; struct dentry *parent; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, - sbi->options.derive, 0, 0)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -767,13 +752,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); - fsstack_copy_attr_all(inode, lower_inode); + + sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - /* if the dentry has been moved from other location - * so, on this stage, its derived permission must be - * rechecked from its private field. - */ - fix_derived_permission(inode); + generic_fillattr(inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a4b94df99f32..f80abcb6b467 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -64,10 +64,17 @@ int new_dentry_private_data(struct dentry *dentry) return 0; } -static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode) +struct inode_data { + struct inode *lower_inode; + userid_t id; +}; + +static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/) { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); - if (current_lower_inode == (struct inode *)candidate_lower_inode) + userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && + current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ else return 0; /* no match */ @@ -79,12 +86,15 @@ static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) return 0; } -struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) +struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id) { struct sdcardfs_inode_info *info; + struct inode_data data; struct inode *inode; /* the new inode to return */ int err; + data.id = id; + data.lower_inode = lower_inode; inode = iget5_locked(sb, /* our superblock */ /* * hashval: we use inode number, but we can @@ -94,7 +104,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) lower_inode->i_ino, /* hashval */ sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ - lower_inode); /* data passed to test+set fxns */ + &data); /* data passed to test+set fxns */ if (!inode) { err = -EACCES; iput(lower_inode); @@ -146,11 +156,9 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) lower_inode->i_rdev); /* all well, copy inode attributes */ - fsstack_copy_attr_all(inode, lower_inode); + sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - fix_derived_permission(inode); - unlock_new_inode(inode); return inode; } @@ -164,7 +172,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) * @lower_path: the lower path (caller does path_get/put) */ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path) + struct path *lower_path, userid_t id) { int err = 0; struct inode *inode; @@ -186,14 +194,14 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, */ /* inherit lower inode number for sdcardfs's inode */ - inode = sdcardfs_iget(sb, lower_inode); + inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out; } d_add(dentry, inode); - update_derived_permission(dentry); + update_derived_permission_lock(dentry); out: return err; } @@ -205,7 +213,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, * Fills in lower_parent_path with on success. */ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, - unsigned int flags, struct path *lower_parent_path) + unsigned int flags, struct path *lower_parent_path, userid_t id) { int err = 0; struct vfsmount *lower_dir_mnt; @@ -266,7 +274,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); if (err) /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); goto out; @@ -328,13 +336,11 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, struct dentry *ret = NULL, *parent; struct path lower_parent_path; int err = 0; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, 0, 0)) { + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -354,7 +360,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) { goto out; @@ -365,8 +371,10 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, fsstack_copy_attr_times(dentry->d_inode, sdcardfs_lower_inode(dentry->d_inode)); /* get drived permission */ + mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); + mutex_unlock(&dentry->d_inode->i_mutex); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 9d04ae8ceb46..80aa355d801e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -24,25 +24,27 @@ #include enum { - Opt_uid, + Opt_fsuid, + Opt_fsgid, Opt_gid, - Opt_wgid, Opt_debug, - Opt_split, - Opt_derive, Opt_lower_fs, + Opt_mask, + Opt_multiuser, // May need? + Opt_userid, Opt_reserved_mb, Opt_err, }; static const match_table_t sdcardfs_tokens = { - {Opt_uid, "uid=%u"}, + {Opt_fsuid, "fsuid=%u"}, + {Opt_fsgid, "fsgid=%u"}, {Opt_gid, "gid=%u"}, - {Opt_wgid, "wgid=%u"}, {Opt_debug, "debug"}, - {Opt_split, "split"}, - {Opt_derive, "derive=%s"}, {Opt_lower_fs, "lower_fs=%s"}, + {Opt_mask, "mask=%u"}, + {Opt_userid, "userid=%d"}, + {Opt_multiuser, "multiuser"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -58,12 +60,10 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; - /* by default, we use AID_SDCARD_RW as write_gid */ - opts->write_gid = AID_SDCARD_RW; - /* default permission policy - * (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */ - opts->derive = DERIVE_NONE; - opts->split_perms = 0; + opts->mask = 0; + opts->multiuser = false; + opts->fs_user_id = 0; + opts->gid = 0; /* by default, we use LOWER_FS_EXT4 as lower fs type */ opts->lower_fs = LOWER_FS_EXT4; /* by default, 0MB is reserved */ @@ -85,37 +85,33 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_debug: *debug = 1; break; - case Opt_uid: + case Opt_fsuid: if (match_int(&args[0], &option)) return 0; opts->fs_low_uid = option; break; - case Opt_gid: + case Opt_fsgid: if (match_int(&args[0], &option)) return 0; opts->fs_low_gid = option; break; - case Opt_wgid: + case Opt_gid: if (match_int(&args[0], &option)) return 0; - opts->write_gid = option; + opts->gid = option; break; - case Opt_split: - opts->split_perms=1; + case Opt_userid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_user_id = option; break; - case Opt_derive: - string_option = match_strdup(&args[0]); - if (!strcmp("none", string_option)) { - opts->derive = DERIVE_NONE; - } else if (!strcmp("legacy", string_option)) { - opts->derive = DERIVE_LEGACY; - } else if (!strcmp("unified", string_option)) { - opts->derive = DERIVE_UNIFIED; - } else { - kfree(string_option); - goto invalid_option; - } - kfree(string_option); + case Opt_mask: + if (match_int(&args[0], &option)) + return 0; + opts->mask = option; + break; + case Opt_multiuser: + opts->multiuser = true; break; case Opt_lower_fs: string_option = match_strdup(&args[0]); @@ -184,6 +180,11 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) } #endif +DEFINE_MUTEX(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); +EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +EXPORT_SYMBOL_GPL(sdcardfs_super_list); + /* * There is no need to lock the sdcardfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. @@ -196,7 +197,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, struct super_block *lower_sb; struct path lower_path; struct sdcardfs_sb_info *sb_info; - void *pkgl_id; struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -215,8 +215,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing " - "lower directory '%s'\n", dev_name); + printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } @@ -229,7 +228,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, } sb_info = sb->s_fs_info; - /* parse options */ err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); if (err) { @@ -237,14 +235,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, goto out_freesbi; } - if (sb_info->options.derive != DERIVE_NONE) { - pkgl_id = packagelist_create(sb_info->options.write_gid); - if(IS_ERR(pkgl_id)) - goto out_freesbi; - else - sb_info->pkgl_id = pkgl_id; - } - /* set the lower superblock field of upper superblock */ lower_sb = lower_path.dentry->d_sb; atomic_inc(&lower_sb->s_active); @@ -263,7 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb->s_op = &sdcardfs_sops; /* get a new inode and allocate our root dentry */ - inode = sdcardfs_iget(sb, lower_path.dentry->d_inode); + inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; @@ -292,41 +282,22 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, d_rehash(sb->s_root); /* setup permission policy */ - switch(sb_info->options.derive) { - case DERIVE_NONE: - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); - sb_info->obbpath_s = NULL; - break; - case DERIVE_LEGACY: - /* Legacy behavior used to support internal multiuser layout which - * places user_id at the top directory level, with the actual roots - * just below that. Shared OBB path is also at top level. */ - setup_derived_state(sb->s_root->d_inode, - PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - /* initialize the obbpath string and lookup the path - * sb_info->obb_path will be deactivated by path_put - * on sdcardfs_put_super */ - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - err = prepare_dir(sb_info->obbpath_s, + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + mutex_lock(&sdcardfs_super_list_lock); + if(sb_info->options.multiuser) { + setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + /*err = prepare_dir(sb_info->obbpath_s, sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755); - if(err) - printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", - __func__,__LINE__, sb_info->obbpath_s); - break; - case DERIVE_UNIFIED: - /* Unified multiuser layout which places secondary user_id under - * /Android/user and shared OBB path under /Android/obb. */ - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); - break; + sb_info->options.fs_low_gid, 00755);*/ + } else { + setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); + sb_info->sb = sb; + list_add(&sb_info->list, &sdcardfs_super_list); + mutex_unlock(&sdcardfs_super_list_lock); if (!silent) printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", @@ -341,7 +312,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); - packagelist_destroy(sb_info->pkgl_id); out_freesbi: kfree(SDCARDFS_SB(sb)); sb->s_fs_info = NULL; @@ -386,11 +356,22 @@ struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, raw_data, sdcardfs_read_super); } +void sdcardfs_kill_sb(struct super_block *sb) { + struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { + sbi = SDCARDFS_SB(sb); + mutex_lock(&sdcardfs_super_list_lock); + list_del(&sbi->list); + mutex_unlock(&sdcardfs_super_list_lock); + } + generic_shutdown_super(sb); +} + static struct file_system_type sdcardfs_fs_type = { .owner = THIS_MODULE, .name = SDCARDFS_NAME, .mount = sdcardfs_mount, - .kill_sb = generic_shutdown_super, + .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index f11591da141d..ba3478d94107 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -19,13 +19,16 @@ */ #include "sdcardfs.h" -#include "strtok.h" #include -#include -#include -#include #include + +#include +#include +#include + +#include + #define STRING_BUF_SIZE (512) struct hashtable_entry { @@ -34,25 +37,20 @@ struct hashtable_entry { unsigned int value; }; +struct sb_list { + struct super_block *sb; + struct list_head list; +}; + struct packagelist_data { DECLARE_HASHTABLE(package_to_appid,8); - DECLARE_HASHTABLE(appid_with_rw,7); struct mutex hashtable_lock; - struct task_struct *thread_id; - gid_t write_gid; - char *strtok_last; - char read_buf[STRING_BUF_SIZE]; - char event_buf[STRING_BUF_SIZE]; - char app_name_buf[STRING_BUF_SIZE]; - char gids_buf[STRING_BUF_SIZE]; + }; -static struct kmem_cache *hashtable_entry_cachep; +static struct packagelist_data *pkgl_data_all; -/* Path to system-provided mapping of package name to appIds */ -static const char* const kpackageslist_file = "/data/system/packages.list"; -/* Supplementary groups to execute with */ -static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; +static struct kmem_cache *hashtable_entry_cachep; static unsigned int str_hash(const char *key) { int i; @@ -66,62 +64,29 @@ static unsigned int str_hash(const char *key) { return h; } -static int contain_appid_key(struct packagelist_data *pkgl_dat, unsigned int appid) { - struct hashtable_entry *hash_cur; - - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, appid) - if ((void *)(uintptr_t)appid == hash_cur->key) - return 1; - - return 0; -} - -/* Return if the calling UID holds sdcard_rw. */ -int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; - appid_t appid; - int ret; - - /* No additional permissions enforcement */ - if (derive == DERIVE_NONE) { - return 1; - } - - appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); - mutex_lock(&pkgl_dat->hashtable_lock); - ret = contain_appid_key(pkgl_dat, appid); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; -} - appid_t get_appid(void *pkgl_id, const char *app_name) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + struct packagelist_data *pkgl_dat = pkgl_data_all; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(app_name); appid_t ret_id; - //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); mutex_lock(&pkgl_dat->hashtable_lock); hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { - //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); if (!strcasecmp(app_name, hash_cur->key)) { ret_id = (appid_t)hash_cur->value; mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "=> app_id: %d\n", (int)ret_id); return ret_id; } } mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "=> app_id: %d\n", 0); return 0; } /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char* name, - derive_t derive, int w_ok, int has_rw) { +int check_caller_access_to_name(struct inode *parent_node, const char* name) { /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { @@ -132,28 +97,12 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, } } - /* No additional permissions enforcement */ - if (derive == DERIVE_NONE) { - return 1; - } - /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ if (from_kuid(&init_user_ns, current_fsuid()) == 0) { return 1; } - /* If asking to write, verify that caller either owns the - * parent or holds sdcard_rw. */ - if (w_ok) { - if (parent_node && - (from_kuid(&init_user_ns, current_fsuid()) == - SDCARDFS_I(parent_node)->d_uid)) { - return 1; - } - return has_rw; - } - /* No extra permissions to enforce */ return 1; } @@ -171,14 +120,13 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, +static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key, unsigned int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); - //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_cur->value = value; @@ -194,245 +142,275 @@ static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, return 0; } -static void remove_str_to_int(struct hashtable_entry *h_entry) { - //printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value); - kfree(h_entry->key); - kmem_cache_free(hashtable_entry_cachep, h_entry); +static void fixup_perms(struct super_block *sb) { + if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { + mutex_lock(&sb->s_root->d_inode->i_mutex); + get_derive_permissions_recursive(sb->s_root); + mutex_unlock(&sb->s_root->d_inode->i_mutex); + } } -static int insert_int_to_null(struct packagelist_data *pkgl_dat, unsigned int key, - unsigned int value) -{ - struct hashtable_entry *hash_cur; - struct hashtable_entry *new_entry; +static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, + unsigned int value) { + int ret; + struct sdcardfs_sb_info *sbinfo; + mutex_lock(&sdcardfs_super_list_lock); + mutex_lock(&pkgl_dat->hashtable_lock); + ret = insert_str_to_int_lock(pkgl_dat, key, value); + mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, key) { - if ((void *)(uintptr_t)key == hash_cur->key) { - hash_cur->value = value; - return 0; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo) { + fixup_perms(sbinfo->sb); } } - new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); - if (!new_entry) - return -ENOMEM; - new_entry->key = (void *)(uintptr_t)key; - new_entry->value = value; - hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, key); - return 0; + mutex_unlock(&sdcardfs_super_list_lock); + return ret; } -static void remove_int_to_null(struct hashtable_entry *h_entry) { - //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value); +static void remove_str_to_int_lock(struct hashtable_entry *h_entry) { + kfree(h_entry->key); + hash_del(&h_entry->hlist); kmem_cache_free(hashtable_entry_cachep, h_entry); } +static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key) +{ + struct sdcardfs_sb_info *sbinfo; + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + mutex_lock(&sdcardfs_super_list_lock); + mutex_lock(&pkgl_dat->hashtable_lock); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { + remove_str_to_int_lock(hash_cur); + break; + } + } + mutex_unlock(&pkgl_dat->hashtable_lock); + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo) { + fixup_perms(sbinfo->sb); + } + } + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) { struct hashtable_entry *hash_cur; struct hlist_node *h_t; int i; - + mutex_lock(&pkgl_dat->hashtable_lock); hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) - remove_str_to_int(hash_cur); - hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist) - remove_int_to_null(hash_cur); - + remove_str_to_int_lock(hash_cur); + mutex_unlock(&pkgl_dat->hashtable_lock); hash_init(pkgl_dat->package_to_appid); - hash_init(pkgl_dat->appid_with_rw); } -static int read_package_list(struct packagelist_data *pkgl_dat) { - int ret; - int fd; - int read_amount; +static struct packagelist_data * packagelist_create(void) +{ + struct packagelist_data *pkgl_dat; - printk(KERN_INFO "sdcardfs: read_package_list\n"); + pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); + if (!pkgl_dat) { + printk(KERN_ERR "sdcardfs: Failed to create hash\n"); + return ERR_PTR(-ENOMEM); + } - mutex_lock(&pkgl_dat->hashtable_lock); + mutex_init(&pkgl_dat->hashtable_lock); + hash_init(pkgl_dat->package_to_appid); + + return pkgl_dat; +} +static void packagelist_destroy(struct packagelist_data *pkgl_dat) +{ remove_all_hashentrys(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + kfree(pkgl_dat); +} - fd = sys_open(kpackageslist_file, O_RDONLY, 0); - if (fd < 0) { - printk(KERN_ERR "sdcardfs: failed to open package list\n"); - mutex_unlock(&pkgl_dat->hashtable_lock); - return fd; - } +struct package_appid { + struct config_item item; + int add_pid; +}; - while ((read_amount = sys_read(fd, pkgl_dat->read_buf, - sizeof(pkgl_dat->read_buf))) > 0) { - unsigned int appid; - char *token; - int one_line_len = 0; - int additional_read; - unsigned long ret_gid; - - while (one_line_len < read_amount) { - if (pkgl_dat->read_buf[one_line_len] == '\n') { - one_line_len++; - break; - } - one_line_len++; - } - additional_read = read_amount - one_line_len; - if (additional_read > 0) - sys_lseek(fd, -additional_read, SEEK_CUR); - - if (sscanf(pkgl_dat->read_buf, "%s %u %*d %*s %*s %s", - pkgl_dat->app_name_buf, &appid, - pkgl_dat->gids_buf) == 3) { - ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); - if (ret) { - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; - } - - token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last); - while (token != NULL) { - if (!kstrtoul(token, 10, &ret_gid) && - (ret_gid == pkgl_dat->write_gid)) { - ret = insert_int_to_null(pkgl_dat, appid, 1); - if (ret) { - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; - } - break; - } - token = strtok_r(NULL, ",", &pkgl_dat->strtok_last); - } - } - } +static inline struct package_appid *to_package_appid(struct config_item *item) +{ + return item ? container_of(item, struct package_appid, item) : NULL; +} - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return 0; +static ssize_t package_appid_attr_show(struct config_item *item, + char *page) +{ + ssize_t count; + count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name)); + return count; } -static int packagelist_reader(void *thread_data) +static ssize_t package_appid_attr_store(struct config_item *item, + const char *page, size_t count) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data; - struct inotify_event *event; - bool active = false; - int event_pos; - int event_size; - int res = 0; - int nfd; - - allow_signal(SIGINT); - - nfd = sys_inotify_init(); - if (nfd < 0) { - printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd); - return nfd; - } + struct package_appid *package_appid = to_package_appid(item); + unsigned long tmp; + char *p = (char *) page; + int ret; - while (!kthread_should_stop()) { - if (signal_pending(current)) { - ssleep(1); - continue; - } + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; - if (!active) { - res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF); - if (res < 0) { - if (res == -ENOENT || res == -EACCES) { - /* Framework may not have created yet, sleep and retry */ - printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n"); - ssleep(2); - printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n"); - continue; - } else { - printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res); - goto interruptable_sleep; - } - } - /* Watch above will tell us about any future changes, so - * read the current state. */ - res = read_package_list(pkgl_dat); - if (res) { - printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res); - goto interruptable_sleep; - } - active = true; - } + if (tmp > INT_MAX) + return -ERANGE; + ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp); + package_appid->add_pid = tmp; + if (ret) + return ret; - event_pos = 0; - res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf)); - if (res < (int) sizeof(*event)) { - if (res == -EINTR) - continue; - printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res); - goto interruptable_sleep; - } + return count; +} - while (res >= (int) sizeof(*event)) { - event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos); +static struct configfs_attribute package_appid_attr_add_pid = { + .ca_owner = THIS_MODULE, + .ca_name = "appid", + .ca_mode = S_IRUGO | S_IWUGO, + .show = package_appid_attr_show, + .store = package_appid_attr_store, +}; - printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask); - if ((event->mask & IN_IGNORED) == IN_IGNORED) { - /* Previously watched file was deleted, probably due to move - * that swapped in new data; re-arm the watch and read. */ - active = false; - } +static struct configfs_attribute *package_appid_attrs[] = { + &package_appid_attr_add_pid, + NULL, +}; - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - continue; +static void package_appid_release(struct config_item *item) +{ + printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); + /* item->ci_name is freed already, so we rely on the dentry */ + remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name); + kfree(to_package_appid(item)); +} -interruptable_sleep: - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - flush_signals(current); - sys_close(nfd); - return res; +static struct configfs_item_operations package_appid_item_ops = { + .release = package_appid_release, +}; + +static struct config_item_type package_appid_type = { + .ct_item_ops = &package_appid_item_ops, + .ct_attrs = package_appid_attrs, + .ct_owner = THIS_MODULE, +}; + + +struct sdcardfs_packages { + struct config_group group; +}; + +static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL; } -void * packagelist_create(gid_t write_gid) +static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name) { - struct packagelist_data *pkgl_dat; - struct task_struct *packagelist_thread; + struct package_appid *package_appid; - pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); - if (!pkgl_dat) { - printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL); + if (!package_appid) return ERR_PTR(-ENOMEM); - } - mutex_init(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); - hash_init(pkgl_dat->appid_with_rw); - pkgl_dat->write_gid = write_gid; + config_item_init_type_name(&package_appid->item, name, + &package_appid_type); + + package_appid->add_pid = 0; - packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld"); - if (IS_ERR(packagelist_thread)) { - printk(KERN_ERR "sdcardfs: creating kthread failed\n"); - kfree(pkgl_dat); - return packagelist_thread; - } - pkgl_dat->thread_id = packagelist_thread; + return &package_appid->item; +} - printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n", - (int)pkgl_dat->thread_id->pid); +static ssize_t packages_attr_show(struct config_item *item, + char *page) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + int i; + int count = 0; + mutex_lock(&pkgl_data_all->hashtable_lock); + hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) + count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + mutex_unlock(&pkgl_data_all->hashtable_lock); - return (void *)pkgl_dat; + + return count; } -void packagelist_destroy(void *pkgl_id) +static struct configfs_attribute sdcardfs_packages_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "packages_gid.list", + .ca_mode = S_IRUGO, + .show = packages_attr_show, +}; + +static struct configfs_attribute *sdcardfs_packages_attrs[] = { + &sdcardfs_packages_attr_description, + NULL, +}; + +static void sdcardfs_packages_release(struct config_item *item) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; - pid_t pkgl_pid = pkgl_dat->thread_id->pid; - force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id); - kthread_stop(pkgl_dat->thread_id); - remove_all_hashentrys(pkgl_dat); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid); - kfree(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed something?\n"); + kfree(to_sdcardfs_packages(item)); +} + +static struct configfs_item_operations sdcardfs_packages_item_ops = { + .release = sdcardfs_packages_release, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations sdcardfs_packages_group_ops = { + .make_item = sdcardfs_packages_make_item, +}; + +static struct config_item_type sdcardfs_packages_type = { + .ct_item_ops = &sdcardfs_packages_item_ops, + .ct_group_ops = &sdcardfs_packages_group_ops, + .ct_attrs = sdcardfs_packages_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem sdcardfs_packages_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "sdcardfs", + .ci_type = &sdcardfs_packages_type, + }, + }, +}; + +static int __init configfs_sdcardfs_init(void) +{ + int ret; + struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + ret = configfs_register_subsystem(subsys); + if (ret) { + printk(KERN_ERR "Error %d while registering subsystem %s\n", + ret, + subsys->su_group.cg_item.ci_namebuf); + } + return ret; +} + +static void __exit configfs_sdcardfs_exit(void) +{ + configfs_unregister_subsystem(&sdcardfs_packages_subsys); } int packagelist_init(void) @@ -445,13 +423,15 @@ int packagelist_init(void) return -ENOMEM; } + pkgl_data_all = packagelist_create(); + configfs_sdcardfs_init(); return 0; } void packagelist_exit(void) { + configfs_sdcardfs_exit(); + packagelist_destroy(pkgl_data_all); if (hashtable_entry_cachep) kmem_cache_destroy(hashtable_entry_cachep); } - - diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 51f6c7912584..1b85f4e70324 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "multiuser.h" /* the file system name */ @@ -70,10 +71,11 @@ #define fix_derived_permission(x) \ do { \ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ - (x)->i_gid = make_kgid(&init_user_ns, SDCARDFS_I(x)->d_gid); \ - (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ + (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\ } while (0) + /* OVERRIDE_CRED() and REVERT_CRED() * OVERRID_CRED() * backup original task->cred @@ -99,35 +101,28 @@ (int)current->cred->fsuid, \ (int)current->cred->fsgid); -/* Android 4.4 support */ +/* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions * are derived for children nodes. */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_LEGACY_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/user" */ - PERM_ANDROID_USER, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, } perm_t; -/* Permissions structure to derive */ -typedef enum { - DERIVE_NONE, - DERIVE_LEGACY, - DERIVE_UNIFIED, -} derive_t; - typedef enum { LOWER_FS_EXT4, LOWER_FS_FAT, @@ -161,9 +156,9 @@ extern void free_dentry_private_data(struct dentry *dentry); extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); extern struct inode *sdcardfs_iget(struct super_block *sb, - struct inode *lower_inode); + struct inode *lower_inode, userid_t id); extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path); + struct path *lower_path, userid_t id); /* file private data */ struct sdcardfs_file_info { @@ -174,18 +169,16 @@ struct sdcardfs_file_info { /* sdcardfs inode data in memory */ struct sdcardfs_inode_info { struct inode *lower_inode; - /* state derived based on current position in hierachy - * caution: d_mode does not include file types - */ + /* state derived based on current position in hierachy */ perm_t perm; userid_t userid; uid_t d_uid; - gid_t d_gid; - mode_t d_mode; + bool under_android; struct inode vfs_inode; }; + /* sdcardfs dentry data in memory */ struct sdcardfs_dentry_info { spinlock_t lock; /* protects lower_path */ @@ -196,15 +189,17 @@ struct sdcardfs_dentry_info { struct sdcardfs_mount_options { uid_t fs_low_uid; gid_t fs_low_gid; - gid_t write_gid; - int split_perms; - derive_t derive; + userid_t fs_user_id; + gid_t gid; lower_fs_t lower_fs; + mode_t mask; + bool multiuser; unsigned int reserved_mb; }; /* sdcardfs super-block data in memory */ struct sdcardfs_sb_info { + struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added * to sdcardfs_mount_options (Android 4.4 support) */ @@ -213,6 +208,7 @@ struct sdcardfs_sb_info { char *obbpath_s; struct path obbpath; void *pkgl_id; + struct list_head list; }; /* @@ -331,6 +327,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +static inline int get_gid(struct sdcardfs_inode_info *info) { + struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + if (sb_info->options.gid == AID_SDCARD_RW) { + /* As an optimization, certain trusted system components only run + * as owner but operate across all users. Since we're now handing + * out the sdcard_rw GID only to trusted apps, we're okay relaxing + * the user boundary enforcement for the default view. The UIDs + * assigned to app directories are still multiuser aware. */ + return AID_SDCARD_RW; + } else { + return multiuser_get_uid(info->userid, sb_info->options.gid); + } +} +static inline int get_mode(struct sdcardfs_inode_info *info) { + int owner_mode; + int filtered_mode; + struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + int visible_mode = 0775 & ~sb_info->options.mask; + + if (info->perm == PERM_PRE_ROOT) { + /* Top of multi-user view should always be visible to ensure + * secondary users can traverse inside. */ + visible_mode = 0711; + } else if (info->under_android) { + /* Block "other" access to Android directories, since only apps + * belonging to a specific user should be in there; we still + * leave +x open for the default view. */ + if (sb_info->options.gid == AID_SDCARD_RW) { + visible_mode = visible_mode & ~0006; + } else { + visible_mode = visible_mode & ~0007; + } + } + owner_mode = info->lower_inode->i_mode & 0700; + filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + return filtered_mode; +} + static inline int has_graft_path(const struct dentry *dent) { int ret = 0; @@ -364,22 +398,24 @@ static inline void sdcardfs_put_real_lower(const struct dentry *dent, sdcardfs_put_lower_path(dent, real_lower); } +extern struct mutex sdcardfs_super_list_lock; +extern struct list_head sdcardfs_super_list; + /* for packagelist.c */ -extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive); extern appid_t get_appid(void *pkgl_id, const char *app_name); -extern int check_caller_access_to_name(struct inode *parent_node, const char* name, - derive_t derive, int w_ok, int has_rw); +extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); -extern void * packagelist_create(gid_t write_gid); -extern void packagelist_destroy(void *pkgl_id); extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, gid_t gid, mode_t mode); + userid_t userid, uid_t uid, bool under_android); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void update_derived_permission(struct dentry *dentry); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); +extern void get_derive_permissions_recursive(struct dentry *parent); + +extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); @@ -483,4 +519,18 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d return 1; } +/* Copies attrs and maintains sdcardfs managed attrs */ +static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src) +{ + dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest)); + dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); + dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest))); + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; + set_nlink(dest, src->i_nlink); +} #endif /* not _SDCARDFS_H_ */ diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h deleted file mode 100644 index 50ab25aa0bc4..000000000000 --- a/fs/sdcardfs/strtok.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * fs/sdcardfs/strtok.h - * - * Copyright (c) 2013 Samsung Electronics Co. Ltd - * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, - * Sunghwan Yun, Sungjong Seo - * - * This program has been developed as a stackable file system based on - * the WrapFS which written by - * - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 The Research Foundation of SUNY - * - * This file is dual licensed. It may be redistributed and/or modified - * under the terms of the Apache 2.0 License OR version 2 of the GNU - * General Public License. - */ - -static char * -strtok_r(char *s, const char *delim, char **last) -{ - char *spanp; - int c, sc; - char *tok; - - - /* if (s == NULL && (s = *last) == NULL) - return NULL; */ - if (s == NULL) { - s = *last; - if (s == NULL) - return NULL; - } - - /* - * Skip (span) leading delimiters (s += strspn(s, delim), sort of). - */ -cont: - c = *s++; - for (spanp = (char *)delim; (sc = *spanp++) != 0;) { - if (c == sc) - goto cont; - } - - if (c == 0) { /* no non-delimiter characters */ - *last = NULL; - return NULL; - } - tok = s - 1; - - /* - * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). - * Note that delim must have one NUL; we stop if we see that, too. - */ - for (;;) { - c = *s++; - spanp = (char *)delim; - do { - sc = *spanp++; - if (sc == c) { - if (c == 0) - s = NULL; - else - s[-1] = 0; - *last = s; - return tok; - } - } while (sc != 0); - } - - /* NOTREACHED */ -} - diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index f153ce1b8cf3..1d6490128c99 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -46,9 +46,6 @@ static void sdcardfs_put_super(struct super_block *sb) sdcardfs_set_lower_super(sb, NULL); atomic_dec(&s->s_active); - if(spd->pkgl_id) - packagelist_destroy(spd->pkgl_id); - kfree(spd); sb->s_fs_info = NULL; } @@ -203,12 +200,8 @@ static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) if (opts->fs_low_gid != 0) seq_printf(m, ",gid=%u", opts->fs_low_gid); - if (opts->derive == DERIVE_NONE) - seq_printf(m, ",derive=none"); - else if (opts->derive == DERIVE_LEGACY) - seq_printf(m, ",derive=legacy"); - else if (opts->derive == DERIVE_UNIFIED) - seq_printf(m, ",derive=unified"); + if (opts->multiuser) + seq_printf(m, ",multiuser"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From a43aa502c608df462bee48f97021182f9da2d285 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 11 Feb 2016 16:53:36 -0800 Subject: [PATCH 0533/1276] ANDROID: sdcardfs: Add support for d_canonical_path Change-Id: I5d6f0e71b8ca99aec4b0894412f1dfd1cfe12add Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index dbbcfd091fc7..971928ab6c21 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -172,10 +172,15 @@ static int sdcardfs_cmp_ci(const struct dentry *parent, return 1; } +static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { + sdcardfs_get_real_lower(path->dentry, actual_path); +} + const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, + .d_canonical_path = sdcardfs_canonical_path, }; From d8fefbf85af24f064afebb027927d0711ae8f7a0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 23 Mar 2016 16:39:30 -0700 Subject: [PATCH 0534/1276] ANDROID: sdcardfs: remove effectless config option CONFIG_SDCARD_FS_CI_SEARCH only guards a define for LOOKUP_CASE_INSENSITIVE, which is never used in the kernel. Remove both, along with the option matching that supports it. Change-Id: I363a8f31de8ee7a7a934d75300cc9ba8176e2edf Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/Kconfig | 5 ----- fs/sdcardfs/lookup.c | 7 +------ fs/sdcardfs/main.c | 15 --------------- fs/sdcardfs/sdcardfs.h | 6 ------ 4 files changed, 1 insertion(+), 32 deletions(-) diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index ab25f88ebb37..a1c103316ac7 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -11,8 +11,3 @@ config SDCARD_FS_FADV_NOACTIVE default y help Sdcardfs supports fadvise noactive mode. - -config SDCARD_FS_CI_SEARCH - tristate "sdcardfs case-insensitive search support" - depends on SDCARD_FS - default y diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f80abcb6b467..a01b06a514fd 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -238,13 +238,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ - if (sbi->options.lower_fs == LOWER_FS_EXT4) { - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, - LOOKUP_CASE_INSENSITIVE, &lower_path); - } else if (sbi->options.lower_fs == LOWER_FS_FAT) { - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_path); - } /* no error: handle positive dentries */ if (!err) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 80aa355d801e..fa11a0458b84 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -41,7 +41,6 @@ static const match_table_t sdcardfs_tokens = { {Opt_fsgid, "fsgid=%u"}, {Opt_gid, "gid=%u"}, {Opt_debug, "debug"}, - {Opt_lower_fs, "lower_fs=%s"}, {Opt_mask, "mask=%u"}, {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, @@ -64,8 +63,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->multiuser = false; opts->fs_user_id = 0; opts->gid = 0; - /* by default, we use LOWER_FS_EXT4 as lower fs type */ - opts->lower_fs = LOWER_FS_EXT4; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -113,18 +110,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_multiuser: opts->multiuser = true; break; - case Opt_lower_fs: - string_option = match_strdup(&args[0]); - if (!strcmp("ext4", string_option)) { - opts->lower_fs = LOWER_FS_EXT4; - } else if (!strcmp("fat", string_option)) { - opts->lower_fs = LOWER_FS_FAT; - } else { - kfree(string_option); - goto invalid_option; - } - kfree(string_option); - break; case Opt_reserved_mb: if (match_int(&args[0], &option)) return 0; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 1b85f4e70324..f111f898b630 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -123,11 +123,6 @@ typedef enum { PERM_ANDROID_MEDIA, } perm_t; -typedef enum { - LOWER_FS_EXT4, - LOWER_FS_FAT, -} lower_fs_t; - struct sdcardfs_sb_info; struct sdcardfs_mount_options; @@ -191,7 +186,6 @@ struct sdcardfs_mount_options { gid_t fs_low_gid; userid_t fs_user_id; gid_t gid; - lower_fs_t lower_fs; mode_t mask; bool multiuser; unsigned int reserved_mb; From 4166774093363c049ce6c60cc8fe8930f041b906 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 28 Mar 2016 15:00:20 -0700 Subject: [PATCH 0535/1276] ANDROID: sdcardfs: Remove unused code Change-Id: Ie97cba27ce44818ac56cfe40954f164ad44eccf6 --- fs/sdcardfs/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index fa11a0458b84..a6522286d731 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -54,7 +54,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, char *p; substring_t args[MAX_OPT_ARGS]; int option; - char *string_option; /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; @@ -117,7 +116,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: -invalid_option: if (!silent) { printk( KERN_ERR "Unrecognized mount option \"%s\" " "or missing value", p); From 8e49a570d3511ebd370236c883454481f8cddff2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 28 Mar 2016 16:00:34 -0700 Subject: [PATCH 0536/1276] ANDROID: sdcardfs: remove unneeded __init and __exit Change-Id: I2a2d45d52f891332174c3000e8681c5167c1564f --- fs/sdcardfs/packagelist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index ba3478d94107..10f0d6be718b 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -392,7 +392,7 @@ static struct configfs_subsystem sdcardfs_packages_subsys = { }, }; -static int __init configfs_sdcardfs_init(void) +static int configfs_sdcardfs_init(void) { int ret; struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; @@ -408,7 +408,7 @@ static int __init configfs_sdcardfs_init(void) return ret; } -static void __exit configfs_sdcardfs_exit(void) +static void configfs_sdcardfs_exit(void) { configfs_unregister_subsystem(&sdcardfs_packages_subsys); } From 40ee0e93f1d745afb6ca14c37f17e3f3a3015e0a Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 8 Jul 2016 14:15:14 -0700 Subject: [PATCH 0537/1276] ANDROID: sdcardfs: Truncate packages_gid.list on overflow packages_gid.list was improperly returning the wrong count. Use scnprintf instead, and inform the user that the list was truncated if it is. Bug: 30013843 Change-Id: Ida2b2ef7cd86dd87300bfb4c2cdb6bfe2ee1650d Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/packagelist.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 10f0d6be718b..9c3340528eee 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -335,13 +335,20 @@ static ssize_t packages_attr_show(struct config_item *item, struct hashtable_entry *hash_cur; struct hlist_node *h_t; int i; - int count = 0; + int count = 0, written = 0; + char errormsg[] = "\n"; + mutex_lock(&pkgl_data_all->hashtable_lock); - hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) - count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) { + written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + if (count + written == PAGE_SIZE - sizeof(errormsg)) { + count += scnprintf(page + count, PAGE_SIZE - count, errormsg); + break; + } + count += written; + } mutex_unlock(&pkgl_data_all->hashtable_lock); - return count; } From b1d9602aa3fecfdf645b446b5d0880c40601b5be Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jun 2016 10:28:49 -0700 Subject: [PATCH 0538/1276] ANDROID: sdcardfs: fix itnull.cocci warnings List_for_each_entry has the property that the first argument is always bound to a real list element, never NULL, so testing dentry is not needed. Generated by: scripts/coccinelle/iterators/itnull.cocci Cc: Daniel Rosenberg Signed-off-by: Julia Lawall Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck --- fs/sdcardfs/derived_perm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 128b3e56851f..41e0e11b3c35 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -112,7 +112,7 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) void get_derive_permissions_recursive(struct dentry *parent) { struct dentry *dentry; list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry && dentry->d_inode) { + if (dentry->d_inode) { mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); From 60a177f5a167ba99f75d8a0ca4bbe8e41035dc96 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 13 Apr 2016 16:38:34 -0700 Subject: [PATCH 0539/1276] ANDROID: sdcardfs: override umask on mkdir and create The mode on files created on the lower fs should not be affected by the umask of the calling task's fs_struct. Instead, we create a copy and modify it as needed. This also lets us avoid the string shenanigans around .nomedia files. Bug: 27992761 Change-Id: Ia3a6e56c24c6e19b3b01c1827e46403bb71c2f4c Signed-off-by: Daniel Rosenberg --- fs/fs_struct.c | 1 + fs/sdcardfs/inode.c | 70 ++++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/fs/fs_struct.c b/fs/fs_struct.c index be0250788b73..47b0ec5d5006 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -128,6 +128,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) } return fs; } +EXPORT_SYMBOL_GPL(copy_fs_struct); int unshare_fs_struct(void) { diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2528da0d3ae1..4b140ba86955 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -19,6 +19,7 @@ */ #include "sdcardfs.h" +#include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) @@ -56,6 +57,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" @@ -74,6 +77,16 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; + + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -85,6 +98,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -245,11 +261,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); - char *page_buf; - char *nomedia_dir_name; - char *nomedia_fullpath; - int fullpath_namelen; int touch_err = 0; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" @@ -276,6 +290,16 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; + + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); if (err) @@ -316,42 +340,18 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { - - page_buf = (char *)__get_free_page(GFP_KERNEL); - if (!page_buf) { - printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); - goto out; - } - - nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); - if (IS_ERR(nomedia_dir_name)) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); - goto out; - } - - fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; - fullpath_namelen += strlen("/.nomedia"); - nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); - if (!nomedia_fullpath) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); - goto out; - } - - strcpy(nomedia_fullpath, nomedia_dir_name); - free_page((unsigned long)page_buf); - strcat(nomedia_fullpath, "/.nomedia"); - touch_err = touch(nomedia_fullpath, 0664); + set_fs_pwd(current->fs, &lower_path); + touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", - nomedia_fullpath, touch_err); - kfree(nomedia_fullpath); + printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, touch_err); goto out; } - kfree(nomedia_fullpath); } out: + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: From efb3d26952031459dae8e9c3cb62d5c971d44bb8 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 27 Apr 2016 15:31:29 -0700 Subject: [PATCH 0540/1276] ANDROID: sdcardfs: Check for other cases on path lookup This fixes a bug where the first lookup of a file or folder created under a different view would not be case insensitive. It will now search through for a case insensitive match if the initial lookup fails. Bug:28024488 Change-Id: I4ff9ce297b9f2f9864b47540e740fd491c545229 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/lookup.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a01b06a514fd..a127d05b5054 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -240,6 +240,28 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* Use vfs_path_lookup to check if the dentry exists or not */ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_path); + /* check for other cases */ + if (err == -ENOENT) { + struct dentry *child; + struct dentry *match = NULL; + spin_lock(&lower_dir_dentry->d_lock); + list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { + if (child && d_inode(child)) { + if (strcasecmp(child->d_name.name, name)==0) { + match = dget(child); + break; + } + } + } + spin_unlock(&lower_dir_dentry->d_lock); + if (match) { + err = vfs_path_lookup(lower_dir_dentry, + lower_dir_mnt, + match->d_name.name, 0, + &lower_path); + dput(match); + } + } /* no error: handle positive dentries */ if (!err) { From 0da87f63666fbbc510b3da78c4a1570f3c2f69ad Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 16 Aug 2016 15:19:26 -0700 Subject: [PATCH 0541/1276] ANDROID: sdcardfs: Fix locking for permission fix up Iterating over d_subdirs requires taking d_lock. Removed several unneeded locks. Change-Id: I5b1588e54c7e6ee19b756d6705171c7f829e2650 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 6 ++---- fs/sdcardfs/inode.c | 2 -- fs/sdcardfs/lookup.c | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 41e0e11b3c35..bfe402b8cf32 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -111,15 +111,15 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) void get_derive_permissions_recursive(struct dentry *parent) { struct dentry *dentry; + spin_lock(&parent->d_lock); list_for_each_entry(dentry, &parent->d_subdirs, d_child) { if (dentry->d_inode) { - mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); get_derive_permissions_recursive(dentry); - mutex_unlock(&dentry->d_inode->i_mutex); } } + spin_unlock(&parent->d_lock); } /* main function for updating derived permission */ @@ -135,7 +135,6 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - mutex_lock(&dentry->d_inode->i_mutex); if(IS_ROOT(dentry)) { //setup_default_pre_root_state(dentry->d_inode); } else { @@ -146,7 +145,6 @@ inline void update_derived_permission_lock(struct dentry *dentry) } } fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); } int need_graft_path(struct dentry *dentry) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4b140ba86955..1a23c0cc8f58 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -513,11 +513,9 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ - mutex_lock(&d_inode(old_dentry)->i_mutex); get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); fix_derived_permission(d_inode(old_dentry)); get_derive_permissions_recursive(old_dentry); - mutex_unlock(&d_inode(old_dentry)->i_mutex); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a127d05b5054..c74a7d1bc18e 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -387,11 +387,9 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (dentry->d_inode) { fsstack_copy_attr_times(dentry->d_inode, sdcardfs_lower_inode(dentry->d_inode)); - /* get drived permission */ - mutex_lock(&dentry->d_inode->i_mutex); + /* get derived permission */ get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, From 75b93060655e899e0216efb2aab8b343d27bf7e6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 10 May 2016 13:42:43 -0700 Subject: [PATCH 0542/1276] ANDROID: sdcardfs: Switch package list to RCU Switched the package id hashmap to use RCU. Change-Id: I9fdcab279009005bf28536247d11e13babab0b93 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 3 +- fs/sdcardfs/packagelist.c | 200 +++++++++++++++++-------------------- fs/sdcardfs/sdcardfs.h | 2 +- 3 files changed, 93 insertions(+), 112 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index bfe402b8cf32..2a75ad873a7c 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -47,7 +47,6 @@ void setup_derived_state(struct inode *inode, perm_t perm, /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); appid_t appid; @@ -96,7 +95,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: - appid = get_appid(sbi->pkgl_id, newdentry->d_name.name); + appid = get_appid(newdentry->d_name.name); if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 9c3340528eee..f5a49c513568 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -29,26 +29,13 @@ #include -#define STRING_BUF_SIZE (512) - struct hashtable_entry { struct hlist_node hlist; - void *key; - unsigned int value; -}; - -struct sb_list { - struct super_block *sb; - struct list_head list; + const char *key; + atomic_t value; }; -struct packagelist_data { - DECLARE_HASHTABLE(package_to_appid,8); - struct mutex hashtable_lock; - -}; - -static struct packagelist_data *pkgl_data_all; +static DEFINE_HASHTABLE(package_to_appid, 8); static struct kmem_cache *hashtable_entry_cachep; @@ -64,22 +51,21 @@ static unsigned int str_hash(const char *key) { return h; } -appid_t get_appid(void *pkgl_id, const char *app_name) +appid_t get_appid(const char *app_name) { - struct packagelist_data *pkgl_dat = pkgl_data_all; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(app_name); appid_t ret_id; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(app_name, hash_cur->key)) { - ret_id = (appid_t)hash_cur->value; - mutex_unlock(&pkgl_dat->hashtable_lock); + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); return ret_id; } } - mutex_unlock(&pkgl_dat->hashtable_lock); + rcu_read_unlock(); return 0; } @@ -120,116 +106,118 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) +static struct hashtable_entry *alloc_packagelist_entry(const char *key, + appid_t value) +{ + struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, + GFP_KERNEL); + if (!ret) + return NULL; + + ret->key = kstrdup(key, GFP_KERNEL); + if (!ret->key) { + kmem_cache_free(hashtable_entry_cachep, ret); + return NULL; + } + + atomic_set(&ret->value, value); + return ret; +} + +static int insert_packagelist_entry_locked(const char *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { - hash_cur->value = value; + atomic_set(&hash_cur->value, value); return 0; } } - new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + new_entry = alloc_packagelist_entry(key, value); if (!new_entry) return -ENOMEM; - new_entry->key = kstrdup(key, GFP_KERNEL); - new_entry->value = value; - hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash); + hash_add_rcu(package_to_appid, &new_entry->hlist, hash); return 0; } static void fixup_perms(struct super_block *sb) { if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - mutex_lock(&sb->s_root->d_inode->i_mutex); get_derive_permissions_recursive(sb->s_root); - mutex_unlock(&sb->s_root->d_inode->i_mutex); } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) { - int ret; +static void fixup_all_perms(void) +{ struct sdcardfs_sb_info *sbinfo; - mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - ret = insert_str_to_int_lock(pkgl_dat, key, value); - mutex_unlock(&pkgl_dat->hashtable_lock); - - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) + if (sbinfo) fixup_perms(sbinfo->sb); - } - } +} + +static int insert_packagelist_entry(const char *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_packagelist_entry_locked(key, value); + if (!err) + fixup_all_perms(); mutex_unlock(&sdcardfs_super_list_lock); - return ret; + + return err; } -static void remove_str_to_int_lock(struct hashtable_entry *h_entry) { - kfree(h_entry->key); - hash_del(&h_entry->hlist); - kmem_cache_free(hashtable_entry_cachep, h_entry); +static void free_packagelist_entry(struct hashtable_entry *entry) +{ + kfree(entry->key); + hash_del_rcu(&entry->hlist); + kmem_cache_free(hashtable_entry_cachep, entry); } -static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key) +static void remove_packagelist_entry_locked(const char *key) { - struct sdcardfs_sb_info *sbinfo; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(key); - mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { - remove_str_to_int_lock(hash_cur); - break; - } - } - mutex_unlock(&pkgl_dat->hashtable_lock); - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { - fixup_perms(sbinfo->sb); + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_packagelist_entry(hash_cur); + return; } } +} + +static void remove_packagelist_entry(const char *key) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_packagelist_entry_locked(key); + fixup_all_perms(); mutex_unlock(&sdcardfs_super_list_lock); return; } -static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) +static void packagelist_destroy(void) { struct hashtable_entry *hash_cur; struct hlist_node *h_t; + HLIST_HEAD(free_list); int i; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) - remove_str_to_int_lock(hash_cur); - mutex_unlock(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); -} - -static struct packagelist_data * packagelist_create(void) -{ - struct packagelist_data *pkgl_dat; + mutex_lock(&sdcardfs_super_list_lock); + hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->hlist, &free_list); - pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); - if (!pkgl_dat) { - printk(KERN_ERR "sdcardfs: Failed to create hash\n"); - return ERR_PTR(-ENOMEM); } - - mutex_init(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); - - return pkgl_dat; -} - -static void packagelist_destroy(struct packagelist_data *pkgl_dat) -{ - remove_all_hashentrys(pkgl_dat); + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist) + free_packagelist_entry(hash_cur); + mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); - kfree(pkgl_dat); } struct package_appid { @@ -245,26 +233,21 @@ static inline struct package_appid *to_package_appid(struct config_item *item) static ssize_t package_appid_attr_show(struct config_item *item, char *page) { - ssize_t count; - count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name)); - return count; + return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(item->ci_name)); } static ssize_t package_appid_attr_store(struct config_item *item, const char *page, size_t count) { struct package_appid *package_appid = to_package_appid(item); - unsigned long tmp; - char *p = (char *) page; + unsigned int tmp; int ret; - tmp = simple_strtoul(p, &p, 10); - if (!p || (*p && (*p != '\n'))) - return -EINVAL; + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; - if (tmp > INT_MAX) - return -ERANGE; - ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp); + ret = insert_packagelist_entry(item->ci_name, tmp); package_appid->add_pid = tmp; if (ret) return ret; @@ -289,7 +272,7 @@ static void package_appid_release(struct config_item *item) { printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); /* item->ci_name is freed already, so we rely on the dentry */ - remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name); + remove_packagelist_entry(item->ci_dentry->d_name.name); kfree(to_package_appid(item)); } @@ -333,21 +316,21 @@ static ssize_t packages_attr_show(struct config_item *item, char *page) { struct hashtable_entry *hash_cur; - struct hlist_node *h_t; int i; int count = 0, written = 0; - char errormsg[] = "\n"; + const char errormsg[] = "\n"; - mutex_lock(&pkgl_data_all->hashtable_lock); - hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) { - written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + rcu_read_lock(); + hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", + (const char *)hash_cur->key, atomic_read(&hash_cur->value)); if (count + written == PAGE_SIZE - sizeof(errormsg)) { count += scnprintf(page + count, PAGE_SIZE - count, errormsg); break; } count += written; } - mutex_unlock(&pkgl_data_all->hashtable_lock); + rcu_read_unlock(); return count; } @@ -430,7 +413,6 @@ int packagelist_init(void) return -ENOMEM; } - pkgl_data_all = packagelist_create(); configfs_sdcardfs_init(); return 0; } @@ -438,7 +420,7 @@ int packagelist_init(void) void packagelist_exit(void) { configfs_sdcardfs_exit(); - packagelist_destroy(pkgl_data_all); + packagelist_destroy(); if (hashtable_entry_cachep) kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index f111f898b630..75284f339ae0 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -396,7 +396,7 @@ extern struct mutex sdcardfs_super_list_lock; extern struct list_head sdcardfs_super_list; /* for packagelist.c */ -extern appid_t get_appid(void *pkgl_id, const char *app_name); +extern appid_t get_appid(const char *app_name); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); From 657b0a00f49799b3cd4d7fb7bbec16cb8f9f3577 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 18 May 2016 16:57:10 -0700 Subject: [PATCH 0543/1276] ANDROID: sdcardfs: Added top to sdcardfs_inode_info Adding packages to the package list and moving files takes a large amount of locks, and is currently a heavy operation. This adds a 'top' field to the inode_info, which points to the inode for the top most directory whose owner you would like to match. On permission checks and get_attr, we look up the owner based on the information at top. When we change a package mapping, we need only modify the information in the corresponding top inode_info's. When renaming, we must ensure top is set correctly in all children. This happens when an app specific folder gets moved outside of the folder for that app. Change-Id: Ib749c60b568e9a45a46f8ceed985c1338246ec6c Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 73 ++++++++++++++++++++++++++++++++++---- fs/sdcardfs/inode.c | 45 +++++++++++++++++++---- fs/sdcardfs/main.c | 4 +-- fs/sdcardfs/packagelist.c | 12 +++---- fs/sdcardfs/sdcardfs.h | 40 ++++++++++++++++++--- fs/sdcardfs/super.c | 1 + 6 files changed, 149 insertions(+), 26 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 2a75ad873a7c..89daf69efbaa 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -30,11 +30,12 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->under_android = pi->under_android; + set_top(ci, pi->top); } /* helper function for derived state */ -void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android) +void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -42,6 +43,7 @@ void setup_derived_state(struct inode *inode, perm_t perm, info->userid = userid; info->d_uid = uid; info->under_android = under_android; + set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ @@ -70,6 +72,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); + set_top(info, &info->vfs_inode); break; case PERM_ROOT: /* Assume masked off by default. */ @@ -77,19 +80,23 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->under_android = true; + set_top(info, &info->vfs_inode); } break; case PERM_ANDROID: if (!strcasecmp(newdentry->d_name.name, "data")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; + set_top(info, &info->vfs_inode); } else if (!strcasecmp(newdentry->d_name.name, "obb")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; + set_top(info, &info->vfs_inode); /* Single OBB directory is always shared */ } else if (!strcasecmp(newdentry->d_name.name, "media")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); } break; case PERM_ANDROID_DATA: @@ -99,6 +106,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } + set_top(info, &info->vfs_inode); break; } } @@ -108,14 +116,65 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, dentry); } -void get_derive_permissions_recursive(struct dentry *parent) { +static int descendant_may_need_fixup(perm_t perm) { + if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID) + return 1; + return 0; +} + +static int needs_fixup(perm_t perm) { + if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB + || perm == PERM_ANDROID_MEDIA) + return 1; + return 0; +} + +void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) { + struct dentry *child; + struct sdcardfs_inode_info *info; + if (!dget(dentry)) + return; + if (!dentry->d_inode) { + dput(dentry); + return; + } + info = SDCARDFS_I(d_inode(dentry)); + + if (needs_fixup(info->perm)) { + mutex_lock(&d_inode(dentry)->i_mutex); + child = lookup_one_len(name, dentry, len); + mutex_unlock(&d_inode(dentry)->i_mutex); + if (!IS_ERR(child)) { + if (child->d_inode) { + get_derived_permission(dentry, child); + fix_derived_permission(d_inode(child)); + } + dput(child); + } + } else if (descendant_may_need_fixup(info->perm)) { + mutex_lock(&d_inode(dentry)->i_mutex); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + fixup_perms_recursive(child, name, len); + } + mutex_unlock(&d_inode(dentry)->i_mutex); + } + dput(dentry); +} + +void fixup_top_recursive(struct dentry *parent) { struct dentry *dentry; + struct sdcardfs_inode_info *info; + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); spin_lock(&parent->d_lock); list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry->d_inode) { - get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); - get_derive_permissions_recursive(dentry); + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + get_derived_permission(parent, dentry); + fix_derived_permission(d_inode(dentry)); + fixup_top_recursive(dentry); + } } } spin_unlock(&parent->d_lock); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1a23c0cc8f58..67bcee2c379a 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -515,7 +515,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, * we pass along new_dentry for the name.*/ get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); fix_derived_permission(d_inode(old_dentry)); - get_derive_permissions_recursive(old_dentry); + fixup_top_recursive(old_dentry); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -587,6 +587,16 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission(struct inode *inode, int mask) { int err; + struct inode *top = grab_top(SDCARDFS_I(inode)); + + if (!top) + return -EINVAL; + /* Ensure owner is up to date */ + if (!uid_eq(inode->i_uid, top->i_uid)) { + SDCARDFS_I(inode)->d_uid = SDCARDFS_I(top)->d_uid; + fix_derived_permission(inode); + } + release_top(SDCARDFS_I(inode)); /* * Permission check on sdcardfs inode. @@ -725,6 +735,30 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) return err; } +static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) +{ + struct sdcardfs_inode_info *info = SDCARDFS_I(inode); + struct inode *top = grab_top(info); + if (!top) + return -EINVAL; + + stat->dev = inode->i_sb->s_dev; + stat->ino = inode->i_ino; + stat->mode = (inode->i_mode & S_IFMT) | get_mode(SDCARDFS_I(top)); + stat->nlink = inode->i_nlink; + stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(top))); + stat->rdev = inode->i_rdev; + stat->size = i_size_read(inode); + stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode->i_ctime; + stat->blksize = (1 << inode->i_blkbits); + stat->blocks = inode->i_blocks; + release_top(info); + return 0; +} + static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -733,6 +767,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *lower_inode; struct path lower_path; struct dentry *parent; + int err; parent = dget_parent(dentry); if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { @@ -750,14 +785,12 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); - sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - - generic_fillattr(inode, stat); + err = sdcardfs_fillattr(inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); - return 0; + return err; } const struct inode_operations sdcardfs_symlink_iops = { @@ -775,9 +808,7 @@ const struct inode_operations sdcardfs_symlink_iops = { const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, -#if 0 .permission = sdcardfs_permission, -#endif .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index a6522286d731..6d526bf3d956 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -268,13 +268,13 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); if(sb_info->options.multiuser) { - setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); /*err = prepare_dir(sb_info->obbpath_s, sb_info->options.fs_low_uid, sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); + setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false, sb->s_root->d_inode); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index f5a49c513568..03776fa5f26c 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -143,18 +143,18 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value) return 0; } -static void fixup_perms(struct super_block *sb) { +static void fixup_perms(struct super_block *sb, const char *key) { if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - get_derive_permissions_recursive(sb->s_root); + fixup_perms_recursive(sb->s_root, key, strlen(key)); } } -static void fixup_all_perms(void) +static void fixup_all_perms(const char *key) { struct sdcardfs_sb_info *sbinfo; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) if (sbinfo) - fixup_perms(sbinfo->sb); + fixup_perms(sbinfo->sb, key); } static int insert_packagelist_entry(const char *key, appid_t value) @@ -164,7 +164,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) mutex_lock(&sdcardfs_super_list_lock); err = insert_packagelist_entry_locked(key, value); if (!err) - fixup_all_perms(); + fixup_all_perms(key); mutex_unlock(&sdcardfs_super_list_lock); return err; @@ -196,7 +196,7 @@ static void remove_packagelist_entry(const char *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); - fixup_all_perms(); + fixup_all_perms(key); mutex_unlock(&sdcardfs_super_list_lock); return; } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 75284f339ae0..cfda98d257b6 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -169,6 +169,8 @@ struct sdcardfs_inode_info { userid_t userid; uid_t d_uid; bool under_android; + /* top folder for ownership */ + struct inode *top; struct inode vfs_inode; }; @@ -321,6 +323,35 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +/* grab a refererence if we aren't linking to ourself */ +static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +{ + struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); + if (info->top && info->top != &info->vfs_inode) { + old_top = info->top; + } + if (top != &info->vfs_inode) + igrab(top); + info->top = top; + iput(old_top); +} + +static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +{ + struct inode *top = info->top; + if (top) { + return igrab(top); + } else { + return NULL; + } +} + +static inline void release_top(struct sdcardfs_inode_info *info) +{ + iput(info->top); +} + static inline int get_gid(struct sdcardfs_inode_info *info) { struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); if (sb_info->options.gid == AID_SDCARD_RW) { @@ -337,7 +368,7 @@ static inline int get_gid(struct sdcardfs_inode_info *info) { static inline int get_mode(struct sdcardfs_inode_info *info) { int owner_mode; int filtered_mode; - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + struct sdcardfs_sb_info * sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); int visible_mode = 0775 & ~sb_info->options.mask; if (info->perm == PERM_PRE_ROOT) { @@ -403,11 +434,12 @@ extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ -extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android); +extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); -extern void get_derive_permissions_recursive(struct dentry *parent); +extern void fixup_top_recursive(struct dentry *parent); +extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len); extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 1d6490128c99..0a465395aab7 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -126,6 +126,7 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); + set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } From 5008d91cba251557facaec6e9a80360efc48ab6f Mon Sep 17 00:00:00 2001 From: alvin_liang Date: Mon, 19 Sep 2016 16:59:12 +0800 Subject: [PATCH 0544/1276] ANDROID: sdcardfs: fix external storage exporting incorrect uid Symptom: App cannot write into per-app folder Root Cause: sdcardfs exports incorrect uid Solution: fix uid Project: All Note: Test done by RD: passed Change-Id: Iff64f6f40ba4c679f07f4426d3db6e6d0db7e3ca --- fs/sdcardfs/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 6d526bf3d956..2decea3d1e3e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -274,7 +274,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info->options.fs_low_uid, sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false, sb->s_root->d_inode); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); From e06c452d0d079c32869303e1ff16e162f16d1cf9 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 26 Sep 2016 14:48:22 -0700 Subject: [PATCH 0545/1276] ANDROID: sdcardfs: Move directory unlock before touch This removes a deadlock under low memory conditions. filp_open can call lookup_slow, which will attempt to lock the parent. Change-Id: I940643d0793f5051d1e79a56f4da2fa8ca3d8ff7 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 67bcee2c379a..3c353c95ef3e 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -296,14 +296,17 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode copied_fs = copy_fs_struct(current->fs); if (!copied_fs) { err = -ENOMEM; + unlock_dir(lower_parent_dentry); goto out_unlock; } current->fs = copied_fs; current->fs->umask = 0; err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); - if (err) + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { @@ -325,14 +328,18 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); - if (err) + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); + unlock_dir(lower_parent_dentry); + if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -352,7 +359,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode current->fs = saved_fs; free_fs_struct(copied_fs); out_unlock: - unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); From 72e5443a28165a23822d24db205927c3c3762847 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:48:45 -0700 Subject: [PATCH 0546/1276] ANDROID: sdcardfs: User new permission2 functions Change-Id: Ic7e0fb8fdcebb31e657b079fe02ac834c4a50db9 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 27 +++++++++++++++++++++------ fs/sdcardfs/sdcardfs.h | 4 ++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 3c353c95ef3e..503501388ef9 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -54,6 +54,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, { int err; struct dentry *lower_dentry; + struct vfsmount *lower_dentry_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; @@ -73,6 +74,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_dentry_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0664 */ @@ -87,7 +89,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } current->fs = copied_fs; current->fs->umask = 0; - err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -154,6 +156,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; @@ -172,10 +175,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); + err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL); /* * Note: unlinking on top of NFS can cause silly-renamed files. @@ -256,6 +260,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int err; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); @@ -286,6 +291,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0775 */ @@ -301,7 +307,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } current->fs = copied_fs; current->fs->umask = 0; - err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); if (err) { unlock_dir(lower_parent_dentry); @@ -370,6 +376,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; + struct vfsmount *lower_mnt; int err; struct path lower_path; const struct cred *saved_cred = NULL; @@ -390,9 +397,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); + err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry); if (err) goto out; @@ -456,6 +464,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; + struct vfsmount *lower_mnt = NULL; struct dentry *trap = NULL; struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; @@ -477,6 +486,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; + lower_mnt = lower_old_path.mnt; lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); @@ -492,7 +502,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, + err = vfs_rename2(lower_mnt, + d_inode(lower_old_dir_dentry), lower_old_dentry, d_inode(lower_new_dir_dentry), lower_new_dentry, NULL, 0); if (err) @@ -595,6 +606,8 @@ static int sdcardfs_permission(struct inode *inode, int mask) int err; struct inode *top = grab_top(SDCARDFS_I(inode)); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); if (!top) return -EINVAL; /* Ensure owner is up to date */ @@ -642,6 +655,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *inode; struct inode *lower_inode; struct path lower_path; @@ -675,6 +689,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_inode = sdcardfs_lower_inode(inode); /* prepare our own lower struct iattr (with the lower file) */ @@ -718,7 +733,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) * tries to open(), unlink(), then ftruncate() a file. */ mutex_lock(&d_inode(lower_dentry)->i_mutex); - err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ + err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); mutex_unlock(&d_inode(lower_dentry)->i_mutex); if (current->mm) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index cfda98d257b6..5132f1dc5a4d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -476,7 +476,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m goto out_unlock; } - err = vfs_mkdir(d_inode(parent.dentry), dent, mode); + err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode); if (err) { if (err == -EEXIST) err = 0; @@ -487,7 +487,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; mutex_lock(&d_inode(dent)->i_mutex); - notify_change(dent, &attrs, NULL); + notify_change2(parent.mnt, dent, &attrs, NULL); mutex_unlock(&d_inode(dent)->i_mutex); out_dput: From ae8be7da556d678c95d81c7bda69cee30be613ec Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 17:36:05 -0700 Subject: [PATCH 0547/1276] ANDROID: sdcardfs: Add gid and mask to private mount data Adds support for mount2, remount2, and the functions to allocate/clone/copy the private data The next patch will switch over to actually using it. Change-Id: I8a43da26021d33401f655f0b2784ead161c575e3 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/main.c | 103 ++++++++++++++++++++++++++++++++++++----- fs/sdcardfs/sdcardfs.h | 8 ++++ fs/sdcardfs/super.c | 64 ++++++++++++++++++++++--- 3 files changed, 157 insertions(+), 18 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 2decea3d1e3e..5400e7e63d27 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -49,7 +49,8 @@ static const match_table_t sdcardfs_tokens = { }; static int parse_options(struct super_block *sb, char *options, int silent, - int *debug, struct sdcardfs_mount_options *opts) + int *debug, struct sdcardfs_vfsmount_options *vfsopts, + struct sdcardfs_mount_options *opts) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -58,9 +59,11 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; + vfsopts->mask = 0; opts->mask = 0; opts->multiuser = false; opts->fs_user_id = 0; + vfsopts->gid = 0; opts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -95,6 +98,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, if (match_int(&args[0], &option)) return 0; opts->gid = option; + vfsopts->gid = option; break; case Opt_userid: if (match_int(&args[0], &option)) @@ -105,6 +109,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, if (match_int(&args[0], &option)) return 0; opts->mask = option; + vfsopts->mask = option; break; case Opt_multiuser: opts->multiuser = true; @@ -135,6 +140,65 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; } +int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + int debug; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sdcardfs_tokens, args); + + switch (token) { + case Opt_debug: + debug = 1; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + vfsopts->gid = option; + + break; + case Opt_mask: + if (match_int(&args[0], &option)) + return 0; + vfsopts->mask = option; + break; + case Opt_multiuser: + case Opt_userid: + case Opt_fsuid: + case Opt_fsgid: + case Opt_reserved_mb: + printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + break; + /* unknown option */ + default: + if (!silent) { + printk( KERN_ERR "Unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + + if (debug) { + printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); + printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); + printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + } + + return 0; +} + #if 0 /* * our custom d_alloc_root work-alike @@ -172,14 +236,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list); * There is no need to lock the sdcardfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ -static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, - void *raw_data, int silent) +static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, + const char *dev_name, void *raw_data, int silent) { int err = 0; int debug; struct super_block *lower_sb; struct path lower_path; struct sdcardfs_sb_info *sb_info; + struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -212,7 +277,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info = sb->s_fs_info; /* parse options */ - err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); + err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { printk(KERN_ERR "sdcardfs: invalid options\n"); goto out_freesbi; @@ -306,9 +371,9 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, } /* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, - int (*fill_super)(struct super_block *, const char *, void *, int)) +static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) { int error; @@ -319,7 +384,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, s->s_flags = flags; - error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); + error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); if (error) { deactivate_locked_super(s); return ERR_PTR(error); @@ -328,15 +393,27 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, return dget(s->s_root); } -struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, +static struct dentry *sdcardfs_mount(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { /* * dev_name is a lower_path_name, * raw_data is a option string. */ - return mount_nodev_with_options(fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + return mount_nodev_with_options(mnt, fs_type, flags, dev_name, + raw_data, sdcardfs_read_super); +} + +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + WARN(1, "sdcardfs does not support mount. Use mount2.\n"); + return ERR_PTR(-EINVAL); +} + +void *sdcardfs_alloc_mnt_data(void) { + return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } void sdcardfs_kill_sb(struct super_block *sb) { @@ -353,7 +430,9 @@ void sdcardfs_kill_sb(struct super_block *sb) { static struct file_system_type sdcardfs_fs_type = { .owner = THIS_MODULE, .name = SDCARDFS_NAME, - .mount = sdcardfs_mount, + .mount = sdcardfs_mount_wrn, + .mount2 = sdcardfs_mount, + .alloc_mnt_data = sdcardfs_alloc_mnt_data, .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 5132f1dc5a4d..22ef29857022 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -193,6 +193,14 @@ struct sdcardfs_mount_options { unsigned int reserved_mb; }; +struct sdcardfs_vfsmount_options { + gid_t gid; + mode_t mask; +}; + +extern int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts); + /* sdcardfs super-block data in memory */ struct sdcardfs_sb_info { struct super_block *sb; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 0a465395aab7..edda32b68dc0 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -108,6 +108,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options return err; } +/* + * @mnt: mount point we are remounting + * @sb: superblock we are remounting + * @flags: numeric mount options + * @options: mount options string + */ +static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, + int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { + printk(KERN_ERR + "sdcardfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); + + + return err; +} + +static void* sdcardfs_clone_mnt_data(void *data) { + struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options* old = data; + if(!opt) return NULL; + opt->gid = old->gid; + opt->mask = old->mask; + return opt; +} + +static void sdcardfs_copy_mnt_data(void *data, void *newdata) { + struct sdcardfs_vfsmount_options* old = data; + struct sdcardfs_vfsmount_options* new = newdata; + old->gid = new->gid; + old->mask = new->mask; +} + /* * Called by iput() when the inode reference count reached zero * and the inode is not hashed anywhere. Used to clear anything @@ -191,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; if (opts->fs_low_uid != 0) - seq_printf(m, ",uid=%u", opts->fs_low_uid); + seq_printf(m, ",fsuid=%u", opts->fs_low_uid); if (opts->fs_low_gid != 0) - seq_printf(m, ",gid=%u", opts->fs_low_gid); - + seq_printf(m, ",fsgid=%u", opts->fs_low_gid); + if (vfsopts->gid != 0) + seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) seq_printf(m, ",multiuser"); - + if (vfsopts->mask) + seq_printf(m, ",mask=%u", vfsopts->mask); + if (opts->fs_user_id) + seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); @@ -214,9 +263,12 @@ const struct super_operations sdcardfs_sops = { .put_super = sdcardfs_put_super, .statfs = sdcardfs_statfs, .remount_fs = sdcardfs_remount_fs, + .remount_fs2 = sdcardfs_remount_fs2, + .clone_mnt_data = sdcardfs_clone_mnt_data, + .copy_mnt_data = sdcardfs_copy_mnt_data, .evict_inode = sdcardfs_evict_inode, .umount_begin = sdcardfs_umount_begin, - .show_options = sdcardfs_show_options, + .show_options2 = sdcardfs_show_options, .alloc_inode = sdcardfs_alloc_inode, .destroy_inode = sdcardfs_destroy_inode, .drop_inode = generic_delete_inode, From 151a3efe57a62bef8c988f3745eac9d0860a6260 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 20:27:20 -0700 Subject: [PATCH 0548/1276] ANDROID: sdcardfs: Use per mount permissions This switches sdcardfs over to using permission2. Instead of mounting several sdcardfs instances onto the same underlaying directory, you bind mount a single mount several times, and remount with the options you want. These are stored in the private mount data, allowing you to maintain the same tree, but have different permissions for different mount points. Warning functions have been added for permission, as it should never be called, and the correct behavior is unclear. Change-Id: I841b1d70ec60cf2b866fa48edeb74a0b0f8334f5 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 20 ++++-- fs/sdcardfs/inode.c | 128 ++++++++++++++++++++++++++++++------- fs/sdcardfs/lookup.c | 4 +- fs/sdcardfs/main.c | 8 +-- fs/sdcardfs/sdcardfs.h | 44 ++++++++----- 5 files changed, 151 insertions(+), 53 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 89daf69efbaa..066edbbb6ad6 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -141,13 +141,23 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { + /* We need permission to fix up these values. + * Since permissions are based of of the mount, and + * we are accessing without the mount point, we create + * a fake mount with the permissions we will be using. + */ + struct vfsmount fakemnt; + struct sdcardfs_vfsmount_options opts; + fakemnt.data = &opts; + opts.gid = AID_SDCARD_RW; + opts.mask = 0; mutex_lock(&d_inode(dentry)->i_mutex); - child = lookup_one_len(name, dentry, len); + child = lookup_one_len2(name, &fakemnt, dentry, len); mutex_unlock(&d_inode(dentry)->i_mutex); if (!IS_ERR(child)) { - if (child->d_inode) { + if (d_inode(child)) { get_derived_permission(dentry, child); - fix_derived_permission(d_inode(child)); + fixup_tmp_permissions(d_inode(child)); } dput(child); } @@ -172,7 +182,7 @@ void fixup_top_recursive(struct dentry *parent) { if (d_inode(dentry)) { if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { get_derived_permission(parent, dentry); - fix_derived_permission(d_inode(dentry)); + fixup_tmp_permissions(d_inode(dentry)); fixup_top_recursive(dentry); } } @@ -202,7 +212,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) dput(parent); } } - fix_derived_permission(dentry->d_inode); + fixup_tmp_permissions(d_inode(dentry)); } int need_graft_path(struct dentry *dentry) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 503501388ef9..1b8f068987c7 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -531,7 +531,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); - fix_derived_permission(d_inode(old_dentry)); + fixup_tmp_permissions(d_inode(old_dentry)); fixup_top_recursive(old_dentry); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); @@ -601,28 +601,66 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) } #endif -static int sdcardfs_permission(struct inode *inode, int mask) +static int sdcardfs_permission_wrn(struct inode *inode, int mask) +{ + WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + return -EINVAL; +} + +void copy_attrs(struct inode *dest, const struct inode *src) +{ + dest->i_mode = src->i_mode; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; +#ifdef CONFIG_FS_POSIX_ACL + dest->i_acl = src->i_acl; +#endif +#ifdef CONFIG_SECURITY + dest->i_security = src->i_security; +#endif +} + +static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask) { int err; + struct inode tmp; struct inode *top = grab_top(SDCARDFS_I(inode)); if (IS_ERR(mnt)) return PTR_ERR(mnt); - if (!top) + + if (!top) { + release_top(SDCARDFS_I(inode)); + WARN(1, "Top value was null!\n"); return -EINVAL; - /* Ensure owner is up to date */ - if (!uid_eq(inode->i_uid, top->i_uid)) { - SDCARDFS_I(inode)->d_uid = SDCARDFS_I(top)->d_uid; - fix_derived_permission(inode); } - release_top(SDCARDFS_I(inode)); /* * Permission check on sdcardfs inode. * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. */ - err = generic_permission(inode, mask); - + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; + if (IS_POSIXACL(inode)) + printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) * for checking inode permission. But doing such things here seems @@ -651,7 +689,13 @@ static int sdcardfs_permission(struct inode *inode, int mask) } -static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) +static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) +{ + WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + return -EINVAL; +} + +static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia) { int err; struct dentry *lower_dentry; @@ -661,17 +705,45 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct path lower_path; struct iattr lower_ia; struct dentry *parent; + struct inode tmp; + struct inode *top; + const struct cred *saved_cred = NULL; inode = d_inode(dentry); + top = grab_top(SDCARDFS_I(inode)); + + if (!top) { + release_top(SDCARDFS_I(inode)); + return -EINVAL; + } + + /* + * Permission check on sdcardfs inode. + * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. + * + */ + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_size = i_size_read(inode); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; /* * Check if user has permission to change inode. We don't check if * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ - err = inode_change_ok(inode, ia); + err = inode_change_ok(&tmp, ia); - /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); @@ -687,6 +759,9 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (err) goto out_err; + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred); + sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_mnt = lower_path.mnt; @@ -710,7 +785,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (current->mm) down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { - err = inode_newsize_ok(inode, ia->ia_size); + err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { if (current->mm) up_write(¤t->mm->mmap_sem); @@ -752,11 +827,12 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) out: sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); out_err: return err; } -static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); @@ -765,10 +841,10 @@ static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; - stat->mode = (inode->i_mode & S_IFMT) | get_mode(SDCARDFS_I(top)); + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); stat->nlink = inode->i_nlink; stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(top))); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; @@ -809,14 +885,14 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - err = sdcardfs_fillattr(inode, stat); + err = sdcardfs_fillattr(mnt, inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); return err; } const struct inode_operations sdcardfs_symlink_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission2 = sdcardfs_permission, + .setattr2 = sdcardfs_setattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them * These methods are *NOT* perfectly tested. @@ -829,12 +905,14 @@ const struct inode_operations sdcardfs_symlink_iops = { const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, - .permission = sdcardfs_permission, + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, .rename = sdcardfs_rename, - .setattr = sdcardfs_setattr, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them @@ -846,7 +924,9 @@ const struct inode_operations sdcardfs_dir_iops = { }; const struct inode_operations sdcardfs_main_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, }; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index c74a7d1bc18e..00a711ec2733 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -244,6 +244,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err == -ENOENT) { struct dentry *child; struct dentry *match = NULL; + mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { @@ -254,6 +255,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } spin_unlock(&lower_dir_dentry->d_lock); + mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); if (match) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, @@ -389,7 +391,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, sdcardfs_lower_inode(dentry->d_inode)); /* get derived permission */ get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); + fixup_tmp_permissions(d_inode(dentry)); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 5400e7e63d27..eec10ccacd99 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -28,7 +28,6 @@ enum { Opt_fsgid, Opt_gid, Opt_debug, - Opt_lower_fs, Opt_mask, Opt_multiuser, // May need? Opt_userid, @@ -60,11 +59,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; vfsopts->mask = 0; - opts->mask = 0; opts->multiuser = false; opts->fs_user_id = 0; vfsopts->gid = 0; - opts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -97,7 +94,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid: if (match_int(&args[0], &option)) return 0; - opts->gid = option; vfsopts->gid = option; break; case Opt_userid: @@ -108,7 +104,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_mask: if (match_int(&args[0], &option)) return 0; - opts->mask = option; vfsopts->mask = option; break; case Opt_multiuser: @@ -258,6 +253,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); + printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, @@ -342,7 +338,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } - fix_derived_permission(sb->s_root->d_inode); + fixup_tmp_permissions(d_inode(sb->s_root)); sb_info->sb = sb; list_add(&sb_info->list, &sdcardfs_super_list); mutex_unlock(&sdcardfs_super_list_lock); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 22ef29857022..b03130329014 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -68,14 +68,20 @@ #define AID_PACKAGE_INFO 1027 -#define fix_derived_permission(x) \ + +/* + * Permissions are handled by our permission function. + * We don't want anyone who happens to look at our inode value to prematurely + * block access, so store more permissive values. These are probably never + * used. + */ +#define fixup_tmp_permissions(x) \ do { \ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ - (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \ - (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\ + (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) - /* OVERRIDE_CRED() and REVERT_CRED() * OVERRID_CRED() * backup original task->cred @@ -187,8 +193,6 @@ struct sdcardfs_mount_options { uid_t fs_low_uid; gid_t fs_low_gid; userid_t fs_user_id; - gid_t gid; - mode_t mask; bool multiuser; unsigned int reserved_mb; }; @@ -360,9 +364,10 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct sdcardfs_inode_info *info) { - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - if (sb_info->options.gid == AID_SDCARD_RW) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + struct sdcardfs_vfsmount_options *opts = mnt->data; + + if (opts->gid == AID_SDCARD_RW) { /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -370,14 +375,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) { * assigned to app directories are still multiuser aware. */ return AID_SDCARD_RW; } else { - return multiuser_get_uid(info->userid, sb_info->options.gid); + return multiuser_get_uid(info->userid, opts->gid); } } -static inline int get_mode(struct sdcardfs_inode_info *info) { +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { int owner_mode; int filtered_mode; - struct sdcardfs_sb_info * sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - int visible_mode = 0775 & ~sb_info->options.mask; + struct sdcardfs_vfsmount_options *opts = mnt->data; + int visible_mode = 0775 & ~opts->mask; + if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure @@ -387,7 +393,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. */ - if (sb_info->options.gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) { visible_mode = visible_mode & ~0006; } else { visible_mode = visible_mode & ~0007; @@ -553,12 +559,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d return 1; } -/* Copies attrs and maintains sdcardfs managed attrs */ +/* + * Copies attrs and maintains sdcardfs managed attrs + * Since our permission check handles all special permissions, set those to be open + */ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src) { - dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest)); + dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | + S_IROTH | S_IXOTH; /* 0775 */ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); - dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest))); + dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; dest->i_mtime = src->i_mtime; From 641cc16369f48007bc51105a2a248104ed0aec38 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 15 Nov 2016 13:35:18 -0800 Subject: [PATCH 0549/1276] ANDROID: sdcardfs: Change magic value Sdcardfs uses the same magic value as wrapfs. This should not be the case. As it is entirely in memory, the value can be changed without any loss of compatibility. Change-Id: I24200b805d5e6d32702638be99e47d50d7f2f746 Signed-off-by: Daniel Rosenberg --- include/uapi/linux/magic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 6f7b217c44ac..d72347947304 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -56,7 +56,7 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" -#define SDCARDFS_SUPER_MAGIC 0xb550ca10 +#define SDCARDFS_SUPER_MAGIC 0x5dca2df5 #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb From cff865a370f31e3392e91990960e7fd30f7e8baf Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Dec 2016 14:36:29 -0800 Subject: [PATCH 0550/1276] ANDROID: sdcardfs: Switch ->d_inode to d_inode() Change-Id: I12375cc2d6e82fb8adf0319be971f335f8d7a312 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 16 ++++++++-------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/lookup.c | 14 +++++++------- fs/sdcardfs/main.c | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 066edbbb6ad6..60ae94bb99f7 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -49,8 +49,8 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { - struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); appid_t appid; /* By default, each inode inherits from its parent. @@ -61,7 +61,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st * stage of each system call by fix_derived_permission(inode). */ - inherit_derived_state(parent->d_inode, dentry->d_inode); + inherit_derived_state(d_inode(parent), d_inode(dentry)); /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { @@ -134,7 +134,7 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) struct sdcardfs_inode_info *info; if (!dget(dentry)) return; - if (!dentry->d_inode) { + if (!d_inode(dentry)) { dput(dentry); return; } @@ -195,7 +195,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !dentry->d_inode) { + if(!dentry || !d_inode(dentry)) { printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); return; } @@ -204,7 +204,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 2. remove the root dentry update */ if(IS_ROOT(dentry)) { - //setup_default_pre_root_state(dentry->d_inode); + //setup_default_pre_root_state(d_inode(dentry)); } else { parent = dget_parent(dentry); if(parent) { @@ -219,7 +219,7 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); if(parent_info->perm == PERM_ANDROID && @@ -278,7 +278,7 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); spin_lock(&SDCARDFS_D(dentry)->lock); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c249fa982d3c..7750a0472389 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 00a711ec2733..e94a65c8bbbd 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, struct inode *lower_inode; struct super_block *lower_sb; - lower_inode = lower_path->dentry->d_inode; + lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ @@ -359,7 +359,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -386,16 +386,16 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } if (ret) dentry = ret; - if (dentry->d_inode) { - fsstack_copy_attr_times(dentry->d_inode, - sdcardfs_lower_inode(dentry->d_inode)); + if (d_inode(dentry)) { + fsstack_copy_attr_times(d_inode(dentry), + sdcardfs_lower_inode(d_inode(dentry))); /* get derived permission */ get_derived_permission(parent, dentry); fixup_tmp_permissions(d_inode(dentry)); } /* update parent directory's atime */ - fsstack_copy_attr_atime(parent->d_inode, - sdcardfs_lower_inode(parent->d_inode)); + fsstack_copy_attr_atime(d_inode(parent), + sdcardfs_lower_inode(d_inode(parent))); out: sdcardfs_put_lower_path(parent, &lower_parent_path); diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index eec10ccacd99..7a8eae29e44d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -297,7 +297,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, sb->s_op = &sdcardfs_sops; /* get a new inode and allocate our root dentry */ - inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); + inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; From 065ac66804bf42c5cbaa80d1bd570c581903e6eb Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 27 Dec 2016 12:36:29 -0800 Subject: [PATCH 0551/1276] ANDROID: sdcardfs: Fix locking issue with permision fix up Don't use lookup_one_len so we can grab the spinlock that protects d_subdirs. Bug: 30954918 Change-Id: I0c6a393252db7beb467e0d563739a3a14e1b5115 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 60ae94bb99f7..9408a5477ada 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -141,32 +141,26 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - /* We need permission to fix up these values. - * Since permissions are based of of the mount, and - * we are accessing without the mount point, we create - * a fake mount with the permissions we will be using. - */ - struct vfsmount fakemnt; - struct sdcardfs_vfsmount_options opts; - fakemnt.data = &opts; - opts.gid = AID_SDCARD_RW; - opts.mask = 0; - mutex_lock(&d_inode(dentry)->i_mutex); - child = lookup_one_len2(name, &fakemnt, dentry, len); - mutex_unlock(&d_inode(dentry)->i_mutex); - if (!IS_ERR(child)) { - if (d_inode(child)) { - get_derived_permission(dentry, child); - fixup_tmp_permissions(d_inode(child)); - } - dput(child); + spin_lock(&dentry->d_lock); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + dget(child); + if (!strncasecmp(child->d_name.name, name, len)) { + if (child->d_inode) { + get_derived_permission(dentry, child); + fixup_tmp_permissions(child->d_inode); + dput(child); + break; + } + } + dput(child); } + spin_unlock(&dentry->d_lock); } else if (descendant_may_need_fixup(info->perm)) { - mutex_lock(&d_inode(dentry)->i_mutex); + spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { fixup_perms_recursive(child, name, len); } - mutex_unlock(&d_inode(dentry)->i_mutex); + spin_unlock(&dentry->d_lock); } dput(dentry); } From 31ea603eb3c4c6ab2f7d56edda793156d33b8650 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 1 Jun 2016 21:53:20 +0530 Subject: [PATCH 0552/1276] ANDROID: sdcardfs: use wrappers to access i_mutex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use inode_{lock,unlock,lock_nested} wrappers as suggested by upstream commit 5955102c9984 (wrappers for ->i_mutex access) for access to ->i_mutex, otherwise we run into following build error: CC [M] fs/sdcardfs/dentry.o In file included from fs/sdcardfs/dentry.c:21:0: fs/sdcardfs/sdcardfs.h: In function ‘lock_parent’: fs/sdcardfs/sdcardfs.h:422:33: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); ^ fs/sdcardfs/sdcardfs.h: In function ‘unlock_dir’: fs/sdcardfs/sdcardfs.h:428:28: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_unlock(&d_inode(dir)->i_mutex); ^ In file included from ./include/linux/fs.h:19:0, from fs/sdcardfs/sdcardfs.h:31, from fs/sdcardfs/dentry.c:21: fs/sdcardfs/sdcardfs.h: In function ‘prepare_dir’: fs/sdcardfs/sdcardfs.h:457:27: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_lock(&d_inode(dent)->i_mutex); ^ ./include/linux/mutex.h:146:44: note: in definition of macro ‘mutex_lock’ #define mutex_lock(lock) mutex_lock_nested(lock, 0) ^ In file included from fs/sdcardfs/dentry.c:21:0: fs/sdcardfs/sdcardfs.h:459:29: error: ‘struct inode’ has no member named‘i_mutex’ mutex_unlock(&d_inode(dent)->i_mutex); ^ fs/sdcardfs/sdcardfs.h:466:38: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_unlock(&d_inode(parent.dentry)->i_mutex); ^ Change-Id: I4c8298045ac511aba5542d9ca967331f550376a5 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 4 ++-- fs/sdcardfs/lookup.c | 6 +++--- fs/sdcardfs/sdcardfs.h | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1b8f068987c7..bd8a70185147 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -807,10 +807,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ - mutex_lock(&d_inode(lower_dentry)->i_mutex); + inode_lock(d_inode(lower_dentry)); err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); - mutex_unlock(&d_inode(lower_dentry)->i_mutex); + inode_unlock(d_inode(lower_dentry)); if (current->mm) up_write(¤t->mm->mmap_sem); if (err) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index e94a65c8bbbd..d9d46308ef94 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -244,7 +244,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err == -ENOENT) { struct dentry *child; struct dentry *match = NULL; - mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); + inode_lock(d_inode(lower_dir_dentry)); spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { @@ -255,7 +255,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } spin_unlock(&lower_dir_dentry->d_lock); - mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); + inode_unlock(d_inode(lower_dir_dentry)); if (match) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, @@ -344,7 +344,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * On fail (== error) * returns error ptr * - * @dir : Parent inode. It is locked (dir->i_mutex) + * @dir : Parent inode. * @dentry : Target dentry to lookup. we should set each of fields. * (dentry->d_name is initialized already) * @nd : nameidata of parent inode diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index b03130329014..66a97ef8d261 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -465,13 +465,13 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); - mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); return dir; } static inline void unlock_dir(struct dentry *dir) { - mutex_unlock(&d_inode(dir)->i_mutex); + inode_unlock(d_inode(dir)); dput(dir); } @@ -500,16 +500,16 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m attrs.ia_uid = make_kuid(&init_user_ns, uid); attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; - mutex_lock(&d_inode(dent)->i_mutex); + inode_lock(d_inode(dent)); notify_change2(parent.mnt, dent, &attrs, NULL); - mutex_unlock(&d_inode(dent)->i_mutex); + inode_unlock(d_inode(dent)); out_dput: dput(dent); out_unlock: /* parent dentry locked by lookup_create */ - mutex_unlock(&d_inode(parent.dentry)->i_mutex); + inode_unlock(d_inode(parent.dentry)); path_put(&parent); return err; } From c25c2f5018a22e527224e4eee138fa851816730e Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Thu, 4 Aug 2016 21:04:31 +0530 Subject: [PATCH 0553/1276] ANDROID: sdcardfs: add parent pointer into dentry name hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix following sdcardfs compilation error introduced in code refactoring by upstream commit 8387ff2577eb ("vfs: make the string hashes salt the hash"). CC [M] fs/sdcardfs/dentry.o In file included from ./include/linux/dcache.h:13:0, from fs/sdcardfs/sdcardfs.h:29, from fs/sdcardfs/dentry.c:21: fs/sdcardfs/dentry.c: In function ‘sdcardfs_hash_ci’: ./include/linux/stringhash.h:38:51: error: expected expression before ‘)’ token #define init_name_hash(salt) (unsigned long)(salt) ^ fs/sdcardfs/dentry.c:138:9: note: in expansion of macro ‘init_name_hash’ hash = init_name_hash(); ^ Change-Id: I9feb6c075a7e953726954f5746fc009202d3121c Signed-off-by: Amit Pundir --- fs/sdcardfs/dentry.c | 2 +- fs/sdcardfs/lookup.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 971928ab6c21..e4156a6fa8c8 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -135,7 +135,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, //len = vfat_striptail_len(qstr); len = qstr->len; - hash = init_name_hash(); + hash = init_name_hash(dentry); while (len--) //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index d9d46308ef94..d271617290ee 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -309,7 +309,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* instatiate a new negative dentry */ this.name = name; this.len = strlen(name); - this.hash = full_name_hash(this.name, this.len); + this.hash = full_name_hash(dentry, this.name, this.len); lower_dentry = d_lookup(lower_dir_dentry, &this); if (lower_dentry) goto setup_lower; From 58616bb4ec684ab1f23616ce6a2c489510b93eed Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Aug 2016 12:27:33 +0530 Subject: [PATCH 0554/1276] ANDROID: sdcardfs: get rid of 'parent' argument of ->d_compare() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ->d_compare() doesn't get parent as a separate argument anymore according to upstream commit 6fa67e707559 ("get rid of 'parent' argument of ->d_compare()"). We run into following build error otherwise: CC [M] fs/sdcardfs/dentry.o fs/sdcardfs/dentry.c:183:15: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .d_compare = sdcardfs_cmp_ci, ^ fs/sdcardfs/dentry.c:183:15: note: (near initialization for ‘sdcardfs_ci_dops.d_compare’) Change-Id: I51801b57aeb8287f1e69ce6cb944e8722ff37bea Signed-off-by: Amit Pundir --- fs/sdcardfs/dentry.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index e4156a6fa8c8..f22de8add10c 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -147,8 +147,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, /* * Case insensitive compare of two vfat names. */ -static int sdcardfs_cmp_ci(const struct dentry *parent, - const struct dentry *dentry, +static int sdcardfs_cmp_ci(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { /* This function is copy of vfat_cmpi */ From 1654d7ffdd2098c00760ad3d2fbfbb14c62987a9 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 11 Oct 2016 13:26:17 +0530 Subject: [PATCH 0555/1276] ANDROID: sdcardfs: Propagate dentry down to inode_change_ok() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 31051c85b5e2 ("fs: Give dentry to inode_change_ok() instead of inode"), to avoid clearing of capabilities or security related extended attributes too early, inode_change_ok() will need to take dentry instead of inode. Propagate it down to sdcardfs_setattr() and also rename it to setattr_prepare(), otherwise we run into following build error: CC [M] fs/sdcardfs/inode.o fs/sdcardfs/inode.c: In function ‘sdcardfs_setattr’: fs/sdcardfs/inode.c:644:8: error: implicit declaration of function ‘inode_change_ok’ [-Werror=implicit-function-declaration] err = inode_change_ok(inode, ia); ^ Change-Id: I714b4f4f68b7fea1ac82a71d2f323c76b11fa008 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index bd8a70185147..76b57c8c32fd 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -706,6 +706,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct struct iattr lower_ia; struct dentry *parent; struct inode tmp; + struct dentry tmp_d; struct inode *top; const struct cred *saved_cred = NULL; @@ -736,13 +737,14 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct tmp.i_size = i_size_read(inode); release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; + tmp_d.d_inode = &tmp; /* - * Check if user has permission to change inode. We don't check if + * Check if user has permission to change dentry. We don't check if * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ - err = inode_change_ok(&tmp, ia); + err = setattr_prepare(&tmp_d, ia); if (!err) { /* check the Android group ID */ From 39335cac1d2fddbf5059ee7fdbf60871d45fe7c6 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Sun, 16 Oct 2016 15:24:15 +0530 Subject: [PATCH 0556/1276] ANDROID: sdcardfs: make it use new .rename i_op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 2773bf00aeb9 ("fs: rename "rename2" i_op to "rename""), syscall rename2 is merged with rename syscall and it broke sdcard_fs build and we get following build error: CC [M] fs/sdcardfs/inode.o fs/sdcardfs/inode.c:786:13: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .rename = sdcardfs_rename, ^ fs/sdcardfs/inode.c:786:13: note: (near initialization for ‘sdcardfs_dir_iops.rename’) renameat2 syscall is the same as renameat with an added flags argument and calling renameat2 with flags=0 is equivalent to calling renameat. Change-Id: I48f3c76c3af481241188253a76f310670de6bd18 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 76b57c8c32fd..b5a43c11a00c 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -457,7 +457,8 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode * superblock-level name-space lock for renames and copy-ups. */ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { int err = 0; struct dentry *lower_old_dentry = NULL; @@ -470,6 +471,9 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; + if (flags) + return -EINVAL; + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" From 7622bb3fcc791058b7f44ad605209d2b0fe5c893 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Jun 2016 16:30:56 +0530 Subject: [PATCH 0557/1276] ANDROID: sdcardfs: eliminate the offset argument to ->direct_IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate the offset argument to sdcardfs_direct_IO() which is dropped by upstream commit c8b8e32d700f ("direct-io: eliminate the offset argument to ->direct_IO"), otherwise we run into following build error: CC [M] fs/sdcardfs/mmap.o fs/sdcardfs/mmap.c:76:15: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .direct_IO = sdcardfs_direct_IO, ^ fs/sdcardfs/mmap.c:76:15: note: (near initialization for ‘sdcardfs_aops.direct_IO’) Change-Id: I292d93bb16365a9fa46494accb2b5da51028b5c1 Signed-off-by: Amit Pundir --- fs/sdcardfs/mmap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index e21f64675a80..ac5f3deae088 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,8 +48,7 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } -static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, - struct iov_iter *iter, loff_t pos) +static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* * This function returns zero on purpose in order to support direct IO. From 843bd7295ee0658738cd65e131037ffeb9e0f644 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 20 Jan 2017 15:19:13 -0800 Subject: [PATCH 0558/1276] ANDROID: sdcardfs: Allow non-owners to touch This modifies the permission checks in setattr to allow for non-owners to modify the timestamp of files to things other than the current time. This still requires write access, as enforced by the permission call, but relaxes the requirement that the caller must be the owner, allowing those with group permissions to change it as well. Signed-off-by: Daniel Rosenberg Bug: 11118565 Change-Id: Ied31f0cce2797675c7ef179eeb4e088185adcbad --- fs/sdcardfs/inode.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b5a43c11a00c..4bafdf783354 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -748,6 +748,11 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + /* Allow touch updating timestamps. A previous permission check ensures + * we have write access. Changes to mode, owner, and group are ignored*/ + ia->ia_valid |= ATTR_FORCE; err = setattr_prepare(&tmp_d, ia); if (!err) { @@ -773,8 +778,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct lower_mnt = lower_path.mnt; lower_inode = sdcardfs_lower_inode(inode); - /* prepare our own lower struct iattr (with the lower file) */ - memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); From e3d74804d1740e27b01fe64f35bc9aca51c6c046 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Sat, 21 Jan 2017 00:35:26 -0800 Subject: [PATCH 0559/1276] ANDROID: sdcardfs: Refactor configfs interface This refactors the configfs code to be more easily extended. It will allow additional files to be added easily. Signed-off-by: Daniel Rosenberg Bug: 34542611 Bug: 34262585 Change-Id: I73c9b0ae5ca7eb27f4ebef3e6807f088b512d539 --- fs/sdcardfs/packagelist.c | 133 +++++++++++++++----------------------- 1 file changed, 53 insertions(+), 80 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 03776fa5f26c..0b3fb50b1fe4 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -220,26 +220,24 @@ static void packagelist_destroy(void) printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } -struct package_appid { +struct package_details { struct config_item item; - int add_pid; + const char *name; }; -static inline struct package_appid *to_package_appid(struct config_item *item) +static inline struct package_details *to_package_details(struct config_item *item) { - return item ? container_of(item, struct package_appid, item) : NULL; + return item ? container_of(item, struct package_details, item) : NULL; } -static ssize_t package_appid_attr_show(struct config_item *item, - char *page) +static ssize_t package_details_appid_show(struct config_item *item, char *page) { - return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(item->ci_name)); + return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(to_package_details(item)->name)); } -static ssize_t package_appid_attr_store(struct config_item *item, +static ssize_t package_details_appid_store(struct config_item *item, const char *page, size_t count) { - struct package_appid *package_appid = to_package_appid(item); unsigned int tmp; int ret; @@ -247,73 +245,60 @@ static ssize_t package_appid_attr_store(struct config_item *item, if (ret) return ret; - ret = insert_packagelist_entry(item->ci_name, tmp); - package_appid->add_pid = tmp; + ret = insert_packagelist_entry(to_package_details(item)->name, tmp); + if (ret) return ret; return count; } -static struct configfs_attribute package_appid_attr_add_pid = { - .ca_owner = THIS_MODULE, - .ca_name = "appid", - .ca_mode = S_IRUGO | S_IWUGO, - .show = package_appid_attr_show, - .store = package_appid_attr_store, -}; +static void package_details_release(struct config_item *item) +{ + struct package_details *package_details = to_package_details(item); + printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name); + remove_packagelist_entry(package_details->name); + kfree(package_details->name); + kfree(package_details); +} + +CONFIGFS_ATTR(package_details_, appid); -static struct configfs_attribute *package_appid_attrs[] = { - &package_appid_attr_add_pid, +static struct configfs_attribute *package_details_attrs[] = { + &package_details_attr_appid, NULL, }; -static void package_appid_release(struct config_item *item) -{ - printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); - /* item->ci_name is freed already, so we rely on the dentry */ - remove_packagelist_entry(item->ci_dentry->d_name.name); - kfree(to_package_appid(item)); -} - -static struct configfs_item_operations package_appid_item_ops = { - .release = package_appid_release, +static struct configfs_item_operations package_details_item_ops = { + .release = package_details_release, }; static struct config_item_type package_appid_type = { - .ct_item_ops = &package_appid_item_ops, - .ct_attrs = package_appid_attrs, + .ct_item_ops = &package_details_item_ops, + .ct_attrs = package_details_attrs, .ct_owner = THIS_MODULE, }; - -struct sdcardfs_packages { - struct config_group group; -}; - -static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item) +static struct config_item *packages_make_item(struct config_group *group, const char *name) { - return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL; -} + struct package_details *package_details; -static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name) -{ - struct package_appid *package_appid; - - package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL); - if (!package_appid) + package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL); + if (!package_details) + return ERR_PTR(-ENOMEM); + package_details->name = kstrdup(name, GFP_KERNEL); + if (!package_details->name) { + kfree(package_details); return ERR_PTR(-ENOMEM); + } - config_item_init_type_name(&package_appid->item, name, + config_item_init_type_name(&package_details->item, name, &package_appid_type); - package_appid->add_pid = 0; - - return &package_appid->item; + return &package_details->item; } -static ssize_t packages_attr_show(struct config_item *item, - char *page) +static ssize_t packages_list_show(struct config_item *item, char *page) { struct hashtable_entry *hash_cur; int i; @@ -335,49 +320,37 @@ static ssize_t packages_attr_show(struct config_item *item, return count; } -static struct configfs_attribute sdcardfs_packages_attr_description = { - .ca_owner = THIS_MODULE, - .ca_name = "packages_gid.list", - .ca_mode = S_IRUGO, - .show = packages_attr_show, +static struct configfs_attribute packages_attr_packages_gid_list = { + .ca_name = "packages_gid.list", + .ca_mode = S_IRUGO, + .ca_owner = THIS_MODULE, + .show = packages_list_show, }; -static struct configfs_attribute *sdcardfs_packages_attrs[] = { - &sdcardfs_packages_attr_description, +static struct configfs_attribute *packages_attrs[] = { + &packages_attr_packages_gid_list, NULL, }; -static void sdcardfs_packages_release(struct config_item *item) -{ - - printk(KERN_INFO "sdcardfs: destroyed something?\n"); - kfree(to_sdcardfs_packages(item)); -} - -static struct configfs_item_operations sdcardfs_packages_item_ops = { - .release = sdcardfs_packages_release, -}; - /* * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */ -static struct configfs_group_operations sdcardfs_packages_group_ops = { - .make_item = sdcardfs_packages_make_item, +static struct configfs_group_operations packages_group_ops = { + .make_item = packages_make_item, }; -static struct config_item_type sdcardfs_packages_type = { - .ct_item_ops = &sdcardfs_packages_item_ops, - .ct_group_ops = &sdcardfs_packages_group_ops, - .ct_attrs = sdcardfs_packages_attrs, +static struct config_item_type packages_type = { + .ct_group_ops = &packages_group_ops, + .ct_attrs = packages_attrs, .ct_owner = THIS_MODULE, }; -static struct configfs_subsystem sdcardfs_packages_subsys = { +static struct configfs_subsystem sdcardfs_packages = { .su_group = { .cg_item = { .ci_namebuf = "sdcardfs", - .ci_type = &sdcardfs_packages_type, + .ci_type = &packages_type, }, }, }; @@ -385,7 +358,7 @@ static struct configfs_subsystem sdcardfs_packages_subsys = { static int configfs_sdcardfs_init(void) { int ret; - struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; + struct configfs_subsystem *subsys = &sdcardfs_packages; config_group_init(&subsys->su_group); mutex_init(&subsys->su_mutex); @@ -400,7 +373,7 @@ static int configfs_sdcardfs_init(void) static void configfs_sdcardfs_exit(void) { - configfs_unregister_subsystem(&sdcardfs_packages_subsys); + configfs_unregister_subsystem(&sdcardfs_packages); } int packagelist_init(void) From 5833eda87a728091b1ac003e4f5d9b7745ef49da Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Sun, 22 Jan 2017 15:32:49 -0800 Subject: [PATCH 0560/1276] ANDROID: sdcardfs: add support for user permission isolation This allows you to hide the existence of a package from a user by adding them to an exclude list. If a user creates that package's folder and is on the exclude list, they will not see that package's id. Signed-off-by: Daniel Rosenberg Bug: 34542611 Change-Id: I9eb82e0bf2457d7eb81ee56153b9c7d2f6646323 --- fs/sdcardfs/derived_perm.c | 34 +++-- fs/sdcardfs/packagelist.c | 297 ++++++++++++++++++++++++++++++++++--- fs/sdcardfs/sdcardfs.h | 17 ++- 3 files changed, 308 insertions(+), 40 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 9408a5477ada..a6d87d900ce5 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -103,7 +103,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: appid = get_appid(newdentry->d_name.name); - if (appid != 0) { + if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } set_top(info, &info->vfs_inode); @@ -116,8 +116,11 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, dentry); } -static int descendant_may_need_fixup(perm_t perm) { - if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID) +static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +{ + if (info->perm == PERM_ROOT) + return (limit->flags & BY_USERID)?info->userid == limit->userid:1; + if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) return 1; return 0; } @@ -129,7 +132,8 @@ static int needs_fixup(perm_t perm) { return 0; } -void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) { +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ struct dentry *child; struct sdcardfs_inode_info *info; if (!dget(dentry)) @@ -143,22 +147,22 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) if (needs_fixup(info->perm)) { spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); - if (!strncasecmp(child->d_name.name, name, len)) { - if (child->d_inode) { - get_derived_permission(dentry, child); - fixup_tmp_permissions(child->d_inode); - dput(child); - break; - } + dget(child); + if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + if (d_inode(child)) { + get_derived_permission(dentry, child); + fixup_tmp_permissions(d_inode(child)); + dput(child); + break; } - dput(child); + } + dput(child); } spin_unlock(&dentry->d_lock); - } else if (descendant_may_need_fixup(info->perm)) { + } else if (descendant_may_need_fixup(info, limit)) { spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, name, len); + fixup_perms_recursive(child, limit); } spin_unlock(&dentry->d_lock); } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 0b3fb50b1fe4..6eb73ddc2ceb 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -31,11 +31,13 @@ struct hashtable_entry { struct hlist_node hlist; + struct hlist_node dlist; /* for deletion cleanup */ const char *key; atomic_t value; }; static DEFINE_HASHTABLE(package_to_appid, 8); +static DEFINE_HASHTABLE(package_to_userid, 8); static struct kmem_cache *hashtable_entry_cachep; @@ -69,6 +71,22 @@ appid_t get_appid(const char *app_name) return 0; } +appid_t is_excluded(const char *app_name, userid_t user) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(app_name); + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == user && !strcasecmp(app_name, hash_cur->key)) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); + return 0; +} + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ @@ -124,7 +142,7 @@ static struct hashtable_entry *alloc_packagelist_entry(const char *key, return ret; } -static int insert_packagelist_entry_locked(const char *key, appid_t value) +static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; @@ -143,18 +161,64 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value) return 0; } -static void fixup_perms(struct super_block *sb, const char *key) { - if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - fixup_perms_recursive(sb->s_root, key, strlen(key)); +static int insert_userid_exclude_entry_locked(const char *key, userid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = str_hash(key); + + /* Only insert if not already present */ + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) + return 0; + } + new_entry = alloc_packagelist_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(package_to_userid, &new_entry->hlist, hash); + return 0; +} + +static void fixup_all_perms_name(const char *key) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_NAME, + .name = key, + .length = strlen(key), + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); } } -static void fixup_all_perms(const char *key) +static void fixup_all_perms_name_userid(const char *key, userid_t userid) { struct sdcardfs_sb_info *sbinfo; - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) - if (sbinfo) - fixup_perms(sbinfo->sb, key); + struct limit_search limit = { + .flags = BY_NAME | BY_USERID, + .name = key, + .length = strlen(key), + .userid = userid, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } +} + +static void fixup_all_perms_userid(userid_t userid) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_USERID, + .userid = userid, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } } static int insert_packagelist_entry(const char *key, appid_t value) @@ -162,9 +226,22 @@ static int insert_packagelist_entry(const char *key, appid_t value) int err; mutex_lock(&sdcardfs_super_list_lock); - err = insert_packagelist_entry_locked(key, value); + err = insert_packagelist_appid_entry_locked(key, value); if (!err) - fixup_all_perms(key); + fixup_all_perms_name(key); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; +} + +static int insert_userid_exclude_entry(const char *key, userid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_userid_exclude_entry_locked(key, value); + if (!err) + fixup_all_perms_name_userid(key, value); mutex_unlock(&sdcardfs_super_list_lock); return err; @@ -173,7 +250,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) static void free_packagelist_entry(struct hashtable_entry *entry) { kfree(entry->key); - hash_del_rcu(&entry->hlist); + hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } @@ -181,22 +258,84 @@ static void remove_packagelist_entry_locked(const char *key) { struct hashtable_entry *hash_cur; unsigned int hash = str_hash(key); + struct hlist_node *h_t; + HLIST_HEAD(free_list); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); - synchronize_rcu(); - free_packagelist_entry(hash_cur); - return; + hlist_add_head(&hash_cur->dlist, &free_list); + break; } } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + free_packagelist_entry(hash_cur); } static void remove_packagelist_entry(const char *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); - fixup_all_perms(key); + fixup_all_perms_name(key); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + +static void remove_userid_all_entry_locked(userid_t userid) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + int i; + + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + if (atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) { + free_packagelist_entry(hash_cur); + } +} + +static void remove_userid_all_entry(userid_t userid) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_all_entry_locked(userid); + fixup_all_perms_userid(userid); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + +static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_packagelist_entry(hash_cur); + break; + } + } +} + +static void remove_userid_exclude_entry(const char *key, userid_t userid) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_exclude_entry_locked(key, userid); + fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); return; } @@ -210,16 +349,44 @@ static void packagelist_destroy(void) mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); - hlist_add_head(&hash_cur->hlist, &free_list); - + hlist_add_head(&hash_cur->dlist, &free_list); + } + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); } synchronize_rcu(); - hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist) + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_packagelist_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } +#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO | S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ + .store = _pfx##_name##_store, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .store = _pfx##_name##_store, \ +} + struct package_details { struct config_item item; const char *name; @@ -253,6 +420,58 @@ static ssize_t package_details_appid_store(struct config_item *item, return count; } +static ssize_t package_details_excluded_userids_show(struct config_item *item, + char *page) +{ + struct package_details *package_details = to_package_details(item); + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(package_details->name); + int count = 0; + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(package_details->name, hash_cur->key)) + count += scnprintf(page + count, PAGE_SIZE - count, + "%d ", atomic_read(&hash_cur->value)); + } + rcu_read_unlock(); + if (count) + count--; + count += scnprintf(page + count, PAGE_SIZE - count, "\n"); + return count; +} + +static ssize_t package_details_excluded_userids_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + + ret = insert_userid_exclude_entry(to_package_details(item)->name, tmp); + + if (ret) + return ret; + + return count; +} + +static ssize_t package_details_clear_userid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_exclude_entry(to_package_details(item)->name, tmp); + return count; +} + static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); @@ -262,10 +481,14 @@ static void package_details_release(struct config_item *item) kfree(package_details); } -CONFIGFS_ATTR(package_details_, appid); +SDCARDFS_CONFIGFS_ATTR(package_details_, appid); +SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids); +SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid); static struct configfs_attribute *package_details_attrs[] = { &package_details_attr_appid, + &package_details_attr_excluded_userids, + &package_details_attr_clear_userid, NULL, }; @@ -293,23 +516,33 @@ static struct config_item *packages_make_item(struct config_group *group, const } config_item_init_type_name(&package_details->item, name, - &package_appid_type); + &package_appid_type); return &package_details->item; } static ssize_t packages_list_show(struct config_item *item, char *page) { - struct hashtable_entry *hash_cur; + struct hashtable_entry *hash_cur_app; + struct hashtable_entry *hash_cur_user; int i; int count = 0, written = 0; const char errormsg[] = "\n"; + unsigned int hash; rcu_read_lock(); - hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) { written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", - (const char *)hash_cur->key, atomic_read(&hash_cur->value)); - if (count + written == PAGE_SIZE - sizeof(errormsg)) { + hash_cur_app->key, atomic_read(&hash_cur_app->value)); + hash = str_hash(hash_cur_app->key); + hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { + if (!strcasecmp(hash_cur_app->key, hash_cur_user->key)) { + written += scnprintf(page + count + written - 1, + PAGE_SIZE - sizeof(errormsg) - count - written + 1, + " %d\n", atomic_read(&hash_cur_user->value)) - 1; + } + } + if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) { count += scnprintf(page + count, PAGE_SIZE - count, errormsg); break; } @@ -320,6 +553,19 @@ static ssize_t packages_list_show(struct config_item *item, char *page) return count; } +static ssize_t packages_remove_userid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_all_entry(tmp); + return count; +} + static struct configfs_attribute packages_attr_packages_gid_list = { .ca_name = "packages_gid.list", .ca_mode = S_IRUGO, @@ -327,8 +573,11 @@ static struct configfs_attribute packages_attr_packages_gid_list = { .show = packages_list_show, }; +SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid); + static struct configfs_attribute *packages_attrs[] = { &packages_attr_packages_gid_list, + &packages_attr_remove_userid, NULL, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 66a97ef8d261..9f287a65338c 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -335,6 +335,11 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) +{ + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; +} + /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { @@ -442,18 +447,28 @@ extern struct list_head sdcardfs_super_list; /* for packagelist.c */ extern appid_t get_appid(const char *app_name); +extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ +#define BY_NAME (1 << 0) +#define BY_USERID (1 << 1) +struct limit_search { + unsigned int flags; + const char *name; + size_t length; + userid_t userid; +}; + extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); extern void fixup_top_recursive(struct dentry *parent); -extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len); +extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); From d83fb1f41dd42c89a70c9276a133203043ddbb63 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 26 Jan 2017 20:10:34 -0800 Subject: [PATCH 0561/1276] ANDROID: sdcardfs: Remove redundant operation We call get_derived_permission_new unconditionally, so we don't need to call update_derived_permission_lock, which does the same thing. Signed-off-by: Daniel Rosenberg Change-Id: I0748100828c6af806da807241a33bf42be614935 --- fs/sdcardfs/inode.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4bafdf783354..d2b404c7da14 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -467,7 +467,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dir_dentry = NULL; struct vfsmount *lower_mnt = NULL; struct dentry *trap = NULL; - struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; @@ -520,17 +519,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir != old_dir) { sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); - - /* update the derived permission of the old_dentry - * with its new parent - */ - new_parent = dget_parent(new_dentry); - if(new_parent) { - if(d_inode(old_dentry)) { - update_derived_permission_lock(old_dentry); - } - dput(new_parent); - } } /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ From 8767af17c0e5638eeeba0a790830a2ebe6d5be76 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 25 Jan 2017 13:48:45 -0800 Subject: [PATCH 0562/1276] ANDROID: sdcardfs: Add GID Derivation to sdcardfs This changes sdcardfs to modify the user and group in the underlying filesystem depending on its usage. Ownership is set by Android user, and package, as well as if the file is under obb or cache. Other files can be labeled by extension. Those values are set via the configfs interace. To add an entry, mkdir -p [configfs root]/sdcardfs/extensions/[gid]/[ext] Signed-off-by: Daniel Rosenberg Bug: 34262585 Change-Id: I4e030ce84f094a678376349b1a96923e5076a0f4 --- fs/sdcardfs/derived_perm.c | 239 ++++++++++++++++++++++++++++++------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/inode.c | 42 ++++--- fs/sdcardfs/lookup.c | 3 +- fs/sdcardfs/multiuser.h | 31 +++-- fs/sdcardfs/packagelist.c | 231 ++++++++++++++++++++++++++++++++--- fs/sdcardfs/sdcardfs.h | 35 ++++-- 7 files changed, 487 insertions(+), 96 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index a6d87d900ce5..16f05172f9bf 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -30,6 +30,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->under_android = pi->under_android; + ci->under_cache = pi->under_cache; + ci->under_obb = pi->under_obb; set_top(ci, pi->top); } @@ -43,11 +45,13 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, info->userid = userid; info->d_uid = uid; info->under_android = under_android; + info->under_cache = false; + info->under_obb = false; set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); @@ -57,63 +61,185 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st * the properties are maintained on its private fields * because the inode attributes will be modified with that of * its lower inode. - * The derived state will be updated on the last - * stage of each system call by fix_derived_permission(inode). + * These values are used by our custom permission call instead + * of using the inode permissions. */ inherit_derived_state(d_inode(parent), d_inode(dentry)); + /* Files don't get special labels */ + if (!S_ISDIR(d_inode(dentry)->i_mode)) + return; /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { - case PERM_INHERIT: - /* Already inherited above */ - break; - case PERM_PRE_ROOT: - /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); + case PERM_INHERIT: + case PERM_ANDROID_PACKAGE_CACHE: + /* Already inherited above */ + break; + case PERM_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(name, NULL, 10); + set_top(info, &info->vfs_inode); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + if (!strcasecmp(name, "Android")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->under_android = true; set_top(info, &info->vfs_inode); - break; - case PERM_ROOT: - /* Assume masked off by default. */ - if (!strcasecmp(newdentry->d_name.name, "Android")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID: - if (!strcasecmp(newdentry->d_name.name, "data")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); - } else if (!strcasecmp(newdentry->d_name.name, "obb")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - set_top(info, &info->vfs_inode); - /* Single OBB directory is always shared */ - } else if (!strcasecmp(newdentry->d_name.name, "media")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID_DATA: - case PERM_ANDROID_OBB: - case PERM_ANDROID_MEDIA: - appid = get_appid(newdentry->d_name.name); - if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) { - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } + } + break; + case PERM_ANDROID: + if (!strcasecmp(name, "data")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; + set_top(info, &info->vfs_inode); + } else if (!strcasecmp(name, "obb")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->under_obb = true; set_top(info, &info->vfs_inode); - break; + /* Single OBB directory is always shared */ + } else if (!strcasecmp(name, "media")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); + } + break; + case PERM_ANDROID_OBB: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + info->perm = PERM_ANDROID_PACKAGE; + appid = get_appid(name); + if (appid != 0 && !is_excluded(name, parent_info->userid)) { + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + } + set_top(info, &info->vfs_inode); + break; + case PERM_ANDROID_PACKAGE: + if (!strcasecmp(name, "cache")) { + info->perm = PERM_ANDROID_PACKAGE_CACHE; + info->under_cache = true; + } + break; } } void get_derived_permission(struct dentry *parent, struct dentry *dentry) { - get_derived_permission_new(parent, dentry, dentry); + get_derived_permission_new(parent, dentry, dentry->d_name.name); +} + +static appid_t get_type(const char *name) +{ + const char *ext = strrchr(name, '.'); + appid_t id; + + if (ext && ext[0]) { + ext = &ext[1]; + id = get_ext_gid(ext); + return id?:AID_MEDIA_RW; + } + return AID_MEDIA_RW; +} + +void fixup_lower_ownership(struct dentry *dentry, const char *name) +{ + struct path path; + struct inode *inode; + struct inode *delegated_inode = NULL; + int error; + struct sdcardfs_inode_info *info; + struct sdcardfs_inode_info *info_top; + perm_t perm; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + uid_t uid = sbi->options.fs_low_uid; + gid_t gid = sbi->options.fs_low_gid; + struct iattr newattrs; + + info = SDCARDFS_I(d_inode(dentry)); + perm = info->perm; + if (info->under_obb) { + perm = PERM_ANDROID_OBB; + } else if (info->under_cache) { + perm = PERM_ANDROID_PACKAGE_CACHE; + } else if (perm == PERM_INHERIT) { + info_top = SDCARDFS_I(grab_top(info)); + perm = info_top->perm; + release_top(info); + } + + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + case PERM_ANDROID_PACKAGE: + case PERM_ANDROID_PACKAGE_CACHE: + uid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_OBB: + uid = AID_MEDIA_OBB; + break; + case PERM_PRE_ROOT: + default: + break; + } + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + if (S_ISDIR(d_inode(dentry)->i_mode)) + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + else + gid = multiuser_get_uid(info->userid, get_type(name)); + break; + case PERM_ANDROID_OBB: + gid = AID_MEDIA_OBB; + break; + case PERM_ANDROID_PACKAGE: + if (info->d_uid != 0) + gid = multiuser_get_ext_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_PACKAGE_CACHE: + if (info->d_uid != 0) + gid = multiuser_get_cache_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_PRE_ROOT: + default: + break; + } + + sdcardfs_get_lower_path(dentry, &path); + inode = d_inode(path.dentry); + if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) { +retry_deleg: + newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE; + newattrs.ia_uid = make_kuid(current_user_ns(), uid); + newattrs.ia_gid = make_kgid(current_user_ns(), gid); + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + inode_lock(inode); + error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid); + if (!error) + error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode); + inode_unlock(inode); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry_deleg; + } + if (error) + pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + } } static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) @@ -169,9 +295,30 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) dput(dentry); } -void fixup_top_recursive(struct dentry *parent) { +void drop_recursive(struct dentry *parent) +{ struct dentry *dentry; struct sdcardfs_inode_info *info; + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); + spin_lock(&parent->d_lock); + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + drop_recursive(dentry); + d_drop(dentry); + } + } + } + spin_unlock(&parent->d_lock); +} + +void fixup_top_recursive(struct dentry *parent) +{ + struct dentry *dentry; + struct sdcardfs_inode_info *info; + if (!d_inode(parent)) return; info = SDCARDFS_I(d_inode(parent)); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 7750a0472389..006c6ff57ad7 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred); + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index d2b404c7da14..103558987362 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,16 +22,21 @@ #include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) { - struct cred * cred; - const struct cred * old_cred; + struct cred *cred; + const struct cred *old_cred; + uid_t uid; cred = prepare_creds(); if (!cred) return NULL; - cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); + if (info->under_obb) + uid = AID_MEDIA_OBB; + else + uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); old_cred = override_creds(cred); @@ -40,9 +45,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) } /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred) +void revert_fsids(const struct cred *old_cred) { - const struct cred * cur_cred; + const struct cred *cur_cred; cur_cred = current->cred; revert_creds(old_cred); @@ -70,7 +75,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -98,6 +103,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); out: current->fs = saved_fs; @@ -171,7 +177,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -279,7 +285,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { @@ -343,9 +349,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); - + fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); - if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -353,6 +358,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + REVERT_CRED(saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { @@ -390,7 +397,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. */ @@ -483,7 +490,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -520,11 +527,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); } - /* At this point, not all dentry information has been moved, so - * we pass along new_dentry for the name.*/ - get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); + get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry->d_name.name); fixup_tmp_permissions(d_inode(old_dentry)); - fixup_top_recursive(old_dentry); + fixup_lower_ownership(old_dentry, new_dentry->d_name.name); + drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -759,7 +765,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct goto out_err; /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index d271617290ee..1d664c9e2942 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -368,7 +368,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -392,6 +392,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, /* get derived permission */ get_derived_permission(parent, dentry); fixup_tmp_permissions(d_inode(dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); } /* update parent directory's atime */ fsstack_copy_attr_atime(d_inode(parent), diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 923ba101dfa9..950cffddc556 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -18,20 +18,35 @@ * General Public License. */ -#define MULTIUSER_APP_PER_USER_RANGE 100000 +#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ +#define AID_APP_START 10000 /* first app user */ +#define AID_APP_END 19999 /* last app user */ +#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ +#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; typedef uid_t appid_t; -static inline userid_t multiuser_get_user_id(uid_t uid) { - return uid / MULTIUSER_APP_PER_USER_RANGE; +static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) +{ + return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline appid_t multiuser_get_app_id(uid_t uid) { - return uid % MULTIUSER_APP_PER_USER_RANGE; +static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) +{ + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); + } else { + return -1; + } } -static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) { - return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) +{ + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); + } else { + return -1; + } } - diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 6eb73ddc2ceb..8ef0b0797872 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -21,6 +21,7 @@ #include "sdcardfs.h" #include #include +#include #include @@ -38,6 +39,8 @@ struct hashtable_entry { static DEFINE_HASHTABLE(package_to_appid, 8); static DEFINE_HASHTABLE(package_to_userid, 8); +static DEFINE_HASHTABLE(ext_to_groupid, 8); + static struct kmem_cache *hashtable_entry_cachep; @@ -53,15 +56,33 @@ static unsigned int str_hash(const char *key) { return h; } -appid_t get_appid(const char *app_name) +appid_t get_appid(const char *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(app_name); + unsigned int hash = str_hash(key); appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(app_name, hash_cur->key)) { + if (!strcasecmp(key, hash_cur->key)) { + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); + return ret_id; + } + } + rcu_read_unlock(); + return 0; +} + +appid_t get_ext_gid(const char *key) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + appid_t ret_id; + + rcu_read_lock(); + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -90,8 +111,8 @@ appid_t is_excluded(const char *app_name, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char* name) { - +int check_caller_access_to_name(struct inode *parent_node, const char *name) +{ /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { if (!strcasecmp(name, "autorun.inf") @@ -124,7 +145,7 @@ int open_flags_to_access_mode(int open_flags) { } } -static struct hashtable_entry *alloc_packagelist_entry(const char *key, +static struct hashtable_entry *alloc_hashtable_entry(const char *key, appid_t value) { struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, @@ -154,13 +175,31 @@ static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) return 0; } } - new_entry = alloc_packagelist_entry(key, value); + new_entry = alloc_hashtable_entry(key, value); if (!new_entry) return -ENOMEM; hash_add_rcu(package_to_appid, &new_entry->hlist, hash); return 0; } +static int insert_ext_gid_entry_locked(const char *key, appid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = str_hash(key); + + /* An extension can only belong to one gid */ + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) + return -EINVAL; + } + new_entry = alloc_hashtable_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash); + return 0; +} + static int insert_userid_exclude_entry_locked(const char *key, userid_t value) { struct hashtable_entry *hash_cur; @@ -172,7 +211,7 @@ static int insert_userid_exclude_entry_locked(const char *key, userid_t value) if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) return 0; } - new_entry = alloc_packagelist_entry(key, value); + new_entry = alloc_hashtable_entry(key, value); if (!new_entry) return -ENOMEM; hash_add_rcu(package_to_userid, &new_entry->hlist, hash); @@ -234,6 +273,17 @@ static int insert_packagelist_entry(const char *key, appid_t value) return err; } +static int insert_ext_gid_entry(const char *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_ext_gid_entry_locked(key, value); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; +} + static int insert_userid_exclude_entry(const char *key, userid_t value) { int err; @@ -247,7 +297,7 @@ static int insert_userid_exclude_entry(const char *key, userid_t value) return err; } -static void free_packagelist_entry(struct hashtable_entry *entry) +static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key); hash_del_rcu(&entry->dlist); @@ -276,7 +326,7 @@ static void remove_packagelist_entry_locked(const char *key) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); } static void remove_packagelist_entry(const char *key) @@ -288,6 +338,29 @@ static void remove_packagelist_entry(const char *key) return; } +static void remove_ext_gid_entry_locked(const char *key, gid_t group) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == group) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_hashtable_entry(hash_cur); + break; + } + } +} + +static void remove_ext_gid_entry(const char *key, gid_t group) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_ext_gid_entry_locked(key, group); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + static void remove_userid_all_entry_locked(userid_t userid) { struct hashtable_entry *hash_cur; @@ -303,7 +376,7 @@ static void remove_userid_all_entry_locked(userid_t userid) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) { - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); } } @@ -325,7 +398,7 @@ static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); break; } } @@ -357,7 +430,7 @@ static void packagelist_destroy(void) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } @@ -502,6 +575,127 @@ static struct config_item_type package_appid_type = { .ct_owner = THIS_MODULE, }; +struct extensions_value { + struct config_group group; + unsigned int num; +}; + +struct extension_details { + struct config_item item; + const char *name; + unsigned int num; +}; + +static inline struct extensions_value *to_extensions_value(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL; +} + +static inline struct extension_details *to_extension_details(struct config_item *item) +{ + return item ? container_of(item, struct extension_details, item) : NULL; +} + +static void extension_details_release(struct config_item *item) +{ + struct extension_details *extension_details = to_extension_details(item); + + printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + extension_details->name, extension_details->num); + remove_ext_gid_entry(extension_details->name, extension_details->num); + kfree(extension_details->name); + kfree(extension_details); +} + +static struct configfs_item_operations extension_details_item_ops = { + .release = extension_details_release, +}; + +static struct config_item_type extension_details_type = { + .ct_item_ops = &extension_details_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *extension_details_make_item(struct config_group *group, const char *name) +{ + struct extensions_value *extensions_value = to_extensions_value(&group->cg_item); + struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); + int ret; + if (!extension_details) + return ERR_PTR(-ENOMEM); + + extension_details->name = kstrdup(name, GFP_KERNEL); + if (!extension_details->name) { + kfree(extension_details); + return ERR_PTR(-ENOMEM); + } + extension_details->num = extensions_value->num; + ret = insert_ext_gid_entry(name, extensions_value->num); + + if (ret) { + kfree(extension_details->name); + kfree(extension_details); + return ERR_PTR(ret); + } + config_item_init_type_name(&extension_details->item, name, &extension_details_type); + + return &extension_details->item; +} + +static struct configfs_group_operations extensions_value_group_ops = { + .make_item = extension_details_make_item, +}; + +static struct config_item_type extensions_name_type = { + .ct_group_ops = &extensions_value_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *extensions_make_group(struct config_group *group, const char *name) +{ + struct extensions_value *extensions_value; + unsigned int tmp; + int ret; + + extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL); + if (!extensions_value) + return ERR_PTR(-ENOMEM); + ret = kstrtouint(name, 10, &tmp); + if (ret) { + kfree(extensions_value); + return ERR_PTR(ret); + } + + extensions_value->num = tmp; + config_group_init_type_name(&extensions_value->group, name, + &extensions_name_type); + return &extensions_value->group; +} + +static void extensions_drop_group(struct config_group *group, struct config_item *item) +{ + struct extensions_value *value = to_extensions_value(item); + printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + kfree(value); +} + +static struct configfs_group_operations extensions_group_ops = { + .make_group = extensions_make_group, + .drop_item = extensions_drop_group, +}; + +static struct config_item_type extensions_type = { + .ct_group_ops = &extensions_group_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group extension_group = { + .cg_item = { + .ci_namebuf = "extensions", + .ci_type = &extensions_type, + }, +}; + static struct config_item *packages_make_item(struct config_group *group, const char *name) { struct package_details *package_details; @@ -595,6 +789,11 @@ static struct config_item_type packages_type = { .ct_owner = THIS_MODULE, }; +struct config_group *sd_default_groups[] = { + &extension_group, + NULL, +}; + static struct configfs_subsystem sdcardfs_packages = { .su_group = { .cg_item = { @@ -606,10 +805,14 @@ static struct configfs_subsystem sdcardfs_packages = { static int configfs_sdcardfs_init(void) { - int ret; + int ret, i; struct configfs_subsystem *subsys = &sdcardfs_packages; config_group_init(&subsys->su_group); + for (i = 0; sd_default_groups[i]; i++) { + config_group_init(sd_default_groups[i]); + configfs_add_default_group(sd_default_groups[i], &subsys->su_group); + } mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 9f287a65338c..87a57ce25785 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -65,6 +65,9 @@ #define AID_SDCARD_PICS 1033 /* external storage photos access */ #define AID_SDCARD_AV 1034 /* external storage audio/video access */ #define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_MEDIA_OBB 1059 /* obb files */ + +#define AID_SDCARD_IMAGE 1057 #define AID_PACKAGE_INFO 1027 @@ -91,13 +94,19 @@ * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ -#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ - if (!saved_cred) { return -ENOMEM; } +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ + if (!saved_cred) \ + return -ENOMEM; \ + } while (0) -#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ - if (!saved_cred) { return ERR_PTR(-ENOMEM); } +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ + } while (0) #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) @@ -127,13 +136,18 @@ typedef enum { PERM_ANDROID_OBB, /* This node is "/Android/media" */ PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; struct sdcardfs_mount_options; +struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred * old_cred); @@ -175,6 +189,8 @@ struct sdcardfs_inode_info { userid_t userid; uid_t d_uid; bool under_android; + bool under_cache; + bool under_obb; /* top folder for ownership */ struct inode *top; @@ -447,6 +463,7 @@ extern struct list_head sdcardfs_super_list; /* for packagelist.c */ extern appid_t get_appid(const char *app_name); +extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); @@ -466,11 +483,13 @@ struct limit_search { extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name); +extern void drop_recursive(struct dentry *parent); extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); +void fixup_lower_ownership(struct dentry *dentry, const char *name); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); From 7119d96ad3ee5eabababc7f8f70df6d5abf68007 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 27 Jan 2017 19:35:08 -0800 Subject: [PATCH 0563/1276] ANDROID: sdcardfs: switch to full_name_hash and qstr Use the kernel's string hash function instead of rolling our own. Additionally, save a bit of calculation by using the qstr struct in place of strings. Signed-off-by: Daniel Rosenberg Change-Id: I0bbeb5ec2a9233f40135ad632e6f22c30ffa95c1 --- fs/sdcardfs/packagelist.c | 191 ++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 81 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 8ef0b0797872..2b0040f95396 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -22,7 +22,7 @@ #include #include #include - +#include #include #include @@ -33,7 +33,7 @@ struct hashtable_entry { struct hlist_node hlist; struct hlist_node dlist; /* for deletion cleanup */ - const char *key; + struct qstr key; atomic_t value; }; @@ -44,27 +44,30 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; -static unsigned int str_hash(const char *key) { - int i; - unsigned int h = strlen(key); - char *data = (char *)key; +static inline void qstr_init(struct qstr *q, const char *name) +{ + q->name = name; + q->len = strlen(q->name); + q->hash = full_name_hash(0, q->name, q->len); +} - for (i = 0; i < strlen(key); i++) { - h = h * 31 + *data; - data++; - } - return h; +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) +{ + dest->name = kstrdup(src->name, GFP_KERNEL); + dest->hash_len = src->hash_len; + return !!dest->name; } -appid_t get_appid(const char *key) + +static appid_t __get_appid(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -74,15 +77,22 @@ appid_t get_appid(const char *key) return 0; } -appid_t get_ext_gid(const char *key) +appid_t get_appid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_appid(&q); +} + +static appid_t __get_ext_gid(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -92,14 +102,22 @@ appid_t get_ext_gid(const char *key) return 0; } -appid_t is_excluded(const char *app_name, userid_t user) +appid_t get_ext_gid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_ext_gid(&q); +} + +static appid_t __is_excluded(const struct qstr *app_name, userid_t user) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(app_name); + unsigned int hash = app_name->hash; rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (atomic_read(&hash_cur->value) == user && !strcasecmp(app_name, hash_cur->key)) { + if (atomic_read(&hash_cur->value) == user && + !strcasecmp(app_name->name, hash_cur->key.name)) { rcu_read_unlock(); return 1; } @@ -108,6 +126,14 @@ appid_t is_excluded(const char *app_name, userid_t user) return 0; } +appid_t is_excluded(const char *app_name, userid_t user) +{ + struct qstr q; + qstr_init(&q, app_name); + return __is_excluded(&q, user); +} + + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ @@ -145,7 +171,7 @@ int open_flags_to_access_mode(int open_flags) { } } -static struct hashtable_entry *alloc_hashtable_entry(const char *key, +static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, appid_t value) { struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, @@ -153,8 +179,7 @@ static struct hashtable_entry *alloc_hashtable_entry(const char *key, if (!ret) return NULL; - ret->key = kstrdup(key, GFP_KERNEL); - if (!ret->key) { + if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); return NULL; } @@ -163,14 +188,14 @@ static struct hashtable_entry *alloc_hashtable_entry(const char *key, return ret; } -static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) +static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { atomic_set(&hash_cur->value, value); return 0; } @@ -182,15 +207,15 @@ static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) return 0; } -static int insert_ext_gid_entry_locked(const char *key, appid_t value) +static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; /* An extension can only belong to one gid */ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) + if (!strcasecmp(key->name, hash_cur->key.name)) return -EINVAL; } new_entry = alloc_hashtable_entry(key, value); @@ -200,15 +225,16 @@ static int insert_ext_gid_entry_locked(const char *key, appid_t value) return 0; } -static int insert_userid_exclude_entry_locked(const char *key, userid_t value) +static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; /* Only insert if not already present */ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) + if (atomic_read(&hash_cur->value) == value && + !strcasecmp(key->name, hash_cur->key.name)) return 0; } new_entry = alloc_hashtable_entry(key, value); @@ -218,13 +244,13 @@ static int insert_userid_exclude_entry_locked(const char *key, userid_t value) return 0; } -static void fixup_all_perms_name(const char *key) +static void fixup_all_perms_name(const struct qstr *key) { struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key, - .length = strlen(key), + .name = key->name, + .length = key->len, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -232,13 +258,13 @@ static void fixup_all_perms_name(const char *key) } } -static void fixup_all_perms_name_userid(const char *key, userid_t userid) +static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) { struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key, - .length = strlen(key), + .name = key->name, + .length = key->len, .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { @@ -260,7 +286,7 @@ static void fixup_all_perms_userid(userid_t userid) } } -static int insert_packagelist_entry(const char *key, appid_t value) +static int insert_packagelist_entry(const struct qstr *key, appid_t value) { int err; @@ -273,7 +299,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) return err; } -static int insert_ext_gid_entry(const char *key, appid_t value) +static int insert_ext_gid_entry(const struct qstr *key, appid_t value) { int err; @@ -284,7 +310,7 @@ static int insert_ext_gid_entry(const char *key, appid_t value) return err; } -static int insert_userid_exclude_entry(const char *key, userid_t value) +static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) { int err; @@ -299,26 +325,26 @@ static int insert_userid_exclude_entry(const char *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { - kfree(entry->key); + kfree(entry->key.name); hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } -static void remove_packagelist_entry_locked(const char *key) +static void remove_packagelist_entry_locked(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; struct hlist_node *h_t; HLIST_HEAD(free_list); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); } } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); break; @@ -329,7 +355,7 @@ static void remove_packagelist_entry_locked(const char *key) free_hashtable_entry(hash_cur); } -static void remove_packagelist_entry(const char *key) +static void remove_packagelist_entry(const struct qstr *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); @@ -338,13 +364,13 @@ static void remove_packagelist_entry(const char *key) return; } -static void remove_ext_gid_entry_locked(const char *key, gid_t group) +static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == group) { + if (!strcasecmp(key->name, hash_cur->key.name) && atomic_read(&hash_cur->value) == group) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -353,7 +379,7 @@ static void remove_ext_gid_entry_locked(const char *key, gid_t group) } } -static void remove_ext_gid_entry(const char *key, gid_t group) +static void remove_ext_gid_entry(const struct qstr *key, gid_t group) { mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); @@ -389,13 +415,14 @@ static void remove_userid_all_entry(userid_t userid) return; } -static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) +static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { + if (!strcasecmp(key->name, hash_cur->key.name) && + atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -404,7 +431,7 @@ static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) } } -static void remove_userid_exclude_entry(const char *key, userid_t userid) +static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) { mutex_lock(&sdcardfs_super_list_lock); remove_userid_exclude_entry_locked(key, userid); @@ -462,7 +489,7 @@ static struct configfs_attribute _pfx##attr_##_name = { \ struct package_details { struct config_item item; - const char *name; + struct qstr name; }; static inline struct package_details *to_package_details(struct config_item *item) @@ -472,7 +499,7 @@ static inline struct package_details *to_package_details(struct config_item *ite static ssize_t package_details_appid_show(struct config_item *item, char *page) { - return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(to_package_details(item)->name)); + return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&to_package_details(item)->name)); } static ssize_t package_details_appid_store(struct config_item *item, @@ -485,7 +512,7 @@ static ssize_t package_details_appid_store(struct config_item *item, if (ret) return ret; - ret = insert_packagelist_entry(to_package_details(item)->name, tmp); + ret = insert_packagelist_entry(&to_package_details(item)->name, tmp); if (ret) return ret; @@ -498,12 +525,12 @@ static ssize_t package_details_excluded_userids_show(struct config_item *item, { struct package_details *package_details = to_package_details(item); struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(package_details->name); + unsigned int hash = package_details->name.hash; int count = 0; rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(package_details->name, hash_cur->key)) + if (!strcasecmp(package_details->name.name, hash_cur->key.name)) count += scnprintf(page + count, PAGE_SIZE - count, "%d ", atomic_read(&hash_cur->value)); } @@ -524,7 +551,7 @@ static ssize_t package_details_excluded_userids_store(struct config_item *item, if (ret) return ret; - ret = insert_userid_exclude_entry(to_package_details(item)->name, tmp); + ret = insert_userid_exclude_entry(&to_package_details(item)->name, tmp); if (ret) return ret; @@ -541,16 +568,16 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, ret = kstrtouint(page, 10, &tmp); if (ret) return ret; - remove_userid_exclude_entry(to_package_details(item)->name, tmp); + remove_userid_exclude_entry(&to_package_details(item)->name, tmp); return count; } static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name); - remove_packagelist_entry(package_details->name); - kfree(package_details->name); + printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + remove_packagelist_entry(&package_details->name); + kfree(package_details->name.name); kfree(package_details); } @@ -582,7 +609,7 @@ struct extensions_value { struct extension_details { struct config_item item; - const char *name; + struct qstr name; unsigned int num; }; @@ -601,9 +628,9 @@ static void extension_details_release(struct config_item *item) struct extension_details *extension_details = to_extension_details(item); printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", - extension_details->name, extension_details->num); - remove_ext_gid_entry(extension_details->name, extension_details->num); - kfree(extension_details->name); + extension_details->name.name, extension_details->num); + remove_ext_gid_entry(&extension_details->name, extension_details->num); + kfree(extension_details->name.name); kfree(extension_details); } @@ -620,20 +647,21 @@ static struct config_item *extension_details_make_item(struct config_group *grou { struct extensions_value *extensions_value = to_extensions_value(&group->cg_item); struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); + const char *tmp; int ret; if (!extension_details) return ERR_PTR(-ENOMEM); - extension_details->name = kstrdup(name, GFP_KERNEL); - if (!extension_details->name) { + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { kfree(extension_details); return ERR_PTR(-ENOMEM); } - extension_details->num = extensions_value->num; - ret = insert_ext_gid_entry(name, extensions_value->num); + qstr_init(&extension_details->name, tmp); + ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); if (ret) { - kfree(extension_details->name); + kfree(extension_details->name.name); kfree(extension_details); return ERR_PTR(ret); } @@ -699,16 +727,17 @@ struct config_group extension_group = { static struct config_item *packages_make_item(struct config_group *group, const char *name) { struct package_details *package_details; + const char *tmp; package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL); if (!package_details) return ERR_PTR(-ENOMEM); - package_details->name = kstrdup(name, GFP_KERNEL); - if (!package_details->name) { + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { kfree(package_details); return ERR_PTR(-ENOMEM); } - + qstr_init(&package_details->name, tmp); config_item_init_type_name(&package_details->item, name, &package_appid_type); @@ -727,13 +756,13 @@ static ssize_t packages_list_show(struct config_item *item, char *page) rcu_read_lock(); hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) { written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", - hash_cur_app->key, atomic_read(&hash_cur_app->value)); - hash = str_hash(hash_cur_app->key); + hash_cur_app->key.name, atomic_read(&hash_cur_app->value)); + hash = hash_cur_app->key.hash; hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { - if (!strcasecmp(hash_cur_app->key, hash_cur_user->key)) { + if (!strcasecmp(hash_cur_app->key.name, hash_cur_user->key.name)) { written += scnprintf(page + count + written - 1, PAGE_SIZE - sizeof(errormsg) - count - written + 1, - " %d\n", atomic_read(&hash_cur_user->value)) - 1; + " %d\n", atomic_read(&hash_cur_user->value)) - 1; } } if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) { From 778e02a54859b3b07286e637f87e421f56f39db1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 31 Jan 2017 20:07:51 -0800 Subject: [PATCH 0564/1276] ANDROID: sdcardfs: Switch strcasecmp for internal call This moves our uses of strcasecmp over to an internal call so we can easily change implementations later if we so desire. Additionally, we leverage qstr's where appropriate to save time on comparisons. Signed-off-by: Daniel Rosenberg Change-Id: I32fdc4fd0cd3b7b735dcfd82f60a2516fd8272a5 --- fs/sdcardfs/derived_perm.c | 35 +++++++++++++++++++------------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/inode.c | 24 ++++++++++++---------- fs/sdcardfs/lookup.c | 18 +++++++---------- fs/sdcardfs/packagelist.c | 41 ++++++++++++++++++++------------------ fs/sdcardfs/sdcardfs.h | 17 ++++++++++++++-- 6 files changed, 79 insertions(+), 58 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 16f05172f9bf..4b3801885959 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -51,11 +51,16 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name) +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); appid_t appid; + struct qstr q_Android = QSTR_LITERAL("Android"); + struct qstr q_data = QSTR_LITERAL("data"); + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_media = QSTR_LITERAL("media"); + struct qstr q_cache = QSTR_LITERAL("cache"); /* By default, each inode inherits from its parent. * the properties are maintained on its private fields @@ -79,12 +84,12 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(name, NULL, 10); + info->userid = simple_strtoul(name->name, NULL, 10); set_top(info, &info->vfs_inode); break; case PERM_ROOT: /* Assume masked off by default. */ - if (!strcasecmp(name, "Android")) { + if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->under_android = true; @@ -92,17 +97,17 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co } break; case PERM_ANDROID: - if (!strcasecmp(name, "data")) { + if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; set_top(info, &info->vfs_inode); - } else if (!strcasecmp(name, "obb")) { + } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; info->under_obb = true; set_top(info, &info->vfs_inode); /* Single OBB directory is always shared */ - } else if (!strcasecmp(name, "media")) { + } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; set_top(info, &info->vfs_inode); @@ -112,14 +117,14 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: info->perm = PERM_ANDROID_PACKAGE; - appid = get_appid(name); - if (appid != 0 && !is_excluded(name, parent_info->userid)) { + appid = get_appid(name->name); + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } set_top(info, &info->vfs_inode); break; case PERM_ANDROID_PACKAGE: - if (!strcasecmp(name, "cache")) { + if (qstr_case_eq(name, &q_cache)) { info->perm = PERM_ANDROID_PACKAGE_CACHE; info->under_cache = true; } @@ -129,7 +134,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co void get_derived_permission(struct dentry *parent, struct dentry *dentry) { - get_derived_permission_new(parent, dentry, dentry->d_name.name); + get_derived_permission_new(parent, dentry, &dentry->d_name); } static appid_t get_type(const char *name) @@ -366,9 +371,10 @@ int need_graft_path(struct dentry *dentry) struct dentry *parent = dget_parent(dentry); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr obb = QSTR_LITERAL("obb"); if(parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if(!(sbi->options.multiuser == false @@ -405,7 +411,7 @@ int is_obbpath_invalid(struct dentry *dent) } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || - strcasecmp(sbi->obbpath_s, obbpath_s)) { + !str_case_eq(sbi->obbpath_s, obbpath_s)) { ret = 1; } kfree(path_buf); @@ -425,15 +431,16 @@ int is_base_obbpath(struct dentry *dentry) struct dentry *parent = dget_parent(dentry); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { if(parent_info->perm == PERM_PRE_ROOT && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } } else if (parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } spin_unlock(&SDCARDFS_D(dentry)->lock); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 006c6ff57ad7..56000a004b38 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 103558987362..4646dbb3ee03 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -66,7 +66,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *saved_fs; struct fs_struct *copied_fs; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -168,7 +168,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -275,8 +275,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int touch_err = 0; struct fs_struct *saved_fs; struct fs_struct *copied_fs; + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_data = QSTR_LITERAL("data"); - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -351,13 +353,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); - if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) + if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { REVERT_CRED(saved_cred); OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); @@ -388,7 +390,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -480,8 +482,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (flags) return -EINVAL; - if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || - !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { + if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || + !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); @@ -527,7 +529,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); } - get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry->d_name.name); + get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ @@ -752,7 +754,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -870,7 +872,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, int err; parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 1d664c9e2942..0d2caddd8510 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -219,9 +219,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; - const char *name; + const struct qstr *name; struct path lower_path; - struct qstr this; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -231,14 +230,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (IS_ROOT(dentry)) goto out; - name = dentry->d_name.name; + name = &dentry->d_name; /* now start the actual lookup procedure */ lower_dir_dentry = lower_parent_path->dentry; lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0, &lower_path); /* check for other cases */ if (err == -ENOENT) { @@ -248,7 +247,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { - if (strcasecmp(child->d_name.name, name)==0) { + if (qstr_case_eq(&child->d_name, name)) { match = dget(child); break; } @@ -307,14 +306,11 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - this.name = name; - this.len = strlen(name); - this.hash = full_name_hash(dentry, this.name, this.len); - lower_dentry = d_lookup(lower_dir_dentry, &this); + lower_dentry = d_lookup(lower_dir_dentry, name); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, &this); + lower_dentry = d_alloc(lower_dir_dentry, name); if (!lower_dentry) { err = -ENOMEM; goto out; @@ -359,7 +355,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 2b0040f95396..537ff523c32a 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -67,7 +67,7 @@ static appid_t __get_appid(const struct qstr *key) rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -92,7 +92,7 @@ static appid_t __get_ext_gid(const struct qstr *key) rcu_read_lock(); hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -117,7 +117,7 @@ static appid_t __is_excluded(const struct qstr *app_name, userid_t user) rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { if (atomic_read(&hash_cur->value) == user && - !strcasecmp(app_name->name, hash_cur->key.name)) { + qstr_case_eq(app_name, &hash_cur->key)) { rcu_read_unlock(); return 1; } @@ -126,24 +126,27 @@ static appid_t __is_excluded(const struct qstr *app_name, userid_t user) return 0; } -appid_t is_excluded(const char *app_name, userid_t user) +appid_t is_excluded(const char *key, userid_t user) { struct qstr q; - qstr_init(&q, app_name); + qstr_init(&q, key); return __is_excluded(&q, user); } - /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char *name) +int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { + struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); + struct qstr q__android_secure = QSTR_LITERAL(".android_secure"); + struct qstr q_android_secure = QSTR_LITERAL("android_secure"); + /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { - if (!strcasecmp(name, "autorun.inf") - || !strcasecmp(name, ".android_secure") - || !strcasecmp(name, "android_secure")) { + if (qstr_case_eq(name, &q_autorun) + || qstr_case_eq(name, &q__android_secure) + || qstr_case_eq(name, &q_android_secure)) { return 0; } } @@ -195,7 +198,7 @@ static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { atomic_set(&hash_cur->value, value); return 0; } @@ -215,7 +218,7 @@ static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value) /* An extension can only belong to one gid */ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) + if (qstr_case_eq(key, &hash_cur->key)) return -EINVAL; } new_entry = alloc_hashtable_entry(key, value); @@ -234,7 +237,7 @@ static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t v /* Only insert if not already present */ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { if (atomic_read(&hash_cur->value) == value && - !strcasecmp(key->name, hash_cur->key.name)) + qstr_case_eq(key, &hash_cur->key)) return 0; } new_entry = alloc_hashtable_entry(key, value); @@ -338,13 +341,13 @@ static void remove_packagelist_entry_locked(const struct qstr *key) HLIST_HEAD(free_list); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); } } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); break; @@ -370,7 +373,7 @@ static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) unsigned int hash = key->hash; hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name) && atomic_read(&hash_cur->value) == group) { + if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -421,7 +424,7 @@ static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name) && + if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); @@ -530,7 +533,7 @@ static ssize_t package_details_excluded_userids_show(struct config_item *item, rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(package_details->name.name, hash_cur->key.name)) + if (qstr_case_eq(&package_details->name, &hash_cur->key)) count += scnprintf(page + count, PAGE_SIZE - count, "%d ", atomic_read(&hash_cur->value)); } @@ -759,7 +762,7 @@ static ssize_t packages_list_show(struct config_item *item, char *page) hash_cur_app->key.name, atomic_read(&hash_cur_app->value)); hash = hash_cur_app->key.hash; hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { - if (!strcasecmp(hash_cur_app->key.name, hash_cur_user->key.name)) { + if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) { written += scnprintf(page + count + written - 1, PAGE_SIZE - sizeof(errormsg) - count - written + 1, " %d\n", atomic_read(&hash_cur_user->value)) - 1; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 87a57ce25785..62838aeb57ec 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -465,7 +465,7 @@ extern struct list_head sdcardfs_super_list; extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); -extern int check_caller_access_to_name(struct inode *parent_node, const char* name); +extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); @@ -483,7 +483,7 @@ struct limit_search { extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void drop_recursive(struct dentry *parent); extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); @@ -611,4 +611,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct dest->i_flags = src->i_flags; set_nlink(dest, src->i_nlink); } + +static inline bool str_case_eq(const char *s1, const char *s2) +{ + return !strcasecmp(s1, s2); +} + +static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) +{ + return q1->len == q2->len && str_case_eq(q1->name, q2->name); +} + +#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) + #endif /* not _SDCARDFS_H_ */ From cd4965d04404c505eae45dc52b8a1afa8c9e08e5 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 14 Feb 2017 20:47:17 -0800 Subject: [PATCH 0565/1276] ANDROID: sdcardfs: Fix incorrect hash This adds back the hash calculation removed as part of the previous patch, as it is in fact necessary. Signed-off-by: Daniel Rosenberg Bug: 35307857 Change-Id: Ie607332bcf2c5d2efdf924e4060ef3f576bf25dc --- fs/sdcardfs/lookup.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 0d2caddd8510..ae8e9b519e16 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -221,6 +221,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct dentry *lower_dentry; const struct qstr *name; struct path lower_path; + struct qstr dname; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -306,11 +307,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - lower_dentry = d_lookup(lower_dir_dentry, name); + dname.name = name->name; + dname.len = name->len; + dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); + lower_dentry = d_lookup(lower_dir_dentry, &dname); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, name); + lower_dentry = d_alloc(lower_dir_dentry, &dname); if (!lower_dentry) { err = -ENOMEM; goto out; From 40a2ee053505a23dc8e79fb426af278d01f06e16 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Feb 2017 17:55:22 -0800 Subject: [PATCH 0566/1276] ANDROID: sdcardfs: Add missing path_put "ANDROID: sdcardfs: Add GID Derivation to sdcardfs" introduced an unbalanced pat_get, leading to storage space not being freed after deleting a file until rebooting. This adds the missing path_put. Signed-off-by: Daniel Rosenberg Bug: 34691169 Change-Id: Ia7ef97ec2eca2c555cc06b235715635afc87940e --- fs/sdcardfs/derived_perm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 4b3801885959..1b5029d81921 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -245,6 +245,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) if (error) pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); } + sdcardfs_put_lower_path(dentry, &path); } static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) From da5342bac57a973c2096836fbb98fc92d761ef12 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 22 Feb 2017 14:41:58 -0800 Subject: [PATCH 0567/1276] ANDROID: sdcardfs: Don't bother deleting freelist There is no point deleting entries from dlist, as that is a temporary list on the stack from which contains only entries that are being deleted. Not all code paths set up dlist, so those that don't were performing invalid accesses in hash_del_rcu. As an additional means to prevent any other issue, we null out the list entries when we allocate from the cache. Signed-off-by: Daniel Rosenberg Bug: 35666680 Change-Id: Ibb1e28c08c3a600c29418d39ba1c0f3db3bf31e5 --- fs/sdcardfs/packagelist.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 537ff523c32a..5f81284a4f44 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -181,6 +181,8 @@ static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, GFP_KERNEL); if (!ret) return NULL; + INIT_HLIST_NODE(&ret->dlist); + INIT_HLIST_NODE(&ret->hlist); if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); @@ -329,7 +331,6 @@ static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key.name); - hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } From c91857b01e05fa210694bace7fcfbcf12ffd7302 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 24 Feb 2017 15:41:48 -0800 Subject: [PATCH 0568/1276] ANDROID: sdcardfs: implement vm_ops->page_mkwrite This comes from wrapfs commit 3dfec0ffe5e2 ("Wrapfs: implement vm_ops->page_mkwrite") Some file systems (e.g., ext4) require it. Reported by Ted Ts'o. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 34133558 Change-Id: I1a389b2422c654a6d3046bb8ec3e20511aebfa8e --- fs/sdcardfs/mmap.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index ac5f3deae088..28db1d11c99e 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,6 +48,39 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } +static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + int err = 0; + struct file *file, *lower_file; + const struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + if (!lower_vm_ops->page_mkwrite) + goto out; + + lower_file = sdcardfs_lower_file(file); + /* + * XXX: vm_ops->page_mkwrite may be called in parallel. + * Because we have to resort to temporarily changing the + * vma->vm_file to point to the lower file, a concurrent + * invocation of sdcardfs_page_mkwrite could see a different + * value. In this workaround, we keep a different copy of the + * vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. + * A better fix would be to change the calling semantics of + * ->page_mkwrite to take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + err = lower_vm_ops->page_mkwrite(&lower_vma, vmf); +out: + return err; +} + static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* @@ -77,4 +110,5 @@ const struct address_space_operations sdcardfs_aops = { const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, + .page_mkwrite = sdcardfs_page_mkwrite, }; From f62b3906044bd23095de6013f0c421520c3611c6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 24 Feb 2017 15:49:45 -0800 Subject: [PATCH 0569/1276] ANDROID: sdcardfs: support direct-IO (DIO) operations This comes from the wrapfs commit 2e346c83b26e ("Wrapfs: support direct-IO (DIO) operations") Signed-off-by: Li Mengyang Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 34133558 Change-Id: I3fd779c510ab70d56b1d918f99c20421b524cdc4 --- fs/sdcardfs/mmap.c | 21 ++++----------------- fs/sdcardfs/sdcardfs.h | 1 + 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 28db1d11c99e..51266f517fe2 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -84,27 +84,14 @@ static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* - * This function returns zero on purpose in order to support direct IO. - * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. - * - * However, this function won't be called by certain file operations - * including generic fs functions. * reads and writes are delivered to - * the lower file systems and the direct IOs will be handled by them. - * - * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), - * swap_writepage invokes this function directly. + * This function should never be called directly. We need it + * to exist, to get past a check in open_check_o_direct(), + * which is called from do_last(). */ - printk(KERN_INFO "%s, operation is not supported\n", __func__); - return 0; + return -EINVAL; } -/* - * XXX: the default address_space_ops for sdcardfs is empty. We cannot set - * our inode->i_mapping->a_ops to NULL because too many code paths expect - * the a_ops vector to be non-NULL. - */ const struct address_space_operations sdcardfs_aops = { - /* empty on purpose */ .direct_IO = sdcardfs_direct_IO, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 62838aeb57ec..7740124049cd 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include From c2e216d36d638d259d3e30cd35678a9b6a3fb080 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 1 Mar 2017 17:04:41 -0800 Subject: [PATCH 0570/1276] ANDROID: sdcardfs: Fix case insensitive lookup The previous case insensitive lookup relied on the entry being present in the dcache. This instead uses iterate_dir to find the correct case. Signed-off-by: Daniel Rosenberg bug: 35633782 Change-Id: I556f7090773468c1943c89a5e2aa07f746ba49c5 --- fs/sdcardfs/lookup.c | 68 +++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index ae8e9b519e16..58a1d8001037 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -206,6 +206,28 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, return err; } +struct sdcardfs_name_data { + struct dir_context ctx; + const struct qstr *to_find; + char *name; + bool found; +}; + +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); + struct qstr candidate = QSTR_INIT(name, namelen); + + if (qstr_case_eq(buf->to_find, &candidate)) { + memcpy(buf->name, name, namelen); + buf->name[namelen] = 0; + buf->found = true; + return 1; + } + return 0; +} + /* * Main driver function for sdcardfs's lookup. * @@ -242,27 +264,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, &lower_path); /* check for other cases */ if (err == -ENOENT) { - struct dentry *child; - struct dentry *match = NULL; - inode_lock(d_inode(lower_dir_dentry)); - spin_lock(&lower_dir_dentry->d_lock); - list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { - if (child && d_inode(child)) { - if (qstr_case_eq(&child->d_name, name)) { - match = dget(child); - break; - } - } + struct file *file; + const struct cred *cred = current_cred(); + + struct sdcardfs_name_data buffer = { + .ctx.actor = sdcardfs_name_match, + .to_find = name, + .name = __getname(), + .found = false, + }; + + if (!buffer.name) { + err = -ENOMEM; + goto out; + } + file = dentry_open(lower_parent_path, O_RDONLY, cred); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto put_name; } - spin_unlock(&lower_dir_dentry->d_lock); - inode_unlock(d_inode(lower_dir_dentry)); - if (match) { + err = iterate_dir(file, &buffer.ctx); + fput(file); + if (err) + goto put_name; + + if (buffer.found) err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, - match->d_name.name, 0, + buffer.name, 0, &lower_path); - dput(match); - } + else + err = -ENOENT; +put_name: + __putname(buffer.name); } /* no error: handle positive dentries */ From 57b92ab6f7748d1859bc21035840142cb920fb89 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 2 Mar 2017 18:07:21 -0800 Subject: [PATCH 0571/1276] ANDROID: sdcardfs: rate limit warning print Signed-off-by: Daniel Rosenberg Bug: 35848445 Change-Id: Ida72ea0ece191b2ae4a8babae096b2451eb563f6 --- fs/sdcardfs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4646dbb3ee03..5d05449720d8 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include +#include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) @@ -603,7 +604,7 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission_wrn(struct inode *inode, int mask) { - WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); return -EINVAL; } @@ -691,7 +692,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) { - WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n"); return -EINVAL; } From 8534cee39a8124b682d03facc4e5222f2f58e2a1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 2 Mar 2017 15:11:27 -0800 Subject: [PATCH 0572/1276] ANDROID: sdcardfs: Replace get/put with d_lock dput cannot be called with a spin_lock. Instead, we protect our accesses by holding the d_lock. Signed-off-by: Daniel Rosenberg Bug: 35643557 Change-Id: I22cf30856d75b5616cbb0c223724f5ab866b5114 --- fs/sdcardfs/derived_perm.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 1b5029d81921..0f227b95c9e3 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -264,41 +264,48 @@ static int needs_fixup(perm_t perm) { return 0; } -void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth) { struct dentry *child; struct sdcardfs_inode_info *info; - if (!dget(dentry)) - return; + + /* + * All paths will terminate their recursion on hitting PERM_ANDROID_OBB, + * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of + * at most 3. + */ + WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__); + spin_lock_nested(&dentry->d_lock, depth); if (!d_inode(dentry)) { - dput(dentry); + spin_unlock(&dentry->d_lock); return; } info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); + spin_lock_nested(&child->d_lock, depth + 1); if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); - dput(child); + spin_unlock(&child->d_lock); break; } } - dput(child); + spin_unlock(&child->d_lock); } - spin_unlock(&dentry->d_lock); } else if (descendant_may_need_fixup(info, limit)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, limit); + __fixup_perms_recursive(child, limit, depth + 1); } - spin_unlock(&dentry->d_lock); } - dput(dentry); + spin_unlock(&dentry->d_lock); +} + +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ + __fixup_perms_recursive(dentry, limit, 0); } void drop_recursive(struct dentry *parent) From 156085b2fccfaada1e832443efbb0de198207393 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:11:51 -0800 Subject: [PATCH 0573/1276] ANDROID: sdcardfs: Use spin_lock_nested Signed-off-by: Daniel Rosenberg Bug: 36007653 Change-Id: I805d5afec797669679853fb2bb993ee38e6276e4 --- fs/sdcardfs/dentry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index f22de8add10c..d1eff456cc8a 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -76,10 +76,10 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (dentry < lower_dentry) { spin_lock(&dentry->d_lock); - spin_lock(&lower_dentry->d_lock); + spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED); } else { spin_lock(&lower_dentry->d_lock); - spin_lock(&dentry->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } if (dentry->d_name.len != lower_dentry->d_name.len) { From 8a260cabac4eb3268120bd9cf5174b69b5211b54 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:20:02 -0800 Subject: [PATCH 0574/1276] ANDROID: sdcardfs: Switch to internal case insensitive compare There were still a few places where we called into a case insensitive lookup that was not defined by sdcardfs. Moving them all to the same place will allow us to switch the implementation in the future. Additionally, the check in fixup_perms_recursive did not take into account the length of both strings, causing extraneous matches when the name we were looking for was a prefix of the child name. Signed-off-by: Daniel Rosenberg Change-Id: I45ce768cd782cb4ea1ae183772781387c590ecc2 --- fs/sdcardfs/dentry.c | 8 ++------ fs/sdcardfs/derived_perm.c | 2 +- fs/sdcardfs/packagelist.c | 6 ++---- fs/sdcardfs/sdcardfs.h | 8 ++++++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index d1eff456cc8a..2797d2fc4227 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -82,11 +82,7 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } - if (dentry->d_name.len != lower_dentry->d_name.len) { - __d_drop(dentry); - err = 0; - } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, - dentry->d_name.len) != 0) { + if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { __d_drop(dentry); err = 0; } @@ -165,7 +161,7 @@ static int sdcardfs_cmp_ci(const struct dentry *dentry, } */ if (name->len == len) { - if (strncasecmp(name->name, str, len) == 0) + if (str_n_case_eq(name->name, str, len)) return 0; } return 1; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 0f227b95c9e3..69ffbf253645 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -285,7 +285,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * if (needs_fixup(info->perm)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { spin_lock_nested(&child->d_lock, depth + 1); - if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 5f81284a4f44..d933bffacc7a 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -254,8 +254,7 @@ static void fixup_all_perms_name(const struct qstr *key) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -268,8 +267,7 @@ static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 7740124049cd..a42125ef39f5 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -476,8 +476,7 @@ extern void packagelist_exit(void); #define BY_USERID (1 << 1) struct limit_search { unsigned int flags; - const char *name; - size_t length; + struct qstr name; userid_t userid; }; @@ -618,6 +617,11 @@ static inline bool str_case_eq(const char *s1, const char *s2) return !strcasecmp(s1, s2); } +static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) +{ + return !strncasecmp(s1, s2, len); +} + static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { return q1->len == q2->len && str_case_eq(q1->name, q2->name); From a8d51569573ca424772ae2d08530d3dd17776ca7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:45:46 -0800 Subject: [PATCH 0575/1276] ANDROID: sdcardfs: Use d_invalidate instead of drop_recurisve drop_recursive did not properly remove stale dentries. Instead, we use the vfs's d_invalidate, which does the proper cleanup. Additionally, remove the no longer used drop_recursive, and fixup_top_recursive that that are no longer used. Signed-off-by: Daniel Rosenberg Change-Id: Ibff61b0c34b725b024a050169047a415bc90f0d8 --- fs/sdcardfs/derived_perm.c | 40 -------------------------------------- fs/sdcardfs/inode.c | 2 +- fs/sdcardfs/sdcardfs.h | 2 -- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 69ffbf253645..72ae4b9edc31 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -308,46 +308,6 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) __fixup_perms_recursive(dentry, limit, 0); } -void drop_recursive(struct dentry *parent) -{ - struct dentry *dentry; - struct sdcardfs_inode_info *info; - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - drop_recursive(dentry); - d_drop(dentry); - } - } - } - spin_unlock(&parent->d_lock); -} - -void fixup_top_recursive(struct dentry *parent) -{ - struct dentry *dentry; - struct sdcardfs_inode_info *info; - - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - get_derived_permission(parent, dentry); - fixup_tmp_permissions(d_inode(dentry)); - fixup_top_recursive(dentry); - } - } - } - spin_unlock(&parent->d_lock); -} - /* main function for updating derived permission */ inline void update_derived_permission_lock(struct dentry *dentry) { diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 5d05449720d8..9052ed472157 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -533,7 +533,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); - drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ + d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index a42125ef39f5..09ec1e415e6d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -484,8 +484,6 @@ extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t useri uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); -extern void drop_recursive(struct dentry *parent); -extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); From 932a6071de63c9f82a0cac867c28c0101de54128 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 18:12:16 -0800 Subject: [PATCH 0576/1276] ANDROID: sdcardfs: Get the blocksize from the lower fs This changes sdcardfs to be more in line with the getattr in wrapfs, which calls the lower fs's getattr to get the block size Signed-off-by: Daniel Rosenberg Bug: 34723223 Change-Id: I1c9e16604ba580a8cdefa17f02dcc489d7351aed --- fs/sdcardfs/inode.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 9052ed472157..3022cccc039b 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -865,9 +865,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct k static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; + struct kstat lower_stat; struct path lower_path; struct dentry *parent; int err; @@ -882,16 +880,15 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, } dput(parent); - inode = d_inode(dentry); - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - sdcardfs_copy_and_fix_attrs(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - - err = sdcardfs_fillattr(mnt, inode, stat); + err = vfs_getattr(&lower_path, &lower_stat); + if (err) + goto out; + sdcardfs_copy_and_fix_attrs(d_inode(dentry), + d_inode(lower_path.dentry)); + err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); + stat->blocks = lower_stat.blocks; +out: sdcardfs_put_lower_path(dentry, &lower_path); return err; } From 0ad4c0f8752776378d19306e2bbb22df1203c8f1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 20:59:18 -0800 Subject: [PATCH 0577/1276] ANDROID: sdcardfs: declare MODULE_ALIAS_FS From commit ee616b78aa87 ("Wrapfs: declare MODULE_ALIAS_FS") Signed-off-by: Daniel Rosenberg bug: 35766959 Change-Id: Ia4728ab49d065b1d2eb27825046f14b97c328cba --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7a8eae29e44d..4e2aded8d1d9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -432,6 +432,7 @@ static struct file_system_type sdcardfs_fs_type = { .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; +MODULE_ALIAS_FS(SDCARDFS_NAME); static int __init init_sdcardfs_fs(void) { From b97c83b5b683a3af50084a3cec352fafa93f7473 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 12:39:42 -0800 Subject: [PATCH 0578/1276] ANDROID: sdcardfs: Use case insensitive hash function Case insensitive comparisons don't help us much if we hash to different buckets... Signed-off-by: Daniel Rosenberg bug: 36004503 Change-Id: I91e00dbcd860a709cbd4f7fd7fc6d855779f3285 --- fs/sdcardfs/packagelist.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d933bffacc7a..2cc076ca85de 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include +#include #include #include #include @@ -44,11 +45,19 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; +static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(salt); + while (len--) + hash = partial_name_hash(tolower(*name++), hash); + return end_name_hash(hash); +} + static inline void qstr_init(struct qstr *q, const char *name) { q->name = name; q->len = strlen(q->name); - q->hash = full_name_hash(0, q->name, q->len); + q->hash = full_name_case_hash(0, q->name, q->len); } static inline int qstr_copy(const struct qstr *src, struct qstr *dest) From 9920dfb082652542fdba32f4afa2b53d5a2d1df7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 13:54:30 -0800 Subject: [PATCH 0579/1276] ANDROID: sdcardfs: move path_put outside of spinlock Signed-off-by: Daniel Rosenberg Bug: 35643557 Change-Id: Ib279ebd7dd4e5884d184d67696a93e34993bc1ef --- fs/sdcardfs/derived_perm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 72ae4b9edc31..f47884b7d397 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -360,6 +360,8 @@ int is_obbpath_invalid(struct dentry *dent) struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); char *path_buf, *obbpath_s; + int need_put = 0; + struct path lower_path; /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. @@ -386,10 +388,13 @@ int is_obbpath_invalid(struct dentry *dent) } //unlock_dir(lower_parent); - path_put(&di->lower_path); + pathcpy(&lower_path, &di->lower_path); + need_put = 1; } } spin_unlock(&di->lock); + if (need_put) + path_put(&lower_path); return ret; } From f9a25348b233b31e8c4f7138d8dbedca8dbc11b1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 18:58:25 -0800 Subject: [PATCH 0580/1276] ANDROID: sdcardfs: Remove uninformative prints At best these prints do not provide useful information, and at worst, some allow userspace to abuse the kernel log. Signed-off-by: Daniel Rosenberg Bug: 36138424 Change-Id: I812c57cc6a22b37262935ab77f48f3af4c36827e --- fs/sdcardfs/derived_perm.c | 1 - fs/sdcardfs/file.c | 3 --- fs/sdcardfs/inode.c | 24 +----------------------- fs/sdcardfs/lookup.c | 3 --- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index f47884b7d397..bc7f09800dbb 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -440,7 +440,6 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) if(!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 56000a004b38..0f2db26895bf 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -217,9 +217,6 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_err; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 3022cccc039b..7ca5b8ab4313 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -68,9 +68,6 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *copied_fs; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -170,9 +167,6 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) const struct cred *saved_cred = NULL; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -280,9 +274,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct qstr q_data = QSTR_LITERAL("data"); if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -392,9 +383,6 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) const struct cred *saved_cred = NULL; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -485,9 +473,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " new_dentry: %s, task:%s\n", - __func__, new_dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -755,12 +740,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) err = -EACCES; - } dput(parent); } @@ -872,9 +853,6 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, parent = dget_parent(dentry); if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); dput(parent); return -EACCES; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 58a1d8001037..7d26c269da35 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -395,9 +395,6 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); goto out_err; } From 720d9030bea1aa1bc8c5ebe5d1e6e4632777e6f7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 13 Mar 2017 15:34:03 -0700 Subject: [PATCH 0581/1276] ANDROID: sdcardfs: Fix gid issue We were already calculating most of these values, and erroring out because the check was confused by this. Instead of recalculating, adjust it as needed. Signed-off-by: Daniel Rosenberg Bug: 36160015 Change-Id: I9caf3e2fd32ca2e37ff8ed71b1d392f1761bc9a9 --- fs/sdcardfs/derived_perm.c | 4 ++-- fs/sdcardfs/multiuser.h | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index bc7f09800dbb..fc5a632e9b83 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -207,13 +207,13 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) break; case PERM_ANDROID_PACKAGE: if (info->d_uid != 0) - gid = multiuser_get_ext_gid(info->userid, info->d_uid); + gid = multiuser_get_ext_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; case PERM_ANDROID_PACKAGE_CACHE: if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->userid, info->d_uid); + gid = multiuser_get_cache_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 950cffddc556..2e89b5872314 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -33,20 +33,12 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) +static inline gid_t multiuser_get_cache_gid(uid_t uid) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); - } else { - return -1; - } + return uid - AID_APP_START + AID_CACHE_GID_START; } -static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) +static inline gid_t multiuser_get_ext_gid(uid_t uid) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); - } else { - return -1; - } + return uid - AID_APP_START + AID_EXT_GID_START; } From 4cbb7fa6e66cbea3f34b1da3f341f1370011f2d8 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 16:28:27 -0700 Subject: [PATCH 0582/1276] ANDROID: sdcardfs: correct order of descriptors Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: Ia6d16b19c8c911f41231d2a12be0740057edfacf --- fs/sdcardfs/packagelist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 2cc076ca85de..7abfeb124fc2 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -48,6 +48,7 @@ static struct kmem_cache *hashtable_entry_cachep; static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len) { unsigned long hash = init_name_hash(salt); + while (len--) hash = partial_name_hash(tolower(*name++), hash); return end_name_hash(hash); From 6cff6cc301ed1e41abfd83087affa13661d32349 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 17:42:58 -0700 Subject: [PATCH 0583/1276] ANDROID: sdcardfs: Fix formatting This fixes various spacing and bracket related issues pointed out by checkpatch. Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I6e248833a7a04e3899f3ae9462d765cfcaa70c96 --- fs/sdcardfs/dentry.c | 13 ++--- fs/sdcardfs/derived_perm.c | 55 +++++++++++--------- fs/sdcardfs/file.c | 3 +- fs/sdcardfs/inode.c | 28 +++++++---- fs/sdcardfs/lookup.c | 30 +++++------ fs/sdcardfs/main.c | 34 ++++++++----- fs/sdcardfs/packagelist.c | 36 ++++++------- fs/sdcardfs/sdcardfs.h | 100 ++++++++++++++++++++----------------- fs/sdcardfs/super.c | 29 ++++++----- 9 files changed, 184 insertions(+), 144 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 2797d2fc4227..26665da31732 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -46,7 +46,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); /* check uninitialized obb_dentry and - * whether the base obbpath has been changed or not */ + * whether the base obbpath has been changed or not + */ if (is_obbpath_invalid(dentry)) { d_drop(dentry); return 0; @@ -106,12 +107,10 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) static void sdcardfs_d_release(struct dentry *dentry) { /* release and reset the lower paths */ - if(has_graft_path(dentry)) { + if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); - } sdcardfs_put_reset_lower_path(dentry); free_dentry_private_data(dentry); - return; } static int sdcardfs_hash_ci(const struct dentry *dentry, @@ -167,14 +166,16 @@ static int sdcardfs_cmp_ci(const struct dentry *dentry, return 1; } -static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { +static void sdcardfs_canonical_path(const struct path *path, + struct path *actual_path) +{ sdcardfs_get_real_lower(path->dentry, actual_path); } const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, - .d_hash = sdcardfs_hash_ci, + .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, .d_canonical_path = sdcardfs_canonical_path, }; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index fc5a632e9b83..9de548016a3b 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -37,7 +37,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top) + uid_t uid, bool under_android, + struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -50,11 +51,14 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, set_top(info, top); } -/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) +/* While renaming, there is a point where we want the path from dentry, + * but the name from newdentry + */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, + const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); @@ -118,9 +122,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_ANDROID_MEDIA: info->perm = PERM_ANDROID_PACKAGE; appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } set_top(info, &info->vfs_inode); break; case PERM_ANDROID_PACKAGE: @@ -257,7 +260,8 @@ static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct li return 0; } -static int needs_fixup(perm_t perm) { +static int needs_fixup(perm_t perm) +{ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB || perm == PERM_ANDROID_MEDIA) return 1; @@ -295,9 +299,9 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } spin_unlock(&child->d_lock); } - } else if (descendant_may_need_fixup(info, limit)) { + } else if (descendant_may_need_fixup(info, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { - __fixup_perms_recursive(child, limit, depth + 1); + __fixup_perms_recursive(child, limit, depth + 1); } } spin_unlock(&dentry->d_lock); @@ -313,7 +317,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !d_inode(dentry)) { + if (!dentry || !d_inode(dentry)) { printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); return; } @@ -321,11 +325,11 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if(IS_ROOT(dentry)) { + if (IS_ROOT(dentry)) { //setup_default_pre_root_state(d_inode(dentry)); } else { parent = dget_parent(dentry); - if(parent) { + if (parent) { get_derived_permission(parent, dentry); dput(parent); } @@ -337,15 +341,15 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if(parent_info->perm == PERM_ANDROID && + if (parent_info->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.multiuser == false + if (!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -365,17 +369,18 @@ int is_obbpath_invalid(struct dentry *dent) /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. - * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() + */ spin_lock(&di->lock); - if(di->orig_path.dentry) { - if(!di->lower_path.dentry) { + if (di->orig_path.dentry) { + if (!di->lower_path.dentry) { ret = 1; } else { path_get(&di->lower_path); //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); - if(!path_buf) { + if (!path_buf) { ret = 1; printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { @@ -402,13 +407,13 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if(parent_info->perm == PERM_PRE_ROOT && + if (parent_info->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } @@ -423,7 +428,8 @@ int is_base_obbpath(struct dentry *dentry) /* The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) { int err = 0; @@ -438,7 +444,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) err = kern_path(sbi->obbpath_s, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); - if(!err) { + if (!err) { /* the obbpath base has been found */ pathcpy(lower_path, &obbpath); } else { @@ -446,7 +452,8 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ + * a lookup fail. + */ printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); } return err; diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 0f2db26895bf..4c9726606c0f 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -248,9 +248,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); - else { + else sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); - } out_revert_cred: REVERT_CRED(saved_cred); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 7ca5b8ab4313..bd05ed88d692 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -240,13 +240,14 @@ static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, } #endif -static int touch(char *abs_path, mode_t mode) { +static int touch(char *abs_path, mode_t mode) +{ struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { if (PTR_ERR(filp) == -EEXIST) { return 0; - } - else { + } else { printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); @@ -315,19 +316,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* if it is a local obb dentry, setup it with the base obbpath */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. - * but now, the lower_path should be NULL */ + * but now, the lower_path should be NULL + */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required + */ path_get(&lower_path); } else make_nomedia_in_obb = 1; @@ -391,7 +394,8 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry - * the dentry on the original path should be deleted. */ + * the dentry on the original path should be deleted. + */ sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -663,6 +667,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * we check it with AID_MEDIA_RW permission */ struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); lower_inode = sdcardfs_lower_inode(inode); @@ -733,7 +738,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct /* prepare our own lower struct iattr (with the lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); /* Allow touch updating timestamps. A previous permission check ensures - * we have write access. Changes to mode, owner, and group are ignored*/ + * we have write access. Changes to mode, owner, and group are ignored + */ ia->ia_valid |= ATTR_FORCE; err = setattr_prepare(&tmp_d, ia); @@ -819,10 +825,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, + struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); + if (!top) return -EINVAL; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7d26c269da35..6b450c48e5d3 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -73,6 +73,7 @@ static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void * { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ @@ -102,7 +103,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u * instead. */ lower_inode->i_ino, /* hashval */ - sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { @@ -213,8 +214,8 @@ struct sdcardfs_name_data { bool found; }; -static int sdcardfs_name_match(struct dir_context *ctx, const char *name, int namelen, - loff_t offset, u64 ino, unsigned int d_type) +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, unsigned int d_type) { struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); struct qstr candidate = QSTR_INIT(name, namelen); @@ -303,23 +304,26 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (!err) { /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with - * the inode of the graft path */ + * the inode of the graft path + */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { /* setup_obb_dentry() - * The lower_path will be stored to the dentry's orig_path + * The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ + * a lookup fail. + */ printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; @@ -374,9 +378,9 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* * On success: - * fills dentry object appropriate values and returns NULL. + * fills dentry object appropriate values and returns NULL. * On fail (== error) - * returns error ptr + * returns error ptr * * @dir : Parent inode. * @dentry : Target dentry to lookup. we should set each of fields. @@ -396,7 +400,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); goto out_err; - } + } /* save current_cred and override it */ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); @@ -412,9 +416,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) - { goto out; - } if (ret) dentry = ret; if (d_inode(dentry)) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 4e2aded8d1d9..c249bd73b121 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -72,6 +72,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -148,6 +149,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -223,8 +225,8 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) #endif DEFINE_MUTEX(sdcardfs_super_list_lock); -LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list); /* @@ -328,14 +330,15 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* setup permission policy */ sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); - if(sb_info->options.multiuser) { - setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + if (sb_info->options.multiuser) { + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - /*err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); @@ -368,8 +371,10 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* A feature which supports mount_nodev() with options */ static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) + struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, + const char *, void *, int)) { int error; @@ -401,19 +406,22 @@ static struct dentry *sdcardfs_mount(struct vfsmount *mnt, raw_data, sdcardfs_read_super); } -static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data) { WARN(1, "sdcardfs does not support mount. Use mount2.\n"); return ERR_PTR(-EINVAL); } -void *sdcardfs_alloc_mnt_data(void) { +void *sdcardfs_alloc_mnt_data(void) +{ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } -void sdcardfs_kill_sb(struct super_block *sb) { +void sdcardfs_kill_sb(struct super_block *sb) +{ struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 7abfeb124fc2..3df39a715a58 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -90,6 +90,7 @@ static appid_t __get_appid(const struct qstr *key) appid_t get_appid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_appid(&q); } @@ -115,6 +116,7 @@ static appid_t __get_ext_gid(const struct qstr *key) appid_t get_ext_gid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_ext_gid(&q); } @@ -145,7 +147,8 @@ appid_t is_excluded(const char *key, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access - * even further, such as enforcing that apps hold sdcard_rw. */ + * even further, such as enforcing that apps hold sdcard_rw. + */ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); @@ -162,26 +165,26 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na } /* Root always has access; access for any other UIDs should always - * be controlled through packages.list. */ - if (from_kuid(&init_user_ns, current_fsuid()) == 0) { + * be controlled through packages.list. + */ + if (from_kuid(&init_user_ns, current_fsuid()) == 0) return 1; - } /* No extra permissions to enforce */ return 1; } /* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() */ -int open_flags_to_access_mode(int open_flags) { - if((open_flags & O_ACCMODE) == O_RDONLY) { + * checked before calling check_caller_access_to_name() + */ +int open_flags_to_access_mode(int open_flags) +{ + if ((open_flags & O_ACCMODE) == O_RDONLY) return 0; /* R_OK */ - } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + if ((open_flags & O_ACCMODE) == O_WRONLY) return 1; /* W_OK */ - } else { - /* Probably O_RDRW, but treat as default to be safe */ + /* Probably O_RDRW, but treat as default to be safe */ return 1; /* R_OK | W_OK */ - } } static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, @@ -373,7 +376,6 @@ static void remove_packagelist_entry(const struct qstr *key) remove_packagelist_entry_locked(key); fixup_all_perms_name(key); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) @@ -396,7 +398,6 @@ static void remove_ext_gid_entry(const struct qstr *key, gid_t group) mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_all_entry_locked(userid_t userid) @@ -424,7 +425,6 @@ static void remove_userid_all_entry(userid_t userid) remove_userid_all_entry_locked(userid); fixup_all_perms_userid(userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) @@ -449,7 +449,6 @@ static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) remove_userid_exclude_entry_locked(key, userid); fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void packagelist_destroy(void) @@ -458,6 +457,7 @@ static void packagelist_destroy(void) struct hlist_node *h_t; HLIST_HEAD(free_list); int i; + mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); @@ -605,7 +605,7 @@ static struct configfs_attribute *package_details_attrs[] = { }; static struct configfs_item_operations package_details_item_ops = { - .release = package_details_release, + .release = package_details_release, }; static struct config_item_type package_appid_type = { @@ -661,6 +661,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); const char *tmp; int ret; + if (!extension_details) return ERR_PTR(-ENOMEM); @@ -715,6 +716,7 @@ static struct config_group *extensions_make_group(struct config_group *group, co static void extensions_drop_group(struct config_group *group, struct config_item *item) { struct extensions_value *value = to_extensions_value(item); + printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -880,7 +882,7 @@ int packagelist_init(void) } configfs_sdcardfs_init(); - return 0; + return 0; } void packagelist_exit(void) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 09ec1e415e6d..34e6f318b60d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -87,11 +87,11 @@ } while (0) /* OVERRIDE_CRED() and REVERT_CRED() - * OVERRID_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. + * OVERRIDE_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. + * restore original task->cred->fsuid/fsgid. * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ @@ -120,27 +120,29 @@ /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions - * are derived for children nodes. */ + * are derived for children nodes. + */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/media" */ - PERM_ANDROID_MEDIA, - /* This node is "/Android/[data|media|obb]/[package]" */ - PERM_ANDROID_PACKAGE, - /* This node is "/Android/[data|media|obb]/[package]/cache" */ - PERM_ANDROID_PACKAGE_CACHE, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. + */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; @@ -150,7 +152,7 @@ struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred); +void revert_fsids(const struct cred *old_cred); /* operations vectors defined in specific files */ extern const struct file_operations sdcardfs_main_fops; @@ -227,7 +229,8 @@ struct sdcardfs_sb_info { struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added - * to sdcardfs_mount_options (Android 4.4 support) */ + * to sdcardfs_mount_options (Android 4.4 support) + */ struct sdcardfs_mount_options options; spinlock_t lock; /* protects obbpath */ char *obbpath_s; @@ -338,7 +341,7 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ { \ struct path pname; \ spin_lock(&SDCARDFS_D(dent)->lock); \ - if(SDCARDFS_D(dent)->pname.dentry) { \ + if (SDCARDFS_D(dent)->pname.dentry) { \ pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ SDCARDFS_D(dent)->pname.dentry = NULL; \ SDCARDFS_D(dent)->pname.mnt = NULL; \ @@ -354,17 +357,17 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) { + if (info->top && info->top != &info->vfs_inode) old_top = info->top; - } if (top != &info->vfs_inode) igrab(top); info->top = top; @@ -374,11 +377,11 @@ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) static inline struct inode *grab_top(struct sdcardfs_inode_info *info) { struct inode *top = info->top; - if (top) { + + if (top) return igrab(top); - } else { + else return NULL; - } } static inline void release_top(struct sdcardfs_inode_info *info) @@ -386,21 +389,24 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ struct sdcardfs_vfsmount_options *opts = mnt->data; - if (opts->gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing * the user boundary enforcement for the default view. The UIDs - * assigned to app directories are still multiuser aware. */ + * assigned to app directories are still multiuser aware. + */ return AID_SDCARD_RW; - } else { + else return multiuser_get_uid(info->userid, opts->gid); - } } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ int owner_mode; int filtered_mode; struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -409,17 +415,18 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure - * secondary users can traverse inside. */ + * secondary users can traverse inside. + */ visible_mode = 0711; } else if (info->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still - * leave +x open for the default view. */ - if (opts->gid == AID_SDCARD_RW) { + * leave +x open for the default view. + */ + if (opts->gid == AID_SDCARD_RW) visible_mode = visible_mode & ~0006; - } else { + else visible_mode = visible_mode & ~0007; - } } owner_mode = info->lower_inode->i_mode & 0700; filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); @@ -444,7 +451,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, /* in case of a local obb dentry * the orig_path should be returned */ - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_get_orig_path(dent, real_lower); else sdcardfs_get_lower_path(dent, real_lower); @@ -453,7 +460,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, static inline void sdcardfs_put_real_lower(const struct dentry *dent, struct path *real_lower) { - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_put_orig_path(dent, real_lower); else sdcardfs_put_lower_path(dent, real_lower); @@ -497,6 +504,7 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); return dir; } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index edda32b68dc0..a4629f20812e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -36,7 +36,7 @@ static void sdcardfs_put_super(struct super_block *sb) if (!spd) return; - if(spd->obbpath_s) { + if (spd->obbpath_s) { kfree(spd->obbpath_s); path_put(&spd->obbpath); } @@ -125,29 +125,33 @@ static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } - printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt); err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); return err; } -static void* sdcardfs_clone_mnt_data(void *data) { - struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); - struct sdcardfs_vfsmount_options* old = data; - if(!opt) return NULL; +static void *sdcardfs_clone_mnt_data(void *data) +{ + struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options *old = data; + + if (!opt) + return NULL; opt->gid = old->gid; opt->mask = old->mask; return opt; } -static void sdcardfs_copy_mnt_data(void *data, void *newdata) { - struct sdcardfs_vfsmount_options* old = data; - struct sdcardfs_vfsmount_options* new = newdata; +static void sdcardfs_copy_mnt_data(void *data, void *newdata) +{ + struct sdcardfs_vfsmount_options *old = data; + struct sdcardfs_vfsmount_options *new = newdata; + old->gid = new->gid; old->mask = new->mask; } @@ -235,7 +239,8 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, + struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; From ac2a40412e2619c33d64b99823db96186199b7a9 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 19:33:35 -0700 Subject: [PATCH 0584/1276] ANDROID: sdcardfs: Fix style issues with comments Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I8791ef7eac527645ecb9407908e7e5ece35b8f80 --- fs/sdcardfs/dentry.c | 16 +--------------- fs/sdcardfs/derived_perm.c | 9 +++------ fs/sdcardfs/main.c | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 26665da31732..afd97710f7ad 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -127,12 +127,10 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, unsigned long hash; name = qstr->name; - //len = vfat_striptail_len(qstr); len = qstr->len; hash = init_name_hash(dentry); while (len--) - //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); qstr->hash = end_name_hash(hash); @@ -145,20 +143,8 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, static int sdcardfs_cmp_ci(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - /* This function is copy of vfat_cmpi */ - // FIXME Should we support national language? - //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; - //unsigned int alen, blen; + /* FIXME Should we support national language? */ - /* A filename cannot end in '.' or we treat it like it has none */ - /* - alen = vfat_striptail_len(name); - blen = __vfat_striptail_len(len, str); - if (alen == blen) { - if (nls_strnicmp(t, name->name, str, alen) == 0) - return 0; - } - */ if (name->len == len) { if (str_n_case_eq(name->name, str, len)) return 0; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 9de548016a3b..329e91129808 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -325,9 +325,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if (IS_ROOT(dentry)) { - //setup_default_pre_root_state(d_inode(dentry)); - } else { + if (!IS_ROOT(dentry)) { parent = dget_parent(dentry); if (parent) { get_derived_permission(parent, dentry); @@ -377,7 +375,6 @@ int is_obbpath_invalid(struct dentry *dent) ret = 1; } else { path_get(&di->lower_path); - //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if (!path_buf) { @@ -392,7 +389,6 @@ int is_obbpath_invalid(struct dentry *dent) kfree(path_buf); } - //unlock_dir(lower_parent); pathcpy(&lower_path, &di->lower_path); need_put = 1; } @@ -438,7 +434,8 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) /* A local obb dentry must have its own orig_path to support rmdir * and mkdir of itself. Usually, we expect that the sbi->obbpath - * is avaiable on this stage. */ + * is avaiable on this stage. + */ sdcardfs_set_orig_path(dentry, lower_path); err = kern_path(sbi->obbpath_s, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index c249bd73b121..95cf03379019 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -29,7 +29,7 @@ enum { Opt_gid, Opt_debug, Opt_mask, - Opt_multiuser, // May need? + Opt_multiuser, Opt_userid, Opt_reserved_mb, Opt_err, From 2212bb8ec06419445f281d9f8eb544d1adda2e98 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 16:29:13 -0700 Subject: [PATCH 0585/1276] ANDROID: sdcardfs: remove unneeded null check As pointed out by checkpatch, these functions already handle null inputs, so the checks are not needed. Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I189342f032dfcefee36b27648bb512488ad61d20 --- fs/sdcardfs/lookup.c | 3 +-- fs/sdcardfs/packagelist.c | 3 +-- fs/sdcardfs/super.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 6b450c48e5d3..6de2747b636a 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -36,8 +36,7 @@ int sdcardfs_init_dentry_cache(void) void sdcardfs_destroy_dentry_cache(void) { - if (sdcardfs_dentry_cachep) - kmem_cache_destroy(sdcardfs_dentry_cachep); + kmem_cache_destroy(sdcardfs_dentry_cachep); } void free_dentry_private_data(struct dentry *dentry) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 3df39a715a58..cadb5fa231c1 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -889,6 +889,5 @@ void packagelist_exit(void) { configfs_sdcardfs_exit(); packagelist_destroy(); - if (hashtable_entry_cachep) - kmem_cache_destroy(hashtable_entry_cachep); + kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index a4629f20812e..7d3c331379f5 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -222,8 +222,7 @@ int sdcardfs_init_inode_cache(void) /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { - if (sdcardfs_inode_cachep) - kmem_cache_destroy(sdcardfs_inode_cachep); + kmem_cache_destroy(sdcardfs_inode_cachep); } /* From 4c1a0add8d21a4e405d99c4adb85b5ab48d877d1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 17:46:13 -0700 Subject: [PATCH 0586/1276] ANDROID: sdcardfs: Use pr_[...] instead of printk Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: Ibc635ec865750530d32b87067779f681fe58a003 --- fs/sdcardfs/derived_perm.c | 6 ++--- fs/sdcardfs/file.c | 9 ++++---- fs/sdcardfs/inode.c | 8 +++---- fs/sdcardfs/lookup.c | 2 +- fs/sdcardfs/main.c | 45 +++++++++++++++++--------------------- fs/sdcardfs/packagelist.c | 13 ++++++----- fs/sdcardfs/sdcardfs.h | 2 +- fs/sdcardfs/super.c | 5 ++--- 8 files changed, 42 insertions(+), 48 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 329e91129808..411a9a9258b6 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -318,7 +318,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) struct dentry *parent; if (!dentry || !d_inode(dentry)) { - printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + pr_err("sdcardfs: %s: invalid dentry\n", __func__); return; } /* FIXME: @@ -379,7 +379,7 @@ int is_obbpath_invalid(struct dentry *dent) path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if (!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); + pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -451,7 +451,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); + pr_info("sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 4c9726606c0f..eee4eb5896e5 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -65,7 +65,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { - printk(KERN_INFO "No minimum free space.\n"); + pr_err("No minimum free space.\n"); return -ENOSPC; } @@ -160,8 +160,7 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) lower_file = sdcardfs_lower_file(file); if (willwrite && !lower_file->f_mapping->a_ops->writepage) { err = -EINVAL; - printk(KERN_ERR "sdcardfs: lower file system does not " - "support writeable mmap\n"); + pr_err("sdcardfs: lower file system does not support writeable mmap\n"); goto out; } @@ -173,14 +172,14 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) if (!SDCARDFS_F(file)->lower_vm_ops) { err = lower_file->f_op->mmap(lower_file, vma); if (err) { - printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + pr_err("sdcardfs: lower mmap failed %d\n", err); goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ err = do_munmap(current->mm, vma->vm_start, vma->vm_end - vma->vm_start); if (err) { - printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); + pr_err("sdcardfs: do_munmap failed %d\n", err); goto out; } } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index bd05ed88d692..97120e85e31c 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -248,7 +248,7 @@ static int touch(char *abs_path, mode_t mode) if (PTR_ERR(filp) == -EEXIST) { return 0; } else { - printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + pr_err("sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); } @@ -284,7 +284,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { - printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } @@ -360,7 +360,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", lower_path.dentry->d_name.name, touch_err); goto out; } @@ -649,7 +649,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) - printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 6de2747b636a..f028bfdebeaf 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -323,7 +323,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + pr_info("sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 95cf03379019..7344635522cd 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -117,19 +117,17 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (*debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); - printk( KERN_INFO "sdcardfs : options - uid:%d\n", + pr_info("sdcardfs : options - debug:%d\n", *debug); + pr_info("sdcardfs : options - uid:%d\n", opts->fs_low_uid); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", + pr_info("sdcardfs : options - gid:%d\n", opts->fs_low_gid); } @@ -175,22 +173,20 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, case Opt_fsuid: case Opt_fsgid: case Opt_reserved_mb: - printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + pr_warn("Option \"%s\" can't be changed during remount\n", p); break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); - printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + pr_info("sdcardfs : options - debug:%d\n", debug); + pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid); + pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask); } return 0; @@ -244,31 +240,30 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; - printk(KERN_INFO "sdcardfs version 2.0\n"); + pr_info("sdcardfs version 2.0\n"); if (!dev_name) { - printk(KERN_ERR - "sdcardfs: read_super: missing dev_name argument\n"); + pr_err("sdcardfs: read_super: missing dev_name argument\n"); err = -EINVAL; goto out; } - printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); - printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); - printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); + pr_info("sdcardfs: dev_name -> %s\n", dev_name); + pr_info("sdcardfs: options -> %s\n", (char *)raw_data); + pr_info("sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); + pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } /* allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); if (!SDCARDFS_SB(sb)) { - printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + pr_crit("sdcardfs: read_super: out of memory\n"); err = -ENOMEM; goto out_free; } @@ -277,7 +272,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* parse options */ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { - printk(KERN_ERR "sdcardfs: invalid options\n"); + pr_err("sdcardfs: invalid options\n"); goto out_freesbi; } @@ -347,7 +342,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_unlock(&sdcardfs_super_list_lock); if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + pr_info("sdcardfs: mounted on top of %s type %s\n", dev_name, lower_sb->s_type->name); goto out; /* all is well */ diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index cadb5fa231c1..5ea6469638d8 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -471,7 +471,7 @@ static void packagelist_destroy(void) hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + pr_info("sdcardfs: destroyed packagelist pkgld\n"); } #define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ @@ -587,7 +587,8 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + + pr_info("sdcardfs: removing %s\n", package_details->name.name); remove_packagelist_entry(&package_details->name); kfree(package_details->name.name); kfree(package_details); @@ -639,7 +640,7 @@ static void extension_details_release(struct config_item *item) { struct extension_details *extension_details = to_extension_details(item); - printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + pr_info("sdcardfs: No longer mapping %s files to gid %d\n", extension_details->name.name, extension_details->num); remove_ext_gid_entry(&extension_details->name, extension_details->num); kfree(extension_details->name.name); @@ -717,7 +718,7 @@ static void extensions_drop_group(struct config_group *group, struct config_item { struct extensions_value *value = to_extensions_value(item); - printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -859,7 +860,7 @@ static int configfs_sdcardfs_init(void) mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { - printk(KERN_ERR "Error %d while registering subsystem %s\n", + pr_err("Error %d while registering subsystem %s\n", ret, subsys->su_group.cg_item.ci_namebuf); } @@ -877,7 +878,7 @@ int packagelist_init(void) kmem_cache_create("packagelist_hashtable_entry", sizeof(struct hashtable_entry), 0, 0, NULL); if (!hashtable_entry_cachep) { - printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); return -ENOMEM; } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 34e6f318b60d..cbeac711c396 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -53,7 +53,7 @@ #define SDCARDFS_ROOT_INO 1 /* useful for tracking code reachability */ -#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) +#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) #define SDCARDFS_DIRENT_SIZE 256 diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 7d3c331379f5..67b4ae24337a 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -64,7 +64,7 @@ static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) if (sbi->options.reserved_mb) { /* Invalid statfs informations. */ if (buf->f_bsize == 0) { - printk(KERN_ERR "Returned block size is zero.\n"); + pr_err("Returned block size is zero.\n"); return -EINVAL; } @@ -100,8 +100,7 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } From 74535fe211ac06e20e386e214ff2f6a32b1bde03 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 19:32:59 -0700 Subject: [PATCH 0587/1276] ANDROID: sdcardfs: Use to kstrout Switch from deprecated simple_strtoul to kstrout Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: If18bd133b4d2877f71e58b58fc31371ff6613ed5 --- fs/sdcardfs/derived_perm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 411a9a9258b6..14747a83ef7e 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -60,6 +60,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; + unsigned long user_num; + int err; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); struct qstr q_obb = QSTR_LITERAL("obb"); @@ -88,7 +90,11 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(name->name, NULL, 10); + err = kstrtoul(name->name, 10, &user_num); + if (err) + info->userid = 0; + else + info->userid = user_num; set_top(info, &info->vfs_inode); break; case PERM_ROOT: From e6cf8dffd0147e27c69d8865e534d2cf8977c521 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 17:27:40 -0700 Subject: [PATCH 0588/1276] ANDROID: sdcardfs: Use seq_puts over seq_printf Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I3795ec61ce61e324738815b1ce3b0e09b25d723f --- fs/sdcardfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 67b4ae24337a..a3393e959c63 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -251,7 +251,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, if (vfsopts->gid != 0) seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) - seq_printf(m, ",multiuser"); + seq_puts(m, ",multiuser"); if (vfsopts->mask) seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) From 2b1ac93a90b63e3d99328bc911a3e5940a956a38 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 19:11:38 -0700 Subject: [PATCH 0589/1276] ANDROID: sdcardfs: Fix style issues in macros Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I89c4035029dc2236081a7685c55cac595d9e7ebf --- fs/sdcardfs/sdcardfs.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index cbeac711c396..380982b4a567 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -111,12 +111,6 @@ #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) -#define DEBUG_CRED() \ - printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ - __FUNCTION__, __LINE__, \ - (int)current->cred->fsuid, \ - (int)current->cred->fsgid); - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions From bab6d117426fd53aacad681a4ea6d26eabafcc47 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:14:45 -0800 Subject: [PATCH 0590/1276] ANDROID: sdcardfs: remove unnecessary call to do_munmap Adapted from wrapfs commit 5be6de9ecf02 ("Wrapfs: use vm_munmap in ->mmap") commit 2c9f6014a8bb ("Wrapfs: remove unnecessary call to vm_unmap in ->mmap") Code is unnecessary and causes deadlocks in newer kernels. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Ia252d60c60799d7e28fc5f1f0f5b5ec2430a2379 --- fs/sdcardfs/file.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index eee4eb5896e5..7e2c50b30782 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -176,12 +176,6 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ - err = do_munmap(current->mm, vma->vm_start, - vma->vm_end - vma->vm_start); - if (err) { - pr_err("sdcardfs: do_munmap failed %d\n", err); - goto out; - } } /* From 1c0bf09f19b69b69dee8ff1ece408db57e15a260 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:24:58 -0800 Subject: [PATCH 0591/1276] ANDROID: sdcardfs: copy lower inode attributes in ->ioctl Adapted from wrapfs commit fbc9c6f83ea6 ("Wrapfs: copy lower inode attributes in ->ioctl") commit e97d8e26cc9e ("Wrapfs: use file_inode helper") Some ioctls (e.g., EXT2_IOC_SETFLAGS) can change inode attributes, so copy them from lower inode. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I0f12684b9dbd4088b4a622c7ea9c03087f40e572 --- fs/sdcardfs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 7e2c50b30782..41a550fec3f2 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -113,6 +113,10 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ + if (!err) + sdcardfs_copy_and_fix_attrs(file_inode(file), + file_inode(lower_file)); out: return err; } From 42f3db55942b239290bec67b9cde55639ecd92db Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:42:01 -0800 Subject: [PATCH 0592/1276] ANDROID: sdcardfs: fix ->llseek to update upper and lower offset Adapted from wrapfs commit 1d1d23a47baa ("Wrapfs: fix ->llseek to update upper and lower offsets") Fixes bug: xfstests generic/257. f_pos consistently is required by and only by dir_ops->wrapfs_readdir, main_ops is not affected. Signed-off-by: Erez Zadok Signed-off-by: Mengyang Li Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I360a1368ac37ea8966910a58972b81504031d437 --- fs/sdcardfs/file.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 41a550fec3f2..f49df7ac370e 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -316,6 +316,29 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag) return err; } +/* + * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would + * only set the offset of the upper file. So we have to implement our + * own method to set both the upper and lower file offsets + * consistently. + */ +static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + int err; + struct file *lower_file; + + err = generic_file_llseek(file, offset, whence); + if (err < 0) + goto out; + + lower_file = sdcardfs_lower_file(file); + err = generic_file_llseek(lower_file, offset, whence); + +out: + return err; +} + + const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, .read = sdcardfs_read, @@ -334,7 +357,7 @@ const struct file_operations sdcardfs_main_fops = { /* trimmed directory options */ const struct file_operations sdcardfs_dir_fops = { - .llseek = generic_file_llseek, + .llseek = sdcardfs_file_llseek, .read = generic_read_dir, .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, From 97ad6205055e0ffe19816707a4de3dd5eb0e13ad Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:48:09 -0800 Subject: [PATCH 0593/1276] ANDROID: sdcardfs: add read_iter/write_iter opeations Adapted from wrapfs commit f398bf6a7377 ("Wrapfs: add read_iter/write_iter opeations") Signed-off-by: Erez Zadok Signed-off-by: Mengyang Li Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I2b3de59c9682fc705bf21df0de6df81e76fd2e40 --- fs/sdcardfs/file.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index f49df7ac370e..c0146e03fa2e 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -338,6 +338,52 @@ static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) return err; } +/* + * Sdcardfs read_iter, redirect modified iocb to lower read_iter + */ +ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->read_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->read_iter(iocb, iter); + /* ? wait IO finish to update atime as ecryptfs ? */ + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} + +/* + * Sdcardfs write_iter, redirect modified iocb to lower write_iter + */ +ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->write_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->write_iter(iocb, iter); + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, @@ -353,6 +399,8 @@ const struct file_operations sdcardfs_main_fops = { .release = sdcardfs_file_release, .fsync = sdcardfs_fsync, .fasync = sdcardfs_fasync, + .read_iter = sdcardfs_read_iter, + .write_iter = sdcardfs_write_iter, }; /* trimmed directory options */ From be9abc81332bf7c6fa7521313d92321b3bc21866 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 22:11:08 -0800 Subject: [PATCH 0594/1276] ANDROID: sdcardfs: use d_splice_alias adapted from wrapfs commit 9671770ff8b9 ("Wrapfs: use d_splice_alias") Refactor interpose code to allow lookup to use d_splice_alias. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Icf51db8658202c48456724275b03dc77f73f585b --- fs/sdcardfs/lookup.c | 55 +++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f028bfdebeaf..f9c02828f6aa 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -164,27 +164,25 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u } /* - * Connect a sdcardfs inode dentry/inode with several lower ones. This is - * the classic stackable file system "vnode interposition" action. - * - * @dentry: sdcardfs's dentry which interposes on lower one - * @sb: sdcardfs's super_block - * @lower_path: the lower path (caller does path_get/put) + * Helper interpose routine, called directly by ->lookup to handle + * spliced dentries. */ -int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path, userid_t id) +static struct dentry *__sdcardfs_interpose(struct dentry *dentry, + struct super_block *sb, + struct path *lower_path, + userid_t id) { - int err = 0; struct inode *inode; struct inode *lower_inode; struct super_block *lower_sb; + struct dentry *ret_dentry; lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ if (lower_inode->i_sb != lower_sb) { - err = -EXDEV; + ret_dentry = ERR_PTR(-EXDEV); goto out; } @@ -196,14 +194,32 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, /* inherit lower inode number for sdcardfs's inode */ inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { - err = PTR_ERR(inode); + ret_dentry = ERR_CAST(inode); goto out; } - d_add(dentry, inode); + ret_dentry = d_splice_alias(inode, dentry); + dentry = ret_dentry ?: dentry; update_derived_permission_lock(dentry); out: - return err; + return ret_dentry; +} + +/* + * Connect an sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path, userid_t id) +{ + struct dentry *ret_dentry; + + ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id); + return PTR_ERR(ret_dentry); } struct sdcardfs_name_data { @@ -244,6 +260,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, const struct qstr *name; struct path lower_path; struct qstr dname; + struct dentry *ret_dentry = NULL; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -330,9 +347,13 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); - if (err) /* path_put underlying path on error */ + ret_dentry = + __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); + if (IS_ERR(ret_dentry)) { + err = PTR_ERR(ret_dentry); + /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); + } goto out; } @@ -372,7 +393,9 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, err = 0; out: - return ERR_PTR(err); + if (err) + return ERR_PTR(err); + return ret_dentry; } /* From 4e90114cb1b45641dd538a582b702071d086582a Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 20:56:05 -0800 Subject: [PATCH 0595/1276] ANDROID: sdcardfs: update module info Signed-off-by: Daniel Rosenberg Change-Id: I958c7c226d4e9265fea8996803e5b004fb33d8ad --- fs/sdcardfs/main.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7344635522cd..953d2156d2e9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -471,10 +471,15 @@ static void __exit exit_sdcardfs_fs(void) pr_info("Completed sdcardfs module unload\n"); } -MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" - " (http://www.fsl.cs.sunysb.edu/)"); -MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION - " (http://wrapfs.filesystems.org/)"); +/* Original wrapfs authors */ +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)"); + +/* Original sdcardfs authors */ +MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics"); + +/* Current maintainer */ +MODULE_AUTHOR("Daniel Rosenberg, Google"); +MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION); MODULE_LICENSE("GPL"); module_init(init_sdcardfs_fs); From 0e1f7ab1492472dc134874d5503e19c13f660109 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 10 Apr 2017 20:54:30 -0700 Subject: [PATCH 0596/1276] ANDROID: sdcardfs: Directly pass lower file for mmap Instead of relying on a copy hack, pass the lower file as private data. This lets the kernel find the vma mapping for pages used by the file, allowing pages used by mapping to be reclaimed. This is adapted from following esdfs patches commit 0647e638d: ("esdfs: store lower file in vm_file for mmap") commit 064850866: ("esdfs: keep a counter for mmaped file") Change-Id: I75b74d1e5061db1b8c13be38d184e118c0851a1a Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/file.c | 3 +++ fs/sdcardfs/mmap.c | 57 ++++++++++++++++++---------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c0146e03fa2e..1f6921e2ffbf 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -192,6 +192,9 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; + vma->vm_private_data = file; + get_file(lower_file); + vma->vm_file = lower_file; out: return err; diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 51266f517fe2..391d2a7d10e9 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -23,60 +23,45 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { int err; - struct file *file, *lower_file; + struct file *file; const struct vm_operations_struct *lower_vm_ops; - struct vm_area_struct lower_vma; - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; + file = (struct file *)vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); - lower_file = sdcardfs_lower_file(file); - /* - * XXX: vm_ops->fault may be called in parallel. Because we have to - * resort to temporarily changing the vma->vm_file to point to the - * lower file, a concurrent invocation of sdcardfs_fault could see a - * different value. In this workaround, we keep a different copy of - * the vma structure in our stack, so we never expose a different - * value of the vma->vm_file called to us, even temporarily. A - * better fix would be to change the calling semantics of ->fault to - * take an explicit file pointer. - */ - lower_vma.vm_file = lower_file; - err = lower_vm_ops->fault(&lower_vma, vmf); + err = lower_vm_ops->fault(vma, vmf); return err; } +static void sdcardfs_vm_open(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + get_file(file); +} + +static void sdcardfs_vm_close(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + fput(file); +} + static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { int err = 0; - struct file *file, *lower_file; + struct file *file; const struct vm_operations_struct *lower_vm_ops; - struct vm_area_struct lower_vma; - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; + file = (struct file *)vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); if (!lower_vm_ops->page_mkwrite) goto out; - lower_file = sdcardfs_lower_file(file); - /* - * XXX: vm_ops->page_mkwrite may be called in parallel. - * Because we have to resort to temporarily changing the - * vma->vm_file to point to the lower file, a concurrent - * invocation of sdcardfs_page_mkwrite could see a different - * value. In this workaround, we keep a different copy of the - * vma structure in our stack, so we never expose a different - * value of the vma->vm_file called to us, even temporarily. - * A better fix would be to change the calling semantics of - * ->page_mkwrite to take an explicit file pointer. - */ - lower_vma.vm_file = lower_file; - err = lower_vm_ops->page_mkwrite(&lower_vma, vmf); + err = lower_vm_ops->page_mkwrite(vma, vmf); out: return err; } @@ -98,4 +83,6 @@ const struct address_space_operations sdcardfs_aops = { const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, .page_mkwrite = sdcardfs_page_mkwrite, + .open = sdcardfs_vm_open, + .close = sdcardfs_vm_close, }; From 28be4beb43f9cd73ec3fa5ae93a47cf1ddc0fa82 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 17 Apr 2017 17:11:38 -0700 Subject: [PATCH 0597/1276] ANDROID: sdcardfs: Change cache GID value Change-Id: Ieb955dd26493da26a458bc20fbbe75bca32b094f Signed-off-by: Daniel Rosenberg Bug: 37193650 --- fs/sdcardfs/derived_perm.c | 2 +- fs/sdcardfs/multiuser.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 14747a83ef7e..10153339288a 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -222,7 +222,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) break; case PERM_ANDROID_PACKAGE_CACHE: if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->d_uid); + gid = multiuser_get_ext_cache_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 2e89b5872314..d0c925cda299 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -23,6 +23,8 @@ #define AID_APP_END 19999 /* last app user */ #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ #define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */ +#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; @@ -33,9 +35,9 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(uid_t uid) +static inline gid_t multiuser_get_ext_cache_gid(uid_t uid) { - return uid - AID_APP_START + AID_CACHE_GID_START; + return uid - AID_APP_START + AID_EXT_CACHE_GID_START; } static inline gid_t multiuser_get_ext_gid(uid_t uid) From 9fc2c452aefe975b207b31111d4d664ac0ab93fd Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 12:45:48 -0700 Subject: [PATCH 0598/1276] ANDROID: sdcardfs: ->iget fixes Adapted from wrapfs commit 8c49eaa0sb9c ("Wrapfs: ->iget fixes") Change where we igrab/iput to ensure we always hold a valid lower_inode. Return ENOMEM (not EACCES) if iget5_locked returns NULL. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Id8d4e0c0cbc685a0a77685ce73c923e9a3ddc094 --- fs/sdcardfs/lookup.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f9c02828f6aa..19154b77b0fc 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -91,7 +91,9 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u struct sdcardfs_inode_info *info; struct inode_data data; struct inode *inode; /* the new inode to return */ - int err; + + if (!igrab(lower_inode)) + return ERR_PTR(-ESTALE); data.id = id; data.lower_inode = lower_inode; @@ -106,22 +108,19 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { - err = -EACCES; iput(lower_inode); - return ERR_PTR(err); + return ERR_PTR(-ENOMEM); } - /* if found a cached inode, then just return it */ - if (!(inode->i_state & I_NEW)) + /* if found a cached inode, then just return it (after iput) */ + if (!(inode->i_state & I_NEW)) { + iput(lower_inode); return inode; + } /* initialize new inode */ info = SDCARDFS_I(inode); inode->i_ino = lower_inode->i_ino; - if (!igrab(lower_inode)) { - err = -ESTALE; - return ERR_PTR(err); - } sdcardfs_set_lower_inode(inode, lower_inode); inode->i_version++; From 9bb72cf15cbcbecf43c03bc4f565a66a70da5e54 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 22:25:15 -0700 Subject: [PATCH 0599/1276] ANDROID: sdcardfs: Don't do d_add for lower fs For file based encryption, ext4 explicitly does not create negative dentries for encrypted files. If you force one over it, the decrypted file will be hidden until the cache is cleared. Instead, just fail out. Signed-off-by: Daniel Rosenberg Bug: 37231161 Change-Id: Id2a9708dfa75e1c22f89915c529789caadd2ca4b --- fs/sdcardfs/lookup.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 19154b77b0fc..126e233654fe 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -368,17 +368,15 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, dname.len = name->len; dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); lower_dentry = d_lookup(lower_dir_dentry, &dname); - if (lower_dentry) - goto setup_lower; - - lower_dentry = d_alloc(lower_dir_dentry, &dname); if (!lower_dentry) { - err = -ENOMEM; + /* We called vfs_path_lookup earlier, and did not get a negative + * dentry then. Don't confuse the lower filesystem by forcing + * one on it now... + */ + err = -ENOENT; goto out; } - d_add(lower_dentry, NULL); /* instantiate and hash */ -setup_lower: lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); sdcardfs_set_lower_path(dentry, &lower_path); From 1bc21a04c11b4873038fbaeaced41a6b160c6d27 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 22:49:38 -0700 Subject: [PATCH 0600/1276] ANDROID: sdcardfs: Don't complain in fixup_lower_ownership Not all filesystems support changing the owner of a file. We shouldn't complain if it doesn't happen. Signed-off-by: Daniel Rosenberg Bug: 37488099 Change-Id: I403e44ab7230f176e6df82f6adb4e5c82ce57f33 --- fs/sdcardfs/derived_perm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 10153339288a..29645276c734 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -252,7 +252,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) goto retry_deleg; } if (error) - pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name); } sdcardfs_put_lower_path(dentry, &path); } From 0fb5b10b28a947ba6bd056fb1a0acdf2534c58ac Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 20 Apr 2017 18:05:02 -0700 Subject: [PATCH 0601/1276] ANDROID: sdcardfs: Use filesystem specific hash We weren't accounting for FS specific hash functions, causing us to miss negative dentries for any FS that had one. Similar to a patch from esdfs commit 75bd25a9476d ("esdfs: support lower's own hash") Signed-off-by: Daniel Rosenberg Change-Id: I32d1ba304d728e0ca2648cacfb4c2e441ae63608 --- fs/sdcardfs/lookup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 126e233654fe..7d7c4515539b 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -366,8 +366,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* instatiate a new negative dentry */ dname.name = name->name; dname.len = name->len; - dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); - lower_dentry = d_lookup(lower_dir_dentry, &dname); + + /* See if the low-level filesystem might want + * to use its own hash + */ + lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); + if (IS_ERR(lower_dentry)) + return lower_dentry; + if (!lower_dentry) { /* We called vfs_path_lookup earlier, and did not get a negative * dentry then. Don't confuse the lower filesystem by forcing From 30e2f0aadce27f83e0a95e8590dbd649ff63e901 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 16:10:21 -0700 Subject: [PATCH 0602/1276] ANDROID: sdcardfs: Copy meta-data from lower inode From wrapfs commit 3ee9b365e38c ("Wrapfs: properly copy meta-data after AIO operations from lower inode") Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I9a789222e27a17b8d85ce61c45397d1839f9a675 --- fs/sdcardfs/file.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1f6921e2ffbf..6076c342dae6 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -358,9 +358,12 @@ ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) get_file(lower_file); /* prevent lower_file from being released */ iocb->ki_filp = lower_file; err = lower_file->f_op->read_iter(iocb, iter); - /* ? wait IO finish to update atime as ecryptfs ? */ iocb->ki_filp = file; fput(lower_file); + /* update upper inode atime as needed */ + if (err >= 0 || err == -EIOCBQUEUED) + fsstack_copy_attr_atime(file->f_path.dentry->d_inode, + file_inode(lower_file)); out: return err; } @@ -384,6 +387,13 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) err = lower_file->f_op->write_iter(iocb, iter); iocb->ki_filp = file; fput(lower_file); + /* update upper inode times/sizes as needed */ + if (err >= 0 || err == -EIOCBQUEUED) { + fsstack_copy_inode_size(file->f_path.dentry->d_inode, + file_inode(lower_file)); + fsstack_copy_attr_times(file->f_path.dentry->d_inode, + file_inode(lower_file)); + } out: return err; } From f748c7053194ebfaa98d309380a4bc03356da244 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 16:11:03 -0700 Subject: [PATCH 0603/1276] ANDROID: sdcardfs: Avoid setting GIDs outside of valid ranges When setting up the ownership of files on the lower filesystem, ensure that these values are in reasonable ranges for apps. If they aren't, default to AID_MEDIA_RW Signed-off-by: Daniel Rosenberg Bug: 37516160 Change-Id: I0bec76a61ac72aff0b993ab1ad04be8382178a00 --- fs/sdcardfs/derived_perm.c | 8 ++++---- fs/sdcardfs/multiuser.h | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 29645276c734..5a0ef3889846 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -215,16 +215,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) gid = AID_MEDIA_OBB; break; case PERM_ANDROID_PACKAGE: - if (info->d_uid != 0) + if (uid_is_app(info->d_uid)) gid = multiuser_get_ext_gid(info->d_uid); else - gid = multiuser_get_uid(info->userid, uid); + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); break; case PERM_ANDROID_PACKAGE_CACHE: - if (info->d_uid != 0) + if (uid_is_app(info->d_uid)) gid = multiuser_get_ext_cache_gid(info->d_uid); else - gid = multiuser_get_uid(info->userid, uid); + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); break; case PERM_PRE_ROOT: default: diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index d0c925cda299..85341e753f8c 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -35,6 +35,13 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } +static inline bool uid_is_app(uid_t uid) +{ + appid_t appid = uid % AID_USER_OFFSET; + + return appid >= AID_APP_START && appid <= AID_APP_END; +} + static inline gid_t multiuser_get_ext_cache_gid(uid_t uid) { return uid - AID_APP_START + AID_EXT_CACHE_GID_START; From 3d38f08bacdb4edbba0917daf53f60faa2d790db Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 19:49:02 -0700 Subject: [PATCH 0604/1276] ANDROID: sdcardfs: Call lower fs's revalidate We should be calling the lower filesystem's revalidate inside of sdcardfs's revalidate, as wrapfs does. Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I939d1c4192fafc1e21678aeab43fe3d588b8e2f4 --- fs/sdcardfs/dentry.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index afd97710f7ad..ae2b4babe2e5 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -60,6 +60,14 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) lower_dentry = lower_path.dentry; lower_cur_parent_dentry = dget_parent(lower_dentry); + if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { + err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); + if (err == 0) { + d_drop(dentry); + goto out; + } + } + spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); From 2d1f1c20397868121ed9c02d1df13eae823aef11 Mon Sep 17 00:00:00 2001 From: Daniel Roseberg Date: Tue, 9 May 2017 13:36:35 -0700 Subject: [PATCH 0605/1276] ANDROID: sdcardfs: Don't iput if we didn't igrab If we fail to get top, top is either NULL, or igrab found that we're in the process of freeing that inode, and did not grab it. Either way, we didn't grab it, and have no business putting it. Signed-off-by: Daniel Rosenberg Bug: 38117720 Change-Id: Ie2f587483b9abb5144263156a443e89bc69b767b --- fs/sdcardfs/inode.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 97120e85e31c..1beda0c7772b 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -625,11 +625,8 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_ERR(mnt)) return PTR_ERR(mnt); - if (!top) { - release_top(SDCARDFS_I(inode)); - WARN(1, "Top value was null!\n"); + if (!top) return -EINVAL; - } /* * Permission check on sdcardfs inode. @@ -704,10 +701,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct inode = d_inode(dentry); top = grab_top(SDCARDFS_I(inode)); - if (!top) { - release_top(SDCARDFS_I(inode)); + if (!top) return -EINVAL; - } /* * Permission check on sdcardfs inode. From 857fc5e717fc38c044f02c54bda183b3bba083d0 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 10 May 2017 23:01:15 +0800 Subject: [PATCH 0606/1276] ANDROID: sdcardfs: fix sdcardfs_destroy_inode for the inode RCU approach According to the following commits, fs: icache RCU free inodes vfs: fix the stupidity with i_dentry in inode destructors sdcardfs_destroy_inode should be fixed for the fast path safety. Signed-off-by: Gao Xiang Change-Id: I84f43c599209d23737c7e28b499dd121cb43636d --- fs/sdcardfs/super.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index a3393e959c63..8a9c9c7adca2 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -192,11 +192,18 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) return &i->vfs_inode; } -static void sdcardfs_destroy_inode(struct inode *inode) +static void i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); + kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); } +static void sdcardfs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, i_callback); +} + /* sdcardfs inode cache constructor */ static void init_once(void *obj) { From 4fceeccf1d230d49341010a23588f18095f7a296 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 15 May 2017 14:03:15 -0700 Subject: [PATCH 0607/1276] ANDROID: sdcardfs: Move top to its own struct Move top, and the associated data, to its own struct. This way, we can properly track refcounts on top without interfering with the inode's accounting. Signed-off-by: Daniel Rosenberg Bug: 38045152 Change-Id: I1968e480d966c3f234800b72e43670ca11e1d3fd --- fs/sdcardfs/dentry.c | 15 +++++ fs/sdcardfs/derived_perm.c | 130 +++++++++++++++++++------------------ fs/sdcardfs/inode.c | 54 ++++++++------- fs/sdcardfs/lookup.c | 5 +- fs/sdcardfs/main.c | 8 +-- fs/sdcardfs/packagelist.c | 2 +- fs/sdcardfs/sdcardfs.h | 106 +++++++++++++++++++----------- fs/sdcardfs/super.c | 49 ++++++++++++-- 8 files changed, 234 insertions(+), 135 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index ae2b4babe2e5..a23168179716 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -34,6 +34,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) struct dentry *parent_lower_dentry = NULL; struct dentry *lower_cur_parent_dentry = NULL; struct dentry *lower_dentry = NULL; + struct inode *inode; + struct sdcardfs_inode_data *data; if (flags & LOOKUP_RCU) return -ECHILD; @@ -103,6 +105,19 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); spin_unlock(&lower_dentry->d_lock); } + if (!err) + goto out; + + /* If our top's inode is gone, we may be out of date */ + inode = d_inode(dentry); + if (inode) { + data = top_data_get(SDCARDFS_I(inode)); + if (data->abandoned) { + d_drop(dentry); + err = 0; + } + data_put(data); + } out: dput(parent_dentry); diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 5a0ef3889846..1239d1cd208b 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -26,28 +26,28 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); struct sdcardfs_inode_info *ci = SDCARDFS_I(child); - ci->perm = PERM_INHERIT; - ci->userid = pi->userid; - ci->d_uid = pi->d_uid; - ci->under_android = pi->under_android; - ci->under_cache = pi->under_cache; - ci->under_obb = pi->under_obb; - set_top(ci, pi->top); + ci->data->perm = PERM_INHERIT; + ci->data->userid = pi->data->userid; + ci->data->d_uid = pi->data->d_uid; + ci->data->under_android = pi->data->under_android; + ci->data->under_cache = pi->data->under_cache; + ci->data->under_obb = pi->data->under_obb; + set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct inode *top) + uid_t uid, bool under_android, + struct sdcardfs_inode_data *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - info->perm = perm; - info->userid = userid; - info->d_uid = uid; - info->under_android = under_android; - info->under_cache = false; - info->under_obb = false; + info->data->perm = perm; + info->data->userid = userid; + info->data->d_uid = uid; + info->data->under_android = under_android; + info->data->under_cache = false; + info->data->under_obb = false; set_top(info, top); } @@ -58,7 +58,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_data *parent_data = + SDCARDFS_I(d_inode(parent))->data; appid_t appid; unsigned long user_num; int err; @@ -82,60 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (!S_ISDIR(d_inode(dentry)->i_mode)) return; /* Derive custom permissions based on parent and current node */ - switch (parent_info->perm) { + switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: /* Already inherited above */ break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; + info->data->perm = PERM_ROOT; err = kstrtoul(name->name, 10, &user_num); if (err) - info->userid = 0; + info->data->userid = 0; else - info->userid = user_num; - set_top(info, &info->vfs_inode); + info->data->userid = user_num; + set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID; + info->data->under_android = true; + set_top(info, info->data); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_DATA; + set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - info->under_obb = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_OBB; + info->data->under_obb = true; + set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_MEDIA; + set_top(info, info->data); } break; case PERM_ANDROID_OBB: case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: - info->perm = PERM_ANDROID_PACKAGE; + info->data->perm = PERM_ANDROID_PACKAGE; appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - set_top(info, &info->vfs_inode); + if (appid != 0 && !is_excluded(name->name, parent_data->userid)) + info->data->d_uid = + multiuser_get_uid(parent_data->userid, appid); + set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { - info->perm = PERM_ANDROID_PACKAGE_CACHE; - info->under_cache = true; + info->data->perm = PERM_ANDROID_PACKAGE_CACHE; + info->data->under_cache = true; } break; } @@ -166,7 +168,8 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct inode *delegated_inode = NULL; int error; struct sdcardfs_inode_info *info; - struct sdcardfs_inode_info *info_top; + struct sdcardfs_inode_data *info_d; + struct sdcardfs_inode_data *info_top; perm_t perm; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); uid_t uid = sbi->options.fs_low_uid; @@ -174,15 +177,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct iattr newattrs; info = SDCARDFS_I(d_inode(dentry)); - perm = info->perm; - if (info->under_obb) { + info_d = info->data; + perm = info_d->perm; + if (info_d->under_obb) { perm = PERM_ANDROID_OBB; - } else if (info->under_cache) { + } else if (info_d->under_cache) { perm = PERM_ANDROID_PACKAGE_CACHE; } else if (perm == PERM_INHERIT) { - info_top = SDCARDFS_I(grab_top(info)); + info_top = top_data_get(info); perm = info_top->perm; - release_top(info); + data_put(info_top); } switch (perm) { @@ -192,7 +196,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_MEDIA: case PERM_ANDROID_PACKAGE: case PERM_ANDROID_PACKAGE_CACHE: - uid = multiuser_get_uid(info->userid, uid); + uid = multiuser_get_uid(info_d->userid, uid); break; case PERM_ANDROID_OBB: uid = AID_MEDIA_OBB; @@ -207,24 +211,24 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: if (S_ISDIR(d_inode(dentry)->i_mode)) - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); else - gid = multiuser_get_uid(info->userid, get_type(name)); + gid = multiuser_get_uid(info_d->userid, get_type(name)); break; case PERM_ANDROID_OBB: gid = AID_MEDIA_OBB; break; case PERM_ANDROID_PACKAGE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_ANDROID_PACKAGE_CACHE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_cache_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_cache_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_PRE_ROOT: default: @@ -257,11 +261,13 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) sdcardfs_put_lower_path(dentry, &path); } -static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +static int descendant_may_need_fixup(struct sdcardfs_inode_data *data, + struct limit_search *limit) { - if (info->perm == PERM_ROOT) - return (limit->flags & BY_USERID)?info->userid == limit->userid:1; - if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) + if (data->perm == PERM_ROOT) + return (limit->flags & BY_USERID) ? + data->userid == limit->userid : 1; + if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID) return 1; return 0; } @@ -292,7 +298,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } info = SDCARDFS_I(d_inode(dentry)); - if (needs_fixup(info->perm)) { + if (needs_fixup(info->data->perm)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { spin_lock_nested(&child->d_lock, depth + 1); if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { @@ -305,7 +311,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } spin_unlock(&child->d_lock); } - } else if (descendant_may_need_fixup(info, limit)) { + } else if (descendant_may_need_fixup(info->data, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { __fixup_perms_recursive(child, limit, depth + 1); } @@ -349,12 +355,12 @@ int need_graft_path(struct dentry *dentry) struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if (parent_info->perm == PERM_ANDROID && + if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if (!(sbi->options.multiuser == false - && parent_info->userid == 0)) { + && parent_info->data->userid == 0)) { ret = 1; } } @@ -415,11 +421,11 @@ int is_base_obbpath(struct dentry *dentry) spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if (parent_info->perm == PERM_PRE_ROOT && + if (parent_info->data->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } - } else if (parent_info->perm == PERM_ANDROID && + } else if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1beda0c7772b..a3f7c8edb9d5 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -23,7 +23,8 @@ #include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data) { struct cred *cred; const struct cred *old_cred; @@ -33,10 +34,10 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_ if (!cred) return NULL; - if (info->under_obb) + if (data->under_obb) uid = AID_MEDIA_OBB; else - uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); @@ -96,7 +97,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, + SDCARDFS_I(dir)->data->userid); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); @@ -267,7 +269,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); + struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data; int touch_err = 0; struct fs_struct *saved_fs; struct fs_struct *copied_fs; @@ -336,7 +338,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode make_nomedia_in_obb = 1; } - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid); if (err) { unlock_dir(lower_parent_dentry); goto out; @@ -349,12 +351,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) - && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) + && (pd->perm == PERM_ANDROID) && (pd->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { + ((pd->perm == PERM_ANDROID) + && (qstr_case_eq(&dentry->d_name, &q_data)))) { REVERT_CRED(saved_cred); OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); @@ -620,7 +623,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma { int err; struct inode tmp; - struct inode *top = grab_top(SDCARDFS_I(inode)); + struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode)); if (IS_ERR(mnt)) return PTR_ERR(mnt); @@ -640,10 +643,11 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * locks must be dealt with to avoid undefined behavior. */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); - release_top(SDCARDFS_I(inode)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); + data_put(top); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); @@ -695,11 +699,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct struct dentry *parent; struct inode tmp; struct dentry tmp_d; - struct inode *top; + struct sdcardfs_inode_data *top; + const struct cred *saved_cred = NULL; inode = d_inode(dentry); - top = grab_top(SDCARDFS_I(inode)); + top = top_data_get(SDCARDFS_I(inode)); if (!top) return -EINVAL; @@ -717,11 +722,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); - release_top(SDCARDFS_I(inode)); + data_put(top); tmp.i_sb = inode->i_sb; tmp_d.d_inode = &tmp; @@ -824,17 +830,17 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - struct inode *top = grab_top(info); + struct sdcardfs_inode_data *top = top_data_get(info); if (!top) return -EINVAL; stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; - stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; - stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + stat->uid = make_kuid(&init_user_ns, top->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; @@ -842,7 +848,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->ctime = inode->i_ctime; stat->blksize = (1 << inode->i_blkbits); stat->blocks = inode->i_blocks; - release_top(info); + data_put(top); return 0; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7d7c4515539b..83f6083e5aad 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -71,7 +71,7 @@ struct inode_data { static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/) { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); - userid_t current_userid = SDCARDFS_I(inode)->userid; + userid_t current_userid = SDCARDFS_I(inode)->data->userid; if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) @@ -439,7 +439,8 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, + SDCARDFS_I(dir)->data->userid); if (IS_ERR(ret)) goto out; if (ret) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 953d2156d2e9..3c5b51d49d21 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -327,13 +327,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(d_inode(sb->s_root), PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 5ea6469638d8..00a0f656acc7 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -156,7 +156,7 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na struct qstr q_android_secure = QSTR_LITERAL("android_secure"); /* Always block security-sensitive files at root */ - if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { + if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) { if (qstr_case_eq(name, &q_autorun) || qstr_case_eq(name, &q__android_secure) || qstr_case_eq(name, &q_android_secure)) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 380982b4a567..3687b22a2e6b 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,8 @@ */ #define fixup_tmp_permissions(x) \ do { \ - (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ + (x)->i_uid = make_kuid(&init_user_ns, \ + SDCARDFS_I(x)->data->d_uid); \ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) @@ -97,14 +99,14 @@ */ #define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ if (!saved_cred) \ return -ENOMEM; \ } while (0) #define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ if (!saved_cred) \ return ERR_PTR(-ENOMEM); \ } while (0) @@ -142,9 +144,11 @@ typedef enum { struct sdcardfs_sb_info; struct sdcardfs_mount_options; struct sdcardfs_inode_info; +struct sdcardfs_inode_data; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred); @@ -178,18 +182,26 @@ struct sdcardfs_file_info { const struct vm_operations_struct *lower_vm_ops; }; -/* sdcardfs inode data in memory */ -struct sdcardfs_inode_info { - struct inode *lower_inode; - /* state derived based on current position in hierachy */ +struct sdcardfs_inode_data { + struct kref refcount; + bool abandoned; + perm_t perm; userid_t userid; uid_t d_uid; bool under_android; bool under_cache; bool under_obb; +}; + +/* sdcardfs inode data in memory */ +struct sdcardfs_inode_info { + struct inode *lower_inode; + /* state derived based on current position in hierarchy */ + struct sdcardfs_inode_data *data; + /* top folder for ownership */ - struct inode *top; + struct sdcardfs_inode_data *top_data; struct inode vfs_inode; }; @@ -351,39 +363,56 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb + && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } -/* grab a refererence if we aren't linking to ourself */ -static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +static inline struct sdcardfs_inode_data *data_get( + struct sdcardfs_inode_data *data) { - struct inode *old_top = NULL; - - BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) - old_top = info->top; - if (top != &info->vfs_inode) - igrab(top); - info->top = top; - iput(old_top); + if (data) + kref_get(&data->refcount); + return data; } -static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +static inline struct sdcardfs_inode_data *top_data_get( + struct sdcardfs_inode_info *info) { - struct inode *top = info->top; + return data_get(info->top_data); +} - if (top) - return igrab(top); - else - return NULL; +extern void data_release(struct kref *ref); + +static inline void data_put(struct sdcardfs_inode_data *data) +{ + kref_put(&data->refcount, data_release); +} + +static inline void release_own_data(struct sdcardfs_inode_info *info) +{ + /* + * This happens exactly once per inode. At this point, the inode that + * originally held this data is about to be freed, and all references + * to it are held as a top value, and will likely be released soon. + */ + info->data->abandoned = true; + data_put(info->data); } -static inline void release_top(struct sdcardfs_inode_info *info) +static inline void set_top(struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *top) { - iput(info->top); + struct sdcardfs_inode_data *old_top = info->top_data; + + if (top) + data_get(top); + info->top_data = top; + if (old_top) + data_put(old_top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_gid(struct vfsmount *mnt, + struct sdcardfs_inode_data *data) { struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -396,10 +425,12 @@ static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info */ return AID_SDCARD_RW; else - return multiuser_get_uid(info->userid, opts->gid); + return multiuser_get_uid(data->userid, opts->gid); } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_mode(struct vfsmount *mnt, + struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *data) { int owner_mode; int filtered_mode; @@ -407,12 +438,12 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf int visible_mode = 0775 & ~opts->mask; - if (info->perm == PERM_PRE_ROOT) { + if (data->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure * secondary users can traverse inside. */ visible_mode = 0711; - } else if (info->under_android) { + } else if (data->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. @@ -481,8 +512,9 @@ struct limit_search { userid_t userid; }; -extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top); +extern void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, bool under_android, + struct sdcardfs_inode_data *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); @@ -601,7 +633,7 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct { dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; /* 0775 */ - dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); + dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid); dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 8a9c9c7adca2..7f4539b4b249 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -26,6 +26,23 @@ */ static struct kmem_cache *sdcardfs_inode_cachep; +/* + * To support the top references, we must track some data separately. + * An sdcardfs_inode_info always has a reference to its data, and once set up, + * also has a reference to its top. The top may be itself, in which case it + * holds two references to its data. When top is changed, it takes a ref to the + * new data and then drops the ref to the old data. + */ +static struct kmem_cache *sdcardfs_inode_data_cachep; + +void data_release(struct kref *ref) +{ + struct sdcardfs_inode_data *data = + container_of(ref, struct sdcardfs_inode_data, refcount); + + kmem_cache_free(sdcardfs_inode_data_cachep, data); +} + /* final actions when unmounting a file system */ static void sdcardfs_put_super(struct super_block *sb) { @@ -166,6 +183,7 @@ static void sdcardfs_evict_inode(struct inode *inode) struct inode *lower_inode; truncate_inode_pages(&inode->i_data, 0); + set_top(SDCARDFS_I(inode), NULL); clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented @@ -173,13 +191,13 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); - set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } static struct inode *sdcardfs_alloc_inode(struct super_block *sb) { struct sdcardfs_inode_info *i; + struct sdcardfs_inode_data *d; i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL); if (!i) @@ -188,6 +206,16 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) /* memset everything up to the inode to 0 */ memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode)); + d = kmem_cache_alloc(sdcardfs_inode_data_cachep, + GFP_KERNEL | __GFP_ZERO); + if (!d) { + kmem_cache_free(sdcardfs_inode_cachep, i); + return NULL; + } + + i->data = d; + kref_init(&d->refcount); + i->vfs_inode.i_version = 1; return &i->vfs_inode; } @@ -196,6 +224,7 @@ static void i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); + release_own_data(SDCARDFS_I(inode)); kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); } @@ -214,20 +243,30 @@ static void init_once(void *obj) int sdcardfs_init_inode_cache(void) { - int err = 0; - sdcardfs_inode_cachep = kmem_cache_create("sdcardfs_inode_cache", sizeof(struct sdcardfs_inode_info), 0, SLAB_RECLAIM_ACCOUNT, init_once); + if (!sdcardfs_inode_cachep) - err = -ENOMEM; - return err; + return -ENOMEM; + + sdcardfs_inode_data_cachep = + kmem_cache_create("sdcardfs_inode_data_cache", + sizeof(struct sdcardfs_inode_data), 0, + SLAB_RECLAIM_ACCOUNT, NULL); + if (!sdcardfs_inode_data_cachep) { + kmem_cache_destroy(sdcardfs_inode_cachep); + return -ENOMEM; + } + + return 0; } /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { + kmem_cache_destroy(sdcardfs_inode_data_cachep); kmem_cache_destroy(sdcardfs_inode_cachep); } From f51470044a15a465dd3603b99487ec49287df31e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 22 May 2017 13:23:56 -0700 Subject: [PATCH 0608/1276] ANDROID: sdcardfs: Check for NULL in revalidate If the inode is in the process of being evicted, the top value may be NULL. Signed-off-by: Daniel Rosenberg Bug: 38502532 Change-Id: I0b9d04aab621e0398d44d1c5dc53293106aa5f89 --- fs/sdcardfs/dentry.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index a23168179716..e9426a61d04a 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -109,14 +109,16 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) goto out; /* If our top's inode is gone, we may be out of date */ - inode = d_inode(dentry); + inode = igrab(d_inode(dentry)); if (inode) { data = top_data_get(SDCARDFS_I(inode)); - if (data->abandoned) { + if (!data || data->abandoned) { d_drop(dentry); err = 0; } - data_put(data); + if (data) + data_put(data); + iput(inode); } out: From 8c7f6c97ac817965f2607f992ac1f3fee38af68b Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 Jun 2017 12:44:50 -0700 Subject: [PATCH 0609/1276] ANDROID: sdcardfs: d_splice_alias can return error values We must check that d_splice_alias was successful before using its output. Signed-off-by: Daniel Rosenberg Bug: 62390017 Change-Id: Ifda0a052fb3f67e35c635a4e5e907876c5400978 --- fs/sdcardfs/lookup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 83f6083e5aad..7dab5f76896b 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -199,7 +199,8 @@ static struct dentry *__sdcardfs_interpose(struct dentry *dentry, ret_dentry = d_splice_alias(inode, dentry); dentry = ret_dentry ?: dentry; - update_derived_permission_lock(dentry); + if (!IS_ERR(dentry)) + update_derived_permission_lock(dentry); out: return ret_dentry; } From 17da01b37d61a8a979e8dfe4ba3096db801b370b Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 16 May 2017 16:48:49 -0700 Subject: [PATCH 0610/1276] ANDROID: sdcardfs: remove dead function open_flags_to_access_mode() smatch warns about the suspicious formatting in the last line of open_flags_to_access_mode(). It turns out the only caller was deleted over a year ago by "ANDROID: sdcardfs: Bring up to date with Android M permissions:", so we can "fix" the function's formatting by deleting it. Change-Id: Id85946f3eb01722eef35b1815f405a6fda3aa4ff Signed-off-by: Greg Hackmann --- fs/sdcardfs/packagelist.c | 13 ------------- fs/sdcardfs/sdcardfs.h | 1 - 2 files changed, 14 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 00a0f656acc7..6da0c2186d39 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -174,19 +174,6 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na return 1; } -/* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() - */ -int open_flags_to_access_mode(int open_flags) -{ - if ((open_flags & O_ACCMODE) == O_RDONLY) - return 0; /* R_OK */ - if ((open_flags & O_ACCMODE) == O_WRONLY) - return 1; /* W_OK */ - /* Probably O_RDRW, but treat as default to be safe */ - return 1; /* R_OK | W_OK */ -} - static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, appid_t value) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 3687b22a2e6b..4e0ce49a906d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -499,7 +499,6 @@ extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); -extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); From 16662dd604be836eb9c30d03c9d20338e253ad37 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 9 May 2017 12:30:56 +0800 Subject: [PATCH 0611/1276] ANDROID: sdcardfs: use mount_nodev and fix a issue in sdcardfs_kill_sb Use the VFS mount_nodev instead of customized mount_nodev_with_options and fix generic_shutdown_super to kill_anon_super because of set_anon_super Signed-off-by: Gao Xiang Change-Id: Ibe46647aa2ce49d79291aa9d0295e9625cfccd80 --- fs/sdcardfs/main.c | 47 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 3c5b51d49d21..80825b287836 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -364,41 +364,34 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, return err; } -/* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, - const char *, void *, int)) +struct sdcardfs_mount_private { + struct vfsmount *mnt; + const char *dev_name; + void *raw_data; +}; +static int __sdcardfs_fill_super( + struct super_block *sb, + void *_priv, int silent) { - int error; - struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); - - if (IS_ERR(s)) - return ERR_CAST(s); - - s->s_flags = flags; + struct sdcardfs_mount_private *priv = _priv; - error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); - if (error) { - deactivate_locked_super(s); - return ERR_PTR(error); - } - s->s_flags |= MS_ACTIVE; - return dget(s->s_root); + return sdcardfs_read_super(priv->mnt, + sb, priv->dev_name, priv->raw_data, silent); } static struct dentry *sdcardfs_mount(struct vfsmount *mnt, struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { - /* - * dev_name is a lower_path_name, - * raw_data is a option string. - */ - return mount_nodev_with_options(mnt, fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + struct sdcardfs_mount_private priv = { + .mnt = mnt, + .dev_name = dev_name, + .raw_data = raw_data + }; + + return mount_nodev(fs_type, flags, + &priv, __sdcardfs_fill_super); } static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, @@ -423,7 +416,7 @@ void sdcardfs_kill_sb(struct super_block *sb) list_del(&sbi->list); mutex_unlock(&sdcardfs_super_list_lock); } - generic_shutdown_super(sb); + kill_anon_super(sb); } static struct file_system_type sdcardfs_fs_type = { From 43c0dca6039a1f51b6cf8c8144b6d0bbffa342b0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 19 Jul 2017 17:16:43 -0700 Subject: [PATCH 0612/1276] ANDROID: sdcardfs: Remove unnecessary lock The mmap_sem lock does not appear to be protecting anything, and has been removed in Samsung's more recent versions of sdcardfs. Signed-off-by: Daniel Rosenberg Change-Id: I76ff3e33002716b8384fc8be368028ed63dffe4e Bug: 63785372 --- fs/sdcardfs/inode.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index a3f7c8edb9d5..70a4e3cb8781 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -776,13 +776,9 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * afterwards in the other cases: we fsstack_copy_inode_size from * the lower level. */ - if (current->mm) - down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { - if (current->mm) - up_write(¤t->mm->mmap_sem); goto out; } truncate_setsize(inode, ia->ia_size); @@ -805,8 +801,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); inode_unlock(d_inode(lower_dentry)); - if (current->mm) - up_write(¤t->mm->mmap_sem); if (err) goto out; From 48960c25cdc1d960036d11f29c824a2dd98c2f1a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 6 Jul 2017 19:12:22 -0700 Subject: [PATCH 0613/1276] ANDROID: sdcardfs: override credential for ioctl to lower fs Otherwise, lower_fs->ioctl() fails due to inode_owner_or_capable(). Signed-off-by: Jaegeuk Kim Bug: 63260873 Change-Id: I623a6c7c5f8a3cbd7ec73ef89e18ddb093c43805 --- fs/sdcardfs/file.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 6076c342dae6..5ac0b0bbb0ec 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -104,12 +104,19 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, { long err = -ENOTTY; struct file *lower_file; + const struct cred *saved_cred = NULL; + struct dentry *dentry = file->f_path.dentry; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); lower_file = sdcardfs_lower_file(file); /* XXX: use vfs_ioctl if/when VFS exports it */ if (!lower_file || !lower_file->f_op) goto out; + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); @@ -117,6 +124,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (!err) sdcardfs_copy_and_fix_attrs(file_inode(file), file_inode(lower_file)); + REVERT_CRED(saved_cred); out: return err; } @@ -127,15 +135,23 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, { long err = -ENOTTY; struct file *lower_file; + const struct cred *saved_cred = NULL; + struct dentry *dentry = file->f_path.dentry; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); lower_file = sdcardfs_lower_file(file); /* XXX: use vfs_ioctl if/when VFS exports it */ if (!lower_file || !lower_file->f_op) goto out; + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + if (lower_file->f_op->compat_ioctl) err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + REVERT_CRED(saved_cred); out: return err; } From 5d6410b9a88d730955beebd375c82e868e33368c Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 19 Jul 2017 17:25:07 -0700 Subject: [PATCH 0614/1276] ANDROID: Sdcardfs: Move gid derivation under flag This moves the code to adjust the gid/uid of lower filesystem files under the mount flag derive_gid. Signed-off-by: Daniel Rosenberg Change-Id: I44eaad4ef67c7fcfda3b6ea3502afab94442610c Bug: 63245673 --- fs/sdcardfs/derived_perm.c | 3 +++ fs/sdcardfs/inode.c | 12 ++++++++---- fs/sdcardfs/main.c | 7 +++++++ fs/sdcardfs/sdcardfs.h | 1 + fs/sdcardfs/super.c | 2 ++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 1239d1cd208b..fffaad4701cf 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -176,6 +176,9 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) gid_t gid = sbi->options.fs_low_gid; struct iattr newattrs; + if (!sbi->options.gid_derivation) + return; + info = SDCARDFS_I(d_inode(dentry)); info_d = info->data; perm = info_d->perm; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 70a4e3cb8781..5e919e2956e0 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -34,10 +34,14 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, if (!cred) return NULL; - if (data->under_obb) - uid = AID_MEDIA_OBB; - else - uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); + if (sbi->options.gid_derivation) { + if (data->under_obb) + uid = AID_MEDIA_OBB; + else + uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); + } else { + uid = sbi->options.fs_low_uid; + } cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 80825b287836..0a2b5167e9a2 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -32,6 +32,7 @@ enum { Opt_multiuser, Opt_userid, Opt_reserved_mb, + Opt_gid_derivation, Opt_err, }; @@ -43,6 +44,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_mask, "mask=%u"}, {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, + {Opt_gid_derivation, "derive_gid"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -64,6 +66,8 @@ static int parse_options(struct super_block *sb, char *options, int silent, vfsopts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; + /* by default, gid derivation is off */ + opts->gid_derivation = false; *debug = 0; @@ -115,6 +119,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; opts->reserved_mb = option; break; + case Opt_gid_derivation: + opts->gid_derivation = true; + break; /* unknown option */ default: if (!silent) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 4e0ce49a906d..d1d8bab00fe5 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -219,6 +219,7 @@ struct sdcardfs_mount_options { gid_t fs_low_gid; userid_t fs_user_id; bool multiuser; + bool gid_derivation; unsigned int reserved_mb; }; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 7f4539b4b249..b89947d878e3 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -302,6 +302,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) seq_printf(m, ",userid=%u", opts->fs_user_id); + if (opts->gid_derivation) + seq_puts(m, ",derive_gid"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From c7dd98431f8301ea6e81ea0d92f7467dd21b0d97 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 2 Jan 2018 14:44:49 -0800 Subject: [PATCH 0615/1276] ANDROID: sdcardfs: Add default_normal option The default_normal option causes mounts with the gid set to AID_SDCARD_RW to have user specific gids, as in the normal case. Signed-off-by: Daniel Rosenberg Change-Id: I9619b8ac55f41415df943484dc8db1ea986cef6f Bug: 64672411 --- fs/sdcardfs/inode.c | 7 ++++--- fs/sdcardfs/main.c | 7 +++++++ fs/sdcardfs/sdcardfs.h | 9 ++++++--- fs/sdcardfs/super.c | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 5e919e2956e0..c3763c50b018 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -648,7 +648,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, inode->i_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); data_put(top); @@ -727,7 +727,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, dentry->d_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); @@ -829,6 +829,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); + struct super_block *sb = inode->i_sb; if (!top) return -EINVAL; @@ -838,7 +839,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; stat->uid = make_kuid(&init_user_ns, top->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 0a2b5167e9a2..ac27bb301c5e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -33,6 +33,7 @@ enum { Opt_userid, Opt_reserved_mb, Opt_gid_derivation, + Opt_default_normal, Opt_err, }; @@ -45,6 +46,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, {Opt_gid_derivation, "derive_gid"}, + {Opt_default_normal, "default_normal"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -68,6 +70,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->reserved_mb = 0; /* by default, gid derivation is off */ opts->gid_derivation = false; + opts->default_normal = false; *debug = 0; @@ -122,6 +125,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid_derivation: opts->gid_derivation = true; break; + case Opt_default_normal: + opts->default_normal = true; + break; /* unknown option */ default: if (!silent) @@ -175,6 +181,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, return 0; vfsopts->mask = option; break; + case Opt_default_normal: case Opt_multiuser: case Opt_userid: case Opt_fsuid: diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index d1d8bab00fe5..3da9fe94b772 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -220,6 +220,7 @@ struct sdcardfs_mount_options { userid_t fs_user_id; bool multiuser; bool gid_derivation; + bool default_normal; unsigned int reserved_mb; }; @@ -413,11 +414,13 @@ static inline void set_top(struct sdcardfs_inode_info *info, } static inline int get_gid(struct vfsmount *mnt, + struct super_block *sb, struct sdcardfs_inode_data *data) { - struct sdcardfs_vfsmount_options *opts = mnt->data; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(sb); - if (opts->gid == AID_SDCARD_RW) + if (vfsopts->gid == AID_SDCARD_RW && !sbi->options.default_normal) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -426,7 +429,7 @@ static inline int get_gid(struct vfsmount *mnt, */ return AID_SDCARD_RW; else - return multiuser_get_uid(data->userid, opts->gid); + return multiuser_get_uid(data->userid, vfsopts->gid); } static inline int get_mode(struct vfsmount *mnt, diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index b89947d878e3..87d6f836592e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -304,6 +304,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->gid_derivation) seq_puts(m, ",derive_gid"); + if (opts->default_normal) + seq_puts(m, ",default_normal"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From 2e9a639597cd214d2d202b17dac3d9f8c1bbb6dc Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Feb 2017 19:38:57 -0800 Subject: [PATCH 0616/1276] ANDROID: export security_path_chown Signed-off-by: Daniel Rosenberg BUG: 35142419 Change-Id: I05a9430a3c1bc624e019055175ad377290b4e774 --- security/security.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/security.c b/security/security.c index 736e78da1ab9..957be344cd25 100644 --- a/security/security.c +++ b/security/security.c @@ -607,6 +607,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL(security_path_chown); int security_path_chroot(const struct path *path) { From 17071a8e1e7dd6572c085ee2379c679c0052fb66 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 30 Jan 2017 12:26:08 -0800 Subject: [PATCH 0617/1276] ANDROID: fs: Export free_fs_struct and set_fs_pwd allmodconfig builds fail with: ERROR: "free_fs_struct" undefined! ERROR: "set_fs_pwd" undefined! Export the missing symbols. Change-Id: I4877ead19d7e7f0c93d4c4cad5681364284323aa Fixes: 0ec03f845799 ("ANDROID: sdcardfs: override umask on mkdir and create") Signed-off-by: Guenter Roeck --- fs/fs_struct.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 47b0ec5d5006..987c95b950f6 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -45,6 +45,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path) if (old_pwd.dentry) path_put(&old_pwd); } +EXPORT_SYMBOL(set_fs_pwd); static inline int replace_path(struct path *p, const struct path *old, const struct path *new) { @@ -90,6 +91,7 @@ void free_fs_struct(struct fs_struct *fs) path_put(&fs->pwd); kmem_cache_free(fs_cachep, fs); } +EXPORT_SYMBOL(free_fs_struct); void exit_fs(struct task_struct *tsk) { From 1a3b60ebaeb6217771ff0d10523a2c2fb13f1332 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 24 Mar 2016 10:32:35 -0700 Subject: [PATCH 0618/1276] ANDROID: fs: Export d_absolute_path The 0-day build bot reports the following build error, seen if SDCARD_FS is built as module. ERROR: "d_absolute_path" undefined! Fixes: 84a1b7d3d312 ("Included sdcardfs source code for kernel 3.0") Reported-by: Fengguang Wu Signed-off-by: Guenter Roeck --- fs/d_path.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/d_path.c b/fs/d_path.c index e8fce6b1174f..3af92cc4bdae 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -204,6 +204,7 @@ char *d_absolute_path(const struct path *path, return ERR_PTR(error); return res; } +EXPORT_SYMBOL(d_absolute_path); /* * same as __d_path but appends "(deleted)" for unlinked files. From f1e41e4147abbd72dab3a97b22d9dd23a0cf4969 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 24 Mar 2016 10:39:14 -0700 Subject: [PATCH 0619/1276] ANDROID: mm: Export do_munmap The 0-day build bot reports the following build error, seen if SDCARD_FS is built as module. ERROR: "do_munmap" undefined! Fixes: 84a1b7d3d312 ("Included sdcardfs source code for kernel 3.0") Reported-by: Fengguang Wu Signed-off-by: Guenter Roeck --- mm/mmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/mmap.c b/mm/mmap.c index 510369eb8847..53bbe0d275c1 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2796,6 +2796,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, return 0; } +EXPORT_SYMBOL(do_munmap); int vm_munmap(unsigned long start, size_t len) { From f46c9d62dd8141afe596c72e22c2e8e9bf10e46e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 30 Jan 2017 12:29:00 -0800 Subject: [PATCH 0620/1276] ANDROID: fs: Export vfs_rmdir2 allmodconfig builds fail with ERROR: "vfs_rmdir2" undefined! Export the missing function. Change-Id: I983d327e59fd34e0484f3c54d925e97d3905c19c Fixes: f9cb61dcb00c ("ANDROID: sdcardfs: User new permission2 functions") Signed-off-by: Guenter Roeck --- fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index b463413ec56e..bd04eef84c6f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3939,6 +3939,8 @@ int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry) d_delete(dentry); return error; } +EXPORT_SYMBOL(vfs_rmdir2); + int vfs_rmdir(struct inode *dir, struct dentry *dentry) { return vfs_rmdir2(NULL, dir, dentry); From db9bf31a5d86addf6bc13a781feee5734eae9cd4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 24 Jan 2018 17:25:05 -0800 Subject: [PATCH 0621/1276] ANDROID: sdcardfs: port to 4.14 Change-Id: I03271d8e8229ce6f22f337dc7d1938e0bf060f2a Signed-off-by: Daniel Rosenberg Bug: 70278506 --- fs/sdcardfs/inode.c | 9 +++++---- fs/sdcardfs/mmap.c | 13 ++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index c3763c50b018..08ef11e94cca 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -850,10 +850,11 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, data_put(top); return 0; } - -static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +static int sdcardfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) { + struct vfsmount *mnt = path->mnt; + struct dentry *dentry = path->dentry; struct kstat lower_stat; struct path lower_path; struct dentry *parent; @@ -867,7 +868,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, dput(parent); sdcardfs_get_lower_path(dentry, &lower_path); - err = vfs_getattr(&lower_path, &lower_stat); + err = vfs_getattr(&lower_path, &lower_stat, request_mask, flags); if (err) goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 391d2a7d10e9..2847c0ec5e0a 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -20,17 +20,17 @@ #include "sdcardfs.h" -static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int sdcardfs_fault(struct vm_fault *vmf) { int err; struct file *file; const struct vm_operations_struct *lower_vm_ops; - file = (struct file *)vma->vm_private_data; + file = (struct file *)vmf->vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); - err = lower_vm_ops->fault(vma, vmf); + err = lower_vm_ops->fault(vmf); return err; } @@ -48,20 +48,19 @@ static void sdcardfs_vm_close(struct vm_area_struct *vma) fput(file); } -static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int sdcardfs_page_mkwrite(struct vm_fault *vmf) { int err = 0; struct file *file; const struct vm_operations_struct *lower_vm_ops; - file = (struct file *)vma->vm_private_data; + file = (struct file *)vmf->vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); if (!lower_vm_ops->page_mkwrite) goto out; - err = lower_vm_ops->page_mkwrite(vma, vmf); + err = lower_vm_ops->page_mkwrite(vmf); out: return err; } From e19f69662df5d249341b0bb7750e406a67e3a2a1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 30 Jan 2018 14:24:02 -0800 Subject: [PATCH 0622/1276] ANDROID: Revert "fs: unexport vfs_read and vfs_write" This reverts commit bd8df82be66698042d11e7919e244c8d72b042ca. These functions are used in sdcardfs Change-Id: If740718cca903c211d1c2832c7fd95a4c4cfe20f Signed-off-by: Daniel Rosenberg --- fs/read_write.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index 8a2737f0d61d..26f71acb0c48 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -460,6 +460,8 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) return ret; } +EXPORT_SYMBOL(vfs_read); + static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; @@ -558,6 +560,8 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ return ret; } +EXPORT_SYMBOL(vfs_write); + static inline loff_t file_pos_read(struct file *file) { return file->f_pos; From c70c9d1e82d2b52d6bb46ea436517eb8faf272a1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 Jan 2018 21:31:21 -0800 Subject: [PATCH 0623/1276] ANDROID: sdcardfs: Use lower getattr times/size We now use the lower filesystem's getattr for time and size related information. Change-Id: I3dd05614a0c2837a13eeb033444fbdf070ddce2a Signed-off-by: Daniel Rosenberg Bug: 72007585 --- fs/sdcardfs/inode.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 08ef11e94cca..b43258684fb9 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -824,8 +824,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, - struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, + struct kstat *lower_stat, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); @@ -841,12 +841,12 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->uid = make_kuid(&init_user_ns, top->d_uid); stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; - stat->size = i_size_read(inode); - stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; - stat->blksize = (1 << inode->i_blkbits); - stat->blocks = inode->i_blocks; + stat->size = lower_stat->size; + stat->atime = lower_stat->atime; + stat->mtime = lower_stat->mtime; + stat->ctime = lower_stat->ctime; + stat->blksize = lower_stat->blksize; + stat->blocks = lower_stat->blocks; data_put(top); return 0; } @@ -873,8 +873,7 @@ static int sdcardfs_getattr(const struct path *path, struct kstat *stat, goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), d_inode(lower_path.dentry)); - err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); - stat->blocks = lower_stat.blocks; + err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat); out: sdcardfs_put_lower_path(dentry, &lower_path); return err; From 34df4102216ea810af862150d9596d24139fbc14 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 15:02:50 -0800 Subject: [PATCH 0624/1276] ANDROID: fsnotify: Notify lower fs of open If the filesystem being watched supports d_canonical_path, notify the lower filesystem of the open as well. Change-Id: I2b1739e068afbaf5eb39950516072bff8345ebfe Signed-off-by: Daniel Rosenberg Bug: 70706497 --- include/linux/fsnotify.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index fd1ce10553bf..61b72519f2f2 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -210,12 +210,19 @@ static inline void fsnotify_modify(struct file *file) static inline void fsnotify_open(struct file *file) { const struct path *path = &file->f_path; + struct path lower_path; struct inode *inode = file_inode(file); __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; + if (path->dentry->d_op && path->dentry->d_op->d_canonical_path) { + path->dentry->d_op->d_canonical_path(path, &lower_path); + fsnotify_parent(&lower_path, NULL, mask); + fsnotify(lower_path.dentry->d_inode, mask, &lower_path, FSNOTIFY_EVENT_PATH, NULL, 0); + path_put(&lower_path); + } fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } From 04e961477d62c6893f95519b07dc90ccc4a6c715 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Feb 2018 16:52:22 -0800 Subject: [PATCH 0625/1276] ANDROID: sdcardfs: Protect set_top If the top is changed while we're attempting to use it, it's possible that the reference will be put while we are in the process of grabbing a reference. Now we grab a spinlock to protect grabbing our reference count. Additionally, we now set the inode_info's top value to point to it's own data when initializing, which makes tracking changes easier. Change-Id: If15748c786ce4c0480ab8c5051a92523aff284d2 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 28 +++++++++++++--------------- fs/sdcardfs/main.c | 6 ++---- fs/sdcardfs/sdcardfs.h | 26 ++++++++++++++++++-------- fs/sdcardfs/super.c | 3 +++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index fffaad4701cf..0b3b22334e54 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -32,23 +32,20 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->data->under_android = pi->data->under_android; ci->data->under_cache = pi->data->under_cache; ci->data->under_obb = pi->data->under_obb; - set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct sdcardfs_inode_data *top) + uid_t uid) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->data->perm = perm; info->data->userid = userid; info->data->d_uid = uid; - info->data->under_android = under_android; + info->data->under_android = false; info->data->under_cache = false; info->data->under_obb = false; - set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, @@ -58,8 +55,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_data *parent_data = - SDCARDFS_I(d_inode(parent))->data; + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_data *parent_data = parent_info->data; appid_t appid; unsigned long user_num; int err; @@ -80,13 +77,15 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, inherit_derived_state(d_inode(parent), d_inode(dentry)); /* Files don't get special labels */ - if (!S_ISDIR(d_inode(dentry)->i_mode)) + if (!S_ISDIR(d_inode(dentry)->i_mode)) { + set_top(info, parent_info); return; + } /* Derive custom permissions based on parent and current node */ switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: - /* Already inherited above */ + set_top(info, parent_info); break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ @@ -96,7 +95,6 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, info->data->userid = 0; else info->data->userid = user_num; - set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ @@ -104,24 +102,24 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID; info->data->under_android = true; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_DATA; - set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_OBB; info->data->under_obb = true; - set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_MEDIA; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID_OBB: @@ -132,13 +130,13 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (appid != 0 && !is_excluded(name->name, parent_data->userid)) info->data->d_uid = multiuser_get_uid(parent_data->userid, appid); - set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { info->data->perm = PERM_ANDROID_PACKAGE_CACHE; info->data->under_cache = true; } + set_top(info, parent_info); break; } } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index ac27bb301c5e..e4fd3fbb05e6 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -341,13 +341,11 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(d_inode(sb->s_root))->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(d_inode(sb->s_root), PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(d_inode(sb->s_root))->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 3da9fe94b772..610466ad153d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -201,6 +201,7 @@ struct sdcardfs_inode_info { struct sdcardfs_inode_data *data; /* top folder for ownership */ + spinlock_t top_lock; struct sdcardfs_inode_data *top_data; struct inode vfs_inode; @@ -380,7 +381,12 @@ static inline struct sdcardfs_inode_data *data_get( static inline struct sdcardfs_inode_data *top_data_get( struct sdcardfs_inode_info *info) { - return data_get(info->top_data); + struct sdcardfs_inode_data *top_data; + + spin_lock(&info->top_lock); + top_data = data_get(info->top_data); + spin_unlock(&info->top_lock); + return top_data; } extern void data_release(struct kref *ref); @@ -402,15 +408,20 @@ static inline void release_own_data(struct sdcardfs_inode_info *info) } static inline void set_top(struct sdcardfs_inode_info *info, - struct sdcardfs_inode_data *top) + struct sdcardfs_inode_info *top_owner) { - struct sdcardfs_inode_data *old_top = info->top_data; + struct sdcardfs_inode_data *old_top; + struct sdcardfs_inode_data *new_top = NULL; + + if (top_owner) + new_top = top_data_get(top_owner); - if (top) - data_get(top); - info->top_data = top; + spin_lock(&info->top_lock); + old_top = info->top_data; + info->top_data = new_top; if (old_top) data_put(old_top); + spin_unlock(&info->top_lock); } static inline int get_gid(struct vfsmount *mnt, @@ -516,8 +527,7 @@ struct limit_search { }; extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android, - struct sdcardfs_inode_data *top); + userid_t userid, uid_t uid); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 87d6f836592e..cffcdb11cb8a 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -215,6 +215,9 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) i->data = d; kref_init(&d->refcount); + i->top_data = d; + spin_lock_init(&i->top_lock); + kref_get(&d->refcount); i->vfs_inode.i_version = 1; return &i->vfs_inode; From 1ed04b79d28119650ae0a1d66148f7ebdb22a9f3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 20 Feb 2018 20:25:45 -0800 Subject: [PATCH 0626/1276] ANDROID: sdcardfs: Hold i_mutex for i_size_write When we call i_size_write, we must be holding i_mutex to avoid possible lockups on 32 bit/SMP architectures. This is not necessary on 64 bit architectures. Change-Id: Ic3b946507c54d81b5c9046f9b57d25d4b0f9feef Signed-off-by: Daniel Rosenberg Bug: 73287721 --- fs/sdcardfs/file.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 5ac0b0bbb0ec..2879d1291a11 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -62,6 +62,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, int err; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; + struct inode *inode = d_inode(dentry); /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { @@ -73,10 +74,12 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, err = vfs_write(lower_file, buf, count, ppos); /* update our inode times+sizes upon a successful lower write */ if (err >= 0) { - fsstack_copy_inode_size(d_inode(dentry), - file_inode(lower_file)); - fsstack_copy_attr_times(d_inode(dentry), - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_unlock(inode); } return err; @@ -391,6 +394,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) { int err; struct file *file = iocb->ki_filp, *lower_file; + struct inode *inode = file->f_path.dentry->d_inode; lower_file = sdcardfs_lower_file(file); if (!lower_file->f_op->write_iter) { @@ -405,10 +409,12 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) fput(lower_file); /* update upper inode times/sizes as needed */ if (err >= 0 || err == -EIOCBQUEUED) { - fsstack_copy_inode_size(file->f_path.dentry->d_inode, - file_inode(lower_file)); - fsstack_copy_attr_times(file->f_path.dentry->d_inode, - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); } out: return err; From 77f52fc109820039801f96096e8f7de6bc279197 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 4 Dec 2017 09:51:07 +0530 Subject: [PATCH 0627/1276] ANDROID: sdcardfs: Set num in extension_details during make_item Without this patch when you delete an extension from configfs it still exists in the hash table data structures and we are unable to delete it or change it's group. This happens because during deletion the key & value is taken from extension_details, and was not properly set. Fix it by this patch. Change-Id: I7c20cb1ab4d99e6aceadcb5ef850f0bb47f18be8 Signed-off-by: Ritesh Harjani Signed-off-by: Daniel Rosenberg Bug: 73055997 --- fs/sdcardfs/packagelist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 6da0c2186d39..4b9a5635f1e0 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -659,6 +659,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou return ERR_PTR(-ENOMEM); } qstr_init(&extension_details->name, tmp); + extension_details->num = extensions_value->num; ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); if (ret) { From c9c096ef0e679445f496b15843529153f1b21f6c Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 7 Mar 2018 14:11:03 -0800 Subject: [PATCH 0628/1276] ANDROID: uid_sys_stats: Copy task_struct comm field to bigger buffer get_task_comm() currently checks if buf_size != TASK_COMM_LEN and fails even if sizeof(buf) > TASK_COMM_LEN. Change-Id: Icb3e9c172607534ef1db10baf5d626083db73498 Signed-off-by: Dmitry Shmidt --- drivers/misc/uid_sys_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 9f5b6d1dde0a..ace629934821 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -129,7 +129,7 @@ static void get_full_task_comm(struct task_entry *task_entry, struct mm_struct *mm = task->mm; /* fill the first TASK_COMM_LEN bytes with thread name */ - get_task_comm(task_entry->comm, task); + __get_task_comm(task_entry->comm, TASK_COMM_LEN, task); i = strlen(task_entry->comm); while (i < TASK_COMM_LEN) task_entry->comm[i++] = ' '; From d71596efa2477966976246e6397f557bc2724008 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 15 Mar 2018 19:37:16 -0700 Subject: [PATCH 0629/1276] ANDROID: sdcardfs: fix lock issue on 32 bit/SMP architectures Fixes: cc668ff4b6a1 ("ANDROID: sdcardfs: Hold i_mutex for i_size_write") Change-Id: If7f2ed90f59c552b9ef9262b0f6aaed394f68784 Signed-off-by: Daniel Rosenberg Bug: 73287721 --- fs/sdcardfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 2879d1291a11..1461254f301d 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -414,7 +414,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) fsstack_copy_inode_size(inode, file_inode(lower_file)); fsstack_copy_attr_times(inode, file_inode(lower_file)); if (sizeof(loff_t) > sizeof(long)) - inode_lock(inode); + inode_unlock(inode); } out: return err; From 4672e99aa6b6bbd00bcce1b6fef2f2233eefc989 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 26 Mar 2018 20:43:33 +0530 Subject: [PATCH 0630/1276] ANDROID: arm64: Image.gz-dtb build target depends on Image.gz While doing parallel builds using "make -j" option, I ran into a build race condition a few times where-in Image.gz-dtb target starts building before Image.gz is even ready, resulting in a corrupt Image.gz-dtb kernel image. How to reproduce --> $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-androidkernel- defconfig menuconfig + CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES="qcom/apq8096-db820c" $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-androidkernel- -j9 .. SYSMAP System.map OBJCOPY arch/arm64/boot/Image GZIP arch/arm64/boot/Image.gz DTC arch/arm64/boot/dts/qcom/apq8096-db820c.dtb Building modules, stage 2. CAT arch/arm64/boot/Image.gz-dtb GZIP arch/arm64/boot/Image.gz .. $ du -sh arch/arm64/boot/Image.gz-dtb 28K arch/arm64/boot/Image.gz-dtb When built with this patch --> $ du -sh arch/arm64/boot/Image.gz-dtb 8.9M arch/arm64/boot/Image.gz-dtb Let's make Image.gz-dtb build target depend on Image.gz explicitly. Signed-off-by: Amit Pundir --- arch/arm64/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 2dbca8c11498..0b0f5b7cead1 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -143,7 +143,10 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts -Image-dtb Image.gz-dtb: vmlinux scripts dtbs +Image-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +Image.gz-dtb: vmlinux scripts dtbs Image.gz $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ PHONY += vdso_install From ee6b07fced4acf9456d14d6009eb8cb9239e9513 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 15:49:54 +0530 Subject: [PATCH 0631/1276] ANDROID: sdcardfs: Fix sdcardfs to stop creating cases-sensitive duplicate entries. sdcardfs_name_match gets a 'name' argument from the underlying FS. This need not be null terminated string. So in sdcardfs_name_match -> qstr_case_eq -> we should use str_n_case_eq. This happens because few of the entries in lower level FS may not be NULL terminated and may have some garbage characters passed while doing sdcardfs_name_match. For e.g. # dmesg |grep Download [ 103.646386] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 104.021340] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 105.196864] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 109.113521] sdcardfs_name_match: q1->name=logs, q1->len=4, q2->name=Download\x17\x80\x03, q2->len=8 Now when we try to create a directory with different case for a such files. SDCARDFS creates a entry if it could not find the underlying entry in it's dcache. To reproduce:- 1. bootup the device wait for some time after sdcardfs mounting to complete. 2. cd /storage/emulated/0 3. echo 3 > /proc/sys/vm/drop_caches 4. mkdir download We now start seeing two entries with name. Download & download. Change-Id: I976d92a220a607dd8cdb96c01c2041c5c2bc3326 Signed-off-by: Ritesh Harjani bug: 75987238 --- fs/sdcardfs/sdcardfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 610466ad153d..826afb5c7e88 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -669,7 +669,7 @@ static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { - return q1->len == q2->len && str_case_eq(q1->name, q2->name); + return q1->len == q2->len && str_n_case_eq(q1->name, q2->name, q2->len); } #define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) From 7612a7e8e709100eb43f1707da2516e89a829752 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 16:03:09 +0530 Subject: [PATCH 0632/1276] ANDROID: fuse: Add null terminator to path in canonical path to avoid issue page allocated in fuse_dentry_canonical_path to be handled in fuse_dev_do_write is allocated using __get_free_pages(GFP_KERNEL). This may not return a page with data filled with 0. Now this page may not have a null terminator at all. If this happens and userspace fuse daemon screws up by passing a string to kernel which is not NULL terminated (or did not fill anything), then inside fuse driver in kernel when we try to do strlen(fuse_dev_write->kern_path->getname_kernel) on that page data -> it may give us issue with kernel paging request. Unable to handle kernel paging request at virtual address ------------[ cut here ]------------ <..> PC is at strlen+0x10/0x90 LR is at getname_kernel+0x2c/0xf4 <..> strlen+0x10/0x90 kern_path+0x28/0x4c fuse_dev_do_write+0x5b8/0x694 fuse_dev_write+0x74/0x94 do_iter_readv_writev+0x80/0xb8 do_readv_writev+0xec/0x1cc vfs_writev+0x54/0x64 SyS_writev+0x64/0xe4 el0_svc_naked+0x24/0x28 To avoid this we should ensure in case of FUSE_CANONICAL_PATH, the page is null terminated. Change-Id: I33ca7cc76b4472eaa982c67bb20685df451121f5 Signed-off-by: Ritesh Harjani Bug: 75984715 [Daniel - small edit, using args size ] Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 9fc81f1f0e7e..c5d395f4f181 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1902,8 +1902,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, err = copy_out_args(cs, &req->out, nbytes); if (req->in.h.opcode == FUSE_CANONICAL_PATH) { - req->out.h.error = kern_path((char *)req->out.args[0].value, 0, - req->canonical_path); + char *path = (char *)req->out.args[0].value; + + path[req->out.args[0].size - 1] = 0; + req->out.h.error = kern_path(path, 0, req->canonical_path); } fuse_copy_finish(cs); From eefaed805b7695889d1c7a48641339b62c70999a Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Wed, 31 Jan 2018 18:11:57 -0800 Subject: [PATCH 0633/1276] ANDROID: cpufreq: track per-task time in state Add time in state data to task structs, and create /proc//time_in_state files to show how long each individual task has run at each frequency. Create a CONFIG_CPU_FREQ_TIMES option to enable/disable this tracking. Signed-off-by: Connor O'Brien Bug: 72339335 Test: Read /proc//time_in_state Change-Id: Ia6456754f4cb1e83b2bc35efa8fbe9f8696febc8 --- drivers/cpufreq/Kconfig | 7 ++ drivers/cpufreq/Makefile | 5 +- drivers/cpufreq/cpufreq.c | 3 + drivers/cpufreq/cpufreq_times.c | 204 ++++++++++++++++++++++++++++++++ fs/proc/base.c | 7 ++ include/linux/cpufreq_times.h | 35 ++++++ include/linux/sched.h | 4 + kernel/exit.c | 4 + kernel/sched/core.c | 5 + kernel/sched/cputime.c | 10 ++ 10 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 drivers/cpufreq/cpufreq_times.c create mode 100644 include/linux/cpufreq_times.h diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 608af20a3494..e1312374725b 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -37,6 +37,13 @@ config CPU_FREQ_STAT If in doubt, say N. +config CPU_FREQ_TIMES + bool "CPU frequency time-in-state statistics" + help + Export CPU time-in-state information through procfs. + + If in doubt, say N. + choice prompt "Default CPUFreq governor" default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c1ffeabe4ecf..648beca8ad41 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -5,7 +5,10 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o -# CPUfreq governors +# CPUfreq times +obj-$(CONFIG_CPU_FREQ_TIMES) += cpufreq_times.o + +# CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f53fb41efb7b..98b5bac02dff 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -349,6 +350,7 @@ static void cpufreq_notify_transition(struct cpufreq_policy *policy, } cpufreq_stats_record_transition(policy, freqs->new); + cpufreq_times_record_transition(freqs); policy->cur = freqs->new; } } @@ -1295,6 +1297,7 @@ static int cpufreq_online(unsigned int cpu) goto out_destroy_policy; cpufreq_stats_create_table(policy); + cpufreq_times_create_policy(policy); write_lock_irqsave(&cpufreq_driver_lock, flags); list_add(&policy->policy_list, &cpufreq_policy_list); diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c new file mode 100644 index 000000000000..fa46fcec5388 --- /dev/null +++ b/drivers/cpufreq/cpufreq_times.c @@ -0,0 +1,204 @@ +/* drivers/cpufreq/cpufreq_times.c + * + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */ + +/** + * struct cpu_freqs - per-cpu frequency information + * @offset: start of these freqs' stats in task time_in_state array + * @max_state: number of entries in freq_table + * @last_index: index in freq_table of last frequency switched to + * @freq_table: list of available frequencies + */ +struct cpu_freqs { + unsigned int offset; + unsigned int max_state; + unsigned int last_index; + unsigned int freq_table[0]; +}; + +static struct cpu_freqs *all_freqs[NR_CPUS]; + +static unsigned int next_offset; + +void cpufreq_task_times_init(struct task_struct *p) +{ + void *temp; + unsigned long flags; + unsigned int max_state; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = 0; + + max_state = READ_ONCE(next_offset); + + /* We use one array to avoid multiple allocs per task */ + temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC); + if (!temp) + return; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = temp; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = max_state; +} + +/* Caller must hold task_time_in_state_lock */ +static int cpufreq_task_times_realloc_locked(struct task_struct *p) +{ + void *temp; + unsigned int max_state = READ_ONCE(next_offset); + + temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC); + if (!temp) + return -ENOMEM; + p->time_in_state = temp; + memset(p->time_in_state + p->max_state, 0, + (max_state - p->max_state) * sizeof(u64)); + p->max_state = max_state; + return 0; +} + +void cpufreq_task_times_exit(struct task_struct *p) +{ + unsigned long flags; + void *temp; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + temp = p->time_in_state; + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + kfree(temp); +} + +int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *p) +{ + unsigned int cpu, i; + u64 cputime; + unsigned long flags; + struct cpu_freqs *freqs; + struct cpu_freqs *last_freqs = NULL; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + + seq_printf(m, "cpu%u\n", cpu); + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == CPUFREQ_ENTRY_INVALID) + continue; + cputime = 0; + if (freqs->offset + i < p->max_state && + p->time_in_state) + cputime = p->time_in_state[freqs->offset + i]; + seq_printf(m, "%u %lu\n", freqs->freq_table[i], + (unsigned long)nsec_to_clock_t(cputime)); + } + } + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + return 0; +} + +void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) +{ + unsigned long flags; + unsigned int state; + struct cpu_freqs *freqs = all_freqs[task_cpu(p)]; + + if (!freqs || p->flags & PF_EXITING) + return; + + state = freqs->offset + READ_ONCE(freqs->last_index); + + spin_lock_irqsave(&task_time_in_state_lock, flags); + if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) && + p->time_in_state) + p->time_in_state[state] += cputime; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); +} + +void cpufreq_times_create_policy(struct cpufreq_policy *policy) +{ + int cpu, index; + unsigned int count = 0; + struct cpufreq_frequency_table *pos, *table; + struct cpu_freqs *freqs; + void *tmp; + + if (all_freqs[policy->cpu]) + return; + + table = policy->freq_table; + if (!table) + return; + + cpufreq_for_each_entry(pos, table) + count++; + + tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count, + GFP_KERNEL); + if (!tmp) + return; + + freqs = tmp; + freqs->max_state = count; + + index = cpufreq_frequency_table_get_index(policy, policy->cur); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_for_each_entry(pos, table) + freqs->freq_table[pos - table] = pos->frequency; + + freqs->offset = next_offset; + WRITE_ONCE(next_offset, freqs->offset + count); + for_each_cpu(cpu, policy->related_cpus) + all_freqs[cpu] = freqs; +} + +void cpufreq_times_record_transition(struct cpufreq_freqs *freq) +{ + int index; + struct cpu_freqs *freqs = all_freqs[freq->cpu]; + struct cpufreq_policy *policy; + + if (!freqs) + return; + + policy = cpufreq_cpu_get(freq->cpu); + if (!policy) + return; + + index = cpufreq_frequency_table_get_index(policy, freq->new); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_cpu_put(policy); +} diff --git a/fs/proc/base.c b/fs/proc/base.c index 7e9f07bf260d..b51e81e1158c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -94,6 +94,7 @@ #include #include #include +#include #include #include "internal.h" #include "fd.h" @@ -3006,6 +3007,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_LIVEPATCH ONE("patch_state", S_IRUSR, proc_pid_patch_state), #endif +#ifdef CONFIG_CPU_FREQ_TIMES + ONE("time_in_state", 0444, proc_time_in_state_show), +#endif }; static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) @@ -3384,6 +3388,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_LIVEPATCH ONE("patch_state", S_IRUSR, proc_pid_patch_state), #endif +#ifdef CONFIG_CPU_FREQ_TIMES + ONE("time_in_state", 0444, proc_time_in_state_show), +#endif }; static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx) diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h new file mode 100644 index 000000000000..8cdbbc8fccec --- /dev/null +++ b/include/linux/cpufreq_times.h @@ -0,0 +1,35 @@ +/* drivers/cpufreq/cpufreq_times.c + * + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_CPUFREQ_TIMES_H +#define _LINUX_CPUFREQ_TIMES_H + +#include +#include + +#ifdef CONFIG_CPU_FREQ_TIMES +void cpufreq_task_times_init(struct task_struct *p); +void cpufreq_task_times_exit(struct task_struct *p); +int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *p); +void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); +void cpufreq_times_create_policy(struct cpufreq_policy *policy); +void cpufreq_times_record_transition(struct cpufreq_freqs *freq); +#else +static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} +static inline void cpufreq_times_record_transition( + struct cpufreq_freqs *freq) {} +#endif /* CONFIG_CPU_FREQ_TIMES */ +#endif /* _LINUX_CPUFREQ_TIMES_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 977cb57d7bc9..97f4d64d0b92 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -799,6 +799,10 @@ struct task_struct { u64 stimescaled; #endif u64 gtime; +#ifdef CONFIG_CPU_FREQ_TIMES + u64 *time_in_state; + unsigned int max_state; +#endif struct prev_cputime prev_cputime; #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN struct vtime vtime; diff --git a/kernel/exit.c b/kernel/exit.c index 0e21e6d21f35..07eceecc6402 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -186,6 +187,9 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; +#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_exit(p); +#endif repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ad97f3ba5ec5..6b2a27bd29bc 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6,6 +6,7 @@ * Copyright (C) 1991-2002 Linus Torvalds */ #include "sched.h" +#include #include @@ -2156,6 +2157,10 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif +#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_init(p); +#endif + RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); init_dl_inactive_task_timer(&p->dl); diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 0796f938c4f0..ab9b6ff7117a 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -1,6 +1,7 @@ /* * Simple CPU accounting cgroup controller */ +#include #include "sched.h" #ifdef CONFIG_IRQ_TIME_ACCOUNTING @@ -128,6 +129,11 @@ void account_user_time(struct task_struct *p, u64 cputime) /* Account for user time used */ acct_account_cputime(p); + +#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for user time */ + cpufreq_acct_update_power(p, cputime); +#endif } /* @@ -172,6 +178,10 @@ void account_system_index_time(struct task_struct *p, /* Account for system time used */ acct_account_cputime(p); +#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for system time */ + cpufreq_acct_update_power(p, cputime); +#endif } /* From 6fb7ea7ea483efd93ff66913849e41ccae241818 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Tue, 6 Feb 2018 13:30:27 -0800 Subject: [PATCH 0634/1276] ANDROID: cpufreq: times: track per-uid time in state Add /proc/uid_time_in_state showing per uid/frequency/cluster times. Allow uid removal through /proc/uid_cputime/remove_uid_range. Signed-off-by: Connor O'Brien Bug: 72339335 Test: Read /proc/uid_time_in_state Change-Id: I20ba3546a27c25b7e7991e2a86986e158aafa58c --- drivers/cpufreq/cpufreq_times.c | 208 ++++++++++++++++++++++++++++++++ drivers/misc/uid_sys_stats.c | 5 + include/linux/cpufreq_times.h | 3 + 3 files changed, 216 insertions(+) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index fa46fcec5388..5e19ac5c8157 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -15,14 +15,30 @@ #include #include +#include +#include #include +#include #include #include #include #include #include +#define UID_HASH_BITS 10 + +static DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS); + static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */ +static DEFINE_SPINLOCK(uid_lock); /* uid_hash_table */ + +struct uid_entry { + uid_t uid; + unsigned int max_state; + struct hlist_node hash; + struct rcu_head rcu; + u64 time_in_state[0]; +}; /** * struct cpu_freqs - per-cpu frequency information @@ -42,6 +58,137 @@ static struct cpu_freqs *all_freqs[NR_CPUS]; static unsigned int next_offset; +/* Caller must hold uid lock */ +static struct uid_entry *find_uid_entry_locked(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +/* Caller must hold uid lock */ +static struct uid_entry *find_or_register_uid_locked(uid_t uid) +{ + struct uid_entry *uid_entry, *temp; + unsigned int max_state = READ_ONCE(next_offset); + size_t alloc_size = sizeof(*uid_entry) + max_state * + sizeof(uid_entry->time_in_state[0]); + + uid_entry = find_uid_entry_locked(uid); + if (uid_entry) { + if (uid_entry->max_state == max_state) + return uid_entry; + /* uid_entry->time_in_state is too small to track all freqs, so + * expand it. + */ + temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC); + if (!temp) + return uid_entry; + temp->max_state = max_state; + memset(temp->time_in_state + uid_entry->max_state, 0, + (max_state - uid_entry->max_state) * + sizeof(uid_entry->time_in_state[0])); + if (temp != uid_entry) { + hlist_replace_rcu(&uid_entry->hash, &temp->hash); + kfree_rcu(uid_entry, rcu); + } + return temp; + } + + uid_entry = kzalloc(alloc_size, GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + uid_entry->max_state = max_state; + + hash_add_rcu(uid_hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static bool freq_index_invalid(unsigned int index) +{ + unsigned int cpu; + struct cpu_freqs *freqs; + + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || index < freqs->offset || + freqs->offset + freqs->max_state <= index) + continue; + return freqs->freq_table[index - freqs->offset] == + CPUFREQ_ENTRY_INVALID; + } + return true; +} + +static void *uid_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + (*pos)++; + + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void uid_seq_stop(struct seq_file *seq, void *v) { } + +static int uid_time_in_state_seq_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct cpu_freqs *freqs, *last_freqs = NULL; + int i, cpu; + + if (v == uid_hash_table) { + seq_puts(m, "uid:"); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == + CPUFREQ_ENTRY_INVALID) + continue; + seq_printf(m, " %d", freqs->freq_table[i]); + } + } + seq_putc(m, '\n'); + } + + rcu_read_lock(); + + hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) { + if (uid_entry->max_state) + seq_printf(m, "%d:", uid_entry->uid); + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + seq_printf(m, " %lu", (unsigned long)nsec_to_clock_t( + uid_entry->time_in_state[i])); + } + if (uid_entry->max_state) + seq_putc(m, '\n'); + } + + rcu_read_unlock(); + return 0; +} + void cpufreq_task_times_init(struct task_struct *p) { void *temp; @@ -87,6 +234,9 @@ void cpufreq_task_times_exit(struct task_struct *p) unsigned long flags; void *temp; + if (!p->time_in_state) + return; + spin_lock_irqsave(&task_time_in_state_lock, flags); temp = p->time_in_state; p->time_in_state = NULL; @@ -130,7 +280,9 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) { unsigned long flags; unsigned int state; + struct uid_entry *uid_entry; struct cpu_freqs *freqs = all_freqs[task_cpu(p)]; + uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); if (!freqs || p->flags & PF_EXITING) return; @@ -142,6 +294,12 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) p->time_in_state) p->time_in_state[state] += cputime; spin_unlock_irqrestore(&task_time_in_state_lock, flags); + + spin_lock_irqsave(&uid_lock, flags); + uid_entry = find_or_register_uid_locked(uid); + if (uid_entry && state < uid_entry->max_state) + uid_entry->time_in_state[state] += cputime; + spin_unlock_irqrestore(&uid_lock, flags); } void cpufreq_times_create_policy(struct cpufreq_policy *policy) @@ -183,6 +341,27 @@ void cpufreq_times_create_policy(struct cpufreq_policy *policy) all_freqs[cpu] = freqs; } +void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + unsigned long flags; + + spin_lock_irqsave(&uid_lock, flags); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp, + hash, uid_start) { + if (uid_start == uid_entry->uid) { + hash_del_rcu(&uid_entry->hash); + kfree_rcu(uid_entry, rcu); + } + } + } + + spin_unlock_irqrestore(&uid_lock, flags); +} + void cpufreq_times_record_transition(struct cpufreq_freqs *freq) { int index; @@ -202,3 +381,32 @@ void cpufreq_times_record_transition(struct cpufreq_freqs *freq) cpufreq_cpu_put(policy); } + +static const struct seq_operations uid_time_in_state_seq_ops = { + .start = uid_seq_start, + .next = uid_seq_next, + .stop = uid_seq_stop, + .show = uid_time_in_state_seq_show, +}; + +static int uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &uid_time_in_state_seq_ops); +} + +static const struct file_operations uid_time_in_state_fops = { + .open = uid_time_in_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init cpufreq_times_init(void) +{ + proc_create_data("uid_time_in_state", 0444, NULL, + &uid_time_in_state_fops, NULL); + + return 0; +} + +early_initcall(cpufreq_times_init); diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ace629934821..fbf7db598d55 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -419,6 +420,10 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } + + /* Also remove uids from /proc/uid_time_in_state */ + cpufreq_task_times_remove_uids(uid_start, uid_end); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index 8cdbbc8fccec..b7ea7d343317 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -27,9 +27,12 @@ int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); void cpufreq_times_create_policy(struct cpufreq_policy *policy); void cpufreq_times_record_transition(struct cpufreq_freqs *freq); +void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); #else static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( struct cpufreq_freqs *freq) {} +static inline void cpufreq_task_times_remove_uids(uid_t uid_start, + uid_t uid_end) {} #endif /* CONFIG_CPU_FREQ_TIMES */ #endif /* _LINUX_CPUFREQ_TIMES_H */ From b1e78e04f129c3113958ad6fa21e3b72de2761b3 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Mon, 16 Oct 2017 10:30:24 -0700 Subject: [PATCH 0635/1276] ANDROID: proc: Add /proc/uid directory Add support for reporting per-uid information through procfs, roughly following the approach used for per-tid and per-tgid directories in fs/proc/base.c. This also entails some new tracking of which uids have been used, to avoid losing information when the last task with a given uid exits. Signed-off-by: Connor O'Brien Bug: 72339335 Test: ls /proc/uid/; compare with UIDs in /proc/uid_time_in_state Change-Id: I0908f0c04438b11ceb673d860e58441bf503d478 [AmitP: Fix proc_fill_cache() now that upstream commit 0168b9e38c42 ("procfs: switch instantiate_t to d_splice_alias()"), switched instantiate() callback to d_splice_alias()] Signed-off-by: Amit Pundir --- fs/proc/Kconfig | 7 + fs/proc/Makefile | 1 + fs/proc/internal.h | 9 ++ fs/proc/root.c | 1 + fs/proc/uid.c | 290 ++++++++++++++++++++++++++++++++++++++++ include/linux/proc_fs.h | 6 + kernel/user.c | 3 + 7 files changed, 317 insertions(+) create mode 100644 fs/proc/uid.c diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 817c02b13b1d..4d96a7cc7ea8 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -97,3 +97,10 @@ config PROC_CHILDREN Say Y if you are running any user-space software which takes benefit from this interface. For example, rkt is such a piece of software. + +config PROC_UID + bool "Include /proc/uid/ files" + default y + depends on PROC_FS && RT_MUTEXES + help + Provides aggregated per-uid information under /proc/uid. diff --git a/fs/proc/Makefile b/fs/proc/Makefile index ead487e80510..3f849ca0edce 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -27,6 +27,7 @@ proc-y += softirqs.o proc-y += namespaces.o proc-y += self.o proc-y += thread_self.o +proc-$(CONFIG_PROC_UID) += uid.o proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o proc-$(CONFIG_NET) += proc_net.o proc-$(CONFIG_PROC_KCORE) += kcore.o diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 5185d7f6a51e..93b92902ee12 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -256,6 +256,15 @@ static inline void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head) { } #endif +/* + * uid.c + */ +#ifdef CONFIG_PROC_UID +extern int proc_uid_init(void); +#else +static inline void proc_uid_init(void) { } +#endif + /* * proc_tty.c */ diff --git a/fs/proc/root.c b/fs/proc/root.c index f4b1a9d2eca6..efc63a6d5a87 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -130,6 +130,7 @@ void __init proc_root_init(void) proc_symlink("mounts", NULL, "self/mounts"); proc_net_init(); + proc_uid_init(); proc_mkdir("fs", NULL); proc_mkdir("driver", NULL); proc_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */ diff --git a/fs/proc/uid.c b/fs/proc/uid.c new file mode 100644 index 000000000000..b3ef8a2f5ab2 --- /dev/null +++ b/fs/proc/uid.c @@ -0,0 +1,290 @@ +/* + * /proc/uid support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static struct proc_dir_entry *proc_uid; + +#define UID_HASH_BITS 10 + +static DECLARE_HASHTABLE(proc_uid_hash_table, UID_HASH_BITS); + +/* + * use rt_mutex here to avoid priority inversion between high-priority readers + * of these files and tasks calling proc_register_uid(). + */ +static DEFINE_RT_MUTEX(proc_uid_lock); /* proc_uid_hash_table */ + +struct uid_hash_entry { + uid_t uid; + struct hlist_node hash; +}; + +/* Caller must hold proc_uid_lock */ +static bool uid_hash_entry_exists_locked(uid_t uid) +{ + struct uid_hash_entry *entry; + + hash_for_each_possible(proc_uid_hash_table, entry, hash, uid) { + if (entry->uid == uid) + return true; + } + return false; +} + +void proc_register_uid(kuid_t kuid) +{ + struct uid_hash_entry *entry; + bool exists; + uid_t uid = from_kuid_munged(current_user_ns(), kuid); + + rt_mutex_lock(&proc_uid_lock); + exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (exists) + return; + + entry = kzalloc(sizeof(struct uid_hash_entry), GFP_KERNEL); + if (!entry) + return; + entry->uid = uid; + + rt_mutex_lock(&proc_uid_lock); + if (uid_hash_entry_exists_locked(uid)) + kfree(entry); + else + hash_add(proc_uid_hash_table, &entry->hash, uid); + rt_mutex_unlock(&proc_uid_lock); +} + +struct uid_entry { + const char *name; + int len; + umode_t mode; + const struct inode_operations *iop; + const struct file_operations *fop; +}; + +#define NOD(NAME, MODE, IOP, FOP) { \ + .name = (NAME), \ + .len = sizeof(NAME) - 1, \ + .mode = MODE, \ + .iop = IOP, \ + .fop = FOP, \ +} + +static const struct uid_entry uid_base_stuff[] = {}; + +static const struct inode_operations proc_uid_def_inode_operations = { + .setattr = proc_setattr, +}; + +static struct inode *proc_uid_make_inode(struct super_block *sb, kuid_t kuid) +{ + struct inode *inode; + + inode = new_inode(sb); + if (!inode) + return NULL; + + inode->i_ino = get_next_ino(); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + inode->i_op = &proc_uid_def_inode_operations; + inode->i_uid = kuid; + + return inode; +} + +static struct dentry *proc_uident_instantiate(struct dentry *dentry, + struct task_struct *unused, const void *ptr) +{ + const struct uid_entry *u = ptr; + struct inode *inode; + + uid_t uid = name_to_int(&dentry->d_name); + kuid_t kuid; + bool uid_exists; + rt_mutex_lock(&proc_uid_lock); + uid_exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (uid_exists) { + kuid = make_kuid(current_user_ns(), uid); + inode = proc_uid_make_inode(dentry->d_sb, kuid); + if (!inode) + return ERR_PTR(-ENOENT); + } else { + return ERR_PTR(-ENOENT); + } + + inode->i_mode = u->mode; + if (S_ISDIR(inode->i_mode)) + set_nlink(inode, 2); + if (u->iop) + inode->i_op = u->iop; + if (u->fop) + inode->i_fop = u->fop; + + return d_splice_alias(inode, dentry); +} + +static struct dentry *proc_uid_base_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + const struct uid_entry *u, *last; + unsigned int nents = ARRAY_SIZE(uid_base_stuff); + + if (nents == 0) + return ERR_PTR(-ENOENT); + + last = &uid_base_stuff[nents - 1]; + for (u = uid_base_stuff; u <= last; u++) { + if (u->len != dentry->d_name.len) + continue; + if (!memcmp(dentry->d_name.name, u->name, u->len)) + break; + } + if (u > last) + return ERR_PTR(-ENOENT); + + return proc_uident_instantiate(dentry, NULL, u); +} + +static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx) +{ + unsigned int nents = ARRAY_SIZE(uid_base_stuff); + const struct uid_entry *u; + + if (!dir_emit_dots(file, ctx)) + return 0; + + if (ctx->pos >= nents + 2) + return 0; + + for (u = uid_base_stuff + (ctx->pos - 2); + u <= uid_base_stuff + nents - 1; u++) { + if (!proc_fill_cache(file, ctx, u->name, u->len, + proc_uident_instantiate, NULL, u)) + break; + ctx->pos++; + } + + return 0; +} + +static const struct inode_operations proc_uid_base_inode_operations = { + .lookup = proc_uid_base_lookup, + .setattr = proc_setattr, +}; + +static const struct file_operations proc_uid_base_operations = { + .read = generic_read_dir, + .iterate = proc_uid_base_readdir, + .llseek = default_llseek, +}; + +static struct dentry *proc_uid_instantiate(struct dentry *dentry, + struct task_struct *unused, const void *ptr) +{ + unsigned int i, len; + nlink_t nlinks; + kuid_t *kuid = (kuid_t *)ptr; + struct inode *inode = proc_uid_make_inode(dentry->d_sb, *kuid); + + if (!inode) + return ERR_PTR(-ENOENT); + + inode->i_mode = S_IFDIR | 0555; + inode->i_op = &proc_uid_base_inode_operations; + inode->i_fop = &proc_uid_base_operations; + inode->i_flags |= S_IMMUTABLE; + + nlinks = 2; + len = ARRAY_SIZE(uid_base_stuff); + for (i = 0; i < len; ++i) { + if (S_ISDIR(uid_base_stuff[i].mode)) + ++nlinks; + } + set_nlink(inode, nlinks); + + return d_splice_alias(inode, dentry); +} + +static int proc_uid_readdir(struct file *file, struct dir_context *ctx) +{ + int last_shown, i; + unsigned long bkt; + struct uid_hash_entry *entry; + + if (!dir_emit_dots(file, ctx)) + return 0; + + i = 0; + last_shown = ctx->pos - 2; + rt_mutex_lock(&proc_uid_lock); + hash_for_each(proc_uid_hash_table, bkt, entry, hash) { + int len; + char buf[PROC_NUMBUF]; + + if (i < last_shown) + continue; + len = snprintf(buf, sizeof(buf), "%u", entry->uid); + if (!proc_fill_cache(file, ctx, buf, len, + proc_uid_instantiate, NULL, &entry->uid)) + break; + i++; + ctx->pos++; + } + rt_mutex_unlock(&proc_uid_lock); + return 0; +} + +static struct dentry *proc_uid_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + int result = -ENOENT; + + uid_t uid = name_to_int(&dentry->d_name); + bool uid_exists; + + rt_mutex_lock(&proc_uid_lock); + uid_exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (uid_exists) { + kuid_t kuid = make_kuid(current_user_ns(), uid); + + return proc_uid_instantiate(dentry, NULL, &kuid); + } + return ERR_PTR(result); +} + +static const struct file_operations proc_uid_operations = { + .read = generic_read_dir, + .iterate = proc_uid_readdir, + .llseek = default_llseek, +}; + +static const struct inode_operations proc_uid_inode_operations = { + .lookup = proc_uid_lookup, + .setattr = proc_setattr, +}; + +int __init proc_uid_init(void) +{ + proc_uid = proc_mkdir("uid", NULL); + if (!proc_uid) + return -ENOMEM; + proc_uid->proc_iops = &proc_uid_inode_operations; + proc_uid->proc_fops = &proc_uid_operations; + + return 0; +} diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index d0e1f1522a78..a50b1c981749 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -116,6 +116,12 @@ static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *p #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_PROC_UID +extern void proc_register_uid(kuid_t uid); +#else +static inline void proc_register_uid(kuid_t uid) {} +#endif + struct net; static inline struct proc_dir_entry *proc_net_mkdir( diff --git a/kernel/user.c b/kernel/user.c index 0df9b1640b2a..7f74a8a6fb30 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* @@ -208,6 +209,7 @@ struct user_struct *alloc_uid(kuid_t uid) } spin_unlock_irq(&uidhash_lock); } + proc_register_uid(uid); return up; @@ -229,6 +231,7 @@ static int __init uid_cache_init(void) spin_lock_irq(&uidhash_lock); uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID)); spin_unlock_irq(&uidhash_lock); + proc_register_uid(GLOBAL_ROOT_UID); return 0; } From f718697ec7ccde81244290d1eeb5852aafd7e337 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Mon, 22 Jan 2018 18:28:08 -0800 Subject: [PATCH 0636/1276] ANDROID: cpufreq: Add time_in_state to /proc/uid directories Add per-uid files that report the data in binary format rather than text, to allow faster reading & parsing by userspace. Signed-off-by: Connor O'Brien Bug: 72339335 Test: compare values to those reported in /proc/uid_time_in_state Change-Id: I463039ea7f17b842be4c70024fe772539fe2ce02 --- drivers/cpufreq/cpufreq_times.c | 49 +++++++++++++++++++++++++++++++++ fs/proc/uid.c | 16 ++++++++++- include/linux/cpufreq_times.h | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index 5e19ac5c8157..f560e10ba183 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -58,6 +58,19 @@ static struct cpu_freqs *all_freqs[NR_CPUS]; static unsigned int next_offset; + +/* Caller must hold rcu_read_lock() */ +static struct uid_entry *find_uid_entry_rcu(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + /* Caller must hold uid lock */ static struct uid_entry *find_uid_entry_locked(uid_t uid) { @@ -127,6 +140,36 @@ static bool freq_index_invalid(unsigned int index) return true; } +static int single_uid_time_in_state_show(struct seq_file *m, void *ptr) +{ + struct uid_entry *uid_entry; + unsigned int i; + u64 time; + uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private); + + if (uid == overflowuid) + return -EINVAL; + + rcu_read_lock(); + + uid_entry = find_uid_entry_rcu(uid); + if (!uid_entry) { + rcu_read_unlock(); + return 0; + } + + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + time = nsec_to_clock_t(uid_entry->time_in_state[i]); + seq_write(m, &time, sizeof(time)); + } + + rcu_read_unlock(); + + return 0; +} + static void *uid_seq_start(struct seq_file *seq, loff_t *pos) { if (*pos >= HASH_SIZE(uid_hash_table)) @@ -394,6 +437,12 @@ static int uid_time_in_state_open(struct inode *inode, struct file *file) return seq_open(file, &uid_time_in_state_seq_ops); } +int single_uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, single_uid_time_in_state_show, + &(inode->i_uid)); +} + static const struct file_operations uid_time_in_state_fops = { .open = uid_time_in_state_open, .read = seq_read, diff --git a/fs/proc/uid.c b/fs/proc/uid.c index b3ef8a2f5ab2..c3764b472be1 100644 --- a/fs/proc/uid.c +++ b/fs/proc/uid.c @@ -2,6 +2,7 @@ * /proc/uid support */ +#include #include #include #include @@ -82,7 +83,20 @@ struct uid_entry { .fop = FOP, \ } -static const struct uid_entry uid_base_stuff[] = {}; +#ifdef CONFIG_CPU_FREQ_TIMES +static const struct file_operations proc_uid_time_in_state_operations = { + .open = single_uid_time_in_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static const struct uid_entry uid_base_stuff[] = { +#ifdef CONFIG_CPU_FREQ_TIMES + NOD("time_in_state", 0444, NULL, &proc_uid_time_in_state_operations), +#endif +}; static const struct inode_operations proc_uid_def_inode_operations = { .setattr = proc_setattr, diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index b7ea7d343317..e84b576a20d5 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -28,6 +28,7 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); void cpufreq_times_create_policy(struct cpufreq_policy *policy); void cpufreq_times_record_transition(struct cpufreq_freqs *freq); void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); +int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( From 025178b1502ee7e161a0a0414cd8bb1fa436ba4c Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 5 Apr 2018 18:01:04 -0700 Subject: [PATCH 0637/1276] ANDROID: Add defconfig for cuttlefish. This file is based on x86_64_defconfig, merged with the base and recommended configs from configs.git, with the virtio drivers enabled and some spurious kernel features turned off. Change-Id: I61bde941e8cfef2dd83cb4ff040f7380922cc44e Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 444 +++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 arch/x86/configs/x86_64_cuttlefish_defconfig diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig new file mode 100644 index 000000000000..1b9020912aae --- /dev/null +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -0,0 +1,444 @@ +CONFIG_POSIX_MQUEUE=y +# CONFIG_FHANDLE is not set +# CONFIG_USELIB is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_LZ4 is not set +CONFIG_KALLSYMS_ALL=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_SMP=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_PARAVIRT_SPINLOCKS=y +CONFIG_MCORE2=y +CONFIG_PROCESSOR_SELECT=y +# CONFIG_CPU_SUP_CENTAUR is not set +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_TRANSPARENT_HUGEPAGE=y +# CONFIG_MTRR is not set +CONFIG_HZ_100=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_PHYSICAL_START=0x200000 +CONFIG_PHYSICAL_ALIGN=0x1000000 +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 reboot=p" +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_ACPI_PROCFS_POWER=y +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_THERMAL is not set +# CONFIG_X86_PM_TIMER is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_MSI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_IA32_EMULATION=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEBUG_DEVRES=y +CONFIG_OF=y +CONFIG_OF_UNITTEST=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +# CONFIG_ETHERNET is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_CISCO is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +CONFIG_MAC80211_HWSIM=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_EXAR is not set +CONFIG_SERIAL_8250_NR_UARTS=48 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_VIA is not set +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_HPET=y +# CONFIG_HPET_MMAP_DEFAULT is not set +# CONFIG_DEVPORT is not set +# CONFIG_ACPI_I2C_OPREGION is not set +# CONFIG_I2C_COMPAT is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_PTP_1588_CLOCK=y +# CONFIG_HWMON is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_SOFT_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +# CONFIG_VGA_ARB is not set +CONFIG_DRM=y +# CONFIG_DRM_FBDEV_EMULATION is not set +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +# CONFIG_HID_GENERIC is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_GADGET=y +CONFIG_USB_DUMMY_HCD=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_SW_SYNC=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_VSOC=y +CONFIG_ION=y +# CONFIG_X86_PLATFORM_DEVICES is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +# CONFIG_FIRMWARE_MEMMAP is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_SDCARD_FS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_ENABLE_DEFAULT_TRACERS=y +# CONFIG_KPROBE_EVENTS is not set +# CONFIG_UPROBE_EVENTS is not set +CONFIG_IO_DELAY_NONE=y +CONFIG_DEBUG_BOOT_PARAMS=y +CONFIG_OPTIMIZE_INLINING=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PATH=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_DEV_VIRTIO=y From 76f8e423db491b440690d7e40a9c2d98815ac94d Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Fri, 13 Apr 2018 15:36:37 -0700 Subject: [PATCH 0638/1276] ANDROID: Add build server config for cuttlefish. The build server config can be used with gcc or clang. Specify CC=clang to build with clang. Change-Id: Id346ab1489ecaaef8e9e66b084cc416dd0581f69 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 build.config.cuttlefish.x86_64 diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 new file mode 100644 index 000000000000..36c8a933f843 --- /dev/null +++ b/build.config.cuttlefish.x86_64 @@ -0,0 +1,15 @@ +ARCH=x86_64 +BRANCH=android-4.14 +CLANG_TRIPLE=x86_64-linux-gnu- +CROSS_COMPILE=x86_64-linux-androidkernel- +DEFCONFIG=x86_64_cuttlefish_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +POST_DEFCONFIG_CMDS="check_defconfig" +CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-4630689/bin +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" From c647c69ace4027f8d7d75db485a886222369a596 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 13 Nov 2017 08:19:17 -0800 Subject: [PATCH 0639/1276] FROMLIST: arm64: kvm: use -fno-jump-tables with clang Starting with LLVM r308050, clang generates a jump table with EL1 virtual addresses in __init_stage2_translation, which results in a kernel panic when booting at EL2: Kernel panic - not syncing: HYP panic: PS:800003c9 PC:ffff0000089e6fd8 ESR:86000004 FAR:ffff0000089e6fd8 HPFAR:0000000009825000 PAR:0000000000000000 VCPU:000804fc20001221 CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.14.0-rc7-dirty #3 Hardware name: ARM Juno development board (r1) (DT) Call trace: [] dump_backtrace+0x0/0x34c [] show_stack+0x18/0x20 [] dump_stack+0xc4/0xfc [] panic+0x138/0x2b4 [] panic+0x0/0x2b4 SMP: stopping secondary CPUs SMP: failed to stop secondary CPUs 0-3,5 Kernel Offset: disabled CPU features: 0x002086 Memory Limit: none ---[ end Kernel panic - not syncing: HYP panic: PS:800003c9 PC:ffff0000089e6fd8 ESR:86000004 FAR:ffff0000089e6fd8 HPFAR:0000000009825000 PAR:0000000000000000 VCPU:000804fc20001221 This change adds -fno-jump-tables to arm64/hyp to work around the bug. Bug: 62093296 Bug: 67506682 Change-Id: I1257be1febdcbfcc886fe6183c698b7a98d2a153 (am from https://patchwork.kernel.org/patch/10060301/) Suggested-by: AKASHI Takahiro Signed-off-by: Sami Tolvanen --- arch/arm64/kvm/hyp/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile index 2fabc2dc1966..78e8a37fba9a 100644 --- a/arch/arm64/kvm/hyp/Makefile +++ b/arch/arm64/kvm/hyp/Makefile @@ -6,6 +6,10 @@ ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \ $(DISABLE_STACKLEAK_PLUGIN) +ifeq ($(cc-name),clang) +ccflags-y += -fno-jump-tables +endif + KVM=../../../../virt/kvm obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v3-sr.o From ce12807d5b75aed0098b04b1509bcad044347e0f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:19:10 -0700 Subject: [PATCH 0640/1276] ANDROID: sdcardfs: Check for private data earlier When an sdcardfs dentry is destroyed, it may not yet have its fsdata initialized. It must be checked before we try to access the paths in its private data. Additionally, when cleaning up the superblock after a failure, we don't have our sb private data, so check for that case. Bug: 77923821 Change-Id: I89caf6e121ed86480b42024664453fe0031bbcf3 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 2 ++ fs/sdcardfs/lookup.c | 2 -- fs/sdcardfs/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index e9426a61d04a..166f14b2400b 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -131,6 +131,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) static void sdcardfs_d_release(struct dentry *dentry) { + if (!dentry || !dentry->d_fsdata) + return; /* release and reset the lower paths */ if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7dab5f76896b..2ff305161882 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -41,8 +41,6 @@ void sdcardfs_destroy_dentry_cache(void) void free_dentry_private_data(struct dentry *dentry) { - if (!dentry || !dentry->d_fsdata) - return; kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); dentry->d_fsdata = NULL; } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index e4fd3fbb05e6..c3120108f627 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -422,7 +422,7 @@ void sdcardfs_kill_sb(struct super_block *sb) { struct sdcardfs_sb_info *sbi; - if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { + if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); list_del(&sbi->list); From c080450304cdcf6e796593cdb60ae106a89ba99f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:24:51 -0700 Subject: [PATCH 0641/1276] ANDROID: sdcardfs: d_make_root calls iput d_make_root will call iput on failure, so we shouldn't try to do that ourselves. Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1abb4afb0f894ab917b7c6be8c833676f436beb7 --- fs/sdcardfs/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index c3120108f627..1a977493f88d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -316,7 +316,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto out_iput; + goto out_sput; } d_set_d_op(sb->s_root, &sdcardfs_ci_dops); @@ -361,8 +361,6 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); -out_iput: - iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 900e77796781e0002e19535ac7574553c3fa3a32 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 18:39:43 -0700 Subject: [PATCH 0642/1276] ANDROID: sdcardfs: Set s_root to NULL after putting Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1705bfd146009561d2d1da5f0e6a342ec6932a1c --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 1a977493f88d..30e0c431a1ea 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -361,6 +361,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); + sb->s_root = NULL; out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 6d59a0d3ecc1e8c8a0d2c45207abefb51f70c853 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 11 May 2017 15:03:36 -0700 Subject: [PATCH 0643/1276] ANDROID: add support for clang Control Flow Integrity (CFI) This change adds the CONFIG_CFI_CLANG option, CFI error handling, and a faster look-up table for cross module CFI checks. Bug: 67506682 Change-Id: Ic009f0a629b552a0eb16e6d89808c7029e91447d Signed-off-by: Sami Tolvanen [AmitP: Rebased to v4.19 without clang LTO support] Signed-off-by: Amit Pundir --- Makefile | 30 +++ arch/Kconfig | 28 +++ include/asm-generic/vmlinux.lds.h | 3 + include/linux/cfi.h | 38 ++++ include/linux/compiler-clang.h | 4 + include/linux/compiler_types.h | 4 + include/linux/init.h | 2 +- include/linux/module.h | 5 + init/Kconfig | 2 +- kernel/Makefile | 4 + kernel/cfi.c | 300 ++++++++++++++++++++++++++++++ kernel/module.c | 27 +++ net/Kconfig | 1 + 13 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 include/linux/cfi.h create mode 100644 kernel/cfi.c diff --git a/Makefile b/Makefile index e95542b02752..b2cc482ad03e 100644 --- a/Makefile +++ b/Makefile @@ -792,6 +792,30 @@ KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections LDFLAGS_vmlinux += --gc-sections endif +ifdef CONFIG_CFI_CLANG +cfi-clang-flags += -fsanitize=cfi +DISABLE_CFI_CLANG := -fno-sanitize=cfi +ifdef CONFIG_MODULES +cfi-clang-flags += -fsanitize-cfi-cross-dso +DISABLE_CFI_CLANG += -fno-sanitize-cfi-cross-dso +endif +ifdef CONFIG_CFI_PERMISSIVE +cfi-clang-flags += -fsanitize-recover=cfi -fno-sanitize-trap=cfi +endif + +# allow disabling only clang CFI where needed +export DISABLE_CFI_CLANG +endif + +ifdef CONFIG_CFI +# cfi-flags are re-tested in prepare-compiler-check +cfi-flags := $(cfi-clang-flags) +KBUILD_CFLAGS += $(cfi-flags) + +DISABLE_CFI := $(DISABLE_CFI_CLANG) +export DISABLE_CFI +endif + # arch Makefile may override CC so keep this after arch Makefile is included NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) @@ -1118,6 +1142,12 @@ uapi-asm-generic: PHONY += prepare-objtool prepare-objtool: $(objtool_target) +ifdef cfi-flags + ifeq ($(call cc-option, $(cfi-flags)),) + @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1 + endif +endif + # Generate some files # --------------------------------------------------------------------------- diff --git a/arch/Kconfig b/arch/Kconfig index 6801123932a5..9379d03bd5a4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -474,6 +474,34 @@ config STACKPROTECTOR_STRONG about 20% of all kernel functions, which increases the kernel code size by about 2%. +config CFI + bool + +config CFI_PERMISSIVE + bool "Use CFI in permissive mode" + depends on CFI + help + When selected, Control Flow Integrity (CFI) violations result in a + warning instead of a kernel panic. This option is useful for finding + CFI violations in drivers during development. + +config CFI_CLANG + bool "Use clang Control Flow Integrity (CFI) (EXPERIMENTAL)" + depends on CC_IS_CLANG + depends on KALLSYMS + select CFI + help + This option enables clang Control Flow Integrity (CFI), which adds + runtime checking for indirect function calls. + +config CFI_CLANG_SHADOW + bool "Use CFI shadow to speed up cross-module checks" + default y + depends on CFI_CLANG + help + If you select this option, the kernel builds a fast look-up table of + CFI check functions in loaded modules to reduce overhead. + config HAVE_ARCH_WITHIN_STACK_FRAMES bool help diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index d7701d466b60..ebf2eead399d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -68,6 +68,7 @@ */ #ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION #define TEXT_MAIN .text .text.[0-9a-zA-Z_]* +#define TEXT_CFI_MAIN .text.cfi .text.[0-9a-zA-Z_]*.cfi #define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data..LPBX* #define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]* #define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* @@ -75,6 +76,7 @@ #define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]* #else #define TEXT_MAIN .text +#define TEXT_CFI_MAIN .text.cfi #define DATA_MAIN .data #define SDATA_MAIN .sdata #define RODATA_MAIN .rodata @@ -492,6 +494,7 @@ ALIGN_FUNCTION(); \ *(.text.hot TEXT_MAIN .text.fixup .text.unlikely) \ *(.text..refcount) \ + *(TEXT_CFI_MAIN) \ *(.ref.text) \ MEM_KEEP(init.text*) \ MEM_KEEP(exit.text*) \ diff --git a/include/linux/cfi.h b/include/linux/cfi.h new file mode 100644 index 000000000000..e27033d5dd53 --- /dev/null +++ b/include/linux/cfi.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_CFI_H +#define _LINUX_CFI_H + +#include + +#ifdef CONFIG_CFI_CLANG +#ifdef CONFIG_MODULES + +typedef void (*cfi_check_fn)(uint64_t, void *, void *); + +/* Compiler-generated function in each module, and the kernel */ +#define CFI_CHECK_FN __cfi_check +#define CFI_CHECK_FN_NAME __stringify(CFI_CHECK_FN) + +extern void CFI_CHECK_FN(uint64_t, void *, void *); + +#ifdef CONFIG_CFI_CLANG_SHADOW +extern void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr); + +extern void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr); +#else +static inline void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ +} + +static inline void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ +} +#endif /* CONFIG_CFI_CLANG_SHADOW */ + +#endif /* CONFIG_MODULES */ +#endif /* CONFIG_CFI_CLANG */ + +#endif /* _LINUX_CFI_H */ diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index b1ce500fe8b3..426c9e9093a7 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -44,3 +44,7 @@ #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) #define __assume_aligned(a, ...) \ __attribute__((__assume_aligned__(a, ## __VA_ARGS__))) + +#ifdef CONFIG_CFI_CLANG +#define __nocfi __attribute__((no_sanitize("cfi"))) +#endif diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index db192becfec4..ecb53be5844d 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -144,6 +144,10 @@ struct ftrace_likely_data { #define __visible #endif +#ifndef __nocfi +#define __nocfi +#endif + /* * Assume alignment of return value. */ diff --git a/include/linux/init.h b/include/linux/init.h index 2538d176dd1f..9ef90a312a6f 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -47,7 +47,7 @@ /* These are for everybody (although not all archs will actually discard it in modules) */ -#define __init __section(.init.text) __cold __latent_entropy __noinitretpoline +#define __init __section(.init.text) __cold __latent_entropy __noinitretpoline __nocfi #define __initdata __section(.init.data) #define __initconst __section(.init.rodata) #define __exitdata __section(.exit.data) diff --git a/include/linux/module.h b/include/linux/module.h index e19ae08c7fb8..de6a79dbbe2e 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -348,6 +349,10 @@ struct module { const s32 *crcs; unsigned int num_syms; +#ifdef CONFIG_CFI_CLANG + cfi_check_fn cfi_check; +#endif + /* Kernel parameters. */ #ifdef CONFIG_SYSFS struct mutex param_lock; diff --git a/init/Kconfig b/init/Kconfig index 1e234e2f1cba..01cad95346df 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1972,7 +1972,7 @@ endif # MODULES config MODULES_TREE_LOOKUP def_bool y - depends on PERF_EVENTS || TRACING + depends on PERF_EVENTS || TRACING || CFI_CLANG config INIT_ALL_POSSIBLE bool diff --git a/kernel/Makefile b/kernel/Makefile index 7a63d567fdb5..7d146967402a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -34,6 +34,9 @@ KASAN_SANITIZE_kcov.o := n # cond_syscall is currently not LTO compatible CFLAGS_sys_ni.o = $(DISABLE_LTO) +# Don't instrument error handlers +CFLAGS_cfi.o = $(DISABLE_CFI_CLANG) + obj-y += sched/ obj-y += locking/ obj-y += power/ @@ -103,6 +106,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/ obj-$(CONFIG_IRQ_WORK) += irq_work.o obj-$(CONFIG_CPU_PM) += cpu_pm.o obj-$(CONFIG_BPF) += bpf/ +obj-$(CONFIG_CFI_CLANG) += cfi.o obj-$(CONFIG_PERF_EVENTS) += events/ diff --git a/kernel/cfi.c b/kernel/cfi.c new file mode 100644 index 000000000000..7c403dc5091c --- /dev/null +++ b/kernel/cfi.c @@ -0,0 +1,300 @@ +/* + * CFI (Control Flow Integrity) error and slowpath handling + * + * Copyright (C) 2017 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Compiler-defined handler names */ +#ifdef CONFIG_CFI_PERMISSIVE +#define cfi_failure_handler __ubsan_handle_cfi_check_fail +#define cfi_slowpath_handler __cfi_slowpath_diag +#else /* enforcing */ +#define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort +#define cfi_slowpath_handler __cfi_slowpath +#endif /* CONFIG_CFI_PERMISSIVE */ + +static inline void handle_cfi_failure() +{ +#ifdef CONFIG_CFI_PERMISSIVE + WARN_RATELIMIT(1, "CFI failure:\n"); +#else + pr_err("CFI failure:\n"); + BUG(); +#endif +} + +#ifdef CONFIG_MODULES +#ifdef CONFIG_CFI_CLANG_SHADOW +struct shadow_range { + /* Module address range */ + unsigned long mod_min_addr; + unsigned long mod_max_addr; + /* Module page range */ + unsigned long min_page; + unsigned long max_page; +}; + +#define SHADOW_ORDER 1 +#define SHADOW_PAGES (1 << SHADOW_ORDER) +#define SHADOW_SIZE \ + ((SHADOW_PAGES * PAGE_SIZE - sizeof(struct shadow_range)) / sizeof(u16)) +#define SHADOW_INVALID 0xFFFF + +struct cfi_shadow { + /* Page range covered by the shadow */ + struct shadow_range r; + /* Page offsets to __cfi_check functions in modules */ + u16 shadow[SHADOW_SIZE]; +}; + +static DEFINE_SPINLOCK(shadow_update_lock); +static struct cfi_shadow __rcu *cfi_shadow __read_mostly = NULL; + +static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr) +{ + unsigned long index; + unsigned long page = ptr >> PAGE_SHIFT; + + if (unlikely(page < s->r.min_page)) + return -1; /* Outside of module area */ + + index = page - s->r.min_page; + + if (index >= SHADOW_SIZE) + return -1; /* Cannot be addressed with shadow */ + + return (int)index; +} + +static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s, + int index) +{ + BUG_ON(index < 0 || index >= SHADOW_SIZE); + + if (unlikely(s->shadow[index] == SHADOW_INVALID)) + return 0; + + return (s->r.min_page + s->shadow[index]) << PAGE_SHIFT; +} + +static void prepare_next_shadow(const struct cfi_shadow __rcu *prev, + struct cfi_shadow *next) +{ + int i, index, check; + + /* Mark everything invalid */ + memset(next->shadow, 0xFF, sizeof(next->shadow)); + + if (!prev) + return; /* No previous shadow */ + + /* If the base address didn't change, update is not needed */ + if (prev->r.min_page == next->r.min_page) { + memcpy(next->shadow, prev->shadow, sizeof(next->shadow)); + return; + } + + /* Convert the previous shadow to the new address range */ + for (i = 0; i < SHADOW_SIZE; ++i) { + if (prev->shadow[i] == SHADOW_INVALID) + continue; + + index = ptr_to_shadow(next, shadow_to_ptr(prev, i)); + if (index < 0) + continue; + + check = ptr_to_shadow(next, + shadow_to_ptr(prev, prev->shadow[i])); + if (check < 0) + continue; + + next->shadow[index] = (u16)check; + } +} + +static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod) +{ + unsigned long ptr; + unsigned long min_page_addr; + unsigned long max_page_addr; + unsigned long check = (unsigned long)mod->cfi_check; + int check_index = ptr_to_shadow(s, check); + + BUG_ON((check & PAGE_MASK) != check); /* Must be page aligned */ + + if (check_index < 0) + return; /* Module not addressable with shadow */ + + min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; + max_page_addr = (unsigned long)mod->core_layout.base + + mod->core_layout.text_size; + max_page_addr &= PAGE_MASK; + + /* For each page, store the check function index in the shadow */ + for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { + int index = ptr_to_shadow(s, ptr); + if (index >= 0) { + /* Assume a page only contains code for one module */ + BUG_ON(s->shadow[index] != SHADOW_INVALID); + s->shadow[index] = (u16)check_index; + } + } +} + +static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod) +{ + unsigned long ptr; + unsigned long min_page_addr; + unsigned long max_page_addr; + + min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; + max_page_addr = (unsigned long)mod->core_layout.base + + mod->core_layout.text_size; + max_page_addr &= PAGE_MASK; + + for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { + int index = ptr_to_shadow(s, ptr); + if (index >= 0) + s->shadow[index] = SHADOW_INVALID; + } +} + +typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *); + +static void update_shadow(struct module *mod, unsigned long min_addr, + unsigned long max_addr, update_shadow_fn fn) +{ + struct cfi_shadow *prev; + struct cfi_shadow *next = (struct cfi_shadow *) + __get_free_pages(GFP_KERNEL, SHADOW_ORDER); + + BUG_ON(!next); + + next->r.mod_min_addr = min_addr; + next->r.mod_max_addr = max_addr; + next->r.min_page = min_addr >> PAGE_SHIFT; + next->r.max_page = max_addr >> PAGE_SHIFT; + + spin_lock(&shadow_update_lock); + prev = rcu_dereference_protected(cfi_shadow, 1); + prepare_next_shadow(prev, next); + + fn(next, mod); + set_memory_ro((unsigned long)next, SHADOW_PAGES); + rcu_assign_pointer(cfi_shadow, next); + + spin_unlock(&shadow_update_lock); + synchronize_rcu(); + + if (prev) { + set_memory_rw((unsigned long)prev, SHADOW_PAGES); + free_pages((unsigned long)prev, SHADOW_ORDER); + } +} + +void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ + update_shadow(mod, min_addr, max_addr, add_module_to_shadow); +} +EXPORT_SYMBOL(cfi_module_add); + +void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ + update_shadow(mod, min_addr, max_addr, remove_module_from_shadow); +} +EXPORT_SYMBOL(cfi_module_remove); + +static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s, + unsigned long ptr) +{ + int index; + unsigned long check; + + if (unlikely(!s)) + return NULL; /* No shadow available */ + + if (ptr < s->r.mod_min_addr || ptr > s->r.mod_max_addr) + return NULL; /* Not in a mapped module */ + + index = ptr_to_shadow(s, ptr); + if (index < 0) + return NULL; /* Cannot be addressed with shadow */ + + return (cfi_check_fn)shadow_to_ptr(s, index); +} +#endif /* CONFIG_CFI_CLANG_SHADOW */ + +static inline cfi_check_fn find_module_cfi_check(void *ptr) +{ + struct module *mod; + + preempt_disable(); + mod = __module_address((unsigned long)ptr); + preempt_enable(); + + if (mod) + return mod->cfi_check; + + return CFI_CHECK_FN; +} + +static inline cfi_check_fn find_cfi_check(void *ptr) +{ +#ifdef CONFIG_CFI_CLANG_SHADOW + cfi_check_fn f; + + if (!rcu_access_pointer(cfi_shadow)) + return CFI_CHECK_FN; /* No loaded modules */ + + /* Look up the __cfi_check function to use */ + rcu_read_lock(); + f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr); + rcu_read_unlock(); + + if (f) + return f; + + /* + * Fall back to find_module_cfi_check, which works also for a larger + * module address space, but is slower. + */ +#endif /* CONFIG_CFI_CLANG_SHADOW */ + + return find_module_cfi_check(ptr); +} + +void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) +{ + cfi_check_fn check = find_cfi_check(ptr); + + if (likely(check)) + check(id, ptr, diag); + else /* Don't allow unchecked modules */ + handle_cfi_failure(); +} +EXPORT_SYMBOL(cfi_slowpath_handler); +#endif /* CONFIG_MODULES */ + +void cfi_failure_handler(void *data, void *value, void *vtable) +{ + handle_cfi_failure(); +} +EXPORT_SYMBOL(cfi_failure_handler); + +void __cfi_check_fail(void *data, void *value) +{ + handle_cfi_failure(); +} diff --git a/kernel/module.c b/kernel/module.c index 6746c85511fe..f896873975a5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2123,6 +2123,8 @@ void __weak module_arch_freeing_init(struct module *mod) { } +static void cfi_cleanup(struct module *mod); + /* Free a module, remove from lists, etc. */ static void free_module(struct module *mod) { @@ -2164,6 +2166,10 @@ static void free_module(struct module *mod) /* This may be empty, but that's OK */ disable_ro_nx(&mod->init_layout); + + /* Clean up CFI for the module. */ + cfi_cleanup(mod); + module_arch_freeing_init(mod); module_memfree(mod->init_layout.base); kfree(mod->args); @@ -3351,6 +3357,8 @@ int __weak module_finalize(const Elf_Ehdr *hdr, return 0; } +static void cfi_init(struct module *mod); + static int post_relocation(struct module *mod, const struct load_info *info) { /* Sort exception table now relocations are done. */ @@ -3363,6 +3371,9 @@ static int post_relocation(struct module *mod, const struct load_info *info) /* Setup kallsyms-specific fields. */ add_kallsyms(mod, info); + /* Setup CFI for the module. */ + cfi_init(mod); + /* Arch-specific module finalizing. */ return module_finalize(info->hdr, info->sechdrs, mod); } @@ -4132,6 +4143,22 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, } #endif /* CONFIG_KALLSYMS */ +static void cfi_init(struct module *mod) +{ +#ifdef CONFIG_CFI_CLANG + mod->cfi_check = + (cfi_check_fn)mod_find_symname(mod, CFI_CHECK_FN_NAME); + cfi_module_add(mod, module_addr_min, module_addr_max); +#endif +} + +static void cfi_cleanup(struct module *mod) +{ +#ifdef CONFIG_CFI_CLANG + cfi_module_remove(mod, module_addr_min, module_addr_max); +#endif +} + /* Maximum number of characters written by module_flags() */ #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) diff --git a/net/Kconfig b/net/Kconfig index 4d84cee12ab7..56bf7db443a2 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -293,6 +293,7 @@ config BPF_JIT bool "enable BPF Just In Time compiler" depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT depends on MODULES + depends on !CFI ---help--- Berkeley Packet Filter filtering capabilities are normally handled by an interpreter. This option allows kernel to generate a native From 72f24ee60f379f8ae2c9566170d526f6ddff3b4d Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 14 Feb 2018 10:26:35 -0800 Subject: [PATCH 0644/1276] ANDROID: kallsyms: strip the .cfi postfix from symbols with CONFIG_CFI_CLANG With CFI enabled, LLVM appends .cfi to most function names, which potentially breaks user space tools. While stripping the postfix is not optimal either, this should at least create less confusion. Bug: 67506682 Bug: 73328469 Change-Id: I253f34a562629032ddd792b8498e171109ea7cbc Signed-off-by: Sami Tolvanen --- kernel/kallsyms.c | 49 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 02a0b01380d8..672ed40e60c2 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -268,6 +268,24 @@ int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize, !!__bpf_address_lookup(addr, symbolsize, offset, namebuf); } +#ifdef CONFIG_CFI_CLANG +/* + * LLVM appends .cfi to function names when CONFIG_CFI_CLANG is enabled, + * which causes confusion and potentially breaks user space tools, so we + * will strip the postfix from expanded symbol names. + */ +static inline void cleanup_symbol_name(char *s) +{ + char *res; + + res = strrchr(s, '.'); + if (res && !strcmp(res, ".cfi")) + *res = '\0'; +} +#else +static inline void cleanup_symbol_name(char *s) {} +#endif + /* * Lookup an address * - modname is set to NULL if it's in the kernel. @@ -294,7 +312,9 @@ const char *kallsyms_lookup(unsigned long addr, namebuf, KSYM_NAME_LEN); if (modname) *modname = NULL; - return namebuf; + + ret = namebuf; + goto found; } /* See if it's in a module or a BPF JITed image. */ @@ -307,11 +327,16 @@ const char *kallsyms_lookup(unsigned long addr, if (!ret) ret = ftrace_mod_address_lookup(addr, symbolsize, offset, modname, namebuf); + +found: + cleanup_symbol_name(namebuf); return ret; } int lookup_symbol_name(unsigned long addr, char *symname) { + int res; + symname[0] = '\0'; symname[KSYM_NAME_LEN - 1] = '\0'; @@ -322,15 +347,23 @@ int lookup_symbol_name(unsigned long addr, char *symname) /* Grab name */ kallsyms_expand_symbol(get_symbol_offset(pos), symname, KSYM_NAME_LEN); - return 0; + goto found; } /* See if it's in a module. */ - return lookup_module_symbol_name(addr, symname); + res = lookup_module_symbol_name(addr, symname); + if (res) + return res; + +found: + cleanup_symbol_name(symname); + return 0; } int lookup_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name) { + int res; + name[0] = '\0'; name[KSYM_NAME_LEN - 1] = '\0'; @@ -342,10 +375,16 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size, kallsyms_expand_symbol(get_symbol_offset(pos), name, KSYM_NAME_LEN); modname[0] = '\0'; - return 0; + goto found; } /* See if it's in a module. */ - return lookup_module_symbol_attrs(addr, size, offset, modname, name); + res = lookup_module_symbol_attrs(addr, size, offset, modname, name); + if (res) + return res; + +found: + cleanup_symbol_name(name); + return 0; } /* Look up a kernel symbol and return it in a text buffer. */ From 502a6c490314ca3dc9c29da84cc49fe7769d63a8 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 18 Aug 2017 14:31:23 -0700 Subject: [PATCH 0645/1276] ANDROID: arm64: disable CFI for cpu_replace_ttbr1 Disable CFI to allow an indirect call to a physical address. Bug: 67506682 Change-Id: I0ec38f34245a4ad52f508f6989093526d3bf442f Signed-off-by: Sami Tolvanen --- arch/arm64/include/asm/mmu_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 39ec0b8a689e..1b7aa97b9c9a 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -141,7 +141,7 @@ static inline void cpu_install_idmap(void) * Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, * avoiding the possibility of conflicting TLB entries being allocated. */ -static inline void cpu_replace_ttbr1(pgd_t *pgdp) +static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgdp) { typedef void (ttbr_replace_func)(phys_addr_t); extern ttbr_replace_func idmap_cpu_replace_ttbr1; From be4c5d88bec379db4605600317c597b08ede6cf1 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 9 Apr 2018 13:48:49 -0700 Subject: [PATCH 0646/1276] ANDROID: arm64: mark kpti_install_ng_mappings as __nocfi 4.9.93 panics on boot when CFI_CLANG and UNMAP_KERNEL_AT_EL0 are both enabled. From Sami Tolvanen: "kpti_install_ng_mappings makes an indirect call to a physical address, which trips CFI. Adding the __nocfi attribute to this function should fix the problem." Bug: 77811249 Change-Id: I87d1ceb29f1ba2caee8954547596f4236bdfc31f Reported-by: Jean-Baptiste Theou Signed-off-by: Greg Hackmann --- arch/arm64/kernel/cpufeature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index e238b7932096..703ebe0de2f8 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -902,7 +902,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, return !has_cpuid_feature(entry, scope); } -static void +static void __nocfi kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused) { typedef void (kpti_remap_fn)(int, int, phys_addr_t); From 6d3f45c0b9b40fbcf07c0c93a53425fcaa611e18 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 23 Apr 2018 12:52:07 -0700 Subject: [PATCH 0647/1276] ANDROID: arm64: kvm: disable CFI Disable CFI for code that runs at EL2 because __cfi_check only understands EL1 addresses. Bug: 67506682 Change-Id: Ia582943be0b31669d88464fd99228a5368b1aa6a Signed-off-by: Sami Tolvanen --- arch/arm64/kvm/hyp/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile index 78e8a37fba9a..d9028bba9d46 100644 --- a/arch/arm64/kvm/hyp/Makefile +++ b/arch/arm64/kvm/hyp/Makefile @@ -4,7 +4,7 @@ # ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_STACKLEAK_PLUGIN) $(DISABLE_CFI) ifeq ($(cc-name),clang) ccflags-y += -fno-jump-tables From 4225228c071b21f9aa4c55a1884f92201a7abf94 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 10 Aug 2017 09:39:53 -0700 Subject: [PATCH 0648/1276] ANDROID: arch/arm64/crypto: fix CFI in SHA CE Add C wrappers to allow indirect calls to sha[12]_ce_transform without tripping CFI. Bug: 67506682 Change-Id: If872f30095994206bc768eee13670be552b2a247 Signed-off-by: Sami Tolvanen --- arch/arm64/crypto/sha1-ce-glue.c | 8 ++++++++ arch/arm64/crypto/sha2-ce-glue.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c index 17fac2889f56..c3d572af201c 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -29,6 +29,14 @@ struct sha1_ce_state { asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha1_ce_transform(struct sha1_state *sst, + u8 const *src, int blocks) +{ + sha1_ce_transform((struct sha1_ce_state *)sst, src, blocks); +} +#define sha1_ce_transform __cfi_sha1_ce_transform +#endif const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count); const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize); diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c index 261f5195cab7..db37282ca060 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -29,6 +29,14 @@ struct sha256_ce_state { asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha2_ce_transform(struct sha256_state *sst, + u8 const *src, int blocks) +{ + sha2_ce_transform((struct sha256_ce_state *)sst, src, blocks); +} +#define sha2_ce_transform __cfi_sha2_ce_transform +#endif const u32 sha256_ce_offsetof_count = offsetof(struct sha256_ce_state, sst.count); From 82429eae50d674a2348ec5fb635dc1e65ac35115 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 25 Apr 2018 16:08:11 -0700 Subject: [PATCH 0649/1276] ANDROID: mm: fix filler function type mismatch Bug: 67506682 Change-Id: I6f615164ccd86b407540ada9bbcb39d910395db9 Signed-off-by: Sami Tolvanen --- include/linux/pagemap.h | 4 ++-- mm/filemap.c | 6 +++--- mm/readahead.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index b1bd2186e6d2..f24b02098061 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -239,7 +239,7 @@ static inline gfp_t readahead_gfp_mask(struct address_space *x) return mapping_gfp_mask(x) | __GFP_NORETRY | __GFP_NOWARN; } -typedef int filler_t(void *, struct page *); +typedef int filler_t(struct file *, struct page *); pgoff_t page_cache_next_hole(struct address_space *mapping, pgoff_t index, unsigned long max_scan); @@ -398,7 +398,7 @@ extern int read_cache_pages(struct address_space *mapping, static inline struct page *read_mapping_page(struct address_space *mapping, pgoff_t index, void *data) { - filler_t *filler = (filler_t *)mapping->a_ops->readpage; + filler_t *filler = mapping->a_ops->readpage; return read_cache_page(mapping, index, filler, data); } diff --git a/mm/filemap.c b/mm/filemap.c index 52517f28e6f4..59ebf349a988 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2780,7 +2780,7 @@ static struct page *wait_on_page_read(struct page *page) static struct page *do_read_cache_page(struct address_space *mapping, pgoff_t index, - int (*filler)(void *, struct page *), + int (*filler)(struct file *, struct page *), void *data, gfp_t gfp) { @@ -2887,7 +2887,7 @@ static struct page *do_read_cache_page(struct address_space *mapping, */ struct page *read_cache_page(struct address_space *mapping, pgoff_t index, - int (*filler)(void *, struct page *), + int (*filler)(struct file *, struct page *), void *data) { return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping)); @@ -2909,7 +2909,7 @@ struct page *read_cache_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp) { - filler_t *filler = (filler_t *)mapping->a_ops->readpage; + filler_t *filler = mapping->a_ops->readpage; return do_read_cache_page(mapping, index, filler, NULL, gfp); } diff --git a/mm/readahead.c b/mm/readahead.c index 4e630143a0ba..4b47bef36dad 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -83,7 +83,7 @@ static void read_cache_pages_invalidate_pages(struct address_space *mapping, * Hides the details of the LRU cache etc from the filesystems. */ int read_cache_pages(struct address_space *mapping, struct list_head *pages, - int (*filler)(void *, struct page *), void *data) + int (*filler)(struct file *, struct page *), void *data) { struct page *page; int ret = 0; From 8d4644239703d735256e854a869417fa2d0e619b Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 25 Apr 2018 16:08:28 -0700 Subject: [PATCH 0650/1276] ANDROID: fs: fuse: fix filler function type mismatch Bug: 67506682 Change-Id: Iabe7cdcc90dd2ea62976860531b8cbfcd76bd64b Signed-off-by: Sami Tolvanen --- fs/fuse/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 32d0b883e74f..5e1177754944 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -838,9 +838,9 @@ struct fuse_fill_data { unsigned nr_pages; }; -static int fuse_readpages_fill(void *_data, struct page *page) +static int fuse_readpages_fill(struct file *_data, struct page *page) { - struct fuse_fill_data *data = _data; + struct fuse_fill_data *data = (struct fuse_fill_data *)_data; struct fuse_req *req = data->req; struct inode *inode = data->inode; struct fuse_conn *fc = get_fuse_conn(inode); From 9173db9fa4bb8dbf61d184d246cf1a780ab72d0d Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Tue, 13 Feb 2018 13:01:39 -0800 Subject: [PATCH 0651/1276] ANDROID: fs: nfs: fix filler function type Bug: 67506682 Change-Id: I04d4b1b9ab0720a4f342d6617dd132de8654b94c Signed-off-by: Sami Tolvanen --- fs/nfs/dir.c | 5 +++-- fs/nfs/read.c | 2 +- fs/nfs/symlink.c | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8bfaa658b2c1..e337a16236a3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -664,8 +664,9 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, * We only need to convert from xdr once so future lookups are much simpler */ static -int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) +int nfs_readdir_filler(struct file *file, struct page* page) { + nfs_readdir_descriptor_t *desc = (nfs_readdir_descriptor_t *)file; struct inode *inode = file_inode(desc->file); int ret; @@ -698,7 +699,7 @@ static struct page *get_cache_page(nfs_readdir_descriptor_t *desc) { return read_cache_page(desc->file->f_mapping, - desc->page_index, (filler_t *)nfs_readdir_filler, desc); + desc->page_index, nfs_readdir_filler, desc); } /* diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 48d7277c60a9..42dbf4f4d5aa 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -354,7 +354,7 @@ struct nfs_readdesc { }; static int -readpage_async_filler(void *data, struct page *page) +readpage_async_filler(struct file *data, struct page *page) { struct nfs_readdesc *desc = (struct nfs_readdesc *)data; struct nfs_page *new; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 06eb44b47885..220d5ba2bd9b 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -26,8 +26,9 @@ * and straight-forward than readdir caching. */ -static int nfs_symlink_filler(struct inode *inode, struct page *page) +static int nfs_symlink_filler(struct file *file, struct page *page) { + struct inode *inode = (struct inode *)file; int error; error = NFS_PROTO(inode)->readlink(inode, page, 0, PAGE_SIZE); @@ -66,7 +67,7 @@ static const char *nfs_get_link(struct dentry *dentry, if (err) return err; page = read_cache_page(&inode->i_data, 0, - (filler_t *)nfs_symlink_filler, inode); + nfs_symlink_filler, inode); if (IS_ERR(page)) return ERR_CAST(page); } From cac1bd75b353e9777ab017316849a667f01e9600 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:02:16 -0800 Subject: [PATCH 0652/1276] ANDROID: fs: afs: fix filler function type Bug: 67506682 Change-Id: I76d208c8606ee5af144891d14bd309912d4d788d Signed-off-by: Sami Tolvanen --- fs/afs/file.c | 14 ++++++++++---- fs/afs/internal.h | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/afs/file.c b/fs/afs/file.c index 7d4f26198573..b95ff424bd08 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -260,12 +260,11 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de /* * read page from file, directory or symlink, given a key to use */ -int afs_page_filler(void *data, struct page *page) +static int __afs_page_filler(struct key *key, struct page *page) { struct inode *inode = page->mapping->host; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_read *req; - struct key *key = data; int ret; _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index); @@ -372,6 +371,13 @@ int afs_page_filler(void *data, struct page *page) return ret; } +int afs_page_filler(struct file *data, struct page *page) +{ + struct key *key = (struct key *)data; + + return __afs_page_filler(key, page); +} + /* * read page from file, directory or symlink, given a file to nominate the key * to be used @@ -384,14 +390,14 @@ static int afs_readpage(struct file *file, struct page *page) if (file) { key = afs_file_key(file); ASSERT(key != NULL); - ret = afs_page_filler(key, page); + ret = __afs_page_filler(key, page); } else { struct inode *inode = page->mapping->host; key = afs_request_key(AFS_FS_S(inode->i_sb)->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); } else { - ret = afs_page_filler(key, page); + ret = __afs_page_filler(key, page); key_put(key); } } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 34c02fdcc25f..75d263c80703 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -752,7 +752,7 @@ extern void afs_put_wb_key(struct afs_wb_key *); extern int afs_open(struct inode *, struct file *); extern int afs_release(struct inode *, struct file *); extern int afs_fetch_data(struct afs_vnode *, struct key *, struct afs_read *); -extern int afs_page_filler(void *, struct page *); +extern int afs_page_filler(struct file *, struct page *); extern void afs_put_read(struct afs_read *); /* From 54eeda1c0a4d927c4b561d5e100407535fa0dbd8 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:04:53 -0800 Subject: [PATCH 0653/1276] ANDROID: fs: exofs: fix filler function type Bug: 67506682 Change-Id: I42f297bfe07a1b7916790415f35ad4f2574ceec7 Signed-off-by: Sami Tolvanen --- fs/exofs/inode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 5f81fcd383a4..aef3f4206540 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -377,9 +377,8 @@ static int read_exec(struct page_collect *pcol) * and will start a new collection. Eventually caller must submit the last * segment if present. */ -static int readpage_strip(void *data, struct page *page) +static int __readpage_strip(struct page_collect *pcol, struct page *page) { - struct page_collect *pcol = data; struct inode *inode = pcol->inode; struct exofs_i_info *oi = exofs_i(inode); loff_t i_size = i_size_read(inode); @@ -470,6 +469,13 @@ static int readpage_strip(void *data, struct page *page) return ret; } +static int readpage_strip(struct file *data, struct page *page) +{ + struct page_collect *pcol = (struct page_collect *)data; + + return __readpage_strip(pcol, page); +} + static int exofs_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { @@ -499,7 +505,7 @@ static int _readpage(struct page *page, bool read_4_write) _pcol_init(&pcol, 1, page->mapping->host); pcol.read_4_write = read_4_write; - ret = readpage_strip(&pcol, page); + ret = __readpage_strip(&pcol, page); if (ret) { EXOFS_ERR("_readpage => %d\n", ret); return ret; From dbda45653fa8a8c8b8430980c28f2e6aba960982 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:06:24 -0800 Subject: [PATCH 0654/1276] ANDROID: fs: gfs2: fix filler function type Bug: 67506682 Change-Id: I50a3f85965de6e041d0f40e7bf9c2ced15ccfd49 Signed-off-by: Sami Tolvanen --- fs/gfs2/aops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 31e8270d0b26..f3bdc4bdd5b1 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -504,7 +504,7 @@ int stuffed_readpage(struct gfs2_inode *ip, struct page *page) * called by gfs2_readpage() once the required lock has been granted. */ -static int __gfs2_readpage(void *file, struct page *page) +static int __gfs2_readpage(struct file *file, struct page *page) { struct gfs2_inode *ip = GFS2_I(page->mapping->host); struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); From 2a2bacbd4431124085581d3abde67ef5e60017c1 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 30 Apr 2018 12:23:14 -0700 Subject: [PATCH 0655/1276] ANDROID: cfi: print target address on failure Bug: 78862212 Bug: 67506682 Change-Id: Ifaa3e3f8fc5f19649f4857d185d50383b4a89055 Signed-off-by: Sami Tolvanen [AmitP: Added ANDROID prefix in commit header] Signed-off-by: Amit Pundir --- kernel/cfi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/cfi.c b/kernel/cfi.c index 7c403dc5091c..c32e6b358797 100644 --- a/kernel/cfi.c +++ b/kernel/cfi.c @@ -24,12 +24,12 @@ #define cfi_slowpath_handler __cfi_slowpath #endif /* CONFIG_CFI_PERMISSIVE */ -static inline void handle_cfi_failure() +static inline void handle_cfi_failure(void *ptr) { #ifdef CONFIG_CFI_PERMISSIVE - WARN_RATELIMIT(1, "CFI failure:\n"); + WARN_RATELIMIT(1, "CFI failure (target: [<%px>] %pF):\n", ptr, ptr); #else - pr_err("CFI failure:\n"); + pr_err("CFI failure (target: [<%px>] %pF):\n", ptr, ptr); BUG(); #endif } @@ -283,18 +283,18 @@ void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) if (likely(check)) check(id, ptr, diag); else /* Don't allow unchecked modules */ - handle_cfi_failure(); + handle_cfi_failure(ptr); } EXPORT_SYMBOL(cfi_slowpath_handler); #endif /* CONFIG_MODULES */ -void cfi_failure_handler(void *data, void *value, void *vtable) +void cfi_failure_handler(void *data, void *ptr, void *vtable) { - handle_cfi_failure(); + handle_cfi_failure(ptr); } EXPORT_SYMBOL(cfi_failure_handler); -void __cfi_check_fail(void *data, void *value) +void __cfi_check_fail(void *data, void *ptr) { - handle_cfi_failure(); + handle_cfi_failure(ptr); } From ab11c7615f6e420a20cefefe884b13f09045ad1e Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 3 May 2018 22:16:10 -0700 Subject: [PATCH 0656/1276] ANDROID: build.config: enforce trace_printk check Bug: 79166848 Change-Id: I41d2fe57b377e305b4b68c30c98ee94643d142e4 Test: Build a kernel with trace_prink and see warning Signed-off-by: Wei Wang --- build.config.cuttlefish.x86_64 | 1 + build.config.goldfish.arm | 1 + build.config.goldfish.arm64 | 1 + build.config.goldfish.mips | 1 + build.config.goldfish.mips64 | 1 + build.config.goldfish.x86 | 1 + build.config.goldfish.x86_64 | 1 + 7 files changed, 7 insertions(+) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 36c8a933f843..83344f38d104 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -13,3 +13,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm index 866da9361b71..ff5646ab4f40 100644 --- a/build.config.goldfish.arm +++ b/build.config.goldfish.arm @@ -10,3 +10,4 @@ arch/arm/boot/zImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 index 9c963cf4a3d8..4c896a679ab9 100644 --- a/build.config.goldfish.arm64 +++ b/build.config.goldfish.arm64 @@ -10,3 +10,4 @@ arch/arm64/boot/Image vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips index 8af53d2c2940..9a14a444ac14 100644 --- a/build.config.goldfish.mips +++ b/build.config.goldfish.mips @@ -9,3 +9,4 @@ FILES=" vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 index 2a33d36dc4c8..6ad9759f5f4a 100644 --- a/build.config.goldfish.mips64 +++ b/build.config.goldfish.mips64 @@ -9,3 +9,4 @@ FILES=" vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 index f86253f58d4d..2266c621835e 100644 --- a/build.config.goldfish.x86 +++ b/build.config.goldfish.x86 @@ -10,3 +10,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 index e1738861ec5c..08c42c2eba03 100644 --- a/build.config.goldfish.x86_64 +++ b/build.config.goldfish.x86_64 @@ -10,3 +10,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 From 70a9396e383865fd5c55b67ba4e0cc9fa5508448 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 24 Oct 2015 16:20:18 -0700 Subject: [PATCH 0657/1276] ANDROID: goldfish: drop CONFIG_INPUT_KEYCHORD Remove keychord driver, replaced in user space by https://android-review.googlesource.com/c/677629. Signed-off-by: Mark Salyzyn Cc: Jin Qian Cc: Amit Pundir Bug: 64114943 Change-Id: I0b673a5c68dbe28afa033d2ca70e12daea144b2a --- arch/arm/configs/ranchu_defconfig | 1 - arch/arm64/configs/ranchu64_defconfig | 1 - arch/x86/configs/i386_ranchu_defconfig | 1 - arch/x86/configs/x86_64_cuttlefish_defconfig | 1 - arch/x86/configs/x86_64_ranchu_defconfig | 1 - 5 files changed, 5 deletions(-) diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig index 461a85a02764..69157c4c21fd 100644 --- a/arch/arm/configs/ranchu_defconfig +++ b/arch/arm/configs/ranchu_defconfig @@ -183,7 +183,6 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig index 51c3bfc8658c..3d2eb3275b1f 100644 --- a/arch/arm64/configs/ranchu64_defconfig +++ b/arch/arm64/configs/ranchu64_defconfig @@ -186,7 +186,6 @@ CONFIG_KEYBOARD_GOLDFISH_EVENTS=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TABLET=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig index 18d3675d28f0..4e9dc7d49cbe 100644 --- a/arch/x86/configs/i386_ranchu_defconfig +++ b/arch/x86/configs/i386_ranchu_defconfig @@ -251,7 +251,6 @@ CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO is not set diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 1b9020912aae..3de85deeb44b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -263,7 +263,6 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_I8042 is not set diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig index 7eff3002db18..81202e3f6ae8 100644 --- a/arch/x86/configs/x86_64_ranchu_defconfig +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -248,7 +248,6 @@ CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO is not set From 49092e89ffa43771f56fd22fa4230d2e6f311cb4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 24 Apr 2018 18:06:56 -0700 Subject: [PATCH 0658/1276] ANDROID: sdcardfs: Don't d_drop in d_revalidate After d_revalidate returns 0, the vfs will call d_invalidate, which will call d_drop itself, along with other cleanup. Bug: 78262592 Change-Id: Idbb30e008c05d62edf2217679cb6a5517d8d1a2c Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 166f14b2400b..776d549b397b 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -51,7 +51,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) * whether the base obbpath has been changed or not */ if (is_obbpath_invalid(dentry)) { - d_drop(dentry); return 0; } @@ -65,7 +64,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); if (err == 0) { - d_drop(dentry); goto out; } } @@ -73,14 +71,12 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); - d_drop(dentry); err = 0; goto out; } spin_unlock(&lower_dentry->d_lock); if (parent_lower_dentry != lower_cur_parent_dentry) { - d_drop(dentry); err = 0; goto out; } @@ -94,7 +90,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) } if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { - __d_drop(dentry); err = 0; } @@ -113,7 +108,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (inode) { data = top_data_get(SDCARDFS_I(inode)); if (!data || data->abandoned) { - d_drop(dentry); err = 0; } if (data) From 60b5786aac8b36ae86f70e61aea7c5385157190e Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 11:41:31 -0700 Subject: [PATCH 0659/1276] ANDROID: build: cuttlefish: Fix path to clang. Reconcile with changes made to the kernel manifest. Clang must come from master because it was not usable for kernel builds in older branches of the Android platform. Bug: 63889157 Change-Id: Id0a080fc2f1cba495f37f26afa48e43e736b756a Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 83344f38d104..5741a01ba5a2 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-4630689/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4630689/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From 5ebf86022fd68a8e82fddb11955e1bf68d2634ce Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 16:09:23 -0700 Subject: [PATCH 0660/1276] ANDROID: build: cuttlefish: Upgrade clang to newer version. Use the same clang version as hikey-linaro. Bug: 63889157 Change-Id: I6932d6149642d429086207e63aa8a8d5c2afd6f7 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 5741a01ba5a2..770f1b382e72 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4630689/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4679922/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From a7d8030a0683549d16b12d574c30222c8940e77a Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 18:01:41 -0700 Subject: [PATCH 0661/1276] ANDROID: build: cuttlefish: Upgrade clang to newer version. The last upgrade introduced a new build failure, because it had a bug which caused it to emit PLT relocations, certain types of which cannot be handled by the reloc tool in the kernel. See https://bugs.llvm.org/show_bug.cgi?id=36674 for more details. Bug: 63889157 Change-Id: I813febdbacb0579abcb12dc7f2164cce1e2f5a26 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 770f1b382e72..694ed56a5f47 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4679922/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r328903/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From 42f561a7e864e1abccdcf01c7718eb25d8da1ef0 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 16:35:04 -0700 Subject: [PATCH 0662/1276] ANDROID: x86_64_cuttlefish_defconfig: Disable ORC unwinder. Disable the ORC unwinder. This feature requires libelf-dev, which is breaking some automated build systems that do not have it installed. As we already enabled CONFIG_FRAME_POINTER, we already incurred the performance penalty of the legacy stack unwinder, so this is pretty much a no-op change. Bug: 63889157 Change-Id: Ic0704ebb726c97449ed873556262cc0db3e9a6cf Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 3de85deeb44b..90be23b26f21 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -432,6 +432,7 @@ CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_IO_DELAY_NONE=y CONFIG_DEBUG_BOOT_PARAMS=y CONFIG_OPTIMIZE_INLINING=y +CONFIG_UNWINDER_FRAME_POINTER=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y From 97b7790f505eeb4b8cece96c056f17e7e3c4363b Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Wed, 23 May 2018 13:00:23 -0700 Subject: [PATCH 0663/1276] ANDROID: proc: fix undefined behavior in proc_uid_base_readdir When uid_base_stuff has no entries, proc_uid_base_readdir tries to compute an address before the start of the array. Revise this check to use uid_base_stuff + nents instead, which makes the code valid regardless of array size. Bug: 80158484 Test: No more compiler warning with CONFIG_CPU_FREQ_TIMES=n Change-Id: I6e55b27c3ba8210cee194f6d27bbd62c0b263796 Signed-off-by: Connor O'Brien --- fs/proc/uid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/uid.c b/fs/proc/uid.c index c3764b472be1..311717ea199a 100644 --- a/fs/proc/uid.c +++ b/fs/proc/uid.c @@ -185,7 +185,7 @@ static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx) return 0; for (u = uid_base_stuff + (ctx->pos - 2); - u <= uid_base_stuff + nents - 1; u++) { + u < uid_base_stuff + nents; u++) { if (!proc_fill_cache(file, ctx, u->name, u->len, proc_uident_instantiate, NULL, u)) break; From 0aea97b7b93c34076cf3befbf09c790405f41ca3 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 31 May 2018 13:36:29 -0700 Subject: [PATCH 0664/1276] ANDROID: Update x86_64_cuttlefish_defconfig Merge with the configs from kernel/configs.git added recently. This should fix ipsec VPN functionality. Bug: 80540078 Change-Id: I9cc99f5e34d2809670fe2fc0df121610657f6769 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 90be23b26f21..1c6247dd06d9 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -32,6 +32,7 @@ CONFIG_OPROFILE=y CONFIG_KPROBES=y CONFIG_JUMP_LABEL=y CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_REFCOUNT_FULL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y @@ -89,8 +90,8 @@ CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y CONFIG_INET_ESP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set # CONFIG_INET_XFRM_MODE_BEET is not set CONFIG_INET_DIAG_DESTROY=y CONFIG_TCP_CONG_ADVANCED=y @@ -105,6 +106,7 @@ CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETLABEL=y CONFIG_NETFILTER=y @@ -131,6 +133,7 @@ CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -144,14 +147,17 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -169,6 +175,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_MATCH_IPV6HEADER=y CONFIG_IP6_NF_MATCH_RPFILTER=y @@ -263,6 +270,7 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_I8042 is not set @@ -303,7 +311,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_HIDRAW=y CONFIG_UHID=y -# CONFIG_HID_GENERIC is not set CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y CONFIG_HID_ACRUX_FF=y @@ -423,12 +430,9 @@ CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_HARDLOCKUP_DETECTOR=y CONFIG_PANIC_TIMEOUT=5 -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 CONFIG_ENABLE_DEFAULT_TRACERS=y -# CONFIG_KPROBE_EVENTS is not set -# CONFIG_UPROBE_EVENTS is not set CONFIG_IO_DELAY_NONE=y CONFIG_DEBUG_BOOT_PARAMS=y CONFIG_OPTIMIZE_INLINING=y @@ -441,4 +445,5 @@ CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y From 29c47d94603998e8d874bd6e8f279aae8721c8a1 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 31 May 2018 15:47:36 -0700 Subject: [PATCH 0665/1276] ANDROID: x86_64_cuttlefish_defconfig: Enable F2FS Bug: 80475502 Change-Id: I061467404f1d4b828ac1b7423db375a35934ce28 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 1c6247dd06d9..26a42b91140b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -398,6 +398,9 @@ CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_FS_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y # CONFIG_PRINT_QUOTA_WARNING is not set From c6e355e98901d4718b790cb78807d5f0400a6010 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 1 Sep 2011 15:26:50 -0400 Subject: [PATCH 0666/1276] ANDROID: add extra free kbytes tunable Add a userspace visible knob to tell the VM to keep an extra amount of memory free, by increasing the gap between each zone's min and low watermarks. This is useful for realtime applications that call system calls and have a bound on the number of allocations that happen in any short time period. In this application, extra_free_kbytes would be left at an amount equal to or larger than than the maximum number of allocations that happen in any burst. It may also be useful to reduce the memory use of virtual machines (temporarily?), in a way that does not cause memory fragmentation like ballooning does. [ccross] Revived for use on old kernels where no other solution exists. The tunable will be removed on kernels that do better at avoiding direct reclaim. [surenb] Will be reverted as soon as Android framework is reworked to use upstream-supported watermark_scale_factor instead of extra_free_kbytes. Bug: 86445363 Change-Id: I765a42be8e964bfd3e2886d1ca85a29d60c3bb3e Signed-off-by: Rik van Riel Signed-off-by: Colin Cross Signed-off-by: Suren Baghdasaryan --- Documentation/sysctl/vm.txt | 16 ++++++++++++++++ kernel/sysctl.c | 9 +++++++++ mm/page_alloc.c | 34 ++++++++++++++++++++++++++-------- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 7d73882e2c27..a48baf202265 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -31,6 +31,7 @@ Currently, these files are in /proc/sys/vm: - dirty_writeback_centisecs - drop_caches - extfrag_threshold +- extra_free_kbytes - hugetlb_shm_group - laptop_mode - legacy_va_layout @@ -274,6 +275,21 @@ any throttling. ============================================================== +extra_free_kbytes + +This parameter tells the VM to keep extra free memory between the threshold +where background reclaim (kswapd) kicks in, and the threshold where direct +reclaim (by allocating processes) kicks in. + +This is useful for workloads that require low latency memory allocations +and have a bounded burstiness in memory allocations, for example a +realtime application that receives and transmits network traffic +(causing in-kernel memory allocations) with a maximum total message burst +size of 200MB may need 200MB of extra free memory to avoid direct reclaim +related latencies. + +============================================================== + hugetlb_shm_group hugetlb_shm_group contains group id that is allowed to create SysV diff --git a/kernel/sysctl.c b/kernel/sysctl.c index cc02050fd0c4..8c7635ecb752 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -106,6 +106,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; #endif extern int pid_max; +extern int extra_free_kbytes; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; extern int latencytop_enabled; @@ -1459,6 +1460,14 @@ static struct ctl_table vm_table[] = { .extra1 = &one, .extra2 = &one_thousand, }, + { + .procname = "extra_free_kbytes", + .data = &extra_free_kbytes, + .maxlen = sizeof(extra_free_kbytes), + .mode = 0644, + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, { .procname = "percpu_pagelist_fraction", .data = &percpu_pagelist_fraction, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e2ef1c17942f..2c543599c489 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -261,10 +261,22 @@ compound_page_dtor * const compound_page_dtors[] = { #endif }; +/* + * Try to keep at least this much lowmem free. Do not allow normal + * allocations below this point, only high priority ones. Automatically + * tuned according to the amount of memory in the system. + */ int min_free_kbytes = 1024; int user_min_free_kbytes = -1; int watermark_scale_factor = 10; +/* + * Extra memory for the system to try freeing. Used to temporarily + * free memory, to make space for new workloads. Anyone can allocate + * down to the min watermarks controlled by min_free_kbytes above. + */ +int extra_free_kbytes = 0; + static unsigned long nr_kernel_pages __meminitdata; static unsigned long nr_all_pages __meminitdata; static unsigned long dma_reserve __meminitdata; @@ -7214,6 +7226,7 @@ static void setup_per_zone_lowmem_reserve(void) static void __setup_per_zone_wmarks(void) { unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; @@ -7225,11 +7238,14 @@ static void __setup_per_zone_wmarks(void) } for_each_zone(zone) { - u64 tmp; + u64 min, low; spin_lock_irqsave(&zone->lock, flags); - tmp = (u64)pages_min * zone->managed_pages; - do_div(tmp, lowmem_pages); + min = (u64)pages_min * zone->managed_pages; + do_div(min, lowmem_pages); + low = (u64)pages_low * zone->managed_pages; + do_div(low, vm_total_pages); + if (is_highmem(zone)) { /* * __GFP_HIGH and PF_MEMALLOC allocations usually don't @@ -7250,7 +7266,7 @@ static void __setup_per_zone_wmarks(void) * If it's a lowmem zone, reserve a number of pages * proportionate to the zone's size. */ - zone->watermark[WMARK_MIN] = tmp; + zone->watermark[WMARK_MIN] = min; } /* @@ -7258,12 +7274,14 @@ static void __setup_per_zone_wmarks(void) * scale factor in proportion to available memory, but * ensure a minimum size on small systems. */ - tmp = max_t(u64, tmp >> 2, + min = max_t(u64, min >> 2, mult_frac(zone->managed_pages, watermark_scale_factor, 10000)); - zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + tmp; - zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2; + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + + low + min; + zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + + low + min * 2; spin_unlock_irqrestore(&zone->lock, flags); } @@ -7346,7 +7364,7 @@ core_initcall(init_per_zone_wmark_min) /* * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so * that we can call two helper functions whenever min_free_kbytes - * changes. + * or extra_free_kbytes changes. */ int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) From 37fe857fba534b7e16ba6475f132d553d41f937c Mon Sep 17 00:00:00 2001 From: Patrik Torstensson Date: Fri, 13 Apr 2018 15:34:48 -0700 Subject: [PATCH 0667/1276] ANDROID: Add kconfig to make dm-verity check_at_most_once default enabled This change adds a kernel config for default enable the check_at_most_once dm-verity option. This is to give us the ability to enforce the usage of at_most_once for entry-level phones. Change-Id: Id40416672c4c2209a9866997d8c164b5de5dc7dc Signed-off-by: Patrik Torstensson Bug: 72664474 --- drivers/md/Kconfig | 20 ++++++++++++++++++++ drivers/md/dm-verity-target.c | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 44d56f41660f..8a258c688a07 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -591,4 +591,24 @@ config DM_ANDROID_VERITY of the metadata contents are verified against the key included in the system keyring. Upon success, the underlying verity target is setup. + +config DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED + bool "Verity will validate blocks at most once" + depends on DM_VERITY + ---help--- + Default enables at_most_once option for dm-verity + + Verify data blocks only the first time they are read from the + data device, rather than every time. This reduces the overhead + of dm-verity so that it can be used on systems that are memory + and/or CPU constrained. However, it provides a reduced level + of security because only offline tampering of the data device's + content will be detected, not online tampering. + + Hash blocks are still verified each time they are read from the + hash device, since verification of hash blocks is less performance + critical than data blocks, and a hash block will not be verified + any more after all the data blocks it covers have been verified anyway. + + If unsure, say N. endif # MD diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 427cabe675a8..1a9f464d15c7 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -1098,6 +1098,14 @@ int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) goto bad; } +#ifdef CONFIG_DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED + if (!v->validated_blocks) { + r = verity_alloc_most_once(v); + if (r) + goto bad; + } +#endif + v->hash_per_block_bits = __fls((1 << v->hash_dev_block_bits) / v->digest_size); From e1f978bc9b9cb2a142de82ee1714475a020f568e Mon Sep 17 00:00:00 2001 From: Lianjun Huang Date: Sat, 16 Jun 2018 22:59:46 +0800 Subject: [PATCH 0668/1276] ANDROID: sdcardfs: fix potential crash when reserved_mb is not zero sdcardfs_mkdir() calls check_min_free_space(). When reserved_mb is not zero, a negative dentry will be passed to ext4_statfs() at last and ext4_statfs() will crash. The parent dentry is positive. So we use the parent dentry to check free space. Change-Id: I80ab9623fe59ba911f4cc9f0e029a1c6f7ee421b Signed-off-by: Lianjun Huang --- fs/sdcardfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b43258684fb9..2de5a4dffa22 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -270,6 +270,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; + struct dentry *parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; @@ -289,11 +290,14 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ - if (!check_min_free_space(dentry, 0, 1)) { + parent_dentry = dget_parent(dentry); + if (!check_min_free_space(parent_dentry, 0, 1)) { pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; + dput(parent_dentry); goto out_revert; } + dput(parent_dentry); /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); From a6d3de6a7fbae65d1cb98c16ac907b32c88c053c Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Fri, 13 Jul 2018 14:31:40 -0700 Subject: [PATCH 0669/1276] ANDROID: Reduce use of #ifdef CONFIG_CPU_FREQ_TIMES Add empty versions of functions to cpufreq_times.h to cut down on use of #ifdef in .c files. Test: kernel builds with and without CONFIG_CPU_FREQ_TIMES=y Change-Id: I49ac364fac3d42bba0ca1801e23b15081094fb12 Signed-off-by: Connor O'Brien --- include/linux/cpufreq_times.h | 4 ++++ kernel/exit.c | 3 +-- kernel/sched/core.c | 2 -- kernel/sched/cputime.c | 5 +---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index e84b576a20d5..54419522a53c 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -30,6 +30,10 @@ void cpufreq_times_record_transition(struct cpufreq_freqs *freq); void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else +static inline void cpufreq_task_times_init(struct task_struct *p) {} +static inline void cpufreq_task_times_exit(struct task_struct *p) {} +static inline void cpufreq_acct_update_power(struct task_struct *p, + u64 cputime) {} static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( struct cpufreq_freqs *freq) {} diff --git a/kernel/exit.c b/kernel/exit.c index 07eceecc6402..2d95762343af 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -187,9 +187,8 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; -#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_exit(p); -#endif repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6b2a27bd29bc..623c6c5c941a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2157,9 +2157,7 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif -#ifdef CONFIG_CPU_FREQ_TIMES cpufreq_task_times_init(p); -#endif RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index ab9b6ff7117a..ca4208d46872 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -130,10 +130,8 @@ void account_user_time(struct task_struct *p, u64 cputime) /* Account for user time used */ acct_account_cputime(p); -#ifdef CONFIG_CPU_FREQ_TIMES /* Account power usage for user time */ cpufreq_acct_update_power(p, cputime); -#endif } /* @@ -178,10 +176,9 @@ void account_system_index_time(struct task_struct *p, /* Account for system time used */ acct_account_cputime(p); -#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for system time */ cpufreq_acct_update_power(p, cputime); -#endif } /* From b89ada5d9c09222a77243e488c049e2bb4941837 Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Sun, 3 Jun 2018 10:47:51 -0700 Subject: [PATCH 0670/1276] ANDROID: Fix massive cpufreq_times memory leaks Every time _cpu_up() is called for a CPU, idle_thread_get() is called which then re-initializes a CPU's idle thread that was already previously created and cached in a global variable in smpboot.c. idle_thread_get() calls init_idle() which then calls __sched_fork(). __sched_fork() is where cpufreq_task_times_init() is, and cpufreq_task_times_init() allocates memory for the task struct's time_in_state array. Since idle_thread_get() reuses a task struct instance that was already previously created, this means that every time it calls init_idle(), cpufreq_task_times_init() allocates this array again and overwrites the existing allocation that the idle thread already had. This causes memory to be leaked every time a CPU is onlined. In order to fix this, move allocation of time_in_state into _do_fork to avoid allocating it at all for idle threads. The cpufreq times interface is intended to be used for tracking userspace tasks, so we can safely remove it from the kernel's idle threads without killing any functionality. But that's not all! Task structs can be freed outside of release_task(), which creates another memory leak because a task struct can be freed without having its cpufreq times allocation freed. To fix this, free the cpufreq times allocation at the same time that task struct allocations are freed, in free_task(). Since free_task() can also be called in error paths of copy_process() after dup_task_struct(), set time_in_state to NULL immediately after calling dup_task_struct() to avoid possible double free. Bug description and fix adapted from patch submitted by Sultan Alsawaf at https://android-review.googlesource.com/c/kernel/msm/+/700134 Bug: 110044919 Test: Hikey960 builds, boots & reports /proc//time_in_state correctly Change-Id: I12fe7611fc88eb7f6c39f8f7629ad27b6ec4722c Signed-off-by: Connor O'Brien --- drivers/cpufreq/cpufreq_times.c | 9 ++++++--- include/linux/cpufreq_times.h | 2 ++ kernel/exit.c | 3 --- kernel/fork.c | 7 +++++++ kernel/sched/core.c | 3 --- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index f560e10ba183..a43eeee30e8e 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -234,16 +234,19 @@ static int uid_time_in_state_seq_show(struct seq_file *m, void *v) void cpufreq_task_times_init(struct task_struct *p) { - void *temp; unsigned long flags; - unsigned int max_state; spin_lock_irqsave(&task_time_in_state_lock, flags); p->time_in_state = NULL; spin_unlock_irqrestore(&task_time_in_state_lock, flags); p->max_state = 0; +} - max_state = READ_ONCE(next_offset); +void cpufreq_task_times_alloc(struct task_struct *p) +{ + void *temp; + unsigned long flags; + unsigned int max_state = READ_ONCE(next_offset); /* We use one array to avoid multiple allocs per task */ temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC); diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index 54419522a53c..757bf0cb6070 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -21,6 +21,7 @@ #ifdef CONFIG_CPU_FREQ_TIMES void cpufreq_task_times_init(struct task_struct *p); +void cpufreq_task_times_alloc(struct task_struct *p); void cpufreq_task_times_exit(struct task_struct *p); int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *p); @@ -31,6 +32,7 @@ void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else static inline void cpufreq_task_times_init(struct task_struct *p) {} +static inline void cpufreq_task_times_alloc(struct task_struct *p) {} static inline void cpufreq_task_times_exit(struct task_struct *p) {} static inline void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) {} diff --git a/kernel/exit.c b/kernel/exit.c index 2d95762343af..0e21e6d21f35 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -62,7 +62,6 @@ #include #include #include -#include #include #include @@ -187,8 +186,6 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; - - cpufreq_task_times_exit(p); repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/fork.c b/kernel/fork.c index f0b58479534f..65acaa274a54 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -91,6 +91,7 @@ #include #include #include +#include #include #include @@ -394,6 +395,8 @@ void put_task_stack(struct task_struct *tsk) void free_task(struct task_struct *tsk) { + cpufreq_task_times_exit(tsk); + #ifndef CONFIG_THREAD_INFO_IN_TASK /* * The task is finally done with both the stack and thread_info, @@ -1708,6 +1711,8 @@ static __latent_entropy struct task_struct *copy_process( if (!p) goto fork_out; + cpufreq_task_times_init(p); + /* * This _must_ happen before we call free_task(), i.e. before we jump * to any of the bad_fork_* labels. This is to avoid freeing @@ -2170,6 +2175,8 @@ long _do_fork(unsigned long clone_flags, if (IS_ERR(p)) return PTR_ERR(p); + cpufreq_task_times_alloc(p); + /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 623c6c5c941a..ad97f3ba5ec5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6,7 +6,6 @@ * Copyright (C) 1991-2002 Linus Torvalds */ #include "sched.h" -#include #include @@ -2157,8 +2156,6 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif - cpufreq_task_times_init(p); - RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); init_dl_inactive_task_timer(&p->dl); From 89a54ed3bf68224e4b9374b6befe71ac5cda1518 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 May 2017 16:38:16 -0700 Subject: [PATCH 0671/1276] ANDROID: mnt: Fix next_descendent next_descendent did not properly handle the case where the initial mount had no slaves. In this case, we would look for the next slave, but since don't have a master, the check for wrapping around to the start of the list will always fail. Instead, we check for this case, and ensure that we end the iteration when we come back to the root. Signed-off-by: Daniel Rosenberg Bug: 62094374 Change-Id: I43dfcee041aa3730cb4b9a1161418974ef84812e --- fs/pnode.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/pnode.c b/fs/pnode.c index 386884dd6d97..56f9a28a688b 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -616,9 +616,14 @@ static struct mount *next_descendent(struct mount *root, struct mount *cur) if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list)) return first_slave(cur); do { - if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list) - return next_slave(cur); - cur = cur->mnt_master; + struct mount *master = cur->mnt_master; + + if (!master || cur->mnt_slave.next != &master->mnt_slave_list) { + struct mount *next = next_slave(cur); + + return (next == root) ? NULL : next; + } + cur = master; } while (cur != root); return NULL; } From 82b9872b3894ad1a02e018f4b1642745e9587e30 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 21 Jun 2017 09:22:45 +0530 Subject: [PATCH 0672/1276] ANDROID: uid_sys_stats: Replace tasklist lock with RCU in uid_cputime_show Tasklist lock is acuquired in uid_cputime_show for updating the stats for all tasks in the system. This can potentially disable preemption for several milli seconds. Replace tasklist_lock with RCU read side primitives. Change-Id: Ife69cb577bfdceaae6eb21b9bda09a0fe687e140 Signed-off-by: Pavankumar Kondeti --- drivers/misc/uid_sys_stats.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index fbf7db598d55..88dc1cd3a204 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -346,13 +346,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime = 0; } - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); if (!uid_entry || uid_entry->uid != uid) uid_entry = find_or_register_uid(uid); if (!uid_entry) { - read_unlock(&tasklist_lock); + rcu_read_unlock(); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, uid); @@ -362,7 +362,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime += utime; uid_entry->active_stime += stime; } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { u64 total_utime = uid_entry->utime + From 6b1524334fe290b43156ff13ec62c93d96688555 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 18 Jul 2018 07:16:42 -0700 Subject: [PATCH 0673/1276] ANDROID: verity: fix android-verity Kconfig dependencies Bug: 72722987 Test: Android verity now shows up in 'make menuconfig' Change-Id: I21c2f36c17f45e5eb0daa1257f5817f9d56527e7 Signed-off-by: Sandeep Patil --- drivers/md/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 8a258c688a07..49ea175c6d38 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -576,10 +576,11 @@ config DM_ZONED config DM_ANDROID_VERITY bool "Android verity target support" + depends on BLK_DEV_DM depends on DM_VERITY depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING - depends on PUBLIC_KEY_ALGO_RSA + depends on CRYPTO_RSA depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE @@ -594,8 +595,8 @@ config DM_ANDROID_VERITY config DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED bool "Verity will validate blocks at most once" - depends on DM_VERITY - ---help--- + depends on DM_VERITY + ---help--- Default enables at_most_once option for dm-verity Verify data blocks only the first time they are read from the From 997e74035d74b1af038e16dbb1f60ca7df14d29e Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 24 Jul 2018 09:40:07 -0700 Subject: [PATCH 0674/1276] ANDROID: android-verity: Add API to verify signature with builtin keys. The builtin keyring was exported prior to this which allowed android-verity to simply lookup the key in the builtin keyring and verify the signature of the verity metadata. This is now broken as the kernel expects the signature to be in pkcs#7 format (same used for module signing). Obviously, this doesn't work with the verity metadata as we just append the raw signature in the metadata .. sigh. *This one time*, add an API to accept arbitrary signature and verify that with a key from system's trusted keyring. Bug: 72722987 Test: $ adb push verity_fs.img /data/local/tmp/ $ adb root && adb shell > cd /data/local/tmp > losetup /dev/block/loop0 verity_fs.img > dmctl create verity-fs android-verity 0 4200 Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f 7:0 > mount -t ext4 /dev/block/dm-0 temp/ > cat temp/foo.txt temp/bar.txt Change-Id: I0c14f3cb2b587b73a4c75907367769688756213e Signed-off-by: Sandeep Patil --- certs/system_keyring.c | 43 +++++++++++++++++++++++++++++++++++- include/linux/verification.h | 8 +++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 81728717523d..4ba922ff3db6 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -264,5 +264,46 @@ int verify_pkcs7_signature(const void *data, size_t len, return ret; } EXPORT_SYMBOL_GPL(verify_pkcs7_signature); - #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ + +/** + * verify_signature_one - Verify a signature with keys from given keyring + * @sig: The signature to be verified + * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only, + * (void *)1UL for all trusted keys). + * @keyid: key description (not partial) + */ +int verify_signature_one(const struct public_key_signature *sig, + struct key *trusted_keys, const char *keyid) +{ + key_ref_t ref; + struct key *key; + int ret; + + if (!sig) + return -EBADMSG; + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == (void *)1UL) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } + + ref = keyring_search(make_key_ref(trusted_keys, 1), + &key_type_asymmetric, keyid); + if (IS_ERR(ref)) { + pr_err("Asymmetric key (%s) not found in keyring(%s)\n", + keyid, trusted_keys->description); + return -ENOKEY; + } + + key = key_ref_to_ptr(ref); + ret = verify_signature(key, sig); + key_put(key); + return ret; +} +EXPORT_SYMBOL_GPL(verify_signature_one); + diff --git a/include/linux/verification.h b/include/linux/verification.h index cfa4730d607a..60ea906b603f 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -32,9 +32,13 @@ enum key_being_used_for { }; extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION - struct key; +struct public_key_signature; + +extern int verify_signature_one(const struct public_key_signature *sig, + struct key *trusted_keys, const char *keyid); + +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION extern int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, From ee9bb971efdc7f9b3977389f5f4e313355157df6 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Mon, 23 Jul 2018 16:31:32 -0700 Subject: [PATCH 0675/1276] ANDROID: android-verity: Make it work with newer kernels Fixed bio API calls as they changed from 4.4 to 4.9. Fixed the driver to use the new verify_signature_one() API. Remove the dead code resulted from the rebase. Bug: 72722987 Test: Build and boot hikey with system partition mounted as root using android-verity Signed-off-by: Sandeep Patil Change-Id: I1e29111d57b62f0451404c08d49145039dd00737 --- drivers/md/dm-android-verity.c | 194 +++++++++++++++------------------ drivers/md/dm-android-verity.h | 5 +- 2 files changed, 88 insertions(+), 111 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index cfb592e43e5f..aa6ee02c286d 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -122,75 +123,6 @@ static inline bool is_unlocked(void) return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); } -static int table_extract_mpi_array(struct public_key_signature *pks, - const void *data, size_t len) -{ - MPI mpi = mpi_read_raw_data(data, len); - - if (!mpi) { - DMERR("Error while allocating mpi array"); - return -ENOMEM; - } - - pks->mpi[0] = mpi; - pks->nr_mpi = 1; - return 0; -} - -static struct public_key_signature *table_make_digest( - enum hash_algo hash, - const void *table, - unsigned long table_len) -{ - struct public_key_signature *pks = NULL; - struct crypto_shash *tfm; - struct shash_desc *desc; - size_t digest_size, desc_size; - int ret; - - /* Allocate the hashing algorithm we're going to need and find out how - * big the hash operational data will be. - */ - tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); - if (IS_ERR(tfm)) - return ERR_CAST(tfm); - - desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); - - /* We allocate the hash operational data storage on the end of out - * context data and the digest output buffer on the end of that. - */ - ret = -ENOMEM; - pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); - if (!pks) - goto error; - - pks->pkey_hash_algo = hash; - pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; - pks->digest_size = digest_size; - - desc = (struct shash_desc *)(pks + 1); - desc->tfm = tfm; - desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; - - ret = crypto_shash_init(desc); - if (ret < 0) - goto error; - - ret = crypto_shash_finup(desc, table, table_len, pks->digest); - if (ret < 0) - goto error; - - crypto_free_shash(tfm); - return pks; - -error: - kfree(pks); - crypto_free_shash(tfm); - return ERR_PTR(ret); -} - static int read_block_dev(struct bio_read *payload, struct block_device *bdev, sector_t offset, int length) { @@ -205,8 +137,9 @@ static int read_block_dev(struct bio_read *payload, struct block_device *bdev, return -ENOMEM; } - bio->bi_bdev = bdev; + bio_set_dev(bio, bdev); bio->bi_iter.bi_sector = offset; + bio_set_op_attrs(bio, REQ_OP_READ, 0); payload->page_io = kzalloc(sizeof(struct page *) * payload->number_of_pages, GFP_KERNEL); @@ -230,7 +163,7 @@ static int read_block_dev(struct bio_read *payload, struct block_device *bdev, } } - if (!submit_bio_wait(READ, bio)) + if (!submit_bio_wait(bio)) /* success */ goto free_bio; DMERR("bio read failed"); @@ -567,28 +500,85 @@ static int verity_mode(void) return DM_VERITY_MODE_EIO; } +static void handle_error(void) +{ + int mode = verity_mode(); + if (mode == DM_VERITY_MODE_RESTART) { + DMERR("triggering restart"); + kernel_restart("dm-verity device corrupted"); + } else { + DMERR("Mounting verity root failed"); + } +} + +static struct public_key_signature *table_make_digest( + enum hash_algo hash, + const void *table, + unsigned long table_len) +{ + struct public_key_signature *pks = NULL; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + int ret; + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return ERR_CAST(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of out + * context data and the digest output buffer on the end of that. + */ + ret = -ENOMEM; + pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); + if (!pks) + goto error; + + pks->pkey_algo = "rsa"; + pks->hash_algo = hash_algo_name[hash]; + pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; + pks->digest_size = digest_size; + + desc = (struct shash_desc *)(pks + 1); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_finup(desc, table, table_len, pks->digest); + if (ret < 0) + goto error; + + crypto_free_shash(tfm); + return pks; + +error: + kfree(pks); + crypto_free_shash(tfm); + return ERR_PTR(ret); +} + + static int verify_verity_signature(char *key_id, struct android_metadata *metadata) { - key_ref_t key_ref; - struct key *key; struct public_key_signature *pks = NULL; int retval = -EINVAL; - key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1), - &key_type_asymmetric, key_id); - - if (IS_ERR(key_ref)) { - DMERR("keyring: key not found"); - return -ENOKEY; - } - - key = key_ref_to_ptr(key_ref); + if (!key_id) + goto error; pks = table_make_digest(HASH_ALGO_SHA256, (const void *)metadata->verity_table, le32_to_cpu(metadata->header->table_length)); - if (IS_ERR(pks)) { DMERR("hashing failed"); retval = PTR_ERR(pks); @@ -596,33 +586,20 @@ static int verify_verity_signature(char *key_id, goto error; } - retval = table_extract_mpi_array(pks, &metadata->header->signature[0], - RSANUMBYTES); - if (retval < 0) { - DMERR("Error extracting mpi %d", retval); + pks->s = kmemdup(&metadata->header->signature[0], RSANUMBYTES, GFP_KERNEL); + if (!pks->s) { + DMERR("Error allocating memory for signature"); goto error; } + pks->s_size = RSANUMBYTES; - retval = verify_signature(key, pks); - mpi_free(pks->rsa.s); + retval = verify_signature_one(pks, NULL, key_id); + kfree(pks->s); error: kfree(pks); - key_put(key); - return retval; } -static void handle_error(void) -{ - int mode = verity_mode(); - if (mode == DM_VERITY_MODE_RESTART) { - DMERR("triggering restart"); - kernel_restart("dm-verity device corrupted"); - } else { - DMERR("Mounting verity root failed"); - } -} - static inline bool test_mult_overflow(sector_t a, u32 b) { sector_t r = (sector_t)~0ULL; @@ -698,8 +675,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *metadata = NULL; int err = 0, i, mode; - char *key_id, *table_ptr, dummy, *target_device, - *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; + char *key_id = NULL, *table_ptr, dummy, *target_device; + char *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; u32 data_block_size; @@ -881,12 +858,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) } err = verity_ctr(ti, no_of_args, verity_table_args); - - if (err) - DMERR("android-verity failed to mount as verity target"); - else { + if (err) { + DMERR("android-verity failed to create a verity target"); + } else { target_added = true; - DMINFO("android-verity mounted as verity target"); + DMINFO("android-verity created as verity target"); } free_metadata: diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index ce4eb95c2f6e..a1c8f4b5d8c2 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -122,8 +122,9 @@ extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); #if IS_ENABLED(CONFIG_DAX_DRIVER) -extern long dm_linear_dax_direct_access(struct dm_target *ti, sector_t sector, - void **kaddr, pfn_t *pfn, long size); +extern long dm_linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, + long nr_pages, void **kaddr, + pfn_t *pfn); extern size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i); #else From 25cddfe8593aa5dc927ba44eeb98aff48fb003cb Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 24 Jul 2018 16:59:40 -0700 Subject: [PATCH 0676/1276] ANDROID: android-verity: Fix broken parameter handling. android-verity documentation states that the target expectets the key, followed by the backing device on the commandline as follows "dm=system none ro,0 1 android-verity " However, the code actually expects the backing device as the first parameter. Fix that. Bug: 72722987 Change-Id: Ibd56c0220f6003bdfb95aa2d611f787e75a65c97 Signed-off-by: Sandeep Patil --- drivers/md/dm-android-verity.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index aa6ee02c286d..e004e2c55a3a 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -695,16 +695,16 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) handle_error(); return -EINVAL; } - } else if (argc == 2) - key_id = argv[1]; - else { + target_device = argv[0]; + } else if (argc == 2) { + key_id = argv[0]; + target_device = argv[1]; + } else { DMERR("Incorrect number of arguments"); handle_error(); return -EINVAL; } - target_device = argv[0]; - dev = name_to_dev_t(target_device); if (!dev) { DMERR("no dev found for %s", target_device); From 2c2951d10d1f43ae3d6f33339fca00b0ea78ac6e Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 25 Jul 2018 16:11:38 -0700 Subject: [PATCH 0677/1276] ANDROID: x86_64_cuttlefish_defconfig: enable verity cert Bug: 72722987 Test: Build, boot and verify in /proc/keys Change-Id: Ia55b94d56827003a88cb6083a75340ee31347470 Signed-off-by: Alistair Strachan [AmitP: Added ANDROID prefix in commit header] Signed-off-by: Amit Pundir --- arch/x86/configs/x86_64_cuttlefish_defconfig | 5 ++++ verity_dev_keys.x509 | 24 ++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 verity_dev_keys.x509 diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 26a42b91140b..e33b351b1538 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -450,3 +450,8 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="verity_dev_keys.x509" diff --git a/verity_dev_keys.x509 b/verity_dev_keys.x509 new file mode 100644 index 000000000000..86399c3c1dd7 --- /dev/null +++ b/verity_dev_keys.x509 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/TCCAuWgAwIBAgIJAJcPmDkJqolJMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g +VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE +AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe +Fw0xNDExMDYxOTA3NDBaFw00MjAzMjQxOTA3NDBaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G +A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p +ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOjreE0vTVSRenuzO9vnaWfk0eQzYab0gqpi +6xAzi6dmD+ugoEKJmbPiuE5Dwf21isZ9uhUUu0dQM46dK4ocKxMRrcnmGxydFn6o +fs3ODJMXOkv2gKXL/FdbEPdDbxzdu8z3yk+W67udM/fW7WbaQ3DO0knu+izKak/3 +T41c5uoXmQ81UNtAzRGzGchNVXMmWuTGOkg6U+0I2Td7K8yvUMWhAWPPpKLtVH9r +AL5TzjYNR92izdKcz3AjRsI3CTjtpiVABGeX0TcjRSuZB7K9EK56HV+OFNS6I1NP +jdD7FIShyGlqqZdUOkAUZYanbpgeT5N7QL6uuqcGpoTOkalu6kkCAwEAAaNQME4w +HQYDVR0OBBYEFH5DM/m7oArf4O3peeKO0ZIEkrQPMB8GA1UdIwQYMBaAFH5DM/m7 +oArf4O3peeKO0ZIEkrQPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AHO3NSvDE5jFvMehGGtS8BnFYdFKRIglDMc4niWSzhzOVYRH4WajxdtBWc5fx0ix +NF/+hVKVhP6AIOQa+++sk+HIi7RvioPPbhjcsVlZe7cUEGrLSSveGouQyc+j0+m6 +JF84kszIl5GGNMTnx0XRPO+g8t6h5LWfnVydgZfpGRRg+WHewk1U2HlvTjIceb0N +dcoJ8WKJAFWdcuE7VIm4w+vF/DYX/A2Oyzr2+QRhmYSv1cusgAeC1tvH4ap+J1Lg +UnOu5Kh/FqPLLSwNVQp4Bu7b9QFfqK8Moj84bj88NqRGZgDyqzuTrFxn6FW7dmyA +yttuAJAEAymk1mipd9+zp38= +-----END CERTIFICATE----- From ef2f0176888245f58248ba81774d0cdaa74cc4c8 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 25 Jul 2018 16:11:09 -0700 Subject: [PATCH 0678/1276] ANDROID: x86_64_cuttlefish_defconfig: Enable android-verity Bug: 72722987 Test: Build & boot with x86_64_cuttlefish_defconfig Change-Id: I961e6aaa944b5ab0c005cb39604a52f8dc98fb06 Signed-off-by: Alistair Strachan [AmitP: Added ANDROID prefix in commit header] Signed-off-by: Amit Pundir --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index e33b351b1538..19e3a812306b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -219,7 +219,9 @@ CONFIG_DM_MIRROR=y CONFIG_DM_ZERO=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 CONFIG_DM_VERITY_FEC=y +CONFIG_DM_ANDROID_VERITY=y CONFIG_NETDEVICES=y CONFIG_NETCONSOLE=y CONFIG_NETCONSOLE_DYNAMIC=y @@ -447,6 +449,7 @@ CONFIG_SECURITY_PATH=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y From d5b23a4ab2ceb4402fd96665ba9f41a020e97ec8 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Fri, 27 Jul 2018 09:18:28 -0700 Subject: [PATCH 0679/1276] ANDROID: verity: really fix android-verity Kconfig The change "ANDROID: verity: fix android-verity Kconfig dependencies" relaxed the dependency on DM_VERITY=y to just DM_VERITY, but this is not correct because there are parts of the verity and dm-mod API that android-verity is using but which are not exported to modules. Work around this problem by disallowing android-verity to be built-in when the dm/verity core is built modularly. Bug: 72722987 Change-Id: I3cfaa2acca8e4a4b5c459afdddd958ac9f8c1eb3 Signed-off-by: Alistair Strachan --- drivers/md/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 49ea175c6d38..be6b64ce3881 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -576,8 +576,8 @@ config DM_ZONED config DM_ANDROID_VERITY bool "Android verity target support" - depends on BLK_DEV_DM - depends on DM_VERITY + depends on BLK_DEV_DM=y + depends on DM_VERITY=y depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING depends on CRYPTO_RSA From faa148eaf8eddad751e18184c077b060ef8ee71b Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 26 Jul 2018 16:32:09 -0700 Subject: [PATCH 0680/1276] ANDROID: sdcardfs: Check stacked filesystem depth bug: 111860541 Change-Id: Ia0a30b2b8956c4ada28981584cd8647713a1e993 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 30e0c431a1ea..27ec726e7a46 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -295,6 +295,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, atomic_inc(&lower_sb->s_active); sdcardfs_set_lower_super(sb, lower_sb); + sb->s_stack_depth = lower_sb->s_stack_depth + 1; + if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { + pr_err("sdcardfs: maximum fs stacking depth exceeded\n"); + err = -EINVAL; + goto out_sput; + } + /* inherit maxbytes from lower file system */ sb->s_maxbytes = lower_sb->s_maxbytes; From 8705cf55a26addcae32a7edeab94f89766e0e9fb Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 10 May 2018 14:56:41 -0700 Subject: [PATCH 0681/1276] ANDROID: ftrace: fix function type mismatches This change fixes indirect call mismatches with function and function graph tracing, which trip Control-Flow Integrity (CFI) checking. Bug: 79510107 Bug: 67506682 Change-Id: I5de08c113fb970ffefedce93c58e0161f22c7ca2 Signed-off-by: Sami Tolvanen --- include/linux/ftrace.h | 8 ++++++++ kernel/trace/ftrace.c | 17 +++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index a397907e8d72..8ff2bfb22ecf 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -224,8 +224,16 @@ extern enum ftrace_tracing_type_t ftrace_tracing_type; int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); +#ifdef CONFIG_CFI_CLANG +/* Use a C stub with the correct type for CFI */ +static inline void ftrace_stub(unsigned long a0, unsigned long a1, + struct ftrace_ops *op, struct pt_regs *regs) +{ +} +#else extern void ftrace_stub(unsigned long a0, unsigned long a1, struct ftrace_ops *op, struct pt_regs *regs); +#endif #else /* !CONFIG_FUNCTION_TRACER */ /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f536f601bd46..40aec2d6051b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -123,8 +123,9 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs); #else /* See comment below, where ftrace_ops_list_func is defined */ -static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); -#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) +static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs); +#define ftrace_ops_list_func ftrace_ops_no_ops #endif /* @@ -6310,7 +6311,8 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, __ftrace_ops_list_func(ip, parent_ip, NULL, regs); } #else -static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip) +static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs) { __ftrace_ops_list_func(ip, parent_ip, NULL, NULL); } @@ -6771,14 +6773,17 @@ void ftrace_graph_graph_time_control(bool enable) fgraph_graph_time = enable; } +void ftrace_graph_return_stub(struct ftrace_graph_ret *trace) +{ +} + int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace) { return 0; } /* The callbacks that hook a function */ -trace_func_graph_ret_t ftrace_graph_return = - (trace_func_graph_ret_t)ftrace_stub; +trace_func_graph_ret_t ftrace_graph_return = ftrace_graph_return_stub; trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub; static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub; @@ -7007,7 +7012,7 @@ void unregister_ftrace_graph(void) goto out; ftrace_graph_active--; - ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; + ftrace_graph_return = ftrace_graph_return_stub; ftrace_graph_entry = ftrace_graph_entry_stub; __ftrace_graph_entry = ftrace_graph_entry_stub; ftrace_shutdown(&graph_ops, FTRACE_STOP_FUNC_RET); From 6e0938f4cd0ca353cd3fa1ca589ee04ed0b8f0dd Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Fri, 20 Jul 2018 09:52:12 -0700 Subject: [PATCH 0682/1276] ANDROID: remove android config fragments The authoritative versions of the android kconfig fragments were moved into a separate repository some time back: https://android.googlesource.com/kernel/configs/ Change-Id: I1fc770b4f040de983c6b7a041502aef864c86cff Signed-off-by: Steve Muckle --- kernel/configs/android-base.config | 161 ---------------------- kernel/configs/android-recommended.config | 129 ----------------- 2 files changed, 290 deletions(-) delete mode 100644 kernel/configs/android-base.config delete mode 100644 kernel/configs/android-recommended.config diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config deleted file mode 100644 index d3fd428f4b92..000000000000 --- a/kernel/configs/android-base.config +++ /dev/null @@ -1,161 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_DEVKMEM is not set -# CONFIG_DEVMEM is not set -# CONFIG_FHANDLE is not set -# CONFIG_INET_LRO is not set -# CONFIG_NFSD is not set -# CONFIG_NFS_FS is not set -# CONFIG_OABI_COMPAT is not set -# CONFIG_SYSVIPC is not set -# CONFIG_USELIB is not set -CONFIG_ANDROID=y -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder -CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ARMV8_DEPRECATED=y -CONFIG_ASHMEM=y -CONFIG_AUDIT=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_CGROUPS=y -CONFIG_CGROUP_BPF=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_DEBUG=y -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_SCHED=y -CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_DEFAULT_SECURITY_SELINUX=y -CONFIG_EMBEDDED=y -CONFIG_FB=y -CONFIG_HARDENED_USERCOPY=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_INET=y -CONFIG_INET_DIAG_DESTROY=y -CONFIG_INET_ESP=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IPV6=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_SECURITY=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_NET=y -CONFIG_NETDEVICES=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_TPROXY=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_LENGTH=y -CONFIG_NETFILTER_XT_MATCH_LIMIT=y -CONFIG_NETFILTER_XT_MATCH_MAC=y -CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y -CONFIG_NETFILTER_XT_MATCH_STATISTIC=y -CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_SECMARK=y -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -CONFIG_NET_CLS_ACT=y -CONFIG_NET_CLS_U32=y -CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_U32=y -CONFIG_NET_KEY=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_HTB=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_NAT=y -CONFIG_NO_HZ=y -CONFIG_PACKET=y -CONFIG_PM_AUTOSLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_MPPE=y -CONFIG_PREEMPT=y -CONFIG_QUOTA=y -CONFIG_RANDOMIZE_BASE=y -CONFIG_RTC_CLASS=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_SECCOMP=y -CONFIG_SECURITY=y -CONFIG_SECURITY_NETWORK=y -CONFIG_SECURITY_SELINUX=y -CONFIG_SETEND_EMULATION=y -CONFIG_STAGING=y -CONFIG_SWP_EMULATION=y -CONFIG_SYNC=y -CONFIG_TUN=y -CONFIG_UNIX=y -CONFIG_USB_GADGET=y -CONFIG_USB_CONFIGFS=y -CONFIG_USB_CONFIGFS_F_FS=y -CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_USB_OTG_WAKELOCK=y -CONFIG_XFRM_USER=y diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config deleted file mode 100644 index 81e9af7dcec2..000000000000 --- a/kernel/configs/android-recommended.config +++ /dev/null @@ -1,129 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_AIO is not set -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_LEGACY_PTYS is not set -# CONFIG_NF_CONNTRACK_SIP is not set -# CONFIG_PM_WAKELOCKS_GC is not set -# CONFIG_VT is not set -CONFIG_ARM64_SW_TTBR0_PAN=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BLK_DEV_DM=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_STACKPROTECTOR_STRONG=y -CONFIG_COMPACTION=y -CONFIG_CPU_SW_DOMAIN_PAN=y -CONFIG_DM_CRYPT=y -CONFIG_DM_UEVENT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y -CONFIG_DRAGONRISE_FF=y -CONFIG_ENABLE_DEFAULT_TRACERS=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_FUSE_FS=y -CONFIG_GREENASIA_FF=y -CONFIG_HIDRAW=y -CONFIG_HID_A4TECH=y -CONFIG_HID_ACRUX=y -CONFIG_HID_ACRUX_FF=y -CONFIG_HID_APPLE=y -CONFIG_HID_BELKIN=y -CONFIG_HID_CHERRY=y -CONFIG_HID_CHICONY=y -CONFIG_HID_CYPRESS=y -CONFIG_HID_DRAGONRISE=y -CONFIG_HID_ELECOM=y -CONFIG_HID_EMS_FF=y -CONFIG_HID_EZKEY=y -CONFIG_HID_GREENASIA=y -CONFIG_HID_GYRATION=y -CONFIG_HID_HOLTEK=y -CONFIG_HID_KENSINGTON=y -CONFIG_HID_KEYTOUCH=y -CONFIG_HID_KYE=y -CONFIG_HID_LCPOWER=y -CONFIG_HID_LOGITECH=y -CONFIG_HID_LOGITECH_DJ=y -CONFIG_HID_MAGICMOUSE=y -CONFIG_HID_MICROSOFT=y -CONFIG_HID_MONTEREY=y -CONFIG_HID_MULTITOUCH=y -CONFIG_HID_NTRIG=y -CONFIG_HID_ORTEK=y -CONFIG_HID_PANTHERLORD=y -CONFIG_HID_PETALYNX=y -CONFIG_HID_PICOLCD=y -CONFIG_HID_PRIMAX=y -CONFIG_HID_PRODIKEYS=y -CONFIG_HID_ROCCAT=y -CONFIG_HID_SAITEK=y -CONFIG_HID_SAMSUNG=y -CONFIG_HID_SMARTJOYPLUS=y -CONFIG_HID_SONY=y -CONFIG_HID_SPEEDLINK=y -CONFIG_HID_SUNPLUS=y -CONFIG_HID_THRUSTMASTER=y -CONFIG_HID_TIVO=y -CONFIG_HID_TOPSEED=y -CONFIG_HID_TWINHAN=y -CONFIG_HID_UCLOGIC=y -CONFIG_HID_WACOM=y -CONFIG_HID_WALTOP=y -CONFIG_HID_WIIMOTE=y -CONFIG_HID_ZEROPLUS=y -CONFIG_HID_ZYDACRON=y -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_GPIO=y -CONFIG_INPUT_JOYSTICK=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_TABLET=y -CONFIG_INPUT_UINPUT=y -CONFIG_ION=y -CONFIG_JOYSTICK_XPAD=y -CONFIG_JOYSTICK_XPAD_FF=y -CONFIG_JOYSTICK_XPAD_LEDS=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KSM=y -CONFIG_LOGIG940_FF=y -CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_LOGITECH_FF=y -CONFIG_MD=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_MSDOS_FS=y -CONFIG_PANIC_TIMEOUT=5 -CONFIG_PANTHERLORD_FF=y -CONFIG_PERF_EVENTS=y -CONFIG_PM_DEBUG=y -CONFIG_PM_RUNTIME=y -CONFIG_PM_WAKELOCKS_LIMIT=0 -CONFIG_POWER_SUPPLY=y -CONFIG_PSTORE=y -CONFIG_PSTORE_CONSOLE=y -CONFIG_PSTORE_RAM=y -CONFIG_SCHEDSTATS=y -CONFIG_SMARTJOYPLUS_FF=y -CONFIG_SND=y -CONFIG_SOUND=y -CONFIG_STRICT_KERNEL_RWX=y -CONFIG_SUSPEND_TIME=y -CONFIG_TABLET_USB_ACECAD=y -CONFIG_TABLET_USB_AIPTEK=y -CONFIG_TABLET_USB_GTCO=y -CONFIG_TABLET_USB_HANWANG=y -CONFIG_TABLET_USB_KBTAB=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_TASK_XACCT=y -CONFIG_TIMER_STATS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_UHID=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_HIDDEV=y -CONFIG_USB_USBNET=y -CONFIG_VFAT_FS=y From 08a3bf400cf1df47493347899498e5b207be322b Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 24 Jan 2017 13:17:01 -0500 Subject: [PATCH 0683/1276] ANDROID: AVB error handler to invalidate vbmeta partition. If androidboot.vbmeta.invalidate_on_error is 'yes' and androidboot.vbmeta.device is set and points to a device with vbmeta magic, this header will be overwritten upon an irrecoverable dm-verity error. The side-effect of this is that the slot will fail to verify on next reboot, effectively triggering the boot loader to fallback to another slot. This work both if the vbmeta struct is at the start of a partition or if there's an AVB footer at the end. This code is based on drivers/md/dm-verity-chromeos.c from ChromiumOS. Bug: 31622239 Test: Manually tested (other arch). Change-Id: I571b5a75461da38ad832a9bea33c298bef859e26 Signed-off-by: David Zeuthen --- drivers/md/Kconfig | 9 ++ drivers/md/Makefile | 4 + drivers/md/dm-verity-avb.c | 229 ++++++++++++++++++++++++++++++++++ drivers/md/dm-verity-target.c | 6 +- drivers/md/dm-verity.h | 1 + 5 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 drivers/md/dm-verity-avb.c diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index be6b64ce3881..7a0fea9633fb 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -574,6 +574,15 @@ config DM_ZONED If unsure, say N. +config DM_VERITY_AVB + tristate "Support AVB specific verity error behavior" + depends on DM_VERITY + ---help--- + Enables Android Verified Boot platform-specific error + behavior. In particular, it will modify the vbmeta partition + specified on the kernel command-line when non-transient error + occurs (followed by a panic). + config DM_ANDROID_VERITY bool "Android verity target support" depends on BLK_DEV_DM=y diff --git a/drivers/md/Makefile b/drivers/md/Makefile index dab38ff6f542..8e371cd1d2ed 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -77,6 +77,10 @@ ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif +ifeq ($(CONFIG_DM_VERITY_AVB),y) +dm-verity-objs += dm-verity-avb.o +endif + ifeq ($(CONFIG_DM_ANDROID_VERITY),y) dm-verity-objs += dm-android-verity.o endif diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c new file mode 100644 index 000000000000..a9f102aa379e --- /dev/null +++ b/drivers/md/dm-verity-avb.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2017 Google. + * + * This file is released under the GPLv2. + * + * Based on drivers/md/dm-verity-chromeos.c + */ + +#include +#include +#include + +#define DM_MSG_PREFIX "verity-avb" + +/* Set via module parameters. */ +static char avb_vbmeta_device[64]; +static char avb_invalidate_on_error[4]; + +static void invalidate_vbmeta_endio(struct bio *bio) +{ + if (bio->bi_status) + DMERR("invalidate_vbmeta_endio: error %d", bio->bi_status); + complete(bio->bi_private); +} + +static int invalidate_vbmeta_submit(struct bio *bio, + struct block_device *bdev, + int op, int access_last_sector, + struct page *page) +{ + DECLARE_COMPLETION_ONSTACK(wait); + + bio->bi_private = &wait; + bio->bi_end_io = invalidate_vbmeta_endio; + bio_set_dev(bio, bdev); + bio_set_op_attrs(bio, op, REQ_SYNC); + + bio->bi_iter.bi_sector = 0; + if (access_last_sector) { + sector_t last_sector; + + last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + bio->bi_iter.bi_sector = last_sector; + } + if (!bio_add_page(bio, page, PAGE_SIZE, 0)) { + DMERR("invalidate_vbmeta_submit: bio_add_page error"); + return -EIO; + } + + submit_bio(bio); + /* Wait up to 2 seconds for completion or fail. */ + if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000))) + return -EIO; + return 0; +} + +static int invalidate_vbmeta(dev_t vbmeta_devt) +{ + int ret = 0; + struct block_device *bdev; + struct bio *bio; + struct page *page; + fmode_t dev_mode; + /* Ensure we do synchronous unblocked I/O. We may also need + * sync_bdev() on completion, but it really shouldn't. + */ + int access_last_sector = 0; + + DMINFO("invalidate_vbmeta: acting on device %d:%d", + MAJOR(vbmeta_devt), MINOR(vbmeta_devt)); + + /* First we open the device for reading. */ + dev_mode = FMODE_READ | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_kernel: could not open device for reading"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_read; + } + + bio = bio_alloc(GFP_NOIO, 1); + if (!bio) { + ret = -ENOMEM; + goto failed_bio_alloc; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + ret = -ENOMEM; + goto failed_to_alloc_page; + } + + access_last_sector = 0; + ret = invalidate_vbmeta_submit(bio, bdev, REQ_OP_READ, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + + /* We have a page. Let's make sure it looks right. */ + if (memcmp("AVB0", page_address(page), 4) == 0) { + /* Stamp it. */ + memcpy(page_address(page), "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta partition"); + } else { + /* Could be this is on a AVB footer, check. Also, since the + * AVB footer is in the last 64 bytes, adjust for the fact that + * we're dealing with 512-byte sectors. + */ + size_t offset = (1<bi_remaining. + */ + bio_reset(bio); + + ret = invalidate_vbmeta_submit(bio, bdev, REQ_OP_WRITE, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error writing"); + goto failed_to_submit_write; + } + + DMERR("invalidate_vbmeta: completed."); + ret = 0; +failed_to_submit_write: +failed_to_write: +invalid_header: + __free_page(page); +failed_to_submit_read: + /* Technically, we'll leak a page with the pending bio, but + * we're about to reboot anyway. + */ +failed_to_alloc_page: + bio_put(bio); +failed_bio_alloc: + if (dev_mode) + blkdev_put(bdev, dev_mode); +failed_to_read: + return ret; +} + +void dm_verity_avb_error_handler(void) +{ + dev_t dev; + + DMINFO("AVB error handler called for %s", avb_vbmeta_device); + + if (strcmp(avb_invalidate_on_error, "yes") != 0) { + DMINFO("Not configured to invalidate"); + return; + } + + if (avb_vbmeta_device[0] == '\0') { + DMERR("avb_vbmeta_device parameter not set"); + goto fail_no_dev; + } + + dev = name_to_dev_t(avb_vbmeta_device); + if (!dev) { + DMERR("No matching partition for device: %s", + avb_vbmeta_device); + goto fail_no_dev; + } + + invalidate_vbmeta(dev); + +fail_no_dev: + ; +} + +static int __init dm_verity_avb_init(void) +{ + DMINFO("AVB error handler initialized with vbmeta device: %s", + avb_vbmeta_device); + return 0; +} + +static void __exit dm_verity_avb_exit(void) +{ +} + +module_init(dm_verity_avb_init); +module_exit(dm_verity_avb_exit); + +MODULE_AUTHOR("David Zeuthen "); +MODULE_DESCRIPTION("AVB-specific error handler for dm-verity"); +MODULE_LICENSE("GPL"); + +/* Declare parameter with no module prefix */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "androidboot.vbmeta." +module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); +module_param_string(invalidate_on_error, avb_invalidate_on_error, + sizeof(avb_invalidate_on_error), 0); diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 1a9f464d15c7..ea131ea0d8ea 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -251,8 +251,12 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, if (v->mode == DM_VERITY_MODE_LOGGING) return 0; - if (v->mode == DM_VERITY_MODE_RESTART) + if (v->mode == DM_VERITY_MODE_RESTART) { +#ifdef CONFIG_DM_VERITY_AVB + dm_verity_avb_error_handler(); +#endif kernel_restart("dm-verity device corrupted"); + } return 1; } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 0f634e49f63b..233cc99d440d 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); extern void verity_dtr(struct dm_target *ti); extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); extern int verity_map(struct dm_target *ti, struct bio *bio); +extern void dm_verity_avb_error_handler(void); #endif /* DM_VERITY_H */ From 249d16c742ea7a9e3ff05ef26c61171c7c695f94 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Thu, 23 Aug 2018 16:26:51 -0700 Subject: [PATCH 0684/1276] ANDROID: x86_64_cuttlefish_defconfig: Enable zram and zstd Signed-off-by: Peter Kalauskas Bug: 112488418 Change-Id: Ib4c5d7d9145c0258a956ce0af70d7924f3f2fd96 --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 19e3a812306b..761739a11425 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -52,6 +52,7 @@ CONFIG_X86_CPUID=y CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_ZSMALLOC=y # CONFIG_MTRR is not set CONFIG_HZ_100=y CONFIG_KEXEC=y @@ -198,6 +199,7 @@ CONFIG_DEBUG_DEVRES=y CONFIG_OF=y CONFIG_OF_UNITTEST=y # CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 @@ -452,6 +454,7 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_DEV_VIRTIO=y CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y From 306ed4d9cdfa922d23c5136559d84b9338774e24 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Fri, 24 Aug 2018 12:27:25 -0700 Subject: [PATCH 0685/1276] ANDROID: x86_64_cuttlefish_defconfig: Enable lz4 compression for zram Signed-off-by: Peter Kalauskas Bug: 112488418 Change-Id: I999c604d85f3a96bea97829b98101ea865a23275 --- arch/x86/configs/x86_64_cuttlefish_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 761739a11425..db63c91b57b7 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -454,6 +454,7 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_LZ4=y CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_DEV_VIRTIO=y CONFIG_ASYMMETRIC_KEY_TYPE=y From f4bb220e097d6a932e67c98b2644d6ae9255ba6e Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Sat, 25 Aug 2018 13:50:56 -0700 Subject: [PATCH 0686/1276] FROMLIST: ANDROID: binder: Add BINDER_GET_NODE_INFO_FOR_REF ioctl. This allows the context manager to retrieve information about nodes that it holds a reference to, such as the current number of references to those nodes. Such information can for example be used to determine whether the servicemanager is the only process holding a reference to a node. This information can then be passed on to the process holding the node, which can in turn decide whether it wants to shut down to reduce resource usage. Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 55 +++++++++++++++++++++++++++++ include/uapi/linux/android/binder.h | 10 ++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0d475a17089b..c0cd8b65fdf5 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -4715,6 +4715,42 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) return ret; } +static int binder_ioctl_get_node_info_for_ref(struct binder_proc *proc, + struct binder_node_info_for_ref *info) +{ + struct binder_node *node; + struct binder_context *context = proc->context; + __u32 handle = info->handle; + + if (info->strong_count || info->weak_count || info->reserved1 || + info->reserved2 || info->reserved3) { + binder_user_error("%d BINDER_GET_NODE_INFO_FOR_REF: only handle may be non-zero.", + proc->pid); + return -EINVAL; + } + + /* This ioctl may only be used by the context manager */ + mutex_lock(&context->context_mgr_node_lock); + if (!context->binder_context_mgr_node || + context->binder_context_mgr_node->proc != proc) { + mutex_unlock(&context->context_mgr_node_lock); + return -EPERM; + } + mutex_unlock(&context->context_mgr_node_lock); + + node = binder_get_node_from_ref(proc, handle, true, NULL); + if (!node) + return -EINVAL; + + info->strong_count = node->local_strong_refs + + node->internal_strong_refs; + info->weak_count = node->local_weak_refs; + + binder_put_node(node); + + return 0; +} + static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, struct binder_node_debug_info *info) { @@ -4809,6 +4845,25 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } + case BINDER_GET_NODE_INFO_FOR_REF: { + struct binder_node_info_for_ref info; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + ret = binder_ioctl_get_node_info_for_ref(proc, &info); + if (ret < 0) + goto err; + + if (copy_to_user(ubuf, &info, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + break; + } case BINDER_GET_NODE_DEBUG_INFO: { struct binder_node_debug_info info; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index b4723e36b6cf..4a1c285b97b1 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -247,6 +247,15 @@ struct binder_node_debug_info { __u32 has_weak_ref; }; +struct binder_node_info_for_ref { + __u32 handle; + __u32 strong_count; + __u32 weak_count; + __u32 reserved1; + __u32 reserved2; + __u32 reserved3; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -255,6 +264,7 @@ struct binder_node_debug_info { #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) +#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) /* * NOTE: Two special error codes you should check for when calling From 6edd721e972c634a1b9a1157ade2b692372195d3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 19 Jul 2018 18:08:35 -0700 Subject: [PATCH 0687/1276] ANDROID: sdcardfs: Don't use OVERRIDE_CRED macro The macro hides some control flow, making it easier to run into bugs. bug: 111642636 Change-Id: I37ec207c277d97c4e7f1e8381bc9ae743ad78435 Reported-by: Jann Horn Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/file.c | 24 +++-- fs/sdcardfs/inode.c | 198 +++++++++-------------------------------- fs/sdcardfs/lookup.c | 9 +- fs/sdcardfs/sdcardfs.h | 25 ------ 4 files changed, 66 insertions(+), 190 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1461254f301d..271c4c4cb760 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -118,7 +118,11 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); + if (!saved_cred) { + err = -ENOMEM; + goto out; + } if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); @@ -127,7 +131,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (!err) sdcardfs_copy_and_fix_attrs(file_inode(file), file_inode(lower_file)); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out: return err; } @@ -149,12 +153,16 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); + if (!saved_cred) { + err = -ENOMEM; + goto out; + } if (lower_file->f_op->compat_ioctl) err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out: return err; } @@ -241,7 +249,11 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data); + if (!saved_cred) { + err = -ENOMEM; + goto out_err; + } file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); @@ -271,7 +283,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); out_revert_cred: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: dput(parent); return err; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2de5a4dffa22..7fc2f083aaad 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,7 +22,6 @@ #include #include -/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) { @@ -50,7 +49,6 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, return old_cred; } -/* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred) { const struct cred *cur_cred; @@ -78,7 +76,10 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -115,53 +116,11 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_dir_dentry; - u64 file_size_save; - int err; - struct path lower_old_path, lower_new_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - file_size_save = i_size_read(d_inode(old_dentry)); - sdcardfs_get_lower_path(old_dentry, &lower_old_path); - sdcardfs_get_lower_path(new_dentry, &lower_new_path); - lower_old_dentry = lower_old_path.dentry; - lower_new_dentry = lower_new_path.dentry; - lower_dir_dentry = lock_parent(lower_new_dentry); - - err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), - lower_new_dentry, NULL); - if (err || !d_inode(lower_new_dentry)) - goto out; - - err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); - fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); - set_nlink(d_inode(old_dentry), - sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); - i_size_write(d_inode(new_dentry), file_size_save); -out: - unlock_dir(lower_dir_dentry); - sdcardfs_put_lower_path(old_dentry, &lower_old_path); - sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(); - return err; -} -#endif - static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; @@ -178,7 +137,10 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -209,43 +171,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); - if (err) - goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - static int touch(char *abs_path, mode_t mode) { struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); @@ -287,7 +217,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* check disk space */ parent_dentry = dget_parent(dentry); @@ -366,13 +299,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if (make_nomedia_in_obb || ((pd->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { - REVERT_CRED(saved_cred); - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); + revert_fsids(saved_cred); + saved_cred = override_fsids(sbi, + SDCARDFS_I(d_inode(dentry))->data); + if (!saved_cred) { + pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, + -ENOMEM); + goto out; + } set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", - lower_path.dentry->d_name.name, touch_err); + lower_path.dentry->d_name.name, + touch_err); goto out; } } @@ -382,7 +323,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -402,7 +343,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. @@ -427,44 +371,11 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) out: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); - if (err) - goto out; - - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - /* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. @@ -493,7 +404,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); + saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb), + SDCARDFS_I(new_dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -540,7 +454,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, dput(lower_new_dir_dentry); sdcardfs_put_real_lower(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -660,33 +574,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); - /* XXX - * Original sdcardfs code calls inode_permission(lower_inode,.. ) - * for checking inode permission. But doing such things here seems - * duplicated work, because the functions called after this func, - * such as vfs_create, vfs_unlink, vfs_rename, and etc, - * does exactly same thing, i.e., they calls inode_permission(). - * So we just let they do the things. - * If there are any security hole, just uncomment following if block. - */ -#if 0 - if (!err) { - /* - * Permission check on lower_inode(=EXT4). - * we check it with AID_MEDIA_RW permission - */ - struct inode *lower_inode; - - OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); - - lower_inode = sdcardfs_lower_inode(inode); - err = inode_permission(lower_inode, mask); - - REVERT_CRED(); - } -#endif return err; - } static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) @@ -764,7 +652,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct goto out_err; /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb), + SDCARDFS_I(inode)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -823,7 +714,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct out: sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: return err; } @@ -907,13 +798,6 @@ const struct inode_operations sdcardfs_dir_iops = { .setattr = sdcardfs_setattr_wrn, .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, - /* XXX Following operations are implemented, - * but FUSE(sdcard) or FAT does not support them - * These methods are *NOT* perfectly tested. - .symlink = sdcardfs_symlink, - .link = sdcardfs_link, - .mknod = sdcardfs_mknod, - */ }; const struct inode_operations sdcardfs_main_iops = { diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 2ff305161882..73179ce2591f 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -427,7 +427,12 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) { + ret = ERR_PTR(-ENOMEM); + goto out_err; + } sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -458,7 +463,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, out: sdcardfs_put_lower_path(parent, &lower_parent_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: dput(parent); return ret; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 826afb5c7e88..ec2290a16d3c 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -88,31 +88,6 @@ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) -/* OVERRIDE_CRED() and REVERT_CRED() - * OVERRIDE_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. - * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. - * These two macro should be used in pair, and OVERRIDE_CRED() should be - * placed at the beginning of a function, right after variable declaration. - */ -#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ - do { \ - saved_cred = override_fsids(sdcardfs_sbi, info->data); \ - if (!saved_cred) \ - return -ENOMEM; \ - } while (0) - -#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ - do { \ - saved_cred = override_fsids(sdcardfs_sbi, info->data); \ - if (!saved_cred) \ - return ERR_PTR(-ENOMEM); \ - } while (0) - -#define REVERT_CRED(saved_cred) revert_fsids(saved_cred) - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions From 11ca578b43363d3f9cdf6fa9261d49bc5e12161f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 20 Jul 2018 16:11:40 -0700 Subject: [PATCH 0688/1276] ANDROID: sdcardfs: Change current->fs under lock Adjusted from previous version to add missing include bug: 111641492 Change-Id: I321d83f5d599efb3abdfaf2f3a4900ac512beca6 Reported-by: Jann Horn Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 7fc2f083aaad..4dd681e0d59d 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -21,6 +21,7 @@ #include "sdcardfs.h" #include #include +#include const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) @@ -96,8 +97,11 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, err = -ENOMEM; goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -111,7 +115,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fixup_lower_ownership(dentry, dentry->d_name.name); out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); free_fs_struct(copied_fs); out_unlock: unlock_dir(lower_parent_dentry); @@ -249,8 +255,11 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode unlock_dir(lower_parent_dentry); goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); if (err) { @@ -318,7 +327,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } } out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); + free_fs_struct(copied_fs); out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); From 9c38cec637334ecc1b7e55b764d3b51a340c4a59 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Tue, 16 Oct 2018 11:23:05 -0700 Subject: [PATCH 0689/1276] ANDROID: x86_64_cuttlefish_defconfig: disable CONFIG_MEMORY_STATE_TIME Bug: 117847156 Change-Id: Idfbac9c1f0dc2617642c30ddb65400083da44b49 Signed-off-by: Steve Muckle --- arch/x86/configs/x86_64_cuttlefish_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index db63c91b57b7..6425593f7aec 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -205,7 +205,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_VIRTIO_BLK=y CONFIG_UID_SYS_STATS=y -CONFIG_MEMORY_STATE_TIME=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y From 9dbea0e201351c6dde6e24561f49278ed9e7333f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 4 Oct 2018 14:04:02 +0200 Subject: [PATCH 0690/1276] FROMLIST: sched: Factor out nr_iowait and nr_iowait_cpu https://lore.kernel.org/patchwork/patch/995380/ The function nr_iowait_cpu() can be used directly by nr_iowait() instead of duplicating code. Call nr_iowait_cpu() from nr_iowait() Reviewed-by: Rafael J. Wysocki Signed-off-by: Daniel Lezcano Acked-by: Peter Zijlstra (Intel) Signed-off-by: Amit Pundir --- kernel/sched/core.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ad97f3ba5ec5..6e7ff7e6205b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2874,6 +2874,25 @@ unsigned long long nr_context_switches(void) return sum; } +/* + * Consumers of these two interfaces, like for example the cpufreq menu + * governor are using nonsensical data. Boosting frequency for a CPU that has + * IO-wait which might not even end up running the task when it does become + * runnable. + */ + +unsigned long nr_iowait_cpu(int cpu) +{ + return atomic_read(&cpu_rq(cpu)->nr_iowait); +} + +void get_iowait_load(unsigned long *nr_waiters, unsigned long *load) +{ + struct rq *rq = this_rq(); + *nr_waiters = atomic_read(&rq->nr_iowait); + *load = rq->load.weight; +} + /* * IO-wait accounting, and how its mostly bollocks (on SMP). * @@ -2909,31 +2928,11 @@ unsigned long nr_iowait(void) unsigned long i, sum = 0; for_each_possible_cpu(i) - sum += atomic_read(&cpu_rq(i)->nr_iowait); + sum += nr_iowait_cpu(i); return sum; } -/* - * Consumers of these two interfaces, like for example the cpufreq menu - * governor are using nonsensical data. Boosting frequency for a CPU that has - * IO-wait which might not even end up running the task when it does become - * runnable. - */ - -unsigned long nr_iowait_cpu(int cpu) -{ - struct rq *this = cpu_rq(cpu); - return atomic_read(&this->nr_iowait); -} - -void get_iowait_load(unsigned long *nr_waiters, unsigned long *load) -{ - struct rq *rq = this_rq(); - *nr_waiters = atomic_read(&rq->nr_iowait); - *load = rq->load.weight; -} - #ifdef CONFIG_SMP /* From dfe442d1f4f17ed69b6bd31b717a54c0a8daaeb7 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 4 Oct 2018 14:04:03 +0200 Subject: [PATCH 0691/1276] FROMLIST: cpuidle/drivers/menu: Remove get_loadavg in the performance multiplier https://lore.kernel.org/patchwork/patch/995381/ The function get_loadavg() returns almost always zero. To be more precise, statistically speaking for a total of 1023379 times passing in the function, the load is equal to zero 1020728 times, greater than 100, 610 times, the remaining is between 0 and 5. In 2011, the get_loadavg() was removed from the Android tree because of the above [1]. At this time, the load was: unsigned long this_cpu_load(void) { struct rq *this = this_rq(); return this->cpu_load[0]; } In 2014, the code was changed by commit 372ba8cb46b2 (cpuidle: menu: Lookup CPU runqueues less) and the load is: void get_iowait_load(unsigned long *nr_waiters, unsigned long *load) { struct rq *rq = this_rq(); *nr_waiters = atomic_read(&rq->nr_iowait); *load = rq->load.weight; } with the same result. Both measurements show using the load in this code path does no matter anymore. Removing it. [1] https://android.googlesource.com/kernel/common/+/4dedd9f124703207895777ac6e91dacde0f7cc17 Cc: Peter Zijlstra Cc: Todd Kjos Cc: Joel Fernandes Cc: Colin Cross Cc: Ramesh Thomas Cc: Mel Gorman Signed-off-by: Daniel Lezcano Acked-by: Mel Gorman Reviewed-by: Rafael J. Wysocki Acked-by: Peter Zijlstra (Intel) Signed-off-by: Amit Pundir --- drivers/cpuidle/governors/menu.c | 26 +++++++------------------- include/linux/sched/stat.h | 1 - kernel/sched/core.c | 7 ------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index e26a40971b26..066b01f8ae00 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -135,11 +135,6 @@ struct menu_device { #define LOAD_INT(x) ((x) >> FSHIFT) #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) -static inline int get_loadavg(unsigned long load) -{ - return LOAD_INT(load) * 10 + LOAD_FRAC(load) / 10; -} - static inline int which_bucket(unsigned int duration, unsigned long nr_iowaiters) { int bucket = 0; @@ -173,18 +168,10 @@ static inline int which_bucket(unsigned int duration, unsigned long nr_iowaiters * to be, the higher this multiplier, and thus the higher * the barrier to go to an expensive C state. */ -static inline int performance_multiplier(unsigned long nr_iowaiters, unsigned long load) +static inline int performance_multiplier(unsigned long nr_iowaiters) { - int mult = 1; - - /* for higher loadavg, we are more reluctant */ - - mult += 2 * get_loadavg(load); - - /* for IO wait tasks (per cpu!) we add 5x each */ - mult += 10 * nr_iowaiters; - - return mult; + /* for IO wait tasks (per cpu!) we add 10x each */ + return 1 + 10 * nr_iowaiters; } static DEFINE_PER_CPU(struct menu_device, menu_devices); @@ -290,7 +277,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, int idx; unsigned int interactivity_req; unsigned int expected_interval; - unsigned long nr_iowaiters, cpu_load; + unsigned long nr_iowaiters; ktime_t delta_next; if (data->needs_update) { @@ -307,7 +294,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, /* determine the expected residency time, round up */ data->next_timer_us = ktime_to_us(tick_nohz_get_sleep_length(&delta_next)); - get_iowait_load(&nr_iowaiters, &cpu_load); + nr_iowaiters = nr_iowait_cpu(dev->cpu); data->bucket = which_bucket(data->next_timer_us, nr_iowaiters); /* @@ -359,7 +346,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * Use the performance multiplier and the user-configurable * latency_req to determine the maximum exit latency. */ - interactivity_req = data->predicted_us / performance_multiplier(nr_iowaiters, cpu_load); + interactivity_req = data->predicted_us / + performance_multiplier(nr_iowaiters); if (latency_req > interactivity_req) latency_req = interactivity_req; } diff --git a/include/linux/sched/stat.h b/include/linux/sched/stat.h index 04f1321d14c4..f30954cc059d 100644 --- a/include/linux/sched/stat.h +++ b/include/linux/sched/stat.h @@ -20,7 +20,6 @@ extern unsigned long nr_running(void); extern bool single_task_running(void); extern unsigned long nr_iowait(void); extern unsigned long nr_iowait_cpu(int cpu); -extern void get_iowait_load(unsigned long *nr_waiters, unsigned long *load); static inline int sched_info_on(void) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6e7ff7e6205b..1d2db12cbab6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2886,13 +2886,6 @@ unsigned long nr_iowait_cpu(int cpu) return atomic_read(&cpu_rq(cpu)->nr_iowait); } -void get_iowait_load(unsigned long *nr_waiters, unsigned long *load) -{ - struct rq *rq = this_rq(); - *nr_waiters = atomic_read(&rq->nr_iowait); - *load = rq->load.weight; -} - /* * IO-wait accounting, and how its mostly bollocks (on SMP). * From abc55cad37aba67d9555521e0adb87c60560476d Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 4 May 2018 00:08:53 +0530 Subject: [PATCH 0692/1276] FROMLIST: NFC: st21nfca: Fix out of bounds kernel access when handling ATR_REQ https://patchwork.kernel.org/patch/10378899/ Out of bounds kernel accesses in st21nfca's NFC HCI layer might happen when handling ATR_REQ events if user-specified atr_req->length is bigger than the buffer size. In that case memcpy() inside st21nfca_tm_send_atr_res() will read extra bytes resulting in OOB read from the kernel heap. cc: Stable Signed-off-by: Suren Baghdasaryan Signed-off-by: Amit Pundir Reviewed-by: Andy Shevchenko --- drivers/nfc/st21nfca/dep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c index fd08be2917e6..3420c5104c94 100644 --- a/drivers/nfc/st21nfca/dep.c +++ b/drivers/nfc/st21nfca/dep.c @@ -217,7 +217,8 @@ static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev, atr_req = (struct st21nfca_atr_req *)skb->data; - if (atr_req->length < sizeof(struct st21nfca_atr_req)) { + if (atr_req->length < sizeof(struct st21nfca_atr_req) || + atr_req->length > skb->len) { r = -EPROTO; goto exit; } From 6097899b5547f8e6035a2bb48826b5c9dbe48bca Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 4 May 2018 00:08:55 +0530 Subject: [PATCH 0693/1276] FROMLIST: NFC: fdp: Fix possible buffer overflow in WCS4000 NFC driver https://patchwork.kernel.org/patch/10378897/ Possible buffer overflow when reading next_read_size bytes into tmp buffer after next_read_size was extracted from a previous packet. cc: Stable Signed-off-by: Suren Baghdasaryan Signed-off-by: Amit Pundir Reviewed-by: Andy Shevchenko --- drivers/nfc/fdp/i2c.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index d8d70dd830b0..6bdf607e3d6e 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -176,6 +176,15 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb) /* Packet that contains a length */ if (tmp[0] == 0 && tmp[1] == 0) { phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3; + /* + * Ensure next_read_size does not exceed sizeof(tmp) + * for reading that many bytes during next iteration + */ + if (phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) { + dev_dbg(&client->dev, "corrupted packet\n"); + phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; + goto flush; + } } else { phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; From 02a8468b6d94a14f2d936f53d740d70825180d50 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 4 May 2018 00:08:56 +0530 Subject: [PATCH 0694/1276] FROMLIST: NFC: fdp: Remove __func__ from dev_dbg() https://patchwork.kernel.org/patch/10378891/ Remove redundant __func__ parameter from dev_dgb() calls and delete empty dev_dbg() trace calls, which are redundant if function tracer is enabled. Signed-off-by: Amit Pundir Reviewed-by: Andy Shevchenko --- drivers/nfc/fdp/fdp.c | 18 +++--------------- drivers/nfc/fdp/i2c.c | 17 ++++------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index d5784a47fc13..f64a6fd65c41 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -249,8 +249,6 @@ static int fdp_nci_open(struct nci_dev *ndev) struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); - r = info->phy_ops->enable(info->phy); return r; @@ -261,7 +259,6 @@ static int fdp_nci_close(struct nci_dev *ndev) struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); return 0; } @@ -270,8 +267,6 @@ static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); - if (atomic_dec_and_test(&info->data_pkt_counter)) info->data_pkt_counter_cb(ndev); @@ -283,7 +278,6 @@ int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); return nci_recv_frame(ndev, skb); } EXPORT_SYMBOL(fdp_nci_recv_frame); @@ -498,8 +492,6 @@ static int fdp_nci_setup(struct nci_dev *ndev) int r; u8 patched = 0; - dev_dbg(dev, "%s\n", __func__); - r = nci_core_init(ndev); if (r) goto error; @@ -609,7 +601,6 @@ static int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev, struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); info->setup_reset_ntf = 1; wake_up(&info->setup_wq); @@ -622,7 +613,6 @@ static int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev, struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); info->setup_patch_ntf = 1; info->setup_patch_status = skb->data[0]; wake_up(&info->setup_wq); @@ -637,7 +627,7 @@ static int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev, struct device *dev = &info->phy->i2c_dev->dev; u8 status = skb->data[0]; - dev_dbg(dev, "%s: status 0x%x\n", __func__, status); + dev_dbg(dev, "status 0x%x\n", status); nci_req_complete(ndev, status); return 0; @@ -650,7 +640,7 @@ static int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev, struct device *dev = &info->phy->i2c_dev->dev; u8 status = skb->data[0]; - dev_dbg(dev, "%s: status 0x%x\n", __func__, status); + dev_dbg(dev, "status 0x%x\n", status); nci_req_complete(ndev, status); return 0; @@ -695,7 +685,7 @@ static int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev, dev_dbg(dev, "OTP version %d\n", info->otp_version); dev_dbg(dev, "RAM version %d\n", info->ram_version); dev_dbg(dev, "key index %d\n", info->key_index); - dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status); + dev_dbg(dev, "status 0x%x\n", rsp->status); nci_req_complete(ndev, rsp->status); @@ -798,8 +788,6 @@ void fdp_nci_remove(struct nci_dev *ndev) struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - dev_dbg(dev, "%s\n", __func__); - nci_unregister_device(ndev); nci_free_device(ndev); } diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 6bdf607e3d6e..70a970c7375c 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -57,7 +57,6 @@ static int fdp_nci_i2c_enable(void *phy_id) { struct fdp_i2c_phy *phy = phy_id; - dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__); fdp_nci_i2c_reset(phy); return 0; @@ -67,7 +66,6 @@ static void fdp_nci_i2c_disable(void *phy_id) { struct fdp_i2c_phy *phy = phy_id; - dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__); fdp_nci_i2c_reset(phy); } @@ -113,8 +111,8 @@ static int fdp_nci_i2c_write(void *phy_id, struct sk_buff *skb) } if (r < 0 || r != skb->len) - dev_dbg(&client->dev, "%s: error err=%d len=%d\n", - __func__, r, skb->len); + dev_dbg(&client->dev, "error err=%d len=%d\n", + r, skb->len); if (r >= 0) { if (r != skb->len) { @@ -152,8 +150,7 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb) r = i2c_master_recv(client, tmp, len); if (r != len) { - dev_dbg(&client->dev, "%s: i2c recv err: %d\n", - __func__, r); + dev_dbg(&client->dev, "i2c recv err: %d\n", r); goto flush; } @@ -167,8 +164,7 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb) * and force resynchronization */ if (lrc) { - dev_dbg(&client->dev, "%s: corrupted packet\n", - __func__); + dev_dbg(&client->dev, "corrupted packet\n"); phy->next_read_size = 5; goto flush; } @@ -224,7 +220,6 @@ static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id) } client = phy->i2c_dev; - dev_dbg(&client->dev, "%s\n", __func__); r = fdp_nci_i2c_read(phy, &skb); @@ -305,8 +300,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client) u32 clock_freq; int r = 0; - dev_dbg(dev, "%s\n", __func__); - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { nfc_err(dev, "No I2C_FUNC_I2C support\n"); return -ENODEV; @@ -368,8 +361,6 @@ static int fdp_nci_i2c_remove(struct i2c_client *client) { struct fdp_i2c_phy *phy = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __func__); - fdp_nci_remove(phy->ndev); fdp_nci_i2c_disable(phy); From 4f7a8ef9f2c4965ce0678103ae949e9801914eaa Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 12 Jun 2018 19:44:10 +0530 Subject: [PATCH 0695/1276] FROMLIST: NFC: fdp: Fix unused variable warnings https://patchwork.kernel.org/patch/10460429/ Fix unused variable warnings reported on next-20180612. Fixes: 7579d009c4a1 ("NFC: fdp: Remove __func__ from dev_dbg()") Signed-off-by: Amit Pundir --- drivers/nfc/fdp/fdp.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index f64a6fd65c41..f47de8427b13 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -247,7 +247,6 @@ static int fdp_nci_open(struct nci_dev *ndev) { int r; struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; r = info->phy_ops->enable(info->phy); @@ -256,16 +255,12 @@ static int fdp_nci_open(struct nci_dev *ndev) static int fdp_nci_close(struct nci_dev *ndev) { - struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; - return 0; } static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) { struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; if (atomic_dec_and_test(&info->data_pkt_counter)) info->data_pkt_counter_cb(ndev); @@ -275,9 +270,6 @@ static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) int fdp_nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) { - struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; - return nci_recv_frame(ndev, skb); } EXPORT_SYMBOL(fdp_nci_recv_frame); @@ -599,7 +591,6 @@ static int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; info->setup_reset_ntf = 1; wake_up(&info->setup_wq); @@ -611,7 +602,6 @@ static int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; info->setup_patch_ntf = 1; info->setup_patch_status = skb->data[0]; @@ -785,9 +775,6 @@ EXPORT_SYMBOL(fdp_nci_probe); void fdp_nci_remove(struct nci_dev *ndev) { - struct fdp_nci_info *info = nci_get_drvdata(ndev); - struct device *dev = &info->phy->i2c_dev->dev; - nci_unregister_device(ndev); nci_free_device(ndev); } From 24c1c53bbdd63be19db830f136db8586a0920f24 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Jan 2018 22:51:52 +0530 Subject: [PATCH 0696/1276] ANDROID: net: ipv4: tcp: Namespace-ify sysctl_tcp_default_init_rwnd Make default TCP initial rwnd control to a per namespace value. Refactoring Change-Id I386b2a9d62de0ebe05c1ebe1b4bd91b314af5c54 ("ANDROID: net: ipv4: tcp: add a sysctl to config the tcp_default_init_rwnd"), as per similar upstream commits. Signed-off-by: Amit Pundir --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 2 -- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 1 - net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e47503b4e4d1..9b8ab971ad93 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -159,6 +159,7 @@ struct netns_ipv4 { int sysctl_tcp_invalid_ratelimit; int sysctl_tcp_pacing_ss_ratio; int sysctl_tcp_pacing_ca_ratio; + int sysctl_tcp_default_init_rwnd; int sysctl_tcp_wmem[3]; int sysctl_tcp_rmem[3]; int sysctl_tcp_comp_sack_nr; diff --git a/include/net/tcp.h b/include/net/tcp.h index 87ed93f14c52..770917d0caa7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -248,8 +248,6 @@ extern long sysctl_tcp_mem[3]; #define TCP_RACK_STATIC_REO_WND 0x2 /* Use static RACK reo wnd */ #define TCP_RACK_NO_DUPTHRESH 0x4 /* Do not use DUPACK threshold in RACK */ -extern int sysctl_tcp_default_init_rwnd; - extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; extern unsigned long tcp_memory_pressure; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index bad5a38de987..314ea3159cec 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -541,13 +541,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0444, .proc_handler = proc_tcp_available_ulp, }, - { - .procname = "tcp_default_init_rwnd", - .data = &sysctl_tcp_default_init_rwnd, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_tcp_default_init_rwnd - }, { .procname = "icmp_msgs_per_sec", .data = &sysctl_icmp_msgs_per_sec, @@ -1198,6 +1191,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &thousand, }, + { + .procname = "tcp_default_init_rwnd", + .data = &init_net.ipv4.sysctl_tcp_default_init_rwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_default_init_rwnd + }, { .procname = "tcp_wmem", .data = &init_net.ipv4.sysctl_tcp_wmem, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88db2d6f70aa..47e08c1b5bc3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,7 +81,6 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; -int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index cd426313a298..0c65fc967014 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2583,6 +2583,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; net->ipv4.sysctl_tcp_pacing_ss_ratio = 200; net->ipv4.sysctl_tcp_pacing_ca_ratio = 120; + net->ipv4.sysctl_tcp_default_init_rwnd = TCP_INIT_CWND * 2; if (net != &init_net) { memcpy(net->ipv4.sysctl_tcp_rmem, init_net.ipv4.sysctl_tcp_rmem, From bad9354ee30068fd78f09390fd41a93d6927ea1e Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Jan 2018 22:55:09 +0530 Subject: [PATCH 0697/1276] ANDROID: net: ipv4: sysfs_net_ipv4: Fix TCP window size controlling knobs Refactor /sys/kernel/ipv4/tcp_* send/receive window size controlling knobs to align with the changes of upstream commit 356d1833b638 ("tcp: Namespace-ify sysctl_tcp_rmem and sysctl_tcp_wmem"). Fixes: ("ANDROID: net: ipv4: sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size") Signed-off-by: Amit Pundir --- include/net/tcp.h | 2 +- net/ipv4/sysfs_net_ipv4.c | 12 ++++++------ net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 770917d0caa7..a709723b4e65 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1315,7 +1315,7 @@ static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) rx_opt->num_sacks = 0; } -u32 tcp_default_init_rwnd(u32 mss); +u32 tcp_default_init_rwnd(const struct sock *sk, u32 mss); void tcp_cwnd_restart(struct sock *sk, s32 delta); static inline void tcp_slow_start_after_idle_check(struct sock *sk) diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c index 0cbbf10026a6..35a651aaee47 100644 --- a/net/ipv4/sysfs_net_ipv4.c +++ b/net/ipv4/sysfs_net_ipv4.c @@ -45,13 +45,13 @@ static ssize_t _name##_store(struct kobject *kobj, \ static struct kobj_attribute _name##_attr = \ __ATTR(_name, 0644, _name##_show, _name##_store) -CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); -CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); -CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); +CREATE_IPV4_FILE(tcp_wmem_min, init_net.ipv4.sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, init_net.ipv4.sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, init_net.ipv4.sysctl_tcp_wmem[2]); -CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); -CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); -CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); +CREATE_IPV4_FILE(tcp_rmem_min, init_net.ipv4.sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, init_net.ipv4.sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, init_net.ipv4.sysctl_tcp_rmem[2]); static struct attribute *ipv4_attrs[] = { &tcp_wmem_min_attr.attr, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 47e08c1b5bc3..83cadba78b7d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -433,7 +433,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) int rcvmem; rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER) * - tcp_default_init_rwnd(mss); + tcp_default_init_rwnd(sk, mss); /* Dynamic Right Sizing (DRS) has 2 to 3 RTT latency * Allow enough cushion so that sender is not limited by our window diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 20abda1b6ff1..178ddcb87aec 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -180,14 +180,14 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts, } -u32 tcp_default_init_rwnd(u32 mss) +u32 tcp_default_init_rwnd(const struct sock *sk, u32 mss) { /* Initial receive window should be twice of TCP_INIT_CWND to * enable proper sending of new unsent data during fast recovery * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ - u32 init_rwnd = sysctl_tcp_default_init_rwnd; + u32 init_rwnd = sock_net(sk)->ipv4.sysctl_tcp_default_init_rwnd; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); @@ -243,7 +243,7 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss, } if (!init_rcv_wnd) /* Use default unless specified otherwise */ - init_rcv_wnd = tcp_default_init_rwnd(mss); + init_rcv_wnd = tcp_default_init_rwnd(sk, mss); *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss); /* Set the clamp no higher than max representable value */ From 83dea6ba6ea71d503b7ff5e6556e0a6387b332a9 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 9 Mar 2018 19:51:44 +0530 Subject: [PATCH 0698/1276] ANDROID: sdcardfs: Use inode iversion helpers Commit f02a9ad1f15d ("fs: handle inode->i_version more efficiently") converted the inode -> i_version counter to an atomic64_t. Hence switch to using relevant iversion helper routines for basic operations instead. Signed-off-by: Amit Pundir --- fs/sdcardfs/lookup.c | 2 +- fs/sdcardfs/sdcardfs.h | 1 + fs/sdcardfs/super.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 73179ce2591f..a5c9686090e0 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -121,7 +121,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u inode->i_ino = lower_inode->i_ino; sdcardfs_set_lower_inode(inode, lower_inode); - inode->i_version++; + inode_inc_iversion_raw(inode); /* use different set of inode ops for symlinks & directories */ if (S_ISDIR(lower_inode->i_mode)) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index ec2290a16d3c..b96f4c3e1f40 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -45,6 +45,7 @@ #include #include #include +#include #include "multiuser.h" /* the file system name */ diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index cffcdb11cb8a..76afaa97e672 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -219,7 +219,7 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) spin_lock_init(&i->top_lock); kref_get(&d->refcount); - i->vfs_inode.i_version = 1; + inode_set_iversion(&i->vfs_inode, 1); return &i->vfs_inode; } From 95d2445cdb0164baf823882e47d675fa492f8c2f Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:31 +0100 Subject: [PATCH 0699/1276] UPSTREAM: sched/topology: Add SD_ASYM_CPUCAPACITY flag detection The SD_ASYM_CPUCAPACITY sched_domain flag is supposed to mark the sched_domain in the hierarchy where all CPU capacities are visible for any CPU's point of view on asymmetric CPU capacity systems. The scheduler can then take to take capacity asymmetry into account when balancing at this level. It also serves as an indicator for how wide task placement heuristics have to search to consider all available CPU capacities as asymmetric systems might often appear symmetric at smallest level(s) of the sched_domain hierarchy. The flag has been around for while but so far only been set by out-of-tree code in Android kernels. One solution is to let each architecture provide the flag through a custom sched_domain topology array and associated mask and flag functions. However, SD_ASYM_CPUCAPACITY is special in the sense that it depends on the capacity and presence of all CPUs in the system, i.e. when hotplugging all CPUs out except those with one particular CPU capacity the flag should disappear even if the sched_domains don't collapse. Similarly, the flag is affected by cpusets where load-balancing is turned off. Detecting when the flags should be set therefore depends not only on topology information but also the cpuset configuration and hotplug state. The arch code doesn't have easy access to the cpuset configuration. Instead, this patch implements the flag detection in generic code where cpusets and hotplug state is already taken care of. All the arch is responsible for is to implement arch_scale_cpu_capacity() and force a full rebuild of the sched_domain hierarchy if capacities are updated, e.g. later in the boot process when cpufreq has initialized. Change-Id: Iaa1723691f11834eb261ccaeb17954e7edad5b32 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-2-git-send-email-morten.rasmussen@arm.com [ Fixed 'CPU' capitalization. ] Signed-off-by: Ingo Molnar (cherry picked from commit 05484e0984487d42e97c417cbb0697fa9d16e7e9 in tip/sched/core) Signed-off-by: Quentin Perret --- include/linux/sched/topology.h | 6 +-- kernel/sched/topology.c | 81 +++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index 26347741ba50..6b9976180c1e 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -23,10 +23,10 @@ #define SD_BALANCE_FORK 0x0008 /* Balance on fork, clone */ #define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */ #define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */ -#define SD_ASYM_CPUCAPACITY 0x0040 /* Groups have different max cpu capacities */ -#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share cpu capacity */ +#define SD_ASYM_CPUCAPACITY 0x0040 /* Domain members have different CPU capacities */ +#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share CPU capacity */ #define SD_SHARE_POWERDOMAIN 0x0100 /* Domain members share power domain */ -#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */ +#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share CPU pkg resources */ #define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */ #define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */ #define SD_PREFER_SIBLING 0x1000 /* Prefer to place tasks in a sibling domain */ diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 505a41c42b96..5c4d583d53ee 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -1061,7 +1061,6 @@ static struct cpumask ***sched_domains_numa_masks; * SD_SHARE_PKG_RESOURCES - describes shared caches * SD_NUMA - describes NUMA topologies * SD_SHARE_POWERDOMAIN - describes shared power domain - * SD_ASYM_CPUCAPACITY - describes mixed capacity topologies * * Odd one out, which beside describing the topology has a quirk also * prescribes the desired behaviour that goes along with it: @@ -1073,13 +1072,12 @@ static struct cpumask ***sched_domains_numa_masks; SD_SHARE_PKG_RESOURCES | \ SD_NUMA | \ SD_ASYM_PACKING | \ - SD_ASYM_CPUCAPACITY | \ SD_SHARE_POWERDOMAIN) static struct sched_domain * sd_init(struct sched_domain_topology_level *tl, const struct cpumask *cpu_map, - struct sched_domain *child, int cpu) + struct sched_domain *child, int dflags, int cpu) { struct sd_data *sdd = &tl->data; struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu); @@ -1100,6 +1098,9 @@ sd_init(struct sched_domain_topology_level *tl, "wrong sd_flags in topology description\n")) sd_flags &= ~TOPOLOGY_SD_FLAGS; + /* Apply detected topology flags */ + sd_flags |= dflags; + *sd = (struct sched_domain){ .min_interval = sd_weight, .max_interval = 2*sd_weight, @@ -1604,9 +1605,9 @@ static void __sdt_free(const struct cpumask *cpu_map) static struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl, const struct cpumask *cpu_map, struct sched_domain_attr *attr, - struct sched_domain *child, int cpu) + struct sched_domain *child, int dflags, int cpu) { - struct sched_domain *sd = sd_init(tl, cpu_map, child, cpu); + struct sched_domain *sd = sd_init(tl, cpu_map, child, dflags, cpu); if (child) { sd->level = child->level + 1; @@ -1632,6 +1633,65 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve return sd; } +/* + * Find the sched_domain_topology_level where all CPU capacities are visible + * for all CPUs. + */ +static struct sched_domain_topology_level +*asym_cpu_capacity_level(const struct cpumask *cpu_map) +{ + int i, j, asym_level = 0; + bool asym = false; + struct sched_domain_topology_level *tl, *asym_tl = NULL; + unsigned long cap; + + /* Is there any asymmetry? */ + cap = arch_scale_cpu_capacity(NULL, cpumask_first(cpu_map)); + + for_each_cpu(i, cpu_map) { + if (arch_scale_cpu_capacity(NULL, i) != cap) { + asym = true; + break; + } + } + + if (!asym) + return NULL; + + /* + * Examine topology from all CPU's point of views to detect the lowest + * sched_domain_topology_level where a highest capacity CPU is visible + * to everyone. + */ + for_each_cpu(i, cpu_map) { + unsigned long max_capacity = arch_scale_cpu_capacity(NULL, i); + int tl_id = 0; + + for_each_sd_topology(tl) { + if (tl_id < asym_level) + goto next_level; + + for_each_cpu_and(j, tl->mask(i), cpu_map) { + unsigned long capacity; + + capacity = arch_scale_cpu_capacity(NULL, j); + + if (capacity <= max_capacity) + continue; + + max_capacity = capacity; + asym_level = tl_id; + asym_tl = tl; + } +next_level: + tl_id++; + } + } + + return asym_tl; +} + + /* * Build sched domains for a given set of CPUs and attach the sched domains * to the individual CPUs @@ -1644,18 +1704,27 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att struct s_data d; struct rq *rq = NULL; int i, ret = -ENOMEM; + struct sched_domain_topology_level *tl_asym; alloc_state = __visit_domain_allocation_hell(&d, cpu_map); if (alloc_state != sa_rootdomain) goto error; + tl_asym = asym_cpu_capacity_level(cpu_map); + /* Set up domains for CPUs specified by the cpu_map: */ for_each_cpu(i, cpu_map) { struct sched_domain_topology_level *tl; sd = NULL; for_each_sd_topology(tl) { - sd = build_sched_domain(tl, cpu_map, attr, sd, i); + int dflags = 0; + + if (tl == tl_asym) + dflags |= SD_ASYM_CPUCAPACITY; + + sd = build_sched_domain(tl, cpu_map, attr, sd, dflags, i); + if (tl == sched_domain_topology) *per_cpu_ptr(d.sd, i) = sd; if (tl->flags & SDTL_OVERLAP) From 98b7f0234ec92695f815bc5e3f9f081da056e83f Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:32 +0100 Subject: [PATCH 0700/1276] UPSTREAM: sched/topology, drivers/base/arch_topology: Rebuild the sched_domain hierarchy when capacities change The setting of SD_ASYM_CPUCAPACITY depends on the per-CPU capacities. These might not have their final values when the hierarchy is initially built as the values depend on cpufreq to be initialized or the values being set through sysfs. To ensure that the flags are set correctly we need to rebuild the sched_domain hierarchy whenever the reported per-CPU capacity (arch_scale_cpu_capacity()) changes. This patch ensure that a full sched_domain rebuild happens when CPU capacity changes occur. Change-Id: I7d65b17bca085312db443513bc813bbc729f3445 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Greg Kroah-Hartman Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-3-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit bb1fbdd3c3fd12b612c7d8cdf13bd6bfeebdefa3 in tip/sched/core) Signed-off-by: Quentin Perret --- drivers/base/arch_topology.c | 26 ++++++++++++++++++++++++++ include/linux/arch_topology.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index e7cb0c6ade81..edfcf8d982e4 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -15,6 +15,7 @@ #include #include #include +#include DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; @@ -47,6 +48,9 @@ static ssize_t cpu_capacity_show(struct device *dev, return sprintf(buf, "%lu\n", topology_get_cpu_scale(NULL, cpu->dev.id)); } +static void update_topology_flags_workfn(struct work_struct *work); +static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn); + static ssize_t cpu_capacity_store(struct device *dev, struct device_attribute *attr, const char *buf, @@ -72,6 +76,8 @@ static ssize_t cpu_capacity_store(struct device *dev, topology_set_cpu_scale(i, new_capacity); mutex_unlock(&cpu_scale_mutex); + schedule_work(&update_topology_flags_work); + return count; } @@ -96,6 +102,25 @@ static int register_cpu_capacity_sysctl(void) } subsys_initcall(register_cpu_capacity_sysctl); +static int update_topology; + +int topology_update_cpu_topology(void) +{ + return update_topology; +} + +/* + * Updating the sched_domains can't be done directly from cpufreq callbacks + * due to locking, so queue the work for later. + */ +static void update_topology_flags_workfn(struct work_struct *work) +{ + update_topology = 1; + rebuild_sched_domains(); + pr_debug("sched_domain hierarchy rebuilt, flags updated\n"); + update_topology = 0; +} + static u32 capacity_scale; static u32 *raw_capacity; @@ -201,6 +226,7 @@ init_cpu_capacity_callback(struct notifier_block *nb, if (cpumask_empty(cpus_to_visit)) { topology_normalize_cpu_scale(); + schedule_work(&update_topology_flags_work); free_raw_capacity(); pr_debug("cpu_capacity: parsing done\n"); schedule_work(&parsing_done_work); diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 2b709416de05..d9bdc1a7f4e7 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -9,6 +9,7 @@ #include void topology_normalize_cpu_scale(void); +int topology_update_cpu_topology(void); struct device_node; bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); From 0cf9c371f86c59f031b0c8cb4ae4dd77149728d5 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:33 +0100 Subject: [PATCH 0701/1276] UPSTREAM: sched/topology, arch/arm64: Rebuild the sched_domain hierarchy when the CPU capacity changes Asymmetric CPU capacity can not necessarily be determined accurately at the time the initial sched_domain hierarchy is built during boot. It is therefore necessary to be able to force a full rebuild of the hierarchy later triggered by the arch_topology driver. A full rebuild requires the arch-code to implement arch_update_cpu_topology() which isn't yet implemented for arm64. This patch points the arm64 implementation to arch_topology driver to ensure that full hierarchy rebuild happens when needed. Change-Id: If0bc210d9e091edbcc698ee4a2726c7fd0a2ec44 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Catalin Marinas Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-4-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 3ba09df4b8b6e3f01ed6381e8fb890840fd0bca3 in tip/sched/core) Signed-off-by: Quentin Perret --- arch/arm64/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 49a0fee4f89b..0524f2438649 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -45,6 +45,9 @@ int pcibus_to_node(struct pci_bus *bus); /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale +/* Enable topology flag updates */ +#define arch_update_cpu_topology topology_update_cpu_topology + #include #endif /* _ASM_ARM_TOPOLOGY_H */ From 69a4e2991bd4b9ddac678d14b540873549aa391c Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:34 +0100 Subject: [PATCH 0702/1276] UPSTREAM: sched/topology, arch/arm: Rebuild sched_domain hierarchy when CPU capacity changes Asymmetric CPU capacity can not necessarily be determined accurately at the time the initial sched_domain hierarchy is built during boot. It is therefore necessary to be able to force a full rebuild of the hierarchy later triggered by the arch_topology driver. A full rebuild requires the arch-code to implement arch_update_cpu_topology() which isn't yet implemented for arm. This patch points the arm implementation to arch_topology driver to ensure that full hierarchy rebuild happens when needed. Change-Id: Id533bbf86e39ff65810e9a5404e70f8abb8f5943 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Russell King Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-5-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e1799a80a4f5a463f252b7325da8bb66dfd55471 in tip/sched/core) Signed-off-by: Quentin Perret --- arch/arm/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h index 5d88d2f22b2c..2a786f54d8b8 100644 --- a/arch/arm/include/asm/topology.h +++ b/arch/arm/include/asm/topology.h @@ -33,6 +33,9 @@ const struct cpumask *cpu_coregroup_mask(int cpu); /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale +/* Enable topology flag updates */ +#define arch_update_cpu_topology topology_update_cpu_topology + #else static inline void init_cpu_topology(void) { } From 1ebfca7aca3713160d62d004858a3de1096b13d4 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:39 +0100 Subject: [PATCH 0703/1276] UPSTREAM: sched/topology: Add static_key for asymmetric CPU capacity optimizations The existing asymmetric CPU capacity code should cause minimal overhead for others. Putting it behind a static_key, it has been done for SMT optimizations, would make it easier to extend and improve without causing harm to others moving forward. Change-Id: Ibed747ab20aeb2338ac2b9766a0105777f277f57 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-2-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit df054e8445a4011e3d693c2268129c0456108663 in tip/sched/core) Signed-off-by: Quentin Perret Change-Id: I09e315ff897ffba65c7cb82715ab0d6c20b76e02 --- kernel/sched/fair.c | 3 +++ kernel/sched/sched.h | 1 + kernel/sched/topology.c | 9 ++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 725488a1f628..d32867242b83 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6281,6 +6281,9 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) { long min_cap, max_cap; + if (!static_branch_unlikely(&sched_asym_cpucapacity)) + return 0; + min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu)); max_cap = cpu_rq(cpu)->rd->max_cpu_capacity; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 9683f458aec7..34f79bbd07a2 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1188,6 +1188,7 @@ DECLARE_PER_CPU(int, sd_llc_id); DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DECLARE_PER_CPU(struct sched_domain *, sd_numa); DECLARE_PER_CPU(struct sched_domain *, sd_asym); +extern struct static_key_false sched_asym_cpucapacity; struct sched_group_capacity { atomic_t ref; diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 5c4d583d53ee..b0cdf5e95bda 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -398,6 +398,7 @@ DEFINE_PER_CPU(int, sd_llc_id); DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DEFINE_PER_CPU(struct sched_domain *, sd_numa); DEFINE_PER_CPU(struct sched_domain *, sd_asym); +DEFINE_STATIC_KEY_FALSE(sched_asym_cpucapacity); static void update_top_cache_domain(int cpu) { @@ -1705,6 +1706,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att struct rq *rq = NULL; int i, ret = -ENOMEM; struct sched_domain_topology_level *tl_asym; + bool has_asym = false; alloc_state = __visit_domain_allocation_hell(&d, cpu_map); if (alloc_state != sa_rootdomain) @@ -1720,8 +1722,10 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att for_each_sd_topology(tl) { int dflags = 0; - if (tl == tl_asym) + if (tl == tl_asym) { dflags |= SD_ASYM_CPUCAPACITY; + has_asym = true; + } sd = build_sched_domain(tl, cpu_map, attr, sd, dflags, i); @@ -1773,6 +1777,9 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att } rcu_read_unlock(); + if (has_asym) + static_branch_enable_cpuslocked(&sched_asym_cpucapacity); + if (rq && sched_debug_enabled) { pr_info("root domain span: %*pbl (max cpu_capacity = %lu)\n", cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity); From a85a7a918079ca5c40f5f4c40046a2e3bd3895d2 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:40 +0100 Subject: [PATCH 0704/1276] UPSTREAM: sched/fair: Add 'group_misfit_task' load-balance type To maximize throughput in systems with asymmetric CPU capacities (e.g. ARM big.LITTLE) load-balancing has to consider task and CPU utilization as well as per-CPU compute capacity when load-balancing in addition to the current average load based load-balancing policy. Tasks with high utilization that are scheduled on a lower capacity CPU need to be identified and migrated to a higher capacity CPU if possible to maximize throughput. To implement this additional policy an additional group_type (load-balance scenario) is added: 'group_misfit_task'. This represents scenarios where a sched_group has one or more tasks that are not suitable for its per-CPU capacity. 'group_misfit_task' is only considered if the system is not overloaded or imbalanced ('group_imbalanced' or 'group_overloaded'). Identifying misfit tasks requires the rq lock to be held. To avoid taking remote rq locks to examine source sched_groups for misfit tasks, each CPU is responsible for tracking misfit tasks themselves and update the rq->misfit_task flag. This means checking task utilization when tasks are scheduled and on sched_tick. Change-Id: I68514339e47427b38c0b77341c662e162225ad26 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-3-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 3b1baa6496e6b7ad016342a9d256bdfb072ce902 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 54 +++++++++++++++++++++++++++++++++++++------- kernel/sched/sched.h | 2 ++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d32867242b83..5b79144ac81b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -693,6 +693,7 @@ static u64 sched_vslice(struct cfs_rq *cfs_rq, struct sched_entity *se) static int select_idle_sibling(struct task_struct *p, int prev_cpu, int cpu); static unsigned long task_h_load(struct task_struct *p); +static unsigned long capacity_of(int cpu); /* Give new sched_entity start runnable values to heavy its load in infant time */ void init_entity_runnable_average(struct sched_entity *se) @@ -1457,7 +1458,6 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page, static unsigned long weighted_cpuload(struct rq *rq); static unsigned long source_load(int cpu, int type); static unsigned long target_load(int cpu, int type); -static unsigned long capacity_of(int cpu); /* Cached statistics for all CPUs within a node */ struct numa_stats { @@ -3724,6 +3724,29 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) WRITE_ONCE(p->se.avg.util_est, ue); } +static inline int task_fits_capacity(struct task_struct *p, long capacity) +{ + return capacity * 1024 > task_util_est(p) * capacity_margin; +} + +static inline void update_misfit_status(struct task_struct *p, struct rq *rq) +{ + if (!static_branch_unlikely(&sched_asym_cpucapacity)) + return; + + if (!p) { + rq->misfit_task_load = 0; + return; + } + + if (task_fits_capacity(p, capacity_of(cpu_of(rq)))) { + rq->misfit_task_load = 0; + return; + } + + rq->misfit_task_load = task_h_load(p); +} + #else /* CONFIG_SMP */ #define UPDATE_TG 0x0 @@ -3753,6 +3776,7 @@ util_est_enqueue(struct cfs_rq *cfs_rq, struct task_struct *p) {} static inline void util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) {} +static inline void update_misfit_status(struct task_struct *p, struct rq *rq) {} #endif /* CONFIG_SMP */ @@ -6294,7 +6318,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) /* Bring task utilization in sync with prev_cpu */ sync_entity_load_avg(&p->se); - return min_cap * 1024 < task_util(p) * capacity_margin; + return !task_fits_capacity(p, min_cap); } /* @@ -6713,9 +6737,12 @@ done: __maybe_unused; if (hrtick_enabled(rq)) hrtick_start_fair(rq, p); + update_misfit_status(p, rq); + return p; idle: + update_misfit_status(NULL, rq); new_tasks = idle_balance(rq, rf); /* @@ -6921,6 +6948,13 @@ static unsigned long __read_mostly max_load_balance_interval = HZ/10; enum fbq_type { regular, remote, all }; +enum group_type { + group_other = 0, + group_misfit_task, + group_imbalanced, + group_overloaded, +}; + #define LBF_ALL_PINNED 0x01 #define LBF_NEED_BREAK 0x02 #define LBF_DST_PINNED 0x04 @@ -7494,12 +7528,6 @@ static unsigned long task_h_load(struct task_struct *p) /********** Helpers for find_busiest_group ************************/ -enum group_type { - group_other = 0, - group_imbalanced, - group_overloaded, -}; - /* * sg_lb_stats - stats of a sched_group required for load_balancing */ @@ -7515,6 +7543,7 @@ struct sg_lb_stats { unsigned int group_weight; enum group_type group_type; int group_no_capacity; + unsigned long group_misfit_task_load; /* A CPU has a task too big for its capacity */ #ifdef CONFIG_NUMA_BALANCING unsigned int nr_numa_running; unsigned int nr_preferred_running; @@ -7807,6 +7836,9 @@ group_type group_classify(struct sched_group *group, if (sg_imbalanced(group)) return group_imbalanced; + if (sgs->group_misfit_task_load) + return group_misfit_task; + return group_other; } @@ -7881,6 +7913,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, */ if (!nr_running && idle_cpu(i)) sgs->idle_cpus++; + + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + sgs->group_misfit_task_load < rq->misfit_task_load) + sgs->group_misfit_task_load = rq->misfit_task_load; } /* Adjust by relative CPU capacity of the group */ @@ -9662,6 +9698,8 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) if (static_branch_unlikely(&sched_numa_balancing)) task_tick_numa(rq, curr); + + update_misfit_status(curr, rq); } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 34f79bbd07a2..8ba7d2403caf 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -845,6 +845,8 @@ struct rq { unsigned char idle_balance; + unsigned long misfit_task_load; + /* For active balancing */ int active_balance; int push_cpu; From 5016fc0abcce71c394a5504d0bd9593bdc4203d8 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:41 +0100 Subject: [PATCH 0705/1276] UPSTREAM: sched/fair: Add sched_group per-CPU max capacity The current sg->min_capacity tracks the lowest per-CPU compute capacity available in the sched_group when rt/irq pressure is taken into account. Minimum capacity isn't the ideal metric for tracking if a sched_group needs offloading to another sched_group for some scenarios, e.g. a sched_group with multiple CPUs if only one is under heavy pressure. Tracking maximum capacity isn't perfect either but a better choice for some situations as it indicates that the sched_group definitely compute capacity constrained either due to rt/irq pressure on all CPUs or asymmetric CPU capacities (e.g. big.LITTLE). Change-Id: If9c2ec9b6c34d002715d08f13f795cc8f27b9c72 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-4-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e3d6d0cb66f2351cbfd09fbae04eb9804afe9577 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 24 ++++++++++++++++++++---- kernel/sched/sched.h | 1 + kernel/sched/topology.c | 2 ++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5b79144ac81b..02c0caa0c2d5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7652,13 +7652,14 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu) cpu_rq(cpu)->cpu_capacity = capacity; sdg->sgc->capacity = capacity; sdg->sgc->min_capacity = capacity; + sdg->sgc->max_capacity = capacity; } void update_group_capacity(struct sched_domain *sd, int cpu) { struct sched_domain *child = sd->child; struct sched_group *group, *sdg = sd->groups; - unsigned long capacity, min_capacity; + unsigned long capacity, min_capacity, max_capacity; unsigned long interval; interval = msecs_to_jiffies(sd->balance_interval); @@ -7672,6 +7673,7 @@ void update_group_capacity(struct sched_domain *sd, int cpu) capacity = 0; min_capacity = ULONG_MAX; + max_capacity = 0; if (child->flags & SD_OVERLAP) { /* @@ -7702,6 +7704,7 @@ void update_group_capacity(struct sched_domain *sd, int cpu) } min_capacity = min(capacity, min_capacity); + max_capacity = max(capacity, max_capacity); } } else { /* @@ -7715,12 +7718,14 @@ void update_group_capacity(struct sched_domain *sd, int cpu) capacity += sgc->capacity; min_capacity = min(sgc->min_capacity, min_capacity); + max_capacity = max(sgc->max_capacity, max_capacity); group = group->next; } while (group != child->groups); } sdg->sgc->capacity = capacity; sdg->sgc->min_capacity = min_capacity; + sdg->sgc->max_capacity = max_capacity; } /* @@ -7816,16 +7821,27 @@ group_is_overloaded(struct lb_env *env, struct sg_lb_stats *sgs) } /* - * group_smaller_cpu_capacity: Returns true if sched_group sg has smaller + * group_smaller_min_cpu_capacity: Returns true if sched_group sg has smaller * per-CPU capacity than sched_group ref. */ static inline bool -group_smaller_cpu_capacity(struct sched_group *sg, struct sched_group *ref) +group_smaller_min_cpu_capacity(struct sched_group *sg, struct sched_group *ref) { return sg->sgc->min_capacity * capacity_margin < ref->sgc->min_capacity * 1024; } +/* + * group_smaller_max_cpu_capacity: Returns true if sched_group sg has smaller + * per-CPU capacity_orig than sched_group ref. + */ +static inline bool +group_smaller_max_cpu_capacity(struct sched_group *sg, struct sched_group *ref) +{ + return sg->sgc->max_capacity * capacity_margin < + ref->sgc->max_capacity * 1024; +} + static inline enum group_type group_classify(struct sched_group *group, struct sg_lb_stats *sgs) @@ -7971,7 +7987,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, * power/energy consequences are not considered. */ if (sgs->sum_nr_running <= sgs->group_weight && - group_smaller_cpu_capacity(sds->local, sg)) + group_smaller_min_cpu_capacity(sds->local, sg)) return false; asym_packing: diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8ba7d2403caf..38127177ffbf 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1200,6 +1200,7 @@ struct sched_group_capacity { */ unsigned long capacity; unsigned long min_capacity; /* Min per-CPU capacity in group */ + unsigned long max_capacity; /* Max per-CPU capacity in group */ unsigned long next_update; int imbalance; /* XXX unrelated to capacity but shared group state */ diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index b0cdf5e95bda..2536e1b938f9 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -693,6 +693,7 @@ static void init_overlap_sched_group(struct sched_domain *sd, sg_span = sched_group_span(sg); sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span); sg->sgc->min_capacity = SCHED_CAPACITY_SCALE; + sg->sgc->max_capacity = SCHED_CAPACITY_SCALE; } static int @@ -852,6 +853,7 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd) sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sched_group_span(sg)); sg->sgc->min_capacity = SCHED_CAPACITY_SCALE; + sg->sgc->max_capacity = SCHED_CAPACITY_SCALE; return sg; } From a65e56768a14107b22dad34488947b2e00dca1b4 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:42 +0100 Subject: [PATCH 0706/1276] UPSTREAM: sched/fair: Consider misfit tasks when load-balancing On asymmetric CPU capacity systems load intensive tasks can end up on CPUs that don't suit their compute demand. In this scenarios 'misfit' tasks should be migrated to CPUs with higher compute capacity to ensure better throughput. group_misfit_task indicates this scenario, but tweaks to the load-balance code are needed to make the migrations happen. Misfit balancing only makes sense between a source group of lower per-CPU capacity and destination group of higher compute capacity. Otherwise, misfit balancing is ignored. group_misfit_task has lowest priority so any imbalance due to overload is dealt with first. The modifications are: 1. Only pick a group containing misfit tasks as the busiest group if the destination group has higher capacity and has spare capacity. 2. When the busiest group is a 'misfit' group, skip the usual average load and group capacity checks. 3. Set the imbalance for 'misfit' balancing sufficiently high for a task to be pulled ignoring average load. 4. Pick the CPU with the highest misfit load as the source CPU. 5. If the misfit task is alone on the source CPU, go for active balancing. Change-Id: I7a3b1bc27960599fdb2085584e85d6772f426d13 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-5-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit cad68e552e7774b68ae6a2c5fedb792936098b72 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 51 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 02c0caa0c2d5..c21c361bd677 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6985,6 +6985,7 @@ struct lb_env { unsigned int loop_max; enum fbq_type fbq_type; + enum group_type src_grp_type; struct list_head tasks; }; @@ -7968,6 +7969,17 @@ static bool update_sd_pick_busiest(struct lb_env *env, { struct sg_lb_stats *busiest = &sds->busiest_stat; + /* + * Don't try to pull misfit tasks we can't help. + * We can use max_capacity here as reduction in capacity on some + * CPUs in the group should either be possible to resolve + * internally or be covered by avg_load imbalance (eventually). + */ + if (sgs->group_type == group_misfit_task && + (!group_smaller_max_cpu_capacity(sg, sds->local) || + !group_has_capacity(env, &sds->local_stat))) + return false; + if (sgs->group_type > busiest->group_type) return true; @@ -7990,6 +8002,13 @@ static bool update_sd_pick_busiest(struct lb_env *env, group_smaller_min_cpu_capacity(sds->local, sg)) return false; + /* + * If we have more than one misfit sg go with the biggest misfit. + */ + if (sgs->group_type == group_misfit_task && + sgs->group_misfit_task_load < busiest->group_misfit_task_load) + return false; + asym_packing: /* This is the busiest node in its class. */ if (!(env->sd->flags & SD_ASYM_PACKING)) @@ -8287,8 +8306,9 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s * factors in sg capacity and sgs with smaller group_type are * skipped when updating the busiest sg: */ - if (busiest->avg_load <= sds->avg_load || - local->avg_load >= sds->avg_load) { + if (busiest->group_type != group_misfit_task && + (busiest->avg_load <= sds->avg_load || + local->avg_load >= sds->avg_load)) { env->imbalance = 0; return fix_small_imbalance(env, sds); } @@ -8322,6 +8342,12 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s (sds->avg_load - local->avg_load) * local->group_capacity ) / SCHED_CAPACITY_SCALE; + /* Boost imbalance to allow misfit task to be balanced. */ + if (busiest->group_type == group_misfit_task) { + env->imbalance = max_t(long, env->imbalance, + busiest->group_misfit_task_load); + } + /* * if *imbalance is less than the average load per runnable task * there is no guarantee that any tasks will be moved so we'll have @@ -8388,6 +8414,10 @@ static struct sched_group *find_busiest_group(struct lb_env *env) busiest->group_no_capacity) goto force_balance; + /* Misfit tasks should be dealt with regardless of the avg load */ + if (busiest->group_type == group_misfit_task) + goto force_balance; + /* * If the local group is busier than the selected busiest group * don't try and pull any tasks. @@ -8425,6 +8455,7 @@ static struct sched_group *find_busiest_group(struct lb_env *env) force_balance: /* Looks like there is an imbalance. Compute it */ + env->src_grp_type = busiest->group_type; calculate_imbalance(env, &sds); return env->imbalance ? sds.busiest : NULL; @@ -8472,6 +8503,19 @@ static struct rq *find_busiest_queue(struct lb_env *env, if (rt > env->fbq_type) continue; + /* + * For ASYM_CPUCAPACITY domains with misfit tasks we simply + * seek the "biggest" misfit task. + */ + if (env->src_grp_type == group_misfit_task) { + if (rq->misfit_task_load > busiest_load) { + busiest_load = rq->misfit_task_load; + busiest = rq; + } + + continue; + } + capacity = capacity_of(i); wl = weighted_cpuload(rq); @@ -8541,6 +8585,9 @@ static int need_active_balance(struct lb_env *env) return 1; } + if (env->src_grp_type == group_misfit_task) + return 1; + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From 80a8eb703b3ba7bbb83722e38465f10d5f0992c4 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:43 +0100 Subject: [PATCH 0707/1276] UPSTREAM: sched/fair: Kick nohz balance if rq->misfit_task_load There already are a few conditions in nohz_kick_needed() to ensure a nohz kick is triggered, but they are not enough for some misfit task scenarios. Excluding asym packing, those are: - rq->nr_running >=2: Not relevant here because we are running a misfit task, it needs to be migrated regardless and potentially through active balance. - sds->nr_busy_cpus > 1: If there is only the misfit task being run on a group of low capacity CPUs, this will be evaluated to False. - rq->cfs.h_nr_running >=1 && check_cpu_capacity(): Not relevant here, misfit task needs to be migrated regardless of rt/IRQ pressure As such, this commit adds an rq->misfit_task_load condition to trigger a nohz kick. The idea to kick a nohz balance for misfit tasks originally came from Leo Yan , and a similar patch was submitted for the Android Common Kernel - see: https://lists.linaro.org/pipermail/eas-dev/2016-September/000551.html Change-Id: I93840d0c3eb7910b044140959c260a9faf603eb5 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-6-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 5fbdfae5221a5208ed8e7653fc1c4b31de420f74 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c21c361bd677..bd55a5706e10 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9230,7 +9230,7 @@ static void nohz_balancer_kick(struct rq *rq) if (time_before(now, nohz.next_balance)) goto out; - if (rq->nr_running >= 2) { + if (rq->nr_running >= 2 || rq->misfit_task_load) { flags = NOHZ_KICK_MASK; goto out; } From 3eaa67ff7cce14779b7f1939dd81278f50e91063 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:44 +0100 Subject: [PATCH 0708/1276] UPSTREAM: sched/fair: Change 'prefer_sibling' type to bool This variable is entirely local to update_sd_lb_stats, so we can safely change its type and slightly clean up its initialisation. Change-Id: Iefb32ff774541d4e057b74fe8357221529e0ac7c Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-7-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit dbbad719449e06d73db21598d6eee178f7a54b3b in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bd55a5706e10..9e03ebc763bf 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8077,11 +8077,9 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd struct sched_group *sg = env->sd->groups; struct sg_lb_stats *local = &sds->local_stat; struct sg_lb_stats tmp_sgs; - int load_idx, prefer_sibling = 0; + int load_idx; bool overload = false; - - if (child && child->flags & SD_PREFER_SIBLING) - prefer_sibling = 1; + bool prefer_sibling = child && child->flags & SD_PREFER_SIBLING; #ifdef CONFIG_NO_HZ_COMMON if (env->idle == CPU_NEWLY_IDLE && READ_ONCE(nohz.has_blocked)) From 8787472b25d65c7f49d5163e6798b88949a5e9b7 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:45 +0100 Subject: [PATCH 0709/1276] UPSTREAM: sched/core: Change root_domain->overload type to int sizeof(_Bool) is implementation defined, so let's just go with 'int' as is done for other structures e.g. sched_domain_shared->has_idle_cores. The local 'overload' variable used in update_sd_lb_stats can remain bool, as it won't impact any struct layout and can be assigned to the root_domain field. Change-Id: Iaaafc128f3b6b940bb04857e5d175a5e281c759a Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-8-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 575638d1047eb057a5cdf95cc0b3c084e1279508 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 38127177ffbf..c655b4feffcf 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -718,7 +718,7 @@ struct root_domain { cpumask_var_t online; /* Indicate more than one runnable task for any CPU */ - bool overload; + int overload; /* * The bit corresponding to a CPU gets set here if such CPU has more @@ -1701,7 +1701,7 @@ static inline void add_nr_running(struct rq *rq, unsigned count) if (prev_nr < 2 && rq->nr_running >= 2) { #ifdef CONFIG_SMP if (!rq->rd->overload) - rq->rd->overload = true; + rq->rd->overload = 1; #endif } From bb3fc6b401fcaa53cdda73a9d7b69e34a1f2ca61 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:46 +0100 Subject: [PATCH 0710/1276] UPSTREAM: sched/fair: Wrap rq->rd->overload accesses with READ/WRITE_ONCE() This variable can be read and set locklessly within update_sd_lb_stats(). As such, READ/WRITE_ONCE() are added to make sure nothing terribly wrong can happen because of the compiler. Change-Id: I8120c4a62474e0c8d8babdbb24880385c864f793 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-9-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e90c8fe15a3bf93a23088bcf1a56a0fa391d4e50 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 +++--- kernel/sched/sched.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9e03ebc763bf..af235a4a2a0f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8153,8 +8153,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (!env->sd->parent) { /* update overload indicator if we are at root domain */ - if (env->dst_rq->rd->overload != overload) - env->dst_rq->rd->overload = overload; + if (READ_ONCE(env->dst_rq->rd->overload) != overload) + WRITE_ONCE(env->dst_rq->rd->overload, overload); } } @@ -9597,7 +9597,7 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf) rq_unpin_lock(this_rq, rf); if (this_rq->avg_idle < sysctl_sched_migration_cost || - !this_rq->rd->overload) { + !READ_ONCE(this_rq->rd->overload)) { rcu_read_lock(); sd = rcu_dereference_check_sched_domain(this_rq->sd); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c655b4feffcf..47ced388d473 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1700,8 +1700,8 @@ static inline void add_nr_running(struct rq *rq, unsigned count) if (prev_nr < 2 && rq->nr_running >= 2) { #ifdef CONFIG_SMP - if (!rq->rd->overload) - rq->rd->overload = 1; + if (!READ_ONCE(rq->rd->overload)) + WRITE_ONCE(rq->rd->overload, 1); #endif } From 6505160b62c3b5ad0e5bdd1270fdb7e1fb96e6bf Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:47 +0100 Subject: [PATCH 0711/1276] UPSTREAM: sched/fair: Set rq->rd->overload when misfit Idle balance is a great opportunity to pull a misfit task. However, there are scenarios where misfit tasks are present but idle balance is prevented by the overload flag. A good example of this is a workload of n identical tasks. Let's suppose we have a 2+2 Arm big.LITTLE system. We then spawn 4 fairly CPU-intensive tasks - for the sake of simplicity let's say they are just CPU hogs, even when running on big CPUs. They are identical tasks, so on an SMP system they should all end at (roughly) the same time. However, in our case the LITTLE CPUs are less performing than the big CPUs, so tasks running on the LITTLEs will have a longer completion time. This means that the big CPUs will complete their work earlier, at which point they should pull the tasks from the LITTLEs. What we want to happen is summarized as follows: a,b,c,d are our CPU-hogging tasks _ signifies idling LITTLE_0 | a a a a _ _ LITTLE_1 | b b b b _ _ ---------|------------- big_0 | c c c c a a big_1 | d d d d b b ^ ^ Tasks end on the big CPUs, idle balance happens and the misfit tasks are pulled straight away This however won't happen, because currently the overload flag is only set when there is any CPU that has more than one runnable task - which may very well not be the case here if our CPU-hogging workload is all there is to run. As such, this commit sets the overload flag in update_sg_lb_stats when a group is flagged as having a misfit task. Change-Id: I8889880224c9c5a47f2f9c36809d1fafe9b3ec48 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-10-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 757ffdd705ee942fc8150b17942d968601d2a15b in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 ++++-- kernel/sched/sched.h | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index af235a4a2a0f..814ab7acd8ee 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7888,7 +7888,7 @@ static bool update_nohz_stats(struct rq *rq, bool force) * @load_idx: Load index of sched_domain of this_cpu for load calc. * @local_group: Does group contain this_cpu. * @sgs: variable to hold the statistics for this group. - * @overload: Indicate more than one runnable task for any CPU. + * @overload: Indicate pullable load (e.g. >1 runnable task). */ static inline void update_sg_lb_stats(struct lb_env *env, struct sched_group *group, int load_idx, @@ -7932,8 +7932,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->idle_cpus++; if (env->sd->flags & SD_ASYM_CPUCAPACITY && - sgs->group_misfit_task_load < rq->misfit_task_load) + sgs->group_misfit_task_load < rq->misfit_task_load) { sgs->group_misfit_task_load = rq->misfit_task_load; + *overload = 1; + } } /* Adjust by relative CPU capacity of the group */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 47ced388d473..b6c4c8619700 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -717,7 +717,11 @@ struct root_domain { cpumask_var_t span; cpumask_var_t online; - /* Indicate more than one runnable task for any CPU */ + /* + * Indicate pullable load on at least one CPU, e.g: + * - More than one runnable task + * - Running task is misfit + */ int overload; /* From e02924bc54023170742c872e6228074ccd86bee2 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Wed, 4 Jul 2018 11:17:48 +0100 Subject: [PATCH 0712/1276] UPSTREAM: sched/fair: Don't move tasks to lower capacity CPUs unless necessary When lower capacity CPUs are load balancing and considering to pull something from a higher capacity group, we should not pull tasks from a CPU with only one task running as this is guaranteed to impede progress for that task. If there is more than one task running, load balance in the higher capacity group would have already made any possible moves to resolve imbalance and we should make better use of system compute capacity by moving a task if we still have more than one running. Change-Id: Ic0358bea431012aaab57092de92c34d0ed51142f Signed-off-by: Chris Redpath Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-11-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 4ad3831a9d4af5e36da5d44a3b9c6522d0353cee in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 814ab7acd8ee..ea2e1af44dbb 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8518,6 +8518,17 @@ static struct rq *find_busiest_queue(struct lb_env *env, capacity = capacity_of(i); + /* + * For ASYM_CPUCAPACITY domains, don't pick a CPU that could + * eventually lead to active_balancing high->low capacity. + * Higher per-CPU capacity is considered better than balancing + * average load. + */ + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + capacity_of(env->dst_cpu) < capacity && + rq->nr_running == 1) + continue; + wl = weighted_cpuload(rq); /* From 608be3738df1bbaf1ceec941f0cfe8a5ce97bc61 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:50 +0100 Subject: [PATCH 0713/1276] UPSTREAM: sched/core: Disable SD_PREFER_SIBLING on asymmetric CPU capacity domains The 'prefer sibling' sched_domain flag is intended to encourage spreading tasks to sibling sched_domain to take advantage of more caches and core for SMT systems. It has recently been changed to be on all non-NUMA topology level. However, spreading across domains with CPU capacity asymmetry isn't desirable, e.g. spreading from high capacity to low capacity CPUs even if high capacity CPUs aren't overutilized might give access to more cache but the CPU will be slower and possibly lead to worse overall throughput. To prevent this, we need to remove SD_PREFER_SIBLING on the sched_domain level immediately below SD_ASYM_CPUCAPACITY. Change-Id: Id9f4944a71b6bbc91368ed8f0e045a9e4b234b49 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-13-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 9c63e84db29bcf584040931ad97c2edd11e35f6c in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/topology.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 2536e1b938f9..7ffad0d3a4eb 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -1126,7 +1126,7 @@ sd_init(struct sched_domain_topology_level *tl, | 0*SD_SHARE_CPUCAPACITY | 0*SD_SHARE_PKG_RESOURCES | 0*SD_SERIALIZE - | 0*SD_PREFER_SIBLING + | 1*SD_PREFER_SIBLING | 0*SD_NUMA | sd_flags , @@ -1152,17 +1152,21 @@ sd_init(struct sched_domain_topology_level *tl, if (sd->flags & SD_ASYM_CPUCAPACITY) { struct sched_domain *t = sd; + /* + * Don't attempt to spread across CPUs of different capacities. + */ + if (sd->child) + sd->child->flags &= ~SD_PREFER_SIBLING; + for_each_lower_domain(t) t->flags |= SD_BALANCE_WAKE; } if (sd->flags & SD_SHARE_CPUCAPACITY) { - sd->flags |= SD_PREFER_SIBLING; sd->imbalance_pct = 110; sd->smt_gain = 1178; /* ~15% */ } else if (sd->flags & SD_SHARE_PKG_RESOURCES) { - sd->flags |= SD_PREFER_SIBLING; sd->imbalance_pct = 117; sd->cache_nice_tries = 1; sd->busy_idx = 2; @@ -1173,6 +1177,7 @@ sd_init(struct sched_domain_topology_level *tl, sd->busy_idx = 3; sd->idle_idx = 2; + sd->flags &= ~SD_PREFER_SIBLING; sd->flags |= SD_SERIALIZE; if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) { sd->flags &= ~(SD_BALANCE_EXEC | @@ -1182,7 +1187,6 @@ sd_init(struct sched_domain_topology_level *tl, #endif } else { - sd->flags |= SD_PREFER_SIBLING; sd->cache_nice_tries = 1; sd->busy_idx = 2; sd->idle_idx = 1; From 1ebde9191d8a99d549803f10b7fbb126fa3f9393 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 25 Apr 2018 14:12:58 +0100 Subject: [PATCH 0714/1276] FROMLIST: sched: Relocate arch_scale_cpu_capacity By default, arch_scale_cpu_capacity() is only visible from within the kernel/sched folder. Relocate it to include/linux/sched/topology.h to make it visible to other clients needing to know about the capacity of CPUs, such as the Energy Model framework. Change-Id: I8bfc9a1f85111a69102e15fcd98458de7d180d01 Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from: https://lore.kernel.org/lkml/20180912091309.7551-2-quentin.perret@arm.com/ Signed-off-by: Quentin Perret --- include/linux/sched/topology.h | 19 +++++++++++++++++++ kernel/sched/sched.h | 21 --------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index 6b9976180c1e..5e56d6b1e217 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -202,6 +202,17 @@ extern void set_sched_topology(struct sched_domain_topology_level *tl); # define SD_INIT_NAME(type) #endif +#ifndef arch_scale_cpu_capacity +static __always_inline +unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) +{ + if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) + return sd->smt_gain / sd->span_weight; + + return SCHED_CAPACITY_SCALE; +} +#endif + #else /* CONFIG_SMP */ struct sched_domain_attr; @@ -217,6 +228,14 @@ static inline bool cpus_share_cache(int this_cpu, int that_cpu) return true; } +#ifndef arch_scale_cpu_capacity +static __always_inline +unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu) +{ + return SCHED_CAPACITY_SCALE; +} +#endif + #endif /* !CONFIG_SMP */ static inline int task_node(const struct task_struct *p) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b6c4c8619700..b2dc595308ea 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1764,27 +1764,6 @@ unsigned long arch_scale_freq_capacity(int cpu) } #endif -#ifdef CONFIG_SMP -#ifndef arch_scale_cpu_capacity -static __always_inline -unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) -{ - if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) - return sd->smt_gain / sd->span_weight; - - return SCHED_CAPACITY_SCALE; -} -#endif -#else -#ifndef arch_scale_cpu_capacity -static __always_inline -unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu) -{ - return SCHED_CAPACITY_SCALE; -} -#endif -#endif - struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf) __acquires(rq->lock); From b9776c5f02bbfcc87e6e10a2190fe91809fd6560 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 27 Apr 2018 15:05:47 +0100 Subject: [PATCH 0715/1276] FROMLIST: sched/cpufreq: Prepare schedutil for Energy Aware Scheduling Schedutil requests frequency by aggregating utilization signals from the scheduler (CFS, RT, DL, IRQ) and applying and 25% margin on top of them. Since Energy Aware Scheduling (EAS) needs to be able to predict the frequency requests, it needs to forecast the decisions made by the governor. In order to prepare the introduction of EAS, introduce schedutil_freq_util() to centralize the aforementioned signal aggregation and make it available to both schedutil and EAS. Since frequency selection and energy estimation still need to deal with RT and DL signals slightly differently, schedutil_freq_util() is called with a different 'type' parameter in those two contexts, and returns an aggregated utilization signal accordingly. While at it, introduce the map_util_freq() function which is designed to make schedutil's 25% margin usable easily for both sugov and EAS. As EAS will be able to predict schedutil's frequency requests more accurately than any other governor by design, it'd be sensible to make sure EAS cannot be used without schedutil. This will be done later, once EAS has actually been introduced. Change-Id: I9a4a56f46c21dc8f63413d86e0f42a8c3ba98418 Cc: Ingo Molnar Cc: Peter Zijlstra Suggested-by: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-3-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/sched/cpufreq.h | 6 +++ kernel/sched/cpufreq_schedutil.c | 89 +++++++++++++++++++++----------- kernel/sched/sched.h | 14 +++++ 3 files changed, 80 insertions(+), 29 deletions(-) diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h index 59667444669f..afa940cd50dc 100644 --- a/include/linux/sched/cpufreq.h +++ b/include/linux/sched/cpufreq.h @@ -20,6 +20,12 @@ void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data, void (*func)(struct update_util_data *data, u64 time, unsigned int flags)); void cpufreq_remove_update_util_hook(int cpu); + +static inline unsigned long map_util_freq(unsigned long util, + unsigned long freq, unsigned long cap) +{ + return (freq + (freq >> 2)) * util / cap; +} #endif /* CONFIG_CPU_FREQ */ #endif /* _LINUX_SCHED_CPUFREQ_H */ diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 3fffad3bc8a8..8356cb0072a6 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -13,6 +13,7 @@ #include "sched.h" +#include #include struct sugov_tunables { @@ -167,7 +168,7 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, unsigned int freq = arch_scale_freq_invariant() ? policy->cpuinfo.max_freq : policy->cur; - freq = (freq + (freq >> 2)) * util / max; + freq = map_util_freq(util, freq, max); if (freq == sg_policy->cached_raw_freq && !sg_policy->need_freq_update) return sg_policy->next_freq; @@ -197,15 +198,15 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * based on the task model parameters and gives the minimal utilization * required to meet deadlines. */ -static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) +unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, + enum schedutil_type type) { - struct rq *rq = cpu_rq(sg_cpu->cpu); + struct rq *rq = cpu_rq(cpu); unsigned long util, irq, max; - sg_cpu->max = max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); - sg_cpu->bw_dl = cpu_bw_dl(rq); + max = arch_scale_cpu_capacity(NULL, cpu); - if (rt_rq_is_runnable(&rq->rt)) + if (type == FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) return max; /* @@ -223,20 +224,33 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) * utilization (PELT windows are synchronized) we can directly add them * to obtain the CPU's actual utilization. */ - util = cpu_util_cfs(rq); + util = util_cfs; util += cpu_util_rt(rq); - /* - * We do not make cpu_util_dl() a permanent part of this sum because we - * want to use cpu_bw_dl() later on, but we need to check if the - * CFS+RT+DL sum is saturated (ie. no idle time) such that we select - * f_max when there is no idle time. - * - * NOTE: numerical errors or stop class might cause us to not quite hit - * saturation when we should -- something for later. - */ - if ((util + cpu_util_dl(rq)) >= max) - return max; + if (type == FREQUENCY_UTIL) { + /* + * For frequency selection we do not make cpu_util_dl() a + * permanent part of this sum because we want to use + * cpu_bw_dl() later on, but we need to check if the + * CFS+RT+DL sum is saturated (ie. no idle time) such + * that we select f_max when there is no idle time. + * + * NOTE: numerical errors or stop class might cause us + * to not quite hit saturation when we should -- + * something for later. + */ + + if ((util + cpu_util_dl(rq)) >= max) + return max; + } else { + /* + * OTOH, for energy computation we need the estimated + * running time, so include util_dl and ignore dl_bw. + */ + util += cpu_util_dl(rq); + if (util >= max) + return max; + } /* * There is still idle time; further improve the number by using the @@ -250,17 +264,34 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) util = scale_irq_capacity(util, irq, max); util += irq; - /* - * Bandwidth required by DEADLINE must always be granted while, for - * FAIR and RT, we use blocked utilization of IDLE CPUs as a mechanism - * to gracefully reduce the frequency when no tasks show up for longer - * periods of time. - * - * Ideally we would like to set bw_dl as min/guaranteed freq and util + - * bw_dl as requested freq. However, cpufreq is not yet ready for such - * an interface. So, we only do the latter for now. - */ - return min(max, util + sg_cpu->bw_dl); + if (type == FREQUENCY_UTIL) { + /* + * Bandwidth required by DEADLINE must always be granted + * while, for FAIR and RT, we use blocked utilization of + * IDLE CPUs as a mechanism to gracefully reduce the + * frequency when no tasks show up for longer periods of + * time. + * + * Ideally we would like to set bw_dl as min/guaranteed + * freq and util + bw_dl as requested freq. However, + * cpufreq is not yet ready for such an interface. So, + * we only do the latter for now. + */ + util += cpu_bw_dl(rq); + } + + return min(max, util); +} + +static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) +{ + struct rq *rq = cpu_rq(sg_cpu->cpu); + unsigned long util = cpu_util_cfs(rq); + + sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); + sg_cpu->bw_dl = cpu_bw_dl(rq); + + return schedutil_freq_util(sg_cpu->cpu, util, FREQUENCY_UTIL); } /** diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b2dc595308ea..aa5a5d1806c4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2175,7 +2175,15 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {} # define arch_scale_freq_invariant() false #endif +enum schedutil_type { + FREQUENCY_UTIL, + ENERGY_UTIL, +}; + #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL +unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, + enum schedutil_type type); + static inline unsigned long cpu_bw_dl(struct rq *rq) { return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT; @@ -2202,6 +2210,12 @@ static inline unsigned long cpu_util_rt(struct rq *rq) { return READ_ONCE(rq->avg_rt.util_avg); } +#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ +static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, + enum schedutil_type type) +{ + return util; +} #endif #ifdef HAVE_SCHED_AVG_IRQ From 15867668a4a63c4ed48e0917d38a03a38563f0cc Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 25 Apr 2018 18:34:11 +0100 Subject: [PATCH 0716/1276] FROMLIST: PM: Introduce an Energy Model management framework Several subsystems in the kernel (task scheduler and/or thermal at the time of writing) can benefit from knowing about the energy consumed by CPUs. Yet, this information can come from different sources (DT or firmware for example), in different formats, hence making it hard to exploit without a standard API. As an attempt to address this, introduce a centralized Energy Model (EM) management framework which aggregates the power values provided by drivers into a table for each performance domain in the system. The power cost tables are made available to interested clients (e.g. task scheduler or thermal) via platform-agnostic APIs. The overall design is represented by the diagram below (focused on Arm-related drivers as an example, but applicable to any architecture): +---------------+ +-----------------+ +-------------+ | Thermal (IPA) | | Scheduler (EAS) | | Other | +---------------+ +-----------------+ +-------------+ | | em_pd_energy() | | | em_cpu_get() | +-----------+ | +--------+ | | | v v v +---------------------+ | | | Energy Model | | | | Framework | | | +---------------------+ ^ ^ ^ | | | em_register_perf_domain() +----------+ | +---------+ | | | +---------------+ +---------------+ +--------------+ | cpufreq-dt | | arm_scmi | | Other | +---------------+ +---------------+ +--------------+ ^ ^ ^ | | | +--------------+ +---------------+ +--------------+ | Device Tree | | Firmware | | ? | +--------------+ +---------------+ +--------------+ Drivers (typically, but not limited to, CPUFreq drivers) can register data in the EM framework using the em_register_perf_domain() API. The calling driver must provide a callback function with a standardized signature that will be used by the EM framework to build the power cost tables of the performance domain. This design should offer a lot of flexibility to calling drivers which are free of reading information from any location and to use any technique to compute power costs. Moreover, the capacity states registered by drivers in the EM framework are not required to match real performance states of the target. This is particularly important on targets where the performance states are not known by the OS. The power cost coefficients managed by the EM framework are specified in milli-watts. Although the two potential users of those coefficients (IPA and EAS) only need relative correctness, IPA specifically needs to compare the power of CPUs with the power of other components (GPUs, for example), which are still expressed in absolute terms in their respective subsystems. Hence, specifying the power of CPUs in milli-watts should help transitioning IPA to using the EM framework without introducing new problems by keeping units comparable across sub-systems. On the longer term, the EM of other devices than CPUs could also be managed by the EM framework, which would enable to remove the absolute unit. However, this is not absolutely required as a first step, so this extension of the EM framework is left for later. On the client side, the EM framework offers APIs to access the power cost tables of a CPU (em_cpu_get()), and to estimate the energy consumed by the CPUs of a performance domain (em_pd_energy()). Clients such as the task scheduler can then use these APIs to access the shared data structures holding the Energy Model of CPUs. Change-Id: I40c09c52a6a4e6376daa533d3b5b965eb16d1a29 Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-4-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/energy_model.h | 185 +++++++++++++++++++++++++++++++++ kernel/power/Kconfig | 15 +++ kernel/power/Makefile | 1 + kernel/power/energy_model.c | 195 +++++++++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+) create mode 100644 include/linux/energy_model.h create mode 100644 kernel/power/energy_model.c diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h new file mode 100644 index 000000000000..a472076f9c80 --- /dev/null +++ b/include/linux/energy_model.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ENERGY_MODEL_H +#define _LINUX_ENERGY_MODEL_H +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ENERGY_MODEL +/** + * em_cap_state - Capacity state of a performance domain + * @frequency: The CPU frequency in KHz, for consistency with CPUFreq + * @power: The power consumed by 1 CPU at this level, in milli-watts + * @cost: The cost coefficient associated with this level, used during + * energy calculation. Equal to: power * max_frequency / frequency + */ +struct em_cap_state { + unsigned long frequency; + unsigned long power; + unsigned long cost; +}; + +/** + * em_perf_domain - Performance domain + * @table: List of capacity states, in ascending order + * @nr_cap_states: Number of capacity states + * @cpus: Cpumask covering the CPUs of the domain + * + * A "performance domain" represents a group of CPUs whose performance is + * scaled together. All CPUs of a performance domain must have the same + * micro-architecture. Performance domains often have a 1-to-1 mapping with + * CPUFreq policies. + */ +struct em_perf_domain { + struct em_cap_state *table; + int nr_cap_states; + unsigned long cpus[0]; +}; + +#define EM_CPU_MAX_POWER 0xFFFF + +struct em_data_callback { + /** + * active_power() - Provide power at the next capacity state of a CPU + * @power : Active power at the capacity state in mW (modified) + * @freq : Frequency at the capacity state in kHz (modified) + * @cpu : CPU for which we do this operation + * + * active_power() must find the lowest capacity state of 'cpu' above + * 'freq' and update 'power' and 'freq' to the matching active power + * and frequency. + * + * The power is the one of a single CPU in the domain, expressed in + * milli-watts. It is expected to fit in the [0, EM_CPU_MAX_POWER] + * range. + * + * Return 0 on success. + */ + int (*active_power)(unsigned long *power, unsigned long *freq, int cpu); +}; +#define EM_DATA_CB(_active_power_cb) { .active_power = &_active_power_cb } + +struct em_perf_domain *em_cpu_get(int cpu); +int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, + struct em_data_callback *cb); + +/** + * em_pd_energy() - Estimates the energy consumed by the CPUs of a perf. domain + * @pd : performance domain for which energy has to be estimated + * @max_util : highest utilization among CPUs of the domain + * @sum_util : sum of the utilization of all CPUs in the domain + * + * Return: the sum of the energy consumed by the CPUs of the domain assuming + * a capacity state satisfying the max utilization of the domain. + */ +static inline unsigned long em_pd_energy(struct em_perf_domain *pd, + unsigned long max_util, unsigned long sum_util) +{ + unsigned long freq, scale_cpu; + struct em_cap_state *cs; + int i, cpu; + + /* + * In order to predict the capacity state, map the utilization of the + * most utilized CPU of the performance domain to a requested frequency, + * like schedutil. + */ + cpu = cpumask_first(to_cpumask(pd->cpus)); + scale_cpu = arch_scale_cpu_capacity(NULL, cpu); + cs = &pd->table[pd->nr_cap_states - 1]; + freq = map_util_freq(max_util, cs->frequency, scale_cpu); + + /* + * Find the lowest capacity state of the Energy Model above the + * requested frequency. + */ + for (i = 0; i < pd->nr_cap_states; i++) { + cs = &pd->table[i]; + if (cs->frequency >= freq) + break; + } + + /* + * The capacity of a CPU in the domain at that capacity state (cs) + * can be computed as: + * + * cs->freq * scale_cpu + * cs->cap = -------------------- (1) + * cpu_max_freq + * + * So, the energy consumed by this CPU at that capacity state is: + * + * cs->power * cpu_util + * cpu_nrg = -------------------- (2) + * cs->cap + * + * since 'cpu_util / cs->cap' represents its percentage of busy time. + * + * NOTE: Although the result of this computation actually is in + * units of power, it can be manipulated as an energy value + * over a scheduling period, since it is assumed to be + * constant during that interval. + * + * By injecting (1) in (2), 'cpu_nrg' can be re-expressed as a product + * of two terms: + * + * cs->power * cpu_max_freq cpu_util + * cpu_nrg = ------------------------ * --------- (3) + * cs->freq scale_cpu + * + * The first term is static, and is stored in the em_cap_state struct + * as 'cs->cost'. + * + * Since all CPUs of the domain have the same micro-architecture, they + * share the same 'cs->cost', and the same CPU capacity. Hence, the + * total energy of the domain (which is the simple sum of the energy of + * all of its CPUs) can be factorized as: + * + * cs->cost * \Sum cpu_util + * pd_nrg = ------------------------ (4) + * scale_cpu + */ + return cs->cost * sum_util / scale_cpu; +} + +/** + * em_pd_nr_cap_states() - Get the number of capacity states of a perf. domain + * @pd : performance domain for which this must be done + * + * Return: the number of capacity states in the performance domain table + */ +static inline int em_pd_nr_cap_states(struct em_perf_domain *pd) +{ + return pd->nr_cap_states; +} + +#else +struct em_perf_domain {}; +struct em_data_callback {}; +#define EM_DATA_CB(_active_power_cb) { } + +static inline int em_register_perf_domain(cpumask_t *span, + unsigned int nr_states, struct em_data_callback *cb) +{ + return -EINVAL; +} +static inline struct em_perf_domain *em_cpu_get(int cpu) +{ + return NULL; +} +static inline unsigned long em_pd_energy(struct em_perf_domain *pd, + unsigned long max_util, unsigned long sum_util) +{ + return 0; +} +static inline int em_pd_nr_cap_states(struct em_perf_domain *pd) +{ + return 0; +} +#endif + +#endif diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3a6c2f87699e..f8fe57d1022e 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -298,3 +298,18 @@ config PM_GENERIC_DOMAINS_OF config CPU_PM bool + +config ENERGY_MODEL + bool "Energy Model for CPUs" + depends on SMP + depends on CPU_FREQ + default n + help + Several subsystems (thermal and/or the task scheduler for example) + can leverage information about the energy consumed by CPUs to make + smarter decisions. This config option enables the framework from + which subsystems can access the energy models. + + The exact usage of the energy model is subsystem-dependent. + + If in doubt, say N. diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 5c1743d4d8ef..3f8db8361561 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_SUSPEND) += wakeup_reason.o +obj-$(CONFIG_ENERGY_MODEL) += energy_model.o diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c new file mode 100644 index 000000000000..d77cd295f3e9 --- /dev/null +++ b/kernel/power/energy_model.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Energy Model of CPUs + * + * Copyright (c) 2018, Arm ltd. + * Written by: Quentin Perret, Arm ltd. + */ + +#define pr_fmt(fmt) "energy_model: " fmt + +#include +#include +#include +#include +#include + +/* Mapping of each CPU to the performance domain to which it belongs. */ +static DEFINE_PER_CPU(struct em_perf_domain *, em_data); + +/* + * Mutex serializing the registrations of performance domains and letting + * callbacks defined by drivers sleep. + */ +static DEFINE_MUTEX(em_pd_mutex); + +static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, + struct em_data_callback *cb) +{ + unsigned long opp_eff, prev_opp_eff = ULONG_MAX; + unsigned long power, freq, prev_freq = 0; + int i, ret, cpu = cpumask_first(span); + struct em_cap_state *table; + struct em_perf_domain *pd; + u64 fmax; + + if (!cb->active_power) + return NULL; + + pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL); + if (!pd) + return NULL; + + table = kcalloc(nr_states, sizeof(*table), GFP_KERNEL); + if (!table) + goto free_pd; + + /* Build the list of capacity states for this performance domain */ + for (i = 0, freq = 0; i < nr_states; i++, freq++) { + /* + * active_power() is a driver callback which ceils 'freq' to + * lowest capacity state of 'cpu' above 'freq' and updates + * 'power' and 'freq' accordingly. + */ + ret = cb->active_power(&power, &freq, cpu); + if (ret) { + pr_err("pd%d: invalid cap. state: %d\n", cpu, ret); + goto free_cs_table; + } + + /* + * We expect the driver callback to increase the frequency for + * higher capacity states. + */ + if (freq <= prev_freq) { + pr_err("pd%d: non-increasing freq: %lu\n", cpu, freq); + goto free_cs_table; + } + + /* + * The power returned by active_state() is expected to be + * positive, in milli-watts and to fit into 16 bits. + */ + if (!power || power > EM_CPU_MAX_POWER) { + pr_err("pd%d: invalid power: %lu\n", cpu, power); + goto free_cs_table; + } + + table[i].power = power; + table[i].frequency = prev_freq = freq; + + /* + * The hertz/watts efficiency ratio should decrease as the + * frequency grows on sane platforms. But this isn't always + * true in practice so warn the user if a higher OPP is more + * power efficient than a lower one. + */ + opp_eff = freq / power; + if (opp_eff >= prev_opp_eff) + pr_warn("pd%d: hertz/watts ratio non-monotonically decreasing: OPP%d >= OPP%d\n", + cpu, i, i - 1); + prev_opp_eff = opp_eff; + } + + /* Compute the cost of each capacity_state. */ + fmax = (u64) table[nr_states - 1].frequency; + for (i = 0; i < nr_states; i++) { + table[i].cost = div64_u64(fmax * table[i].power, + table[i].frequency); + } + + pd->table = table; + pd->nr_cap_states = nr_states; + cpumask_copy(to_cpumask(pd->cpus), span); + + return pd; + +free_cs_table: + kfree(table); +free_pd: + kfree(pd); + + return NULL; +} + +/** + * em_cpu_get() - Return the performance domain for a CPU + * @cpu : CPU to find the performance domain for + * + * Return: the performance domain to which 'cpu' belongs, or NULL if it doesn't + * exist. + */ +struct em_perf_domain *em_cpu_get(int cpu) +{ + return READ_ONCE(per_cpu(em_data, cpu)); +} +EXPORT_SYMBOL_GPL(em_cpu_get); + +/** + * em_register_perf_domain() - Register the Energy Model of a performance domain + * @span : Mask of CPUs in the performance domain + * @nr_states : Number of capacity states to register + * @cb : Callback functions providing the data of the Energy Model + * + * Create Energy Model tables for a performance domain using the callbacks + * defined in cb. + * + * If multiple clients register the same performance domain, all but the first + * registration will be ignored. + * + * Return 0 on success + */ +int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, + struct em_data_callback *cb) +{ + unsigned long cap, prev_cap = 0; + struct em_perf_domain *pd; + int cpu, ret = 0; + + if (!span || !nr_states || !cb) + return -EINVAL; + + /* + * Use a mutex to serialize the registration of performance domains and + * let the driver-defined callback functions sleep. + */ + mutex_lock(&em_pd_mutex); + + for_each_cpu(cpu, span) { + /* Make sure we don't register again an existing domain. */ + if (READ_ONCE(per_cpu(em_data, cpu))) { + ret = -EEXIST; + goto unlock; + } + + /* + * All CPUs of a domain must have the same micro-architecture + * since they all share the same table. + */ + cap = arch_scale_cpu_capacity(NULL, cpu); + if (prev_cap && prev_cap != cap) { + pr_err("CPUs of %*pbl must have the same capacity\n", + cpumask_pr_args(span)); + ret = -EINVAL; + goto unlock; + } + prev_cap = cap; + } + + /* Create the performance domain and add it to the Energy Model. */ + pd = em_create_pd(span, nr_states, cb); + if (!pd) { + ret = -EINVAL; + goto unlock; + } + + for_each_cpu(cpu, span) + WRITE_ONCE(per_cpu(em_data, cpu), pd); + + pr_debug("Created perf domain %*pbl\n", cpumask_pr_args(span)); +unlock: + mutex_unlock(&em_pd_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(em_register_perf_domain); From 3917007dad8be5350090cc9b7251ff6870faf737 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 17 May 2018 15:50:07 +0100 Subject: [PATCH 0717/1276] FROMLIST: PM / EM: Expose the Energy Model in sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the Energy Model (read-only) of all performance domains in sysfs for convenience. To do so, add a kobject to the CPU subsystem under the umbrella of which a kobject for each performance domain is attached. The resulting hierarchy is as follows for a platform with two performance domains for example: /sys/devices/system/cpu/energy_model ├── pd0 │   ├── cost │   ├── cpus │   ├── frequency │   └── power └── pd4 ├── cost ├── cpus ├── frequency └── power In this implementation, the kobject abstraction is only used as a convenient way of exposing data to sysfs. However, it could also be used in the future to allocate and release performance domains in a more dynamic way using reference counting. Change-Id: Ia8184e2100a38dbec0837e8efc755d0702ef86a7 Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-5-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/energy_model.h | 2 + kernel/power/energy_model.c | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h index a472076f9c80..69184f4f4ae0 100644 --- a/include/linux/energy_model.h +++ b/include/linux/energy_model.h @@ -27,6 +27,7 @@ struct em_cap_state { * em_perf_domain - Performance domain * @table: List of capacity states, in ascending order * @nr_cap_states: Number of capacity states + * @kobj: Kobject used to expose the domain in sysfs * @cpus: Cpumask covering the CPUs of the domain * * A "performance domain" represents a group of CPUs whose performance is @@ -37,6 +38,7 @@ struct em_cap_state { struct em_perf_domain { struct em_cap_state *table; int nr_cap_states; + struct kobject kobj; unsigned long cpus[0]; }; diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index d77cd295f3e9..2934ebb09e9d 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -23,6 +23,82 @@ static DEFINE_PER_CPU(struct em_perf_domain *, em_data); */ static DEFINE_MUTEX(em_pd_mutex); +static struct kobject *em_kobject; + +/* Getters for the attributes of em_perf_domain objects */ +struct em_pd_attr { + struct attribute attr; + ssize_t (*show)(struct em_perf_domain *pd, char *buf); + ssize_t (*store)(struct em_perf_domain *pd, const char *buf, size_t s); +}; + +#define EM_ATTR_LEN 13 +#define show_table_attr(_attr) \ +static ssize_t show_##_attr(struct em_perf_domain *pd, char *buf) \ +{ \ + ssize_t cnt = 0; \ + int i; \ + for (i = 0; i < pd->nr_cap_states; i++) { \ + if (cnt >= (ssize_t) (PAGE_SIZE / sizeof(char) \ + - (EM_ATTR_LEN + 2))) \ + goto out; \ + cnt += scnprintf(&buf[cnt], EM_ATTR_LEN + 1, "%lu ", \ + pd->table[i]._attr); \ + } \ +out: \ + cnt += sprintf(&buf[cnt], "\n"); \ + return cnt; \ +} + +show_table_attr(power); +show_table_attr(frequency); +show_table_attr(cost); + +static ssize_t show_cpus(struct em_perf_domain *pd, char *buf) +{ + return sprintf(buf, "%*pbl\n", cpumask_pr_args(to_cpumask(pd->cpus))); +} + +#define pd_attr(_name) em_pd_##_name##_attr +#define define_pd_attr(_name) static struct em_pd_attr pd_attr(_name) = \ + __ATTR(_name, 0444, show_##_name, NULL) + +define_pd_attr(power); +define_pd_attr(frequency); +define_pd_attr(cost); +define_pd_attr(cpus); + +static struct attribute *em_pd_default_attrs[] = { + &pd_attr(power).attr, + &pd_attr(frequency).attr, + &pd_attr(cost).attr, + &pd_attr(cpus).attr, + NULL +}; + +#define to_pd(k) container_of(k, struct em_perf_domain, kobj) +#define to_pd_attr(a) container_of(a, struct em_pd_attr, attr) + +static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct em_perf_domain *pd = to_pd(kobj); + struct em_pd_attr *pd_attr = to_pd_attr(attr); + ssize_t ret; + + ret = pd_attr->show(pd, buf); + + return ret; +} + +static const struct sysfs_ops em_pd_sysfs_ops = { + .show = show, +}; + +static struct kobj_type ktype_em_pd = { + .sysfs_ops = &em_pd_sysfs_ops, + .default_attrs = em_pd_default_attrs, +}; + static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, struct em_data_callback *cb) { @@ -102,6 +178,11 @@ static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, pd->nr_cap_states = nr_states; cpumask_copy(to_cpumask(pd->cpus), span); + ret = kobject_init_and_add(&pd->kobj, &ktype_em_pd, em_kobject, + "pd%u", cpu); + if (ret) + pr_err("pd%d: failed kobject_init_and_add(): %d\n", cpu, ret); + return pd; free_cs_table: @@ -155,6 +236,15 @@ int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, */ mutex_lock(&em_pd_mutex); + if (!em_kobject) { + em_kobject = kobject_create_and_add("energy_model", + &cpu_subsys.dev_root->kobj); + if (!em_kobject) { + ret = -ENODEV; + goto unlock; + } + } + for_each_cpu(cpu, span) { /* Make sure we don't register again an existing domain. */ if (READ_ONCE(per_cpu(em_data, cpu))) { From 38c1ab92444524a48c160a61a232d6b81070d383 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 11 Sep 2018 12:04:40 +0100 Subject: [PATCH 0718/1276] FROMLIST: sched: Introduce a sched_feat for Energy Aware Scheduling In order to make sure Energy Aware Scheduling (EAS) doesn't hurt systems not using it, add a new sched_feat, called ENERGY_AWARE, guarding the access to EAS code paths. Change-Id: I70e1ca087908acbc98fb752c40b8ff65bb28ef2b (Applied from https://lore.kernel.org/lkml/20180912091309.7551-6-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/features.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 85ae8488039c..26ff6752e492 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -90,3 +90,10 @@ SCHED_FEAT(WA_BIAS, true) * UtilEstimation. Use estimated CPU utilization. */ SCHED_FEAT(UTIL_EST, true) + +/* + * Energy-Aware Scheduling. Whether or not tasks will be placed into an + * energy-aware fashion depends on this feature being enabled and on the + * root domain having an Energy Model attached. + */ +SCHED_FEAT(ENERGY_AWARE, false) From 73bdf02f587a22b40ee9567d5d8e93cc19106352 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 30 Apr 2018 11:23:39 +0100 Subject: [PATCH 0719/1276] FROMLIST: sched/topology: Reference the Energy Model of CPUs when available The existing scheduling domain hierarchy is defined to map to the cache topology of the system. However, Energy Aware Scheduling (EAS) requires more knowledge about the platform, and specifically needs to know about the span of Performance Domains (PD), which do not always align with caches. To address this issue, use the Energy Model (EM) of the system to extend the scheduler topology code with a representation of the PDs, alongside the scheduling domains. More specifically, a linked list of PDs is attached to each root domain. When multiple root domains are in use, each list contains only the PDs covering the CPUs of its root domain. If a PD spans over CPUs of two different root domains, it will be duplicated in both lists. The lists are fully maintained by the scheduler from partition_sched_domains() in order to cope with hotplug and cpuset changes. As for scheduling domains, the list are protected by RCU to ensure safe concurrent updates. The linked lists should be used only from code paths protected by the ENERGY_AWARE sched_feat. Change-Id: I65ba204388315cd3e7048241a7dd1c119021d63e Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-7-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 21 +++++++ kernel/sched/topology.c | 134 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 4 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index aa5a5d1806c4..022518c0efc9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -702,6 +703,12 @@ static inline bool sched_asym_prefer(int a, int b) return arch_asym_cpu_priority(a) > arch_asym_cpu_priority(b); } +struct perf_domain { + struct em_perf_domain *obj; + struct perf_domain *next; + struct rcu_head rcu; +}; + /* * We add the notion of a root-domain which will be used to define per-domain * variables. Each exclusive cpuset essentially defines an island domain by @@ -754,6 +761,12 @@ struct root_domain { struct cpupri cpupri; unsigned long max_cpu_capacity; + + /* + * NULL-terminated list of performance domains intersecting with the + * CPUs of the rd. Protected by RCU. + */ + struct perf_domain *pd; }; extern struct root_domain def_root_domain; @@ -2245,3 +2258,11 @@ unsigned long scale_irq_capacity(unsigned long util, unsigned long irq, unsigned return util; } #endif + +#ifdef CONFIG_SMP +#ifdef CONFIG_ENERGY_MODEL +#define perf_domain_span(pd) (to_cpumask(((pd)->obj->cpus))) +#else +#define perf_domain_span(pd) NULL +#endif +#endif diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 7ffad0d3a4eb..23230b4d629f 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -201,6 +201,116 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) return 1; } +#ifdef CONFIG_ENERGY_MODEL +static void free_pd(struct perf_domain *pd) +{ + struct perf_domain *tmp; + + while (pd) { + tmp = pd->next; + kfree(pd); + pd = tmp; + } +} + +static struct perf_domain *find_pd(struct perf_domain *pd, int cpu) +{ + while (pd) { + if (cpumask_test_cpu(cpu, perf_domain_span(pd))) + return pd; + pd = pd->next; + } + + return NULL; +} + +static struct perf_domain *pd_init(int cpu) +{ + struct em_perf_domain *obj = em_cpu_get(cpu); + struct perf_domain *pd; + + if (!obj) { + if (sched_debug()) + pr_info("%s: no EM found for CPU%d\n", __func__, cpu); + return NULL; + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return NULL; + pd->obj = obj; + + return pd; +} + +static void perf_domain_debug(const struct cpumask *cpu_map, + struct perf_domain *pd) +{ + if (!sched_debug() || !pd) + return; + + printk(KERN_DEBUG "root_domain %*pbl: ", cpumask_pr_args(cpu_map)); + + while (pd) { + printk(KERN_CONT " pd%d:{ cpus=%*pbl nr_cstate=%d }", + cpumask_first(perf_domain_span(pd)), + cpumask_pr_args(perf_domain_span(pd)), + em_pd_nr_cap_states(pd->obj)); + pd = pd->next; + } + + printk(KERN_CONT "\n"); +} + +static void destroy_perf_domain_rcu(struct rcu_head *rp) +{ + struct perf_domain *pd; + + pd = container_of(rp, struct perf_domain, rcu); + free_pd(pd); +} + +static void build_perf_domains(const struct cpumask *cpu_map) +{ + struct perf_domain *pd = NULL, *tmp; + int cpu = cpumask_first(cpu_map); + struct root_domain *rd = cpu_rq(cpu)->rd; + int i; + + for_each_cpu(i, cpu_map) { + /* Skip already covered CPUs. */ + if (find_pd(pd, i)) + continue; + + /* Create the new pd and add it to the local list. */ + tmp = pd_init(i); + if (!tmp) + goto free; + tmp->next = pd; + pd = tmp; + } + + perf_domain_debug(cpu_map, pd); + + /* Attach the new list of performance domains to the root domain. */ + tmp = rd->pd; + rcu_assign_pointer(rd->pd, pd); + if (tmp) + call_rcu(&tmp->rcu, destroy_perf_domain_rcu); + + return; + +free: + free_pd(pd); + tmp = rd->pd; + rcu_assign_pointer(rd->pd, NULL); + if (tmp) + call_rcu(&tmp->rcu, destroy_perf_domain_rcu); +} +#else +static void free_pd(struct perf_domain *pd) { } +#endif /* CONFIG_ENERGY_MODEL */ + static void free_rootdomain(struct rcu_head *rcu) { struct root_domain *rd = container_of(rcu, struct root_domain, rcu); @@ -211,6 +321,7 @@ static void free_rootdomain(struct rcu_head *rcu) free_cpumask_var(rd->rto_mask); free_cpumask_var(rd->online); free_cpumask_var(rd->span); + free_pd(rd->pd); kfree(rd); } @@ -1961,8 +2072,8 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], /* Destroy deleted domains: */ for (i = 0; i < ndoms_cur; i++) { for (j = 0; j < n && !new_topology; j++) { - if (cpumask_equal(doms_cur[i], doms_new[j]) - && dattrs_equal(dattr_cur, i, dattr_new, j)) + if (cpumask_equal(doms_cur[i], doms_new[j]) && + dattrs_equal(dattr_cur, i, dattr_new, j)) goto match1; } /* No match - a current sched domain not in new doms_new[] */ @@ -1982,8 +2093,8 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], /* Build new domains: */ for (i = 0; i < ndoms_new; i++) { for (j = 0; j < n && !new_topology; j++) { - if (cpumask_equal(doms_new[i], doms_cur[j]) - && dattrs_equal(dattr_new, i, dattr_cur, j)) + if (cpumask_equal(doms_new[i], doms_cur[j]) && + dattrs_equal(dattr_new, i, dattr_cur, j)) goto match2; } /* No match - add a new doms_new */ @@ -1992,6 +2103,21 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], ; } +#ifdef CONFIG_ENERGY_MODEL + /* Build perf. domains: */ + for (i = 0; i < ndoms_new; i++) { + for (j = 0; j < n; j++) { + if (cpumask_equal(doms_new[i], doms_cur[j]) && + cpu_rq(cpumask_first(doms_cur[j]))->rd->pd) + goto match3; + } + /* No match - add perf. domains for a new rd */ + build_perf_domains(doms_new[i]); +match3: + ; + } +#endif + /* Remember the new sched domains: */ if (doms_cur != &fallback_doms) free_sched_domains(doms_cur, ndoms_cur); From 3c9119296620f167e9427ae1e17669425e771af9 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 10:16:33 +0100 Subject: [PATCH 0720/1276] FROMLIST: sched/topology: Lowest CPU asymmetry sched_domain level pointer Add another member to the family of per-cpu sched_domain shortcut pointers. This one, sd_asym_cpucapacity, points to the lowest level at which the SD_ASYM_CPUCAPACITY flag is set. While at it, rename the sd_asym shortcut to sd_asym_packing to avoid confusions. Generally speaking, the largest opportunity to save energy via scheduling comes from a smarter exploitation of heterogeneous platforms (i.e. big.LITTLE). Consequently, the sd_asym_cpucapacity shortcut will be used at first as the lowest domain where Energy-Aware Scheduling (EAS) should be applied. For example, it is possible to apply EAS within a socket on a multi-socket system, as long as each socket has an asymmetric topology. Cross-sockets wake-up balancing will only happen when the system is over-utilized, or this_cpu and prev_cpu are in different sockets. Change-Id: I6a1585a858aa6ca0b9d8cc66f8559dfa2797a6af cc: Ingo Molnar cc: Peter Zijlstra Suggested-by: Morten Rasmussen (Applied from https://lore.kernel.org/lkml/20180912091309.7551-8-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 2 +- kernel/sched/sched.h | 3 ++- kernel/sched/topology.c | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ea2e1af44dbb..61e59c26e88c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9270,7 +9270,7 @@ static void nohz_balancer_kick(struct rq *rq) } } - sd = rcu_dereference(per_cpu(sd_asym, cpu)); + sd = rcu_dereference(per_cpu(sd_asym_packing, cpu)); if (sd) { for_each_cpu(i, sched_domain_span(sd)) { if (i == cpu || diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 022518c0efc9..b71d3387ef60 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1206,7 +1206,8 @@ DECLARE_PER_CPU(int, sd_llc_size); DECLARE_PER_CPU(int, sd_llc_id); DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DECLARE_PER_CPU(struct sched_domain *, sd_numa); -DECLARE_PER_CPU(struct sched_domain *, sd_asym); +DECLARE_PER_CPU(struct sched_domain *, sd_asym_packing); +DECLARE_PER_CPU(struct sched_domain *, sd_asym_cpucapacity); extern struct static_key_false sched_asym_cpucapacity; struct sched_group_capacity { diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 23230b4d629f..c63a6dee1088 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -508,7 +508,8 @@ DEFINE_PER_CPU(int, sd_llc_size); DEFINE_PER_CPU(int, sd_llc_id); DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DEFINE_PER_CPU(struct sched_domain *, sd_numa); -DEFINE_PER_CPU(struct sched_domain *, sd_asym); +DEFINE_PER_CPU(struct sched_domain *, sd_asym_packing); +DEFINE_PER_CPU(struct sched_domain *, sd_asym_cpucapacity); DEFINE_STATIC_KEY_FALSE(sched_asym_cpucapacity); static void update_top_cache_domain(int cpu) @@ -534,7 +535,10 @@ static void update_top_cache_domain(int cpu) rcu_assign_pointer(per_cpu(sd_numa, cpu), sd); sd = highest_flag_domain(cpu, SD_ASYM_PACKING); - rcu_assign_pointer(per_cpu(sd_asym, cpu), sd); + rcu_assign_pointer(per_cpu(sd_asym_packing, cpu), sd); + + sd = lowest_flag_domain(cpu, SD_ASYM_CPUCAPACITY); + rcu_assign_pointer(per_cpu(sd_asym_cpucapacity, cpu), sd); } /* From e84df0ebf431bf16ea04498c75bcaa05abfa46ad Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 21 Jun 2018 15:34:57 +0100 Subject: [PATCH 0721/1276] FROMLIST: sched/topology: Disable EAS on inappropriate platforms Energy Aware Scheduling (EAS) in its current form is most relevant on platforms with asymmetric CPU topologies (e.g. Arm big.LITTLE) since this is where there is a lot of potential for saving energy through scheduling. This is particularly true since the Energy Model only includes the active power costs of CPUs, hence not providing enough data to compare packing-vs-spreading strategies. As such, disable EAS on root domains where the SD_ASYM_CPUCAPACITY flag is not set. While at it, disable EAS on systems where the complexity of the Energy Model is too high since that could lead to unacceptable scheduling overhead. All in all, EAS can be used on a root domain if and only if: 1. the ENERGY_AWARE sched_feat is enabled; 2. the root domain has an asymmetric CPU capacity topology; 3. the complexity of the root domain's EM is low enough to keep scheduling overheads low. Change-Id: I51ca817b328e1990790b5d801e05fc844199ac9c cc: Ingo Molnar cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-9-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/topology.c | 50 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index c63a6dee1088..0e83747897b4 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -270,12 +270,45 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) free_pd(pd); } +/* + * EAS can be used on a root domain if it meets all the following conditions: + * 1. the ENERGY_AWARE sched_feat is enabled; + * 2. the SD_ASYM_CPUCAPACITY flag is set in the sched_domain hierarchy. + * 3. the EM complexity is low enough to keep scheduling overheads low; + * + * The complexity of the Energy Model is defined as: + * + * C = nr_pd * (nr_cpus + nr_cs) + * + * with parameters defined as: + * - nr_pd: the number of performance domains + * - nr_cpus: the number of CPUs + * - nr_cs: the sum of the number of capacity states of all performance + * domains (for example, on a system with 2 performance domains, + * with 10 capacity states each, nr_cs = 2 * 10 = 20). + * + * It is generally not a good idea to use such a model in the wake-up path on + * very complex platforms because of the associated scheduling overheads. The + * arbitrary constraint below prevents that. It makes EAS usable up to 16 CPUs + * with per-CPU DVFS and less than 8 capacity states each, for example. + */ +#define EM_MAX_COMPLEXITY 2048 + static void build_perf_domains(const struct cpumask *cpu_map) { + int i, nr_pd = 0, nr_cs = 0, nr_cpus = cpumask_weight(cpu_map); struct perf_domain *pd = NULL, *tmp; int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; - int i; + + /* EAS is enabled for asymmetric CPU capacity topologies. */ + if (!per_cpu(sd_asym_cpucapacity, cpu)) { + if (sched_debug()) { + pr_info("rd %*pbl: CPUs do not have asymmetric capacities\n", + cpumask_pr_args(cpu_map)); + } + goto free; + } for_each_cpu(i, cpu_map) { /* Skip already covered CPUs. */ @@ -288,6 +321,21 @@ static void build_perf_domains(const struct cpumask *cpu_map) goto free; tmp->next = pd; pd = tmp; + + /* + * Count performance domains and capacity states for the + * complexity check. + */ + nr_pd++; + nr_cs += em_pd_nr_cap_states(pd->obj); + } + + /* Bail out if the Energy Model complexity is too high. */ + if (nr_pd * (nr_cs + nr_cpus) > EM_MAX_COMPLEXITY) { + if (sched_debug()) + pr_info("rd %*pbl: EM complexity is too high\n ", + cpumask_pr_args(cpu_map)); + goto free; } perf_domain_debug(cpu_map, pd); From 3875fd9271ee10400bfb18e28232daeb19aa255e Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 18 Jul 2018 12:09:49 +0100 Subject: [PATCH 0722/1276] FROMLIST: sched/fair: Clean-up update_sg_lb_stats parameters In preparation for the introduction of a new root domain flag which can be set during load balance (the 'overutilized' flag), clean-up the set of parameters passed to update_sg_lb_stats(). More specifically, the 'local_group' and 'local_idx' parameters can be removed since they can easily be reconstructed from within the function. While at it, transform the 'overload' parameter into a flag stored in the 'sg_status' parameter hence facilitating the definition of new flags when needed. Change-Id: I0a7b6766152f8c7037dde2485eee3f498cbc49c1 Cc: Ingo Molnar Cc: Peter Zijlstra Suggested-by: Peter Zijlstra Suggested-by: Valentin Schneider (Applied from https://lore.kernel.org/lkml/20180912091309.7551-10-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 27 +++++++++++---------------- kernel/sched/sched.h | 3 +++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 61e59c26e88c..601452cfe5a9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7885,16 +7885,16 @@ static bool update_nohz_stats(struct rq *rq, bool force) * update_sg_lb_stats - Update sched_group's statistics for load balancing. * @env: The load balancing environment. * @group: sched_group whose statistics are to be updated. - * @load_idx: Load index of sched_domain of this_cpu for load calc. - * @local_group: Does group contain this_cpu. * @sgs: variable to hold the statistics for this group. - * @overload: Indicate pullable load (e.g. >1 runnable task). + * @sg_status: Holds flag indicating the status of the sched_group */ static inline void update_sg_lb_stats(struct lb_env *env, - struct sched_group *group, int load_idx, - int local_group, struct sg_lb_stats *sgs, - bool *overload) + struct sched_group *group, + struct sg_lb_stats *sgs, + int *sg_status) { + int local_group = cpumask_test_cpu(env->dst_cpu, sched_group_span(group)); + int load_idx = get_sd_load_idx(env->sd, env->idle); unsigned long load; int i, nr_running; @@ -7918,7 +7918,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, nr_running = rq->nr_running; if (nr_running > 1) - *overload = true; + *sg_status |= SG_OVERLOAD; #ifdef CONFIG_NUMA_BALANCING sgs->nr_numa_running += rq->nr_numa_running; @@ -7934,7 +7934,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (env->sd->flags & SD_ASYM_CPUCAPACITY && sgs->group_misfit_task_load < rq->misfit_task_load) { sgs->group_misfit_task_load = rq->misfit_task_load; - *overload = 1; + *sg_status |= SG_OVERLOAD; } } @@ -8079,17 +8079,14 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd struct sched_group *sg = env->sd->groups; struct sg_lb_stats *local = &sds->local_stat; struct sg_lb_stats tmp_sgs; - int load_idx; - bool overload = false; bool prefer_sibling = child && child->flags & SD_PREFER_SIBLING; + int sg_status = 0; #ifdef CONFIG_NO_HZ_COMMON if (env->idle == CPU_NEWLY_IDLE && READ_ONCE(nohz.has_blocked)) env->flags |= LBF_NOHZ_STATS; #endif - load_idx = get_sd_load_idx(env->sd, env->idle); - do { struct sg_lb_stats *sgs = &tmp_sgs; int local_group; @@ -8104,8 +8101,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd update_group_capacity(env->sd, env->dst_cpu); } - update_sg_lb_stats(env, sg, load_idx, local_group, sgs, - &overload); + update_sg_lb_stats(env, sg, sgs, &sg_status); if (local_group) goto next_group; @@ -8155,8 +8151,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (!env->sd->parent) { /* update overload indicator if we are at root domain */ - if (READ_ONCE(env->dst_rq->rd->overload) != overload) - WRITE_ONCE(env->dst_rq->rd->overload, overload); + WRITE_ONCE(env->dst_rq->rd->overload, sg_status & SG_OVERLOAD); } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b71d3387ef60..8d8db38aff0b 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -709,6 +709,9 @@ struct perf_domain { struct rcu_head rcu; }; +/* Scheduling group status flags */ +#define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ + /* * We add the notion of a root-domain which will be used to define per-domain * variables. Each exclusive cpuset essentially defines an island domain by From 461d54bb3a7b5a6f28a5fa26a1b4804280aa2243 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Sat, 9 May 2015 16:49:57 +0100 Subject: [PATCH 0723/1276] FROMLIST: sched: Add over-utilization/tipping point indicator Energy-aware scheduling is only meant to be active while the system is _not_ over-utilized. That is, there are spare cycles available to shift tasks around based on their actual utilization to get a more energy-efficient task distribution without depriving any tasks. When above the tipping point task placement is done the traditional way based on load_avg, spreading the tasks across as many cpus as possible based on priority scaled load to preserve smp_nice. Below the tipping point we want to use util_avg instead. We need to define a criteria for when we make the switch. The util_avg for each cpu converges towards 100% regardless of how many additional tasks we may put on it. If we define over-utilized as: sum_{cpus}(rq.cfs.avg.util_avg) + margin > sum_{cpus}(rq.capacity) some individual cpus may be over-utilized running multiple tasks even when the above condition is false. That should be okay as long as we try to spread the tasks out to avoid per-cpu over-utilization as much as possible and if all tasks have the _same_ priority. If the latter isn't true, we have to consider priority to preserve smp_nice. For example, we could have n_cpus nice=-10 util_avg=55% tasks and n_cpus/2 nice=0 util_avg=60% tasks. Balancing based on util_avg we are likely to end up with nice=-10 tasks sharing cpus and nice=0 tasks getting their own as we 1.5*n_cpus tasks in total and 55%+55% is less over-utilized than 55%+60% for those cpus that have to be shared. The system utilization is only 85% of the system capacity, but we are breaking smp_nice. To be sure not to break smp_nice, we have defined over-utilization conservatively as when any cpu in the system is fully utilized at its highest frequency instead: cpu_rq(any).cfs.avg.util_avg + margin > cpu_rq(any).capacity IOW, as soon as one cpu is (nearly) 100% utilized, we switch to load_avg to factor in priority to preserve smp_nice. With this definition, we can skip periodic load-balance as no cpu has an always-running task when the system is not over-utilized. All tasks will be periodic and we can balance them at wake-up. This conservative condition does however mean that some scenarios that could benefit from energy-aware decisions even if one cpu is fully utilized would not get those benefits. For systems where some cpus might have reduced capacity on some cpus (RT-pressure and/or big.LITTLE), we want periodic load-balance checks as soon a just a single cpu is fully utilized as it might one of those with reduced capacity and in that case we want to migrate it. Change-Id: Id230f344c32cb077a578d4c1aedc532f1470a3e3 cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen [ - Added a comment explaining why new tasks are not accounted during overutilization detection - ANDROID: applied from https://lore.kernel.org/lkml/20180912091309.7551-11-quentin.perret@arm.com/] Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 59 ++++++++++++++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 4 +++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 601452cfe5a9..633e586d7288 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5095,6 +5095,24 @@ static inline void hrtick_update(struct rq *rq) } #endif +#ifdef CONFIG_SMP +static inline unsigned long cpu_util(int cpu); +static unsigned long capacity_of(int cpu); + +static inline bool cpu_overutilized(int cpu) +{ + return (capacity_of(cpu) * 1024) < (cpu_util(cpu) * capacity_margin); +} + +static inline void update_overutilized_status(struct rq *rq) +{ + if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) + WRITE_ONCE(rq->rd->overutilized, SG_OVERUTILIZED); +} +#else +static inline void update_overutilized_status(struct rq *rq) { } +#endif + /* * The enqueue_task method is called before nr_running is * increased. Here we update the fair scheduling stats and @@ -5152,8 +5170,26 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) update_cfs_group(se); } - if (!se) + if (!se) { add_nr_running(rq, 1); + /* + * Since new tasks are assigned an initial util_avg equal to + * half of the spare capacity of their CPU, tiny tasks have the + * ability to cross the overutilized threshold, which will + * result in the load balancer ruining all the task placement + * done by EAS. As a way to mitigate that effect, do not account + * for the first enqueue operation of new tasks during the + * overutilized flag detection. + * + * A better way of solving this problem would be to wait for + * the PELT signals of tasks to converge before taking them + * into account, but that is not straightforward to implement, + * and the following generally works well enough in practice. + */ + if (flags & ENQUEUE_WAKEUP) + update_overutilized_status(rq); + + } hrtick_update(rq); } @@ -7920,6 +7956,9 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (nr_running > 1) *sg_status |= SG_OVERLOAD; + if (cpu_overutilized(i)) + *sg_status |= SG_OVERUTILIZED; + #ifdef CONFIG_NUMA_BALANCING sgs->nr_numa_running += rq->nr_numa_running; sgs->nr_preferred_running += rq->nr_preferred_running; @@ -8150,8 +8189,15 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd env->fbq_type = fbq_classify_group(&sds->busiest_stat); if (!env->sd->parent) { + struct root_domain *rd = env->dst_rq->rd; + /* update overload indicator if we are at root domain */ - WRITE_ONCE(env->dst_rq->rd->overload, sg_status & SG_OVERLOAD); + WRITE_ONCE(rd->overload, sg_status & SG_OVERLOAD); + + /* Update over-utilization (tipping point, U >= 0) indicator */ + WRITE_ONCE(rd->overutilized, sg_status & SG_OVERUTILIZED); + } else if (sg_status & SG_OVERUTILIZED) { + WRITE_ONCE(env->dst_rq->rd->overutilized, SG_OVERUTILIZED); } } @@ -8378,6 +8424,14 @@ static struct sched_group *find_busiest_group(struct lb_env *env) * this level. */ update_sd_lb_stats(env, &sds); + + if (sched_feat(ENERGY_AWARE)) { + struct root_domain *rd = env->dst_rq->rd; + + if (rcu_dereference(rd->pd) && !READ_ONCE(rd->overutilized)) + goto out_balanced; + } + local = &sds.local_stat; busiest = &sds.busiest_stat; @@ -9769,6 +9823,7 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) task_tick_numa(rq, curr); update_misfit_status(curr, rq); + update_overutilized_status(task_rq(curr)); } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8d8db38aff0b..3e3b780c9404 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -711,6 +711,7 @@ struct perf_domain { /* Scheduling group status flags */ #define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ +#define SG_OVERUTILIZED 0x2 /* One or more CPUs are over-utilized. */ /* * We add the notion of a root-domain which will be used to define per-domain @@ -734,6 +735,9 @@ struct root_domain { */ int overload; + /* Indicate one or more cpus over-utilized (tipping point) */ + int overutilized; + /* * The bit corresponding to a CPU gets set here if such CPU has more * than one runnable -deadline task (as it is below for RT tasks). From 479e6f04a37d9c395efead5a372c189279263797 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 10 Nov 2017 11:20:03 +0000 Subject: [PATCH 0724/1276] FROMLIST: sched/fair: Introduce an energy estimation helper function In preparation for the definition of an energy-aware wakeup path, introduce a helper function to estimate the consequence on system energy when a specific task wakes-up on a specific CPU. compute_energy() estimates the capacity state to be reached by all performance domains and estimates the consumption of each online CPU according to its Energy Model and its percentage of busy time. Change-Id: Id7f3cc5acbcef42dc9fa5b6045099cbeee87612c Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-12-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 633e586d7288..af59d151299b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6357,6 +6357,83 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) return !task_fits_capacity(p, min_cap); } +/* + * Predicts what cpu_util(@cpu) would return if @p was migrated (and enqueued) + * to @dst_cpu. + */ +static unsigned long cpu_util_next(int cpu, struct task_struct *p, int dst_cpu) +{ + struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs; + unsigned long util_est, util = READ_ONCE(cfs_rq->avg.util_avg); + + /* + * If @p migrates from @cpu to another, remove its contribution. Or, + * if @p migrates from another CPU to @cpu, add its contribution. In + * the other cases, @cpu is not impacted by the migration, so the + * util_avg should already be correct. + */ + if (task_cpu(p) == cpu && dst_cpu != cpu) + util = max_t(long, util - task_util(p), 0); + else if (task_cpu(p) != cpu && dst_cpu == cpu) + util += task_util(p); + + if (sched_feat(UTIL_EST)) { + util_est = READ_ONCE(cfs_rq->avg.util_est.enqueued); + + /* + * During wake-up, the task isn't enqueued yet and doesn't + * appear in the cfs_rq->avg.util_est.enqueued of any rq, + * so just add it (if needed) to "simulate" what will be + * cpu_util() after the task has been enqueued. + */ + if (dst_cpu == cpu) + util_est += _task_util_est(p); + + util = max(util, util_est); + } + + return min_t(unsigned long, util, capacity_orig_of(cpu)); +} + +/* + * compute_energy(): Estimates the energy that would be consumed if @p was + * migrated to @dst_cpu. compute_energy() predicts what will be the utilization + * landscape of the * CPUs after the task migration, and uses the Energy Model + * to compute what would be the energy if we decided to actually migrate that + * task. + */ +static long compute_energy(struct task_struct *p, int dst_cpu, + struct perf_domain *pd) +{ + long util, max_util, sum_util, energy = 0; + int cpu; + + while (pd) { + max_util = sum_util = 0; + /* + * The capacity state of CPUs of the current rd can be driven by + * CPUs of another rd if they belong to the same performance + * domain. So, account for the utilization of these CPUs too + * by masking pd with cpu_online_mask instead of the rd span. + * + * If an entire performance domain is outside of the current rd, + * it will not appear in its pd list and will not be accounted + * by compute_energy(). + */ + for_each_cpu_and(cpu, perf_domain_span(pd), cpu_online_mask) { + util = cpu_util_next(cpu, p, dst_cpu); + util = schedutil_freq_util(cpu, util, ENERGY_UTIL); + max_util = max(util, max_util); + sum_util += util; + } + + energy += em_pd_energy(pd->obj, max_util, sum_util); + pd = pd->next; + } + + return energy; +} + /* * select_task_rq_fair: Select target runqueue for the waking task in domains * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, From 1bedb03badc5ea6be8e6bfd563f029990483ab03 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 10 Nov 2017 12:17:34 +0000 Subject: [PATCH 0725/1276] FROMLIST: sched/fair: Select an energy-efficient CPU on task wake-up If an Energy Model (EM) is available and if the system isn't overutilized, re-route waking tasks into an energy-aware placement algorithm. The selection of an energy-efficient CPU for a task is achieved by estimating the impact on system-level active energy resulting from the placement of the task on the CPU with the highest spare capacity in each performance domain. This strategy spreads tasks in a performance domain and avoids overly aggressive task packing. The best CPU energy-wise is then selected if it saves a large enough amount of energy with respect to prev_cpu. Although it has already shown significant benefits on some existing targets, this approach cannot scale to platforms with numerous CPUs. This is an attempt to do something useful as writing a fast heuristic that performs reasonably well on a broad spectrum of architectures isn't an easy task. As such, the scope of usability of the energy-aware wake-up path is restricted to systems with the SD_ASYM_CPUCAPACITY flag set, and where the EM isn't too complex. Change-Id: Idf91ad38aef203d469751497f1169e26107858f5 Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-13-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 139 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index af59d151299b..0121e2879188 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6434,6 +6434,114 @@ static long compute_energy(struct task_struct *p, int dst_cpu, return energy; } +/* + * find_energy_efficient_cpu(): Find most energy-efficient target CPU for the + * waking task. find_energy_efficient_cpu() looks for the CPU with maximum + * spare capacity in each performance domain and uses it as a potential + * candidate to execute the task. Then, it uses the Energy Model to figure + * out which of the CPU candidates is the most energy-efficient. + * + * The rationale for this heuristic is as follows. In a performance domain, + * all the most energy efficient CPU candidates (according to the Energy + * Model) are those for which we'll request a low frequency. When there are + * several CPUs for which the frequency request will be the same, we don't + * have enough data to break the tie between them, because the Energy Model + * only includes active power costs. With this model, if we assume that + * frequency requests follow utilization (e.g. using schedutil), the CPU with + * the maximum spare capacity in a performance domain is guaranteed to be among + * the best candidates of the performance domain. + * + * In practice, it could be preferable from an energy standpoint to pack + * small tasks on a CPU in order to let other CPUs go in deeper idle states, + * but that could also hurt our chances to go cluster idle, and we have no + * ways to tell with the current Energy Model if this is actually a good + * idea or not. So, find_energy_efficient_cpu() basically favors + * cluster-packing, and spreading inside a cluster. That should at least be + * a good thing for latency, and this is consistent with the idea that most + * of the energy savings of EAS come from the asymmetry of the system, and + * not so much from breaking the tie between identical CPUs. That's also the + * reason why EAS is enabled in the topology code only for systems where + * SD_ASYM_CPUCAPACITY is set. + */ +static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, + struct perf_domain *pd) +{ + unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; + int cpu, best_energy_cpu = prev_cpu; + struct perf_domain *head = pd; + unsigned long cpu_cap, util; + struct sched_domain *sd; + + sync_entity_load_avg(&p->se); + + if (!task_util_est(p)) + return prev_cpu; + + /* + * Energy-aware wake-up happens on the lowest sched_domain starting + * from sd_asym_cpucapacity spanning over this_cpu and prev_cpu. + */ + sd = rcu_dereference(*this_cpu_ptr(&sd_asym_cpucapacity)); + while (sd && !cpumask_test_cpu(prev_cpu, sched_domain_span(sd))) + sd = sd->parent; + if (!sd) + return prev_cpu; + + while (pd) { + unsigned long cur_energy, spare_cap, max_spare_cap = 0; + int max_spare_cap_cpu = -1; + + for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { + if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) + continue; + + /* Skip CPUs that will be overutilized. */ + util = cpu_util_next(cpu, p, cpu); + cpu_cap = capacity_of(cpu); + if (cpu_cap * 1024 < util * capacity_margin) + continue; + + /* Always use prev_cpu as a candidate. */ + if (cpu == prev_cpu) { + prev_energy = compute_energy(p, prev_cpu, head); + if (prev_energy < best_energy) + best_energy = prev_energy; + continue; + } + + /* + * Find the CPU with the maximum spare capacity in + * the performance domain + */ + spare_cap = cpu_cap - util; + if (spare_cap > max_spare_cap) { + max_spare_cap = spare_cap; + max_spare_cap_cpu = cpu; + } + } + + /* Evaluate the energy impact of using this CPU. */ + if (max_spare_cap_cpu >= 0) { + cur_energy = compute_energy(p, max_spare_cap_cpu, head); + if (cur_energy < best_energy) { + best_energy = cur_energy; + best_energy_cpu = max_spare_cap_cpu; + } + } + pd = pd->next; + } + + /* + * Pick the best CPU if prev_cpu cannot be used, or if it saves at + * least 6% of the energy used by prev_cpu. + */ + if (prev_energy == ULONG_MAX || + (prev_energy - best_energy) > (prev_energy >> 4)) + return best_energy_cpu; + + return prev_cpu; +} + /* * select_task_rq_fair: Select target runqueue for the waking task in domains * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, @@ -6455,13 +6563,37 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f int want_affine = 0; int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING); + rcu_read_lock(); if (sd_flag & SD_BALANCE_WAKE) { record_wakee(p); - want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) - && cpumask_test_cpu(cpu, &p->cpus_allowed); + + /* + * Forkees are not accepted in the energy-aware wake-up path + * because they don't have any useful utilization data yet and + * it's not possible to forecast their impact on energy + * consumption. Consequently, they will be placed by + * find_idlest_cpu() on the least loaded CPU, which might turn + * out to be energy-inefficient in some use-cases. The + * alternative would be to bias new tasks towards specific types + * of CPUs first, or to try to infer their util_avg from the + * parent task, but those heuristics could hurt other use-cases + * too. So, until someone finds a better way to solve this, + * let's keep things simple by re-using the existing slow path. + */ + if (sched_feat(ENERGY_AWARE)) { + struct root_domain *rd = cpu_rq(cpu)->rd; + struct perf_domain *pd = rcu_dereference(rd->pd); + + if (pd && !READ_ONCE(rd->overutilized)) { + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd); + goto unlock; + } + } + + want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && + cpumask_test_cpu(cpu, &p->cpus_allowed); } - rcu_read_lock(); for_each_domain(cpu, tmp) { if (!(tmp->flags & SD_LOAD_BALANCE)) break; @@ -6496,6 +6628,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f if (want_affine) current->recent_used_cpu = cpu; } +unlock: rcu_read_unlock(); return new_cpu; From 3ab46b2278defc12a9158ab76d4d745b8efedca0 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 3 Aug 2018 11:19:36 +0100 Subject: [PATCH 0726/1276] FROMLIST: sched/topology: Make Energy Aware Scheduling depend on schedutil Energy Aware Scheduling (EAS) is designed with the assumption that frequencies of CPUs follow their utilization value. When using a CPUFreq governor other than schedutil, the chances of this assumption being true are small, if any. When schedutil is being used, EAS' predictions are at least consistent with the frequency requests. Although those requests have no guarantees to be honored by the hardware, they should at least guide DVFS in the right direction and provide some hope in regards to the EAS model being accurate. To make sure EAS is only used in a sane configuration, create a strong dependency on schedutil being used. Since having sugov compiled-in does not provide that guarantee, make CPUFreq call a scheduler function on on governor changes hence letting it rebuild the scheduling domains, check the governors of the online CPUs, and enable/disable EAS accordingly. Change-Id: I983ab94a22fce45376f50a892653a39d5c8cf5b1 Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-14-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq.c | 2 ++ include/linux/sched/cpufreq.h | 9 ++++++++ kernel/sched/cpufreq_schedutil.c | 37 ++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 4 +--- kernel/sched/topology.c | 23 ++++++++++++++++---- 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 98b5bac02dff..e1f8e760a5e8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2280,6 +2281,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, ret = cpufreq_start_governor(policy); if (!ret) { pr_debug("cpufreq: governor change\n"); + sched_cpufreq_governor_change(policy, old_gov); return 0; } cpufreq_exit_governor(policy); diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h index afa940cd50dc..a2ead52feb17 100644 --- a/include/linux/sched/cpufreq.h +++ b/include/linux/sched/cpufreq.h @@ -2,6 +2,7 @@ #ifndef _LINUX_SCHED_CPUFREQ_H #define _LINUX_SCHED_CPUFREQ_H +#include #include /* @@ -28,4 +29,12 @@ static inline unsigned long map_util_freq(unsigned long util, } #endif /* CONFIG_CPU_FREQ */ +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) +void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov); +#else +static inline void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov) { } +#endif + #endif /* _LINUX_SCHED_CPUFREQ_H */ diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 8356cb0072a6..d2236d1166f4 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -632,7 +632,7 @@ static struct kobj_type sugov_tunables_ktype = { /********************** cpufreq governor interface *********************/ -static struct cpufreq_governor schedutil_gov; +struct cpufreq_governor schedutil_gov; static struct sugov_policy *sugov_policy_alloc(struct cpufreq_policy *policy) { @@ -891,7 +891,7 @@ static void sugov_limits(struct cpufreq_policy *policy) sg_policy->need_freq_update = true; } -static struct cpufreq_governor schedutil_gov = { +struct cpufreq_governor schedutil_gov = { .name = "schedutil", .owner = THIS_MODULE, .dynamic_switching = true, @@ -914,3 +914,36 @@ static int __init sugov_register(void) return cpufreq_register_governor(&schedutil_gov); } fs_initcall(sugov_register); + +#ifdef CONFIG_ENERGY_MODEL +extern bool sched_energy_update; +static DEFINE_MUTEX(rebuild_sd_mutex); + +static void rebuild_sd_workfn(struct work_struct *work) +{ + mutex_lock(&rebuild_sd_mutex); + sched_energy_update = true; + rebuild_sched_domains(); + sched_energy_update = false; + mutex_unlock(&rebuild_sd_mutex); +} +static DECLARE_WORK(rebuild_sd_work, rebuild_sd_workfn); + +/* + * EAS shouldn't be attempted without sugov, so rebuild the sched_domains + * on governor changes to make sure the scheduler knows about it. + */ +void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov) +{ + if (old_gov == &schedutil_gov || policy->governor == &schedutil_gov) { + /* + * When called from the cpufreq_register_driver() path, the + * cpu_hotplug_lock is already held, so use a work item to + * avoid nested locking in rebuild_sched_domains(). + */ + schedule_work(&rebuild_sd_work); + } + +} +#endif diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 3e3b780c9404..ce83194c274a 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2267,10 +2267,8 @@ unsigned long scale_irq_capacity(unsigned long util, unsigned long irq, unsigned } #endif -#ifdef CONFIG_SMP -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) #define perf_domain_span(pd) (to_cpumask(((pd)->obj->cpus))) #else #define perf_domain_span(pd) NULL #endif -#endif diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 0e83747897b4..8f7baceb150d 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -201,7 +201,9 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) return 1; } -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) +bool sched_energy_update; + static void free_pd(struct perf_domain *pd) { struct perf_domain *tmp; @@ -275,6 +277,7 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) * 1. the ENERGY_AWARE sched_feat is enabled; * 2. the SD_ASYM_CPUCAPACITY flag is set in the sched_domain hierarchy. * 3. the EM complexity is low enough to keep scheduling overheads low; + * 4. schedutil is driving the frequency of all CPUs of the rd; * * The complexity of the Energy Model is defined as: * @@ -294,12 +297,15 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) */ #define EM_MAX_COMPLEXITY 2048 +extern struct cpufreq_governor schedutil_gov; static void build_perf_domains(const struct cpumask *cpu_map) { int i, nr_pd = 0, nr_cs = 0, nr_cpus = cpumask_weight(cpu_map); struct perf_domain *pd = NULL, *tmp; int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; + struct cpufreq_policy *policy; + struct cpufreq_governor *gov; /* EAS is enabled for asymmetric CPU capacity topologies. */ if (!per_cpu(sd_asym_cpucapacity, cpu)) { @@ -315,6 +321,15 @@ static void build_perf_domains(const struct cpumask *cpu_map) if (find_pd(pd, i)) continue; + /* Do not attempt EAS if schedutil is not being used. */ + policy = cpufreq_cpu_get(i); + if (!policy) + goto free; + gov = policy->governor; + cpufreq_cpu_put(policy); + if (gov != &schedutil_gov) + goto free; + /* Create the new pd and add it to the local list. */ tmp = pd_init(i); if (!tmp) @@ -357,7 +372,7 @@ static void build_perf_domains(const struct cpumask *cpu_map) } #else static void free_pd(struct perf_domain *pd) { } -#endif /* CONFIG_ENERGY_MODEL */ +#endif /* CONFIG_ENERGY_MODEL && CONFIG_CPU_FREQ_GOV_SCHEDUTIL*/ static void free_rootdomain(struct rcu_head *rcu) { @@ -2155,10 +2170,10 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], ; } -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) /* Build perf. domains: */ for (i = 0; i < ndoms_new; i++) { - for (j = 0; j < n; j++) { + for (j = 0; j < n && !sched_energy_update; j++) { if (cpumask_equal(doms_new[i], doms_cur[j]) && cpu_rq(cpumask_first(doms_cur[j]))->rd->pd) goto match3; From 625388e76d3b97ef7117c28a2aebcb3e89a815fc Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Thu, 2 Jul 2015 17:16:34 +0100 Subject: [PATCH 0727/1276] ANDROID: sched: Prevent unnecessary active balance of single task in sched group Scenarios with the busiest group having just one task and the local being idle on topologies with sched groups with different numbers of cpus manage to dodge all load-balance bailout conditions resulting the nr_balance_failed counter to be incremented. This eventually causes a pointless active migration of the task. This patch prevents this by not incrementing the counter when the busiest group only has one task. ASYM_PACKING migrations and migrations due to reduced capacity should still take place as these are explicitly captured by need_active_balance(). A better solution would be to not attempt the load-balance in the first place, but that requires significant changes to the order of bailout conditions and statistics gathering. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Change-Id: I28f69c72febe0211decbe77b7bc3e48839d3d7b3 --- kernel/sched/fair.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0121e2879188..a3a00ab1cbb9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7221,6 +7221,7 @@ struct lb_env { int new_dst_cpu; enum cpu_idle_type idle; long imbalance; + unsigned int src_grp_nr_running; /* The set of CPUs under consideration for load-balancing */ struct cpumask *cpus; @@ -8398,6 +8399,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (env->sd->flags & SD_NUMA) env->fbq_type = fbq_classify_group(&sds->busiest_stat); + env->src_grp_nr_running = sds->busiest_stat.sum_nr_running; + if (!env->sd->parent) { struct root_domain *rd = env->dst_rq->rd; @@ -9076,7 +9079,8 @@ static int load_balance(int this_cpu, struct rq *this_rq, * excessive cache_hot migrations and active balances. */ if (idle != CPU_NEWLY_IDLE) - sd->nr_balance_failed++; + if (env.src_grp_nr_running > 1) + sd->nr_balance_failed++; if (need_active_balance(&env)) { unsigned long flags; From eceef7830533e36b2e43cddab999fc0e3fdfbc4c Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Mon, 26 Jan 2015 19:47:28 +0000 Subject: [PATCH 0728/1276] ANDROID: sched: Enable idle balance to pull single task towards cpu with higher capacity We do not want to miss out on the ability to pull a single remaining task from a potential source cpu towards an idle destination cpu. Add an extra criteria to need_active_balance() to kick off active load balance if the source cpu is over-utilized and has lower capacity than the destination cpu. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Signed-off-by: Dietmar Eggemann Change-Id: Iea3b42b2a0f8d8a4252e42ba67cc33381a4a1075 --- kernel/sched/fair.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index a3a00ab1cbb9..e919c31562ab 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8861,6 +8861,13 @@ static int need_active_balance(struct lb_env *env) if (env->src_grp_type == group_misfit_task) return 1; + if ((capacity_of(env->src_cpu) < capacity_of(env->dst_cpu)) && + env->src_rq->cfs.h_nr_running == 1 && + cpu_overutilized(env->src_cpu) && + !cpu_overutilized(env->dst_cpu)) { + return 1; + } + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From b08261f8f586828b58f08b8a06a2f03be946e557 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 3 Jul 2018 10:35:03 +0100 Subject: [PATCH 0729/1276] ANDROID: arm, arm64: Enable kernel config options required for EAS arm and arm64: Add Cgroups support Add Energy Model Add CpuFreq governors and make schedutil default for arm: Add Cpuset support Add Scheduler autogroups Add DIE sched domain level Signed-off-by: Dietmar Eggemann Change-Id: Ib52a0bd27702c3f2c3d692e49c9c8e2fbbea2cf7 --- arch/arm/configs/multi_v7_defconfig | 16 ++++++++++++---- arch/arm64/configs/defconfig | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index fc33444e94f0..2721877d5a11 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -2,6 +2,12 @@ CONFIG_SYSVIPC=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y @@ -116,6 +122,7 @@ CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m CONFIG_SMP=y +CONFIG_SCHED_MC=y CONFIG_NR_CPUS=16 CONFIG_SECCOMP=y CONFIG_ARM_APPENDED_DTB=y @@ -124,10 +131,10 @@ CONFIG_KEXEC=y CONFIG_EFI=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m -CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_CPUFREQ_DT=y CONFIG_ARM_IMX6Q_CPUFREQ=y @@ -137,6 +144,7 @@ CONFIG_ARM_CPUIDLE=y CONFIG_ARM_ZYNQ_CPUIDLE=y CONFIG_ARM_EXYNOS_CPUIDLE=y CONFIG_KERNEL_MODE_NEON=y +CONFIG_ENERGY_MODEL=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index db8d364f8476..31fd24bff688 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -19,9 +19,13 @@ CONFIG_BLK_CGROUP=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_HUGETLB=y CONFIG_CPUSETS=y +CONFIG_CGROUPS=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CGROUP_SCHED=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_FREEZER=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y @@ -101,13 +105,15 @@ CONFIG_XEN=y CONFIG_COMPAT=y CONFIG_HIBERNATION=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y CONFIG_ARM_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_CPUFREQ_DT=y CONFIG_ACPI_CPPC_CPUFREQ=m From 266864f37b0948c64db15c9816d257e34f10f65e Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Sun, 18 Feb 2018 14:27:49 +0100 Subject: [PATCH 0730/1276] ANDROID: arm64: dts: juno: Add dynamic-power-coefficient properties Taken from commit cadf54148974 "arm64: dts: Add IPA parameters to soc thermal zone" wich also sets up SoC thermal zones and bind them to cpufreq cooling devices. We don't want this functionality right now. The commit is for example part of: git.linaro.org/landing-teams/working/arm/kernel-release.git lt_arm/ack-4.9-armlt-18.01 Signed-off-by: Dietmar Eggemann Change-Id: I7c23a58fa49b281ed5df2f60db0514a9b3b50c7b --- arch/arm64/boot/dts/arm/juno.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index 1fb5c5a0f32e..e3069e286256 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -98,6 +98,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <530>; }; A57_1: cpu@1 { @@ -115,6 +116,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <530>; }; A53_0: cpu@100 { @@ -132,6 +134,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_1: cpu@101 { @@ -149,6 +152,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_2: cpu@102 { @@ -166,6 +170,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_3: cpu@103 { @@ -183,6 +188,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A57_L2: l2-cache0 { From a1bf694c9d067c6f1e6a231009bfa49585a7776c Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 22 May 2018 17:01:09 +0100 Subject: [PATCH 0731/1276] ANDROID: arm64: dts: juno-r2: Add dynamic-power-coefficient properties Taken from commit cadf54148974 "arm64: dts: Add IPA parameters to soc thermal zone" wich also sets up SoC thermal zones and bind them to cpufreq cooling devices. We don't want this functionality right now. The commit is for example part of: git.linaro.org/landing-teams/working/arm/kernel-release.git lt_arm/ack-4.9-armlt-18.01 Signed-off-by: Dietmar Eggemann Change-Id: Id1a44fb7d222d59f7d44b5f55797d407513eb7e7 --- arch/arm64/boot/dts/arm/juno-r2.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts index ab77adb4f3c2..66f0ec79c864 100644 --- a/arch/arm64/boot/dts/arm/juno-r2.dts +++ b/arch/arm64/boot/dts/arm/juno-r2.dts @@ -99,6 +99,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <450>; }; A72_1: cpu@1 { @@ -116,6 +117,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <450>; }; A53_0: cpu@100 { @@ -133,6 +135,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_1: cpu@101 { @@ -150,6 +153,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_2: cpu@102 { @@ -167,6 +171,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_3: cpu@103 { @@ -184,6 +189,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A72_L2: l2-cache0 { From 78be9ddae72927ac65d40cfab8176e5617f1142a Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 5 Jun 2018 10:35:18 +0100 Subject: [PATCH 0732/1276] ANDROID: arm: dts: vexpress-v2p-ca15_a7: Add dynamic-power-coefficient properties The values are computed from measuring energy over a 10 secs sysbench workload running at each frequency and affine to 1 or 2 A15's as well as 1 or 2 or 3 A7's. The Power values for individual cpus are calculated from the Energy values divided by workload runtime by taking the difference of the energy values between n+1 and n cpus. P [mW] = C * freq [Mhz] * voltage [mV] * voltage [mV] / 1000000000 C = P [mW] / (freq [Mhz] * voltage [mV] * voltage [mV]) * 1000000000 The actual C (dynamic-power-coefficient) value is the mean value out of all the C values of the OPP's. A15: freq power voltage dyn_pwr_coef [MhZ] [mW] [mV] 0 500.0 534.57550 900 1319.939506 1 600.0 547.15468 900 1125.832675 2 700.0 572.22060 900 1009.207407 3 800.0 607.76592 900 937.910370 4 900.0 648.50552 900 889.582332 5 1000.0 693.86776 900 856.626864 6 1100.0 916.51314 975 876.469442 7 1200.0 1198.57566 1050 905.952880 mean: 990 A7: freq power voltage dyn_pwr_coef [MhZ] [mW] [mV] 0 350.0 40.17430 900 141.708289 1 400.0 42.68700 900 131.750000 2 500.0 54.25716 900 133.968296 3 600.0 64.09914 900 131.891235 4 700.0 74.09736 900 130.683175 5 800.0 82.69694 900 127.618735 6 900.0 113.71386 975 132.911225 7 1000.0 144.94124 1050 131.465977 mean: 133 The ratio between A15 and A7 is 990/113 = 7.44 This value (7.44) is very close to mean ratio between the power value of A15 an A7 of the per sched-domain Energy Model (7.96): mV Mhz MhZ old EM ratio A7 A15 core power 900 350 500 6997/1024 6.83 900 400 600 5177/761 6.80 900 500 700 3846/549 7.01 900 600 800 3524/447 7.88 900 700 900 3125/407 7.68 900 800 1000 2756/334 8.25 975 900 1100 2312/275 8.40 1050 1000 1200 2021/187 10.80 mean: 7.96 Signed-off-by: Dietmar Eggemann Change-Id: I93ef375d05ff769481a07f2c74f061e307cb14d4 --- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index ac6b90e9d806..75663ed4817f 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -42,6 +42,7 @@ cci-control-port = <&cci_control1>; cpu-idle-states = <&CLUSTER_SLEEP_BIG>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <990>; }; cpu1: cpu@1 { @@ -51,6 +52,7 @@ cci-control-port = <&cci_control1>; cpu-idle-states = <&CLUSTER_SLEEP_BIG>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <990>; }; cpu2: cpu@2 { @@ -60,6 +62,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; cpu3: cpu@3 { @@ -69,6 +72,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; cpu4: cpu@4 { @@ -78,6 +82,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; idle-states { From ff99f38d9bb46231c988d042f3fa121b2bbb4e7c Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:07:59 +0100 Subject: [PATCH 0733/1276] FROMLIST: cpufreq: dt: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. Call this API from the cpufreq-dt driver with an estimation of the power as P = C * V^2 * f with C, V, and f respectively the capacitance of the CPU and the voltage and frequency of the OPP. The CPU capacitance is read from the "dynamic-power-coefficient" DT binding (originally introduced for thermal/IPA), and the voltage and frequency values from PM_OPP. Change-Id: Id7b79ae3aafcc53574b850cb91a25240ebffbdd4 Cc: "Rafael J. Wysocki" Cc: Viresh Kumar (Removed "OPTIONAL" label from title. Applied from https://lore.kernel.org/lkml/20180912091309.7551-15-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq-dt.c | 48 +++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 0a9ebf00be46..15ac9754afa2 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -149,8 +150,50 @@ static int resources_available(void) return 0; } +static int __maybe_unused of_est_power(unsigned long *mW, unsigned long *KHz, + int cpu) +{ + unsigned long mV, Hz, MHz; + struct device *cpu_dev; + struct dev_pm_opp *opp; + struct device_node *np; + u32 cap; + u64 tmp; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return -ENODEV; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return -EINVAL; + + if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) + return -EINVAL; + + Hz = *KHz * 1000; + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + if (IS_ERR(opp)) + return -EINVAL; + + mV = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); + if (!mV) + return -EINVAL; + + MHz = Hz / 1000000; + tmp = (u64)cap * mV * mV * MHz; + do_div(tmp, 1000000000); + + *mW = (unsigned long)tmp; + *KHz = Hz / 1000; + + return 0; +} + static int cpufreq_init(struct cpufreq_policy *policy) { + struct em_data_callback em_cb = EM_DATA_CB(of_est_power); struct cpufreq_frequency_table *freq_table; struct opp_table *opp_table = NULL; struct private_data *priv; @@ -159,7 +202,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) unsigned int transition_latency; bool fallback = false; const char *name; - int ret; + int ret, nr_opp; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -226,6 +269,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; if (fallback) { cpumask_setall(policy->cpus); @@ -278,6 +322,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = transition_latency; policy->dvfs_possible_from_any_cpu = true; + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_cpufreq_table: From be1e8c05cf844f33b9c46b41dcbe7081ab90d97b Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 14:49:58 +0100 Subject: [PATCH 0734/1276] ANDROID: PM / OPP: cpufreq-dt: Move power estimation function The cpufreq-dt driver has a function estimating the power of CPUs using the dynamic-power-coefficient DT binding and the parameters of PM_OPP. Since this function can be useful to other drivers, relocate it to PM_OPP which is already a dependency anyway. Change-Id: I34f8f9cd9433c622c82f23f32ae9968a096a4390 Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq-dt.c | 43 +----------------------------------- drivers/opp/of.c | 41 ++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 5 +++++ 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 15ac9754afa2..83ad2a60991c 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -150,50 +150,9 @@ static int resources_available(void) return 0; } -static int __maybe_unused of_est_power(unsigned long *mW, unsigned long *KHz, - int cpu) -{ - unsigned long mV, Hz, MHz; - struct device *cpu_dev; - struct dev_pm_opp *opp; - struct device_node *np; - u32 cap; - u64 tmp; - - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) - return -ENODEV; - - np = of_node_get(cpu_dev->of_node); - if (!np) - return -EINVAL; - - if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) - return -EINVAL; - - Hz = *KHz * 1000; - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); - if (IS_ERR(opp)) - return -EINVAL; - - mV = dev_pm_opp_get_voltage(opp) / 1000; - dev_pm_opp_put(opp); - if (!mV) - return -EINVAL; - - MHz = Hz / 1000000; - tmp = (u64)cap * mV * mV * MHz; - do_div(tmp, 1000000000); - - *mW = (unsigned long)tmp; - *KHz = Hz / 1000; - - return 0; -} - static int cpufreq_init(struct cpufreq_policy *policy) { - struct em_data_callback em_cb = EM_DATA_CB(of_est_power); + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); struct cpufreq_frequency_table *freq_table; struct opp_table *opp_table = NULL; struct private_data *priv; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 7af0ddec936b..34157eac789d 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -777,3 +777,44 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) return of_node_get(opp->np); } EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); + +int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + unsigned long mV, Hz, MHz; + struct device *cpu_dev; + struct dev_pm_opp *opp; + struct device_node *np; + u32 cap; + u64 tmp; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return -ENODEV; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return -EINVAL; + + if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) + return -EINVAL; + + Hz = *KHz * 1000; + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + if (IS_ERR(opp)) + return -EINVAL; + + mV = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); + if (!mV) + return -EINVAL; + + MHz = Hz / 1000000; + tmp = (u64)cap * mV * mV * MHz; + do_div(tmp, 1000000000); + + *mW = (unsigned long)tmp; + *KHz = Hz / 1000; + + return 0; +} +EXPORT_SYMBOL_GPL(of_dev_pm_opp_get_cpu_power); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 099b31960dec..11dbffc7f889 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -301,6 +301,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, struct device_node *np); struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); +int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu); #else static inline int dev_pm_opp_of_add_table(struct device *dev) { @@ -343,6 +344,10 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) { return NULL; } +static inline int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + return -ENOTSUPP; +} #endif #endif /* __LINUX_OPP_H__ */ From 2a8e54f87e8c6c4197d55f54bb7437b97d6ad3e6 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:08:19 +0100 Subject: [PATCH 0735/1276] ANDROID: cpufreq: scpi: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scpi-cpufreq driver which uses the power estimation helper from PM_OPP. Change-Id: I113fa2edf8201c1272c9fb5a0c6c39622ae53f94 Signed-off-by: Quentin Perret --- drivers/cpufreq/scpi-cpufreq.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index 87a98ec77773..05fc7448f5cb 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -98,11 +99,12 @@ scpi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) static int scpi_cpufreq_init(struct cpufreq_policy *policy) { - int ret; + int ret, nr_opp; unsigned int latency; struct device *cpu_dev; struct scpi_data *priv; struct cpufreq_frequency_table *freq_table; + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -135,6 +137,7 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -170,6 +173,9 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = false; + + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_cpufreq_table: From 82347cc4936332e0b3cc30253c2f3f25388517f4 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 1 Jun 2018 14:27:24 +0100 Subject: [PATCH 0736/1276] ANDROID: cpufreq: arm_big_little: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scpi-cpufreq driver which uses the power estimation helper from PM_OPP. Todo: Check if driver can handle -EPROBE_DEFER and if the call to dev_pm_opp_get_opp_count() id realy necessary. Change-Id: Ia808262ef6c9f2cc7819a83e8eb2f602454edfa3 Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret --- drivers/cpufreq/arm_big_little.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index cf62a1f64dd7..803d41c629c3 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -456,6 +457,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev, /* Per-CPU initialization */ static int bL_cpufreq_init(struct cpufreq_policy *policy) { + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); u32 cur_cluster = cpu_to_cluster(policy->cpu); struct device *cpu_dev; int ret; @@ -487,6 +489,14 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = arm_bL_ops->get_transition_latency(cpu_dev); + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret <= 0) { + dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); + return -EPROBE_DEFER; + } + + em_register_perf_domain(policy->cpus, ret, &em_cb); + if (is_bL_switching_enabled()) per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu); From d3316b935d57b21c3490e3f517a245ff594b1ec4 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 10 Sep 2018 17:28:10 +0100 Subject: [PATCH 0737/1276] UPSTREAM: firmware: arm_scmi: add a getter for power of performance states The SCMI protocol can be used to get power estimates from firmware corresponding to each performance state of a device. Although these power costs are already managed by the SCMI firmware driver, they are not exposed to any external subsystem yet. Fix this by adding a new get_power() interface to the exisiting perf_ops defined for the SCMI protocol. Change-Id: Iae7b1b60c1955f6590764f3de459a32320eba448 Signed-off-by: Quentin Perret Signed-off-by: Sudeep Holla (cherry picked from commit 1a63fe9a2b1f47af5b2b7436b41824b14999c17a in git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git) Signed-off-by: Quentin Perret --- drivers/firmware/arm_scmi/perf.c | 28 ++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 64342944d917..c8024a39171b 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain, return ret; } +static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain, + unsigned long *freq, unsigned long *power) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom; + unsigned long opp_freq; + int idx, ret = -EINVAL; + struct scmi_opp *opp; + + dom = pi->dom_info + domain; + if (!dom) + return -EIO; + + for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { + opp_freq = opp->perf * dom->mult_factor; + if (opp_freq < *freq) + continue; + + *freq = opp_freq; + *power = opp->power; + ret = 0; + break; + } + + return ret; +} + static struct scmi_perf_ops perf_ops = { .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, @@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = { .device_opps_add = scmi_dvfs_device_opps_add, .freq_set = scmi_dvfs_freq_set, .freq_get = scmi_dvfs_freq_get, + .est_power_get = scmi_dvfs_est_power_get, }; static int scmi_perf_protocol_init(struct scmi_handle *handle) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index f4c9fc0fc755..3105055c00a7 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -91,6 +91,8 @@ struct scmi_clk_ops { * to sustained performance level mapping * @freq_get: gets the frequency for a given device using sustained frequency * to sustained performance level mapping + * @est_power_get: gets the estimated power cost for a given performance domain + * at a given frequency */ struct scmi_perf_ops { int (*limits_set)(const struct scmi_handle *handle, u32 domain, @@ -110,6 +112,8 @@ struct scmi_perf_ops { unsigned long rate, bool poll); int (*freq_get)(const struct scmi_handle *handle, u32 domain, unsigned long *rate, bool poll); + int (*est_power_get)(const struct scmi_handle *handle, u32 domain, + unsigned long *rate, unsigned long *power); }; /** From 6b8617cab29b5fdd76a75e4442ae9728751faf90 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:10:02 +0100 Subject: [PATCH 0738/1276] ANDROID: cpufreq: scmi: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scmi-cpufreq driver which uses the power costs provided by the firmware. Change-Id: I2e6036acbf004d41f921e1396983b07e022a5399 Signed-off-by: Quentin Perret --- drivers/cpufreq/scmi-cpufreq.c | 36 +++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 50b1551ba894..80a7f8da7e74 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -103,13 +104,42 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) return 0; } +static int __maybe_unused +scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu) +{ + struct device *cpu_dev = get_cpu_device(cpu); + unsigned long Hz; + int ret, domain; + + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", cpu); + return -ENODEV; + } + + domain = handle->perf_ops->device_domain_id(cpu_dev); + if (domain < 0) + return domain; + + /* Get the power cost of the performance domain. */ + Hz = *KHz * 1000; + ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power); + if (ret) + return ret; + + /* The EM framework specifies the frequency in KHz. */ + *KHz = Hz / 1000; + + return 0; +} + static int scmi_cpufreq_init(struct cpufreq_policy *policy) { - int ret; + int ret, nr_opp; unsigned int latency; struct device *cpu_dev; struct scmi_data *priv; struct cpufreq_frequency_table *freq_table; + struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -142,6 +172,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -171,6 +202,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = true; + + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_priv: From 3c7908fdff361eb1606800934a0334ce9e025e23 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 12 Jul 2018 16:58:37 +0100 Subject: [PATCH 0739/1276] ANDROID: drivers: Introduce a legacy Energy Model loading driver The Energy Aware Scheduler (EAS) used to rely on statically defined Energy Models (EMs) in the device tree. Now that EAS uses the EM framework, the old-style EMs are not usable by default. To address this issue, introduce a driver able to read DT-based EMs and to load them in the EM framework, hence making them available to EAS. Since EAS now uses only the active costs of CPUs, the idle cost and cluster cost of the old EM are ignored. The driver can be compiled in using the CONFIG_LEGACY_ENERGY_MODEL_DT Kconfig option (off by default). The implementation of the driver is highly inspired by the EM loading code from android-4.14 and before (written by Robin Randhawa ), and the arch_topology driver (Juri Lelli ). Signed-off-by: Quentin Perret Change-Id: I4f525dfb45113ba63f01aaf8e1e809ae6b34dd52 --- drivers/Kconfig | 1 + drivers/Makefile | 2 + drivers/energy_model/Kconfig | 16 +++ drivers/energy_model/Makefile | 3 + drivers/energy_model/legacy_em_dt.c | 193 ++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 drivers/energy_model/Kconfig create mode 100644 drivers/energy_model/Makefile create mode 100644 drivers/energy_model/legacy_em_dt.c diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..681051a96e67 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -219,4 +219,5 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/energy_model/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..1e847e29661c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -157,6 +157,8 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_RPMSG) += rpmsg/ obj-$(CONFIG_SOUNDWIRE) += soundwire/ +obj-$(CONFIG_ENERGY_MODEL) += energy_model/ + # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ obj-$(CONFIG_HYPERV) += hv/ diff --git a/drivers/energy_model/Kconfig b/drivers/energy_model/Kconfig new file mode 100644 index 000000000000..3fbf968926d5 --- /dev/null +++ b/drivers/energy_model/Kconfig @@ -0,0 +1,16 @@ +config LEGACY_ENERGY_MODEL_DT + bool "Legacy DT-based Energy Model of CPUs" + default n + help + The Energy Aware Scheduler (EAS) used to rely on Energy Models + (EMs) statically defined in the Device Tree. More recent + versions of EAS now rely on the EM framework to get the power + costs of CPUs. + + This driver reads old-style static EMs in DT and feeds them in + the EM framework, hence enabling to use EAS on platforms with + old DT files. Since EAS now uses only the active costs of CPUs, + the cluster-related costs and idle-costs of the old EM are + ignored. + + If in doubt, say N. diff --git a/drivers/energy_model/Makefile b/drivers/energy_model/Makefile new file mode 100644 index 000000000000..7bc0a7e502ea --- /dev/null +++ b/drivers/energy_model/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_LEGACY_ENERGY_MODEL_DT) += legacy_em_dt.o diff --git a/drivers/energy_model/legacy_em_dt.c b/drivers/energy_model/legacy_em_dt.c new file mode 100644 index 000000000000..b608790fcc19 --- /dev/null +++ b/drivers/energy_model/legacy_em_dt.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Legacy Energy Model loading driver + * + * Copyright (C) 2018, ARM Ltd. + * Written by: Quentin Perret, ARM Ltd. + */ + +#define pr_fmt(fmt) "legacy-dt-em: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static cpumask_var_t cpus_to_visit; + +static DEFINE_PER_CPU(unsigned long, nr_states) = 0; + +struct em_state { + unsigned long frequency; + unsigned long power; + unsigned long capacity; +}; +static DEFINE_PER_CPU(struct em_state*, cpu_em) = NULL; + +static void finish_em_loading_workfn(struct work_struct *work); +static DECLARE_WORK(finish_em_loading_work, finish_em_loading_workfn); + +static DEFINE_MUTEX(em_loading_mutex); + +/* + * Callback given to the EM framework. All this does is browse the table + * created by legacy_em_dt(). + */ +static int get_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + unsigned long nstates = per_cpu(nr_states, cpu); + struct em_state *em = per_cpu(cpu_em, cpu); + int i; + + if (!nstates || !em) + return -ENODEV; + + for (i = 0; i < nstates - 1; i++) { + if (em[i].frequency > *KHz) + break; + } + + *KHz = em[i].frequency; + *mW = em[i].power; + + return 0; +} + +static int init_em_dt_callback(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct em_data_callback em_cb = EM_DATA_CB(get_power); + unsigned long nstates, scale_cpu, max_freq; + struct cpufreq_policy *policy = data; + const struct property *prop; + struct device_node *cn, *cp; + struct em_state *em; + int cpu, i, ret = 0; + const __be32 *tmp; + + if (val != CPUFREQ_NOTIFY) + return 0; + + mutex_lock(&em_loading_mutex); + + /* Do not register twice an energy model */ + for_each_cpu(cpu, policy->cpus) { + if (per_cpu(nr_states, cpu) || per_cpu(cpu_em, cpu)) { + pr_err("EM of CPU%d already loaded\n", cpu); + ret = -EEXIST; + goto unlock; + } + } + + max_freq = policy->cpuinfo.max_freq; + if (!max_freq) { + pr_err("No policy->max for CPU%d\n", cpu); + ret = -EINVAL; + goto unlock; + } + + cpu = cpumask_first(policy->cpus); + cn = of_get_cpu_node(cpu, NULL); + if (!cn) { + pr_err("No device_node for CPU%d\n", cpu); + ret = -ENODEV; + goto unlock; + } + + cp = of_parse_phandle(cn, "sched-energy-costs", 0); + if (!cp) { + pr_err("CPU%d node has no sched-energy-costs\n", cpu); + ret = -ENODEV; + goto unlock; + } + + prop = of_find_property(cp, "busy-cost-data", NULL); + if (!prop || !prop->value) { + pr_err("No busy-cost-data for CPU%d\n", cpu); + ret = -ENODEV; + goto unlock; + } + + nstates = (prop->length / sizeof(u32)) / 2; + em = kcalloc(nstates, sizeof(struct em_cap_state), GFP_KERNEL); + if (!em) { + ret = -ENOMEM; + goto unlock; + } + + /* Copy the capacity and power cost to the table. */ + for (i = 0, tmp = prop->value; i < nstates; i++) { + em[i].capacity = be32_to_cpup(tmp++); + em[i].power = be32_to_cpup(tmp++); + } + + /* Get the CPU capacity (according to the EM) */ + scale_cpu = em[nstates - 1].capacity; + if (!scale_cpu) { + pr_err("CPU%d: capacity cannot be 0\n", cpu); + kfree(em); + ret = -EINVAL; + goto unlock; + } + + /* Re-compute the intermediate frequencies based on the EM. */ + for (i = 0; i < nstates; i++) + em[i].frequency = em[i].capacity * max_freq / scale_cpu; + + /* Assign the table to all CPUs of this policy. */ + for_each_cpu(i, policy->cpus) { + per_cpu(nr_states, i) = nstates; + per_cpu(cpu_em, i) = em; + } + + pr_info("Registering EM of %*pbl\n", cpumask_pr_args(policy->cpus)); + em_register_perf_domain(policy->cpus, nstates, &em_cb); + + /* Finish the work when all possible CPUs have been registered. */ + cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->cpus); + if (cpumask_empty(cpus_to_visit)) + schedule_work(&finish_em_loading_work); + +unlock: + mutex_unlock(&em_loading_mutex); + + return ret; +} + +static struct notifier_block init_em_dt_notifier = { + .notifier_call = init_em_dt_callback, +}; + +static void finish_em_loading_workfn(struct work_struct *work) +{ + cpufreq_unregister_notifier(&init_em_dt_notifier, + CPUFREQ_POLICY_NOTIFIER); + free_cpumask_var(cpus_to_visit); + + /* Let the scheduler know the Energy Model is ready. */ + rebuild_sched_domains(); +} + +static int __init register_cpufreq_notifier(void) +{ + int ret; + + if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) + return -ENOMEM; + + cpumask_copy(cpus_to_visit, cpu_possible_mask); + + ret = cpufreq_register_notifier(&init_em_dt_notifier, + CPUFREQ_POLICY_NOTIFIER); + + if (ret) + free_cpumask_var(cpus_to_visit); + + return ret; +} +core_initcall(register_cpufreq_notifier); From 181592f89f14a51590d9d02d0ecb9ff357e223dc Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 16:25:33 +0100 Subject: [PATCH 0740/1276] ANDROID: sched: Turn ENERGY_AWARE on by default The ENERGY_AWARE sched_feat must be ON in order to use Energy Aware Scheduling (EAS). Turn it ON by default to make sure it can be used on mobile devices. EAS also requires an Energy Model to start, in addition to the sched_feat. Providing one is the job of platform specific code. Change-Id: I5341ce7f4cc1271199ef1f62decc651312b7ae04 Signed-off-by: Quentin Perret --- kernel/sched/features.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 26ff6752e492..a49c0701a2f2 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -96,4 +96,4 @@ SCHED_FEAT(UTIL_EST, true) * energy-aware fashion depends on this feature being enabled and on the * root domain having an Energy Model attached. */ -SCHED_FEAT(ENERGY_AWARE, false) +SCHED_FEAT(ENERGY_AWARE, true) From 83426e3e5a1110894ad3846eb5afa5f561426b08 Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Sat, 21 Oct 2017 18:07:35 +0100 Subject: [PATCH 0741/1276] ANDROID: sched: fair/tune: Add schedtune with cgroups interface Schedtune is the framework we use in Android to allow userspace task classification and provides a CGroup controller which has two attributes per group. * schedtune.boost * schedtune.prefer_idle Schedtune itself provides task and CPU utilization boosting. EAS in the fair scheduler uses boosted utilization and prefer_idle status to control the algorithm used for wakeup task placement. Boosting: The task utilization signal, which is derived from PELT signals and properly scaled to be architecture and frequency invariant, is used by EAS as an estimation of the task requirements in terms of CPU bandwidth. Schedtune allows userspace to assign a percentage boost to each group and this boost is used to calculate an additional utilization margin. The margin added to the original utilization is: 1. computed based on the "boosting strategy" in use 2. proportional to boost value defined by the "taskgroup" value The boosted signal is used by EAS for task placement, and boosted CPU utilization (if boosted tasks are running) is given when schedutil requests utilization. Prefer_idle: When this attribute is 1 for a group, this is used as a signal from userspace that tasks in this group need to be serviced with the minimum latency possible. Previous versions of schedtune had much more functionality around allowing a more tuneable tradeoff between performand and energy, however this has not been used a lot up until now. If necessary, we can easily resurrect it based upon old code. Signed-off-by: Patrick Bellasi Signed-off-by: Chris Redpath (cherry-picked from commit 159c14f0397790405b9e8435184366b31f2ed15b) [ - Removed tracepoints (to be added in a separate patch) - Integrated boosted_cpu_util() with cpu_util_cfs() - Backported Patrick's util_est related fixes from android-4.14 ] Signed-off-by: Quentin Perret Change-Id: Ie2fd63d82f604f34bcbc7e1ca9b5af1bdcc037e0 --- Documentation/scheduler/sched-tune.txt | 413 ++++++++++++++++ include/linux/cgroup_subsys.h | 4 + init/Kconfig | 23 + kernel/sched/Makefile | 1 + kernel/sched/cpufreq_schedutil.c | 2 +- kernel/sched/fair.c | 118 +++++ kernel/sched/sched.h | 2 + kernel/sched/tune.c | 622 +++++++++++++++++++++++++ kernel/sched/tune.h | 37 ++ 9 files changed, 1221 insertions(+), 1 deletion(-) create mode 100644 Documentation/scheduler/sched-tune.txt create mode 100644 kernel/sched/tune.c create mode 100644 kernel/sched/tune.h diff --git a/Documentation/scheduler/sched-tune.txt b/Documentation/scheduler/sched-tune.txt new file mode 100644 index 000000000000..5df0ea361311 --- /dev/null +++ b/Documentation/scheduler/sched-tune.txt @@ -0,0 +1,413 @@ + Central, scheduler-driven, power-performance control + (EXPERIMENTAL) + +Abstract +======== + +The topic of a single simple power-performance tunable, that is wholly +scheduler centric, and has well defined and predictable properties has come up +on several occasions in the past [1,2]. With techniques such as a scheduler +driven DVFS [3], we now have a good framework for implementing such a tunable. +This document describes the overall ideas behind its design and implementation. + + +Table of Contents +================= + +1. Motivation +2. Introduction +3. Signal Boosting Strategy +4. OPP selection using boosted CPU utilization +5. Per task group boosting +6. Per-task wakeup-placement-strategy Selection +7. Question and Answers + - What about "auto" mode? + - What about boosting on a congested system? + - How CPUs are boosted when we have tasks with multiple boost values? +8. References + + +1. Motivation +============= + +Sched-DVFS [3] was a new event-driven cpufreq governor which allows the +scheduler to select the optimal DVFS operating point (OPP) for running a task +allocated to a CPU. Later, the cpufreq maintainers introduced a similar +governor, schedutil. The introduction of schedutil also enables running +workloads at the most energy efficient OPPs. + +However, sometimes it may be desired to intentionally boost the performance of +a workload even if that could imply a reasonable increase in energy +consumption. For example, in order to reduce the response time of a task, we +may want to run the task at a higher OPP than the one that is actually required +by it's CPU bandwidth demand. + +This last requirement is especially important if we consider that one of the +main goals of the utilization-driven governor component is to replace all +currently available CPUFreq policies. Since sched-DVFS and schedutil are event +based, as opposed to the sampling driven governors we currently have, they are +already more responsive at selecting the optimal OPP to run tasks allocated to +a CPU. However, just tracking the actual task load demand may not be enough +from a performance standpoint. For example, it is not possible to get +behaviors similar to those provided by the "performance" and "interactive" +CPUFreq governors. + +This document describes an implementation of a tunable, stacked on top of the +utilization-driven governors which extends their functionality to support task +performance boosting. + +By "performance boosting" we mean the reduction of the time required to +complete a task activation, i.e. the time elapsed from a task wakeup to its +next deactivation (e.g. because it goes back to sleep or it terminates). For +example, if we consider a simple periodic task which executes the same workload +for 5[s] every 20[s] while running at a certain OPP, a boosted execution of +that task must complete each of its activations in less than 5[s]. + +A previous attempt [5] to introduce such a boosting feature has not been +successful mainly because of the complexity of the proposed solution. Previous +versions of the approach described in this document exposed a single simple +interface to user-space. This single tunable knob allowed the tuning of +system wide scheduler behaviours ranging from energy efficiency at one end +through to incremental performance boosting at the other end. This first +tunable affects all tasks. However, that is not useful for Android products +so in this version only a more advanced extension of the concept is provided +which uses CGroups to boost the performance of only selected tasks while using +the energy efficient default for all others. + +The rest of this document introduces in more details the proposed solution +which has been named SchedTune. + + +2. Introduction +=============== + +SchedTune exposes a simple user-space interface provided through a new +CGroup controller 'stune' which provides two power-performance tunables +per group: + + //schedtune.prefer_idle + //schedtune.boost + +The CGroup implementation permits arbitrary user-space defined task +classification to tune the scheduler for different goals depending on the +specific nature of the task, e.g. background vs interactive vs low-priority. + +More details are given in section 5. + +2.1 Boosting +============ + +The boost value is expressed as an integer in the range [-100..0..100]. + +A value of 0 (default) configures the CFS scheduler for maximum energy +efficiency. This means that sched-DVFS runs the tasks at the minimum OPP +required to satisfy their workload demand. + +A value of 100 configures scheduler for maximum performance, which translates +to the selection of the maximum OPP on that CPU. + +A value of -100 configures scheduler for minimum performance, which translates +to the selection of the minimum OPP on that CPU. + +The range between -100, 0 and 100 can be set to satisfy other scenarios suitably. +For example to satisfy interactive response or depending on other system events +(battery level etc). + +The overall design of the SchedTune module is built on top of "Per-Entity Load +Tracking" (PELT) signals and sched-DVFS by introducing a bias on the Operating +Performance Point (OPP) selection. + +Each time a task is allocated on a CPU, cpufreq is given the opportunity to tune +the operating frequency of that CPU to better match the workload demand. The +selection of the actual OPP being activated is influenced by the boost value +for the task CGroup. + +This simple biasing approach leverages existing frameworks, which means minimal +modifications to the scheduler, and yet it allows to achieve a range of +different behaviours all from a single simple tunable knob. + +In EAS schedulers, we use boosted task and CPU utilization for energy +calculation and energy-aware task placement. + +2.2 prefer_idle +=============== + +This is a flag which indicates to the scheduler that userspace would like +the scheduler to focus on energy or to focus on performance. + +A value of 0 (default) signals to the CFS scheduler that tasks in this group +can be placed according to the energy-aware wakeup strategy. + +A value of 1 signals to the CFS scheduler that tasks in this group should be +placed to minimise wakeup latency. + +The value is combined with the boost value - task placement will not be +boost aware however CPU OPP selection is still boost aware. + +Android platforms typically use this flag for application tasks which the +user is currently interacting with. + + +3. Signal Boosting Strategy +=========================== + +The whole PELT machinery works based on the value of a few load tracking signals +which basically track the CPU bandwidth requirements for tasks and the capacity +of CPUs. The basic idea behind the SchedTune knob is to artificially inflate +some of these load tracking signals to make a task or RQ appears more demanding +that it actually is. + +Which signals have to be inflated depends on the specific "consumer". However, +independently from the specific (signal, consumer) pair, it is important to +define a simple and possibly consistent strategy for the concept of boosting a +signal. + +A boosting strategy defines how the "abstract" user-space defined +sched_cfs_boost value is translated into an internal "margin" value to be added +to a signal to get its inflated value: + + margin := boosting_strategy(sched_cfs_boost, signal) + boosted_signal := signal + margin + +Different boosting strategies were identified and analyzed before selecting the +one found to be most effective. + +Signal Proportional Compensation (SPC) +-------------------------------------- + +In this boosting strategy the sched_cfs_boost value is used to compute a +margin which is proportional to the complement of the original signal. +When a signal has a maximum possible value, its complement is defined as +the delta from the actual value and its possible maximum. + +Since the tunable implementation uses signals which have SCHED_LOAD_SCALE as +the maximum possible value, the margin becomes: + + margin := sched_cfs_boost * (SCHED_LOAD_SCALE - signal) + +Using this boosting strategy: +- a 100% sched_cfs_boost means that the signal is scaled to the maximum value +- each value in the range of sched_cfs_boost effectively inflates the signal in + question by a quantity which is proportional to the maximum value. + +For example, by applying the SPC boosting strategy to the selection of the OPP +to run a task it is possible to achieve these behaviors: + +- 0% boosting: run the task at the minimum OPP required by its workload +- 100% boosting: run the task at the maximum OPP available for the CPU +- 50% boosting: run at the half-way OPP between minimum and maximum + +Which means that, at 50% boosting, a task will be scheduled to run at half of +the maximum theoretically achievable performance on the specific target +platform. + +A graphical representation of an SPC boosted signal is represented in the +following figure where: + a) "-" represents the original signal + b) "b" represents a 50% boosted signal + c) "p" represents a 100% boosted signal + + + ^ + | SCHED_LOAD_SCALE + +-----------------------------------------------------------------+ + |pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp + | + | boosted_signal + | bbbbbbbbbbbbbbbbbbbbbbbb + | + | original signal + | bbbbbbbbbbbbbbbbbbbbbbbb+----------------------+ + | | + |bbbbbbbbbbbbbbbbbb | + | | + | | + | | + | +-----------------------+ + | | + | | + | | + |------------------+ + | + | + +-----------------------------------------------------------------------> + +The plot above shows a ramped load signal (titled 'original_signal') and it's +boosted equivalent. For each step of the original signal the boosted signal +corresponding to a 50% boost is midway from the original signal and the upper +bound. Boosting by 100% generates a boosted signal which is always saturated to +the upper bound. + + +4. OPP selection using boosted CPU utilization +============================================== + +It is worth calling out that the implementation does not introduce any new load +signals. Instead, it provides an API to tune existing signals. This tuning is +done on demand and only in scheduler code paths where it is sensible to do so. +The new API calls are defined to return either the default signal or a boosted +one, depending on the value of sched_cfs_boost. This is a clean an non invasive +modification of the existing existing code paths. + +The signal representing a CPU's utilization is boosted according to the +previously described SPC boosting strategy. To sched-DVFS, this allows a CPU +(ie CFS run-queue) to appear more used then it actually is. + +Thus, with the sched_cfs_boost enabled we have the following main functions to +get the current utilization of a CPU: + + cpu_util() + boosted_cpu_util() + +The new boosted_cpu_util() is similar to the first but returns a boosted +utilization signal which is a function of the sched_cfs_boost value. + +This function is used in the CFS scheduler code paths where sched-DVFS needs to +decide the OPP to run a CPU at. +For example, this allows selecting the highest OPP for a CPU which has +the boost value set to 100%. + + +5. Per task group boosting +========================== + +On battery powered devices there usually are many background services which are +long running and need energy efficient scheduling. On the other hand, some +applications are more performance sensitive and require an interactive +response and/or maximum performance, regardless of the energy cost. + +To better service such scenarios, the SchedTune implementation has an extension +that provides a more fine grained boosting interface. + +A new CGroup controller, namely "schedtune", can be enabled which allows to +defined and configure task groups with different boosting values. +Tasks that require special performance can be put into separate CGroups. +The value of the boost associated with the tasks in this group can be specified +using a single knob exposed by the CGroup controller: + + schedtune.boost + +This knob allows the definition of a boost value that is to be used for +SPC boosting of all tasks attached to this group. + +The current schedtune controller implementation is really simple and has these +main characteristics: + + 1) It is only possible to create 1 level depth hierarchies + + The root control groups define the system-wide boost value to be applied + by default to all tasks. Its direct subgroups are named "boost groups" and + they define the boost value for specific set of tasks. + Further nested subgroups are not allowed since they do not have a sensible + meaning from a user-space standpoint. + + 2) It is possible to define only a limited number of "boost groups" + + This number is defined at compile time and by default configured to 16. + This is a design decision motivated by two main reasons: + a) In a real system we do not expect utilization scenarios with more then few + boost groups. For example, a reasonable collection of groups could be + just "background", "interactive" and "performance". + b) It simplifies the implementation considerably, especially for the code + which has to compute the per CPU boosting once there are multiple + RUNNABLE tasks with different boost values. + +Such a simple design should allow servicing the main utilization scenarios identified +so far. It provides a simple interface which can be used to manage the +power-performance of all tasks or only selected tasks. +Moreover, this interface can be easily integrated by user-space run-times (e.g. +Android, ChromeOS) to implement a QoS solution for task boosting based on tasks +classification, which has been a long standing requirement. + +Setup and usage +--------------- + +0. Use a kernel with CONFIG_SCHED_TUNE support enabled + +1. Check that the "schedtune" CGroup controller is available: + + root@linaro-nano:~# cat /proc/cgroups + #subsys_name hierarchy num_cgroups enabled + cpuset 0 1 1 + cpu 0 1 1 + schedtune 0 1 1 + +2. Mount a tmpfs to create the CGroups mount point (Optional) + + root@linaro-nano:~# sudo mount -t tmpfs cgroups /sys/fs/cgroup + +3. Mount the "schedtune" controller + + root@linaro-nano:~# mkdir /sys/fs/cgroup/stune + root@linaro-nano:~# sudo mount -t cgroup -o schedtune stune /sys/fs/cgroup/stune + +4. Create task groups and configure their specific boost value (Optional) + + For example here we create a "performance" boost group configure to boost + all its tasks to 100% + + root@linaro-nano:~# mkdir /sys/fs/cgroup/stune/performance + root@linaro-nano:~# echo 100 > /sys/fs/cgroup/stune/performance/schedtune.boost + +5. Move tasks into the boost group + + For example, the following moves the tasks with PID $TASKPID (and all its + threads) into the "performance" boost group. + + root@linaro-nano:~# echo "TASKPID > /sys/fs/cgroup/stune/performance/cgroup.procs + +This simple configuration allows only the threads of the $TASKPID task to run, +when needed, at the highest OPP in the most capable CPU of the system. + + +6. Per-task wakeup-placement-strategy Selection +=============================================== + +Many devices have a number of CFS tasks in use which require an absolute +minimum wakeup latency, and many tasks for which wakeup latency is not +important. + +For touch-driven environments, removing additional wakeup latency can be +critical. + +When you use the Schedtume CGroup controller, you have access to a second +parameter which allows a group to be marked such that energy_aware task +placement is bypassed for tasks belonging to that group. + +prefer_idle=0 (default - use energy-aware task placement if available) +prefer_idle=1 (never use energy-aware task placement for these tasks) + +Since the regular wakeup task placement algorithm in CFS is biased for +performance, this has the effect of restoring minimum wakeup latency +for the desired tasks whilst still allowing energy-aware wakeup placement +to save energy for other tasks. + + +7. Question and Answers +======================= + +What about "auto" mode? +----------------------- + +The 'auto' mode as described in [5] can be implemented by interfacing SchedTune +with some suitable user-space element. This element could use the exposed +system-wide or cgroup based interface. + +How are multiple groups of tasks with different boost values managed? +--------------------------------------------------------------------- + +The current SchedTune implementation keeps track of the boosted RUNNABLE tasks +on a CPU. The CPU utilization seen by the scheduler-driven cpufreq governors +(and used to select an appropriate OPP) is boosted with a value which is the +maximum of the boost values of the currently RUNNABLE tasks in its RQ. + +This allows cpufreq to boost a CPU only while there are boosted tasks ready +to run and switch back to the energy efficient mode as soon as the last boosted +task is dequeued. + + +8. References +============= +[1] http://lwn.net/Articles/552889 +[2] http://lkml.org/lkml/2012/5/18/91 +[3] http://lkml.org/lkml/2015/6/26/620 diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index acb77dcff3b4..8996c092568b 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -21,6 +21,10 @@ SUBSYS(cpu) SUBSYS(cpuacct) #endif +#if IS_ENABLED(CONFIG_SCHED_TUNE) +SUBSYS(schedtune) +#endif + #if IS_ENABLED(CONFIG_BLK_CGROUP) SUBSYS(io) #endif diff --git a/init/Kconfig b/init/Kconfig index 01cad95346df..2a9efce3f75d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -991,6 +991,29 @@ config SCHED_AUTOGROUP desktop applications. Task group autogeneration is currently based upon task session. +config SCHED_TUNE + bool "Boosting for CFS tasks (EXPERIMENTAL)" + depends on SMP + help + This option enables support for task classification using a new + cgroup controller, schedtune. Schedtune allows tasks to be given + a boost value and marked as latency-sensitive or not. This option + provides the "schedtune" controller. + + This new controller: + 1. allows only a two layers hierarchy, where the root defines the + system-wide boost value and its direct childrens define each one a + different "class of tasks" to be boosted with a different value + 2. supports up to 16 different task classes, each one which could be + configured with a different boost value + + Latency-sensitive tasks are not subject to energy-aware wakeup + task placement. The boost value assigned to tasks is used to + influence task placement and CPU frequency selection (if + utilization-driven frequency selection is in use). + + If unsure, say N. + config SYSFS_DEPRECATED bool "Enable deprecated sysfs features to support old userspace tools" depends on SYSFS diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile index 7fe183404c38..2389350a8268 100644 --- a/kernel/sched/Makefile +++ b/kernel/sched/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o topology.o stop_task.o pelt.o obj-$(CONFIG_SCHED_AUTOGROUP) += autogroup.o obj-$(CONFIG_SCHEDSTATS) += stats.o obj-$(CONFIG_SCHED_DEBUG) += debug.o +obj-$(CONFIG_SCHED_TUNE) += tune.o obj-$(CONFIG_CGROUP_CPUACCT) += cpuacct.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index d2236d1166f4..645f54a0c2dc 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -286,7 +286,7 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq = cpu_rq(sg_cpu->cpu); - unsigned long util = cpu_util_cfs(rq); + unsigned long util = boosted_cpu_util(sg_cpu->cpu); sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); sg_cpu->bw_dl = cpu_bw_dl(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e919c31562ab..1db3fdf2dfc3 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5132,6 +5132,24 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) */ util_est_enqueue(&rq->cfs, p); + /* + * The code below (indirectly) updates schedutil which looks at + * the cfs_rq utilization to select a frequency. + * Let's update schedtune here to ensure the boost value of the + * current task is accounted for in the selection of the OPP. + * + * We do it also in the case where we enqueue a throttled task; + * we could argue that a throttled task should not boost a CPU, + * however: + * a) properly implementing CPU boosting considering throttled + * tasks will increase a lot the complexity of the solution + * b) it's not easy to quantify the benefits introduced by + * such a more complex solution. + * Thus, for the time being we go for the simple solution and boost + * also for throttled RQs. + */ + schedtune_enqueue_task(p, cpu_of(rq)); + /* * If in_iowait is set, the code below may not trigger any cpufreq * utilization updates, so do it here explicitly with the IOWAIT flag @@ -5207,6 +5225,14 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) struct sched_entity *se = &p->se; int task_sleep = flags & DEQUEUE_SLEEP; + /* + * The code below (indirectly) updates schedutil which looks at + * the cfs_rq utilization to select a frequency. + * Let's update schedtune here to ensure the boost value of the + * current task is not more accounted for in the selection of the OPP. + */ + schedtune_dequeue_task(p, cpu_of(rq)); + for_each_sched_entity(se) { cfs_rq = cfs_rq_of(se); dequeue_entity(cfs_rq, se, flags); @@ -5733,6 +5759,98 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, return target; } +#ifdef CONFIG_SCHED_TUNE +struct reciprocal_value schedtune_spc_rdiv; + +static long +schedtune_margin(unsigned long signal, long boost) +{ + long long margin = 0; + + /* + * Signal proportional compensation (SPC) + * + * The Boost (B) value is used to compute a Margin (M) which is + * proportional to the complement of the original Signal (S): + * M = B * (SCHED_CAPACITY_SCALE - S) + * The obtained M could be used by the caller to "boost" S. + */ + if (boost >= 0) { + margin = SCHED_CAPACITY_SCALE - signal; + margin *= boost; + } else + margin = -signal * boost; + + margin = reciprocal_divide(margin, schedtune_spc_rdiv); + + if (boost < 0) + margin *= -1; + return margin; +} + +static inline int +schedtune_cpu_margin(unsigned long util, int cpu) +{ + int boost = schedtune_cpu_boost(cpu); + + if (boost == 0) + return 0; + + return schedtune_margin(util, boost); +} + +static inline long +schedtune_task_margin(struct task_struct *task) +{ + int boost = schedtune_task_boost(task); + unsigned long util; + long margin; + + if (boost == 0) + return 0; + + util = task_util_est(task); + margin = schedtune_margin(util, boost); + + return margin; +} + +unsigned long +boosted_cpu_util(int cpu) +{ + unsigned long util = cpu_util_cfs(cpu_rq(cpu)); + long margin = schedtune_cpu_margin(util, cpu); + + return util + margin; +} + +#else /* CONFIG_SCHED_TUNE */ + +static inline int +schedtune_cpu_margin(unsigned long util, int cpu) +{ + return 0; +} + +static inline int +schedtune_task_margin(struct task_struct *task) +{ + return 0; +} + +#endif /* CONFIG_SCHED_TUNE */ + + + +static inline unsigned long +boosted_task_util(struct task_struct *task) +{ + unsigned long util = task_util_est(task); + long margin = schedtune_task_margin(task); + + return util + margin; +} + static unsigned long cpu_util_wake(int cpu, struct task_struct *p); static unsigned long capacity_spare_wake(int cpu, struct task_struct *p) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ce83194c274a..16fc88a9b43e 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -80,6 +80,8 @@ # define SCHED_WARN_ON(x) ({ (void)(x), 0; }) #endif +#include "tune.h" + struct rq; struct cpuidle_state; diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c new file mode 100644 index 000000000000..af6de6d3df6d --- /dev/null +++ b/kernel/sched/tune.c @@ -0,0 +1,622 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sched.h" + +bool schedtune_initialized = false; +extern struct reciprocal_value schedtune_spc_rdiv; + +/* + * EAS scheduler tunables for task groups. + * + * When CGroup support is enabled, we have to synchronize two different + * paths: + * - slow path: where CGroups are created/updated/removed + * - fast path: where tasks in a CGroups are accounted + * + * The slow path tracks (a limited number of) CGroups and maps each on a + * "boost_group" index. The fastpath accounts tasks currently RUNNABLE on each + * "boost_group". + * + * Once a new CGroup is created, a boost group idx is assigned and the + * corresponding "boost_group" marked as valid on each CPU. + * Once a CGroup is release, the corresponding "boost_group" is marked as + * invalid on each CPU. The CPU boost value (boost_max) is aggregated by + * considering only valid boost_groups with a non null tasks counter. + * + * .:: Locking strategy + * + * The fast path uses a spin lock for each CPU boost_group which protects the + * tasks counter. + * + * The "valid" and "boost" values of each CPU boost_group is instead + * protected by the RCU lock provided by the CGroups callbacks. Thus, only the + * slow path can access and modify the boost_group attribtues of each CPU. + * The fast path will catch up the most updated values at the next scheduling + * event (i.e. enqueue/dequeue). + * + * | + * SLOW PATH | FAST PATH + * CGroup add/update/remove | Scheduler enqueue/dequeue events + * | + * | + * | DEFINE_PER_CPU(struct boost_groups) + * | +--------------+----+---+----+----+ + * | | idle | | | | | + * | | boost_max | | | | | + * | +---->lock | | | | | + * struct schedtune allocated_groups | | | group[ ] | | | | | + * +------------------------------+ +-------+ | | +--+---------+-+----+---+----+----+ + * | idx | | | | | | valid | + * | boots / prefer_idle | | | | | | boost | + * | perf_{boost/constraints}_idx | <---------+(*) | | | | tasks | <------------+ + * | css | +-------+ | | +---------+ | + * +-+----------------------------+ | | | | | | | + * ^ | | | | | | | + * | +-------+ | | +---------+ | + * | | | | | | | | + * | | | | | | | | + * | +-------+ | | +---------+ | + * | zmalloc | | | | | | | + * | | | | | | | | + * | +-------+ | | +---------+ | + * + BOOSTGROUPS_COUNT | | BOOSTGROUPS_COUNT | + * schedtune_boostgroup_init() | + | + * | schedtune_{en,de}queue_task() | + * | + + * | schedtune_tasks_update() + * | + */ + +/* SchdTune tunables for a group of tasks */ +struct schedtune { + /* SchedTune CGroup subsystem */ + struct cgroup_subsys_state css; + + /* Boost group allocated ID */ + int idx; + + /* Boost value for tasks on that SchedTune CGroup */ + int boost; + + /* Hint to bias scheduling of tasks on that SchedTune CGroup + * towards idle CPUs */ + int prefer_idle; +}; + +static inline struct schedtune *css_st(struct cgroup_subsys_state *css) +{ + return css ? container_of(css, struct schedtune, css) : NULL; +} + +static inline struct schedtune *task_schedtune(struct task_struct *tsk) +{ + return css_st(task_css(tsk, schedtune_cgrp_id)); +} + +static inline struct schedtune *parent_st(struct schedtune *st) +{ + return css_st(st->css.parent); +} + +/* + * SchedTune root control group + * The root control group is used to defined a system-wide boosting tuning, + * which is applied to all tasks in the system. + * Task specific boost tuning could be specified by creating and + * configuring a child control group under the root one. + * By default, system-wide boosting is disabled, i.e. no boosting is applied + * to tasks which are not into a child control group. + */ +static struct schedtune +root_schedtune = { + .boost = 0, + .prefer_idle = 0, +}; + +/* + * Maximum number of boost groups to support + * When per-task boosting is used we still allow only limited number of + * boost groups for two main reasons: + * 1. on a real system we usually have only few classes of workloads which + * make sense to boost with different values (e.g. background vs foreground + * tasks, interactive vs low-priority tasks) + * 2. a limited number allows for a simpler and more memory/time efficient + * implementation especially for the computation of the per-CPU boost + * value + */ +#define BOOSTGROUPS_COUNT 5 + +/* Array of configured boostgroups */ +static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = { + &root_schedtune, + NULL, +}; + +/* SchedTune boost groups + * Keep track of all the boost groups which impact on CPU, for example when a + * CPU has two RUNNABLE tasks belonging to two different boost groups and thus + * likely with different boost values. + * Since on each system we expect only a limited number of boost groups, here + * we use a simple array to keep track of the metrics required to compute the + * maximum per-CPU boosting value. + */ +struct boost_groups { + /* Maximum boost value for all RUNNABLE tasks on a CPU */ + int boost_max; + struct { + /* True when this boost group maps an actual cgroup */ + bool valid; + /* The boost for tasks on that boost group */ + int boost; + /* Count of RUNNABLE tasks on that boost group */ + unsigned tasks; + } group[BOOSTGROUPS_COUNT]; + /* CPU's boost group locking */ + raw_spinlock_t lock; +}; + +/* Boost groups affecting each CPU in the system */ +DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups); + +static void +schedtune_cpu_update(int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + int boost_max; + int idx; + + /* The root boost group is always active */ + boost_max = bg->group[0].boost; + for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) { + + /* Ignore non boostgroups not mapping a cgroup */ + if (!bg->group[idx].valid) + continue; + + /* + * A boost group affects a CPU only if it has + * RUNNABLE tasks on that CPU + */ + if (bg->group[idx].tasks == 0) + continue; + + boost_max = max(boost_max, bg->group[idx].boost); + } + + /* Ensures boost_max is non-negative when all cgroup boost values + * are neagtive. Avoids under-accounting of cpu capacity which may cause + * task stacking and frequency spikes.*/ + boost_max = max(boost_max, 0); + bg->boost_max = boost_max; +} + +static int +schedtune_boostgroup_update(int idx, int boost) +{ + struct boost_groups *bg; + int cur_boost_max; + int old_boost; + int cpu; + + /* Update per CPU boost groups */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + + /* CGroups are never associated to non active cgroups */ + BUG_ON(!bg->group[idx].valid); + + /* + * Keep track of current boost values to compute the per CPU + * maximum only when it has been affected by the new value of + * the updated boost group + */ + cur_boost_max = bg->boost_max; + old_boost = bg->group[idx].boost; + + /* Update the boost value of this boost group */ + bg->group[idx].boost = boost; + + /* Check if this update increase current max */ + if (boost > cur_boost_max && bg->group[idx].tasks) { + bg->boost_max = boost; + continue; + } + + /* Check if this update has decreased current max */ + if (cur_boost_max == old_boost && old_boost > boost) { + schedtune_cpu_update(cpu); + continue; + } + } + + return 0; +} + +#define ENQUEUE_TASK 1 +#define DEQUEUE_TASK -1 + +static inline void +schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + int tasks = bg->group[idx].tasks + task_count; + + /* Update boosted tasks count while avoiding to make it negative */ + bg->group[idx].tasks = max(0, tasks); + + /* Boost group activation or deactivation on that RQ */ + if (tasks == 1 || tasks == 0) + schedtune_cpu_update(cpu); +} + +/* + * NOTE: This function must be called while holding the lock on the CPU RQ + */ +void schedtune_enqueue_task(struct task_struct *p, int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + unsigned long irq_flags; + struct schedtune *st; + int idx; + + if (unlikely(!schedtune_initialized)) + return; + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions for example on + * do_exit()::cgroup_exit() and task migration. + */ + raw_spin_lock_irqsave(&bg->lock, irq_flags); + rcu_read_lock(); + + st = task_schedtune(p); + idx = st->idx; + + schedtune_tasks_update(p, cpu, idx, ENQUEUE_TASK); + + rcu_read_unlock(); + raw_spin_unlock_irqrestore(&bg->lock, irq_flags); +} + +int schedtune_can_attach(struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct cgroup_subsys_state *css; + struct boost_groups *bg; + struct rq_flags rq_flags; + unsigned int cpu; + struct rq *rq; + int src_bg; /* Source boost group index */ + int dst_bg; /* Destination boost group index */ + int tasks; + + if (unlikely(!schedtune_initialized)) + return 0; + + + cgroup_taskset_for_each(task, css, tset) { + + /* + * Lock the CPU's RQ the task is enqueued to avoid race + * conditions with migration code while the task is being + * accounted + */ + rq = task_rq_lock(task, &rq_flags); + + if (!task->on_rq) { + task_rq_unlock(rq, task, &rq_flags); + continue; + } + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions on... + */ + cpu = cpu_of(rq); + bg = &per_cpu(cpu_boost_groups, cpu); + raw_spin_lock(&bg->lock); + + dst_bg = css_st(css)->idx; + src_bg = task_schedtune(task)->idx; + + /* + * Current task is not changing boostgroup, which can + * happen when the new hierarchy is in use. + */ + if (unlikely(dst_bg == src_bg)) { + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); + continue; + } + + /* + * This is the case of a RUNNABLE task which is switching its + * current boost group. + */ + + /* Move task from src to dst boost group */ + tasks = bg->group[src_bg].tasks - 1; + bg->group[src_bg].tasks = max(0, tasks); + bg->group[dst_bg].tasks += 1; + + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); + + /* Update CPU boost group */ + if (bg->group[src_bg].tasks == 0 || bg->group[dst_bg].tasks == 1) + schedtune_cpu_update(task_cpu(task)); + + } + + return 0; +} + +void schedtune_cancel_attach(struct cgroup_taskset *tset) +{ + /* This can happen only if SchedTune controller is mounted with + * other hierarchies ane one of them fails. Since usually SchedTune is + * mouted on its own hierarcy, for the time being we do not implement + * a proper rollback mechanism */ + WARN(1, "SchedTune cancel attach not implemented"); +} + +/* + * NOTE: This function must be called while holding the lock on the CPU RQ + */ +void schedtune_dequeue_task(struct task_struct *p, int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + unsigned long irq_flags; + struct schedtune *st; + int idx; + + if (unlikely(!schedtune_initialized)) + return; + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions on... + */ + raw_spin_lock_irqsave(&bg->lock, irq_flags); + rcu_read_lock(); + + st = task_schedtune(p); + idx = st->idx; + + schedtune_tasks_update(p, cpu, idx, DEQUEUE_TASK); + + rcu_read_unlock(); + raw_spin_unlock_irqrestore(&bg->lock, irq_flags); +} + +int schedtune_cpu_boost(int cpu) +{ + struct boost_groups *bg; + + bg = &per_cpu(cpu_boost_groups, cpu); + return bg->boost_max; +} + +int schedtune_task_boost(struct task_struct *p) +{ + struct schedtune *st; + int task_boost; + + if (unlikely(!schedtune_initialized)) + return 0; + + /* Get task boost value */ + rcu_read_lock(); + st = task_schedtune(p); + task_boost = st->boost; + rcu_read_unlock(); + + return task_boost; +} + +int schedtune_prefer_idle(struct task_struct *p) +{ + struct schedtune *st; + int prefer_idle; + + if (unlikely(!schedtune_initialized)) + return 0; + + /* Get prefer_idle value */ + rcu_read_lock(); + st = task_schedtune(p); + prefer_idle = st->prefer_idle; + rcu_read_unlock(); + + return prefer_idle; +} + +static u64 +prefer_idle_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + struct schedtune *st = css_st(css); + + return st->prefer_idle; +} + +static int +prefer_idle_write(struct cgroup_subsys_state *css, struct cftype *cft, + u64 prefer_idle) +{ + struct schedtune *st = css_st(css); + st->prefer_idle = !!prefer_idle; + + return 0; +} + +static s64 +boost_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + struct schedtune *st = css_st(css); + + return st->boost; +} + +static int +boost_write(struct cgroup_subsys_state *css, struct cftype *cft, + s64 boost) +{ + struct schedtune *st = css_st(css); + + if (boost < 0 || boost > 100) + return -EINVAL; + + st->boost = boost; + + /* Update CPU boost */ + schedtune_boostgroup_update(st->idx, st->boost); + + return 0; +} + +static struct cftype files[] = { + { + .name = "boost", + .read_s64 = boost_read, + .write_s64 = boost_write, + }, + { + .name = "prefer_idle", + .read_u64 = prefer_idle_read, + .write_u64 = prefer_idle_write, + }, + { } /* terminate */ +}; + +static void +schedtune_boostgroup_init(struct schedtune *st, int idx) +{ + struct boost_groups *bg; + int cpu; + + /* Initialize per CPUs boost group support */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + bg->group[idx].boost = 0; + bg->group[idx].valid = true; + } + + /* Keep track of allocated boost groups */ + allocated_group[idx] = st; + st->idx = idx; +} + +static struct cgroup_subsys_state * +schedtune_css_alloc(struct cgroup_subsys_state *parent_css) +{ + struct schedtune *st; + int idx; + + if (!parent_css) + return &root_schedtune.css; + + /* Allow only single level hierachies */ + if (parent_css != &root_schedtune.css) { + pr_err("Nested SchedTune boosting groups not allowed\n"); + return ERR_PTR(-ENOMEM); + } + + /* Allow only a limited number of boosting groups */ + for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) + if (!allocated_group[idx]) + break; + if (idx == BOOSTGROUPS_COUNT) { + pr_err("Trying to create more than %d SchedTune boosting groups\n", + BOOSTGROUPS_COUNT); + return ERR_PTR(-ENOSPC); + } + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + goto out; + + /* Initialize per CPUs boost group support */ + schedtune_boostgroup_init(st, idx); + + return &st->css; + +out: + return ERR_PTR(-ENOMEM); +} + +static void +schedtune_boostgroup_release(struct schedtune *st) +{ + struct boost_groups *bg; + int cpu; + + /* Reset per CPUs boost group support */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + bg->group[st->idx].valid = false; + bg->group[st->idx].boost = 0; + } + + /* Keep track of allocated boost groups */ + allocated_group[st->idx] = NULL; +} + +static void +schedtune_css_free(struct cgroup_subsys_state *css) +{ + struct schedtune *st = css_st(css); + + /* Release per CPUs boost group support */ + schedtune_boostgroup_release(st); + kfree(st); +} + +struct cgroup_subsys schedtune_cgrp_subsys = { + .css_alloc = schedtune_css_alloc, + .css_free = schedtune_css_free, + .can_attach = schedtune_can_attach, + .cancel_attach = schedtune_cancel_attach, + .legacy_cftypes = files, + .early_init = 1, +}; + +static inline void +schedtune_init_cgroups(void) +{ + struct boost_groups *bg; + int cpu; + + /* Initialize the per CPU boost groups */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + memset(bg, 0, sizeof(struct boost_groups)); + bg->group[0].valid = true; + raw_spin_lock_init(&bg->lock); + } + + pr_info("schedtune: configured to support %d boost groups\n", + BOOSTGROUPS_COUNT); + + schedtune_initialized = true; +} + +/* + * Initialize the cgroup structures + */ +static int +schedtune_init(void) +{ + schedtune_spc_rdiv = reciprocal_value(100); + schedtune_init_cgroups(); + return 0; +} +postcore_initcall(schedtune_init); diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h new file mode 100644 index 000000000000..bb187c6112a7 --- /dev/null +++ b/kernel/sched/tune.h @@ -0,0 +1,37 @@ + +#ifdef CONFIG_SCHED_TUNE + +#include + +/* + * System energy normalization constants + */ +struct target_nrg { + unsigned long min_power; + unsigned long max_power; + struct reciprocal_value rdiv; +}; + +int schedtune_cpu_boost(int cpu); +int schedtune_task_boost(struct task_struct *tsk); + +int schedtune_prefer_idle(struct task_struct *tsk); + +void schedtune_enqueue_task(struct task_struct *p, int cpu); +void schedtune_dequeue_task(struct task_struct *p, int cpu); + +unsigned long boosted_cpu_util(int cpu); + +#else /* CONFIG_SCHED_TUNE */ + +#define schedtune_cpu_boost(cpu) 0 +#define schedtune_task_boost(tsk) 0 + +#define schedtune_prefer_idle(tsk) 0 + +#define schedtune_enqueue_task(task, cpu) do { } while (0) +#define schedtune_dequeue_task(task, cpu) do { } while (0) + +#define boosted_cpu_util(cpu) cpu_util_cfs(cpu_rq(cpu)) + +#endif /* CONFIG_SCHED_TUNE */ From f6dbf1d67a4a348a9ef294928caa0b11fd710ad3 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Tue, 27 Jan 2015 13:48:07 +0000 Subject: [PATCH 0742/1276] ANDROID: sched, cpuidle: Track cpuidle state index in the scheduler The idle-state of each cpu is currently pointed to by rq->idle_state but there isn't any information in the struct cpuidle_state that can used to look up the idle-state energy model data stored in struct sched_group_energy. For this purpose is necessary to store the idle state index as well. Ideally, the idle-state data should be unified. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Change-Id: Ib3d1178512735b0e314881f73fb8ccff5a69319f Signed-off-by: Chris Redpath (cherry picked from commit a732c97420e109956c20f34c70b91e6d06f5df31) [ Fixed trivial cherry-pick conflict in kernel/sched/sched.h ] Signed-off-by: Quentin Perret --- drivers/cpuidle/cpuidle.c | 4 ++-- include/linux/cpuidle.h | 2 +- kernel/sched/idle.c | 3 ++- kernel/sched/sched.h | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 6df894d65d9e..96a3a9bf8b12 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -221,7 +221,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, } /* Take note of the planned idle state. */ - sched_idle_set_state(target_state); + sched_idle_set_state(target_state, index); trace_cpu_idle_rcuidle(index, dev->cpu); time_start = ns_to_ktime(local_clock()); @@ -235,7 +235,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* The cpu is no longer idle or about to enter idle. */ - sched_idle_set_state(NULL); + sched_idle_set_state(NULL, -1); if (broadcast) { if (WARN_ON_ONCE(!irqs_disabled())) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 4325d6fdde9b..35b067126e9a 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -219,7 +219,7 @@ static inline void cpuidle_use_deepest_state(bool enable) #endif /* kernel/sched/idle.c */ -extern void sched_idle_set_state(struct cpuidle_state *idle_state); +extern void sched_idle_set_state(struct cpuidle_state *idle_state, int index); extern void default_idle_call(void); #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 16f84142f2f4..2d3e2804c08b 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -16,9 +16,10 @@ extern char __cpuidle_text_start[], __cpuidle_text_end[]; * sched_idle_set_state - Record idle state for the current CPU. * @idle_state: State to record. */ -void sched_idle_set_state(struct cpuidle_state *idle_state) +void sched_idle_set_state(struct cpuidle_state *idle_state, int index) { idle_set_state(this_rq(), idle_state); + idle_set_state_idx(this_rq(), index); } static int __read_mostly cpu_idle_force_poll; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 16fc88a9b43e..510ea49f1222 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -944,6 +944,7 @@ struct rq { #ifdef CONFIG_CPU_IDLE /* Must be inspected within a rcu lock section */ struct cpuidle_state *idle_state; + int idle_state_idx; #endif }; @@ -1644,6 +1645,17 @@ static inline struct cpuidle_state *idle_get_state(struct rq *rq) return rq->idle_state; } + +static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx) +{ + rq->idle_state_idx = idle_state_idx; +} + +static inline int idle_get_state_idx(struct rq *rq) +{ + WARN_ON(!rcu_read_lock_held()); + return rq->idle_state_idx; +} #else static inline void idle_set_state(struct rq *rq, struct cpuidle_state *idle_state) @@ -1654,6 +1666,15 @@ static inline struct cpuidle_state *idle_get_state(struct rq *rq) { return NULL; } + +static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx) +{ +} + +static inline int idle_get_state_idx(struct rq *rq) +{ + return -1; +} #endif extern void schedule_idle(void); From 61042e2f0df48ead352e0bb8a16c903e9c9dbaaf Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 23 Jul 2018 15:14:59 +0100 Subject: [PATCH 0743/1276] ANDROID: sched: Introduce sysctl_sched_cstate_aware Introduce a new sysctl for this option, 'sched_cstate_aware'. When this is enabled, the scheduler can make use of the idle state indexes in order to break the tie between potential CPU candidates. This patch is based on 7f6fb825d6bc ("ANDROID: sched: EAS: take cstate into account when selecting idle core") from android-4.14. All the credits goes to the authors. Change-Id: Ia076cf32faff91e90905291fa6f7924dc3dd6458 Signed-off-by: Quentin Perret --- include/linux/sched/sysctl.h | 1 + kernel/sched/fair.c | 5 +++++ kernel/sysctl.c | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index a9c32daeb9d8..3f9ae132037e 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -22,6 +22,7 @@ enum { sysctl_hung_task_timeout_secs = 0 }; extern unsigned int sysctl_sched_latency; extern unsigned int sysctl_sched_min_granularity; +extern unsigned int sysctl_sched_cstate_aware; extern unsigned int sysctl_sched_wakeup_granularity; extern unsigned int sysctl_sched_child_runs_first; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1db3fdf2dfc3..edf8347bc1e2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -40,6 +40,11 @@ unsigned int sysctl_sched_latency = 6000000ULL; unsigned int normalized_sysctl_sched_latency = 6000000ULL; +/* + * Enable/disable using cstate knowledge in idle sibling selection + */ +unsigned int sysctl_sched_cstate_aware = 1; + /* * The initial- and re-scaling of tunables is configurable * diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8c7635ecb752..3f85f43a6f4a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -321,6 +321,13 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #ifdef CONFIG_SCHED_DEBUG + { + .procname = "sched_cstate_aware", + .data = &sysctl_sched_cstate_aware, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "sched_min_granularity_ns", .data = &sysctl_sched_min_granularity, From b01a6ee180891075aaa18e44f18c85e22cef806d Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 31 Aug 2018 11:24:44 +0100 Subject: [PATCH 0744/1276] ANDROID: sched/fair: Factor out CPU selection from find_energy_efficient_cpu find_energy_efficient_cpu() is composed of two steps; we first look for the CPU with the max spare capacity in each frequency domain, and then the impact on energy of each candidate is estimated. In order to make it easier to implement other CPU selection policies, let's factor the candidate selection algorithm out of find_energy_efficient_cpu(), and mark the candidates in a mask. This should result in no functional difference. Signed-off-by: Quentin Perret Change-Id: I85a28880f01fcd11d7af28f9fbc1fe0cf4f197cf --- kernel/sched/fair.c | 104 +++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index edf8347bc1e2..c9aa3db808cf 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6557,6 +6557,46 @@ static long compute_energy(struct task_struct *p, int dst_cpu, return energy; } +static void select_max_spare_cap_cpus(struct sched_domain *sd, cpumask_t *cpus, + struct perf_domain *pd, struct task_struct *p) +{ + unsigned long spare_cap, max_spare_cap, util, cpu_cap; + int cpu, max_spare_cap_cpu; + + while (pd) { + max_spare_cap_cpu = -1; + max_spare_cap = 0; + + for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { + if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) + continue; + + /* Skip CPUs that will be overutilized. */ + util = cpu_util_next(cpu, p, cpu); + cpu_cap = capacity_of(cpu); + if (cpu_cap * 1024 < util * capacity_margin) + continue; + + /* + * Find the CPU with the maximum spare capacity in + * the performance domain + */ + spare_cap = cpu_cap - util; + if (spare_cap > max_spare_cap) { + max_spare_cap = spare_cap; + max_spare_cap_cpu = cpu; + } + } + + if (max_spare_cap_cpu >= 0) + cpumask_set_cpu(max_spare_cap_cpu, cpus); + + pd = pd->next; + } +} + +static DEFINE_PER_CPU(cpumask_t, energy_cpus); + /* * find_energy_efficient_cpu(): Find most energy-efficient target CPU for the * waking task. find_energy_efficient_cpu() looks for the CPU with maximum @@ -6590,10 +6630,10 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, struct perf_domain *pd) { unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; - int cpu, best_energy_cpu = prev_cpu; - struct perf_domain *head = pd; - unsigned long cpu_cap, util; + int weight, cpu, best_energy_cpu = prev_cpu; + unsigned long cur_energy; struct sched_domain *sd; + cpumask_t *candidates; sync_entity_load_avg(&p->se); @@ -6610,48 +6650,30 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, if (!sd) return prev_cpu; - while (pd) { - unsigned long cur_energy, spare_cap, max_spare_cap = 0; - int max_spare_cap_cpu = -1; - - for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { - if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) - continue; + /* Pre-select a set of candidate CPUs. */ + candidates = this_cpu_ptr(&energy_cpus); + cpumask_clear(candidates); + select_max_spare_cap_cpus(sd, candidates, pd, p); - /* Skip CPUs that will be overutilized. */ - util = cpu_util_next(cpu, p, cpu); - cpu_cap = capacity_of(cpu); - if (cpu_cap * 1024 < util * capacity_margin) - continue; - - /* Always use prev_cpu as a candidate. */ - if (cpu == prev_cpu) { - prev_energy = compute_energy(p, prev_cpu, head); - if (prev_energy < best_energy) - best_energy = prev_energy; - continue; - } + /* Bail out if there is no candidate, or if the only one is prev_cpu */ + weight = cpumask_weight(candidates); + if (!weight || (weight == 1 && cpumask_first(candidates) == prev_cpu)) + return prev_cpu; - /* - * Find the CPU with the maximum spare capacity in - * the performance domain - */ - spare_cap = cpu_cap - util; - if (spare_cap > max_spare_cap) { - max_spare_cap = spare_cap; - max_spare_cap_cpu = cpu; - } - } + if (cpumask_test_cpu(prev_cpu, &p->cpus_allowed)) + prev_energy = best_energy = compute_energy(p, prev_cpu, pd); + else + prev_energy = best_energy = ULONG_MAX; - /* Evaluate the energy impact of using this CPU. */ - if (max_spare_cap_cpu >= 0) { - cur_energy = compute_energy(p, max_spare_cap_cpu, head); - if (cur_energy < best_energy) { - best_energy = cur_energy; - best_energy_cpu = max_spare_cap_cpu; - } + /* Select the best candidate energy-wise. */ + for_each_cpu(cpu, candidates) { + if (cpu == prev_cpu) + continue; + cur_energy = compute_energy(p, cpu, pd); + if (cur_energy < best_energy) { + best_energy = cur_energy; + best_energy_cpu = cpu; } - pd = pd->next; } /* From 3842d1f6d625ab4e1b39dcd0cae95d77a19f373d Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 19 Dec 2017 19:43:01 +0000 Subject: [PATCH 0745/1276] ANDROID: Add find_best_target to minimise energy calculation overhead find_best_target started life as an optimised energy cpu selection algorithm designed to be efficient and high performance and integrate well with schedtune and userspace cpuset configuration. It has had many many small tweaks over time, and the version added here is forward ported from the version used in android-4.4 and android-4.9. The linkage to the rest of EAS is slightly different, however. This version is split into the three main use-cases and addresses them in priority order: A) latency sensitive tasks B) non latency sensitive tasks on IDLE CPUs C) non latency sensitive tasks on ACTIVE CPUs Case A) Latency sensitive tasks Unconditionally favoring tasks that prefer idle CPU to improve latency. When we do enter here, we are looking for: - an idle CPU, whatever its idle_state is, since the first CPUs we explore are more likely to be reserved for latency sensitive tasks. - a non idle CPU where the task fits in its current capacity and has the maximum spare capacity. - a non idle CPU with lower contention from other tasks and running at the lowest possible OPP. The last two goals try to favor a non idle CPU where the task can run as if it is "almost alone". A maximum spare capacity CPU is favored since the task already fits into that CPU's capacity without waiting for an OPP change. For any case other than case A, we avoid CPUs which would become overutilized if we placed the task there. Case B) Non latency sensitive tasks on IDLE CPUs. Find an optimal backup IDLE CPU for non latency sensitive tasks. Here we are looking for: - minimizing the capacity_orig, i.e. preferring LITTLE CPUs If IDLE cpus are available, we prefer to choose one in order to spread tasks and improve performance. Case C) Non latency sensitive tasks on ACTIVE CPUs. Pack tasks in the most energy efficient capacities. This task packing strategy prefers more energy efficient CPUs (i.e. pack on smaller maximum capacity CPUs) while also trying to spread tasks to run them all at the lower OPP. This assumes for example that it's more energy efficient to run two tasks on two CPUs at a lower OPP than packing both on a single CPU but running that CPU at an higher OPP. This code has had many other contributors over the development listed here as Cc. Cc: Ke Wang Cc: Joel Fernandes Cc: Patrick Bellasi Cc: Valentin Schneider Cc: Dietmar Eggemann Cc: Srinath Sridharan Cc: Todd Kjos Cc: Juri Lelli Signed-off-by: Chris Redpath (cherry picked from commit f240e44406558b17ff7765f252b0bcdcbc15126f) [ - Removed tracepoints - Took capacity_curr_of() from "7f6fb82 ANDROID: sched: EAS: take cstate into account when selecting idle core" - Re-use sd_ea from find_energy_efficient_cpu() / removed start_cpu() - Mark candidates with a cpumask given by feec() - Squashed Ionela's tri-gear fbt fixes from android-4.14 - Squashed Patrick's changes related to util_est from android-4.14 ] Signed-off-by: Quentin Perret Change-Id: I9500d308c879dd53b08adeda8f988238e39cc392 --- kernel/sched/fair.c | 318 +++++++++++++++++++++++++++++++++++++++- kernel/sched/features.h | 5 + 2 files changed, 322 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c9aa3db808cf..2643f0e0beb2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6453,6 +6453,318 @@ static unsigned long cpu_util_wake(int cpu, struct task_struct *p) return min_t(unsigned long, util, capacity_orig_of(cpu)); } +/* + * Returns the current capacity of cpu after applying both + * cpu and freq scaling. + */ +unsigned long capacity_curr_of(int cpu) +{ + unsigned long max_cap = cpu_rq(cpu)->cpu_capacity_orig; + unsigned long scale_freq = arch_scale_freq_capacity(cpu); + + return cap_scale(max_cap, scale_freq); +} + +static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, + struct task_struct *p) +{ + unsigned long min_util = boosted_task_util(p); + unsigned long target_capacity = ULONG_MAX; + unsigned long min_wake_util = ULONG_MAX; + unsigned long target_max_spare_cap = 0; + unsigned long target_util = ULONG_MAX; + bool prefer_idle = schedtune_prefer_idle(p); + bool boosted = schedtune_task_boost(p) > 0; + /* Initialise with deepest possible cstate (INT_MAX) */ + int shallowest_idle_cstate = INT_MAX; + struct sched_group *sg; + int best_active_cpu = -1; + int best_idle_cpu = -1; + int target_cpu = -1; + int backup_cpu = -1; + int i; + + /* + * In most cases, target_capacity tracks capacity_orig of the most + * energy efficient CPU candidate, thus requiring to minimise + * target_capacity. For these cases target_capacity is already + * initialized to ULONG_MAX. + * However, for prefer_idle and boosted tasks we look for a high + * performance CPU, thus requiring to maximise target_capacity. In this + * case we initialise target_capacity to 0. + */ + if (prefer_idle && boosted) + target_capacity = 0; + + /* Scan CPUs in all SDs */ + sg = sd->groups; + do { + for_each_cpu_and(i, &p->cpus_allowed, sched_group_span(sg)) { + unsigned long capacity_curr = capacity_curr_of(i); + unsigned long capacity_orig = capacity_orig_of(i); + unsigned long wake_util, new_util; + long spare_cap; + int idle_idx = INT_MAX; + + if (!cpu_online(i)) + continue; + + /* + * p's blocked utilization is still accounted for on prev_cpu + * so prev_cpu will receive a negative bias due to the double + * accounting. However, the blocked utilization may be zero. + */ + wake_util = cpu_util_wake(i, p); + new_util = wake_util + task_util_est(p); + + /* + * Ensure minimum capacity to grant the required boost. + * The target CPU can be already at a capacity level higher + * than the one required to boost the task. + */ + new_util = max(min_util, new_util); + if (new_util > capacity_orig) + continue; + + /* + * Pre-compute the maximum possible capacity we expect + * to have available on this CPU once the task is + * enqueued here. + */ + spare_cap = capacity_orig - new_util; + + if (idle_cpu(i)) + idle_idx = idle_get_state_idx(cpu_rq(i)); + + + /* + * Case A) Latency sensitive tasks + * + * Unconditionally favoring tasks that prefer idle CPU to + * improve latency. + * + * Looking for: + * - an idle CPU, whatever its idle_state is, since + * the first CPUs we explore are more likely to be + * reserved for latency sensitive tasks. + * - a non idle CPU where the task fits in its current + * capacity and has the maximum spare capacity. + * - a non idle CPU with lower contention from other + * tasks and running at the lowest possible OPP. + * + * The last two goals tries to favor a non idle CPU + * where the task can run as if it is "almost alone". + * A maximum spare capacity CPU is favoured since + * the task already fits into that CPU's capacity + * without waiting for an OPP chance. + * + * The following code path is the only one in the CPUs + * exploration loop which is always used by + * prefer_idle tasks. It exits the loop with wither a + * best_active_cpu or a target_cpu which should + * represent an optimal choice for latency sensitive + * tasks. + */ + if (prefer_idle) { + + /* + * Case A.1: IDLE CPU + * Return the best IDLE CPU we find: + * - for boosted tasks: the CPU with the highest + * performance (i.e. biggest capacity_orig) + * - for !boosted tasks: the most energy + * efficient CPU (i.e. smallest capacity_orig) + */ + if (idle_cpu(i)) { + if (boosted && + capacity_orig < target_capacity) + continue; + if (!boosted && + capacity_orig > target_capacity) + continue; + /* + * Minimise value of idle state: skip + * deeper idle states and pick the + * shallowest. + */ + if (capacity_orig == target_capacity && + sysctl_sched_cstate_aware && + idle_idx >= shallowest_idle_cstate) + continue; + + target_capacity = capacity_orig; + shallowest_idle_cstate = idle_idx; + best_idle_cpu = i; + continue; + } + if (best_idle_cpu != -1) + continue; + + /* + * Case A.2: Target ACTIVE CPU + * Favor CPUs with max spare capacity. + */ + if (capacity_curr > new_util && + spare_cap > target_max_spare_cap) { + target_max_spare_cap = spare_cap; + target_cpu = i; + continue; + } + if (target_cpu != -1) + continue; + + + /* + * Case A.3: Backup ACTIVE CPU + * Favor CPUs with: + * - lower utilization due to other tasks + * - lower utilization with the task in + */ + if (wake_util > min_wake_util) + continue; + min_wake_util = wake_util; + best_active_cpu = i; + continue; + } + + /* + * Enforce EAS mode + * + * For non latency sensitive tasks, skip CPUs that + * will be overutilized by moving the task there. + * + * The goal here is to remain in EAS mode as long as + * possible at least for !prefer_idle tasks. + */ + if ((new_util * capacity_margin) > + (capacity_orig * SCHED_CAPACITY_SCALE)) + continue; + + /* + * Favor CPUs with smaller capacity for non latency + * sensitive tasks. + */ + if (capacity_orig > target_capacity) + continue; + + /* + * Case B) Non latency sensitive tasks on IDLE CPUs. + * + * Find an optimal backup IDLE CPU for non latency + * sensitive tasks. + * + * Looking for: + * - minimizing the capacity_orig, + * i.e. preferring LITTLE CPUs + * - favoring shallowest idle states + * i.e. avoid to wakeup deep-idle CPUs + * + * The following code path is used by non latency + * sensitive tasks if IDLE CPUs are available. If at + * least one of such CPUs are available it sets the + * best_idle_cpu to the most suitable idle CPU to be + * selected. + * + * If idle CPUs are available, favour these CPUs to + * improve performances by spreading tasks. + * Indeed, the energy_diff() computed by the caller + * will take care to ensure the minimization of energy + * consumptions without affecting performance. + */ + if (idle_cpu(i)) { + /* + * Skip CPUs in deeper idle state, but only + * if they are also less energy efficient. + * IOW, prefer a deep IDLE LITTLE CPU vs a + * shallow idle big CPU. + */ + if (capacity_orig == target_capacity && + sysctl_sched_cstate_aware && + idle_idx >= shallowest_idle_cstate) + continue; + + target_capacity = capacity_orig; + shallowest_idle_cstate = idle_idx; + best_idle_cpu = i; + continue; + } + + /* + * Case C) Non latency sensitive tasks on ACTIVE CPUs. + * + * Pack tasks in the most energy efficient capacities. + * + * This task packing strategy prefers more energy + * efficient CPUs (i.e. pack on smaller maximum + * capacity CPUs) while also trying to spread tasks to + * run them all at the lower OPP. + * + * This assumes for example that it's more energy + * efficient to run two tasks on two CPUs at a lower + * OPP than packing both on a single CPU but running + * that CPU at an higher OPP. + * + * Thus, this case keep track of the CPU with the + * smallest maximum capacity and highest spare maximum + * capacity. + */ + + /* Favor CPUs with maximum spare capacity */ + if (capacity_orig == target_capacity && + spare_cap < target_max_spare_cap) + continue; + + target_max_spare_cap = spare_cap; + target_capacity = capacity_orig; + target_util = new_util; + target_cpu = i; + } + + } while (sg = sg->next, sg != sd->groups); + + /* + * For non latency sensitive tasks, cases B and C in the previous loop, + * we pick the best IDLE CPU only if we was not able to find a target + * ACTIVE CPU. + * + * Policies priorities: + * + * - prefer_idle tasks: + * + * a) IDLE CPU available: best_idle_cpu + * b) ACTIVE CPU where task fits and has the bigger maximum spare + * capacity (i.e. target_cpu) + * c) ACTIVE CPU with less contention due to other tasks + * (i.e. best_active_cpu) + * + * - NON prefer_idle tasks: + * + * a) ACTIVE CPU: target_cpu + * b) IDLE CPU: best_idle_cpu + */ + + if (prefer_idle && (best_idle_cpu != -1)) { + target_cpu = best_idle_cpu; + goto target; + } + + if (target_cpu == -1) + target_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; + else + backup_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; + + if (backup_cpu >= 0) + cpumask_set_cpu(backup_cpu, cpus); + if (target_cpu >= 0) { +target: + cpumask_set_cpu(target_cpu, cpus); + } +} + /* * Disable WAKE_AFFINE in the case where task @p doesn't fit in the * capacity of either the waking CPU @cpu or the previous CPU @prev_cpu. @@ -6653,7 +6965,11 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, /* Pre-select a set of candidate CPUs. */ candidates = this_cpu_ptr(&energy_cpus); cpumask_clear(candidates); - select_max_spare_cap_cpus(sd, candidates, pd, p); + + if (sched_feat(FIND_BEST_TARGET)) + find_best_target(sd, candidates, p); + else + select_max_spare_cap_cpus(sd, candidates, pd, p); /* Bail out if there is no candidate, or if the only one is prev_cpu */ weight = cpumask_weight(candidates); diff --git a/kernel/sched/features.h b/kernel/sched/features.h index a49c0701a2f2..3fe0fb0b4465 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -97,3 +97,8 @@ SCHED_FEAT(UTIL_EST, true) * root domain having an Energy Model attached. */ SCHED_FEAT(ENERGY_AWARE, true) + +/* + * Fast pre-selection of CPU candidates for EAS. + */ +SCHED_FEAT(FIND_BEST_TARGET, true) From 0faf7aff802e1e0cb1c708b581e3ba84548632cf Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Sat, 21 Oct 2017 15:56:50 +0100 Subject: [PATCH 0746/1276] ANDROID: sched: Unconditionally honor sync flag for energy-aware wakeups Since we don't do energy-aware wakeups when we are overutilized, always honoring sync wakeups in this state does not prevent wake-wide mechanics overruling the flag as normal. This patch is based upon previous work to build EAS for android products. sync-hint code taken from commit 4a5e890ec60d "sched/fair: add tunable to force selection at cpu granularity" written by Juri Lelli Signed-off-by: Chris Redpath (cherry-picked from commit f1ec666a62dec1083ed52fe1ddef093b84373aaf) [ Moved the feature to find_energy_efficient_cpu() ] Signed-off-by: Quentin Perret Change-Id: I4b3d79141fc8e53dc51cd63ac11096c2e3cb10f5 --- include/linux/sched/sysctl.h | 1 + kernel/sched/fair.c | 15 +++++++++++++-- kernel/sysctl.c | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index 3f9ae132037e..77e9fa3c5b49 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -22,6 +22,7 @@ enum { sysctl_hung_task_timeout_secs = 0 }; extern unsigned int sysctl_sched_latency; extern unsigned int sysctl_sched_min_granularity; +extern unsigned int sysctl_sched_sync_hint_enable; extern unsigned int sysctl_sched_cstate_aware; extern unsigned int sysctl_sched_wakeup_granularity; extern unsigned int sysctl_sched_child_runs_first; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2643f0e0beb2..82bb2a62edd8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -40,6 +40,11 @@ unsigned int sysctl_sched_latency = 6000000ULL; unsigned int normalized_sysctl_sched_latency = 6000000ULL; +/* + * Enable/disable honoring sync flag in energy-aware wakeups. + */ +unsigned int sysctl_sched_sync_hint_enable = 1; + /* * Enable/disable using cstate knowledge in idle sibling selection */ @@ -6939,7 +6944,7 @@ static DEFINE_PER_CPU(cpumask_t, energy_cpus); * SD_ASYM_CPUCAPACITY is set. */ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, - struct perf_domain *pd) + struct perf_domain *pd, int sync) { unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; int weight, cpu, best_energy_cpu = prev_cpu; @@ -6947,6 +6952,12 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, struct sched_domain *sd; cpumask_t *candidates; + if (sysctl_sched_sync_hint_enable && sync) { + cpu = smp_processor_id(); + if (cpumask_test_cpu(cpu, &p->cpus_allowed)) + return cpu; + } + sync_entity_load_avg(&p->se); if (!task_util_est(p)) @@ -7046,7 +7057,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f struct perf_domain *pd = rcu_dereference(rd->pd); if (pd && !READ_ONCE(rd->overutilized)) { - new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd); + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); goto unlock; } } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 3f85f43a6f4a..ed676ec4ddac 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -346,6 +346,13 @@ static struct ctl_table kern_table[] = { .extra1 = &min_sched_granularity_ns, .extra2 = &max_sched_granularity_ns, }, + { + .procname = "sched_sync_hint_enable", + .data = &sysctl_sched_sync_hint_enable, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "sched_wakeup_granularity_ns", .data = &sysctl_sched_wakeup_granularity, From ac0e1470bf13e3fa88a3f992ad4f42457513c054 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 11 Aug 2017 10:45:58 +0100 Subject: [PATCH 0747/1276] FROMLIST: sched/fair: Use wake_q length as a hint for wake_wide This patch adds a parameter to select_task_rq, sibling_count_hint allowing the caller, where it has this information, to inform the sched_class the number of tasks that are being woken up as part of the same event. The wake_q mechanism is one case where this information is available. select_task_rq_fair can then use the information to detect that it needs to widen the search space for task placement in order to avoid overloading the last-level cache domain's CPUs. * * * The reason I am investigating this change is the following use case on ARM big.LITTLE (asymmetrical CPU capacity): 1 task per CPU, which all repeatedly do X amount of work then pthread_barrier_wait (i.e. sleep until the last task finishes its X and hits the barrier). On big.LITTLE, the tasks which get a "big" CPU finish faster, and then those CPUs pull over the tasks that are still running: v CPU v ->time-> ------------- 0 (big) 11111 /333 ------------- 1 (big) 22222 /444| ------------- 2 (LITTLE) 333333/ ------------- 3 (LITTLE) 444444/ ------------- Now when task 4 hits the barrier (at |) and wakes the others up, there are 4 tasks with prev_cpu= and 0 tasks with prev_cpu=. want_affine therefore means that we'll only look in CPUs 0 and 1 (sd_llc), so tasks will be unnecessarily coscheduled on the bigs until the next load balance, something like this: v CPU v ->time-> ------------------------ 0 (big) 11111 /333 31313\33333 ------------------------ 1 (big) 22222 /444|424\4444444 ------------------------ 2 (LITTLE) 333333/ \222222 ------------------------ 3 (LITTLE) 444444/ \1111 ------------------------ ^^^ underutilization So, I'm trying to get want_affine = 0 for these tasks. I don't _think_ any incarnation of the wakee_flips mechanism can help us here because which task is waker and which tasks are wakees generally changes with each iteration. However pthread_barrier_wait (or more accurately FUTEX_WAKE) has the nice property that we know exactly how many tasks are being woken, so we can cheat. It might be a disadvantage that we "widen" _every_ task that's woken in an event, while select_idle_sibling would work fine for the first sd_llc_size - 1 tasks. IIUC, if wake_affine() behaves correctly this trick wouldn't be necessary on SMP systems, so it might be best guarded by the presence of SD_ASYM_CPUCAPACITY? * * * Final note.. In order to observe "perfect" behaviour for this use case, I also had to disable the TTWU_QUEUE sched feature. Suppose during the wakeup above we are working through the work queue and have placed tasks 3 and 2, and are about to place task 1: v CPU v ->time-> -------------- 0 (big) 11111 /333 3 -------------- 1 (big) 22222 /444|4 -------------- 2 (LITTLE) 333333/ 2 -------------- 3 (LITTLE) 444444/ <- Task 1 should go here -------------- If TTWU_QUEUE is enabled, we will not yet have enqueued task 2 (having instead sent a reschedule IPI) or attached its load to CPU 2. So we are likely to also place task 1 on cpu 2. Disabling TTWU_QUEUE means that we enqueue task 2 before placing task 1, solving this issue. TTWU_QUEUE is there to minimise rq lock contention, and I guess that this contention is less of an issue on big.LITTLE systems since they have relatively few CPUs, which suggests the trade-off makes sense here. Signed-off-by: Brendan Jackman Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Josef Bacik Cc: Joel Fernandes Cc: Mike Galbraith Cc: Matt Fleming ( - Applied from https://patchwork.kernel.org/patch/9895261/ - Fixed trivial conflict in kernel/sched/core.c - Fixed select_task_rq_idle, now in kernel/sched/idle.c - Fixed trivial conflict in select_task_rq_fair ) Signed-off-by: Quentin Perret Change-Id: I3cfc4bf48c3d7feef969db4d22449f4fbb4f795d --- include/linux/sched/wake_q.h | 2 ++ kernel/sched/core.c | 34 +++++++++++++++++++++++----------- kernel/sched/deadline.c | 3 ++- kernel/sched/fair.c | 15 ++++++++++----- kernel/sched/idle.c | 3 ++- kernel/sched/rt.c | 3 ++- kernel/sched/sched.h | 3 ++- kernel/sched/stop_task.c | 3 ++- 8 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/linux/sched/wake_q.h b/include/linux/sched/wake_q.h index 10b19a192b2d..a3661e93da6f 100644 --- a/include/linux/sched/wake_q.h +++ b/include/linux/sched/wake_q.h @@ -34,6 +34,7 @@ struct wake_q_head { struct wake_q_node *first; struct wake_q_node **lastp; + int count; }; #define WAKE_Q_TAIL ((struct wake_q_node *) 0x01) @@ -45,6 +46,7 @@ static inline void wake_q_init(struct wake_q_head *head) { head->first = WAKE_Q_TAIL; head->lastp = &head->first; + head->count = 0; } extern void wake_q_add(struct wake_q_head *head, diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 1d2db12cbab6..619a31b94e5b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -412,6 +412,8 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task) if (cmpxchg(&node->next, NULL, WAKE_Q_TAIL)) return; + head->count++; + get_task_struct(task); /* @@ -421,6 +423,10 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task) head->lastp = &node->next; } +static int +try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags, + int sibling_count_hint); + void wake_up_q(struct wake_q_head *head) { struct wake_q_node *node = head->first; @@ -435,10 +441,10 @@ void wake_up_q(struct wake_q_head *head) task->wake_q.next = NULL; /* - * wake_up_process() executes a full barrier, which pairs with + * try_to_wake_up() executes a full barrier, which pairs with * the queueing in wake_q_add() so as not to miss wakeups. */ - wake_up_process(task); + try_to_wake_up(task, TASK_NORMAL, 0, head->count); put_task_struct(task); } } @@ -1523,12 +1529,14 @@ static int select_fallback_rq(int cpu, struct task_struct *p) * The caller (fork, wakeup) owns p->pi_lock, ->cpus_allowed is stable. */ static inline -int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags) +int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags, + int sibling_count_hint) { lockdep_assert_held(&p->pi_lock); if (p->nr_cpus_allowed > 1) - cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags); + cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags, + sibling_count_hint); else cpu = cpumask_any(&p->cpus_allowed); @@ -1931,6 +1939,8 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) * @p: the thread to be awakened * @state: the mask of task states that can be woken * @wake_flags: wake modifier flags (WF_*) + * @sibling_count_hint: A hint at the number of threads that are being woken up + * in this event. * * If (@state & @p->state) @p->state = TASK_RUNNING. * @@ -1946,7 +1956,8 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) * %false otherwise. */ static int -try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) +try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags, + int sibling_count_hint) { unsigned long flags; int cpu, success = 0; @@ -2033,7 +2044,8 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) atomic_dec(&task_rq(p)->nr_iowait); } - cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); + cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags, + sibling_count_hint); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); @@ -2120,13 +2132,13 @@ static void try_to_wake_up_local(struct task_struct *p, struct rq_flags *rf) */ int wake_up_process(struct task_struct *p) { - return try_to_wake_up(p, TASK_NORMAL, 0); + return try_to_wake_up(p, TASK_NORMAL, 0, 1); } EXPORT_SYMBOL(wake_up_process); int wake_up_state(struct task_struct *p, unsigned int state) { - return try_to_wake_up(p, state, 0); + return try_to_wake_up(p, state, 0, 1); } /* @@ -2408,7 +2420,7 @@ void wake_up_new_task(struct task_struct *p) * as we're not fully set-up yet. */ p->recent_used_cpu = task_cpu(p); - __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0)); + __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0, 1)); #endif rq = __task_rq_lock(p, &rf); update_rq_clock(rq); @@ -2939,7 +2951,7 @@ void sched_exec(void) int dest_cpu; raw_spin_lock_irqsave(&p->pi_lock, flags); - dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0); + dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0, 1); if (dest_cpu == smp_processor_id()) goto unlock; @@ -3700,7 +3712,7 @@ asmlinkage __visible void __sched preempt_schedule_irq(void) int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags, void *key) { - return try_to_wake_up(curr->private, mode, wake_flags); + return try_to_wake_up(curr->private, mode, wake_flags, 1); } EXPORT_SYMBOL(default_wake_function); diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 91e4202b0634..921685482cc0 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1567,7 +1567,8 @@ static void yield_task_dl(struct rq *rq) static int find_later_rq(struct task_struct *task); static int -select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { struct task_struct *curr; struct rq *rq; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 82bb2a62edd8..28551d0f5376 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5657,15 +5657,18 @@ static void record_wakee(struct task_struct *p) * whatever is irrelevant, spread criteria is apparent partner count exceeds * socket size. */ -static int wake_wide(struct task_struct *p) +static int wake_wide(struct task_struct *p, int sibling_count_hint) { unsigned int master = current->wakee_flips; unsigned int slave = p->wakee_flips; - int factor = this_cpu_read(sd_llc_size); + int llc_size = this_cpu_read(sd_llc_size); + + if (sibling_count_hint >= llc_size) + return 1; if (master < slave) swap(master, slave); - if (slave < factor || master < slave * factor) + if (slave < llc_size || master < slave * llc_size) return 0; return 1; } @@ -7027,7 +7030,8 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, * preempt must be disabled. */ static int -select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) +select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags, + int sibling_count_hint) { struct sched_domain *tmp, *sd = NULL; int cpu = smp_processor_id(); @@ -7062,7 +7066,8 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f } } - want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && + want_affine = !wake_wide(p, sibling_count_hint) && + !wake_cap(p, cpu, prev_cpu) && cpumask_test_cpu(cpu, &p->cpus_allowed); } diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 2d3e2804c08b..2c8719f1de0a 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -375,7 +375,8 @@ void cpu_startup_entry(enum cpuhp_state state) #ifdef CONFIG_SMP static int -select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { return task_cpu(p); /* IDLE tasks as never migrated */ } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 2e2955a8cf8f..0be707d9c2db 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1386,7 +1386,8 @@ static void yield_task_rt(struct rq *rq) static int find_lowest_rq(struct task_struct *task); static int -select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { struct task_struct *curr; struct rq *rq; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 510ea49f1222..b1baf6d9dabc 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1557,7 +1557,8 @@ struct sched_class { void (*put_prev_task)(struct rq *rq, struct task_struct *p); #ifdef CONFIG_SMP - int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags); + int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags, + int subling_count_hint); void (*migrate_task_rq)(struct task_struct *p, int new_cpu); void (*task_woken)(struct rq *this_rq, struct task_struct *task); diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index c183b790ca54..6446d6130c5d 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -11,7 +11,8 @@ #ifdef CONFIG_SMP static int -select_task_rq_stop(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_stop(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { return task_cpu(p); /* stop tasks as never migrate */ } From 1546d05f66b586993aff9454b864bb188c4550ee Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 19 Dec 2017 19:32:02 +0000 Subject: [PATCH 0748/1276] ANDROID: sched: fair: Bypass energy-aware wakeup for prefer-idle tasks Use the upstream slow path to find an idle cpu for prefer-idle tasks. This slow-path is actually faster than the EAS path we are currently going through (compute_energy()) which is really slow. No performance degradation is seen with this and it reduces the delta quite a bit between upstream and out of tree code. It's not clear yet if using the mainline slow path task placement when a task has the schedtune attribute prefer_idle=1 is the right thing to do for products. Put the option to disable this behind a sched feature so we can try out both options. Signed-off-by: Joel Fernandes (refactored for 4.14 version) Signed-off-by: Chris Redpath (cherry picked from commit c0ff131c88f68e4985793663144b6f9cf77be9d3) [ - Refactored for 4.17 version - Adjusted the commit header to the new function names ] Signed-off-by: Quentin Perret Change-Id: Icf762a101c92c0e3f9e61df0370247fa15455581 --- kernel/sched/fair.c | 14 ++++++++++---- kernel/sched/features.h | 9 +++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 28551d0f5376..9e5f9c0cd709 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7060,17 +7060,23 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f struct root_domain *rd = cpu_rq(cpu)->rd; struct perf_domain *pd = rcu_dereference(rd->pd); - if (pd && !READ_ONCE(rd->overutilized)) { - new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); - goto unlock; - } + if (!pd || READ_ONCE(rd->overutilized)) + goto affine; + + if (schedtune_prefer_idle(p) && !sched_feat(EAS_PREFER_IDLE) && !sync) + goto sd_loop; + + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); + goto unlock; } +affine: want_affine = !wake_wide(p, sibling_count_hint) && !wake_cap(p, cpu, prev_cpu) && cpumask_test_cpu(cpu, &p->cpus_allowed); } +sd_loop: for_each_domain(cpu, tmp) { if (!(tmp->flags & SD_LOAD_BALANCE)) break; diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 3fe0fb0b4465..2c33917b2067 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -102,3 +102,12 @@ SCHED_FEAT(ENERGY_AWARE, true) * Fast pre-selection of CPU candidates for EAS. */ SCHED_FEAT(FIND_BEST_TARGET, true) + +/* + * Energy aware scheduling algorithm choices: + * EAS_PREFER_IDLE + * Direct tasks in a schedtune.prefer_idle=1 group through + * the EAS path for wakeup task placement. Otherwise, put + * those tasks through the mainline slow path. + */ +SCHED_FEAT(EAS_PREFER_IDLE, true) From 3cfab4f41eb697a2e24c5467e755c06c4bab2b6a Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 24 May 2018 17:35:02 +0100 Subject: [PATCH 0749/1276] ANDROID: sched/fair: Bypass energy computation for prefer_idle tasks If the only pre-selected candidate CPU in find_energy_efficient_cpu() happens to be prev_cpu, there is not point in computing the system energy since we have nothing to compare it against, so we currently bail out early. The same logic can be extended when prefer_idle tasks are routed in the energy-aware wake-up path: if the only candidate is idle for a prefer_idle task, just select it no matter what the energy impact is. That should help speeding-up wake-ups of prefer_idle tasks, at least when find_best_target() is used for them. Signed-off-by: Quentin Perret Change-Id: Idd0e387e4a766061cc05d2584df3a31e4dabfd09 --- kernel/sched/fair.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9e5f9c0cd709..637ae1eae58f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6985,11 +6985,17 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, else select_max_spare_cap_cpus(sd, candidates, pd, p); - /* Bail out if there is no candidate, or if the only one is prev_cpu */ + /* Bail out if no candidate was found. */ weight = cpumask_weight(candidates); - if (!weight || (weight == 1 && cpumask_first(candidates) == prev_cpu)) + if (!weight) return prev_cpu; + /* If there is only one sensible candidate, select it now. */ + cpu = cpumask_first(candidates); + if (weight == 1 && ((schedtune_prefer_idle(p) && idle_cpu(cpu)) || + (cpu == prev_cpu))) + return cpu; + if (cpumask_test_cpu(prev_cpu, &p->cpus_allowed)) prev_energy = best_energy = compute_energy(p, prev_cpu, pd); else From c839f856318a806af76d539b604fc7d98c7c4c99 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 15:48:06 +0100 Subject: [PATCH 0750/1276] ANDROID: sched/fair: add arch scaling function for max frequency capping To be able to scale the cpu capacity by this factor introduce a call to the new arch scaling function arch_scale_max_freq_capacity() in update_cpu_capacity() and provide a default implementation which returns SCHED_CAPACITY_SCALE. Another subsystem (e.g. cpufreq) or architectural or platform specific code can overwrite this default implementation, exactly as for frequency and cpu invariance. It has to be enabled by the arch by defining arch_scale_max_freq_capacity to the actual implementation. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann ( Fixed conflict with scaling against the PELT-based scale_rt_capacity ) Signed-off-by: Quentin Perret Change-Id: I770a8b1f4f7340e9e314f71c64a765bf880f4b4d --- kernel/sched/fair.c | 12 ++++++++---- kernel/sched/sched.h | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 637ae1eae58f..2ced8d17212a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8353,10 +8353,9 @@ static inline int get_sd_load_idx(struct sched_domain *sd, return load_idx; } -static unsigned long scale_rt_capacity(struct sched_domain *sd, int cpu) +static unsigned long scale_rt_capacity(int cpu, unsigned long max) { struct rq *rq = cpu_rq(cpu); - unsigned long max = arch_scale_cpu_capacity(sd, cpu); unsigned long used, free; unsigned long irq; @@ -8378,10 +8377,15 @@ static unsigned long scale_rt_capacity(struct sched_domain *sd, int cpu) static void update_cpu_capacity(struct sched_domain *sd, int cpu) { - unsigned long capacity = scale_rt_capacity(sd, cpu); + unsigned long capacity = arch_scale_cpu_capacity(sd, cpu); struct sched_group *sdg = sd->groups; - cpu_rq(cpu)->cpu_capacity_orig = arch_scale_cpu_capacity(sd, cpu); + cpu_rq(cpu)->cpu_capacity_orig = capacity; + + capacity *= arch_scale_max_freq_capacity(sd, cpu); + capacity >>= SCHED_CAPACITY_SHIFT; + + capacity = scale_rt_capacity(cpu, capacity); if (!capacity) capacity = 1; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b1baf6d9dabc..69808442598c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1809,6 +1809,15 @@ unsigned long arch_scale_freq_capacity(int cpu) } #endif +#ifndef arch_scale_max_freq_capacity +struct sched_domain; +static __always_inline +unsigned long arch_scale_max_freq_capacity(struct sched_domain *sd, int cpu) +{ + return SCHED_CAPACITY_SCALE; +} +#endif + struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf) __acquires(rq->lock); From 8474f300b23a2c1a705c8e86bb06930408390ab5 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:52:33 +0100 Subject: [PATCH 0751/1276] ANDROID: implement max frequency capping Implements the Max Frequency Capping Engine (MFCE) getter function topology_get_max_freq_scale() to provide the scheduler with a maximum frequency scaling correction factor for more accurate cpu capacity handling by being able to deal with max frequency capping. This scaling factor describes the influence of running a cpu with a current maximum frequency (policy) lower than the maximum possible frequency (cpuinfo). The factor is: policy_max_freq(cpu) << SCHED_CAPACITY_SHIFT / cpuinfo_max_freq(cpu) It also implements the MFCE setter function arch_set_max_freq_scale() which is called from cpufreq_set_policy(). Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann [Trivial cherry-pick issue in cpufreq.c] Signed-off-by: Quentin Perret Change-Id: I59e52861ee260755ab0518fe1f7183a2e4e3d0fc --- drivers/base/arch_topology.c | 25 ++++++++++++++++++++++++- drivers/cpufreq/cpufreq.c | 8 ++++++++ include/linux/arch_topology.h | 8 ++++++++ include/linux/cpufreq.h | 2 ++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index edfcf8d982e4..b5f61f2840d0 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -18,6 +18,8 @@ #include DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; +DEFINE_PER_CPU(unsigned long, max_cpu_freq); +DEFINE_PER_CPU(unsigned long, max_freq_scale) = SCHED_CAPACITY_SCALE; void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq) @@ -27,8 +29,29 @@ void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; - for_each_cpu(i, cpus) + for_each_cpu(i, cpus) { per_cpu(freq_scale, i) = scale; + per_cpu(max_cpu_freq, i) = max_freq; + } +} + +void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq) +{ + unsigned long scale, max_freq; + int cpu = cpumask_first(cpus); + + if (cpu > nr_cpu_ids) + return; + + max_freq = per_cpu(max_cpu_freq, cpu); + if (!max_freq) + return; + + scale = (policy_max_freq << SCHED_CAPACITY_SHIFT) / max_freq; + + for_each_cpu(cpu, cpus) + per_cpu(max_freq_scale, cpu) = scale; } static DEFINE_MUTEX(cpu_scale_mutex); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e1f8e760a5e8..b3c384ac6064 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -160,6 +160,12 @@ __weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, } EXPORT_SYMBOL_GPL(arch_set_freq_scale); +__weak void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq) +{ +} +EXPORT_SYMBOL_GPL(arch_set_max_freq_scale); + /* * This is a generic cpufreq init() routine which can be used by cpufreq * drivers of SMP systems. It will do following: @@ -2247,6 +2253,8 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, policy->max = new_policy->max; trace_cpu_frequency_limits(policy); + arch_set_max_freq_scale(policy->cpus, policy->max); + policy->cached_target_freq = UINT_MAX; pr_debug("new min and max freqs are %u - %u kHz\n", diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index d9bdc1a7f4e7..7e5a33ea8df0 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -33,4 +33,12 @@ unsigned long topology_get_freq_scale(int cpu) return per_cpu(freq_scale, cpu); } +DECLARE_PER_CPU(unsigned long, max_freq_scale); + +static inline +unsigned long topology_get_max_freq_scale(struct sched_domain *sd, int cpu) +{ + return per_cpu(max_freq_scale, cpu); +} + #endif /* _LINUX_ARCH_TOPOLOGY_H_ */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 882a9b9e34bc..eb7b26f53f55 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -955,6 +955,8 @@ extern unsigned int arch_freq_get_on_cpu(int cpu); extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq); +extern void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq); /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; From 2ef32e49013bfc1e04d82ad339b53ba5e0bcbfd3 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:54:16 +0100 Subject: [PATCH 0752/1276] ANDROID: arm64: enable max frequency capping Defines arch_scale_max_freq_capacity() to use the topology driver scale function. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret Change-Id: If7565747ec862e42ac55196240522ef8d22ca67d --- arch/arm64/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 0524f2438649..8e0a96d71bd6 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -42,6 +42,9 @@ int pcibus_to_node(struct pci_bus *bus); /* Replace task scheduler's default frequency-invariant accounting */ #define arch_scale_freq_capacity topology_get_freq_scale +/* Replace task scheduler's default max-frequency-invariant accounting */ +#define arch_scale_max_freq_capacity topology_get_max_freq_scale + /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale From 7e1703f2cce1ab73ae74c0194bfc467c8794f2d3 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:58:04 +0100 Subject: [PATCH 0753/1276] ANDROID: arm: enable max frequency capping Defines arch_scale_max_freq_capacity() to use the topology driver scale function. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret Change-Id: I79f444399ea3b2948364fde80ccee52a9ece5b9a --- arch/arm/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h index 2a786f54d8b8..201dc2011c16 100644 --- a/arch/arm/include/asm/topology.h +++ b/arch/arm/include/asm/topology.h @@ -30,6 +30,9 @@ const struct cpumask *cpu_coregroup_mask(int cpu); /* Replace task scheduler's default frequency-invariant accounting */ #define arch_scale_freq_capacity topology_get_freq_scale +/* Replace task scheduler's default max-frequency-invariant accounting */ +#define arch_scale_max_freq_capacity topology_get_max_freq_scale + /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale From 8f8ad288153d17793cbc883fbd40f2e9d89d790b Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Sat, 26 Sep 2015 18:19:54 +0100 Subject: [PATCH 0754/1276] ANDROID: sched: Update max cpu capacity in case of max frequency constraints Wakeup balancing uses cpu capacity awareness and needs to know the system-wide maximum cpu capacity. Patch "sched: Store system-wide maximum cpu capacity in root domain" finds the system-wide maximum cpu capacity during scheduler domain hierarchy setup. This is sufficient as long as maximum frequency invariance is not enabled. If it is enabled, the system-wide maximum cpu capacity can change between scheduler domain hierarchy setups due to frequency capping. The cpu capacity is changed in update_cpu_capacity() which is called in load balance on the lowest scheduler domain hierarchy level. To be able to know if a change in cpu capacity for a certain cpu also has an effect on the system-wide maximum cpu capacity it is normally necessary to iterate over all cpus. This would be way too costly. That's why this patch follows a different approach. The unsigned long max_cpu_capacity value in struct root_domain is replaced with a struct max_cpu_capacity, containing value (the max_cpu_capacity) and cpu (the cpu index of the cpu providing the maximum cpu_capacity). Changes to the system-wide maximum cpu capacity and the cpu index are made if: 1 System-wide maximum cpu capacity < cpu capacity 2 System-wide maximum cpu capacity > cpu capacity and cpu index == cpu There are no changes to the system-wide maximum cpu capacity in all other cases. Atomic read and write access to the pair (max_cpu_capacity.val, max_cpu_capacity.cpu) is enforced by max_cpu_capacity.lock. The access to max_cpu_capacity.val in task_fits_max() is still performed without taking the max_cpu_capacity.lock. The code to set max cpu capacity in build_sched_domains() has been removed because the whole functionality is now provided by update_cpu_capacity() instead. This approach can introduce errors temporarily, e.g. in case the cpu currently providing the max cpu capacity has its cpu capacity lowered due to frequency capping and calls update_cpu_capacity() before any cpu which might provide the max cpu now. Signed-off-by: Ionela Voinescu * Signed-off-by: Dietmar Eggemann ( Fixed cherry-pick issues ) Signed-off-by: Quentin Perret Change-Id: Idaa7a16723001e222e476de34df332558e48dd13 --- kernel/sched/fair.c | 31 ++++++++++++++++++++++++++++++- kernel/sched/sched.h | 10 +++++++++- kernel/sched/topology.c | 15 +++------------ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2ced8d17212a..30dab33a94fb 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6788,7 +6788,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) return 0; min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu)); - max_cap = cpu_rq(cpu)->rd->max_cpu_capacity; + max_cap = cpu_rq(cpu)->rd->max_cpu_capacity.val; /* Minimum capacity is close to max, no need to abort wake_affine */ if (max_cap - min_cap < max_cap >> 3) @@ -8375,16 +8375,45 @@ static unsigned long scale_rt_capacity(int cpu, unsigned long max) return scale_irq_capacity(free, irq, max); } +void init_max_cpu_capacity(struct max_cpu_capacity *mcc) { + raw_spin_lock_init(&mcc->lock); + mcc->val = 0; + mcc->cpu = -1; +} + static void update_cpu_capacity(struct sched_domain *sd, int cpu) { unsigned long capacity = arch_scale_cpu_capacity(sd, cpu); struct sched_group *sdg = sd->groups; + struct max_cpu_capacity *mcc; + unsigned long max_capacity; + int max_cap_cpu; + unsigned long flags; cpu_rq(cpu)->cpu_capacity_orig = capacity; capacity *= arch_scale_max_freq_capacity(sd, cpu); capacity >>= SCHED_CAPACITY_SHIFT; + mcc = &cpu_rq(cpu)->rd->max_cpu_capacity; + + raw_spin_lock_irqsave(&mcc->lock, flags); + max_capacity = mcc->val; + max_cap_cpu = mcc->cpu; + + if ((max_capacity > capacity && max_cap_cpu == cpu) || + (max_capacity < capacity)) { + mcc->val = capacity; + mcc->cpu = cpu; +#ifdef CONFIG_SCHED_DEBUG + raw_spin_unlock_irqrestore(&mcc->lock, flags); + pr_info("CPU%d: update max cpu_capacity %lu\n", cpu, capacity); + goto skip_unlock; +#endif + } + raw_spin_unlock_irqrestore(&mcc->lock, flags); + +skip_unlock: __attribute__ ((unused)); capacity = scale_rt_capacity(cpu, capacity); if (!capacity) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 69808442598c..61236d0b49bb 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -711,6 +711,12 @@ struct perf_domain { struct rcu_head rcu; }; +struct max_cpu_capacity { + raw_spinlock_t lock; + unsigned long val; + int cpu; +}; + /* Scheduling group status flags */ #define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ #define SG_OVERUTILIZED 0x2 /* One or more CPUs are over-utilized. */ @@ -769,7 +775,8 @@ struct root_domain { cpumask_var_t rto_mask; struct cpupri cpupri; - unsigned long max_cpu_capacity; + /* Maximum cpu capacity in the system. */ + struct max_cpu_capacity max_cpu_capacity; /* * NULL-terminated list of performance domains intersecting with the @@ -782,6 +789,7 @@ extern struct root_domain def_root_domain; extern struct mutex sched_domains_mutex; extern void init_defrootdomain(void); +extern void init_max_cpu_capacity(struct max_cpu_capacity *mcc); extern int sched_init_domains(const struct cpumask *cpu_map); extern void rq_attach_root(struct rq *rq, struct root_domain *rd); extern void sched_get_rd(struct root_domain *rd); diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 8f7baceb150d..7f194a3a3523 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -461,6 +461,9 @@ static int init_rootdomain(struct root_domain *rd) if (cpupri_init(&rd->cpupri) != 0) goto free_cpudl; + + init_max_cpu_capacity(&rd->max_cpu_capacity); + return 0; free_cpudl: @@ -1887,7 +1890,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att enum s_alloc alloc_state; struct sched_domain *sd; struct s_data d; - struct rq *rq = NULL; int i, ret = -ENOMEM; struct sched_domain_topology_level *tl_asym; bool has_asym = false; @@ -1950,13 +1952,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att /* Attach the domains */ rcu_read_lock(); for_each_cpu(i, cpu_map) { - rq = cpu_rq(i); sd = *per_cpu_ptr(d.sd, i); - - /* Use READ_ONCE()/WRITE_ONCE() to avoid load/store tearing: */ - if (rq->cpu_capacity_orig > READ_ONCE(d.rd->max_cpu_capacity)) - WRITE_ONCE(d.rd->max_cpu_capacity, rq->cpu_capacity_orig); - cpu_attach_domain(sd, d.rd, i); } rcu_read_unlock(); @@ -1964,11 +1960,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att if (has_asym) static_branch_enable_cpuslocked(&sched_asym_cpucapacity); - if (rq && sched_debug_enabled) { - pr_info("root domain span: %*pbl (max cpu_capacity = %lu)\n", - cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity); - } - ret = 0; error: __free_domain_allocs(&d, alloc_state, cpu_map); From dd27f0617b56fc8dc8118c48455f42d8d007f244 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 10 Jul 2018 15:44:09 +0100 Subject: [PATCH 0755/1276] ANDROID: cpufreq/schedutil: Select frequency using util_avg for RT Schedutil always requests max frequency whenever a RT task is running. Now that we have a better estimate of the utilization of RT runqueues, it is possible to make a less conservative decision and scale frequency according to the needs of the RT tasks. To do so, protect the RT-go-to-max code with a new sched_feature. The sched_feature is disabled by default, hence favoring energy savings as required in mobile environments. Signed-off-by: Quentin Perret Change-Id: Ic9f01c8703d4f843addaa0d684012a422fe9f3b8 --- kernel/sched/cpufreq_schedutil.c | 3 ++- kernel/sched/features.h | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 645f54a0c2dc..b7bc4fe481fe 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -206,7 +206,8 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, max = arch_scale_cpu_capacity(NULL, cpu); - if (type == FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) + if (sched_feat(SUGOV_RT_MAX_FREQ) && type == FREQUENCY_UTIL && + rt_rq_is_runnable(&rq->rt)) return max; /* diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 2c33917b2067..fb995c5f69ba 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -111,3 +111,8 @@ SCHED_FEAT(FIND_BEST_TARGET, true) * those tasks through the mainline slow path. */ SCHED_FEAT(EAS_PREFER_IDLE, true) + +/* + * Request max frequency from schedutil whenever a RT task is running. + */ +SCHED_FEAT(SUGOV_RT_MAX_FREQ, false) From f945d03671e681b3c2c6a1632cc2b74a6ad57f16 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Thu, 17 Nov 2016 10:48:45 +0530 Subject: [PATCH 0756/1276] ANDROID: cpufreq/schedutil: add up/down frequency transition rate limits The rate-limit tunable in the schedutil governor applies to transitions to both lower and higher frequencies. On several platforms it is not the ideal tunable though, as it is difficult to get best power/performance figures using the same limit in both directions. It is common on mobile platforms with demanding user interfaces to want to increase frequency rapidly for example but decrease slowly. One of the example can be a case where we have short busy periods followed by similar or longer idle periods. If we keep the rate-limit high enough, we will not go to higher frequencies soon enough. On the other hand, if we keep it too low, we will have too many frequency transitions, as we will always reduce the frequency after the busy period. It would be very useful if we can set low rate-limit while increasing the frequency (so that we can respond to the short busy periods quickly) and high rate-limit while decreasing frequency (so that we don't reduce the frequency immediately after the short busy period and that may avoid frequency transitions before the next busy period). Implement separate up/down transition rate limits. Note that the governor avoids frequency recalculations for a period equal to minimum of up and down rate-limit. A global mutex is also defined to protect updates to min_rate_limit_us via two separate sysfs files. Note that this wouldn't change behavior of the schedutil governor for the platforms which wish to keep same values for both up and down rate limits. This is tested with the rt-app [1] on ARM Exynos, dual A15 processor platform. Testcase: Run a SCHED_OTHER thread on CPU0 which will emulate work-load for X ms of busy period out of the total period of Y ms, i.e. Y - X ms of idle period. The values of X/Y taken were: 20/40, 20/50, 20/70, i.e idle periods of 20, 30 and 50 ms respectively. These were tested against values of up/down rate limits as: 10/10 ms and 10/40 ms. For every test we noticed a performance increase of 5-10% with the schedutil governor, which was very much expected. [Viresh]: Simplified user interface and introduced min_rate_limit_us + mutex, rewrote commit log and included test results. [1] https://github.com/scheduler-tools/rt-app/ Signed-off-by: Steve Muckle Signed-off-by: Viresh Kumar (applied from https://marc.info/?l=linux-kernel&m=147936011103832&w=2) [trivial adaptations] Signed-off-by: Juri Lelli [updated rate limiting & fixed conflicts] Signed-off-by: Chris Redpath (cherry picked from commit 50c26fdb74563ec0cb4a83373d42667f4e83a23e) [Trivial cherry-pick conflicts] Signed-off-by: Quentin Perret Change-Id: I18720a83855b196b8e21dcdc8deae79131635b84 --- kernel/sched/cpufreq_schedutil.c | 103 ++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index b7bc4fe481fe..6704b2b3cb41 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -18,7 +18,8 @@ struct sugov_tunables { struct gov_attr_set attr_set; - unsigned int rate_limit_us; + unsigned int up_rate_limit_us; + unsigned int down_rate_limit_us; }; struct sugov_policy { @@ -29,7 +30,9 @@ struct sugov_policy { raw_spinlock_t update_lock; /* For shared policies */ u64 last_freq_update_time; - s64 freq_update_delay_ns; + s64 min_rate_limit_ns; + s64 up_rate_delay_ns; + s64 down_rate_delay_ns; unsigned int next_freq; unsigned int cached_raw_freq; @@ -94,9 +97,32 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) if (unlikely(sg_policy->need_freq_update)) return true; + /* No need to recalculate next freq for min_rate_limit_us + * at least. However we might still decide to further rate + * limit once frequency change direction is decided, according + * to the separate rate limits. + */ + delta_ns = time - sg_policy->last_freq_update_time; + return delta_ns >= sg_policy->min_rate_limit_ns; +} + +static bool sugov_up_down_rate_limit(struct sugov_policy *sg_policy, u64 time, + unsigned int next_freq) +{ + s64 delta_ns; + + delta_ns = time - sg_policy->last_freq_update_time; + + if (next_freq > sg_policy->next_freq && + delta_ns < sg_policy->up_rate_delay_ns) + return true; - return delta_ns >= sg_policy->freq_update_delay_ns; + if (next_freq < sg_policy->next_freq && + delta_ns < sg_policy->down_rate_delay_ns) + return true; + + return false; } static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time, @@ -105,6 +131,9 @@ static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time, if (sg_policy->next_freq == next_freq) return false; + if (sugov_up_down_rate_limit(sg_policy, time, next_freq)) + return false; + sg_policy->next_freq = next_freq; sg_policy->last_freq_update_time = time; @@ -594,15 +623,52 @@ static inline struct sugov_tunables *to_sugov_tunables(struct gov_attr_set *attr return container_of(attr_set, struct sugov_tunables, attr_set); } -static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) +static DEFINE_MUTEX(min_rate_lock); + +static void update_min_rate_limit_ns(struct sugov_policy *sg_policy) +{ + mutex_lock(&min_rate_lock); + sg_policy->min_rate_limit_ns = min(sg_policy->up_rate_delay_ns, + sg_policy->down_rate_delay_ns); + mutex_unlock(&min_rate_lock); +} + +static ssize_t up_rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) +{ + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + + return sprintf(buf, "%u\n", tunables->up_rate_limit_us); +} + +static ssize_t down_rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) { struct sugov_tunables *tunables = to_sugov_tunables(attr_set); - return sprintf(buf, "%u\n", tunables->rate_limit_us); + return sprintf(buf, "%u\n", tunables->down_rate_limit_us); +} + +static ssize_t up_rate_limit_us_store(struct gov_attr_set *attr_set, + const char *buf, size_t count) +{ + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + struct sugov_policy *sg_policy; + unsigned int rate_limit_us; + + if (kstrtouint(buf, 10, &rate_limit_us)) + return -EINVAL; + + tunables->up_rate_limit_us = rate_limit_us; + + list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) { + sg_policy->up_rate_delay_ns = rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); + } + + return count; } -static ssize_t -rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count) +static ssize_t down_rate_limit_us_store(struct gov_attr_set *attr_set, + const char *buf, size_t count) { struct sugov_tunables *tunables = to_sugov_tunables(attr_set); struct sugov_policy *sg_policy; @@ -611,18 +677,22 @@ rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count if (kstrtouint(buf, 10, &rate_limit_us)) return -EINVAL; - tunables->rate_limit_us = rate_limit_us; + tunables->down_rate_limit_us = rate_limit_us; - list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) - sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC; + list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) { + sg_policy->down_rate_delay_ns = rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); + } return count; } -static struct governor_attr rate_limit_us = __ATTR_RW(rate_limit_us); +static struct governor_attr up_rate_limit_us = __ATTR_RW(up_rate_limit_us); +static struct governor_attr down_rate_limit_us = __ATTR_RW(down_rate_limit_us); static struct attribute *sugov_attributes[] = { - &rate_limit_us.attr, + &up_rate_limit_us.attr, + &down_rate_limit_us.attr, NULL }; @@ -778,7 +848,8 @@ static int sugov_init(struct cpufreq_policy *policy) goto stop_kthread; } - tunables->rate_limit_us = cpufreq_policy_transition_delay_us(policy); + tunables->up_rate_limit_us = cpufreq_policy_transition_delay_us(policy); + tunables->down_rate_limit_us = cpufreq_policy_transition_delay_us(policy); policy->governor_data = sg_policy; sg_policy->tunables = tunables; @@ -836,7 +907,11 @@ static int sugov_start(struct cpufreq_policy *policy) struct sugov_policy *sg_policy = policy->governor_data; unsigned int cpu; - sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; + sg_policy->up_rate_delay_ns = + sg_policy->tunables->up_rate_limit_us * NSEC_PER_USEC; + sg_policy->down_rate_delay_ns = + sg_policy->tunables->down_rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); sg_policy->last_freq_update_time = 0; sg_policy->next_freq = 0; sg_policy->work_in_progress = false; From 63d0c99f7bb7230de9b2f784b31a6374a560a631 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 1 Jun 2018 20:34:10 +0100 Subject: [PATCH 0757/1276] ANDROID: sched/fair: Attempt to improve throughput for asym cap systems In some systems the capacity and group weights line up to defeat all the small imbalance correction conditions in fix_small_imbalance, which can cause bad task placement. Add a new condition if the existing code can't see anything to fix: If we have asymmetric capacity, and there are more tasks than CPUs in the busiest group *and* there are less tasks than CPUs in the local group then we try to pull something. There could be transient small tasks which prevent this from working, but on the whole it is beneficial for those systems with inconvenient capacity/cluster size relationships. Signed-off-by: Chris Redpath Change-Id: Icf81cde215c082a61f816534b7990ccb70aee409 --- kernel/sched/fair.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 30dab33a94fb..0555a0738097 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9049,7 +9049,22 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds) capa_move /= SCHED_CAPACITY_SCALE; /* Move if we gain throughput */ - if (capa_move > capa_now) + if (capa_move > capa_now) { + env->imbalance = busiest->load_per_task; + return; + } + + /* We can't see throughput improvement with the load-based + * method, but it is possible depending upon group size and + * capacity range that there might still be an underutilized + * cpu available in an asymmetric capacity system. Do one last + * check just in case. + */ + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + busiest->group_type == group_overloaded && + busiest->sum_nr_running > busiest->group_weight && + local->sum_nr_running < local->group_weight && + local->group_capacity < busiest->group_capacity) env->imbalance = busiest->load_per_task; } From 09deccbce7713dedb55899f470d2050e7d25bae0 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 5 Jun 2018 11:47:57 +0100 Subject: [PATCH 0758/1276] ANDROID: sched/fair: Don't balance misfits if it would overload local group When load balancing in a system with misfit tasks present, if we always pull a misfit task to the local group this can lead to pulling a running task from a smaller capacity CPUs to a bigger CPU which is busy. In this situation, the pulled task is likely not to get a chance to run before an idle balance on another small CPU pulls it back. This penalises the pulled task as it is stopped for a short amount of time and then likely relocated to a different CPU (since the original CPU just did a NEWLY_IDLE balance and reset the periodic interval). If we only do this unconditionally for NEWLY_IDLE balance, we can be sure that any tasks and load which are present on the local group are related to short-running tasks which we are happy to displace for a longer running task in a system with misfit tasks present. However, other balance types should only pull a task if we think that the local group is underutilized - checking the number of tasks gives us a conservative estimate here since if they were short tasks we would have been doing NEWLY_IDLE balances instead. Signed-off-by: Chris Redpath Change-Id: I710add1ab1139482620b6addc8370ad194791beb --- kernel/sched/fair.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0555a0738097..abf02fbb4560 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9133,8 +9133,18 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s (sds->avg_load - local->avg_load) * local->group_capacity ) / SCHED_CAPACITY_SCALE; - /* Boost imbalance to allow misfit task to be balanced. */ - if (busiest->group_type == group_misfit_task) { + /* Boost imbalance to allow misfit task to be balanced. + * Always do this if we are doing a NEWLY_IDLE balance + * on the assumption that any tasks we have must not be + * long-running (and hence we cannot rely upon load). + * However if we are not idle, we should assume the tasks + * we have are longer running and not override load-based + * calculations above unless we are sure that the local + * group is underutilized. + */ + if (busiest->group_type == group_misfit_task && + (env->idle == CPU_NEWLY_IDLE || + local->sum_nr_running < local->group_weight)) { env->imbalance = max_t(long, env->imbalance, busiest->group_misfit_task_load); } From 19817a38dd6dd4ca654da10707a2811952eca4ca Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 5 Jun 2018 12:21:33 +0100 Subject: [PATCH 0759/1276] ANDROID: sched/fair: Also do misfit in overloaded groups If we can classify the group as overloaded, that overrides any classification as misfit but we may still have misfit tasks present. Check the rq we're looking at to see if this is the case. Signed-off-by: Chris Redpath [Removed stray reference to rq_has_misfit] Signed-off-by: Valentin Schneider Change-Id: Ida8eb66aa625e34de3fe2ee1b0dd8a78926273d8 --- kernel/sched/fair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index abf02fbb4560..e63f3deaa6e1 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9415,6 +9415,10 @@ static int need_active_balance(struct lb_env *env) return 1; } + if (env->src_grp_type == group_overloaded && env->src_rq->misfit_task_load) + return 1; + + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From ca0a359e6ece8d93e7c14d5ba36a6dd7ac2ad18e Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 19:09:03 +0000 Subject: [PATCH 0760/1276] ANDROID: sched/autogroup: Define autogroup_path() for !CONFIG_SCHED_DEBUG Define autogroup_path() even in the !CONFIG_SCHED_DEBUG case. If CONFIG_SCHED_AUTOGROUP is enabled the path of an autogroup has to be available to be printed in the load tracking trace events provided by this patch-stack regardless whether CONFIG_SCHED_DEBUG is set or not. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Change-Id: Iecf5466fc837f428ee545ddabe70024c152ff38d --- kernel/sched/autogroup.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/sched/autogroup.c b/kernel/sched/autogroup.c index 2d4ff5353ded..2067080bb235 100644 --- a/kernel/sched/autogroup.c +++ b/kernel/sched/autogroup.c @@ -259,7 +259,6 @@ void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) } #endif /* CONFIG_PROC_FS */ -#ifdef CONFIG_SCHED_DEBUG int autogroup_path(struct task_group *tg, char *buf, int buflen) { if (!task_group_is_autogroup(tg)) @@ -267,4 +266,3 @@ int autogroup_path(struct task_group *tg, char *buf, int buflen) return snprintf(buf, buflen, "%s-%ld", "/autogroup", tg->autogroup->id); } -#endif From f0e23572bbd44fbe3cab2a49223e29f3861e68fc Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 20:27:06 +0000 Subject: [PATCH 0761/1276] ANDROID: sched/events: Introduce cfs_rq load tracking trace event The following trace event keys are mapped to: (1) load : cfs_rq->avg.load_avg (2) rbl_load : cfs_rq->avg.runnable_load_avg (2) util : cfs_rq->avg.util_avg To let this trace event work for configurations w/ and w/o group scheduling support for cfs (CONFIG_FAIR_GROUP_SCHED) the following special handling is necessary for a non-existent key=value pair: path = "(null)" : In case of !CONFIG_FAIR_GROUP_SCHED. The following list shows examples of the key=value pairs in different configurations for: (1) a root task_group: cpu=4 path=/ load=6 rbl_load=6 util=331 (2) a task_group: cpu=1 path=/tg1/tg11/tg111 load=538 rbl_load=538 util=522 (3) an autogroup: cpu=3 path=/autogroup-18 load=997 rbl_load=997 util=517 (4) w/o CONFIG_FAIR_GROUP_SCHED: cpu=0 path=(null) load=314 rbl_load=314 util=289 The trace event is only defined for CONFIG_SMP. The helper function __trace_sched_path() can be used to get the length parameter of the dynamic array (path == NULL) and to copy the path into it (path != NULL). Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt [ Fixed issues related to the new pelt.c file ] Signed-off-by: Quentin Perret Change-Id: I1107044c52b74ecb3df69f3a45c1e530f0e59b1b --- include/trace/events/sched.h | 66 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 6 ++++ kernel/sched/pelt.c | 5 +++ 3 files changed, 77 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 4a68273b29d5..6bcb1853f889 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -596,6 +596,72 @@ TRACE_EVENT(sched_wake_idle_without_ipi, TP_printk("cpu=%d", __entry->cpu) ); + +#ifdef CONFIG_SMP +#ifdef CREATE_TRACE_POINTS +static inline +int __trace_sched_cpu(struct cfs_rq *cfs_rq) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + struct rq *rq = cfs_rq->rq; +#else + struct rq *rq = container_of(cfs_rq, struct rq, cfs); +#endif + return cpu_of(rq); +} + +static inline +int __trace_sched_path(struct cfs_rq *cfs_rq, char *path, int len) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + int l = path ? len : 0; + + if (task_group_is_autogroup(cfs_rq->tg)) + return autogroup_path(cfs_rq->tg, path, l) + 1; + else + return cgroup_path(cfs_rq->tg->css.cgroup, path, l) + 1; +#else + if (path) + strcpy(path, "(null)"); + + return strlen("(null)"); +#endif +} + +#endif /* CREATE_TRACE_POINTS */ + +/* + * Tracepoint for cfs_rq load tracking: + */ +TRACE_EVENT(sched_load_cfs_rq, + + TP_PROTO(struct cfs_rq *cfs_rq), + + TP_ARGS(cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(cfs_rq, NULL, 0) ) + __field( unsigned long, load ) + __field( unsigned long, rbl_load ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + __entry->cpu = __trace_sched_cpu(cfs_rq); + __trace_sched_path(cfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + __entry->load = cfs_rq->avg.load_avg; + __entry->rbl_load = cfs_rq->avg.runnable_load_avg; + __entry->util = cfs_rq->avg.util_avg; + ), + + TP_printk("cpu=%d path=%s load=%lu rbl_load=%lu util=%lu", + __entry->cpu, __get_str(path), __entry->load, + __entry->rbl_load,__entry->util) +); +#endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ /* This part must be outside protection */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e63f3deaa6e1..ebd6d0a93185 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3339,6 +3339,8 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) update_tg_cfs_util(cfs_rq, se, gcfs_rq); update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); + trace_sched_load_cfs_rq(cfs_rq); + return 1; } @@ -3491,6 +3493,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s add_tg_cfs_propagate(cfs_rq, se->avg.load_sum); cfs_rq_util_change(cfs_rq, flags); + + trace_sched_load_cfs_rq(cfs_rq); } /** @@ -3510,6 +3514,8 @@ static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s add_tg_cfs_propagate(cfs_rq, -se->avg.load_sum); cfs_rq_util_change(cfs_rq, 0); + + trace_sched_load_cfs_rq(cfs_rq); } /* diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 35475c0c5419..f042ee0f7b08 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -29,6 +29,8 @@ #include "sched-pelt.h" #include "pelt.h" +#include + /* * Approximate: * val * y^n, where y^32 ~= 0.5 (~1 scheduling period) @@ -304,6 +306,9 @@ int __update_load_avg_cfs_rq(u64 now, int cpu, struct cfs_rq *cfs_rq) cfs_rq->curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1, 1); + + trace_sched_load_cfs_rq(cfs_rq); + return 1; } From 93f0064cd7e14a7c555d8a877c07f6cc35eea67a Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Mon, 20 Mar 2017 17:26:47 +0000 Subject: [PATCH 0762/1276] ANDROID: sched/events: Introduce sched_entity load tracking trace event The following trace event keys are mapped to: (1) load : se->avg.load_avg (2) rbl_load : se->avg.runnable_load_avg (3) util : se->avg.util_avg To let this trace event work for configurations w/ and w/o group scheduling support for cfs (CONFIG_FAIR_GROUP_SCHED) the following special handling is necessary for non-existent key=value pairs: path = "(null)" : In case of !CONFIG_FAIR_GROUP_SCHED or the sched_entity represents a task. comm = "(null)" : In case sched_entity represents a task_group. pid = -1 : In case sched_entity represents a task_group. The following list shows examples of the key=value pairs in different configurations for: (1) a task: cpu=0 path=(null) comm=sshd pid=2206 load=102 rbl_load=102 util=102 (2) a taskgroup: cpu=1 path=/tg1/tg11/tg111 comm=(null) pid=-1 load=882 rbl_load=882 util=510 (3) an autogroup: cpu=0 path=/autogroup-13 comm=(null) pid=-1 load=49 rbl_load=49 util=48 (4) w/o CONFIG_FAIR_GROUP_SCHED: cpu=0 path=(null) comm=sshd pid=2211 load=301 rbl_load=301 util=265 The trace event is only defined for CONFIG_SMP. The helper functions __trace_sched_cpu(), __trace_sched_path() and __trace_sched_id() are extended to deal with sched_entities as well. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt [ Fixed issues related to the new pelt.c file ] Signed-off-by: Quentin Perret Change-Id: Id2e4d1ddb79c13412c80e4fa4147b9df3b1e212a --- include/trace/events/sched.h | 67 +++++++++++++++++++++++++++++++----- kernel/sched/fair.c | 1 + kernel/sched/pelt.c | 6 ++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 6bcb1853f889..96092f803c34 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -600,14 +600,15 @@ TRACE_EVENT(sched_wake_idle_without_ipi, #ifdef CONFIG_SMP #ifdef CREATE_TRACE_POINTS static inline -int __trace_sched_cpu(struct cfs_rq *cfs_rq) +int __trace_sched_cpu(struct cfs_rq *cfs_rq, struct sched_entity *se) { #ifdef CONFIG_FAIR_GROUP_SCHED - struct rq *rq = cfs_rq->rq; + struct rq *rq = cfs_rq ? cfs_rq->rq : NULL; #else - struct rq *rq = container_of(cfs_rq, struct rq, cfs); + struct rq *rq = cfs_rq ? container_of(cfs_rq, struct rq, cfs) : NULL; #endif - return cpu_of(rq); + return rq ? cpu_of(rq) + : task_cpu((container_of(se, struct task_struct, se))); } static inline @@ -616,18 +617,26 @@ int __trace_sched_path(struct cfs_rq *cfs_rq, char *path, int len) #ifdef CONFIG_FAIR_GROUP_SCHED int l = path ? len : 0; - if (task_group_is_autogroup(cfs_rq->tg)) + if (cfs_rq && task_group_is_autogroup(cfs_rq->tg)) return autogroup_path(cfs_rq->tg, path, l) + 1; - else + else if (cfs_rq && cfs_rq->tg->css.cgroup) return cgroup_path(cfs_rq->tg->css.cgroup, path, l) + 1; -#else +#endif if (path) strcpy(path, "(null)"); return strlen("(null)"); -#endif } +static inline +struct cfs_rq *__trace_sched_group_cfs_rq(struct sched_entity *se) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + return se->my_q; +#else + return NULL; +#endif +} #endif /* CREATE_TRACE_POINTS */ /* @@ -649,7 +658,7 @@ TRACE_EVENT(sched_load_cfs_rq, ), TP_fast_assign( - __entry->cpu = __trace_sched_cpu(cfs_rq); + __entry->cpu = __trace_sched_cpu(cfs_rq, NULL); __trace_sched_path(cfs_rq, __get_dynamic_array(path), __get_dynamic_array_len(path)); __entry->load = cfs_rq->avg.load_avg; @@ -661,6 +670,46 @@ TRACE_EVENT(sched_load_cfs_rq, __entry->cpu, __get_str(path), __entry->load, __entry->rbl_load,__entry->util) ); + +/* + * Tracepoint for sched_entity load tracking: + */ +TRACE_EVENT(sched_load_se, + + TP_PROTO(struct sched_entity *se), + + TP_ARGS(se), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(__trace_sched_group_cfs_rq(se), NULL, 0) ) + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, load ) + __field( unsigned long, rbl_load ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + struct cfs_rq *gcfs_rq = __trace_sched_group_cfs_rq(se); + struct task_struct *p = gcfs_rq ? NULL + : container_of(se, struct task_struct, se); + + __entry->cpu = __trace_sched_cpu(gcfs_rq, se); + __trace_sched_path(gcfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + memcpy(__entry->comm, p ? p->comm : "(null)", TASK_COMM_LEN); + __entry->pid = p ? p->pid : -1; + __entry->load = se->avg.load_avg; + __entry->rbl_load = se->avg.runnable_load_avg; + __entry->util = se->avg.util_avg; + ), + + TP_printk("cpu=%d path=%s comm=%s pid=%d load=%lu rbl_load=%lu util=%lu", + __entry->cpu, __get_str(path), __entry->comm, __entry->pid, + __entry->load, __entry->rbl_load, __entry->util) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ebd6d0a93185..7c8300fd6869 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3340,6 +3340,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); trace_sched_load_cfs_rq(cfs_rq); + trace_sched_load_se(se); return 1; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index f042ee0f7b08..95923939c027 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -276,6 +276,9 @@ int __update_load_avg_blocked_se(u64 now, int cpu, struct sched_entity *se) if (___update_load_sum(now, cpu, &se->avg, 0, 0, 0)) { ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + + trace_sched_load_se(se); + return 1; } @@ -292,6 +295,9 @@ int __update_load_avg_se(u64 now, int cpu, struct cfs_rq *cfs_rq, struct sched_e ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); cfs_se_util_change(&se->avg); + + trace_sched_load_se(se); + return 1; } From 14e2ba586e495790ef1deb421204764e90a3e9b5 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 21:23:35 +0000 Subject: [PATCH 0763/1276] ANDROID: sched/events: Introduce task_group load tracking trace event The trace event key load is mapped to: (1) load : cfs_rq->tg->load_avg The cfs_rq owned by the task_group is used as the only parameter for the trace event because it has a reference to the taskgroup and the cpu. Using the taskgroup as a parameter instead would require the cpu as a second parameter. A task_group is global and not per-cpu data. The cpu key only tells on which cpu the value was gathered. The following list shows examples of the key=value pairs for: (1) a task group: cpu=1 path=/tg1/tg11/tg111 load=517 (2) an autogroup: cpu=1 path=/autogroup-10 load=1050 We don't maintain a load signal for a root task group. The trace event is only defined if cfs group scheduling support (CONFIG_FAIR_GROUP_SCHED) is enabled. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Signed-off-by: Dietmar Eggemann Change-Id: I7de38e6b30a99d7c9887c94c707ded26b383b5f8 --- include/trace/events/sched.h | 29 +++++++++++++++++++++++++++++ kernel/sched/fair.c | 2 ++ 2 files changed, 31 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 96092f803c34..1bffb9a52655 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -710,6 +710,35 @@ TRACE_EVENT(sched_load_se, __entry->cpu, __get_str(path), __entry->comm, __entry->pid, __entry->load, __entry->rbl_load, __entry->util) ); + +/* + * Tracepoint for task_group load tracking: + */ +#ifdef CONFIG_FAIR_GROUP_SCHED +TRACE_EVENT(sched_load_tg, + + TP_PROTO(struct cfs_rq *cfs_rq), + + TP_ARGS(cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(cfs_rq, NULL, 0) ) + __field( long, load ) + ), + + TP_fast_assign( + __entry->cpu = cfs_rq->rq->cpu; + __trace_sched_path(cfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + __entry->load = atomic_long_read(&cfs_rq->tg->load_avg); + ), + + TP_printk("cpu=%d path=%s load=%ld", __entry->cpu, __get_str(path), + __entry->load) +); +#endif /* CONFIG_FAIR_GROUP_SCHED */ #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7c8300fd6869..11d2e11b9870 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3097,6 +3097,8 @@ static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) if (force || abs(delta) > cfs_rq->tg_load_avg_contrib / 64) { atomic_long_add(delta, &cfs_rq->tg->load_avg); cfs_rq->tg_load_avg_contrib = cfs_rq->avg.load_avg; + + trace_sched_load_tg(cfs_rq); } } From 5ac8e867957ac79b12b9250d6ed24a0516dbaaea Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Fri, 27 Oct 2017 16:12:51 +0100 Subject: [PATCH 0764/1276] ANDROID: sched/events: Introduce util_est trace events Signed-off-by: Patrick Bellasi Change-Id: I65e294c454369cbc15a29370d8a13ce358a95c39 --- include/trace/events/sched.h | 64 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 10 ++++++ 2 files changed, 74 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 1bffb9a52655..fc9aaeb55def 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -739,6 +739,70 @@ TRACE_EVENT(sched_load_tg, __entry->load) ); #endif /* CONFIG_FAIR_GROUP_SCHED */ + +/* + * Tracepoint for tasks' estimated utilization. + */ +TRACE_EVENT(sched_util_est_task, + + TP_PROTO(struct task_struct *tsk, struct sched_avg *avg), + + TP_ARGS(tsk, avg), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( int, cpu ) + __field( unsigned int, util_avg ) + __field( unsigned int, est_enqueued ) + __field( unsigned int, est_ewma ) + + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->cpu = task_cpu(tsk); + __entry->util_avg = avg->util_avg; + __entry->est_enqueued = avg->util_est.enqueued; + __entry->est_ewma = avg->util_est.ewma; + ), + + TP_printk("comm=%s pid=%d cpu=%d util_avg=%u util_est_ewma=%u util_est_enqueued=%u", + __entry->comm, + __entry->pid, + __entry->cpu, + __entry->util_avg, + __entry->est_ewma, + __entry->est_enqueued) +); + +/* + * Tracepoint for root cfs_rq's estimated utilization. + */ +TRACE_EVENT(sched_util_est_cpu, + + TP_PROTO(int cpu, struct cfs_rq *cfs_rq), + + TP_ARGS(cpu, cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned int, util_avg ) + __field( unsigned int, util_est_enqueued ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->util_avg = cfs_rq->avg.util_avg; + __entry->util_est_enqueued = cfs_rq->avg.util_est.enqueued; + ), + + TP_printk("cpu=%d util_avg=%u util_est_enqueued=%u", + __entry->cpu, + __entry->util_avg, + __entry->util_est_enqueued) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 11d2e11b9870..a77c171f055a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3666,6 +3666,10 @@ static inline void util_est_enqueue(struct cfs_rq *cfs_rq, enqueued = cfs_rq->avg.util_est.enqueued; enqueued += (_task_util_est(p) | UTIL_AVG_UNCHANGED); WRITE_ONCE(cfs_rq->avg.util_est.enqueued, enqueued); + + /* Update plots for Task and CPU estimated utilization */ + trace_sched_util_est_task(p, &p->se.avg); + trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); } /* @@ -3696,6 +3700,9 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) (_task_util_est(p) | UTIL_AVG_UNCHANGED)); WRITE_ONCE(cfs_rq->avg.util_est.enqueued, ue.enqueued); + /* Update plots for CPU's estimated utilization */ + trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); + /* * Skip update of task's estimated utilization when the task has not * yet completed an activation, e.g. being migrated. @@ -3741,6 +3748,9 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) ue.ewma += last_ewma_diff; ue.ewma >>= UTIL_EST_WEIGHT_SHIFT; WRITE_ONCE(p->se.avg.util_est, ue); + + /* Update plots for Task's estimated utilization */ + trace_sched_util_est_task(p, &p->se.avg); } static inline int task_fits_capacity(struct task_struct *p, long capacity) From e22a29f26743d06aac247bd7d6cda7804b370c44 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 31 May 2018 11:15:26 +0100 Subject: [PATCH 0765/1276] ANDROID: sched/events: Introduce find_best_target trace event Adapated from the existing trace event from android-4.14. Change-Id: I9785e692fb0af087c236906d7f47fed1b20690f5 Signed-off-by: Quentin Perret --- include/trace/events/sched.h | 41 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 3 +++ 2 files changed, 44 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index fc9aaeb55def..f5662789777d 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -803,6 +803,47 @@ TRACE_EVENT(sched_util_est_cpu, __entry->util_avg, __entry->util_est_enqueued) ); + +/* + * Tracepoint for find_best_target + */ +TRACE_EVENT(sched_find_best_target, + + TP_PROTO(struct task_struct *tsk, bool prefer_idle, + unsigned long min_util, int best_idle, int best_active, + int target, int backup), + + TP_ARGS(tsk, prefer_idle, min_util, best_idle, + best_active, target, backup), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, min_util ) + __field( bool, prefer_idle ) + __field( int, best_idle ) + __field( int, best_active ) + __field( int, target ) + __field( int, backup ) + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->min_util = min_util; + __entry->prefer_idle = prefer_idle; + __entry->best_idle = best_idle; + __entry->best_active = best_active; + __entry->target = target; + __entry->backup = backup; + ), + + TP_printk("pid=%d comm=%s prefer_idle=%d " + "best_idle=%d best_active=%d target=%d backup=%d", + __entry->pid, __entry->comm, __entry->prefer_idle, + __entry->best_idle, __entry->best_active, + __entry->target, __entry->backup) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index a77c171f055a..c7507760092e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6790,6 +6790,9 @@ static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, target: cpumask_set_cpu(target_cpu, cpus); } + + trace_sched_find_best_target(p, prefer_idle, min_util, best_idle_cpu, + best_active_cpu, target_cpu, backup_cpu); } /* From ebc3220fd86489b481d6b30e378c57d3c44e8f2a Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 31 May 2018 11:26:52 +0100 Subject: [PATCH 0766/1276] ANDROID: sched/events: Introduce schedtune trace events Suggested-by: Patrick Bellasi Signed-off-by: Quentin Perret Change-Id: I2c43bcb37f57844a07aa36e339da00180e65b6c2 --- include/trace/events/sched.h | 120 +++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 4 ++ kernel/sched/tune.c | 7 ++ 3 files changed, 131 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index f5662789777d..278d00dcf363 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -844,6 +844,126 @@ TRACE_EVENT(sched_find_best_target, __entry->best_idle, __entry->best_active, __entry->target, __entry->backup) ); + +/* + * Tracepoint for accounting CPU boosted utilization + */ +TRACE_EVENT(sched_boost_cpu, + + TP_PROTO(int cpu, unsigned long util, long margin), + + TP_ARGS(cpu, util, margin), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned long, util ) + __field(long, margin ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->util = util; + __entry->margin = margin; + ), + + TP_printk("cpu=%d util=%lu margin=%ld", + __entry->cpu, + __entry->util, + __entry->margin) +); + +/* + * Tracepoint for schedtune_tasks_update + */ +TRACE_EVENT(sched_tune_tasks_update, + + TP_PROTO(struct task_struct *tsk, int cpu, int tasks, int idx, + int boost, int max_boost), + + TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( int, cpu ) + __field( int, tasks ) + __field( int, idx ) + __field( int, boost ) + __field( int, max_boost ) + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->cpu = cpu; + __entry->tasks = tasks; + __entry->idx = idx; + __entry->boost = boost; + __entry->max_boost = max_boost; + ), + + TP_printk("pid=%d comm=%s " + "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d", + __entry->pid, __entry->comm, + __entry->cpu, __entry->tasks, __entry->idx, + __entry->boost, __entry->max_boost) +); + +/* + * Tracepoint for schedtune_boostgroup_update + */ +TRACE_EVENT(sched_tune_boostgroup_update, + + TP_PROTO(int cpu, int variation, int max_boost), + + TP_ARGS(cpu, variation, max_boost), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( int, variation ) + __field( int, max_boost ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->variation = variation; + __entry->max_boost = max_boost; + ), + + TP_printk("cpu=%d variation=%d max_boost=%d", + __entry->cpu, __entry->variation, __entry->max_boost) +); + +/* + * Tracepoint for accounting task boosted utilization + */ +TRACE_EVENT(sched_boost_task, + + TP_PROTO(struct task_struct *tsk, unsigned long util, long margin), + + TP_ARGS(tsk, util, margin), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, util ) + __field( long, margin ) + + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->util = util; + __entry->margin = margin; + ), + + TP_printk("comm=%s pid=%d util=%lu margin=%ld", + __entry->comm, __entry->pid, + __entry->util, + __entry->margin) +); + #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c7507760092e..f675d60f8134 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5853,6 +5853,8 @@ boosted_cpu_util(int cpu) unsigned long util = cpu_util_cfs(cpu_rq(cpu)); long margin = schedtune_cpu_margin(util, cpu); + trace_sched_boost_cpu(cpu, util, margin); + return util + margin; } @@ -5880,6 +5882,8 @@ boosted_task_util(struct task_struct *task) unsigned long util = task_util_est(task); long margin = schedtune_task_margin(task); + trace_sched_boost_task(task, util, margin); + return util + margin; } diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c index af6de6d3df6d..7e0630c52b81 100644 --- a/kernel/sched/tune.c +++ b/kernel/sched/tune.c @@ -227,14 +227,18 @@ schedtune_boostgroup_update(int idx, int boost) /* Check if this update increase current max */ if (boost > cur_boost_max && bg->group[idx].tasks) { bg->boost_max = boost; + trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max); continue; } /* Check if this update has decreased current max */ if (cur_boost_max == old_boost && old_boost > boost) { schedtune_cpu_update(cpu); + trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max); continue; } + + trace_sched_tune_boostgroup_update(cpu, 0, bg->boost_max); } return 0; @@ -252,6 +256,9 @@ schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) /* Update boosted tasks count while avoiding to make it negative */ bg->group[idx].tasks = max(0, tasks); + trace_sched_tune_tasks_update(p, cpu, tasks, idx, + bg->group[idx].boost, bg->boost_max); + /* Boost group activation or deactivation on that RQ */ if (tasks == 1 || tasks == 0) schedtune_cpu_update(cpu); From 3ead56f2f734778df223ed52c86db9ae7e71d727 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 27 Oct 2017 16:52:17 +0100 Subject: [PATCH 0767/1276] ANDROID: sched/events: Introduce rt_rq load tracking trace event We want to be able to track rt_rq signals same as we do for other RQs. Signed-off-by: Chris Redpath (cherry-picked from commit 574a2d189695c334ae290f522b098f05398a3765) [ - Fixed conflicts with the refactored RT util_avg tracking - Changed commit title for consistency with other tracepoint patches ] Signed-off-by: Quentin Perret Change-Id: I38b64baa50e1ff86019ca4b8b0a04af994880b35 --- include/trace/events/sched.h | 24 ++++++++++++++++++++++++ kernel/sched/pelt.c | 3 +++ 2 files changed, 27 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 278d00dcf363..97c3a46105ca 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -671,6 +671,30 @@ TRACE_EVENT(sched_load_cfs_rq, __entry->rbl_load,__entry->util) ); +/* + * Tracepoint for rt_rq load tracking: + */ +struct rq; +TRACE_EVENT(sched_load_rt_rq, + + TP_PROTO(struct rq *rq), + + TP_ARGS(rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + __entry->cpu = rq->cpu; + __entry->util = rq->avg_rt.util_avg; + ), + + TP_printk("cpu=%d util=%lu", __entry->cpu, + __entry->util) +); + /* * Tracepoint for sched_entity load tracking: */ diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 95923939c027..c116744ec44b 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -340,6 +340,9 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) running)) { ___update_load_avg(&rq->avg_rt, 1, 1); + + trace_sched_load_rt_rq(rq); + return 1; } From 6dc69863580fc6d7f66200909bf93fd815c4f200 Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Wed, 10 Feb 2016 09:24:36 +0000 Subject: [PATCH 0768/1276] ANDROID: sched/events: Introduce overutilized trace event Signed-off-by: Patrick Bellasi Signed-off-by: Andres Oportus (cherry picked from commit 8e45d941282039d5379f4e286e5bd0a2044e105c) [ - Trivial cherry pick issues - Changed commit title for consistency ] Signed-off-by: Quentin Perret Change-Id: I78fb5e82223558def0cf16105c233591cda81d5c --- include/trace/events/sched.h | 21 +++++++++++++++++++++ kernel/sched/fair.c | 7 ++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 97c3a46105ca..e3928f7286d6 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -988,6 +988,27 @@ TRACE_EVENT(sched_boost_task, __entry->margin) ); +/* + * Tracepoint for system overutilized flag +*/ +TRACE_EVENT(sched_overutilized, + + TP_PROTO(int overutilized), + + TP_ARGS(overutilized), + + TP_STRUCT__entry( + __field( int, overutilized ) + ), + + TP_fast_assign( + __entry->overutilized = overutilized; + ), + + TP_printk("overutilized=%d", + __entry->overutilized) +); + #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f675d60f8134..ded2a685ba25 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5135,8 +5135,10 @@ static inline bool cpu_overutilized(int cpu) static inline void update_overutilized_status(struct rq *rq) { - if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) + if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) { WRITE_ONCE(rq->rd->overutilized, SG_OVERUTILIZED); + trace_sched_overutilized(1); + } } #else static inline void update_overutilized_status(struct rq *rq) { } @@ -8957,9 +8959,12 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd /* Update over-utilization (tipping point, U >= 0) indicator */ WRITE_ONCE(rd->overutilized, sg_status & SG_OVERUTILIZED); + trace_sched_overutilized(!!(sg_status & SG_OVERUTILIZED)); } else if (sg_status & SG_OVERUTILIZED) { WRITE_ONCE(env->dst_rq->rd->overutilized, SG_OVERUTILIZED); + trace_sched_overutilized(1); } + } /** From 51473594072d920ea28726ef9d28abdd01c71ddd Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 10 Jul 2018 16:57:36 +0100 Subject: [PATCH 0769/1276] ANDROID: arm64: defconfig: Enable CONFIG_SCHED_TUNE Signed-off-by: Quentin Perret Change-Id: Id31c1a0811a1ee45e5533d948dc2faef50624a5a --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 31fd24bff688..2fc45d5505a4 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -106,6 +106,7 @@ CONFIG_COMPAT=y CONFIG_HIBERNATION=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y CONFIG_ENERGY_MODEL=y +CONFIG_SCHED_TUNE=y CONFIG_ARM_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y From 94639c14f91906528194a6d2a23d9407a906c745 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 15:34:46 +0100 Subject: [PATCH 0770/1276] ANDROID: sched: Make the cpu_util* accessors available without sugov In preparation for the introduction of the boost hold feature in SchedTune, make the cpu_util_* signals available even without sugov, hence making the stune integration a lot easier without breaking other platforms Change-Id: I77c14b6fd470a41ffb3ac510b76395ebc116ddfd Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 61236d0b49bb..9ca0289f7280 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2245,7 +2245,15 @@ enum schedutil_type { #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, enum schedutil_type type); +#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ +static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, + enum schedutil_type type) +{ + return util; +} +#endif +#ifdef CONFIG_SMP static inline unsigned long cpu_bw_dl(struct rq *rq) { return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT; @@ -2272,12 +2280,6 @@ static inline unsigned long cpu_util_rt(struct rq *rq) { return READ_ONCE(rq->avg_rt.util_avg); } -#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ -static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, - enum schedutil_type type) -{ - return util; -} #endif #ifdef HAVE_SCHED_AVG_IRQ From e97f6da469f0983e0cc1a812c5a59abf0ecea200 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Mon, 9 Jul 2018 16:54:02 +0100 Subject: [PATCH 0771/1276] ANDROID: sched/rt: Add schedtune accounting to rt task enqueue/dequeue rt tasks are currently not eligible for schedtune boosting. Make it so by adding enqueue/dequeue hooks. For rt tasks, schedtune only acts as a frequency boosting framework, it has no impact on placement decisions and the prefer_idle attribute is not used. Also prepare schedutil use of boosted util for rt task boosting With this change, schedtune accounting will include rt class tasks, however boosting currently only applies to the utilization provided by fair class tasks. Sum up the tracked CPU utilization applying boost to the aggregate util instead - this includes RT task util in the boosting if any tasks are runnable. Scenario 1, considering one CPU: 1x rt task running, util 250, boost 0 1x cfs task runnable, util 250, boost 50 previous util=250+(50pct_boosted_250) = 887 new util=50_pct_boosted_500 = 762 Scenario 2, considering one CPU: 1x rt task running, util 250, boost 50 1x cfs task runnable, util 250, boost 0 previous util=250+250 = 500 new util=50_pct_boosted_500 = 762 Scenario 3, considering one CPU: 1x rt task running, util 250, boost 50 1x cfs task runnable, util 250, boost 50 previous util=250+(50pct_boosted_250) = 887 new util=50_pct_boosted_500 = 762 Scenario 4: 1x rt task running, util 250, boost 50 previous util=250 = 250 new util=50_pct_boosted_250 = 637 Change-Id: Ie287cbd0692468525095b5024db9faac8b2f4878 Signed-off-by: Chris Redpath (cherry picked from commit 8e266aebf737262aeca9662254a3e61ccc7f8dec) [ - Fixed conflicts in sugov related to PELT signals of all classes - Fixed conflicts related to boosted_cpu_util being in a different location - Moved the CFS/RT util aggregation out of schedutil_freq_util to ease the integration with SchedTune ] Signed-off-by: Quentin Perret --- kernel/sched/cpufreq_schedutil.c | 21 ++++++++++----------- kernel/sched/fair.c | 7 +++---- kernel/sched/rt.c | 4 ++++ kernel/sched/sched.h | 2 +- kernel/sched/tune.h | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 6704b2b3cb41..e56afcf60d19 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -219,6 +219,9 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * Where the cfs,rt and dl util numbers are tracked with the same metric and * synchronized windows and are thus directly comparable. * + * The @util parameter passed to this function is assumed to be the aggregation + * of RT and CFS util numbers. The cases of DL and IRQ are managed here. + * * The cfs,rt,dl utilization are the running times measured with rq->clock_task * which excludes things like IRQ and steal-time. These latter are then accrued * in the irq utilization. @@ -227,11 +230,11 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * based on the task model parameters and gives the minimal utilization * required to meet deadlines. */ -unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, +unsigned long schedutil_freq_util(int cpu, unsigned long util, enum schedutil_type type) { struct rq *rq = cpu_rq(cpu); - unsigned long util, irq, max; + unsigned long irq, max; max = arch_scale_cpu_capacity(NULL, cpu); @@ -249,14 +252,11 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, return max; /* - * Because the time spend on RT/DL tasks is visible as 'lost' time to - * CFS tasks and we use the same metric to track the effective - * utilization (PELT windows are synchronized) we can directly add them - * to obtain the CPU's actual utilization. + * The function is called with @util defined as the aggregation (the + * sum) of RT and CFS signals, hence leaving the special case of DL + * to be delt with. The exact way of doing things depend on the calling + * context. */ - util = util_cfs; - util += cpu_util_rt(rq); - if (type == FREQUENCY_UTIL) { /* * For frequency selection we do not make cpu_util_dl() a @@ -269,7 +269,6 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, * to not quite hit saturation when we should -- * something for later. */ - if ((util + cpu_util_dl(rq)) >= max) return max; } else { @@ -316,7 +315,7 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq = cpu_rq(sg_cpu->cpu); - unsigned long util = boosted_cpu_util(sg_cpu->cpu); + unsigned long util = boosted_cpu_util(sg_cpu->cpu, cpu_util_rt(rq)); sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); sg_cpu->bw_dl = cpu_bw_dl(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ded2a685ba25..7c73c9e73767 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5850,9 +5850,9 @@ schedtune_task_margin(struct task_struct *task) } unsigned long -boosted_cpu_util(int cpu) +boosted_cpu_util(int cpu, unsigned long other_util) { - unsigned long util = cpu_util_cfs(cpu_rq(cpu)); + unsigned long util = cpu_util_cfs(cpu_rq(cpu)) + other_util; long margin = schedtune_cpu_margin(util, cpu); trace_sched_boost_cpu(cpu, util, margin); @@ -5876,8 +5876,6 @@ schedtune_task_margin(struct task_struct *task) #endif /* CONFIG_SCHED_TUNE */ - - static inline unsigned long boosted_task_util(struct task_struct *task) { @@ -6893,6 +6891,7 @@ static long compute_energy(struct task_struct *p, int dst_cpu, */ for_each_cpu_and(cpu, perf_domain_span(pd), cpu_online_mask) { util = cpu_util_next(cpu, p, dst_cpu); + util += cpu_util_rt(cpu_rq(cpu)); util = schedutil_freq_util(cpu, util, ENERGY_UTIL); max_util = max(util, max_util); sum_util += util; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 0be707d9c2db..3eed85fc86db 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1329,6 +1329,8 @@ enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct sched_rt_entity *rt_se = &p->rt; + schedtune_enqueue_task(p, cpu_of(rq)); + if (flags & ENQUEUE_WAKEUP) rt_se->timeout = 0; @@ -1342,6 +1344,8 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct sched_rt_entity *rt_se = &p->rt; + schedtune_dequeue_task(p, cpu_of(rq)); + update_curr_rt(rq); dequeue_rt_entity(rt_se, flags); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 9ca0289f7280..c0397a6501f4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2243,7 +2243,7 @@ enum schedutil_type { }; #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL -unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, +unsigned long schedutil_freq_util(int cpu, unsigned long util, enum schedutil_type type); #else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h index bb187c6112a7..821f026b510f 100644 --- a/kernel/sched/tune.h +++ b/kernel/sched/tune.h @@ -20,7 +20,7 @@ int schedtune_prefer_idle(struct task_struct *tsk); void schedtune_enqueue_task(struct task_struct *p, int cpu); void schedtune_dequeue_task(struct task_struct *p, int cpu); -unsigned long boosted_cpu_util(int cpu); +unsigned long boosted_cpu_util(int cpu, unsigned long other_util); #else /* CONFIG_SCHED_TUNE */ @@ -32,6 +32,6 @@ unsigned long boosted_cpu_util(int cpu); #define schedtune_enqueue_task(task, cpu) do { } while (0) #define schedtune_dequeue_task(task, cpu) do { } while (0) -#define boosted_cpu_util(cpu) cpu_util_cfs(cpu_rq(cpu)) +#define boosted_cpu_util(cpu, other_util) cpu_util_cfs(cpu_rq(cpu)) #endif /* CONFIG_SCHED_TUNE */ From ebd1927621d87704181ecccf61ca62e67fd0d15f Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Mon, 9 Jul 2018 15:44:00 +0100 Subject: [PATCH 0772/1276] ANDROID: Add hold functionality to schedtune CPU boost When tasks come and go from a runqueue quickly, this can lead to boost being applied and removed quickly which sometimes means we cannot raise the CPU frequency again when we need to (due to the rate limit on frequency updates). This has proved to be a particular issue for RT tasks and alternative methods have been used in the past to work around it. This is an attempt to solve the issue for all task classes and cpufreq governors by introducing a generic mechanism in schedtune to retain the max boost level from task enqueue for a minimum period - defined here as 50ms. This timeout was determined experimentally and is not configurable. A sched_feat guards the application of this to tasks - in the default configuration, task boosting only applied to tasks which have RT policy. Change SCHEDTUNE_BOOST_HOLD_ALL to true to apply it to all tasks regardless of class. It works like so: Every task enqueue (in an allowed class) stores a cpu-local timestamp. If the task is not a member of an allowed class (all or RT depending upon feature selection), the timestamp is not updated. The boost group will stay active regardless of tasks present until 50ms beyond the last timestamp stored. We also store the timestamp of the active boost group to avoid unneccesarily revisiting the boost groups when checking CPU boost level. If the timestamp is more than 50ms in the past when we check boost then we re-evaluate the boost groups for that CPU, taking into account the timestamps associated with each group. Idea based on rt-boost-retention patches from Joel. Change-Id: I52cc2d2e82d1c5aa03550378c8836764f41630c1 Suggested-by: Joel Fernandes Reviewed-by: Patrick Bellasi Signed-off-by: Chris Redpath [forward ported from android-4.9-eas-dev proposal] (cherry picked from commit a485e8b7bf8e95759e600396feeb7bfb400b6e46) [ - Trivial cherry-pick conflicts in include/trace/events/sched.h ] Signed-off-by: Quentin Perret --- include/trace/events/sched.h | 11 +++-- kernel/sched/features.h | 11 +++++ kernel/sched/tune.c | 95 ++++++++++++++++++++++++++++++------ 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index e3928f7286d6..ee2dcc08943e 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -902,9 +902,9 @@ TRACE_EVENT(sched_boost_cpu, TRACE_EVENT(sched_tune_tasks_update, TP_PROTO(struct task_struct *tsk, int cpu, int tasks, int idx, - int boost, int max_boost), + int boost, int max_boost, u64 group_ts), - TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost), + TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost, group_ts), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) @@ -914,6 +914,7 @@ TRACE_EVENT(sched_tune_tasks_update, __field( int, idx ) __field( int, boost ) __field( int, max_boost ) + __field( u64, group_ts ) ), TP_fast_assign( @@ -924,13 +925,15 @@ TRACE_EVENT(sched_tune_tasks_update, __entry->idx = idx; __entry->boost = boost; __entry->max_boost = max_boost; + __entry->group_ts = group_ts; ), TP_printk("pid=%d comm=%s " - "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d", + "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d timeout=%llu", __entry->pid, __entry->comm, __entry->cpu, __entry->tasks, __entry->idx, - __entry->boost, __entry->max_boost) + __entry->boost, __entry->max_boost, + __entry->group_ts) ); /* diff --git a/kernel/sched/features.h b/kernel/sched/features.h index fb995c5f69ba..010dce97a308 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -116,3 +116,14 @@ SCHED_FEAT(EAS_PREFER_IDLE, true) * Request max frequency from schedutil whenever a RT task is running. */ SCHED_FEAT(SUGOV_RT_MAX_FREQ, false) + +/* + * Apply schedtune boost hold to tasks of all sched classes. + * If enabled, schedtune will hold the boost applied to a CPU + * for 50ms regardless of task activation - if the task is + * still running 50ms later, the boost hold expires and schedtune + * boost will expire immediately the task stops. + * If disabled, this behaviour will only apply to tasks of the + * RT class. + */ +SCHED_FEAT(SCHEDTUNE_BOOST_HOLD_ALL, false) diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c index 7e0630c52b81..3b231c639fe4 100644 --- a/kernel/sched/tune.c +++ b/kernel/sched/tune.c @@ -13,6 +13,9 @@ bool schedtune_initialized = false; extern struct reciprocal_value schedtune_spc_rdiv; +/* We hold schedtune boost in effect for at least this long */ +#define SCHEDTUNE_BOOST_HOLD_NS 50000000ULL + /* * EAS scheduler tunables for task groups. * @@ -151,6 +154,7 @@ static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = { struct boost_groups { /* Maximum boost value for all RUNNABLE tasks on a CPU */ int boost_max; + u64 boost_ts; struct { /* True when this boost group maps an actual cgroup */ bool valid; @@ -158,6 +162,8 @@ struct boost_groups { int boost; /* Count of RUNNABLE tasks on that boost group */ unsigned tasks; + /* Timestamp of boost activation */ + u64 ts; } group[BOOSTGROUPS_COUNT]; /* CPU's boost group locking */ raw_spinlock_t lock; @@ -166,15 +172,31 @@ struct boost_groups { /* Boost groups affecting each CPU in the system */ DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups); +static inline bool schedtune_boost_timeout(u64 now, u64 ts) +{ + return ((now - ts) > SCHEDTUNE_BOOST_HOLD_NS); +} + +static inline bool +schedtune_boost_group_active(int idx, struct boost_groups* bg, u64 now) +{ + if (bg->group[idx].tasks) + return true; + + return !schedtune_boost_timeout(now, bg->group[idx].ts); +} + static void -schedtune_cpu_update(int cpu) +schedtune_cpu_update(int cpu, u64 now) { struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); int boost_max; + u64 boost_ts; int idx; /* The root boost group is always active */ boost_max = bg->group[0].boost; + boost_ts = now; for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) { /* Ignore non boostgroups not mapping a cgroup */ @@ -183,12 +205,18 @@ schedtune_cpu_update(int cpu) /* * A boost group affects a CPU only if it has - * RUNNABLE tasks on that CPU + * RUNNABLE tasks on that CPU or it has hold + * in effect from a previous task. */ - if (bg->group[idx].tasks == 0) + if (!schedtune_boost_group_active(idx, bg, now)) + continue; + + /* This boost group is active */ + if (boost_max > bg->group[idx].boost) continue; - boost_max = max(boost_max, bg->group[idx].boost); + boost_max = bg->group[idx].boost; + boost_ts = bg->group[idx].ts; } /* Ensures boost_max is non-negative when all cgroup boost values @@ -196,6 +224,7 @@ schedtune_cpu_update(int cpu) * task stacking and frequency spikes.*/ boost_max = max(boost_max, 0); bg->boost_max = boost_max; + bg->boost_ts = boost_ts; } static int @@ -205,6 +234,7 @@ schedtune_boostgroup_update(int idx, int boost) int cur_boost_max; int old_boost; int cpu; + u64 now; /* Update per CPU boost groups */ for_each_possible_cpu(cpu) { @@ -225,15 +255,19 @@ schedtune_boostgroup_update(int idx, int boost) bg->group[idx].boost = boost; /* Check if this update increase current max */ - if (boost > cur_boost_max && bg->group[idx].tasks) { + now = sched_clock_cpu(cpu); + if (boost > cur_boost_max && + schedtune_boost_group_active(idx, bg, now)) { bg->boost_max = boost; + bg->boost_ts = bg->group[idx].ts; + trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max); continue; } /* Check if this update has decreased current max */ if (cur_boost_max == old_boost && old_boost > boost) { - schedtune_cpu_update(cpu); + schedtune_cpu_update(cpu, now); trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max); continue; } @@ -247,6 +281,15 @@ schedtune_boostgroup_update(int idx, int boost) #define ENQUEUE_TASK 1 #define DEQUEUE_TASK -1 +static inline bool +schedtune_update_timestamp(struct task_struct *p) +{ + if (sched_feat(SCHEDTUNE_BOOST_HOLD_ALL)) + return true; + + return task_has_rt_policy(p); +} + static inline void schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) { @@ -256,12 +299,21 @@ schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) /* Update boosted tasks count while avoiding to make it negative */ bg->group[idx].tasks = max(0, tasks); - trace_sched_tune_tasks_update(p, cpu, tasks, idx, - bg->group[idx].boost, bg->boost_max); + /* Update timeout on enqueue */ + if (task_count > 0) { + u64 now = sched_clock_cpu(cpu); + + if (schedtune_update_timestamp(p)) + bg->group[idx].ts = now; - /* Boost group activation or deactivation on that RQ */ - if (tasks == 1 || tasks == 0) - schedtune_cpu_update(cpu); + /* Boost group activation or deactivation on that RQ */ + if (bg->group[idx].tasks == 1) + schedtune_cpu_update(cpu, now); + } + + trace_sched_tune_tasks_update(p, cpu, tasks, idx, + bg->group[idx].boost, bg->boost_max, + bg->group[idx].ts); } /* @@ -305,6 +357,7 @@ int schedtune_can_attach(struct cgroup_taskset *tset) int src_bg; /* Source boost group index */ int dst_bg; /* Destination boost group index */ int tasks; + u64 now; if (unlikely(!schedtune_initialized)) return 0; @@ -355,13 +408,15 @@ int schedtune_can_attach(struct cgroup_taskset *tset) bg->group[src_bg].tasks = max(0, tasks); bg->group[dst_bg].tasks += 1; - raw_spin_unlock(&bg->lock); - task_rq_unlock(rq, task, &rq_flags); + /* Update boost hold start for this group */ + now = sched_clock_cpu(cpu); + bg->group[dst_bg].ts = now; - /* Update CPU boost group */ - if (bg->group[src_bg].tasks == 0 || bg->group[dst_bg].tasks == 1) - schedtune_cpu_update(task_cpu(task)); + /* Force boost group re-evaluation at next boost check */ + bg->boost_ts = now - SCHEDTUNE_BOOST_HOLD_NS; + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); } return 0; @@ -408,8 +463,15 @@ void schedtune_dequeue_task(struct task_struct *p, int cpu) int schedtune_cpu_boost(int cpu) { struct boost_groups *bg; + u64 now; bg = &per_cpu(cpu_boost_groups, cpu); + now = sched_clock_cpu(cpu); + + /* Check to see if we have a hold in effect */ + if (schedtune_boost_timeout(now, bg->boost_ts)) + schedtune_cpu_update(cpu, now); + return bg->boost_max; } @@ -515,6 +577,7 @@ schedtune_boostgroup_init(struct schedtune *st, int idx) bg = &per_cpu(cpu_boost_groups, cpu); bg->group[idx].boost = 0; bg->group[idx].valid = true; + bg->group[idx].ts = 0; } /* Keep track of allocated boost groups */ From 22a871b5115157006c049e0dbd2dfe7d5355dfcc Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 4 Sep 2018 12:51:08 +0100 Subject: [PATCH 0773/1276] ANDROID: thermal: cpu_cooling: Migrate to using the EM framework The newly introduced Energy Model framework manages power cost tables in a generic way. Moreover, it supports a wide variety of models since the tables can come from DT, firmware, etc. On the other hand, the cpu_cooling subsystem manages its own power cost tables using only DT information. In order to avoid the duplication of data in the kernel, and in order to enable IPA with EMs comming from more than just DT, remove the private tables from cpu_cooling.c and migrate it to using the centralized EM framework. The case where the thermal subsystem is used without an Energy Model (cpufreq_cooling_ops) is handled by looking at CPUFreq's frequency table which is already a dependency for cpu_cooling.c anyway. Signed-off-by: Quentin Perret Change-Id: I064e4b0e56d1c52b82a4dc800a1fa90456429911 --- drivers/thermal/cpu_cooling.c | 262 +++++++++++----------------------- 1 file changed, 83 insertions(+), 179 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index dfd23245f778..eb0d87f32bc7 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -48,19 +49,6 @@ * ... */ -/** - * struct freq_table - frequency table along with power entries - * @frequency: frequency in KHz - * @power: power in mW - * - * This structure is built when the cooling device registers and helps - * in translating frequency to power and vice versa. - */ -struct freq_table { - u32 frequency; - u32 power; -}; - /** * struct time_in_idle - Idle time stats * @time: previous reading of the absolute time that this cpu was idle @@ -82,7 +70,7 @@ struct time_in_idle { * frequency. * @max_level: maximum cooling level. One less than total number of valid * cpufreq frequencies. - * @freq_table: Freq table in descending order of frequencies + * @em: Reference on the Energy Model of the device * @cdev: thermal_cooling_device pointer to keep track of the * registered cooling device. * @policy: cpufreq policy. @@ -98,7 +86,7 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int clipped_freq; unsigned int max_level; - struct freq_table *freq_table; /* In descending order */ + struct em_perf_domain *em; struct thermal_cooling_device *cdev; struct cpufreq_policy *policy; struct list_head node; @@ -111,26 +99,6 @@ static LIST_HEAD(cpufreq_cdev_list); /* Below code defines functions to be used for cpufreq as cooling device */ -/** - * get_level: Find the level for a particular frequency - * @cpufreq_cdev: cpufreq_cdev for which the property is required - * @freq: Frequency - * - * Return: level corresponding to the frequency. - */ -static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, - unsigned int freq) -{ - struct freq_table *freq_table = cpufreq_cdev->freq_table; - unsigned long level; - - for (level = 1; level <= cpufreq_cdev->max_level; level++) - if (freq > freq_table[level].frequency) - break; - - return level - 1; -} - /** * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. * @nb: struct notifier_block * with callback info. @@ -184,105 +152,52 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, return NOTIFY_OK; } +#ifdef CONFIG_ENERGY_MODEL /** - * update_freq_table() - Update the freq table with power numbers - * @cpufreq_cdev: the cpufreq cooling device in which to update the table - * @capacitance: dynamic power coefficient for these cpus - * - * Update the freq table with power numbers. This table will be used in - * cpu_power_to_freq() and cpu_freq_to_power() to convert between power and - * frequency efficiently. Power is stored in mW, frequency in KHz. The - * resulting table is in descending order. + * get_level: Find the level for a particular frequency + * @cpufreq_cdev: cpufreq_cdev for which the property is required + * @freq: Frequency * - * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, - * or -ENOMEM if we run out of memory. + * Return: level corresponding to the frequency. */ -static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev, - u32 capacitance) +static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned int freq) { - struct freq_table *freq_table = cpufreq_cdev->freq_table; - struct dev_pm_opp *opp; - struct device *dev = NULL; - int num_opps = 0, cpu = cpufreq_cdev->policy->cpu, i; - - dev = get_cpu_device(cpu); - if (unlikely(!dev)) { - dev_warn(&cpufreq_cdev->cdev->device, - "No cpu device for cpu %d\n", cpu); - return -ENODEV; - } - - num_opps = dev_pm_opp_get_opp_count(dev); - if (num_opps < 0) - return num_opps; - - /* - * The cpufreq table is also built from the OPP table and so the count - * should match. - */ - if (num_opps != cpufreq_cdev->max_level + 1) { - dev_warn(dev, "Number of OPPs not matching with max_levels\n"); - return -EINVAL; - } - - for (i = 0; i <= cpufreq_cdev->max_level; i++) { - unsigned long freq = freq_table[i].frequency * 1000; - u32 freq_mhz = freq_table[i].frequency / 1000; - u64 power; - u32 voltage_mv; - - /* - * Find ceil frequency as 'freq' may be slightly lower than OPP - * freq due to truncation while converting to kHz. - */ - opp = dev_pm_opp_find_freq_ceil(dev, &freq); - if (IS_ERR(opp)) { - dev_err(dev, "failed to get opp for %lu frequency\n", - freq); - return -EINVAL; - } - - voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; - dev_pm_opp_put(opp); - - /* - * Do the multiplication with MHz and millivolt so as - * to not overflow. - */ - power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv; - do_div(power, 1000000000); + int i; - /* power is stored in mW */ - freq_table[i].power = power; + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) + break; } - return 0; + return cpufreq_cdev->max_level - i - 1; } + static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, u32 freq) { int i; - struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i <= cpufreq_cdev->max_level; i++) - if (freq > freq_table[i].frequency) + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) break; + } - return freq_table[i - 1].power; + return cpufreq_cdev->em->table[i + 1].power; } static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, u32 power) { int i; - struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i <= cpufreq_cdev->max_level; i++) - if (power > freq_table[i].power) + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (power > cpufreq_cdev->em->table[i].power) break; + } - return freq_table[i - 1].frequency; + return cpufreq_cdev->em->table[i + 1].frequency; } /** @@ -332,6 +247,7 @@ static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, raw_cpu_power = cpu_freq_to_power(cpufreq_cdev, freq); return (raw_cpu_power * cpufreq_cdev->last_load) / 100; } +#endif /* cpufreq cooling device callback functions are defined below */ @@ -374,6 +290,30 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, return 0; } +static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned long state) +{ + struct cpufreq_policy *policy; + unsigned long idx; + +#ifdef CONFIG_ENERGY_MODEL + /* Use the Energy Model table if available */ + if (cpufreq_cdev->em) { + idx = cpufreq_cdev->max_level - state; + return cpufreq_cdev->em->table[idx].frequency; + } +#endif + + /* Otherwise, fallback on the CPUFreq table */ + policy = cpufreq_cdev->policy; + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) + idx = cpufreq_cdev->max_level - state; + else + idx = state; + + return policy->freq_table[idx].frequency; +} + /** * cpufreq_set_cur_state - callback function to set the current cooling state. * @cdev: thermal cooling device pointer. @@ -398,7 +338,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, if (cpufreq_cdev->cpufreq_state == state) return 0; - clip_freq = cpufreq_cdev->freq_table[state].frequency; + clip_freq = get_state_freq(cpufreq_cdev, state); cpufreq_cdev->cpufreq_state = state; cpufreq_cdev->clipped_freq = clip_freq; @@ -407,6 +347,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, return 0; } +#ifdef CONFIG_ENERGY_MODEL /** * cpufreq_get_requested_power() - get the current power * @cdev: &thermal_cooling_device pointer @@ -497,7 +438,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, struct thermal_zone_device *tz, unsigned long state, u32 *power) { - unsigned int freq, num_cpus; + unsigned int freq, num_cpus, idx; struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; /* Request state should be less than max_level */ @@ -506,7 +447,8 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); - freq = cpufreq_cdev->freq_table[state].frequency; + idx = cpufreq_cdev->max_level - state; + freq = cpufreq_cdev->em->table[idx].frequency; *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus; return 0; @@ -553,14 +495,6 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, return 0; } -/* Bind cpufreq callbacks to thermal cooling device ops */ - -static struct thermal_cooling_device_ops cpufreq_cooling_ops = { - .get_max_state = cpufreq_get_max_state, - .get_cur_state = cpufreq_get_cur_state, - .set_cur_state = cpufreq_set_cur_state, -}; - static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { .get_max_state = cpufreq_get_max_state, .get_cur_state = cpufreq_get_cur_state, @@ -569,32 +503,27 @@ static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { .state2power = cpufreq_state2power, .power2state = cpufreq_power2state, }; +#endif + +/* Bind cpufreq callbacks to thermal cooling device ops */ + +static struct thermal_cooling_device_ops cpufreq_cooling_ops = { + .get_max_state = cpufreq_get_max_state, + .get_cur_state = cpufreq_get_cur_state, + .set_cur_state = cpufreq_set_cur_state, +}; /* Notifier for cpufreq policy change */ static struct notifier_block thermal_cpufreq_notifier_block = { .notifier_call = cpufreq_thermal_notifier, }; -static unsigned int find_next_max(struct cpufreq_frequency_table *table, - unsigned int prev_max) -{ - struct cpufreq_frequency_table *pos; - unsigned int max = 0; - - cpufreq_for_each_valid_entry(pos, table) { - if (pos->frequency > max && pos->frequency < prev_max) - max = pos->frequency; - } - - return max; -} - /** * __cpufreq_cooling_register - helper function to create cpufreq cooling device * @np: a valid struct device_node to the cooling device device tree node * @policy: cpufreq policy * Normally this should be same as cpufreq policy->related_cpus. - * @capacitance: dynamic power coefficient for these cpus + * @try_model: true if a power model should be used * * This interface function registers the cpufreq cooling device with the name * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq @@ -606,12 +535,12 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, */ static struct thermal_cooling_device * __cpufreq_cooling_register(struct device_node *np, - struct cpufreq_policy *policy, u32 capacitance) + struct cpufreq_policy *policy, bool try_model) { struct thermal_cooling_device *cdev; struct cpufreq_cooling_device *cpufreq_cdev; char dev_name[THERMAL_NAME_LENGTH]; - unsigned int freq, i, num_cpus; + unsigned int i, num_cpus; int ret; struct thermal_cooling_device_ops *cooling_ops; bool first; @@ -645,54 +574,36 @@ __cpufreq_cooling_register(struct device_node *np, /* max_level is an index, not a counter */ cpufreq_cdev->max_level = i - 1; - cpufreq_cdev->freq_table = kmalloc_array(i, - sizeof(*cpufreq_cdev->freq_table), - GFP_KERNEL); - if (!cpufreq_cdev->freq_table) { - cdev = ERR_PTR(-ENOMEM); - goto free_idle_time; - } +#ifdef CONFIG_ENERGY_MODEL + if (try_model) { + struct em_perf_domain *em = em_cpu_get(policy->cpu); + + if (!em || !cpumask_equal(policy->cpus, to_cpumask(em->cpus))) { + cdev = ERR_PTR(-EINVAL); + goto free_idle_time; + } + cpufreq_cdev->em = em; + cooling_ops = &cpufreq_power_cooling_ops; + } else +#endif + cooling_ops = &cpufreq_cooling_ops; ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); if (ret < 0) { cdev = ERR_PTR(ret); - goto free_table; + goto free_idle_time; } cpufreq_cdev->id = ret; snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", cpufreq_cdev->id); - /* Fill freq-table in descending order of frequencies */ - for (i = 0, freq = -1; i <= cpufreq_cdev->max_level; i++) { - freq = find_next_max(policy->freq_table, freq); - cpufreq_cdev->freq_table[i].frequency = freq; - - /* Warn for duplicate entries */ - if (!freq) - pr_warn("%s: table has duplicate entries\n", __func__); - else - pr_debug("%s: freq:%u KHz\n", __func__, freq); - } - - if (capacitance) { - ret = update_freq_table(cpufreq_cdev, capacitance); - if (ret) { - cdev = ERR_PTR(ret); - goto remove_ida; - } - - cooling_ops = &cpufreq_power_cooling_ops; - } else { - cooling_ops = &cpufreq_cooling_ops; - } - cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; - cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; + cpufreq_cdev->clipped_freq = get_state_freq(cpufreq_cdev, 0); cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); @@ -709,8 +620,6 @@ __cpufreq_cooling_register(struct device_node *np, remove_ida: ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); -free_table: - kfree(cpufreq_cdev->freq_table); free_idle_time: kfree(cpufreq_cdev->idle_time); free_cdev: @@ -732,7 +641,7 @@ __cpufreq_cooling_register(struct device_node *np, struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { - return __cpufreq_cooling_register(NULL, policy, 0); + return __cpufreq_cooling_register(NULL, policy, false); } EXPORT_SYMBOL_GPL(cpufreq_cooling_register); @@ -760,7 +669,6 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { struct device_node *np = of_get_cpu_node(policy->cpu, NULL); struct thermal_cooling_device *cdev = NULL; - u32 capacitance = 0; if (!np) { pr_err("cpu_cooling: OF node not available for cpu%d\n", @@ -769,10 +677,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) } if (of_find_property(np, "#cooling-cells", NULL)) { - of_property_read_u32(np, "dynamic-power-coefficient", - &capacitance); - - cdev = __cpufreq_cooling_register(np, policy, capacitance); + cdev = __cpufreq_cooling_register(np, policy, true); if (IS_ERR(cdev)) { pr_err("cpu_cooling: cpu%d is not running as cooling device: %ld\n", policy->cpu, PTR_ERR(cdev)); @@ -814,7 +719,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); kfree(cpufreq_cdev->idle_time); - kfree(cpufreq_cdev->freq_table); kfree(cpufreq_cdev); } EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); From d12c09e8c6b1596647a6a267da18f6dd4c929381 Mon Sep 17 00:00:00 2001 From: Alice Liu Date: Thu, 10 Nov 2016 20:11:59 +0800 Subject: [PATCH 0774/1276] [REVERTME] dwc3 setup highspeed to USB3.0 on bxtp platform We find USB3.0 dwc3 can't work at superspeed on bxtp platform. We setup dwc3 highspeed to enable USB3.0 on bxtp platform. Change-Id: I48df90c39b652f8e2a48598e9e31947e5d68ca17 Tracked-On: Signed-off-by: xiao jin Signed-off-by: Liu, Alice Signed-off-by: Greniger, Jacek (cherry picked from commit 4fc6d0494f0d5f21223c5b3f802a116a757c5893) --- drivers/usb/dwc3/gadget.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2b53194081ba..cefd586301c8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1810,6 +1811,17 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DCFG, reg); } +static inline bool platform_is_bxtp(void) +{ +#ifdef CONFIG_X86_64 + if ((boot_cpu_data.x86_model == 0x5c) + && (boot_cpu_data.x86_stepping >= 0x8) + && (boot_cpu_data.x86_stepping <= 0xf)) + return true; +#endif + return false; +} + static int __dwc3_gadget_start(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -2018,10 +2030,23 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g, reg |= DWC3_DCFG_HIGHSPEED; break; case USB_SPEED_SUPER: - reg |= DWC3_DCFG_SUPERSPEED; + /* + * WORKAROUND: BXTP platform USB3.0 port SS fail, + * We switch SS to HS to enable USB3.0. + */ + if (platform_is_bxtp()) + reg |= DWC3_DCFG_HIGHSPEED; + else + reg |= DWC3_DCFG_SUPERSPEED; break; case USB_SPEED_SUPER_PLUS: - if (dwc3_is_usb31(dwc)) + /* + * WORKAROUND: BXTP platform USB3.0 port SS fail, + * We switch SS to HS to enable USB3.0. + */ + if (platform_is_bxtp()) + reg |= DWC3_DCFG_HIGHSPEED; + else if (dwc3_is_usb31(dwc)) reg |= DWC3_DCFG_SUPERSPEED_PLUS; else reg |= DWC3_DCFG_SUPERSPEED; From 0d5052271d54db1ba7a8208ddc26b2385295af25 Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Fri, 5 Oct 2018 15:19:47 +0530 Subject: [PATCH 0775/1276] xhci-ext-caps.c: Add property to disable SW switch In platforms like Cherrytrail, 'SW switch enable' bit should not be enabled for role switch. This patch adds a property to Intel USB Role Switch platform driver to denote that SW switch should be disabled in Cherrytrail devices. Signed-off-by: Saranya Gopal Signed-off-by: M, Balaji [ heikki: fixed compile warnings ] Signed-off-by: Heikki Krogerus --- drivers/usb/host/xhci-ext-caps.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c index 399113f9fc5c..b4129dd6a64a 100644 --- a/drivers/usb/host/xhci-ext-caps.c +++ b/drivers/usb/host/xhci-ext-caps.c @@ -6,11 +6,20 @@ */ #include +#include +#include #include "xhci.h" #define USB_SW_DRV_NAME "intel_xhci_usb_sw" #define USB_SW_RESOURCE_SIZE 0x400 +#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 + +static const struct property_entry role_switch_props[] = { + PROPERTY_ENTRY_BOOL("drd,sw_switch_disable"), + {}, +}; + static void xhci_intel_unregister_pdev(void *arg) { platform_device_unregister(arg); @@ -21,6 +30,7 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) struct usb_hcd *hcd = xhci_to_hcd(xhci); struct device *dev = hcd->self.controller; struct platform_device *pdev; + struct pci_dev *pci = to_pci_dev(dev); struct resource res = { 0, }; int ret; @@ -43,6 +53,14 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) return ret; } + if (pci->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { + ret = platform_device_add_properties(pdev, role_switch_props); + if (ret) { + dev_err(dev, "failed to register device properties\n"); + return ret; + } + } + pdev->dev.parent = dev; ret = platform_device_add(pdev); From 99b6186fbef953d5463c612cbfc00c716841997e Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Fri, 5 Oct 2018 15:20:15 +0530 Subject: [PATCH 0776/1276] roles: Enable static DRD mode for role switch in Intel platforms Enable static DRD mode in Intel platforms which guarantees successful role switch all the time. This fixes issues like software role switch failure after cold boot and issue with role switch when 3.0 OTG cable is used. But, do not enable static DRD mode for Cherrytrail devices which rely on firmware for role switch. Signed-off-by: Saranya Gopal Signed-off-by: M, Balaji [ heikki: Minor cleanup ] Signed-off-by: Heikki Krogerus --- .../usb/roles/intel-xhci-usb-role-switch.c | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 277de96181f9..f9ed262c15b3 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* register definition */ @@ -26,6 +27,9 @@ #define SW_VBUS_VALID BIT(24) #define SW_IDPIN_EN BIT(21) #define SW_IDPIN BIT(20) +#define SW_SWITCH_EN_CFG0 BIT(16) +#define SW_DRD_STATIC_HOST_CFG0 1 +#define SW_DRD_STATIC_DEV_CFG0 2 #define DUAL_ROLE_CFG1 0x6c #define HOST_MODE BIT(29) @@ -37,6 +41,7 @@ struct intel_xhci_usb_data { struct usb_role_switch *role_sw; void __iomem *base; + bool disable_sw_switch; }; static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) @@ -59,23 +64,39 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) pm_runtime_get_sync(dev); - /* Set idpin value as requested */ + /* + * Set idpin value as requested. + * Since some devices rely on firmware setting DRD_CONFIG and + * SW_SWITCH_EN_CFG0 bits to be zero for role switch, + * do not set these bits for those devices. + */ val = readl(data->base + DUAL_ROLE_CFG0); switch (role) { case USB_ROLE_NONE: val |= SW_IDPIN; val &= ~SW_VBUS_VALID; + val &= ~(SW_DRD_STATIC_DEV_CFG0 | SW_DRD_STATIC_HOST_CFG0); break; case USB_ROLE_HOST: val &= ~SW_IDPIN; val &= ~SW_VBUS_VALID; + if (!data->disable_sw_switch) { + val &= ~SW_DRD_STATIC_DEV_CFG0; + val |= SW_DRD_STATIC_HOST_CFG0; + } break; case USB_ROLE_DEVICE: val |= SW_IDPIN; val |= SW_VBUS_VALID; + if (!data->disable_sw_switch) { + val &= ~SW_DRD_STATIC_HOST_CFG0; + val |= SW_DRD_STATIC_DEV_CFG0; + } break; } val |= SW_IDPIN_EN; + if (!data->disable_sw_switch) + val |= SW_SWITCH_EN_CFG0; writel(val, data->base + DUAL_ROLE_CFG0); @@ -147,6 +168,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + data->disable_sw_switch = device_property_read_bool(dev, + "drd,sw_switch_disable"); + data->role_sw = usb_role_switch_register(dev, &sw_desc); if (IS_ERR(data->role_sw)) return PTR_ERR(data->role_sw); From 37048ee5eecc697eec4048797a52f9cdf4d8e877 Mon Sep 17 00:00:00 2001 From: Sathyanarayanan Kuppuswamy Date: Tue, 12 Sep 2017 12:12:17 -0700 Subject: [PATCH 0777/1276] init: do_mount_dm: Add logic to wait for block device enumeration Calling dm_setup_drives() before the completion of raw block device enumeration leads to "device lookup failed" error in dm_table_add_target() function, which in turn leads to incomplete device mapper configuration for the given block device. Without proper DM setup, if we try to mount the DM device, it leads to following kernel panic in mount_root() function due to invalid DM queue setup. This patch fixes this issue by adding a logic to wait for block device enumeration to complete before performing the verity checks. [ 0.875069] device-mapper: table: 253:0: verity: Data device lookup failed [ 0.883103] BUG: unable to handle kernel NULL pointer dereference at (null) [ 0.891854] IP: (null) [ 0.895453] PGD 0 [ 0.895454] P4D 0 [ 0.897686] [ 0.901574] Oops: 0010 [#1] PREEMPT SMP [ 0.905856] Modules linked in: [ 0.909266] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G U W 4.13.0-rc4-quilt-2e5dc0ac #2 [ 0.919083] task: ffff968eb6a06040 task.stack: ffff9b9f40010000 [ 0.925698] RIP: 0010: (null) [ 0.929880] RSP: 0000:ffff9b9f40013a30 EFLAGS: 00010246 [ 0.935714] RAX: 0000000000000000 RBX: ffff968eb63620c0 RCX: 0000000000000000 [ 0.943674] RDX: 0000000000000000 RSI: ffff968eb63620c0 RDI: ffff968eb4a55370 [ 0.951649] RBP: ffff9b9f40013a88 R08: 0000000000000001 R09: ffff968eb6362130 [ 0.959623] R10: fffff5b2c9d28d00 R11: ffff968eb51fbec0 R12: ffff968eb4a55370 [ 0.967586] R13: 00000000ffffffff R14: 0000000000000000 R15: 0000000000000000 [ 0.975557] FS: 0000000000000000(0000) GS:ffff968ebfc80000(0000) knlGS:0000000000000000 [ 0.984588] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 0.990997] CR2: 0000000000000000 CR3: 0000000185212000 CR4: 00000000003406e0 [ 0.998958] Call Trace: [ 1.001692] ? generic_make_request+0x122/0x320 [ 1.006753] submit_bio+0x73/0x160 [ 1.010550] submit_bh_wbc.isra.44+0x113/0x140 [ 1.015518] __bread_gfp+0x67/0x120 [ 1.019414] ext4_fill_super+0x184/0x3880 [ 1.023891] ? vsnprintf+0x201/0x490 [ 1.027885] ? set_bdev_super+0x30/0x30 [ 1.032170] ? snprintf+0x43/0x60 [ 1.035869] mount_bdev+0x17d/0x1b0 [ 1.039762] ? ext4_calculate_overhead+0x430/0x430 [ 1.045113] ext4_mount+0x15/0x20 [ 1.048813] mount_fs+0x153/0x180 [ 1.052513] vfs_kern_mount+0x90/0x180 [ 1.056699] do_mount+0x1e0/0xd00 [ 1.060397] ? _copy_from_user+0x60/0xb0 [ 1.064780] ? memdup_user+0x53/0x80 [ 1.068770] SyS_mount+0x94/0xd0 [ 1.072378] mount_block_root+0x105/0x2c4 [ 1.076857] mount_root+0x6d/0x71 [ 1.080555] prepare_namespace+0x172/0x19f [ 1.085127] kernel_init_freeable+0x21f/0x243 [ 1.089990] ? rest_init+0xd0/0xd0 [ 1.093777] kernel_init+0xe/0x100 [ 1.097575] ret_from_fork+0x27/0x40 [ 1.101563] Code: Bad RIP value. [ 1.105266] RIP: (null) RSP: ffff9b9f40013a30 [ 1.111089] CR2: 0000000000000000 [ 1.114789] ---[ end trace 7a237ef06917bfe3 ]--- [ 1.123581] Kernel panic - not syncing: Fatal exception [ 1.129510] reboot: panic mode set: p,w [ 1.133784] Kernel Offset: 0x25000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) Fixes: e97f06fcd04b ("CHROMIUM: dm: boot time specification of dm=") Change-Id: I48ac7fc8ac752b4ebac25b8b21267f9ff9ba07bd Signed-off-by: Sathyanarayanan Kuppuswamy --- include/linux/device-mapper.h | 5 ++++ init/do_mounts_dm.c | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index b7b047709918..63974da8c1ba 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -526,6 +526,11 @@ struct dm_table *dm_swap_table(struct mapped_device *md, */ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); +/* + * Helper function to parse DM arguments + */ +int dm_split_args(int *argc, char ***argvp, char *input); + /*----------------------------------------------------------------- * Macros. *---------------------------------------------------------------*/ diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index af84b01ccfbc..cb63c625fd0a 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -343,6 +343,47 @@ static int __init dm_setup(char *str) return 0; } +/* Number of milliseconds to wait before checking for drive status */ +#define DM_DRIVE_WAIT 20 +/* Number of tries allowed to wait for DM drive, before timeout */ +#define DM_DRIVE_RETRY_COUNT 100 + +static int __init dm_wait_for_drive(char *params) +{ + char *dm_params; + int ret, argc = 0, try = 0; + char **argv; + dev_t dm_dev; + + dm_params = kstrndup(params, strlen(params), GFP_KERNEL); + if (!dm_params) + return -ENOMEM; + + ret = dm_split_args(&argc, &argv, dm_params); + if (ret || argc < 2) { + DMDEBUG("failed to get dm params"); + goto free_dm_params; + } + + dm_dev = dm_get_dev_t(argv[1]); + while (!dm_dev && try++ < DM_DRIVE_RETRY_COUNT) { + DMDEBUG("Waiting for device %s\n", argv[1]); + msleep(DM_DRIVE_WAIT); + dm_dev = dm_get_dev_t(argv[1]); + } + + if (!dm_dev) { + ret = -ENODEV; + goto free_dm_params; + } + + DMDEBUG("Device %s found\n", argv[1]); + +free_dm_params: + kfree(dm_params); + return ret; +} + static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; @@ -382,6 +423,10 @@ static void __init dm_setup_drives(void) (unsigned long long) target->begin, (unsigned long long) target->length, target->type, target->params); + + if (dm_wait_for_drive(target->params)) + goto add_target_fail; + if (dm_table_add_target(table, target->type, target->begin, target->length, From fcde3c03ab1226994d60762299b2b27c101c0fc3 Mon Sep 17 00:00:00 2001 From: "hangyu.li" Date: Wed, 9 Aug 2017 10:41:48 +0000 Subject: [PATCH 0778/1276] staging/android: add kernel config SYNC The VTS case VtsKernelConfigTest#testKernelConfigs needs the kernel configs conformed to Android requirements as in android-base.cfg. Change-Id: Ia0e5ce86ec8864a78a49a9e5e5e43aa56d70c16f Signed-off-by: Pan, Kris --- drivers/staging/android/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 17c5587805f5..2cf17c1dff86 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -23,6 +23,14 @@ config ANDROID_VSOC a 'cuttlefish' Android image inside QEmu. The driver interacts with a QEmu ivshmem device. If built as a module, it will be called vsoc. +config SYNC + bool "Synchronization framework" + default n + ---help--- + This option enables the framework for synchronization between multiple + drivers. Sync implementations can take advantage of hardware + synchronization built into devices like GPUs. + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID From da5ba0d384cfad42f4e6b7c5f5a2a314a0dcd696 Mon Sep 17 00:00:00 2001 From: Yu Ning Date: Wed, 29 Nov 2017 17:05:47 +0800 Subject: [PATCH 0779/1276] staging: android: Add fwdata driver In order to enable Android first stage mount (early mount) for Intel physical platforms (Gordon Peak, etc.), import the goldfish_fwdata driver from AOSP kernel/goldfish.git (branch android-goldfish-3.18), and remove its dependency on the Goldfish virtual platform (CONFIG_GOLDFISH). For more details, see the original AOSP patch that introduced goldfish_fwdata: https://r.android.com/442393 as well as: https://r.android.com/442315 + Fix a few warnings and style issues caught by checkpatch.pl. Change-Id: Ib882cb76f26726c41b49cbf14ffde803eab5d28d Tracked-On: Signed-off-by: Yu Ning Signed-off-by: Pan, Kris --- drivers/staging/android/Kconfig | 15 ++ drivers/staging/android/Makefile | 1 + drivers/staging/android/fwdata.c | 308 +++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/staging/android/fwdata.c diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 2cf17c1dff86..0c5f13dc3acf 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -31,6 +31,21 @@ config SYNC drivers. Sync implementations can take advantage of hardware synchronization built into devices like GPUs. +config ANDROID_FWDATA + tristate "Parser for Android-specific firmware data" + depends on ACPI + default n + ---help--- + This driver parses Android-specific data (e.g. fstab configuration) + stored in firmware (e.g. ACPI tables), and present it to user space + via sysfs. Android Oreo (8.0) and later requires some essential boot- + time configuration to be available in a directory structure organized + in Device Tree style, e.g. /proc/device-tree/firmware/android/ on + platforms that enable DT. Platforms that use ACPI instead of DT, such + as Goldfish (Ranchu) x86/x86_64, should enable this driver to ensure + the required information can be found in sysfs with the expected + layout. + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 90e6154f11a4..341bfa11316e 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -4,3 +4,4 @@ obj-y += ion/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o +obj-$(CONFIG_ANDROID_FWDATA) += fwdata.o diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c new file mode 100644 index 000000000000..28595eca1781 --- /dev/null +++ b/drivers/staging/android/fwdata.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +struct android_fwdata_state { + struct device *dev; + struct kobject *properties_kobj; + struct kobject *android_kobj; + struct kobject *fstab_kobj; + struct kobject *system_kobj; + struct kobject *vendor_kobj; +}; + +static struct android_fwdata_state state; + +/* Called when /properties// is read. */ +static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + const char *prefix = NULL; + char key[128]; + const char *value = NULL; + int ret; + + /* It would be much more convenient if show() gave us the relative path + * to the file being read, e.g. properties/android/fstab/system/dev, + * which could be easily converted to a property key. + * TODO: Infer the relative path from kobj and remove all hard-coded + * property keys. + */ + if (kobj == state.android_kobj) { + prefix = "android"; + } else if (kobj == state.fstab_kobj) { + prefix = "android.fstab"; + } else if (kobj == state.system_kobj) { + prefix = "android.fstab.system"; + } else if (kobj == state.vendor_kobj) { + prefix = "android.fstab.vendor"; + } else { + pr_err("%s: Unexpected folder\n", __func__); + return -EINVAL; + } + /* We don't put any file in properties/ directly, so prefix can't be + * empty. + */ + snprintf(key, sizeof(key), "%s.%s", prefix, attr->attr.name); + + ret = device_property_read_string(state.dev, key, &value); + if (ret) { + pr_err("%s: Failed to read property '%s', ret=%d\n", __func__, + key, ret); + return ret; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", value); +} + +#define DT_COMPATIBLE_ATTR(_folder) \ + struct kobj_attribute _folder##_compatible_attr = { \ + .attr = { .name = "compatible", .mode = 0444, }, \ + .show = property_show, \ + } + +static DT_COMPATIBLE_ATTR(android); +static DT_COMPATIBLE_ATTR(fstab); +static DT_COMPATIBLE_ATTR(system); +static DT_COMPATIBLE_ATTR(vendor); + +#define FSTAB_PROPERTY_ATTR(_partition, _name) \ + struct kobj_attribute _partition##_##_name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0444, \ + }, \ + .show = property_show, \ + } + +static FSTAB_PROPERTY_ATTR(system, dev); +static FSTAB_PROPERTY_ATTR(system, type); +static FSTAB_PROPERTY_ATTR(system, mnt_flags); +static FSTAB_PROPERTY_ATTR(system, fsmgr_flags); + +static FSTAB_PROPERTY_ATTR(vendor, dev); +static FSTAB_PROPERTY_ATTR(vendor, type); +static FSTAB_PROPERTY_ATTR(vendor, mnt_flags); +static FSTAB_PROPERTY_ATTR(vendor, fsmgr_flags); + +static struct attribute *system_attrs[] = { + &system_compatible_attr.attr, + &system_dev_attr.attr, + &system_type_attr.attr, + &system_mnt_flags_attr.attr, + &system_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group system_group = { + .attrs = system_attrs, +}; + +static struct attribute *vendor_attrs[] = { + &vendor_compatible_attr.attr, + &vendor_dev_attr.attr, + &vendor_type_attr.attr, + &vendor_mnt_flags_attr.attr, + &vendor_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group vendor_group = { + .attrs = vendor_attrs, +}; + +static struct kobject *create_folder(struct kobject *parent, const char *name) +{ + struct kobject *kobj; + + kobj = kobject_create_and_add(name, parent); + if (!kobj) { + pr_err("%s: Failed to create %s/\n", __func__, name); + return NULL; + } + return kobj; +} + +static struct kobject *create_folder_with_file(struct kobject *parent, + const char *name, + struct kobj_attribute *attr) +{ + struct kobject *kobj; + + kobj = create_folder(parent, name); + if (kobj) { + /* Note: Usually drivers should use device_create_file() rather + * than sysfs_create_file(), but the former does not support + * creating the file in a subfolder. + */ + int ret; + + ret = sysfs_create_file(kobj, &attr->attr); + if (ret) { + pr_err("%s: Failed to create %s/%s: ret=%d\n", __func__, + name, attr->attr.name, ret); + kobject_put(kobj); + return NULL; + } + } + return kobj; +} + +static void remove_folder_with_file(struct kobject *kobj, + struct kobj_attribute *attr) +{ + sysfs_remove_file(kobj, &attr->attr); + kobject_put(kobj); +} + +static struct kobject *create_folder_with_files(struct kobject *parent, + const char *name, + struct attribute_group *group) +{ + struct kobject *kobj; + + kobj = create_folder(parent, name); + if (kobj) { + /* Note: Usually drivers should use device_add_groups() rather + * than sysfs_create_group(), but the former does not support + * creating the folder in a subfolder. + */ + int ret; + + ret = sysfs_create_group(kobj, group); + if (ret) { + pr_err("%s: Failed to create %s/*: ret=%d\n", __func__, + name, ret); + kobject_put(kobj); + return NULL; + } + } + return kobj; +} + +static void remove_folder_with_files(struct kobject *kobj, + struct attribute_group *group) +{ + sysfs_remove_group(kobj, group); + kobject_put(kobj); +} + +static void clean_up(void) +{ + if (state.vendor_kobj) { + /* Delete /properties/android/fstab/vendor/ */ + remove_folder_with_files(state.vendor_kobj, &vendor_group); + state.vendor_kobj = NULL; + } + if (state.system_kobj) { + /* Delete /properties/android/fstab/system/ */ + remove_folder_with_files(state.system_kobj, &system_group); + state.system_kobj = NULL; + } + if (state.fstab_kobj) { + /* Delete /properties/android/fstab/ */ + remove_folder_with_file(state.fstab_kobj, + &fstab_compatible_attr); + state.fstab_kobj = NULL; + } + if (state.android_kobj) { + /* Delete /properties/android/ */ + remove_folder_with_file(state.android_kobj, + &android_compatible_attr); + state.android_kobj = NULL; + } + if (state.properties_kobj) { + /* Delete /properties/ */ + kobject_put(state.properties_kobj); + state.properties_kobj = NULL; + } +} + +static int android_fwdata_probe(struct platform_device *pdev) +{ + int ret = -EIO; + + state.dev = &pdev->dev; + /* Create /properties/ */ + state.properties_kobj = create_folder(&state.dev->kobj, "properties"); + if (!state.properties_kobj) + goto out; + + /* TODO: Iterate over all device properties in firmware, and dynamically + * create sysfs nodes under /properties/ + */ + + /* Create /properties/android/compatible */ + state.android_kobj = create_folder_with_file(state.properties_kobj, + "android", + &android_compatible_attr); + if (!state.android_kobj) + goto out; + + /* Create /properties/android/fstab/compatible */ + state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab", + &fstab_compatible_attr); + if (!state.fstab_kobj) + goto out; + + if (device_property_present(state.dev, "android.fstab.system.dev")) { + /* Firmware contains fstab config for early mount of /system */ + state.system_kobj = create_folder_with_files(state.fstab_kobj, + "system", + &system_group); + if (!state.system_kobj) + goto out; + } + if (device_property_present(state.dev, "android.fstab.vendor.dev")) { + /* Firmware contains fstab config for early mount of /vendor */ + state.vendor_kobj = create_folder_with_files(state.fstab_kobj, + "vendor", + &vendor_group); + if (!state.vendor_kobj) + goto out; + } + return 0; + +out: + clean_up(); + return ret; +} + +static int android_fwdata_remove(struct platform_device *pdev) +{ + clean_up(); + return 0; +} + +static const struct acpi_device_id android_fwdata_acpi_match[] = { + { "ANDR0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, android_fwdata_acpi_match); + +static struct platform_driver android_fwdata_driver = { + .probe = android_fwdata_probe, + .remove = android_fwdata_remove, + .driver = { + .name = "android_fwdata", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(android_fwdata_acpi_match), + } +}; + +module_platform_driver(android_fwdata_driver); From 8a0ba06f64f76afb9f20fd71b1c521afaf14d964 Mon Sep 17 00:00:00 2001 From: Yu Ning Date: Thu, 30 Nov 2017 11:28:30 +0800 Subject: [PATCH 0780/1276] staging: android: fwdata: Support vbmeta data Besides android.fstab.*, Intel physical platforms (Gordon Peak, etc.) also encode Android Verified Boot configuration in firmware (i.e. the android.vbmeta.{compatible, parts} properties packaged in ACPI _DSD), and need the fwdata driver to present this data to user space. Refactor the macros for declaring sysfs attributes, and add a new, optional attribute group for vbmeta. Change-Id: Iabc7a4c8f6ce813bb17f4d7d8382c14299bbce31 Tracked-On: Signed-off-by: Yu Ning --- drivers/staging/android/fwdata.c | 66 ++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c index 28595eca1781..1f06c615a64e 100644 --- a/drivers/staging/android/fwdata.c +++ b/drivers/staging/android/fwdata.c @@ -22,6 +22,7 @@ struct android_fwdata_state { struct device *dev; struct kobject *properties_kobj; struct kobject *android_kobj; + struct kobject *vbmeta_kobj; struct kobject *fstab_kobj; struct kobject *system_kobj; struct kobject *vendor_kobj; @@ -46,6 +47,8 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, */ if (kobj == state.android_kobj) { prefix = "android"; + } else if (kobj == state.vbmeta_kobj) { + prefix = "android.vbmeta"; } else if (kobj == state.fstab_kobj) { prefix = "android.fstab"; } else if (kobj == state.system_kobj) { @@ -70,19 +73,8 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, return scnprintf(buf, PAGE_SIZE, "%s\n", value); } -#define DT_COMPATIBLE_ATTR(_folder) \ - struct kobj_attribute _folder##_compatible_attr = { \ - .attr = { .name = "compatible", .mode = 0444, }, \ - .show = property_show, \ - } - -static DT_COMPATIBLE_ATTR(android); -static DT_COMPATIBLE_ATTR(fstab); -static DT_COMPATIBLE_ATTR(system); -static DT_COMPATIBLE_ATTR(vendor); - -#define FSTAB_PROPERTY_ATTR(_partition, _name) \ - struct kobj_attribute _partition##_##_name##_attr = { \ +#define DT_SIMPLE_ATTR(_prefix, _name) \ + struct kobj_attribute _prefix##_##_name##_attr = { \ .attr = { \ .name = __stringify(_name), \ .mode = 0444, \ @@ -90,15 +82,33 @@ static DT_COMPATIBLE_ATTR(vendor); .show = property_show, \ } -static FSTAB_PROPERTY_ATTR(system, dev); -static FSTAB_PROPERTY_ATTR(system, type); -static FSTAB_PROPERTY_ATTR(system, mnt_flags); -static FSTAB_PROPERTY_ATTR(system, fsmgr_flags); +static DT_SIMPLE_ATTR(android, compatible); +static DT_SIMPLE_ATTR(vbmeta, compatible); +static DT_SIMPLE_ATTR(fstab, compatible); +static DT_SIMPLE_ATTR(system, compatible); +static DT_SIMPLE_ATTR(vendor, compatible); -static FSTAB_PROPERTY_ATTR(vendor, dev); -static FSTAB_PROPERTY_ATTR(vendor, type); -static FSTAB_PROPERTY_ATTR(vendor, mnt_flags); -static FSTAB_PROPERTY_ATTR(vendor, fsmgr_flags); +static DT_SIMPLE_ATTR(vbmeta, parts); + +static struct attribute *vbmeta_attrs[] = { + &vbmeta_compatible_attr.attr, + &vbmeta_parts_attr.attr, + NULL, +}; + +static struct attribute_group vbmeta_group = { + .attrs = vbmeta_attrs, +}; + +static DT_SIMPLE_ATTR(system, dev); +static DT_SIMPLE_ATTR(system, type); +static DT_SIMPLE_ATTR(system, mnt_flags); +static DT_SIMPLE_ATTR(system, fsmgr_flags); + +static DT_SIMPLE_ATTR(vendor, dev); +static DT_SIMPLE_ATTR(vendor, type); +static DT_SIMPLE_ATTR(vendor, mnt_flags); +static DT_SIMPLE_ATTR(vendor, fsmgr_flags); static struct attribute *system_attrs[] = { &system_compatible_attr.attr, @@ -220,6 +230,11 @@ static void clean_up(void) &fstab_compatible_attr); state.fstab_kobj = NULL; } + if (state.vbmeta_kobj) { + /* Delete /properties/android/vbmeta/ */ + remove_folder_with_files(state.vbmeta_kobj, &vbmeta_group); + state.vbmeta_kobj = NULL; + } if (state.android_kobj) { /* Delete /properties/android/ */ remove_folder_with_file(state.android_kobj, @@ -254,6 +269,15 @@ static int android_fwdata_probe(struct platform_device *pdev) if (!state.android_kobj) goto out; + if (device_property_present(state.dev, "android.vbmeta.compatible")) { + /* Firmware contains vbmeta config for AVB 2.0 */ + state.vbmeta_kobj = create_folder_with_files(state.android_kobj, + "vbmeta", + &vbmeta_group); + if (!state.vbmeta_kobj) + goto out; + } + /* Create /properties/android/fstab/compatible */ state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab", &fstab_compatible_attr); From c54b08c3246ce9859169c186e0208150f7fc988b Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 21 Aug 2018 14:01:30 +0800 Subject: [PATCH 0781/1276] staging: android: fwdata: Support product and odm Android P need the earlymount supports the product partition and the odm partition Tracked-On: OAM-67693 Signed-off-by: biyilix --- drivers/staging/android/fwdata.c | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c index 1f06c615a64e..525f7e92ec84 100644 --- a/drivers/staging/android/fwdata.c +++ b/drivers/staging/android/fwdata.c @@ -26,6 +26,8 @@ struct android_fwdata_state { struct kobject *fstab_kobj; struct kobject *system_kobj; struct kobject *vendor_kobj; + struct kobject *product_kobj; + struct kobject *odm_kobj; }; static struct android_fwdata_state state; @@ -55,6 +57,10 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, prefix = "android.fstab.system"; } else if (kobj == state.vendor_kobj) { prefix = "android.fstab.vendor"; + } else if (kobj == state.product_kobj) { + prefix = "android.fstab.product"; + } else if (kobj == state.odm_kobj) { + prefix = "android.fstab.odm"; } else { pr_err("%s: Unexpected folder\n", __func__); return -EINVAL; @@ -87,6 +93,8 @@ static DT_SIMPLE_ATTR(vbmeta, compatible); static DT_SIMPLE_ATTR(fstab, compatible); static DT_SIMPLE_ATTR(system, compatible); static DT_SIMPLE_ATTR(vendor, compatible); +static DT_SIMPLE_ATTR(product, compatible); +static DT_SIMPLE_ATTR(odm, compatible); static DT_SIMPLE_ATTR(vbmeta, parts); @@ -110,6 +118,16 @@ static DT_SIMPLE_ATTR(vendor, type); static DT_SIMPLE_ATTR(vendor, mnt_flags); static DT_SIMPLE_ATTR(vendor, fsmgr_flags); +static DT_SIMPLE_ATTR(product, dev); +static DT_SIMPLE_ATTR(product, type); +static DT_SIMPLE_ATTR(product, mnt_flags); +static DT_SIMPLE_ATTR(product, fsmgr_flags); + +static DT_SIMPLE_ATTR(odm, dev); +static DT_SIMPLE_ATTR(odm, type); +static DT_SIMPLE_ATTR(odm, mnt_flags); +static DT_SIMPLE_ATTR(odm, fsmgr_flags); + static struct attribute *system_attrs[] = { &system_compatible_attr.attr, &system_dev_attr.attr, @@ -136,6 +154,32 @@ static struct attribute_group vendor_group = { .attrs = vendor_attrs, }; +static struct attribute *product_attrs[] = { + &product_compatible_attr.attr, + &product_dev_attr.attr, + &product_type_attr.attr, + &product_mnt_flags_attr.attr, + &product_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group product_group = { + .attrs = product_attrs, +}; + +static struct attribute *odm_attrs[] = { + &odm_compatible_attr.attr, + &odm_dev_attr.attr, + &odm_type_attr.attr, + &odm_mnt_flags_attr.attr, + &odm_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group odm_group = { + .attrs = odm_attrs, +}; + static struct kobject *create_folder(struct kobject *parent, const char *name) { struct kobject *kobj; @@ -219,6 +263,16 @@ static void clean_up(void) remove_folder_with_files(state.vendor_kobj, &vendor_group); state.vendor_kobj = NULL; } + if (state.product_kobj) { + /* Delete /properties/android/fstab/product/ */ + remove_folder_with_files(state.product_kobj, &product_group); + state.product_kobj = NULL; + } + if (state.odm_kobj) { + /* Delete /properties/android/fstab/odm/ */ + remove_folder_with_files(state.odm_kobj, &odm_group); + state.odm_kobj = NULL; + } if (state.system_kobj) { /* Delete /properties/android/fstab/system/ */ remove_folder_with_files(state.system_kobj, &system_group); @@ -300,6 +354,22 @@ static int android_fwdata_probe(struct platform_device *pdev) if (!state.vendor_kobj) goto out; } + if (device_property_present(state.dev, "android.fstab.product.dev")) { + /* Firmware contains fstab config for early mount of /product */ + state.product_kobj = create_folder_with_files(state.fstab_kobj, + "product", + &product_group); + if (!state.product_kobj) + goto out; + } + if (device_property_present(state.dev, "android.fstab.odm.dev")) { + /* Firmware contains fstab config for early mount of /odm */ + state.odm_kobj = create_folder_with_files(state.fstab_kobj, + "odm", + &odm_group); + if (!state.odm_kobj) + goto out; + } return 0; out: From e5a085dbd341277f28fe6c161fc4519bd97f6a70 Mon Sep 17 00:00:00 2001 From: "Yang, Dong" Date: Tue, 23 Oct 2018 16:22:43 +0800 Subject: [PATCH 0782/1276] Allow RenderNode to be used for kms getters. Minigbm relies on this to query if a particular format can be scanned out or not. Change-Id: Ia83295af76e821afdb1aeb07bf2eb20b048e91dd Signed-off-by: Kalyan Kondapally Signed-off-by: Yang, Dong --- drivers/gpu/drm/drm_ioctl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index ea10e9a26aad..248dfa53fce1 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -557,7 +557,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_UNLOCKED | DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -628,10 +628,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED), @@ -640,7 +640,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_connector_property_set_ioctl, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_UNLOCKED), @@ -652,7 +652,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_UNLOCKED), From 7a38b61d49195288d340361047134d6fd5f176ce Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:51 +0300 Subject: [PATCH 0783/1276] stm class: Rework policy node fallback Currently, if no matching policy node can be found for a trace source, we'll try to use "default" policy node, then, if that doesn't exist, we'll pick the first node, in order of creation. If that also fails, we'll allocate M/C range from the beginning of the device's M/C range. This makes it difficult to know which node (if any) was used in any particular case. In order to make things more deterministic, the new order is as follows: * if they supply ID string, use that and nothing else, * if they are a task, use their task name (comm), * use "default", if it exists, * return failure, to let them know there is no suitable rule. This should provide enough convenience with the "default" catch-all node, while not leaving *everything* to chance. As a side effect, this relaxes the requirement of using ioctl() for identification with the possibility of using task names as policy nodes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 85 ++++++++++++++++++++-------------- drivers/hwtracing/stm/policy.c | 12 ++--- drivers/hwtracing/stm/stm.h | 2 - 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 10bcb5d73f90..17198e79df3e 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -293,15 +293,15 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, if (width > stm->data->sw_nchannels) return -EINVAL; - if (policy_node) { - stp_policy_node_get_ranges(policy_node, - &midx, &mend, &cidx, &cend); - } else { - midx = stm->data->sw_start; - cidx = 0; - mend = stm->data->sw_end; - cend = stm->data->sw_nchannels - 1; - } + /* We no longer accept policy_node==NULL here */ + if (WARN_ON_ONCE(!policy_node)) + return -EINVAL; + + /* + * Also, the caller holds reference to policy_node, so it won't + * disappear on us. + */ + stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend); spin_lock(&stm->mc_lock); spin_lock(&output->lock); @@ -405,19 +405,30 @@ static int stm_char_release(struct inode *inode, struct file *file) return 0; } -static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) +static int +stm_assign_first_policy(struct stm_device *stm, struct stm_output *output, + char **ids, unsigned int width) { - struct stm_device *stm = stmf->stm; - int ret; + struct stp_policy_node *pn; + int err, n; - stmf->policy_node = stp_policy_node_lookup(stm, id); + /* + * On success, stp_policy_node_lookup() will return holding the + * configfs subsystem mutex, which is then released in + * stp_policy_node_put(). This allows the pdrv->output_open() in + * stm_output_assign() to serialize against the attribute accessors. + */ + for (n = 0, pn = NULL; ids[n] && !pn; n++) + pn = stp_policy_node_lookup(stm, ids[n]); - ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output); + if (!pn) + return -EINVAL; - if (stmf->policy_node) - stp_policy_node_put(stmf->policy_node); + err = stm_output_assign(stm, width, pn, output); - return ret; + stp_policy_node_put(pn); + + return err; } static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, @@ -455,16 +466,21 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, count = PAGE_SIZE - 1; /* - * if no m/c have been assigned to this writer up to this - * point, use "default" policy entry + * If no m/c have been assigned to this writer up to this + * point, try to use the task name and "default" policy entries. */ if (!stmf->output.nr_chans) { - err = stm_file_assign(stmf, "default", 1); + char comm[sizeof(current->comm)]; + char *ids[] = { comm, "default", NULL }; + + get_task_comm(comm, current); + + err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1); /* * EBUSY means that somebody else just assigned this * output, which is just fine for write() */ - if (err && err != -EBUSY) + if (err) return err; } @@ -550,6 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) { struct stm_device *stm = stmf->stm; struct stp_policy_id *id; + char *ids[] = { NULL, NULL }; int ret = -EINVAL; u32 size; @@ -582,7 +599,9 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) id->width > PAGE_SIZE / stm->data->sw_mmiosz) goto err_free; - ret = stm_file_assign(stmf, id->id, id->width); + ids[0] = id->id; + ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids, + id->width); if (ret) goto err_free; @@ -818,8 +837,8 @@ EXPORT_SYMBOL_GPL(stm_unregister_device); static int stm_source_link_add(struct stm_source_device *src, struct stm_device *stm) { - char *id; - int err; + char *ids[] = { NULL, "default", NULL }; + int err = -ENOMEM; mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); @@ -833,19 +852,13 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&stm->link_lock); mutex_unlock(&stm->link_mutex); - id = kstrdup(src->data->name, GFP_KERNEL); - if (id) { - src->policy_node = - stp_policy_node_lookup(stm, id); - - kfree(id); - } - - err = stm_output_assign(stm, src->data->nr_chans, - src->policy_node, &src->output); + ids[0] = kstrdup(src->data->name, GFP_KERNEL); + if (!ids[0]) + goto fail_detach; - if (src->policy_node) - stp_policy_node_put(src->policy_node); + err = stm_assign_first_policy(stm, &src->output, ids, + src->data->nr_chans); + kfree(ids[0]); if (err) goto fail_detach; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 3fd07e275b34..15d35d891643 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -392,7 +392,7 @@ static struct configfs_subsystem stp_policy_subsys = { static struct stp_policy_node * __stp_policy_node_lookup(struct stp_policy *policy, char *s) { - struct stp_policy_node *policy_node, *ret; + struct stp_policy_node *policy_node, *ret = NULL; struct list_head *head = &policy->group.cg_children; struct config_item *item; char *start, *end = s; @@ -400,10 +400,6 @@ __stp_policy_node_lookup(struct stp_policy *policy, char *s) if (list_empty(head)) return NULL; - /* return the first entry if everything else fails */ - item = list_entry(head->next, struct config_item, ci_entry); - ret = to_stp_policy_node(item); - next: for (;;) { start = strsep(&end, "/"); @@ -449,13 +445,17 @@ stp_policy_node_lookup(struct stm_device *stm, char *s) if (policy_node) config_item_get(&policy_node->group.cg_item); - mutex_unlock(&stp_policy_subsys.su_mutex); + else + mutex_unlock(&stp_policy_subsys.su_mutex); return policy_node; } void stp_policy_node_put(struct stp_policy_node *policy_node) { + lockdep_assert_held(&stp_policy_subsys.su_mutex); + + mutex_unlock(&stp_policy_subsys.su_mutex); config_item_put(&policy_node->group.cg_item); } diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 923571adc6f4..e5df08ae59cf 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -57,7 +57,6 @@ struct stm_output { struct stm_file { struct stm_device *stm; - struct stp_policy_node *policy_node; struct stm_output output; }; @@ -71,7 +70,6 @@ struct stm_source_device { struct stm_device __rcu *link; struct list_head link_entry; /* one output per stm_source device */ - struct stp_policy_node *policy_node; struct stm_output output; }; From 9d501ea07311aff06227fd0f1cda2e9d8018a5a9 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:52 +0300 Subject: [PATCH 0784/1276] stm class: Clarify configfs root type/operations names The current naming of stp-policy root type and group ops is confusing, rename them for better readability. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 15d35d891643..530448bd5a34 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -311,7 +311,7 @@ static const struct config_item_type stp_policy_type = { }; static struct config_group * -stp_policies_make(struct config_group *group, const char *name) +stp_policy_make(struct config_group *group, const char *name) { struct config_group *ret; struct stm_device *stm; @@ -368,12 +368,12 @@ stp_policies_make(struct config_group *group, const char *name) return ret; } -static struct configfs_group_operations stp_policies_group_ops = { - .make_group = stp_policies_make, +static struct configfs_group_operations stp_policy_root_group_ops = { + .make_group = stp_policy_make, }; -static const struct config_item_type stp_policies_type = { - .ct_group_ops = &stp_policies_group_ops, +static const struct config_item_type stp_policy_root_type = { + .ct_group_ops = &stp_policy_root_group_ops, .ct_owner = THIS_MODULE, }; @@ -381,7 +381,7 @@ static struct configfs_subsystem stp_policy_subsys = { .su_group = { .cg_item = { .ci_namebuf = "stp-policy", - .ci_type = &stp_policies_type, + .ci_type = &stp_policy_root_type, }, }, }; From bd04da48b3d328669e3973e14aa6ffcbaca533bd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:53 +0300 Subject: [PATCH 0785/1276] stm class: Clean up stp_configfs_init Minor code shortening, no functional changes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 530448bd5a34..a505f055f464 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -461,13 +461,9 @@ void stp_policy_node_put(struct stp_policy_node *policy_node) int __init stp_configfs_init(void) { - int err; - config_group_init(&stp_policy_subsys.su_group); mutex_init(&stp_policy_subsys.su_mutex); - err = configfs_register_subsystem(&stp_policy_subsys); - - return err; + return configfs_register_subsystem(&stp_policy_subsys); } void __exit stp_configfs_exit(void) From 6b92142d9d0e61aab7e93d40ad21262c9e5cde75 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:54 +0300 Subject: [PATCH 0786/1276] stm class: Introduce framing protocol drivers At the moment, the stm class applies a certain STP framing pattern to the data as it is written to the underlying STM device. In order to allow different framing patterns (aka protocols), this patch introduces the concept of STP protocol drivers, defines data structures and APIs for the protocol drivers to use. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 140 +++++++++++++++++++++++++++++++ drivers/hwtracing/stm/policy.c | 145 ++++++++++++++++++++++++++++++--- drivers/hwtracing/stm/stm.h | 51 ++++++++++-- 3 files changed, 318 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 17198e79df3e..915af2541dcd 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -316,11 +316,26 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, output->master = midx; output->channel = cidx; output->nr_chans = width; + if (stm->pdrv->output_open) { + void *priv = stp_policy_node_priv(policy_node); + + if (WARN_ON_ONCE(!priv)) + goto unlock; + + /* configfs subsys mutex is held by the caller */ + ret = stm->pdrv->output_open(priv, output); + if (ret) + goto unlock; + } + stm_output_claim(stm, output); dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width); ret = 0; unlock: + if (ret) + output->nr_chans = 0; + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); @@ -333,6 +348,8 @@ static void stm_output_free(struct stm_device *stm, struct stm_output *output) spin_lock(&output->lock); if (output->nr_chans) stm_output_disclaim(stm, output); + if (stm->pdrv && stm->pdrv->output_close) + stm->pdrv->output_close(output); spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); } @@ -349,6 +366,127 @@ static int major_match(struct device *dev, const void *data) return MAJOR(dev->devt) == major; } +/* + * Framing protocol management + * Modules can implement STM protocol drivers and (un-)register them + * with the STM class framework. + */ +static struct list_head stm_pdrv_head; +static struct mutex stm_pdrv_mutex; + +struct stm_pdrv_entry { + struct list_head entry; + const struct stm_protocol_driver *pdrv; + const struct config_item_type *node_type; +}; + +static const struct stm_pdrv_entry * +__stm_lookup_protocol(const char *name) +{ + struct stm_pdrv_entry *pe; + + /* + * If no name is given (NULL or ""), fall back to "p_basic". + */ + if (!name || !*name) + name = "p_basic"; + + list_for_each_entry(pe, &stm_pdrv_head, entry) { + if (!strcmp(name, pe->pdrv->name)) + return pe; + } + + return NULL; +} + +int stm_register_protocol(const struct stm_protocol_driver *pdrv) +{ + struct stm_pdrv_entry *pe = NULL; + int ret = -ENOMEM; + + mutex_lock(&stm_pdrv_mutex); + + if (__stm_lookup_protocol(pdrv->name)) { + ret = -EEXIST; + goto unlock; + } + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + goto unlock; + + if (pdrv->policy_attr) { + pe->node_type = get_policy_node_type(pdrv->policy_attr); + if (!pe->node_type) + goto unlock; + } + + list_add_tail(&pe->entry, &stm_pdrv_head); + pe->pdrv = pdrv; + + ret = 0; +unlock: + mutex_unlock(&stm_pdrv_mutex); + + if (ret) + kfree(pe); + + return ret; +} +EXPORT_SYMBOL_GPL(stm_register_protocol); + +void stm_unregister_protocol(const struct stm_protocol_driver *pdrv) +{ + struct stm_pdrv_entry *pe, *iter; + + mutex_lock(&stm_pdrv_mutex); + + list_for_each_entry_safe(pe, iter, &stm_pdrv_head, entry) { + if (pe->pdrv == pdrv) { + list_del(&pe->entry); + + if (pe->node_type) { + kfree(pe->node_type->ct_attrs); + kfree(pe->node_type); + } + kfree(pe); + break; + } + } + + mutex_unlock(&stm_pdrv_mutex); +} +EXPORT_SYMBOL_GPL(stm_unregister_protocol); + +static bool stm_get_protocol(const struct stm_protocol_driver *pdrv) +{ + return try_module_get(pdrv->owner); +} + +void stm_put_protocol(const struct stm_protocol_driver *pdrv) +{ + module_put(pdrv->owner); +} + +int stm_lookup_protocol(const char *name, + const struct stm_protocol_driver **pdrv, + const struct config_item_type **node_type) +{ + const struct stm_pdrv_entry *pe; + + mutex_lock(&stm_pdrv_mutex); + + pe = __stm_lookup_protocol(name); + if (pe && pe->pdrv && stm_get_protocol(pe->pdrv)) { + *pdrv = pe->pdrv; + *node_type = pe->node_type; + } + + mutex_unlock(&stm_pdrv_mutex); + + return pe ? 0 : -ENOENT; +} + static int stm_char_open(struct inode *inode, struct file *file) { struct stm_file *stmf; @@ -1176,6 +1314,8 @@ static int __init stm_core_init(void) goto err_src; init_srcu_struct(&stm_source_srcu); + INIT_LIST_HEAD(&stm_pdrv_head); + mutex_init(&stm_pdrv_mutex); stm_core_up++; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index a505f055f464..8bc90432ae69 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -33,8 +33,18 @@ struct stp_policy_node { unsigned int last_master; unsigned int first_channel; unsigned int last_channel; + /* this is the one that's exposed to the attributes */ + unsigned char priv[0]; }; +void *stp_policy_node_priv(struct stp_policy_node *pn) +{ + if (!pn) + return NULL; + + return pn->priv; +} + static struct configfs_subsystem stp_policy_subsys; void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, @@ -68,6 +78,14 @@ to_stp_policy_node(struct config_item *item) NULL; } +void *to_pdrv_policy_node(struct config_item *item) +{ + struct stp_policy_node *node = to_stp_policy_node(item); + + return stp_policy_node_priv(node); +} +EXPORT_SYMBOL_GPL(to_pdrv_policy_node); + static ssize_t stp_policy_node_masters_show(struct config_item *item, char *page) { @@ -163,7 +181,9 @@ stp_policy_node_channels_store(struct config_item *item, const char *page, static void stp_policy_node_release(struct config_item *item) { - kfree(to_stp_policy_node(item)); + struct stp_policy_node *node = to_stp_policy_node(item); + + kfree(node); } static struct configfs_item_operations stp_policy_node_item_ops = { @@ -182,10 +202,61 @@ static struct configfs_attribute *stp_policy_node_attrs[] = { static const struct config_item_type stp_policy_type; static const struct config_item_type stp_policy_node_type; +/* lifted from arch/x86/events/core.c */ +static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) +{ + struct configfs_attribute **new; + int j, i; + + for (j = 0; a[j]; j++) + ; + for (i = 0; b[i]; i++) + j++; + j++; + + new = kmalloc_array(j, sizeof(struct configfs_attribute *), + GFP_KERNEL); + if (!new) + return NULL; + + j = 0; + for (i = 0; a[i]; i++) + new[j++] = a[i]; + for (i = 0; b[i]; i++) + new[j++] = b[i]; + new[j] = NULL; + + return new; +} + +const struct config_item_type * +get_policy_node_type(struct configfs_attribute **attrs) +{ + struct config_item_type *type; + struct configfs_attribute **merged; + + type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), + GFP_KERNEL); + if (!type) + return NULL; + + merged = merge_attr(stp_policy_node_attrs, attrs); + if (!merged) { + kfree(type); + return NULL; + } + + type->ct_attrs = merged; + + return type; +} + static struct config_group * stp_policy_node_make(struct config_group *group, const char *name) { + const struct config_item_type *type = &stp_policy_node_type; struct stp_policy_node *policy_node, *parent_node; + const struct stm_protocol_driver *pdrv; struct stp_policy *policy; if (group->cg_item.ci_type == &stp_policy_type) { @@ -199,12 +270,20 @@ stp_policy_node_make(struct config_group *group, const char *name) if (!policy->stm) return ERR_PTR(-ENODEV); - policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL); + pdrv = policy->stm->pdrv; + policy_node = + kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), + GFP_KERNEL); if (!policy_node) return ERR_PTR(-ENOMEM); - config_group_init_type_name(&policy_node->group, name, - &stp_policy_node_type); + if (pdrv->policy_node_init) + pdrv->policy_node_init((void *)policy_node->priv); + + if (policy->stm->pdrv_node_type) + type = policy->stm->pdrv_node_type; + + config_group_init_type_name(&policy_node->group, name, type); policy_node->policy = policy; @@ -254,8 +333,25 @@ static ssize_t stp_policy_device_show(struct config_item *item, CONFIGFS_ATTR_RO(stp_policy_, device); +static ssize_t stp_policy_protocol_show(struct config_item *item, + char *page) +{ + struct stp_policy *policy = to_stp_policy(item); + ssize_t count; + + count = sprintf(page, "%s\n", + (policy && policy->stm) ? + policy->stm->pdrv->name : + ""); + + return count; +} + +CONFIGFS_ATTR_RO(stp_policy_, protocol); + static struct configfs_attribute *stp_policy_attrs[] = { &stp_policy_attr_device, + &stp_policy_attr_protocol, NULL, }; @@ -276,6 +372,7 @@ void stp_policy_unbind(struct stp_policy *policy) stm->policy = NULL; policy->stm = NULL; + stm_put_protocol(stm->pdrv); stm_put_device(stm); } @@ -313,9 +410,12 @@ static const struct config_item_type stp_policy_type = { static struct config_group * stp_policy_make(struct config_group *group, const char *name) { + const struct config_item_type *pdrv_node_type; + const struct stm_protocol_driver *pdrv; + char *devname, *proto, *p; struct config_group *ret; struct stm_device *stm; - char *devname, *p; + int err; devname = kasprintf(GFP_KERNEL, "%s", name); if (!devname) @@ -326,6 +426,7 @@ stp_policy_make(struct config_group *group, const char *name) * is the name of an existing stm device; may * contain dots; * is an arbitrary string; may not contain dots + * :. */ p = strrchr(devname, '.'); if (!p) { @@ -335,11 +436,29 @@ stp_policy_make(struct config_group *group, const char *name) *p = '\0'; + /* + * look for ":": + * + no protocol suffix: fall back to whatever is available; + * + unknown protocol: fail the whole thing + */ + proto = strrchr(devname, ':'); + if (proto) + *proto++ = '\0'; + stm = stm_find_device(devname); + if (!stm) { + kfree(devname); + return ERR_PTR(-ENODEV); + } + + err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); kfree(devname); - if (!stm) + /* We don't have any protocol drivers yet */ + if (err != -ENOENT) { + stm_put_device(stm); return ERR_PTR(-ENODEV); + } mutex_lock(&stm->policy_mutex); if (stm->policy) { @@ -349,21 +468,27 @@ stp_policy_make(struct config_group *group, const char *name) stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); if (!stm->policy) { - ret = ERR_PTR(-ENOMEM); - goto unlock_policy; + mutex_unlock(&stm->policy_mutex); + stm_put_protocol(pdrv); + stm_put_device(stm); + return ERR_PTR(-ENOMEM); } config_group_init_type_name(&stm->policy->group, name, &stp_policy_type); - stm->policy->stm = stm; + stm->pdrv = pdrv; + stm->pdrv_node_type = pdrv_node_type; + stm->policy->stm = stm; ret = &stm->policy->group; unlock_policy: mutex_unlock(&stm->policy_mutex); - if (IS_ERR(ret)) + if (IS_ERR(ret)) { + stm_put_protocol(stm->pdrv); stm_put_device(stm); + } return ret; } diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index e5df08ae59cf..ed7f3d07fa47 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -10,20 +10,17 @@ #ifndef _STM_STM_H_ #define _STM_STM_H_ +#include + struct stp_policy; struct stp_policy_node; +struct stm_protocol_driver; -struct stp_policy_node * -stp_policy_node_lookup(struct stm_device *stm, char *s); -void stp_policy_node_put(struct stp_policy_node *policy_node); -void stp_policy_unbind(struct stp_policy *policy); - -void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, - unsigned int *mstart, unsigned int *mend, - unsigned int *cstart, unsigned int *cend); int stp_configfs_init(void); void stp_configfs_exit(void); +void *stp_policy_node_priv(struct stp_policy_node *pn); + struct stp_master { unsigned int nr_free; unsigned long chan_map[0]; @@ -40,6 +37,9 @@ struct stm_device { struct mutex link_mutex; spinlock_t link_lock; struct list_head link_list; + /* framing protocol in use */ + const struct stm_protocol_driver *pdrv; + const struct config_item_type *pdrv_node_type; /* master allocation */ spinlock_t mc_lock; struct stp_master *masters[0]; @@ -48,11 +48,24 @@ struct stm_device { #define to_stm_device(_d) \ container_of((_d), struct stm_device, dev) +struct stp_policy_node * +stp_policy_node_lookup(struct stm_device *stm, char *s); +void stp_policy_node_put(struct stp_policy_node *policy_node); +void stp_policy_unbind(struct stp_policy *policy); + +void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, + unsigned int *mstart, unsigned int *mend, + unsigned int *cstart, unsigned int *cend); + +const struct config_item_type * +get_policy_node_type(struct configfs_attribute **attrs); + struct stm_output { spinlock_t lock; unsigned int master; unsigned int channel; unsigned int nr_chans; + void *pdrv_private; }; struct stm_file { @@ -76,4 +89,26 @@ struct stm_source_device { #define to_stm_source_device(_d) \ container_of((_d), struct stm_source_device, dev) +void *to_pdrv_policy_node(struct config_item *item); + +struct stm_protocol_driver { + struct module *owner; + const char *name; + ssize_t (*write)(struct stm_data *data, + struct stm_output *output, unsigned int chan, + const char *buf, size_t count); + void (*policy_node_init)(void *arg); + int (*output_open)(void *priv, struct stm_output *output); + void (*output_close)(struct stm_output *output); + ssize_t priv_sz; + struct configfs_attribute **policy_attr; +}; + +int stm_register_protocol(const struct stm_protocol_driver *pdrv); +void stm_unregister_protocol(const struct stm_protocol_driver *pdrv); +int stm_lookup_protocol(const char *name, + const struct stm_protocol_driver **pdrv, + const struct config_item_type **type); +void stm_put_protocol(const struct stm_protocol_driver *pdrv); + #endif /* _STM_STM_H_ */ From c2a10f4c313c5d617f68f5f18c8297cdb2abeff7 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:55 +0300 Subject: [PATCH 0787/1276] stm class: Add a helper for writing data packets Add a helper to write a sequence of bytes as STP data packets. This is used by protocol drivers to output their metadata, as well as the actual data payload. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 51 +++++++++++++++++++++++++++--------- drivers/hwtracing/stm/stm.h | 3 +++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 915af2541dcd..b789a5f0688e 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -569,27 +569,52 @@ stm_assign_first_policy(struct stm_device *stm, struct stm_output *output, return err; } -static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +/** + * stm_data_write() - send the given payload as data packets + * @data: stm driver's data + * @m: STP master + * @c: STP channel + * @ts_first: timestamp the first packet + * @buf: data payload buffer + * @count: data payload size + */ +ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m, + unsigned int c, bool ts_first, const void *buf, + size_t count) { - unsigned int flags = STP_PACKET_TIMESTAMPED; - const unsigned char *p = buf, nil = 0; - size_t pos; + unsigned int flags = ts_first ? STP_PACKET_TIMESTAMPED : 0; ssize_t sz; + size_t pos; - for (pos = 0, p = buf; count > pos; pos += sz, p += sz) { + for (pos = 0, sz = 0; pos < count; pos += sz) { sz = min_t(unsigned int, count - pos, 8); - sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, - sz, p); - flags = 0; - - if (sz < 0) + sz = data->packet(data, m, c, STP_PACKET_DATA, flags, sz, + &((u8 *)buf)[pos]); + if (sz <= 0) break; + + if (ts_first) { + flags = 0; + ts_first = false; + } } - data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); + return sz < 0 ? sz : pos; +} +EXPORT_SYMBOL_GPL(stm_data_write); + +static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) +{ + const unsigned char nil = 0; + ssize_t sz; + + sz = stm_data_write(data, master, channel, true, buf, count); + if (sz > 0) + data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, + &nil); - return pos; + return sz; } static ssize_t stm_char_write(struct file *file, const char __user *buf, diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index ed7f3d07fa47..3569439d53bb 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -110,5 +110,8 @@ int stm_lookup_protocol(const char *name, const struct stm_protocol_driver **pdrv, const struct config_item_type **type); void stm_put_protocol(const struct stm_protocol_driver *pdrv); +ssize_t stm_data_write(struct stm_data *data, unsigned int m, + unsigned int c, bool ts_first, const void *buf, + size_t count); #endif /* _STM_STM_H_ */ From 8ba3ac5b680bd6ad148034fc4bd93cc5813f5eb3 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:56 +0300 Subject: [PATCH 0788/1276] stm class: Factor out default framing protocol The STP framing pattern that the stm class implicitly applies to the data payload is, in fact, a protocol. This patch moves the relevant code out of the stm core into its own driver module. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 15 +++++++++++ drivers/hwtracing/stm/Makefile | 4 +++ drivers/hwtracing/stm/p_basic.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 drivers/hwtracing/stm/p_basic.c diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 723e2d90083d..262e7891fb97 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -11,6 +11,21 @@ config STM if STM +config STM_PROTO_BASIC + tristate "Basic STM framing protocol driver" + default CONFIG_STM + help + This is a simple framing protocol for sending data over STM + devices. This was the protocol that the STM framework used + exclusively until the MIPI SyS-T support was added. Use this + driver for compatibility with your existing STM setup. + + The receiving side only needs to be able to decode the MIPI + STP protocol in order to extract the data. + + If you want to be able to use the basic protocol or want the + backwards compatibility for your existing setup, say Y. + config STM_DUMMY tristate "Dummy STM driver" help diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index effc19e5190f..1571de66eca4 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -3,6 +3,10 @@ obj-$(CONFIG_STM) += stm_core.o stm_core-y := core.o policy.o +obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o + +stm_p_basic-y := p_basic.o + obj-$(CONFIG_STM_DUMMY) += dummy_stm.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o diff --git a/drivers/hwtracing/stm/p_basic.c b/drivers/hwtracing/stm/p_basic.c new file mode 100644 index 000000000000..8980a6a5fd6c --- /dev/null +++ b/drivers/hwtracing/stm/p_basic.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Basic framing protocol for STM devices. + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include "stm.h" + +static ssize_t basic_write(struct stm_data *data, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) +{ + unsigned int c = output->channel + chan; + unsigned int m = output->master; + const unsigned char nil = 0; + ssize_t sz; + + sz = stm_data_write(data, m, c, true, buf, count); + if (sz > 0) + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sz; +} + +static const struct stm_protocol_driver basic_pdrv = { + .owner = THIS_MODULE, + .name = "p_basic", + .write = basic_write, +}; + +static int basic_stm_init(void) +{ + return stm_register_protocol(&basic_pdrv); +} + +static void basic_stm_exit(void) +{ + stm_unregister_protocol(&basic_pdrv); +} + +module_init(basic_stm_init); +module_exit(basic_stm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Basic STM framing protocol driver"); +MODULE_AUTHOR("Alexander Shishkin "); From 4d2177243c0146dce2b735edfb1922ea97b18e84 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:57 +0300 Subject: [PATCH 0789/1276] stm class: Switch over to the protocol driver Now that the default framing protocol is factored out into its own driver, switch over to using the driver for writing data. To that end, make the policy code require a valid protocol name (or absence thereof, which is equivalent to "p_basic"). Also, to make transition easier, make stm class request "p_basic" module at initialization time. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 34 ++++++++++++++++++++-------------- drivers/hwtracing/stm/policy.c | 3 +-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b789a5f0688e..93ce3aa740a9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -603,18 +603,21 @@ ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m, } EXPORT_SYMBOL_GPL(stm_data_write); -static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +static ssize_t notrace +stm_write(struct stm_device *stm, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) { - const unsigned char nil = 0; - ssize_t sz; + int err; + + /* stm->pdrv is serialized against policy_mutex */ + if (!stm->pdrv) + return -ENODEV; - sz = stm_data_write(data, master, channel, true, buf, count); - if (sz > 0) - data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, - &nil); + err = stm->pdrv->write(stm->data, output, chan, buf, count); + if (err < 0) + return err; - return sz; + return err; } static ssize_t stm_char_write(struct file *file, const char __user *buf, @@ -659,8 +662,7 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, pm_runtime_get_sync(&stm->dev); - count = stm_write(stm->data, stmf->output.master, stmf->output.channel, - kbuf, count); + count = stm_write(stm, &stmf->output, 0, kbuf, count); pm_runtime_mark_last_busy(&stm->dev); pm_runtime_put_autosuspend(&stm->dev); @@ -1310,9 +1312,7 @@ int notrace stm_source_write(struct stm_source_data *data, stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) - count = stm_write(stm->data, src->output.master, - src->output.channel + chan, - buf, count); + count = stm_write(stm, &src->output, chan, buf, count); else count = -ENODEV; @@ -1342,6 +1342,12 @@ static int __init stm_core_init(void) INIT_LIST_HEAD(&stm_pdrv_head); mutex_init(&stm_pdrv_mutex); + /* + * So as to not confuse existing users with a requirement + * to load yet another module, do it here. + */ + if (IS_ENABLED(CONFIG_STM_PROTO_BASIC)) + (void)request_module_nowait("stm_p_basic"); stm_core_up++; return 0; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 8bc90432ae69..5e51bed0787b 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -454,8 +454,7 @@ stp_policy_make(struct config_group *group, const char *name) err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); kfree(devname); - /* We don't have any protocol drivers yet */ - if (err != -ENOENT) { + if (err) { stm_put_device(stm); return ERR_PTR(-ENODEV); } From 680f20c9b7da1327283627986b725018b0e1d972 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:58 +0300 Subject: [PATCH 0790/1276] stm class: Add MIPI SyS-T protocol support This adds support for MIPI SyS-T protocol as specified in an open standard [1]. In addition to marking message boundaries, it also supports tagging messages with the source UUID, to provide better distinction between trace sources, including payload length and timestamp in the message's metadata. This driver adds attributes to STP policy nodes to control/configure these metadata features. [1] https://www.mipi.org/specifications/sys-t Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 14 ++ drivers/hwtracing/stm/Makefile | 2 + drivers/hwtracing/stm/p_sys-t.c | 302 ++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 drivers/hwtracing/stm/p_sys-t.c diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 262e7891fb97..752dd66742bf 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -26,6 +26,20 @@ config STM_PROTO_BASIC If you want to be able to use the basic protocol or want the backwards compatibility for your existing setup, say Y. +config STM_PROTO_SYS_T + tristate "MIPI SyS-T STM framing protocol driver" + default CONFIG_STM + help + This is an implementation of MIPI SyS-T protocol to be used + over the STP transport. In addition to the data payload, it + also carries additional metadata for time correlation, better + means of trace source identification, etc. + + The receiving side must be able to decode this protocol in + addition to the MIPI STP, in order to extract the data. + + If you don't know what this is, say N. + config STM_DUMMY tristate "Dummy STM driver" help diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index 1571de66eca4..1692fcd29277 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -4,8 +4,10 @@ obj-$(CONFIG_STM) += stm_core.o stm_core-y := core.o policy.o obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o +obj-$(CONFIG_STM_PROTO_SYS_T) += stm_p_sys-t.o stm_p_basic-y := p_basic.o +stm_p_sys-t-y := p_sys-t.o obj-$(CONFIG_STM_DUMMY) += dummy_stm.o diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c new file mode 100644 index 000000000000..ffeb057b777c --- /dev/null +++ b/drivers/hwtracing/stm/p_sys-t.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MIPI SyS-T framing protocol for STM devices. + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include "stm.h" + +enum sys_t_message_type { + MIPI_SYST_TYPE_BUILD = 0, + MIPI_SYST_TYPE_SHORT32, + MIPI_SYST_TYPE_STRING, + MIPI_SYST_TYPE_CATALOG, + MIPI_SYST_TYPE_RAW = 6, + MIPI_SYST_TYPE_SHORT64, + MIPI_SYST_TYPE_CLOCK, +}; + +enum sys_t_message_severity { + MIPI_SYST_SEVERITY_MAX = 0, + MIPI_SYST_SEVERITY_FATAL, + MIPI_SYST_SEVERITY_ERROR, + MIPI_SYST_SEVERITY_WARNING, + MIPI_SYST_SEVERITY_INFO, + MIPI_SYST_SEVERITY_USER1, + MIPI_SYST_SEVERITY_USER2, + MIPI_SYST_SEVERITY_DEBUG, +}; + +enum sys_t_message_build_subtype { + MIPI_SYST_BUILD_ID_COMPACT32 = 0, + MIPI_SYST_BUILD_ID_COMPACT64, + MIPI_SYST_BUILD_ID_LONG, +}; + +enum sys_t_message_clock_subtype { + MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1, +}; + +enum sys_t_message_string_subtype { + MIPI_SYST_STRING_GENERIC = 1, + MIPI_SYST_STRING_FUNCTIONENTER, + MIPI_SYST_STRING_FUNCTIONEXIT, + MIPI_SYST_STRING_INVALIDPARAM = 5, + MIPI_SYST_STRING_ASSERT = 7, + MIPI_SYST_STRING_PRINTF_32 = 11, + MIPI_SYST_STRING_PRINTF_64 = 12, +}; + +#define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t)) +#define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4) +#define MIPI_SYST_OPT_LOC BIT(8) +#define MIPI_SYST_OPT_LEN BIT(9) +#define MIPI_SYST_OPT_CHK BIT(10) +#define MIPI_SYST_OPT_TS BIT(11) +#define MIPI_SYST_UNIT(u) ((u32)(u) << 12) +#define MIPI_SYST_ORIGIN(o) ((u32)(o) << 16) +#define MIPI_SYST_OPT_GUID BIT(23) +#define MIPI_SYST_SUBTYPE(s) ((u32)(MIPI_SYST_ ## s) << 24) +#define MIPI_SYST_UNITLARGE(u) (MIPI_SYST_UNIT(u & 0xf) | \ + MIPI_SYST_ORIGIN(u >> 4)) +#define MIPI_SYST_TYPES(t, s) (MIPI_SYST_TYPE(t) | \ + MIPI_SYST_SUBTYPE(t ## _ ## s)) + +#define DATA_HEADER (MIPI_SYST_TYPES(STRING, GENERIC) | \ + MIPI_SYST_SEVERITY(INFO) | \ + MIPI_SYST_OPT_GUID) + +struct sys_t_policy_node { + uuid_t uuid; + bool do_len; + unsigned long ts_interval; +}; + +struct sys_t_output { + struct sys_t_policy_node node; + unsigned long ts_jiffies; +}; + +static void sys_t_policy_node_init(void *priv) +{ + struct sys_t_policy_node *pn = priv; + + generate_random_uuid(pn->uuid.b); +} + +static int sys_t_output_open(void *priv, struct stm_output *output) +{ + struct sys_t_policy_node *pn = priv; + struct sys_t_output *opriv; + + opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC); + if (!opriv) + return -ENOMEM; + + memcpy(&opriv->node, pn, sizeof(opriv->node)); + output->pdrv_private = opriv; + + return 0; +} + +static void sys_t_output_close(struct stm_output *output) +{ + kfree(output->pdrv_private); +} + +static ssize_t sys_t_policy_uuid_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%pU\n", &pn->uuid); +} + +static ssize_t +sys_t_policy_uuid_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + int ret; + + mutex_lock(mutexp); + ret = uuid_parse(page, &pn->uuid); + mutex_unlock(mutexp); + + return ret < 0 ? ret : count; +} + +CONFIGFS_ATTR(sys_t_policy_, uuid); + +static ssize_t sys_t_policy_do_len_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%d\n", pn->do_len); +} + +static ssize_t +sys_t_policy_do_len_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + int ret; + + mutex_lock(mutexp); + ret = kstrtobool(page, &pn->do_len); + mutex_unlock(mutexp); + + return ret ? ret : count; +} + +CONFIGFS_ATTR(sys_t_policy_, do_len); + +static ssize_t sys_t_policy_ts_interval_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval)); +} + +static ssize_t +sys_t_policy_ts_interval_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + unsigned int ms; + int ret; + + mutex_lock(mutexp); + ret = kstrtouint(page, 10, &ms); + mutex_unlock(mutexp); + + if (!ret) { + pn->ts_interval = msecs_to_jiffies(ms); + return count; + } + + return ret; +} + +CONFIGFS_ATTR(sys_t_policy_, ts_interval); + +static struct configfs_attribute *sys_t_policy_attrs[] = { + &sys_t_policy_attr_uuid, + &sys_t_policy_attr_do_len, + &sys_t_policy_attr_ts_interval, + NULL, +}; + +static inline bool sys_t_need_ts(struct sys_t_output *op) +{ + if (op->node.ts_interval && + time_after(op->ts_jiffies + op->node.ts_interval, jiffies)) { + op->ts_jiffies = jiffies; + + return true; + } + + return false; +} + +static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) +{ + struct sys_t_output *op = output->pdrv_private; + unsigned int c = output->channel + chan; + unsigned int m = output->master; + const unsigned char nil = 0; + u32 header = DATA_HEADER; + ssize_t sz; + + /* We require an existing policy node to proceed */ + if (!op) + return -EINVAL; + + if (op->node.do_len) + header |= MIPI_SYST_OPT_LEN; + if (sys_t_need_ts(op)) + header |= MIPI_SYST_OPT_TS; + + /* + * STP framing rules for SyS-T frames: + * * the first packet of the SyS-T frame is timestamped; + * * the last packet is a FLAG. + */ + /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */ + /* HEADER */ + sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, + 4, (u8 *)&header); + if (sz <= 0) + return sz; + + /* GUID */ + sz = stm_data_write(data, m, c, false, op->node.uuid.b, UUID_SIZE); + if (sz <= 0) + return sz; + + /* [LENGTH] */ + if (op->node.do_len) { + u16 length = count; + + sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2, + (u8 *)&length); + if (sz <= 0) + return sz; + } + + /* [TIMESTAMP] */ + if (header & MIPI_SYST_OPT_TS) { + u64 ts = ktime_get_real_ns(); + + sz = stm_data_write(data, m, c, false, &ts, sizeof(ts)); + if (sz <= 0) + return sz; + } + + /* DATA */ + sz = stm_data_write(data, m, c, false, buf, count); + if (sz > 0) + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sz; +} + +static const struct stm_protocol_driver sys_t_pdrv = { + .owner = THIS_MODULE, + .name = "p_sys-t", + .priv_sz = sizeof(struct sys_t_policy_node), + .write = sys_t_write, + .policy_attr = sys_t_policy_attrs, + .policy_node_init = sys_t_policy_node_init, + .output_open = sys_t_output_open, + .output_close = sys_t_output_close, +}; + +static int sys_t_stm_init(void) +{ + return stm_register_protocol(&sys_t_pdrv); +} + +static void sys_t_stm_exit(void) +{ + stm_unregister_protocol(&sys_t_pdrv); +} + +module_init(sys_t_stm_init); +module_exit(sys_t_stm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver"); +MODULE_AUTHOR("Alexander Shishkin "); From 929ee4c05258a8890abcab2f986ba57c1d2a690c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:59 +0300 Subject: [PATCH 0791/1276] stm class: p_sys-t: Add support for CLOCKSYNC packets This adds support for CLOCKSYNC SyS-T packets, that establish correlation between the transport clock (STP timestamps) and SyS-T timestamps. These packets are sent periodically to allow the decoder to keep both time sources in sync. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/p_sys-t.c | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c index ffeb057b777c..b178a5495b67 100644 --- a/drivers/hwtracing/stm/p_sys-t.c +++ b/drivers/hwtracing/stm/p_sys-t.c @@ -72,15 +72,20 @@ enum sys_t_message_string_subtype { MIPI_SYST_SEVERITY(INFO) | \ MIPI_SYST_OPT_GUID) +#define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \ + MIPI_SYST_SEVERITY(MAX)) + struct sys_t_policy_node { uuid_t uuid; bool do_len; unsigned long ts_interval; + unsigned long clocksync_interval; }; struct sys_t_output { struct sys_t_policy_node node; unsigned long ts_jiffies; + unsigned long clocksync_jiffies; }; static void sys_t_policy_node_init(void *priv) @@ -191,10 +196,42 @@ sys_t_policy_ts_interval_store(struct config_item *item, const char *page, CONFIGFS_ATTR(sys_t_policy_, ts_interval); +static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval)); +} + +static ssize_t +sys_t_policy_clocksync_interval_store(struct config_item *item, + const char *page, size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + unsigned int ms; + int ret; + + mutex_lock(mutexp); + ret = kstrtouint(page, 10, &ms); + mutex_unlock(mutexp); + + if (!ret) { + pn->clocksync_interval = msecs_to_jiffies(ms); + return count; + } + + return ret; +} + +CONFIGFS_ATTR(sys_t_policy_, clocksync_interval); + static struct configfs_attribute *sys_t_policy_attrs[] = { &sys_t_policy_attr_uuid, &sys_t_policy_attr_do_len, &sys_t_policy_attr_ts_interval, + &sys_t_policy_attr_clocksync_interval, NULL, }; @@ -210,6 +247,43 @@ static inline bool sys_t_need_ts(struct sys_t_output *op) return false; } +static bool sys_t_need_clock_sync(struct sys_t_output *op) +{ + if (op->node.clocksync_interval && + time_after(op->clocksync_jiffies + op->node.clocksync_interval, + jiffies)) { + op->clocksync_jiffies = jiffies; + + return true; + } + + return false; +} + +static ssize_t +sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c) +{ + u32 header = CLOCK_SYNC_HEADER; + const unsigned char nil = 0; + u64 payload[2]; /* Clock value and frequency */ + ssize_t sz; + + sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, + 4, (u8 *)&header); + if (sz <= 0) + return sz; + + payload[0] = ktime_get_real_ns(); + payload[1] = NSEC_PER_SEC; + sz = stm_data_write(data, m, c, false, &payload, sizeof(payload)); + if (sz <= 0) + return sz; + + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sizeof(header) + sizeof(payload); +} + static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, unsigned int chan, const char *buf, size_t count) { @@ -224,6 +298,12 @@ static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, if (!op) return -EINVAL; + if (sys_t_need_clock_sync(op)) { + sz = sys_t_clock_sync(data, m, c); + if (sz <= 0) + return sz; + } + if (op->node.do_len) header |= MIPI_SYST_OPT_LEN; if (sys_t_need_ts(op)) From 7db2d8b904ace90bd6414eb6c38fa8d50240a80a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:00 +0300 Subject: [PATCH 0792/1276] stm class: p_sys-t: Document the configfs interface This adds ABI documentation for the new configfs attributes that come with the MIPI SyS-T protocol driver. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/configfs-stp-policy-p_sys-t | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-stp-policy-p_sys-t diff --git a/Documentation/ABI/testing/configfs-stp-policy-p_sys-t b/Documentation/ABI/testing/configfs-stp-policy-p_sys-t new file mode 100644 index 000000000000..b290d1c00dcf --- /dev/null +++ b/Documentation/ABI/testing/configfs-stp-policy-p_sys-t @@ -0,0 +1,41 @@ +What: /config/stp-policy/:p_sys-t.//uuid +Date: June 2018 +KernelVersion: 4.19 +Description: + UUID source identifier string, RW. + Default value is randomly generated at the mkdir time. + Data coming from trace sources that use this will be + tagged with this UUID in the MIPI SyS-T packet stream, to + allow the decoder to discern between different sources + within the same master/channel range, and identify the + higher level decoders that may be needed for each source. + +What: /config/stp-policy/:p_sys-t.//do_len +Date: June 2018 +KernelVersion: 4.19 +Description: + Include payload length in the MIPI SyS-T header, boolean. + If enabled, the SyS-T protocol encoder will include payload + length in each packet's metadata. This is normally redundant + if the underlying transport protocol supports marking message + boundaries (which STP does), so this is off by default. + +What: /config/stp-policy/:p_sys-t.//ts_interval +Date: June 2018 +KernelVersion: 4.19 +Description: + Time interval in milliseconds. Include a timestamp in the + MIPI SyS-T packet metadata, if this many milliseconds have + passed since the previous packet from this source. Zero is + the default and stands for "never send the timestamp". + +What: /config/stp-policy/:p_sys-t.//clocksync_interval +Date: June 2018 +KernelVersion: 4.19 +Description: + Time interval in milliseconds. Send a CLOCKSYNC packet if + this many milliseconds have passed since the previous + CLOCKSYNC packet from this source. Zero is the default and + stands for "never send the CLOCKSYNC". It makes sense to + use this option with sources that generate constant and/or + periodic data, like stm_heartbeat. From c9c273dbfc55df5659c7edfe415c48bf68c4510a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:01 +0300 Subject: [PATCH 0793/1276] stm class: Document the MIPI SyS-T protocol usage Add a document describing MIPI SyS-T protocol driver usage. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/sys-t.rst | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/trace/sys-t.rst diff --git a/Documentation/trace/sys-t.rst b/Documentation/trace/sys-t.rst new file mode 100644 index 000000000000..3d8eb92735e9 --- /dev/null +++ b/Documentation/trace/sys-t.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================== +MIPI SyS-T over STP +=================== + +The MIPI SyS-T protocol driver can be used with STM class devices to +generate standardized trace stream. Aside from being a standard, it +provides better trace source identification and timestamp correlation. + +In order to use the MIPI SyS-T protocol driver with your STM device, +first, you'll need CONFIG_STM_PROTO_SYS_T. + +Now, you can select which protocol driver you want to use when you create +a policy for your STM device, by specifying it in the policy name: + +# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/ + +In other words, the policy name format is extended like this: + + :. + +With Intel TH, therefore it can look like "0-sth:p_sys-t.my-policy". + +If the protocol name is omitted, the STM class will chose whichever +protocol driver was loaded first. + +You can also double check that everything is working as expected by + +# cat /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/protocol +p_sys-t + +Now, with the MIPI SyS-T protocol driver, each policy node in the +configfs gets a few additional attributes, which determine per-source +parameters specific to the protocol: + +# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default +# ls /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default +channels +clocksync_interval +do_len +masters +ts_interval +uuid + +The most important one here is the "uuid", which determines the UUID +that will be used to tag all data coming from this source. It is +automatically generated when a new node is created, but it is likely +that you would want to change it. + +do_len switches on/off the additional "payload length" field in the +MIPI SyS-T message header. It is off by default as the STP already +marks message boundaries. + +ts_interval and clocksync_interval determine how much time in milliseconds +can pass before we need to include a protocol (not transport, aka STP) +timestamp in a message header or send a CLOCKSYNC packet, respectively. + +See Documentation/ABI/testing/configfs-stp-policy-p_sys-t for more +details. + +* [1] https://www.mipi.org/specifications/sys-t From bbb718a26dd2f4f6fb85dd683862c9a42ab7b078 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:02 +0300 Subject: [PATCH 0794/1276] stm class: Update documentation to match the new identification rules The rules and order of identification of trace sources against the "stp-policy" have changed; update the documentation to reflect that. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/stm.rst | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Documentation/trace/stm.rst b/Documentation/trace/stm.rst index 2c22ddb7fd3e..944994fd6368 100644 --- a/Documentation/trace/stm.rst +++ b/Documentation/trace/stm.rst @@ -53,12 +53,30 @@ under "user" directory from the example above and this new rule will be used for trace sources with the id string of "user/dummy". Trace sources have to open the stm class device's node and write their -trace data into its file descriptor. In order to identify themselves -to the policy, they need to do a STP_POLICY_ID_SET ioctl on this file -descriptor providing their id string. Otherwise, they will be -automatically allocated a master/channel pair upon first write to this -file descriptor according to the "default" rule of the policy, if such -exists. +trace data into its file descriptor. + +In order to find an appropriate policy node for a given trace source, +several mechanisms can be used. First, a trace source can explicitly +identify itself by calling an STP_POLICY_ID_SET ioctl on the character +device's file descriptor, providing their id string, before they write +any data there. Secondly, if they chose not to perform the explicit +identification (because you may not want to patch existing software +to do this), they can just start writing the data, at which point the +stm core will try to find a policy node with the name matching the +task's name (e.g., "syslogd") and if one exists, it will be used. +Thirdly, if the task name can't be found among the policy nodes, the +catch-all entry "default" will be used, if it exists. This entry also +needs to be created and configured by the system administrator or +whatever tools are taking care of the policy configuration. Finally, +if all the above steps failed, the write() to an stm file descriptor +will return a error (EINVAL). + +Previously, if no policy nodes were found for a trace source, the stm +class would silently fall back to allocating the first available +contiguous range of master/channels from the beginning of the device's +master/channel range. The new requirement for a policy node to exist +will help programmers and sysadmins identify gaps in configuration +and have better control over the un-identified sources. Some STM devices may allow direct mapping of the channel mmio regions to userspace for zero-copy writing. One mappable page (in terms of @@ -92,9 +110,9 @@ allocated for the device according to the policy configuration. If there's a node in the root of the policy directory that matches the stm_source device's name (for example, "console"), this node will be used to allocate master and channel numbers. If there's no such policy -node, the stm core will pick the first contiguous chunk of channels -within the first available master. Note that the node must exist -before the stm_source device is connected to its stm device. +node, the stm core will use the catch-all entry "default", if one +exists. If neither policy nodes exist, the write() to stm_source_link +will return an error. stm_console =========== From fbcd531749b65bef7a46105254083493049740e0 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:03 +0300 Subject: [PATCH 0795/1276] stm class: SPDX-ify the documentation Add the SPDX header to the STM class documentation. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/stm.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/trace/stm.rst b/Documentation/trace/stm.rst index 944994fd6368..99f99963e5e7 100644 --- a/Documentation/trace/stm.rst +++ b/Documentation/trace/stm.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + =================== System Trace Module =================== From 6468fca596f002ab04feacef26ce01c01785d579 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:04 +0300 Subject: [PATCH 0796/1276] stm class: heartbeat: Fix whitespace Fix whitespace in the code for better readability, no functional changes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/heartbeat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c index 7db42395e131..3e7df1c0477f 100644 --- a/drivers/hwtracing/stm/heartbeat.c +++ b/drivers/hwtracing/stm/heartbeat.c @@ -76,7 +76,7 @@ static int stm_heartbeat_init(void) goto fail_unregister; stm_heartbeat[i].data.nr_chans = 1; - stm_heartbeat[i].data.link = stm_heartbeat_link; + stm_heartbeat[i].data.link = stm_heartbeat_link; stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); From 75b4c98fcd69961629e0c555967a8f0621a5398f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:05 +0300 Subject: [PATCH 0797/1276] lib: Add memcat_p(): paste 2 pointer arrays together This adds a helper to paste 2 pointer arrays together, useful for merging various types of attribute arrays. There are a few places in the kernel tree where this is open coded, and I just added one more in the STM class. The naming is inspired by memset_p() and memcat(), and partial credit for it goes to Andy Shevchenko. This patch adds the function wrapped in a type-enforcing macro and a test module. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- include/linux/string.h | 7 +++ lib/Kconfig.debug | 8 +++ lib/Makefile | 1 + lib/string.c | 31 +++++++++++ lib/test_memcat_p.c | 115 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 lib/test_memcat_p.c diff --git a/include/linux/string.h b/include/linux/string.h index 4a5a0eb7df51..27d0482e5e05 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -131,6 +131,13 @@ static inline void *memset_p(void **p, void *v, __kernel_size_t n) return memset64((uint64_t *)p, (uintptr_t)v, n); } +extern void **__memcat_p(void **a, void **b); +#define memcat_p(a, b) ({ \ + BUILD_BUG_ON_MSG(!__same_type(*(a), *(b)), \ + "type mismatch in memcat_p()"); \ + (typeof(*a) *)__memcat_p((void **)(a), (void **)(b)); \ +}) + #ifndef __HAVE_ARCH_MEMCPY extern void * memcpy(void *,const void *,__kernel_size_t); #endif diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 4966c4fbe7f7..c0176510262e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1965,6 +1965,14 @@ config TEST_DEBUG_VIRTUAL If unsure, say N. +config TEST_MEMCAT_P + tristate "Test memcat_p() helper function" + help + Test the memcat_p() helper for correctly merging two + pointer arrays together. + + If unsure, say N. + endif # RUNTIME_TESTING_MENU config MEMTEST diff --git a/lib/Makefile b/lib/Makefile index 423876446810..02f5f811d461 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o obj-$(CONFIG_TEST_KMOD) += test_kmod.o obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o +obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/string.c b/lib/string.c index 2c0900a5d51a..453f35994eb6 100644 --- a/lib/string.c +++ b/lib/string.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -890,6 +891,36 @@ void *memscan(void *addr, int c, size_t size) EXPORT_SYMBOL(memscan); #endif +/* + * Merge two NULL-terminated pointer arrays into a newly allocated + * array, which is also NULL-terminated. Nomenclature is inspired by + * memset_p() and memcat() found elsewhere in the kernel source tree. + */ +void **__memcat_p(void **a, void **b) +{ + void **p = a, **new; + int nr; + + /* count the elements in both arrays */ + for (nr = 0, p = a; *p; nr++, p++) + ; + for (p = b; *p; nr++, p++) + ; + /* one for the NULL-terminator */ + nr++; + + new = kmalloc_array(nr, sizeof(void *), GFP_KERNEL); + if (!new) + return NULL; + + /* nr -> last index; p points to NULL in b[] */ + for (nr--; nr >= 0; nr--, p = p == b ? &a[nr] : p - 1) + new[nr] = *p; + + return new; +} +EXPORT_SYMBOL_GPL(__memcat_p); + #ifndef __HAVE_ARCH_STRSTR /** * strstr - Find the first substring in a %NUL terminated string diff --git a/lib/test_memcat_p.c b/lib/test_memcat_p.c new file mode 100644 index 000000000000..2b163a749ecb --- /dev/null +++ b/lib/test_memcat_p.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for memcat_p() in lib/string.c + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +struct test_struct { + int num; + unsigned int magic; +}; + +#define MAGIC 0xf00ff00f +/* Size of each of the NULL-terminated input arrays */ +#define INPUT_MAX 128 +/* Expected number of non-NULL elements in the output array */ +#define EXPECT (INPUT_MAX * 2 - 2) + +static int __init test_memcat_p_init(void) +{ + struct test_struct **in0, **in1, **out, **p; + int err = -ENOMEM, i, r, total = 0; + + in0 = kcalloc(INPUT_MAX, sizeof(*in0), GFP_KERNEL); + if (!in0) + return err; + + in1 = kcalloc(INPUT_MAX, sizeof(*in1), GFP_KERNEL); + if (!in1) + goto err_free_in0; + + for (i = 0, r = 1; i < INPUT_MAX - 1; i++) { + in0[i] = kmalloc(sizeof(**in0), GFP_KERNEL); + if (!in0[i]) + goto err_free_elements; + + in1[i] = kmalloc(sizeof(**in1), GFP_KERNEL); + if (!in1[i]) { + kfree(in0[i]); + goto err_free_elements; + } + + /* lifted from test_sort.c */ + r = (r * 725861) % 6599; + in0[i]->num = r; + in1[i]->num = -r; + in0[i]->magic = MAGIC; + in1[i]->magic = MAGIC; + } + + in0[i] = in1[i] = NULL; + + out = memcat_p(in0, in1); + if (!out) + goto err_free_all_elements; + + err = -EINVAL; + for (i = 0, p = out; *p && (i < INPUT_MAX * 2 - 1); p++, i++) { + total += (*p)->num; + + if ((*p)->magic != MAGIC) { + pr_err("test failed: wrong magic at %d: %u\n", i, + (*p)->magic); + goto err_free_out; + } + } + + if (total) { + pr_err("test failed: expected zero total, got %d\n", total); + goto err_free_out; + } + + if (i != EXPECT) { + pr_err("test failed: expected output size %d, got %d\n", + EXPECT, i); + goto err_free_out; + } + + for (i = 0; i < INPUT_MAX - 1; i++) + if (out[i] != in0[i] || out[i + INPUT_MAX - 1] != in1[i]) { + pr_err("test failed: wrong element order at %d\n", i); + goto err_free_out; + } + + err = 0; + pr_info("test passed\n"); + +err_free_out: + kfree(out); +err_free_all_elements: + i = INPUT_MAX; +err_free_elements: + for (i--; i >= 0; i--) { + kfree(in1[i]); + kfree(in0[i]); + } + + kfree(in1); +err_free_in0: + kfree(in0); + + return err; +} + +static void __exit test_memcat_p_exit(void) +{ +} + +module_init(test_memcat_p_init); +module_exit(test_memcat_p_exit); + +MODULE_LICENSE("GPL"); From e7d8138b35dcadec511bf7ca59aa131544d9dca7 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:06 +0300 Subject: [PATCH 0798/1276] stm class: Use memcat_p() Instead of a local copy, use the memcat_p() helper to merge policy node attributes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 5e51bed0787b..0910ec807187 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -202,33 +202,6 @@ static struct configfs_attribute *stp_policy_node_attrs[] = { static const struct config_item_type stp_policy_type; static const struct config_item_type stp_policy_node_type; -/* lifted from arch/x86/events/core.c */ -static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) -{ - struct configfs_attribute **new; - int j, i; - - for (j = 0; a[j]; j++) - ; - for (i = 0; b[i]; i++) - j++; - j++; - - new = kmalloc_array(j, sizeof(struct configfs_attribute *), - GFP_KERNEL); - if (!new) - return NULL; - - j = 0; - for (i = 0; a[i]; i++) - new[j++] = a[i]; - for (i = 0; b[i]; i++) - new[j++] = b[i]; - new[j] = NULL; - - return new; -} - const struct config_item_type * get_policy_node_type(struct configfs_attribute **attrs) { @@ -240,7 +213,7 @@ get_policy_node_type(struct configfs_attribute **attrs) if (!type) return NULL; - merged = merge_attr(stp_policy_node_attrs, attrs); + merged = memcat_p(stp_policy_node_attrs, attrs); if (!merged) { kfree(type); return NULL; From a24254aea0a0c3f789bf76f2d776e6ff80d86158 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 16 Oct 2018 14:13:40 +0300 Subject: [PATCH 0799/1276] lib: Fix ia64 bootloader linkage kbuild robot reports that since commit ce76d938dd98 ("lib: Add memcat_p(): paste 2 pointer arrays together") the ia64/hp/sim/boot fails to link: > LD arch/ia64/hp/sim/boot/bootloader > lib/string.o: In function `__memcat_p': > string.c:(.text+0x1f22): undefined reference to `__kmalloc' > string.c:(.text+0x1ff2): undefined reference to `__kmalloc' > make[1]: *** [arch/ia64/hp/sim/boot/Makefile:37: arch/ia64/hp/sim/boot/bootloader] Error 1 The reason is, the above commit, via __memcat_p(), adds a call to __kmalloc to string.o, which happens to be used in the bootloader, but there's no kmalloc or slab or anything. Since the linker would only pull in objects that contain referenced symbols, moving __memcat_p() to a different compilation unit solves the problem. Fixes: ce76d938dd98 ("lib: Add memcat_p(): paste 2 pointer arrays together") Signed-off-by: Alexander Shishkin Reported-by: kbuild test robot Cc: Fenghua Yu Cc: Tony Luck Cc: Joe Perches Signed-off-by: Greg Kroah-Hartman --- lib/Makefile | 2 +- lib/memcat_p.c | 34 ++++++++++++++++++++++++++++++++++ lib/string.c | 30 ------------------------------ lib/test_memcat_p.c | 2 +- 4 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 lib/memcat_p.c diff --git a/lib/Makefile b/lib/Makefile index 02f5f811d461..df0eeb669fcb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -24,7 +24,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ flex_proportions.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ earlycpio.o seq_buf.o siphash.o dec_and_lock.o \ - nmi_backtrace.o nodemask.o win_minmax.o + nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o lib-$(CONFIG_PRINTK) += dump_stack.o lib-$(CONFIG_MMU) += ioremap.o diff --git a/lib/memcat_p.c b/lib/memcat_p.c new file mode 100644 index 000000000000..b810fbc66962 --- /dev/null +++ b/lib/memcat_p.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +/* + * Merge two NULL-terminated pointer arrays into a newly allocated + * array, which is also NULL-terminated. Nomenclature is inspired by + * memset_p() and memcat() found elsewhere in the kernel source tree. + */ +void **__memcat_p(void **a, void **b) +{ + void **p = a, **new; + int nr; + + /* count the elements in both arrays */ + for (nr = 0, p = a; *p; nr++, p++) + ; + for (p = b; *p; nr++, p++) + ; + /* one for the NULL-terminator */ + nr++; + + new = kmalloc_array(nr, sizeof(void *), GFP_KERNEL); + if (!new) + return NULL; + + /* nr -> last index; p points to NULL in b[] */ + for (nr--; nr >= 0; nr--, p = p == b ? &a[nr] : p - 1) + new[nr] = *p; + + return new; +} +EXPORT_SYMBOL_GPL(__memcat_p); + diff --git a/lib/string.c b/lib/string.c index 453f35994eb6..38e4ca08e757 100644 --- a/lib/string.c +++ b/lib/string.c @@ -891,36 +891,6 @@ void *memscan(void *addr, int c, size_t size) EXPORT_SYMBOL(memscan); #endif -/* - * Merge two NULL-terminated pointer arrays into a newly allocated - * array, which is also NULL-terminated. Nomenclature is inspired by - * memset_p() and memcat() found elsewhere in the kernel source tree. - */ -void **__memcat_p(void **a, void **b) -{ - void **p = a, **new; - int nr; - - /* count the elements in both arrays */ - for (nr = 0, p = a; *p; nr++, p++) - ; - for (p = b; *p; nr++, p++) - ; - /* one for the NULL-terminator */ - nr++; - - new = kmalloc_array(nr, sizeof(void *), GFP_KERNEL); - if (!new) - return NULL; - - /* nr -> last index; p points to NULL in b[] */ - for (nr--; nr >= 0; nr--, p = p == b ? &a[nr] : p - 1) - new[nr] = *p; - - return new; -} -EXPORT_SYMBOL_GPL(__memcat_p); - #ifndef __HAVE_ARCH_STRSTR /** * strstr - Find the first substring in a %NUL terminated string diff --git a/lib/test_memcat_p.c b/lib/test_memcat_p.c index 2b163a749ecb..849c477d49d0 100644 --- a/lib/test_memcat_p.c +++ b/lib/test_memcat_p.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Test cases for memcat_p() in lib/string.c + * Test cases for memcat_p() in lib/memcat_p.c */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 2e90d6307e2b59174b46d055dd96e11b91baaf3f Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Fri, 31 Aug 2018 10:58:54 +0800 Subject: [PATCH 0800/1276] PCI: add pci_devices_ignore cmdline option some PCI devices may be occupied by hypervisor and do not want to enable in linux guest. add cmdline option "pci_devices_ignore=(B1:D1:F1),(B2:D2:F2, ...)" to ignore PCI devices when system doing pci scan. Change-Id: I506efef0a9d3a20b207770c744c70a013b10de13 Tracked-On:218445 Signed-off-by: Yonghua Huang Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/pci/probe.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 201f9e5ff55c..b9dda363aea6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -42,6 +42,70 @@ struct pci_domain_busn_res { int domain_nr; }; +#define PCI_IGNORE_MAX 8 + +static u16 devices_ignore_table[PCI_IGNORE_MAX]; +static int devices_ignore_cnt; + +static void parse_ignore_device(char *bdf_str) +{ + int fields; + unsigned int bus; + unsigned int dev; + unsigned int func; + + if (devices_ignore_cnt >= PCI_IGNORE_MAX - 1) + return; + + fields = sscanf(bdf_str, "%x:%x:%x", &bus, &dev, &func); + if (fields != 3) + return; + + devices_ignore_table[devices_ignore_cnt++] = + PCI_DEVID(bus, PCI_DEVFN(dev, func)); +} + +static int __init pci_deivces_ignore(char *str) +{ + int len; + char *start, *end; + char bdf[16]; + + devices_ignore_cnt = 0; + + while ((start = strchr(str, '('))) { + + end = strchr(start, ')'); + if (end == NULL) + break; + + len = end - start - 1; + if (len >= 16) /*invalid string*/ + break; + + memcpy((void *)bdf, (void *)(start+1), len); + bdf[len] = '\0'; + parse_ignore_device(bdf); + str = end + 1; + } + + return 1; +} +__setup("pci_devices_ignore=", pci_deivces_ignore); + +static bool device_on_ignore_list(int bus, int dev, int func) +{ + int i; + + for (i = 0; i < devices_ignore_cnt; i++) + if ((PCI_BUS_NUM(devices_ignore_table[i]) == bus) && + (PCI_SLOT(devices_ignore_table[i]) == dev) && + (PCI_FUNC(devices_ignore_table[i]) == func)) + return true; + + return false; +} + static struct resource *get_pci_domain_busn_res(int domain_nr) { struct pci_domain_busn_res *r; @@ -2442,6 +2506,11 @@ struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) return dev; } + if (device_on_ignore_list(bus->number, + PCI_SLOT(devfn), + PCI_FUNC(devfn))) + return NULL; + dev = pci_scan_device(bus, devfn); if (!dev) return NULL; From 4862b0cbd994bd609098340fd9e4c6fae256e847 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:54 +0800 Subject: [PATCH 0801/1276] x86: add ACRN hypervisor guest add x86_hyper_acrn into supported hypervisors array, which enabling ACRN services guest run on ACRN hypervisor. And it is restricted to X86_64. Change-Id: Ib9e7d9a8e971d5f32290953b4916dea064de8638 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Jack Ren Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- arch/x86/Kbuild | 2 + arch/x86/Kconfig | 2 + arch/x86/acrn/Kconfig | 15 +++++++ arch/x86/acrn/Makefile | 2 + arch/x86/acrn/acrn.c | 71 +++++++++++++++++++++++++++++++ arch/x86/include/asm/hypervisor.h | 1 + arch/x86/kernel/cpu/hypervisor.c | 4 ++ 7 files changed, 97 insertions(+) create mode 100644 arch/x86/acrn/Kconfig create mode 100644 arch/x86/acrn/Makefile create mode 100644 arch/x86/acrn/acrn.c diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index 0038a2d10a7a..466219296cd6 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -7,6 +7,8 @@ obj-$(CONFIG_KVM) += kvm/ # Xen paravirtualization support obj-$(CONFIG_XEN) += xen/ +obj-$(CONFIG_ACRN) += acrn/ + # Hyper-V paravirtualization support obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1a0be022f91d..04855e782c63 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -782,6 +782,8 @@ config QUEUED_LOCK_STAT behavior of paravirtualized queued spinlocks and report them on debugfs. +source "arch/x86/acrn/Kconfig" + source "arch/x86/xen/Kconfig" config KVM_GUEST diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig new file mode 100644 index 000000000000..0ba9e36c41f3 --- /dev/null +++ b/arch/x86/acrn/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# This Kconfig describes ACRN options +# + +config ACRN + bool "Enable services run on ACRN hypervisor" + depends on X86_64 + depends on PARAVIRT + depends on DMA_CMA + depends on !INTEL_IOMMU + depends on !VMAP_STACK + help + This option is needed if were to run ACRN services linux on top of + ACRN hypervisor. diff --git a/arch/x86/acrn/Makefile b/arch/x86/acrn/Makefile new file mode 100644 index 000000000000..d961d8c5ee93 --- /dev/null +++ b/arch/x86/acrn/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_ACRN) += acrn.o diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c new file mode 100644 index 000000000000..a042b544af33 --- /dev/null +++ b/arch/x86/acrn/acrn.c @@ -0,0 +1,71 @@ +/* + * ACRN hypervisor support + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ +#include + +static uint32_t __init acrn_detect(void) +{ + return hypervisor_cpuid_base("ACRNACRNACRN\0\0", 0); +} + +static void __init acrn_init_platform(void) +{ +} + +static void acrn_pin_vcpu(int cpu) +{ + /* do nothing here now */ +} + +static bool acrn_x2apic_available(void) +{ + /* do not support x2apic */ + return false; +} + +static void __init acrn_init_mem_mapping(void) +{ + /* do nothing here now */ +} + +const struct hypervisor_x86 x86_hyper_acrn = { + .name = "ACRN", + .detect = acrn_detect, + .type = X86_HYPER_ACRN, + .init.init_platform = acrn_init_platform, + .runtime.pin_vcpu = acrn_pin_vcpu, + .init.x2apic_available = acrn_x2apic_available, + .init.init_mem_mapping = acrn_init_mem_mapping, +}; +EXPORT_SYMBOL(x86_hyper_acrn); diff --git a/arch/x86/include/asm/hypervisor.h b/arch/x86/include/asm/hypervisor.h index 8c5aaba6633f..50a30f6c668b 100644 --- a/arch/x86/include/asm/hypervisor.h +++ b/arch/x86/include/asm/hypervisor.h @@ -29,6 +29,7 @@ enum x86_hypervisor_type { X86_HYPER_XEN_HVM, X86_HYPER_KVM, X86_HYPER_JAILHOUSE, + X86_HYPER_ACRN, }; #ifdef CONFIG_HYPERVISOR_GUEST diff --git a/arch/x86/kernel/cpu/hypervisor.c b/arch/x86/kernel/cpu/hypervisor.c index 479ca4728de0..5a6f072e6748 100644 --- a/arch/x86/kernel/cpu/hypervisor.c +++ b/arch/x86/kernel/cpu/hypervisor.c @@ -32,6 +32,7 @@ extern const struct hypervisor_x86 x86_hyper_xen_pv; extern const struct hypervisor_x86 x86_hyper_xen_hvm; extern const struct hypervisor_x86 x86_hyper_kvm; extern const struct hypervisor_x86 x86_hyper_jailhouse; +extern const struct hypervisor_x86 x86_hyper_acrn; static const __initconst struct hypervisor_x86 * const hypervisors[] = { @@ -49,6 +50,9 @@ static const __initconst struct hypervisor_x86 * const hypervisors[] = #ifdef CONFIG_JAILHOUSE_GUEST &x86_hyper_jailhouse, #endif +#ifdef CONFIG_ACRN + &x86_hyper_acrn, +#endif }; enum x86_hypervisor_type x86_hyper_type; From e420d5939d6fadd0ed90800c2a9b810e1a588d63 Mon Sep 17 00:00:00 2001 From: liang ding Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0802/1276] VHM: add vhm char device driver VHM(virtio and hypervisor service module) is the important middle layer to run virtio and hypervisor services in linux kernel for ACRN hypervisor. The vhm char device is the main interface. It provides ioctls to applications and interacts with ACRN hypervisor through different hypercalls. This patch enable ACRN vhm service based on CONFIG_ACRN; added a basic vhm char device which contains services for VM management in drivers/char/vhm; and located vhm service lib in drivers/vhm. Change-Id: Ib6c95d810581abd226692cbec9649a24b466a93b Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Jason Zeng Signed-off-by: Xiao Zheng Signed-off-by: Jason Chen CJ Signed-off-by: Jack Ren Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/Makefile | 1 + drivers/char/Makefile | 1 + drivers/char/vhm/Makefile | 1 + drivers/char/vhm/vhm_dev.c | 247 +++++++++++++++++++++++++++++ drivers/vhm/Makefile | 1 + drivers/vhm/vhm_hypercall.c | 134 ++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 107 +++++++++++++ include/linux/vhm/acrn_common.h | 65 ++++++++ include/linux/vhm/acrn_hv_defs.h | 81 ++++++++++ include/linux/vhm/vhm_hypercall.h | 148 +++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 67 ++++++++ include/linux/vhm/vhm_vm_mngt.h | 78 +++++++++ 12 files changed, 931 insertions(+) create mode 100644 drivers/char/vhm/Makefile create mode 100644 drivers/char/vhm/vhm_dev.c create mode 100644 drivers/vhm/Makefile create mode 100644 drivers/vhm/vhm_hypercall.c create mode 100644 drivers/vhm/vhm_vm_mngt.c create mode 100644 include/linux/vhm/acrn_common.h create mode 100644 include/linux/vhm/acrn_hv_defs.h create mode 100644 include/linux/vhm/vhm_hypercall.h create mode 100644 include/linux/vhm/vhm_ioctl_defs.h create mode 100644 include/linux/vhm/vhm_vm_mngt.h diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..07528374561a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -183,6 +183,7 @@ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ +obj-$(CONFIG_ACRN) += vhm/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b8d42b4e979b..39eccecf0d3a 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,3 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_ACRN) += vhm/ diff --git a/drivers/char/vhm/Makefile b/drivers/char/vhm/Makefile new file mode 100644 index 000000000000..cb801c70a37e --- /dev/null +++ b/drivers/char/vhm/Makefile @@ -0,0 +1 @@ +obj-y += vhm_dev.o diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c new file mode 100644 index 000000000000..527e90b187cf --- /dev/null +++ b/drivers/char/vhm/vhm_dev.c @@ -0,0 +1,247 @@ +/* + * virtio and hyperviosr service module (VHM): main framework + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * Xiao Zheng + * Jason Chen CJ + * Jack Ren + * Mingqiang Chi + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEVICE_NAME "acrn_vhm" +#define CLASS_NAME "vhm" + +static int major; +static struct class *vhm_class; +static struct device *vhm_device; + +static int vhm_dev_open(struct inode *inodep, struct file *filep) +{ + struct vhm_vm *vm; + + vm = kzalloc(sizeof(struct vhm_vm), GFP_KERNEL); + pr_info("vhm_dev_open: opening device node\n"); + + if (!vm) + return -ENOMEM; + vm->vmid = ACRN_INVALID_VMID; + vm->dev = vhm_device; + + vm_mutex_lock(&vhm_vm_list_lock); + vm->refcnt = 1; + vm_list_add(&vm->list); + vm_mutex_unlock(&vhm_vm_list_lock); + filep->private_data = vm; + return 0; +} + +static ssize_t vhm_dev_read(struct file *filep, char *buffer, size_t len, + loff_t *offset) +{ + /* Does Nothing */ + pr_info("vhm_dev_read: reading device node\n"); + return 0; +} + +static ssize_t vhm_dev_write(struct file *filep, const char *buffer, + size_t len, loff_t *offset) +{ + /* Does Nothing */ + pr_info("vhm_dev_read: writing device node\n"); + return 0; +} + +static long vhm_dev_ioctl(struct file *filep, + unsigned int ioctl_num, unsigned long ioctl_param) +{ + long ret = 0; + struct vhm_vm *vm; + + trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + + vm = (struct vhm_vm *)filep->private_data; + if (vm == NULL) { + pr_err("vhm: invalid VM !\n"); + return -EFAULT; + } + if ((vm->vmid == ACRN_INVALID_VMID) && (ioctl_num != IC_CREATE_VM)) { + pr_err("vhm: invalid VM ID !\n"); + return -EFAULT; + } + + switch (ioctl_num) { + case IC_CREATE_VM: + ret = vhm_create_vm(vm, ioctl_param); + break; + + case IC_RESUME_VM: + ret = vhm_resume_vm(vm); + break; + + case IC_PAUSE_VM: + ret = vhm_pause_vm(vm); + break; + + case IC_DESTROY_VM: + ret = vhm_destroy_vm(vm); + break; + + case IC_QUERY_VMSTATE: + ret = vhm_query_vm_state(vm); + break; + + default: + pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); + ret = 0; + break; + } + + return ret; +} + +static int vhm_dev_release(struct inode *inodep, struct file *filep) +{ + struct vhm_vm *vm = filep->private_data; + + if (vm == NULL) { + pr_err("vhm: invalid VM !\n"); + return -EFAULT; + } + put_vm(vm); + filep->private_data = NULL; + return 0; +} + +static const struct file_operations fops = { + .open = vhm_dev_open, + .read = vhm_dev_read, + .write = vhm_dev_write, + .release = vhm_dev_release, + .unlocked_ioctl = vhm_dev_ioctl, +}; + +static int __init vhm_init(void) +{ + pr_info("vhm: initializing\n"); + + /* Try to dynamically allocate a major number for the device */ + major = register_chrdev(0, DEVICE_NAME, &fops); + if (major < 0) { + pr_warn("vhm: failed to register a major number\n"); + return major; + } + pr_info("vhm: registered correctly with major number %d\n", major); + + /* Register the device class */ + vhm_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(vhm_class)) { + unregister_chrdev(major, DEVICE_NAME); + pr_warn("vhm: failed to register device class\n"); + return PTR_ERR(vhm_class); + } + pr_info("vhm: device class registered correctly\n"); + + /* Register the device driver */ + vhm_device = device_create(vhm_class, NULL, MKDEV(major, 0), + NULL, DEVICE_NAME); + if (IS_ERR(vhm_device)) { + class_destroy(vhm_class); + unregister_chrdev(major, DEVICE_NAME); + pr_warn("vhm: failed to create the device\n"); + return PTR_ERR(vhm_device); + } + + pr_info("vhm: Virtio & Hypervisor service module initialized\n"); + return 0; +} +static void __exit vhm_exit(void) +{ + device_destroy(vhm_class, MKDEV(major, 0)); + class_unregister(vhm_class); + class_destroy(vhm_class); + unregister_chrdev(major, DEVICE_NAME); + pr_info("vhm: exit\n"); +} + +module_init(vhm_init); +module_exit(vhm_exit); + +MODULE_AUTHOR("Intel"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("This is a char device driver, acts as a route " + "responsible for transferring IO requsts from other modules " + "either in user-space or in kernel to and from hypervisor"); +MODULE_VERSION("0.1"); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile new file mode 100644 index 000000000000..220697aaccb7 --- /dev/null +++ b/drivers/vhm/Makefile @@ -0,0 +1 @@ +obj-y += vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c new file mode 100644 index 000000000000..ddc085d0fa11 --- /dev/null +++ b/drivers/vhm/vhm_hypercall.c @@ -0,0 +1,134 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall wrap + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include + +inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_create_vm created_vm; + + if (copy_from_user(&created_vm, (void *)ioctl_param, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + ret = acrn_hypercall2(HC_CREATE_VM, 0, + virt_to_phys(&created_vm)); + if ((ret < 0) || + (created_vm.vmid == ACRN_INVALID_VMID)) { + pr_err("vhm: failed to create VM from Hypervisor !\n"); + return -EFAULT; + } + + if (copy_to_user((void *)ioctl_param, &created_vm, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + vm->vmid = created_vm.vmid; + pr_info("vhm: VM %ld created\n", created_vm.vmid); + + return ret; +} + +inline long vhm_resume_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_RESUME_VM, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to start VM %ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} + +inline long vhm_pause_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_PAUSE_VM, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} + +inline long vhm_destroy_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_DESTROY_VM, vm->vmid); + if (ret < 0) { + pr_err("failed to destroy VM %ld\n", vm->vmid); + return -EFAULT; + } + vm->vmid = ACRN_INVALID_VMID; + + return ret; +} + +inline long vhm_query_vm_state(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_QUERY_VMSTATE, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c new file mode 100644 index 000000000000..61db04b57362 --- /dev/null +++ b/drivers/vhm/vhm_vm_mngt.c @@ -0,0 +1,107 @@ +/* + * virtio and hyperviosr service module (VHM): vm management + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * + */ + +#include +#include +#include +#include +#include +#include +#include + +LIST_HEAD(vhm_vm_list); +DEFINE_MUTEX(vhm_vm_list_lock); + +struct vhm_vm *find_get_vm(unsigned long vmid) +{ + struct vhm_vm *vm; + + mutex_lock(&vhm_vm_list_lock); + list_for_each_entry(vm, &vhm_vm_list, list) { + if (vm->vmid == vmid) { + vm->refcnt++; + mutex_unlock(&vhm_vm_list_lock); + return vm; + } + } + mutex_unlock(&vhm_vm_list_lock); + return NULL; +} + +void put_vm(struct vhm_vm *vm) +{ + mutex_lock(&vhm_vm_list_lock); + vm->refcnt--; + if (vm->refcnt == 0) { + list_del(&vm->list); + kfree(vm); + pr_info("vhm: freed vm\n"); + } + mutex_unlock(&vhm_vm_list_lock); +} + +void vm_list_add(struct list_head *list) +{ + list_add(list, &vhm_vm_list); +} + +void vm_mutex_lock(struct mutex *mlock) +{ + mutex_lock(mlock); +} + +void vm_mutex_unlock(struct mutex *mlock) +{ + mutex_unlock(mlock); +} diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h new file mode 100644 index 000000000000..08e47732f4d0 --- /dev/null +++ b/include/linux/vhm/acrn_common.h @@ -0,0 +1,65 @@ +/* + * virtio and hyperviosr service module (VHM): commom.h + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ACRN_COMMON_H +#define ACRN_COMMON_H + +/* + * Commmon structures for ACRN/VHM/DM + */ + +/* Common API params */ +struct acrn_create_vm { + unsigned long vmid; /* OUT: HV return vmid to VHM */ + unsigned long vcpu_num; /* IN: VM vcpu number */ +} __attribute__((aligned(8))); + +#endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h new file mode 100644 index 000000000000..f338a8fbad3d --- /dev/null +++ b/include/linux/vhm/acrn_hv_defs.h @@ -0,0 +1,81 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall header + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ACRN_HV_DEFS_H +#define ACRN_HV_DEFS_H + +/* + * Commmon structures for ACRN/VHM/DM + */ +#include "acrn_common.h" + +/* + * Commmon structures for HV/VHM + */ + +#define _HC_ID(x, y) (((x)<<24)|(y)) + +#define HC_ID 0x7FUL + +/* VM management */ +#define HC_ID_VM_BASE 0x0UL +#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) +#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) +#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) +#define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) +#define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) + +#define ACRN_DOM0_VMID (0UL) +#define ACRN_INVALID_VMID (-1UL) +#define ACRN_INVALID_HPA (-1UL) + +#endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h new file mode 100644 index 000000000000..c07163dbc3bd --- /dev/null +++ b/include/linux/vhm/vhm_hypercall.h @@ -0,0 +1,148 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall.h + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VHM_HYPERCALL_H +#define VHM_HYPERCALL_H + +#include + +static inline long acrn_hypercall0(unsigned long hyp_id) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, + unsigned long param2) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, + unsigned long param2, unsigned long param3) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "d"(param3), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, + unsigned long param2, unsigned long param3, + unsigned long param4) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "d"(param3), + "c"(param4), "r"(r8)); + + /* Return result to caller */ + return result; +} + +inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_resume_vm(struct vhm_vm *vm); +inline long vhm_pause_vm(struct vhm_vm *vm); +inline long vhm_destroy_vm(struct vhm_vm *vm); +inline long vhm_query_vm_state(struct vhm_vm *vm); + +#endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h new file mode 100644 index 000000000000..d8c81b6e9306 --- /dev/null +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -0,0 +1,67 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VHM_IOCTL_DEFS_H_ +#define _VHM_IOCTL_DEFS_H_ + +/* Commmon structures for ACRN/VHM/DM */ +#include "acrn_common.h" + +/* + * Commmon IOCTL ID defination for VHM/DM + */ +#define _IC_ID(x, y) (((x)<<24)|(y)) +#define IC_ID 0x5FUL + +/* VM management */ +#define IC_ID_VM_BASE 0x0UL +#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) +#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) +#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) +#define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) +#define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) + +#endif /* VHM_IOCTL_DEFS_H */ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h new file mode 100644 index 000000000000..dcb246af561a --- /dev/null +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -0,0 +1,78 @@ +/* + * virtio and hyperviosr service module (VHM): vm management + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * Xiao Zheng + * Jason Chen CJ + * + */ +#ifndef VHM_VM_MNGT_H +#define VHM_VM_MNGT_H + +#include + +extern struct list_head vhm_vm_list; +extern struct mutex vhm_vm_list_lock; + +struct vhm_vm { + struct device *dev; + struct list_head list; + unsigned long vmid; + long refcnt; +}; + +struct vhm_vm *find_get_vm(unsigned long vmid); +void put_vm(struct vhm_vm *vm); + +void vm_list_add(struct list_head *list); +void vm_mutex_lock(struct mutex *mlock); +void vm_mutex_unlock(struct mutex *mlock); + +#endif From 7cff955dab9ba008a40ad4a23d46c5ca511039e4 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0803/1276] VHM: add guest memory management support VHM provides guest memory management services for application. It allocates/frees contiguous physical memory for guest based on dma-cma, and provides corresponding EPT mapping for the allocated memory segment. Change-Id: Ibbe26b0ccf8436700f44bca899b1ee38c2e4ef72 Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Jason Chen CJ Signed-off-by: Jason Zeng Signed-off-by: Xiao Zheng Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 26 +++ drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hypercall.c | 5 + drivers/vhm/vhm_mm.c | 364 +++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 2 + include/linux/vhm/acrn_hv_defs.h | 26 +++ include/linux/vhm/acrn_vhm_mm.h | 88 +++++++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 40 ++++ include/linux/vhm/vhm_vm_mngt.h | 2 + 10 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_mm.c create mode 100644 include/linux/vhm/acrn_vhm_mm.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 527e90b187cf..3ea8de27cb3e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -78,6 +78,7 @@ #include #include +#include #include #include @@ -100,6 +101,9 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm->vmid = ACRN_INVALID_VMID; vm->dev = vhm_device; + INIT_LIST_HEAD(&vm->memseg_list); + mutex_init(&vm->seg_lock); + vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; vm_list_add(&vm->list); @@ -163,6 +167,27 @@ static long vhm_dev_ioctl(struct file *filep, ret = vhm_query_vm_state(vm); break; + case IC_ALLOC_MEMSEG: { + struct vm_memseg memseg; + + if (copy_from_user(&memseg, (void *)ioctl_param, + sizeof(struct vm_memseg))) + return -EFAULT; + + return alloc_guest_memseg(vm, &memseg); + } + + case IC_SET_MEMSEG: { + struct vm_memmap memmap; + + if (copy_from_user(&memmap, (void *)ioctl_param, + sizeof(struct vm_memmap))) + return -EFAULT; + + ret = map_guest_memseg(vm, &memmap); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; @@ -189,6 +214,7 @@ static const struct file_operations fops = { .open = vhm_dev_open, .read = vhm_dev_read, .write = vhm_dev_write, + .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, }; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 220697aaccb7..7e5ec421fbc7 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index ddc085d0fa11..d80087bcb5fb 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,11 @@ #include #include +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +{ + return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); +} + inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) { long ret = 0; diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c new file mode 100644 index 000000000000..9dd0b9414d3a --- /dev/null +++ b/drivers/vhm/vhm_mm.c @@ -0,0 +1,364 @@ +/* + * virtio and hyperviosr service module (VHM): memory map + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Jason Zeng + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct guest_memseg { + struct list_head list; + int segid; + u64 base; + size_t len; + char name[SPECNAMELEN + 1]; + u64 gpa; + int prot; /* RWX */ + long vma_count; +}; + +static u64 _alloc_memblk(struct device *dev, size_t len) +{ + unsigned int count; + struct page *page; + + if (!PAGE_ALIGNED(len)) { + pr_warn("alloc size of memblk must be page aligned\n"); + return 0ULL; + } + + count = PAGE_ALIGN(len) >> PAGE_SHIFT; + page = dma_alloc_from_contiguous(dev, count, 1, GFP_KERNEL); + if (page) + return page_to_phys(page); + else + return 0ULL; +} + +static bool _free_memblk(struct device *dev, u64 base, size_t len) +{ + unsigned int count = PAGE_ALIGN(len) >> PAGE_SHIFT; + struct page *page = pfn_to_page(base >> PAGE_SHIFT); + + return dma_release_from_contiguous(dev, page, count); +} + +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +{ + struct guest_memseg *seg; + u64 base; + + seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); + if (seg == NULL) + return -ENOMEM; + + base = _alloc_memblk(vm->dev, memseg->len); + if (base == 0ULL) { + kfree(seg); + return -ENOMEM; + } + + seg->segid = memseg->segid; + seg->base = base; + seg->len = memseg->len; + strncpy(seg->name, memseg->name, SPECNAMELEN + 1); + seg->gpa = memseg->gpa; + + pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," + " and its guest gpa = 0x%llx\n", + seg->name, seg->len, seg->base, seg->gpa); + + seg->vma_count = 0; + mutex_lock(&vm->seg_lock); + list_add(&seg->list, &vm->memseg_list); + mutex_unlock(&vm->seg_lock); + + return 0; +} + +static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot, int type) +{ + struct vm_set_memmap set_memmap; + + set_memmap.type = type; + set_memmap.foreign_gpa = guest_gpa; + set_memmap.hvm_gpa = host_gpa; + set_memmap.length = len; + set_memmap.prot = prot; + + /* hypercall to notify hv the guest EPT setting*/ + if (hcall_set_memmap(vmid, + virt_to_phys(&set_memmap)) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vmid); + return -EFAULT; + } + + pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," + "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", + type, host_gpa, guest_gpa, len, prot); + + return 0; +} + +int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_MMIO); +} + +int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_UNMAP); +} + +int update_mem_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_UPDATE); +} + +int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct guest_memseg *seg = NULL; + struct vm_set_memmap set_memmap; + + mutex_lock(&vm->seg_lock); + + if (memmap->segid != VM_MMIO) { + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid == memmap->segid + && seg->gpa == memmap->mem.gpa + && seg->len == memmap->mem.len) + break; + } + if (&seg->list == &vm->memseg_list) { + mutex_unlock(&vm->seg_lock); + return -EINVAL; + } + seg->prot = memmap->mem.prot; + set_memmap.type = MAP_MEM; + set_memmap.foreign_gpa = seg->gpa; + set_memmap.hvm_gpa = seg->base; + set_memmap.length = seg->len; + set_memmap.prot = seg->prot; + set_memmap.prot |= MMU_MEM_ATTR_WB_CACHE; + } else { + set_memmap.type = MAP_MMIO; + set_memmap.foreign_gpa = memmap->mmio.gpa; + set_memmap.hvm_gpa = memmap->mmio.hpa; + set_memmap.length = memmap->mmio.len; + set_memmap.prot = memmap->mmio.prot; + set_memmap.prot |= MMU_MEM_ATTR_UNCACHED; + } + + /* hypercall to notify hv the guest EPT setting*/ + if (hcall_set_memmap(vm->vmid, virt_to_phys(&set_memmap)) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + mutex_unlock(&vm->seg_lock); + return -EFAULT; + } + + mutex_unlock(&vm->seg_lock); + + if (memmap->segid != VM_MMIO) + pr_debug("VHM: set ept for memseg [hvm_gpa=0x%llx," + "guest_gpa=0x%llx,len=0x%lx, prot=0x%x]\n", + seg->base, seg->gpa, seg->len, seg->prot); + else + pr_debug("VHM: set ept for mmio [hpa=0x%llx," + "gpa=0x%llx,len=0x%lx, prot=0x%x]\n", + memmap->mmio.hpa, memmap->mmio.gpa, + memmap->mmio.len, memmap->mmio.prot); + + return 0; +} + +void free_guest_mem(struct vhm_vm *vm) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + while (!list_empty(&vm->memseg_list)) { + seg = list_first_entry(&vm->memseg_list, + struct guest_memseg, list); + if (!_free_memblk(vm->dev, seg->base, seg->len)) + pr_warn("failed to free memblk\n"); + list_del(&seg->list); + kfree(seg); + } + mutex_unlock(&vm->seg_lock); +} + +int check_guest_mem(struct vhm_vm *vm) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->vma_count == 0) + continue; + + mutex_unlock(&vm->seg_lock); + return -EAGAIN; + } + mutex_unlock(&vm->seg_lock); + return 0; +} + +static void guest_vm_open(struct vm_area_struct *vma) +{ + struct vhm_vm *vm = vma->vm_file->private_data; + struct guest_memseg *seg = vma->vm_private_data; + + mutex_lock(&vm->seg_lock); + seg->vma_count++; + mutex_unlock(&vm->seg_lock); +} + +static void guest_vm_close(struct vm_area_struct *vma) +{ + struct vhm_vm *vm = vma->vm_file->private_data; + struct guest_memseg *seg = vma->vm_private_data; + + mutex_lock(&vm->seg_lock); + seg->vma_count--; + BUG_ON(seg->vma_count < 0); + mutex_unlock(&vm->seg_lock); +} + +static const struct vm_operations_struct guest_vm_ops = { + .open = guest_vm_open, + .close = guest_vm_close, +}; + +static int do_mmap_guest(struct file *file, + struct vm_area_struct *vma, struct guest_memseg *seg) +{ + struct page *page; + size_t size = seg->len; + unsigned long pfn; + unsigned long start_addr; + + vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; + pfn = seg->base >> PAGE_SHIFT; + start_addr = vma->vm_start; + while (size > 0) { + page = pfn_to_page(pfn); + if (vm_insert_page(vma, start_addr, page)) + return -EINVAL; + size -= PAGE_SIZE; + start_addr += PAGE_SIZE; + pfn++; + } + seg->vma_count++; + vma->vm_ops = &guest_vm_ops; + vma->vm_private_data = (void *)seg; + + pr_info("VHM: mmap for memseg [seg base=0x%llx, gpa=0x%llx] " + "to start addr 0x%lx\n", + seg->base, seg->gpa, start_addr); + + return 0; +} + +int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vhm_vm *vm = file->private_data; + struct guest_memseg *seg; + u64 offset = vma->vm_pgoff << PAGE_SHIFT; + size_t len = vma->vm_end - vma->vm_start; + int ret; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa != offset || seg->len != len) + continue; + + ret = do_mmap_guest(file, vma, seg); + mutex_unlock(&vm->seg_lock); + return ret; + } + mutex_unlock(&vm->seg_lock); + return -EINVAL; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 61db04b57362..3c4e6d2b2f23 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -58,6 +58,7 @@ #include #include #include +#include #include LIST_HEAD(vhm_vm_list); @@ -85,6 +86,7 @@ void put_vm(struct vhm_vm *vm) vm->refcnt--; if (vm->refcnt == 0) { list_del(&vm->list); + free_guest_mem(vm); kfree(vm); pr_info("vhm: freed vm\n"); } diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f338a8fbad3d..ab6554d017cb 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,8 +74,34 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* Guest memory management */ +#define HC_ID_MEM_BASE 0x300UL +#define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) +enum vm_memmap_type { + MAP_MEM = 0, + MAP_MMIO, + MAP_UNMAP, + MAP_UPDATE, +}; + +struct vm_set_memmap { + enum vm_memmap_type type; + /* IN: beginning guest GPA to map */ + unsigned long foreign_gpa; + + /* IN: VM0's GPA which foreign gpa will be mapped to */ + unsigned long hvm_gpa; + + /* IN: length of the range */ + unsigned long length; + + /* IN: not used right now */ + int prot; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h new file mode 100644 index 000000000000..325f2b2026e8 --- /dev/null +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -0,0 +1,88 @@ +/* + * virtio and hyperviosr service module (VHM): memory map + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_MM_H__ +#define __ACRN_VHM_MM_H__ + +#include +#include + +#define MMU_MEM_ATTR_READ 0x00000001 +#define MMU_MEM_ATTR_WRITE 0x00000002 +#define MMU_MEM_ATTR_EXECUTE 0x00000004 +#define MMU_MEM_ATTR_WB_CACHE 0x00000040 +#define MMU_MEM_ATTR_WT_CACHE 0x00000080 +#define MMU_MEM_ATTR_UNCACHED 0x00000100 +#define MMU_MEM_ATTR_WC 0x00000200 + +#define MMU_MEM_ATTR_ALL 0x00000007 +#define MMU_MEM_ATTR_WP 0x00000005 +#define MMU_MEM_ATTR_ALL_WB 0x00000047 +#define MMU_MEM_ATTR_ALL_WC 0x00000207 + +int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); +int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); +int update_mem_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); + +int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); + +int check_guest_mem(struct vhm_vm *vm); +void free_guest_mem(struct vhm_vm *vm); + +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); +int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); + +#endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index c07163dbc3bd..e098a1f959bf 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index d8c81b6e9306..872092490259 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,4 +64,44 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* Guest memory management */ +#define IC_ID_MEM_BASE 0x300UL +#define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) +#define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) + +#define SPECNAMELEN 63 + +enum { + VM_SYSMEM, + VM_BOOTROM, + VM_FRAMEBUFFER, + VM_MMIO, +}; + +struct vm_memseg { + int segid; + size_t len; + char name[SPECNAMELEN + 1]; + unsigned long gpa; +}; + +struct vm_memmap { + int segid; /* memory segment */ + union { + struct { + uint64_t gpa; + uint64_t segoff; /* offset into memory segment */ + size_t len; /* mmap length */ + int prot; /* RWX */ + int flags; + } mem; + struct { + uint64_t gpa; + uint64_t hpa; + size_t len; + int prot; + } mmio; + }; +}; + #endif /* VHM_IOCTL_DEFS_H */ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index dcb246af561a..4f1a0db2c54d 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -66,6 +66,8 @@ struct vhm_vm { struct list_head list; unsigned long vmid; long refcnt; + struct mutex seg_lock; + struct list_head memseg_list; }; struct vhm_vm *find_get_vm(unsigned long vmid); From b10850a26917d8908e2627535b8531a1c78ebbe5 Mon Sep 17 00:00:00 2001 From: Jason Zeng Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0804/1276] VHM: add guest memory remote mapping support There is use case which needs do data operation based on guest physical address. This patch added such support to do remote mapping for guest physical memory. Change-Id: I37755ddcf742129d272f535e99a070965e01c01e Tracked-On: 218445 Signed-off-by: Jason Zeng Signed-off-by: liang ding Signed-off-by: Jason Chen CJ Signed-off-by: Min He Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 79 +++++++++++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 2 + 2 files changed, 81 insertions(+) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 9dd0b9414d3a..ea7604b19aaf 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -362,3 +362,82 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) mutex_unlock(&vm->seg_lock); return -EINVAL; } + +static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa > guest_phys || + guest_phys >= seg->gpa + seg->len) + continue; + + if (guest_phys + size > seg->gpa + seg->len) { + mutex_unlock(&vm->seg_lock); + return NULL; + } + + mutex_unlock(&vm->seg_lock); + return phys_to_virt(seg->base + guest_phys - seg->gpa); + } + mutex_unlock(&vm->seg_lock); + return NULL; +} + +void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) +{ + struct vhm_vm *vm; + void *ret; + + vm = find_get_vm(vmid); + if (vm == NULL) + return NULL; + + ret = do_map_guest_phys(vm, guest_phys, size); + + put_vm(vm); + + return ret; +} +EXPORT_SYMBOL(map_guest_phys); + +static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa <= guest_phys && + guest_phys < seg->gpa + seg->len) { + mutex_unlock(&vm->seg_lock); + return 0; + } + } + mutex_unlock(&vm->seg_lock); + + return -ESRCH; +} + +int unmap_guest_phys(unsigned long vmid, u64 guest_phys) +{ + struct vhm_vm *vm; + int ret; + + vm = find_get_vm(vmid); + if (vm == NULL) { + pr_warn("vm_list corrupted\n"); + return -ESRCH; + } + + ret = do_unmap_guest_phys(vm, guest_phys); + put_vm(vm); + return ret; +} +EXPORT_SYMBOL(unmap_guest_phys); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 325f2b2026e8..e701254bc249 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -70,6 +70,8 @@ #define MMU_MEM_ATTR_ALL_WB 0x00000047 #define MMU_MEM_ATTR_ALL_WC 0x00000207 +void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); +int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, From fcb10cb4c557614ada052778d1a18e8ebb489fda Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0805/1276] VHM: add ioreq service support Once there is an IO request, a virtual irq will be injected into service OS by ACRN hypervisor. The VHM handles this virtual irq (which is based on an ipi vector), parses corresponding IO request from shared IOReq buffer then distributes it to different ioreq client. This patch added ioreq service, and defines IOReq APIs like below: int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); void acrn_ioreq_destroy_client(int client_id); int acrn_ioreq_add_iorange(int client_id, enum request_type type, long start, long end); int acrn_ioreq_del_iorange(int client_id, enum request_type type, long start, long end); struct vhm_request * acrn_ioreq_get_reqbuf(int client_id); int acrn_ioreq_attach_client(int client_id); int acrn_ioreq_distribute_request(struct vhm_vm *vm); int acrn_ioreq_complete_request(int client_id); Change-Id: I828744cb60e1c77543e1fafaa372597173039846 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: liang ding Signed-off-by: Xiao Zheng Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 88 +++ drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hypercall.c | 10 + drivers/vhm/vhm_ioreq.c | 922 +++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 2 + include/linux/vhm/acrn_common.h | 117 ++++ include/linux/vhm/acrn_hv_defs.h | 5 + include/linux/vhm/acrn_vhm_ioreq.h | 86 +++ include/linux/vhm/vhm_hypercall.h | 3 + include/linux/vhm/vhm_ioctl_defs.h | 8 + include/linux/vhm/vhm_vm_mngt.h | 5 + 11 files changed, 1247 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_ioreq.c create mode 100644 include/linux/vhm/acrn_vhm_ioreq.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3ea8de27cb3e..3129a8f1503b 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -78,6 +78,7 @@ #include #include +#include #include #include #include @@ -88,6 +89,8 @@ static int major; static struct class *vhm_class; static struct device *vhm_device; +static struct tasklet_struct vhm_io_req_tasklet; +static atomic_t ioreq_retry = ATOMIC_INIT(0); static int vhm_dev_open(struct inode *inodep, struct file *filep) { @@ -104,6 +107,9 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) INIT_LIST_HEAD(&vm->memseg_list); mutex_init(&vm->seg_lock); + INIT_LIST_HEAD(&vm->ioreq_client_list); + spin_lock_init(&vm->ioreq_client_lock); + vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; vm_list_add(&vm->list); @@ -188,6 +194,50 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_SET_IOREQ_BUFFER: { + /* init ioreq buffer */ + ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); + if (ret < 0) + return ret; + break; + } + + case IC_CREATE_IOREQ_CLIENT: { + int client_id; + + client_id = acrn_ioreq_create_fallback_client(vm->vmid, "acrndm"); + if (client_id < 0) + return -EFAULT; + return client_id; + } + + case IC_DESTROY_IOREQ_CLIENT: { + int client = ioctl_param; + + acrn_ioreq_destroy_client(client); + break; + } + + case IC_ATTACH_IOREQ_CLIENT: { + int client = ioctl_param; + + return acrn_ioreq_attach_client(client, 0); + } + + case IC_NOTIFY_REQUEST_FINISH: { + struct acrn_ioreq_notify notify; + + if (copy_from_user(¬ify, (void *)ioctl_param, + sizeof(notify))) + return -EFAULT; + + ret = acrn_ioreq_complete_request(notify.client_id, + notify.vcpu_mask); + if (ret < 0) + return -EFAULT; + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; @@ -197,6 +247,31 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } +static void io_req_tasklet(unsigned long data) +{ + struct vhm_vm *vm; + + list_for_each_entry(vm, &vhm_vm_list, list) { + if (!vm || !vm->req_buf) + break; + + acrn_ioreq_distribute_request(vm); + } + + if (atomic_read(&ioreq_retry) > 0) { + atomic_dec(&ioreq_retry); + tasklet_schedule(&vhm_io_req_tasklet); + } +} + +static void vhm_intr_handler(void) +{ + if (test_bit(TASKLET_STATE_SCHED, &(vhm_io_req_tasklet.state))) + atomic_inc(&ioreq_retry); + else + tasklet_schedule(&vhm_io_req_tasklet); +} + static int vhm_dev_release(struct inode *inodep, struct file *filep) { struct vhm_vm *vm = filep->private_data; @@ -217,10 +292,13 @@ static const struct file_operations fops = { .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, + .poll = vhm_dev_poll, }; static int __init vhm_init(void) { + unsigned long flag; + pr_info("vhm: initializing\n"); /* Try to dynamically allocate a major number for the device */ @@ -249,12 +327,22 @@ static int __init vhm_init(void) pr_warn("vhm: failed to create the device\n"); return PTR_ERR(vhm_device); } + pr_info("register IPI handler\n"); + tasklet_init(&vhm_io_req_tasklet, io_req_tasklet, 0); + if (x86_platform_ipi_callback) { + pr_warn("vhm: ipi callback was occupied\n"); + return -EINVAL; + } + local_irq_save(flag); + x86_platform_ipi_callback = vhm_intr_handler; + local_irq_restore(flag); pr_info("vhm: Virtio & Hypervisor service module initialized\n"); return 0; } static void __exit vhm_exit(void) { + tasklet_kill(&vhm_io_req_tasklet); device_destroy(vhm_class, MKDEV(major, 0)); class_unregister(vhm_class); class_destroy(vhm_class); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 7e5ec421fbc7..4bd960d564b3 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d80087bcb5fb..1b25f4ec4d06 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,16 @@ #include #include +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) +{ + return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); +} + +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +{ + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c new file mode 100644 index 000000000000..6054e3d00eb2 --- /dev/null +++ b/drivers/vhm/vhm_ioreq.c @@ -0,0 +1,922 @@ +/* + * virtio and hyperviosr service module (VHM): ioreq multi client feature + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ioreq_range { + struct list_head list; + enum request_type type; + long start; + long end; +}; + +struct ioreq_client { + /* client name */ + char name[16]; + /* client id */ + int id; + /* vm this client belongs to */ + unsigned long vmid; + /* list node for this ioreq_client */ + struct list_head list; + /* + * is this client fallback? + * there is only one fallback client in a vm - dm + * a fallback client shares IOReq buffer pages + * a fallback client handles all left IOReq not handled by other clients + * a fallback client does not need add io ranges + * a fallback client handles ioreq in its own context + */ + bool fallback; + + bool destroying; + bool kthread_exit; + + /* client covered io ranges - N/A for fallback client */ + struct list_head range_list; + spinlock_t range_lock; + + /* + * this req records the req number this client need handle + */ + atomic_t req; + + /* + * client ioreq handler: + * if client provides a handler, it means vhm need create a kthread + * to call the handler while there is ioreq. + * if client doesn't provide a handler, client should handle ioreq + * in its own context when calls acrn_ioreq_attach_client. + * + * NOTE: for fallback client, there is no ioreq handler. + */ + ioreq_handler_t handler; + bool vhm_create_kthread; + struct task_struct *thread; + wait_queue_head_t wq; + + /* pci bdf trap */ + bool trap_bdf; + int pci_bus; + int pci_dev; + int pci_func; +}; + +#define MAX_CLIENT 64 +static struct ioreq_client *clients[MAX_CLIENT]; +static DECLARE_BITMAP(client_bitmap, MAX_CLIENT); + +static void acrn_ioreq_notify_client(struct ioreq_client *client); + +static inline bool is_range_type(enum request_type type) +{ + return (type == REQ_MMIO || type == REQ_PORTIO || type == REQ_WP); +} + +static int alloc_client(void) +{ + struct ioreq_client *client; + int i; + + i = find_first_zero_bit(client_bitmap, MAX_CLIENT); + if (i >= MAX_CLIENT) + return -ENOMEM; + set_bit(i, client_bitmap); + + client = kzalloc(sizeof(struct ioreq_client), GFP_KERNEL); + if (!client) + return -ENOMEM; + client->id = i; + clients[i] = client; + + return i; +} + +static void free_client(int i) +{ + if (i < MAX_CLIENT && i >= 0) { + if (test_and_clear_bit(i, client_bitmap)) { + kfree(clients[i]); + clients[i] = NULL; + } + } +} + +int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, + char *name) +{ + struct vhm_vm *vm; + struct ioreq_client *client; + unsigned long flags; + int client_id; + + might_sleep(); + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + if (unlikely(vm->req_buf == NULL)) { + pr_err("vhm-ioreq: vm[%ld]'s reqbuf is not ready\n", + vmid); + put_vm(vm); + return -EINVAL; + } + + client_id = alloc_client(); + if (unlikely(client_id < 0)) { + pr_err("vhm-ioreq: vm[%ld] failed to alloc ioreq " + "client id\n", vmid); + put_vm(vm); + return -EINVAL; + } + + client = clients[client_id]; + + if (handler) { + client->handler = handler; + client->vhm_create_kthread = true; + } + + client->vmid = vmid; + if (name) + strncpy(client->name, name, 16); + spin_lock_init(&client->range_lock); + INIT_LIST_HEAD(&client->range_list); + init_waitqueue_head(&client->wq); + + spin_lock_irqsave(&vm->ioreq_client_lock, flags); + list_add(&client->list, &vm->ioreq_client_list); + spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); + + put_vm(vm); + + pr_info("vhm-ioreq: created ioreq client %d\n", client_id); + + return client_id; +} + +int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name) +{ + struct vhm_vm *vm; + int client_id; + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + + if (unlikely(vm->ioreq_fallback_client > 0)) { + pr_err("vhm-ioreq: there is already fallback " + "client exist for vm %ld\n", + vmid); + put_vm(vm); + return -EINVAL; + } + + client_id = acrn_ioreq_create_client(vmid, NULL, name); + if (unlikely(client_id < 0)) { + put_vm(vm); + return -EINVAL; + } + + clients[client_id]->fallback = true; + vm->ioreq_fallback_client = client_id; + + put_vm(vm); + + return client_id; +} + +static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, + struct vhm_vm *vm) +{ + struct list_head *pos, *tmp; + unsigned long flags; + + /* blocking operation: notify client for cleanup + * if waitqueue not active, it means client is handling request, + * at that time, we need wait client finish its handling. + */ + while (!waitqueue_active(&client->wq) && !client->kthread_exit) + msleep(10); + client->destroying = true; + acrn_ioreq_notify_client(client); + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each_safe(pos, tmp, &client->range_list) { + struct ioreq_range *range = + container_of(pos, struct ioreq_range, list); + list_del(&range->list); + kfree(range); + } + spin_unlock_irqrestore(&client->range_lock, flags); + + spin_lock_irqsave(&vm->ioreq_client_lock, flags); + list_del(&client->list); + spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); + free_client(client->id); + + if (client->id == vm->ioreq_fallback_client) + vm->ioreq_fallback_client = -1; +} + +void acrn_ioreq_destroy_client(int client_id) +{ + struct vhm_vm *vm; + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + + might_sleep(); + + vm = find_get_vm(client->vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + client->vmid); + return; + } + + acrn_ioreq_destroy_client_pervm(client, vm); + + put_vm(vm); +} + +static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) +{ + struct list_head *pos; + unsigned long flags; + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each(pos, &client->range_list) { + struct ioreq_range *range = + container_of(pos, struct ioreq_range, list); + pr_debug("\tio range: type %d, start 0x%lx, " + "end 0x%lx\n", range->type, range->start, range->end); + } + spin_unlock_irqrestore(&client->range_lock, flags); +} + +/* + * NOTE: here just add iorange entry directly, no check for the overlap.. + * please client take care of it + */ +int acrn_ioreq_add_iorange(int client_id, enum request_type type, + long start, long end) +{ + struct ioreq_client *client; + struct ioreq_range *range; + unsigned long flags; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (end < start) { + pr_err("vhm-ioreq: end < start\n"); + return -EFAULT; + } + + might_sleep(); + + range = kzalloc(sizeof(struct ioreq_range), GFP_KERNEL); + if (!range) { + pr_err("vhm-ioreq: failed to alloc ioreq range\n"); + return -ENOMEM; + } + range->type = type; + range->start = start; + range->end = end; + + spin_lock_irqsave(&client->range_lock, flags); + list_add(&range->list, &client->range_list); + spin_unlock_irqrestore(&client->range_lock, flags); + + return 0; +} + +int acrn_ioreq_del_iorange(int client_id, enum request_type type, + long start, long end) +{ + struct ioreq_client *client; + struct ioreq_range *range; + struct list_head *pos, *tmp; + unsigned long flags; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (end < start) { + pr_err("vhm-ioreq: end < start\n"); + return -EFAULT; + } + + might_sleep(); + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each_safe(pos, tmp, &client->range_list) { + range = container_of(pos, struct ioreq_range, list); + if (range->type == type) { + if (is_range_type(type)) { + if (start == range->start && + end == range->end) { + list_del(&range->list); + kfree(range); + break; + } + } else { + list_del(&range->list); + kfree(range); + break; + } + } + } + spin_unlock_irqrestore(&client->range_lock, flags); + + return 0; +} + +static inline bool is_destroying(struct ioreq_client *client) +{ + if (client) + return client->destroying; + else + return true; +} + +static inline bool has_pending_request(struct ioreq_client *client) +{ + if (client) + return (atomic_read(&client->req) > 0); + else + return false; +} + +struct vhm_request *acrn_ioreq_get_reqbuf(int client_id) +{ + struct ioreq_client *client; + struct vhm_vm *vm; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return NULL; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return NULL; + } + vm = find_get_vm(client->vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + client->vmid); + return NULL; + } + + if (vm->req_buf == NULL) { + pr_warn("vhm-ioreq: the req buf page not ready yet " + "for vmid %ld\n", client->vmid); + } + put_vm(vm); + return (struct vhm_request *)vm->req_buf; +} + +static int ioreq_client_thread(void *data) +{ + struct ioreq_client *client; + int ret, client_id = (unsigned long)data; + + while (1) { + client = clients[client_id]; + if (is_destroying(client)) { + pr_info("vhm-ioreq: client destroying->stop thread\n"); + break; + } + if (has_pending_request(client)) { + if (client->handler) { + ret = client->handler(client->id, + client->req.counter); + if (ret < 0) + BUG(); + } else { + pr_err("vhm-ioreq: no ioreq handler\n"); + break; + } + } else + wait_event_freezable(client->wq, + (has_pending_request(client) || + is_destroying(client))); + } + + return 0; +} + +int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (client->vhm_create_kthread) { + if (client->thread) { + pr_warn("vhm-ioreq: kthread already exist" + " for client %s\n", client->name); + return 0; + } + client->thread = kthread_run(ioreq_client_thread, + (void *)(unsigned long)client_id, + "ioreq_client[%ld]:%s", + client->vmid, client->name); + if (IS_ERR(client->thread)) { + pr_err("vhm-ioreq: failed to run kthread " + "for client %s\n", client->name); + return -ENOMEM; + } + } else { + might_sleep(); + + if (check_kthread_stop) { + wait_event_freezable(client->wq, + (kthread_should_stop() || + has_pending_request(client) || + is_destroying(client))); + if (kthread_should_stop()) + client->kthread_exit = true; + } else { + wait_event_freezable(client->wq, + (has_pending_request(client) || + is_destroying(client))); + } + + if (is_destroying(client)) + return 1; + } + + return 0; +} + +void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client->trap_bdf = true; + client->pci_bus = bus; + client->pci_dev = dev; + client->pci_func = func; +} + +void acrn_ioreq_unintercept_bdf(int client_id) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client->trap_bdf = false; + client->pci_bus = -1; + client->pci_dev = -1; + client->pci_func = -1; +} + +static void acrn_ioreq_notify_client(struct ioreq_client *client) +{ + /* if client thread is in waitqueue, wake up it */ + if (waitqueue_active(&client->wq)) + wake_up_interruptible(&client->wq); +} + +static bool req_in_range(struct ioreq_range *range, struct vhm_request *req) +{ + bool ret = false; + + if (range->type == req->type) { + switch (req->type) { + case REQ_MMIO: + case REQ_WP: + { + if (req->reqs.mmio_request.address >= range->start && + (req->reqs.mmio_request.address + + req->reqs.mmio_request.size - 1) <= range->end) + ret = true; + break; + } + case REQ_PORTIO: { + if (req->reqs.pio_request.address >= range->start && + (req->reqs.pio_request.address + + req->reqs.pio_request.size - 1) <= range->end) + ret = true; + break; + } + case REQ_MSR: /*TODO: add bitmap for MSR range */ + case REQ_CPUID: + case REQ_EXIT: + { + ret = true; + break; + } + + default: + ret = false; + break; + } + } + + return ret; +} + +static bool is_cfg_addr(struct vhm_request *req) +{ + return (req->type == REQ_PORTIO && + (req->reqs.pio_request.address >= 0xcf8 && + req->reqs.pio_request.address < 0xcf8+4)); +} + +static bool is_cfg_data(struct vhm_request *req) +{ + return (req->type == REQ_PORTIO && + (req->reqs.pio_request.address >= 0xcfc && + req->reqs.pio_request.address < 0xcfc+4)); +} + +static int cached_bus; +static int cached_dev; +static int cached_func; +static int cached_reg; +static int cached_enable; +#define PCI_REGMAX 255 /* highest supported config register addr.*/ +#define PCI_FUNCMAX 7 /* highest supported function number */ +#define PCI_SLOTMAX 31 /* highest supported slot number */ +#define PCI_BUSMAX 255 /* highest supported bus number */ +#define CONF1_ENABLE 0x80000000ul +static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) +{ + int req_handled = 0; + + /*XXX: like DM, assume cfg address write is size 4 */ + if (is_cfg_addr(req)) { + if (req->reqs.pio_request.direction == REQUEST_WRITE) { + if (req->reqs.pio_request.size == 4) { + int value = req->reqs.pio_request.value; + + cached_bus = (value >> 16) & PCI_BUSMAX; + cached_dev = (value >> 11) & PCI_SLOTMAX; + cached_func = (value >> 8) & PCI_FUNCMAX; + cached_reg = value & PCI_REGMAX; + cached_enable = + (value & CONF1_ENABLE) == CONF1_ENABLE; + req_handled = 1; + } + } else { + if (req->reqs.pio_request.size == 4) { + req->reqs.pio_request.value = + (cached_bus << 16) | + (cached_dev << 11) | (cached_func << 8) + | cached_reg; + if (cached_enable) + req->reqs.pio_request.value |= + CONF1_ENABLE; + req_handled = 1; + } + } + } else if (is_cfg_data(req)) { + if (!cached_enable) { + if (req->reqs.pio_request.direction == REQUEST_READ) + req->reqs.pio_request.value = 0xffffffff; + req_handled = 1; + } else { + /* pci request is same as io request at top */ + int offset = req->reqs.pio_request.address - 0xcfc; + + req->type = REQ_PCICFG; + req->reqs.pci_request.bus = cached_bus; + req->reqs.pci_request.dev = cached_dev; + req->reqs.pci_request.func = cached_func; + req->reqs.pci_request.reg = cached_reg + offset; + } + } + + if (req_handled) { + req->processed = REQ_STATE_SUCCESS; + if (hcall_notify_req_finish(vm->vmid, 1 << vcpu) < 0) { + pr_err("vhm-ioreq: failed to " + "notify request finished !\n"); + return -EFAULT; + } + } + + return req_handled; +} + +static bool bdf_match(struct ioreq_client *client) +{ + return (client->trap_bdf && + client->pci_bus == cached_bus && + client->pci_dev == cached_dev && + client->pci_func == cached_func); +} + +static struct ioreq_client *acrn_ioreq_find_client_by_request(struct vhm_vm *vm, + struct vhm_request *req) +{ + struct list_head *pos, *range_pos; + struct ioreq_client *client; + struct ioreq_client *target_client = NULL, *fallback_client = NULL; + struct ioreq_range *range; + bool found = false; + + spin_lock(&vm->ioreq_client_lock); + list_for_each(pos, &vm->ioreq_client_list) { + client = container_of(pos, struct ioreq_client, list); + + if (client->fallback) { + fallback_client = client; + continue; + } + + if (req->type == REQ_PCICFG) { + if (bdf_match(client)) { /* bdf match client */ + target_client = client; + break; + } else /* other or fallback client */ + continue; + } + + spin_lock(&client->range_lock); + list_for_each(range_pos, &client->range_list) { + range = + container_of(range_pos, struct ioreq_range, list); + if (req_in_range(range, req)) { + found = true; + target_client = client; + break; + } + } + spin_unlock(&client->range_lock); + + if (found) + break; + } + spin_unlock(&vm->ioreq_client_lock); + + if (target_client) + return target_client; + + if (fallback_client) + return fallback_client; + + return NULL; +} + +int acrn_ioreq_distribute_request(struct vhm_vm *vm) +{ + struct vhm_request *req; + struct list_head *pos; + struct ioreq_client *client; + int i; + + /* TODO: replace VHM_REQUEST_MAX with vcpu num get at runtime */ + for (i = 0; i < VHM_REQUEST_MAX; i++) { + req = vm->req_buf->req_queue + i; + if (req->valid && (req->processed == REQ_STATE_PENDING)) { + if (handle_cf8cfc(vm, req, i)) + continue; + client = acrn_ioreq_find_client_by_request(vm, req); + if (client == NULL) { + pr_err("vhm-ioreq: failed to " + "find ioreq client -> " + "BUG\n"); + BUG(); + } else { + req->processed = REQ_STATE_PROCESSING; + req->client = client->id; + atomic_inc(&client->req); + } + } + } + + spin_lock(&vm->ioreq_client_lock); + list_for_each(pos, &vm->ioreq_client_list) { + client = container_of(pos, struct ioreq_client, list); + if (has_pending_request(client)) + acrn_ioreq_notify_client(client); + } + spin_unlock(&vm->ioreq_client_lock); + + return 0; +} + +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) +{ + struct ioreq_client *client; + int ret; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EINVAL; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EINVAL; + } + + atomic_sub(bitmap_weight((unsigned long *)&vcpu_mask, + VHM_REQUEST_MAX), &client->req); + ret = hcall_notify_req_finish(client->vmid, vcpu_mask); + if (ret < 0) { + pr_err("vhm-ioreq: failed to notify request finished !\n"); + return -EFAULT; + } + + return 0; +} + +unsigned int vhm_dev_poll(struct file *filep, poll_table *wait) +{ + struct vhm_vm *vm = filep->private_data; + struct ioreq_client *fallback_client; + unsigned int ret = 0; + + if (vm == NULL || vm->req_buf == NULL || + vm->ioreq_fallback_client <= 0) { + pr_err("vhm: invalid VM !\n"); + ret = POLLERR; + return ret; + } + + fallback_client = clients[vm->ioreq_fallback_client]; + if (!fallback_client) { + pr_err("vhm-ioreq: no client for id %d\n", + vm->ioreq_fallback_client); + return -EINVAL; + } + + poll_wait(filep, &fallback_client->wq, wait); + if (has_pending_request(fallback_client) || + is_destroying(fallback_client)) + ret = POLLIN | POLLRDNORM; + + return ret; +} + +int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) +{ + struct acrn_set_ioreq_buffer set_buffer; + struct page *page; + int ret; + + if (vm->req_buf) + BUG(); + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("vhm-ioreq: failed to pin request buffer!\n"); + return -ENOMEM; + } + + vm->req_buf = page_address(page); + vm->pg = page; + + set_buffer.req_buf = (long) page_to_phys(page); + + ret = hcall_set_ioreq_buffer(vm->vmid, virt_to_phys(&set_buffer)); + if (ret < 0) { + pr_err("vhm-ioreq: failed to set request buffer !\n"); + return -EFAULT; + } + + /* reserve 0, let client_id start from 1 */ + set_bit(0, client_bitmap); + + pr_info("vhm-ioreq: init request buffer @ %p!\n", + vm->req_buf); + + return 0; +} + +void acrn_ioreq_free(struct vhm_vm *vm) +{ + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, &vm->ioreq_client_list) { + struct ioreq_client *client = + container_of(pos, struct ioreq_client, list); + acrn_ioreq_destroy_client_pervm(client, vm); + } + + if (vm->req_buf && vm->pg) { + put_page(vm->pg); + vm->pg = NULL; + vm->req_buf = NULL; + } +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 3c4e6d2b2f23..564435f2bb40 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ void put_vm(struct vhm_vm *vm) if (vm->refcnt == 0) { list_del(&vm->list); free_guest_mem(vm); + acrn_ioreq_free(vm); kfree(vm); pr_info("vhm: freed vm\n"); } diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 08e47732f4d0..bc2237331231 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,10 +56,127 @@ * Commmon structures for ACRN/VHM/DM */ +/* + * IO request + */ +#define VHM_REQUEST_MAX 16 + +enum request_state { + REQ_STATE_SUCCESS = 1, + REQ_STATE_PENDING = 0, + REQ_STATE_PROCESSING = 2, + REQ_STATE_FAILED = -1, +} __attribute__((aligned(4))); + +enum request_type { + REQ_MSR, + REQ_CPUID, + REQ_PORTIO, + REQ_MMIO, + REQ_PCICFG, + REQ_WP, + REQ_EXIT, + REQ_MAX, +} __attribute__((aligned(4))); + +enum request_direction { + REQUEST_READ, + REQUEST_WRITE, + DIRECTION_MAX, +} __attribute__((aligned(4))); + +struct msr_request { + enum request_direction direction; + long index; + long value; +} __attribute__((aligned(8))); + +struct cpuid_request { + long eax_in; + long ecx_in; + long eax_out; + long ebx_out; + long ecx_out; + long edx_out; +} __attribute__((aligned(8))); + +struct mmio_request { + enum request_direction direction; + long address; + long size; + long value; +} __attribute__((aligned(8))); + +struct io_request { + enum request_direction direction; + long address; + long size; + int value; +} __attribute__((aligned(8))); + +struct pci_request { + enum request_direction direction; + long reserve; /*io_request address*/ + long size; + int value; + int bus; + int dev; + int func; + int reg; +} __attribute__((aligned(8))); + +/* vhm_request are 256Bytes aligned */ +struct vhm_request { + /* offset: 0bytes - 63bytes */ + enum request_type type; + int reserved0[15]; + + /* offset: 64bytes-127bytes */ + union { + struct msr_request msr_request; + struct cpuid_request cpuid_request; + struct io_request pio_request; + struct pci_request pci_request; + struct mmio_request mmio_request; + long reserved1[8]; + } reqs; + + /* True: valid req which need VHM to process. + * ACRN write, VHM read only + **/ + int valid; + + /* the client which is distributed to handle this request */ + int client; + + /* 1: VHM had processed and success + * 0: VHM had not yet processed + * -1: VHM failed to process. Invalid request + * VHM write, ACRN read only + **/ + enum request_state processed; +} __attribute__((aligned(256))); + +struct vhm_request_buffer { + union { + struct vhm_request req_queue[VHM_REQUEST_MAX]; + char reserved[4096]; + }; +} __attribute__((aligned(4096))); + /* Common API params */ struct acrn_create_vm { unsigned long vmid; /* OUT: HV return vmid to VHM */ unsigned long vcpu_num; /* IN: VM vcpu number */ } __attribute__((aligned(8))); +struct acrn_set_ioreq_buffer { + long req_buf; /* IN: gpa of per VM request_buffer*/ +} __attribute__((aligned(8))); + +struct acrn_ioreq_notify { + int client_id; + unsigned long vcpu_mask; +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index ab6554d017cb..f57f2b62e972 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,6 +74,11 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* DM ioreq management */ +#define HC_ID_IOREQ_BASE 0x200UL +#define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) +#define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) + /* Guest memory management */ #define HC_ID_MEM_BASE 0x300UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h new file mode 100644 index 000000000000..0daf46dcf9f7 --- /dev/null +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -0,0 +1,86 @@ +/* + * virtio and hyperviosr service module (VHM): ioreq multi client feature + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_IOREQ_H__ +#define __ACRN_VHM_IOREQ_H__ + +#include +#include + +typedef int (*ioreq_handler_t)(int client_id, int req); + +int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, + char *name); +void acrn_ioreq_destroy_client(int client_id); + +int acrn_ioreq_add_iorange(int client_id, enum request_type type, + long start, long end); +int acrn_ioreq_del_iorange(int client_id, enum request_type type, + long start, long end); + +struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); +int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); + +int acrn_ioreq_distribute_request(struct vhm_vm *vm); +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask); + +void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); +void acrn_ioreq_unintercept_bdf(int client_id); + +/* IOReq APIs */ +int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma); +void acrn_ioreq_free(struct vhm_vm *vm); +int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name); +unsigned int vhm_dev_poll(struct file *filep, poll_table *wait); + +#endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e098a1f959bf..86b5f579687a 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,9 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); +inline long hcall_notify_req_finish(unsigned long vmid, + unsigned long vcpu_mask); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 872092490259..01adcfade99c 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,6 +64,14 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* DM ioreq management */ +#define IC_ID_IOREQ_BASE 0x200UL +#define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) +#define IC_NOTIFY_REQUEST_FINISH _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x01) +#define IC_CREATE_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x02) +#define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) +#define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) + /* Guest memory management */ #define IC_ID_MEM_BASE 0x300UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 4f1a0db2c54d..eb410024157f 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -65,9 +65,14 @@ struct vhm_vm { struct device *dev; struct list_head list; unsigned long vmid; + int ioreq_fallback_client; long refcnt; struct mutex seg_lock; struct list_head memseg_list; + spinlock_t ioreq_client_lock; + struct list_head ioreq_client_list; + struct vhm_request_buffer *req_buf; + struct page *pg; }; struct vhm_vm *find_get_vm(unsigned long vmid); From efa73b89480c08c099d41b455bbd1084f385c7bf Mon Sep 17 00:00:00 2001 From: liang ding Date: Fri, 29 Dec 2017 16:38:20 +0800 Subject: [PATCH 0806/1276] VHM: add interrupt injection support VHM provides interrupt injection service for emulated devices. this patch added interrupt injection support APIs. Change-Id: I10385318877aa52026d6d2fc56f5fdbc8106bbd9 Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Xiao Zheng Signed-off-by: Jason Chen CJ Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 29 +++++++++++++++ drivers/vhm/vhm_hypercall.c | 59 ++++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 18 +++++++++ include/linux/vhm/acrn_common.h | 32 ++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 7 ++++ include/linux/vhm/vhm_hypercall.h | 4 ++ include/linux/vhm/vhm_ioctl_defs.h | 7 ++++ include/linux/vhm/vhm_vm_mngt.h | 2 + 8 files changed, 158 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3129a8f1503b..97f7c466e11d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -238,6 +238,35 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_ASSERT_IRQLINE: { + ret = vhm_assert_irqline(vm, ioctl_param); + break; + } + + case IC_DEASSERT_IRQLINE: { + ret = vhm_deassert_irqline(vm, ioctl_param); + break; + } + + case IC_PULSE_IRQLINE: { + ret = vhm_pulse_irqline(vm, ioctl_param); + break; + } + + case IC_INJECT_MSI: { + struct acrn_msi_entry msi; + + if (copy_from_user(&msi, (void *)ioctl_param, sizeof(msi))) + return -EFAULT; + + ret = hcall_inject_msi(vm->vmid, virt_to_phys(&msi)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 1b25f4ec4d06..dc87d30151d5 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,11 @@ #include #include +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) +{ + return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); @@ -147,3 +152,57 @@ inline long vhm_query_vm_state(struct vhm_vm *vm) return ret; } + +inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_ASSERT_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_DEASSERT_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to deassert irq!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_PULSE_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } + + return ret; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 564435f2bb40..048ab41f4f9c 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -95,6 +95,24 @@ void put_vm(struct vhm_vm *vm) mutex_unlock(&vhm_vm_list_lock); } +int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, + unsigned long msi_data) +{ + struct acrn_msi_entry msi; + int ret; + + /* msi_addr: addr[19:12] with dest vcpu id */ + /* msi_data: data[7:0] with vector */ + msi.msi_addr = msi_addr; + msi.msi_data = msi_data; + ret = hcall_inject_msi(vmid, virt_to_phys(&msi)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + return 0; +} + void vm_list_add(struct list_head *list) { list_add(list, &vhm_vm_list); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index bc2237331231..cafb171490ca 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,6 +56,20 @@ * Commmon structures for ACRN/VHM/DM */ +enum irq_mode { + IRQ_PULSE, + IRQ_ASSERT, + IRQ_DEASSERT, +} __attribute__((aligned(4))); + +/* ISA type + * inject interrut to both PIC and IOAPIC + */ +enum interrupt_type { + ACRN_INTR_TYPE_ISA, + ACRN_INTR_TYPE_IOAPIC, +} __attribute__((aligned(4))); + /* * IO request */ @@ -179,4 +193,22 @@ struct acrn_ioreq_notify { unsigned long vcpu_mask; } __attribute__((aligned(8))); +/* For ISA, PIC, IOAPIC etc */ +struct acrn_irqline { + enum interrupt_type intr_type; + unsigned long pic_irq; /* IN: for ISA type */ + unsigned long ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ +} __attribute__((aligned(8))); + +/* For MSI type inject */ +struct acrn_msi_entry { + unsigned long msi_addr; /* IN: addr[19:12] with dest vcpu id */ + unsigned long msi_data; /* IN: data[7:0] with vector */ +} __attribute__((aligned(8))); + +/* For NMI inject */ +struct acrn_nmi_entry { + unsigned long vcpuid; /* IN: -1 means vcpu0 */ +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f57f2b62e972..7b438cc01b48 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,6 +74,13 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* IRQ and Interrupts */ +#define HC_ID_IRQ_BASE 0x100UL +#define HC_ASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x00) +#define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) +#define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) +#define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) + /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x200UL #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 86b5f579687a..e372ea48fa81 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); @@ -148,5 +149,8 @@ inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); inline long vhm_destroy_vm(struct vhm_vm *vm); inline long vhm_query_vm_state(struct vhm_vm *vm); +inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 01adcfade99c..3be6aca40844 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,6 +64,13 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* IRQ and Interrupts */ +#define IC_ID_IRQ_BASE 0x100UL +#define IC_ASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x00) +#define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) +#define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) +#define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) + /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x200UL #define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index eb410024157f..fb02c00ec5e2 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -77,6 +77,8 @@ struct vhm_vm { struct vhm_vm *find_get_vm(unsigned long vmid); void put_vm(struct vhm_vm *vm); +int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, + unsigned long msi_data); void vm_list_add(struct list_head *list); void vm_mutex_lock(struct mutex *mlock); From 0a000ec7e1fd7d997c5802095102454048b9d1fb Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0807/1276] VHM: add API to get vm info Added API vhm_get_vm_info: get guest vm's max_vcpu & max_gfn Change-Id: Ibe668c75e893092a1e5ea824aa09d9b65825fabb Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 9 +++++++-- drivers/vhm/vhm_vm_mngt.c | 17 +++++++++++++++++ include/linux/vhm/vhm_vm_mngt.h | 7 +++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index ea7604b19aaf..61ebb8c508d2 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -117,6 +117,7 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) { struct guest_memseg *seg; u64 base; + int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) @@ -134,9 +135,13 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) strncpy(seg->name, memseg->name, SPECNAMELEN + 1); seg->gpa = memseg->gpa; + max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; + if (vm->max_gfn < max_gfn) + vm->max_gfn = max_gfn; + pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," - " and its guest gpa = 0x%llx\n", - seg->name, seg->len, seg->base, seg->gpa); + " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", + seg->name, seg->len, seg->base, seg->gpa, vm->max_gfn); seg->vma_count = 0; mutex_lock(&vm->seg_lock); diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 048ab41f4f9c..d1aa4ba1a4f0 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -95,6 +95,23 @@ void put_vm(struct vhm_vm *vm) mutex_unlock(&vhm_vm_list_lock); } +int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) +{ + struct vhm_vm *vm; + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + /*TODO: hardcode max_vcpu here, should be fixed by getting at runtime */ + info->max_vcpu = 4; + info->max_gfn = vm->max_gfn; + put_vm(vm); + return 0; +} + int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data) { diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index fb02c00ec5e2..77c21c4bba7a 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -69,14 +69,21 @@ struct vhm_vm { long refcnt; struct mutex seg_lock; struct list_head memseg_list; + int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; }; +struct vm_info { + int max_vcpu; + int max_gfn; +}; + struct vhm_vm *find_get_vm(unsigned long vmid); void put_vm(struct vhm_vm *vm); +int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); From 46a5cf2bdafcf75e39bfe770c59b0f0cdf402da3 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0808/1276] VHM: add API to do guest gpa2hpa translation Added API vhm_vm_gpa2hpa: do translation between gpa and hpa for corresponding guest. Change-Id: I5ccdc3c6ac73d02d854878957093895c7f0cbee6 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_vm_mngt.c | 16 ++++++++++++++++ include/linux/vhm/acrn_common.h | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 2 ++ include/linux/vhm/acrn_vhm_mm.h | 6 ++++++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_vm_mngt.h | 1 + 7 files changed, 36 insertions(+) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index dc87d30151d5..384b86e60c9c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -73,6 +73,11 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa) +{ + return acrn_hypercall2(HC_VM_GPA2HPA, vmid, gpa2hpa); +} + inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) { long ret = 0; diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index d1aa4ba1a4f0..8f1a00777dd4 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -130,6 +130,22 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, return 0; } +unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) +{ + struct vm_gpa2hpa gpa2hpa; + int ret; + + gpa2hpa.gpa = gpa; + gpa2hpa.hpa = -1UL; /* Init value as invalid gpa */ + ret = hcall_vm_gpa2hpa(vmid, virt_to_phys(&gpa2hpa)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + mb(); + return gpa2hpa.hpa; +} + void vm_list_add(struct list_head *list) { list_add(list, &vhm_vm_list); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index cafb171490ca..f0567aa0f1dd 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -211,4 +211,9 @@ struct acrn_nmi_entry { unsigned long vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); +struct vm_gpa2hpa { + unsigned long gpa; /* IN: gpa to translation */ + unsigned long hpa; /* OUT: -1 means invalid gpa */ +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 7b438cc01b48..d527a8fa8435 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -86,9 +86,11 @@ #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) #define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) + /* Guest memory management */ #define HC_ID_MEM_BASE 0x300UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) +#define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index e701254bc249..1af6fd3aa11b 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -70,6 +70,12 @@ #define MMU_MEM_ATTR_ALL_WB 0x00000047 #define MMU_MEM_ATTR_ALL_WC 0x00000207 +/* 1:1 mapping for service OS */ +static inline unsigned long acrn_hpa2gpa(unsigned long hpa) +{ + return hpa; +} + void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e372ea48fa81..f1ed9a07e708 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -144,6 +144,7 @@ inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 77c21c4bba7a..5edacb31dc1b 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -86,6 +86,7 @@ void put_vm(struct vhm_vm *vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); +unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa); void vm_list_add(struct list_head *list); void vm_mutex_lock(struct mutex *mlock); From c42ad8566d312ed5e732ab7fcbbe258eda31b1fe Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0809/1276] VHM: add passthrough device support add following ioctl in vhm_dev to support device passthrough - assign, deassign pass-through device ACRN_ASSIGN_PTDEV ACRN_DEASSIGN_PTDEV - set, reset pass-through device intr info ACRN_SET_PTDEV_INTR_INFO ACRN_RESET_PTDEV_INTR_INFO reuse exist ioctl to support device passthrough - BAR mapping ACRN_IOC_SET_MEMSEG - MSI support ACRN_VM_PCI_MSIX_REMAP Change-Id: I94bbee48e8de1faf70804061c65c2e2855e6bf0f Tracked-On: 218445 Signed-off-by: Gao, Shiqing Signed-off-by: Binbin Wu Signed-off-by: Edwin Zhai Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 25 +++++ drivers/vhm/vhm_hypercall.c | 175 +++++++++++++++++++++++++++++ include/linux/vhm/acrn_common.h | 43 +++++++ include/linux/vhm/acrn_hv_defs.h | 8 ++ include/linux/vhm/vhm_hypercall.h | 8 ++ include/linux/vhm/vhm_ioctl_defs.h | 9 ++ 6 files changed, 268 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 97f7c466e11d..97c20d82154f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -267,6 +267,31 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_ASSIGN_PTDEV: { + ret = vhm_assign_ptdev(vm, ioctl_param); + break; + } + + case IC_DEASSIGN_PTDEV: { + ret = vhm_deassign_ptdev(vm, ioctl_param); + break; + } + + case IC_SET_PTDEV_INTR_INFO: { + ret = vhm_set_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_RESET_PTDEV_INTR_INFO: { + ret = vhm_reset_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_VM_PCI_MSIX_REMAP: { + ret = vhm_remap_pci_msix(vm, ioctl_param); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 384b86e60c9c..0f3f6c1c5f4c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -50,14 +50,30 @@ */ #include #include +#include #include #include +/* max num of pass-through devices using msix */ +#define MAX_ENTRY 3 + +struct table_iomems { + /* device's virtual BDF */ + unsigned short virt_bdf; + /* virtual base address of MSI-X table in memory space after ioremap */ + unsigned long mmap_addr; +} tables[MAX_ENTRY]; + inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); } +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix) +{ + return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msix); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); @@ -211,3 +227,162 @@ inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) return ret; } + +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_ASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to assign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_DEASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to deassign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to set intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to reset intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_vm_pci_msix_remap msix_remap; + + if (copy_from_user(&msix_remap, + (void *)ioctl_param, sizeof(msix_remap))) + return -EFAULT; + + ret = acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vm->vmid, + virt_to_phys(&msix_remap)); + + if (copy_to_user((void *)ioctl_param, + &msix_remap, sizeof(msix_remap))) + return -EFAULT; + + if (msix_remap.msix) { + void __iomem *msix_entry; + int i; + + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf == msix_remap.virt_bdf) + break; + } + + if (!tables[i].mmap_addr) + return -EFAULT; + + msix_entry = (void *)(tables[i].mmap_addr + + msix_remap.msix_entry_index * + PCI_MSIX_ENTRY_SIZE); + + /* mask the entry when setup */ + writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + + /* setup the msi entry */ + writel((uint32_t)msix_remap.msi_addr, + msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + writel((uint32_t)(msix_remap.msi_addr >> 32), + msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(msix_remap.msi_data, + msix_entry + PCI_MSIX_ENTRY_DATA); + + /* unmask the entry */ + writel(msix_remap.vector_ctl & + PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + } + + return ret; +} diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index f0567aa0f1dd..5723e5e2d537 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -99,6 +99,15 @@ enum request_direction { DIRECTION_MAX, } __attribute__((aligned(4))); +/* + * IRQ type for ptdev + */ +enum irq_type { + IRQ_INTX, + IRQ_MSI, + IRQ_MSIX, +} __attribute__((aligned(4))); + struct msr_request { enum request_direction direction; long index; @@ -216,4 +225,38 @@ struct vm_gpa2hpa { unsigned long hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); +struct acrn_ptdev_irq { + enum irq_type type; + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + int virt_pin; /* IN: virtual IOAPIC pin */ + int phys_pin; /* IN: physical IOAPIC pin */ + bool pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + int vector_cnt; /* IN: vector count of MSI/MSIX */ + + /* IN: physcial address of MSI-X table */ + unsigned long table_paddr; + + /* IN: size of MSI-X table (round up to 4K) */ + int table_size; + } msix; + }; +} __attribute__((aligned(8))); + +struct acrn_vm_pci_msix_remap { + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + unsigned short msi_ctl; /* IN: PCI MSI/x cap control data */ + unsigned long msi_addr; /* IN/OUT: msi address to fix */ + unsigned int msi_data; /* IN/OUT: msi data to fix */ + int msix; /* IN: 0 - MSI, 1 - MSI-X */ + int msix_entry_index; /* IN: MSI-X the entry table index */ + /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ + unsigned int vector_ctl; +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d527a8fa8435..3e43da56813d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -92,6 +92,14 @@ #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define HC_ID_PCI_BASE 0x400UL +#define HC_ASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x00) +#define HC_DEASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x01) +#define HC_VM_PCI_MSIX_REMAP _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02) +#define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) +#define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index f1ed9a07e708..ce579e3734ff 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -140,6 +140,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, } inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); @@ -153,5 +154,12 @@ inline long vhm_query_vm_state(struct vhm_vm *vm); inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 3be6aca40844..8d03d38b788d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -79,11 +79,20 @@ #define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) #define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) + /* Guest memory management */ #define IC_ID_MEM_BASE 0x300UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define IC_ID_PCI_BASE 0x400UL +#define IC_ASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x00) +#define IC_DEASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x01) +#define IC_VM_PCI_MSIX_REMAP _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x02) +#define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) +#define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) + #define SPECNAMELEN 63 enum { From 8104f2de9f3237d1a8a6e38af8a886bbe17149cf Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0810/1276] x86:acrn: add write_msi pv ops to intercept pci msi write with pv method added pv ops write_msi into pv_irq_ops, the function write_msi_msg_paravirt is to write msi msg through paravirt way. for acrn, it calls acrn_write_msi_msg which includes acrn_notify_msix_remap for passthrough device msi/msix remapping. Change-Id: Ib5c0687c6227b527ff629c7884246417686b5896 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Zheng Xiao Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- arch/x86/acrn/Kconfig | 1 + arch/x86/acrn/acrn.c | 4 + arch/x86/include/asm/paravirt.h | 10 ++ arch/x86/include/asm/paravirt_types.h | 4 + arch/x86/kernel/paravirt.c | 4 + drivers/pci/msi.c | 4 +- drivers/pci/pci.h | 2 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_msi.c | 135 ++++++++++++++++++++++++++ include/linux/msi.h | 10 +- include/linux/vhm/vhm_msi.h | 61 ++++++++++++ 11 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 drivers/vhm/vhm_msi.c create mode 100644 include/linux/vhm/vhm_msi.h diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig index 0ba9e36c41f3..7788cb8cfb4b 100644 --- a/arch/x86/acrn/Kconfig +++ b/arch/x86/acrn/Kconfig @@ -8,6 +8,7 @@ config ACRN depends on X86_64 depends on PARAVIRT depends on DMA_CMA + depends on PCI_MSI depends on !INTEL_IOMMU depends on !VMAP_STACK help diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c index a042b544af33..3987e2287a9f 100644 --- a/arch/x86/acrn/acrn.c +++ b/arch/x86/acrn/acrn.c @@ -33,6 +33,7 @@ * */ #include +#include static uint32_t __init acrn_detect(void) { @@ -41,6 +42,9 @@ static uint32_t __init acrn_detect(void) static void __init acrn_init_platform(void) { +#ifdef CONFIG_PCI_MSI + pv_irq_ops.write_msi = acrn_write_msi_msg; +#endif } static void acrn_pin_vcpu(int cpu) diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index e375d4266b53..f5af75bc2d45 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -807,6 +807,16 @@ static inline notrace unsigned long arch_local_irq_save(void) return f; } +static inline void write_msi_msg_paravirt(struct msi_desc *entry, + struct msi_msg *msg) +{ + if ((pv_irq_ops.write_msi == NULL) || + (pv_irq_ops.write_msi == paravirt_nop)) + return; + + return PVOP_VCALL2(pv_irq_ops.write_msi, entry, msg); +} + /* Make sure as little as possible of this mess escapes. */ #undef PARAVIRT_CALL diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 4b75acc23b30..06e01d87d76a 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -56,6 +56,9 @@ struct cpumask; struct flush_tlb_info; struct mmu_gather; +struct msi_desc; +struct msi_msg; + /* * Wrapper type for pointers to code which uses the non-standard * calling convention. See PV_CALL_SAVE_REGS_THUNK below. @@ -196,6 +199,7 @@ struct pv_irq_ops { void (*safe_halt)(void); void (*halt)(void); + void (*write_msi)(struct msi_desc *entry, struct msi_msg *msg); } __no_randomize_layout; struct pv_mmu_ops { diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 8dc69d82567e..eaa8917dab73 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -336,6 +337,9 @@ __visible struct pv_irq_ops pv_irq_ops = { .irq_enable = __PV_IS_CALLEE_SAVE(native_irq_enable), .safe_halt = native_safe_halt, .halt = native_halt, +#ifdef CONFIG_PCI_MSI + .write_msi = native_write_msi_msg, +#endif }; __visible struct pv_cpu_ops pv_cpu_ops = { diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f2ef896464b3..66b5001abe96 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -190,7 +190,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag); } -static void __iomem *pci_msix_desc_addr(struct msi_desc *desc) +void __iomem *pci_msix_desc_addr(struct msi_desc *desc) { return desc->mask_base + desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; @@ -294,7 +294,7 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) } } -void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +void native_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { struct pci_dev *dev = msi_desc_to_pci_dev(entry); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6e0d1528d471..d3fe892641d0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -168,6 +168,8 @@ static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); } +void __iomem *pci_msix_desc_addr(struct msi_desc *desc); + void pci_realloc_get_opt(char *); static inline int pci_no_d1d2(struct pci_dev *dev) diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 4bd960d564b3..b4d58a92dcfd 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_msi.c b/drivers/vhm/vhm_msi.c new file mode 100644 index 000000000000..73affd60fc46 --- /dev/null +++ b/drivers/vhm/vhm_msi.c @@ -0,0 +1,135 @@ +/* + * virtio and hyperviosr service module (VHM): msi paravirt + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include + +#include "../pci/pci.h" + +static struct msi_msg acrn_notify_msix_remap(struct msi_desc *entry, + struct msi_msg *msg) +{ + volatile struct acrn_vm_pci_msix_remap notify; + struct pci_dev *dev = msi_desc_to_pci_dev(entry); + struct msi_msg remapped_msg = *msg; + u16 msgctl; + int ret; + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); + + notify.msi_ctl = msgctl; + notify.virt_bdf = (dev->bus->number << 8) | dev->devfn; + notify.msi_addr = msg->address_hi; + notify.msi_addr <<= 32; + notify.msi_addr |= msg->address_lo; + notify.msi_data = msg->data; + notify.msix = !!entry->msi_attrib.is_msix; + + if (notify.msix) + notify.msix_entry_index = entry->msi_attrib.entry_nr; + else + notify.msix_entry_index = 0; + + ret = hcall_remap_pci_msix(0, virt_to_phys(¬ify)); + if (ret < 0) + dev_err(&dev->dev, "Failed to notify MSI/x change to HV\n"); + else { + remapped_msg.address_hi = (unsigned int)(notify.msi_addr >> 32); + remapped_msg.address_lo = (unsigned int)notify.msi_addr; + remapped_msg.data = notify.msi_data; + } + return remapped_msg; +} + +void acrn_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct pci_dev *dev = msi_desc_to_pci_dev(entry); + struct msi_msg fmsg; + + if (dev->current_state != PCI_D0 || pci_dev_is_disconnected(dev)) { + /* Don't touch the hardware now */ + } else if (entry->msi_attrib.is_msix) { + void __iomem *base = pci_msix_desc_addr(entry); + + fmsg = acrn_notify_msix_remap(entry, msg); + + writel(fmsg.address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR); + writel(fmsg.address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(fmsg.data, base + PCI_MSIX_ENTRY_DATA); + } else { + int pos = dev->msi_cap; + u16 msgctl; + + fmsg = acrn_notify_msix_remap(entry, msg); + + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + msgctl &= ~PCI_MSI_FLAGS_QSIZE; + msgctl |= entry->msi_attrib.multiple << 4; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); + + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, + fmsg.address_lo); + if (entry->msi_attrib.is_64) { + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, + fmsg.address_hi); + pci_write_config_word(dev, pos + PCI_MSI_DATA_64, + fmsg.data); + } else { + pci_write_config_word(dev, pos + PCI_MSI_DATA_32, + fmsg.data); + } + } + entry->msg = *msg; +} diff --git a/include/linux/msi.h b/include/linux/msi.h index 5839d8062dfc..2c1e1d0c5d92 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -139,7 +139,15 @@ struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, const struct cpumask *affinity); void free_msi_entry(struct msi_desc *entry); void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +void native_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +#if defined(CONFIG_PARAVIRT) && defined(CONFIG_X86) +#include +#define __pci_write_msi_msg write_msi_msg_paravirt +#else +#define __pci_write_msi_msg native_write_msi_msg +#endif u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); diff --git a/include/linux/vhm/vhm_msi.h b/include/linux/vhm/vhm_msi.h new file mode 100644 index 000000000000..059e97a0e543 --- /dev/null +++ b/include/linux/vhm/vhm_msi.h @@ -0,0 +1,61 @@ +/* + * virtio and hyperviosr service module (VHM): msi paravirt + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_MSI_H__ +#define __ACRN_VHM_MSI_H__ + +struct msi_desc; +struct msi_msg; +void acrn_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +#endif From a5803b653ed7668ee31512ba6610cebb6338431d Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0811/1276] sos: cleanup hypercall API Put all hypercall APIs into vhm_hypercall.c other modules need to call hypercall API from this file. Change-Id: Id896a8300cf54279151a9d5674ed27a352df3f3f Tracked-On:218445 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 237 +++++++++++++++++++--- drivers/vhm/vhm_hypercall.c | 320 ++++-------------------------- include/linux/vhm/vhm_hypercall.h | 63 +++--- 3 files changed, 283 insertions(+), 337 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 97c20d82154f..32de7aeb30a1 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -92,6 +92,16 @@ static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; static atomic_t ioreq_retry = ATOMIC_INIT(0); +/* max num of pass-through devices using msix */ +#define MAX_ENTRY 3 + +struct table_iomems { + /* device's virtual BDF */ + unsigned short virt_bdf; + /* virtual base address of MSI-X table in memory space after ioremap */ + unsigned long mmap_addr; +} tables[MAX_ENTRY]; + static int vhm_dev_open(struct inode *inodep, struct file *filep) { struct vhm_vm *vm; @@ -153,25 +163,66 @@ static long vhm_dev_ioctl(struct file *filep, } switch (ioctl_num) { - case IC_CREATE_VM: - ret = vhm_create_vm(vm, ioctl_param); - break; + case IC_CREATE_VM: { + struct acrn_create_vm created_vm; + + if (copy_from_user(&created_vm, (void *)ioctl_param, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + ret = hcall_create_vm(virt_to_phys(&created_vm)); + if ((ret < 0) || + (created_vm.vmid == ACRN_INVALID_VMID)) { + pr_err("vhm: failed to create VM from Hypervisor !\n"); + return -EFAULT; + } + + if (copy_to_user((void *)ioctl_param, &created_vm, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + vm->vmid = created_vm.vmid; - case IC_RESUME_VM: - ret = vhm_resume_vm(vm); + pr_info("vhm: VM %ld created\n", created_vm.vmid); break; + } - case IC_PAUSE_VM: - ret = vhm_pause_vm(vm); + case IC_RESUME_VM: { + ret = hcall_resume_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to start VM %ld!\n", vm->vmid); + return -EFAULT; + } break; + } - case IC_DESTROY_VM: - ret = vhm_destroy_vm(vm); + case IC_PAUSE_VM: { + ret = hcall_pause_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); + return -EFAULT; + } break; + } - case IC_QUERY_VMSTATE: - ret = vhm_query_vm_state(vm); + case IC_DESTROY_VM: { + ret = hcall_destroy_vm(vm->vmid); + if (ret < 0) { + pr_err("failed to destroy VM %ld\n", vm->vmid); + return -EFAULT; + } + vm->vmid = ACRN_INVALID_VMID; break; + } + + case IC_QUERY_VMSTATE: { + ret = hcall_query_vm_state(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); + return -EFAULT; + } + return ret; + } case IC_ALLOC_MEMSEG: { struct vm_memseg memseg; @@ -239,17 +290,43 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_ASSERT_IRQLINE: { - ret = vhm_assert_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_assert_irqline(vm->vmid, virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } break; } - case IC_DEASSERT_IRQLINE: { - ret = vhm_deassert_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_deassert_irqline(vm->vmid, virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to deassert irq!\n"); + return -EFAULT; + } break; } - case IC_PULSE_IRQLINE: { - ret = vhm_pulse_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_pulse_irqline(vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } break; } @@ -268,27 +345,141 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_ASSIGN_PTDEV: { - ret = vhm_assign_ptdev(vm, ioctl_param); + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = hcall_assign_ptdev(vm->vmid, virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to assign ptdev!\n"); + return -EFAULT; + } break; } - case IC_DEASSIGN_PTDEV: { - ret = vhm_deassign_ptdev(vm, ioctl_param); + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = hcall_deassign_ptdev(vm->vmid, virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to deassign ptdev!\n"); + return -EFAULT; + } break; } case IC_SET_PTDEV_INTR_INFO: { - ret = vhm_set_ptdev_intr_info(vm, ioctl_param); + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = hcall_set_ptdev_intr_info(vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to set intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + break; } - case IC_RESET_PTDEV_INTR_INFO: { - ret = vhm_reset_ptdev_intr_info(vm, ioctl_param); + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = hcall_reset_ptdev_intr_info(vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to reset intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + break; } case IC_VM_PCI_MSIX_REMAP: { - ret = vhm_remap_pci_msix(vm, ioctl_param); + struct acrn_vm_pci_msix_remap msix_remap; + + if (copy_from_user(&msix_remap, + (void *)ioctl_param, sizeof(msix_remap))) + return -EFAULT; + + ret = hcall_remap_pci_msix(vm->vmid, virt_to_phys(&msix_remap)); + + if (copy_to_user((void *)ioctl_param, + &msix_remap, sizeof(msix_remap))) + return -EFAULT; + + if (msix_remap.msix) { + void __iomem *msix_entry; + int i; + + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf == msix_remap.virt_bdf) + break; + } + + if (!tables[i].mmap_addr) + return -EFAULT; + + msix_entry = tables[i].mmap_addr + + msix_remap.msix_entry_index * + PCI_MSIX_ENTRY_SIZE; + + /* mask the entry when setup */ + writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + + /* setup the msi entry */ + writel((uint32_t)msix_remap.msi_addr, + msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + writel((uint32_t)(msix_remap.msi_addr >> 32), + msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(msix_remap.msi_data, + msix_entry + PCI_MSIX_ENTRY_DATA); + + /* unmask the entry */ + writel(msix_remap.vector_ctl & + PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + } break; } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 0f3f6c1c5f4c..11ca6b86baed 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -48,341 +48,97 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include -#include -#include +#include #include #include -/* max num of pass-through devices using msix */ -#define MAX_ENTRY 3 - -struct table_iomems { - /* device's virtual BDF */ - unsigned short virt_bdf; - /* virtual base address of MSI-X table in memory space after ioremap */ - unsigned long mmap_addr; -} tables[MAX_ENTRY]; - -inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) +inline long hcall_create_vm(unsigned long vminfo) { - return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); + return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); } -inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix) +inline long hcall_resume_vm(unsigned long vmid) { - return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msix); + return acrn_hypercall1(HC_RESUME_VM, vmid); } -inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) +inline long hcall_pause_vm(unsigned long vmid) { - return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); + return acrn_hypercall1(HC_PAUSE_VM, vmid); } -inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +inline long hcall_destroy_vm(unsigned long vmid) { - return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); + return acrn_hypercall1(HC_DESTROY_VM, vmid); } -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +inline long hcall_query_vm_state(unsigned long vmid) { - return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); + return acrn_hypercall1(HC_QUERY_VMSTATE, vmid); } -inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa) -{ - return acrn_hypercall2(HC_VM_GPA2HPA, vmid, gpa2hpa); -} - -inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { - long ret = 0; - struct acrn_create_vm created_vm; - - if (copy_from_user(&created_vm, (void *)ioctl_param, - sizeof(struct acrn_create_vm))) - return -EFAULT; - - ret = acrn_hypercall2(HC_CREATE_VM, 0, - virt_to_phys(&created_vm)); - if ((ret < 0) || - (created_vm.vmid == ACRN_INVALID_VMID)) { - pr_err("vhm: failed to create VM from Hypervisor !\n"); - return -EFAULT; - } - - if (copy_to_user((void *)ioctl_param, &created_vm, - sizeof(struct acrn_create_vm))) - return -EFAULT; - - vm->vmid = created_vm.vmid; - pr_info("vhm: VM %ld created\n", created_vm.vmid); - - return ret; + return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } -inline long vhm_resume_vm(struct vhm_vm *vm) +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { - long ret = 0; - - ret = acrn_hypercall1(HC_RESUME_VM, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to start VM %ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); } -inline long vhm_pause_vm(struct vhm_vm *vm) +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) { - long ret = 0; - - ret = acrn_hypercall1(HC_PAUSE_VM, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); } -inline long vhm_destroy_vm(struct vhm_vm *vm) +inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - - ret = acrn_hypercall1(HC_DESTROY_VM, vm->vmid); - if (ret < 0) { - pr_err("failed to destroy VM %ld\n", vm->vmid); - return -EFAULT; - } - vm->vmid = ACRN_INVALID_VMID; - - return ret; + return acrn_hypercall2(HC_ASSERT_IRQLINE, vmid, irq); } -inline long vhm_query_vm_state(struct vhm_vm *vm) +inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - - ret = acrn_hypercall1(HC_QUERY_VMSTATE, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_DEASSERT_IRQLINE, vmid, irq); } -inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_ASSERT_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_PULSE_IRQLINE, vmid, irq); } -inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_DEASSERT_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to deassert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); } -inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_PULSE_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_ASSIGN_PTDEV, vmid, bdf); } -inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf) { - long ret = 0; - uint16_t bdf; - - if (copy_from_user(&bdf, - (void *)ioctl_param, sizeof(uint16_t))) - return -EFAULT; - - ret = acrn_hypercall2(HC_ASSIGN_PTDEV, vm->vmid, - virt_to_phys(&bdf)); - if (ret < 0) { - pr_err("vhm: failed to assign ptdev!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_DEASSIGN_PTDEV, vmid, bdf); } -inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_set_ptdev_intr_info(unsigned long vmid, unsigned long pt_irq) { - long ret = 0; - uint16_t bdf; - - if (copy_from_user(&bdf, - (void *)ioctl_param, sizeof(uint16_t))) - return -EFAULT; - - ret = acrn_hypercall2(HC_DEASSIGN_PTDEV, vm->vmid, - virt_to_phys(&bdf)); - if (ret < 0) { - pr_err("vhm: failed to deassign ptdev!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vmid, pt_irq); } -inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param) +inline long hcall_reset_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq) { - long ret = 0; - struct acrn_ptdev_irq pt_irq; - int i; - - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vm->vmid, - virt_to_phys(&pt_irq)); - if (ret < 0) { - pr_err("vhm: failed to set intr info for ptdev!\n"); - return -EFAULT; - } - - if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); - break; - } - } - - return ret; + return acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vmid, pt_irq); } -inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param) +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi) { - long ret = 0; - struct acrn_ptdev_irq pt_irq; - int i; - - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vm->vmid, - virt_to_phys(&pt_irq)); - if (ret < 0) { - pr_err("vhm: failed to reset intr info for ptdev!\n"); - return -EFAULT; - } - - if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); - break; - } - } - - return ret; + return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msi); } -inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr) { - long ret = 0; - struct acrn_vm_pci_msix_remap msix_remap; - - if (copy_from_user(&msix_remap, - (void *)ioctl_param, sizeof(msix_remap))) - return -EFAULT; - - ret = acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vm->vmid, - virt_to_phys(&msix_remap)); - - if (copy_to_user((void *)ioctl_param, - &msix_remap, sizeof(msix_remap))) - return -EFAULT; - - if (msix_remap.msix) { - void __iomem *msix_entry; - int i; - - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf == msix_remap.virt_bdf) - break; - } - - if (!tables[i].mmap_addr) - return -EFAULT; - - msix_entry = (void *)(tables[i].mmap_addr + - msix_remap.msix_entry_index * - PCI_MSIX_ENTRY_SIZE); - - /* mask the entry when setup */ - writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, - msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); - - /* setup the msi entry */ - writel((uint32_t)msix_remap.msi_addr, - msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); - writel((uint32_t)(msix_remap.msi_addr >> 32), - msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); - writel(msix_remap.msi_data, - msix_entry + PCI_MSIX_ENTRY_DATA); - - /* unmask the entry */ - writel(msix_remap.vector_ctl & - PCI_MSIX_ENTRY_CTRL_MASKBIT, - msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); - } - - return ret; + return acrn_hypercall2(HC_VM_GPA2HPA, vmid, addr); } diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index ce579e3734ff..35bb48ae6cd3 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -52,14 +52,12 @@ #ifndef VHM_HYPERCALL_H #define VHM_HYPERCALL_H -#include - -static inline long acrn_hypercall0(unsigned long hyp_id) +static inline long acrn_hypercall0(unsigned long hcall_id) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -70,12 +68,12 @@ static inline long acrn_hypercall0(unsigned long hyp_id) return result; } -static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) +static inline long acrn_hypercall1(unsigned long hcall_id, unsigned long param1) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -86,13 +84,13 @@ static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) return result; } -static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall2(unsigned long hcall_id, unsigned long param1, unsigned long param2) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -103,13 +101,13 @@ static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, return result; } -static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall3(unsigned long hcall_id, unsigned long param1, unsigned long param2, unsigned long param3) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -120,14 +118,14 @@ static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, return result; } -static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, unsigned long param2, unsigned long param3, unsigned long param4) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -139,27 +137,28 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } -inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); -inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix); -inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); +inline long hcall_create_vm(unsigned long vminfo); +inline long hcall_resume_vm(unsigned long vmid); +inline long hcall_pause_vm(unsigned long vmid); +inline long hcall_destroy_vm(unsigned long vmid); +inline long hcall_query_vm_state(unsigned long vmid); +inline long hcall_set_memmap(unsigned long vmid, + unsigned long memmap); +inline long hcall_set_ioreq_buffer(unsigned long vmid, + unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); -inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa); -inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_resume_vm(struct vhm_vm *vm); -inline long vhm_pause_vm(struct vhm_vm *vm); -inline long vhm_destroy_vm(struct vhm_vm *vm); -inline long vhm_query_vm_state(struct vhm_vm *vm); -inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param); -inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param); -inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param); +inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); +inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf); +inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf); +inline long hcall_set_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq); +inline long hcall_reset_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq); +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi); +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr); #endif /* VHM_HYPERCALL_H */ From a24829f0b1791fd970c978676136e8091be4c7c1 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0812/1276] vcpu: export vcpu create interface to DM Change-Id: I64a179c2452f67285f347bbaa60c702dec5f55de Tracked-On: 218445 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 17 +++++++++++++++++ include/linux/vhm/acrn_common.h | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 24 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 32de7aeb30a1..d7ad18f6a55e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -224,6 +224,23 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } + case IC_CREATE_VCPU: { + struct acrn_create_vcpu cv; + + if (copy_from_user(&cv, (void *)ioctl_param, + sizeof(struct acrn_create_vcpu))) + return -EFAULT; + + ret = acrn_hypercall2(HC_CREATE_VCPU, vm->vmid, + virt_to_phys(&cv)); + if (ret < 0) { + pr_err("vhm: failed to create vcpu %ld!\n", cv.vcpuid); + return -EFAULT; + } + + return ret; + } + case IC_ALLOC_MEMSEG: { struct vm_memseg memseg; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 5723e5e2d537..2fa43a288ee5 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -193,6 +193,11 @@ struct acrn_create_vm { unsigned long vcpu_num; /* IN: VM vcpu number */ } __attribute__((aligned(8))); +struct acrn_create_vcpu { + int vcpuid; /* IN: vcpu id */ + int pcpuid; /* IN: pcpu id */ +} __attribute__((aligned(8))); + struct acrn_set_ioreq_buffer { long req_buf; /* IN: gpa of per VM request_buffer*/ } __attribute__((aligned(8))); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 3e43da56813d..329c38b961e5 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -73,6 +73,7 @@ #define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x100UL diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 8d03d38b788d..5ec2d10fc350 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -63,6 +63,7 @@ #define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x100UL From f41541c40f9aa76b0d389df827f91a5ae6590612 Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0813/1276] sos: clean up ptdev msi-x table ioremap operations - use list rather than array to store the virtual base address of MSI-X table in memory space after ioremap - use physical BDF rather than virtual BDF for index purpose this could avoid the issue when different UOSs might use same virtual BDF for different pass-through PCI devices. Change-Id: Ie8330bc5054ba549a11866b84770df5d1a257a6c Tracked-On:218445 Signed-off-by: Shiqing Gao Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 70 +++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d7ad18f6a55e..5130fb508bc9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -92,15 +93,16 @@ static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; static atomic_t ioreq_retry = ATOMIC_INIT(0); -/* max num of pass-through devices using msix */ -#define MAX_ENTRY 3 - struct table_iomems { - /* device's virtual BDF */ - unsigned short virt_bdf; + /* list node for this table_iomems */ + struct list_head list; + /* device's physical BDF */ + unsigned short phys_bdf; /* virtual base address of MSI-X table in memory space after ioremap */ unsigned long mmap_addr; -} tables[MAX_ENTRY]; +}; +static LIST_HEAD(table_iomems_list); +static DEFINE_MUTEX(table_iomems_lock); static int vhm_dev_open(struct inode *inodep, struct file *filep) { @@ -392,7 +394,7 @@ static long vhm_dev_ioctl(struct file *filep, case IC_SET_PTDEV_INTR_INFO: { struct acrn_ptdev_irq pt_irq; - int i; + struct table_iomems *new; if (copy_from_user(&pt_irq, (void *)ioctl_param, sizeof(pt_irq))) @@ -406,23 +408,24 @@ static long vhm_dev_ioctl(struct file *filep, } if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = - ioremap_nocache(pt_irq.msix.table_paddr, + new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); + if (new == NULL) + return -EFAULT; + new->phys_bdf = pt_irq.phys_bdf; + new->mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, pt_irq.msix.table_size); - break; - } + + mutex_lock(&table_iomems_lock); + list_add(&new->list, &table_iomems_list); + mutex_unlock(&table_iomems_lock); } break; } case IC_RESET_PTDEV_INTR_INFO: { struct acrn_ptdev_irq pt_irq; - int i; + struct table_iomems *new; if (copy_from_user(&pt_irq, (void *)ioctl_param, sizeof(pt_irq))) @@ -436,16 +439,17 @@ static long vhm_dev_ioctl(struct file *filep, } if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = - ioremap_nocache(pt_irq.msix.table_paddr, + new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); + if (new == NULL) + return -EFAULT; + new->phys_bdf = pt_irq.phys_bdf; + new->mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, pt_irq.msix.table_size); - break; - } + + mutex_lock(&table_iomems_lock); + list_add(&new->list, &table_iomems_list); + mutex_unlock(&table_iomems_lock); } break; @@ -466,19 +470,21 @@ static long vhm_dev_ioctl(struct file *filep, if (msix_remap.msix) { void __iomem *msix_entry; - int i; + struct table_iomems *ptr; - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf == msix_remap.virt_bdf) + mutex_lock(&table_iomems_lock); + list_for_each_entry(ptr, &table_iomems_list, list) { + if (ptr->phys_bdf == msix_remap.phys_bdf) break; } + mutex_unlock(&table_iomems_lock); - if (!tables[i].mmap_addr) + if (!ptr->mmap_addr) return -EFAULT; - msix_entry = tables[i].mmap_addr + + msix_entry = (void __iomem *) (ptr->mmap_addr + msix_remap.msix_entry_index * - PCI_MSIX_ENTRY_SIZE; + PCI_MSIX_ENTRY_SIZE); /* mask the entry when setup */ writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, From 48cc594a6a46700b0bc12d1ebab1cf12313465ee Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0814/1276] sos: Update the common head file --remove unused data structures --move data structure(vm_gpa2hpa) to acrn_hv_defs.h --combine 2 data structures vm_exit(dm) and vhm_requeset(hv sos) to vhm_request and put it in acrn_common.h Change-Id: Ice1f93bf7083b08001b2dfdea257aa7d58e9e751 Tracked-On:218445 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/acrn_common.h | 29 ++++++----------------------- include/linux/vhm/acrn_hv_defs.h | 5 +++++ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 2fa43a288ee5..0e9293d08e01 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,12 +56,6 @@ * Commmon structures for ACRN/VHM/DM */ -enum irq_mode { - IRQ_PULSE, - IRQ_ASSERT, - IRQ_DEASSERT, -} __attribute__((aligned(4))); - /* ISA type * inject interrut to both PIC and IOAPIC */ @@ -114,14 +108,6 @@ struct msr_request { long value; } __attribute__((aligned(8))); -struct cpuid_request { - long eax_in; - long ecx_in; - long eax_out; - long ebx_out; - long ecx_out; - long edx_out; -} __attribute__((aligned(8))); struct mmio_request { enum request_direction direction; @@ -151,13 +137,15 @@ struct pci_request { /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ - enum request_type type; - int reserved0[15]; - + union { + int exitcode; + enum request_type type; + unsigned long rip; + int reserved0[16]; + }; /* offset: 64bytes-127bytes */ union { struct msr_request msr_request; - struct cpuid_request cpuid_request; struct io_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; @@ -225,11 +213,6 @@ struct acrn_nmi_entry { unsigned long vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); -struct vm_gpa2hpa { - unsigned long gpa; /* IN: gpa to translation */ - unsigned long hpa; /* OUT: -1 means invalid gpa */ -} __attribute__((aligned(8))); - struct acrn_ptdev_irq { enum irq_type type; unsigned short virt_bdf; /* IN: Device virtual BDF# */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 329c38b961e5..1d21bf21c91c 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -127,4 +127,9 @@ struct vm_set_memmap { int prot; } __attribute__((aligned(8))); +struct vm_gpa2hpa { + unsigned long gpa; /* IN: gpa to translation */ + unsigned long hpa; /* OUT: -1 means invalid gpa */ +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ From 45dfabc70999360b21bdb26c1a248342da502e40 Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0815/1276] sos: cleanup ptdev irq structure - Use individual data struct of ptdev irq for ioctl and hypercall Change-Id: Id7b02038d0c149a0d1206206f18d54c91c7350d3 Tracked-On: 218445 Signed-off-by: Binbin Wu Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 38 +++++++++++++++++------------- include/linux/vhm/acrn_hv_defs.h | 20 ++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 27 +++++++++++++++++++++ 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5130fb508bc9..ebada9a11552 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -151,9 +151,13 @@ static long vhm_dev_ioctl(struct file *filep, { long ret = 0; struct vhm_vm *vm; + struct ic_ptdev_irq ic_pt_irq; + struct hc_ptdev_irq hc_pt_irq; trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); + memset(&ic_pt_irq, 0, sizeof(ic_pt_irq)); vm = (struct vhm_vm *)filep->private_data; if (vm == NULL) { pr_err("vhm: invalid VM !\n"); @@ -393,28 +397,29 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_SET_PTDEV_INTR_INFO: { - struct acrn_ptdev_irq pt_irq; struct table_iomems *new; - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) + if (copy_from_user(&ic_pt_irq, + (void *)ioctl_param, sizeof(ic_pt_irq))) return -EFAULT; + memcpy(&hc_pt_irq, &ic_pt_irq, sizeof(hc_pt_irq)); + ret = hcall_set_ptdev_intr_info(vm->vmid, - virt_to_phys(&pt_irq)); + virt_to_phys(&hc_pt_irq)); if (ret < 0) { pr_err("vhm: failed to set intr info for ptdev!\n"); return -EFAULT; } - if (pt_irq.msix.table_paddr) { + if (ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; - new->phys_bdf = pt_irq.phys_bdf; + new->phys_bdf = ic_pt_irq.phys_bdf; new->mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); + ioremap_nocache(ic_pt_irq.msix.table_paddr, + ic_pt_irq.msix.table_size); mutex_lock(&table_iomems_lock); list_add(&new->list, &table_iomems_list); @@ -424,28 +429,29 @@ static long vhm_dev_ioctl(struct file *filep, break; } case IC_RESET_PTDEV_INTR_INFO: { - struct acrn_ptdev_irq pt_irq; struct table_iomems *new; - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) + if (copy_from_user(&ic_pt_irq, + (void *)ioctl_param, sizeof(ic_pt_irq))) return -EFAULT; + memcpy(&hc_pt_irq, &ic_pt_irq, sizeof(hc_pt_irq)); + ret = hcall_reset_ptdev_intr_info(vm->vmid, - virt_to_phys(&pt_irq)); + virt_to_phys(&hc_pt_irq)); if (ret < 0) { pr_err("vhm: failed to reset intr info for ptdev!\n"); return -EFAULT; } - if (pt_irq.msix.table_paddr) { + if (ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; - new->phys_bdf = pt_irq.phys_bdf; + new->phys_bdf = ic_pt_irq.phys_bdf; new->mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); + ioremap_nocache(ic_pt_irq.msix.table_paddr, + ic_pt_irq.msix.table_size); mutex_lock(&table_iomems_lock); list_add(&new->list, &table_iomems_list); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 1d21bf21c91c..eeac0e9b4e76 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -132,4 +132,24 @@ struct vm_gpa2hpa { unsigned long hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); +struct hc_ptdev_irq { +#define IRQ_INTX 0 +#define IRQ_MSI 1 +#define IRQ_MSIX 2 + uint32_t type; + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + uint32_t virt_pin; /* IN: virtual IOAPIC pin */ + uint32_t phys_pin; /* IN: physical IOAPIC pin */ + uint32_t pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + /* IN: vector count of MSI/MSIX */ + uint32_t vector_cnt; + } msix; + }; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 5ec2d10fc350..df07e3c93467 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -129,4 +129,31 @@ struct vm_memmap { }; }; +struct ic_ptdev_irq { +#define IRQ_INTX 0 +#define IRQ_MSI 1 +#define IRQ_MSIX 2 + uint32_t type; + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + uint32_t virt_pin; /* IN: virtual IOAPIC pin */ + uint32_t phys_pin; /* IN: physical IOAPIC pin */ + uint32_t pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + /* IN: vector count of MSI/MSIX, + * Keep this filed on top of msix */ + uint32_t vector_cnt; + + /* IN: size of MSI-X table (round up to 4K) */ + uint32_t table_size; + + /* IN: physical address of MSI-X table */ + uint64_t table_paddr; + } msix; + }; +}; + #endif /* VHM_IOCTL_DEFS_H */ From 74259ae31bb96a157f730a3e0710200ed31c5d78 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0816/1276] VBS-K (Virtio Backend Service in Kernel): a kernel-level virtio framework for ACRN hypervisor. This patch added the basic VBS-K framework including the following: - Definitions of the data structures shared between VBS-K and its counterpart in userspace, which is VBS-U; - VBS-K device common data structures; - Core runtime control logic of the VBS-K framework; Change-Id: I8d9e86de701c1aef965a2490f398a2360cb5bd92 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/vbs/Kconfig | 20 +++++ drivers/vbs/Makefile | 3 + drivers/vbs/vbs.c | 125 ++++++++++++++++++++++++++++++ include/linux/vbs/vbs.h | 98 +++++++++++++++++++++++ include/linux/vbs/vbs_common_if.h | 78 +++++++++++++++++++ 7 files changed, 327 insertions(+) create mode 100644 drivers/vbs/Kconfig create mode 100644 drivers/vbs/Makefile create mode 100644 drivers/vbs/vbs.c create mode 100644 include/linux/vbs/vbs.h create mode 100644 include/linux/vbs/vbs_common_if.h diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..b9ac6b8cbdd4 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -219,4 +219,6 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/vbs/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 07528374561a..6d0c3cd78ffd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -146,6 +146,7 @@ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_BCMA) += bcma/ obj-$(CONFIG_VHOST_RING) += vhost/ +obj-$(CONFIG_VBS) += vbs/ obj-$(CONFIG_VHOST) += vhost/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/ diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig new file mode 100644 index 000000000000..156c3162fd63 --- /dev/null +++ b/drivers/vbs/Kconfig @@ -0,0 +1,20 @@ +# +# This Kconfig describes VBS for ACRN hypervisor +# +config VBS + tristate "Enable VBS framework for ACRN hypervisor" + depends on ACRN + depends on ACRN_VHM + default n + ---help--- + This option is selected by any driver which needs to use + the Virtio Backend Service (VBS) framework on ACRN + hypervisor. + +config VBS_DEBUG + bool "ACRN VBS debugging" + depends on VBS != n + default n + ---help--- + This is an option for use by developers; most people should + say N here. This enables ACRN VBS debugging. diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile new file mode 100644 index 000000000000..b52b65b6bd13 --- /dev/null +++ b/drivers/vbs/Makefile @@ -0,0 +1,3 @@ +ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG + +obj-$(CONFIG_VBS) += vbs.o diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c new file mode 100644 index 000000000000..591d43dbe536 --- /dev/null +++ b/drivers/vbs/vbs.c @@ -0,0 +1,125 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * Created Virtio Backend Service (VBS) framework: + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + * - VBS-K mainly handles data plane part of a virtio backend driver, + * such as virtqueue parsing and processing, while VBS-U mainly + * hanldes control plane part. + */ + +#include +#include +#include + +static long virtio_dev_info_set(struct virtio_dev_info *dev, + struct vbs_dev_info __user *i) +{ + struct vbs_dev_info info; + + if (copy_from_user(&info, i, sizeof(struct vbs_dev_info))) + return -EFAULT; + + /* setup struct virtio_dev_info based on info in vbs_dev_info */ + strncpy(dev->name, info.name, VBS_NAME_LEN); + dev->_ctx.vmid = info.vmid; + dev->negotiated_features = info.negotiated_features; + dev->io_range_start = info.pio_range_start; + dev->io_range_len = info.pio_range_len; + dev->io_range_type = PIO_RANGE; + + return 0; +} + +long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp) +{ + long ret; + + /* + * Currently we don't conduct ownership checking, + * but assuming caller would have device mutex. + */ + + switch (ioctl) { + case VBS_SET_DEV: + ret = virtio_dev_info_set(dev, argp); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(virtio_dev_ioctl); + +static int __init vbs_init(void) +{ + return 0; +} + +static void __exit vbs_exit(void) +{ +} + +module_init(vbs_init); +module_exit(vbs_exit); + +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Virtio Backend Service framework for ACRN hypervisor"); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h new file mode 100644 index 000000000000..7b876782fe41 --- /dev/null +++ b/include/linux/vbs/vbs.h @@ -0,0 +1,98 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * Define data structures and runtime control APIs for VBS framework. + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + */ + +#ifndef _VBS_H_ +#define _VBS_H_ + +#include + +/* + * VBS-K device needs to handle frontend driver's kick in kernel. + * For virtio 0.9.5, the kick register is a PIO register, + * for virtio 1.0+, the kick register could be a MMIO register. + */ +enum IORangeType { + PIO_RANGE = 0x0, /* default */ + MMIO_RANGE = 0x1, +}; + +/* device context */ +struct ctx { + /* VHM required info */ + int vmid; +}; + +/* struct used to maintain virtio device info from userspace VBS */ +struct virtio_dev_info { + /* dev info from VBS */ + char name[VBS_NAME_LEN]; /* VBS device name */ + struct ctx _ctx; /* device context */ + uint32_t negotiated_features; /* features after guest loads driver */ + uint64_t io_range_start; /* IO range start of VBS device */ + uint64_t io_range_len; /* IO range len of VBS device */ + enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ +}; + +/* VBS Runtime Control APIs */ +long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp); + +#endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h new file mode 100644 index 000000000000..78b36a6c58c8 --- /dev/null +++ b/include/linux/vbs/vbs_common_if.h @@ -0,0 +1,78 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * - Define data structures shared between VBS userspace and VBS kernel + * space. + */ + +#ifndef _VBS_COMMON_IF_H_ +#define _VBS_COMMON_IF_H_ + +#define VBS_NAME_LEN 32 + +struct vbs_dev_info { + char name[VBS_NAME_LEN];/* VBS name */ + int vmid; /* id of VM this device belongs to */ + uint32_t negotiated_features; + /* features after VIRTIO_CONFIG_S_DRIVER_OK */ + uint64_t pio_range_start; + /* start of PIO range initialized by guest OS */ + uint64_t pio_range_len; /* len of PIO range initialized by guest OS */ +}; + +#define VBS_IOCTL 0xAF + +#define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) + +#endif From 2d5007770f7de522577ca8478f46496d3445793d Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0817/1276] VBS-K: virtqueue initialization API. This patch added the following to the VBS-K framework: - virtqueue data structures shared between VBS-K and its counterpart in userspace, which is VBS-U; - virtqueue initialization API; Change-Id: Ib928ea94cb4f33cf30abd17921089afc14518365 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/Makefile | 1 + drivers/vbs/vbs.c | 75 ++++++++++++++++++ drivers/vbs/vq.c | 125 ++++++++++++++++++++++++++++++ include/linux/vbs/vbs.h | 57 ++++++++++++++ include/linux/vbs/vbs_common_if.h | 18 +++++ include/linux/vbs/vq.h | 99 +++++++++++++++++++++++ 6 files changed, 375 insertions(+) create mode 100644 drivers/vbs/vq.c create mode 100644 include/linux/vbs/vq.h diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index b52b65b6bd13..cbd5076e2313 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -1,3 +1,4 @@ ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o +obj-$(CONFIG_VBS) += vq.o diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 591d43dbe536..1e7a9645a353 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -65,6 +65,66 @@ #include #include #include +#include + +static long virtio_vqs_info_set(struct virtio_dev_info *dev, + struct vbs_vqs_info __user *i) +{ + struct vbs_vqs_info info; + struct virtio_vq_info *vq; + int j; + + vq = dev->vqs; + + if (copy_from_user(&info, i, sizeof(struct vbs_vqs_info))) + return -EFAULT; + + /* setup struct virtio_vq_info based on info in struct vbs_vq_info */ + if (dev->nvq && dev->nvq != info.nvq) { + pr_err("Oops! dev's nvq != vqs's nvq. Not the same device?\n"); + return -EFAULT; + } + + for (j = 0; j < info.nvq; j++) { + vq->qsize = info.vqs[j].qsize; + vq->pfn = info.vqs[j].pfn; + vq->msix_idx = info.vqs[j].msix_idx; + vq->msix_addr = info.vqs[j].msix_addr; + vq->msix_data = info.vqs[j].msix_data; + + pr_debug("msix id %x, addr %llx, data %x\n", vq->msix_idx, + vq->msix_addr, vq->msix_data); + + virtio_vq_init(vq, vq->pfn); + + vq++; + } + + return 0; +} + +/* invoked by VBS-K device's ioctl routine */ +long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp) +{ + long ret; + + /* + * Currently we don't conduct ownership checking, + * but assuming caller would have device mutex. + */ + + switch (ioctl) { + case VBS_SET_VQ: + ret = virtio_vqs_info_set(dev, argp); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(virtio_vqs_ioctl); static long virtio_dev_info_set(struct virtio_dev_info *dev, struct vbs_dev_info __user *i) @@ -77,6 +137,7 @@ static long virtio_dev_info_set(struct virtio_dev_info *dev, /* setup struct virtio_dev_info based on info in vbs_dev_info */ strncpy(dev->name, info.name, VBS_NAME_LEN); dev->_ctx.vmid = info.vmid; + dev->nvq = info.nvq; dev->negotiated_features = info.negotiated_features; dev->io_range_start = info.pio_range_start; dev->io_range_len = info.pio_range_len; @@ -85,6 +146,7 @@ static long virtio_dev_info_set(struct virtio_dev_info *dev, return 0; } +/* invoked by VBS-K device's ioctl routine */ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp) { @@ -107,6 +169,19 @@ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, } EXPORT_SYMBOL_GPL(virtio_dev_ioctl); +/* called in VBS-K device's .open() */ +long virtio_dev_init(struct virtio_dev_info *dev, + struct virtio_vq_info *vqs, int nvq) +{ + int i; + + for (i = 0; i < nvq; i++) + virtio_vq_reset(&vqs[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_dev_init); + static int __init vbs_init(void) { return 0; diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c new file mode 100644 index 000000000000..95a6757a1c85 --- /dev/null +++ b/drivers/vbs/vq.c @@ -0,0 +1,125 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Chris Torek + * Hao Li + * Created Virtqueue APIs for ACRN VBS framework: + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - Virtqueue APIs abstract away the details of the internal data + * structures of virtqueue, so that callers could easily access + * the data from guest through virtqueues. + */ + +#include +#include +#include +#include + +/* helper function for remote memory map */ +void * paddr_guest2host(struct ctx *ctx, uintptr_t gaddr, size_t len) +{ + return map_guest_phys(ctx->vmid, gaddr, len); +} + +/* + * Initialize the currently-selected virtqueue. + * The guest just gave us a page frame number, from which we can + * calculate the addresses of the queue. + */ +void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn) +{ + uint64_t phys; + size_t size; + char *base; + struct ctx *ctx; + + ctx = &vq->dev->_ctx; + + phys = (uint64_t)pfn << VRING_PAGE_BITS; + size = virtio_vq_ring_size(vq->qsize); + base = paddr_guest2host(ctx, phys, size); + + /* First page(s) are descriptors... */ + vq->desc = (struct virtio_desc *)base; + base += vq->qsize * sizeof(struct virtio_desc); + + /* ... immediately followed by "avail" ring (entirely uint16_t's) */ + vq->avail = (struct vring_avail *)base; + base += (2 + vq->qsize + 1) * sizeof(uint16_t); + + /* Then it's rounded up to the next page... */ + base = (char *)roundup2((uintptr_t)base, VRING_ALIGN); + + /* ... and the last page(s) are the used ring. */ + vq->used = (struct vring_used *)base; + + /* Mark queue as allocated, and start at 0 when we use it. */ + vq->flags = VQ_ALLOC; + vq->last_avail = 0; + vq->save_used = 0; +} + +/* reset one virtqueue, make it invalid */ +void virtio_vq_reset(struct virtio_vq_info *vq) +{ + if (!vq) { + pr_info("%s: vq is NULL!\n", __func__); + return; + } + + vq->pfn = 0; + vq->msix_idx = VIRTIO_MSI_NO_VECTOR; + vq->flags = 0; + vq->last_avail = 0; + vq->save_used = 0; +} diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 7b876782fe41..715c49156a1a 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -80,19 +80,76 @@ struct ctx { int vmid; }; +struct virtio_desc { /* AKA vring_desc */ + uint64_t addr; /* guest physical address */ + uint32_t len; /* length of scatter/gather seg */ + uint16_t flags; /* desc flags */ + uint16_t next; /* next desc if F_NEXT */ +} __attribute__((packed)); + +struct virtio_used { /* AKA vring_used_elem */ + uint32_t idx; /* head of used descriptor chain */ + uint32_t len; /* length written-to */ +} __attribute__((packed)); + +struct vring_avail { + uint16_t flags; /* vring_avail flags */ + uint16_t idx; /* counts to 65535, then cycles */ + uint16_t ring[]; /* size N, reported in QNUM value */ +} __attribute__((packed)); + +struct vring_used { + uint16_t flags; /* vring_used flags */ + uint16_t idx; /* counts to 65535, then cycles */ + struct virtio_used ring[]; /* size N */ +} __attribute__((packed)); + +/* struct used to maintain virtqueue info from userspace VBS */ +struct virtio_vq_info { + /* virtqueue info from VBS-U */ + uint16_t qsize; /* size of this queue (a power of 2) */ + uint32_t pfn; /* PFN of virt queue (not shifted!) */ + uint16_t msix_idx; /* MSI-X index/VIRTIO_MSI_NO_VECTOR */ + uint64_t msix_addr; /* MSI-X address specified by index */ + uint32_t msix_data; /* MSI-X data specified by index */ + + /* members created in kernel space VBS */ + int (*vq_notify)(int); /* vq-wide notification */ + struct virtio_dev_info *dev; /* backpointer to virtio_dev_info */ + uint16_t num; /* we're the num'th virtqueue */ + uint16_t flags; /* virtqueue flags */ + uint16_t last_avail; /* a recent value of vq_avail->va_idx */ + uint16_t save_used; /* saved vq_used->vu_idx */ + + volatile struct virtio_desc *desc; /* descriptor array */ + volatile struct vring_avail *avail; /* the "avail" ring */ + volatile struct vring_used *used; /* the "used" ring */ +}; + /* struct used to maintain virtio device info from userspace VBS */ struct virtio_dev_info { /* dev info from VBS */ char name[VBS_NAME_LEN]; /* VBS device name */ struct ctx _ctx; /* device context */ + int nvq; /* number of virtqueues */ uint32_t negotiated_features; /* features after guest loads driver */ uint64_t io_range_start; /* IO range start of VBS device */ uint64_t io_range_len; /* IO range len of VBS device */ enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ + + /* members created in kernel space VBS */ + void (*dev_notify)(void *, struct virtio_vq_info *); + /* device-wide notification */ + struct virtio_vq_info *vqs; /* virtqueue(s) */ + int curq; /* current virtqueue index */ }; /* VBS Runtime Control APIs */ +long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, + int nvq); long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); +long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp); #endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 78b36a6c58c8..91eb4e01d95c 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -59,11 +59,28 @@ #ifndef _VBS_COMMON_IF_H_ #define _VBS_COMMON_IF_H_ +#define VBS_MAX_VQ_CNT 10 #define VBS_NAME_LEN 32 +#define VIRTIO_MSI_NO_VECTOR 0xFFFF + +struct vbs_vq_info { + uint16_t qsize; /* size of this virtqueue (a power of 2) */ + uint32_t pfn; /* PFN of virtqueue (not shifted!) */ + uint16_t msix_idx; /* MSI-X index, or VIRTIO_MSI_NO_VECTOR */ + uint64_t msix_addr; /* MSI-X address specified by index */ + uint32_t msix_data; /* MSI-X data specified by index */ +}; + +struct vbs_vqs_info { + uint32_t nvq; /* number of virtqueues */ + struct vbs_vq_info vqs[VBS_MAX_VQ_CNT]; + /* array of struct vbs_vq_info */ +}; struct vbs_dev_info { char name[VBS_NAME_LEN];/* VBS name */ int vmid; /* id of VM this device belongs to */ + int nvq; /* number of virtqueues */ uint32_t negotiated_features; /* features after VIRTIO_CONFIG_S_DRIVER_OK */ uint64_t pio_range_start; @@ -74,5 +91,6 @@ struct vbs_dev_info { #define VBS_IOCTL 0xAF #define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) +#define VBS_SET_VQ _IOW(VBS_IOCTL, 0x01, struct vbs_vqs_info) #endif diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h new file mode 100644 index 000000000000..55ff810fa094 --- /dev/null +++ b/include/linux/vbs/vq.h @@ -0,0 +1,99 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Chris Torek + * Hao Li + * Define virtqueue data structures and APIs for VBS framework. + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + */ + +#ifndef _VQ_H_ +#define _VQ_H_ + +#include +#include + +/* virtqueue alignment */ +#define VRING_ALIGN 4096 +#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) + +/* PFN register shift amount */ +#define VRING_PAGE_BITS 12 + +/* virtqueue flags */ +#define VQ_ALLOC 0x01 +#define VQ_BROKED 0x02 + +/* get virtqueue size according to virtio specification */ +static inline size_t virtio_vq_ring_size(unsigned int qsz) +{ + size_t size; + + /* constant 3 below = va_flags, va_idx, va_used_event */ + size = sizeof(struct virtio_desc) * qsz + sizeof(uint16_t) * (3 + qsz); + size = roundup2(size, VRING_ALIGN); + + /* constant 3 below = vu_flags, vu_idx, vu_avail_event */ + size += sizeof(uint16_t) * 3 + sizeof(struct virtio_used) * qsz; + size = roundup2(size, VRING_ALIGN); + + return size; +} + +/* virtqueue initialization APIs */ +void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); +void virtio_vq_reset(struct virtio_vq_info *vq); + +#endif From 30923d8ad672b447bcee9581abdf1f3768638a7b Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0818/1276] VBS-K: virtqueue runtime API. This patch added the virtqueue runtime API to the VBS-K framework: - int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, struct iovec *iov, int n_iov, uint16_t *flags); - void virtio_vq_retchain(struct virtio_vq_info *vq); - void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); - void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); Change-Id: Ie7f81d96c895a16e210133c19aca99b185b8682d Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/vq.c | 270 +++++++++++++++++++++++++++++++++++++++++ include/linux/vbs/vq.h | 73 +++++++++++ 2 files changed, 343 insertions(+) diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c index 95a6757a1c85..886f48225de9 100644 --- a/drivers/vbs/vq.c +++ b/drivers/vbs/vq.c @@ -71,6 +71,276 @@ void * paddr_guest2host(struct ctx *ctx, uintptr_t gaddr, size_t len) return map_guest_phys(ctx->vmid, gaddr, len); } +/* + * helper function for vq_getchain(): + * record the i'th "real" descriptor. + */ +static inline void _vq_record(int i, volatile struct virtio_desc *vd, + struct ctx *ctx, struct iovec *iov, + int n_iov, uint16_t *flags) +{ + if (i >= n_iov) + return; + + iov[i].iov_base = paddr_guest2host(ctx, vd->addr, vd->len); + iov[i].iov_len = vd->len; + + if (flags != NULL) + flags[i] = vd->flags; +} + +/* + * Walk descriptor table and put requests into iovec. + * + * Examine the chain of descriptors starting at the "next one" to + * make sure that they describe a sensible request. If so, return + * the number of "real" descriptors that would be needed/used in + * acting on this request. This may be smaller than the number of + * available descriptors, e.g., if there are two available but + * they are two separate requests, this just returns 1. Or, it + * may be larger: if there are indirect descriptors involved, + * there may only be one descriptor available but it may be an + * indirect pointing to eight more. We return 8 in this case, + * i.e., we do not count the indirect descriptors, only the "real" + * ones. + * + * Basically, this vets the vd_flags and vd_next field of each + * descriptor and tells you how many are involved. Since some may + * be indirect, this also needs the vmctx (in the pci_vdev + * at vc->vc_pi) so that it can find indirect descriptors. + * + * As we process each descriptor, we copy and adjust it (guest to + * host address wise, also using the vmtctx) into the given iov[] + * array (of the given size). If the array overflows, we stop + * placing values into the array but keep processing descriptors, + * up to VQ_MAX_DESCRIPTORS, before giving up and returning -1. + * So you, the caller, must not assume that iov[] is as big as the + * return value (you can process the same thing twice to allocate + * a larger iov array if needed, or supply a zero length to find + * out how much space is needed). + * + * If you want to verify the WRITE flag on each descriptor, pass a + * non-NULL "flags" pointer to an array of "uint16_t" of the same size + * as n_iov and we'll copy each vd_flags field after unwinding any + * indirects. + * + * If some descriptor(s) are invalid, this prints a diagnostic message + * and returns -1. If no descriptors are ready now it simply returns 0. + * + * You are assumed to have done a vq_ring_ready() if needed (note + * that vq_has_descs() does one). + */ +int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, + struct iovec *iov, int n_iov, uint16_t *flags) +{ + int i; + unsigned int ndesc, n_indir; + unsigned int idx, next; + struct ctx *ctx; + struct virtio_dev_info *dev; + const char *name; + + volatile struct virtio_desc *vdir, *vindir, *vp; + + dev = vq->dev; + name = dev->name; + + /* + * Note: it's the responsibility of the guest not to + * update vq->vq_avail->va_idx until all of the descriptors + * the guest has written are valid (including all their + * vd_next fields and vd_flags). + * + * Compute (last_avail - va_idx) in integers mod 2**16. This is + * the number of descriptors the device has made available + * since the last time we updated vq->vq_last_avail. + * + * We just need to do the subtraction as an unsigned int, + * then trim off excess bits. + */ + idx = vq->last_avail; + ndesc = (uint16_t)((unsigned int)vq->avail->idx - idx); + + if (ndesc == 0) + return 0; + + if (ndesc > vq->qsize) { + /* XXX need better way to diagnose issues */ + pr_err("%s: ndesc (%u) out of range, driver confused?\r\n", + name, (unsigned int)ndesc); + return -1; + } + + /* + * Now count/parse "involved" descriptors starting from + * the head of the chain. + * + * To prevent loops, we could be more complicated and + * check whether we're re-visiting a previously visited + * index, but we just abort if the count gets excessive. + */ + ctx = &dev->_ctx; + *pidx = next = vq->avail->ring[idx & (vq->qsize - 1)]; + vq->last_avail++; + for (i = 0; i < VQ_MAX_DESCRIPTORS; next = vdir->next) { + if (next >= vq->qsize) { + pr_err("%s: descriptor index %u out of range, " + "driver confused?\r\n", name, next); + return -1; + } + vdir = &vq->desc[next]; + if ((vdir->flags & VRING_DESC_F_INDIRECT) == 0) { + _vq_record(i, vdir, ctx, iov, n_iov, flags); + i++; + } else if ((dev->negotiated_features & + VIRTIO_RING_F_INDIRECT_DESC) == 0) { + pr_err("%s: descriptor has forbidden INDIRECT flag, " + "driver confused?\r\n", name); + return -1; + } else { + n_indir = vdir->len / 16; + if ((vdir->len & 0xf) || n_indir == 0) { + pr_err("%s: invalid indir len 0x%x, " + "driver confused?\r\n", name, + (unsigned int)vdir->len); + return -1; + } + vindir = paddr_guest2host(ctx, vdir->addr, vdir->len); + /* + * Indirects start at the 0th, then follow + * their own embedded "next"s until those run + * out. Each one's indirect flag must be off + * (we don't really have to check, could just + * ignore errors...). + */ + next = 0; + for (;;) { + vp = &vindir[next]; + if (vp->flags & VRING_DESC_F_INDIRECT) { + pr_err("%s: indirect desc has INDIR flag," + " driver confused?\r\n", name); + return -1; + } + _vq_record(i, vp, ctx, iov, n_iov, flags); + if (++i > VQ_MAX_DESCRIPTORS) + goto loopy; + if ((vp->flags & VRING_DESC_F_NEXT) == 0) + break; + next = vp->next; + if (next >= n_indir) { + pr_err("%s: invalid next %u > %u, " + "driver confused?\r\n", + name, (unsigned int)next, n_indir); + return -1; + } + } + } + if ((vdir->flags & VRING_DESC_F_NEXT) == 0) + return i; + } +loopy: + pr_err("%s: descriptor loop? count > %d - driver confused?\r\n", + name, i); + return -1; +} + +/* + * Return the currently-first request chain back to the available queue. + * + * (This chain is the one you handled when you called vq_getchain() + * and used its positive return value.) + */ +void virtio_vq_retchain(struct virtio_vq_info *vq) +{ + vq->last_avail--; +} + +/* + * Return specified request chain to the guest, setting its I/O length + * to the provided value. + * + * (This chain is the one you handled when you called vq_getchain() + * and used its positive return value.) + */ +void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, + uint32_t iolen) +{ + uint16_t uidx, mask; + volatile struct vring_used *vuh; + volatile struct virtio_used *vue; + + /* + * Notes: + * - mask is N-1 where N is a power of 2 so computes x % N + * - vuh points to the "used" data shared with guest + * - vue points to the "used" ring entry we want to update + * - head is the same value we compute in vq_iovecs(). + * + * (I apologize for the two fields named vu_idx; the + * virtio spec calls the one that vue points to, "id"...) + */ + mask = vq->qsize - 1; + vuh = vq->used; + + uidx = vuh->idx; + vue = &vuh->ring[uidx++ & mask]; + vue->idx = idx; + vue->len = iolen; + vuh->idx = uidx; +} + +/* + * Driver has finished processing "available" chains and calling + * vq_relchain on each one. If driver used all the available + * chains, used_all should be set. + * + * If the "used" index moved we may need to inform the guest, i.e., + * deliver an interrupt. Even if the used index did NOT move we + * may need to deliver an interrupt, if the avail ring is empty and + * we are supposed to interrupt on empty. + * + * Note that used_all_avail is provided by the caller because it's + * a snapshot of the ring state when he decided to finish interrupt + * processing -- it's possible that descriptors became available after + * that point. (It's also typically a constant 1/True as well.) + */ +void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail) +{ + struct virtio_dev_info *dev; + uint16_t event_idx, new_idx, old_idx; + int intr; + + /* + * Interrupt generation: if we're using EVENT_IDX, + * interrupt if we've crossed the event threshold. + * Otherwise interrupt is generated if we added "used" entries, + * but suppressed by VRING_AVAIL_F_NO_INTERRUPT. + * + * In any case, though, if NOTIFY_ON_EMPTY is set and the + * entire avail was processed, we need to interrupt always. + */ + dev = vq->dev; + old_idx = vq->save_used; + vq->save_used = new_idx = vq->used->idx; + if (used_all_avail && + (dev->negotiated_features & VIRTIO_F_NOTIFY_ON_EMPTY)) + intr = 1; + else if (dev->negotiated_features & VIRTIO_RING_F_EVENT_IDX) { + event_idx = VQ_USED_EVENT_IDX(vq); + /* + * This calculation is per docs and the kernel + * (see src/sys/dev/virtio/virtio_ring.h). + */ + intr = (uint16_t)(new_idx - event_idx - 1) < + (uint16_t)(new_idx - old_idx); + } else { + intr = new_idx != old_idx && + !(vq->avail->flags & VRING_AVAIL_F_NO_INTERRUPT); + } + if (intr) + virtio_vq_interrupt(dev, vq); +} + /* * Initialize the currently-selected virtqueue. * The guest just gave us a page frame number, from which we can diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 55ff810fa094..9ebde05e4663 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -64,6 +64,7 @@ #include #include +#include /* virtqueue alignment */ #define VRING_ALIGN 4096 @@ -76,6 +77,30 @@ #define VQ_ALLOC 0x01 #define VQ_BROKED 0x02 +/* + * Feature flags. + * Note: bits 0 through 23 are reserved to each device type. + */ +#define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24) +#define VIRTIO_RING_F_INDIRECT_DESC (1 << 28) +#define VIRTIO_RING_F_EVENT_IDX (1 << 29) + +#define VQ_MAX_DESCRIPTORS 512 + +/* virtio_desc flags */ +#define VRING_DESC_F_NEXT (1 << 0) +#define VRING_DESC_F_WRITE (1 << 1) +#define VRING_DESC_F_INDIRECT (1 << 2) + +/* vring_avail flags */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* vring_used flags */ +#define VRING_USED_F_NO_NOTIFY 1 + +/* Functions for dealing with generalized "virtual devices" */ +#define VQ_USED_EVENT_IDX(vq) ((vq)->avail->ring[(vq)->qsize]) + /* get virtqueue size according to virtio specification */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { @@ -92,8 +117,56 @@ static inline size_t virtio_vq_ring_size(unsigned int qsz) return size; } +/* Is this ring ready for I/O? */ +static inline int virtio_vq_ring_ready(struct virtio_vq_info *vq) +{ + return (vq->flags & VQ_ALLOC); +} + +/* + * Are there "available" descriptors? (This does not count + * how many, just returns True if there are some). + */ +static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) +{ + return (virtio_vq_ring_ready(vq) && + vq->last_avail != vq->avail->idx); +} + +/* Deliver an interrupt to guest on the given virtual queue */ +static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, + struct virtio_vq_info *vq) +{ + uint16_t msix_idx; + uint64_t msix_addr; + uint32_t msix_data; + + /* Currently we only support MSIx */ + msix_idx = vq->msix_idx; + + if (msix_idx == VIRTIO_MSI_NO_VECTOR) { + pr_err("msix idx is VIRTIO_MSI_NO_VECTOR!\n"); + return; + } + + msix_addr = vq->msix_addr; + msix_data = vq->msix_data; + + pr_debug("virtio_vq_interrupt: vmid is %d\n", dev->_ctx.vmid); + vhm_inject_msi(dev->_ctx.vmid, msix_addr, msix_data); +} + + /* virtqueue initialization APIs */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); void virtio_vq_reset(struct virtio_vq_info *vq); +/* virtqueue runtime APIs */ +int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, + struct iovec *iov, int n_iov, uint16_t *flags); +void virtio_vq_retchain(struct virtio_vq_info *vq); +void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, + uint32_t iolen); +void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); + #endif From f8c52d204784e06b03b551d58e45f64fb0d3a650 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0819/1276] VBS-K: added a VBS-K reference driver. This patch implemented a VBS-K reference driver: virtio RNG. This reference driver shows how to use VBS-K APIs to implement virtio backend drivers for ACRN hypervisor. Key points from the reference driver: - Each VBS-K driver exports a char device to /dev/, e.g. /dev/vbs_rng; - Each VBS-K driver should use Virtqueue APIs to interact with the virtio frontend driver in guest; - Each VBS-K driver could register itelf as VHM (Virtio and Hypervisor service Module) client, and uses VHM API to handle frontend's register access to backend; - Each VBS-K driver could maintain the connections, from VBS-U, in a list/table, so that it could serve multiple guests. Sometimes even single guest could have multiple connections from VBS-U, depending on the device type. The reference driver shows how to maintain connections in a hashtable. Change-Id: Id590930d2f64d391ceb18c6ef491ec48412a89d8 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/Kconfig | 11 + drivers/vbs/Makefile | 2 + drivers/vbs/vbs_rng.c | 589 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 602 insertions(+) create mode 100644 drivers/vbs/vbs_rng.c diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig index 156c3162fd63..e3bb44d6a5e9 100644 --- a/drivers/vbs/Kconfig +++ b/drivers/vbs/Kconfig @@ -18,3 +18,14 @@ config VBS_DEBUG ---help--- This is an option for use by developers; most people should say N here. This enables ACRN VBS debugging. + +config VBS_RNG + tristate "ACRN VBS reference driver: virtio RNG" + depends on VBS != n + default n + ---help--- + Say M or * here to enable a VBS-K reference driver for ACRN + hypervisor, virtio RNG driver, to work with virtio-rng + frontend driver in guest. + The reference driver shows an example on how to use VBS-K + APIs. diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index cbd5076e2313..85e1cc252197 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -2,3 +2,5 @@ ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o obj-$(CONFIG_VBS) += vq.o + +obj-$(CONFIG_VBS_RNG) += vbs_rng.o diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c new file mode 100644 index 000000000000..ef6f8776e71c --- /dev/null +++ b/drivers/vbs/vbs_rng.c @@ -0,0 +1,589 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * VBS-K Reference Driver: virtio-rng + * - Each VBS-K driver exports a char device to /dev/, e.g. /dev/vbs_rng; + * - Each VBS-K driver uses Virtqueue APIs to interact with the virtio + * frontend driver in guest; + * - Each VBS-K driver registers itelf as VHM (Virtio and Hypervisor + * service Module) client, which enables in-kernel handling of register + * access of virtio device; + * - Each VBS-K driver could maintain the connections, from VBS-U, in a + * list/table, so that it could serve multiple guests. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +enum { + VBS_K_RNG_VQ = 0, + VBS_K_RNG_VQ_MAX = 1, +}; + +#define VTRND_RINGSZ 64 + +/* VBS-K features if any */ +/* + *enum { + * VBS_K_RNG_FEATURES = VBS_K_FEATURES | + * (1ULL << VIRTIO_F_VERSION_1), + *}; + */ + +struct vbs_rng { + struct virtio_dev_info dev; + struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; + int vhm_client_id; + /* Below could be device specific members */ + struct hwrng hwrng; +}; + +/* + * Each VBS-K module might serve multiple connections from multiple + * guests/device models/VBS-Us, so better to maintain the connections + * in a list, and here we use hashtalble as an example. + */ +struct vbs_rng_client { + struct vbs_rng *rng; + int vhm_client_id; + int max_vcpu; + struct vhm_request *req_buf; +}; + +/* instances malloced/freed by hashtable routines */ +struct vbs_rng_hash_entry { + struct vbs_rng_client *info; + struct hlist_node node; +}; + +#define RNG_MAX_HASH_BITS 4 /* MAX is 2^4 */ +#define HASH_NAME vbs_rng_hash + +DECLARE_HASHTABLE(HASH_NAME, RNG_MAX_HASH_BITS); +static int vbs_rng_hash_initialized = 0; +static int vbs_rng_connection_cnt = 0; + +/* function declarations */ +static int handle_kick(int client_id, int req_cnt); +static void vbs_rng_reset(struct vbs_rng *rng); +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static int vbs_rng_enable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static void vbs_rng_stop(struct vbs_rng *rng); +static void vbs_rng_flush_vq(struct vbs_rng *rng, int index); +static void vbs_rng_flush(struct vbs_rng *rng); + +/* hash table related functions */ +static void vbs_rng_hash_init(void) +{ + if (vbs_rng_hash_initialized) + return; + + hash_init(HASH_NAME); + vbs_rng_hash_initialized = 1; +} + +static int vbs_rng_hash_add(struct vbs_rng_client *client) +{ + struct vbs_rng_hash_entry *entry; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + pr_err("Failed to alloc memory for rng hash entry!\n"); + return -1; + } + + entry->info = client; + + hash_add(HASH_NAME, &entry->node, entry->info->vhm_client_id); + return 0; +} + +static struct vbs_rng_client *vbs_rng_hash_find(int client_id) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return NULL; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (entry->info->vhm_client_id == client_id) + return entry->info; + + pr_err("Not found item matching client_id!\n"); + return NULL; +} + +static int vbs_rng_hash_del(int client_id) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (entry->info->vhm_client_id == client_id) { + hash_del(&entry->node); + kfree(entry); + return 0; + } + + pr_err("%s failed, not found matching client_id!\n", + __func__); + return -1; +} + +static int vbs_rng_hash_del_all(void) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (1) { + hash_del(&entry->node); + kfree(entry); + } + + return 0; +} + +static int register_vhm_client(struct virtio_dev_info *dev) +{ + unsigned int vmid; + struct vm_info info; + struct vbs_rng_client *client; + int ret; + + client = kcalloc(1, sizeof(*client), GFP_KERNEL); + if (!client) { + pr_err("failed to malloc vbs_rng_client!\n"); + return -EINVAL; + } + + client->rng = container_of(dev, struct vbs_rng, dev); + vmid = dev->_ctx.vmid; + pr_debug("vmid is %d\n", vmid); + + client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, + "vbs_rng kick init\n"); + if (client->vhm_client_id < 0) { + pr_err("failed to create client of acrn ioreq!\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(client->vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + if (ret < 0) { + pr_err("failed to add iorange to acrn ioreq!\n"); + goto err; + } + + /* feed up max_cpu and req_buf */ + ret = vhm_get_vm_info(vmid, &info); + if (ret < 0) { + pr_err("failed in vhm_get_vm_info!\n"); + goto err; + } + client->max_vcpu = info.max_vcpu; + + client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); + if (client->req_buf == NULL) { + pr_err("failed in acrn_ioreq_get_reqbuf!\n"); + goto err; + } + + /* just attach once as vhm will kick kthread */ + acrn_ioreq_attach_client(client->vhm_client_id, 0); + + client->rng->vhm_client_id = client->vhm_client_id; + vbs_rng_hash_add(client); + + return 0; +err: + acrn_ioreq_destroy_client(client->vhm_client_id); + kfree(client); + + return -EINVAL; +} + +static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) +{ + struct iovec iov; + struct vbs_rng *sc; + struct virtio_vq_info *vq; + int len; + uint16_t idx; + + pr_debug("%s: vq_idx %d\n", __func__, vq_idx); + + sc = rng; + + if (!sc) { + pr_err("rng is NULL! Cannot proceed!\n"); + return; + } + + vq = &(sc->vqs[vq_idx]); + + pr_debug("before vq_has_desc!\n"); + + while (virtio_vq_has_descs(vq)) { + virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + + /* device specific operations, for example: */ + /* len = read(sc->vrsc_fd, iov.iov_base, iov.iov_len); */ + pr_debug("iov base %p len %lx\n", iov.iov_base, iov.iov_len); + + /* let's generate some cool data... :-) */ + len = iov.iov_len; + + pr_debug("vtrnd: vtrnd_notify(): %d\r\n", len); + + /* + * Release this chain and handle more + */ + virtio_vq_relchain(vq, idx, len); + } + virtio_vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ +} + +static int handle_kick(int client_id, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + struct vbs_rng_client *client; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + pr_debug("%s!\n", __func__); + + client = vbs_rng_hash_find(client_id); + if (!client) { + pr_err("Ooops! client %d not found!\n", client_id); + return -EINVAL; + } + + for (i = 0; i < client->max_vcpu; i++) { + req = &client->req_buf[i]; + if (req->valid && req->processed == REQ_STATE_PROCESSING && + req->client == client->vhm_client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) + /* currently we handle kick only, + * so read will return 0 + */ + req->reqs.pio_request.value = 0; + else + val = req->reqs.pio_request.value; + pr_debug("%s: ioreq type %d, direction %d, " + "addr 0x%lx, size 0x%lx, value 0x%x\n", + __func__, + req->type, + req->reqs.pio_request.direction, + req->reqs.pio_request.address, + req->reqs.pio_request.size, + req->reqs.pio_request.value); + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(client->vhm_client_id, + 1 << i); + } + } + + if (val >= 0) + handle_vq_kick(client->rng, val); + return 0; +} + +static int vbs_rng_open(struct inode *inode, struct file *f) +{ + struct vbs_rng *rng; + struct virtio_dev_info *dev; + struct virtio_vq_info *vqs; + int i; + + pr_debug("%s!\n", __func__); + + rng = kmalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) { + pr_err("Failed to allocate memory for vbs_rng!\n"); + return -ENOMEM; + } + + dev = &rng->dev; + vqs = (struct virtio_vq_info *)&rng->vqs; + + for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) { + vqs[i].dev = dev; + /* + * Currently relies on VHM to kick us, + * thus vq_notify not used + */ + vqs[i].vq_notify = NULL; + } + + /* link dev and vqs */ + dev->vqs = vqs; + + virtio_dev_init(dev, vqs, VBS_K_RNG_VQ_MAX); + + f->private_data = rng; + vbs_rng_hash_init(); + + return 0; +} + +static int vbs_rng_release(struct inode *inode, struct file *f) +{ + struct vbs_rng *rng = f->private_data; + struct vbs_rng_client *client; + int i; + + pr_debug("%s!\n", __func__); + + client = vbs_rng_hash_find(rng->vhm_client_id); + if (!client) + pr_err("%s: UNLIKELY not found client!\n", + __func__); + + vbs_rng_stop(rng); + vbs_rng_flush(rng); + for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) + virtio_vq_reset(&(rng->vqs[i])); + + /* device specific release */ + vbs_rng_reset(rng); + + pr_debug("vbs_rng_connection cnt is %d\n", vbs_rng_connection_cnt); + + if (client && vbs_rng_connection_cnt--) + vbs_rng_hash_del(client->vhm_client_id); + if (!vbs_rng_connection_cnt) { + pr_debug("vbs_rng remove all hash entries\n"); + vbs_rng_hash_del_all(); + } + + kfree(client); + kfree(rng); + + pr_debug("%s done\n", __func__); + return 0; +} + +static struct hwrng get_hwrng(struct vbs_rng *rng) +{ + return rng->hwrng; +} + +/* Set feature bits in kernel side device */ +static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) +{ + return 0; +} + +static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct vbs_rng *rng = f->private_data; + void __user *argp = (void __user *)arg; + /*u64 __user *featurep = argp;*/ + /*u64 features;*/ + int r; + + switch (ioctl) { +/* + * case VHOST_GET_FEATURES: + * features = VHOST_NET_FEATURES; + * if (copy_to_user(featurep, &features, sizeof features)) + * return -EFAULT; + * return 0; + * case VHOST_SET_FEATURES: + * if (copy_from_user(&features, featurep, sizeof features)) + * return -EFAULT; + * if (features & ~VHOST_NET_FEATURES) + * return -EOPNOTSUPP; + * return vhost_net_set_features(n, features); + */ + case VBS_SET_VQ: + /* we handle this here because we want to register VHM client + * after handling VBS_K_SET_VQ request + */ + pr_debug("VBS_K_SET_VQ ioctl:\n"); + r = virtio_vqs_ioctl(&rng->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) { + pr_err("VBS_K_SET_VQ: virtio_vqs_ioctl failed!\n"); + return -EFAULT; + } + /* Register VHM client */ + if (register_vhm_client(&rng->dev) < 0) { + pr_err("failed to register VHM client!\n"); + return -EFAULT; + } + vbs_rng_connection_cnt++; + return r; + default: + /*mutex_lock(&n->dev.mutex);*/ + pr_debug("VBS_K generic ioctls!\n"); + r = virtio_dev_ioctl(&rng->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&rng->dev, ioctl, argp); + else + vbs_rng_flush(rng); + /*mutex_unlock(&n->dev.mutex);*/ + return r; + } +} + +/* device specific function to cleanup itself */ +static void vbs_rng_reset(struct vbs_rng *rng) +{ +} + +/* device specific function */ +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static int vbs_rng_enable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ + return 0; +} + +/* device specific function */ +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static void vbs_rng_stop(struct vbs_rng *rng) +{ +} + +/* device specific function */ +static void vbs_rng_flush_vq(struct vbs_rng *rng, int index) +{ +} + +/* device specific function */ +static void vbs_rng_flush(struct vbs_rng *rng) +{ +} + +static const struct file_operations vbs_rng_fops = { + .owner = THIS_MODULE, + .release = vbs_rng_release, + .unlocked_ioctl = vbs_rng_ioctl, + .open = vbs_rng_open, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_rng_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_rng", + .fops = &vbs_rng_fops, +}; + +static int vbs_rng_init(void) +{ + return misc_register(&vbs_rng_misc); +} +module_init(vbs_rng_init); + +static void vbs_rng_exit(void) +{ + misc_deregister(&vbs_rng_misc); +} +module_exit(vbs_rng_exit); + +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Virtio Backend Service reference driver on ACRN hypervisor"); From 5bbc36098bf42c290a6b49c536def0a28f99e629 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0820/1276] hypercall: refine hypercall interfaces - HC_VM_SET_MEMMAP: remove MAP_UPDATE, refine API structure - HC_NOTIFY_REQUEST_FINISH: use vcpu_id instead of vcpu_mask - HC_VM_GPA2HPA: refine API structure - HC_SET_IOREQ_BUFFER: refine API structure - IC_XXXX_IRQLINE: refine API structure Change-Id: Ie9f6af005160a807335b2f266d7139abc06d8db9 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Edwin Zhai Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 5 +- drivers/vbs/vbs_rng.c | 3 +- drivers/vhm/vhm_hypercall.c | 4 +- drivers/vhm/vhm_ioreq.c | 26 ++--- drivers/vhm/vhm_mm.c | 29 ++--- include/linux/vhm/acrn_common.h | 181 ++++++++++------------------- include/linux/vhm/acrn_hv_defs.h | 63 +++++++--- include/linux/vhm/acrn_vhm_ioreq.h | 6 +- include/linux/vhm/acrn_vhm_mm.h | 21 +--- include/linux/vhm/vhm_hypercall.h | 3 +- include/linux/vhm/vhm_ioctl_defs.h | 5 + 11 files changed, 152 insertions(+), 194 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index ebada9a11552..e36a5e225e12 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -299,14 +299,13 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_NOTIFY_REQUEST_FINISH: { - struct acrn_ioreq_notify notify; + struct ioreq_notify notify; if (copy_from_user(¬ify, (void *)ioctl_param, sizeof(notify))) return -EFAULT; - ret = acrn_ioreq_complete_request(notify.client_id, - notify.vcpu_mask); + ret = acrn_ioreq_complete_request(notify.client_id, notify.vcpu); if (ret < 0) return -EFAULT; break; diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index ef6f8776e71c..f2234e73034d 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -369,8 +369,7 @@ static int handle_kick(int client_id, int req_cnt) req->reqs.pio_request.size, req->reqs.pio_request.value); req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(client->vhm_client_id, - 1 << i); + acrn_ioreq_complete_request(client->vhm_client_id, i); } } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 11ca6b86baed..94a95933d51e 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -87,9 +87,9 @@ inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); } -inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu) { - return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu); } inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 6054e3d00eb2..08826c575780 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -67,7 +67,7 @@ struct ioreq_range { struct list_head list; - enum request_type type; + uint32_t type; long start; long end; }; @@ -130,7 +130,7 @@ static DECLARE_BITMAP(client_bitmap, MAX_CLIENT); static void acrn_ioreq_notify_client(struct ioreq_client *client); -static inline bool is_range_type(enum request_type type) +static inline bool is_range_type(uint32_t type) { return (type == REQ_MMIO || type == REQ_PORTIO || type == REQ_WP); } @@ -335,7 +335,7 @@ static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) * NOTE: here just add iorange entry directly, no check for the overlap.. * please client take care of it */ -int acrn_ioreq_add_iorange(int client_id, enum request_type type, +int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end) { struct ioreq_client *client; @@ -375,7 +375,7 @@ int acrn_ioreq_add_iorange(int client_id, enum request_type type, return 0; } -int acrn_ioreq_del_iorange(int client_id, enum request_type type, +int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end) { struct ioreq_client *client; @@ -617,13 +617,6 @@ static bool req_in_range(struct ioreq_range *range, struct vhm_request *req) ret = true; break; } - case REQ_MSR: /*TODO: add bitmap for MSR range */ - case REQ_CPUID: - case REQ_EXIT: - { - ret = true; - break; - } default: ret = false; @@ -707,7 +700,7 @@ static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) if (req_handled) { req->processed = REQ_STATE_SUCCESS; - if (hcall_notify_req_finish(vm->vmid, 1 << vcpu) < 0) { + if (hcall_notify_req_finish(vm->vmid, vcpu) < 0) { pr_err("vhm-ioreq: failed to " "notify request finished !\n"); return -EFAULT; @@ -815,7 +808,7 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) return 0; } -int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) { struct ioreq_client *client; int ret; @@ -830,9 +823,8 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) return -EINVAL; } - atomic_sub(bitmap_weight((unsigned long *)&vcpu_mask, - VHM_REQUEST_MAX), &client->req); - ret = hcall_notify_req_finish(client->vmid, vcpu_mask); + atomic_dec(&client->req); + ret = hcall_notify_req_finish(client->vmid, vcpu); if (ret < 0) { pr_err("vhm-ioreq: failed to notify request finished !\n"); return -EFAULT; @@ -887,7 +879,7 @@ int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) vm->req_buf = page_address(page); vm->pg = page; - set_buffer.req_buf = (long) page_to_phys(page); + set_buffer.req_buf = page_to_phys(page); ret = hcall_set_ioreq_buffer(vm->vmid, virt_to_phys(&set_buffer)); if (ret < 0) { diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 61ebb8c508d2..b475aa91a348 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -152,13 +152,14 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) } static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot, int type) + unsigned long host_gpa, unsigned long len, + unsigned int prot, unsigned int type) { struct vm_set_memmap set_memmap; set_memmap.type = type; - set_memmap.foreign_gpa = guest_gpa; - set_memmap.hvm_gpa = host_gpa; + set_memmap.remote_gpa = guest_gpa; + set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; set_memmap.prot = prot; @@ -177,24 +178,24 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, } int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, prot, MAP_MMIO); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, prot, MAP_UNMAP); } -int update_mem_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) +int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_UPDATE); + prot, MAP_MMIO); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) @@ -217,18 +218,18 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) } seg->prot = memmap->mem.prot; set_memmap.type = MAP_MEM; - set_memmap.foreign_gpa = seg->gpa; - set_memmap.hvm_gpa = seg->base; + set_memmap.remote_gpa = seg->gpa; + set_memmap.vm0_gpa = seg->base; set_memmap.length = seg->len; set_memmap.prot = seg->prot; - set_memmap.prot |= MMU_MEM_ATTR_WB_CACHE; + set_memmap.prot |= MEM_ATTR_WB_CACHE; } else { set_memmap.type = MAP_MMIO; - set_memmap.foreign_gpa = memmap->mmio.gpa; - set_memmap.hvm_gpa = memmap->mmio.hpa; + set_memmap.remote_gpa = memmap->mmio.gpa; + set_memmap.vm0_gpa = memmap->mmio.hpa; set_memmap.length = memmap->mmio.len; set_memmap.prot = memmap->mmio.prot; - set_memmap.prot |= MMU_MEM_ATTR_UNCACHED; + set_memmap.prot |= MEM_ATTR_UNCACHED; } /* hypercall to notify hv the guest EPT setting*/ diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 0e9293d08e01..aa61fbed2596 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -1,5 +1,5 @@ /* - * virtio and hyperviosr service module (VHM): commom.h + * common definition * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -53,125 +53,89 @@ #define ACRN_COMMON_H /* - * Commmon structures for ACRN/VHM/DM + * Common structures for ACRN/VHM/DM */ -/* ISA type - * inject interrut to both PIC and IOAPIC - */ -enum interrupt_type { - ACRN_INTR_TYPE_ISA, - ACRN_INTR_TYPE_IOAPIC, -} __attribute__((aligned(4))); - /* * IO request */ #define VHM_REQUEST_MAX 16 -enum request_state { - REQ_STATE_SUCCESS = 1, - REQ_STATE_PENDING = 0, - REQ_STATE_PROCESSING = 2, - REQ_STATE_FAILED = -1, -} __attribute__((aligned(4))); - -enum request_type { - REQ_MSR, - REQ_CPUID, - REQ_PORTIO, - REQ_MMIO, - REQ_PCICFG, - REQ_WP, - REQ_EXIT, - REQ_MAX, -} __attribute__((aligned(4))); - -enum request_direction { - REQUEST_READ, - REQUEST_WRITE, - DIRECTION_MAX, -} __attribute__((aligned(4))); +#define REQ_STATE_PENDING 0 +#define REQ_STATE_SUCCESS 1 +#define REQ_STATE_PROCESSING 2 +#define REQ_STATE_FAILED -1 -/* - * IRQ type for ptdev - */ -enum irq_type { - IRQ_INTX, - IRQ_MSI, - IRQ_MSIX, -} __attribute__((aligned(4))); - -struct msr_request { - enum request_direction direction; - long index; - long value; -} __attribute__((aligned(8))); +#define REQ_PORTIO 0 +#define REQ_MMIO 1 +#define REQ_PCICFG 2 +#define REQ_WP 3 +#define REQUEST_READ 0 +#define REQUEST_WRITE 1 struct mmio_request { - enum request_direction direction; - long address; - long size; - long value; + uint32_t direction; + uint32_t reserved; + int64_t address; + int64_t size; + int64_t value; } __attribute__((aligned(8))); -struct io_request { - enum request_direction direction; - long address; - long size; - int value; +struct pio_request { + uint32_t direction; + uint32_t reserved; + int64_t address; + int64_t size; + int32_t value; } __attribute__((aligned(8))); struct pci_request { - enum request_direction direction; - long reserve; /*io_request address*/ - long size; - int value; - int bus; - int dev; - int func; - int reg; + uint32_t direction; + uint32_t reserved[3];/* need keep same header fields with pio_request */ + int64_t size; + int32_t value; + int32_t bus; + int32_t dev; + int32_t func; + int32_t reg; } __attribute__((aligned(8))); /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ union { - int exitcode; - enum request_type type; - unsigned long rip; - int reserved0[16]; + uint32_t type; + int32_t reserved0[16]; }; /* offset: 64bytes-127bytes */ union { - struct msr_request msr_request; - struct io_request pio_request; + struct pio_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; - long reserved1[8]; + int64_t reserved1[8]; } reqs; /* True: valid req which need VHM to process. * ACRN write, VHM read only **/ - int valid; + int32_t valid; /* the client which is distributed to handle this request */ - int client; + int32_t client; /* 1: VHM had processed and success * 0: VHM had not yet processed * -1: VHM failed to process. Invalid request * VHM write, ACRN read only **/ - enum request_state processed; + int32_t processed; } __attribute__((aligned(256))); struct vhm_request_buffer { union { struct vhm_request req_queue[VHM_REQUEST_MAX]; - char reserved[4096]; + int8_t reserved[4096]; }; } __attribute__((aligned(4096))); @@ -182,69 +146,52 @@ struct acrn_create_vm { } __attribute__((aligned(8))); struct acrn_create_vcpu { - int vcpuid; /* IN: vcpu id */ + int vcpuid; /* IN: vcpu id */ int pcpuid; /* IN: pcpu id */ } __attribute__((aligned(8))); struct acrn_set_ioreq_buffer { - long req_buf; /* IN: gpa of per VM request_buffer*/ + uint64_t req_buf; /* IN: gpa of per VM request_buffer*/ } __attribute__((aligned(8))); -struct acrn_ioreq_notify { - int client_id; - unsigned long vcpu_mask; -} __attribute__((aligned(8))); +/* + * intr type + * IOAPIC: inject interrupt to IOAPIC + * ISA: inject interrupt to both PIC and IOAPIC + */ +#define ACRN_INTR_TYPE_ISA 0 +#define ACRN_INTR_TYPE_IOAPIC 1 /* For ISA, PIC, IOAPIC etc */ struct acrn_irqline { - enum interrupt_type intr_type; - unsigned long pic_irq; /* IN: for ISA type */ - unsigned long ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ + uint32_t intr_type; + uint32_t reserved; + uint64_t pic_irq; /* IN: for ISA type */ + uint64_t ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ } __attribute__((aligned(8))); /* For MSI type inject */ struct acrn_msi_entry { - unsigned long msi_addr; /* IN: addr[19:12] with dest vcpu id */ - unsigned long msi_data; /* IN: data[7:0] with vector */ + uint64_t msi_addr; /* IN: addr[19:12] with dest vcpu id */ + uint64_t msi_data; /* IN: data[7:0] with vector */ } __attribute__((aligned(8))); /* For NMI inject */ struct acrn_nmi_entry { - unsigned long vcpuid; /* IN: -1 means vcpu0 */ -} __attribute__((aligned(8))); - -struct acrn_ptdev_irq { - enum irq_type type; - unsigned short virt_bdf; /* IN: Device virtual BDF# */ - unsigned short phys_bdf; /* IN: Device physical BDF# */ - union { - struct { - int virt_pin; /* IN: virtual IOAPIC pin */ - int phys_pin; /* IN: physical IOAPIC pin */ - bool pic_pin; /* IN: pin from PIC? */ - } intx; - struct { - int vector_cnt; /* IN: vector count of MSI/MSIX */ - - /* IN: physcial address of MSI-X table */ - unsigned long table_paddr; - - /* IN: size of MSI-X table (round up to 4K) */ - int table_size; - } msix; - }; + int64_t vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); struct acrn_vm_pci_msix_remap { - unsigned short virt_bdf; /* IN: Device virtual BDF# */ - unsigned short phys_bdf; /* IN: Device physical BDF# */ - unsigned short msi_ctl; /* IN: PCI MSI/x cap control data */ - unsigned long msi_addr; /* IN/OUT: msi address to fix */ - unsigned int msi_data; /* IN/OUT: msi data to fix */ - int msix; /* IN: 0 - MSI, 1 - MSI-X */ - int msix_entry_index; /* IN: MSI-X the entry table index */ + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + uint16_t msi_ctl; /* IN: PCI MSI/x cap control data */ + uint16_t reserved; + uint64_t msi_addr; /* IN/OUT: msi address to fix */ + uint32_t msi_data; /* IN/OUT: msi data to fix */ + int32_t msix; /* IN: 0 - MSI, 1 - MSI-X */ + int32_t msix_entry_index; /* IN: MSI-X the entry table index */ /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ - unsigned int vector_ctl; + uint32_t vector_ctl; } __attribute__((aligned(8))); #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index eeac0e9b4e76..fa32243a6407 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -1,5 +1,5 @@ /* - * virtio and hyperviosr service module (VHM): hypercall header + * hypercall definition * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -53,12 +53,12 @@ #define ACRN_HV_DEFS_H /* - * Commmon structures for ACRN/VHM/DM + * Common structures for ACRN/VHM/DM */ #include "acrn_common.h" /* - * Commmon structures for HV/VHM + * Common structures for HV/VHM */ #define _HC_ID(x, y) (((x)<<24)|(y)) @@ -101,35 +101,59 @@ #define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) #define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) +/* TRACE */ +#define HC_ID_TRACE_BASE 0x600UL +#define HC_ACRN_SBUF_SETUP _HC_ID(HC_ID, HC_ID_TRACE_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) -enum vm_memmap_type { - MAP_MEM = 0, - MAP_MMIO, - MAP_UNMAP, - MAP_UPDATE, -}; +/* Generic memory attributes */ +#define MEM_ATTR_READ 0x00000001 +#define MEM_ATTR_WRITE 0x00000002 +#define MEM_ATTR_EXECUTE 0x00000004 +#define MEM_ATTR_USER 0x00000008 +#define MEM_ATTR_WB_CACHE 0x00000040 +#define MEM_ATTR_WT_CACHE 0x00000080 +#define MEM_ATTR_UNCACHED 0x00000100 +#define MEM_ATTR_WC 0x00000200 +#define MEM_ATTR_WP 0x00000400 + +#define MEM_ATTR_ALL 0x00000007 +#define MEM_ATTR_WRITE_PROT 0x00000005 +#define MEM_ATTR_ALL_WB 0x00000047 +#define MEM_ATTR_ALL_WC 0x00000207 struct vm_set_memmap { - enum vm_memmap_type type; +#define MAP_MEM 0 +#define MAP_MMIO 1 +#define MAP_UNMAP 2 + uint32_t type; + uint32_t reserved; + /* IN: beginning guest GPA to map */ - unsigned long foreign_gpa; + uint64_t remote_gpa; /* IN: VM0's GPA which foreign gpa will be mapped to */ - unsigned long hvm_gpa; + uint64_t vm0_gpa; /* IN: length of the range */ - unsigned long length; + uint64_t length; - /* IN: not used right now */ - int prot; + /* IN: mem attr */ + uint32_t prot; +} __attribute__((aligned(8))); + +struct sbuf_setup_param { + uint32_t pcpu_id; + uint32_t sbuf_id; + uint64_t gpa; } __attribute__((aligned(8))); struct vm_gpa2hpa { - unsigned long gpa; /* IN: gpa to translation */ - unsigned long hpa; /* OUT: -1 means invalid gpa */ + uint64_t gpa; /* IN: gpa to translation */ + uint64_t hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); struct hc_ptdev_irq { @@ -152,4 +176,9 @@ struct hc_ptdev_irq { }; } __attribute__((aligned(8))); +struct hc_api_version { + uint32_t major_version; + uint32_t minor_version; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index 0daf46dcf9f7..fcec2c1e2eac 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -63,16 +63,16 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); void acrn_ioreq_destroy_client(int client_id); -int acrn_ioreq_add_iorange(int client_id, enum request_type type, +int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end); -int acrn_ioreq_del_iorange(int client_id, enum request_type type, +int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end); struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); int acrn_ioreq_distribute_request(struct vhm_vm *vm); -int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask); +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu); void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); void acrn_ioreq_unintercept_bdf(int client_id); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 1af6fd3aa11b..f0401ac6a942 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -57,19 +57,6 @@ #include #include -#define MMU_MEM_ATTR_READ 0x00000001 -#define MMU_MEM_ATTR_WRITE 0x00000002 -#define MMU_MEM_ATTR_EXECUTE 0x00000004 -#define MMU_MEM_ATTR_WB_CACHE 0x00000040 -#define MMU_MEM_ATTR_WT_CACHE 0x00000080 -#define MMU_MEM_ATTR_UNCACHED 0x00000100 -#define MMU_MEM_ATTR_WC 0x00000200 - -#define MMU_MEM_ATTR_ALL 0x00000007 -#define MMU_MEM_ATTR_WP 0x00000005 -#define MMU_MEM_ATTR_ALL_WB 0x00000047 -#define MMU_MEM_ATTR_ALL_WC 0x00000207 - /* 1:1 mapping for service OS */ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) { @@ -79,11 +66,11 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); + unsigned long host_gpa, unsigned long len, unsigned int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); -int update_mem_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); + unsigned long host_gpa, unsigned long len, unsigned int prot); +int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, unsigned int prot); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 35bb48ae6cd3..5447e951bf4b 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -146,8 +146,7 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); -inline long hcall_notify_req_finish(unsigned long vmid, - unsigned long vcpu_mask); +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index df07e3c93467..79d91a858226 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -156,4 +156,9 @@ struct ic_ptdev_irq { }; }; +struct ioreq_notify { + int32_t client_id; + uint32_t vcpu; +}; + #endif /* VHM_IOCTL_DEFS_H */ From 86e8c459a2c5196a3c19a9aeb4f54e171a7b7d6a Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0821/1276] vhm: refine vm related hypercall/ioctrl Change-Id: I028f59998733f3d066e2ead7768297570d97bf22 Tracked-On:218445 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 17 ++++------------- drivers/vhm/vhm_hypercall.c | 9 ++------- include/linux/vhm/acrn_common.h | 11 +++++++---- include/linux/vhm/acrn_hv_defs.h | 5 ++--- include/linux/vhm/vhm_hypercall.h | 2 +- include/linux/vhm/vhm_ioctl_defs.h | 5 ++--- 6 files changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index e36a5e225e12..d3fd572c1642 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -189,12 +189,12 @@ static long vhm_dev_ioctl(struct file *filep, vm->vmid = created_vm.vmid; - pr_info("vhm: VM %ld created\n", created_vm.vmid); + pr_info("vhm: VM %d created\n", created_vm.vmid); break; } - case IC_RESUME_VM: { - ret = hcall_resume_vm(vm->vmid); + case IC_START_VM: { + ret = hcall_start_vm(vm->vmid); if (ret < 0) { pr_err("vhm: failed to start VM %ld!\n", vm->vmid); return -EFAULT; @@ -221,15 +221,6 @@ static long vhm_dev_ioctl(struct file *filep, break; } - case IC_QUERY_VMSTATE: { - ret = hcall_query_vm_state(vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); - return -EFAULT; - } - return ret; - } - case IC_CREATE_VCPU: { struct acrn_create_vcpu cv; @@ -240,7 +231,7 @@ static long vhm_dev_ioctl(struct file *filep, ret = acrn_hypercall2(HC_CREATE_VCPU, vm->vmid, virt_to_phys(&cv)); if (ret < 0) { - pr_err("vhm: failed to create vcpu %ld!\n", cv.vcpuid); + pr_err("vhm: failed to create vcpu %d!\n", cv.vcpu_id); return -EFAULT; } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 94a95933d51e..b2738474afaf 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -57,9 +57,9 @@ inline long hcall_create_vm(unsigned long vminfo) return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); } -inline long hcall_resume_vm(unsigned long vmid) +inline long hcall_start_vm(unsigned long vmid) { - return acrn_hypercall1(HC_RESUME_VM, vmid); + return acrn_hypercall1(HC_START_VM, vmid); } inline long hcall_pause_vm(unsigned long vmid) @@ -72,11 +72,6 @@ inline long hcall_destroy_vm(unsigned long vmid) return acrn_hypercall1(HC_DESTROY_VM, vmid); } -inline long hcall_query_vm_state(unsigned long vmid) -{ - return acrn_hypercall1(HC_QUERY_VMSTATE, vmid); -} - inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index aa61fbed2596..23f80acd92f6 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -141,13 +141,16 @@ struct vhm_request_buffer { /* Common API params */ struct acrn_create_vm { - unsigned long vmid; /* OUT: HV return vmid to VHM */ - unsigned long vcpu_num; /* IN: VM vcpu number */ + int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ + uint32_t vcpu_num; /* IN: VM vcpu number */ + uint8_t GUID[16]; /* IN: GUID of this vm */ + uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); struct acrn_create_vcpu { - int vcpuid; /* IN: vcpu id */ - int pcpuid; /* IN: pcpu id */ + uint32_t vcpu_id; /* IN: vcpu id */ + uint32_t pcpu_id; /* IN: pcpu id */ } __attribute__((aligned(8))); struct acrn_set_ioreq_buffer { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index fa32243a6407..eb1d4c974a3e 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -70,10 +70,9 @@ #define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) #define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) #define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) -#define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) -#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x100UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 5447e951bf4b..b40f8f898046 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -138,7 +138,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, } inline long hcall_create_vm(unsigned long vminfo); -inline long hcall_resume_vm(unsigned long vmid); +inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 79d91a858226..e157d6a86a66 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -60,10 +60,9 @@ #define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) #define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) #define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) -#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x100UL From 5461eeeb6e017c1da1011b546cdb5b5da674f978 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0822/1276] hypercall: refine HC ID and parameter number Change-Id: Ie5b73055add4c69b4dbf5cae1bb8bf941997ee6b Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_hypercall.c | 7 +++++- include/linux/vhm/acrn_common.h | 10 ++++----- include/linux/vhm/acrn_hv_defs.h | 36 ++++++++++++++++--------------- include/linux/vhm/vhm_hypercall.h | 1 + 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index b2738474afaf..741b8bd837cc 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -52,9 +52,14 @@ #include #include +inline long hcall_get_api_version(unsigned long api_version) +{ + return acrn_hypercall1(HC_GET_API_VERSION, api_version); +} + inline long hcall_create_vm(unsigned long vminfo) { - return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); + return acrn_hypercall1(HC_CREATE_VM, vminfo); } inline long hcall_start_vm(unsigned long vmid) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 23f80acd92f6..a6f46648c853 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -141,11 +141,11 @@ struct vhm_request_buffer { /* Common API params */ struct acrn_create_vm { - int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ - uint32_t vcpu_num; /* IN: VM vcpu number */ - uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t trusty_enabled;/* IN: whether trusty is enabled */ - uint8_t reserved[31]; /* Reserved for future use */ + int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ + uint32_t vcpu_num; /* IN: VM vcpu number */ + uint8_t GUID[16]; /* IN: GUID of this vm */ + uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); struct acrn_create_vcpu { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index eb1d4c974a3e..bb57fb4f5cdd 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -63,49 +63,51 @@ #define _HC_ID(x, y) (((x)<<24)|(y)) -#define HC_ID 0x7FUL +#define HC_ID 0x80UL + +/* general */ +#define HC_ID_GEN_BASE 0x0UL +#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x00) /* VM management */ -#define HC_ID_VM_BASE 0x0UL -#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) -#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) -#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) -#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) -#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_ID_VM_BASE 0x10UL +#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) +#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) +#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) +#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) /* IRQ and Interrupts */ -#define HC_ID_IRQ_BASE 0x100UL +#define HC_ID_IRQ_BASE 0x20UL #define HC_ASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x00) #define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) /* DM ioreq management */ -#define HC_ID_IOREQ_BASE 0x200UL +#define HC_ID_IOREQ_BASE 0x30UL #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) #define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) - /* Guest memory management */ -#define HC_ID_MEM_BASE 0x300UL +#define HC_ID_MEM_BASE 0x40UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) /* PCI assignment*/ -#define HC_ID_PCI_BASE 0x400UL +#define HC_ID_PCI_BASE 0x50UL #define HC_ASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x00) #define HC_DEASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x01) #define HC_VM_PCI_MSIX_REMAP _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02) #define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) #define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) -/* TRACE */ -#define HC_ID_TRACE_BASE 0x600UL -#define HC_ACRN_SBUF_SETUP _HC_ID(HC_ID, HC_ID_TRACE_BASE + 0x00) +/* DEBUG */ +#define HC_ID_DBG_BASE 0x60UL +#define HC_SBUF_SETUP _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) #define ACRN_DOM0_VMID (0UL) -#define ACRN_INVALID_VMID (-1UL) +#define ACRN_INVALID_VMID (-1) #define ACRN_INVALID_HPA (-1UL) /* Generic memory attributes */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index b40f8f898046..f4a5793f3ef7 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -137,6 +137,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, return result; } +inline long hcall_get_api_version(unsigned long api_version); inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); From 0fe44f11527002b9197852da10a01b1895b884bb Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0823/1276] ioctl: cleanup ioctl structure vm_memseg/vm_memmap: remove unused fileds and define field size Change-Id: I9cb01cc6ea8eb97989e0b4b4ff6c55fa9b9822c8 Tracked-On: 218445 Signed-off-by: Edwin Zhai Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/vhm_ioctl_defs.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index e157d6a86a66..60bfb299e040 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -60,7 +60,7 @@ #define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) #define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) #define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) @@ -95,35 +95,31 @@ #define SPECNAMELEN 63 -enum { - VM_SYSMEM, - VM_BOOTROM, - VM_FRAMEBUFFER, - VM_MMIO, -}; +#define VM_SYSMEM 0 +#define VM_MMIO 1 struct vm_memseg { - int segid; - size_t len; + uint32_t segid; + uint32_t reserved; + uint64_t len; + uint64_t gpa; char name[SPECNAMELEN + 1]; - unsigned long gpa; }; struct vm_memmap { - int segid; /* memory segment */ + uint32_t segid; /* memory segment */ + uint32_t reserved; union { struct { uint64_t gpa; - uint64_t segoff; /* offset into memory segment */ - size_t len; /* mmap length */ - int prot; /* RWX */ - int flags; + uint64_t len; /* mmap length */ + uint32_t prot; /* RWX */ } mem; struct { uint64_t gpa; uint64_t hpa; - size_t len; - int prot; + uint64_t len; + uint32_t prot; } mmio; }; }; From c41fb00c6bd2ca60c0ec324faac7b7c5e82cbe33 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0824/1276] Shared_buf: add shared buffer Added a ring buffer shared between ACRN hypervisor and service OS. Change-Id: Ib82f50d842592099629e0f764e0576306252c51b Tracked-On: Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/acrn/Kconfig | 5 ++ drivers/acrn/Makefile | 1 + drivers/acrn/sbuf.c | 188 ++++++++++++++++++++++++++++++++++++++++++ drivers/acrn/sbuf.h | 119 ++++++++++++++++++++++++++ 6 files changed, 315 insertions(+) create mode 100644 drivers/acrn/Kconfig create mode 100644 drivers/acrn/Makefile create mode 100644 drivers/acrn/sbuf.c create mode 100644 drivers/acrn/sbuf.h diff --git a/drivers/Kconfig b/drivers/Kconfig index b9ac6b8cbdd4..ae0772a3581a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -221,4 +221,5 @@ source "drivers/slimbus/Kconfig" source "drivers/vbs/Kconfig" +source "drivers/acrn/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 6d0c3cd78ffd..22376163e35d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -185,6 +185,7 @@ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_ACRN) += vhm/ +obj-$(CONFIG_ACRN) += acrn/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig new file mode 100644 index 000000000000..f25f0ae77727 --- /dev/null +++ b/drivers/acrn/Kconfig @@ -0,0 +1,5 @@ +config ACRN_SHARED_BUFFER + bool "Intel ACRN SHARED BUFFER" + ---help--- + Ring buffer shared between ACRN Hypervisor and its SOS. + Help ACRN performance profiling. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile new file mode 100644 index 000000000000..bc475f8116e3 --- /dev/null +++ b/drivers/acrn/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c new file mode 100644 index 000000000000..dcf203222c5b --- /dev/null +++ b/drivers/acrn/sbuf.c @@ -0,0 +1,188 @@ +/* + * shared buffer + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ + +#define pr_fmt(fmt) "SBuf: " fmt + +#include +#include +#include "sbuf.h" + +static inline bool sbuf_is_empty(shared_buf_t *sbuf) +{ + return (sbuf->head == sbuf->tail); +} + +static inline uint32_t sbuf_next_ptr(uint32_t pos, + uint32_t span, uint32_t scope) +{ + pos += span; + pos = (pos >= scope) ? (pos - scope) : pos; + return pos; +} + +static inline uint32_t sbuf_calculate_allocate_size(uint32_t ele_num, + uint32_t ele_size) +{ + uint64_t sbuf_allocate_size; + + sbuf_allocate_size = ele_num * ele_size; + sbuf_allocate_size += SBUF_HEAD_SIZE; + if (sbuf_allocate_size > SBUF_MAX_SIZE) { + pr_err("num=0x%x, size=0x%x exceed 0x%llx!\n", + ele_num, ele_size, SBUF_MAX_SIZE); + return 0; + } + + /* align to PAGE_SIZE */ + return (sbuf_allocate_size + PAGE_SIZE - 1) & PAGE_MASK; +} + +shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size) +{ + shared_buf_t *sbuf; + struct page *page; + uint32_t sbuf_allocate_size; + + if (!ele_num || !ele_size) { + pr_err("invalid parameter %s!\n", __func__); + return NULL; + } + + sbuf_allocate_size = sbuf_calculate_allocate_size(ele_num, ele_size); + if (!sbuf_allocate_size) + return NULL; + + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order(sbuf_allocate_size)); + if (page == NULL) { + pr_err("failed to alloc pages!\n"); + return NULL; + } + + sbuf = phys_to_virt(page_to_phys(page)); + sbuf->ele_num = ele_num; + sbuf->ele_size = ele_size; + sbuf->size = ele_num * ele_size; + sbuf->magic = SBUF_MAGIC; + pr_info("ele_num=0x%x, ele_size=0x%x allocated!\n", + ele_num, ele_size); + return sbuf; +} +EXPORT_SYMBOL(sbuf_allocate); + +void sbuf_free(shared_buf_t *sbuf) +{ + uint32_t sbuf_allocate_size; + + if ((sbuf == NULL) || sbuf->magic != SBUF_MAGIC) { + pr_err("invalid parameter %s\n", __func__); + return; + } + + sbuf_allocate_size = sbuf_calculate_allocate_size(sbuf->ele_num, + sbuf->ele_size); + if (!sbuf_allocate_size) + return; + + sbuf->magic = 0; + __free_pages((struct page *)virt_to_page(sbuf), + get_order(sbuf_allocate_size)); +} +EXPORT_SYMBOL(sbuf_free); + +int sbuf_get(shared_buf_t *sbuf, uint8_t *data) +{ + const void *from; + + if ((sbuf == NULL) || (data == NULL)) + return -EINVAL; + + if (sbuf_is_empty(sbuf)) { + /* no data available */ + return 0; + } + + from = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head; + + memcpy(data, from, sbuf->ele_size); + + sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size); + + return sbuf->ele_size; +} +EXPORT_SYMBOL(sbuf_get); + +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t paddr) +{ + shared_buf_t *sbuf; + + if (!ele_num || !ele_size || !paddr) + return NULL; + + sbuf = (shared_buf_t *)phys_to_virt(paddr); + BUG_ON(!virt_addr_valid(sbuf)); + + if ((sbuf->magic == SBUF_MAGIC) && + (sbuf->ele_num == ele_num) && + (sbuf->ele_size == ele_size)) { + pr_info("construct sbuf at 0x%llx.\n", paddr); + /* return sbuf for dump */ + return sbuf; + } + + return NULL; +} +EXPORT_SYMBOL(sbuf_construct); diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h new file mode 100644 index 000000000000..7f3694920232 --- /dev/null +++ b/drivers/acrn/sbuf.h @@ -0,0 +1,119 @@ +/* + * shared buffer + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ + +#ifndef SHARED_BUF_H +#define SHARED_BUF_H + +#include + + +#define SBUF_MAGIC 0x5aa57aa71aa13aa3 +#define SBUF_MAX_SIZE (1ULL << 22) +#define SBUF_HEAD_SIZE 64 + +/* sbuf flags */ +#define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */ +#define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */ + +/** + * (sbuf) head + buf (store (ele_num - 1) elements at most) + * buffer empty: tail == head + * buffer full: (tail + ele_size) % size == head + * + * Base of memory for elements + * | + * | + * --------------------------------------------------------------------------------------- + * | shared_buf_t | raw data (ele_size)| raw date (ele_size) | ... | raw data (ele_size) | + * --------------------------------------------------------------------------------------- + * | + * | + * shared_buf_t *buf + */ + +/* Make sure sizeof(shared_buf_t) == SBUF_HEAD_SIZE */ +typedef struct shared_buf { + uint64_t magic; + uint32_t ele_num; /* number of elements */ + uint32_t ele_size; /* sizeof of elements */ + uint32_t head; /* offset from base, to read */ + uint32_t tail; /* offset from base, to write */ + uint64_t flags; + uint32_t overrun_cnt; /* count of overrun */ + uint32_t size; /* ele_num * ele_size */ + uint32_t padding[6]; +} ____cacheline_aligned shared_buf_t; + +static inline void sbuf_clear_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags &= ~flags; +} + +static inline void sbuf_set_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags = flags; +} + +static inline void sbuf_add_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags |= flags; +} + +shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); +void sbuf_free(shared_buf_t *sbuf); +int sbuf_get(shared_buf_t *sbuf, uint8_t *data); +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); + +#endif /* SHARED_BUF_H */ From ad67c62104b78570b3bda7d024d87b68b9cadf12 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0825/1276] Shared_buf: added hypercall for shared_buf setup Change-Id: I24ad2f767c7d633ad41d787c7d1a052b0fb75fb4 Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/acrn/sbuf.c | 21 +++++++++++++++++++++ drivers/acrn/sbuf.h | 1 + drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 2 +- include/linux/vhm/vhm_hypercall.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index dcf203222c5b..8849ce28a06c 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -57,6 +57,8 @@ #include #include +#include +#include #include "sbuf.h" static inline bool sbuf_is_empty(shared_buf_t *sbuf) @@ -164,6 +166,25 @@ int sbuf_get(shared_buf_t *sbuf, uint8_t *data) } EXPORT_SYMBOL(sbuf_get); +int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) +{ + struct sbuf_setup_param ssp; + + ssp.pcpu_id = pcpu_id; + ssp.sbuf_id = sbuf_id; + + if (!sbuf) { + ssp.gpa = 0; + } else { + BUG_ON(!virt_addr_valid(sbuf)); + ssp.gpa = virt_to_phys(sbuf); + } + pr_info("setup phys add = 0x%llx\n", ssp.gpa); + + return hcall_setup_sbuf(virt_to_phys(&ssp)); +} +EXPORT_SYMBOL(sbuf_share_setup); + shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t paddr) { diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h index 7f3694920232..73608c35046c 100644 --- a/drivers/acrn/sbuf.h +++ b/drivers/acrn/sbuf.h @@ -114,6 +114,7 @@ static inline void sbuf_add_flags(shared_buf_t *sbuf, uint64_t flags) shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); void sbuf_free(shared_buf_t *sbuf); int sbuf_get(shared_buf_t *sbuf, uint8_t *data); +int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf); shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); #endif /* SHARED_BUF_H */ diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 741b8bd837cc..d0da22f2a88b 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -77,6 +77,11 @@ inline long hcall_destroy_vm(unsigned long vmid) return acrn_hypercall1(HC_DESTROY_VM, vmid); } +inline long hcall_setup_sbuf(unsigned long sbuf_head) +{ + return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index bb57fb4f5cdd..688d69b6f5b0 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -104,7 +104,7 @@ /* DEBUG */ #define HC_ID_DBG_BASE 0x60UL -#define HC_SBUF_SETUP _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +#define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index f4a5793f3ef7..e56a16c5518f 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -143,6 +143,7 @@ inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); +inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, From 6f8f7a376e37716223f70033937eb5f8a5dafbd2 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0826/1276] ACRNTrace: add acrn trace module Change-Id: I9bf3a0a13e411e15063eb50905875e86e5731d1b Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/acrn/Kconfig | 8 + drivers/acrn/Makefile | 1 + drivers/acrn/acrn_trace.c | 297 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/acrn/acrn_trace.c diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index f25f0ae77727..08b24a168167 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -3,3 +3,11 @@ config ACRN_SHARED_BUFFER ---help--- Ring buffer shared between ACRN Hypervisor and its SOS. Help ACRN performance profiling. + +config ACRN_TRACE + tristate "Intel ACRN Hypervisor Trace support" + select ACRN_SHARED_BUFFER + ---help--- + This is the Trace driver for the Intel ACRN hypervisor. + You can say y to build it into the kernel, or m to build + it as a module. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index bc475f8116e3..5430f4fa06fd 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o +obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o \ No newline at end of file diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c new file mode 100644 index 000000000000..31470a3de6ac --- /dev/null +++ b/drivers/acrn/acrn_trace.c @@ -0,0 +1,297 @@ +/* +* +* ACRN Trace module +* +* This file is provided under a dual BSD/GPLv2 license.  When using or +* redistributing this file, you may do so under either license. +* +* GPL LICENSE SUMMARY +* +* Copyright (c) 2017 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* General Public License for more details. +* +* Contact Information: Yan, Like +* +* BSD LICENSE +* +* Copyright (c) 2017 Intel Corporation. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +*   * Redistributions of source code must retain the above copyright +*     notice, this list of conditions and the following disclaimer. +*   * Redistributions in binary form must reproduce the above copyright +*     notice, this list of conditions and the following disclaimer in +*     the documentation and/or other materials provided with the +*     distribution. +*   * Neither the name of Intel Corporation nor the names of its +*     contributors may be used to endorse or promote products derived +*     from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Like Yan +* +*/ + +#define pr_fmt(fmt) "ACRNTrace: " fmt + +#include +#include +#include +#include +#include +#include + +#include "sbuf.h" + + +#define TRACE_SBUF_SIZE (4 * 1024 * 1024) +#define TRACE_ELEMENT_SIZE 32 /* byte */ +#define TRACE_ELEMENT_NUM ((TRACE_SBUF_SIZE - SBUF_HEAD_SIZE) / \ + TRACE_ELEMENT_SIZE) + +#define foreach_cpu(cpu, cpu_num) \ + for ((cpu) = 0; (cpu) < (cpu_num); (cpu)++) + +#define MAX_NR_CPUS 4 +/* actual physical cpu number, initialized by module init */ +static int pcpu_num; + +static int nr_cpus = MAX_NR_CPUS; +module_param(nr_cpus, int, S_IRUSR | S_IWUSR); + +static atomic_t open_cnt[MAX_NR_CPUS]; +static shared_buf_t *sbuf_per_cpu[MAX_NR_CPUS]; + +static inline int get_id_from_devname(struct file *filep) +{ + uint32_t cpuid; + int err; + char id_str[16]; + struct miscdevice *dev = filep->private_data; + + strncpy(id_str, (void *)dev->name + sizeof("acrn_trace_") - 1, 16); + id_str[15] = '\0'; + err = kstrtoul(&id_str[0], 10, (unsigned long *)&cpuid); + + if (err) + return err; + + if (cpuid >= pcpu_num) { + pr_err("%s, failed to get cpuid, cpuid %d\n", + __func__, cpuid); + return -1; + } + + return cpuid; +} + +/************************************************************************ + * + * file_operations functions + * + ***********************************************************************/ +static int acrn_trace_open(struct inode *inode, struct file *filep) +{ + int cpuid = get_id_from_devname(filep); + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + /* More than one reader at the same time could get data messed up */ + if (atomic_read(&open_cnt[cpuid])) + return -EBUSY; + + atomic_inc(&open_cnt[cpuid]); + + return 0; +} + +static int acrn_trace_release(struct inode *inode, struct file *filep) +{ + int cpuid = get_id_from_devname(filep); + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + atomic_dec(&open_cnt[cpuid]); + + return 0; +} + +static int acrn_trace_mmap(struct file *filep, struct vm_area_struct *vma) +{ + int cpuid = get_id_from_devname(filep); + phys_addr_t paddr; + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + BUG_ON(!virt_addr_valid(sbuf_per_cpu[cpuid])); + paddr = virt_to_phys(sbuf_per_cpu[cpuid]); + + if (remap_pfn_range(vma, vma->vm_start, + paddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + pr_err("Failed to mmap sbuf for cpu%d\n", cpuid); + return -EAGAIN; + } + + return 0; +} + +static const struct file_operations acrn_trace_fops = { + .owner = THIS_MODULE, + .open = acrn_trace_open, + .release = acrn_trace_release, + .mmap = acrn_trace_mmap, +}; + +static struct miscdevice acrn_trace_dev0 = { + .name = "acrn_trace_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev1 = { + .name = "acrn_trace_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev2 = { + .name = "acrn_trace_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev3 = { + .name = "acrn_trace_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice *acrn_trace_devs[4] = { + &acrn_trace_dev0, + &acrn_trace_dev1, + &acrn_trace_dev2, + &acrn_trace_dev3, +}; + +/* + * acrn_trace_init() + */ +static int __init acrn_trace_init(void) +{ + int ret = 0; + int i, cpu; + + /* TBD: we could get the native cpu number by hypercall later */ + pr_info("%s, cpu_num %d\n", __func__, nr_cpus); + if (nr_cpus > MAX_NR_CPUS) { + pr_err("nr_cpus %d exceed MAX_NR_CPUS %d !\n", + nr_cpus, MAX_NR_CPUS); + return -EINVAL; + } + pcpu_num = nr_cpus; + + foreach_cpu(cpu, pcpu_num) { + /* allocate shared_buf */ + sbuf_per_cpu[cpu] = sbuf_allocate(TRACE_ELEMENT_NUM, + TRACE_ELEMENT_SIZE); + if (!sbuf_per_cpu[cpu]) { + pr_err("Failed alloc SBuf, cpuid %d\n", cpu); + ret = -ENOMEM; + goto out_free; + } + } + + foreach_cpu(cpu, pcpu_num) { + ret = sbuf_share_setup(cpu, 0, sbuf_per_cpu[cpu]); + if (ret < 0) { + pr_err("Failed to setup SBuf, cpuid %d\n", cpu); + goto out_sbuf; + } + } + + foreach_cpu(cpu, pcpu_num) { + ret = misc_register(acrn_trace_devs[cpu]); + if (ret < 0) { + pr_err("Failed to register acrn_trace_%d, errno %d\n", + cpu, ret); + goto out_dereg; + } + } + + return ret; + +out_dereg: + for (i = --cpu; i >= 0; i--) + misc_deregister(acrn_trace_devs[i]); + cpu = pcpu_num; + +out_sbuf: + for (i = --cpu; i >= 0; i--) + sbuf_share_setup(i, 0, NULL); + cpu = pcpu_num; + +out_free: + for (i = --cpu; i >= 0; i--) + sbuf_free(sbuf_per_cpu[i]); + + return ret; +} + +/* + * acrn_trace_exit() + */ +static void __exit acrn_trace_exit(void) +{ + int cpu; + + pr_info("%s, cpu_num %d\n", __func__, pcpu_num); + + foreach_cpu(cpu, pcpu_num) { + /* deregister devices */ + misc_deregister(acrn_trace_devs[cpu]); + + /* set sbuf pointer to NULL in HV */ + sbuf_share_setup(cpu, 0, NULL); + + /* free sbuf, sbuf_per_cpu[cpu] should be set NULL */ + sbuf_free(sbuf_per_cpu[cpu]); + } +} + +module_init(acrn_trace_init); +module_exit(acrn_trace_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor Trace"); +MODULE_VERSION("0.1"); From d2675c541514b2a3a8ae63d965c390915b36813a Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0827/1276] sos: fix potential bugs in ptdev msi-x access - delete the node in the 'table_iomems' list for msi-x table when the pass-through device is deinited. otherwise, memory leak might occur. - add a check of irq type before msi-x table access Change-Id: I9d2ec1e430356ef0ca61855cfbc76ae9cfdb2529 Signed-off-by: Shiqing Gao --- drivers/char/vhm/vhm_dev.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d3fd572c1642..7894297ca872 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -402,7 +402,8 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; } - if (ic_pt_irq.msix.table_paddr) { + if ((ic_pt_irq.type == IRQ_MSIX) && + ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; @@ -419,7 +420,8 @@ static long vhm_dev_ioctl(struct file *filep, break; } case IC_RESET_PTDEV_INTR_INFO: { - struct table_iomems *new; + struct table_iomems *ptr; + int dev_found = 0; if (copy_from_user(&ic_pt_irq, (void *)ioctl_param, sizeof(ic_pt_irq))) @@ -434,17 +436,18 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; } - if (ic_pt_irq.msix.table_paddr) { - new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); - if (new == NULL) - return -EFAULT; - new->phys_bdf = ic_pt_irq.phys_bdf; - new->mmap_addr = (unsigned long) - ioremap_nocache(ic_pt_irq.msix.table_paddr, - ic_pt_irq.msix.table_size); - + if (ic_pt_irq.type == IRQ_MSIX) { mutex_lock(&table_iomems_lock); - list_add(&new->list, &table_iomems_list); + list_for_each_entry(ptr, &table_iomems_list, list) { + if (ptr->phys_bdf == ic_pt_irq.phys_bdf) { + dev_found = 1; + break; + } + } + if (dev_found) { + iounmap((void __iomem *)ptr->mmap_addr); + list_del(&ptr->list); + } mutex_unlock(&table_iomems_lock); } @@ -467,15 +470,18 @@ static long vhm_dev_ioctl(struct file *filep, if (msix_remap.msix) { void __iomem *msix_entry; struct table_iomems *ptr; + int dev_found = 0; mutex_lock(&table_iomems_lock); list_for_each_entry(ptr, &table_iomems_list, list) { - if (ptr->phys_bdf == msix_remap.phys_bdf) + if (ptr->phys_bdf == msix_remap.phys_bdf) { + dev_found = 1; break; + } } mutex_unlock(&table_iomems_lock); - if (!ptr->mmap_addr) + if (!dev_found || !ptr->mmap_addr) return -EFAULT; msix_entry = (void __iomem *) (ptr->mmap_addr + From 6c2919bf6196ca6a710ef310106c4e467a8e0f75 Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0828/1276] vhm: cleanup ioctls Redefine ioctl command number Change-Id: I555cdbdd03c50f9fa5b66eb95d61c8d83c60a276 Tracked-On: 212688 Signed-off-by: Edwin Zhai --- include/linux/vhm/vhm_ioctl_defs.h | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 60bfb299e040..d00b6588f296 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -53,26 +53,29 @@ * Commmon IOCTL ID defination for VHM/DM */ #define _IC_ID(x, y) (((x)<<24)|(y)) -#define IC_ID 0x5FUL +#define IC_ID 0x43UL + +/* General */ +#define IC_ID_GEN_BASE 0x0UL +#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_GEN_BASE + 0x00) /* VM management */ -#define IC_ID_VM_BASE 0x0UL -#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) -#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) -#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) -#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_ID_VM_BASE 0x10UL +#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) +#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) +#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) /* IRQ and Interrupts */ -#define IC_ID_IRQ_BASE 0x100UL +#define IC_ID_IRQ_BASE 0x20UL #define IC_ASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x00) #define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) /* DM ioreq management */ -#define IC_ID_IOREQ_BASE 0x200UL +#define IC_ID_IOREQ_BASE 0x30UL #define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) #define IC_NOTIFY_REQUEST_FINISH _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x01) #define IC_CREATE_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x02) @@ -81,12 +84,12 @@ /* Guest memory management */ -#define IC_ID_MEM_BASE 0x300UL +#define IC_ID_MEM_BASE 0x40UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) /* PCI assignment*/ -#define IC_ID_PCI_BASE 0x400UL +#define IC_ID_PCI_BASE 0x50UL #define IC_ASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x00) #define IC_DEASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x01) #define IC_VM_PCI_MSIX_REMAP _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x02) From fd93dc060e52a575de0f3edf83cbb9e9af47961e Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0829/1276] VHM: check HV api version for VHM module init Change-Id: I8d49db28e235fe643380b4e8b82fb629e89accaf Tracked-On: 218802 Signed-off-by: Jason Chen CJ Signed-off-by: Yonghua Huang --- drivers/char/vhm/vhm_dev.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 7894297ca872..88fb0faf19fc 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -565,12 +565,30 @@ static const struct file_operations fops = { .poll = vhm_dev_poll, }; +#define SUPPORT_HV_API_VERSION_MAJOR 1 +#define SUPPORT_HV_API_VERSION_MINOR 0 static int __init vhm_init(void) { unsigned long flag; + struct hc_api_version api_version = {0, 0}; pr_info("vhm: initializing\n"); + if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { + pr_err("vhm: failed to get api version from Hypervisor !\n"); + return -EINVAL; + } + + if (api_version.major_version == SUPPORT_HV_API_VERSION_MAJOR && + api_version.minor_version == SUPPORT_HV_API_VERSION_MINOR) { + pr_info("vhm: hv api version %d.%d\n", + api_version.major_version, api_version.minor_version); + } else { + pr_err("vhm: not support hv api version %d.%d!\n", + api_version.major_version, api_version.minor_version); + return -EINVAL; + } + /* Try to dynamically allocate a major number for the device */ major = register_chrdev(0, DEVICE_NAME, &fops); if (major < 0) { From bab12110a419937ca12232f5a8d345fd60d0e795 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0830/1276] VHM: add VHM api version support Change-Id: I36dd051d0cc04720ab8d69817392ff97f1e5ad34 Tracked-On: 218802 Signed-off-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 16 ++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 88fb0faf19fc..27844c87a41f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -87,6 +87,9 @@ #define DEVICE_NAME "acrn_vhm" #define CLASS_NAME "vhm" +#define VHM_API_VERSION_MAJOR 1 +#define VHM_API_VERSION_MINOR 0 + static int major; static struct class *vhm_class; static struct device *vhm_device; @@ -156,6 +159,19 @@ static long vhm_dev_ioctl(struct file *filep, trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + if (ioctl_num == IC_GET_API_VERSION) { + struct api_version api_version; + + api_version.major_version = VHM_API_VERSION_MAJOR; + api_version.minor_version = VHM_API_VERSION_MINOR; + + if (copy_to_user((void *)ioctl_param, &api_version, + sizeof(struct api_version))) + return -EFAULT; + + return 0; + } + memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); memset(&ic_pt_irq, 0, sizeof(ic_pt_irq)); vm = (struct vhm_vm *)filep->private_data; diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index d00b6588f296..258ec3982da9 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -159,4 +159,9 @@ struct ioreq_notify { uint32_t vcpu; }; +struct api_version { + uint32_t major_version; + uint32_t minor_version; +}; + #endif /* VHM_IOCTL_DEFS_H */ From 172787b505f6803fe062536574a83ea2b67809ed Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0831/1276] virtio framework: support ACRN virtio devices To support ACRN virtio devices which use Intel VID:DID, relax virtio device probing conditions in frontend virtio framework. Change-Id: I9a49ad3fbdbd0a615398218382624031d6908526 Tracked-On: 219551 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Liu, Fuzhong Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/virtio/Kconfig | 17 +++++++++++++++++ drivers/virtio/virtio_pci_common.c | 29 +++++++++++++++++++++++++++++ drivers/virtio/virtio_pci_legacy.c | 10 ++++++++++ drivers/virtio/virtio_pci_modern.c | 15 +++++++++++++++ include/uapi/linux/virtio_ids.h | 13 +++++++++++++ 5 files changed, 84 insertions(+) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 35897649c24f..51d43fd4bd2b 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -83,4 +83,21 @@ config VIRTIO_MMIO_CMDLINE_DEVICES If unsure, say 'N'. +config ACRN_VIRTIO_DEVICES + bool "Support for ACRN virtio devices drivers in frontend/guest" + default n + depends on VIRTIO_PCI + ---help--- + ACRN virtio devices support in frontend/guest. + + This option enables support for ACRN virtio devices which use Intel + vendor ID and device IDs, by extending virtio frontend framework + a little bit so that virtio PCI driver could be loaded for these + devices. + + Eventually if all devices obtain virtio VID and DIDs, we don't + need this option anymore. + + If unsure, say 'N'. + endif # VIRTIO_MENU diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 465a6f5142cc..9bb79f578fe5 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -494,6 +494,35 @@ static const struct dev_pm_ops virtio_pci_pm_ops = { /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ static const struct pci_device_id virtio_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) }, +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ +#define ACRN_VIRTIO_DEVICE_ID_RPMB 0x8601 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_RPMB) }, + +#define ACRN_VIRTIO_DEVICE_ID_HECI 0x8602 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HECI) }, + +#define ACRN_VIRTIO_DEVICE_ID_AUDIO 0x8603 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_AUDIO) }, + +#define ACRN_VIRTIO_DEVICE_ID_IPU 0x8604 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_IPU) }, + +#define ACRN_VIRTIO_DEVICE_ID_TSN 0x8605 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_TSN) }, + +#define ACRN_VIRTIO_DEVICE_ID_HYPERDMABUF 0x8606 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HYPERDMABUF) }, + +#define ACRN_VIRTIO_DEVICE_ID_HDCP 0x8607 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HDCP) }, + +#define ACRN_VIRTIO_DEVICE_ID_COREU 0x8608 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_COREU) }, +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ { 0 } }; diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index de062fb201bc..b21f1034054c 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -215,9 +215,19 @@ int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) struct pci_dev *pci_dev = vp_dev->pci_dev; int rc; +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ + if (pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)) + return -ENODEV; +#else /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f) return -ENODEV; +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION) { printk(KERN_ERR "virtio_pci: expected ABI version %d, got %d\n", diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 07571daccfec..b7474e0cce21 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -590,11 +590,26 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) check_offsets(); +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ + if (pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)) + return -ENODEV; + + if ((pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + pci_dev->device < 0x1040) || + (pci_dev->vendor == PCI_VENDOR_ID_INTEL && + pci_dev->device < 0x8640)) { +#else /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) return -ENODEV; if (pci_dev->device < 0x1040) { +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ /* Transitional devices: use the PCI subsystem device id as * virtio device id, same as legacy driver always did. */ diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 6d5c3b2d4f4d..a95019652bb5 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -44,4 +44,17 @@ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ +#ifdef CONFIG_ACRN_VIRTIO_DEVICES +/* ACRN virtio device types */ +#define VIRTIO_ID_RPMB 0xFFFF +#define VIRTIO_ID_HECI 0xFFFE +#define VIRTIO_ID_AUDIO 0xFFFD +#define VIRTIO_ID_IPU 0xFFFC +#define VIRTIO_ID_TSN 0xFFFB +#define VIRTIO_ID_HYPERDMABUF 0xFFFA +#define VIRTIO_ID_HDCP 0xFFF9 +#define VIRTIO_ID_COREU 0xFFF8 + +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ + #endif /* _LINUX_VIRTIO_IDS_H */ From 6f1abcf267b5e35c46c77fab8da6146c95e15fb5 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0832/1276] VHM: sync public header file acrn_common.h Change-Id: I1e0ac4d26b22cda4d1db81a83dca8d8806405a8c Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- include/linux/vhm/acrn_common.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index a6f46648c853..f27feb7a3e57 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -144,7 +144,7 @@ struct acrn_create_vm { int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ uint32_t vcpu_num; /* IN: VM vcpu number */ uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t secure_world_enabled;/* IN: whether Secure World is enabled */ uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); @@ -197,4 +197,13 @@ struct acrn_vm_pci_msix_remap { uint32_t vector_ctl; } __attribute__((aligned(8))); +/* It's designed to support passing DM config data pointer, based on it, + * hypervisor would parse then pass DM defined configration to GUEST vcpu + * when booting guest VM. + * the address 0xd0000 here is designed by DM, as it arranged all memory + * layout below 1M, DM should make sure there is no overlap for the address + * 0xd0000 usage. + */ +#define GUEST_CFG_OFFSET 0xd0000 + #endif /* ACRN_COMMON_H */ From 94024f07e60591c61b1cbaa78e99149fc1cf2d5e Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0833/1276] Check x86_hyper type before doing hypercall this is to fix native boot failure issue with CONFIG_ACRN Change-Id: I735283cbf462c8b79d9742d64950685d6ae552c1 Tracked-On: Signed-off-by: Jason Chen CJ --- drivers/acrn/acrn_trace.c | 7 +++++++ drivers/char/vhm/vhm_dev.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c index 31470a3de6ac..856ab650acfd 100644 --- a/drivers/acrn/acrn_trace.c +++ b/drivers/acrn/acrn_trace.c @@ -63,6 +63,8 @@ #include #include +#include + #include "sbuf.h" @@ -211,6 +213,11 @@ static int __init acrn_trace_init(void) int ret = 0; int i, cpu; + if (x86_hyper_type != X86_HYPER_ACRN) { + pr_err("acrn_trace: not support acrn hypervisor!\n"); + return -EINVAL; + } + /* TBD: we could get the native cpu number by hypercall later */ pr_info("%s, cpu_num %d\n", __func__, nr_cpus); if (nr_cpus > MAX_NR_CPUS) { diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 27844c87a41f..b724c9e7bce2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -84,6 +84,8 @@ #include #include +#include + #define DEVICE_NAME "acrn_vhm" #define CLASS_NAME "vhm" @@ -590,6 +592,11 @@ static int __init vhm_init(void) pr_info("vhm: initializing\n"); + if (x86_hyper_type != X86_HYPER_ACRN) { + pr_err("vhm: not support acrn hypervisor!\n"); + return -EINVAL; + } + if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { pr_err("vhm: failed to get api version from Hypervisor !\n"); return -EINVAL; From cc1fbc64f22aa0c52a8b3ea03fe697e3b4604b37 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0834/1276] VHM: replace function name update_mmio_map with update_memmap_attr Change-Id: Ia4e4c621d4a8bc6738042cede93b9b145af291f9 Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/acrn_vhm_mm.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index b475aa91a348..712a905040a4 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -191,11 +191,11 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, prot, MAP_UNMAP); } -int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, +int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MMIO); + prot, MAP_MEM); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index f0401ac6a942..2ff1e25b22ce 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -69,7 +69,7 @@ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); -int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, +int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); From 1621d8f1f3339bba66ac5ce6e8d26006530bbfb4 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0835/1276] VHM: refine memory segment interface - restruct guest_memseg & remove redundant paramters in it - restruct vm_memmap & remove redundant paramters in it - remove redundant paramters in vm_memseg Change-Id: I7661cfd464bc2748f9d5f1d0751f52782332c97a Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 90 ++++++++++-------------------- include/linux/vhm/vhm_ioctl_defs.h | 31 +++------- 2 files changed, 37 insertions(+), 84 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 712a905040a4..a9ba810a7fd7 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -78,12 +78,9 @@ struct guest_memseg { struct list_head list; - int segid; - u64 base; + u64 vm0_gpa; size_t len; - char name[SPECNAMELEN + 1]; u64 gpa; - int prot; /* RWX */ long vma_count; }; @@ -105,10 +102,10 @@ static u64 _alloc_memblk(struct device *dev, size_t len) return 0ULL; } -static bool _free_memblk(struct device *dev, u64 base, size_t len) +static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) { unsigned int count = PAGE_ALIGN(len) >> PAGE_SHIFT; - struct page *page = pfn_to_page(base >> PAGE_SHIFT); + struct page *page = pfn_to_page(vm0_gpa >> PAGE_SHIFT); return dma_release_from_contiguous(dev, page, count); } @@ -116,32 +113,30 @@ static bool _free_memblk(struct device *dev, u64 base, size_t len) int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) { struct guest_memseg *seg; - u64 base; + u64 vm0_gpa; int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) return -ENOMEM; - base = _alloc_memblk(vm->dev, memseg->len); - if (base == 0ULL) { + vm0_gpa = _alloc_memblk(vm->dev, memseg->len); + if (vm0_gpa == 0ULL) { kfree(seg); return -ENOMEM; } - seg->segid = memseg->segid; - seg->base = base; + seg->vm0_gpa = vm0_gpa; seg->len = memseg->len; - strncpy(seg->name, memseg->name, SPECNAMELEN + 1); seg->gpa = memseg->gpa; max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; if (vm->max_gfn < max_gfn) vm->max_gfn = max_gfn; - pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," + pr_info("VHM: alloc memseg with len=0x%lx, vm0_gpa=0x%llx," " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", - seg->name, seg->len, seg->base, seg->gpa, vm->max_gfn); + seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); seg->vma_count = 0; mutex_lock(&vm->seg_lock); @@ -201,39 +196,34 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; - struct vm_set_memmap set_memmap; + unsigned int type, prot; + unsigned long guest_gpa, host_gpa; mutex_lock(&vm->seg_lock); - if (memmap->segid != VM_MMIO) { + if (memmap->type == VM_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid == memmap->segid - && seg->gpa == memmap->mem.gpa - && seg->len == memmap->mem.len) + if (seg->gpa == memmap->gpa + && seg->len == memmap->len) break; } if (&seg->list == &vm->memseg_list) { mutex_unlock(&vm->seg_lock); return -EINVAL; } - seg->prot = memmap->mem.prot; - set_memmap.type = MAP_MEM; - set_memmap.remote_gpa = seg->gpa; - set_memmap.vm0_gpa = seg->base; - set_memmap.length = seg->len; - set_memmap.prot = seg->prot; - set_memmap.prot |= MEM_ATTR_WB_CACHE; + guest_gpa = seg->gpa; + host_gpa = seg->vm0_gpa; + prot = memmap->prot | MEM_ATTR_WB_CACHE; + type = MAP_MEM; } else { - set_memmap.type = MAP_MMIO; - set_memmap.remote_gpa = memmap->mmio.gpa; - set_memmap.vm0_gpa = memmap->mmio.hpa; - set_memmap.length = memmap->mmio.len; - set_memmap.prot = memmap->mmio.prot; - set_memmap.prot |= MEM_ATTR_UNCACHED; + guest_gpa = memmap->gpa; + host_gpa = acrn_hpa2gpa(memmap->hpa); + prot = memmap->prot | MEM_ATTR_UNCACHED; + type = MAP_MMIO; } - /* hypercall to notify hv the guest EPT setting*/ - if (hcall_set_memmap(vm->vmid, virt_to_phys(&set_memmap)) < 0) { + if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, + prot, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); mutex_unlock(&vm->seg_lock); return -EFAULT; @@ -241,16 +231,6 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) mutex_unlock(&vm->seg_lock); - if (memmap->segid != VM_MMIO) - pr_debug("VHM: set ept for memseg [hvm_gpa=0x%llx," - "guest_gpa=0x%llx,len=0x%lx, prot=0x%x]\n", - seg->base, seg->gpa, seg->len, seg->prot); - else - pr_debug("VHM: set ept for mmio [hpa=0x%llx," - "gpa=0x%llx,len=0x%lx, prot=0x%x]\n", - memmap->mmio.hpa, memmap->mmio.gpa, - memmap->mmio.len, memmap->mmio.prot); - return 0; } @@ -262,7 +242,7 @@ void free_guest_mem(struct vhm_vm *vm) while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->base, seg->len)) + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) pr_warn("failed to free memblk\n"); list_del(&seg->list); kfree(seg); @@ -276,9 +256,6 @@ int check_guest_mem(struct vhm_vm *vm) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->vma_count == 0) continue; @@ -324,7 +301,7 @@ static int do_mmap_guest(struct file *file, unsigned long start_addr; vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; - pfn = seg->base >> PAGE_SHIFT; + pfn = seg->vm0_gpa >> PAGE_SHIFT; start_addr = vma->vm_start; while (size > 0) { page = pfn_to_page(pfn); @@ -338,9 +315,9 @@ static int do_mmap_guest(struct file *file, vma->vm_ops = &guest_vm_ops; vma->vm_private_data = (void *)seg; - pr_info("VHM: mmap for memseg [seg base=0x%llx, gpa=0x%llx] " + pr_info("VHM: mmap for memseg [seg vm0_gpa=0x%llx, gpa=0x%llx] " "to start addr 0x%lx\n", - seg->base, seg->gpa, start_addr); + seg->vm0_gpa, seg->gpa, start_addr); return 0; } @@ -355,9 +332,6 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa != offset || seg->len != len) continue; @@ -375,9 +349,6 @@ static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa > guest_phys || guest_phys >= seg->gpa + seg->len) continue; @@ -388,7 +359,7 @@ static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) } mutex_unlock(&vm->seg_lock); - return phys_to_virt(seg->base + guest_phys - seg->gpa); + return phys_to_virt(seg->vm0_gpa + guest_phys - seg->gpa); } mutex_unlock(&vm->seg_lock); return NULL; @@ -417,9 +388,6 @@ static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa <= guest_phys && guest_phys < seg->gpa + seg->len) { mutex_unlock(&vm->seg_lock); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 258ec3982da9..494213a9f9f0 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -82,7 +82,6 @@ #define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) #define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) - /* Guest memory management */ #define IC_ID_MEM_BASE 0x40UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) @@ -96,35 +95,21 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) -#define SPECNAMELEN 63 - -#define VM_SYSMEM 0 -#define VM_MMIO 1 - struct vm_memseg { - uint32_t segid; - uint32_t reserved; uint64_t len; uint64_t gpa; - char name[SPECNAMELEN + 1]; }; +#define VM_SYSMEM 0 +#define VM_MMIO 1 + struct vm_memmap { - uint32_t segid; /* memory segment */ + uint32_t type; uint32_t reserved; - union { - struct { - uint64_t gpa; - uint64_t len; /* mmap length */ - uint32_t prot; /* RWX */ - } mem; - struct { - uint64_t gpa; - uint64_t hpa; - uint64_t len; - uint32_t prot; - } mmio; - }; + uint64_t gpa; + uint64_t hpa; /* only for type == VM_MMIO */ + uint64_t len; /* mmap length */ + uint32_t prot; /* RWX */ }; struct ic_ptdev_irq { From c05e21f46c1d85283c7e8a009c652713b13a99dc Mon Sep 17 00:00:00 2001 From: Hao Li Date: Thu, 25 Jan 2018 10:12:26 -0500 Subject: [PATCH 0836/1276] VBS-K: added VHM wrapper APIs This patch added 3 VHM wrapper APIs to the VBS-K framework: - long virtio_dev_register(struct virtio_dev_info *dev); - long virtio_dev_deregister(struct virtio_dev_info *dev); - int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); VBS-K modules could use the APIs above to register kick callback handlers to VHM. This patch also updated the reference driver with the new APIs usage. Change-Id: I6a92a36eb785d55c1a4aa09bba46c67ed5dd2194 Signed-off-by: Hao Li --- drivers/vbs/vbs.c | 121 ++++++++++++++++++++++++ drivers/vbs/vbs_rng.c | 199 ++++++++++------------------------------ include/linux/vbs/vbs.h | 16 +++- 3 files changed, 181 insertions(+), 155 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 1e7a9645a353..9d96f45b9644 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -67,6 +67,127 @@ #include #include +long virtio_dev_register(struct virtio_dev_info *dev) +{ + struct vm_info info; + int ret; + + pr_debug("vmid is %d\n", dev->_ctx.vmid); + + if (dev->dev_notify == NULL) { + pr_err("%s dev_notify empty!\n", dev->name); + goto err; + } + + /* + * dev->name is 32 chars while vhm only accepts 16 chars + * at most, so we make sure there will be a NULL + * terminator for the chars. + */ + dev->name[15] = '\0'; + dev->_ctx.vhm_client_id = + acrn_ioreq_create_client(dev->_ctx.vmid, + dev->dev_notify, + dev->name); + if (dev->_ctx.vhm_client_id < 0) { + pr_err("failed to create client of acrn ioreq!\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len - 1); + if (ret < 0) { + pr_err("failed to add iorange to acrn ioreq!\n"); + goto err; + } + + /* feed up max_cpu and req_buf */ + ret = vhm_get_vm_info(dev->_ctx.vmid, &info); + if (ret < 0) { + pr_err("failed in vhm_get_vm_info!\n"); + goto range_err; + } + dev->_ctx.max_vcpu = info.max_vcpu; + + dev->_ctx.req_buf = acrn_ioreq_get_reqbuf(dev->_ctx.vhm_client_id); + if (dev->_ctx.req_buf == NULL) { + pr_err("failed in acrn_ioreq_get_reqbuf!\n"); + goto range_err; + } + + acrn_ioreq_attach_client(dev->_ctx.vhm_client_id, 0); + + return 0; + +range_err: + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + +err: + acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + + return -EINVAL; +} + +long virtio_dev_deregister(struct virtio_dev_info *dev) +{ + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + + acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + + return 0; +} + +int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + if (dev == NULL) { + pr_err("%s: dev is NULL!\n", __func__); + return -EINVAL; + } + + for (i = 0; i < dev->_ctx.max_vcpu; i++) { + req = &dev->_ctx.req_buf[i]; + if (req->valid && req->processed == REQ_STATE_PROCESSING && + req->client == dev->_ctx.vhm_client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) { + /* currently we handle kick only, + * so read will return 0 + */ + pr_debug("%s: read request!\n", __func__); + if (dev->io_range_type == PIO_RANGE) + req->reqs.pio_request.value = 0; + else + req->reqs.mmio_request.value = 0; + } else { + pr_debug("%s: write request! type %d\n", + __func__, req->type); + if (dev->io_range_type == PIO_RANGE) + val = req->reqs.pio_request.value; + else + val = req->reqs.mmio_request.value; + } + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(dev->_ctx.vhm_client_id, i); + } + } + + return val; +} + static long virtio_vqs_info_set(struct virtio_dev_info *dev, struct vbs_vqs_info __user *i) { diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index f2234e73034d..87965bafbbb3 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -74,8 +74,6 @@ #include #include -#include -#include #include enum { @@ -96,26 +94,14 @@ enum { struct vbs_rng { struct virtio_dev_info dev; struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; - int vhm_client_id; /* Below could be device specific members */ struct hwrng hwrng; -}; - -/* - * Each VBS-K module might serve multiple connections from multiple - * guests/device models/VBS-Us, so better to maintain the connections - * in a list, and here we use hashtalble as an example. - */ -struct vbs_rng_client { - struct vbs_rng *rng; - int vhm_client_id; - int max_vcpu; - struct vhm_request *req_buf; -}; - -/* instances malloced/freed by hashtable routines */ -struct vbs_rng_hash_entry { - struct vbs_rng_client *info; + /* + * Each VBS-K module might serve multiple connections + * from multiple guests/device models/VBS-Us, so better + * to maintain the connections in a list, and here we + * use hashtable as an example. + */ struct hlist_node node; }; @@ -149,30 +135,20 @@ static void vbs_rng_hash_init(void) vbs_rng_hash_initialized = 1; } -static int vbs_rng_hash_add(struct vbs_rng_client *client) +static int vbs_rng_hash_add(struct vbs_rng *entry) { - struct vbs_rng_hash_entry *entry; - if (!vbs_rng_hash_initialized) { pr_err("RNG hash table not initialized!\n"); return -1; } - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - pr_err("Failed to alloc memory for rng hash entry!\n"); - return -1; - } - - entry->info = client; - - hash_add(HASH_NAME, &entry->node, entry->info->vhm_client_id); + hash_add(HASH_NAME, &entry->node, virtio_dev_client_id(&entry->dev)); return 0; } -static struct vbs_rng_client *vbs_rng_hash_find(int client_id) +static struct vbs_rng *vbs_rng_hash_find(int client_id) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -181,8 +157,8 @@ static struct vbs_rng_client *vbs_rng_hash_find(int client_id) } hash_for_each(HASH_NAME, bkt, entry, node) - if (entry->info->vhm_client_id == client_id) - return entry->info; + if (virtio_dev_client_id(&entry->dev) == client_id) + return entry; pr_err("Not found item matching client_id!\n"); return NULL; @@ -190,7 +166,7 @@ static struct vbs_rng_client *vbs_rng_hash_find(int client_id) static int vbs_rng_hash_del(int client_id) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -199,9 +175,8 @@ static int vbs_rng_hash_del(int client_id) } hash_for_each(HASH_NAME, bkt, entry, node) - if (entry->info->vhm_client_id == client_id) { + if (virtio_dev_client_id(&entry->dev) == client_id) { hash_del(&entry->node); - kfree(entry); return 0; } @@ -212,7 +187,7 @@ static int vbs_rng_hash_del(int client_id) static int vbs_rng_hash_del_all(void) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -221,75 +196,11 @@ static int vbs_rng_hash_del_all(void) } hash_for_each(HASH_NAME, bkt, entry, node) - if (1) { - hash_del(&entry->node); - kfree(entry); - } + hash_del(&entry->node); return 0; } -static int register_vhm_client(struct virtio_dev_info *dev) -{ - unsigned int vmid; - struct vm_info info; - struct vbs_rng_client *client; - int ret; - - client = kcalloc(1, sizeof(*client), GFP_KERNEL); - if (!client) { - pr_err("failed to malloc vbs_rng_client!\n"); - return -EINVAL; - } - - client->rng = container_of(dev, struct vbs_rng, dev); - vmid = dev->_ctx.vmid; - pr_debug("vmid is %d\n", vmid); - - client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, - "vbs_rng kick init\n"); - if (client->vhm_client_id < 0) { - pr_err("failed to create client of acrn ioreq!\n"); - goto err; - } - - ret = acrn_ioreq_add_iorange(client->vhm_client_id, - dev->io_range_type ? REQ_MMIO : REQ_PORTIO, - dev->io_range_start, - dev->io_range_start + dev->io_range_len); - if (ret < 0) { - pr_err("failed to add iorange to acrn ioreq!\n"); - goto err; - } - - /* feed up max_cpu and req_buf */ - ret = vhm_get_vm_info(vmid, &info); - if (ret < 0) { - pr_err("failed in vhm_get_vm_info!\n"); - goto err; - } - client->max_vcpu = info.max_vcpu; - - client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); - if (client->req_buf == NULL) { - pr_err("failed in acrn_ioreq_get_reqbuf!\n"); - goto err; - } - - /* just attach once as vhm will kick kthread */ - acrn_ioreq_attach_client(client->vhm_client_id, 0); - - client->rng->vhm_client_id = client->vhm_client_id; - vbs_rng_hash_add(client); - - return 0; -err: - acrn_ioreq_destroy_client(client->vhm_client_id); - kfree(client); - - return -EINVAL; -} - static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) { struct iovec iov; @@ -309,8 +220,6 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) vq = &(sc->vqs[vq_idx]); - pr_debug("before vq_has_desc!\n"); - while (virtio_vq_has_descs(vq)) { virtio_vq_getchain(vq, &idx, &iov, 1, NULL); @@ -334,47 +243,25 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) static int handle_kick(int client_id, int req_cnt) { int val = -1; - struct vhm_request *req; - struct vbs_rng_client *client; - int i; + struct vbs_rng *rng; if (unlikely(req_cnt <= 0)) return -EINVAL; - pr_debug("%s!\n", __func__); + pr_debug("%s: handle kick!\n", __func__); - client = vbs_rng_hash_find(client_id); - if (!client) { - pr_err("Ooops! client %d not found!\n", client_id); + rng = vbs_rng_hash_find(client_id); + if (rng == NULL) { + pr_err("%s: client %d not found!\n", + __func__, client_id); return -EINVAL; } - for (i = 0; i < client->max_vcpu; i++) { - req = &client->req_buf[i]; - if (req->valid && req->processed == REQ_STATE_PROCESSING && - req->client == client->vhm_client_id) { - if (req->reqs.pio_request.direction == REQUEST_READ) - /* currently we handle kick only, - * so read will return 0 - */ - req->reqs.pio_request.value = 0; - else - val = req->reqs.pio_request.value; - pr_debug("%s: ioreq type %d, direction %d, " - "addr 0x%lx, size 0x%lx, value 0x%x\n", - __func__, - req->type, - req->reqs.pio_request.direction, - req->reqs.pio_request.address, - req->reqs.pio_request.size, - req->reqs.pio_request.value); - req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(client->vhm_client_id, i); - } - } + val = virtio_vq_index_get(&rng->dev, req_cnt); if (val >= 0) - handle_vq_kick(client->rng, val); + handle_vq_kick(rng, val); + return 0; } @@ -385,15 +272,15 @@ static int vbs_rng_open(struct inode *inode, struct file *f) struct virtio_vq_info *vqs; int i; - pr_debug("%s!\n", __func__); - rng = kmalloc(sizeof(*rng), GFP_KERNEL); - if (!rng) { + if (rng == NULL) { pr_err("Failed to allocate memory for vbs_rng!\n"); return -ENOMEM; } dev = &rng->dev; + strncpy(dev->name, "vbs_rng", VBS_NAME_LEN); + dev->dev_notify = handle_kick; vqs = (struct virtio_vq_info *)&rng->vqs; for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) { @@ -411,6 +298,8 @@ static int vbs_rng_open(struct inode *inode, struct file *f) virtio_dev_init(dev, vqs, VBS_K_RNG_VQ_MAX); f->private_data = rng; + + /* init a hash table to maintain multi-connections */ vbs_rng_hash_init(); return 0; @@ -419,14 +308,10 @@ static int vbs_rng_open(struct inode *inode, struct file *f) static int vbs_rng_release(struct inode *inode, struct file *f) { struct vbs_rng *rng = f->private_data; - struct vbs_rng_client *client; int i; - pr_debug("%s!\n", __func__); - - client = vbs_rng_hash_find(rng->vhm_client_id); - if (!client) - pr_err("%s: UNLIKELY not found client!\n", + if (!rng) + pr_err("%s: UNLIKELY rng NULL!\n", __func__); vbs_rng_stop(rng); @@ -437,16 +322,16 @@ static int vbs_rng_release(struct inode *inode, struct file *f) /* device specific release */ vbs_rng_reset(rng); - pr_debug("vbs_rng_connection cnt is %d\n", vbs_rng_connection_cnt); + pr_debug("vbs_rng_connection cnt is %d\n", + vbs_rng_connection_cnt); - if (client && vbs_rng_connection_cnt--) - vbs_rng_hash_del(client->vhm_client_id); + if (rng && vbs_rng_connection_cnt--) + vbs_rng_hash_del(virtio_dev_client_id(&rng->dev)); if (!vbs_rng_connection_cnt) { pr_debug("vbs_rng remove all hash entries\n"); vbs_rng_hash_del_all(); } - kfree(client); kfree(rng); pr_debug("%s done\n", __func__); @@ -488,7 +373,8 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, * return vhost_net_set_features(n, features); */ case VBS_SET_VQ: - /* we handle this here because we want to register VHM client + /* + * we handle this here because we want to register VHM client * after handling VBS_K_SET_VQ request */ pr_debug("VBS_K_SET_VQ ioctl:\n"); @@ -498,10 +384,16 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, return -EFAULT; } /* Register VHM client */ - if (register_vhm_client(&rng->dev) < 0) { + if (virtio_dev_register(&rng->dev) < 0) { pr_err("failed to register VHM client!\n"); return -EFAULT; } + /* Added to local hash table */ + if (vbs_rng_hash_add(rng) < 0) { + pr_err("failed to add to hashtable!\n"); + return -EFAULT; + } + /* Increment counter */ vbs_rng_connection_cnt++; return r; default: @@ -544,6 +436,7 @@ static void vbs_rng_stop_vq(struct vbs_rng *rng, /* device specific function */ static void vbs_rng_stop(struct vbs_rng *rng) { + virtio_dev_deregister(&rng->dev); } /* device specific function */ diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 715c49156a1a..b2e185e115c8 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -63,6 +63,8 @@ #define _VBS_H_ #include +#include +#include /* * VBS-K device needs to handle frontend driver's kick in kernel. @@ -78,6 +80,9 @@ enum IORangeType { struct ctx { /* VHM required info */ int vmid; + int vhm_client_id; + int max_vcpu; + struct vhm_request *req_buf; }; struct virtio_desc { /* AKA vring_desc */ @@ -138,12 +143,16 @@ struct virtio_dev_info { enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ /* members created in kernel space VBS */ - void (*dev_notify)(void *, struct virtio_vq_info *); - /* device-wide notification */ + int (*dev_notify)(int, int); /* device-wide notification */ struct virtio_vq_info *vqs; /* virtqueue(s) */ int curq; /* current virtqueue index */ }; +static inline int virtio_dev_client_id(struct virtio_dev_info *dev) +{ + return dev->_ctx.vhm_client_id; +} + /* VBS Runtime Control APIs */ long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, int nvq); @@ -151,5 +160,8 @@ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); +long virtio_dev_register(struct virtio_dev_info *dev); +long virtio_dev_deregister(struct virtio_dev_info *dev); +int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); #endif From 328eff2b4c6e743011966e04562a888a01c1dbcb Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0837/1276] api doc: add ACRN VBS API docs Change-Id: I634c0117392ca529d7bd4b89a02fec43a0f70d63 Tracked-On: 220254 Signed-off-by: Hao Li --- Documentation/virtual/00-INDEX | 3 + Documentation/virtual/acrn/00-INDEX | 8 ++ Documentation/virtual/acrn/conf.py | 5 + Documentation/virtual/acrn/index.rst | 17 +++ Documentation/virtual/acrn/vbs.rst | 20 +++ Documentation/virtual/acrn/vhm.rst | 5 + drivers/vbs/vbs_rng.c | 9 ++ include/linux/vbs/vbs.h | 177 ++++++++++++++++++++++----- include/linux/vbs/vq.h | 106 +++++++++++++++- 9 files changed, 311 insertions(+), 39 deletions(-) create mode 100644 Documentation/virtual/acrn/00-INDEX create mode 100644 Documentation/virtual/acrn/conf.py create mode 100644 Documentation/virtual/acrn/index.rst create mode 100644 Documentation/virtual/acrn/vbs.rst create mode 100644 Documentation/virtual/acrn/vhm.rst diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX index af0d23968ee7..257aec22dbff 100644 --- a/Documentation/virtual/00-INDEX +++ b/Documentation/virtual/00-INDEX @@ -9,3 +9,6 @@ kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org uml/ - User Mode Linux, builds/runs Linux kernel as a userspace program. + +acrn/ + - ACRN Project. See also http://github.com/projectacrn/ diff --git a/Documentation/virtual/acrn/00-INDEX b/Documentation/virtual/acrn/00-INDEX new file mode 100644 index 000000000000..5beb50eef9e1 --- /dev/null +++ b/Documentation/virtual/acrn/00-INDEX @@ -0,0 +1,8 @@ +00-INDEX + - this file. +index.rst + - Index. +vhm.rst + - virtio and hypervisor service module (VHM) APIs. +vbs.rst + - virtio and backend service (VBS) APIs. diff --git a/Documentation/virtual/acrn/conf.py b/Documentation/virtual/acrn/conf.py new file mode 100644 index 000000000000..ed247df22700 --- /dev/null +++ b/Documentation/virtual/acrn/conf.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8; mode: python -*- + +project = "ACRN Project" + +tags.add("subproject") diff --git a/Documentation/virtual/acrn/index.rst b/Documentation/virtual/acrn/index.rst new file mode 100644 index 000000000000..3630d4fe3207 --- /dev/null +++ b/Documentation/virtual/acrn/index.rst @@ -0,0 +1,17 @@ +.. -*- coding: utf-8; mode: rst -*- + +============================= +ACRN Project +============================= + +.. toctree:: + + vbs.rst + vhm.rst + +.. only:: subproject + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/virtual/acrn/vbs.rst b/Documentation/virtual/acrn/vbs.rst new file mode 100644 index 000000000000..40a0683a1c0b --- /dev/null +++ b/Documentation/virtual/acrn/vbs.rst @@ -0,0 +1,20 @@ +================================ +Virtio and Backend Service (VBS) +================================ + +The Virtio and Backend Service (VBS) in part of ACRN Project. + +The VBS can be further divided into two parts: VBS in user space (VBS-U) +and VBS in kernel space (VBS-K). + +Example: +-------- +A reference driver for VBS-K can be found at :c:type:`struct vbs_rng`. + +.. kernel-doc:: drivers/vbs/vbs_rng.c + +APIs: +----- + +.. kernel-doc:: include/linux/vbs/vbs.h +.. kernel-doc:: include/linux/vbs/vq.h diff --git a/Documentation/virtual/acrn/vhm.rst b/Documentation/virtual/acrn/vhm.rst new file mode 100644 index 000000000000..56d498a016b0 --- /dev/null +++ b/Documentation/virtual/acrn/vhm.rst @@ -0,0 +1,5 @@ +================================== +Virtio and Hypervisor Module (VHM) +================================== + +The Virtio and Hypervisor service Module (VHM) in part of ACRN Project. diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 87965bafbbb3..2c71186801e7 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -91,6 +91,15 @@ enum { *}; */ +/** + * struct vbs_rng - Backend of virtio-rng based on VBS-K + * + * @dev : instance of struct virtio_dev_info + * @vqs : instances of struct virtio_vq_info + * @hwrng : device specific member + * @node : hashtable maintaining multiple connections + * from multiple guests/devices + */ struct vbs_rng { struct virtio_dev_info dev; struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index b2e185e115c8..725f1626dbc6 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -66,19 +66,26 @@ #include #include -/* - * VBS-K device needs to handle frontend driver's kick in kernel. - * For virtio 0.9.5, the kick register is a PIO register, - * for virtio 1.0+, the kick register could be a MMIO register. +/** + * enum IORangeType - type of registers to be handled in VBS-K + * + * @PIO_RANGE : Port I/O registers, for virtio 0.9.5 + * @MMIO_RANGE : Memory-Mapped I/O registers, for virtio 1.0+ */ enum IORangeType { PIO_RANGE = 0x0, /* default */ MMIO_RANGE = 0x1, }; -/* device context */ +/** + * struct ctx - VM context this device belongs to + * + * @vmid : ID of VM this device belongs to + * @vhm_client_id : ID of VHM client this device registers + * @max_vcpu : number of VCPU in this VM + * @req_buf : request buffers + */ struct ctx { - /* VHM required info */ int vmid; int vhm_client_id; int max_vcpu; @@ -109,59 +116,163 @@ struct vring_used { struct virtio_used ring[]; /* size N */ } __attribute__((packed)); -/* struct used to maintain virtqueue info from userspace VBS */ +/** + * struct virtio_vq_info - virtqueue data structure + */ struct virtio_vq_info { /* virtqueue info from VBS-U */ - uint16_t qsize; /* size of this queue (a power of 2) */ - uint32_t pfn; /* PFN of virt queue (not shifted!) */ - uint16_t msix_idx; /* MSI-X index/VIRTIO_MSI_NO_VECTOR */ - uint64_t msix_addr; /* MSI-X address specified by index */ - uint32_t msix_data; /* MSI-X data specified by index */ + /** @qsize: size of this queue (a power of 2) */ + uint16_t qsize; + /** @pfn: PFN of virt queue (not shifted!) */ + uint32_t pfn; + /** @msix_idx: MSI-X index/VIRTIO_MSI_NO_VECTOR */ + uint16_t msix_idx; + /** @msix_addr: MSI-X address specified by index */ + uint64_t msix_addr; + /** @msix_data: MSI-X data specified by index */ + uint32_t msix_data; /* members created in kernel space VBS */ - int (*vq_notify)(int); /* vq-wide notification */ - struct virtio_dev_info *dev; /* backpointer to virtio_dev_info */ - uint16_t num; /* we're the num'th virtqueue */ - uint16_t flags; /* virtqueue flags */ - uint16_t last_avail; /* a recent value of vq_avail->va_idx */ - uint16_t save_used; /* saved vq_used->vu_idx */ - - volatile struct virtio_desc *desc; /* descriptor array */ - volatile struct vring_avail *avail; /* the "avail" ring */ - volatile struct vring_used *used; /* the "used" ring */ + /** @vq_notify: vq-wide notification */ + int (*vq_notify)(int); + /** @dev: backpointer to virtio_dev_info */ + struct virtio_dev_info *dev; + /** @num: we're the num'th virtqueue */ + uint16_t num; + /** @flags: virtqueue flags */ + uint16_t flags; + /* private: a recent value of vq_avail->va_idx */ + uint16_t last_avail; + /* private: saved vq_used->vu_idx */ + uint16_t save_used; + + /* private: descriptor array */ + volatile struct virtio_desc *desc; + /* private: the "avail" ring */ + volatile struct vring_avail *avail; + /* private: the "used" ring */ + volatile struct vring_used *used; }; -/* struct used to maintain virtio device info from userspace VBS */ +/** + * struct virtio_dev_info - VBS-K device data structure + */ struct virtio_dev_info { /* dev info from VBS */ - char name[VBS_NAME_LEN]; /* VBS device name */ - struct ctx _ctx; /* device context */ - int nvq; /* number of virtqueues */ - uint32_t negotiated_features; /* features after guest loads driver */ - uint64_t io_range_start; /* IO range start of VBS device */ - uint64_t io_range_len; /* IO range len of VBS device */ - enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ + /** @name[]: VBS device name */ + char name[VBS_NAME_LEN]; + /** @_ctx: VM context this device belongs to */ + struct ctx _ctx; + /** @nvq: number of virtqueues */ + int nvq; + /** @negotiated_features: features after guest loads driver */ + uint32_t negotiated_features; + /** @io_range_start: start of an IO range VBS needs to handle */ + uint64_t io_range_start; + /** @io_range_len: len of an IO range VBS needs to handle */ + uint64_t io_range_len; + /** @io_range_type: IO range type, PIO or MMIO */ + enum IORangeType io_range_type; /* members created in kernel space VBS */ - int (*dev_notify)(int, int); /* device-wide notification */ - struct virtio_vq_info *vqs; /* virtqueue(s) */ - int curq; /* current virtqueue index */ + /** + * @dev_notify: device-wide notification + * + * This is the callback function to be registered to VHM, + * so that VBS gets notified when frontend accessed the register. + */ + int (*dev_notify)(int, int); + /** @vqs: virtqueue(s) of this device */ + struct virtio_vq_info *vqs; + /** @curq: current virtqueue index */ + int curq; }; +/** + * virtio_dev_client_id - get device's VHM client ID + * + * @dev: VBS-K device data struct + * + * Return: device's VHM client ID + */ static inline int virtio_dev_client_id(struct virtio_dev_info *dev) { return dev->_ctx.vhm_client_id; } /* VBS Runtime Control APIs */ + +/** + * virtio_dev_init - Initialize VBS-K device data structures + * + * @dev: Pointer to VBS-K device data struct + * @vqs: Pointer to VBS-K virtqueue data struct, normally in an array + * @nvq: Number of virtqueues this device has + * + * Return: 0 on success, <0 on error + */ long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, int nvq); + +/** + * virtio_dev_ioctl - VBS-K device's common ioctl routine + * + * @dev: Pointer to VBS-K device data struct + * @ioctl: Command of ioctl to device + * @argp: Data from user space + * + * Return: 0 on success, <0 on error + */ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); + +/** + * virtio_vqs_ioctl - VBS-K vq's common ioctl routine + * + * @dev: Pointer to VBS-K device data struct + * @ioctl: Command of ioctl to virtqueue + * @argp: Data from user space + * + * Return: 0 on success, <0 on error + */ long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); + +/** + * virtio_dev_register - register a VBS-K device to VHM + * + * Each VBS-K device will be registered as a VHM client, with the + * information including "kick" register location, callback, etc. + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ long virtio_dev_register(struct virtio_dev_info *dev); + +/** + * virtio_dev_register - unregister a VBS-K device from VHM + * + * Destroy the client corresponding to the VBS-K device specified. + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ long virtio_dev_deregister(struct virtio_dev_info *dev); + +/** + * virtio_vq_index_get - get virtqueue index that frontend kicks + * + * This API is normally called in the VBS-K device's callback + * function, to get value write to the "kick" register from + * frontend. + * + * @dev: Pointer to VBS-K device data struct + * @req_cnt: Number of requests need to handle, provided by VHM + * + * Return: >=0 on virtqueue index, <0 on error + */ int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); #endif diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 9ebde05e4663..9e865b8dff05 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -101,7 +101,13 @@ /* Functions for dealing with generalized "virtual devices" */ #define VQ_USED_EVENT_IDX(vq) ((vq)->avail->ring[(vq)->qsize]) -/* get virtqueue size according to virtio specification */ +/** + * virtio_vq_ring_size - Calculate size of a virtqueue + * + * @qsz: size of raw data in a certain virtqueue + * + * Return: size of a certain virtqueue + */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { size_t size; @@ -117,15 +123,26 @@ static inline size_t virtio_vq_ring_size(unsigned int qsz) return size; } -/* Is this ring ready for I/O? */ +/** + * virtio_vq_ring_ready - Is this ring ready for I/O? + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: 0 on not ready, and 1 on ready + */ static inline int virtio_vq_ring_ready(struct virtio_vq_info *vq) { return (vq->flags & VQ_ALLOC); } -/* - * Are there "available" descriptors? (This does not count - * how many, just returns True if there are some). +/** + * virtio_vq_has_descs - Are there "available" descriptors? + * + * This does not count how many, just returns True if there is any. + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: 0 on no available, and non-zero on available */ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) { @@ -133,7 +150,16 @@ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) vq->last_avail != vq->avail->idx); } -/* Deliver an interrupt to guest on the given virtual queue */ +/** + * virtio_vq_interrupt - Deliver an interrupt to guest on the given + * virtqueue. + * MSI-x or a generic MSI interrupt. + * + * @dev: Pointer to struct virtio_dev_info + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, struct virtio_vq_info *vq) { @@ -158,15 +184,83 @@ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, /* virtqueue initialization APIs */ + +/** + * virtio_vq_init - Initialize the currently-selected virtqueue + * + * The guest just gave us a page frame number, from which we can + * calculate the addresses of the queue. After calculation, the + * addresses are updated in vq's members. + * + * @vq: Pointer to struct virtio_vq_info + * @pfn: page frame number in guest physical address space + * + * Return: NULL + */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); + +/** + * virtio_vq_reset - reset one virtqueue, make it invalid + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ void virtio_vq_reset(struct virtio_vq_info *vq); /* virtqueue runtime APIs */ + +/** + * virtio_vq_getchain - Walk through the chain of descriptors + * involved in a request and put them into + * a given iov[] array + * + * @vq: Pointer to struct virtio_vq_info + * @pidx: Pointer to available ring position + * @iov: Pointer to iov[] array prepared by caller + * @n_iov: Size of iov[] array + * @flags: Pointer to a uint16_t array which will contain flag of + * each descriptor + * + * Return: number of descriptors + */ int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, struct iovec *iov, int n_iov, uint16_t *flags); + +/** + * virtio_vq_retchain - Return the currently-first request chain + * back to the available ring + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ void virtio_vq_retchain(struct virtio_vq_info *vq); + +/** + * virtio_vq_relchain - Return specified request chain to the guest, + * setting its I/O length to the provided value + * + * @vq: Pointer to struct virtio_vq_info + * @idx: Pointer to available ring position, returned by vq_getchain() + * @iolen: Number of data bytes to be returned to frontend + * + * Return: NULL + */ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); + +/** + * virtio_vq_endchains - Driver has finished processing "available" + * chains and calling vq_relchain on each one + * + * If driver used all the available chains, used_all should be set. + * + * @vq: Pointer to struct virtio_vq_info + * @used_all_avail: Flag indicating if driver used all available chains + * + * Return: NULL + */ void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); #endif From ccfb3afe9afd62470226fd6c1793daf01e312ec3 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0838/1276] HVLog: reserve memory for ACRN HVLog Change-Id: Ic87c83510d1405c791ce9c47872b960f801d45c2 Tracked-On: 220304 Signed-off-by: Li, Fei1 --- drivers/acrn/Kconfig | 7 ++++ drivers/acrn/Makefile | 3 +- drivers/acrn/acrn_hvlog.c | 83 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 drivers/acrn/acrn_hvlog.c diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 08b24a168167..9056a4f1f20a 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -11,3 +11,10 @@ config ACRN_TRACE This is the Trace driver for the Intel ACRN hypervisor. You can say y to build it into the kernel, or m to build it as a module. + +config ACRN_HVLOG + bool "Intel ACRN Hypervisor Logmsg support" + select ACRN_SHARED_BUFFER + ---help--- + This is the Trace driver for the Intel ACRN hypervisor log. + You can say y to build it into the kernel. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index 5430f4fa06fd..05dd698e8171 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o -obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o \ No newline at end of file +obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o +obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o diff --git a/drivers/acrn/acrn_hvlog.c b/drivers/acrn/acrn_hvlog.c new file mode 100644 index 000000000000..9c30fba58faf --- /dev/null +++ b/drivers/acrn/acrn_hvlog.c @@ -0,0 +1,83 @@ +/* + * ACRN Hypervisor logmsg + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ +#include +#include + +static unsigned long long hvlog_buf_size; +static unsigned long long hvlog_buf_base; + +static int __init early_hvlog(char *p) +{ + int ret; + + pr_debug("%s(%s)\n", __func__, p); + hvlog_buf_size = memparse(p, &p); + if (*p != '@') + return 0; + hvlog_buf_base = memparse(p + 1, &p); + + if (!!hvlog_buf_base && !!hvlog_buf_size) { + ret = memblock_reserve(hvlog_buf_base, hvlog_buf_size); + if (ret) { + pr_err("%s: Error reserving hvlog memblock\n", + __func__); + hvlog_buf_base = 0; + hvlog_buf_size = 0; + return ret; + } + } + return 0; +} +early_param("hvlog", early_hvlog); From 585de9c56aaf5fc6290da50dea274fb8660195ba Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0839/1276] HVLog: add HVLog module Change-Id: I328bee769ea93dacf1642e4ffc142adb66356d2a Tracked-On:220304 Signed-off-by: Li, Fei1 --- drivers/acrn/acrn_hvlog.c | 349 ++++++++++++++++++++++++++++++++++++++ drivers/acrn/acrn_trace.c | 6 +- drivers/acrn/sbuf.c | 34 +++- drivers/acrn/sbuf.h | 11 +- 4 files changed, 393 insertions(+), 7 deletions(-) diff --git a/drivers/acrn/acrn_hvlog.c b/drivers/acrn/acrn_hvlog.c index 9c30fba58faf..6d27b79cb1a1 100644 --- a/drivers/acrn/acrn_hvlog.c +++ b/drivers/acrn/acrn_hvlog.c @@ -52,8 +52,39 @@ * Li Fei * */ +#define pr_fmt(fmt) "ACRN HVLog: " fmt + #include #include +#include +#include +#include +#include +#include + +#include "sbuf.h" + +#define LOG_ENTRY_SIZE 80 +#define PCPU_NRS 4 + +#define foreach_cpu(cpu, cpu_num) \ + for ((cpu) = 0; (cpu) < (cpu_num); (cpu)++) + +#define foreach_hvlog_type(idx, hvlog_type) \ + for ((idx) = 0; (idx) < (hvlog_type); (idx)++) + +enum sbuf_hvlog_index { + ACRN_CURRNET_HVLOG = 0, + ACRN_LAST_HVLOG, + ACRN_HVLOG_TYPE +}; + +struct acrn_hvlog { + struct miscdevice miscdev; + shared_buf_t *sbuf; + atomic_t open_cnt; + int pcpu_num; +}; static unsigned long long hvlog_buf_size; static unsigned long long hvlog_buf_base; @@ -78,6 +109,324 @@ static int __init early_hvlog(char *p) return ret; } } + return 0; } early_param("hvlog", early_hvlog); + + +static inline shared_buf_t *hvlog_mark_unread(shared_buf_t *sbuf) +{ + /* sbuf must point to valid data. + * clear the lowest bit in the magic to indicate that + * the sbuf point to the last boot valid data, we should + * read it later. + */ + if (sbuf != NULL) + sbuf->magic &= ~1; + + return sbuf; +} + +static int acrn_hvlog_open(struct inode *inode, struct file *filp) +{ + struct acrn_hvlog *acrn_hvlog; + + acrn_hvlog = container_of(filp->private_data, + struct acrn_hvlog, miscdev); + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + /* More than one reader at the same time could get data messed up */ + if (atomic_cmpxchg(&acrn_hvlog->open_cnt, 0, 1) != 0) + return -EBUSY; + + filp->private_data = acrn_hvlog; + + return 0; +} + +static int acrn_hvlog_release(struct inode *inode, struct file *filp) +{ + struct acrn_hvlog *acrn_hvlog; + + acrn_hvlog = filp->private_data; + + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + atomic_dec(&acrn_hvlog->open_cnt); + filp->private_data = NULL; + + return 0; +} + +static ssize_t acrn_hvlog_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + char data[LOG_ENTRY_SIZE]; + struct acrn_hvlog *acrn_hvlog; + int ret; + + acrn_hvlog = (struct acrn_hvlog *)filp->private_data; + + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + if (acrn_hvlog->sbuf != NULL) { + ret = sbuf_get(acrn_hvlog->sbuf, (uint8_t *)&data); + if (ret > 0) { + if (copy_to_user(buf, &data, ret)) + return -EFAULT; + } + + return ret; + } + + return 0; +} + +static const struct file_operations acrn_hvlog_fops = { + .owner = THIS_MODULE, + .open = acrn_hvlog_open, + .release = acrn_hvlog_release, + .read = acrn_hvlog_read, +}; + +static struct acrn_hvlog acrn_hvlog_devs[ACRN_HVLOG_TYPE][PCPU_NRS] = { + [ACRN_CURRNET_HVLOG] = { + { + .miscdev = { + .name = "acrn_hvlog_cur_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 0, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 1, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 2, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 3, + }, + }, + [ACRN_LAST_HVLOG] = { + { + .miscdev = { + .name = "acrn_hvlog_last_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 0, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 1, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 2, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 3, + }, + } +}; + +static int __init acrn_hvlog_init(void) +{ + int ret = 0; + int i, j, idx; + uint32_t pcpu_id; + uint64_t logbuf_base0; + uint64_t logbuf_base1; + uint64_t logbuf_size; + uint32_t ele_size; + uint32_t ele_num; + uint32_t size; + bool sbuf_constructed = false; + + shared_buf_t *sbuf0[PCPU_NRS]; + shared_buf_t *sbuf1[PCPU_NRS]; + + pr_info("%s\n", __func__); + if (!hvlog_buf_base || !hvlog_buf_size) { + pr_warn("no fixed memory reserve for hvlog.\n"); + return 0; + } + + logbuf_base0 = hvlog_buf_base; + logbuf_size = (hvlog_buf_size >> 1); + logbuf_base1 = hvlog_buf_base + logbuf_size; + + size = (logbuf_size / PCPU_NRS); + ele_size = LOG_ENTRY_SIZE; + ele_num = (size - SBUF_HEAD_SIZE) / ele_size; + + foreach_cpu(pcpu_id, PCPU_NRS) { + sbuf0[pcpu_id] = sbuf_check_valid(ele_num, ele_size, + logbuf_base0 + size * pcpu_id); + sbuf1[pcpu_id] = sbuf_check_valid(ele_num, ele_size, + logbuf_base1 + size * pcpu_id); + } + + foreach_cpu(pcpu_id, PCPU_NRS) { + if (sbuf0[pcpu_id] == NULL) + continue; + + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf = + hvlog_mark_unread(sbuf0[pcpu_id]); + acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf = + sbuf_construct(ele_num, ele_size, + logbuf_base1 + size * pcpu_id); + } + sbuf_constructed = true; + } + + if (sbuf_constructed == false) { + foreach_cpu(pcpu_id, PCPU_NRS) { + if (sbuf1[pcpu_id] == NULL) + continue; + + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf = + hvlog_mark_unread(sbuf1[pcpu_id]); + } + } + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf = + sbuf_construct(ele_num, ele_size, + logbuf_base0 + size * pcpu_id); + } + sbuf_constructed = true; + } + + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(pcpu_id, PCPU_NRS) { + ret = sbuf_share_setup(pcpu_id, ACRN_HVLOG, + acrn_hvlog_devs[idx][pcpu_id].sbuf); + if (ret < 0) { + pr_err("Failed to setup %s, errno %d\n", + acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret); + goto setup_err; + } + } + } + + foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) { + foreach_cpu(pcpu_id, PCPU_NRS) { + atomic_set(&acrn_hvlog_devs[idx][pcpu_id].open_cnt, 0); + + ret = misc_register( + &acrn_hvlog_devs[idx][pcpu_id].miscdev); + if (ret < 0) { + pr_err("Failed to register %s, errno %d\n", + acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret); + goto reg_err; + } + } + } + + return 0; + +reg_err: + foreach_hvlog_type(i, idx) { + foreach_cpu(j, PCPU_NRS) { + misc_deregister(&acrn_hvlog_devs[i][j].miscdev); + } + } + + foreach_cpu(j, pcpu_id) { + misc_deregister(&acrn_hvlog_devs[idx][j].miscdev); + } + + pcpu_id = PCPU_NRS; +setup_err: + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(j, pcpu_id) { + sbuf_share_setup(j, ACRN_HVLOG, 0); + sbuf_deconstruct(acrn_hvlog_devs[idx][j].sbuf); + } + } + + return ret; +} + +static void __exit acrn_hvlog_exit(void) +{ + int idx; + uint32_t pcpu_id; + + pr_info("%s\n", __func__); + + foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) { + foreach_cpu(pcpu_id, PCPU_NRS) { + misc_deregister(&acrn_hvlog_devs[idx][pcpu_id].miscdev); + } + } + + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(pcpu_id, PCPU_NRS) { + sbuf_share_setup(pcpu_id, ACRN_HVLOG, 0); + sbuf_deconstruct(acrn_hvlog_devs[idx][pcpu_id].sbuf); + } + } +} + +module_init(acrn_hvlog_init); +module_exit(acrn_hvlog_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor Logmsg"); +MODULE_VERSION("0.1"); diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c index 856ab650acfd..d48b03625223 100644 --- a/drivers/acrn/acrn_trace.c +++ b/drivers/acrn/acrn_trace.c @@ -239,7 +239,7 @@ static int __init acrn_trace_init(void) } foreach_cpu(cpu, pcpu_num) { - ret = sbuf_share_setup(cpu, 0, sbuf_per_cpu[cpu]); + ret = sbuf_share_setup(cpu, ACRN_TRACE, sbuf_per_cpu[cpu]); if (ret < 0) { pr_err("Failed to setup SBuf, cpuid %d\n", cpu); goto out_sbuf; @@ -264,7 +264,7 @@ static int __init acrn_trace_init(void) out_sbuf: for (i = --cpu; i >= 0; i--) - sbuf_share_setup(i, 0, NULL); + sbuf_share_setup(i, ACRN_TRACE, NULL); cpu = pcpu_num; out_free: @@ -288,7 +288,7 @@ static void __exit acrn_trace_exit(void) misc_deregister(acrn_trace_devs[cpu]); /* set sbuf pointer to NULL in HV */ - sbuf_share_setup(cpu, 0, NULL); + sbuf_share_setup(cpu, ACRN_TRACE, NULL); /* free sbuf, sbuf_per_cpu[cpu] should be set NULL */ sbuf_free(sbuf_per_cpu[cpu]); diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index 8849ce28a06c..a3582325d9b9 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -185,7 +185,7 @@ int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) } EXPORT_SYMBOL(sbuf_share_setup); -shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, +shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size, uint64_t paddr) { shared_buf_t *sbuf; @@ -199,11 +199,39 @@ shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, if ((sbuf->magic == SBUF_MAGIC) && (sbuf->ele_num == ele_num) && (sbuf->ele_size == ele_size)) { - pr_info("construct sbuf at 0x%llx.\n", paddr); - /* return sbuf for dump */ return sbuf; } return NULL; } +EXPORT_SYMBOL(sbuf_check_valid); + +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t paddr) +{ + shared_buf_t *sbuf; + + if (!ele_num || !ele_size || !paddr) + return NULL; + + sbuf = (shared_buf_t *)phys_to_virt(paddr); + BUG_ON(!virt_addr_valid(sbuf)); + + memset(sbuf, 0, SBUF_HEAD_SIZE); + sbuf->magic = SBUF_MAGIC; + sbuf->ele_num = ele_num; + sbuf->ele_size = ele_size; + sbuf->size = ele_num * ele_size; + pr_info("construct sbuf at 0x%llx.\n", paddr); + return sbuf; +} EXPORT_SYMBOL(sbuf_construct); + +void sbuf_deconstruct(shared_buf_t *sbuf) +{ + if (sbuf == NULL) + return; + + sbuf->magic = 0; +} +EXPORT_SYMBOL(sbuf_deconstruct); diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h index 73608c35046c..4fae7a258bce 100644 --- a/drivers/acrn/sbuf.h +++ b/drivers/acrn/sbuf.h @@ -67,6 +67,11 @@ #define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */ #define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */ +enum sbuf_type { + ACRN_TRACE, + ACRN_HVLOG, + ACRN_SBUF_TYPE_MAX, +}; /** * (sbuf) head + buf (store (ele_num - 1) elements at most) * buffer empty: tail == head @@ -115,6 +120,10 @@ shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); void sbuf_free(shared_buf_t *sbuf); int sbuf_get(shared_buf_t *sbuf, uint8_t *data); int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf); -shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); +shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size, + uint64_t gpa); +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t gpa); +void sbuf_deconstruct(shared_buf_t *sbuf); #endif /* SHARED_BUF_H */ From 9273e9c3bf17958965aa57bdb2f962c91d3471fe Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0840/1276] update MEM_ATTR_WRITE_PROT with WB policy Change-Id: Icfc16c58148f8329528e27346dc2db047b7f37e2 Tracked-On: Signed-off-by: Jason Chen CJ --- include/linux/vhm/acrn_hv_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 688d69b6f5b0..12dc3c954526 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -122,7 +122,7 @@ #define MEM_ATTR_WP 0x00000400 #define MEM_ATTR_ALL 0x00000007 -#define MEM_ATTR_WRITE_PROT 0x00000005 +#define MEM_ATTR_WRITE_PROT 0x00000045 #define MEM_ATTR_ALL_WB 0x00000047 #define MEM_ATTR_ALL_WC 0x00000207 From e1f6fb124e66c4388b91a3179ecebd212b30a512 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0841/1276] vhm: modify mmio/memory map/unmap api Split the parameter(prot) to two parameters(mem_type and mem_access_right) Remove the parameter(prot) in unset_mmio_map Change-Id: I9d8bf3401898d53ec2b765135601d1e4bed1e09d Tracked-On: 222796 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 33 +++++++++++++++++++------------- include/linux/vhm/acrn_hv_defs.h | 26 ++++++++++++------------- include/linux/vhm/acrn_vhm_mm.h | 8 +++++--- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index a9ba810a7fd7..be6a47afad9a 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -148,7 +148,8 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, - unsigned int prot, unsigned int type) + unsigned int mem_type, unsigned int mem_access_right, + unsigned int type) { struct vm_set_memmap set_memmap; @@ -156,7 +157,8 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, set_memmap.remote_gpa = guest_gpa; set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; - set_memmap.prot = prot; + set_memmap.prot = ((mem_type & MEM_TYPE_MASK) | + (mem_access_right & MEM_ACCESS_RIGHT_MASK)); /* hypercall to notify hv the guest EPT setting*/ if (hcall_set_memmap(vmid, @@ -167,36 +169,39 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", - type, host_gpa, guest_gpa, len, prot); + type, host_gpa, guest_gpa, len, set_memmap.prot); return 0; } int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MMIO); + mem_type, mem_access_right, MAP_MMIO); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_UNMAP); + 0, 0, MAP_UNMAP); } int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MEM); + mem_type, mem_access_right, MAP_MEM); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; - unsigned int type, prot; + unsigned int type; + unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; mutex_lock(&vm->seg_lock); @@ -213,17 +218,19 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) } guest_gpa = seg->gpa; host_gpa = seg->vm0_gpa; - prot = memmap->prot | MEM_ATTR_WB_CACHE; + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); type = MAP_MEM; } else { guest_gpa = memmap->gpa; host_gpa = acrn_hpa2gpa(memmap->hpa); - prot = memmap->prot | MEM_ATTR_UNCACHED; + mem_type = MEM_TYPE_UC; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); type = MAP_MMIO; } if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, - prot, type) < 0) { + mem_type, mem_access_right, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); mutex_unlock(&vm->seg_lock); return -EFAULT; diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 12dc3c954526..411f197f7f3a 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -111,20 +111,18 @@ #define ACRN_INVALID_HPA (-1UL) /* Generic memory attributes */ -#define MEM_ATTR_READ 0x00000001 -#define MEM_ATTR_WRITE 0x00000002 -#define MEM_ATTR_EXECUTE 0x00000004 -#define MEM_ATTR_USER 0x00000008 -#define MEM_ATTR_WB_CACHE 0x00000040 -#define MEM_ATTR_WT_CACHE 0x00000080 -#define MEM_ATTR_UNCACHED 0x00000100 -#define MEM_ATTR_WC 0x00000200 -#define MEM_ATTR_WP 0x00000400 - -#define MEM_ATTR_ALL 0x00000007 -#define MEM_ATTR_WRITE_PROT 0x00000045 -#define MEM_ATTR_ALL_WB 0x00000047 -#define MEM_ATTR_ALL_WC 0x00000207 +#define MEM_ACCESS_READ 0x00000001 +#define MEM_ACCESS_WRITE 0x00000002 +#define MEM_ACCESS_EXEC 0x00000004 +#define MEM_ACCESS_RWX (MEM_ACCESS_READ | MEM_ACCESS_WRITE | \ + MEM_ACCESS_EXEC) +#define MEM_ACCESS_RIGHT_MASK 0x00000007 +#define MEM_TYPE_WB 0x00000040 +#define MEM_TYPE_WT 0x00000080 +#define MEM_TYPE_UC 0x00000100 +#define MEM_TYPE_WC 0x00000200 +#define MEM_TYPE_WP 0x00000400 +#define MEM_TYPE_MASK 0x000007C0 struct vm_set_memmap { #define MAP_MEM 0 diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 2ff1e25b22ce..ba8558949e48 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -66,11 +66,13 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len); int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); From 85c7dcfe34c85c54dea6305f18241a63b837f9de Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0842/1276] vhm cleanup: update one field name in vhm Change-Id: Ib125147ff72b566b183d20496251fa74244d7970 Tracked-On: 212688 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/vhm_ioctl_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 494213a9f9f0..9f2f21acbbe3 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -123,7 +123,7 @@ struct ic_ptdev_irq { struct { uint32_t virt_pin; /* IN: virtual IOAPIC pin */ uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t pic_pin; /* IN: pin from PIC? */ + uint32_t is_pic_pin; /* IN: pin from PIC? */ } intx; struct { /* IN: vector count of MSI/MSIX, From 28d51a834a335c4b02acc3caa2190a47cdcbf6c2 Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0843/1276] sos: add a config for VHM seperate the config for ACRN and VHM SOS has to enable both CONFIG_ACRN and CONFIG_ACRN_VHM. UOS only needs to enable CONFIG_ACRN. VHM is not used in UOS. Change-Id: I8529771e1943c18d790230533f7a4bcc84966350 Tracked-On: 224645 Signed-off-by: Shiqing Gao Reviewed-on: --- arch/x86/acrn/Kconfig | 4 ---- arch/x86/acrn/acrn.c | 2 +- drivers/Kconfig | 2 ++ drivers/Makefile | 2 +- drivers/char/Makefile | 2 +- drivers/vhm/Kconfig | 18 ++++++++++++++++++ 6 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 drivers/vhm/Kconfig diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig index 7788cb8cfb4b..ce0abc8cdcad 100644 --- a/arch/x86/acrn/Kconfig +++ b/arch/x86/acrn/Kconfig @@ -7,10 +7,6 @@ config ACRN bool "Enable services run on ACRN hypervisor" depends on X86_64 depends on PARAVIRT - depends on DMA_CMA - depends on PCI_MSI - depends on !INTEL_IOMMU - depends on !VMAP_STACK help This option is needed if were to run ACRN services linux on top of ACRN hypervisor. diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c index 3987e2287a9f..eea9db84ca89 100644 --- a/arch/x86/acrn/acrn.c +++ b/arch/x86/acrn/acrn.c @@ -42,7 +42,7 @@ static uint32_t __init acrn_detect(void) static void __init acrn_init_platform(void) { -#ifdef CONFIG_PCI_MSI +#if defined(CONFIG_PCI_MSI) && defined(CONFIG_ACRN_VHM) pv_irq_ops.write_msi = acrn_write_msi_msg; #endif } diff --git a/drivers/Kconfig b/drivers/Kconfig index ae0772a3581a..9ecbc1173cf9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -222,4 +222,6 @@ source "drivers/slimbus/Kconfig" source "drivers/vbs/Kconfig" source "drivers/acrn/Kconfig" + +source "drivers/vhm/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 22376163e35d..a4712ea26716 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -184,8 +184,8 @@ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ -obj-$(CONFIG_ACRN) += vhm/ obj-$(CONFIG_ACRN) += acrn/ +obj-$(CONFIG_ACRN_VHM) += vhm/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 39eccecf0d3a..6c32da6e54ad 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,4 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o -obj-$(CONFIG_ACRN) += vhm/ +obj-$(CONFIG_ACRN_VHM) += vhm/ diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig new file mode 100644 index 000000000000..0b0faeeff74a --- /dev/null +++ b/drivers/vhm/Kconfig @@ -0,0 +1,18 @@ +config ACRN_VHM + bool "Intel ACRN Hypervisor Virtio and Hypervisor service Module (VHM)" + depends on ACRN + depends on DMA_CMA + depends on PCI_MSI + depends on !INTEL_IOMMU + depends on !VMAP_STACK + default n + ---help--- + This is the Virtio and Hypervisor service Module (VHM) for + Intel ACRN hypervisor. + + It is required for Service OS. + User OS doesn't need to have this config. + + Say Y for SOS and say N for UOS. + + If unsure, say N. From 842db231625a79e75b4777377f6c25d73f816f82 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0844/1276] api doc: add vhm API docs Change-Id: If6df309ea215c1592ce41f7da724388ff1084087 Tracked-On: 220254 Signed-off-by: Yin Fengwei Reviewed-on: Signed-off-by: Yin Fengwei Reviewed-on: --- Documentation/virtual/acrn/vhm.rst | 8 ++ include/linux/vhm/acrn_vhm_ioreq.h | 106 +++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 119 ++++++++++++++++++++++++++++- include/linux/vhm/vhm_ioctl_defs.h | 60 +++++++++++++-- include/linux/vhm/vhm_vm_mngt.h | 73 ++++++++++++++++++ 5 files changed, 358 insertions(+), 8 deletions(-) diff --git a/Documentation/virtual/acrn/vhm.rst b/Documentation/virtual/acrn/vhm.rst index 56d498a016b0..901cff492e2b 100644 --- a/Documentation/virtual/acrn/vhm.rst +++ b/Documentation/virtual/acrn/vhm.rst @@ -3,3 +3,11 @@ Virtio and Hypervisor Module (VHM) ================================== The Virtio and Hypervisor service Module (VHM) in part of ACRN Project. + +APIs: +----- + +.. kernel-doc:: include/linux/vhm/acrn_vhm_ioreq.h +.. kernel-doc:: include/linux/vhm/acrn_vhm_mm.h +.. kernel-doc:: include/linux/vhm/vhm_ioctl_defs.h +.. kernel-doc:: include/linux/vhm/vhm_vm_mngt.h diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index fcec2c1e2eac..de3a8aa4eaf6 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -51,6 +51,12 @@ * */ +/** + * @file acrn_vhm_ioreq.h + * + * @brief Virtio and Hypervisor Module(VHM) ioreq APIs + */ + #ifndef __ACRN_VHM_IOREQ_H__ #define __ACRN_VHM_IOREQ_H__ @@ -59,22 +65,122 @@ typedef int (*ioreq_handler_t)(int client_id, int req); +/** + * acrn_ioreq_create_client - create ioreq client + * + * @vmid: ID to identify guest + * @handler: ioreq_handler of ioreq client + * If client want request handled in client thread context, set + * this parameter to NULL. If client want request handled out of + * client thread context, set handler function pointer of its own. + * VHM will create kernel thread and call handler to handle request + * + * @name: the name of ioreq client + * + * Return: client id on success, <0 on error + */ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); + +/** + * acrn_ioreq_destroy_client - destroy ioreq client + * + * @client_id: client id to identify ioreq client + * + * Return: + */ void acrn_ioreq_destroy_client(int client_id); +/** + * acrn_ioreq_add_iorange - add iorange monitored by ioreq client + * + * @client_id: client id to identify ioreq client + * @type: iorange type + * @start: iorange start address + * @end: iorange end address + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end); + +/** + * acrn_ioreq_del_iorange - del iorange monitored by ioreq client + * + * @client_id: client id to identify ioreq client + * @type: iorange type + * @start: iorange start address + * @end: iorange end address + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end); +/** + * acrn_ioreq_get_reqbuf - get request buffer + * request buffer is shared by all clients in one guest + * + * @client_id: client id to identify ioreq client + * + * Return: pointer to request buffer, NULL on error + */ struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); + +/** + * acrn_ioreq_attach_client - start handle request for ioreq client + * If request is handled out of client thread context, this function is + * only called once to be ready to handle new request. + * + * If request is handled in client thread context, this function must + * be called every time after the previous request handling is completed + * to be ready to handle new request. + * + * @client_id: client id to identify ioreq client + * @check_kthread_stop: whether check current kthread should be stopped + * + * Return: 0 on success, <0 on error, 1 if ioreq client is destroying + */ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); +/** + * acrn_ioreq_distribute_request - deliver request to corresponding client + * + * @vm: pointer to guest + * + * Return: 0 always + */ int acrn_ioreq_distribute_request(struct vhm_vm *vm); + +/** + * acrn_ioreq_complete_request - notify guest request handling is completed + * + * @client_id: client id to identify ioreq client + * @vcpu: identify request submitter + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu); +/** + * acrn_ioreq_intercept_bdf - set intercept bdf info of ioreq client + * + * @client_id: client id to identify ioreq client + * @bus: bus number + * @dev: device number + * @func: function number + * + * Return: + */ void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); + +/** + * acrn_ioreq_unintercept_bdf - clear intercept bdf info of ioreq client + * + * @client_id: client id to identify ioreq client + * + * Return: + */ void acrn_ioreq_unintercept_bdf(int client_id); /* IOReq APIs */ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index ba8558949e48..ba383b354986 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -51,25 +51,115 @@ * */ +/** + * @file acrn_vhm_mm.h + * + * @brief Virtio and Hypervisor Module memory manager APIs + */ + #ifndef __ACRN_VHM_MM_H__ #define __ACRN_VHM_MM_H__ #include #include -/* 1:1 mapping for service OS */ +/** + * acrn_hpa2gpa - physical address conversion + * + * convert host physical address (hpa) to guest physical address (gpa) + * gpa and hpa is 1:1 mapping for service OS + * + * @hpa: host physical address + * + * Return: guest physical address + */ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) { return hpa; } +/** + * map_guest_phys - map guest physical address + * + * to SOS kernel virtual address + * + * @vmid: guest vmid + * @uos_phy: phsical address in guest + * @size: the memory size mapped + * + * Return: SOS kernel virtual address, NULL on error + */ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); + +/** + * unmap_guest_phys - unmap guest physical address + * + * @vmid: guest vmid + * @uos_phy: phsical address in guest + * + * Return: 0 on success, <0 for error. + */ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); + +/** + * set_mmio_map - map mmio EPT mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * @mem_type: memory mapping type. Possilble value could be: + * MEM_TYPE_WB + * MEM_TYPE_WT + * MEM_TYPE_UC + * MEM_TYPE_WC + * MEM_TYPE_WP + * @mem_access_right: memory mapping access. Possible value could be: + * MEM_ACCESS_READ + * MEM_ACCESS_WRITE + * MEM_ACCESS_EXEC + * MEM_ACCESS_RWX + * + * Return: 0 on success, <0 for error. + */ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right); + +/** + * unset_mmio_map - unmap mmio mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * + * Return: 0 on success, <0 for error. + */ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len); + +/** + * update_memmap_attr - update mmio EPT mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * @mem_type: memory mapping type. Possilble value could be: + * MEM_TYPE_WB + * MEM_TYPE_WT + * MEM_TYPE_UC + * MEM_TYPE_WC + * MEM_TYPE_WP + * @mem_access_right: memory mapping access. Possible value could be: + * MEM_ACCESS_READ + * MEM_ACCESS_WRITE + * MEM_ACCESS_EXEC + * MEM_ACCESS_RWX + * + * Return: 0 on success, <0 for error. + */ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right); @@ -77,9 +167,36 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); int check_guest_mem(struct vhm_vm *vm); + +/** + * free_guest_mem - free memory of guest + * + * @vm: pointer to guest vm + * + * Return: + */ void free_guest_mem(struct vhm_vm *vm); +/** + * alloc_guest_memseg - alloc memory of guest according to pre-defined + * memory segment info + * + * @vm: pointer to guest vm + * @memseg: pointer to guest memory segment info + * + * Return: + */ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); + +/** + * map_guest_memseg - map EPT mmapping of memory of guest according to + * pre-defined memory mapping info + * + * @vm: pointer to guest vm + * @memmap: pointer to guest memory mapping info + * + * Return: + */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); #endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 9f2f21acbbe3..5bc7c666f2ea 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -43,6 +43,12 @@ * $FreeBSD$ */ +/** + * @file vhm_ioctl_defs.h + * + * @brief Virtio and Hypervisor Module definition for ioctl to user space + */ + #ifndef _VHM_IOCTL_DEFS_H_ #define _VHM_IOCTL_DEFS_H_ @@ -95,6 +101,12 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) +/** + * struct vm_memseg - memory segment info for guest + * + * @len: length of memory segment + * @gpa: guest physical start address of memory segment + */ struct vm_memseg { uint64_t len; uint64_t gpa; @@ -103,6 +115,15 @@ struct vm_memseg { #define VM_SYSMEM 0 #define VM_MMIO 1 +/** + * struct vm_memmap - EPT memory mapping info for guest + * + * @type: memory mapping type + * @gpa: guest physical start address of memory mapping + * @hpa: host physical start address of memory + * @len: the length of memory range mapped + * @prot: memory mapping attribute + */ struct vm_memmap { uint32_t type; uint32_t reserved; @@ -112,38 +133,63 @@ struct vm_memmap { uint32_t prot; /* RWX */ }; +/** + * struct ic_ptdev_irq - pass thru device irq data structure + */ struct ic_ptdev_irq { #define IRQ_INTX 0 #define IRQ_MSI 1 #define IRQ_MSIX 2 + /** @type: irq type */ uint32_t type; + /** @virt_bdf: virtual bdf description of pass thru device */ uint16_t virt_bdf; /* IN: Device virtual BDF# */ + /** @phy_bdf: physical bdf description of pass thru device */ uint16_t phys_bdf; /* IN: Device physical BDF# */ + /** union */ union { + /** struct intx - info of IOAPIC/PIC interrupt */ struct { - uint32_t virt_pin; /* IN: virtual IOAPIC pin */ - uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t is_pic_pin; /* IN: pin from PIC? */ + /** @virt_pin: virtual IOAPIC pin */ + uint32_t virt_pin; + /** @phys_pin: physical IOAPIC pin */ + uint32_t phys_pin; + /** @pic_pin: PIC pin */ + uint32_t is_pic_pin; } intx; + + /** struct msix - info of MSI/MSIX interrupt */ struct { - /* IN: vector count of MSI/MSIX, - * Keep this filed on top of msix */ + /* Keep this filed on top of msix */ + /** @vector_cnt: vector count of MSI/MSIX */ uint32_t vector_cnt; - /* IN: size of MSI-X table (round up to 4K) */ + /** @table_size: size of MSIX table(round up to 4K) */ uint32_t table_size; - /* IN: physical address of MSI-X table */ + /** @table_paddr: physical address of MSIX table */ uint64_t table_paddr; } msix; }; }; +/** + * struct ioreq_notify - data strcture to notify hypervisor ioreq is handled + * + * @client_id: client id to identify ioreq client + * @vcpu: identify the ioreq submitter + */ struct ioreq_notify { int32_t client_id; uint32_t vcpu; }; +/** + * struct api_version - data structure to track VHM API version + * + * @major_version: major version of VHM API + * @minor_version: minor version of VHM API + */ struct api_version { uint32_t major_version; uint32_t minor_version; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 5edacb31dc1b..e7bc8b2372f7 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -53,6 +53,12 @@ * Jason Chen CJ * */ + +/** + * @file vhm_vm_mngt.h + * + * @brief Virtio and Hypervisor Module(VHM) management APIs + */ #ifndef VHM_VM_MNGT_H #define VHM_VM_MNGT_H @@ -61,6 +67,22 @@ extern struct list_head vhm_vm_list; extern struct mutex vhm_vm_list_lock; +/** + * struct vhm_vm - data structure to track guest + * + * @dev: pointer to dev of linux device mode + * @list: list of vhm_vm + * @vmid: guest vmid + * @ioreq_fallback_client: default ioreq client + * @refcnt: reference count of guest + * @seg_lock: mutex to protect memseg_list + * @memseg_list: list of memseg + * @max_gfn: maximum guest page frame number + * @ioreq_client_lock: spinlock to protect ioreq_client_list + * @ioreq_client_list: list of ioreq clients + * @req_buf: request buffer shared between HV, SOS and UOS + * @pg: pointer to linux page which holds req_buf + */ struct vhm_vm { struct device *dev; struct list_head list; @@ -76,16 +98,67 @@ struct vhm_vm { struct page *pg; }; +/** + * struct vm_info - data structure to track guest info + * + * @max_vcpu: maximum vcpu number of guest + * @max_gfn: maximum guest page frame number + */ struct vm_info { int max_vcpu; int max_gfn; }; +/** + * struct find_get_vm - find and hold vhm_vm of guest according to guest vmid + * + * @vmid: guest vmid + * + * Return: pointer to vhm_vm, NULL if can't find vm matching vmid + */ struct vhm_vm *find_get_vm(unsigned long vmid); + +/** + * struct put_vm - release vhm_vm of guest according to guest vmid + * If the latest reference count drops to zero, free vhm_vm as well + * + * @vm: pointer to vhm_vm which identrify specific guest + * + * Return: + */ void put_vm(struct vhm_vm *vm); + +/** + * struct vhm_get_vm_info - get vm_info of specific guest + * + * @vmid: guest vmid + * @info: pointer to vm_info for returned vm_info + * + * Return: 0 on success, <0 on error + */ int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); + +/** + * struct vhm_inject_msi - inject MSI interrupt to guest + * + * @vmid: guest vmid + * @msi_addr: MSI addr matches MSI spec + * @msi_data: MSI data matches MSI spec + * + * Return: 0 on success, <0 on error + */ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); + +/** + * struct vhm_vm_gpa2hpa - convert guest physical address to + * host physical address + * + * @vmid: guest vmid + * @gap: guest physical address + * + * Return: host physical address, <0 on error + */ unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa); void vm_list_add(struct list_head *list); From deccd9f571c23c146eddf16216606240d7be1292 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0845/1276] api doc: update ACRN VBS API docs Change-Id: I9e56502a114019297ac04d7e8a2a07230e7adcfe Tracked-On: 220254 Signed-off-by: Hao Li Reviewed-on: --- include/linux/vbs/vq.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 9e865b8dff05..647466567db4 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -106,7 +106,7 @@ * * @qsz: size of raw data in a certain virtqueue * - * Return: size of a certain virtqueue + * Return: size of a certain virtqueue, in bytes */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { @@ -158,7 +158,7 @@ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) * @dev: Pointer to struct virtio_dev_info * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, struct virtio_vq_info *vq) @@ -195,7 +195,7 @@ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, * @vq: Pointer to struct virtio_vq_info * @pfn: page frame number in guest physical address space * - * Return: NULL + * Return: N/A */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); @@ -204,7 +204,7 @@ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); * * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ void virtio_vq_reset(struct virtio_vq_info *vq); @@ -233,7 +233,7 @@ int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, * * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ void virtio_vq_retchain(struct virtio_vq_info *vq); @@ -245,7 +245,7 @@ void virtio_vq_retchain(struct virtio_vq_info *vq); * @idx: Pointer to available ring position, returned by vq_getchain() * @iolen: Number of data bytes to be returned to frontend * - * Return: NULL + * Return: N/A */ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); @@ -259,7 +259,7 @@ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, * @vq: Pointer to struct virtio_vq_info * @used_all_avail: Flag indicating if driver used all available chains * - * Return: NULL + * Return: N/A */ void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); From e36d4f7309adbec317703d5c10be1f3d8b1ea23e Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0846/1276] license: update intel license for ACRN VBS Change-Id: I9afb8d0b45075051e59307b41f6754ebabfe6808 Tracked-On: 219330 Signed-off-by: Hao Li Reviewed-on: --- drivers/vbs/vbs.c | 4 ++-- drivers/vbs/vbs_rng.c | 4 ++-- drivers/vbs/vq.c | 4 ++-- include/linux/vbs/vbs.h | 4 ++-- include/linux/vbs/vbs_common_if.h | 4 ++-- include/linux/vbs/vq.h | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 9d96f45b9644..6c364364db3c 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 2c71186801e7..cdfed63bb1ad 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c index 886f48225de9..9f7a829c7a67 100644 --- a/drivers/vbs/vq.c +++ b/drivers/vbs/vq.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 725f1626dbc6..e4ecba020b08 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 91eb4e01d95c..25d6002c92bb 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 647466567db4..ca14f8f34888 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions From 85c8d4ad8c072bcbfaa2b4174f8767c2aa4ce7f2 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0847/1276] VBS-K: fix compilation warnings in VBS-K reference driver Change-Id: I0e67e625145082d7e2a9a44f744de015855aa7ce Tracked-On: 218445 Signed-off-by: Hao Li Reviewed-on: --- drivers/vbs/vbs_rng.c | 50 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index cdfed63bb1ad..88f6108ca2e8 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -124,15 +124,17 @@ static int vbs_rng_connection_cnt = 0; /* function declarations */ static int handle_kick(int client_id, int req_cnt); static void vbs_rng_reset(struct vbs_rng *rng); -static void vbs_rng_disable_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq); +static void vbs_rng_stop(struct vbs_rng *rng); +static void vbs_rng_flush(struct vbs_rng *rng); +#ifdef RUNTIME_CTRL static int vbs_rng_enable_vq(struct vbs_rng *rng, struct virtio_vq_info *vq); +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); static void vbs_rng_stop_vq(struct vbs_rng *rng, struct virtio_vq_info *vq); -static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush_vq(struct vbs_rng *rng, int index); -static void vbs_rng_flush(struct vbs_rng *rng); +#endif /* hash table related functions */ static void vbs_rng_hash_init(void) @@ -347,17 +349,6 @@ static int vbs_rng_release(struct inode *inode, struct file *f) return 0; } -static struct hwrng get_hwrng(struct vbs_rng *rng) -{ - return rng->hwrng; -} - -/* Set feature bits in kernel side device */ -static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) -{ - return 0; -} - static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, unsigned long arg) { @@ -424,11 +415,17 @@ static void vbs_rng_reset(struct vbs_rng *rng) } /* device specific function */ -static void vbs_rng_disable_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq) +static void vbs_rng_stop(struct vbs_rng *rng) +{ + virtio_dev_deregister(&rng->dev); +} + +/* device specific function */ +static void vbs_rng_flush(struct vbs_rng *rng) { } +#ifdef RUNTIME_CTRL /* device specific function */ static int vbs_rng_enable_vq(struct vbs_rng *rng, struct virtio_vq_info *vq) @@ -437,15 +434,15 @@ static int vbs_rng_enable_vq(struct vbs_rng *rng, } /* device specific function */ -static void vbs_rng_stop_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq) +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) { } /* device specific function */ -static void vbs_rng_stop(struct vbs_rng *rng) +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) { - virtio_dev_deregister(&rng->dev); } /* device specific function */ @@ -453,10 +450,17 @@ static void vbs_rng_flush_vq(struct vbs_rng *rng, int index) { } -/* device specific function */ -static void vbs_rng_flush(struct vbs_rng *rng) +static struct hwrng get_hwrng(struct vbs_rng *rng) { + return rng->hwrng; +} + +/* Set feature bits in kernel side device */ +static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) +{ + return 0; } +#endif static const struct file_operations vbs_rng_fops = { .owner = THIS_MODULE, From dfc7c7be7ecc3ca2fffb4881f3a18a3f560983fe Mon Sep 17 00:00:00 2001 From: "Yan, Like" Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0848/1276] Cleanup Kconfig Change-Id: I219bc9343fe47a1cdab70c247370beb9e425fcd4 Signed-off-by: Yan, Like --- drivers/acrn/Kconfig | 5 +++-- drivers/vbs/Kconfig | 4 ++-- drivers/vhm/Kconfig | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 9056a4f1f20a..9fc4cae04a56 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -1,12 +1,13 @@ config ACRN_SHARED_BUFFER bool "Intel ACRN SHARED BUFFER" + depends on ACRN_VHM ---help--- Ring buffer shared between ACRN Hypervisor and its SOS. Help ACRN performance profiling. config ACRN_TRACE tristate "Intel ACRN Hypervisor Trace support" - select ACRN_SHARED_BUFFER + depends on ACRN_SHARED_BUFFER ---help--- This is the Trace driver for the Intel ACRN hypervisor. You can say y to build it into the kernel, or m to build @@ -14,7 +15,7 @@ config ACRN_TRACE config ACRN_HVLOG bool "Intel ACRN Hypervisor Logmsg support" - select ACRN_SHARED_BUFFER + depends on ACRN_SHARED_BUFFER ---help--- This is the Trace driver for the Intel ACRN hypervisor log. You can say y to build it into the kernel. diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig index e3bb44d6a5e9..663d09c2c42f 100644 --- a/drivers/vbs/Kconfig +++ b/drivers/vbs/Kconfig @@ -2,7 +2,7 @@ # This Kconfig describes VBS for ACRN hypervisor # config VBS - tristate "Enable VBS framework for ACRN hypervisor" + bool "Enable VBS framework for ACRN hypervisor" depends on ACRN depends on ACRN_VHM default n @@ -20,7 +20,7 @@ config VBS_DEBUG say N here. This enables ACRN VBS debugging. config VBS_RNG - tristate "ACRN VBS reference driver: virtio RNG" + bool "ACRN VBS reference driver: virtio RNG" depends on VBS != n default n ---help--- diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig index 0b0faeeff74a..e59b9487cd65 100644 --- a/drivers/vhm/Kconfig +++ b/drivers/vhm/Kconfig @@ -3,7 +3,6 @@ config ACRN_VHM depends on ACRN depends on DMA_CMA depends on PCI_MSI - depends on !INTEL_IOMMU depends on !VMAP_STACK default n ---help--- From 56fa6270cc4e46a149e61d8022daa666b33928c9 Mon Sep 17 00:00:00 2001 From: Jack Ren Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0849/1276] skip sbuf and vhm initialization when booting natively Change-Id: Ib5cd72c208f6e7cc905418671cd655054132806f Tracked-On: 229665 Signed-off-by: Jack Ren Reviewed-on: --- drivers/acrn/sbuf.c | 4 ++++ drivers/char/vhm/vhm_dev.c | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index a3582325d9b9..b51ee04e12fa 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -57,6 +57,7 @@ #include #include +#include #include #include #include "sbuf.h" @@ -170,6 +171,9 @@ int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) { struct sbuf_setup_param ssp; + if (x86_hyper_type != X86_HYPER_ACRN) + return -ENODEV; + ssp.pcpu_id = pcpu_id; ssp.sbuf_id = sbuf_id; diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index b724c9e7bce2..8590d69fa4e6 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -590,12 +590,10 @@ static int __init vhm_init(void) unsigned long flag; struct hc_api_version api_version = {0, 0}; - pr_info("vhm: initializing\n"); + if (x86_hyper_type != X86_HYPER_ACRN) + return -ENODEV; - if (x86_hyper_type != X86_HYPER_ACRN) { - pr_err("vhm: not support acrn hypervisor!\n"); - return -EINVAL; - } + pr_info("vhm: initializing\n"); if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { pr_err("vhm: failed to get api version from Hypervisor !\n"); From 7d49ce6a75a8c342ff9953bd6aaa1e541591ae47 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0850/1276] VHM: add hugetlb page ept mapping support unlike cma, hugetlb allocates hugepage under user space, so VHM only need take care of ept mapping for these allocated huge pages. this patch add hugepage_map_guest function, it gets huge page struct pointer according to user virtual address input from ioctl IC_SET_MEMSEG, then build all required parameters for recording guest memseg and mapping ept entry through this page struct. Change-Id: I0b333613dc20fce41b9b091c72892bbac6b07735 Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 1 + drivers/vhm/Kconfig | 1 + drivers/vhm/vhm_mm.c | 101 +++++++++++++++++++++++++---- include/linux/vhm/vhm_ioctl_defs.h | 27 +++++--- include/linux/vhm/vhm_vm_mngt.h | 1 + 5 files changed, 110 insertions(+), 21 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 8590d69fa4e6..8d083a09bf06 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -129,6 +129,7 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; + vm->hugetlb_enabled = 0; vm_list_add(&vm->list); vm_mutex_unlock(&vhm_vm_list_lock); filep->private_data = vm; diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig index e59b9487cd65..4ddb1314709a 100644 --- a/drivers/vhm/Kconfig +++ b/drivers/vhm/Kconfig @@ -3,6 +3,7 @@ config ACRN_VHM depends on ACRN depends on DMA_CMA depends on PCI_MSI + depends on HUGETLBFS depends on !VMAP_STACK default n ---help--- diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index be6a47afad9a..cc08fd9d0965 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -110,31 +110,25 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +static int add_guest_memseg(struct vhm_vm *vm, unsigned long vm0_gpa, + unsigned long guest_gpa, unsigned long len) { struct guest_memseg *seg; - u64 vm0_gpa; int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) return -ENOMEM; - vm0_gpa = _alloc_memblk(vm->dev, memseg->len); - if (vm0_gpa == 0ULL) { - kfree(seg); - return -ENOMEM; - } - seg->vm0_gpa = vm0_gpa; - seg->len = memseg->len; - seg->gpa = memseg->gpa; + seg->gpa = guest_gpa; + seg->len = len; max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; if (vm->max_gfn < max_gfn) vm->max_gfn = max_gfn; - pr_info("VHM: alloc memseg with len=0x%lx, vm0_gpa=0x%llx," + pr_info("VHM: add memseg with len=0x%lx, vm0_gpa=0x%llx," " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); @@ -146,6 +140,22 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) return 0; } +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +{ + unsigned long vm0_gpa; + int ret; + + vm0_gpa = _alloc_memblk(vm->dev, memseg->len); + if (vm0_gpa == 0ULL) + return -ENOMEM; + + ret = add_guest_memseg(vm, vm0_gpa, memseg->gpa, memseg->len); + if (ret < 0) + _free_memblk(vm->dev, vm0_gpa, memseg->len); + + return ret; +} + static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, @@ -197,6 +207,61 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, mem_type, mem_access_right, MAP_MEM); } +static int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct page *page; + unsigned long len, guest_gpa, vma; + unsigned int type; + unsigned int mem_type, mem_access_right; + int ret; + + if (vm == NULL || memmap == NULL) + return -EINVAL; + + len = memmap->len; + vma = memmap->vma_base; + guest_gpa = memmap->gpa; + + while (len > 0) { + unsigned long vm0_gpa, pagesize; + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("failed to pin huge page!\n"); + return -ENOMEM; + } + + vm0_gpa = page_to_phys(page); + pagesize = PAGE_SIZE << compound_order(page); + + ret = add_guest_memseg(vm, vm0_gpa, guest_gpa, pagesize); + if (ret < 0) { + pr_err("failed to add memseg for huge page!\n"); + put_page(page); + return ret; + } + + /* TODO: do batch hypercall for multi ept mapping */ + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MEM; + if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, + mem_type, mem_access_right, type) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + put_page(page); + return -EFAULT; + } + + len -= pagesize; + vma += pagesize; + guest_gpa += pagesize; + } + + vm->hugetlb_enabled = 1; + + return 0; +} + int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; @@ -204,8 +269,13 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; + /* hugetlb use vma to do the mapping */ + if (memmap->type == VM_SYSMEM && memmap->using_vma) + return hugepage_map_guest(vm, memmap); + mutex_lock(&vm->seg_lock); + /* cma or mmio */ if (memmap->type == VM_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa == memmap->gpa @@ -249,8 +319,13 @@ void free_guest_mem(struct vhm_vm *vm) while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); + if (vm->hugetlb_enabled) { + /* just put_page to unpin huge page */ + put_page(pfn_to_page(seg->vm0_gpa >> PAGE_SHIFT)); + } else { + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) + pr_warn("failed to free memblk\n"); + } list_del(&seg->list); kfree(seg); } diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 5bc7c666f2ea..a0a830dec3fa 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -117,19 +117,30 @@ struct vm_memseg { /** * struct vm_memmap - EPT memory mapping info for guest - * - * @type: memory mapping type - * @gpa: guest physical start address of memory mapping - * @hpa: host physical start address of memory - * @len: the length of memory range mapped - * @prot: memory mapping attribute */ struct vm_memmap { + /** @type: memory mapping type */ uint32_t type; - uint32_t reserved; + /** @using_vma: using vma_base to get vm0_gpa, + * only for type == VM_SYSTEM + */ + uint32_t using_vma; + /** @gpa: user OS guest physical start address of memory mapping */ uint64_t gpa; - uint64_t hpa; /* only for type == VM_MMIO */ + /** union */ + union { + /** @hpa: host physical start address of memory, + * only for type == VM_MMIO + */ + uint64_t hpa; + /** @vma_base: service OS user virtual start address of + * memory, only for type == VM_SYSMEM && using_vma == true + */ + uint64_t vma_base; + }; + /** @len: the length of memory range mapped */ uint64_t len; /* mmap length */ + /** @prot: memory mapping attribute */ uint32_t prot; /* RWX */ }; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index e7bc8b2372f7..306bd54c4103 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -96,6 +96,7 @@ struct vhm_vm { struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; + int hugetlb_enabled; }; /** From 9393dc9d8c2f9b9df3c5b8d412d801d0afe77ded Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0851/1276] VHM: change VM_SYSMEM/VM_MMIO to VM_MEMMAP_SYSMEM/VM_MEMMAP_MMIO Change-Id: I7dc07502530ae47c6f9a3bc6a29fc271a053e8da Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/vhm_ioctl_defs.h | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index cc08fd9d0965..728998d0341d 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -270,13 +270,13 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) unsigned long guest_gpa, host_gpa; /* hugetlb use vma to do the mapping */ - if (memmap->type == VM_SYSMEM && memmap->using_vma) + if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); mutex_lock(&vm->seg_lock); /* cma or mmio */ - if (memmap->type == VM_SYSMEM) { + if (memmap->type == VM_MEMMAP_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa == memmap->gpa && seg->len == memmap->len) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index a0a830dec3fa..eb8d0d08a89d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -112,8 +112,8 @@ struct vm_memseg { uint64_t gpa; }; -#define VM_SYSMEM 0 -#define VM_MMIO 1 +#define VM_MEMMAP_SYSMEM 0 +#define VM_MEMMAP_MMIO 1 /** * struct vm_memmap - EPT memory mapping info for guest @@ -130,11 +130,12 @@ struct vm_memmap { /** union */ union { /** @hpa: host physical start address of memory, - * only for type == VM_MMIO + * only for type == VM_MEMMAP_MMIO */ uint64_t hpa; /** @vma_base: service OS user virtual start address of - * memory, only for type == VM_SYSMEM && using_vma == true + * memory, only for type == VM_MEMMAP_SYSMEM && + * using_vma == true */ uint64_t vma_base; }; From bb1fc820dca28773d990e9318852abf335751d1a Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0852/1276] VHM: add hash table support for huge pages use HUGEPAGE_2M_HLIST_ARRAY_SIZE(16) for 2M hash table size, HUGEPAGE_1G_HLIST_ARRAY_SIZE(1) for 1G hash table size. The assumption is that we only support 2M & 1G huge pages. Change-Id: I08d331d7b7ff7e6a96f36e8c496db3644628aa9e Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 5 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hugetlb.c | 273 ++++++++++++++++++++++++++++++++ drivers/vhm/vhm_mm.c | 83 +++------- include/linux/vhm/acrn_vhm_mm.h | 8 + include/linux/vhm/vhm_vm_mngt.h | 8 + 6 files changed, 313 insertions(+), 66 deletions(-) create mode 100644 drivers/vhm/vhm_hugetlb.c diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 8d083a09bf06..f4d2ec2b7e23 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -112,6 +112,7 @@ static DEFINE_MUTEX(table_iomems_lock); static int vhm_dev_open(struct inode *inodep, struct file *filep) { struct vhm_vm *vm; + int i; vm = kzalloc(sizeof(struct vhm_vm), GFP_KERNEL); pr_info("vhm_dev_open: opening device node\n"); @@ -124,6 +125,10 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) INIT_LIST_HEAD(&vm->memseg_list); mutex_init(&vm->seg_lock); + for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) + INIT_HLIST_HEAD(&vm->hugepage_hlist[i]); + mutex_init(&vm->hugepage_lock); + INIT_LIST_HEAD(&vm->ioreq_client_list); spin_lock_init(&vm->ioreq_client_lock); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index b4d58a92dcfd..23f17ae24f78 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c new file mode 100644 index 000000000000..afab8ab52567 --- /dev/null +++ b/drivers/vhm/vhm_hugetlb.c @@ -0,0 +1,273 @@ +/* + * virtio and hyperviosr service module (VHM): hugetlb + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HUGEPAGE_2M_SHIFT 21 +#define HUGEPAGE_1G_SHIFT 30 + +#define HUGEPAGE_1G_HLIST_IDX (HUGEPAGE_HLIST_ARRAY_SIZE - 1) + +struct hugepage_map { + struct hlist_node hlist; + u64 vm0_gpa; + size_t size; + u64 guest_gpa; +}; + +static inline struct hlist_head *hlist_2m_hash(struct vhm_vm *vm, + unsigned long guest_gpa) +{ + return &vm->hugepage_hlist[guest_gpa >> HUGEPAGE_2M_SHIFT & + (HUGEPAGE_2M_HLIST_ARRAY_SIZE - 1)]; +} + +static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, + unsigned long guest_gpa, unsigned long size) +{ + struct hugepage_map *map; + int max_gfn; + + map = kzalloc(sizeof(struct hugepage_map), GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->vm0_gpa = vm0_gpa; + map->guest_gpa = guest_gpa; + map->size = size; + + INIT_HLIST_NODE(&map->hlist); + + max_gfn = (map->guest_gpa + map->size) >> PAGE_SHIFT; + if (vm->max_gfn < max_gfn) + vm->max_gfn = max_gfn; + + pr_info("VHM: add hugepage with size=0x%lx, vm0_gpa=0x%llx," + " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", + map->size, map->vm0_gpa, map->guest_gpa, vm->max_gfn); + + mutex_lock(&vm->hugepage_lock); + /* 1G hugepage? */ + if (map->size == (1UL << HUGEPAGE_1G_SHIFT)) + hlist_add_head(&map->hlist, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX]); + else + hlist_add_head(&map->hlist, + hlist_2m_hash(vm, map->guest_gpa)); + mutex_unlock(&vm->hugepage_lock); + + return 0; +} + +int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct page *page; + unsigned long len, guest_gpa, vma; + unsigned int type; + unsigned int mem_type, mem_access_right; + int ret; + + if (vm == NULL || memmap == NULL) + return -EINVAL; + + len = memmap->len; + vma = memmap->vma_base; + guest_gpa = memmap->gpa; + + while (len > 0) { + unsigned long vm0_gpa, pagesize; + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("failed to pin huge page!\n"); + return -ENOMEM; + } + + vm0_gpa = page_to_phys(page); + pagesize = PAGE_SIZE << compound_order(page); + + ret = add_guest_map(vm, vm0_gpa, guest_gpa, pagesize); + if (ret < 0) { + pr_err("failed to add memseg for huge page!\n"); + put_page(page); + return ret; + } + + /* TODO: do batch hypercall for multi ept mapping */ + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MEM; + if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, + mem_type, mem_access_right, type) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + put_page(page); + return -EFAULT; + } + + len -= pagesize; + vma += pagesize; + guest_gpa += pagesize; + } + + vm->hugetlb_enabled = 1; + + return 0; +} + +void hugepage_free_guest(struct vhm_vm *vm) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + int i; + + mutex_lock(&vm->hugepage_lock); + for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) { + if (!hlist_empty(&vm->hugepage_hlist[i])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[i], hlist) { + hlist_del(&map->hlist); + /* put_page to unpin huge page */ + put_page(pfn_to_page( + map->vm0_gpa >> PAGE_SHIFT)); + kfree(map); + } + } + } + mutex_unlock(&vm->hugepage_lock); +} + +void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + + mutex_lock(&vm->hugepage_lock); + /* check 1G hlist first */ + if (!hlist_empty(&vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX], hlist) { + if (map->guest_gpa >= guest_phys + size || + guest_phys >= map->guest_gpa + map->size) + continue; + + if (guest_phys + size > map->guest_gpa + map->size || + guest_phys < map->guest_gpa) + goto err; + + mutex_unlock(&vm->hugepage_lock); + return phys_to_virt(map->vm0_gpa + + guest_phys - map->guest_gpa); + } + } + + /* check 2m hlist */ + hlist_for_each_entry_safe(map, htmp, + hlist_2m_hash(vm, guest_phys), hlist) { + if (map->guest_gpa >= guest_phys + size || + guest_phys >= map->guest_gpa + map->size) + continue; + + if (guest_phys + size > map->guest_gpa + map->size || + guest_phys < map->guest_gpa) + goto err; + + mutex_unlock(&vm->hugepage_lock); + return phys_to_virt(map->vm0_gpa + + guest_phys - map->guest_gpa); + } + +err: + mutex_unlock(&vm->hugepage_lock); + printk(KERN_WARNING "cannot find correct mem map, please check the " + "input's range or alignment"); + return NULL; +} + +int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + + mutex_lock(&vm->hugepage_lock); + /* check 1G hlist first */ + if (!hlist_empty(&vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX], hlist) { + if (map->guest_gpa <= guest_phys && + guest_phys < map->guest_gpa + map->size) { + mutex_unlock(&vm->hugepage_lock); + return 0; + } + } + } + /* check 2m hlist */ + hlist_for_each_entry_safe(map, htmp, + hlist_2m_hash(vm, guest_phys), hlist) { + if (map->guest_gpa <= guest_phys && + guest_phys < map->guest_gpa + map->size) { + mutex_unlock(&vm->hugepage_lock); + return 0; + } + } + mutex_unlock(&vm->hugepage_lock); + return -ESRCH; +} diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 728998d0341d..070327e616d6 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -156,7 +156,7 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) return ret; } -static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, +int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, unsigned int type) @@ -207,61 +207,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, mem_type, mem_access_right, MAP_MEM); } -static int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) -{ - struct page *page; - unsigned long len, guest_gpa, vma; - unsigned int type; - unsigned int mem_type, mem_access_right; - int ret; - - if (vm == NULL || memmap == NULL) - return -EINVAL; - - len = memmap->len; - vma = memmap->vma_base; - guest_gpa = memmap->gpa; - - while (len > 0) { - unsigned long vm0_gpa, pagesize; - - ret = get_user_pages_fast(vma, 1, 1, &page); - if (unlikely(ret != 1) || (page == NULL)) { - pr_err("failed to pin huge page!\n"); - return -ENOMEM; - } - - vm0_gpa = page_to_phys(page); - pagesize = PAGE_SIZE << compound_order(page); - - ret = add_guest_memseg(vm, vm0_gpa, guest_gpa, pagesize); - if (ret < 0) { - pr_err("failed to add memseg for huge page!\n"); - put_page(page); - return ret; - } - - /* TODO: do batch hypercall for multi ept mapping */ - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - put_page(page); - return -EFAULT; - } - - len -= pagesize; - vma += pagesize; - guest_gpa += pagesize; - } - - vm->hugetlb_enabled = 1; - - return 0; -} - int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; @@ -315,17 +260,15 @@ void free_guest_mem(struct vhm_vm *vm) { struct guest_memseg *seg; + if (vm->hugetlb_enabled) + return hugepage_free_guest(vm); + mutex_lock(&vm->seg_lock); while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (vm->hugetlb_enabled) { - /* just put_page to unpin huge page */ - put_page(pfn_to_page(seg->vm0_gpa >> PAGE_SHIFT)); - } else { - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); - } + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) + pr_warn("failed to free memblk\n"); list_del(&seg->list); kfree(seg); } @@ -412,6 +355,9 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) size_t len = vma->vm_end - vma->vm_start; int ret; + if (vm->hugetlb_enabled) + return -EINVAL; + mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa != offset || seg->len != len) @@ -456,7 +402,10 @@ void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) if (vm == NULL) return NULL; - ret = do_map_guest_phys(vm, guest_phys, size); + if (vm->hugetlb_enabled) + ret = hugepage_map_guest_phys(vm, guest_phys, size); + else + ret = do_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -492,7 +441,11 @@ int unmap_guest_phys(unsigned long vmid, u64 guest_phys) return -ESRCH; } - ret = do_unmap_guest_phys(vm, guest_phys); + if (vm->hugetlb_enabled) + ret = hugepage_unmap_guest_phys(vm, guest_phys); + else + ret = do_unmap_guest_phys(vm, guest_phys); + put_vm(vm); return ret; } diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index ba383b354986..9be6749d12e2 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -199,4 +199,12 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right, + unsigned int type); +int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); +void hugepage_free_guest(struct vhm_vm *vm); +void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); +int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); #endif diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 306bd54c4103..f0a7e1cf7b05 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -67,6 +67,10 @@ extern struct list_head vhm_vm_list; extern struct mutex vhm_vm_list_lock; +#define HUGEPAGE_2M_HLIST_ARRAY_SIZE 16 +#define HUGEPAGE_1G_HLIST_ARRAY_SIZE 1 +#define HUGEPAGE_HLIST_ARRAY_SIZE (HUGEPAGE_2M_HLIST_ARRAY_SIZE + \ + HUGEPAGE_1G_HLIST_ARRAY_SIZE) /** * struct vhm_vm - data structure to track guest * @@ -77,6 +81,8 @@ extern struct mutex vhm_vm_list_lock; * @refcnt: reference count of guest * @seg_lock: mutex to protect memseg_list * @memseg_list: list of memseg + * @hugepage_lock: mutex to protect hugepage_hlist + * @hugepage_hlist: hash list of hugepage * @max_gfn: maximum guest page frame number * @ioreq_client_lock: spinlock to protect ioreq_client_list * @ioreq_client_list: list of ioreq clients @@ -91,6 +97,8 @@ struct vhm_vm { long refcnt; struct mutex seg_lock; struct list_head memseg_list; + struct mutex hugepage_lock; + struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; From 2ccd2f6e2ba7f71669a566ed197dea025edc2051 Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0853/1276] VHM: add service to support px data transition The px data is hard coded within HV, DM will get these data to build DSDT for UOS. With this DSDT, UOS would have capability on Px control if acpi-cpufreq driver is enabled in kernel. So this patch is to add the service to interact with both HV and DM. The detailed working rationale is illustrated in HV patch set. Change-Id: Icfd01880dcfe0fd938a05c6f31614dfdcd48631a Tracked-On: 212378 Signed-off-by: Victor Sun Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 40 ++++++++++++++++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 ++++ include/linux/vhm/acrn_common.h | 23 +++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 4 +++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 4 +++ 6 files changed, 77 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index f4d2ec2b7e23..5000ed80e177 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -532,6 +532,46 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_PM_GET_CPU_STATE: { + uint64_t cmd; + + if (copy_from_user(&cmd, + (void *)ioctl_param, sizeof(cmd))) + return -EFAULT; + + switch (cmd & PMCMD_TYPE_MASK) { + case PMCMD_GET_PX_CNT: { + uint8_t px_cnt; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_cnt)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &px_cnt, sizeof(px_cnt))) + ret = -EFAULT; + + break; + } + case PMCMD_GET_PX_DATA: { + struct cpu_px_data px_data; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_data)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &px_data, sizeof(px_data))) + ret = -EFAULT; + break; + } + default: + ret = -EFAULT; + break; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d0da22f2a88b..df87febaf60d 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -82,6 +82,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) +{ + return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index f27feb7a3e57..d48fe80f6fab 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -206,4 +206,27 @@ struct acrn_vm_pci_msix_remap { */ #define GUEST_CFG_OFFSET 0xd0000 +struct cpu_px_data { + uint64_t core_frequency; /* megahertz */ + uint64_t power; /* milliWatts */ + uint64_t transition_latency; /* microseconds */ + uint64_t bus_master_latency; /* microseconds */ + uint64_t control; /* control value */ + uint64_t status; /* success indicator */ +} __attribute__((aligned(8))); + +#define PMCMD_VMID_MASK 0xff000000 +#define PMCMD_VCPUID_MASK 0x00ff0000 +#define PMCMD_STATE_NUM_MASK 0x0000ff00 +#define PMCMD_TYPE_MASK 0x000000ff + +#define PMCMD_VMID_SHIFT 24 +#define PMCMD_VCPUID_SHIFT 16 +#define PMCMD_STATE_NUM_SHIFT 8 + +enum pm_cmd_type { + PMCMD_GET_PX_CNT, + PMCMD_GET_PX_DATA, +}; + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 411f197f7f3a..d2da1a760783 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -106,6 +106,10 @@ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +/* Power management */ +#define HC_ID_PM_BASE 0x80UL +#define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) #define ACRN_INVALID_HPA (-1UL) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e56a16c5518f..2372906946d6 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -144,6 +144,7 @@ inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index eb8d0d08a89d..3b05d8228e53 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -101,6 +101,10 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) +/* Power management */ +#define IC_ID_PM_BASE 0x60UL +#define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) + /** * struct vm_memseg - memory segment info for guest * From f72f5324bd149f1e8cfe141c6d3ceb1453760221 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0854/1276] sos: sync common header file sync common header file (acrn_common.h) Change-Id: I5d236b89f0799c788dca652ac0ebeb729e20e40c Signed-off-by: Mingqiang Chi Reviewed-on: --- include/linux/vhm/acrn_common.h | 169 ++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 31 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index d48fe80f6fab..40f3444b7e73 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -74,6 +74,16 @@ #define REQUEST_READ 0 #define REQUEST_WRITE 1 +/* Generic VM flags from guest OS */ +#define SECURE_WORLD_ENABLED (1UL<<0) /* Whether secure world is enabled */ + +/** + * @brief Hypercall + * + * @addtogroup acrn_hypercall ACRN Hypercall + * @{ + */ + struct mmio_request { uint32_t direction; uint32_t reserved; @@ -139,66 +149,149 @@ struct vhm_request_buffer { }; } __attribute__((aligned(4096))); -/* Common API params */ +/** + * @brief Info to create a VM, the parameter for HC_CREATE_VM hypercall + */ struct acrn_create_vm { - int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ - uint32_t vcpu_num; /* IN: VM vcpu number */ - uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t secure_world_enabled;/* IN: whether Secure World is enabled */ - uint8_t reserved[31]; /* Reserved for future use */ + /** created vmid return to VHM. Keep it first field */ + int32_t vmid; + + /** VCPU numbers this VM want to create */ + uint32_t vcpu_num; + + /** the GUID of this VM */ + uint8_t GUID[16]; + + /* VM flag bits from Guest OS, now used + * SECURE_WORLD_ENABLED (1UL<<0) + */ + uint64_t vm_flag; + + /** Reserved for future use*/ + uint8_t reserved[24]; } __attribute__((aligned(8))); +/** + * @brief Info to create a VCPU + * + * the parameter for HC_CREATE_VCPU hypercall + */ struct acrn_create_vcpu { - uint32_t vcpu_id; /* IN: vcpu id */ - uint32_t pcpu_id; /* IN: pcpu id */ + /** the virtual CPU ID for the VCPU created */ + uint32_t vcpu_id; + + /** the physical CPU ID for the VCPU created */ + uint32_t pcpu_id; } __attribute__((aligned(8))); +/** + * @brief Info to set ioreq buffer for a created VM + * + * the parameter for HC_SET_IOREQ_BUFFER hypercall + */ struct acrn_set_ioreq_buffer { - uint64_t req_buf; /* IN: gpa of per VM request_buffer*/ + /** guest physical address of VM request_buffer */ + uint64_t req_buf; } __attribute__((aligned(8))); -/* - * intr type - * IOAPIC: inject interrupt to IOAPIC - * ISA: inject interrupt to both PIC and IOAPIC - */ +/** Interrupt type for acrn_irqline: inject interrupt to IOAPIC */ #define ACRN_INTR_TYPE_ISA 0 + +/** Interrupt type for acrn_irqline: inject interrupt to both PIC and IOAPIC */ #define ACRN_INTR_TYPE_IOAPIC 1 -/* For ISA, PIC, IOAPIC etc */ +/** + * @brief Info to assert/deassert/pulse a virtual IRQ line for a VM + * + * the parameter for HC_ASSERT_IRQLINE/HC_DEASSERT_IRQLINE/HC_PULSE_IRQLINE + * hypercall + */ struct acrn_irqline { + /** interrupt type which could be IOAPIC or ISA */ uint32_t intr_type; + + /** reserved for alignment padding */ uint32_t reserved; - uint64_t pic_irq; /* IN: for ISA type */ - uint64_t ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ + + /** pic IRQ for ISA type */ + uint64_t pic_irq; + + /** ioapic IRQ for IOAPIC & ISA TYPE, + * if -1 then this IRQ will not be injected + */ + uint64_t ioapic_irq; } __attribute__((aligned(8))); -/* For MSI type inject */ +/** + * @brief Info to inject a MSI interrupt to VM + * + * the parameter for HC_INJECT_MSI hypercall + */ struct acrn_msi_entry { - uint64_t msi_addr; /* IN: addr[19:12] with dest vcpu id */ - uint64_t msi_data; /* IN: data[7:0] with vector */ + /** MSI addr[19:12] with dest VCPU ID */ + uint64_t msi_addr; + + /** MSI data[7:0] with vector */ + uint64_t msi_data; } __attribute__((aligned(8))); -/* For NMI inject */ +/** + * @brief Info to inject a NMI interrupt for a VM + */ struct acrn_nmi_entry { - int64_t vcpuid; /* IN: -1 means vcpu0 */ + /** virtual CPU ID to inject */ + int64_t vcpu_id; } __attribute__((aligned(8))); +/** + * @brief Info to remap pass-through PCI MSI for a VM + * + * the parameter for HC_VM_PCI_MSIX_REMAP hypercall + */ struct acrn_vm_pci_msix_remap { - uint16_t virt_bdf; /* IN: Device virtual BDF# */ - uint16_t phys_bdf; /* IN: Device physical BDF# */ - uint16_t msi_ctl; /* IN: PCI MSI/x cap control data */ + /** pass-through PCI device virtual BDF# */ + uint16_t virt_bdf; + + /** pass-through PCI device physical BDF# */ + uint16_t phys_bdf; + + /** pass-through PCI device MSI/MSI-X cap control data */ + uint16_t msi_ctl; + + /** reserved for alignment padding */ uint16_t reserved; + + /** pass-through PCI device MSI address to remap, which will + * return the caller after remapping + */ uint64_t msi_addr; /* IN/OUT: msi address to fix */ - uint32_t msi_data; /* IN/OUT: msi data to fix */ - int32_t msix; /* IN: 0 - MSI, 1 - MSI-X */ - int32_t msix_entry_index; /* IN: MSI-X the entry table index */ - /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ + + /** pass-through PCI device MSI data to remap, which will + * return the caller after remapping + */ + uint32_t msi_data; + + /** pass-through PCI device is MSI or MSI-X + * 0 - MSI, 1 - MSI-X + */ + int32_t msix; + + /** if the pass-through PCI device is MSI-X, this field contains + * the MSI-X entry table index + */ + int32_t msix_entry_index; + + /** if the pass-through PCI device is MSI-X, this field contains + * Vector Control for MSI-X Entry, field defined in MSI-X spec + */ uint32_t vector_ctl; } __attribute__((aligned(8))); -/* It's designed to support passing DM config data pointer, based on it, - * hypervisor would parse then pass DM defined configration to GUEST vcpu +/** + * @brief The guest config pointer offset. + * + * It's designed to support passing DM config data pointer, based on it, + * hypervisor would parse then pass DM defined configuration to GUEST VCPU * when booting guest VM. * the address 0xd0000 here is designed by DM, as it arranged all memory * layout below 1M, DM should make sure there is no overlap for the address @@ -206,6 +299,10 @@ struct acrn_vm_pci_msix_remap { */ #define GUEST_CFG_OFFSET 0xd0000 +/** + * @brief Info The power state data of a VCPU. + * + */ struct cpu_px_data { uint64_t core_frequency; /* megahertz */ uint64_t power; /* milliWatts */ @@ -215,6 +312,12 @@ struct cpu_px_data { uint64_t status; /* success indicator */ } __attribute__((aligned(8))); +/** + * @brief Info PM command from DM/VHM. + * + * The command would specify request type(i.e. get px count or data) for + * specific VM and specific VCPU with specific state number.like P(n). + */ #define PMCMD_VMID_MASK 0xff000000 #define PMCMD_VCPUID_MASK 0x00ff0000 #define PMCMD_STATE_NUM_MASK 0x0000ff00 @@ -229,4 +332,8 @@ enum pm_cmd_type { PMCMD_GET_PX_DATA, }; +/** + * @} + */ + #endif /* ACRN_COMMON_H */ From 6bafc7cbc370aa6e2fb20a8f6862b1db79b459ce Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0855/1276] sos_kernel: export restart vm function to DM. Two major changes: - Add ioctl interface to invoke vm restart between DM and vhm - Add hypercall interface to invoke vm restart between vhm and hv Change-Id: If5d1555b2fe7b6e3ef9dad2c471b67ff1ac888c9 Signed-off-by: Yin Fengwei --- drivers/char/vhm/vhm_dev.c | 9 +++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 17 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5000ed80e177..29b962405f9d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -235,6 +235,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_RESTART_VM: { + ret = hcall_restart_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to restart VM %ld!\n", vm->vmid); + return -EFAULT; + } + break; + } + case IC_DESTROY_VM: { ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index df87febaf60d..463cd71d4b93 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -72,6 +72,11 @@ inline long hcall_pause_vm(unsigned long vmid) return acrn_hypercall1(HC_PAUSE_VM, vmid); } +inline long hcall_restart_vm(unsigned long vmid) +{ + return acrn_hypercall1(HC_RESTART_VM, vmid); +} + inline long hcall_destroy_vm(unsigned long vmid) { return acrn_hypercall1(HC_DESTROY_VM, vmid); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d2da1a760783..8dc5b37510bb 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -76,6 +76,7 @@ #define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) +#define HC_RESTART_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 2372906946d6..38a9cb9e8487 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -142,6 +142,7 @@ inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); +inline long hcall_restart_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 3b05d8228e53..028096bde2ec 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -72,6 +72,7 @@ #define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) +#define IC_RESTART_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 064bc0ce6a740210359bc8cc122830101dae210c Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Thu, 19 Apr 2018 00:15:43 +0800 Subject: [PATCH 0856/1276] VHM: add service to support cx data transition Like Acrn px enabling, the cx data is also hard coded within HV, DM will get hard coded cx data to build DSDT for UOS. With this DSDT, UOS would have capability on Cx control if acpi-idle driver is enabled in kernel. Change-Id: I34cf5d99a7458ced51a52789027b0451e40a20bb Signed-off-by: Victor Sun --- drivers/char/vhm/vhm_dev.c | 21 +++++++++++++++++---- include/linux/vhm/acrn_common.h | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 29b962405f9d..fe8d16df129f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -549,15 +549,16 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; switch (cmd & PMCMD_TYPE_MASK) { - case PMCMD_GET_PX_CNT: { - uint8_t px_cnt; + case PMCMD_GET_PX_CNT: + case PMCMD_GET_CX_CNT: { + uint64_t pm_info; - ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_cnt)); + ret = hcall_get_cpu_state(cmd, virt_to_phys(&pm_info)); if (ret < 0) return -EFAULT; if (copy_to_user((void *)ioctl_param, - &px_cnt, sizeof(px_cnt))) + &pm_info, sizeof(pm_info))) ret = -EFAULT; break; @@ -574,6 +575,18 @@ static long vhm_dev_ioctl(struct file *filep, ret = -EFAULT; break; } + case PMCMD_GET_CX_DATA: { + struct cpu_cx_data cx_data; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&cx_data)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &cx_data, sizeof(cx_data))) + ret = -EFAULT; + break; + } default: ret = -EFAULT; break; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 40f3444b7e73..00088dcc8d7d 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -303,6 +303,30 @@ struct acrn_vm_pci_msix_remap { * @brief Info The power state data of a VCPU. * */ + +#define SPACE_SYSTEM_MEMORY 0 +#define SPACE_SYSTEM_IO 1 +#define SPACE_PCI_CONFIG 2 +#define SPACE_Embedded_Control 3 +#define SPACE_SMBUS 4 +#define SPACE_PLATFORM_COMM 10 +#define SPACE_FFixedHW 0x7F + +struct acpi_generic_address { + uint8_t space_id; + uint8_t bit_width; + uint8_t bit_offset; + uint8_t access_size; + uint64_t address; +} __attribute__((aligned(8))); + +struct cpu_cx_data { + struct acpi_generic_address cx_reg; + uint8_t type; + uint32_t latency; + uint64_t power; +} __attribute__((aligned(8))); + struct cpu_px_data { uint64_t core_frequency; /* megahertz */ uint64_t power; /* milliWatts */ @@ -315,8 +339,10 @@ struct cpu_px_data { /** * @brief Info PM command from DM/VHM. * - * The command would specify request type(i.e. get px count or data) for - * specific VM and specific VCPU with specific state number.like P(n). + * The command would specify request type(e.g. get px count or data) for + * specific VM and specific VCPU with specific state number. + * For Px, PMCMD_STATE_NUM means Px number from 0 to (MAX_PSTATE - 1), + * For Cx, PMCMD_STATE_NUM means Cx entry index from 1 to MAX_CX_ENTRY. */ #define PMCMD_VMID_MASK 0xff000000 #define PMCMD_VCPUID_MASK 0x00ff0000 @@ -330,6 +356,8 @@ struct cpu_px_data { enum pm_cmd_type { PMCMD_GET_PX_CNT, PMCMD_GET_PX_DATA, + PMCMD_GET_CX_CNT, + PMCMD_GET_CX_DATA, }; /** From 6664ad9036f8b08902f48f6ea6ad3ca79134ade5 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0857/1276] vhm: add set_memmaps hypercall support This new added hypercall is to support multi regions memmap in one time, which improve the performance. 1 API is added to support set_memmaps hypercall: - int set_memmaps(struct set_memmaps *memmaps) struct set_memmaps is added to present multi region memmap info, which include a page buffer to fill the memmaps array. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_mm.c | 14 ++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 32 +++++++++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 12 +++++++++++- include/linux/vhm/vhm_hypercall.h | 1 + 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 463cd71d4b93..bbdbea8d623c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -97,6 +97,11 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } +inline long hcall_set_memmaps(unsigned long pa_memmaps) +{ + return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 070327e616d6..75ccd3f09a4e 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -199,6 +199,20 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, 0, 0, MAP_UNMAP); } +int set_memmaps(struct set_memmaps *memmaps) +{ + if (memmaps == NULL) + return -EINVAL; + if (memmaps->memmaps_num > 0) { + if (hcall_set_memmaps(virt_to_phys(memmaps)) < 0) { + pr_err("vhm: failed to set memmaps!\n"); + return -EFAULT; + } + } + + return 0; +} + int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 8dc5b37510bb..cd0147f732bc 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -94,6 +94,7 @@ #define HC_ID_MEM_BASE 0x40UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) +#define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) /* PCI assignment*/ #define HC_ID_PCI_BASE 0x50UL @@ -149,6 +150,37 @@ struct vm_set_memmap { uint32_t prot; } __attribute__((aligned(8))); +struct memory_map { + uint32_t type; + + /* IN: mem attr */ + uint32_t prot; + + /* IN: beginning guest GPA to map */ + uint64_t remote_gpa; + + /* IN: VM0's GPA which foreign gpa will be mapped to */ + uint64_t vm0_gpa; + + /* IN: length of the range */ + uint64_t length; +} __attribute__((aligned(8))); + +struct set_memmaps { + /*IN: vmid for this hypercall */ + uint64_t vmid; + + /* IN: multi memmaps numbers */ + uint32_t memmaps_num; + + /* IN: + * the gpa of memmaps buffer, point to the memmaps array: + * struct memory_map memmap_array[memmaps_num] + * the max buffer size is one page. + */ + uint64_t memmaps_gpa; +} __attribute__((aligned(8))); + struct sbuf_setup_param { uint32_t pcpu_id; uint32_t sbuf_id; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 9be6749d12e2..712860b5f5af 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -62,6 +62,7 @@ #include #include +#include /** * acrn_hpa2gpa - physical address conversion @@ -189,7 +190,7 @@ void free_guest_mem(struct vhm_vm *vm); int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); /** - * map_guest_memseg - map EPT mmapping of memory of guest according to + * map_guest_memseg - set guest mmapping of memory according to * pre-defined memory mapping info * * @vm: pointer to guest vm @@ -207,4 +208,13 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); void hugepage_free_guest(struct vhm_vm *vm); void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); + +/** + * set_memmaps - set guest mapping for multi regions + * + * @memmaps: pointer to set_memmaps + * + * Return: 0 on success, <0 for error. + */ +int set_memmaps(struct set_memmaps *memmaps); #endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 38a9cb9e8487..ea4c3c2e416d 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -148,6 +148,7 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); +inline long hcall_set_memmaps(unsigned long pa_memmaps); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); From d071e1d3a964654d3140197a7ef134f54eb55fe7 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0858/1276] vhm: use set memmaps hypercall for hugetlb If hugetlb is using 2M pages, there may be too many memmap hypercall for ept mapping. To avoid such kind of performance drop, this patch enabled set memmaps hypercall for hugetlb to handle multi memmap hypercall in one time. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_hugetlb.c | 62 +++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index afab8ab52567..9c39f9167f77 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -120,10 +120,11 @@ static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct page *page; + struct page *page = NULL, *memmaps_buf_pg = NULL; unsigned long len, guest_gpa, vma; - unsigned int type; - unsigned int mem_type, mem_access_right; + struct memory_map *memmap_array; + struct set_memmaps memmaps; + int max_size = PAGE_SIZE/sizeof(struct memory_map); int ret; if (vm == NULL || memmap == NULL) @@ -133,13 +134,23 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) vma = memmap->vma_base; guest_gpa = memmap->gpa; + /* prepare set_memmaps info */ + memmaps_buf_pg = alloc_page(GFP_KERNEL); + if (memmaps_buf_pg == NULL) + return -ENOMEM; + memmaps.memmaps_num = 0; + memmaps.vmid = vm->vmid; + memmaps.memmaps_gpa = page_to_phys(memmaps_buf_pg); + memmap_array = page_to_virt(memmaps_buf_pg); + while (len > 0) { unsigned long vm0_gpa, pagesize; ret = get_user_pages_fast(vma, 1, 1, &page); if (unlikely(ret != 1) || (page == NULL)) { pr_err("failed to pin huge page!\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } vm0_gpa = page_to_phys(page); @@ -148,19 +159,27 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) ret = add_guest_map(vm, vm0_gpa, guest_gpa, pagesize); if (ret < 0) { pr_err("failed to add memseg for huge page!\n"); - put_page(page); - return ret; + goto err; } - /* TODO: do batch hypercall for multi ept mapping */ - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - put_page(page); - return -EFAULT; + /* fill each memmap region into memmap_array */ + memmap_array[memmaps.memmaps_num].type = MAP_MEM; + memmap_array[memmaps.memmaps_num].remote_gpa = guest_gpa; + memmap_array[memmaps.memmaps_num].vm0_gpa = vm0_gpa; + memmap_array[memmaps.memmaps_num].length = pagesize; + memmap_array[memmaps.memmaps_num].prot = + MEM_TYPE_WB & MEM_TYPE_MASK; + memmap_array[memmaps.memmaps_num].prot |= + memmap->prot & MEM_ACCESS_RIGHT_MASK; + memmaps.memmaps_num++; + if (memmaps.memmaps_num == max_size) { + pr_info("region buffer full, set & renew memmaps!\n"); + ret = set_memmaps(&memmaps); + if (ret < 0) { + pr_err("failed to set memmaps,ret=%d!\n", ret); + goto err; + } + memmaps.memmaps_num = 0; } len -= pagesize; @@ -168,9 +187,22 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) guest_gpa += pagesize; } + ret = set_memmaps(&memmaps); + if (ret < 0) { + pr_err("failed to set memmaps, ret=%d!\n", ret); + goto err; + } + + __free_page(memmaps_buf_pg); vm->hugetlb_enabled = 1; return 0; +err: + if (memmaps_buf_pg) + __free_page(memmaps_buf_pg); + if (page) + put_page(page); + return ret; } void hugepage_free_guest(struct vhm_vm *vm) From 2b95c6cfc93d149ad8e1d19bd5f91cec43fe47e3 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0859/1276] vhm: prepare future update for struct vm_set_memmap for back compatible, there is a uint32_t reserved field in struct vm_set_memmap, which will be removed in the future - finally change to struct set_memmap. this patch is preparing such change by change reserved field to prot , prot field to prot_2, and updating both prot & prot_2 during vm_set_memmap setting. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 2 +- include/linux/vhm/acrn_hv_defs.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 75ccd3f09a4e..fb09ed2f994f 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -167,7 +167,7 @@ int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, set_memmap.remote_gpa = guest_gpa; set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; - set_memmap.prot = ((mem_type & MEM_TYPE_MASK) | + set_memmap.prot = set_memmap.prot_2 = ((mem_type & MEM_TYPE_MASK) | (mem_access_right & MEM_ACCESS_RIGHT_MASK)); /* hypercall to notify hv the guest EPT setting*/ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index cd0147f732bc..d3af0970f7d2 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -135,7 +135,9 @@ struct vm_set_memmap { #define MAP_MMIO 1 #define MAP_UNMAP 2 uint32_t type; - uint32_t reserved; + + /* IN: mem attr */ + uint32_t prot; /* IN: beginning guest GPA to map */ uint64_t remote_gpa; @@ -146,8 +148,7 @@ struct vm_set_memmap { /* IN: length of the range */ uint64_t length; - /* IN: mem attr */ - uint32_t prot; + uint32_t prot_2; } __attribute__((aligned(8))); struct memory_map { From 58463e4159875157939ad7b0ed243abf9c04d215 Mon Sep 17 00:00:00 2001 From: "Zheng, Gen" Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0860/1276] VHM: bug fix on operating multi-thread synchronization With current code, the ioreq client based on VHM kthread may access client->wq after the client got freed. The acrn_ioreq_destroy_client_pervm should wait for the client thread exit then free its client. So do the following fixes: Make the client threads for vcpu and hyper-dma mark kthread_exit flag as true before exit. Make the task that triggered to destroy the client thread, explicitly waits for the kthread_exit flag turnning to true. Signed-off-by: Zheng, Gen Reviewed-by: Chen, Jason CJ Reviewed-by: Zhao, Yakui --- drivers/vhm/vhm_ioreq.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 08826c575780..b570b826be95 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -91,8 +91,8 @@ struct ioreq_client { */ bool fallback; - bool destroying; - bool kthread_exit; + volatile bool destroying; + volatile bool kthread_exit; /* client covered io ranges - N/A for fallback client */ struct list_head range_list; @@ -260,15 +260,15 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, struct list_head *pos, *tmp; unsigned long flags; - /* blocking operation: notify client for cleanup - * if waitqueue not active, it means client is handling request, - * at that time, we need wait client finish its handling. - */ - while (!waitqueue_active(&client->wq) && !client->kthread_exit) - msleep(10); client->destroying = true; acrn_ioreq_notify_client(client); + /* the client thread will mark kthread_exit flag as true before exit, + * so wait for it exited. + */ + while (!client->kthread_exit) + msleep(10); + spin_lock_irqsave(&client->range_lock, flags); list_for_each_safe(pos, tmp, &client->range_list) { struct ioreq_range *range = @@ -495,6 +495,10 @@ static int ioreq_client_thread(void *data) is_destroying(client))); } + /* the client thread such as for hyper-dma will exit from here, + * so mark kthread_exit as true before exit */ + client->kthread_exit = true; + return 0; } @@ -543,8 +547,12 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) is_destroying(client))); } - if (is_destroying(client)) + if (is_destroying(client)) { + /* the client thread for vcpu will exit from here, + * so mark kthread_exit as true before exit */ + client->kthread_exit = true; return 1; + } } return 0; From 8cc8e425dc48417e92769d48a854179d020badfe Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0861/1276] vhm: add hypercall to set sstate data The host ACPI State data which needed by S3/S5 implementation will be parsed in userspace, add one hypercall api to pass these information to Hypervisor. Signed-off-by: Victor Sun Reviewed-by: Tian, Kevin Reviewed-by: Zhao, Yakui --- drivers/char/vhm/vhm_dev.c | 13 +++++++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_common.h | 17 +++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 6 files changed, 38 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index fe8d16df129f..92075def6218 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -178,6 +178,19 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; return 0; + } else if (ioctl_num == IC_PM_SET_SSTATE_DATA) { + struct acpi_sstate_data host_sstate_data; + + if (copy_from_user(&host_sstate_data, + (void *)ioctl_param, sizeof(host_sstate_data))) + return -EFAULT; + + ret = hcall_set_sstate_data(virt_to_phys(&host_sstate_data)); + if (ret < 0) { + pr_err("vhm: failed to set host Sstate data!"); + return -EFAULT; + } + return 0; } memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index bbdbea8d623c..93dfb661d133 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -87,6 +87,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_set_sstate_data(unsigned long sx_data_addr) +{ + return acrn_hypercall1(HC_PM_SET_SSTATE_DATA, sx_data_addr); +} + inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) { return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 00088dcc8d7d..d45b27f55451 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -336,6 +336,23 @@ struct cpu_px_data { uint64_t status; /* success indicator */ } __attribute__((aligned(8))); +struct acpi_sstate_pkg { + uint8_t val_pm1a; + uint8_t val_pm1b; + uint16_t reserved; +} __attribute__((aligned(8))); + +struct acpi_sstate_data { + struct acpi_generic_address pm1a_evt; + struct acpi_generic_address pm1b_evt; + struct acpi_generic_address pm1a_cnt; + struct acpi_generic_address pm1b_cnt; + struct acpi_sstate_pkg s3_pkg; + struct acpi_sstate_pkg s5_pkg; + uint32_t *wake_vector_32; + uint64_t *wake_vector_64; +}__attribute__((aligned(8))); + /** * @brief Info PM command from DM/VHM. * diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d3af0970f7d2..7cfcf7a30813 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -111,6 +111,7 @@ /* Power management */ #define HC_ID_PM_BASE 0x80UL #define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) +#define HC_PM_SET_SSTATE_DATA _HC_ID(HC_ID, HC_ID_PM_BASE + 0x01) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index ea4c3c2e416d..49e94ecc37b9 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -145,6 +145,7 @@ inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_restart_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 028096bde2ec..a9e71616cc80 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -105,6 +105,7 @@ /* Power management */ #define IC_ID_PM_BASE 0x60UL #define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) +#define IC_PM_SET_SSTATE_DATA _IC_ID(IC_ID, IC_ID_PM_BASE + 0x01) /** * struct vm_memseg - memory segment info for guest From 67c0498acc6fbb8e1e34a6608bd9fb32077b4784 Mon Sep 17 00:00:00 2001 From: Xiangyang Wu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0862/1276] VHM:Update cpu id type as uint16_t for struct acrn_create_vcpu Update the cpu id type as uint16_t for struct acrn_create_vcpu in the VHM driver, this structure is for data transfering between the hypervisor and device modle in SOS. Change-Id: I6bfb67cc25d12f24dbc423ea1a0b91d876c9812e Tracked-On: Signed-off-by: Xiangyang Wu --- include/linux/vhm/acrn_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index d45b27f55451..7482320a36a8 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -178,10 +178,10 @@ struct acrn_create_vm { */ struct acrn_create_vcpu { /** the virtual CPU ID for the VCPU created */ - uint32_t vcpu_id; + uint16_t vcpu_id; /** the physical CPU ID for the VCPU created */ - uint32_t pcpu_id; + uint16_t pcpu_id; } __attribute__((aligned(8))); /** From 089345c87e1b65170f530065548b5a4eafcf0ee3 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0863/1276] vhm: add sos offline cpu support add sysfs with attr "offline_cpu", use echo cpu_id > /sys/class/vhm/acrn_vhm/offline_cpu to do the hypercall offline/destroy according vcpu. before doing it, please make sure you already did cpu offline with standard flow like below: echo 0 > /sys/devices/system/cpu/cpuX/online Signed-off-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 41 +++++++++++++++++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 ++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + 4 files changed, 48 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 92075def6218..75cdbb730fa9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -664,6 +664,41 @@ static const struct file_operations fops = { .poll = vhm_dev_poll, }; +static ssize_t +store_offline_cpu(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef CONFIG_X86 + u64 cpu, lapicid; + + if (kstrtoull(buf, 0, &cpu) < 0) + return -EINVAL; + + if (cpu_possible(cpu)) { + lapicid = cpu_data(cpu).apicid; + pr_info("vhm: try to offline cpu %lld with lapicid %lld\n", + cpu, lapicid); + if (hcall_sos_offline_cpu(lapicid) < 0) { + pr_err("vhm: failed to offline cpu from Hypervisor!\n"); + return -EINVAL; + } + } +#endif + return count; +} + +static DEVICE_ATTR(offline_cpu, S_IWUSR, NULL, store_offline_cpu); + +static struct attribute *vhm_attrs[] = { + &dev_attr_offline_cpu.attr, + NULL +}; + +static struct attribute_group vhm_attr_group = { + .attrs = vhm_attrs, +}; + #define SUPPORT_HV_API_VERSION_MAJOR 1 #define SUPPORT_HV_API_VERSION_MINOR 0 static int __init vhm_init(void) @@ -727,6 +762,11 @@ static int __init vhm_init(void) x86_platform_ipi_callback = vhm_intr_handler; local_irq_restore(flag); + if (sysfs_create_group(&vhm_device->kobj, &vhm_attr_group)) { + pr_warn("vhm: sysfs create failed\n"); + return -EINVAL; + } + pr_info("vhm: Virtio & Hypervisor service module initialized\n"); return 0; } @@ -737,6 +777,7 @@ static void __exit vhm_exit(void) class_unregister(vhm_class); class_destroy(vhm_class); unregister_chrdev(major, DEVICE_NAME); + sysfs_remove_group(&vhm_device->kobj, &vhm_attr_group); pr_info("vhm: exit\n"); } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 93dfb661d133..4c94d8f962ad 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -52,6 +52,11 @@ #include #include +inline long hcall_sos_offline_cpu(unsigned long cpu) +{ + return acrn_hypercall1(HC_SOS_OFFLINE_CPU, cpu); +} + inline long hcall_get_api_version(unsigned long api_version) { return acrn_hypercall1(HC_GET_API_VERSION, api_version); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 7cfcf7a30813..b9465a87fe46 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -68,6 +68,7 @@ /* general */ #define HC_ID_GEN_BASE 0x0UL #define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x00) +#define HC_SOS_OFFLINE_CPU _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x01) /* VM management */ #define HC_ID_VM_BASE 0x10UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 49e94ecc37b9..7d4b15af39a0 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -137,6 +137,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, return result; } +inline long hcall_sos_offline_cpu(unsigned long cpu); inline long hcall_get_api_version(unsigned long api_version); inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); From 9030d44a304f0f1850fc20d0b66f0fccc4cdb83c Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0864/1276] vhm: Fix kernel-doc issues Some comments are not in kernel-doc format so got error like: include/linux/vhm/vhm_vm_mngt.h:128: error: Cannot parse struct or union! Some are typo or not updated,eg: include/linux/vhm/acrn_vhm_mm.h:93: warning: Excess function parameter 'uos_phy' description in 'map_guest_phys' V2: More typo fix and re-wording on Geoffroy's suggestion V1: Fixed kernel-doc format issue Signed-off-by: Xinyun Liu Reviewed-by: Geoffroy Van Cutsem Reviewed-by: Eddie Dong --- include/linux/vhm/acrn_vhm_mm.h | 12 +++++------- include/linux/vhm/vhm_ioctl_defs.h | 10 +++++----- include/linux/vhm/vhm_vm_mngt.h | 16 ++++++++-------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 712860b5f5af..87d668f735dc 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -80,12 +80,10 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) } /** - * map_guest_phys - map guest physical address - * - * to SOS kernel virtual address + * map_guest_phys - map guest physical address to SOS kernel virtual address * * @vmid: guest vmid - * @uos_phy: phsical address in guest + * @uos_phys: physical address in guest * @size: the memory size mapped * * Return: SOS kernel virtual address, NULL on error @@ -96,7 +94,7 @@ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); * unmap_guest_phys - unmap guest physical address * * @vmid: guest vmid - * @uos_phy: phsical address in guest + * @uos_phys: physical address in guest * * Return: 0 on success, <0 for error. */ @@ -109,7 +107,7 @@ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); * @guest_gpa: gpa of UOS * @host_gpa: gpa of SOS * @len: memory mapped length - * @mem_type: memory mapping type. Possilble value could be: + * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT * MEM_TYPE_UC @@ -147,7 +145,7 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, * @guest_gpa: gpa of UOS * @host_gpa: gpa of SOS * @len: memory mapped length - * @mem_type: memory mapping type. Possilble value could be: + * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT * MEM_TYPE_UC diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index a9e71616cc80..9a7189ffffc4 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -52,11 +52,11 @@ #ifndef _VHM_IOCTL_DEFS_H_ #define _VHM_IOCTL_DEFS_H_ -/* Commmon structures for ACRN/VHM/DM */ +/* Common structures for ACRN/VHM/DM */ #include "acrn_common.h" /* - * Commmon IOCTL ID defination for VHM/DM + * Common IOCTL ID definition for VHM/DM */ #define _IC_ID(x, y) (((x)<<24)|(y)) #define IC_ID 0x43UL @@ -162,7 +162,7 @@ struct ic_ptdev_irq { uint32_t type; /** @virt_bdf: virtual bdf description of pass thru device */ uint16_t virt_bdf; /* IN: Device virtual BDF# */ - /** @phy_bdf: physical bdf description of pass thru device */ + /** @phys_bdf: physical bdf description of pass thru device */ uint16_t phys_bdf; /* IN: Device physical BDF# */ /** union */ union { @@ -172,7 +172,7 @@ struct ic_ptdev_irq { uint32_t virt_pin; /** @phys_pin: physical IOAPIC pin */ uint32_t phys_pin; - /** @pic_pin: PIC pin */ + /** @is_pic_pin: PIC pin */ uint32_t is_pic_pin; } intx; @@ -192,7 +192,7 @@ struct ic_ptdev_irq { }; /** - * struct ioreq_notify - data strcture to notify hypervisor ioreq is handled + * struct ioreq_notify - data structure to notify hypervisor ioreq is handled * * @client_id: client id to identify ioreq client * @vcpu: identify the ioreq submitter diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index f0a7e1cf7b05..14e9fe7a4d5f 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -88,6 +88,7 @@ extern struct mutex vhm_vm_list_lock; * @ioreq_client_list: list of ioreq clients * @req_buf: request buffer shared between HV, SOS and UOS * @pg: pointer to linux page which holds req_buf + * @hugetlb_enabled: flag to enable/disable hugetlb page ept mapping */ struct vhm_vm { struct device *dev; @@ -119,7 +120,7 @@ struct vm_info { }; /** - * struct find_get_vm - find and hold vhm_vm of guest according to guest vmid + * find_get_vm() - find and keep guest vhm_vm based on the vmid * * @vmid: guest vmid * @@ -128,17 +129,16 @@ struct vm_info { struct vhm_vm *find_get_vm(unsigned long vmid); /** - * struct put_vm - release vhm_vm of guest according to guest vmid + * put_vm() - release vhm_vm of guest according to guest vmid * If the latest reference count drops to zero, free vhm_vm as well - * - * @vm: pointer to vhm_vm which identrify specific guest + * @vm: pointer to vhm_vm which identify specific guest * * Return: */ void put_vm(struct vhm_vm *vm); /** - * struct vhm_get_vm_info - get vm_info of specific guest + * vhm_get_vm_info() - get vm_info of specific guest * * @vmid: guest vmid * @info: pointer to vm_info for returned vm_info @@ -148,7 +148,7 @@ void put_vm(struct vhm_vm *vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); /** - * struct vhm_inject_msi - inject MSI interrupt to guest + * vhm_inject_msi() - inject MSI interrupt to guest * * @vmid: guest vmid * @msi_addr: MSI addr matches MSI spec @@ -160,11 +160,11 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); /** - * struct vhm_vm_gpa2hpa - convert guest physical address to + * vhm_vm_gpa2hpa() - convert guest physical address to * host physical address * * @vmid: guest vmid - * @gap: guest physical address + * @gpa: guest physical address * * Return: host physical address, <0 on error */ From e67c2c11f1e732d96b0f180b56526a4cbd00b311 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0865/1276] vhm: add trusty init/de-init support vhm will allocate trusty memory from cma then do ept map for a VM with trusty. vhm will de-init trusty for a VM during its destroying. Signed-off-by: Mingqiang Chi Signed-off-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 10 ++++++++++ drivers/vhm/vhm_mm.c | 25 +++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 3 +++ include/linux/vhm/vhm_vm_mngt.h | 1 + 4 files changed, 39 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 75cdbb730fa9..964aee295cda 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -226,6 +226,14 @@ static long vhm_dev_ioctl(struct file *filep, vm->vmid = created_vm.vmid; + if (created_vm.vm_flag & SECURE_WORLD_ENABLED) { + ret = init_trusty(vm); + if (ret < 0) { + pr_err("vhm: failed to init trusty for VM!\n"); + return ret; + } + } + pr_info("vhm: VM %d created\n", created_vm.vmid); break; } @@ -258,6 +266,8 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { + if (vm->trusty_host_gpa) + deinit_trusty(vm); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index fb09ed2f994f..bff448208836 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -305,6 +305,31 @@ int check_guest_mem(struct vhm_vm *vm) return 0; } +#define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) +#define TRUSTY_MEM_SIZE (0x01000000) +int init_trusty(struct vhm_vm *vm) +{ + unsigned long host_gpa, guest_gpa = TRUSTY_MEM_GPA_BASE; + unsigned long len = TRUSTY_MEM_SIZE; + + host_gpa = _alloc_memblk(vm->dev, TRUSTY_MEM_SIZE); + if (host_gpa == 0ULL) + return -ENOMEM; + + vm->trusty_host_gpa = host_gpa; + + pr_info("VHM: set ept for trusty memory [host_gpa=0x%lx, " + "guest_gpa=0x%lx, len=0x%lx]", host_gpa, guest_gpa, len); + return _mem_set_memmap(vm->vmid, guest_gpa, host_gpa, len, + MEM_TYPE_WB, MEM_ACCESS_RWX, MAP_MEM); +} + +void deinit_trusty(struct vhm_vm *vm) +{ + _free_memblk(vm->dev, vm->trusty_host_gpa, TRUSTY_MEM_SIZE); + vm->trusty_host_gpa = 0; +} + static void guest_vm_open(struct vm_area_struct *vma) { struct vhm_vm *vm = vma->vm_file->private_data; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 87d668f735dc..5ff9af92f81f 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -198,6 +198,9 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +int init_trusty(struct vhm_vm *vm); +void deinit_trusty(struct vhm_vm *vm); + int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 14e9fe7a4d5f..b5f6ca1f2dea 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -94,6 +94,7 @@ struct vhm_vm { struct device *dev; struct list_head list; unsigned long vmid; + unsigned long trusty_host_gpa; int ioreq_fallback_client; long refcnt; struct mutex seg_lock; From 6be3ba8a282e39b9fb9a5f3e707bc7ad2a1848c1 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0866/1276] vhm: Rename the restart_vm to reset_vm Signed-off-by: Yin Fengwei Reviewed-by: Zhao Yakui Reviewed-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 4 ++-- drivers/vhm/vhm_hypercall.c | 4 ++-- include/linux/vhm/acrn_hv_defs.h | 2 +- include/linux/vhm/vhm_hypercall.h | 2 +- include/linux/vhm/vhm_ioctl_defs.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 964aee295cda..a67ba6c589f9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -256,8 +256,8 @@ static long vhm_dev_ioctl(struct file *filep, break; } - case IC_RESTART_VM: { - ret = hcall_restart_vm(vm->vmid); + case IC_RESET_VM: { + ret = hcall_reset_vm(vm->vmid); if (ret < 0) { pr_err("vhm: failed to restart VM %ld!\n", vm->vmid); return -EFAULT; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 4c94d8f962ad..2c51c366c60f 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -77,9 +77,9 @@ inline long hcall_pause_vm(unsigned long vmid) return acrn_hypercall1(HC_PAUSE_VM, vmid); } -inline long hcall_restart_vm(unsigned long vmid) +inline long hcall_reset_vm(unsigned long vmid) { - return acrn_hypercall1(HC_RESTART_VM, vmid); + return acrn_hypercall1(HC_RESET_VM, vmid); } inline long hcall_destroy_vm(unsigned long vmid) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index b9465a87fe46..f51f56b58147 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -77,7 +77,7 @@ #define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_RESTART_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_RESET_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 7d4b15af39a0..4de5e46b9d0f 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -143,7 +143,7 @@ inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); -inline long hcall_restart_vm(unsigned long vmid); +inline long hcall_reset_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_sstate_data(unsigned long sx_data_addr); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 9a7189ffffc4..ec560621ca73 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -72,7 +72,7 @@ #define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_RESTART_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_RESET_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 07092ac30edbc9b5db144a83e198cee72727e08f Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0867/1276] vhm: fix kerneldoc format remove doxygen commands and add missing description Signed-off-by: Xinyun Liu Reviewed-by: Mingqiang Chi Acked-by: Yakui Zhao --- include/linux/vhm/acrn_vhm_ioreq.h | 4 +--- include/linux/vhm/acrn_vhm_mm.h | 4 +--- include/linux/vhm/vhm_vm_mngt.h | 5 ++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index de3a8aa4eaf6..fbf69b37d356 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -52,9 +52,7 @@ */ /** - * @file acrn_vhm_ioreq.h - * - * @brief Virtio and Hypervisor Module(VHM) ioreq APIs + * DOC: Virtio and Hypervisor Module(VHM) ioreq APIs */ #ifndef __ACRN_VHM_IOREQ_H__ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 5ff9af92f81f..21269e47b26a 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -52,9 +52,7 @@ */ /** - * @file acrn_vhm_mm.h - * - * @brief Virtio and Hypervisor Module memory manager APIs + * DOC: Virtio and Hypervisor Module memory manager APIs */ #ifndef __ACRN_VHM_MM_H__ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index b5f6ca1f2dea..e89b78c7fff5 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -55,9 +55,7 @@ */ /** - * @file vhm_vm_mngt.h - * - * @brief Virtio and Hypervisor Module(VHM) management APIs + * DOC: brief Virtio and Hypervisor Module(VHM) management APIs */ #ifndef VHM_VM_MNGT_H #define VHM_VM_MNGT_H @@ -77,6 +75,7 @@ extern struct mutex vhm_vm_list_lock; * @dev: pointer to dev of linux device mode * @list: list of vhm_vm * @vmid: guest vmid + * @trusty_host_gpa: host physical address of continuous memory for Trusty * @ioreq_fallback_client: default ioreq client * @refcnt: reference count of guest * @seg_lock: mutex to protect memseg_list From c7f955f290b29fe1142b5c96b2ed24229ac84f7d Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0868/1276] sos: vhm: remove set guest memory map by CMA We removed CMA Device Manager memory allocation mechanisms and use hugetlb as the only Device Manager memory allocation mechanism. So there is no needs to support set guest vm memory by CMA any more. Signed-off-by: Li, Fei1 Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 14 -- drivers/vhm/vhm_mm.c | 252 ++--------------------------- include/linux/vhm/acrn_vhm_mm.h | 13 -- include/linux/vhm/vhm_ioctl_defs.h | 1 + include/linux/vhm/vhm_vm_mngt.h | 4 - 5 files changed, 15 insertions(+), 269 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index a67ba6c589f9..3dca3b3679a2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -122,9 +122,6 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm->vmid = ACRN_INVALID_VMID; vm->dev = vhm_device; - INIT_LIST_HEAD(&vm->memseg_list); - mutex_init(&vm->seg_lock); - for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) INIT_HLIST_HEAD(&vm->hugepage_hlist[i]); mutex_init(&vm->hugepage_lock); @@ -294,16 +291,6 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } - case IC_ALLOC_MEMSEG: { - struct vm_memseg memseg; - - if (copy_from_user(&memseg, (void *)ioctl_param, - sizeof(struct vm_memseg))) - return -EFAULT; - - return alloc_guest_memseg(vm, &memseg); - } - case IC_SET_MEMSEG: { struct vm_memmap memmap; @@ -668,7 +655,6 @@ static const struct file_operations fops = { .open = vhm_dev_open, .read = vhm_dev_read, .write = vhm_dev_write, - .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, .poll = vhm_dev_poll, diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index bff448208836..3c0c2acbe522 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -76,14 +76,6 @@ #include #include -struct guest_memseg { - struct list_head list; - u64 vm0_gpa; - size_t len; - u64 gpa; - long vma_count; -}; - static u64 _alloc_memblk(struct device *dev, size_t len) { unsigned int count; @@ -110,52 +102,6 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -static int add_guest_memseg(struct vhm_vm *vm, unsigned long vm0_gpa, - unsigned long guest_gpa, unsigned long len) -{ - struct guest_memseg *seg; - int max_gfn; - - seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); - if (seg == NULL) - return -ENOMEM; - - seg->vm0_gpa = vm0_gpa; - seg->gpa = guest_gpa; - seg->len = len; - - max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; - if (vm->max_gfn < max_gfn) - vm->max_gfn = max_gfn; - - pr_info("VHM: add memseg with len=0x%lx, vm0_gpa=0x%llx," - " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", - seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); - - seg->vma_count = 0; - mutex_lock(&vm->seg_lock); - list_add(&seg->list, &vm->memseg_list); - mutex_unlock(&vm->seg_lock); - - return 0; -} - -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) -{ - unsigned long vm0_gpa; - int ret; - - vm0_gpa = _alloc_memblk(vm->dev, memseg->len); - if (vm0_gpa == 0ULL) - return -ENOMEM; - - ret = add_guest_memseg(vm, vm0_gpa, memseg->gpa, memseg->len); - if (ret < 0) - _free_memblk(vm->dev, vm0_gpa, memseg->len); - - return ret; -} - int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, @@ -223,7 +169,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct guest_memseg *seg = NULL; unsigned int type; unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; @@ -232,77 +177,31 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); - mutex_lock(&vm->seg_lock); - - /* cma or mmio */ - if (memmap->type == VM_MEMMAP_SYSMEM) { - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa == memmap->gpa - && seg->len == memmap->len) - break; - } - if (&seg->list == &vm->memseg_list) { - mutex_unlock(&vm->seg_lock); - return -EINVAL; - } - guest_gpa = seg->gpa; - host_gpa = seg->vm0_gpa; - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - } else { - guest_gpa = memmap->gpa; - host_gpa = acrn_hpa2gpa(memmap->hpa); - mem_type = MEM_TYPE_UC; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MMIO; + /* mmio */ + if (memmap->type != VM_MEMMAP_MMIO) { + pr_err("vhm: %s invalid memmap type: %d\n", + __func__, memmap->type); + return -EINVAL; } + guest_gpa = memmap->gpa; + host_gpa = acrn_hpa2gpa(memmap->hpa); + mem_type = MEM_TYPE_UC; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MMIO; if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, mem_type, mem_access_right, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - mutex_unlock(&vm->seg_lock); return -EFAULT; } - mutex_unlock(&vm->seg_lock); - return 0; } void free_guest_mem(struct vhm_vm *vm) { - struct guest_memseg *seg; - if (vm->hugetlb_enabled) return hugepage_free_guest(vm); - - mutex_lock(&vm->seg_lock); - while (!list_empty(&vm->memseg_list)) { - seg = list_first_entry(&vm->memseg_list, - struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); - list_del(&seg->list); - kfree(seg); - } - mutex_unlock(&vm->seg_lock); -} - -int check_guest_mem(struct vhm_vm *vm) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->vma_count == 0) - continue; - - mutex_unlock(&vm->seg_lock); - return -EAGAIN; - } - mutex_unlock(&vm->seg_lock); - return 0; } #define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) @@ -330,121 +229,17 @@ void deinit_trusty(struct vhm_vm *vm) vm->trusty_host_gpa = 0; } -static void guest_vm_open(struct vm_area_struct *vma) -{ - struct vhm_vm *vm = vma->vm_file->private_data; - struct guest_memseg *seg = vma->vm_private_data; - - mutex_lock(&vm->seg_lock); - seg->vma_count++; - mutex_unlock(&vm->seg_lock); -} - -static void guest_vm_close(struct vm_area_struct *vma) -{ - struct vhm_vm *vm = vma->vm_file->private_data; - struct guest_memseg *seg = vma->vm_private_data; - - mutex_lock(&vm->seg_lock); - seg->vma_count--; - BUG_ON(seg->vma_count < 0); - mutex_unlock(&vm->seg_lock); -} - -static const struct vm_operations_struct guest_vm_ops = { - .open = guest_vm_open, - .close = guest_vm_close, -}; - -static int do_mmap_guest(struct file *file, - struct vm_area_struct *vma, struct guest_memseg *seg) -{ - struct page *page; - size_t size = seg->len; - unsigned long pfn; - unsigned long start_addr; - - vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; - pfn = seg->vm0_gpa >> PAGE_SHIFT; - start_addr = vma->vm_start; - while (size > 0) { - page = pfn_to_page(pfn); - if (vm_insert_page(vma, start_addr, page)) - return -EINVAL; - size -= PAGE_SIZE; - start_addr += PAGE_SIZE; - pfn++; - } - seg->vma_count++; - vma->vm_ops = &guest_vm_ops; - vma->vm_private_data = (void *)seg; - - pr_info("VHM: mmap for memseg [seg vm0_gpa=0x%llx, gpa=0x%llx] " - "to start addr 0x%lx\n", - seg->vm0_gpa, seg->gpa, start_addr); - - return 0; -} - -int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct vhm_vm *vm = file->private_data; - struct guest_memseg *seg; - u64 offset = vma->vm_pgoff << PAGE_SHIFT; - size_t len = vma->vm_end - vma->vm_start; - int ret; - - if (vm->hugetlb_enabled) - return -EINVAL; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa != offset || seg->len != len) - continue; - - ret = do_mmap_guest(file, vma, seg); - mutex_unlock(&vm->seg_lock); - return ret; - } - mutex_unlock(&vm->seg_lock); - return -EINVAL; -} - -static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa > guest_phys || - guest_phys >= seg->gpa + seg->len) - continue; - - if (guest_phys + size > seg->gpa + seg->len) { - mutex_unlock(&vm->seg_lock); - return NULL; - } - - mutex_unlock(&vm->seg_lock); - return phys_to_virt(seg->vm0_gpa + guest_phys - seg->gpa); - } - mutex_unlock(&vm->seg_lock); - return NULL; -} - void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) { struct vhm_vm *vm; - void *ret; + void *ret = NULL; vm = find_get_vm(vmid); if (vm == NULL) - return NULL; + return ret; if (vm->hugetlb_enabled) ret = hugepage_map_guest_phys(vm, guest_phys, size); - else - ret = do_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -452,38 +247,19 @@ void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) } EXPORT_SYMBOL(map_guest_phys); -static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa <= guest_phys && - guest_phys < seg->gpa + seg->len) { - mutex_unlock(&vm->seg_lock); - return 0; - } - } - mutex_unlock(&vm->seg_lock); - - return -ESRCH; -} - int unmap_guest_phys(unsigned long vmid, u64 guest_phys) { struct vhm_vm *vm; - int ret; + int ret = -ESRCH; vm = find_get_vm(vmid); if (vm == NULL) { pr_warn("vm_list corrupted\n"); - return -ESRCH; + return ret; } if (vm->hugetlb_enabled) ret = hugepage_unmap_guest_phys(vm, guest_phys); - else - ret = do_unmap_guest_phys(vm, guest_phys); put_vm(vm); return ret; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 21269e47b26a..645a8a56531e 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -163,8 +163,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); -int check_guest_mem(struct vhm_vm *vm); - /** * free_guest_mem - free memory of guest * @@ -174,17 +172,6 @@ int check_guest_mem(struct vhm_vm *vm); */ void free_guest_mem(struct vhm_vm *vm); -/** - * alloc_guest_memseg - alloc memory of guest according to pre-defined - * memory segment info - * - * @vm: pointer to guest vm - * @memseg: pointer to guest memory segment info - * - * Return: - */ -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); - /** * map_guest_memseg - set guest mmapping of memory according to * pre-defined memory mapping info diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index ec560621ca73..cf4f63211aa2 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -91,6 +91,7 @@ /* Guest memory management */ #define IC_ID_MEM_BASE 0x40UL +/* IC_ALLOC_MEMSEG not used */ #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index e89b78c7fff5..91cd13dad69a 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -78,8 +78,6 @@ extern struct mutex vhm_vm_list_lock; * @trusty_host_gpa: host physical address of continuous memory for Trusty * @ioreq_fallback_client: default ioreq client * @refcnt: reference count of guest - * @seg_lock: mutex to protect memseg_list - * @memseg_list: list of memseg * @hugepage_lock: mutex to protect hugepage_hlist * @hugepage_hlist: hash list of hugepage * @max_gfn: maximum guest page frame number @@ -96,8 +94,6 @@ struct vhm_vm { unsigned long trusty_host_gpa; int ioreq_fallback_client; long refcnt; - struct mutex seg_lock; - struct list_head memseg_list; struct mutex hugepage_lock; struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; int max_gfn; From e0ab4ac076793993af1300191452d95ea5af3999 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0869/1276] sos: vhm: remove hugetlb_enabled flag Since we only have hugetlb memory allocation mechanism, there no needs hugetlb_enabled to indicate we're using hugetlb. Signed-off-by: Li, Fei1 --- drivers/char/vhm/vhm_dev.c | 1 - drivers/vhm/vhm_hugetlb.c | 1 - drivers/vhm/vhm_mm.c | 17 +++++++---------- include/linux/vhm/vhm_vm_mngt.h | 2 -- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3dca3b3679a2..e454b9efcda2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -131,7 +131,6 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; - vm->hugetlb_enabled = 0; vm_list_add(&vm->list); vm_mutex_unlock(&vhm_vm_list_lock); filep->private_data = vm; diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index 9c39f9167f77..a83f00ad2e9d 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -194,7 +194,6 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) } __free_page(memmaps_buf_pg); - vm->hugetlb_enabled = 1; return 0; err: diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 3c0c2acbe522..c7ca10255064 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -200,8 +200,7 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) void free_guest_mem(struct vhm_vm *vm) { - if (vm->hugetlb_enabled) - return hugepage_free_guest(vm); + return hugepage_free_guest(vm); } #define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) @@ -232,14 +231,13 @@ void deinit_trusty(struct vhm_vm *vm) void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) { struct vhm_vm *vm; - void *ret = NULL; + void *ret; vm = find_get_vm(vmid); if (vm == NULL) - return ret; + return NULL; - if (vm->hugetlb_enabled) - ret = hugepage_map_guest_phys(vm, guest_phys, size); + ret = hugepage_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -250,16 +248,15 @@ EXPORT_SYMBOL(map_guest_phys); int unmap_guest_phys(unsigned long vmid, u64 guest_phys) { struct vhm_vm *vm; - int ret = -ESRCH; + int ret; vm = find_get_vm(vmid); if (vm == NULL) { pr_warn("vm_list corrupted\n"); - return ret; + return -ESRCH; } - if (vm->hugetlb_enabled) - ret = hugepage_unmap_guest_phys(vm, guest_phys); + ret = hugepage_unmap_guest_phys(vm, guest_phys); put_vm(vm); return ret; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 91cd13dad69a..29fee8fe0a7b 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -85,7 +85,6 @@ extern struct mutex vhm_vm_list_lock; * @ioreq_client_list: list of ioreq clients * @req_buf: request buffer shared between HV, SOS and UOS * @pg: pointer to linux page which holds req_buf - * @hugetlb_enabled: flag to enable/disable hugetlb page ept mapping */ struct vhm_vm { struct device *dev; @@ -101,7 +100,6 @@ struct vhm_vm { struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; - int hugetlb_enabled; }; /** From db8366a0f18f71a1855487453099341c784bcafc Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0870/1276] sos: vhm: remove MAP_MMIO Now the MAP_MMIO has no difference with MAP_MEM. So there's no needs to keep it. Signed-off-by: Li, Fei1 Acked-by: Eddie Dong --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/acrn_hv_defs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index c7ca10255064..f663558ae943 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -135,7 +135,7 @@ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned int mem_type, unsigned mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MMIO); + mem_type, mem_access_right, MAP_MEM); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, @@ -187,7 +187,7 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) host_gpa = acrn_hpa2gpa(memmap->hpa); mem_type = MEM_TYPE_UC; mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MMIO; + type = MAP_MEM; if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, mem_type, mem_access_right, type) < 0) { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f51f56b58147..31cdebefedf2 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -134,7 +134,6 @@ struct vm_set_memmap { #define MAP_MEM 0 -#define MAP_MMIO 1 #define MAP_UNMAP 2 uint32_t type; From 350982616f963f370c4b4e2b05695667205b8759 Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0871/1276] vhm: revisit types in structure parameters of hypercalls While fixing the MISRA C violations related to integral types, we have unified the type of the following data: uint8_t: phys_pin, virt_pin, vpic_pin, ioapic_pin, vioapic_pin uint16_t: vm_id, pcpu_id, vcpu_id, vpid uint32_t: vector, irq This patch revisits the types of the fields in vhm_request as well as the structures used as parameters in the hypercalls, and make them aligned with the types the hypervisor uses for such data. Reserved fields are added to keep the size and layout of the structures. Implicit paddings are also made explicit as reserved fields. This is the update on the VHM side in correspondance to the same changes in the hypervisor and device model. v1 -> v2: * Make reserved fields unsigned. * Combine continuous reserved fields using proper arrays. * Make msix_entry_index unsigned as it is used in this way in both the hypervisor and kernel. Signed-off-by: Junjie Mao Reviewed-by: Zhao Yakui --- include/linux/vhm/acrn_common.h | 54 ++++++++++++++++++++------------ include/linux/vhm/acrn_hv_defs.h | 17 +++++++--- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 7482320a36a8..91951b7e1fc3 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -87,17 +87,17 @@ struct mmio_request { uint32_t direction; uint32_t reserved; - int64_t address; - int64_t size; - int64_t value; + uint64_t address; + uint64_t size; + uint64_t value; } __attribute__((aligned(8))); struct pio_request { uint32_t direction; uint32_t reserved; - int64_t address; - int64_t size; - int32_t value; + uint64_t address; + uint64_t size; + uint32_t value; } __attribute__((aligned(8))); struct pci_request { @@ -114,16 +114,15 @@ struct pci_request { /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ - union { - uint32_t type; - int32_t reserved0[16]; - }; + uint32_t type; + uint32_t reserved0[15]; + /* offset: 64bytes-127bytes */ union { struct pio_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; - int64_t reserved1[8]; + uint64_t reserved1[8]; } reqs; /* True: valid req which need VHM to process. @@ -145,7 +144,7 @@ struct vhm_request { struct vhm_request_buffer { union { struct vhm_request req_queue[VHM_REQUEST_MAX]; - int8_t reserved[4096]; + uint8_t reserved[4096]; }; } __attribute__((aligned(4096))); @@ -154,10 +153,16 @@ struct vhm_request_buffer { */ struct acrn_create_vm { /** created vmid return to VHM. Keep it first field */ - int32_t vmid; + uint16_t vmid; + + /** Reserved */ + uint16_t reserved0; /** VCPU numbers this VM want to create */ - uint32_t vcpu_num; + uint16_t vcpu_num; + + /** Reserved */ + uint16_t reserved1; /** the GUID of this VM */ uint8_t GUID[16]; @@ -168,7 +173,7 @@ struct acrn_create_vm { uint64_t vm_flag; /** Reserved for future use*/ - uint8_t reserved[24]; + uint8_t reserved2[24]; } __attribute__((aligned(8))); /** @@ -214,12 +219,18 @@ struct acrn_irqline { uint32_t reserved; /** pic IRQ for ISA type */ - uint64_t pic_irq; + uint32_t pic_irq; + + /** Reserved */ + uint32_t reserved0; /** ioapic IRQ for IOAPIC & ISA TYPE, - * if -1 then this IRQ will not be injected + * if ~0U then this IRQ will not be injected */ - uint64_t ioapic_irq; + uint32_t ioapic_irq; + + /** Reserved */ + uint32_t reserved1; } __attribute__((aligned(8))); /** @@ -240,7 +251,10 @@ struct acrn_msi_entry { */ struct acrn_nmi_entry { /** virtual CPU ID to inject */ - int64_t vcpu_id; + uint16_t vcpu_id; + + /** Reserved */ + uint16_t reserved[3]; } __attribute__((aligned(8))); /** @@ -279,7 +293,7 @@ struct acrn_vm_pci_msix_remap { /** if the pass-through PCI device is MSI-X, this field contains * the MSI-X entry table index */ - int32_t msix_entry_index; + uint32_t msix_entry_index; /** if the pass-through PCI device is MSI-X, this field contains * Vector Control for MSI-X Entry, field defined in MSI-X spec diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 31cdebefedf2..67316fb49999 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -170,7 +170,10 @@ struct memory_map { struct set_memmaps { /*IN: vmid for this hypercall */ - uint64_t vmid; + uint16_t vmid; + + /** Reserved */ + uint16_t reserved[3]; /* IN: multi memmaps numbers */ uint32_t memmaps_num; @@ -184,7 +187,8 @@ struct set_memmaps { } __attribute__((aligned(8))); struct sbuf_setup_param { - uint32_t pcpu_id; + uint16_t pcpu_id; + uint16_t reserved; uint32_t sbuf_id; uint64_t gpa; } __attribute__((aligned(8))); @@ -203,9 +207,12 @@ struct hc_ptdev_irq { uint16_t phys_bdf; /* IN: Device physical BDF# */ union { struct { - uint32_t virt_pin; /* IN: virtual IOAPIC pin */ - uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t pic_pin; /* IN: pin from PIC? */ + uint8_t virt_pin; /* IN: virtual IOAPIC pin */ + uint8_t reserved0[3]; /* Reserved */ + uint8_t phys_pin; /* IN: physical IOAPIC pin */ + uint8_t reserved1[3]; /* Reserved */ + bool pic_pin; /* IN: pin from PIC? */ + uint8_t reserved2[3]; /* Reserved */ } intx; struct { /* IN: vector count of MSI/MSIX */ From 1ce8912464ce3c6665d22a17a51ea652b9105524 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0872/1276] sos: vhm: add hcall_write_protect_page hypercall 1. add write_protect_page to set or unset one page write protect 2. replace update_memmap_attr with write_protect_page to set or unset one page write protect 3. replace update_memmap_attr with set_mmio_map to add guest memory region Signed-off-by: Li, Fei1 --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_mm.c | 25 ++++++++++++++++++++----- include/linux/vhm/acrn_hv_defs.h | 14 ++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 23 +++++------------------ include/linux/vhm/vhm_hypercall.h | 2 ++ 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 2c51c366c60f..8611bb1819d4 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -112,6 +112,11 @@ inline long hcall_set_memmaps(unsigned long pa_memmaps) return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); } +inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp) +{ + return acrn_hypercall2(HC_VM_WRITE_PROTECT_PAGE, vmid, wp); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index f663558ae943..c7ca8e99612d 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -159,12 +159,27 @@ int set_memmaps(struct set_memmaps *memmaps) return 0; } -int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right) +/* + * when set is true, set page write protection, + * else clear page write protection. + */ +int write_protect_page(unsigned long vmid, + unsigned long gpa, unsigned char set) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MEM); + struct wp_data wp; + + wp.set = set; + wp.gpa = gpa; + + if (hcall_write_protect_page(vmid, + virt_to_phys(&wp)) < 0) { + pr_err("vhm: vm[%ld] %s failed !\n", vmid, __func__); + return -EFAULT; + } + + pr_debug("VHM: %s, gpa: 0x%lx, set: %d\n", __func__, gpa, set); + + return 0; } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 67316fb49999..74f8a137206d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -96,6 +96,7 @@ #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) #define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) +#define HC_VM_WRITE_PROTECT_PAGE _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x03) /* PCI assignment*/ #define HC_ID_PCI_BASE 0x50UL @@ -186,6 +187,19 @@ struct set_memmaps { uint64_t memmaps_gpa; } __attribute__((aligned(8))); +struct wp_data { + /** set page write protect permission. + * ture: set the wp; flase: clear the wp + */ + uint8_t set; + + /** Reserved */ + uint64_t pad:56; + + /** the guest physical address of the page to change */ + uint64_t gpa; +} __aligned(8); + struct sbuf_setup_param { uint16_t pcpu_id; uint16_t reserved; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 645a8a56531e..0769200ea3bf 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -137,29 +137,16 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len); /** - * update_memmap_attr - update mmio EPT mapping between UOS gpa and SOS gpa + * write_protect_page - change one page write protection * * @vmid: guest vmid - * @guest_gpa: gpa of UOS - * @host_gpa: gpa of SOS - * @len: memory mapped length - * @mem_type: memory mapping type. Possible value could be: - * MEM_TYPE_WB - * MEM_TYPE_WT - * MEM_TYPE_UC - * MEM_TYPE_WC - * MEM_TYPE_WP - * @mem_access_right: memory mapping access. Possible value could be: - * MEM_ACCESS_READ - * MEM_ACCESS_WRITE - * MEM_ACCESS_EXEC - * MEM_ACCESS_RWX + * @gpa: gpa in guest vmid + * @set: set or clear page write protection * * Return: 0 on success, <0 for error. */ -int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right); +int write_protect_page(unsigned long vmid, + unsigned long gpa, unsigned char set); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 4de5e46b9d0f..87a34fec8d90 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -151,6 +151,8 @@ inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_memmaps(unsigned long pa_memmaps); +inline long hcall_write_protect_page(unsigned long vmid, + unsigned long wp); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); From 31a53c58560daf49f75481f270284cc7715abd47 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0873/1276] sos: vhm: refine set memory region API 1. rename set_mmio_map to add_memory_region; unset_mmio_map to del_memory_region. 2. rename set_memmap to set_memory_region; set_memmaps to set_memory_region; 3. remove HV_VM_SET_MEMORY_REGION hypercall Signed-off-by: Li, Fei1 Acked-by: Eddie Dong --- drivers/vhm/vhm_hugetlb.c | 61 +++++++++++---------- drivers/vhm/vhm_hypercall.c | 9 +--- drivers/vhm/vhm_mm.c | 89 +++++++++++++++---------------- include/linux/vhm/acrn_hv_defs.h | 39 ++++---------- include/linux/vhm/acrn_vhm_mm.h | 31 +++++------ include/linux/vhm/vhm_hypercall.h | 4 +- 6 files changed, 98 insertions(+), 135 deletions(-) diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index a83f00ad2e9d..34ebbd90acea 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -120,11 +120,11 @@ static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct page *page = NULL, *memmaps_buf_pg = NULL; + struct page *page = NULL, *regions_buf_pg = NULL; unsigned long len, guest_gpa, vma; - struct memory_map *memmap_array; - struct set_memmaps memmaps; - int max_size = PAGE_SIZE/sizeof(struct memory_map); + struct vm_memory_region *region_array; + struct set_regions regions; + int max_size = PAGE_SIZE/sizeof(struct vm_memory_region); int ret; if (vm == NULL || memmap == NULL) @@ -134,14 +134,14 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) vma = memmap->vma_base; guest_gpa = memmap->gpa; - /* prepare set_memmaps info */ - memmaps_buf_pg = alloc_page(GFP_KERNEL); - if (memmaps_buf_pg == NULL) + /* prepare set_memory_regions info */ + regions_buf_pg = alloc_page(GFP_KERNEL); + if (regions_buf_pg == NULL) return -ENOMEM; - memmaps.memmaps_num = 0; - memmaps.vmid = vm->vmid; - memmaps.memmaps_gpa = page_to_phys(memmaps_buf_pg); - memmap_array = page_to_virt(memmaps_buf_pg); + regions.mr_num = 0; + regions.vmid = vm->vmid; + regions.regions_gpa = page_to_phys(regions_buf_pg); + region_array = page_to_virt(regions_buf_pg); while (len > 0) { unsigned long vm0_gpa, pagesize; @@ -162,24 +162,23 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) goto err; } - /* fill each memmap region into memmap_array */ - memmap_array[memmaps.memmaps_num].type = MAP_MEM; - memmap_array[memmaps.memmaps_num].remote_gpa = guest_gpa; - memmap_array[memmaps.memmaps_num].vm0_gpa = vm0_gpa; - memmap_array[memmaps.memmaps_num].length = pagesize; - memmap_array[memmaps.memmaps_num].prot = - MEM_TYPE_WB & MEM_TYPE_MASK; - memmap_array[memmaps.memmaps_num].prot |= - memmap->prot & MEM_ACCESS_RIGHT_MASK; - memmaps.memmaps_num++; - if (memmaps.memmaps_num == max_size) { - pr_info("region buffer full, set & renew memmaps!\n"); - ret = set_memmaps(&memmaps); + /* fill each memory region into region_array */ + region_array[regions.mr_num].type = MR_ADD; + region_array[regions.mr_num].gpa = guest_gpa; + region_array[regions.mr_num].vm0_gpa = vm0_gpa; + region_array[regions.mr_num].size = pagesize; + region_array[regions.mr_num].prot = + (MEM_TYPE_WB & MEM_TYPE_MASK) | + (memmap->prot & MEM_ACCESS_RIGHT_MASK); + regions.mr_num++; + if (regions.mr_num == max_size) { + pr_info("region buffer full, set & renew regions!\n"); + ret = set_memory_regions(®ions); if (ret < 0) { - pr_err("failed to set memmaps,ret=%d!\n", ret); + pr_err("failed to set regions,ret=%d!\n", ret); goto err; } - memmaps.memmaps_num = 0; + regions.mr_num = 0; } len -= pagesize; @@ -187,18 +186,18 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) guest_gpa += pagesize; } - ret = set_memmaps(&memmaps); + ret = set_memory_regions(®ions); if (ret < 0) { - pr_err("failed to set memmaps, ret=%d!\n", ret); + pr_err("failed to set regions, ret=%d!\n", ret); goto err; } - __free_page(memmaps_buf_pg); + __free_page(regions_buf_pg); return 0; err: - if (memmaps_buf_pg) - __free_page(memmaps_buf_pg); + if (regions_buf_pg) + __free_page(regions_buf_pg); if (page) put_page(page); return ret; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 8611bb1819d4..9a92d6888b90 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -102,14 +102,9 @@ inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); } -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +inline long hcall_set_memory_regions(unsigned long pa_regions) { - return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); -} - -inline long hcall_set_memmaps(unsigned long pa_memmaps) -{ - return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); + return acrn_hypercall1(HC_VM_SET_MEMORY_REGIONS, pa_regions); } inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index c7ca8e99612d..4d5854d0c139 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -102,56 +102,59 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right, - unsigned int type) +static int set_memory_region(unsigned long vmid, + struct vm_memory_region *region) { - struct vm_set_memmap set_memmap; + struct set_regions regions; - set_memmap.type = type; - set_memmap.remote_gpa = guest_gpa; - set_memmap.vm0_gpa = host_gpa; - set_memmap.length = len; - set_memmap.prot = set_memmap.prot_2 = ((mem_type & MEM_TYPE_MASK) | - (mem_access_right & MEM_ACCESS_RIGHT_MASK)); + regions.vmid = vmid; + regions.mr_num = 1; + regions.regions_gpa = virt_to_phys(region); - /* hypercall to notify hv the guest EPT setting*/ - if (hcall_set_memmap(vmid, - virt_to_phys(&set_memmap)) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vmid); + if (set_memory_regions(®ions) < 0) { + pr_err("vhm: failed to set memory region for vm[%ld]!\n", vmid); return -EFAULT; } - pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," - "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", - type, host_gpa, guest_gpa, len, set_memmap.prot); - return 0; } -int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, +int add_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long host_gpa, unsigned long size, unsigned int mem_type, unsigned mem_access_right) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MEM); + struct vm_memory_region region; + + region.type = MR_ADD; + region.gpa = gpa; + region.vm0_gpa = host_gpa; + region.size = size; + region.prot = ((mem_type & MEM_TYPE_MASK) | + (mem_access_right & MEM_ACCESS_RIGHT_MASK)); + return set_memory_region(vmid, ®ion); } -int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len) +int del_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long size) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - 0, 0, MAP_UNMAP); + struct vm_memory_region region; + + region.type = MR_DEL; + region.gpa = gpa; + region.vm0_gpa = 0; + region.size = size; + region.prot = 0; + + return set_memory_region(vmid, ®ion); } -int set_memmaps(struct set_memmaps *memmaps) +int set_memory_regions(struct set_regions *regions) { - if (memmaps == NULL) + if (regions == NULL) return -EINVAL; - if (memmaps->memmaps_num > 0) { - if (hcall_set_memmaps(virt_to_phys(memmaps)) < 0) { - pr_err("vhm: failed to set memmaps!\n"); + if (regions->mr_num > 0) { + if (hcall_set_memory_regions(virt_to_phys(regions)) < 0) { + pr_err("vhm: failed to set memory regions!\n"); return -EFAULT; } } @@ -184,10 +187,6 @@ int write_protect_page(unsigned long vmid, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { - unsigned int type; - unsigned int mem_type, mem_access_right; - unsigned long guest_gpa, host_gpa; - /* hugetlb use vma to do the mapping */ if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); @@ -198,15 +197,11 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) __func__, memmap->type); return -EINVAL; } - guest_gpa = memmap->gpa; - host_gpa = acrn_hpa2gpa(memmap->hpa); - mem_type = MEM_TYPE_UC; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - - if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + + if (add_memory_region(vm->vmid, memmap->gpa, + acrn_hpa2gpa(memmap->hpa), memmap->len, + MEM_TYPE_UC, memmap->prot) < 0){ + pr_err("vhm: failed to set memory region %ld!\n", vm->vmid); return -EFAULT; } @@ -233,8 +228,8 @@ int init_trusty(struct vhm_vm *vm) pr_info("VHM: set ept for trusty memory [host_gpa=0x%lx, " "guest_gpa=0x%lx, len=0x%lx]", host_gpa, guest_gpa, len); - return _mem_set_memmap(vm->vmid, guest_gpa, host_gpa, len, - MEM_TYPE_WB, MEM_ACCESS_RWX, MAP_MEM); + return add_memory_region(vm->vmid, guest_gpa, host_gpa, len, + MEM_TYPE_WB, MEM_ACCESS_RWX); } void deinit_trusty(struct vhm_vm *vm) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 74f8a137206d..223702c0330c 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -93,9 +93,8 @@ /* Guest memory management */ #define HC_ID_MEM_BASE 0x40UL -#define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) -#define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) +#define HC_VM_SET_MEMORY_REGIONS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) #define HC_VM_WRITE_PROTECT_PAGE _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x03) /* PCI assignment*/ @@ -133,43 +132,25 @@ #define MEM_TYPE_WP 0x00000400 #define MEM_TYPE_MASK 0x000007C0 -struct vm_set_memmap { -#define MAP_MEM 0 -#define MAP_UNMAP 2 +struct vm_memory_region { +#define MR_ADD 0 +#define MR_DEL 2 uint32_t type; /* IN: mem attr */ uint32_t prot; /* IN: beginning guest GPA to map */ - uint64_t remote_gpa; - - /* IN: VM0's GPA which foreign gpa will be mapped to */ - uint64_t vm0_gpa; - - /* IN: length of the range */ - uint64_t length; - - uint32_t prot_2; -} __attribute__((aligned(8))); - -struct memory_map { - uint32_t type; - - /* IN: mem attr */ - uint32_t prot; - - /* IN: beginning guest GPA to map */ - uint64_t remote_gpa; + uint64_t gpa; /* IN: VM0's GPA which foreign gpa will be mapped to */ uint64_t vm0_gpa; - /* IN: length of the range */ - uint64_t length; + /* IN: size of the region */ + uint64_t size; } __attribute__((aligned(8))); -struct set_memmaps { +struct set_regions { /*IN: vmid for this hypercall */ uint16_t vmid; @@ -177,14 +158,14 @@ struct set_memmaps { uint16_t reserved[3]; /* IN: multi memmaps numbers */ - uint32_t memmaps_num; + uint32_t mr_num; /* IN: * the gpa of memmaps buffer, point to the memmaps array: * struct memory_map memmap_array[memmaps_num] * the max buffer size is one page. */ - uint64_t memmaps_gpa; + uint64_t regions_gpa; } __attribute__((aligned(8))); struct wp_data { diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 0769200ea3bf..62aed3466e9f 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -99,12 +99,12 @@ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); /** - * set_mmio_map - map mmio EPT mapping between UOS gpa and SOS gpa + * add_memory_region - add a guest memory region * * @vmid: guest vmid - * @guest_gpa: gpa of UOS + * @gpa: gpa of UOS * @host_gpa: gpa of SOS - * @len: memory mapped length + * @size: memory region size * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT @@ -119,22 +119,21 @@ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); * * Return: 0 on success, <0 for error. */ -int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, +int add_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long host_gpa, unsigned long size, unsigned int mem_type, unsigned int mem_access_right); /** - * unset_mmio_map - unmap mmio mapping between UOS gpa and SOS gpa + * del_memory_region - delete a guest memory region * * @vmid: guest vmid - * @guest_gpa: gpa of UOS - * @host_gpa: gpa of SOS - * @len: memory mapped length + * @gpa: gpa of UOS + * @size: memory region size * * Return: 0 on success, <0 for error. */ -int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len); +int del_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long size); /** * write_protect_page - change one page write protection @@ -173,21 +172,17 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); int init_trusty(struct vhm_vm *vm); void deinit_trusty(struct vhm_vm *vm); -int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right, - unsigned int type); int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); void hugepage_free_guest(struct vhm_vm *vm); void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); /** - * set_memmaps - set guest mapping for multi regions + * set_memory_regions - set guest mapping for multi regions * - * @memmaps: pointer to set_memmaps + * @regions: pointer to set_regions * * Return: 0 on success, <0 for error. */ -int set_memmaps(struct set_memmaps *memmaps); +int set_memory_regions(struct set_regions *regions); #endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 87a34fec8d90..0a3869b356b8 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -148,9 +148,7 @@ inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); -inline long hcall_set_memmap(unsigned long vmid, - unsigned long memmap); -inline long hcall_set_memmaps(unsigned long pa_memmaps); +inline long hcall_set_memory_regions(unsigned long pa_regions); inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp); inline long hcall_set_ioreq_buffer(unsigned long vmid, From 96ddd327f08322129e37fdd185f92cbc4ad9dd30 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0874/1276] vhm: remove re-schedule for ioreq tasklet io_req_tasklet can process all existing or even incoming ioreqs in the ioreq shared page once. So when the ioreq IPI rising, we needn't rearm the tasklet if the previous one havn't got running. tasklet_schedule can gurantee to execute once after schedule, and all pending ioreqs can be processed once. tasklet_schedule also can rearm the running tasklet so no ioreq interrupt lost. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index e454b9efcda2..39204b5d7898 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -96,7 +96,6 @@ static int major; static struct class *vhm_class; static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; -static atomic_t ioreq_retry = ATOMIC_INIT(0); struct table_iomems { /* list node for this table_iomems */ @@ -622,19 +621,11 @@ static void io_req_tasklet(unsigned long data) acrn_ioreq_distribute_request(vm); } - - if (atomic_read(&ioreq_retry) > 0) { - atomic_dec(&ioreq_retry); - tasklet_schedule(&vhm_io_req_tasklet); - } } static void vhm_intr_handler(void) { - if (test_bit(TASKLET_STATE_SCHED, &(vhm_io_req_tasklet.state))) - atomic_inc(&ioreq_retry); - else - tasklet_schedule(&vhm_io_req_tasklet); + tasklet_schedule(&vhm_io_req_tasklet); } static int vhm_dev_release(struct inode *inodep, struct file *filep) From 273cd329584c21fd69d528f485c6ee2bd25ab473 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0875/1276] vhm: Add vcpu_num to record vcpu number of each VM Add a new field 'vcpu_num' in vhm_vm struct to count the number of vcpu of this VM. And uses atomic operation to avoid lock. There is no vcpu_num decrease as we don't have vcpu destroying. Signed-off-by: Shuo Liu Reviewed-by: Anthony Xu Reviewed-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 1 + drivers/vhm/vhm_ioreq.c | 6 +++--- include/linux/vhm/vhm_vm_mngt.h | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 39204b5d7898..5884fd2b259e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -285,6 +285,7 @@ static long vhm_dev_ioctl(struct file *filep, pr_err("vhm: failed to create vcpu %d!\n", cv.vcpu_id); return -EFAULT; } + atomic_inc(&vm->vcpu_num); return ret; } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index b570b826be95..bf55a0138943 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -783,10 +783,10 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) struct vhm_request *req; struct list_head *pos; struct ioreq_client *client; - int i; + int i, vcpu_num; - /* TODO: replace VHM_REQUEST_MAX with vcpu num get at runtime */ - for (i = 0; i < VHM_REQUEST_MAX; i++) { + vcpu_num = atomic_read(&vm->vcpu_num); + for (i = 0; i < vcpu_num; i++) { req = vm->req_buf->req_queue + i; if (req->valid && (req->processed == REQ_STATE_PENDING)) { if (handle_cf8cfc(vm, req, i)) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 29fee8fe0a7b..d3b26bdbc675 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -80,6 +80,7 @@ extern struct mutex vhm_vm_list_lock; * @refcnt: reference count of guest * @hugepage_lock: mutex to protect hugepage_hlist * @hugepage_hlist: hash list of hugepage + * @vcpu_num: vcpu number * @max_gfn: maximum guest page frame number * @ioreq_client_lock: spinlock to protect ioreq_client_list * @ioreq_client_list: list of ioreq clients @@ -95,6 +96,7 @@ struct vhm_vm { long refcnt; struct mutex hugepage_lock; struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; + atomic_t vcpu_num; int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; From cc6ba5be1b6e0c33fe48d924bc0214de40170f45 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0876/1276] vhm: mark pending ioreqs in bitmap then dispatch it to vhm client Currently, we are passing the req_count to vhm client which is useless. Clients need get the ioreq shared buffer and the vcpu number of this VM, then loop all the vcpu ioreq slots to find the ones to handle. The patch will record pending ioreqs of the vhm client into a bitmap, then vhm client can process the ioreq directly according to the bitmap. Signed-off-by: Shuo Liu Reviewed-by: Zhao Yakui Reviewed-by: Jason Chen CJ --- drivers/vbs/vbs.c | 16 ++++++++-------- drivers/vbs/vbs_rng.c | 8 ++++---- drivers/vhm/vhm_ioreq.c | 10 +++++----- include/linux/vbs/vbs.h | 6 +++--- include/linux/vhm/acrn_vhm_ioreq.h | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 6c364364db3c..1d2e1b40728e 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -145,22 +145,22 @@ long virtio_dev_deregister(struct virtio_dev_info *dev) return 0; } -int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) +int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) { int val = -1; struct vhm_request *req; - int i; - - if (unlikely(req_cnt <= 0)) - return -EINVAL; + int vcpu; if (dev == NULL) { pr_err("%s: dev is NULL!\n", __func__); return -EINVAL; } - for (i = 0; i < dev->_ctx.max_vcpu; i++) { - req = &dev->_ctx.req_buf[i]; + while (1) { + vcpu = find_first_bit(ioreqs_map, dev->_ctx.max_vcpu); + if (vcpu == dev->_ctx.max_vcpu) + break; + req = &dev->_ctx.req_buf[vcpu]; if (req->valid && req->processed == REQ_STATE_PROCESSING && req->client == dev->_ctx.vhm_client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) { @@ -181,7 +181,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) val = req->reqs.mmio_request.value; } req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(dev->_ctx.vhm_client_id, i); + acrn_ioreq_complete_request(req->client, vcpu); } } diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 88f6108ca2e8..569d1d5d689c 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -122,7 +122,7 @@ static int vbs_rng_hash_initialized = 0; static int vbs_rng_connection_cnt = 0; /* function declarations */ -static int handle_kick(int client_id, int req_cnt); +static int handle_kick(int client_id, unsigned long *ioreqs_map); static void vbs_rng_reset(struct vbs_rng *rng); static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush(struct vbs_rng *rng); @@ -251,12 +251,12 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) virtio_vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ } -static int handle_kick(int client_id, int req_cnt) +static int handle_kick(int client_id, unsigned long *ioreqs_map) { int val = -1; struct vbs_rng *rng; - if (unlikely(req_cnt <= 0)) + if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX) <= 0)) return -EINVAL; pr_debug("%s: handle kick!\n", __func__); @@ -268,7 +268,7 @@ static int handle_kick(int client_id, int req_cnt) return -EINVAL; } - val = virtio_vq_index_get(&rng->dev, req_cnt); + val = virtio_vq_index_get(&rng->dev, ioreqs_map); if (val >= 0) handle_vq_kick(rng, val); diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index bf55a0138943..c91a60598114 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -101,7 +101,7 @@ struct ioreq_client { /* * this req records the req number this client need handle */ - atomic_t req; + DECLARE_BITMAP(ioreqs_map, VHM_REQUEST_MAX); /* * client ioreq handler: @@ -434,7 +434,7 @@ static inline bool is_destroying(struct ioreq_client *client) static inline bool has_pending_request(struct ioreq_client *client) { if (client) - return (atomic_read(&client->req) > 0); + return !bitmap_empty(client->ioreqs_map, VHM_REQUEST_MAX); else return false; } @@ -482,7 +482,7 @@ static int ioreq_client_thread(void *data) if (has_pending_request(client)) { if (client->handler) { ret = client->handler(client->id, - client->req.counter); + client->ioreqs_map); if (ret < 0) BUG(); } else { @@ -800,7 +800,7 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) } else { req->processed = REQ_STATE_PROCESSING; req->client = client->id; - atomic_inc(&client->req); + set_bit(i, client->ioreqs_map); } } } @@ -831,7 +831,7 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) return -EINVAL; } - atomic_dec(&client->req); + clear_bit(vcpu, client->ioreqs_map); ret = hcall_notify_req_finish(client->vmid, vcpu); if (ret < 0) { pr_err("vhm-ioreq: failed to notify request finished !\n"); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index e4ecba020b08..33ca072c5a83 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -181,7 +181,7 @@ struct virtio_dev_info { * This is the callback function to be registered to VHM, * so that VBS gets notified when frontend accessed the register. */ - int (*dev_notify)(int, int); + int (*dev_notify)(int, unsigned long *); /** @vqs: virtqueue(s) of this device */ struct virtio_vq_info *vqs; /** @curq: current virtqueue index */ @@ -269,10 +269,10 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); * frontend. * * @dev: Pointer to VBS-K device data struct - * @req_cnt: Number of requests need to handle, provided by VHM + * @ioreqs_map: requests bitmap need to handle, provided by VHM * * Return: >=0 on virtqueue index, <0 on error */ -int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); +int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); #endif diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index fbf69b37d356..52b3ac83203c 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -61,7 +61,7 @@ #include #include -typedef int (*ioreq_handler_t)(int client_id, int req); +typedef int (*ioreq_handler_t)(int client_id, unsigned long *ioreqs_map); /** * acrn_ioreq_create_client - create ioreq client From a86a6e327887c28457391ae0a8cb0e8aefa24d6f Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0877/1276] vhm: use correct string length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix below compile warning: drivers/vhm/vhm_ioreq.c:207:3: warning: ‘strncpy’ specified bound 16 equals destination size [-Wstringop-truncation] strncpy(client->name, name, sizeof(client->name)); Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ --- drivers/vhm/vhm_ioreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index c91a60598114..5cb15b5badd8 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -204,7 +204,7 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, client->vmid = vmid; if (name) - strncpy(client->name, name, 16); + strncpy(client->name, name, sizeof(client->name) - 1); spin_lock_init(&client->range_lock); INIT_LIST_HEAD(&client->range_list); init_waitqueue_head(&client->wq); From e8dba6db4296664f04db793b1619a3878420e025 Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0878/1276] vhm: adapt to the new state transition of VHM requests This is the counter-part of VHM request state transition update. Instead of using two members (namely ''valid'' and ''processed''), the new state transition uses a single member (i.e. ''processed) following the transition pattern below. FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... The ownership of a VHM request shifts according to its state. Namely: 1) When a request is in COMPLETE or FREE state, the request is owned by the hypervisor. SOS (VHM or DM) shall not read or write the internals of the request except the state. 2) When a request is in PENDING or PROCESSING state, the request is owned by SOS. The hypervisor shall not read or write the request other than the state. Atomic operations should be used to access the state. For this the ''processed'' member in vhm_request is typed 'atomic_t' which is essentially the same 32-bit signed integer. This allows us to use atomic operations on the state directly, instead of casting it everywhere. It is also worth noting that atomic_set() in Linux does not imply a memory fence; it only includes a barrier to prevent compiler reordering. Thus smp_mb() are added before changing the state to COMPLETE so that the hypervisor always has the right results after it sees the state change. v1 -> v2: * Keep the original flow (i.e. update state after a client is found) instead of atomic_cmpxchg, given that acrn_ioreq_distribute_request() is called in tasklet and only on cpu 0. * Add documentation on struct vhm_request, following the kernel-doc style. Signed-off-by: Junjie Mao Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 5 +- drivers/vhm/vhm_ioreq.c | 11 +++- include/linux/vhm/acrn_common.h | 113 +++++++++++++++++++++++++++----- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 1d2e1b40728e..9b7a279bca90 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -161,7 +161,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) if (vcpu == dev->_ctx.max_vcpu) break; req = &dev->_ctx.req_buf[vcpu]; - if (req->valid && req->processed == REQ_STATE_PROCESSING && + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == dev->_ctx.vhm_client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) { /* currently we handle kick only, @@ -180,7 +180,8 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) else val = req->reqs.mmio_request.value; } - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); acrn_ioreq_complete_request(req->client, vcpu); } } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 5cb15b5badd8..5716bc5968d5 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -707,7 +707,8 @@ static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) } if (req_handled) { - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); if (hcall_notify_req_finish(vm->vmid, vcpu) < 0) { pr_err("vhm-ioreq: failed to " "notify request finished !\n"); @@ -788,7 +789,11 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) vcpu_num = atomic_read(&vm->vcpu_num); for (i = 0; i < vcpu_num; i++) { req = vm->req_buf->req_queue + i; - if (req->valid && (req->processed == REQ_STATE_PENDING)) { + + /* This function is called in tasklet only on SOS CPU0. Thus it + * is safe to read the state first and update it later as long + * as the update is atomic. */ + if (atomic_read(&req->processed) == REQ_STATE_PENDING) { if (handle_cf8cfc(vm, req, i)) continue; client = acrn_ioreq_find_client_by_request(vm, req); @@ -798,8 +803,8 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) "BUG\n"); BUG(); } else { - req->processed = REQ_STATE_PROCESSING; req->client = client->id; + atomic_set(&req->processed, REQ_STATE_PROCESSING); set_bit(i, client->ioreqs_map); } } diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 91951b7e1fc3..6269b07dcea4 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -52,6 +52,8 @@ #ifndef ACRN_COMMON_H #define ACRN_COMMON_H +#include + /* * Common structures for ACRN/VHM/DM */ @@ -62,9 +64,9 @@ #define VHM_REQUEST_MAX 16 #define REQ_STATE_PENDING 0 -#define REQ_STATE_SUCCESS 1 +#define REQ_STATE_COMPLETE 1 #define REQ_STATE_PROCESSING 2 -#define REQ_STATE_FAILED -1 +#define REQ_STATE_FREE 3 #define REQ_PORTIO 0 #define REQ_MMIO 1 @@ -111,13 +113,88 @@ struct pci_request { int32_t reg; } __attribute__((aligned(8))); -/* vhm_request are 256Bytes aligned */ +/** + * struct vhm_request - 256-byte VHM request + * + * The state transitions of a VHM request are: + * + * FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... + * \ / + * +--> FAILED -+ + * + * When a request is in COMPLETE or FREE state, the request is owned by the + * hypervisor. SOS (VHM or DM) shall not read or write the internals of the + * request except the state. + * + * When a request is in PENDING or PROCESSING state, the request is owned by + * SOS. The hypervisor shall not read or write the request other than the state. + * + * Based on the rules above, a typical VHM request lifecycle should looks like + * the following. + * + * (assume the initial state is FREE) + * + * SOS vCPU 0 SOS vCPU x UOS vCPU y + * + * hypervisor: + * fill in type, addr, etc. + * pause UOS vcpu y + * set state to PENDING (a) + * fire upcall to SOS vCPU 0 + * + * VHM: + * scan for pending requests + * set state to PROCESSING (b) + * assign requests to clients (c) + * + * client: + * scan for assigned requests + * handle the requests (d) + * set state to COMPLETE + * notify the hypervisor + * + * hypervisor: + * resume UOS vcpu y (e) + * + * hypervisor: + * post-work (f) + * set state to FREE + * + * Note that the following shall hold. + * + * 1. (a) happens before (b) + * 2. (c) happens before (d) + * 3. (e) happens before (f) + * 4. One vCPU cannot trigger another I/O request before the previous one has + * completed (i.e. the state switched to FREE) + * + * Accesses to the state of a vhm_request shall be atomic and proper barriers + * are needed to ensure that: + * + * 1. Setting state to PENDING is the last operation when issuing a request in + * the hypervisor, as the hypervisor shall not access the request any more. + * + * 2. Due to similar reasons, setting state to COMPLETE is the last operation + * of request handling in VHM or clients in SOS. + */ struct vhm_request { - /* offset: 0bytes - 63bytes */ + /** + * @type: Type of this request. Byte offset: 0. + */ uint32_t type; + + /** + * @reserved0: Reserved fields. Byte offset: 4. + */ uint32_t reserved0[15]; - /* offset: 64bytes-127bytes */ + /** + * @reqs: Details about this request. + * + * For REQ_PORTIO, this has type pio_request. For REQ_MMIO and REQ_WP, + * this has type mmio_request. For REQ_PCICFG, this has type + * pci_request. Byte offset: 64. + */ union { struct pio_request pio_request; struct pci_request pci_request; @@ -125,20 +202,24 @@ struct vhm_request { uint64_t reserved1[8]; } reqs; - /* True: valid req which need VHM to process. - * ACRN write, VHM read only - **/ - int32_t valid; + /** + * @reserved1: Reserved fields. Byte offset: 128. + */ + uint32_t reserved1; - /* the client which is distributed to handle this request */ + /** + * @client: The client which is distributed to handle this request. + * + * Accessed by VHM only. Byte offset: 132. + */ int32_t client; - /* 1: VHM had processed and success - * 0: VHM had not yet processed - * -1: VHM failed to process. Invalid request - * VHM write, ACRN read only - **/ - int32_t processed; + /** + * @processed: The status of this request. + * + * Take REQ_STATE_xxx as values. Byte offset: 136. + */ + atomic_t processed; } __attribute__((aligned(256))); struct vhm_request_buffer { From bf0df44d434bceb30099a51faa560f45a385a00c Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0879/1276] vhm: Add error handling for IC_CREATE_VM ioctl If returned with error after hypervisor creating the VM, then we have no chance to destroy it. Add the error handling to destroy the VM if fail. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5884fd2b259e..1286f7750906 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -216,21 +216,27 @@ static long vhm_dev_ioctl(struct file *filep, } if (copy_to_user((void *)ioctl_param, &created_vm, - sizeof(struct acrn_create_vm))) - return -EFAULT; - + sizeof(struct acrn_create_vm))) { + ret = -EFAULT; + goto create_vm_fail; + } vm->vmid = created_vm.vmid; if (created_vm.vm_flag & SECURE_WORLD_ENABLED) { ret = init_trusty(vm); if (ret < 0) { pr_err("vhm: failed to init trusty for VM!\n"); - return ret; + goto create_vm_fail; } } pr_info("vhm: VM %d created\n", created_vm.vmid); break; +create_vm_fail: + hcall_destroy_vm(created_vm.vmid); + vm->vmid = ACRN_INVALID_VMID; + break; + } case IC_START_VM: { From 1780842fd0eded06d3f4014dab65177643bc396a Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0880/1276] vhm: setup ioreq shared buf in IC_CREATE_VM ioctl ioreq shared buffer page is a necessary for each VM. So separate VM creating and shared buffer page setup into two ioctls is meaningless. This patch intends to move the ioreq shared buffer page setup into IC_CREATE_VM ioctl. With this change, we can create vhm ioreq client just after CREATE_VM step. We need be careful with the compatibility. To achieve it safely, we will do, 1) Do shared page setup in IC_CREATE_VM, what this patch do exactly 2) Move the shared page setup action into vm creating in DM. 3) Remove the ioctl IC_SET_IOREQ_BUFFER in kernel. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 12 +++++++++++- drivers/vhm/vhm_ioreq.c | 2 +- include/linux/vhm/acrn_common.h | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 1286f7750906..152aba23d95f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -230,8 +230,17 @@ static long vhm_dev_ioctl(struct file *filep, } } + if (created_vm.req_buf) { + ret = acrn_ioreq_init(vm, created_vm.req_buf); + if (ret < 0) + goto ioreq_buf_fail; + } + pr_info("vhm: VM %d created\n", created_vm.vmid); break; +ioreq_buf_fail: + if (created_vm.vm_flag & SECURE_WORLD_ENABLED) + deinit_trusty(vm); create_vm_fail: hcall_destroy_vm(created_vm.vmid); vm->vmid = ACRN_INVALID_VMID; @@ -310,8 +319,9 @@ static long vhm_dev_ioctl(struct file *filep, case IC_SET_IOREQ_BUFFER: { /* init ioreq buffer */ ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); - if (ret < 0) + if (ret < 0 && ret != -EEXIST) return ret; + ret = 0; break; } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 5716bc5968d5..960723b1778d 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -881,7 +881,7 @@ int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) int ret; if (vm->req_buf) - BUG(); + return -EEXIST; ret = get_user_pages_fast(vma, 1, 1, &page); if (unlikely(ret != 1) || (page == NULL)) { diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 6269b07dcea4..dfe89309fb17 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -253,8 +253,11 @@ struct acrn_create_vm { */ uint64_t vm_flag; + /** guest physical address of VM request_buffer */ + uint64_t req_buf; + /** Reserved for future use*/ - uint8_t reserved2[24]; + uint8_t reserved2[16]; } __attribute__((aligned(8))); /** From 496372bff6bf5eb1b11ba0507afa2d810b097d26 Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0881/1276] VBS-K: add virtio_dev_reset A new ioctl VBS_RESET_DEV is introduced to support D3. VBS-U issues this ioctl to VBS-K driver when receives reset notification from FE driver. VBS-K driver should perform stop/flush/reset to react to this ioctl. virtio_dev_reset can be called in VBS-K driver's reset function. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 19 +++++++++++++++++++ include/linux/vbs/vbs.h | 8 ++++++++ include/linux/vbs/vbs_common_if.h | 1 + 3 files changed, 28 insertions(+) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 9b7a279bca90..7427942c12f7 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -304,6 +304,25 @@ long virtio_dev_init(struct virtio_dev_info *dev, } EXPORT_SYMBOL_GPL(virtio_dev_init); +long virtio_dev_reset(struct virtio_dev_info *dev) +{ + int i; + + for (i = 0; i < dev->nvq; i++) + virtio_vq_reset(&dev->vqs[i]); + + memset(dev->name, 0, sizeof(dev->name)); + dev->_ctx.vmid = 0; + dev->nvq = 0; + dev->negotiated_features = 0; + dev->io_range_start = 0; + dev->io_range_len = 0; + dev->io_range_type = PIO_RANGE; + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_dev_reset); + static int __init vbs_init(void) { return 0; diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 33ca072c5a83..30df8ebf68a0 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -275,4 +275,12 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); */ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); +/** + * virtio_dev_reset - reset a VBS-K device + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ +long virtio_dev_reset(struct virtio_dev_info *dev); #endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 25d6002c92bb..59c65786c088 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -92,5 +92,6 @@ struct vbs_dev_info { #define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) #define VBS_SET_VQ _IOW(VBS_IOCTL, 0x01, struct vbs_vqs_info) +#define VBS_RESET_DEV _IO(VBS_IOCTL, 0x02) #endif From 5aacc0c82c36ae3266c649addd6a66cb07af7a7e Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0882/1276] VBS-K: Check whether vhm_client_id is valid before deregister Need to check whether vhm_client_id is valid before trying to deregister from VHM. It is possbile that virtio_dev_deregister is called more than once by vbs-k driver. Signed-off-by: Jian Jun Chen Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 7427942c12f7..c2d8a5262a8d 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -135,12 +135,15 @@ long virtio_dev_register(struct virtio_dev_info *dev) long virtio_dev_deregister(struct virtio_dev_info *dev) { + if (dev->_ctx.vhm_client_id < 0) + return 0; + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, dev->io_range_type ? REQ_MMIO : REQ_PORTIO, dev->io_range_start, dev->io_range_start + dev->io_range_len); - acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + dev->_ctx.vhm_client_id = -1; return 0; } @@ -300,6 +303,8 @@ long virtio_dev_init(struct virtio_dev_info *dev, for (i = 0; i < nvq; i++) virtio_vq_reset(&vqs[i]); + dev->_ctx.vhm_client_id = -1; + return 0; } EXPORT_SYMBOL_GPL(virtio_dev_init); From 4456be46498a273691e9062a6ebd7822ed65567d Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0883/1276] VBS-K: add reset support for vbs_rng Add reset support for vbs_rng, it acts as an example about how to support D3. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs_rng.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 569d1d5d689c..45e17b086c29 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -123,7 +123,7 @@ static int vbs_rng_connection_cnt = 0; /* function declarations */ static int handle_kick(int client_id, unsigned long *ioreqs_map); -static void vbs_rng_reset(struct vbs_rng *rng); +static long vbs_rng_reset(struct vbs_rng *rng); static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush(struct vbs_rng *rng); #ifdef RUNTIME_CTRL @@ -319,7 +319,6 @@ static int vbs_rng_open(struct inode *inode, struct file *f) static int vbs_rng_release(struct inode *inode, struct file *f) { struct vbs_rng *rng = f->private_data; - int i; if (!rng) pr_err("%s: UNLIKELY rng NULL!\n", @@ -327,8 +326,6 @@ static int vbs_rng_release(struct inode *inode, struct file *f) vbs_rng_stop(rng); vbs_rng_flush(rng); - for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) - virtio_vq_reset(&(rng->vqs[i])); /* device specific release */ vbs_rng_reset(rng); @@ -396,6 +393,12 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, /* Increment counter */ vbs_rng_connection_cnt++; return r; + case VBS_RESET_DEV: + pr_debug("VBS_RESET_DEV ioctl:\n"); + vbs_rng_stop(rng); + vbs_rng_flush(rng); + r = vbs_rng_reset(rng); + return r; default: /*mutex_lock(&n->dev.mutex);*/ pr_debug("VBS_K generic ioctls!\n"); @@ -410,8 +413,9 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, } /* device specific function to cleanup itself */ -static void vbs_rng_reset(struct vbs_rng *rng) +static long vbs_rng_reset(struct vbs_rng *rng) { + return virtio_dev_reset(&rng->dev); } /* device specific function */ From 2996ecb210bf928ac77a336f7932eba9be5e49a4 Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0884/1276] VBS-K: fix a bug due to incorrect check of return value of bitmap_empty When no bits are set in ioreqs_map, bitmap_empty returns 1. In this case we can just return 0 since no virtqueues are kicked. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs_rng.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 45e17b086c29..fd2bb27af66e 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -256,8 +256,8 @@ static int handle_kick(int client_id, unsigned long *ioreqs_map) int val = -1; struct vbs_rng *rng; - if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX) <= 0)) - return -EINVAL; + if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX))) + return 0; pr_debug("%s: handle kick!\n", __func__); From 4bc0c6f8ffa8e05c43000cee302d51ba2ab52397 Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0885/1276] VHM: remove panic action when ioreq fails. handle the ioreq failed cases instead of calling BUG(), which will cause system panic directly. Signed-off-by: Minggui Cao Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 960723b1778d..da61069b8d01 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -483,8 +483,10 @@ static int ioreq_client_thread(void *data) if (client->handler) { ret = client->handler(client->id, client->ioreqs_map); - if (ret < 0) - BUG(); + if (ret < 0) { + pr_err("vhm-ioreq: err:%d\n", ret); + break; + } } else { pr_err("vhm-ioreq: no ioreq handler\n"); break; @@ -799,9 +801,8 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) client = acrn_ioreq_find_client_by_request(vm, req); if (client == NULL) { pr_err("vhm-ioreq: failed to " - "find ioreq client -> " - "BUG\n"); - BUG(); + "find ioreq client\n"); + return -EINVAL; } else { req->client = client->id; atomic_set(&req->processed, REQ_STATE_PROCESSING); From f3c352940e0741f3e5cf93bd045e9a1f3cd714bc Mon Sep 17 00:00:00 2001 From: Ong Hock Yu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0886/1276] vbs: fix virtio_vq_index_get func handling of multi VQ concurrent request. Under multiple VQ use case, it is possible to have concurrent requests. Added support to return multiple vq index from all vcpu. Signed-off-by: Ong Hock Yu --- drivers/vbs/vbs.c | 27 ++++++++++++++++++++++----- drivers/vbs/vbs_rng.c | 2 +- include/linux/vbs/vbs.h | 11 ++++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index c2d8a5262a8d..0e0516ad8794 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -148,9 +148,12 @@ long virtio_dev_deregister(struct virtio_dev_info *dev) return 0; } -int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) +int virtio_vqs_index_get(struct virtio_dev_info *dev, + unsigned long *ioreqs_map, + int *vqs_index, + int max_vqs_index) { - int val = -1; + int idx = 0; struct vhm_request *req; int vcpu; @@ -178,10 +181,24 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) } else { pr_debug("%s: write request! type %d\n", __func__, req->type); + + if (idx == max_vqs_index) { + pr_warn("%s: The allocated vqs\n" + "size (%d) is smaller than the\n" + "number of vcpu (%d)! This\n" + "might caused the process of\n" + "some requests be delayed.", + __func__, max_vqs_index, + dev->_ctx.max_vcpu); + break; + } + if (dev->io_range_type == PIO_RANGE) - val = req->reqs.pio_request.value; + vqs_index[idx++] = + req->reqs.pio_request.value; else - val = req->reqs.mmio_request.value; + vqs_index[idx++] = + req->reqs.mmio_request.value; } smp_mb(); atomic_set(&req->processed, REQ_STATE_COMPLETE); @@ -189,7 +206,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) } } - return val; + return idx; } static long virtio_vqs_info_set(struct virtio_dev_info *dev, diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index fd2bb27af66e..c5e28cc12c55 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -268,7 +268,7 @@ static int handle_kick(int client_id, unsigned long *ioreqs_map) return -EINVAL; } - val = virtio_vq_index_get(&rng->dev, ioreqs_map); + virtio_vqs_index_get(&rng->dev, ioreqs_map, &val, 1); if (val >= 0) handle_vq_kick(rng, val); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 30df8ebf68a0..d9d932c49e88 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -262,7 +262,7 @@ long virtio_dev_register(struct virtio_dev_info *dev); long virtio_dev_deregister(struct virtio_dev_info *dev); /** - * virtio_vq_index_get - get virtqueue index that frontend kicks + * virtio_vqs_index_get - get virtqueue indexes that frontend kicks * * This API is normally called in the VBS-K device's callback * function, to get value write to the "kick" register from @@ -270,10 +270,15 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); * * @dev: Pointer to VBS-K device data struct * @ioreqs_map: requests bitmap need to handle, provided by VHM + * @vqs_index: array to store the vq indexes + * @max_vqs_index: size of vqs_index array * - * Return: >=0 on virtqueue index, <0 on error + * Return: Number of vq request */ -int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); +int virtio_vqs_index_get(struct virtio_dev_info *dev, + unsigned long *ioreqs_map, + int *vqs_index, + int max_vqs_index); /** * virtio_dev_reset - reset a VBS-K device From 581b65247f2a4355e66735fd596f3d9749d53373 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 22 Aug 2018 16:12:20 +0000 Subject: [PATCH 0887/1276] vhm: init client->kthread_exit true Previously, there is a deadlock at below case - acrn-dm create gvt instance successfully - acrn-dm open uos image failed(wrong image path), the acrn-dm does some cleanup, like destroy gvt instance then acrn-dm stucks. when destroying gvt instance, it waits client->kthread_exit to be true while client->kthread_exit is set to be 0 at initializing and the thread is not created/started actually. V3: add vhm_create_kthread as a condition (Yakui) Signed-off-by: Li Zhijian Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index da61069b8d01..0bcb0e053947 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -149,6 +149,7 @@ static int alloc_client(void) if (!client) return -ENOMEM; client->id = i; + client->kthread_exit = true; clients[i] = client; return i; @@ -266,7 +267,7 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, /* the client thread will mark kthread_exit flag as true before exit, * so wait for it exited. */ - while (!client->kthread_exit) + while (client->vhm_create_kthread && !client->kthread_exit) msleep(10); spin_lock_irqsave(&client->range_lock, flags); @@ -533,7 +534,9 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) "for client %s\n", client->name); return -ENOMEM; } + client->kthread_exit = false; } else { + client->kthread_exit = false; might_sleep(); if (check_kthread_stop) { From fee09051ea04364d7063eae7bf4e3aa73819d01a Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 31 Aug 2018 10:59:04 +0800 Subject: [PATCH 0888/1276] vhm: fix client use-after-free free_client() will free resource of client V2: adjust order to avoid use-after-free (yu1.wang@intel.com) Signed-off-by: Li Zhijian Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 0bcb0e053947..6bf07e812d03 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -282,10 +282,11 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, spin_lock_irqsave(&vm->ioreq_client_lock, flags); list_del(&client->list); spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); - free_client(client->id); if (client->id == vm->ioreq_fallback_client) vm->ioreq_fallback_client = -1; + + free_client(client->id); } void acrn_ioreq_destroy_client(int client_id) From 55f3e97a505efa143a858defe06995085088f26c Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Wed, 5 Sep 2018 09:35:05 +0800 Subject: [PATCH 0889/1276] Adds new API for unmap memseg It is required when DM unmap ptdev BAR in deinit Tracked-on: projectacrn/acrn-hypervisor#1146 Signed-off-by: Edwin Zhai Reviewed-by: Yin Fengwei Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 11 +++++++++++ drivers/vhm/vhm_mm.c | 17 +++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 11 +++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 40 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 152aba23d95f..6e351c387928 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -316,6 +316,17 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_UNSET_MEMSEG: { + struct vm_memmap memmap; + + if (copy_from_user(&memmap, (void *)ioctl_param, + sizeof(struct vm_memmap))) + return -EFAULT; + + ret = unmap_guest_memseg(vm, &memmap); + break; + } + case IC_SET_IOREQ_BUFFER: { /* init ioreq buffer */ ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 4d5854d0c139..6bea6688ddc0 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -208,6 +208,23 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) return 0; } +int unmap_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + /* only handle mmio */ + if (memmap->type != VM_MEMMAP_MMIO) { + pr_err("vhm: %s invalid memmap type: %d for unmap\n", + __func__, memmap->type); + return -EINVAL; + } + + if (del_memory_region(vm->vmid, memmap->gpa, memmap->len) < 0) { + pr_err("vhm: failed to del memory region %ld!\n", vm->vmid); + return -EFAULT; + } + + return 0; +} + void free_guest_mem(struct vhm_vm *vm) { return hugepage_free_guest(vm); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 62aed3466e9f..7b6b1b40615b 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -169,6 +169,17 @@ void free_guest_mem(struct vhm_vm *vm); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +/** + * unmap_guest_memseg - unset guest mmapping of memory set by + * map_guest_memseg + * + * @vm: pointer to guest vm + * @memmap: pointer to guest memory mapping info + * + * Return: + */ +int unmap_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); + int init_trusty(struct vhm_vm *vm); void deinit_trusty(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index cf4f63211aa2..6c09157a9bef 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -94,6 +94,7 @@ /* IC_ALLOC_MEMSEG not used */ #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) +#define IC_UNSET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x02) /* PCI assignment*/ #define IC_ID_PCI_BASE 0x50UL From 889978d66c081550f434ee4e45ab69dfe45058f1 Mon Sep 17 00:00:00 2001 From: Zhi Jin Date: Wed, 1 Aug 2018 14:58:50 +0800 Subject: [PATCH 0890/1276] sos: vhm: add HC_SETUP_HV_NPK_LOG hypercall This hypercall is used to enable/disable/configure the hypervisor NPK log. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1138 Signed-off-by: Zhi Jin Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 18 ++++++++++++++++++ include/linux/vhm/vhm_hypercall.h | 1 + 3 files changed, 24 insertions(+) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 9a92d6888b90..d994835dd924 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -92,6 +92,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_setup_hv_npk_log(unsigned long hv_npk_log) +{ + return acrn_hypercall1(HC_SETUP_HV_NPK_LOG, hv_npk_log); +} + inline long hcall_set_sstate_data(unsigned long sx_data_addr) { return acrn_hypercall1(HC_PM_SET_SSTATE_DATA, sx_data_addr); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 223702c0330c..24ca7be2c2b9 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -108,6 +108,7 @@ /* DEBUG */ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +#define HC_SETUP_HV_NPK_LOG _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x01) /* Power management */ #define HC_ID_PM_BASE 0x80UL @@ -188,6 +189,23 @@ struct sbuf_setup_param { uint64_t gpa; } __attribute__((aligned(8))); +struct hv_npk_log_param { + /* the setup command for the hypervisor NPK log */ + uint16_t cmd; + + /* the setup result for the hypervisor NPK log */ + uint16_t res; + + /* the loglevel for the hypervisor NPK log */ + uint16_t loglevel; + + /* Reserved */ + uint16_t reserved; + + /* the MMIO address for the hypervisor NPK log */ + uint64_t mmio_addr; +} __attribute__((aligned(8))); + struct vm_gpa2hpa { uint64_t gpa; /* IN: gpa to translation */ uint64_t hpa; /* OUT: -1 means invalid gpa */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 0a3869b356b8..703b35bec053 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -146,6 +146,7 @@ inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_reset_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_setup_hv_npk_log(unsigned long hv_npk_log); inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memory_regions(unsigned long pa_regions); From 1e9266ae1ec4e7e98d3796757e9d6fd08e81f438 Mon Sep 17 00:00:00 2001 From: Zhi Jin Date: Wed, 1 Aug 2018 14:58:50 +0800 Subject: [PATCH 0891/1276] acrn: add hv_npk_log module The hv_npk_log driver is used to enable/disable/configure the hypervisor NPK log via the hypercall HC_SETUP_HV_NPK_LOG. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1138 Signed-off-by: Zhi Jin Reviewed-by: Zhao Yakui --- drivers/acrn/Kconfig | 8 + drivers/acrn/Makefile | 1 + drivers/acrn/hv_npk_log.c | 384 ++++++++++++++++++++++++++++++++++++++ drivers/acrn/hv_npk_log.h | 109 +++++++++++ 4 files changed, 502 insertions(+) create mode 100644 drivers/acrn/hv_npk_log.c create mode 100644 drivers/acrn/hv_npk_log.h diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 9fc4cae04a56..706aba14ad00 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -19,3 +19,11 @@ config ACRN_HVLOG ---help--- This is the Trace driver for the Intel ACRN hypervisor log. You can say y to build it into the kernel. + +config ACRN_HV_NPK_LOG + bool "Intel ACRN Hypervisor NPK Log" + depends on INTEL_TH + depends on ACRN_VHM + ---help--- + The driver is to configure/enable/disable the Intel ACRN hypervisor + NPK log. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index 05dd698e8171..d0e14f43bcba 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o +obj-$(CONFIG_ACRN_HV_NPK_LOG) += hv_npk_log.o diff --git a/drivers/acrn/hv_npk_log.c b/drivers/acrn/hv_npk_log.c new file mode 100644 index 000000000000..2303b9a72a3a --- /dev/null +++ b/drivers/acrn/hv_npk_log.c @@ -0,0 +1,384 @@ +/* + * ACRN Hypervisor NPK Log + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * The hv_npk_log driver is to enable/disable/configure the ACRN hypervisor + * NPK log. It communicates with the hypervisor via the HC_SETUP_HV_NPK_LOG + * hypercall, and exposes the interface to usperspace via sysfs. + * With this driver, the user could: + * 1. Configure the Master/Channel used for the hypervisor NPK log. + * 2. Configure the log level of the hypervisor NPK log. + * 3. Enable/Disable the hypervisor NPK log. + * + * +-------------------------------------+ + * | Interfaces exposed by the driver | + * SOS | U: and used by the applications | + * | +----------------^----------------+ | + * | | | + * | K: +---------v---------+ | + * | | hv_npk_log driver | | + * | +---------^---------+ | + * +------------------|------------------+ + * | HC_SETUP_HV_NPK_LOG + * +------------------|------------------+ + * HV | +-------v-------+ | + * | | npk_log | | + * | +-------+-------+ | + * +------------------|------------------+ + * | + * +-------------+----v----+-------------+ + * NPK MMIO | | /////// | | + * | | /////// | | + * +-------------+----+----+-------------+ + * | + * +---+ The Master/Channel reserved + * for the hypervisor NPK logs + */ + +#define pr_fmt(fmt) "ACRN HV_NPK_LOG: " fmt + +#include +#include +#include +#include +#include +#include +#include "hv_npk_log.h" + +#define HV_NPK_LOG_USAGE \ + "echo \"E[nable] [#M #C] [#L]\" to enable the ACRN HV NPK Log\n" \ + "echo \"D[isable]\" to disable the ACRN HV NPK Log\n" \ + "echo \"C[onfig] [#M #C] [#L]\" to configure the ACRN HV NPK Log\n" + +static struct hv_npk_log_conf *hnl_conf; + +/* Try to get the master/channel based on the given address */ +static int addr2mc(phys_addr_t addr, int *master, int *channel) +{ + phys_addr_t offset, base; + unsigned int start, end; + int c, m; + + if (!hnl_conf || !master || !channel || !hnl_conf->nchan) + return -EINVAL; + + /* check if the addr belongs to SW_BAR or FW_BAR */ + if (addr >= hnl_conf->stmr_base && addr < hnl_conf->stmr_end) { + base = hnl_conf->stmr_base; + start = hnl_conf->sw_start; + end = hnl_conf->sw_end; + } else if (addr >= hnl_conf->ftmr_base && addr < hnl_conf->ftmr_end) { + base = hnl_conf->ftmr_base; + start = hnl_conf->fw_start; + end = hnl_conf->fw_end; + } else { + return -EINVAL; + } + + offset = addr - base; + if (offset % NPK_CHAN_SIZE) + return -EINVAL; + + c = offset / NPK_CHAN_SIZE; + m = c / hnl_conf->nchan; + c = c % hnl_conf->nchan; + if (start + m > end) + return -EINVAL; + + *channel = c; + *master = m + start; + return 0; +} + +/* Try to get the MMIO address based on the given master/channel */ +static int mc2addr(phys_addr_t *addr, unsigned int master, unsigned int channel) +{ + phys_addr_t base; + unsigned int start; + + if (!hnl_conf || !addr || !hnl_conf->nchan + || channel >= hnl_conf->nchan) + return -EINVAL; + + /* check if the master belongs to SW_BAR or FW_BAR */ + if (master >= hnl_conf->sw_start && master <= hnl_conf->sw_end) { + base = hnl_conf->stmr_base; + start = hnl_conf->sw_start; + } else if (master >= hnl_conf->fw_start && master <= hnl_conf->fw_end) { + base = hnl_conf->ftmr_base; + start = hnl_conf->fw_start; + } else { + return -EINVAL; + } + + *addr = base + ((master - start) * hnl_conf->nchan + channel) + * NPK_CHAN_SIZE; + return 0; +} + +static int npk_dev_match(struct device *dev, void *data) +{ + return 1; +} + +/* Check if the NPK device/driver exists, and get info from them */ +static int load_npk_conf(void) +{ + u32 reg; + int err; + void __iomem *base; + struct device *dev; + struct pci_dev *pdev; + struct device_driver *drv; + + /* check if the NPK device and driver exists */ + drv = driver_find(NPK_DRV_NAME, &pci_bus_type); + if (!drv) { + pr_err("Cannot find the %s driver\n", NPK_DRV_NAME); + return -ENODEV; + } + + dev = driver_find_device(drv, NULL, NULL, npk_dev_match); + if (!dev) { + pr_err("Cannot find the NPK device\n"); + return -ENODEV; + } + + hnl_conf = kzalloc(sizeof(struct hv_npk_log_conf), GFP_KERNEL); + if (!hnl_conf) + return -ENOMEM; + + /* get the base address of FW_BAR */ + pdev = to_pci_dev(dev); + err = pci_read_config_dword(pdev, PCI_REG_FW_LBAR, ®); + if (err) + return err; + hnl_conf->ftmr_base = reg & 0xfffc0000U; + err = pci_read_config_dword(pdev, PCI_REG_FW_UBAR, ®); + if (err) + return err; + hnl_conf->ftmr_base |= ((phys_addr_t)reg << 32); + + /* read out some configurations of NPK */ + base = devm_ioremap(dev, pdev->resource[TH_MMIO_CONFIG].start, + resource_size(&(pdev->resource[TH_MMIO_CONFIG]))); + if (!base) { + pr_err("Cannot map the NPK configuration address\n"); + goto error; + } + + reg = ioread32(base + REG_STH_STHCAP0); + hnl_conf->sw_start = reg & 0xffffU; + hnl_conf->sw_end = reg >> 16; + reg = ioread32(base + REG_STH_STHCAP1); + hnl_conf->nchan = reg & 0xffU; + hnl_conf->fw_end = reg >> 24; + reg = ioread32(base + REG_STH_STHCAP2); + hnl_conf->fw_start = reg & 0xffffU; + devm_iounmap(dev, base); + + hnl_conf->status = HV_NPK_LOG_UNKNOWN; + hnl_conf->master = HV_NPK_LOG_UNKNOWN; + hnl_conf->channel = HV_NPK_LOG_UNKNOWN; + hnl_conf->loglevel = HV_NPK_LOG_UNKNOWN; + hnl_conf->stmr_base = pdev->resource[TH_MMIO_SW].start; + + if (hnl_conf->sw_end < hnl_conf->sw_start + || hnl_conf->fw_end < hnl_conf->fw_start + || hnl_conf->nchan == 0) + goto error; + + hnl_conf->stmr_end = hnl_conf->stmr_base + (hnl_conf->sw_end - + hnl_conf->sw_start) * hnl_conf->nchan * NPK_CHAN_SIZE; + hnl_conf->ftmr_end = hnl_conf->ftmr_base + (hnl_conf->fw_end - + hnl_conf->fw_start) * hnl_conf->nchan * NPK_CHAN_SIZE; + + return 0; + +error: + kfree(hnl_conf); + hnl_conf = NULL; + return -EINVAL; +} + +/* User interface to set the configuration */ +static int hv_npk_log_conf_set(const char *val, const struct kernel_param *kp) +{ + char **argv; + int i, argc, ret = -EINVAL; + struct hv_npk_log_param cmd; + unsigned int args[HV_NPK_LOG_MAX_PARAM]; + + if (!hnl_conf && load_npk_conf() < 0) + return -EINVAL; + + argv = argv_split(GFP_KERNEL, val, &argc); + if (!argv) + return -ENOMEM; + if (!argc || argc > HV_NPK_LOG_MAX_PARAM) + goto out; + + for (i = 1; i < argc; i++) + if (kstrtouint(argv[i], 10, &args[i]) < 0) + goto out; + + memset(&cmd, 0, sizeof(struct hv_npk_log_param)); + cmd.loglevel = 0xffffU; + cmd.cmd = HV_NPK_LOG_CMD_INVALID; + switch (tolower(argv[0][0])) { + case 'e': /* enable */ + case 'c': /* configure */ + if (!strncasecmp(argv[0], "enable", strlen(argv[0]))) { + cmd.cmd = HV_NPK_LOG_CMD_ENABLE; + } else if (!strncasecmp(argv[0], "configure", strlen(argv[0])) + && argc != 1) { + cmd.cmd = HV_NPK_LOG_CMD_CONF; + } else + break; + + if (argc <= 2) { + cmd.loglevel = argc == 2 ? args[1] : 0xffffU; + if (hnl_conf->master == HV_NPK_LOG_UNKNOWN) + mc2addr(&cmd.mmio_addr, HV_NPK_LOG_DFT_MASTER, + HV_NPK_LOG_DFT_CHANNEL); + } else if (argc > 2 && !mc2addr(&cmd.mmio_addr, + args[1], args[2])) { + cmd.loglevel = argc == 4 ? args[3] : 0xffffU; + } + break; + case 'd': /* disable */ + if (!strncasecmp(argv[0], "disable", strlen(argv[0])) + && argc == 1) + cmd.cmd = HV_NPK_LOG_CMD_DISABLE; + break; + default: + pr_err("Unsupported command : %s\n", argv[0]); + break; + } + + if (cmd.cmd != HV_NPK_LOG_CMD_INVALID) { + ret = hcall_setup_hv_npk_log(virt_to_phys(&cmd)); + ret = (ret < 0 || cmd.res == HV_NPK_LOG_RES_KO) ? -EINVAL : 0; + } + +out: + argv_free(argv); + if (ret < 0) + pr_err("Unsupported configuration : %s\n", val); + return ret; +} + +/* User interface to query the configuration */ +static int hv_npk_log_conf_get(char *buffer, const struct kernel_param *kp) +{ + long ret; + struct hv_npk_log_param query; + + if (!hnl_conf && load_npk_conf() < 0) + return sprintf(buffer, "%s\n", + "Failed to init the configuration."); + + memset(&query, 0, sizeof(struct hv_npk_log_param)); + query.cmd = HV_NPK_LOG_CMD_QUERY; + ret = hcall_setup_hv_npk_log(virt_to_phys(&query)); + if (ret < 0 || query.res == HV_NPK_LOG_RES_KO) + return sprintf(buffer, "%s\n", "Failed to invoke the hcall."); + + if (!addr2mc(query.mmio_addr, &hnl_conf->master, &hnl_conf->channel)) { + hnl_conf->status = query.res == HV_NPK_LOG_RES_ENABLED ? + HV_NPK_LOG_ENABLED : HV_NPK_LOG_DISABLED; + } else { + hnl_conf->status = HV_NPK_LOG_UNKNOWN; + hnl_conf->master = HV_NPK_LOG_UNKNOWN; + hnl_conf->channel = HV_NPK_LOG_UNKNOWN; + } + hnl_conf->loglevel = query.loglevel; + + return scnprintf(buffer, PAGE_SIZE, "Master(SW:%d~%d FW:%d~%d):%d " + "Channel(0~%d):%d Status:%d Log Level: %d\n%s\n", + hnl_conf->sw_start, hnl_conf->sw_end, + hnl_conf->fw_start, hnl_conf->fw_end, + hnl_conf->master, hnl_conf->nchan - 1, + hnl_conf->channel, hnl_conf->status, + hnl_conf->loglevel, HV_NPK_LOG_USAGE); +} + +/* /sys/module/hv_npk_log/parameters/hv_npk_log_conf */ +static struct kernel_param_ops hv_npk_log_conf_param_ops = { + .set = hv_npk_log_conf_set, + .get = hv_npk_log_conf_get, +}; +module_param_cb(hv_npk_log_conf, &hv_npk_log_conf_param_ops, NULL, 0644); + +static struct miscdevice hv_npk_log_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "hv_npk_log", +}; + +static int __init hv_npk_log_init(void) +{ + return misc_register(&hv_npk_log_misc); +} + +static void __exit hv_npk_log_exit(void) +{ + kfree(hnl_conf); + + misc_deregister(&hv_npk_log_misc); +} + +module_init(hv_npk_log_init); +module_exit(hv_npk_log_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor NPK Log"); +MODULE_VERSION("0.1"); diff --git a/drivers/acrn/hv_npk_log.h b/drivers/acrn/hv_npk_log.h new file mode 100644 index 000000000000..68bd68286522 --- /dev/null +++ b/drivers/acrn/hv_npk_log.h @@ -0,0 +1,109 @@ +/* + * ACRN Hypervisor NPK Log + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HV_NPK_LOG_H_ +#define _HV_NPK_LOG_H_ + +#define NPK_DRV_NAME "intel_th_pci" + +#define NPK_CHAN_SIZE 64 +#define TH_MMIO_CONFIG 0 +#define TH_MMIO_SW 2 + +#define PCI_REG_FW_LBAR 0x70 +#define PCI_REG_FW_UBAR 0x74 + +#define REG_STH_STHCAP0 0x4000 +#define REG_STH_STHCAP1 0x4004 +#define REG_STH_STHCAP2 0x407C + +#define HV_NPK_LOG_ENABLED 1 +#define HV_NPK_LOG_DISABLED 0 +#define HV_NPK_LOG_UNKNOWN (-1) +#define HV_NPK_LOG_MAX_PARAM 4 + +#define HV_NPK_LOG_DFT_MASTER 74 +#define HV_NPK_LOG_DFT_CHANNEL 0 + +enum { + HV_NPK_LOG_CMD_INVALID, + HV_NPK_LOG_CMD_CONF, + HV_NPK_LOG_CMD_ENABLE, + HV_NPK_LOG_CMD_DISABLE, + HV_NPK_LOG_CMD_QUERY, +}; + +enum { + HV_NPK_LOG_RES_INVALID, + HV_NPK_LOG_RES_OK, + HV_NPK_LOG_RES_KO, + HV_NPK_LOG_RES_ENABLED, + HV_NPK_LOG_RES_DISABLED, +}; + +struct hv_npk_log_conf { + int status; + int master; + int channel; + int loglevel; + unsigned int fw_start; + unsigned int fw_end; + unsigned int sw_start; + unsigned int sw_end; + unsigned int nchan; + phys_addr_t stmr_base; + phys_addr_t stmr_end; + phys_addr_t ftmr_base; + phys_addr_t ftmr_end; + phys_addr_t ch_addr; +}; + +#endif /* _HV_NPK_LOG_H_ */ From 91034528c5777ddbd79cf0e504b4003f3025296f Mon Sep 17 00:00:00 2001 From: Sainath Grandhi Date: Thu, 13 Sep 2018 16:14:53 +0800 Subject: [PATCH 0892/1276] Adding kernel parameter for forcing xapic in physical mode This patch does the following Adds a kernel boot parameter xapic_phys to force xAPIC to work in physical mode if the boot kernel parameter says so. This is needed for VMs running in ACRN partition mode Tracked-on: projectacrn/acrn-hypervisor#1163 Signed-off-by: Sainath Grandhi --- arch/x86/kernel/apic/apic_flat_64.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kernel/apic/apic_flat_64.c b/arch/x86/kernel/apic/apic_flat_64.c index e84c9eb4e5b4..9ba0ac0c8c1f 100644 --- a/arch/x86/kernel/apic/apic_flat_64.c +++ b/arch/x86/kernel/apic/apic_flat_64.c @@ -29,6 +29,15 @@ static struct apic apic_flat; struct apic *apic __ro_after_init = &apic_flat; EXPORT_SYMBOL_GPL(apic); +int xapic_phys = 0; + +static int set_xapic_phys_mode(char *arg) +{ + xapic_phys = 1; + return 0; +} +early_param("xapic_phys", set_xapic_phys_mode); + static int flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id) { return 1; @@ -236,6 +245,9 @@ static void physflat_send_IPI_all(int vector) static int physflat_probe(void) { + if (xapic_phys == 1) + return 1; + if (apic == &apic_physflat || num_possible_cpus() > 8 || jailhouse_paravirt()) return 1; From 65ad2a1dca2a5e1e523cb2c072d8f1c12ea647aa Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Fri, 21 Sep 2018 09:52:17 +0800 Subject: [PATCH 0893/1276] VHM: Add EXPORT_SYMBOL for VHM API function so that it can be used by other module The following symbols were not exported causing link error when building i915/gvt driver as module. ERROR: "acrn_ioreq_destroy_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_attach_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_inject_msi" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "add_memory_region" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "del_memory_region" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_complete_request" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_intercept_bdf" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "find_get_vm" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_get_vm_info" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_del_iorange" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_get_reqbuf" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "write_protect_page" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_add_iorange" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "put_vm" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_create_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_vm_gpa2hpa" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Singed-off-by: Fei Yang --- drivers/vhm/vhm_ioreq.c | 8 ++++++++ drivers/vhm/vhm_mm.c | 3 +++ drivers/vhm/vhm_vm_mngt.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 6bf07e812d03..ff19cc3b76ba 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -220,6 +220,7 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, return client_id; } +EXPORT_SYMBOL_GPL(acrn_ioreq_create_client); int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name) { @@ -317,6 +318,7 @@ void acrn_ioreq_destroy_client(int client_id) put_vm(vm); } +EXPORT_SYMBOL_GPL(acrn_ioreq_destroy_client); static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) { @@ -376,6 +378,7 @@ int acrn_ioreq_add_iorange(int client_id, uint32_t type, return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_add_iorange); int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end) @@ -424,6 +427,7 @@ int acrn_ioreq_del_iorange(int client_id, uint32_t type, return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_del_iorange); static inline bool is_destroying(struct ioreq_client *client) { @@ -469,6 +473,7 @@ struct vhm_request *acrn_ioreq_get_reqbuf(int client_id) put_vm(vm); return (struct vhm_request *)vm->req_buf; } +EXPORT_SYMBOL_GPL(acrn_ioreq_get_reqbuf); static int ioreq_client_thread(void *data) { @@ -563,6 +568,7 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_attach_client); void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) { @@ -582,6 +588,7 @@ void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) client->pci_dev = dev; client->pci_func = func; } +EXPORT_SYMBOL_GPL(acrn_ioreq_intercept_bdf); void acrn_ioreq_unintercept_bdf(int client_id) { @@ -850,6 +857,7 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_complete_request); unsigned int vhm_dev_poll(struct file *filep, poll_table *wait) { diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 6bea6688ddc0..0b9168f5672b 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -133,6 +133,7 @@ int add_memory_region(unsigned long vmid, unsigned long gpa, (mem_access_right & MEM_ACCESS_RIGHT_MASK)); return set_memory_region(vmid, ®ion); } +EXPORT_SYMBOL_GPL(add_memory_region); int del_memory_region(unsigned long vmid, unsigned long gpa, unsigned long size) @@ -147,6 +148,7 @@ int del_memory_region(unsigned long vmid, unsigned long gpa, return set_memory_region(vmid, ®ion); } +EXPORT_SYMBOL_GPL(del_memory_region); int set_memory_regions(struct set_regions *regions) { @@ -184,6 +186,7 @@ int write_protect_page(unsigned long vmid, return 0; } +EXPORT_SYMBOL_GPL(write_protect_page); int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 8f1a00777dd4..c186b97a3c09 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -80,6 +80,7 @@ struct vhm_vm *find_get_vm(unsigned long vmid) mutex_unlock(&vhm_vm_list_lock); return NULL; } +EXPORT_SYMBOL_GPL(find_get_vm); void put_vm(struct vhm_vm *vm) { @@ -94,6 +95,7 @@ void put_vm(struct vhm_vm *vm) } mutex_unlock(&vhm_vm_list_lock); } +EXPORT_SYMBOL_GPL(put_vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) { @@ -111,6 +113,7 @@ int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) put_vm(vm); return 0; } +EXPORT_SYMBOL_GPL(vhm_get_vm_info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data) @@ -129,6 +132,7 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, } return 0; } +EXPORT_SYMBOL_GPL(vhm_inject_msi); unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) { @@ -145,6 +149,7 @@ unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) mb(); return gpa2hpa.hpa; } +EXPORT_SYMBOL_GPL(vhm_vm_gpa2hpa); void vm_list_add(struct list_head *list) { From 828d4d6af55e0a43a701d19d60f5abf55c7d8c19 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Sat, 8 Sep 2018 15:37:38 +0800 Subject: [PATCH 0894/1276] vhm: deinit trusty after hcall_destroy_vm after deinit_trusty, the released cma memory could be used by other drivers. while EPT of this memory area need be remap back to SOS by hcall_destroy_vm. with current sequence, an access to EPT unmapped area will cause MMIO access error. for trusty creation, the sequence is like: hcall_create_vm -> init_trusty (allocate CMA for trusty memory) ....> UOS hypercall to create trusty world (EPT remapping etc) for trusty destroy, the sequence changed like below: hcall_destroy_vm (include trusty destroy - EPT remapping back etc) -> deinit_trusty (free CMA for trusty memory) this sequence looks a little confuse, but as hcall_destroy_vm combined trusty world destroy operation, we need this operation be done before deinit_trusty. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1200 Signed-off-by: Jason Chen CJ Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 6e351c387928..d57dc3972bc5 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -276,13 +276,13 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { - if (vm->trusty_host_gpa) - deinit_trusty(vm); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); return -EFAULT; } + if (vm->trusty_host_gpa) + deinit_trusty(vm); vm->vmid = ACRN_INVALID_VMID; break; } From 8f9b1218cf9a43994d8573b177b6456e2fe33ba8 Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Wed, 8 Aug 2018 15:42:00 +0800 Subject: [PATCH 0895/1276] VHM: add ioctl/hypercall for UOS intr data monitor DM can use this ioctl/hypercall to get the UOS pass-through devices' interrupt count data, to monitor its status. It is used to enhance the feature "interrupt storm mitigation"; DM can monitor UOS pass-thru devices' interrupt data and give a response if one "interrupt storm" happens. V2: Fix the building warning. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/866 Signed-off-by: Minggui Cao Signed-off-by: Wei Liu Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 17 +++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 25 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d57dc3972bc5..0d25f732da5b 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -630,6 +630,23 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_VM_INTR_MONITOR: { + struct page *page; + + ret = get_user_pages_fast(ioctl_param, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("vhm-dev: failed to pin intr hdr buffer!\n"); + return -ENOMEM; + } + + ret = hcall_vm_intr_monitor(vm->vmid, page_to_phys(page)); + if (ret < 0) { + pr_err("vhm-dev: monitor intr data err=%ld\n", ret); + return -EFAULT; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d994835dd924..666f9aeb87ad 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -177,3 +177,8 @@ inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr) { return acrn_hypercall2(HC_VM_GPA2HPA, vmid, addr); } + +inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr) +{ + return acrn_hypercall2(HC_VM_INTR_MONITOR, vmid, addr); +} diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 24ca7be2c2b9..2cd6172e8e1d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -85,6 +85,7 @@ #define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) +#define HC_VM_INTR_MONITOR _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x04) /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x30UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 703b35bec053..062196ab1194 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -167,5 +167,6 @@ inline long hcall_reset_ptdev_intr_info(unsigned long vmid, unsigned long pt_irq); inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi); inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr); +inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 6c09157a9bef..063ebd5c7a93 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -80,6 +80,7 @@ #define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) +#define IC_VM_INTR_MONITOR _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x04) /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x30UL From dd9f887458477dca9e27fea5d146de593ce9422d Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 20 Sep 2018 10:16:56 +0800 Subject: [PATCH 0896/1276] vhm: enable -Werror while compiling vhm/vbs/hyper-dmabuf Sometimes warnings are caused by the potential errors, in order to add the strict check, the warnings will be treated as error and should be fixed. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1300 Signed-off-by: Wei Liu Reviewed-by: Zhao Yakui --- drivers/acrn/Makefile | 1 + drivers/char/vhm/Makefile | 1 + drivers/vbs/Makefile | 1 + drivers/vhm/Makefile | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index d0e14f43bcba..dad6a9e8c42c 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,3 +1,4 @@ +subdir-ccflags-$(CONFIG_ACRN) := -Werror obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o diff --git a/drivers/char/vhm/Makefile b/drivers/char/vhm/Makefile index cb801c70a37e..5ee68c5f7278 100644 --- a/drivers/char/vhm/Makefile +++ b/drivers/char/vhm/Makefile @@ -1 +1,2 @@ +subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror obj-y += vhm_dev.o diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index 85e1cc252197..a6734db96eee 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -1,3 +1,4 @@ +subdir-ccflags-$(CONFIG_VBS) := -Werror ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 23f17ae24f78..c3bce347f786 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1,2 @@ +subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o From dadc0c4b22650fa1ef940179941d32a723049520 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Sat, 29 Sep 2018 11:00:46 +0800 Subject: [PATCH 0897/1276] vhm: change trace_printk of vhm_dev_ioctl to pr_debug trace_printk will write into the ring buffer by default. Actually, we don't need this trace entry as there are many vhm ioctls happen at runtime which will flood the ring buffer. So change it to dynamic printk. Tracked-On: projectacrn/acrn-hypervisor#1328 Signed-off-by: Shuo Liu Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 0d25f732da5b..987c7ceea261 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -160,7 +160,7 @@ static long vhm_dev_ioctl(struct file *filep, struct ic_ptdev_irq ic_pt_irq; struct hc_ptdev_irq hc_pt_irq; - trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + pr_debug("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); if (ioctl_num == IC_GET_API_VERSION) { struct api_version api_version; From f1a5ed0fb60b02bf8315394c7ef94163853211d8 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Wed, 11 Jul 2018 14:37:53 +0800 Subject: [PATCH 0898/1276] vhm: add ioeventfd support for ACRN hypervisor service module ioeventfd which is based on eventfd, intends to glue vhm module and other modules who are interested in guest IOs. Each ioeventfd registered by userspace can map a PIO/MMIO range of the guest to eventfd, and response to signal the eventfd when get the in-range IO write from guest. Then the other side of eventfd can be notified to process the IO request. Now we only use the ioeventfd to listen virtqueue's kick register, there are some limitations: 1) Length support can only be 1, 2, 4 or 8 2) Only support write operation, read will get 0 3) Same address, shorter length writing can be handled with the integral data matching Tracked-On: projectacrn/acrn-hypervisor#1329 Signed-off-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 13 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_ioeventfd.c | 473 +++++++++++++++++++++++++++++ include/linux/vhm/vhm_eventfd.h | 10 + include/linux/vhm/vhm_ioctl_defs.h | 16 + 5 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_ioeventfd.c create mode 100644 include/linux/vhm/vhm_eventfd.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 987c7ceea261..672b58a29a1f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -83,6 +83,7 @@ #include #include #include +#include #include @@ -236,6 +237,8 @@ static long vhm_dev_ioctl(struct file *filep, goto ioreq_buf_fail; } + acrn_ioeventfd_init(vm->vmid); + pr_info("vhm: VM %d created\n", created_vm.vmid); break; ioreq_buf_fail: @@ -276,6 +279,7 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { + acrn_ioeventfd_deinit(vm->vmid); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); @@ -647,6 +651,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_EVENT_IOEVENTFD: { + struct acrn_ioeventfd args; + + if (copy_from_user(&args, (void *)ioctl_param, sizeof(args))) + return -EFAULT; + ret = acrn_ioeventfd(vm->vmid, &args); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index c3bce347f786..193f7692c9e3 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1,2 +1,2 @@ subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror -obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o diff --git a/drivers/vhm/vhm_ioeventfd.c b/drivers/vhm/vhm_ioeventfd.c new file mode 100644 index 000000000000..d5efb1a88dfa --- /dev/null +++ b/drivers/vhm/vhm_ioeventfd.c @@ -0,0 +1,473 @@ +/* + * ioeventfd for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static LIST_HEAD(vhm_ioeventfd_clients); +static DEFINE_MUTEX(vhm_ioeventfds_mutex); + +/* use internally to record properties of each ioeventfd */ +struct acrn_vhm_ioeventfd { + /* list to link all ioventfd together */ + struct list_head list; + /* eventfd of this ioeventfd */ + struct eventfd_ctx *eventfd; + /* start address for IO range*/ + u64 addr; + /* match data */ + u64 data; + /* length for IO range */ + int length; + /* IO range type, can be REQ_PORTIO and REQ_MMIO */ + int type; + /* ignore match data if true */ + bool wildcard; +}; + +/* instance to bind ioeventfds of each VM */ +struct vhm_ioeventfd_info { + struct list_head list; + int refcnt; + /* vmid of VM */ + uint16_t vmid; + /* vhm ioreq client for this instance */ + int vhm_client_id; + /* vcpu number of this VM */ + int vcpu_num; + /* ioreq shared buffer of this VM */ + struct vhm_request *req_buf; + + /* ioeventfds in this instance */ + struct list_head ioeventfds; + struct mutex ioeventfds_lock; +}; + +static struct vhm_ioeventfd_info *get_ioeventfd_info_by_client( + int client_id) +{ + struct vhm_ioeventfd_info *info = NULL; + + mutex_lock(&vhm_ioeventfds_mutex); + list_for_each_entry(info, &vhm_ioeventfd_clients, list) { + if (info->vhm_client_id == client_id) { + info->refcnt++; + mutex_unlock(&vhm_ioeventfds_mutex); + return info; + } + } + mutex_unlock(&vhm_ioeventfds_mutex); + return NULL; +} + +static struct vhm_ioeventfd_info *get_ioeventfd_info_by_vm(uint16_t vmid) +{ + struct vhm_ioeventfd_info *info = NULL; + + mutex_lock(&vhm_ioeventfds_mutex); + list_for_each_entry(info, &vhm_ioeventfd_clients, list) { + if (info->vmid == vmid) { + info->refcnt++; + mutex_unlock(&vhm_ioeventfds_mutex); + return info; + } + } + mutex_unlock(&vhm_ioeventfds_mutex); + return NULL; +} + +static void put_ioeventfd_info(struct vhm_ioeventfd_info *info) +{ + mutex_lock(&vhm_ioeventfds_mutex); + info->refcnt--; + if (info->refcnt == 0) { + list_del(&info->list); + mutex_unlock(&vhm_ioeventfds_mutex); + acrn_ioreq_destroy_client(info->vhm_client_id); + kfree(info); + return; + } + mutex_unlock(&vhm_ioeventfds_mutex); +} + +/* assumes info->ioeventfds_lock held */ +static void vhm_ioeventfd_shutdown(struct acrn_vhm_ioeventfd *p) +{ + eventfd_ctx_put(p->eventfd); + list_del(&p->list); + kfree(p); +} + +static inline int ioreq_type_from_flags(int flags) +{ + return flags & ACRN_IOEVENTFD_FLAG_PIO ? + REQ_PORTIO : REQ_MMIO; +} + +/* assumes info->ioeventfds_lock held */ +static bool vhm_ioeventfd_is_duplicated(struct vhm_ioeventfd_info *info, + struct acrn_vhm_ioeventfd *ioeventfd) +{ + struct acrn_vhm_ioeventfd *p; + + /* + * Treat same addr/type/data with different length combination + * as the same one. + * Register PIO[0x100~0x107] with data 0x10 as ioeventfd A, later + * PIO[0x100~0x103] with data 0x10 will be failed to register. + */ + list_for_each_entry(p, &info->ioeventfds, list) + if (p->addr == ioeventfd->addr && + p->type == ioeventfd->type && + (p->wildcard || ioeventfd->wildcard || + p->data == ioeventfd->data)) + return true; + + return false; +} + +static int acrn_assign_ioeventfd(struct vhm_ioeventfd_info *info, + struct acrn_ioeventfd *args) +{ + struct eventfd_ctx *eventfd; + struct acrn_vhm_ioeventfd *p; + int ret = -ENOENT; + + /* check for range overflow */ + if (args->addr + args->len < args->addr) + return -EINVAL; + + /* Only support 1,2,4,8 width registers */ + if (!(args->len == 1 || args->len == 2 || + args->len == 4 || args->len == 8)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto fail; + } + + INIT_LIST_HEAD(&p->list); + p->addr = args->addr; + p->length = args->len; + p->eventfd = eventfd; + p->type = ioreq_type_from_flags(args->flags); + + /* If datamatch enabled, we compare the data + * otherwise this is a wildcard + */ + if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH) + p->data = args->data; + else + p->wildcard = true; + + mutex_lock(&info->ioeventfds_lock); + + /* Verify that there isn't a match already */ + if (vhm_ioeventfd_is_duplicated(info, p)) { + ret = -EEXIST; + goto unlock_fail; + } + + /* register the IO range into vhm */ + ret = acrn_ioreq_add_iorange(info->vhm_client_id, p->type, + p->addr, p->addr + p->length - 1); + if (ret < 0) + goto unlock_fail; + + list_add_tail(&p->list, &info->ioeventfds); + mutex_unlock(&info->ioeventfds_lock); + + return 0; + +unlock_fail: + mutex_unlock(&info->ioeventfds_lock); +fail: + kfree(p); + eventfd_ctx_put(eventfd); + return ret; +} + +static int acrn_deassign_ioeventfd(struct vhm_ioeventfd_info *info, + struct acrn_ioeventfd *args) +{ + struct acrn_vhm_ioeventfd *p, *tmp; + struct eventfd_ctx *eventfd; + int ret = 0; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&info->ioeventfds_lock); + + list_for_each_entry_safe(p, tmp, &info->ioeventfds, list) { + if (p->eventfd != eventfd) + continue; + + ret = acrn_ioreq_del_iorange(info->vhm_client_id, p->type, + p->addr, p->addr + p->length - 1); + if (ret) + break; + vhm_ioeventfd_shutdown(p); + break; + } + + mutex_unlock(&info->ioeventfds_lock); + + eventfd_ctx_put(eventfd); + + return ret; +} + +int acrn_ioeventfd(uint16_t vmid, struct acrn_ioeventfd *args) +{ + struct vhm_ioeventfd_info *info = NULL; + int ret; + + info = get_ioeventfd_info_by_vm(vmid); + if (!info) + return -ENOENT; + + if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN) + ret = acrn_deassign_ioeventfd(info, args); + else + ret = acrn_assign_ioeventfd(info, args); + + put_ioeventfd_info(info); + return ret; +} + +static struct acrn_vhm_ioeventfd *vhm_ioeventfd_match( + struct vhm_ioeventfd_info *info, + u64 addr, u64 data, int length, int type) +{ + struct acrn_vhm_ioeventfd *p = NULL; + + /* + * Same addr/type/data will be treated as hit, otherwise ignore. + * Register PIO[0x100~0x107] with data 0x10 as ioeventfd A, later + * request PIO[0x100~0x103] with data 0x10 will hit A. + */ + list_for_each_entry(p, &info->ioeventfds, list) { + if (p->type == type && + p->addr == addr && + (p->wildcard || p->data == data)) + return p; + } + + return NULL; +} + +static int acrn_ioeventfd_dispatch_ioreq(int client_id, + unsigned long *ioreqs_map) +{ + struct vhm_request *req; + struct acrn_vhm_ioeventfd *p; + struct vhm_ioeventfd_info *info; + u64 addr; + u64 val; + int size; + int vcpu; + + info = get_ioeventfd_info_by_client(client_id); + if (!info) + return -EINVAL; + + /* get req buf */ + if (!info->req_buf) { + info->req_buf = acrn_ioreq_get_reqbuf(info->vhm_client_id); + if (!info->req_buf) { + pr_err("Failed to get req_buf for client %d\n", + info->vhm_client_id); + put_ioeventfd_info(info); + return -EINVAL; + } + } + + while (1) { + vcpu = find_first_bit(ioreqs_map, info->vcpu_num); + if (vcpu == info->vcpu_num) + break; + req = &info->req_buf[vcpu]; + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && + req->client == client_id) { + if (req->type == REQ_MMIO) { + if (req->reqs.mmio_request.direction == + REQUEST_READ) { + /* reading does nothing and return 0 */ + req->reqs.mmio_request.value = 0; + goto next_ioreq; + } + addr = req->reqs.mmio_request.address; + size = req->reqs.mmio_request.size; + val = req->reqs.mmio_request.value; + } else { + if (req->reqs.pio_request.direction == + REQUEST_READ) { + /* reading does nothing and return 0 */ + req->reqs.pio_request.value = 0; + goto next_ioreq; + } + addr = req->reqs.pio_request.address; + size = req->reqs.pio_request.size; + val = req->reqs.pio_request.value; + } + + mutex_lock(&info->ioeventfds_lock); + p = vhm_ioeventfd_match(info, addr, val, size, + req->type); + if (p) + eventfd_signal(p->eventfd, 1); + mutex_unlock(&info->ioeventfds_lock); + +next_ioreq: + atomic_set(&req->processed, REQ_STATE_COMPLETE); + acrn_ioreq_complete_request(client_id, vcpu); + } + } + + put_ioeventfd_info(info); + return 0; +} + +int acrn_ioeventfd_init(uint16_t vmid) +{ + int ret = 0; + char name[16]; + struct vm_info vm_info; + struct vhm_ioeventfd_info *info; + + info = get_ioeventfd_info_by_vm(vmid); + if (info) { + put_ioeventfd_info(info); + return -EEXIST; + } + + info = kzalloc(sizeof(struct vhm_ioeventfd_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + snprintf(name, sizeof(name), "ioeventfd-%hu", vmid); + info->vhm_client_id = acrn_ioreq_create_client(vmid, + acrn_ioeventfd_dispatch_ioreq, name); + if (info->vhm_client_id < 0) { + pr_err("Failed to create ioeventfd client for ioreq!\n"); + ret = -EINVAL; + goto fail; + } + + ret = vhm_get_vm_info(vmid, &vm_info); + if (ret < 0) { + pr_err("Failed to get vm info for vmid %d\n", vmid); + goto client_fail; + } + + info->vcpu_num = vm_info.max_vcpu; + + ret = acrn_ioreq_attach_client(info->vhm_client_id, 0); + if (ret < 0) { + pr_err("Failed to attach vhm client %d!\n", + info->vhm_client_id); + goto client_fail; + } + mutex_init(&info->ioeventfds_lock); + info->vmid = vmid; + info->refcnt = 1; + INIT_LIST_HEAD(&info->ioeventfds); + + mutex_lock(&vhm_ioeventfds_mutex); + list_add(&info->list, &vhm_ioeventfd_clients); + mutex_unlock(&vhm_ioeventfds_mutex); + + pr_info("ACRN vhm ioeventfd init done!\n"); + return 0; +client_fail: + acrn_ioreq_destroy_client(info->vhm_client_id); +fail: + kfree(info); + return ret; +} + +void acrn_ioeventfd_deinit(uint16_t vmid) +{ + struct acrn_vhm_ioeventfd *p, *tmp; + struct vhm_ioeventfd_info *info = NULL; + + info = get_ioeventfd_info_by_vm(vmid); + if (!info) + return; + + put_ioeventfd_info(info); + + mutex_lock(&info->ioeventfds_lock); + list_for_each_entry_safe(p, tmp, &info->ioeventfds, list) + vhm_ioeventfd_shutdown(p); + mutex_unlock(&info->ioeventfds_lock); + + /* put one more as we count it in finding */ + put_ioeventfd_info(info); +} diff --git a/include/linux/vhm/vhm_eventfd.h b/include/linux/vhm/vhm_eventfd.h new file mode 100644 index 000000000000..3c73194cd1a1 --- /dev/null +++ b/include/linux/vhm/vhm_eventfd.h @@ -0,0 +1,10 @@ +#ifndef __ACRN_VHM_EVENTFD_H__ +#define __ACRN_VHM_EVENTFD_H__ + +/* ioeventfd APIs */ +struct acrn_ioeventfd; +int acrn_ioeventfd_init(int vmid); +int acrn_ioeventfd(int vmid, struct acrn_ioeventfd *args); +void acrn_ioeventfd_deinit(int vmid); + +#endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 063ebd5c7a93..4ab6b02aced8 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -110,6 +110,10 @@ #define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) #define IC_PM_SET_SSTATE_DATA _IC_ID(IC_ID, IC_ID_PM_BASE + 0x01) +/* VHM eventfd */ +#define IC_ID_EVENT_BASE 0x70UL +#define IC_EVENT_IOEVENTFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x00) + /** * struct vm_memseg - memory segment info for guest * @@ -216,4 +220,16 @@ struct api_version { uint32_t minor_version; }; +#define ACRN_IOEVENTFD_FLAG_PIO 0x01 +#define ACRN_IOEVENTFD_FLAG_DATAMATCH 0x02 +#define ACRN_IOEVENTFD_FLAG_DEASSIGN 0x04 +struct acrn_ioeventfd { + int32_t fd; + uint32_t flags; + uint64_t addr; + uint32_t len; + uint32_t reserved; + uint64_t data; +}; + #endif /* VHM_IOCTL_DEFS_H */ From fe156ecf5c62325fbd68a88cdf402b60bc2161d2 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Thu, 12 Jul 2018 15:40:52 +0800 Subject: [PATCH 0899/1276] vhm: add irqfd support for ACRN hypervisor service module irqfd which is based on eventfd, provides a pipe for injecting guest interrupt through a file description writing operation. Each irqfd registered by userspace can map a interrupt of the guest to eventfd, and a writing operation on one side of the eventfd will trigger the interrupt injection on vhm side. Tracked-On: projectacrn/acrn-hypervisor#1329 Signed-off-by: Shuo Liu Reviewed-by: Zhao Yakui Reviewed-by: Yu Wang --- drivers/char/vhm/vhm_dev.c | 11 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_irqfd.c | 381 +++++++++++++++++++++++++++++ include/linux/vhm/vhm_eventfd.h | 6 + include/linux/vhm/vhm_ioctl_defs.h | 8 + 5 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_irqfd.c diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 672b58a29a1f..4d76905e616d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -238,6 +238,7 @@ static long vhm_dev_ioctl(struct file *filep, } acrn_ioeventfd_init(vm->vmid); + acrn_irqfd_init(vm->vmid); pr_info("vhm: VM %d created\n", created_vm.vmid); break; @@ -280,6 +281,7 @@ static long vhm_dev_ioctl(struct file *filep, case IC_DESTROY_VM: { acrn_ioeventfd_deinit(vm->vmid); + acrn_irqfd_deinit(vm->vmid); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); @@ -660,6 +662,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_EVENT_IRQFD: { + struct acrn_irqfd args; + + if (copy_from_user(&args, (void *)ioctl_param, sizeof(args))) + return -EFAULT; + ret = acrn_irqfd(vm->vmid, &args); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 193f7692c9e3..4abfbfcba4aa 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1,2 +1,2 @@ subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror -obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o vhm_irqfd.o diff --git a/drivers/vhm/vhm_irqfd.c b/drivers/vhm/vhm_irqfd.c new file mode 100644 index 000000000000..b8c122d5ea1f --- /dev/null +++ b/drivers/vhm/vhm_irqfd.c @@ -0,0 +1,381 @@ +/* + * irqfd for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static LIST_HEAD(vhm_irqfd_clients); +static DEFINE_MUTEX(vhm_irqfds_mutex); +static ASYNC_DOMAIN_EXCLUSIVE(irqfd_domain); + +/* use internally to record properties of each irqfd */ +struct acrn_vhm_irqfd { + /* vhm_irqfd_info which this irqfd belong to */ + struct vhm_irqfd_info *info; + /* wait queue node */ + wait_queue_entry_t wait; + /* async shutdown work */ + struct work_struct shutdown; + /* eventfd of this irqfd */ + struct eventfd_ctx *eventfd; + /* list to link all ioventfd together */ + struct list_head list; + /* poll_table of this irqfd */ + poll_table pt; + /* msi to send when this irqfd triggerd */ + struct acrn_msi_entry msi; +}; + +/* instance to bind irqfds of each VM */ +struct vhm_irqfd_info { + struct list_head list; + int refcnt; + /* vmid of VM */ + uint16_t vmid; + /* workqueue for async shutdown work */ + struct workqueue_struct *wq; + + /* irqfds in this instance */ + struct list_head irqfds; + spinlock_t irqfds_lock; +}; + +static struct vhm_irqfd_info *get_irqfd_info_by_vm(uint16_t vmid) +{ + struct vhm_irqfd_info *info = NULL; + + mutex_lock(&vhm_irqfds_mutex); + list_for_each_entry(info, &vhm_irqfd_clients, list) { + if (info->vmid == vmid) { + info->refcnt++; + mutex_unlock(&vhm_irqfds_mutex); + return info; + } + } + mutex_unlock(&vhm_irqfds_mutex); + return NULL; +} + +static void put_irqfd_info(struct vhm_irqfd_info *info) +{ + mutex_lock(&vhm_irqfds_mutex); + info->refcnt--; + if (info->refcnt == 0) { + list_del(&info->list); + kfree(info); + } + mutex_unlock(&vhm_irqfds_mutex); +} + +static void vhm_irqfd_inject(struct acrn_vhm_irqfd *irqfd) +{ + struct vhm_irqfd_info *info = irqfd->info; + + vhm_inject_msi(info->vmid, irqfd->msi.msi_addr, + irqfd->msi.msi_data); +} + +/* + * Try to find if the irqfd still in list info->irqfds + * + * assumes info->irqfds_lock is held + */ +static bool vhm_irqfd_is_active(struct vhm_irqfd_info *info, + struct acrn_vhm_irqfd *irqfd) +{ + struct acrn_vhm_irqfd *_irqfd; + + list_for_each_entry(_irqfd, &info->irqfds, list) + if (_irqfd == irqfd) + return true; + + return false; +} + +/* + * Remove irqfd and free it. + * + * assumes info->irqfds_lock is held + */ +static void vhm_irqfd_shutdown(struct acrn_vhm_irqfd *irqfd) +{ + u64 cnt; + + /* remove from wait queue */ + list_del_init(&irqfd->list); + eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); + eventfd_ctx_put(irqfd->eventfd); + kfree(irqfd); +} + +static void vhm_irqfd_shutdown_work(struct work_struct *work) +{ + unsigned long flags; + struct acrn_vhm_irqfd *irqfd = + container_of(work, struct acrn_vhm_irqfd, shutdown); + struct vhm_irqfd_info *info = irqfd->info; + + spin_lock_irqsave(&info->irqfds_lock, flags); + if (vhm_irqfd_is_active(info, irqfd)) + vhm_irqfd_shutdown(irqfd); + spin_unlock_irqrestore(&info->irqfds_lock, flags); +} + +/* + * Called with wqh->lock held and interrupts disabled + */ +static int vhm_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, + int sync, void *key) +{ + struct acrn_vhm_irqfd *irqfd = + container_of(wait, struct acrn_vhm_irqfd, wait); + unsigned long poll_bits = (unsigned long)key; + struct vhm_irqfd_info *info = irqfd->info; + + if (poll_bits & POLLIN) + /* An event has been signaled, inject an interrupt */ + vhm_irqfd_inject(irqfd); + + if (poll_bits & POLLHUP) + /* async close eventfd as shutdown need hold wqh->lock */ + queue_work(info->wq, &irqfd->shutdown); + + return 0; +} + +static void vhm_irqfd_poll_func(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct acrn_vhm_irqfd *irqfd = + container_of(pt, struct acrn_vhm_irqfd, pt); + add_wait_queue(wqh, &irqfd->wait); +} + +static int acrn_irqfd_assign(struct vhm_irqfd_info *info, + struct acrn_irqfd *args) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct fd f; + struct eventfd_ctx *eventfd = NULL; + int ret = 0; + unsigned int events; + + irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL); + if (!irqfd) + return -ENOMEM; + + irqfd->info = info; + memcpy(&irqfd->msi, &args->msi, sizeof(args->msi)); + INIT_LIST_HEAD(&irqfd->list); + INIT_WORK(&irqfd->shutdown, vhm_irqfd_shutdown_work); + + f = fdget(args->fd); + if (!f.file) { + ret = -EBADF; + goto out; + } + + eventfd = eventfd_ctx_fileget(f.file); + if (IS_ERR(eventfd)) { + ret = PTR_ERR(eventfd); + goto fail; + } + + irqfd->eventfd = eventfd; + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd + */ + init_waitqueue_func_entry(&irqfd->wait, vhm_irqfd_wakeup); + init_poll_funcptr(&irqfd->pt, vhm_irqfd_poll_func); + + spin_lock_irq(&info->irqfds_lock); + + list_for_each_entry(tmp, &info->irqfds, list) { + if (irqfd->eventfd != tmp->eventfd) + continue; + /* This fd is used for another irq already. */ + ret = -EBUSY; + spin_unlock_irq(&info->irqfds_lock); + goto fail; + } + list_add_tail(&irqfd->list, &info->irqfds); + + spin_unlock_irq(&info->irqfds_lock); + + /* Check the pending event in this stage */ + events = f.file->f_op->poll(f.file, &irqfd->pt); + + if (events & POLLIN) + vhm_irqfd_inject(irqfd); + + fdput(f); + + return 0; +fail: + if (eventfd && !IS_ERR(eventfd)) + eventfd_ctx_put(eventfd); + + fdput(f); +out: + kfree(irqfd); + return ret; +} + +static int acrn_irqfd_deassign(struct vhm_irqfd_info *info, + struct acrn_irqfd *args) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct eventfd_ctx *eventfd; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + spin_lock_irq(&info->irqfds_lock); + + list_for_each_entry_safe(irqfd, tmp, &info->irqfds, list) { + if (irqfd->eventfd == eventfd) { + vhm_irqfd_shutdown(irqfd); + break; + } + } + + spin_unlock_irq(&info->irqfds_lock); + eventfd_ctx_put(eventfd); + + return 0; +} + +int acrn_irqfd(uint16_t vmid, struct acrn_irqfd *args) +{ + struct vhm_irqfd_info *info; + int ret; + + info = get_irqfd_info_by_vm(vmid); + if (!info) + return -ENOENT; + + if (args->flags & ACRN_IRQFD_FLAG_DEASSIGN) + ret = acrn_irqfd_deassign(info, args); + else + ret = acrn_irqfd_assign(info, args); + + put_irqfd_info(info); + return ret; +} + +int acrn_irqfd_init(uint16_t vmid) +{ + struct vhm_irqfd_info *info; + + info = get_irqfd_info_by_vm(vmid); + if (info) { + put_irqfd_info(info); + return -EEXIST; + } + + info = kzalloc(sizeof(struct vhm_irqfd_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->vmid = vmid; + info->refcnt = 1; + INIT_LIST_HEAD(&info->irqfds); + spin_lock_init(&info->irqfds_lock); + + info->wq = alloc_workqueue("acrn_irqfd-%d", 0, 0, vmid); + if (!info->wq) { + kfree(info); + return -ENOMEM; + } + + mutex_lock(&vhm_irqfds_mutex); + list_add(&info->list, &vhm_irqfd_clients); + mutex_unlock(&vhm_irqfds_mutex); + + pr_info("ACRN vhm irqfd init done!\n"); + return 0; +} + +void acrn_irqfd_deinit(uint16_t vmid) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct vhm_irqfd_info *info; + + info = get_irqfd_info_by_vm(vmid); + if (!info) + return; + put_irqfd_info(info); + + destroy_workqueue(info->wq); + + spin_lock_irq(&info->irqfds_lock); + list_for_each_entry_safe(irqfd, tmp, &info->irqfds, list) + vhm_irqfd_shutdown(irqfd); + spin_unlock_irq(&info->irqfds_lock); + + /* put one more to release it */ + put_irqfd_info(info); +} diff --git a/include/linux/vhm/vhm_eventfd.h b/include/linux/vhm/vhm_eventfd.h index 3c73194cd1a1..7ee5843eca0e 100644 --- a/include/linux/vhm/vhm_eventfd.h +++ b/include/linux/vhm/vhm_eventfd.h @@ -7,4 +7,10 @@ int acrn_ioeventfd_init(int vmid); int acrn_ioeventfd(int vmid, struct acrn_ioeventfd *args); void acrn_ioeventfd_deinit(int vmid); +/* irqfd APIs */ +struct acrn_irqfd; +int acrn_irqfd_init(uint16_t vmid); +int acrn_irqfd(uint16_t vmid, struct acrn_irqfd *args); +void acrn_irqfd_deinit(uint16_t vmid); + #endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 4ab6b02aced8..2007d58faa3f 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -113,6 +113,7 @@ /* VHM eventfd */ #define IC_ID_EVENT_BASE 0x70UL #define IC_EVENT_IOEVENTFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x00) +#define IC_EVENT_IRQFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x01) /** * struct vm_memseg - memory segment info for guest @@ -232,4 +233,11 @@ struct acrn_ioeventfd { uint64_t data; }; +#define ACRN_IRQFD_FLAG_DEASSIGN 0x01 +struct acrn_irqfd { + int32_t fd; + uint32_t flags; + struct acrn_msi_entry msi; +}; + #endif /* VHM_IOCTL_DEFS_H */ From e9d331eb66d3560a6bfa2e9066489d801fd08065 Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Thu, 27 Sep 2018 00:24:18 +0800 Subject: [PATCH 0900/1276] vhm: add ioctl for set/clear IRQ line ASSERT/DEASSERT/PULSE IRQ line ioctl commands will be deprecated. Tracked-On: projectacrn/acrn-hypervisor#861 Signed-off-by: Yonghua Huang Reviewed-by: Fei Li Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 9 +++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 17 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 4d76905e616d..0eb31d8d8535 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -418,6 +418,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_SET_IRQLINE: { + ret = hcall_set_irqline(vm->vmid, ioctl_param); + if (ret < 0) { + pr_err("vhm: failed to set irqline!\n"); + return -EFAULT; + } + break; + } + case IC_INJECT_MSI: { struct acrn_msi_entry msi; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 666f9aeb87ad..9a761022be08 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -142,6 +142,11 @@ inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq) return acrn_hypercall2(HC_PULSE_IRQLINE, vmid, irq); } +inline long hcall_set_irqline(unsigned long vmid, unsigned long op) +{ + return acrn_hypercall2(HC_SET_IRQLINE, vmid, op); +} + inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 2cd6172e8e1d..6b7bfb421301 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -86,6 +86,7 @@ #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) #define HC_VM_INTR_MONITOR _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x04) +#define HC_SET_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x05) /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x30UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 062196ab1194..7f8b6bcecd09 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -158,6 +158,7 @@ inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_set_irqline(unsigned long vmid, unsigned long op); inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf); inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 2007d58faa3f..fb7b154dbd6b 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -81,6 +81,7 @@ #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) #define IC_VM_INTR_MONITOR _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x04) +#define IC_SET_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x05) /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x30UL From 5892cf12e39c8c3377d02a5c29611b98ec2aa9c4 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Thu, 23 Aug 2018 07:29:24 +0000 Subject: [PATCH 0901/1276] sos: vhm: add hypercall to set guest vcpu registers DM will use this hypercall to set the BSP registers of UOS. To avoid the hypervisor involving for UOS boot. Tracked-On: #1231 Signed-off-by: Yin Fengwei Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 17 +++++++ include/linux/vhm/acrn_common.h | 71 ++++++++++++++++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 90 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 0eb31d8d8535..c6a97e8305fd 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -311,6 +311,23 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } + case IC_SET_VCPU_REGS: { + struct acrn_set_vcpu_regs asvr; + + if (copy_from_user(&asvr, (void *)ioctl_param, sizeof(asvr))) + return -EFAULT; + + ret = acrn_hypercall2(HC_SET_VCPU_REGS, vm->vmid, + virt_to_phys(&asvr)); + if (ret < 0) { + pr_err("vhm: failed to set bsp state of vm %ld!\n", + vm->vmid); + return -EFAULT; + } + + return ret; + } + case IC_SET_MEMSEG: { struct vm_memmap memmap; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index dfe89309fb17..69499245a994 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -273,6 +273,77 @@ struct acrn_create_vcpu { uint16_t pcpu_id; } __attribute__((aligned(8))); +struct acrn_gp_regs { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; +}; + +struct acrn_descriptor_ptr { + uint16_t limit; + uint64_t base; + uint16_t reserved[3]; +} __attribute__((packed)); + +struct acrn_vcpu_regs { + struct acrn_gp_regs gprs; + struct acrn_descriptor_ptr gdt; + struct acrn_descriptor_ptr idt; + + uint64_t rip; + uint64_t cs_base; + uint64_t cr0; + uint64_t cr4; + uint64_t cr3; + uint64_t ia32_efer; + uint64_t rflags; + uint64_t reserved_64[4]; + + uint32_t cs_ar; + uint32_t reserved_32[4]; + + /* don't change the order of following sel */ + uint16_t cs_sel; + uint16_t ss_sel; + uint16_t ds_sel; + uint16_t es_sel; + uint16_t fs_sel; + uint16_t gs_sel; + uint16_t ldt_sel; + uint16_t tr_sel; + + uint16_t reserved_16[4]; +}; + +/** + * @brief Info to set vcpu state + * + * the pamameter for HC_SET_VCPU_REGS + */ +struct acrn_set_vcpu_regs { + /** the virtual CPU ID for the VCPU */ + uint16_t vcpu_id; + + /** reserved space to make cpu_state aligned to 8 bytes */ + uint16_t reserved0[3]; + + /** the structure to hold vcpu state */ + struct acrn_vcpu_regs vcpu_regs; +} __attribute__((aligned(8))); + /** * @brief Info to set ioreq buffer for a created VM * diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 6b7bfb421301..c74f05fc9d47 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -78,6 +78,7 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_RESET_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_SET_VCPU_REGS _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index fb7b154dbd6b..73be2dde243d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -73,6 +73,7 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_RESET_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_SET_VCPU_REGS _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 592fdb4fac8caef817366a7b52947984f0defee1 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 16 Oct 2018 13:38:18 +0800 Subject: [PATCH 0902/1276] Kernel/Acrn: Use HYPERVISOR_CALLBACK_VECTOR for Acrn upcall vector Linux kernel uses the HYPERVISOR_CALLBACK_VECTOR for hypervisor upcall vector. And it is already used for Xen and HyperV. After Acrn hypervisor is detected, it will also use this defined vector as notify vector to kernel. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1325 Signed-off-by: Zhao Yakui --- arch/x86/acrn/acrn.c | 40 ++++++++++++++++++++++++++++++++ arch/x86/entry/entry_64.S | 5 ++++ arch/x86/include/asm/acrnhyper.h | 18 ++++++++++++++ arch/x86/include/asm/hardirq.h | 2 +- arch/x86/kernel/irq.c | 2 +- 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 arch/x86/include/asm/acrnhyper.h diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c index eea9db84ca89..12ebc4f93611 100644 --- a/arch/x86/acrn/acrn.c +++ b/arch/x86/acrn/acrn.c @@ -34,6 +34,10 @@ */ #include #include +#include +#include +#include +#include static uint32_t __init acrn_detect(void) { @@ -45,6 +49,9 @@ static void __init acrn_init_platform(void) #if defined(CONFIG_PCI_MSI) && defined(CONFIG_ACRN_VHM) pv_irq_ops.write_msi = acrn_write_msi_msg; #endif + + alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, + acrn_hv_callback_vector); } static void acrn_pin_vcpu(int cpu) @@ -63,6 +70,37 @@ static void __init acrn_init_mem_mapping(void) /* do nothing here now */ } + +static void (*acrn_intr_handler)(void); +/* + * Handler for ACRN_HV_CALLBACK. + */ +__visible void acrn_hv_vector_handler(struct pt_regs *regs) +{ + struct pt_regs *old_regs = set_irq_regs(regs); + + entering_ack_irq(); +#ifdef CONFIG_X86 + inc_irq_stat(irq_hv_callback_count); +#endif + + if (acrn_intr_handler) + acrn_intr_handler(); + + exiting_irq(); + set_irq_regs(old_regs); +} + +void acrn_setup_intr_irq(void (*handler)(void)) +{ + acrn_intr_handler = handler; +} + +void acrn_remove_intr_irq(void) +{ + acrn_intr_handler = NULL; +} + const struct hypervisor_x86 x86_hyper_acrn = { .name = "ACRN", .detect = acrn_detect, @@ -73,3 +111,5 @@ const struct hypervisor_x86 x86_hyper_acrn = { .init.init_mem_mapping = acrn_init_mem_mapping, }; EXPORT_SYMBOL(x86_hyper_acrn); +EXPORT_SYMBOL(acrn_setup_intr_irq); +EXPORT_SYMBOL(acrn_remove_intr_irq); diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index f95dcb209fdf..cdff14eafcd0 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -1147,6 +1147,11 @@ apicinterrupt3 HYPERV_STIMER0_VECTOR \ hv_stimer0_callback_vector hv_stimer0_vector_handler #endif /* CONFIG_HYPERV */ +#if IS_ENABLED(CONFIG_ACRN) +apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ + acrn_hv_callback_vector acrn_hv_vector_handler +#endif + idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK idtentry int3 do_int3 has_error_code=0 idtentry stack_segment do_stack_segment has_error_code=1 diff --git a/arch/x86/include/asm/acrnhyper.h b/arch/x86/include/asm/acrnhyper.h new file mode 100644 index 000000000000..1cfdb24de696 --- /dev/null +++ b/arch/x86/include/asm/acrnhyper.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_ACRNHYPER_H +#define _ASM_X86_ACRNHYPER_H + +#include +#include +#include +#include + +#ifdef CONFIG_ACRN +/* ACRN Hypervisor callback */ +void acrn_hv_callback_vector(void); + +void acrn_setup_intr_irq(void (*handler)(void)); +void acrn_remove_intr_irq(void); +#endif + +#endif diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h index d9069bb26c7f..5aa52d2c1dcd 100644 --- a/arch/x86/include/asm/hardirq.h +++ b/arch/x86/include/asm/hardirq.h @@ -37,7 +37,7 @@ typedef struct { #ifdef CONFIG_X86_MCE_AMD unsigned int irq_deferred_error_count; #endif -#if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN) +#if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN) || defined(CONFIG_ACRN) unsigned int irq_hv_callback_count; #endif #if IS_ENABLED(CONFIG_HYPERV) diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 59b5f2ea7c2f..6b3eaabeb019 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -134,7 +134,7 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%10u ", per_cpu(mce_poll_count, j)); seq_puts(p, " Machine check polls\n"); #endif -#if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN) +#if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN) || defined(CONFIG_ACRN) if (test_bit(HYPERVISOR_CALLBACK_VECTOR, system_vectors)) { seq_printf(p, "%*s: ", prec, "HYP"); for_each_online_cpu(j) From b31fe142fd107eb7161424da0c43841afebd714c Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 16 Oct 2018 13:38:22 +0800 Subject: [PATCH 0903/1276] VHM: Add one hypercall to configure the up-notifier interrupt vector Currently the acrn-hypervisor is using the PLATFORM_IPI vector to notify the sos_kernel. And then sos_kernel will handle the notification from acrn hypervisor in PLATFORM_IPI ISR. But as the PLATFORM_IPI ISR can be registered by the other modules, it will have the conflict when trying to register acrn intr ISR. So the HYPERVISOR_CALLBACK_VECTOR will be used instead. In order to switch the notification vector from PLATFORM_IPI to HYPERVISOR_CALLBACK_VECTOR, one API is added so that sos can configure the up-notifier interrrupt vector. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1325 Signed-off-by: Zhao Yakui --- drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 5 +++++ include/linux/vhm/vhm_hypercall.h | 1 + 3 files changed, 11 insertions(+) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 9a761022be08..f3348f7e3903 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -187,3 +187,8 @@ inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr) { return acrn_hypercall2(HC_VM_INTR_MONITOR, vmid, addr); } + +inline long hcall_set_callback_vector(unsigned long intr_vector) +{ + return acrn_hypercall1(HC_SET_CALLBACK_VECTOR, intr_vector); +} diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index c74f05fc9d47..c13257fcaaa1 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -69,6 +69,11 @@ #define HC_ID_GEN_BASE 0x0UL #define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x00) #define HC_SOS_OFFLINE_CPU _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x01) +/* this is the temporally added hypercall. + * after HYPERVISOR_CALLBACK_VECTOR is switched in both kernel and hypervisor, + * this will be removed. + */ +#define HC_SET_CALLBACK_VECTOR _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x02) /* VM management */ #define HC_ID_VM_BASE 0x10UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 7f8b6bcecd09..130069926786 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, inline long hcall_sos_offline_cpu(unsigned long cpu); inline long hcall_get_api_version(unsigned long api_version); +inline long hcall_set_callback_vector(unsigned long intr_vector); inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); From ceeac225964c8191872f057c818e189594832fa8 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 16 Oct 2018 13:38:25 +0800 Subject: [PATCH 0904/1276] VHM: Notify hypervisor to switch the up-noitifier Interrupt vector Currently the acrn-hypervisor is using the PLATFORM_IPI vector to notify the sos_kernel. And then sos_kernel will handle the notification from acrn hypervisor in PLATFORM_IPI ISR. But as the PLATFORM_IPI ISR can be registered by the other modules, it will have the conflict when trying to register acrn intr ISR. So the HYPERVISOR_CALLBACK_VECTOR will be used instead. This patch tries to notify the hypervisor so that the hypervisor notifies the kernel by using HYPERVISOR_CALLBACK_VECTOR instead of PLATFORM_IPI. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1325 Signed-off-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index c6a97e8305fd..f730467ca8d9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -86,6 +86,7 @@ #include #include +#include #define DEVICE_NAME "acrn_vhm" #define CLASS_NAME "vhm" @@ -835,13 +836,19 @@ static int __init vhm_init(void) } pr_info("register IPI handler\n"); tasklet_init(&vhm_io_req_tasklet, io_req_tasklet, 0); - if (x86_platform_ipi_callback) { - pr_warn("vhm: ipi callback was occupied\n"); - return -EINVAL; + + if (hcall_set_callback_vector(HYPERVISOR_CALLBACK_VECTOR)) { + if (x86_platform_ipi_callback) { + pr_warn("vhm: ipi callback was occupied\n"); + return -EINVAL; + } + local_irq_save(flag); + x86_platform_ipi_callback = vhm_intr_handler; + local_irq_restore(flag); + } + else { + acrn_setup_intr_irq(vhm_intr_handler); } - local_irq_save(flag); - x86_platform_ipi_callback = vhm_intr_handler; - local_irq_restore(flag); if (sysfs_create_group(&vhm_device->kobj, &vhm_attr_group)) { pr_warn("vhm: sysfs create failed\n"); @@ -854,6 +861,7 @@ static int __init vhm_init(void) static void __exit vhm_exit(void) { tasklet_kill(&vhm_io_req_tasklet); + acrn_remove_intr_irq(); device_destroy(vhm_class, MKDEV(major, 0)); class_unregister(vhm_class); class_destroy(vhm_class); From 084d710fc377bf1d32d67c000590d9d1ab37d375 Mon Sep 17 00:00:00 2001 From: Saranya Gopal Date: Wed, 12 Sep 2018 01:03:57 +0530 Subject: [PATCH 0905/1276] usbcore: Select UAC3 configuration for audio if present USB audio class 3.0 specification introduced many significant changes like - new power domains, support for LPM/L1 - new cluster descriptor - new high capability and class-specific string descriptors - BADD profiles - ... and many other things (check spec from link below: http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip) Now that UAC3 is supported in linux, choose UAC3 configuration for audio if the device supports it. Selecting this configuration will enable the system to save power by leveraging the new power domains and LPM L1 capability and also support new codec types and data formats for consumer audio applications. Signed-off-by: Saranya Gopal Reviewed-by: Felipe Balbi --- drivers/usb/core/generic.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index bc8242bc4564..df38d5a32e95 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -21,6 +21,7 @@ #include #include +#include #include "usb.h" static inline const char *plural(int n) @@ -42,6 +43,16 @@ static int is_activesync(struct usb_interface_descriptor *desc) && desc->bInterfaceProtocol == 1; } +static int is_audio(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_AUDIO; +} + +static int is_uac3_config(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceProtocol == UAC_VERSION_3; +} + int usb_choose_configuration(struct usb_device *udev) { int i; @@ -121,6 +132,22 @@ int usb_choose_configuration(struct usb_device *udev) #endif } + /* + * Select first configuration as default for audio so that + * devices that don't comply with UAC3 protocol are supported. + * But, still iterate through other configurations and + * select UAC3 compliant config if present. + */ + if (i == 0 && num_configs > 1 && desc && is_audio(desc)) { + best = c; + continue; + } + + if (i > 0 && desc && is_audio(desc) && is_uac3_config(desc)) { + best = c; + break; + } + /* From the remaining configs, choose the first one whose * first interface is for a non-vendor-specific class. * Reason: Linux is more likely to have a class driver From 9f55bce058f0a2c7be233dffcf88245df86b27a3 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Wed, 10 Oct 2018 14:08:56 -0700 Subject: [PATCH 0906/1276] usb: dwc3: Fix NULL pointer exception in dwc3_pci_remove() In dwc3_pci_quirks() function, gpiod lookup table is only registered for baytrail SOC. But in dwc3_pci_remove(), we try to unregistered it without any checks. This leads to NULL pointer de-reference exception in gpiod_remove_lookup_table() when unloading the module. This patch fixes this issue. Signed-off-by: Kuppuswamy Sathyanarayanan --- drivers/usb/dwc3/dwc3-pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 1286076a8890..842795856bf4 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -283,8 +283,10 @@ static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) static void dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *dwc = pci_get_drvdata(pci); + struct pci_dev *pdev = dwc->pci; - gpiod_remove_lookup_table(&platform_bytcr_gpios); + if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) + gpiod_remove_lookup_table(&platform_bytcr_gpios); #ifdef CONFIG_PM cancel_work_sync(&dwc->wakeup_work); #endif From 8e0a1a6191a1282dcc56da32fe7e09a54885a232 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Jun 2011 12:41:46 -0700 Subject: [PATCH 0907/1276] ANDROID: netfilter: xt_qtaguid: add qtaguid matching module This module allows tracking stats at the socket level for given UIDs. It replaces xt_owner. If the --uid-owner is not specified, it will just count stats based on who the skb belongs to. This will even happen on incoming skbs as it looks into the skb via xt_socket magic to see who owns it. If an skb is lost, it will be assigned to uid=0. To control what sockets of what UIDs are tagged by what, one uses: echo t $sock_fd $accounting_tag $the_billed_uid \ > /proc/net/xt_qtaguid/ctrl So whenever an skb belongs to a sock_fd, it will be accounted against $the_billed_uid and matching stats will show up under the uid with the given $accounting_tag. Because the number of allocations for the stats structs is not that big: ~500 apps * 32 per app we'll just do it atomic. This avoids walking lists many times, and the fancy worker thread handling. Slabs will grow when needed later. It use netdevice and inetaddr notifications instead of hooks in the core dev code to track when a device comes and goes. This removes the need for exposed iface_stat.h. Put procfs dirs in /proc/net/xt_qtaguid/ ctrl stats iface_stat//... The uid stats are obtainable in ./stats. Change-Id: I01af4fd91c8de651668d3decb76d9bdc1e343919 Signed-off-by: JP Abgrall [AmitP: Folded following android-4.9 commit changes into this patch e5d798684a71 ("ANDROID: netfilter: qtaguid: initialize a local var to keep compiler happy")] Signed-off-by: Amit Pundir --- include/linux/android_aid.h | 2 + include/linux/netfilter/xt_qtaguid.h | 13 + net/netfilter/Kconfig | 18 + net/netfilter/Makefile | 1 + net/netfilter/xt_qtaguid.c | 2796 ++++++++++++++++++++++++++ net/netfilter/xt_qtaguid_internal.h | 330 +++ net/netfilter/xt_qtaguid_print.c | 556 +++++ net/netfilter/xt_qtaguid_print.h | 120 ++ 8 files changed, 3836 insertions(+) create mode 100644 include/linux/netfilter/xt_qtaguid.h create mode 100644 net/netfilter/xt_qtaguid.c create mode 100644 net/netfilter/xt_qtaguid_internal.h create mode 100644 net/netfilter/xt_qtaguid_print.c create mode 100644 net/netfilter/xt_qtaguid_print.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index 3d7a5ead1200..6f1fa1792dfc 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -22,5 +22,7 @@ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) #define AID_NET_ADMIN KGIDT_INIT(3005) +#define AID_NET_BW_STATS KGIDT_INIT(3006) /* read bandwidth statistics */ +#define AID_NET_BW_ACCT KGIDT_INIT(3007) /* change bandwidth statistics accounting */ #endif diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h new file mode 100644 index 000000000000..ca60fbdec2f3 --- /dev/null +++ b/include/linux/netfilter/xt_qtaguid.h @@ -0,0 +1,13 @@ +#ifndef _XT_QTAGUID_MATCH_H +#define _XT_QTAGUID_MATCH_H + +/* For now we just replace the xt_owner. + * FIXME: make iptables aware of qtaguid. */ +#include + +#define XT_QTAGUID_UID XT_OWNER_UID +#define XT_QTAGUID_GID XT_OWNER_GID +#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET +#define xt_qtaguid_match_info xt_owner_match_info + +#endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 4bde51a5902e..1b371f6cdedb 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1417,6 +1417,8 @@ config NETFILTER_XT_MATCH_OWNER based on who created the socket: the user or group. It is also possible to check whether a socket actually exists. + Conflicts with '"quota, tag, uid" match' + config NETFILTER_XT_MATCH_POLICY tristate 'IPsec "policy" match support' depends on XFRM @@ -1450,6 +1452,22 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QTAGUID + bool '"quota, tag, owner" match and stats support' + depends on NETFILTER_XT_MATCH_SOCKET + depends on NETFILTER_XT_MATCH_OWNER=n + help + This option replaces the `owner' match. In addition to matching + on uid, it keeps stats based on a tag assigned to a socket. + The full tag is comprised of a UID and an accounting tag. + The tags are assignable to sockets from user space (e.g. a download + manager can assign the socket to another UID for accounting). + Stats and control are done via /proc/net/xt_qtaguid/. + It replaces owner as it takes the same arguments, but should + really be recognized by the iptables tool. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_QUOTA tristate '"quota" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9c87ed55eba7..f2c701e55b1c 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -190,6 +190,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CGROUP) += xt_cgroup.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c new file mode 100644 index 000000000000..3d3928291efb --- /dev/null +++ b/net/netfilter/xt_qtaguid.c @@ -0,0 +1,2796 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ +#define XT_SOCKET_SUPPORTED_HOOKS \ + ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) + + +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; + +static unsigned int proc_iface_perms = S_IRUGO; +module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_stats_file; +static unsigned int proc_stats_perms = S_IRUGO; +module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_ctrl_file; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; +#else +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; +#endif +module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; +static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; +#else +/* 0 means, don't limit anybody */ +static gid_t proc_stats_readall_gid; +static gid_t proc_ctrl_write_gid; +#endif +module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, + S_IRUGO | S_IWUSR); +module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, + S_IRUGO | S_IWUSR); + +/* + * Limit the number of active tags (via socket tags) for a given UID. + * Multiple processes could share the UID. + */ +static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; +module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); + +/* + * After the kernel has initiallized this module, it is still possible + * to make it passive. + * Setting passive to Y: + * - the iface stats handling will not act on notifications. + * - iptables matches will never match. + * - ctrl commands silently succeed. + * - stats are always empty. + * This is mostly usefull when a bug is suspected. + */ +static bool module_passive; +module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); + +/* + * Control how qtaguid data is tracked per proc/uid. + * Setting tag_tracking_passive to Y: + * - don't create proc specific structs to track tags + * - don't check that active tag stats exceed some limits. + * - don't clean up socket tags on process exits. + * This is mostly usefull when a bug is suspected. + */ +static bool qtu_proc_handling_passive; +module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, + S_IRUGO | S_IWUSR); + +#define QTU_DEV_NAME "xt_qtaguid" + +uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; +module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); + +/*---------------------------------------------------------------------------*/ +static const char *iface_stat_procdirname = "iface_stat"; +static struct proc_dir_entry *iface_stat_procdir; +static const char *iface_stat_all_procfilename = "iface_stat_all"; +static struct proc_dir_entry *iface_stat_all_procfile; + +/* + * Ordering of locks: + * outer locks: + * iface_stat_list_lock + * sock_tag_list_lock + * inner locks: + * uid_tag_data_tree_lock + * tag_counter_set_list_lock + * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock + * is acquired. + * + * Call tree with all lock holders as of 2011-09-25: + * + * iface_stat_all_proc_read() + * iface_stat_list_lock + * (struct iface_stat) + * + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * (sock_tag_tree) + * (struct proc_qtu_data->sock_tag_list) + * prdebug_full_state() + * sock_tag_list_lock + * (sock_tag_tree) + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * (proc_qtu_data_tree) + * iface_stat_list_lock + * + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * + * qtudev_open() + * uid_tag_data_tree_lock + * + * qtudev_release() + * sock_tag_data_list_lock + * uid_tag_data_tree_lock + * prdebug_full_state() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * iface_stat_list_lock + * + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * struct iface_stat->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() + * sock_tag_list_lock + * (sock_tag_tree) + * get_tag_ref() + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * uid_tag_data_tree_lock + * (proc_qtu_data_tree) + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * + */ +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); + +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + +static struct rb_root uid_tag_data_tree = RB_ROOT; +static DEFINE_SPINLOCK(uid_tag_data_tree_lock); + +static struct rb_root proc_qtu_data_tree = RB_ROOT; +/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ + +static struct qtaguid_event_counts qtu_events; +/*----------------------------------------------*/ +static bool can_manipulate_uids(void) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) + || in_egroup_p(proc_ctrl_write_gid); +} + +static bool can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) + || in_egroup_p(proc_stats_readall_gid); +} + +static inline void dc_add_byte_packets(struct data_counters *counters, int set, + enum ifs_tx_rx direction, + enum ifs_proto ifs_proto, + int bytes, + int packets) +{ + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; +} + +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct tag_node *data = rb_entry(node, struct tag_node, node); + int result; + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " node=%p data=%p\n", tag, node, data); + result = tag_compare(tag, data->tag); + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " data.tag=0x%llx (uid=%u) res=%d\n", + tag, data->tag, get_uid_from_tag(data->tag), result); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct tag_node *this = rb_entry(*new, struct tag_node, + node); + int result = tag_compare(data->tag, this->tag); + RB_DEBUG("qtaguid: %s(): tag=0x%llx" + " (uid=%u)\n", __func__, + this->tag, + get_uid_from_tag(this->tag)); + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + +} + +static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_ref, tn.node); +} + +static struct sock_tag *sock_tag_tree_search(struct rb_root *root, + const struct sock *sk) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); + if (sk < data->sk) + node = node->rb_left; + else if (sk > data->sk) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); + parent = *new; + if (data->sk < this->sk) + new = &((*new)->rb_left); + else if (data->sk > this->sk) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); +} + +static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) +{ + struct rb_node *node; + struct sock_tag *st_entry; + + node = rb_first(st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: %s(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, + st_entry->sk, + st_entry->tag, + get_uid_from_tag(st_entry->tag)); + rb_erase(&st_entry->sock_node, st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } +} + +static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, + const pid_t pid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct proc_qtu_data *data = rb_entry(node, + struct proc_qtu_data, + node); + if (pid < data->pid) + node = node->rb_left; + else if (pid > data->pid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct proc_qtu_data *this = rb_entry(*new, + struct proc_qtu_data, + node); + parent = *new; + if (data->pid < this->pid) + new = &((*new)->rb_left); + else if (data->pid > this->pid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void uid_tag_data_tree_insert(struct uid_tag_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct uid_tag_data *this = rb_entry(*new, + struct uid_tag_data, + node); + parent = *new; + if (data->uid < this->uid) + new = &((*new)->rb_left); + else if (data->uid > this->uid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, + uid_t uid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct uid_tag_data *data = rb_entry(node, + struct uid_tag_data, + node); + if (uid < data->uid) + node = node->rb_left; + else if (uid > data->uid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +/* + * Allocates a new uid_tag_data struct if needed. + * Returns a pointer to the found or allocated uid_tag_data. + * Returns a PTR_ERR on failures, and lock is not held. + * If found is not NULL: + * sets *found to true if not allocated. + * sets *found to false if allocated. + */ +struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) +{ + struct uid_tag_data *utd_entry; + + /* Look for top level uid_tag_data for the UID */ + utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); + DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); + + if (found_res) + *found_res = utd_entry; + if (utd_entry) + return utd_entry; + + utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); + if (!utd_entry) { + pr_err("qtaguid: get_uid_data(%u): " + "tag data alloc failed\n", uid); + return ERR_PTR(-ENOMEM); + } + + utd_entry->uid = uid; + utd_entry->tag_ref_tree = RB_ROOT; + uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); + DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); + return utd_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *new_tag_ref(tag_t new_tag, + struct uid_tag_data *utd_entry) +{ + struct tag_ref *tr_entry; + int res; + + if (utd_entry->num_active_tags + 1 > max_sock_tags) { + pr_info("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc quota exceeded. max=%d\n", + new_tag, max_sock_tags); + res = -EMFILE; + goto err_res; + + } + + tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); + if (!tr_entry) { + pr_err("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc failed\n", + new_tag); + res = -ENOMEM; + goto err_res; + } + tr_entry->tn.tag = new_tag; + /* tr_entry->num_sock_tags handled by caller */ + utd_entry->num_active_tags++; + tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " + " inserted new tag ref %p\n", + new_tag, tr_entry); + return tr_entry; + +err_res: + return ERR_PTR(res); +} + +static struct tag_ref *lookup_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + bool found_utd; + uid_t uid = get_uid_from_tag(full_tag); + + DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", + full_tag, uid); + + utd_entry = get_uid_data(uid, &found_utd); + if (IS_ERR_OR_NULL(utd_entry)) { + if (utd_res) + *utd_res = utd_entry; + return NULL; + } + + tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *get_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + + DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", + full_tag); + spin_lock_bh(&uid_tag_data_tree_lock); + tr_entry = lookup_tag_ref(full_tag, &utd_entry); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + if (!tr_entry) + tr_entry = new_tag_ref(full_tag, utd_entry); + + spin_unlock_bh(&uid_tag_data_tree_lock); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Checks and maybe frees the UID Tag Data entry */ +static void put_utd_entry(struct uid_tag_data *utd_entry) +{ + /* Are we done with the UID tag data entry? */ + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && + !utd_entry->num_pqd) { + DR_DEBUG("qtaguid: %s(): " + "erase utd_entry=%p uid=%u " + "by pid=%u tgid=%u uid=%u\n", __func__, + utd_entry, utd_entry->uid, + current->pid, current->tgid, current_fsuid()); + BUG_ON(utd_entry->num_active_tags); + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } else { + DR_DEBUG("qtaguid: %s(): " + "utd_entry=%p still has %d tags %d proc_qtu_data\n", + __func__, utd_entry, utd_entry->num_active_tags, + utd_entry->num_pqd); + BUG_ON(!(utd_entry->num_active_tags || + utd_entry->num_pqd)); + } +} + +/* + * If no sock_tags are using this tag_ref, + * decrements refcount of utd_entry, removes tr_entry + * from utd_entry->tag_ref_tree and frees. + */ +static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, + struct uid_tag_data *utd_entry) +{ + DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, + tr_entry, tr_entry->tn.tag, + get_uid_from_tag(tr_entry->tn.tag)); + if (!tr_entry->num_sock_tags) { + BUG_ON(!utd_entry->num_active_tags); + utd_entry->num_active_tags--; + rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); + kfree(tr_entry); + } +} + +static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) +{ + struct rb_node *node; + struct tag_ref *tr_entry; + tag_t acct_tag; + + DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, + full_tag, get_uid_from_tag(full_tag)); + acct_tag = get_atag_from_tag(full_tag); + node = rb_first(&utd_entry->tag_ref_tree); + while (node) { + tr_entry = rb_entry(node, struct tag_ref, tn.node); + node = rb_next(node); + if (!acct_tag || tr_entry->tn.tag == full_tag) + free_tag_ref_from_utd_entry(tr_entry, utd_entry); + } +} + +static int read_proc_u64(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + uint64_t value; + char *p = page; + uint64_t *iface_entry = data; + + if (!data) + return 0; + + value = *iface_entry; + p += sprintf(p, "%llu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + bool *bool_entry = data; + + if (!data) + return 0; + + value = *bool_entry; + p += sprintf(p, "%u\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%u)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ +static struct iface_stat *get_iface_entry(const char *ifname) +{ + struct iface_stat *iface_entry; + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("qtaguid: iface_stat: get() NULL device name\n"); + return NULL; + } + + /* Iterate over interfaces */ + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(ifname, iface_entry->ifname)) + goto done; + } + iface_entry = NULL; +done: + return iface_entry; +} + +static int iface_stat_all_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, + int *eof, void *data) +{ + char *outp = page; + int item_index = 0; + int len; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + CT_DEBUG("qtaguid:proc iface_stat_all " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* + * This lock will prevent iface_stat_update() from changing active, + * and in turn prevent an interface from unregistering itself. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (item_index++ < items_to_skip) + continue; + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + len = snprintf(outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals[IFS_RX].bytes, + iface_entry->totals[IFS_RX].packets, + iface_entry->totals[IFS_TX].bytes, + iface_entry->totals[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets); + if (len >= char_count) { + spin_unlock_bh(&iface_stat_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return outp - page; +} + +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* + * Will set the entry's active state, and + * update the net_dev accordingly also. + */ +static void _iface_stat_set_active(struct iface_stat *entry, + struct net_device *net_dev, + bool activate) +{ + if (activate) { + entry->net_dev = net_dev; + entry->active = true; + IF_DEBUG("qtaguid: %s(%s): " + "enable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + } else { + entry->active = false; + entry->net_dev = NULL; + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + + } +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(struct net_device *net_dev) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", net_dev->name); + return NULL; + } + new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", net_dev->name); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->tag_stat_tree = RB_ROOT; + _iface_stat_set_active(new_iface, net_dev, true); + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); + _iface_stat_set_active(new_iface, net_dev, false); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + +static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, + struct iface_stat *iface) +{ + struct rtnl_link_stats64 dev_stats, *stats; + bool stats_rewound; + + stats = dev_get_stats(net_dev, &dev_stats); + /* No empty packets */ + stats_rewound = + (stats->rx_bytes < iface->last_known[IFS_RX].bytes) + || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); + + IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " + "bytes rx/tx=%llu/%llu " + "active=%d last_known=%d " + "stats_rewound=%d\n", __func__, + net_dev ? net_dev->name : "?", + iface, net_dev, + stats->rx_bytes, stats->tx_bytes, + iface->active, iface->last_known_valid, stats_rewound); + + if (iface->active && iface->last_known_valid && stats_rewound) { + pr_warn_once("qtaguid: iface_stat: %s(%s): " + "iface reset its stats unexpectedly\n", __func__, + net_dev->name); + + iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; + iface->totals[IFS_TX].packets += + iface->last_known[IFS_TX].packets; + iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; + iface->totals[IFS_RX].packets += + iface->last_known[IFS_RX].packets; + iface->last_known_valid = false; + IF_DEBUG("qtaguid: %s(%s): iface=%p " + "used last known bytes rx/tx=%llu/%llu\n", __func__, + iface->ifname, iface, iface->last_known[IFS_RX].bytes, + iface->last_known[IFS_TX].bytes); + } +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +static void iface_stat_create(struct net_device *net_dev, + struct in_ifaddr *ifa) +{ + struct in_device *in_dev = NULL; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct iface_stat *new_iface; + + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create(): no net dev\n"); + return; + } + + ifname = net_dev->name; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } + } + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", + ifname); + goto done_put; + } + ipaddr = ifa->ifa_local; + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !ipv4_is_loopback(ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI4\n", __func__, + entry->ifname, activate, &ipaddr); + goto done_unlock_put; + } else if (ipv4_is_loopback(ipaddr)) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +static void iface_stat_create_ipv6(struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); + return; + } + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", + ifname); + return; + } + + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI6c\n", __func__, + entry->ifname, activate, &ifa->addr); + goto done_unlock_put; + } else if (addr_type & IPV6_ADDR_LOOPBACK) { + IF_DEBUG("qtaguid: %s(%s): " + "ignore loopback dev. ip=%pI6c\n", __func__, + ifname, &ifa->addr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); +} + +static struct sock_tag *get_sock_stat_nl(const struct sock *sk) +{ + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + return sock_tag_tree_search(&sock_tag_tree, sk); +} + +static struct sock_tag *get_sock_stat(const struct sock *sk) +{ + struct sock_tag *sock_tag_entry; + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); + if (!sk) + return NULL; + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(sk); + spin_unlock_bh(&sock_tag_list_lock); + return sock_tag_entry; +} + +static void +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) +{ + switch (proto) { + case IPPROTO_TCP: + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); + break; + case IPPROTO_UDP: + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); + break; + case IPPROTO_IP: + default: + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); + break; + } +} + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +static void iface_stat_update(struct net_device *net_dev, bool stash_only) +{ + struct rtnl_link_stats64 dev_stats, *stats; + struct iface_stat *entry; + + stats = dev_get_stats(net_dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(net_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + net_dev->name, entry); + if (!entry->active) { + IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + if (stash_only) { + entry->last_known[IFS_TX].bytes = stats->tx_bytes; + entry->last_known[IFS_TX].packets = stats->tx_packets; + entry->last_known[IFS_RX].bytes = stats->rx_bytes; + entry->last_known[IFS_RX].packets = stats->rx_packets; + entry->last_known_valid = true; + IF_DEBUG("qtaguid: %s(%s): " + "dev stats stashed rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + entry->totals[IFS_TX].bytes += stats->tx_bytes; + entry->totals[IFS_TX].packets += stats->tx_packets; + entry->totals[IFS_RX].bytes += stats->rx_bytes; + entry->totals[IFS_RX].packets += stats->rx_packets; + /* We don't need the last_known[] anymore */ + entry->last_known_valid = false; + _iface_stat_set_active(entry, net_dev, false); + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + +static void tag_stat_update(struct tag_stat *tag_entry, + enum ifs_tx_rx direction, int proto, int bytes) +{ + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); + if (tag_entry->parent_counters) + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); +} + +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within + * the interface. + * iface_entry->tag_stat_list_lock should be held. + */ +static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, + tag_t tag) +{ + struct tag_stat *new_tag_stat_entry = NULL; + IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" + " (uid=%u)\n", __func__, + iface_entry, tag, get_uid_from_tag(tag)); + new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + if (!new_tag_stat_entry) { + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); + goto done; + } + new_tag_stat_entry->tn.tag = tag; + tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); +done: + return new_tag_stat_entry; +} + +static void if_tag_stat_update(const char *ifname, uid_t uid, + const struct sock *sk, enum ifs_tx_rx direction, + int proto, int bytes) +{ + struct tag_stat *tag_stat_entry; + tag_t tag, acct_tag; + tag_t uid_tag; + struct data_counters *uid_tag_counters; + struct sock_tag *sock_tag_entry; + struct iface_stat *iface_entry; + struct tag_stat *new_tag_stat = NULL; + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", + ifname, uid, sk, direction, proto, bytes); + + + iface_entry = get_iface_entry(ifname); + if (!iface_entry) { + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); + return; + } + /* It is ok to process data when an iface_entry is inactive */ + + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); + + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ + sock_tag_entry = get_sock_stat(sk); + if (sock_tag_entry) { + tag = sock_tag_entry->tag; + acct_tag = get_atag_from_tag(tag); + uid_tag = get_utag_from_tag(tag); + } else { + acct_tag = make_atag_from_value(0); + tag = combine_atag_with_uid(acct_tag, uid); + uid_tag = make_tag_from_uid(uid); + } + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%u) in ife=%p\n", + tag, get_uid_from_tag(tag), iface_entry); + /* Loop over tag list under this interface for {acct_tag,uid_tag} */ + spin_lock_bh(&iface_entry->tag_stat_list_lock); + + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + tag); + if (tag_stat_entry) { + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ + tag_stat_update(tag_stat_entry, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + return; + } + + /* Loop over tag list under this interface for {0,uid_tag} */ + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + uid_tag); + if (!tag_stat_entry) { + /* Here: the base uid_tag did not exist */ + /* + * No parent counters. So + * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. + */ + new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + uid_tag_counters = &new_tag_stat->counters; + } else { + uid_tag_counters = &tag_stat_entry->counters; + } + + if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ + new_tag_stat = create_if_tag_stat(iface_entry, tag); + new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); + } + tag_stat_update(new_tag_stat, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); +} + +static int iface_netdev_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + struct net_device *dev = ptr; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx/%s netdev=%p->name=%s\n", + event, netdev_evt_str(event), dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + iface_stat_create(dev, NULL); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block iface_netdev_notifier_blk = { + .notifier_call = iface_netdev_event_handler, +}; + +static struct notifier_block iface_inetaddr_notifier_blk = { + .notifier_call = iface_inetaddr_event_handler, +}; + +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + +static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) +{ + int err; + + iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); + if (!iface_stat_procdir) { + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); + err = -1; + goto err; + } + + iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_all_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_entry; + } + iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + + + err = register_netdevice_notifier(&iface_netdev_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); + goto err_zap_all_stats_entry; + } + err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; + } + return 0; + +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); +err_unreg_nd: + unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entry: + remove_proc_entry(iface_stat_all_procfilename, parent_procdir); +err_zap_entry: + remove_proc_entry(iface_stat_procdirname, parent_procdir); +err: + return err; +} + +static struct sock *qtaguid_find_sk(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct sock *sk; + unsigned int hook_mask = (1 << par->hooknum); + + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + par->hooknum, par->family); + + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ + if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) + return NULL; + + switch (par->family) { + case NFPROTO_IPV6: + sk = xt_socket_get6_sk(skb, par); + break; + case NFPROTO_IPV4: + sk = xt_socket_get4_sk(skb, par); + break; + default: + return NULL; + } + + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + * Not fixed in 3.0-r3 :( + */ + if (sk) { + MT_DEBUG("qtaguid: %p->sk_proto=%u " + "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + if (sk->sk_state == TCP_TIME_WAIT) { + xt_socket_put_sk(sk); + sk = NULL; + } + } + return sk; +} + +static void account_for_uid(const struct sk_buff *skb, + const struct sock *alternate_sk, uid_t uid, + struct xt_action_param *par) +{ + const struct net_device *el_dev; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + } else if (unlikely(!el_dev->name)) { + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); + } else { + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); + + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + par->in ? IFS_RX : IFS_TX, + ip_hdr(skb)->protocol, skb->len); + } +} + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct file *filp; + bool got_sock = false; + struct sock *sk; + uid_t sock_uid; + bool res; + + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + par->hooknum, skb, par->in, par->out, par->family); + + atomic64_inc(&qtu_events.match_calls); + if (skb == NULL) { + res = (info->match ^ info->invert) == 0; + goto ret_res; + } + + sk = skb->sk; + + if (sk == NULL) { + /* + * A missing sk->sk_socket happens when packets are in-flight + * and the matching socket is already closed and gone. + */ + sk = qtaguid_find_sk(skb, par); + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + if (sk) + atomic64_inc(&qtu_events.match_found_sk_in_ct); + else + atomic64_inc(&qtu_events.match_found_no_sk_in_ct); + } else { + atomic64_inc(&qtu_events.match_found_sk); + } + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + if (sk != NULL) { + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? filp->f_cred->fsuid : -1); + } + + if (sk == NULL || sk->sk_socket == NULL) { + /* + * Here, the qtaguid_find_sk() using connection tracking + * couldn't find the owner, so for now we just count them + * against the system. + */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not + * requested. + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, 0, par); + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + par->hooknum, + sk ? sk->sk_socket : NULL); + res = (info->match ^ info->invert) == 0; + atomic64_inc(&qtu_events.match_no_sk); + goto put_sock_ret_res; + } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { + res = false; + goto put_sock_ret_res; + } + filp = sk->sk_socket->file; + if (filp == NULL) { + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + account_for_uid(skb, sk, 0, par); + res = ((info->match ^ info->invert) & + (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + atomic64_inc(&qtu_events.match_no_sk_file); + goto put_sock_ret_res; + } + sock_uid = filp->f_cred->fsuid; + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, sock_uid, par); + + /* + * The following two tests fail the match when: + * id not in range AND no inverted condition requested + * or id in range AND inverted condition requested + * Thus (!a && b) || (a && !b) == a ^ b + */ + if (info->match & XT_QTAGUID_UID) + if ((filp->f_cred->fsuid >= info->uid_min && + filp->f_cred->fsuid <= info->uid_max) ^ + !(info->invert & XT_QTAGUID_UID)) { + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + if (info->match & XT_QTAGUID_GID) + if ((filp->f_cred->fsgid >= info->gid_min && + filp->f_cred->fsgid <= info->gid_max) ^ + !(info->invert & XT_QTAGUID_GID)) { + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + res = true; + +put_sock_ret_res: + if (got_sock) + xt_socket_put_sk(sk); +ret_res: + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + return res; +} + +#ifdef DDEBUG +/* This function is not in xt_qtaguid_print.c because of locks visibility */ +static void prdebug_full_state(int indent_level, const char *fmt, ...) +{ + va_list args; + char *fmt_buff; + char *buff; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + fmt_buff = kasprintf(GFP_ATOMIC, + "qtaguid: %s(): %s {\n", __func__, fmt); + BUG_ON(!fmt_buff); + va_start(args, fmt); + buff = kvasprintf(GFP_ATOMIC, + fmt_buff, args); + BUG_ON(!buff); + pr_debug("%s", buff); + kfree(fmt_buff); + kfree(buff); + va_end(args); + + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); + + pr_debug("qtaguid: %s(): }\n", __func__); +} +#else +static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +#endif + +/* + * Procfs reader to get all active socket tags using style "1)" as described in + * fs/proc/generic.c + */ +static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + char *outp = page; + int len; + uid_t uid; + struct rb_node *node; + struct sock_tag *sock_tag_entry; + int item_index = 0; + int indent_level = 0; + long f_count; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + if (*eof) + return 0; + + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + page, items_to_skip, char_count, *eof); + + spin_lock_bh(&sock_tag_list_lock); + for (node = rb_first(&sock_tag_tree); + node; + node = rb_next(node)) { + if (item_index++ < items_to_skip) + continue; + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + uid = get_uid_from_tag(sock_tag_entry->tag); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " + "pid=%u\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid, + sock_tag_entry->pid + ); + f_count = atomic_long_read( + &sock_tag_entry->socket->file->f_count); + len = snprintf(outp, char_count, + "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + if (len >= char_count) { + spin_unlock_bh(&sock_tag_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&sock_tag_list_lock); + + if (item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read( + &qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + + /* Count the following as part of the last item_index */ + if (item_index > items_to_skip) { + prdebug_full_state(indent_level, "proc ctrl"); + } + + *eof = 1; + return outp - page; +} + +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ +static int ctrl_cmd_delete(const char *input) +{ + char cmd; + uid_t uid; + uid_t entry_uid; + tag_t acct_tag; + tag_t tag; + int res, argc; + struct iface_stat *iface_entry; + struct rb_node *node; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; + struct tag_ref *tr_entry; + struct uid_tag_data *utd_entry; + + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + "user_tag=0x%llx uid=%u\n", input, argc, cmd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 3) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = combine_atag_with_uid(acct_tag, uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "looking for tag=0x%llx (uid=%u)\n", + input, tag, uid); + + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); + node = rb_first(&sock_tag_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + entry_uid = get_uid_from_tag(st_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + + CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", + input, st_entry->tag, entry_uid); + + if (!acct_tag || st_entry->tag == tag) { + rb_erase(&st_entry->sock_node, &sock_tag_tree); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + tr_entry = lookup_tag_ref(st_entry->tag, NULL); + BUG_ON(tr_entry->num_sock_tags <= 0); + tr_entry->num_sock_tags--; + /* + * TODO: remove if, and start failing. + * This is a hack to work around the fact that in some + * places we have "if (IS_ERR_OR_NULL(pqd_entry))" + * and are trying to work around apps + * that didn't open the /dev/xt_qtaguid. + */ + if (st_entry->list.next && st_entry->list.prev) + list_del(&st_entry->list); + } + } + spin_unlock_bh(&sock_tag_list_lock); + + sock_tag_tree_erase(&st_to_free_tree); + + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + /* Counter sets are only on the uid tag, not full tag */ + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", + input, + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + spin_lock_bh(&iface_entry->tag_stat_list_lock); + node = rb_first(&iface_entry->tag_stat_tree); + while (node) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "ts tag=0x%llx (uid=%u)\n", + input, ts_entry->tn.tag, entry_uid); + + if (entry_uid != uid) + continue; + if (!acct_tag || ts_entry->tn.tag == tag) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase ts: %s 0x%llx %u\n", + input, iface_entry->ifname, + get_atag_from_tag(ts_entry->tn.tag), + entry_uid); + rb_erase(&ts_entry->tn.node, + &iface_entry->tag_stat_tree); + kfree(ts_entry); + } + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + /* Cleanup the uid_tag_data */ + spin_lock_bh(&uid_tag_data_tree_lock); + node = rb_first(&uid_tag_data_tree); + while (node) { + utd_entry = rb_entry(node, struct uid_tag_data, node); + entry_uid = utd_entry->uid; + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "utd uid=%u\n", + input, entry_uid); + + if (entry_uid != uid) + continue; + /* + * Go over the tag_refs, and those that don't have + * sock_tags using them are freed. + */ + put_tag_ref_tree(tag, utd_entry); + put_utd_entry(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + + atomic64_inc(&qtu_events.delete_cmds); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%u) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + atomic64_inc(&qtu_events.counter_set_changes); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_tag(const char *input) +{ + char cmd; + int sock_fd = 0; + uid_t uid = 0; + tag_t acct_tag = make_atag_from_value(0); + tag_t full_tag; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *uid_tag_data_entry; + struct proc_qtu_data *pqd_entry; + + /* Unassigned args will get defaulted later. */ + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + if (argc < 3) { + acct_tag = make_atag_from_value(0); + } else if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); + res = -EINVAL; + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " + "in_group=%d in_egroup=%d\n", + input, current->pid, current->tgid, current_uid(), + current_euid(), current_fsuid(), + in_group_p(proc_ctrl_write_gid), + in_egroup_p(proc_ctrl_write_gid)); + if (argc < 4) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err_put; + } + full_tag = combine_atag_with_uid(acct_tag, uid); + + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); + if (IS_ERR(tag_ref_entry)) { + res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&sock_tag_list_lock); + goto err_put; + } + tag_ref_entry->num_sock_tags++; + if (sock_tag_entry) { + struct tag_ref *prev_tag_ref_entry; + + CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " + "st@%p ...->f_count=%ld\n", + input, el_socket->sk, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + * There is still the ref from this call's sockfd_lookup() so + * it can be done within the spinlock. + */ + sockfd_put(sock_tag_entry->socket); + prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, + &uid_tag_data_entry); + BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); + BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); + prev_tag_ref_entry->num_sock_tags--; + sock_tag_entry->tag = full_tag; + } else { + CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", + input, el_socket->sk); + sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), + GFP_ATOMIC); + if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); + res = -ENOMEM; + goto err_tag_unref_put; + } + sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; + sock_tag_entry->pid = current->tgid; + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once( + "qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, + current_fsuid()); + else + list_add(&sock_tag_entry->list, + &pqd_entry->sock_tag_list); + spin_unlock_bh(&uid_tag_data_tree_lock); + + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + atomic64_inc(&qtu_events.sockets_tagged); + } + spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + return 0; + +err_tag_unref_put: + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); +err_put: + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); + return res; +} + +static int ctrl_cmd_untag(const char *input) +{ + char cmd; + int sock_fd = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + + argc = sscanf(input, "%c %d", &cmd, &sock_fd); + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + input, argc, cmd, sock_fd); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) { + spin_unlock_bh(&sock_tag_list_lock); + res = -EINVAL; + goto err_put; + } + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); + BUG_ON(!tag_ref_entry); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once("qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, current_fsuid()); + else + list_del(&sock_tag_entry->list); + spin_unlock_bh(&uid_tag_data_tree_lock); + /* + * We don't free tag_ref from the utd_entry here, + * only during a cmd_delete(). + */ + tag_ref_entry->num_sock_tags--; + spin_unlock_bh(&sock_tag_list_lock); + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count) - 1); + sockfd_put(el_socket); + + kfree(sock_tag_entry); + atomic64_inc(&qtu_events.sockets_untagged); + + return 0; + +err_put: + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); + return res; +} + +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int res; + + cmd = input[0]; + /* Collect params for commands */ + switch (cmd) { + case 'd': + res = ctrl_cmd_delete(input); + break; + + case 's': + res = ctrl_cmd_counter_set(input); + break; + + case 't': + res = ctrl_cmd_tag(input); + break; + + case 'u': + res = ctrl_cmd_untag(input); + break; + + default: + res = -EINVAL; + goto err; + } + if (!res) + res = count; +err: + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + return res; +} + +#define MAX_QTAGUID_CTRL_INPUT_LEN 255 +static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + + if (unlikely(module_passive)) + return count; + + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) + return -EINVAL; + + if (copy_from_user(input_buf, buffer, count)) + return -EFAULT; + + input_buf[count] = '\0'; + return qtaguid_ctrl_parse(input_buf, count); +} + +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int items_to_skip; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +{ + int len; + struct data_counters *cnts; + + if (!ppi->item_index) { + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); + } else { + tag_t tag = ppi->ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + + if (!can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid()); + return 0; + } + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} + +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + struct proc_print_info ppi; + int len; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; + ppi.items_to_skip = items_to_skip; + + if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + BUG_ON(len >= ppi.char_count); + (*num_items_returned)++; + *eof = 1; + return len; + } + + CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* The idx is there to help debug when things go belly up. */ + len = pp_stats_line(&ppi, 0); + /* Don't advance the outp unless the whole line was printed */ + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; + } + if (len) { + ppi.outp += len; + ppi.char_count -= len; + (*num_items_returned)++; + } + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { + struct rb_node *node; + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); + node; + node = rb_next(node)) { + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; + } + } + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return ppi.outp - page; +} + +/*------------------------------------------*/ +static int qtudev_open(struct inode *inode, struct file *file) +{ + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + struct proc_qtu_data *new_pqd_entry; + int res; + bool utd_entry_found; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + spin_lock_bh(&uid_tag_data_tree_lock); + + /* Look for existing uid data, or alloc one. */ + utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + if (IS_ERR_OR_NULL(utd_entry)) { + res = PTR_ERR(utd_entry); + goto err; + } + + /* Look for existing PID based proc_data */ + pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, + current->tgid); + if (pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u " + "%s already opened\n", + current->pid, current->tgid, current_fsuid(), + QTU_DEV_NAME); + res = -EBUSY; + goto err_unlock_free_utd; + } + + new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); + if (!new_pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u: " + "proc data alloc failed\n", + current->pid, current->tgid, current_fsuid()); + res = -ENOMEM; + goto err_unlock_free_utd; + } + new_pqd_entry->pid = current->tgid; + INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); + new_pqd_entry->parent_tag_data = utd_entry; + utd_entry->num_pqd++; + + proc_qtu_data_tree_insert(new_pqd_entry, + &proc_qtu_data_tree); + + spin_unlock_bh(&uid_tag_data_tree_lock); + DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", + current_fsuid(), new_pqd_entry); + file->private_data = new_pqd_entry; + return 0; + +err_unlock_free_utd: + if (!utd_entry_found) { + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); +err: + return res; +} + +static int qtudev_release(struct inode *inode, struct file *file) +{ + struct proc_qtu_data *pqd_entry = file->private_data; + struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct list_head *entry, *next; + struct tag_ref *tr; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + /* + * Do not trust the current->pid, it might just be a kworker cleaning + * up after a dead proc. + */ + DR_DEBUG("qtaguid: qtudev_release(): " + "pid=%u tgid=%u uid=%u " + "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", + current->pid, current->tgid, pqd_entry->parent_tag_data->uid, + pqd_entry, pqd_entry->pid, utd_entry, + utd_entry->num_active_tags); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + + list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { + st_entry = list_entry(entry, struct sock_tag, list); + DR_DEBUG("qtaguid: %s(): " + "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", + __func__, + st_entry, st_entry->sk, + current->pid, current->tgid, + pqd_entry->parent_tag_data->uid); + + utd_entry = uid_tag_data_tree_search( + &uid_tag_data_tree, + get_uid_from_tag(st_entry->tag)); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + DR_DEBUG("qtaguid: %s(): " + "looking for tag=0x%llx in utd_entry=%p\n", __func__, + st_entry->tag, utd_entry); + tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, + st_entry->tag); + BUG_ON(!tr); + BUG_ON(tr->num_sock_tags <= 0); + tr->num_sock_tags--; + free_tag_ref_from_utd_entry(tr, utd_entry); + + rb_erase(&st_entry->sock_node, &sock_tag_tree); + list_del(&st_entry->list); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + + /* + * Try to free the utd_entry if no other proc_qtu_data is + * using it (num_pqd is 0) and it doesn't have active tags + * (num_active_tags is 0). + */ + put_utd_entry(utd_entry); + } + + rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); + pqd_entry->parent_tag_data->num_pqd--; + put_utd_entry(pqd_entry->parent_tag_data); + kfree(pqd_entry); + file->private_data = NULL; + + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + + sock_tag_tree_erase(&st_to_free_tree); + + prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + current->pid, current->tgid); + return 0; +} + +/*------------------------------------------*/ +static const struct file_operations qtudev_fops = { + .owner = THIS_MODULE, + .open = qtudev_open, + .release = qtudev_release, +}; + +static struct miscdevice qtu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = QTU_DEV_NAME, + .fops = &qtudev_fops, + /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ +}; + +/*------------------------------------------*/ +static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) +{ + int ret; + *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); + if (!*res_procdir) { + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); + ret = -ENOMEM; + goto no_dir; + } + + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, + *res_procdir); + if (!xt_qtaguid_ctrl_file) { + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " + " file\n"); + ret = -ENOMEM; + goto no_ctrl_entry; + } + xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; + xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; + + xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, + *res_procdir); + if (!xt_qtaguid_stats_file) { + pr_err("qtaguid: failed to create xt_qtaguid/stats " + "file\n"); + ret = -ENOMEM; + goto no_stats_entry; + } + xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; + /* + * TODO: add support counter hacking + * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; + */ + return 0; + +no_stats_entry: + remove_proc_entry("ctrl", *res_procdir); +no_ctrl_entry: + remove_proc_entry("xt_qtaguid", NULL); +no_dir: + return ret; +} + +static struct xt_match qtaguid_mt_reg __read_mostly = { + /* + * This module masquerades as the "owner" module so that iptables + * tools can deal with it. + */ + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = qtaguid_mt, + .matchsize = sizeof(struct xt_qtaguid_match_info), + .me = THIS_MODULE, +}; + +static int __init qtaguid_mt_init(void) +{ + if (qtaguid_proc_register(&xt_qtaguid_procdir) + || iface_stat_init(xt_qtaguid_procdir) + || xt_register_match(&qtaguid_mt_reg) + || misc_register(&qtu_device)) + return -1; + return 0; +} + +/* + * TODO: allow unloading of the module. + * For now stats are permanent. + * Kconfig forces'y/n' and never an 'm'. + */ + +module_init(qtaguid_mt_init); +MODULE_AUTHOR("jpa "); +MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_owner"); +MODULE_ALIAS("ip6t_owner"); +MODULE_ALIAS("ipt_qtaguid"); +MODULE_ALIAS("ip6t_qtaguid"); diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h new file mode 100644 index 000000000000..02479d6d317d --- /dev/null +++ b/net/netfilter/xt_qtaguid_internal.h @@ -0,0 +1,330 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_INTERNAL_H__ +#define __XT_QTAGUID_INTERNAL_H__ + +#include +#include +#include +#include + +/* Iface handling */ +#define IDEBUG_MASK (1<<0) +/* Iptable Matching. Per packet. */ +#define MDEBUG_MASK (1<<1) +/* Red-black tree handling. Per packet. */ +#define RDEBUG_MASK (1<<2) +/* procfs ctrl/stats handling */ +#define CDEBUG_MASK (1<<3) +/* dev and resource tracking */ +#define DDEBUG_MASK (1<<4) + +/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ +#define DEFAULT_DEBUG_MASK 0 + +/* + * (Un)Define these *DEBUG to compile out/in the pr_debug calls. + * All undef: text size ~ 0x3030; all def: ~ 0x4404. + */ +#define IDEBUG +#define MDEBUG +#define RDEBUG +#define CDEBUG +#define DDEBUG + +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(qtaguid_debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ + } while (0) +#ifdef IDEBUG +#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef MDEBUG +#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef RDEBUG +#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef CDEBUG +#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef DDEBUG +#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) +#else +#define DR_DEBUG(...) no_printk(__VA_ARGS__) +#endif + +extern uint qtaguid_debug_mask; + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policing, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belonging to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion separately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +#define TAG_UID_MASK 0xFFFFFFFFULL +#define TAG_ACCT_MASK (~0xFFFFFFFFULL) + +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & TAG_ACCT_MASK; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & TAG_UID_MASK); +} +static inline tag_t make_atag_from_value(uint32_t value) +{ + return (uint64_t)value << 32; +} +/*---------------------------------------------------------------------------*/ + +/* + * Maximum number of socket tags that a UID is allowed to have active. + * Multiple processes belonging to the same UID contribute towards this limit. + * Special UIDs that can impersonate a UID also contribute (e.g. download + * manager, ...) + */ +#define DEFAULT_MAX_SOCK_TAGS 1024 + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +/* Generic X based nodes used as a base for rb_tree ops */ +struct tag_node { + struct rb_node node; + tag_t tag; +}; + +struct tag_stat { + struct tag_node tn; + struct data_counters counters; + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ + struct data_counters *parent_counters; +}; + +struct iface_stat { + struct list_head list; /* in iface_stat_list */ + char *ifname; + bool active; + /* net_dev is only valid for active iface_stat */ + struct net_device *net_dev; + + struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + /* + * We keep the last_known, because some devices reset their counters + * just before NETDEV_UP, while some will reset just before + * NETDEV_REGISTER (which is more normal). + * So now, if the device didn't do a NETDEV_UNREGISTER and we see + * its current dev stats smaller that what was previously known, we + * assume an UNREGISTER and just use the last_known. + */ + struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; + /* last_known is usable when last_known_valid is true */ + bool last_known_valid; + + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + +/* + * Track tag that this socket is transferring data for, and not necessarily + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + * These structs need to be looked up by sock and pid. + */ +struct sock_tag { + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + /* Used to associate with a given pid */ + struct list_head list; /* in proc_qtu_data.sock_tag_list */ + pid_t pid; + + tag_t tag; +}; + +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + + atomic64_t match_calls; /* Number of times iptables called mt */ + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + * Total skbs processed is sum(match_found*). + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had or didn't have the sk. */ + atomic64_t match_found_sk_in_ct; + atomic64_t match_found_no_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_no_sk; + /* + * The file ptr in the sk_socket wasn't there. + * This might happen for traffic while the socket is being closed. + */ + atomic64_t match_no_sk_file; +}; + +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +/*----------------------------------------------*/ +/* + * The qtu uid data is used to track resources that are created directly or + * indirectly by processes (uid tracked). + * It is shared by the processes with the same uid. + * Some of the resource will be counted to prevent further rogue allocations, + * some will need freeing once the owner process (uid) exits. + */ +struct uid_tag_data { + struct rb_node node; + uid_t uid; + + /* + * For the uid, how many accounting tags have been set. + */ + int num_active_tags; + /* Track the number of proc_qtu_data that reference it */ + int num_pqd; + struct rb_root tag_ref_tree; + /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ +}; + +struct tag_ref { + struct tag_node tn; + + /* + * This tracks the number of active sockets that have a tag on them + * which matches this tag_ref.tn.tag. + * A tag ref can live on after the sockets are untagged. + * A tag ref can only be removed during a tag delete command. + */ + int num_sock_tags; +}; + +struct proc_qtu_data { + struct rb_node node; + pid_t pid; + + struct uid_tag_data *parent_tag_data; + + /* Tracks the sock_tags that need freeing upon this proc's death */ + struct list_head sock_tag_list; + /* No spinlock_t sock_tag_list_lock; use the global one. */ +}; + +/*----------------------------------------------*/ +#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c new file mode 100644 index 000000000000..39176785c91f --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.c @@ -0,0 +1,556 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Most of the functions in this file just waste time if DEBUG is not defined. + * The matching xt_qtaguid_print.h will static inline empty funcs if the needed + * debug flags ore not defined. + * Those funcs that fail to allocate memory will panic as there is no need to + * hobble allong just pretending to do the requested work. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include + + +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +#ifdef DDEBUG + +static void _bug_on_err_or_null(void *ptr) +{ + if (IS_ERR_OR_NULL(ptr)) { + pr_err("qtaguid: kmalloc failed\n"); + BUG(); + } +} + +char *pp_tag_t(tag_t *tag) +{ + char *res; + + if (!tag) + res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); + else + res = kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); + _bug_on_err_or_null(res); + return res; +} + +char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + char *res; + + if (!dc) + res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); + else if (showValues) + res = kasprintf( + GFP_ATOMIC, "data_counters@%p{" + "set0{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}, " + "set1{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}}", + dc, + dc->bpc[0][IFS_RX][IFS_TCP].bytes, + dc->bpc[0][IFS_RX][IFS_TCP].packets, + dc->bpc[0][IFS_RX][IFS_UDP].bytes, + dc->bpc[0][IFS_RX][IFS_UDP].packets, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[0][IFS_TX][IFS_TCP].bytes, + dc->bpc[0][IFS_TX][IFS_TCP].packets, + dc->bpc[0][IFS_TX][IFS_UDP].bytes, + dc->bpc[0][IFS_TX][IFS_UDP].packets, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_RX][IFS_TCP].bytes, + dc->bpc[1][IFS_RX][IFS_TCP].packets, + dc->bpc[1][IFS_RX][IFS_UDP].bytes, + dc->bpc[1][IFS_RX][IFS_UDP].packets, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_TX][IFS_TCP].bytes, + dc->bpc[1][IFS_TX][IFS_TCP].packets, + dc->bpc[1][IFS_TX][IFS_UDP].bytes, + dc->bpc[1][IFS_TX][IFS_UDP].packets, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); + else + res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + _bug_on_err_or_null(res); + return res; +} + +char *pp_tag_node(struct tag_node *tn) +{ + char *tag_str; + char *res; + + if (!tn) { + res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&tn->tag); + res = kasprintf(GFP_ATOMIC, + "tag_node@%p{tag=%s}", + tn, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_tag_ref(struct tag_ref *tr) +{ + char *tn_str; + char *res; + + if (!tr) { + res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&tr->tn); + res = kasprintf(GFP_ATOMIC, + "tag_ref@%p{%s, num_sock_tags=%d}", + tr, tn_str, tr->num_sock_tags); + _bug_on_err_or_null(res); + kfree(tn_str); + return res; +} + +char *pp_tag_stat(struct tag_stat *ts) +{ + char *tn_str; + char *counters_str; + char *parent_counters_str; + char *res; + + if (!ts) { + res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&ts->tn); + counters_str = pp_data_counters(&ts->counters, true); + parent_counters_str = pp_data_counters(ts->parent_counters, false); + res = kasprintf(GFP_ATOMIC, + "tag_stat@%p{%s, counters=%s, parent_counters=%s}", + ts, tn_str, counters_str, parent_counters_str); + _bug_on_err_or_null(res); + kfree(tn_str); + kfree(counters_str); + kfree(parent_counters_str); + return res; +} + +char *pp_iface_stat(struct iface_stat *is) +{ + char *res; + if (!is) + res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + else + res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "total={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "active=%d, " + "net_dev=%p, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->totals[IFS_RX].bytes, + is->totals[IFS_RX].packets, + is->totals[IFS_TX].bytes, + is->totals[IFS_TX].packets, + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, + is->active, + is->net_dev, + is->proc_ptr); + _bug_on_err_or_null(res); + return res; +} + +char *pp_sock_tag(struct sock_tag *st) +{ + char *tag_str; + char *res; + + if (!st) { + res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&st->tag); + res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" + "sock_node=rb_node{...}, " + "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "pid=%u, tag=%s}", + st, st->sk, st->socket, atomic_long_read( + &st->socket->file->f_count), + st->pid, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_uid_tag_data(struct uid_tag_data *utd) +{ + char *res; + + if (!utd) + res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + else + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "num_pqd=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags, utd->num_pqd); + _bug_on_err_or_null(res); + return res; +} + +char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + char *parent_tag_data_str; + char *res; + + if (!pqd) { + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + _bug_on_err_or_null(res); + return res; + } + parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" + "node=rb_node{...}, pid=%u, " + "parent_tag_data=%s, " + "sock_tag_list=list_head{...}}", + pqd, pqd->pid, parent_tag_data_str + ); + _bug_on_err_or_null(res); + kfree(parent_tag_data_str); + return res; +} + +/*------------------------------------------*/ +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ + struct rb_node *node; + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(sock_tag_tree)) { + str = "sock_tag_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(sock_tag_list)) { + str = "sock_tag_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(sock_tag_entry, sock_tag_list, list) { + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ + char *str; + struct rb_node *node; + struct proc_qtu_data *proc_qtu_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { + str = "proc_qtu_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "proc_qtu_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(proc_qtu_data_tree); + node; + node = rb_next(node)) { + proc_qtu_data_entry = rb_entry(node, + struct proc_qtu_data, + node); + str = pp_proc_qtu_data(proc_qtu_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + indent_level++; + prdebug_sock_tag_list(indent_level, + &proc_qtu_data_entry->sock_tag_list); + indent_level--; + + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ + char *str; + struct rb_node *node; + struct tag_ref *tag_ref_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_ref_tree)) { + str = "tag_ref_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_ref_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_ref_tree); + node; + node = rb_next(node)) { + tag_ref_entry = rb_entry(node, + struct tag_ref, + tn.node); + str = pp_tag_ref(tag_ref_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ + char *str; + struct rb_node *node; + struct uid_tag_data *uid_tag_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(uid_tag_data_tree)) { + str = "uid_tag_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "uid_tag_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(uid_tag_data_tree); + node; + node = rb_next(node)) { + uid_tag_data_entry = rb_entry(node, struct uid_tag_data, + node); + str = pp_uid_tag_data(uid_tag_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { + indent_level++; + prdebug_tag_ref_tree(indent_level, + &uid_tag_data_entry->tag_ref_tree); + indent_level--; + } + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ + char *str; + struct rb_node *node; + struct tag_stat *ts_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_stat_tree)) { + str = "tag_stat_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_stat_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + str = pp_tag_stat(ts_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ + char *str; + struct iface_stat *iface_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(iface_stat_list)) { + str = "iface_stat_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "iface_stat_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(iface_entry, iface_stat_list, list) { + str = pp_iface_stat(iface_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + kfree(str); + + spin_lock_bh(&iface_entry->tag_stat_list_lock); + if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { + indent_level++; + prdebug_tag_stat_tree(indent_level, + &iface_entry->tag_stat_tree); + indent_level--; + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +#endif /* ifdef DDEBUG */ +/*------------------------------------------*/ +static const char * const netdev_event_strings[] = { + "netdev_unknown", + "NETDEV_UP", + "NETDEV_DOWN", + "NETDEV_REBOOT", + "NETDEV_CHANGE", + "NETDEV_REGISTER", + "NETDEV_UNREGISTER", + "NETDEV_CHANGEMTU", + "NETDEV_CHANGEADDR", + "NETDEV_GOING_DOWN", + "NETDEV_CHANGENAME", + "NETDEV_FEAT_CHANGE", + "NETDEV_BONDING_FAILOVER", + "NETDEV_PRE_UP", + "NETDEV_PRE_TYPE_CHANGE", + "NETDEV_POST_TYPE_CHANGE", + "NETDEV_POST_INIT", + "NETDEV_UNREGISTER_BATCH", + "NETDEV_RELEASE", + "NETDEV_NOTIFY_PEERS", + "NETDEV_JOIN", +}; + +const char *netdev_evt_str(int netdev_event) +{ + if (netdev_event < 0 + || netdev_event >= ARRAY_SIZE(netdev_event_strings)) + return "bad event num"; + return netdev_event_strings[netdev_event]; +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h new file mode 100644 index 000000000000..b63871a0be5a --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.h @@ -0,0 +1,120 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_PRINT_H__ +#define __XT_QTAGUID_PRINT_H__ + +#include "xt_qtaguid_internal.h" + +#ifdef DDEBUG + +char *pp_tag_t(tag_t *tag); +char *pp_data_counters(struct data_counters *dc, bool showValues); +char *pp_tag_node(struct tag_node *tn); +char *pp_tag_ref(struct tag_ref *tr); +char *pp_tag_stat(struct tag_stat *ts); +char *pp_iface_stat(struct iface_stat *is); +char *pp_sock_tag(struct sock_tag *st); +char *pp_uid_tag_data(struct uid_tag_data *qtd); +char *pp_proc_qtu_data(struct proc_qtu_data *pqd); + +/*------------------------------------------*/ +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list); +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree); +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree); +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree); +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree); +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list); + +#else + +/*------------------------------------------*/ +static inline char *pp_tag_t(tag_t *tag) +{ + return NULL; +} +static inline char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + return NULL; +} +static inline char *pp_tag_node(struct tag_node *tn) +{ + return NULL; +} +static inline char *pp_tag_ref(struct tag_ref *tr) +{ + return NULL; +} +static inline char *pp_tag_stat(struct tag_stat *ts) +{ + return NULL; +} +static inline char *pp_iface_stat(struct iface_stat *is) +{ + return NULL; +} +static inline char *pp_sock_tag(struct sock_tag *st) +{ + return NULL; +} +static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) +{ + return NULL; +} +static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + return NULL; +} + +/*------------------------------------------*/ +static inline +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ +} +static inline +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ +} +static inline +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ +} +static inline +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ +} +static inline +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ +} +static inline +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ +} +static inline +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ +} +#endif +/*------------------------------------------*/ +const char *netdev_evt_str(int netdev_event); +#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 8eb5dc055d723aeb9b59fea13150de0047d255fa Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 17 Apr 2012 16:00:07 -0700 Subject: [PATCH 0908/1276] ANDROID: netfilter: xt_qtaguid: fix ipv6 protocol lookup When updating the stats for a given uid it would incorrectly assume IPV4 and pick up the wrong protocol when IPV6. Change-Id: Iea4a635012b4123bf7aa93809011b7b2040bb3d5 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3d3928291efb..2c1170f89d0f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -26,6 +26,10 @@ #include #include +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include +#endif + #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -1546,6 +1550,27 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1572,15 +1597,15 @@ static void account_for_uid(const struct sk_buff *skb, } else if (unlikely(!el_dev->name)) { pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, par->in ? IFS_RX : IFS_TX, - ip_hdr(skb)->protocol, skb->len); + proto, skb->len); } } @@ -1625,8 +1650,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } else { atomic64_inc(&qtu_events.match_found_sk); } - MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", - par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, From 03d3371c2b5ab415ebe61ac5117c0f530f0132b2 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 27 Apr 2012 12:57:39 -0700 Subject: [PATCH 0909/1276] ANDROID: netfilter: xt_qtaguid: start tracking iface rx/tx at low level qtaguid tracks the device stats by monitoring when it goes up and down, then it gets the dev_stats(). But devs don't correctly report stats (either they don't count headers symmetrically between rx/tx, or they count internal control messages). Now qtaguid counts the rx/tx bytes/packets during raw:prerouting and mangle:postrouting (nat is not available in ipv6). The results are in /proc/net/xt_qtaguid/iface_stat_fmt which outputs a format line (bash expansion): ifname total_skb_{rx,tx}_{bytes,packets} Added event counters for pre/post handling. Added extra ctrl_*() pid/uid debugging. Change-Id: Id84345d544ad1dd5f63e3842cab229e71d339297 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 277 ++++++++++++++++++++++------ net/netfilter/xt_qtaguid_internal.h | 5 +- net/netfilter/xt_qtaguid_print.c | 18 +- 3 files changed, 233 insertions(+), 67 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2c1170f89d0f..9fd0ffa6c365 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -114,8 +114,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ static const char *iface_stat_all_procfilename = "iface_stat_all"; static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + /* * Ordering of locks: @@ -128,9 +135,9 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-25: + * Call tree with all lock holders as of 2012-04-27: * - * iface_stat_all_proc_read() + * iface_stat_fmt_proc_read() * iface_stat_list_lock * (struct iface_stat) * @@ -781,13 +788,14 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } -static int iface_stat_all_proc_read(char *page, char **num_items_returned, +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { char *outp = page; int item_index = 0; int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ struct iface_stat *iface_entry; struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; @@ -797,14 +805,32 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, return 0; } - CT_DEBUG("qtaguid:proc iface_stat_all " + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, items_to_skip, char_count, *eof); if (*eof) return 0; + if (fmt == 2 && item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets\n" + ); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. @@ -820,18 +846,37 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, } else { stats = &no_dev_stats; } - len = snprintf(outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals[IFS_RX].bytes, - iface_entry->totals[IFS_RX].packets, - iface_entry->totals[IFS_TX].bytes, - iface_entry->totals[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets); + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->totals_via_skb[IFS_RX].bytes, + iface_entry->totals_via_skb[IFS_RX].packets, + iface_entry->totals_via_skb[IFS_TX].bytes, + iface_entry->totals_via_skb[IFS_TX].packets + ); + } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); *outp = '\0'; @@ -865,13 +910,17 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -975,11 +1024,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, "iface reset its stats unexpectedly\n", __func__, net_dev->name); - iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; - iface->totals[IFS_TX].packets += + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += iface->last_known[IFS_TX].packets; - iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; - iface->totals[IFS_RX].packets += + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += iface->last_known[IFS_RX].packets; iface->last_known_valid = false; IF_DEBUG("qtaguid: %s(%s): iface=%p " @@ -1147,6 +1198,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk) return sock_tag_entry; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void data_counters_update(struct data_counters *dc, int set, enum ifs_tx_rx direction, int proto, int bytes) @@ -1207,10 +1279,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); return; } - entry->totals[IFS_TX].bytes += stats->tx_bytes; - entry->totals[IFS_TX].packets += stats->tx_packets; - entry->totals[IFS_RX].bytes += stats->rx_bytes; - entry->totals[IFS_RX].packets += stats->rx_packets; + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); @@ -1220,6 +1292,67 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + entry->totals_via_skb[direction].bytes += bytes; + entry->totals_via_skb[direction].packets++; + spin_unlock_bh(&iface_stat_list_lock); +} + static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { @@ -1467,18 +1600,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) parent_procdir); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " - " failed to create stat_all proc entry\n"); + " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_all_stats_entry; + goto err_zap_all_stats_entries; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1499,6 +1645,8 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); err_zap_all_stats_entry: remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: @@ -1550,27 +1698,6 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } -static int ipx_proto(const struct sk_buff *skb, - struct xt_action_param *par) -{ - int thoff = 0, tproto; - - switch (par->family) { - case NFPROTO_IPV6: - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) - MT_DEBUG("%s(): transport header not found in ipv6" - " skb=%p\n", __func__, skb); - break; - case NFPROTO_IPV4: - tproto = ip_hdr(skb)->protocol; - break; - default: - tproto = IPPROTO_RAW; - } - return tproto; -} - static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1630,8 +1757,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - sk = skb->sk; + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + sk = skb->sk; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight @@ -1806,8 +1947,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); @@ -1851,6 +1994,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "delete_cmds=%llu " "iface_events=%llu " "match_calls=%llu " + "match_calls_prepost=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " @@ -1862,6 +2006,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), atomic64_read( @@ -2135,7 +2280,9 @@ static int ctrl_cmd_tag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2280,7 +2427,9 @@ static int ctrl_cmd_untag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2356,6 +2505,9 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -2532,9 +2684,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return len; } - CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, - items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); if (*eof) return 0; diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 02479d6d317d..d79f8383abf4 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -202,7 +202,8 @@ struct iface_stat { /* net_dev is only valid for active iface_stat */ struct net_device *net_dev; - struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before @@ -254,6 +255,8 @@ struct qtaguid_event_counts { atomic64_t iface_events; /* Number of NETDEV_* events handled */ atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 39176785c91f..8cbd8e42bcc4 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -183,7 +183,11 @@ char *pp_iface_stat(struct iface_stat *is) res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "total={rx={bytes=%llu, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " "packets=%llu}, " "tx={bytes=%llu, " "packets=%llu}}, " @@ -198,10 +202,14 @@ char *pp_iface_stat(struct iface_stat *is) "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + is->totals_via_skb[IFS_RX].bytes, + is->totals_via_skb[IFS_RX].packets, + is->totals_via_skb[IFS_TX].bytes, + is->totals_via_skb[IFS_TX].packets, is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, From fa5dba2f7062d92316f3538fe802d9e65101ef8e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 28 Aug 2012 16:53:32 -0700 Subject: [PATCH 0910/1276] ANDROID: netfilter: xt_qtaguid: report only uid tags to non-privileged processes In the past, a process could only see its own stats (uid-based summary, and details). Now we allow any process to see other UIDs uid-based stats, but still hide the detailed stats. Change-Id: I7666961ed244ac1d9359c339b048799e5db9facc Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 9fd0ffa6c365..14b003da1423 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2588,8 +2588,9 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u\n", From c9c775b94d84fed094fcb7fa6b62eed5011dbe46 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Oct 2012 20:38:21 -0700 Subject: [PATCH 0911/1276] ANDROID: netfilter: xt_qtaguid: fix error exit that would keep a spinlock. qtudev_open() could return with a uid_tag_data_tree_lock held when an kzalloc(..., GFP_ATOMIC) would fail. Very unlikely to get triggered AND survive the mayhem of running out of mem. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 14b003da1423..6b22563a924f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2752,7 +2752,7 @@ static int qtudev_open(struct inode *inode, struct file *file) utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); - goto err; + goto err_unlock; } /* Look for existing PID based proc_data */ @@ -2794,8 +2794,8 @@ static int qtudev_open(struct inode *inode, struct file *file) rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); } +err_unlock: spin_unlock_bh(&uid_tag_data_tree_lock); -err: return res; } From 4d6c00e18babfddd90842e4bbdf6f61830024410 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 19 Nov 2012 11:44:51 -0800 Subject: [PATCH 0912/1276] ANDROID: netfilter: xt_qtaguid: Don't BUG_ON if create_if_tag_stat fails If create_if_tag_stat fails to allocate memory (GFP_ATOMIC) the following will happen: qtaguid: iface_stat: tag stat alloc failed ... kernel BUG at xt_qtaguid.c:1482! Signed-off-by: Pontus Fuchs --- net/netfilter/xt_qtaguid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 6b22563a924f..603bdd206990 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1461,6 +1461,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. */ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + if (!new_tag_stat) + goto unlock; uid_tag_counters = &new_tag_stat->counters; } else { uid_tag_counters = &tag_stat_entry->counters; @@ -1469,6 +1471,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, if (acct_tag) { /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); + if (!new_tag_stat) + goto unlock; new_tag_stat->parent_counters = uid_tag_counters; } else { /* @@ -1482,6 +1486,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); +unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); } From ef136e9affb05927236eace87b845c8be42149ba Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 4 Jan 2013 18:18:36 -0800 Subject: [PATCH 0913/1276] ANDROID: netfilter: xt_qtaguid: remove AID_* dependency for access control qtaguid limits what can be done with /ctrl and /stats based on group membership. This changes removes AID_NET_BW_STATS and AID_NET_BW_ACCT, and picks up the groups from the gid of the matching proc entry files. Signed-off-by: JP Abgrall Change-Id: I42e477adde78a12ed5eb58fbc0b277cdaadb6f94 --- net/netfilter/xt_qtaguid.c | 51 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 603bdd206990..923f1bdd02e8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -53,25 +53,22 @@ static unsigned int proc_stats_perms = S_IRUGO; module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); static struct proc_dir_entry *xt_qtaguid_ctrl_file; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK + +/* Everybody can write. But proc_ctrl_write_limited is true by default which + * limits what can be controlled. See the can_*() functions. + */ static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; -#else -static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; -#endif module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -#include -static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; -static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; -#else -/* 0 means, don't limit anybody */ -static gid_t proc_stats_readall_gid; -static gid_t proc_ctrl_write_gid; -#endif -module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, +/* Limited by default, so the gid of the ctrl and stats proc entries + * will limit what can be done. See the can_*() functions. + */ +static bool proc_stats_readall_limited = true; +static bool proc_ctrl_write_limited = true; + +module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, S_IRUGO | S_IWUSR); -module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, +module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, S_IRUGO | S_IWUSR); /* @@ -242,8 +239,9 @@ static struct qtaguid_event_counts qtu_events; static bool can_manipulate_uids(void) { /* root pwnd */ - return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) - || in_egroup_p(proc_ctrl_write_gid); + return in_egroup_p(xt_qtaguid_ctrl_file->gid) + || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static bool can_impersonate_uid(uid_t uid) @@ -254,9 +252,10 @@ static bool can_impersonate_uid(uid_t uid) static bool can_read_other_uid_stats(uid_t uid) { /* root pwnd */ - return unlikely(!current_fsuid()) || uid == current_fsuid() - || unlikely(!proc_stats_readall_gid) - || in_egroup_p(proc_stats_readall_gid); + return in_egroup_p(xt_qtaguid_stats_file->gid) + || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -2302,11 +2301,12 @@ static int ctrl_cmd_tag(const char *input) } CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " - "in_group=%d in_egroup=%d\n", + "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", input, current->pid, current->tgid, current_uid(), current_euid(), current_fsuid(), - in_group_p(proc_ctrl_write_gid), - in_egroup_p(proc_ctrl_write_gid)); + xt_qtaguid_ctrl_file->gid, + in_group_p(xt_qtaguid_ctrl_file->gid), + in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { @@ -2598,10 +2598,11 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u\n", + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); return 0; } if (ppi->item_index++ < ppi->items_to_skip) From dd4ec2b2d7acf451dee0464eca7272806a4ee22f Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 28 Jan 2013 16:50:44 -0800 Subject: [PATCH 0914/1276] ANDROID: netfilter: xt_qtaguid: extend iface stat to report protocols In the past the iface_stat_fmt would only show global bytes/packets for the skb-based numbers. For stall detection in userspace, distinguishing tcp vs other protocols makes it easier. Now we report ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets {rx,tx}_{tcp,udp,ohter}_{bytes,packets} Bug: 6818637 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 90 +++++++++++++++++------------ net/netfilter/xt_qtaguid_internal.h | 21 ++++++- net/netfilter/xt_qtaguid_print.c | 14 +++-- 3 files changed, 82 insertions(+), 43 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 923f1bdd02e8..92e5f80bd8fa 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -268,24 +268,6 @@ static inline void dc_add_byte_packets(struct data_counters *counters, int set, counters->bpc[set][direction][ifs_proto].packets += packets; } -static inline uint64_t dc_sum_bytes(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].bytes - + counters->bpc[set][direction][IFS_UDP].bytes - + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; -} - -static inline uint64_t dc_sum_packets(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].packets - + counters->bpc[set][direction][IFS_UDP].packets - + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; -} - static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; @@ -787,6 +769,53 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } +/* This is for fmt2 only */ +static int pp_iface_stat_line(bool header, char *outp, + int char_count, struct iface_stat *iface_entry) +{ + int len; + if (header) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); + } else { + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) @@ -816,11 +845,7 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, return 0; if (fmt == 2 && item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets\n" - ); + len = pp_iface_stat_line(true, outp, char_count, NULL); if (len >= char_count) { *outp = '\0'; return outp - page; @@ -865,16 +890,8 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, stats->tx_bytes, stats->tx_packets ); } else { - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->totals_via_skb[IFS_RX].bytes, - iface_entry->totals_via_skb[IFS_RX].packets, - iface_entry->totals_via_skb[IFS_TX].bytes, - iface_entry->totals_via_skb[IFS_TX].packets - ); + len = pp_iface_stat_line(false, outp, char_count, + iface_entry); } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); @@ -1304,6 +1321,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, const struct net_device *el_dev; enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; int bytes = skb->len; + int proto; if (!skb->dev) { MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); @@ -1329,7 +1347,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, par->hooknum, __func__); BUG(); } else { - int proto = ipx_proto(skb, par); + proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", par->hooknum, el_dev->name, el_dev->type, par->family, proto); @@ -1347,8 +1365,8 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, el_dev->name, entry); - entry->totals_via_skb[direction].bytes += bytes; - entry->totals_via_skb[direction].packets++; + data_counters_update(&entry->totals_via_skb, 0, direction, proto, + bytes); spin_unlock_bh(&iface_stat_list_lock); } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index d79f8383abf4..6dc14a9c6889 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -179,6 +179,25 @@ struct data_counters { struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + + /* Generic X based nodes used as a base for rb_tree ops */ struct tag_node { struct rb_node node; @@ -203,7 +222,7 @@ struct iface_stat { struct net_device *net_dev; struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; - struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; + struct data_counters totals_via_skb; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 8cbd8e42bcc4..f6a00a3520ed 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -177,9 +177,10 @@ char *pp_tag_stat(struct tag_stat *ts) char *pp_iface_stat(struct iface_stat *is) { char *res; - if (!is) + if (!is) { res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); - else + } else { + struct data_counters *cnts = &is->totals_via_skb; res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " @@ -206,10 +207,10 @@ char *pp_iface_stat(struct iface_stat *is) is->totals_via_dev[IFS_RX].packets, is->totals_via_dev[IFS_TX].bytes, is->totals_via_dev[IFS_TX].packets, - is->totals_via_skb[IFS_RX].bytes, - is->totals_via_skb[IFS_RX].packets, - is->totals_via_skb[IFS_TX].bytes, - is->totals_via_skb[IFS_TX].packets, + dc_sum_bytes(cnts, 0, IFS_RX), + dc_sum_packets(cnts, 0, IFS_RX), + dc_sum_bytes(cnts, 0, IFS_TX), + dc_sum_packets(cnts, 0, IFS_TX), is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, @@ -218,6 +219,7 @@ char *pp_iface_stat(struct iface_stat *is) is->active, is->net_dev, is->proc_ptr); + } _bug_on_err_or_null(res); return res; } From 184ef13bb6ad95919745068b6e7c8ba7eb630eda Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Feb 2013 17:40:07 -0800 Subject: [PATCH 0915/1276] ANDROID: netfilter: xt_qtaguid: Allow tracking loopback In the past it would always ignore interfaces with loopback addresses. Now we just treat them like any other. This also helps with writing tests that check for the presence of the qtaguid module. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 92e5f80bd8fa..992a6e044902 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1108,18 +1108,13 @@ static void iface_stat_create(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !ipv4_is_loopback(ipaddr); IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI4\n", __func__, - entry->ifname, activate, &ipaddr); - goto done_unlock_put; - } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + entry->ifname, true, &ipaddr); goto done_unlock_put; } @@ -1170,19 +1165,13 @@ static void iface_stat_create_ipv6(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI6c\n", __func__, - entry->ifname, activate, &ifa->addr); - goto done_unlock_put; - } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: %s(%s): " - "ignore loopback dev. ip=%pI6c\n", __func__, - ifname, &ifa->addr); + entry->ifname, true, &ifa->addr); goto done_unlock_put; } From d62159de8436b3fa475bd85062bb7b047ed0f551 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 8 Apr 2013 15:09:26 -0700 Subject: [PATCH 0916/1276] ANDROID: netfilter: xt_qtaguid: rate limit some of the printks Some of the printks are in the packet handling path. We now ratelimit the very unlikely errors to avoid kmsg spamming. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 992a6e044902..4ec6d23876c5 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1328,12 +1329,12 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, } if (unlikely(!el_dev)) { - pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); BUG(); } else if (unlikely(!el_dev->name)) { - pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); BUG(); } else { proto = ipx_proto(skb, par); @@ -1416,8 +1417,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: stat_update() %s not found\n", - ifname); + pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + "%s not found\n", ifname); return; } /* It is ok to process data when an iface_entry is inactive */ From 835c39646f3948936a891eaff6467bc26f212673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:45:02 -0700 Subject: [PATCH 0917/1276] ANDROID: netfilter: xt_qtaguid: 3.10 fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop using obsolete procfs api. Signed-off-by: Arve HjønnevÃ¥g [AmitP: Folded following android-4.9 commit changes into this patch 564729173b12 ("netfilter: xt_qtaguid: fix memory leak in seq_file handlers") 85a2eb5b48fc ("ANDROID: netfilter: xt_qtaguid: 64-bit warning fixes")] Signed-off-by: Amit Pundir --- net/netfilter/xt_qtaguid.c | 1063 ++++++++++++++++++------------------ 1 file changed, 539 insertions(+), 524 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4ec6d23876c5..505dbab40210 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" +#include "../../fs/proc/internal.h" /* * We only use the xt_socket funcs within a similar context to avoid unexpected @@ -122,104 +124,6 @@ static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; static struct proc_dir_entry *iface_stat_fmt_procfile; -/* - * Ordering of locks: - * outer locks: - * iface_stat_list_lock - * sock_tag_list_lock - * inner locks: - * uid_tag_data_tree_lock - * tag_counter_set_list_lock - * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock - * is acquired. - * - * Call tree with all lock holders as of 2012-04-27: - * - * iface_stat_fmt_proc_read() - * iface_stat_list_lock - * (struct iface_stat) - * - * qtaguid_ctrl_proc_read() - * sock_tag_list_lock - * (sock_tag_tree) - * (struct proc_qtu_data->sock_tag_list) - * prdebug_full_state() - * sock_tag_list_lock - * (sock_tag_tree) - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * (proc_qtu_data_tree) - * iface_stat_list_lock - * - * qtaguid_stats_proc_read() - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * - * qtudev_open() - * uid_tag_data_tree_lock - * - * qtudev_release() - * sock_tag_data_list_lock - * uid_tag_data_tree_lock - * prdebug_full_state() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * iface_stat_list_lock - * - * iface_netdev_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inetaddr_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inet6addr_event_handler() - * iface_stat_create_ipv6() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * qtaguid_mt() - * account_for_uid() - * if_tag_stat_update() - * get_sock_stat() - * sock_tag_list_lock - * struct iface_stat->tag_stat_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * - * - * qtaguid_ctrl_parse() - * ctrl_cmd_delete() - * sock_tag_list_lock - * tag_counter_set_list_lock - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * uid_tag_data_tree_lock - * ctrl_cmd_counter_set() - * tag_counter_set_list_lock - * ctrl_cmd_tag() - * sock_tag_list_lock - * (sock_tag_tree) - * get_tag_ref() - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * uid_tag_data_tree_lock - * (proc_qtu_data_tree) - * ctrl_cmd_untag() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * - */ static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); @@ -690,42 +594,26 @@ static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) } } -static int read_proc_u64(char *page, char **start, off_t off, - int count, int *eof, void *data) +static ssize_t read_proc_u64(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - uint64_t value; - char *p = page; - uint64_t *iface_entry = data; + uint64_t *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - if (!data) - return 0; - - value = *iface_entry; - p += sprintf(p, "%llu\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int read_proc_bool(char *page, char **start, off_t off, - int count, int *eof, void *data) +static ssize_t read_proc_bool(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - bool value; - char *p = page; - bool *bool_entry = data; - - if (!data) - return 0; + bool *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - value = *bool_entry; - p += sprintf(p, "%u\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } static int get_active_counter_set(tag_t tag) @@ -771,144 +659,132 @@ static struct iface_stat *get_iface_entry(const char *ifname) } /* This is for fmt2 only */ -static int pp_iface_stat_line(bool header, char *outp, - int char_count, struct iface_stat *iface_entry) -{ - int len; - if (header) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n" - ); - } else { - struct data_counters *cnts; - int cnt_set = 0; /* We only use one set for the device */ - cnts = &iface_entry->totals_via_skb; - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu %llu %llu %llu %llu " - "%llu %llu %llu %llu %llu %llu %llu %llu\n", - iface_entry->ifname, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; -} - -static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, - int *eof, void *data) -{ - char *outp = page; - int item_index = 0; - int len; - int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ - struct iface_stat *iface_entry; - struct rtnl_link_stats64 dev_stats, *stats; - struct rtnl_link_stats64 no_dev_stats = {0}; - - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } - - CT_DEBUG("qtaguid:proc iface_stat_fmt " - "pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); +static void pp_iface_stat_header(struct seq_file *m) +{ + seq_puts(m, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); +} - if (*eof) - return 0; +static void pp_iface_stat_line(struct seq_file *m, + struct iface_stat *iface_entry) +{ + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); +} + +struct proc_iface_stat_fmt_info { + int fmt; +}; - if (fmt == 2 && item_index++ >= items_to_skip) { - len = pp_iface_stat_line(true, outp, char_count, NULL); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } +static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_iface_stat_fmt_info *p = m->private; + loff_t n = *pos; /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. */ spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (item_index++ < items_to_skip) - continue; - if (iface_entry->active) { - stats = dev_get_stats(iface_entry->net_dev, - &dev_stats); - } else { - stats = &no_dev_stats; - } - /* - * If the meaning of the data changes, then update the fmtX - * string. - */ - if (fmt == 1) { - len = snprintf( - outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals_via_dev[IFS_RX].bytes, - iface_entry->totals_via_dev[IFS_RX].packets, - iface_entry->totals_via_dev[IFS_TX].bytes, - iface_entry->totals_via_dev[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets - ); - } else { - len = pp_iface_stat_line(false, outp, char_count, - iface_entry); - } - if (len >= char_count) { - spin_unlock_bh(&iface_stat_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } + if (unlikely(module_passive)) + return NULL; + + if (!n && p->fmt == 2) + pp_iface_stat_header(m); + + return seq_list_start(&iface_stat_list, n); +} + +static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos) +{ + return seq_list_next(p, &iface_stat_list, pos); +} + +static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p) +{ spin_unlock_bh(&iface_stat_list_lock); +} - *eof = 1; - return outp - page; +static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) +{ + struct proc_iface_stat_fmt_info *p = m->private; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + + CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + iface_entry = list_entry(v, struct iface_stat, list); + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (p->fmt == 1) { + seq_printf(m, "%s %d %llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + pp_iface_stat_line(m, iface_entry); + } + return 0; } +static const struct file_operations read_u64_fops = { + .read = read_proc_u64, + .llseek = default_llseek, +}; + +static const struct file_operations read_bool_fops = { + .read = read_proc_bool, + .llseek = default_llseek, +}; + static void iface_create_proc_worker(struct work_struct *work) { struct proc_dir_entry *proc_entry; @@ -926,20 +802,20 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; - create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].bytes); - create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].bytes); - create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].packets); - create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].packets); - create_proc_read_entry("active", proc_iface_perms, proc_entry, - read_proc_bool, &new_iface->active); + proc_create_data("tx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].bytes); + proc_create_data("rx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].bytes); + proc_create_data("tx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].packets); + proc_create_data("rx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].packets); + proc_create_data("active", proc_iface_perms, proc_entry, + &read_bool_fops, &new_iface->active); IF_DEBUG("qtaguid: iface_stat: create_proc(): done " "entry=%p dev=%s\n", new_iface, new_iface->ifname); @@ -1596,6 +1472,33 @@ static struct notifier_block iface_inet6addr_notifier_blk = { .notifier_call = iface_inet6addr_event_handler, }; +static const struct seq_operations iface_stat_fmt_proc_seq_ops = { + .start = iface_stat_fmt_proc_start, + .next = iface_stat_fmt_proc_next, + .stop = iface_stat_fmt_proc_stop, + .show = iface_stat_fmt_proc_show, +}; + +static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file) +{ + struct proc_iface_stat_fmt_info *s; + + s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops, + sizeof(struct proc_iface_stat_fmt_info)); + if (!s) + return -ENOMEM; + + s->fmt = (uintptr_t)PDE_DATA(inode); + return 0; +} + +static const struct file_operations proc_iface_stat_fmt_fops = { + .open = proc_iface_stat_fmt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) { int err; @@ -1607,29 +1510,29 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) goto err; } - iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)1 /* fmt1 */); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_all_procfile->data = (void *)1; /* fmt1 */ - iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)2 /* fmt2 */); if (!iface_stat_fmt_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_all proc entry\n"); err = -1; goto err_zap_all_stats_entry; } - iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); @@ -1934,43 +1837,85 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) static void prdebug_full_state(int indent_level, const char *fmt, ...) {} #endif +struct proc_ctrl_print_info { + struct sock *sk; /* socket found by reading to sk_pos */ + loff_t sk_pos; +}; + +static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry = v; + struct rb_node *node; + + (*pos)++; + + if (!v || v == SEQ_START_TOKEN) + return NULL; + + node = rb_next(&sock_tag_entry->sock_node); + if (!node) { + pcpi->sk = NULL; + sock_tag_entry = SEQ_START_TOKEN; + } else { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } + pcpi->sk_pos = *pos; + return sock_tag_entry; +} + +static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry; + struct rb_node *node; + + spin_lock_bh(&sock_tag_list_lock); + + if (unlikely(module_passive)) + return NULL; + + if (*pos == 0) { + pcpi->sk_pos = 0; + node = rb_first(&sock_tag_tree); + if (!node) { + pcpi->sk = NULL; + return SEQ_START_TOKEN; + } + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } else { + sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) : + NULL) ?: SEQ_START_TOKEN; + if (*pos != pcpi->sk_pos) { + /* seq_read skipped a next call */ + *pos = pcpi->sk_pos; + return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos); + } + } + return sock_tag_entry; +} + +static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v) +{ + spin_unlock_bh(&sock_tag_list_lock); +} + /* * Procfs reader to get all active socket tags using style "1)" as described in * fs/proc/generic.c */ -static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) +static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) { - char *outp = page; - int len; + struct sock_tag *sock_tag_entry = v; uid_t uid; - struct rb_node *node; - struct sock_tag *sock_tag_entry; - int item_index = 0; - int indent_level = 0; long f_count; - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } - - if (*eof) - return 0; - - CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " - "page=%p off=%ld char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); - spin_lock_bh(&sock_tag_list_lock); - for (node = rb_first(&sock_tag_tree); - node; - node = rb_next(node)) { - if (item_index++ < items_to_skip) - continue; - sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " "pid=%u\n", @@ -1981,66 +1926,42 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, ); f_count = atomic_long_read( &sock_tag_entry->socket->file->f_count); - len = snprintf(outp, char_count, - "sock=%p tag=0x%llx (uid=%u) pid=%u " - "f_count=%lu\n", - sock_tag_entry->sk, - sock_tag_entry->tag, uid, - sock_tag_entry->pid, f_count); - if (len >= char_count) { - spin_unlock_bh(&sock_tag_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - spin_unlock_bh(&sock_tag_list_lock); - - if (item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "events: sockets_tagged=%llu " - "sockets_untagged=%llu " - "counter_set_changes=%llu " - "delete_cmds=%llu " - "iface_events=%llu " - "match_calls=%llu " - "match_calls_prepost=%llu " - "match_found_sk=%llu " - "match_found_sk_in_ct=%llu " - "match_found_no_sk_in_ct=%llu " - "match_no_sk=%llu " - "match_no_sk_file=%llu\n", - atomic64_read(&qtu_events.sockets_tagged), - atomic64_read(&qtu_events.sockets_untagged), - atomic64_read(&qtu_events.counter_set_changes), - atomic64_read(&qtu_events.delete_cmds), - atomic64_read(&qtu_events.iface_events), - atomic64_read(&qtu_events.match_calls), - atomic64_read(&qtu_events.match_calls_prepost), - atomic64_read(&qtu_events.match_found_sk), - atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read( - &qtu_events.match_found_no_sk_in_ct), - atomic64_read(&qtu_events.match_no_sk), - atomic64_read(&qtu_events.match_no_sk_file)); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - - /* Count the following as part of the last item_index */ - if (item_index > items_to_skip) { - prdebug_full_state(indent_level, "proc ctrl"); + seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + } else { + seq_printf(m, "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_calls_prepost=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read(&qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + + /* Count the following as part of the last item_index */ + prdebug_full_state(0, "proc ctrl"); } - *eof = 1; - return outp - page; + return 0; } /* @@ -2513,10 +2434,10 @@ static int ctrl_cmd_untag(const char *input) return res; } -static int qtaguid_ctrl_parse(const char *input, int count) +static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) { char cmd; - int res; + ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", input, current->pid, current->tgid, current_fsuid()); @@ -2547,13 +2468,13 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res); return res; } #define MAX_QTAGUID_CTRL_INPUT_LEN 255 -static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *offp) { char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; @@ -2571,178 +2492,230 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, } struct proc_print_info { - char *outp; - char **num_items_returned; struct iface_stat *iface_entry; - struct tag_stat *ts_entry; int item_index; - int items_to_skip; - int char_count; + tag_t tag; /* tag found by reading to tag_pos */ + off_t tag_pos; + int tag_item_index; }; -static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +static void pp_stats_header(struct seq_file *m) { - int len; - struct data_counters *cnts; + seq_puts(m, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); +} - if (!ppi->item_index) { - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - len = snprintf(ppi->outp, ppi->char_count, - "idx iface acct_tag_hex uid_tag_int cnt_set " - "rx_bytes rx_packets " - "tx_bytes tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n"); - } else { - tag_t tag = ppi->ts_entry->tn.tag; - uid_t stat_uid = get_uid_from_tag(tag); - /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) - && !can_read_other_uid_stats(stat_uid)) { - CT_DEBUG("qtaguid: stats line: " - "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u stats.gid=%u\n", - ppi->iface_entry->ifname, - get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid(), - xt_qtaguid_stats_file->gid); - return 0; - } - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - cnts = &ppi->ts_entry->counters; - len = snprintf( - ppi->outp, ppi->char_count, - "%d %s 0x%llx %u %u " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu\n", - ppi->item_index, - ppi->iface_entry->ifname, - get_atag_from_tag(tag), - stat_uid, - cnt_set, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; -} - -static bool pp_sets(struct proc_print_info *ppi) -{ - int len; +static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, + int cnt_set) +{ + int ret; + struct data_counters *cnts; + tag_t tag = ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + struct proc_print_info *ppi = m->private; + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); + return 0; + } + ppi->item_index++; + cnts = &ts_entry->counters; + ret = seq_printf(m, "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + return ret ?: 1; +} + +static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) +{ + int ret; int counter_set; for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; counter_set++) { - len = pp_stats_line(ppi, counter_set); - if (len >= ppi->char_count) { - *ppi->outp = '\0'; + ret = pp_stats_line(m, ts_entry, counter_set); + if (ret < 0) return false; - } - if (len) { - ppi->outp += len; - ppi->char_count -= len; - (*ppi->num_items_returned)++; - } } return true; } -/* - * Procfs reader to get all tag stats using style "1)" as described in - * fs/proc/generic.c - * Groups all protocols tx/rx bytes. - */ -static int qtaguid_stats_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) -{ - struct proc_print_info ppi; - int len; - - ppi.outp = page; - ppi.item_index = 0; - ppi.char_count = char_count; - ppi.num_items_returned = num_items_returned; - ppi.items_to_skip = items_to_skip; - - if (unlikely(module_passive)) { - len = pp_stats_line(&ppi, 0); - /* The header should always be shorter than the buffer. */ - BUG_ON(len >= ppi.char_count); - (*num_items_returned)++; - *eof = 1; - return len; - } - - CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); - - if (*eof) - return 0; +static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr) +{ + struct iface_stat *iface_entry; + + if (!ptr) + return false; - /* The idx is there to help debug when things go belly up. */ - len = pp_stats_line(&ppi, 0); - /* Don't advance the outp unless the whole line was printed */ - if (len >= ppi.char_count) { - *ppi.outp = '\0'; - return ppi.outp - page; + list_for_each_entry(iface_entry, &iface_stat_list, list) + if (iface_entry == ptr) + return true; + return false; +} + +static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi) +{ + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); + list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) { + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + return; } - if (len) { - ppi.outp += len; - ppi.char_count -= len; - (*num_items_returned)++; + ppi->iface_entry = NULL; +} + +static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry; + struct rb_node *node; + + if (!v) { + pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__); + return NULL; } + (*pos)++; + + if (!ppi->iface_entry || unlikely(module_passive)) + return NULL; + + if (v == SEQ_START_TOKEN) + node = rb_first(&ppi->iface_entry->tag_stat_tree); + else + node = rb_next(&((struct tag_stat *)v)->tn.node); + + while (!node) { + qtaguid_stats_proc_next_iface_entry(ppi); + if (!ppi->iface_entry) + return NULL; + node = rb_first(&ppi->iface_entry->tag_stat_tree); + } + + ts_entry = rb_entry(node, struct tag_stat, tn.node); + ppi->tag = ts_entry->tn.tag; + ppi->tag_pos = *pos; + ppi->tag_item_index = ppi->item_index; + return ts_entry; +} + +static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry = NULL; + spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { - struct rb_node *node; - spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); - for (node = rb_first(&ppi.iface_entry->tag_stat_tree); - node; - node = rb_next(node)) { - ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); - if (!pp_sets(&ppi)) { - spin_unlock_bh( - &ppi.iface_entry->tag_stat_list_lock); - spin_unlock_bh(&iface_stat_list_lock); - return ppi.outp - page; - } + + if (*pos == 0) { + ppi->item_index = 1; + ppi->tag_pos = 0; + if (list_empty(&iface_stat_list)) { + ppi->iface_entry = NULL; + } else { + ppi->iface_entry = list_first_entry(&iface_stat_list, + struct iface_stat, + list); + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + } + return SEQ_START_TOKEN; + } + if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) { + if (ppi->iface_entry) { + pr_err("qtaguid: %s(): iface_entry %p not found\n", + __func__, ppi->iface_entry); + ppi->iface_entry = NULL; + } + return NULL; + } + + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + + if (!ppi->tag_pos) { + /* seq_read skipped first next call */ + ts_entry = SEQ_START_TOKEN; + } else { + ts_entry = tag_stat_tree_search( + &ppi->iface_entry->tag_stat_tree, ppi->tag); + if (!ts_entry) { + pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n", + __func__, ppi->tag); + return NULL; } - spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); } + + if (*pos == ppi->tag_pos) { /* normal resume */ + ppi->item_index = ppi->tag_item_index; + } else { + /* seq_read skipped a next call */ + *pos = ppi->tag_pos; + ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos); + } + + return ts_entry; +} + +static void qtaguid_stats_proc_stop(struct seq_file *m, void *v) +{ + struct proc_print_info *ppi = m->private; + if (ppi->iface_entry) + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); spin_unlock_bh(&iface_stat_list_lock); +} - *eof = 1; - return ppi.outp - page; +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_show(struct seq_file *m, void *v) +{ + struct tag_stat *ts_entry = v; + + if (v == SEQ_START_TOKEN) + pp_stats_header(m); + else + pp_sets(m, ts_entry); + + return 0; } /*------------------------------------------*/ @@ -2907,6 +2880,47 @@ static struct miscdevice qtu_device = { /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ }; +static const struct seq_operations proc_qtaguid_ctrl_seqops = { + .start = qtaguid_ctrl_proc_start, + .next = qtaguid_ctrl_proc_next, + .stop = qtaguid_ctrl_proc_stop, + .show = qtaguid_ctrl_proc_show, +}; + +static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_ctrl_seqops, + sizeof(struct proc_ctrl_print_info)); +} + +static const struct file_operations proc_qtaguid_ctrl_fops = { + .open = proc_qtaguid_ctrl_open, + .read = seq_read, + .write = qtaguid_ctrl_proc_write, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +static const struct seq_operations proc_qtaguid_stats_seqops = { + .start = qtaguid_stats_proc_start, + .next = qtaguid_stats_proc_next, + .stop = qtaguid_stats_proc_stop, + .show = qtaguid_stats_proc_show, +}; + +static int proc_qtaguid_stats_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_stats_seqops, + sizeof(struct proc_print_info)); +} + +static const struct file_operations proc_qtaguid_stats_fops = { + .open = proc_qtaguid_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + /*------------------------------------------*/ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) { @@ -2918,26 +2932,27 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) goto no_dir; } - xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, - *res_procdir); + xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms, + *res_procdir, + &proc_qtaguid_ctrl_fops, + NULL); if (!xt_qtaguid_ctrl_file) { pr_err("qtaguid: failed to create xt_qtaguid/ctrl " " file\n"); ret = -ENOMEM; goto no_ctrl_entry; } - xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; - xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; - xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, - *res_procdir); + xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms, + *res_procdir, + &proc_qtaguid_stats_fops, + NULL); if (!xt_qtaguid_stats_file) { pr_err("qtaguid: failed to create xt_qtaguid/stats " "file\n"); ret = -ENOMEM; goto no_stats_entry; } - xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; /* * TODO: add support counter hacking * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; From 70be2dc15ef202b4fd513a52c0a3f95d673ec0a6 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 20 Feb 2013 16:38:34 -0800 Subject: [PATCH 0918/1276] ANDROID: netfilter: xt_qtaguid: fix bad tcp_time_wait sock handling Since (41063e9 ipv4: Early TCP socket demux), skb's can have an sk which is not a struct sock but the smaller struct inet_timewait_sock without an sk->sk_socket. Now we bypass sk_state == TCP_TIME_WAIT Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 505dbab40210..eee667987f7d 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1597,14 +1597,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; } - /* - * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. - * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 - * Not fixed in 3.0-r3 :( - */ if (sk) { MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + */ if (sk->sk_state == TCP_TIME_WAIT) { xt_socket_put_sk(sk); sk = NULL; @@ -1688,6 +1687,13 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } sk = skb->sk; + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + * So we ignore it. + */ + if (sk && sk->sk_state == TCP_TIME_WAIT) + sk = NULL; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight From 7699377664632e45cfea042140b8d4f191565f24 Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 14 Apr 2014 21:20:49 -0700 Subject: [PATCH 0919/1276] ANDROID: netfilter: xt_qtaguid: Fix boot panic We need the change below because of mainline commit 351638e7de (net: pass info struct via netdevice notifier). Otherwise we panic. Change-Id: I7daf7513a733933fdcbaeebea7f8191f8b6a0432 Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index eee667987f7d..4f574a6fc1fb 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1375,7 +1375,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, static int iface_netdev_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (unlikely(module_passive)) return NOTIFY_DONE; From 578ff347b3f84d242531a959cab1ec4e46a63643 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 28 Mar 2014 16:23:48 -0700 Subject: [PATCH 0920/1276] ANDROID: netfilter: xt_qtaguid/xt_socket: Build fixups Fix up build kuid/kguid build issues in netfilter code. Also re-add the xt_socket_get/put_sk interfaces needed by xt_qtaguid. Change-Id: I7027fb840e109785bddffe8ea717b8d018b26d82 Signed-off-by: John Stultz [AmitP: Folded following android-4.9 commit changes into this patch da5ea99a74f2 ("ANDROID: netfilter: xt_qtaguid: fix seq_printf type mismatch warning") 070eff8f023c ("ANDROID: netfilter: xt_qtaguid: fix broken uid/gid range check")] 2879b6ec24ee ("ANDROID: xt_qtaguid: use sock_gen_put() instead of xt_socket_put_sk()")] Signed-off-by: Amit Pundir --- include/uapi/linux/netfilter/xt_socket.h | 5 + net/netfilter/xt_qtaguid.c | 149 +++++++++++++---------- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index a7bdc9d882b0..5075d2e3ae30 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -27,4 +27,9 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) +struct sock *xt_socket_get4_sk(const struct sk_buff *skb, + struct xt_action_param *par); +struct sock *xt_socket_get6_sk(const struct sk_buff *skb, + struct xt_action_param *par); + #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4f574a6fc1fb..cd2da5460db2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -145,22 +145,22 @@ static bool can_manipulate_uids(void) { /* root pwnd */ return in_egroup_p(xt_qtaguid_ctrl_file->gid) - || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) - || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited) + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } -static bool can_impersonate_uid(uid_t uid) +static bool can_impersonate_uid(kuid_t uid) { - return uid == current_fsuid() || can_manipulate_uids(); + return uid_eq(uid, current_fsuid()) || can_manipulate_uids(); } -static bool can_read_other_uid_stats(uid_t uid) +static bool can_read_other_uid_stats(kuid_t uid) { /* root pwnd */ return in_egroup_p(xt_qtaguid_stats_file->gid) - || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid()) || unlikely(!proc_stats_readall_limited) - || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -542,7 +542,7 @@ static void put_utd_entry(struct uid_tag_data *utd_entry) "erase utd_entry=%p uid=%u " "by pid=%u tgid=%u uid=%u\n", __func__, utd_entry, utd_entry->uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); BUG_ON(utd_entry->num_active_tags); rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); @@ -744,7 +744,7 @@ static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); iface_entry = list_entry(v, struct iface_stat, list); @@ -1605,7 +1605,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * "struct inet_timewait_sock" which is missing fields. */ if (sk->sk_state == TCP_TIME_WAIT) { - xt_socket_put_sk(sk); + sock_gen_put(sk); sk = NULL; } } @@ -1656,7 +1656,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct file *filp; bool got_sock = false; struct sock *sk; - uid_t sock_uid; + kuid_t sock_uid; bool res; if (unlikely(module_passive)) @@ -1720,7 +1720,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? filp->f_cred->fsuid : -1); + par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); } if (sk == NULL || sk->sk_socket == NULL) { @@ -1761,7 +1761,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * For now we only do iface stats when the uid-owner is not requested */ if (!(info->match & XT_QTAGUID_UID)) - account_for_uid(skb, sk, sock_uid, par); + account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); /* * The following two tests fail the match when: @@ -1769,31 +1769,38 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * or id in range AND inverted condition requested * Thus (!a && b) || (a && !b) == a ^ b */ - if (info->match & XT_QTAGUID_UID) - if ((filp->f_cred->fsuid >= info->uid_min && - filp->f_cred->fsuid <= info->uid_max) ^ + if (info->match & XT_QTAGUID_UID) { + kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); + kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); + + if ((uid_gte(filp->f_cred->fsuid, uid_min) && + uid_lte(filp->f_cred->fsuid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - if (info->match & XT_QTAGUID_GID) - if ((filp->f_cred->fsgid >= info->gid_min && - filp->f_cred->fsgid <= info->gid_max) ^ + } + if (info->match & XT_QTAGUID_GID) { + kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); + kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); + + if ((gid_gte(filp->f_cred->fsgid, gid_min) && + gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - + } MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); res = true; put_sock_ret_res: if (got_sock) - xt_socket_put_sk(sk); + sock_gen_put(sk); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; @@ -1919,7 +1926,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) long f_count; CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); @@ -1950,18 +1957,18 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) "match_found_no_sk_in_ct=%llu " "match_no_sk=%llu " "match_no_sk_file=%llu\n", - atomic64_read(&qtu_events.sockets_tagged), - atomic64_read(&qtu_events.sockets_untagged), - atomic64_read(&qtu_events.counter_set_changes), - atomic64_read(&qtu_events.delete_cmds), - atomic64_read(&qtu_events.iface_events), - atomic64_read(&qtu_events.match_calls), - atomic64_read(&qtu_events.match_calls_prepost), - atomic64_read(&qtu_events.match_found_sk), - atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read(&qtu_events.match_found_no_sk_in_ct), - atomic64_read(&qtu_events.match_no_sk), - atomic64_read(&qtu_events.match_no_sk_file)); + (u64)atomic64_read(&qtu_events.sockets_tagged), + (u64)atomic64_read(&qtu_events.sockets_untagged), + (u64)atomic64_read(&qtu_events.counter_set_changes), + (u64)atomic64_read(&qtu_events.delete_cmds), + (u64)atomic64_read(&qtu_events.iface_events), + (u64)atomic64_read(&qtu_events.match_calls), + (u64)atomic64_read(&qtu_events.match_calls_prepost), + (u64)atomic64_read(&qtu_events.match_found_sk), + (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_no_sk), + (u64)atomic64_read(&qtu_events.match_no_sk_file)); /* Count the following as part of the last item_index */ prdebug_full_state(0, "proc ctrl"); @@ -1977,7 +1984,8 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) static int ctrl_cmd_delete(const char *input) { char cmd; - uid_t uid; + int uid_int; + kuid_t uid; uid_t entry_uid; tag_t acct_tag; tag_t tag; @@ -1991,10 +1999,11 @@ static int ctrl_cmd_delete(const char *input) struct tag_ref *tr_entry; struct uid_tag_data *utd_entry; - argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "user_tag=0x%llx uid=%u\n", input, argc, cmd, - acct_tag, uid); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2006,18 +2015,19 @@ static int ctrl_cmd_delete(const char *input) } if (argc < 3) { uid = current_fsuid(); + uid_int = from_kuid(&init_user_ns, uid); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_delete(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } - tag = combine_atag_with_uid(acct_tag, uid); + tag = combine_atag_with_uid(acct_tag, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): " "looking for tag=0x%llx (uid=%u)\n", - input, tag, uid); + input, tag, uid_int); /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); @@ -2026,7 +2036,7 @@ static int ctrl_cmd_delete(const char *input) st_entry = rb_entry(node, struct sock_tag, sock_node); entry_uid = get_uid_from_tag(st_entry->tag); node = rb_next(node); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", @@ -2087,7 +2097,7 @@ static int ctrl_cmd_delete(const char *input) "ts tag=0x%llx (uid=%u)\n", input, ts_entry->tn.tag, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; if (!acct_tag || ts_entry->tn.tag == tag) { CT_DEBUG("qtaguid: ctrl_delete(%s): " @@ -2116,7 +2126,7 @@ static int ctrl_cmd_delete(const char *input) "utd uid=%u\n", input, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; /* * Go over the tag_refs, and those that don't have @@ -2160,7 +2170,7 @@ static int ctrl_cmd_counter_set(const char *input) if (!can_manipulate_uids()) { pr_info("qtaguid: ctrl_counterset(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } @@ -2197,7 +2207,8 @@ static int ctrl_cmd_tag(const char *input) { char cmd; int sock_fd = 0; - uid_t uid = 0; + kuid_t uid; + unsigned int uid_int = 0; tag_t acct_tag = make_atag_from_value(0); tag_t full_tag; struct socket *el_socket; @@ -2208,10 +2219,11 @@ static int ctrl_cmd_tag(const char *input) struct proc_qtu_data *pqd_entry; /* Unassigned args will get defaulted later. */ - argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, - acct_tag, uid); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2221,7 +2233,7 @@ static int ctrl_cmd_tag(const char *input) pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2237,21 +2249,24 @@ static int ctrl_cmd_tag(const char *input) CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", - input, current->pid, current->tgid, current_uid(), - current_euid(), current_fsuid(), - xt_qtaguid_ctrl_file->gid, + input, current->pid, current->tgid, + from_kuid(&init_user_ns, current_uid()), + from_kuid(&init_user_ns, current_euid()), + from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns, xt_qtaguid_ctrl_file->gid), in_group_p(xt_qtaguid_ctrl_file->gid), in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); + uid_int = from_kuid(&init_user_ns, uid); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_tag(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err_put; } - full_tag = combine_atag_with_uid(acct_tag, uid); + full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); @@ -2298,8 +2313,7 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->socket = el_socket; sock_tag_entry->pid = current->tgid; - sock_tag_entry->tag = combine_atag_with_uid(acct_tag, - uid); + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); @@ -2314,7 +2328,7 @@ static int ctrl_cmd_tag(const char *input) "User space forgot to open /dev/xt_qtaguid? " "pid=%u tgid=%u uid=%u\n", __func__, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); @@ -2369,7 +2383,7 @@ static int ctrl_cmd_untag(const char *input) pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2403,7 +2417,7 @@ static int ctrl_cmd_untag(const char *input) pr_warn_once("qtaguid: %s(): " "User space forgot to open /dev/xt_qtaguid? " "pid=%u tgid=%u uid=%u\n", __func__, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); else list_del(&sock_tag_entry->list); spin_unlock_bh(&uid_tag_data_tree_lock); @@ -2446,7 +2460,7 @@ static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); cmd = input[0]; /* Collect params for commands */ @@ -2528,14 +2542,15 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, uid_t stat_uid = get_uid_from_tag(tag); struct proc_print_info *ppi = m->private; /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) { + if (get_atag_from_tag(tag) && !can_read_other_uid_stats( + make_kuid(&init_user_ns,stat_uid))) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid(), - xt_qtaguid_stats_file->gid); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid)); return 0; } ppi->item_index++; @@ -2737,12 +2752,12 @@ static int qtudev_open(struct inode *inode, struct file *file) return 0; DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); spin_lock_bh(&uid_tag_data_tree_lock); /* Look for existing uid data, or alloc one. */ - utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); goto err_unlock; @@ -2754,7 +2769,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u " "%s already opened\n", - current->pid, current->tgid, current_fsuid(), + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), QTU_DEV_NAME); res = -EBUSY; goto err_unlock_free_utd; @@ -2764,7 +2779,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (!new_pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u: " "proc data alloc failed\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -ENOMEM; goto err_unlock_free_utd; } @@ -2778,7 +2793,7 @@ static int qtudev_open(struct inode *inode, struct file *file) spin_unlock_bh(&uid_tag_data_tree_lock); DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", - current_fsuid(), new_pqd_entry); + from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry); file->private_data = new_pqd_entry; return 0; From 9712945dba312f1e7ec9dc96fe77c3ef59fb1d2b Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Tue, 13 Jan 2015 19:20:44 -0800 Subject: [PATCH 0921/1276] ANDROID: netfilter: xt_qtaguid: Use sk_callback_lock read locks before reading sk->sk_socket It prevents a kernel panic when accessing sk->sk_socket fields due to NULLing sk->sk_socket when sock_orphan is called through sk_common_release. Change-Id: I4aa46b4e2d8600e4d4ef8dcdd363aa4e6e5f8433 Signed-off-by: Mohamad Ayyash (cherry picked from commit cdea0ebcb8bcfe57688f6cb692b49e550ebd9796) Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index cd2da5460db2..2f9784c1e692 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1658,6 +1658,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; kuid_t sock_uid; bool res; + bool set_sk_callback_lock = false; if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1715,6 +1716,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); @@ -1801,6 +1804,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) put_sock_ret_res: if (got_sock) sock_gen_put(sk); + if (set_sk_callback_lock) + read_unlock_bh(&sk->sk_callback_lock); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; From a8807cbda08a26461a172f8b0892dc37f609f1c2 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Jul 2015 00:28:49 +0530 Subject: [PATCH 0922/1276] ANDROID: netfilter: xt_qtaguid: xt_socket: build fixes Add missing header and use xt_socket_lookup_slow_v* instead of xt_socket_get*_sk in xt_qtaguid.c. Fix xt_socket_lookup_slow_v* functions in xt_socket.c and declare them in xt_socket.h Change-Id: I55819b2d4ffa82a2be20995c87d28fb5cc77b5ba Signed-off-by: John Stultz [AmitP: Upstream commit 8db4c5be88f6 ("netfilter: move socket lookup infrastructure to nf_socket_ipv{4,6}.c")] moved socket lookup to nf_socket_ipv{4,6}.c, hence use nf_sk_lookup_slow_v[4|6]() instead of obsolete xt_socket_lookup_slow_v[4|6](). Also folded following android-4.9 commit changes into this patch 7de1bb86dc5a ("ANDROID: netfilter: xt_qtaguid/socket: build fixes for 4.4") 5b5ab94817f9 ("ANDROID: netfilter: xt_qtaguid: seq_printf fixes")] Signed-off-by: Amit Pundir --- include/uapi/linux/netfilter/xt_socket.h | 5 ----- net/netfilter/xt_qtaguid.c | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index 5075d2e3ae30..a7bdc9d882b0 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -27,9 +27,4 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) -struct sock *xt_socket_get4_sk(const struct sk_buff *skb, - struct xt_action_param *par); -struct sock *xt_socket_get6_sk(const struct sk_buff *skb, - struct xt_action_param *par); - #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2f9784c1e692..b43e7dc301e7 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1588,10 +1589,10 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, switch (par->family) { case NFPROTO_IPV6: - sk = xt_socket_get6_sk(skb, par); + sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, par->in); break; case NFPROTO_IPV4: - sk = xt_socket_get4_sk(skb, par); + sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, par->in); break; default: return NULL; @@ -2541,7 +2542,6 @@ static void pp_stats_header(struct seq_file *m) static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, int cnt_set) { - int ret; struct data_counters *cnts; tag_t tag = ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); @@ -2560,7 +2560,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, } ppi->item_index++; cnts = &ts_entry->counters; - ret = seq_printf(m, "%d %s 0x%llx %u %u " + seq_printf(m, "%d %s 0x%llx %u %u " "%llu %llu " "%llu %llu " "%llu %llu " @@ -2590,7 +2590,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - return ret ?: 1; + return seq_has_overflowed(m) ? -ENOSPC : 1; } static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) From cf80dd989ecc5b3d2bb8a668c074219ba8fe5e50 Mon Sep 17 00:00:00 2001 From: "liping.zhang" Date: Mon, 11 Jan 2016 13:31:01 +0800 Subject: [PATCH 0923/1276] ANDROID: netfilter: xt_qtaguid: fix a race condition in if_tag_stat_update Miss a lock protection in if_tag_stat_update while doing get_iface_entry. So if one CPU is doing iface_stat_create while another CPU is doing if_tag_stat_update, race will happened. Change-Id: Ib8d98e542f4e385685499f5b7bb7354f08654a75 Signed-off-by: Liping Zhang --- net/netfilter/xt_qtaguid.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b43e7dc301e7..243d9b8e84e9 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1291,11 +1291,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); - + spin_lock_bh(&iface_stat_list_lock); iface_entry = get_iface_entry(ifname); if (!iface_entry) { pr_err_ratelimited("qtaguid: iface_stat: stat_update() " "%s not found\n", ifname); + spin_unlock_bh(&iface_stat_list_lock); return; } /* It is ok to process data when an iface_entry is inactive */ @@ -1331,8 +1332,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * {0, uid_tag} will also get updated. */ tag_stat_update(tag_stat_entry, direction, proto, bytes); - spin_unlock_bh(&iface_entry->tag_stat_list_lock); - return; + goto unlock; } /* Loop over tag list under this interface for {0,uid_tag} */ @@ -1372,6 +1372,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag_stat_update(new_tag_stat, direction, proto, bytes); unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); } static int iface_netdev_event_handler(struct notifier_block *nb, From e316f8ef1d6dd3bd42931474afe5e5d24961aab5 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 22 Apr 2016 17:12:57 -0700 Subject: [PATCH 0924/1276] ANDROID: netfilter: xt_qtaguid: Fix panic caused by synack processing In upstream commit ca6fb06518836ef9b65dc0aac02ff97704d52a05 (tcp: attach SYNACK messages to request sockets instead of listener) http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ca6fb0651883 The building of synack messages was changed, which made it so the skb->sk points to a casted request_sock. This is problematic, as there is no sk_socket in a request_sock. So when the qtaguid_mt function tries to access the sk->sk_socket, it accesses uninitialized memory. After looking at how other netfilter implementations handle this, I realized there was a skb_to_full_sk() helper added, which the xt_qtaguid code isn't yet using. This patch adds its use, and resovles panics seen when accessing uninitialzed memory when processing synack packets. Reported-by: YongQin Liu Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 243d9b8e84e9..3f5a107342af 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1689,7 +1689,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) /* default: Fall through and do UID releated work */ } - sk = skb->sk; + sk = skb_to_full_sk(skb); /* * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. From 7ec8644b69e790f6a780693f2c86185b1d938bb3 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 12 May 2016 11:17:52 -0700 Subject: [PATCH 0925/1276] ANDROID: netfilter: xt_qtaguid: Fix panic caused by processing non-full socket. In an issue very similar to 4e461c777e3 (xt_qtaguid: Fix panic caused by synack processing), we were seeing panics on occasion in testing. In this case, it was the same issue, but caused by a different call path, as the sk being returned from qtaguid_find_sk() was not a full socket. Resulting in the sk->sk_socket deref to fail. This patch adds an extra check to ensure the sk being retuned is a full socket, and if not it returns NULL. Reported-by: Milosz Wasilewski Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3f5a107342af..1c419df66958 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1606,7 +1606,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. */ - if (sk->sk_state == TCP_TIME_WAIT) { + if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { sock_gen_put(sk); sk = NULL; } From 963783b29444fd25fdf564b6b215dfbe155b1d50 Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Wed, 11 May 2016 13:18:35 -0700 Subject: [PATCH 0926/1276] ANDROID: netfilter: xt_qtaguid: Don't show empty tag stats for unprivileged uids BUG: 27577101 BUG: 27532522 Change-Id: Ibee3c5d224f139b9312a40acb203e87aa7060797 Signed-off-by: Mohamad Ayyash --- net/netfilter/xt_qtaguid.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 1c419df66958..8e971c9065cc 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1946,7 +1946,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) ); f_count = atomic_long_read( &sock_tag_entry->socket->file->f_count); - seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " + seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " "f_count=%lu\n", sock_tag_entry->sk, sock_tag_entry->tag, uid, @@ -2548,8 +2548,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, uid_t stat_uid = get_uid_from_tag(tag); struct proc_print_info *ppi = m->private; /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) && !can_read_other_uid_stats( - make_kuid(&init_user_ns,stat_uid))) { + if (!can_read_other_uid_stats(make_kuid(&init_user_ns,stat_uid))) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u stats.gid=%u\n", From 2a653c21d842bf3bbb87f24f60c62532acd5bc5d Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 23 Mar 2017 13:51:24 -0700 Subject: [PATCH 0927/1276] ANDROID: netfilter: xt_qtaguid: fix the deadlock when enable DDEBUG When DDEBUG is enabled, the prdebug_full_state() function will try to recursively aquire the spinlock of sock_tag_list and causing deadlock. A check statement is added before it aquire the spinlock to differentiate the behavior depend on the caller of the function. Bug: 36559739 Test: Compile and run test under system/extra/test/iptables/ Change-Id: Ie3397fbaa207e14fe214d47aaf5e8ca1f4a712ee Signed-off-by: Chenbo Feng (cherry picked from commit f0faedd6b468777f3bb5834f97100794d562c8b7) --- net/netfilter/xt_qtaguid.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 8e971c9065cc..44db68bf2575 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1814,8 +1814,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } #ifdef DDEBUG -/* This function is not in xt_qtaguid_print.c because of locks visibility */ -static void prdebug_full_state(int indent_level, const char *fmt, ...) +/* + * This function is not in xt_qtaguid_print.c because of locks visibility. + * The lock of sock_tag_list must be aquired before calling this function + */ +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) { va_list args; char *fmt_buff; @@ -1836,16 +1839,12 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) kfree(buff); va_end(args); - spin_lock_bh(&sock_tag_list_lock); prdebug_sock_tag_tree(indent_level, &sock_tag_tree); - spin_unlock_bh(&sock_tag_list_lock); - spin_lock_bh(&sock_tag_list_lock); spin_lock_bh(&uid_tag_data_tree_lock); prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); spin_unlock_bh(&uid_tag_data_tree_lock); - spin_unlock_bh(&sock_tag_list_lock); spin_lock_bh(&iface_stat_list_lock); prdebug_iface_stat_list(indent_level, &iface_stat_list); @@ -1854,7 +1853,7 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) pr_debug("qtaguid: %s(): }\n", __func__); } #else -static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {} #endif struct proc_ctrl_print_info { @@ -1977,8 +1976,11 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_no_sk), (u64)atomic64_read(&qtu_events.match_no_sk_file)); - /* Count the following as part of the last item_index */ - prdebug_full_state(0, "proc ctrl"); + /* Count the following as part of the last item_index. No need + * to lock the sock_tag_list here since it is already locked when + * starting the seq_file operation + */ + prdebug_full_state_locked(0, "proc ctrl"); } return 0; @@ -2887,8 +2889,10 @@ static int qtudev_release(struct inode *inode, struct file *file) sock_tag_tree_erase(&st_to_free_tree); - prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + spin_lock_bh(&sock_tag_list_lock); + prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__, current->pid, current->tgid); + spin_unlock_bh(&sock_tag_list_lock); return 0; } From f54a807e14e44c2e5d6d9e163cfe1f6be2254a72 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 24 Apr 2017 16:16:15 -0700 Subject: [PATCH 0928/1276] ANDROID: netfilter: xt_qtaguid: don't check if embedded arrays are NULL clang warns about four NULL pointer checks: net/netfilter/xt_qtaguid.c:973:11: warning: address of array 'ifa->ifa_label' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:974:13: warning: address of array 'ifa->ifa_label' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:1212:31: warning: address of array 'el_dev->name' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:1640:31: warning: address of array 'el_dev->name' will always evaluate to 'true' [-Wpointer-bool-conversion] Both of these fields are embedded char[16] arrays rather than pointers, so they can never be NULL. Change-Id: I748ff6dd11569e5596a9d5cecdf9c334847e7307 Signed-off-by: Greg Hackmann --- net/netfilter/xt_qtaguid.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 44db68bf2575..caba267c6289 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -969,9 +969,8 @@ static void iface_stat_create(struct net_device *net_dev, for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { IF_DEBUG("qtaguid: iface_stat: create(%s): " "ifa=%p ifa_label=%s\n", - ifname, ifa, - ifa->ifa_label ? ifa->ifa_label : "(null)"); - if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + ifname, ifa, ifa->ifa_label); + if (!strcmp(ifname, ifa->ifa_label)) break; } } @@ -1209,10 +1208,6 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", par->hooknum, __func__); BUG(); - } else if (unlikely(!el_dev->name)) { - pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); - BUG(); } else { proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", @@ -1637,8 +1632,6 @@ static void account_for_uid(const struct sk_buff *skb, if (unlikely(!el_dev)) { pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); - } else if (unlikely(!el_dev->name)) { - pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { int proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", From b3bb9e55fb71152a4948d76ffd805d421208dda1 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 19 Apr 2017 14:22:47 -0700 Subject: [PATCH 0929/1276] ANDROID: netfilter: xt_qtaguid: Add untag hacks to inet_release function To prevent protential risk of memory leak caused by closing socket with out untag it from qtaguid module, the qtaguid module now do not hold any socket file reference count. Instead, it will increase the sk_refcnt of the sk struct to prevent a reuse of the socket pointer. And when a socket is released. It will delete the tag if the socket is previously tagged so no more resources is held by xt_qtaguid moudle. A flag is added to the untag process to prevent possible kernel crash caused by fail to delete corresponding socket_tag_entry list. Bug: 36374484 Test: compile and run test under system/extra/test/iptables, run cts -m CtsNetTestCases -t android.net.cts.SocketRefCntTest Signed-off-by: Chenbo Feng Change-Id: Iea7c3bf0c59b9774a5114af905b2405f6bc9ee52 --- include/linux/netfilter/xt_qtaguid.h | 1 + net/ipv4/af_inet.c | 4 + net/netfilter/xt_qtaguid.c | 107 ++++++++++++++------------- net/netfilter/xt_qtaguid_internal.h | 2 - net/netfilter/xt_qtaguid_print.c | 8 +- 5 files changed, 63 insertions(+), 59 deletions(-) diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h index ca60fbdec2f3..1c671552ec37 100644 --- a/include/linux/netfilter/xt_qtaguid.h +++ b/include/linux/netfilter/xt_qtaguid.h @@ -10,4 +10,5 @@ #define XT_QTAGUID_SOCKET XT_OWNER_SOCKET #define xt_qtaguid_match_info xt_owner_match_info +int qtaguid_untag(struct socket *sock, bool kernel); #endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 32615ca1dd6f..66c70a1becfe 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -89,6 +89,7 @@ #include #include #include +#include #include @@ -426,6 +427,9 @@ int inet_release(struct socket *sock) if (sk) { long timeout; +#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID + qtaguid_untag(sock, true); +#endif /* Applications forget to leave groups before exiting */ ip_mc_drop_socket(sk); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index caba267c6289..a7f4d8b3e9e8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -321,7 +321,7 @@ static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) st_entry->tag, get_uid_from_tag(st_entry->tag)); rb_erase(&st_entry->sock_node, st_to_free_tree); - sockfd_put(st_entry->socket); + sock_put(st_entry->sk); kfree(st_entry); } } @@ -1922,12 +1922,12 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) { struct sock_tag *sock_tag_entry = v; uid_t uid; - long f_count; CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); if (sock_tag_entry != SEQ_START_TOKEN) { + int sk_ref_count; uid = get_uid_from_tag(sock_tag_entry->tag); CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " "pid=%u\n", @@ -1936,13 +1936,13 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) uid, sock_tag_entry->pid ); - f_count = atomic_long_read( - &sock_tag_entry->socket->file->f_count); + sk_ref_count = atomic_read( + &sock_tag_entry->sk->sk_refcnt); seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " - "f_count=%lu\n", + "f_count=%d\n", sock_tag_entry->sk, sock_tag_entry->tag, uid, - sock_tag_entry->pid, f_count); + sock_tag_entry->pid, sk_ref_count); } else { seq_printf(m, "events: sockets_tagged=%llu " "sockets_untagged=%llu " @@ -2238,8 +2238,8 @@ static int ctrl_cmd_tag(const char *input) from_kuid(&init_user_ns, current_fsuid())); goto err; } - CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", - input, atomic_long_read(&el_socket->file->f_count), + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n", + input, atomic_read(&el_socket->sk->sk_refcnt), el_socket->sk); if (argc < 3) { acct_tag = make_atag_from_value(0); @@ -2283,16 +2283,9 @@ static int ctrl_cmd_tag(const char *input) struct tag_ref *prev_tag_ref_entry; CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " - "st@%p ...->f_count=%ld\n", + "st@%p ...->sk_refcnt=%d\n", input, el_socket->sk, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count)); - /* - * This is a re-tagging, so release the sock_fd that was - * locked at the time of the 1st tagging. - * There is still the ref from this call's sockfd_lookup() so - * it can be done within the spinlock. - */ - sockfd_put(sock_tag_entry->socket); + atomic_read(&el_socket->sk->sk_refcnt)); prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &uid_tag_data_entry); BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); @@ -2312,8 +2305,12 @@ static int ctrl_cmd_tag(const char *input) res = -ENOMEM; goto err_tag_unref_put; } + /* + * Hold the sk refcount here to make sure the sk pointer cannot + * be freed and reused + */ + sock_hold(el_socket->sk); sock_tag_entry->sk = el_socket->sk; - sock_tag_entry->socket = el_socket; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&uid_tag_data_tree_lock); @@ -2340,10 +2337,11 @@ static int ctrl_cmd_tag(const char *input) atomic64_inc(&qtu_events.sockets_tagged); } spin_unlock_bh(&sock_tag_list_lock); - /* We keep the ref to the socket (file) until it is untagged */ - CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + /* We keep the ref to the sk until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", input, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count)); + atomic_read(&el_socket->sk->sk_refcnt)); + sockfd_put(el_socket); return 0; err_tag_unref_put: @@ -2351,8 +2349,8 @@ static int ctrl_cmd_tag(const char *input) tag_ref_entry->num_sock_tags--; free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: - CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", - input, atomic_long_read(&el_socket->file->f_count) - 1); + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", + input, atomic_read(&el_socket->sk->sk_refcnt) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); return res; @@ -2368,17 +2366,13 @@ static int ctrl_cmd_untag(const char *input) int sock_fd = 0; struct socket *el_socket; int res, argc; - struct sock_tag *sock_tag_entry; - struct tag_ref *tag_ref_entry; - struct uid_tag_data *utd_entry; - struct proc_qtu_data *pqd_entry; argc = sscanf(input, "%c %d", &cmd, &sock_fd); CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); if (argc < 2) { res = -EINVAL; - goto err; + return res; } el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { @@ -2386,17 +2380,31 @@ static int ctrl_cmd_untag(const char *input) " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); - goto err; + return res; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", input, atomic_long_read(&el_socket->file->f_count), el_socket->sk); + res = qtaguid_untag(el_socket, false); + sockfd_put(el_socket); + return res; +} + +int qtaguid_untag(struct socket *el_socket, bool kernel) +{ + int res; + pid_t pid; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; - goto err_put; + return res; } /* * The socket already belongs to the current process @@ -2408,20 +2416,26 @@ static int ctrl_cmd_untag(const char *input) BUG_ON(!tag_ref_entry); BUG_ON(tag_ref_entry->num_sock_tags <= 0); spin_lock_bh(&uid_tag_data_tree_lock); + if (kernel) + pid = sock_tag_entry->pid; + else + pid = current->tgid; pqd_entry = proc_qtu_data_tree_search( - &proc_qtu_data_tree, current->tgid); + &proc_qtu_data_tree, pid); /* * TODO: remove if, and start failing. * At first, we want to catch user-space code that is not * opening the /dev/xt_qtaguid. */ - if (IS_ERR_OR_NULL(pqd_entry)) + if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) { pr_warn_once("qtaguid: %s(): " "User space forgot to open /dev/xt_qtaguid? " - "pid=%u tgid=%u uid=%u\n", __func__, - current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); - else + "pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__, + current->pid, current->tgid, sock_tag_entry->pid, + from_kuid(&init_user_ns, current_fsuid())); + } else { list_del(&sock_tag_entry->list); + } spin_unlock_bh(&uid_tag_data_tree_lock); /* * We don't free tag_ref from the utd_entry here, @@ -2430,30 +2444,17 @@ static int ctrl_cmd_untag(const char *input) tag_ref_entry->num_sock_tags--; spin_unlock_bh(&sock_tag_list_lock); /* - * Release the sock_fd that was grabbed at tag time, - * and once more for the sockfd_lookup() here. + * Release the sock_fd that was grabbed at tag time. */ - sockfd_put(sock_tag_entry->socket); - CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", - input, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count) - 1); - sockfd_put(el_socket); + sock_put(sock_tag_entry->sk); + CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n", + sock_tag_entry, + atomic_read(&el_socket->sk->sk_refcnt)); kfree(sock_tag_entry); atomic64_inc(&qtu_events.sockets_untagged); return 0; - -err_put: - CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", - input, atomic_long_read(&el_socket->file->f_count) - 1); - /* Release the sock_fd that was grabbed by sockfd_lookup(). */ - sockfd_put(el_socket); - return res; - -err: - CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); - return res; } static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 6dc14a9c6889..8178fbdfb036 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -256,8 +256,6 @@ struct iface_stat_work { struct sock_tag { struct rb_node sock_node; struct sock *sk; /* Only used as a number, never dereferenced */ - /* The socket is needed for sockfd_put() */ - struct socket *socket; /* Used to associate with a given pid */ struct list_head list; /* in proc_qtu_data.sock_tag_list */ pid_t pid; diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index f6a00a3520ed..2a7190d285e6 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -24,7 +24,7 @@ #include #include #include - +#include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -237,10 +237,10 @@ char *pp_sock_tag(struct sock_tag *st) tag_str = pp_tag_t(&st->tag); res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" "sock_node=rb_node{...}, " - "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "sk=%p (f_count=%d), list=list_head{...}, " "pid=%u, tag=%s}", - st, st->sk, st->socket, atomic_long_read( - &st->socket->file->f_count), + st, st->sk, atomic_read( + &st->sk->sk_refcnt), st->pid, tag_str); _bug_on_err_or_null(res); kfree(tag_str); From 13ace9e6c1bfd5dd8389920799992d29059e972e Mon Sep 17 00:00:00 2001 From: Simon Dubray Date: Tue, 1 Aug 2017 18:22:47 +0200 Subject: [PATCH 0930/1276] ANDROID: netfilter: xt_qtaguid: handle properly request sockets To match rules related to uid/gid for syn recv packets we need to get the full socket from request_sock struct. Bug: 63917742 Change-Id: I03acb2251319fd800d0e36a6dde30fc1fbb7d1b0 Signed-off-by: Simon Dubray --- net/netfilter/xt_qtaguid.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index a7f4d8b3e9e8..00a0ebbbb609 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1597,14 +1597,6 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (sk) { MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); - /* - * When in TCP_TIME_WAIT the sk is not a "struct sock" but - * "struct inet_timewait_sock" which is missing fields. - */ - if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { - sock_gen_put(sk); - sk = NULL; - } } return sk; } @@ -1697,10 +1689,25 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ sk = qtaguid_find_sk(skb, par); /* - * If we got the socket from the find_sk(), we will need to put - * it back, as nf_tproxy_get_sock_v4() got it. + * TCP_NEW_SYN_RECV are not "struct sock" but "struct request_sock" + * where we can get a pointer to a full socket to retrieve uid/gid. + * When in TCP_TIME_WAIT, sk is a struct inet_timewait_sock + * which is missing fields and does not contain any reference + * to a full socket, so just ignore the socket. */ - got_sock = sk; + if (sk && sk->sk_state == TCP_NEW_SYN_RECV) { + sock_gen_put(sk); + sk = sk_to_full_sk(sk); + } else if (sk && (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT)) { + sock_gen_put(sk); + sk = NULL; + } else { + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + } if (sk) atomic64_inc(&qtu_events.match_found_sk_in_ct); else From 7dc85913d79a8ad890cfa508d23eca5e7fbf022e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 20 Dec 2013 16:51:11 -0800 Subject: [PATCH 0931/1276] ANDROID: netfilter: xt_qtaguid: fix handling for cases where tunnels are used. * fix skb->dev vs par->in/out When there is some forwarding going on, it introduces extra state around devs associated with xt_action_param->in/out and sk_buff->dev. E.g. par->in and par->out are both set, or skb->dev and par->out are both set (and different) This would lead qtaguid to make the wrong assumption about the direction and update the wrong device stats. Now we rely more on par->in/out. * Fix handling when qtaguid is used as "owner" When qtaguid is used as an owner module, and sk_socket->file is not there (happens when tunnels are involved), it would incorrectly do a tag stats update. * Correct debug messages. Bug: 11687690 Change-Id: I2b1ff8bd7131969ce9e25f8291d83a6280b3ba7f CRs-Fixed: 747810 Signed-off-by: JP Abgrall Git-commit: 2b71479d6f5fe8f33b335f713380f72037244395 Git-repo: https://www.codeaurora.org/cgit/quic/la/kernel/mediatek [imaund@codeaurora.org: Resolved trivial context conflicts.] Signed-off-by: Ian Maund [bflowers@codeaurora.org: Resolved merge conflicts] Signed-off-by: Bryse Flowers Signed-off-by: Chenbo Feng --- net/netfilter/xt_qtaguid.c | 144 ++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 75 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 00a0ebbbb609..5f23d35ebb96 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1174,6 +1174,38 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* Guarantied to return a net_device that has a name */ +static void get_dev_and_dir(const struct sk_buff *skb, + struct xt_action_param *par, + enum ifs_tx_rx *direction, + const struct net_device **el_dev) +{ + BUG_ON(!direction || !el_dev); + + if (par->in) { + *el_dev = par->in; + *direction = IFS_RX; + } else if (par->out) { + *el_dev = par->out; + *direction = IFS_TX; + } else { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } + if (unlikely(!(*el_dev)->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } + if (skb->dev && *el_dev != skb->dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", + par->hooknum, skb->dev, skb->dev->name, + *direction == IFS_RX ? "in" : "out", *el_dev, + (*el_dev)->name); + } +} + /* * Update stats for the specified interface from the skb. * Do nothing if the entry @@ -1185,46 +1217,27 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, { struct iface_stat *entry; const struct net_device *el_dev; - enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + enum ifs_tx_rx direction; int bytes = skb->len; int proto; - if (!skb->dev) { - MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); - el_dev = par->in ? : par->out; - } else { - const struct net_device *other_dev; - el_dev = skb->dev; - other_dev = par->in ? : par->out; - if (el_dev != other_dev) { - MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " - "par->(in/out)=%p %s\n", - par->hooknum, el_dev, el_dev->name, other_dev, - other_dev->name); - } - } - - if (unlikely(!el_dev)) { - pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); - BUG(); - } else { - proto = ipx_proto(skb, par); - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto); - } + get_dev_and_dir(skb, par, &direction, &el_dev); + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): " + "type=%d fam=%d proto=%d dir=%d\n", + par->hooknum, __func__, el_dev->name, el_dev->type, + par->family, proto, direction); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(el_dev->name); if (entry == NULL) { - IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", - __func__, el_dev->name); + IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n", + par->hooknum, __func__, el_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__, el_dev->name, entry); data_counters_update(&entry->totals_via_skb, 0, direction, proto, @@ -1289,14 +1302,14 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, spin_lock_bh(&iface_stat_list_lock); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + pr_err_ratelimited("qtaguid: tag_stat: stat_update() " "%s not found\n", ifname); spin_unlock_bh(&iface_stat_list_lock); return; } /* It is ok to process data when an iface_entry is inactive */ - MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%p\n", ifname, iface_entry); /* @@ -1313,7 +1326,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag = combine_atag_with_uid(acct_tag, uid); uid_tag = make_tag_from_uid(uid); } - MT_DEBUG("qtaguid: iface_stat: stat_update(): " + MT_DEBUG("qtaguid: tag_stat: stat_update(): " " looking for tag=0x%llx (uid=%u) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ @@ -1573,8 +1586,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct sock *sk; unsigned int hook_mask = (1 << par->hooknum); - MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, - par->hooknum, par->family); + MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n", + par->hooknum, skb, par->family); /* * Let's not abuse the the xt_socket_get*_sk(), or else it will @@ -1595,8 +1608,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, } if (sk) { - MT_DEBUG("qtaguid: %p->sk_proto=%u " - "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n", + par->hooknum, sk, sk->sk_protocol, sk->sk_state); } return sk; } @@ -1606,35 +1619,19 @@ static void account_for_uid(const struct sk_buff *skb, struct xt_action_param *par) { const struct net_device *el_dev; + enum ifs_tx_rx direction; + int proto; - if (!skb->dev) { - MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); - el_dev = par->in ? : par->out; - } else { - const struct net_device *other_dev; - el_dev = skb->dev; - other_dev = par->in ? : par->out; - if (el_dev != other_dev) { - MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " - "par->(in/out)=%p %s\n", - par->hooknum, el_dev, el_dev->name, other_dev, - other_dev->name); - } - } - - if (unlikely(!el_dev)) { - pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); - } else { - int proto = ipx_proto(skb, par); - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto); + get_dev_and_dir(skb, par, &direction, &el_dev); + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto, direction); - if_tag_stat_update(el_dev->name, uid, - skb->sk ? skb->sk : alternate_sk, - par->in ? IFS_RX : IFS_TX, - proto, skb->len); - } + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + direction, + proto, skb->len); } static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) @@ -1646,6 +1643,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kuid_t sock_uid; bool res; bool set_sk_callback_lock = false; + /* + * TODO: unhack how to force just accounting. + * For now we only do tag stats when the uid-owner is not requested + */ + bool do_tag_stat = !(info->match & XT_QTAGUID_UID); if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1734,12 +1736,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * couldn't find the owner, so for now we just count them * against the system. */ - /* - * TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not - * requested. - */ - if (!(info->match & XT_QTAGUID_UID)) + if (do_tag_stat) account_for_uid(skb, sk, 0, par); MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", par->hooknum, @@ -1754,18 +1751,15 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) filp = sk->sk_socket->file; if (filp == NULL) { MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); - account_for_uid(skb, sk, 0, par); + if (do_tag_stat) + account_for_uid(skb, sk, 0, par); res = ((info->match ^ info->invert) & (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; atomic64_inc(&qtu_events.match_no_sk_file); goto put_sock_ret_res; } sock_uid = filp->f_cred->fsuid; - /* - * TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not requested - */ - if (!(info->match & XT_QTAGUID_UID)) + if (do_tag_stat) account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); /* From a951e2e0846744950bd3c9065972fe7951927cf2 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 20 Apr 2017 18:54:13 -0700 Subject: [PATCH 0932/1276] ANDROID: netfilter: xt_qtaguid: Use sk_uid to replace uid get from socket file Retrieve socket uid from the sk_uid field added to struct sk instead of read it from sk->socket->file. It prevent the packet been dropped when the socket file doesn't exist. Bug: 37524657 Signed-off-by: Chenbo Feng Change-Id: Ic58239c1f9aa7e0eb1d4d1c09d40b845fd4e8e57 --- net/netfilter/xt_qtaguid.c | 55 +++++++++++++---------------- net/netfilter/xt_qtaguid_internal.h | 4 +-- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 5f23d35ebb96..661a496f2c04 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1719,18 +1719,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); - if (sk != NULL) { - set_sk_callback_lock = true; - read_lock_bh(&sk->sk_callback_lock); - MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", - par->hooknum, sk, sk->sk_socket, - sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); - filp = sk->sk_socket ? sk->sk_socket->file : NULL; - MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); - } - if (sk == NULL || sk->sk_socket == NULL) { + if (!sk) { /* * Here, the qtaguid_find_sk() using connection tracking * couldn't find the owner, so for now we just count them @@ -1738,9 +1728,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ if (do_tag_stat) account_for_uid(skb, sk, 0, par); - MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", - par->hooknum, - sk ? sk->sk_socket : NULL); + MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum); res = (info->match ^ info->invert) == 0; atomic64_inc(&qtu_events.match_no_sk); goto put_sock_ret_res; @@ -1748,19 +1736,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) res = false; goto put_sock_ret_res; } - filp = sk->sk_socket->file; - if (filp == NULL) { - MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); - if (do_tag_stat) - account_for_uid(skb, sk, 0, par); - res = ((info->match ^ info->invert) & - (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; - atomic64_inc(&qtu_events.match_no_sk_file); - goto put_sock_ret_res; - } - sock_uid = filp->f_cred->fsuid; + sock_uid = sk->sk_uid; if (do_tag_stat) - account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); + account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), + par); /* * The following two tests fail the match when: @@ -1772,8 +1751,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); - if ((uid_gte(filp->f_cred->fsuid, uid_min) && - uid_lte(filp->f_cred->fsuid, uid_max)) ^ + if ((uid_gte(sock_uid, uid_min) && + uid_lte(sock_uid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); @@ -1784,7 +1763,21 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->match & XT_QTAGUID_GID) { kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); - + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + if (!filp) { + res = ((info->match ^ info->invert) & + XT_QTAGUID_GID) == 0; + atomic64_inc(&qtu_events.match_no_sk_gid); + goto put_sock_ret_res; + } + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? + from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); if ((gid_gte(filp->f_cred->fsgid, gid_min) && gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { @@ -1956,7 +1949,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " "match_no_sk=%llu " - "match_no_sk_file=%llu\n", + "match_no_sk_gid=%llu\n", (u64)atomic64_read(&qtu_events.sockets_tagged), (u64)atomic64_read(&qtu_events.sockets_untagged), (u64)atomic64_read(&qtu_events.counter_set_changes), @@ -1968,7 +1961,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), (u64)atomic64_read(&qtu_events.match_no_sk), - (u64)atomic64_read(&qtu_events.match_no_sk_file)); + (u64)atomic64_read(&qtu_events.match_no_sk_gid)); /* Count the following as part of the last item_index. No need * to lock the sock_tag_list here since it is already locked when diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 8178fbdfb036..c7052707a6a4 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -289,10 +289,10 @@ struct qtaguid_event_counts { */ atomic64_t match_no_sk; /* - * The file ptr in the sk_socket wasn't there. + * The file ptr in the sk_socket wasn't there and we couldn't get GID. * This might happen for traffic while the socket is being closed. */ - atomic64_t match_no_sk_file; + atomic64_t match_no_sk_gid; }; /* Track the set active_set for the given tag. */ From f1b6297147fbc23098d611859fd1519ee70dcbb0 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 8 Jan 2018 15:21:57 -0800 Subject: [PATCH 0933/1276] ANDROID: netfilter: xt_qtaguid: Fix 4.14 compilation struct xt_action_param was changed: in, out, family and hooknum were moved to struct nf_hook_state *state in, out, pf and hook Replace atomic_read() with refcount_read() Change-Id: If463bf84db08fe382baa825ca7818cab2150b60d Signed-off-by: Dmitry Shmidt --- net/netfilter/xt_qtaguid.c | 80 +++++++++++++++++--------------- net/netfilter/xt_qtaguid_print.c | 3 +- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 661a496f2c04..c6a51d91f26f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) #include @@ -1085,7 +1086,7 @@ static int ipx_proto(const struct sk_buff *skb, { int thoff = 0, tproto; - switch (par->family) { + switch (par->state->pf) { case NFPROTO_IPV6: tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); if (tproto < 0) @@ -1180,27 +1181,29 @@ static void get_dev_and_dir(const struct sk_buff *skb, enum ifs_tx_rx *direction, const struct net_device **el_dev) { + const struct nf_hook_state *parst = par->state; + BUG_ON(!direction || !el_dev); - if (par->in) { - *el_dev = par->in; + if (parst->in) { + *el_dev = parst->in; *direction = IFS_RX; - } else if (par->out) { - *el_dev = par->out; + } else if (parst->out) { + *el_dev = parst->out; *direction = IFS_TX; } else { - pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); + pr_err("qtaguid[%d]: %s(): no par->state->in/out?!!\n", + parst->hook, __func__); BUG(); } if (unlikely(!(*el_dev)->name)) { pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); + parst->hook, __func__); BUG(); } if (skb->dev && *el_dev != skb->dev) { MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", - par->hooknum, skb->dev, skb->dev->name, + parst->hook, skb->dev, skb->dev->name, *direction == IFS_RX ? "in" : "out", *el_dev, (*el_dev)->name); } @@ -1215,6 +1218,7 @@ static void get_dev_and_dir(const struct sk_buff *skb, static void iface_stat_update_from_skb(const struct sk_buff *skb, struct xt_action_param *par) { + const struct nf_hook_state *parst = par->state; struct iface_stat *entry; const struct net_device *el_dev; enum ifs_tx_rx direction; @@ -1225,19 +1229,19 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): " "type=%d fam=%d proto=%d dir=%d\n", - par->hooknum, __func__, el_dev->name, el_dev->type, - par->family, proto, direction); + parst->hook, __func__, el_dev->name, el_dev->type, + parst->pf, proto, direction); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(el_dev->name); if (entry == NULL) { IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n", - par->hooknum, __func__, el_dev->name); + parst->hook, __func__, el_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__, + IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", parst->hook, __func__, el_dev->name, entry); data_counters_update(&entry->totals_via_skb, 0, direction, proto, @@ -1583,11 +1587,12 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct xt_action_param *par) { + const struct nf_hook_state *parst = par->state; struct sock *sk; - unsigned int hook_mask = (1 << par->hooknum); + unsigned int hook_mask = (1 << parst->hook); MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n", - par->hooknum, skb, par->family); + parst->hook, skb, parst->pf); /* * Let's not abuse the the xt_socket_get*_sk(), or else it will @@ -1596,12 +1601,12 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) return NULL; - switch (par->family) { + switch (parst->pf) { case NFPROTO_IPV6: - sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, par->in); + sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, parst->in); break; case NFPROTO_IPV4: - sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, par->in); + sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, parst->in); break; default: return NULL; @@ -1609,7 +1614,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (sk) { MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n", - par->hooknum, sk, sk->sk_protocol, sk->sk_state); + parst->hook, sk, sk->sk_protocol, sk->sk_state); } return sk; } @@ -1625,8 +1630,8 @@ static void account_for_uid(const struct sk_buff *skb, get_dev_and_dir(skb, par, &direction, &el_dev); proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto, direction); + par->state->hook, el_dev->name, el_dev->type, + par->state->pf, proto, direction); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, @@ -1637,6 +1642,7 @@ static void account_for_uid(const struct sk_buff *skb, static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct nf_hook_state *parst = par->state; const struct file *filp; bool got_sock = false; struct sock *sk; @@ -1653,7 +1659,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) return (info->match ^ info->invert) == 0; MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", - par->hooknum, skb, par->in, par->out, par->family); + parst->hook, skb, parst->in, parst->out, parst->pf); atomic64_inc(&qtu_events.match_calls); if (skb == NULL) { @@ -1661,7 +1667,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - switch (par->hooknum) { + switch (parst->hook) { case NF_INET_PRE_ROUTING: case NF_INET_POST_ROUTING: atomic64_inc(&qtu_events.match_calls_prepost); @@ -1718,7 +1724,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) atomic64_inc(&qtu_events.match_found_sk); } MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", - par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); + parst->hook, sk, got_sock, parst->pf, ipx_proto(skb, par)); if (!sk) { /* @@ -1728,7 +1734,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ if (do_tag_stat) account_for_uid(skb, sk, 0, par); - MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", parst->hook); res = (info->match ^ info->invert) == 0; atomic64_inc(&qtu_events.match_no_sk); goto put_sock_ret_res; @@ -1755,7 +1761,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_lte(sock_uid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", - par->hooknum); + parst->hook); res = false; goto put_sock_ret_res; } @@ -1766,7 +1772,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) set_sk_callback_lock = true; read_lock_bh(&sk->sk_callback_lock); MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", - par->hooknum, sk, sk->sk_socket, + parst->hook, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; if (!filp) { @@ -1776,18 +1782,18 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto put_sock_ret_res; } MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? + parst->hook, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); if ((gid_gte(filp->f_cred->fsgid, gid_min) && gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", - par->hooknum); + parst->hook); res = false; goto put_sock_ret_res; } } - MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving matched\n", parst->hook); res = true; put_sock_ret_res: @@ -1796,7 +1802,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if (set_sk_callback_lock) read_unlock_bh(&sk->sk_callback_lock); ret_res: - MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + MT_DEBUG("qtaguid[%d]: left %d\n", parst->hook, res); return res; } @@ -1930,7 +1936,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) uid, sock_tag_entry->pid ); - sk_ref_count = atomic_read( + sk_ref_count = refcount_read( &sock_tag_entry->sk->sk_refcnt); seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " "f_count=%d\n", @@ -2233,7 +2239,7 @@ static int ctrl_cmd_tag(const char *input) goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n", - input, atomic_read(&el_socket->sk->sk_refcnt), + input, refcount_read(&el_socket->sk->sk_refcnt), el_socket->sk); if (argc < 3) { acct_tag = make_atag_from_value(0); @@ -2279,7 +2285,7 @@ static int ctrl_cmd_tag(const char *input) CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " "st@%p ...->sk_refcnt=%d\n", input, el_socket->sk, sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &uid_tag_data_entry); BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); @@ -2334,7 +2340,7 @@ static int ctrl_cmd_tag(const char *input) /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", input, sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); sockfd_put(el_socket); return 0; @@ -2344,7 +2350,7 @@ static int ctrl_cmd_tag(const char *input) free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", - input, atomic_read(&el_socket->sk->sk_refcnt) - 1); + input, refcount_read(&el_socket->sk->sk_refcnt) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); return res; @@ -2443,7 +2449,7 @@ int qtaguid_untag(struct socket *el_socket, bool kernel) sock_put(sock_tag_entry->sk); CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n", sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); kfree(sock_tag_entry); atomic64_inc(&qtu_events.sockets_untagged); diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 2a7190d285e6..cab478eba9c8 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -239,8 +239,7 @@ char *pp_sock_tag(struct sock_tag *st) "sock_node=rb_node{...}, " "sk=%p (f_count=%d), list=list_head{...}, " "pid=%u, tag=%s}", - st, st->sk, atomic_read( - &st->sk->sk_refcnt), + st, st->sk, refcount_read(&st->sk->sk_refcnt), st->pid, tag_str); _bug_on_err_or_null(res); kfree(tag_str); From 75156b55fde580ce9870e51866f091d1e20e1e22 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Tue, 28 Nov 2017 18:22:11 -0800 Subject: [PATCH 0934/1276] ANDROID: qtaguid: Fix the UAF probelm with tag_ref_tree When multiple threads is trying to tag/delete the same socket at the same time, there is a chance the tag_ref_entry of the target socket to be null before the uid_tag_data entry is freed. It is caused by the ctrl_cmd_tag function where it doesn't correctly grab the spinlocks when tagging a socket. Signed-off-by: Chenbo Feng Bug: 65853158 Change-Id: I5d89885918054cf835370a52bff2d693362ac5f0 --- net/netfilter/xt_qtaguid.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index c6a51d91f26f..cd7c34b45221 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -520,13 +520,11 @@ static struct tag_ref *get_tag_ref(tag_t full_tag, DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", full_tag); - spin_lock_bh(&uid_tag_data_tree_lock); tr_entry = lookup_tag_ref(full_tag, &utd_entry); BUG_ON(IS_ERR_OR_NULL(utd_entry)); if (!tr_entry) tr_entry = new_tag_ref(full_tag, utd_entry); - spin_unlock_bh(&uid_tag_data_tree_lock); if (utd_res) *utd_res = utd_entry; DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", @@ -2033,6 +2031,7 @@ static int ctrl_cmd_delete(const char *input) /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, sock_node); @@ -2062,6 +2061,7 @@ static int ctrl_cmd_delete(const char *input) list_del(&st_entry->list); } } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); sock_tag_tree_erase(&st_to_free_tree); @@ -2271,10 +2271,12 @@ static int ctrl_cmd_tag(const char *input) full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); if (IS_ERR(tag_ref_entry)) { res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); goto err_put; } @@ -2301,9 +2303,14 @@ static int ctrl_cmd_tag(const char *input) pr_err("qtaguid: ctrl_tag(%s): " "socket tag alloc failed\n", input); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, + uid_tag_data_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err_tag_unref_put; + goto err_put; } /* * Hold the sk refcount here to make sure the sk pointer cannot @@ -2313,7 +2320,6 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); - spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); /* @@ -2331,11 +2337,11 @@ static int ctrl_cmd_tag(const char *input) else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); - spin_unlock_bh(&uid_tag_data_tree_lock); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); atomic64_inc(&qtu_events.sockets_tagged); } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", @@ -2344,10 +2350,6 @@ static int ctrl_cmd_tag(const char *input) sockfd_put(el_socket); return 0; -err_tag_unref_put: - BUG_ON(tag_ref_entry->num_sock_tags <= 0); - tag_ref_entry->num_sock_tags--; - free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", input, refcount_read(&el_socket->sk->sk_refcnt) - 1); From 85ca9a399d0e97071368a14d26c35956a7c1a874 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 31 Mar 2018 20:56:23 -0700 Subject: [PATCH 0935/1276] ANDROID: xt_qtaguid: Remove unnecessary null checks to device's name 'name' will never be NULL since it isn't a plain pointer but an array of char values. ../net/netfilter/xt_qtaguid.c:1195:27: warning: address of array '(*el_dev)->name' will always evaluate to 'true' [-Wpointer-bool-conversion] if (unlikely(!(*el_dev)->name)) { ~~~~~~~~~~~~^~~~ Change-Id: If3b25f17829b43e8a639193fb9cd04ae45947200 Signed-off-by: Nathan Chancellor (cherry picked from android-4.4 commit 207b579e3db6fd0cb6fe40ba3e929635ad748d89) Signed-off-by: Chenbo Feng --- net/netfilter/xt_qtaguid.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index cd7c34b45221..d261932ee595 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1194,11 +1194,6 @@ static void get_dev_and_dir(const struct sk_buff *skb, parst->hook, __func__); BUG(); } - if (unlikely(!(*el_dev)->name)) { - pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - parst->hook, __func__); - BUG(); - } if (skb->dev && *el_dev != skb->dev) { MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", parst->hook, skb->dev, skb->dev->name, From cd4dc4dc018043181df82e663ffadf1fd30057d2 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:30:56 +0300 Subject: [PATCH 0936/1276] dvctrace-bus: DvC-Trace bus driver DvC Trace is part of USB Debug class which provides a way of sending trace data via USB. This adds a pseudobus for devices/drivers capable of sending traces over such interface. Signed-off-by: Traian Schiau Signed-off-by: Tian, Baofeng --- Documentation/ABI/testing/sysfs-bus-dvctrace | 26 ++ MAINTAINERS | 7 + drivers/bus/Kconfig | 24 ++ drivers/bus/Makefile | 2 + drivers/bus/dvctrace.c | 278 +++++++++++++++++++ include/linux/dvctrace.h | 134 +++++++++ 6 files changed, 471 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-dvctrace create mode 100644 drivers/bus/dvctrace.c create mode 100644 include/linux/dvctrace.h diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace b/Documentation/ABI/testing/sysfs-bus-dvctrace new file mode 100644 index 000000000000..0abdd3293364 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace @@ -0,0 +1,26 @@ +What: /sys/bus/dvctrace +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: Groups the devices and drivers registered to + to dvc-trace bus. + +What: /sys/bus/dvctrace/devices//status +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) The status of a dvc-trace source device with + respect to an USB function driver. + Free - The device is free + Reserved - The device is reserved by an USB + function but not in use. + In use - The device is used by an USB function. + +What: /sys/bus/dvctrace/devices//protocol +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) The protocol id of a dvc-trace source device, + this will used in function driver interface + descriptors (u8). According to USB debug class + specification the protocol id is vendor specific. diff --git a/MAINTAINERS b/MAINTAINERS index b2f710eee67a..f957a0cf899b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5143,6 +5143,13 @@ S: Maintained F: drivers/media/usb/dvb-usb-v2/dvb_usb* F: drivers/media/usb/dvb-usb-v2/usb_urb.c +DVC_TRACE BUS DRIVER +M: Traian Schiau +S: Maintained +F: drivers/bus/dvctrace.c +F: include/linux/dvctrace.h +F: Documentation/ABI/testing/sysfs-bus-dvctrace + DYNAMIC DEBUG M: Jason Baron S: Maintained diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 1851112ccc29..16f0e0f0d59c 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -183,4 +183,28 @@ config DA8XX_MSTPRI source "drivers/bus/fsl-mc/Kconfig" +config DVC_TRACE_BUS + bool "DvC-Trace pseudobus" + default n + depends on USB_GADGET + help + DvC-Trace pseudobus is meant to group devices capable of sending + trace data via a USB DvC-Trace gadget function. + An usb function driver will be able to choose a source device and + provide the means to transfer the data. + + Say Y to enable it. + +config DVC_TRACE_BUS_DEBUG + bool "DvC-Trace pseudobus debug" + default n + depends on DVC_TRACE_BUS + help + DvC-Trace pseudobus is meant to group devices capable of sending + trace data via a USB DvC-Trace gadget function. + An usb function driver will be able to choose a source device and + provide the means to transfer the data. + + Say Y to enable extended debug messages in this driver. + endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ca300b1914ce..cf118be0785f 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -32,3 +32,5 @@ obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o +obj-$(CONFIG_DVC_TRACE_BUS) += dvctrace.o +subdir-ccflags-$(CONFIG_DVC_TRACE_BUS_DEBUG) += -DDVCT_DEBUG diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c new file mode 100644 index 000000000000..c1770ca44422 --- /dev/null +++ b/drivers/bus/dvctrace.c @@ -0,0 +1,278 @@ +/* + * DvC-Trace(dvct) Bus driver + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include + +#ifdef DVCT_DEBUG +#define DVCT_IN() pr_debug("in\n") +#else +#define DVCT_IN() do {} while (0) +#endif + +static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + DVCT_IN(); + return sprintf(buf, "%d\n", dev_to_dvct_source_device(dev)->protocol); +} + +static ssize_t protocol_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (ds_dev->instance_taken) + return -EBUSY; + + if (!kstrtou8(buf, 10, &ds_dev->protocol)) + return size; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(protocol); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (ds_dev->instance_taken) { + if (ds_dev->function_taken) + return sprintf(buf, "In use\n"); + else + return sprintf(buf, "Reserved\n"); + } else { + return sprintf(buf, "Free\n"); + } +} + +static DEVICE_ATTR_RO(status); + +static struct attribute *dvct_source_attrs[] = { + &dev_attr_protocol.attr, + &dev_attr_status.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(dvct_source); + + +static int dvct_match(struct device *dev, struct device_driver *drv) +{ + const char *devname = dev_name(dev); + + DVCT_IN(); + if (strlen(devname) <= strlen(drv->name)) + return -1; + if (strncmp(devname, drv->name, strlen(drv->name))) + return -1; + return devname[strlen(drv->name)] == '-'; +}; + +static struct bus_type dvctrace_bus_type = { + .name = "dvctrace", + .match = dvct_match, + .dev_groups = dvct_source_groups, +}; + +static struct device dvctrace_bus = { + .init_name = "dvctrace-bus", +}; + +static int dvct_match_free(struct device *dev, void *data) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + return !ds_dev->instance_taken; +} + +struct dvct_source_device *dvct_source_find_by_name(const char *name) +{ + struct device *dev; + + DVCT_IN(); + dev = bus_find_device_by_name(&dvctrace_bus_type, NULL, name); + if (IS_ERR_OR_NULL(dev)) + return ERR_PTR(-ENODEV); + return dev_to_dvct_source_device(dev); +} +EXPORT_SYMBOL_GPL(dvct_source_find_by_name); + +struct dvct_source_device *dvct_source_find_free_by_name(const char *name) +{ + struct dvct_source_device *ds_dev = dvct_source_find_by_name(name); + + DVCT_IN(); + if (IS_ERR_OR_NULL(ds_dev)) + return ERR_PTR(-ENODEV); + + if (ds_dev->instance_taken) + return ERR_PTR(-EBUSY); + + return ds_dev; +} +EXPORT_SYMBOL_GPL(dvct_source_find_free_by_name); + +struct dvct_source_device *dvct_source_find_free(void) +{ + struct device *dev = bus_find_device(&dvctrace_bus_type, NULL, + NULL, dvct_match_free); + DVCT_IN(); + if (IS_ERR_OR_NULL(dev)) + return ERR_PTR(-ENODEV); + + return dev_to_dvct_source_device(dev); +} +EXPORT_SYMBOL_GPL(dvct_source_find_free); + +static int fn_count_free(struct device *dev, void *data) +{ + int *count = data; + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (!ds_dev->instance_taken) + (*count)++; + return 0; +} + +int dvct_source_count_free(void) +{ + int count = 0; + + DVCT_IN(); + bus_for_each_dev(&dvctrace_bus_type, NULL, &count, fn_count_free); + return count; +} +EXPORT_SYMBOL_GPL(dvct_source_count_free); + +struct dvct_source_driver +*dvct_source_get_drv(struct dvct_source_device *ds_dev) +{ + BUG_ON(ds_dev->device.driver == NULL); + return drv_to_dvct_source_driver(ds_dev->device.driver); +} +EXPORT_SYMBOL_GPL(dvct_source_get_drv); + +int dvct_source_device_add(struct dvct_source_device *ds_dev, + struct dvct_source_driver *ds_drv) +{ + int ret; + + DVCT_IN(); + if (!ds_dev) + return -ENODEV; + if (!ds_drv) + return -EINVAL; + + spin_lock_init(&ds_dev->lock); + spin_lock(&ds_dev->lock); + ds_dev->instance_taken = 0; + ds_dev->function_taken = 0; + spin_unlock(&ds_dev->lock); + + device_initialize(&ds_dev->device); + ds_dev->device.bus = &dvctrace_bus_type; + + if (!ds_dev->device.parent) + ds_dev->device.parent = &dvctrace_bus; + + dev_set_name(&ds_dev->device, "%s-%s", ds_drv->driver.name, + ds_dev->name_add); + + ret = device_add(&ds_dev->device); + if (ret) { + dev_err(&dvctrace_bus, "Cannot add device %s %d\n", + ds_dev->name_add, ret); + return ret; + } + + dev_notice(&dvctrace_bus, "Adding device %s\n", ds_dev->name_add); + return 0; +}; +EXPORT_SYMBOL_GPL(dvct_source_device_add); + +void dvct_source_device_del(struct dvct_source_device *ds_dev) +{ + DVCT_IN(); + device_del(&ds_dev->device); +}; +EXPORT_SYMBOL_GPL(dvct_source_device_del); + +int __dvct_source_driver_register(struct dvct_source_driver *ds_drv, + struct module *owner) +{ + DVCT_IN(); + if (!ds_drv->activate || + !ds_drv->binded || + !ds_drv->start_transfer || + !ds_drv->stop_transfer || + !ds_drv->unbinded || + !ds_drv->deactivate) + return -EINVAL; + + ds_drv->driver.owner = owner; + ds_drv->driver.bus = &dvctrace_bus_type; + return driver_register(&ds_drv->driver); +} +EXPORT_SYMBOL_GPL(__dvct_source_driver_register); + +void dvct_source_driver_unregister(struct dvct_source_driver *ds_drv) +{ + DVCT_IN(); + driver_unregister(&ds_drv->driver); +} +EXPORT_SYMBOL_GPL(dvct_source_driver_unregister); + +static int __init dtb_init(void) +{ + int ret; + + DVCT_IN(); + ret = device_register(&dvctrace_bus); + if (ret) { + pr_err("Cannot register bus device %d\n", ret); + return ret; + } + + ret = bus_register(&dvctrace_bus_type); + if (ret) { + pr_err("Cannot register bus %d\n", ret); + return ret; + } + return 0; +} + +static void __exit dtb_exit(void) +{ + DVCT_IN(); + bus_unregister(&dvctrace_bus_type); +} + +subsys_initcall(dtb_init); +module_exit(dtb_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DvC-Trace bus implementation"); +MODULE_AUTHOR("Traian Schiau "); diff --git a/include/linux/dvctrace.h b/include/linux/dvctrace.h new file mode 100644 index 000000000000..18291efb18b6 --- /dev/null +++ b/include/linux/dvctrace.h @@ -0,0 +1,134 @@ +/* + * DvC.Trace(dvct) Bus + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DVCTRACE_H +#define __DVCTRACE_H + +#include + +/** + * The function is binded to a gadget + * function-driver RW + * client-driver RO + */ +#define DVCT_MASK_ONLINE BIT(0) + +/** + * The client is currently transferring + * function-driver RO + * client-driver RW + */ +#define DVCT_MASK_TRANS BIT(1) + +/** + * An error was detected the client + * function-driver RO + * client-driver RW + */ +#define DVCT_MASK_ERR BIT(2) + +#define DVCT_MASK_ONLINE_TRANS (DVCT_MASK_ONLINE | DVCT_MASK_TRANS) +#define DVCT_MASK_ALL (DVCT_MASK_ONLINE | DVCT_MASK_TRANS | DVCT_MASK_ERR) + +/** + * Bitwise status manipulation macros + */ +#define dvct_set_status(s, m) atomic_set(s, atomic_read(s)|(m)) +#define dvct_clr_status(s, m) atomic_set(s, atomic_read(s) & (~(m))) +#define dvct_get_status(s, m) (atomic_read(s) & (m)) + +/** + * dvct_source_device - DvC Trace function to source (backend) interface + * + * @name_add: postfix used to compute the source name (-) + * @protocol: Interface protocol code (used in IAD, control and data + * + * @instance_taken, @function_taken, @lock, @device are reserved for + * internal use only. + */ +struct dvct_source_device { + const char *name_add; + u8 protocol; + u32 instance_taken:1; + u32 function_taken:1; + spinlock_t lock; + struct device device; +}; + +/** + * dvct_source_driver - DvC Trace source (backend) representation + * @activate: pre-bind callback called when the function instance linked to the + * source device is selected as a part of a configuration eg. + * ln -s cfgfs/.../functions/dvctrace.x cfgfs/.../configs/c.x/ + * atomic_t * , points to the status field of the function, the client + * should update relevant bits (DVCT_MASK_TRANS, DVCT_MASK_ERR). + * @binded: post-bind callback, at this stage the transfer endpoint is + * allocated; + * @connected: (Optional) The gadget is now connected to a host, the connection + * speed is available. + * @start_transfer: called upon receiving SET_TRACE, the host side should be + * able ready to receive data, the client could submit usb requests. + * @stop_transfer: host side request to stop the data transfer. + * @disconnected: (Optional) The gadget was disconnected from the host. + * Before calling this the function driver will call stop_trasfer if + * DVCT_MASK_TRANS is set. + * @unbinded: The USB-function is no longer used. + * @deactivate: The USB-function is no longer part of a configuration. + * + * If any of the non optional callbacks is not provided (NULL) driver + * registration will fail. + */ +struct dvct_source_driver { + struct device_driver driver; + int (*activate)(struct dvct_source_device *, atomic_t *); + int (*binded)(struct dvct_source_device *, struct usb_ep *, + struct usb_function *); + void (*connected)(struct dvct_source_device *, enum usb_device_speed); + int (*start_transfer)(struct dvct_source_device *, u8); + int (*stop_transfer)(struct dvct_source_device *); + void (*disconnected)(struct dvct_source_device *); + void (*unbinded)(struct dvct_source_device *); + void (*deactivate)(struct dvct_source_device *); +}; + +#define dev_to_dvct_source_device(ptr) \ + container_of(ptr, struct dvct_source_device, device) + +#define drv_to_dvct_source_driver(ptr) \ + container_of(ptr, struct dvct_source_driver, driver) + +extern struct dvct_source_driver +*dvct_source_get_drv(struct dvct_source_device *dev); + +#define dvct_source_driver_register(drv) \ + __dvct_source_driver_register(drv, THIS_MODULE) +extern int __dvct_source_driver_register(struct dvct_source_driver *, + struct module *); +extern void dvct_source_driver_unregister(struct dvct_source_driver *); +extern int dvct_source_device_add(struct dvct_source_device *, + struct dvct_source_driver *); +extern void dvct_source_device_del(struct dvct_source_device *); + +extern struct dvct_source_device *dvct_source_find_by_name(const char *name); + +extern struct dvct_source_device +*dvct_source_find_free_by_name(const char *name); + +extern struct dvct_source_device *dvct_source_find_free(void); + +extern int dvct_source_count_free(void); + +#endif /* __DVCTRACE_H*/ From 3fb6d91471c4ffc9822936552de8a26f331aedef Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 12:00:49 +0300 Subject: [PATCH 0937/1276] dvctrace-function: Implement DvC Trace USB Gadget function driver Implement DvC Trace function class driver. Instances of this function are able to connect to a dvctrace bus source driver. Signed-off-by: Traian Schiau Signed-off-by: Tian, Baofeng --- .../ABI/testing/configfs-usb-gadget-dvctrace | 9 + MAINTAINERS | 8 + drivers/usb/gadget/Kconfig | 11 + drivers/usb/gadget/function/Makefile | 9 + drivers/usb/gadget/function/f_dvctrace.c | 754 ++++++++++++++++++ drivers/usb/gadget/function/u_dvctrace.h | 72 ++ include/linux/usb/debug.h | 189 +++++ include/uapi/linux/usb/ch9.h | 1 + 8 files changed, 1053 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-dvctrace create mode 100644 drivers/usb/gadget/function/f_dvctrace.c create mode 100644 drivers/usb/gadget/function/u_dvctrace.h create mode 100644 include/linux/usb/debug.h diff --git a/Documentation/ABI/testing/configfs-usb-gadget-dvctrace b/Documentation/ABI/testing/configfs-usb-gadget-dvctrace new file mode 100644 index 000000000000..6391096ac151 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-dvctrace @@ -0,0 +1,9 @@ +What: /config/usb-gadget//functions/dvctrace./source_dev +Date: Mar 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) The name of the source device paired with this function + instance, if upon creation of the instance a source device + named exists and is free, the source device will be + associated with the current instance, otherwise the first free + source device will be used. diff --git a/MAINTAINERS b/MAINTAINERS index f957a0cf899b..6bc3e0be7f66 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5150,6 +5150,14 @@ F: drivers/bus/dvctrace.c F: include/linux/dvctrace.h F: Documentation/ABI/testing/sysfs-bus-dvctrace +DVC_TRACE USB_GADGET DRIVER +M: Traian Schiau +S: Maintained +F: drivers/usb/gadget/function/f_dvctrace.c +F: drivers/usb/gadget/function/u_dvctrace.h +F: include/linux/usb/debug.h +F: Documentation/ABI/testing/configfs-usb-gadget-dvctrace + DYNAMIC DEBUG M: Jason Baron S: Maintained diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..7974363f3f10 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -215,6 +215,10 @@ config USB_F_PRINTER config USB_F_TCM tristate +config USB_F_DVCTRACE + tristate + select DVC_TRACE_BUS + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -508,6 +512,13 @@ choice controller, and the relevant drivers for each function declared by the device. +config USB_CONFIGFS_F_DVCTRACE + bool "DvC Trace gadget" + depends on USB_CONFIGFS + select USB_F_DVCTRACE + help + USB gadget DvC Trace support + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..2952adb51ebd 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -50,3 +50,12 @@ usb_f_printer-y := f_printer.o obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o +obj-$(CONFIG_USB_F_DVCTRACE) += f_dvctrace.o diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c new file mode 100644 index 000000000000..dffd06126d8f --- /dev/null +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -0,0 +1,754 @@ +/* + * Gadget Driver for DvC.Trace Function + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#ifdef VERBOSE_DEBUG +#define DVCT_IN() pr_debug("in\n") +#else +#define DVCT_IN() do {} while (0) +#endif + +#include +#include +#include +#include +#include "u_dvctrace.h" + +enum { + DVCT_IAD_DESC_POS, + DVCT_CITF_DESC_POS, + DVCT_DITF_DESC_POS, + DVCT_EP_DESC_POS, + DVCT_LS_NULL_DESC_POS, /*Low speed descriptors end with this one*/ + DVCT_EP_COMP_DESC_POS = DVCT_LS_NULL_DESC_POS, + DVCT_LS_DESC_COUNT, /*Count of low speed descriptors*/ + DVCT_NULL_DESC_POS = DVCT_LS_DESC_COUNT, + DVCT_HS_DESC_COUNT,/*Count of super speed descriptors*/ +}; + +enum { + DVCT_STR_IAD_IDX, + DVCT_STR_C_ITF_IDX, + DVCT_STR_D_ITF_IDX, + DVCT_STR_NULL_IDX, /*always last */ + DVCT_STR_COUNT, +}; + +static int dvct_alloc_desc(struct dvct_function *d_fun) +{ + struct dvct_function_desc *desc = &d_fun->desc; + + DVCT_IN(); + + desc->fs = + kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->fs) + goto err_fs; + + desc->hs = + kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->hs) + goto err_hs; + + desc->ss = + kzalloc(DVCT_HS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->ss) + goto err_ss; + + /*IAD */ + desc->iad = kzalloc(sizeof(*desc->iad), GFP_KERNEL); + if (!desc->iad) + goto err_iad; + + desc->iad->bLength = sizeof(*desc->iad); + desc->iad->bDescriptorType = USB_DT_INTERFACE_ASSOCIATION; + desc->iad->bInterfaceCount = 2; + desc->iad->bFunctionClass = USB_CLASS_DEBUG; + desc->iad->bFunctionSubClass = USB_SUBCLASS_DVC_TRACE; + desc->iad->bFunctionProtocol = d_fun->source_dev->protocol; + /*bFirstInterface - updated on bind */ + + desc->fs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->hs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->ss[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + + /*Control interface */ + desc->c_itf = kzalloc(sizeof(*desc->c_itf), GFP_KERNEL); + if (!desc->c_itf) + goto err_c_itf; + + desc->c_itf->bLength = USB_DT_INTERFACE_SIZE; + desc->c_itf->bDescriptorType = USB_DT_INTERFACE; + desc->c_itf->bInterfaceClass = USB_CLASS_DEBUG; + desc->c_itf->bInterfaceSubClass = USB_SUBCLASS_DEBUG_CONTROL; + desc->c_itf->bInterfaceProtocol = d_fun->source_dev->protocol; + + desc->fs[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + desc->hs[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + desc->ss[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + + /*Data interface */ + desc->d_itf = kzalloc(sizeof(*desc->d_itf), GFP_KERNEL); + if (!desc->d_itf) + goto err_d_itf; + + desc->d_itf->bLength = USB_DT_INTERFACE_SIZE; + desc->d_itf->bDescriptorType = USB_DT_INTERFACE; + desc->d_itf->bNumEndpoints = 1; + desc->d_itf->bInterfaceClass = USB_CLASS_DEBUG; + desc->d_itf->bInterfaceSubClass = USB_SUBCLASS_DVC_TRACE; + desc->d_itf->bInterfaceProtocol = d_fun->source_dev->protocol; + + desc->fs[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + desc->hs[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + desc->ss[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + + /*Full Speed ep */ + desc->fs_ep = kzalloc(sizeof(*desc->fs_ep), GFP_KERNEL); + if (!desc->fs_ep) + goto err_fs_ep; + + desc->fs_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->fs_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->fs_ep->bEndpointAddress = USB_DIR_IN; + desc->fs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->fs_ep->wMaxPacketSize = cpu_to_le16(64); + + desc->fs[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->fs_ep; + + /*High Speed ep */ + desc->hs_ep = kzalloc(sizeof(*desc->hs_ep), GFP_KERNEL); + if (!desc->hs_ep) + goto err_hs_ep; + + desc->hs_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->hs_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->hs_ep->bEndpointAddress = USB_DIR_IN; + desc->hs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->hs_ep->wMaxPacketSize = cpu_to_le16(512); + + desc->hs[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->hs_ep; + + /*Super Speed ep */ + desc->ss_ep = kzalloc(sizeof(*desc->ss_ep), GFP_KERNEL); + if (!desc->ss_ep) + goto err_ss_ep; + + desc->ss_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->ss_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->ss_ep->bEndpointAddress = USB_DIR_IN; + desc->ss_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->ss_ep->wMaxPacketSize = cpu_to_le16(1024); + + desc->ss[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->ss_ep; + + /*Super Speed ep comp */ + desc->ss_ep_comp = kzalloc(sizeof(*desc->ss_ep_comp), GFP_KERNEL); + if (!desc->ss_ep_comp) + goto err_ss_ep_comp; + + desc->ss_ep_comp->bLength = USB_DT_SS_EP_COMP_SIZE; + desc->ss_ep_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + + desc->ss[DVCT_EP_COMP_DESC_POS] = + (struct usb_descriptor_header *)desc->ss_ep_comp; + + /* strings */ + /*the table */ + desc->str.language = 0x0409; /*en-us */ + desc->str.strings = + kzalloc(DVCT_STR_COUNT * sizeof(struct usb_string), GFP_KERNEL); + if (!desc->str.strings) + goto err_str; + + /*IAD*/ + desc->str.strings[DVCT_STR_IAD_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_IAD_IDX].s) + goto err_str_iad; + + /*control */ + desc->str.strings[DVCT_STR_C_ITF_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace Control (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_C_ITF_IDX].s) + goto err_str_ctrl; + + /*data */ + desc->str.strings[DVCT_STR_D_ITF_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace Data (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_D_ITF_IDX].s) + goto err_str_data; + + return 0; +/*cleanup*/ +err_str_data: + kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); +err_str_ctrl: + kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); +err_str_iad: + kfree(desc->str.strings); +err_str: + kfree(desc->ss_ep_comp); +err_ss_ep_comp: + kfree(desc->ss_ep); +err_ss_ep: + kfree(desc->hs_ep); +err_hs_ep: + kfree(desc->fs_ep); +err_fs_ep: + kfree(desc->d_itf); +err_d_itf: + kfree(desc->c_itf); +err_c_itf: + kfree(desc->iad); +err_iad: + kfree(desc->ss); +err_ss: + kfree(desc->hs); +err_hs: + kfree(desc->fs); +err_fs: + pr_err("Failed OFM"); + return -ENOMEM; +} + +static void dvct_free_desc(struct dvct_function *d_fun) +{ + struct dvct_function_desc *desc = &d_fun->desc; + + DVCT_IN(); + + kfree(desc->str.strings[DVCT_STR_D_ITF_IDX].s); + kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); + kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); + kfree(desc->str.strings); + kfree(desc->ss_ep_comp); + kfree(desc->ss_ep); + kfree(desc->hs_ep); + kfree(desc->fs_ep); + kfree(desc->d_itf); + kfree(desc->c_itf); + kfree(desc->iad); + kfree(desc->ss); + kfree(desc->hs); + kfree(desc->fs); +} + +ssize_t dvct_start_transfer(struct dvct_function *d_fun, u8 config) +{ + DVCT_IN(); + if (!dvct_get_status(&d_fun->status, DVCT_MASK_ONLINE)) + return -EIO; + + d_fun->trace_config = config; + return d_fun->source_drv->start_transfer(d_fun->source_dev, config); +} +EXPORT_SYMBOL(dvct_start_transfer); + +int dvct_stop_transfer(struct dvct_function *d_fun) +{ + + DVCT_IN(); + if (!dvct_get_status(&d_fun->status, DVCT_MASK_ONLINE)) + return -EIO; + + if (dvct_get_status(&d_fun->status, DVCT_MASK_TRANS)) { + d_fun->trace_config = 0; + return d_fun->source_drv->stop_transfer(d_fun->source_dev); + } + + return 0; +} +EXPORT_SYMBOL(dvct_stop_transfer); + +static int dvct_strings_setup(struct usb_composite_dev *cdev, + struct dvct_function *f_fun) +{ + int status; + + DVCT_IN(); + if (!f_fun->desc.str.strings) + return -EINVAL; + + status = usb_string_ids_tab(cdev, f_fun->desc.str.strings); + if (status < 0) + return status; + + f_fun->desc.iad->iFunction = + f_fun->desc.str.strings[DVCT_STR_IAD_IDX].id; + f_fun->desc.c_itf->iInterface = + f_fun->desc.str.strings[DVCT_STR_C_ITF_IDX].id; + f_fun->desc.d_itf->iInterface = + f_fun->desc.str.strings[DVCT_STR_D_ITF_IDX].id; + + return 0; +} + +static int dvct_setup(struct usb_function *func, + const struct usb_ctrlrequest *ctrl) +{ + int status = -EOPNOTSUPP; + u16 w_index; + u16 w_value; + u16 w_length; + u8 b_index_value; + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + + w_index = le16_to_cpu(ctrl->wIndex); + w_value = le16_to_cpu(ctrl->wValue); + w_length = le16_to_cpu(ctrl->wLength); + b_index_value = (u8) (w_index >> 8); + + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + goto done; + + switch (ctrl->bRequest) { + case DC_REQUEST_SET_RESET: + + pr_info("SET_RESET v%04x i%04x l%u\n", + w_value, w_index, w_length); + + dvct_stop_transfer(d_fun); + status = 0; + break; + + case DC_REQUEST_SET_TRACE: + /* There are some inconsistencies in the spec regarding some of the + * control requests, like SET/GET _TRACE, where even if the message + * is defined as interface specific the wIndex field is used for + * something else, making these request unusable in a "standard" + * composite device. + * To get around this we expect the interface to be specified in + * wIndex 7:0 and any other values in wIndex 15:8. + * A "special" composite implementation is free to treat these setup + * requests "on spec" and call directly dvct_start_transfer and/or + * dvct_stop_transfer (exported in u_dvctrace.h). + */ + pr_info("SET_TRACE v%04x i%04x l%u\n", + w_value, w_index, w_length); + + if (!b_index_value) { + dvct_stop_transfer(d_fun); + status = 0; + } else { + status = dvct_start_transfer(d_fun, b_index_value); + } + break; + } + +done: + if (status >= 0) { + d_fun->cdev->req->zero = 0; + d_fun->cdev->req->length = 0; + status = + usb_ep_queue(d_fun->cdev->gadget->ep0, d_fun->cdev->req, + GFP_ATOMIC); + if (status) + pr_err("Setup response queue error\n"); + } else { + pr_debug("Unexpected request %02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, w_value, w_index, + w_length); + } + + return status; +} + +static int dvct_function_bind(struct usb_configuration *cconfig, + struct usb_function *func) +{ + int id, ret; + struct usb_ep *ep; + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + d_fun->cdev = cconfig->cdev; + + ret = dvct_strings_setup(d_fun->cdev, d_fun); + if (ret) + pr_warn("Cannot allocate function string id's\n"); + + /* allocate interface ID(s) */ + id = usb_interface_id(cconfig, func); + if (id < 0) + return id; + + d_fun->desc.c_itf->bInterfaceNumber = id; + d_fun->desc.iad->bFirstInterface = id; + + pr_debug("Setting id %d for dvc-control interface\n", id); + + id = usb_interface_id(cconfig, func); + if (id < 0) + return id; + + d_fun->desc.d_itf->bInterfaceNumber = id; + + pr_debug("Setting id %d for dvc-trace-data interface\n", id); + + /* allocate endpoints */ + d_fun->desc.ss_ep->wMaxPacketSize = 0; /*get the real max */ + ep = usb_ep_autoconfig_ss(d_fun->cdev->gadget, + d_fun->desc.ss_ep, d_fun->desc.ss_ep_comp); + + if (!ep) { + pr_err("usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + + /*copy over the endpoint parameters */ + d_fun->desc.hs_ep->bEndpointAddress = + d_fun->desc.ss_ep->bEndpointAddress; + d_fun->desc.fs_ep->bEndpointAddress = + d_fun->desc.ss_ep->bEndpointAddress; + + if (le16_to_cpu(d_fun->desc.hs_ep->wMaxPacketSize) > + le16_to_cpu(d_fun->desc.ss_ep->wMaxPacketSize)) + d_fun->desc.hs_ep->wMaxPacketSize = + d_fun->desc.ss_ep->wMaxPacketSize; + + if (le16_to_cpu(d_fun->desc.fs_ep->wMaxPacketSize) > + le16_to_cpu(d_fun->desc.ss_ep->wMaxPacketSize)) + d_fun->desc.fs_ep->wMaxPacketSize = + d_fun->desc.ss_ep->wMaxPacketSize; + + pr_info("usb_ep_autoconfig %s, addr 0x%hhx, size ss=%hu hs=%hu fs=%hu\n", + ep->name, + d_fun->desc.ss_ep->bEndpointAddress, + d_fun->desc.ss_ep->wMaxPacketSize, + d_fun->desc.hs_ep->wMaxPacketSize, + d_fun->desc.fs_ep->wMaxPacketSize); + + ep->driver_data = d_fun; /* claim the endpoint */ + d_fun->ep_in = ep; + + ret = d_fun->source_drv->binded(d_fun->source_dev, ep, + &d_fun->function); + + return ret; +} + +static void dvct_function_unbind(struct usb_configuration *c, + struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + dvct_clr_status(&d_fun->status, DVCT_MASK_ONLINE); + d_fun->online_data = 0; + d_fun->online_ctrl = 0; + + d_fun->source_drv->unbinded(d_fun->source_dev); +} + +static int dvct_function_set_alt(struct usb_function *func, + unsigned intf, unsigned alt) +{ + struct dvct_function *d_fun = to_dvct_function(func); + struct usb_composite_dev *cdev = func->config->cdev; + int ret; + + DVCT_IN(); + + if (intf == d_fun->desc.c_itf->bInterfaceNumber) { + d_fun->online_ctrl = 1; + pr_debug("dvc-control interface %u set alt %u\n", intf, alt); + } + + if (intf == d_fun->desc.d_itf->bInterfaceNumber) { + ret = config_ep_by_speed(cdev->gadget, func, d_fun->ep_in); + if (ret) { + pr_debug("intf: %d alt: %d ep_by_speed in err %d\n", + intf, alt, ret); + return ret; + } + + ret = usb_ep_enable(d_fun->ep_in); + if (ret) { + pr_debug("intf: %d alt: %d ep_enable in err %d\n", + intf, alt, ret); + return ret; + } + d_fun->online_data = 1; + } + + pr_info("dvc-trace interface %u set alt %u\n", intf, alt); + + if (unlikely(dvct_get_status(&d_fun->status, DVCT_MASK_TRANS))) + dvct_stop_transfer(d_fun); + + if (d_fun->online_data && d_fun->online_ctrl) { + dvct_set_status(&d_fun->status, DVCT_MASK_ONLINE); + if (d_fun->source_drv->connected) + d_fun->source_drv->connected(d_fun->source_dev, + cdev->gadget->speed); + } + return 0; +} + +static void dvct_function_disable(struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + struct usb_composite_dev *cdev; + + DVCT_IN(); + + cdev = d_fun->cdev; + + if (dvct_get_status(&d_fun->status, DVCT_MASK_TRANS)) + dvct_stop_transfer(d_fun); + + usb_ep_disable(d_fun->ep_in); + + d_fun->online_ctrl = 0; + d_fun->online_data = 0; + + if (d_fun->source_drv->disconnected) + d_fun->source_drv->disconnected(d_fun->source_dev); + + pr_debug("%s disabled\n", d_fun->function.name); +} + +CONFIGFS_ATTR_STRUCT(dvct_function_inst); + +static ssize_t dvct_attr_show(struct config_item *item, + struct configfs_attribute *attr, char *page) +{ + struct dvct_function_inst *d_inst; + struct dvct_function_inst_attribute *d_fun_attr; + ssize_t ret = 0; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + d_fun_attr = container_of(attr, struct dvct_function_inst_attribute, + attr); + + if (d_fun_attr->show) + ret = d_fun_attr->show(d_inst, page); + + return ret; +} + +static void dvct_attr_release(struct config_item *item) +{ + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + usb_put_function_instance(&d_inst->instance); +} + +static struct configfs_item_operations dvctrace_item_ops = { + .release = dvct_attr_release, + .show_attribute = dvct_attr_show, +}; + +static ssize_t dvct_device_show(struct dvct_function_inst *d_inst, char *page) +{ + return sprintf(page, "%s\n", d_inst->source_dev->name_add); +} + +static struct dvct_function_inst_attribute f_dvctrace_device = + __CONFIGFS_ATTR_RO(source_dev, dvct_device_show); + +static struct configfs_attribute *dvct_attrs[] = { + &f_dvctrace_device.attr, + NULL, +}; + +static struct config_item_type dvct_func_type = { + .ct_item_ops = &dvctrace_item_ops, + .ct_attrs = dvct_attrs, + .ct_owner = THIS_MODULE, +}; + +static void dvct_free_func_inst(struct usb_function_instance *inst) +{ + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = to_dvct_function_inst(inst); + + spin_lock(&d_inst->source_dev->lock); + d_inst->source_dev->instance_taken = 0; + spin_unlock(&d_inst->source_dev->lock); + + kfree(d_inst); +} + +static int dvct_set_inst_name(struct usb_function_instance *inst, + const char *name) +{ + struct dvct_function_inst *d_inst; + struct dvct_source_device *new_src; + struct dvct_source_device *old_src; + + DVCT_IN(); + d_inst = to_dvct_function_inst(inst); + old_src = d_inst->source_dev; + + new_src = dvct_source_find_free_by_name(name); + + if (IS_ERR_OR_NULL(new_src)) + return -ENODEV; + + if (new_src) { + spin_lock(&new_src->lock); + spin_lock(&old_src->lock); + + d_inst->source_dev = new_src; + new_src->instance_taken = 1; + old_src->instance_taken = 0; + + spin_unlock(&old_src->lock); + spin_unlock(&new_src->lock); + } + return 0; +} + +static struct usb_function_instance *dvct_alloc_inst(void) +{ + struct dvct_function_inst *d_inst; + struct dvct_source_device *src_dev = NULL; + + DVCT_IN(); + /*get the first free source, this will change via set name + * if available */ + src_dev = dvct_source_find_free(); + + if (IS_ERR_OR_NULL(src_dev)) + return ERR_PTR(-ENODEV); + + d_inst = kzalloc(sizeof(*d_inst), GFP_KERNEL); + + if (!d_inst) + return ERR_PTR(-ENOMEM); + + d_inst->instance.free_func_inst = dvct_free_func_inst; + d_inst->instance.set_inst_name = dvct_set_inst_name; + + spin_lock(&src_dev->lock); + d_inst->source_dev = src_dev; + src_dev->instance_taken = 1; + spin_unlock(&src_dev->lock); + + config_group_init_type_name(&d_inst->instance.group, + "", &dvct_func_type); + return &d_inst->instance; +} + +static void dvct_free_func(struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + d_fun->source_drv->deactivate(d_fun->source_dev); + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 0; + spin_unlock(&d_fun->source_dev->lock); + + dvct_free_desc(d_fun); + + kfree(d_fun); +} + +static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) +{ + int ret; + struct dvct_function *d_fun; + struct dvct_function_inst *d_inst = to_dvct_function_inst(inst); + + DVCT_IN(); + d_fun = kzalloc(sizeof(struct dvct_function), GFP_KERNEL); + if (!d_fun) + return ERR_PTR(-ENOMEM); + + d_fun->source_dev = d_inst->source_dev; + d_fun->source_drv = dvct_source_get_drv(d_fun->source_dev); + d_fun->trace_config = 0; + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 1; + spin_unlock(&d_fun->source_dev->lock); + + ret = d_fun->source_drv->activate(d_fun->source_dev, &d_fun->status); + if (ret) { + pr_err("Cannot activate source device %d\n", ret); + goto err; + } + + ret = dvct_alloc_desc(d_fun); + if (ret) + goto err_des; + + /*String table */ + d_fun->function.strings = + kzalloc(2 * sizeof(struct usb_gadget_strings), GFP_KERNEL); + + if (!d_fun->function.strings) { + ret = -ENOMEM; + goto err_string_table; + } + + d_fun->function.strings[0] = &d_fun->desc.str; + + d_fun->function.name = "dvctrace"; + d_fun->function.fs_descriptors = d_fun->desc.fs; + d_fun->function.hs_descriptors = d_fun->desc.hs; + d_fun->function.ss_descriptors = d_fun->desc.ss; + d_fun->function.bind = dvct_function_bind; + d_fun->function.unbind = dvct_function_unbind; + d_fun->function.set_alt = dvct_function_set_alt; + d_fun->function.disable = dvct_function_disable; + d_fun->function.free_func = dvct_free_func; + d_fun->function.setup = dvct_setup; + + return &d_fun->function; + +err_string_table: + dvct_free_desc(d_fun); +err_des: + d_fun->source_drv->deactivate(d_fun->source_dev); +err: + kfree(d_fun); + return ERR_PTR(ret); +} + +DECLARE_USB_FUNCTION_INIT(dvctrace, dvct_alloc_inst, dvct_alloc_func); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DvC-Trace function driver"); +MODULE_AUTHOR("Traian Schiau "); diff --git a/drivers/usb/gadget/function/u_dvctrace.h b/drivers/usb/gadget/function/u_dvctrace.h new file mode 100644 index 000000000000..564d6fa7525d --- /dev/null +++ b/drivers/usb/gadget/function/u_dvctrace.h @@ -0,0 +1,72 @@ + +/* + * Gadget Driver for DvC.Trace Function + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __U_DVCTRACE_H +#define __U_DVCTRACE_H + +#include +#include + +struct dvct_function_desc { + struct usb_descriptor_header **fs; + struct usb_descriptor_header **hs; + struct usb_descriptor_header **ss; + + /*special descriptors, update on bind */ + struct usb_interface_assoc_descriptor *iad; + struct usb_interface_descriptor *d_itf; + struct usb_interface_descriptor *c_itf; + struct usb_endpoint_descriptor *fs_ep; + struct usb_endpoint_descriptor *hs_ep; + struct usb_endpoint_descriptor *ss_ep; + struct usb_ss_ep_comp_descriptor *ss_ep_comp; + + /* strings */ + struct usb_gadget_strings str; +}; + +struct dvct_function { + struct usb_function function; + struct usb_composite_dev *cdev; + struct usb_ep *ep_in; + + u32 online_data:1; /*set to one when the data itf is set */ + u32 online_ctrl:1; /*set to one when the control itf is set */ + atomic_t status; + + struct dvct_source_device *source_dev; + struct dvct_source_driver *source_drv; + + u8 trace_config; + struct dvct_function_desc desc; +}; + +struct dvct_function_inst { + struct usb_function_instance instance; + struct dvct_source_device *source_dev; +}; + +#define to_dvct_function_inst(inst) \ + container_of(inst, struct dvct_function_inst, instance) + +#define to_dvct_function(func) \ + container_of(func, struct dvct_function, function) + +ssize_t dvct_start_transfer(struct dvct_function *dev, u8 config); +int dvct_stop_transfer(struct dvct_function *dev); + +#endif /*__U_DVCTRACE_H*/ diff --git a/include/linux/usb/debug.h b/include/linux/usb/debug.h new file mode 100644 index 000000000000..074b930aedf6 --- /dev/null +++ b/include/linux/usb/debug.h @@ -0,0 +1,189 @@ +/* + * -- USB Debug Class definitions. + * + * Usb debug class specific constants, based on: + * USB 3.1 Device Class Specification for Debug Devices + * Revision 1.0 – July 14, 2015 + * http://www.usb.org/developers/docs/usb_31_072715.zip + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is distributed under the terms of the GNU General Public + * License ("GPL") version 2, as published by the Free Software Foundation. + * + */ + +#ifndef __LINUX_USB_DEBUG_H +#define __LINUX_USB_DEBUG_H + +#include + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-2: Debug Interface Sub-Class Code (SC_DEBUG) + */ +#define USB_SUBCLASS_DBC 0x02 +#define USB_SUBCLASS_DBC_DFX 0x03 +#define USB_SUBCLASS_DBC_TRACE 0x04 +#define USB_SUBCLASS_DVC_GP 0x05 +#define USB_SUBCLASS_DVC_DFX 0x06 +#define USB_SUBCLASS_DVC_TRACE 0x07 +#define USB_SUBCLASS_DEBUG_CONTROL 0x08 + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-3: Debug Interface Protocol Code (PC_DEBUG) + */ +#define DC_PROTOCOL_CODE_UNDEFINED 0x00 +#define DC_PROTOCOL_VENDOR_OR_GNU 0x01 + +#define DC_PROTOCOL_GP_VENDOR 0x00 +#define DC_PROTOCOL_GP_GNU 0x01 + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-9: Debug Unit Descriptor - bmControl + */ +#define DC_CTL_SET_CFG_DATA_SG (1 << 0) +#define DC_CTL_SET_CFG_DATA (1 << 1) +#define DC_CTL_GET_CFG_DATA (1 << 2) +#define DC_CTL_SET_CFG_ADDR (1 << 3) +#define DC_CTL_GET_CFG_ADDR (1 << 4) +#define DC_CTL_SET_OP_MODE (1 << 7) +#define DC_CTL_GET_OP_MODE (1 << 8) +#define DC_CTL_SET_BUFF_INFO (1 << 11) +#define DC_CTL_GET_BUFF_INFO (1 << 12) +#define DC_CTL_SET_RESET (1 << 13) + + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-6: Debug Class-Specific Descriptor SubTypes + */ +#define DC_UNDEFINED 0x00 +#define DC_INPUT_CONNECTION 0x01 +#define DC_OUTPUT_CONNECTION 0x02 +#define DC_DEBUG_UNIT 0x03 +#define DC_DEBUG_ATTRIBUTES 0x04 + +/* + * USB Debug Class Rev. 1.0 + * + * 4.4.4 Input-Connection Descriptor + * Table 4-7: Input Connection Descriptor - bConnectionType + * + * 4.4.5 Output Connection Descriptor + * Table 4-8: Output Connection Descriptor - bConnectionType + */ +#define DC_CONNECTION_USB 0x00 +#define DC_CONNECTION_DEBUG_CONTROL 0x01 +#define DC_CONNECTION_DEBUG_DATA 0x02 +#define DC_CONNECTION_DEBUG_DATA_CONTROL 0x03 + + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-11: dTraceFormat + */ +#define DC_VENDOR_FORMAT(v, f) (((v)<<24)|(f)) +/*Vendor N/A*/ +#define DC_TRACE_NOT_FORMATED_PASSTHROUGH DC_VENDOR_FORMAT(0x0, 0x0) +#define DC_TRACE_NOT_FORMATED_HEADER DC_VENDOR_FORMAT(0x0, 0x1) +#define DC_TRACE_NOT_FORMATED_FOOTER DC_VENDOR_FORMAT(0x0, 0x2) +#define DC_TRACE_NOT_FORMATED_GUID DC_VENDOR_FORMAT(0x0, 0x5) +#define DC_TRACE_NOT_FORMATED_UTF8 DC_VENDOR_FORMAT(0x0, 0x6) +/*Vendor Intel*/ +#define DC_TRACE_INTEL_FORMATED_VENDOR DC_VENDOR_FORMAT(0x1, 0x0) +/*Vendor ARM*/ +#define DC_TRACE_ARM_FORMATED_VENDOR DC_VENDOR_FORMAT(0x2, 0x0) +/*Vendor ST*/ +#define DC_TRACE_ST_FORMATED_VENDOR DC_VENDOR_FORMAT(0x3, 0x0) +/*Vendor TI*/ +#define DC_TRACE_TI_FORMATED_VENDOR DC_VENDOR_FORMAT(0x4, 0x0) +/*Vendor Qualcomm*/ +#define DC_TRACE_QCOMM_FORMATED_VENDOR DC_VENDOR_FORMAT(0x5, 0x0) +/*Vendor AMD*/ +#define DC_TRACE_AMD_FORMATED_VENDOR DC_VENDOR_FORMAT(0x6, 0x0) +/*Vendor MIPI*/ +#define DC_TRACE_MIPI_FORMATED DC_VENDOR_FORMAT(0x80, 0x0) +/*Vendor Nexus*/ +#define DC_TRACE_NEXUS_FORMATED DC_VENDOR_FORMAT(0x81, 0x0) + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-9: Debug Unit Descriptor - bDebugUnitType + */ +#define DC_UNIT_TYPE_UNDEFINED 0x00 +#define DC_UNIT_TYPE_DFX 0x01 +#define DC_UNIT_TYPE_SELECT 0x02 +#define DC_UNIT_TYPE_TRACE_ROUTE 0x03 +#define DC_UNIT_TYPE_TRACE_PROC 0x04 +#define DC_UNIT_TYPE_TRACE_GEN 0x05 +#define DC_UNIT_TYPE_TRACE_SINK 0x06 +#define DC_UNIT_TYPE_CONTROL 0x07 +#define DC_UNIT_TYPE_VENDOR_FIRST 0x40 +#define DC_UNIT_TYPE_VENDOR_LAST 0x5F + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-10: Debug Sub-Unit Type + */ +#define DC_UNIT_SUBTYPE_NULL 0x00 +#define DC_UNIT_SUBTYPE_CPU 0x01 +#define DC_UNIT_SUBTYPE_GFX 0x02 +#define DC_UNIT_SUBTYPE_VIDEO 0x03 +#define DC_UNIT_SUBTYPE_IMAGING 0x04 +#define DC_UNIT_SUBTYPE_AUDIO 0x05 +#define DC_UNIT_SUBTYPE_MODEM 0x06 +#define DC_UNIT_SUBTYPE_BLUETOOTH 0x07 +#define DC_UNIT_SUBTYPE_PWR_MGT 0x08 +#define DC_UNIT_SUBTYPE_SECURITY 0x09 +#define DC_UNIT_SUBTYPE_SENSOR 0x0A +#define DC_UNIT_SUBTYPE_BUSWATCH 0x0B +#define DC_UNIT_SUBTYPE_LOCATION 0x0C +#define DC_UNIT_SUBTYPE_TRACEZIP 0x0D +#define DC_UNIT_SUBTYPE_TAPCTL 0x0E +#define DC_UNIT_SUBTYPE_MEMACC 0x0F +#define DC_UNIT_SUBTYPE_OTHER 0x3F +#define DC_UNIT_SUBTYPE_SWLOGGER 0x40 +#define DC_UNIT_SUBTYPE_SWROUTER 0x41 +#define DC_UNIT_SUBTYPE_SWUNINT 0x42 +#define DC_UNIT_SUBTYPE_SWCFGUNINT 0x43 +#define DC_UNIT_SUBTYPE_SWDEBUGGER 0x44 +#define DC_UNIT_SUBTYPE_VENDOR_FIRST 0x80 +#define DC_UNIT_SUBTYPE_VENDOR_LAST 0xBF +#define DC_UNIT_SUBTYPE_STANDARDS 0xFF + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-5: Debug Class-Specific Commands bRequest + */ +/*Set*/ +#define DC_REQUEST_SET_CONFIG_DATA 0x01 /*S 5.4.4 T 5-6*/ +#define DC_REQUEST_SET_CONFIG_DATA_SINGLE 0x02 /*S 5.4.3 T 5-4*/ +#define DC_REQUEST_SET_CONFIG_ADDRESS 0x03 /*S 5.4.6 T 5-8*/ +#define DC_REQUEST_SET_ALT_STACK 0x04 /*S 5.4.8 T 5-10*/ +#define DC_REQUEST_SET_OPERATING_MODE 0x05 /*S 5.4.10 T 5-15*/ +#define DC_REQUEST_SET_TRACE 0x06 /*S 5.4.14 T 5-23*/ +#define DC_REQUEST_SET_BUFFER 0x09 /*S 5.4.16 T 5-25*/ +#define DC_REQUEST_SET_RESET 0x0A /*S 5.4.18 T 5-28f*/ +/*Get*/ +#define DC_REQUEST_GET_CONFIG_DATA 0x81 /*S 5.4.5 T 5-7*/ +#define DC_REQUEST_GET_CONFIG_DATA_SINGLE 0x82 +#define DC_REQUEST_GET_CONFIG_ADDRESS 0x83 /*S 5.4.7 T 5-9*/ +#define DC_REQUEST_GET_ALT_STACK 0x84 /*S 5.4.9 T 5-11*/ +#define DC_REQUEST_GET_OPERATING_MODE 0x85 /*S 5.4.11 T 5-18*/ +#define DC_REQUEST_GET_TRACE 0x86 /*S 5.4.15 T 5-24*/ +#define DC_REQUEST_GET_INFO 0x87 /*S 5.4.12 T 5-19*/ +#define DC_REQUEST_GET_ERROR 0x88 /*S 5.4.13 T 5-21*/ +#define DC_REQUEST_GET_BUFFER 0x89 /*S 5.4.17 T 5-27*/ + +#endif /* __LINUX_USB_DEBUG_H */ diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index d5a5caec8fbc..821ff8778618 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -325,6 +325,7 @@ struct usb_device_descriptor { #define USB_CLASS_CSCID 0x0b /* chip+ smart card */ #define USB_CLASS_CONTENT_SEC 0x0d /* content security */ #define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_DEBUG 0xdc #define USB_CLASS_WIRELESS_CONTROLLER 0xe0 #define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe From 69630367458cca34724f95e457786593ac28c7bb Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:32:11 +0300 Subject: [PATCH 0938/1276] dvctrace: Topology descriptors support. DvC Trace supports additional custom USB descriptors that should be provided by the source device. Signed-off-by: Traian Schiau --- Documentation/ABI/testing/sysfs-bus-dvctrace | 42 ++ drivers/bus/dvctrace.c | 441 +++++++++++++++++++ drivers/usb/gadget/function/f_dvctrace.c | 149 +++++-- drivers/usb/gadget/function/u_dvctrace.h | 1 + include/linux/dvctrace.h | 33 +- 5 files changed, 633 insertions(+), 33 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace b/Documentation/ABI/testing/sysfs-bus-dvctrace index 0abdd3293364..bf2b5bb2144a 100644 --- a/Documentation/ABI/testing/sysfs-bus-dvctrace +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace @@ -24,3 +24,45 @@ Description: (RW) The protocol id of a dvc-trace source device, this will used in function driver interface descriptors (u8). According to USB debug class specification the protocol id is vendor specific. + +What: /sys/bus/dvctrace/devices//descriptors +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Hex-dump of the descriptors provided by the + source device. + eg. A debug class output connection descriptor + 09 24 02 04 03 00 00 00 00 + ll tt ss xx xx xx xx xx ii + | | | +- iConnection string id. + | | +- Descriptor sub-type DC_OUTPUT_CONNECTION + | +- Descriptor type (USB_DT_CS_INTERFACE) + +- Descriptor length + Writing: + - is not allowed while the device is Reserved or In Use. + - will replace all the descriptors currently present. + - will remove any strings previously provided. + - should use the same format. + - accepts multiple descriptors separated by space or '\n'. + +What: /sys/bus/dvctrace/devices//strings +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Currently set usb descriptor strings in + .: string format. + . identifies the location where + the string id is needed. + eg. Having the same debug class output connection descriptor, + as the first descriptor. + 09 24 02 04 03 00 00 00 00 + ll tt ss xx xx xx xx xx ii + +- iConnection string id. + 0.8: My output connection - will identify the string associated + with this descriptor. + Writing: + - is not allowed while the device is Reserved or In Use. + - will replace all the strings currently present. + - should use the same format. + - accepts multiple strings separated by ";" or '\n'. + eg. "0.4: first string; 1.4: second string" diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index c1770ca44422..906e972103d9 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef DVCT_DEBUG #define DVCT_IN() pr_debug("in\n") @@ -26,6 +27,435 @@ #define DVCT_IN() do {} while (0) #endif +/* Count the number of USB descriptors in the given ascii hex string + * What we expect: + * ll tt ss xx xx xx + * | | | +- Fill up the descriptor + * | | +- Descriptor sub-type (1-4) + * | | DC_INPUT_CONNECTION 0x01 + * | | DC_OUTPUT_CONNECTION 0x02 + * | | DC_DEBUG_UNIT 0x03 + * | | DC_DEBUG_ATTRIBUTES 0x04 + * | +- Descriptor type (USB_DT_CS_INTERFACE) + * +- Descriptor length (check > 3 and we have the rest of it) + */ +static int count_descriptors(const char *buf, size_t size) +{ + size_t off = 0; + int i, j, count = 0; + u8 len, tmp; + + DVCT_IN(); + while (off < size) { + /*the length*/ + j = sscanf(buf + off, "%2hhx%n", &len, &i); + if (!j) + break; + if (j < 0 || len < 4) + return -EINVAL; + len--; + off += i; + + /*Type*/ + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0 || tmp != USB_DT_CS_INTERFACE) + return -EINVAL; + len--; + off += i; + + /*Sub Type*/ + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0 || tmp < DC_INPUT_CONNECTION + || tmp > DC_DEBUG_ATTRIBUTES) + return -EINVAL; + len--; + off += i; + + while (len) { + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0) + return -EINVAL; + len--; + off += i; + } + count++; + } + return count; +} + +/* Parse @buf and get a pointer to the descriptor identified + * @idx*/ +static u8 *get_descriptor(const char *buf, size_t size, int idx) +{ + size_t off = 0; + int i, j, k, count = 0; + u8 len, tmp, *ret = NULL; + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%2hhx%n", &len, &i); + if (j < 0) + return ERR_PTR(-EINVAL); + if (!j) + return ERR_PTR(-ERANGE); + + if (count == idx) { + ret = kmalloc(len, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + ret[0] = len; + } + off += i; + for (k = 1; k < len; k++) { + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0) { + kfree(ret); + return ERR_PTR(-EINVAL); + } + if (count == idx) + ret[k] = tmp; + off += i; + } + if (count == idx) + break; + count++; + } + return ret; +} + + +static void free_strings(struct dvct_usb_descriptors *desc) +{ + struct usb_string *string; + + DVCT_IN(); + for (string = desc->str.strings; string && string->s; string++) + kfree(string->s); + + kfree(desc->str.strings); + desc->str.strings = NULL; + kfree(desc->lk_tbl); + desc->lk_tbl = NULL; +} + +static void free_descriptors(struct dvct_usb_descriptors *desc) +{ + struct usb_descriptor_header **hdr; + + DVCT_IN(); + if (desc->dvc_spec) { + for (hdr = desc->dvc_spec; *hdr; hdr++) + kfree(*hdr); + kfree(desc->dvc_spec); + desc->dvc_spec = NULL; + } + free_strings(desc); + kfree(desc); +} + +static int alloc_strings(struct dvct_usb_descriptors *desc, int count) +{ + DVCT_IN(); + desc->lk_tbl = kzalloc((count + 1) * sizeof(struct dvct_string_lookup), + GFP_KERNEL); + if (!desc->lk_tbl) + goto err; + + desc->str.strings = kzalloc(sizeof(desc->str), GFP_KERNEL); + if (!desc->str.strings) + goto err_str; + + desc->str.language = 0x0409; + + return count; +err_str: + kfree(desc->lk_tbl); + desc->lk_tbl = NULL; +err: + return -ENOMEM; +} + +static struct dvct_usb_descriptors *alloc_descriptors(int count) +{ + struct dvct_usb_descriptors *desc; + + DVCT_IN(); + desc = kzalloc(sizeof(struct dvct_usb_descriptors), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + desc->dvc_spec = + kzalloc((count + 1) * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + + if (!desc->dvc_spec) { + kfree(desc); + return ERR_PTR(-ENOMEM); + } + return desc; +} + +static ssize_t descriptors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + struct usb_descriptor_header **desc; + int ret = 0; + + DVCT_IN(); + if (!ds_dev->desc || !ds_dev->desc->dvc_spec + || !*ds_dev->desc->dvc_spec) + return sprintf(buf, "No Descriptors.\n"); + + for (desc = ds_dev->desc->dvc_spec; *desc; desc++) { + u8 len, *pdesc; + int i; + + len = (*desc)->bLength; + pdesc = (u8 *)(*desc); + for (i = 0; i < len; i++) + ret += snprintf(buf + ret, PAGE_SIZE - ret, "%02hhX ", + pdesc[i]); + buf[ret - 1] = '\n'; + } + return ret; +} + +static ssize_t descriptors_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int desc_count, i; + u8 *hdr; + + DVCT_IN(); + + if (ds_dev->instance_taken) + return -EBUSY; + + /*count the new descriptors, exit if invalid input*/ + desc_count = count_descriptors(buf, size); + if (desc_count <= 0) { + dev_warn(dev, "Invalid descriptor input:[%zu] %s", size, buf); + return -EINVAL; + } + + if (ds_dev->desc && ds_dev->desc != &ds_dev->static_desc) + free_descriptors(ds_dev->desc); + + ds_dev->desc = alloc_descriptors(desc_count); + if (IS_ERR_OR_NULL(ds_dev->desc)) { + ds_dev->desc = NULL; + return -ENOMEM; + } + + for (i = 0; i < desc_count; i++) { + hdr = get_descriptor(buf, size, i); + if (IS_ERR_OR_NULL(hdr)) { + dev_err(dev, "Cannot get descriptor %d, %ld\n", i, + PTR_ERR(hdr)); + free_descriptors(ds_dev->desc); + ds_dev->desc = NULL; + return -EINVAL; + } + ds_dev->desc->dvc_spec[i] = (struct usb_descriptor_header *)hdr; + } + return size; +} + +static DEVICE_ATTR_RW(descriptors); + + +/*find out at which member(offset) of which descriptor the pointer + * points to */ +static int dvctrace_string_ptr_to_offset(struct usb_descriptor_header **first, + u8 *ptr, int *desc_offset, int *offset) +{ + u8 *hdr_start, *hdr_end; + int idx = 0; + + DVCT_IN(); + for (; *first; first++, idx++) { + hdr_start = (u8 *) (*first); + hdr_end = hdr_start + ((*first)->bLength - 1); + if (ptr >= hdr_start && ptr <= hdr_end) { + *desc_offset = idx; + *offset = ptr - hdr_start; + return 0; + } + } + return -EINVAL; +} + +static u8 *dvctrace_offset_to_string_ptr(struct usb_descriptor_header **first, + int desc_offset, int offset) +{ + int idx = 0; + + DVCT_IN(); + for (; *first; first++, idx++) { + if (idx == desc_offset) { + if (offset >= (*first)->bLength) + return ERR_PTR(-ERANGE); + return ((u8 *) (*first)) + offset; + } + } + return ERR_PTR(-ERANGE); +} + +static int count_strings(const char *buf, size_t size) +{ + int count = 0; + size_t off = 0, slen; + int i = 0, j, desc_offset, offset; + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%d.%d: %n", &desc_offset, &offset, &i); + if (j < 2) + break; + off += i; + slen = 0; + while (off + slen < size) { + if (buf[off + slen] == ';' || buf[off + slen] == '\n') + break; + slen++; + } + off += slen; + if (buf[off] == ';' || buf[off] == '\n') + off++; + count++; + } + return count; +} + +static char *get_string(const char *buf, size_t size, int index, + int *desc_offset, int *offset) +{ + int count = 0; + size_t off = 0, slen; + int i, j; + char *ret = ERR_PTR(-EINVAL); + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%d.%d: %n", desc_offset, offset, &i); + if (j < 2) + return ERR_PTR(-EINVAL); + off += i; + slen = 0; + while (off + slen < size) { + if (buf[off + slen] == ';' || buf[off + slen] == '\n') + break; + slen++; + } + + if (count == index) { + ret = kmalloc(slen+1, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + memcpy(ret, buf + off, slen); + ret[slen] = 0; + return ret; + } + off += slen; + if (buf[off] == ';' || buf[off] == '\n') + off++; + count++; + } + return ERR_PTR(-EINVAL); +} + +static ssize_t strings_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dvct_string_lookup *lk_s; + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int ret = 0; + + DVCT_IN(); + if (!ds_dev->desc || !ds_dev->desc->dvc_spec + || !*ds_dev->desc->dvc_spec) + return sprintf(buf, "No Descriptors.\n"); + + if (!ds_dev->desc->lk_tbl) + return sprintf(buf, "No Strings.\n"); + + for (lk_s = ds_dev->desc->lk_tbl; lk_s->str && lk_s->id; lk_s++) { + int desc_offset, offset; + + if (dvctrace_string_ptr_to_offset(ds_dev->desc->dvc_spec, + lk_s->id, &desc_offset, + &offset)) + ret += snprintf(buf + ret, PAGE_SIZE, + "Unknown(%p): %s\n", lk_s->id, + lk_s->str->s); + else + ret += snprintf(buf + ret, PAGE_SIZE, "%d.%d: %s\n", + desc_offset, offset, lk_s->str->s); + } + return ret; +} + +static ssize_t strings_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int count, i, ret; + + DVCT_IN(); + if (ds_dev->instance_taken) + return -EBUSY; + + count = count_strings(buf, size); + if (count <= 0) { + dev_err(dev, "Invalid input string:(%zu) %s\n", size, buf); + return -EINVAL; + } + + if (ds_dev->desc == &ds_dev->static_desc) { + dev_warn(&ds_dev->device, "Cannot set strings in static descriptors\n"); + return -EINVAL; + } + + if (ds_dev->desc->str.strings) + free_strings(ds_dev->desc); + + ret = alloc_strings(ds_dev->desc, count); + if (ret < 0) { + dev_err(dev, "Cannot allocate strings %d\n", ret); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + char *tmp; + int d_off, off; + u8 *pid; + + tmp = get_string(buf, size, i, &d_off, &off); + if (IS_ERR_OR_NULL(tmp)) { + free_strings(ds_dev->desc); + return -EINVAL; + } + + pid = dvctrace_offset_to_string_ptr(ds_dev->desc->dvc_spec, + d_off, off); + if (IS_ERR_OR_NULL(pid)) { + dev_warn(&ds_dev->device, "String out of bounds\n"); + free_strings(ds_dev->desc); + return -EINVAL; + } + + ds_dev->desc->lk_tbl[i].id = pid; + ds_dev->desc->lk_tbl[i].str = &ds_dev->desc->str.strings[i]; + ds_dev->desc->str.strings[i].s = tmp; + } + return size; +} + +static DEVICE_ATTR_RW(strings); + static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -71,6 +501,8 @@ static DEVICE_ATTR_RO(status); static struct attribute *dvct_source_attrs[] = { &dev_attr_protocol.attr, &dev_attr_status.attr, + &dev_attr_strings.attr, + &dev_attr_descriptors.attr, NULL, }; @@ -208,6 +640,9 @@ int dvct_source_device_add(struct dvct_source_device *ds_dev, return ret; } + if (ds_dev->static_desc.dvc_spec) + ds_dev->desc = &ds_dev->static_desc; + dev_notice(&dvctrace_bus, "Adding device %s\n", ds_dev->name_add); return 0; }; @@ -216,6 +651,12 @@ EXPORT_SYMBOL_GPL(dvct_source_device_add); void dvct_source_device_del(struct dvct_source_device *ds_dev) { DVCT_IN(); + + if (ds_dev->desc && ds_dev->desc != &ds_dev->static_desc) { + free_descriptors(ds_dev->desc); + ds_dev->desc = NULL; + } + device_del(&ds_dev->device); }; EXPORT_SYMBOL_GPL(dvct_source_device_del); diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c index dffd06126d8f..d5cb49db52bc 100644 --- a/drivers/usb/gadget/function/f_dvctrace.c +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -39,6 +39,26 @@ enum { DVCT_HS_DESC_COUNT,/*Count of super speed descriptors*/ }; +/*The full list of descriptors will look like: + * IAD_DESCRIPTOR -----|=> USB function specific + * CONTROL_ITF_DESCRIPTOR -----| + * SOURCE_SPECIFIC_DESCRIPTOR_0 ----| + * .... |=> s_cnt descriptors provided by the + * SOURCE_SPECIFIC_DESCRIPTOR_s_cnt----| source device. + * DATA_ITF_DESCRIPTOR -----| + * ENDPOINT_DESCRIPTOR |=> USB function specific + * .... -----| + * This makes a good part of the descriptors to shift, + * the following should help*/ +#define DVCT_IAD_DESC_DYN_POS(s_cnt) (DVCT_IAD_DESC_POS) +#define DVCT_CITF_DESC_DYN_POS(s_cnt) (DVCT_CITF_DESC_POS) +#define DVCT_SOURCE_DESC_FIRST(s_cnt) (DVCT_DITF_DESC_POS) +#define DVCT_DITF_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_DITF_DESC_POS) +#define DVCT_EP_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_EP_DESC_POS) +#define DVCT_EP_COMP_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_EP_COMP_DESC_POS) +#define DVCT_LS_DESC_DYN_COUNT(s_cnt) ((s_cnt)+DVCT_LS_DESC_COUNT) +#define DVCT_HS_DESC_DYN_COUNT(s_cnt) ((s_cnt)+DVCT_HS_DESC_COUNT) + enum { DVCT_STR_IAD_IDX, DVCT_STR_C_ITF_IDX, @@ -49,25 +69,35 @@ enum { static int dvct_alloc_desc(struct dvct_function *d_fun) { + int i; + unsigned int s_desc_count = 0; + struct usb_descriptor_header **s_desc; struct dvct_function_desc *desc = &d_fun->desc; DVCT_IN(); + if (d_fun->source_dev->desc) { + for (s_desc = d_fun->source_dev->desc->dvc_spec; + s_desc && (*s_desc); s_desc++) + s_desc_count++; + } + + /*alloc the descriptors array */ desc->fs = - kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_LS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->fs) goto err_fs; desc->hs = - kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_LS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->hs) goto err_hs; desc->ss = - kzalloc(DVCT_HS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_HS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->ss) goto err_ss; @@ -84,9 +114,12 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->iad->bFunctionProtocol = d_fun->source_dev->protocol; /*bFirstInterface - updated on bind */ - desc->fs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; - desc->hs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; - desc->ss[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->fs[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; + desc->hs[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; + desc->ss[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; /*Control interface */ desc->c_itf = kzalloc(sizeof(*desc->c_itf), GFP_KERNEL); @@ -99,13 +132,25 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->c_itf->bInterfaceSubClass = USB_SUBCLASS_DEBUG_CONTROL; desc->c_itf->bInterfaceProtocol = d_fun->source_dev->protocol; - desc->fs[DVCT_CITF_DESC_POS] = + desc->fs[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; - desc->hs[DVCT_CITF_DESC_POS] = + desc->hs[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; - desc->ss[DVCT_CITF_DESC_POS] = + desc->ss[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; + if (d_fun->source_dev->desc) { + /*Copy whatever the source device has provided */ + s_desc = d_fun->source_dev->desc->dvc_spec; + for (i = 0; i < s_desc_count; i++) { + desc->fs[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + desc->hs[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + desc->ss[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + } + } /*Data interface */ desc->d_itf = kzalloc(sizeof(*desc->d_itf), GFP_KERNEL); if (!desc->d_itf) @@ -118,11 +163,11 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->d_itf->bInterfaceSubClass = USB_SUBCLASS_DVC_TRACE; desc->d_itf->bInterfaceProtocol = d_fun->source_dev->protocol; - desc->fs[DVCT_DITF_DESC_POS] = + desc->fs[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; - desc->hs[DVCT_DITF_DESC_POS] = + desc->hs[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; - desc->ss[DVCT_DITF_DESC_POS] = + desc->ss[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; /*Full Speed ep */ @@ -136,7 +181,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->fs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->fs_ep->wMaxPacketSize = cpu_to_le16(64); - desc->fs[DVCT_EP_DESC_POS] = + desc->fs[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->fs_ep; /*High Speed ep */ @@ -150,7 +195,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->hs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->hs_ep->wMaxPacketSize = cpu_to_le16(512); - desc->hs[DVCT_EP_DESC_POS] = + desc->hs[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->hs_ep; /*Super Speed ep */ @@ -164,7 +209,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->ss_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->ss_ep->wMaxPacketSize = cpu_to_le16(1024); - desc->ss[DVCT_EP_DESC_POS] = + desc->ss[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->ss_ep; /*Super Speed ep comp */ @@ -175,7 +220,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->ss_ep_comp->bLength = USB_DT_SS_EP_COMP_SIZE; desc->ss_ep_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP; - desc->ss[DVCT_EP_COMP_DESC_POS] = + desc->ss[DVCT_EP_COMP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->ss_ep_comp; /* strings */ @@ -186,6 +231,14 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings) goto err_str; + /*lookup table */ + desc->lk_tbl = + kzalloc(DVCT_STR_COUNT * sizeof(struct dvct_string_lookup), + GFP_KERNEL); + if (!desc->lk_tbl) + goto err_str_lk; + + /*actual strings */ /*IAD*/ desc->str.strings[DVCT_STR_IAD_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace (%s)", @@ -193,6 +246,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_IAD_IDX].s) goto err_str_iad; + desc->lk_tbl[DVCT_STR_IAD_IDX].str = + &desc->str.strings[DVCT_STR_IAD_IDX]; + desc->lk_tbl[DVCT_STR_IAD_IDX].id = &desc->iad->iFunction; + /*control */ desc->str.strings[DVCT_STR_C_ITF_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace Control (%s)", @@ -200,6 +257,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_C_ITF_IDX].s) goto err_str_ctrl; + desc->lk_tbl[DVCT_STR_C_ITF_IDX].str = + &desc->str.strings[DVCT_STR_C_ITF_IDX]; + desc->lk_tbl[DVCT_STR_C_ITF_IDX].id = &desc->c_itf->iInterface; + /*data */ desc->str.strings[DVCT_STR_D_ITF_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace Data (%s)", @@ -207,6 +268,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_D_ITF_IDX].s) goto err_str_data; + desc->lk_tbl[DVCT_STR_D_ITF_IDX].str = + &desc->str.strings[DVCT_STR_D_ITF_IDX]; + desc->lk_tbl[DVCT_STR_D_ITF_IDX].id = &desc->d_itf->iInterface; + return 0; /*cleanup*/ err_str_data: @@ -214,6 +279,8 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) err_str_ctrl: kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); err_str_iad: + kfree(desc->lk_tbl); +err_str_lk: kfree(desc->str.strings); err_str: kfree(desc->ss_ep_comp); @@ -249,6 +316,7 @@ static void dvct_free_desc(struct dvct_function *d_fun) kfree(desc->str.strings[DVCT_STR_D_ITF_IDX].s); kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); + kfree(desc->lk_tbl); kfree(desc->str.strings); kfree(desc->ss_ep_comp); kfree(desc->ss_ep); @@ -290,25 +358,25 @@ int dvct_stop_transfer(struct dvct_function *d_fun) EXPORT_SYMBOL(dvct_stop_transfer); static int dvct_strings_setup(struct usb_composite_dev *cdev, - struct dvct_function *f_fun) + struct usb_string *strings, + struct dvct_string_lookup *lk_tbl) { int status; + struct dvct_string_lookup *str_lk; DVCT_IN(); - if (!f_fun->desc.str.strings) + if (!strings || !lk_tbl) return -EINVAL; - status = usb_string_ids_tab(cdev, f_fun->desc.str.strings); + status = usb_string_ids_tab(cdev, strings); if (status < 0) return status; - f_fun->desc.iad->iFunction = - f_fun->desc.str.strings[DVCT_STR_IAD_IDX].id; - f_fun->desc.c_itf->iInterface = - f_fun->desc.str.strings[DVCT_STR_C_ITF_IDX].id; - f_fun->desc.d_itf->iInterface = - f_fun->desc.str.strings[DVCT_STR_D_ITF_IDX].id; - + for (str_lk = lk_tbl; str_lk->str; str_lk++) { + *str_lk->id = str_lk->str->id; + pr_info("Setting id %d for str \"%s\"\n", str_lk->str->id, + str_lk->str->s); + } return 0; } @@ -395,7 +463,17 @@ static int dvct_function_bind(struct usb_configuration *cconfig, DVCT_IN(); d_fun->cdev = cconfig->cdev; - ret = dvct_strings_setup(d_fun->cdev, d_fun); + /*allocate id's */ + /*strings. not crucial just print on failure */ + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) { + ret = dvct_strings_setup(d_fun->cdev, + d_fun->source_dev->desc->str.strings, + d_fun->source_dev->desc->lk_tbl); + if (ret) + pr_warn("Cannot allocate source device string id's\n"); + } + ret = dvct_strings_setup(d_fun->cdev, d_fun->desc.str.strings, + d_fun->desc.lk_tbl); if (ret) pr_warn("Cannot allocate function string id's\n"); @@ -715,8 +793,13 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) if (ret) goto err_des; - /*String table */ - d_fun->function.strings = + /*String table*/ + /*1 - source dev (if present) , 1 - function, 1 - NULL */ + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) + d_fun->function.strings = + kzalloc(3 * sizeof(struct usb_gadget_strings), GFP_KERNEL); + else + d_fun->function.strings = kzalloc(2 * sizeof(struct usb_gadget_strings), GFP_KERNEL); if (!d_fun->function.strings) { @@ -725,6 +808,8 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) } d_fun->function.strings[0] = &d_fun->desc.str; + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) + d_fun->function.strings[1] = &d_fun->source_dev->desc->str; d_fun->function.name = "dvctrace"; d_fun->function.fs_descriptors = d_fun->desc.fs; diff --git a/drivers/usb/gadget/function/u_dvctrace.h b/drivers/usb/gadget/function/u_dvctrace.h index 564d6fa7525d..56a5d0cd577a 100644 --- a/drivers/usb/gadget/function/u_dvctrace.h +++ b/drivers/usb/gadget/function/u_dvctrace.h @@ -37,6 +37,7 @@ struct dvct_function_desc { /* strings */ struct usb_gadget_strings str; + struct dvct_string_lookup *lk_tbl; }; struct dvct_function { diff --git a/include/linux/dvctrace.h b/include/linux/dvctrace.h index 18291efb18b6..4a21f58b1aae 100644 --- a/include/linux/dvctrace.h +++ b/include/linux/dvctrace.h @@ -50,22 +50,53 @@ #define dvct_clr_status(s, m) atomic_set(s, atomic_read(s) & (~(m))) #define dvct_get_status(s, m) (atomic_read(s) & (m)) +/** + * dvct_string_lookup - helper struct to better manage the string id allocation + * @str: the usb string to allocate the id for + * @id: the location in the descriptor where the id should be updated + */ +struct dvct_string_lookup { + struct usb_string *str; + u8 *id; /*points to the location of the string id */ +}; + +/** + * dvctrace_usb_descriptors - holds DvC Trace usb descriptors information + * @dvc_spec: Dvc Trace specific descriptors, will be added in the function + * usb descriptors. + * @str: Usb gadget string table for strings referenced in @dvc_spec. + * @lk_tbl: Strings lookup table. + * + * From a strict USB point of view the descriptors are optional so @dvc_spec, + * @str.strings and @lk_tbl can be null. However the dvc-trace host + * implementation might need them. + */ +struct dvct_usb_descriptors { + struct usb_descriptor_header **dvc_spec; /*null terminated*/ + struct usb_gadget_strings str; + struct dvct_string_lookup *lk_tbl;/*null terminated*/ +}; + /** * dvct_source_device - DvC Trace function to source (backend) interface * * @name_add: postfix used to compute the source name (-) * @protocol: Interface protocol code (used in IAD, control and data + * @static_desc: DvC-Trace specific usb descriptors, provided by the driver + * interface descriptors) * - * @instance_taken, @function_taken, @lock, @device are reserved for + * @instance_taken, @function_taken, @lock, @device, @desc are reserved for * internal use only. */ struct dvct_source_device { const char *name_add; u8 protocol; + struct dvct_usb_descriptors static_desc; u32 instance_taken:1; u32 function_taken:1; spinlock_t lock; struct device device; + struct dvct_usb_descriptors *desc; }; /** From 5f9c16de0e0d6aa3b0e136715b09a318aa16b918 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:32:37 +0300 Subject: [PATCH 0939/1276] dvctrace: Fix topology descriptor corruption. A bug was found in dvc-trace topology descriptors (strings) allocation (insufficient space) leading to memory corruption in some cases. Also improve buffer overflow checks for long attributes (descriptors and strings). Signed-off-by: Traian Schiau --- drivers/bus/dvctrace.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index 906e972103d9..915c42593edb 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -161,7 +161,8 @@ static int alloc_strings(struct dvct_usb_descriptors *desc, int count) if (!desc->lk_tbl) goto err; - desc->str.strings = kzalloc(sizeof(desc->str), GFP_KERNEL); + desc->str.strings = kzalloc((count + 1) * sizeof(*desc->str.strings), + GFP_KERNEL); if (!desc->str.strings) goto err_str; @@ -212,6 +213,13 @@ static ssize_t descriptors_show(struct device *dev, int i; len = (*desc)->bLength; + + /* Check if it fits, total output is 3 * len */ + if ((ret + 3 * len) > PAGE_SIZE) { + dev_warn(dev, "Descriptors attribute page overrun\n"); + break; + } + pdesc = (u8 *)(*desc); for (i = 0; i < len; i++) ret += snprintf(buf + ret, PAGE_SIZE - ret, "%02hhX ", @@ -385,15 +393,25 @@ static ssize_t strings_show(struct device *dev, struct device_attribute *attr, for (lk_s = ds_dev->desc->lk_tbl; lk_s->str && lk_s->id; lk_s++) { int desc_offset, offset; + /* + * Check if it fits, worst case is "Unknown(%p): %s\n" + * 8 + 16 + 3 + string length + 1 + */ + if ((ret + 28 + strlen(lk_s->str->s)) > PAGE_SIZE) { + dev_warn(dev, "Strings attribute page overrun\n"); + break; + } + if (dvctrace_string_ptr_to_offset(ds_dev->desc->dvc_spec, lk_s->id, &desc_offset, &offset)) - ret += snprintf(buf + ret, PAGE_SIZE, + ret += snprintf(buf + ret, PAGE_SIZE - ret, "Unknown(%p): %s\n", lk_s->id, lk_s->str->s); else - ret += snprintf(buf + ret, PAGE_SIZE, "%d.%d: %s\n", - desc_offset, offset, lk_s->str->s); + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "%d.%d: %s\n", desc_offset, offset, + lk_s->str->s); } return ret; } @@ -415,7 +433,7 @@ static ssize_t strings_store(struct device *dev, struct device_attribute *attr, } if (ds_dev->desc == &ds_dev->static_desc) { - dev_warn(&ds_dev->device, "Cannot set strings in static descriptors\n"); + dev_warn(dev, "Cannot set strings in static descriptors\n"); return -EINVAL; } @@ -442,7 +460,7 @@ static ssize_t strings_store(struct device *dev, struct device_attribute *attr, pid = dvctrace_offset_to_string_ptr(ds_dev->desc->dvc_spec, d_off, off); if (IS_ERR_OR_NULL(pid)) { - dev_warn(&ds_dev->device, "String out of bounds\n"); + dev_warn(dev, "String out of bounds\n"); free_strings(ds_dev->desc); return -EINVAL; } From 8bc06d79dad8c57b8ab8be58d062c2df417e0e69 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 18:27:14 +0300 Subject: [PATCH 0940/1276] dvctrace-function: Configfs changes Adapt to the new configfs api. Minor changes in instance/function allocation and work flow. Signed-off-by: Traian Schiau --- drivers/usb/gadget/function/f_dvctrace.c | 60 +++++++++--------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c index d5cb49db52bc..2589632a0e90 100644 --- a/drivers/usb/gadget/function/f_dvctrace.c +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -463,6 +463,10 @@ static int dvct_function_bind(struct usb_configuration *cconfig, DVCT_IN(); d_fun->cdev = cconfig->cdev; + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 1; + spin_unlock(&d_fun->source_dev->lock); + /*allocate id's */ /*strings. not crucial just print on failure */ if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) { @@ -548,6 +552,10 @@ static void dvct_function_unbind(struct usb_configuration *c, d_fun->online_ctrl = 0; d_fun->source_drv->unbinded(d_fun->source_dev); + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 0; + spin_unlock(&d_fun->source_dev->lock); } static int dvct_function_set_alt(struct usb_function *func, @@ -618,27 +626,6 @@ static void dvct_function_disable(struct usb_function *func) pr_debug("%s disabled\n", d_fun->function.name); } -CONFIGFS_ATTR_STRUCT(dvct_function_inst); - -static ssize_t dvct_attr_show(struct config_item *item, - struct configfs_attribute *attr, char *page) -{ - struct dvct_function_inst *d_inst; - struct dvct_function_inst_attribute *d_fun_attr; - ssize_t ret = 0; - - DVCT_IN(); - d_inst = container_of(to_config_group(item), struct dvct_function_inst, - instance.group); - d_fun_attr = container_of(attr, struct dvct_function_inst_attribute, - attr); - - if (d_fun_attr->show) - ret = d_fun_attr->show(d_inst, page); - - return ret; -} - static void dvct_attr_release(struct config_item *item) { struct dvct_function_inst *d_inst; @@ -651,19 +638,23 @@ static void dvct_attr_release(struct config_item *item) static struct configfs_item_operations dvctrace_item_ops = { .release = dvct_attr_release, - .show_attribute = dvct_attr_show, }; -static ssize_t dvct_device_show(struct dvct_function_inst *d_inst, char *page) +static ssize_t f_dvctrace_device_show(struct config_item *item, char *page) { - return sprintf(page, "%s\n", d_inst->source_dev->name_add); + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + + return sprintf(page, "%s\n", dev_name(&d_inst->source_dev->device)); } -static struct dvct_function_inst_attribute f_dvctrace_device = - __CONFIGFS_ATTR_RO(source_dev, dvct_device_show); +CONFIGFS_ATTR_RO(f_dvctrace_, device); static struct configfs_attribute *dvct_attrs[] = { - &f_dvctrace_device.attr, + &f_dvctrace_attr_device, NULL, }; @@ -698,12 +689,15 @@ static int dvct_set_inst_name(struct usb_function_instance *inst, d_inst = to_dvct_function_inst(inst); old_src = d_inst->source_dev; - new_src = dvct_source_find_free_by_name(name); + new_src = dvct_source_find_by_name(name); if (IS_ERR_OR_NULL(new_src)) return -ENODEV; - if (new_src) { + if (new_src != old_src) { + if (new_src->instance_taken) + return -EBUSY; + spin_lock(&new_src->lock); spin_lock(&old_src->lock); @@ -755,10 +749,6 @@ static void dvct_free_func(struct usb_function *func) DVCT_IN(); d_fun->source_drv->deactivate(d_fun->source_dev); - spin_lock(&d_fun->source_dev->lock); - d_fun->source_dev->function_taken = 0; - spin_unlock(&d_fun->source_dev->lock); - dvct_free_desc(d_fun); kfree(d_fun); @@ -779,10 +769,6 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) d_fun->source_drv = dvct_source_get_drv(d_fun->source_dev); d_fun->trace_config = 0; - spin_lock(&d_fun->source_dev->lock); - d_fun->source_dev->function_taken = 1; - spin_unlock(&d_fun->source_dev->lock); - ret = d_fun->source_drv->activate(d_fun->source_dev, &d_fun->status); if (ret) { pr_err("Cannot activate source device %d\n", ret); From 98aa41d990ff7c23af63adb55839ba857a8467b0 Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Mon, 22 Aug 2016 16:56:06 +0200 Subject: [PATCH 0941/1276] staging/android: ABL Bootloader Control driver * Set reboot target in NVRAM - register to reboot event - create CDATA ABL code following reboot target * Set capsule parameters in NVRAM - accessible through /sys/kernel/capsule/capsule_name - same syntax as "nvram" ABL command - e.g. m1:capsule.bin - m => emmc - 1 => partition #1 - capsule.bin => file name * Notify on capsule request - accessible through /sys/kernel/capsule/capsule_requested - 0 : no capsule requested - 1 : capsule requested (triggered through /sys/kernel/capsule/capsule_name access) Signed-off-by: Guillaume Betous Signed-off-by: Tian, Baofeng --- arch/x86/configs/abl_diffconfig | 1 + drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/abl/Kconfig | 8 + drivers/staging/android/abl/Makefile | 1 + drivers/staging/android/abl/ablbc.c | 360 +++++++++++++++++++++++++++ 6 files changed, 372 insertions(+) create mode 100644 arch/x86/configs/abl_diffconfig create mode 100644 drivers/staging/android/abl/Kconfig create mode 100644 drivers/staging/android/abl/Makefile create mode 100644 drivers/staging/android/abl/ablbc.c diff --git a/arch/x86/configs/abl_diffconfig b/arch/x86/configs/abl_diffconfig new file mode 100644 index 000000000000..899be931801b --- /dev/null +++ b/arch/x86/configs/abl_diffconfig @@ -0,0 +1 @@ +CONFIG_ABL_BOOTLOADER_CONTROL=y diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 17c5587805f5..87054ad922aa 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -24,6 +24,7 @@ config ANDROID_VSOC a QEmu ivshmem device. If built as a module, it will be called vsoc. source "drivers/staging/android/ion/Kconfig" +source "drivers/staging/android/abl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 90e6154f11a4..729d11f4e150 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,6 +1,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ +obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig new file mode 100644 index 000000000000..a89ac5b5efb1 --- /dev/null +++ b/drivers/staging/android/abl/Kconfig @@ -0,0 +1,8 @@ +config ABL_BOOTLOADER_CONTROL + tristate "ABL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/abl/Makefile b/drivers/staging/android/abl/Makefile new file mode 100644 index 000000000000..b70a05a2af6d --- /dev/null +++ b/drivers/staging/android/abl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += ablbc.o diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c new file mode 100644 index 000000000000..a9f82d658247 --- /dev/null +++ b/drivers/staging/android/abl/ablbc.c @@ -0,0 +1,360 @@ +/* + * ablbc: control ABL bootloaders + * Copyright (c) 2013-2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "ablbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* ABL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) +#define USERCMD_UPDATE_IFWI(len) _USERCMD_(2, len) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +static bool capsule_request; + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_capsule_cmd { + char action; + char device; + char partition; + char file_name[1]; +} __packed; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram(nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram(nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + write_data_to_nvram(&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static struct kobject *capsule_kobject; + +static ssize_t is_capsule_requested(struct kobject *kobj, + struct kobj_attribute *attr, char *buf, size_t count) +{ + return sprintf(buf, "%d\n", capsule_request); +} + +enum capsule_device_type { + EMMC = 2, + SDCARD = 4 +}; + +static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, + char *buf, size_t count) +{ + struct nvram_msg msg; + struct nvram_capsule_cmd *capsule_cmd; + char name[32], partition; + enum capsule_device_type device; + int ret, crc, padding; + unsigned char size; + union _cdata_header cdh; + + device = (buf[0] == 'm' ? EMMC : SDCARD); + partition = buf[1] - '0'; + ret = sscanf(buf+3, "%s", name); + pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", + ret, device, partition, name); + + cdh.data = 0; + cdh.tag = CDATA_TAG_USER_CMD; + + /* padding of filename on next dword */ + padding = (4 - (3 + strlen(name))%4)%4; + size = 2 + sizeof(cdh) + 3 + strlen(name) + padding + 4; + cdh.length = 1 + (3 + strlen(name) + padding) / 4; + + msg.magic = NVRAM_VALID_FLAG; + msg.size = size; + msg.cdata_header.data = cdh.data; + + capsule_cmd = kmalloc(size, GFP_KERNEL); + if (!capsule_cmd) + return -ENOMEM; + + capsule_cmd->action = USERCMD_UPDATE_IFWI(strlen(name) + 2); + capsule_cmd->device = device; + capsule_cmd->partition = partition; + strncpy(capsule_cmd->file_name, name, strlen(name)); + msg.cdata_payload = capsule_cmd; + msg.cdata_payload_size = 3 + strlen(name) + padding; + msg.crc = crc32c_msg(&msg); + write_msg_to_nvram(&msg); + capsule_request = true; + + kfree(capsule_cmd); + + return count; +} + +static struct kobj_attribute capsule_name_attribute = + __ATTR(capsule_name, 0600, NULL, capsule_store); + +static struct kobj_attribute capsule_requested_attribute = + __ATTR(capsule_requested, 0400, is_capsule_requested, NULL); + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = &reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static const unsigned int DEFAULT_TARGET_INDEX; + +static int ablbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + + return NOTIFY_DONE; +} + +static struct notifier_block ablbc_reboot_notifier = { + .notifier_call = ablbc_reboot_notifier_call, +}; + +static int __init ablbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&ablbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + capsule_kobject = kobject_create_and_add("capsule", kernel_kobj); + if (!capsule_kobject) + return -ENOMEM; + + ret = sysfs_create_file(capsule_kobject, + &capsule_name_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_name\n"); + goto err; + } + + ret = sysfs_create_file(capsule_kobject, + &capsule_requested_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_requested\n"); + goto err; + } + + return 0; + +err: + kobject_put(capsule_kobject); + return ret; +} + +module_init(ablbc_init); + +static void __exit ablbc_exit(void) +{ + unregister_reboot_notifier(&ablbc_reboot_notifier); + kobject_put(capsule_kobject); +} +module_exit(ablbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Automotive Bootloader boot control driver"); +MODULE_LICENSE("GPL v2"); From 785232498b5e7642d15ad1e880c82cc1e31a6e57 Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Thu, 6 Oct 2016 12:51:07 +0200 Subject: [PATCH 0942/1276] ablbc: SLCAN commands on shutdown Moving shutdown logic to driver as we can't detect shutdown in userspace in ROS (no SIGKILL to all daemons, no logic in init.rc files) Signed-off-by: Guillaume Betous --- drivers/staging/android/abl/ablbc.c | 61 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index a9f82d658247..d02ac378fd70 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -18,6 +18,7 @@ #include #include #include +#include #define MODULE_NAME "ablbc" @@ -289,6 +290,48 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; +static const char * const cold_reset[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#05025555555555", + NULL}; +static const char * const cold_reset_capsule[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#05035555555555", + NULL}; +static const char * const suppress_heartbeat[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#01035555555555", + NULL}; +static const char * const reboot_request[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#03015555555555", + NULL}; + +static int execute_slcan_command(const char *cmd[]) +{ + struct subprocess_info *sub_info; + int ret = -1; + + sub_info = call_usermodehelper_setup(cmd[0], + cmd, NULL, GFP_KERNEL, + NULL, NULL, NULL); + + if (sub_info) { + ret = call_usermodehelper_exec(sub_info, + UMH_WAIT_PROC); + pr_info("Exec cmd=%s ret=%d\n", cmd[0], ret); + } + + if (!ret) + pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); + + return ret; +} + static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { @@ -298,12 +341,28 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (what != SYS_RESTART) return NOTIFY_DONE; - if (target[0] != '\0') + ret = execute_slcan_command(suppress_heartbeat); + if (ret) + goto done; + + ret = execute_slcan_command(reboot_request); + if (ret) + goto done; + if (target[0] != '\0') { ret = set_reboot_target(target); if (ret) pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); + else { + ret = execute_slcan_command(cold_reset); + if (ret) + goto done; + } + } + if (capsule_request) + ret = execute_slcan_command(cold_reset_capsule); +done: return NOTIFY_DONE; } From 145ea987fe9204f6d8b018ddc5c5d9e7f67a93e0 Mon Sep 17 00:00:00 2001 From: songjinguo Date: Fri, 28 Oct 2016 09:40:21 +0800 Subject: [PATCH 0943/1276] add crashmode for adb reboot crashmode function. Signed-off-by: songjinguo --- drivers/staging/android/abl/ablbc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index d02ac378fd70..ac9afbd36318 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -98,6 +98,7 @@ static const struct name2id NAME2ID[] = { { "fastboot", 0x01 }, { "elk", 0x02 }, { "recovery", 0x03 }, + { "crashmode", 0x04 }, { "cli", 0x10 }, }; From a1be56eee49e40999bddb40fe2cb9048f747372c Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Fri, 4 Nov 2016 15:48:19 +0800 Subject: [PATCH 0944/1276] ablbc: fix debug message error Signed-off-by: xihua.chen --- drivers/staging/android/abl/ablbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index ac9afbd36318..c7ed097b8ac1 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -327,7 +327,7 @@ static int execute_slcan_command(const char *cmd[]) pr_info("Exec cmd=%s ret=%d\n", cmd[0], ret); } - if (!ret) + if (ret) pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); return ret; From 4179245a7ef48f4e7cb2da1059cb4ed555d1a070 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 18 Aug 2017 06:56:56 +0800 Subject: [PATCH 0945/1276] iTCO_wdt: add module parameter "force_no_reboot" Setting "force_no_reboot" parameter to true (y/Y/1) will have the effect to prevent to reset the NO_REBOOT flag thus preventing the tco to reboot the platform, if not set or set to false, then system will reboot after about 30s. Signed-off-by: Tian, Baofeng --- drivers/watchdog/iTCO_wdt.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 347f0389b089..255318bb425c 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -131,6 +131,11 @@ module_param(turn_SMI_watchdog_clear_off, int, 0); MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, "Turn off SMI clearing watchdog (depends on TCO-version)(default=1)"); +static bool force_no_reboot; +module_param(force_no_reboot, bool, 0); +MODULE_PARM_DESC(force_no_reboot, + "Prevents the watchdog rebooting the platform (default=0)"); + /* * Some TCO specific functions */ @@ -243,6 +248,10 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; + /* force_no_reboot will prevent to unset NO_REBOOT bit */ + if (force_no_reboot) + return -EIO; + spin_lock(&p->io_lock); iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); @@ -250,7 +259,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) /* disable chipset's NO_REBOOT bit */ if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { spin_unlock(&p->io_lock); - pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); + pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS/rc_cmd\n"); return -EIO; } From 06ab9232b24fa2b355381e3084447b2354dcde07 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 30 Aug 2017 07:39:49 +0800 Subject: [PATCH 0946/1276] kernel: watchdog: add NMI handler for iTCO watchdog once this NMI triggered, it will dump all cpu backtraces and call panic to continue reboot. Signed-off-by: Tian, Baofeng --- drivers/watchdog/iTCO_wdt.c | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) mode change 100644 => 100755 drivers/watchdog/iTCO_wdt.c diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c old mode 100644 new mode 100755 index 255318bb425c..9a2a9b6b865c --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -68,6 +68,8 @@ #include /* For inb/outb/... */ #include +#include +#include #include "iTCO_vendor.h" /* Address definitions for the TCO */ @@ -86,6 +88,11 @@ #define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ #define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ +#define TCO_RLD_sub(base) (base + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCO1_STS_sub(base) (base + 0x04) /* TCO1 Status Register */ +#define TCOv2_TMR_sub(base) (base + 0x12) /* TCOv2 Timer Initial Value*/ + + /* internal variables */ struct iTCO_wdt_private { struct watchdog_device wddev; @@ -112,6 +119,14 @@ struct iTCO_wdt_private { int (*update_no_reboot_bit)(void *p, bool set); }; +static struct { + resource_size_t tco_base_address; + unsigned int iTCO_version; + bool pretimeout_occurred; + unsigned int second_to_ticks; +} iTCO_wdt_sub; + + /* module parameters */ #define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */ @@ -431,6 +446,33 @@ static const struct watchdog_ops iTCO_wdt_ops = { .get_timeleft = iTCO_wdt_get_timeleft, }; +static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) +{ + resource_size_t tco_base_address; + + /* Prevent re-entrance */ + if (iTCO_wdt_sub.pretimeout_occurred) + return NMI_HANDLED; + + tco_base_address = iTCO_wdt_sub.tco_base_address; + + /* Check the NMI is from the TCO first expiration */ + if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { + iTCO_wdt_sub.pretimeout_occurred = true; + + /* Forward next expiration */ + outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); + outw(0x01, TCO_RLD_sub(tco_base_address)); + + trigger_all_cpu_backtrace(); + panic_timeout = 0; + panic("Kernel Watchdog"); + return NMI_HANDLED; + } + + return NMI_DONE; +} + /* * Init & exit routines */ @@ -564,6 +606,17 @@ static int iTCO_wdt_probe(struct platform_device *pdev) return ret; } + /* init vars that used for nmi handler */ + iTCO_wdt_sub.iTCO_version = p->iTCO_version; + iTCO_wdt_sub.second_to_ticks = seconds_to_ticks(p, 10); + iTCO_wdt_sub.tco_base_address = TCOBASE(p); + + ret = register_nmi_handler(NMI_LOCAL, iTCO_pretimeout, 0 ,"iTCO_wdt"); + if (ret != 0) { + pr_err("cannot register nmi handler (err=%d)\n", ret); + return ret; + } + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); From cc1952c927d02868d07271589aca4c09ccd99307 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 26 Feb 2015 18:58:04 +0100 Subject: [PATCH 0947/1276] Add sven header to stm console Signed-off-by: Laurent FERT --- drivers/hwtracing/stm/console.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/console.c b/drivers/hwtracing/stm/console.c index a00f65e21747..a363467ac4a8 100644 --- a/drivers/hwtracing/stm/console.c +++ b/drivers/hwtracing/stm/console.c @@ -31,8 +31,22 @@ static void stm_console_write(struct console *con, const char *buf, unsigned len) { struct stm_console *sc = container_of(con, struct stm_console, console); + static char svenbuf[1024]; + char *p = svenbuf; + unsigned int towrite; + u16 textlen; + const u32 sven_header = 0x01000242; - stm_source_write(&sc->data, 0, buf, len); + textlen = min_t(u16, len, 1024 - sizeof(sven_header) - sizeof(textlen)); + towrite = textlen + sizeof(sven_header) + sizeof(textlen); + + memcpy(p, &sven_header, sizeof(sven_header)); + p += sizeof(sven_header); + memcpy(p, &textlen, sizeof(textlen)); + p += sizeof(textlen); + memcpy(p, buf, textlen); + + stm_source_write(&sc->data, 0, svenbuf, towrite); } static int stm_console_link(struct stm_source_data *data) From 4f89de14063e23b55cf8c8cd049b536b0ce2e2c8 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 27 Apr 2015 11:39:59 +0200 Subject: [PATCH 0948/1276] Do not reset trace hub on probe Initialize gth with current registers values when probing. Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/gth.c | 96 ++++++++++++++------------------ 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 8426b7970c14..2683f0e7f993 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -139,6 +139,24 @@ gth_master_set(struct gth_device *gth, unsigned int master, int port) iowrite32(val, gth->base + reg); } +static int gth_master_get(struct gth_device *gth, unsigned int master) +{ + unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u); + unsigned int shift = (master & 0x7) * 4; + u32 val; + + if (master >= 256) { + reg = REG_GTH_GSWTDEST; + shift = 0; + } + + val = ioread32(gth->base + reg); + val &= (0xf << shift); + val >>= shift; + + return val ? val & 0x7 : -1; +} + static ssize_t master_attr_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -183,13 +201,7 @@ static ssize_t master_attr_store(struct device *dev, if (old_port >= 0) { gth->master[ma->master] = -1; clear_bit(ma->master, gth->output[old_port].master); - - /* - * if the port is active, program this setting, - * implies that runtime PM is on - */ - if (gth->output[old_port].output->active) - gth_master_set(gth, ma->master, -1); + gth_master_set(gth, ma->master, -1); } /* connect to the new output port, if any */ @@ -201,10 +213,8 @@ static ssize_t master_attr_store(struct device *dev, } set_bit(ma->master, gth->output[port].master); - - /* if the port is active, program this setting, see above */ - if (gth->output[port].output->active) - gth_master_set(gth, ma->master, port); + gth_master_set(gth, ma->master, port); + gth->master[ma->master] = port; } gth->master[ma->master] = port; @@ -275,40 +285,21 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) /* * Reset outputs and sources */ -static int intel_th_gth_reset(struct gth_device *gth) +static void intel_th_gth_reset(struct gth_device *gth) { - u32 reg; - int port, i; - - reg = ioread32(gth->base + REG_GTH_SCRPD0); - if (reg & SCRPD_DEBUGGER_IN_USE) - return -EBUSY; + u32 scratchpad; /* Always save/restore STH and TU registers in S0ix entry/exit */ - reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(reg, gth->base + REG_GTH_SCRPD0); - - /* output ports */ - for (port = 0; port < 8; port++) { - if (gth_output_parm_get(gth, port, TH_OUTPUT_PARM(port)) == - GTH_NONE) - continue; + scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); + scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); - gth_output_set(gth, port, 0); - gth_smcfreq_set(gth, port, 16); - } /* disable overrides */ iowrite32(0, gth->base + REG_GTH_DESTOVR); - /* masters swdest_0~31 and gswdest */ - for (i = 0; i < 33; i++) - iowrite32(0, gth->base + REG_GTH_SWDEST0 + i * 4); - /* sources */ iowrite32(0, gth->base + REG_GTH_SCR); iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - return 0; } /* @@ -530,6 +521,8 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, int master; spin_lock(>h->gth_lock); + intel_th_gth_reset(gth); + for_each_set_bit(master, gth->output[output->port].master, TH_CONFIGURABLE_MASTERS + 1) { gth_master_set(gth, master, output->port); @@ -649,6 +642,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) struct resource *res; void __iomem *base; int i, ret; + u32 scratchpad; res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); if (!res) @@ -666,29 +660,23 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); - dev_set_drvdata(dev, gth); + dev_set_drvdata(dev, gth); - /* - * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE - * bit. Either way, don't reset HW in this case, and don't export any - * capture configuration attributes. Also, refuse to assign output - * drivers to ports, see intel_th_gth_assign(). - */ - if (thdev->host_mode) - return 0; + /* + * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE + * bit. Either way, don't reset HW in this case, and don't export any + * capture configuration attributes. Also, refuse to assign output + * drivers to ports, see intel_th_gth_assign(). + */ + if (thdev->host_mode) + return 0; - ret = intel_th_gth_reset(gth); - if (ret) { - if (ret != -EBUSY) - return ret; - - thdev->host_mode = true; - - return 0; - } + scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); + if (scratchpad & SCRPD_DEBUGGER_IN_USE) + return -EBUSY; for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) - gth->master[i] = -1; + gth->master[i] = gth_master_get(gth, i); for (i = 0; i < TH_POSSIBLE_OUTPUTS; i++) { gth->output[i].gth = gth; From 305c49b157adf5b7a0b87508c2c293c456b03489 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 2 Apr 2015 11:06:34 +0200 Subject: [PATCH 0949/1276] Add switch window control from sysfs Sequence to get the MSC to switch window is to * assert a trigger * de-assert storeEn * re-assert storeEn Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/core.c | 21 +++++ drivers/hwtracing/intel_th/gth.c | 122 +++++++++++++++++++++----- drivers/hwtracing/intel_th/gth.h | 7 ++ drivers/hwtracing/intel_th/intel_th.h | 9 +- drivers/hwtracing/intel_th/msu.c | 51 ++++++++--- 5 files changed, 176 insertions(+), 34 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index fc6b7f8b62fb..fb7c9a5144e4 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -923,6 +923,27 @@ int intel_th_trace_enable(struct intel_th_device *thdev) } EXPORT_SYMBOL_GPL(intel_th_trace_enable); +/** + * intel_th_trace_switch() - execute a switch sequence + * @thdev: output device that requests tracing switch + */ +int intel_th_trace_switch(struct intel_th_device *thdev) +{ + struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); + struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + + if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) + return -EINVAL; + + if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; + + hubdrv->trig_switch(hub, &thdev->output); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_th_trace_switch); + /** * intel_th_trace_disable() - disable tracing for an output device * @thdev: output device that requests tracing be disabled diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 2683f0e7f993..7d1c2e9cd6b2 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -300,6 +300,10 @@ static void intel_th_gth_reset(struct gth_device *gth) /* sources */ iowrite32(0, gth->base + REG_GTH_SCR); iowrite32(0xfc, gth->base + REG_GTH_SCR2); + + /* setup CTS for single trigger */ + iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); + iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); } /* @@ -447,6 +451,66 @@ static int intel_th_output_attributes(struct gth_device *gth) return sysfs_create_group(>h->dev->kobj, >h->output_group); } +/** + * intel_th_gth_stop() - stop tracing to an output device + * @gth: GTH device + * @output: output device's descriptor + * @capture_done: set when no more traces will be captured + * + * This will stop tracing using force storeEn off signal and wait for the + * pipelines to be empty for the corresponding output port. + */ +static void intel_th_gth_stop(struct gth_device *gth, + struct intel_th_output *output, + bool capture_done) +{ + struct intel_th_device *outdev = + container_of(output, struct intel_th_device, output); + unsigned long count; + u32 reg; + u32 scr2 = 0xfc | (capture_done ? 1 : 0); + + iowrite32(0, gth->base + REG_GTH_SCR); + iowrite32(scr2, gth->base + REG_GTH_SCR2); + + /* wait on pipeline empty for the given port */ + for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH; + count && !(reg & BIT(output->port)); count--) { + reg = ioread32(gth->base + REG_GTH_STAT); + cpu_relax(); + } + + if (!count) + dev_dbg(gth->dev, "timeout waiting for GTH[%d] PLE\n", + output->port); + + /* wait on output piepline empty */ + if (output->wait_empty) + output->wait_empty(outdev); + + /* clear force capture done for next captures */ + iowrite32(0xfc, gth->base + REG_GTH_SCR2); +} + +/** + * intel_th_gth_start() - start tracing to an output device + * @gth: GTH device + * @output: output device's descriptor + * + * This will start tracing using force storeEn signal. + */ +static void intel_th_gth_start(struct gth_device *gth, + struct intel_th_output *output) +{ + u32 scr = 0xfc0000; + + if (output->multiblock) + scr |= 0xff; + + iowrite32(scr, gth->base + REG_GTH_SCR); + iowrite32(0, gth->base + REG_GTH_SCR2); +} + /** * intel_th_gth_disable() - disable tracing to an output device * @thdev: GTH device @@ -460,7 +524,6 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - unsigned long count; int master; u32 reg; @@ -473,22 +536,7 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, } spin_unlock(>h->gth_lock); - iowrite32(0, gth->base + REG_GTH_SCR); - iowrite32(0xfd, gth->base + REG_GTH_SCR2); - - /* wait on pipeline empty for the given port */ - for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH; - count && !(reg & BIT(output->port)); count--) { - reg = ioread32(gth->base + REG_GTH_STAT); - cpu_relax(); - } - - /* clear force capture done for next captures */ - iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - if (!count) - dev_dbg(&thdev->dev, "timeout waiting for GTH[%d] PLE\n", - output->port); + intel_th_gth_stop(gth, output, true); reg = ioread32(gth->base + REG_GTH_SCRPD0); reg &= ~output->scratchpad; @@ -517,7 +565,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, { struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct intel_th *th = to_intel_th(thdev); - u32 scr = 0xfc0000, scrpd; + u32 scrpd; int master; spin_lock(>h->gth_lock); @@ -528,9 +576,6 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, gth_master_set(gth, master, output->port); } - if (output->multiblock) - scr |= 0xff; - output->active = true; spin_unlock(>h->gth_lock); @@ -541,8 +586,38 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); - iowrite32(scr, gth->base + REG_GTH_SCR); - iowrite32(0, gth->base + REG_GTH_SCR2); + intel_th_gth_start(gth, output); +} + +/** + * intel_th_gth_switch() - execute a switch sequence + * @thdev: GTH device + * @output: output device's descriptor + * + * This will execute a switch sequence that will trigger a switch window + * when tracing to MSC in multi-block mode. + */ +static void intel_th_gth_switch(struct intel_th_device *thdev, + struct intel_th_output *output) +{ + struct gth_device *gth = dev_get_drvdata(&thdev->dev); + unsigned long count; + u32 reg; + + /* trigger */ + iowrite32(0, gth->base + REG_CTS_CTL); + iowrite32(1, gth->base + REG_CTS_CTL); + /* wait on trigger status */ + for (reg = 0, count = CTS_TRIG_WAITLOOP_DEPTH; + count && !(reg & BIT(4)); count--) { + reg = ioread32(gth->base + REG_CTS_STAT); + cpu_relax(); + } + if (!count) + dev_dbg(&thdev->dev, "timeout waiting for CTS Trigger\n"); + + intel_th_gth_stop(gth, output, false); + intel_th_gth_start(gth, output); } /** @@ -719,6 +794,7 @@ static struct intel_th_driver intel_th_gth_driver = { .unassign = intel_th_gth_unassign, .set_output = intel_th_gth_set_output, .enable = intel_th_gth_enable, + .trig_switch = intel_th_gth_switch, .disable = intel_th_gth_disable, .driver = { .name = "gth", diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h index 6f2b0b930875..1f7d0d886320 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -49,6 +49,11 @@ enum { REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ + /* Common Capture Sequencer (CTS) registers */ + REG_CTS_C0S0_EN = 0x30c0, /* clause_event_enable_c0s0 */ + REG_CTS_C0S0_ACT = 0x3180, /* clause_action_control_c0s0 */ + REG_CTS_STAT = 0x32a0, /* cts_status */ + REG_CTS_CTL = 0x32a4, /* cts_control */ }; /* waiting for Pipeline Empty bit(s) to assert for GTH */ @@ -56,5 +61,7 @@ enum { #define TSUCTRL_CTCRESYNC BIT(0) #define TSCUSTAT_CTCSYNCING BIT(1) +/* waiting for Trigger status to assert for CTS */ +#define CTS_TRIG_WAITLOOP_DEPTH 10000 #endif /* __INTEL_TH_GTH_H__ */ diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 780206dc9012..4f55d53ac761 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -18,6 +18,8 @@ enum { INTEL_TH_SWITCH, }; +struct intel_th_device; + /** * struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices * @port: output port number, assigned by the switch @@ -25,6 +27,7 @@ enum { * @scratchpad: scratchpad bits to flag when this output is enabled * @multiblock: true for multiblock output configuration * @active: true when this output is enabled + * @wait_empty: wait for device pipeline to be empty * * Output port descriptor, used by switch driver to tell which output * port this output device corresponds to. Filled in at output device's @@ -37,6 +40,7 @@ struct intel_th_output { unsigned int scratchpad; bool multiblock; bool active; + void (*wait_empty)(struct intel_th_device *); }; /** @@ -157,6 +161,8 @@ struct intel_th_driver { struct intel_th_device *othdev); void (*enable)(struct intel_th_device *thdev, struct intel_th_output *output); + void (*trig_switch)(struct intel_th_device *thdev, + struct intel_th_output *output); void (*disable)(struct intel_th_device *thdev, struct intel_th_output *output); /* output ops */ @@ -220,6 +226,7 @@ int intel_th_driver_register(struct intel_th_driver *thdrv); void intel_th_driver_unregister(struct intel_th_driver *thdrv); int intel_th_trace_enable(struct intel_th_device *thdev); +int intel_th_trace_switch(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, unsigned int master); @@ -290,7 +297,7 @@ to_intel_th_hub(struct intel_th_device *thdev) enum { /* Global Trace Hub (GTH) */ REG_GTH_OFFSET = 0x0000, - REG_GTH_LENGTH = 0x2000, + REG_GTH_LENGTH = 0x4000, /* Timestamp counter unit (TSCU) */ REG_TSCU_OFFSET = 0x2000, diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index d293e55553bd..6eae13f02629 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -531,23 +531,14 @@ static int msc_configure(struct msc *msc) */ static void msc_disable(struct msc *msc) { - unsigned long count; u32 reg; lockdep_assert_held(&msc->buf_mutex); intel_th_trace_disable(msc->thdev); - for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH; - count && !(reg & MSCSTS_PLE); count--) { - reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); - cpu_relax(); - } - - if (!count) - dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); - if (msc->mode == MSC_MODE_SINGLE) { + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); msc->single_wrap = !!(reg & MSCSTS_WRAPSTAT); reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP); @@ -1250,6 +1241,22 @@ static const struct file_operations intel_th_msc_fops = { .owner = THIS_MODULE, }; +static void msc_wait_ple(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + unsigned long count; + u32 reg; + + for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH; + count && !(reg & MSCSTS_PLE); count--) { + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); + cpu_relax(); + } + + if (!count) + dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); +} + static int intel_th_msc_init(struct msc *msc) { atomic_set(&msc->user_count, -1); @@ -1263,6 +1270,8 @@ static int intel_th_msc_init(struct msc *msc) (ioread32(msc->reg_base + REG_MSU_MSC0CTL) & MSC_LEN) >> __ffs(MSC_LEN); + msc->thdev->output.wait_empty = msc_wait_ple; + return 0; } @@ -1439,10 +1448,32 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(nr_pages); +static ssize_t +win_switch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct msc *msc = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val != 1) + return -EINVAL; + + intel_th_trace_switch(msc->thdev); + return size; +} + +static DEVICE_ATTR_WO(win_switch); + static struct attribute *msc_output_attrs[] = { &dev_attr_wrap.attr, &dev_attr_mode.attr, &dev_attr_nr_pages.attr, + &dev_attr_win_switch.attr, NULL, }; From 3936e31b28bd0b8016264c2ae31648f4bafa6740 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 13 Aug 2015 19:31:41 +0200 Subject: [PATCH 0950/1276] intel_th: Reset before starting a capture Intel Trace Hub may be configured by firmwares or by a debugger before the driver is used. It is therefore difficult to start a capture from the OS with a well known state. Trigger a reset and re-apply all the configuration before starting a capture. Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/core.c | 93 ++++++++++---------- drivers/hwtracing/intel_th/gth.c | 120 ++++++++++++++------------ drivers/hwtracing/intel_th/intel_th.h | 22 +++-- drivers/hwtracing/intel_th/msu.c | 21 ++--- drivers/hwtracing/intel_th/pci.c | 31 ++++++- drivers/hwtracing/intel_th/pti.c | 2 - 6 files changed, 165 insertions(+), 124 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index fb7c9a5144e4..db28872bba34 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -215,48 +215,46 @@ static ssize_t port_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(port); -static int intel_th_output_activate(struct intel_th_device *thdev) +/** + * intel_th_output_activate() - call output initialization procedure + * @output: output to activate + */ +int intel_th_output_activate(struct intel_th_output *output) { - struct intel_th_driver *thdrv = - to_intel_th_driver_or_null(thdev->dev.driver); - struct intel_th *th = to_intel_th(thdev); - int ret = 0; - - if (!thdrv) - return -ENODEV; - - if (!try_module_get(thdrv->driver.owner)) - return -ENODEV; + struct intel_th_device *outdev = + container_of(output, struct intel_th_device, output); + struct intel_th_driver *outdrv = + to_intel_th_driver(outdev->dev.driver); - pm_runtime_get_sync(&thdev->dev); - - if (th->activate) - ret = th->activate(th); - if (ret) - goto fail_put; - - if (thdrv->activate) - ret = thdrv->activate(thdev); - else - intel_th_trace_enable(thdev); + if (WARN_ON_ONCE(outdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; - if (ret) - goto fail_deactivate; + if (outdrv->activate) + return outdrv->activate(outdev); return 0; +} +EXPORT_SYMBOL_GPL(intel_th_output_activate); -fail_deactivate: - if (th->deactivate) - th->deactivate(th); + * @thdev: output device that requests tracing + */ +static int intel_th_start_trace(struct intel_th_device *thdev) +{ + struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); + struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); -fail_put: - pm_runtime_put(&thdev->dev); - module_put(thdrv->driver.owner); + if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) + return -EINVAL; - return ret; + if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; + + /* The hub has control over Intel Trace Hub. + * Let the hub start a trace if possible and activate the output. */ + return hubdrv->enable(hub, &thdev->output); } -static void intel_th_output_deactivate(struct intel_th_device *thdev) +static void intel_th_stop_trace(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); @@ -298,9 +296,9 @@ static ssize_t active_store(struct device *dev, struct device_attribute *attr, if (!!val != thdev->output.active) { if (val) - ret = intel_th_output_activate(thdev); + ret = intel_th_start_trace(thdev); else - intel_th_output_deactivate(thdev); + intel_th_stop_trace(thdev); } return ret ? ret : size; @@ -369,6 +367,7 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, thdev->id = id; thdev->type = type; + thdev->th = th; strcpy(thdev->name, name); device_initialize(&thdev->dev); @@ -811,10 +810,11 @@ static const struct file_operations intel_th_output_fops = { * @devres: parent's resources * @ndevres: number of resources * @irq: irq number + * @reset: parent's reset function */ struct intel_th * intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, - struct resource *devres, unsigned int ndevres, int irq) + struct resource *devres, unsigned int ndevres, int irq, void (*reset)(struct intel_th *th)) { struct intel_th *th; int err, r; @@ -848,6 +848,7 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, th->resource = devres; th->num_resources = ndevres; th->irq = irq; + th->reset = reset; dev_set_drvdata(dev, th); @@ -902,26 +903,20 @@ void intel_th_free(struct intel_th *th) EXPORT_SYMBOL_GPL(intel_th_free); /** - * intel_th_trace_enable() - enable tracing for an output device - * @thdev: output device that requests tracing be enabled + * intel_th_reset() - reset hardware registers + * @hub: hub requesting the reset */ -int intel_th_trace_enable(struct intel_th_device *thdev) +void intel_th_reset(struct intel_th_device *hub) { - struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); - struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + struct intel_th *th = hub->th; if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) - return -EINVAL; - - if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) - return -EINVAL; - - pm_runtime_get_sync(&thdev->dev); - hubdrv->enable(hub, &thdev->output); + return; - return 0; + if (th->reset) + th->reset(th); } -EXPORT_SYMBOL_GPL(intel_th_trace_enable); +EXPORT_SYMBOL_GPL(intel_th_reset); /** * intel_th_trace_switch() - execute a switch sequence diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 7d1c2e9cd6b2..91d3ffc779b7 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -27,14 +27,16 @@ struct gth_device; * @output: link to output device's output descriptor * @index: output port number * @port_type: one of GTH_* port type values - * @master: bitmap of masters configured for this output + * @config: output configuration backup + * @smcfreq: maintenance packet frequency backup */ struct gth_output { struct gth_device *gth; struct intel_th_output *output; unsigned int index; unsigned int port_type; - DECLARE_BITMAP(master, TH_CONFIGURABLE_MASTERS + 1); + u32 config; + u32 smcfreq; }; /** @@ -65,6 +67,8 @@ static void gth_output_set(struct gth_device *gth, int port, u32 val; int shift = (port & 3) * 8; + gth->output[port].config = config; + val = ioread32(gth->base + reg); val &= ~(0xff << shift); val |= config << shift; @@ -91,6 +95,8 @@ static void gth_smcfreq_set(struct gth_device *gth, int port, int shift = (port & 1) * 16; u32 val; + gth->output[port].smcfreq = freq; + val = ioread32(gth->base + reg); val &= ~(0xffff << shift); val |= freq << shift; @@ -200,7 +206,6 @@ static ssize_t master_attr_store(struct device *dev, old_port = gth->master[ma->master]; if (old_port >= 0) { gth->master[ma->master] = -1; - clear_bit(ma->master, gth->output[old_port].master); gth_master_set(gth, ma->master, -1); } @@ -212,7 +217,6 @@ static ssize_t master_attr_store(struct device *dev, goto unlock; } - set_bit(ma->master, gth->output[port].master); gth_master_set(gth, ma->master, port); gth->master[ma->master] = port; } @@ -282,30 +286,6 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) return config; } -/* - * Reset outputs and sources - */ -static void intel_th_gth_reset(struct gth_device *gth) -{ - u32 scratchpad; - - /* Always save/restore STH and TU registers in S0ix entry/exit */ - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); - - /* disable overrides */ - iowrite32(0, gth->base + REG_GTH_DESTOVR); - - /* sources */ - iowrite32(0, gth->base + REG_GTH_SCR); - iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - /* setup CTS for single trigger */ - iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); - iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); -} - /* * "outputs" attribute group */ @@ -524,16 +504,16 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - int master; + int i; u32 reg; spin_lock(>h->gth_lock); output->active = false; - for_each_set_bit(master, gth->output[output->port].master, - TH_CONFIGURABLE_MASTERS) { - gth_master_set(gth, master, -1); - } + for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) + if (gth->master[i] == output->port) + gth_master_set(gth, i, -1); + spin_unlock(>h->gth_lock); intel_th_gth_stop(gth, output, true); @@ -543,13 +523,25 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, iowrite32(reg, gth->base + REG_GTH_SCRPD0); } -static void gth_tscu_resync(struct gth_device *gth) +/* + * Set default configuration. + */ +static void intel_th_gth_reset(struct gth_device *gth) { u32 reg; - reg = ioread32(gth->base + REG_TSCU_TSUCTRL); - reg &= ~TSUCTRL_CTCRESYNC; - iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); + /* Always save/restore STH and TU registers in S0ix entry/exit */ + reg = ioread32(gth->base + REG_GTH_SCRPD0); + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); + + /* Force sources off */ + iowrite32(0, gth->base + REG_GTH_SCR); + iowrite32(0xfc, gth->base + REG_GTH_SCR2); + + /* Setup CTS for single trigger */ + iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); + iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); } /** @@ -560,33 +552,59 @@ static void gth_tscu_resync(struct gth_device *gth) * This will configure all masters set to output to this device and * enable tracing using force storeEn signal. */ -static void intel_th_gth_enable(struct intel_th_device *thdev, - struct intel_th_output *output) +static int intel_th_gth_enable(struct intel_th_device *thdev, + struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct intel_th *th = to_intel_th(thdev); u32 scrpd; - int master; + int i; + int ret = -EBUSY; + + /* No operation allowed while a debugger is connected */ + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); + if (scrpd & SCRPD_DEBUGGER_IN_USE) + return ret; spin_lock(>h->gth_lock); - intel_th_gth_reset(gth); - for_each_set_bit(master, gth->output[output->port].master, - TH_CONFIGURABLE_MASTERS + 1) { - gth_master_set(gth, master, output->port); + /* Only allow one output active at a time */ + for (i = 0; i < TH_POSSIBLE_OUTPUTS; i++) { + if (gth->output[i].output && + gth->output[i].output->active) { + spin_unlock(>h->gth_lock); + return ret; + } } + intel_th_reset(thdev); + intel_th_gth_reset(gth); + + /* Re-configure output */ + gth_output_set(gth, output->port, gth->output[output->port].config); + gth_smcfreq_set(gth, output->port, gth->output[output->port].smcfreq); + + /* Enable masters for the output, disable others */ + for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) + gth_master_set(gth, i, gth->master[i] == output->port ? + output->port : -1); + output->active = true; spin_unlock(>h->gth_lock); - if (INTEL_TH_CAP(th, tscu_enable)) - gth_tscu_resync(gth); + /* Setup the output */ + ret = intel_th_output_activate(output); + if (ret) + return ret; scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); + /* Enable sources */ intel_th_gth_start(gth, output); + + return 0; } /** @@ -700,10 +718,9 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) master = TH_CONFIGURABLE_MASTERS; spin_lock(>h->gth_lock); - if (gth->master[master] == -1) { - set_bit(master, gth->output[port].master); + if (gth->master[master] == -1) gth->master[master] = port; - } + spin_unlock(>h->gth_lock); return 0; @@ -717,7 +734,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) struct resource *res; void __iomem *base; int i, ret; - u32 scratchpad; res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); if (!res) @@ -746,10 +762,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) if (thdev->host_mode) return 0; - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - if (scratchpad & SCRPD_DEBUGGER_IN_USE) - return -EBUSY; - for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) gth->master[i] = gth_master_get(gth, i); diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 4f55d53ac761..441fb9d85add 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -59,6 +59,7 @@ struct intel_th_drvdata { * struct intel_th_device - device on the intel_th bus * @dev: device * @drvdata: hardware capabilities/quirks + * @th: core device * @resource: array of resources available to this device * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} @@ -68,12 +69,13 @@ struct intel_th_drvdata { * @name: device name to match the driver */ struct intel_th_device { - struct device dev; + struct device dev; struct intel_th_drvdata *drvdata; - struct resource *resource; - unsigned int num_resources; - unsigned int type; - int id; + struct intel_th *th; + struct resource *resource; + unsigned int num_resources; + unsigned int type; + int id; /* INTEL_TH_SWITCH specific */ bool host_mode; @@ -159,7 +161,7 @@ struct intel_th_driver { struct intel_th_device *othdev); void (*unassign)(struct intel_th_device *thdev, struct intel_th_device *othdev); - void (*enable)(struct intel_th_device *thdev, + int (*enable)(struct intel_th_device *thdev, struct intel_th_output *output); void (*trig_switch)(struct intel_th_device *thdev, struct intel_th_output *output); @@ -219,13 +221,14 @@ static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) struct intel_th * intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, - struct resource *devres, unsigned int ndevres, int irq); + struct resource *devres, unsigned int ndevres, int irq, void (*reset)(struct intel_th *th)); void intel_th_free(struct intel_th *th); int intel_th_driver_register(struct intel_th_driver *thdrv); void intel_th_driver_unregister(struct intel_th_driver *thdrv); -int intel_th_trace_enable(struct intel_th_device *thdev); +int intel_th_output_activate(struct intel_th_output *output); +void intel_th_reset(struct intel_th_device *hub); int intel_th_trace_switch(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, @@ -253,6 +256,7 @@ enum { * @num_thdevs: number of devices in the @thdev array * @num_resources: number or resources in the @resource array * @irq: irq number + * @reset: reset function of the core device * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -270,6 +274,8 @@ struct intel_th { unsigned int num_resources; int irq; + void (*reset)(struct intel_th *th); + int id; int major; #ifdef CONFIG_MODULES diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 6eae13f02629..47d2f7d8419b 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -89,6 +89,7 @@ struct msc_iter { * @single_wrap: single mode wrap occurred * @base: buffer's base pointer * @base_addr: buffer's base address + * @nwsa: next window start address backup * @user_count: number of users of the buffer * @mmap_count: number of mappings * @buf_mutex: mutex to serialize access to buffer-related bits @@ -109,6 +110,7 @@ struct msc { unsigned int single_wrap : 1; void *base; dma_addr_t base_addr; + unsigned long nwsa; /* <0: no buffer, 0: no users, >0: active users */ atomic_t user_count; @@ -151,8 +153,6 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) static struct msc_window *msc_oldest_window(struct msc *msc) { struct msc_window *win; - u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); - unsigned long win_addr = (unsigned long)reg << PAGE_SHIFT; unsigned int found = 0; if (list_empty(&msc->win_list)) @@ -164,7 +164,7 @@ static struct msc_window *msc_oldest_window(struct msc *msc) * something like 2, in which case we're good */ list_for_each_entry(win, &msc->win_list, entry) { - if (win->block[0].addr == win_addr) + if (win->block[0].addr == msc->nwsa) found++; /* skip the empty ones */ @@ -478,9 +478,9 @@ static void msc_buffer_clear_hw_header(struct msc *msc) * msc_configure() - set up MSC hardware * @msc: the MSC device to configure * - * Program storage mode, wrapping, burst length and trace buffer address - * into a given MSC. Then, enable tracing and set msc::enabled. - * The latter is serialized on msc::buf_mutex, so make sure to hold it. + * Program all relevant registers for a given MSC. + * Programming registers must be delayed until this stage since the hardware + * will be reset before a capture is started. */ static int msc_configure(struct msc *msc) { @@ -515,10 +515,8 @@ static int msc_configure(struct msc *msc) iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI; - intel_th_trace_enable(msc->thdev); msc->enabled = 1; - return 0; } @@ -547,6 +545,10 @@ static void msc_disable(struct msc *msc) reg, msc->single_sz, msc->single_wrap); } + /* Save next window start address before disabling */ + reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + msc->nwsa = (unsigned long)reg << PAGE_SHIFT; + reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); reg &= ~MSC_EN; iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); @@ -555,8 +557,7 @@ static void msc_disable(struct msc *msc) iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR); iowrite32(0, msc->reg_base + REG_MSU_MSC0SIZE); - dev_dbg(msc_dev(msc), "MSCnNWSA: %08x\n", - ioread32(msc->reg_base + REG_MSU_MSC0NWSA)); + dev_dbg(msc_dev(msc), "MSCnNWSA: %08lx\n", msc->nwsa); reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); dev_dbg(msc_dev(msc), "MSCnSTS: %08x\n", reg); diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 1cf6290d6435..3a4f5733d24d 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -61,6 +61,35 @@ static void intel_th_pci_deactivate(struct intel_th *th) if (err) dev_err(&pdev->dev, "failed to read NPKDSC register\n"); } +/* + * PCI Configuration Registers + */ +enum { + REG_PCI_NPKDSC = 0x80, /* NPK Device Specific Control */ + REG_PCI_NPKDSD = 0x90, /* NPK Device Specific Defeature */ +}; + +/* Trace Hub software reset */ +#define NPKDSC_RESET BIT(1) + +/* Force On */ +#define NPKDSD_FON BIT(0) + +static void intel_th_pci_reset(struct intel_th *th) +{ + struct pci_dev *pdev = container_of(th->dev, struct pci_dev, dev); + u32 val; + + /* Software reset */ + pci_read_config_dword(pdev, REG_PCI_NPKDSC, &val); + val |= NPKDSC_RESET; + pci_write_config_dword(pdev, REG_PCI_NPKDSC, val); + + /* Always set FON for S0ix flow */ + pci_read_config_dword(pdev, REG_PCI_NPKDSD, &val); + val |= NPKDSD_FON; + pci_write_config_dword(pdev, REG_PCI_NPKDSD, val); +} static int intel_th_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -78,7 +107,7 @@ static int intel_th_pci_probe(struct pci_dev *pdev, return err; th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, - DEVICE_COUNT_RESOURCE, pdev->irq); + DEVICE_COUNT_RESOURCE, pdev->irq, intel_th_pci_reset); if (IS_ERR(th)) return PTR_ERR(th); diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 56694339cb06..9b6224b22a5f 100644 --- a/drivers/hwtracing/intel_th/pti.c +++ b/drivers/hwtracing/intel_th/pti.c @@ -161,8 +161,6 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) iowrite32(ctl, pti->base + REG_PTI_CTL); - intel_th_trace_enable(thdev); - return 0; } From f85fb4b3ecec58352eb214b30677858f2d557f01 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 26 Oct 2015 17:54:46 +0100 Subject: [PATCH 0951/1276] intel_th: Workaround PTI pipeline not empty When stopping a capture to PTI, the driver waits for PTI pipeline empty bit to be set. If this does not happen, the trace hub will be in an inconsistent state where it is not outputting any traces but has non-empty pipeline. Such state will end up in a global reset if the platform attempts to enter low power S0ix mode. For PTI only and when PTI pipeline is not empty at the end of a capture, do a reset of the trace hub. Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/gth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 91d3ffc779b7..e95301f91627 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -521,6 +521,11 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, reg = ioread32(gth->base + REG_GTH_SCRPD0); reg &= ~output->scratchpad; iowrite32(reg, gth->base + REG_GTH_SCRPD0); + + /* Workaround for PTI pipeline empty not set by hardware */ + if (output->type == GTH_PTI && + !(BIT(output->port) & ioread32(gth->base + REG_GTH_STAT))) + intel_th_reset(thdev); } /* From 3e1227e73c4df1913a99c4a6b7e8e1a89ca2ddf1 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Wed, 23 Mar 2016 09:19:49 +0100 Subject: [PATCH 0952/1276] intel_th: Implement suspend and resume Intel Trace Hub does not need to do anything when going to suspend. When going to low power or suspending, firmwares will take care of saving its current state, stopping the current trace and powering it down. When coming out of low power or suspend, firmwares will take care of powering it up restoring its state and resuming the current trace. The driver still needs to implement the suspend and resume calls so that the kernel does not disable memory access by setting BME=0 in its PCI config space before the firmwares do the proper shut down of the Trace Hub. Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/pci.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 3a4f5733d24d..460850b16d19 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -199,11 +199,34 @@ static const struct pci_device_id intel_th_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, intel_th_pci_id_table); +static int intel_th_suspend(struct device *dev) +{ + /* + * Stub the call to avoid disabling the device. + * Suspend is fully handled by firmwares. + */ + return 0; +} + +static int intel_th_resume(struct device *dev) +{ + /* Firmwares have already restored the device state. */ + return 0; +} + +static const struct dev_pm_ops intel_th_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(intel_th_suspend, + intel_th_resume) +}; + static struct pci_driver intel_th_pci_driver = { .name = DRIVER_NAME, .id_table = intel_th_pci_id_table, .probe = intel_th_pci_probe, .remove = intel_th_pci_remove, + .driver = { + .pm = &intel_th_pm_ops, + }, }; module_pci_driver(intel_th_pci_driver); From acb5f251fc0d573342d40eae45cfb11e6ab5f576 Mon Sep 17 00:00:00 2001 From: Yann Fouassier Date: Fri, 29 May 2015 11:12:35 +0200 Subject: [PATCH 0953/1276] intel_th: add earlyprintk support Enable trace hub earlyprintk with cmdline parameter. Specify the software trace hub bar address and channel: earlyprintk=intelth,0x:[,keep]. Signed-off-by: Yann Fouassier Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- arch/x86/include/asm/early_intel_th.h | 20 +++++ arch/x86/kernel/early_printk.c | 7 ++ drivers/hwtracing/intel_th/Kconfig | 11 +++ drivers/hwtracing/intel_th/Makefile | 3 + drivers/hwtracing/intel_th/early_printk.c | 98 +++++++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 arch/x86/include/asm/early_intel_th.h create mode 100644 drivers/hwtracing/intel_th/early_printk.c diff --git a/arch/x86/include/asm/early_intel_th.h b/arch/x86/include/asm/early_intel_th.h new file mode 100644 index 000000000000..bf93609995c8 --- /dev/null +++ b/arch/x86/include/asm/early_intel_th.h @@ -0,0 +1,20 @@ +/* + * early_intel_th.h: Intel Trace Hub early printk + * + * (C) Copyright 2015 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _ASM_X86_EARLY_INTEL_TH_H +#define _ASM_X86_EARLY_INTEL_TH_H + +#ifdef CONFIG_INTEL_TH_EARLY_PRINTK +extern struct console intel_th_early_console; +extern void early_intel_th_init(const char *); +#endif /* CONFIG_INTEL_TH_EARLY_PRINTK */ + +#endif /* _ASM_X86_EARLY_INTEL_TH_H */ + diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 5e801c8c8ce7..f65d32c7040c 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -22,6 +22,7 @@ #include #include #include +#include /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -387,6 +388,12 @@ static int __init setup_early_printk(char *buf) if (!strncmp(buf, "xdbc", 4)) early_xdbc_parse_parameter(buf + 4); #endif +#ifdef CONFIG_INTEL_TH_EARLY_PRINTK + if (!strncmp(buf, "intelth", 7)) { + early_intel_th_init(buf + 7); + early_console_register(&intel_th_early_console, keep); + } +#endif buf++; } diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index ca0527d588e9..4fdf936dc04b 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -82,4 +82,15 @@ config INTEL_TH_DEBUG help Say Y here to enable debugging. +config INTEL_TH_EARLY_PRINTK + bool "Intel TH early printk console" + depends on INTEL_TH=y + default n + ---help--- + Enables early printk console. + When the early printk console is enabled in the kernel + command line, kernel log messages are sent to Intel TH + (hence they are aggregated with the other trace messages + from the platform). + endif diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index d9252fa8d9ca..d72530116664 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile @@ -20,3 +20,6 @@ intel_th_msu-y := msu.o obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o intel_th_pti-y := pti.o + +obj-$(CONFIG_INTEL_TH_EARLY_PRINTK) += intel_th_early_printk.o +intel_th_early_printk-y := early_printk.o \ No newline at end of file diff --git a/drivers/hwtracing/intel_th/early_printk.c b/drivers/hwtracing/intel_th/early_printk.c new file mode 100644 index 000000000000..bbcb9f89161d --- /dev/null +++ b/drivers/hwtracing/intel_th/early_printk.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include "sth.h" + +static unsigned long sth_phys_addr; + +void early_intel_th_init(const char *s) +{ + size_t n; + unsigned long addr, chan; + char buf[32] = {0, }; + char *match, *next; + + /* Expect ,0x:[,keep] */ + if (*s == ',') + ++s; + if (strncmp(s, "0x", 2)) + goto fail; + + n = strcspn(s, ","); + if (n > sizeof(buf) - 1) + goto fail; + strncpy(buf, s, n); + next = buf; + + /* Get sw_bar */ + match = strsep(&next, ":"); + if (!match) + goto fail; + + if (kstrtoul(match, 16, &addr)) + goto fail; + + /* Get channel */ + if (kstrtoul(next, 0, &chan)) + goto fail; + + sth_phys_addr = addr + chan * sizeof(struct intel_th_channel); + return; + +fail: + pr_err("%s invalid parameter %s", __func__, s); +} + +static void intel_th_early_write(struct console *con, const char *buf, + unsigned len) +{ + struct intel_th_channel *channel; + const u8 *p = buf; + const u32 sven_header = 0x01000242; + + if (WARN_ON_ONCE(!sth_phys_addr)) + return; + + /* Software can send messages to Intel TH by writing to an MMIO space + * that is divided in several Master/Channel regions. + * Write directly to the address provided through the cmdline. + */ + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, sth_phys_addr); + channel = (struct intel_th_channel *) + (__fix_to_virt(FIX_EARLYCON_MEM_BASE) + + (sth_phys_addr & (PAGE_SIZE - 1))); + + /* Add hardcoded SVEN header + * type: DEBUG_STRING + * severity: SVEN_SEVERITY_NORMAL + * length: payload size + * subtype: SVEN_DEBUGSTR_Generic + */ + iowrite32(sven_header, &channel->DnTS); + iowrite16(len, &channel->Dn); + + while (len) { + if (len >= 4) { + iowrite32(*(u32 *)p, &channel->Dn); + p += 4; + len -= 4; + } else if (len >= 2) { + iowrite16(*(u16 *)p, &channel->Dn); + p += 2; + len -= 2; + } else { + iowrite8(*(u8 *)p, &channel->Dn); + p += 1; + len -= 1; + } + } + + iowrite32(0, &channel->FLAG); +} + +struct console intel_th_early_console = { + .name = "earlyintelth", + .write = intel_th_early_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; From b9416b7619ce9bd40d27c783e0cf35eb8eedab63 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Wed, 23 Dec 2015 15:56:11 +0100 Subject: [PATCH 0954/1276] intel_th: Support NPKT ACPI table in MSU When the bios is configured to trace to memory, it will * trace to MTB before memory is available * save MTB traces to a memory area when memory is available * trace to CSR in a reserved memory area In case of reboot, MTB and CSR buffers may be backed up by bios in recovery copies. The memory area for all those buffers is exposed to the kernel through NPKT ACPI table. Stop the current trace if the CSR capture is still in progress when the user either tries to read the CSR buffer or starts a new capture. Make the various buffers available to userspace through debugfs. Signed-off-by: Yann Fouassier Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/core.c | 29 +++ drivers/hwtracing/intel_th/intel_th.h | 1 + drivers/hwtracing/intel_th/msu.c | 319 +++++++++++++++++++++++--- 3 files changed, 319 insertions(+), 30 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index db28872bba34..5a5464944e80 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -236,12 +236,38 @@ int intel_th_output_activate(struct intel_th_output *output) } EXPORT_SYMBOL_GPL(intel_th_output_activate); +/** + * intel_th_first_trace() - notification callback for first trace + * + * Notify each child device that the first capture is about to begin. + * This gives a chance to save the current data as the Trace Hub may have + * already been configured by the BIOS to trace to a given output. + * + * @dev: output device to notify + * @data: private data - unused + */ +static int intel_th_first_trace(struct device *dev, void *data) +{ + struct intel_th_device *thdev = + container_of(dev, struct intel_th_device, dev); + struct intel_th_driver *thdrv = + to_intel_th_driver(thdev->dev.driver); + + if (thdrv && thdrv->first_trace) + thdrv->first_trace(thdev); + + return 0; +} + +/** + * intel_th_start_trace() - start tracing to an output device * @thdev: output device that requests tracing */ static int intel_th_start_trace(struct intel_th_device *thdev) { struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + static atomic_t first = { .counter = 1, }; if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) return -EINVAL; @@ -249,6 +275,9 @@ static int intel_th_start_trace(struct intel_th_device *thdev) if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) return -EINVAL; + if (atomic_dec_if_positive(&first) == 0) + device_for_each_child(&hub->dev, NULL, intel_th_first_trace); + /* The hub has control over Intel Trace Hub. * Let the hub start a trace if possible and activate the output. */ return hubdrv->enable(hub, &thdev->output); diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 441fb9d85add..7bbc75626f1d 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -154,6 +154,7 @@ intel_th_output_assigned(struct intel_th_device *thdev) */ struct intel_th_driver { struct device_driver driver; + void (*first_trace)(struct intel_th_device *thdev); int (*probe)(struct intel_th_device *thdev); void (*remove)(struct intel_th_device *thdev); /* switch (GTH) ops */ diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 47d2f7d8419b..6e1d359ed2d5 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -23,6 +23,9 @@ #include #endif +#include +#include + #include "intel_th.h" #include "msu.h" @@ -1053,16 +1056,19 @@ static int intel_th_msc_release(struct inode *inode, struct file *file) } static ssize_t -msc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len) +msc_single_to_user(void *in_buf, unsigned long in_pages, + unsigned long in_sz, bool wrapped, + char __user *buf, loff_t off, size_t len) { - unsigned long size = msc->nr_pages << PAGE_SHIFT, rem = len; + unsigned long size = in_pages << PAGE_SHIFT, rem = len; unsigned long start = off, tocopy = 0; - if (msc->single_wrap) { - start += msc->single_sz; + /* With wrapping, copy the end of the buffer first */ + if (wrapped) { + start += in_sz; if (start < size) { tocopy = min(rem, size - start); - if (copy_to_user(buf, msc->base + start, tocopy)) + if (copy_to_user(buf, in_buf + start, tocopy)) return -EFAULT; buf += tocopy; @@ -1071,21 +1077,17 @@ msc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len) } start &= size - 1; - if (rem) { - tocopy = min(rem, msc->single_sz - start); - if (copy_to_user(buf, msc->base + start, tocopy)) - return -EFAULT; - - rem -= tocopy; - } - - return len - rem; } + /* Copy the beginning of the buffer */ + if (rem) { + tocopy = min(rem, in_sz - start); + if (copy_to_user(buf, in_buf + start, tocopy)) + return -EFAULT; - if (copy_to_user(buf, msc->base + start, rem)) - return -EFAULT; + rem -= tocopy; + } - return len; + return len - rem; } static ssize_t intel_th_msc_read(struct file *file, char __user *buf, @@ -1115,8 +1117,10 @@ static ssize_t intel_th_msc_read(struct file *file, char __user *buf, len = size - off; if (msc->mode == MSC_MODE_SINGLE) { - ret = msc_single_to_user(msc, buf, off, len); - if (ret >= 0) + ret = msc_single_to_user(msc->base, msc->nr_pages, + msc->single_sz, msc->single_wrap, + buf, off, len); + if (ret > 0) *ppos += ret; } else if (msc->mode == MSC_MODE_MULTI) { struct msc_win_to_user_struct u = { @@ -1258,6 +1262,267 @@ static void msc_wait_ple(struct intel_th_device *thdev) dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); } +#ifdef CONFIG_ACPI +#define ACPI_SIG_NPKT "NPKT" + +/* Buffers that may be handed through NPKT ACPI table */ +enum NPKT_BUF_TYPE { + NPKT_MTB = 0, + NPKT_MTB_REC, + NPKT_CSR, + NPKT_CSR_REC, + NPKT_NBUF +}; +static const char * const npkt_buf_name[NPKT_NBUF] = { + [NPKT_MTB] = "mtb", + [NPKT_MTB_REC] = "mtb_rec", + [NPKT_CSR] = "csr", + [NPKT_CSR_REC] = "csr_rec" +}; + +/* CSR capture still active */ +#define NPKT_CSR_USED BIT(4) + +struct acpi_npkt_buf { + u64 addr; + u32 size; + u32 offset; +}; + +/* NPKT ACPI table */ +struct acpi_table_npkt { + struct acpi_table_header header; + struct acpi_npkt_buf buffers[NPKT_NBUF]; + u8 flags; +} __packed; + +/* Trace buffer obtained from NPKT table */ +struct npkt_buf { + dma_addr_t phy; + void *buf; + u32 size; + u32 offset; + bool wrapped; + atomic_t active; + struct msc *msc; +}; + +static struct npkt_buf *npkt_bufs; +static struct dentry *npkt_dump_dir; +static DEFINE_MUTEX(npkt_lock); + +/** + * Stop current trace if a buffer was marked with a capture in pogress. + * + * Update buffer write offset and wrap status after stopping the trace. + */ +static void stop_buffer_trace(struct npkt_buf *buf) +{ + u32 reg, mode; + struct msc *msc = buf->msc; + + mutex_lock(&npkt_lock); + if (!atomic_read(&buf->active)) + goto unlock; + + reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); + mode = (reg & MSC_MODE) >> __ffs(MSC_MODE); + if (!(reg & MSC_EN) || mode != MSC_MODE_SINGLE) { + /* Assume full buffer */ + pr_warn("NPKT reported CSR in use but not tracing to CSR\n"); + buf->offset = 0; + buf->wrapped = true; + atomic_set(&buf->active, 0); + goto unlock; + } + + /* The hub must be able to stop a capture not started by the driver */ + intel_th_trace_disable(msc->thdev); + + /* Update offset and wrap status */ + reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP); + buf->offset = reg - (u32)buf->phy; + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); + buf->wrapped = !!(reg & MSCSTS_WRAPSTAT); + atomic_set(&buf->active, 0); + +unlock: + mutex_unlock(&npkt_lock); +} + +/** + * Copy re-ordered data from an NPKT buffer to a user buffer. + */ +static ssize_t read_npkt_dump_buf(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct npkt_buf *buf = file->private_data; + size_t size = buf->size; + loff_t off = *ppos; + ssize_t ret; + + if (atomic_read(&buf->active)) + stop_buffer_trace(buf); + + if (off >= size) + return 0; + + ret = msc_single_to_user(buf->buf, size >> PAGE_SHIFT, + buf->offset, buf->wrapped, + user_buf, off, count); + if (ret > 0) + *ppos += ret; + + return ret; +} + +static const struct file_operations npkt_dump_buf_fops = { + .read = read_npkt_dump_buf, + .open = simple_open, + .llseek = noop_llseek, +}; + +/** + * Prepare a buffer with remapped address for a given NPKT buffer and add + * an entry for it in debugfs. + */ +static void npkt_bind_buffer(enum NPKT_BUF_TYPE type, + struct acpi_npkt_buf *abuf, u8 flags, + struct npkt_buf *buf, struct msc *msc) +{ + const char *name = npkt_buf_name[type]; + + /* No buffer handed through ACPI */ + if (!abuf->addr || !abuf->size) + return; + + /* Only expect multiples of page size */ + if (abuf->size & (PAGE_SIZE - 1)) { + pr_warn("invalid size 0x%x for buffer %s\n", + abuf->size, name); + return; + } + + buf->size = abuf->size; + buf->offset = abuf->offset; + buf->wrapped = !!(flags & BIT(type)); + /* CSR may still be active */ + if (type == NPKT_CSR && (flags & NPKT_CSR_USED)) { + atomic_set(&buf->active, 1); + buf->msc = msc; + } + + buf->phy = abuf->addr; + buf->buf = (__force void *)ioremap(buf->phy, buf->size); + if (!buf->buf) { + pr_err("ioremap failed for buffer %s 0x%llx size:0x%x\n", + name, buf->phy, buf->size); + return; + } + + debugfs_create_file(name, S_IRUGO, npkt_dump_dir, buf, + &npkt_dump_buf_fops); +} + +static void npkt_bind_buffers(struct acpi_table_npkt *npkt, + struct npkt_buf *bufs, struct msc *msc) +{ + int i; + + for (i = 0; i < NPKT_NBUF; i++) + npkt_bind_buffer(i, &npkt->buffers[i], npkt->flags, + &bufs[i], msc); +} + +static void npkt_unbind_buffers(struct npkt_buf *bufs) +{ + int i; + + for (i = 0; i < NPKT_NBUF; i++) + if (bufs[i].buf) + iounmap((__force void __iomem *)bufs[i].buf); +} + +/** + * Prepare debugfs access to NPKT buffers. + */ +static void intel_th_npkt_init(struct msc *msc) +{ + acpi_status status; + struct acpi_table_npkt *npkt; + + /* Associate NPKT to msc0 */ + if (npkt_bufs || msc->index != 0) + return; + + status = acpi_get_table(ACPI_SIG_NPKT, 0, + (struct acpi_table_header **)&npkt); + if (ACPI_FAILURE(status)) { + pr_warn("Failed to get NPKT table, %s\n", + acpi_format_exception(status)); + return; + } + + npkt_bufs = kzalloc(sizeof(struct npkt_buf) * NPKT_NBUF, GFP_KERNEL); + if (!npkt_bufs) + return; + + npkt_dump_dir = debugfs_create_dir("npkt_dump", NULL); + if (!npkt_dump_dir) { + pr_err("npkt_dump debugfs create dir failed\n"); + goto free_npkt_bufs; + } + + npkt_bind_buffers(npkt, npkt_bufs, msc); + + return; + +free_npkt_bufs: + kfree(npkt_bufs); + npkt_bufs = NULL; +} + +/** + * Remove debugfs access to NPKT buffers and release resources. + */ +static void intel_th_npkt_remove(struct msc *msc) +{ + /* Only clean for msc 0 if necessary */ + if (!npkt_bufs || msc->index != 0) + return; + + npkt_unbind_buffers(npkt_bufs); + debugfs_remove_recursive(npkt_dump_dir); + kfree(npkt_bufs); + npkt_bufs = NULL; +} + +/** + * First trace callback. + * + * If NPKT notified a CSR capture is in progress, stop it and update buffer + * write offset and wrap status. + */ +static void intel_th_msc_first_trace(struct intel_th_device *thdev) +{ + struct device *dev = &thdev->dev; + struct msc *msc = dev_get_drvdata(dev); + struct npkt_buf *buf; + + if (!npkt_bufs || msc->index != 0) + return; + + buf = &npkt_bufs[NPKT_CSR]; + if (atomic_read(&buf->active)) + stop_buffer_trace(buf); +} + +#else /* !CONFIG_ACPI */ +static inline void intel_th_npkt_init(struct msc *msc) {} +static inline void intel_th_npkt_remove(struct msc *msc) {} +#define intel_th_msc_first_trace NULL +#endif /* !CONFIG_ACPI */ + static int intel_th_msc_init(struct msc *msc) { atomic_set(&msc->user_count, -1); @@ -1513,26 +1778,20 @@ static int intel_th_msc_probe(struct intel_th_device *thdev) dev_set_drvdata(dev, msc); + intel_th_npkt_init(msc); + return 0; } static void intel_th_msc_remove(struct intel_th_device *thdev) { struct msc *msc = dev_get_drvdata(&thdev->dev); - int ret; - - intel_th_msc_deactivate(thdev); - - /* - * Buffers should not be used at this point except if the - * output character device is still open and the parent - * device gets detached from its bus, which is a FIXME. - */ - ret = msc_buffer_free_unless_used(msc); - WARN_ON_ONCE(ret); + intel_th_npkt_remove(msc); + sysfs_remove_group(&thdev->dev.kobj, &msc_output_group); } static struct intel_th_driver intel_th_msc_driver = { + .first_trace = intel_th_msc_first_trace, .probe = intel_th_msc_probe, .remove = intel_th_msc_remove, .activate = intel_th_msc_activate, From 09ba7c26929195a3f57382cda2ed0fd23de9e25f Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 6 Aug 2015 16:48:34 +0300 Subject: [PATCH 0955/1276] intel_th: New MSU kernel APIs The following new kernel APIs are added: Instance management: msc_register_callbacks(), msc_unregister_callbacks() Provide the means for an external module to be notified when msc sub-devices are added/removed. Configuration helpers: msc_max_blocks(), msc_block_max_size() Transfer: msc_switch_window(), msc_current_win_bytes(), msc_sg_oldest_win() Perform a window switch, get the state of the current window used and get the oldest data. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 286 +++++++++++++++++++++++++++++++ drivers/hwtracing/intel_th/msu.h | 17 ++ 2 files changed, 303 insertions(+) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 6e1d359ed2d5..fa149469d3b9 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -102,6 +102,8 @@ struct msc_iter { * @mode: MSC operating mode * @burst_len: write burst length * @index: number of this MSC in the MSU + * + * @max_blocks: Maximum number of blocks in a window */ struct msc { void __iomem *reg_base; @@ -129,8 +131,99 @@ struct msc { unsigned int mode; unsigned int burst_len; unsigned int index; + unsigned int max_blocks; }; +static struct msc_probe_rem_cb msc_probe_rem_cb; + +struct msc_device_instance { + struct list_head list; + struct intel_th_device *thdev; +}; + +static LIST_HEAD(msc_dev_instances); +static DEFINE_SPINLOCK(msc_dev_reg_lock); +/** + * msc_register_callbacks() + * @cbs + */ +int msc_register_callbacks(struct msc_probe_rem_cb cbs) +{ + struct msc_device_instance *it; + + spin_lock(&msc_dev_reg_lock); + + msc_probe_rem_cb.probe = cbs.probe; + msc_probe_rem_cb.remove = cbs.remove; + /* Call the probe callback for the already existing ones*/ + list_for_each_entry(it, &msc_dev_instances, list) { + cbs.probe(it->thdev); + } + + spin_unlock(&msc_dev_reg_lock); + return 0; +} +EXPORT_SYMBOL_GPL(msc_register_callbacks); + +/** + * msc_unregister_callbacks() + */ +void msc_unregister_callbacks(void) +{ + spin_lock(&msc_dev_reg_lock); + + msc_probe_rem_cb.probe = NULL; + msc_probe_rem_cb.remove = NULL; + + spin_unlock(&msc_dev_reg_lock); +} +EXPORT_SYMBOL_GPL(msc_unregister_callbacks); + +static void msc_add_instance(struct intel_th_device *thdev) +{ + struct msc_device_instance *instance; + + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) + return; + + spin_lock(&msc_dev_reg_lock); + + instance->thdev = thdev; + list_add(&instance->list, &msc_dev_instances); + + if (msc_probe_rem_cb.probe) + msc_probe_rem_cb.probe(thdev); + + spin_unlock(&msc_dev_reg_lock); +} + +static void msc_rm_instance(struct intel_th_device *thdev) +{ + struct msc_device_instance *instance = NULL, *it; + + spin_lock(&msc_dev_reg_lock); + + if (msc_probe_rem_cb.remove) + msc_probe_rem_cb.remove(thdev); + + list_for_each_entry(it, &msc_dev_instances, list) { + if (it->thdev == thdev) { + instance = it; + break; + } + } + + if (instance) { + list_del(&instance->list); + kfree(instance); + } else { + pr_warn("msu: cannot remove %p (not found)", thdev); + } + + spin_unlock(&msc_dev_reg_lock); +} + static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) { /* header hasn't been written */ @@ -144,6 +237,37 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) return false; } +/** + * msc_current_window() - locate the window in use + * @msc: MSC device + * + * This should only be used in multiblock mode. Caller should hold the + * msc::user_count reference. + * + * Return: the current output window + */ +static struct msc_window *msc_current_window(struct msc *msc) +{ + struct msc_window *win, *prev = NULL; + /*BAR is never changing, so the current one is the one before the next*/ + u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + unsigned long win_addr = (unsigned long)reg << PAGE_SHIFT; + + if (list_empty(&msc->win_list)) + return NULL; + + list_for_each_entry(win, &msc->win_list, entry) { + if (win->block[0].addr == win_addr) + break; + prev = win; + } + if (!prev) + prev = list_entry(msc->win_list.prev, struct msc_window, entry); + + return prev; +} + + /** * msc_oldest_window() - locate the window with oldest data * @msc: MSC device @@ -210,6 +334,160 @@ static unsigned int msc_win_oldest_block(struct msc_window *win) return 0; } +/** + * msc_max_blocks() - get the maximum number of block + * @thdev: the sub-device + * + * Return: the maximum number of blocks / window + */ +unsigned int msc_max_blocks(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + + return msc->max_blocks; +} +EXPORT_SYMBOL_GPL(msc_max_blocks); + +/** + * msc_block_max_size() - get the size of biggest block + * @thdev: the sub-device + * + * Return: the size of biggest block + */ +unsigned int msc_block_max_size(struct intel_th_device *thdev) +{ + return PAGE_SIZE; +} +EXPORT_SYMBOL_GPL(msc_block_max_size); + +/** + * msc_switch_window() - perform a window switch + * @thdev: the sub-device + */ +int msc_switch_window(struct intel_th_device *thdev) +{ + intel_th_trace_switch(thdev); + return 0; +} +EXPORT_SYMBOL_GPL(msc_switch_window); + +/** + * msc_current_win_bytes() - get the current window data size + * @thdev: the sub-device + * + * Get the number of valid data bytes in the current window. + * Based on this the dvc-source part can decide to request a window switch. + */ +int msc_current_win_bytes(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + struct msc_window *win; + u32 reg_mwp, blk, offset, i; + int size = 0; + + /* proceed only if actively storing in muli-window mode */ + if (!msc->enabled || + (msc->mode != MSC_MODE_MULTI) || + !atomic_inc_unless_negative(&msc->user_count)) + return -EINVAL; + + win = msc_current_window(msc); + reg_mwp = ioread32(msc->reg_base + REG_MSU_MSC0MWP); + + if (!win) { + atomic_dec(&msc->user_count); + return -EINVAL; + } + + blk = 0; + while (blk < win->nr_blocks) { + if (win->block[blk].addr == (reg_mwp & PAGE_MASK)) + break; + blk++; + } + + if (blk >= win->nr_blocks) { + atomic_dec(&msc->user_count); + return -EINVAL; + } + + offset = (reg_mwp & (PAGE_SIZE - 1)); + + + /*if wrap*/ + if (msc_block_wrapped(win->block[blk].bdesc)) { + for (i = blk+1; i < win->nr_blocks; i++) + size += msc_data_sz(win->block[i].bdesc); + } + + for (i = 0; i < blk; i++) + size += msc_data_sz(win->block[i].bdesc); + + /*finaly the current one*/ + size += (offset - MSC_BDESC); + + atomic_dec(&msc->user_count); + return size; +} +EXPORT_SYMBOL_GPL(msc_current_win_bytes); + +/** + * msc_sg_oldest_win() - get the data from the oldest window + * @thdev: the sub-device + * @sg_array: destination sg array + * + * Return: sg count + */ +int msc_sg_oldest_win(struct intel_th_device *thdev, + struct scatterlist *sg_array) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + struct msc_window *win, *c_win; + struct msc_block_desc *bdesc; + unsigned int blk, sg = 0; + + /* proceed only if actively storing in muli-window mode */ + if (!msc->enabled || + (msc->mode != MSC_MODE_MULTI) || + !atomic_inc_unless_negative(&msc->user_count)) + return -EINVAL; + + win = msc_oldest_window(msc); + if (!win) + return 0; + + c_win = msc_current_window(msc); + + if (win == c_win) + return 0; + + blk = msc_win_oldest_block(win); + + /* start with the first block containing only oldest data */ + if (msc_block_wrapped(win->block[blk].bdesc)) + if (++blk == win->nr_blocks) + blk = 0; + + do { + bdesc = win->block[blk].bdesc; + sg_set_buf(&sg_array[sg++], bdesc, PAGE_SIZE); + + if (bdesc->hw_tag & MSC_HW_TAG_ENDBIT) + break; + + if (++blk == win->nr_blocks) + blk = 0; + + } while (sg <= win->nr_blocks); + + sg_mark_end(&sg_array[sg - 1]); + + atomic_dec(&msc->user_count); + + return sg; +} +EXPORT_SYMBOL_GPL(msc_sg_oldest_win); + /** * msc_is_last_win() - check if a window is the last one for a given MSC * @win: window @@ -1661,6 +1939,8 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; + msc->max_blocks = 0; + /* scan the comma-separated list of allocation sizes */ end = memchr(buf, '\n', len); if (end) @@ -1695,6 +1975,9 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, win = rewin; win[nr_wins - 1] = val; + msc->max_blocks = + (val > msc->max_blocks) ? val : msc->max_blocks; + if (!end) break; @@ -1776,9 +2059,11 @@ static int intel_th_msc_probe(struct intel_th_device *thdev) if (err) return err; + msc->max_blocks = 0; dev_set_drvdata(dev, msc); intel_th_npkt_init(msc); + msc_add_instance(thdev); return 0; } @@ -1787,6 +2072,7 @@ static void intel_th_msc_remove(struct intel_th_device *thdev) { struct msc *msc = dev_get_drvdata(&thdev->dev); intel_th_npkt_remove(msc); + msc_rm_instance(thdev); sysfs_remove_group(&thdev->dev.kobj, &msc_output_group); } diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h index 9cc8aced6116..ccfed662a726 100644 --- a/drivers/hwtracing/intel_th/msu.h +++ b/drivers/hwtracing/intel_th/msu.h @@ -8,6 +8,8 @@ #ifndef __INTEL_TH_MSU_H__ #define __INTEL_TH_MSU_H__ +#include "intel_th.h" + enum { REG_MSU_MSUPARAMS = 0x0000, REG_MSU_MSUSTS = 0x0008, @@ -105,4 +107,19 @@ static inline bool msc_block_last_written(struct msc_block_desc *bdesc) /* waiting for Pipeline Empty bit(s) to assert for MSC */ #define MSC_PLE_WAITLOOP_DEPTH 10000 +/* API */ +struct msc_probe_rem_cb { + void (*probe)(struct intel_th_device *thdev); + void (*remove)(struct intel_th_device *thdev); +}; + +int msc_register_callbacks(struct msc_probe_rem_cb cbs); +void msc_unregister_callbacks(void); +unsigned int msc_max_blocks(struct intel_th_device *thdev); +unsigned int msc_block_max_size(struct intel_th_device *thdev); +int msc_switch_window(struct intel_th_device *thdev); +int msc_sg_oldest_win(struct intel_th_device *thdev, + struct scatterlist *sg_array); +int msc_current_win_bytes(struct intel_th_device *thdev); + #endif /* __INTEL_TH_MSU_H__ */ From 8da1e3cc8c4c4541523e7c28905ef3e8ed35c626 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 6 Aug 2015 16:48:47 +0300 Subject: [PATCH 0956/1276] intel_th: implement dvc trace source for intel_th Add support for sending Intel Trace Hub data over USB, using DvC-Trace specification. Signed-off-by: Traian Schiau --- .../testing/sysfs-bus-dvctrace-devices-dvcith | 68 ++ MAINTAINERS | 6 + drivers/hwtracing/intel_th/Kconfig | 20 + drivers/hwtracing/intel_th/Makefile | 6 +- drivers/hwtracing/intel_th/msu-dvc.c | 1084 +++++++++++++++++ 5 files changed, 1183 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith create mode 100644 drivers/hwtracing/intel_th/msu-dvc.c diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith b/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith new file mode 100644 index 000000000000..0dda3aefb89c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith @@ -0,0 +1,68 @@ +What: /sys/bus/dvctrace/devices/dvcith-/msc +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) Symbolic link to the Intel Trace Hub MSC + (Memory Storage Controller) sub-device used to get tracing data. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_min_transfer +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Window transfer watermark, the driver will queue a + new transfer only if at least bytes + of trace data is available. Since on every switch @48 bytes + of trace data is generated, this should not be set under this + threshold. + Default 2048 + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_retry_timeout +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Read retry interval, If by the time the last usb transfer + is complete, there is no new data to be sent the driver will + sleep ms, before checking again. + Default: 2 ms + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_max_retry +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) the maximum retries to be bone before triggering a switch + and sending the currently available data regardless of the + available size. + Default: 150 + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_proc_type +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Data process type, during DvC tracing the MSC is set up in + Multi Window mode (check Intel Trace Hub Developer's Manual for + details), This attribute specifies what the dvc-trace data stream + should contain. + Available values are: + - 1 - Full blocks, + - 2 - Trimmed blocks (Block header + STP data) + - 3 - STP data only. + Default 3. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_transfer_type +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Data transfer type, This attribute specifies how the trace data + is queued in the USB requests. + Available values are: + - 1 - Auto, + - 2 - SG-List, + - 3 - Linear buffer. + Default 1. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_stats +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) Provides statistical information regarding the latest. + trace session. Available if (CONFIG_INTEL_TH_MSU_DVC_DEBUG). diff --git a/MAINTAINERS b/MAINTAINERS index 6bc3e0be7f66..846ff260d914 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7608,6 +7608,12 @@ S: Supported F: Documentation/trace/intel_th.rst F: drivers/hwtracing/intel_th/ +INTEL(R) TRACE HUB TO USB-DVC.TRACE +M: Traian Schiau +S: Supported +F: drivers/hwtracing/intel_th/msu-dvc.c +F: Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith + INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT) M: Ning Sun L: tboot-devel@lists.sourceforge.net diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index 4fdf936dc04b..027edb7da72c 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -67,6 +67,26 @@ config INTEL_TH_MSU Say Y here to enable MSU output device for Intel TH. +config INTEL_TH_MSU_DVC + tristate "Intel Trace Hub Memory Storage Unit to USB-dvc" + help + Memory Storage Unit (MSU) trace output device enables + storing STP traces to system memory. + This provides the means to route this data over USB, + using DvC-Trace. + + Say Y here to enable DvC-Trace output device for Intel TH. + +config INTEL_TH_MSU_DVC_DEBUG + tristate "Intel Trace Hub Memory Storage Unit to USB-dvc debug" + help + Memory Storage Unit (MSU) trace output device enables + storing STP traces to system memory. + This enables extensive logging and collection of + statistical data on MSU/DvC-Trace device performance. + + Say Y to enable extended debug features on MSU-DvC. + config INTEL_TH_PTI tristate "Intel(R) Trace Hub PTI output" help diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index d72530116664..6d3dc7de0bb3 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile @@ -18,8 +18,12 @@ intel_th_sth-y := sth.o obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o intel_th_msu-y := msu.o +obj-$(CONFIG_INTEL_TH_MSU_DVC) += intel_th_msu_dvc.o +intel_th_msu_dvc-y := msu-dvc.o +subdir-ccflags-$(CONFIG_INTEL_TH_MSU_DVC_DEBUG) += -DMDD_DEBUG + obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o intel_th_pti-y := pti.o obj-$(CONFIG_INTEL_TH_EARLY_PRINTK) += intel_th_early_printk.o -intel_th_early_printk-y := early_printk.o \ No newline at end of file +intel_th_early_printk-y := early_printk.o diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c new file mode 100644 index 000000000000..475f4cfe51ba --- /dev/null +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -0,0 +1,1084 @@ +/* + * Intel Trace Hub to USB dvc-trace driver + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msu.h" + +#ifdef MDD_DEBUG +#define MDD_F_DEBUG() pr_debug("\n") +#else +#define MDD_F_DEBUG() do {} while (0) +#endif + +#define DTC_DRV_NAME "dvcith" + +#define MDD_MIN_TRANSFER_DEF 2048 +#define MDD_RETRY_TIMEOUT_DEF 2 +#define MDD_MAX_RETRY_CNT_DEF 150 + +#define mdd_err(mdd, ...) dev_err(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_warn(mdd, ...) dev_warn(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_info(mdd, ...) dev_info(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_debug(mdd, ...) dev_debug(&(mdd)->ddev.device, ## __VA_ARGS__) + +#ifdef MDD_DEBUG +struct msu_dvc_stats { + unsigned long work_start; + unsigned long work_end; + + unsigned long loop_count; + unsigned long hits; + + u64 full_block_size; + u64 valid_block_size; + u64 valid_data_size; + + u32 transfer_type:2; + u32 process_type:2; + + enum usb_device_speed speed; +}; +#endif + +enum { + MDD_TRANSFER_NO_CHANGE, + MDD_TRANSFER_AUTO, + MDD_TRANSFER_MIN = MDD_TRANSFER_AUTO, + MDD_TRANSFER_SINGLE, + MDD_TRANSFER_MULTI, + MDD_TRANSFER_MAX = MDD_TRANSFER_MULTI, +}; + +static const char *const transfer_type_name[] = { + [MDD_TRANSFER_AUTO] = "Auto", + [MDD_TRANSFER_SINGLE] = "Single", + [MDD_TRANSFER_MULTI] = "Multi", +}; + +enum { + MDD_PROC_NO_CHANGE, + MDD_PROC_NONE, + MDD_PROC_MIN = MDD_PROC_NONE, + MDD_PROC_REM_TAIL, + MDD_PROC_REM_ALL, + MDD_PROC_MAX = MDD_PROC_REM_ALL, +}; + +static const char *const process_type_name[] = { + [MDD_PROC_NONE] = "Full-Blocks", + [MDD_PROC_REM_TAIL] = "Trimmed-Blocks", + [MDD_PROC_REM_ALL] = "STP", +}; + +struct mdd_transfer_data { + u8 *buffer; + dma_addr_t buffer_dma; + size_t buffer_len; + struct scatterlist *sg_raw; + struct scatterlist *sg_proc; + struct scatterlist *sg_trans; /* not be allocated */ + size_t block_count; + size_t block_size; + spinlock_t lock; +}; + +#define mdd_lock_transfer(mdd) spin_lock(&mdd->tdata.lock) +#define mdd_unlock_transfer(mdd) spin_unlock(&mdd->tdata.lock) + +struct msu_dvc_dev { + struct dvct_source_device ddev; + atomic_t *dtc_status; + struct usb_ep *ep; + struct usb_function *func; + enum usb_device_speed speed; + struct intel_th_device *th_dev; + + struct workqueue_struct *wrq; + struct work_struct work; + struct usb_request *req; + wait_queue_head_t wq; + atomic_t req_ongoing; + + /*attributes */ + u32 retry_timeout; + u32 max_retry_count; + u32 transfer_type:2; + u32 process_type:2; + u32 min_transfer; + +#ifdef MDD_DEBUG + struct msu_dvc_stats stats; +#endif + struct mdd_transfer_data tdata; + + struct list_head mdd_list; +}; + +static LIST_HEAD(mdd_devs); +static DEFINE_SPINLOCK(mdd_devs_lock); + +static inline struct usb_gadget *mdd_gadget(struct msu_dvc_dev *mdd) +{ + BUG_ON(!mdd->func); + BUG_ON(!mdd->func->config); + BUG_ON(!mdd->func->config->cdev); + BUG_ON(!mdd->func->config->cdev->gadget); + return mdd->func->config->cdev->gadget; +} + +/* Back-cast to msu_dvc_dev */ +static inline struct msu_dvc_dev *dtc_to_mdd(struct dvct_source_device *p_dtc) +{ + return container_of(p_dtc, struct msu_dvc_dev, ddev); +} + +static ssize_t mdd_min_transfer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->min_transfer); +} + +static ssize_t mdd_min_transfer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + /* 48 represents the size of sync frames that are generated by + * a window switch, from this point on we have "real data" + * Going under this value could result in unneeded switching */ + if (!kstrtou32(buf, 10, &mdd->min_transfer)) { + if (mdd->min_transfer < 48) + mdd->min_transfer = 48; + return count; + } + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_min_transfer); + + +static ssize_t mdd_retry_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->retry_timeout); +} + +static ssize_t mdd_retry_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + if (!kstrtou32(buf, 10, &mdd->retry_timeout)) + return count; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_retry_timeout); + +static ssize_t mdd_max_retry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->max_retry_count); +} + +static ssize_t mdd_max_retry_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + if (!kstrtou32(buf, 10, &mdd->max_retry_count)) + return count; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_max_retry); + +static ssize_t mdd_transfer_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%d %s\n", mdd->transfer_type, + transfer_type_name[mdd->transfer_type]); +} + +static ssize_t mdd_transfer_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + u8 tmp; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + if (!kstrtou8(buf, 10, &tmp) && tmp <= MDD_TRANSFER_MAX + && tmp >= MDD_TRANSFER_MIN) { + mdd->transfer_type = tmp; + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_transfer_type); + +static ssize_t mdd_proc_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%d %s\n", mdd->process_type, + process_type_name[mdd->process_type]); +} + +static ssize_t mdd_proc_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + u8 tmp; + + MDD_F_DEBUG(); + + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + if (!kstrtou8(buf, 10, &tmp) && tmp <= MDD_PROC_MAX + && tmp >= MDD_PROC_MIN) { + mdd->process_type = tmp; + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_proc_type); + +#ifdef MDD_DEBUG + +static ssize_t mdd_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + int len = 0; + + static const char *const u_speed_names[] = { + [USB_SPEED_UNKNOWN] = "?", + [USB_SPEED_LOW] = "LS", + [USB_SPEED_FULL] = "FS", + [USB_SPEED_HIGH] = "HS", + [USB_SPEED_WIRELESS] = "WR", + [USB_SPEED_SUPER] = "SS", + }; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + len += snprintf(buf + len, PAGE_SIZE - len, "R.count\tR.hits\t"); + + len += snprintf(buf + len, PAGE_SIZE - len, "T.tot_j\tT.tot_ms\t"); + len += + snprintf(buf + len, PAGE_SIZE - len, "D.total\tD.block\tT.stp\t"); + len += snprintf(buf + len, PAGE_SIZE - len, "Tr.type\tProc.type\t"); + + len += snprintf(buf + len, PAGE_SIZE - len, "USB.speed\n"); + + /* Actual values starts here */ + len += + snprintf(buf + len, PAGE_SIZE - len, "%lu\t%lu\t", + mdd->stats.loop_count, mdd->stats.hits); + + len += + snprintf(buf + len, PAGE_SIZE - len, "%lu\t%u\t", + (mdd->stats.work_end - mdd->stats.work_start), + jiffies_to_msecs(mdd->stats.work_end - + mdd->stats.work_start)); + len += + snprintf(buf + len, PAGE_SIZE - len, "%llu\t%llu\t%llu\t", + mdd->stats.full_block_size, mdd->stats.valid_block_size, + mdd->stats.valid_data_size); + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\t", + transfer_type_name[mdd->transfer_type]); + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\t", + process_type_name[mdd->process_type]); + + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\n", + u_speed_names[mdd->stats.speed]); + + return len; +} + +static DEVICE_ATTR_RO(mdd_stats); + +static void init_stats_start(struct msu_dvc_dev *mdd) +{ + mdd->stats.loop_count = 0; + mdd->stats.hits = 0; + + mdd->stats.work_start = jiffies; + + mdd->stats.full_block_size = 0; + mdd->stats.valid_block_size = 0; + mdd->stats.valid_data_size = 0; + + mdd->stats.process_type = mdd->process_type; + mdd->stats.transfer_type = mdd->transfer_type; + mdd->stats.speed = mdd->speed; +} + +#define stats_loop(mdd) ((mdd)->stats.loop_count++) +#define stats_hit(mdd) ((mdd)->stats.hits++) +#else +#define init_stats_start(n) do {} while (0) +#define stats_loop(mdd) do {} while (0) +#define stats_hit(mdd) do {} while (0) +#endif + +static void mdd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msu_dvc_dev *mdd = (struct msu_dvc_dev *)req->context; + + mdd_lock_transfer(mdd); + + if (req->status != 0) { + mdd_err(mdd, "Usb request error %d\n", req->status); + dvct_clr_status(mdd->dtc_status, DVCT_MASK_TRANS); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + } + atomic_set(&mdd->req_ongoing, 0); + wake_up(&mdd->wq); + mdd_unlock_transfer(mdd); +} + +static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) +{ + int ret = -EINVAL; + + MDD_F_DEBUG(); + + if (!mdd->ep || !mdd->req) { + mdd_err(mdd, "Invalid endpoint data\n"); + goto err; + } + + mdd->tdata.block_count = msc_max_blocks(mdd->th_dev); + if (mdd->tdata.block_count <= 0) { + mdd_err(mdd, "Invalid block count %zu\n", + mdd->tdata.block_count); + goto err; + } + + mdd->tdata.block_size = msc_block_max_size(mdd->th_dev); + if (mdd->tdata.block_size <= 0) { + mdd_err(mdd, "Invalid block size %zu\n", mdd->tdata.block_size); + goto err; + } + + mdd->tdata.sg_raw = kmalloc_array(mdd->tdata.block_count, + sizeof(*mdd->tdata.sg_raw), + GFP_KERNEL); + if (!mdd->tdata.sg_raw) { + mdd_err(mdd, "Cannot allocate sg memory %zu\n", + mdd->tdata.block_size); + goto err_sg_raw; + } + + if (mdd->process_type != MDD_PROC_NONE) { + mdd->tdata.sg_proc = kmalloc_array(mdd->tdata.block_count, + sizeof(*mdd->tdata.sg_proc), + GFP_KERNEL); + if (!mdd->tdata.sg_proc) { + mdd_err(mdd, "Cannot allocate sg memory %zu\n", + mdd->tdata.block_size); + goto err_sg_proc; + } + mdd->tdata.sg_trans = mdd->tdata.sg_proc; + } else { + mdd->tdata.sg_trans = mdd->tdata.sg_raw; + } + + if (mdd->transfer_type == MDD_TRANSFER_SINGLE) { + mdd->tdata.buffer_len = + mdd->tdata.block_count * mdd->tdata.block_size; + mdd->tdata.buffer = + dma_alloc_coherent(&(mdd_gadget(mdd)->dev), + mdd->tdata.buffer_len, + &mdd->tdata.buffer_dma, GFP_KERNEL); + if (!mdd->tdata.buffer) { + mdd_err(mdd, "Cannot allocate DMA memory\n"); + goto err_l_buf; + } + } else { + mdd->tdata.buffer = NULL; + mdd->tdata.buffer_dma = 0; + mdd->tdata.buffer_len = 0; + } + return 0; +err_l_buf: + kfree(mdd->tdata.sg_proc); + mdd->tdata.sg_proc = NULL; +err_sg_proc: + kfree(mdd->tdata.sg_raw); + mdd->tdata.sg_raw = NULL; +err_sg_raw: + ret = -ENOMEM; +err: + return ret; +} + +static void mdd_reset_transfer_data(struct msu_dvc_dev *mdd) +{ + MDD_F_DEBUG(); + kfree(mdd->tdata.sg_proc); + mdd->tdata.sg_proc = NULL; + kfree(mdd->tdata.sg_raw); + mdd->tdata.sg_raw = NULL; + if (mdd->tdata.buffer) { + dma_free_coherent(&(mdd_gadget(mdd)->dev), + mdd->tdata.buffer_len, mdd->tdata.buffer, + mdd->tdata.buffer_dma); + mdd->tdata.buffer = NULL; + mdd->tdata.buffer_dma = 0; + mdd->tdata.buffer_len = 0; + } +} + +static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) +{ + int i; + struct scatterlist *sg; + unsigned ret = 0; + + /*MDD_F_DEBUG(); */ + for_each_sg(sgl, sg, nents, i) { + ret += sg->length; + } + return ret; +} + +static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) +{ + /*MDD_F_DEBUG(); */ + if (nents == 1) { + mdd->req->buf = sg_virt(mdd->tdata.sg_trans); + mdd->req->length = mdd->tdata.sg_trans->length; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + } else { + mdd->req->buf = NULL; + mdd->req->length = mdd_sg_len(mdd->tdata.sg_trans, nents); + mdd->req->dma = 0; + mdd->req->sg = mdd->tdata.sg_trans; + mdd->req->num_sgs = nents; + } + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + return -EINVAL; + } + return 0; +} + +static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) +{ + size_t transfer_len; + + /*MDD_F_DEBUG(); */ + transfer_len = + sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer, + mdd->tdata.buffer_len); + if (!transfer_len) { + mdd_err(mdd, "Cannot copy into nonsg memory\n"); + return -EINVAL; + } + mdd->req->buf = mdd->tdata.buffer; + mdd->req->length = transfer_len; + mdd->req->dma = mdd->tdata.buffer_dma; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + return -EINVAL; + } + return 0; +} + +static int mdd_send_auto(struct msu_dvc_dev *mdd, int nents) +{ + /*MDD_F_DEBUG(); */ + if (!mdd_gadget(mdd)->sg_supported) + return mdd_send_buffer(mdd, nents); + else + return mdd_send_sg(mdd, nents); +} + +static int (*send_funcs[])(struct msu_dvc_dev *, int) = { + [MDD_TRANSFER_AUTO] = mdd_send_auto, + [MDD_TRANSFER_SINGLE] = mdd_send_buffer, + [MDD_TRANSFER_MULTI] = mdd_send_sg, +}; + +#ifdef MDD_DEBUG +static int mdd_proc_add_stats(struct msu_dvc_dev *mdd, int nents) +{ + int i, count; + struct scatterlist *sg; + + /*MDD_F_DEBUG(); */ + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + count = msc_data_sz((struct msc_block_desc *)sg_virt(sg)); + mdd->stats.full_block_size += sg->length; + mdd->stats.valid_block_size += (count + MSC_BDESC); + mdd->stats.valid_data_size += count; + } + return i; +} +#else +#define mdd_proc_add_stats(m, n) do {} while (0) +#endif + +static int mdd_proc_trimmed_blocks(struct msu_dvc_dev *mdd, int nents) +{ + u8 *ptr; + size_t len; + int i, out_cnt = 0; + struct scatterlist *sg, *sg_dest = NULL; + + /*MDD_F_DEBUG(); */ + mdd_proc_add_stats(mdd, nents); + + sg_init_table(mdd->tdata.sg_proc, nents); + + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + ptr = sg_virt(sg); + len = msc_data_sz((struct msc_block_desc *)ptr) + MSC_BDESC; + if (!len) { + mdd_err(mdd, "Zero length block"); + } else { + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; + } + } + sg_mark_end(sg_dest); + return out_cnt; +} + +static int mdd_proc_stp_only(struct msu_dvc_dev *mdd, int nents) +{ + u8 *ptr; + size_t len; + int i, out_cnt = 0; + struct scatterlist *sg, *sg_dest = NULL; + + /*MDD_F_DEBUG(); */ + mdd_proc_add_stats(mdd, nents); + + sg_init_table(mdd->tdata.sg_proc, nents); + + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + ptr = sg_virt(sg); + len = msc_data_sz((struct msc_block_desc *)ptr); + ptr += MSC_BDESC; + if (!len) { + mdd_err(mdd, "Zero data length block"); + } else { + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; + } + } + sg_mark_end(sg_dest); + return out_cnt; +} + +static int (*proc_funcs[]) (struct msu_dvc_dev *, int) = { +#ifdef MDD_DEBUG + [MDD_PROC_NONE] = mdd_proc_add_stats, +#endif + [MDD_PROC_REM_TAIL] = mdd_proc_trimmed_blocks, + [MDD_PROC_REM_ALL] = mdd_proc_stp_only, +}; + +static void mdd_work(struct work_struct *work) +{ + int nents, current_bytes, retry_cnt = 0; + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(work, struct msu_dvc_dev, work); + init_stats_start(mdd); + + if (mdd_setup_transfer_data(mdd)) { + mdd_err(mdd, "Cannot setup transfer data\n"); + return; + } + mdd_info(mdd, "Start transfer loop\n"); + while (atomic_read(mdd->dtc_status) == DVCT_MASK_ONLINE_TRANS) { + sg_init_table(mdd->tdata.sg_raw, mdd->tdata.block_count); + /* Maybe will be better if msc_sg_oldest_win changes the window + * if the "oldest" one contains data and is the current one..*/ + + current_bytes = msc_current_win_bytes(mdd->th_dev); + if (current_bytes > mdd->min_transfer || + (current_bytes && retry_cnt >= mdd->max_retry_count)) { + msc_switch_window(mdd->th_dev); + nents = msc_sg_oldest_win(mdd->th_dev, + mdd->tdata.sg_raw); + retry_cnt = 0; + } else { + if (unlikely(current_bytes < 0)) { + mdd_warn(mdd, "Unexpected state (%d), switch", + current_bytes); + msc_switch_window(mdd->th_dev); + } else { + if (retry_cnt < mdd->max_retry_count) + retry_cnt++; + } + nents = 0; + } + stats_loop(mdd); + + if (nents < 0) { + mdd_err(mdd, "Cannot get ith data\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + break; + } + + if (nents && proc_funcs[mdd->process_type]) { + nents = proc_funcs[mdd->process_type] (mdd, nents); + if (nents < 0) { + mdd_err(mdd, "Cannot process data\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + break; + } + } + + if (nents) { + + stats_hit(mdd); + mdd_lock_transfer(mdd); + atomic_set(&mdd->req_ongoing, 1); + if (send_funcs[mdd->transfer_type] (mdd, nents)) { + mdd_unlock_transfer(mdd); + break; + } + + mdd_unlock_transfer(mdd); + + /*wait for done stop or disable */ + wait_event(mdd->wq, + (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); + } else { + /*wait for stop or timeout */ + wait_event_timeout(mdd->wq, + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS), + msecs_to_jiffies(mdd-> + retry_timeout)); + } + } + mdd_info(mdd, "End transfer loop\n"); + if (atomic_read(&mdd->req_ongoing)) { + usb_ep_dequeue(mdd->ep, mdd->req); + atomic_set(&mdd->req_ongoing, 0); + } + +#ifdef MDD_DEBUG + mdd->stats.work_end = jiffies; +#endif + mdd_reset_transfer_data(mdd); +} + +static int mdd_activate(struct dvct_source_device *client, atomic_t *status) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + + mdd->dtc_status = status; + + return 0; +} + +static int mdd_binded(struct dvct_source_device *client, struct usb_ep *ep, + struct usb_function *func) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->ep = ep; + mdd->func = func; + + mdd->req = usb_ep_alloc_request(mdd->ep, GFP_KERNEL); + if (!mdd->req) { + mdd_err(mdd, "Cannot allocate usb request\n"); + return -ENOMEM; + } + return 0; +} + +static void mdd_connected(struct dvct_source_device *client, + enum usb_device_speed speed) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->speed = speed; +} + +union mdd_config { + u8 config; + struct { + u8 enable:1; /* one */ + u8 tr_type:2; + u8 proc_type:2; + } params; +}; + +static int mdd_start_transfer(struct dvct_source_device *client, u8 config) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + union mdd_config cfg; + + MDD_F_DEBUG(); + /* If we share the resources with node base reading this is + * the place where we should lock.*/ + + cfg.config = config; + + if (cfg.params.proc_type <= MDD_PROC_MAX + && cfg.params.proc_type >= MDD_PROC_MIN) { + mdd_info(mdd, "Set process type %d", cfg.params.proc_type); + mdd->process_type = cfg.params.proc_type; + } + + if (cfg.params.tr_type <= MDD_TRANSFER_MAX + && cfg.params.tr_type >= MDD_TRANSFER_MIN) { + mdd_info(mdd, "Set transfer type %d", cfg.params.tr_type); + mdd->transfer_type = cfg.params.tr_type; + } + + /*Force linear buffer transfer if the gadget is not supporting SGs */ + if (mdd->transfer_type != MDD_TRANSFER_SINGLE + && !mdd_gadget(mdd)->sg_supported) { + mdd_info(mdd, "Force linear buffer transfer"); + mdd->transfer_type = MDD_TRANSFER_SINGLE; + } + + dvct_clr_status(mdd->dtc_status, DVCT_MASK_ERR); + dvct_set_status(mdd->dtc_status, DVCT_MASK_TRANS); + queue_work(mdd->wrq, &mdd->work); + return 0; +} + +static int mdd_stop_transfer(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + dvct_clr_status(mdd->dtc_status, DVCT_MASK_TRANS); + wake_up(&mdd->wq); + + return 0; +} + +static void mdd_disconnected(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->speed = USB_SPEED_UNKNOWN; +} + +static void mdd_unbinded(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + + if (mdd->req) { + usb_ep_free_request(mdd->ep, mdd->req); + mdd->req = NULL; + } + mdd->ep = NULL; +} + +static void mdd_deactivate(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->th_dev = NULL; + mdd->dtc_status = NULL; +} + +/*the driver*/ +static struct dvct_source_driver mdd_drv = { + .activate = mdd_activate, + .binded = mdd_binded, + .connected = mdd_connected, + .start_transfer = mdd_start_transfer, + .stop_transfer = mdd_stop_transfer, + .disconnected = mdd_disconnected, + .unbinded = mdd_unbinded, + .deactivate = mdd_deactivate, + .driver.name = DTC_DRV_NAME, +}; + +static struct msu_dvc_dev *mdd_alloc_device(const char *name) +{ + struct msu_dvc_dev *mdd; + + mdd = kzalloc(sizeof(*mdd), GFP_KERNEL); + + if (!mdd) + return ERR_PTR(-ENOMEM); + + mdd->ddev.name_add = kstrdup(name, GFP_KERNEL); + if (!mdd->ddev.name_add) + return ERR_PTR(-ENOMEM); + + /* mdd->ddev.protocol = 0; */ + /* mdd->ddev.desc = NULL; */ + /* mdd->dtc_status = NULL; */ + /* mdd->ep = NULL; */ + mdd->speed = USB_SPEED_UNKNOWN; + /* mdd->msu_dev = NULL; */ + /* mdd->wrq = NULL; */ + mdd->retry_timeout = MDD_RETRY_TIMEOUT_DEF; + mdd->max_retry_count = MDD_MAX_RETRY_CNT_DEF; + /* mdd->req = NULL; */ + atomic_set(&mdd->req_ongoing, 0); + /*mdd->tdata is all NULL */ + mdd->transfer_type = MDD_TRANSFER_AUTO; + mdd->process_type = MDD_PROC_REM_ALL; + mdd->min_transfer = MDD_MIN_TRANSFER_DEF; + + spin_lock_init(&mdd->tdata.lock); + + return mdd; +}; + +static void mdd_free_device(struct msu_dvc_dev *mdd) +{ + kfree(mdd->ddev.name_add); + kfree(mdd); +}; + +static struct attribute *mdd_attrs[] = { + &dev_attr_mdd_min_transfer.attr, + &dev_attr_mdd_retry_timeout.attr, + &dev_attr_mdd_max_retry.attr, + &dev_attr_mdd_transfer_type.attr, + &dev_attr_mdd_proc_type.attr, +#ifdef MDD_DEBUG + &dev_attr_mdd_stats.attr, +#endif + NULL, +}; + +static struct attribute_group mdd_attrs_group = { + .attrs = mdd_attrs, +}; + +void mdd_msc_probe(struct intel_th_device *thdev) +{ + int ret; + struct msu_dvc_dev *mdd; + struct device *dev; + + pr_info("New th-msc device %s", dev_name(&thdev->dev)); + mdd = mdd_alloc_device(dev_name(&thdev->dev)); + + if (IS_ERR_OR_NULL(mdd)) { + pr_err("Cannot allocate device %s (%ld)", dev_name(&thdev->dev), + PTR_ERR(mdd)); + return; + } + + ret = dvct_source_device_add(&mdd->ddev, &mdd_drv); + if (ret) { + pr_err("Cannot register dvc device %d", ret); + mdd_free_device(mdd); + return; + } + + mdd->th_dev = thdev; + dev = &mdd->ddev.device; + + mdd->wrq = alloc_workqueue("%s_workqueue", WQ_MEM_RECLAIM | WQ_HIGHPRI, + 1, dev_name(&mdd->ddev.device)); + if (!mdd->wrq) { + mdd_err(mdd, "Cannot allocate work queue\n"); + mdd_free_device(mdd); + return; + } + + INIT_WORK(&mdd->work, mdd_work); + + init_waitqueue_head(&mdd->wq); + + /*Attributes */ + ret = sysfs_create_group(&dev->kobj, &mdd_attrs_group); + if (ret) + mdd_warn(mdd, "Cannot add attribute group %d\n", ret); + + ret = sysfs_create_link(&dev->kobj, &thdev->dev.kobj, "msc"); + if (ret) + mdd_warn(mdd, "Cannot add msc link %d\n", ret); + + spin_lock(&mdd_devs_lock); + list_add(&mdd->mdd_list, &mdd_devs); + spin_unlock(&mdd_devs_lock); +} + +void mdd_msc_remove(struct intel_th_device *thdev) +{ + struct msu_dvc_dev *mdd = NULL; + struct msu_dvc_dev *mdd_iter = NULL; + + spin_lock(&mdd_devs_lock); + list_for_each_entry(mdd_iter, &mdd_devs, mdd_list) { + if (mdd_iter->th_dev == thdev) + mdd = mdd_iter; + } + + if (!mdd) { + pr_err("No such mdd device, %s", dev_name(&thdev->dev)); + spin_unlock(&mdd_devs_lock); + return; + } + list_del(&mdd->mdd_list); + + spin_unlock(&mdd_devs_lock); + + flush_workqueue(mdd->wrq); + destroy_workqueue(mdd->wrq); + + sysfs_remove_group(&mdd->ddev.device.kobj, &mdd_attrs_group); + sysfs_remove_link(&mdd->ddev.device.kobj, "msc"); + + mdd->wrq = NULL; + + dvct_source_device_del(&mdd->ddev); + mdd_free_device(mdd); +} + +struct msc_probe_rem_cb mdd_msc_cbs = { + .probe = mdd_msc_probe, + .remove = mdd_msc_remove, +}; + +static int __init msu_dvc_init(void) +{ + int ret; + + MDD_F_DEBUG(); + ret = dvct_source_driver_register(&mdd_drv); + if (ret) { + pr_err("Cannot register dvc driver %d", ret); + return ret; + } + + msc_register_callbacks(mdd_msc_cbs); + return 0; +} + +static void __exit msu_dvc_exit(void) +{ + MDD_F_DEBUG(); + msc_unregister_callbacks(); + dvct_source_driver_unregister(&mdd_drv); +} + +module_init(msu_dvc_init); +module_exit(msu_dvc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Traian Schiau "); From 8c3f221895ae01e1c917f830e7026fbfd6a60e69 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Mon, 5 Oct 2015 12:37:02 +0300 Subject: [PATCH 0957/1276] intel_th: Fix msc probe/remove locking. [ 15.493015] BUG: sleeping function called from invalid .. Is genetared while msu_dvc_init/msc_register_callbacks because msc_register_callbacks is protected by spinlock and the provided callbacks (called for preexisting msu devices) might sleep. Change the protecting spinlock to mutex. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index fa149469d3b9..c179e7558c5b 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -142,7 +142,7 @@ struct msc_device_instance { }; static LIST_HEAD(msc_dev_instances); -static DEFINE_SPINLOCK(msc_dev_reg_lock); +static DEFINE_MUTEX(msc_dev_reg_lock); /** * msc_register_callbacks() * @cbs @@ -151,7 +151,7 @@ int msc_register_callbacks(struct msc_probe_rem_cb cbs) { struct msc_device_instance *it; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); msc_probe_rem_cb.probe = cbs.probe; msc_probe_rem_cb.remove = cbs.remove; @@ -160,7 +160,7 @@ int msc_register_callbacks(struct msc_probe_rem_cb cbs) cbs.probe(it->thdev); } - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); return 0; } EXPORT_SYMBOL_GPL(msc_register_callbacks); @@ -170,12 +170,12 @@ EXPORT_SYMBOL_GPL(msc_register_callbacks); */ void msc_unregister_callbacks(void) { - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); msc_probe_rem_cb.probe = NULL; msc_probe_rem_cb.remove = NULL; - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } EXPORT_SYMBOL_GPL(msc_unregister_callbacks); @@ -187,7 +187,7 @@ static void msc_add_instance(struct intel_th_device *thdev) if (!instance) return; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); instance->thdev = thdev; list_add(&instance->list, &msc_dev_instances); @@ -195,14 +195,14 @@ static void msc_add_instance(struct intel_th_device *thdev) if (msc_probe_rem_cb.probe) msc_probe_rem_cb.probe(thdev); - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } static void msc_rm_instance(struct intel_th_device *thdev) { struct msc_device_instance *instance = NULL, *it; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); if (msc_probe_rem_cb.remove) msc_probe_rem_cb.remove(thdev); @@ -221,7 +221,7 @@ static void msc_rm_instance(struct intel_th_device *thdev) pr_warn("msu: cannot remove %p (not found)", thdev); } - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) From ea8c41d0b58298467a373d99a9ce0500b8fd4ca4 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Tue, 3 Nov 2015 12:50:55 +0200 Subject: [PATCH 0958/1276] intel_th: Use saved nwsa only when msc is disabled Use the saved nwsa only when msc is disabled, otherwise use the value provided by MSCxNWSA register. The nswa (next window start address) backup was introduced to have a valid starting window for post capture dumping in case of in memory tracing, but a regresion was introduced for "online" memory tracing (eg. dvc). Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index c179e7558c5b..70d41a0e2e0a 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -281,17 +281,25 @@ static struct msc_window *msc_oldest_window(struct msc *msc) { struct msc_window *win; unsigned int found = 0; + unsigned long nwsa; if (list_empty(&msc->win_list)) return NULL; + if (msc->enabled) { + u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + + nwsa = (unsigned long)reg << PAGE_SHIFT; + } else { + nwsa = msc->nwsa; + } /* * we might need a radix tree for this, depending on how * many windows a typical user would allocate; ideally it's * something like 2, in which case we're good */ list_for_each_entry(win, &msc->win_list, entry) { - if (win->block[0].addr == msc->nwsa) + if (win->block[0].addr == nwsa) found++; /* skip the empty ones */ From d8be0937bf40316bf52fa16d082efcc0b313c42c Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Wed, 25 Nov 2015 13:47:01 +0200 Subject: [PATCH 0959/1276] intel_th: Keep switch window as short as possible Stop/start GTH with irq's disabled. Since dvc part runs in a kernel thread, disable preemption for the time switch_window is called. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/gth.c | 4 +++- drivers/hwtracing/intel_th/msu-dvc.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index e95301f91627..f697528e4d1c 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -624,7 +624,7 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - unsigned long count; + unsigned long count, flags; u32 reg; /* trigger */ @@ -639,8 +639,10 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, if (!count) dev_dbg(&thdev->dev, "timeout waiting for CTS Trigger\n"); + local_irq_save(flags); intel_th_gth_stop(gth, output, false); intel_th_gth_start(gth, output); + local_irq_restore(flags); } /** diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index 475f4cfe51ba..5ed280dfd36c 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -702,6 +702,7 @@ static void mdd_work(struct work_struct *work) /* Maybe will be better if msc_sg_oldest_win changes the window * if the "oldest" one contains data and is the current one..*/ + preempt_disable(); current_bytes = msc_current_win_bytes(mdd->th_dev); if (current_bytes > mdd->min_transfer || (current_bytes && retry_cnt >= mdd->max_retry_count)) { @@ -720,6 +721,7 @@ static void mdd_work(struct work_struct *work) } nents = 0; } + preempt_enable(); stats_loop(mdd); if (nents < 0) { From bda1b0e48ce4cdae135a4dd14f2bcb6ccd5a4e56 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 3 Dec 2015 16:16:36 +0200 Subject: [PATCH 0960/1276] intel_th-dvc: Limit the usb TRB count. DWC3 gadget implementation is limited to accept only 32 TRBs per endpoint, if this number is exceeded in the middle of a request, the extra data is dropped without warning. Limit the maximum number of sg-s that are submitted in a single request. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 97 ++++++++++++++++++---------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index 5ed280dfd36c..db8d877e6ec1 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -39,6 +39,11 @@ #define MDD_RETRY_TIMEOUT_DEF 2 #define MDD_MAX_RETRY_CNT_DEF 150 +/* The DWC3 gadget is able to handle a maximum of 32 TRBs per-ep (an sg based + * request counts as the number of sg-s). + * This should be updated in case some other UDC has a lower threshold. */ +#define MDD_MAX_TRB_CNT 32 + #define mdd_err(mdd, ...) dev_err(&(mdd)->ddev.device, ## __VA_ARGS__) #define mdd_warn(mdd, ...) dev_warn(&(mdd)->ddev.device, ## __VA_ARGS__) #define mdd_info(mdd, ...) dev_info(&(mdd)->ddev.device, ## __VA_ARGS__) @@ -523,29 +528,55 @@ static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) { + struct scatterlist *sgl = mdd->tdata.sg_trans; + /*MDD_F_DEBUG(); */ - if (nents == 1) { - mdd->req->buf = sg_virt(mdd->tdata.sg_trans); - mdd->req->length = mdd->tdata.sg_trans->length; - mdd->req->dma = 0; - mdd->req->sg = NULL; - mdd->req->num_sgs = 0; - } else { - mdd->req->buf = NULL; - mdd->req->length = mdd_sg_len(mdd->tdata.sg_trans, nents); - mdd->req->dma = 0; - mdd->req->sg = mdd->tdata.sg_trans; - mdd->req->num_sgs = nents; - } + while (nents) { + int trans_ents; - mdd->req->context = mdd; - mdd->req->complete = mdd_complete; - mdd->req->zero = 1; + mdd_lock_transfer(mdd); - if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { - mdd_err(mdd, "Cannot queue request\n"); - dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); - return -EINVAL; + if (nents > MDD_MAX_TRB_CNT) { + trans_ents = MDD_MAX_TRB_CNT; + sg_mark_end(&sgl[trans_ents - 1]); + } else { + trans_ents = nents; + } + + if (trans_ents == 1) { + mdd->req->buf = sg_virt(sgl); + mdd->req->length = sgl->length; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + } else { + mdd->req->buf = NULL; + mdd->req->length = mdd_sg_len(sgl, trans_ents); + mdd->req->dma = 0; + mdd->req->sg = sgl; + mdd->req->num_sgs = trans_ents; + } + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + atomic_set(&mdd->req_ongoing, 1); + nents -= trans_ents; + sgl += trans_ents; + + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); } return 0; } @@ -555,11 +586,13 @@ static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) size_t transfer_len; /*MDD_F_DEBUG(); */ + mdd_lock_transfer(mdd); transfer_len = sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer, mdd->tdata.buffer_len); if (!transfer_len) { mdd_err(mdd, "Cannot copy into nonsg memory\n"); + mdd_unlock_transfer(mdd); return -EINVAL; } mdd->req->buf = mdd->tdata.buffer; @@ -575,8 +608,16 @@ static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { mdd_err(mdd, "Cannot queue request\n"); dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); return -EINVAL; } + + atomic_set(&mdd->req_ongoing, 1); + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); return 0; } @@ -608,6 +649,7 @@ static int mdd_proc_add_stats(struct msu_dvc_dev *mdd, int nents) mdd->stats.valid_block_size += (count + MSC_BDESC); mdd->stats.valid_data_size += count; } + return i; } #else @@ -740,22 +782,9 @@ static void mdd_work(struct work_struct *work) } if (nents) { - stats_hit(mdd); - mdd_lock_transfer(mdd); - atomic_set(&mdd->req_ongoing, 1); - if (send_funcs[mdd->transfer_type] (mdd, nents)) { - mdd_unlock_transfer(mdd); + if (send_funcs[mdd->transfer_type] (mdd, nents)) break; - } - - mdd_unlock_transfer(mdd); - - /*wait for done stop or disable */ - wait_event(mdd->wq, - (!atomic_read(&mdd->req_ongoing) || - (atomic_read(mdd->dtc_status) != - DVCT_MASK_ONLINE_TRANS))); } else { /*wait for stop or timeout */ wait_event_timeout(mdd->wq, From c185ddaf90f268e8aca9aec28503716462d9e545 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 7 Jan 2016 10:23:19 +0200 Subject: [PATCH 0961/1276] intel_th-dvc: Fix deactivation issue. mdd->th_dev should not be reset upon dvc-source device deactivation. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index db8d877e6ec1..b8c700125ef7 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -925,7 +925,6 @@ static void mdd_deactivate(struct dvct_source_device *client) struct msu_dvc_dev *mdd = dtc_to_mdd(client); MDD_F_DEBUG(); - mdd->th_dev = NULL; mdd->dtc_status = NULL; } From e5f69263520a041479e502297fc13876985854d5 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Wed, 11 May 2016 10:22:36 +0300 Subject: [PATCH 0962/1276] intel_th-dvc: Fix code analysis issues Fix some issues like possible NULL dereferencing, possible memory leaks and invalid conditions, flagged by the code analysis tools. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 35 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index b8c700125ef7..8e5b245e64a9 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -432,14 +432,14 @@ static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) } mdd->tdata.block_count = msc_max_blocks(mdd->th_dev); - if (mdd->tdata.block_count <= 0) { + if (mdd->tdata.block_count == 0) { mdd_err(mdd, "Invalid block count %zu\n", mdd->tdata.block_count); goto err; } mdd->tdata.block_size = msc_block_max_size(mdd->th_dev); - if (mdd->tdata.block_size <= 0) { + if (mdd->tdata.block_size == 0) { mdd_err(mdd, "Invalid block size %zu\n", mdd->tdata.block_size); goto err; } @@ -670,19 +670,24 @@ static int mdd_proc_trimmed_blocks(struct msu_dvc_dev *mdd, int nents) for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { ptr = sg_virt(sg); - len = msc_data_sz((struct msc_block_desc *)ptr) + MSC_BDESC; + len = msc_data_sz((struct msc_block_desc *)ptr); if (!len) { mdd_err(mdd, "Zero length block"); - } else { - if (!sg_dest) - sg_dest = mdd->tdata.sg_proc; - else - sg_dest = sg_next(sg_dest); - sg_set_buf(sg_dest, ptr, len); - out_cnt++; + continue; } + + len += MSC_BDESC; + + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; } - sg_mark_end(sg_dest); + if (sg_dest) + sg_mark_end(sg_dest); + return out_cnt; } @@ -713,7 +718,9 @@ static int mdd_proc_stp_only(struct msu_dvc_dev *mdd, int nents) out_cnt++; } } - sg_mark_end(sg_dest); + if (sg_dest) + sg_mark_end(sg_dest); + return out_cnt; } @@ -951,8 +958,10 @@ static struct msu_dvc_dev *mdd_alloc_device(const char *name) return ERR_PTR(-ENOMEM); mdd->ddev.name_add = kstrdup(name, GFP_KERNEL); - if (!mdd->ddev.name_add) + if (!mdd->ddev.name_add) { + kfree(mdd); return ERR_PTR(-ENOMEM); + } /* mdd->ddev.protocol = 0; */ /* mdd->ddev.desc = NULL; */ From 1e432a7ec39f7c933b8081f495d6fe0b12bbce24 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Wed, 4 Jun 2014 16:50:19 +0200 Subject: [PATCH 0963/1276] platform: x86: add pstore RAM backend managed by BIOS BIOS manage a persisted RAM buffer across watchdog reset, this feature is named PRAM. Buffer is reserved by BIOS and exposed as PRAM ACPI table. PRAM BIOS feature is configurable through BIOS setup or PRAM_Conf EFI variable (GUID ecb54cd9-e5ae-4fdc-a971-e877756068f7). Accepted values for this variable are 0, 1, 2 and 3 as ASCII string; will configure PRAM feature respectively as Disabled, 4 MB, 16 MB and 64 MB. This device driver bind the PRAM buffer exposed in PRAM ACPI table to a pstore ram (ramoops) driver. It also exposes similar parameters as the ramoops dummy device driver (fs/pstore/ram.c) : record_size, console_size, ftrace_size, dump_oops, ecc. Signed-off-by: Jeremy Rocher Signed-off-by: Tian, Baofeng --- drivers/platform/x86/Kconfig | 17 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_pstore_pram.c | 135 +++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 drivers/platform/x86/intel_pstore_pram.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0c1aa6c314f5..4d0dfd030b07 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1229,6 +1229,23 @@ config I2C_MULTI_INSTANTIATE To compile this driver as a module, choose M here: the module will be called i2c-multi-instantiate. +config INTEL_PSTORE_PRAM + tristate "Intel pstore RAM backend driver (PRAM BIOS feature)" + depends on ACPI + depends on PSTORE_RAM + ---help--- + This driver provides RAM backend for pstore, managed by BIOS + as PRAM (Persisted RAM buffer) debug feature. + + PRAM BIOS feature is configurable through BIOS setup or PRAM_Conf + EFI variable (GUID ecb54cd9-e5ae-4fdc-a971-e877756068f7). + Accepted values for variable are 0, 1, 2 and 3 as ASCII + string; will configure PRAM feature respectively as + Disabled, 4 MB, 16 MB and 64 MB. + + Safe to say Y, will not bind if your BIOS doesn't support + this feature. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e6d1becf81ce..91fda053d781 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_INTEL_PSTORE_PRAM) += intel_pstore_pram.o diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c new file mode 100644 index 000000000000..2d5205bfd204 --- /dev/null +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define SZ_4K 0x00001000 +#define SZ_2M 0x00200000 + +/* PRAM stands for 'Persisted RAM' from BIOS point of view */ +#define ACPI_SIG_PRAM "PRAM" + +/* + * Following parameters match to those defined in fs/pstore/ram.c in + * order to keep campatibility between driver intefaces, please refer + * to it for implementaion details. + */ +static ulong pram_record_size = SZ_4K; +module_param_named(record_size, pram_record_size, ulong, 0400); +MODULE_PARM_DESC(record_size, "size of each dump done on oops/panic"); + +static ulong pram_console_size = SZ_2M; +module_param_named(console_size, pram_console_size, ulong, 0400); +MODULE_PARM_DESC(console_size, "size of kernel console log"); + +static ulong pram_ftrace_size = 2*SZ_4K; +module_param_named(ftrace_size, pram_ftrace_size, ulong, 0400); +MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); + +static int pram_dump_oops = 1; +module_param_named(dump_oops, pram_dump_oops, int, 0600); +MODULE_PARM_DESC(dump_oops, + "set to 1 to dump oopses, 0 to only dump panics (default 1)"); + +static int pram_ecc; +module_param_named(ecc, pram_ecc, int, 0600); +MODULE_PARM_DESC(ecc, + "if non-zero, the option enables SW ECC support, provided by" + "fs/pstore/ram_core.c, and specifies ECC buffer size in bytes" + "(1 is a special value, means 16 bytes ECC)"); + +static struct ramoops_platform_data *pram_data; +static struct platform_device *pram_dev; + +struct acpi_table_pram { + struct acpi_table_header header; + u64 addr; + u32 size; +} __packed; + +static int register_pram_dev(unsigned long mem_address, + unsigned long mem_size) +{ + pram_data = kzalloc(sizeof(*pram_data), GFP_KERNEL); + if (!pram_data) { + pr_err("could not allocate pram_data\n"); + return -ENOMEM; + } + + pram_data->mem_address = mem_address; + pram_data->mem_size = mem_size; + pram_data->record_size = pram_record_size; + pram_data->console_size = pram_console_size; + pram_data->ftrace_size = pram_ftrace_size; + pram_data->dump_oops = pram_dump_oops; + /* + * For backwards compatibility with previous + * fs/pstore/ram_core.c implementation, + * intel_pstore_pram.ecc=1 means 16 bytes ECC. + */ + pram_data->ecc_info.ecc_size = pram_ecc == 1 ? 16 : pram_ecc; + + pram_dev = platform_device_register_data(NULL, "ramoops", -1, + pram_data, sizeof(struct ramoops_platform_data)); + if (IS_ERR(pram_dev)) { + pr_err("could not create platform device: %ld\n", + PTR_ERR(pram_dev)); + kfree(pram_data); + return PTR_ERR(pram_dev); + } + + pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", + pram_data->mem_address, pram_data->mem_size); + + return 0; +} + +static int __init intel_pram_init(void) +{ + acpi_status status; + struct acpi_table_pram *pramt; + + status = acpi_get_table(ACPI_SIG_PRAM, 0, + (struct acpi_table_header **)&pramt); + if (status == AE_NOT_FOUND) { + pr_debug("PRAM table not found\n"); + return -ENODEV; + } else if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err("Failed to get PRAM table: %s\n", msg); + return -EINVAL; + } + + if (!pramt->addr || !pramt->size) { + pr_debug("PRAM: bad address (0x%llx) or size (0x%lx)\n", + (unsigned long long)pramt->addr, + (unsigned long)pramt->size); + return -ENODEV; + } + + return register_pram_dev(pramt->addr, pramt->size); +} +device_initcall(intel_pram_init); + +static void __exit intel_pram_exit(void) +{ + platform_device_unregister(pram_dev); + kfree(pram_data); +} +module_exit(intel_pram_exit); From 54f31890d4c8c4c6db9f00243bb2053e2c3ade53 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Mon, 18 Apr 2016 17:27:00 +0200 Subject: [PATCH 0964/1276] intel_pstore_pram: load module earlier Currently intel_pstore_pram is loaded as a normal module at device_initcall() but if a panic occurs before it is loaded, nothing is logged in pstore. Load module earlier, in postcore_initcall(), like ramoops. Signed-off-by: Jeremy Rocher --- drivers/platform/x86/intel_pstore_pram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c index 2d5205bfd204..78dd9c3b08c5 100644 --- a/drivers/platform/x86/intel_pstore_pram.c +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -125,7 +125,7 @@ static int __init intel_pram_init(void) return register_pram_dev(pramt->addr, pramt->size); } -device_initcall(intel_pram_init); +postcore_initcall(intel_pram_init); static void __exit intel_pram_exit(void) { From d6eaa88da148e1412fab29243c49af5c3508dfdc Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 23 Jun 2017 03:36:20 +0800 Subject: [PATCH 0965/1276] change itco watchdog type for tristate to bool tristate will allow kernel config to M and build ko file this will cause build allmodconfig error as necessary symbols is not exported. change to bool only allow config ITCO_WDT to yes/no. Signed-off-by: Tian, Baofeng --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5ea8909a41f9..8bf318d44a88 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1059,7 +1059,7 @@ config INTEL_MID_WATCHDOG To compile this driver as a module, choose M here. config ITCO_WDT - tristate "Intel TCO Timer/Watchdog" + bool "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI select WATCHDOG_CORE depends on I2C || I2C=n From 1e7a4791bea258fc87ab2d992d25a108cb25ef0c Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 23 Aug 2017 02:00:35 +0800 Subject: [PATCH 0966/1276] kernel: printk: move some definition out of config scope to avoid build error These definitions previously put in #ifdef CONFIG_PRINTK, this config is defined as default however, if you build code with allnoconfig, you will find this error, change it to out of #ifdef to avoid this error. Signed-off-by: Tian, Baofeng --- kernel/printk/printk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 9bf5404397e0..578cad2f66a0 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -377,6 +377,11 @@ __packed __aligned(4) */ DEFINE_RAW_SPINLOCK(logbuf_lock); +/* Give the posibility to temporary disable slow (!CON_FAST) consoles */ +static atomic_t console_slow_suspended = ATOMIC_INIT(0); +/* Keep the number of slow suspend in check */ +#define MAX_SLOW_SUSPEND_COUNT (50) + /* * Helper macros to lock/unlock logbuf_lock and switch between * printk-safe/unsafe modes. From 1c6e036386e09fd9e7a8d5f242a24d4c687b1b12 Mon Sep 17 00:00:00 2001 From: "he, bo" Date: Thu, 5 Jan 2017 10:01:37 +0800 Subject: [PATCH 0967/1276] implement the adb reboot dnx feature in kernel. there are 3 ways flash the IFWI on BXTP board: Method 1: debug card + ias-spi-programmer (Linux host only) Method 2: CSE DnX + PlatformFlashTool + switch Method 3: CSE DnX + PlatformFlashTool + ABL CLI Method 1 and Method 3 depend on the debug board, but not everyone has the debug board. Method 2 need rework the board, and also if we want to flash the IFWI, we need to switch the Jumper 4 to ON while if we want to boot to Android, we need to switch the Jumper 4 to OFF. the patch implement one feature to reboot the board to dnx mode, then we can flash the IFWI via platform tools. the reboot to dnx command is: adb reboot dnx Signed-off-by: he, bo --- drivers/staging/android/abl/ablbc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index c7ed097b8ac1..6bb5d25aab44 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -99,6 +99,7 @@ static const struct name2id NAME2ID[] = { { "elk", 0x02 }, { "recovery", 0x03 }, { "crashmode", 0x04 }, + { "dnx", 0x05 }, { "cli", 0x10 }, }; @@ -294,7 +295,7 @@ static const unsigned int DEFAULT_TARGET_INDEX; static const char * const cold_reset[] = { "/sbin/cansend", "slcan0", - "0000FFFF#05025555555555", + "0000FFFF#05035555555555", NULL}; static const char * const cold_reset_capsule[] = { "/sbin/cansend", From 20876fe72d1ecae8c493539ace77a15e258f5b1c Mon Sep 17 00:00:00 2001 From: Emmanuel Berthier Date: Thu, 28 Jul 2016 15:47:23 +0300 Subject: [PATCH 0968/1276] Panic in case of Bad page Bug Controled by CONFIG_DEBUG_PANIC_ON_BAD_PAGE config switch. Signed-off-by: Emmanuel Berthier --- mm/Kconfig | 6 ++++++ mm/page_alloc.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/mm/Kconfig b/mm/Kconfig index de64ea658716..fe1aebb64897 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -604,6 +604,12 @@ config PGTABLE_MAPPING You can check speed with zsmalloc benchmark: https://github.com/spartacus06/zsmapbench +config DEBUG_PANIC_ON_BAD_PAGE + bool "Panic on Bad Page" + default n + help + Generate a panic in case of Bad Page error detection. + config ZSMALLOC_STAT bool "Export zsmalloc statistics" depends on ZSMALLOC diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e2ef1c17942f..dfec095a6c9a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -544,6 +544,10 @@ static void bad_page(struct page *page, const char *reason, /* Leave bad fields for debug, except PageBuddy could make trouble */ page_mapcount_reset(page); /* remove PageBuddy */ add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); + +#ifdef CONFIG_DEBUG_PANIC_ON_BAD_PAGE + panic("Bad page"); +#endif } /* From 14efc2c37f611722a2c54ed563bbe83d01304d37 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Wed, 12 Jul 2017 11:48:01 +0800 Subject: [PATCH 0969/1276] Fix the ablbc.c compile error: incompatible pointer type [-Werror=incompatible-pointer-typesi] Signed-off-by: Li, Hongli --- drivers/staging/android/abl/ablbc.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 6bb5d25aab44..428b0f478bf7 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -124,11 +124,11 @@ static void write_msg_to_nvram(struct nvram_msg *nvram_msg) { /* Ensure to start from top : only one command expected */ offset = 0; - write_data_to_nvram(nvram_msg, + write_data_to_nvram((void*)nvram_msg, offsetof(struct nvram_msg, cdata_payload)); - write_data_to_nvram(nvram_msg->cdata_payload, + write_data_to_nvram((void*)(nvram_msg->cdata_payload), nvram_msg->cdata_payload_size); - write_data_to_nvram(&(nvram_msg->crc), sizeof(nvram_msg->crc)); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); } /* Compute CRC for one byte (shift register-based: one bit at a time). */ @@ -174,7 +174,7 @@ static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) static struct kobject *capsule_kobject; static ssize_t is_capsule_requested(struct kobject *kobj, - struct kobj_attribute *attr, char *buf, size_t count) + struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", capsule_request); } @@ -185,13 +185,13 @@ enum capsule_device_type { }; static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, - char *buf, size_t count) + const char *buf, size_t count) { struct nvram_msg msg; struct nvram_capsule_cmd *capsule_cmd; char name[32], partition; enum capsule_device_type device; - int ret, crc, padding; + int ret, padding; unsigned char size; union _cdata_header cdh; @@ -221,7 +221,7 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, capsule_cmd->device = device; capsule_cmd->partition = partition; strncpy(capsule_cmd->file_name, name, strlen(name)); - msg.cdata_payload = capsule_cmd; + msg.cdata_payload = (char *)capsule_cmd; msg.cdata_payload_size = 3 + strlen(name) + padding; msg.crc = crc32c_msg(&msg); write_msg_to_nvram(&msg); @@ -279,7 +279,7 @@ static int set_reboot_target(const char *name) reboot_cmd.action = USERCMD_ACTION; reboot_cmd.target = id; - msg.cdata_payload = &reboot_cmd; + msg.cdata_payload = (void*)&reboot_cmd; msg.cdata_payload_size = sizeof(reboot_cmd); msg.size = offsetof(struct nvram_msg, cdata_payload) + sizeof(reboot_cmd) + sizeof(msg.crc); @@ -318,9 +318,9 @@ static int execute_slcan_command(const char *cmd[]) struct subprocess_info *sub_info; int ret = -1; - sub_info = call_usermodehelper_setup(cmd[0], - cmd, NULL, GFP_KERNEL, - NULL, NULL, NULL); + sub_info = call_usermodehelper_setup((char *)cmd[0], + (char **)cmd,(char **) NULL, GFP_KERNEL, + (void *)NULL, (void*)NULL, (void*)NULL); if (sub_info) { ret = call_usermodehelper_exec(sub_info, @@ -343,11 +343,11 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (what != SYS_RESTART) return NOTIFY_DONE; - ret = execute_slcan_command(suppress_heartbeat); + ret = execute_slcan_command((const char **)suppress_heartbeat); if (ret) goto done; - ret = execute_slcan_command(reboot_request); + ret = execute_slcan_command((const char **)reboot_request); if (ret) goto done; if (target[0] != '\0') { @@ -356,13 +356,13 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); else { - ret = execute_slcan_command(cold_reset); + ret = execute_slcan_command((const char **)cold_reset); if (ret) goto done; } } if (capsule_request) - ret = execute_slcan_command(cold_reset_capsule); + ret = execute_slcan_command((const char **)cold_reset_capsule); done: return NOTIFY_DONE; From e03f796a558b659c41326279b2c24f65f8bb1ee4 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 13 Oct 2017 14:38:09 +0800 Subject: [PATCH 0970/1276] intel_th: add security check to avoid null pointer happens Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/gth.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index f697528e4d1c..854411879008 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -659,9 +659,16 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, static int intel_th_gth_assign(struct intel_th_device *thdev, struct intel_th_device *othdev) { - struct gth_device *gth = dev_get_drvdata(&thdev->dev); + struct gth_device *gth; int i, id; + if(thdev == NULL || othdev == NULL) + return -EINVAL; + + gth = dev_get_drvdata(&thdev->dev); + if(gth == NULL) + return -EINVAL; + if (thdev->host_mode) return -EBUSY; From feb84e0a14c48ec3e7699c971517ef78c6acdb93 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Wed, 12 Jul 2017 15:13:36 +0800 Subject: [PATCH 0971/1276] Modify cansend path to adjust Treble feature Signed-off-by: jiangyao --- drivers/staging/android/abl/ablbc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 428b0f478bf7..b4a491cba7c5 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -293,22 +293,22 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; static const char * const cold_reset[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#05035555555555", NULL}; static const char * const cold_reset_capsule[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#05035555555555", NULL}; static const char * const suppress_heartbeat[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#01035555555555", NULL}; static const char * const reboot_request[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#03015555555555", NULL}; From 01d1820bcff6c61be2a908cb7464d6f2a2a0c138 Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Thu, 23 Nov 2017 15:42:46 +0800 Subject: [PATCH 0972/1276] staging/android: SBL boot Control driver * Set reboot target in NVRAM - register to reboot event - create CDATA SBL code following reboot target * Set capsule parameters in NVRAM - accessible through /sys/kernel/capsule/capsule_name - same syntax as "nvram" SBL command - e.g. m1:capsule.bin - m => emmc - 1 => partition #1 - capsule.bin => file name * Notify on capsule request - accessible through /sys/kernel/capsule/capsule_requested - 0 : no capsule requested - 1 : capsule requested (triggered through /sys/kernel/capsule/capsule_name access) Signed-off-by: Zhou, JianfengX --- arch/x86/configs/sbl_diffconfig | 1 + drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/sbl/Kconfig | 8 + drivers/staging/android/sbl/Makefile | 1 + drivers/staging/android/sbl/sblbc.c | 368 +++++++++++++++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 arch/x86/configs/sbl_diffconfig create mode 100644 drivers/staging/android/sbl/Kconfig create mode 100644 drivers/staging/android/sbl/Makefile create mode 100644 drivers/staging/android/sbl/sblbc.c diff --git a/arch/x86/configs/sbl_diffconfig b/arch/x86/configs/sbl_diffconfig new file mode 100644 index 000000000000..efbd1ae0b5e1 --- /dev/null +++ b/arch/x86/configs/sbl_diffconfig @@ -0,0 +1 @@ +CONFIG_SBL_BOOTLOADER_CONTROL=y diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 87054ad922aa..9f6b54a3c90f 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -25,6 +25,7 @@ config ANDROID_VSOC source "drivers/staging/android/ion/Kconfig" source "drivers/staging/android/abl/Kconfig" +source "drivers/staging/android/sbl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 729d11f4e150..fb345067b7f0 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -2,6 +2,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ +obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sbl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/sbl/Kconfig b/drivers/staging/android/sbl/Kconfig new file mode 100644 index 000000000000..f5f754f8d890 --- /dev/null +++ b/drivers/staging/android/sbl/Kconfig @@ -0,0 +1,8 @@ +config SBL_BOOTLOADER_CONTROL + tristate "SBL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/sbl/Makefile b/drivers/staging/android/sbl/Makefile new file mode 100644 index 000000000000..6d1258d7bfc1 --- /dev/null +++ b/drivers/staging/android/sbl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sblbc.o diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c new file mode 100644 index 000000000000..6c2d7ed6c2ee --- /dev/null +++ b/drivers/staging/android/sbl/sblbc.c @@ -0,0 +1,368 @@ +/* + * sblbc: control SBL bootloaders + * Copyright (c) 2013-2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "sblbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* ABL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) +#define USERCMD_UPDATE_IFWI(len) _USERCMD_(2, len) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +static bool capsule_request; + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_capsule_cmd { + char action; + char device; + char partition; + char file_name[1]; +} __packed; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "crashmode", 0x04 }, + { "dnx", 0x05 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + for (i = 0; i < size; i++) + { + pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); + } + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram((void*)nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram((void*)(nvram_msg->cdata_payload), + nvram_msg->cdata_payload_size); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static struct kobject *capsule_kobject; + +static ssize_t is_capsule_requested(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", capsule_request); +} + +enum capsule_device_type { + EMMC = 2, + SDCARD = 4 +}; + +static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct nvram_msg msg; + struct nvram_capsule_cmd *capsule_cmd; + char name[32], partition; + enum capsule_device_type device; + int ret, padding; + unsigned char size; + union _cdata_header cdh; + + device = (buf[0] == 'm' ? EMMC : SDCARD); + partition = buf[1] - '0'; + ret = sscanf(buf+3, "%s", name); + pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", + ret, device, partition, name); + + cdh.data = 0; + cdh.tag = CDATA_TAG_USER_CMD; + + /* padding of filename on next dword */ + padding = (4 - (3 + strlen(name))%4)%4; + size = 2 + sizeof(cdh) + 3 + strlen(name) + padding + 4; + cdh.length = 1 + (3 + strlen(name) + padding) / 4; + + msg.magic = NVRAM_VALID_FLAG; + msg.size = size; + msg.cdata_header.data = cdh.data; + + capsule_cmd = kmalloc(size, GFP_KERNEL); + if (!capsule_cmd) + return -ENOMEM; + + capsule_cmd->action = USERCMD_UPDATE_IFWI(strlen(name) + 2); + capsule_cmd->device = device; + capsule_cmd->partition = partition; + strncpy(capsule_cmd->file_name, name, strlen(name)); + msg.cdata_payload = (char *)capsule_cmd; + msg.cdata_payload_size = 3 + strlen(name) + padding; + msg.crc = crc32c_msg(&msg); + write_msg_to_nvram(&msg); + capsule_request = true; + + kfree(capsule_cmd); + + return count; +} + +static struct kobj_attribute capsule_name_attribute = + __ATTR(capsule_name, 0600, NULL, capsule_store); + +static struct kobj_attribute capsule_requested_attribute = + __ATTR(capsule_requested, 0400, is_capsule_requested, NULL); + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = (void*)&reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static int sblbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } + +done: + return NOTIFY_DONE; +} + +static struct notifier_block sblbc_reboot_notifier = { + .notifier_call = sblbc_reboot_notifier_call, +}; + +static int __init sblbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&sblbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + capsule_kobject = kobject_create_and_add("capsule", kernel_kobj); + if (!capsule_kobject) + return -ENOMEM; + + ret = sysfs_create_file(capsule_kobject, + &capsule_name_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_name\n"); + goto err; + } + + ret = sysfs_create_file(capsule_kobject, + &capsule_requested_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_requested\n"); + goto err; + } + + return 0; + +err: + kobject_put(capsule_kobject); + return ret; +} + +module_init(sblbc_init); + +static void __exit sblbc_exit(void) +{ + unregister_reboot_notifier(&sblbc_reboot_notifier); + kobject_put(capsule_kobject); +} +module_exit(sblbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Slimboot boot control driver"); +MODULE_LICENSE("GPL v2"); From 5d6950e422b63e591b6a4f21db43cf41211279de Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Wed, 27 Dec 2017 14:00:27 +0800 Subject: [PATCH 0973/1276] Debug: Pstore driver: assign value to part of ramoops For optimizing kernel command line and reduce some parameter passing. We will directly assign/set below variables to a default value rather than by passing from kernel command line. pdata->record_size padta->record_size pdata->ftrace_size pdata->dump_oops Signed-off-by: Duan, YayongX --- fs/pstore/ram.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index f4fd2e72add4..2f883a4c3420 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -38,6 +38,10 @@ #define RAMOOPS_KERNMSG_HDR "====" #define MIN_MEM_SIZE 4096UL +#define DEFAULT_RECORD_SIZE 0X4000 +#define DEFAULT_CONSOLE_SIZE 0X200000 +#define DEFAULT_FTRACE_SIZE 0x2000 +#define DEFAULT_DUMP_OOPS 1 static ulong record_size = MIN_MEM_SIZE; module_param(record_size, ulong, 0400); @@ -688,7 +692,7 @@ static int ramoops_parse_dt(struct platform_device *pdev, pdata->mem_size = resource_size(res); pdata->mem_address = res->start; pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); - pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); + pdata->dump_oops = DEFAULT_DUMP_OOPS; #define parse_size(name, field) { \ ret = ramoops_parse_dt_size(pdev, name, &value); \ @@ -697,9 +701,9 @@ static int ramoops_parse_dt(struct platform_device *pdev, field = value; \ } - parse_size("record-size", pdata->record_size); - parse_size("console-size", pdata->console_size); - parse_size("ftrace-size", pdata->ftrace_size); + pdata->record_size = DEFAULT_RECORD_SIZE; + pdata->console_size = DEFAULT_CONSOLE_SIZE; + pdata->ftrace_size = DEFAULT_FTRACE_SIZE; parse_size("pmsg-size", pdata->pmsg_size); parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_size("flags", pdata->flags); From fc97f4d45717ad202bc1a177f652b136204d6f65 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Thu, 28 Dec 2017 14:30:53 +0800 Subject: [PATCH 0974/1276] Edit suppress_heartbeat to 10mins and remove unnecessary commands Suppress_heartbeat and cold_reset_capsule configure are used for IFWI(Integrated firmware image) and ota update, 10mins is enough. For cold_reset configure, it is not need for normal reboot, keep this will cause reboot issue, need remove it. Signed-off-by: Xihua Chen --- drivers/staging/android/abl/ablbc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index b4a491cba7c5..7b15fa39a43d 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -292,15 +292,10 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; -static const char * const cold_reset[] = { - "/vendor/bin/cansend", - "slcan0", - "0000FFFF#05035555555555", - NULL}; static const char * const cold_reset_capsule[] = { "/vendor/bin/cansend", "slcan0", - "0000FFFF#05035555555555", + "0000FFFF#05025555555555", NULL}; static const char * const suppress_heartbeat[] = { "/vendor/bin/cansend", @@ -355,11 +350,6 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (ret) pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); - else { - ret = execute_slcan_command((const char **)cold_reset); - if (ret) - goto done; - } } if (capsule_request) ret = execute_slcan_command((const char **)cold_reset_capsule); From a7d8ab42be35f3c2d9b8ec285e0111f67c82979a Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 3 Jan 2018 07:23:31 +0800 Subject: [PATCH 0975/1276] debug and trace: npk: add one more buffer before send log data to usb logs data is saved with scatter list buffer, it have multiple pages( up to 32) dvc driver send this buffer to usb driver, however, in usb driver, it may not support scatter list well in current 4.14 lts code, so we add one more buffer to combine all pages data to one linear buffer, then tranfer it to usb. Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/intel_th.h | 2 +- drivers/hwtracing/intel_th/msu-dvc.c | 61 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) mode change 100644 => 100755 drivers/hwtracing/intel_th/msu-dvc.c diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 7bbc75626f1d..f385471fe56a 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -308,7 +308,7 @@ enum { /* Timestamp counter unit (TSCU) */ REG_TSCU_OFFSET = 0x2000, - REG_TSCU_LENGTH = 0x1000, + REG_TSCU_LENGTH = 0x2000, /* Software Trace Hub (STH) [0x4000..0x4fff] */ REG_STH_OFFSET = 0x4000, diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c old mode 100644 new mode 100755 index 8e5b245e64a9..91693c0f8175 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -100,6 +100,8 @@ static const char *const process_type_name[] = { struct mdd_transfer_data { u8 *buffer; + u8 *buffer_sg; + size_t buffer_sg_len; dma_addr_t buffer_dma; size_t buffer_len; struct scatterlist *sg_raw; @@ -479,6 +481,12 @@ static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) goto err_l_buf; } } else { + mdd->tdata.buffer_sg_len = + mdd->tdata.block_count * mdd->tdata.block_size; + mdd->tdata.buffer_sg = kmalloc(mdd->tdata.buffer_sg_len, GFP_KERNEL); + if(mdd->tdata.buffer_sg == NULL) + mdd->tdata.buffer_sg_len = 0; + mdd->tdata.buffer = NULL; mdd->tdata.buffer_dma = 0; mdd->tdata.buffer_len = 0; @@ -511,6 +519,12 @@ static void mdd_reset_transfer_data(struct msu_dvc_dev *mdd) mdd->tdata.buffer_dma = 0; mdd->tdata.buffer_len = 0; } + + if(mdd->tdata.buffer_sg != NULL) + { + mdd->tdata.buffer_sg_len = 0; + kfree(mdd->tdata.buffer_sg); + } } static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) @@ -526,10 +540,57 @@ static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) return ret; } +static int mdd_send_sg_buffer(struct msu_dvc_dev *mdd, int nents) +{ + size_t transfer_len; + + /*MDD_F_DEBUG(); */ + mdd_lock_transfer(mdd); + transfer_len = + sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer_sg, + mdd->tdata.buffer_sg_len); + + if (!transfer_len) { + mdd_err(mdd, "Cannot copy into nonsg memory\n"); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + mdd->req->buf = mdd->tdata.buffer_sg; + mdd->req->length = transfer_len; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + atomic_set(&mdd->req_ongoing, 1); + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); + return 0; +} + static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) { struct scatterlist *sgl = mdd->tdata.sg_trans; + if(mdd->tdata.buffer_sg != NULL) + { + return mdd_send_sg_buffer(mdd, nents); + } + /*MDD_F_DEBUG(); */ while (nents) { int trans_ents; From ca44562af2c33eaf2450ea9d4dc36a2271d29a85 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Thu, 25 Jan 2018 17:06:12 +0800 Subject: [PATCH 0976/1276] add depends for msc-dvc msc-dvc function must depend on DVC_TRACE_BUS. msc-dvc is the debug function, it based on USB DWC3 gadget function to transfer trace log. Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index 027edb7da72c..dc22562a16fc 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -69,6 +69,7 @@ config INTEL_TH_MSU config INTEL_TH_MSU_DVC tristate "Intel Trace Hub Memory Storage Unit to USB-dvc" + depends on DVC_TRACE_BUS help Memory Storage Unit (MSU) trace output device enables storing STP traces to system memory. @@ -79,6 +80,7 @@ config INTEL_TH_MSU_DVC config INTEL_TH_MSU_DVC_DEBUG tristate "Intel Trace Hub Memory Storage Unit to USB-dvc debug" + depends on INTEL_TH_MSU_DVC help Memory Storage Unit (MSU) trace output device enables storing STP traces to system memory. From e5a1288db913b144a9cfb06e612811d17d40f550 Mon Sep 17 00:00:00 2001 From: "Jiang, YaoX" Date: Wed, 7 Feb 2018 14:27:27 +0800 Subject: [PATCH 0977/1276] Add control for ioc_slcan in kernel driver Customer has requirement to disable slcan on none IOC borad. Add slcan_diffconfig to control slcan in ablbc driver. CONFIG_SEND_SLCAN_ENABLE=y -> enable slcan CONFIG_SEND_SLCAN_ENABLE=n -> disable slcan Signed-off-by: Jiang, YaoX --- drivers/staging/android/abl/Kconfig | 9 +++++++++ drivers/staging/android/abl/ablbc.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig index a89ac5b5efb1..a9e81e7a7a23 100644 --- a/drivers/staging/android/abl/Kconfig +++ b/drivers/staging/android/abl/Kconfig @@ -6,3 +6,12 @@ config ABL_BOOTLOADER_CONTROL invoked with a string argument, the corresponding ABL Action is written in CMOS data, in order to be processed by ABL on reboot. + +config SEND_SLCAN_ENABLE + bool "control slcan protocol" + default n + help + This option control slcan protocol enable/disable in ablbc driver + The IOC compononent on broxton IVI platform use slcan protocol to + communicate befor calling powerctl program. + If no use IOC, this option can be disabed. diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 7b15fa39a43d..d9d751d806b1 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -310,6 +310,7 @@ static const char * const reboot_request[] = { static int execute_slcan_command(const char *cmd[]) { +#ifdef CONFIG_SEND_SLCAN_ENABLE struct subprocess_info *sub_info; int ret = -1; @@ -327,6 +328,9 @@ static int execute_slcan_command(const char *cmd[]) pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); return ret; +#else + return 0; +#endif } static int ablbc_reboot_notifier_call(struct notifier_block *notifier, From 7d6dbec0facce235cfb404ae3e79e90146ae80ae Mon Sep 17 00:00:00 2001 From: "Tang, Haoyu" Date: Thu, 22 Feb 2018 13:47:23 +0800 Subject: [PATCH 0978/1276] ablbc: panic if fail to call cansend If failed to cummunicate with IOC, IOC could take reboot as shutdown. To triger a panic will avoid this case by warm-reset. Signed-off-by: Tang, Haoyu --- drivers/staging/android/abl/ablbc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index d9d751d806b1..81c5c94ed5f7 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -337,7 +337,7 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { const char *target = (const char *)data; - int ret; + int ret = 0; if (what != SYS_RESTART) return NOTIFY_DONE; @@ -359,6 +359,10 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, ret = execute_slcan_command((const char **)cold_reset_capsule); done: +#ifdef CONFIG_SEND_SLCAN_ENABLE + if (ret) + panic("ablbc failed to cummnunicate with IOC."); +#endif return NOTIFY_DONE; } From 93a8746f82695c56bb25b285bcccbd4eba6b31e3 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Mon, 26 Feb 2018 13:16:04 +0800 Subject: [PATCH 0979/1276] Edit cold_reset/cold_reset_capsule commands Using HECI interface, no need keep toggle Set sus stat toggle use default 1 Just send cold_reset is enough Signed-off-by: jiangyao --- drivers/staging/android/abl/ablbc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 81c5c94ed5f7..73a3e34958c8 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -292,10 +292,10 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; -static const char * const cold_reset_capsule[] = { +static const char * const cold_reset[] = { "/vendor/bin/cansend", "slcan0", - "0000FFFF#05025555555555", + "0000FFFF#05015555555555", NULL}; static const char * const suppress_heartbeat[] = { "/vendor/bin/cansend", @@ -355,8 +355,8 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); } - if (capsule_request) - ret = execute_slcan_command((const char **)cold_reset_capsule); + + ret = execute_slcan_command((const char **)cold_reset); done: #ifdef CONFIG_SEND_SLCAN_ENABLE From 03febd854259e35ac615976d191a289a630ce501 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Thu, 15 Mar 2018 14:15:50 +0800 Subject: [PATCH 0980/1276] Fix the build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/hwtracing/intel_th/gth.c:572:19: warning: unused variable ‘th’ [-Wunused-variable] struct intel_th *th = to_intel_th(thdev); drivers/platform/x86/intel_pstore_pram.c:97:2: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘phys_addr_t’ [-Wformat=] pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", drivers/staging/android/sbl/sblbc.c:119:3: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 2 has type ‘size_t’ [-Wformat=] pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); drivers/staging/android/sbl/sblbc.c:314:1: warning: label ‘done’ defined but not used [-Wunused-label] done: Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/gth.c | 1 - drivers/platform/x86/intel_pstore_pram.c | 2 +- drivers/staging/android/sbl/sblbc.c | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 854411879008..2bc9b9f0b752 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -561,7 +561,6 @@ static int intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - struct intel_th *th = to_intel_th(thdev); u32 scrpd; int i; int ret = -EBUSY; diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c index 78dd9c3b08c5..8c8b1291545f 100644 --- a/drivers/platform/x86/intel_pstore_pram.c +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -95,7 +95,7 @@ static int register_pram_dev(unsigned long mem_address, } pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", - pram_data->mem_address, pram_data->mem_size); + (unsigned long)pram_data->mem_address, (unsigned long)pram_data->mem_size); return 0; } diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index 6c2d7ed6c2ee..d77700b70e25 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -116,7 +116,7 @@ static size_t write_data_to_nvram(char *data, size_t size) for (i = 0; i < size; i++) { - pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); + pr_err("Kernel Addr=0x%X, data=0x%X\n", (unsigned int)(NVRAM_START_ADDRESS + offset + i), (unsigned int)(*(unsigned char *)(data + i))); } offset += size; @@ -311,7 +311,6 @@ static int sblbc_reboot_notifier_call(struct notifier_block *notifier, __func__, ret); } -done: return NOTIFY_DONE; } From 697dcc91ac71e73197a6588530960474609378f0 Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Mon, 19 Mar 2018 12:50:29 +0800 Subject: [PATCH 0981/1276] staging/android: Virtual Slimboot boot Control driver Virtual Slimboot is bootloader of Android Guest OS run on hypervisor this patch Set reboot target in NVRAM - register to reboot event - create CDATA Virtual Slimboot code following reboot target Signed-off-by: zhouji3x --- drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/vsbl/Kconfig | 8 + drivers/staging/android/vsbl/Makefile | 1 + drivers/staging/android/vsbl/vsblbc.c | 262 ++++++++++++++++++++++++++ 5 files changed, 273 insertions(+) create mode 100644 drivers/staging/android/vsbl/Kconfig create mode 100644 drivers/staging/android/vsbl/Makefile create mode 100644 drivers/staging/android/vsbl/vsblbc.c diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 9f6b54a3c90f..0b793ab6d15f 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -26,6 +26,7 @@ config ANDROID_VSOC source "drivers/staging/android/ion/Kconfig" source "drivers/staging/android/abl/Kconfig" source "drivers/staging/android/sbl/Kconfig" +source "drivers/staging/android/vsbl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index fb345067b7f0..864902159d0c 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -3,6 +3,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sbl/ +obj-$(CONFIG_VSBL_BOOTLOADER_CONTROL) += vsbl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/vsbl/Kconfig b/drivers/staging/android/vsbl/Kconfig new file mode 100644 index 000000000000..bb53cf922685 --- /dev/null +++ b/drivers/staging/android/vsbl/Kconfig @@ -0,0 +1,8 @@ +config VSBL_BOOTLOADER_CONTROL + tristate "vSBL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/vsbl/Makefile b/drivers/staging/android/vsbl/Makefile new file mode 100644 index 000000000000..8ce038941fc6 --- /dev/null +++ b/drivers/staging/android/vsbl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VSBL_BOOTLOADER_CONTROL) += vsblbc.o diff --git a/drivers/staging/android/vsbl/vsblbc.c b/drivers/staging/android/vsbl/vsblbc.c new file mode 100644 index 000000000000..527a174f0130 --- /dev/null +++ b/drivers/staging/android/vsbl/vsblbc.c @@ -0,0 +1,262 @@ +/* + * vsblbc: control vSBL bootloaders + * Copyright (c) 2013-2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "vsblbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* vSBL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "crashmode", 0x04 }, + { "dnx", 0x05 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram((void*)nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram((void*)(nvram_msg->cdata_payload), + nvram_msg->cdata_payload_size); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = (void*)&reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static int vsblbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } + + return NOTIFY_DONE; +} + +static struct notifier_block vsblbc_reboot_notifier = { + .notifier_call = vsblbc_reboot_notifier_call, +}; + +static int __init vsblbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&vsblbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + return 0; +} + +module_init(vsblbc_init); + +static void __exit vsblbc_exit(void) +{ + unregister_reboot_notifier(&vsblbc_reboot_notifier); +} +module_exit(vsblbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Virtual Slimboot boot control driver"); +MODULE_LICENSE("GPL v2"); From 04b7f4d6eaadd8de0c8713a78b68d58c8b73e6a5 Mon Sep 17 00:00:00 2001 From: "Zhou, JianfengX" Date: Tue, 3 Apr 2018 11:54:57 +0800 Subject: [PATCH 0982/1276] vsblbc: only built on X86 virtual slimboot boot control driver is for X86 only. add depends on X86 to Kconfig Signed-off-by: Zhou, JianfengX --- drivers/staging/android/vsbl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/vsbl/Kconfig b/drivers/staging/android/vsbl/Kconfig index bb53cf922685..465fed22ca6b 100644 --- a/drivers/staging/android/vsbl/Kconfig +++ b/drivers/staging/android/vsbl/Kconfig @@ -1,5 +1,6 @@ config VSBL_BOOTLOADER_CONTROL tristate "vSBL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is From fc4e35c79665806cf31d7fbbf8c52c844fca9965 Mon Sep 17 00:00:00 2001 From: "Jiang, YaoX" Date: Sun, 8 Apr 2018 13:04:15 +0800 Subject: [PATCH 0983/1276] ablbc: print log target before panic If panic in ablbc due to cansend called failure, the log of dm-verity will not print. Log target before panic, so we can know the reason of restarting system. Signed-off-by: Jiang, YaoX --- drivers/staging/android/abl/ablbc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 73a3e34958c8..1cb31fa1af9a 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -360,8 +360,14 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, done: #ifdef CONFIG_SEND_SLCAN_ENABLE - if (ret) + if (ret) { + if (!target) + pr_emerg("Restarting system\n"); + else + pr_emerg("Restarting system with command '%s'\n", target); + panic("ablbc failed to cummnunicate with IOC."); + } #endif return NOTIFY_DONE; } From b770def07a18c7423e1764b47147b08a434f62d3 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Mon, 16 Apr 2018 16:03:24 +0800 Subject: [PATCH 0984/1276] add dependent for intel trace hub Intel trace hub is only used for x86 platform. Only in X86 architecture, this function can be enabled. Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index dc22562a16fc..4be9d14de4fb 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -1,6 +1,6 @@ config INTEL_TH tristate "Intel(R) Trace Hub controller" - depends on HAS_DMA && HAS_IOMEM + depends on HAS_DMA && HAS_IOMEM && X86 help Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that produce, switch and output trace data from multiple hardware and From 1065a9ac2d0518fffd03aa0790e2bb3c30fdcbea Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Tue, 17 Apr 2018 14:15:05 +0800 Subject: [PATCH 0985/1276] intel_th: sync "intel_th_alloc" function for ACPI ACPI probe will call the function "intel_th_alloc" Sync it with the "intel_th_alloc" Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/acpi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c index 87bc3744755f..5aef93d75ff2 100644 --- a/drivers/hwtracing/intel_th/acpi.c +++ b/drivers/hwtracing/intel_th/acpi.c @@ -34,6 +34,13 @@ static const struct acpi_device_id intel_th_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids); +static void intel_th_acpi_reset(struct intel_th *th) +{ + /* Software reset */ + + /* Always set FON for S0ix flow */ +} + static int intel_th_acpi_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); @@ -45,7 +52,7 @@ static int intel_th_acpi_probe(struct platform_device *pdev) return -ENODEV; th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, - pdev->resource, pdev->num_resources, -1); + pdev->resource, pdev->num_resources, -1,intel_th_acpi_reset); if (IS_ERR(th)) return PTR_ERR(th); From a8d36b1330e2625485cb0987ba99013ab56c3ac1 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Mon, 23 Apr 2018 10:12:52 +0800 Subject: [PATCH 0986/1276] abl and sbl are only used for x86 Automotive Boot Loader(abl) and slimboot boot loader(sbl) control driver are for X86 only. add depends on X86 to Kconfig Signed-off-by: Li, Hongli --- drivers/staging/android/abl/Kconfig | 2 ++ drivers/staging/android/sbl/Kconfig | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig index a9e81e7a7a23..3c0a2c566ba0 100644 --- a/drivers/staging/android/abl/Kconfig +++ b/drivers/staging/android/abl/Kconfig @@ -1,5 +1,6 @@ config ABL_BOOTLOADER_CONTROL tristate "ABL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is @@ -9,6 +10,7 @@ config ABL_BOOTLOADER_CONTROL config SEND_SLCAN_ENABLE bool "control slcan protocol" + depends on X86 default n help This option control slcan protocol enable/disable in ablbc driver diff --git a/drivers/staging/android/sbl/Kconfig b/drivers/staging/android/sbl/Kconfig index f5f754f8d890..4b550cadcb40 100644 --- a/drivers/staging/android/sbl/Kconfig +++ b/drivers/staging/android/sbl/Kconfig @@ -1,5 +1,6 @@ config SBL_BOOTLOADER_CONTROL tristate "SBL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is From b0d88db0037a50c97fb2cbc79320786b183fd5e1 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Mon, 7 May 2018 13:10:25 +0800 Subject: [PATCH 0987/1276] Fix compile warning in watchdog Compile warnings when make it on arch i386,fix it in iTCO_wdt.c Signed-off-by: jiangyao --- drivers/watchdog/iTCO_wdt.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 9a2a9b6b865c..26cdfd4126c1 100755 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -448,29 +448,29 @@ static const struct watchdog_ops iTCO_wdt_ops = { static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) { - resource_size_t tco_base_address; + resource_size_t tco_base_address; - /* Prevent re-entrance */ - if (iTCO_wdt_sub.pretimeout_occurred) - return NMI_HANDLED; + /* Prevent re-entrance */ + if (iTCO_wdt_sub.pretimeout_occurred) + return NMI_HANDLED; - tco_base_address = iTCO_wdt_sub.tco_base_address; + tco_base_address = iTCO_wdt_sub.tco_base_address; - /* Check the NMI is from the TCO first expiration */ - if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { - iTCO_wdt_sub.pretimeout_occurred = true; + /* Check the NMI is from the TCO first expiration */ + if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { + iTCO_wdt_sub.pretimeout_occurred = true; - /* Forward next expiration */ - outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); - outw(0x01, TCO_RLD_sub(tco_base_address)); + /* Forward next expiration */ + outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); + outw(0x01, TCO_RLD_sub(tco_base_address)); - trigger_all_cpu_backtrace(); - panic_timeout = 0; - panic("Kernel Watchdog"); - return NMI_HANDLED; - } + trigger_all_cpu_backtrace(); + panic_timeout = 0; + panic("Kernel Watchdog"); + return NMI_HANDLED; + } - return NMI_DONE; + return NMI_DONE; } /* From 5af1357e4804ebe0b56165d0b94fc8c265a7688b Mon Sep 17 00:00:00 2001 From: "Tang, Haoyu" Date: Sat, 19 May 2018 17:08:40 +0800 Subject: [PATCH 0988/1276] ablbc: revert patches of panic and revise logic Revert "ablbc: panic if fail to call cansend" Revert "ablbc: print log target before panic" Move setting boot-target before sending SLCAN message user-space service ioc_reboot send SLCAN reboot message to IOC during reboot phase also. cansend is a double safety at here, panic is not necessary for failure case. move the setting of reboot-target before sending SLCAN message in order to ensure the correct target set even failing to send SLCAN message. This reverts commit 1bbadb2ec5c7eb33a56bb77edee6ee73941bb1ea. This reverts commit bde2660bf18d560f029f759c1bb182d1b3a74119. Change-Id: Ic892a88b03c861e00198cb11211c82203f91779e Signed-off-by: Tang, Haoyu --- drivers/staging/android/abl/ablbc.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 1cb31fa1af9a..59154f5e10ef 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -337,10 +337,16 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { const char *target = (const char *)data; - int ret = 0; + int ret; if (what != SYS_RESTART) return NOTIFY_DONE; + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } ret = execute_slcan_command((const char **)suppress_heartbeat); if (ret) @@ -349,26 +355,10 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, ret = execute_slcan_command((const char **)reboot_request); if (ret) goto done; - if (target[0] != '\0') { - ret = set_reboot_target(target); - if (ret) - pr_err("%s: Failed to set reboot target, ret=%d\n", - __func__, ret); - } ret = execute_slcan_command((const char **)cold_reset); done: -#ifdef CONFIG_SEND_SLCAN_ENABLE - if (ret) { - if (!target) - pr_emerg("Restarting system\n"); - else - pr_emerg("Restarting system with command '%s'\n", target); - - panic("ablbc failed to cummnunicate with IOC."); - } -#endif return NOTIFY_DONE; } From 988a40e4dab19497a995764b43ee7caf20b56cfd Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Mon, 14 May 2018 21:11:48 +0000 Subject: [PATCH 0989/1276] Kernel: Fix below potential issue in dvctrace c file Issue description: 1. when "buf" is NULL, there is not a protection. 2. if there is not only one USB descriptor was saved in 'buf', below two rare issues maybe occur: 1) (descriptor length*2) > 'strlen(buf)' 'sscanf()' stops at the null terminator. No overflow, but the mismatch could be indicative of another problem resulting potentially in data loss. 2) (descriptor length*2) < 'strlen(buf)' No overflow, but data loss if there are more descriptors than accounted for in the length. Patch effect: 1. Ensuring 'buf' is not NULL. 2. Checking 'strlen(buf)' is consist of several (descriptor length*2) to make descriptor can be read correctly. 3. remove useless 'while(len)' loop that check real descriptor to enhance performance. 4. merge the code for 'lenth' 'type' and 'sub_type' checking to enhance performance. Change-Id: I4abd5194d91423b5b6201911881d0656f2055a59 Tracked-On: Signed-off-by: Duan, YayongX Signed-off-by: Tian, Baofeng --- drivers/bus/dvctrace.c | 63 +++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index 915c42593edb..a44b1b3e5490 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -42,45 +42,34 @@ static int count_descriptors(const char *buf, size_t size) { size_t off = 0; - int i, j, count = 0; - u8 len, tmp; + int j, count0 = 0, count1 = 0; + u8 len, type, sub_type; + if (buf == NULL) + return -EINVAL; + /* Ensuring 'buf' only has serval correct format USB descriptors */ + while(off < size) { + j = sscanf(buf + off, "%2hhx", &len); + if (j <= 0) + return -EINVAL; + /* skip related data and space to read next 'len': aa bb cc ... */ + off += len * 3; + count0++; + } + if (off != size) + return -EINVAL; + off = 0; DVCT_IN(); - while (off < size) { + /* Check every USB descriptor type and sub_type */ + while (count0--) { /*the length*/ - j = sscanf(buf + off, "%2hhx%n", &len, &i); - if (!j) - break; - if (j < 0 || len < 4) - return -EINVAL; - len--; - off += i; - - /*Type*/ - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0 || tmp != USB_DT_CS_INTERFACE) + j = sscanf(buf + off, "%2hhx%2hhx%2hhx", &len, &type, &sub_type); + if (j <= 0 || type != USB_DT_CS_INTERFACE || sub_type < DC_INPUT_CONNECTION || sub_type > DC_DEBUG_ATTRIBUTES) return -EINVAL; - len--; - off += i; - - /*Sub Type*/ - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0 || tmp < DC_INPUT_CONNECTION - || tmp > DC_DEBUG_ATTRIBUTES) - return -EINVAL; - len--; - off += i; - - while (len) { - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0) - return -EINVAL; - len--; - off += i; - } - count++; + off += len * 3; + count1++; } - return count; + return count1; } /* Parse @buf and get a pointer to the descriptor identified @@ -91,6 +80,8 @@ static u8 *get_descriptor(const char *buf, size_t size, int idx) int i, j, k, count = 0; u8 len, tmp, *ret = NULL; + if (buf == NULL) + return ERR_PTR(-EINVAL); DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%2hhx%n", &len, &i); @@ -318,6 +309,8 @@ static int count_strings(const char *buf, size_t size) size_t off = 0, slen; int i = 0, j, desc_offset, offset; + if (buf == NULL) + return -EINVAL; DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%d.%d: %n", &desc_offset, &offset, &i); @@ -346,6 +339,8 @@ static char *get_string(const char *buf, size_t size, int index, int i, j; char *ret = ERR_PTR(-EINVAL); + if (buf == NULL) + return ERR_PTR(-EINVAL); DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%d.%d: %n", desc_offset, offset, &i); From d15fc180912f8bee19dc9052c0e48c55b95e35c7 Mon Sep 17 00:00:00 2001 From: btian1 Date: Mon, 16 Jul 2018 16:21:18 +0000 Subject: [PATCH 0990/1276] kernel: ipc1: add a read-only attribute for PMC ssram base Add a read only attribute for PMC ssram base address, in ipc driver. This address will be used to retrieve intel crashlog data from PMC SSRAM. when fw crash happens, the logs are saved to ssram before reset. After reset, crashlogd read logs data out from this address for crash analysis. Signed-off-by: btian1 Signed-off-by: xinanlux --- drivers/platform/x86/intel_pmc_ipc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e7edc8c63936..a3ee24e49e96 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -606,15 +606,28 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev, } return (ssize_t)count; } +static ssize_t intel_ssrambase_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (ipcdev.telem_punit_ssram_base > TELEM_PUNIT_SSRAM_OFFSET) + return scnprintf(buf, 64, "%x\n", + ipcdev.telem_punit_ssram_base - TELEM_PUNIT_SSRAM_OFFSET); + else + return scnprintf(buf, 64, "%x\n", 0); +} static DEVICE_ATTR(simplecmd, S_IWUSR, NULL, intel_pmc_ipc_simple_cmd_store); static DEVICE_ATTR(northpeak, S_IWUSR, NULL, intel_pmc_ipc_northpeak_store); +static DEVICE_ATTR(ssrambase, S_IRUGO, + intel_ssrambase_show, NULL); static struct attribute *intel_ipc_attrs[] = { &dev_attr_northpeak.attr, &dev_attr_simplecmd.attr, + &dev_attr_ssrambase.attr, NULL }; From b268953e32a99dd1c054f7fa6f41f9538842ded5 Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Mon, 23 Jul 2018 09:03:57 +0000 Subject: [PATCH 0991/1276] Sbl: Klocwork: Fix the potential issue of sbl code - 'buf' is passed from a interface, which result in we cannot ensure what is involved in it. But there is not a security checking for copying 'buf' to 'name'. And this issue may cause a memory overflow of 'name'. - This patch add a checking for 'buf+3'(something will be copied to 'name' buffer) to keep no overflow occur in 'name' buffer. Change-Id: I9c57ba19bb5cec1849bd6b11aa87bd4e4e09ed52 Signed-off-by: Duan, YayongX --- drivers/staging/android/sbl/sblbc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index d77700b70e25..85865eb5d281 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -202,6 +202,11 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, device = (buf[0] == 'm' ? EMMC : SDCARD); partition = buf[1] - '0'; + if (strlen(buf+3) >= sizeof(name)) { + pr_err(MODULE_NAME " buf+3: %d is too long\n", strlen(buf+3)); + return -ENOMEM; + } + ret = sscanf(buf+3, "%s", name); pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", ret, device, partition, name); From ec450806b548a0153113359b232aa088415a01ea Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Mon, 30 Jul 2018 15:36:34 +0800 Subject: [PATCH 0992/1276] Flush CPU cache before reboot on panic This patch flush CPU cache when panic occurs, so data in the cache can be written back to RAM. This patch is neccessary for ramdump feature. If data in the cache is not written back to RAM before reboot, data pulled for offline analysis may be incorrect. Signed-off-by: zhouji3x --- kernel/panic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/panic.c b/kernel/panic.c index 8b2e002d52eb..d4f06598eef8 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -29,6 +29,7 @@ #include #include #include +#include #define PANIC_TIMER_STEP 100 #define PANIC_BLINK_SPD 18 @@ -246,6 +247,9 @@ void panic(const char *fmt, ...) debug_locks_off(); console_flush_on_panic(); + /* Flush CPU cache */ + ACPI_FLUSH_CPU_CACHE(); + if (!panic_blink) panic_blink = no_blink; From 2f44a0650b92fdd4f0f7410b6a9d7c254a9c69d4 Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Thu, 2 Aug 2018 09:56:56 +0800 Subject: [PATCH 0993/1276] Debug: Warning: Only fix some build warnings - For intel_pmc_ipc.c: 'ipcdev.telem_punit_ssram_base' is long long unsigned int type, So it request a '%llx' match with it. - For sblbc.c: Function 'strlen' is 'sized_t' type, So it return a unsigned long int type, So here need a '%ld' matching. - For printk.c: Function 'console_solw_suspend' was already not used, So remove it. Change-Id: I22208302d125e8654592f45e200551eb9fa67a11 Signed-off-by: Duan, YayongX --- drivers/platform/x86/intel_pmc_ipc.c | 2 +- drivers/staging/android/sbl/sblbc.c | 2 +- kernel/printk/printk.c | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index a3ee24e49e96..ffe0cac68694 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -611,7 +611,7 @@ static ssize_t intel_ssrambase_show(struct device *dev, char *buf) { if (ipcdev.telem_punit_ssram_base > TELEM_PUNIT_SSRAM_OFFSET) - return scnprintf(buf, 64, "%x\n", + return scnprintf(buf, 64, "%llx\n", ipcdev.telem_punit_ssram_base - TELEM_PUNIT_SSRAM_OFFSET); else return scnprintf(buf, 64, "%x\n", 0); diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index 85865eb5d281..3d354fd31e23 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -203,7 +203,7 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, device = (buf[0] == 'm' ? EMMC : SDCARD); partition = buf[1] - '0'; if (strlen(buf+3) >= sizeof(name)) { - pr_err(MODULE_NAME " buf+3: %d is too long\n", strlen(buf+3)); + pr_err(MODULE_NAME " buf+3: %ld is too long\n", strlen(buf+3)); return -ENOMEM; } diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 578cad2f66a0..948187ea2f59 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -377,8 +377,6 @@ __packed __aligned(4) */ DEFINE_RAW_SPINLOCK(logbuf_lock); -/* Give the posibility to temporary disable slow (!CON_FAST) consoles */ -static atomic_t console_slow_suspended = ATOMIC_INIT(0); /* Keep the number of slow suspend in check */ #define MAX_SLOW_SUSPEND_COUNT (50) From 4406f41122989256a3ded673887544c7624e6bc5 Mon Sep 17 00:00:00 2001 From: "Luo, XinanX" Date: Thu, 14 Jun 2018 23:29:47 +0000 Subject: [PATCH 0994/1276] wdt:reboot at second watchdog timeout This patch is to implement watchdog dump kernel logs on first timeout, and then reboot on second timeout. Only for the platforms that iTCO watchdog can't trigger NMI interrupts. Because iTCO can only trigger SMI, need BIOS/Bootloader use SMM to translate it to NMI. But on some platforms, BIOS/Bootloader doesn't support SMM. Modifications: 1, Add a perf_event to trigger periodic NMI interrupt, because some platforms' iTCO can't trigger NMI interrupts for use. 2, Disable reboot bit when first timeout, then it will not reboot after about 2s(HW default). It will waiting for a NMI interrupt and check the iTCO status. If timeout, then dump kernel log and enable the second timeout in iTCO to reboot system. 3, This implement are controlled by a config: CONFIG_ITCO_NO_NMI_INTR defaut n. If iTCO can't trigger NMI interrupts, set CONFIG_ITCO_NO_NMI_INTR=y to enable it. Change-Id: Id41cb5f5b480c1614afa53732dd92a8febf1362f Signed-off-by: Luo, XinanX --- drivers/watchdog/Kconfig | 9 ++++ drivers/watchdog/iTCO_wdt.c | 94 ++++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8bf318d44a88..b2dd4ac2fc63 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1083,6 +1083,15 @@ config ITCO_WDT To compile this driver as a module, choose M here: the module will be called iTCO_wdt. +config ITCO_NO_NMI_INTR + bool "Intel TCO Watchdog NMI interrupt state" + depends on ITCO_WDT + default n + help + On some platforms, the iTCO watchdog can't trigger a NMI + interrupt on the first timeout. Need to set this flag to enable + another way instead. + config ITCO_VENDOR_SUPPORT bool "Intel TCO Timer/Watchdog Specific Vendor Support" depends on ITCO_WDT diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 26cdfd4126c1..0285c0be8907 100755 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -70,6 +70,7 @@ #include #include +#include #include "iTCO_vendor.h" /* Address definitions for the TCO */ @@ -124,6 +125,9 @@ static struct { unsigned int iTCO_version; bool pretimeout_occurred; unsigned int second_to_ticks; +#ifdef CONFIG_ITCO_NO_NMI_INTR + struct iTCO_wdt_private *wdt_priv; +#endif } iTCO_wdt_sub; @@ -257,6 +261,61 @@ static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, p->no_reboot_priv = p; } +/* iTCO need to wait for a NMI interrupt on first timeout, in order to dump kernel +** logs in the interrupt handler-iTCO_pretimeout. But on some platforms iTCO timeout +** can't triger NMI interrupts.These functions will trigger periodly NMI +** interrupts by perf_event. In iTCO_pretimeout will check iTCO status, if timeout, then +** dump logs and reboot. +*/ +#ifdef CONFIG_ITCO_NO_NMI_INTR +static struct perf_event_attr wd_hw_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .size = sizeof(struct perf_event_attr), + .pinned = 1, + .disabled = 1, +}; +static struct perf_event *itco_perf_evt = NULL; +static unsigned int itco_perf_sample_period = 10; //default 10 seconds +static int itco_perf_event_create(void) +{ + unsigned int cpu = smp_processor_id(); + struct perf_event_attr *wd_attr; + + wd_attr = &wd_hw_attr; + wd_attr->sample_period = (u64)(cpu_khz) * 1000 * itco_perf_sample_period; + + /* Try to register using hardware perf events */ + itco_perf_evt = perf_event_create_kernel_counter(wd_attr, cpu, NULL, + NULL, NULL); + if (IS_ERR(itco_perf_evt)) { + pr_err("Perf event create on CPU %d failed with %ld\n", cpu, + PTR_ERR(itco_perf_evt)); + return PTR_ERR(itco_perf_evt); + } + return 0; +} +static void itco_perf_event_enable(void) +{ + if (itco_perf_evt) + perf_event_enable(itco_perf_evt); +} +static void itco_perf_event_disable(void) +{ + if (itco_perf_evt) + perf_event_disable(itco_perf_evt); +} +static void itco_perf_event_remove(void) +{ + if (itco_perf_evt) + perf_event_release_kernel(itco_perf_evt); +} +#else +static int itco_perf_event_create(void) { return 0; } +static void itco_perf_event_enable(void) { } +static void itco_perf_event_disable(void) { } +static void itco_perf_event_remove(void) { } +#endif static int iTCO_wdt_start(struct watchdog_device *wd_dev) { @@ -267,16 +326,27 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) if (force_no_reboot) return -EIO; + itco_perf_event_enable(); + spin_lock(&p->io_lock); iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); +#ifdef CONFIG_ITCO_NO_NMI_INTR + /* Set the NO_REBOOT bit to prevent reboots of first timeout */ + if (p->update_no_reboot_bit(p->no_reboot_priv, true)) { + spin_unlock(&p->io_lock); + pr_err("failed to set NO_REBOOT flag\n"); + return -EIO; + } +#else /* disable chipset's NO_REBOOT bit */ if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { spin_unlock(&p->io_lock); pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS/rc_cmd\n"); return -EIO; } +#endif /* Force the timer to its reload value by writing to the TCO_RLD register */ @@ -302,6 +372,8 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; + itco_perf_event_disable(); + spin_lock(&p->io_lock); iTCO_vendor_pre_stop(p->smi_res); @@ -449,6 +521,9 @@ static const struct watchdog_ops iTCO_wdt_ops = { static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) { resource_size_t tco_base_address; +#ifdef CONFIG_ITCO_NO_NMI_INTR + struct iTCO_wdt_private *p = iTCO_wdt_sub.wdt_priv; +#endif /* Prevent re-entrance */ if (iTCO_wdt_sub.pretimeout_occurred) @@ -459,6 +534,10 @@ static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) /* Check the NMI is from the TCO first expiration */ if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { iTCO_wdt_sub.pretimeout_occurred = true; +#ifdef CONFIG_ITCO_NO_NMI_INTR + /*Enable reboot for the second timeout*/ + p->update_no_reboot_bit(p->no_reboot_priv, false); +#endif /* Forward next expiration */ outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); @@ -606,11 +685,20 @@ static int iTCO_wdt_probe(struct platform_device *pdev) return ret; } + /*create a perf_event to generate nmi interrupt*/ + ret = itco_perf_event_create(); + if (ret != 0) { + pr_err("cannot create perf event (err=%d)\n", ret); + return ret; + } + /* init vars that used for nmi handler */ iTCO_wdt_sub.iTCO_version = p->iTCO_version; iTCO_wdt_sub.second_to_ticks = seconds_to_ticks(p, 10); iTCO_wdt_sub.tco_base_address = TCOBASE(p); - +#ifdef CONFIG_ITCO_NO_NMI_INTR + iTCO_wdt_sub.wdt_priv = p; +#endif ret = register_nmi_handler(NMI_LOCAL, iTCO_pretimeout, 0 ,"iTCO_wdt"); if (ret != 0) { pr_err("cannot register nmi handler (err=%d)\n", ret); @@ -628,8 +716,10 @@ static int iTCO_wdt_remove(struct platform_device *pdev) struct iTCO_wdt_private *p = platform_get_drvdata(pdev); /* Stop the timer before we leave */ - if (!nowayout) + if (!nowayout) { iTCO_wdt_stop(&p->wddev); + itco_perf_event_remove(); + } return 0; } From c665890bc792fbd9553c75bf035cb332d52e0d37 Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 4 Jul 2018 10:47:08 +0800 Subject: [PATCH 0995/1276] Increase COMMAND_LINE_SIZE from 2048 to 4096 The real size of kernel command line exceeds 2048. Signed-off-by: Chen, ZhiminX --- arch/x86/include/asm/setup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index ae13bc974416..9490cb15a275 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -4,7 +4,7 @@ #include -#define COMMAND_LINE_SIZE 2048 +#define COMMAND_LINE_SIZE 4096 #include #include From 1bb7f41f050eae20e40c1b1b425773067d68d3dc Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 8 Aug 2018 22:04:48 +0800 Subject: [PATCH 0996/1276] When kernel crash, save the reboot reason to EFI variable. Change-Id: Ib785ffdd8991c485b1ab3733f9883dc67ec9053e Signed-off-by: Jeremy Compostella Signed-off-by: Xinanx Luo --- drivers/firmware/efi/efibc.c | 51 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/efibc.c b/drivers/firmware/efi/efibc.c index 503bbe2a9d49..ff145f39e8ca 100644 --- a/drivers/firmware/efi/efibc.c +++ b/drivers/firmware/efi/efibc.c @@ -19,6 +19,15 @@ #include #include +#define REBOOT_REASON_CRASH "kernel_panic" +#define REBOOT_REASON_NORMAL "reboot" +#define REBOOT_REASON_SHUTDOWN "shutdown" +#define REBOOT_REASON_WATCHDOG "watchdog" + +#define WATCHDOG_KERNEL_H "Watchdog" +#define WATCHDOG_KERNEL_S "softlockup" +#define WATCHDOG_KERNEL_D "Software Watchdog" + static void efibc_str_to_str16(const char *str, efi_char16_t *str16) { size_t i; @@ -67,11 +76,11 @@ static int efibc_set_variable(const char *name, const char *value) static int efibc_reboot_notifier_call(struct notifier_block *notifier, unsigned long event, void *data) { - const char *reason = "shutdown"; + const char *reason = REBOOT_REASON_SHUTDOWN; int ret; if (event == SYS_RESTART) - reason = "reboot"; + reason = REBOOT_REASON_NORMAL; ret = efibc_set_variable("LoaderEntryRebootReason", reason); if (ret || !data) @@ -82,10 +91,41 @@ static int efibc_reboot_notifier_call(struct notifier_block *notifier, return NOTIFY_DONE; } +static int efibc_panic_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + int i; + char *str = data; + const char *reason = REBOOT_REASON_CRASH; + const char *watchdogs[] = { + WATCHDOG_KERNEL_H, + WATCHDOG_KERNEL_S, + WATCHDOG_KERNEL_D + }; + + + if (str) { + for (i = 0; i < ARRAY_SIZE(watchdogs); i++) { + if (strncmp(str, watchdogs[i], strlen(watchdogs[i])) == 0) { + reason = REBOOT_REASON_WATCHDOG; + break; + } + } + } + + efibc_set_variable("LoaderEntryRebootReason", reason); + + return NOTIFY_DONE; +} + static struct notifier_block efibc_reboot_notifier = { .notifier_call = efibc_reboot_notifier_call, }; +static struct notifier_block paniced = { + .notifier_call = efibc_panic_notifier_call, +}; + static int __init efibc_init(void) { int ret; @@ -94,8 +134,12 @@ static int __init efibc_init(void) return -ENODEV; ret = register_reboot_notifier(&efibc_reboot_notifier); - if (ret) + if (ret) { pr_err("unable to register reboot notifier\n"); + return ret; + } + + atomic_notifier_chain_register(&panic_notifier_list, &paniced); return ret; } @@ -104,6 +148,7 @@ module_init(efibc_init); static void __exit efibc_exit(void) { unregister_reboot_notifier(&efibc_reboot_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); } module_exit(efibc_exit); From 7da66d3bd83250c047ffa9dbc59b20ee6266f5cb Mon Sep 17 00:00:00 2001 From: "Luo, Xinanx" Date: Fri, 27 Jul 2018 15:01:13 +0800 Subject: [PATCH 0997/1276] watchdog: add an IRQ handler to dump log on first timeout Add an IRQ handler to dump kernel logs when i6300esb timeout for the first time(30s) Modifications: 1,Alloc an IRQ and register a handler for it at probe of driver 2,Set i6300esb interrupt type of first stage timeout to IRQ 3,Check if it's i6300esb interrupt in handler, if yes,then dump kernel logs, and then panic. Then system will reboot at second stage timeout(30s) Change-Id: I26567fa8b8ab29fcb0c9a99f462e772f4f65d53a Signed-off-by: Luo, Xinanx Signed-off-by: Tian, Baofeng --- drivers/watchdog/Kconfig | 2 +- drivers/watchdog/i6300esb.c | 47 +++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b2dd4ac2fc63..5396f58b444b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1013,7 +1013,7 @@ config WAFER_WDT module will be called wafer5823wdt. config I6300ESB_WDT - tristate "Intel 6300ESB Timer/Watchdog" + bool "Intel 6300ESB Timer/Watchdog" depends on PCI select WATCHDOG_CORE ---help--- diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 950c71a8bb22..676bd72fb524 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -41,6 +41,7 @@ #include #include #include +#include /* Module and version information */ #define ESB_MODULE_NAME "i6300ESB timer" @@ -69,6 +70,9 @@ #define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */ #define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ +/* General Interrupt Status Register bits*/ +#define ESB_WDT_INTR_ACTIVE (0x01 << 0) /* Intr happens on first timeout*/ + /* Magic constants */ #define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ #define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ @@ -217,9 +221,34 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl); /* * Init & exit routines */ +static irqreturn_t esb_pretimeout(int irq, void *dev_id) +{ + u16 val = 0; + struct esb_dev *edev = NULL; + if (!dev_id) + return IRQ_NONE; + edev = dev_get_drvdata(&((struct pci_dev *)dev_id)->dev); + if (!edev) + return IRQ_NONE; + + val = readw(ESB_GINTSR_REG(edev)); + if (val & ESB_WDT_INTR_ACTIVE) { + //clear interrupt status bit + writew(ESB_WDT_INTR_ACTIVE, ESB_GINTSR_REG(edev)); + + //dump logs + trigger_all_cpu_backtrace(); + panic_timeout = 0; + panic("Kernel Watchdog"); + return IRQ_HANDLED; + } + return IRQ_NONE; +} static unsigned char esb_getdevice(struct esb_dev *edev) { + int nvec, ret; + if (pci_enable_device(edev->pdev)) { dev_err(&edev->pdev->dev, "failed to enable device\n"); goto err_devput; @@ -236,7 +265,16 @@ static unsigned char esb_getdevice(struct esb_dev *edev) dev_err(&edev->pdev->dev, "failed to get BASEADDR\n"); goto err_release; } - + nvec = pci_alloc_irq_vectors(edev->pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (nvec < 0) + pr_err("failed to alloc irq\n"); + else { + edev->pdev->irq = pci_irq_vector(edev->pdev, 0); + ret = request_irq(edev->pdev->irq, esb_pretimeout, 0, + "i6300esb", edev->pdev); + if (ret) + pr_err("failed to request_irq\n"); + } /* Done */ dev_set_drvdata(&edev->pdev->dev, edev); return 1; @@ -266,7 +304,7 @@ static void esb_initdevice(struct esb_dev *edev) * any interrupts as there is not much we can do with it * right now. */ - pci_write_config_word(edev->pdev, ESB_CONFIG_REG, 0x0003); + pci_write_config_word(edev->pdev, ESB_CONFIG_REG, 0x0000); /* Check that the WDT isn't already locked */ pci_read_config_byte(edev->pdev, ESB_LOCK_REG, &val1); @@ -345,6 +383,11 @@ static void esb_remove(struct pci_dev *pdev) watchdog_unregister_device(&edev->wdd); iounmap(edev->base); + + if (edev->pdev->irq) { + free_irq(edev->pdev->irq, edev->pdev); + pci_free_irq_vectors(edev->pdev); + } pci_release_region(edev->pdev, 0); pci_disable_device(edev->pdev); } From 01416a3ce8a0d4b549b5a3fd2516c8b9ac9a2c9b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 1 Feb 2015 10:17:24 +0200 Subject: [PATCH 0998/1276] rpmb: add Replay Protected Memory Block (RPMB) subsystem Few storage technologies such is EMMC, UFS, and NVMe support RPMB a hardware partition with common protocol and frame layout. The RPMB partition cannot be accessed via standard block layer, but by a set of specific commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a partition provides authenticated and replay protected access, hence suitable as a secure storage. The RPMB layer aims to provide in-kernel API for Trusted Execution Environment (TEE) devices that are capable to securely compute block frame signature. In case a TEE device wishes to store a replay protected data, it creates an RPMB frame with requested data and computes HMAC of the frame, then it requests the storage device via RPMB layer to store the data. A TEE device driver can claim the RPMB interface, for example, via class_interface_register(). The RPMB layer provides an API for issuing a sequence of RPMB protocol frames via rpmb_cmd_seq() call. A storage device registers its RPMB (eMMC) partition, RPMB W-LUN (UFS), or RPMB target NVMe with the RPMB layer providing an implementation for rpmb_cmd_seq() handler, that enables sending sequence of RPMB standard frames and set of attributes. V2: added short workflow description in the commit message V3: commit message fix V4: resend V5: add rpmb sequence interface. V6: 1. More info in the commit message 2. Define simulation device type V7: resend V8: 1. Add rpmb_cmd_req_write/read helper functions. 2. Fix minor checkpatch warning. 3. Change the license to Dual BSD/GPL V9: 1. Drop rpmb_cmd_req interface. 2. Add NVME type 3. Support for multiple RPMB partition on same device. 4. Add additional information about partition. 5. Add driver data access functions. 6. Add SPDX identifiers. 7. Unexport rpmb_dev_find_device() Change-Id: I830751859c2aed519c41a8123bd96c7a7243262a Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- MAINTAINERS | 7 + drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/rpmb/Kconfig | 9 + drivers/char/rpmb/Makefile | 5 + drivers/char/rpmb/core.c | 333 +++++++++++++++++++++++++++++++++++++ include/linux/rpmb.h | 250 ++++++++++++++++++++++++++++ 7 files changed, 607 insertions(+) create mode 100644 drivers/char/rpmb/Kconfig create mode 100644 drivers/char/rpmb/Makefile create mode 100644 drivers/char/rpmb/core.c create mode 100644 include/linux/rpmb.h diff --git a/MAINTAINERS b/MAINTAINERS index b2f710eee67a..3c99a53c4590 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12527,6 +12527,13 @@ F: include/net/rose.h F: include/uapi/linux/rose.h F: net/rose/ +RPMB SUBSYSTEM +M: Tomas Winkler +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/char/rpmb/* +F: include/linux/rpmb.h + RTL2830 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 40728491f37b..26a2da8dde63 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -552,6 +552,8 @@ config ADI and SSM (Silicon Secured Memory). Intended consumers of this driver include crash and makedumpfile. +source "drivers/char/rpmb/Kconfig" + endmenu config RANDOM_TRUST_CPU diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b8d42b4e979b..88764b76d975 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,3 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_RPMB) += rpmb/ diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig new file mode 100644 index 000000000000..b5cd02de91bb --- /dev/null +++ b/drivers/char/rpmb/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +config RPMB + tristate "RPMB partition interface" + help + Unified RPMB partition interface for eMMC and UFS. + Provides interface for in kernel security controllers to + access RPMB partition. + + If unsure, select N. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile new file mode 100644 index 000000000000..badc1cd9428b --- /dev/null +++ b/drivers/char/rpmb/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RPMB) += rpmb.o +rpmb-objs += core.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c new file mode 100644 index 000000000000..69a590106ae1 --- /dev/null +++ b/drivers/char/rpmb/core.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +static DEFINE_IDA(rpmb_ida); + +/** + * rpmb_dev_get - increase rpmb device ref counter + * + * @rdev: rpmb device + */ +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return get_device(&rdev->dev) ? rdev : NULL; +} +EXPORT_SYMBOL_GPL(rpmb_dev_get); + +/** + * rpmb_dev_put - decrease rpmb device ref counter + * + * @rdev: rpmb device + */ +void rpmb_dev_put(struct rpmb_dev *rdev) +{ + put_device(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_put); + +/** + * rpmb_cmd_seq - send RPMB command sequence + * + * @rdev: rpmb device + * @cmds: rpmb command list + * @ncmds: number of commands + * + * Return: 0 on success + * -EINVAL on wrong parameters + * -EOPNOTSUPP if device doesn't support the requested operation + * < 0 if the operation fails + */ +int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds) +{ + int err; + + if (!rdev || !cmds || !ncmds) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->cmd_seq) { + err = rdev->ops->cmd_seq(rdev->dev.parent, rdev->target, + cmds, ncmds); + } + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_cmd_seq); + +int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + int err; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->get_capacity) + err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target); + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_get_capacity); + +static void rpmb_dev_release(struct device *dev) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + ida_simple_remove(&rpmb_ida, rdev->id); + kfree(rdev); +} + +struct class rpmb_class = { + .name = "rpmb", + .owner = THIS_MODULE, + .dev_release = rpmb_dev_release, +}; +EXPORT_SYMBOL(rpmb_class); + +/** + * rpmb_dev_find_device - return first matching rpmb device + * + * @data: data for the match function + * @match: the matching function + * + * Return: matching rpmb device or NULL on failure + */ +static +struct rpmb_dev *rpmb_dev_find_device(const void *data, + int (*match)(struct device *dev, + const void *data)) +{ + struct device *dev; + + dev = class_find_device(&rpmb_class, NULL, data, match); + + return dev ? to_rpmb_dev(dev) : NULL; +} + +static int match_by_type(struct device *dev, const void *data) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + const u32 *type = data; + + return (*type == RPMB_TYPE_ANY || rdev->ops->type == *type); +} + +/** + * rpmb_dev_get_by_type - return first registered rpmb device + * with matching type. + * If run with RPMB_TYPE_ANY the first an probably only + * device is returned + * + * @type: rpbm underlying device type + * + * Return: matching rpmb device or NULL/ERR_PTR on failure + */ +struct rpmb_dev *rpmb_dev_get_by_type(u32 type) +{ + if (type > RPMB_TYPE_MAX) + return ERR_PTR(-EINVAL); + + return rpmb_dev_find_device(&type, match_by_type); +} +EXPORT_SYMBOL_GPL(rpmb_dev_get_by_type); + +struct device_with_target { + const struct device *dev; + u8 target; +}; + +static int match_by_parent(struct device *dev, const void *data) +{ + const struct device_with_target *d = data; + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return (d->dev && dev->parent == d->dev && rdev->target == d->target); +} + +/** + * rpmb_dev_find_by_device - retrieve rpmb device from the parent device + * + * @parent: parent device of the rpmb device + * @target: RPMB target/region within the physical device + * + * Return: NULL if there is no rpmb device associated with the parent device + */ +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target) +{ + struct device_with_target t; + + if (!parent) + return NULL; + + t.dev = parent; + t.target = target; + + return rpmb_dev_find_device(&t, match_by_parent); +} +EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device); + +/** + * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem + * + * @rdev: the rpmb device to unregister + */ +int rpmb_dev_unregister(struct rpmb_dev *rdev) +{ + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + device_del(&rdev->dev); + mutex_unlock(&rdev->lock); + + rpmb_dev_put(rdev); + + return 0; +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister); + +/** + * rpmb_dev_unregister_by_device - unregister RPMB partition + * from the RPMB subsystem + * + * @dev: the parent device of the rpmb device + * @target: RPMB target/region within the physical device + */ +int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + struct rpmb_dev *rdev; + + if (!dev) + return -EINVAL; + + rdev = rpmb_dev_find_by_device(dev, target); + if (!rdev) { + dev_warn(dev, "no disk found %s\n", dev_name(dev->parent)); + return -ENODEV; + } + + rpmb_dev_put(rdev); + + return rpmb_dev_unregister(rdev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device); + +/** + * rpmb_dev_get_drvdata - driver data getter + * + * @rdev: rpmb device + * + * Return: driver private data + */ +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return dev_get_drvdata(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata); + +/** + * rpmb_dev_set_drvdata - driver data setter + * + * @rdev: rpmb device + * @data: data to store + */ +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ + dev_set_drvdata(&rdev->dev, data); +} +EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata); + +/** + * rpmb_dev_register - register RPMB partition with the RPMB subsystem + * + * @dev: storage device of the rpmb device + * @target: RPMB target/region within the physical device + * @ops: device specific operations + */ +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops) +{ + struct rpmb_dev *rdev; + int id; + int ret; + + if (!dev || !ops) + return ERR_PTR(-EINVAL); + + if (!ops->cmd_seq) + return ERR_PTR(-EINVAL); + + if (!ops->get_capacity) + return ERR_PTR(-EINVAL); + + if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX) + return ERR_PTR(-EINVAL); + + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return ERR_PTR(-ENOMEM); + + id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto exit; + } + + mutex_init(&rdev->lock); + rdev->ops = ops; + rdev->id = id; + rdev->target = target; + + dev_set_name(&rdev->dev, "rpmb%d", id); + rdev->dev.class = &rpmb_class; + rdev->dev.parent = dev; + ret = device_register(&rdev->dev); + if (ret) + goto exit; + + dev_dbg(&rdev->dev, "registered device\n"); + + return rdev; + +exit: + if (id >= 0) + ida_simple_remove(&rpmb_ida, id); + kfree(rdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rpmb_dev_register); + +static int __init rpmb_init(void) +{ + ida_init(&rpmb_ida); + class_register(&rpmb_class); + return 0; +} + +static void __exit rpmb_exit(void) +{ + class_unregister(&rpmb_class); + ida_destroy(&rpmb_ida); +} + +subsys_initcall(rpmb_init); +module_exit(rpmb_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("RPMB class"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h new file mode 100644 index 000000000000..6acd9b1e70f6 --- /dev/null +++ b/include/linux/rpmb.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef __RPMB_H__ +#define __RPMB_H__ + +#include +#include +#include + +/** + * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + */ +struct rpmb_frame_jdec { + u8 stuff[196]; + u8 key_mac[32]; + u8 data[256]; + u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __packed; + +#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ + +#define RPMB_REQ2RESP(_OP) ((_OP) << 8) +#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) + +/** + * enum rpmb_op_result - rpmb operation results + * + * @RPMB_ERR_OK : operation successful + * @RPMB_ERR_GENERAL : general failure + * @RPMB_ERR_AUTH : mac doesn't match or ac calculation failure + * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure + * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment + * @RPMB_ERR_WRITE : data, counter, or result write failure + * @RPMB_ERR_READ : data, counter, or result read failure + * @RPMB_ERR_NO_KEY : authentication key not yet programmed + * + * @RPMB_ERR_COUNTER_EXPIRED: counter expired + */ +enum rpmb_op_result { + RPMB_ERR_OK = 0x0000, + RPMB_ERR_GENERAL = 0x0001, + RPMB_ERR_AUTH = 0x0002, + RPMB_ERR_COUNTER = 0x0003, + RPMB_ERR_ADDRESS = 0x0004, + RPMB_ERR_WRITE = 0x0005, + RPMB_ERR_READ = 0x0006, + RPMB_ERR_NO_KEY = 0x0007, + + RPMB_ERR_COUNTER_EXPIRED = 0x0080 +}; + +/** + * enum rpmb_type - type of underlying storage technology + * + * @RPMB_TYPE_ANY : any type used for search only + * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) + * @RPMB_TYPE_UFS : UFS (JESD220) + * @RPMB_TYPE_NVME : NVM Express Revision 1.3a + * @RPMB_TYPE_SIM : Simulation device. + * @RPMB_TYPE_MAX : upper sentinel + */ +enum rpmb_type { + RPMB_TYPE_ANY = 0, + RPMB_TYPE_EMMC, + RPMB_TYPE_UFS, + RPMB_TYPE_NVME, + + RPMB_TYPE_SIM = 0x0100, + RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, +}; + +#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) + +extern struct class rpmb_class; + +#define RPMB_F_WRITE BIT(0) +#define RPMB_F_REL_WRITE BIT(1) + +/** + * struct rpmb_cmd: rpmb access command + * + * @flags: command flags + * 0 - read command + * 1 - write command RPMB_F_WRITE + * 2 - reliable write RPMB_F_REL_WRITE + * @nframes: number of rpmb frames in the command + * @frames: list of rpmb frames + */ +struct rpmb_cmd { + u32 flags; + u32 nframes; + void *frames; +}; + +enum rpmb_auth_method { + RPMB_HMAC_ALGO_SHA_256 = 0, +}; + +/** + * struct rpmb_ops - RPMB ops to be implemented by underlying block device + * + * @cmd_seq : send RPMB command sequence to the RPBM partition + * backed by the storage device to specific + * region(UFS)/target(NVMe) + * @get_capacity : rpmb size in 128K units in for region/target. + * @type : block device type eMMC, UFS, NVMe. + * @block_size : block size in half sectors (1 == 256B) + * @wr_cnt_max : maximal number of blocks that can be + * written in one access. + * @rd_cnt_max : maximal number of blocks that can be + * read in one access. + * @auth_method : rpmb_auth_method + * @dev_id : unique device identifier + * @dev_id_len : unique device identifier length + */ +struct rpmb_ops { + int (*cmd_seq)(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds); + int (*get_capacity)(struct device *dev, u8 target); + u32 type; + u16 block_size; + u16 wr_cnt_max; + u16 rd_cnt_max; + u16 auth_method; + const u8 *dev_id; + size_t dev_id_len; +}; + +/** + * struct rpmb_dev - device which can support RPMB partition + * + * @lock : the device lock + * @dev : device + * @id : device id + * @target : RPMB target/region within the physical device + * @ops : operation exported by block layer + */ +struct rpmb_dev { + struct mutex lock; /* device serialization lock */ + struct device dev; + int id; + u8 target; + const struct rpmb_ops *ops; +}; + +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev) + +#if IS_ENABLED(CONFIG_RPMB) +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev); +void rpmb_dev_put(struct rpmb_dev *rdev); +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target); +struct rpmb_dev *rpmb_dev_get_by_type(u32 type); +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops); +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev); +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data); +int rpmb_dev_unregister(struct rpmb_dev *rdev); +int rpmb_dev_unregister_by_device(struct device *dev, u8 target); +int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds); +int rpmb_get_capacity(struct rpmb_dev *rdev); + +#else +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { } + +static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, + u8 target) +{ + return NULL; +} + +static inline +struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type) +{ + return NULL; +} + +static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ +} + +static inline struct rpmb_dev * +rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops) +{ + return NULL; +} + +static inline int rpmb_dev_unregister(struct rpmb_dev *dev) +{ + return 0; +} + +static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + return 0; +} + +static inline int rpmb_cmd_seq(struct rpmb_dev *rdev, + struct rpmb_cmd *cmds, u32 ncmds) +{ + return 0; +} + +static inline int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + return 0; +} + +#endif /* CONFIG_RPMB */ + +#endif /* __RPMB_H__ */ From 67c80ae4077e82a8311573a223bed1f9544a7fb9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Aug 2016 11:27:08 +0300 Subject: [PATCH 0999/1276] rpmb: enable emmc specific read data fixup For eMMC the block count of the RPMB read operation is not indicated in the original RPMB Data Read Request packet. This might be different then the implementation of other protocol standards. This patch implements a fixup for this behavior. V6: New in the series. V7: Resend V8: Resend. V9: Scan all the frames in the sequence. Change-Id: I34a4aeccbd0294b2c7c83837faa4ba5a54b9be48 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- drivers/char/rpmb/core.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 69a590106ae1..3bf3f0db54fb 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -36,6 +36,38 @@ void rpmb_dev_put(struct rpmb_dev *rdev) } EXPORT_SYMBOL_GPL(rpmb_dev_put); +/** + * rpmb_cmd_fixup - fixup rpmb command + * + * @rdev: rpmb device + * @cmds: rpmb command list + * @ncmds: number of commands + * + */ +static void rpmb_cmd_fixup(struct rpmb_dev *rdev, + struct rpmb_cmd *cmds, u32 ncmds) +{ + int i; + + if (RPMB_TYPE_HW(rdev->ops->type) != RPMB_TYPE_EMMC) + return; + + /* Fixup RPMB_READ_DATA specific to eMMC + * The block count of the RPMB read operation is not indicated + * in the original RPMB Data Read Request packet. + * This is different then implementation for other protocol + * standards. + */ + for (i = 0; i < ncmds; i++) { + struct rpmb_frame_jdec *frame = cmds[i].frames; + + if (frame->req_resp == cpu_to_be16(RPMB_READ_DATA)) { + dev_dbg(&rdev->dev, "Fixing up READ_DATA frame to block_count=0\n"); + frame->block_count = 0; + } + } +} + /** * rpmb_cmd_seq - send RPMB command sequence * @@ -58,6 +90,7 @@ int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds) mutex_lock(&rdev->lock); err = -EOPNOTSUPP; if (rdev->ops && rdev->ops->cmd_seq) { + rpmb_cmd_fixup(rdev, cmds, ncmds); err = rdev->ops->cmd_seq(rdev->dev.parent, rdev->target, cmds, ncmds); } From 50a5c277413c89b2a2daf7d83fe484d889be6802 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 13 Mar 2016 13:36:52 +0200 Subject: [PATCH 1000/1276] rpmb: add sysfs-class ABI documentation V2: resend V3: add more verbose description V4: resend V5: adjust date and kernel version V6: adjust date and kernel version V7: adjust date and kernel version V8: adjust date and kernel version V9: adjust date and kernel version Change-Id: I2d71ca467e5960ca93c904e92cfcf69591a3de59 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-rpmb | 20 ++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 21 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-rpmb diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb new file mode 100644 index 000000000000..a9d45aa77f77 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-rpmb @@ -0,0 +1,20 @@ +What: /sys/class/rpmb/ +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The rpmb/ class sub-directory belongs to RPMB device class. + + Few storage technologies such is EMMC, UFS, and NVMe support + Replay Protected Memory Block (RPMB) hardware partition with + common protocol and similar frame layout. + Such a partition provides authenticated and replay protected access, + hence suitable as a secure storage. + +What: /sys/class/rpmb/rpmbN/ +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN directory is created for + each RPMB registered device. diff --git a/MAINTAINERS b/MAINTAINERS index 3c99a53c4590..070cb20bcf78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12533,6 +12533,7 @@ L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* F: include/linux/rpmb.h +F: Documentation/ABI/testing/sysfs-class-rpmb RTL2830 MEDIA DRIVER M: Antti Palosaari From dc8b45cce8f8bddb21d164a46d71ab11c6d0028c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 28 Feb 2016 23:59:39 +0200 Subject: [PATCH 1001/1276] char: rpmb: add device attributes Add attribute type that displays underlay storage type technology EMMC, UFS, and attribute id, that displays underlay storage device id. For EMMC this would be content of CID and for UFS serial number from the device descriptor. V2: resend V3: set kernel version to 4.7 V4: update target date to Maj V5: update date and kernel version V6: 1. Add simulation device type 2. Update date and kernel version 3. Use binary attribute for id 4. use simple sprintf instead of scnprintf 5. Add more verbose documenation V7: resend V8: update date and kernel version V9: 1. update date and kernel version 2. add new rd_cnt_max and wr_cnt_max attributes. 3. Use SIM as a suffix of the device type. Change-Id: If25a96f1371d8fea5820f6e06366bc0945d32faa Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-rpmb | 37 ++++++++++ drivers/char/rpmb/core.c | 84 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb index a9d45aa77f77..d0540be7db19 100644 --- a/Documentation/ABI/testing/sysfs-class-rpmb +++ b/Documentation/ABI/testing/sysfs-class-rpmb @@ -18,3 +18,40 @@ Contact: Tomas Winkler Description: The /sys/class/rpmb/rpmbN directory is created for each RPMB registered device. + +What: /sys/class/rpmb/rpmbN/type +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/type file contains device + underlying storage type technology: EMMC, UFS, NVMe. + In case of simulated device it will have :SIM suffix + i.e EMMC:SIM. + +What: /sys/class/rpmb/rpmbN/id +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/id file contains unique device id + in a binary form as defined by underlying storage device. + In case of multiple RPMB devices a user can determine correct + device. + The content can be parsed according the storage device type. + +What: /sys/class/rpmb/rpmbN/wr_cnt_max +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/wr_cnt_max file contains + number of blocks that can be reliable written in a single request. + +What: /sys/class/rpmb/rpmbN/rd_cnt_max +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/rd_cnt_max file contains + number of blocks that can be read in a single request. diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 3bf3f0db54fb..41a249c0c58f 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -214,6 +214,88 @@ struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target) } EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device); +static ssize_t type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + const char *sim; + ssize_t ret; + + sim = (rdev->ops->type & RPMB_TYPE_SIM) ? ":SIM" : ""; + switch (RPMB_TYPE_HW(rdev->ops->type)) { + case RPMB_TYPE_EMMC: + ret = sprintf(buf, "EMMC%s\n", sim); + break; + case RPMB_TYPE_UFS: + ret = sprintf(buf, "UFS%s\n", sim); + break; + case RPMB_TYPE_NVME: + ret = sprintf(buf, "NVMe%s\n", sim); + break; + default: + ret = sprintf(buf, "UNKNOWN\n"); + break; + } + + return ret; +} +static DEVICE_ATTR_RO(type); + +static ssize_t id_read(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + size_t sz = min_t(size_t, rdev->ops->dev_id_len, PAGE_SIZE); + + if (!rdev->ops->dev_id) + return 0; + + return memory_read_from_buffer(buf, count, &off, rdev->ops->dev_id, sz); +} +static BIN_ATTR_RO(id, 0); + +static ssize_t wr_cnt_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return sprintf(buf, "%u\n", rdev->ops->wr_cnt_max); +} +static DEVICE_ATTR_RO(wr_cnt_max); + +static ssize_t rd_cnt_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return sprintf(buf, "%u\n", rdev->ops->rd_cnt_max); +} +static DEVICE_ATTR_RO(rd_cnt_max); + +static struct attribute *rpmb_attrs[] = { + &dev_attr_type.attr, + &dev_attr_wr_cnt_max.attr, + &dev_attr_rd_cnt_max.attr, + NULL, +}; + +static struct bin_attribute *rpmb_bin_attributes[] = { + &bin_attr_id, + NULL, +}; + +static struct attribute_group rpmb_attr_group = { + .attrs = rpmb_attrs, + .bin_attrs = rpmb_bin_attributes, +}; + +static const struct attribute_group *rpmb_attr_groups[] = { + &rpmb_attr_group, + NULL +}; + /** * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem * @@ -329,6 +411,8 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, dev_set_name(&rdev->dev, "rpmb%d", id); rdev->dev.class = &rpmb_class; rdev->dev.parent = dev; + rdev->dev.groups = rpmb_attr_groups; + ret = device_register(&rdev->dev); if (ret) goto exit; From 8ebe341725016b753b1c2f8bdf355267ceb0feec Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 16 Jul 2015 12:29:50 +0300 Subject: [PATCH 1002/1276] char: rpmb: provide a user space interface The user space API is achieved via two synchronous IOCTLs. Simplified one, RPMB_IOC_REQ_CMD, were read result cycles is performed by the framework on behalf the user and second, RPMB_IOC_SEQ_CMD where the whole RPMB sequence including RESULT_READ is supplied by the caller. The latter is intended for easier adjusting of the applications that use MMC_IOC_MULTI_CMD ioctl. V2: use memdup_user V3: commit message fix V4: resend V5: 1. Add RPMB_IOC_SEQ_CMD API. 2. Export uapi rpmb.h header V6: 1. Remove #include . 2. Add ioctl documentation. V7: 1. copy_from_user the value of the frame pointer. 2. Fix possible macro side-effect due to macro argument reuse. V8: 1. Fix kdoc errors 2. Move IOCTL to a different range due to conflict 3. Change license to dual BSD/GPL V9: 1. Add version and capability ioctls and drop the request ioctl 2. Use zero based frame count: 0 means only meted are in a frame. 2. Add SPDX identifiers. 3. Fix comment typo in uapi/linux/rpmb.h V10: 1. Rebase on 4.18 V11: 1. Rebase on 4.19 Change-Id: I00f2b5d5c92982fa2a3814a8bc56a3fecd19456f Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 1 + drivers/char/rpmb/Kconfig | 7 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/cdev.c | 296 +++++++++++++++++++++++++++ drivers/char/rpmb/core.c | 9 +- drivers/char/rpmb/rpmb-cdev.h | 17 ++ include/linux/rpmb.h | 107 +--------- include/uapi/linux/rpmb.h | 192 +++++++++++++++++ 9 files changed, 532 insertions(+), 99 deletions(-) create mode 100644 drivers/char/rpmb/cdev.c create mode 100644 drivers/char/rpmb/rpmb-cdev.h create mode 100644 include/uapi/linux/rpmb.h diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 13a7c999c04a..ae2f081324f4 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -324,6 +324,7 @@ Code Seq#(hex) Include File Comments 0xB3 00 linux/mmc/ioctl.h 0xB4 00-0F linux/gpio.h 0xB5 00-0F uapi/linux/rpmsg.h +0xB5 80-8F linux/uapi/linux/rpmb.h 0xB6 all linux/fpga-dfl.h 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h diff --git a/MAINTAINERS b/MAINTAINERS index 070cb20bcf78..1868f61fffc1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12532,6 +12532,7 @@ M: Tomas Winkler L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* +F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index b5cd02de91bb..cfecb1fccfc0 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -7,3 +7,10 @@ config RPMB access RPMB partition. If unsure, select N. + +config RPMB_INTF_DEV + bool "RPMB character device interface /dev/rpmbN" + depends on RPMB + help + Say yes here if you want to access RPMB from user space + via character device interface /dev/rpmb%d diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index badc1cd9428b..c171a5cfaed3 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new file mode 100644 index 000000000000..bdac3894b111 --- /dev/null +++ b/drivers/char/rpmb/cdev.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +#include "rpmb-cdev.h" + +static dev_t rpmb_devt; +#define RPMB_MAX_DEVS MINORMASK + +#define RPMB_DEV_OPEN 0 /** single open bit (position) */ +/* from MMC_IOC_MAX_CMDS */ +#define RPMB_MAX_FRAMES 255 + +/** + * rpmb_open - the open function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_open(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev; + + rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev); + if (!rdev) + return -ENODEV; + + /* the rpmb is single open! */ + if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status)) + return -EBUSY; + + mutex_lock(&rdev->lock); + + fp->private_data = rdev; + + mutex_unlock(&rdev->lock); + + return nonseekable_open(inode, fp); +} + +/** + * rpmb_release - the cdev release function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 always. + */ +static int rpmb_release(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev = fp->private_data; + + clear_bit(RPMB_DEV_OPEN, &rdev->status); + + return 0; +} + +/** + * rpmb_cmd_copy_from_user - copy rpmb command from the user space + * + * @cmd: internal cmd structure + * @ucmd: user space cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, + struct rpmb_ioc_cmd __user *ucmd) +{ + struct rpmb_frame *frames; + u64 frames_ptr; + + if (get_user(cmd->flags, &ucmd->flags)) + return -EFAULT; + + if (get_user(cmd->nframes, &ucmd->nframes)) + return -EFAULT; + + if (cmd->nframes > RPMB_MAX_FRAMES) + return -EOVERFLOW; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + frames = memdup_user(u64_to_user_ptr(frames_ptr), + rpmb_ioc_frames_len_jdec(cmd->nframes)); + if (IS_ERR(frames)) + return PTR_ERR(frames); + + cmd->frames = frames; + return 0; +} + +/** + * rpmb_cmd_copy_to_user - copy rpmb command to the user space + * + * @ucmd: user space cmd structure + * @cmd: internal cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, + struct rpmb_cmd *cmd) +{ + u64 frames_ptr; + + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + /* some archs have issues with 64bit get_user */ + if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, + rpmb_ioc_frames_len_jdec(cmd->nframes))) + return -EFAULT; + + return 0; +} + +/** + * rpmb_ioctl_seq_cmd - issue an rpmb command sequence + * + * @rdev: rpmb device + * @ptr: rpmb cmd sequence + * + * RPMB_IOC_SEQ_CMD handler + * + * Return: 0 on success, <0 on error + */ +static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_seq_cmd __user *ptr) +{ + __u64 ncmds; + struct rpmb_cmd *cmds; + struct rpmb_ioc_cmd __user *ucmds; + + int i; + int ret; + + /* The caller must have CAP_SYS_RAWIO, like mmc ioctl */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&ncmds, &ptr->num_of_cmds, sizeof(ncmds))) + return -EFAULT; + + if (ncmds > 3) { + dev_err(&rdev->dev, "supporting up to 3 packets (%llu)\n", + ncmds); + return -EINVAL; + } + + cmds = kcalloc(ncmds, sizeof(*cmds), GFP_KERNEL); + if (!cmds) + return -ENOMEM; + + ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]); + if (ret) + goto out; + } + + ret = rpmb_cmd_seq(rdev, cmds, ncmds); + if (ret) + goto out; + + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]); + if (ret) + goto out; + } +out: + for (i = 0; i < ncmds; i++) + kfree(cmds[i].frames); + kfree(cmds); + return ret; +} + +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_ver_cmd __user *ptr) +{ + struct rpmb_ioc_ver_cmd ver = { + .api_version = RPMB_API_VERSION, + }; + + return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; +} + +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_cap_cmd __user *ptr) +{ + struct rpmb_ioc_cap_cmd cap; + + cap.device_type = rdev->ops->type; + cap.target = rdev->target; + cap.block_size = rdev->ops->block_size; + cap.wr_cnt_max = rdev->ops->wr_cnt_max; + cap.rd_cnt_max = rdev->ops->rd_cnt_max; + cap.auth_method = rdev->ops->auth_method; + cap.capacity = rpmb_get_capacity(rdev); + cap.reserved = 0; + + return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; +} + +/** + * rpmb_ioctl - rpmb ioctl dispatcher + * + * @fp: a file pointer + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD RPMB_IOC_CAP_CMD + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd pmb_ioc_seq_cmd + * + * Return: 0 on success; < 0 on error + */ +static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct rpmb_dev *rdev = fp->private_data; + void __user *ptr = (void __user *)arg; + + switch (cmd) { + case RPMB_IOC_VER_CMD: + return rpmb_ioctl_ver_cmd(rdev, ptr); + case RPMB_IOC_CAP_CMD: + return rpmb_ioctl_cap_cmd(rdev, ptr); + case RPMB_IOC_SEQ_CMD: + return rpmb_ioctl_seq_cmd(rdev, ptr); + default: + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long rpmb_compat_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + return rpmb_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations rpmb_fops = { + .open = rpmb_open, + .release = rpmb_release, + .unlocked_ioctl = rpmb_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rpmb_compat_ioctl, +#endif + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +void rpmb_cdev_prepare(struct rpmb_dev *rdev) +{ + rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id); + rdev->cdev.owner = THIS_MODULE; + cdev_init(&rdev->cdev, &rpmb_fops); +} + +void rpmb_cdev_add(struct rpmb_dev *rdev) +{ + cdev_add(&rdev->cdev, rdev->dev.devt, 1); +} + +void rpmb_cdev_del(struct rpmb_dev *rdev) +{ + if (rdev->dev.devt) + cdev_del(&rdev->cdev); +} + +int __init rpmb_cdev_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS, "rpmb"); + if (ret < 0) + pr_err("unable to allocate char dev region\n"); + + return ret; +} + +void __exit rpmb_cdev_exit(void) +{ + unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); +} diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 41a249c0c58f..e02c12b8046c 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -11,6 +11,7 @@ #include #include +#include "rpmb-cdev.h" static DEFINE_IDA(rpmb_ida); @@ -307,6 +308,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); + rpmb_cdev_del(rdev); device_del(&rdev->dev); mutex_unlock(&rdev->lock); @@ -413,10 +415,14 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, rdev->dev.parent = dev; rdev->dev.groups = rpmb_attr_groups; + rpmb_cdev_prepare(rdev); + ret = device_register(&rdev->dev); if (ret) goto exit; + rpmb_cdev_add(rdev); + dev_dbg(&rdev->dev, "registered device\n"); return rdev; @@ -433,11 +439,12 @@ static int __init rpmb_init(void) { ida_init(&rpmb_ida); class_register(&rpmb_class); - return 0; + return rpmb_cdev_init(); } static void __exit rpmb_exit(void) { + rpmb_cdev_exit(); class_unregister(&rpmb_class); ida_destroy(&rpmb_ida); } diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d --- /dev/null +++ b/drivers/char/rpmb/rpmb-cdev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifdef CONFIG_RPMB_INTF_DEV +int __init rpmb_cdev_init(void); +void __exit rpmb_cdev_exit(void); +void rpmb_cdev_prepare(struct rpmb_dev *rdev); +void rpmb_cdev_add(struct rpmb_dev *rdev); +void rpmb_cdev_del(struct rpmb_dev *rdev); +#else +static inline int __init rpmb_cdev_init(void) { return 0; } +static inline void __exit rpmb_cdev_exit(void) {} +static inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_add(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {} +#endif /* CONFIG_RPMB_INTF_DEV */ diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index 6acd9b1e70f6..fb535336d50f 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -7,105 +7,14 @@ #include #include +#include +#include #include -/** - * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs - * - * @stuff : stuff bytes - * @key_mac : The authentication key or the message authentication - * code (MAC) depending on the request/response type. - * The MAC will be delivered in the last (or the only) - * block of data. - * @data : Data to be written or read by signed access. - * @nonce : Random number generated by the host for the requests - * and copied to the response by the RPMB engine. - * @write_counter: Counter value for the total amount of the successful - * authenticated data write requests made by the host. - * @addr : Address of the data to be programmed to or read - * from the RPMB. Address is the serial number of - * the accessed block (half sector 256B). - * @block_count : Number of blocks (half sectors, 256B) requested to be - * read/programmed. - * @result : Includes information about the status of the write counter - * (valid, expired) and result of the access made to the RPMB. - * @req_resp : Defines the type of request and response to/from the memory. - */ -struct rpmb_frame_jdec { - u8 stuff[196]; - u8 key_mac[32]; - u8 data[256]; - u8 nonce[16]; - __be32 write_counter; - __be16 addr; - __be16 block_count; - __be16 result; - __be16 req_resp; -} __packed; - -#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ -#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ -#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ -#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ -#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ - -#define RPMB_REQ2RESP(_OP) ((_OP) << 8) -#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) - -/** - * enum rpmb_op_result - rpmb operation results - * - * @RPMB_ERR_OK : operation successful - * @RPMB_ERR_GENERAL : general failure - * @RPMB_ERR_AUTH : mac doesn't match or ac calculation failure - * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure - * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment - * @RPMB_ERR_WRITE : data, counter, or result write failure - * @RPMB_ERR_READ : data, counter, or result read failure - * @RPMB_ERR_NO_KEY : authentication key not yet programmed - * - * @RPMB_ERR_COUNTER_EXPIRED: counter expired - */ -enum rpmb_op_result { - RPMB_ERR_OK = 0x0000, - RPMB_ERR_GENERAL = 0x0001, - RPMB_ERR_AUTH = 0x0002, - RPMB_ERR_COUNTER = 0x0003, - RPMB_ERR_ADDRESS = 0x0004, - RPMB_ERR_WRITE = 0x0005, - RPMB_ERR_READ = 0x0006, - RPMB_ERR_NO_KEY = 0x0007, - - RPMB_ERR_COUNTER_EXPIRED = 0x0080 -}; - -/** - * enum rpmb_type - type of underlying storage technology - * - * @RPMB_TYPE_ANY : any type used for search only - * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) - * @RPMB_TYPE_UFS : UFS (JESD220) - * @RPMB_TYPE_NVME : NVM Express Revision 1.3a - * @RPMB_TYPE_SIM : Simulation device. - * @RPMB_TYPE_MAX : upper sentinel - */ -enum rpmb_type { - RPMB_TYPE_ANY = 0, - RPMB_TYPE_EMMC, - RPMB_TYPE_UFS, - RPMB_TYPE_NVME, - - RPMB_TYPE_SIM = 0x0100, - RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, -}; - -#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) +#define RPMB_API_VERSION 0x80000001 extern struct class rpmb_class; -#define RPMB_F_WRITE BIT(0) -#define RPMB_F_REL_WRITE BIT(1) - /** * struct rpmb_cmd: rpmb access command * @@ -122,10 +31,6 @@ struct rpmb_cmd { void *frames; }; -enum rpmb_auth_method { - RPMB_HMAC_ALGO_SHA_256 = 0, -}; - /** * struct rpmb_ops - RPMB ops to be implemented by underlying block device * @@ -163,6 +68,8 @@ struct rpmb_ops { * @dev : device * @id : device id * @target : RPMB target/region within the physical device + * @cdev : character dev + * @status : device status * @ops : operation exported by block layer */ struct rpmb_dev { @@ -170,6 +77,10 @@ struct rpmb_dev { struct device dev; int id; u8 target; +#ifdef CONFIG_RPMB_INTF_DEV + struct cdev cdev; + unsigned long status; +#endif /* CONFIG_RPMB_INTF_DEV */ const struct rpmb_ops *ops; }; diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file mode 100644 index 000000000000..d304701cd258 --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _UAPI_LINUX_RPMB_H_ +#define _UAPI_LINUX_RPMB_H_ + +#include + +/** + * enum rpmb_type - type of underlying storage technology + * + * @RPMB_TYPE_ANY : any type used for search only + * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) + * @RPMB_TYPE_UFS : UFS (JESD220) + * @RPMB_TYPE_NVME : NVM Express Revision 1.3a + * @RPMB_TYPE_SIM : Simulation device. + * @RPMB_TYPE_MAX : upper sentinel + */ +enum rpmb_type { + RPMB_TYPE_ANY = 0, + RPMB_TYPE_EMMC, + RPMB_TYPE_UFS, + RPMB_TYPE_NVME, + + RPMB_TYPE_SIM = 0x0100, + RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, +}; + +#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) + +/** + * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + */ +struct rpmb_frame_jdec { + __u8 stuff[196]; + __u8 key_mac[32]; + __u8 data[256]; + __u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __attribute__((packed)); + +/* length of the part of the frame used for HMAC computation */ +#define rpmb_jdec_hmac_data_len \ + (sizeof(struct rpmb_frame_jdec) - \ + offsetof(struct rpmb_frame_jdec, data)) + +#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ + +#define RPMB_REQ2RESP(_OP) ((_OP) << 8) +#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) + +/** + * enum rpmb_op_result - rpmb operation results + * + * @RPMB_ERR_OK: operation successful + * @RPMB_ERR_GENERAL: general failure + * @RPMB_ERR_AUTH: mac doesn't match or ac calculation failure + * @RPMB_ERR_COUNTER: counter doesn't match or counter increment failure + * @RPMB_ERR_ADDRESS: address out of range or wrong address alignment + * @RPMB_ERR_WRITE: data, counter, or result write failure + * @RPMB_ERR_READ: data, counter, or result read failure + * @RPMB_ERR_NO_KEY: authentication key not yet programmed + * + * @RPMB_ERR_COUNTER_EXPIRED: counter expired + */ +enum rpmb_op_result { + RPMB_ERR_OK = 0x0000, + RPMB_ERR_GENERAL = 0x0001, + RPMB_ERR_AUTH = 0x0002, + RPMB_ERR_COUNTER = 0x0003, + RPMB_ERR_ADDRESS = 0x0004, + RPMB_ERR_WRITE = 0x0005, + RPMB_ERR_READ = 0x0006, + RPMB_ERR_NO_KEY = 0x0007, + + RPMB_ERR_COUNTER_EXPIRED = 0x0080 +}; + +#define RPMB_F_READ 0UL +#define RPMB_F_WRITE (1UL << 0) +#define RPMB_F_REL_WRITE (1UL << 1) + +enum rpmb_auth_method { + RPMB_HMAC_ALGO_SHA_256 = 0, +}; + +/** + * struct rpmb_cmd - rpmb access command + * + * @flags: command flags + * 0 - read command + * 1 - write command RPMB_F_WRITE + * 2 - reliable write RPMB_F_REL_WRITE + * @nframes: number of rpmb data frames in the command. + * 0 means 1 frame with meta data only. + * @frames_ptr: a pointer to the list of rpmb frames + */ +struct rpmb_ioc_cmd { + __u32 flags; + __u32 nframes; + __aligned_u64 frames_ptr; +}; + +#define rpmb_ioc_cmd_set_frames(_cmd, _ptr) \ + (_cmd).frames_ptr = (__aligned_u64)(intptr_t)(_ptr) + +#define rpmb_ioc_cmd_set(_cmd, _flags, _ptr, _n) do { \ + struct rpmb_ioc_cmd *icmd = &(_cmd); \ + icmd->flags = (_flags); \ + icmd->nframes = (_n); \ + icmd->frames_ptr = (__aligned_u64)(intptr_t)(_ptr); \ +} while (0) + +#define rpmb_ioc_frames_len_jdec(_n) \ + (((_n) ?: 1) * sizeof(struct rpmb_frame_jdec)) + +/** + * struct rpmb_ioc_seq_cmd - rpmb command sequence + * + * @num_of_cmds: number of commands + * @cmds: list of rpmb commands + */ +struct rpmb_ioc_seq_cmd { + __u64 num_of_cmds; + struct rpmb_ioc_cmd cmds[0]; +} __attribute__((packed)); + +/** + * struct rpmb_ioc_ver_cmd - rpmb api version + * + * @api_version: rpmb API version. + */ +struct rpmb_ioc_ver_cmd { + __u32 api_version; +} __attribute__((packed)); + +/** + * struct rpmb_ioc_ver_cmd - rpmb api version + * + * @device_type: underlying storage device type + * @target: rpmb target/region within RPMB partition. + * @capacity: storage capacity + * @block_size: storage data block size + * @wr_cnt_max: maximal number of block that can be written in a single request. + * @rd_cnt_max: maximal number of block that can be read in a single request. + * @auth_method: authentication method: currently always HMAC_SHA_256 + * @reserved: reserved to align to 4 bytes. + */ +struct rpmb_ioc_cap_cmd { + __u16 device_type; + __u16 target; + __u16 capacity; + __u16 block_size; + __u16 wr_cnt_max; + __u16 rd_cnt_max; + __u16 auth_method; + __u16 reserved; +} __attribute__((packed)); + +#define RPMB_IOC_VER_CMD _IOR(0xB5, 80, struct rpmb_ioc_ver_cmd) +#define RPMB_IOC_CAP_CMD _IOR(0xB5, 81, struct rpmb_ioc_cap_cmd) +#define RPMB_IOC_SEQ_CMD _IOWR(0xB5, 82, struct rpmb_ioc_seq_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */ From c87cff4e5e024699746dff34c41c3b13a2284c20 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 28 Feb 2016 10:36:13 +0200 Subject: [PATCH 1003/1276] char: rpmb: add RPMB simulation device The RPMB partition simulation device is a virtual device that provides simulation of the RPMB protocol and use kernel memory as storage. Be aware it doesn't promise any real security. This driver is suitable only for testing of the RPMB subsystem or RPMB applications prior to RPMB key provisioning, as RPMB key programming can be performed only once in the life time of the storage device. The module currently supports two configuration options via module parameters 1. max_wr_blks: for specifying max blocks that can be written in a single command 2. daunits: used to set storage capacity in 128K units. V2: remove .owner setting, it is set automatically V3: 1. Add shutdown handler (similar to ufshcd) 2. Commit message fix V4: Use select RPMB in Kconfg to ensure valid configuration. V5: Revamp the code using the sequence command. V6: 1. Be more verbose about some errors, after all this is a testing module. 2. Fix RPMB_READ_DATA: a. The number of blocks for eMMC request frame should be 0 b. Fix missing return before bailing on error c. Copy all the frames back 3. Fix RPMB_WRITE_DATA: a. Compute MAC on result packet b. Also address should be set in the result frame. 4. Remove platform device 5. Update the commit message V7: Resend. V8: 1. drop use SHASH_DESC_ON_STACK, variable length arrays are problematic in C. 2. Fix typos. 3. Set out_frames in case of not programmed keys otherwise read cycle won't return correct answer. V9: 1. Add SPDX identifiers. 2. Adjust to new unregister API. 3. Adjust to the new zero based RPMB frame count. Change-Id: Idd0a414c4ce157631f69586f1ed3a6e88cd8a4ee Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- drivers/char/rpmb/Kconfig | 16 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/rpmb_sim.c | 715 +++++++++++++++++++++++++++++++++++ 3 files changed, 732 insertions(+) create mode 100644 drivers/char/rpmb/rpmb_sim.c diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index cfecb1fccfc0..c069664eec92 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -14,3 +14,19 @@ config RPMB_INTF_DEV help Say yes here if you want to access RPMB from user space via character device interface /dev/rpmb%d + +config RPMB_SIM + tristate "RPMB partition device simulator" + default n + select RPMB + select CRYPTO_SHA256 + select CRYPTO_HMAC + help + RPMB partition simulation device is a virtual device that + provides simulation of the RPMB protocol and use kernel memory + as storage. + + Be aware it doesn't promise any real security. This driver is + suitable only for testing of the RPMB subsystem or RPMB applications + prior to RPMB key provisioning. + Most people should say N here. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index c171a5cfaed3..8bd1186948b0 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o +obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/rpmb_sim.c b/drivers/char/rpmb/rpmb_sim.c new file mode 100644 index 000000000000..728e25511377 --- /dev/null +++ b/drivers/char/rpmb/rpmb_sim.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include + +static const char id[] = "RPMB:SIM"; +#define CAPACITY_UNIT SZ_128K +#define CAPACITY_MIN SZ_128K +#define CAPACITY_MAX SZ_16M +#define BLK_UNIT SZ_256 + +static unsigned int max_wr_blks = 2; +module_param(max_wr_blks, uint, 0644); +MODULE_PARM_DESC(max_wr_blks, "max blocks that can be written in a single command (default: 2)"); + +static unsigned int daunits = 1; +module_param(daunits, uint, 0644); +MODULE_PARM_DESC(daunits, "number of data area units of 128K (default: 1)"); + +struct blk { + u8 data[BLK_UNIT]; +}; + +/** + * struct rpmb_sim_dev + * + * @dev: back pointer device + * @rdev: rpmb device + * @auth_key: Authentication key register which is used to authenticate + * accesses when MAC is calculated; + * @auth_key_set: true if authentication key was set + * @write_counter: Counter value for the total amount of successful + * authenticated data write requests made by the host. + * The initial value of this register after production is 00000000h. + * The value will be incremented by one along with each successful + * programming access. The value cannot be reset. After the counter + * has reached the maximum value of FFFFFFFFh, + * it will not be incremented anymore (overflow prevention) + * @hash_desc: hmac(sha256) shash descriptor + * + * @res_frames: frame that holds the result of the last write operation + * @out_frames: next read operation result frames + * @out_frames_cnt: number of the output frames + * + * @capacity: size of the partition in bytes multiple of 128K + * @blkcnt: block count + * @da: data area in blocks + */ +struct rpmb_sim_dev { + struct device *dev; + struct rpmb_dev *rdev; + u8 auth_key[32]; + bool auth_key_set; + u32 write_counter; + struct shash_desc *hash_desc; + + struct rpmb_frame_jdec res_frames[1]; + struct rpmb_frame_jdec *out_frames; + unsigned int out_frames_cnt; + + size_t capacity; + size_t blkcnt; + struct blk *da; +}; + +static __be16 op_result(struct rpmb_sim_dev *rsdev, u16 result) +{ + if (!rsdev->auth_key_set) + return cpu_to_be16(RPMB_ERR_NO_KEY); + + if (rsdev->write_counter == 0xFFFFFFFF) + result |= RPMB_ERR_COUNTER_EXPIRED; + + return cpu_to_be16(result); +} + +static __be16 req_to_resp(u16 req) +{ + return cpu_to_be16(RPMB_REQ2RESP(req)); +} + +static int rpmb_sim_calc_hmac(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, + unsigned int blks, u8 *mac) +{ + struct shash_desc *desc = rsdev->hash_desc; + int i; + int ret; + + ret = crypto_shash_init(desc); + if (ret) + goto out; + + for (i = 0; i < blks; i++) { + ret = crypto_shash_update(desc, frames[i].data, + rpmb_jdec_hmac_data_len); + if (ret) + goto out; + } + ret = crypto_shash_final(desc, mac); +out: + if (ret) + dev_err(rsdev->dev, "digest error = %d", ret); + + return ret; +} + +static int rpmb_op_not_programmed(struct rpmb_sim_dev *rsdev, u16 req) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, RPMB_ERR_NO_KEY); + + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + dev_err(rsdev->dev, "not programmed\n"); + + return 0; +} + +static int rpmb_op_program_key(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + struct crypto_shash *tfm = rsdev->hash_desc->tfm; + u16 req; + int ret; + u16 err = RPMB_ERR_OK; + + req = be16_to_cpu(in_frame[0].req_resp); + + if (req != RPMB_PROGRAM_KEY) + return -EINVAL; + + if (cnt != 1) { + dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt); + return -EINVAL; + } + + if (rsdev->auth_key_set) { + dev_err(rsdev->dev, "key already set\n"); + err = RPMB_ERR_WRITE; + goto out; + } + + ret = crypto_shash_setkey(tfm, in_frame[0].key_mac, 32); + if (ret) { + dev_err(rsdev->dev, "set key failed = %d\n", ret); + err = RPMB_ERR_GENERAL; + goto out; + } + + dev_dbg(rsdev->dev, "digest size %u\n", crypto_shash_digestsize(tfm)); + + memcpy(rsdev->auth_key, in_frame[0].key_mac, 32); + rsdev->auth_key_set = true; +out: + + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, err); + + return 0; +} + +static int rpmb_op_get_wr_counter(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *frame; + int ret = 0; + u16 req; + u16 err; + + req = be16_to_cpu(in_frame[0].req_resp); + if (req != RPMB_GET_WRITE_COUNTER) + return -EINVAL; + + if (cnt != 1) { + dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt); + return -EINVAL; + } + + frame = kcalloc(1, sizeof(*frame), GFP_KERNEL); + if (!frame) { + err = RPMB_ERR_READ; + ret = -ENOMEM; + rsdev->out_frames = rsdev->res_frames; + rsdev->out_frames_cnt = cnt; + goto out; + } + + rsdev->out_frames = frame; + rsdev->out_frames_cnt = cnt; + + frame->req_resp = req_to_resp(req); + frame->write_counter = cpu_to_be32(rsdev->write_counter); + memcpy(frame->nonce, in_frame[0].nonce, 16); + + err = RPMB_ERR_OK; + if (rpmb_sim_calc_hmac(rsdev, frame, cnt, frame->key_mac)) + err = RPMB_ERR_READ; + +out: + rsdev->out_frames[0].req_resp = req_to_resp(req); + rsdev->out_frames[0].result = op_result(rsdev, err); + + return ret; +} + +static int rpmb_op_write_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + u8 mac[32]; + u16 req, err, addr, blks; + unsigned int i; + int ret = 0; + + req = be16_to_cpu(in_frame[0].req_resp); + if (req != RPMB_WRITE_DATA) + return -EINVAL; + + if (rsdev->write_counter == 0xFFFFFFFF) { + err = RPMB_ERR_WRITE; + goto out; + } + + blks = be16_to_cpu(in_frame[0].block_count); + if (blks == 0 || blks > cnt) { + dev_err(rsdev->dev, "wrong number of blocks: blks=%u cnt=%u\n", + blks, cnt); + ret = -EINVAL; + err = RPMB_ERR_GENERAL; + goto out; + } + + if (blks > max_wr_blks) { + err = RPMB_ERR_WRITE; + goto out; + } + + addr = be16_to_cpu(in_frame[0].addr); + if (addr >= rsdev->blkcnt) { + err = RPMB_ERR_ADDRESS; + goto out; + } + + if (rpmb_sim_calc_hmac(rsdev, in_frame, blks, mac)) { + err = RPMB_ERR_AUTH; + goto out; + } + + /* mac is in the last frame */ + if (memcmp(mac, in_frame[blks - 1].key_mac, sizeof(mac)) != 0) { + err = RPMB_ERR_AUTH; + goto out; + } + + if (be32_to_cpu(in_frame[0].write_counter) != rsdev->write_counter) { + err = RPMB_ERR_COUNTER; + goto out; + } + + if (addr + blks > rsdev->blkcnt) { + err = RPMB_ERR_WRITE; + goto out; + } + + dev_dbg(rsdev->dev, "Writing = %u blocks at addr = 0x%X\n", blks, addr); + err = RPMB_ERR_OK; + for (i = 0; i < blks; i++) + memcpy(rsdev->da[addr + i].data, in_frame[i].data, BLK_UNIT); + + rsdev->write_counter++; + + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->write_counter = cpu_to_be32(rsdev->write_counter); + res_frame->addr = cpu_to_be16(addr); + if (rpmb_sim_calc_hmac(rsdev, res_frame, 1, res_frame->key_mac)) + err = RPMB_ERR_READ; + +out: + if (err != RPMB_ERR_OK) { + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + } + res_frame->result = op_result(rsdev, err); + + return ret; +} + +static int rpmb_do_read_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + struct rpmb_frame_jdec *out_frames = NULL; + u8 mac[32]; + u16 req, err, addr, blks; + unsigned int i; + int ret; + + req = be16_to_cpu(in_frame->req_resp); + if (req != RPMB_READ_DATA) + return -EINVAL; + + /* eMMC intentionally set 0 here */ + blks = be16_to_cpu(in_frame->block_count); + blks = blks ?: cnt; + if (blks > cnt) { + dev_err(rsdev->dev, "wrong number of frames cnt %u\n", blks); + ret = -EINVAL; + err = RPMB_ERR_GENERAL; + goto out; + } + + out_frames = kcalloc(blks, sizeof(*out_frames), GFP_KERNEL); + if (!out_frames) { + ret = -ENOMEM; + err = RPMB_ERR_READ; + goto out; + } + + ret = 0; + addr = be16_to_cpu(in_frame[0].addr); + if (addr >= rsdev->blkcnt) { + err = RPMB_ERR_ADDRESS; + goto out; + } + + if (addr + blks > rsdev->blkcnt) { + err = RPMB_ERR_READ; + goto out; + } + + dev_dbg(rsdev->dev, "reading = %u blocks at addr = 0x%X\n", blks, addr); + for (i = 0; i < blks; i++) { + memcpy(out_frames[i].data, rsdev->da[addr + i].data, BLK_UNIT); + memcpy(out_frames[i].nonce, in_frame[0].nonce, 16); + out_frames[i].req_resp = req_to_resp(req); + out_frames[i].addr = in_frame[0].addr; + out_frames[i].block_count = cpu_to_be16(blks); + } + + if (rpmb_sim_calc_hmac(rsdev, out_frames, blks, mac)) { + err = RPMB_ERR_AUTH; + goto out; + } + + memcpy(out_frames[blks - 1].key_mac, mac, sizeof(mac)); + + err = RPMB_ERR_OK; + for (i = 0; i < blks; i++) + out_frames[i].result = op_result(rsdev, err); + + rsdev->out_frames = out_frames; + rsdev->out_frames_cnt = cnt; + + return 0; + +out: + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, err); + kfree(out_frames); + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + return ret; +} + +static int rpmb_op_read_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + u16 req; + + req = be16_to_cpu(in_frame->req_resp); + if (req != RPMB_READ_DATA) + return -EINVAL; + + memcpy(res_frame, in_frame, sizeof(*res_frame)); + + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + return 0; +} + +static int rpmb_op_result_read(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + u16 req = be16_to_cpu(frames[0].req_resp); + u16 blks = be16_to_cpu(frames[0].block_count); + + if (req != RPMB_RESULT_READ) + return -EINVAL; + + if (blks != 0) { + dev_err(rsdev->dev, "wrong number of frames %u != 0\n", blks); + return -EINVAL; + } + + rsdev->out_frames = rsdev->res_frames; + rsdev->out_frames_cnt = 1; + return 0; +} + +static int rpmb_sim_write(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + u16 req; + int ret; + + if (!frames) + return -EINVAL; + + if (cnt == 0) + cnt = 1; + + req = be16_to_cpu(frames[0].req_resp); + if (!rsdev->auth_key_set && req != RPMB_PROGRAM_KEY) + return rpmb_op_not_programmed(rsdev, req); + + switch (req) { + case RPMB_PROGRAM_KEY: + dev_dbg(rsdev->dev, "rpmb: program key\n"); + ret = rpmb_op_program_key(rsdev, frames, cnt); + break; + case RPMB_WRITE_DATA: + dev_dbg(rsdev->dev, "rpmb: write data\n"); + ret = rpmb_op_write_data(rsdev, frames, cnt); + break; + case RPMB_GET_WRITE_COUNTER: + dev_dbg(rsdev->dev, "rpmb: get write counter\n"); + ret = rpmb_op_get_wr_counter(rsdev, frames, cnt); + break; + case RPMB_READ_DATA: + dev_dbg(rsdev->dev, "rpmb: read data\n"); + ret = rpmb_op_read_data(rsdev, frames, cnt); + break; + case RPMB_RESULT_READ: + dev_dbg(rsdev->dev, "rpmb: result read\n"); + ret = rpmb_op_result_read(rsdev, frames, cnt); + break; + default: + dev_err(rsdev->dev, "unsupported command %u\n", req); + ret = -EINVAL; + break; + } + + dev_dbg(rsdev->dev, "rpmb: ret=%d\n", ret); + + return ret; +} + +static int rpmb_sim_read(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + int i; + + if (!frames) + return -EINVAL; + + if (cnt == 0) + cnt = 1; + + if (!rsdev->out_frames || rsdev->out_frames_cnt == 0) { + dev_err(rsdev->dev, "out_frames are not set\n"); + return -EINVAL; + } + + if (rsdev->out_frames->req_resp == cpu_to_be16(RPMB_READ_DATA)) + rpmb_do_read_data(rsdev, rsdev->out_frames, cnt); + + for (i = 0; i < min_t(u32, rsdev->out_frames_cnt, cnt); i++) + memcpy(&frames[i], &rsdev->out_frames[i], sizeof(frames[i])); + + if (rsdev->out_frames != rsdev->res_frames) + kfree(rsdev->out_frames); + + rsdev->out_frames = NULL; + rsdev->out_frames_cnt = 0; + dev_dbg(rsdev->dev, "rpmb: cnt=%d\n", cnt); + + return 0; +} + +static int rpmb_sim_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct rpmb_sim_dev *rsdev; + int i; + int ret; + struct rpmb_cmd *cmd; + + if (!dev) + return -EINVAL; + + rsdev = dev_get_drvdata(dev); + + if (!rsdev) + return -EINVAL; + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + if (cmd->flags & RPMB_F_WRITE) + ret = rpmb_sim_write(rsdev, cmd->frames, cmd->nframes); + else + ret = rpmb_sim_read(rsdev, cmd->frames, cmd->nframes); + } + return ret; +} + +static int rpmb_sim_get_capacity(struct device *dev, u8 target) +{ + return daunits; +} + +static struct rpmb_ops rpmb_sim_ops = { + .cmd_seq = rpmb_sim_cmd_seq, + .get_capacity = rpmb_sim_get_capacity, + .type = RPMB_TYPE_EMMC | RPMB_TYPE_SIM, +}; + +static int rpmb_sim_hmac_256_alloc(struct rpmb_sim_dev *rsdev) +{ + struct shash_desc *desc; + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + rsdev->hash_desc = desc; + + dev_dbg(rsdev->dev, "hamac(sha256) registered\n"); + return 0; +} + +static void rpmb_sim_hmac_256_free(struct rpmb_sim_dev *rsdev) +{ + struct shash_desc *desc = rsdev->hash_desc; + + if (desc->tfm) + crypto_free_shash(desc->tfm); + kfree(desc); + + rsdev->hash_desc = NULL; +} + +static int rpmb_sim_probe(struct device *dev) +{ + struct rpmb_sim_dev *rsdev; + int ret; + + rsdev = kzalloc(sizeof(*rsdev), GFP_KERNEL); + if (!rsdev) + return -ENOMEM; + + rsdev->dev = dev; + + ret = rpmb_sim_hmac_256_alloc(rsdev); + if (ret) + goto err; + + rsdev->capacity = CAPACITY_UNIT * daunits; + rsdev->blkcnt = rsdev->capacity / BLK_UNIT; + rsdev->da = kzalloc(rsdev->capacity, GFP_KERNEL); + if (!rsdev->da) { + ret = -ENOMEM; + goto err; + } + + rpmb_sim_ops.dev_id_len = strlen(id); + rpmb_sim_ops.dev_id = id; + rpmb_sim_ops.wr_cnt_max = max_wr_blks; + rpmb_sim_ops.rd_cnt_max = max_wr_blks; + rpmb_sim_ops.block_size = 1; + + rsdev->rdev = rpmb_dev_register(rsdev->dev, 0, &rpmb_sim_ops); + if (IS_ERR(rsdev->rdev)) { + ret = PTR_ERR(rsdev->rdev); + goto err; + } + + dev_info(dev, "registered RPMB capacity = %zu of %zu blocks\n", + rsdev->capacity, rsdev->blkcnt); + + dev_set_drvdata(dev, rsdev); + + return 0; +err: + rpmb_sim_hmac_256_free(rsdev); + if (rsdev) + kfree(rsdev->da); + kfree(rsdev); + return ret; +} + +static int rpmb_sim_remove(struct device *dev) +{ + struct rpmb_sim_dev *rsdev; + + rsdev = dev_get_drvdata(dev); + + rpmb_dev_unregister(rsdev->rdev); + + dev_set_drvdata(dev, NULL); + + rpmb_sim_hmac_256_free(rsdev); + + kfree(rsdev->da); + kfree(rsdev); + return 0; +} + +static void rpmb_sim_shutdown(struct device *dev) +{ + rpmb_sim_remove(dev); +} + +static int rpmb_sim_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static struct bus_type rpmb_sim_bus = { + .name = "rpmb_sim", + .match = rpmb_sim_match, +}; + +static struct device_driver rpmb_sim_drv = { + .name = "rpmb_sim", + .probe = rpmb_sim_probe, + .remove = rpmb_sim_remove, + .shutdown = rpmb_sim_shutdown, +}; + +static void rpmb_sim_dev_release(struct device *dev) +{ +} + +static struct device rpmb_sim_dev; + +static int __init rpmb_sim_init(void) +{ + int ret; + struct device *dev = &rpmb_sim_dev; + struct device_driver *drv = &rpmb_sim_drv; + + ret = bus_register(&rpmb_sim_bus); + if (ret) + return ret; + + dev->bus = &rpmb_sim_bus; + dev->release = rpmb_sim_dev_release; + dev_set_name(dev, "%s", "rpmb_sim"); + ret = device_register(dev); + if (ret) { + pr_err("device register failed %d\n", ret); + goto err_device; + } + + drv->bus = &rpmb_sim_bus; + ret = driver_register(drv); + if (ret) { + pr_err("driver register failed %d\n", ret); + goto err_driver; + } + + return 0; + +err_driver: + device_unregister(dev); +err_device: + bus_unregister(&rpmb_sim_bus); + return ret; +} + +static void __exit rpmb_sim_exit(void) +{ + struct device *dev = &rpmb_sim_dev; + struct device_driver *drv = &rpmb_sim_drv; + + device_unregister(dev); + driver_unregister(drv); + bus_unregister(&rpmb_sim_bus); +} + +module_init(rpmb_sim_init); +module_exit(rpmb_sim_exit); + +MODULE_AUTHOR("Tomas Winkler Date: Sun, 20 Mar 2016 11:32:42 +0200 Subject: [PATCH 1004/1276] tools rpmb: add RPBM access tool Add simple RPMB host testing tool. It can be used to program key, write and read data block, and retrieve write counter. V2: Resend. V3: Fix missing objtool. V4: Add verbose option. V5: 1. Adjust to the new API. 2. Exercise both request and sequence ioctls. V6: 1. Add includes to openssl/rand.h and endian.h. 2. Fix some signed, unsigned comparisons. 3. Check results more thoroughly. 4. use HOSTCFLAGS in compilation. 5. Allocate frames dynamically. V7: 1. Fix rpmb_alloc_frames, it has always allocated one frame instead of requested number. 2. Use an inline function instead of macro for rw blocking wrapper. V8: 1. Free frames in write_counter functions. 2. Enhance error and debug messages. 3. Tool can be compiled statically if RPMB_STATIC is set 4. Support for openssl 1.1.0 V9: 1. Remove request_cmd() interface. 2. Fix c&p error in rpmb_result_str 3. Use new version and capability ioctls. 4. Add get-info argument 5. Use SPDX identifiers. Change-Id: I914bb9918e7a555932b38b11950b22fd5187799b Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- MAINTAINERS | 1 + tools/Makefile | 15 +- tools/rpmb/.gitignore | 2 + tools/rpmb/Makefile | 38 ++ tools/rpmb/rpmb.c | 1020 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1070 insertions(+), 6 deletions(-) create mode 100644 tools/rpmb/.gitignore create mode 100644 tools/rpmb/Makefile create mode 100644 tools/rpmb/rpmb.c diff --git a/MAINTAINERS b/MAINTAINERS index 1868f61fffc1..861ef1fb8e6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12535,6 +12535,7 @@ F: drivers/char/rpmb/* F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb +F: tools/rpmb/ RTL2830 MEDIA DRIVER M: Antti Palosaari diff --git a/tools/Makefile b/tools/Makefile index be02c8b904db..72f644639fcc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -22,6 +22,7 @@ help: @echo ' liblockdep - user-space wrapper for kernel locking-validator' @echo ' bpf - misc BPF tools' @echo ' perf - Linux performance measurement and analysis tool' + @echo ' rpmb - Replay protected memory block access tool' @echo ' selftests - various kernel selftests' @echo ' spi - spi tools' @echo ' objtool - an ELF object analysis tool' @@ -59,7 +60,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE +cgroup firewire hv guest rpmb spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE $(call descend,$@) liblockdep: FORCE @@ -92,7 +93,7 @@ kvm_stat: FORCE $(call descend,kvm/$@) all: acpi cgroup cpupower gpio hv firewire liblockdep \ - perf selftests spi turbostat usb \ + perf rpmb selftests spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi @@ -102,7 +103,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install: +cgroup_install firewire_install gpio_install hv_install iio_install rpmb_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install: $(call descend,$(@:_install=),install) liblockdep_install: @@ -125,11 +126,10 @@ kvm_stat_install: install: acpi_install cgroup_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ - perf_install selftests_install turbostat_install usb_install \ + perf_install rpmb_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install \ wmi_install - acpi_clean: $(call descend,power/acpi,clean) @@ -155,6 +155,9 @@ perf_clean: $(Q)mkdir -p $(PERF_O) . $(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= clean +rpmb_clean: + $(call descend,$(@:_clean=),clean) + selftests_clean: $(call descend,testing/$(@:_clean=),clean) @@ -171,7 +174,7 @@ build_clean: $(call descend,build,clean) clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ - perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ + perf_clean rpmb_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ gpio_clean objtool_clean leds_clean wmi_clean diff --git a/tools/rpmb/.gitignore b/tools/rpmb/.gitignore new file mode 100644 index 000000000000..218f680548e6 --- /dev/null +++ b/tools/rpmb/.gitignore @@ -0,0 +1,2 @@ +*.o +rpmb diff --git a/tools/rpmb/Makefile b/tools/rpmb/Makefile new file mode 100644 index 000000000000..c99d5f9e9439 --- /dev/null +++ b/tools/rpmb/Makefile @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +PKG_CONFIG = $(CROSS_COMPILE)pkg-config + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +INSTALL = install +prefix ?= /usr/local +bindir = $(prefix)/bin + + +CFLAGS += $(HOSTCFLAGS) +CFLAGS += -D__EXPORTED_HEADERS__ -g +ifdef RPMB_STATIC +LDFLAGS += -pthread -static +CFLAGS += -pthread -static +PKG_STATIC = --static +endif +CFLAGS += -I$(srctree)/include/uapi -I$(srctree)/include +LDLIBS += $(shell $(PKG_CONFIG) --libs $(PKG_STATIC) libcrypto) + +prog := rpmb + +all : $(prog) + +$(prog): rpmb.o + +clean : + $(RM) $(prog) *.o + +install: $(prog) + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(prog) $(DESTDIR)$(bindir) diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c new file mode 100644 index 000000000000..1a507db2739e --- /dev/null +++ b/tools/rpmb/rpmb.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (C) 2016-2018 Intel Corp. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "linux/rpmb.h" + +#define RPMB_KEY_SIZE 32 +#define RPMB_MAC_SIZE 32 +#define RPMB_NONCE_SIZE 16 + +bool verbose; +#define rpmb_dbg(fmt, ARGS...) do { \ + if (verbose) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS); \ +} while (0) + +#define rpmb_msg(fmt, ARGS...) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS) + +#define rpmb_err(fmt, ARGS...) \ + fprintf(stderr, "rpmb: error: " fmt, ##ARGS) + +static const char *rpmb_op_str(uint16_t op) +{ +#define RPMB_OP(_op) case RPMB_##_op: return #_op + + switch (op) { + RPMB_OP(PROGRAM_KEY); + RPMB_OP(GET_WRITE_COUNTER); + RPMB_OP(WRITE_DATA); + RPMB_OP(READ_DATA); + RPMB_OP(RESULT_READ); + break; + default: + return "unknown"; + } +#undef RPMB_OP +} + +static const char *rpmb_result_str(enum rpmb_op_result result) +{ +#define str(x) #x +#define RPMB_ERR(_res) case RPMB_ERR_##_res: \ + { if (result & RPMB_ERR_COUNTER_EXPIRED) \ + return "COUNTER_EXPIRE:" str(_res); \ + else \ + return str(_res); \ + } + + switch (result & 0x000F) { + RPMB_ERR(OK); + RPMB_ERR(GENERAL); + RPMB_ERR(AUTH); + RPMB_ERR(COUNTER); + RPMB_ERR(ADDRESS); + RPMB_ERR(WRITE); + RPMB_ERR(READ); + RPMB_ERR(NO_KEY); + break; + default: + return "unknown"; + } +#undef RPMB_ERR +#undef str +}; + +static inline void __dump_buffer(const char *buf) +{ + fprintf(stderr, "%s\n", buf); +} + +static void +dump_hex_buffer(const char *title, const void *buf, size_t len) +{ + const unsigned char *_buf = (const unsigned char *)buf; + const size_t pbufsz = 16 * 3; + char pbuf[pbufsz]; + int j = 0; + + if (title) + fprintf(stderr, "%s\n", title); + while (len-- > 0) { + snprintf(&pbuf[j], pbufsz - j, "%02X ", *_buf++); + j += 3; + if (j == 16 * 3) { + __dump_buffer(pbuf); + j = 0; + } + } + if (j) + __dump_buffer(pbuf); +} + +static int open_dev_file(const char *devfile, struct rpmb_ioc_cap_cmd *cap) +{ + struct rpmb_ioc_ver_cmd ver; + int fd; + int ret; + + fd = open(devfile, O_RDWR); + if (fd < 0) + rpmb_err("Cannot open: %s: %s.\n", devfile, strerror(errno)); + + ret = ioctl(fd, RPMB_IOC_VER_CMD, &ver); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + printf("RPMB API Version %X\n", ver.api_version); + + ret = ioctl(fd, RPMB_IOC_CAP_CMD, cap); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + rpmb_dbg("RPMB device_type = %hd\n", cap->device_type); + rpmb_dbg("RPMB rpmb_target = %hd\n", cap->target); + rpmb_dbg("RPMB capacity = %hd\n", cap->capacity); + rpmb_dbg("RPMB block_size = %hd\n", cap->block_size); + rpmb_dbg("RPMB wr_cnt_max = %hd\n", cap->wr_cnt_max); + rpmb_dbg("RPMB rd_cnt_max = %hd\n", cap->rd_cnt_max); + rpmb_dbg("RPMB auth_method = %hd\n", cap->auth_method); + + return fd; +err: + close(fd); + return -1; +} + +static int open_rd_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDIN_FILENO; + else + fd = open(datafile, O_RDONLY); + + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + + return fd; +} + +static int open_wr_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDOUT_FILENO; + else + fd = open(datafile, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + return fd; +} + +static void close_fd(int fd) +{ + if (fd > 0 && fd != STDIN_FILENO && fd != STDOUT_FILENO) + close(fd); +} + +/* need to just cast out 'const' in write(2) */ +typedef ssize_t (*rwfunc_t)(int fd, void *buf, size_t count); +/* blocking rw wrapper */ +static inline ssize_t rw(rwfunc_t func, int fd, unsigned char *buf, size_t size) +{ + ssize_t ntotal = 0, n; + char *_buf = (char *)buf; + + do { + n = func(fd, _buf + ntotal, size - ntotal); + if (n == -1 && errno != EINTR) { + ntotal = -1; + break; + } else if (n > 0) { + ntotal += n; + } + } while (n != 0 && (size_t)ntotal != size); + + return ntotal; +} + +static ssize_t read_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw(read, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s\n.", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("read %zd but must be %zu bytes length.\n", ret, size); + ret = -EINVAL; + } + + return ret; +} + +static ssize_t write_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw((rwfunc_t)write, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s.\n", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("data is %zd but must be %zu bytes length.\n", + ret, size); + ret = -EINVAL; + } + return ret; +} + +static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) +{ + uint16_t result, req_resp; + + if (!verbose) + return; + + if (!f) + return; + + result = be16toh(f->result); + req_resp = be16toh(f->req_resp); + if (req_resp & 0xf00) + req_resp = RPMB_RESP2REQ(req_resp); + + fprintf(stderr, "--------------- %s ---------------\n", + title ? title : "start"); + fprintf(stderr, "ptr: %p\n", f); + dump_hex_buffer("key_mac: ", f->key_mac, 32); + dump_hex_buffer("data: ", f->data, 256); + dump_hex_buffer("nonce: ", f->nonce, 16); + fprintf(stderr, "write_counter: %u\n", be32toh(f->write_counter)); + fprintf(stderr, "address: %0X\n", be16toh(f->addr)); + fprintf(stderr, "block_count: %u\n", be16toh(f->block_count)); + fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); + fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); + fprintf(stderr, "--------------- End ---------------\n"); +} + +static struct rpmb_frame_jdec *rpmb_alloc_frames(unsigned int cnt) +{ + return calloc(1, rpmb_ioc_frames_len_jdec(cnt)); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < blocks_cnt; i++) + HMAC_Update(&ctx, frames[i].data, hmac_data_len); + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_cleanup(&ctx); + return ret == 1 ? 0 : -1; +} +#else +static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX *ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + ctx = HMAC_CTX_new(); + + ret = HMAC_Init_ex(ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < blocks_cnt; i++) + HMAC_Update(ctx, frames[i].data, rpmb_jdec_hmac_data_len); + + ret = HMAC_Final(ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_free(ctx); + return ret == 1 ? 0 : -1; +} +#endif + +static int rpmb_check_req_resp(uint16_t req, struct rpmb_frame_jdec *frame_out) +{ + if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { + rpmb_err("RPMB response mismatch %04X != %04X\n.", + RPMB_REQ2RESP(req), be16toh(frame_out->req_resp)); + return -1; + } + return 0; +} + +static int rpmb_check_mac(const unsigned char *key, + struct rpmb_frame_jdec *frames_out, + unsigned int cnt_out) +{ + unsigned char mac[RPMB_MAC_SIZE]; + + if (cnt_out == 0) { + rpmb_err("RPMB 0 output frames.\n"); + return -1; + } + + rpmb_calc_hmac_sha256(frames_out, cnt_out, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE); + + if (memcmp(mac, frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE)) { + rpmb_err("RPMB hmac mismatch:\n"); + dump_hex_buffer("Result MAC: ", + frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE); + dump_hex_buffer("Expected MAC: ", mac, RPMB_MAC_SIZE); + return -1; + } + + return 0; +} + +static int rpmb_ioctl(int fd, uint16_t req, + const struct rpmb_frame_jdec *frames_in, + unsigned int cnt_in, + struct rpmb_frame_jdec *frames_out, + unsigned int cnt_out) +{ + int ret; + struct __attribute__((packed)) { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame_jdec *frame_res = NULL; + int i; + uint32_t flags; + + rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req)); + dbg_dump_frame("In Frame: ", frames_in); + + i = 0; + flags = RPMB_F_WRITE; + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) + flags |= RPMB_F_REL_WRITE; + rpmb_ioc_cmd_set(iseq.cmd[i], flags, frames_in, cnt_in); + i++; + + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { + frame_res = rpmb_alloc_frames(0); + if (!frame_res) + return -ENOMEM; + frame_res->req_resp = htobe16(RPMB_RESULT_READ); + rpmb_ioc_cmd_set(iseq.cmd[i], RPMB_F_WRITE, frame_res, 0); + i++; + } + + rpmb_ioc_cmd_set(iseq.cmd[i], 0, frames_out, cnt_out); + i++; + + iseq.h.num_of_cmds = i; + ret = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + if (ret < 0) + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + + ret = rpmb_check_req_resp(req, frames_out); + + dbg_dump_frame("Res Frame: ", frame_res); + dbg_dump_frame("Out Frame: ", frames_out); + free(frame_res); + return ret; +} + +static int op_get_info(int nargs, char *argv[]) +{ + int dev_fd; + struct rpmb_ioc_cap_cmd cap; + + if (nargs != 1) + return -EINVAL; + + memset(&cap, 0, sizeof(cap)); + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + return -errno; + argv++; + + printf("RPMB device_type = %hd\n", cap.device_type); + printf("RPMB rpmb_target = %hd\n", cap.target); + printf("RPMB capacity = %hd\n", cap.capacity); + printf("RPMB block_size = %hd\n", cap.block_size); + printf("RPMB wr_cnt_max = %hd\n", cap.wr_cnt_max); + printf("RPMB rd_cnt_max = %hd\n", cap.rd_cnt_max); + printf("RPMB auth_method = %hd\n", cap.auth_method); + + close(dev_fd); + + return 0; +} + +static int op_rpmb_program_key(int nargs, char *argv[]) +{ + int ret; + int dev_fd = -1, key_fd = -1; + uint16_t req = RPMB_PROGRAM_KEY; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frame_in = NULL, *frame_out = NULL; + + ret = -EINVAL; + if (nargs != 2) + return ret; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + frame_in = rpmb_alloc_frames(0); + frame_out = rpmb_alloc_frames(0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + + read_file(key_fd, frame_in->key_mac, RPMB_KEY_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + if (ret) + goto out; + + if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { + rpmb_err("RPMB response mismatch.\n"); + ret = -1; + goto out; + } + + ret = be16toh(frame_out->result); + if (ret) + rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + +out: + free(frame_in); + free(frame_out); + close_fd(dev_fd); + close_fd(key_fd); + + return ret; +} + +static int rpmb_get_write_counter(int dev_fd, unsigned int *cnt, + const unsigned char *key) +{ + int ret; + uint16_t res = 0x000F; + uint16_t req = RPMB_GET_WRITE_COUNTER; + struct rpmb_frame_jdec *frame_in = NULL; + struct rpmb_frame_jdec *frame_out = NULL; + + frame_in = rpmb_alloc_frames(0); + frame_out = rpmb_alloc_frames(0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + if (ret) + goto out; + + res = be16toh(frame_out->result); + if (res != RPMB_ERR_OK) { + ret = -1; + goto out; + } + + if (memcmp(&frame_in->nonce, &frame_out->nonce, RPMB_NONCE_SIZE)) { + rpmb_err("RPMB NONCE mismatch\n"); + dump_hex_buffer("Result NONCE:", + &frame_out->nonce, RPMB_NONCE_SIZE); + dump_hex_buffer("Expected NONCE: ", + &frame_in->nonce, RPMB_NONCE_SIZE); + ret = -1; + goto out; + } + + if (key) { + ret = rpmb_check_mac(key, frame_out, 1); + if (ret) + goto out; + } + + *cnt = be32toh(frame_out->write_counter); + +out: + if (ret) + rpmb_err("RPMB operation %s failed=%d %s[0x%04x]\n", + rpmb_op_str(req), ret, rpmb_result_str(res), res); + free(frame_in); + free(frame_out); + return ret; +} + +static int op_rpmb_get_write_counter(int nargs, char **argv) +{ + int ret; + int dev_fd = -1, key_fd = -1; + bool has_key; + struct rpmb_ioc_cap_cmd cap; + unsigned char key[RPMB_KEY_SIZE]; + unsigned int cnt; + + if (nargs == 2) + has_key = true; + else if (nargs == 1) + has_key = false; + else + return -EINVAL; + + ret = -EINVAL; + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + return ret; + argv++; + + if (has_key) { + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + + ret = rpmb_get_write_counter(dev_fd, &cnt, key); + } else { + ret = rpmb_get_write_counter(dev_fd, &cnt, NULL); + } + + if (!ret) + printf("Counter value: 0x%08x\n", cnt); + +out: + close_fd(dev_fd); + close_fd(key_fd); + return ret; +} + +static int op_rpmb_read_blocks(int nargs, char **argv) +{ + int i, ret; + int dev_fd = -1, data_fd = -1, key_fd = -1; + uint16_t req = RPMB_READ_DATA; + uint16_t addr, blocks_cnt; + unsigned char key[RPMB_KEY_SIZE]; + unsigned long numarg; + bool has_key; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frame_in = NULL; + struct rpmb_frame_jdec *frames_out = NULL; + struct rpmb_frame_jdec *frame_out; + + ret = -EINVAL; + if (nargs == 4) + has_key = false; + else if (nargs == 5) + has_key = true; + else + return ret; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address\n"); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_wr_file(argv[0], "output data"); + if (data_fd < 0) + goto out; + argv++; + + if (has_key) { + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + } + + ret = 0; + frames_out = rpmb_alloc_frames(blocks_cnt); + frame_in = rpmb_alloc_frames(0); + if (!frames_out || !frame_in) { + rpmb_err("Cannot allocate %d RPMB frames\n", blocks_cnt); + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + frame_in->addr = htobe16(addr); + /* eMMc spec ask for 0 here this will be translated by the rpmb layer */ + frame_in->block_count = htobe16(blocks_cnt); + RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frames_out, blocks_cnt); + if (ret) + goto out; + + frame_out = &frames_out[blocks_cnt - 1]; + ret = be16toh(frame_out->result); + if (ret) { + rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + goto out; + } + + if (has_key) { + ret = rpmb_check_mac(key, frames_out, blocks_cnt); + if (ret) + goto out; + } + + for (i = 0; i < blocks_cnt; i++) { + ret = write_file(data_fd, frames_out[i].data, + sizeof(frames_out[i].data)); + if (ret < 0) + goto out; + } + +out: + free(frame_in); + free(frames_out); + close_fd(dev_fd); + close_fd(data_fd); + close_fd(key_fd); + + return ret; +} + +static int op_rpmb_write_blocks(int nargs, char **argv) +{ + int ret; + int dev_fd = -1, key_fd = -1, data_fd = -1; + int i; + uint16_t req = RPMB_WRITE_DATA; + unsigned char key[RPMB_KEY_SIZE]; + unsigned char mac[RPMB_MAC_SIZE]; + unsigned long numarg; + uint16_t addr, blocks_cnt; + uint32_t write_counter; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frames_in = NULL; + struct rpmb_frame_jdec *frame_out = NULL; + + ret = -EINVAL; + if (nargs != 5) + goto out; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address %s\n", argv[0]); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_rd_file(argv[0], "data file"); + if (data_fd < 0) + goto out; + argv++; + + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + + frames_in = rpmb_alloc_frames(blocks_cnt); + frame_out = rpmb_alloc_frames(0); + if (!frames_in || !frame_out) { + rpmb_err("can't allocate memory for RPMB outer frames\n"); + ret = -ENOMEM; + goto out; + } + + ret = rpmb_get_write_counter(dev_fd, &write_counter, key); + if (ret) + goto out; + + for (i = 0; i < blocks_cnt; i++) { + frames_in[i].req_resp = htobe16(req); + frames_in[i].block_count = htobe16(blocks_cnt); + frames_in[i].addr = htobe16(addr); + frames_in[i].write_counter = htobe32(write_counter); + } + + for (i = 0; i < blocks_cnt; i++) { + ret = read_file(data_fd, frames_in[i].data, + sizeof(frames_in[0].data)); + if (ret < 0) + goto out; + } + + rpmb_calc_hmac_sha256(frames_in, blocks_cnt, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE); + memcpy(frames_in[blocks_cnt - 1].key_mac, mac, RPMB_MAC_SIZE); + ret = rpmb_ioctl(dev_fd, req, frames_in, blocks_cnt, frame_out, 0); + if (ret != 0) + goto out; + + ret = be16toh(frame_out->result); + if (ret) { + rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + ret = -1; + } + + if (be16toh(frame_out->addr) != addr) { + rpmb_err("RPMB addr mismatchs res=%04x req=%04x\n", + be16toh(frame_out->addr), addr); + ret = -1; + } + + if (be32toh(frame_out->write_counter) <= write_counter) { + rpmb_err("RPMB write counter not incremented res=%x req=%x\n", + be32toh(frame_out->write_counter), write_counter); + ret = -1; + } + + ret = rpmb_check_mac(key, frame_out, 1); +out: + free(frames_in); + free(frame_out); + close_fd(dev_fd); + close_fd(data_fd); + close_fd(key_fd); + return ret; +} + +typedef int (*rpmb_op)(int argc, char *argv[]); + +struct rpmb_cmd { + const char *op_name; + rpmb_op op; + const char *usage; /* usage title */ + const char *help; /* help */ +}; + +static const struct rpmb_cmd cmds[] = { + { + "get-info", + op_get_info, + "", + " Get RPMB device info\n", + }, + { + "program-key", + op_rpmb_program_key, + " ", + " Program authentication key of 32 bytes length from the KEY_FILE\n" + " when KEY_FILE is -, read standard input.\n" + " NOTE: This is a one-time programmable irreversible change.\n", + }, + { + "write-counter", + op_rpmb_get_write_counter, + " [KEY_FILE]", + " Rertrive write counter value from the to stdout.\n" + " When KEY_FILE is present data is verified via HMAC\n" + " when KEY_FILE is -, read standard input.\n" + }, + { + "write-blocks", + op_rpmb_write_blocks, + "
", + " of 256 bytes will be written from the DATA_FILE\n" + " to the at block offset
.\n" + " When DATA_FILE is -, read from standard input.\n", + }, + { + "read-blocks", + op_rpmb_read_blocks, + "
[KEY_FILE]", + " of 256 bytes will be read from \n" + " to the OUTPUT_FILE\n" + " When KEY_FILE is present data is verified via HMAC\n" + " When OUTPUT/KEY_FILE is -, read from standard input.\n" + " When OUTPUT_FILE is -, write to standard output\n", + }, + + { NULL, NULL, NULL, NULL } +}; + +static void help(const char *prog, const struct rpmb_cmd *cmd) +{ + printf("%s %s %s\n", prog, cmd->op_name, cmd->usage); + printf("%s\n", cmd->help); +} + +static void usage(const char *prog) +{ + int i; + + printf("\n"); + printf("Usage: %s [-v] \n\n", prog); + for (i = 0; cmds[i].op_name; i++) + printf(" %s %s %s\n", + prog, cmds[i].op_name, cmds[i].usage); + + printf("\n"); + printf(" %s -v/--verbose: runs in verbose mode\n", prog); + printf(" %s help : shows this help\n", prog); + printf(" %s help : shows detailed help\n", prog); +} + +static bool call_for_help(const char *arg) +{ + return !strcmp(arg, "help") || + !strcmp(arg, "-h") || + !strcmp(arg, "--help"); +} + +static bool parse_verbose(const char *arg) +{ + return !strcmp(arg, "-v") || + !strcmp(arg, "--verbose"); +} + +static const +struct rpmb_cmd *parse_args(const char *prog, int *_argc, char **_argv[]) +{ + int i; + int argc = *_argc; + char **argv = *_argv; + const struct rpmb_cmd *cmd = NULL; + bool need_help = false; + + argc--; argv++; + + if (argc == 0) + goto out; + + if (call_for_help(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + need_help = true; + } + + if (parse_verbose(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + verbose = true; + } + + for (i = 0; cmds[i].op_name; i++) { + if (!strncmp(argv[0], cmds[i].op_name, + strlen(cmds[i].op_name))) { + cmd = &cmds[i]; + argc--; argv++; + break; + } + } + + if (!cmd) + goto out; + + if (need_help || (argc > 0 && call_for_help(argv[0]))) { + help(prog, cmd); + argc--; argv++; + return NULL; + } + +out: + *_argc = argc; + *_argv = argv; + + if (!cmd) + usage(prog); + + return cmd; +} + +int main(int argc, char *argv[]) +{ + const char *prog = basename(argv[0]); + const struct rpmb_cmd *cmd; + int ret; + + cmd = parse_args(prog, &argc, &argv); + if (!cmd) + exit(EXIT_SUCCESS); + + ret = cmd->op(argc, argv); + if (ret == -EINVAL) + help(prog, cmd); + + if (ret) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} From d8b8bf456fef1fa12e5b47ce0861f9fe7c42a713 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 28 Jan 2015 17:01:34 +0200 Subject: [PATCH 1005/1276] mmc: block: register RPMB partition with the RPMB subsystem Register eMMC RPMB partition with the RPMB subsystem and provide implementation for the RPMB access operations abstracting actual multi step process. V2: resend V3: commit message fix V4: Kconfig: use select RPMB to ensure valid configuration Switch back to main area after RPMB access V5: Revamp code using new sequence command Support for 8K packets in e.MMC v5.1 V6: Resend. V7: Resend. V8: Rebase after block.c was moved under core/ Rebase for 4.14 V9: Rebase for 4.16 and 4.17 Build RPMB connection above ioctl layer Supply RPMB capabilities. Change-Id: I6de67f475ef738e30dc3b8c78185a1bee24595b2 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- drivers/mmc/core/Kconfig | 1 + drivers/mmc/core/block.c | 222 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 220 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 42e89060cd41..96c7ff63178c 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -36,6 +36,7 @@ config PWRSEQ_SIMPLE config MMC_BLOCK tristate "MMC block device driver" depends on BLOCK + select RPMB default y help Say Y here to enable the MMC block device driver support. diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index e201ccb3fda4..078e4fdc8203 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -409,8 +410,8 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, return 0; } -static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, - u32 retries_max) +static int ioctl_mmc_blk_rpmb_status_poll(struct mmc_card *card, u32 *status, + u32 retries_max) { int err; u32 retry_count = 0; @@ -612,7 +613,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, * Ensure RPMB command has completed by polling CMD13 * "Send Status". */ - err = ioctl_rpmb_card_status_poll(card, &status, 5); + err = ioctl_mmc_blk_rpmb_status_poll(card, &status, 5); if (err) dev_err(mmc_dev(card->host), "%s: Card Status=0x%08X, error %d\n", @@ -1116,6 +1117,217 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) blk_mq_end_request(req, ret ? BLK_STS_IOERR : BLK_STS_OK); } +static int mmc_blk_rpmb_process(struct mmc_blk_data *md, + struct mmc_blk_ioc_data *idata[], + u64 num_of_cmds) +{ + struct mmc_card *card; + struct mmc_queue *mq; + int err = 0; + struct request *req; + int op_mode; + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_err; + } + + /* + * Dispatch the ioctl()s into the block request queue. + */ + mq = &md->queue; + op_mode = idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, + req = blk_get_request(mq->queue, op_mode, 0); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto cmd_err; + } + + req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL_RPMB; + req_to_mmc_queue_req(req)->drv_op_data = idata; + req_to_mmc_queue_req(req)->ioc_count = num_of_cmds; + + blk_execute_rq(mq->queue, NULL, req, 0); + + err = req_to_mmc_queue_req(req)->drv_op_result; + + blk_put_request(req); + +cmd_err: + return err; +} + +static +struct mmc_blk_ioc_data *mmc_blk_rpmb_cmd_to_ioc_data(struct rpmb_cmd *cmd) +{ + struct mmc_blk_ioc_data *idata; + int err; + + idata = kzalloc(sizeof(*idata), GFP_KERNEL); + if (!idata) { + err = -ENOMEM; + goto out; + } + + if (cmd->flags & RPMB_F_WRITE) { + idata->ic.opcode = MMC_WRITE_MULTIPLE_BLOCK; + idata->ic.write_flag = 1; + if (cmd->flags & RPMB_F_REL_WRITE) + idata->ic.write_flag |= 1 << 31; + } else { + idata->ic.opcode = MMC_READ_MULTIPLE_BLOCK; + } + + /* nframes == 0 in case there is only meta data in the frame */ + idata->ic.blocks = cmd->nframes ?: 1; + idata->ic.blksz = 512; + + idata->buf_bytes = (u64)idata->ic.blksz * idata->ic.blocks; + if (idata->buf_bytes > MMC_IOC_MAX_BYTES) { + err = -EOVERFLOW; + goto out; + } + + idata->buf = (unsigned char *)cmd->frames; + + return idata; +out: + kfree(idata); + return ERR_PTR(err); +} + +static int mmc_blk_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, + u32 num_of_cmds) +{ + struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + struct mmc_blk_ioc_data **idata; + int err = 0; + u32 i; + + if (!rpmb) + return -ENODEV; + + idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + for (i = 0; i < num_of_cmds; i++) { + idata[i] = mmc_blk_rpmb_cmd_to_ioc_data(&cmds[i]); + if (IS_ERR(idata[i])) { + err = PTR_ERR(idata[i]); + num_of_cmds = i; + goto cmd_err; + } + idata[i]->rpmb = rpmb; + } + + get_device(&rpmb->dev); + mmc_blk_get(rpmb->md->disk); + + err = mmc_blk_rpmb_process(rpmb->md, idata, num_of_cmds); + +cmd_err: + for (i = 0; i < num_of_cmds; i++) + kfree(idata[i]); + + kfree(idata); + + put_device(&rpmb->dev); + mmc_blk_put(rpmb->md); + + return err; +} + +static int mmc_blk_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + struct mmc_card *card; + + card = rpmb->md->queue.card; + return card->ext_csd.raw_rpmb_size_mult; +} + +static struct rpmb_ops mmc_rpmb_dev_ops = { + .cmd_seq = mmc_blk_rpmb_cmd_seq, + .get_capacity = mmc_blk_rpmb_get_capacity, + .type = RPMB_TYPE_EMMC, + .auth_method = RPMB_HMAC_ALGO_SHA_256, +}; + +static void mmc_blk_rpmb_unset_dev_id(struct rpmb_ops *ops) +{ + kfree(ops->dev_id); + ops->dev_id = NULL; +} + +static int mmc_blk_rpmb_set_dev_id(struct rpmb_ops *ops, struct mmc_card *card) +{ + char *id; + + id = kmalloc(sizeof(card->raw_cid), GFP_KERNEL); + if (!id) + return -ENOMEM; + + memcpy(id, card->raw_cid, sizeof(card->raw_cid)); + ops->dev_id = id; + ops->dev_id_len = sizeof(card->raw_cid); + + return 0; +} + +static void mmc_blk_rpmb_set_cap(struct rpmb_ops *ops, + struct mmc_card *card) +{ + u16 rel_wr_cnt; + + /* RPMB blocks are written in half sectors hence '* 2' */ + rel_wr_cnt = card->ext_csd.rel_sectors * 2; + /* eMMC 5.1 may support RPMB 8K (32) frames */ + if (card->ext_csd.rev >= 8) { + if (card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) + rel_wr_cnt = 32; + else + rel_wr_cnt = 2; + } + ops->wr_cnt_max = rel_wr_cnt; + ops->rd_cnt_max = card->host->max_blk_count; + ops->block_size = 1; /* 256B */ +} + +static void mmc_blk_rpmb_add(struct mmc_card *card) +{ + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); + struct rpmb_dev *rdev; + struct mmc_rpmb_data *rpmb; + u8 i = 0; + + mmc_blk_rpmb_set_dev_id(&mmc_rpmb_dev_ops, card); + mmc_blk_rpmb_set_cap(&mmc_rpmb_dev_ops, card); + + /* Add RPMB partitions */ + list_for_each_entry(rpmb, &md->rpmbs, node) { + rdev = rpmb_dev_register(&rpmb->dev, i++, &mmc_rpmb_dev_ops); + if (IS_ERR(rdev)) { + pr_warn("%s: cannot register to rpmb %ld\n", + dev_name(&rpmb->dev), PTR_ERR(rdev)); + } + } +} + +static void mmc_blk_rpmb_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); + struct mmc_rpmb_data *rpmb; + u8 i = 0; + + list_for_each_entry(rpmb, &md->rpmbs, node) + rpmb_dev_unregister_by_device(&rpmb->dev, i++); + + mmc_blk_rpmb_unset_dev_id(&mmc_rpmb_dev_ops); +} + static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->blkdata; @@ -2946,6 +3158,9 @@ static int mmc_blk_probe(struct mmc_card *card) goto out; } + /* Add rpmb layer */ + mmc_blk_rpmb_add(card); + /* Add two debugfs entries */ mmc_blk_add_debugfs(card, md); @@ -2974,6 +3189,7 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = dev_get_drvdata(&card->dev); mmc_blk_remove_debugfs(card, md); + mmc_blk_rpmb_remove(card); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); if (md->part_curr != md->part_type) { From 598dc3a35c45b09581c734dfba484810976deb80 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 4 Jan 2017 14:14:21 +0200 Subject: [PATCH 1006/1276] scsi: ufs: revamp string descriptor reading Define new a type: uc_string_id for easier string handling and less casting. Reduce number or string copies in price of a dynamic allocation. In addition drop usage of variable length array (VLA) as it's not considered to be safe. V9: Fix memory corruption. Change-Id: Ieda6a4b68e60b8a2d8a2d93a371ff5396dec989b Signed-off-by: Tomas Winkler --- drivers/scsi/ufs/ufs-sysfs.c | 20 ++--- drivers/scsi/ufs/ufs.h | 2 +- drivers/scsi/ufs/ufshcd.c | 163 +++++++++++++++++++++-------------- drivers/scsi/ufs/ufshcd.h | 9 +- 4 files changed, 114 insertions(+), 80 deletions(-) diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 8d9332bb7d0c..0b221c5a244c 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -570,10 +570,11 @@ static ssize_t _name##_show(struct device *dev, \ struct ufs_hba *hba = dev_get_drvdata(dev); \ int ret; \ int desc_len = QUERY_DESC_MAX_SIZE; \ - u8 *desc_buf; \ + char *desc_buf; \ + \ desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_ATOMIC); \ - if (!desc_buf) \ - return -ENOMEM; \ + if (!desc_buf) \ + return -ENOMEM; \ ret = ufshcd_query_descriptor_retry(hba, \ UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \ 0, 0, desc_buf, &desc_len); \ @@ -582,14 +583,13 @@ static ssize_t _name##_show(struct device *dev, \ goto out; \ } \ index = desc_buf[DEVICE_DESC_PARAM##_pname]; \ - memset(desc_buf, 0, QUERY_DESC_MAX_SIZE); \ - if (ufshcd_read_string_desc(hba, index, desc_buf, \ - QUERY_DESC_MAX_SIZE, true)) { \ - ret = -EINVAL; \ + kfree(desc_buf); \ + desc_buf = NULL; \ + ret = ufshcd_read_string_desc(hba, index, &desc_buf, \ + SD_ASCII_STD); \ + if (ret < 0) \ goto out; \ - } \ - ret = snprintf(buf, PAGE_SIZE, "%s\n", \ - desc_buf + QUERY_DESC_HDR_SIZE); \ + ret = snprintf(buf, PAGE_SIZE, "%s\n", desc_buf); \ out: \ kfree(desc_buf); \ return ret; \ diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 14e5bf7af0bb..c9774b59b6bf 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -617,7 +617,7 @@ struct ufs_dev_info { */ struct ufs_dev_desc { u16 wmanufacturerid; - char model[MAX_MODEL_LEN + 1]; + char *model; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c55f38ec391c..0ee9a4baaeb8 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -297,16 +297,6 @@ static void ufshcd_scsi_block_requests(struct ufs_hba *hba) scsi_block_requests(hba->host); } -/* replace non-printable or non-ASCII characters with spaces */ -static inline void ufshcd_remove_non_printable(char *val) -{ - if (!val) - return; - - if (*val < 0x20 || *val > 0x7e) - *val = ' '; -} - static void ufshcd_add_cmd_upiu_trace(struct ufs_hba *hba, unsigned int tag, const char *str) { @@ -3120,7 +3110,7 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, u8 param_offset, - u8 *param_read_buf, + void *param_read_buf, u8 param_size) { int ret; @@ -3188,7 +3178,7 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, static inline int ufshcd_read_desc(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, - u8 *buf, + void *buf, u32 size) { return ufshcd_read_desc_param(hba, desc_id, desc_index, 0, buf, size); @@ -3206,49 +3196,77 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size); } +/** + * struct uc_string_id - unicode string + * + * @len: size of this descriptor inclusive + * @type: descriptor type + * @uc: unicode string character + */ +struct uc_string_id { + u8 len; + u8 type; + wchar_t uc[0]; +} __packed; + +/* replace non-printable or non-ASCII characters with spaces */ +static inline char blank_non_printable(char ch) +{ + return (ch >= 0x20 && ch <= 0x7e) ? ch : ' '; +} + /** * ufshcd_read_string_desc - read string descriptor * @hba: pointer to adapter instance * @desc_index: descriptor index - * @buf: pointer to buffer where descriptor would be read - * @size: size of buf + * @buf: pointer to buffer where descriptor would be read, + * the caller should free the memory. * @ascii: if true convert from unicode to ascii characters + * null terminated string. * - * Return 0 in case of success, non-zero otherwise + * Return: string size on success. + * -ENOMEM: on allocation failure + * -EINVAL: on a wrong parameter */ -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii) +int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, + char **buf, bool ascii) { - int err = 0; + struct uc_string_id *uc_str; + char *str; + int ret; - err = ufshcd_read_desc(hba, - QUERY_DESC_IDN_STRING, desc_index, buf, size); + if (!buf) + return -EINVAL; - if (err) { - dev_err(hba->dev, "%s: reading String Desc failed after %d retries. err = %d\n", - __func__, QUERY_REQ_RETRIES, err); + uc_str = kzalloc(QUERY_DESC_MAX_SIZE, GFP_KERNEL); + if (!uc_str) + return -ENOMEM; + + ret = ufshcd_read_desc(hba, QUERY_DESC_IDN_STRING, + desc_index, uc_str, + QUERY_DESC_MAX_SIZE); + if (ret < 0) { + dev_err(hba->dev, "Reading String Desc failed after %d retries. err = %d\n", + QUERY_REQ_RETRIES, ret); + str = NULL; + goto out; + } + + if (uc_str->len <= QUERY_DESC_HDR_SIZE) { + dev_dbg(hba->dev, "String Desc is of zero length\n"); + str = NULL; + ret = 0; goto out; } if (ascii) { - int desc_len; - int ascii_len; + ssize_t ascii_len; int i; - char *buff_ascii; - - desc_len = buf[0]; /* remove header and divide by 2 to move from UTF16 to UTF8 */ - ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1; - if (size < ascii_len + QUERY_DESC_HDR_SIZE) { - dev_err(hba->dev, "%s: buffer allocated size is too small\n", - __func__); - err = -ENOMEM; - goto out; - } - - buff_ascii = kmalloc(ascii_len, GFP_KERNEL); - if (!buff_ascii) { - err = -ENOMEM; + ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1; + str = kzalloc(ascii_len, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; goto out; } @@ -3256,22 +3274,29 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, * the descriptor contains string in UTF16 format * we need to convert to utf-8 so it can be displayed */ - utf16s_to_utf8s((wchar_t *)&buf[QUERY_DESC_HDR_SIZE], - desc_len - QUERY_DESC_HDR_SIZE, - UTF16_BIG_ENDIAN, buff_ascii, ascii_len); + ret = utf16s_to_utf8s(uc_str->uc, + uc_str->len - QUERY_DESC_HDR_SIZE, + UTF16_BIG_ENDIAN, str, ascii_len); /* replace non-printable or non-ASCII characters with spaces */ - for (i = 0; i < ascii_len; i++) - ufshcd_remove_non_printable(&buff_ascii[i]); + for (i = 0; i < ret; i++) + str[i] = blank_non_printable(str[i]); - memset(buf + QUERY_DESC_HDR_SIZE, 0, - size - QUERY_DESC_HDR_SIZE); - memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len); - buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE; - kfree(buff_ascii); + str[ret++] = '\0'; + + } else { + str = kzalloc(uc_str->len, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto out; + } + memcpy(str, uc_str, uc_str->len); + ret = uc_str->len; } out: - return err; + *buf = str; + kfree(uc_str); + return ret; } /** @@ -6250,6 +6275,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba, u8 model_index; u8 *desc_buf; + if (!dev_desc) + return -EINVAL; + buff_len = max_t(size_t, hba->desc_size.dev_desc, QUERY_DESC_MAX_SIZE + 1); desc_buf = kmalloc(buff_len, GFP_KERNEL); @@ -6273,31 +6301,31 @@ static int ufs_get_device_desc(struct ufs_hba *hba, desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - - /* Zero-pad entire buffer for string termination. */ - memset(desc_buf, 0, buff_len); - - err = ufshcd_read_string_desc(hba, model_index, desc_buf, - QUERY_DESC_MAX_SIZE, true/*ASCII*/); - if (err) { + err = ufshcd_read_string_desc(hba, model_index, + &dev_desc->model, SD_ASCII_STD); + if (err < 0) { dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", __func__, err); goto out; } - desc_buf[QUERY_DESC_MAX_SIZE] = '\0'; - strlcpy(dev_desc->model, (desc_buf + QUERY_DESC_HDR_SIZE), - min_t(u8, desc_buf[QUERY_DESC_LENGTH_OFFSET], - MAX_MODEL_LEN)); - - /* Null terminate the model string */ - dev_desc->model[MAX_MODEL_LEN] = '\0'; + /* + * ufshcd_read_string_desc returns size of the string + * reset the error value + */ + err = 0; out: kfree(desc_buf); return err; } +static void ufs_put_device_desc(struct ufs_dev_desc *dev_desc) +{ + kfree(dev_desc->model); + dev_desc->model = NULL; +} + static void ufs_fixup_device_setup(struct ufs_hba *hba, struct ufs_dev_desc *dev_desc) { @@ -6306,8 +6334,9 @@ static void ufs_fixup_device_setup(struct ufs_hba *hba, for (f = ufs_fixups; f->quirk; f++) { if ((f->card.wmanufacturerid == dev_desc->wmanufacturerid || f->card.wmanufacturerid == UFS_ANY_VENDOR) && - (STR_PRFX_EQUAL(f->card.model, dev_desc->model) || - !strcmp(f->card.model, UFS_ANY_MODEL))) + ((dev_desc->model && + STR_PRFX_EQUAL(f->card.model, dev_desc->model)) || + !strcmp(f->card.model, UFS_ANY_MODEL))) hba->dev_quirks |= f->quirk; } } @@ -6590,6 +6619,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } ufs_fixup_device_setup(hba, &card); + ufs_put_device_desc(&card); + ufshcd_tune_unipro_params(hba); ret = ufshcd_set_vccq_rail_unused(hba, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 33fdd3f281ae..c04bdbb1ea57 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -875,14 +875,17 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, u8 param_offset, - u8 *param_read_buf, + void *param_read_buf, u8 param_size); int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii); + +#define SD_ASCII_STD true +#define SD_RAW false +int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, + char **buf, bool ascii); int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); From 2c8c765287df8e4c3d132c864a0afce64e11e587 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 5 Nov 2015 10:22:44 +0200 Subject: [PATCH 1007/1276] scsi: ufs: connect to RPMB subsystem Register UFS RPMB LUN with the RPMB subsystem and provide implementation for the RPMB access operations. RPMB partition is accessed via a sequence of security protocol in and security protocol out commands with UFS specific parameters. This multi step process is abstracted into 4 basic RPMB commands. V2: resend V3: resend V4: Kconfig: use select RPMB to ensure valid configuration V5: Revamp code using new sequence command. V6: Resend V7: Resend V8: 1. Replace scsi_execute_req_flags() with scsi_execute_req() 2. line over 80 characters fixes 3. scsi_device_get return value has to be checked V9: V1. adjust to new unregister api V2: nframes is 0 based now Change-Id: Ia45c6776d534fb311b6016aaa88f441d403cb0ca Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Tested-by: Avri Altman --- drivers/scsi/ufs/Kconfig | 1 + drivers/scsi/ufs/ufshcd.c | 223 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 2 + 3 files changed, 226 insertions(+) diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index e09fe6ab3572..a3c1982b213a 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -38,6 +38,7 @@ config SCSI_UFSHCD select PM_DEVFREQ select DEVFREQ_GOV_SIMPLE_ONDEMAND select NLS + select RPMB ---help--- This selects the support for UFS devices in Linux, say Y and make sure that you know the name of your UFS host adapter (the card diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 0ee9a4baaeb8..3d8b47055b6c 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -37,11 +37,14 @@ * license terms, and distributes only under these terms. */ +#include #include #include #include #include #include +#include + #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" @@ -6204,6 +6207,217 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) kfree(desc_buf); } +#define SEC_PROTOCOL_UFS 0xEC +#define SEC_SPECIFIC_UFS_RPMB 0x001 + +#define SEC_PROTOCOL_CMD_SIZE 12 +#define SEC_PROTOCOL_RETRIES 3 +#define SEC_PROTOCOL_RETRIES_ON_RESET 10 +#define SEC_PROTOCOL_TIMEOUT msecs_to_jiffies(1000) + +static int +ufshcd_rpmb_security_out(struct scsi_device *sdev, u8 region, + void *frames, u32 trans_len) +{ + struct scsi_sense_hdr sshdr; + int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET; + int ret; + u8 cmd[SEC_PROTOCOL_CMD_SIZE]; + + memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE); + cmd[0] = SECURITY_PROTOCOL_OUT; + cmd[1] = SEC_PROTOCOL_UFS; + cmd[2] = region; + cmd[3] = SEC_SPECIFIC_UFS_RPMB; + cmd[4] = 0; /* inc_512 bit 7 set to 0 */ + put_unaligned_be32(trans_len, cmd + 6); /* transfer length */ + +retry: + ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, + frames, trans_len, &sshdr, + SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES, + NULL); + + if (ret && scsi_sense_valid(&sshdr) && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* + * Device reset might occur several times, + * give it one more chance + */ + if (--reset_retries > 0) + goto retry; + + if (ret) + dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n", + __func__, ret); + + if (driver_byte(ret) & DRIVER_SENSE) + scsi_print_sense_hdr(sdev, "rpmb: security out", &sshdr); + + return ret; +} + +static int +ufshcd_rpmb_security_in(struct scsi_device *sdev, u8 region, + void *frames, u32 alloc_len) +{ + struct scsi_sense_hdr sshdr; + int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET; + int ret; + u8 cmd[SEC_PROTOCOL_CMD_SIZE]; + + memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE); + cmd[0] = SECURITY_PROTOCOL_IN; + cmd[1] = SEC_PROTOCOL_UFS; + cmd[2] = region; + cmd[3] = SEC_SPECIFIC_UFS_RPMB; + cmd[4] = 0; /* inc_512 bit 7 set to 0 */ + put_unaligned_be32(alloc_len, cmd + 6); /* allocation length */ + +retry: + ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, + frames, alloc_len, &sshdr, + SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES, + NULL); + + if (ret && scsi_sense_valid(&sshdr) && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* + * Device reset might occur several times, + * give it one more chance + */ + if (--reset_retries > 0) + goto retry; + + if (ret) + dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n", + __func__, ret); + + if (driver_byte(ret) & DRIVER_SENSE) + scsi_print_sense_hdr(sdev, "rpmb: security in", &sshdr); + + return ret; +} + +static int ufshcd_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + unsigned long flags; + struct ufs_hba *hba = dev_get_drvdata(dev); + struct scsi_device *sdev; + struct rpmb_cmd *cmd; + u32 len; + u32 i; + int ret; + + spin_lock_irqsave(hba->host->host_lock, flags); + sdev = hba->sdev_ufs_rpmb; + if (sdev) { + ret = scsi_device_get(sdev); + if (!ret && !scsi_device_online(sdev)) { + ret = -ENODEV; + scsi_device_put(sdev); + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (ret) + return ret; + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + len = rpmb_ioc_frames_len_jdec(cmd->nframes); + if (cmd->flags & RPMB_F_WRITE) + ret = ufshcd_rpmb_security_out(sdev, target, + cmd->frames, len); + else + ret = ufshcd_rpmb_security_in(sdev, target, + cmd->frames, len); + } + scsi_device_put(sdev); + return ret; +} + +static int ufshcd_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + __be64 block_count; + int ret; + + ret = ufshcd_read_unit_desc_param(hba, + UFS_UPIU_RPMB_WLUN, + UNIT_DESC_PARAM_LOGICAL_BLK_COUNT, + (u8 *)&block_count, + sizeof(block_count)); + if (ret) + return ret; + + return be64_to_cpu(block_count) * SZ_512 / SZ_128K; +} + +static struct rpmb_ops ufshcd_rpmb_dev_ops = { + .cmd_seq = ufshcd_rpmb_cmd_seq, + .get_capacity = ufshcd_rpmb_get_capacity, + .type = RPMB_TYPE_UFS, + .auth_method = RPMB_HMAC_ALGO_SHA_256, + +}; + +static inline void ufshcd_rpmb_add(struct ufs_hba *hba) +{ + struct rpmb_dev *rdev; + u8 rpmb_rw_size = 1; + int ret; + + ret = scsi_device_get(hba->sdev_ufs_rpmb); + if (ret) + goto out_put_dev; + + if (hba->ufs_version >= UFSHCI_VERSION_21) { + ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0, + GEOMETRY_DESC_PARAM_RPMB_RW_SIZE, + &rpmb_rw_size, + sizeof(rpmb_rw_size)); + if (ret) + goto out_put_dev; + } + + ufshcd_rpmb_dev_ops.rd_cnt_max = rpmb_rw_size; + ufshcd_rpmb_dev_ops.wr_cnt_max = rpmb_rw_size; + + rdev = rpmb_dev_register(hba->dev, 0, &ufshcd_rpmb_dev_ops); + if (IS_ERR(rdev)) { + dev_warn(hba->dev, "%s: cannot register to rpmb %ld\n", + dev_name(hba->dev), PTR_ERR(rdev)); + goto out_put_dev; + } + + return; + +out_put_dev: + scsi_device_put(hba->sdev_ufs_rpmb); + hba->sdev_ufs_rpmb = NULL; +} + +static inline void ufshcd_rpmb_remove(struct ufs_hba *hba) +{ + unsigned long flags; + + if (!hba->sdev_ufs_rpmb) + return; + + spin_lock_irqsave(hba->host->host_lock, flags); + + rpmb_dev_unregister_by_device(hba->dev, 0); + scsi_device_put(hba->sdev_ufs_rpmb); + hba->sdev_ufs_rpmb = NULL; + + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + /** * ufshcd_scsi_add_wlus - Adds required W-LUs * @hba: per-adapter instance @@ -6251,6 +6465,8 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) ret = PTR_ERR(sdev_rpmb); goto remove_sdev_ufs_device; } + hba->sdev_ufs_rpmb = sdev_rpmb; + scsi_device_put(sdev_rpmb); sdev_boot = __scsi_add_device(hba->host, 0, 0, @@ -6669,6 +6885,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ufshcd_scsi_add_wlus(hba)) goto out; + ufshcd_rpmb_add(hba); + /* Initialize devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_supported(hba)) { memcpy(&hba->clk_scaling.saved_pwr_info.info, @@ -7890,6 +8108,8 @@ int ufshcd_shutdown(struct ufs_hba *hba) goto out; } + ufshcd_rpmb_remove(hba); + ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); out: if (ret) @@ -7906,7 +8126,10 @@ EXPORT_SYMBOL(ufshcd_shutdown); */ void ufshcd_remove(struct ufs_hba *hba) { + ufshcd_rpmb_remove(hba); + ufs_sysfs_remove_nodes(hba->dev); + scsi_remove_host(hba->host); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c04bdbb1ea57..82b5e7d317f5 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -458,6 +458,7 @@ struct ufs_stats { * @utmrdl_dma_addr: UTMRDL DMA address * @host: Scsi_Host instance of the driver * @dev: device handle + * @sdev_ufs_rpmb: reference to RPMB device W-LU * @lrb: local reference block * @lrb_in_use: lrb in use * @outstanding_tasks: Bits representing outstanding task requests @@ -522,6 +523,7 @@ struct ufs_hba { * "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; + struct scsi_device *sdev_ufs_rpmb; enum ufs_dev_pwr_mode curr_dev_pwr_mode; enum uic_link_state uic_link_state; From 76b04633d28429c2800fc694d315bd2f3acfff30 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 14 May 2015 00:00:59 +0300 Subject: [PATCH 1008/1276] scsi: ufs: store device serial number. Retrieve device serial number and store it for RPMB subsystem use. V9: rebase v10: Fix Kdoc Change-Id: Ieee7f85696f6614cd2f3c81403124159ea85b77e Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- drivers/scsi/ufs/ufs.h | 4 ++++ drivers/scsi/ufs/ufshcd.c | 35 +++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index c9774b59b6bf..b60dfcb5f009 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -614,10 +614,14 @@ struct ufs_dev_info { * * @wmanufacturerid: card details * @model: card model + * @serial_no: serial number + * @serial_no_len: serial number string length */ struct ufs_dev_desc { u16 wmanufacturerid; char *model; + char *serial_no; + size_t serial_no_len; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3d8b47055b6c..e32741957288 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "ufshcd.h" @@ -6366,12 +6367,19 @@ static struct rpmb_ops ufshcd_rpmb_dev_ops = { }; -static inline void ufshcd_rpmb_add(struct ufs_hba *hba) +static inline void ufshcd_rpmb_add(struct ufs_hba *hba, + struct ufs_dev_desc *dev_desc) { struct rpmb_dev *rdev; u8 rpmb_rw_size = 1; int ret; + ufshcd_rpmb_dev_ops.dev_id = kmemdup(dev_desc->serial_no, + dev_desc->serial_no_len, + GFP_KERNEL); + if (ufshcd_rpmb_dev_ops.dev_id) + ufshcd_rpmb_dev_ops.dev_id_len = dev_desc->serial_no_len; + ret = scsi_device_get(hba->sdev_ufs_rpmb); if (ret) goto out_put_dev; @@ -6415,6 +6423,9 @@ static inline void ufshcd_rpmb_remove(struct ufs_hba *hba) scsi_device_put(hba->sdev_ufs_rpmb); hba->sdev_ufs_rpmb = NULL; + kfree(ufshcd_rpmb_dev_ops.dev_id); + ufshcd_rpmb_dev_ops.dev_id = NULL; + spin_unlock_irqrestore(hba->host->host_lock, flags); } @@ -6488,7 +6499,7 @@ static int ufs_get_device_desc(struct ufs_hba *hba, { int err; size_t buff_len; - u8 model_index; + u8 index; u8 *desc_buf; if (!dev_desc) @@ -6516,8 +6527,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba, dev_desc->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 | desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; - model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - err = ufshcd_read_string_desc(hba, model_index, + index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; + err = ufshcd_read_string_desc(hba, index, &dev_desc->model, SD_ASCII_STD); if (err < 0) { dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", @@ -6525,6 +6536,14 @@ static int ufs_get_device_desc(struct ufs_hba *hba, goto out; } + index = desc_buf[DEVICE_DESC_PARAM_SN]; + err = ufshcd_read_string_desc(hba, index, &dev_desc->serial_no, SD_RAW); + if (err < 0) { + dev_err(hba->dev, "%s: Failed reading Serial No. err = %d\n", + __func__, err); + goto out; + } + /* * ufshcd_read_string_desc returns size of the string * reset the error value @@ -6540,6 +6559,9 @@ static void ufs_put_device_desc(struct ufs_dev_desc *dev_desc) { kfree(dev_desc->model); dev_desc->model = NULL; + + kfree(dev_desc->serial_no); + dev_desc->serial_no = NULL; } static void ufs_fixup_device_setup(struct ufs_hba *hba, @@ -6835,7 +6857,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } ufs_fixup_device_setup(hba, &card); - ufs_put_device_desc(&card); ufshcd_tune_unipro_params(hba); @@ -6885,7 +6906,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ufshcd_scsi_add_wlus(hba)) goto out; - ufshcd_rpmb_add(hba); + ufshcd_rpmb_add(hba, &card); /* Initialize devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_supported(hba)) { @@ -6909,6 +6930,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) hba->is_init_prefetch = true; out: + + ufs_put_device_desc(&card); /* * If we failed to initialize the device or the device is not * present, turn off the power/clocks etc. From 32ae91528785c048190fc4bdfc75459634507e5f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 28 Apr 2018 22:01:32 +0300 Subject: [PATCH 1009/1276] rpmb: add nvme rpmb frame type The NVMe RPMB frame differs in layout and endianity from the one defined by JDEC. Change-Id: Ifae77454e1bc8733eb1e5bcb2146dc198f94151d Signed-off-by: Tomas Winkler --- drivers/char/rpmb/cdev.c | 26 ++++++++++++++++++------- include/uapi/linux/rpmb.h | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c index bdac3894b111..028c7ecd2ac7 100644 --- a/drivers/char/rpmb/cdev.c +++ b/drivers/char/rpmb/cdev.c @@ -67,18 +67,28 @@ static int rpmb_release(struct inode *inode, struct file *fp) return 0; } +static size_t rpmb_ioc_frames_len(struct rpmb_dev *rdev, size_t nframes) +{ + if (rdev->ops->type == RPMB_TYPE_NVME) + return rpmb_ioc_frames_len_nvme(nframes); + else + return rpmb_ioc_frames_len_jdec(nframes); +} + /** * rpmb_cmd_copy_from_user - copy rpmb command from the user space * + * @rdev: rpmb device * @cmd: internal cmd structure * @ucmd: user space cmd structure * * Return: 0 on success, <0 on error */ -static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, +static int rpmb_cmd_copy_from_user(struct rpmb_dev *rdev, + struct rpmb_cmd *cmd, struct rpmb_ioc_cmd __user *ucmd) { - struct rpmb_frame *frames; + void *frames; u64 frames_ptr; if (get_user(cmd->flags, &ucmd->flags)) @@ -95,7 +105,7 @@ static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, return -EFAULT; frames = memdup_user(u64_to_user_ptr(frames_ptr), - rpmb_ioc_frames_len_jdec(cmd->nframes)); + rpmb_ioc_frames_len(rdev, cmd->nframes)); if (IS_ERR(frames)) return PTR_ERR(frames); @@ -106,12 +116,14 @@ static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, /** * rpmb_cmd_copy_to_user - copy rpmb command to the user space * + * @rdev: rpmb device * @ucmd: user space cmd structure * @cmd: internal cmd structure * * Return: 0 on success, <0 on error */ -static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, +static int rpmb_cmd_copy_to_user(struct rpmb_dev *rdev, + struct rpmb_ioc_cmd __user *ucmd, struct rpmb_cmd *cmd) { u64 frames_ptr; @@ -121,7 +133,7 @@ static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, /* some archs have issues with 64bit get_user */ if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, - rpmb_ioc_frames_len_jdec(cmd->nframes))) + rpmb_ioc_frames_len(rdev, cmd->nframes))) return -EFAULT; return 0; @@ -167,7 +179,7 @@ static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; for (i = 0; i < ncmds; i++) { - ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]); + ret = rpmb_cmd_copy_from_user(rdev, &cmds[i], &ucmds[i]); if (ret) goto out; } @@ -177,7 +189,7 @@ static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, goto out; for (i = 0; i < ncmds; i++) { - ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]); + ret = rpmb_cmd_copy_to_user(rdev, &ucmds[i], &cmds[i]); if (ret) goto out; } diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h index d304701cd258..f41a28ea9765 100644 --- a/include/uapi/linux/rpmb.h +++ b/include/uapi/linux/rpmb.h @@ -68,6 +68,44 @@ struct rpmb_frame_jdec { (sizeof(struct rpmb_frame_jdec) - \ offsetof(struct rpmb_frame_jdec, data)) +/** + * struct rpmb_frame_nvme - rpmb frame as defined by specs + * + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @rpmb_target : RPMB target to access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of sctors (sectors, 512B) requested to be + * read/programmed. (In spec this field is named sector_count). + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + * @data : variable sized payload 512 * block_count + */ +struct rpmb_frame_nvme { + __u8 key_mac[223]; + __u8 rpmb_target; + __u8 nonce[16]; + __le32 write_counter; + __le32 addr; + __le32 block_count; + __le16 result; + __le16 req_resp; + __u8 data[0]; +} __attribute__((packed)); + +#define rpmb_nvme_hmac_data_len \ + (sizeof(struct rpmb_frame_nvme) - \ + offsetof(struct rpmb_frame_nvme, rpmb_target)) + #define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ #define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ #define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ @@ -142,6 +180,9 @@ struct rpmb_ioc_cmd { #define rpmb_ioc_frames_len_jdec(_n) \ (((_n) ?: 1) * sizeof(struct rpmb_frame_jdec)) +#define rpmb_ioc_frames_len_nvme(_n) \ + (sizeof(struct rpmb_frame_nvme) + (_n) * 512) + /** * struct rpmb_ioc_seq_cmd - rpmb command sequence * From 171fd7e60f4af468b6ff4d902db7a1d1f6d1fca0 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 30 Apr 2018 12:57:51 +0300 Subject: [PATCH 1010/1276] tools/rpmb: add support for nvme device Change-Id: Iab3171a22d3f502b11beed1496959bfd8f47d568 Signed-off-by: Tomas Winkler --- tools/rpmb/rpmb.c | 816 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 656 insertions(+), 160 deletions(-) diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c index 1a507db2739e..28b2280776d5 100644 --- a/tools/rpmb/rpmb.c +++ b/tools/rpmb/rpmb.c @@ -31,6 +31,11 @@ #define RPMB_MAC_SIZE 32 #define RPMB_NONCE_SIZE 16 +#define RPMB_FRAME_TYPE_JDEC 0 +#define RPMB_FRAME_TYPE_NVME 1 +#define RPMB_BLOCK_SIZE 256 +#define RPMB_SECTOR_SIZE 512 + bool verbose; #define rpmb_dbg(fmt, ARGS...) do { \ if (verbose) \ @@ -239,9 +244,10 @@ static ssize_t write_file(int fd, unsigned char *data, size_t size) return ret; } -static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) +static void dbg_dump_frame_jdec(const char *title, const void *f, uint32_t cnt) { uint16_t result, req_resp; + const struct rpmb_frame_jdec *frame = f; if (!verbose) return; @@ -249,37 +255,405 @@ static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) if (!f) return; - result = be16toh(f->result); - req_resp = be16toh(f->req_resp); + result = be16toh(frame->result); + req_resp = be16toh(frame->req_resp); if (req_resp & 0xf00) req_resp = RPMB_RESP2REQ(req_resp); fprintf(stderr, "--------------- %s ---------------\n", title ? title : "start"); fprintf(stderr, "ptr: %p\n", f); - dump_hex_buffer("key_mac: ", f->key_mac, 32); - dump_hex_buffer("data: ", f->data, 256); - dump_hex_buffer("nonce: ", f->nonce, 16); - fprintf(stderr, "write_counter: %u\n", be32toh(f->write_counter)); - fprintf(stderr, "address: %0X\n", be16toh(f->addr)); - fprintf(stderr, "block_count: %u\n", be16toh(f->block_count)); + dump_hex_buffer("key_mac: ", frame->key_mac, 32); + dump_hex_buffer("data: ", frame->data, 256); + dump_hex_buffer("nonce: ", frame->nonce, 16); + fprintf(stderr, "write_counter: %u\n", be32toh(frame->write_counter)); + fprintf(stderr, "address: %0X\n", be16toh(frame->addr)); + fprintf(stderr, "block_count: %u\n", be16toh(frame->block_count)); fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); fprintf(stderr, "--------------- End ---------------\n"); } -static struct rpmb_frame_jdec *rpmb_alloc_frames(unsigned int cnt) +static void dbg_dump_frame_nvme(const char *title, const void *f, uint32_t cnt) +{ + uint16_t result, req_resp; + uint32_t keysize = 4; + uint32_t sector_count; + const struct rpmb_frame_nvme *frame = f; + + if (!verbose) + return; + + if (!f) + return; + + result = le16toh(frame->result); + req_resp = le16toh(frame->req_resp); + if (req_resp & 0xf00) + req_resp = RPMB_RESP2REQ(req_resp); + + sector_count = le32toh(frame->block_count); + + fprintf(stderr, "--------------- %s ---------------\n", + title ? title : "start"); + fprintf(stderr, "ptr: %p\n", f); + dump_hex_buffer("key_mac: ", &frame->key_mac[223 - keysize], keysize); + dump_hex_buffer("nonce: ", frame->nonce, 16); + fprintf(stderr, "rpmb_target: %u\n", frame->rpmb_target); + fprintf(stderr, "write_counter: %u\n", le32toh(frame->write_counter)); + fprintf(stderr, "address: %0X\n", le32toh(frame->addr)); + fprintf(stderr, "block_count: %u\n", sector_count); + fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); + fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); + dump_hex_buffer("data: ", frame->data, RPMB_SECTOR_SIZE * cnt); + fprintf(stderr, "--------------- End --------------\n"); +} + +static void dbg_dump_frame(uint8_t frame_type, const char *title, + const void *f, uint32_t cnt) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + dbg_dump_frame_nvme(title, f, cnt); + else + dbg_dump_frame_jdec(title, f, cnt); +} + +static int rpmb_frame_set_key_mac_jdec(void *f, uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + struct rpmb_frame_jdec *frames = f; + + if (block_count == 0) + block_count = 1; + + memcpy(&frames[block_count - 1].key_mac, key_mac, key_mac_size); + + return 0; +} + +static int rpmb_frame_set_key_mac_nvme(void *f, uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + struct rpmb_frame_nvme *frame = f; + + memcpy(&frame->key_mac[223 - key_mac_size], key_mac, key_mac_size); + + return 0; +} + +static int rpmb_frame_set_key_mac(uint8_t frame_type, void *f, + uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_set_key_mac_nvme(f, block_count, + key_mac, key_mac_size); + else + return rpmb_frame_set_key_mac_jdec(f, block_count, + key_mac, key_mac_size); +} + +static uint8_t *rpmb_frame_get_key_mac_ptr_jdec(void *f, uint32_t block_count, + size_t key_size) { - return calloc(1, rpmb_ioc_frames_len_jdec(cnt)); + struct rpmb_frame_jdec *frame = f; + + if (block_count == 0) + block_count = 1; + + return frame[block_count - 1].key_mac; +} + +static uint8_t *rpmb_frame_get_key_mac_ptr_nvme(void *f, uint32_t block_count, + size_t key_size) +{ + struct rpmb_frame_nvme *frame = f; + + return &frame->key_mac[223 - key_size]; +} + +static uint8_t *rpmb_frame_get_key_mac_ptr(uint8_t frame_type, void *f, + uint32_t block_count, + size_t key_size) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_get_key_mac_ptr_nvme(f, block_count, + key_size); + else + return rpmb_frame_get_key_mac_ptr_jdec(f, block_count, + key_size); +} + +static uint8_t *rpmb_frame_get_nonce_ptr_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return frame->nonce; +} + +static uint8_t *rpmb_frame_get_nonce_ptr_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return frame->nonce; +} + +static uint8_t *rpmb_frame_get_nonce_ptr(uint8_t frame_type, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_get_nonce_ptr_nvme(f) : + rpmb_frame_get_nonce_ptr_jdec(f); +} + +static uint32_t rpmb_frame_get_write_counter_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be32toh(frame->write_counter); +} + +static uint32_t rpmb_frame_get_write_counter_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le32toh(frame->write_counter); +} + +static uint32_t rpmb_frame_get_write_counter(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_write_counter_nvme(f) : + rpmb_frame_get_write_counter_jdec(f); +} + +static uint32_t rpmb_frame_get_addr_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be16toh(frame->addr); +} + +static uint32_t rpmb_frame_get_addr_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le32toh(frame->addr); +} + +static uint32_t rpmb_frame_get_addr(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_addr_nvme(f) : + rpmb_frame_get_addr_jdec(f); +} + +static uint16_t rpmb_frame_get_result_jdec(void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t block_count = be16toh(frames[0].block_count); + + if (block_count == 0) + block_count = 1; + + return be16toh(frames[block_count - 1].result); +} + +static uint16_t rpmb_frame_get_result_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le16toh(frame->result); +} + +static uint16_t rpmb_frame_get_result(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_result_nvme(f) : + rpmb_frame_get_result_jdec(f); +} + +static uint16_t rpmb_frame_get_req_resp_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be16toh(frame->req_resp); +} + +static uint16_t rpmb_frame_get_req_resp_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le16toh(frame->req_resp); +} + +static uint16_t rpmb_frame_get_req_resp(uint8_t frame_type, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_get_req_resp_nvme(f) : + rpmb_frame_get_req_resp_jdec(f); +} + +static int rpmb_frame_set_jdec(void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + struct rpmb_frame_jdec *frames = f; + uint32_t i; + /* FIMXE: validate overflow */ + uint16_t __block_count = (uint16_t)block_count; + uint16_t __addr = (uint16_t)addr; + + for (i = 0; i < (block_count ?: 1); i++) { + frames[i].req_resp = htobe16(req_resp); + frames[i].block_count = htobe16(__block_count); + frames[i].addr = htobe16(__addr); + frames[i].write_counter = htobe32(write_counter); + } + + return 0; +} + +static int rpmb_frame_set_nvme(void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + struct rpmb_frame_nvme *frame = f; + + frame->req_resp = htole16(req_resp); + frame->block_count = htole32(block_count); + frame->addr = htole32(addr); + frame->write_counter = htole32(write_counter); + + return 0; +} + +static int rpmb_frame_set(uint8_t frame_type, void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) { + return rpmb_frame_set_nvme(f, req_resp, block_count, + addr, write_counter); + } else { + return rpmb_frame_set_jdec(f, req_resp, block_count, + addr, write_counter); + } +} + +static int rpmb_frame_write_data_jdec(int fd, void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t i, block_count = be16toh(frames[0].block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = write_file(fd, frames[i].data, sizeof(frames[i].data)); + if (ret < 0) + return ret; + } + return 0; +} + +static int rpmb_frame_write_data_nvme(int fd, void *f) +{ + struct rpmb_frame_nvme *frame = f; + uint32_t i, block_count = le32toh(frame->block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = write_file(fd, &frame->data[i], RPMB_SECTOR_SIZE); + if (ret < 0) + return ret; + } + return 0; +} + +static int rpmb_frame_write_data(uint8_t frame_type, int fd, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_write_data_nvme(fd, f) : + rpmb_frame_write_data_jdec(fd, f); +} + +static int rpmb_frame_read_data_jdec(int fd, void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t i, block_count = be16toh(frames[0].block_count); + + for (i = 0; i < block_count; i++) { + int ret = read_file(fd, frames[i].data, + sizeof(frames[0].data)); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rpmb_frame_read_data_nvme(int fd, void *f) +{ + struct rpmb_frame_nvme *frame = f; + uint32_t i, block_count = le32toh(frame->block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = read_file(fd, &frame->data[i], RPMB_SECTOR_SIZE); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rpmb_frame_read_data(uint8_t frame_type, int fd, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_read_data_nvme(fd, f) : + rpmb_frame_read_data_jdec(fd, f); } #if OPENSSL_VERSION_NUMBER < 0x10100000L -static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, - size_t blocks_cnt, - const unsigned char key[], - unsigned int key_size, - unsigned char mac[], - unsigned int mac_size) +static int rpmb_calc_hmac_sha256_jdec(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < block_count; i++) + HMAC_Update(&ctx, frames[i].data, rpmb_jdec_hmac_data_len); + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_cleanup(&ctx); + return ret == 1 ? 0 : -1; +} + +static int rpmb_calc_hmac_sha256_nvme(struct rpmb_frame_nvme *frame, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { HMAC_CTX ctx; int ret; @@ -291,8 +665,10 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); if (ret == 0) goto out; - for (i = 0; i < blocks_cnt; i++) - HMAC_Update(&ctx, frames[i].data, hmac_data_len); + + HMAC_Update(&ctx, &frame->rpmb_target, hmac_nvme_data_len); + for (i = 0; i < block_count; i++) + HMAC_Update(&ctx, frames->data[i], RPMB_SECTOR_SIZE); ret = HMAC_Final(&ctx, mac, &mac_size); if (ret == 0) @@ -306,12 +682,12 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, return ret == 1 ? 0 : -1; } #else -static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, - size_t blocks_cnt, - const unsigned char key[], - unsigned int key_size, - unsigned char mac[], - unsigned int mac_size) +static int rpmb_calc_hmac_sha256_jdec(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { HMAC_CTX *ctx; int ret; @@ -338,37 +714,83 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, HMAC_CTX_free(ctx); return ret == 1 ? 0 : -1; } + +static int rpmb_calc_hmac_sha256_nvme(struct rpmb_frame_nvme *frame, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX *ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + ctx = HMAC_CTX_new(); + + ret = HMAC_Init_ex(ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + + HMAC_Update(ctx, &frame->rpmb_target, rpmb_nvme_hmac_data_len); + for (i = 0; i < block_count; i++) + HMAC_Update(ctx, &frame->data[i], RPMB_SECTOR_SIZE); + + ret = HMAC_Final(ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_free(ctx); + return ret == 1 ? 0 : -1; +} #endif -static int rpmb_check_req_resp(uint16_t req, struct rpmb_frame_jdec *frame_out) +static int rpmb_calc_hmac_sha256(uint8_t frame_type, void *f, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { - if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { - rpmb_err("RPMB response mismatch %04X != %04X\n.", - RPMB_REQ2RESP(req), be16toh(frame_out->req_resp)); - return -1; - } - return 0; + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_calc_hmac_sha256_nvme(f, block_count, + key, key_size, + mac, mac_size); + else + return rpmb_calc_hmac_sha256_jdec(f, block_count, + key, key_size, + mac, mac_size); } -static int rpmb_check_mac(const unsigned char *key, - struct rpmb_frame_jdec *frames_out, - unsigned int cnt_out) +static int rpmb_check_mac(uint8_t frame_type, + const unsigned char *key, size_t key_size, + void *frames_out, unsigned int block_count) { unsigned char mac[RPMB_MAC_SIZE]; + unsigned char *mac_out; + int ret; - if (cnt_out == 0) { + if (block_count == 0) { rpmb_err("RPMB 0 output frames.\n"); return -1; } - rpmb_calc_hmac_sha256(frames_out, cnt_out, - key, RPMB_KEY_SIZE, - mac, RPMB_MAC_SIZE); + ret = rpmb_calc_hmac_sha256(frame_type, frames_out, block_count, + key, key_size, mac, RPMB_MAC_SIZE); + if (ret) + return ret; - if (memcmp(mac, frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE)) { + mac_out = rpmb_frame_get_key_mac_ptr(frame_type, frames_out, + block_count, RPMB_MAC_SIZE); + if (memcmp(mac, mac_out, RPMB_MAC_SIZE)) { rpmb_err("RPMB hmac mismatch:\n"); - dump_hex_buffer("Result MAC: ", - frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE); + dump_hex_buffer("Result MAC: ", mac_out, RPMB_MAC_SIZE); dump_hex_buffer("Expected MAC: ", mac, RPMB_MAC_SIZE); return -1; } @@ -376,23 +798,54 @@ static int rpmb_check_mac(const unsigned char *key, return 0; } -static int rpmb_ioctl(int fd, uint16_t req, - const struct rpmb_frame_jdec *frames_in, - unsigned int cnt_in, - struct rpmb_frame_jdec *frames_out, - unsigned int cnt_out) +static int rpmb_check_req_resp(uint8_t frame_type, + uint16_t req, void *frame_out) +{ + uint16_t req_resp = rpmb_frame_get_req_resp(frame_type, frame_out); + + if (RPMB_REQ2RESP(req) != req_resp) { + rpmb_err("RPMB response mismatch %04X != %04X\n.", + RPMB_REQ2RESP(req), req_resp); + return -1; + } + + return 0; +} + +static struct rpmb_frame_jdec *rpmb_frame_alloc_jdec(size_t block_count) +{ + return calloc(1, rpmb_ioc_frames_len_jdec(block_count)); +} + +static struct rpmb_frame_nvme *rpmb_frame_alloc_nvme(size_t sector_count) +{ + return calloc(1, rpmb_ioc_frames_len_nvme(sector_count)); +} + +static void *rpmb_frame_alloc(uint8_t type, size_t count) +{ + if (type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_alloc_nvme(count); + else + return rpmb_frame_alloc_jdec(count); +} + +static int rpmb_ioctl(uint8_t frame_type, int fd, uint16_t req, + const void *frames_in, unsigned int cnt_in, + void *frames_out, unsigned int cnt_out) { int ret; struct __attribute__((packed)) { struct rpmb_ioc_seq_cmd h; struct rpmb_ioc_cmd cmd[3]; } iseq = {}; - struct rpmb_frame_jdec *frame_res = NULL; + + void *frame_res = NULL; int i; uint32_t flags; rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req)); - dbg_dump_frame("In Frame: ", frames_in); + dbg_dump_frame(frame_type, "In Frame: ", frames_in, cnt_in); i = 0; flags = RPMB_F_WRITE; @@ -402,10 +855,11 @@ static int rpmb_ioctl(int fd, uint16_t req, i++; if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { - frame_res = rpmb_alloc_frames(0); + frame_res = rpmb_frame_alloc(frame_type, 0); if (!frame_res) return -ENOMEM; - frame_res->req_resp = htobe16(RPMB_RESULT_READ); + rpmb_frame_set(frame_type, frame_res, + RPMB_RESULT_READ, 0, 0, 0); rpmb_ioc_cmd_set(iseq.cmd[i], RPMB_F_WRITE, frame_res, 0); i++; } @@ -418,10 +872,10 @@ static int rpmb_ioctl(int fd, uint16_t req, if (ret < 0) rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); - ret = rpmb_check_req_resp(req, frames_out); + ret = rpmb_check_req_resp(frame_type, req, frames_out); - dbg_dump_frame("Res Frame: ", frame_res); - dbg_dump_frame("Out Frame: ", frames_out); + dbg_dump_frame(frame_type, "Res Frame: ", frame_res, 1); + dbg_dump_frame(frame_type, "Out Frame: ", frames_out, cnt_out); free(frame_res); return ret; } @@ -453,13 +907,61 @@ static int op_get_info(int nargs, char *argv[]) return 0; } +static int __rpmb_program_key(uint8_t frame_type, int dev_fd, + uint8_t *key, size_t key_size) +{ + void *frame_in, *frame_out; + uint16_t req = RPMB_PROGRAM_KEY; + int ret; + + frame_in = rpmb_frame_alloc(frame_type, 0); + frame_out = rpmb_frame_alloc(frame_type, 0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + rpmb_frame_set(frame_type, frame_in, req, 0, 0, 0); + + ret = rpmb_frame_set_key_mac(frame_type, frame_in, 0, key, key_size); + if (ret) + goto out; + + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 1, frame_out, 1); + if (ret) + goto out; + + ret = rpmb_check_req_resp(frame_type, req, frame_out); + if (ret) + goto out; + + ret = rpmb_frame_get_result(frame_type, frame_out); + if (ret) + rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + +out: + free(frame_in); + free(frame_out); + + return 0; +} + +static uint8_t rpmb_cap_get_frame_type(struct rpmb_ioc_cap_cmd *cap) +{ + if (cap->device_type == RPMB_TYPE_NVME) + return RPMB_FRAME_TYPE_NVME; + else + return RPMB_FRAME_TYPE_JDEC; +} + static int op_rpmb_program_key(int nargs, char *argv[]) { int ret; int dev_fd = -1, key_fd = -1; - uint16_t req = RPMB_PROGRAM_KEY; + uint8_t key[RPMB_KEY_SIZE]; + uint8_t frame_type; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frame_in = NULL, *frame_out = NULL; ret = -EINVAL; if (nargs != 2) @@ -475,87 +977,73 @@ static int op_rpmb_program_key(int nargs, char *argv[]) goto out; argv++; - frame_in = rpmb_alloc_frames(0); - frame_out = rpmb_alloc_frames(0); - if (!frame_in || !frame_out) { - ret = -ENOMEM; - goto out; - } + read_file(key_fd, key, RPMB_KEY_SIZE); - frame_in->req_resp = htobe16(req); + frame_type = rpmb_cap_get_frame_type(&cap); - read_file(key_fd, frame_in->key_mac, RPMB_KEY_SIZE); - - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); - if (ret) - goto out; - - if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { - rpmb_err("RPMB response mismatch.\n"); - ret = -1; - goto out; - } - - ret = be16toh(frame_out->result); - if (ret) - rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", - rpmb_op_str(req), rpmb_result_str(ret), ret); + ret = __rpmb_program_key(frame_type, dev_fd, key, RPMB_KEY_SIZE); out: - free(frame_in); - free(frame_out); close_fd(dev_fd); close_fd(key_fd); return ret; } -static int rpmb_get_write_counter(int dev_fd, unsigned int *cnt, - const unsigned char *key) +static int rpmb_get_write_counter(uint8_t frame_type, int dev_fd, + unsigned int *cnt, const unsigned char *key) { int ret; uint16_t res = 0x000F; uint16_t req = RPMB_GET_WRITE_COUNTER; - struct rpmb_frame_jdec *frame_in = NULL; - struct rpmb_frame_jdec *frame_out = NULL; + void *frame_in = NULL; + void *frame_out = NULL; + uint8_t *nonce_in; + uint8_t *nonce_out; - frame_in = rpmb_alloc_frames(0); - frame_out = rpmb_alloc_frames(0); + frame_in = rpmb_frame_alloc(frame_type, 0); + frame_out = rpmb_frame_alloc(frame_type, 0); if (!frame_in || !frame_out) { ret = -ENOMEM; goto out; } - frame_in->req_resp = htobe16(req); - RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + rpmb_frame_set(frame_type, frame_in, req, 0, 0, 0); + nonce_in = rpmb_frame_get_nonce_ptr(frame_type, frame_in); + RAND_bytes(nonce_in, RPMB_NONCE_SIZE); - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 0, frame_out, 0); if (ret) goto out; - res = be16toh(frame_out->result); + ret = rpmb_check_req_resp(frame_type, req, frame_out); + if (ret) + goto out; + + res = rpmb_frame_get_result(frame_type, frame_out); if (res != RPMB_ERR_OK) { ret = -1; goto out; } - if (memcmp(&frame_in->nonce, &frame_out->nonce, RPMB_NONCE_SIZE)) { + nonce_out = rpmb_frame_get_nonce_ptr(frame_type, frame_out); + + if (memcmp(nonce_in, nonce_out, RPMB_NONCE_SIZE)) { rpmb_err("RPMB NONCE mismatch\n"); - dump_hex_buffer("Result NONCE:", - &frame_out->nonce, RPMB_NONCE_SIZE); - dump_hex_buffer("Expected NONCE: ", - &frame_in->nonce, RPMB_NONCE_SIZE); + dump_hex_buffer("Result NONCE:", nonce_out, RPMB_NONCE_SIZE); + dump_hex_buffer("Expected NONCE: ", nonce_in, RPMB_NONCE_SIZE); ret = -1; goto out; } if (key) { - ret = rpmb_check_mac(key, frame_out, 1); + ret = rpmb_check_mac(frame_type, key, RPMB_KEY_SIZE, + frame_out, 1); if (ret) goto out; } - *cnt = be32toh(frame_out->write_counter); + *cnt = rpmb_frame_get_write_counter(frame_type, frame_out); out: if (ret) @@ -573,7 +1061,8 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) bool has_key; struct rpmb_ioc_cap_cmd cap; unsigned char key[RPMB_KEY_SIZE]; - unsigned int cnt; + unsigned int cnt = 0; + uint8_t frame_type; if (nargs == 2) has_key = true; @@ -588,6 +1077,8 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) return ret; argv++; + frame_type = rpmb_cap_get_frame_type(&cap); + if (has_key) { key_fd = open_rd_file(argv[0], "key file"); if (key_fd < 0) @@ -598,9 +1089,9 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) if (ret < 0) goto out; - ret = rpmb_get_write_counter(dev_fd, &cnt, key); + ret = rpmb_get_write_counter(frame_type, dev_fd, &cnt, key); } else { - ret = rpmb_get_write_counter(dev_fd, &cnt, NULL); + ret = rpmb_get_write_counter(frame_type, dev_fd, &cnt, NULL); } if (!ret) @@ -614,17 +1105,18 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) static int op_rpmb_read_blocks(int nargs, char **argv) { - int i, ret; + int ret; int dev_fd = -1, data_fd = -1, key_fd = -1; uint16_t req = RPMB_READ_DATA; - uint16_t addr, blocks_cnt; + uint32_t addr, block_count; unsigned char key[RPMB_KEY_SIZE]; + uint8_t *nonce_in; unsigned long numarg; bool has_key; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frame_in = NULL; - struct rpmb_frame_jdec *frames_out = NULL; - struct rpmb_frame_jdec *frame_out; + void *frame_in = NULL; + void *frames_out = NULL; + uint8_t frame_type; ret = -EINVAL; if (nargs == 4) @@ -641,23 +1133,23 @@ static int op_rpmb_read_blocks(int nargs, char **argv) errno = 0; numarg = strtoul(argv[0], NULL, 0); - if (errno || numarg > USHRT_MAX) { + if (errno || numarg > UINT_MAX) { rpmb_err("wrong block address\n"); goto out; } - addr = (uint16_t)numarg; + addr = (uint32_t)numarg; argv++; errno = 0; numarg = strtoul(argv[0], NULL, 0); - if (errno || numarg > USHRT_MAX) { + if (errno || numarg > UINT_MAX) { rpmb_err("wrong blocks count\n"); goto out; } - blocks_cnt = (uint16_t)numarg; + block_count = (uint32_t)numarg; argv++; - if (blocks_cnt == 0) { + if (block_count == 0) { rpmb_err("wrong blocks count\n"); goto out; } @@ -678,27 +1170,30 @@ static int op_rpmb_read_blocks(int nargs, char **argv) goto out; } + frame_type = rpmb_cap_get_frame_type(&cap); + ret = 0; - frames_out = rpmb_alloc_frames(blocks_cnt); - frame_in = rpmb_alloc_frames(0); + frames_out = rpmb_frame_alloc(frame_type, block_count); + frame_in = rpmb_frame_alloc(frame_type, 0); if (!frames_out || !frame_in) { - rpmb_err("Cannot allocate %d RPMB frames\n", blocks_cnt); + rpmb_err("Cannot allocate %d RPMB frames\n", block_count); ret = -ENOMEM; goto out; } - frame_in->req_resp = htobe16(req); - frame_in->addr = htobe16(addr); - /* eMMc spec ask for 0 here this will be translated by the rpmb layer */ - frame_in->block_count = htobe16(blocks_cnt); - RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + /* eMMc spec ask for 0 block_count here + * this will be translated by the rpmb layer + */ + rpmb_frame_set(frame_type, frame_in, req, block_count, addr, 0); + nonce_in = rpmb_frame_get_nonce_ptr(frame_type, frame_in); + RAND_bytes(nonce_in, RPMB_NONCE_SIZE); - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frames_out, blocks_cnt); + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 0, + frames_out, block_count); if (ret) goto out; - frame_out = &frames_out[blocks_cnt - 1]; - ret = be16toh(frame_out->result); + ret = rpmb_frame_get_result(frame_type, frames_out); if (ret) { rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", rpmb_op_str(req), rpmb_result_str(ret), ret); @@ -706,17 +1201,13 @@ static int op_rpmb_read_blocks(int nargs, char **argv) } if (has_key) { - ret = rpmb_check_mac(key, frames_out, blocks_cnt); + ret = rpmb_check_mac(frame_type, key, RPMB_KEY_SIZE, + frames_out, block_count); if (ret) goto out; } - for (i = 0; i < blocks_cnt; i++) { - ret = write_file(data_fd, frames_out[i].data, - sizeof(frames_out[i].data)); - if (ret < 0) - goto out; - } + ret = rpmb_frame_write_data(frame_type, data_fd, frames_out); out: free(frame_in); @@ -732,16 +1223,17 @@ static int op_rpmb_write_blocks(int nargs, char **argv) { int ret; int dev_fd = -1, key_fd = -1, data_fd = -1; - int i; uint16_t req = RPMB_WRITE_DATA; unsigned char key[RPMB_KEY_SIZE]; unsigned char mac[RPMB_MAC_SIZE]; unsigned long numarg; - uint16_t addr, blocks_cnt; - uint32_t write_counter; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frames_in = NULL; - struct rpmb_frame_jdec *frame_out = NULL; + uint16_t addr, block_count; + uint32_t write_counter = 0; + uint32_t write_counter_out = 0; + void *frames_in = NULL; + void *frame_out = NULL; + uint8_t frame_type; ret = -EINVAL; if (nargs != 5) @@ -767,10 +1259,10 @@ static int op_rpmb_write_blocks(int nargs, char **argv) rpmb_err("wrong blocks count\n"); goto out; } - blocks_cnt = (uint16_t)numarg; + block_count = (uint16_t)numarg; argv++; - if (blocks_cnt == 0) { + if (block_count == 0) { rpmb_err("wrong blocks count\n"); goto out; } @@ -789,60 +1281,64 @@ static int op_rpmb_write_blocks(int nargs, char **argv) if (ret < 0) goto out; - frames_in = rpmb_alloc_frames(blocks_cnt); - frame_out = rpmb_alloc_frames(0); + frame_type = rpmb_cap_get_frame_type(&cap); + + frames_in = rpmb_frame_alloc(frame_type, block_count); + frame_out = rpmb_frame_alloc(frame_type, 0); if (!frames_in || !frame_out) { rpmb_err("can't allocate memory for RPMB outer frames\n"); ret = -ENOMEM; goto out; } - ret = rpmb_get_write_counter(dev_fd, &write_counter, key); + ret = rpmb_get_write_counter(frame_type, dev_fd, &write_counter, NULL); if (ret) goto out; - for (i = 0; i < blocks_cnt; i++) { - frames_in[i].req_resp = htobe16(req); - frames_in[i].block_count = htobe16(blocks_cnt); - frames_in[i].addr = htobe16(addr); - frames_in[i].write_counter = htobe32(write_counter); - } + ret = rpmb_frame_set(frame_type, frames_in, + req, block_count, addr, write_counter); + if (ret) + goto out; - for (i = 0; i < blocks_cnt; i++) { - ret = read_file(data_fd, frames_in[i].data, - sizeof(frames_in[0].data)); - if (ret < 0) - goto out; - } + ret = rpmb_frame_read_data(frame_type, data_fd, frames_in); + if (ret) + goto out; - rpmb_calc_hmac_sha256(frames_in, blocks_cnt, + rpmb_calc_hmac_sha256(frame_type, frames_in, + block_count, key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); - memcpy(frames_in[blocks_cnt - 1].key_mac, mac, RPMB_MAC_SIZE); - ret = rpmb_ioctl(dev_fd, req, frames_in, blocks_cnt, frame_out, 0); + + rpmb_frame_set_key_mac(frame_type, frames_in, block_count, + mac, RPMB_MAC_SIZE); + + ret = rpmb_ioctl(frame_type, dev_fd, req, + frames_in, block_count, + frame_out, 0); if (ret != 0) goto out; - ret = be16toh(frame_out->result); + ret = rpmb_frame_get_result(frame_type, frame_out); if (ret) { rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", rpmb_op_str(req), rpmb_result_str(ret), ret); ret = -1; } - if (be16toh(frame_out->addr) != addr) { + if (rpmb_frame_get_addr(frame_type, frame_out) != addr) { rpmb_err("RPMB addr mismatchs res=%04x req=%04x\n", - be16toh(frame_out->addr), addr); + rpmb_frame_get_addr(frame_type, frame_out), addr); ret = -1; } - if (be32toh(frame_out->write_counter) <= write_counter) { + write_counter_out = rpmb_frame_get_write_counter(frame_type, frame_out); + if (write_counter_out <= write_counter) { rpmb_err("RPMB write counter not incremented res=%x req=%x\n", - be32toh(frame_out->write_counter), write_counter); + write_counter_out, write_counter); ret = -1; } - ret = rpmb_check_mac(key, frame_out, 1); + /* TODO: check mac: spec is not clear what is computed by the device */ out: free(frames_in); free(frame_out); From 96c570cf9b93bb4b15eeab4060b016f41c2ad285 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 25 Apr 2018 19:07:06 +0300 Subject: [PATCH 1011/1276] nvme: connect to rpmb layer This patch covers rpmb storage operation as defined in NVMe spec 1.3a in section 8.10. It only covers standard RPMB storage API, the device configuration is not covered. Change-Id: I35c9cc7aeec5a08041b9986d60fc9ee55c66dda7 Signed-off-by: Tomas Winkler --- drivers/nvme/host/Kconfig | 1 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/core.c | 54 +++++++++++++---- drivers/nvme/host/nvme.h | 9 +++ drivers/nvme/host/pci.c | 4 ++ drivers/nvme/host/rpmb.c | 119 +++++++++++++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 drivers/nvme/host/rpmb.c diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index 88a8b5916624..a0027cebf2db 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -5,6 +5,7 @@ config BLK_DEV_NVME tristate "NVM Express block device" depends on PCI && BLOCK select NVME_CORE + select RPMB ---help--- The NVM Express driver is for solid state drives directly connected to the PCI or PCI Express bus. If you know you diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index aea459c65ae1..99f99e87b82b 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -15,6 +15,7 @@ nvme-core-$(CONFIG_NVM) += lightnvm.o nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o nvme-y += pci.o +nvme-y += rpmb.o nvme-fabrics-y += fabrics.o diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 6bb9908bf46f..9d84b7ecb67e 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -11,7 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ - #include #include #include @@ -1659,25 +1658,57 @@ static const struct pr_ops nvme_pr_ops = { .pr_clear = nvme_pr_clear, }; -#ifdef CONFIG_BLK_SED_OPAL -int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, - bool send) +int nvme_sec_send(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len) { - struct nvme_ctrl *ctrl = data; struct nvme_command cmd; + dev_dbg(ctrl->device, "%s target = %hhu SPSP = %hu SECP = %hhX len=%zd\n", + __func__, nssf, spsp, secp, len); + memset(&cmd, 0, sizeof(cmd)); - if (send) - cmd.common.opcode = nvme_admin_security_send; - else - cmd.common.opcode = nvme_admin_security_recv; + cmd.common.opcode = nvme_admin_security_send; + cmd.common.nsid = 0; + cmd.common.cdw10[0] = + cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8 | nssf); + cmd.common.cdw10[1] = cpu_to_le32(len); + + return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, + ADMIN_TIMEOUT, NVME_QID_ANY, 1, 0); +} +EXPORT_SYMBOL_GPL(nvme_sec_send); + +int nvme_sec_recv(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len) +{ + struct nvme_command cmd; + + dev_dbg(ctrl->device, "%s target = %hhu SPSP = %hu SECP = %hhX len=%zd\n", + __func__, nssf, spsp, secp, len); + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.opcode = nvme_admin_security_recv; cmd.common.nsid = 0; - cmd.common.cdw10[0] = cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8); + cmd.common.cdw10[0] = + cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8 | nssf); cmd.common.cdw10[1] = cpu_to_le32(len); return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, ADMIN_TIMEOUT, NVME_QID_ANY, 1, 0); } +EXPORT_SYMBOL_GPL(nvme_sec_recv); + +#ifdef CONFIG_BLK_SED_OPAL +int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, + bool send) +{ + struct nvme_ctrl *ctrl = data; + + if (send) + return nvme_sec_send(ctrl, 0, spsp, secp, buffer, len); + else + return nvme_sec_recv(ctrl, 0, spsp, secp, buffer, len); +} EXPORT_SYMBOL_GPL(nvme_sec_submit); #endif /* CONFIG_BLK_SED_OPAL */ @@ -2468,7 +2499,10 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->hmmaxd = le16_to_cpu(id->hmmaxd); } + ctrl->rpmbs = le32_to_cpu(id->rpmbs); + ret = nvme_mpath_init(ctrl, id); + kfree(id); if (ret < 0) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index bb4a2003c097..a596e9e84d1e 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,7 @@ struct nvme_ctrl { struct list_head subsys_entry; struct opal_dev *opal_dev; + struct rpmb_dev *rdev; char name[12]; u16 cntlid; @@ -193,6 +195,7 @@ struct nvme_ctrl { u8 apsta; u32 oaes; u32 aen_result; + u32 rpmbs; unsigned int shutdown_timeout; unsigned int kato; bool subsystem; @@ -420,6 +423,12 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl); void nvme_stop_ctrl(struct nvme_ctrl *ctrl); void nvme_put_ctrl(struct nvme_ctrl *ctrl); int nvme_init_identify(struct nvme_ctrl *ctrl); +int nvme_sec_send(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len); +int nvme_sec_recv(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len); +int nvme_init_rpmb(struct nvme_ctrl *ctrl); +void nvme_exit_rpmb(struct nvme_ctrl *ctrl); void nvme_remove_namespaces(struct nvme_ctrl *ctrl); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index d668682f91df..6c19fa7525e3 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2289,6 +2289,10 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out; + result = nvme_init_rpmb(&dev->ctrl); + if (result < 0) + goto out; + if (dev->ctrl.oacs & NVME_CTRL_OACS_SEC_SUPP) { if (!dev->ctrl.opal_dev) dev->ctrl.opal_dev = diff --git a/drivers/nvme/host/rpmb.c b/drivers/nvme/host/rpmb.c new file mode 100644 index 000000000000..34e807bfc4f9 --- /dev/null +++ b/drivers/nvme/host/rpmb.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2018 Intel Corporation. All rights reserved. + */ +#include +#include "nvme.h" +#define NVME_SECP_RPMB 0xEA /* Security Protocol EAh is assigned + * for NVMe use (refer to ACS-4) + */ +#define NVME_SPSP_RPMB 0x0001 /* RPMB Target */ +static int nvme_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct nvme_ctrl *ctrl; + struct rpmb_cmd *cmd; + u32 size; + int ret; + int i; + + ctrl = dev_get_drvdata(dev); + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + size = rpmb_ioc_frames_len_nvme(cmd->nframes); + if (cmd->flags & RPMB_F_WRITE) + ret = nvme_sec_send(ctrl, target, + NVME_SPSP_RPMB, NVME_SECP_RPMB, + cmd->frames, size); + else + ret = nvme_sec_recv(ctrl, target, + NVME_SPSP_RPMB, NVME_SECP_RPMB, + cmd->frames, size); + } + + return ret; +} + +static int nvme_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct nvme_ctrl *ctrl; + + ctrl = dev_get_drvdata(dev); + + return ((ctrl->rpmbs >> 16) & 0xFF) + 1; +} + +static struct rpmb_ops nvme_rpmb_dev_ops = { + .cmd_seq = nvme_rpmb_cmd_seq, + .get_capacity = nvme_rpmb_get_capacity, + .type = RPMB_TYPE_NVME, +}; + +static void nvme_rpmb_set_cap(struct nvme_ctrl *ctrl, + struct rpmb_ops *ops) +{ + ops->wr_cnt_max = ((ctrl->rpmbs >> 24) & 0xFF) + 1; + ops->rd_cnt_max = ops->wr_cnt_max; + ops->block_size = 2; /* 1 sector == 2 half sectors */ + ops->auth_method = (ctrl->rpmbs >> 3) & 0x3; +} + +static void nvme_rpmb_add(struct nvme_ctrl *ctrl) +{ + struct rpmb_dev *rdev; + int ndevs = ctrl->rpmbs & 0x7; + int i; + + nvme_rpmb_set_cap(ctrl, &nvme_rpmb_dev_ops); + + /* Add RPMB partitions */ + for (i = 0; i < ndevs; i++) { + rdev = rpmb_dev_register(ctrl->device, i, &nvme_rpmb_dev_ops); + if (IS_ERR(rdev)) { + dev_warn(ctrl->device, "%s: cannot register to rpmb %ld\n", + dev_name(ctrl->device), PTR_ERR(rdev)); + } + dev_set_drvdata(&rdev->dev, ctrl); + } +} + +static void nvme_rpmb_remove(struct nvme_ctrl *ctrl) +{ + int ndevs = ctrl->rpmbs & 0x7; + int i; + + /* FIXME: target */ + for (i = 0; i < ndevs; i++) + rpmb_dev_unregister_by_device(ctrl->device, i); +} + +int nvme_init_rpmb(struct nvme_ctrl *ctrl) +{ + dev_err(ctrl->device, "RPMBS %X\n", ctrl->rpmbs); + + if ((ctrl->rpmbs & 0x7) == 0x0) { + dev_err(ctrl->device, "RPMBS No partitions\n"); + return 0; + } + + dev_err(ctrl->device, "RPMBS Number of partitions %d\n", + ctrl->rpmbs & 0x7); + dev_err(ctrl->device, "RPMBS Authentication Method: %d\n", + (ctrl->rpmbs >> 3) & 0x3); + dev_err(ctrl->device, "RPMBS Total Size: %d %dK", + (ctrl->rpmbs >> 16) & 0xFF, + (((ctrl->rpmbs >> 16) & 0xFF) + 1) * 128); + dev_err(ctrl->device, "RPMBS Access Size: %d %dB", + (ctrl->rpmbs >> 24) & 0xFF, + (((ctrl->rpmbs >> 24) & 0xFF) + 1) * 512); + + nvme_rpmb_add(ctrl); + + return 0; +} + +void nvme_exit_rpmb(struct nvme_ctrl *ctrl) +{ + nvme_rpmb_remove(ctrl); +} From 35680bf7692249df992df40adb4ff1470ddd9089 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 24 May 2018 14:57:02 +0300 Subject: [PATCH 1012/1276] rpmb: VRPMB-FE create virtio rpmb frontend driver This patch implements virtio rpmb frontend driver. The driver will work with RPMB VBS-U together to provide one communication channel between UOS and SOS. V2: 1. Change license to dual BSD/GPL 2. Fix coding style. 3. Use pr_fmt macro instead of ERR, DBG, ... V3: 1. Replace - with _ in file name. 2. Plug to rpmb framework instead of using own misc device 3. Use arrays of scatter lists instead of linearizing the data. V4: 1. Allocate memory for control structures, it's not possible to DMA from the stack. V5: 1. Add mutex and use wait queue instead of completion. 2. WIP code for getting capabilities V6: 1. Fix calculation of the allocation size for seq cmd 2. WIP code for getting capabilities 3. Drop unused constant RPMB_MAX_FRAMES Change-Id: I88a42f2e8f2ea1573aad9b5cafeae812c669a73e Signed-off-by: Tomas Winkler --- drivers/char/rpmb/Kconfig | 10 ++ drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/virtio_rpmb.c | 305 ++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 drivers/char/rpmb/virtio_rpmb.c diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index c069664eec92..48f11c19bbda 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -30,3 +30,13 @@ config RPMB_SIM suitable only for testing of the RPMB subsystem or RPMB applications prior to RPMB key provisioning. Most people should say N here. + +config VIRTIO_RPMB + tristate "Virtio RPMB character device interface /dev/vrpmb" + default n + depends on VIRTIO + select RPMB + help + Say yes here if you want to access virtio RPMB from user space + via character device interface /dev/vrpmb. + This device interface is only for guest/frontend virtio driver. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index 8bd1186948b0..281c012712ca 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o +obj-$(CONFIG_VIRTIO_RPMB) += virtio_rpmb.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/virtio_rpmb.c b/drivers/char/rpmb/virtio_rpmb.c new file mode 100644 index 000000000000..ef3487c98805 --- /dev/null +++ b/drivers/char/rpmb/virtio_rpmb.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Virtio RPMB Front End Driver + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char id[] = "RPMB:VIRTIO"; +#ifndef VIRTIO_ID_RPMB +#define VIRTIO_ID_RPMB 0xFFFF +#endif + +#define RPMB_SEQ_CMD_MAX 3 /* support up to 3 cmds */ + +struct virtio_rpmb_info { + struct virtqueue *vq; + struct mutex lock; /* info lock */ + wait_queue_head_t have_data; + struct rpmb_dev *rdev; +}; + +struct virtio_rpmb_ioc { + unsigned int ioc_cmd; + int result; + u8 target; + u8 reserved[3]; +}; + +static void virtio_rpmb_recv_done(struct virtqueue *vq) +{ + struct virtio_rpmb_info *vi; + struct virtio_device *vdev = vq->vdev; + + vi = vq->vdev->priv; + if (!vi) { + dev_err(&vdev->dev, "Error: no found vi data.\n"); + return; + } + + wake_up(&vi->have_data); +} + +static int rpmb_virtio_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + unsigned int i; + struct virtio_rpmb_ioc *vio_cmd; + struct rpmb_ioc_seq_cmd *seq_cmd; + size_t seq_cmd_sz; + struct scatterlist vio_ioc, vio_seq, frame[3]; + struct scatterlist *sgs[5]; + unsigned int num_out = 0, num_in = 0; + size_t sz; + int ret; + unsigned int len; + + if (ncmds > RPMB_SEQ_CMD_MAX) + return -EINVAL; + + mutex_lock(&vi->lock); + + vio_cmd = kzalloc(sizeof(*vio_cmd), GFP_KERNEL); + seq_cmd_sz = sizeof(*seq_cmd) + sizeof(struct rpmb_ioc_cmd) * ncmds; + seq_cmd = kzalloc(seq_cmd_sz, GFP_KERNEL); + if (!vio_cmd || !seq_cmd) { + ret = -ENOMEM; + goto out; + } + + vio_cmd->ioc_cmd = RPMB_IOC_SEQ_CMD; + vio_cmd->result = 0; + vio_cmd->target = target; + sg_init_one(&vio_ioc, vio_cmd, sizeof(*vio_cmd)); + sgs[num_out + num_in++] = &vio_ioc; + + seq_cmd->num_of_cmds = ncmds; + for (i = 0; i < ncmds; i++) { + seq_cmd->cmds[i].flags = cmds[i].flags; + seq_cmd->cmds[i].nframes = cmds[i].nframes; + seq_cmd->cmds[i].frames_ptr = i; + } + sg_init_one(&vio_seq, seq_cmd, seq_cmd_sz); + sgs[num_out + num_in++] = &vio_seq; + + for (i = 0; i < ncmds; i++) { + sz = sizeof(struct rpmb_frame_jdec) * (cmds[i].nframes ?: 1); + sg_init_one(&frame[i], cmds[i].frames, sz); + sgs[num_out + num_in++] = &frame[i]; + } + + virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + ret = 0; + + if (vio_cmd->result != 0) { + dev_err(dev, "Error: command error = %d.\n", vio_cmd->result); + ret = -EIO; + } + +out: + kfree(vio_cmd); + kfree(seq_cmd); + mutex_unlock(&vi->lock); + return ret; +} + +static int rpmb_virtio_cmd_cap(struct device *dev, u8 target) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + struct virtio_rpmb_ioc *vio_cmd; + struct rpmb_ioc_cap_cmd *cap_cmd; + struct scatterlist vio_ioc, cap_ioc; + struct scatterlist *sgs[2]; + unsigned int num_out = 0, num_in = 0; + unsigned int len; + int ret; + + mutex_lock(&vi->lock); + + vio_cmd = kzalloc(sizeof(*vio_cmd), GFP_KERNEL); + cap_cmd = kzalloc(sizeof(*cap_cmd), GFP_KERNEL); + if (!vio_cmd || !cap_cmd) { + ret = -ENOMEM; + goto out; + } + + vio_cmd->ioc_cmd = RPMB_IOC_CAP_CMD; + vio_cmd->result = 0; + vio_cmd->target = target; + sg_init_one(&vio_ioc, vio_cmd, sizeof(*vio_cmd)); + sgs[num_out + num_in++] = &vio_ioc; + + sg_init_one(&cap_ioc, cap_cmd, sizeof(*cap_cmd)); + sgs[num_out + num_in++] = &cap_ioc; + + virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + ret = 0; + + if (vio_cmd->result != 0) { + dev_err(dev, "Error: command error = %d.\n", vio_cmd->result); + ret = -EIO; + } + +out: + kfree(vio_cmd); + kfree(cap_cmd); + + mutex_unlock(&vi->lock); + return ret; +} + +static int rpmb_virtio_get_capacity(struct device *dev, u8 target) +{ + return 0; +} + +static struct rpmb_ops rpmb_virtio_ops = { + .cmd_seq = rpmb_virtio_cmd_seq, + .get_capacity = rpmb_virtio_get_capacity, + .type = RPMB_TYPE_EMMC, +}; + +static int rpmb_virtio_dev_init(struct virtio_rpmb_info *vi) +{ + int ret = 0; + struct device *dev = &vi->vq->vdev->dev; + + rpmb_virtio_ops.dev_id_len = strlen(id); + rpmb_virtio_ops.dev_id = id; + rpmb_virtio_ops.wr_cnt_max = 1; + rpmb_virtio_ops.rd_cnt_max = 1; + rpmb_virtio_ops.block_size = 1; + + vi->rdev = rpmb_dev_register(dev, 0, &rpmb_virtio_ops); + if (IS_ERR(vi->rdev)) { + ret = PTR_ERR(vi->rdev); + goto err; + } + + dev_set_drvdata(dev, vi); +err: + return ret; +} + +static int virtio_rpmb_init(struct virtio_device *vdev) +{ + int ret; + struct virtio_rpmb_info *vi; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) + return -ENOMEM; + + init_waitqueue_head(&vi->have_data); + mutex_init(&vi->lock); + vdev->priv = vi; + + /* We expect a single virtqueue. */ + vi->vq = virtio_find_single_vq(vdev, virtio_rpmb_recv_done, "request"); + if (IS_ERR(vi->vq)) { + dev_err(&vdev->dev, "get single vq failed!\n"); + ret = PTR_ERR(vi->vq); + goto err; + } + + /* create vrpmb device. */ + ret = rpmb_virtio_dev_init(vi); + if (ret) { + dev_err(&vdev->dev, "create vrpmb device failed.\n"); + goto err; + } + + dev_info(&vdev->dev, "init done!\n"); + + return 0; + +err: + kfree(vi); + return ret; +} + +static void virtio_rpmb_remove(struct virtio_device *vdev) +{ + struct virtio_rpmb_info *vi; + + vi = vdev->priv; + if (!vi) + return; + + if (wq_has_sleeper(&vi->have_data)) + wake_up(&vi->have_data); + + rpmb_dev_unregister(vi->rdev); + + if (vdev->config->reset) + vdev->config->reset(vdev); + + if (vdev->config->del_vqs) + vdev->config->del_vqs(vdev); + + kfree(vi); +} + +static int virtio_rpmb_probe(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} + +#ifdef CONFIG_PM_SLEEP +static int virtio_rpmb_freeze(struct virtio_device *vdev) +{ + virtio_rpmb_remove(vdev); + return 0; +} + +static int virtio_rpmb_restore(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} +#endif + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMB, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_rpmb_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_rpmb_probe, + .remove = virtio_rpmb_remove, +#ifdef CONFIG_PM_SLEEP + .freeze = virtio_rpmb_freeze, + .restore = virtio_rpmb_restore, +#endif +}; + +module_virtio_driver(virtio_rpmb_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_DESCRIPTION("Virtio rpmb frontend driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); From 8e80bf878b1158ab380ab2761623a963b11def24 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 19 Jul 2016 00:08:05 +0300 Subject: [PATCH 1013/1276] char: rpmb: Document Replay Protected Memory Block (RPMB) subsystem Add rpmb documentatin in sphinx format. V7: new in the series V8: Rebase for v4.10 fix conf.py V9: 1. Rebase for v4.17 2. Add SPDX intentifiers. 3. Move under driver-api 4. Drop req_cmd() Change-Id: I4ec3481a8cf443ea6f5fb88a11b616d815163e8c Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/conf.py | 2 + Documentation/driver-api/index.rst | 1 + Documentation/driver-api/rpmb/conf.py | 5 + Documentation/driver-api/rpmb/index.rst | 18 ++++ .../driver-api/rpmb/introduction.rst | 98 +++++++++++++++++++ Documentation/driver-api/rpmb/rpmb-tool.rst | 19 ++++ .../driver-api/rpmb/simulation-device.rst | 21 ++++ MAINTAINERS | 1 + 8 files changed, 165 insertions(+) create mode 100644 Documentation/driver-api/rpmb/conf.py create mode 100644 Documentation/driver-api/rpmb/index.rst create mode 100644 Documentation/driver-api/rpmb/introduction.rst create mode 100644 Documentation/driver-api/rpmb/rpmb-tool.rst create mode 100644 Documentation/driver-api/rpmb/simulation-device.rst diff --git a/Documentation/conf.py b/Documentation/conf.py index b691af4831fa..a57272e7820c 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -403,6 +403,8 @@ 'The kernel development community', 'manual'), ('userspace-api/index', 'userspace-api.tex', 'The Linux kernel user-space API guide', 'The kernel development community', 'manual'), + ('rpmb/index', 'rpmb.tex', 'Linux RPMB Subsystem Documentation', + 'The kernel development community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6d9f2f9fe20e..d602f6c05972 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -53,6 +53,7 @@ available subsections can be seen below. slimbus soundwire/index fpga/index + rpmb/index .. only:: subproject and html diff --git a/Documentation/driver-api/rpmb/conf.py b/Documentation/driver-api/rpmb/conf.py new file mode 100644 index 000000000000..15430a0b3a08 --- /dev/null +++ b/Documentation/driver-api/rpmb/conf.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8; mode: python -*- + +project = "Linux RPMB Subsystem" + +tags.add("subproject") diff --git a/Documentation/driver-api/rpmb/index.rst b/Documentation/driver-api/rpmb/index.rst new file mode 100644 index 000000000000..3813a44ad06e --- /dev/null +++ b/Documentation/driver-api/rpmb/index.rst @@ -0,0 +1,18 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +============================================== +Replay Protected Memory Block (RPMB) subsystem +============================================== + +.. toctree:: + + introduction + simulation-device.rst + rpmb-tool.rst + +.. only:: subproject + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/driver-api/rpmb/introduction.rst b/Documentation/driver-api/rpmb/introduction.rst new file mode 100644 index 000000000000..403cbcf6e142 --- /dev/null +++ b/Documentation/driver-api/rpmb/introduction.rst @@ -0,0 +1,98 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +============= +Introduction: +============= + +Few storage technologies such is EMMC, UFS, and NVMe support RPMB +hardware partition with common protocol and frame layout. +The RPMB partition `cannot` be accessed via standard block layer, +but by a set of specific commands: + +WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. + +The commands and the data are embedded within :c:type:`rpmb_frame `. + +An RPMB partition provides authenticated and replay protected access, +hence it is suitable as a secure storage. + +In-kernel API +------------- +The RPMB layer aims to provide in-kernel API for Trusted Execution +Environment (TEE) devices that are capable to securely compute the block +frame signature. In case a TEE device wish to store a replay protected +data, it creates an RPMB frame with requested data and computes HMAC of +the frame, then it requests the storage device via RPMB layer to store +the data. + +The layer provides APIs, for :c:func:`rpmb_seq_cmd()` for issuing sequence +of raw RPMB protocol frames, which is close to the functionality provided +by emmc multi ioctl interface. + +.. c:function:: int rpmb_cmd_seq(struct rpmb_dev *rdev, u8 target, struct rpmb_cmd *cmds, u32 ncmds); + + +A TEE driver can claim the RPMB interface, for example, via +:c:func:`class_interface_register`: + +.. code-block:: c + + struct class_interface tee_rpmb_intf = { + .class = &rpmb_class; + .add_dev = rpmb_add_device; + .remove_dev = rpmb_remove_device; + } + class_interface_register(&tee_rpmb_intf); + + +RPMB device registeration +---------------------------- + +A storage device registers its RPMB hardware (eMMC) partition or RPMB +W-LUN (UFS) with the RPMB layer :c:func:`rpmb_dev_register` providing +an implementation for :c:func:`rpmb_seq_cmd()` handler. The interface +enables sending sequence of RPMB standard frames. + +.. code-block:: c + + struct rpmb_ops mmc_rpmb_dev_ops = { + .cmd_seq = mmc_blk_rpmb_cmd_seq, + .type = RPMB_TYPE_EMMC, + ... + } + rpmb_dev_register(disk_to_dev(part_md->disk), &mmc_rpmb_dev_ops); + + +User space API +-------------- + +A parallel user space API is provided via /dev/rpmbX character +device with two IOCTL commands. +- First ``RPMB_IOC_VER_CMD``, return driver protocol version, +- second ``RPMB_IOC_CAP_CMD`` return capability structure, +- last ``RPMB_IOC_SEQ_CMD`` where the whole RPMB sequence, and + including ``RESULT_READ`` is supplied by the caller. +https://android.googlesource.com/trusty/app/storage/ + +.. code-block:: c + + struct rpmb_ioc_req_cmd ireq; + int ret; + + ireq.req_type = RPMB_WRITE_DATA; + rpmb_ioc_cmd_set(ireq.icmd, RPMB_F_WRITE, frames_in, cnt_in); + rpmb_ioc_cmd_set(ireq.ocmd, 0, frames_out, cnt_out); + + ret = ioctl(fd, RPMB_IOC_REQ_CMD, &ireq); + + +API +--- +.. kernel-doc:: include/linux/rpmb.h + +.. kernel-doc:: drivers/char/rpmb/core.c + +.. kernel-doc:: include/uapi/linux/rpmb.h + +.. kernel-doc:: drivers/char/rpmb/cdev.c + diff --git a/Documentation/driver-api/rpmb/rpmb-tool.rst b/Documentation/driver-api/rpmb/rpmb-tool.rst new file mode 100644 index 000000000000..3f4eed84542a --- /dev/null +++ b/Documentation/driver-api/rpmb/rpmb-tool.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +========== +RPMB Tool +========== + +There is a sample rpmb tool under tools/rpmb/ directory that exercises +the RPMB devices via RPMB character devices interface (/dev/rpmbX) + +.. code-block:: none + + rpmb [-v] [-r|-s] + + rpmb get-info + rpmb program-key + rpmb write-counter [KEY_FILE] + rpmb write-blocks
+ rpmb read-blocks
[KEY_FILE] + + rpmb -v/--verbose: runs in verbose mode diff --git a/Documentation/driver-api/rpmb/simulation-device.rst b/Documentation/driver-api/rpmb/simulation-device.rst new file mode 100644 index 000000000000..21b7bc8bc39d --- /dev/null +++ b/Documentation/driver-api/rpmb/simulation-device.rst @@ -0,0 +1,21 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +====================== +RPMB Simulation Device +====================== + +RPMB partition simulation device is a virtual device that +provides simulation of the RPMB protocol and uses kernel memory +as storage. + +This driver cannot promise any real security, it is suitable for testing +of the RPMB subsystem it self and mostly it was found useful for testing of +RPMB applications prior to RPMB key provisioning/programming as +The RPMB key programming can be performed only once in the life time +of the storage device. + +Implementation: +--------------- + +.. kernel-doc:: drivers/char/rpmb/rpmb_sim.c + diff --git a/MAINTAINERS b/MAINTAINERS index 861ef1fb8e6c..37f70a707519 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12535,6 +12535,7 @@ F: drivers/char/rpmb/* F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb +F: Documentation/driver-api/rpmb.rst F: tools/rpmb/ RTL2830 MEDIA DRIVER From 2a9703f42cb280011a60fc2c56416ee5185ef765 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 29 Mar 2016 16:14:01 +0300 Subject: [PATCH 1014/1276] block: export block_class to be used by class interfaces Enable access to block devices via class_interface outside of the block subsystem. Change-Id: I6115a9b4655e47ec42e47c9720da8784139557bd Signed-off-by: Tomas Winkler --- block/genhd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/genhd.c b/block/genhd.c index be5bab20b2ab..4c777e1b3bd9 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1284,6 +1284,7 @@ static void disk_release(struct device *dev) struct class block_class = { .name = "block", }; +EXPORT_SYMBOL_GPL(block_class); static char *block_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) From 2eb395137cba00503441b1c2d3c83a13eaea9025 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 23 Dec 2014 17:18:37 +0200 Subject: [PATCH 1015/1276] mei: spd: storage proxy driver Host storage proxy driver enables ME FW to store its data on a storage devices such as eMMC or UFS via host interface on a dedicated partition. This patch implements storage over an eMMC GPP partition and UFS LUN. A GPP partition is required for pre manufacturing operation since RPMB partition key can be written only once. V9: 1. Add SPXD Identifiers Change-Id: I524c237d240d93accc9ce071e92744191f23ce87 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig | 2 + drivers/misc/mei/Makefile | 2 + drivers/misc/mei/spd/Kconfig | 12 + drivers/misc/mei/spd/Makefile | 11 + drivers/misc/mei/spd/cmd.c | 485 +++++++++++++++++++++++++++++++++ drivers/misc/mei/spd/cmd.h | 230 ++++++++++++++++ drivers/misc/mei/spd/debugfs.c | 79 ++++++ drivers/misc/mei/spd/gpp.c | 299 ++++++++++++++++++++ drivers/misc/mei/spd/main.c | 118 ++++++++ drivers/misc/mei/spd/spd.h | 93 +++++++ 10 files changed, 1331 insertions(+) create mode 100644 drivers/misc/mei/spd/Kconfig create mode 100644 drivers/misc/mei/spd/Makefile create mode 100644 drivers/misc/mei/spd/cmd.c create mode 100644 drivers/misc/mei/spd/cmd.h create mode 100644 drivers/misc/mei/spd/debugfs.c create mode 100644 drivers/misc/mei/spd/gpp.c create mode 100644 drivers/misc/mei/spd/main.c create mode 100644 drivers/misc/mei/spd/spd.h diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index c49e1d2269af..2a82520f3e4e 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -43,3 +43,5 @@ config INTEL_MEI_TXE Supported SoCs: Intel Bay Trail + +source "drivers/misc/mei/spd/Kconfig" diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index cd6825afa8e1..f83e2fdc01d3 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -23,3 +23,5 @@ mei-txe-objs += hw-txe.o mei-$(CONFIG_EVENT_TRACING) += mei-trace.o CFLAGS_mei-trace.o = -I$(src) + +obj-$(CONFIG_INTEL_MEI_SPD) += spd/ diff --git a/drivers/misc/mei/spd/Kconfig b/drivers/misc/mei/spd/Kconfig new file mode 100644 index 000000000000..8347fbc9fbe0 --- /dev/null +++ b/drivers/misc/mei/spd/Kconfig @@ -0,0 +1,12 @@ +# +# Storage proxy device configuration +# +config INTEL_MEI_SPD + tristate "Intel MEI Host Storage Proxy Driver" + depends on INTEL_MEI && BLOCK + help + A driver for the host storage proxy ME client + The driver enables ME FW to store data on a storage devices + that are accessible only from the host. + + To compile this driver as a module, choose M here. diff --git a/drivers/misc/mei/spd/Makefile b/drivers/misc/mei/spd/Makefile new file mode 100644 index 000000000000..8e8aba94b9ef --- /dev/null +++ b/drivers/misc/mei/spd/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Storage Proxy device driver. +# + +obj-$(CONFIG_INTEL_MEI_SPD) += mei_spd.o +mei_spd-objs := main.o +mei_spd-objs += cmd.o +mei_spd-objs += gpp.o +mei_spd-$(CONFIG_DEBUG_FS) += debugfs.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/misc/mei/spd/cmd.c b/drivers/misc/mei/spd/cmd.c new file mode 100644 index 000000000000..25d682d7eb09 --- /dev/null +++ b/drivers/misc/mei/spd/cmd.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +#define spd_cmd_size(_cmd) \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_##_cmd)) +#define to_spd_hdr(_buf) (struct spd_cmd_hdr *)(_buf) +#define to_spd_cmd(_cmd, _buf) \ + (struct spd_cmd_##_cmd *)((_buf) + sizeof(struct spd_cmd_hdr)) + +const char *spd_cmd_str(enum spd_cmd_type cmd) +{ +#define __SPD_CMD(_cmd) SPD_##_cmd##_CMD +#define SPD_CMD(cmd) case __SPD_CMD(cmd): return #cmd + switch (cmd) { + SPD_CMD(NONE); + SPD_CMD(START_STOP); + SPD_CMD(RPMB_WRITE); + SPD_CMD(RPMB_READ); + SPD_CMD(RPMB_GET_COUNTER); + SPD_CMD(GPP_WRITE); + SPD_CMD(GPP_READ); + SPD_CMD(TRIM); + SPD_CMD(INIT); + SPD_CMD(STORAGE_STATUS); + SPD_CMD(MAX); + default: + return "unknown"; + } +#undef SPD_CMD +#undef __SPD_CMD +} + +const char *mei_spd_dev_str(enum spd_storage_type type) +{ +#define SPD_TYPE(type) case SPD_TYPE_##type: return #type + switch (type) { + SPD_TYPE(UNDEF); + SPD_TYPE(EMMC); + SPD_TYPE(UFS); + default: + return "unknown"; + } +#undef SPD_TYPE +} + +const char *mei_spd_state_str(enum mei_spd_state state) +{ +#define SPD_STATE(state) case MEI_SPD_STATE_##state: return #state + switch (state) { + SPD_STATE(INIT); + SPD_STATE(INIT_WAIT); + SPD_STATE(INIT_DONE); + SPD_STATE(RUNNING); + SPD_STATE(STOPPING); + default: + return "unknown"; + } +#undef SPD_STATE +} + +/** + * mei_spd_init_req - send init request + * + * @spd: spd device + * + * Return: 0 on success + * -EPROTO if called in wrong state + * < 0 on write error + */ +int mei_spd_cmd_init_req(struct mei_spd *spd) +{ + const int req_len = sizeof(struct spd_cmd_hdr); + struct spd_cmd_hdr *hdr; + u32 cmd_type = SPD_INIT_CMD; + ssize_t ret; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state != MEI_SPD_STATE_INIT) + return -EPROTO; + + memset(spd->buf, 0, req_len); + hdr = to_spd_hdr(spd->buf); + + hdr->command_type = cmd_type; + hdr->is_response = 0; + hdr->len = req_len; + + spd->state = MEI_SPD_STATE_INIT_WAIT; + ret = mei_cldev_send(spd->cldev, spd->buf, req_len); + if (ret != req_len) { + spd_err(spd, "start send failed ret = %zd\n", ret); + return ret; + } + + return 0; +} + +/** + * mei_spd_cmd_init_rsp - handle init response message + * + * @spd: spd device + * @cmd: received spd command + * @cmd_sz: received command size + * + * Return: 0 on success; < 0 otherwise + */ +static int mei_spd_cmd_init_rsp(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t cmd_sz) +{ + int type; + int gpp_id; + int i; + + if (cmd_sz < spd_cmd_size(init_resp)) { + spd_err(spd, "Wrong init response size\n"); + return -EINVAL; + } + + if (spd->state != MEI_SPD_STATE_INIT_WAIT) + return -EPROTO; + + type = cmd->init_rsp.type; + gpp_id = cmd->init_rsp.gpp_partition_id; + + switch (type) { + case SPD_TYPE_EMMC: + if (gpp_id < 1 || gpp_id > 4) { + spd_err(spd, "%s unsupported gpp id %d\n", + mei_spd_dev_str(type), gpp_id); + return -EINVAL; + } + break; + + case SPD_TYPE_UFS: + if (gpp_id < 1 || gpp_id > 6) { + spd_err(spd, "%s unsupported gpp id %d\n", + mei_spd_dev_str(type), gpp_id); + return -EINVAL; + } + break; + + default: + spd_err(spd, "unsupported storage type %d\n", + cmd->init_rsp.type); + return -EINVAL; + } + + spd->dev_type = type; + spd->gpp_partition_id = gpp_id; + + if (cmd->init_rsp.serial_no_sz != 0) { + if (cmd->init_rsp.serial_no_sz != + cmd_sz - spd_cmd_size(init_resp)) { + spd_err(spd, "wrong serial no size %u?=%zu\n", + cmd->init_rsp.serial_no_sz, + cmd_sz - spd_cmd_size(init_resp)); + return -EMSGSIZE; + } + + if (cmd->init_rsp.serial_no_sz > 256) { + spd_err(spd, "serial no is too large %u\n", + cmd->init_rsp.serial_no_sz); + return -EMSGSIZE; + } + + spd->dev_id = kzalloc(cmd->init_rsp.serial_no_sz, GFP_KERNEL); + if (!spd->dev_id) + return -ENOMEM; + + spd->dev_id_sz = cmd->init_rsp.serial_no_sz; + if (type == SPD_TYPE_EMMC) { + /* FW have this in be32 format */ + __be32 *sno = (__be32 *)cmd->init_rsp.serial_no; + u32 *dev_id = (u32 *)spd->dev_id; + + for (i = 0; i < spd->dev_id_sz / sizeof(u32); i++) + dev_id[i] = be32_to_cpu(sno[i]); + } else { + memcpy(spd->dev_id, &cmd->init_rsp.serial_no, + cmd->init_rsp.serial_no_sz); + } + } + + spd->state = MEI_SPD_STATE_INIT_DONE; + + return 0; +} + +/** + * mei_spd_cmd_storage_status_req - send storage status message + * + * @spd: spd device + * + * Return: 0 on success + * -EPROTO if called in wrong state + * < 0 on write error + */ +int mei_spd_cmd_storage_status_req(struct mei_spd *spd) +{ + struct spd_cmd_hdr *hdr; + struct spd_cmd_storage_status_req *req; + const int req_len = spd_cmd_size(storage_status_req); + u32 cmd_type = SPD_STORAGE_STATUS_CMD; + ssize_t ret; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state < MEI_SPD_STATE_INIT_DONE) + return -EPROTO; + + memset(spd->buf, 0, req_len); + hdr = to_spd_hdr(spd->buf); + + hdr->command_type = cmd_type; + hdr->is_response = 0; + hdr->len = req_len; + + req = to_spd_cmd(storage_status_req, spd->buf); + req->gpp_on = mei_spd_gpp_is_open(spd); + req->rpmb_on = 0; + + ret = mei_cldev_send(spd->cldev, spd->buf, req_len); + if (ret != req_len) { + spd_err(spd, "send storage status failed ret = %zd\n", ret); + return ret; + } + + if (req->gpp_on || req->rpmb_on) + spd->state = MEI_SPD_STATE_RUNNING; + else + spd->state = MEI_SPD_STATE_INIT_DONE; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + return 0; +} + +static int mei_spd_cmd_gpp_write(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + size_t len = SPD_GPP_WRITE_DATA_LEN(*cmd); + int ret; + + if (out_buf_sz < spd_cmd_size(gpp_write_req)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + ret = mei_spd_gpp_write(spd, cmd->gpp_write_req.offset, + cmd->gpp_write_req.data, len); + if (ret) { + spd_err(spd, "Failed to write to gpp ret = %d\n", ret); + return SPD_STATUS_GENERAL_FAILURE; + } + + spd_dbg(spd, "wrote %zd bytes of data\n", len); + + cmd->header.len = spd_cmd_size(gpp_write_rsp); + + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_gpp_read(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + size_t len; + int ret; + + if (out_buf_sz < spd_cmd_size(gpp_read_req)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + len = cmd->gpp_read_req.size_to_read; + if (len > SPD_CLIENT_GPP_DATA_MAX_SIZE) { + spd_err(spd, "Block is to large to read\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + ret = mei_spd_gpp_read(spd, cmd->gpp_read_req.offset, + cmd->gpp_read_resp.data, len); + + if (ret) { + spd_err(spd, "Failed to read from gpp ret = %d\n", ret); + return SPD_STATUS_GENERAL_FAILURE; + } + + spd_dbg(spd, "read %zd bytes of data\n", len); + + cmd->header.len = spd_cmd_size(gpp_read_rsp) + len; + + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + u32 spd_cmd; + int ret; + + spd_cmd = cmd->header.command_type; + + spd_dbg(spd, "rsp [%d] %s : state [%d] %s\n", + spd_cmd, spd_cmd_str(spd_cmd), + spd->state, mei_spd_state_str(spd->state)); + + switch (spd_cmd) { + case SPD_INIT_CMD: + ret = mei_spd_cmd_init_rsp(spd, cmd, out_buf_sz); + if (ret) + break; + mutex_unlock(&spd->lock); + mei_spd_gpp_init(spd); + mutex_lock(&spd->lock); + break; + default: + ret = -EINVAL; + spd_err(spd, "Wrong response command %d\n", spd_cmd); + break; + } + + return ret; +} + +/** + * mei_spd_cmd_request - dispatch command requests from the SPD device + * + * @spd: spd device + * @out_buf_sz: buffer size + * + * Return: (TBD) + */ +static int mei_spd_cmd_request(struct mei_spd *spd, ssize_t out_buf_sz) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + ssize_t written; + u32 spd_cmd; + int ret; + + spd_cmd = cmd->header.command_type; + + spd_dbg(spd, "req [%d] %s : state [%d] %s\n", + spd_cmd, spd_cmd_str(spd_cmd), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state < MEI_SPD_STATE_RUNNING) { + spd_err(spd, "Wrong state %d\n", spd->state); + ret = SPD_STATUS_INVALID_COMMAND; + goto reply; + } + + switch (spd_cmd) { + case SPD_RPMB_WRITE_CMD: + case SPD_RPMB_READ_CMD: + case SPD_RPMB_GET_COUNTER_CMD: + spd_err(spd, "Command %d is not supported\n", spd_cmd); + ret = SPD_STATUS_NOT_SUPPORTED; + break; + case SPD_GPP_WRITE_CMD: + ret = mei_spd_cmd_gpp_write(spd, cmd, out_buf_sz); + break; + case SPD_GPP_READ_CMD: + ret = mei_spd_cmd_gpp_read(spd, cmd, out_buf_sz); + break; + case SPD_TRIM_CMD: + spd_err(spd, "Command %d is not supported\n", spd_cmd); + ret = SPD_STATUS_NOT_SUPPORTED; + break; + default: + spd_err(spd, "Wrong request command %d\n", spd_cmd); + ret = SPD_STATUS_INVALID_COMMAND; + break; + } +reply: + cmd->header.is_response = 1; + cmd->header.status = ret; + if (ret != SPD_STATUS_SUCCESS) + cmd->header.len = sizeof(struct spd_cmd_hdr); + + written = mei_cldev_send(spd->cldev, spd->buf, cmd->header.len); + if (written != cmd->header.len) { + ret = SPD_STATUS_GENERAL_FAILURE; + spd_err(spd, "Failed to send reply written = %zd\n", written); + } + + /* FIXME: translate ret to errno */ + if (ret) + return -EINVAL; + + return 0; +} + +ssize_t mei_spd_cmd(struct mei_spd *spd) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + ssize_t out_buf_sz; + int ret; + + out_buf_sz = mei_cldev_recv(spd->cldev, spd->buf, spd->buf_sz); + if (out_buf_sz < 0) { + spd_err(spd, "failure in receive ret = %zd\n", out_buf_sz); + return out_buf_sz; + } + + if (out_buf_sz == 0) { + spd_err(spd, "received empty msg\n"); + return 0; + } + + /* check that we've received at least sizeof(header) */ + if (out_buf_sz < sizeof(struct spd_cmd_hdr)) { + spd_err(spd, "Request is too short\n"); + return -EFAULT; + } + + if (cmd->header.is_response) + ret = mei_spd_cmd_response(spd, out_buf_sz); + else + ret = mei_spd_cmd_request(spd, out_buf_sz); + + return ret; +} + +static void mei_spd_status_send_work(struct work_struct *work) +{ + struct mei_spd *spd = + container_of(work, struct mei_spd, status_send_w); + + mutex_lock(&spd->lock); + mei_spd_cmd_storage_status_req(spd); + mutex_unlock(&spd->lock); +} + +void mei_spd_free(struct mei_spd *spd) +{ + if (!spd) + return; + + cancel_work_sync(&spd->status_send_w); + + kfree(spd->buf); + kfree(spd); +} + +struct mei_spd *mei_spd_alloc(struct mei_cl_device *cldev) +{ + struct mei_spd *spd; + u8 *buf; + + spd = kzalloc(sizeof(*spd), GFP_KERNEL); + if (!spd) + return NULL; + + spd->buf_sz = sizeof(struct spd_cmd) + SPD_CLIENT_GPP_DATA_MAX_SIZE; + buf = kmalloc(spd->buf_sz, GFP_KERNEL); + if (!buf) + goto free; + + spd->cldev = cldev; + spd->buf = buf; + spd->state = MEI_SPD_STATE_INIT; + mutex_init(&spd->lock); + INIT_WORK(&spd->status_send_w, mei_spd_status_send_work); + + return spd; +free: + kfree(spd); + return NULL; +} diff --git a/drivers/misc/mei/spd/cmd.h b/drivers/misc/mei/spd/cmd.h new file mode 100644 index 000000000000..3f77550f44ab --- /dev/null +++ b/drivers/misc/mei/spd/cmd.h @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _SPD_CMD_H +#define _SPD_CMD_H + +#include + +/** + * enum spd_cmd_type - available commands + * + * @SPD_NONE_CMD : Lower command sentinel. + * @SPD_START_STOP_CMD : start stop command (deprecated). [Host -> TEE] + * @SPD_RPMB_WRITE_CMD : RPMB write request. [TEE -> Host] + * @SPD_RPMB_READ_CMD : RPMB read request. [TEE -> Host] + * @SPD_RPMB_GET_COUNTER_CMD: get counter request [TEE -> Host] + * @SPD_GPP_WRITE_CMD : GPP write request. [TEE -> Host] + * @SPD_GPP_READ_CMD : GPP read request. [TEE -> Host] + * @SPD_TRIM_CMD : TRIM command [TEE -> Host] + * @SPD_INIT_CMD : initial handshake between host and fw. [Host -> TEE] + * @SPD_STORAGE_STATUS_CMD : the backing storage status. [Host -> TEE] + * @SPD_MAX_CMD: Upper command sentinel. + */ +enum spd_cmd_type { + SPD_NONE_CMD = 0, + SPD_START_STOP_CMD, + SPD_RPMB_WRITE_CMD, + SPD_RPMB_READ_CMD, + SPD_RPMB_GET_COUNTER_CMD, + SPD_GPP_WRITE_CMD, + SPD_GPP_READ_CMD, + SPD_TRIM_CMD, + SPD_INIT_CMD, + SPD_STORAGE_STATUS_CMD, + SPD_MAX_CMD, +}; + +enum spd_status { + SPD_STATUS_SUCCESS = 0, + SPD_STATUS_GENERAL_FAILURE = 1, + SPD_STATUS_NOT_READY = 2, + SPD_STATUS_NOT_SUPPORTED = 3, + SPD_STATUS_INVALID_COMMAND = 4, +}; + +/** + * enum spd_storage_type - storage device type + * + * @SPD_TYPE_UNDEF: lower enum sentinel + * @SPD_TYPE_EMMC: emmc device + * @SPD_TYPE_UFS: ufs device + * @SPD_TYPE_MAX: upper enum sentinel + */ +enum spd_storage_type { + SPD_TYPE_UNDEF = 0, + SPD_TYPE_EMMC = 1, + SPD_TYPE_UFS = 2, + SPD_TYPE_MAX +}; + +/** + * struct spd_cmd_hdr - Host storage Command Header + * + * @command_type: SPD_TYPES + * @is_response: 1 == Response, 0 == Request + * @len: command length + * @status: command status + * @reserved: reserved + */ +struct spd_cmd_hdr { + u32 command_type : 7; + u32 is_response : 1; + u32 len : 13; + u32 status : 8; + u32 reserved : 3; +} __packed; + +/** + * RPMB Frame Size as defined by the JDEC spec + */ +#define SPD_CLIENT_RPMB_DATA_MAX_SIZE (512) + +/** + * struct spd_cmd_init_resp + * commandType == HOST_STORAGE_INIT_CMD + * + * @gpp_partition_id: gpp_partition: + * UFS: LUN Number (0-7) + * EMMC: 1-4. + * 0xff: GPP not supported + * @type: storage hw type + * SPD_TYPE_EMMC + * SPD_TYPE_UFS + * @serial_no_sz: serial_no size + * @serial_no: device serial number + */ +struct spd_cmd_init_resp { + u32 gpp_partition_id; + u32 type; + u32 serial_no_sz; + u8 serial_no[0]; +}; + +/** + * struct spd_cmd_storage_status_req + * commandType == SPD_STORAGE_STATUS_CMD + * + * @gpp_on: availability of the gpp backing storage + * 0 - GP partition is accessible + * 1 - GP partition is not accessible + * @rpmb_on: availability of the backing storage + * 0 - RPMB partition is accessible + * 1 - RPBM partition is not accessible + */ +struct spd_cmd_storage_status_req { + u32 gpp_on; + u32 rpmb_on; +} __packed; + +/** + * struct spd_cmd_rpmb_write + * command_type == SPD_RPMB_WRITE_CMD + * + * @rpmb_frame: RPMB frame are constant size (512) + */ +struct spd_cmd_rpmb_write { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_rpmb_read + * command_type == SPD_RPMB_READ_CMD + * + * @rpmb_frame: RPMB frame are constant size (512) + */ +struct spd_cmd_rpmb_read { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_rpmb_get_counter + * command_type == SPD_RPMB_GET_COUNTER_CMD + * + * @rpmb_frame: frame containing frame counter + */ +struct spd_cmd_rpmb_get_counter { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_gpp_write_req + * command_type == SPD_GPP_WRITE_CMD + * + * @offset: frame offset in partition + * @data: 4K page + */ +struct spd_cmd_gpp_write_req { + u32 offset; + u8 data[0]; +} __packed; + +/** + * struct spd_cmd_gpp_write_rsp + * command_type == SPD_GPP_WRITE_CMD + * + * @reserved: reserved + */ +struct spd_cmd_gpp_write_rsp { + u32 reserved[2]; +} __packed; + +/** + * struct spd_cmd_gpp_read_req + * command_type == SPD_GPP_READ_CMD + * + * @offset: offset of a frame on GPP partition + * @size_to_read: data length to read (must be ) + */ +struct spd_cmd_gpp_read_req { + u32 offset; + u32 size_to_read; +} __packed; + +/** + * struct spd_cmd_gpp_read_rsp + * command_type == SPD_GPP_READ_CMD + * + * @reserved: reserved + * @data: data + */ +struct spd_cmd_gpp_read_rsp { + u32 reserved; + u8 data[0]; +} __packed; + +#define SPD_GPP_READ_DATA_LEN(cmd) ((cmd).header.len - \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_gpp_read_rsp))) + +#define SPD_GPP_WRITE_DATA_LEN(cmd) ((cmd).header.len - \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_gpp_write_req))) + +struct spd_cmd { + struct spd_cmd_hdr header; + + union { + struct spd_cmd_rpmb_write rpmb_write; + struct spd_cmd_rpmb_read rpmb_read; + struct spd_cmd_rpmb_get_counter rpmb_get_counter; + + struct spd_cmd_gpp_write_req gpp_write_req; + struct spd_cmd_gpp_write_rsp gpp_write_rsp; + + struct spd_cmd_gpp_read_req gpp_read_req; + struct spd_cmd_gpp_read_rsp gpp_read_resp; + + struct spd_cmd_init_resp init_rsp; + struct spd_cmd_storage_status_req status_req; + }; +} __packed; + +/* GPP Max data 4K */ +#define SPD_CLIENT_GPP_DATA_MAX_SIZE (4096) + +const char *spd_cmd_str(enum spd_cmd_type cmd); +const char *mei_spd_dev_str(enum spd_storage_type type); + +#endif /* _SPD_CMD_H */ diff --git a/drivers/misc/mei/spd/debugfs.c b/drivers/misc/mei/spd/debugfs.c new file mode 100644 index 000000000000..dfbb62a49fcc --- /dev/null +++ b/drivers/misc/mei/spd/debugfs.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +static ssize_t mei_spd_dbgfs_read_info(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_spd *spd = fp->private_data; + size_t bufsz = 4095; + char *buf; + int pos = 0; + int ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "DEV STATE: [%d] %s\n", + spd->state, mei_spd_state_str(spd->state)); + pos += scnprintf(buf + pos, bufsz - pos, "DEV TYPE : [%d] %s\n", + spd->dev_type, mei_spd_dev_str(spd->dev_type)); + pos += scnprintf(buf + pos, bufsz - pos, " ID SIZE : %d\n", + spd->dev_id_sz); + pos += scnprintf(buf + pos, bufsz - pos, " ID : '%s'\n", "N/A"); + pos += scnprintf(buf + pos, bufsz - pos, "GPP\n"); + pos += scnprintf(buf + pos, bufsz - pos, " id : %d\n", + spd->gpp_partition_id); + pos += scnprintf(buf + pos, bufsz - pos, " opened : %1d\n", + mei_spd_gpp_is_open(spd)); + + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_spd_dbgfs_fops_info = { + .open = simple_open, + .read = mei_spd_dbgfs_read_info, + .llseek = generic_file_llseek, +}; + +void mei_spd_dbgfs_deregister(struct mei_spd *spd) +{ + if (!spd->dbgfs_dir) + return; + debugfs_remove_recursive(spd->dbgfs_dir); + spd->dbgfs_dir = NULL; +} + +int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir(name, NULL); + if (!dir) + return -ENOMEM; + + spd->dbgfs_dir = dir; + + f = debugfs_create_file("info", 0400, dir, + spd, &mei_spd_dbgfs_fops_info); + if (!f) { + spd_err(spd, "info: registration failed\n"); + goto err; + } + + return 0; +err: + mei_spd_dbgfs_deregister(spd); + return -ENODEV; +} diff --git a/drivers/misc/mei/spd/gpp.c b/drivers/misc/mei/spd/gpp.c new file mode 100644 index 000000000000..b5d1a27a50ee --- /dev/null +++ b/drivers/misc/mei/spd/gpp.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +static struct page *page_read(struct address_space *mapping, int index) +{ + return read_mapping_page(mapping, index, NULL); +} + +static int mei_spd_bd_read(struct mei_spd *spd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE - 1); + int cpylen; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; + else + cpylen = len; + len = len - cpylen; + + page = page_read(spd->gpp->bd_inode->i_mapping, index); + if (IS_ERR(page)) + return PTR_ERR(page); + + memcpy(buf, page_address(page) + offset, cpylen); + put_page(page); + + if (retlen) + *retlen += cpylen; + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + +static int _mei_spd_bd_write(struct block_device *dev, const u_char *buf, + loff_t to, size_t len, size_t *retlen) +{ + struct page *page; + struct address_space *mapping = dev->bd_inode->i_mapping; + int index = to >> PAGE_SHIFT; /* page index */ + int offset = to & ~PAGE_MASK; /* page offset */ + int cpylen; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; + else + cpylen = len; + len = len - cpylen; + + page = page_read(mapping, index); + if (IS_ERR(page)) + return PTR_ERR(page); + + if (memcmp(page_address(page) + offset, buf, cpylen)) { + lock_page(page); + memcpy(page_address(page) + offset, buf, cpylen); + set_page_dirty(page); + unlock_page(page); + balance_dirty_pages_ratelimited(mapping); + } + put_page(page); + + if (retlen) + *retlen += cpylen; + + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + +static int mei_spd_bd_write(struct mei_spd *spd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + + ret = _mei_spd_bd_write(spd->gpp, buf, to, len, retlen); + if (ret > 0) + ret = 0; + + sync_blockdev(spd->gpp); + + return ret; +} + +static void mei_spd_bd_sync(struct mei_spd *spd) +{ + sync_blockdev(spd->gpp); +} + +#define GPP_FMODE (FMODE_WRITE | FMODE_READ | FMODE_EXCL) + +bool mei_spd_gpp_is_open(struct mei_spd *spd) +{ + struct request_queue *q; + + if (!spd->gpp) + return false; + + q = spd->gpp->bd_queue; + if (q && !blk_queue_stopped(q)) + return true; + + return false; +} + +static int mei_spd_gpp_open(struct mei_spd *spd, struct device *dev) +{ + int ret; + + if (spd->gpp) + return 0; + + spd->gpp = blkdev_get_by_dev(dev->devt, GPP_FMODE, spd); + if (IS_ERR(spd->gpp)) { + ret = PTR_ERR(spd->gpp); + spd->gpp = NULL; + spd_dbg(spd, "Can't get GPP block device %s ret = %d\n", + dev_name(dev), ret); + return ret; + } + + spd_dbg(spd, "gpp partition created\n"); + return 0; +} + +static int mei_spd_gpp_close(struct mei_spd *spd) +{ + if (!spd->gpp) + return 0; + + mei_spd_bd_sync(spd); + blkdev_put(spd->gpp, GPP_FMODE); + spd->gpp = NULL; + + spd_dbg(spd, "gpp partition removed\n"); + return 0; +} + +#define UFSHCD "ufshcd" +static bool mei_spd_lun_ufs_match(struct mei_spd *spd, struct device *dev) +{ + struct gendisk *disk = dev_to_disk(dev); + struct scsi_device *sdev; + + switch (disk->major) { + case SCSI_DISK0_MAJOR: + case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR: + case SCSI_DISK8_MAJOR ... SCSI_DISK15_MAJOR: + break; + default: + return false; + } + + sdev = to_scsi_device(dev->parent); + + if (!sdev->host || + strncmp(sdev->host->hostt->name, UFSHCD, strlen(UFSHCD))) + return false; + + return sdev->lun == spd->gpp_partition_id; +} + +static bool mei_spd_gpp_mmc_match(struct mei_spd *spd, struct device *dev) +{ + struct gendisk *disk = dev_to_disk(dev); + int idx, part_id; + + if (disk->major != MMC_BLOCK_MAJOR) + return false; + + if (sscanf(disk->disk_name, "mmcblk%dgp%d", &idx, &part_id) != 2) + return false; + + return part_id == spd->gpp_partition_id - 1; +} + +static bool mei_spd_gpp_match(struct mei_spd *spd, struct device *dev) +{ + /* we are only interested in physical partitions */ + if (strncmp(dev->type->name, "disk", sizeof("disk"))) + return false; + + if (spd->dev_type == SPD_TYPE_EMMC) + return mei_spd_gpp_mmc_match(spd, dev); + else if (spd->dev_type == SPD_TYPE_UFS) + return mei_spd_lun_ufs_match(spd, dev); + else + return false; +} + +static int gpp_add_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = container_of(intf, struct mei_spd, gpp_interface); + + if (!mei_spd_gpp_match(spd, dev)) + return 0; + + mutex_lock(&spd->lock); + if (mei_spd_gpp_open(spd, dev)) { + mutex_unlock(&spd->lock); + return 0; + } + + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); + + return 0; +} + +static void gpp_remove_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = container_of(intf, struct mei_spd, gpp_interface); + + if (!mei_spd_gpp_match(spd, dev)) + return; + + mutex_lock(&spd->lock); + if (mei_spd_gpp_close(spd)) { + mutex_unlock(&spd->lock); + return; + } + + if (spd->state != MEI_SPD_STATE_STOPPING) + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); +} + +int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size) +{ + int ret; + + spd_dbg(spd, "GPP read offset = %zx, size = %zx\n", off, size); + + if (!mei_spd_gpp_is_open(spd)) + return -ENODEV; + + ret = mei_spd_bd_read(spd, off, size, NULL, data); + if (ret) + spd_err(spd, "GPP read failed ret = %d\n", ret); + + return ret; +} + +int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size) +{ + int ret; + + spd_dbg(spd, "GPP write offset = %zx, size = %zx\n", off, size); + + if (!mei_spd_gpp_is_open(spd)) + return -ENODEV; + + ret = mei_spd_bd_write(spd, off, size, NULL, data); + if (ret) + spd_err(spd, "GPP write failed ret = %d\n", ret); + + return ret; +} + +void mei_spd_gpp_prepare(struct mei_spd *spd) +{ + spd->gpp_interface.add_dev = gpp_add_device; + spd->gpp_interface.remove_dev = gpp_remove_device; + spd->gpp_interface.class = &block_class; +} + +int mei_spd_gpp_init(struct mei_spd *spd) +{ + int ret; + + ret = class_interface_register(&spd->gpp_interface); + if (ret) + spd_err(spd, "Can't register interface\n"); + return ret; +} + +void mei_spd_gpp_exit(struct mei_spd *spd) +{ + class_interface_unregister(&spd->gpp_interface); +} diff --git a/drivers/misc/mei/spd/main.c b/drivers/misc/mei/spd/main.c new file mode 100644 index 000000000000..2adccce70eaf --- /dev/null +++ b/drivers/misc/mei/spd/main.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include + +#include "spd.h" + +static void mei_spd_rx_cb(struct mei_cl_device *cldev) +{ + struct mei_spd *spd = mei_cldev_get_drvdata(cldev); + + mutex_lock(&spd->lock); + mei_spd_cmd(spd); + mutex_unlock(&spd->lock); +} + +static int mei_spd_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct mei_spd *spd; + u8 ver = mei_cldev_ver(cldev); + int ret; + + dev_dbg(&cldev->dev, "probing mei spd ver = %d\n", ver); + + if (ver < 2) { + dev_warn(&cldev->dev, "unuspported protocol version %d\n", ver); + return -ENODEV; + } + + spd = mei_spd_alloc(cldev); + if (!spd) + return -ENOMEM; + + mei_cldev_set_drvdata(cldev, spd); + + ret = mei_spd_dbgfs_register(spd, "spd"); + if (ret) + goto free; + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + dev_err(&cldev->dev, "Could not enable device ret = %d\n", ret); + goto free; + } + + ret = mei_cldev_register_rx_cb(cldev, mei_spd_rx_cb); + if (ret) { + dev_err(&cldev->dev, "Error register event %d\n", ret); + goto disable; + } + + spd_dbg(spd, "protocol version %d\n", ver); + mei_spd_gpp_prepare(spd); + mutex_lock(&spd->lock); + ret = mei_spd_cmd_init_req(spd); + mutex_unlock(&spd->lock); + if (ret) { + dev_err(&cldev->dev, "Could not start ret = %d\n", ret); + goto disable; + } + + return 0; + +disable: + mei_cldev_disable(cldev); + +free: + mei_spd_dbgfs_deregister(spd); + mei_cldev_set_drvdata(cldev, NULL); + mei_spd_free(spd); + return ret; +} + +static int mei_spd_remove(struct mei_cl_device *cldev) +{ + struct mei_spd *spd = mei_cldev_get_drvdata(cldev); + + if (spd->state == MEI_SPD_STATE_RUNNING) { + spd->state = MEI_SPD_STATE_STOPPING; + mei_spd_gpp_exit(spd); + mutex_lock(&spd->lock); + mei_spd_cmd_storage_status_req(spd); + mutex_unlock(&spd->lock); + } + + mei_cldev_disable(cldev); + mei_spd_dbgfs_deregister(spd); + mei_cldev_set_drvdata(cldev, NULL); + mei_spd_free(spd); + + return 0; +} + +#define MEI_SPD_UUID UUID_LE(0x2a39291f, 0x5551, 0x482f, \ + 0x99, 0xcb, 0x9e, 0x22, 0x74, 0x97, 0x8c, 0xa8) + +static struct mei_cl_device_id mei_spd_tbl[] = { + { .uuid = MEI_SPD_UUID, .version = MEI_CL_VERSION_ANY}, + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, mei_spd_tbl); + +static struct mei_cl_driver mei_spd_driver = { + .id_table = mei_spd_tbl, + .name = "mei_spd", + + .probe = mei_spd_probe, + .remove = mei_spd_remove, +}; + +module_mei_cl_driver(mei_spd_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Storage Proxy driver based on mei bus"); diff --git a/drivers/misc/mei/spd/spd.h b/drivers/misc/mei/spd/spd.h new file mode 100644 index 000000000000..cd30d0ed18ae --- /dev/null +++ b/drivers/misc/mei/spd/spd.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _MEI_SPD_H +#define _MEI_SPD_H + +#include +#include + +enum mei_spd_state { + MEI_SPD_STATE_INIT, + MEI_SPD_STATE_INIT_WAIT, + MEI_SPD_STATE_INIT_DONE, + MEI_SPD_STATE_RUNNING, + MEI_SPD_STATE_STOPPING, +}; + +/** + * struct mei_spd - spd device struct + * + * @cldev: client bus device + * @gpp: GPP partition block device + * @gpp_partition_id: GPP partition id (1-6) + * @gpp_interface: gpp class interface for discovery + * @dev_type: storage device type + * @dev_id_sz: device id size + * @dev_id: device id string + * @lock: mutex to sync request processing + * @state: driver state + * @status_send_w: workitem for sending status to the FW + * @buf_sz: receive/transmit buffer allocated size + * @buf: receive/transmit buffer + * @dbgfs_dir: debugfs directory entry + */ +struct mei_spd { + struct mei_cl_device *cldev; + struct block_device *gpp; + u32 gpp_partition_id; + struct class_interface gpp_interface; + u32 dev_type; + u32 dev_id_sz; + u8 *dev_id; + struct mutex lock; /* mutex to sync request processing */ + enum mei_spd_state state; + struct work_struct status_send_w; + size_t buf_sz; + u8 *buf; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ +}; + +struct mei_spd *mei_spd_alloc(struct mei_cl_device *cldev); +void mei_spd_free(struct mei_spd *spd); + +int mei_spd_cmd_init_req(struct mei_spd *spd); +int mei_spd_cmd_storage_status_req(struct mei_spd *spd); +ssize_t mei_spd_cmd(struct mei_spd *spd); + +void mei_spd_gpp_prepare(struct mei_spd *spd); +bool mei_spd_gpp_is_open(struct mei_spd *spd); +int mei_spd_gpp_init(struct mei_spd *spd); +void mei_spd_gpp_exit(struct mei_spd *spd); +int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size); +int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size); + +#if IS_ENABLED(CONFIG_DEBUG_FS) +int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name); +void mei_spd_dbgfs_deregister(struct mei_spd *spd); +#else +static inline int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name) +{ + return 0; +} + +static inline void mei_spd_dbgfs_deregister(struct mei_spd *spd) +{ +} + +#endif /* CONFIG_DEBUG_FS */ + +const char *mei_spd_state_str(enum mei_spd_state state); + +#define spd_err(spd, fmt, ...) \ + dev_err(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) +#define spd_warn(spd, fmt, ...) \ + dev_warn(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) +#define spd_dbg(spd, fmt, ...) \ + dev_dbg(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) + +#endif /* _MEI_SPD_H */ From a429e0ac2b21a7479cd3be048c8c9f7e08cd3ace Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 9 Feb 2015 17:13:20 +0200 Subject: [PATCH 1016/1276] mei: spd: connect to the rpmb subsystem Connect SPD to RPMB subsystem and implement RPMB storage commands. V9: add SPDX identifiers. Change-Id: I21c9f4526ae5906779b03a488c289c037a18d6e2 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/spd/Kconfig | 2 +- drivers/misc/mei/spd/Makefile | 1 + drivers/misc/mei/spd/cmd.c | 67 +++++++++++- drivers/misc/mei/spd/main.c | 2 + drivers/misc/mei/spd/rpmb.c | 199 ++++++++++++++++++++++++++++++++++ drivers/misc/mei/spd/spd.h | 11 ++ 6 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/mei/spd/rpmb.c diff --git a/drivers/misc/mei/spd/Kconfig b/drivers/misc/mei/spd/Kconfig index 8347fbc9fbe0..085f9caa8c66 100644 --- a/drivers/misc/mei/spd/Kconfig +++ b/drivers/misc/mei/spd/Kconfig @@ -3,7 +3,7 @@ # config INTEL_MEI_SPD tristate "Intel MEI Host Storage Proxy Driver" - depends on INTEL_MEI && BLOCK + depends on INTEL_MEI && BLOCK && RPMB help A driver for the host storage proxy ME client The driver enables ME FW to store data on a storage devices diff --git a/drivers/misc/mei/spd/Makefile b/drivers/misc/mei/spd/Makefile index 8e8aba94b9ef..72d0bca2974e 100644 --- a/drivers/misc/mei/spd/Makefile +++ b/drivers/misc/mei/spd/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_INTEL_MEI_SPD) += mei_spd.o mei_spd-objs := main.o mei_spd-objs += cmd.o mei_spd-objs += gpp.o +mei_spd-objs += rpmb.o mei_spd-$(CONFIG_DEBUG_FS) += debugfs.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/misc/mei/spd/cmd.c b/drivers/misc/mei/spd/cmd.c index 25d682d7eb09..3f45902e23da 100644 --- a/drivers/misc/mei/spd/cmd.c +++ b/drivers/misc/mei/spd/cmd.c @@ -12,6 +12,9 @@ #define spd_cmd_size(_cmd) \ (sizeof(struct spd_cmd_hdr) + \ sizeof(struct spd_cmd_##_cmd)) +#define spd_cmd_rpmb_size(_cmd) \ + (spd_cmd_size(_cmd) + SPD_CLIENT_RPMB_DATA_MAX_SIZE) + #define to_spd_hdr(_buf) (struct spd_cmd_hdr *)(_buf) #define to_spd_cmd(_cmd, _buf) \ (struct spd_cmd_##_cmd *)((_buf) + sizeof(struct spd_cmd_hdr)) @@ -231,7 +234,7 @@ int mei_spd_cmd_storage_status_req(struct mei_spd *spd) req = to_spd_cmd(storage_status_req, spd->buf); req->gpp_on = mei_spd_gpp_is_open(spd); - req->rpmb_on = 0; + req->rpmb_on = mei_spd_rpmb_is_open(spd); ret = mei_cldev_send(spd->cldev, spd->buf, req_len); if (ret != req_len) { @@ -308,6 +311,60 @@ static int mei_spd_cmd_gpp_read(struct mei_spd *spd, struct spd_cmd *cmd, return SPD_STATUS_SUCCESS; } +static int mei_spd_cmd_rpmb_read(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_read.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_read)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_READ_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "read RPMB frame performed\n"); + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_rpmb_write(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_write.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_write)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_WRITE_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "write RPMB frame performed\n"); + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_rpmb_get_counter(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_get_counter.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_get_counter)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_WRITE_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "get RPMB counter performed\n"); + return SPD_STATUS_SUCCESS; +} + static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) { struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; @@ -326,6 +383,7 @@ static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) if (ret) break; mutex_unlock(&spd->lock); + mei_spd_rpmb_init(spd); mei_spd_gpp_init(spd); mutex_lock(&spd->lock); break; @@ -367,10 +425,13 @@ static int mei_spd_cmd_request(struct mei_spd *spd, ssize_t out_buf_sz) switch (spd_cmd) { case SPD_RPMB_WRITE_CMD: + ret = mei_spd_cmd_rpmb_write(spd, cmd, out_buf_sz); + break; case SPD_RPMB_READ_CMD: + ret = mei_spd_cmd_rpmb_read(spd, cmd, out_buf_sz); + break; case SPD_RPMB_GET_COUNTER_CMD: - spd_err(spd, "Command %d is not supported\n", spd_cmd); - ret = SPD_STATUS_NOT_SUPPORTED; + ret = mei_spd_cmd_rpmb_get_counter(spd, cmd, out_buf_sz); break; case SPD_GPP_WRITE_CMD: ret = mei_spd_cmd_gpp_write(spd, cmd, out_buf_sz); diff --git a/drivers/misc/mei/spd/main.c b/drivers/misc/mei/spd/main.c index 2adccce70eaf..468cceffb7a0 100644 --- a/drivers/misc/mei/spd/main.c +++ b/drivers/misc/mei/spd/main.c @@ -53,6 +53,7 @@ static int mei_spd_probe(struct mei_cl_device *cldev, spd_dbg(spd, "protocol version %d\n", ver); mei_spd_gpp_prepare(spd); + mei_spd_rpmb_prepare(spd); mutex_lock(&spd->lock); ret = mei_spd_cmd_init_req(spd); mutex_unlock(&spd->lock); @@ -80,6 +81,7 @@ static int mei_spd_remove(struct mei_cl_device *cldev) if (spd->state == MEI_SPD_STATE_RUNNING) { spd->state = MEI_SPD_STATE_STOPPING; mei_spd_gpp_exit(spd); + mei_spd_rpmb_exit(spd); mutex_lock(&spd->lock); mei_spd_cmd_storage_status_req(spd); mutex_unlock(&spd->lock); diff --git a/drivers/misc/mei/spd/rpmb.c b/drivers/misc/mei/spd/rpmb.c new file mode 100644 index 000000000000..b74d0cd8f802 --- /dev/null +++ b/drivers/misc/mei/spd/rpmb.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Intel Host Storage Interface Linux driver + * Copyright (c) 2015 - 2018, Intel Corporation. + */ + +#include "cmd.h" +#include "spd.h" +#include + +static int mei_spd_rpmb_start(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (spd->rdev == rdev) + return 0; + + if (spd->rdev) { + spd_warn(spd, "rpmb device already registered\n"); + return -EEXIST; + } + + spd->rdev = rpmb_dev_get(rdev); + spd_dbg(spd, "rpmb partition created\n"); + return 0; +} + +static int mei_spd_rpmb_stop(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (!spd->rdev) { + spd_dbg(spd, "Already stopped\n"); + return -EPROTO; + } + + if (rdev && spd->rdev != rdev) { + spd_dbg(spd, "Wrong RPMB on stop\n"); + return -EINVAL; + } + + rpmb_dev_put(spd->rdev); + spd->rdev = NULL; + + spd_dbg(spd, "rpmb partition removed\n"); + return 0; +} + +static int mei_spd_rpmb_match(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (spd->dev_id_sz && rdev->ops->dev_id) { + if (rdev->ops->dev_id_len != spd->dev_id_sz || + memcmp(rdev->ops->dev_id, spd->dev_id, + rdev->ops->dev_id_len)) { + spd_dbg(spd, "ignore request for another rpmb\n"); + /* return 0; FW sends garbage now, ignore it */ + } + } + + switch (rdev->ops->type) { + case RPMB_TYPE_EMMC: + if (spd->dev_type != SPD_TYPE_EMMC) + return 0; + break; + case RPMB_TYPE_UFS: + if (spd->dev_type != SPD_TYPE_UFS) + return 0; + break; + default: + return 0; + } + + return 1; +} + +static int rpmb_add_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = + container_of(intf, struct mei_spd, rpmb_interface); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + if (!mei_spd_rpmb_match(spd, rdev)) + return 0; + + mutex_lock(&spd->lock); + if (mei_spd_rpmb_start(spd, rdev)) { + mutex_unlock(&spd->lock); + return 0; + } + + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); + + return 0; +} + +static void rpmb_remove_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = + container_of(intf, struct mei_spd, rpmb_interface); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + if (!mei_spd_rpmb_match(spd, rdev)) + return; + + mutex_lock(&spd->lock); + if (mei_spd_rpmb_stop(spd, rdev)) { + mutex_unlock(&spd->lock); + return; + } + + if (spd->state != MEI_SPD_STATE_STOPPING) + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); +} + +void mei_spd_rpmb_prepare(struct mei_spd *spd) +{ + spd->rpmb_interface.add_dev = rpmb_add_device; + spd->rpmb_interface.remove_dev = rpmb_remove_device; + spd->rpmb_interface.class = &rpmb_class; +} + +/** + * mei_spd_rpmb_init - init RPMB connection + * + * @spd: device + * + * Locking: spd->lock should not be held + * Returns: 0 if initialized successfully, <0 otherwise + */ +int mei_spd_rpmb_init(struct mei_spd *spd) +{ + int ret; + + ret = class_interface_register(&spd->rpmb_interface); + if (ret) + spd_err(spd, "Can't register interface\n"); + return ret; +} + +/** + * mei_spd_rpmb_exit - clean RPMB connection + * + * @spd: device + * + * Locking: spd->lock should not be held + */ +void mei_spd_rpmb_exit(struct mei_spd *spd) +{ + class_interface_unregister(&spd->rpmb_interface); +} + +int mei_spd_rpmb_cmd_req(struct mei_spd *spd, u16 req, void *buf) +{ + struct rpmb_cmd cmd[3]; + struct rpmb_frame_jdec *frame_res = NULL; + u32 flags; + unsigned int i; + int ret; + + if (!spd->rdev) { + spd_err(spd, "RPMB not ready\n"); + return -ENODEV; + } + + i = 0; + flags = RPMB_F_WRITE; + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) + flags |= RPMB_F_REL_WRITE; + cmd[i].flags = flags; + cmd[i].nframes = 1; + cmd[i].frames = buf; + i++; + + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { + frame_res = kzalloc(sizeof(*frame_res), GFP_KERNEL); + if (!frame_res) + return -ENOMEM; + frame_res->req_resp = cpu_to_be16(RPMB_RESULT_READ); + cmd[i].flags = RPMB_F_WRITE; + cmd[i].nframes = 1; + cmd[i].frames = frame_res; + i++; + } + + cmd[i].flags = 0; + cmd[i].nframes = 1; + cmd[i].frames = buf; + i++; + + ret = rpmb_cmd_seq(spd->rdev, cmd, i); + if (ret) + spd_err(spd, "RPMB req failed ret = %d\n", ret); + + kfree(frame_res); + return ret; +} + +bool mei_spd_rpmb_is_open(struct mei_spd *spd) +{ + return !!spd->rdev; +} diff --git a/drivers/misc/mei/spd/spd.h b/drivers/misc/mei/spd/spd.h index cd30d0ed18ae..b919a5cb7a4c 100644 --- a/drivers/misc/mei/spd/spd.h +++ b/drivers/misc/mei/spd/spd.h @@ -7,6 +7,7 @@ #include #include +#include enum mei_spd_state { MEI_SPD_STATE_INIT, @@ -26,6 +27,8 @@ enum mei_spd_state { * @dev_type: storage device type * @dev_id_sz: device id size * @dev_id: device id string + * @rdev: RPMB device + * @rpmb_interface: gpp class interface for discovery * @lock: mutex to sync request processing * @state: driver state * @status_send_w: workitem for sending status to the FW @@ -41,6 +44,8 @@ struct mei_spd { u32 dev_type; u32 dev_id_sz; u8 *dev_id; + struct rpmb_dev *rdev; + struct class_interface rpmb_interface; struct mutex lock; /* mutex to sync request processing */ enum mei_spd_state state; struct work_struct status_send_w; @@ -66,6 +71,12 @@ void mei_spd_gpp_exit(struct mei_spd *spd); int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size); int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size); +void mei_spd_rpmb_prepare(struct mei_spd *spd); +bool mei_spd_rpmb_is_open(struct mei_spd *spd); +int mei_spd_rpmb_init(struct mei_spd *spd); +void mei_spd_rpmb_exit(struct mei_spd *spd); +int mei_spd_rpmb_cmd_req(struct mei_spd *spd, u16 req_type, void *buf); + #if IS_ENABLED(CONFIG_DEBUG_FS) int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name); void mei_spd_dbgfs_deregister(struct mei_spd *spd); From 272a271e82770ea6eb53ecbc52d1a3fe76003019 Mon Sep 17 00:00:00 2001 From: "Huang, Yang" Date: Tue, 24 Jul 2018 13:52:54 +0800 Subject: [PATCH 1017/1276] rpmb: add rpmb multiplexor kernel module This module owns RPMB authentication key in ACRN SOS kernel. It receives the requests from SOS/DM and replaces with real contents such as MAC calculated by real RPMB auth key. And it will forward the requests by calling RPMB kernel driver directly. Change-Id: I326f1a04b45a2bc450be4b120635489ec102a0e6 Signed-off-by: Huang, Yang Signed-off-by: Tomas Winkler --- drivers/char/rpmb/Kconfig | 2 + drivers/char/rpmb/Makefile | 2 + drivers/char/rpmb/mux/Kconfig | 14 + drivers/char/rpmb/mux/Makefile | 3 + drivers/char/rpmb/mux/mux.c | 742 +++++++++++++++++++++++++++++++++ 5 files changed, 763 insertions(+) create mode 100644 drivers/char/rpmb/mux/Kconfig create mode 100644 drivers/char/rpmb/mux/Makefile create mode 100644 drivers/char/rpmb/mux/mux.c diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index 48f11c19bbda..568ba9adb117 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -40,3 +40,5 @@ config VIRTIO_RPMB Say yes here if you want to access virtio RPMB from user space via character device interface /dev/vrpmb. This device interface is only for guest/frontend virtio driver. + +source "drivers/char/rpmb/mux/Kconfig" diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index 281c012712ca..20cf088d2476 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o obj-$(CONFIG_VIRTIO_RPMB) += virtio_rpmb.o ccflags-y += -D__CHECK_ENDIAN__ + +obj-$(CONFIG_RPMB_MUX) += mux/ diff --git a/drivers/char/rpmb/mux/Kconfig b/drivers/char/rpmb/mux/Kconfig new file mode 100644 index 000000000000..8087f9b797f8 --- /dev/null +++ b/drivers/char/rpmb/mux/Kconfig @@ -0,0 +1,14 @@ +config RPMB_MUX + tristate "RPMB Mux kernel module interface /dev/rpmbmux" + default n + select RPMB + select CRYPTO_SHA256 + select CRYPTO_HMAC + help + Say yes here if you want to access RPMB from user space + via character device interface /dev/rpmbmux, which is acted + as a multiplexor for RPMB by calling RPMB native driver directly. + + It owns RPMB authentication key internally for RPMB + virtualization usage.The users who don't own RPMB key + in such a RPMB virtualization use case could enable it. diff --git a/drivers/char/rpmb/mux/Makefile b/drivers/char/rpmb/mux/Makefile new file mode 100644 index 000000000000..7afc458a0dd2 --- /dev/null +++ b/drivers/char/rpmb/mux/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_RPMB_MUX) += mux.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/mux/mux.c b/drivers/char/rpmb/mux/mux.c new file mode 100644 index 000000000000..55901197d0a5 --- /dev/null +++ b/drivers/char/rpmb/mux/mux.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * RPMB Mux Kernel Module Driver + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct rpmb_mux_dev - device which can support RPMB partition + * @lock : the device lock + * @rdev : point to the rpmb device + * @cdev : character dev + * @rpmb_interface : rpmb class interface + * @write_counter : write counter of RPMB + * @wc_inited : write counter is initialized + * @rpmb_key : RPMB authentication key + * @hash_desc : hmac(sha256) shash descriptor + */ +struct rpmb_mux_dev { + struct mutex lock; /* device serialization lock */ + struct rpmb_dev *rdev; + struct cdev cdev; + struct class_interface rpmb_interface; + + u32 write_counter; + u32 wc_inited; + u8 rpmb_key[32]; + struct shash_desc *hash_desc; +}; + +static dev_t rpmb_mux_devt; +static struct rpmb_mux_dev *__mux_dev; +static struct class *rpmb_mux_class; +/* from MMC_IOC_MAX_CMDS */ +#define RPMB_MAX_FRAMES 255 + +static int rpmb_mux_open(struct inode *inode, struct file *fp) +{ + struct rpmb_mux_dev *mux_dev; + + mux_dev = container_of(inode->i_cdev, struct rpmb_mux_dev, cdev); + if (!mux_dev) + return -ENODEV; + + mutex_lock(&mux_dev->lock); + + fp->private_data = mux_dev; + + mutex_unlock(&mux_dev->lock); + + return nonseekable_open(inode, fp); +} + +static int rpmb_mux_release(struct inode *inode, struct file *fp) +{ + return 0; +} + +static int rpmb_key_retrieval(void *rpmb_key) +{ + /* hard code */ + memset(rpmb_key, 0x31, 32); + return 0; +} + +static int rpmb_mux_hmac_256_alloc(struct rpmb_mux_dev *mux_dev) +{ + struct shash_desc *desc; + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + mux_dev->hash_desc = desc; + + return 0; +} + +static void rpmb_mux_hmac_256_free(struct rpmb_mux_dev *mux_dev) +{ + struct shash_desc *desc = mux_dev->hash_desc; + + crypto_free_shash(desc->tfm); + kfree(desc); + + mux_dev->hash_desc = NULL; +} + +static int rpmb_mux_calc_hmac(struct rpmb_mux_dev *mux_dev, + struct rpmb_frame_jdec *frames, + unsigned int blks, u8 *mac) +{ + struct shash_desc *desc = mux_dev->hash_desc; + int ret; + unsigned int i; + + ret = crypto_shash_init(desc); + if (ret) + return ret; + + for (i = 0; i < blks; i++) { + ret = crypto_shash_update(desc, frames[i].data, + rpmb_jdec_hmac_data_len); + if (ret) + return ret; + } + + ret = crypto_shash_final(desc, mac); + + return ret; +} + +static int rpmb_program_key(struct rpmb_mux_dev *mux_dev) +{ + struct rpmb_frame_jdec *frame_write, *frame_rel, *frame_out; + struct rpmb_cmd *cmds; + int ret; + + frame_write = kzalloc(sizeof(*frame_write), GFP_KERNEL); + frame_rel = kzalloc(sizeof(*frame_rel), GFP_KERNEL); + frame_out = kzalloc(sizeof(*frame_out), GFP_KERNEL); + cmds = kcalloc(3, sizeof(*cmds), GFP_KERNEL); + if (!frame_write || !frame_rel || !frame_out || !cmds) { + ret = -ENOMEM; + goto out; + } + + /* fill rel write frame */ + memcpy(frame_rel->key_mac, mux_dev->rpmb_key, + sizeof(mux_dev->rpmb_key)); + frame_rel->req_resp = cpu_to_be16(RPMB_PROGRAM_KEY); + + /* fill write frame */ + frame_write->req_resp = cpu_to_be16(RPMB_RESULT_READ); + + /* fill io cmd */ + cmds[0].flags = RPMB_F_WRITE | RPMB_F_REL_WRITE; + cmds[0].nframes = 1; + cmds[0].frames = frame_rel; + cmds[1].flags = RPMB_F_WRITE; + cmds[1].nframes = 1; + cmds[1].frames = frame_write; + cmds[2].flags = 0; + cmds[2].nframes = 1; + cmds[2].frames = frame_out; + + ret = rpmb_cmd_seq(mux_dev->rdev, cmds, 3); + if (ret) + goto out; + + if (be16_to_cpu(frame_out->result) != RPMB_ERR_OK) { + ret = -EPERM; + dev_err(&mux_dev->rdev->dev, "rpmb program key failed(0x%X).\n", + be16_to_cpu(frame_out->result)); + } + +out: + kfree(frame_write); + kfree(frame_rel); + kfree(frame_out); + kfree(cmds); + + return ret; +} + +static int rpmb_get_counter(struct rpmb_mux_dev *mux_dev) +{ + struct rpmb_frame_jdec *in_frame, *out_frame; + struct rpmb_cmd *cmds; + int ret; + u8 mac[32]; + + in_frame = kzalloc(sizeof(*in_frame), GFP_KERNEL); + out_frame = kzalloc(sizeof(*out_frame), GFP_KERNEL); + cmds = kcalloc(2, sizeof(*cmds), GFP_KERNEL); + if (!in_frame || !out_frame || !cmds) { + ret = -ENOMEM; + goto out; + } + + in_frame->req_resp = cpu_to_be16(RPMB_GET_WRITE_COUNTER); + cmds[0].flags = RPMB_F_WRITE; + cmds[0].nframes = 1; + cmds[0].frames = in_frame; + cmds[1].flags = 0; + cmds[1].nframes = 1; + cmds[1].frames = out_frame; + + ret = rpmb_cmd_seq(mux_dev->rdev, cmds, 2); + if (ret) + goto out; + + ret = rpmb_mux_calc_hmac(mux_dev, out_frame, 1, mac); + if (ret) { + dev_err(&mux_dev->rdev->dev, "MAC calculation failed for read counter\n"); + goto out; + } + + if (memcmp(mac, out_frame->key_mac, sizeof(mac))) { + ret = -EPERM; + dev_err(&mux_dev->rdev->dev, "MAC check failed for read counter\n"); + goto out; + } + + if (be16_to_cpu(out_frame->result) == RPMB_ERR_NO_KEY) { + dev_dbg(&mux_dev->rdev->dev, "Start to program key...\n"); + ret = rpmb_program_key(mux_dev); + if (ret) + goto out; + } else if (be16_to_cpu(out_frame->result) != RPMB_ERR_OK) { + ret = -EPERM; + dev_err(&mux_dev->rdev->dev, "get rpmb counter failed(0x%X).\n", + be16_to_cpu(out_frame->result)); + goto out; + } + + mux_dev->write_counter = be32_to_cpu(out_frame->write_counter); + +out: + kfree(in_frame); + kfree(out_frame); + kfree(cmds); + + return ret; +} + +static size_t rpmb_ioc_frames_len(struct rpmb_dev *rdev, size_t nframes) +{ + return rpmb_ioc_frames_len_jdec(nframes); +} + +/** + * rpmb_mux_copy_from_user - copy rpmb command from the user space + * + * @rdev: rpmb device + * @cmd: internal cmd structure + * @ucmd: user space cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_mux_copy_from_user(struct rpmb_dev *rdev, + struct rpmb_cmd *cmd, + struct rpmb_ioc_cmd __user *ucmd) +{ + void *frames; + u64 frames_ptr; + + if (get_user(cmd->flags, &ucmd->flags)) + return -EFAULT; + + if (get_user(cmd->nframes, &ucmd->nframes)) + return -EFAULT; + + if (cmd->nframes > RPMB_MAX_FRAMES) + return -EOVERFLOW; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + frames = memdup_user(u64_to_user_ptr(frames_ptr), + rpmb_ioc_frames_len(rdev, cmd->nframes)); + if (IS_ERR(frames)) + return PTR_ERR(frames); + + cmd->frames = frames; + return 0; +} + +/** + * rpmb_mux_copy_to_user - copy rpmb command to the user space + * + * @rdev: rpmb device + * @ucmd: user space cmd structure + * @cmd: internal cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_mux_copy_to_user(struct rpmb_dev *rdev, + struct rpmb_ioc_cmd __user *ucmd, + struct rpmb_cmd *cmd) +{ + u64 frames_ptr; + + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + /* some archs have issues with 64bit get_user */ + if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, + rpmb_ioc_frames_len(rdev, cmd->nframes))) + return -EFAULT; + + return 0; +} + +static int rpmb_replace_write_frame(struct rpmb_mux_dev *mux_dev, + struct rpmb_cmd *cmds, u32 ncmd) +{ + u32 i; + u32 frame_cnt; + __be32 write_counter; + struct rpmb_frame_jdec *in_frames = cmds[0].frames; + + if (in_frames->req_resp != cpu_to_be16(RPMB_WRITE_DATA)) { + dev_err(&mux_dev->rdev->dev, "rpmb ioctl frame is unsupported(0x%X).\n", + in_frames->req_resp); + return -EINVAL; + } + + frame_cnt = cmds[0].nframes; + write_counter = cpu_to_be32(mux_dev->write_counter); + for (i = 0; i < frame_cnt; i++) + in_frames[i].write_counter = write_counter; + + if (rpmb_mux_calc_hmac(mux_dev, in_frames, frame_cnt, + in_frames[frame_cnt - 1].key_mac)) { + dev_err(&mux_dev->rdev->dev, "MAC calculation failed for rpmb write\n"); + return -ERANGE; + } + + return 0; +} + +static int rpmb_check_mac(struct rpmb_mux_dev *mux_dev, struct rpmb_cmd *cmds) +{ + u32 frame_cnt; + u8 mac[32]; + struct rpmb_frame_jdec *in_frames = cmds[0].frames; + + frame_cnt = cmds[0].nframes; + + if (rpmb_mux_calc_hmac(mux_dev, in_frames, frame_cnt, mac)) { + dev_err(&mux_dev->rdev->dev, "MAC calculation failed for rpmb write\n"); + return -ERANGE; + } + + if (memcmp(mac, in_frames[frame_cnt - 1].key_mac, sizeof(mac))) { + dev_err(&mux_dev->rdev->dev, "MAC check failed for write data\n"); + return -EPERM; + } + + return 0; +} + +static int rpmb_check_result(struct rpmb_mux_dev *mux_dev, + struct rpmb_cmd *cmds, u32 ncmd) +{ + struct rpmb_frame_jdec *out_frames = cmds[ncmd - 1].frames; + int ret; + + ret = rpmb_check_mac(mux_dev, cmds); + if (ret) { + dev_err(&mux_dev->rdev->dev, "rpmb check mac fail!\n"); + return ret; + } + + /* write retry */ + if (out_frames->result == cpu_to_be16(RPMB_ERR_COUNTER)) { + dev_err(&mux_dev->rdev->dev, "rpmb counter error, write retry!\n"); + memset(out_frames, 0, sizeof(*out_frames)); + + ret = rpmb_get_counter(mux_dev); + if (ret) { + dev_err(&mux_dev->rdev->dev, "rpmb_get_counter failed!\n"); + return ret; + } + + /* Since phy_counter has changed, + * so we have to generate mac again + */ + ret = rpmb_replace_write_frame(mux_dev, cmds, ncmd); + if (ret) { + dev_err(&mux_dev->rdev->dev, "rpmb replace write frame failed\n"); + return ret; + } + + ret = rpmb_cmd_seq(mux_dev->rdev, cmds, ncmd); + if (ret) { + dev_err(&mux_dev->rdev->dev, "rpmb write retry failed\n"); + return ret; + } + + ret = rpmb_check_mac(mux_dev, cmds); + if (ret) { + dev_err(&mux_dev->rdev->dev, "write retry rpmb check mac fail!\n"); + return ret; + } + } + + if (out_frames->result == cpu_to_be16(RPMB_ERR_OK)) { + dev_dbg(&mux_dev->rdev->dev, "write_counter =%d\n", + mux_dev->write_counter); + mux_dev->write_counter++; + } else { + dev_err(&mux_dev->rdev->dev, "ERR result is 0x%X.\n", + be16_to_cpu(out_frames->result)); + } + + return 0; +} + +/** + * rpmb_ioctl_seq_cmd() - issue an rpmb command sequence + * @mux_dev: rpmb mux_device + * @ptr: rpmb cmd sequence + * + * RPMB_IOC_SEQ_CMD handler + * + * Return: 0 on success, <0 on error + */ +static long rpmb_ioctl_seq_cmd(struct rpmb_mux_dev *mux_dev, + struct rpmb_ioc_seq_cmd __user *ptr) +{ + struct rpmb_dev *rdev = mux_dev->rdev; + __u64 ncmds; + struct rpmb_cmd *cmds; + struct rpmb_ioc_cmd __user *ucmds; + unsigned int i; + int ret; + + /* The caller must have CAP_SYS_RAWIO, like mmc ioctl */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&ncmds, &ptr->num_of_cmds, sizeof(ncmds))) + return -EFAULT; + + if (ncmds > 3) { + dev_err(&rdev->dev, "supporting up to 3 packets (%llu)\n", + ncmds); + return -EINVAL; + } + + cmds = kcalloc(ncmds, sizeof(*cmds), GFP_KERNEL); + if (!cmds) + return -ENOMEM; + + ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; + for (i = 0; i < ncmds; i++) { + ret = rpmb_mux_copy_from_user(rdev, &cmds[i], &ucmds[i]); + if (ret) + goto out; + } + + if (cmds->flags & RPMB_F_REL_WRITE) { + ret = rpmb_replace_write_frame(mux_dev, cmds, ncmds); + if (ret) + goto out; + } + + ret = rpmb_cmd_seq(rdev, cmds, ncmds); + if (ret) + goto out; + + if (cmds->flags & RPMB_F_REL_WRITE) { + ret = rpmb_check_result(mux_dev, cmds, ncmds); + if (ret) + goto out; + } + + for (i = 0; i < ncmds; i++) { + ret = rpmb_mux_copy_to_user(rdev, &ucmds[i], &cmds[i]); + if (ret) + goto out; + } + +out: + for (i = 0; i < ncmds; i++) + kfree(cmds[i].frames); + kfree(cmds); + + return ret; +} + +static long rpmb_mux_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + long ret; + struct rpmb_mux_dev *mux_dev = fp->private_data; + void __user *ptr = (void __user *)arg; + + mutex_lock(&mux_dev->lock); + + if (!mux_dev->rdev) { + pr_err("rpmb dev is NULL!\n"); + ret = -EINVAL; + goto out; + } + + if (!mux_dev->wc_inited) { + ret = rpmb_get_counter(mux_dev); + if (ret) { + dev_err(&mux_dev->rdev->dev, + "init counter failed = %ld\n", ret); + goto out; + } + + mux_dev->wc_inited = true; + } + + switch (cmd) { + case RPMB_IOC_SEQ_CMD: + ret = rpmb_ioctl_seq_cmd(mux_dev, ptr); + break; + default: + dev_err(&mux_dev->rdev->dev, "unsupport:0x%X!!!\n", cmd); + ret = -ENOIOCTLCMD; + } + +out: + mutex_unlock(&mux_dev->lock); + + return ret; +} + +static int rpmb_mux_start(struct rpmb_mux_dev *mux_dev, struct rpmb_dev *rdev) +{ + if (mux_dev->rdev == rdev) + return 0; + + if (mux_dev->rdev) { + dev_err(&rdev->dev, "rpmb device already registered\n"); + return -EEXIST; + } + + mux_dev->rdev = rpmb_dev_get(rdev); + dev_dbg(&rdev->dev, "rpmb partition created\n"); + return 0; +} + +static int rpmb_mux_stop(struct rpmb_mux_dev *mux_dev, struct rpmb_dev *rdev) +{ + if (!mux_dev->rdev) { + dev_err(&rdev->dev, "Already stopped\n"); + return -EPROTO; + } + + if (rdev && mux_dev->rdev != rdev) { + dev_err(&rdev->dev, "Wrong RPMB on stop\n"); + return -EINVAL; + } + + rpmb_dev_put(mux_dev->rdev); + mux_dev->rdev = NULL; + + dev_dbg(&rdev->dev, "rpmb partition removed\n"); + return 0; +} + +static int rpmb_add_device(struct device *dev, struct class_interface *intf) +{ + struct rpmb_mux_dev *mux_dev; + struct rpmb_dev *rdev = to_rpmb_dev(dev); + int ret; + + mux_dev = container_of(intf, struct rpmb_mux_dev, rpmb_interface); + + if (!rdev->ops) + return -EINVAL; + + if (rdev->ops->type != RPMB_TYPE_EMMC) { + dev_err(&rdev->dev, "support RPMB_TYPE_EMMC only.\n"); + return -ENOENT; + } + + mutex_lock(&mux_dev->lock); + + ret = rpmb_mux_start(mux_dev, rdev); + if (ret) { + dev_err(&rdev->dev, "fail in rpmb_mux_start.\n"); + mutex_unlock(&mux_dev->lock); + return ret; + } + + mutex_unlock(&mux_dev->lock); + + return 0; +} + +static void rpmb_remove_device(struct device *dev, struct class_interface *intf) +{ + struct rpmb_mux_dev *mux_dev; + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + mux_dev = container_of(intf, struct rpmb_mux_dev, rpmb_interface); + + mutex_lock(&mux_dev->lock); + if (rpmb_mux_stop(mux_dev, rdev)) + dev_err(&rdev->dev, "fail in rpmb_mux_stop.\n"); + mutex_unlock(&mux_dev->lock); +} + +#ifdef CONFIG_COMPAT +static long rpmb_mux_compat_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + return rpmb_mux_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations rpmb_mux_fops = { + .open = rpmb_mux_open, + .release = rpmb_mux_release, + .unlocked_ioctl = rpmb_mux_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rpmb_mux_compat_ioctl, +#endif + .llseek = noop_llseek, + .owner = THIS_MODULE, +}; + +static int __init rpmb_mux_init(void) +{ + int ret; + struct device *class_dev; + struct rpmb_mux_dev *mux_dev; + + ret = alloc_chrdev_region(&rpmb_mux_devt, 0, MINORMASK, "rpmbmux"); + if (ret < 0) { + pr_err("unable to allocate char dev region\n"); + return ret; + } + + mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL); + if (!mux_dev) { + ret = -ENOMEM; + goto err_kzalloc; + } + __mux_dev = mux_dev; + + cdev_init(&mux_dev->cdev, &rpmb_mux_fops); + mux_dev->cdev.owner = THIS_MODULE; + ret = cdev_add(&mux_dev->cdev, rpmb_mux_devt, 1); + if (ret) { + pr_err("unable to cdev_add.\n"); + goto err_cdev_add; + } + + rpmb_mux_class = class_create(THIS_MODULE, "rpmbmux"); + if (IS_ERR(rpmb_mux_class)) { + ret = PTR_ERR(rpmb_mux_class); + goto err_class_create; + } + + class_dev = device_create(rpmb_mux_class, NULL, + rpmb_mux_devt, mux_dev, "rpmbmux"); + if (IS_ERR(class_dev)) { + pr_err("failed to device_create!!!\n"); + ret = PTR_ERR(class_dev); + goto err_device_create; + } + + ret = rpmb_mux_hmac_256_alloc(mux_dev); + if (ret) { + pr_err("failed to set rpmb_mux_hmac_256_alloc.\n"); + goto err_rpmb_mux_hmac_256_alloc; + } + + ret = rpmb_key_retrieval(mux_dev->rpmb_key); + if (ret) { + pr_err("rpmb_key_retrieval failed.\n"); + goto err_rpmb_key_retrieval; + } + + ret = crypto_shash_setkey(mux_dev->hash_desc->tfm, + mux_dev->rpmb_key, 32); + if (ret) { + pr_err("set key failed = %d\n", ret); + goto err_crypto_shash_setkey; + } + + mux_dev->rpmb_interface.add_dev = rpmb_add_device; + mux_dev->rpmb_interface.remove_dev = rpmb_remove_device; + mux_dev->rpmb_interface.class = &rpmb_class; + + ret = class_interface_register(&mux_dev->rpmb_interface); + if (ret) { + pr_err("Can't register interface\n"); + goto err_class_interface_register; + } + + return 0; + +err_class_interface_register: +err_crypto_shash_setkey: + memset(mux_dev->rpmb_key, 0, sizeof(mux_dev->rpmb_key)); +err_rpmb_key_retrieval: + rpmb_mux_hmac_256_free(mux_dev); +err_rpmb_mux_hmac_256_alloc: + device_destroy(rpmb_mux_class, rpmb_mux_devt); +err_device_create: + class_destroy(rpmb_mux_class); +err_class_create: + cdev_del(&mux_dev->cdev); +err_cdev_add: + kfree(mux_dev); +err_kzalloc: + unregister_chrdev_region(rpmb_mux_devt, 0); + return ret; +} + +static void __exit rpmb_mux_exit(void) +{ + struct rpmb_mux_dev *mux_dev = __mux_dev; + + class_interface_unregister(&mux_dev->rpmb_interface); + device_destroy(rpmb_mux_class, rpmb_mux_devt); + class_destroy(rpmb_mux_class); + cdev_del(&mux_dev->cdev); + unregister_chrdev_region(rpmb_mux_devt, 0); + + rpmb_mux_hmac_256_free(mux_dev); + memset(mux_dev->rpmb_key, 0, sizeof(mux_dev->rpmb_key)); + kfree(mux_dev); +} + +module_init(rpmb_mux_init); +module_exit(rpmb_mux_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("RPMB Mux kernel module"); +MODULE_LICENSE("Dual BSD/GPL"); From 6cdb226b483845b490aeac8faa291d653ab54907 Mon Sep 17 00:00:00 2001 From: Manisha Chinthapally Date: Tue, 23 Oct 2018 13:46:26 -0700 Subject: [PATCH 1018/1276] Add support for hypercalls for sep and socwatch tool support This patch has following changes New sbuf type for each tool is added New hypercall for profiling operations [HC_PROFILING_OPS] added List of profiling commands supported is added Tracked-On: projectacrn#1409 Acked-by: Eddie Dong Signed-off-by: Manisha Chinthapally --- drivers/acrn/sbuf.h | 2 ++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 12 ++++++++++++ include/linux/vhm/vhm_hypercall.h | 1 + 4 files changed, 20 insertions(+) diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h index 4fae7a258bce..d08bf9fedf70 100644 --- a/drivers/acrn/sbuf.h +++ b/drivers/acrn/sbuf.h @@ -70,6 +70,8 @@ enum sbuf_type { ACRN_TRACE, ACRN_HVLOG, + ACRN_SEP, + ACRN_SOCWATCH, ACRN_SBUF_TYPE_MAX, }; /** diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index f3348f7e3903..6c67d04dd77e 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -183,6 +183,11 @@ inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr) return acrn_hypercall2(HC_VM_GPA2HPA, vmid, addr); } +inline long hcall_profiling_ops(unsigned long cmd, unsigned long msr_nodes) +{ + return acrn_hypercall2(HC_PROFILING_OPS, cmd, msr_nodes); +} + inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr) { return acrn_hypercall2(HC_VM_INTR_MONITOR, vmid, addr); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index c13257fcaaa1..d9160221f657 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -117,6 +117,7 @@ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) #define HC_SETUP_HV_NPK_LOG _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x01) +#define HC_PROFILING_OPS _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x02) /* Power management */ #define HC_ID_PM_BASE 0x80UL @@ -247,4 +248,15 @@ struct hc_api_version { uint32_t minor_version; } __attribute__((aligned(8))); + +enum profiling_cmd_type { + PROFILING_MSR_OPS = 0, + PROFILING_GET_VMINFO, + PROFILING_GET_VERSION, + PROFILING_GET_CONTROL_SWITCH, + PROFILING_SET_CONTROL_SWITCH, + PROFILING_CONFIG_PMI, + PROFILING_CONFIG_VMSWITCH, + PROFILING_GET_PCPUID, +}; #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 130069926786..2c79bf3df6b4 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -170,5 +170,6 @@ inline long hcall_reset_ptdev_intr_info(unsigned long vmid, inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi); inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr); inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr); +inline long hcall_profiling_ops(unsigned long cmd, unsigned long msr_nodes); #endif /* VHM_HYPERCALL_H */ From ec3e9ab7b624defe3b78db0be96102412caabaea Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 1019/1276] Kernel/VHM: Rename acpi_generic_address in acrn_common.h to avoid redefinition Currently the acpi_generic_address is defined in acrn_common.h. And it is also defined in include/linux/acpi.h. If the two files are included by one driver, it will complain the redefinition of acpi_generic_address. So it is renamed to avoid the redefinition conflict. Signed-off-by: Zhao Yakui --- include/linux/vhm/acrn_common.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 69499245a994..179957d273e9 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -481,7 +481,7 @@ struct acrn_vm_pci_msix_remap { #define SPACE_PLATFORM_COMM 10 #define SPACE_FFixedHW 0x7F -struct acpi_generic_address { +struct acrn_generic_address { uint8_t space_id; uint8_t bit_width; uint8_t bit_offset; @@ -490,7 +490,7 @@ struct acpi_generic_address { } __attribute__((aligned(8))); struct cpu_cx_data { - struct acpi_generic_address cx_reg; + struct acrn_generic_address cx_reg; uint8_t type; uint32_t latency; uint64_t power; @@ -512,10 +512,10 @@ struct acpi_sstate_pkg { } __attribute__((aligned(8))); struct acpi_sstate_data { - struct acpi_generic_address pm1a_evt; - struct acpi_generic_address pm1b_evt; - struct acpi_generic_address pm1a_cnt; - struct acpi_generic_address pm1b_cnt; + struct acrn_generic_address pm1a_evt; + struct acrn_generic_address pm1b_evt; + struct acrn_generic_address pm1a_cnt; + struct acrn_generic_address pm1b_cnt; struct acpi_sstate_pkg s3_pkg; struct acpi_sstate_pkg s5_pkg; uint32_t *wake_vector_32; From 26dfd3e93c46fe7f584d56cf689cc7ab124fe803 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 1020/1276] drm/i915/gvt: some changes to support xengt/acrngt Set guest ppgtt entry in ppgtt_handle_guest_write_page_table_bytes() Change-Id: Ic6e46561f58eec1a17fb8d5bd9c5dd76d32b5350 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gtt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 00aad8164dec..6262308c4a78 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1688,6 +1688,8 @@ static int ppgtt_handle_guest_write_page_table_bytes( index = (pa & (PAGE_SIZE - 1)) >> info->gtt_entry_size_shift; + /* Set guest ppgtt entry. Optional for KVMGT, but MUST for XENGT. */ + intel_gvt_hypervisor_write_gpa(vgpu, pa, p_data, bytes); ppgtt_get_guest_entry(spt, &we, index); /* From 9a80649c93ea31343a164e1e622a69a4bec18c12 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 1021/1276] drm/i915/gvt: Refactored BXT plane registers Refactored BXT plane registers and implemented their plane register handlers. Signed-off-by: Min He Change-Id: Id72f6aa11db332008b73ce4ad1069386b9b81f35 Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 283 ++++++++++++--------------- drivers/gpu/drm/i915/gvt/interrupt.c | 4 + drivers/gpu/drm/i915/gvt/interrupt.h | 3 + drivers/gpu/drm/i915/gvt/reg.h | 7 +- 4 files changed, 133 insertions(+), 164 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 94c1089ecf59..e89d228ddc58 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1959,71 +1959,71 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x70098), D_ALL); MMIO_D(_MMIO(0x7009c), D_ALL); - MMIO_D(DSPCNTR(PIPE_A), D_ALL); - MMIO_D(DSPADDR(PIPE_A), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_A), D_ALL); - MMIO_D(DSPPOS(PIPE_A), D_ALL); - MMIO_D(DSPSIZE(PIPE_A), D_ALL); - MMIO_DH(DSPSURF(PIPE_A), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_A), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_A), D_ALL); - - MMIO_D(DSPCNTR(PIPE_B), D_ALL); - MMIO_D(DSPADDR(PIPE_B), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_B), D_ALL); - MMIO_D(DSPPOS(PIPE_B), D_ALL); - MMIO_D(DSPSIZE(PIPE_B), D_ALL); - MMIO_DH(DSPSURF(PIPE_B), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_B), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_B), D_ALL); - - MMIO_D(DSPCNTR(PIPE_C), D_ALL); - MMIO_D(DSPADDR(PIPE_C), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_C), D_ALL); - MMIO_D(DSPPOS(PIPE_C), D_ALL); - MMIO_D(DSPSIZE(PIPE_C), D_ALL); - MMIO_DH(DSPSURF(PIPE_C), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_C), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_C), D_ALL); - - MMIO_D(SPRCTL(PIPE_A), D_ALL); - MMIO_D(SPRLINOFF(PIPE_A), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_A), D_ALL); - MMIO_D(SPRPOS(PIPE_A), D_ALL); - MMIO_D(SPRSIZE(PIPE_A), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_A), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_A), D_ALL); - MMIO_DH(SPRSURF(PIPE_A), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_A), D_ALL); - MMIO_D(SPROFFSET(PIPE_A), D_ALL); - MMIO_D(SPRSCALE(PIPE_A), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_A), D_ALL); - - MMIO_D(SPRCTL(PIPE_B), D_ALL); - MMIO_D(SPRLINOFF(PIPE_B), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_B), D_ALL); - MMIO_D(SPRPOS(PIPE_B), D_ALL); - MMIO_D(SPRSIZE(PIPE_B), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_B), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_B), D_ALL); - MMIO_DH(SPRSURF(PIPE_B), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_B), D_ALL); - MMIO_D(SPROFFSET(PIPE_B), D_ALL); - MMIO_D(SPRSCALE(PIPE_B), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_B), D_ALL); - - MMIO_D(SPRCTL(PIPE_C), D_ALL); - MMIO_D(SPRLINOFF(PIPE_C), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_C), D_ALL); - MMIO_D(SPRPOS(PIPE_C), D_ALL); - MMIO_D(SPRSIZE(PIPE_C), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_C), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_C), D_ALL); - MMIO_DH(SPRSURF(PIPE_C), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_C), D_ALL); - MMIO_D(SPROFFSET(PIPE_C), D_ALL); - MMIO_D(SPRSCALE(PIPE_C), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_C), D_ALL); + MMIO_D(DSPCNTR(PIPE_A), D_BDW); + MMIO_D(DSPADDR(PIPE_A), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_A), D_BDW); + MMIO_D(DSPPOS(PIPE_A), D_BDW); + MMIO_D(DSPSIZE(PIPE_A), D_BDW); + MMIO_DH(DSPSURF(PIPE_A), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_A), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_A), D_BDW); + + MMIO_D(DSPCNTR(PIPE_B), D_BDW); + MMIO_D(DSPADDR(PIPE_B), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_B), D_BDW); + MMIO_D(DSPPOS(PIPE_B), D_BDW); + MMIO_D(DSPSIZE(PIPE_B), D_BDW); + MMIO_DH(DSPSURF(PIPE_B), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_B), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_B), D_BDW); + + MMIO_D(DSPCNTR(PIPE_C), D_BDW); + MMIO_D(DSPADDR(PIPE_C), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_C), D_BDW); + MMIO_D(DSPPOS(PIPE_C), D_BDW); + MMIO_D(DSPSIZE(PIPE_C), D_BDW); + MMIO_DH(DSPSURF(PIPE_C), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_C), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_C), D_BDW); + + MMIO_D(SPRCTL(PIPE_A), D_BDW); + MMIO_D(SPRLINOFF(PIPE_A), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_A), D_BDW); + MMIO_D(SPRPOS(PIPE_A), D_BDW); + MMIO_D(SPRSIZE(PIPE_A), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_A), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_A), D_BDW); + MMIO_DH(SPRSURF(PIPE_A), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_A), D_BDW); + MMIO_D(SPROFFSET(PIPE_A), D_BDW); + MMIO_D(SPRSCALE(PIPE_A), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_A), D_BDW); + + MMIO_D(SPRCTL(PIPE_B), D_BDW); + MMIO_D(SPRLINOFF(PIPE_B), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_B), D_BDW); + MMIO_D(SPRPOS(PIPE_B), D_BDW); + MMIO_D(SPRSIZE(PIPE_B), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_B), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_B), D_BDW); + MMIO_DH(SPRSURF(PIPE_B), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_B), D_BDW); + MMIO_D(SPROFFSET(PIPE_B), D_BDW); + MMIO_D(SPRSCALE(PIPE_B), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_B), D_BDW); + + MMIO_D(SPRCTL(PIPE_C), D_BDW); + MMIO_D(SPRLINOFF(PIPE_C), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_C), D_BDW); + MMIO_D(SPRPOS(PIPE_C), D_BDW); + MMIO_D(SPRSIZE(PIPE_C), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_C), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_C), D_BDW); + MMIO_DH(SPRSURF(PIPE_C), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_C), D_BDW); + MMIO_D(SPROFFSET(PIPE_C), D_BDW); + MMIO_D(SPRSCALE(PIPE_C), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_C), D_BDW); MMIO_D(HTOTAL(TRANSCODER_A), D_ALL); MMIO_D(HBLANK(TRANSCODER_A), D_ALL); @@ -2804,6 +2804,46 @@ static int init_broadwell_mmio_info(struct intel_gvt *gvt) return 0; } +static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); + int flip_event = SKL_FLIP_EVENT(pipe, plane); + + write_vreg(vgpu, offset, p_data, bytes); + vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); + + set_bit(flip_event, vgpu->irq.flip_done_event[pipe]); + return 0; +} + +static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + write_vreg(vgpu, offset, p_data, bytes); + return 0; +} + +#define MMIO_PIPES_SDH(prefix, plane, s, d, r, w) do { \ + int pipe; \ + for_each_pipe(dev_priv, pipe) \ + MMIO_F(prefix(pipe, plane), s, 0, 0, 0, d, r, w); \ +} while (0) + +#define MMIO_PLANES_SDH(prefix, s, d, r, w) do { \ + int pipe, plane; \ + for_each_pipe(dev_priv, pipe) \ + for_each_universal_plane(dev_priv, pipe, plane) \ + MMIO_F(prefix(pipe, plane), s, 0, 0, 0, d, r, w); \ +} while (0) + +#define MMIO_PLANES_DH(prefix, d, r, w) \ + MMIO_PLANES_SDH(prefix, 4, d, r, w) + +#define PLANE_WM_BASE(pipe, plane) _MMIO(_PLANE_WM_BASE(pipe, plane)) + static int init_skl_mmio_info(struct intel_gvt *gvt) { struct drm_i915_private *dev_priv = gvt->dev_priv; @@ -2875,108 +2915,37 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_CTL, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_STRIDE, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_POS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_SIZE, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_KEYVAL, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_KEYMSK, D_SKL_PLUS, NULL, skl_plane_mmio_write); + + MMIO_PLANES_DH(PLANE_SURF, D_SKL_PLUS, NULL, skl_plane_surf_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_KEYMAX, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_A), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_B), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_C), D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - - MMIO_F(PLANE_WM(PIPE_B, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_B, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_B, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - - MMIO_F(PLANE_WM(PIPE_C, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_C, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_C, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(CUR_WM(PIPE_A, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); MMIO_F(CUR_WM(PIPE_B, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); MMIO_F(CUR_WM(PIPE_C, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(CUR_WM_TRANS(PIPE_A), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_WM_TRANS(PIPE_B), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_WM_TRANS(PIPE_C), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_D(_MMIO(0x70380), D_SKL_PLUS); - MMIO_D(_MMIO(0x71380), D_SKL_PLUS); - MMIO_D(_MMIO(0x72380), D_SKL_PLUS); - MMIO_D(_MMIO(0x7239c), D_SKL_PLUS); - MMIO_D(_MMIO(0x7039c), D_SKL_PLUS); - MMIO_D(_MMIO(0x8f074), D_SKL_PLUS); MMIO_D(_MMIO(0x8f004), D_SKL_PLUS); MMIO_D(_MMIO(0x8f034), D_SKL_PLUS); @@ -3031,16 +3000,6 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x71034), D_SKL_PLUS); MMIO_D(_MMIO(0x72034), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(0x44500), D_SKL_PLUS); MMIO_DFH(GEN9_CSFE_CHICKEN1_RCS, D_SKL_PLUS, F_CMD_ACCESS, NULL, NULL); MMIO_DFH(GEN8_HDC_CHICKEN1, D_SKL_PLUS, F_MODE_MASK | F_CMD_ACCESS, diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index 5daa23ae566b..d749d46bc05b 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -595,6 +595,10 @@ static void gen8_init_irq( SET_BIT_INFO(irq, 4, SPRITE_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); SET_BIT_INFO(irq, 4, SPRITE_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); SET_BIT_INFO(irq, 4, SPRITE_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); + + SET_BIT_INFO(irq, 5, PLANE_3_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); + SET_BIT_INFO(irq, 5, PLANE_3_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); + SET_BIT_INFO(irq, 5, PLANE_3_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); } /* GEN8 interrupt PCU events */ diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index 5313fb1b33e1..f7d7ade4f13c 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -92,6 +92,9 @@ enum intel_gvt_event_type { SPRITE_A_FLIP_DONE, SPRITE_B_FLIP_DONE, SPRITE_C_FLIP_DONE, + PLANE_3_A_FLIP_DONE, + PLANE_3_B_FLIP_DONE, + PLANE_3_C_FLIP_DONE, PCU_THERMAL, PCU_PCODE2DRIVER_MAILBOX, diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index d4f7ce6dc1d7..d05c5516a472 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -57,8 +57,11 @@ #define VGT_SPRSTRIDE(pipe) _PIPE(pipe, _SPRA_STRIDE, _PLANE_STRIDE_2_B) -#define _REG_701C0(pipe, plane) (0x701c0 + pipe * 0x1000 + (plane - 1) * 0x100) -#define _REG_701C4(pipe, plane) (0x701c4 + pipe * 0x1000 + (plane - 1) * 0x100) +#define _REG_701AC(pipe, plane) (0x701ac + pipe * 0x1000 + plane * 0x100) + +#define SKL_PLANE_REG_TO_PIPE(reg) (((reg) >> 12) & 0x3) +#define SKL_PLANE_REG_TO_PLANE(reg) ((((reg) & 0xFFF) - 0x180) >> 8) +#define SKL_FLIP_EVENT(pipe, plane) (PRIMARY_A_FLIP_DONE + (plane)*3 + pipe) #define GFX_MODE_BIT_SET_IN_MASK(val, bit) \ ((((bit) & 0xffff0000) == 0) && !!((val) & (((bit) << 16)))) From acbf7a86a1879d809b119170f1d96cdc71c998e0 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 1022/1276] drm/i915/gvt: passthru PIPE_DSL regiser to guest Change-Id: I4d903a982052b8b241f090a91e2251d66bba2778 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index e89d228ddc58..7f7f2fb09a37 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -530,6 +530,14 @@ static int force_nonpriv_write(struct intel_vgpu *vgpu, return 0; } +static int pipe_dsl_mmio_read(struct intel_vgpu *vgpu, + unsigned int offset, void *p_data, unsigned int bytes) +{ + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + vgpu_vreg(vgpu, offset) = I915_READ(_MMIO(offset)); + return intel_vgpu_default_mmio_read(vgpu, offset, p_data, bytes); +} + static int ddi_buf_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -1909,9 +1917,9 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0xc4040), D_ALL); MMIO_D(DERRMR, D_ALL); - MMIO_D(PIPEDSL(PIPE_A), D_ALL); - MMIO_D(PIPEDSL(PIPE_B), D_ALL); - MMIO_D(PIPEDSL(PIPE_C), D_ALL); + MMIO_DH(PIPEDSL(PIPE_A), D_ALL, pipe_dsl_mmio_read, NULL); + MMIO_DH(PIPEDSL(PIPE_B), D_ALL, pipe_dsl_mmio_read, NULL); + MMIO_DH(PIPEDSL(PIPE_C), D_ALL, pipe_dsl_mmio_read, NULL); MMIO_D(PIPEDSL(_PIPE_EDP), D_ALL); MMIO_DH(PIPECONF(PIPE_A), D_ALL, NULL, pipeconf_mmio_write); From 60ad6019da56dcd80b902ad74b9864552e4f6681 Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 19 Dec 2017 10:22:34 +0800 Subject: [PATCH 1023/1276] drm/i915/gvt: local display support Added local display dispaly support for GVT-g. Signed-off-by: Min He Change-Id: I430fbf98318cd35f7104657c4d1c128e50f56f2d Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/cfg_space.c | 9 +++ drivers/gpu/drm/i915/gvt/display.c | 100 ++++++++++++++++++++++++--- drivers/gpu/drm/i915/gvt/edid.c | 20 +++--- drivers/gpu/drm/i915/gvt/edid.h | 2 +- drivers/gpu/drm/i915/gvt/gvt.c | 43 ++++++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 13 ++++ drivers/gpu/drm/i915/gvt/handlers.c | 33 ++++++--- drivers/gpu/drm/i915/i915_irq.c | 25 ++++++- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_pm.c | 17 +++++ drivers/gpu/drm/i915/intel_sprite.c | 15 ++++ 12 files changed, 251 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index c62346fdc05d..707b0a50da3c 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -322,6 +322,15 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, case INTEL_GVT_PCI_OPREGION: if (WARN_ON(!IS_ALIGNED(offset, 4))) return -EINVAL; + + /* + * To support virtual display, we need to override the real VBT in the + * OpRegion. So here we don't report OpRegion to guest. + */ + if (IS_BROXTON(vgpu->gvt->dev_priv) || + IS_KABYLAKE(vgpu->gvt->dev_priv)) + return 0; + ret = intel_vgpu_opregion_base_write_handler(vgpu, *(u32 *)p_data); if (ret) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 3019dbc39aef..2e01c38887bd 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -315,14 +315,19 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num) } static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, - int type, unsigned int resolution) + int type, unsigned int resolution, void *edid) { struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num); + int valid_extensions = 1; + struct edid *tmp_edid = NULL; if (WARN_ON(resolution >= GVT_EDID_NUM)) return -EINVAL; - port->edid = kzalloc(sizeof(*(port->edid)), GFP_KERNEL); + if (edid) + valid_extensions += ((struct edid *)edid)->extensions; + port->edid = kzalloc(sizeof(*(port->edid)) + + valid_extensions * EDID_SIZE, GFP_KERNEL); if (!port->edid) return -ENOMEM; @@ -332,8 +337,23 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, return -ENOMEM; } - memcpy(port->edid->edid_block, virtual_dp_monitor_edid[resolution], - EDID_SIZE); + if (edid) + memcpy(port->edid->edid_block, edid, EDID_SIZE * valid_extensions); + else + memcpy(port->edid->edid_block, virtual_dp_monitor_edid[resolution], + EDID_SIZE); + + /* Sometimes the physical display will report the EDID with no + * digital bit set, which will cause the guest fail to enumerate + * the virtual HDMI monitor. So here we will set the digital + * bit and re-calculate the checksum. + */ + tmp_edid = ((struct edid *)port->edid->edid_block); + if (!(tmp_edid->input & DRM_EDID_INPUT_DIGITAL)) { + tmp_edid->input += DRM_EDID_INPUT_DIGITAL; + tmp_edid->checksum -= DRM_EDID_INPUT_DIGITAL; + } + port->edid->data_valid = true; memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); @@ -442,6 +462,66 @@ void intel_gvt_emulate_vblank(struct intel_gvt *gvt) mutex_unlock(&gvt->lock); } +static void intel_gvt_vblank_work(struct work_struct *w) +{ + struct intel_gvt_pipe_info *pipe_info = container_of(w, + struct intel_gvt_pipe_info, vblank_work); + struct intel_gvt *gvt = pipe_info->gvt; + struct intel_vgpu *vgpu; + int id; + + mutex_lock(&gvt->lock); + for_each_active_vgpu(gvt, vgpu, id) + emulate_vblank_on_pipe(vgpu, pipe_info->pipe_num); + mutex_unlock(&gvt->lock); +} + +void intel_gvt_init_pipe_info(struct intel_gvt *gvt) +{ + int pipe; + + for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) { + gvt->pipe_info[pipe].pipe_num = pipe; + gvt->pipe_info[pipe].gvt = gvt; + INIT_WORK(&gvt->pipe_info[pipe].vblank_work, + intel_gvt_vblank_work); + } +} + +int setup_virtual_monitors(struct intel_vgpu *vgpu) +{ + struct intel_connector *connector = NULL; + struct drm_connector_list_iter conn_iter; + int pipe = 0; + int ret = 0; + + drm_connector_list_iter_begin(&vgpu->gvt->dev_priv->drm, &conn_iter); + for_each_intel_connector_iter(connector, &conn_iter) { + if (connector->encoder->get_hw_state(connector->encoder, &pipe) + && connector->detect_edid) { + ret = setup_virtual_dp_monitor(vgpu, pipe, + GVT_DP_A + pipe, 0, + connector->detect_edid); + if (ret) + return ret; + } + } + drm_connector_list_iter_end(&conn_iter); + return 0; +} + +void clean_virtual_monitors(struct intel_vgpu *vgpu) +{ + int port = 0; + + for (port = PORT_A; port < I915_MAX_PORTS; port++) { + struct intel_vgpu_port *p = intel_vgpu_port(vgpu, port); + + if (p->edid) + clean_virtual_dp_monitor(vgpu, port); + } +} + /** * intel_vgpu_clean_display - clean vGPU virtual display emulation * @vgpu: a vGPU @@ -453,7 +533,9 @@ void intel_vgpu_clean_display(struct intel_vgpu *vgpu) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) + clean_virtual_monitors(vgpu); + else if (IS_SKYLAKE(dev_priv)) clean_virtual_dp_monitor(vgpu, PORT_D); else clean_virtual_dp_monitor(vgpu, PORT_B); @@ -475,12 +557,14 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution) intel_vgpu_init_i2c_edid(vgpu); - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) + return setup_virtual_monitors(vgpu); + else if (IS_SKYLAKE(dev_priv)) return setup_virtual_dp_monitor(vgpu, PORT_D, GVT_DP_D, - resolution); + resolution, NULL); else return setup_virtual_dp_monitor(vgpu, PORT_B, GVT_DP_B, - resolution); + resolution, NULL); } /** diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 4b98539025c5..4785b8a10619 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -55,10 +55,6 @@ static unsigned char edid_get_byte(struct intel_vgpu *vgpu) gvt_vgpu_err("Driver tries to read EDID without proper sequence!\n"); return 0; } - if (edid->current_edid_read >= EDID_SIZE) { - gvt_vgpu_err("edid_get_byte() exceeds the size of EDID!\n"); - return 0; - } if (!edid->edid_available) { gvt_vgpu_err("Reading EDID but EDID is not available!\n"); @@ -452,6 +448,8 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, u32 value = *(u32 *)p_data; int aux_data_for_write = 0; int reg = get_aux_ch_reg(offset); + uint8_t rxbuf[20]; + size_t rxsize; if (reg != AUX_CH_CTL) { vgpu_vreg(vgpu, offset) = value; @@ -459,6 +457,9 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, } msg_length = AUX_CTL_MSG_LENGTH(value); + for (rxsize = 0; rxsize < msg_length; rxsize += 4) + intel_dp_unpack_aux(vgpu_vreg(vgpu, offset + 4 + rxsize), + rxbuf + rxsize, msg_length - rxsize); // check the msg in DATA register. msg = vgpu_vreg(vgpu, offset + 4); addr = (msg >> 8) & 0xffff; @@ -498,12 +499,13 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, } } } else if ((op & 0x1) == GVT_AUX_I2C_WRITE) { - /* TODO - * We only support EDID reading from I2C_over_AUX. And - * we do not expect the index mode to be used. Right now - * the WRITE operation is ignored. It is good enough to - * support the gfx driver to do EDID access. + /* We only support EDID reading from I2C_over_AUX. + * But if EDID has extension blocks, we use this write + * operation to set block starting address */ + if (addr == EDID_ADDR) { + i2c_edid->current_edid_read = rxbuf[4]; + } } else { if (WARN_ON((op & 0x1) != GVT_AUX_I2C_READ)) return; diff --git a/drivers/gpu/drm/i915/gvt/edid.h b/drivers/gpu/drm/i915/gvt/edid.h index f6dfc8b795ec..11a75d69062d 100644 --- a/drivers/gpu/drm/i915/gvt/edid.h +++ b/drivers/gpu/drm/i915/gvt/edid.h @@ -48,7 +48,7 @@ struct intel_vgpu_edid_data { bool data_valid; - unsigned char edid_block[EDID_SIZE]; + unsigned char edid_block[0]; }; enum gmbus_cycle_type { diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 46c8b720e336..8523fbc1e494 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -301,6 +301,46 @@ static int init_service_thread(struct intel_gvt *gvt) return 0; } +void intel_gvt_init_pipe_info(struct intel_gvt *gvt); + +/* + * When enabling multi-plane in DomU, an issue is that the PLANE_BUF_CFG + * register cannot be updated dynamically, since Dom0 has no idea of the + * plane information of DomU's planes, so here we statically allocate the + * ddb entries for all the possible enabled planes. + */ +static void intel_gvt_init_ddb(struct intel_gvt *gvt) +{ + struct drm_i915_private *dev_priv = gvt->dev_priv; + struct skl_ddb_allocation *ddb = &gvt->ddb; + unsigned int pipe_size, ddb_size, plane_size, plane_cnt; + u16 start, end; + enum pipe pipe; + enum plane_id plane; + + ddb_size = INTEL_INFO(dev_priv)->ddb_size; + ddb_size -= 4; /* 4 blocks for bypass path allocation */ + pipe_size = ddb_size / INTEL_INFO(dev_priv)->num_pipes; + + memset(ddb, 0, sizeof(*ddb)); + for_each_pipe(dev_priv, pipe) { + start = pipe * ddb_size / INTEL_INFO(dev_priv)->num_pipes; + end = start + pipe_size; + ddb->plane[pipe][PLANE_CURSOR].start = end - 8; + ddb->plane[pipe][PLANE_CURSOR].end = end; + + plane_cnt = (INTEL_INFO(dev_priv)->num_sprites[pipe] + 1); + plane_size = (pipe_size - 8) / plane_cnt; + + for_each_universal_plane(dev_priv, pipe, plane) { + ddb->plane[pipe][plane].start = start + + (plane * (pipe_size - 8) / plane_cnt); + ddb->plane[pipe][plane].end = + ddb->plane[pipe][plane].start + plane_size; + } + } +} + /** * intel_gvt_clean_device - clean a GVT device * @gvt: intel gvt device @@ -421,6 +461,9 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) goto out_clean_types; } + intel_gvt_init_pipe_info(gvt); + intel_gvt_init_ddb(gvt); + ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt, &intel_gvt_ops); if (ret) { diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 31f6cdbe5c42..47ed1789ea28 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -291,6 +291,7 @@ struct intel_gvt_firmware { }; #define NR_MAX_INTEL_VGPU_TYPES 20 + struct intel_vgpu_type { char name[16]; unsigned int avail_instance; @@ -301,6 +302,14 @@ struct intel_vgpu_type { enum intel_vgpu_edid resolution; }; +struct intel_gvt_pipe_info { + enum pipe pipe_num; + int owner; + struct intel_gvt *gvt; + struct work_struct vblank_work; + int plane_owner[I915_MAX_PLANES]; +}; + struct intel_gvt { /* GVT scope lock, protect GVT itself, and all resource currently * not yet protected by special locks(vgpu and scheduler lock). @@ -334,6 +343,10 @@ struct intel_gvt { */ unsigned long service_request; + struct intel_gvt_pipe_info pipe_info[I915_MAX_PIPES]; + + struct skl_ddb_allocation ddb; + struct { struct engine_mmio *mmio; int ctx_mmio_count[I915_NUM_ENGINES]; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 7f7f2fb09a37..e1855328eba6 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -441,18 +441,21 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { u32 data; + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + struct intel_crtc *crtc = intel_get_crtc_for_pipe( + vgpu->gvt->dev_priv, pipe); write_vreg(vgpu, offset, p_data, bytes); data = vgpu_vreg(vgpu, offset); - if (data & PIPECONF_ENABLE) + if (data & PIPECONF_ENABLE) { vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE; - else + if (crtc) + drm_crtc_vblank_get(&crtc->base); + } else { vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE; - /* vgpu_lock already hold by emulate mmio r/w */ - mutex_unlock(&vgpu->vgpu_lock); - intel_gvt_check_vblank_emulation(vgpu->gvt); - mutex_lock(&vgpu->vgpu_lock); + } + return 0; } @@ -2815,6 +2818,7 @@ static int init_broadwell_mmio_info(struct intel_gvt *gvt) static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); @@ -2823,6 +2827,11 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, write_vreg(vgpu, offset, p_data, bytes); vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); + if ((vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE) && + (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id)) { + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + } + set_bit(flip_event, vgpu->irq.flip_done_event[pipe]); return 0; } @@ -2830,7 +2839,15 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + write_vreg(vgpu, offset, p_data, bytes); + if ((vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE) && + (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id)) { + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + } return 0; } @@ -2937,8 +2954,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); - MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 29877969310d..0b16775e292c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -37,6 +37,10 @@ #include "i915_trace.h" #include "intel_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /** * DOC: interrupt handling * @@ -221,6 +225,17 @@ static void gen2_assert_iir_is_zero(struct drm_i915_private *dev_priv, static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); + +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +static inline void gvt_notify_vblank(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + if (dev_priv->gvt) + queue_work(system_highpri_wq, + &dev_priv->gvt->pipe_info[pipe].vblank_work); +} +#endif + /* For display hotplug interrupt */ static inline void i915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv, @@ -2837,8 +2852,12 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); - if (iir & GEN8_PIPE_VBLANK) + if (iir & GEN8_PIPE_VBLANK) { drm_handle_vblank(&dev_priv->drm, pipe); +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + gvt_notify_vblank(dev_priv, pipe); +#endif + } if (iir & GEN8_PIPE_CDCLK_CRC_DONE) hsw_pipe_crc_irq_handler(dev_priv, pipe); @@ -3446,7 +3465,9 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); + /*since guest will see all the pipes, we don't want it disable vblank*/ + if (!dev_priv->gvt) + bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1193202766a2..37c08c71fdf6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -479,7 +479,7 @@ uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes) return v; } -static void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) +void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) { int i; if (dst_bytes > 4) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc61e96754f..7c11b8d10b66 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1708,6 +1708,7 @@ int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); void intel_dp_hot_plug(struct intel_encoder *intel_encoder); void intel_power_sequencer_reset(struct drm_i915_private *dev_priv); uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes); +void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes); void intel_plane_destroy(struct drm_plane *plane); void intel_edp_drrs_enable(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 43ae9de12ba3..01d0f4d3653a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -33,6 +33,10 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /** * DOC: RC6 * @@ -5128,6 +5132,14 @@ skl_compute_ddb(struct drm_atomic_state *state) memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb)); +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + /* In GVT environemnt, we only use the statically allocated ddb */ + if (dev_priv->gvt) { + memcpy(ddb, &dev_priv->gvt->ddb, sizeof(*ddb)); + return 0; + } +#endif + for_each_new_intel_crtc_in_state(intel_state, crtc, cstate, i) { ret = skl_allocate_pipe_ddb(cstate, ddb); if (ret) @@ -5354,6 +5366,11 @@ static void skl_atomic_update_crtc_wm(struct intel_atomic_state *state, I915_WRITE(PIPE_WM_LINETIME(pipe), pipe_wm->linetime); for_each_plane_id_on_crtc(crtc, plane_id) { +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif if (plane_id != PLANE_CURSOR) skl_write_plane_wm(crtc, &pipe_wm->planes[plane_id], ddb, plane_id); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index f7026e887fa9..11b7afc0a0d3 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -41,6 +41,10 @@ #include #include "i915_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode, int usecs) { @@ -253,6 +257,11 @@ skl_update_plane(struct intel_plane *plane, uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; unsigned long irqflags; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif /* Sizes are 0 based */ src_w--; src_h--; @@ -336,6 +345,12 @@ skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) enum pipe pipe = plane->pipe; unsigned long irqflags; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); I915_WRITE_FW(PLANE_CTL(pipe, plane_id), 0); From 50180d0fb90be832168a824735b4f471a5056e92 Mon Sep 17 00:00:00 2001 From: Min He Date: Thu, 28 Dec 2017 12:21:16 +0800 Subject: [PATCH 1024/1276] drm/i915/gvt: local display support in GVT-g guest This patch includes below features in GVT-g guest 1. DP on port A will be treated as external DP 2. Avoid some unnecessary checks in GVT-g guest 3. initial default vbt values by using PCH_NONE Change-Id: I6f6dfa4e5c801e7a63105310d5991adb0a0acad1 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_bios.c | 8 ++++++++ drivers/gpu/drm/i915/intel_ddi.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 11 ++++++++++- drivers/gpu/drm/i915/intel_dp.c | 7 ++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 1faa494e2bc9..1f99373dcd77 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1726,6 +1726,14 @@ void intel_bios_init(struct drm_i915_private *dev_priv) return; } + if (HAS_PCH_NOP(dev_priv) && !intel_vgpu_active(dev_priv)) { + DRM_DEBUG_KMS("Skipping VBT init due to disabled display.\n"); + return; + } + else if (HAS_PCH_NOP(dev_priv)) { + dev_priv->pch_type = PCH_NONE; + } + init_vbt_defaults(dev_priv); /* If the OpRegion does not have VBT, look in PCI ROM. */ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c9af34861d9e..588b03353fa7 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1882,7 +1882,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) goto out; } - if (port == PORT_A) + if (port == PORT_A && !intel_vgpu_active(dev_priv)) cpu_transcoder = TRANSCODER_EDP; else cpu_transcoder = (enum transcoder) pipe; @@ -3278,7 +3278,7 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, enum port port = encoder->port; int ret; - if (port == PORT_A) + if (port == PORT_A && !intel_vgpu_active(dev_priv)) pipe_config->cpu_transcoder = TRANSCODER_EDP; if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_HDMI)) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2951096bca0..8547b379c110 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11874,7 +11874,16 @@ verify_crtc_state(struct drm_crtc *crtc, intel_pipe_config_sanity_check(dev_priv, pipe_config); sw_config = to_intel_crtc_state(new_crtc_state); - if (!intel_pipe_config_compare(dev_priv, sw_config, + + /* + * Only check for pipe config if we are not in a GVT guest environment, + * because such a check in a GVT guest environment doesn't make any sense + * as we don't allow the guest to do a mode set, so there can very well + * be a difference between what it has programmed vs. what the host + * truly configured the HW pipe to be in. + */ + if (!intel_vgpu_active(dev_priv) && + !intel_pipe_config_compare(dev_priv, sw_config, pipe_config, false)) { I915_STATE_WARN(1, "pipe state doesn't match!\n"); intel_dump_pipe_config(intel_crtc, pipe_config, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 37c08c71fdf6..23a94eedb896 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2045,7 +2045,12 @@ static void wait_panel_status(struct intel_dp *intel_dp, I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); - if (intel_wait_for_register(dev_priv, + /* + * Only wait for panel status if we are not in a GVT guest environment, + * because such a wait in a GVT guest environment doesn't make any sense + * as we are exposing virtual DP monitors to the guest. + */ + if (!intel_vgpu_active(dev_priv) && intel_wait_for_register(dev_priv, pp_stat_reg, mask, value, 5000)) DRM_ERROR("Panel status timeout: status %08x control %08x\n", From 9c7f6b8e6fb96096c8a2d0c7fd78a3f979135513 Mon Sep 17 00:00:00 2001 From: Michael Byrne Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 1025/1276] drm/i915/gvt: Change DomU to support 3 HDMI displays. Request DomU supports 3 virtual HDMI displays. This requires eDP-1, DP-1 and DP-2 to be disabled on DomU command line. DomU's ias.conf must also be updated to support HDMI and not DP displays. If a panel is not present on port A, DDI B and DDI C will be made active (as in Dom0). Reviewed by: Min He Signed-off-by: Michael Byrne Change-Id: Ib944de7bf9be5c19db28d35edd77a9f89148f27a Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/display.c | 40 +++++++++++++++++++++--------- drivers/gpu/drm/i915/gvt/display.h | 1 + drivers/gpu/drm/i915/gvt/edid.c | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 2e01c38887bd..40ecca217733 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -314,8 +314,8 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num) port->dpcd = NULL; } -static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, - int type, unsigned int resolution, void *edid) +static int setup_virtual_monitor(struct intel_vgpu *vgpu, int port_num, + int type, unsigned int resolution, void *edid, bool is_dp) { struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num); int valid_extensions = 1; @@ -356,9 +356,11 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, port->edid->data_valid = true; - memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); - port->dpcd->data_valid = true; - port->dpcd->data[DPCD_SINK_COUNT] = 0x1; + if (is_dp) { + memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); + port->dpcd->data_valid = true; + port->dpcd->data[DPCD_SINK_COUNT] = 0x1; + } port->type = type; emulate_monitor_status_change(vgpu); @@ -488,22 +490,36 @@ void intel_gvt_init_pipe_info(struct intel_gvt *gvt) } } +bool gvt_emulate_hdmi = true; + int setup_virtual_monitors(struct intel_vgpu *vgpu) { struct intel_connector *connector = NULL; struct drm_connector_list_iter conn_iter; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; int pipe = 0; int ret = 0; + int type = gvt_emulate_hdmi ? GVT_HDMI_A : GVT_DP_A; + int port = PORT_B; + + /* BXT have to use port A for HDMI to support 3 HDMI monitors */ + if (IS_BROXTON(dev_priv)) + port = PORT_A; drm_connector_list_iter_begin(&vgpu->gvt->dev_priv->drm, &conn_iter); for_each_intel_connector_iter(connector, &conn_iter) { if (connector->encoder->get_hw_state(connector->encoder, &pipe) && connector->detect_edid) { - ret = setup_virtual_dp_monitor(vgpu, pipe, - GVT_DP_A + pipe, 0, - connector->detect_edid); + /* Get (Dom0) port associated with current pipe. */ + port = enc_to_dig_port( + &(connector->encoder->base))->base.port; + ret = setup_virtual_monitor(vgpu, port, + type, 0, connector->detect_edid, + !gvt_emulate_hdmi); if (ret) return ret; + type++; + port++; } } drm_connector_list_iter_end(&conn_iter); @@ -560,11 +576,11 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution) if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) return setup_virtual_monitors(vgpu); else if (IS_SKYLAKE(dev_priv)) - return setup_virtual_dp_monitor(vgpu, PORT_D, GVT_DP_D, - resolution, NULL); + return setup_virtual_monitor(vgpu, PORT_D, GVT_DP_D, + resolution, NULL, true); else - return setup_virtual_dp_monitor(vgpu, PORT_B, GVT_DP_B, - resolution, NULL); + return setup_virtual_monitor(vgpu, PORT_B, GVT_DP_B, + resolution, NULL, true); } /** diff --git a/drivers/gpu/drm/i915/gvt/display.h b/drivers/gpu/drm/i915/gvt/display.h index ea7c1c525b8c..e6d3912bc730 100644 --- a/drivers/gpu/drm/i915/gvt/display.h +++ b/drivers/gpu/drm/i915/gvt/display.h @@ -140,6 +140,7 @@ enum intel_vgpu_port_type { GVT_DP_B, GVT_DP_C, GVT_DP_D, + GVT_HDMI_A, GVT_HDMI_B, GVT_HDMI_C, GVT_HDMI_D, diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 4785b8a10619..fb690a4f55a0 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -83,7 +83,7 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0) else if (port_select == 2) port = PORT_C; else if (port_select == 3) - port = PORT_D; + port = PORT_A; return port; } From 7d6677889fbe7d8bcc494a8c9dd0a2d0ee6eec0c Mon Sep 17 00:00:00 2001 From: Michael Byrne Date: Wed, 25 Oct 2017 13:12:34 +0200 Subject: [PATCH 1026/1276] drm/i915: i915 changes to allow DomU to support 3 HDMI displays. If vgpu is active; a) and we are requesting port A, and a monitor is present on port A, force HDMI initialization (Port A is normally reserved for DP/eDP). b) ignore check for lspcon as this disables HDMI initialiisation if lspcon is supported. c) ignore check for intel_encoder->type != INTEL_OUTPUT_EDP so we can initialise HDMI (HDMI initialization will change the encode type anyway). d) allow HDMI to specify pins for port A to allow initialisation. Change-Id: I99f9636dc6403d67a0de0422eff9b504190fc5f1 Reviewed-by: Min He Signed-off-by: Michael Byrne Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_ddi.c | 14 +++++++++++--- drivers/gpu/drm/i915/intel_hdmi.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 588b03353fa7..1ca518775b68 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3548,11 +3548,18 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) bool init_hdmi, init_dp, init_lspcon = false; - init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi || + /* + * For port A check whether vgpu is active and we have a monitor + * attached to port A. + * */ + init_hdmi = (intel_vgpu_active(dev_priv) && port == PORT_A && + (I915_READ(GEN8_DE_PORT_ISR) & BXT_DE_PORT_HP_DDIA)) || + (dev_priv->vbt.ddi_port_info[port].supports_dvi || dev_priv->vbt.ddi_port_info[port].supports_hdmi); init_dp = dev_priv->vbt.ddi_port_info[port].supports_dp; - if (intel_bios_is_lspcon_present(dev_priv, port)) { + if (!intel_vgpu_active(dev_priv) && + intel_bios_is_lspcon_present(dev_priv, port)) { /* * Lspcon device needs to be driven with DP connector * with special detection sequence. So make sure DP @@ -3648,7 +3655,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) /* In theory we don't need the encoder->type check, but leave it just in * case we have some really bad VBTs... */ - if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) { + if ((intel_vgpu_active(dev_priv) && IS_BROXTON(dev_priv)) || + (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi)) { if (!intel_ddi_init_hdmi_connector(intel_dig_port)) goto err; } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 192972a7d287..a3b6d078ca24 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2182,6 +2182,14 @@ static u8 bxt_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) u8 ddc_pin; switch (port) { + case PORT_A: + if ((IS_GEN9_LP(dev_priv)) && (intel_vgpu_active(dev_priv))) + ddc_pin = GMBUS_PIN_3_BXT; + else { + MISSING_CASE(port); + ddc_pin = GMBUS_PIN_DPB; + } + break; case PORT_B: ddc_pin = GMBUS_PIN_1_BXT; break; @@ -2365,7 +2373,8 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port); - if (WARN_ON(port == PORT_A)) + if (!intel_vgpu_active(dev_priv) && + WARN_ON(port == PORT_A)) return; intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port); From 36b7b81e63cf539e31a931affd7b97f3fa1d79e7 Mon Sep 17 00:00:00 2001 From: Min He Date: Sat, 15 Apr 2017 04:20:20 +0800 Subject: [PATCH 1027/1276] drm/i915/gvt: removed save/store registers Simple the logic of workload conext status change. Remove the unnecessary status switch when one workload is scheduled in. Signed-off-by: Zhao Yan Signed-off-by: Zhao Yakui Signed-off-by: Min He Change-Id: I509310bf1febb4cd9918440521c29eacad8c06eb Acknowledged-by: Vivek Kasireddy Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/sched_policy.c | 12 -------- drivers/gpu/drm/i915/gvt/scheduler.c | 39 ++++++++----------------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index c32e7d5e8629..4fac40d26549 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -444,9 +444,7 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) { struct intel_gvt_workload_scheduler *scheduler = &vgpu->gvt->scheduler; - int ring_id; struct vgpu_sched_data *vgpu_data = vgpu->sched_data; - struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; if (!vgpu_data->active) return; @@ -465,15 +463,5 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) scheduler->current_vgpu = NULL; } - intel_runtime_pm_get(dev_priv); - spin_lock_bh(&scheduler->mmio_context_lock); - for (ring_id = 0; ring_id < I915_NUM_ENGINES; ring_id++) { - if (scheduler->engine_owner[ring_id] == vgpu) { - intel_gvt_switch_mmio(vgpu, NULL, ring_id); - scheduler->engine_owner[ring_id] = NULL; - } - } - spin_unlock_bh(&scheduler->mmio_context_lock); - intel_runtime_pm_put(dev_priv); mutex_unlock(&vgpu->gvt->sched_lock); } diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 43aa058e29fc..e7514bdf2fe9 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -203,6 +203,7 @@ static inline bool is_gvt_request(struct i915_request *req) return i915_gem_context_force_single_submission(req->gem_context); } +/* static void save_ring_hw_state(struct intel_vgpu *vgpu, int ring_id) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; @@ -216,6 +217,7 @@ static void save_ring_hw_state(struct intel_vgpu *vgpu, int ring_id) reg = RING_ACTHD_UDW(ring_base); vgpu_vreg(vgpu, i915_mmio_reg_offset(reg)) = I915_READ_FW(reg); } +*/ static int shadow_context_status_change(struct notifier_block *nb, unsigned long action, void *data) @@ -226,21 +228,9 @@ static int shadow_context_status_change(struct notifier_block *nb, struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; enum intel_engine_id ring_id = req->engine->id; struct intel_vgpu_workload *workload; - unsigned long flags; - - if (!is_gvt_request(req)) { - spin_lock_irqsave(&scheduler->mmio_context_lock, flags); - if (action == INTEL_CONTEXT_SCHEDULE_IN && - scheduler->engine_owner[ring_id]) { - /* Switch ring from vGPU to host. */ - intel_gvt_switch_mmio(scheduler->engine_owner[ring_id], - NULL, ring_id); - scheduler->engine_owner[ring_id] = NULL; - } - spin_unlock_irqrestore(&scheduler->mmio_context_lock, flags); + if (!is_gvt_request(req)) return NOTIFY_OK; - } workload = scheduler->current_workload[ring_id]; if (unlikely(!workload)) @@ -248,25 +238,13 @@ static int shadow_context_status_change(struct notifier_block *nb, switch (action) { case INTEL_CONTEXT_SCHEDULE_IN: - spin_lock_irqsave(&scheduler->mmio_context_lock, flags); - if (workload->vgpu != scheduler->engine_owner[ring_id]) { - /* Switch ring from host to vGPU or vGPU to vGPU. */ - intel_gvt_switch_mmio(scheduler->engine_owner[ring_id], - workload->vgpu, ring_id); - scheduler->engine_owner[ring_id] = workload->vgpu; - } else - gvt_dbg_sched("skip ring %d mmio switch for vgpu%d\n", - ring_id, workload->vgpu->id); - spin_unlock_irqrestore(&scheduler->mmio_context_lock, flags); atomic_set(&workload->shadow_ctx_active, 1); break; case INTEL_CONTEXT_SCHEDULE_OUT: - save_ring_hw_state(workload->vgpu, ring_id); atomic_set(&workload->shadow_ctx_active, 0); break; case INTEL_CONTEXT_SCHEDULE_PREEMPTED: - save_ring_hw_state(workload->vgpu, ring_id); - break; + return NOTIFY_OK; default: WARN_ON(1); return NOTIFY_OK; @@ -909,6 +887,7 @@ static int workload_thread(void *priv) struct intel_vgpu_workload *workload = NULL; struct intel_vgpu *vgpu = NULL; int ret; + long lret; bool need_force_wake = IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv) || IS_BROXTON(gvt->dev_priv); @@ -955,7 +934,13 @@ static int workload_thread(void *priv) gvt_dbg_sched("ring id %d wait workload %p\n", workload->ring_id, workload); - i915_request_wait(workload->req, 0, MAX_SCHEDULE_TIMEOUT); + lret = i915_request_wait(workload->req, 0, + MAX_SCHEDULE_TIMEOUT); + + gvt_dbg_sched("i915_wait_request %p returns %ld\n", + workload, lret); + if (lret >= 0 && workload->status == -EINPROGRESS) + workload->status = 0; complete: gvt_dbg_sched("will complete workload %p, status: %d\n", From 1b61e9d12c2713e848c430dcdcf10b1a75b6dce4 Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Fri, 2 Jun 2017 09:03:19 +0800 Subject: [PATCH 1028/1276] drm/i915/gvt: ivi: lazy shadow context It's a significant overhead to shadow guest context by copying all the pages, for performance consideration this patch introduce context lazy shadow, it copy the first page of the context only and let the GGTT entries of other shadow context pages point to the corresponding pages of the guest context. Change-Id: I6b806da29fa75eff73122d0328b1a277780eabe1 Signed-off-by: Ping Gao Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 92 +++++++++++++++++++++------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index e7514bdf2fe9..f0f0ada705cd 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -119,6 +119,7 @@ static void sr_oa_regs(struct intel_vgpu_workload *workload, } } +static bool enable_lazy_shadow_ctx = true; static int populate_shadow_context(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; @@ -130,6 +131,10 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) struct page *page; void *dst; unsigned long context_gpa, context_page_num; + struct drm_i915_private *dev_priv = gvt->dev_priv; + struct i915_ggtt *ggtt = &gvt->dev_priv->ggtt; + dma_addr_t addr; + gen8_pte_t __iomem *pte; int i; gvt_dbg_sched("ring id %d workload lrca %x", ring_id, @@ -143,6 +148,18 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) context_page_num = 19; i = 2; +#ifdef CONFIG_INTEL_IOMMU + /* + * In case IOMMU for graphics is turned on, we don't want to + * turn on lazy shadow context feature because it will touch + * GGTT entries which require a BKL and since this is a + * performance enhancement feature, we will end up negating + * the performance. + */ + if(intel_iommu_gfx_mapped) { + enable_lazy_shadow_ctx = false; + } +#endif while (i < context_page_num) { context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, @@ -153,14 +170,41 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) return -EFAULT; } - page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); - dst = kmap(page); - intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst, + if (!enable_lazy_shadow_ctx) { + page = i915_gem_object_get_page(ctx_obj, + LRC_PPHWSP_PN + i); + dst = kmap(page); + intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst, I915_GTT_PAGE_SIZE); - kunmap(page); + kunmap(page); + } else { + unsigned long mfn; + struct i915_gem_context *shadow_ctx = + workload->vgpu->submission.shadow_ctx; + + addr = i915_ggtt_offset( + shadow_ctx->__engine[ring_id].state) + + (LRC_PPHWSP_PN + i) * PAGE_SIZE; + pte = (gen8_pte_t __iomem *)ggtt->gsm + + (addr >> PAGE_SHIFT); + + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, + context_gpa >> 12); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn during context shadow\n"); + return -ENXIO; + } + + mfn <<= 12; + mfn |= _PAGE_PRESENT | _PAGE_RW | PPAT_CACHED; + writeq(mfn, pte); + } i++; } + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); + POSTING_READ(GFX_FLSH_CNTL_GEN6); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); shadow_ring_context = kmap(page); @@ -712,29 +756,31 @@ static void update_guest_context(struct intel_vgpu_workload *workload) gvt_dbg_sched("ring id %d workload lrca %x\n", rq->engine->id, workload->ctx_desc.lrca); - context_page_num = rq->engine->context_size; - context_page_num = context_page_num >> PAGE_SHIFT; + if (!enable_lazy_shadow_ctx) { + context_page_num = rq->engine->context_size; + context_page_num = context_page_num >> PAGE_SHIFT; - if (IS_BROADWELL(gvt->dev_priv) && rq->engine->id == RCS) - context_page_num = 19; + if (IS_BROADWELL(gvt->dev_priv) && rq->engine->id == RCS) + context_page_num = 19; - i = 2; + i = 2; - while (i < context_page_num) { - context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, - (u32)((workload->ctx_desc.lrca + i) << - I915_GTT_PAGE_SHIFT)); - if (context_gpa == INTEL_GVT_INVALID_ADDR) { - gvt_vgpu_err("invalid guest context descriptor\n"); - return; - } + while (i < context_page_num) { + context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, + (u32)((workload->ctx_desc.lrca + i) << + I915_GTT_PAGE_SHIFT)); + if (context_gpa == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("invalid guest context descriptor\n"); + return; + } - page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); - src = kmap(page); - intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src, - I915_GTT_PAGE_SIZE); - kunmap(page); - i++; + page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); + src = kmap(page); + intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src, + I915_GTT_PAGE_SIZE); + kunmap(page); + i++; + } } intel_gvt_hypervisor_write_gpa(vgpu, workload->ring_context_gpa + From 619a44e288ab4a33cc329deaff8936f66dc065b8 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 1029/1276] drm/i915/gvt: add some MMIO value initialization There are some virtual MMIO registers contains static value which won't be changed by guest writing. Those registers could be optimized with a static value initialization, and do nothing in write handler. Change-Id: I1a85f717ef23c171651f1a1cad5c73880d538e3d Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 32 +++++++---------------------- drivers/gpu/drm/i915/gvt/mmio.c | 9 ++++++++ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index e1855328eba6..495d6a298924 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -413,27 +413,9 @@ static int lcpll_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } -static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, +static int mmio_write_empty(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { - switch (offset) { - case 0xe651c: - case 0xe661c: - case 0xe671c: - case 0xe681c: - vgpu_vreg(vgpu, offset) = 1 << 17; - break; - case 0xe6c04: - vgpu_vreg(vgpu, offset) = 0x3; - break; - case 0xe6e1c: - vgpu_vreg(vgpu, offset) = 0x2f << 16; - break; - default: - return -EINVAL; - } - - read_vreg(vgpu, offset, p_data, bytes); return 0; } @@ -2240,12 +2222,12 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(PCH_PP_ON_DELAYS, D_ALL); MMIO_D(PCH_PP_OFF_DELAYS, D_ALL); - MMIO_DH(_MMIO(0xe651c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe661c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe671c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe681c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe6c04), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe6e1c), D_ALL, dpy_reg_mmio_read, NULL); + MMIO_DH(_MMIO(0xe651c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe661c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe671c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe681c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe6c04), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe6e1c), D_ALL, NULL, mmio_write_empty); MMIO_RO(PCH_PORT_HOTPLUG, D_ALL, 0, PORTA_HOTPLUG_STATUS_MASK diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 9bb9a85c992c..4149eae235b5 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -282,6 +282,15 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) memcpy(vgpu->mmio.sreg, mmio, GVT_GEN8_MMIO_RESET_OFFSET); } + /* below vreg init value are got from handler.c, + * which won't change during vgpu life cycle + */ + vgpu_vreg(vgpu, 0xe651c) = 1 << 17; + vgpu_vreg(vgpu, 0xe661c) = 1 << 17; + vgpu_vreg(vgpu, 0xe671c) = 1 << 17; + vgpu_vreg(vgpu, 0xe681c) = 1 << 17; + vgpu_vreg(vgpu, 0xe6c04) = 3; + vgpu_vreg(vgpu, 0xe6e1c) = 0x2f << 16; } /** From f9783507930e7f7dbe652847a8ca959b297b4bc3 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 1030/1276] drm/i915/gvt: added option to disable wa_ctx shadowing This patch add an option in GVTg to disable shadowing wa_ctx. In current IVI environment, since Dom0 and DomU has the same i915, the wa_ctx of them is same. So no need to do shadow copy of wa_ctx. By default, shadowing wa_ctx is disabled. If for testing purpose, people can enable it manually by modifying the code. Change-Id: I535af0a720e7ccfbca80686e45c79c5c2a3dd9c6 Signed-off-by: Min He Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index f0f0ada705cd..9d8f0bfc434c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -41,6 +41,8 @@ #define RING_CTX_OFF(x) \ offsetof(struct execlist_ring_context, x) +bool gvt_shadow_wa_ctx = false; + static void set_context_pdp_root_pointer( struct execlist_ring_context *ring_context, u32 pdp[8]) @@ -403,7 +405,8 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload) goto err_unpin; if ((workload->ring_id == RCS) && - (workload->wa_ctx.indirect_ctx.size != 0)) { + (workload->wa_ctx.indirect_ctx.size != 0) + && gvt_shadow_wa_ctx) { ret = intel_gvt_scan_and_shadow_wa_ctx(&workload->wa_ctx); if (ret) goto err_shadow; @@ -628,10 +631,12 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } - ret = prepare_shadow_wa_ctx(&workload->wa_ctx); - if (ret) { - gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n"); - goto err_shadow_batch; + if (gvt_shadow_wa_ctx) { + ret = prepare_shadow_wa_ctx(&workload->wa_ctx); + if (ret) { + gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n"); + goto err_shadow_batch; + } } if (workload->prepare) { @@ -887,7 +892,8 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) if (!workload->status) { release_shadow_batch_buffer(workload); - release_shadow_wa_ctx(&workload->wa_ctx); + if(gvt_shadow_wa_ctx) + release_shadow_wa_ctx(&workload->wa_ctx); } if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) { From 0d2ca2bb98da5a397dbad5130c73c71a7b934e00 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Thu, 22 Jun 2017 18:45:21 +0800 Subject: [PATCH 1031/1276] drm/i915/gvt: enable ppgtt oos sync by default After enabling oos page sync, the loading time of GVT-g guest workload will be reduced. Signed-off-by: Pei Zhang Reviewed-by: Vivek Kasireddy Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 6262308c4a78..af3b1dea4510 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -44,7 +44,7 @@ #define gvt_vdbg_mm(fmt, args...) #endif -static bool enable_out_of_sync = false; +static bool enable_out_of_sync = true; static int preallocated_oos_pages = 8192; /* From e35e1fc2753a9604caf53086cbdb40440063bb01 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 1032/1276] drm/i915/gvt: emit shadow ppgtt root in LRI Usually the PDP/PML4 root poiners should be updated through the context, however, on BXT, we found that sometimes the PDP/PML4 root pointers will be reverted back to an old value, which causes GPU hang. The reason is still unclear, but by adding LRI command in the ring buffer, we are able to update the PDP/PML4 root pointers successfully. So far we will treat this patch as a workaround, unless we figure out why the PDP/PML4 root pointers was reverted back. Change-Id: Id65f7621ed9d45073f220fd2d91112558e7820d9 Signed-off-by: Min He Reviewed-by: Singh, Satyeshwar Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/cmd_parser.h | 1 + drivers/gpu/drm/i915/gvt/scheduler.c | 8 ++++++++ 3 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index a614db310ea2..95244b2cb679 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -2710,6 +2710,33 @@ static int scan_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) return ret; } +#define GEN8_PDPES 4 +int gvt_emit_pdps(struct intel_vgpu_workload *workload) +{ + const int num_cmds = GEN8_PDPES * 2; + struct i915_request *req = workload->req; + struct intel_engine_cs *engine = req->engine; + u32 *cs; + u32 *pdps = (u32 *)(workload->shadow_mm->ppgtt_mm.shadow_pdps); + int i; + + cs = intel_ring_begin(req, num_cmds * 2 + 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_LOAD_REGISTER_IMM(num_cmds); + for (i = 0; i < GEN8_PDPES; i++) { + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); + *cs++ = pdps[i * 2]; + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); + *cs++ = pdps[i * 2 + 1]; + } + *cs++ = MI_NOOP; + intel_ring_advance(req, cs); + + return 0; +} + static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.h b/drivers/gpu/drm/i915/gvt/cmd_parser.h index 286703643002..1356803a0586 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.h +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.h @@ -46,4 +46,5 @@ int intel_gvt_scan_and_shadow_ringbuffer(struct intel_vgpu_workload *workload); int intel_gvt_scan_and_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx); +int gvt_emit_pdps(struct intel_vgpu_workload *workload); #endif diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 9d8f0bfc434c..7243324cb93c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -619,6 +619,14 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } + /* we consider this as an workaround to avoid the situation that + * PDP's not updated, and right now we only limit it to BXT platform + * since it's not reported on the other platforms + */ + if (IS_BROXTON(vgpu->gvt->dev_priv)) { + gvt_emit_pdps(workload); + } + ret = copy_workload_to_ring_buffer(workload); if (ret) { gvt_vgpu_err("fail to generate request\n"); From 88bb8ab15125cbed5b47233d5f4dc7d819be3acf Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Wed, 13 Sep 2017 17:29:57 -0700 Subject: [PATCH 1033/1276] drm/i915/gvt: Raise a uevent when Dom 0 is ready for Dom U HV vendors want to know when Dom 0 is ready to start a Dom U because they want to start Dom U as early as possible. This feature informs XenGT module as soon as Dom 0 is ready. In our example, we raise a uevent from XenGT module but the HV vendors are free to change the module to do any custom action that they want. Change-Id: Ibfdaca65002825e14e15527c386db00f59b372e5 Signed-off-by: Satyeshwar Singh Reviewed-by: He, Min Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gvt.c | 8 ++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 1 + drivers/gpu/drm/i915/gvt/hypercall.h | 1 + drivers/gpu/drm/i915/gvt/mpt.h | 8 ++++++++ drivers/gpu/drm/i915/intel_display.c | 13 +++++++++++++ 5 files changed, 31 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 8523fbc1e494..25e0a58a24a5 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -511,6 +511,14 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) return ret; } +int gvt_dom0_ready(struct drm_i915_private *dev_priv) +{ + if (!intel_gvt_active(dev_priv)) + return 0; + + return intel_gvt_hypervisor_dom0_ready(); +} + #if IS_ENABLED(CONFIG_DRM_I915_GVT_KVMGT) MODULE_SOFTDEP("pre: kvmgt"); #endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 47ed1789ea28..9c291924c1d0 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -592,6 +592,7 @@ struct intel_gvt_ops { unsigned int); }; +int gvt_dom0_ready(struct drm_i915_private *dev_priv); enum { GVT_FAILSAFE_UNSUPPORTED_GUEST, diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index 5af11cf1b482..f14cff32ae2f 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -64,6 +64,7 @@ struct intel_gvt_mpt { int (*get_vfio_device)(void *vgpu); void (*put_vfio_device)(void *vgpu); bool (*is_valid_gfn)(unsigned long handle, unsigned long gfn); + int (*dom0_ready)(void); }; extern struct intel_gvt_mpt xengt_mpt; diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index 67f19992b226..feed7adb6fde 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -362,4 +362,12 @@ static inline bool intel_gvt_hypervisor_is_valid_gfn( return intel_gvt_host.mpt->is_valid_gfn(vgpu->handle, gfn); } +static inline int intel_gvt_hypervisor_dom0_ready(void) +{ + if (!intel_gvt_host.mpt->dom0_ready) + return 0; + + return intel_gvt_host.mpt->dom0_ready(); +} + #endif /* _GVT_MPT_H_ */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8547b379c110..d095f1f46de8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -49,6 +49,10 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /* Primary plane formats for gen <= 3 */ static const uint32_t i8xx_primary_formats[] = { DRM_FORMAT_C8, @@ -14324,6 +14328,15 @@ static void intel_setup_outputs(struct drm_i915_private *dev_priv) intel_encoder_clones(encoder); } +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + /* + * Encoders have been initialized. If we are in VGT mode, + * let's inform the HV that it can start Dom U as Dom 0 + * is ready to accept new Dom Us. + */ + gvt_dom0_ready(dev_priv); +#endif + intel_init_pch_refclk(dev_priv); drm_helper_move_panel_connectors_to_head(&dev_priv->drm); From ae57fb5b823cb026a937a8cf3039a7ff4f728121 Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 1034/1276] drm/i915/gvt: Don't load CSR for Dom U This change prevents the CSR firmware to be loaded for Dom U as we don't allow Dom U to control display power management settings and it can save time for Dom U bootup by skipping this firmware loading. Tests have shown this can be anywhere between 80 to 500 ms. Change-Id: Ic8119d69eaf01fda3082f055e306a2d6263411ed Signed-off-by: Satyeshwar Singh Reviewed-by: Vivek Kasireddy Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_csr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index cf9b600cca79..addb223604fa 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -453,7 +453,13 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) INIT_WORK(&dev_priv->csr.work, csr_load_work_fn); - if (!HAS_CSR(dev_priv)) + /* + * In a GVTg enabled environment, loading the CSR firmware for DomU doesn't + * make much sense since we don't allow it to control display power + * management settings. Furthermore, we can save some time for DomU bootup + * by skipping CSR loading. + */ + if (!HAS_CSR(dev_priv) || intel_vgpu_active(dev_priv)) return; if (i915_modparams.dmc_firmware_path) From 9ed7bddd7b98f2ee162e5804c4b2acc4919eec9e Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 1035/1276] drm/i915/gvt: add acrngt support Refine the structure based on the latest gvt-g structure Change-Id: Ifd599d5a7375d73c557a878027bca7ba8851c5f1 Signed-off-by: Fei Jiang Signed-off-by: Min He Signed-off-by: Zhao Yakui Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/Kconfig | 9 + drivers/gpu/drm/i915/gvt/Makefile | 3 +- drivers/gpu/drm/i915/gvt/acrngt.c | 893 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/acrngt.h | 81 +++ drivers/gpu/drm/i915/gvt/gvt.c | 6 + drivers/gpu/drm/i915/gvt/gvt.h | 1 + drivers/gpu/drm/i915/gvt/hypercall.h | 1 + 7 files changed, 993 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/gvt/acrngt.c create mode 100644 drivers/gpu/drm/i915/gvt/acrngt.h diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 33a458b7f1fc..19dfbf39f6ca 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -127,6 +127,15 @@ config DRM_I915_GVT_KVMGT help Choose this option if you want to enable KVMGT support for Intel GVT-g. +config DRM_I915_GVT_ACRN_GVT + tristate "Enable ACRN support for Intel GVT-g" + depends on DRM_I915_GVT + depends on ACRN + depends on ACRN_VHM + default n + help + Choose this option if you want to enable ACRN_GVT support for + Intel GVT-g under ACRN hypervisor environment. menu "drm/i915 Debugging" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile index b016dc753db9..0acb4dabc00c 100644 --- a/drivers/gpu/drm/i915/gvt/Makefile +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -5,6 +5,7 @@ GVT_SOURCE := gvt.o aperture_gm.o handlers.o vgpu.o trace_points.o firmware.o \ execlist.o scheduler.o sched_policy.o mmio_context.o cmd_parser.o debugfs.o \ fb_decoder.o dmabuf.o page_track.o -ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) +ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) -Wall i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE)) obj-$(CONFIG_DRM_I915_GVT_KVMGT) += $(GVT_DIR)/kvmgt.o +obj-$(CONFIG_DRM_I915_GVT_ACRN_GVT) += $(GVT_DIR)/acrngt.o diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c new file mode 100644 index 000000000000..fec5751a4bed --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -0,0 +1,893 @@ +/* + * Interfaces coupled to ACRN + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of Version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + */ + +/* + * NOTE: + * This file contains hypervisor specific interactions to + * implement the concept of mediated pass-through framework. + * What this file provides is actually a general abstraction + * of in-kernel device model, which is not gvt specific. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "acrngt.h" + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("ACRNGT mediated passthrough driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#define ASSERT(x) \ +do { if (x) break; \ + printk(KERN_EMERG "### ASSERTION FAILED %s: %s: %d: %s\n", \ + __FILE__, __func__, __LINE__, #x); dump_stack(); BUG(); \ +} while (0) + + +struct kobject *acrn_gvt_ctrl_kobj; +static struct kset *acrn_gvt_kset; +static DEFINE_MUTEX(acrn_gvt_sysfs_lock); + +struct gvt_acrngt acrngt_priv; +const struct intel_gvt_ops *intel_gvt_ops; + +static void disable_domu_plane(int pipe, int plane) +{ + struct drm_i915_private *dev_priv = acrngt_priv.gvt->dev_priv; + + I915_WRITE(PLANE_CTL(pipe, plane), 0); + + I915_WRITE(PLANE_SURF(pipe, plane), 0); + POSTING_READ(PLANE_SURF(pipe, plane)); +} + +void acrngt_instance_destroy(struct intel_vgpu *vgpu) +{ + int pipe, plane; + struct acrngt_hvm_dev *info = NULL; + struct intel_gvt *gvt = acrngt_priv.gvt; + + if (vgpu) { + info = (struct acrngt_hvm_dev *)vgpu->handle; + + if (info && info->emulation_thread != NULL) + kthread_stop(info->emulation_thread); + + for_each_pipe(gvt->dev_priv, pipe) { + for_each_universal_plane(gvt->dev_priv, pipe, plane) { + if (gvt->pipe_info[pipe].plane_owner[plane] == + vgpu->id) { + disable_domu_plane(pipe, plane); + } + } + } + + intel_gvt_ops->vgpu_deactivate(vgpu); + intel_gvt_ops->vgpu_destroy(vgpu); + } + + if (info) { + gvt_dbg_core("destroy vgpu instance, vm id: %d, client %d", + info->vm_id, info->client); + + if (info->client != 0) + acrn_ioreq_destroy_client(info->client); + + if (info->vm) + put_vm(info->vm); + + kfree(info); + } +} + +static bool acrngt_write_cfg_space(struct intel_vgpu *vgpu, + unsigned int port, unsigned int bytes, unsigned long val) +{ + if (intel_gvt_ops->emulate_cfg_write(vgpu, port, &val, bytes)) { + gvt_err("failed to write config space port 0x%x\n", port); + return false; + } + return true; +} + +static bool acrngt_read_cfg_space(struct intel_vgpu *vgpu, + unsigned int port, unsigned int bytes, unsigned long *val) +{ + unsigned long data; + + if (intel_gvt_ops->emulate_cfg_read(vgpu, port, &data, bytes)) { + gvt_err("failed to read config space port 0x%x\n", port); + return false; + } + memcpy(val, &data, bytes); + return true; +} + +static int acrngt_hvm_pio_emulation(struct intel_vgpu *vgpu, + struct vhm_request *req) +{ + if (req->reqs.pci_request.direction == REQUEST_READ) { + /* PIO READ */ + gvt_dbg_core("handle pio read emulation at port 0x%x\n", + req->reqs.pci_request.reg); + if (!acrngt_read_cfg_space(vgpu, + req->reqs.pci_request.reg, + req->reqs.pci_request.size, + (unsigned long *)&req->reqs.pci_request.value)) { + gvt_err("failed to read pio at addr 0x%x\n", + req->reqs.pci_request.reg); + return -EINVAL; + } + } else if (req->reqs.pci_request.direction == REQUEST_WRITE) { + /* PIO WRITE */ + gvt_dbg_core("handle pio write emulation at address 0x%x, " + "value 0x%x\n", + req->reqs.pci_request.reg, req->reqs.pci_request.value); + if (!acrngt_write_cfg_space(vgpu, + req->reqs.pci_request.reg, + req->reqs.pci_request.size, + (unsigned long)req->reqs.pci_request.value)) { + gvt_err("failed to write pio at addr 0x%x\n", + req->reqs.pci_request.reg); + return -EINVAL; + } + } + return 0; +} + +static int acrngt_hvm_write_handler(struct intel_vgpu *vgpu, uint64_t pa, + void *p_data, unsigned int bytes) +{ + + /* Check whether pa is ppgtt */ + if (intel_gvt_ops->write_protect_handler(vgpu, pa, p_data, bytes) == 0) + return 0; + + /* pa is mmio reg or gtt */ + return intel_gvt_ops->emulate_mmio_write(vgpu, pa, p_data, bytes); +} + +static int acrngt_hvm_mmio_emulation(struct intel_vgpu *vgpu, + struct vhm_request *req) +{ + if (req->reqs.mmio_request.direction == REQUEST_READ) { + /* MMIO READ */ + gvt_dbg_core("handle mmio read emulation at address 0x%llx\n", + req->reqs.mmio_request.address); + if (intel_gvt_ops->emulate_mmio_read(vgpu, + req->reqs.mmio_request.address, + &req->reqs.mmio_request.value, + req->reqs.mmio_request.size)) { + gvt_err("failed to read mmio at addr 0x%llx\n", + req->reqs.mmio_request.address); + return -EINVAL; + } + } else if (req->reqs.mmio_request.direction == REQUEST_WRITE) { + /* MMIO Write */ + if (acrngt_hvm_write_handler(vgpu, + req->reqs.mmio_request.address, + &req->reqs.mmio_request.value, + req->reqs.mmio_request.size)) { + gvt_err("failed to write mmio at addr 0x%llx\n", + req->reqs.mmio_request.address); + return -EINVAL; + } + gvt_dbg_core("handle mmio write emulation at address 0x%llx, " + "value 0x%llx\n", + req->reqs.mmio_request.address, req->reqs.mmio_request.value); + } + + return 0; +} + +static void handle_request_error(struct intel_vgpu *vgpu) +{ + mutex_lock(&vgpu->gvt->lock); + if (vgpu->failsafe == false) { + vgpu->failsafe= true; + gvt_err("Now vgpu %d will enter failsafe mode.\n", vgpu->id); + } + mutex_unlock(&vgpu->gvt->lock); +} + +static int acrngt_emulation_thread(void *priv) +{ + struct intel_vgpu *vgpu = (struct intel_vgpu *)priv; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)vgpu->handle; + struct vhm_request *req; + + int vcpu, ret; + int nr_vcpus = info->nr_vcpu; + + gvt_dbg_core("start kthread for VM%d\n", info->vm_id); + ASSERT(info->nr_vcpu <= MAX_HVM_VCPUS_SUPPORTED); + + set_freezable(); + while (1) { + acrn_ioreq_attach_client(info->client, 1); + + if (kthread_should_stop()) + return 0; + + for (vcpu = 0; vcpu < nr_vcpus; vcpu++) { + req = &info->req_buf[vcpu]; + if (atomic_read(&req->processed) == + REQ_STATE_PROCESSING && + req->client == info->client) { + gvt_dbg_core("handle ioreq type %d\n", + req->type); + switch (req->type) { + case REQ_PCICFG: + ret = acrngt_hvm_pio_emulation(vgpu, req); + break; + case REQ_MMIO: + case REQ_WP: + ret = acrngt_hvm_mmio_emulation(vgpu, req); + break; + default: + gvt_err("Unknown ioreq type %x\n", + req->type); + ret = -EINVAL; + break; + } + /* error handling */ + if (ret) + handle_request_error(vgpu); + + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); + /* complete request */ + if (acrn_ioreq_complete_request(info->client, + vcpu)) + gvt_err("failed complete request\n"); + } + } + } + + BUG(); /* It's actually impossible to reach here */ + return 0; +} + +struct intel_vgpu *acrngt_instance_create(domid_t vm_id, + struct intel_vgpu_type *vgpu_type) +{ + struct acrngt_hvm_dev *info; + struct intel_vgpu *vgpu; + int ret = 0; + struct task_struct *thread; + struct vm_info vm_info; + + gvt_dbg_core("acrngt_instance_create enter\n"); + if (!intel_gvt_ops || !acrngt_priv.gvt) + return NULL; + + vgpu = intel_gvt_ops->vgpu_create(acrngt_priv.gvt, vgpu_type); + if (IS_ERR(vgpu)) { + gvt_err("failed to create vgpu\n"); + return NULL; + } + + info = kzalloc(sizeof(struct acrngt_hvm_dev), GFP_KERNEL); + if (info == NULL) { + gvt_err("failed to alloc acrngt_hvm_dev\n"); + goto err; + } + + info->vm_id = vm_id; + info->vgpu = vgpu; + vgpu->handle = (unsigned long)info; + + if ((info->vm = find_get_vm(vm_id)) == NULL) { + gvt_err("failed to get vm %d\n", vm_id); + acrngt_instance_destroy(vgpu); + return NULL; + } + if (info->vm->req_buf == NULL) { + gvt_err("failed to get req buf for vm %d\n", vm_id); + goto err; + } + gvt_dbg_core("get vm req_buf from vm_id %d\n", vm_id); + + /* create client: no handler -> handle request by itself */ + info->client = acrn_ioreq_create_client(vm_id, NULL, "ioreq gvt-g"); + if (info->client < 0) { + gvt_err("failed to create ioreq client for vm id %d\n", vm_id); + goto err; + } + + /* get vm info */ + ret = vhm_get_vm_info(vm_id, &vm_info); + if (ret < 0) { + gvt_err("failed to get vm info for vm id %d\n", vm_id); + goto err; + } + + info->nr_vcpu = vm_info.max_vcpu; + + /* get req buf */ + info->req_buf = acrn_ioreq_get_reqbuf(info->client); + if (info->req_buf == NULL) { + gvt_err("failed to get req_buf for client %d\n", info->client); + goto err; + } + + /* trap config space access */ + acrn_ioreq_intercept_bdf(info->client, 0, 2, 0); + + thread = kthread_run(acrngt_emulation_thread, vgpu, + "acrngt_emulation:%d", vm_id); + if (IS_ERR(thread)) { + gvt_err("failed to run emulation thread for vm %d\n", vm_id); + goto err; + } + info->emulation_thread = thread; + gvt_dbg_core("create vgpu instance success, vm_id %d, client %d," + " nr_vcpu %d\n", info->vm_id,info->client, info->nr_vcpu); + + intel_gvt_ops->vgpu_activate(vgpu); + + return vgpu; + +err: + acrngt_instance_destroy(vgpu); + return NULL; +} + +static ssize_t kobj_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show) + ret = kattr->show(kobj, kattr, buf); + return ret; +} + +static ssize_t kobj_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store) + ret = kattr->store(kobj, kattr, buf, count); + return ret; +} + +const struct sysfs_ops acrngt_kobj_sysfs_ops = { + .show = kobj_attr_show, + .store = kobj_attr_store, +}; + +static ssize_t acrngt_sysfs_vgpu_id(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + + for (i = 0; i < GVT_MAX_VGPU_INSTANCE; i++) { + if (acrngt_priv.vgpus[i] && + (kobj == &((struct acrngt_hvm_dev *) + (acrngt_priv.vgpus[i]->handle))->kobj)) { + return sprintf(buf, "%d\n", acrngt_priv.vgpus[i]->id); + } + } + return 0; +} + +static struct kobj_attribute acrngt_vm_attr = +__ATTR(vgpu_id, 0440, acrngt_sysfs_vgpu_id, NULL); + + +static struct attribute *acrngt_vm_attrs[] = { + &acrngt_vm_attr.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type acrngt_instance_ktype = { + .sysfs_ops = &acrngt_kobj_sysfs_ops, + .default_attrs = acrngt_vm_attrs, +}; + +static int acrngt_sysfs_add_instance(struct acrngt_hvm_params *vp) +{ + int ret = 0; + struct intel_vgpu *vgpu; + struct acrngt_hvm_dev *info; + + struct intel_vgpu_type type = acrngt_priv.gvt->types[0]; + type.low_gm_size = vp->aperture_sz * VMEM_1MB; + type.high_gm_size = (vp->gm_sz - vp->aperture_sz) * VMEM_1MB; + type.fence = vp->fence_sz; + mutex_lock(&acrn_gvt_sysfs_lock); + vgpu = acrngt_instance_create(vp->vm_id, &type); + mutex_unlock(&acrn_gvt_sysfs_lock); + if (vgpu == NULL) { + gvt_err("acrngt_sysfs_add_instance failed.\n"); + ret = -EINVAL; + } else { + info = (struct acrngt_hvm_dev *) vgpu->handle; + info->vm_id = vp->vm_id; + acrngt_priv.vgpus[vgpu->id - 1] = vgpu; + gvt_dbg_core("add acrngt instance for vm-%d with vgpu-%d.\n", + vp->vm_id, vgpu->id); + + kobject_init(&info->kobj, &acrngt_instance_ktype); + info->kobj.kset = acrn_gvt_kset; + /* add kobject, NULL parent indicates using kset as parent */ + ret = kobject_add(&info->kobj, NULL, "vm%u", info->vm_id); + if (ret) { + gvt_err("%s: kobject add error: %d\n", __func__, ret); + kobject_put(&info->kobj); + } + } + + return ret; +} + +static struct intel_vgpu *vgpu_from_id(int vm_id) +{ + int i; + struct acrngt_hvm_dev *hvm_dev = NULL; + + /* vm_id is negtive in del_instance call */ + if (vm_id < 0) + vm_id = -vm_id; + for (i = 0; i < GVT_MAX_VGPU_INSTANCE; i++) + if (acrngt_priv.vgpus[i]) { + hvm_dev = (struct acrngt_hvm_dev *) + acrngt_priv.vgpus[i]->handle; + if (hvm_dev && (vm_id == hvm_dev->vm_id)) + return acrngt_priv.vgpus[i]; + } + return NULL; +} + +static int acrngt_sysfs_del_instance(struct acrngt_hvm_params *vp) +{ + int ret = 0; + struct intel_vgpu *vgpu = vgpu_from_id(vp->vm_id); + struct acrngt_hvm_dev *info = NULL; + + if (vgpu) { + info = (struct acrngt_hvm_dev *) vgpu->handle; + gvt_dbg_core("remove vm-%d sysfs node.\n", vp->vm_id); + kobject_put(&info->kobj); + + mutex_lock(&acrn_gvt_sysfs_lock); + acrngt_priv.vgpus[vgpu->id - 1] = NULL; + acrngt_instance_destroy(vgpu); + mutex_unlock(&acrn_gvt_sysfs_lock); + } + + return ret; +} + +static ssize_t acrngt_sysfs_instance_manage(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct acrngt_hvm_params vp; + int param_cnt; + char param_str[64]; + int rc; + int high_gm_sz; + int low_gm_sz; + + /* We expect the param_str should be vmid,a,b,c (where the guest + * wants a MB aperture and b MB gm, and c fence registers) or -vmid + * (where we want to release the gvt instance). + */ + (void)sscanf(buf, "%63s", param_str); + param_cnt = sscanf(param_str, "%d,%d,%d,%d", &vp.vm_id, + &low_gm_sz, &high_gm_sz, &vp.fence_sz); + gvt_dbg_core("create vm-%d sysfs node, low gm size %d," + " high gm size %d, fence size %d\n", + vp.vm_id, low_gm_sz, high_gm_sz, vp.fence_sz); + vp.aperture_sz = low_gm_sz; + vp.gm_sz = high_gm_sz + low_gm_sz; + if (param_cnt == 1) { + if (vp.vm_id >= 0) + return -EINVAL; + } else if (param_cnt == 4) { + if (!(vp.vm_id > 0 && vp.aperture_sz > 0 && + vp.aperture_sz <= vp.gm_sz && vp.fence_sz > 0)) + return -EINVAL; + } else { + gvt_err("%s: parameter counter incorrect\n", __func__); + return -EINVAL; + } + + rc = (vp.vm_id > 0) ? acrngt_sysfs_add_instance(&vp) : + acrngt_sysfs_del_instance(&vp); + + return rc < 0 ? rc : count; +} + +static ssize_t show_plane_owner(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "Planes:\nPipe A: %d %d %d %d\n" + "Pipe B: %d %d %d %d\nPipe C: %d %d %d\n", + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE1], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE2], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE1], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE2], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_SPRITE1]); +} + +static struct kobj_attribute acrngt_instance_attr = +__ATTR(create_gvt_instance, 0220, NULL, acrngt_sysfs_instance_manage); + +static struct kobj_attribute plane_owner_attr = +__ATTR(plane_owner_show, 0440, show_plane_owner, NULL); + +static struct attribute *acrngt_ctrl_attrs[] = { + &acrngt_instance_attr.attr, + &plane_owner_attr.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type acrngt_ctrl_ktype = { + .sysfs_ops = &acrngt_kobj_sysfs_ops, + .default_attrs = acrngt_ctrl_attrs, +}; + +int acrngt_sysfs_init(struct intel_gvt *gvt) +{ + int ret; + + acrn_gvt_kset = kset_create_and_add("gvt", NULL, kernel_kobj); + if (!acrn_gvt_kset) { + ret = -ENOMEM; + goto kset_fail; + } + + acrn_gvt_ctrl_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); + if (!acrn_gvt_ctrl_kobj) { + ret = -ENOMEM; + goto ctrl_fail; + } + + acrn_gvt_ctrl_kobj->kset = acrn_gvt_kset; + ret = kobject_init_and_add(acrn_gvt_ctrl_kobj, &acrngt_ctrl_ktype, + NULL, "control"); + if (ret) { + ret = -EINVAL; + goto kobj_fail; + } + + return 0; + +kobj_fail: + kobject_put(acrn_gvt_ctrl_kobj); +ctrl_fail: + kset_unregister(acrn_gvt_kset); +kset_fail: + return ret; +} + +void acrngt_sysfs_del(void) +{ + kobject_put(acrn_gvt_ctrl_kobj); + kset_unregister(acrn_gvt_kset); +} + +static int acrngt_host_init(struct device *dev, void *gvt, const void *ops) +{ + int ret = -EFAULT; + + if (!gvt || !ops) + return -EINVAL; + + acrngt_priv.gvt = (struct intel_gvt *)gvt; + intel_gvt_ops = (const struct intel_gvt_ops *)ops; + + ret = acrngt_sysfs_init(acrngt_priv.gvt); + if (ret) { + gvt_err("failed call acrngt_sysfs_init, error: %d\n", ret); + acrngt_priv.gvt = NULL; + intel_gvt_ops = NULL; + } + + return ret; +} + +static void acrngt_host_exit(struct device *dev, void *gvt) +{ + acrngt_sysfs_del(); + acrngt_priv.gvt = NULL; + intel_gvt_ops = NULL; +} + +static int acrngt_attach_vgpu(void *vgpu, unsigned long *handle) +{ + return 0; +} + +static void acrngt_detach_vgpu(unsigned long handle) +{ + return; +} + +static int acrngt_inject_msi(unsigned long handle, u32 addr_lo, u16 data) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("inject msi irq, addr 0x%x, data 0x%hx\n", addr_lo, data); + + ret = vhm_inject_msi(info->vm_id, addr_lo, data); + if (ret) + gvt_err("failed to inject msi for vm %d\n", info->vm_id); + return ret; +} + +static unsigned long acrngt_virt_to_mfn(void *addr) +{ + uint64_t gpa; + uint64_t hpa; + gvt_dbg_core("virt 0x%lx to mfn\n", (unsigned long)addr); + + gpa = virt_to_phys(addr); + hpa = vhm_vm_gpa2hpa(0, gpa); + + return (unsigned long) (hpa >> PAGE_SHIFT); +} + +static int acrngt_page_track_add(unsigned long handle, u64 gfn) +{ + int ret; + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("set wp page for gfn 0x%llx\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + ret = acrn_ioreq_add_iorange(info->client, REQ_WP, gfn << PAGE_SHIFT, + ((gfn + 1) << PAGE_SHIFT) - 1); + if (ret) { + gvt_err("failed acrn_ioreq_add_iorange for gfn 0x%llx\n", gfn); + return ret; + } + ret = write_protect_page(info->vm_id, gfn << PAGE_SHIFT, true); + if (ret) + gvt_err("failed set write protect for gfn 0x%llx\n", gfn); + return ret; +} + +static int acrngt_page_track_remove(unsigned long handle, u64 gfn) +{ + int ret; + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("unset wp page for gfx 0x%llx\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + ret = write_protect_page(info->vm_id, gfn << PAGE_SHIFT, false); + if (ret) { + gvt_err("failed update_memmap_attr unset for gfn 0x%llx\n", gfn); + return ret; + } + ret = acrn_ioreq_del_iorange(info->client, REQ_WP, gfn << PAGE_SHIFT, + ((gfn + 1) << PAGE_SHIFT) - 1); + if (ret) + gvt_err("failed acrn_ioreq_del_iorange for gfn 0x%llx\n", gfn); + return ret; +} + +static int acrngt_read_gpa(unsigned long handle, unsigned long gpa, + void *buf, unsigned long len) +{ + void *va = NULL; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("read gpa 0x%lx with len 0x%lx\n", gpa, len); + + va = map_guest_phys(info->vm_id, gpa, len); + if (!va) { + gvt_err("GVT: can not read gpa = 0x%lx!!!\n", gpa); + return -EFAULT; + } + + switch (len) + { + case 1: + *((uint8_t *) buf) = *((uint8_t *) va); + break; + case 2: + *((uint16_t *) buf) = *((uint16_t *) va); + break; + case 4: + *((uint32_t *) buf) = *((uint32_t *) va); + break; + case 8: + *((uint64_t *) buf) = *((uint64_t *) va); + break; + default: + memcpy(buf, va, len); + } + return 0; +} + +static int acrngt_write_gpa(unsigned long handle, unsigned long gpa, + void *buf, unsigned long len) +{ + void *va = NULL; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("write gpa 0x%lx with len 0x%lx\n", gpa, len); + + va = map_guest_phys(info->vm_id, gpa, len); + if (!va) { + gvt_err("GVT: can not write gpa = 0x%lx!!!\n", gpa); + return -EFAULT; + } + + switch (len) + { + case 1: + *((uint8_t *) va) = *((uint8_t *) buf); + break; + case 2: + *((uint16_t *) va) = *((uint16_t *) buf); + break; + case 4: + *((uint32_t *) va) = *((uint32_t *) buf); + break; + case 8: + *((uint64_t *) va) = *((uint64_t *) buf); + break; + default: + memcpy(va, buf, len); + } + return 0; +} + +static unsigned long acrngt_gfn_to_pfn(unsigned long handle, unsigned long gfn) +{ + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("convert gfn 0x%lx to pfn\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + return hpa >> PAGE_SHIFT; +} + +static int acrngt_map_gfn_to_mfn(unsigned long handle, unsigned long gfn, + unsigned long mfn, unsigned int nr, bool map) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("map/unmap gfn 0x%lx to mfn 0x%lx with %u pages, map %d\n", + gfn, mfn, nr, map); + + if (map) + ret = add_memory_region(info->vm_id, gfn << PAGE_SHIFT, + mfn << PAGE_SHIFT, nr << PAGE_SHIFT, + MEM_TYPE_UC, MEM_ACCESS_RWX); + else + ret = del_memory_region(info->vm_id, gfn << PAGE_SHIFT, + nr << PAGE_SHIFT); + if (ret) + gvt_err("failed map/unmap gfn 0x%lx to mfn 0x%lx with %u pages," + " map %d\n", gfn, mfn, nr, map); + return ret; +} + +static int acrngt_set_trap_area(unsigned long handle, u64 start, + u64 end, bool map) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("set trap area, start 0x%llx, end 0x%llx, map %d\n", + start, end, map); + + if (map) + ret = acrn_ioreq_add_iorange(info->client, REQ_MMIO, + start, end); + else + ret = acrn_ioreq_del_iorange(info->client, REQ_MMIO, + start, end); + if (ret) + gvt_err("failed set trap, start 0x%llx, end 0x%llx, map %d\n", + start, end, map); + return ret; +} + +static int acrngt_dom0_ready(void) +{ + char *env[] = {"GVT_DOM0_READY=1", NULL}; + if(!acrn_gvt_ctrl_kobj) + return 0; + gvt_dbg_core("acrngt: Dom 0 ready to accept Dom U guests\n"); + return kobject_uevent_env(acrn_gvt_ctrl_kobj, KOBJ_ADD, env); +} + +static int acrngt_dma_map_guest_page(unsigned long handle, unsigned long gfn, + unsigned long size, dma_addr_t *dma_addr) +{ + unsigned long pfn; + + pfn = acrngt_gfn_to_pfn(handle, gfn); + *dma_addr = pfn << PAGE_SHIFT; + + return 0; +} + +static void acrngt_dma_unmap_guest_page(unsigned long handle, + dma_addr_t dma_addr) +{ +} + +struct intel_gvt_mpt acrn_gvt_mpt = { + //.detect_host = acrngt_detect_host, + .host_init = acrngt_host_init, + .host_exit = acrngt_host_exit, + .attach_vgpu = acrngt_attach_vgpu, + .detach_vgpu = acrngt_detach_vgpu, + .inject_msi = acrngt_inject_msi, + .from_virt_to_mfn = acrngt_virt_to_mfn, + .enable_page_track = acrngt_page_track_add, + .disable_page_track = acrngt_page_track_remove, + .read_gpa = acrngt_read_gpa, + .write_gpa = acrngt_write_gpa, + .gfn_to_mfn = acrngt_gfn_to_pfn, + .map_gfn_to_mfn = acrngt_map_gfn_to_mfn, + .dma_map_guest_page = acrngt_dma_map_guest_page, + .dma_unmap_guest_page = acrngt_dma_unmap_guest_page, + .set_trap_area = acrngt_set_trap_area, + .dom0_ready = acrngt_dom0_ready, +}; +EXPORT_SYMBOL_GPL(acrn_gvt_mpt); + +static int __init acrngt_init(void) +{ + /* todo: to support need implment check_gfx_iommu_enabled func */ + gvt_dbg_core("acrngt loaded\n"); + return 0; +} + +static void __exit acrngt_exit(void) +{ + gvt_dbg_core("acrngt: unloaded\n"); +} + +module_init(acrngt_init); +module_exit(acrngt_exit); diff --git a/drivers/gpu/drm/i915/gvt/acrngt.h b/drivers/gpu/drm/i915/gvt/acrngt.h new file mode 100644 index 000000000000..0799df2ec557 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/acrngt.h @@ -0,0 +1,81 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef INTEL_GVT_ACRNGT_H +#define INTEL_GVT_ACRNGT_H + +extern struct intel_gvt *gvt_instance; +extern const struct intel_gvt_ops *acrn_intel_gvt_ops; + +#define MAX_HVM_VCPUS_SUPPORTED 127 + +#define VMEM_1MB (1ULL << 20) /* the size of the first 1MB */ + +typedef uint16_t domid_t; + +/* + * acrngt_hvm_dev is a wrapper of a vGPU instance which is reprensented by the + * intel_vgpu structure. Under acrn hypervisor, the acrngt_instance stands for a + * HVM device, which the related resource. + */ +struct acrngt_hvm_dev { + domid_t vm_id; + struct kobject kobj; + struct intel_vgpu *vgpu; + + int nr_vcpu; + struct task_struct *emulation_thread; + + int client; + struct vhm_request *req_buf; + struct vhm_vm *vm; +}; + +struct acrngt_hvm_params { + int vm_id; + int aperture_sz; /* in MB */ + int gm_sz; /* in MB */ + int fence_sz; +}; + +/* + * struct gvt_acrngt should be a single instance to share global + * information for ACRNGT module. + */ +#define GVT_MAX_VGPU_INSTANCE 15 +struct gvt_acrngt { + struct intel_gvt *gvt; + struct intel_vgpu *vgpus[GVT_MAX_VGPU_INSTANCE]; +}; + +static ssize_t acrngt_sysfs_instance_manage(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t acrngt_sysfs_vgpu_id(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +struct intel_vgpu *acrngt_instance_create(domid_t vm_id, + struct intel_vgpu_type *type); +void acrngt_instance_destroy(struct intel_vgpu *vgpu); + +#endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 25e0a58a24a5..6261af450ee4 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -44,6 +44,7 @@ struct intel_gvt_host intel_gvt_host; static const char * const supported_hypervisors[] = { [INTEL_GVT_HYPERVISOR_XEN] = "XEN", [INTEL_GVT_HYPERVISOR_KVM] = "KVM", + [INTEL_GVT_HYPERVISOR_ACRN] = "ACRN", }; static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt, @@ -221,6 +222,11 @@ int intel_gvt_init_host(void) symbol_get(kvmgt_mpt), "kvmgt"); intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_KVM; #endif + /* not in Xen. Try ACRN */ + intel_gvt_host.mpt = try_then_request_module( + symbol_get(acrn_gvt_mpt), "acrn_gvt"); + intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_ACRN; + printk("acrngt %s\n", intel_gvt_host.mpt?"found":"not found"); } /* Fail to load MPT modules - bail out */ diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 9c291924c1d0..d673c46e3179 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -55,6 +55,7 @@ enum { INTEL_GVT_HYPERVISOR_XEN = 0, INTEL_GVT_HYPERVISOR_KVM, + INTEL_GVT_HYPERVISOR_ACRN, }; struct intel_gvt_host { diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index f14cff32ae2f..d4b7929c8bee 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -69,5 +69,6 @@ struct intel_gvt_mpt { extern struct intel_gvt_mpt xengt_mpt; extern struct intel_gvt_mpt kvmgt_mpt; +extern struct intel_gvt_mpt acrn_gvt_mpt; #endif /* _GVT_HYPERCALL_H_ */ From f6f91287ca27b8205884e903423115218dc72561 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 29 Dec 2017 19:14:16 +0800 Subject: [PATCH 1036/1276] drm/i915/gvt: hard code Pipe B plane owner to UOS It is a work around patch due to plane restriction patches are not porting Change-Id: If09ff8c40254ec275dc2d9b9674d7267d306a7e7 Signed-off-by: Fei Jiang Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/acrngt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index fec5751a4bed..346a676d77bc 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -431,6 +431,13 @@ static int acrngt_sysfs_add_instance(struct acrngt_hvm_params *vp) struct acrngt_hvm_dev *info; struct intel_vgpu_type type = acrngt_priv.gvt->types[0]; + + /* todo: wa patch due to plane restriction patches are not porting */ + acrngt_priv.gvt->pipe_info[1].plane_owner[0] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[1] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[2] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[3] = 1; + type.low_gm_size = vp->aperture_sz * VMEM_1MB; type.high_gm_size = (vp->gm_sz - vp->aperture_sz) * VMEM_1MB; type.fence = vp->fence_sz; From d7323a144bab572f90ecfceb7880eec6d243c5ee Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1037/1276] drm/i915/gvt: remove some initialization of ggtt in GVTg guest This patch removed the initialization for the ggtt holes in GVT-g guest. So that: 1. can improve the guest boot up time; 2. avoid boot failure issue when i915.enable_guc_loading != 0. Signed-off-by: Min He Reviewed-by: Vivek Kasireddy Reviewed-by: Singh, Satyeshwar (cherry picked from commit d1ca0614ef13513202362994d5506ae9a33b3483) Change-Id: I2d854d5fea65dfbd92c12b9426fa882075d766e1 Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_gem_gtt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index f00c7fbef79e..6e792e3167a4 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2949,16 +2949,19 @@ int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) if (ret) return ret; - /* Clear any non-preallocated blocks */ - drm_mm_for_each_hole(entry, &ggtt->vm.mm, hole_start, hole_end) { - DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", + if (!intel_vgpu_active(dev_priv)) { + /* Clear any non-preallocated blocks */ + drm_mm_for_each_hole(entry, &ggtt->vm.mm, hole_start, hole_end) { + DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", hole_start, hole_end); - ggtt->vm.clear_range(&ggtt->vm, hole_start, + ggtt->vm.clear_range(&ggtt->vm, hole_start, hole_end - hole_start); - } + } - /* And finally clear the reserved guard page */ - ggtt->vm.clear_range(&ggtt->vm, ggtt->vm.total - PAGE_SIZE, PAGE_SIZE); + /* And finally clear the reserved guard page */ + ggtt->vm.clear_range(&ggtt->vm, ggtt->vm.total - PAGE_SIZE, PAGE_SIZE); + + } if (USES_PPGTT(dev_priv) && !USES_FULL_PPGTT(dev_priv)) { ret = i915_gem_init_aliasing_ppgtt(dev_priv); From cc4c2720c4d2f5605dbf0b3f35140a6f934530bb Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1038/1276] drm/i915/gvt: avoid unncessary reset in GVT-g guest When i915 boots up, it will trigger a reset, but it's unnecessary in GVT-g environment, so remove this reset which can reduce guest boot time. Change-Id: Id8c120c3229118af3c41fb1ef4ddbfbf71cb69fe Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fcc73a6ab503..c3e87d10f14e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5027,7 +5027,8 @@ void i915_gem_sanitize(struct drm_i915_private *i915) * of the reset, so this could be applied to even earlier gen. */ err = -ENODEV; - if (INTEL_GEN(i915) >= 5 && intel_has_gpu_reset(i915)) + if (INTEL_GEN(i915) >= 5 && intel_has_gpu_reset(i915) && + !intel_vgpu_active(i915)) err = WARN_ON(intel_gpu_reset(i915, ALL_ENGINES)); if (!err) intel_engines_sanitize(i915); From 077c3c6d5ea73b923c002fef3ff7bcfa326d1c00 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 29 Dec 2017 18:48:22 +0800 Subject: [PATCH 1039/1276] drm/i915/gvt: add param disable_gvt_fw_loading to disable gvt fw loading when gvt fw doesn't exist, try to load fw from initrd will cost about 1 minute, add disable_gvt_fw_loading to speed up sos boot up time. Change-Id: I07aca795057fec09ff9ee729a65a5f96006c5ef8 Signed-off-by: Fei Jiang Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/firmware.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/firmware.c b/drivers/gpu/drm/i915/gvt/firmware.c index 4ac18b447247..f0d30237c988 100644 --- a/drivers/gpu/drm/i915/gvt/firmware.c +++ b/drivers/gpu/drm/i915/gvt/firmware.c @@ -199,6 +199,7 @@ static int verify_firmware(struct intel_gvt *gvt, #define GVT_FIRMWARE_PATH "i915/gvt" +bool disable_gvt_fw_loading=true; /** * intel_gvt_load_firmware - load GVT firmware * @gvt: intel gvt device @@ -216,27 +217,27 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt) void *mem; int ret; - path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!path) - return -ENOMEM; - mem = kmalloc(info->cfg_space_size, GFP_KERNEL); - if (!mem) { - kfree(path); + if (!mem) return -ENOMEM; - } firmware->cfg_space = mem; mem = kmalloc(info->mmio_size, GFP_KERNEL); if (!mem) { - kfree(path); kfree(firmware->cfg_space); return -ENOMEM; } firmware->mmio = mem; + if (disable_gvt_fw_loading) + goto expose_firmware; + + path = kmalloc(PATH_MAX, GFP_KERNEL); + if (!path) + return -ENOMEM; + sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state", GVT_FIRMWARE_PATH, pdev->vendor, pdev->device, pdev->revision); From eac35d73d6129b8bc94c3bf9a3b19e88ced7949c Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1040/1276] drm/i915/gvt: inject error interrupt to DomU when GPU hang When GVT finds a request from DomU causes GPU hang, it will trigger an error interrupt to guest, so that DomU can trigger a virtual GPU reset. Change-Id: I49d9339e99ebfdbe9b158ba311655ab356562bae Signed-off-by: Min He Signed-off-by: Satyeshwar Singh Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/interrupt.c | 7 +++++++ drivers/gpu/drm/i915/gvt/interrupt.h | 2 ++ drivers/gpu/drm/i915/gvt/scheduler.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/scheduler.h | 1 + 4 files changed, 37 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index d749d46bc05b..06ce906b6673 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -69,6 +69,7 @@ static const char * const irq_name[INTEL_GVT_EVENT_MAX] = { [VCS_PAGE_DIRECTORY_FAULT] = "Video page directory faults", [VCS_AS_CONTEXT_SWITCH] = "Video AS Context Switch Interrupt", [VCS2_MI_USER_INTERRUPT] = "VCS2 Video CS MI USER INTERRUPT", + [VCS2_CMD_STREAMER_ERR] = "VCS2 Video CS error interrupt", [VCS2_MI_FLUSH_DW] = "VCS2 Video MI FLUSH DW notify", [VCS2_AS_CONTEXT_SWITCH] = "VCS2 Context Switch Interrupt", @@ -524,21 +525,26 @@ static void gen8_init_irq( /* GEN8 interrupt GT0 events */ SET_BIT_INFO(irq, 0, RCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT0); + SET_BIT_INFO(irq, 3, RCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 4, RCS_PIPE_CONTROL, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 8, RCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 16, BCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT0); + SET_BIT_INFO(irq, 19, BCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 20, BCS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 24, BCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT0); /* GEN8 interrupt GT1 events */ SET_BIT_INFO(irq, 0, VCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT1); + SET_BIT_INFO(irq, 3, VCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 4, VCS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 8, VCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT1); if (HAS_BSD2(gvt->dev_priv)) { SET_BIT_INFO(irq, 16, VCS2_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT1); + SET_BIT_INFO(irq, 19, VCS2_CMD_STREAMER_ERR, + INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 20, VCS2_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 24, VCS2_AS_CONTEXT_SWITCH, @@ -547,6 +553,7 @@ static void gen8_init_irq( /* GEN8 interrupt GT3 events */ SET_BIT_INFO(irq, 0, VECS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT3); + SET_BIT_INFO(irq, 3, VECS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT3); SET_BIT_INFO(irq, 4, VECS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT3); SET_BIT_INFO(irq, 8, VECS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT3); diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index f7d7ade4f13c..6ec761a84557 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -53,6 +53,7 @@ enum intel_gvt_event_type { VCS_AS_CONTEXT_SWITCH, VCS2_MI_USER_INTERRUPT, + VCS2_CMD_STREAMER_ERR, VCS2_MI_FLUSH_DW, VCS2_AS_CONTEXT_SWITCH, @@ -64,6 +65,7 @@ enum intel_gvt_event_type { BCS_AS_CONTEXT_SWITCH, VECS_MI_USER_INTERRUPT, + VECS_CMD_STREAMER_ERR, VECS_MI_FLUSH_DW, VECS_AS_CONTEXT_SWITCH, diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 7243324cb93c..13f83135f5b1 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -682,6 +682,7 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) ret = prepare_workload(workload); + workload->guilty_count = atomic_read(&workload->req->gem_context->guilty_count); out: if (ret) workload->status = ret; @@ -898,6 +899,9 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) list_del_init(&workload->list); + if (workload->status == -EIO) + intel_vgpu_reset_submission(vgpu, 1 << ring_id); + if (!workload->status) { release_shadow_batch_buffer(workload); if(gvt_shadow_wa_ctx) @@ -933,6 +937,18 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) mutex_unlock(&vgpu->vgpu_lock); } +static void inject_error_cs_irq(struct intel_vgpu *vgpu, int ring_id) +{ + enum intel_gvt_event_type events[] = { + RCS_CMD_STREAMER_ERR, + BCS_CMD_STREAMER_ERR, + VCS_CMD_STREAMER_ERR, + VCS2_CMD_STREAMER_ERR, + VECS_CMD_STREAMER_ERR, + }; + intel_vgpu_trigger_virtual_event(vgpu, events[ring_id]); +} + struct workload_thread_param { struct intel_gvt *gvt; int ring_id; @@ -1002,6 +1018,17 @@ static int workload_thread(void *priv) if (lret >= 0 && workload->status == -EINPROGRESS) workload->status = 0; + /* + * increased guilty_count means that this request triggerred + * a GPU reset, so we need to notify the guest about the + * hang. + */ + if (workload->guilty_count < + atomic_read(&workload->req->gem_context->guilty_count)) { + workload->status = -EIO; + inject_error_cs_irq(workload->vgpu, ring_id); + } + complete: gvt_dbg_sched("will complete workload %p, status: %d\n", workload, workload->status); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index ca5529d0e48e..043c2ff07a7c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -84,6 +84,7 @@ struct intel_vgpu_workload { /* if this workload has been dispatched to i915? */ bool dispatched; int status; + unsigned int guilty_count; struct intel_vgpu_mm *shadow_mm; From 22b132c8424cc915b3273449741959dbabea06d0 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1041/1276] drm/i915/gvt: Added error interrupt handler for GVT-g guest An draft version of adding error interrupt handler in GVT-g guest, to trigger a GPU reset when receiving the CS error interrupt. v2: Updated by JH - made the worker object local to the engine rather than a global. Also, don't re-initialise it in the interrupt handler otherwise null pointer dereferences ensue when the interrupts occur too quickly. In the Xen virtualised environment, the TDR is a co-operative effort between the host and guest domains. The actual detection is done entirely by the host as only it really knows what the hardware is doing. The guest still needs to do all the recovery processing when informed about a TDR event occuring by the host. However, the guest should not attempt to do the detection itself. Change-Id: I3edac5211f9878725b14abaab7cacf9048ccd620 Signed-off-by: Min He Signed-off-by: John Harrison Signed-off-by: Satyeshwar Singh Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_debugfs.c | 3 +++ drivers/gpu/drm/i915/i915_irq.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/intel_hangcheck.c | 3 +++ drivers/gpu/drm/i915/intel_lrc.c | 12 ++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 1 + 5 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f9ce35da4123..892a5e7f0648 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -4000,6 +4000,9 @@ i915_wedged_set(void *data, u64 val) struct intel_engine_cs *engine; unsigned int tmp; + if (intel_vgpu_active(i915)) + return -EINVAL; + /* * There is no safeguard against this debugfs entry colliding * with the hangcheck calling same i915_handle_error() in diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0b16775e292c..71977fb8725c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1518,6 +1518,12 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir) tasklet |= USES_GUC_SUBMISSION(engine->i915); } + if ((iir & (GT_RENDER_CS_MASTER_ERROR_INTERRUPT)) && + intel_vgpu_active(engine->i915)) { + queue_work(system_highpri_wq, &engine->reset_work); + return; + } + if (tasklet) tasklet_hi_schedule(&engine->execlists.tasklet); } @@ -4159,6 +4165,19 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) if (HAS_L3_DPF(dev_priv)) gt_interrupts[0] |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; + if (intel_vgpu_active(dev_priv)) { + gt_interrupts[0] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_RCS_IRQ_SHIFT | + GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_BCS_IRQ_SHIFT; + gt_interrupts[1] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VCS1_IRQ_SHIFT | + GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VCS2_IRQ_SHIFT; + gt_interrupts[3] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VECS_IRQ_SHIFT; + } + dev_priv->pm_ier = 0x0; dev_priv->pm_imr = ~dev_priv->pm_ier; GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]); diff --git a/drivers/gpu/drm/i915/intel_hangcheck.c b/drivers/gpu/drm/i915/intel_hangcheck.c index 2fc7a0dd0df9..1f7da1cfd4b2 100644 --- a/drivers/gpu/drm/i915/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/intel_hangcheck.c @@ -418,6 +418,9 @@ static void i915_hangcheck_elapsed(struct work_struct *work) if (!i915_modparams.enable_hangcheck) return; + if (intel_vgpu_active(dev_priv)) + return; + if (!READ_ONCE(dev_priv->gt.awake)) return; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 174479232e94..db2cb423e88f 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2369,6 +2369,16 @@ logical_ring_default_irqs(struct intel_engine_cs *engine) engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift; } +static void i915_error_reset(struct work_struct *work) { + struct intel_engine_cs *engine = + container_of(work, struct intel_engine_cs, + reset_work); + i915_handle_error(engine->i915, 1 << engine->id, + I915_ERROR_CAPTURE, + "Received error interrupt from engine %d", + engine->id); +} + static void logical_ring_setup(struct intel_engine_cs *engine) { @@ -2382,6 +2392,8 @@ logical_ring_setup(struct intel_engine_cs *engine) logical_ring_default_vfuncs(engine); logical_ring_default_irqs(engine); + + INIT_WORK(&engine->reset_work, i915_error_reset); } static bool csb_force_mmio(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index f5ffa6d31e82..93daa92232e0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -567,6 +567,7 @@ struct intel_engine_cs { } semaphore; struct intel_engine_execlists execlists; + struct work_struct reset_work; /* Contexts are pinned whilst they are active on the GPU. The last * context executed remains active whilst the GPU is idle - the From 6abfb2d2ede690643ad44b6ad88e70f7dfc6f7ca Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 22 Mar 2018 17:01:07 +0800 Subject: [PATCH 1042/1276] drm/i915/gvt: Add the support of HUC_STATUS2 reg emulation for Guest VGPU The HUC_STATUS2 reg is used to indicate whether the Huc FW is loaded. Only when it is loaded successfully, the user-space driver on guest can use the Huc to do the expected operation. This provides the support of HUC_STATUS2 trap for guest VGPU. Signed-off-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 2 ++ drivers/gpu/drm/i915/gvt/mmio.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 495d6a298924..91fe0a44367d 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -3017,6 +3017,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x4ab8), D_KBL); MMIO_D(_MMIO(0x2248), D_KBL | D_SKL); + MMIO_D(HUC_STATUS2, D_SKL_PLUS); + return 0; } diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 4149eae235b5..878a8a1f5ff5 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -235,6 +235,7 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) struct intel_gvt *gvt = vgpu->gvt; const struct intel_gvt_device_info *info = &gvt->device_info; void *mmio = gvt->firmware.mmio; + struct drm_i915_private *dev_priv = gvt->dev_priv; if (dmlr) { memcpy(vgpu->mmio.vreg, mmio, info->mmio_size); @@ -291,6 +292,12 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) vgpu_vreg(vgpu, 0xe681c) = 1 << 17; vgpu_vreg(vgpu, 0xe6c04) = 3; vgpu_vreg(vgpu, 0xe6e1c) = 0x2f << 16; + + if (HAS_HUC_UCODE(dev_priv)) { + mmio_hw_access_pre(dev_priv); + vgpu_vreg_t(vgpu, HUC_STATUS2) = I915_READ(HUC_STATUS2); + mmio_hw_access_post(dev_priv); + } } /** From 2001c0b76e8abd56ed0be6aca61aef275dc376e7 Mon Sep 17 00:00:00 2001 From: Mitul Chokshi Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1043/1276] drm/i915/gvt: Add vgt-id in context id Context id is reported to OABUFFER along with performance statistics counter. So when performance monitoring code gets the Context id, it can extract vm-id to identify which VM submitted given context. v2: define SIZE_CONTEXT_HW_ID even when CONFIG_DRM_I915_GVT is not set, which fixes RTC defect 192523. Change-Id: I7e8ed7f741e2f41e2e9da4b7bdd463dfd3e2fe12 Signed-off-by: Mitul Chokshi Signed-off-by: Daniel van der Wath Reviewed-by: Singh, Satyeshwar Reviewed-by: Adebisi, YetundeX Verified-by: Van Der Wath, Daniel J (cherry picked from commit 3e52d8f679eb93c2c1eb7e0f69de34c7d78260d2) (cherry picked from commit 74c64f2595b70a2079c28bbda960fff7d3e99289) Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: He, Min Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/i915_gem_context.c | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4aca5344863d..bfb655ec6996 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1837,6 +1837,10 @@ struct drm_i915_private { * This is limited in execlists to 21 bits. */ struct ida hw_ida; + + /* In case of virtualization, 3-bits of vgt-id will be added to hw_id */ +#define SIZE_CONTEXT_HW_ID_GVT (18) +#define MAX_CONTEXT_HW_ID_GVT (1<link); - ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id); + if (intel_vgpu_active(ctx->i915)) + ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id & + ~(0x7 << SIZE_CONTEXT_HW_ID_GVT)); + else + ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id); + kfree_rcu(ctx, rcu); } @@ -217,6 +223,8 @@ static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) */ if (USES_GUC_SUBMISSION(dev_priv)) max = MAX_GUC_CONTEXT_HW_ID; + else if (intel_vgpu_active(dev_priv) || intel_gvt_active(dev_priv)) + max = MAX_CONTEXT_HW_ID_GVT; else max = MAX_CONTEXT_HW_ID; } @@ -236,6 +244,12 @@ static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) return ret; } + if (intel_vgpu_active(dev_priv)) { + /* add vgpu_id to context hw_id */ + ret = ret | (I915_READ(vgtif_reg(vgt_id)) + << SIZE_CONTEXT_HW_ID_GVT); + } + *out = ret; return 0; } From 316a9cdd6b0bbc85534b7279e908bb95837e6c3a Mon Sep 17 00:00:00 2001 From: Min He Date: Thu, 4 Jan 2018 21:48:47 +0800 Subject: [PATCH 1044/1276] drm/i915/gvt: show pid/hw_id of current DomU process in debugfs v1: show pid and hw id of current DomU process when showing shadow context status This allows us to identify which process a domu workload has come from. v2: expose HW context id to debugfs This patch expose the HW context id to the debugfs node, so that vtune can utilize this context id to match with the one exposed by MD API. v3: When storing DomU pid and hw id in the HWS page, offset them by the vgpu id. When there were multiple DomUs running, they would all write their pid to the same address in the HWS page. When we checked i915_context_status each shadow context would show the same current pid and hw id. By offsetting the writes by the DomU's ID, we can see the details for each shadow context correctly. This fixes defect 201282. Change-Id: I106fae75af5963f043286acd604d3bab02b87c17 Signed-off-by: Min He Signed-off-by: Daniel van der Wath Signed-off-by: Fei Jiang Reviewed-by: Singh, Satyeshwar Reviewed-by: Abes, Brahim Reviewed-by: He, Min Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 38 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_debugfs.c | 26 ++++++++++++++++- drivers/gpu/drm/i915/intel_lrc.c | 8 ++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 5 ++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 13f83135f5b1..7c3636e3a725 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -435,6 +435,38 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload) return ret; } +static void gen8_shadow_pid_cid(struct intel_vgpu_workload *workload) +{ + int ring_id = workload->ring_id; + struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv; + struct intel_engine_cs *engine = dev_priv->engine[ring_id]; + u32 *cs; + + /* Copy the PID and CID from the guest's HWS page to the host's one */ + cs = intel_ring_begin(workload->req, 16); + *cs++ = MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = (workload->ctx_desc.lrca << I915_GTT_PAGE_SHIFT) + + I915_GEM_HWS_PID_ADDR; + *cs++ = 0; + *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = engine->status_page.ggtt_offset + I915_GEM_HWS_PID_ADDR + + (workload->vgpu->id << MI_STORE_DWORD_INDEX_SHIFT); + *cs++ = 0; + *cs++ = MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = (workload->ctx_desc.lrca << I915_GTT_PAGE_SHIFT) + + I915_GEM_HWS_CID_ADDR; + *cs++ = 0; + *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = engine->status_page.ggtt_offset + I915_GEM_HWS_CID_ADDR + + (workload->vgpu->id << MI_STORE_DWORD_INDEX_SHIFT); + *cs++ = 0; + intel_ring_advance(workload->req, cs); +} + static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload); static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload) @@ -633,6 +665,8 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } + gen8_shadow_pid_cid(workload); + ret = prepare_shadow_batch_buffer(workload); if (ret) { gvt_vgpu_err("fail to prepare_shadow_batch_buffer\n"); @@ -1180,6 +1214,10 @@ int intel_vgpu_setup_submission(struct intel_vgpu *vgpu) if (IS_ERR(s->shadow_ctx)) return PTR_ERR(s->shadow_ctx); + if (!s->shadow_ctx->name) { + s->shadow_ctx->name = kasprintf(GFP_KERNEL, "Shadow Context %d", vgpu->id); + } + bitmap_zero(s->shadow_ctx_desc_updated, I915_NUM_ENGINES); s->workloads = kmem_cache_create_usercopy("gvt-g_vgpu_workload", diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 892a5e7f0648..f575aff83c9b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1939,6 +1939,19 @@ static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring) ring->space, ring->head, ring->tail, ring->emit); } +static void describe_ctx_ring_shadowed(struct seq_file *m, + struct i915_gem_context *ctx, struct intel_ring *ring, + struct intel_engine_cs *engine) +{ + int pid, cid, vgt_id; + + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + pid = intel_read_status_page(engine, I915_GEM_HWS_PID_INDEX + vgt_id); + cid = intel_read_status_page(engine, I915_GEM_HWS_CID_INDEX + vgt_id); + seq_printf(m, " (Current DomU Process PID: %d, CID: %d)", + pid, cid); +} + static int i915_context_status(struct seq_file *m, void *unused) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -1953,6 +1966,7 @@ static int i915_context_status(struct seq_file *m, void *unused) return ret; list_for_each_entry(ctx, &dev_priv->contexts.list, link) { + bool is_shadow_context = false; seq_printf(m, "HW context %u ", ctx->hw_id); if (ctx->pid) { struct task_struct *task; @@ -1963,6 +1977,9 @@ static int i915_context_status(struct seq_file *m, void *unused) task->comm, task->pid); put_task_struct(task); } + } else if (ctx->name && !strncmp(ctx->name, "Shadow Context", 14)) { + seq_puts(m, "DomU Shadow Context "); + is_shadow_context = true; } else if (IS_ERR(ctx->file_priv)) { seq_puts(m, "(deleted) "); } else { @@ -1975,12 +1992,19 @@ static int i915_context_status(struct seq_file *m, void *unused) for_each_engine(engine, dev_priv, id) { struct intel_context *ce = to_intel_context(ctx, engine); + u64 lrc_desc = ce->lrc_desc; + seq_printf(m, "ctx id 0x%x ", (uint32_t)((lrc_desc >> 12) & + 0xFFFFF)); seq_printf(m, "%s: ", engine->name); if (ce->state) describe_obj(m, ce->state->obj); - if (ce->ring) + if (ce->ring) { describe_ctx_ring(m, ce->ring); + if(is_shadow_context) + describe_ctx_ring_shadowed(m, ctx, + ce->ring, engine); + } seq_putc(m, '\n'); } diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index db2cb423e88f..5dde7253c963 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2724,6 +2724,14 @@ populate_lr_context(struct i915_gem_context *ctx, _MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT | CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT); + /* write the context's pid and hw_id/cid to the per-context HWS page */ + if(intel_vgpu_active(engine->i915) && pid_nr(ctx->pid)) { + *(u32*)(vaddr + LRC_PPHWSP_PN * PAGE_SIZE + I915_GEM_HWS_PID_ADDR) + = pid_nr(ctx->pid) & 0x3fffff; + *(u32*)(vaddr + LRC_PPHWSP_PN * PAGE_SIZE + I915_GEM_HWS_CID_ADDR) + = ctx->hw_id & 0x3fffff; + } + err_unpin_ctx: i915_gem_object_unpin_map(ctx_obj); return ret; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 93daa92232e0..ff42345d735b 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -790,6 +790,11 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value) #define I915_GEM_HWS_SCRATCH_INDEX 0x40 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT) +#define I915_GEM_HWS_PID_INDEX 0x50 +#define I915_GEM_HWS_PID_ADDR (I915_GEM_HWS_PID_INDEX << MI_STORE_DWORD_INDEX_SHIFT) +#define I915_GEM_HWS_CID_INDEX 0x58 +#define I915_GEM_HWS_CID_ADDR (I915_GEM_HWS_CID_INDEX << MI_STORE_DWORD_INDEX_SHIFT) + #define I915_HWS_CSB_BUF0_INDEX 0x10 #define I915_HWS_CSB_WRITE_INDEX 0x1f #define CNL_HWS_CSB_WRITE_INDEX 0x2f From b6ec472d8d5494b4aaf31b7bbd77fd1a64d717ad Mon Sep 17 00:00:00 2001 From: Brahim Abes Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 1045/1276] drm/i915/gvt: Add new trace point to output per domain info Added trace point "i915_gem_request_add_domain" that prints the following extra fields per each packet: -is_shadow_ctx: Check for Dom0 or guest domains -hw_id: To check against i915_context_status's HW context id -vgt_id: The host or guests domain ID -pid: Process ID submitting the request Change-Id: I3a71e1d5909260df5a07c98291ee9e908f698ea2 Signed-off-by: Brahim Abes Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: He, Min Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_debugfs.c | 30 ++++++++++++++++++ drivers/gpu/drm/i915/i915_request.c | 1 + drivers/gpu/drm/i915/i915_trace.h | 47 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 5 +++ 4 files changed, 83 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f575aff83c9b..ff6d2c8b41bb 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1939,6 +1939,36 @@ static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring) ring->space, ring->head, ring->tail, ring->emit); } +bool is_shadow_context(struct i915_gem_context *ctx) +{ + if (ctx->name && !strncmp(ctx->name, "Shadow Context", 14)) + return true; + + return false; +} + +int get_vgt_id(struct i915_gem_context *ctx) +{ + int vgt_id; + + vgt_id = 0; + + if (is_shadow_context(ctx)) + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + + return vgt_id; +} + +int get_pid_shadowed(struct i915_gem_context *ctx, + struct intel_engine_cs *engine) +{ + int pid, vgt_id; + + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + pid = intel_read_status_page(engine, I915_GEM_HWS_PID_INDEX + vgt_id); + return pid; +} + static void describe_ctx_ring_shadowed(struct seq_file *m, struct i915_gem_context *ctx, struct intel_ring *ring, struct intel_engine_cs *engine) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 5c2c93cbab12..1bd2a7ef1885 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1054,6 +1054,7 @@ void i915_request_add(struct i915_request *request) lockdep_assert_held(&request->i915->drm.struct_mutex); trace_i915_request_add(request); + trace_i915_request_add_domain(request); /* * Make sure that no request gazumped us - if it was allocated after diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index b50c6b829715..af592e3d09a9 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -679,6 +679,53 @@ DEFINE_EVENT(i915_request, i915_request_add, TP_ARGS(rq) ); +TRACE_EVENT(i915_multi_domains, + TP_PROTO(struct i915_request *req), + TP_ARGS(req), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, ctx) + __field(u32, ring) + __field(u32, seqno) + __field(u32, global) + __field(int, prio_req) + __field(int, prio_ctx) + __field(bool, shadow_ctx) + __field(u32, hw_id) + __field(int, vgt_id) + __field(u32, pid) + ), + + TP_fast_assign( + __entry->dev = req->i915->drm.primary->index; + __entry->ring = req->engine->id; + __entry->ctx = req->fence.context; + __entry->seqno = req->fence.seqno; + __entry->global = req->global_seqno; + __entry->prio_req = req->sched.attr.priority; + __entry->prio_ctx = req->sched.attr.priority; + __entry->shadow_ctx = is_shadow_context(req->gem_context); + __entry->hw_id = req->gem_context->hw_id; + __entry->vgt_id = get_vgt_id(req->gem_context); + __entry->pid = is_shadow_context(req->gem_context) ? + get_pid_shadowed(req->gem_context, req->engine) : + pid_nr(req->gem_context->pid); + ), + + TP_printk("dev=%u, ring=%u, ctx=%u, seqno=%u, global=%u, " + "priority=%d (%d), is_shadow_ctx=%u, hw_id=%u, " + "vgt_id=%u, pid=%u", __entry->dev, __entry->ring, + __entry->ctx, __entry->seqno, __entry->global, + __entry->prio_req, __entry->prio_ctx, __entry->shadow_ctx, + __entry->hw_id, __entry->vgt_id, __entry->pid) +); + +DEFINE_EVENT(i915_multi_domains, i915_request_add_domain, + TP_PROTO(struct i915_request *req), + TP_ARGS(req) +); + #if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) DEFINE_EVENT(i915_request, i915_request_submit, TP_PROTO(struct i915_request *rq), diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7c11b8d10b66..b64da5510f67 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1384,6 +1384,11 @@ static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) return dev_priv->runtime_pm.irqs_enabled; } +bool is_shadow_context(struct i915_gem_context *ctx); +int get_vgt_id(struct i915_gem_context *ctx); +int get_pid_shadowed(struct i915_gem_context *ctx, + struct intel_engine_cs *engine); + int intel_get_crtc_scanline(struct intel_crtc *crtc); void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv, u8 pipe_mask); From 0210b97676e6444fe0a37c34639585f032483e8b Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1046/1276] drm/i915/gvt: preliminary per ring scheduler The basic idea is to make the scheduler of fine granularity at per-ring level, and to let vGPUs run simultaneously on different rings when they have different type of workload. Signed-off-by: Ping Gao Signed-off-by: Fei Jiang Acknowledged-by: Singh, Satyeshwar --- drivers/gpu/drm/i915/gvt/gvt.h | 2 +- drivers/gpu/drm/i915/gvt/sched_policy.c | 212 +++++++++++++++--------- drivers/gpu/drm/i915/gvt/scheduler.c | 12 +- drivers/gpu/drm/i915/gvt/scheduler.h | 6 +- drivers/gpu/drm/i915/gvt/vgpu.c | 7 +- 5 files changed, 147 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index d673c46e3179..718cf020e963 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -183,7 +183,7 @@ struct intel_vgpu { * scheduler structure. So below 2 vgpu data are protected * by sched_lock, not vgpu_lock. */ - void *sched_data; + void *sched_data[I915_NUM_ENGINES]; struct vgpu_sched_ctl sched_ctl; struct intel_vgpu_fence fence; diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index 4fac40d26549..f5127e07570b 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -34,15 +34,11 @@ #include "i915_drv.h" #include "gvt.h" -static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu) +static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu, + enum intel_engine_id ring_id) { - enum intel_engine_id i; - struct intel_engine_cs *engine; - - for_each_engine(engine, vgpu->gvt->dev_priv, i) { - if (!list_empty(workload_q_head(vgpu, i))) - return true; - } + if (!list_empty(workload_q_head(vgpu, ring_id))) + return true; return false; } @@ -68,11 +64,12 @@ struct gvt_sched_data { struct intel_gvt *gvt; struct hrtimer timer; unsigned long period; - struct list_head lru_runq_head; + struct list_head lru_runq_head[I915_NUM_ENGINES]; ktime_t expire_time; }; -static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) +static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time, + enum intel_engine_id ring_id) { ktime_t delta_ts; struct vgpu_sched_data *vgpu_data; @@ -80,7 +77,7 @@ static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) if (!vgpu || vgpu == vgpu->gvt->idle_vgpu) return; - vgpu_data = vgpu->sched_data; + vgpu_data = vgpu->sched_data[ring_id]; delta_ts = ktime_sub(cur_time, vgpu_data->sched_in_time); vgpu_data->sched_time = ktime_add(vgpu_data->sched_time, delta_ts); vgpu_data->left_ts = ktime_sub(vgpu_data->left_ts, delta_ts); @@ -90,12 +87,13 @@ static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) #define GVT_TS_BALANCE_PERIOD_MS 100 #define GVT_TS_BALANCE_STAGE_NUM 10 -static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) +static void gvt_balance_timeslice(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct vgpu_sched_data *vgpu_data; struct list_head *pos; - static uint64_t stage_check; - int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM; + static uint64_t stage_check[I915_NUM_ENGINES]; + int stage = stage_check[ring_id]++ % GVT_TS_BALANCE_STAGE_NUM; /* The timeslice accumulation reset at stage 0, which is * allocated again without adding previous debt. @@ -104,12 +102,12 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) int total_weight = 0; ktime_t fair_timeslice; - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); total_weight += vgpu_data->sched_ctl.weight; } - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); fair_timeslice = ktime_divns(ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS), total_weight) * vgpu_data->sched_ctl.weight; @@ -118,7 +116,7 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) vgpu_data->left_ts = vgpu_data->allocated_ts; } } else { - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); /* timeslice for next 100ms should add the left/debt @@ -129,62 +127,63 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) } } -static void try_to_schedule_next_vgpu(struct intel_gvt *gvt) +static void try_to_schedule_next_vgpu(struct intel_gvt *gvt, + enum intel_engine_id ring_id) { struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; - enum intel_engine_id i; - struct intel_engine_cs *engine; struct vgpu_sched_data *vgpu_data; ktime_t cur_time; /* no need to schedule if next_vgpu is the same with current_vgpu, * let scheduler chose next_vgpu again by setting it to NULL. */ - if (scheduler->next_vgpu == scheduler->current_vgpu) { - scheduler->next_vgpu = NULL; + if (scheduler->next_vgpu[ring_id] == + scheduler->current_vgpu[ring_id]) { + scheduler->next_vgpu[ring_id] = NULL; return; } + /* no target to schedule */ + if (!scheduler->next_vgpu[ring_id]) + return; /* * after the flag is set, workload dispatch thread will * stop dispatching workload for current vgpu */ - scheduler->need_reschedule = true; + scheduler->need_reschedule[ring_id] = true; /* still have uncompleted workload? */ - for_each_engine(engine, gvt->dev_priv, i) { - if (scheduler->current_workload[i]) - return; - } + if (scheduler->current_workload[ring_id]) + return; cur_time = ktime_get(); - vgpu_update_timeslice(scheduler->current_vgpu, cur_time); - vgpu_data = scheduler->next_vgpu->sched_data; + vgpu_update_timeslice(scheduler->current_vgpu[ring_id], cur_time, ring_id); + vgpu_data = scheduler->next_vgpu[ring_id]->sched_data[ring_id]; vgpu_data->sched_in_time = cur_time; /* switch current vgpu */ - scheduler->current_vgpu = scheduler->next_vgpu; - scheduler->next_vgpu = NULL; + scheduler->current_vgpu[ring_id] = scheduler->next_vgpu[ring_id]; + scheduler->next_vgpu[ring_id] = NULL; - scheduler->need_reschedule = false; + scheduler->need_reschedule[ring_id] = false; /* wake up workload dispatch thread */ - for_each_engine(engine, gvt->dev_priv, i) - wake_up(&scheduler->waitq[i]); + wake_up(&scheduler->waitq[ring_id]); } -static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data) +static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct vgpu_sched_data *vgpu_data; struct intel_vgpu *vgpu = NULL; - struct list_head *head = &sched_data->lru_runq_head; + struct list_head *head = &sched_data->lru_runq_head[ring_id]; struct list_head *pos; /* search a vgpu with pending workload */ list_for_each(pos, head) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); - if (!vgpu_has_pending_workload(vgpu_data->vgpu)) + if (!vgpu_has_pending_workload(vgpu_data->vgpu, ring_id)) continue; if (vgpu_data->pri_sched) { @@ -208,7 +207,8 @@ static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data) /* in nanosecond */ #define GVT_DEFAULT_TIME_SLICE 1000000 -static void tbs_sched_func(struct gvt_sched_data *sched_data) +static void tbs_sched_func(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct intel_gvt *gvt = sched_data->gvt; struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; @@ -216,31 +216,34 @@ static void tbs_sched_func(struct gvt_sched_data *sched_data) struct intel_vgpu *vgpu = NULL; /* no active vgpu or has already had a target */ - if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu) + if (list_empty(&sched_data->lru_runq_head[ring_id]) + || scheduler->next_vgpu[ring_id]) goto out; - vgpu = find_busy_vgpu(sched_data); + vgpu = find_busy_vgpu(sched_data, ring_id); if (vgpu) { - scheduler->next_vgpu = vgpu; - vgpu_data = vgpu->sched_data; + scheduler->next_vgpu[ring_id] = vgpu; + vgpu_data = vgpu->sched_data[ring_id]; if (!vgpu_data->pri_sched) { /* Move the last used vGPU to the tail of lru_list */ list_del_init(&vgpu_data->lru_list); list_add_tail(&vgpu_data->lru_list, - &sched_data->lru_runq_head); + &sched_data->lru_runq_head[ring_id]); } } else { - scheduler->next_vgpu = gvt->idle_vgpu; + scheduler->next_vgpu[ring_id] = gvt->idle_vgpu; } out: - if (scheduler->next_vgpu) - try_to_schedule_next_vgpu(gvt); + if (scheduler->next_vgpu[ring_id]) + try_to_schedule_next_vgpu(gvt, ring_id); } void intel_gvt_schedule(struct intel_gvt *gvt) { struct gvt_sched_data *sched_data = gvt->scheduler.sched_data; ktime_t cur_time; + enum intel_engine_id i; + struct intel_engine_cs *engine; mutex_lock(&gvt->sched_lock); cur_time = ktime_get(); @@ -248,15 +251,19 @@ void intel_gvt_schedule(struct intel_gvt *gvt) if (test_and_clear_bit(INTEL_GVT_REQUEST_SCHED, (void *)&gvt->service_request)) { if (cur_time >= sched_data->expire_time) { - gvt_balance_timeslice(sched_data); + for_each_engine(engine, gvt->dev_priv, i) + gvt_balance_timeslice(sched_data, i); sched_data->expire_time = ktime_add_ms( cur_time, GVT_TS_BALANCE_PERIOD_MS); } } clear_bit(INTEL_GVT_REQUEST_EVENT_SCHED, (void *)&gvt->service_request); - vgpu_update_timeslice(gvt->scheduler.current_vgpu, cur_time); - tbs_sched_func(sched_data); + for_each_engine(engine, gvt->dev_priv, i) { + vgpu_update_timeslice(gvt->scheduler.current_vgpu[i], + cur_time, i); + tbs_sched_func(sched_data, i); + } mutex_unlock(&gvt->sched_lock); } @@ -276,6 +283,9 @@ static enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data) static int tbs_sched_init(struct intel_gvt *gvt) { + enum intel_engine_id i; + struct intel_engine_cs *engine; + struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; @@ -285,7 +295,9 @@ static int tbs_sched_init(struct intel_gvt *gvt) if (!data) return -ENOMEM; - INIT_LIST_HEAD(&data->lru_runq_head); + for_each_engine(engine, gvt->dev_priv, i) + INIT_LIST_HEAD(&data->lru_runq_head[i]); + hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); data->timer.function = tbs_timer_fn; data->period = GVT_DEFAULT_TIME_SLICE; @@ -311,18 +323,29 @@ static void tbs_sched_clean(struct intel_gvt *gvt) static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu) { struct vgpu_sched_data *data; + enum intel_engine_id i; + struct intel_engine_cs *engine; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto err; - data->sched_ctl.weight = vgpu->sched_ctl.weight; - data->vgpu = vgpu; - INIT_LIST_HEAD(&data->lru_list); + data->sched_ctl.weight = vgpu->sched_ctl.weight; + data->vgpu = vgpu; + INIT_LIST_HEAD(&data->lru_list); - vgpu->sched_data = data; + vgpu->sched_data[i] = data; + } return 0; + +err: + for (; i >= 0; i--) { + kfree(vgpu->sched_data[i]); + vgpu->sched_data[i] = NULL; + } + return -ENOMEM; } static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) @@ -330,8 +353,13 @@ static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) struct intel_gvt *gvt = vgpu->gvt; struct gvt_sched_data *sched_data = gvt->scheduler.sched_data; - kfree(vgpu->sched_data); - vgpu->sched_data = NULL; + enum intel_engine_id i; + struct intel_engine_cs *engine; + + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + kfree(vgpu->sched_data[i]); + vgpu->sched_data[i] = NULL; + } /* this vgpu id has been removed */ if (idr_is_empty(&gvt->vgpu_idr)) @@ -341,31 +369,42 @@ static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) static void tbs_sched_start_schedule(struct intel_vgpu *vgpu) { struct gvt_sched_data *sched_data = vgpu->gvt->scheduler.sched_data; - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; ktime_t now; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; - if (!list_empty(&vgpu_data->lru_list)) - return; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + if (!list_empty(&vgpu_data->lru_list)) + continue; - now = ktime_get(); - vgpu_data->pri_time = ktime_add(now, + now = ktime_get(); + vgpu_data->pri_time = ktime_add(now, ktime_set(GVT_SCHED_VGPU_PRI_TIME, 0)); - vgpu_data->pri_sched = true; + vgpu_data->pri_sched = true; - list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head); + list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head[i]); + vgpu_data->active = true; + } if (!hrtimer_active(&sched_data->timer)) hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(), sched_data->period), HRTIMER_MODE_ABS); - vgpu_data->active = true; } static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu) { - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; + + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; - list_del_init(&vgpu_data->lru_list); - vgpu_data->active = false; + list_del_init(&vgpu_data->lru_list); + vgpu_data->active = false; + } } static struct intel_gvt_sched_policy_ops tbs_schedule_ops = { @@ -423,10 +462,16 @@ void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu) void intel_vgpu_start_schedule(struct intel_vgpu *vgpu) { - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; + struct vgpu_sched_data *vgpu_data; + struct intel_engine_cs *engine; + enum intel_engine_id i; mutex_lock(&vgpu->gvt->sched_lock); - if (!vgpu_data->active) { + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + if (vgpu_data->active) + continue; + gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id); vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu); } @@ -444,23 +489,26 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) { struct intel_gvt_workload_scheduler *scheduler = &vgpu->gvt->scheduler; - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; - - if (!vgpu_data->active) - return; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id); mutex_lock(&vgpu->gvt->sched_lock); scheduler->sched_ops->stop_schedule(vgpu); - if (scheduler->next_vgpu == vgpu) - scheduler->next_vgpu = NULL; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + + if (scheduler->next_vgpu[i] == vgpu) + scheduler->next_vgpu[i] = NULL; - if (scheduler->current_vgpu == vgpu) { - /* stop workload dispatching */ - scheduler->need_reschedule = true; - scheduler->current_vgpu = NULL; + if (scheduler->current_vgpu[i] == vgpu) { + /* stop workload dispatching */ + scheduler->need_reschedule[i] = true; + scheduler->current_vgpu[i] = NULL; + } } mutex_unlock(&vgpu->gvt->sched_lock); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 7c3636e3a725..ebba07a402d7 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -745,17 +745,18 @@ static struct intel_vgpu_workload *pick_next_workload( * no current vgpu / will be scheduled out / no workload * bail out */ - if (!scheduler->current_vgpu) { + if (!scheduler->current_vgpu[ring_id]) { gvt_dbg_sched("ring id %d stop - no current vgpu\n", ring_id); goto out; } - if (scheduler->need_reschedule) { + if (scheduler->need_reschedule[ring_id]) { gvt_dbg_sched("ring id %d stop - will reschedule\n", ring_id); goto out; } - if (list_empty(workload_q_head(scheduler->current_vgpu, ring_id))) + if (list_empty(workload_q_head(scheduler->current_vgpu[ring_id], + ring_id))) goto out; /* @@ -776,7 +777,8 @@ static struct intel_vgpu_workload *pick_next_workload( * schedule out a vgpu. */ scheduler->current_workload[ring_id] = container_of( - workload_q_head(scheduler->current_vgpu, ring_id)->next, + workload_q_head(scheduler->current_vgpu[ring_id], + ring_id)->next, struct intel_vgpu_workload, list); workload = scheduler->current_workload[ring_id]; @@ -964,7 +966,7 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) atomic_dec(&s->running_workload_num); wake_up(&scheduler->workload_complete_wq); - if (gvt->scheduler.need_reschedule) + if (gvt->scheduler.need_reschedule[ring_id]) intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EVENT_SCHED); mutex_unlock(&gvt->sched_lock); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index 043c2ff07a7c..3cec02d2ac1a 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -37,10 +37,10 @@ #define _GVT_SCHEDULER_H_ struct intel_gvt_workload_scheduler { - struct intel_vgpu *current_vgpu; - struct intel_vgpu *next_vgpu; + struct intel_vgpu *current_vgpu[I915_NUM_ENGINES]; + struct intel_vgpu *next_vgpu[I915_NUM_ENGINES]; struct intel_vgpu_workload *current_workload[I915_NUM_ENGINES]; - bool need_reschedule; + bool need_reschedule[I915_NUM_ENGINES]; spinlock_t mmio_context_lock; /* can be null when owner is host */ diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index c628be05fbfe..d83e48b53d62 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -525,6 +525,8 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; unsigned int resetting_eng = dmlr ? ALL_ENGINES : engine_mask; + enum intel_engine_id i; + struct intel_engine_cs *engine; gvt_dbg_core("------------------------------------------\n"); gvt_dbg_core("resseting vgpu%d, dmlr %d, engine_mask %08x\n", @@ -537,7 +539,10 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, * The current_vgpu will set to NULL after stopping the * scheduler when the reset is triggered by current vgpu. */ - if (scheduler->current_vgpu == NULL) { + for_each_engine_masked(engine, gvt->dev_priv, resetting_eng, i) { + if (scheduler->current_vgpu[i] != NULL) + continue; + mutex_unlock(&vgpu->vgpu_lock); intel_gvt_wait_vgpu_idle(vgpu); mutex_lock(&vgpu->vgpu_lock); From 4e228ab87c034e1af20780aff282aa07983fcad3 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1047/1276] drm/i915/gvt: Support vGPU guest framebuffer GEM object In the compositor mode of display, dom0/host needs to get the guest framebuffer to do more rendering, so that the guest VM's screen can show up in more fancy way, e.g., in an X window of dom0/host. In order to do that, a new gem object type "gvtbuffer" is introduced to i915. Different from normal gem object in i915, gvtbuffer does not have its own backing storage. Instead, it borrows the page frames of guest VM's framebuffer as its own backing storage. From high level, it works this way: a) gvt notifies kernel/userspace the guest OS page flip by monitoring the related guest MMIO changes and commands. b) user space issue IOCTL to create gvtbuffer gem object. c) kernel creates the gem object, and record the guest FB base address (gfx address) from MMIO. d) When needed, the gvtbuffer will be bound to graphics memory, and be used as normal gem object for rendering. Guest framebuffer must be inside GGTT, whereas the gvtbuffer can be in either GGTT or PPGTT, depending on the requirement of the rendering. Since the gvtbuffer corresponds to the guest framebuffer, which is from guest physical memory, we may not be able to get "page struct" for them. But i915 gem framework has had similar cases. A gem object can have stolen memory as its backing storage. In such case, the backing storage does not have "page struct" as well, and i915 has handled the case in the framework well. This patch was originally from daivid.j.cowperthwaite@intel.com, and pretty some changes were made since then. Change-Id: Ic0821f58dd568217a44b1b478c9659c709889c43 Signed-off-by: Zhiyuan Lv Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/gvt/fb_decoder.c | 68 +++++ drivers/gpu/drm/i915/gvt/fb_decoder.h | 4 + drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 293 ++++++++++++++++++++++ include/uapi/drm/drm_fourcc.h | 9 + include/uapi/drm/i915_drm.h | 40 +++ 8 files changed, 418 insertions(+) create mode 100644 drivers/gpu/drm/i915/i915_gem_gvtbuffer.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5794f102f9b8..a5198df1b1ca 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -79,6 +79,7 @@ i915-y += i915_cmd_parser.o \ i915_trace_points.o \ i915_vma.o \ intel_breadcrumbs.o \ + i915_gem_gvtbuffer.o \ intel_engine_cs.o \ intel_hangcheck.o \ intel_lrc.o \ diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 481896fb712a..6bc40be48649 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -37,6 +37,7 @@ #include "i915_drv.h" #include "gvt.h" #include "i915_pvinfo.h" +#include "fb_decoder.h" #define PRIMARY_FORMAT_NUM 16 struct pixel_format { @@ -511,3 +512,70 @@ int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, return 0; } + +/** + * intel_vgpu_decode_fb_format - Decode framebuffer information from raw vMMIO + * @gvt: GVT device + * @vmid: guest domain ID + * @fb: frame buffer infomation of guest. + * This function is called for query frame buffer format, so that gl can + * display guest fb in Dom0 + * + * Returns: + * Zero on success, negative error code if failed. + */ +int intel_vgpu_decode_fb_format(struct intel_gvt *gvt, int id, + struct intel_vgpu_fb_format *fb) + +{ + int i; + struct intel_vgpu *vgpu = NULL; + int ret = 0; + struct drm_i915_private *dev_priv = gvt->dev_priv; + + if (!fb) + return -EINVAL; + + /* TODO: use fine-grained refcnt later */ + mutex_lock(&gvt->lock); + + for_each_active_vgpu(gvt, vgpu, i) + if (vgpu->id == id) + break; + + if (!vgpu) { + gvt_err("Invalid vgpu ID (%d)\n", id); + mutex_unlock(&gvt->lock); + return -ENODEV; + } + + for (i = 0; i < I915_MAX_PIPES; i++) { + struct intel_vgpu_pipe_format *pipe = &fb->pipes[i]; + u32 ddi_func_ctl = vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(i)); + + if (!(ddi_func_ctl & TRANS_DDI_FUNC_ENABLE)) { + pipe->ddi_port = DDI_PORT_NONE; + } else { + u32 port = (ddi_func_ctl & TRANS_DDI_PORT_MASK) >> + TRANS_DDI_PORT_SHIFT; + if (port <= DDI_PORT_E) + pipe->ddi_port = port; + else + pipe->ddi_port = DDI_PORT_NONE; + } + + ret |= intel_vgpu_decode_primary_plane(vgpu, &pipe->primary); + ret |= intel_vgpu_decode_sprite_plane(vgpu, &pipe->sprite); + ret |= intel_vgpu_decode_cursor_plane(vgpu, &pipe->cursor); + + if (ret) { + gvt_err("Decode format error for pipe(%d)\n", i); + ret = -EINVAL; + break; + } + } + + mutex_unlock(&gvt->lock); + + return ret; +} diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index 60c155085029..a202f9f6e81a 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -166,4 +166,8 @@ int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, struct intel_vgpu_sprite_plane_format *plane); +extern +int intel_vgpu_decode_fb_format(struct intel_gvt *pdev, int vmid, + struct intel_vgpu_fb_format *fb); + #endif diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f8cfd16be534..91fd59fe6345 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -2860,6 +2860,7 @@ static const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_QUERY, i915_query_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_GVTBUFFER, i915_gem_gvtbuffer_ioctl, DRM_RENDER_ALLOW), }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bfb655ec6996..127f69ad8368 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3246,6 +3246,8 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data, void i915_oa_init_reg_state(struct intel_engine_cs *engine, struct i915_gem_context *ctx, uint32_t *reg_state); +int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct i915_address_space *vm, diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c new file mode 100644 index 000000000000..fe723099788d --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -0,0 +1,293 @@ +/* + * Copyright © 2012 - 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" +#include "i915_trace.h" +#include "intel_drv.h" +#include + +#include "gvt/gvt.h" +#include "gvt/fb_decoder.h" + +static int +i915_gem_gvtbuffer_get_pages(struct drm_i915_gem_object *obj) +{ + BUG(); + return -EINVAL; +} + +static void i915_gem_gvtbuffer_put_pages(struct drm_i915_gem_object *obj, + struct sg_table *pages) +{ + /* like stolen memory, this should only be called during free + * after clearing pin count. + */ + sg_free_table(pages); + kfree(pages); +} + +static void +i915_gem_gvtbuffer_release(struct drm_i915_gem_object *obj) +{ + i915_gem_object_unpin_pages(obj); +} + +static const struct drm_i915_gem_object_ops i915_gem_gvtbuffer_ops = { + .get_pages = i915_gem_gvtbuffer_get_pages, + .put_pages = i915_gem_gvtbuffer_put_pages, + .release = i915_gem_gvtbuffer_release, +}; + +#define GEN8_DECODE_PTE(pte) \ + ((dma_addr_t)(((((u64)pte) >> 12) & 0x7ffffffULL) << 12)) + +#define GEN7_DECODE_PTE(pte) \ + ((dma_addr_t)(((((u64)pte) & 0x7f0) << 28) | (u64)(pte & 0xfffff000))) + +static struct sg_table * +i915_create_sg_pages_for_gvtbuffer(struct drm_device *dev, + u32 start, u32 num_pages) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st; + struct scatterlist *sg; + int i; + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return NULL; + + if (sg_alloc_table(st, num_pages, GFP_KERNEL)) { + kfree(st); + return NULL; + } + + if (INTEL_INFO(dev_priv)->gen >= 8) { + gen8_pte_t __iomem *gtt_entries = + (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + + (start >> PAGE_SHIFT); + for_each_sg(st->sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = + GEN8_DECODE_PTE(readq(>t_entries[i])); + sg_dma_len(sg) = PAGE_SIZE; + } + } else { + gen6_pte_t __iomem *gtt_entries = + (gen6_pte_t __iomem *)dev_priv->ggtt.gsm + + (start >> PAGE_SHIFT); + for_each_sg(st->sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = + GEN7_DECODE_PTE(readq(>t_entries[i])); + sg_dma_len(sg) = PAGE_SIZE; + } + } + + return st; +} + +struct drm_i915_gem_object * +i915_gem_object_create_gvtbuffer(struct drm_device *dev, + u32 start, u32 num_pages) +{ + struct drm_i915_gem_object *obj; + obj = i915_gem_object_alloc(to_i915(dev)); + if (obj == NULL) + return NULL; + + drm_gem_private_object_init(dev, &obj->base, num_pages << PAGE_SHIFT); + i915_gem_object_init(obj, &i915_gem_gvtbuffer_ops); + + obj->mm.pages = i915_create_sg_pages_for_gvtbuffer(dev, start, num_pages); + if (obj->mm.pages == NULL) { + i915_gem_object_free(obj); + return NULL; + } + + if (i915_gem_object_pin_pages(obj)) + printk(KERN_ERR "%s:%d> Pin pages failed!\n", __func__, __LINE__); + obj->cache_level = I915_CACHE_L3_LLC; + + DRM_DEBUG_DRIVER("GVT_GEM: backing store base = 0x%x pages = 0x%x\n", + start, num_pages); + return obj; +} + +static int gvt_decode_information(struct drm_device *dev, + struct drm_i915_gem_gvtbuffer *args) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_vgpu_fb_format fb; + struct intel_vgpu_primary_plane_format *p; + struct intel_vgpu_cursor_plane_format *c; + struct intel_vgpu_pipe_format *pipe; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + u32 id = args->id; + + if (intel_vgpu_decode_fb_format(dev_priv->gvt, id, &fb)) + return -EINVAL; +#else + return -EINVAL; +#endif + + pipe = ((args->pipe_id >= I915_MAX_PIPES) ? + NULL : &fb.pipes[args->pipe_id]); + + if (!pipe || !pipe->primary.enabled) { + DRM_DEBUG_DRIVER("GVT_GEM: Invalid pipe_id: %d\n", + args->pipe_id); + return -EINVAL; + } + + if ((args->plane_id) == I915_GVT_PLANE_PRIMARY) { + p = &pipe->primary; + args->enabled = p->enabled; + args->x_offset = p->x_offset; + args->y_offset = p->y_offset; + args->start = p->base; + args->width = p->width; + args->height = p->height; + args->stride = p->stride; + args->bpp = p->bpp; + args->hw_format = p->hw_format; + args->drm_format = p->drm_format; + args->tiled = p->tiled; + } else if ((args->plane_id) == I915_GVT_PLANE_CURSOR) { + c = &pipe->cursor; + args->enabled = c->enabled; + args->x_offset = c->x_hot; + args->y_offset = c->y_hot; + args->x_pos = c->x_pos; + args->y_pos = c->y_pos; + args->start = c->base; + args->width = c->width; + args->height = c->height; + args->stride = c->width * (c->bpp / 8); + args->bpp = c->bpp; + args->tiled = 0; + } else { + DRM_DEBUG_DRIVER("GVT_GEM: Invalid plaine_id: %d\n", + args->plane_id); + return -EINVAL; + } + + args->size = (((args->width * args->height * args->bpp) / 8) + + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + + if (args->start & (PAGE_SIZE - 1)) { + DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: " + "0x%x\n", args->start); + return -EINVAL; + } + + if (((args->start >> PAGE_SHIFT) + args->size) > + ggtt_total_entries(&dev_priv->ggtt)) { + DRM_DEBUG_DRIVER("GVT: Invalid GTT offset or size\n"); + return -EINVAL; + } + return 0; +} + +/** + * Creates a new mm object that wraps some user memory. + */ +int +i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_gem_gvtbuffer *args = data; + struct drm_i915_gem_object *obj; + u32 handle; + int ret = 0; + + if (INTEL_INFO(dev_priv)->gen < 7) + return -EPERM; + + if (args->flags & I915_GVTBUFFER_CHECK_CAPABILITY) + return 0; +#if 0 + if (!gvt_check_host()) + return -EPERM; +#endif + /* if args->start != 0 do not decode, but use it as ggtt offset*/ + if (args->start == 0) { + ret = gvt_decode_information(dev, args); + if (ret) + return ret; + } + + if (ret) + return ret; + + if (args->flags & I915_GVTBUFFER_QUERY_ONLY) + return 0; + + obj = i915_gem_object_create_gvtbuffer(dev, args->start, args->size); + if (!obj) { + DRM_DEBUG_DRIVER("GVT_GEM: Failed to create gem object" + " for VM FB!\n"); + return -EINVAL; + } + + if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) || + IS_KABYLAKE(dev_priv)) { + unsigned int tiling_mode = I915_TILING_NONE; + unsigned int stride = 0; + + switch (args->tiled << 10) { + case PLANE_CTL_TILED_LINEAR: + /* Default valid value */ + break; + case PLANE_CTL_TILED_X: + tiling_mode = I915_TILING_X; + stride = args->stride; + break; + case PLANE_CTL_TILED_Y: + tiling_mode = I915_TILING_Y; + stride = args->stride; + break; + default: + DRM_ERROR("gvt: tiling mode %d not supported\n", args->tiled); + } + obj->tiling_and_stride = tiling_mode | stride; + } else { + obj->tiling_and_stride = (args->tiled ? I915_TILING_X : I915_TILING_NONE) | + (args->tiled ? args->stride : 0); + } + + ret = drm_gem_handle_create(file, &obj->base, &handle); + + /* drop reference from allocate - handle holds it now */ + i915_gem_object_put(obj); + + if (ret) + return ret; + + args->handle = handle; + return 0; +} diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 721ab7e54d96..39b9a73a3c77 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -100,6 +100,15 @@ extern "C" { #define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ #define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ +/* 64 bpp RGB, below two items is add by VGT project, the reason as below: + * 1. Current version DRM code is not contains 64 bpp RGB definations. + * 2. VGT should support 64 bpp RGB for Windows 10 guest. + * 3. VGT add the 64 bpp RGB definations temperarily, before the DRM code add these definations. + */ +#define DRM_FORMAT_XRGB161616_VGT fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR161616_VGT fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + + #define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ #define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 7f5634ce8e88..fc70f54e6ee9 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -319,6 +319,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_PERF_ADD_CONFIG 0x37 #define DRM_I915_PERF_REMOVE_CONFIG 0x38 #define DRM_I915_QUERY 0x39 +#define DRM_I915_GEM_GVTBUFFER 0x40 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -378,6 +379,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) #define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) +#define DRM_IOCTL_I915_GEM_GVTBUFFER DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GVTBUFFER, struct drm_i915_gem_gvtbuffer) + /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. */ @@ -1717,6 +1720,43 @@ struct drm_i915_query_topology_info { __u8 data[]; }; +struct drm_i915_gem_gvtbuffer { + __u32 id; + __u32 plane_id; +#define I915_GVT_PLANE_PRIMARY 1 +#define I915_GVT_PLANE_SPRITE 2 +#define I915_GVT_PLANE_CURSOR 3 + __u32 pipe_id; + __u32 phys_pipe_id; + __u8 enabled; + __u8 tiled; + __u32 bpp; + __u32 hw_format; + __u32 drm_format; + __u32 start; + __u32 x_pos; + __u32 y_pos; + __u32 x_offset; + __u32 y_offset; + __u32 size; + __u32 width; + __u32 height; + __u32 stride; + __u64 user_ptr; + __u32 user_size; + __u32 flags; +#define I915_GVTBUFFER_READ_ONLY (1<<0) +#define I915_GVTBUFFER_QUERY_ONLY (1<<1) +#define I915_GVTBUFFER_CHECK_CAPABILITY (1<<2) +#define I915_GVTBUFFER_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + #if defined(__cplusplus) } #endif From fa6046e2a93c18369df0cbf29940e902a23ad57f Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 27 Mar 2018 22:59:22 +0800 Subject: [PATCH 1048/1276] drm/i915/gvt: unset DDI_BUF_CTL_ENABLE during port emulation reset HDMI port enabling will assert port status, if it's already set during reset stage, i915 will pop up warning message. Unset those bits to avoid such warning message. Signed-off-by: Fei Jiang Change-Id: Ic8c738baa472d7f1086081cb1b634670327aae97 --- drivers/gpu/drm/i915/gvt/display.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 40ecca217733..58b32dbd50e3 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -228,7 +228,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_B)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SDEISR) |= SDE_PORTB_HOTPLUG_CPT; } @@ -248,7 +248,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_C)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDIC_DETECTED; } @@ -268,7 +268,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_D)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED; } From f6dc739467a7c2dbf503d04906733d63c94d4e14 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1049/1276] drm/i915/gvt: add scaler owner to support guest plane scaling It is to support plane scaling feature, add scaler owner to avoid con-current scaler access. Such ownership is passed from SOS side through pvmmio scaler_owned member. Guest OS patch. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_pvinfo.h | 3 ++- drivers/gpu/drm/i915/i915_vgpu.c | 2 ++ drivers/gpu/drm/i915/intel_atomic.c | 14 +++++++++----- drivers/gpu/drm/i915/intel_display.c | 8 +++++++- drivers/gpu/drm/i915/intel_drv.h | 1 + 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 127f69ad8368..671c1e66f9b7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1306,6 +1306,7 @@ struct i915_workarounds { struct i915_virtual_gpu { bool active; u32 caps; + u32 scaler_owned; }; /* used in computing the new watermarks state */ diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index eeaa3d506d95..dc9bdeaa3147 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -106,8 +106,9 @@ struct vgt_if { u32 execlist_context_descriptor_lo; u32 execlist_context_descriptor_hi; + u32 scaler_owned; - u32 rsv7[0x200 - 24]; /* pad to one page */ + u32 rsv7[0x200 - 25]; /* pad to one page */ } __packed; #define vgtif_reg(x) \ diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 869cf4a3b6de..0f6182f32ded 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -76,6 +76,8 @@ void i915_check_vgpu(struct drm_i915_private *dev_priv) } dev_priv->vgpu.caps = __raw_i915_read32(dev_priv, vgtif_reg(vgt_caps)); + dev_priv->vgpu.scaler_owned = + __raw_i915_read32(dev_priv, vgtif_reg(scaler_owned)); dev_priv->vgpu.active = true; DRM_INFO("Virtual GPU for Intel GVT-g detected.\n"); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index b04952bacf77..ec4a73e79709 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -316,7 +316,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, if (*scaler_id < 0) { /* find a free scaler */ for (j = 0; j < intel_crtc->num_scalers; j++) { - if (!scaler_state->scalers[j].in_use) { + if (!scaler_state->scalers[j].in_use && + scaler_state->scalers[j].owned == 1) { scaler_state->scalers[j].in_use = 1; *scaler_id = j; DRM_DEBUG_KMS("Attached scaler id %u.%u to %s:%d\n", @@ -350,10 +351,13 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, * scaler 0 operates in high quality (HQ) mode. * In this case use scaler 0 to take advantage of HQ mode */ - *scaler_id = 0; - scaler_state->scalers[0].in_use = 1; - scaler_state->scalers[0].mode = PS_SCALER_MODE_HQ; - scaler_state->scalers[1].in_use = 0; + if (scaler_state->scalers[0].owned == 1) { + *scaler_id = 0; + scaler_state->scalers[0].in_use = 1; + scaler_state->scalers[0].mode = + PS_SCALER_MODE_HQ; + scaler_state->scalers[1].in_use = 0; + } } else { scaler_state->scalers[*scaler_id].mode = PS_SCALER_MODE_DYN; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d095f1f46de8..91b53bc444dd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8732,7 +8732,8 @@ static void skylake_get_pfit_config(struct intel_crtc *crtc, /* find scaler attached to this pipe */ for (i = 0; i < crtc->num_scalers; i++) { ps_ctrl = I915_READ(SKL_PS_CTRL(crtc->pipe, i)); - if (ps_ctrl & PS_SCALER_EN && !(ps_ctrl & PS_PLANE_SEL_MASK)) { + if (ps_ctrl & PS_SCALER_EN && !(ps_ctrl & PS_PLANE_SEL_MASK) && + scaler_state->scalers[i].owned) { id = i; pipe_config->pch_pfit.enabled = true; pipe_config->pch_pfit.pos = I915_READ(SKL_PS_WIN_POS(crtc->pipe, i)); @@ -13924,6 +13925,11 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler->in_use = 0; scaler->mode = PS_SCALER_MODE_DYN; + scaler->owned = 1; + if (intel_vgpu_active(dev_priv) && + !(1 << (crtc->pipe * SKL_NUM_SCALERS + i) & + dev_priv->vgpu.scaler_owned)) + scaler->owned = 0; } scaler_state->scaler_id = -1; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b64da5510f67..098c2886cf7e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -564,6 +564,7 @@ struct intel_initial_plane_config { struct intel_scaler { int in_use; uint32_t mode; + int owned; }; struct intel_crtc_scaler_state { From dfc6594fedd9c0cb7121c3cff5c5b9e5f732eceb Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1050/1276] drm/i915/gvt: support guest plane scaling It is to support plane scaling feature, need let guest access PS_CTRL, PS_WIN, PS_POS registers. Use parameter domain_scaler_owner to control domain's scaler ownership. Validate with IGT test: kms_plane_scaling. SOS only patch. V2: Fix the issue when it writes 0 to PS_CTRL register to disable scaling. Signed-off-by: Min He Signed-off-by: Fei Jiang Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/display.c | 20 +++++- drivers/gpu/drm/i915/gvt/gvt.h | 6 ++ drivers/gpu/drm/i915/gvt/handlers.c | 96 +++++++++++++++++----------- drivers/gpu/drm/i915/gvt/reg.h | 4 ++ drivers/gpu/drm/i915/gvt/vgpu.c | 13 ++++ drivers/gpu/drm/i915/i915_params.c | 22 +++++++ drivers/gpu/drm/i915/i915_params.h | 1 + drivers/gpu/drm/i915/intel_display.c | 5 ++ 8 files changed, 130 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 58b32dbd50e3..73a2dbbaf79d 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -478,15 +478,33 @@ static void intel_gvt_vblank_work(struct work_struct *w) mutex_unlock(&gvt->lock); } +#define BITS_PER_DOMAIN 4 +#define MAX_SCALERS_PER_DOMAIN 2 + +#define DOMAIN_SCALER_OWNER(owner, pipe, scaler) \ + ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_SCALERS_PER_DOMAIN) >> \ + BITS_PER_DOMAIN * (scaler)) & 0xf) + void intel_gvt_init_pipe_info(struct intel_gvt *gvt) { - int pipe; + enum pipe pipe; + unsigned int scaler; + unsigned int domain_scaler_owner = i915_modparams.domain_scaler_owner; + struct drm_i915_private *dev_priv = gvt->dev_priv; for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) { gvt->pipe_info[pipe].pipe_num = pipe; gvt->pipe_info[pipe].gvt = gvt; INIT_WORK(&gvt->pipe_info[pipe].vblank_work, intel_gvt_vblank_work); + /* Each nibble represents domain id + * ids can be from 0-F. 0 for Dom0, 1,2,3...0xF for DomUs + * scaler_owner[i] holds the id of the domain that owns it, + * eg:0,1,2 etc + */ + for_each_universal_scaler(dev_priv, pipe, scaler) + gvt->pipe_info[pipe].scaler_owner[scaler] = + DOMAIN_SCALER_OWNER(domain_scaler_owner, pipe, scaler); } } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 718cf020e963..64139240207f 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -309,6 +309,7 @@ struct intel_gvt_pipe_info { struct intel_gvt *gvt; struct work_struct vblank_work; int plane_owner[I915_MAX_PLANES]; + int scaler_owner[SKL_NUM_SCALERS]; }; struct intel_gvt { @@ -472,6 +473,11 @@ void intel_vgpu_write_fence(struct intel_vgpu *vgpu, idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) \ for_each_if(vgpu->active) +#define for_each_universal_scaler(__dev_priv, __pipe, __s) \ + for ((__s) = 0; \ + (__s) < INTEL_INFO(__dev_priv)->num_scalers[(__pipe)] + 1; \ + (__s)++) + static inline void intel_vgpu_write_pci_bar(struct intel_vgpu *vgpu, u32 offset, u32 val, bool low) { diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 91fe0a44367d..88d34ef2c057 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1161,6 +1161,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, break; case 0x78010: /* vgt_caps */ case 0x7881c: + case _vgtif_reg(scaler_owned): break; default: invalid_read = true; @@ -1259,22 +1260,6 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } -static int pf_write(struct intel_vgpu *vgpu, - unsigned int offset, void *p_data, unsigned int bytes) -{ - u32 val = *(u32 *)p_data; - - if ((offset == _PS_1A_CTRL || offset == _PS_2A_CTRL || - offset == _PS_1B_CTRL || offset == _PS_2B_CTRL || - offset == _PS_1C_CTRL) && (val & PS_PLANE_SEL_MASK) != 0) { - WARN_ONCE(true, "VM(%d): guest is trying to scaling a plane\n", - vgpu->id); - return 0; - } - - return intel_vgpu_default_mmio_write(vgpu, offset, p_data, bytes); -} - static int power_well_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -2851,6 +2836,45 @@ static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, #define PLANE_WM_BASE(pipe, plane) _MMIO(_PLANE_WM_BASE(pipe, plane)) +static int skl_ps_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + unsigned int pipe = SKL_PS_REG_TO_PIPE(offset); + unsigned int scaler = SKL_PS_REG_TO_SCALER(offset) - 1; + + if (pipe >= I915_MAX_PIPES || scaler >= SKL_NUM_SCALERS || + vgpu->gvt->pipe_info[pipe].scaler_owner[scaler] != vgpu->id) { + gvt_vgpu_err("Unsupport pipe %d, scaler %d scaling\n", + pipe, scaler); + return 0; + } + + if (!(vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE)) + return 0; + + if ((offset == _PS_1A_CTRL || offset == _PS_2A_CTRL || + offset == _PS_1B_CTRL || offset == _PS_2B_CTRL || + offset == _PS_1C_CTRL) && ((*(u32 *)p_data) & PS_SCALER_EN)) { + unsigned int plane; + + if (SKL_PS_REG_VALUE_TO_PLANE(*(u32 *)p_data) == 0) { + gvt_vgpu_err("Unsupport crtc scaling for UOS\n"); + return 0; + } + plane = SKL_PS_REG_VALUE_TO_PLANE(*(u32 *)p_data) - 1; + if (plane >= I915_MAX_PLANES || + vgpu->gvt->pipe_info[pipe].plane_owner[plane] != vgpu->id) { + gvt_vgpu_err("Unsupport plane %d scaling\n", plane); + return 0; + } + } + + write_vreg(vgpu, offset, p_data, bytes); + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + return 0; +} + static int init_skl_mmio_info(struct intel_gvt *gvt) { struct drm_i915_private *dev_priv = gvt->dev_priv; @@ -2901,26 +2925,26 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x6c05c), D_SKL_PLUS); MMIO_DH(_MMIO(0x6c060), D_SKL_PLUS, dpll_status_read, NULL); - MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - - MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - - MMIO_DH(SKL_PS_CTRL(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + + MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + + MMIO_DH(SKL_PS_CTRL(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); MMIO_PLANES_DH(PLANE_CTL, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_STRIDE, D_SKL_PLUS, NULL, skl_plane_mmio_write); diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index d05c5516a472..b55fc82027e0 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -59,6 +59,10 @@ #define _REG_701AC(pipe, plane) (0x701ac + pipe * 0x1000 + plane * 0x100) +#define SKL_PS_REG_TO_PIPE(reg) (((reg) >> 11) & 0x3) +#define SKL_PS_REG_TO_SCALER(reg) (((reg) >> 8) & 0x3) +#define SKL_PS_REG_VALUE_TO_PLANE(val) (((val) >> 25) & 0x7) + #define SKL_PLANE_REG_TO_PIPE(reg) (((reg) >> 12) & 0x3) #define SKL_PLANE_REG_TO_PLANE(reg) ((((reg) & 0xFFF) - 0x180) >> 8) #define SKL_FLIP_EVENT(pipe, plane) (PRIMARY_A_FLIP_DONE + (plane)*3 + pipe) diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index d83e48b53d62..5cb46999b238 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -37,6 +37,11 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) { + enum pipe pipe; + int scaler; + struct intel_gvt *gvt = vgpu->gvt; + struct drm_i915_private *dev_priv = gvt->dev_priv; + /* setup the ballooning information */ vgpu_vreg64_t(vgpu, vgtif_reg(magic)) = VGT_MAGIC; vgpu_vreg_t(vgpu, vgtif_reg(version_major)) = 1; @@ -62,6 +67,14 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, vgtif_reg(cursor_x_hot)) = UINT_MAX; vgpu_vreg_t(vgpu, vgtif_reg(cursor_y_hot)) = UINT_MAX; + vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) = 0; + for_each_pipe(dev_priv, pipe) + for_each_universal_scaler(dev_priv, pipe, scaler) + if (gvt->pipe_info[pipe].scaler_owner[scaler] == + vgpu->id) + vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) |= + 1 << (pipe * SKL_NUM_SCALERS + scaler); + gvt_dbg_core("Populate PVINFO PAGE for vGPU %d\n", vgpu->id); gvt_dbg_core("aperture base [GMADR] 0x%llx size 0x%llx\n", vgpu_aperture_gmadr_base(vgpu), vgpu_aperture_sz(vgpu)); diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 295e981e4a39..8bdd4043b563 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -174,6 +174,28 @@ i915_param_named(enable_dpcd_backlight, bool, 0600, i915_param_named(enable_gvt, bool, 0400, "Enable support for Intel GVT-g graphics virtualization host support(default:false)"); +i915_param_named(domain_scaler_owner, int, 0400, + "scaler owners for each domain and for each pipe ids can be from 0-F"); + +/* pipeA Scaler = BITS 0-7 pipeB scaler = 8-15, pipeC = 16-19 + * + * +----------+------------+-------------+------------+ + * |unused | Pipe C | Pipe B | Pipe A | + * +----------+------------+-------------+------------+ + * 31 20 19 16 15 8 7 0 + * + * Each nibble represents domain id. 0 for Dom0, 1,2,3...0xF for DomUs + * eg: domains_scaler_owners = 0x00030210 // 0x000|3|02|10 + * scaler domain + * scaler_owner1A -0 + * scaler_owner2A -1 + * scaler_owner1B -2 + * scaler_owner2B -0 + * scaler_owner1C -3 + * scaler_owner2C -0 + * + */ + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 6c4d4a21474b..74865c23f809 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -68,6 +68,7 @@ struct drm_printer; param(bool, nuclear_pageflip, false) \ param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ + param(int, domain_scaler_owner, 0x11100) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 91b53bc444dd..bc4d36c1a672 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13926,6 +13926,11 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler->in_use = 0; scaler->mode = PS_SCALER_MODE_DYN; scaler->owned = 1; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (intel_gvt_active(dev_priv) && + dev_priv->gvt->pipe_info[crtc->pipe].scaler_owner[i] != 0) + scaler->owned = 0; +#endif if (intel_vgpu_active(dev_priv) && !(1 << (crtc->pipe * SKL_NUM_SCALERS + i) & dev_priv->vgpu.scaler_owned)) From a52c58ff4aea166901f8aaa54f33bd9e142d6684 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1051/1276] drm/i915/gvt: add module parameter enable_pvmmio This uint type module parameter is used to control the pvmmio features for MMIO emulation in GVT. This parameter is default 0. Its permission type is 0400 which means user could only change its value through the cmdline, this is to prevent the dynamic modification during runtime which would break the pvmmio internal logic. Notice: this patch is required to be applied to guest kernel. Change-Id: I570f1fe02101e518595c02fce67601b692871aa9 Signed-off-by: Pei Zhang Signed-off-by: Jiang, Fei Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_params.c | 5 +++++ drivers/gpu/drm/i915/i915_params.h | 1 + drivers/gpu/drm/i915/i915_pvinfo.h | 13 ++++++++++++- drivers/gpu/drm/i915/i915_vgpu.c | 9 +++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 91fd59fe6345..acc3be54b9f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -51,6 +51,7 @@ #include "i915_pmu.h" #include "i915_query.h" #include "i915_vgpu.h" +#include "intel_uc.h" #include "intel_drv.h" #include "intel_uc.h" @@ -991,6 +992,9 @@ static void i915_mmio_cleanup(struct drm_i915_private *dev_priv) intel_teardown_mchbar(dev_priv); pci_iounmap(pdev, dev_priv->regs); + if (intel_vgpu_active(dev_priv) && dev_priv->shared_page) + pci_iounmap(pdev, dev_priv->shared_page); + } /** @@ -1024,6 +1028,21 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv) intel_uc_init_mmio(dev_priv); + if (intel_vgpu_active(dev_priv) && i915_modparams.enable_pvmmio) { + u32 bar = 0; + u32 mmio_size = 2 * 1024 * 1024; + + /* Map a share page from the end of 2M mmio region in bar0. */ + dev_priv->shared_page = (struct gvt_shared_page *) + pci_iomap_range(dev_priv->drm.pdev, bar, + mmio_size, PAGE_SIZE); + if (dev_priv->shared_page == NULL) { + ret = -EIO; + DRM_ERROR("ivi: failed to map share page.\n"); + goto err_uncore; + } + } + ret = intel_engines_init_mmio(dev_priv); if (ret) goto err_uncore; @@ -1033,6 +1052,8 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv) return 0; err_uncore: + if (intel_vgpu_active(dev_priv) && dev_priv->shared_page) + pci_iounmap(dev_priv->drm.pdev, dev_priv->shared_page); intel_uncore_fini(dev_priv); err_bridge: pci_dev_put(dev_priv->bridge_dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 671c1e66f9b7..a5f8d05793ba 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1590,6 +1590,7 @@ struct drm_i915_private { resource_size_t stolen_usable_size; /* Total size minus reserved ranges */ void __iomem *regs; + struct gvt_shared_page *shared_page; struct intel_uncore uncore; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 8bdd4043b563..062190f99a28 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -196,6 +196,11 @@ i915_param_named(domain_scaler_owner, int, 0400, * */ + +i915_param_named(enable_pvmmio, uint, 0400, + "Enable pv mmio feature and set pvmmio level, default 1." + "This parameter could only set from host, guest value is set through vgt_if"); + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 74865c23f809..358094837650 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -69,6 +69,7 @@ struct drm_printer; param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ + param(unsigned int, enable_pvmmio, 0) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index dc9bdeaa3147..d1a3e3e68512 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,15 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +#define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) + +/* + * define different levels of PVMMIO optimization + */ +enum pvmmio_levels { + PVMMIO_ELSP_SUBMIT = 0x1, +}; + /* * VGT capabilities type */ @@ -106,9 +115,11 @@ struct vgt_if { u32 execlist_context_descriptor_lo; u32 execlist_context_descriptor_hi; + u32 enable_pvmmio; + u32 pv_mmio; u32 scaler_owned; - u32 rsv7[0x200 - 25]; /* pad to one page */ + u32 rsv7[0x200 - 27]; /* pad to one page */ } __packed; #define vgtif_reg(x) \ diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 0f6182f32ded..d7a328f52978 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -79,6 +79,15 @@ void i915_check_vgpu(struct drm_i915_private *dev_priv) dev_priv->vgpu.scaler_owned = __raw_i915_read32(dev_priv, vgtif_reg(scaler_owned)); + /* If guest wants to enable pvmmio, it needs to enable it explicitly + * through vgt_if interface, and then read back the enable state from + * gvt layer. + */ + __raw_i915_write32(dev_priv, vgtif_reg(enable_pvmmio), + i915_modparams.enable_pvmmio); + i915_modparams.enable_pvmmio = __raw_i915_read16(dev_priv, + vgtif_reg(enable_pvmmio)); + dev_priv->vgpu.active = true; DRM_INFO("Virtual GPU for Intel GVT-g detected.\n"); } From ae3753cdc0e2f450c58e1093c5d58a92d77bb7d8 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1052/1276] drm/i915/gvt: get ready of memory for pvmmio To enable pvmmio feature, we need to prepare to regions memory: the mmio memory whose size is 2M for Gen8/9, and the 4K shared page. GVT creates them for every vGPU instance, guest i915 driver will map them to virtual address. Change-Id: Ifcbd0e55783e19125e98036622cd5d08624d34fa Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gvt.c | 2 ++ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/gvt/mmio.c | 22 ++++++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 6261af450ee4..e4a3823e1226 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -248,6 +248,8 @@ static void init_device_info(struct intel_gvt *gvt) info->max_support_vgpus = 8; info->cfg_space_size = PCI_CFG_SPACE_EXP_SIZE; info->mmio_size = 2 * 1024 * 1024; + /* order of mmio size. assert(2^order == mmio_size) */ + info->mmio_size_order = 9; info->mmio_bar = 0; info->gtt_start_offset = 8 * 1024 * 1024; info->gtt_entry_size = 8; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 64139240207f..ba88f722d602 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -71,6 +71,7 @@ struct intel_gvt_device_info { u32 max_support_vgpus; u32 cfg_space_size; u32 mmio_size; + u32 mmio_size_order; u32 mmio_bar; unsigned long msi_cap_offset; u32 gtt_start_offset; @@ -100,6 +101,7 @@ struct intel_vgpu_fence { struct intel_vgpu_mmio { void *vreg; void *sreg; + struct gvt_shared_page *shared_page; }; #define INTEL_GVT_MAX_BAR_NUM 4 diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 878a8a1f5ff5..4cb3f72ab56a 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -311,11 +311,21 @@ int intel_vgpu_init_mmio(struct intel_vgpu *vgpu) { const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; - vgpu->mmio.vreg = vzalloc(array_size(info->mmio_size, 2)); + BUILD_BUG_ON(sizeof(struct gvt_shared_page) != PAGE_SIZE); + + vgpu->mmio.sreg = vzalloc(info->mmio_size); + vgpu->mmio.vreg = (void *)__get_free_pages(GFP_KERNEL, + info->mmio_size_order); if (!vgpu->mmio.vreg) return -ENOMEM; - vgpu->mmio.sreg = vgpu->mmio.vreg + info->mmio_size; + vgpu->mmio.shared_page = (struct gvt_shared_page *) __get_free_pages( + GFP_KERNEL, 0); + if (!vgpu->mmio.shared_page) { + vfree(vgpu->mmio.vreg); + vgpu->mmio.vreg = NULL; + return -ENOMEM; + } intel_vgpu_reset_mmio(vgpu, true); @@ -329,6 +339,10 @@ int intel_vgpu_init_mmio(struct intel_vgpu *vgpu) */ void intel_vgpu_clean_mmio(struct intel_vgpu *vgpu) { - vfree(vgpu->mmio.vreg); - vgpu->mmio.vreg = vgpu->mmio.sreg = NULL; + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + + vfree(vgpu->mmio.sreg); + free_pages((unsigned long) vgpu->mmio.vreg, info->mmio_size_order); + free_pages((unsigned long) vgpu->mmio.shared_page, 0); + vgpu->mmio.vreg = vgpu->mmio.sreg = vgpu->mmio.shared_page = NULL; } From 67d79b7f3ee44af097c23dc4f0f5a95e7ca06ef5 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1053/1276] drm/i915: implement pvmmio in guest i915 If pvmmio is enabled in i915 host driver, guest i915 will read most MMIO register directly, which won't be trapped to host GVT. A small range MMIOs still need trap. They are filtered in a static function. This patch is to implement the pvmmio in guest i915 driver. Also, for the elsp port writting, we will optimize it to leverage pvmmio. Notice: this patch is required in GVTg guest. Change-Id: I8d51430015822976450c14085979460b9cc021d7 Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++-- drivers/gpu/drm/i915/i915_pvinfo.h | 12 ++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_lrc.c | 21 ++++++++++++++++++++- 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index acc3be54b9f4..06b786199215 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -896,6 +896,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, sizeof(device_info->platform_mask) * BITS_PER_BYTE); BUG_ON(device_info->gen > sizeof(device_info->gen_mask) * BITS_PER_BYTE); spin_lock_init(&dev_priv->irq_lock); + spin_lock_init(&dev_priv->shared_page_lock); spin_lock_init(&dev_priv->gpu_error.lock); mutex_init(&dev_priv->backlight_lock); spin_lock_init(&dev_priv->uncore.lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a5f8d05793ba..18650bc9c8e4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -55,6 +55,7 @@ #include "i915_params.h" #include "i915_reg.h" +#include "i915_pvinfo.h" #include "i915_utils.h" #include "intel_bios.h" @@ -1591,6 +1592,7 @@ struct drm_i915_private { void __iomem *regs; struct gvt_shared_page *shared_page; + spinlock_t shared_page_lock; struct intel_uncore uncore; @@ -2786,7 +2788,7 @@ static inline bool intel_gvt_active(struct drm_i915_private *dev_priv) return dev_priv->gvt; } -static inline bool intel_vgpu_active(struct drm_i915_private *dev_priv) +static inline bool intel_vgpu_active(const struct drm_i915_private *dev_priv) { return dev_priv->vgpu.active; } @@ -3586,7 +3588,11 @@ static inline u64 intel_rc6_residency_us(struct drm_i915_private *dev_priv, static inline uint##x##_t __raw_i915_read##x(const struct drm_i915_private *dev_priv, \ i915_reg_t reg) \ { \ - return read##s(dev_priv->regs + i915_mmio_reg_offset(reg)); \ + if (!intel_vgpu_active(dev_priv) || !i915_modparams.enable_pvmmio || \ + likely(!in_mmio_read_trap_list((reg).reg))) \ + return read##s(dev_priv->regs + i915_mmio_reg_offset(reg)); \ + dev_priv->shared_page->reg_addr = i915_mmio_reg_offset(reg); \ + return read##s(dev_priv->regs + i915_mmio_reg_offset(vgtif_reg(pv_mmio))); \ } #define __raw_write(x, s) \ diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index d1a3e3e68512..c1089bdedd2e 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,15 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +/* shared page(4KB) between gvt and VM, located at the first page next + * to MMIO region(2MB size normally). + */ +struct gvt_shared_page { + u32 elsp_data[4]; + u32 reg_addr; + u32 rsvd2[0x400 - 5]; +}; + #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) /* @@ -65,6 +74,9 @@ enum pvmmio_levels { #define VGT_CAPS_HWSP_EMULATION BIT(3) #define VGT_CAPS_HUGE_GTT BIT(4) +#define PVMMIO_LEVEL(dev_priv, level) \ + (intel_vgpu_active(dev_priv) && (i915_modparams.enable_pvmmio & level)) + struct vgt_if { u64 magic; /* VGT_MAGIC */ u16 version_major; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9e63cd47b60f..3c2cc2e19826 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -10652,4 +10652,31 @@ enum skl_power_gate { _ICL_DSC1_RC_BUF_THRESH_1_UDW_PB, \ _ICL_DSC1_RC_BUF_THRESH_1_UDW_PC) +/* GVT has special read process from some MMIO register, + * which so that should be trapped to GVT to make a + * complete emulation. Such MMIO is not too much, now using + * a static list to cover them. + */ +static inline bool in_mmio_read_trap_list(u32 reg) +{ + if (unlikely(reg >= PCH_GMBUS0.reg && reg <= PCH_GMBUS5.reg)) + return true; + + if (unlikely(reg == RING_TIMESTAMP(RENDER_RING_BASE).reg || + reg == RING_TIMESTAMP(BLT_RING_BASE).reg || + reg == RING_TIMESTAMP(GEN6_BSD_RING_BASE).reg || + reg == RING_TIMESTAMP(VEBOX_RING_BASE).reg || + reg == RING_TIMESTAMP(GEN8_BSD2_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(RENDER_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(BLT_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(GEN6_BSD_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(VEBOX_RING_BASE).reg)) + return true; + + if (unlikely(reg == SBI_DATA.reg || reg == 0x6c060 || reg == 0x206c)) + return true; + + return false; +} + #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 5dde7253c963..b937f9f05e6a 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -451,6 +451,8 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) struct intel_engine_execlists *execlists = &engine->execlists; struct execlist_port *port = execlists->port; unsigned int n; + u32 descs[4]; + int i = 0; /* * We can skip acquiring intel_runtime_pm_get() here as it was taken @@ -493,10 +495,27 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) GEM_BUG_ON(!n); desc = 0; } + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + BUG_ON(i >= 4); + descs[i] = upper_32_bits(desc); + descs[i + 1] = lower_32_bits(desc); + i += 2; + continue; + } write_desc(execlists, desc, n); } - + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + u32 __iomem *elsp_data = engine->i915->shared_page->elsp_data; + spin_lock(&engine->i915->shared_page_lock); + writel(descs[0], elsp_data); + writel(descs[1], elsp_data + 1); + writel(descs[2], elsp_data + 2); + writel(descs[3], execlists->submit_reg); + spin_unlock(&engine->i915->shared_page_lock); + } /* we need to manually load the submit queue */ if (execlists->ctrl_reg) writel(EL_CTRL_LOAD, execlists->ctrl_reg); From 5c673d62be4f83ac32f21bb8a6a0a4acdc4ceea3 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 1054/1276] drm/i915/gvt: implement pvmmio in GVTg If pvmmio is enabled in i915 host driver, guest i915 will read most MMIO register directly, which won't be trapped to host GVT. A small range MMIOs still need trap. They are filtered in a static function, and this patch is to implement the handler of these registers in GVTg. Also, when pvmmio is enabled, we will optimize ELSP port writing, to reduce the mmio trap numbers from 4 to 1, which can improve the guest GPU performance. Change-Id: Ic72a87499baabe9b3b2fbb5ad827e6ae062ff959 Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/acrngt.c | 92 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/handlers.c | 75 ++++++++++++++++++++--- drivers/gpu/drm/i915/gvt/hypercall.h | 2 + drivers/gpu/drm/i915/gvt/mpt.h | 21 +++++++ drivers/gpu/drm/i915/gvt/vgpu.c | 4 ++ 5 files changed, 186 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index 346a676d77bc..c6fff10a1679 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -838,6 +838,97 @@ static int acrngt_set_trap_area(unsigned long handle, u64 start, return ret; } +static int acrngt_set_pvmmio(unsigned long handle, u64 start, u64 end, bool map) +{ + int rc, i; + unsigned long mfn, shared_mfn; + unsigned long pfn = start >> PAGE_SHIFT; + u32 mmio_size_fn = acrngt_priv.gvt->device_info.mmio_size >> PAGE_SHIFT; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + + if (map) { + mfn = acrngt_virt_to_mfn(info->vgpu->mmio.vreg); + rc = acrngt_map_gfn_to_mfn(handle, pfn, mfn, mmio_size_fn, map); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + + /* map the shared page to guest */ + shared_mfn = acrngt_virt_to_mfn(info->vgpu->mmio.shared_page); + rc = acrngt_map_gfn_to_mfn(handle, pfn + mmio_size_fn, shared_mfn, 1, map); + if (rc) { + gvt_err("acrn-gvt: map shared page fail with ret %d\n", rc); + return rc; + } + + /* mmio access is trapped like memory write protection */ + rc = acrn_ioreq_add_iorange(info->client, REQ_WP, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", pfn); + return rc; + } + + for (i = 0; i < mmio_size_fn; i++) { + rc = write_protect_page(info->vm_id, + (pfn + i) << PAGE_SHIFT, true); + if (rc) { + gvt_err("failed set wp for pfn 0x%lx\n", pfn + i); + return rc; + } + } + + /* scratch reg access is trapped like mmio access, 1 page */ + rc = acrngt_map_gfn_to_mfn(handle, pfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), + mfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), 1, 0); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + rc = acrn_ioreq_add_iorange(info->client, REQ_MMIO, + (pfn << PAGE_SHIFT) + VGT_PVINFO_PAGE, + ((pfn + 1) << PAGE_SHIFT) + VGT_PVINFO_PAGE - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", + (pfn << PAGE_SHIFT) + VGT_PVINFO_PAGE); + return rc; + } + + } else { + mfn = acrngt_virt_to_mfn(info->vgpu->mmio.vreg); + rc = acrngt_map_gfn_to_mfn(handle, pfn, mfn, mmio_size_fn, map); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + rc = acrn_ioreq_del_iorange(info->client, REQ_WP, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", pfn); + return rc; + } + rc = acrn_ioreq_add_iorange(info->client, REQ_MMIO, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_del_iorange for pfn 0x%lx\n", pfn); + return rc; + } + + /* unmap the shared page to guest */ + shared_mfn = acrngt_virt_to_mfn(info->vgpu->mmio.shared_page); + rc = acrngt_map_gfn_to_mfn(handle, pfn + mmio_size_fn, shared_mfn, 1, map); + if (rc) { + gvt_err("acrn-gvt: map shared page fail with ret %d\n", rc); + return rc; + } + } + return rc; +} + static int acrngt_dom0_ready(void) { char *env[] = {"GVT_DOM0_READY=1", NULL}; @@ -880,6 +971,7 @@ struct intel_gvt_mpt acrn_gvt_mpt = { .dma_map_guest_page = acrngt_dma_map_guest_page, .dma_unmap_guest_page = acrngt_dma_unmap_guest_page, .set_trap_area = acrngt_set_trap_area, + .set_pvmmio = acrngt_set_pvmmio, .dom0_ready = acrngt_dom0_ready, }; EXPORT_SYMBOL_GPL(acrn_gvt_mpt); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 88d34ef2c057..14a75c6dff4d 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1145,6 +1145,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { bool invalid_read = false; + int ret = 0; read_vreg(vgpu, offset, p_data, bytes); @@ -1159,9 +1160,27 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, _vgtif_reg(avail_rs.fence_num) + 4) invalid_read = true; break; + case _vgtif_reg(pv_mmio): + /* a remap happens from guest mmio read operation, the target reg offset + * is in the first DWORD of shared_page. + */ + { + u32 reg = vgpu->mmio.shared_page->reg_addr; + struct intel_gvt_mmio_info *mmio; + + mmio = find_mmio_info(vgpu->gvt, rounddown(reg, 4)); + if (mmio) + ret = mmio->read(vgpu, reg, p_data, bytes); + else + ret = intel_vgpu_default_mmio_read(vgpu, reg, p_data, + bytes); + break; + } + case 0x78010: /* vgt_caps */ case 0x7881c: case _vgtif_reg(scaler_owned): + case _vgtif_reg(enable_pvmmio): break; default: invalid_read = true; @@ -1171,7 +1190,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, gvt_vgpu_err("invalid pvinfo read: [%x:%x] = %x\n", offset, bytes, *(u32 *)p_data); vgpu->pv_notified = true; - return 0; + return ret; } static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) @@ -1219,6 +1238,26 @@ static int send_display_ready_uevent(struct intel_vgpu *vgpu, int ready) return kobject_uevent_env(kobj, KOBJ_ADD, env); } +#define INTEL_GVT_PCI_BAR_GTTMMIO 0 +static int set_pvmmio(struct intel_vgpu *vgpu, bool map) +{ + u64 start, end; + u64 val; + int ret; + + val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_0]; + if (val & PCI_BASE_ADDRESS_MEM_TYPE_64) + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + else + start = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + + start &= ~GENMASK(3, 0); + end = start + vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size - 1; + + ret = intel_gvt_hypervisor_set_pvmmio(vgpu, start, end, map); + return ret; +} + static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -1235,6 +1274,18 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, case _vgtif_reg(g2v_notify): ret = handle_g2v_notification(vgpu, data); break; + case _vgtif_reg(enable_pvmmio): + if (i915_modparams.enable_pvmmio) { + vgpu_vreg(vgpu, offset) = data & + i915_modparams.enable_pvmmio; + if (set_pvmmio(vgpu, !!vgpu_vreg(vgpu, offset))) { + vgpu_vreg(vgpu, offset) = 0; + break; + } + } else { + vgpu_vreg(vgpu, offset) = 0; + } + break; /* add xhot and yhot to handled list to avoid error log */ case _vgtif_reg(cursor_x_hot): case _vgtif_reg(cursor_y_hot): @@ -1628,6 +1679,7 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, int ring_id = intel_gvt_render_mmio_to_ring_id(vgpu->gvt, offset); struct intel_vgpu_execlist *execlist; u32 data = *(u32 *)p_data; + u32 *elsp_data = vgpu->mmio.shared_page->elsp_data; int ret = 0; if (WARN_ON(ring_id < 0 || ring_id >= I915_NUM_ENGINES)) @@ -1635,16 +1687,23 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, execlist = &vgpu->submission.execlist[ring_id]; - execlist->elsp_dwords.data[3 - execlist->elsp_dwords.index] = data; - if (execlist->elsp_dwords.index == 3) { + if (VGPU_PVMMIO(vgpu) & PVMMIO_ELSP_SUBMIT) { + execlist->elsp_dwords.data[3] = elsp_data[0]; + execlist->elsp_dwords.data[2] = elsp_data[1]; + execlist->elsp_dwords.data[1] = elsp_data[2]; + execlist->elsp_dwords.data[0] = data; ret = intel_vgpu_submit_execlist(vgpu, ring_id); - if(ret) - gvt_vgpu_err("fail submit workload on ring %d\n", - ring_id); + } else { + execlist->elsp_dwords.data[3 - execlist->elsp_dwords.index] = data; + if (execlist->elsp_dwords.index == 3) + ret = intel_vgpu_submit_execlist(vgpu, ring_id); + ++execlist->elsp_dwords.index; + execlist->elsp_dwords.index &= 0x3; } - ++execlist->elsp_dwords.index; - execlist->elsp_dwords.index &= 0x3; + if (ret) + gvt_vgpu_err("fail submit workload on ring %d\n", ring_id); + return ret; } diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index d4b7929c8bee..4c550627e78e 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -60,6 +60,8 @@ struct intel_gvt_mpt { unsigned long mfn, unsigned int nr, bool map); int (*set_trap_area)(unsigned long handle, u64 start, u64 end, bool map); + int (*set_pvmmio)(unsigned long handle, u64 start, u64 end, + bool map); int (*set_opregion)(void *vgpu); int (*get_vfio_device)(void *vgpu); void (*put_vfio_device)(void *vgpu); diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index feed7adb6fde..6eef2e01e46a 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -300,6 +300,27 @@ static inline int intel_gvt_hypervisor_set_trap_area( return intel_gvt_host.mpt->set_trap_area(vgpu->handle, start, end, map); } +/** + * intel_gvt_hypervisor_set_pvmmio - Set the pvmmio area + * @vgpu: a vGPU + * @start: the beginning of the guest physical address region + * @end: the end of the guest physical address region + * @map: map or unmap + * + * Returns: + * Zero on success, negative error code if failed. + */ +static inline int intel_gvt_hypervisor_set_pvmmio( + struct intel_vgpu *vgpu, u64 start, u64 end, bool map) +{ + /* a MPT implementation could have MMIO trapped elsewhere */ + if (!intel_gvt_host.mpt->set_pvmmio) + return -ENOENT; + + return intel_gvt_host.mpt->set_pvmmio(vgpu->handle, start, end, map); +} + + /** * intel_gvt_hypervisor_set_opregion - Set opregion for guest * @vgpu: a vGPU diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 5cb46999b238..9e4a0b2b5f22 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -75,6 +75,8 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) |= 1 << (pipe * SKL_NUM_SCALERS + scaler); + vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) = 0; + gvt_dbg_core("Populate PVINFO PAGE for vGPU %d\n", vgpu->id); gvt_dbg_core("aperture base [GMADR] 0x%llx size 0x%llx\n", vgpu_aperture_gmadr_base(vgpu), vgpu_aperture_sz(vgpu)); @@ -540,6 +542,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, unsigned int resetting_eng = dmlr ? ALL_ENGINES : engine_mask; enum intel_engine_id i; struct intel_engine_cs *engine; + bool enable_pvmmio = vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)); gvt_dbg_core("------------------------------------------\n"); gvt_dbg_core("resseting vgpu%d, dmlr %d, engine_mask %08x\n", @@ -574,6 +577,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, intel_vgpu_reset_mmio(vgpu, dmlr); populate_pvinfo_page(vgpu); + vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) = enable_pvmmio; intel_vgpu_reset_display(vgpu); if (dmlr) { From 307f54ecfd83039c0e0d1bd69511573a43612946 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 26 Feb 2018 20:07:34 +0800 Subject: [PATCH 1055/1276] drm/i915/gvt: add pvmmio support in preempt context submission This patch added the pvmmio support in preemption context submission for gvt-g guest. And because GVT-g doesn't support preemption in guests, this patch also disabled preempttion in guest. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/intel_lrc.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index b937f9f05e6a..534223874dd2 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -186,7 +186,8 @@ static inline bool need_preempt(const struct intel_engine_cs *engine, const struct i915_request *last, int prio) { - return (intel_engine_has_preemption(engine) && + return (!intel_vgpu_active(engine->i915) && + intel_engine_has_preemption(engine) && __execlists_need_preempt(prio, rq_prio(last)) && !i915_request_completed(last)); } @@ -571,10 +572,24 @@ static void inject_preempt_context(struct intel_engine_cs *engine) * the state of the GPU is known (idle). */ GEM_TRACE("%s\n", engine->name); - for (n = execlists_num_ports(execlists); --n; ) - write_desc(execlists, 0, n); - write_desc(execlists, ce->lrc_desc, n); + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + u32 __iomem *elsp_data = engine->i915->shared_page->elsp_data; + + spin_lock(&engine->i915->shared_page_lock); + writel(0, elsp_data); + writel(0, elsp_data + 1); + writel(upper_32_bits(ce->lrc_desc), elsp_data + 2); + writel(lower_32_bits(ce->lrc_desc), execlists->submit_reg); + spin_unlock(&engine->i915->shared_page_lock); + + } else { + for (n = execlists_num_ports(execlists); --n; ) + write_desc(execlists, 0, n); + + write_desc(execlists, ce->lrc_desc, n); + } /* we need to manually load the submit queue */ if (execlists->ctrl_reg) From 1512d5d6c75ceee114ee3c629767e1b749f4627e Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1056/1276] drm/i915: Use 64-bit write to optimize writing fence_reg On VGPU scenario the read/write operation of fence_reg will be trapped by the GVT-g. Then gvt-g follows the HW spec to program the fence_reg. And the gvt-g takes care of updating the fence reg correctly for any trapped value of fence reg. So it is unnecessary to read/write fence reg several times. It is enough that the fence reg is written only value in 64-bit mode. This will help to reduce the redundant trap of fence_reg mmio operation. V1->V2: Add back the condition judgement of !pipelined Signed-off-by: Zhao Yakui Reviewed-by: He Min --- drivers/gpu/drm/i915/i915_gem_fence_reg.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c index d548ac05ccd7..317e376cc2da 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c @@ -63,6 +63,7 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, i915_reg_t fence_reg_lo, fence_reg_hi; int fence_pitch_shift; u64 val; + struct drm_i915_private *dev_priv = fence->i915; if (INTEL_GEN(fence->i915) >= 6) { fence_reg_lo = FENCE_REG_GEN6_LO(fence->id); @@ -92,9 +93,17 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, val |= I965_FENCE_REG_VALID; } - if (!pipelined) { - struct drm_i915_private *dev_priv = fence->i915; - + if (intel_vgpu_active(dev_priv)) { + /* Use the 64-bit RW to write fence reg on VGPU mode. + * The GVT-g can trap the written val of VGPU to program the + * fence reg. And the fence write in gvt-g follows the + * sequence of off/read/double-write/read. This assures that + * the fence reg is configured as expected. + * At the same time the 64-bit op can help to reduce the num + * of VGPU trap for the fence reg. + */ + I915_WRITE64_FW(fence_reg_lo, val); + } else if (!pipelined) { /* To w/a incoherency with non-atomic 64-bit register updates, * we split the 64-bit update into two 32-bit writes. In order * for a partial fence not to be evaluated between writes, we From ac4f70a503895efea07f9afbc5ffb6cf108eada7 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 10 Jul 2018 09:55:30 +0800 Subject: [PATCH 1057/1276] drm/i915/gvt: don't treat EINVAL if trap pci_command and pci_status together Previously we only support single pci_command writing trap. While when system suspends, pci_command and pci_status are written together in 32 bits, GVT-g also need trap them in such scenario. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/cfg_space.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 707b0a50da3c..f6bcfcb571b7 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -295,9 +295,22 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, /* First check if it's PCI_COMMAND */ if (IS_ALIGNED(offset, 2) && offset == PCI_COMMAND) { - if (WARN_ON(bytes > 2)) + if (WARN_ON(bytes != 2 && bytes != 4)) return -EINVAL; - return emulate_pci_command_write(vgpu, offset, p_data, bytes); + + ret = -EINVAL; + if (bytes == 2) + ret = emulate_pci_command_write(vgpu, offset, + p_data, bytes); + if (bytes == 4) { + ret = emulate_pci_command_write(vgpu, offset, + p_data, 2); + if (ret) + return ret; + vgpu_pci_cfg_mem_write(vgpu, offset + 2, + (u8 *)p_data + 2, 2); + } + return ret; } switch (rounddown(offset, 4)) { From c6c80ac186b69656f77f7b9730e32c8e5b712d24 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1058/1276] drm/i915/gvt: pvmmio optimization for plane update It is performance optimization to reduce plane related register trap counter. When update plane, multiple plane related registers are updated together, optimize it to firstly cache all register values in share page, then only PLANE_SURF register writing is trapped. Plane pvmmio level is PVMMIO_PLANE_UPDATE. Patch for both SOS and UOS. V2: add memset tmp_plane to be more safer and add more commit description Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_pvinfo.h | 27 +++++++++++- drivers/gpu/drm/i915/intel_sprite.c | 68 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index c1089bdedd2e..740b2da14186 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,13 +49,37 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +#define PLANE_COLOR_CTL_BIT (1 << 0) +#define PLANE_KEY_BIT (1 << 1) +#define PLANE_SCALER_BIT (1 << 2) + +struct pv_plane_update { + u32 flags; + u32 plane_color_ctl; + u32 plane_key_val; + u32 plane_key_max; + u32 plane_key_msk; + u32 plane_offset; + u32 plane_stride; + u32 plane_size; + u32 plane_aux_dist; + u32 plane_aux_offset; + u32 ps_ctrl; + u32 ps_pwr_gate; + u32 ps_win_ps; + u32 ps_win_sz; + u32 plane_pos; + u32 plane_ctl; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; - u32 rsvd2[0x400 - 5]; + struct pv_plane_update pv_plane; + u32 rsvd2[0x400 - 21]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -65,6 +89,7 @@ struct gvt_shared_page { */ enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, + PVMMIO_PLANE_UPDATE = 0x2, }; /* diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 11b7afc0a0d3..4b89ba3e021c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -232,6 +232,68 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) #endif } +static void pv_update_plane_reg(struct intel_plane *plane, + u32 stride, uint32_t src_w, uint32_t src_h, + uint32_t crtc_w, uint32_t crtc_h, u32 aux_stride, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + int i; + struct pv_plane_update tmp_plane; + uint32_t x = plane_state->main.x; + uint32_t y = plane_state->main.y; + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + u32 __iomem *pv_plane = (u32 *)&(dev_priv->shared_page->pv_plane); + + memset(&tmp_plane, 0, sizeof(struct pv_plane_update)); + if (IS_GEMINILAKE(dev_priv) || IS_CANNONLAKE(dev_priv)) { + tmp_plane.flags |= PLANE_COLOR_CTL_BIT; + tmp_plane.plane_color_ctl = PLANE_COLOR_PIPE_GAMMA_ENABLE | + PLANE_COLOR_PIPE_CSC_ENABLE | + PLANE_COLOR_PLANE_GAMMA_DISABLE; + } + + if (plane_state->ckey.flags) { + tmp_plane.flags |= PLANE_KEY_BIT; + tmp_plane.plane_key_val = plane_state->ckey.min_value; + tmp_plane.plane_key_max = plane_state->ckey.max_value; + tmp_plane.plane_key_msk = plane_state->ckey.channel_mask; + } + + tmp_plane.plane_offset = (y << 16) | x; + tmp_plane.plane_stride = stride; + tmp_plane.plane_size = (src_h << 16) | src_w; + tmp_plane.plane_aux_dist = + (plane_state->aux.offset - plane_state->main.offset) | + aux_stride; + tmp_plane.plane_aux_offset = + (plane_state->aux.y << 16) | plane_state->aux.x; + + /* program plane scaler */ + if (plane_state->scaler_id >= 0) { + tmp_plane.flags |= PLANE_SCALER_BIT; + tmp_plane.ps_ctrl = PS_SCALER_EN | PS_PLANE_SEL(plane->id) | + crtc_state->scaler_state.scalers[plane_state->scaler_id].mode; + tmp_plane.ps_pwr_gate = 0; + tmp_plane.ps_win_ps = + (plane_state->base.dst.x1 << 16) | plane_state->base.dst.y1; + tmp_plane.ps_win_sz = ((crtc_w + 1) << 16) | (crtc_h + 1); + tmp_plane.plane_pos = 0; + } else { + tmp_plane.plane_pos = + (plane_state->base.dst.y1 << 16) | plane_state->base.dst.x1; + } + + tmp_plane.plane_ctl = plane_state->ctl; + + spin_lock(&dev_priv->shared_page_lock); + for (i = 0; i < sizeof(struct pv_plane_update) / 4; i++) + writel(*((u32 *)(&tmp_plane) + i), pv_plane + i); + I915_WRITE_FW(PLANE_SURF(plane->pipe, plane->id), + intel_plane_ggtt_offset(plane_state) + plane_state->main.offset); + spin_unlock(&dev_priv->shared_page_lock); +} + void skl_update_plane(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, @@ -268,6 +330,12 @@ skl_update_plane(struct intel_plane *plane, crtc_w--; crtc_h--; + if (PVMMIO_LEVEL(dev_priv, PVMMIO_PLANE_UPDATE)) { + pv_update_plane_reg(plane, stride, src_w, src_h, + crtc_w, crtc_h, aux_stride, crtc_state, plane_state); + return; + } + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) From 120f451be23e5fd4be3c2eb456b0d215db4b7826 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1059/1276] drm/i915/gvt: handling pvmmio update of plane registers in GVT-g When pvmmio level PVMMIO_PLANE_UPDATE is enabled, need handle multiple plane related registers updating together when PLANE_SURF is traped. sos only patch. V2: restore sequence of skl_plane_mmio_write/skl_plane_mmio_write and skl_plane_surf_write, while need add extra declarations. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 14a75c6dff4d..afa15c0bb347 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -778,6 +778,66 @@ static int spr_surf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } +static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes); +static int skl_ps_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes); + +static void pvmmio_update_plane_register(struct intel_vgpu *vgpu, + unsigned int pipe, unsigned int plane) +{ + struct pv_plane_update *pv_plane = &vgpu->mmio.shared_page->pv_plane; + + /* null function for PLANE_COLOR_CTL, PLANE_AUX_DIST, PLANE_AUX_OFFSET, + * and SKL_PS_PWR_GATE register trap + */ + + if (pv_plane->flags & PLANE_KEY_BIT) { + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYVAL(pipe, plane)), + &pv_plane->plane_key_val, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYMAX(pipe, plane)), + &pv_plane->plane_key_max, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYMSK(pipe, plane)), + &pv_plane->plane_key_msk, 4); + } + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_OFFSET(pipe, plane)), + &pv_plane->plane_offset, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_STRIDE(pipe, plane)), + &pv_plane->plane_stride, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_SIZE(pipe, plane)), + &pv_plane->plane_size, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_AUX_DIST(pipe, plane)), + &pv_plane->plane_aux_dist, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_AUX_OFFSET(pipe, plane)), + &pv_plane->plane_aux_offset, 4); + + if (pv_plane->flags & PLANE_SCALER_BIT) { + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_CTRL(pipe, plane)), + &pv_plane->ps_ctrl, 4); + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_WIN_POS(pipe, plane)), + &pv_plane->ps_win_ps, 4); + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_WIN_SZ(pipe, plane)), + &pv_plane->ps_win_sz, 4); + } + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_POS(pipe, plane)), + &pv_plane->plane_pos, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_CTL(pipe, plane)), + &pv_plane->plane_ctl, 4); +} + static int trigger_aux_channel_interrupt(struct intel_vgpu *vgpu, unsigned int reg) { @@ -2850,6 +2910,10 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); int flip_event = SKL_FLIP_EVENT(pipe, plane); + /* plane disable is not pv and it is indicated by value 0 */ + if (*(u32 *)p_data != 0 && VGPU_PVMMIO(vgpu) & PVMMIO_PLANE_UPDATE) + pvmmio_update_plane_register(vgpu, pipe, plane); + write_vreg(vgpu, offset, p_data, bytes); vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); From cf01fc28f4a68a5441c1a37dab3a7208ef833f53 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 23 Jul 2018 10:51:33 +0800 Subject: [PATCH 1060/1276] drm/i915/gvt: enable plane update pvmmio through enable_pvmmio param plane update pvmmio level is 0x2, need set it in enable_pvmmio for both SOS and UOS kernel driver. Patch for both SOS and UOS. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 358094837650..d9b901f622eb 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -69,7 +69,8 @@ struct drm_printer; param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ - param(unsigned int, enable_pvmmio, 0) \ + param(unsigned int, enable_pvmmio, \ + PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From ee455428b1d5be59c02ef6e34d048fb4af8c84e0 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 31 Jul 2018 12:06:29 +0800 Subject: [PATCH 1061/1276] drm/i915/gvt: implement gfn_to_mfn with identical 1:1 mapping check If hypervisor is implementing identical 1:1 memory mapping for sos kernel, sos gpa equals to hpa, so we don't need call hypercall for gfn_to_mfn. Currently this is a hack solution in function is_identical_mmap, later hypervisor will provide one hypercall to let SOS query such kind of info. V2: add error handling, if map_guest_phys fail, still call vhm_gpa2hpa Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/acrngt.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index c6fff10a1679..abde541b76f8 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -787,13 +787,33 @@ static int acrngt_write_gpa(unsigned long handle, unsigned long gpa, return 0; } +static bool is_identical_mmap(void) +{ + /* todo: need add hypercall to get such info from hypervisor */ + return true; +} + static unsigned long acrngt_gfn_to_pfn(unsigned long handle, unsigned long gfn) { unsigned long hpa; struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("convert gfn 0x%lx to pfn\n", gfn); + if (is_identical_mmap()) { + void *va = NULL; + + va = map_guest_phys(info->vm_id, gfn << PAGE_SHIFT, + 1 << PAGE_SHIFT); + if (!va) { + gvt_err("GVT: can not map gfn = 0x%lx!!!\n", gfn); + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + } else { + hpa = virt_to_phys(va); + } + } else { + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + } - hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); return hpa >> PAGE_SHIFT; } From ec8287300479372c76b0593a6631727d6149bec3 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1062/1276] drm/i915/gvt: cached read_gpa optimization in shadow ppgtt update During shadow ppgtt update, we need call intel_gvt_hypervisor_read_gpa every entry for whole ppgtt page, for performance consideration, optmize it by firstly reading whole page ppgtt gpa content into one scratch page, then following read_gpa directly read from scratch page. Have vgpu->ge_cache_enable to control, currently we only cache in ppgtt_populate_shadow_page for pte update case. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/gtt.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index af3b1dea4510..3fbce20268c6 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -303,6 +303,18 @@ static inline int gtt_get_entry64(void *pt, return -EINVAL; if (hypervisor_access) { + if (vgpu->ge_cache_enable && vgpu->cached_guest_entry) { + if (index == 0) { + ret = intel_gvt_hypervisor_read_gpa(vgpu, gpa, + vgpu->cached_guest_entry, + I915_GTT_PAGE_SIZE); + if (WARN_ON(ret)) + return ret; + } + e->val64 = *(vgpu->cached_guest_entry + index); + return 0; + } + ret = intel_gvt_hypervisor_read_gpa(vgpu, gpa + (index << info->gtt_entry_size_shift), &e->val64, 8); @@ -1277,8 +1289,10 @@ static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) trace_spt_change(spt->vgpu->id, "born", spt, spt->guest_page.gfn, spt->shadow_page.type); + vgpu->ge_cache_enable = true; for_each_present_guest_entry(spt, &ge, i) { if (gtt_type_is_pt(get_next_pt_type(ge.type))) { + vgpu->ge_cache_enable = false; s = ppgtt_populate_spt_by_guest_entry(vgpu, &ge); if (IS_ERR(s)) { ret = PTR_ERR(s); @@ -1300,6 +1314,7 @@ static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) goto fail; } } + vgpu->ge_cache_enable = false; return 0; fail: gvt_vgpu_err("fail: shadow page %p guest entry 0x%llx type %d\n", @@ -2428,6 +2443,13 @@ int intel_vgpu_init_gtt(struct intel_vgpu *vgpu) intel_vgpu_reset_ggtt(vgpu, false); + vgpu->cached_guest_entry = kzalloc(I915_GTT_PAGE_SIZE, GFP_KERNEL); + if (!vgpu->cached_guest_entry) { + gvt_vgpu_err("fail to allocate cached_guest_entry page\n"); + return -ENOMEM; + } + vgpu->ge_cache_enable = false; + return create_scratch_page_tree(vgpu); } @@ -2470,6 +2492,7 @@ void intel_vgpu_clean_gtt(struct intel_vgpu *vgpu) { intel_vgpu_destroy_all_ppgtt_mm(vgpu); intel_vgpu_destroy_ggtt_mm(vgpu); + kfree(vgpu->cached_guest_entry); release_scratch_page_tree(vgpu); } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index ba88f722d602..1a287ba76923 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -235,6 +235,9 @@ struct intel_vgpu { struct completion vblank_done; u32 scan_nonprivbb; + + unsigned long long *cached_guest_entry; + bool ge_cache_enable; }; /* validating GM healthy status*/ From 0240cae14919d6af2392af17f21b2fc314f1fab0 Mon Sep 17 00:00:00 2001 From: Zhao Yan Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1063/1276] drm/i915/gvt: add a fastpath for cmd parsing on MI_NOOP MI_NOOP is a common command appearing in almost all command buffers, put it into a fastpath can improve perfomance, especially in command buffers contains lots of MI_NOOPs (0s). Take glmark2 as an example, 3% performance increase is observed after introduced this patch. Meanwhile, in case where abundant in MI_NOOPs, up to 12% performance increase is measured. v2: use lowercase for index of MI_NOOP in cmd_info (zhenyu wang) Signed-off-by: Li Weinan Signed-off-by: Zhao Yan Signed-off-by: Zhenyu Wang Reviewed-by: Zhao Yakui Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 95244b2cb679..8d130d4d58b7 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -1840,6 +1840,8 @@ static int cmd_handler_mi_batch_buffer_start(struct parser_exec_state *s) return ret; } +static int mi_noop_index; + static struct cmd_info cmd_info[] = { {"MI_NOOP", OP_MI_NOOP, F_LEN_CONST, R_ALL, D_ALL, 0, 1, NULL}, @@ -2525,7 +2527,12 @@ static int cmd_parser_exec(struct parser_exec_state *s) cmd = cmd_val(s, 0); - info = get_cmd_info(s->vgpu->gvt, cmd, s->ring_id); + /* fastpath for MI_NOOP */ + if (cmd == MI_NOOP) + info = &cmd_info[mi_noop_index]; + else + info = get_cmd_info(s->vgpu->gvt, cmd, s->ring_id); + if (info == NULL) { gvt_vgpu_err("unknown cmd 0x%x, opcode=0x%x, addr_type=%s, ring %d, workload=%p\n", cmd, get_opcode(cmd, s->ring_id), @@ -2955,6 +2962,8 @@ static int init_cmd_table(struct intel_gvt *gvt) kfree(e); return -EEXIST; } + if (cmd_info[i].opcode == OP_MI_NOOP) + mi_noop_index = i; INIT_HLIST_NODE(&e->hlist); add_cmd_entry(gvt, e); From 6832ae7f3e9a68fcf3d4c2749774acacbbc2269e Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 1064/1276] drm/i915/gvt: notify ppgtt update through g2v This patch extends g2v notification to notify host GVT-g of ppgtt update from guest, including alloc_4lvl, clear_4lv4 and insert_4lvl. It uses shared page to pass the additional params. This patch also add one new pvmmio level to control ppgtt update. This patch is needed for both uos and sos v2: - create a struct for ppggt update in shared page. - use multiple notifications in case insert size is too big. v3: - not pass pd pages, let GVT-g read from guest memory instead. v4: - not change rsvd2 type. v5: - pass cache_level to GVT-g Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_gem_gtt.c | 38 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_pvinfo.h | 14 ++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 6e792e3167a4..c8c1df7812d7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -998,6 +998,8 @@ static void gen8_ppgtt_clear_4lvl(struct i915_address_space *vm, struct i915_pml4 *pml4 = &ppgtt->pml4; struct i915_page_directory_pointer *pdp; unsigned int pml4e; + u64 orig_start = start; + u64 orig_length = length; GEM_BUG_ON(!use_4lvl(vm)); @@ -1011,6 +1013,17 @@ static void gen8_ppgtt_clear_4lvl(struct i915_address_space *vm, free_pdp(vm, pdp); } + + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(pml4), &pv_ppgtt->pdp); + writeq(orig_start, &pv_ppgtt->start); + writeq(orig_length, &pv_ppgtt->length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_CLEAR); + } } static inline struct sgt_dma { @@ -1250,6 +1263,18 @@ static void gen8_ppgtt_insert_4lvl(struct i915_address_space *vm, flags)) GEM_BUG_ON(idx.pml4e >= GEN8_PML4ES_PER_PML4); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(&ppgtt->pml4), &pv_ppgtt->pdp); + writeq(vma->node.start, &pv_ppgtt->start); + writeq(vma->node.size, &pv_ppgtt->length); + writel(cache_level, &pv_ppgtt->cache_level); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_INSERT); + } + vma->page_sizes.gtt = I915_GTT_PAGE_SIZE; } } @@ -1498,6 +1523,8 @@ static int gen8_ppgtt_alloc_4lvl(struct i915_address_space *vm, u64 from = start; u32 pml4e; int ret; + u64 orig_start = start; + u64 orig_length = length; gen8_for_each_pml4e(pdp, pml4, start, length, pml4e) { if (pml4->pdps[pml4e] == vm->scratch_pdp) { @@ -1514,6 +1541,17 @@ static int gen8_ppgtt_alloc_4lvl(struct i915_address_space *vm, goto unwind_pdp; } + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(pml4), &pv_ppgtt->pdp); + writeq(orig_start, &pv_ppgtt->start); + writeq(orig_length, &pv_ppgtt->length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_ALLOC); + } + return 0; unwind_pdp: diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 740b2da14186..9c76cab07010 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -46,6 +46,9 @@ enum vgt_g2v_type { VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY, VGT_G2V_EXECLIST_CONTEXT_CREATE, VGT_G2V_EXECLIST_CONTEXT_DESTROY, + VGT_G2V_PPGTT_L4_ALLOC, + VGT_G2V_PPGTT_L4_CLEAR, + VGT_G2V_PPGTT_L4_INSERT, VGT_G2V_MAX, }; @@ -72,6 +75,13 @@ struct pv_plane_update { u32 plane_ctl; }; +struct pv_ppgtt_update { + u64 pdp; + u64 start; + u64 length; + u32 cache_level; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ @@ -79,7 +89,8 @@ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; struct pv_plane_update pv_plane; - u32 rsvd2[0x400 - 21]; + struct pv_ppgtt_update pv_ppgtt; + u32 rsvd2[0x400 - 30]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -90,6 +101,7 @@ struct gvt_shared_page { enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, PVMMIO_PLANE_UPDATE = 0x2, + PVMMIO_PPGTT_UPDATE = 0x10, }; /* From e9d4206065e3e75af8f847e69e858f1439c4488c Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 1065/1276] drm/i915/gvt: handle ppgtt update from g2v This patch handles ppgtt update from g2v notification. It read out ppgtt pte entries from guest pte tables page and convert them to host pfns. It creates local ppgtt tables and insert the content pages into the local ppgtt tables directly, which does not track the usage of guest page table and removes the cost of write protection from the original shadow page mechansim. This patch is only for sos. v3: - Not pass pd pages, let GVT-g read from guest memory instead. v4: - fix page walk error. - remove insert_4lvl_sg. v5: - use cache_level from guest. Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/gtt.c | 354 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/gtt.h | 11 +- drivers/gpu/drm/i915/gvt/handlers.c | 6 + 3 files changed, 370 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 3fbce20268c6..0c7160a3b925 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1757,6 +1757,32 @@ static int ppgtt_handle_guest_write_page_table_bytes( return 0; } +static void invalidate_mm_pv(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_gvt *gvt = vgpu->gvt; + struct intel_gvt_gtt *gtt = &gvt->gtt; + struct intel_gvt_gtt_pte_ops *ops = gtt->pte_ops; + struct intel_gvt_gtt_entry se; + + if (WARN_ON(mm->ppgtt_mm.root_entry_type != + GTT_TYPE_PPGTT_ROOT_L4_ENTRY)) + return; + + i915_ppgtt_close(&mm->ppgtt_mm.ppgtt->vm); + i915_ppgtt_put(mm->ppgtt_mm.ppgtt); + + ppgtt_get_shadow_root_entry(mm, &se, 0); + if (!ops->test_present(&se)) + return; + trace_spt_guest_change(vgpu->id, "destroy root pointer", + NULL, se.type, se.val64, 0); + se.val64 = 0; + ppgtt_set_shadow_root_entry(mm, &se, 0); + + mm->ppgtt_mm.shadowed = false; +} + static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) { struct intel_vgpu *vgpu = mm->vgpu; @@ -1769,6 +1795,11 @@ static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) if (!mm->ppgtt_mm.shadowed) return; + if (VGPU_PVMMIO(mm->vgpu) & PVMMIO_PPGTT_UPDATE) { + invalidate_mm_pv(mm); + return; + } + for (index = 0; index < ARRAY_SIZE(mm->ppgtt_mm.shadow_pdps); index++) { ppgtt_get_shadow_root_entry(mm, &se, index); @@ -1786,6 +1817,33 @@ static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) mm->ppgtt_mm.shadowed = false; } +static int shadow_mm_pv(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_gvt *gvt = vgpu->gvt; + struct intel_gvt_gtt_entry se; + + if (WARN_ON(mm->ppgtt_mm.root_entry_type != + GTT_TYPE_PPGTT_ROOT_L4_ENTRY)) + return -EINVAL; + + mm->ppgtt_mm.ppgtt = i915_ppgtt_create(gvt->dev_priv, NULL); + if (IS_ERR(mm->ppgtt_mm.ppgtt)) { + gvt_vgpu_err("fail to create ppgtt for pdp 0x%llx\n", + px_dma(&mm->ppgtt_mm.ppgtt->pml4)); + return PTR_ERR(mm->ppgtt_mm.ppgtt); + } + + se.type = GTT_TYPE_PPGTT_ROOT_L4_ENTRY; + se.val64 = px_dma(&mm->ppgtt_mm.ppgtt->pml4); + ppgtt_set_shadow_root_entry(mm, &se, 0); + + trace_spt_guest_change(vgpu->id, "populate root pointer", + NULL, se.type, se.val64, 0); + mm->ppgtt_mm.shadowed = true; + + return 0; +} static int shadow_ppgtt_mm(struct intel_vgpu_mm *mm) { @@ -1800,6 +1858,9 @@ static int shadow_ppgtt_mm(struct intel_vgpu_mm *mm) if (mm->ppgtt_mm.shadowed) return 0; + if (VGPU_PVMMIO(mm->vgpu) & PVMMIO_PPGTT_UPDATE) + return shadow_mm_pv(mm); + mm->ppgtt_mm.shadowed = true; for (index = 0; index < ARRAY_SIZE(mm->ppgtt_mm.guest_pdps); index++) { @@ -2788,3 +2849,296 @@ void intel_vgpu_reset_gtt(struct intel_vgpu *vgpu) intel_vgpu_destroy_all_ppgtt_mm(vgpu); intel_vgpu_reset_ggtt(vgpu, true); } + +int intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("alloc_4lvl pdp=%llx start=%llx length=%llx\n", + pv_ppgtt->pdp, pv_ppgtt->start, + pv_ppgtt->length); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("failed to find mm for pdp 0x%llx\n", pdps[0]); + ret = -EINVAL; + } else { + ret = mm->ppgtt_mm.ppgtt->vm.allocate_va_range( + &mm->ppgtt_mm.ppgtt->vm, + pv_ppgtt->start, pv_ppgtt->length); + if (ret) + gvt_vgpu_err("failed to alloc for pdp %llx\n", pdps[0]); + } + + return ret; +} + +int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("clear_4lvl pdp=%llx start=%llx length=%llx\n", + pv_ppgtt->pdp, pv_ppgtt->start, + pv_ppgtt->length); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("failed to find mm for pdp 0x%llx\n", pdps[0]); + ret = -EINVAL; + } else { + mm->ppgtt_mm.ppgtt->vm.clear_range( + &mm->ppgtt_mm.ppgtt->vm, + pv_ppgtt->start, pv_ppgtt->length); + } + + return ret; +} + +#define GEN8_PML4E_SIZE (1UL << GEN8_PML4E_SHIFT) +#define GEN8_PML4E_SIZE_MASK (~(GEN8_PML4E_SIZE - 1)) +#define GEN8_PDPE_SIZE (1UL << GEN8_PDPE_SHIFT) +#define GEN8_PDPE_SIZE_MASK (~(GEN8_PDPE_SIZE - 1)) +#define GEN8_PDE_SIZE (1UL << GEN8_PDE_SHIFT) +#define GEN8_PDE_SIZE_MASK (~(GEN8_PDE_SIZE - 1)) + +#define pml4_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PML4E_SIZE) & GEN8_PML4E_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +#define pdp_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PDPE_SIZE) & GEN8_PDPE_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +#define pd_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PDE_SIZE) & GEN8_PDE_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +struct ppgtt_walk { + unsigned long *mfns; + int mfn_index; + unsigned long *pt; +}; + +static int walk_pt_range(struct intel_vgpu *vgpu, u64 pt, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long start_index, end_index; + int ret; + int i; + unsigned long mfn, gfn; + + start_index = gma_ops->gma_to_pte_index(start); + end_index = ((end - start) >> PAGE_SHIFT) + start_index; + + gvt_dbg_mm("%s: %llx start=%llx end=%llx start_index=%lx end_index=%lx mfn_index=%x\n", + __func__, pt, start, end, + start_index, end_index, walk->mfn_index); + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pt & PAGE_MASK) + (start_index << info->gtt_entry_size_shift), + walk->pt + start_index, + (end_index - start_index) << info->gtt_entry_size_shift); + if (ret) { + gvt_vgpu_err("fail to read gpa %llx\n", pt); + return ret; + } + + for (i = start_index; i < end_index; i++) { + gfn = walk->pt[i] >> PAGE_SHIFT; + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, gfn); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn: 0x%lx\n", gfn); + return -ENXIO; + } + walk->mfns[walk->mfn_index++] = mfn << PAGE_SHIFT; + } + + return 0; +} + + +static int walk_pd_range(struct intel_vgpu *vgpu, u64 pd, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pt, next; + int ret = 0; + + do { + index = gma_ops->gma_to_pde_index(start); + + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pd & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pt, 8); + if (ret) + return ret; + next = pd_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pd, start, end, next); + walk_pt_range(vgpu, pt, start, next, walk); + + start = next; + } while (start != end); + + return ret; +} + + +static int walk_pdp_range(struct intel_vgpu *vgpu, u64 pdp, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pd, next; + int ret = 0; + + do { + index = gma_ops->gma_to_l4_pdp_index(start); + + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pdp & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pd, 8); + if (ret) + return ret; + next = pdp_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pdp, start, end, next); + + walk_pd_range(vgpu, pd, start, next, walk); + start = next; + } while (start != end); + + return ret; +} + + +static int walk_pml4_range(struct intel_vgpu *vgpu, u64 pml4, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pdp, next; + int ret = 0; + + do { + index = gma_ops->gma_to_pml4_index(start); + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pml4 & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pdp, 8); + if (ret) + return ret; + next = pml4_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pml4, start, end, next); + + walk_pdp_range(vgpu, pdp, start, next, walk); + start = next; + } while (start != end); + + return ret; +} + +int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + u64 start = pv_ppgtt->start; + u64 length = pv_ppgtt->length; + struct sg_table st; + struct scatterlist *sg = NULL; + int num_pages = length >> PAGE_SHIFT; + struct i915_vma vma; + struct ppgtt_walk walk; + int i; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("insert_4lvl pml4=%llx start=%llx length=%llx cache=%x\n", + pv_ppgtt->pdp, start, length, pv_ppgtt->cache_level); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("fail to find mm for pml4 0x%llx\n", pdps[0]); + return -EINVAL; + } + + walk.mfn_index = 0; + walk.mfns = NULL; + walk.pt = NULL; + + walk.mfns = kmalloc_array(num_pages, + sizeof(unsigned long), GFP_KERNEL); + if (!walk.mfns) { + ret = -ENOMEM; + goto fail; + } + + walk.pt = (unsigned long *)__get_free_pages(GFP_KERNEL, 0); + if (!walk.pt) { + ret = -ENOMEM; + goto fail; + } + + if (sg_alloc_table(&st, num_pages, GFP_KERNEL)) { + ret = -ENOMEM; + goto fail; + } + + ret = walk_pml4_range(vgpu, pdps[0], start, start + length, &walk); + if (ret) + goto fail_free_sg; + + WARN_ON(num_pages != walk.mfn_index); + + for_each_sg(st.sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = walk.mfns[i]; + sg_dma_len(sg) = PAGE_SIZE; + } + + /* fake vma for insert call*/ + memset(&vma, 0, sizeof(vma)); + vma.node.start = start; + vma.pages = &st; + mm->ppgtt_mm.ppgtt->vm.insert_entries( + &mm->ppgtt_mm.ppgtt->vm, &vma, + pv_ppgtt->cache_level, 0); + +fail_free_sg: + sg_free_table(&st); +fail: + kfree(walk.mfns); + free_page((unsigned long)walk.pt); + + return ret; +} diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 7a9b36176efb..6d46a123cb96 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -131,7 +131,7 @@ enum intel_gvt_mm_type { INTEL_GVT_MM_PPGTT, }; -#define GVT_RING_CTX_NR_PDPS GEN8_3LVL_PDPES +#define GVT_RING_CTX_NR_PDPS GEN8_3LVL_PDPES struct intel_vgpu_mm { enum intel_gvt_mm_type type; @@ -154,6 +154,7 @@ struct intel_vgpu_mm { struct list_head list; struct list_head lru_list; + struct i915_hw_ppgtt *ppgtt; } ppgtt_mm; struct { void *virtual_ggtt; @@ -272,4 +273,12 @@ int intel_vgpu_emulate_ggtt_mmio_read(struct intel_vgpu *vgpu, int intel_vgpu_emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off, void *p_data, unsigned int bytes); +int intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(struct intel_vgpu *vgpu, + int page_table_level); + +int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, + int page_table_level); + +int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, + int page_table_level); #endif /* _GVT_GTT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index afa15c0bb347..d1870e0a97f4 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1271,6 +1271,12 @@ static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) case VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY: case VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY: return intel_vgpu_put_ppgtt_mm(vgpu, pdps); + case VGT_G2V_PPGTT_L4_ALLOC: + return intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(vgpu, 4); + case VGT_G2V_PPGTT_L4_INSERT: + return intel_vgpu_g2v_pv_ppgtt_insert_4lvl(vgpu, 4); + case VGT_G2V_PPGTT_L4_CLEAR: + return intel_vgpu_g2v_pv_ppgtt_clear_4lvl(vgpu, 4); case VGT_G2V_EXECLIST_CONTEXT_CREATE: case VGT_G2V_EXECLIST_CONTEXT_DESTROY: case 1: /* Remove this in guest driver. */ From 8c4f37d34df5f08da6d2c443f60cb516b9354417 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Tue, 10 Jul 2018 15:29:22 +0800 Subject: [PATCH 1066/1276] drm/i915/gvt: enable pv ppgtt update by default This patch enables pv ppgtt update by default. It is needed for both uos and sos. Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index d9b901f622eb..2e56ea5d7411 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -70,7 +70,8 @@ struct drm_printer; param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ - PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE) \ + PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ + | PVMMIO_PPGTT_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 8429149b12610679d2e1ffa6a2501b775541c9ff Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Wed, 29 Aug 2018 11:49:44 +0800 Subject: [PATCH 1067/1276] drm/i915/gvt: pvmmio optimization for plane wm register update It is performance optimization to reduce plane wm related register trap counter. When update plane wm, multiple plane wm related registers are updated together, optimize it to firstly cache all register values in share page, then only PLANE_NV12_BUF_CFG register writing is trapped. Plane pvmmio level is PVMMIO_PLANE_WM_UPDATE. If plane restriction feature is enabled, trap handlers for plane wm related register are null, then directly return. Patch for both SOS and UOS. V2: when plane restriction feature is enabled, SOS trap handlers for plane wm related registers are null, then don't trap Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_pvinfo.h | 11 ++++- drivers/gpu/drm/i915/intel_pm.c | 79 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 9c76cab07010..82ab32e87805 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -75,6 +75,13 @@ struct pv_plane_update { u32 plane_ctl; }; +struct pv_plane_wm_update { + u32 max_wm_level; + u32 plane_wm_level[8]; + u32 plane_trans_wm_level; + u32 plane_buf_cfg; +}; + struct pv_ppgtt_update { u64 pdp; u64 start; @@ -89,8 +96,9 @@ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; struct pv_plane_update pv_plane; + struct pv_plane_wm_update pv_plane_wm; struct pv_ppgtt_update pv_ppgtt; - u32 rsvd2[0x400 - 30]; + u32 rsvd2[0x400 - 40]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -101,6 +109,7 @@ struct gvt_shared_page { enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, PVMMIO_PLANE_UPDATE = 0x2, + PVMMIO_PLANE_WM_UPDATE = 0x4, PVMMIO_PPGTT_UPDATE = 0x10, }; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 01d0f4d3653a..affd3e821687 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4961,6 +4961,70 @@ static void skl_write_wm_level(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); } +static void skl_pv_write_wm_level(u32 *plane_wm_level, + const struct skl_wm_level *level) +{ + uint32_t val = 0; + + if (level->plane_en) { + val |= PLANE_WM_EN; + val |= level->plane_res_b; + val |= level->plane_res_l << PLANE_WM_LINES_SHIFT; + } + + *plane_wm_level = val; +} + +static void skl_pv_ddb_entry_write(u32 *plane_cfg, + const struct skl_ddb_entry *entry) +{ + if (entry->end) + *plane_cfg = (entry->end - 1) << 16 | entry->start; + else + *plane_cfg = 0; +} + +static void skl_pv_write_plane_wm(struct intel_crtc *intel_crtc, + const struct skl_plane_wm *wm, + const struct skl_ddb_allocation *ddb, + enum plane_id plane_id) +{ + int i, level; + struct pv_plane_wm_update tmp_plane_wm; + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + int max_level = ilk_wm_max_level(dev_priv); + u32 __iomem *pv_plane_wm = (u32 *)&(dev_priv->shared_page->pv_plane_wm); + enum pipe pipe = intel_crtc->pipe; + + memset(&tmp_plane_wm, 0, sizeof(struct pv_plane_wm_update)); + tmp_plane_wm.max_wm_level = max_level; + for (level = 0; level <= max_level; level++) { + skl_pv_write_wm_level(&tmp_plane_wm.plane_wm_level[level], + &wm->wm[level]); + } + skl_pv_write_wm_level(&tmp_plane_wm.plane_trans_wm_level, + &wm->trans_wm); + + if (wm->is_planar) { + skl_pv_ddb_entry_write(&tmp_plane_wm.plane_buf_cfg, + &ddb->uv_plane[pipe][plane_id]); + } else { + skl_pv_ddb_entry_write(&tmp_plane_wm.plane_buf_cfg, + &ddb->plane[pipe][plane_id]); + } + + spin_lock(&dev_priv->shared_page_lock); + for (i = 0; i < sizeof(struct pv_plane_wm_update) / 4; i++) + writel(*((u32 *)(&tmp_plane_wm) + i), pv_plane_wm + i); + if (wm->is_planar) + skl_ddb_entry_write(dev_priv, + PLANE_NV12_BUF_CFG(pipe, plane_id), + &ddb->plane[pipe][plane_id]); + else + I915_WRITE(PLANE_NV12_BUF_CFG(pipe, plane_id), 0x0); + spin_unlock(&dev_priv->shared_page_lock); +} + static void skl_write_plane_wm(struct intel_crtc *intel_crtc, const struct skl_plane_wm *wm, const struct skl_ddb_allocation *ddb, @@ -4972,6 +5036,21 @@ static void skl_write_plane_wm(struct intel_crtc *intel_crtc, int level, max_level = ilk_wm_max_level(dev_priv); enum pipe pipe = intel_crtc->pipe; + if (INTEL_GEN(dev_priv) < 11) { + /* + * when plane restriction feature is enabled, + * sos trap handlers for plane wm related registers are null + */ + /* TODO: uncomment when plane restriction feature is enabled */ +#if 0 + if (i915_modparams.avail_planes_per_pipe) + return; +#endif + if (PVMMIO_LEVEL(dev_priv, PVMMIO_PLANE_WM_UPDATE)) + return skl_pv_write_plane_wm(intel_crtc, wm, + ddb, plane_id); + } + for (level = 0; level <= max_level; level++) { skl_write_wm_level(dev_priv, PLANE_WM(pipe, plane_id, level), &wm->wm[level]); From 5325d292e02a5244d92fe9345f4ef4bdc3dd7b72 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 1068/1276] drm/i915/gvt: handling pvmmio update of plane wm registers in GVT-g When pvmmio level PVMMIO_PLANE_WM_UPDATE is enabled, need handle multiple plane wm related registers updating when PLANE_NV12_BUF_CFG writing is traped. sos only patch. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index d1870e0a97f4..16e2d41174bd 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -2947,6 +2947,29 @@ static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } +static int pv_plane_wm_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + struct pv_plane_wm_update *pv_plane_wm = + &vgpu->mmio.shared_page->pv_plane_wm; + int level; + + if (VGPU_PVMMIO(vgpu) & PVMMIO_PLANE_WM_UPDATE) { + for (level = 0; level <= pv_plane_wm->max_wm_level; level++) + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset( + PLANE_WM(pipe, plane, level)), + &pv_plane_wm->plane_wm_level[level], 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_WM_TRANS(pipe, plane)), + &pv_plane_wm->plane_trans_wm_level, 4); + /* null function for PLANE_BUF_CFG and PLANE_NV12_BUF_CFG */ + } + return 0; +} + #define MMIO_PIPES_SDH(prefix, plane, s, d, r, w) do { \ int pipe; \ for_each_pipe(dev_priv, pipe) \ @@ -3091,7 +3114,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, + pv_plane_wm_mmio_write); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_A), D_SKL_PLUS, NULL, NULL); From d5993f188722b50e626b76158a15692061c35000 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Wed, 29 Aug 2018 12:08:45 +0800 Subject: [PATCH 1069/1276] drm/i915/gvt: enable plane wm pvmmio level through enable_pvmmio param plane wm update pvmmio level is 0x4, need set it in enable_pvmmio for both SOS and UOS kernel driver. Patch for both SOS and UOS. V2: use PVMMIO_PLANE_WM_UPDATE bit definition to improve readability Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 2e56ea5d7411..28769e6b6837 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -71,7 +71,7 @@ struct drm_printer; param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ - | PVMMIO_PPGTT_UPDATE) \ + | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 04473d912c5e8d3e3832a395999ecb2cc80a52c4 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 1070/1276] drm/i915/gvt: notify global gtt update through g2v This patch extends g2v notification to notify host GVT-g of ggtt update from guest, including ggtt_insert_entries and ggtt_clear_range. This patch also add one new pvmmio level to control ggtt update. This patch is needed for both uos and sos. v2: - calculate num_entries from gtt_entries. v3: - pass cache_level to GVT-g - consolidate the same code into a function. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/i915_gem_gtt.c | 40 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_pvinfo.h | 12 ++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index c8c1df7812d7..280095010286 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2503,6 +2503,17 @@ static void gen8_set_pte(void __iomem *addr, gen8_pte_t pte) writeq(pte, addr); } +static void vgpu_ggtt_insert(struct drm_i915_private *dev_priv, + u64 start, int num_entries, enum i915_cache_level level) +{ + struct gvt_shared_page *shared_page = dev_priv->shared_page; + + writeq(start, &shared_page->pv_ggtt.start); + writeq(num_entries, &shared_page->pv_ggtt.length); + writel(level, &shared_page->pv_ggtt.cache_level); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_GGTT_INSERT); +} + static void gen8_ggtt_insert_page(struct i915_address_space *vm, dma_addr_t addr, u64 offset, @@ -2515,6 +2526,11 @@ static void gen8_ggtt_insert_page(struct i915_address_space *vm, gen8_set_pte(pte, gen8_pte_encode(addr, level, 0)); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + vgpu_ggtt_insert(vm->i915, offset, 1, level); + return; + } + ggtt->invalidate(vm->i915); } @@ -2539,6 +2555,20 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm, for_each_sgt_dma(addr, sgt_iter, vma->pages) gen8_set_pte(gtt_entries++, pte_encode | addr); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + int num_entries = gtt_entries - + ((gen8_pte_t __iomem *)ggtt->gsm + + (vma->node.start >> PAGE_SHIFT)); + /* + * Sometimes number of entries does not match vma node size. + * Pass number of pte entries instead. + */ + vgpu_ggtt_insert(vm->i915, vma->node.start, + num_entries, level); + return; + } + + /* * We want to flush the TLBs only after we're certain all the PTE * updates have finished. @@ -2612,6 +2642,16 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm, for (i = 0; i < num_entries; i++) gen8_set_pte(>t_base[i], scratch_pte); + + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct gvt_shared_page *shared_page = dev_priv->shared_page; + + writeq(start, &shared_page->pv_ggtt.start); + writeq(length, &shared_page->pv_ggtt.length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_GGTT_CLEAR); + } + } static void bxt_vtd_ggtt_wa(struct i915_address_space *vm) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 82ab32e87805..c15d4578bb5f 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,8 @@ enum vgt_g2v_type { VGT_G2V_PPGTT_L4_ALLOC, VGT_G2V_PPGTT_L4_CLEAR, VGT_G2V_PPGTT_L4_INSERT, + VGT_G2V_GGTT_INSERT, + VGT_G2V_GGTT_CLEAR, VGT_G2V_MAX, }; @@ -89,6 +91,12 @@ struct pv_ppgtt_update { u32 cache_level; }; +struct pv_ggtt_update { + u64 start; + u64 length; + u32 cache_level; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ @@ -98,7 +106,8 @@ struct gvt_shared_page { struct pv_plane_update pv_plane; struct pv_plane_wm_update pv_plane_wm; struct pv_ppgtt_update pv_ppgtt; - u32 rsvd2[0x400 - 40]; + struct pv_ggtt_update pv_ggtt; + u32 rsvd2[0x400 - 46]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -111,6 +120,7 @@ enum pvmmio_levels { PVMMIO_PLANE_UPDATE = 0x2, PVMMIO_PLANE_WM_UPDATE = 0x4, PVMMIO_PPGTT_UPDATE = 0x10, + PVMMIO_GGTT_UPDATE = 0x20, }; /* From 04a3c19ba7aa6aa19ad7d11131a433246ed79de4 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 1071/1276] drm/i915/gvt: handle global gtt update from g2v This patch handles ggtt update from g2v notification. It maps the physical pages behind virtual page table to guest, so guest can update its pte entries directly to avoid mmio trap. Then guest ggtt pte entries are converted to host pte entries and inserted into host gtt table. The tricky part is that pvmmio parameter detection is later than virtual page trable creation and pci bar address update, So the map ggtt mmio is done during pvmmio param detection. This patch is only for sos. v2: - cut invalid range. - release gfn to mfn mapping when free gtt. v3: - call ggtt insert_entries function - add size check in validate_ggtt_range - rename trap to map V4: Fall back to disable ggtt PV when it fails to alloc 2M pages for GGTT pV. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cfg_space.c | 41 +++++ drivers/gpu/drm/i915/gvt/gtt.c | 261 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/gvt/gtt.h | 5 + drivers/gpu/drm/i915/gvt/gvt.h | 3 + drivers/gpu/drm/i915/gvt/handlers.c | 15 ++ 5 files changed, 320 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index f6bcfcb571b7..fd461db48a8d 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -138,6 +138,47 @@ static int map_aperture(struct intel_vgpu *vgpu, bool map) return 0; } +int map_gttmmio(struct intel_vgpu *vgpu, bool map) +{ + struct intel_vgpu_gm *gm = &vgpu->gm; + unsigned long mfn; + struct scatterlist *sg; + struct sg_table *st = gm->st; + u64 start, end; + int ret = 0; + + if (!st) { + DRM_INFO("no scatter list, fallback to disable ggtt pv\n"); + return -EINVAL; + } + + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + start &= ~GENMASK(3, 0); + start += vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1; + + end = start + + (vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1); + + WARN_ON((end - start) != gvt_ggtt_sz(vgpu->gvt)); + + gvt_dbg_mmio("%s start=%llx end=%llx map=%d\n", + __func__, start, end, map); + + start >>= PAGE_SHIFT; + for (sg = st->sgl; sg; sg = __sg_next(sg)) { + mfn = page_to_pfn(sg_page(sg)); + gvt_dbg_mmio("page=%p mfn=%lx size=%x start=%llx\n", + sg_page(sg), mfn, sg->length, start); + ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, start, + mfn, sg->length >> PAGE_SHIFT, map); + if (ret) + return ret; + start += sg->length >> PAGE_SHIFT; + } + + return ret; +} + static int trap_gttmmio(struct intel_vgpu *vgpu, bool trap) { u64 start, end; diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 0c7160a3b925..22c79df59d65 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1569,6 +1569,106 @@ int intel_vgpu_sync_oos_pages(struct intel_vgpu *vgpu) return 0; } +static void free_ggtt_virtual_page_table(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu_gm *gm = &mm->vgpu->gm; + struct sg_table *st = gm->st; + struct scatterlist *sg; + + for (sg = st->sgl; sg; sg = __sg_next(sg)) { + if (sg_page(sg)) + __free_pages(sg_page(sg), get_order(sg->length)); + } + + sg_free_table(st); + kfree(st); + vunmap(mm->ggtt_mm.virtual_ggtt); + gm->st = NULL; +} + +/* + * Alloc virtual page table for guest ggtt. If ggtt pv enabled, the + * physical pages behind virtual page table is also mapped to guest, + * guest can update its pte entries directly to avoid trap. + */ +static void *alloc_ggtt_virtual_page_table(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + unsigned int page_count = gvt_ggtt_sz(vgpu->gvt) >> PAGE_SHIFT; + struct intel_vgpu_gm *gm = &vgpu->gm; + struct page **pages = NULL; + struct page *p; + unsigned int i; + void *vaddr = NULL; + int order; + struct sg_table *st; + struct scatterlist *sg; + struct sgt_iter sgt_iter; + unsigned int npages = page_count; + + /* + * page_table_entry_size is bigger than the size alloc_pages can + * allocate, We have to split it according to the PMD size (2M). + * Head page is kept in scatter list so that we can free them later. + */ + order = get_order(1 << PMD_SHIFT); + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return ERR_PTR(-ENOMEM); + + if (sg_alloc_table(st, page_count, GFP_KERNEL)) { + kfree(st); + return ERR_PTR(-ENOMEM); + } + + sg = st->sgl; + st->nents = 0; + gm->st = st; + do { + p = alloc_pages(GFP_KERNEL, order); + if (!p) + goto fail; + gvt_dbg_mm("page=%p size=%ld\n", p, PAGE_SIZE << order); + sg_set_page(sg, p, PAGE_SIZE << order, 0); + st->nents++; + npages -= 1 << order; + if (!npages) { + sg_mark_end(sg); + break; + } + sg = __sg_next(sg); + } while (1); + + + /* keep all the pages for vmap */ + pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); + if (!pages) + goto fail; + + i = 0; + for_each_sgt_page(p, sgt_iter, st) + pages[i++] = p; + + WARN_ON(i != page_count); + + vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL); + if (!vaddr) { + gvt_vgpu_err("fail to vmap pages"); + goto fail; + } + kfree(pages); + return vaddr; + +fail: + sg_set_page(sg, NULL, 0, 0); + sg_mark_end(sg); + free_ggtt_virtual_page_table(mm); + kfree(pages); + gm->st = NULL; + return NULL; +} + /* * The heart of PPGTT shadow page table. */ @@ -1963,7 +2063,6 @@ struct intel_vgpu_mm *intel_vgpu_create_ppgtt_mm(struct intel_vgpu *vgpu, static struct intel_vgpu_mm *intel_vgpu_create_ggtt_mm(struct intel_vgpu *vgpu) { struct intel_vgpu_mm *mm; - unsigned long nr_entries; mm = vgpu_alloc_mm(vgpu); if (!mm) @@ -1971,10 +2070,17 @@ static struct intel_vgpu_mm *intel_vgpu_create_ggtt_mm(struct intel_vgpu *vgpu) mm->type = INTEL_GVT_MM_GGTT; - nr_entries = gvt_ggtt_gm_sz(vgpu->gvt) >> I915_GTT_PAGE_SHIFT; - mm->ggtt_mm.virtual_ggtt = - vzalloc(array_size(nr_entries, + mm->ggtt_mm.virtual_ggtt = alloc_ggtt_virtual_page_table(mm); + if (!mm->ggtt_mm.virtual_ggtt) { + unsigned long nr_entries; + + DRM_INFO("fail to alloc contiguous pages, fallback\n"); + nr_entries = gvt_ggtt_gm_sz(vgpu->gvt) >> I915_GTT_PAGE_SHIFT; + mm->ggtt_mm.virtual_ggtt = + vzalloc(array_size(nr_entries, vgpu->gvt->device_info.gtt_entry_size)); + } + if (!mm->ggtt_mm.virtual_ggtt) { vgpu_free_mm(mm); return ERR_PTR(-ENOMEM); @@ -2003,7 +2109,17 @@ void _intel_vgpu_mm_release(struct kref *mm_ref) list_del(&mm->ppgtt_mm.lru_list); invalidate_ppgtt_mm(mm); } else { - vfree(mm->ggtt_mm.virtual_ggtt); + if (mm->ggtt_mm.virtual_ggtt) { + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_vgpu_gm *gm = &vgpu->gm; + + if (gm->st) { + map_gttmmio(mm->vgpu, false); + free_ggtt_virtual_page_table(mm); + } else + vfree(mm->ggtt_mm.virtual_ggtt); + mm->ggtt_mm.virtual_ggtt = NULL; + } mm->ggtt_mm.last_partial_off = -1UL; } @@ -3142,3 +3258,138 @@ int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, return ret; } + +static void validate_ggtt_range(struct intel_vgpu *vgpu, + u64 *start, u64 *length) +{ + u64 end; + + if (WARN_ON(*start > vgpu->gvt->dev_priv->ggtt.vm.total || + *length > vgpu->gvt->dev_priv->ggtt.vm.total)) { + *length = 0; + return; + } + + end = *start + *length - 1; + + if (*start >= vgpu_aperture_gmadr_base(vgpu) && + end <= vgpu_aperture_gmadr_end(vgpu)) + return; + + if (*start >= vgpu_hidden_gmadr_base(vgpu) && + end <= vgpu_hidden_gmadr_end(vgpu)) + return; + + /* handle the cases with invalid ranges */ + WARN_ON(1); + + /* start is in aperture range, end is after apeture range */ + if (*start >= vgpu_aperture_gmadr_base(vgpu) && + *start <= vgpu_aperture_gmadr_end(vgpu)) { + *length = vgpu_aperture_gmadr_end(vgpu) - *start + 1; + return; + } + + /* start is before aperture range, end is in apeture range */ + if (end >= vgpu_aperture_gmadr_base(vgpu) && + end <= vgpu_aperture_gmadr_end(vgpu)) { + *start = vgpu_aperture_gmadr_base(vgpu); + return; + } + + /* start is in hidden range, end is after hidden range */ + if (*start >= vgpu_hidden_gmadr_base(vgpu) && + *start <= vgpu_hidden_gmadr_end(vgpu)) { + *length = vgpu_hidden_gmadr_end(vgpu) - *start + 1; + return; + } + + /* start is before hidden range, end is in hidden range */ + if (end >= vgpu_hidden_gmadr_base(vgpu) && + end <= vgpu_hidden_gmadr_end(vgpu)) { + *start = vgpu_hidden_gmadr_base(vgpu); + return; + } + + /* both start and end are not in valid range*/ + *length = 0; + + return; +} + +int intel_vgpu_g2v_pv_ggtt_insert(struct intel_vgpu *vgpu) +{ + struct intel_vgpu_gtt *gtt = &vgpu->gtt; + struct gvt_shared_page *shared_page = vgpu->mmio.shared_page; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + struct i915_ggtt *ggtt = &dev_priv->ggtt; + u64 start = shared_page->pv_ggtt.start; + u64 num_entries = shared_page->pv_ggtt.length; + u32 cache_level = shared_page->pv_ggtt.cache_level; + u64 length = num_entries << PAGE_SHIFT; + u64 *vaddr = gtt->ggtt_mm->ggtt_mm.virtual_ggtt; + u64 gtt_entry_index; + u64 gtt_entry; + unsigned long mfn; + struct i915_vma vma; + struct sg_table st; + struct scatterlist *sg = NULL; + int ret = 0; + int i; + + gvt_dbg_mm("ggtt_insert: start=%llx length=%llx cache=%x\n", + start, length, cache_level); + validate_ggtt_range(vgpu, &start, &length); + if (length == 0) + return 0; + + num_entries = length >> PAGE_SHIFT; + + if (sg_alloc_table(&st, num_entries, GFP_KERNEL)) + return -ENOMEM; + + for_each_sg(st.sgl, sg, num_entries, i) { + gtt_entry_index = (start >> PAGE_SHIFT) + i; + gtt_entry = vaddr[gtt_entry_index]; + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, + gtt_entry >> PAGE_SHIFT); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn: 0x%llx\n", + gtt_entry >> PAGE_SHIFT); + ret = -ENXIO; + goto fail; + } + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = mfn << PAGE_SHIFT; + sg_dma_len(sg) = PAGE_SIZE; + } + + /* fake vma for insert call*/ + memset(&vma, 0, sizeof(vma)); + vma.node.start = start; + vma.pages = &st; + ggtt->vm.insert_entries(&ggtt->vm, &vma, cache_level, 0); + +fail: + sg_free_table(&st); + return ret; +} + +int intel_vgpu_g2v_pv_ggtt_clear(struct intel_vgpu *vgpu) +{ + struct gvt_shared_page *shared_page = vgpu->mmio.shared_page; + u64 start = shared_page->pv_ggtt.start; + u64 length = shared_page->pv_ggtt.length; + struct i915_ggtt *ggtt = &vgpu->gvt->dev_priv->ggtt; + + gvt_dbg_mm("ggtt_clear: start=%llx length=%llx\n", + start, length); + validate_ggtt_range(vgpu, &start, &length); + if (length == 0) + return 0; + + ggtt->vm.clear_range(&ggtt->vm, start, length); + + return 0; +} diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 6d46a123cb96..7ba7400772b0 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -281,4 +281,9 @@ int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, int page_table_level); + +int intel_vgpu_g2v_pv_ggtt_insert(struct intel_vgpu *vgpu); + +int intel_vgpu_g2v_pv_ggtt_clear(struct intel_vgpu *vgpu); + #endif /* _GVT_GTT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 1a287ba76923..9344293ed692 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -85,6 +85,7 @@ struct intel_gvt_device_info { struct intel_vgpu_gm { u64 aperture_sz; u64 hidden_sz; + struct sg_table *st; struct drm_mm_node low_gm_node; struct drm_mm_node high_gm_node; }; @@ -568,6 +569,8 @@ static inline u64 intel_vgpu_get_bar_gpa(struct intel_vgpu *vgpu, int bar) PCI_BASE_ADDRESS_MEM_MASK; } +int map_gttmmio(struct intel_vgpu *vgpu, bool map); + void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu); int intel_vgpu_init_opregion(struct intel_vgpu *vgpu); int intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 16e2d41174bd..8fa3bed75d61 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1277,6 +1277,12 @@ static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) return intel_vgpu_g2v_pv_ppgtt_insert_4lvl(vgpu, 4); case VGT_G2V_PPGTT_L4_CLEAR: return intel_vgpu_g2v_pv_ppgtt_clear_4lvl(vgpu, 4); + case VGT_G2V_GGTT_INSERT: + return intel_vgpu_g2v_pv_ggtt_insert(vgpu); + break; + case VGT_G2V_GGTT_CLEAR: + return intel_vgpu_g2v_pv_ggtt_clear(vgpu); + break; case VGT_G2V_EXECLIST_CONTEXT_CREATE: case VGT_G2V_EXECLIST_CONTEXT_DESTROY: case 1: /* Remove this in guest driver. */ @@ -1348,6 +1354,15 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, vgpu_vreg(vgpu, offset) = 0; break; } + if (vgpu_vreg(vgpu, offset) & PVMMIO_GGTT_UPDATE) { + ret = map_gttmmio(vgpu, true); + if (ret) { + DRM_INFO("ggtt pv mode is off\n"); + vgpu_vreg(vgpu, offset) &= + ~PVMMIO_GGTT_UPDATE; + } + } + } else { vgpu_vreg(vgpu, offset) = 0; } From f2606a9705ba29ccffa75733c37cda9cad8f607e Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Thu, 26 Jul 2018 10:03:02 +0800 Subject: [PATCH 1072/1276] drm/i915/gvt: enable pv global gtt update by default This patch enables pv ggtt update by default. It is needed for both uos and sos. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 28769e6b6837..40aee5b37645 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -71,7 +71,8 @@ struct drm_printer; param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ - | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE) \ + | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE \ + | PVMMIO_GGTT_UPDATE ) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 4bf144d95aae94f6ef85a660cc207c0e74c864ec Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 5 Sep 2018 15:52:06 +0800 Subject: [PATCH 1073/1276] drm/i915/gvt: Check the state of PVMMIO gtt table to avoid incorrect calling for hypervisor Now hypervisor adds strict checks when the sos tries to add/delete the mapping between gfn and mfn. In such case it should check the state of PVMMIO gtt table to avoid the incorrect hyper call. If the mapping between gfn and mfn is not setup for PVMMIO GTT, it should not remove the mapping. Otherwise the hypervisor will report that the mapping doesn't exist. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1158 Signed-off-by: Zhao Yakui Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cfg_space.c | 7 +++++++ drivers/gpu/drm/i915/gvt/gtt.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index fd461db48a8d..505e255c4d8a 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -152,6 +152,11 @@ int map_gttmmio(struct intel_vgpu *vgpu, bool map) return -EINVAL; } + if (vgpu->gtt.ggtt_pv_mapped == map) { + /* If it is already set as the target state, skip it */ + return ret; + } + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); start &= ~GENMASK(3, 0); start += vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1; @@ -176,6 +181,8 @@ int map_gttmmio(struct intel_vgpu *vgpu, bool map) start += sg->length >> PAGE_SHIFT; } + vgpu->gtt.ggtt_pv_mapped = map; + return ret; } diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 7ba7400772b0..6c0d3bdcaee4 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -199,6 +199,9 @@ struct intel_vgpu_gtt { struct list_head oos_page_list_head; struct list_head post_shadow_list_head; struct intel_vgpu_scratch_pt scratch_pt[GTT_TYPE_MAX]; + + /* indicate whether the PV mapped is enabled for ggtt */ + bool ggtt_pv_mapped; }; extern int intel_vgpu_init_gtt(struct intel_vgpu *vgpu); From f41b181ed23796be0257290b0fcec1616b64ba42 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:22 +0800 Subject: [PATCH 1074/1276] drm/i915/gvt: allocate ddb according to active pipes This patch add back the ddb allocation for the PLANE_CURSOR and allocate ddb according to current active pipes. In this way, this patch can support 4K monitors and fix the issue that cursor not work when enable_initial_modeset is 0. v2: - correct ddb start calculation Tracked-On: projectacrn/acrn-hypervisor#1171 Signed-off-by: Zhipeng Gong Reviewed-by: Zhao Yakui Reviewed-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/gvt.c | 16 ++++++++++------ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/intel_pm.c | 13 +++++++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index e4a3823e1226..940443f08dfd 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -317,22 +317,27 @@ void intel_gvt_init_pipe_info(struct intel_gvt *gvt); * plane information of DomU's planes, so here we statically allocate the * ddb entries for all the possible enabled planes. */ -static void intel_gvt_init_ddb(struct intel_gvt *gvt) +void intel_gvt_allocate_ddb(struct intel_gvt *gvt, + struct skl_ddb_allocation *ddb, unsigned int active_crtcs) { struct drm_i915_private *dev_priv = gvt->dev_priv; - struct skl_ddb_allocation *ddb = &gvt->ddb; unsigned int pipe_size, ddb_size, plane_size, plane_cnt; u16 start, end; enum pipe pipe; enum plane_id plane; + int i = 0; + int num_active = hweight32(active_crtcs); + + if (!num_active) + return; ddb_size = INTEL_INFO(dev_priv)->ddb_size; ddb_size -= 4; /* 4 blocks for bypass path allocation */ - pipe_size = ddb_size / INTEL_INFO(dev_priv)->num_pipes; + pipe_size = ddb_size / num_active; memset(ddb, 0, sizeof(*ddb)); - for_each_pipe(dev_priv, pipe) { - start = pipe * ddb_size / INTEL_INFO(dev_priv)->num_pipes; + for_each_pipe_masked(dev_priv, pipe, active_crtcs) { + start = pipe_size * (i++); end = start + pipe_size; ddb->plane[pipe][PLANE_CURSOR].start = end - 8; ddb->plane[pipe][PLANE_CURSOR].end = end; @@ -470,7 +475,6 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) } intel_gvt_init_pipe_info(gvt); - intel_gvt_init_ddb(gvt); ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt, &intel_gvt_ops); diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 9344293ed692..f4d9056175ae 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -608,6 +608,8 @@ struct intel_gvt_ops { }; int gvt_dom0_ready(struct drm_i915_private *dev_priv); +void intel_gvt_allocate_ddb(struct intel_gvt *gvt, + struct skl_ddb_allocation *ddb, unsigned int active_crtcs); enum { GVT_FAILSAFE_UNSUPPORTED_GUEST, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index affd3e821687..c7c7b5e78330 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5212,9 +5212,18 @@ skl_compute_ddb(struct drm_atomic_state *state) memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb)); #if IS_ENABLED(CONFIG_DRM_I915_GVT) - /* In GVT environemnt, we only use the statically allocated ddb */ + /* + * In GVT environemnt, allocate ddb for all planes in active crtc. + * When there is active pipe change, intel_state active_crtcs is + * not zero and updated before dev_priv, so use intel_state + * active_crtc when it is not zero. + */ if (dev_priv->gvt) { - memcpy(ddb, &dev_priv->gvt->ddb, sizeof(*ddb)); + unsigned int active_crtcs; + + active_crtcs = intel_state->active_crtcs ? + intel_state->active_crtcs : dev_priv->active_crtcs; + intel_gvt_allocate_ddb(dev_priv->gvt, ddb, active_crtcs); return 0; } #endif From 9916789387403966d7a169c778a9630877194a21 Mon Sep 17 00:00:00 2001 From: "Kim, Dongwon" Date: Thu, 2 Nov 2017 14:58:23 -0700 Subject: [PATCH 1075/1276] REVERTME [IOTG]: hyper_dmabuf: Introducing the hyper_dmabuf driver This commit squishes all the different commits related to the hyper_dmabuf driver into one. This driver is currently under reiview upstream: https://lists.freedesktop.org/archives/dri-devel/2018-February/165720.html Once the review is done upstream, the relevant changes between this version and the upstream one need to be synced. Upload of intial version of hyper_DMABUF driver enabling DMA_BUF exchange between two different VMs in virtualized platform based on hypervisor such as KVM or XEN. Hyper_DMABUF drv's primary role is to import a DMA_BUF from originator then re-export it to another Linux VM so that it can be mapped and accessed by it. The functionality of this driver highly depends on Hypervisor's native page sharing mechanism and inter-VM communication support. This driver has two layers, one is main hyper_DMABUF framework for scatter-gather list management that handles actual import and export of DMA_BUF. Lower layer is about actual memory sharing and communication between two VMs, which is hypervisor-specific interface. This driver is initially designed to enable DMA_BUF sharing across VMs in Xen environment, so currently working with Xen only. This also adds Kernel configuration for hyper_DMABUF drv under Device Drivers->Xen driver support->hyper_dmabuf options. To give some brief information about each source file, hyper_dmabuf/hyper_dmabuf_conf.h : configuration info hyper_dmabuf/hyper_dmabuf_drv.c : driver interface and initialization hyper_dmabuf/hyper_dmabuf_imp.c : scatter-gather list generation and management. DMA_BUF ops for DMA_BUF reconstructed from hyper_DMABUF hyper_dmabuf/hyper_dmabuf_ioctl.c : IOCTLs calls for export/import and comm channel creation unexport. hyper_dmabuf/hyper_dmabuf_list.c : Database (linked-list) for exported and imported hyper_DMABUF hyper_dmabuf/hyper_dmabuf_msg.c : creation and management of messages between exporter and importer hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c : comm ch management and ISRs for incoming messages. hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c : Database (linked-list) for keeping information about existing comm channels among VMs Signed-off-by: Kim, Dongwon Signed-off-by: Matuesz, Polrola --- drivers/dma-buf/Kconfig | 2 + drivers/dma-buf/Makefile | 1 + drivers/dma-buf/hyper_dmabuf/Kconfig | 71 ++ drivers/dma-buf/hyper_dmabuf/Makefile | 57 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c | 411 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h | 118 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_event.c | 122 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_event.h | 38 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_id.c | 133 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_id.h | 51 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 789 +++++++++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h | 50 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.c | 293 ++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.h | 71 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 413 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h | 87 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 414 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h | 32 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_query.c | 172 ++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_query.h | 10 + .../hyper_dmabuf/hyper_dmabuf_remote_sync.c | 322 ++++++ .../hyper_dmabuf/hyper_dmabuf_remote_sync.h | 30 + .../hyper_dmabuf/hyper_dmabuf_sgl_proc.c | 261 +++++ .../hyper_dmabuf/hyper_dmabuf_sgl_proc.h | 41 + .../hyper_dmabuf/hyper_dmabuf_struct.h | 141 +++ .../virtio/hyper_dmabuf_virtio_be_drv.c | 505 ++++++++++ .../virtio/hyper_dmabuf_virtio_comm_ring.c | 89 ++ .../virtio/hyper_dmabuf_virtio_comm_ring.h | 68 ++ .../virtio/hyper_dmabuf_virtio_common.c | 35 + .../virtio/hyper_dmabuf_virtio_common.h | 55 + .../virtio/hyper_dmabuf_virtio_fe_drv.c | 385 +++++++ .../virtio/hyper_dmabuf_virtio_fe_list.c | 99 ++ .../virtio/hyper_dmabuf_virtio_fe_list.h | 48 + .../virtio/hyper_dmabuf_virtio_shm.c | 343 +++++++ .../virtio/hyper_dmabuf_virtio_shm.h | 40 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c | 951 ++++++++++++++++++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h | 78 ++ .../xen/hyper_dmabuf_xen_comm_list.c | 158 +++ .../xen/hyper_dmabuf_xen_comm_list.h | 67 ++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c | 46 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h | 53 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c | 525 ++++++++++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h | 46 + include/uapi/linux/hyper_dmabuf.h | 134 +++ include/uapi/xen/Kbuild | 5 + 45 files changed, 7860 insertions(+) create mode 100644 drivers/dma-buf/hyper_dmabuf/Kconfig create mode 100644 drivers/dma-buf/hyper_dmabuf/Makefile create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h create mode 100644 include/uapi/linux/hyper_dmabuf.h create mode 100644 include/uapi/xen/Kbuild diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index ed3b785bae37..09ccac1768e3 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -30,4 +30,6 @@ config SW_SYNC WARNING: improper use of this can result in deadlocking kernel drivers from userspace. Intended for test and debug only. +source "drivers/dma-buf/hyper_dmabuf/Kconfig" + endmenu diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index c33bf8863147..3f15a841502e 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1,3 +1,4 @@ obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o +obj-$(CONFIG_HYPER_DMABUF) += hyper_dmabuf/ diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig new file mode 100644 index 000000000000..88992167c645 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -0,0 +1,71 @@ +menu "hyper_dmabuf options" + +config HYPER_DMABUF + bool "Enables hyper dmabuf driver" + default y + +choice + prompt "Hypervisor" + depends on HYPER_DMABUF + default HYPER_DMABUF_XEN + +config HYPER_DMABUF_XEN + bool "Configure hyper_dmabuf for XEN hypervisor" + depends on HYPER_DMABUF && XEN + help + Configuring hyper_dmabuf driver for XEN hypervisor + +config HYPER_DMABUF_ACRN + bool "Configure hyper_dmabuf for ACRN hypervisor" + depends on HYPER_DMABUF && ACRN_VIRTIO_DEVICES + help + Configuring hyper_dmabuf driver for ACRN hypervisor +endchoice + +choice + prompt "Virtio driver type" + depends on HYPER_DMABUF && HYPER_DMABUF_ACRN + default HYPER_DMABUF_VIRTIO_BE + +config HYPER_DMABUF_VIRTIO_BE + depends on VBS && DRM_I915_GVT + bool "Configure hyper_dmabuf as virtio backend" + help + Configuring hyper_dmabuf driver as virtio backend + +config HYPER_DMABUF_VIRTIO_FE + depends on ACRN_VIRTIO_DEVICES + bool "Configure hyper_dmabuf as virtio frontend" + help + Configuring hyper_dmabuf driver as virtio frontend +endchoice + +config HYPER_DMABUF_SYSFS + bool "Enable sysfs information about hyper DMA buffers" + default y + depends on HYPER_DMABUF + help + Expose information about imported and exported buffers using + hyper_dmabuf driver + +config HYPER_DMABUF_EVENT_GEN + bool "Enable event-generation and polling operation" + default n + depends on HYPER_DMABUF + help + With this config enabled, hyper_dmabuf driver on the importer side + generates events and queue those up in the event list whenever a new + shared DMA-BUF is available. Events in the list can be retrieved by + read operation. + +config HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + bool "Enable automatic rx-ch add with 10 secs interval" + default y + depends on HYPER_DMABUF && HYPER_DMABUF_XEN + help + If enabled, driver reads a node in xenstore every 10 seconds + to check whether there is any tx comm ch configured by another + domain then initialize matched rx comm ch automatically for any + existing tx comm chs. + +endmenu diff --git a/drivers/dma-buf/hyper_dmabuf/Makefile b/drivers/dma-buf/hyper_dmabuf/Makefile new file mode 100644 index 000000000000..f63967cc99f6 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/Makefile @@ -0,0 +1,57 @@ +TARGET_MODULE:=hyper_dmabuf + +# If we running by kernel building system +ifneq ($(KERNELRELEASE),) + $(TARGET_MODULE)-objs := hyper_dmabuf_drv.o \ + hyper_dmabuf_ioctl.o \ + hyper_dmabuf_list.o \ + hyper_dmabuf_sgl_proc.o \ + hyper_dmabuf_ops.o \ + hyper_dmabuf_msg.o \ + hyper_dmabuf_id.o \ + hyper_dmabuf_remote_sync.o \ + hyper_dmabuf_query.o \ + +ifeq ($(CONFIG_HYPER_DMABUF_EVENT_GEN), y) + $(TARGET_MODULE)-objs += hyper_dmabuf_event.o +endif + +ifeq ($(CONFIG_HYPER_DMABUF_XEN), y) + $(TARGET_MODULE)-objs += xen/hyper_dmabuf_xen_comm.o \ + xen/hyper_dmabuf_xen_comm_list.o \ + xen/hyper_dmabuf_xen_shm.o \ + xen/hyper_dmabuf_xen_drv.o +else ifeq ($(CONFIG_HYPER_DMABUF_ACRN), y) + ifeq ($(CONFIG_HYPER_DMABUF_VIRTIO_BE), y) + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_be_drv.o \ + virtio/hyper_dmabuf_virtio_fe_list.o + else + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_fe_drv.o + endif + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_common.o \ + virtio/hyper_dmabuf_virtio_shm.o \ + virtio/hyper_dmabuf_virtio_comm_ring.o +endif + +obj-$(CONFIG_HYPER_DMABUF) := $(TARGET_MODULE).o + +# If we are running without kernel build system +else +BUILDSYSTEM_DIR?=../../../ +PWD:=$(shell pwd) + +all : +# run kernel build system to make module + $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules + +clean: +# run kernel build system to cleanup in current directory + $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean + +load: + insmod ./$(TARGET_MODULE).ko + +unload: + rmmod ./$(TARGET_MODULE).ko + +endif diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c new file mode 100644 index 000000000000..f1afce29d6af --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c @@ -0,0 +1,411 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_ioctl.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_event.h" + +#ifdef CONFIG_HYPER_DMABUF_XEN +#include "xen/hyper_dmabuf_xen_drv.h" +#elif defined (CONFIG_HYPER_DMABUF_ACRN) +#include "virtio/hyper_dmabuf_virtio_common.h" +#endif + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Intel Corporation"); + +struct hyper_dmabuf_private *hy_drv_priv; + +static void force_free(struct exported_sgt_info *exported, + void *attr) +{ + struct ioctl_hyper_dmabuf_unexport unexport_attr; + struct file *filp = (struct file *)attr; + + if (!filp || !exported) + return; + + if (exported->filp == filp) { + dev_dbg(hy_drv_priv->dev, + "Forcefully releasing buffer {id:%d key:%d %d %d}\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + unexport_attr.hid = exported->hid; + unexport_attr.delay_ms = 0; + + hyper_dmabuf_unexport_ioctl(filp, &unexport_attr); + } +} + +static int hyper_dmabuf_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + /* Do not allow exclusive open */ + if (filp->f_flags & O_EXCL) + return -EBUSY; + + return ret; +} + +static int hyper_dmabuf_release(struct inode *inode, struct file *filp) +{ + hyper_dmabuf_foreach_exported(force_free, filp); + + return 0; +} + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + +static unsigned int hyper_dmabuf_event_poll(struct file *filp, + struct poll_table_struct *wait) +{ + poll_wait(filp, &hy_drv_priv->event_wait, wait); + + if (!list_empty(&hy_drv_priv->event_list)) + return POLLIN | POLLRDNORM; + + return 0; +} + +static ssize_t hyper_dmabuf_event_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + int ret; + + /* only root can read events */ + if (!capable(CAP_DAC_OVERRIDE)) { + dev_err(hy_drv_priv->dev, + "Only root can read events\n"); + return -EPERM; + } + + /* make sure user buffer can be written */ + if (!access_ok(VERIFY_WRITE, buffer, count)) { + dev_err(hy_drv_priv->dev, + "User buffer can't be written.\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&hy_drv_priv->event_read_lock); + if (ret) + return ret; + + while (1) { + struct hyper_dmabuf_event *e = NULL; + + spin_lock_irq(&hy_drv_priv->event_lock); + if (!list_empty(&hy_drv_priv->event_list)) { + e = list_first_entry(&hy_drv_priv->event_list, + struct hyper_dmabuf_event, link); + list_del(&e->link); + } + spin_unlock_irq(&hy_drv_priv->event_lock); + + if (!e) { + if (ret) + break; + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + mutex_unlock(&hy_drv_priv->event_read_lock); + ret = wait_event_interruptible(hy_drv_priv->event_wait, + !list_empty(&hy_drv_priv->event_list)); + + if (ret == 0) + ret = mutex_lock_interruptible( + &hy_drv_priv->event_read_lock); + + if (ret) + return ret; + } else { + unsigned int length = (sizeof(e->event_data.hdr) + + e->event_data.hdr.size); + + if (length > count - ret) { +put_back_event: + spin_lock_irq(&hy_drv_priv->event_lock); + list_add(&e->link, &hy_drv_priv->event_list); + spin_unlock_irq(&hy_drv_priv->event_lock); + break; + } + + if (copy_to_user(buffer + ret, &e->event_data.hdr, + sizeof(e->event_data.hdr))) { + if (ret == 0) + ret = -EFAULT; + + goto put_back_event; + } + + ret += sizeof(e->event_data.hdr); + + if (copy_to_user(buffer + ret, e->event_data.data, + e->event_data.hdr.size)) { + /* error while copying void *data */ + + struct hyper_dmabuf_event_hdr dummy_hdr = {0}; + + ret -= sizeof(e->event_data.hdr); + + /* nullifying hdr of the event in user buffer */ + if (copy_to_user(buffer + ret, &dummy_hdr, + sizeof(dummy_hdr))) { + dev_err(hy_drv_priv->dev, + "failed to nullify invalid hdr already in userspace\n"); + } + + ret = -EFAULT; + + goto put_back_event; + } + + ret += e->event_data.hdr.size; + hy_drv_priv->pending--; + kfree(e); + } + } + + mutex_unlock(&hy_drv_priv->event_read_lock); + + return ret; +} + +#endif + +static const struct file_operations hyper_dmabuf_driver_fops = { + .owner = THIS_MODULE, + .open = hyper_dmabuf_open, + .release = hyper_dmabuf_release, + +/* poll and read interfaces are needed only for event-polling */ +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + .read = hyper_dmabuf_event_read, + .poll = hyper_dmabuf_event_poll, +#endif + + .unlocked_ioctl = hyper_dmabuf_ioctl, +}; + +static struct miscdevice hyper_dmabuf_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "hyper_dmabuf", + .fops = &hyper_dmabuf_driver_fops, +}; + +static int register_device(void) +{ + int ret = 0; + + ret = misc_register(&hyper_dmabuf_miscdev); + + if (ret) { + printk(KERN_ERR "hyper_dmabuf: driver can't be registered\n"); + return ret; + } + + hy_drv_priv->dev = hyper_dmabuf_miscdev.this_device; + + /* TODO: Check if there is a different way to initialize dma mask */ + dma_coerce_mask_and_coherent(hy_drv_priv->dev, DMA_BIT_MASK(64)); + + return ret; +} + +static void unregister_device(void) +{ + dev_info(hy_drv_priv->dev, + "hyper_dmabuf: unregister_device() is called\n"); + + misc_deregister(&hyper_dmabuf_miscdev); +} + +static int __init hyper_dmabuf_drv_init(void) +{ + int ret = 0; + + printk(KERN_NOTICE "hyper_dmabuf_starting: Initialization started\n"); + + hy_drv_priv = kcalloc(1, sizeof(struct hyper_dmabuf_private), + GFP_KERNEL); + + if (!hy_drv_priv) + return -ENOMEM; + + ret = register_device(); + if (ret < 0) + return ret; + +/* currently only supports XEN hypervisor */ +#ifdef CONFIG_HYPER_DMABUF_XEN + hy_drv_priv->bknd_ops = &xen_bknd_ops; +#elif defined (CONFIG_HYPER_DMABUF_ACRN) + hy_drv_priv->bknd_ops = &virtio_bknd_ops; +#else + hy_drv_priv->bknd_ops = NULL; + printk(KERN_ERR "No backend configured for hyper_dmabuf in kernel config\n"); +#endif + + if (hy_drv_priv->bknd_ops == NULL) { + printk(KERN_ERR "Hyper_dmabuf: no backend found\n"); + return -1; + } + + mutex_init(&hy_drv_priv->lock); + + mutex_lock(&hy_drv_priv->lock); + + hy_drv_priv->initialized = false; + + dev_info(hy_drv_priv->dev, + "initializing database for imported/exported dmabufs\n"); + + hy_drv_priv->work_queue = create_workqueue("hyper_dmabuf_wqueue"); + + ret = hyper_dmabuf_table_init(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "fail to init table for exported/imported entries\n"); + mutex_unlock(&hy_drv_priv->lock); + kfree(hy_drv_priv); + return ret; + } + +#ifdef CONFIG_HYPER_DMABUF_SYSFS + ret = hyper_dmabuf_register_sysfs(hy_drv_priv->dev); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "failed to initialize sysfs\n"); + mutex_unlock(&hy_drv_priv->lock); + kfree(hy_drv_priv); + return ret; + } +#endif + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + mutex_init(&hy_drv_priv->event_read_lock); + spin_lock_init(&hy_drv_priv->event_lock); + + /* Initialize event queue */ + INIT_LIST_HEAD(&hy_drv_priv->event_list); + init_waitqueue_head(&hy_drv_priv->event_wait); + + /* resetting number of pending events */ + hy_drv_priv->pending = 0; +#endif + + if (hy_drv_priv->bknd_ops->init) { + ret = hy_drv_priv->bknd_ops->init(); + + if (ret < 0) { + dev_dbg(hy_drv_priv->dev, + "failed to initialize backend.\n"); + return ret; + } + } + + hy_drv_priv->domid = hy_drv_priv->bknd_ops->get_vm_id(); + + hy_drv_priv->initialized = true; + if (hy_drv_priv->bknd_ops->init_comm_env) { + ret = hy_drv_priv->bknd_ops->init_comm_env(); + if (ret < 0) { + hy_drv_priv->initialized = false; + dev_dbg(hy_drv_priv->dev, + "failed to initialize comm-env.\n"); + } + } + + mutex_unlock(&hy_drv_priv->lock); + + dev_info(hy_drv_priv->dev, + "Finishing up initialization of hyper_dmabuf drv\n"); + + /* interrupt for comm should be registered here: */ + return ret; +} + +static void hyper_dmabuf_drv_exit(void) +{ +#ifdef CONFIG_HYPER_DMABUF_SYSFS + hyper_dmabuf_unregister_sysfs(hy_drv_priv->dev); +#endif + + mutex_lock(&hy_drv_priv->lock); + + /* hash tables for export/import entries and ring_infos */ + hyper_dmabuf_table_destroy(); + + if (hy_drv_priv->bknd_ops->destroy_comm) { + hy_drv_priv->bknd_ops->destroy_comm(); + } + + if (hy_drv_priv->bknd_ops->cleanup) { + hy_drv_priv->bknd_ops->cleanup(); + }; + + /* destroy workqueue */ + if (hy_drv_priv->work_queue) + destroy_workqueue(hy_drv_priv->work_queue); + + /* destroy id_queue */ + if (hy_drv_priv->id_queue) + hyper_dmabuf_free_hid_list(); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* clean up event queue */ + hyper_dmabuf_events_release(); +#endif + + mutex_unlock(&hy_drv_priv->lock); + + dev_info(hy_drv_priv->dev, + "hyper_dmabuf driver: Exiting\n"); + + kfree(hy_drv_priv); + + unregister_device(); +} + +module_init(hyper_dmabuf_drv_init); +module_exit(hyper_dmabuf_drv_exit); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h new file mode 100644 index 000000000000..45c24fd8d25d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h @@ -0,0 +1,118 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ +#define __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ + +#include +#include + +struct hyper_dmabuf_req; + +struct hyper_dmabuf_event { + struct hyper_dmabuf_event_data event_data; + struct list_head link; +}; + +struct hyper_dmabuf_private { + struct device *dev; + + /* VM(domain) id of current VM instance */ + int domid; + + /* workqueue dedicated to hyper_dmabuf driver */ + struct workqueue_struct *work_queue; + + /* list of reusable hyper_dmabuf_ids */ + struct list_reusable_id *id_queue; + + /* backend ops - hypervisor specific */ + struct hyper_dmabuf_bknd_ops *bknd_ops; + + /* device global lock */ + /* TODO: might need a lock per resource (e.g. EXPORT LIST) */ + struct mutex lock; + + /* flag that shows whether backend is initialized */ + bool initialized; + + wait_queue_head_t event_wait; + struct list_head event_list; + + spinlock_t event_lock; + struct mutex event_read_lock; + + /* # of pending events */ + int pending; +}; + +struct list_reusable_id { + hyper_dmabuf_id_t hid; + struct list_head list; +}; + +struct hyper_dmabuf_bknd_ops { + /* backend initialization routine (optional) */ + int (*init)(void); + + /* backend cleanup routine (optional) */ + void (*cleanup)(void); + + /* retreiving id of current virtual machine */ + int (*get_vm_id)(void); + + /* get pages shared via hypervisor-specific method */ + int (*share_pages)(struct page **, int, int, void **); + + /* make shared pages unshared via hypervisor specific method */ + int (*unshare_pages)(void **, int); + + /* map remotely shared pages on importer's side via + * hypervisor-specific method + */ + struct page ** (*map_shared_pages)(unsigned long, int, int, void **); + + /* unmap and free shared pages on importer's side via + * hypervisor-specific method + */ + int (*unmap_shared_pages)(void **, int); + + /* initialize communication environment */ + int (*init_comm_env)(void); + + void (*destroy_comm)(void); + + /* upstream ch setup (receiving and responding) */ + int (*init_rx_ch)(int); + + /* downstream ch setup (transmitting and parsing responses) */ + int (*init_tx_ch)(int); + + int (*send_req)(int, struct hyper_dmabuf_req *, int); +}; + +/* exporting global drv private info */ +extern struct hyper_dmabuf_private *hy_drv_priv; + +#endif /* __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c new file mode 100644 index 000000000000..392ea99e0784 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c @@ -0,0 +1,122 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_event.h" + +static void send_event(struct hyper_dmabuf_event *e) +{ + struct hyper_dmabuf_event *oldest; + unsigned long irqflags; + + spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags); + + /* check current number of event then if it hits the max num allowed + * then remove the oldest event in the list + */ + if (hy_drv_priv->pending > MAX_DEPTH_EVENT_QUEUE - 1) { + oldest = list_first_entry(&hy_drv_priv->event_list, + struct hyper_dmabuf_event, link); + list_del(&oldest->link); + hy_drv_priv->pending--; + kfree(oldest); + } + + list_add_tail(&e->link, + &hy_drv_priv->event_list); + + hy_drv_priv->pending++; + + wake_up_interruptible(&hy_drv_priv->event_wait); + + spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags); +} + +void hyper_dmabuf_events_release(void) +{ + struct hyper_dmabuf_event *e, *et; + unsigned long irqflags; + + spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags); + + list_for_each_entry_safe(e, et, &hy_drv_priv->event_list, + link) { + list_del(&e->link); + kfree(e); + hy_drv_priv->pending--; + } + + if (hy_drv_priv->pending) { + dev_err(hy_drv_priv->dev, + "possible leak on event_list\n"); + } + + spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags); +} + +int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid) +{ + struct hyper_dmabuf_event *e; + struct imported_sgt_info *imported; + + imported = hyper_dmabuf_find_imported(hid); + + if (!imported) { + dev_err(hy_drv_priv->dev, + "can't find imported_sgt_info in the list\n"); + return -EINVAL; + } + + e = kzalloc(sizeof(*e), GFP_KERNEL); + + if (!e) + return -ENOMEM; + + e->event_data.hdr.event_type = HYPER_DMABUF_NEW_IMPORT; + e->event_data.hdr.hid = hid; + e->event_data.data = (void *)imported->priv; + e->event_data.hdr.size = imported->sz_priv; + + send_event(e); + + dev_dbg(hy_drv_priv->dev, + "event number = %d :", hy_drv_priv->pending); + + dev_dbg(hy_drv_priv->dev, + "generating events for {%d, %d, %d, %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h new file mode 100644 index 000000000000..50db04faf222 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_EVENT_H__ +#define __HYPER_DMABUF_EVENT_H__ + +#define MAX_DEPTH_EVENT_QUEUE 32 + +enum hyper_dmabuf_event_type { + HYPER_DMABUF_NEW_IMPORT = 0x10000, +}; + +void hyper_dmabuf_events_release(void); + +int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid); + +#endif /* __HYPER_DMABUF_EVENT_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c new file mode 100644 index 000000000000..e67b84a7e64c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c @@ -0,0 +1,133 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_id.h" + +void hyper_dmabuf_store_hid(hyper_dmabuf_id_t hid) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + struct list_reusable_id *new_reusable; + + new_reusable = kmalloc(sizeof(*new_reusable), GFP_KERNEL); + + if (!new_reusable) + return; + + new_reusable->hid = hid; + + list_add(&new_reusable->list, &reusable_head->list); +} + +static hyper_dmabuf_id_t get_reusable_hid(void) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + hyper_dmabuf_id_t hid = {-1, {0, 0, 0} }; + + /* check there is reusable id */ + if (!list_empty(&reusable_head->list)) { + reusable_head = list_first_entry(&reusable_head->list, + struct list_reusable_id, + list); + + list_del(&reusable_head->list); + hid = reusable_head->hid; + kfree(reusable_head); + } + + return hid; +} + +void hyper_dmabuf_free_hid_list(void) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + struct list_reusable_id *temp_head; + + if (reusable_head) { + /* freeing mem space all reusable ids in the stack */ + while (!list_empty(&reusable_head->list)) { + temp_head = list_first_entry(&reusable_head->list, + struct list_reusable_id, + list); + list_del(&temp_head->list); + kfree(temp_head); + } + + /* freeing head */ + kfree(reusable_head); + } +} + +hyper_dmabuf_id_t hyper_dmabuf_get_hid(void) +{ + static int count; + hyper_dmabuf_id_t hid; + struct list_reusable_id *reusable_head; + + /* first call to hyper_dmabuf_get_id */ + if (count == 0) { + reusable_head = kmalloc(sizeof(*reusable_head), GFP_KERNEL); + + if (!reusable_head) + return (hyper_dmabuf_id_t){-1, {0, 0, 0} }; + + /* list head has an invalid count */ + reusable_head->hid.id = -1; + INIT_LIST_HEAD(&reusable_head->list); + hy_drv_priv->id_queue = reusable_head; + } + + hid = get_reusable_hid(); + + /*creating a new H-ID only if nothing in the reusable id queue + * and count is less than maximum allowed + */ + if (hid.id == -1 && count < HYPER_DMABUF_ID_MAX) + hid.id = HYPER_DMABUF_ID_CREATE(hy_drv_priv->domid, count++); + + /* random data embedded in the id for security */ + get_random_bytes(&hid.rng_key[0], 12); + + return hid; +} + +bool hyper_dmabuf_hid_keycomp(hyper_dmabuf_id_t hid1, hyper_dmabuf_id_t hid2) +{ + int i; + + /* compare keys */ + for (i = 0; i < 3; i++) { + if (hid1.rng_key[i] != hid2.rng_key[i]) + return false; + } + + return true; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h new file mode 100644 index 000000000000..ed690f3a478c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_ID_H__ +#define __HYPER_DMABUF_ID_H__ + +#define HYPER_DMABUF_ID_CREATE(domid, cnt) \ + ((((domid) & 0xFF) << 24) | ((cnt) & 0xFFFFFF)) + +#define HYPER_DMABUF_DOM_ID(hid) \ + (((hid.id) >> 24) & 0xFF) + +/* currently maximum number of buffers shared + * at any given moment is limited to 1000 + */ +#define HYPER_DMABUF_ID_MAX 1000 + +/* adding freed hid to the reusable list */ +void hyper_dmabuf_store_hid(hyper_dmabuf_id_t hid); + +/* freeing the reusasble list */ +void hyper_dmabuf_free_hid_list(void); + +/* getting a hid available to use. */ +hyper_dmabuf_id_t hyper_dmabuf_get_hid(void); + +/* comparing two different hid */ +bool hyper_dmabuf_hid_keycomp(hyper_dmabuf_id_t hid1, hyper_dmabuf_id_t hid2); + +#endif /*__HYPER_DMABUF_ID_H*/ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c new file mode 100644 index 000000000000..20274e1b9e20 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -0,0 +1,789 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_ioctl.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_sgl_proc.h" +#include "hyper_dmabuf_ops.h" +#include "hyper_dmabuf_query.h" + +static int hyper_dmabuf_tx_ch_setup_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_tx_ch_setup *tx_ch_attr; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret = 0; + + if (!data) { + dev_err(hy_drv_priv->dev, "user data is NULL\n"); + return -EINVAL; + } + tx_ch_attr = (struct ioctl_hyper_dmabuf_tx_ch_setup *)data; + + if (bknd_ops->init_tx_ch) { + ret = bknd_ops->init_tx_ch(tx_ch_attr->remote_domain); + } + + return ret; +} + +static int hyper_dmabuf_rx_ch_setup_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_rx_ch_setup *rx_ch_attr; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret = 0; + + if (!data) { + dev_err(hy_drv_priv->dev, "user data is NULL\n"); + return -EINVAL; + } + + rx_ch_attr = (struct ioctl_hyper_dmabuf_rx_ch_setup *)data; + + if (bknd_ops->init_rx_ch) + ret = bknd_ops->init_rx_ch(rx_ch_attr->source_domain); + + return ret; +} + +static int send_export_msg(struct exported_sgt_info *exported, + struct pages_info *pg_info) +{ + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct hyper_dmabuf_req *req; + int op[MAX_NUMBER_OF_OPERANDS] = {0}; + int ret, i; + + /* now create request for importer via ring */ + op[0] = exported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = exported->hid.rng_key[i]; + + if (pg_info) { + op[4] = pg_info->nents; + op[5] = pg_info->frst_ofst; + op[6] = pg_info->last_len; + op[7] = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, + pg_info->nents, &exported->refs_info); + if (op[7] < 0) { + dev_err(hy_drv_priv->dev, "pages sharing failed\n"); + return op[7]; + } + } + + op[8] = exported->sz_priv; + + /* driver/application specific private info */ + memcpy(&op[9], exported->priv, op[8]); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + /* composing a message to the importer */ + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT, &op[0]); + + ret = bknd_ops->send_req(exported->rdomid, req, true); + + kfree(req); + + return ret; +} + +/* Fast path exporting routine in case same buffer is already exported. + * In this function, we skip normal exporting process and just update + * private data on both VMs (importer and exporter) + * + * return '1' if reexport is needed, return '0' if succeeds, return + * Kernel error code if something goes wrong + */ +static int fastpath_export(hyper_dmabuf_id_t hid, int sz_priv, char *priv) +{ + int reexport = 1; + int ret = 0; + struct exported_sgt_info *exported; + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) + return reexport; + + if (exported->valid == false) + return reexport; + + /* + * Check if unexport is already scheduled for that buffer, + * if so try to cancel it. If that will fail, buffer needs + * to be reexport once again. + */ + if (exported->unexport_sched) { + if (!cancel_delayed_work_sync(&exported->unexport)) + return reexport; + + exported->unexport_sched = false; + } + + /* if there's any change in size of private data. + * we reallocate space for private data with new size + */ + if (sz_priv != exported->sz_priv) { + kfree(exported->priv); + + /* truncating size */ + if (sz_priv > MAX_SIZE_PRIV_DATA) + exported->sz_priv = MAX_SIZE_PRIV_DATA; + else + exported->sz_priv = sz_priv; + + exported->priv = kcalloc(1, exported->sz_priv, + GFP_KERNEL); + + if (!exported->priv) { + hyper_dmabuf_remove_exported(exported->hid); + hyper_dmabuf_cleanup_sgt_info(exported, true); + kfree(exported); + return -ENOMEM; + } + } + + /* update private data in sgt_info with new ones */ + ret = copy_from_user(exported->priv, priv, exported->sz_priv); + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to load a new private data\n"); + ret = -EINVAL; + } else { + /* send an export msg for updating priv in importer */ + ret = send_export_msg(exported, NULL); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to send a new private data\n"); + ret = -EBUSY; + } + } + + return ret; +} + +static int hyper_dmabuf_export_remote_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_export_remote *export_remote_attr = + (struct ioctl_hyper_dmabuf_export_remote *)data; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + struct pages_info *pg_info; + struct exported_sgt_info *exported; + hyper_dmabuf_id_t hid; + int ret = 0; + + if (hy_drv_priv->domid == export_remote_attr->remote_domain) { + dev_err(hy_drv_priv->dev, + "exporting to the same VM is not permitted\n"); + return -EINVAL; + } + + dma_buf = dma_buf_get(export_remote_attr->dmabuf_fd); + + if (IS_ERR(dma_buf)) { + dev_err(hy_drv_priv->dev, "Cannot get dma buf\n"); + return PTR_ERR(dma_buf); + } + + /* we check if this specific attachment was already exported + * to the same domain and if yes and it's valid sgt_info, + * it returns hyper_dmabuf_id of pre-exported sgt_info + */ + hid = hyper_dmabuf_find_hid_exported(dma_buf, + export_remote_attr->remote_domain); + + if (hid.id != -1) { + ret = fastpath_export(hid, export_remote_attr->sz_priv, + export_remote_attr->priv); + + /* return if fastpath_export succeeds or + * gets some fatal error + */ + if (ret <= 0) { + dma_buf_put(dma_buf); + export_remote_attr->hid = hid; + return ret; + } + } + + attachment = dma_buf_attach(dma_buf, hy_drv_priv->dev); + if (IS_ERR(attachment)) { + dev_err(hy_drv_priv->dev, "cannot get attachment\n"); + ret = PTR_ERR(attachment); + goto fail_attach; + } + + sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + + if (IS_ERR(sgt)) { + dev_err(hy_drv_priv->dev, "cannot map attachment\n"); + ret = PTR_ERR(sgt); + goto fail_map_attachment; + } + + exported = kcalloc(1, sizeof(*exported), GFP_KERNEL); + + if (!exported) { + ret = -ENOMEM; + goto fail_sgt_info_creation; + } + + /* possible truncation */ + if (export_remote_attr->sz_priv > MAX_SIZE_PRIV_DATA) + exported->sz_priv = MAX_SIZE_PRIV_DATA; + else + exported->sz_priv = export_remote_attr->sz_priv; + + /* creating buffer for private data of buffer */ + if (exported->sz_priv != 0) { + exported->priv = kcalloc(1, exported->sz_priv, GFP_KERNEL); + + if (!exported->priv) { + ret = -ENOMEM; + goto fail_priv_creation; + } + } else { + dev_err(hy_drv_priv->dev, "size is 0\n"); + } + + exported->hid = hyper_dmabuf_get_hid(); + + /* no more exported dmabuf allowed */ + if (exported->hid.id == -1) { + dev_err(hy_drv_priv->dev, + "exceeds allowed number of dmabuf to be exported\n"); + ret = -ENOMEM; + goto fail_sgt_info_creation; + } + + exported->rdomid = export_remote_attr->remote_domain; + exported->dma_buf = dma_buf; + exported->valid = true; + + exported->active_sgts = kmalloc(sizeof(struct sgt_list), GFP_KERNEL); + if (!exported->active_sgts) { + ret = -ENOMEM; + goto fail_map_active_sgts; + } + + exported->active_attached = kmalloc(sizeof(struct attachment_list), + GFP_KERNEL); + if (!exported->active_attached) { + ret = -ENOMEM; + goto fail_map_active_attached; + } + + exported->va_kmapped = kmalloc(sizeof(struct kmap_vaddr_list), + GFP_KERNEL); + if (!exported->va_kmapped) { + ret = -ENOMEM; + goto fail_map_va_kmapped; + } + + exported->va_vmapped = kmalloc(sizeof(struct vmap_vaddr_list), + GFP_KERNEL); + if (!exported->va_vmapped) { + ret = -ENOMEM; + goto fail_map_va_vmapped; + } + + exported->active_sgts->sgt = sgt; + exported->active_attached->attach = attachment; + exported->va_kmapped->vaddr = NULL; + exported->va_vmapped->vaddr = NULL; + + /* initialize list of sgt, attachment and vaddr for dmabuf sync + * via shadow dma-buf + */ + INIT_LIST_HEAD(&exported->active_sgts->list); + INIT_LIST_HEAD(&exported->active_attached->list); + INIT_LIST_HEAD(&exported->va_kmapped->list); + INIT_LIST_HEAD(&exported->va_vmapped->list); + + /* copy private data to sgt_info */ + ret = copy_from_user(exported->priv, export_remote_attr->priv, + exported->sz_priv); + + if (ret) { + dev_err(hy_drv_priv->dev, + "failed to load private data\n"); + ret = -EINVAL; + goto fail_export; + } + + pg_info = hyper_dmabuf_ext_pgs(sgt); + if (!pg_info) { + dev_err(hy_drv_priv->dev, + "failed to construct pg_info\n"); + ret = -ENOMEM; + goto fail_export; + } + + exported->nents = pg_info->nents; + + /* now register it to export list */ + hyper_dmabuf_register_exported(exported); + + export_remote_attr->hid = exported->hid; + + ret = send_export_msg(exported, pg_info); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "failed to send out the export request\n"); + goto fail_send_request; + } + + /* free pg_info */ + kfree(pg_info->pgs); + kfree(pg_info); + + exported->filp = filp; + + return ret; + +/* Clean-up if error occurs */ + +fail_send_request: + hyper_dmabuf_remove_exported(exported->hid); + + /* free pg_info */ + kfree(pg_info->pgs); + kfree(pg_info); + +fail_export: + kfree(exported->va_vmapped); + +fail_map_va_vmapped: + kfree(exported->va_kmapped); + +fail_map_va_kmapped: + kfree(exported->active_attached); + +fail_map_active_attached: + kfree(exported->active_sgts); + kfree(exported->priv); + +fail_priv_creation: + kfree(exported); + +fail_map_active_sgts: +fail_sgt_info_creation: + dma_buf_unmap_attachment(attachment, sgt, + DMA_BIDIRECTIONAL); + +fail_map_attachment: + dma_buf_detach(dma_buf, attachment); + +fail_attach: + dma_buf_put(dma_buf); + + return ret; +} + +static int hyper_dmabuf_export_fd_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_export_fd *export_fd_attr = + (struct ioctl_hyper_dmabuf_export_fd *)data; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct imported_sgt_info *imported; + struct hyper_dmabuf_req *req; + struct page **data_pgs; + int op[4]; + int i; + int ret = 0; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + /* look for dmabuf for the id */ + imported = hyper_dmabuf_find_imported(export_fd_attr->hid); + + /* can't find sgt from the table */ + if (!imported) { + dev_err(hy_drv_priv->dev, "can't find the entry\n"); + return -ENOENT; + } + + mutex_lock(&hy_drv_priv->lock); + + imported->importers++; + + /* send notification for export_fd to exporter */ + op[0] = imported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = imported->hid.rng_key[i]; + + dev_dbg(hy_drv_priv->dev, "Export FD of buffer {id:%d key:%d %d %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) { + mutex_unlock(&hy_drv_priv->lock); + return -ENOMEM; + } + + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT_FD, &op[0]); + + ret = bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, true); + + if (ret < 0) { + /* in case of timeout other end eventually will receive request, + * so we need to undo it + */ + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT_FD_FAILED, + &op[0]); + bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, false); + kfree(req); + dev_err(hy_drv_priv->dev, + "Failed to create sgt or notify exporter\n"); + imported->importers--; + mutex_unlock(&hy_drv_priv->lock); + return ret; + } + + kfree(req); + + if (ret == HYPER_DMABUF_REQ_ERROR) { + dev_err(hy_drv_priv->dev, + "Buffer invalid {id:%d key:%d %d %d}, cannot import\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + imported->importers--; + mutex_unlock(&hy_drv_priv->lock); + return -EINVAL; + } + + ret = 0; + + dev_dbg(hy_drv_priv->dev, + "Found buffer gref %d off %d\n", + imported->ref_handle, imported->frst_ofst); + + dev_dbg(hy_drv_priv->dev, + "last len %d nents %d domain %d\n", + imported->last_len, imported->nents, + HYPER_DMABUF_DOM_ID(imported->hid)); + + if (!imported->sgt) { + dev_dbg(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} pages not mapped yet\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + data_pgs = bknd_ops->map_shared_pages(imported->ref_handle, + HYPER_DMABUF_DOM_ID(imported->hid), + imported->nents, + &imported->refs_info); + + if (!data_pgs) { + dev_err(hy_drv_priv->dev, + "can't map pages hid {id:%d key:%d %d %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], + imported->hid.rng_key[2]); + + imported->importers--; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) { + mutex_unlock(&hy_drv_priv->lock); + return -ENOMEM; + } + + hyper_dmabuf_create_req(req, + HYPER_DMABUF_EXPORT_FD_FAILED, + &op[0]); + bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, + false); + kfree(req); + mutex_unlock(&hy_drv_priv->lock); + return -EINVAL; + } + + imported->sgt = hyper_dmabuf_create_sgt(data_pgs, + imported->frst_ofst, + imported->last_len, + imported->nents); + + } + + export_fd_attr->fd = hyper_dmabuf_export_fd(imported, + export_fd_attr->flags); + + if (export_fd_attr->fd < 0) { + /* fail to get fd */ + ret = export_fd_attr->fd; + } + + mutex_unlock(&hy_drv_priv->lock); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return ret; +} + +/* unexport dmabuf from the database and send int req to the source domain + * to unmap it. + */ +static void delayed_unexport(struct work_struct *work) +{ + struct hyper_dmabuf_req *req; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct exported_sgt_info *exported = + container_of(work, struct exported_sgt_info, unexport.work); + int op[4]; + int i, ret; + + if (!exported) + return; + + dev_dbg(hy_drv_priv->dev, + "Marking buffer {id:%d key:%d %d %d} as invalid\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + /* no longer valid */ + exported->valid = false; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return; + + op[0] = exported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = exported->hid.rng_key[i]; + + hyper_dmabuf_create_req(req, HYPER_DMABUF_NOTIFY_UNEXPORT, &op[0]); + + /* Now send unexport request to remote domain, marking + * that buffer should not be used anymore + */ + ret = bknd_ops->send_req(exported->rdomid, req, true); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "unexport message for buffer {id:%d key:%d %d %d} failed\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + } + + kfree(req); + exported->unexport_sched = false; + + /* Immediately clean-up if it has never been exported by importer + * (so no SGT is constructed on importer). + * clean it up later in remote sync when final release ops + * is called (importer does this only when there's no + * no consumer of locally exported FDs) + */ + if (exported->active == 0) { + dev_dbg(hy_drv_priv->dev, + "claning up buffer {id:%d key:%d %d %d} completly\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + hyper_dmabuf_cleanup_sgt_info(exported, false); + hyper_dmabuf_remove_exported(exported->hid); + + /* register hyper_dmabuf_id to the list for reuse */ + hyper_dmabuf_store_hid(exported->hid); + + if (exported->sz_priv > 0 && !exported->priv) + kfree(exported->priv); + + kfree(exported); + } +} + +/* Schedule unexport of dmabuf. + */ +int hyper_dmabuf_unexport_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_unexport *unexport_attr = + (struct ioctl_hyper_dmabuf_unexport *)data; + struct exported_sgt_info *exported; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + /* find dmabuf in export list */ + exported = hyper_dmabuf_find_exported(unexport_attr->hid); + + dev_dbg(hy_drv_priv->dev, + "scheduling unexport of buffer {id:%d key:%d %d %d}\n", + unexport_attr->hid.id, unexport_attr->hid.rng_key[0], + unexport_attr->hid.rng_key[1], unexport_attr->hid.rng_key[2]); + + /* failed to find corresponding entry in export list */ + if (exported == NULL) { + unexport_attr->status = -ENOENT; + return -ENOENT; + } + + if (exported->unexport_sched) + return 0; + + exported->unexport_sched = true; + INIT_DELAYED_WORK(&exported->unexport, delayed_unexport); + schedule_delayed_work(&exported->unexport, + msecs_to_jiffies(unexport_attr->delay_ms)); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} + +static int hyper_dmabuf_query_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_query *query_attr = + (struct ioctl_hyper_dmabuf_query *)data; + struct exported_sgt_info *exported = NULL; + struct imported_sgt_info *imported = NULL; + int ret = 0; + + if (HYPER_DMABUF_DOM_ID(query_attr->hid) == hy_drv_priv->domid) { + /* query for exported dmabuf */ + exported = hyper_dmabuf_find_exported(query_attr->hid); + if (exported) { + ret = hyper_dmabuf_query_exported(exported, + query_attr->item, + &query_attr->info); + } else { + dev_err(hy_drv_priv->dev, + "hid {id:%d key:%d %d %d} not in exp list\n", + query_attr->hid.id, + query_attr->hid.rng_key[0], + query_attr->hid.rng_key[1], + query_attr->hid.rng_key[2]); + return -ENOENT; + } + } else { + /* query for imported dmabuf */ + imported = hyper_dmabuf_find_imported(query_attr->hid); + if (imported) { + ret = hyper_dmabuf_query_imported(imported, + query_attr->item, + &query_attr->info); + } else { + dev_err(hy_drv_priv->dev, + "hid {id:%d key:%d %d %d} not in imp list\n", + query_attr->hid.id, + query_attr->hid.rng_key[0], + query_attr->hid.rng_key[1], + query_attr->hid.rng_key[2]); + return -ENOENT; + } + } + + return ret; +} + +const struct hyper_dmabuf_ioctl_desc hyper_dmabuf_ioctls[] = { + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_TX_CH_SETUP, + hyper_dmabuf_tx_ch_setup_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_RX_CH_SETUP, + hyper_dmabuf_rx_ch_setup_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_REMOTE, + hyper_dmabuf_export_remote_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_FD, + hyper_dmabuf_export_fd_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_UNEXPORT, + hyper_dmabuf_unexport_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_QUERY, + hyper_dmabuf_query_ioctl, 0), +}; + +long hyper_dmabuf_ioctl(struct file *filp, + unsigned int cmd, unsigned long param) +{ + const struct hyper_dmabuf_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + int ret; + hyper_dmabuf_ioctl_t func; + char *kdata; + + if (nr > ARRAY_SIZE(hyper_dmabuf_ioctls)) { + dev_err(hy_drv_priv->dev, "invalid ioctl\n"); + return -EINVAL; + } + + ioctl = &hyper_dmabuf_ioctls[nr]; + + func = ioctl->func; + + if (unlikely(!func)) { + dev_err(hy_drv_priv->dev, "no function\n"); + return -EINVAL; + } + + kdata = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (!kdata) + return -ENOMEM; + + if (copy_from_user(kdata, (void __user *)param, + _IOC_SIZE(cmd)) != 0) { + dev_err(hy_drv_priv->dev, + "failed to copy from user arguments\n"); + ret = -EFAULT; + goto ioctl_error; + } + + ret = func(filp, kdata); + + if (copy_to_user((void __user *)param, kdata, + _IOC_SIZE(cmd)) != 0) { + dev_err(hy_drv_priv->dev, + "failed to copy to user arguments\n"); + ret = -EFAULT; + goto ioctl_error; + } + +ioctl_error: + kfree(kdata); + + return ret; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h new file mode 100644 index 000000000000..5991a87b194f --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_IOCTL_H__ +#define __HYPER_DMABUF_IOCTL_H__ + +typedef int (*hyper_dmabuf_ioctl_t)(struct file *filp, void *data); + +struct hyper_dmabuf_ioctl_desc { + unsigned int cmd; + int flags; + hyper_dmabuf_ioctl_t func; + const char *name; +}; + +#define HYPER_DMABUF_IOCTL_DEF(ioctl, _func, _flags) \ + [_IOC_NR(ioctl)] = { \ + .cmd = ioctl, \ + .func = _func, \ + .flags = _flags, \ + .name = #ioctl \ + } + +long hyper_dmabuf_ioctl(struct file *filp, + unsigned int cmd, unsigned long param); + +int hyper_dmabuf_unexport_ioctl(struct file *filp, void *data); + +#endif //__HYPER_DMABUF_IOCTL_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c new file mode 100644 index 000000000000..bba6d1d607a8 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c @@ -0,0 +1,293 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_event.h" + +DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED); +DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED); + +#ifdef CONFIG_HYPER_DMABUF_SYSFS +static ssize_t hyper_dmabuf_imported_show(struct device *drv, + struct device_attribute *attr, + char *buf) +{ + struct list_entry_imported *info_entry; + int bkt; + ssize_t count = 0; + size_t total = 0; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) { + hyper_dmabuf_id_t hid = info_entry->imported->hid; + int nents = info_entry->imported->nents; + bool valid = info_entry->imported->valid; + int num_importers = info_entry->imported->importers; + + total += nents; + count += scnprintf(buf + count, PAGE_SIZE - count, + "hid:{%d %d %d %d}, nent:%d, v:%c, numi:%d\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2], nents, (valid ? 't' : 'f'), + num_importers); + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "total nents: %lu\n", total); + + return count; +} + +static ssize_t hyper_dmabuf_exported_show(struct device *drv, + struct device_attribute *attr, + char *buf) +{ + struct list_entry_exported *info_entry; + int bkt; + ssize_t count = 0; + size_t total = 0; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) { + hyper_dmabuf_id_t hid = info_entry->exported->hid; + int nents = info_entry->exported->nents; + bool valid = info_entry->exported->valid; + int importer_exported = info_entry->exported->active; + + total += nents; + count += scnprintf(buf + count, PAGE_SIZE - count, + "hid:{%d %d %d %d}, nent:%d, v:%c, ie:%d\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2], nents, (valid ? 't' : 'f'), + importer_exported); + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "total nents: %lu\n", total); + + return count; +} + +static DEVICE_ATTR(imported, 0400, hyper_dmabuf_imported_show, NULL); +static DEVICE_ATTR(exported, 0400, hyper_dmabuf_exported_show, NULL); + +int hyper_dmabuf_register_sysfs(struct device *dev) +{ + int err; + + err = device_create_file(dev, &dev_attr_imported); + if (err < 0) + goto err1; + err = device_create_file(dev, &dev_attr_exported); + if (err < 0) + goto err2; + + return 0; +err2: + device_remove_file(dev, &dev_attr_imported); +err1: + return -1; +} + +int hyper_dmabuf_unregister_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_imported); + device_remove_file(dev, &dev_attr_exported); + return 0; +} + +#endif + +int hyper_dmabuf_table_init(void) +{ + hash_init(hyper_dmabuf_hash_imported); + hash_init(hyper_dmabuf_hash_exported); + return 0; +} + +int hyper_dmabuf_table_destroy(void) +{ + /* TODO: cleanup hyper_dmabuf_hash_imported + * and hyper_dmabuf_hash_exported + */ + return 0; +} + +int hyper_dmabuf_register_exported(struct exported_sgt_info *exported) +{ + struct list_entry_exported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->exported = exported; + + hash_add(hyper_dmabuf_hash_exported, &info_entry->node, + info_entry->exported->hid.id); + + return 0; +} + +int hyper_dmabuf_register_imported(struct imported_sgt_info *imported) +{ + struct list_entry_imported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->imported = imported; + + hash_add(hyper_dmabuf_hash_imported, &info_entry->node, + info_entry->imported->hid.id); + + return 0; +} + +struct exported_sgt_info *hyper_dmabuf_find_exported(hyper_dmabuf_id_t hid) +{ + struct list_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->exported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->exported->hid, + hid)) + return info_entry->exported; + + /* if key is unmatched, given HID is invalid, + * so returning NULL + */ + break; + } + + return NULL; +} + +/* search for pre-exported sgt and return id of it if it exist */ +hyper_dmabuf_id_t hyper_dmabuf_find_hid_exported(struct dma_buf *dmabuf, + int domid) +{ + struct list_entry_exported *info_entry; + hyper_dmabuf_id_t hid = {-1, {0, 0, 0} }; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + if (info_entry->exported->dma_buf == dmabuf && + info_entry->exported->rdomid == domid) + return info_entry->exported->hid; + + return hid; +} + +struct imported_sgt_info *hyper_dmabuf_find_imported(hyper_dmabuf_id_t hid) +{ + struct list_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->imported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->imported->hid, + hid)) + return info_entry->imported; + /* if key is unmatched, given HID is invalid, + * so returning NULL + */ + break; + } + + return NULL; +} + +int hyper_dmabuf_remove_exported(hyper_dmabuf_id_t hid) +{ + struct list_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->exported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->exported->hid, + hid)) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + break; + } + + return -ENOENT; +} + +int hyper_dmabuf_remove_imported(hyper_dmabuf_id_t hid) +{ + struct list_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->imported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->imported->hid, + hid)) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + break; + } + + return -ENOENT; +} + +void hyper_dmabuf_foreach_exported( + void (*func)(struct exported_sgt_info *, void *attr), + void *attr) +{ + struct list_entry_exported *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(hyper_dmabuf_hash_exported, bkt, tmp, + info_entry, node) { + func(info_entry->exported, attr); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h new file mode 100644 index 000000000000..f7102f5db75d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_LIST_H__ +#define __HYPER_DMABUF_LIST_H__ + +#include "hyper_dmabuf_struct.h" + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_EXPORTED 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_IMPORTED 7 + +struct list_entry_exported { + struct exported_sgt_info *exported; + struct hlist_node node; +}; + +struct list_entry_imported { + struct imported_sgt_info *imported; + struct hlist_node node; +}; + +int hyper_dmabuf_table_init(void); + +int hyper_dmabuf_table_destroy(void); + +int hyper_dmabuf_register_exported(struct exported_sgt_info *info); + +/* search for pre-exported sgt and return id of it if it exist */ +hyper_dmabuf_id_t hyper_dmabuf_find_hid_exported(struct dma_buf *dmabuf, + int domid); + +int hyper_dmabuf_register_imported(struct imported_sgt_info *info); + +struct exported_sgt_info *hyper_dmabuf_find_exported(hyper_dmabuf_id_t hid); + +struct imported_sgt_info *hyper_dmabuf_find_imported(hyper_dmabuf_id_t hid); + +int hyper_dmabuf_remove_exported(hyper_dmabuf_id_t hid); + +int hyper_dmabuf_remove_imported(hyper_dmabuf_id_t hid); + +void hyper_dmabuf_foreach_exported(void (*func)(struct exported_sgt_info *, + void *attr), void *attr); + +int hyper_dmabuf_register_sysfs(struct device *dev); +int hyper_dmabuf_unregister_sysfs(struct device *dev); + +#endif /* __HYPER_DMABUF_LIST_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c new file mode 100644 index 000000000000..37ee894ec418 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -0,0 +1,413 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_remote_sync.h" +#include "hyper_dmabuf_event.h" +#include "hyper_dmabuf_list.h" + +struct cmd_process { + struct work_struct work; + struct hyper_dmabuf_req *rq; + int domid; +}; + +void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, + enum hyper_dmabuf_command cmd, int *op) +{ + int i; + + req->stat = HYPER_DMABUF_REQ_NOT_RESPONDED; + req->cmd = cmd; + + switch (cmd) { + /* as exporter, commands to importer */ + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * op0~op3 : hyper_dmabuf_id + * op4 : number of pages to be shared + * op5 : offset of data in the first page + * op6 : length of data in the last page + * op7 : top-level reference number for shared pages + * op8 : size of private data (from op9) + * op9 ~ : Driver-specific private data + * (e.g. graphic buffer's meta info) + */ + + memcpy(&req->op[0], &op[0], 9 * sizeof(int) + op[8]); + break; + + case HYPER_DMABUF_NOTIFY_UNEXPORT: + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : DMABUF_DESTROY, + * op0~op3 : hyper_dmabuf_id_t hid + */ + + for (i = 0; i < 4; i++) + req->op[i] = op[i]; + break; + + case HYPER_DMABUF_EXPORT_FD: + case HYPER_DMABUF_EXPORT_FD_FAILED: + /* dmabuf fd is being created on imported side or importing + * failed + * + * command : HYPER_DMABUF_EXPORT_FD or + * HYPER_DMABUF_EXPORT_FD_FAILED, + * op0~op3 : hyper_dmabuf_id + */ + + for (i = 0; i < 4; i++) + req->op[i] = op[i]; + break; + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer (probably not needed) + * for dmabuf synchronization + */ + break; + + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will make + * the driver to do shadow mapping or unmapping for + * synchronization with original exporter (e.g. i915) + * + * command : DMABUF_OPS_TO_SOURCE. + * op0~3 : hyper_dmabuf_id + * op4 : map(=1)/unmap(=2)/attach(=3)/detach(=4) + */ + for (i = 0; i < 5; i++) + req->op[i] = op[i]; + break; + + default: + /* no command found */ + return; + } +} + +static void cmd_process_work(struct work_struct *work) +{ + struct imported_sgt_info *imported; + struct cmd_process *proc = container_of(work, + struct cmd_process, work); + struct hyper_dmabuf_req *req; + hyper_dmabuf_id_t hid; + int domid; + int i; + + req = proc->rq; + domid = proc->domid; + + switch (req->cmd) { + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * op0~op3 : hyper_dmabuf_id + * op4 : number of pages to be shared + * op5 : offset of data in the first page + * op6 : length of data in the last page + * op7 : top-level reference number for shared pages + * op8 : size of private data (from op9) + * op9 ~ : Driver-specific private data + * (e.g. graphic buffer's meta info) + */ + + /* if nents == 0, it means it is a message only for + * priv synchronization. for existing imported_sgt_info + * so not creating a new one + */ + if (req->op[4] == 0) { + hyper_dmabuf_id_t exist = {req->op[0], + {req->op[1], req->op[2], + req->op[3] } }; + + imported = hyper_dmabuf_find_imported(exist); + + if (!imported) { + dev_err(hy_drv_priv->dev, + "Can't find imported sgt_info\n"); + break; + } + + /* if size of new private data is different, + * we reallocate it. + */ + if (imported->sz_priv != req->op[8]) { + kfree(imported->priv); + imported->sz_priv = req->op[8]; + imported->priv = kcalloc(1, req->op[8], + GFP_KERNEL); + if (!imported->priv) { + /* set it invalid */ + imported->valid = 0; + break; + } + } + + /* updating priv data */ + memcpy(imported->priv, &req->op[9], req->op[8]); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* generating import event */ + hyper_dmabuf_import_event(imported->hid); +#endif + + break; + } + + imported = kcalloc(1, sizeof(*imported), GFP_KERNEL); + + if (!imported) + break; + + imported->sz_priv = req->op[8]; + imported->priv = kcalloc(1, req->op[8], GFP_KERNEL); + + if (!imported->priv) { + kfree(imported); + break; + } + + imported->hid.id = req->op[0]; + + for (i = 0; i < 3; i++) + imported->hid.rng_key[i] = req->op[i+1]; + + imported->nents = req->op[4]; + imported->frst_ofst = req->op[5]; + imported->last_len = req->op[6]; + imported->ref_handle = req->op[7]; + + dev_dbg(hy_drv_priv->dev, "DMABUF was exported\n"); + dev_dbg(hy_drv_priv->dev, "\thid{id:%d key:%d %d %d}\n", + req->op[0], req->op[1], req->op[2], + req->op[3]); + dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); + dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); + dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); + dev_dbg(hy_drv_priv->dev, "\tgrefid %d\n", req->op[7]); + + memcpy(imported->priv, &req->op[9], req->op[8]); + + imported->valid = true; + hyper_dmabuf_register_imported(imported); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* generating import event */ + hyper_dmabuf_import_event(imported->hid); +#endif + + break; + + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will + * make the driver to do shadow mapping + * or unmapping for synchronization with original + * exporter (e.g. i915) + * + * command : DMABUF_OPS_TO_SOURCE. + * op0~3 : hyper_dmabuf_id + * op1 : enum hyper_dmabuf_ops {....} + */ + dev_dbg(hy_drv_priv->dev, + "%s: HYPER_DMABUF_OPS_TO_SOURCE\n", __func__); + + hid.id = req->op[0]; + hid.rng_key[0] = req->op[1]; + hid.rng_key[1] = req->op[2]; + hid.rng_key[2] = req->op[3]; + hyper_dmabuf_remote_sync(hid, req->op[4]); + + break; + + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer + * (probably not needed) for dmabuf synchronization + */ + break; + + default: + /* shouldn't get here */ + break; + } + + kfree(req); + kfree(proc); +} + +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req) +{ + struct cmd_process *proc; + struct hyper_dmabuf_req *temp_req; + struct imported_sgt_info *imported; + struct exported_sgt_info *exported; + hyper_dmabuf_id_t hid; + + if (!req) { + dev_err(hy_drv_priv->dev, "request is NULL\n"); + return -EINVAL; + } + + hid.id = req->op[0]; + hid.rng_key[0] = req->op[1]; + hid.rng_key[1] = req->op[2]; + hid.rng_key[2] = req->op[3]; + + if ((req->cmd < HYPER_DMABUF_EXPORT) || + (req->cmd > HYPER_DMABUF_OPS_TO_SOURCE)) { + dev_err(hy_drv_priv->dev, "invalid command\n"); + return -EINVAL; + } + + req->stat = HYPER_DMABUF_REQ_PROCESSED; + + /* HYPER_DMABUF_DESTROY requires immediate + * follow up so can't be processed in workqueue + */ + if (req->cmd == HYPER_DMABUF_NOTIFY_UNEXPORT) { + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : HYPER_DMABUF_NOTIFY_UNEXPORT, + * op0~3 : hyper_dmabuf_id + */ + dev_dbg(hy_drv_priv->dev, + "processing HYPER_DMABUF_NOTIFY_UNEXPORT\n"); + + imported = hyper_dmabuf_find_imported(hid); + + if (imported) { + /* if anything is still using dma_buf */ + if (imported->importers) { + /* Buffer is still in use, just mark that + * it should not be allowed to export its fd + * anymore. + */ + imported->valid = false; + } else { + /* No one is using buffer, remove it from + * imported list + */ + hyper_dmabuf_remove_imported(hid); + kfree(imported->priv); + kfree(imported); + } + } else { + req->stat = HYPER_DMABUF_REQ_ERROR; + } + + return req->cmd; + } + + /* synchronous dma_buf_fd export */ + if (req->cmd == HYPER_DMABUF_EXPORT_FD) { + /* find a corresponding SGT for the id */ + dev_dbg(hy_drv_priv->dev, + "HYPER_DMABUF_EXPORT_FD for {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], hid.rng_key[2]); + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} not found\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else if (!exported->valid) { + dev_dbg(hy_drv_priv->dev, + "Buffer no longer valid {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else { + dev_dbg(hy_drv_priv->dev, + "Buffer still valid {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + exported->active++; + req->stat = HYPER_DMABUF_REQ_PROCESSED; + } + return req->cmd; + } + + if (req->cmd == HYPER_DMABUF_EXPORT_FD_FAILED) { + dev_dbg(hy_drv_priv->dev, + "HYPER_DMABUF_EXPORT_FD_FAILED for {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], hid.rng_key[2]); + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} not found\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else { + exported->active--; + req->stat = HYPER_DMABUF_REQ_PROCESSED; + } + return req->cmd; + } + + dev_dbg(hy_drv_priv->dev, + "%s: putting request to workqueue\n", __func__); + temp_req = kmalloc(sizeof(*temp_req), GFP_ATOMIC); + + if (!temp_req) + return -ENOMEM; + + memcpy(temp_req, req, sizeof(*temp_req)); + + proc = kcalloc(1, sizeof(struct cmd_process), GFP_ATOMIC); + + if (!proc) { + kfree(temp_req); + return -ENOMEM; + } + + proc->rq = temp_req; + proc->domid = domid; + + INIT_WORK(&(proc->work), cmd_process_work); + + queue_work(hy_drv_priv->work_queue, &(proc->work)); + + return req->cmd; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h new file mode 100644 index 000000000000..9c8a76bf261e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h @@ -0,0 +1,87 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_MSG_H__ +#define __HYPER_DMABUF_MSG_H__ + +#define MAX_NUMBER_OF_OPERANDS 64 + +struct hyper_dmabuf_req { + unsigned int req_id; + unsigned int stat; + unsigned int cmd; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; +}; + +struct hyper_dmabuf_resp { + unsigned int resp_id; + unsigned int stat; + unsigned int cmd; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; +}; + +enum hyper_dmabuf_command { + HYPER_DMABUF_EXPORT = 0x10, + HYPER_DMABUF_EXPORT_FD, + HYPER_DMABUF_EXPORT_FD_FAILED, + HYPER_DMABUF_NOTIFY_UNEXPORT, + HYPER_DMABUF_OPS_TO_REMOTE, + HYPER_DMABUF_OPS_TO_SOURCE, +}; + +enum hyper_dmabuf_ops { + HYPER_DMABUF_OPS_ATTACH = 0x1000, + HYPER_DMABUF_OPS_DETACH, + HYPER_DMABUF_OPS_MAP, + HYPER_DMABUF_OPS_UNMAP, + HYPER_DMABUF_OPS_RELEASE, + HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS, + HYPER_DMABUF_OPS_END_CPU_ACCESS, + HYPER_DMABUF_OPS_KMAP_ATOMIC, + HYPER_DMABUF_OPS_KUNMAP_ATOMIC, + HYPER_DMABUF_OPS_KMAP, + HYPER_DMABUF_OPS_KUNMAP, + HYPER_DMABUF_OPS_MMAP, + HYPER_DMABUF_OPS_VMAP, + HYPER_DMABUF_OPS_VUNMAP, +}; + +enum hyper_dmabuf_req_feedback { + HYPER_DMABUF_REQ_PROCESSED = 0x100, + HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP, + HYPER_DMABUF_REQ_ERROR, + HYPER_DMABUF_REQ_NOT_RESPONDED +}; + +/* create a request packet with given command and operands */ +void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, + enum hyper_dmabuf_command command, + int *operands); + +/* parse incoming request packet (or response) and take + * appropriate actions for those + */ +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req); + +#endif // __HYPER_DMABUF_MSG_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c new file mode 100644 index 000000000000..915743741897 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -0,0 +1,414 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_ops.h" +#include "hyper_dmabuf_sgl_proc.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_list.h" + +#define WAIT_AFTER_SYNC_REQ 0 +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +static int dmabuf_refcount(struct dma_buf *dma_buf) +{ + if ((dma_buf != NULL) && (dma_buf->file != NULL)) + return file_count(dma_buf->file); + + return -EINVAL; +} + +static int sync_request(hyper_dmabuf_id_t hid, int dmabuf_ops) +{ + struct hyper_dmabuf_req *req; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int op[5]; + int i; + int ret; + + op[0] = hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = hid.rng_key[i]; + + op[4] = dmabuf_ops; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + hyper_dmabuf_create_req(req, HYPER_DMABUF_OPS_TO_SOURCE, &op[0]); + + /* send request and wait for a response */ + ret = bknd_ops->send_req(HYPER_DMABUF_DOM_ID(hid), req, + WAIT_AFTER_SYNC_REQ); + + if (ret < 0) { + dev_dbg(hy_drv_priv->dev, + "dmabuf sync request failed:%d\n", req->op[4]); + } + + kfree(req); + + return ret; +} + +static int hyper_dmabuf_ops_attach(struct dma_buf *dmabuf, + struct device *dev, + struct dma_buf_attachment *attach) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attach->dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)attach->dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_ATTACH); + + return ret; +} + +static void hyper_dmabuf_ops_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attach->dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)attach->dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); +} + +static struct sg_table *hyper_dmabuf_ops_map( + struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct sg_table *st; + struct imported_sgt_info *imported; + struct pages_info *pg_info; + int ret; + + if (!attachment->dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)attachment->dmabuf->priv; + + /* extract pages from sgt */ + pg_info = hyper_dmabuf_ext_pgs(imported->sgt); + + if (!pg_info) + return NULL; + + /* create a new sg_table with extracted pages */ + st = hyper_dmabuf_create_sgt(pg_info->pgs, pg_info->frst_ofst, + pg_info->last_len, pg_info->nents); + if (!st) + goto err_free_sg; + + if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) + goto err_free_sg; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_MAP); + + kfree(pg_info->pgs); + kfree(pg_info); + + return st; + +err_free_sg: + if (st) { + sg_free_table(st); + kfree(st); + } + + kfree(pg_info->pgs); + kfree(pg_info); + + return NULL; +} + +static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sg, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attachment->dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)attachment->dmabuf->priv; + + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + + sg_free_table(sg); + kfree(sg); + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); +} + +static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) +{ + struct imported_sgt_info *imported; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret; + int finish; + + if (!dma_buf->priv) + return; + + imported = (struct imported_sgt_info *)dma_buf->priv; + + if (!dmabuf_refcount(imported->dma_buf)) + imported->dma_buf = NULL; + + imported->importers--; + + if (imported->importers == 0) { + bknd_ops->unmap_shared_pages(&imported->refs_info, + imported->nents); + + if (imported->sgt) { + sg_free_table(imported->sgt); + kfree(imported->sgt); + imported->sgt = NULL; + } + } + + finish = imported && !imported->valid && + !imported->importers; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); + + /* + * Check if buffer is still valid and if not remove it + * from imported list. That has to be done after sending + * sync request + */ + if (finish) { + hyper_dmabuf_remove_imported(imported->hid); + kfree(imported->priv); + kfree(imported); + } +} + +static int hyper_dmabuf_ops_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS); + + return ret; +} + +static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); + + return 0; +} + +static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); + + /* TODO: NULL for now. Need to return the addr of mapped region */ + return NULL; +} + +static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum, void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); +} + +static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); + + /* for now NULL.. need to return the address of mapped region */ + return NULL; +} + +static void hyper_dmabuf_ops_kunmap(struct dma_buf *dmabuf, unsigned long pgnum, + void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); +} + +static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, + struct vm_area_struct *vma) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_MMAP); + + return ret; +} + +static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); + + return NULL; +} + +static void hyper_dmabuf_ops_vunmap(struct dma_buf *dmabuf, void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); +} + +static const struct dma_buf_ops hyper_dmabuf_ops = { + .attach = hyper_dmabuf_ops_attach, + .detach = hyper_dmabuf_ops_detach, + .map_dma_buf = hyper_dmabuf_ops_map, + .unmap_dma_buf = hyper_dmabuf_ops_unmap, + .release = hyper_dmabuf_ops_release, + .begin_cpu_access = (void *)hyper_dmabuf_ops_begin_cpu_access, + .end_cpu_access = (void *)hyper_dmabuf_ops_end_cpu_access, + .map_atomic = hyper_dmabuf_ops_kmap_atomic, + .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, + .map = hyper_dmabuf_ops_kmap, + .unmap = hyper_dmabuf_ops_kunmap, + .mmap = hyper_dmabuf_ops_mmap, + .vmap = hyper_dmabuf_ops_vmap, + .vunmap = hyper_dmabuf_ops_vunmap, +}; + +/* exporting dmabuf as fd */ +int hyper_dmabuf_export_fd(struct imported_sgt_info *imported, int flags) +{ + int fd = -1; + + /* call hyper_dmabuf_export_dmabuf and create + * and bind a handle for it then release + */ + hyper_dmabuf_export_dma_buf(imported); + + if (imported->dma_buf) + fd = dma_buf_fd(imported->dma_buf, flags); + + return fd; +} + +void hyper_dmabuf_export_dma_buf(struct imported_sgt_info *imported) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &hyper_dmabuf_ops; + + /* multiple of PAGE_SIZE, not considering offset */ + exp_info.size = imported->sgt->nents * PAGE_SIZE; + exp_info.flags = /* not sure about flag */ 0; + exp_info.priv = imported; + + imported->dma_buf = dma_buf_export(&exp_info); +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h new file mode 100644 index 000000000000..c5505a41f0fe --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_OPS_H__ +#define __HYPER_DMABUF_OPS_H__ + +int hyper_dmabuf_export_fd(struct imported_sgt_info *imported, int flags); + +void hyper_dmabuf_export_dma_buf(struct imported_sgt_info *imported); + +#endif /* __HYPER_DMABUF_IMP_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c new file mode 100644 index 000000000000..1f2f56b1162d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c @@ -0,0 +1,172 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_id.h" + +#define HYPER_DMABUF_SIZE(nents, first_offset, last_len) \ + ((nents)*PAGE_SIZE - (first_offset) - PAGE_SIZE + (last_len)) + +int hyper_dmabuf_query_exported(struct exported_sgt_info *exported, + int query, unsigned long *info) +{ + switch (query) { + case HYPER_DMABUF_QUERY_TYPE: + *info = EXPORTED; + break; + + /* exporting domain of this specific dmabuf*/ + case HYPER_DMABUF_QUERY_EXPORTER: + *info = HYPER_DMABUF_DOM_ID(exported->hid); + break; + + /* importing domain of this specific dmabuf */ + case HYPER_DMABUF_QUERY_IMPORTER: + *info = exported->rdomid; + break; + + /* size of dmabuf in byte */ + case HYPER_DMABUF_QUERY_SIZE: + *info = exported->dma_buf->size; + break; + + /* whether the buffer is used by importer */ + case HYPER_DMABUF_QUERY_BUSY: + *info = (exported->active > 0); + break; + + /* whether the buffer is unexported */ + case HYPER_DMABUF_QUERY_UNEXPORTED: + *info = !exported->valid; + break; + + /* whether the buffer is scheduled to be unexported */ + case HYPER_DMABUF_QUERY_DELAYED_UNEXPORTED: + *info = !exported->unexport_sched; + break; + + /* size of private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO_SIZE: + *info = exported->sz_priv; + break; + + /* copy private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO: + if (exported->sz_priv > 0) { + int n; + + n = copy_to_user((void __user *) *info, + exported->priv, + exported->sz_priv); + if (n != 0) + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + + +int hyper_dmabuf_query_imported(struct imported_sgt_info *imported, + int query, unsigned long *info) +{ + switch (query) { + case HYPER_DMABUF_QUERY_TYPE: + *info = IMPORTED; + break; + + /* exporting domain of this specific dmabuf*/ + case HYPER_DMABUF_QUERY_EXPORTER: + *info = HYPER_DMABUF_DOM_ID(imported->hid); + break; + + /* importing domain of this specific dmabuf */ + case HYPER_DMABUF_QUERY_IMPORTER: + *info = hy_drv_priv->domid; + break; + + /* size of dmabuf in byte */ + case HYPER_DMABUF_QUERY_SIZE: + if (imported->dma_buf) { + /* if local dma_buf is created (if it's + * ever mapped), retrieve it directly + * from struct dma_buf * + */ + *info = imported->dma_buf->size; + } else { + /* calcuate it from given nents, frst_ofst + * and last_len + */ + *info = HYPER_DMABUF_SIZE(imported->nents, + imported->frst_ofst, + imported->last_len); + } + break; + + /* whether the buffer is used or not */ + case HYPER_DMABUF_QUERY_BUSY: + /* checks if it's used by importer */ + *info = (imported->importers > 0); + break; + + /* whether the buffer is unexported */ + case HYPER_DMABUF_QUERY_UNEXPORTED: + *info = !imported->valid; + break; + + /* size of private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO_SIZE: + *info = imported->sz_priv; + break; + + /* copy private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO: + if (imported->sz_priv > 0) { + int n; + + n = copy_to_user((void __user *)*info, + imported->priv, + imported->sz_priv); + if (n != 0) + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h new file mode 100644 index 000000000000..65ae738f8f53 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h @@ -0,0 +1,10 @@ +#ifndef __HYPER_DMABUF_QUERY_H__ +#define __HYPER_DMABUF_QUERY_H__ + +int hyper_dmabuf_query_imported(struct imported_sgt_info *imported, + int query, unsigned long *info); + +int hyper_dmabuf_query_exported(struct exported_sgt_info *exported, + int query, unsigned long *info); + +#endif // __HYPER_DMABUF_QUERY_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c new file mode 100644 index 000000000000..a82fd7b087b8 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c @@ -0,0 +1,322 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_sgl_proc.h" + +/* Whenever importer does dma operations from remote domain, + * a notification is sent to the exporter so that exporter + * issues equivalent dma operation on the original dma buf + * for indirect synchronization via shadow operations. + * + * All ptrs and references (e.g struct sg_table*, + * struct dma_buf_attachment) created via these operations on + * exporter's side are kept in stack (implemented as circular + * linked-lists) separately so that those can be re-referenced + * later when unmapping operations are invoked to free those. + * + * The very first element on the bottom of each stack holds + * is what is created when initial exporting is issued so it + * should not be modified or released by this fuction. + */ +int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) +{ + struct exported_sgt_info *exported; + struct sgt_list *sgtl; + struct attachment_list *attachl; + struct kmap_vaddr_list *va_kmapl; + struct vmap_vaddr_list *va_vmapl; + int ret; + + /* find a coresponding SGT for the id */ + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "dmabuf remote sync::can't find exported list\n"); + return -ENOENT; + } + + switch (ops) { + case HYPER_DMABUF_OPS_ATTACH: + attachl = kcalloc(1, sizeof(*attachl), GFP_KERNEL); + + if (!attachl) + return -ENOMEM; + + attachl->attach = dma_buf_attach(exported->dma_buf, + hy_drv_priv->dev); + + if (!attachl->attach) { + kfree(attachl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_ATTACH\n"); + return -ENOMEM; + } + + list_add(&attachl->list, &exported->active_attached->list); + break; + + case HYPER_DMABUF_OPS_DETACH: + if (list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_DETACH\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf attachment left to be detached\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + dma_buf_detach(exported->dma_buf, attachl->attach); + list_del(&attachl->list); + kfree(attachl); + break; + + case HYPER_DMABUF_OPS_MAP: + if (list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_MAP\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf attachment left to be mapped\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + sgtl = kcalloc(1, sizeof(*sgtl), GFP_KERNEL); + + if (!sgtl) + return -ENOMEM; + + sgtl->sgt = dma_buf_map_attachment(attachl->attach, + DMA_BIDIRECTIONAL); + if (!sgtl->sgt) { + kfree(sgtl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_MAP\n"); + return -ENOMEM; + } + list_add(&sgtl->list, &exported->active_sgts->list); + break; + + case HYPER_DMABUF_OPS_UNMAP: + if (list_empty(&exported->active_sgts->list) || + list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_UNMAP\n"); + dev_err(hy_drv_priv->dev, + "no SGT or attach left to be unmapped\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + sgtl = list_first_entry(&exported->active_sgts->list, + struct sgt_list, list); + + dma_buf_unmap_attachment(attachl->attach, sgtl->sgt, + DMA_BIDIRECTIONAL); + list_del(&sgtl->list); + kfree(sgtl); + break; + + case HYPER_DMABUF_OPS_RELEASE: + dev_dbg(hy_drv_priv->dev, + "id:%d key:%d %d %d} released, ref left: %d\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2], + exported->active - 1); + + exported->active--; + + /* If there are still importers just break, if no then + * continue with final cleanup + */ + if (exported->active) + break; + + /* Importer just released buffer fd, check if there is + * any other importer still using it. + * If not and buffer was unexported, clean up shared + * data and remove that buffer. + */ + dev_dbg(hy_drv_priv->dev, + "Buffer {id:%d key:%d %d %d} final released\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + if (!exported->valid && !exported->active && + !exported->unexport_sched) { + hyper_dmabuf_cleanup_sgt_info(exported, false); + hyper_dmabuf_remove_exported(hid); + kfree(exported); + /* store hyper_dmabuf_id in the list for reuse */ + hyper_dmabuf_store_hid(hid); + } + + break; + + case HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS: + ret = dma_buf_begin_cpu_access(exported->dma_buf, + DMA_BIDIRECTIONAL); + if (ret) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS\n"); + return ret; + } + break; + + case HYPER_DMABUF_OPS_END_CPU_ACCESS: + ret = dma_buf_end_cpu_access(exported->dma_buf, + DMA_BIDIRECTIONAL); + if (ret) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_END_CPU_ACCESS\n"); + return ret; + } + break; + + case HYPER_DMABUF_OPS_KMAP_ATOMIC: + case HYPER_DMABUF_OPS_KMAP: + va_kmapl = kcalloc(1, sizeof(*va_kmapl), GFP_KERNEL); + if (!va_kmapl) + return -ENOMEM; + + /* dummy kmapping of 1 page */ + if (ops == HYPER_DMABUF_OPS_KMAP_ATOMIC) + va_kmapl->vaddr = dma_buf_kmap_atomic( + exported->dma_buf, 1); + else + va_kmapl->vaddr = dma_buf_kmap( + exported->dma_buf, 1); + + if (!va_kmapl->vaddr) { + kfree(va_kmapl); + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KMAP(_ATOMIC)\n"); + return -ENOMEM; + } + list_add(&va_kmapl->list, &exported->va_kmapped->list); + break; + + case HYPER_DMABUF_OPS_KUNMAP_ATOMIC: + case HYPER_DMABUF_OPS_KUNMAP: + if (list_empty(&exported->va_kmapped->list)) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KUNMAP(_ATOMIC)\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf VA to be freed\n"); + return -EFAULT; + } + + va_kmapl = list_first_entry(&exported->va_kmapped->list, + struct kmap_vaddr_list, list); + if (!va_kmapl->vaddr) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KUNMAP(_ATOMIC)\n"); + return PTR_ERR(va_kmapl->vaddr); + } + + /* unmapping 1 page */ + if (ops == HYPER_DMABUF_OPS_KUNMAP_ATOMIC) + dma_buf_kunmap_atomic(exported->dma_buf, + 1, va_kmapl->vaddr); + else + dma_buf_kunmap(exported->dma_buf, + 1, va_kmapl->vaddr); + + list_del(&va_kmapl->list); + kfree(va_kmapl); + break; + + case HYPER_DMABUF_OPS_MMAP: + /* currently not supported: looking for a way to create + * a dummy vma + */ + dev_warn(hy_drv_priv->dev, + "remote sync::sychronized mmap is not supported\n"); + break; + + case HYPER_DMABUF_OPS_VMAP: + va_vmapl = kcalloc(1, sizeof(*va_vmapl), GFP_KERNEL); + + if (!va_vmapl) + return -ENOMEM; + + /* dummy vmapping */ + va_vmapl->vaddr = dma_buf_vmap(exported->dma_buf); + + if (!va_vmapl->vaddr) { + kfree(va_vmapl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VMAP\n"); + return -ENOMEM; + } + list_add(&va_vmapl->list, &exported->va_vmapped->list); + break; + + case HYPER_DMABUF_OPS_VUNMAP: + if (list_empty(&exported->va_vmapped->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VUNMAP\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf VA to be freed\n"); + return -EFAULT; + } + va_vmapl = list_first_entry(&exported->va_vmapped->list, + struct vmap_vaddr_list, list); + if (!va_vmapl || va_vmapl->vaddr == NULL) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VUNMAP\n"); + return -EFAULT; + } + + dma_buf_vunmap(exported->dma_buf, va_vmapl->vaddr); + + list_del(&va_vmapl->list); + kfree(va_vmapl); + break; + + default: + /* program should not get here */ + break; + } + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h new file mode 100644 index 000000000000..366389287f4e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h @@ -0,0 +1,30 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_REMOTE_SYNC_H__ +#define __HYPER_DMABUF_REMOTE_SYNC_H__ + +int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops); + +#endif // __HYPER_DMABUF_REMOTE_SYNC_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c new file mode 100644 index 000000000000..c1887d1ad709 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c @@ -0,0 +1,261 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_sgl_proc.h" + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +/* return total number of pages referenced by a sgt + * for pre-calculation of # of pages behind a given sgt + */ +static int get_num_pgs(struct sg_table *sgt) +{ + struct scatterlist *sgl; + int length, i; + /* at least one page */ + int num_pages = 1; + + sgl = sgt->sgl; + + length = sgl->length - PAGE_SIZE + sgl->offset; + + /* round-up */ + num_pages += ((length + PAGE_SIZE - 1)/PAGE_SIZE); + + for (i = 1; i < sgt->nents; i++) { + sgl = sg_next(sgl); + + /* round-up */ + num_pages += ((sgl->length + PAGE_SIZE - 1) / + PAGE_SIZE); /* round-up */ + } + + return num_pages; +} + +/* extract pages directly from struct sg_table */ +struct pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt) +{ + struct pages_info *pg_info; + int i, j, k; + int length; + struct scatterlist *sgl; + + pg_info = kmalloc(sizeof(*pg_info), GFP_KERNEL); + if (!pg_info) + return NULL; + + pg_info->pgs = kmalloc_array(get_num_pgs(sgt), + sizeof(struct page *), + GFP_KERNEL); + + if (!pg_info->pgs) { + kfree(pg_info); + return NULL; + } + + sgl = sgt->sgl; + + pg_info->nents = 1; + pg_info->frst_ofst = sgl->offset; + pg_info->pgs[0] = sg_page(sgl); + length = sgl->length - PAGE_SIZE + sgl->offset; + i = 1; + + while (length > 0) { + pg_info->pgs[i] = nth_page(sg_page(sgl), i); + length -= PAGE_SIZE; + pg_info->nents++; + i++; + } + + for (j = 1; j < sgt->nents; j++) { + sgl = sg_next(sgl); + pg_info->pgs[i++] = sg_page(sgl); + length = sgl->length - PAGE_SIZE; + pg_info->nents++; + k = 1; + + while (length > 0) { + pg_info->pgs[i++] = nth_page(sg_page(sgl), k++); + length -= PAGE_SIZE; + pg_info->nents++; + } + } + + /* + * lenght at that point will be 0 or negative, + * so to calculate last page size just add it to PAGE_SIZE + */ + pg_info->last_len = PAGE_SIZE + length; + + return pg_info; +} + +/* create sg_table with given pages and other parameters */ +struct sg_table *hyper_dmabuf_create_sgt(struct page **pgs, + int frst_ofst, int last_len, + int nents) +{ + struct sg_table *sgt; + struct scatterlist *sgl; + int i, ret; + + sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!sgt) + return NULL; + + ret = sg_alloc_table(sgt, nents, GFP_KERNEL); + if (ret) { + if (sgt) { + sg_free_table(sgt); + kfree(sgt); + } + + return NULL; + } + + sgl = sgt->sgl; + + sg_set_page(sgl, pgs[0], PAGE_SIZE-frst_ofst, frst_ofst); + + for (i = 1; i < nents-1; i++) { + sgl = sg_next(sgl); + sg_set_page(sgl, pgs[i], PAGE_SIZE, 0); + } + + if (nents > 1) /* more than one page */ { + sgl = sg_next(sgl); + sg_set_page(sgl, pgs[i], last_len, 0); + } + + return sgt; +} + +int hyper_dmabuf_cleanup_sgt_info(struct exported_sgt_info *exported, + int force) +{ + struct sgt_list *sgtl; + struct attachment_list *attachl; + struct kmap_vaddr_list *va_kmapl; + struct vmap_vaddr_list *va_vmapl; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + + if (!exported) { + dev_err(hy_drv_priv->dev, "invalid hyper_dmabuf_id\n"); + return -EINVAL; + } + + /* if force != 1, sgt_info can be released only if + * there's no activity on exported dma-buf on importer + * side. + */ + if (!force && + exported->active) { + dev_warn(hy_drv_priv->dev, + "dma-buf is used by importer\n"); + + return -EPERM; + } + + /* force == 1 is not recommended */ + while (!list_empty(&exported->va_kmapped->list)) { + va_kmapl = list_first_entry(&exported->va_kmapped->list, + struct kmap_vaddr_list, list); + + dma_buf_kunmap(exported->dma_buf, 1, va_kmapl->vaddr); + list_del(&va_kmapl->list); + kfree(va_kmapl); + } + + while (!list_empty(&exported->va_vmapped->list)) { + va_vmapl = list_first_entry(&exported->va_vmapped->list, + struct vmap_vaddr_list, list); + + dma_buf_vunmap(exported->dma_buf, va_vmapl->vaddr); + list_del(&va_vmapl->list); + kfree(va_vmapl); + } + + while (!list_empty(&exported->active_sgts->list)) { + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + sgtl = list_first_entry(&exported->active_sgts->list, + struct sgt_list, list); + + dma_buf_unmap_attachment(attachl->attach, sgtl->sgt, + DMA_BIDIRECTIONAL); + list_del(&sgtl->list); + kfree(sgtl); + } + + while (!list_empty(&exported->active_sgts->list)) { + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + dma_buf_detach(exported->dma_buf, attachl->attach); + list_del(&attachl->list); + kfree(attachl); + } + + /* Start cleanup of buffer in reverse order to exporting */ + bknd_ops->unshare_pages(&exported->refs_info, exported->nents); + + /* unmap dma-buf */ + dma_buf_unmap_attachment(exported->active_attached->attach, + exported->active_sgts->sgt, + DMA_BIDIRECTIONAL); + + /* detatch dma-buf */ + dma_buf_detach(exported->dma_buf, exported->active_attached->attach); + + /* close connection to dma-buf completely */ + dma_buf_put(exported->dma_buf); + exported->dma_buf = NULL; + + kfree(exported->active_sgts); + kfree(exported->active_attached); + kfree(exported->va_kmapped); + kfree(exported->va_vmapped); + kfree(exported->priv); + + exported->active_sgts = NULL; + exported->active_attached = NULL; + exported->va_kmapped = NULL; + exported->va_vmapped = NULL; + exported->priv = NULL; + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h new file mode 100644 index 000000000000..869d98204e03 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_IMP_H__ +#define __HYPER_DMABUF_IMP_H__ + +/* extract pages directly from struct sg_table */ +struct pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt); + +/* create sg_table with given pages and other parameters */ +struct sg_table *hyper_dmabuf_create_sgt(struct page **pgs, + int frst_ofst, int last_len, + int nents); + +int hyper_dmabuf_cleanup_sgt_info(struct exported_sgt_info *exported, + int force); + +void hyper_dmabuf_free_sgt(struct sg_table *sgt); + +#endif /* __HYPER_DMABUF_IMP_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h new file mode 100644 index 000000000000..a11f804edfb3 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h @@ -0,0 +1,141 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_STRUCT_H__ +#define __HYPER_DMABUF_STRUCT_H__ + +/* stack of mapped sgts */ +struct sgt_list { + struct sg_table *sgt; + struct list_head list; +}; + +/* stack of attachments */ +struct attachment_list { + struct dma_buf_attachment *attach; + struct list_head list; +}; + +/* stack of vaddr mapped via kmap */ +struct kmap_vaddr_list { + void *vaddr; + struct list_head list; +}; + +/* stack of vaddr mapped via vmap */ +struct vmap_vaddr_list { + void *vaddr; + struct list_head list; +}; + +/* Exporter builds pages_info before sharing pages */ +struct pages_info { + int frst_ofst; + int last_len; + int nents; + struct page **pgs; +}; + + +/* Exporter stores references to sgt in a hash table + * Exporter keeps these references for synchronization + * and tracking purposes + */ +struct exported_sgt_info { + hyper_dmabuf_id_t hid; + + /* VM ID of importer */ + int rdomid; + + struct dma_buf *dma_buf; + int nents; + + /* list for tracking activities on dma_buf */ + struct sgt_list *active_sgts; + struct attachment_list *active_attached; + struct kmap_vaddr_list *va_kmapped; + struct vmap_vaddr_list *va_vmapped; + + /* set to 0 when unexported. Importer doesn't + * do a new mapping of buffer if valid == false + */ + bool valid; + + /* active == true if the buffer is actively used + * (mapped) by importer + */ + int active; + + /* hypervisor specific reference data for shared pages */ + void *refs_info; + + struct delayed_work unexport; + bool unexport_sched; + + /* list for file pointers associated with all user space + * application that have exported this same buffer to + * another VM. This needs to be tracked to know whether + * the buffer can be completely freed. + */ + struct file *filp; + + /* size of private */ + size_t sz_priv; + + /* private data associated with the exported buffer */ + char *priv; +}; + +/* imported_sgt_info contains information about imported DMA_BUF + * this info is kept in IMPORT list and asynchorously retrieved and + * used to map DMA_BUF on importer VM's side upon export fd ioctl + * request from user-space + */ + +struct imported_sgt_info { + hyper_dmabuf_id_t hid; /* unique id for shared dmabuf imported */ + + /* hypervisor-specific handle to pages */ + int ref_handle; + + /* offset and size info of DMA_BUF */ + int frst_ofst; + int last_len; + int nents; + + struct dma_buf *dma_buf; + struct sg_table *sgt; + + void *refs_info; + bool valid; + int importers; + + /* size of private */ + size_t sz_priv; + + /* private data associated with the exported buffer */ + char *priv; +}; + +#endif /* __HYPER_DMABUF_STRUCT_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c new file mode 100644 index 000000000000..bb16360d06d5 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -0,0 +1,505 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_msg.h" +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_fe_list.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_comm_ring.h" + +/* + * Identifies which queue is used for TX and RX + * Note: it is opposite regarding to frontent definition + */ +enum virio_queue_type { + HDMA_VIRTIO_RX_QUEUE = 0, + HDMA_VIRTIO_TX_QUEUE, + HDMA_VIRTIO_QUEUE_MAX +}; + +/* Data required for sending TX messages using virtqueues*/ +struct virtio_be_tx_data { + struct iovec tx_iov; + uint16_t tx_idx; +}; + +struct virtio_be_priv { + struct virtio_dev_info dev; + struct virtio_vq_info vqs[HDMA_VIRTIO_QUEUE_MAX]; + bool busy; + struct hyper_dmabuf_req *pending_tx_req; + struct virtio_comm_ring tx_ring; + struct mutex lock; +}; + + +/* + * Received response to TX request, + * or empty buffer to be used for TX requests in future + */ +static void virtio_be_handle_tx_kick(struct virtio_vq_info *vq, + struct virtio_fe_info *fe_info) +{ + struct virtio_be_priv *priv = fe_info->priv; + /* Fill last used buffer with received buffer details */ + struct virtio_be_tx_data *tx_data = + (struct virtio_be_tx_data *) + virtio_comm_ring_pop(&priv->tx_ring); + + virtio_vq_getchain(vq, &tx_data->tx_idx, &tx_data->tx_iov, 1, NULL); + + /* Copy response if request was synchronous */ + if (priv->busy) { + memcpy(priv->pending_tx_req, + tx_data->tx_iov.iov_base, + tx_data->tx_iov.iov_len); + priv->busy = false; + } +} + +/* + * Received request from frontend + */ +static void virtio_be_handle_rx_kick(struct virtio_vq_info *vq, + struct virtio_fe_info *fe_info) +{ + struct iovec iov; + uint16_t idx; + struct hyper_dmabuf_req *req = NULL; + int len; + int ret; + + /* Make sure we will process all pending requests */ + while (virtio_vq_has_descs(vq)) { + virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + + if (iov.iov_len != sizeof(struct hyper_dmabuf_req)) { + /* HACK: if int size buffer was provided, + * treat that as request to get frontend vmid */ + if (iov.iov_len == sizeof(int)) { + *((int *)iov.iov_base) = fe_info->vmid; + len = iov.iov_len; + } else { + len = 0; + dev_warn(hy_drv_priv->dev, + "received request with wrong size"); + dev_warn(hy_drv_priv->dev, + "%zu != %zu\n", + iov.iov_len, + sizeof(struct hyper_dmabuf_req)); + } + + virtio_vq_relchain(vq, idx, len); + continue; + } + + req = (struct hyper_dmabuf_req *)iov.iov_base; + + ret = hyper_dmabuf_msg_parse(1, req); + + len = iov.iov_len; + + virtio_vq_relchain(vq, idx, len); + } + virtio_vq_endchains(vq, 1); +} + +/* + * Check in what virtqueue we received buffer and process it accordingly. + */ +static void virtio_be_handle_vq_kick( + int vq_idx, struct virtio_fe_info *fe_info) +{ + struct virtio_vq_info *vq; + + vq = &fe_info->priv->vqs[vq_idx]; + + if (vq_idx == HDMA_VIRTIO_RX_QUEUE) + virtio_be_handle_rx_kick(vq, fe_info); + else + virtio_be_handle_tx_kick(vq, fe_info); +} + +/* + * Received new buffer in virtqueue + */ +static int virtio_be_handle_kick(int client_id, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + struct virtio_fe_info *fe_info; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + fe_info = virtio_fe_find(client_id); + if (fe_info == NULL) { + dev_warn(hy_drv_priv->dev, "Client %d not found\n", client_id); + return -EINVAL; + } + + for (i = 0; i < fe_info->max_vcpu; ++i) { + req = &fe_info->req_buf[i]; + if (req->valid && + req->processed == REQ_STATE_PROCESSING && + req->client == fe_info->client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) + req->reqs.pio_request.value = 0; + else + val = req->reqs.pio_request.value; + + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(fe_info->client_id, i); + } + } + + if (val >= 0) + virtio_be_handle_vq_kick(val, fe_info); + + return 0; +} + +/* + * New frontend is connecting to backend. + * Creates virtqueues for it and registers internally. + */ +static int virtio_be_register_vhm_client(struct virtio_dev_info *d) +{ + unsigned int vmid; + struct vm_info info; + struct virtio_fe_info *fe_info; + int ret; + + fe_info = kcalloc(1, sizeof(*fe_info), GFP_KERNEL); + if (fe_info == NULL) + return -ENOMEM; + + fe_info->priv = + container_of(d, struct virtio_be_priv, dev); + vmid = d->_ctx.vmid; + fe_info->vmid = vmid; + + dev_dbg(hy_drv_priv->dev, + "Virtio frontend from vm %d connected\n", vmid); + + fe_info->client_id = + acrn_ioreq_create_client(vmid, + virtio_be_handle_kick, + "hyper dmabuf kick"); + if (fe_info->client_id < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create client of ACRN ioreq\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to add iorange to acrn ioreq\n"); + goto err; + } + + ret = vhm_get_vm_info(vmid, &info); + if (ret < 0) { + acrn_ioreq_del_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + dev_err(hy_drv_priv->dev, "Failed in vhm_get_vm_info\n"); + goto err; + } + + fe_info->max_vcpu = info.max_vcpu; + + fe_info->req_buf = acrn_ioreq_get_reqbuf(fe_info->client_id); + if (fe_info->req_buf == NULL) { + acrn_ioreq_del_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + dev_err(hy_drv_priv->dev, "Failed in acrn_ioreq_get_reqbuf\n"); + goto err; + } + + acrn_ioreq_attach_client(fe_info->client_id, 0); + + virtio_fe_add(fe_info); + + return 0; + +err: + acrn_ioreq_destroy_client(fe_info->client_id); + kfree(fe_info); + + return -EINVAL; +} + +/* + * DM is opening our VBS interface to create new frontend instance. + */ +static int vbs_k_open(struct inode *inode, struct file *f) +{ + struct virtio_be_priv *priv; + struct virtio_dev_info *dev; + struct virtio_vq_info *vqs; + int i; + + priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + vqs = &priv->vqs[0]; + + dev = &priv->dev; + + for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) { + vqs[i].dev = dev; + vqs[i].vq_notify = NULL; + } + dev->vqs = vqs; + + virtio_dev_init(dev, vqs, HDMA_VIRTIO_QUEUE_MAX); + + priv->pending_tx_req = + kcalloc(1, sizeof(struct hyper_dmabuf_req), GFP_KERNEL); + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct virtio_be_tx_data), + REQ_RING_SIZE); + + mutex_init(&priv->lock); + + f->private_data = priv; + + return 0; +} + +static int vbs_k_release(struct inode *inode, struct file *f) +{ + struct virtio_be_priv *priv = + (struct virtio_be_priv *) f->private_data; + int i; + +// virtio_dev_stop(&priv->dev); +// virtio_dev_cleanup(&priv->dev, false); + + for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) + virtio_vq_reset(&priv->vqs[i]); + + kfree(priv->pending_tx_req); + virtio_comm_ring_free(&priv->tx_ring); + kfree(priv); + return 0; +} + +static long vbs_k_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct virtio_be_priv *priv = + (struct virtio_be_priv *) f->private_data; + void __user *argp = (void __user *)arg; + int r; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "No backend private data\n"); + + return -EINVAL; + } + + if (ioctl == VBS_SET_VQ) { + /* Overridden to call additionally + * virtio_be_register_vhm_client */ + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + return -EFAULT; + + if (virtio_be_register_vhm_client(&priv->dev) < 0) + return -EFAULT; + } else { + r = virtio_dev_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + } + + return r; +} + +static const struct file_operations vbs_hyper_dmabuf_fops = { + .owner = THIS_MODULE, + .open = vbs_k_open, + .release = vbs_k_release, + .unlocked_ioctl = vbs_k_ioctl, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_hyper_dmabuf_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_hyper_dmabuf", + .fops = &vbs_hyper_dmabuf_fops, +}; + +static int virtio_be_register(void) +{ + return misc_register(&vbs_hyper_dmabuf_misc); +} + +static void virtio_be_unregister(void) +{ + misc_deregister(&vbs_hyper_dmabuf_misc); +} + +/* + * ACRN SOS will always has vmid 0 + * TODO: check if that always will be true + */ +static int virtio_be_get_vmid(void) +{ + return 0; +} + +static int virtio_be_send_req(int vmid, struct hyper_dmabuf_req *req, + int wait) +{ + int timeout = 1000; + struct virtio_fe_info *fe_info; + struct virtio_be_priv *priv; + struct virtio_be_tx_data *tx_data; + struct virtio_vq_info *vq; + int len; + + fe_info = virtio_fe_find_by_vmid(vmid); + + if (fe_info == NULL) { + dev_err(hy_drv_priv->dev, + "No frontend registered for vmid %d\n", vmid); + return -ENOENT; + } + + priv = fe_info->priv; + + mutex_lock(&priv->lock); + + /* Check if we have any free buffers for sending new request */ + while (virtio_comm_ring_full(&priv->tx_ring) && + timeout--) { + usleep_range(100, 120); + } + + if (timeout <= 0) { + dev_warn(hy_drv_priv->dev, "Requests ring full\n"); + return -EBUSY; + } + + /* Get free buffer for sending request from ring */ + tx_data = (struct virtio_be_tx_data *) + virtio_comm_ring_push(&priv->tx_ring); + + vq = &priv->vqs[HDMA_VIRTIO_TX_QUEUE]; + + if (tx_data->tx_iov.iov_len != sizeof(struct hyper_dmabuf_req)) { + dev_warn(hy_drv_priv->dev, + "received request with wrong size\n"); + virtio_vq_relchain(vq, tx_data->tx_idx, 0); + mutex_unlock(&priv->lock); + return -EINVAL; + } + + req->req_id = hyper_dmabuf_virtio_get_next_req_id(); + + /* Copy request data to virtqueue buffer */ + memcpy(tx_data->tx_iov.iov_base, req, sizeof(*req)); + len = tx_data->tx_iov.iov_len; + + /* update req_pending with current request */ + if (wait) { + priv->busy = true; + memcpy(priv->pending_tx_req, req, sizeof(*req)); + } + + virtio_vq_relchain(vq, tx_data->tx_idx, len); + + virtio_vq_endchains(vq, 1); + + if (wait) { + while (timeout--) { + if (priv->pending_tx_req->stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + mutex_unlock(&priv->lock); + dev_err(hy_drv_priv->dev, "request timed-out\n"); + return -EBUSY; + } + } + + mutex_unlock(&priv->lock); + return 0; +}; + +struct hyper_dmabuf_bknd_ops virtio_bknd_ops = { + .init = virtio_be_register, + .cleanup = virtio_be_unregister, + .get_vm_id = virtio_be_get_vmid, + .share_pages = virtio_share_pages, + .unshare_pages = virtio_unshare_pages, + .map_shared_pages = virtio_map_shared_pages, + .unmap_shared_pages = virtio_unmap_shared_pages, + .init_comm_env = NULL, + .destroy_comm = NULL, + .init_rx_ch = NULL, + .init_tx_ch = NULL, + .send_req = virtio_be_send_req, +}; + + +MODULE_DESCRIPTION("Hyper dmabuf virtio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c new file mode 100644 index 000000000000..d73bcbcc8e87 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c @@ -0,0 +1,89 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include "hyper_dmabuf_virtio_comm_ring.h" + +int virtio_comm_ring_init(struct virtio_comm_ring *ring, + int entry_size, + int num_entries) +{ + ring->data = kcalloc(num_entries, entry_size, GFP_KERNEL); + + if (!ring->data) + return -ENOMEM; + + ring->head = 0; + ring->tail = 0; + ring->used = 0; + ring->num_entries = num_entries; + ring->entry_size = entry_size; + + return 0; +} + +void virtio_comm_ring_free(struct virtio_comm_ring *ring) +{ + kfree(ring->data); + ring->data = NULL; +} + +bool virtio_comm_ring_full(struct virtio_comm_ring *ring) +{ + if (ring->used == ring->num_entries) + return true; + + return false; +} + +void *virtio_comm_ring_push(struct virtio_comm_ring *ring) +{ + int old_head; + + if (virtio_comm_ring_full(ring)) + return NULL; + + old_head = ring->head; + + ring->head++; + ring->head %= ring->num_entries; + ring->used++; + + return ring->data + (ring->entry_size * old_head); +} + +void *virtio_comm_ring_pop(struct virtio_comm_ring *ring) +{ + int old_tail = ring->tail; + + ring->tail++; + ring->tail %= ring->num_entries; + ring->used--; + + return ring->data + (ring->entry_size * old_tail); +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h new file mode 100644 index 000000000000..a95a63af2ba0 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_COMM_RING_H__ +#define __HYPER_DMABUF_VIRTIO_COMM_RING_H__ + +/* Generic ring buffer */ +struct virtio_comm_ring { + /* Buffer allocated for keeping ring entries */ + void *data; + + /* Index pointing to next free element in ring */ + int head; + + /* Index pointing to last released element in ring */ + int tail; + + /* Total number of elements that ring can contain */ + int num_entries; + + /* Size of single ring element in bytes */ + int entry_size; + + /* Number of currently used elements */ + int used; +}; + +/* Initializes given ring for keeping given a + * number of entries of specific size */ +int virtio_comm_ring_init(struct virtio_comm_ring *ring, + int entry_size, + int num_entries); + +/* Frees buffer used for storing ring entries */ +void virtio_comm_ring_free(struct virtio_comm_ring *ring); + +/* Checks if ring is full */ +bool virtio_comm_ring_full(struct virtio_comm_ring *ring); + +/* Gets next free element from ring and marks it as used + * or NULL if ring is full */ +void *virtio_comm_ring_push(struct virtio_comm_ring *ring); + +/* Pops oldest element from ring and marks it as free */ +void *virtio_comm_ring_pop(struct virtio_comm_ring *ring); + +#endif /* __HYPER_DMABUF_VIRTIO_COMM_RING_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c new file mode 100644 index 000000000000..05be74358a74 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c @@ -0,0 +1,35 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include "hyper_dmabuf_virtio_common.h" + +int hyper_dmabuf_virtio_get_next_req_id(void) +{ + static int req_id; + + return req_id++; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h new file mode 100644 index 000000000000..24a652ef54c0 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_COMMON_H__ +#define __HYPER_DMABUF_VIRTIO_COMMON_H__ + +/* + * ACRN uses physicall addresses for memory sharing, + * so size of one page ref will be 64-bits + */ + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(u64)) + +/* Defines size of requests circular buffer */ +#define REQ_RING_SIZE 128 + +extern struct hyper_dmabuf_bknd_ops virtio_bknd_ops; +struct virtio_be_priv; +struct vhm_request; + +/* Entry describing each connected frontend */ +struct virtio_fe_info { + struct virtio_be_priv *priv; + int client_id; + int vmid; + int max_vcpu; + struct vhm_request *req_buf; +}; + +extern struct hyper_dmabuf_private hyper_dmabuf_private; + +int hyper_dmabuf_virtio_get_next_req_id(void); + +#endif /* __HYPER_DMABUF_VIRTIO_COMMON_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c new file mode 100644 index 000000000000..9ae290435d70 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -0,0 +1,385 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_msg.h" +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_comm_ring.h" + +/* + * Identifies which queue is used for TX and RX + * Note: it is opposite regarding to backend definition + */ +enum virio_queue_type { + HDMA_VIRTIO_TX_QUEUE = 0, + HDMA_VIRTIO_RX_QUEUE, + HDMA_VIRTIO_QUEUE_MAX +}; + +struct virtio_hdma_fe_priv { + struct virtqueue *vqs[HDMA_VIRTIO_QUEUE_MAX]; + struct virtio_comm_ring tx_ring; + struct virtio_comm_ring rx_ring; + int vmid; +}; + +/* Assuming there will be one FE instance per VM */ +static struct virtio_hdma_fe_priv *hyper_dmabuf_virtio_fe; + +/* + * Received response for request. + * No need for copying request with updated result, + * as backend is processing original request data directly. + */ +static void virtio_hdma_fe_tx_done(struct virtqueue *vq) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vq->vdev->priv; + int len; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + return; + } + + /* Make sure that all pending responses are processed */ + while (virtqueue_get_buf(vq, &len)) { + if (len == sizeof(struct hyper_dmabuf_req)) { + /* Mark that response was received + * and buffer can be reused */ + virtio_comm_ring_pop(&priv->tx_ring); + } + } +} + +/* + * Sends given data buffer via given virtqueue. + */ +static void virtio_hdma_fe_queue_buffer(struct virtio_hdma_fe_priv *priv, + unsigned int queue_nr, + void *buf, size_t size) +{ + struct scatterlist sg; + + if (queue_nr >= HDMA_VIRTIO_QUEUE_MAX) { + dev_dbg(hy_drv_priv->dev, + "queue_nr exceeding max queue number\n"); + return; + } + + sg_init_one(&sg, buf, size); + + virtqueue_add_inbuf(priv->vqs[queue_nr], &sg, 1, buf, GFP_KERNEL); + + virtqueue_kick(priv->vqs[queue_nr]); +} + +/* + * Handle requests coming from other VMs + */ +static void virtio_hdma_fe_handle_rx(struct virtqueue *vq) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vq->vdev->priv; + struct hyper_dmabuf_req *rx_req; + int size, ret; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + return; + } + + /* Make sure all pending requests will be processed */ + while (virtqueue_get_buf(vq, &size)) { + + /* Get next request from ring */ + rx_req = (struct hyper_dmabuf_req *) + virtio_comm_ring_pop(&priv->rx_ring); + + if (size != sizeof(struct hyper_dmabuf_req)) { + dev_dbg(hy_drv_priv->dev, + "Received malformed request\n"); + } else { + ret = hyper_dmabuf_msg_parse(1, rx_req); + } + + /* Send updated request back to virtqueue as a response.*/ + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_RX_QUEUE, + rx_req, sizeof(*rx_req)); + } +} + +static int virtio_hdma_fe_probe_common(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv; + vq_callback_t *callbacks[] = {virtio_hdma_fe_tx_done, + virtio_hdma_fe_handle_rx}; + static const char *names[] = {"txqueue", "rxqueue"}; + int ret; + + priv = kzalloc(sizeof(struct virtio_hdma_fe_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct hyper_dmabuf_req), + REQ_RING_SIZE); + virtio_comm_ring_init(&priv->rx_ring, + sizeof(struct hyper_dmabuf_req), + REQ_RING_SIZE); + + /* Set vmid to -1 to mark that it is not initialized yet */ + priv->vmid = -1; + + vdev->priv = priv; + + ret = virtio_find_vqs(vdev, HDMA_VIRTIO_QUEUE_MAX, + priv->vqs, callbacks, names, NULL); + if (ret) + goto err; + + hyper_dmabuf_virtio_fe = priv; + + return 0; +err: + virtio_comm_ring_free(&priv->tx_ring); + virtio_comm_ring_free(&priv->rx_ring); + kfree(priv); + return ret; +} + +static void virtio_hdma_fe_remove_common(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "No frontend private data\n"); + + return; + } + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + virtio_comm_ring_free(&priv->tx_ring); + virtio_comm_ring_free(&priv->rx_ring); + kfree(priv); + hyper_dmabuf_virtio_fe = NULL; +} + +static int virtio_hdma_fe_probe(struct virtio_device *vdev) +{ + return virtio_hdma_fe_probe_common(vdev); +} + +static void virtio_hdma_fe_remove(struct virtio_device *vdev) +{ + virtio_hdma_fe_remove_common(vdev); +} + +/* + * Queues empty requests buffers to backend, + * which will be used by it to send requests back to frontend. + */ +static void virtio_hdma_fe_scan(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; + struct hyper_dmabuf_req *rx_req; + int timeout = 1000; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + + return; + } + + /* Send request to query vmid, in ACRN guest instances don't + * know their ids, but host does. Here a small hack is used, + * and buffer of int size is sent to backend, in that case + * backend will fill it with vmid of instance that sent that request + */ + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_TX_QUEUE, + &priv->vmid, sizeof(priv->vmid)); + + while (timeout--) { + if (priv->vmid > 0) + break; + usleep_range(100, 120); + } + + if (timeout < 0) + dev_err(hy_drv_priv->dev, + "Cannot query vmid\n"); + + while (!virtio_comm_ring_full(&priv->rx_ring)) { + rx_req = virtio_comm_ring_push(&priv->rx_ring); + + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_RX_QUEUE, + rx_req, sizeof(*rx_req)); + } +} + +#ifdef CONFIG_PM_SLEEP +static int virtio_hdma_fe_freeze(struct virtio_device *vdev) +{ + virtio_hdma_fe_remove_common(vdev); + return 0; +} + +static int virtio_hdma_fe_restore(struct virtio_device *vdev) +{ + return virtio_hdma_fe_probe_common(vdev); +} +#endif + + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_HYPERDMABUF, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_hdma_fe_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_hdma_fe_probe, + .remove = virtio_hdma_fe_remove, + .scan = virtio_hdma_fe_scan, +#ifdef CONFIG_PM_SLEEP + .freeze = virtio_hdma_fe_freeze, + .restore = virtio_hdma_fe_restore, +#endif +}; + +int virtio_hdma_fe_register(void) +{ + return register_virtio_driver(&virtio_hdma_fe_driver); +} + +void virtio_hdma_fe_unregister(void) +{ + unregister_virtio_driver(&virtio_hdma_fe_driver); +} + +static int virtio_hdma_fe_get_vmid(void) +{ + struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; + + if (hyper_dmabuf_virtio_fe == NULL) { + dev_err(hy_drv_priv->dev, + "Backend not connected\n"); + return -1; + } + + return priv->vmid; +} + +static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, + int wait) +{ + struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; + struct hyper_dmabuf_req *tx_req; + int timeout = 1000; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "Backend not connected\n"); + return -ENOENT; + } + + /* Check if there are any free buffers in ring */ + while (timeout--) { + if (!virtio_comm_ring_full(&priv->tx_ring)) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + dev_err(hy_drv_priv->dev, + "Timedout while waiting for free request buffers\n"); + return -EBUSY; + } + + /* Get free buffer for sending request from ring */ + tx_req = (struct hyper_dmabuf_req *) + virtio_comm_ring_push(&priv->tx_ring); + req->req_id = hyper_dmabuf_virtio_get_next_req_id(); + + /* copy request to buffer that will be used in virtqueue */ + memcpy(tx_req, req, sizeof(*req)); + + virtio_hdma_fe_queue_buffer(hyper_dmabuf_virtio_fe, + HDMA_VIRTIO_TX_QUEUE, + tx_req, sizeof(*tx_req)); + + if (wait) { + while (timeout--) { + if (tx_req->stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) + return -EBUSY; + } + + return 0; +} + +struct hyper_dmabuf_bknd_ops virtio_bknd_ops = { + .init = virtio_hdma_fe_register, + .cleanup = virtio_hdma_fe_unregister, + .get_vm_id = virtio_hdma_fe_get_vmid, + .share_pages = virtio_share_pages, + .unshare_pages = virtio_unshare_pages, + .map_shared_pages = virtio_map_shared_pages, + .unmap_shared_pages = virtio_unmap_shared_pages, + .send_req = virtio_hdma_fe_send_req, + .init_comm_env = NULL, + .destroy_comm = NULL, + .init_rx_ch = NULL, + .init_tx_ch = NULL, +}; + + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Hyper dmabuf virtio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c new file mode 100644 index 000000000000..79b30e286b5e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_fe_list.h" + +DECLARE_HASHTABLE(virtio_fe_hash, MAX_ENTRY_FE); + +void virtio_fe_table_init(void) +{ + hash_init(virtio_fe_hash); +} + +int virtio_fe_add(struct virtio_fe_info *fe_info) +{ + struct virtio_fe_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = fe_info; + + hash_add(virtio_fe_hash, &info_entry->node, + info_entry->info->client_id); + + return 0; +} + +struct virtio_fe_info *virtio_fe_find(int client_id) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) + return info_entry->info; + + return NULL; +} + +struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->vmid == vmid) + return info_entry->info; + + return NULL; +} + +int virtio_fe_remove(int client_id) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h new file mode 100644 index 000000000000..bc7ef843161c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_FE_LIST_H__ +#define __HYPER_DMABUF_VIRTIO_FE_LIST_H__ + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_FE 7 + +struct virtio_fe_info; + +struct virtio_fe_info_entry { + struct virtio_fe_info *info; + struct hlist_node node; +}; + +void virtio_fe_table_init(void); + +int virtio_fe_add(struct virtio_fe_info *fe_info); + +int virtio_remove_fe(int client_id); + +struct virtio_fe_info *virtio_fe_find(int client_id); + +struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid); + +#endif /* __HYPER_DMABUF_VIRTIO_FE_LIST_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c new file mode 100644 index 000000000000..be5141c25191 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c @@ -0,0 +1,343 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +#include +#endif +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_common.h" + +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +struct virtio_shared_pages_info { + u64 *lvl3_table; + u64 **lvl2_table; + u64 lvl3_gref; + struct page **data_pages; + int n_lvl2_refs; + int nents_last; + int vmid; +}; +#else +struct virtio_shared_pages_info { + u64 *lvl3_table; + u64 *lvl2_table; + u64 lvl3_gref; +}; +#endif + +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +static int virtio_be_share_pages(struct page **pages, + int vmid, + int nents, + void **refs_info) +{ + dev_err(hy_drv_priv->dev, + "Pages sharing not available with ACRN backend in SOS\n"); + + return -EINVAL; +} + +static int virtio_be_unshare_pages(void **refs_info, + int nents) +{ + dev_err(hy_drv_priv->dev, + "Pages sharing not available with ACRN backend in SOS\n"); + + return -EINVAL; +} + +static struct page **virtio_be_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + u64 *lvl3_table = NULL; + u64 **lvl2_table = NULL; + struct page **data_pages = NULL; + struct virtio_shared_pages_info *sh_pages_info = NULL; + void *pageaddr; + + int nents_last = (nents - 1) % REFS_PER_PAGE + 1; + int n_lvl2_refs = (nents / REFS_PER_PAGE) + ((nents_last > 0) ? 1 : 0) - + (nents_last == REFS_PER_PAGE); + int i, j, k; + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + if (sh_pages_info == NULL) + goto map_failed; + + *refs_info = (void *) sh_pages_info; + + data_pages = kcalloc(nents, sizeof(struct page *), GFP_KERNEL); + if (data_pages == NULL) + goto map_failed; + + lvl2_table = kcalloc(n_lvl2_refs, sizeof(u64 *), GFP_KERNEL); + if (lvl2_table == NULL) + goto map_failed; + + lvl3_table = (u64 *)map_guest_phys(vmid, lvl3_gref, PAGE_SIZE); + if (lvl3_table == NULL) + goto map_failed; + + for (i = 0; i < n_lvl2_refs; i++) { + lvl2_table[i] = (u64 *)map_guest_phys(vmid, + lvl3_table[i], + PAGE_SIZE); + if (lvl2_table[i] == NULL) + goto map_failed; + } + + k = 0; + for (i = 0; i < n_lvl2_refs - 1; i++) { + for (j = 0; j < REFS_PER_PAGE; j++) { + pageaddr = map_guest_phys(vmid, + lvl2_table[i][j], + PAGE_SIZE); + if (pageaddr == NULL) + goto map_failed; + + data_pages[k] = virt_to_page(pageaddr); + k++; + } + } + + for (j = 0; j < nents_last; j++) { + pageaddr = map_guest_phys(vmid, + lvl2_table[i][j], + PAGE_SIZE); + if (pageaddr == NULL) + goto map_failed; + + data_pages[k] = virt_to_page(pageaddr); + k++; + } + + sh_pages_info->lvl2_table = lvl2_table; + sh_pages_info->lvl3_table = lvl3_table; + sh_pages_info->lvl3_gref = lvl3_gref; + sh_pages_info->n_lvl2_refs = n_lvl2_refs; + sh_pages_info->nents_last = nents_last; + sh_pages_info->data_pages = data_pages; + sh_pages_info->vmid = vmid; + + return data_pages; + +map_failed: + dev_err(hy_drv_priv->dev, + "Cannot map guest memory\n"); + + kfree(lvl2_table); + kfree(data_pages); + kfree(sh_pages_info); + + return NULL; +} + +/* + * TODO: In theory pages don't need to be unmaped, + * as ACRN is just translating memory addresses, + * but not sure if that will work the same way in future + */ +static int virtio_be_unmap_shared_pages(void **refs_info, int nents) +{ + struct virtio_shared_pages_info *sh_pages_info; + int vmid; + int i, j; + + sh_pages_info = (struct virtio_shared_pages_info *)(*refs_info); + + if (sh_pages_info->data_pages == NULL) { + dev_warn(hy_drv_priv->dev, + "Imported pages already cleaned up"); + dev_warn(hy_drv_priv->dev, + "or buffer was not imported yet\n"); + return 0; + } + vmid = sh_pages_info->vmid; + + for (i = 0; i < sh_pages_info->n_lvl2_refs - 1; i++) { + for (j = 0; j < REFS_PER_PAGE; j++) + unmap_guest_phys(vmid, + sh_pages_info->lvl2_table[i][j]); + } + + for (j = 0; j < sh_pages_info->nents_last; j++) + unmap_guest_phys(vmid, sh_pages_info->lvl2_table[i][j]); + + for (i = 0; i < sh_pages_info->n_lvl2_refs; i++) + unmap_guest_phys(vmid, sh_pages_info->lvl3_table[i]); + + unmap_guest_phys(vmid, sh_pages_info->lvl3_gref); + + kfree(sh_pages_info->lvl2_table); + kfree(sh_pages_info->data_pages); + sh_pages_info->data_pages = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + return 0; +} +#else +static int virtio_fe_share_pages(struct page **pages, + int domid, int nents, + void **refs_info) +{ + struct virtio_shared_pages_info *sh_pages_info; + u64 lvl3_gref; + u64 *lvl2_table; + u64 *lvl3_table; + int i; + + /* + * Calculate number of pages needed for 2nd level addresing: + */ + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + lvl3_table = (u64 *)__get_free_pages(GFP_KERNEL, 1); + lvl2_table = (u64 *)__get_free_pages(GFP_KERNEL, n_lvl2_grefs); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + + if (sh_pages_info == NULL) + return -ENOMEM; + + *refs_info = (void *)sh_pages_info; + + /* Share physical address of pages */ + for (i = 0; i < nents; i++) + lvl2_table[i] = page_to_phys(pages[i]); + + for (i = 0; i < n_lvl2_grefs; i++) + lvl3_table[i] = + virt_to_phys((void *)lvl2_table + i * PAGE_SIZE); + + lvl3_gref = virt_to_phys(lvl3_table); + + sh_pages_info->lvl3_table = lvl3_table; + sh_pages_info->lvl2_table = lvl2_table; + sh_pages_info->lvl3_gref = lvl3_gref; + + return lvl3_gref; +} + +static int virtio_fe_unshare_pages(void **refs_info, + int nents) +{ + struct virtio_shared_pages_info *sh_pages_info; + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + sh_pages_info = (struct virtio_shared_pages_info *)(*refs_info); + + if (sh_pages_info == NULL) { + dev_err(hy_drv_priv->dev, + "No pages info\n"); + return -EINVAL; + } + + free_pages((unsigned long)sh_pages_info->lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)sh_pages_info->lvl3_table, 1); + + kfree(sh_pages_info); + + return 0; +} + +static struct page **virtio_fe_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + dev_dbg(hy_drv_priv->dev, + "Virtio frontend not supporting currently page mapping\n"); + return NULL; +} + +static int virtio_fe_unmap_shared_pages(void **refs_info, int nents) +{ + dev_dbg(hy_drv_priv->dev, + "Virtio frontend not supporting currently page mapping\n"); + return -EINVAL; +} + +#endif + +int virtio_share_pages(struct page **pages, + int domid, int nents, + void **refs_info) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_share_pages(pages, domid, nents, refs_info); +#else + ret = virtio_fe_share_pages(pages, domid, nents, refs_info); +#endif + return ret; +} + +int virtio_unshare_pages(void **refs_info, int nents) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_unshare_pages(refs_info, nents); +#else + ret = virtio_fe_unshare_pages(refs_info, nents); +#endif + return ret; +} + +struct page **virtio_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + struct page **ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_map_shared_pages(lvl3_gref, vmid, + nents, refs_info); +#else + ret = virtio_fe_map_shared_pages(lvl3_gref, vmid, + nents, refs_info); +#endif + return ret; +} + +int virtio_unmap_shared_pages(void **refs_info, int nents) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_unmap_shared_pages(refs_info, nents); +#else + ret = virtio_fe_unmap_shared_pages(refs_info, nents); +#endif + return ret; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h new file mode 100644 index 000000000000..05cbf5779f86 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_SHM_H__ +#define __HYPER_DMABUF_VIRTIO_SHM_H__ + +int virtio_share_pages(struct page **pages, + int domid, int nents, + void **refs_info); + +int virtio_unshare_pages(void **refs_info, int nents); + +struct page **virtio_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info); + +int virtio_unmap_shared_pages(void **refs_info, int nents); + +#endif /* __HYPER_DMABUF_VIRTIO_SHM_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c new file mode 100644 index 000000000000..3dd49db66e31 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c @@ -0,0 +1,951 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" +#include "../hyper_dmabuf_drv.h" + +static int export_req_id; + +struct hyper_dmabuf_req req_pending = {0}; + +static void xen_get_domid_delayed(struct work_struct *unused); +static void xen_init_comm_env_delayed(struct work_struct *unused); + +static DECLARE_DELAYED_WORK(get_vm_id_work, xen_get_domid_delayed); +static DECLARE_DELAYED_WORK(xen_init_comm_env_work, xen_init_comm_env_delayed); + +/* Creates entry in xen store that will keep details of all + * exporter rings created by this domain + */ +static int xen_comm_setup_data_dir(void) +{ + char buf[255]; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", + hy_drv_priv->domid); + + return xenbus_mkdir(XBT_NIL, buf, ""); +} + +/* Removes entry from xenstore with exporter ring details. + * Other domains that has connected to any of exporter rings + * created by this domain, will be notified about removal of + * this entry and will treat that as signal to cleanup importer + * rings created for this domain + */ +static int xen_comm_destroy_data_dir(void) +{ + char buf[255]; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", + hy_drv_priv->domid); + + return xenbus_rm(XBT_NIL, buf, ""); +} + +/* Adds xenstore entries with details of exporter ring created + * for given remote domain. It requires special daemon running + * in dom0 to make sure that given remote domain will have right + * permissions to access that data. + */ +static int xen_comm_expose_ring_details(int domid, int rdomid, + int gref, int port) +{ + char buf[255]; + int ret; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + domid, rdomid); + + ret = xenbus_printf(XBT_NIL, buf, "grefid", "%d", gref); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to write xenbus entry %s: %d\n", + buf, ret); + + return ret; + } + + ret = xenbus_printf(XBT_NIL, buf, "port", "%d", port); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to write xenbus entry %s: %d\n", + buf, ret); + + return ret; + } + + return 0; +} + +/* + * Queries details of ring exposed by remote domain. + */ +static int xen_comm_get_ring_details(int domid, int rdomid, + int *grefid, int *port) +{ + char buf[255]; + int ret; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + rdomid, domid); + + ret = xenbus_scanf(XBT_NIL, buf, "grefid", "%d", grefid); + + if (ret <= 0) { + dev_err(hy_drv_priv->dev, + "Failed to read xenbus entry %s: %d\n", + buf, ret); + + return 1; + } + + ret = xenbus_scanf(XBT_NIL, buf, "port", "%d", port); + + if (ret <= 0) { + dev_err(hy_drv_priv->dev, + "Failed to read xenbus entry %s: %d\n", + buf, ret); + + return 1; + } + + return 0; +} + +static void xen_get_domid_delayed(struct work_struct *unused) +{ + struct xenbus_transaction xbt; + int domid, ret; + + /* scheduling another if driver is still running + * and xenstore has not been initialized + */ + if (likely(xenstored_ready == 0)) { + dev_dbg(hy_drv_priv->dev, + "Xenstore is not ready yet. Will retry in 500ms\n"); + schedule_delayed_work(&get_vm_id_work, msecs_to_jiffies(500)); + } else { + xenbus_transaction_start(&xbt); + + ret = xenbus_scanf(xbt, "domid", "", "%d", &domid); + + if (ret <= 0) + domid = -1; + + xenbus_transaction_end(xbt, 0); + + /* try again since -1 is an invalid id for domain + * (but only if driver is still running) + */ + if (unlikely(domid == -1)) { + dev_dbg(hy_drv_priv->dev, + "domid==-1 is invalid. Will retry it in 500ms\n"); + schedule_delayed_work(&get_vm_id_work, + msecs_to_jiffies(500)); + } else { + dev_info(hy_drv_priv->dev, + "Successfully retrieved domid from Xenstore:%d\n", + domid); + hy_drv_priv->domid = domid; + } + } +} + +int xen_be_get_domid(void) +{ + struct xenbus_transaction xbt; + int domid; + + if (unlikely(xenstored_ready == 0)) { + xen_get_domid_delayed(NULL); + return -1; + } + + xenbus_transaction_start(&xbt); + + if (!xenbus_scanf(xbt, "domid", "", "%d", &domid)) + domid = -1; + + xenbus_transaction_end(xbt, 0); + + return domid; +} + +static int xen_comm_next_req_id(void) +{ + export_req_id++; + return export_req_id; +} + +/* For now cache latast rings as global variables TODO: keep them in list*/ +static irqreturn_t front_ring_isr(int irq, void *info); +static irqreturn_t back_ring_isr(int irq, void *info); + +/* Callback function that will be called on any change of xenbus path + * being watched. Used for detecting creation/destruction of remote + * domain exporter ring. + * + * When remote domain's exporter ring will be detected, importer ring + * on this domain will be created. + * + * When remote domain's exporter ring destruction will be detected it + * will celanup this domain importer ring. + * + * Destruction can be caused by unloading module by remote domain or + * it's crash/force shutdown. + */ +static void remote_dom_exporter_watch_cb(struct xenbus_watch *watch, + const char *path, const char *token) +{ + int rdom, ret; + uint32_t grefid, port; + struct xen_comm_rx_ring_info *ring_info; + + /* Check which domain has changed its exporter rings */ + ret = sscanf(watch->node, "/local/domain/%d/", &rdom); + if (ret <= 0) + return; + + /* Check if we have importer ring for given remote domain already + * created + */ + ring_info = xen_comm_find_rx_ring(rdom); + + /* Try to query remote domain exporter ring details - if + * that will fail and we have importer ring that means remote + * domains has cleanup its exporter ring, so our importer ring + * is no longer useful. + * + * If querying details will succeed and we don't have importer ring, + * it means that remote domain has setup it for us and we should + * connect to it. + */ + + ret = xen_comm_get_ring_details(xen_be_get_domid(), + rdom, &grefid, &port); + + if (ring_info && ret != 0) { + dev_info(hy_drv_priv->dev, + "Remote exporter closed, cleaninup importer\n"); + xen_be_cleanup_rx_rbuf(rdom); + } else if (!ring_info && ret == 0) { + dev_info(hy_drv_priv->dev, + "Registering importer\n"); + xen_be_init_rx_rbuf(rdom); + } +} + +/* exporter needs to generated info for page sharing */ +int xen_be_init_tx_rbuf(int domid) +{ + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_sring *sring; + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_close close; + + void *shared_ring; + int ret; + + /* check if there's any existing tx channel in the table */ + ring_info = xen_comm_find_tx_ring(domid); + + if (ring_info) { + dev_info(hy_drv_priv->dev, + "tx ring ch to domid = %d already exist\ngref = %d, port = %d\n", + ring_info->rdomain, ring_info->gref_ring, ring_info->port); + return 0; + } + + ring_info = kmalloc(sizeof(*ring_info), GFP_KERNEL); + + if (!ring_info) + return -ENOMEM; + + /* from exporter to importer */ + shared_ring = (void *)__get_free_pages(GFP_KERNEL, 1); + if (shared_ring == 0) { + kfree(ring_info); + return -ENOMEM; + } + + sring = (struct xen_comm_sring *) shared_ring; + + SHARED_RING_INIT(sring); + + FRONT_RING_INIT(&(ring_info->ring_front), sring, PAGE_SIZE); + + ring_info->gref_ring = gnttab_grant_foreign_access(domid, + virt_to_mfn(shared_ring), + 0); + if (ring_info->gref_ring < 0) { + /* fail to get gref */ + kfree(ring_info); + return -EFAULT; + } + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = domid; + ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, + &alloc_unbound); + if (ret) { + dev_err(hy_drv_priv->dev, + "Cannot allocate event channel\n"); + kfree(ring_info); + return -EIO; + } + + /* setting up interrupt */ + ret = bind_evtchn_to_irqhandler(alloc_unbound.port, + front_ring_isr, 0, + NULL, (void *) ring_info); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to setup event channel\n"); + close.port = alloc_unbound.port; + HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + gnttab_end_foreign_access(ring_info->gref_ring, 0, + virt_to_mfn(shared_ring)); + kfree(ring_info); + return -EIO; + } + + ring_info->rdomain = domid; + ring_info->irq = ret; + ring_info->port = alloc_unbound.port; + + mutex_init(&ring_info->lock); + + dev_dbg(hy_drv_priv->dev, + "%s: allocated eventchannel gref %d port: %d irq: %d\n", + __func__, + ring_info->gref_ring, + ring_info->port, + ring_info->irq); + + ret = xen_comm_add_tx_ring(ring_info); + + if (ret < 0) { + kfree(ring_info); + return -ENOMEM; + } + + ret = xen_comm_expose_ring_details(xen_be_get_domid(), + domid, + ring_info->gref_ring, + ring_info->port); + + /* Register watch for remote domain exporter ring. + * When remote domain will setup its exporter ring, + * we will automatically connect our importer ring to it. + */ + ring_info->watch.callback = remote_dom_exporter_watch_cb; + ring_info->watch.node = kmalloc(255, GFP_KERNEL); + + if (!ring_info->watch.node) { + kfree(ring_info); + return -ENOMEM; + } + + sprintf((char *)ring_info->watch.node, + "/local/domain/%d/data/hyper_dmabuf/%d/port", + domid, xen_be_get_domid()); + + register_xenbus_watch(&ring_info->watch); + + return ret; +} + +/* cleans up exporter ring created for given remote domain */ +void xen_be_cleanup_tx_rbuf(int domid) +{ + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_rx_ring_info *rx_ring_info; + + /* check if we at all have exporter ring for given rdomain */ + ring_info = xen_comm_find_tx_ring(domid); + + if (!ring_info) + return; + + xen_comm_remove_tx_ring(domid); + + unregister_xenbus_watch(&ring_info->watch); + kfree(ring_info->watch.node); + + /* No need to close communication channel, will be done by + * this function + */ + unbind_from_irqhandler(ring_info->irq, (void *) ring_info); + + /* No need to free sring page, will be freed by this function + * when other side will end its access + */ + gnttab_end_foreign_access(ring_info->gref_ring, 0, + (unsigned long) ring_info->ring_front.sring); + + kfree(ring_info); + + rx_ring_info = xen_comm_find_rx_ring(domid); + if (!rx_ring_info) + return; + + BACK_RING_INIT(&(rx_ring_info->ring_back), + rx_ring_info->ring_back.sring, + PAGE_SIZE); +} + +/* importer needs to know about shared page and port numbers for + * ring buffer and event channel + */ +int xen_be_init_rx_rbuf(int domid) +{ + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_sring *sring; + + struct page *shared_ring; + + struct gnttab_map_grant_ref *map_ops; + + int ret; + int rx_gref, rx_port; + + /* check if there's existing rx ring channel */ + ring_info = xen_comm_find_rx_ring(domid); + + if (ring_info) { + dev_info(hy_drv_priv->dev, + "rx ring ch from domid = %d already exist\n", + ring_info->sdomain); + + return 0; + } + + ret = xen_comm_get_ring_details(xen_be_get_domid(), domid, + &rx_gref, &rx_port); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Domain %d has not created exporter ring for current domain\n", + domid); + + return ret; + } + + ring_info = kmalloc(sizeof(*ring_info), GFP_KERNEL); + + if (!ring_info) + return -ENOMEM; + + ring_info->sdomain = domid; + ring_info->evtchn = rx_port; + + map_ops = kmalloc(sizeof(*map_ops), GFP_KERNEL); + + if (!map_ops) { + ret = -ENOMEM; + goto fail_no_map_ops; + } + + if (gnttab_alloc_pages(1, &shared_ring)) { + ret = -ENOMEM; + goto fail_others; + } + + gnttab_set_map_op(&map_ops[0], + (unsigned long)pfn_to_kaddr( + page_to_pfn(shared_ring)), + GNTMAP_host_map, rx_gref, domid); + + gnttab_set_unmap_op(&ring_info->unmap_op, + (unsigned long)pfn_to_kaddr( + page_to_pfn(shared_ring)), + GNTMAP_host_map, -1); + + ret = gnttab_map_refs(map_ops, NULL, &shared_ring, 1); + if (ret < 0) { + dev_err(hy_drv_priv->dev, "Cannot map ring\n"); + ret = -EFAULT; + goto fail_others; + } + + if (map_ops[0].status) { + dev_err(hy_drv_priv->dev, "Ring mapping failed\n"); + ret = -EFAULT; + goto fail_others; + } else { + ring_info->unmap_op.handle = map_ops[0].handle; + } + + kfree(map_ops); + + sring = (struct xen_comm_sring *)pfn_to_kaddr(page_to_pfn(shared_ring)); + + BACK_RING_INIT(&ring_info->ring_back, sring, PAGE_SIZE); + + ret = bind_interdomain_evtchn_to_irq(domid, rx_port); + + if (ret < 0) { + ret = -EIO; + goto fail_others; + } + + ring_info->irq = ret; + + dev_dbg(hy_drv_priv->dev, + "%s: bound to eventchannel port: %d irq: %d\n", __func__, + rx_port, + ring_info->irq); + + ret = xen_comm_add_rx_ring(ring_info); + + if (ret < 0) { + ret = -ENOMEM; + goto fail_others; + } + + /* Setup communcation channel in opposite direction */ + if (!xen_comm_find_tx_ring(domid)) + ret = xen_be_init_tx_rbuf(domid); + + ret = request_irq(ring_info->irq, + back_ring_isr, 0, + NULL, (void *)ring_info); + + return ret; + +fail_others: + kfree(map_ops); + +fail_no_map_ops: + kfree(ring_info); + + return ret; +} + +/* clenas up importer ring create for given source domain */ +void xen_be_cleanup_rx_rbuf(int domid) +{ + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_tx_ring_info *tx_ring_info; + struct page *shared_ring; + + /* check if we have importer ring created for given sdomain */ + ring_info = xen_comm_find_rx_ring(domid); + + if (!ring_info) + return; + + xen_comm_remove_rx_ring(domid); + + /* no need to close event channel, will be done by that function */ + unbind_from_irqhandler(ring_info->irq, (void *)ring_info); + + /* unmapping shared ring page */ + shared_ring = virt_to_page(ring_info->ring_back.sring); + gnttab_unmap_refs(&ring_info->unmap_op, NULL, &shared_ring, 1); + gnttab_free_pages(1, &shared_ring); + + kfree(ring_info); + + tx_ring_info = xen_comm_find_tx_ring(domid); + if (!tx_ring_info) + return; + + SHARED_RING_INIT(tx_ring_info->ring_front.sring); + FRONT_RING_INIT(&(tx_ring_info->ring_front), + tx_ring_info->ring_front.sring, + PAGE_SIZE); +} + +#ifdef CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + +static void xen_rx_ch_add_delayed(struct work_struct *unused); + +static DECLARE_DELAYED_WORK(xen_rx_ch_auto_add_work, xen_rx_ch_add_delayed); + +#define DOMID_SCAN_START 1 /* domid = 1 */ +#define DOMID_SCAN_END 10 /* domid = 10 */ + +static void xen_rx_ch_add_delayed(struct work_struct *unused) +{ + int ret; + char buf[128]; + int i, dummy; + + dev_dbg(hy_drv_priv->dev, + "Scanning new tx channel comming from another domain\n"); + + /* check other domains and schedule another work if driver + * is still running and backend is valid + */ + if (hy_drv_priv && + hy_drv_priv->initialized) { + for (i = DOMID_SCAN_START; i < DOMID_SCAN_END + 1; i++) { + if (i == hy_drv_priv->domid) + continue; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + i, hy_drv_priv->domid); + + ret = xenbus_scanf(XBT_NIL, buf, "port", "%d", &dummy); + + if (ret > 0) { + if (xen_comm_find_rx_ring(i) != NULL) + continue; + + ret = xen_be_init_rx_rbuf(i); + + if (!ret) + dev_info(hy_drv_priv->dev, + "Done rx ch init for VM %d\n", + i); + } + } + + /* check every 10 seconds */ + schedule_delayed_work(&xen_rx_ch_auto_add_work, + msecs_to_jiffies(10000)); + } +} + +#endif /* CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD */ + +void xen_init_comm_env_delayed(struct work_struct *unused) +{ + int ret; + + /* scheduling another work if driver is still running + * and xenstore hasn't been initialized or dom_id hasn't + * been correctly retrieved. + */ + if (likely(xenstored_ready == 0 || + hy_drv_priv->domid == -1)) { + dev_dbg(hy_drv_priv->dev, + "Xenstore not ready Will re-try in 500ms\n"); + schedule_delayed_work(&xen_init_comm_env_work, + msecs_to_jiffies(500)); + } else { + ret = xen_comm_setup_data_dir(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create data dir in Xenstore\n"); + } else { + dev_info(hy_drv_priv->dev, + "Successfully finished comm env init\n"); + hy_drv_priv->initialized = true; + +#ifdef CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + xen_rx_ch_add_delayed(NULL); +#endif /* CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD */ + } + } +} + +int xen_be_init_comm_env(void) +{ + int ret; + + xen_comm_ring_table_init(); + + if (unlikely(xenstored_ready == 0 || + hy_drv_priv->domid == -1)) { + xen_init_comm_env_delayed(NULL); + return -1; + } + + ret = xen_comm_setup_data_dir(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create data dir in Xenstore\n"); + } else { + dev_info(hy_drv_priv->dev, + "Successfully finished comm env initialization\n"); + + hy_drv_priv->initialized = true; + } + + return ret; +} + +/* cleans up all tx/rx rings */ +static void xen_be_cleanup_all_rbufs(void) +{ + xen_comm_foreach_tx_ring(xen_be_cleanup_tx_rbuf); + xen_comm_foreach_rx_ring(xen_be_cleanup_rx_rbuf); +} + +void xen_be_destroy_comm(void) +{ + xen_be_cleanup_all_rbufs(); + xen_comm_destroy_data_dir(); +} + +int xen_be_send_req(int domid, struct hyper_dmabuf_req *req, + int wait) +{ + struct xen_comm_front_ring *ring; + struct hyper_dmabuf_req *new_req; + struct xen_comm_tx_ring_info *ring_info; + int notify; + + struct timeval tv_start, tv_end; + struct timeval tv_diff; + + int timeout = 1000; + + /* find a ring info for the channel */ + ring_info = xen_comm_find_tx_ring(domid); + if (!ring_info) { + dev_err(hy_drv_priv->dev, + "Can't find ring info for the channel\n"); + return -ENOENT; + } + + + ring = &ring_info->ring_front; + + do_gettimeofday(&tv_start); + + while (RING_FULL(ring)) { + dev_dbg(hy_drv_priv->dev, "RING_FULL\n"); + + if (timeout == 0) { + dev_err(hy_drv_priv->dev, + "Timeout while waiting for an entry in the ring\n"); + return -EIO; + } + usleep_range(100, 120); + timeout--; + } + + timeout = 1000; + + mutex_lock(&ring_info->lock); + + new_req = RING_GET_REQUEST(ring, ring->req_prod_pvt); + if (!new_req) { + mutex_unlock(&ring_info->lock); + dev_err(hy_drv_priv->dev, + "NULL REQUEST\n"); + return -EIO; + } + + req->req_id = xen_comm_next_req_id(); + + /* update req_pending with current request */ + memcpy(&req_pending, req, sizeof(req_pending)); + + /* pass current request to the ring */ + memcpy(new_req, req, sizeof(*new_req)); + + ring->req_prod_pvt++; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify); + if (notify) + notify_remote_via_irq(ring_info->irq); + + if (wait) { + while (timeout--) { + if (req_pending.stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + mutex_unlock(&ring_info->lock); + dev_err(hy_drv_priv->dev, + "request timed-out\n"); + return -EBUSY; + } + + mutex_unlock(&ring_info->lock); + do_gettimeofday(&tv_end); + + /* checking time duration for round-trip of a request + * for debugging + */ + if (tv_end.tv_usec >= tv_start.tv_usec) { + tv_diff.tv_sec = tv_end.tv_sec-tv_start.tv_sec; + tv_diff.tv_usec = tv_end.tv_usec-tv_start.tv_usec; + } else { + tv_diff.tv_sec = tv_end.tv_sec-tv_start.tv_sec-1; + tv_diff.tv_usec = tv_end.tv_usec+1000000- + tv_start.tv_usec; + } + + if (tv_diff.tv_sec != 0 && tv_diff.tv_usec > 16000) + dev_dbg(hy_drv_priv->dev, + "send_req:time diff: %ld sec, %ld usec\n", + tv_diff.tv_sec, tv_diff.tv_usec); + } + + mutex_unlock(&ring_info->lock); + + return 0; +} + +/* ISR for handling request */ +static irqreturn_t back_ring_isr(int irq, void *info) +{ + RING_IDX rc, rp; + struct hyper_dmabuf_req req; + struct hyper_dmabuf_resp resp; + + int notify, more_to_do; + int ret; + + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_back_ring *ring; + + ring_info = (struct xen_comm_rx_ring_info *)info; + ring = &ring_info->ring_back; + + dev_dbg(hy_drv_priv->dev, "%s\n", __func__); + + do { + rc = ring->req_cons; + rp = ring->sring->req_prod; + more_to_do = 0; + while (rc != rp) { + if (RING_REQUEST_CONS_OVERFLOW(ring, rc)) + break; + + memcpy(&req, RING_GET_REQUEST(ring, rc), sizeof(req)); + ring->req_cons = ++rc; + + ret = hyper_dmabuf_msg_parse(ring_info->sdomain, &req); + + if (ret > 0) { + /* preparing a response for the request and + * send it to the requester + */ + memcpy(&resp, &req, sizeof(resp)); + memcpy(RING_GET_RESPONSE(ring, + ring->rsp_prod_pvt), + &resp, sizeof(resp)); + ring->rsp_prod_pvt++; + + dev_dbg(hy_drv_priv->dev, + "responding to exporter for req:%d\n", + resp.resp_id); + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, + notify); + + if (notify) + notify_remote_via_irq(ring_info->irq); + } + + RING_FINAL_CHECK_FOR_REQUESTS(ring, more_to_do); + } + } while (more_to_do); + + return IRQ_HANDLED; +} + +/* ISR for handling responses */ +static irqreturn_t front_ring_isr(int irq, void *info) +{ + /* front ring only care about response from back */ + struct hyper_dmabuf_resp *resp; + RING_IDX i, rp; + int more_to_do, ret; + + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_front_ring *ring; + + ring_info = (struct xen_comm_tx_ring_info *)info; + ring = &ring_info->ring_front; + + dev_dbg(hy_drv_priv->dev, "%s\n", __func__); + + do { + more_to_do = 0; + rp = ring->sring->rsp_prod; + for (i = ring->rsp_cons; i != rp; i++) { + resp = RING_GET_RESPONSE(ring, i); + + /* update pending request's status with what is + * in the response + */ + + dev_dbg(hy_drv_priv->dev, + "getting response from importer\n"); + + if (req_pending.req_id == resp->resp_id) + req_pending.stat = resp->stat; + + if (resp->stat == HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP) { + /* parsing response */ + ret = hyper_dmabuf_msg_parse(ring_info->rdomain, + (struct hyper_dmabuf_req *)resp); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "err while parsing resp\n"); + } + } else if (resp->stat == HYPER_DMABUF_REQ_PROCESSED) { + /* for debugging dma_buf remote synch */ + dev_dbg(hy_drv_priv->dev, + "original request = 0x%x\n", resp->cmd); + dev_dbg(hy_drv_priv->dev, + "got HYPER_DMABUF_REQ_PROCESSED\n"); + } else if (resp->stat == HYPER_DMABUF_REQ_ERROR) { + /* for debugging dma_buf remote synch */ + dev_dbg(hy_drv_priv->dev, + "original request = 0x%x\n", resp->cmd); + dev_dbg(hy_drv_priv->dev, + "got HYPER_DMABUF_REQ_ERROR\n"); + } + } + + ring->rsp_cons = i; + + if (i != ring->req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(ring, more_to_do); + else + ring->sring->rsp_event = i+1; + + } while (more_to_do); + + return IRQ_HANDLED; +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h new file mode 100644 index 000000000000..70a2b704badd --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_COMM_H__ +#define __HYPER_DMABUF_XEN_COMM_H__ + +#include "xen/interface/io/ring.h" +#include "xen/xenbus.h" +#include "../hyper_dmabuf_msg.h" + +extern int xenstored_ready; + +DEFINE_RING_TYPES(xen_comm, struct hyper_dmabuf_req, struct hyper_dmabuf_resp); + +struct xen_comm_tx_ring_info { + struct xen_comm_front_ring ring_front; + int rdomain; + int gref_ring; + int irq; + int port; + struct mutex lock; + struct xenbus_watch watch; +}; + +struct xen_comm_rx_ring_info { + int sdomain; + int irq; + int evtchn; + struct xen_comm_back_ring ring_back; + struct gnttab_unmap_grant_ref unmap_op; +}; + +int xen_be_get_domid(void); + +int xen_be_init_comm_env(void); + +/* exporter needs to generated info for page sharing */ +int xen_be_init_tx_rbuf(int domid); + +/* importer needs to know about shared page and port numbers + * for ring buffer and event channel + */ +int xen_be_init_rx_rbuf(int domid); + +/* cleans up exporter ring created for given domain */ +void xen_be_cleanup_tx_rbuf(int domid); + +/* cleans up importer ring created for given domain */ +void xen_be_cleanup_rx_rbuf(int domid); + +void xen_be_destroy_comm(void); + +/* send request to the remote domain */ +int xen_be_send_req(int domid, struct hyper_dmabuf_req *req, + int wait); + +#endif /* __HYPER_DMABUF_XEN_COMM_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c new file mode 100644 index 000000000000..15023dbc8ced --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c @@ -0,0 +1,158 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" + +DECLARE_HASHTABLE(xen_comm_tx_ring_hash, MAX_ENTRY_TX_RING); +DECLARE_HASHTABLE(xen_comm_rx_ring_hash, MAX_ENTRY_RX_RING); + +void xen_comm_ring_table_init(void) +{ + hash_init(xen_comm_rx_ring_hash); + hash_init(xen_comm_tx_ring_hash); +} + +int xen_comm_add_tx_ring(struct xen_comm_tx_ring_info *ring_info) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = ring_info; + + hash_add(xen_comm_tx_ring_hash, &info_entry->node, + info_entry->info->rdomain); + + return 0; +} + +int xen_comm_add_rx_ring(struct xen_comm_rx_ring_info *ring_info) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = ring_info; + + hash_add(xen_comm_rx_ring_hash, &info_entry->node, + info_entry->info->sdomain); + + return 0; +} + +struct xen_comm_tx_ring_info *xen_comm_find_tx_ring(int domid) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_tx_ring_hash, bkt, info_entry, node) + if (info_entry->info->rdomain == domid) + return info_entry->info; + + return NULL; +} + +struct xen_comm_rx_ring_info *xen_comm_find_rx_ring(int domid) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_rx_ring_hash, bkt, info_entry, node) + if (info_entry->info->sdomain == domid) + return info_entry->info; + + return NULL; +} + +int xen_comm_remove_tx_ring(int domid) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_tx_ring_hash, bkt, info_entry, node) + if (info_entry->info->rdomain == domid) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} + +int xen_comm_remove_rx_ring(int domid) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_rx_ring_hash, bkt, info_entry, node) + if (info_entry->info->sdomain == domid) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} + +void xen_comm_foreach_tx_ring(void (*func)(int domid)) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(xen_comm_tx_ring_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info->rdomain); + } +} + +void xen_comm_foreach_rx_ring(void (*func)(int domid)) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(xen_comm_rx_ring_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info->sdomain); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h new file mode 100644 index 000000000000..8502fe7df578 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_COMM_LIST_H__ +#define __HYPER_DMABUF_XEN_COMM_LIST_H__ + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_TX_RING 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_RX_RING 7 + +struct xen_comm_tx_ring_info_entry { + struct xen_comm_tx_ring_info *info; + struct hlist_node node; +}; + +struct xen_comm_rx_ring_info_entry { + struct xen_comm_rx_ring_info *info; + struct hlist_node node; +}; + +void xen_comm_ring_table_init(void); + +int xen_comm_add_tx_ring(struct xen_comm_tx_ring_info *ring_info); + +int xen_comm_add_rx_ring(struct xen_comm_rx_ring_info *ring_info); + +int xen_comm_remove_tx_ring(int domid); + +int xen_comm_remove_rx_ring(int domid); + +struct xen_comm_tx_ring_info *xen_comm_find_tx_ring(int domid); + +struct xen_comm_rx_ring_info *xen_comm_find_rx_ring(int domid); + +/* iterates over all exporter rings and calls provided + * function for each of them + */ +void xen_comm_foreach_tx_ring(void (*func)(int domid)); + +/* iterates over all importer rings and calls provided + * function for each of them + */ +void xen_comm_foreach_rx_ring(void (*func)(int domid)); + +#endif // __HYPER_DMABUF_XEN_COMM_LIST_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c new file mode 100644 index 000000000000..14ed3bc51e6a --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c @@ -0,0 +1,46 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_shm.h" + +struct hyper_dmabuf_bknd_ops xen_bknd_ops = { + .init = NULL, /* not needed for xen */ + .cleanup = NULL, /* not needed for xen */ + .get_vm_id = xen_be_get_domid, + .share_pages = xen_be_share_pages, + .unshare_pages = xen_be_unshare_pages, + .map_shared_pages = (void *)xen_be_map_shared_pages, + .unmap_shared_pages = xen_be_unmap_shared_pages, + .init_comm_env = xen_be_init_comm_env, + .destroy_comm = xen_be_destroy_comm, + .init_rx_ch = xen_be_init_rx_rbuf, + .init_tx_ch = xen_be_init_tx_rbuf, + .send_req = xen_be_send_req, +}; diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h new file mode 100644 index 000000000000..a4902b747a87 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_DRV_H__ +#define __HYPER_DMABUF_XEN_DRV_H__ +#include + +extern struct hyper_dmabuf_bknd_ops xen_bknd_ops; + +/* Main purpose of this structure is to keep + * all references created or acquired for sharing + * pages with another domain for freeing those later + * when unsharing. + */ +struct xen_shared_pages_info { + /* top level refid */ + grant_ref_t lvl3_gref; + + /* page of top level addressing, it contains refids of 2nd lvl pages */ + grant_ref_t *lvl3_table; + + /* table of 2nd level pages, that contains refids to data pages */ + grant_ref_t *lvl2_table; + + /* unmap ops for mapped pages */ + struct gnttab_unmap_grant_ref *unmap_ops; + + /* data pages to be unmapped */ + struct page **data_pages; +}; + +#endif // __HYPER_DMABUF_XEN_COMM_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c new file mode 100644 index 000000000000..c6a15f187fe3 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c @@ -0,0 +1,525 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include "hyper_dmabuf_xen_drv.h" +#include "../hyper_dmabuf_drv.h" + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +/* + * Creates 2 level page directory structure for referencing shared pages. + * Top level page is a single page that contains up to 1024 refids that + * point to 2nd level pages. + * + * Each 2nd level page contains up to 1024 refids that point to shared + * data pages. + * + * There will always be one top level page and number of 2nd level pages + * depends on number of shared data pages. + * + * 3rd level page 2nd level pages Data pages + * +-------------------------+ ┌>+--------------------+ ┌>+------------+ + * |2nd level page 0 refid |---┘ |Data page 0 refid |-┘ |Data page 0 | + * |2nd level page 1 refid |---â” |Data page 1 refid |-â” +------------+ + * | ... | | | .... | | + * |2nd level page 1023 refid|-â” | |Data page 1023 refid| â””>+------------+ + * +-------------------------+ | | +--------------------+ |Data page 1 | + * | | +------------+ + * | â””>+--------------------+ + * | |Data page 1024 refid| + * | |Data page 1025 refid| + * | | ... | + * | |Data page 2047 refid| + * | +--------------------+ + * | + * | ..... + * â””-->+-----------------------+ + * |Data page 1047552 refid| + * |Data page 1047553 refid| + * | ... | + * |Data page 1048575 refid| + * +-----------------------+ + * + * Using such 2 level structure it is possible to reference up to 4GB of + * shared data using single refid pointing to top level page. + * + * Returns refid of top level page. + */ +int xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info) +{ + grant_ref_t lvl3_gref; + grant_ref_t *lvl2_table; + grant_ref_t *lvl3_table; + + /* + * Calculate number of pages needed for 2nd level addresing: + */ + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + struct xen_shared_pages_info *sh_pages_info; + int i; + + lvl3_table = (grant_ref_t *)__get_free_pages(GFP_KERNEL, 1); + lvl2_table = (grant_ref_t *)__get_free_pages(GFP_KERNEL, n_lvl2_grefs); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + + if (!sh_pages_info) + return -ENOMEM; + + *refs_info = (void *)sh_pages_info; + + /* share data pages in readonly mode for security */ + for (i = 0; i < nents; i++) { + lvl2_table[i] = gnttab_grant_foreign_access(domid, + pfn_to_mfn(page_to_pfn(pages[i])), + true /* read only */); + if (lvl2_table[i] == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all already shared pages for lvl2 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl2_table[i], 0); + gnttab_free_grant_reference(lvl2_table[i]); + } + goto err_cleanup; + } + } + + /* Share 2nd level addressing pages in readonly mode*/ + for (i = 0; i < n_lvl2_grefs; i++) { + lvl3_table[i] = gnttab_grant_foreign_access(domid, + virt_to_mfn( + (unsigned long)lvl2_table+i*PAGE_SIZE), + true); + + if (lvl3_table[i] == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all already shared pages for lvl3 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl3_table[i], 1); + gnttab_free_grant_reference(lvl3_table[i]); + } + + /* Unshare all pages for lvl2 */ + while (nents--) { + gnttab_end_foreign_access_ref( + lvl2_table[nents], 0); + gnttab_free_grant_reference(lvl2_table[nents]); + } + + goto err_cleanup; + } + } + + /* Share lvl3_table in readonly mode*/ + lvl3_gref = gnttab_grant_foreign_access(domid, + virt_to_mfn((unsigned long)lvl3_table), + true); + + if (lvl3_gref == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all pages for lvl3 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl3_table[i], 1); + gnttab_free_grant_reference(lvl3_table[i]); + } + + /* Unshare all pages for lvl2 */ + while (nents--) { + gnttab_end_foreign_access_ref(lvl2_table[nents], 0); + gnttab_free_grant_reference(lvl2_table[nents]); + } + + goto err_cleanup; + } + + /* Store lvl3_table page to be freed later */ + sh_pages_info->lvl3_table = lvl3_table; + + /* Store lvl2_table pages to be freed later */ + sh_pages_info->lvl2_table = lvl2_table; + + + /* Store exported pages refid to be unshared later */ + sh_pages_info->lvl3_gref = lvl3_gref; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return lvl3_gref; + +err_cleanup: + free_pages((unsigned long)lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)lvl3_table, 1); + + return -ENOSPC; +} + +int xen_be_unshare_pages(void **refs_info, int nents) +{ + struct xen_shared_pages_info *sh_pages_info; + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + int i; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + sh_pages_info = (struct xen_shared_pages_info *)(*refs_info); + + if (sh_pages_info->lvl3_table == NULL || + sh_pages_info->lvl2_table == NULL || + sh_pages_info->lvl3_gref == -1) { + dev_warn(hy_drv_priv->dev, + "gref table for hyper_dmabuf already cleaned up\n"); + return 0; + } + + /* End foreign access for data pages, but do not free them */ + for (i = 0; i < nents; i++) { + if (gnttab_query_foreign_access(sh_pages_info->lvl2_table[i])) + dev_warn(hy_drv_priv->dev, "refid not shared !!\n"); + + gnttab_end_foreign_access_ref(sh_pages_info->lvl2_table[i], 0); + gnttab_free_grant_reference(sh_pages_info->lvl2_table[i]); + } + + /* End foreign access for 2nd level addressing pages */ + for (i = 0; i < n_lvl2_grefs; i++) { + if (gnttab_query_foreign_access(sh_pages_info->lvl3_table[i])) + dev_warn(hy_drv_priv->dev, "refid not shared !!\n"); + + if (!gnttab_end_foreign_access_ref( + sh_pages_info->lvl3_table[i], 1)) + dev_warn(hy_drv_priv->dev, "refid still in use!!!\n"); + + gnttab_free_grant_reference(sh_pages_info->lvl3_table[i]); + } + + /* End foreign access for top level addressing page */ + if (gnttab_query_foreign_access(sh_pages_info->lvl3_gref)) + dev_warn(hy_drv_priv->dev, "gref not shared !!\n"); + + gnttab_end_foreign_access_ref(sh_pages_info->lvl3_gref, 1); + gnttab_free_grant_reference(sh_pages_info->lvl3_gref); + + /* freeing all pages used for 2 level addressing */ + free_pages((unsigned long)sh_pages_info->lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)sh_pages_info->lvl3_table, 1); + + sh_pages_info->lvl3_gref = -1; + sh_pages_info->lvl2_table = NULL; + sh_pages_info->lvl3_table = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} + +/* Maps provided top level ref id and then return array of pages + * containing data refs. + */ +struct page **xen_be_map_shared_pages(unsigned long lvl3_gref, int domid, + int nents, void **refs_info) +{ + struct page *lvl3_table_page; + struct page **lvl2_table_pages; + struct page **data_pages; + struct xen_shared_pages_info *sh_pages_info; + + grant_ref_t *lvl3_table; + grant_ref_t *lvl2_table; + + struct gnttab_map_grant_ref lvl3_map_ops; + struct gnttab_unmap_grant_ref lvl3_unmap_ops; + + struct gnttab_map_grant_ref *lvl2_map_ops; + struct gnttab_unmap_grant_ref *lvl2_unmap_ops; + + struct gnttab_map_grant_ref *data_map_ops; + struct gnttab_unmap_grant_ref *data_unmap_ops; + + /* # of grefs in the last page of lvl2 table */ + int nents_last = (nents - 1) % REFS_PER_PAGE + 1; + int n_lvl2_grefs = (nents / REFS_PER_PAGE) + + ((nents_last > 0) ? 1 : 0) - + (nents_last == REFS_PER_PAGE); + int i, j, k; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + *refs_info = (void *) sh_pages_info; + + lvl2_table_pages = kcalloc(n_lvl2_grefs, sizeof(struct page *), + GFP_KERNEL); + + data_pages = kcalloc(nents, sizeof(struct page *), GFP_KERNEL); + + lvl2_map_ops = kcalloc(n_lvl2_grefs, sizeof(*lvl2_map_ops), + GFP_KERNEL); + + lvl2_unmap_ops = kcalloc(n_lvl2_grefs, sizeof(*lvl2_unmap_ops), + GFP_KERNEL); + + data_map_ops = kcalloc(nents, sizeof(*data_map_ops), GFP_KERNEL); + data_unmap_ops = kcalloc(nents, sizeof(*data_unmap_ops), GFP_KERNEL); + + /* Map top level addressing page */ + if (gnttab_alloc_pages(1, &lvl3_table_page)) { + dev_err(hy_drv_priv->dev, "Cannot allocate pages\n"); + return NULL; + } + + lvl3_table = (grant_ref_t *)pfn_to_kaddr(page_to_pfn(lvl3_table_page)); + + gnttab_set_map_op(&lvl3_map_ops, (unsigned long)lvl3_table, + GNTMAP_host_map | GNTMAP_readonly, + (grant_ref_t)lvl3_gref, domid); + + gnttab_set_unmap_op(&lvl3_unmap_ops, (unsigned long)lvl3_table, + GNTMAP_host_map | GNTMAP_readonly, -1); + + if (gnttab_map_refs(&lvl3_map_ops, NULL, &lvl3_table_page, 1)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed"); + return NULL; + } + + if (lvl3_map_ops.status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d", + lvl3_map_ops.status); + + goto error_cleanup_lvl3; + } else { + lvl3_unmap_ops.handle = lvl3_map_ops.handle; + } + + /* Map all second level pages */ + if (gnttab_alloc_pages(n_lvl2_grefs, lvl2_table_pages)) { + dev_err(hy_drv_priv->dev, "Cannot allocate pages\n"); + goto error_cleanup_lvl3; + } + + for (i = 0; i < n_lvl2_grefs; i++) { + lvl2_table = (grant_ref_t *)pfn_to_kaddr( + page_to_pfn(lvl2_table_pages[i])); + gnttab_set_map_op(&lvl2_map_ops[i], + (unsigned long)lvl2_table, GNTMAP_host_map | + GNTMAP_readonly, + lvl3_table[i], domid); + gnttab_set_unmap_op(&lvl2_unmap_ops[i], + (unsigned long)lvl2_table, GNTMAP_host_map | + GNTMAP_readonly, -1); + } + + /* Unmap top level page, as it won't be needed any longer */ + if (gnttab_unmap_refs(&lvl3_unmap_ops, NULL, + &lvl3_table_page, 1)) { + dev_err(hy_drv_priv->dev, + "xen: cannot unmap top level page\n"); + return NULL; + } + + /* Mark that page was unmapped */ + lvl3_unmap_ops.handle = -1; + + if (gnttab_map_refs(lvl2_map_ops, NULL, + lvl2_table_pages, n_lvl2_grefs)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed"); + return NULL; + } + + /* Checks if pages were mapped correctly */ + for (i = 0; i < n_lvl2_grefs; i++) { + if (lvl2_map_ops[i].status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d", + lvl2_map_ops[i].status); + goto error_cleanup_lvl2; + } else { + lvl2_unmap_ops[i].handle = lvl2_map_ops[i].handle; + } + } + + if (gnttab_alloc_pages(nents, data_pages)) { + dev_err(hy_drv_priv->dev, + "Cannot allocate pages\n"); + goto error_cleanup_lvl2; + } + + k = 0; + + for (i = 0; i < n_lvl2_grefs - 1; i++) { + lvl2_table = pfn_to_kaddr(page_to_pfn(lvl2_table_pages[i])); + for (j = 0; j < REFS_PER_PAGE; j++) { + gnttab_set_map_op(&data_map_ops[k], + (unsigned long)pfn_to_kaddr( + page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, + lvl2_table[j], domid); + + gnttab_set_unmap_op(&data_unmap_ops[k], + (unsigned long)pfn_to_kaddr( + page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, -1); + k++; + } + } + + /* for grefs in the last lvl2 table page */ + lvl2_table = pfn_to_kaddr(page_to_pfn( + lvl2_table_pages[n_lvl2_grefs - 1])); + + for (j = 0; j < nents_last; j++) { + gnttab_set_map_op(&data_map_ops[k], + (unsigned long)pfn_to_kaddr(page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, + lvl2_table[j], domid); + + gnttab_set_unmap_op(&data_unmap_ops[k], + (unsigned long)pfn_to_kaddr(page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, -1); + k++; + } + + if (gnttab_map_refs(data_map_ops, NULL, + data_pages, nents)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed\n"); + return NULL; + } + + /* unmapping lvl2 table pages */ + if (gnttab_unmap_refs(lvl2_unmap_ops, + NULL, lvl2_table_pages, + n_lvl2_grefs)) { + dev_err(hy_drv_priv->dev, + "Cannot unmap 2nd level refs\n"); + return NULL; + } + + /* Mark that pages were unmapped */ + for (i = 0; i < n_lvl2_grefs; i++) + lvl2_unmap_ops[i].handle = -1; + + for (i = 0; i < nents; i++) { + if (data_map_ops[i].status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d\n", + data_map_ops[i].status); + goto error_cleanup_data; + } else { + data_unmap_ops[i].handle = data_map_ops[i].handle; + } + } + + /* store these references for unmapping in the future */ + sh_pages_info->unmap_ops = data_unmap_ops; + sh_pages_info->data_pages = data_pages; + + gnttab_free_pages(1, &lvl3_table_page); + gnttab_free_pages(n_lvl2_grefs, lvl2_table_pages); + kfree(lvl2_table_pages); + kfree(lvl2_map_ops); + kfree(lvl2_unmap_ops); + kfree(data_map_ops); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return data_pages; + +error_cleanup_data: + gnttab_unmap_refs(data_unmap_ops, NULL, data_pages, + nents); + + gnttab_free_pages(nents, data_pages); + +error_cleanup_lvl2: + if (lvl2_unmap_ops[0].handle != -1) + gnttab_unmap_refs(lvl2_unmap_ops, NULL, + lvl2_table_pages, n_lvl2_grefs); + gnttab_free_pages(n_lvl2_grefs, lvl2_table_pages); + +error_cleanup_lvl3: + if (lvl3_unmap_ops.handle != -1) + gnttab_unmap_refs(&lvl3_unmap_ops, NULL, + &lvl3_table_page, 1); + gnttab_free_pages(1, &lvl3_table_page); + + kfree(lvl2_table_pages); + kfree(lvl2_map_ops); + kfree(lvl2_unmap_ops); + kfree(data_map_ops); + + + return NULL; +} + +int xen_be_unmap_shared_pages(void **refs_info, int nents) +{ + struct xen_shared_pages_info *sh_pages_info; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + sh_pages_info = (struct xen_shared_pages_info *)(*refs_info); + + if (sh_pages_info->unmap_ops == NULL || + sh_pages_info->data_pages == NULL) { + dev_warn(hy_drv_priv->dev, + "pages already cleaned up or buffer not imported yet\n"); + return 0; + } + + if (gnttab_unmap_refs(sh_pages_info->unmap_ops, NULL, + sh_pages_info->data_pages, nents)) { + dev_err(hy_drv_priv->dev, "Cannot unmap data pages\n"); + return -EFAULT; + } + + gnttab_free_pages(nents, sh_pages_info->data_pages); + + kfree(sh_pages_info->data_pages); + kfree(sh_pages_info->unmap_ops); + sh_pages_info->unmap_ops = NULL; + sh_pages_info->data_pages = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h new file mode 100644 index 000000000000..d5236b500075 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_SHM_H__ +#define __HYPER_DMABUF_XEN_SHM_H__ + +/* This collects all reference numbers for 2nd level shared pages and + * create a table with those in 1st level shared pages then return reference + * numbers for this top level table. + */ +int xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info); + +int xen_be_unshare_pages(void **refs_info, int nents); + +/* Maps provided top level ref id and then return array of pages containing + * data refs. + */ +struct page **xen_be_map_shared_pages(unsigned long lvl3_gref, int domid, + int nents, + void **refs_info); + +int xen_be_unmap_shared_pages(void **refs_info, int nents); + +#endif /* __HYPER_DMABUF_XEN_SHM_H__ */ diff --git a/include/uapi/linux/hyper_dmabuf.h b/include/uapi/linux/hyper_dmabuf.h new file mode 100644 index 000000000000..cb25299e2666 --- /dev/null +++ b/include/uapi/linux/hyper_dmabuf.h @@ -0,0 +1,134 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __LINUX_PUBLIC_HYPER_DMABUF_H__ +#define __LINUX_PUBLIC_HYPER_DMABUF_H__ + +#define MAX_SIZE_PRIV_DATA 192 + +typedef struct { + int id; + int rng_key[3]; /* 12bytes long random number */ +} hyper_dmabuf_id_t; + +struct hyper_dmabuf_event_hdr { + int event_type; /* one type only for now - new import */ + hyper_dmabuf_id_t hid; /* hyper_dmabuf_id of specific hyper_dmabuf */ + int size; /* size of data */ +}; + +struct hyper_dmabuf_event_data { + struct hyper_dmabuf_event_hdr hdr; + void *data; /* private data */ +}; + +#define IOCTL_HYPER_DMABUF_TX_CH_SETUP \ +_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_hyper_dmabuf_tx_ch_setup)) +struct ioctl_hyper_dmabuf_tx_ch_setup { + /* IN parameters */ + /* Remote domain id */ + int remote_domain; +}; + +#define IOCTL_HYPER_DMABUF_RX_CH_SETUP \ +_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_hyper_dmabuf_rx_ch_setup)) +struct ioctl_hyper_dmabuf_rx_ch_setup { + /* IN parameters */ + /* Source domain id */ + int source_domain; +}; + +#define IOCTL_HYPER_DMABUF_EXPORT_REMOTE \ +_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_hyper_dmabuf_export_remote)) +struct ioctl_hyper_dmabuf_export_remote { + /* IN parameters */ + /* DMA buf fd to be exported */ + int dmabuf_fd; + /* Domain id to which buffer should be exported */ + int remote_domain; + /* exported dma buf id */ + hyper_dmabuf_id_t hid; + int sz_priv; + char *priv; +}; + +#define IOCTL_HYPER_DMABUF_EXPORT_FD \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_hyper_dmabuf_export_fd)) +struct ioctl_hyper_dmabuf_export_fd { + /* IN parameters */ + /* hyper dmabuf id to be imported */ + hyper_dmabuf_id_t hid; + /* flags */ + int flags; + /* OUT parameters */ + /* exported dma buf fd */ + int fd; +}; + +#define IOCTL_HYPER_DMABUF_UNEXPORT \ +_IOC(_IOC_NONE, 'G', 4, sizeof(struct ioctl_hyper_dmabuf_unexport)) +struct ioctl_hyper_dmabuf_unexport { + /* IN parameters */ + /* hyper dmabuf id to be unexported */ + hyper_dmabuf_id_t hid; + /* delay in ms by which unexport processing will be postponed */ + int delay_ms; + /* OUT parameters */ + /* Status of request */ + int status; +}; + +#define IOCTL_HYPER_DMABUF_QUERY \ +_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_hyper_dmabuf_query)) +struct ioctl_hyper_dmabuf_query { + /* in parameters */ + /* hyper dmabuf id to be queried */ + hyper_dmabuf_id_t hid; + /* item to be queried */ + int item; + /* OUT parameters */ + /* Value of queried item */ + unsigned long info; +}; + +/* DMABUF query */ + +enum hyper_dmabuf_query { + HYPER_DMABUF_QUERY_TYPE = 0x10, + HYPER_DMABUF_QUERY_EXPORTER, + HYPER_DMABUF_QUERY_IMPORTER, + HYPER_DMABUF_QUERY_SIZE, + HYPER_DMABUF_QUERY_BUSY, + HYPER_DMABUF_QUERY_UNEXPORTED, + HYPER_DMABUF_QUERY_DELAYED_UNEXPORTED, + HYPER_DMABUF_QUERY_PRIV_INFO_SIZE, + HYPER_DMABUF_QUERY_PRIV_INFO, +}; + +enum hyper_dmabuf_status { + EXPORTED = 0x01, + IMPORTED, +}; + +#endif //__LINUX_PUBLIC_HYPER_DMABUF_H__ diff --git a/include/uapi/xen/Kbuild b/include/uapi/xen/Kbuild new file mode 100644 index 000000000000..5c459628e8c7 --- /dev/null +++ b/include/uapi/xen/Kbuild @@ -0,0 +1,5 @@ +# UAPI Header export list +header-y += evtchn.h +header-y += gntalloc.h +header-y += gntdev.h +header-y += privcmd.h From bc24f393da44582ed1bef4f517a983f319634d5c Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 22 Mar 2018 15:11:05 -0700 Subject: [PATCH 1076/1276] hyper_dmabuf: Enable hyper_dmabuf only on x86 or x86_64 The hyper_dmabuf driver is designed and tested only on x86/x86_64 architecture based systems. Therefore, disable it when trying to build for other architectures. Signed-off-by: Vivek Kasireddy Reviewed-by: Mateusz Polrola --- drivers/dma-buf/hyper_dmabuf/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index 88992167c645..999900b97625 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -3,6 +3,7 @@ menu "hyper_dmabuf options" config HYPER_DMABUF bool "Enables hyper dmabuf driver" default y + depends on (X86=y || X86_64=y || 64BIT=y) choice prompt "Hypervisor" From b34352a95a0e60392f9100050a7351ee888de78c Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 26 Jul 2018 11:24:55 +0800 Subject: [PATCH 1077/1276] hyper_dmabuf: Fix array length check issue in hyper_dmabuf_ioctl() Current boundry check for hyper_dmabuf_ioctls array only verifies whether index value is not greater than total number of elements. But the correct check should be to verify whether index is always less than number of array elements. Change-Id: I711979c270545e02fb878da0eec39b71b451574a Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui Reviewed-by: Wei Liu --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index 20274e1b9e20..e9f1d64ee60b 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -746,7 +746,7 @@ long hyper_dmabuf_ioctl(struct file *filp, hyper_dmabuf_ioctl_t func; char *kdata; - if (nr > ARRAY_SIZE(hyper_dmabuf_ioctls)) { + if (nr >= ARRAY_SIZE(hyper_dmabuf_ioctls)) { dev_err(hy_drv_priv->dev, "invalid ioctl\n"); return -EINVAL; } From c988da4537abaf75897f59fd4fb172f5955ad633 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 19 Jul 2018 15:04:40 +0800 Subject: [PATCH 1078/1276] kernel/hyper_dmabuf: disable hyper_dmabuf on arch arm64 hyper_dmabuf should not be enabled while make allyesconfig on arch arm64, this patch will disable the option and fix the compile error Signed-off-by: Wei Liu --- drivers/dma-buf/hyper_dmabuf/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index 999900b97625..1d91a114ba61 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -3,7 +3,7 @@ menu "hyper_dmabuf options" config HYPER_DMABUF bool "Enables hyper dmabuf driver" default y - depends on (X86=y || X86_64=y || 64BIT=y) + depends on (X86=y || X86_64=y) choice prompt "Hypervisor" From 7bf00dd884994919b1aa3b641eea77bf06a94281 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:37 -0700 Subject: [PATCH 1079/1276] hyper_dmabuf: Remove void* cast in cpu_access function pointers In dma_buf_ops structure init, hyper_dmabuf_ops_begin_cpu_access() and hyper_dmabuf_ops_end_cpu_access() functions are of same type as begin_cpu_access() and end_cpu_access() function declartions. So there is no need for casting them with void*. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index 915743741897..e2bdab75a7cf 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -372,8 +372,8 @@ static const struct dma_buf_ops hyper_dmabuf_ops = { .map_dma_buf = hyper_dmabuf_ops_map, .unmap_dma_buf = hyper_dmabuf_ops_unmap, .release = hyper_dmabuf_ops_release, - .begin_cpu_access = (void *)hyper_dmabuf_ops_begin_cpu_access, - .end_cpu_access = (void *)hyper_dmabuf_ops_end_cpu_access, + .begin_cpu_access = hyper_dmabuf_ops_begin_cpu_access, + .end_cpu_access = hyper_dmabuf_ops_end_cpu_access, .map_atomic = hyper_dmabuf_ops_kmap_atomic, .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, .map = hyper_dmabuf_ops_kmap, From 593853a87d42e0fe2e57598d8787d9ea5b14445e Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:38 -0700 Subject: [PATCH 1080/1276] hyper_dmabuf: Fix incorrect return in hyper_dmabuf_ops_end_cpu_access() In hyper_dmabuf_ops_end_cpu_access(), currently we don't check for return value of sync_request() function and always return zero. This is incorrect. This patch fixes this issue. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index e2bdab75a7cf..a8207b4be20f 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -249,16 +249,13 @@ static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction dir) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return -EINVAL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); - - return 0; + return sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); } static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, From 93a4c92a34524831efc94144022e93c995fb15a5 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:39 -0700 Subject: [PATCH 1081/1276] hyper_dmabuf: Check for NULL value before access work pointer. In delayed_unexport() check for work pointer NULL value before accessing it. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index e9f1d64ee60b..66cdcf6eff78 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -574,14 +574,15 @@ static void delayed_unexport(struct work_struct *work) { struct hyper_dmabuf_req *req; struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; - struct exported_sgt_info *exported = - container_of(work, struct exported_sgt_info, unexport.work); + struct exported_sgt_info *exported; int op[4]; int i, ret; - if (!exported) + if (!work) return; + exported = container_of(work, struct exported_sgt_info, unexport.work); + dev_dbg(hy_drv_priv->dev, "Marking buffer {id:%d key:%d %d %d} as invalid\n", exported->hid.id, exported->hid.rng_key[0], From 198e03189343d2fae359b1ba943bc9e26c6b35fc Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:40 -0700 Subject: [PATCH 1082/1276] hyper_dmabuf: Remove unused variable warnings Remove unused variable warnings in hyper_dmabuf_list.c, hyper_dmabuf_msg.c and hyper_dmabuf_ops.c Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.c | 1 - .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 2 -- .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 27 +++++++------------ 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c index bba6d1d607a8..84cfb065bddd 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c @@ -34,7 +34,6 @@ #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_list.h" #include "hyper_dmabuf_id.h" -#include "hyper_dmabuf_event.h" DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED); DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index 37ee894ec418..c5d99d2f12c9 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -124,11 +124,9 @@ static void cmd_process_work(struct work_struct *work) struct cmd_process, work); struct hyper_dmabuf_req *req; hyper_dmabuf_id_t hid; - int domid; int i; req = proc->rq; - domid = proc->domid; switch (req->cmd) { case HYPER_DMABUF_EXPORT: diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index a8207b4be20f..10b5510b3816 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -106,14 +106,13 @@ static void hyper_dmabuf_ops_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct imported_sgt_info *imported; - int ret; if (!attach->dmabuf->priv) return; imported = (struct imported_sgt_info *)attach->dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); + sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); } static struct sg_table *hyper_dmabuf_ops_map( @@ -169,7 +168,6 @@ static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, enum dma_data_direction dir) { struct imported_sgt_info *imported; - int ret; if (!attachment->dmabuf->priv) return; @@ -181,14 +179,13 @@ static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, sg_free_table(sg); kfree(sg); - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); } static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) { struct imported_sgt_info *imported; struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; - int ret; int finish; if (!dma_buf->priv) @@ -215,7 +212,7 @@ static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) finish = imported && !imported->valid && !imported->importers; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); + sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); /* * Check if buffer is still valid and if not remove it @@ -262,14 +259,13 @@ static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); + sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); /* TODO: NULL for now. Need to return the addr of mapped region */ return NULL; @@ -279,27 +275,25 @@ static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); + sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); } static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); /* for now NULL.. need to return the address of mapped region */ return NULL; @@ -309,14 +303,13 @@ static void hyper_dmabuf_ops_kunmap(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); } static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, @@ -338,14 +331,13 @@ static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); return NULL; } @@ -353,14 +345,13 @@ static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) static void hyper_dmabuf_ops_vunmap(struct dma_buf *dmabuf, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); } static const struct dma_buf_ops hyper_dmabuf_ops = { From 3a14207d5c46779ffcb5a66f56ef80a513ca4a6d Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Wed, 22 Aug 2018 14:27:16 +0200 Subject: [PATCH 1083/1276] hyper_dmabuf/virtio: Protect virtqueue operations with spinlock. virtqueue_add_*/virtqueue_get_buf are not safe to run in parallel as they operate on the same structs, so code that uses them must ensure that they won't run in parallel, as that can lead to corruption of virtqueue data. In case of hyper dmabuf TX queue operations can be run from different threads and need to be protected with spinlock, RX queue operations are always done from one thread and do not require lock. This fixes: RTC252213 Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_fe_drv.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c index 9ae290435d70..5ef8801eb085 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -53,6 +53,11 @@ struct virtio_hdma_fe_priv { struct virtio_comm_ring tx_ring; struct virtio_comm_ring rx_ring; int vmid; + /* + * Lock to protect operations on virtqueue + * which are not safe to run concurrently + */ + spinlock_t lock; }; /* Assuming there will be one FE instance per VM */ @@ -68,6 +73,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) struct virtio_hdma_fe_priv *priv = (struct virtio_hdma_fe_priv *) vq->vdev->priv; int len; + unsigned long flags; if (priv == NULL) { dev_dbg(hy_drv_priv->dev, @@ -75,6 +81,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) return; } + spin_lock_irqsave(&priv->lock, flags); /* Make sure that all pending responses are processed */ while (virtqueue_get_buf(vq, &len)) { if (len == sizeof(struct hyper_dmabuf_req)) { @@ -83,6 +90,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) virtio_comm_ring_pop(&priv->tx_ring); } } + spin_unlock_irqrestore(&priv->lock, flags); } /* @@ -165,6 +173,8 @@ static int virtio_hdma_fe_probe_common(struct virtio_device *vdev) /* Set vmid to -1 to mark that it is not initialized yet */ priv->vmid = -1; + spin_lock_init(&priv->lock); + vdev->priv = priv; ret = virtio_find_vqs(vdev, HDMA_VIRTIO_QUEUE_MAX, @@ -317,6 +327,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; struct hyper_dmabuf_req *tx_req; int timeout = 1000; + unsigned long flags; if (priv == NULL) { dev_err(hy_drv_priv->dev, @@ -337,6 +348,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, return -EBUSY; } + spin_lock_irqsave(&priv->lock, flags); /* Get free buffer for sending request from ring */ tx_req = (struct hyper_dmabuf_req *) virtio_comm_ring_push(&priv->tx_ring); @@ -348,6 +360,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, virtio_hdma_fe_queue_buffer(hyper_dmabuf_virtio_fe, HDMA_VIRTIO_TX_QUEUE, tx_req, sizeof(*tx_req)); + spin_unlock_irqrestore(&priv->lock, flags); if (wait) { while (timeout--) { From 3a8defc4b416915951f953f50e4f85a2120b511b Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Tue, 21 Aug 2018 11:19:52 +0200 Subject: [PATCH 1084/1276] hyper_dmabuf/virtio: Correctly cleanup front end connections Virtio frontends were not fully cleanup on release of VBS-K handle, this change adds helper function that allows to find virtio frontend that was using particular VBS-K handle and clean it up. Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 22 +++++++++++++++++++ .../virtio/hyper_dmabuf_virtio_fe_list.c | 14 ++++++++++++ .../virtio/hyper_dmabuf_virtio_fe_list.h | 5 ++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index bb16360d06d5..a89d557c7c4c 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -319,6 +319,21 @@ static int vbs_k_open(struct inode *inode, struct file *f) return 0; } +static void cleanup_fe(struct virtio_fe_info *fe_info, void *attr) +{ + struct virtio_be_priv *priv = attr; + if (fe_info->priv == priv) { + acrn_ioreq_del_iorange(fe_info->client_id, + priv->dev.io_range_type ? REQ_MMIO : REQ_PORTIO, + priv->dev.io_range_start, + priv->dev.io_range_start + priv->dev.io_range_len); + + acrn_ioreq_destroy_client(fe_info->client_id); + virtio_fe_remove(fe_info->client_id); + kfree(fe_info); + } +} + static int vbs_k_release(struct inode *inode, struct file *f) { struct virtio_be_priv *priv = @@ -333,6 +348,13 @@ static int vbs_k_release(struct inode *inode, struct file *f) kfree(priv->pending_tx_req); virtio_comm_ring_free(&priv->tx_ring); + + /* + * Find and cleanup virtio frontend that + * has been using released vbs k file + */ + virtio_fe_foreach(cleanup_fe, priv); + kfree(priv); return 0; } diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c index 79b30e286b5e..84b6ed5e96c1 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c @@ -97,3 +97,17 @@ int virtio_fe_remove(int client_id) return -ENOENT; } + +void virtio_fe_foreach( + void (*func)(struct virtio_fe_info *, void *attr), + void *attr) +{ + struct virtio_fe_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(virtio_fe_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info, attr); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h index bc7ef843161c..c353c1e5baa1 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h @@ -39,10 +39,13 @@ void virtio_fe_table_init(void); int virtio_fe_add(struct virtio_fe_info *fe_info); -int virtio_remove_fe(int client_id); +int virtio_fe_remove(int client_id); struct virtio_fe_info *virtio_fe_find(int client_id); struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid); +void virtio_fe_foreach(void (*func)(struct virtio_fe_info *, + void *attr), void *attr); + #endif /* __HYPER_DMABUF_VIRTIO_FE_LIST_H__*/ From 6e20f8198e29962c0771b93e6bafcbe1eca264dc Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Tue, 21 Aug 2018 11:19:53 +0200 Subject: [PATCH 1085/1276] hyper_dmabuf/virtio: bugfix on acrn_ioreq_add_iorange() usage Align usage of acrn_ioreq_add_iorange according to description of change: "VBS-K: bugfix on cwp_ioreq_add_iorange() usage": "However, previous VBS-K rng reference driver mistakenly uses "start" and "start + len". This leads to the fact that VBS-K not only hooked "kick" register, VIRTIO_PCI_QUEUE_NOTIFY, but also "status" register, VIRTIO_PCI_STATUS, mistakenly." Signed-off-by: Mateusz Polrola --- .../hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index a89d557c7c4c..c84b2dd746a3 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -234,7 +234,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) ret = acrn_ioreq_add_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); if (ret < 0) { dev_err(hy_drv_priv->dev, @@ -247,7 +247,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) acrn_ioreq_del_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); dev_err(hy_drv_priv->dev, "Failed in vhm_get_vm_info\n"); goto err; @@ -260,7 +260,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) acrn_ioreq_del_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); dev_err(hy_drv_priv->dev, "Failed in acrn_ioreq_get_reqbuf\n"); goto err; @@ -326,7 +326,7 @@ static void cleanup_fe(struct virtio_fe_info *fe_info, void *attr) acrn_ioreq_del_iorange(fe_info->client_id, priv->dev.io_range_type ? REQ_MMIO : REQ_PORTIO, priv->dev.io_range_start, - priv->dev.io_range_start + priv->dev.io_range_len); + priv->dev.io_range_start + priv->dev.io_range_len - 1); acrn_ioreq_destroy_client(fe_info->client_id); virtio_fe_remove(fe_info->client_id); From dac66c1b646f0f609ac88b020a4f05db8c7c6b4a Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Fri, 24 Aug 2018 09:25:02 +0200 Subject: [PATCH 1086/1276] hyper_dmabuf/virtio: Add support for VBS_RESET_DEV ioctl (v2) During VBS_RESET_DEV virtio frontend client will be cleaned up and VBS-K device will be restarted. v2 changes: - call virtio_dev_reset from vbs_k_release Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index c84b2dd746a3..51dd8ed8271a 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -338,13 +338,6 @@ static int vbs_k_release(struct inode *inode, struct file *f) { struct virtio_be_priv *priv = (struct virtio_be_priv *) f->private_data; - int i; - -// virtio_dev_stop(&priv->dev); -// virtio_dev_cleanup(&priv->dev, false); - - for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) - virtio_vq_reset(&priv->vqs[i]); kfree(priv->pending_tx_req); virtio_comm_ring_free(&priv->tx_ring); @@ -355,10 +348,25 @@ static int vbs_k_release(struct inode *inode, struct file *f) */ virtio_fe_foreach(cleanup_fe, priv); + virtio_dev_reset(&priv->dev); + kfree(priv); return 0; } +static int vbs_k_reset(struct virtio_be_priv *priv) +{ + virtio_comm_ring_free(&priv->tx_ring); + + virtio_fe_foreach(cleanup_fe, priv); + + virtio_dev_reset(&priv->dev); + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct virtio_be_tx_data), + REQ_RING_SIZE); +} + static long vbs_k_ioctl(struct file *f, unsigned int ioctl, unsigned long arg) { @@ -374,19 +382,25 @@ static long vbs_k_ioctl(struct file *f, unsigned int ioctl, return -EINVAL; } - if (ioctl == VBS_SET_VQ) { - /* Overridden to call additionally - * virtio_be_register_vhm_client */ - r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); - if (r == -ENOIOCTLCMD) - return -EFAULT; - - if (virtio_be_register_vhm_client(&priv->dev) < 0) - return -EFAULT; - } else { - r = virtio_dev_ioctl(&priv->dev, ioctl, argp); - if (r == -ENOIOCTLCMD) + switch(ioctl) { + case VBS_SET_VQ: + /* Overridden to call additionally + * virtio_be_register_vhm_client */ r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + return -EFAULT; + + if (virtio_be_register_vhm_client(&priv->dev) < 0) + return -EFAULT; + break; + case VBS_RESET_DEV: + vbs_k_reset(priv); + break; + default: + r = virtio_dev_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + break; } return r; From af086bc5b5e9c5c60e9b0596baa1d2a03e28eb22 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Fri, 24 Aug 2018 09:25:03 +0200 Subject: [PATCH 1087/1276] hyper_dmabuf/virtio: Handle S3 resume correctly (v2) After resume from S3 virtqueues are reset, so buffers for communication from SOS to UOS needs to be reinitialized. That cannot be done directly in resume routine, as that point virtio PCI device is not yet fully restored, because of that it has to be scheduled to run after some short delay. v2: changes: - schedule reinit of communication from SOS to UOS immediately at the end of restore routine - Wait for virtio device to become ready before actually doing reinit of communication Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_fe_drv.c | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c index 5ef8801eb085..e0c811135699 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "../hyper_dmabuf_msg.h" #include "../hyper_dmabuf_drv.h" #include "hyper_dmabuf_virtio_common.h" @@ -222,14 +223,20 @@ static void virtio_hdma_fe_remove(struct virtio_device *vdev) virtio_hdma_fe_remove_common(vdev); } +struct virtio_hdma_restore_work +{ + struct work_struct work; + struct virtio_device *dev; +}; + /* * Queues empty requests buffers to backend, * which will be used by it to send requests back to frontend. */ -static void virtio_hdma_fe_scan(struct virtio_device *vdev) +static void virtio_hdma_query_vmid(struct virtio_device *vdev) { - struct virtio_hdma_fe_priv *priv = - (struct virtio_hdma_fe_priv *) vdev->priv; + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; struct hyper_dmabuf_req *rx_req; int timeout = 1000; @@ -266,6 +273,29 @@ static void virtio_hdma_fe_scan(struct virtio_device *vdev) } } +/* + * Queues empty requests buffers to backend, + * which will be used by it to send requests back to frontend. + */ +static void virtio_hdma_fe_scan(struct virtio_device *vdev) +{ + virtio_hdma_query_vmid(vdev); +} + +static void virtio_hdma_restore_bh(struct work_struct *w) +{ + struct virtio_hdma_restore_work *work = + (struct virtio_hdma_restore_work *) w; + + while (!(VIRTIO_CONFIG_S_DRIVER_OK & + work->dev->config->get_status(work->dev))) { + usleep_range(100, 120); + } + + virtio_hdma_query_vmid(work->dev); + kfree(w); +} + #ifdef CONFIG_PM_SLEEP static int virtio_hdma_fe_freeze(struct virtio_device *vdev) { @@ -275,7 +305,18 @@ static int virtio_hdma_fe_freeze(struct virtio_device *vdev) static int virtio_hdma_fe_restore(struct virtio_device *vdev) { - return virtio_hdma_fe_probe_common(vdev); + struct virtio_hdma_restore_work *work; + int ret; + + ret = virtio_hdma_fe_probe_common(vdev); + if (!ret) { + work = kmalloc(sizeof(*work), GFP_KERNEL); + INIT_WORK(&work->work, virtio_hdma_restore_bh); + work->dev = vdev; + schedule_work(&work->work); + } + + return ret; } #endif From d7cc9c67e08931b0a469fa46cbe5ff279b591c7a Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 27 Aug 2018 09:34:15 +0200 Subject: [PATCH 1088/1276] hyper_dmabuf: fix map failure issue when assign 4G memory to UOS (v2) When assign 4G memory to UOS, 32bit GPA ref_handle will overflow, instead we need set ref_handle as unsigned long to fix this issue. op is int type, then we need use two int op[7] and op[8] to pass ref_handle. v2 changes: - Aligned Xen backend with backend interface changes Change-Id: Ibc827e54897b0b48a4056d8df400d0bf4b3e923f Signed-off-by: Fei Jiang Signed-off-by: Mateusz Polrola --- .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h | 2 +- .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 15 ++++---- .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 35 ++++++++++--------- .../hyper_dmabuf/hyper_dmabuf_struct.h | 2 +- .../virtio/hyper_dmabuf_virtio_shm.c | 8 ++--- .../virtio/hyper_dmabuf_virtio_shm.h | 2 +- .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c | 4 +-- .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h | 4 +-- 8 files changed, 39 insertions(+), 33 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h index 45c24fd8d25d..ad4839b9c0f2 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h @@ -83,7 +83,7 @@ struct hyper_dmabuf_bknd_ops { int (*get_vm_id)(void); /* get pages shared via hypervisor-specific method */ - int (*share_pages)(struct page **, int, int, void **); + long (*share_pages)(struct page **, int, int, void **); /* make shared pages unshared via hypervisor specific method */ int (*unshare_pages)(void **, int); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index 66cdcf6eff78..62f83cc45f36 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -86,6 +86,7 @@ static int send_export_msg(struct exported_sgt_info *exported, struct hyper_dmabuf_req *req; int op[MAX_NUMBER_OF_OPERANDS] = {0}; int ret, i; + long tmp; /* now create request for importer via ring */ op[0] = exported->hid.id; @@ -97,18 +98,20 @@ static int send_export_msg(struct exported_sgt_info *exported, op[4] = pg_info->nents; op[5] = pg_info->frst_ofst; op[6] = pg_info->last_len; - op[7] = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, + tmp = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, pg_info->nents, &exported->refs_info); - if (op[7] < 0) { + if (tmp < 0) { dev_err(hy_drv_priv->dev, "pages sharing failed\n"); - return op[7]; + return tmp; } + op[7] = tmp & 0xffffffff; + op[8] = (tmp >> 32) & 0xffffffff; } - op[8] = exported->sz_priv; + op[9] = exported->sz_priv; /* driver/application specific private info */ - memcpy(&op[9], exported->priv, op[8]); + memcpy(&op[10], exported->priv, op[9]); req = kcalloc(1, sizeof(*req), GFP_KERNEL); @@ -501,7 +504,7 @@ static int hyper_dmabuf_export_fd_ioctl(struct file *filp, void *data) ret = 0; dev_dbg(hy_drv_priv->dev, - "Found buffer gref %d off %d\n", + "Found buffer gref 0x%lx off %d\n", imported->ref_handle, imported->frst_ofst); dev_dbg(hy_drv_priv->dev, diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index c5d99d2f12c9..d91a9eb3bed8 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -59,13 +59,14 @@ void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, * op4 : number of pages to be shared * op5 : offset of data in the first page * op6 : length of data in the last page - * op7 : top-level reference number for shared pages - * op8 : size of private data (from op9) - * op9 ~ : Driver-specific private data + * op7 : 32 LSB of top-level reference number for shared pages + * op8 : 32 MSB of top-level reference number for shared pages + * op9 : size of private data (from op9) + * op10 ~ : Driver-specific private data * (e.g. graphic buffer's meta info) */ - memcpy(&req->op[0], &op[0], 9 * sizeof(int) + op[8]); + memcpy(&req->op[0], &op[0], 10 * sizeof(int) + op[9]); break; case HYPER_DMABUF_NOTIFY_UNEXPORT: @@ -136,9 +137,10 @@ static void cmd_process_work(struct work_struct *work) * op4 : number of pages to be shared * op5 : offset of data in the first page * op6 : length of data in the last page - * op7 : top-level reference number for shared pages - * op8 : size of private data (from op9) - * op9 ~ : Driver-specific private data + * op7 : 32 LSB of top-level reference number for shared pages + * op8 : 32 MSB of top-level reference number for shared pages + * op9 : size of private data (from op9) + * op10 ~ : Driver-specific private data * (e.g. graphic buffer's meta info) */ @@ -162,10 +164,10 @@ static void cmd_process_work(struct work_struct *work) /* if size of new private data is different, * we reallocate it. */ - if (imported->sz_priv != req->op[8]) { + if (imported->sz_priv != req->op[9]) { kfree(imported->priv); - imported->sz_priv = req->op[8]; - imported->priv = kcalloc(1, req->op[8], + imported->sz_priv = req->op[9]; + imported->priv = kcalloc(1, req->op[9], GFP_KERNEL); if (!imported->priv) { /* set it invalid */ @@ -175,7 +177,7 @@ static void cmd_process_work(struct work_struct *work) } /* updating priv data */ - memcpy(imported->priv, &req->op[9], req->op[8]); + memcpy(imported->priv, &req->op[10], req->op[9]); #ifdef CONFIG_HYPER_DMABUF_EVENT_GEN /* generating import event */ @@ -190,8 +192,8 @@ static void cmd_process_work(struct work_struct *work) if (!imported) break; - imported->sz_priv = req->op[8]; - imported->priv = kcalloc(1, req->op[8], GFP_KERNEL); + imported->sz_priv = req->op[9]; + imported->priv = kcalloc(1, req->op[9], GFP_KERNEL); if (!imported->priv) { kfree(imported); @@ -206,7 +208,7 @@ static void cmd_process_work(struct work_struct *work) imported->nents = req->op[4]; imported->frst_ofst = req->op[5]; imported->last_len = req->op[6]; - imported->ref_handle = req->op[7]; + imported->ref_handle = (u64)req->op[8] << 32 | req->op[7]; dev_dbg(hy_drv_priv->dev, "DMABUF was exported\n"); dev_dbg(hy_drv_priv->dev, "\thid{id:%d key:%d %d %d}\n", @@ -215,9 +217,10 @@ static void cmd_process_work(struct work_struct *work) dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); - dev_dbg(hy_drv_priv->dev, "\tgrefid %d\n", req->op[7]); + dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%lx\n", + (u64)req->op[8] << 32 | req->op[7]); - memcpy(imported->priv, &req->op[9], req->op[8]); + memcpy(imported->priv, &req->op[10], req->op[9]); imported->valid = true; hyper_dmabuf_register_imported(imported); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h index a11f804edfb3..f7b7de0e1432 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h @@ -117,7 +117,7 @@ struct imported_sgt_info { hyper_dmabuf_id_t hid; /* unique id for shared dmabuf imported */ /* hypervisor-specific handle to pages */ - int ref_handle; + unsigned long ref_handle; /* offset and size info of DMA_BUF */ int frst_ofst; diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c index be5141c25191..b18f7cae0115 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c @@ -56,7 +56,7 @@ struct virtio_shared_pages_info { #endif #ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE -static int virtio_be_share_pages(struct page **pages, +static long virtio_be_share_pages(struct page **pages, int vmid, int nents, void **refs_info) @@ -208,7 +208,7 @@ static int virtio_be_unmap_shared_pages(void **refs_info, int nents) return 0; } #else -static int virtio_fe_share_pages(struct page **pages, +static long virtio_fe_share_pages(struct page **pages, int domid, int nents, void **refs_info) { @@ -292,11 +292,11 @@ static int virtio_fe_unmap_shared_pages(void **refs_info, int nents) #endif -int virtio_share_pages(struct page **pages, +long virtio_share_pages(struct page **pages, int domid, int nents, void **refs_info) { - int ret; + long ret; #ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE ret = virtio_be_share_pages(pages, domid, nents, refs_info); #else diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h index 05cbf5779f86..55f3e13ef2df 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h @@ -25,7 +25,7 @@ #ifndef __HYPER_DMABUF_VIRTIO_SHM_H__ #define __HYPER_DMABUF_VIRTIO_SHM_H__ -int virtio_share_pages(struct page **pages, +long virtio_share_pages(struct page **pages, int domid, int nents, void **refs_info); diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c index c6a15f187fe3..5889485125e0 100644 --- a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c @@ -73,8 +73,8 @@ * * Returns refid of top level page. */ -int xen_be_share_pages(struct page **pages, int domid, int nents, - void **refs_info) +long xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info) { grant_ref_t lvl3_gref; grant_ref_t *lvl2_table; diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h index d5236b500075..f23deb394a00 100644 --- a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h @@ -29,8 +29,8 @@ * create a table with those in 1st level shared pages then return reference * numbers for this top level table. */ -int xen_be_share_pages(struct page **pages, int domid, int nents, - void **refs_info); +long xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info); int xen_be_unshare_pages(void **refs_info, int nents); From 55e369bdbdfd5742b21ea72b15c69db3afaf61b3 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 19 Sep 2018 16:21:22 +0800 Subject: [PATCH 1089/1276] hyper_dmabuf: fix compile warnings in hyper_dmabuf This patch fix the compile warnings in hyper_dmabuf Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1286 Signed-off-by: Wei Liu Reviewed-by: Jason Chen CJ --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index d91a9eb3bed8..fe9e4e2339a1 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -217,7 +217,7 @@ static void cmd_process_work(struct work_struct *work) dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); - dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%lx\n", + dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%llx\n", (u64)req->op[8] << 32 | req->op[7]); memcpy(imported->priv, &req->op[10], req->op[9]); From a407cf985367826fb2d5f234b3492928da07825c Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 12:12:52 +0200 Subject: [PATCH 1090/1276] hyper_dmabuf/virtio: Adapt to the new state transition of VHM requests Instead of using two members (namely ''valid'' and ''processed''), the new state transition uses a single member (i.e. ''processed) following the transition pattern below. FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... Additionally atomic operations should be used to access the state. Signed-off-by: Mateusz Polrola --- .../hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 51dd8ed8271a..400c6e702005 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -179,15 +179,15 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) for (i = 0; i < fe_info->max_vcpu; ++i) { req = &fe_info->req_buf[i]; - if (req->valid && - req->processed == REQ_STATE_PROCESSING && + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == fe_info->client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) req->reqs.pio_request.value = 0; else val = req->reqs.pio_request.value; - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); acrn_ioreq_complete_request(fe_info->client_id, i); } } From fef8a84937d43d59492499cf9fbf7fcc45564e06 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:09:19 +0200 Subject: [PATCH 1091/1276] hyper_dmabuf/virtio: Process ioreq according to bitmap Vhm will record pending ioreqs of the vhm client into a bitmap, then vhm client (like hyper_dmabuf) can process the ioreq directly according to the bitmap. Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 400c6e702005..67c79683407b 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -161,15 +161,12 @@ static void virtio_be_handle_vq_kick( /* * Received new buffer in virtqueue */ -static int virtio_be_handle_kick(int client_id, int req_cnt) +static int virtio_be_handle_kick(int client_id, unsigned long *ioreqs_map) { int val = -1; struct vhm_request *req; struct virtio_fe_info *fe_info; - int i; - - if (unlikely(req_cnt <= 0)) - return -EINVAL; + int vcpu; fe_info = virtio_fe_find(client_id); if (fe_info == NULL) { @@ -177,8 +174,11 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) return -EINVAL; } - for (i = 0; i < fe_info->max_vcpu; ++i) { - req = &fe_info->req_buf[i]; + while (1) { + vcpu = find_first_bit(ioreqs_map, fe_info->max_vcpu); + if (vcpu == fe_info->max_vcpu) + break; + req = &fe_info->req_buf[vcpu]; if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == fe_info->client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) @@ -188,7 +188,7 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) smp_mb(); atomic_set(&req->processed, REQ_STATE_COMPLETE); - acrn_ioreq_complete_request(fe_info->client_id, i); + acrn_ioreq_complete_request(fe_info->client_id, vcpu); } } From b7917b27e685acec55c48c8de023a841d2d4bacc Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:09:37 +0200 Subject: [PATCH 1092/1276] hyper_dmabuf/virtio: Fixed compilation warnings Added missing return in vbs_k_reset. Initialized return value in vbs_k_ioctl. Signed-off-by: Mateusz Polrola --- .../dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 67c79683407b..b308d7e00d18 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -365,6 +365,8 @@ static int vbs_k_reset(struct virtio_be_priv *priv) virtio_comm_ring_init(&priv->tx_ring, sizeof(struct virtio_be_tx_data), REQ_RING_SIZE); + + return 0; } static long vbs_k_ioctl(struct file *f, unsigned int ioctl, @@ -373,7 +375,7 @@ static long vbs_k_ioctl(struct file *f, unsigned int ioctl, struct virtio_be_priv *priv = (struct virtio_be_priv *) f->private_data; void __user *argp = (void __user *)arg; - int r; + int r = 0; if (priv == NULL) { dev_err(hy_drv_priv->dev, From 398e76518d11e9d070a0bfcdc0c7700df6e9c26b Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:01:22 +0200 Subject: [PATCH 1093/1276] hyper_dmabuf: Align with dma_buf_ops changes In 4.17.0 kernel, map_atomic and unmap_atomic callbacks were removed from dma_buf_ops, additionally device param for attach callback was removed. Signed-off-by: Mateusz Polrola --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 7 +++++++ drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index 10b5510b3816..3bd13c584ffc 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_struct.h" #include "hyper_dmabuf_ops.h" @@ -86,7 +87,9 @@ static int sync_request(hyper_dmabuf_id_t hid, int dmabuf_ops) } static int hyper_dmabuf_ops_attach(struct dma_buf *dmabuf, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) struct device *dev, +#endif struct dma_buf_attachment *attach) { struct imported_sgt_info *imported; @@ -255,6 +258,7 @@ static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, return sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) { @@ -283,6 +287,7 @@ static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); } +#endif static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) { @@ -362,8 +367,10 @@ static const struct dma_buf_ops hyper_dmabuf_ops = { .release = hyper_dmabuf_ops_release, .begin_cpu_access = hyper_dmabuf_ops_begin_cpu_access, .end_cpu_access = hyper_dmabuf_ops_end_cpu_access, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) .map_atomic = hyper_dmabuf_ops_kmap_atomic, .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, +#endif .map = hyper_dmabuf_ops_kmap, .unmap = hyper_dmabuf_ops_kunmap, .mmap = hyper_dmabuf_ops_mmap, diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c index a82fd7b087b8..3cd3d6c98c33 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_struct.h" #include "hyper_dmabuf_list.h" @@ -219,10 +220,12 @@ int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) return -ENOMEM; /* dummy kmapping of 1 page */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) if (ops == HYPER_DMABUF_OPS_KMAP_ATOMIC) va_kmapl->vaddr = dma_buf_kmap_atomic( exported->dma_buf, 1); else +#endif va_kmapl->vaddr = dma_buf_kmap( exported->dma_buf, 1); @@ -253,11 +256,13 @@ int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) return PTR_ERR(va_kmapl->vaddr); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) /* unmapping 1 page */ if (ops == HYPER_DMABUF_OPS_KUNMAP_ATOMIC) dma_buf_kunmap_atomic(exported->dma_buf, 1, va_kmapl->vaddr); else +#endif dma_buf_kunmap(exported->dma_buf, 1, va_kmapl->vaddr); From 814382dbac280aaa2c8224f36049249fb57ac5d3 Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 9 Oct 2018 16:41:21 +0800 Subject: [PATCH 1094/1276] drm/i915: diable huge page ppgtt when using PVMMIO ppgtt update When using PVMMIO ppgtt update, it's too complex and has performance impact to support huge page PPGTT, so we will disable this feature in this patch. Tracked-On: projectacrn/acrn-hypervisor#1413 Signed-off-by: Min He Reviewed-by: Xinyun Liu --- drivers/gpu/drm/i915/i915_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c3e87d10f14e..23478d7edc94 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5462,7 +5462,8 @@ int i915_gem_init(struct drm_i915_private *dev_priv) int ret; /* We need to fallback to 4K pages if host doesn't support huge gtt. */ - if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) + if ((intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) + || PVMMIO_LEVEL(dev_priv, PVMMIO_PPGTT_UPDATE)) mkwrite_device_info(dev_priv)->page_sizes = I915_GTT_PAGE_SIZE_4K; From 96a8a43abc9a5d4259d68a238860d26326338a3e Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 8 Mar 2018 15:10:34 -0800 Subject: [PATCH 1095/1276] INTERNAL [IOTG] drm/i915: Decouple pipe and crtc index dependencies i915 driver had assumptions that pipe and the crtc index always matched, but with plane restrictions feature, this assuumption is no longer true. In virualized environment guest Oses may have pipes with no planes attached and no crtc should be created for that pipe (but for service OS, CRTC still needs to be created to do the initial modeset). In cases were no CRTC was created for some pipes, the index and pipe won't match and was causing issues. These changes are to decouple the pipe and index dependencies in the driver. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Anitha Chrisanthus Signed-off-by: Vivek Kasireddy Change-Id: Iccf6b54c254e2b3d7053620c1b64ad8dda7632b8 --- drivers/gpu/drm/i915/i915_drv.h | 10 +++--- drivers/gpu/drm/i915/i915_irq.c | 32 ++++++++++++----- drivers/gpu/drm/i915/i915_trace.h | 2 +- drivers/gpu/drm/i915/intel_display.c | 40 ++++++++++++++++++---- drivers/gpu/drm/i915/intel_dpll_mgr.c | 5 +-- drivers/gpu/drm/i915/intel_drv.h | 10 +++++- drivers/gpu/drm/i915/intel_fifo_underrun.c | 11 ++++-- drivers/gpu/drm/i915/intel_pm.c | 7 ++-- 8 files changed, 87 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 18650bc9c8e4..e477f8a0764f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2822,18 +2822,18 @@ ilk_disable_display_irq(struct drm_i915_private *dev_priv, uint32_t bits) ilk_update_display_irq(dev_priv, bits, 0); } void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, + unsigned int crtc_index, uint32_t interrupt_mask, uint32_t enabled_irq_mask); static inline void bdw_enable_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, uint32_t bits) + unsigned int crtc_index, uint32_t bits) { - bdw_update_pipe_irq(dev_priv, pipe, bits, bits); + bdw_update_pipe_irq(dev_priv, crtc_index, bits, bits); } static inline void bdw_disable_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, uint32_t bits) + unsigned int crtc_index, uint32_t bits) { - bdw_update_pipe_irq(dev_priv, pipe, bits, 0); + bdw_update_pipe_irq(dev_priv, crtc_index, bits, 0); } void ibx_display_interrupt_update(struct drm_i915_private *dev_priv, uint32_t interrupt_mask, diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 71977fb8725c..5e0e9f189418 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -626,11 +626,12 @@ static void bdw_update_port_irq(struct drm_i915_private *dev_priv, * @enabled_irq_mask: mask of interrupt bits to enable */ void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, + unsigned int crtc_index, uint32_t interrupt_mask, uint32_t enabled_irq_mask) { uint32_t new_val; + enum pipe pipe; lockdep_assert_held(&dev_priv->irq_lock); @@ -639,6 +640,9 @@ void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; + if(get_pipe_from_crtc_index(&dev_priv->drm, crtc_index, &pipe)) + return; + new_val = dev_priv->de_irq_mask[pipe]; new_val &= ~interrupt_mask; new_val |= (~enabled_irq_mask & interrupt_mask); @@ -884,9 +888,14 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff; } -static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe) +static u32 g4x_get_vblank_counter(struct drm_device *dev, + unsigned int crtc_index) { struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe; + + if(get_pipe_from_crtc_index(dev, crtc_index, &pipe)) + return 0; return I915_READ(PIPE_FRMCOUNT_G4X(pipe)); } @@ -1002,18 +1011,21 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) return (position + crtc->scanline_offset) % vtotal; } -static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, - bool in_vblank_irq, int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime, - const struct drm_display_mode *mode) +static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int crtc_index, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv, - pipe); + struct intel_crtc *intel_crtc; + enum pipe pipe; int position; int vbl_start, vbl_end, hsync_start, htotal, vtotal; unsigned long irqflags; + intel_crtc = get_intel_crtc_from_index(dev, crtc_index); + pipe = intel_crtc->pipe; + if (WARN_ON(!mode->crtc_clock)) { DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled " "pipe %c\n", pipe_name(pipe)); @@ -2748,6 +2760,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) irqreturn_t ret = IRQ_NONE; u32 iir; enum pipe pipe; + struct intel_crtc *crtc; if (master_ctl & GEN8_DE_MISC_IRQ) { iir = I915_READ(GEN8_DE_MISC_IIR); @@ -2858,8 +2871,9 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); + crtc = intel_get_crtc_for_pipe(dev_priv, pipe); if (iir & GEN8_PIPE_VBLANK) { - drm_handle_vblank(&dev_priv->drm, pipe); + drm_handle_vblank(&dev_priv->drm, drm_crtc_index(&crtc->base)); #if IS_ENABLED(CONFIG_DRM_I915_GVT) gvt_notify_vblank(dev_priv, pipe); #endif diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index af592e3d09a9..6f25961ad9ad 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -281,7 +281,7 @@ TRACE_EVENT(i915_pipe_update_start, TP_fast_assign( __entry->pipe = crtc->pipe; __entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev, - crtc->pipe); + drm_crtc_index(&crtc->base)); __entry->scanline = intel_get_crtc_scanline(crtc); __entry->min = crtc->debug.min_vbl; __entry->max = crtc->debug.max_vbl; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bc4d36c1a672..fe8952f6cdad 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11949,11 +11949,13 @@ verify_single_dpll_state(struct drm_i915_private *dev_priv, if (new_state->active) I915_STATE_WARN(!(pll->active_mask & crtc_mask), "pll active mismatch (expected pipe %c in active mask 0x%02x)\n", - pipe_name(drm_crtc_index(crtc)), pll->active_mask); + pipe_name(to_intel_crtc(crtc)->pipe), + pll->active_mask); else I915_STATE_WARN(pll->active_mask & crtc_mask, "pll active mismatch (didn't expect pipe %c in active mask 0x%02x)\n", - pipe_name(drm_crtc_index(crtc)), pll->active_mask); + pipe_name(to_intel_crtc(crtc)->pipe), + pll->active_mask); I915_STATE_WARN(!(pll->state.crtc_mask & crtc_mask), "pll enabled crtcs mismatch (expected 0x%x in 0x%02x)\n", @@ -11984,10 +11986,10 @@ verify_shared_dpll_state(struct drm_device *dev, struct drm_crtc *crtc, I915_STATE_WARN(pll->active_mask & crtc_mask, "pll active mismatch (didn't expect pipe %c in active mask)\n", - pipe_name(drm_crtc_index(crtc))); + pipe_name(to_intel_crtc(crtc)->pipe)); I915_STATE_WARN(pll->state.crtc_mask & crtc_mask, "pll enabled crtcs mismatch (found %x in enabled mask)\n", - pipe_name(drm_crtc_index(crtc))); + pipe_name(to_intel_crtc(crtc)->pipe)); } } @@ -12402,7 +12404,8 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) if (!dev->max_vblank_count) return (u32)drm_crtc_accurate_vblank_count(&crtc->base); - return dev->driver->get_vblank_counter(dev, crtc->pipe); + return dev->driver->get_vblank_counter(dev, + drm_crtc_index(&crtc->base)); } static void intel_update_crtc(struct drm_crtc *crtc, @@ -14058,6 +14061,27 @@ int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data, return 0; } +int get_pipe_from_crtc_index(struct drm_device *dev, unsigned int index, enum pipe *pipe) +{ + struct drm_crtc *c = drm_crtc_from_index(dev, index); + + if (WARN_ON(!c)) + return -ENOENT; + + *pipe = (to_intel_crtc(c)->pipe); + return 0; +} + +struct intel_crtc *get_intel_crtc_from_index(struct drm_device *dev, + unsigned int index) +{ + struct drm_crtc *c = drm_crtc_from_index(dev, index); + + WARN_ON(!c); + return to_intel_crtc(c); +} + + static int intel_encoder_clones(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -15644,7 +15668,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) crtc->active = crtc_state->base.active; if (crtc_state->base.active) - dev_priv->active_crtcs |= 1 << crtc->pipe; + dev_priv->active_crtcs |= + 1 << drm_crtc_index(&crtc->base); readout_plane_state(crtc); @@ -15665,7 +15690,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) if (crtc_state->base.active && crtc_state->shared_dpll == pll) - pll->state.crtc_mask |= 1 << crtc->pipe; + pll->state.crtc_mask |= + 1 << drm_crtc_index(&crtc->base); } pll->active_mask = pll->state.crtc_mask; diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c index b51ad2917dbe..47b1dfe06152 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c @@ -303,7 +303,7 @@ intel_reference_shared_dpll(struct intel_shared_dpll *pll, DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->info->name, pipe_name(crtc->pipe)); - shared_dpll[id].crtc_mask |= 1 << crtc->pipe; + shared_dpll[id].crtc_mask |= 1 << (drm_crtc_index(&crtc->base)); } /** @@ -3285,7 +3285,8 @@ void intel_release_shared_dpll(struct intel_shared_dpll *dpll, struct intel_shared_dpll_state *shared_dpll_state; shared_dpll_state = intel_atomic_get_shared_dpll_state(state); - shared_dpll_state[dpll->info->id].crtc_mask &= ~(1 << crtc->pipe); + shared_dpll_state[dpll->info->id].crtc_mask &= + ~(1 << drm_crtc_index(&crtc->base)); } /** diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 098c2886cf7e..49d4282b234b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1520,6 +1520,9 @@ enum tc_port intel_port_to_tc(struct drm_i915_private *dev_priv, enum pipe intel_get_pipe_from_connector(struct intel_connector *connector); int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int get_pipe_from_crtc_index(struct drm_device *dev, unsigned int index, enum pipe *pipe); +struct intel_crtc *get_intel_crtc_from_index(struct drm_device *dev, + unsigned int index); enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, enum pipe pipe); static inline bool @@ -1539,7 +1542,12 @@ intel_crtc_has_dp_encoder(const struct intel_crtc_state *crtc_state) static inline void intel_wait_for_vblank(struct drm_i915_private *dev_priv, enum pipe pipe) { - drm_wait_one_vblank(&dev_priv->drm, pipe); + struct intel_crtc *crtc; + + crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + if (crtc) + drm_wait_one_vblank(&dev_priv->drm, + drm_crtc_index(&crtc->base)); } static inline void intel_wait_for_vblank_if_active(struct drm_i915_private *dev_priv, int pipe) diff --git a/drivers/gpu/drm/i915/intel_fifo_underrun.c b/drivers/gpu/drm/i915/intel_fifo_underrun.c index 77c123cc8817..83ed6f56ed56 100644 --- a/drivers/gpu/drm/i915/intel_fifo_underrun.c +++ b/drivers/gpu/drm/i915/intel_fifo_underrun.c @@ -181,11 +181,18 @@ static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev, enum pipe pipe, bool enable) { struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + if (!crtc) { + DRM_DEBUG("No crtc for pipe=%d\n", pipe); + return; + } if (enable) - bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN); + bdw_enable_pipe_irq(dev_priv, drm_crtc_index(&crtc->base), + GEN8_PIPE_FIFO_UNDERRUN); else - bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN); + bdw_disable_pipe_irq(dev_priv, drm_crtc_index(&crtc->base), + GEN8_PIPE_FIFO_UNDERRUN); } static void ibx_set_fifo_underrun_reporting(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c7c7b5e78330..1884b8140131 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3710,7 +3710,6 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) struct intel_crtc *crtc; struct intel_plane *plane; struct intel_crtc_state *cstate; - enum pipe pipe; int level, latency; int sagv_block_time_us; @@ -3736,8 +3735,10 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) return false; /* Since we're now guaranteed to only have one active CRTC... */ - pipe = ffs(intel_state->active_crtcs) - 1; - crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + crtc = get_intel_crtc_from_index(dev, + ffs(intel_state->active_crtcs) - 1); + if (!crtc) + return false; cstate = to_intel_crtc_state(crtc->base.state); if (crtc->base.state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) From a1ec6ccd77cafe73f56bdd5777fe64d72429c5e9 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 8 Mar 2018 11:14:57 -0800 Subject: [PATCH 1096/1276] INTERNAL [IOTG] drm: Don't assume that the primary plane always exists In some virtualization use-cases, the operating system and hence the graphics drivers may have to deal with a crtc(s) that does not have a primary plane associated with it. For such cases, the legacy non-atomic APIs should check for the existence of the primary plane and return -EINVAL if it's not present. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Vivek Kasireddy Change-Id: Ie16c4bdd19a3df25ca5c4241ded12b87ca5a106a --- drivers/gpu/drm/drm_atomic_helper.c | 5 +++- drivers/gpu/drm/drm_crtc.c | 13 ++++++++++ drivers/gpu/drm/drm_crtc_helper.c | 10 +++++++ drivers/gpu/drm/drm_fb_helper.c | 2 ++ drivers/gpu/drm/drm_framebuffer.c | 2 +- drivers/gpu/drm/i915/i915_debugfs.c | 39 ++++++++++++++++++---------- drivers/gpu/drm/i915/intel_display.c | 14 +++++----- drivers/gpu/drm/i915/intel_fbdev.c | 11 ++++++-- 8 files changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1bb4c318bdd4..07cedcd2989a 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1074,7 +1074,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, crtc->enabled = new_crtc_state->enable; new_plane_state = - drm_atomic_get_new_plane_state(old_state, primary); + primary ? drm_atomic_get_new_plane_state(old_state, primary) : NULL; if (new_plane_state && new_plane_state->crtc == crtc) { crtc->x = new_plane_state->src_x >> 16; @@ -2916,6 +2916,9 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set, int hdisplay, vdisplay; int ret; + if (!crtc->primary) + return -EINVAL; + crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9cbe8f5c9aca..bd4684395600 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -410,6 +410,9 @@ int drm_mode_getcrtc(struct drm_device *dev, plane = crtc->primary; + if (!crtc->primary) + return -EINVAL; + crtc_resp->gamma_size = crtc->gamma_size; drm_modeset_lock(&plane->mutex, NULL); @@ -461,6 +464,9 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, struct drm_crtc *tmp; int ret; + if (!crtc->primary) + return -EINVAL; + WARN_ON(drm_drv_uses_atomic_modeset(crtc->dev)); /* @@ -470,6 +476,8 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, */ drm_for_each_crtc(tmp, crtc->dev) { struct drm_plane *plane = tmp->primary; + if (!tmp->primary) + continue; plane->old_fb = plane->fb; } @@ -486,6 +494,8 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, drm_for_each_crtc(tmp, crtc->dev) { struct drm_plane *plane = tmp->primary; + if (!tmp->primary) + continue; if (plane->fb) drm_framebuffer_get(plane->fb); @@ -606,6 +616,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (ret) goto out; + if (!crtc->primary) + return -EINVAL; + if (crtc_req->mode_valid) { /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 5a84c3bc915d..aaa80d640203 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -185,6 +185,8 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) (*crtc_funcs->disable)(crtc); else (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); + if (!crtc->primary) + continue; crtc->primary->fb = NULL; } } @@ -539,6 +541,9 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set, crtc_funcs = set->crtc->helper_private; + if (!set->crtc->primary) + return -EINVAL; + if (!set->mode) set->fb = NULL; @@ -950,6 +955,8 @@ void drm_helper_resume_force_mode(struct drm_device *dev) if (!crtc->enabled) continue; + if (!crtc->primary) + continue; ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->primary->fb); @@ -1072,6 +1079,9 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_plane_state *plane_state; struct drm_plane *plane = crtc->primary; + if (!plane) + return -EINVAL; + if (plane->funcs->atomic_duplicate_state) plane_state = plane->funcs->atomic_duplicate_state(plane); else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 9628dd617826..49babdceaf2c 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -549,6 +549,8 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) drm_for_each_crtc(crtc, dev) { drm_modeset_lock(&crtc->mutex, NULL); + if (!crtc->primary) + continue; if (crtc->primary->fb) crtcs_bound++; if (crtc->primary->fb == fb_helper->fb) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 781af1d42d76..d2d2c30bea97 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -891,7 +891,7 @@ static void legacy_remove_fb(struct drm_framebuffer *fb) drm_modeset_lock_all(dev); /* remove from any CRTC */ drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb == fb) { + if (crtc->primary && crtc->primary->fb == fb) { /* should turn off the crtc */ if (drm_crtc_force_disable(crtc)) DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ff6d2c8b41bb..89b2863845d5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -3008,15 +3008,23 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc) struct drm_device *dev = &dev_priv->drm; struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder; - struct drm_plane_state *plane_state = crtc->primary->state; - struct drm_framebuffer *fb = plane_state->fb; + struct drm_plane_state *plane_state; + struct drm_framebuffer *fb; - if (fb) - seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", + if (!crtc->primary) { + seq_puts(m, "\tno primary plane\n"); + } else { + plane_state = crtc->primary->state; + fb = plane_state->fb; + + if (fb) + seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", fb->base.id, plane_state->src_x >> 16, plane_state->src_y >> 16, fb->width, fb->height); - else - seq_puts(m, "\tprimary plane disabled\n"); + else + seq_puts(m, "\tprimary plane disabled\n"); + } + for_each_encoder_on_crtc(dev, crtc, intel_encoder) intel_encoder_info(m, intel_crtc, intel_encoder); } @@ -3261,13 +3269,18 @@ static int i915_display_info(struct seq_file *m, void *unused) intel_crtc_info(m, crtc); - seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x\n", - yesno(cursor->base.state->visible), - cursor->base.state->crtc_x, - cursor->base.state->crtc_y, - cursor->base.state->crtc_w, - cursor->base.state->crtc_h, - cursor->cursor.base); + if (cursor) { + seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x\n", + yesno(cursor->base.state->visible), + cursor->base.state->crtc_x, + cursor->base.state->crtc_y, + cursor->base.state->crtc_w, + cursor->base.state->crtc_h, + cursor->cursor.base); + } else { + seq_puts(m, "\tNo cursor plane available on this platform\n"); + } + intel_scaler_info(m, crtc); intel_plane_info(m, crtc); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fe8952f6cdad..be346babfbaa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5241,8 +5241,8 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) intel_atomic_get_new_crtc_state(to_intel_atomic_state(old_state), crtc); struct drm_plane *primary = crtc->base.primary; - struct drm_plane_state *old_primary_state = - drm_atomic_get_old_plane_state(old_state, primary); + struct drm_plane_state *old_primary_state = primary ? + drm_atomic_get_old_plane_state(old_state, primary) : NULL; intel_frontbuffer_flip(to_i915(crtc->base.dev), pipe_config->fb_bits); @@ -5280,8 +5280,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, struct drm_i915_private *dev_priv = to_i915(dev); struct drm_atomic_state *old_state = old_crtc_state->base.state; struct drm_plane *primary = crtc->base.primary; - struct drm_plane_state *old_primary_state = - drm_atomic_get_old_plane_state(old_state, primary); + struct drm_plane_state *old_primary_state = primary ? + drm_atomic_get_old_plane_state(old_state, primary) : NULL; bool modeset = needs_modeset(&pipe_config->base); struct intel_atomic_state *old_intel_state = to_intel_atomic_state(old_state); @@ -12419,8 +12419,9 @@ static void intel_update_crtc(struct drm_crtc *crtc, struct intel_crtc_state *pipe_config = to_intel_crtc_state(new_crtc_state); bool modeset = needs_modeset(new_crtc_state); struct intel_plane_state *new_plane_state = + crtc->primary ? intel_atomic_get_new_plane_state(to_intel_atomic_state(state), - to_intel_plane(crtc->primary)); + to_intel_plane(crtc->primary)) : NULL; if (modeset) { update_scanline_offset(intel_crtc); @@ -15671,7 +15672,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) dev_priv->active_crtcs |= 1 << drm_crtc_index(&crtc->base); - readout_plane_state(crtc); + if (crtc->base.primary) + readout_plane_state(crtc); DRM_DEBUG_KMS("[CRTC:%d:%s] hw state readout: %s\n", crtc->base.base.id, crtc->base.name, diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fb2f9fce34cd..f028a6fb33fe 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -548,10 +548,17 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, /* Find the largest fb */ for_each_crtc(dev, crtc) { - struct drm_i915_gem_object *obj = - intel_fb_obj(crtc->primary->state->fb); + struct drm_i915_gem_object *obj; intel_crtc = to_intel_crtc(crtc); + if (!crtc->primary) { + DRM_DEBUG_KMS("pipe %c has no primary plane\n", + pipe_name(intel_crtc->pipe)); + continue; + } + + obj = intel_fb_obj(crtc->primary->state->fb); + if (!crtc->state->active || !obj) { DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n", pipe_name(intel_crtc->pipe)); From 21c202a1ca00aef8b2d515bf8ae27e6c246daef9 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 12 Mar 2018 17:52:42 -0700 Subject: [PATCH 1097/1276] INTERNAL [IOTG] drm/i915: Introduce the Plane Restriction feature This feature allows restrictions to the way planes are created in the driver. Currently driver creates a primary plane and all the sprite planes that are possible depending on the underlying hardware.This feature allows restrictions on the number and type of planes that are created. To accomodate the legacy APIs the first plane that is created is designated as DRM_PLANE_TYPE_PRIMARY. Usecase is to restrict the planes that are available for each domain. Each domain passes in the plane mask through command line paramenter- available_planes_per_pipe. Only these planes are created for each domain. Another parameter domain_plane_owners is passed into dom0 so that gvt is aware of the plane ownership for all the domains and can restrict access. DDBs are written once for all planes in Dom0 when in a virtualized environment. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Anitha Chrisanthus Signed-off-by: Vivek Kasireddy Change-Id: I293c490be8994bb525e511c8d1b8324b3c70932c --- drivers/gpu/drm/i915/gvt/display.c | 20 +++ drivers/gpu/drm/i915/gvt/gvt.c | 28 ++- drivers/gpu/drm/i915/gvt/handlers.c | 10 +- drivers/gpu/drm/i915/i915_drv.c | 25 ++- drivers/gpu/drm/i915/i915_drv.h | 5 + drivers/gpu/drm/i915/i915_params.c | 52 ++++++ drivers/gpu/drm/i915/i915_params.h | 2 + drivers/gpu/drm/i915/intel_display.c | 255 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 3 + drivers/gpu/drm/i915/intel_pm.c | 142 ++++++++++++--- drivers/gpu/drm/i915/intel_sprite.c | 2 +- 11 files changed, 509 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 73a2dbbaf79d..b013d1be62db 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -485,6 +485,22 @@ static void intel_gvt_vblank_work(struct work_struct *w) ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_SCALERS_PER_DOMAIN) >> \ BITS_PER_DOMAIN * (scaler)) & 0xf) +int bxt_check_planes(struct intel_vgpu *vgpu, int pipe) +{ + int plane = 0; + bool ret = false; + + for (plane = 0; + plane < ((INTEL_INFO(vgpu->gvt->dev_priv)->num_sprites[pipe]) + 1); + plane++) { + if (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id) { + ret = true; + break; + } + } + return ret; +} + void intel_gvt_init_pipe_info(struct intel_gvt *gvt) { enum pipe pipe; @@ -528,6 +544,10 @@ int setup_virtual_monitors(struct intel_vgpu *vgpu) for_each_intel_connector_iter(connector, &conn_iter) { if (connector->encoder->get_hw_state(connector->encoder, &pipe) && connector->detect_edid) { + /* if no planes are allocated for this pipe, skip it */ + if (i915_modparams.avail_planes_per_pipe && + !bxt_check_planes(vgpu, pipe)) + continue; /* Get (Dom0) port associated with current pipe. */ port = enc_to_dig_port( &(connector->encoder->base))->base.port; diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 940443f08dfd..3105afe1a6e5 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -389,6 +389,12 @@ void intel_gvt_clean_device(struct drm_i915_private *dev_priv) dev_priv->gvt = NULL; } +#define BITS_PER_DOMAIN 4 +#define MAX_PLANES_PER_DOMAIN 4 +#define DOMAIN_PLANE_OWNER(owner, pipe, plane) \ + ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_PLANES_PER_DOMAIN) >> \ + BITS_PER_DOMAIN * (plane)) & 0xf) + /** * intel_gvt_init_device - initialize a GVT device * @dev_priv: drm i915 private data @@ -495,8 +501,28 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) if (ret) gvt_err("debugfs registeration failed, go on.\n"); - gvt_dbg_core("gvt device initialization is done\n"); dev_priv->gvt = gvt; + + if (i915_modparams.avail_planes_per_pipe) { + unsigned long long domain_plane_owners; + int plane; + enum pipe pipe; + + /* + * Each nibble represents domain id + * ids can be from 0-F. 0 for Dom0, 1,2,3...0xF for DomUs + * plane_owner[i] holds the id of the domain that owns it,eg:0,1,2 etc + */ + domain_plane_owners = i915_modparams.domain_plane_owners; + for_each_pipe(dev_priv, pipe) { + for_each_universal_plane(dev_priv, pipe, plane) { + gvt->pipe_info[pipe].plane_owner[plane] = + DOMAIN_PLANE_OWNER(domain_plane_owners, pipe, plane); + } + } + } + + gvt_dbg_core("gvt device initialization is done\n"); return 0; out_clean_types: diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 8fa3bed75d61..ec0886ac8d2b 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -3127,8 +3127,14 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + if (i915_modparams.avail_planes_per_pipe) { + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + } else { + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + } + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, pv_plane_wm_mmio_write); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 06b786199215..8f7ed15458dc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1340,6 +1340,24 @@ static void i915_welcome_messages(struct drm_i915_private *dev_priv) DRM_INFO("DRM_I915_DEBUG_GEM enabled\n"); } +static inline int get_max_avail_pipes(struct drm_i915_private *dev_priv) +{ + enum pipe pipe; + int index = 0; + + if (!intel_vgpu_active(dev_priv) || + !i915_modparams.avail_planes_per_pipe) + return INTEL_INFO(dev_priv)->num_pipes; + + for_each_pipe(dev_priv, pipe) { + if (AVAIL_PLANE_PER_PIPE(dev_priv, i915_modparams.avail_planes_per_pipe, + pipe)) + index++; + } + + return index; +} + /** * i915_driver_load - setup chip and create an initial config * @pdev: PCI device @@ -1357,6 +1375,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *)ent->driver_data; struct drm_i915_private *dev_priv; int ret; + int num_crtcs = 0; /* Enable nuclear pageflip on ILK+ */ if (!i915_modparams.nuclear_pageflip && match_info->gen < 5) @@ -1408,9 +1427,9 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) * of the i915_driver_init_/i915_driver_register functions according * to the role/effect of the given init step. */ - if (INTEL_INFO(dev_priv)->num_pipes) { - ret = drm_vblank_init(&dev_priv->drm, - INTEL_INFO(dev_priv)->num_pipes); + num_crtcs = get_max_avail_pipes(dev_priv); + if (num_crtcs) { + ret = drm_vblank_init(&dev_priv->drm, num_crtcs); if (ret) goto out_cleanup_hw; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e477f8a0764f..5d97f3b4e0a6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2672,6 +2672,11 @@ intel_info(const struct drm_i915_private *dev_priv) #define GT_FREQUENCY_MULTIPLIER 50 #define GEN9_FREQ_SCALER 3 +#define BITS_PER_PIPE 8 +#define AVAIL_PLANE_PER_PIPE(dev_priv, mask, pipe) \ + (((mask) >> (pipe) * BITS_PER_PIPE) & \ + ((1 << ((INTEL_INFO(dev_priv)->num_sprites[pipe]) + 1)) - 1)) + #include "i915_trace.h" static inline bool intel_vtd_active(void) diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 062190f99a28..4ffdd533bddc 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -201,6 +201,56 @@ i915_param_named(enable_pvmmio, uint, 0400, "Enable pv mmio feature and set pvmmio level, default 1." "This parameter could only set from host, guest value is set through vgt_if"); +/* pipeA = BITS 0-3, pipeB = BITS 8-11, pipeC = BITS 16-18 + * +----------+-------+---------+--------+--------+--------+--------+ + * |unused |unused | Pipe C | unused | Pipe B | unused | Pipe A | + * +----------+-------+---------+--------+--------+--------+--------+ + * 31 23 18 15 11 7 3 0 + * + * + * BITS 0,1,2,3 - needs to be set planes assigned for pipes A and B + * and BITs 0,1,2 - for pipe C + * eg: avail_planes_per_pipe = 0x3 - pipe A=2(planes 1 and 2) , pipeB=0 and pipeC=0 planes + * eg: avail_planes_per_pipe = 0x5 - pipe A=2(planes 1 and 3) , pipeB=0 and pipeC=0 planes + * avail_planes_per_pipe = 0x030701 - pipe A =1(plane 1, pipeB=3(planes 1,2 and 3), pipeC=2( planes 1 and 2) + * + */ +i915_param_named_unsafe(avail_planes_per_pipe, uint, 0400, + "plane mask for each pipe: \ + set BITS 0-3:pipeA 8-11:pipeB 16-18:pipeC to specify the planes that \ + are available eg: 0x030701 : planes 1:pipeA 1,2,3:pipeB \ + 1,2:pipeC (0x0 - default value)"); + +/* pipeA = BITS 0-15 pipeB = 16-31, pipeC = 32-47 + * + * +----------+------------+-------------+------------+ + * |unused | Pipe C | Pipe B | Pipe A | + * +----------+------------+-------------+------------+ + * 63 47 31 15 0 + * + * Each nibble represents domain id. 0 for Dom0, 1,2,3...0xF for DomUs + * eg: domain_plane_owners = 0x022111000010 // 0x0221|1100|0010 + * plane domain + * plane_owner1A -0 + * plane_owner2A -1 + * plane_owner3A -0 + * plane_owner4A -0 + * plane_owner1B -0 + * plane_owner2B -0 + * plane_owner3B -1 + * plane_owner4B -1 + * plane_owner1C -1 + * plane_owner2C -2 + * plane_owner3C -2 + * + * + */ +i915_param_named_unsafe(domain_plane_owners, ullong, 0400, + "plane owners for each domain and for each pipe \ + ids can be from 0-F, eg: domain_plane_owners = 0x022111000010 \ + planes owner: 3C:2 2C:2 1C:1 4B:1 3B:1 2B:1 1B:0 4A:0 3A:0 2A:1 1A:0 \ + (0x0 - default value)"); + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, @@ -212,6 +262,8 @@ static __always_inline void _print_param(struct drm_printer *p, drm_printf(p, "i915.%s=%d\n", name, *(const int *)x); else if (!__builtin_strcmp(type, "unsigned int")) drm_printf(p, "i915.%s=%u\n", name, *(const unsigned int *)x); + else if (!__builtin_strcmp(type, "unsigned long long")) + drm_printf(p, "i915.%s=%llu\n", name, *(const unsigned long long *)x); else if (!__builtin_strcmp(type, "char *")) drm_printf(p, "i915.%s=%s\n", name, *(const char **)x); else diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 40aee5b37645..6b0f98c37761 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -55,6 +55,8 @@ struct drm_printer; param(int, edp_vswing, 0) \ param(int, reset, 2) \ param(unsigned int, inject_load_failure, 0) \ + param(unsigned int, avail_planes_per_pipe, 0) \ + param(unsigned long long, domain_plane_owners, 0) \ /* leave bools at the end to not create holes */ \ param(bool, alpha_support, IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT)) \ param(bool, enable_hangcheck, true) \ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index be346babfbaa..d0b55e39a08e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5362,15 +5362,37 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, intel_update_watermarks(crtc); } +static void disable_primary_plane(struct drm_i915_private *dev_priv, int pipe) +{ + u32 val; + + val = I915_READ(PLANE_CTL(pipe, PLANE_PRIMARY)); + if (val & PLANE_CTL_ENABLE) { + I915_WRITE(PLANE_CTL(pipe, PLANE_PRIMARY), 0); + I915_WRITE(PLANE_SURF(pipe, PLANE_PRIMARY), 0); + POSTING_READ(PLANE_SURF(pipe, PLANE_PRIMARY)); + } +} + static void intel_crtc_disable_planes(struct drm_crtc *crtc, unsigned plane_mask) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_plane *p; int pipe = intel_crtc->pipe; intel_crtc_dpms_overlay_disable(intel_crtc); + /* + * On BIOS based systems, if Dom0 doesn't own Plane 0 (Primary Plane), + * then during modeset, it wouldn't be able to disable this plane and + * this can lead to unexpected behavior after the modeset. Therefore, + * disable the primary plane if it was enabled by the BIOS/GOP. + */ + if (dev_priv->gvt && i915_modparams.avail_planes_per_pipe) + disable_primary_plane(dev_priv, pipe); + drm_for_each_plane_mask(p, dev, plane_mask) to_intel_plane(p)->disable_plane(to_intel_plane(p), intel_crtc); @@ -11634,7 +11656,8 @@ static void verify_wm_state(struct drm_crtc *crtc, const enum pipe pipe = intel_crtc->pipe; int plane, level, max_level = ilk_wm_max_level(dev_priv); - if (INTEL_GEN(dev_priv) < 9 || !new_state->active) + if (INTEL_GEN(dev_priv) < 9 || !new_state->active || + i915_modparams.avail_planes_per_pipe) return; skl_pipe_wm_get_hw_state(crtc, &hw_wm); @@ -13835,6 +13858,109 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) return ERR_PTR(ret); } +static struct intel_plane * +intel_skl_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe, + int plane, bool is_primary) +{ + struct intel_plane *intel_plane = NULL; + struct intel_plane_state *state = NULL; + unsigned long possible_crtcs; + const uint32_t *plane_formats; + unsigned int supported_rotations, plane_type; + unsigned int num_formats; + const uint64_t *modifiers; + int ret; + + intel_plane = kzalloc(sizeof(*intel_plane), GFP_KERNEL); + if (!intel_plane) { + ret = -ENOMEM; + goto fail; + } + + state = intel_create_plane_state(&intel_plane->base); + if (!state) { + ret = -ENOMEM; + goto fail; + } + + intel_plane->base.state = &state->base; + intel_plane->can_scale = false; + state->scaler_id = -1; + intel_plane->pipe = pipe; + + /* + * On gen2/3 only plane A can do FBC, but the panel fitter and LVDS + * port is hooked to pipe B. Hence we want plane A feeding pipe B. + */ + if (is_primary) { + intel_plane->i9xx_plane = (enum i9xx_plane_id) pipe; + intel_plane->check_plane = intel_check_primary_plane; + plane_type = DRM_PLANE_TYPE_PRIMARY; + } else { + intel_plane->i9xx_plane = (enum i9xx_plane_id) plane; + intel_plane->check_plane = intel_check_sprite_plane; + plane_type = DRM_PLANE_TYPE_OVERLAY; + } + + if (plane == PLANE_PRIMARY) { + intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane); + intel_plane->update_plane = skl_update_plane; + intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; + } else { + intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane); + intel_plane->update_plane = skl_update_plane; + intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; + } + + intel_plane->id = plane; + plane_formats = skl_primary_formats; + + if (pipe < PIPE_C) + modifiers = skl_format_modifiers_ccs; + else + modifiers = skl_format_modifiers_noccs; + + num_formats = ARRAY_SIZE(skl_primary_formats); + + /* + * Drop final format (NV12) for pipes or hardware steppings + * that don't support it. + */ + if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_C0) || pipe >= PIPE_C + || plane >= 2) + num_formats--; + + + possible_crtcs = (1 << dev_priv->drm.mode_config.num_crtc); + ret = drm_universal_plane_init(&dev_priv->drm, &intel_plane->base, + possible_crtcs, &skl_plane_funcs, + plane_formats, num_formats, + modifiers, + plane_type, + "plane %d%c", plane+1, pipe_name(pipe)); + + if (ret) + goto fail; + + supported_rotations = DRM_MODE_ROTATE_0; + if (INTEL_GEN(dev_priv) >= 4) + drm_plane_create_rotation_property(&intel_plane->base, + DRM_MODE_ROTATE_0, + supported_rotations); + + drm_plane_helper_add(&intel_plane->base, &intel_plane_helper_funcs); + + return intel_plane; + +fail: + kfree(state); + kfree(intel_plane); + + return ERR_PTR(ret); +} + static struct intel_plane * intel_cursor_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) @@ -13944,6 +14070,79 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler_state->scaler_id = -1; } +static int intel_crtc_init_restrict_planes(struct drm_i915_private *dev_priv, + enum pipe pipe, int planes_mask) +{ + struct intel_crtc *intel_crtc; + struct intel_crtc_state *crtc_state; + struct intel_plane *primary = NULL, *intel_plane = NULL; + bool is_primary = true; + int plane, ret, crtc_plane; + + intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL); + if (!intel_crtc) + return -ENOMEM; + + crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); + if (!crtc_state) { + ret = -ENOMEM; + goto fail; + } + intel_crtc->config = crtc_state; + intel_crtc->base.state = &crtc_state->base; + crtc_state->base.crtc = &intel_crtc->base; + + for_each_universal_plane(dev_priv, pipe, plane) { + if (planes_mask & BIT(plane)) { + intel_plane = intel_skl_plane_create(dev_priv, + pipe, plane, is_primary); + if (IS_ERR(intel_plane)) { + DRM_DEBUG_KMS(" plane %d failed for pipe %d\n", plane, pipe); + ret = PTR_ERR(intel_plane); + goto fail; + } + if (is_primary) { + primary = intel_plane; + is_primary = false; + } + DRM_DEBUG_KMS(" plane %d created for pipe %d\n", plane, pipe); + intel_crtc->plane_ids_mask |= BIT(intel_plane->id); + } + } + + ret = drm_crtc_init_with_planes(&dev_priv->drm, + &intel_crtc->base, + primary ? &primary->base : NULL, NULL, + &intel_crtc_funcs, + "pipe %c", pipe_name(pipe)); + if (ret) + goto fail; + + intel_crtc->pipe = pipe; + crtc_plane = primary ? primary->i9xx_plane : 0; + + dev_priv->plane_to_crtc_mapping[crtc_plane] = intel_crtc; + dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = intel_crtc; + + drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + + intel_color_init(&intel_crtc->base); + + WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe); + + return 0; + +fail: + /* + * drm_mode_config_cleanup() will free up any + * crtcs/planes already initialized. + */ + kfree(crtc_state); + kfree(intel_crtc); + + return ret; +} + static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) { struct intel_crtc *intel_crtc; @@ -15191,12 +15390,26 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("FDI PLL freq=%d\n", dev_priv->fdi_pll_freq); } + +static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) +{ + /*plane restriction feature is only for APL for now*/ + if (!IS_BROXTON(dev_priv)) { + i915_modparams.avail_planes_per_pipe = 0; + DRM_INFO("Turning off Plane Restrictions feature\n"); + } + + return i915_modparams.avail_planes_per_pipe; +} + int intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; enum pipe pipe; struct intel_crtc *crtc; + unsigned int planes_mask[I915_MAX_PIPES]; + unsigned int avail_plane_per_pipe_mask = 0; dev_priv->modeset_wq = alloc_ordered_workqueue("i915_modeset", 0); @@ -15270,10 +15483,29 @@ int intel_modeset_init(struct drm_device *dev) INTEL_INFO(dev_priv)->num_pipes, INTEL_INFO(dev_priv)->num_pipes > 1 ? "s" : ""); + avail_plane_per_pipe_mask = intel_sanitize_plane_restriction(dev_priv); + DRM_DEBUG_KMS("avail_planes_per_pipe = 0x%x \n", i915_modparams.avail_planes_per_pipe); + DRM_DEBUG_KMS("domain_plane_owners = 0x%llx \n", i915_modparams.domain_plane_owners); + + for_each_pipe(dev_priv, pipe) { + planes_mask[pipe] = AVAIL_PLANE_PER_PIPE(dev_priv, + avail_plane_per_pipe_mask, pipe); + DRM_DEBUG_KMS("for pipe %d plane_mask = %d \n", pipe, planes_mask[pipe]); + } + for_each_pipe(dev_priv, pipe) { int ret; - ret = intel_crtc_init(dev_priv, pipe); + if (!i915_modparams.avail_planes_per_pipe) { + ret = intel_crtc_init(dev_priv, pipe); + } else { + if (!intel_vgpu_active(dev_priv) || (intel_vgpu_active(dev_priv) + && planes_mask[pipe])) { + ret = intel_crtc_init_restrict_planes(dev_priv, + pipe, + planes_mask[pipe]); + } + } if (ret) { drm_mode_config_cleanup(dev); return ret; @@ -15708,10 +15940,15 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) struct intel_crtc_state *crtc_state; crtc = intel_get_crtc_for_pipe(dev_priv, pipe); - crtc_state = to_intel_crtc_state(crtc->base.state); + if (!crtc) { + encoder->base.crtc = NULL; + } else { + crtc_state = to_intel_crtc_state(crtc->base.state); - encoder->base.crtc = &crtc->base; - encoder->get_config(encoder, crtc_state); + encoder->base.crtc = &crtc->base; + encoder->get_config(encoder, crtc_state); + + } } else { encoder->base.crtc = NULL; } @@ -15870,9 +16107,11 @@ intel_modeset_setup_hw_state(struct drm_device *dev, for_each_pipe(dev_priv, pipe) { crtc = intel_get_crtc_for_pipe(dev_priv, pipe); - intel_sanitize_crtc(crtc, ctx); - intel_dump_pipe_config(crtc, crtc->config, - "[setup_hw_state]"); + if (crtc) { + intel_sanitize_crtc(crtc, ctx); + intel_dump_pipe_config(crtc, crtc->config, + "[setup_hw_state]"); + } } intel_modeset_update_connector_atomic_state(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 49d4282b234b..c8290474f0ad 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -2114,6 +2114,9 @@ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state); void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state); +int intel_check_sprite_plane(struct intel_plane *plane, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *state); void skl_update_plane(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1884b8140131..276a46f26a4b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -807,11 +807,14 @@ static int intel_wm_num_levels(struct drm_i915_private *dev_priv) static bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - struct intel_plane *plane = to_intel_plane(plane_state->base.plane); + struct intel_plane *plane = plane_state ? to_intel_plane(plane_state->base.plane) : NULL; /* FIXME check the 'enable' instead */ if (!crtc_state->base.active) return false; + if (!plane_state && i915_modparams.avail_planes_per_pipe) { + return true; + } /* * Treat cursor with fb as always visible since cursor updates @@ -3973,11 +3976,15 @@ static uint_fixed_16_16_t skl_plane_downscale_amount(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate) { - struct intel_plane *plane = to_intel_plane(pstate->base.plane); + struct intel_plane *plane = pstate ? to_intel_plane(pstate->base.plane) : NULL; uint32_t src_w, src_h, dst_w, dst_h; uint_fixed_16_16_t fp_w_ratio, fp_h_ratio; uint_fixed_16_16_t downscale_h, downscale_w; + if (!pstate && i915_modparams.avail_planes_per_pipe) { + return mul_fixed16(u32_to_fixed16(1), u32_to_fixed16(1)); + } + if (WARN_ON(!intel_wm_plane_visible(cstate, pstate))) return u32_to_fixed16(0); @@ -4488,9 +4495,9 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, const struct intel_plane_state *intel_pstate, struct skl_wm_params *wp, int plane_id) { - struct intel_plane *plane = to_intel_plane(intel_pstate->base.plane); - const struct drm_plane_state *pstate = &intel_pstate->base; - const struct drm_framebuffer *fb = pstate->fb; + struct intel_plane *plane = intel_pstate ? to_intel_plane(intel_pstate->base.plane) : NULL; + const struct drm_plane_state *pstate = intel_pstate ? &intel_pstate->base : NULL; + const struct drm_framebuffer *fb = pstate ? pstate->fb : NULL; uint32_t interm_pbpl; struct intel_atomic_state *state = to_intel_atomic_state(cstate->base.state); @@ -4505,6 +4512,19 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, return -EINVAL; } + if (!intel_pstate && i915_modparams.avail_planes_per_pipe) { + wp->y_tiled = false; + wp->x_tiled = true; + wp->cpp = 4; + wp->y_min_scanlines = 8; + wp->rc_surface = fb ? fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS || + fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS : 0; + wp->is_planar = fb ? fb->format->format == DRM_FORMAT_NV12 : 0; + wp->width = cstate->pipe_src_w; + wp->dbuf_block_size = 512; + goto calculate_wm; + } + wp->y_tiled = fb->modifier == I915_FORMAT_MOD_Y_TILED || fb->modifier == I915_FORMAT_MOD_Yf_TILED || fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS || @@ -4529,8 +4549,6 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, wp->width /= 2; wp->cpp = fb->format->cpp[plane_id]; - wp->plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, - intel_pstate); if (INTEL_GEN(dev_priv) >= 11 && fb->modifier == I915_FORMAT_MOD_Yf_TILED && wp->cpp == 8) @@ -4561,6 +4579,9 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, if (apply_memory_bw_wa) wp->y_min_scanlines *= 2; +calculate_wm: + wp->plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, + intel_pstate); wp->plane_bytes_per_line = wp->width * wp->cpp; if (wp->y_tiled) { interm_pbpl = DIV_ROUND_UP(wp->plane_bytes_per_line * @@ -4599,7 +4620,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, const struct skl_wm_level *result_prev, struct skl_wm_level *result /* out */) { - const struct drm_plane_state *pstate = &intel_pstate->base; + const struct drm_plane_state *pstate = intel_pstate ? &intel_pstate->base : NULL; + const struct drm_framebuffer *fb = pstate ? pstate->fb : NULL; uint32_t latency = dev_priv->wm.skl_latency[level]; uint_fixed_16_16_t method1, method2; uint_fixed_16_16_t selected_result; @@ -4652,7 +4674,7 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, wp->plane_blocks_per_line); /* Display WA #1125: skl,bxt,kbl,glk */ - if (level == 0 && wp->rc_surface) + if (fb && level == 0 && wp->rc_surface) res_blocks += fixed16_to_u32_round_up(wp->y_tile_minimum); /* Display WA #1126: skl,bxt,kbl,glk */ @@ -4710,12 +4732,16 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, if (level) { return 0; } else { - struct drm_plane *plane = pstate->plane; + struct drm_plane *plane = pstate ? pstate->plane : NULL; DRM_DEBUG_KMS("Requested display configuration exceeds system watermark limitations\n"); - DRM_DEBUG_KMS("[PLANE:%d:%s] blocks required = %u/%u, lines required = %u/31\n", + + if (plane) { + DRM_DEBUG_KMS("[PLANE:%d:%s] blocks required = %u/%u, lines required = %u/31\n", plane->base.id, plane->name, res_blocks, ddb_allocation, res_lines); + } + return -EINVAL; } } @@ -4746,18 +4772,16 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv, const struct intel_plane_state *intel_pstate, const struct skl_wm_params *wm_params, struct skl_plane_wm *wm, - int plane_id) + int plane_id, + enum plane_id intel_plane_id) { struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); - struct drm_plane *plane = intel_pstate->base.plane; - struct intel_plane *intel_plane = to_intel_plane(plane); uint16_t ddb_blocks; enum pipe pipe = intel_crtc->pipe; int level, max_level = ilk_wm_max_level(dev_priv); - enum plane_id intel_plane_id = intel_plane->id; int ret; - if (WARN_ON(!intel_pstate->base.fb)) + if (WARN_ON(intel_pstate && !intel_pstate->base.fb)) return -EINVAL; ddb_blocks = plane_id ? @@ -4787,7 +4811,8 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv, return ret; } - if (intel_pstate->base.fb->format->format == DRM_FORMAT_NV12) + if (intel_pstate && + intel_pstate->base.fb->format->format == DRM_FORMAT_NV12) wm->is_planar = true; return 0; @@ -4871,6 +4896,64 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, trans_wm->plane_en = false; } +static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, + struct skl_ddb_allocation *ddb, + struct skl_pipe_wm *pipe_wm) +{ + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = to_i915(dev); + struct skl_plane_wm *wm; + struct intel_crtc *crtc = to_intel_crtc(cstate->base.crtc); + struct drm_crtc_state *crtc_state = &cstate->base; + struct drm_plane *plane; + const struct drm_plane_state *pstate; + struct intel_plane_state *intel_pstate; + int pipe = crtc->pipe; + int plane_id; + int ret; + + memset(pipe_wm->planes, 0, sizeof(pipe_wm->planes)); + + /* + * Since Dom0 may not own all planes on this pipe, there will + * not be a valid intel_plane for the planes it doesn't own. + * Therefore, we have to pass NULL to skl_compute_wm_level() + * which will then know that this plane is not owned by Dom0 + * and hence will use width and height from the crtc and will + * also assume cpp = 4 and tiling = x_tiled. + */ + for_each_universal_plane(dev_priv, pipe, plane_id) { + struct skl_wm_params wm_params; + uint16_t ddb_blocks; + intel_pstate = NULL; + + drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) { + if (plane_id == to_intel_plane(plane)->id) { + intel_pstate = to_intel_plane_state(pstate); + break; + } + } + + wm = &pipe_wm->planes[plane_id]; + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, &wm_params, 0); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, wm, 0, plane_id); + if (ret) + return ret; + + skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], + ddb_blocks, &wm->trans_wm); + } + pipe_wm->linetime = skl_compute_linetime_wm(cstate); + + return 0; +} + static int skl_build_pipe_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) @@ -4906,7 +4989,7 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, return ret; ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0); + intel_pstate, &wm_params, wm, 0, plane_id); if (ret) return ret; @@ -4926,7 +5009,7 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, ret = skl_compute_wm_levels(dev_priv, ddb, cstate, intel_pstate, &wm_params, - wm, 1); + wm, 1, plane_id); if (ret) return ret; } @@ -5143,7 +5226,10 @@ static int skl_update_pipe_wm(struct drm_crtc_state *cstate, struct intel_crtc_state *intel_cstate = to_intel_crtc_state(cstate); int ret; - ret = skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); + if (i915_modparams.avail_planes_per_pipe) + ret = skl_build_pipe_all_plane_wm(intel_cstate, ddb, pipe_wm); + else + ret = skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); if (ret) return ret; @@ -5387,10 +5473,14 @@ skl_compute_wm(struct drm_atomic_state *state) struct drm_crtc_state *cstate; struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct skl_ddb_values *results = &intel_state->wm_results; + struct drm_i915_private *dev_priv = to_i915(intel_state->base.dev); struct skl_pipe_wm *pipe_wm; bool changed = false; int ret, i; + if (intel_vgpu_active(dev_priv) && i915_modparams.avail_planes_per_pipe) + return 0; + /* Clear all dirty flags */ results->dirty_pipes = 0; @@ -5449,11 +5539,23 @@ static void skl_atomic_update_crtc_wm(struct intel_atomic_state *state, enum pipe pipe = crtc->pipe; enum plane_id plane_id; + if (intel_vgpu_active(dev_priv) && i915_modparams.avail_planes_per_pipe) + return; + if (!(state->wm_results.dirty_pipes & drm_crtc_mask(&crtc->base))) return; I915_WRITE(PIPE_WM_LINETIME(pipe), pipe_wm->linetime); + if (i915_modparams.avail_planes_per_pipe) { + for_each_universal_plane(dev_priv, pipe, plane_id) { + skl_write_plane_wm(crtc, &pipe_wm->planes[plane_id], + ddb, plane_id); + } + + return; + } + for_each_plane_id_on_crtc(crtc, plane_id) { #if IS_ENABLED(CONFIG_DRM_I915_GVT) if (dev_priv->gvt && diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 4b89ba3e021c..e10a64a6265c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1039,7 +1039,7 @@ g4x_plane_get_hw_state(struct intel_plane *plane, return ret; } -static int +int intel_check_sprite_plane(struct intel_plane *plane, struct intel_crtc_state *crtc_state, struct intel_plane_state *state) From e8711d50dd92c5af33bf6a9e4198a10056db7b56 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 26 Jun 2018 08:49:50 +0800 Subject: [PATCH 1098/1276] drm/i915/gvt: make KBL also support plane restriction feature previously plane restriction feature was for APL only, we need extend this feature to KBL also. Change-Id: I192ad3419b96bc34702189fc9c6a3b463ae50061 Signed-off-by: Fei Jiang Reviewed-by: Min He --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d0b55e39a08e..8a9450e37008 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15393,8 +15393,8 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) { - /*plane restriction feature is only for APL for now*/ - if (!IS_BROXTON(dev_priv)) { + /*plane restriction feature is only for APL and KBL for now*/ + if (!(IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv))) { i915_modparams.avail_planes_per_pipe = 0; DRM_INFO("Turning off Plane Restrictions feature\n"); } From 0c1a3e59d3c72337aa71a36d2fea43fbee394c56 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 26 Jul 2018 15:17:00 +0800 Subject: [PATCH 1099/1276] kernel/drm/i915: Check the plane_state->fb to avoid Null pointer After the boot option of "enable_initial_modeset=1" is added for gvt-g, the function of disable_planes will assign the Null fb for each plane. In such case it is possible that the plane_state->fb is NULL when calling intel_can_enable_sagv. Then it will trigger the kernel panic. This is to fix the kernel panic on KBL-NUC. BUG: unable to handle kernel NULL pointer dereference at 0000000000000068 IP: intel_can_enable_sagv+0x1a7/0x1c0 PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 111 Comm: kworker/0:2 Tainted: G U 4.14.55-65.pk414-sos #1 Hardware name: Dell Inc. OptiPlex 7050/062KRH, BIOS 1.5.2 06/19/2017 Workqueue: events modeset_config_fn task: ffff9c020a36a100 task.stack: ffff9c0205ac4000 RIP: 0010:intel_can_enable_sagv+0x1a7/0x1c0 RSP: 0000:ffff9c0205ac7bd8 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 000000000000001e RCX: ffff9c020a388400 RDX: 0000000000000063 RSI: 0000000000000000 RDI: ffff9c02056f5000 RBP: ffff9c0205ac7bf0 R08: ffff9c020a398498 R09: 0000000000000000 R10: 0100000000000001 R11: 000000000000002a R12: ffff9c020576ec00 R13: ffff9c020a398000 R14: ffff9c0205770000 R15: 0000000000000003 FS: 0000000000000000(0000) GS:ffff9c021dc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000068 CR3: 0000000153813000 CR4: 00000000003406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: intel_atomic_commit_tail+0x9fe/0xfc0 ? __i915_sw_fence_complete+0x127/0x1c0 intel_atomic_commit+0x191/0x240 drm_atomic_commit+0x51/0x60 modeset_config_fn+0x4fb/0xc20 ? __switch_to_asm+0x30/0x60 process_one_work+0x192/0x3a0 worker_thread+0x41/0x3b0 kthread+0x132/0x150 ? wq_sysfs_prep_attrs+0x50/0x50 ? kthread_create_on_node+0x40/0x40 ret_from_fork+0x3a/0x50 Code: e8 07 00 00 17 74 19 39 da 0f 8d 3b ff ff ff e9 b6 fe ff ff 5b b8 01 00 00 00 41 5c 41 5d 5d c3 48 8b 81 80 02 00 00 48 8b 40 10 <4c> 39 50 68 75 d6 83 c2 0f eb d1 66 66 2e 0f 1f 84 RIP: intel_can_enable_sagv+0x1a7/0x1c0 RSP: ffff9c0205ac7bd8 Signed-off-by: Zhao Yakui Reviewed-by: Jiang, Fei --- drivers/gpu/drm/i915/intel_pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 276a46f26a4b..27554caeca6b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3763,6 +3763,7 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) latency = dev_priv->wm.skl_latency[level]; if (skl_needs_memory_bw_wa(intel_state) && + plane->base.state->fb && plane->base.state->fb->modifier == I915_FORMAT_MOD_X_TILED) latency += 15; From b67354c11a4abc37c7a04db51a3df657120820a6 Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 14 Aug 2018 02:24:22 +0000 Subject: [PATCH 1100/1276] drm/i915: fix a kernel panic issue of plane restriction When plane restriction feature enabled in sevice os, there could be the case that there're some CRTC's without a primary plane. If we don't assign any plane of pipe A to sos, like i915.avail_planes_per_pipe =0x000F00, it will cause kernel panic when booting because it assumes primary plane existing in intel_find_initial_plane_obj(). Added a check to the primary plane in CRTC to avoid such kind of issue. Signed-off-by: Min He Reviewed-by: Xinyun Liu Reviewed-by: Zhao Yakui Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1155 --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8a9450e37008..34770b9d9764 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15532,7 +15532,7 @@ int intel_modeset_init(struct drm_device *dev) for_each_intel_crtc(dev, crtc) { struct intel_initial_plane_config plane_config = {}; - if (!crtc->active) + if (!crtc->active || !crtc->base.primary) continue; /* From 797c22e46a6475163be9485899a9aa26ec50bafc Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 17 Aug 2018 17:54:24 +0800 Subject: [PATCH 1101/1276] drm/i915/gvt: ensure each pipe has a plane in Host OS This is a workaround patch to fix black screen issue and pass plane restriction tests. Weston 4.0 won't enable the CRTCs which doesn't have a primary plane. So explicitly check `avail_planes_per_pipe` to make sure each pipe has a primary plane to make weston happy. Note: When GVT-g enabled with plane restriction feature, User App changed mode in host OS will make chaos for guest OS. v3: remove unnecessary check and fix style per Min's advice v2: improve the check logic per Fei's advice v1: force enable a plane for each CRTC Tracked-On: projectacrn/acrn-hypervisor#1131 Signed-off-by: Xinyun Liu Reviewed-by: Fei Jiang Reviewed-by: Min He --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34770b9d9764..142b7bf0a762 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15393,13 +15393,29 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) { + unsigned int mask; + /*plane restriction feature is only for APL and KBL for now*/ if (!(IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv))) { i915_modparams.avail_planes_per_pipe = 0; DRM_INFO("Turning off Plane Restrictions feature\n"); } - return i915_modparams.avail_planes_per_pipe; + mask = i915_modparams.avail_planes_per_pipe; + + /* make sure SOS has a (dummy) plane per pipe. */ + if ((IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) && + intel_gvt_active(dev_priv)) { + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + if (!AVAIL_PLANE_PER_PIPE(dev_priv, mask, pipe)) + mask |= (1 << pipe * BITS_PER_PIPE); + } + DRM_INFO("Fix internal plane mask: 0x%06x --> 0x%06x", + i915_modparams.avail_planes_per_pipe, mask); + } + return mask; } int intel_modeset_init(struct drm_device *dev) From 789590e0031d8840f007af144e12b90ee8334fe4 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:22 +0800 Subject: [PATCH 1102/1276] drm/i915: to limit the supported modifiers for plane restriction In GVT-g environment, to ensure all the OS's have enough DDB to display, GVT-g will statically allocate all the DDBs for all the planes on all the pipes. However, when SOS or UOS wants to use Y/Yf tiled modifier, the watermark required will exceed the DDBs allocated by GVT-g, thus causes some display issues. So in this patch, we removed the supports of the Y/Yf tiled modifiers for both SOS and UOS when plane restriction is enabled. And a consequence is that the RBC will be disabled since _CCS modifiers will no longer be supported. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1193 Signed-off-by: Min He Reviewed-by: Zhao Yakui Reviewed-by: Fei Jiang V2: revert commit 01ba86, cd00ba and limited to gvt_active or vgpu_active Signed-off-by: Xinyun Liu Signed-off-by: Zhao Yakui --- drivers/gpu/drm/i915/intel_display.c | 6 ++++++ drivers/gpu/drm/i915/intel_sprite.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 142b7bf0a762..695a97bed66a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13761,6 +13761,9 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) else modifiers = skl_format_modifiers_noccs; + if (intel_gvt_active(dev_priv) || intel_vgpu_active(dev_priv)) + modifiers = i9xx_format_modifiers; + primary->update_plane = skl_update_plane; primary->disable_plane = skl_disable_plane; primary->get_hw_state = skl_plane_get_hw_state; @@ -13922,6 +13925,9 @@ intel_skl_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe, else modifiers = skl_format_modifiers_noccs; + if (intel_gvt_active(dev_priv) || intel_vgpu_active(dev_priv)) + modifiers = i9xx_format_modifiers; + num_formats = ARRAY_SIZE(skl_primary_formats); /* diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index e10a64a6265c..7b8e2c7c9f8d 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1629,6 +1629,9 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, else modifiers = skl_plane_format_modifiers_noccs; + if (intel_gvt_active(dev_priv) || intel_vgpu_active(dev_priv)) + modifiers = i9xx_plane_format_modifiers; + plane_funcs = &skl_plane_funcs; } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { intel_plane->can_scale = false; From d6ff365eebede3b235d06267bc1ede8461004eae Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Fri, 5 Oct 2018 13:52:11 -0700 Subject: [PATCH 1103/1276] drm/i915: Optimize watermark calculation for plane restrictions The same code was being used in skl_build_plane_wm and skl_build_pipe_all_plane_wm which was redundant. Stripped out this code into a common function. Signed-off-by: Satyeshwar Singh --- drivers/gpu/drm/i915/intel_pm.c | 106 +++++++++++++++++--------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 27554caeca6b..07a8f8dc4935 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4897,13 +4897,63 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, trans_wm->plane_en = false; } +static int skl_build_plane_wm(struct intel_crtc_state *cstate, + struct skl_ddb_allocation *ddb, + struct skl_pipe_wm *pipe_wm, + int pipe, + enum plane_id plane_id, + struct intel_plane_state *intel_pstate) +{ + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = to_i915(dev); + struct skl_plane_wm *wm; + struct skl_wm_params wm_params; + uint16_t ddb_blocks; + int ret; + + wm = &pipe_wm->planes[plane_id]; + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); + + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, &wm_params, 0); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, wm, 0, plane_id); + if (ret) + return ret; + + skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], + ddb_blocks, &wm->trans_wm); + + /* uv plane watermarks must also be validated for NV12/Planar */ + if (wm_params.is_planar) { + memset(&wm_params, 0, sizeof(struct skl_wm_params)); + wm->is_planar = true; + + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, + &wm_params, 1); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, + wm, 1, plane_id); + if (ret) + return ret; + } + + return 0; +} + static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) { struct drm_device *dev = cstate->base.crtc->dev; const struct drm_i915_private *dev_priv = to_i915(dev); - struct skl_plane_wm *wm; struct intel_crtc *crtc = to_intel_crtc(cstate->base.crtc); struct drm_crtc_state *crtc_state = &cstate->base; struct drm_plane *plane; @@ -4924,8 +4974,6 @@ static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, * also assume cpp = 4 and tiling = x_tiled. */ for_each_universal_plane(dev_priv, pipe, plane_id) { - struct skl_wm_params wm_params; - uint16_t ddb_blocks; intel_pstate = NULL; drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) { @@ -4935,20 +4983,10 @@ static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, } } - wm = &pipe_wm->planes[plane_id]; - ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, &wm_params, 0); - if (ret) - return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0, plane_id); + ret = skl_build_plane_wm(cstate, ddb, pipe_wm, + pipe, plane_id, (struct intel_plane_state *) intel_pstate); if (ret) return ret; - - skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], - ddb_blocks, &wm->trans_wm); } pipe_wm->linetime = skl_compute_linetime_wm(cstate); @@ -4959,12 +4997,9 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) { - struct drm_device *dev = cstate->base.crtc->dev; struct drm_crtc_state *crtc_state = &cstate->base; - const struct drm_i915_private *dev_priv = to_i915(dev); struct drm_plane *plane; const struct drm_plane_state *pstate; - struct skl_plane_wm *wm; int ret; /* @@ -4977,43 +5012,12 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, const struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate); enum plane_id plane_id = to_intel_plane(plane)->id; - struct skl_wm_params wm_params; enum pipe pipe = to_intel_crtc(cstate->base.crtc)->pipe; - uint16_t ddb_blocks; - wm = &pipe_wm->planes[plane_id]; - ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); - - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, &wm_params, 0); + ret = skl_build_plane_wm(cstate, ddb, pipe_wm, + pipe, plane_id, (struct intel_plane_state *) intel_pstate); if (ret) return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0, plane_id); - if (ret) - return ret; - - skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], - ddb_blocks, &wm->trans_wm); - - /* uv plane watermarks must also be validated for NV12/Planar */ - if (wm_params.is_planar) { - memset(&wm_params, 0, sizeof(struct skl_wm_params)); - wm->is_planar = true; - - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, - &wm_params, 1); - if (ret) - return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, - wm, 1, plane_id); - if (ret) - return ret; - } } pipe_wm->linetime = skl_compute_linetime_wm(cstate); From 3819f5d5b9e30f75a83f0f387b6f52a32f9c676d Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 4 Sep 2018 01:00:01 +0000 Subject: [PATCH 1104/1276] drm/i915/gvt: clean up the cfg space and MMIO spaces When vgpu instance is destroyed, we need to clean up the MMIO and aperture regions, which is missing in current code. Signed-off-by: Min He Signed-off-by: Yin Fengwei Reviewed-by: Zhao Yakui Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1146 --- drivers/gpu/drm/i915/gvt/acrngt.c | 13 +++++++++++++ drivers/gpu/drm/i915/gvt/cfg_space.c | 2 ++ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/gvt/handlers.c | 2 +- drivers/gpu/drm/i915/gvt/vgpu.c | 1 + 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index abde541b76f8..20e3e6718900 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -919,6 +919,19 @@ static int acrngt_set_pvmmio(unsigned long handle, u64 start, u64 end, bool map) } else { mfn = acrngt_virt_to_mfn(info->vgpu->mmio.vreg); + + /* scratch reg access is trapped like mmio access, 1 page */ + rc = acrngt_map_gfn_to_mfn(handle, + pfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), + mfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), 1, 1); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), + mfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), + rc); + return rc; + } + rc = acrngt_map_gfn_to_mfn(handle, pfn, mfn, mmio_size_fn, map); if (rc) { gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 505e255c4d8a..8606925d339d 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -469,6 +469,8 @@ void intel_vgpu_reset_cfg_space(struct intel_vgpu *vgpu) INTEL_GVT_PCI_CLASS_VGA_OTHER; if (cmd & PCI_COMMAND_MEMORY) { + if (VGPU_PVMMIO(vgpu)) + set_pvmmio(vgpu, false); trap_gttmmio(vgpu, false); map_aperture(vgpu, false); } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index f4d9056175ae..bbf2489f251d 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -556,6 +556,8 @@ void intel_vgpu_init_cfg_space(struct intel_vgpu *vgpu, bool primary); void intel_vgpu_reset_cfg_space(struct intel_vgpu *vgpu); +int set_pvmmio(struct intel_vgpu *vgpu, bool map); + int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index ec0886ac8d2b..0fc1fb37e1ef 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1311,7 +1311,7 @@ static int send_display_ready_uevent(struct intel_vgpu *vgpu, int ready) } #define INTEL_GVT_PCI_BAR_GTTMMIO 0 -static int set_pvmmio(struct intel_vgpu *vgpu, bool map) +int set_pvmmio(struct intel_vgpu *vgpu, bool map) { u64 start, end; u64 val; diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 9e4a0b2b5f22..511681b24c59 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -300,6 +300,7 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu) intel_vgpu_clean_gtt(vgpu); intel_gvt_hypervisor_detach_vgpu(vgpu); intel_vgpu_free_resource(vgpu); + intel_vgpu_reset_cfg_space(vgpu); intel_vgpu_clean_mmio(vgpu); intel_vgpu_dmabuf_cleanup(vgpu); mutex_unlock(&vgpu->vgpu_lock); From e3f84b4351515a2d3c14adb5f2f55618dfbf3ff3 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Thu, 20 Sep 2018 13:10:45 +0800 Subject: [PATCH 1105/1276] drm/i915/gvt: use plane size for fb decoder Current fb decoder returns pipe src size to user space, while ggtt surface sharing only show primary plane framebuffer. When plane fb size is smaller than pipe size, some garbages are showed. This patch change fb decoder to use plane size to avoid those garbages. v2: - align size to PAGE_SIZE Tracked-On: projectacrn/acrn-hypervisor#1301 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/fb_decoder.c | 9 +++++---- drivers/gpu/drm/i915/gvt/fb_decoder.h | 4 ++++ drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 3 +-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 6bc40be48649..7c43c916b3d8 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -267,11 +267,12 @@ int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu, (_PRI_PLANE_STRIDE_MASK >> 6) : _PRI_PLANE_STRIDE_MASK, plane->bpp); - plane->width = (vgpu_vreg_t(vgpu, PIPESRC(pipe)) & _PIPE_H_SRCSZ_MASK) >> - _PIPE_H_SRCSZ_SHIFT; + plane->width = vgpu_vreg_t(vgpu, PLANE_SIZE(pipe, PLANE_PRIMARY))& + _PLANE_SIZE_WIDTH_MASK; + plane->width += 1; - plane->height = (vgpu_vreg_t(vgpu, PIPESRC(pipe)) & - _PIPE_V_SRCSZ_MASK) >> _PIPE_V_SRCSZ_SHIFT; + plane->height = (vgpu_vreg_t(vgpu, PLANE_SIZE(pipe, PLANE_PRIMARY)) & + _PLANE_SIZE_HEIGHT_MASK) >> _PLANE_SIZE_HEIGHT_SHIFT; plane->height += 1; /* raw height is one minus the real value */ val = vgpu_vreg_t(vgpu, DSPTILEOFF(pipe)); diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index a202f9f6e81a..51626759534b 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -50,6 +50,10 @@ #define _PRI_PLANE_Y_OFF_SHIFT 16 #define _PRI_PLANE_Y_OFF_MASK (0xfff << _PRI_PLANE_Y_OFF_SHIFT) +#define _PLANE_SIZE_HEIGHT_SHIFT 16 +#define _PLANE_SIZE_HEIGHT_MASK (0xfff << _PLANE_SIZE_HEIGHT_SHIFT) +#define _PLANE_SIZE_WIDTH_MASK 0x1fff + #define _CURSOR_MODE 0x3f #define _CURSOR_ALPHA_FORCE_SHIFT 8 #define _CURSOR_ALPHA_FORCE_MASK (0x3 << _CURSOR_ALPHA_FORCE_SHIFT) diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c index fe723099788d..f482eceb5c7f 100644 --- a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -195,8 +195,7 @@ static int gvt_decode_information(struct drm_device *dev, return -EINVAL; } - args->size = (((args->width * args->height * args->bpp) / 8) + - (PAGE_SIZE - 1)) >> PAGE_SHIFT; + args->size = ALIGN(args->stride * args->height, PAGE_SIZE) >> PAGE_SHIFT; if (args->start & (PAGE_SIZE - 1)) { DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: " From b4fea9838b814fd86b2faae91fd8d3ee31677874 Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Wed, 30 Aug 2017 14:59:22 +0800 Subject: [PATCH 1106/1276] drm/i915/gvt: Forbid command to access non-context registers After MMIO save/restore removing, the guest cannot access non-context registers through cmd. This patch implement it by replacing the target non-context register with a non-functional one in related commands. v2: 1. sort the non-context MMIO lists for searching friendly. 2. add a new flag F_NON_CONTEXT, it help to do fast non-context MMIOs checking during runtime. v3: Mark non-context MMIOs by walk the array. v4: Define PVINFO page as a target scratch reg to redirect the cmd access. Signed-off-by: Ping Gao Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie v5: Rebase v4.19, fix conflict and reuse the code in 6cef21a19649 - drm/i915/gvt: update vreg on inhibit context lri command 410a63dd8ccf - drm/i915/gvt: Introduce non-context MMIO check routines and change mmio_attribute type to u16 Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 9 ++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 29 ++++++++++++++++++++++++- drivers/gpu/drm/i915/gvt/handlers.c | 2 +- drivers/gpu/drm/i915/gvt/mmio_context.c | 2 ++ drivers/gpu/drm/i915/i915_pvinfo.h | 6 +++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 8d130d4d58b7..729eb0f05f9f 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -918,6 +918,15 @@ static int cmd_reg_handler(struct parser_exec_state *s, } } + /* Re-direct the non-context MMIO access to VGT_SCRATCH_REG, it + * has no functional impact to HW. + */ + if (!strcmp(cmd, "lri") || !strcmp(cmd, "lrr-dst") + || !strcmp(cmd, "lrm") || !strcmp(cmd, "pipe_ctrl")) { + if (intel_gvt_mmio_is_non_context(gvt, offset)) + patch_value(s, cmd_ptr(s, index), VGT_SCRATCH_REG); + } + /* TODO: Update the global mask if this MMIO is a masked-MMIO */ intel_gvt_mmio_set_cmd_accessed(gvt, offset); return 0; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index bbf2489f251d..68cc50f75803 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -266,7 +266,7 @@ struct gvt_mmio_block { #define INTEL_GVT_MMIO_HASH_BITS 11 struct intel_gvt_mmio { - u8 *mmio_attribute; + u16 *mmio_attribute; /* Register contains RO bits */ #define F_RO (1 << 0) /* Register contains graphics address */ @@ -283,6 +283,8 @@ struct intel_gvt_mmio { #define F_UNALIGN (1 << 6) /* This reg is saved/restored in context */ #define F_IN_CTX (1 << 7) +/* This reg is not in the context */ +#define F_NON_CONTEXT (1 << 8) struct gvt_mmio_block *mmio_block; unsigned int num_mmio_block; @@ -719,6 +721,31 @@ static inline void intel_gvt_mmio_set_in_ctx( gvt->mmio.mmio_attribute[offset >> 2] |= F_IN_CTX; } +/** + * intel_gvt_mmio_is_non_context - check a MMIO is non-context + * @gvt: a GVT device + * @offset: register offset + * + */ +static inline bool intel_gvt_mmio_is_non_context( + struct intel_gvt *gvt, unsigned int offset) +{ + return gvt->mmio.mmio_attribute[offset >> 2] & F_NON_CONTEXT; +} + +/** + * intel_gvt_mmio_set_non_context - mark a MMIO is non-context + + * @gvt: a GVT device + * @offset: register offset + * + */ +static inline void intel_gvt_mmio_set_non_context( + struct intel_gvt *gvt, unsigned int offset) +{ + gvt->mmio.mmio_attribute[offset >> 2] |= F_NON_CONTEXT; +} + int intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu); void intel_gvt_debugfs_remove_vgpu(struct intel_vgpu *vgpu); int intel_gvt_debugfs_init(struct intel_gvt *gvt); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 0fc1fb37e1ef..ce25433c6d77 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -92,7 +92,7 @@ static struct intel_gvt_mmio_info *find_mmio_info(struct intel_gvt *gvt, } static int new_mmio_info(struct intel_gvt *gvt, - u32 offset, u8 flags, u32 size, + u32 offset, u16 flags, u32 size, u32 addr_mask, u32 ro_mask, u32 device, gvt_mmio_func read, gvt_mmio_func write) { diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index e872f4847fbe..0221e87f34db 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -588,6 +588,8 @@ void intel_gvt_init_engine_mmio_context(struct intel_gvt *gvt) if (mmio->in_context) { gvt->engine_mmio_list.ctx_mmio_count[mmio->ring_id]++; intel_gvt_mmio_set_in_ctx(gvt, mmio->reg.reg); + } else { + intel_gvt_mmio_set_non_context(gvt, mmio->reg.reg); } } } diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index c15d4578bb5f..fd26872f15b6 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -28,6 +28,12 @@ #define VGT_PVINFO_PAGE 0x78000 #define VGT_PVINFO_SIZE 0x1000 +/* Scratch reg used for redirecting command access to registers, any + * command access to PVINFO page would be discarded, so it has no HW + * impact. + */ +#define VGT_SCRATCH_REG VGT_PVINFO_PAGE + /* * The following structure pages are defined in GEN MMIO space * for virtualization. (One page for now) From fbb26af7eacb702382e56bd62f5f1fc8e680882a Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Thu, 31 Aug 2017 10:45:05 +0800 Subject: [PATCH 1107/1276] drm/i915/gvt: Introduce non-context MMIO check routines Guest conformance check can be implemented by verifying whether the non-context MMIOs have the same values with host snapshot. This patch introduced some routines, snapshot the host registers values and compare when with corresponding vregs by walking the non-context MMIO list. Signed-off-by: Ping Gao Reviewed-by: Kevin Tian Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie v2: rebase 4.19: 1. reuse engine_mmio_list->mmio in commit cd7e61b93d06 drm/i915/gvt: init mmio by lri command in vgpu inhibit context 2. split part of code to 2b5955ee506d - drm/i915/gvt: Forbid command to access non-context registers Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/gvt.h | 6 ++ drivers/gpu/drm/i915/gvt/handlers.c | 9 +++ drivers/gpu/drm/i915/gvt/mmio_context.c | 76 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 68cc50f75803..708d690b8c4f 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -289,10 +289,16 @@ struct intel_gvt_mmio { struct gvt_mmio_block *mmio_block; unsigned int num_mmio_block; + void *mmio_host_cache; + bool host_cache_initialized; DECLARE_HASHTABLE(mmio_info_table, INTEL_GVT_MMIO_HASH_BITS); unsigned long num_tracked_mmio; }; +/* Macro for easily access host engine mmio cached register */ +#define gvt_host_reg(gvt, reg) \ + (*(u32 *)(gvt->mmio.mmio_host_cache + reg)) \ + struct intel_gvt_firmware { void *cfg_space; void *mmio; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index ce25433c6d77..f923ca71ab8e 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -3435,6 +3435,9 @@ void intel_gvt_clean_mmio_info(struct intel_gvt *gvt) vfree(gvt->mmio.mmio_attribute); gvt->mmio.mmio_attribute = NULL; + + vfree(gvt->mmio.mmio_host_cache); + gvt->mmio.mmio_host_cache = NULL; } /* Special MMIO blocks. */ @@ -3469,6 +3472,12 @@ int intel_gvt_setup_mmio_info(struct intel_gvt *gvt) if (!gvt->mmio.mmio_attribute) return -ENOMEM; + gvt->mmio.mmio_host_cache = vzalloc(info->mmio_size); + if (!gvt->mmio.mmio_host_cache) { + vfree(gvt->mmio.mmio_attribute); + return -ENOMEM; + } + ret = init_generic_mmio_info(gvt); if (ret) goto err; diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index 0221e87f34db..99b01ab60dc3 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -567,6 +567,82 @@ void intel_gvt_switch_mmio(struct intel_vgpu *pre, intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } +#define MMIO_COMPARE(vgpu, reg, mask) ({ \ + int ret; \ + u32 value = vgpu_vreg(vgpu, reg); \ + u32 host_value = gvt_host_reg(vgpu->gvt, reg); \ + \ + if (mask) { \ + value &= mask; \ + host_value &= mask; \ + } \ + if (host_value == value) { \ + ret = 0; \ + } else { \ + gvt_err("vgpu%d unconformance mmio 0x%x:0x%x,0x%x\n", \ + vgpu->id, reg, \ + vgpu_vreg(vgpu, reg), \ + gvt_host_reg(vgpu->gvt, reg)); \ + ret = -EINVAL; \ + } \ + ret; \ + }) + +static int noncontext_mmio_compare(struct intel_vgpu *vgpu, int ring_id) +{ + struct engine_mmio *mmio, *mmio_list; + + mmio_list = vgpu->gvt->engine_mmio_list.mmio; + + for (mmio = mmio_list; i915_mmio_reg_valid(mmio->reg); mmio++) { + if (mmio->ring_id != ring_id || mmio->in_context) + continue; + + if (MMIO_COMPARE(vgpu, mmio->reg.reg, mmio->mask)) + return -EINVAL; + } + + return 0; +} + +static void get_host_mmio_snapshot(struct intel_gvt *gvt) +{ + struct drm_i915_private *dev_priv = gvt->dev_priv; + struct engine_mmio *mmio, *mmio_list; + + mmio_list = gvt->engine_mmio_list.mmio; + + if (!gvt->mmio.host_cache_initialized) { + /* Snapshot all the non-context MMIOs */ + for (mmio = mmio_list; i915_mmio_reg_valid(mmio->reg); mmio++) { + if (mmio->in_context) + continue; + + gvt_host_reg(gvt, mmio->reg.reg) = + I915_READ_FW(mmio->reg); + if (mmio->mask) + gvt_host_reg(gvt, mmio->reg.reg) &= mmio->mask; + } + gvt->mmio.host_cache_initialized = true; + } +} + +int intel_gvt_vgpu_conformance_check(struct intel_vgpu *vgpu, int ring_id) +{ + int ret; + + get_host_mmio_snapshot(vgpu->gvt); + + ret = noncontext_mmio_compare(vgpu, ring_id); + if (ret) + goto err; + + return 0; +err: + return ret; +} + + /** * intel_gvt_init_engine_mmio_context - Initiate the engine mmio list * @gvt: GVT device From 478b6de9ebd185ccf9eb59af4cff6739271d7449 Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Wed, 6 Sep 2017 19:29:48 +0800 Subject: [PATCH 1108/1276] drm/i915/gvt: Enable guest conformance detection The first overall comparison happen before the wordload submission. Signed-off-by: Ping Gao Reviewed-by: Kevin Tian Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie V2: rebase 4.19 Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/mmio_context.h | 1 + drivers/gpu/drm/i915/gvt/scheduler.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.h b/drivers/gpu/drm/i915/gvt/mmio_context.h index 5c3b9ff9f96a..b5059de42af9 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.h +++ b/drivers/gpu/drm/i915/gvt/mmio_context.h @@ -54,4 +54,5 @@ bool is_inhibit_context(struct intel_context *ce); int intel_vgpu_restore_inhibit_context(struct intel_vgpu *vgpu, struct i915_request *req); +int intel_gvt_vgpu_conformance_check(struct intel_vgpu *vgpu, int ring_id); #endif diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index ebba07a402d7..6dde10291e84 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -711,6 +711,10 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) mutex_lock(&dev_priv->drm.struct_mutex); ret = intel_gvt_scan_and_shadow_workload(workload); + + if (intel_gvt_vgpu_conformance_check(vgpu, ring_id)) + gvt_err("vgpu%d unconformance guest detected\n", vgpu->id); + if (ret) goto out; From fe693aafbe8f8fa20c21260befde3feff6b03169 Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Wed, 6 Sep 2017 19:57:57 +0800 Subject: [PATCH 1109/1276] drm/i915/gvt: Add a module parameter to disable conformance check By default the guest conformance check is enabled. Signed-off-by: Ping Gao Reviewed-by: Kevin Tian Reviewed-by: Singh, Satyeshwar Conflicts: drivers/gpu/drm/i915/i915_params.c drivers/gpu/drm/i915/i915_params.h Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie V2: rebase 4.19 Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/scheduler.c | 3 ++- drivers/gpu/drm/i915/i915_params.c | 3 +++ drivers/gpu/drm/i915/i915_params.h | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 6dde10291e84..c78ed3256142 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -712,7 +712,8 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) ret = intel_gvt_scan_and_shadow_workload(workload); - if (intel_gvt_vgpu_conformance_check(vgpu, ring_id)) + if (i915_modparams.enable_conformance_check + && intel_gvt_vgpu_conformance_check(vgpu, ring_id)) gvt_err("vgpu%d unconformance guest detected\n", vgpu->id); if (ret) diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 4ffdd533bddc..5c6e7dc48b7f 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -251,6 +251,9 @@ i915_param_named_unsafe(domain_plane_owners, ullong, 0400, planes owner: 3C:2 2C:2 1C:1 4B:1 3B:1 2B:1 1B:0 4A:0 3A:0 2A:1 1A:0 \ (0x0 - default value)"); +i915_param_named(enable_conformance_check, bool, 0400, + "To toggle the GVT guest conformance feature(default:true)"); + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 6b0f98c37761..d902bedad72a 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -75,7 +75,8 @@ struct drm_printer; PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE \ | PVMMIO_GGTT_UPDATE ) \ - param(bool, enable_gvt, false) + param(bool, enable_gvt, false) \ + param(bool, enable_conformance_check, true) #define MEMBER(T, member, ...) T member; struct i915_params { From 5e65ac46391122fc00d12657b517251b416b113b Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Wed, 6 Sep 2017 15:42:48 +0800 Subject: [PATCH 1110/1276] drm/i915/gvt: Skip to compare force-nonpriv registers As force-nonpriv registers are updated by command, skip to compare them with host cache during the first entire non-context MMIOs check to avoid failure. Also, when dom0 in guc submission mode, the RING_MODE_GEN7 register could be different in guest, so ignore it too. Signed-off-by: Ping Gao Reviewed-by: Kevin Tian Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie v2: rebase 4.19 Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 2 +- drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/gvt/mmio_context.c | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 729eb0f05f9f..9115c3dc2fc9 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -808,7 +808,7 @@ static bool is_shadowed_mmio(unsigned int offset) return ret; } -static inline bool is_force_nonpriv_mmio(unsigned int offset) +bool is_force_nonpriv_mmio(unsigned int offset) { return (offset >= 0x24d0 && offset < 0x2500); } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 708d690b8c4f..882859c20332 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -758,6 +758,8 @@ int intel_gvt_debugfs_init(struct intel_gvt *gvt); void intel_gvt_debugfs_clean(struct intel_gvt *gvt); +bool is_force_nonpriv_mmio(unsigned int offset); + #include "trace.h" #include "mpt.h" diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index 99b01ab60dc3..e6c592f44b39 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -590,12 +590,16 @@ void intel_gvt_switch_mmio(struct intel_vgpu *pre, static int noncontext_mmio_compare(struct intel_vgpu *vgpu, int ring_id) { + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; struct engine_mmio *mmio, *mmio_list; + struct intel_engine_cs *engine = dev_priv->engine[ring_id]; mmio_list = vgpu->gvt->engine_mmio_list.mmio; for (mmio = mmio_list; i915_mmio_reg_valid(mmio->reg); mmio++) { - if (mmio->ring_id != ring_id || mmio->in_context) + if (mmio->ring_id != ring_id || mmio->in_context + || is_force_nonpriv_mmio(mmio->reg.reg) + || mmio->reg.reg == RING_MODE_GEN7(engine).reg) continue; if (MMIO_COMPARE(vgpu, mmio->reg.reg, mmio->mask)) From 21a8125788713d4a3d838093d49bc09d92b7c7c6 Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Wed, 6 Sep 2017 16:13:40 +0800 Subject: [PATCH 1111/1276] drm/i915/gvt: Simply the conformance check The overall comparison for non-context MMIOs only need once, then it's enough for conformance check that audit these MMIOs during runtime to make sure the value written to them are the same with the host cache. The way could void frequently overall comparion. Signed-off-by: Ping Gao Reviewed-by: Kevin Tian Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie v2: rebase to 4.19, reuse gvt_host_reg() to fix wrong offset Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 1 + drivers/gpu/drm/i915/gvt/mmio.c | 10 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 9115c3dc2fc9..4fa37daf13d6 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -966,6 +966,32 @@ static int cmd_handler_lri(struct parser_exec_state *s) ret |= cmd_reg_handler(s, cmd_reg(s, i), i, "lri"); if (ret) break; + + if (s->vgpu->entire_nonctxmmio_checked + && intel_gvt_mmio_is_non_context(gvt, + cmd_reg(s, i))) { + int offset = cmd_reg(s, i); + int value = cmd_val(s, i + 1); + + if (intel_gvt_mmio_has_mode_mask(gvt, offset)) { + u32 mask = value >> 16; + + vgpu_vreg(s->vgpu, offset) = + (vgpu_vreg(s->vgpu, offset) & ~mask) + | (value & mask); + } else { + vgpu_vreg(s->vgpu, offset) = value; + } + + if (gvt_host_reg(gvt, offset) != + vgpu_vreg(s->vgpu, offset)) { + + gvt_err("vgpu%d unexpected non-context MMIO " + "access by cmd 0x%x:0x%x,0x%x\n", + s->vgpu->id, offset, value, + gvt_host_reg(gvt, offset)); + } + } } return ret; } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 882859c20332..7da1e9bad5cc 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -239,6 +239,7 @@ struct intel_vgpu { unsigned long long *cached_guest_entry; bool ge_cache_enable; + bool entire_nonctxmmio_checked; }; /* validating GM healthy status*/ diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 4cb3f72ab56a..b6a974f55805 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -213,6 +213,14 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, if (ret < 0) goto err; + if (vgpu->entire_nonctxmmio_checked + && intel_gvt_mmio_is_non_context(vgpu->gvt, offset) + && vgpu_vreg(vgpu, offset) != gvt_host_reg(gvt, offset)) { + gvt_err("vgpu%d unexpected non-context MMIO change at 0x%x:0x%x,0x%x\n", + vgpu->id, offset, vgpu_vreg(vgpu, offset), + gvt_host_reg(gvt, offset)); + } + intel_gvt_mmio_set_accessed(gvt, offset); ret = 0; goto out; @@ -298,6 +306,8 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) vgpu_vreg_t(vgpu, HUC_STATUS2) = I915_READ(HUC_STATUS2); mmio_hw_access_post(dev_priv); } + /* Non-context MMIOs need entire check again if mmio/vgpu reset */ + vgpu->entire_nonctxmmio_checked = false; } /** From aea57574de50b85505c05d45917e880950231e4c Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Sun, 14 Oct 2018 16:40:07 -0700 Subject: [PATCH 1112/1276] drm/i915: Fixed uninitialized variable issue There could be a circumstance in which ret may not be initialized and checking for its value may yield unexpected results. https://github.com/projectacrn/acrn-hypervisor/issues/1459 Signed-off-by: Satyeshwar Singh Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 695a97bed66a..5d2bf527cda2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15516,7 +15516,7 @@ int intel_modeset_init(struct drm_device *dev) } for_each_pipe(dev_priv, pipe) { - int ret; + int ret = 0; if (!i915_modparams.avail_planes_per_pipe) { ret = intel_crtc_init(dev_priv, pipe); From 7870242b7c96ad0d4f805354f47f50d80e2df190 Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Sun, 14 Oct 2018 16:41:07 -0700 Subject: [PATCH 1113/1276] drm/i915: Check for a valid fb when computing watermarks fb must be valid when we are computing watermarks for a plane or else we will run into a crash. Checking to see if it is indeed valid and returning EINVAL if it's not. https://github.com/projectacrn/acrn-hypervisor/issues/1459 Signed-off-by: Satyeshwar Singh Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/intel_pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 07a8f8dc4935..d4510a5504e1 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4507,6 +4507,11 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, if (!intel_wm_plane_visible(cstate, intel_pstate)) return 0; + if (plane_id == 1 && !fb) { + DRM_DEBUG_KMS("Invalid fb for plane\n"); + return -EINVAL; + } + /* only NV12 format has two planes */ if (plane_id == 1 && fb->format->format != DRM_FORMAT_NV12) { DRM_DEBUG_KMS("Non NV12 format have single plane\n"); From e82ba296a04ddee69ea589db06537aa8ad9e07b8 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Wed, 19 Sep 2018 15:28:53 +0800 Subject: [PATCH 1114/1276] [upstream] drm/i915/gvt: correct mask setting for CSFE_CHICKEN1 CSFE_CHICKEN1(0x20d4) needs access with mask. This is caught in AcrnGT conformance check test: [drm:intel_gvt_vgpu_conformance_check] *ERROR* gvt: vgpu1 unconformance mmio 0x20d4:0x40004,0x4 Tracked-On: projectacrn/acrn-hypervisor#1459 Signed-off-by: Xinyun Liu --- drivers/gpu/drm/i915/gvt/mmio_context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index e6c592f44b39..dc0a14729b88 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -144,7 +144,7 @@ static struct engine_mmio gen9_engine_mmio_list[] __cacheline_aligned = { {RCS, GAMT_CHKN_BIT_REG, 0x0, false}, /* 0x4ab8 */ {RCS, GEN9_GAMT_ECO_REG_RW_IA, 0x0, false}, /* 0x4ab0 */ - {RCS, GEN9_CSFE_CHICKEN1_RCS, 0x0, false}, /* 0x20d4 */ + {RCS, GEN9_CSFE_CHICKEN1_RCS, 0xffff, false}, /* 0x20d4 */ {RCS, GEN8_GARBCNTL, 0x0, false}, /* 0xb004 */ {RCS, GEN7_FF_THREAD_MODE, 0x0, false}, /* 0x20a0 */ From 3cc394ca6471b38ac8067f10d6d7cfe989c09a8d Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Mon, 22 Oct 2018 16:53:59 +0800 Subject: [PATCH 1115/1276] drm/i915/gvt: Support vgpu workload priority config Add support to configure vgpu workload priority, make the param writable to facilitate performance issue debug. Signed-off-by: Zhipeng Gong Signed-off-by: Min He V2: rebased to 4.19 Tracked-On: projectacrn/acrn-hypervisor#1552 Signed-off-by: Xinyun Liu Reviewed-by: Min He --- drivers/gpu/drm/i915/gvt/scheduler.c | 12 ++++++++++++ drivers/gpu/drm/i915/i915_params.c | 4 ++++ drivers/gpu/drm/i915/i915_params.h | 1 + 3 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index c78ed3256142..c1e6cd606ca3 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -467,6 +467,15 @@ static void gen8_shadow_pid_cid(struct intel_vgpu_workload *workload) intel_ring_advance(workload->req, cs); } +static int sanitize_priority(int priority) +{ + if (priority > I915_CONTEXT_MAX_USER_PRIORITY) + return I915_CONTEXT_MAX_USER_PRIORITY; + else if (priority < I915_CONTEXT_MIN_USER_PRIORITY) + return I915_CONTEXT_MIN_USER_PRIORITY; + return priority; +} + static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload); static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload) @@ -701,6 +710,7 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + struct intel_vgpu_submission *s = &vgpu->submission; int ring_id = workload->ring_id; int ret; @@ -729,6 +739,8 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) if (!IS_ERR_OR_NULL(workload->req)) { gvt_dbg_sched("ring id %d submit workload to i915 %p\n", ring_id, workload->req); + s->shadow_ctx->sched.priority = i915_modparams.gvt_workload_priority = + sanitize_priority(i915_modparams.gvt_workload_priority); i915_request_add(workload->req); workload->dispatched = true; } diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 5c6e7dc48b7f..e594fd3cf6c9 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -174,6 +174,10 @@ i915_param_named(enable_dpcd_backlight, bool, 0600, i915_param_named(enable_gvt, bool, 0400, "Enable support for Intel GVT-g graphics virtualization host support(default:false)"); +i915_param_named(gvt_workload_priority, int, 0600, + "Set GVT-g workload priority, (range: (-1023, 1023), default: 0, " + "more positive value means higher priority)."); + i915_param_named(domain_scaler_owner, int, 0400, "scaler owners for each domain and for each pipe ids can be from 0-F"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index d902bedad72a..a0d1d7e7a3ac 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -75,6 +75,7 @@ struct drm_printer; PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE \ | PVMMIO_GGTT_UPDATE ) \ + param(int, gvt_workload_priority, 0) \ param(bool, enable_gvt, false) \ param(bool, enable_conformance_check, true) From 5a53c88a0c21b5ab569c7fcda7f36b9f485096f6 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 18 Oct 2018 11:43:57 -0700 Subject: [PATCH 1116/1276] hyper_dmabuf: refine Kconfig and Makefile Make sure CONFIG_VIRTIO is selected if hyper_dmabuf is built for ACRN. Also all unused portion is removed from Makefile. Signed-off-by: Dongwon Kim --- drivers/dma-buf/hyper_dmabuf/Kconfig | 27 ++++++----- drivers/dma-buf/hyper_dmabuf/Makefile | 67 ++++++++------------------- 2 files changed, 35 insertions(+), 59 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index 1d91a114ba61..f15a8cdf9250 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -1,44 +1,47 @@ -menu "hyper_dmabuf options" - -config HYPER_DMABUF - bool "Enables hyper dmabuf driver" +menuconfig HYPER_DMABUF + bool "configure HyperDMABUF driver" default y depends on (X86=y || X86_64=y) +if HYPER_DMABUF + choice prompt "Hypervisor" depends on HYPER_DMABUF default HYPER_DMABUF_XEN config HYPER_DMABUF_XEN - bool "Configure hyper_dmabuf for XEN hypervisor" - depends on HYPER_DMABUF && XEN + bool "XEN" + depends on XEN help Configuring hyper_dmabuf driver for XEN hypervisor config HYPER_DMABUF_ACRN - bool "Configure hyper_dmabuf for ACRN hypervisor" - depends on HYPER_DMABUF && ACRN_VIRTIO_DEVICES + bool "ACRN" + depends on ACRN_VIRTIO_DEVICES + select VIRTIO help Configuring hyper_dmabuf driver for ACRN hypervisor endchoice choice prompt "Virtio driver type" - depends on HYPER_DMABUF && HYPER_DMABUF_ACRN + depends on HYPER_DMABUF_ACRN default HYPER_DMABUF_VIRTIO_BE config HYPER_DMABUF_VIRTIO_BE depends on VBS && DRM_I915_GVT - bool "Configure hyper_dmabuf as virtio backend" + bool "virtio backend (SOS)" help Configuring hyper_dmabuf driver as virtio backend + running from service OS config HYPER_DMABUF_VIRTIO_FE depends on ACRN_VIRTIO_DEVICES - bool "Configure hyper_dmabuf as virtio frontend" + bool "virtio frontend (UOS)" help Configuring hyper_dmabuf driver as virtio frontend + running from guest OS endchoice config HYPER_DMABUF_SYSFS @@ -69,4 +72,4 @@ config HYPER_DMABUF_XEN_AUTO_RX_CH_ADD domain then initialize matched rx comm ch automatically for any existing tx comm chs. -endmenu +endif diff --git a/drivers/dma-buf/hyper_dmabuf/Makefile b/drivers/dma-buf/hyper_dmabuf/Makefile index f63967cc99f6..4ad8dc70234b 100644 --- a/drivers/dma-buf/hyper_dmabuf/Makefile +++ b/drivers/dma-buf/hyper_dmabuf/Makefile @@ -1,57 +1,30 @@ -TARGET_MODULE:=hyper_dmabuf - -# If we running by kernel building system -ifneq ($(KERNELRELEASE),) - $(TARGET_MODULE)-objs := hyper_dmabuf_drv.o \ - hyper_dmabuf_ioctl.o \ - hyper_dmabuf_list.o \ - hyper_dmabuf_sgl_proc.o \ - hyper_dmabuf_ops.o \ - hyper_dmabuf_msg.o \ - hyper_dmabuf_id.o \ - hyper_dmabuf_remote_sync.o \ - hyper_dmabuf_query.o \ +obj-y := hyper_dmabuf_drv.o \ + hyper_dmabuf_ioctl.o \ + hyper_dmabuf_list.o \ + hyper_dmabuf_sgl_proc.o \ + hyper_dmabuf_ops.o \ + hyper_dmabuf_msg.o \ + hyper_dmabuf_id.o \ + hyper_dmabuf_remote_sync.o \ + hyper_dmabuf_query.o \ ifeq ($(CONFIG_HYPER_DMABUF_EVENT_GEN), y) - $(TARGET_MODULE)-objs += hyper_dmabuf_event.o + obj-y += hyper_dmabuf_event.o endif ifeq ($(CONFIG_HYPER_DMABUF_XEN), y) - $(TARGET_MODULE)-objs += xen/hyper_dmabuf_xen_comm.o \ - xen/hyper_dmabuf_xen_comm_list.o \ - xen/hyper_dmabuf_xen_shm.o \ - xen/hyper_dmabuf_xen_drv.o + obj-y += xen/hyper_dmabuf_xen_comm.o \ + xen/hyper_dmabuf_xen_comm_list.o \ + xen/hyper_dmabuf_xen_shm.o \ + xen/hyper_dmabuf_xen_drv.o else ifeq ($(CONFIG_HYPER_DMABUF_ACRN), y) ifeq ($(CONFIG_HYPER_DMABUF_VIRTIO_BE), y) - $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_be_drv.o \ - virtio/hyper_dmabuf_virtio_fe_list.o + obj-y += virtio/hyper_dmabuf_virtio_be_drv.o \ + virtio/hyper_dmabuf_virtio_fe_list.o else - $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_fe_drv.o + obj-y += virtio/hyper_dmabuf_virtio_fe_drv.o endif - $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_common.o \ - virtio/hyper_dmabuf_virtio_shm.o \ - virtio/hyper_dmabuf_virtio_comm_ring.o -endif - -obj-$(CONFIG_HYPER_DMABUF) := $(TARGET_MODULE).o - -# If we are running without kernel build system -else -BUILDSYSTEM_DIR?=../../../ -PWD:=$(shell pwd) - -all : -# run kernel build system to make module - $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules - -clean: -# run kernel build system to cleanup in current directory - $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean - -load: - insmod ./$(TARGET_MODULE).ko - -unload: - rmmod ./$(TARGET_MODULE).ko - + obj-y += virtio/hyper_dmabuf_virtio_common.o \ + virtio/hyper_dmabuf_virtio_shm.o \ + virtio/hyper_dmabuf_virtio_comm_ring.o endif From 4e06eab4b109c1fb641272bcd4514b49f48e03ee Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 24 Oct 2018 10:25:38 +0800 Subject: [PATCH 1117/1276] hyper-dmabuf: disable hyper-dmabuf on arch x86 Since hyper-dmabuf enabled treat warnings message as error. And the warnings will be generated when make target as i386. This patch will disable hyer-dmabuf on x86 to fix hyper-dmabuf warnings. Tracked-On: projectacrn/acrn-hypervisor#1572 Signed-off-by: Wei Liu Acked-by: Kim, Dongwon --- drivers/dma-buf/hyper_dmabuf/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index f15a8cdf9250..17ea5b8323da 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -1,7 +1,7 @@ menuconfig HYPER_DMABUF bool "configure HyperDMABUF driver" default y - depends on (X86=y || X86_64=y) + depends on X86_64 if HYPER_DMABUF From 506ac707d0e720fcb2c9a739be68ee8e5fa0807b Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Tue, 23 Oct 2018 16:36:52 +0800 Subject: [PATCH 1118/1276] drm/i915/gvt: fix kernel panic for gvtbuffer The obj created in i915_gem_object_create_gvtbuffer does not init obj->mm.link. when __i915_gem_free_objects call list_del_init(&obj->mm.link), it causes kernel panic. This patch fix it by add obj->mm.link to unbound list. Tracked-On: projectacrn/acrn-hypervisor#1576 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c index f482eceb5c7f..6e58fa1fb1d2 100644 --- a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -115,6 +115,8 @@ i915_gem_object_create_gvtbuffer(struct drm_device *dev, u32 start, u32 num_pages) { struct drm_i915_gem_object *obj; + struct drm_i915_private *i915 = to_i915(dev); + obj = i915_gem_object_alloc(to_i915(dev)); if (obj == NULL) return NULL; @@ -134,6 +136,11 @@ i915_gem_object_create_gvtbuffer(struct drm_device *dev, DRM_DEBUG_DRIVER("GVT_GEM: backing store base = 0x%x pages = 0x%x\n", start, num_pages); + + spin_lock(&i915->mm.obj_lock); + list_add(&obj->mm.link, &i915->mm.unbound_list); + spin_unlock(&i915->mm.obj_lock); + return obj; } From de927a6591b515c3548aaccbd89dc3452245af91 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Tue, 23 Oct 2018 11:02:11 +0800 Subject: [PATCH 1119/1276] drm/i915/gvt: rebase gvtbuffer to use upstream functions. Upstream code ignores pipe id passed from user space and detect active pipe automatically in intel_vgpu_decode_primary_plane. This patch rebases gvtbuffer ioctl to use the upstream functions and ignore pipe id. v2: split the patch v3: add CONFIG_DRM_I915_GVT enable check and gvt null check v4: only build i915_gem_gvtbuffer.c when CONFIG_DRM_I915_GVT enabled Tracked-On: projectacrn/acrn-hypervisor#1576 Signed-off-by: Zhipeng Gong Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/Makefile | 3 +- drivers/gpu/drm/i915/gvt/fb_decoder.c | 67 ------------------ drivers/gpu/drm/i915/gvt/fb_decoder.h | 5 -- drivers/gpu/drm/i915/i915_drv.h | 8 +++ drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 86 ++++++++++++----------- 5 files changed, 55 insertions(+), 114 deletions(-) diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a5198df1b1ca..517620bcbadd 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -79,7 +79,6 @@ i915-y += i915_cmd_parser.o \ i915_trace_points.o \ i915_vma.o \ intel_breadcrumbs.o \ - i915_gem_gvtbuffer.o \ intel_engine_cs.o \ intel_hangcheck.o \ intel_lrc.o \ @@ -184,7 +183,7 @@ i915-y += i915_perf.o \ i915_oa_icl.o ifeq ($(CONFIG_DRM_I915_GVT),y) -i915-y += intel_gvt.o +i915-y += intel_gvt.o i915_gem_gvtbuffer.o include $(src)/gvt/Makefile endif diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 7c43c916b3d8..f638189e572c 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -513,70 +513,3 @@ int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, return 0; } - -/** - * intel_vgpu_decode_fb_format - Decode framebuffer information from raw vMMIO - * @gvt: GVT device - * @vmid: guest domain ID - * @fb: frame buffer infomation of guest. - * This function is called for query frame buffer format, so that gl can - * display guest fb in Dom0 - * - * Returns: - * Zero on success, negative error code if failed. - */ -int intel_vgpu_decode_fb_format(struct intel_gvt *gvt, int id, - struct intel_vgpu_fb_format *fb) - -{ - int i; - struct intel_vgpu *vgpu = NULL; - int ret = 0; - struct drm_i915_private *dev_priv = gvt->dev_priv; - - if (!fb) - return -EINVAL; - - /* TODO: use fine-grained refcnt later */ - mutex_lock(&gvt->lock); - - for_each_active_vgpu(gvt, vgpu, i) - if (vgpu->id == id) - break; - - if (!vgpu) { - gvt_err("Invalid vgpu ID (%d)\n", id); - mutex_unlock(&gvt->lock); - return -ENODEV; - } - - for (i = 0; i < I915_MAX_PIPES; i++) { - struct intel_vgpu_pipe_format *pipe = &fb->pipes[i]; - u32 ddi_func_ctl = vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(i)); - - if (!(ddi_func_ctl & TRANS_DDI_FUNC_ENABLE)) { - pipe->ddi_port = DDI_PORT_NONE; - } else { - u32 port = (ddi_func_ctl & TRANS_DDI_PORT_MASK) >> - TRANS_DDI_PORT_SHIFT; - if (port <= DDI_PORT_E) - pipe->ddi_port = port; - else - pipe->ddi_port = DDI_PORT_NONE; - } - - ret |= intel_vgpu_decode_primary_plane(vgpu, &pipe->primary); - ret |= intel_vgpu_decode_sprite_plane(vgpu, &pipe->sprite); - ret |= intel_vgpu_decode_cursor_plane(vgpu, &pipe->cursor); - - if (ret) { - gvt_err("Decode format error for pipe(%d)\n", i); - ret = -EINVAL; - break; - } - } - - mutex_unlock(&gvt->lock); - - return ret; -} diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index 51626759534b..6c51fe00d421 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -169,9 +169,4 @@ int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, struct intel_vgpu_cursor_plane_format *plane); int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, struct intel_vgpu_sprite_plane_format *plane); - -extern -int intel_vgpu_decode_fb_format(struct intel_gvt *pdev, int vmid, - struct intel_vgpu_fb_format *fb); - #endif diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 5d97f3b4e0a6..d2d0051ebc3d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3255,8 +3255,16 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data, void i915_oa_init_reg_state(struct intel_engine_cs *engine, struct i915_gem_context *ctx, uint32_t *reg_state); +#ifdef CONFIG_DRM_I915_GVT int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +#else +static inline int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + return -EINVAL; +} +#endif /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct i915_address_space *vm, diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c index 6e58fa1fb1d2..fb1ced042a4e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -148,53 +148,59 @@ static int gvt_decode_information(struct drm_device *dev, struct drm_i915_gem_gvtbuffer *args) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_vgpu_fb_format fb; - struct intel_vgpu_primary_plane_format *p; - struct intel_vgpu_cursor_plane_format *c; - struct intel_vgpu_pipe_format *pipe; -#if IS_ENABLED(CONFIG_DRM_I915_GVT) - u32 id = args->id; - - if (intel_vgpu_decode_fb_format(dev_priv->gvt, id, &fb)) + struct intel_gvt *gvt = dev_priv->gvt; + struct intel_vgpu_primary_plane_format p; + struct intel_vgpu_cursor_plane_format c; + struct intel_vgpu *vgpu = NULL; + int ret; + int i; + + if (!intel_gvt_active(dev_priv)) return -EINVAL; -#else - return -EINVAL; -#endif - pipe = ((args->pipe_id >= I915_MAX_PIPES) ? - NULL : &fb.pipes[args->pipe_id]); + mutex_lock(&gvt->lock); + for_each_active_vgpu(gvt, vgpu, i) + if (vgpu->id == args->id) + break; - if (!pipe || !pipe->primary.enabled) { - DRM_DEBUG_DRIVER("GVT_GEM: Invalid pipe_id: %d\n", - args->pipe_id); - return -EINVAL; + if (!vgpu) { + gvt_err("Invalid vgpu ID (%d)\n", args->id); + mutex_unlock(&gvt->lock); + return -ENODEV; } + mutex_unlock(&gvt->lock); if ((args->plane_id) == I915_GVT_PLANE_PRIMARY) { - p = &pipe->primary; - args->enabled = p->enabled; - args->x_offset = p->x_offset; - args->y_offset = p->y_offset; - args->start = p->base; - args->width = p->width; - args->height = p->height; - args->stride = p->stride; - args->bpp = p->bpp; - args->hw_format = p->hw_format; - args->drm_format = p->drm_format; - args->tiled = p->tiled; + ret = intel_vgpu_decode_primary_plane(vgpu, &p); + if (ret) + return ret; + + args->enabled = p.enabled; + args->x_offset = p.x_offset; + args->y_offset = p.y_offset; + args->start = p.base; + args->width = p.width; + args->height = p.height; + args->stride = p.stride; + args->bpp = p.bpp; + args->hw_format = p.hw_format; + args->drm_format = p.drm_format; + args->tiled = p.tiled; } else if ((args->plane_id) == I915_GVT_PLANE_CURSOR) { - c = &pipe->cursor; - args->enabled = c->enabled; - args->x_offset = c->x_hot; - args->y_offset = c->y_hot; - args->x_pos = c->x_pos; - args->y_pos = c->y_pos; - args->start = c->base; - args->width = c->width; - args->height = c->height; - args->stride = c->width * (c->bpp / 8); - args->bpp = c->bpp; + ret = intel_vgpu_decode_cursor_plane(vgpu, &c); + if (ret) + return ret; + + args->enabled = c.enabled; + args->x_offset = c.x_hot; + args->y_offset = c.y_hot; + args->x_pos = c.x_pos; + args->y_pos = c.y_pos; + args->start = c.base; + args->width = c.width; + args->height = c.height; + args->stride = c.width * (c.bpp / 8); + args->bpp = c.bpp; args->tiled = 0; } else { DRM_DEBUG_DRIVER("GVT_GEM: Invalid plaine_id: %d\n", From 8fca086832a0822da22ac01286f952f050c134d6 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Mon, 22 Oct 2018 10:40:18 +0800 Subject: [PATCH 1120/1276] drm/i915/gvt: fix missing kernel doc for cfg/mmio/vgpu/mpt The doc needs to be updated after rebase to v4.19 Tracked-On: projectacrn/acrn-hypervisor#1511 Signed-off-by: Xinyun Liu Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/cfg_space.c | 13 +++++++++++++ drivers/gpu/drm/i915/gvt/mmio.c | 4 ++-- drivers/gpu/drm/i915/gvt/mpt.h | 21 +++++++++++++++++++-- drivers/gpu/drm/i915/gvt/vgpu.c | 2 ++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 8606925d339d..4cf3f1c00cd2 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -56,6 +56,10 @@ static const u8 pci_cfg_space_rw_bmp[PCI_INTERRUPT_LINE + 4] = { /** * vgpu_pci_cfg_mem_write - write virtual cfg space memory + * @vgpu: a vGPU + * @off: offset into the PCI configuration space + * @src: data buffer write to vGPU's emulated configure space + * @bytes: size of data to write in bytes * * Use this function to write virtual cfg space memory. * For standard cfg space, only RW bits can be changed, @@ -92,6 +96,11 @@ static void vgpu_pci_cfg_mem_write(struct intel_vgpu *vgpu, unsigned int off, /** * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space read * + * @vgpu: a vGPU + * @offset: offset into the PCI configuration space + * @p_data: data buffer read from vGPU's emulated configure space + * @bytes: size of data to read in bytes + * * Returns: * Zero on success, negative error code if failed. */ @@ -326,6 +335,10 @@ static int emulate_pci_bar_write(struct intel_vgpu *vgpu, unsigned int offset, /** * intel_vgpu_emulate_cfg_read - emulate vGPU configuration space write + * @vgpu: a vGPU + * @offset: offset into the PCI configuration space + * @p_data: data buffer write to vGPU's emulated configure space + * @bytes: size of data to write in bytes * * Returns: * Zero on success, negative error code if failed. diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index b6a974f55805..8a28db93bb1e 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -39,7 +39,7 @@ /** * intel_vgpu_gpa_to_mmio_offset - translate a GPA to MMIO offset * @vgpu: a vGPU - * + * @gpa: guest physical address * Returns: * Zero on success, negative error code if failed */ @@ -236,7 +236,7 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa, /** * intel_vgpu_reset_mmio - reset virtual MMIO space * @vgpu: a vGPU - * + * @dmlr: vGPU Device Model Level Reset or GT Reset */ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) { diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index 6eef2e01e46a..14fa2ea047fa 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -45,6 +45,9 @@ /** * intel_gvt_hypervisor_host_init - init GVT-g host side + * @dev: i915 device + * @gvt: GVT device + * @ops: intel_gvt_ops interface * * Returns: * Zero on success, negative error code if failed @@ -61,6 +64,8 @@ static inline int intel_gvt_hypervisor_host_init(struct device *dev, /** * intel_gvt_hypervisor_host_exit - exit GVT-g host side + * @dev: i915 device + * @gvt: GVT device */ static inline void intel_gvt_hypervisor_host_exit(struct device *dev, void *gvt) @@ -75,6 +80,7 @@ static inline void intel_gvt_hypervisor_host_exit(struct device *dev, /** * intel_gvt_hypervisor_attach_vgpu - call hypervisor to initialize vGPU * related stuffs inside hypervisor. + * @vgpu: a vGPU * * Returns: * Zero on success, negative error code if failed. @@ -91,6 +97,7 @@ static inline int intel_gvt_hypervisor_attach_vgpu(struct intel_vgpu *vgpu) /** * intel_gvt_hypervisor_detach_vgpu - call hypervisor to release vGPU * related stuffs inside hypervisor. + * @vgpu: a vGPU * * Returns: * Zero on success, negative error code if failed. @@ -111,6 +118,7 @@ static inline void intel_gvt_hypervisor_detach_vgpu(struct intel_vgpu *vgpu) /** * intel_gvt_hypervisor_inject_msi - inject a MSI interrupt into vGPU + * @vgpu: a vGPU * * Returns: * Zero on success, negative error code if failed. @@ -142,7 +150,7 @@ static inline int intel_gvt_hypervisor_inject_msi(struct intel_vgpu *vgpu) } /** - * intel_gvt_hypervisor_set_wp_page - translate a host VA into MFN + * intel_gvt_hypervisor_virt_to_mfn - translate a host VA into MFN * @p: host kernel virtual address * * Returns: @@ -216,7 +224,7 @@ static inline int intel_gvt_hypervisor_write_gpa(struct intel_vgpu *vgpu, /** * intel_gvt_hypervisor_gfn_to_mfn - translate a GFN to MFN * @vgpu: a vGPU - * @gpfn: guest pfn + * @gfn: guest pfn * * Returns: * MFN on success, INTEL_GVT_INVALID_ADDR if failed. @@ -383,6 +391,15 @@ static inline bool intel_gvt_hypervisor_is_valid_gfn( return intel_gvt_host.mpt->is_valid_gfn(vgpu->handle, gfn); } +/** + * intel_gvt_hypervisor_dom0_ready - Signal Dom 0 is ready for Dom U + * + * It's to raise a uevent to notify Dom 0 is ready to start a Dom U, so that + * Dom U can be started as early as possible + * + * Returns: + * Zero on success, negative error code if failed + */ static inline int intel_gvt_hypervisor_dom0_ready(void) { if (!intel_gvt_host.mpt->dom0_ready) diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 511681b24c59..5411b402f728 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -117,6 +117,8 @@ static struct { * * Initialize vGPU type list based on available resource. * + * Returns: + * Zero on success, negative error code if failed. */ int intel_gvt_init_vgpu_types(struct intel_gvt *gvt) { From 3590dbd0a1d4800ed3eeac8e6e9b91bbd69945fa Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Mon, 29 Oct 2018 10:02:48 +0800 Subject: [PATCH 1121/1276] drm/i915/gvt: not to touch undefined MOCS registers Some engines are not available for all Gens, so need to add check before access them. Tracked-On: projectacrn/acrn-hypervisor#1581 Signed-off-by: Xinyun Liu Signed-off-by: Yakui Zhao --- drivers/gpu/drm/i915/gvt/mmio_context.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c index dc0a14729b88..4e99cd0e4fbe 100644 --- a/drivers/gpu/drm/i915/gvt/mmio_context.c +++ b/drivers/gpu/drm/i915/gvt/mmio_context.c @@ -171,6 +171,8 @@ static void load_render_mocs(struct drm_i915_private *dev_priv) int ring_id, i; for (ring_id = 0; ring_id < ARRAY_SIZE(regs); ring_id++) { + if (!HAS_ENGINE(dev_priv, ring_id)) + continue; offset.reg = regs[ring_id]; for (i = 0; i < GEN9_MOCS_SIZE; i++) { gen9_render_mocs.control_table[ring_id][i] = From 295665084f82a9a3674bfbe9e7fded7f493fe848 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Tue, 23 Oct 2018 11:31:17 +0800 Subject: [PATCH 1122/1276] drm/i915/gvt: use snprintf instead of sprintf To use snprintf to avoid potential overflow, although vgpu->id is guaranteed in the range of [0, GVT_MAX_VGPU] and should have no real issue. Tracked-On: projectacrn/acrn-hypervisor#1581 Signed-off-by: Xinyun Liu Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/debugfs.c b/drivers/gpu/drm/i915/gvt/debugfs.c index 2ec89bcb59f1..c41e1e245789 100644 --- a/drivers/gpu/drm/i915/gvt/debugfs.c +++ b/drivers/gpu/drm/i915/gvt/debugfs.c @@ -196,9 +196,9 @@ DEFINE_SIMPLE_ATTRIBUTE(vgpu_scan_nonprivbb_fops, int intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu) { struct dentry *ent; - char name[10] = ""; + char name[10]; - sprintf(name, "vgpu%d", vgpu->id); + snprintf(name, sizeof(name), "vgpu%d", vgpu->id); vgpu->debugfs = debugfs_create_dir(name, vgpu->gvt->debugfs_root); if (!vgpu->debugfs) return -ENOMEM; From 0f01b6a447b0cd62ad174639ba004ed9fe542de3 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Wed, 24 Oct 2018 19:50:08 +0800 Subject: [PATCH 1123/1276] drm/i915/gvt: check the pointer before using fix issues reported by smatch: drivers/gpu/drm/i915/intel_pm.c:828 intel_wm_plane_visible() error: we previously assumed 'plane_state' could be null (see line 815) drivers/gpu/drm/i915/intel_pm.c:3998 skl_plane_downscale_amount() error: we previously assumed 'pstate' could be null (see line 3985) drivers/gpu/drm/i915/intel_pm.c:4534 skl_compute_plane_wm_params() error: we previously assumed 'fb' could be null (see line 4510) drivers/gpu/drm/i915/intel_pm.c:4544 skl_compute_plane_wm_params() error: we previously assumed 'intel_pstate' could be null (see line 4521) drivers/gpu/drm/i915/intel_pm.c:4565 skl_compute_plane_wm_params() error: we previously assumed 'pstate' could be null (see line 4501) Tracked-On: projectacrn/acrn-hypervisor#1581 Signed-off-by: Xinyun Liu Reviewed-by: He, Min --- drivers/gpu/drm/i915/intel_atomic_plane.c | 3 ++- drivers/gpu/drm/i915/intel_display.c | 4 ++++ drivers/gpu/drm/i915/intel_pm.c | 15 ++++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index dcba645cabb8..b950fabb4bbf 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -182,7 +182,8 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_ else crtc_state->active_planes &= ~BIT(intel_plane->id); - if (state->visible && state->fb->format->format == DRM_FORMAT_NV12) + if (state->visible && state->fb && + state->fb->format->format == DRM_FORMAT_NV12) crtc_state->nv12_planes |= BIT(intel_plane->id); else crtc_state->nv12_planes &= ~BIT(intel_plane->id); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5d2bf527cda2..b0e721a9b876 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4947,6 +4947,10 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, } /* Check src format */ + if (!fb) { + DRM_ERROR("skl_update_scaler_plane(): fb is invalid\n"); + return 0; + } switch (fb->format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_XBGR8888: diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d4510a5504e1..eb3a0a387469 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -816,6 +816,11 @@ static bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state, return true; } + if(!plane_state) { + DRM_ERROR("intel_wm_plane_visible(): plane_state==NULL and return 0\n"); + return false; + } + /* * Treat cursor with fb as always visible since cursor updates * can happen faster than the vrefresh rate, and the current @@ -3986,6 +3991,10 @@ skl_plane_downscale_amount(const struct intel_crtc_state *cstate, return mul_fixed16(u32_to_fixed16(1), u32_to_fixed16(1)); } + if (WARN_ON_ONCE(!pstate)) { + return u32_to_fixed16(0); + } + if (WARN_ON(!intel_wm_plane_visible(cstate, pstate))) return u32_to_fixed16(0); @@ -4531,6 +4540,10 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, goto calculate_wm; } + if (!fb || !intel_pstate) { + DRM_ERROR("invalid fb:%p intel_pstate:%p\n", fb, intel_pstate); + return -EINVAL; + } wp->y_tiled = fb->modifier == I915_FORMAT_MOD_Y_TILED || fb->modifier == I915_FORMAT_MOD_Yf_TILED || fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS || @@ -4562,7 +4575,7 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, else wp->dbuf_block_size = 512; - if (drm_rotation_90_or_270(pstate->rotation)) { + if (pstate && drm_rotation_90_or_270(pstate->rotation)) { switch (wp->cpp) { case 1: From fe9f14f9bd6e51dd458b2bf608259a7fa79ef333 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Thu, 24 May 2018 11:52:44 +0800 Subject: [PATCH 1124/1276] drm/i915/gvt: check msg length before use it Spec said `Message Size` should be (0, 20]. Add check code and make static analysis happy also. Cherry-pick f977f43dbda and fda72d479ac from v4.14 branch Tracked-On: projectacrn/acrn-hypervisor#1581 Signed-off-by: Xinyun Liu Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/edid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index fb690a4f55a0..c4bf4800f72f 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -448,7 +448,7 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, u32 value = *(u32 *)p_data; int aux_data_for_write = 0; int reg = get_aux_ch_reg(offset); - uint8_t rxbuf[20]; + uint8_t rxbuf[20] = {0}; size_t rxsize; if (reg != AUX_CH_CTL) { @@ -457,6 +457,9 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, } msg_length = AUX_CTL_MSG_LENGTH(value); + if (WARN_ON(msg_length <= 0 || msg_length > 20)) + return; + for (rxsize = 0; rxsize < msg_length; rxsize += 4) intel_dp_unpack_aux(vgpu_vreg(vgpu, offset + 4 + rxsize), rxbuf + rxsize, msg_length - rxsize); From 124b7282a20bddfdf971ae95dd71a0517e8d9673 Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Sat, 27 Oct 2018 02:10:21 +0800 Subject: [PATCH 1125/1276] kernel: remove deprecated VHM IOCTLs below IOCTLs are wrapped into IC_SET_IRQLINE: - IC_ASSERT_IRQLINE - IC_DEASSERT_IRQLINE - IC_PULSE_IRQLINE Tracked-On: projectacrn/acrn-hypervisor#861 Signed-off-by: Yonghua Huang Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 41 ------------------------------ drivers/vhm/vhm_hypercall.c | 15 ----------- include/linux/vhm/acrn_common.h | 34 ------------------------- include/linux/vhm/acrn_hv_defs.h | 3 --- include/linux/vhm/vhm_ioctl_defs.h | 3 --- 5 files changed, 96 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index f730467ca8d9..a2217949ec74 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -395,47 +395,6 @@ static long vhm_dev_ioctl(struct file *filep, break; } - case IC_ASSERT_IRQLINE: { - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = hcall_assert_irqline(vm->vmid, virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - break; - } - case IC_DEASSERT_IRQLINE: { - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = hcall_deassert_irqline(vm->vmid, virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to deassert irq!\n"); - return -EFAULT; - } - break; - } - case IC_PULSE_IRQLINE: { - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = hcall_pulse_irqline(vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - break; - } - case IC_SET_IRQLINE: { ret = hcall_set_irqline(vm->vmid, ioctl_param); if (ret < 0) { diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 6c67d04dd77e..55289bd70e16 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -127,21 +127,6 @@ inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu) return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu); } -inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq) -{ - return acrn_hypercall2(HC_ASSERT_IRQLINE, vmid, irq); -} - -inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq) -{ - return acrn_hypercall2(HC_DEASSERT_IRQLINE, vmid, irq); -} - -inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq) -{ - return acrn_hypercall2(HC_PULSE_IRQLINE, vmid, irq); -} - inline long hcall_set_irqline(unsigned long vmid, unsigned long op) { return acrn_hypercall2(HC_SET_IRQLINE, vmid, op); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 179957d273e9..d28ce51ff271 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -354,40 +354,6 @@ struct acrn_set_ioreq_buffer { uint64_t req_buf; } __attribute__((aligned(8))); -/** Interrupt type for acrn_irqline: inject interrupt to IOAPIC */ -#define ACRN_INTR_TYPE_ISA 0 - -/** Interrupt type for acrn_irqline: inject interrupt to both PIC and IOAPIC */ -#define ACRN_INTR_TYPE_IOAPIC 1 - -/** - * @brief Info to assert/deassert/pulse a virtual IRQ line for a VM - * - * the parameter for HC_ASSERT_IRQLINE/HC_DEASSERT_IRQLINE/HC_PULSE_IRQLINE - * hypercall - */ -struct acrn_irqline { - /** interrupt type which could be IOAPIC or ISA */ - uint32_t intr_type; - - /** reserved for alignment padding */ - uint32_t reserved; - - /** pic IRQ for ISA type */ - uint32_t pic_irq; - - /** Reserved */ - uint32_t reserved0; - - /** ioapic IRQ for IOAPIC & ISA TYPE, - * if ~0U then this IRQ will not be injected - */ - uint32_t ioapic_irq; - - /** Reserved */ - uint32_t reserved1; -} __attribute__((aligned(8))); - /** * @brief Info to inject a MSI interrupt to VM * diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d9160221f657..af00025953c2 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -87,9 +87,6 @@ /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL -#define HC_ASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x00) -#define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) -#define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) #define HC_VM_INTR_MONITOR _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x04) #define HC_SET_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x05) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 73be2dde243d..19ebf54e57f3 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -77,9 +77,6 @@ /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL -#define IC_ASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x00) -#define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) -#define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) #define IC_VM_INTR_MONITOR _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x04) #define IC_SET_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x05) From badc152592f690b11a69f9f40f3c2e247a9658a7 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Mon, 29 Oct 2018 11:45:11 -0700 Subject: [PATCH 1126/1276] Fix warnings introduced by acrn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 35 warnings, drivers/pci/pci.h:171:41: warning: ‘struct msi_desc’ declared inside parameter list will not be visible outside of this definition or declaration Introduced by the following patch, x86:acrn: add write_msi pv ops to intercept pci msi write with pv method Add this patch to address it. Signed-off-by: Fei Yang --- arch/x86/include/asm/paravirt_types.h | 2 +- drivers/pci/pci.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 06e01d87d76a..9929ab59774b 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -58,7 +58,7 @@ struct mmu_gather; struct msi_desc; struct msi_msg; - + /* * Wrapper type for pointers to code which uses the non-standard * calling convention. See PV_CALL_SAVE_REGS_THUNK below. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d3fe892641d0..9887a92b5e8e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -9,6 +9,8 @@ extern const unsigned char pcie_link_speed[]; extern bool pci_early_dump; +struct msi_desc; + bool pcie_cap_has_lnkctl(const struct pci_dev *dev); /* Functions internal to the PCI core code */ From 5ad87c203efe43630517bf424bccb97ad7373501 Mon Sep 17 00:00:00 2001 From: Qi Yadong Date: Thu, 18 Oct 2018 15:41:11 +0800 Subject: [PATCH 1127/1276] rpmb: mux: add HKDF for RPMB Mux module Implement a HKDF (hash based key derivation function) for RPMB key derivation. Change-Id: Ic1a0d270c32d9ba101369ef10065b93f5c7ec479 Signed-off-by: Huang, Yang Signed-off-by: Wei, Xinghai Signed-off-by: Qi Yadong Signed-off-by: Tomas Winkler --- drivers/char/rpmb/mux/Makefile | 4 +- drivers/char/rpmb/mux/mux_hkdf.c | 166 +++++++++++++++++++++++++++++++ drivers/char/rpmb/mux/mux_hkdf.h | 14 +++ 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 drivers/char/rpmb/mux/mux_hkdf.c create mode 100644 drivers/char/rpmb/mux/mux_hkdf.h diff --git a/drivers/char/rpmb/mux/Makefile b/drivers/char/rpmb/mux/Makefile index 7afc458a0dd2..23234a88aa73 100644 --- a/drivers/char/rpmb/mux/Makefile +++ b/drivers/char/rpmb/mux/Makefile @@ -1,3 +1,5 @@ -obj-$(CONFIG_RPMB_MUX) += mux.o +obj-$(CONFIG_RPMB_MUX) += rpmb_mux.o +rpmb_mux-objs := mux.o +rpmb_mux-objs += mux_hkdf.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/mux/mux_hkdf.c b/drivers/char/rpmb/mux/mux_hkdf.c new file mode 100644 index 000000000000..d4234924148e --- /dev/null +++ b/drivers/char/rpmb/mux/mux_hkdf.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * RPMB Mux HKDF + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#include +#include +#include "mux_hkdf.h" + +static int mux_sha256_extract(u8 *out_key, size_t out_len, + struct shash_desc *desc, + const u8 *secret, size_t secret_len, + const u8 *salt, size_t salt_len) +{ + int ret; + u8 salt0[SHA256_HASH_SIZE]; + + if (!salt || !salt_len) { + memset(salt0, 0, sizeof(salt0)); + salt = salt0; + salt_len = sizeof(salt0); + } + + ret = crypto_shash_setkey(desc->tfm, salt, salt_len); + if (ret) { + pr_err("set key failed = %d\n", ret); + goto out; + } + + ret = crypto_shash_init(desc); + if (ret) + goto out; + + ret = crypto_shash_update(desc, secret, secret_len); + if (ret) + goto out; + + ret = crypto_shash_final(desc, out_key); + if (ret) + goto out; + +out: + return ret; +} + +static int mux_sha256_expand(u8 *out_key, size_t out_len, + struct shash_desc *desc, + const u8 *prk, size_t prk_len, + const u8 *info, size_t info_len) +{ + const size_t digest_len = SHA256_HASH_SIZE; + u8 previous[SHA256_HASH_SIZE]; + size_t n, done = 0; + unsigned int i; + int ret = 0; + + n = (out_len + digest_len - 1) / digest_len; + + /* check for possible integer overflow */ + if (out_len + digest_len < out_len) + return 0; + + if (n > 255) + return 0; + + for (i = 0; i < n; i++) { + u8 ctr = i + 1; + size_t todo; + + ret = crypto_shash_setkey(desc->tfm, prk, prk_len); + if (ret) + goto out; + + ret = crypto_shash_init(desc); + if (ret) + goto out; + + if (i != 0 && crypto_shash_update(desc, previous, digest_len)) + goto out; + + if (crypto_shash_update(desc, info, info_len) || + crypto_shash_update(desc, &ctr, 1) || + crypto_shash_final(desc, previous)) { + ret = -EPERM; + goto out; + } + + todo = digest_len; + /* Check if the length of left buffer is smaller than + * 32 to make sure no buffer overflow in below memcpy + */ + if (done + todo > out_len) + todo = out_len - done; + + memcpy(out_key + done, previous, todo); + done += todo; + } + +out: + memset(previous, 0, sizeof(previous)); + + return ret; +} + +static struct shash_desc *mux_hkdf_init_hmac_sha256_desc(void) +{ + struct shash_desc *desc; + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return ERR_PTR(-EFAULT); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return ERR_PTR(-ENOMEM); + } + desc->tfm = tfm; + + return desc; +} + +int mux_hkdf_sha256(u8 *out_key, size_t out_len, + const u8 *secret, size_t secret_len, + const u8 *salt, size_t salt_len, + const u8 *info, size_t info_len) +{ + u8 prk[SHA256_HASH_SIZE]; + size_t prk_len = SHA256_HASH_SIZE; + int ret; + struct shash_desc *desc; + + if (!out_key || !out_len) + return -EINVAL; + + if (!secret || !secret_len) + return -EINVAL; + + if (!info && info_len) + return -EINVAL; + + desc = mux_hkdf_init_hmac_sha256_desc(); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + memset(prk, 0, sizeof(prk)); + + ret = mux_sha256_extract(prk, prk_len, desc, + secret, secret_len, + salt, salt_len); + if (ret) + goto err_free_shash; + + ret = mux_sha256_expand(out_key, out_len, desc, + prk, prk_len, + info, info_len); + +err_free_shash: + crypto_free_shash(desc->tfm); + kfree(desc); + + return ret; +} diff --git a/drivers/char/rpmb/mux/mux_hkdf.h b/drivers/char/rpmb/mux/mux_hkdf.h new file mode 100644 index 000000000000..0055884054c2 --- /dev/null +++ b/drivers/char/rpmb/mux/mux_hkdf.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2018 Intel Corp. All rights reserved + */ +#ifndef _MUX_HKDF_H +#define _MUX_HKDF_H + +#define SHA256_HASH_SIZE 32 + +int mux_hkdf_sha256(u8 *out_key, size_t out_len, + const u8 *secret, size_t secret_len, + const u8 *salt, size_t salt_len, + const u8 *info, size_t info_len); +#endif /* !_MUX_HKDF_H */ From dce00b2192a31630799ce5f93fea015b6e36b7e6 Mon Sep 17 00:00:00 2001 From: Qi Yadong Date: Wed, 17 Oct 2018 15:10:41 +0800 Subject: [PATCH 1128/1276] rpmb: mux: add key retrieval for RPMB multiplexor Retrieve a RPMB key from a bootloader. Currently automotive bootloader and slim bootloader are supported. Change-Id: If5ab4024fc1cf02967fdc88f097f6918d3833b2b Signed-off-by: Qi Yadong Signed-off-by: Huang, Yang Signed-off-by: Tomas Winkler --- .../admin-guide/kernel-parameters.txt | 4 + drivers/char/rpmb/mux/Makefile | 3 + drivers/char/rpmb/mux/key.c | 58 ++++++ drivers/char/rpmb/mux/key.h | 24 +++ drivers/char/rpmb/mux/key_abl.c | 143 +++++++++++++++ drivers/char/rpmb/mux/key_abl.h | 9 + drivers/char/rpmb/mux/key_sbl.c | 166 ++++++++++++++++++ drivers/char/rpmb/mux/key_sbl.h | 9 + drivers/char/rpmb/mux/mux.c | 60 ++++--- 9 files changed, 452 insertions(+), 24 deletions(-) create mode 100644 drivers/char/rpmb/mux/key.c create mode 100644 drivers/char/rpmb/mux/key.h create mode 100644 drivers/char/rpmb/mux/key_abl.c create mode 100644 drivers/char/rpmb/mux/key_abl.h create mode 100644 drivers/char/rpmb/mux/key_sbl.c create mode 100644 drivers/char/rpmb/mux/key_sbl.h diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..82f27285be6b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -741,6 +741,10 @@ Format: , See also Documentation/input/devices/joystick-parport.rst + dev_sec_info.param_addr= + [BOOT] address of automotive bootloader (abl) + security parameters. + ddebug_query= [KNL,DYNAMIC_DEBUG] Enable debug messages at early boot time. See Documentation/admin-guide/dynamic-debug-howto.rst for diff --git a/drivers/char/rpmb/mux/Makefile b/drivers/char/rpmb/mux/Makefile index 23234a88aa73..94999dd468db 100644 --- a/drivers/char/rpmb/mux/Makefile +++ b/drivers/char/rpmb/mux/Makefile @@ -1,5 +1,8 @@ obj-$(CONFIG_RPMB_MUX) += rpmb_mux.o rpmb_mux-objs := mux.o rpmb_mux-objs += mux_hkdf.o +rpmb_mux-objs += key.o +rpmb_mux-objs += key_abl.o +rpmb_mux-objs += key_sbl.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/mux/key.c b/drivers/char/rpmb/mux/key.c new file mode 100644 index 000000000000..e9df04765ad7 --- /dev/null +++ b/drivers/char/rpmb/mux/key.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * RPMB Key management: key retrieval + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include + +#include "key.h" +#include "key_sbl.h" +#include "key_abl.h" + +static ulong sbl_params_addr; +static ulong abl_params_addr; + +static int __init get_sbl_params_addr(char *str) +{ + if (kstrtoul(str, 16, &sbl_params_addr)) { + pr_err("Failed to parse ImageBootParamsAddr\n"); + return -EINVAL; + } + + return 0; +} +__setup("ImageBootParamsAddr=", get_sbl_params_addr); + +static int __init get_abl_params_addr(char *str) +{ + if (kstrtoul(str, 16, &abl_params_addr)) { + pr_err("Failed to parse dev_sec_info.param\n"); + return -EINVAL; + } + + return 0; +} +__setup("dev_sec_info.param_addr=", get_abl_params_addr); + +int rpmb_key_get(const u8 *dev_id, size_t dev_id_len, + size_t max_partition_num, u8 rpmb_key[][RPMB_KEY_LENGTH]) +{ + int ret = -1; + + if (sbl_params_addr) + ret = rpmb_key_sbl_get(sbl_params_addr, max_partition_num, + rpmb_key); + else if (abl_params_addr) + ret = rpmb_key_abl_get(abl_params_addr, dev_id, dev_id_len, + max_partition_num, rpmb_key); + else + pr_err("Failed to get boot_params from the command line!\n"); + + return ret; +} diff --git a/drivers/char/rpmb/mux/key.h b/drivers/char/rpmb/mux/key.h new file mode 100644 index 000000000000..8c17fbcfc820 --- /dev/null +++ b/drivers/char/rpmb/mux/key.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * RPMB Key management: retrieve and distribute + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#ifndef __RPMB_KEY_H__ +#define __RPMB_KEY_H__ + +/* + * Storage may support multiple rpmb partitions, but the specification + * does not specify the max number of rpmb partitions. + * Here we use 6 for now. In future, this may need to be expanded + * dynamically. + */ +#define RPMB_MAX_PARTITION_NUMBER 6U + +#define RPMB_KEY_LENGTH 64U + +int rpmb_key_get(const u8 *dev_id, size_t dev_id_len, + size_t max_partition_num, u8 rpmb_key[][RPMB_KEY_LENGTH]); + +#endif /* !__RPMB_KEY_H__ */ diff --git a/drivers/char/rpmb/mux/key_abl.c b/drivers/char/rpmb/mux/key_abl.c new file mode 100644 index 000000000000..bcb735562218 --- /dev/null +++ b/drivers/char/rpmb/mux/key_abl.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Parse legacy seed from ABL(Automotive Bootloader). Derive a rpmb key + * with the legacy seed. + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +#include "key.h" +#include "key_abl.h" +#include "mux_hkdf.h" + +#define ABL_SEED_LEN 32U +#define ABL_SEED_LIST_MAX 4U +#define EMMC_SERIAL_LEN 15U + +struct abl_seed_info { + u8 svn; + u8 reserved[3]; + u8 seed[ABL_SEED_LEN]; +}; + +struct dev_sec_info { + u32 size_of_this_struct; + u32 version; + u32 num_seeds; + struct abl_seed_info seed_list[ABL_SEED_LIST_MAX]; +}; + +/* + * The output serial is concatenation of mmc product name with a string + * representation of PSN. + */ +static int rpmb_key_abl_build_serial(const u8 *cid, u8 *serial) +{ + u32 psn; + + if (!cid || !serial) + return -EFAULT; + + psn = (cid[9] << 24) | (cid[8] << 16) | (cid[15] << 8) | cid[14]; + + serial[0] = cid[0]; + serial[1] = cid[7]; + serial[2] = cid[6]; + serial[3] = cid[5]; + serial[4] = cid[4]; + serial[5] = cid[11]; + + snprintf(&serial[6], 9, "%08x", psn); + + return 0; +} + +int rpmb_key_abl_get(ulong params_addr, const u8 *dev_id, size_t dev_id_len, + size_t max_partition_num, u8 rpmb_key[][RPMB_KEY_LENGTH]) +{ + u32 i, legacy_seed_index = 0; + struct dev_sec_info *sec_info; + struct abl_seed_info *seed_list; + u8 serial[EMMC_SERIAL_LEN] = {0}; + int ret; + + if (!params_addr || !dev_id || !dev_id_len || !max_partition_num) { + pr_err("Invalid input params!\n"); + return -EFAULT; + } + + ret = rpmb_key_abl_build_serial(dev_id, serial); + if (ret) { + pr_err("Failed to build serial from cid\n"); + return -EFAULT; + } + + sec_info = memremap(params_addr, sizeof(*sec_info), MEMREMAP_WB); + if (!sec_info) { + pr_err("Remap params_addr failed!\n"); + return -EFAULT; + } + seed_list = &sec_info->seed_list[0]; + + /* + * The seed_list must contain at least 2 seeds: 1 is legacy + * seed and others are SVN based seed. + */ + if (sec_info->num_seeds < 2U || + sec_info->num_seeds > ABL_SEED_LIST_MAX) { + pr_err("Invalid seed number!\n"); + memunmap(sec_info); + return -EFAULT; + } + + /* + * The seed_list from ABL contains several seeds which based on SVN + * and one legacy seed which is not based on SVN. The legacy seed's + * svn value is minimum in the seed list. And CSE ensures at least two + * seeds will be generated which will contain the legacy seed. + * Here find the legacy seed index first. + */ + for (i = 1; i < sec_info->num_seeds; i++) { + if (seed_list[i].svn < seed_list[legacy_seed_index].svn) + legacy_seed_index = i; + } + + /* + * The eMMC Field Firmware Update would impact below fields of + * CID(Card Identification): + * CID[6]:PRV (Product Revision) + * CID[0]:CRC (CRC7 checksum) + * Mapping relation between CID and eMMC serial: + * serial[0] = CID[0] + * serial[2] = CID[6] + * So mask off serial[0]/serial[2] fields when using eMMC serial + * to derive rpmb key. + */ + serial[0] ^= serial[0]; + serial[2] ^= serial[2]; + + /* + * Derive RPMB key from legacy seed with storage serial number. + * Currently, only support eMMC storage device, UFS storage device is + * not supported. + */ + ret = mux_hkdf_sha256(&rpmb_key[0][0], SHA256_HASH_SIZE, + (const u8 *)&seed_list[legacy_seed_index].seed[0], + ABL_SEED_LEN, + NULL, 0, + (const u8 *)serial, sizeof(serial)); + + memset(&seed_list[legacy_seed_index], 0, sizeof(struct abl_seed_info)); + memunmap(sec_info); + + return ret; +} diff --git a/drivers/char/rpmb/mux/key_abl.h b/drivers/char/rpmb/mux/key_abl.h new file mode 100644 index 000000000000..0d2a09abde60 --- /dev/null +++ b/drivers/char/rpmb/mux/key_abl.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ + +#ifndef __RPMB_KEY_ABL__ +#define __RPMB_KEY_ABL__ + +int rpmb_key_abl_get(ulong params_addr, const u8 *dev_id, size_t dev_id_len, + size_t max_partition_num, u8 rpmb_key[][RPMB_KEY_LENGTH]); + +#endif /* !__RPMB_KEY_ABL__ */ diff --git a/drivers/char/rpmb/mux/key_sbl.c b/drivers/char/rpmb/mux/key_sbl.c new file mode 100644 index 000000000000..8a238ac7f5ae --- /dev/null +++ b/drivers/char/rpmb/mux/key_sbl.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Parse RPMB key from SBL(SlimBootloader). + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + +#include +#include +#include +#include +#include + +#include "key.h" +#include "key_sbl.h" + +#define SEED_ENTRY_TYPE_SVNSEED 0x1U +#define SEED_ENTRY_TYPE_RPMBSEED 0x2U + +#define SEED_ENTRY_USAGE_BASE_ON_SERIAL 0x1U +#define SEED_ENTRY_USAGE_NOT_BASE_ON_SERIAL 0x2U + +struct image_boot_params { + u32 size_of_this_struct; + u32 version; + u64 p_seed_list; + u64 p_platform_info; + u64 reserved; +}; + +struct seed_entry { + /* SVN based seed or RPMB seed or attestation key_box */ + u8 type; + /* For SVN seed: useed or dseed + * For RPMB seed: serial number based or not + */ + u8 usage; + /* index for the same type and usage seed */ + u8 index; + u8 reserved; + /* reserved for future use */ + u16 flags; + /* Total size of this seed entry */ + u16 seed_entry_size; + /* SVN seed: struct seed_info + * RPMB seed: u8 rpmb_seed[key_len] + */ + u8 seed[0]; +}; + +struct seed_list_hob { + u8 revision; + u8 rsvd0[3]; + u32 buffer_size; + u8 total_seed_count; + u8 rsvd1[3]; + struct seed_entry entry[0]; +}; + +static int rpmb_key_sbl_parse_seed_list(struct seed_list_hob *seed_hob, + size_t max_partition_num, + u8 rpmb_seed[][RPMB_KEY_LENGTH]) +{ + u8 i; + u8 index = 0U; + struct seed_entry *entry; + + if (!seed_hob || !max_partition_num) { + pr_warn("Invalid input parameters!\n"); + goto fail; + } + + if (seed_hob->total_seed_count == 0U) { + pr_warn("Total seed count is 0.\n"); + goto fail; + } + + entry = seed_hob->entry; + + for (i = 0U; i < seed_hob->total_seed_count; i++) { + if ((u8 *)entry >= (u8 *)seed_hob + seed_hob->buffer_size) { + pr_warn("Exceed memory boundray!\n"); + goto fail; + } + + /* retrieve rpmb seed */ + if (entry->type == SEED_ENTRY_TYPE_RPMBSEED) { + if (entry->index != 0) { + pr_warn("RPMB usage mismatch!\n"); + goto fail; + } + + /* The seed_entry with same type/usage are always + * arranged by index in order of 0~3. + */ + if (entry->index != index) { + pr_warn("Index mismatch.\n"); + goto fail; + } + + if (entry->index > max_partition_num) { + pr_warn("Index exceed max number!\n"); + goto fail; + } + + memcpy(&rpmb_seed[index], entry->seed, RPMB_KEY_LENGTH); + index++; + + /* erase original seed in seed entry */ + memset(entry->seed, 0U, RPMB_KEY_LENGTH); + } + + entry = (struct seed_entry *)((u8 *)entry + + entry->seed_entry_size); + } + + return 0; + +fail: + return -EFAULT; +} + +int rpmb_key_sbl_get(ulong params_addr, size_t max_partition_num, + u8 rpmb_key[][RPMB_KEY_LENGTH]) +{ + struct image_boot_params *boot_params = NULL; + struct seed_list_hob *seed_list = NULL; + u32 remap_buffer_size = 0; + + if (!params_addr || !max_partition_num) { + pr_err("Invalid input params!\n"); + goto fail; + } + + boot_params = memremap(params_addr, sizeof(*boot_params), MEMREMAP_WB); + if (!boot_params) { + pr_err("Remap params_addr failed!\n"); + goto fail; + } + + seed_list = memremap(boot_params->p_seed_list, + sizeof(*seed_list), MEMREMAP_WB); + if (!seed_list) { + pr_err("Remap seed_list failed!\n"); + goto fail; + } + + remap_buffer_size = seed_list->buffer_size; + memunmap(seed_list); + + /* Remap with actual buffer size */ + seed_list = memremap(boot_params->p_seed_list, + remap_buffer_size, MEMREMAP_WB); + + return rpmb_key_sbl_parse_seed_list(seed_list, max_partition_num, + rpmb_key); + +fail: + if (seed_list) + memunmap(seed_list); + if (boot_params) + memunmap(boot_params); + return -EFAULT; +} diff --git a/drivers/char/rpmb/mux/key_sbl.h b/drivers/char/rpmb/mux/key_sbl.h new file mode 100644 index 000000000000..0483c176012c --- /dev/null +++ b/drivers/char/rpmb/mux/key_sbl.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ + +#ifndef __RPMB_KEY_SBL__ +#define __RPMB_KEY_SBL__ + +int rpmb_key_sbl_get(ulong params_addr, size_t max_partition_num, + u8 rpmb_key[][RPMB_KEY_LENGTH]); + +#endif /* __RPMB_KEY_SBL__ */ diff --git a/drivers/char/rpmb/mux/mux.c b/drivers/char/rpmb/mux/mux.c index 55901197d0a5..c7caa0406906 100644 --- a/drivers/char/rpmb/mux/mux.c +++ b/drivers/char/rpmb/mux/mux.c @@ -5,6 +5,8 @@ * Copyright (c) 2018 Intel Corporation. All rights reserved. */ +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + #include #include #include @@ -17,6 +19,8 @@ #include #include +#include "key.h" + /** * struct rpmb_mux_dev - device which can support RPMB partition * @lock : the device lock @@ -68,13 +72,6 @@ static int rpmb_mux_release(struct inode *inode, struct file *fp) return 0; } -static int rpmb_key_retrieval(void *rpmb_key) -{ - /* hard code */ - memset(rpmb_key, 0x31, 32); - return 0; -} - static int rpmb_mux_hmac_256_alloc(struct rpmb_mux_dev *mux_dev) { struct shash_desc *desc; @@ -571,6 +568,7 @@ static int rpmb_add_device(struct device *dev, struct class_interface *intf) { struct rpmb_mux_dev *mux_dev; struct rpmb_dev *rdev = to_rpmb_dev(dev); + u8 rpmb_key[RPMB_MAX_PARTITION_NUMBER][RPMB_KEY_LENGTH]; int ret; mux_dev = container_of(intf, struct rpmb_mux_dev, rpmb_interface); @@ -594,7 +592,38 @@ static int rpmb_add_device(struct device *dev, struct class_interface *intf) mutex_unlock(&mux_dev->lock); + memset(rpmb_key, 0, sizeof(rpmb_key)); + ret = rpmb_key_get(mux_dev->rdev->ops->dev_id, + mux_dev->rdev->ops->dev_id_len, + RPMB_MAX_PARTITION_NUMBER, + rpmb_key); + if (ret) { + dev_err(&rdev->dev, "rpmb_key_get failed.\n"); + goto err_rpmb_key_get; + } + memcpy(mux_dev->rpmb_key, &rpmb_key[0], sizeof(mux_dev->rpmb_key)); + memset(rpmb_key, 0, sizeof(rpmb_key)); + + ret = crypto_shash_setkey(mux_dev->hash_desc->tfm, + mux_dev->rpmb_key, 32); + if (ret) { + dev_err(&rdev->dev, "set key failed = %d\n", ret); + goto err_crypto_shash_setkey; + } + return 0; + +err_crypto_shash_setkey: + memset(mux_dev->rpmb_key, 0, sizeof(mux_dev->rpmb_key)); +err_rpmb_key_get: + rpmb_mux_hmac_256_free(mux_dev); + device_destroy(rpmb_mux_class, rpmb_mux_devt); + class_destroy(rpmb_mux_class); + cdev_del(&mux_dev->cdev); + kfree(mux_dev); + unregister_chrdev_region(rpmb_mux_devt, 0); + + return ret; } static void rpmb_remove_device(struct device *dev, struct class_interface *intf) @@ -676,19 +705,6 @@ static int __init rpmb_mux_init(void) goto err_rpmb_mux_hmac_256_alloc; } - ret = rpmb_key_retrieval(mux_dev->rpmb_key); - if (ret) { - pr_err("rpmb_key_retrieval failed.\n"); - goto err_rpmb_key_retrieval; - } - - ret = crypto_shash_setkey(mux_dev->hash_desc->tfm, - mux_dev->rpmb_key, 32); - if (ret) { - pr_err("set key failed = %d\n", ret); - goto err_crypto_shash_setkey; - } - mux_dev->rpmb_interface.add_dev = rpmb_add_device; mux_dev->rpmb_interface.remove_dev = rpmb_remove_device; mux_dev->rpmb_interface.class = &rpmb_class; @@ -702,10 +718,6 @@ static int __init rpmb_mux_init(void) return 0; err_class_interface_register: -err_crypto_shash_setkey: - memset(mux_dev->rpmb_key, 0, sizeof(mux_dev->rpmb_key)); -err_rpmb_key_retrieval: - rpmb_mux_hmac_256_free(mux_dev); err_rpmb_mux_hmac_256_alloc: device_destroy(rpmb_mux_class, rpmb_mux_devt); err_device_create: From c8e8b6a29ec1060745fa4866716673e664ea2fdb Mon Sep 17 00:00:00 2001 From: Kris Pan Date: Fri, 2 Nov 2018 15:42:59 +0800 Subject: [PATCH 1129/1276] staging: android: fwdata: add missing MODULE_LICENSE The intel fwdata driver causes a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in drivers/staging/android/fwdata.o see include/linux/module.h for more information This adds the license as "GPL v2", which matches the header of the file. Signed-off-by: Kris Pan --- drivers/staging/android/fwdata.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c index 525f7e92ec84..87c346e008f4 100644 --- a/drivers/staging/android/fwdata.c +++ b/drivers/staging/android/fwdata.c @@ -400,3 +400,8 @@ static struct platform_driver android_fwdata_driver = { }; module_platform_driver(android_fwdata_driver); + +MODULE_AUTHOR("Yu Ning "); +MODULE_AUTHOR("Biyi Li "); +MODULE_DESCRIPTION("Intel fwdata driver"); +MODULE_LICENSE("GPL v2"); From efb021703d620506e4aac53ff68afb62c5ee4b45 Mon Sep 17 00:00:00 2001 From: Chang Ying Date: Thu, 25 Oct 2018 16:20:44 +0800 Subject: [PATCH 1130/1276] mm: export some vm_area APIs These APIs are needed in DMA mapping API implementation as a module. Device specific IOMMUs with associated DMA mapping implementations should be buildable as modules. Change-Id: I46bf3823345ae0bd9cd437ea25b6b29a4b727126 Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- mm/vmalloc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index a728fc492557..dc1d9254b832 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1461,6 +1461,7 @@ struct vm_struct *find_vm_area(const void *addr) return NULL; } +EXPORT_SYMBOL_GPL(find_vm_area); /** * remove_vm_area - find and remove a continuous kernel virtual area From 278f0d090c878fbe64fd9ee8304cfcd5907b7347 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:51:54 +0800 Subject: [PATCH 1131/1276] media: Add request API The request API allows bundling media device parameters with request objects and applying them atomically, either synchronously or asynchronously. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/media-device.c | 502 +++++++++++++++++++++++++++++++++- drivers/media/media-devnode.c | 4 + include/media/media-device.h | 67 +++++ include/media/media-devnode.h | 18 +- include/uapi/linux/media.h | 38 +++ 5 files changed, 619 insertions(+), 10 deletions(-) diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 3bae24b15eaa..732378d0dcf0 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -43,6 +45,356 @@ #define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \ MEDIA_ENT_SUBTYPE_MASK) +static char *__request_state[] = { + "IDLE", + "QUEUED", + "DELETED", + "COMPLETED", +}; + +#define request_state(i) \ + ((i) < ARRAY_SIZE(__request_state) ? __request_state[i] : "UNKNOWN") + + +struct media_device_fh { + struct media_devnode_fh fh; + struct list_head requests; + struct { + struct list_head head; + wait_queue_head_t wait; + atomic_t sequence; + } kevents; +}; + +static inline struct media_device_fh *media_device_fh(struct file *filp) +{ + return container_of(filp->private_data, struct media_device_fh, fh); +} + +/* ----------------------------------------------------------------------------- + * Requests + */ + +/** + * media_device_request_find - Find a request based from its ID + * @mdev: The media device + * @reqid: The request ID + * + * Find and return the request associated with the given ID, or NULL if no such + * request exists. + * + * When the function returns a non-NULL request it increases its reference + * count. The caller is responsible for releasing the reference by calling + * media_device_request_put() on the request. + */ +struct media_device_request * +media_device_request_find(struct media_device *mdev, u16 reqid) +{ + struct media_device_request *req; + unsigned long flags; + bool found = false; + + spin_lock_irqsave(&mdev->req_lock, flags); + list_for_each_entry(req, &mdev->requests, list) { + if (req->id == reqid) { + kref_get(&req->kref); + found = true; + break; + } + } + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (!found) { + dev_dbg(mdev->dev, + "request: can't find %u\n", reqid); + return NULL; + } + + return req; +} +EXPORT_SYMBOL_GPL(media_device_request_find); + +void media_device_request_get(struct media_device_request *req) +{ + kref_get(&req->kref); +} +EXPORT_SYMBOL_GPL(media_device_request_get); + +static void media_device_request_queue_event(struct media_device *mdev, + struct media_device_request *req, + struct media_device_fh *fh) +{ + struct media_kevent *kev = req->kev; + struct media_event *ev = &kev->ev; + + lockdep_assert_held(&mdev->req_lock); + + ev->sequence = atomic_inc_return(&fh->kevents.sequence); + ev->type = MEDIA_EVENT_TYPE_REQUEST_COMPLETE; + ev->req_complete.id = req->id; + + list_add(&kev->list, &fh->kevents.head); + req->kev = NULL; + req->state = MEDIA_DEVICE_REQUEST_STATE_COMPLETE; + wake_up(&fh->kevents.wait); +} + +static void media_device_request_release(struct kref *kref) +{ + struct media_device_request *req = + container_of(kref, struct media_device_request, kref); + struct media_device *mdev = req->mdev; + + dev_dbg(mdev->dev, "release request %u\n", req->id); + + ida_simple_remove(&mdev->req_ids, req->id); + + kfree(req->kev); + req->kev = NULL; + + mdev->ops->req_free(mdev, req); +} + +void media_device_request_put(struct media_device_request *req) +{ + kref_put(&req->kref, media_device_request_release); +} +EXPORT_SYMBOL_GPL(media_device_request_put); + +static int media_device_request_alloc(struct media_device *mdev, + struct file *filp, + struct media_request_cmd *cmd) +{ + struct media_device_fh *fh = media_device_fh(filp); + struct media_device_request *req; + struct media_kevent *kev; + unsigned long flags; + int id = ida_simple_get(&mdev->req_ids, 1, 0, GFP_KERNEL); + int ret; + + if (id < 0) { + dev_dbg(mdev->dev, "request: unable to obtain new id\n"); + return id; + } + + kev = kzalloc(sizeof(*kev), GFP_KERNEL); + if (!kev) { + ret = -ENOMEM; + goto out_ida_simple_remove; + } + + req = mdev->ops->req_alloc(mdev); + if (!req) { + ret = -ENOMEM; + goto out_kev_free; + } + + req->mdev = mdev; + req->id = id; + req->filp = filp; + req->state = MEDIA_DEVICE_REQUEST_STATE_IDLE; + req->kev = kev; + kref_init(&req->kref); + + spin_lock_irqsave(&mdev->req_lock, flags); + list_add_tail(&req->list, &mdev->requests); + list_add_tail(&req->fh_list, &fh->requests); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + cmd->request = req->id; + + dev_dbg(mdev->dev, "request: allocated id %u\n", req->id); + + return 0; + +out_kev_free: + kfree(kev); + +out_ida_simple_remove: + ida_simple_remove(&mdev->req_ids, id); + + return ret; +} + +static int media_device_request_delete(struct media_device *mdev, + struct media_device_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&mdev->req_lock, flags); + + if (req->state != MEDIA_DEVICE_REQUEST_STATE_IDLE) { + spin_unlock_irqrestore(&mdev->req_lock, flags); + dev_dbg(mdev->dev, "request: can't delete %u, state %s\n", + req->id, request_state(req->state)); + return -EINVAL; + } + + req->state = MEDIA_DEVICE_REQUEST_STATE_DELETED; + + if (req->filp) { + /* + * If the file handle is gone by now the + * request has already been deleted from the + * two lists. + */ + list_del(&req->list); + list_del(&req->fh_list); + req->filp = NULL; + } + + spin_unlock_irqrestore(&mdev->req_lock, flags); + + media_device_request_put(req); + + return 0; +} + +void media_device_request_complete(struct media_device *mdev, + struct media_device_request *req) +{ + struct file *filp; + unsigned long flags; + + spin_lock_irqsave(&mdev->req_lock, flags); + + if (req->state == MEDIA_DEVICE_REQUEST_STATE_IDLE) { + dev_dbg(mdev->dev, + "request: not completing an idle request %u\n", + req->id); + spin_unlock_irqrestore(&mdev->req_lock, flags); + return; + } + + if (WARN_ON(req->state != MEDIA_DEVICE_REQUEST_STATE_QUEUED)) { + dev_dbg(mdev->dev, "request: can't delete %u, state %s\n", + req->id, request_state(req->state)); + spin_unlock_irqrestore(&mdev->req_lock, flags); + return; + } + + req->state = MEDIA_DEVICE_REQUEST_STATE_COMPLETE; + filp = req->filp; + if (filp) { + /* + * If the file handle is still around we remove if + * from the lists here. Otherwise it has been removed + * when the file handle closed. + */ + list_del(&req->list); + list_del(&req->fh_list); + /* If the user asked for an event, let's queue one. */ + if (req->flags & MEDIA_REQ_FL_COMPLETE_EVENT) + media_device_request_queue_event( + mdev, req, media_device_fh(filp)); + req->filp = NULL; + } + + spin_unlock_irqrestore(&mdev->req_lock, flags); + + /* + * The driver holds a reference to a request if the filp + * pointer is non-NULL: the file handle associated to the + * request may have been released by now, i.e. filp is NULL. + */ + if (filp) + media_device_request_put(req); +} +EXPORT_SYMBOL_GPL(media_device_request_complete); + +static int media_device_request_queue_apply( + struct media_device *mdev, struct media_device_request *req, + u32 req_flags, int (*fn)(struct media_device *mdev, + struct media_device_request *req), bool queue) +{ + char *str = queue ? "queue" : "apply"; + unsigned long flags; + int rval = 0; + + if (!fn) + return -ENOSYS; + + spin_lock_irqsave(&mdev->req_lock, flags); + if (req->state != MEDIA_DEVICE_REQUEST_STATE_IDLE) { + rval = -EINVAL; + dev_dbg(mdev->dev, + "request: unable to %s %u, request in state %s\n", + str, req->id, request_state(req->state)); + } else { + req->state = MEDIA_DEVICE_REQUEST_STATE_QUEUED; + req->flags = req_flags; + } + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (rval) + return rval; + + rval = fn(mdev, req); + if (rval) { + spin_lock_irqsave(&mdev->req_lock, flags); + req->state = MEDIA_DEVICE_REQUEST_STATE_IDLE; + spin_unlock_irqrestore(&mdev->req_lock, flags); + dev_dbg(mdev->dev, + "request: can't %s %u\n", str, req->id); + } else { + dev_dbg(mdev->dev, + "request: %s %u\n", str, req->id); + } + + return rval; +} + +static long media_device_request_cmd(struct media_device *mdev, + struct file *filp, + struct media_request_cmd *cmd) +{ + struct media_device_request *req = NULL; + int ret; + + if (!mdev->ops || !mdev->ops->req_alloc || !mdev->ops->req_free) + return -ENOTTY; + + if (cmd->cmd != MEDIA_REQ_CMD_ALLOC) { + req = media_device_request_find(mdev, cmd->request); + if (!req) + return -EINVAL; + } + + switch (cmd->cmd) { + case MEDIA_REQ_CMD_ALLOC: + ret = media_device_request_alloc(mdev, filp, cmd); + break; + + case MEDIA_REQ_CMD_DELETE: + ret = media_device_request_delete(mdev, req); + break; + + case MEDIA_REQ_CMD_APPLY: + ret = media_device_request_queue_apply(mdev, req, cmd->flags, + mdev->ops->req_apply, + false); + break; + + case MEDIA_REQ_CMD_QUEUE: + ret = media_device_request_queue_apply(mdev, req, cmd->flags, + mdev->ops->req_queue, + true); + break; + + default: + ret = -EINVAL; + break; + } + + if (req) + media_device_request_put(req); + + if (ret < 0) + return ret; + + return 0; +} + /* ----------------------------------------------------------------------------- * Userspace API */ @@ -54,15 +406,58 @@ static inline void __user *media_get_uptr(__u64 arg) static int media_device_open(struct file *filp) { + struct media_device_fh *fh; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + INIT_LIST_HEAD(&fh->requests); + INIT_LIST_HEAD(&fh->kevents.head); + init_waitqueue_head(&fh->kevents.wait); + atomic_set(&fh->kevents.sequence, -1); + filp->private_data = &fh->fh; + return 0; } static int media_device_close(struct file *filp) { + struct media_device_fh *fh = media_device_fh(filp); + struct media_device *mdev = fh->fh.devnode->media_dev; + + spin_lock_irq(&mdev->req_lock); + while (!list_empty(&fh->requests)) { + struct media_device_request *req = + list_first_entry(&fh->requests, typeof(*req), fh_list); + + list_del(&req->list); + list_del(&req->fh_list); + req->filp = NULL; + spin_unlock_irq(&mdev->req_lock); + media_device_request_put(req); + spin_lock_irq(&mdev->req_lock); + } + + while (!list_empty(&fh->kevents.head)) { + struct media_kevent *kev = + list_first_entry(&fh->kevents.head, typeof(*kev), list); + + list_del(&kev->list); + spin_unlock_irq(&mdev->req_lock); + kfree(kev); + spin_lock_irq(&mdev->req_lock); + } + spin_unlock_irq(&mdev->req_lock); + + kfree(fh); + return 0; } -static long media_device_get_info(struct media_device *dev, void *arg) +static long media_device_get_info(struct media_device *dev, + struct file *filp, + void *arg) { struct media_device_info *info = arg; @@ -102,7 +497,9 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) return NULL; } -static long media_device_enum_entities(struct media_device *mdev, void *arg) +static long media_device_enum_entities(struct media_device *mdev, + struct file *filp, + void *arg) { struct media_entity_desc *entd = arg; struct media_entity *ent; @@ -155,7 +552,9 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, upad->flags = kpad->flags; } -static long media_device_enum_links(struct media_device *mdev, void *arg) +static long media_device_enum_links(struct media_device *mdev, + struct file *filp, + void *arg) { struct media_links_enum *links = arg; struct media_entity *entity; @@ -204,7 +603,9 @@ static long media_device_enum_links(struct media_device *mdev, void *arg) return 0; } -static long media_device_setup_link(struct media_device *mdev, void *arg) +static long media_device_setup_link(struct media_device *mdev, + struct file *filp, + void *arg) { struct media_link_desc *linkd = arg; struct media_link *link = NULL; @@ -377,6 +778,49 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) return ret; } +static struct media_kevent *opportunistic_dqevent(struct media_device *mdev, + struct file *filp) +{ + struct media_device_fh *fh = media_device_fh(filp); + struct media_kevent *kev = NULL; + unsigned long flags; + + spin_lock_irqsave(&mdev->req_lock, flags); + if (!list_empty(&fh->kevents.head)) { + kev = list_last_entry(&fh->kevents.head, + struct media_kevent, list); + list_del(&kev->list); + } + spin_unlock_irqrestore(&mdev->req_lock, flags); + + return kev; +} + +static int media_device_dqevent(struct media_device *mdev, + struct file *filp, + struct media_event *ev) +{ + struct media_device_fh *fh = media_device_fh(filp); + struct media_kevent *kev; + + if (filp->f_flags & O_NONBLOCK) { + kev = opportunistic_dqevent(mdev, filp); + if (!kev) + return -ENODATA; + } else { + int ret = wait_event_interruptible( + fh->kevents.wait, + (kev = opportunistic_dqevent(mdev, filp))); + if (ret == -ERESTARTSYS) + return ret; + } + + *ev = kev->ev; + kfree(kev); + + return 0; +} + static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) { /* All media IOCTLs are _IOWR() */ @@ -401,7 +845,8 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) #define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \ [_IOC_NR(MEDIA_IOC_##__cmd)] = { \ .cmd = MEDIA_IOC_##__cmd, \ - .fn = (long (*)(struct media_device *, void *))func, \ + .fn = (long (*)(struct media_device *, \ + struct file *, void *))func, \ .flags = fl, \ .arg_from_user = from_user, \ .arg_to_user = to_user, \ @@ -414,7 +859,7 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) struct media_ioctl_info { unsigned int cmd; unsigned short flags; - long (*fn)(struct media_device *dev, void *arg); + long (*fn)(struct media_device *dev, struct file *file, void *arg); long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd); long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd); }; @@ -425,6 +870,8 @@ static const struct media_ioctl_info ioctl_info[] = { MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX), + MEDIA_IOC(REQUEST_CMD, media_device_request_cmd, 0), + MEDIA_IOC(DQEVENT, media_device_dqevent, 0), }; static long media_device_ioctl(struct file *filp, unsigned int cmd, @@ -458,7 +905,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) mutex_lock(&dev->graph_mutex); - ret = info->fn(dev, karg); + ret = info->fn(dev, filp, karg); if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) mutex_unlock(&dev->graph_mutex); @@ -473,6 +920,34 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, return ret; } +static unsigned int media_device_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct media_device_fh *fh = media_device_fh(filp); + struct media_device *mdev = fh->fh.devnode->media_dev; + unsigned int poll_events = poll_requested_events(wait); + int ret = 0; + + if (poll_events & (POLLIN | POLLOUT)) + return POLLERR; + + if (poll_events & POLLPRI) { + unsigned long flags; + bool empty; + + spin_lock_irqsave(&mdev->req_lock, flags); + empty = list_empty(&fh->kevents.head); + spin_unlock_irqrestore(&mdev->req_lock, flags); + + if (empty) + poll_wait(filp, &fh->kevents.wait, wait); + else + ret |= POLLPRI; + } + + return ret; +} + #ifdef CONFIG_COMPAT struct media_links_enum32 { @@ -483,7 +958,8 @@ struct media_links_enum32 { }; static long media_device_enum_links32(struct media_device *mdev, - struct media_links_enum32 __user *ulinks) + struct file *filp, + struct media_links_enum32 __user *ulinks) { struct media_links_enum links; compat_uptr_t pads_ptr, links_ptr; @@ -498,7 +974,7 @@ static long media_device_enum_links32(struct media_device *mdev, links.pads = compat_ptr(pads_ptr); links.links = compat_ptr(links_ptr); - return media_device_enum_links(mdev, &links); + return media_device_enum_links(mdev, filp, &links); } #define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) @@ -514,6 +990,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, case MEDIA_IOC_ENUM_LINKS32: mutex_lock(&dev->graph_mutex); ret = media_device_enum_links32(dev, + filp, (struct media_links_enum32 __user *)arg); mutex_unlock(&dev->graph_mutex); break; @@ -530,6 +1007,7 @@ static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, .open = media_device_open, .ioctl = media_device_ioctl, + .poll = media_device_poll, #ifdef CONFIG_COMPAT .compat_ioctl = media_device_compat_ioctl, #endif /* CONFIG_COMPAT */ @@ -717,6 +1195,10 @@ int __must_check __media_device_register(struct media_device *mdev, if (!devnode) return -ENOMEM; + ida_init(&mdev->req_ids); + spin_lock_init(&mdev->req_lock); + INIT_LIST_HEAD(&mdev->requests); + /* Register the device node. */ mdev->devnode = devnode; devnode->fops = &media_device_fops; @@ -739,6 +1221,7 @@ int __must_check __media_device_register(struct media_device *mdev, mdev->devnode = NULL; media_devnode_unregister_prepare(devnode); media_devnode_unregister(devnode); + ida_destroy(&mdev->req_ids); return ret; } @@ -823,6 +1306,7 @@ void media_device_unregister(struct media_device *mdev) device_remove_file(&mdev->devnode->dev, &dev_attr_model); media_devnode_unregister(mdev->devnode); + ida_destroy(&mdev->req_ids); /* devnode free is handled in media_devnode_*() */ mdev->devnode = NULL; } diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 6b87a721dc49..86e92cb4289c 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -149,6 +149,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd, /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { + struct media_devnode_fh *fh; struct media_devnode *devnode; int ret; @@ -181,6 +182,9 @@ static int media_open(struct inode *inode, struct file *filp) } } + fh = filp->private_data; + fh->devnode = devnode; + return 0; } diff --git a/include/media/media-device.h b/include/media/media-device.h index bcc6ec434f1f..6f398006b54c 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -19,6 +19,8 @@ #ifndef _MEDIA_DEVICE_H #define _MEDIA_DEVICE_H +#include +#include #include #include @@ -26,7 +28,42 @@ #include struct ida; +#include + struct device; +struct file; +struct media_device; +struct media_device_fh; + +enum media_device_request_state { + MEDIA_DEVICE_REQUEST_STATE_IDLE, + MEDIA_DEVICE_REQUEST_STATE_QUEUED, + MEDIA_DEVICE_REQUEST_STATE_DELETED, + MEDIA_DEVICE_REQUEST_STATE_COMPLETE, +}; + +/** + * struct media_device_request - Media device request + * @id: Request ID + * @mdev: Media device this request belongs to + * @kref: Reference count + * @list: List entry in the media device requests list + * @fh_list: List entry in the media file handle requests list + * @state: The state of the request, MEDIA_DEVICE_REQUEST_STATE_*, + * access to state serialised by mdev->req_lock + * @flags: Request specific flags, MEDIA_REQ_FL_* + */ +struct media_device_request { + u32 id; + struct media_device *mdev; + struct file *filp; + struct media_kevent *kev; + struct kref kref; + struct list_head list; + struct list_head fh_list; + enum media_device_request_state state; + u32 flags; +}; /** * struct media_entity_notify - Media Entity Notify @@ -50,10 +87,26 @@ struct media_entity_notify { * struct media_device_ops - Media device operations * @link_notify: Link state change notification callback. This callback is * called with the graph_mutex held. + * @req_alloc: Allocate a request + * @req_free: Free a request + * @req_apply: Apply a request + * @req_queue: Queue a request */ struct media_device_ops { int (*link_notify)(struct media_link *link, u32 flags, unsigned int notification); + struct media_device_request *(*req_alloc)(struct media_device *mdev); + void (*req_free)(struct media_device *mdev, + struct media_device_request *req); + int (*req_apply)(struct media_device *mdev, + struct media_device_request *req); + int (*req_queue)(struct media_device *mdev, + struct media_device_request *req); +}; + +struct media_kevent { + struct list_head list; + struct media_event ev; }; /** @@ -88,6 +141,9 @@ struct media_device_ops { * @disable_source: Disable Source Handler function pointer * * @ops: Operation handler callbacks + * @req_ids: Allocated request IDs + * @req_lock: Serialise access to requests list + * @requests: List of allocated requests * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The @@ -158,6 +214,10 @@ struct media_device { void (*disable_source)(struct media_entity *entity); const struct media_device_ops *ops; + + struct ida req_ids; + spinlock_t req_lock; + struct list_head requests; }; /* We don't need to include pci.h or usb.h here */ @@ -475,4 +535,11 @@ static inline void __media_device_usb_init(struct media_device *mdev, #define media_device_usb_init(mdev, udev, name) \ __media_device_usb_init(mdev, udev, name, KBUILD_MODNAME) +struct media_device_request * +media_device_request_find(struct media_device *mdev, u16 reqid); +void media_device_request_get(struct media_device_request *req); +void media_device_request_put(struct media_device_request *req); +void media_device_request_complete(struct media_device *mdev, + struct media_device_request *req); + #endif diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index dc2f64e1b08f..2ece17356b33 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -63,6 +63,20 @@ struct media_file_operations { int (*release) (struct file *); }; +/** + * struct media_devnode_fh - Media device node file handle + * @devnode: pointer to the media device node + * + * This structure serves as a base for per-file-handle data storage. Media + * device node users embed media_devnode_fh in their custom file handle data + * structures and store the media_devnode_fh in the file private_data in order + * to let the media device node core locate the media_devnode corresponding to a + * file handle. + */ +struct media_devnode_fh { + struct media_devnode *devnode; +}; + /** * struct media_devnode - Media device node * @media_dev: pointer to struct &media_device @@ -154,7 +168,9 @@ void media_devnode_unregister(struct media_devnode *devnode); */ static inline struct media_devnode *media_devnode_data(struct file *filp) { - return filp->private_data; + struct media_devnode_fh *fh = filp->private_data; + + return fh->devnode; } /** diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 36f76e777ef9..4506519c7b38 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -364,11 +364,49 @@ struct media_v2_topology { /* ioctls */ +#define MEDIA_REQ_CMD_ALLOC 0 +#define MEDIA_REQ_CMD_DELETE 1 +#define MEDIA_REQ_CMD_APPLY 2 +#define MEDIA_REQ_CMD_QUEUE 3 + +#define MEDIA_REQ_FL_COMPLETE_EVENT (1 << 0) + +#ifdef __KERNEL__ +struct __attribute__ ((packed)) media_request_cmd_0 { + __u32 cmd; + __u32 request; +}; +#endif + +struct __attribute__ ((packed)) media_request_cmd { + __u32 cmd; + __u32 request; + __u32 flags; +}; + +struct __attribute__ ((packed)) media_event_request_complete { + __u32 id; +}; + +#define MEDIA_EVENT_TYPE_REQUEST_COMPLETE 1 + +struct __attribute__ ((packed)) media_event { + __u32 type; + __u32 sequence; + __u32 reserved[4]; + + union { + struct media_event_request_complete req_complete; + }; +}; + #define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) #define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) +#define MEDIA_IOC_REQUEST_CMD _IOWR('|', 0x05, struct media_request_cmd) +#define MEDIA_IOC_DQEVENT _IOWR('|', 0x06, struct media_event) #ifndef __KERNEL__ From 2ac5b4ab1b159b68c0ccd4a6273b38d9b093b48d Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:04 +0800 Subject: [PATCH 1132/1276] videodev2.h: Add request field to v4l2_buffer When queuing buffers allow for passing the request ID that should be associated with this buffer. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 10 ++++++++-- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 14 ++++++++++---- include/media/videobuf2-core.h | 1 + include/media/videobuf2-v4l2.h | 2 ++ include/uapi/linux/videodev2.h | 4 ++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 886a2d8d5c6c..5b678ea1dc9e 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -178,6 +178,11 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, return -EINVAL; } + if (!q->allow_requests && b->request) { + dprintk(1, "%s: unsupported request ID\n", opname); + return -EINVAL; + } + return __verify_planes_array(q->bufs[b->index], b); } @@ -203,8 +208,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb) b->timestamp = ns_to_timeval(vb->timestamp); b->timecode = vbuf->timecode; b->sequence = vbuf->sequence; - b->reserved2 = 0; - b->reserved = 0; + b->request = vbuf->request; + b->reserved = vbuf->reserved; if (q->is_multiplanar) { /* @@ -320,6 +325,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, } vb->timestamp = 0; vbuf->sequence = 0; + vbuf->request = b->request; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { if (b->memory == VB2_MEMORY_USERPTR) { diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 6481212fda77..222653915699 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -481,7 +481,7 @@ struct v4l2_buffer32 { __s32 fd; } m; __u32 length; - __u32 reserved2; + __u32 request; __u32 reserved; }; @@ -581,6 +581,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, { u32 type; u32 length; + u32 request; enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; struct v4l2_plane __user *uplane; @@ -595,7 +596,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, get_user(memory, &p32->memory) || put_user(memory, &p64->memory) || get_user(length, &p32->length) || - put_user(length, &p64->length)) + put_user(length, &p64->length) || + get_user(request, &p32->request) || + put_user(request, &p64->request)) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(type)) @@ -677,6 +680,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, { u32 type; u32 length; + u32 request; enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; struct v4l2_plane *uplane; @@ -698,10 +702,12 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, assign_in_user(&p32->timestamp.tv_usec, &p64->timestamp.tv_usec) || copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) || assign_in_user(&p32->sequence, &p64->sequence) || - assign_in_user(&p32->reserved2, &p64->reserved2) || + assign_in_user(&p32->request, &p64->request) || assign_in_user(&p32->reserved, &p64->reserved) || get_user(length, &p64->length) || - put_user(length, &p32->length)) + put_user(length, &p32->length) || + get_user(request, &p64->length) || + put_user(request, &p32->length)) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(type)) { diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index f6818f732f34..aba09cef3ddd 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -516,6 +516,7 @@ struct vb2_queue { unsigned fileio_write_immediately:1; unsigned allow_zero_bytesused:1; unsigned quirk_poll_must_check_waiting_for_buffers:1; + unsigned allow_requests:1; struct mutex *lock; void *owner; diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 3d5e2d739f05..317c2b7fe105 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -43,6 +43,8 @@ struct vb2_v4l2_buffer { __u32 field; struct v4l2_timecode timecode; __u32 sequence; + __u32 request; + __u32 reserved; }; /* diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5d1a3685bea9..1a70f2fad418 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -917,6 +917,7 @@ struct v4l2_plane { * @length: size in bytes of the buffer (NOT its payload) for single-plane * buffers (when type != *_MPLANE); number of elements in the * planes array for multi-plane buffers +* @request: this buffer should use this request * * Contains data exchanged by application and driver using one of the Streaming * I/O methods. @@ -940,7 +941,10 @@ struct v4l2_buffer { __s32 fd; } m; __u32 length; + union { + __u32 request; __u32 reserved2; + }; __u32 reserved; }; From f89a22d0bab79b59d534c525f63c2aeff42e8024 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:10 +0800 Subject: [PATCH 1133/1276] v4l: subdev: Add support for sub-streams The stream id tells which stream the IOCTLs address. The concept of stream ID makes it possible to differentiate between several streams that traverse the same image pipeline but the properties of which are different. Add pad flag MEDIA_PAD_FL_MULTIPLEX to indicate which pad is multiplex. Add format enumeration flag V4L2_SUBDEV_FLAG_NEXT_STREAM to iterate stream id. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/v4l2-core/v4l2-subdev.c | 6 ++++++ include/media/v4l2-subdev.h | 2 ++ include/uapi/linux/media.h | 1 + include/uapi/linux/v4l2-subdev.h | 23 ++++++++++++++++++----- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2b63fa6b6fc9..12eb646c4bc7 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -135,6 +135,9 @@ static int check_format(struct v4l2_subdev *sd, if (format->pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_SUBSTREAMS) && format->stream) + return -EINVAL; + return 0; } @@ -160,6 +163,9 @@ static int check_selection(struct v4l2_subdev *sd, if (sel->pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_SUBSTREAMS) && sel->stream) + return -EINVAL; + return 0; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9102d6ca566e..6f87e7f81759 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -778,6 +778,8 @@ struct v4l2_subdev_internal_ops { #define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) /* Set this flag if this subdev generates events. */ #define V4L2_SUBDEV_FL_HAS_EVENTS (1U << 3) +/* Set this flag if this sub-device supports substreams. */ +#define V4L2_SUBDEV_FL_HAS_SUBSTREAMS (1U << 4) struct regulator_bulk_data; diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 4506519c7b38..585a53bd1461 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -210,6 +210,7 @@ struct media_entity_desc { #define MEDIA_PAD_FL_SINK (1 << 0) #define MEDIA_PAD_FL_SOURCE (1 << 1) #define MEDIA_PAD_FL_MUST_CONNECT (1 << 2) +#define MEDIA_PAD_FL_MULTIPLEX (1 << 3) struct media_pad_desc { __u32 entity; /* entity ID */ diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 03970ce30741..51f637dff468 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -44,12 +44,14 @@ enum v4l2_subdev_format_whence { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @format: media bus format (format code and frame size) + * @stream: sub-stream id */ struct v4l2_subdev_format { __u32 which; __u32 pad; struct v4l2_mbus_framefmt format; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -57,12 +59,14 @@ struct v4l2_subdev_format { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @rect: pad crop rectangle boundaries + * @stream: sub-stream id */ struct v4l2_subdev_crop { __u32 which; __u32 pad; struct v4l2_rect rect; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -71,13 +75,18 @@ struct v4l2_subdev_crop { * @index: format index during enumeration * @code: format code (MEDIA_BUS_FMT_ definitions) * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: sub-stream id */ + +#define V4L2_SUBDEV_FLAG_NEXT_STREAM 0x80000000 + struct v4l2_subdev_mbus_code_enum { __u32 pad; __u32 index; __u32 code; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -86,6 +95,7 @@ struct v4l2_subdev_mbus_code_enum { * @index: format index during enumeration * @code: format code (MEDIA_BUS_FMT_ definitions) * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: sub-stream id */ struct v4l2_subdev_frame_size_enum { __u32 index; @@ -96,7 +106,8 @@ struct v4l2_subdev_frame_size_enum { __u32 min_height; __u32 max_height; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -140,6 +151,7 @@ struct v4l2_subdev_frame_interval_enum { * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of the selection window + * @stream: sub-stream id * @reserved: for future use, set to zero for now * * Hardware may use multiple helper windows to process a video stream. @@ -152,7 +164,8 @@ struct v4l2_subdev_selection { __u32 target; __u32 flags; struct v4l2_rect r; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /* Backwards compatibility define --- to be removed */ From d199b0c765e2231c91654335a593fa5dafe3e19e Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:15 +0800 Subject: [PATCH 1134/1276] v4l: subdev: Add [GS]_ROUTING subdev ioctls and operations add get/set_routing subdev ioctls and helper functions. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 70 +++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 16 +++++ drivers/media/v4l2-core/v4l2-subdev.c | 34 +++++++++ include/media/v4l2-subdev.h | 4 ++ include/uapi/linux/v4l2-subdev.h | 45 ++++++++++++ 5 files changed, 169 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 222653915699..1fbb67714312 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -1051,6 +1051,57 @@ static int put_v4l2_event32(struct v4l2_event __user *p64, return 0; } +struct v4l2_subdev_routing32 { + compat_caddr_t routes; + __u32 num_routes; + __u32 reserved[5]; +}; + +static int get_v4l2_subdev_routing(struct v4l2_subdev_routing __user *kp, + struct v4l2_subdev_routing32 __user *up) +{ + compat_caddr_t p; + u32 num_routes; + + if (!access_ok(VERIFY_READ, up, sizeof(*up)) || + get_user(p, &up->routes) || + put_user(compat_ptr(p), &kp->routes) || + get_user(num_routes, &kp->num_routes) || + assign_in_user(&kp->num_routes, &up->num_routes) || + !access_ok(VERIFY_READ, up->reserved, sizeof(*up->reserved)) || + num_routes > U32_MAX / sizeof(*kp->routes)) + return -EFAULT; + + if (!access_ok(VERIFY_READ, compat_ptr(p), + num_routes * (u32)sizeof(*kp->routes))) + return -EFAULT; + + return 0; +} + +static int put_v4l2_subdev_routing(struct v4l2_subdev_routing __user *kp, + struct v4l2_subdev_routing32 __user *up) +{ + struct v4l2_subdev_route __user *uroutes; + compat_caddr_t p; + u32 num_routes; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || + get_user(p, &up->routes) || + get_user(num_routes, &kp->num_routes) || + assign_in_user(&up->num_routes, &kp->num_routes) || + !access_ok(VERIFY_WRITE, up->reserved, sizeof(*up->reserved))) + return -EFAULT; + + uroutes = compat_ptr(p); + + if (!access_ok(VERIFY_WRITE, uroutes, + num_routes * sizeof(*kp->routes))) + return -EFAULT; + + return 0; +} + struct v4l2_edid32 { __u32 pad; __u32 start_block; @@ -1123,6 +1174,8 @@ static int put_v4l2_edid32(struct v4l2_edid __user *p64, #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) #define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) +#define VIDIOC_SUBDEV_G_ROUTING32 _IOWR('V', 38, struct v4l2_subdev_routing32) +#define VIDIOC_SUBDEV_S_ROUTING32 _IOWR('V', 39, struct v4l2_subdev_routing32) #define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) #define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) @@ -1201,6 +1254,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_SUBDEV_G_ROUTING32: cmd = VIDIOC_SUBDEV_G_ROUTING; break; + case VIDIOC_SUBDEV_S_ROUTING32: cmd = VIDIOC_SUBDEV_S_ROUTING; break; case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; @@ -1233,6 +1288,14 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: + err = alloc_userspace(sizeof(struct v4l2_subdev_routing), 0, &new_p64); + if (!err) + err = get_v4l2_subdev_routing(new_p64, p32); + compatible_arg = 0; + break; + case VIDIOC_G_EDID: case VIDIOC_S_EDID: err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64); @@ -1374,6 +1437,13 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar if (put_v4l2_edid32(new_p64, p32)) err = -EFAULT; break; + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: + err = alloc_userspace(sizeof(struct v4l2_subdev_routing), 0, &new_p64); + if (!err) + if (put_v4l2_subdev_routing(new_p64, p32)) + err = -EFAULT; + break; } if (err) return err; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 54afc9c7ee6e..41bfb83665ef 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2924,6 +2924,22 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, } break; } + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *route = parg; + + if (route->num_routes > 0) { + if (route->num_routes > 256) + return -EINVAL; + + *user_ptr = (void __user *)route->routes; + *kernel_ptr = (void *)&route->routes; + *array_size = sizeof(struct v4l2_subdev_route) + * route->num_routes; + ret = 1; + } + break; + } } return ret; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 12eb646c4bc7..5d0ee2fd384a 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -522,6 +522,40 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); + + case VIDIOC_SUBDEV_G_ROUTING: + return v4l2_subdev_call(sd, pad, get_routing, arg); + + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *route = arg; + unsigned int i; + int rval; + + if (route->num_routes > sd->entity.num_pads) + return -EINVAL; + + for (i = 0; i < route->num_routes; ++i) { + unsigned int sink = route->routes[i].sink_pad; + unsigned int source = route->routes[i].source_pad; + struct media_pad *pads = sd->entity.pads; + + if (sink >= sd->entity.num_pads || + source >= sd->entity.num_pads) + return -EINVAL; + + if ((!(route->routes[i].flags & + V4L2_SUBDEV_ROUTE_FL_SOURCE) && + !(pads[sink].flags & MEDIA_PAD_FL_SINK)) || + !(pads[source].flags & MEDIA_PAD_FL_SOURCE)) + return -EINVAL; + } + + mutex_lock(&sd->entity.graph_obj.mdev->graph_mutex); + rval = v4l2_subdev_call(sd, pad, set_routing, route); + mutex_unlock(&sd->entity.graph_obj.mdev->graph_mutex); + + return rval; + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 6f87e7f81759..4bd777a68d64 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -719,6 +719,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_mbus_frame_desc *fd); int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd); + int (*get_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); + int (*set_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); }; /** diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 51f637dff468..2ed7ebae6e83 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -168,6 +168,49 @@ struct v4l2_subdev_selection { __u32 reserved[7]; }; +#define V4L2_SUBDEV_ROUTE_FL_ACTIVE (1 << 0) +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE (1 << 1) +#define V4L2_SUBDEV_ROUTE_FL_SOURCE (1 << 2) + +/** + * struct v4l2_subdev_route - A signal route inside a subdev + * @sink_pad: the sink pad + * @sink_stream: the sink stream + * @source_pad: the source pad + * @source_stream: the source stream + * @flags: route flags: + * + * V4L2_SUBDEV_ROUTE_FL_ACTIVE: Is the stream in use or not? An + * active stream will start when streaming is enabled on a video + * node. Set by the user. + * + * V4L2_SUBDEV_ROUTE_FL_IMMUTABLE: Is the stream immutable, i.e. + * can it be activated and inactivated? Set by the driver. + * + * V4L2_SUBDEV_ROUTE_FL_SOURCE: Is the sub-device the source of a + * stream? In this case the sink information is unused (and + * zero). Set by the driver. + */ +struct v4l2_subdev_route { + __u32 sink_pad; + __u32 sink_stream; + __u32 source_pad; + __u32 source_stream; + __u32 flags; + __u32 reserved[5]; +}; + +/** + * struct v4l2_subdev_routing - Routing information + * @routes: the routes array + * @num_routes: the total number of routes in the routes array + */ +struct v4l2_subdev_routing { + struct v4l2_subdev_route *routes; + __u32 num_routes; + __u32 reserved[5]; +}; + /* Backwards compatibility define --- to be removed */ #define v4l2_subdev_edid v4l2_edid @@ -194,5 +237,7 @@ struct v4l2_subdev_selection { #define VIDIOC_SUBDEV_ENUM_DV_TIMINGS _IOWR('V', 98, struct v4l2_enum_dv_timings) #define VIDIOC_SUBDEV_QUERY_DV_TIMINGS _IOR('V', 99, struct v4l2_dv_timings) #define VIDIOC_SUBDEV_DV_TIMINGS_CAP _IOWR('V', 100, struct v4l2_dv_timings_cap) +#define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing) +#define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing) #endif From 9ed4319b1947764ed79955599b7d3274b1a58829 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:19 +0800 Subject: [PATCH 1135/1276] media: Use routing info during graph traversal Take internal routing information as reported by the entity has_route operation into account during graph traversal to avoid following unrelated links. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/media-entity.c | 82 +++++++++++++++++++++++++++--------- include/media/media-entity.h | 10 +++++ 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 3498551e618e..0ad584518db7 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -237,18 +237,38 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init); * Graph traversal */ -static struct media_entity * -media_entity_other(struct media_entity *entity, struct media_link *link) +/** + * media_entity_has_route - Check if two entity pads are connected internally + * @entity: The entity + * @pad0: The first pad index + * @pad1: The second pad index + * + * This function can be used to check whether two pads of an entity are + * connected internally in the entity. + * + * The caller must hold entity->source->parent->mutex. + * + * Return: true if the pads are connected internally and false otherwise. + */ +bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) { - if (link->source->entity == entity) - return link->sink->entity; - else - return link->source->entity; + if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) + return false; + + if (pad0 == pad1) + return true; + + if (!entity->ops || !entity->ops->has_route) + return true; + + return entity->ops->has_route(entity, pad0, pad1, NULL); } +EXPORT_SYMBOL_GPL(media_entity_has_route); /* push an entity to traversal stack */ static void stack_push(struct media_graph *graph, - struct media_entity *entity) + struct media_entity *entity, int pad, int stream) { if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { WARN_ON(1); @@ -257,6 +277,8 @@ static void stack_push(struct media_graph *graph, graph->top++; graph->stack[graph->top].link = entity->links.next; graph->stack[graph->top].entity = entity; + graph->stack[graph->top].pad = pad; + graph->stack[graph->top].stream = stream; } static struct media_entity *stack_pop(struct media_graph *graph) @@ -271,6 +293,8 @@ static struct media_entity *stack_pop(struct media_graph *graph) #define link_top(en) ((en)->stack[(en)->top].link) #define stack_top(en) ((en)->stack[(en)->top].entity) +#define pad_top(en) ((en)->stack[(en)->top].pad) +#define stream_top(en) ((en)->stack[(en)->top].stream) /** * media_graph_walk_init - Allocate resources for graph walk @@ -308,7 +332,7 @@ void media_graph_walk_start(struct media_graph *graph, graph->top = 0; graph->stack[graph->top].entity = NULL; - stack_push(graph, entity); + stack_push(graph, entity, 0, -1); dev_dbg(entity->graph_obj.mdev->dev, "begin graph walk at '%s'\n", entity->name); } @@ -319,6 +343,10 @@ static void media_graph_walk_iter(struct media_graph *graph) struct media_entity *entity = stack_top(graph); struct media_link *link; struct media_entity *next; + struct media_pad *remote; + struct media_pad *local; + unsigned int from_pad = pad_top(graph); + int stream = stream_top(graph); link = list_entry(link_top(graph), typeof(*link), list); @@ -332,8 +360,31 @@ static void media_graph_walk_iter(struct media_graph *graph) return; } - /* Get the entity in the other end of the link . */ - next = media_entity_other(entity, link); + /* + * Get the local pad, the remote pad and the entity at the other + * end of the link. + */ + if (link->source->entity == entity) { + remote = link->sink; + local = link->source; + } else { + remote = link->source; + local = link->sink; + } + + next = remote->entity; + + /* + * Are the local pad and the pad we came from connected + * internally in the entity ? + */ + if (entity->ops && entity->ops->has_route) { + if (!entity->ops->has_route(entity, from_pad, + local->index, &stream)) { + link_top(graph) = link_top(graph)->next; + return; + } + } /* Has the entity already been visited? */ if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { @@ -346,7 +397,7 @@ static void media_graph_walk_iter(struct media_graph *graph) /* Push the new entity to stack and start over. */ link_top(graph) = link_top(graph)->next; - stack_push(graph, next); + stack_push(graph, next, remote->index, stream); dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n", next->name); } @@ -436,17 +487,8 @@ __must_check int __media_pipeline_start(struct media_entity *entity, entity->stream_count++; - if (WARN_ON(entity->pipe && entity->pipe != pipe)) { - ret = -EBUSY; - goto error; - } - entity->pipe = pipe; - /* Already streaming --- no need to check. */ - if (entity->stream_count > 1) - continue; - if (!entity->ops || !entity->ops->link_validate) continue; diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 3aa3d58d1d58..0faaf936302b 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -97,6 +97,8 @@ struct media_graph { struct { struct media_entity *entity; struct list_head *link; + int stream; + int pad; } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; struct media_entity_enum ent_enum; @@ -183,6 +185,9 @@ struct media_pad { * @link_validate: Return whether a link is valid from the entity point of * view. The media_pipeline_start() function * validates all links by calling this operation. Optional. + * @has_route: Return whether a route exists inside the entity between + * two given pads. Optional. If the operation isn't + * implemented all pads will be considered as connected. * * .. note:: * @@ -195,6 +200,8 @@ struct media_entity_operations { const struct media_pad *local, const struct media_pad *remote, u32 flags); int (*link_validate)(struct media_link *link); + bool (*has_route)(struct media_entity *entity, unsigned int pad0, + unsigned int pad1, int *stream); }; /** @@ -879,6 +886,9 @@ void media_graph_walk_cleanup(struct media_graph *graph); */ void media_entity_put(struct media_entity *entity); +bool media_entity_has_route(struct media_entity *entity, unsigned int sink, + unsigned int source); + /** * media_graph_walk_start - Start walking the media graph at a * given entity From 2b0ed0782c4ccb17ee0c0d76d85a54b154524d20 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:23 +0800 Subject: [PATCH 1136/1276] v4l: for multiplex pad add routing informaion in link validation for links have 2 multiplexed pad, validating all active streams. otherwise, all follow 1:1 pad link route with stream id 0 Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/v4l2-core/v4l2-subdev.c | 135 +++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 5d0ee2fd384a..e95cda554215 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -668,19 +668,21 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return -EINVAL; } -int v4l2_subdev_link_validate(struct media_link *link) +static int v4l2_subdev_link_validate_one(struct media_link *link, + struct media_pad *source_pad, unsigned int source_stream, + struct media_pad *sink_pad, unsigned int sink_stream) { struct v4l2_subdev *sink; struct v4l2_subdev_format sink_fmt, source_fmt; int rval; - rval = v4l2_subdev_link_validate_get_format( - link->source, &source_fmt); + source_fmt.stream = source_stream; + rval = v4l2_subdev_link_validate_get_format(source_pad, &source_fmt); if (rval < 0) return 0; - rval = v4l2_subdev_link_validate_get_format( - link->sink, &sink_fmt); + sink_fmt.stream = sink_stream; + rval = v4l2_subdev_link_validate_get_format(sink_pad, &sink_fmt); if (rval < 0) return 0; @@ -694,6 +696,129 @@ int v4l2_subdev_link_validate(struct media_link *link) return v4l2_subdev_link_validate_default( sink, link, &source_fmt, &sink_fmt); } + +/* How many routes to assume there can be per a sub-device? */ +#define LINK_VALIDATE_ROUTES 8 + +int v4l2_subdev_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sink; + struct v4l2_subdev_route sink_routes[LINK_VALIDATE_ROUTES]; + struct v4l2_subdev_routing sink_routing = { + .routes = sink_routes, + .num_routes = ARRAY_SIZE(sink_routes), + }; + struct v4l2_subdev_route src_routes[LINK_VALIDATE_ROUTES]; + struct v4l2_subdev_routing src_routing = { + .routes = src_routes, + .num_routes = ARRAY_SIZE(src_routes), + }; + unsigned int i, j; + int rval; + + sink = media_entity_to_v4l2_subdev(link->sink->entity); + + if (!(link->sink->flags & MEDIA_PAD_FL_MULTIPLEX && + link->source->flags & MEDIA_PAD_FL_MULTIPLEX)) + return v4l2_subdev_link_validate_one(link, link->source, 0, + link->sink, 0); + /* + * multiplex link cannot proceed without route information. + */ + rval = v4l2_subdev_call(sink, pad, get_routing, &sink_routing); + + if (rval) { + dev_err(sink->entity.graph_obj.mdev->dev, + "error %d in get_routing() on %s, sink pad %u\n", rval, + sink->entity.name, link->sink->index); + + return rval; + } + + rval = v4l2_subdev_call(media_entity_to_v4l2_subdev( + link->source->entity), + pad, get_routing, &src_routing); + if (rval) { + dev_dbg(sink->entity.graph_obj.mdev->dev, + "error %d in get_routing() on %s, source pad %u\n", + rval, sink->entity.name, link->source->index); + + return rval; + } + + dev_dbg(sink->entity.graph_obj.mdev->dev, + "validating multiplexed link \"%s\":%u -> \"%s\":%u; %u/%u routes\n", + link->source->entity->name, link->source->index, + sink->entity.name, link->sink->index, + src_routing.num_routes, sink_routing.num_routes); + + for (i = 0; i < sink_routing.num_routes; i++) { + /* Get the first active route for the sink pad. */ + if (sink_routes[i].sink_pad != link->sink->index || + !(sink_routes[i].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { + dev_dbg(sink->entity.graph_obj.mdev->dev, + "skipping sink route %u/%u -> %u/%u[%u]\n", + sink_routes[i].sink_pad, + sink_routes[i].sink_stream, + sink_routes[i].source_pad, + sink_routes[i].source_stream, + (bool)(sink_routes[i].flags + & V4L2_SUBDEV_ROUTE_FL_ACTIVE)); + continue; + } + + /* + * Get the corresponding route for the source pad. + * It's ok for the source pad to have routes active + * where the sink pad does not, but the routes that + * are active on the source pad have to be active on + * the sink pad as well. + */ + + for (j = 0; j < src_routing.num_routes; j++) { + if (src_routes[j].source_pad == link->source->index && + src_routes[j].source_stream + == sink_routes[i].sink_stream) + break; + } + + if (j == src_routing.num_routes) { + dev_err(sink->entity.graph_obj.mdev->dev, + "no corresponding source found.\n"); + return -EINVAL; + } + + /* The source route must be active. */ + if (!(src_routes[j].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { + dev_dbg(sink->entity.graph_obj.mdev->dev, + "source route not active\n"); + return -EINVAL; + } + + dev_dbg(sink->entity.graph_obj.mdev->dev, + "validating link \"%s\": %u/%u => \"%s\" %u/%u\n", + link->source->entity->name, src_routes[j].source_pad, + src_routes[j].source_stream, sink->entity.name, + sink_routes[i].sink_pad, sink_routes[i].sink_stream); + + rval = v4l2_subdev_link_validate_one( + link, link->source, src_routes[j].source_stream, + link->sink, sink_routes[j].sink_stream); + if (rval) { + dev_dbg(sink->entity.graph_obj.mdev->dev, + "error %d in link validation\n", rval); + return rval; + } + } + + if (i < sink_routing.num_routes) { + dev_dbg(sink->entity.graph_obj.mdev->dev, + "not all sink routes verified; out of source routes\n"); + return -EINVAL; + } + + return 0; +} EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); struct v4l2_subdev_pad_config * From 2c326609428d70a5e31c6f5d52cff237ebfcbf6b Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:28 +0800 Subject: [PATCH 1137/1276] media:entity: graph walk starting from pad This function initializes the graph traversal structure to walk the entities graph starting at the given entity and pad. As some entity may be on the cross node on pipeline, so add one parameter to start the graph traverse. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/media-entity.c | 4 +++- drivers/media/v4l2-core/v4l2-mc.c | 4 ++++ include/media/media-entity.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 0ad584518db7..a0aeee60cb5d 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -332,7 +332,9 @@ void media_graph_walk_start(struct media_graph *graph, graph->top = 0; graph->stack[graph->top].entity = NULL; - stack_push(graph, entity, 0, -1); + stack_push(graph, entity, + entity->start ? entity->start->index : 0, -1); + entity->start = NULL; dev_dbg(entity->graph_obj.mdev->dev, "begin graph walk at '%s'\n", entity->name); } diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 0fc185a2ce90..031418bb0edf 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -375,6 +375,8 @@ int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, int sink_use; int ret = 0; + source->start = link->source; + sink->start = link->sink; source_use = pipeline_pm_use_count(source, graph); sink_use = pipeline_pm_use_count(sink, graph); @@ -383,6 +385,8 @@ int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, /* Powering off entities is assumed to never fail. */ pipeline_pm_power(source, -sink_use, graph); pipeline_pm_power(sink, -source_use, graph); + source->use_count = 0; + sink->use_count = 0; return 0; } diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 0faaf936302b..95c27c39e42f 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -284,6 +284,7 @@ struct media_entity { int internal_idx; struct media_pad *pads; + struct media_pad *start; struct list_head links; const struct media_entity_operations *ops; From 9804397cc9b7d9cd6b4b81b91751cfaec3c2b30f Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:32 +0800 Subject: [PATCH 1138/1276] media: v4l: Add new vectorised pixel formats Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- include/uapi/linux/videodev2.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1a70f2fad418..e37f299b519f 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -544,6 +544,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ #define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ #define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ +#define V4L2_PIX_FMT_Y210 v4l2_fourcc('Y', '2', '1', '0') /* 20 YUV 4:2:2 10-bit yuyv*/ /* two planes -- one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ @@ -610,6 +611,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG12P v4l2_fourcc('p', 'G', 'C', 'C') #define V4L2_PIX_FMT_SGRBG12P v4l2_fourcc('p', 'g', 'C', 'C') #define V4L2_PIX_FMT_SRGGB12P v4l2_fourcc('p', 'R', 'C', 'C') +#define V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4') /* 14 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4') /* 14 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('B', 'A', '1', '4') /* 14 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4') /* 14 RGRG.. GBGB.. */ /* 14bit raw bayer packed, 7 bytes for every 4 pixels */ #define V4L2_PIX_FMT_SBGGR14P v4l2_fourcc('p', 'B', 'E', 'E') #define V4L2_PIX_FMT_SGBRG14P v4l2_fourcc('p', 'G', 'E', 'E') @@ -620,6 +625,31 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16 RGRG.. GBGB.. */ +/* Raw bayer vector formats. */ +#define V4L2_PIX_FMT_SBGGR8_16V32 v4l2_fourcc('b', 'V', '0', 'A') +#define V4L2_PIX_FMT_SGBRG8_16V32 v4l2_fourcc('b', 'V', '0', 'B') +#define V4L2_PIX_FMT_SGRBG8_16V32 v4l2_fourcc('b', 'V', '0', 'C') +#define V4L2_PIX_FMT_SRGGB8_16V32 v4l2_fourcc('b', 'V', '0', 'D') +#define V4L2_PIX_FMT_SBGGR10V32 v4l2_fourcc('b', 'V', '0', 'E') +#define V4L2_PIX_FMT_SGBRG10V32 v4l2_fourcc('b', 'V', '0', 'F') +#define V4L2_PIX_FMT_SGRBG10V32 v4l2_fourcc('b', 'V', '0', 'G') +#define V4L2_PIX_FMT_SRGGB10V32 v4l2_fourcc('b', 'V', '0', 'H') +#define V4L2_PIX_FMT_SBGGR12V32 v4l2_fourcc('b', 'V', '0', 'I') +#define V4L2_PIX_FMT_SGBRG12V32 v4l2_fourcc('b', 'V', '0', 'J') +#define V4L2_PIX_FMT_SGRBG12V32 v4l2_fourcc('b', 'V', '0', 'K') +#define V4L2_PIX_FMT_SRGGB12V32 v4l2_fourcc('b', 'V', '0', 'L') + +/* BEGIN remove once the user space has been updated */ +#define V4L2_PIX_FMT_SBGGR8V32 v4l2_fourcc('b', 'V', '0', 'A') +#define V4L2_PIX_FMT_SGBRG8V32 v4l2_fourcc('b', 'V', '0', 'B') +#define V4L2_PIX_FMT_SGRBG8V32 v4l2_fourcc('b', 'V', '0', 'C') +#define V4L2_PIX_FMT_SRGGB8V32 v4l2_fourcc('b', 'V', '0', 'D') +/* END remove once the user space has been updated */ + +/* YUV vector formats. */ +#define V4L2_PIX_FMT_UYVY_V32 v4l2_fourcc('y', 'V', '3', '2') +#define V4L2_PIX_FMT_YUYV420_V32 v4l2_fourcc('y', '0', '3', '2') + /* HSV formats */ #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3') #define V4L2_PIX_FMT_HSV32 v4l2_fourcc('H', 'S', 'V', '4') From 38e61af1ffecb0a61b38a757d40f2c73640d1d65 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:36 +0800 Subject: [PATCH 1139/1276] v4l2-ctrl: Add platform specific v4l2 control Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- include/uapi/linux/v4l2-controls.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index e4ee10ee917d..4913a4f78a08 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -999,7 +999,11 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_CID_TEST_PATTERN_GREENR (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 5) #define V4L2_CID_TEST_PATTERN_BLUE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6) #define V4L2_CID_TEST_PATTERN_GREENB (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7) - +#define V4L2_CID_MIPI_LANES (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 64) +#define V4L2_CID_WDR_MODE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 65) +#define V4L2_CID_PWM_DUTY (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 66) +#define V4L2_CID_IRIS_MODE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 67) +#define V4L2_CID_IRIS_STEP (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 68) /* Image processing controls */ From 4a73dc80772341f9b67ab6138b5eb877be26328e Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:40 +0800 Subject: [PATCH 1140/1276] v4l: Extend struct v4l2_mbus_frame_desc_entry Add bus type to frame descriptors Add bpp fields and bus-specific information (CSI-2). Add information for 2D DMA to the frame descriptor entries Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- include/media/v4l2-subdev.h | 53 +++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 4bd777a68d64..889deab36029 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -332,29 +332,66 @@ enum v4l2_mbus_frame_desc_flags { V4L2_MBUS_FRAME_DESC_FL_BLOB = BIT(1), }; +/** + * struct v4l2_mbus_frame_desc_entry_csi2 + * + * @channel: CSI-2 virtual channel + * @data_type: CSI-2 data type ID + */ +struct v4l2_mbus_frame_desc_entry_csi2 { + u8 channel; + u8 data_type; +}; + /** * struct v4l2_mbus_frame_desc_entry - media bus frame description structure * - * @flags: bitmask flags, as defined by &enum v4l2_mbus_frame_desc_flags. - * @pixelcode: media bus pixel code, valid if @flags - * %FRAME_DESC_FL_BLOB is not set. - * @length: number of octets per frame, valid if @flags - * %V4L2_MBUS_FRAME_DESC_FL_LEN_MAX is set. + * @flags: V4L2_MBUS_FRAME_DESC_FL_* flags + * @bpp: bits per pixel + * @pixelcode: media bus pixel code, valid if FRAME_DESC_FL_BLOB is not set + * @start_line: start line of the data for 2D DMA + * @start_pixel: start pixel of the data for 2D DMA + * @width: image width for 2D DMA + * @height: image height for 2D DMA + * @length: number of octets per frame, valid if V4L2_MBUS_FRAME_DESC_FL_BLOB + * is set + * @csi2: CSI-2 specific bus configuration */ struct v4l2_mbus_frame_desc_entry { - enum v4l2_mbus_frame_desc_flags flags; + u16 flags; + u8 bpp; u32 pixelcode; - u32 length; + union { + struct { + u16 start_line; + u16 start_pixel; + u16 width; + u16 height; + } two_dim; + u32 length; + }; + union { + struct v4l2_mbus_frame_desc_entry_csi2 csi2; + } bus; +}; + +enum { + V4L2_MBUS_FRAME_DESC_TYPE_PLATFORM, + V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL, + V4L2_MBUS_FRAME_DESC_TYPE_CCP2, + V4L2_MBUS_FRAME_DESC_TYPE_CSI2, }; #define V4L2_FRAME_DESC_ENTRY_MAX 4 /** * struct v4l2_mbus_frame_desc - media bus data frame description + * @type: type of the bus (V4L2_MBUS_FRAME_DESC_TYPE_*) * @entry: frame descriptors array * @num_entries: number of entries in @entry array */ struct v4l2_mbus_frame_desc { + u32 type; struct v4l2_mbus_frame_desc_entry entry[V4L2_FRAME_DESC_ENTRY_MAX]; unsigned short num_entries; }; @@ -783,7 +820,7 @@ struct v4l2_subdev_internal_ops { /* Set this flag if this subdev generates events. */ #define V4L2_SUBDEV_FL_HAS_EVENTS (1U << 3) /* Set this flag if this sub-device supports substreams. */ -#define V4L2_SUBDEV_FL_HAS_SUBSTREAMS (1U << 4) +#define V4L2_SUBDEV_FL_HAS_SUBSTREAMS (1U << 4) struct regulator_bulk_data; From 22acb2bd518b7b3e289a32c6b31b08270d6b7b16 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:44 +0800 Subject: [PATCH 1141/1276] media: i2c: crlmodule: common register list based sensor driver Common register list based driver is based on a sensor configuration file created for a specific sensor module based on a set of rules. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/Makefile | 2 + drivers/media/i2c/crlmodule/Kconfig | 6 + drivers/media/i2c/crlmodule/Makefile | 18 + .../i2c/crlmodule/crl_adv7481_configuration.h | 706 ++ .../crl_adv7481_cvbs_configuration.h | 285 + .../crl_adv7481_eval_configuration.h | 577 ++ .../crl_adv7481_hdmi_configuration.c | 615 ++ .../crl_adv7481_hdmi_configuration.h | 1025 +++ .../crlmodule/crl_ar0231at_configuration.h | 2409 +++++ .../i2c/crlmodule/crl_ar023z_configuration.h | 1903 ++++ .../i2c/crlmodule/crl_imx132_configuration.h | 699 ++ .../i2c/crlmodule/crl_imx135_configuration.h | 779 ++ .../i2c/crlmodule/crl_imx185_configuration.h | 1772 ++++ .../i2c/crlmodule/crl_imx214_configuration.h | 1428 +++ .../i2c/crlmodule/crl_imx230_configuration.h | 2367 +++++ .../i2c/crlmodule/crl_imx274_configuration.h | 1272 +++ .../i2c/crlmodule/crl_imx290_configuration.h | 1078 +++ .../i2c/crlmodule/crl_imx318_configuration.h | 1050 +++ .../i2c/crlmodule/crl_imx477_common_regs.h | 1096 +++ .../crl_imx477_master_configuration.h | 1375 +++ .../crl_imx477_slave_configuration.h | 509 + .../i2c/crlmodule/crl_magna_configuration.h | 209 + .../i2c/crlmodule/crl_ov10635_configuration.h | 6368 +++++++++++++ .../i2c/crlmodule/crl_ov10640_configuration.h | 3235 +++++++ .../i2c/crlmodule/crl_ov13860_configuration.h | 1537 ++++ .../i2c/crlmodule/crl_ov2740_configuration.h | 761 ++ .../i2c/crlmodule/crl_ov2775_configuration.h | 8177 +++++++++++++++++ .../i2c/crlmodule/crl_ov5670_configuration.h | 1136 +++ .../i2c/crlmodule/crl_ov8858_configuration.h | 1429 +++ .../i2c/crlmodule/crl_ov9281_configuration.h | 520 ++ .../i2c/crlmodule/crl_ox03a10_configuration.h | 1709 ++++ .../crlmodule/crl_pixter_stub_configuration.h | 1386 +++ drivers/media/i2c/crlmodule/crlmodule-core.c | 3519 +++++++ drivers/media/i2c/crlmodule/crlmodule-data.c | 125 + .../media/i2c/crlmodule/crlmodule-msrlist.c | 158 + .../media/i2c/crlmodule/crlmodule-msrlist.h | 51 + drivers/media/i2c/crlmodule/crlmodule-nvm.c | 141 + drivers/media/i2c/crlmodule/crlmodule-nvm.h | 23 + drivers/media/i2c/crlmodule/crlmodule-regs.c | 341 + drivers/media/i2c/crlmodule/crlmodule-regs.h | 26 + .../media/i2c/crlmodule/crlmodule-sensor-ds.h | 622 ++ drivers/media/i2c/crlmodule/crlmodule.h | 125 + include/media/crlmodule.h | 43 + include/uapi/linux/crlmodule.h | 76 + 45 files changed, 52689 insertions(+) create mode 100644 drivers/media/i2c/crlmodule/Kconfig create mode 100644 drivers/media/i2c/crlmodule/Makefile create mode 100644 drivers/media/i2c/crlmodule/crl_adv7481_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_adv7481_cvbs_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_adv7481_eval_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.c create mode 100644 drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ar0231at_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ar023z_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx132_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx135_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx185_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx214_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx230_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx274_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx290_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx318_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx477_common_regs.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx477_master_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_imx477_slave_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_magna_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov10635_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov10640_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov13860_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov2740_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov2775_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov5670_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov8858_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ov9281_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_ox03a10_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crl_pixter_stub_configuration.h create mode 100644 drivers/media/i2c/crlmodule/crlmodule-core.c create mode 100644 drivers/media/i2c/crlmodule/crlmodule-data.c create mode 100644 drivers/media/i2c/crlmodule/crlmodule-msrlist.c create mode 100644 drivers/media/i2c/crlmodule/crlmodule-msrlist.h create mode 100644 drivers/media/i2c/crlmodule/crlmodule-nvm.c create mode 100644 drivers/media/i2c/crlmodule/crlmodule-nvm.h create mode 100644 drivers/media/i2c/crlmodule/crlmodule-regs.c create mode 100644 drivers/media/i2c/crlmodule/crlmodule-regs.h create mode 100644 drivers/media/i2c/crlmodule/crlmodule-sensor-ds.h create mode 100644 drivers/media/i2c/crlmodule/crlmodule.h create mode 100644 include/media/crlmodule.h create mode 100644 include/uapi/linux/crlmodule.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 82af97430e5b..304ee02e2497 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -945,6 +945,7 @@ config VIDEO_S5K5BAF source "drivers/media/i2c/smiapp/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" +source "drivers/media/i2c/crlmodule/Kconfig" config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a94eb03d10d4..db6e8021b947 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -110,3 +110,5 @@ obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_SDR_MAX2175) += max2175.o + +obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule/ diff --git a/drivers/media/i2c/crlmodule/Kconfig b/drivers/media/i2c/crlmodule/Kconfig new file mode 100644 index 000000000000..2da6dafcc2c6 --- /dev/null +++ b/drivers/media/i2c/crlmodule/Kconfig @@ -0,0 +1,6 @@ +config VIDEO_CRLMODULE + tristate "CRL Module sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a generic driver for CRL based camera modules. diff --git a/drivers/media/i2c/crlmodule/Makefile b/drivers/media/i2c/crlmodule/Makefile new file mode 100644 index 000000000000..c3a1fed9c6bb --- /dev/null +++ b/drivers/media/i2c/crlmodule/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2018, Intel Corporation. + +# force check the compile warning to make sure zero warnings +# note we may have build issue when gcc upgraded. +ccflags-y := -Wall -Wextra +ccflags-y += $(call cc-disable-warning, unused-parameter) +ccflags-y += $(call cc-disable-warning, implicit-fallthrough) +ccflags-y += $(call cc-disable-warning, missing-field-initializers) +ccflags-$(CONFIG_VIDEO_INTEL_IPU_WERROR) += -Werror + +crlmodule-objs += crlmodule-core.o crlmodule-data.o \ + crlmodule-regs.o crlmodule-nvm.o \ + crl_adv7481_hdmi_configuration.o \ + crlmodule-msrlist.o +obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/crlmodule/crl_adv7481_configuration.h b/drivers/media/i2c/crlmodule/crl_adv7481_configuration.h new file mode 100644 index 000000000000..9cf6e37074a8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_adv7481_configuration.h @@ -0,0 +1,706 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Author: Jianxu Zheng + * + */ + +#ifndef __CRLMODULE_ADV7481_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep adv7481_powerup_regset[] = { + {0xFF, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* SW reset */ + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, /* Delay 5ms */ + {0x01, CRL_REG_LEN_08BIT, 0x76, 0xE0}, /* ADI recommended setting */ + {0xF2, CRL_REG_LEN_08BIT, 0x01, 0xE0}, /* I2C Rd Auto-Increment=1 */ + {0xF3, CRL_REG_LEN_08BIT, 0x4C, 0xE0}, /* DPLL Map Address */ + {0xF4, CRL_REG_LEN_08BIT, 0x44, 0xE0}, /* CP Map Address */ + {0xF5, CRL_REG_LEN_08BIT, 0x68, 0xE0}, /* HDMI RX Map Address */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* EDID Map Address */ + {0xF7, CRL_REG_LEN_08BIT, 0x64, 0xE0}, /* HDMI RX Repeater Map Addr */ + {0xF8, CRL_REG_LEN_08BIT, 0x62, 0xE0}, /* HDMI RX Infoframe Map Addr */ + {0xF9, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CBUS Map Address Set */ + {0xFA, CRL_REG_LEN_08BIT, 0x82, 0xE0}, /* CEC Map Address Set */ + {0xFB, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* SDP Main Map Address */ + {0xFC, CRL_REG_LEN_08BIT, 0x90, 0xE0}, /* CSI-TXB Map Address */ + {0xFD, CRL_REG_LEN_08BIT, 0x94, 0xE0}, /* CSI-TXA Map Address */ + {0x00, CRL_REG_LEN_08BIT, 0x50, 0xE0}, /* Disable Chip Powerdown & + HDMI Rx Block */ + {0x40, CRL_REG_LEN_08BIT, 0x83, 0x64}, /* Enable HDCP 1.1 */ + {0x00, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* ADI recommended setting */ + {0x3D, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI recommended setting */ + {0x3E, CRL_REG_LEN_08BIT, 0x69, 0x68}, /* ADI recommended setting */ + {0x3F, CRL_REG_LEN_08BIT, 0x46, 0x68}, /* ADI recommended setting */ + {0x4E, CRL_REG_LEN_08BIT, 0xFE, 0x68}, /* ADI recommended setting */ + {0x4F, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* ADI recommended setting */ + {0x57, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI recommended setting */ + {0x58, CRL_REG_LEN_08BIT, 0x04, 0x68}, /* ADI recommended setting */ + {0x85, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI recommended setting */ + {0x83, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Enable All Terminations */ + {0xBE, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI recommended setting */ + {0x6C, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Manual Enable */ + {0xF8, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Asserted */ + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Audio Mute Speed = + Fastest Smallest Step Size */ + {0x70, CRL_REG_LEN_08BIT, 0xA0, 0x64}, /* Write primary edid size */ + {0x74, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* Enable manual edid */ + {0x7A, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Write edid sram select */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* Write edid map bus address */ + + {0x00*4, CRL_REG_LEN_32BIT, 0x00FFFFFF, 0x6C}, /* EDID programming */ + {0x01*4, CRL_REG_LEN_32BIT, 0xFFFFFF00, 0x6C}, /* EDID programming */ + {0x02*4, CRL_REG_LEN_32BIT, 0x4DD90100, 0x6C}, /* EDID programming */ + {0x03*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x04*4, CRL_REG_LEN_32BIT, 0x00110103, 0x6C}, /* EDID programming */ + {0x05*4, CRL_REG_LEN_32BIT, 0x80000078, 0x6C}, /* EDID programming */ + {0x06*4, CRL_REG_LEN_32BIT, 0x0A0DC9A0, 0x6C}, /* EDID programming */ + {0x07*4, CRL_REG_LEN_32BIT, 0x57479827, 0x6C}, /* EDID programming */ + {0x08*4, CRL_REG_LEN_32BIT, 0x12484C00, 0x6C}, /* EDID programming */ + {0x09*4, CRL_REG_LEN_32BIT, 0x00000101, 0x6C}, /* EDID programming */ + {0x0A*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0B*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0C*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0D*4, CRL_REG_LEN_32BIT, 0x0101011D, 0x6C}, /* EDID programming */ + {0x0E*4, CRL_REG_LEN_32BIT, 0x80D0721C, 0x6C}, /* EDID programming */ + {0x0F*4, CRL_REG_LEN_32BIT, 0x1620102C, 0x6C}, /* EDID programming */ + {0x10*4, CRL_REG_LEN_32BIT, 0x2580C48E, 0x6C}, /* EDID programming */ + {0x11*4, CRL_REG_LEN_32BIT, 0x2100009E, 0x6C}, /* EDID programming */ + {0x12*4, CRL_REG_LEN_32BIT, 0x011D8018, 0x6C}, /* EDID programming */ + {0x13*4, CRL_REG_LEN_32BIT, 0x711C1620, 0x6C}, /* EDID programming */ + {0x14*4, CRL_REG_LEN_32BIT, 0x582C2500, 0x6C}, /* EDID programming */ + {0x15*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x16*4, CRL_REG_LEN_32BIT, 0x009E0000, 0x6C}, /* EDID programming */ + {0x17*4, CRL_REG_LEN_32BIT, 0x00FC0048, 0x6C}, /* EDID programming */ + {0x18*4, CRL_REG_LEN_32BIT, 0x444D4920, 0x6C}, /* EDID programming */ + {0x19*4, CRL_REG_LEN_32BIT, 0x4C4C430A, 0x6C}, /* EDID programming */ + {0x1A*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1B*4, CRL_REG_LEN_32BIT, 0x000000FD, 0x6C}, /* EDID programming */ + {0x1C*4, CRL_REG_LEN_32BIT, 0x003B3D0F, 0x6C}, /* EDID programming */ + {0x1D*4, CRL_REG_LEN_32BIT, 0x2D08000A, 0x6C}, /* EDID programming */ + {0x1E*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1F*4, CRL_REG_LEN_32BIT, 0x202001C1, 0x6C}, /* EDID programming */ + {0x20*4, CRL_REG_LEN_32BIT, 0x02031E77, 0x6C}, /* EDID programming */ + {0x21*4, CRL_REG_LEN_32BIT, 0x4F941305, 0x6C}, /* EDID programming */ + {0x22*4, CRL_REG_LEN_32BIT, 0x03040201, 0x6C}, /* EDID programming */ + {0x23*4, CRL_REG_LEN_32BIT, 0x16150706, 0x6C}, /* EDID programming */ + {0x24*4, CRL_REG_LEN_32BIT, 0x1110121F, 0x6C}, /* EDID programming */ + {0x25*4, CRL_REG_LEN_32BIT, 0x23090701, 0x6C}, /* EDID programming */ + {0x26*4, CRL_REG_LEN_32BIT, 0x65030C00, 0x6C}, /* EDID programming */ + {0x27*4, CRL_REG_LEN_32BIT, 0x10008C0A, 0x6C}, /* EDID programming */ + {0x28*4, CRL_REG_LEN_32BIT, 0xD0902040, 0x6C}, /* EDID programming */ + {0x29*4, CRL_REG_LEN_32BIT, 0x31200C40, 0x6C}, /* EDID programming */ + {0x2A*4, CRL_REG_LEN_32BIT, 0x5500138E, 0x6C}, /* EDID programming */ + {0x2B*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x2C*4, CRL_REG_LEN_32BIT, 0x011D00BC, 0x6C}, /* EDID programming */ + {0x2D*4, CRL_REG_LEN_32BIT, 0x52D01E20, 0x6C}, /* EDID programming */ + {0x2E*4, CRL_REG_LEN_32BIT, 0xB8285540, 0x6C}, /* EDID programming */ + {0x2F*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x30*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x31*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x32*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x33*4, CRL_REG_LEN_32BIT, 0x9600C48E, 0x6C}, /* EDID programming */ + {0x34*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x35*4, CRL_REG_LEN_32BIT, 0x011D0072, 0x6C}, /* EDID programming */ + {0x36*4, CRL_REG_LEN_32BIT, 0x51D01E20, 0x6C}, /* EDID programming */ + {0x37*4, CRL_REG_LEN_32BIT, 0x6E285500, 0x6C}, /* EDID programming */ + {0x38*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x39*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x3A*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x3B*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x3C*4, CRL_REG_LEN_32BIT, 0x9600138E, 0x6C}, /* EDID programming */ + {0x3D*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x3E*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x3F*4, CRL_REG_LEN_32BIT, 0x000000CB, 0x6C}, /* EDID programming */ + + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* No MIPI frame start */ + {0x26, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* Disable sleep mode */ + {0x27, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* Disable escape mode */ + {0x7E, CRL_REG_LEN_08BIT, 0xA0, 0x94}, /* ADI recommended setting */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* ADI recommended setting */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x90}, /* ADI recommended setting */ + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, /* ADI recommended setting */ + {0x34, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* ADI recommended setting */ + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, /* ADI recommended setting */ + {0xCA, CRL_REG_LEN_08BIT, 0x02, 0x94}, /* ADI recommended setting */ + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, /* ADI recommended setting */ + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, /* ADI recommended setting */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Power up DPHY */ + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI recommended setting */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_1080p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x5E, 0xE0}, /* Select Resolution 1080P */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 1080P shift left 44 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080P shift left 44 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 1080P shift left 44 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080P shift left 44 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_720p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x53, 0xE0}, /* Select Resolution 720P */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 720P shift left 40 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD8, 0x44}, /* 720P shift left 40 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 720P shift left 40 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD8, 0x44}, /* 720P shift left 40 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_VGA[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x88, 0xE0}, /* Select Resolution VGA */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_1080i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x54, 0xE0}, /* Select Resolution 1080i*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 1080i shift left 44 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080i shift left 44 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 1080i shift left 44 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080i shift left 44 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_480i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* Select Resolution 480i */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_576p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x4B, 0xE0}, /* Select Resolution 576p*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_576i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x41, 0xE0}, /* Select Resolution 576i*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_streamon_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, /* Power-up CSI-TX */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* i2c_mipi_pll_en - 1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x94}, +}; + +static struct crl_sensor_detect_config adv7481_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration adv7481_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + +}; + +static struct crl_subdev_rect_rep adv7481_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_1080p_rects), + .sd_rects = adv7481_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_1080p), + .mode_regs = adv7481_mode_1080p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_720p_rects), + .sd_rects = adv7481_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_720p), + .mode_regs = adv7481_mode_720p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_VGA_rects), + .sd_rects = adv7481_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_VGA), + .mode_regs = adv7481_mode_VGA, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_1080i_rects), + .sd_rects = adv7481_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_1080i), + .mode_regs = adv7481_mode_1080i, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_480i_rects), + .sd_rects = adv7481_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_480i), + .mode_regs = adv7481_mode_480i, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_576p_rects), + .sd_rects = adv7481_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_576p), + .mode_regs = adv7481_mode_576p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_576i_rects), + .sd_rects = adv7481_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = adv7481_mode_576i, + }, +}; + +static struct crl_sensor_subdev_config adv7481_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_mode_1080p), + .regs = adv7481_mode_1080p, /* default yuv422 format */ + }, +}; + +static struct crl_v4l2_ctrl adv7481_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity adv7481_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +static struct crl_sensor_configuration adv7481_crl_configuration = { + + .power_items = ARRAY_SIZE(adv7481_power_items), + .power_entities = adv7481_power_items, + + .powerup_regs_items = ARRAY_SIZE(adv7481_powerup_regset), + .powerup_regs = adv7481_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_streamoff_regs), + .poweroff_regs = adv7481_streamoff_regs, + + .id_reg_items = ARRAY_SIZE(adv7481_sensor_detect_regset), + .id_regs = adv7481_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(adv7481_sensor_subdevs), + .subdevs = adv7481_sensor_subdevs, + + .sensor_limits = &adv7481_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_pll_configurations), + .pll_configs = adv7481_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_modes), + .modes = adv7481_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_streamon_regs), + .streamon_regs = adv7481_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_streamoff_regs), + .streamoff_regs = adv7481_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(adv7481_v4l2_ctrls), + .v4l2_ctrl_bank = adv7481_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_crl_csi_data_fmt), + .csi_fmts = adv7481_crl_csi_data_fmt, +}; + +#endif /* __CRLMODULE_ADV7481_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_adv7481_cvbs_configuration.h b/drivers/media/i2c/crlmodule/crl_adv7481_cvbs_configuration.h new file mode 100644 index 000000000000..2d8b4f22edf3 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_adv7481_cvbs_configuration.h @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Author: Jianxu Zheng + * + */ + +#ifndef __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep adv7481_cvbs_powerup_regset[] = { + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/ + SPI PINS TRISTATED */ + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0xF2}, /* Exit Power Down Mode */ + {0x52, CRL_REG_LEN_08BIT, 0xC0, 0xF2}, /* ADI Required Write */ + {0x00, CRL_REG_LEN_08BIT, 0x0E, 0xF2}, /* INSEL = CVBS in on Ain 1 */ + {0x0E, CRL_REG_LEN_08BIT, 0x80, 0xF2}, /* ADI Required Write */ + {0x9C, CRL_REG_LEN_08BIT, 0x00, 0xF2}, /* ADI Required Write */ + {0x9C, CRL_REG_LEN_08BIT, 0xFF, 0xF2}, /* ADI Required Write */ + {0x0E, CRL_REG_LEN_08BIT, 0x00, 0xF2}, /* ADI Required Write */ + {0x5A, CRL_REG_LEN_08BIT, 0x90, 0xF2}, /* ADI Required Write */ + {0x60, CRL_REG_LEN_08BIT, 0xA0, 0xF2}, /* ADI Required Write */ + {0x00, CRL_REG_LEN_DELAY, 0x19, 0x00}, /* Delay 25*/ + {0x60, CRL_REG_LEN_08BIT, 0xB0, 0xF2}, /* ADI Required Write */ + {0x5F, CRL_REG_LEN_08BIT, 0xA8, 0xF2}, + {0x0E, CRL_REG_LEN_08BIT, 0x80, 0xF2}, /* ADI Required Write */ + {0xB6, CRL_REG_LEN_08BIT, 0x08, 0xF2}, /* ADI Required Write */ + {0xC0, CRL_REG_LEN_08BIT, 0xA0, 0xF2}, /* ADI Required Write */ + {0xD9, CRL_REG_LEN_08BIT, 0x44, 0xF2}, + {0x0E, CRL_REG_LEN_08BIT, 0x40, 0xF2}, + {0xE0, CRL_REG_LEN_08BIT, 0x01, 0xF2}, /* Fast Lock enable*/ + {0x0E, CRL_REG_LEN_08BIT, 0x00, 0xF2}, /* ADI Required Write */ + {0x80, CRL_REG_LEN_08BIT, 0x51, 0xF2}, /* ADI Required Write */ + {0x81, CRL_REG_LEN_08BIT, 0x51, 0xF2}, /* ADI Required Write */ + {0x82, CRL_REG_LEN_08BIT, 0x68, 0xF2}, /* ADI Required Write */ + {0x03, CRL_REG_LEN_08BIT, 0x42, 0xF2}, /* Tri-S Output Drivers, + PwrDwn 656 pads */ + {0x04, CRL_REG_LEN_08BIT, 0x07, 0xF2}, /* Power-up INTRQ pad, + & Enable SFL */ + {0x13, CRL_REG_LEN_08BIT, 0x00, 0xF2}, /* ADI Required Write */ + {0x17, CRL_REG_LEN_08BIT, 0x41, 0xF2}, /* Select SH1 */ + {0x31, CRL_REG_LEN_08BIT, 0x12, 0xF2}, /* ADI Required Write */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x70, 0xE0, 0x70 }, + /* Enable 1-Lane MIPI Tx, + enable pixel output and route + SD through Pixel port */ + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x90}, /* Enable 1-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x90}, /* Set Auto DPHY Timing */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* ADI Required Write */ + {0xD2, CRL_REG_LEN_08BIT, 0x40, 0x90}, /* ADI Required Write */ + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x90}, /* ADI Required Write */ + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x90}, /* ADI Required Write */ + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x90}, /* ADI Required Write */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* i2c_dphy_pwdn - 1'b0 */ + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x90}, /* ADI Required Write */ + {0x1E, CRL_REG_LEN_08BIT, 0xC0, 0x90}, /* ADI Required Write */ +}; + + +static struct crl_register_write_rep adv7481_cvbs_streamon_regs[] = { + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x90}, /* ADI Required Write */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x90}, /* i2c_mipi_pll_en - 1'b1 */ + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x90}, /* Power-up CSI-TX 21 */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x90}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_cvbs_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x90}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x90}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* i2c_mipi_pll_en - + 1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x90}, +}; + + +static struct crl_pll_configuration adv7481_cvbs_pll_configurations[] = { + { + .input_clk = 286363636, + .op_sys_clk = 216000000, + .bitsperpixel = 16, + .pixel_rate_csi = 27000000, + .pixel_rate_pa = 27000000, + .csi_lanes = 1, + }, + { + .input_clk = 24000000, + .op_sys_clk = 130000000, + .bitsperpixel = 16, + .pixel_rate_csi = 130000000, + .pixel_rate_pa = 130000000, + .csi_lanes = 1, + }, +}; + +static struct crl_subdev_rect_rep adv7481_cvbs_ntsc_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 288, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 288, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_cvbs_pal_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 288, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 288, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; + +static struct crl_mode_rep adv7481_cvbs_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_cvbs_ntsc_rects), + .sd_rects = adv7481_cvbs_ntsc_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 240, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_cvbs_pal_rects), + .sd_rects = adv7481_cvbs_pal_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 288, + }, +}; + +static struct crl_sensor_subdev_config adv7481_cvbs_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481-cvbs binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481-cvbs pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_cvbs_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 720, + .y_addr_max = 288, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_cvbs_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + }, +}; + +static struct crl_v4l2_ctrl adv7481_cvbs_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, +}; + +static struct crl_sensor_configuration adv7481_cvbs_crl_configuration = { + + /* one time initialization is done by HDMI part */ + + .powerup_regs_items = ARRAY_SIZE(adv7481_cvbs_powerup_regset), + .powerup_regs = adv7481_cvbs_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_cvbs_streamoff_regs), + .poweroff_regs = adv7481_cvbs_streamoff_regs, + + .subdev_items = ARRAY_SIZE(adv7481_cvbs_sensor_subdevs), + .subdevs = adv7481_cvbs_sensor_subdevs, + + .sensor_limits = &adv7481_cvbs_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_cvbs_pll_configurations), + .pll_configs = adv7481_cvbs_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_cvbs_modes), + .modes = adv7481_cvbs_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_cvbs_streamon_regs), + .streamon_regs = adv7481_cvbs_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_cvbs_streamoff_regs), + .streamoff_regs = adv7481_cvbs_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(adv7481_cvbs_v4l2_ctrls), + .v4l2_ctrl_bank = adv7481_cvbs_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_cvbs_crl_csi_data_fmt), + .csi_fmts = adv7481_cvbs_crl_csi_data_fmt, + + .addr_len = CRL_ADDR_7BIT, + .i2c_mutex_in_use = true, +}; + +#endif /* __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_adv7481_eval_configuration.h b/drivers/media/i2c/crlmodule/crl_adv7481_eval_configuration.h new file mode 100644 index 000000000000..c7781706d1f3 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_adv7481_eval_configuration.h @@ -0,0 +1,577 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Author: Jianxu Zheng + * + */ + +#ifndef __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + + +struct crl_ctrl_data_pair ctrl_data_lanes[] = { + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 4, + }, + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 2, + }, + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 1, + }, +}; + +static struct crl_pll_configuration adv7481_eval_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 24, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_WVGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 800, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_eval_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_1080p_rects), + .sd_rects = adv7481_eval_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[0], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_720p_rects), + .sd_rects = adv7481_eval_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[0], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_WVGA_rects), + .sd_rects = adv7481_eval_WVGA_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 800, + .height = 480, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[2], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_VGA_rects), + .sd_rects = adv7481_eval_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[2], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_1080i_rects), + .sd_rects = adv7481_eval_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_480i_rects), + .sd_rects = adv7481_eval_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[2], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_576p_rects), + .sd_rects = adv7481_eval_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[2], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_576i_rects), + .sd_rects = adv7481_eval_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[2], + .mode_regs_items = 0, + .mode_regs = NULL, + }, +}; + +static struct crl_sensor_subdev_config adv7481_eval_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 pixel array", + }, +}; + +static struct crl_sensor_subdev_config adv7481b_eval_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481b binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481b pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_eval_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_eval_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 24, + .regs_items = 0, + .regs = NULL, + }, +}; + +static struct crl_v4l2_ctrl adv7481_eval_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_MIPI_LANES, + .name = "V4L2_CID_MIPI_LANES", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = 4, + .data.std_data.step = 1, + .data.std_data.def = 4, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_sensor_configuration adv7481_eval_crl_configuration = { + + .powerup_regs_items = 0, + .powerup_regs = NULL, + + .poweroff_regs_items = 0, + .poweroff_regs = NULL, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = ARRAY_SIZE(adv7481_eval_sensor_subdevs), + .subdevs = adv7481_eval_sensor_subdevs, + + .sensor_limits = &adv7481_eval_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_eval_pll_configurations), + .pll_configs = adv7481_eval_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_eval_modes), + .modes = adv7481_eval_modes, + + .streamon_regs_items = 0, + .streamon_regs = NULL, + + .streamoff_regs_items = 0, + .streamoff_regs = NULL, + + .v4l2_ctrls_items = ARRAY_SIZE(adv7481_eval_v4l2_ctrls), + .v4l2_ctrl_bank = adv7481_eval_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_eval_crl_csi_data_fmt), + .csi_fmts = adv7481_eval_crl_csi_data_fmt, +}; + +static struct crl_sensor_configuration adv7481b_eval_crl_configuration = { + + .powerup_regs_items = 0, + .powerup_regs = NULL, + + .poweroff_regs_items = 0, + .poweroff_regs = NULL, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = ARRAY_SIZE(adv7481b_eval_sensor_subdevs), + .subdevs = adv7481b_eval_sensor_subdevs, + + .sensor_limits = &adv7481_eval_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_eval_pll_configurations), + .pll_configs = adv7481_eval_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_eval_modes), + .modes = adv7481_eval_modes, + + .streamon_regs_items = 0, + .streamon_regs = NULL, + + .streamoff_regs_items = 0, + .streamoff_regs = NULL, + + .v4l2_ctrls_items = ARRAY_SIZE(adv7481_eval_v4l2_ctrls), + .v4l2_ctrl_bank = adv7481_eval_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_eval_crl_csi_data_fmt), + .csi_fmts = adv7481_eval_crl_csi_data_fmt, +}; + +#endif /* __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.c b/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.c new file mode 100644 index 000000000000..d8d9c7b930a8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2016 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include + +#include "crlmodule.h" +#include "crlmodule-regs.h" + +/* Size of the mondello KSV buffer in bytes */ +#define ADV7481_KSV_BUFFER_SIZE 0x80 +/* Size of a single KSV */ +#define ADV7481_KSV_SIZE 0x05 +/* Max number of devices (MAX_MONDELO_KSV_SIZE / HDCP_KSV_SIZE */ +#define ADV7481_MAX_DEVICES 0x19 +#define ADV7481_AKSV_UPDATE_A_ST 0x08 +#define ADV7481_CABLE_DET_A_ST 0x40 +#define ADV7481_V_LOCKED_A_ST 0x02 +#define ADV7481_DE_REGEN_A_ST 0x01 + +struct crl_adv7481_hdmi { + unsigned int in_hot_plug_reset; + int hdmi_res_width; + int hdmi_res_height; + int hdmi_res_interlaced; + int hdmi_cable_connected; + struct delayed_work work; + struct mutex hot_plug_reset_lock; + struct i2c_client *client; +}; + +/* ADV7481 HDCP B-status register */ +struct v4l2_adv7481_bstatus { + union { + __u8 bstatus[2]; + struct { + __u8 device_count:7; + __u8 max_devs_exceeded:1; + __u8 depth:3; + __u8 max_cascade_exceeded:1; + __u8 hdmi_mode:1; + __u8 hdmi_reserved_2:1; + __u8 rsvd:2; + }; + }; +}; + +struct v4l2_adv7481_dev_info { + struct v4l2_adv7481_bstatus bstatus; + __u8 ksv[ADV7481_KSV_BUFFER_SIZE]; +}; + +struct v4l2_adv7481_bcaps { + union { + __u8 bcaps; + struct { + __u8 fast_reauth:1; + __u8 features:1; + __u8 reserved:2; + __u8 fast:1; + __u8 ksv_fifo_ready:1; + __u8 repeater:1; + __u8 hdmi_reserved:1; + }; + }; +}; + +static int adv_i2c_write(struct i2c_client *client, + u16 i2c_addr, + u16 reg, + u8 val) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + return crlmodule_write_reg(sensor, i2c_addr, reg, + CRL_REG_LEN_08BIT, 0xFF, val); +} + +static int adv_i2c_read(struct i2c_client *client, + u16 i2c_addr, + u16 reg, + u32 *val) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_register_read_rep read_reg; + + read_reg.address = reg; + read_reg.len = CRL_REG_LEN_08BIT; + read_reg.dev_i2c_addr = i2c_addr; + return crlmodule_read_reg(sensor, read_reg, val); +} + +/* + * Writes the HDCP BKSV list & status when the system acts + * as an HDCP 1.4 repeater + */ +static long adv_write_bksv(struct i2c_client *client, + struct v4l2_adv7481_dev_info *dev_info) +{ + unsigned int k = 0; + int ret = 0; + u32 reg; + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + dev_dbg(&client->dev, "%s: Writing ADV7481 BKSV list.\n", __func__); + + /* Clear BCAPS KSV list ready */ + ret = adv_i2c_write(client, 0x64, 0x78, 0x01); + if (ret) { + dev_err(&client->dev, + "%s: Error clearing BCAPS KSV list ready!\n", + __func__); + return ret; + } + + /* KSV_LIST_READY_PORT_A KSV list not ready */ + ret = adv_i2c_write(client, 0x64, 0x69, 0x00); + if (ret) { + dev_err(&client->dev, + "%s: Error clearing KSV_LIST_READY_PORT_A register!\n", + __func__); + return ret; + } + + /* Write the BSKV list, one device at a time */ + /* Writing the entire list in one call exceeds frame size */ + for (k = 0; k < ADV7481_MAX_DEVICES; ++k) { + unsigned int j = k * ADV7481_KSV_SIZE; + struct crl_register_write_rep adv_ksv_cmd[] = { + {0x80 + j, CRL_REG_LEN_08BIT, + dev_info->ksv[j + 0], 0x64}, + {0x81 + j, CRL_REG_LEN_08BIT, + dev_info->ksv[j + 1], 0x64}, + {0x82 + j, CRL_REG_LEN_08BIT, + dev_info->ksv[j + 2], 0x64}, + {0x83 + j, CRL_REG_LEN_08BIT, + dev_info->ksv[j + 3], 0x64}, + {0x84 + j, CRL_REG_LEN_08BIT, + dev_info->ksv[j + 4], 0x64}, + }; + ret = crlmodule_write_regs(sensor, adv_ksv_cmd, + ARRAY_SIZE(adv_ksv_cmd)); + + if (ret) { + dev_err(&client->dev, + "%s: Error while writing BKSV list!\n", + __func__); + return ret; + } + } + + /* Finally update the bstatus registers */ + ret = adv_i2c_read(client, 0x64, 0x42, ®); + + if (ret) { + dev_err(&client->dev, + "%s: Error reading bstatus register!\n", + __func__); + return ret; + } + + /* ADV recommendation: only update bits [0:11] */ + /* Take the lower nibble (bits [11:8]) of the input bstatus */ + /* Take the upper nibble (bits [15:12]) of the current register */ + dev_info->bstatus.bstatus[1] = + (dev_info->bstatus.bstatus[1] & 0x0F) | (reg & 0xF0); + { + struct crl_register_write_rep adv_cmd[] = { + {0x41, CRL_REG_LEN_08BIT, + dev_info->bstatus.bstatus[0], 0x64}, + {0x42, CRL_REG_LEN_08BIT, + dev_info->bstatus.bstatus[1], 0x64}, + /* KSV_LIST_READY_PORT_A */ + {0x69, CRL_REG_LEN_08BIT, 0x01, 0x64}, + }; + + ret = crlmodule_write_regs(sensor, adv_cmd, + ARRAY_SIZE(adv_cmd)); + } + + return ret; +} + +static ssize_t adv_bcaps_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 val; + int ret; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + ret = adv_i2c_read(client, 0x64, 0x40, &val); + + if (ret != 0) + return -EIO; + + val = val & 0xFF; + *buf = val; + return 1; +} + +/* Declares bcaps attribute that will be exposed to user space via sysfs */ +static DEVICE_ATTR(bcaps, S_IRUGO, adv_bcaps_show, NULL); + +/* + * Writes provided BKSV value from user space to chip. + * BKSV should be formatted as v4l2_adv7481_dev_info struct, + * it does basic validation and checks if provided buffer size matches + * size of v4l2_adv7481_dev_info struct. In case of error return EIO. + */ +static ssize_t adv_bksv_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + struct v4l2_adv7481_dev_info dev_info; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + dev_dbg(&client->dev, "%s\n", __func__); + if (count != sizeof(struct v4l2_adv7481_dev_info)) + return -EIO; + + dev_info = *((struct v4l2_adv7481_dev_info *) buf); + + ret = adv_write_bksv(client, &dev_info); + + if (ret != 0) + return -EIO; + + return count; +} + +/* Declares bksv attribute that will be exposed to user space via sysfs */ +static DEVICE_ATTR(bksv, S_IWUSR | S_IWGRP, NULL, adv_bksv_store); + +/* + * Enables HPA_MAN_VALUE_PORT_A to enable hot plug detection. + */ +static void adv_hpa_assert(struct work_struct *work) +{ + + struct crl_adv7481_hdmi *adv7481_hdmi + = container_of(work, struct crl_adv7481_hdmi, work.work); + struct i2c_client *client = adv7481_hdmi->client; + + adv_i2c_write(client, 0x68, 0xF8, 0x01); + adv7481_hdmi->in_hot_plug_reset = 0; +} + +/* + * Reauthenticates HDCP by disabling hot plug detection for 2 seconds. + * It can be triggered by user space by writing any value to "reauthenticate" + * attribute. After that time connected source will automatically ask for HDCP + * authentication once again. To prevent sleep, timer is used to delay enabling + * of hot plug by 2 seconds. + * In case that previous reauthentication is not completed, returns EBUSY. + * In case of error returns EIO. + */ +static ssize_t adv_reauthenticate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct crl_adv7481_hdmi *adv7481_hdmi; + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + + adv7481_hdmi = sensor->sensor_specific_data; + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_lock(&adv7481_hdmi->hot_plug_reset_lock); + + if (adv7481_hdmi->in_hot_plug_reset) { + mutex_unlock(&adv7481_hdmi->hot_plug_reset_lock); + return -EBUSY; + } + + /* Clear BCAPS KSV list ready */ + ret = adv_i2c_write(client, 0x64, 0x78, 0x01); + if (ret != 0) { + dev_err(&client->dev, + "%s: Error clearing BCAPS KSV list ready!\n", __func__); + mutex_unlock(&adv7481_hdmi->hot_plug_reset_lock); + return -EIO; + } + + /* KSV_LIST_READY_PORT_A KSV list not ready */ + ret = adv_i2c_write(client, 0x64, 0x69, 0x00); + if (ret != 0) { + dev_err(&client->dev, + "%s: Error clearing KSV_LIST_READY_PORT_A register!\n", + __func__); + mutex_unlock(&adv7481_hdmi->hot_plug_reset_lock); + return -EIO; + } + + ret = adv_i2c_write(client, 0x68, 0xF8, 0x00); + + if (ret != 0) { + mutex_unlock(&adv7481_hdmi->hot_plug_reset_lock); + return -EIO; + } + + adv7481_hdmi->in_hot_plug_reset = 1; + schedule_delayed_work(&adv7481_hdmi->work, msecs_to_jiffies(2000)); + + mutex_unlock(&adv7481_hdmi->hot_plug_reset_lock); + return count; +} + +/* Declares reauthenticate attribute that will be exposed + * to user space via sysfs + */ +static DEVICE_ATTR(reauthenticate, S_IWUSR | S_IWGRP, NULL, + adv_reauthenticate_store); + +/* Dummy show to prevent WARN when registering aksv attribute */ +static ssize_t adv_aksv_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + (void) dev; + (void) attr; + (void) buf; + + return -EIO; +} + +/* Declares aksv attribute that will be exposed to user space via sysfs, + * to notify about AKSV events. + */ +static DEVICE_ATTR(aksv, S_IRUGO, adv_aksv_show, NULL); + + +static ssize_t adv_hdmi_cable_connected_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct crl_adv7481_hdmi *adv7481_hdmi; + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + char interlaced = 'p'; + adv7481_hdmi = sensor->sensor_specific_data; + + if (adv7481_hdmi->hdmi_res_interlaced) + interlaced = 'i'; + + return snprintf(buf, PAGE_SIZE, "%dx%d%c", + adv7481_hdmi->hdmi_res_width, + adv7481_hdmi->hdmi_res_height, interlaced); +} +static DEVICE_ATTR(hdmi_cable_connected, S_IRUGO, + adv_hdmi_cable_connected_show, NULL); + +static ssize_t adv_bstatus_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 b0, b1; + int ret; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + dev_dbg(&client->dev, "Getting bstatus\n"); + ret = adv_i2c_read(client, 0x64, 0x41, &b0); + if (ret != 0) { + dev_err(&client->dev, "Error getting bstatus(0)\n"); + return -EIO; + } + dev_dbg(&client->dev, "btatus(0): 0x%x\n", b0 & 0xff); + ret = adv_i2c_read(client, 0x64, 0x42, &b1); + if (ret != 0) { + dev_err(&client->dev, "Error getting bstatus(1)\n"); + return -EIO; + } + dev_dbg(&client->dev, "bstatus(1): 0x%x\n", b1 & 0xff); + *buf = b0 & 0xff; + buf++; + *buf = b1 & 0xff; + return 2; +} +static DEVICE_ATTR(bstatus, S_IRUGO, adv_bstatus_show, NULL); + +irqreturn_t crl_adv7481_threaded_irq_fn(int irq, void *sensor_struct) +{ + struct crl_sensor *sensor = sensor_struct; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + u32 interrupt_st; + u32 raw_value; + u32 temp[3]; + int ret = 0; + struct crl_register_read_rep reg; + struct crl_adv7481_hdmi *adv7481_hdmi; + + adv7481_hdmi = sensor->sensor_specific_data; + reg.address = 0x90; + reg.len = CRL_REG_LEN_08BIT; + reg.mask = 0xFF; + reg.dev_i2c_addr = 0xE0; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!adv7481_hdmi) + return IRQ_HANDLED; + + /* AKSV_UPDATE_A_ST: check interrupt status */ + ret = adv_i2c_read(client, 0xE0, 0x90, &interrupt_st); + + if (interrupt_st & 0x08 /*ADV7481_AKSV_UPDATE_A_ST*/) { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: AKSV_UPDATE_A_ST: 0x%x\n", + __func__, interrupt_st); + + /* Notify user space about AKSV event */ + sysfs_notify(&client->dev.kobj, NULL, "aksv"); + + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x91, 0x08); + } + + /* + * Check interrupt status for: CABLE_DET_A_ST, + * V_LOCKED_A_ST and DE_REGEN_LCK_A_ST + */ + ret = adv_i2c_read(client, 0xE0, 0x72, &interrupt_st); + + /* If any of CABLE_DET_A_ST, V_LOCKED_A_ST and DE_REGEN_LCK_A_ST + * interrupts was set, get updated values of CABLE_DET_RAW, + * V_LOCKED_RAW and DE_REGEN_LCK_RAW + */ + if (interrupt_st) { + ret = adv_i2c_read(client, 0xE0, 0x71, &raw_value); + } + + /* Check CABLE_DET_A_ST interrupt */ + if ((interrupt_st & ADV7481_CABLE_DET_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x40); + + /* HDMI cable is connected */ + if (raw_value & ADV7481_CABLE_DET_A_ST) { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: HDMI cable connected\n", + __func__); + ret = adv_i2c_write(client, 0xE0, 0x10, 0xA1); + } else { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: HDMI cable disconnected\n", + __func__); + } + } + + /* Check V_LOCKED_A_ST interrupt */ + if ((interrupt_st & ADV7481_V_LOCKED_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x02); + /* Vertical sync filter has been locked, + * resolution height can be read + */ + if (raw_value & ADV7481_V_LOCKED_A_ST) { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: Vertical Sync Filter Locked\n", + __func__); + reg.dev_i2c_addr = 0x68; /* HDMI_RX_MAP; */ + reg.address = 0x09; + adv_i2c_read(client, 0x68, 0x09, &temp[0]); + adv_i2c_read(client, 0x68, 0x0A, &temp[1]); + adv_i2c_read(client, 0x68, 0x0B, &temp[2]); + + temp[0] = temp[0] & 0x1F; + adv7481_hdmi->hdmi_res_height = + (temp[0] << 8) + temp[1]; + if (temp[2] & 0x20) { + adv7481_hdmi->hdmi_res_height = + adv7481_hdmi->hdmi_res_height << 1; + adv7481_hdmi->hdmi_res_interlaced = 1; + } else { + adv7481_hdmi->hdmi_res_interlaced = 0; + } + + /* + * If resolution width was already read, + * notify user space about new resolution + */ + if (adv7481_hdmi->hdmi_res_width) { + sysfs_notify(&client->dev.kobj, NULL, + "hdmi_cable_connected"); + } + } else { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: Vertical Sync Filte Lost\n", + __func__); + adv7481_hdmi->hdmi_res_height = 0; + /* Notify user space about losing resolution */ + if (!adv7481_hdmi->hdmi_res_width) { + sysfs_notify(&client->dev.kobj, NULL, + "hdmi_cable_connected"); + } + } + } + + /* Check DE_REGEN_A_ST interrupt */ + if ((interrupt_st & ADV7481_DE_REGEN_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x01); + + /* DE regeneration has been locked, + * resolution height can be read + */ + if (raw_value & ADV7481_DE_REGEN_A_ST) { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: DE Regeneration Locked\n", + __func__); + reg.dev_i2c_addr = 0x68; /* HDMI_RX_MAP; */ + reg.address = 0x07; + adv_i2c_read(client, 0x68, 0x07, &temp[0]); + adv_i2c_read(client, 0x68, 0x08, &temp[1]); + + temp[0] = temp[0] & 0x1F; + adv7481_hdmi->hdmi_res_width = (temp[0] << 8) + temp[1]; + + /* If resolution height was already read back, + notify user space about new resolution */ + if (adv7481_hdmi->hdmi_res_height) { + sysfs_notify(&client->dev.kobj, NULL, + "hdmi_cable_connected"); + } + } else { + dev_dbg(&client->dev, + "%s: ADV7481 ISR: DE Regeneration Lost\n", + __func__); + adv7481_hdmi->hdmi_res_width = 0; + /* Notfiy user space about losing resolution */ + if (!adv7481_hdmi->hdmi_res_height) { + sysfs_notify(&client->dev.kobj, NULL, + "hdmi_cable_connected"); + } + } + } + return IRQ_HANDLED; +} + +static struct attribute *adv7481_attributes[] = { + &dev_attr_bstatus.attr, + &dev_attr_hdmi_cable_connected.attr, + &dev_attr_aksv.attr, + &dev_attr_reauthenticate.attr, + &dev_attr_bksv.attr, + &dev_attr_bcaps.attr, + NULL +}; + +static const struct attribute_group adv7481_attr_group = { + .attrs = adv7481_attributes, +}; + +int adv7481_sensor_init(struct i2c_client *client) +{ + struct crl_adv7481_hdmi *adv7481_hdmi; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + + adv7481_hdmi = devm_kzalloc(&client->dev, + sizeof(*adv7481_hdmi), GFP_KERNEL); + + if (!adv7481_hdmi) + return -ENOMEM; + + sensor->sensor_specific_data = adv7481_hdmi; + adv7481_hdmi->client = client; + mutex_init(&adv7481_hdmi->hot_plug_reset_lock); + INIT_DELAYED_WORK(&adv7481_hdmi->work, adv_hpa_assert); + dev_dbg(&client->dev, "%s ADV7481_sensor_init\n", __func__); + + return sysfs_create_group(&client->dev.kobj, &adv7481_attr_group); + +} + +int adv7481_sensor_cleanup(struct i2c_client *client) +{ + struct crl_adv7481_hdmi *adv7481_hdmi; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + + adv7481_hdmi = sensor->sensor_specific_data; + + /* + * This can be NULL if crlmodule_registered call failed before + * sensor_init call. + */ + if (!adv7481_hdmi) + return 0; + + dev_dbg(&client->dev, "%s: ADV7481_sensor_cleanup\n", __func__); + cancel_delayed_work_sync(&adv7481_hdmi->work); + + sysfs_remove_group(&client->dev.kobj, &adv7481_attr_group); + return 0; +} diff --git a/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.h b/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.h new file mode 100644 index 000000000000..1926bf4a168f --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_adv7481_hdmi_configuration.h @@ -0,0 +1,1025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" +irqreturn_t crl_adv7481_threaded_irq_fn(int irq, void *sensor_struct); + +struct crl_ctrl_data_pair hdmi_ctrl_data_lanes[] = { + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 4, + }, + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 2, + }, + { + .ctrl_id = V4L2_CID_MIPI_LANES, + .data = 1, + }, +}; + + +static struct crl_register_write_rep adv7481_hdmi_onetime_init_regset[] = { + {0xFF, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x01, CRL_REG_LEN_08BIT, 0x76, 0xE0}, /* ADI Required Write */ + {0x05, CRL_REG_LEN_08BIT, 0x96, 0xE0}, /* Setting Vid_Std to + 1600x1200(UXGA)@60 */ + {0xF2, CRL_REG_LEN_08BIT, 0x01, 0xE0}, /* Enable I2C Read + Auto-Increment */ + {0xF3, CRL_REG_LEN_08BIT, 0x4C, 0xE0}, /* DPLL Map Address + Set to 0x4C */ + {0xF4, CRL_REG_LEN_08BIT, 0x44, 0xE0}, /* CP Map Address + Set to 0x44 */ + {0xF5, CRL_REG_LEN_08BIT, 0x68, 0xE0}, /* HDMI RX Map Address + Set to 0x68 */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* EDID Map Address + Set to 0x6C */ + {0xF7, CRL_REG_LEN_08BIT, 0x64, 0xE0}, /* HDMI RX Repeater Map Address + Set to 0x64 */ + {0xF8, CRL_REG_LEN_08BIT, 0x62, 0xE0}, /* HDMI RX Infoframe Map Address + Set to 0x62 */ + {0xF9, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CBUS Map Address + Set to 0xF0 */ + {0xFA, CRL_REG_LEN_08BIT, 0x82, 0xE0}, /* CEC Map Address + Set to 0x82 */ + {0xFB, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* SDP Main Map Address + Set to 0xF2 */ + {0xFC, CRL_REG_LEN_08BIT, 0x90, 0xE0}, /* CSI-TXB Map Address + Set to 0x90 */ + {0xFD, CRL_REG_LEN_08BIT, 0x94, 0xE0}, /* CSI-TXA Map Address + Set to 0x94 */ + {0x00, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* Disable chip powerdown & + Enable HDMI Rx block */ + + {0x40, CRL_REG_LEN_08BIT, 0xC3, 0x64}, /* Enable HDCP 1.1 Repeater */ + {0x69, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* KSV List not ready port A */ + {0x77, CRL_REG_LEN_08BIT, 0x08, 0x64}, /* Clear KSV List */ + {0x78, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* KSV_LIST_READY_CLR_A: + Clears the BCAPS ready bit */ + {0x68, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Disable dual ksv list + for port A */ + {0x41, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Reset b-status (1) */ + {0x42, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Reset b-status (2) */ + {0x91, CRL_REG_LEN_08BIT, 0x08, 0xE0}, /* AKSV Update Clear */ + + {0x00, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* Foreground Channel = A */ + {0x98, CRL_REG_LEN_08BIT, 0xFF, 0x68}, /* ADI Required Write */ + {0x99, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI Required Write */ + {0x9A, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI Required Write */ + {0x9B, CRL_REG_LEN_08BIT, 0x0A, 0x68}, /* ADI Required Write */ + {0x9D, CRL_REG_LEN_08BIT, 0x40, 0x68}, /* ADI Required Write */ + {0xCB, CRL_REG_LEN_08BIT, 0x09, 0x68}, /* ADI Required Write */ + {0x3D, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI Required Write */ + {0x3E, CRL_REG_LEN_08BIT, 0x7B, 0x68}, /* ADI Required Write */ + {0x3F, CRL_REG_LEN_08BIT, 0x5E, 0x68}, /* ADI Required Write */ + {0x4E, CRL_REG_LEN_08BIT, 0xFE, 0x68}, /* ADI Required Write */ + {0x4F, CRL_REG_LEN_08BIT, 0x18, 0x68}, /* ADI Required Write */ + {0x57, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI Required Write */ + {0x58, CRL_REG_LEN_08BIT, 0x04, 0x68}, /* ADI Required Write */ + {0x85, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI Required Write */ + {0x83, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Enable All Terminatio ns */ + {0xA3, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* ADI Required Write */ + {0xBE, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI Required Write */ + {0x6C, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Manual Enable */ + {0xF8, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Asserted */ + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Audio Mute Speed + Set to Fastest (Smallest Step Size) */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS + TRISTATED */ + + {0x74, CRL_REG_LEN_08BIT, 0x43, 0xE0}, /* Enable interrupts */ + {0x75, CRL_REG_LEN_08BIT, 0x43, 0xE0}, + + {0x70, CRL_REG_LEN_08BIT, 0xA0, 0x64}, /* Write primary edid size */ + {0x74, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* Enable manual edid */ + {0x7A, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Write edid sram select */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* Write edid map bus address */ + + {0x00*4, CRL_REG_LEN_32BIT, 0x00FFFFFF, 0x6C}, /* EDID programming */ + {0x01*4, CRL_REG_LEN_32BIT, 0xFFFFFF00, 0x6C}, /* EDID programming */ + {0x02*4, CRL_REG_LEN_32BIT, 0x4DD90100, 0x6C}, /* EDID programming */ + {0x03*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x04*4, CRL_REG_LEN_32BIT, 0x00110103, 0x6C}, /* EDID programming */ + {0x05*4, CRL_REG_LEN_32BIT, 0x80000078, 0x6C}, /* EDID programming */ + {0x06*4, CRL_REG_LEN_32BIT, 0x0A0DC9A0, 0x6C}, /* EDID programming */ + {0x07*4, CRL_REG_LEN_32BIT, 0x57479827, 0x6C}, /* EDID programming */ + {0x08*4, CRL_REG_LEN_32BIT, 0x12484C00, 0x6C}, /* EDID programming */ + {0x09*4, CRL_REG_LEN_32BIT, 0x00000101, 0x6C}, /* EDID programming */ + {0x0A*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0B*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0C*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0D*4, CRL_REG_LEN_32BIT, 0x0101011D, 0x6C}, /* EDID programming */ + {0x0E*4, CRL_REG_LEN_32BIT, 0x80D0721C, 0x6C}, /* EDID programming */ + {0x0F*4, CRL_REG_LEN_32BIT, 0x1620102C, 0x6C}, /* EDID programming */ + {0x10*4, CRL_REG_LEN_32BIT, 0x2580C48E, 0x6C}, /* EDID programming */ + {0x11*4, CRL_REG_LEN_32BIT, 0x2100009E, 0x6C}, /* EDID programming */ + {0x12*4, CRL_REG_LEN_32BIT, 0x011D8018, 0x6C}, /* EDID programming */ + {0x13*4, CRL_REG_LEN_32BIT, 0x711C1620, 0x6C}, /* EDID programming */ + {0x14*4, CRL_REG_LEN_32BIT, 0x582C2500, 0x6C}, /* EDID programming */ + {0x15*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x16*4, CRL_REG_LEN_32BIT, 0x009E0000, 0x6C}, /* EDID programming */ + {0x17*4, CRL_REG_LEN_32BIT, 0x00FC0048, 0x6C}, /* EDID programming */ + {0x18*4, CRL_REG_LEN_32BIT, 0x444D4920, 0x6C}, /* EDID programming */ + {0x19*4, CRL_REG_LEN_32BIT, 0x4C4C430A, 0x6C}, /* EDID programming */ + {0x1A*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1B*4, CRL_REG_LEN_32BIT, 0x000000FD, 0x6C}, /* EDID programming */ + {0x1C*4, CRL_REG_LEN_32BIT, 0x003B3D0F, 0x6C}, /* EDID programming */ + {0x1D*4, CRL_REG_LEN_32BIT, 0x2D08000A, 0x6C}, /* EDID programming */ + {0x1E*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1F*4, CRL_REG_LEN_32BIT, 0x202001C1, 0x6C}, /* EDID programming */ + {0x20*4, CRL_REG_LEN_32BIT, 0x02031E77, 0x6C}, /* EDID programming */ + {0x21*4, CRL_REG_LEN_32BIT, 0x4F941305, 0x6C}, /* EDID programming */ + {0x22*4, CRL_REG_LEN_32BIT, 0x03040201, 0x6C}, /* EDID programming */ + {0x23*4, CRL_REG_LEN_32BIT, 0x16150706, 0x6C}, /* EDID programming */ + {0x24*4, CRL_REG_LEN_32BIT, 0x1110121F, 0x6C}, /* EDID programming */ + {0x25*4, CRL_REG_LEN_32BIT, 0x23090701, 0x6C}, /* EDID programming */ + {0x26*4, CRL_REG_LEN_32BIT, 0x65030C00, 0x6C}, /* EDID programming */ + {0x27*4, CRL_REG_LEN_32BIT, 0x10008C0A, 0x6C}, /* EDID programming */ + {0x28*4, CRL_REG_LEN_32BIT, 0xD0902040, 0x6C}, /* EDID programming */ + {0x29*4, CRL_REG_LEN_32BIT, 0x31200C40, 0x6C}, /* EDID programming */ + {0x2A*4, CRL_REG_LEN_32BIT, 0x5500138E, 0x6C}, /* EDID programming */ + {0x2B*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x2C*4, CRL_REG_LEN_32BIT, 0x011D00BC, 0x6C}, /* EDID programming */ + {0x2D*4, CRL_REG_LEN_32BIT, 0x52D01E20, 0x6C}, /* EDID programming */ + {0x2E*4, CRL_REG_LEN_32BIT, 0xB8285540, 0x6C}, /* EDID programming */ + {0x2F*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x30*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x31*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x32*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x33*4, CRL_REG_LEN_32BIT, 0x9600C48E, 0x6C}, /* EDID programming */ + {0x34*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x35*4, CRL_REG_LEN_32BIT, 0x011D0072, 0x6C}, /* EDID programming */ + {0x36*4, CRL_REG_LEN_32BIT, 0x51D01E20, 0x6C}, /* EDID programming */ + {0x37*4, CRL_REG_LEN_32BIT, 0x6E285500, 0x6C}, /* EDID programming */ + {0x38*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x39*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x3A*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x3B*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x3C*4, CRL_REG_LEN_32BIT, 0x9600138E, 0x6C}, /* EDID programming */ + {0x3D*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x3E*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x3F*4, CRL_REG_LEN_32BIT, 0x000000CB, 0x6C}, /* EDID programming */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_rgb565[] = { + {0x04, CRL_REG_LEN_08BIT, 0x02, 0xE0}, /* RGB Out of CP */ + {0x12, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CSC Depends on ip Packets - SDR 444 */ + {0x17, CRL_REG_LEN_08BIT, 0xB8, 0xE0}, /* Luma & Chroma Values Can Reach 254d */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, + /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + /* Enable 4-lane CSI TXB & Pixel Port */ + {0x7E, CRL_REG_LEN_08BIT, 0x98, 0x94}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_rgb888[] = { + {0x04, CRL_REG_LEN_08BIT, 0x02, 0xE0}, /* RGB Out of CP */ + {0x12, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CSC Depends on ip Packets - SDR 444 */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* Luma & Chroma Values Can Reach 254d */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, + /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0x7E, CRL_REG_LEN_08BIT, 0x1B, 0x94}, /* ADI Required Write */ +}; + + +static struct crl_register_write_rep adv7481_hdmi_mode_uyvy[] = { + {0x1C, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* ADI Require Write*/ + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* Luma & Chroma Values Can Reach 254d */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + /* Enable 4-lane CSI TXB & Pixel Port */ + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0x7E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_yuyv[] = { + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* Enable Interrupt*/ + {0x04, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* YCrCb output good=0xE0*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* Luma & Chroma Values Can Reach 254d */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x3E, CRL_REG_LEN_08BIT, 0x08, 0x44}, /* Invert order of Cb and Cr*/ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + /* Enable 4-lane CSI TXB & Pixel Port */ + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0x7E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_1080p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x5E, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 44 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_1080i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x54, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 44 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_480p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x4A, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_720p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x53, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 40 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD8, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD8, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_576p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x4B, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_576i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x94}, /* Enable 1-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x41, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_480i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x94}, /* Enable 1-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x40, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_vga[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x88, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_powerup_regset[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, /* ADI Required Write */ + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, /* ADI Required Write */ + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, /* ADI Required Write */ + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, /* ADI Required Write */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* i2c_dphy_pwdn - 1'b0 */ + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Required Write */ + {0x1E, CRL_REG_LEN_08BIT, 0xC0, 0x94}, + /* ADI Required Write, transmit only Frame Start/End packets */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, /* i2c_mipi_pll_en - 1'b1 */ +}; + +static struct crl_register_write_rep adv7481_hdmi_streamon_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x21, 0x94, 0xF8}, + /* Power-up CSI-TX */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_hdmi_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x94}, + /* i2c_mipi_pll_en -1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x94}, +}; + +static struct crl_pll_configuration adv7481_hdmi_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 297000000, + .bitsperpixel = 16, + .pixel_rate_csi = 594000000, + .pixel_rate_pa = 594000000, + }, + { + .input_clk = 24000000, + .op_sys_clk = 445500000, + .bitsperpixel = 24, + .pixel_rate_csi = 891000000, + .pixel_rate_pa = 891000000, + }, + /* 28.636 input clock */ + { + .input_clk = 286363636, + .op_sys_clk = 297000000, + .bitsperpixel = 16, + .pixel_rate_csi = 148500000, + .pixel_rate_pa = 297000000, + }, + { + .input_clk = 286363636, + .op_sys_clk = 297000000, + .bitsperpixel = 24, + .pixel_rate_csi = 148500000, + .pixel_rate_pa = 297000000, + }, + { + .input_clk = 286363636, + .op_sys_clk = 148500000, + .bitsperpixel = 16, + .pixel_rate_csi = 74250000, + .pixel_rate_pa = 148500000, + .csi_lanes = 4, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_480p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_hdmi_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_1080p_rects), + .sd_rects = adv7481_hdmi_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_1080p), + .mode_regs = adv7481_hdmi_mode_1080p, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_720p_rects), + .sd_rects = adv7481_hdmi_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_720p), + .mode_regs = adv7481_hdmi_mode_720p, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_VGA_rects), + .sd_rects = adv7481_hdmi_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_vga), + .mode_regs = adv7481_hdmi_mode_vga, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_1080i_rects), + .sd_rects = adv7481_hdmi_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_1080i), + .mode_regs = adv7481_hdmi_mode_1080i, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_480p_rects), + .sd_rects = adv7481_hdmi_480p_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 720, + .height = 480, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_480p), + .mode_regs = adv7481_hdmi_mode_480p, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_480i_rects), + .sd_rects = adv7481_hdmi_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_480i), + .mode_regs = adv7481_hdmi_mode_480i, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[2], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_576p_rects), + .sd_rects = adv7481_hdmi_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_576p), + .mode_regs = adv7481_hdmi_mode_576p, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[0], + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_576i_rects), + .sd_rects = adv7481_hdmi_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_576i), + .mode_regs = adv7481_hdmi_mode_576i, + .comp_items = 1, + .ctrl_data = &hdmi_ctrl_data_lanes[2], + }, +}; + +static struct crl_sensor_subdev_config adv7481_hdmi_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481-hdmi binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481-hdmi pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_hdmi_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_hdmi_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_rgb565), + .regs = adv7481_hdmi_mode_rgb565, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_uyvy), + .regs = adv7481_hdmi_mode_uyvy, + }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 24, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_rgb888), + .regs = adv7481_hdmi_mode_rgb888, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_yuyv), + .regs = adv7481_hdmi_mode_yuyv, + }, +}; + +static const char * const adv7481_hdmi_test_pattern_menu[] = { + "default", + "30fps", + "50fps", + "60fps", + "real", +}; + +static struct crl_register_write_rep adv7481_hdmi_test_pattern_default_mode[] = { + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, + {0x37, CRL_REG_LEN_08BIT, 0x81, 0x44}, + {0x00, CRL_REG_LEN_08BIT, 0x00, 0xE0}, +}; + +static struct crl_register_write_rep adv7481_hdmi_test_pattern_30fps_mode[] = { + {0x00, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x03, CRL_REG_LEN_08BIT, 0xA6, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x80, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x85, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_test_pattern_50fps_mode[] = { + {0x00, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x03, CRL_REG_LEN_08BIT, 0x96, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x80, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x85, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_test_pattern_60fps_mode[] = { + {0x00, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x80, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x85, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_real_mode[] = { + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_dep_reg_list adv7481_hdmi_test_pattern_fps_types_regs[] = { + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 0 }, + ARRAY_SIZE(adv7481_hdmi_test_pattern_default_mode), + adv7481_hdmi_test_pattern_default_mode, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 1 }, + ARRAY_SIZE(adv7481_hdmi_test_pattern_30fps_mode), + adv7481_hdmi_test_pattern_30fps_mode, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 2 }, + ARRAY_SIZE(adv7481_hdmi_test_pattern_50fps_mode), + adv7481_hdmi_test_pattern_50fps_mode, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 3 }, + ARRAY_SIZE(adv7481_hdmi_test_pattern_60fps_mode), + adv7481_hdmi_test_pattern_60fps_mode, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 4 }, + ARRAY_SIZE(adv7481_hdmi_real_mode), + adv7481_hdmi_real_mode, 0, 0 }, +}; + +static struct crl_v4l2_ctrl adv7481_hdmi_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_MIPI_LANES, + .name = "V4L2_CID_MIPI_LANES", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = 4, + .data.std_data.step = 1, + .data.std_data.def = 4, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = adv7481_hdmi_test_pattern_menu, + .data.v4l2_menu_items.size = ARRAY_SIZE(adv7481_hdmi_test_pattern_menu), + .impact = CRL_IMPACTS_NO_IMPACT, + .flags = V4L2_CTRL_FLAG_UPDATE, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .crl_ctrl_dep_reg_list = ARRAY_SIZE(adv7481_hdmi_test_pattern_fps_types_regs), + .dep_regs = adv7481_hdmi_test_pattern_fps_types_regs, + }, +}; + +int adv7481_sensor_init(struct i2c_client *); +int adv7481_sensor_cleanup(struct i2c_client *); + +static struct crl_sensor_configuration adv7481_hdmi_crl_configuration = { + + .sensor_init = adv7481_sensor_init, + .sensor_cleanup = adv7481_sensor_cleanup, + + .onetime_init_regs_items = + ARRAY_SIZE(adv7481_hdmi_onetime_init_regset), + .onetime_init_regs = adv7481_hdmi_onetime_init_regset, + + .powerup_regs_items = ARRAY_SIZE(adv7481_hdmi_powerup_regset), + .powerup_regs = adv7481_hdmi_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_hdmi_streamoff_regs), + .poweroff_regs = adv7481_hdmi_streamoff_regs, + + .subdev_items = ARRAY_SIZE(adv7481_hdmi_sensor_subdevs), + .subdevs = adv7481_hdmi_sensor_subdevs, + + .sensor_limits = &adv7481_hdmi_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_hdmi_pll_configurations), + .pll_configs = adv7481_hdmi_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_hdmi_modes), + .modes = adv7481_hdmi_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_hdmi_streamon_regs), + .streamon_regs = adv7481_hdmi_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_hdmi_streamoff_regs), + .streamoff_regs = adv7481_hdmi_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(adv7481_hdmi_v4l2_ctrls), + .v4l2_ctrl_bank = adv7481_hdmi_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_hdmi_crl_csi_data_fmt), + .csi_fmts = adv7481_hdmi_crl_csi_data_fmt, + + .irq_in_use = true, + .crl_irq_fn = NULL, + .crl_threaded_irq_fn = crl_adv7481_threaded_irq_fn, + + .addr_len = CRL_ADDR_7BIT, + .i2c_mutex_in_use = true, +}; + +#endif /* __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ar0231at_configuration.h b/drivers/media/i2c/crlmodule/crl_ar0231at_configuration.h new file mode 100644 index 000000000000..1905a9e05192 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ar0231at_configuration.h @@ -0,0 +1,2409 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __CRLMODULE_AR0231AT_CONFIGURATION_H_ +#define __CRLMODULE_AR0231AT_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +struct crl_pll_configuration ar0231at_pll_configurations[] = { + { + .input_clk = 27000000, + .op_sys_clk = 87750000, + .bitsperpixel = 12, + .pixel_rate_csi = 176000000, + .pixel_rate_pa = 176000000, /* pixel_rate = op_sys_clk*2 *csi_lanes/bitsperpixel */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, + { + .input_clk = 27000000, + .op_sys_clk = 87750000, + .bitsperpixel = 10, + .pixel_rate_csi = 211200000, + .pixel_rate_pa = 211200000, /* pixel_rate = op_sys_clk*2 *csi_lanes/bitsperpixel */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, +}; + +struct crl_sensor_subdev_config ar0231at_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ar0231at binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ar0231at pixel array", + }, +}; + +struct crl_subdev_rect_rep ar0231at_1920_1088_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + } +}; + +/* + * Exposure mode: + * 0: Linear mode + * 1: 2-HDR mode + * 2: 3-HDR mode + * 3: 4-HDR mode + */ +struct crl_ctrl_data_pair ar0231at_ctrl_data_modes[] = { + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 0, + }, + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 1, + }, + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 2, + }, + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 3, + }, + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 4, + }, +}; + +static struct crl_register_write_rep ar0231at_1920_1088_10bit_linear_mode[] = { + { 0x301A, CRL_REG_LEN_16BIT, 0x1058, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x3092, CRL_REG_LEN_16BIT, 0x0C24, 0x10 }, + { 0x337A, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3520, CRL_REG_LEN_16BIT, 0x1288, 0x10 }, + { 0x3522, CRL_REG_LEN_16BIT, 0x880C, 0x10 }, + { 0x3524, CRL_REG_LEN_16BIT, 0x0C12, 0x10 }, + { 0x352C, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x354A, CRL_REG_LEN_16BIT, 0x007F, 0x10 }, + { 0x350C, CRL_REG_LEN_16BIT, 0x055C, 0x10 }, + { 0x3506, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3508, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3100, CRL_REG_LEN_16BIT, 0x4000, 0x10 }, + { 0x3280, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3282, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3284, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3286, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3288, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3290, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3292, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3294, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3296, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3298, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x2512, CRL_REG_LEN_16BIT, 0x8000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3350, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1578, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7B24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xEA24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1022, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2410, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x155A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24EA, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2324, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x647A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2404, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x052C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x400A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3851, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0801, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0408, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1180, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2652, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1518, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0906, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1348, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1002, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1016, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1181, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1189, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0D09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1413, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2B15, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0311, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1409, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0110, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDD11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x9B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0F11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1A12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xE609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x290B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0904, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0923, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x092C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1388, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C14, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1112, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBF11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB10, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xB812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xA012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3053, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8111, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x010D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0815, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1313, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0515, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0213, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0411, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0814, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD908, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x091A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0903, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10D6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11D9, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0917, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0913, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x121A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1250, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1076, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10E6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1240, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0925, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13AD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0902, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0907, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x138D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0914, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B13, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1C0C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0920, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1262, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1066, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x090A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x093B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1263, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1508, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11B8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x12A0, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1200, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1026, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1300, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1100, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x437A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B05, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0708, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4137, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x502C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2CFE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15FE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C2C, 0x10 }, + { 0x32E6, CRL_REG_LEN_16BIT, 0x00E0, 0x10 }, + { 0x1008, CRL_REG_LEN_16BIT, 0x036F, 0x10 }, + { 0x100C, CRL_REG_LEN_16BIT, 0x058F, 0x10 }, + { 0x100E, CRL_REG_LEN_16BIT, 0x07AF, 0x10 }, + { 0x1010, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x3230, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x3232, CRL_REG_LEN_16BIT, 0x0532, 0x10 }, + { 0x3234, CRL_REG_LEN_16BIT, 0x0752, 0x10 }, + { 0x3236, CRL_REG_LEN_16BIT, 0x00F2, 0x10 }, + { 0x3566, CRL_REG_LEN_16BIT, 0x3328, 0x10 }, + { 0x32D0, CRL_REG_LEN_16BIT, 0x3A02, 0x10 }, + { 0x32D2, CRL_REG_LEN_16BIT, 0x3508, 0x10 }, + { 0x32D4, CRL_REG_LEN_16BIT, 0x3702, 0x10 }, + { 0x32D6, CRL_REG_LEN_16BIT, 0x3C04, 0x10 }, + { 0x32DC, CRL_REG_LEN_16BIT, 0x370A, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x302A, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x302C, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x302E, CRL_REG_LEN_16BIT, 0x0003, 0x10 }, + { 0x3030, CRL_REG_LEN_16BIT, 0x004E, 0x10 }, + { 0x3036, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3038, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x30A2, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30A6, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3180, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E4, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3004, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x3008, CRL_REG_LEN_16BIT, 0x0783, 0x10 }, + { 0x3002, CRL_REG_LEN_16BIT, 0x003C, 0x10 }, + { 0x3006, CRL_REG_LEN_16BIT, 0x047B, 0x10 }, + { 0x3032, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0F10, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x0970, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F1, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F0, 0x10 }, + { 0x300C, CRL_REG_LEN_16BIT, 0x0872, 0x10 }, + { 0x300A, CRL_REG_LEN_16BIT, 0x054A, 0x10 }, + { 0x3042, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3012, CRL_REG_LEN_16BIT, 0x0163, 0x10 }, + { 0x3014, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C08, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x31D0, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AC, CRL_REG_LEN_16BIT, 0x0C0A, 0x10 }, + /* try sync mode */ + { 0x340A, CRL_REG_LEN_16BIT, 0x0077, 0x10 }, + { 0x340C, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x30CE, CRL_REG_LEN_16BIT, 0x0120, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x19DC, 0x10 }, + { 0x3370, CRL_REG_LEN_16BIT, 0x0231, 0x10 }, +}; + +static struct crl_register_write_rep ar0231at_1920_1088_linear_mode[] = { + { 0x301A, CRL_REG_LEN_16BIT, 0x1058, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x3092, CRL_REG_LEN_16BIT, 0x0C24, 0x10 }, + { 0x337A, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3520, CRL_REG_LEN_16BIT, 0x1288, 0x10 }, + { 0x3522, CRL_REG_LEN_16BIT, 0x880C, 0x10 }, + { 0x3524, CRL_REG_LEN_16BIT, 0x0C12, 0x10 }, + { 0x352C, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x354A, CRL_REG_LEN_16BIT, 0x007F, 0x10 }, + { 0x350C, CRL_REG_LEN_16BIT, 0x055C, 0x10 }, + { 0x3506, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3508, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3100, CRL_REG_LEN_16BIT, 0x4000, 0x10 }, + { 0x3280, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3282, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3284, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3286, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3288, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3290, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3292, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3294, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3296, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3298, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x2512, CRL_REG_LEN_16BIT, 0x8000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3350, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1578, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7B24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xEA24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1022, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2410, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x155A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24EA, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2324, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x647A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2404, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x052C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x400A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3851, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0801, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0408, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1180, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2652, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1518, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0906, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1348, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1002, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1016, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1181, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1189, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0D09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1413, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2B15, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0311, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1409, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0110, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDD11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x9B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0F11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1A12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xE609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x290B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0904, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0923, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x092C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1388, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C14, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1112, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBF11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB10, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xB812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xA012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3053, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8111, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x010D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0815, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1313, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0515, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0213, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0411, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0814, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD908, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x091A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0903, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10D6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11D9, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0917, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0913, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x121A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1250, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1076, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10E6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1240, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0925, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13AD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0902, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0907, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x138D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0914, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B13, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1C0C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0920, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1262, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1066, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x090A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x093B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1263, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1508, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11B8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x12A0, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1200, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1026, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1300, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1100, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x437A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B05, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0708, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4137, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x502C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2CFE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15FE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C2C, 0x10 }, + { 0x32E6, CRL_REG_LEN_16BIT, 0x00E0, 0x10 }, + { 0x1008, CRL_REG_LEN_16BIT, 0x036F, 0x10 }, + { 0x100C, CRL_REG_LEN_16BIT, 0x058F, 0x10 }, + { 0x100E, CRL_REG_LEN_16BIT, 0x07AF, 0x10 }, + { 0x1010, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x3230, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x3232, CRL_REG_LEN_16BIT, 0x0532, 0x10 }, + { 0x3234, CRL_REG_LEN_16BIT, 0x0752, 0x10 }, + { 0x3236, CRL_REG_LEN_16BIT, 0x00F2, 0x10 }, + { 0x3566, CRL_REG_LEN_16BIT, 0x3328, 0x10 }, + { 0x32D0, CRL_REG_LEN_16BIT, 0x3A02, 0x10 }, + { 0x32D2, CRL_REG_LEN_16BIT, 0x3508, 0x10 }, + { 0x32D4, CRL_REG_LEN_16BIT, 0x3702, 0x10 }, + { 0x32D6, CRL_REG_LEN_16BIT, 0x3C04, 0x10 }, + { 0x32DC, CRL_REG_LEN_16BIT, 0x370A, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x302A, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x302C, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x302E, CRL_REG_LEN_16BIT, 0x0003, 0x10 }, + { 0x3030, CRL_REG_LEN_16BIT, 0x004E, 0x10 }, + { 0x3036, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3038, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x30A2, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30A6, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3180, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E4, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3004, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x3008, CRL_REG_LEN_16BIT, 0x0783, 0x10 }, + { 0x3002, CRL_REG_LEN_16BIT, 0x003C, 0x10 }, + { 0x3006, CRL_REG_LEN_16BIT, 0x047B, 0x10 }, + { 0x3032, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0F10, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x0970, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F1, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F0, 0x10 }, + { 0x300C, CRL_REG_LEN_16BIT, 0x0872, 0x10 }, + { 0x300A, CRL_REG_LEN_16BIT, 0x054A, 0x10 }, + { 0x3042, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3012, CRL_REG_LEN_16BIT, 0x0163, 0x10 }, + { 0x3014, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C08, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x31D0, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AC, CRL_REG_LEN_16BIT, 0x0C0C, 0x10 }, + /* try sync mode */ + { 0x340A, CRL_REG_LEN_16BIT, 0x0077, 0x10 }, + { 0x340C, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x30CE, CRL_REG_LEN_16BIT, 0x0120, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x19DC, 0x10 }, + { 0x3370, CRL_REG_LEN_16BIT, 0x0231, 0x10 }, +}; + +static struct crl_register_write_rep ar0231at_1920_1088_2hdr_mode[] = { + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 100, 0x10 }, + { 0x3092, CRL_REG_LEN_16BIT, 0x0C24, 0x10 }, + { 0x337A, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3520, CRL_REG_LEN_16BIT, 0x1288, 0x10 }, + { 0x3522, CRL_REG_LEN_16BIT, 0x880C, 0x10 }, + { 0x3524, CRL_REG_LEN_16BIT, 0x0C12, 0x10 }, + { 0x352C, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x354A, CRL_REG_LEN_16BIT, 0x007F, 0x10 }, + { 0x350C, CRL_REG_LEN_16BIT, 0x055C, 0x10 }, + { 0x3506, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3508, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3100, CRL_REG_LEN_16BIT, 0x4000, 0x10 }, + { 0x3280, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3282, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3284, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3286, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3288, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3290, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3292, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3294, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3296, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3298, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x2512, CRL_REG_LEN_16BIT, 0x8000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3350, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1578, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7B24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xEA24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1022, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2410, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x155A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24EA, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2324, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x647A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2404, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x052C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x400A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3851, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0801, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0408, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1180, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2652, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1518, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0906, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1348, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1002, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1016, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1181, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1189, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0D09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1413, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2B15, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0311, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1409, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0110, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDD11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x9B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0F11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1A12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xE609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x290B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0904, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0923, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x092C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1388, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C14, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1112, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBF11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB10, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xB812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xA012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3053, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8111, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x010D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0815, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1313, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0515, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0213, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0411, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0814, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD908, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x091A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0903, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10D6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11D9, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0917, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0913, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x121A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1250, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1076, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10E6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1240, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0925, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13AD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0902, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0907, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x138D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0914, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B13, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1C0C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0920, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1262, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1066, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x090A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x093B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1263, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1508, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11B8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x12A0, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1200, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1026, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1300, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1100, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x437A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B05, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0708, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4137, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x502C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2CFE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15FE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C2C, 0x10 }, + { 0x32E6, CRL_REG_LEN_16BIT, 0x00E0, 0x10 }, + { 0x1008, CRL_REG_LEN_16BIT, 0x036F, 0x10 }, + { 0x100C, CRL_REG_LEN_16BIT, 0x058F, 0x10 }, + { 0x100E, CRL_REG_LEN_16BIT, 0x07AF, 0x10 }, + { 0x1010, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x3230, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x3232, CRL_REG_LEN_16BIT, 0x0532, 0x10 }, + { 0x3234, CRL_REG_LEN_16BIT, 0x0752, 0x10 }, + { 0x3236, CRL_REG_LEN_16BIT, 0x00F2, 0x10 }, + { 0x3566, CRL_REG_LEN_16BIT, 0x3328, 0x10 }, + { 0x32D0, CRL_REG_LEN_16BIT, 0x3A02, 0x10 }, + { 0x32D2, CRL_REG_LEN_16BIT, 0x3508, 0x10 }, + { 0x32D4, CRL_REG_LEN_16BIT, 0x3702, 0x10 }, + { 0x32D6, CRL_REG_LEN_16BIT, 0x3C04, 0x10 }, + { 0x32DC, CRL_REG_LEN_16BIT, 0x370A, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x302A, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x302C, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x302E, CRL_REG_LEN_16BIT, 0x0003, 0x10 }, + { 0x3030, CRL_REG_LEN_16BIT, 0x004E, 0x10 }, + { 0x3036, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3038, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x30A2, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30A6, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F1, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x33C0, CRL_REG_LEN_16BIT, 0x2000, 0x10 }, + { 0x33C2, CRL_REG_LEN_16BIT, 0x3440, 0x10 }, + { 0x33C4, CRL_REG_LEN_16BIT, 0x4890, 0x10 }, + { 0x33C6, CRL_REG_LEN_16BIT, 0x5CE0, 0x10 }, + { 0x33C8, CRL_REG_LEN_16BIT, 0x7140, 0x10 }, + { 0x33CA, CRL_REG_LEN_16BIT, 0x8590, 0x10 }, + { 0x33CC, CRL_REG_LEN_16BIT, 0x99E0, 0x10 }, + { 0x33CE, CRL_REG_LEN_16BIT, 0xAE40, 0x10 }, + { 0x33D0, CRL_REG_LEN_16BIT, 0xC290, 0x10 }, + { 0x33D2, CRL_REG_LEN_16BIT, 0xD6F0, 0x10 }, + { 0x33D4, CRL_REG_LEN_16BIT, 0xEB40, 0x10 }, + { 0x33D6, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33DA, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3180, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E4, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3004, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x3008, CRL_REG_LEN_16BIT, 0x0783, 0x10 }, + { 0x3002, CRL_REG_LEN_16BIT, 0x003C, 0x10 }, + { 0x3006, CRL_REG_LEN_16BIT, 0x047B, 0x10 }, + { 0x3032, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0788, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0F10, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x04B8, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x0970, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F1, 0x10 }, + { 0x300C, CRL_REG_LEN_16BIT, 0x0872, 0x10 }, + { 0x300A, CRL_REG_LEN_16BIT, 0x054A, 0x10 }, + { 0x3042, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3012, CRL_REG_LEN_16BIT, 0x0163, 0x10 }, + { 0x3014, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x321E, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x3222, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x31D0, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0201, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AC, CRL_REG_LEN_16BIT, 0x140C, 0x10 }, + { 0x340A, CRL_REG_LEN_16BIT, 0x0077, 0x10 }, + { 0x340C, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x30CE, CRL_REG_LEN_16BIT, 0x0120, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x19DC, 0x10 }, + { 0x3370, CRL_REG_LEN_16BIT, 0x0231, 0x10 }, +}; + +static struct crl_register_write_rep ar0231at_1920_1088_3hdr_mode[] = { + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 100, 0x10 }, + { 0x3092, CRL_REG_LEN_16BIT, 0x0C24, 0x10 }, + { 0x337A, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3520, CRL_REG_LEN_16BIT, 0x1288, 0x10 }, + { 0x3522, CRL_REG_LEN_16BIT, 0x880C, 0x10 }, + { 0x3524, CRL_REG_LEN_16BIT, 0x0C12, 0x10 }, + { 0x352C, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x354A, CRL_REG_LEN_16BIT, 0x007F, 0x10 }, + { 0x350C, CRL_REG_LEN_16BIT, 0x055C, 0x10 }, + { 0x3506, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3508, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3100, CRL_REG_LEN_16BIT, 0x4000, 0x10 }, + { 0x3280, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3282, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3284, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3286, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3288, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3290, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3292, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3294, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3296, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3298, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x2512, CRL_REG_LEN_16BIT, 0x8000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3350, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1578, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7B24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xEA24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1022, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2410, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x155A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24EA, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2324, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x647A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2404, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x052C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x400A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3851, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0801, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0408, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1180, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2652, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1518, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0906, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1348, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1002, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1016, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1181, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1189, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0D09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1413, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2B15, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0311, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1409, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0110, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDD11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x9B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0F11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1A12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xE609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x290B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0904, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0923, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x092C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1388, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C14, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1112, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBF11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB10, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xB812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xA012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3053, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8111, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x010D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0815, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1313, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0515, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0213, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0411, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0814, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD908, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x091A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0903, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10D6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11D9, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0917, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0913, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x121A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1250, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1076, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10E6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1240, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0925, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13AD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0902, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0907, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x138D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0914, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B13, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1C0C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0920, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1262, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1066, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x090A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x093B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1263, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1508, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11B8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x12A0, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1200, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1026, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1300, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1100, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x437A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B05, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0708, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4137, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x502C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2CFE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15FE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C2C, 0x10 }, + { 0x32E6, CRL_REG_LEN_16BIT, 0x00E0, 0x10 }, + { 0x1008, CRL_REG_LEN_16BIT, 0x036F, 0x10 }, + { 0x100C, CRL_REG_LEN_16BIT, 0x058F, 0x10 }, + { 0x100E, CRL_REG_LEN_16BIT, 0x07AF, 0x10 }, + { 0x1010, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x3230, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x3232, CRL_REG_LEN_16BIT, 0x0532, 0x10 }, + { 0x3234, CRL_REG_LEN_16BIT, 0x0752, 0x10 }, + { 0x3236, CRL_REG_LEN_16BIT, 0x00F2, 0x10 }, + { 0x3566, CRL_REG_LEN_16BIT, 0x3328, 0x10 }, + { 0x32D0, CRL_REG_LEN_16BIT, 0x3A02, 0x10 }, + { 0x32D2, CRL_REG_LEN_16BIT, 0x3508, 0x10 }, + { 0x32D4, CRL_REG_LEN_16BIT, 0x3702, 0x10 }, + { 0x32D6, CRL_REG_LEN_16BIT, 0x3C04, 0x10 }, + { 0x32DC, CRL_REG_LEN_16BIT, 0x370A, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x302A, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x302C, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x302E, CRL_REG_LEN_16BIT, 0x0003, 0x10 }, + { 0x3030, CRL_REG_LEN_16BIT, 0x004E, 0x10 }, + { 0x3036, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3038, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x30A2, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30A6, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x33C0, CRL_REG_LEN_16BIT, 0x2000, 0x10 }, + { 0x33C2, CRL_REG_LEN_16BIT, 0x3440, 0x10 }, + { 0x33C4, CRL_REG_LEN_16BIT, 0x4890, 0x10 }, + { 0x33C6, CRL_REG_LEN_16BIT, 0x5CE0, 0x10 }, + { 0x33C8, CRL_REG_LEN_16BIT, 0x7140, 0x10 }, + { 0x33CA, CRL_REG_LEN_16BIT, 0x8590, 0x10 }, + { 0x33CC, CRL_REG_LEN_16BIT, 0x99E0, 0x10 }, + { 0x33CE, CRL_REG_LEN_16BIT, 0xAE40, 0x10 }, + { 0x33D0, CRL_REG_LEN_16BIT, 0xC290, 0x10 }, + { 0x33D2, CRL_REG_LEN_16BIT, 0xD6F0, 0x10 }, + { 0x33D4, CRL_REG_LEN_16BIT, 0xEB40, 0x10 }, + { 0x33D6, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33DA, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3180, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E4, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3004, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x3008, CRL_REG_LEN_16BIT, 0x0783, 0x10 }, + { 0x3002, CRL_REG_LEN_16BIT, 0x003C, 0x10 }, + { 0x3006, CRL_REG_LEN_16BIT, 0x047B, 0x10 }, + { 0x3032, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0788, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0F10, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x04B8, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x0970, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x300C, CRL_REG_LEN_16BIT, 0x0872, 0x10 }, + { 0x300A, CRL_REG_LEN_16BIT, 0x054A, 0x10 }, + { 0x3042, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3012, CRL_REG_LEN_16BIT, 0x0163, 0x10 }, + { 0x3014, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x321E, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x3222, CRL_REG_LEN_16BIT, 0x0882, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x31D0, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0201, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AC, CRL_REG_LEN_16BIT, 0x140C, 0x10 }, + { 0x340A, CRL_REG_LEN_16BIT, 0x0077, 0x10 }, + { 0x340C, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x30CE, CRL_REG_LEN_16BIT, 0x0120, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x19DC, 0x10 }, + { 0x3370, CRL_REG_LEN_16BIT, 0x0231, 0x10 }, +}; + +static struct crl_register_write_rep ar0231at_1920_1088_4hdr_mode[] = { + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 100, 0x10 }, + { 0x3092, CRL_REG_LEN_16BIT, 0x0C24, 0x10 }, + { 0x337A, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3520, CRL_REG_LEN_16BIT, 0x1288, 0x10 }, + { 0x3522, CRL_REG_LEN_16BIT, 0x880C, 0x10 }, + { 0x3524, CRL_REG_LEN_16BIT, 0x0C12, 0x10 }, + { 0x352C, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x354A, CRL_REG_LEN_16BIT, 0x007F, 0x10 }, + { 0x350C, CRL_REG_LEN_16BIT, 0x055C, 0x10 }, + { 0x3506, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3508, CRL_REG_LEN_16BIT, 0x3333, 0x10 }, + { 0x3100, CRL_REG_LEN_16BIT, 0x4000, 0x10 }, + { 0x3280, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3282, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3284, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3286, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3288, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x328E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3290, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3292, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3294, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3296, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x3298, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329A, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329C, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x329E, CRL_REG_LEN_16BIT, 0x0FA0, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x10D8, 0x10 }, + { 0x0000, CRL_REG_LEN_DELAY, 200, 0x10 }, + { 0x2512, CRL_REG_LEN_16BIT, 0x8000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3350, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1578, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7B24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xEA24, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1022, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2410, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x155A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24FF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x24EA, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2324, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x647A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2404, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x052C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x400A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFF0A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3851, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0801, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0408, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1180, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2652, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1518, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0906, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1348, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1002, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1016, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1181, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1189, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0D09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1413, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2B15, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0311, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1409, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0110, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDD11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xDB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x9B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0F11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1A12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x7610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xE609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x290B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0904, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0923, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13C8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x092C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1388, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C14, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1112, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBF11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB10, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xFB09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3511, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xBB12, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6312, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x6014, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xB812, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xA012, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2610, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0011, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x3053, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4010, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1611, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8111, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8910, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5612, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x010D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0815, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD013, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x5009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1313, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0215, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC015, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0515, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8813, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0213, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0411, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xC909, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0814, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0109, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B11, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0xD908, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x091A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1440, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0903, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1214, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10D6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1212, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11D9, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1056, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0917, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11DB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0913, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0905, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x121A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1210, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1460, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1250, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1076, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x10E6, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13A8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1240, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0925, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x13AD, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0902, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0907, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1588, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0901, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x138D, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B09, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0914, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4009, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B13, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x8809, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1C0C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0920, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1262, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BF, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1066, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x090A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11FB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x093B, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11BB, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1263, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1260, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1400, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1508, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x11B8, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x12A0, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1200, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1026, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1000, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1300, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x1100, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x437A, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0609, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0B05, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0708, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x4137, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x502C, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x2CFE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x15FE, 0x10 }, + { 0x2510, CRL_REG_LEN_16BIT, 0x0C2C, 0x10 }, + { 0x32E6, CRL_REG_LEN_16BIT, 0x00E0, 0x10 }, + { 0x1008, CRL_REG_LEN_16BIT, 0x036F, 0x10 }, + { 0x100C, CRL_REG_LEN_16BIT, 0x058F, 0x10 }, + { 0x100E, CRL_REG_LEN_16BIT, 0x07AF, 0x10 }, + { 0x1010, CRL_REG_LEN_16BIT, 0x014F, 0x10 }, + { 0x3230, CRL_REG_LEN_16BIT, 0x0312, 0x10 }, + { 0x3232, CRL_REG_LEN_16BIT, 0x0532, 0x10 }, + { 0x3234, CRL_REG_LEN_16BIT, 0x0752, 0x10 }, + { 0x3236, CRL_REG_LEN_16BIT, 0x00F2, 0x10 }, + { 0x3566, CRL_REG_LEN_16BIT, 0x3328, 0x10 }, + { 0x32D0, CRL_REG_LEN_16BIT, 0x3A02, 0x10 }, + { 0x32D2, CRL_REG_LEN_16BIT, 0x3508, 0x10 }, + { 0x32D4, CRL_REG_LEN_16BIT, 0x3702, 0x10 }, + { 0x32D6, CRL_REG_LEN_16BIT, 0x3C04, 0x10 }, + { 0x32DC, CRL_REG_LEN_16BIT, 0x370A, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x302A, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x302C, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x302E, CRL_REG_LEN_16BIT, 0x0003, 0x10 }, + { 0x3030, CRL_REG_LEN_16BIT, 0x004E, 0x10 }, + { 0x3036, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3038, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x30A2, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x30A6, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3040, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x0008, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F2, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3044, CRL_REG_LEN_16BIT, 0x0400, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x3064, CRL_REG_LEN_16BIT, 0x1802, 0x10 }, + { 0x33C0, CRL_REG_LEN_16BIT, 0x2000, 0x10 }, + { 0x33C2, CRL_REG_LEN_16BIT, 0x3440, 0x10 }, + { 0x33C4, CRL_REG_LEN_16BIT, 0x4890, 0x10 }, + { 0x33C6, CRL_REG_LEN_16BIT, 0x5CE0, 0x10 }, + { 0x33C8, CRL_REG_LEN_16BIT, 0x7140, 0x10 }, + { 0x33CA, CRL_REG_LEN_16BIT, 0x8590, 0x10 }, + { 0x33CC, CRL_REG_LEN_16BIT, 0x99E0, 0x10 }, + { 0x33CE, CRL_REG_LEN_16BIT, 0xAE40, 0x10 }, + { 0x33D0, CRL_REG_LEN_16BIT, 0xC290, 0x10 }, + { 0x33D2, CRL_REG_LEN_16BIT, 0xD6F0, 0x10 }, + { 0x33D4, CRL_REG_LEN_16BIT, 0xEB40, 0x10 }, + { 0x33D6, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33DA, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3180, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E4, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x33E0, CRL_REG_LEN_16BIT, 0x0C80, 0x10 }, + { 0x3004, CRL_REG_LEN_16BIT, 0x0004, 0x10 }, + { 0x3008, CRL_REG_LEN_16BIT, 0x0783, 0x10 }, + { 0x3002, CRL_REG_LEN_16BIT, 0x003C, 0x10 }, + { 0x3006, CRL_REG_LEN_16BIT, 0x047B, 0x10 }, + { 0x3032, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0010, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0788, 0x10 }, + { 0x3402, CRL_REG_LEN_16BIT, 0x0F10, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x04B8, 0x10 }, + { 0x3404, CRL_REG_LEN_16BIT, 0x0970, 0x10 }, + { 0x3082, CRL_REG_LEN_16BIT, 0x000C, 0x10 }, + { 0x30BA, CRL_REG_LEN_16BIT, 0x11F3, 0x10 }, + { 0x300C, CRL_REG_LEN_16BIT, 0x09B8, 0x10 }, + { 0x300A, CRL_REG_LEN_16BIT, 0x0498, 0x10 }, + { 0x3042, CRL_REG_LEN_16BIT, 0x0000, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3238, CRL_REG_LEN_16BIT, 0x0222, 0x10 }, + { 0x3012, CRL_REG_LEN_16BIT, 0x0131, 0x10 }, + { 0x3014, CRL_REG_LEN_16BIT, 0x098E, 0x10 }, + { 0x321E, CRL_REG_LEN_16BIT, 0x098E, 0x10 }, + { 0x3222, CRL_REG_LEN_16BIT, 0x098E, 0x10 }, + { 0x3226, CRL_REG_LEN_16BIT, 0x098E, 0x10 }, + { 0x30B0, CRL_REG_LEN_16BIT, 0x0800, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EA, CRL_REG_LEN_16BIT, 0x3C0E, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x32EC, CRL_REG_LEN_16BIT, 0x72A1, 0x10 }, + { 0x31D0, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0201, 0x10 }, + { 0x31AE, CRL_REG_LEN_16BIT, 0x0001, 0x10 }, + { 0x31AC, CRL_REG_LEN_16BIT, 0x140C, 0x10 }, + { 0x340A, CRL_REG_LEN_16BIT, 0x0077, 0x10 }, + { 0x340C, CRL_REG_LEN_16BIT, 0x0080, 0x10 }, + { 0x30CE, CRL_REG_LEN_16BIT, 0x0120, 0x10 }, + { 0x301A, CRL_REG_LEN_16BIT, 0x19DC, 0x10 }, + { 0x3370, CRL_REG_LEN_16BIT, 0x0231, 0x10 }, +}; + +struct crl_mode_rep ar0231at_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ar0231at_1920_1088_rects), + .sd_rects = ar0231at_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 2162, + .min_fll = 1354, + .comp_items = 1, + .ctrl_data = &ar0231at_ctrl_data_modes[0], + .mode_regs_items = ARRAY_SIZE(ar0231at_1920_1088_linear_mode), + .mode_regs = ar0231at_1920_1088_linear_mode, + }, + { + .sd_rects_items = ARRAY_SIZE(ar0231at_1920_1088_rects), + .sd_rects = ar0231at_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 1978, + .min_fll = 1480, + .comp_items = 1, + .ctrl_data = &ar0231at_ctrl_data_modes[1], + .mode_regs_items = ARRAY_SIZE(ar0231at_1920_1088_2hdr_mode), + .mode_regs = ar0231at_1920_1088_2hdr_mode, + }, + { + .sd_rects_items = ARRAY_SIZE(ar0231at_1920_1088_rects), + .sd_rects = ar0231at_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 1978, + .min_fll = 1480, + .comp_items = 1, + .ctrl_data = &ar0231at_ctrl_data_modes[2], + .mode_regs_items = ARRAY_SIZE(ar0231at_1920_1088_3hdr_mode), + .mode_regs = ar0231at_1920_1088_3hdr_mode, + }, + { + .sd_rects_items = ARRAY_SIZE(ar0231at_1920_1088_rects), + .sd_rects = ar0231at_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 2246, + .min_fll = 1304, + .comp_items = 1, + .ctrl_data = &ar0231at_ctrl_data_modes[3], + .mode_regs_items = ARRAY_SIZE(ar0231at_1920_1088_4hdr_mode), + .mode_regs = ar0231at_1920_1088_4hdr_mode, + }, + { + .sd_rects_items = ARRAY_SIZE(ar0231at_1920_1088_rects), + .sd_rects = ar0231at_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 2162, + .min_fll = 1354, + .comp_items = 1, + .ctrl_data = &ar0231at_ctrl_data_modes[4], + .mode_regs_items = ARRAY_SIZE(ar0231at_1920_1088_10bit_linear_mode), + .mode_regs = ar0231at_1920_1088_10bit_linear_mode, + }, +}; + +struct crl_csi_data_fmt ar0231at_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, +}; + +static struct crl_arithmetic_ops ar0231at_ls2_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + } +}; + +/* Line length pixel */ +static struct crl_dynamic_register_access ar0231at_llp_regs[] = { + { + .address = 0x300C, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* Frame length lines */ +static struct crl_dynamic_register_access ar0231at_fll_regs[] = { + { + .address = 0x300A, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* Analog gain register, also used in linear(non-HDR) mode */ +static struct crl_dynamic_register_access ar0231at_ana_gain_regs[] = { + { + .address = 0x3366, /* analog gain */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* Digital gain register */ +static struct crl_dynamic_register_access ar0231at_gl_regs[] = { + { + .address = 0x305E, /* global digital gain */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0x07ff, + }, +}; + +/* + * Exposure mode: + * 0: Linear mode + * 1: 2-HDR mode + * 2: 3-HDR mode + * 3: 4-HDR mode + */ +static struct crl_dynamic_register_access ar0231at_exposure_mode_regs[] = { + { + .address = 0x3082, + .len = CRL_REG_LEN_16BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ar0231at_ls2_ops), + .ops = ar0231at_ls2_ops, + .mask = 0x000C, + }, +}; + +/* + * Exposure Ratio in HDR mode + * 0x8000: + * Select exposure ratio mode or + * configure exposure time for each x-HDR individually. + * 0x0222: + * Selected exposure ratio mode and each ratio is 4x. + * The ratio also can be 2x, 8x, 16x + */ +static struct crl_dynamic_register_access ar0231at_hdr_exposure_ratio_regs[] = { + { + .address = 0x3238, + .len = CRL_REG_LEN_16BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x8777, + }, +}; + +/* t1 exposure register, also used in linear(non-HDR) mode */ +static struct crl_dynamic_register_access ar0231at_t1expotime_regs[] = { + { + .address = 0x3012, /* coarse integration time T1 */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* t2 exposure register, only used in HDR mode */ +static struct crl_dynamic_register_access ar0231at_t2expotime_regs[] = { + { + .address = 0x3212, /* coarse integration time T2 */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* t3 exposure register, only used in HDR mode */ +static struct crl_dynamic_register_access ar0231at_t3expotime_regs[] = { + { + .address = 0x3216, /* coarse integration time T3 */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* t4 exposure register, only used in HDR mode */ +static struct crl_dynamic_register_access ar0231at_t4expotime_regs[] = { + { + .address = 0x321A, /* coarse integration time T4 */ + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ar0231at_test_pattern_regs[] = { + { + .address = 0x3070, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static const char * const ar0231at_test_patterns[] = { + "Disabled", + "Solid Color", + "100% Vertical Color Bar", +}; + +struct crl_v4l2_ctrl ar0231at_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1920, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 1978, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_llp_regs), + .regs = ar0231at_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1088, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 1480, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_fll_regs), + .regs = ar0231at_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0x0000, + .data.std_data.max = 0xFFFF, + .data.std_data.step = 1, + .data.std_data.def = 0xAAAA, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_ana_gain_regs), + .regs = ar0231at_ana_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0x0080, + .data.std_data.max = 0x07FF, + .data.std_data.step = 1, + .data.std_data.def = 0x0080, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_gl_regs), + .regs = ar0231at_gl_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .name = "CRL_CID_EXPOSURE_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = ARRAY_SIZE(ar0231at_ctrl_data_modes)-1, + .data.std_data.step = 1, + .data.std_data.def = 0x0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_exposure_mode_regs), + .regs = ar0231at_exposure_mode_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_HDR_RATIO, + .name = "CRL_CID_EXPOSURE_HDR_RATIO", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0x0222, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_hdr_exposure_ratio_regs), + .regs = ar0231at_hdr_exposure_ratio_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "T1_COARSE_EXPOSURE_TIME", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0x0002, + .data.std_data.max = 0x04FF, + .data.std_data.step = 1, + .data.std_data.def = 0x0163, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_t1expotime_regs), + .regs = ar0231at_t1expotime_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .name = "T2_COARSE_EXPOSURE_TIME", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x0002, + .data.std_data.max = 0x0300, + .data.std_data.step = 1, + .data.std_data.def = 0x0002, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_t2expotime_regs), + .regs = ar0231at_t2expotime_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS2, + .name = "T3_COARSE_EXPOSURE_TIME", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x0002, + .data.std_data.max = 0x0180, + .data.std_data.step = 1, + .data.std_data.def = 0x0002, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_t3expotime_regs), + .regs = ar0231at_t3expotime_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS3, + .name = "T4_COARSE_EXPOSURE_TIME", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x0, + .data.std_data.max = 0x0500, + .data.std_data.step = 1, + .data.std_data.def = 0x0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_t4expotime_regs), + .regs = ar0231at_t4expotime_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = ar0231at_test_patterns, + .data.v4l2_menu_items.size = ARRAY_SIZE(ar0231at_test_patterns)-1, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_test_pattern_regs), + .regs = ar0231at_test_pattern_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_sensor_detect_config ar0231at_sensor_detect_regset[] = { + { + .reg = { 0x3000, CRL_REG_LEN_16BIT, 0xFFFF }, + .width = 15, + }, +}; + +static struct crl_sensor_limits ar0231at_maxim_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1088, + .min_frame_length_lines = 240, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 320, + .max_line_length_pixels = 32752, +}; + +struct crl_sensor_configuration ar0231at_crl_configuration = { + .powerup_regs_items = 0, + .powerup_regs = 0, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .power_items = 0, + .power_entities = 0, + + .pll_config_items = ARRAY_SIZE(ar0231at_pll_configurations), + .pll_configs = ar0231at_pll_configurations, + + .id_reg_items = ARRAY_SIZE(ar0231at_sensor_detect_regset), + .id_regs = ar0231at_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ar0231at_sensor_subdevs), + .subdevs = ar0231at_sensor_subdevs, + + .modes_items = ARRAY_SIZE(ar0231at_modes), + .modes = ar0231at_modes, + + .csi_fmts_items = ARRAY_SIZE(ar0231at_crl_csi_data_fmt), + .csi_fmts = ar0231at_crl_csi_data_fmt, + + .v4l2_ctrls_items = ARRAY_SIZE(ar0231at_v4l2_ctrls), + .v4l2_ctrl_bank = ar0231at_v4l2_ctrls, + + .streamon_regs_items = 0, + .streamon_regs = 0, + .streamoff_regs_items = 0, + .streamoff_regs = 0, + + .sensor_limits = &ar0231at_maxim_sensor_limits, + +}; + +#endif /* __CRLMODULE_AR0231AT_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ar023z_configuration.h b/drivers/media/i2c/crlmodule/crl_ar023z_configuration.h new file mode 100644 index 000000000000..2bd2b0f06b18 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ar023z_configuration.h @@ -0,0 +1,1903 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * Author: Alexei Zavjalov + * Author: Kiran Kumar + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __CRLMODULE_AR023Z_CONFIGURATION_H_ +#define __CRLMODULE_AR023Z_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define TC358778_I2C_ADDRESS 0x0E /* Toshiba TC358778 Parallel-MIPI Bridge */ +#define AR023Z_I2C_ADDRESS 0x48 /* OnSemi AP0202AT ISP */ + +static struct crl_register_write_rep ar023z_1920_1080[] = { + { 0x0004, CRL_REG_LEN_16BIT, 0x0004, TC358778_I2C_ADDRESS }, + { 0x0002, CRL_REG_LEN_16BIT, 0x0001, TC358778_I2C_ADDRESS }, + { 0x0002, CRL_REG_LEN_16BIT, 0x0000, TC358778_I2C_ADDRESS }, + { 0x0016, CRL_REG_LEN_16BIT, 0x50cd, TC358778_I2C_ADDRESS }, + { 0x0018, CRL_REG_LEN_16BIT, 0x0213, TC358778_I2C_ADDRESS }, + + { 0x0006, CRL_REG_LEN_16BIT, 0x0040, TC358778_I2C_ADDRESS }, + { 0x0008, CRL_REG_LEN_16BIT, 0x0060, TC358778_I2C_ADDRESS }, + { 0x0022, CRL_REG_LEN_16BIT, 0x0F00, TC358778_I2C_ADDRESS }, + + { 0x0140, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS }, + { 0x0144, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS }, + { 0x0148, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS }, + { 0x014C, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS }, + { 0x0150, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS }, + + { 0x0210, CRL_REG_LEN_32BIT, 0x21000000, TC358778_I2C_ADDRESS }, + { 0x0214, CRL_REG_LEN_32BIT, 0x00040000, TC358778_I2C_ADDRESS }, + { 0x0218, CRL_REG_LEN_32BIT, 0x17050000, TC358778_I2C_ADDRESS }, + { 0x021C, CRL_REG_LEN_32BIT, 0x00020000, TC358778_I2C_ADDRESS }, + { 0x0220, CRL_REG_LEN_32BIT, 0x0a070000, TC358778_I2C_ADDRESS }, + { 0x0224, CRL_REG_LEN_32BIT, 0x41880000, TC358778_I2C_ADDRESS }, + { 0x0228, CRL_REG_LEN_32BIT, 0x00080000, TC358778_I2C_ADDRESS }, + { 0x022C, CRL_REG_LEN_32BIT, 0x00020000, TC358778_I2C_ADDRESS }, + { 0x0234, CRL_REG_LEN_32BIT, 0x00070000, TC358778_I2C_ADDRESS }, + { 0x0238, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS }, + { 0x0204, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS }, + + { 0x0518, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS }, + { 0x0500, CRL_REG_LEN_32BIT, 0x80A3A300, TC358778_I2C_ADDRESS }, + + { 0x0004, CRL_REG_LEN_16BIT, 0x0245, TC358778_I2C_ADDRESS }, + + { 0x0040, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x098E, CRL_REG_LEN_16BIT, 0x7C00, AR023Z_I2C_ADDRESS }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0054, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8706, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0982, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0x098A, CRL_REG_LEN_16BIT, 0x4750, AR023Z_I2C_ADDRESS }, + { 0xC750, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC752, CRL_REG_LEN_16BIT, 0x0CEA, AR023Z_I2C_ADDRESS }, + { 0xC754, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC756, CRL_REG_LEN_16BIT, 0x75CF, AR023Z_I2C_ADDRESS }, + { 0xC758, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC75A, CRL_REG_LEN_16BIT, 0xA1A8, AR023Z_I2C_ADDRESS }, + { 0xC75C, CRL_REG_LEN_16BIT, 0x8DC4, AR023Z_I2C_ADDRESS }, + { 0xC75E, CRL_REG_LEN_16BIT, 0x0E0B, AR023Z_I2C_ADDRESS }, + { 0xC760, CRL_REG_LEN_16BIT, 0x10D1, AR023Z_I2C_ADDRESS }, + { 0xC762, CRL_REG_LEN_16BIT, 0xD804, AR023Z_I2C_ADDRESS }, + { 0xC764, CRL_REG_LEN_16BIT, 0xAD04, AR023Z_I2C_ADDRESS }, + { 0xC766, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC768, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC76A, CRL_REG_LEN_16BIT, 0x7C06, AR023Z_I2C_ADDRESS }, + { 0xC76C, CRL_REG_LEN_16BIT, 0x7840, AR023Z_I2C_ADDRESS }, + { 0xC76E, CRL_REG_LEN_16BIT, 0x0E0F, AR023Z_I2C_ADDRESS }, + { 0xC770, CRL_REG_LEN_16BIT, 0x1111, AR023Z_I2C_ADDRESS }, + { 0xC772, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC774, CRL_REG_LEN_16BIT, 0x0CEE, AR023Z_I2C_ADDRESS }, + { 0xC776, CRL_REG_LEN_16BIT, 0x0760, AR023Z_I2C_ADDRESS }, + { 0xC778, CRL_REG_LEN_16BIT, 0xAD04, AR023Z_I2C_ADDRESS }, + { 0xC77A, CRL_REG_LEN_16BIT, 0x0531, AR023Z_I2C_ADDRESS }, + { 0xC77C, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC77E, CRL_REG_LEN_16BIT, 0x78E0, AR023Z_I2C_ADDRESS }, + { 0xC780, CRL_REG_LEN_16BIT, 0xD900, AR023Z_I2C_ADDRESS }, + { 0xC782, CRL_REG_LEN_16BIT, 0xF00A, AR023Z_I2C_ADDRESS }, + { 0xC784, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC786, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC788, CRL_REG_LEN_16BIT, 0xC79C, AR023Z_I2C_ADDRESS }, + { 0xC78A, CRL_REG_LEN_16BIT, 0x7835, AR023Z_I2C_ADDRESS }, + { 0xC78C, CRL_REG_LEN_16BIT, 0x8041, AR023Z_I2C_ADDRESS }, + { 0xC78E, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0xC790, CRL_REG_LEN_16BIT, 0xE102, AR023Z_I2C_ADDRESS }, + { 0xC792, CRL_REG_LEN_16BIT, 0xA040, AR023Z_I2C_ADDRESS }, + { 0xC794, CRL_REG_LEN_16BIT, 0x09F1, AR023Z_I2C_ADDRESS }, + { 0xC796, CRL_REG_LEN_16BIT, 0x8094, AR023Z_I2C_ADDRESS }, + { 0xC798, CRL_REG_LEN_16BIT, 0x7FE0, AR023Z_I2C_ADDRESS }, + { 0xC79A, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC79C, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC79E, CRL_REG_LEN_16BIT, 0xC160, AR023Z_I2C_ADDRESS }, + { 0xC7A0, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC7A2, CRL_REG_LEN_16BIT, 0xC750, AR023Z_I2C_ADDRESS }, + { 0x098E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x0030, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0140, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xA103, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0204, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x0054, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8702, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8701, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x0054, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x01CC, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8706, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0982, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0x098A, CRL_REG_LEN_16BIT, 0x47A4, AR023Z_I2C_ADDRESS }, + { 0xC7A4, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC7A6, CRL_REG_LEN_16BIT, 0x0C96, AR023Z_I2C_ADDRESS }, + { 0xC7A8, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC7AA, CRL_REG_LEN_16BIT, 0xD900, AR023Z_I2C_ADDRESS }, + { 0xC7AC, CRL_REG_LEN_16BIT, 0xC1A1, AR023Z_I2C_ADDRESS }, + { 0xC7AE, CRL_REG_LEN_16BIT, 0x75CF, AR023Z_I2C_ADDRESS }, + { 0xC7B0, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC7B2, CRL_REG_LEN_16BIT, 0x82A4, AR023Z_I2C_ADDRESS }, + { 0xC7B4, CRL_REG_LEN_16BIT, 0x8DC0, AR023Z_I2C_ADDRESS }, + { 0xC7B6, CRL_REG_LEN_16BIT, 0x0BEE, AR023Z_I2C_ADDRESS }, + { 0xC7B8, CRL_REG_LEN_16BIT, 0x03E0, AR023Z_I2C_ADDRESS }, + { 0xC7BA, CRL_REG_LEN_16BIT, 0x708B, AR023Z_I2C_ADDRESS }, + { 0xC7BC, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC7BE, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC7C0, CRL_REG_LEN_16BIT, 0x7E2A, AR023Z_I2C_ADDRESS }, + { 0xC7C2, CRL_REG_LEN_16BIT, 0x081B, AR023Z_I2C_ADDRESS }, + { 0xC7C4, CRL_REG_LEN_16BIT, 0x0051, AR023Z_I2C_ADDRESS }, + { 0xC7C6, CRL_REG_LEN_16BIT, 0xC020, AR023Z_I2C_ADDRESS }, + { 0xC7C8, CRL_REG_LEN_16BIT, 0xE080, AR023Z_I2C_ADDRESS }, + { 0xC7CA, CRL_REG_LEN_16BIT, 0x20CC, AR023Z_I2C_ADDRESS }, + { 0xC7CC, CRL_REG_LEN_16BIT, 0x8062, AR023Z_I2C_ADDRESS }, + { 0xC7CE, CRL_REG_LEN_16BIT, 0xF407, AR023Z_I2C_ADDRESS }, + { 0xC7D0, CRL_REG_LEN_16BIT, 0xD802, AR023Z_I2C_ADDRESS }, + { 0xC7D2, CRL_REG_LEN_16BIT, 0x7960, AR023Z_I2C_ADDRESS }, + { 0xC7D4, CRL_REG_LEN_16BIT, 0xAD00, AR023Z_I2C_ADDRESS }, + { 0xC7D6, CRL_REG_LEN_16BIT, 0xADC0, AR023Z_I2C_ADDRESS }, + { 0xC7D8, CRL_REG_LEN_16BIT, 0xF002, AR023Z_I2C_ADDRESS }, + { 0xC7DA, CRL_REG_LEN_16BIT, 0x7940, AR023Z_I2C_ADDRESS }, + { 0xC7DC, CRL_REG_LEN_16BIT, 0x04CD, AR023Z_I2C_ADDRESS }, + { 0xC7DE, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC7E0, CRL_REG_LEN_16BIT, 0xC0A1, AR023Z_I2C_ADDRESS }, + { 0xC7E2, CRL_REG_LEN_16BIT, 0x78E0, AR023Z_I2C_ADDRESS }, + { 0xC7E4, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC7E6, CRL_REG_LEN_16BIT, 0x0C4E, AR023Z_I2C_ADDRESS }, + { 0xC7E8, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC7EA, CRL_REG_LEN_16BIT, 0x0CE6, AR023Z_I2C_ADDRESS }, + { 0xC7EC, CRL_REG_LEN_16BIT, 0x03C0, AR023Z_I2C_ADDRESS }, + { 0xC7EE, CRL_REG_LEN_16BIT, 0x701A, AR023Z_I2C_ADDRESS }, + { 0xC7F0, CRL_REG_LEN_16BIT, 0x0D0A, AR023Z_I2C_ADDRESS }, + { 0xC7F2, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC7F4, CRL_REG_LEN_16BIT, 0x218A, AR023Z_I2C_ADDRESS }, + { 0xC7F6, CRL_REG_LEN_16BIT, 0x0A0F, AR023Z_I2C_ADDRESS }, + { 0xC7F8, CRL_REG_LEN_16BIT, 0x7708, AR023Z_I2C_ADDRESS }, + { 0xC7FA, CRL_REG_LEN_16BIT, 0x75CF, AR023Z_I2C_ADDRESS }, + { 0xC7FC, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC7FE, CRL_REG_LEN_16BIT, 0xA168, AR023Z_I2C_ADDRESS }, + { 0xC800, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC802, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC804, CRL_REG_LEN_16BIT, 0x8712, AR023Z_I2C_ADDRESS }, + { 0xC806, CRL_REG_LEN_16BIT, 0x7840, AR023Z_I2C_ADDRESS }, + { 0xC808, CRL_REG_LEN_16BIT, 0x1524, AR023Z_I2C_ADDRESS }, + { 0xC80A, CRL_REG_LEN_16BIT, 0x1080, AR023Z_I2C_ADDRESS }, + { 0xC80C, CRL_REG_LEN_16BIT, 0xE82D, AR023Z_I2C_ADDRESS }, + { 0xC80E, CRL_REG_LEN_16BIT, 0x76CF, AR023Z_I2C_ADDRESS }, + { 0xC810, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC812, CRL_REG_LEN_16BIT, 0xB530, AR023Z_I2C_ADDRESS }, + { 0xC814, CRL_REG_LEN_16BIT, 0x9623, AR023Z_I2C_ADDRESS }, + { 0xC816, CRL_REG_LEN_16BIT, 0x75CF, AR023Z_I2C_ADDRESS }, + { 0xC818, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC81A, CRL_REG_LEN_16BIT, 0xB5B0, AR023Z_I2C_ADDRESS }, + { 0xC81C, CRL_REG_LEN_16BIT, 0x9516, AR023Z_I2C_ADDRESS }, + { 0xC81E, CRL_REG_LEN_16BIT, 0xDB7D, AR023Z_I2C_ADDRESS }, + { 0xC820, CRL_REG_LEN_16BIT, 0xBB0A, AR023Z_I2C_ADDRESS }, + { 0xC822, CRL_REG_LEN_16BIT, 0x782C, AR023Z_I2C_ADDRESS }, + { 0xC824, CRL_REG_LEN_16BIT, 0x2942, AR023Z_I2C_ADDRESS }, + { 0xC826, CRL_REG_LEN_16BIT, 0x77C0, AR023Z_I2C_ADDRESS }, + { 0xC828, CRL_REG_LEN_16BIT, 0x712F, AR023Z_I2C_ADDRESS }, + { 0xC82A, CRL_REG_LEN_16BIT, 0x0EFE, AR023Z_I2C_ADDRESS }, + { 0xC82C, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC82E, CRL_REG_LEN_16BIT, 0xDA00, AR023Z_I2C_ADDRESS }, + { 0xC830, CRL_REG_LEN_16BIT, 0x730A, AR023Z_I2C_ADDRESS }, + { 0xC832, CRL_REG_LEN_16BIT, 0x0E1A, AR023Z_I2C_ADDRESS }, + { 0xC834, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC836, CRL_REG_LEN_16BIT, 0xDA00, AR023Z_I2C_ADDRESS }, + { 0xC838, CRL_REG_LEN_16BIT, 0x72CF, AR023Z_I2C_ADDRESS }, + { 0xC83A, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC83C, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC83E, CRL_REG_LEN_16BIT, 0x7150, AR023Z_I2C_ADDRESS }, + { 0xC840, CRL_REG_LEN_16BIT, 0x22CA, AR023Z_I2C_ADDRESS }, + { 0xC842, CRL_REG_LEN_16BIT, 0x0045, AR023Z_I2C_ADDRESS }, + { 0xC844, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC846, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC848, CRL_REG_LEN_16BIT, 0xADB4, AR023Z_I2C_ADDRESS }, + { 0xC84A, CRL_REG_LEN_16BIT, 0x9122, AR023Z_I2C_ADDRESS }, + { 0xC84C, CRL_REG_LEN_16BIT, 0x1EC0, AR023Z_I2C_ADDRESS }, + { 0xC84E, CRL_REG_LEN_16BIT, 0x1084, AR023Z_I2C_ADDRESS }, + { 0xC850, CRL_REG_LEN_16BIT, 0x854A, AR023Z_I2C_ADDRESS }, + { 0xC852, CRL_REG_LEN_16BIT, 0x7230, AR023Z_I2C_ADDRESS }, + { 0xC854, CRL_REG_LEN_16BIT, 0x21CA, AR023Z_I2C_ADDRESS }, + { 0xC856, CRL_REG_LEN_16BIT, 0x008D, AR023Z_I2C_ADDRESS }, + { 0xC858, CRL_REG_LEN_16BIT, 0xB907, AR023Z_I2C_ADDRESS }, + { 0xC85A, CRL_REG_LEN_16BIT, 0x61F8, AR023Z_I2C_ADDRESS }, + { 0xC85C, CRL_REG_LEN_16BIT, 0xB861, AR023Z_I2C_ADDRESS }, + { 0xC85E, CRL_REG_LEN_16BIT, 0x0C9E, AR023Z_I2C_ADDRESS }, + { 0xC860, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC862, CRL_REG_LEN_16BIT, 0x71E9, AR023Z_I2C_ADDRESS }, + { 0xC864, CRL_REG_LEN_16BIT, 0xB51F, AR023Z_I2C_ADDRESS }, + { 0xC866, CRL_REG_LEN_16BIT, 0x0435, AR023Z_I2C_ADDRESS }, + { 0xC868, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC86A, CRL_REG_LEN_16BIT, 0x78E0, AR023Z_I2C_ADDRESS }, + { 0xC86C, CRL_REG_LEN_16BIT, 0x8850, AR023Z_I2C_ADDRESS }, + { 0xC86E, CRL_REG_LEN_16BIT, 0xD980, AR023Z_I2C_ADDRESS }, + { 0xC870, CRL_REG_LEN_16BIT, 0xEA08, AR023Z_I2C_ADDRESS }, + { 0xC872, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC874, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC876, CRL_REG_LEN_16BIT, 0xAD10, AR023Z_I2C_ADDRESS }, + { 0xC878, CRL_REG_LEN_16BIT, 0x21F4, AR023Z_I2C_ADDRESS }, + { 0xC87A, CRL_REG_LEN_16BIT, 0x0081, AR023Z_I2C_ADDRESS }, + { 0xC87C, CRL_REG_LEN_16BIT, 0xB907, AR023Z_I2C_ADDRESS }, + { 0xC87E, CRL_REG_LEN_16BIT, 0xB925, AR023Z_I2C_ADDRESS }, + { 0xC880, CRL_REG_LEN_16BIT, 0x8851, AR023Z_I2C_ADDRESS }, + { 0xC882, CRL_REG_LEN_16BIT, 0xEA09, AR023Z_I2C_ADDRESS }, + { 0xC884, CRL_REG_LEN_16BIT, 0x72CF, AR023Z_I2C_ADDRESS }, + { 0xC886, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC888, CRL_REG_LEN_16BIT, 0xACF4, AR023Z_I2C_ADDRESS }, + { 0xC88A, CRL_REG_LEN_16BIT, 0x9254, AR023Z_I2C_ADDRESS }, + { 0xC88C, CRL_REG_LEN_16BIT, 0x7A2C, AR023Z_I2C_ADDRESS }, + { 0xC88E, CRL_REG_LEN_16BIT, 0x2941, AR023Z_I2C_ADDRESS }, + { 0xC890, CRL_REG_LEN_16BIT, 0x7141, AR023Z_I2C_ADDRESS }, + { 0xC892, CRL_REG_LEN_16BIT, 0x9043, AR023Z_I2C_ADDRESS }, + { 0xC894, CRL_REG_LEN_16BIT, 0x7A2C, AR023Z_I2C_ADDRESS }, + { 0xC896, CRL_REG_LEN_16BIT, 0x9011, AR023Z_I2C_ADDRESS }, + { 0xC898, CRL_REG_LEN_16BIT, 0x2941, AR023Z_I2C_ADDRESS }, + { 0xC89A, CRL_REG_LEN_16BIT, 0x7141, AR023Z_I2C_ADDRESS }, + { 0xC89C, CRL_REG_LEN_16BIT, 0x782C, AR023Z_I2C_ADDRESS }, + { 0xC89E, CRL_REG_LEN_16BIT, 0x7FE0, AR023Z_I2C_ADDRESS }, + { 0xC8A0, CRL_REG_LEN_16BIT, 0x2941, AR023Z_I2C_ADDRESS }, + { 0xC8A2, CRL_REG_LEN_16BIT, 0x71C0, AR023Z_I2C_ADDRESS }, + { 0xC8A4, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC8A6, CRL_REG_LEN_16BIT, 0x0B92, AR023Z_I2C_ADDRESS }, + { 0xC8A8, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC8AA, CRL_REG_LEN_16BIT, 0x7508, AR023Z_I2C_ADDRESS }, + { 0xC8AC, CRL_REG_LEN_16BIT, 0xFFF0, AR023Z_I2C_ADDRESS }, + { 0xC8AE, CRL_REG_LEN_16BIT, 0xB807, AR023Z_I2C_ADDRESS }, + { 0xC8B0, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC8B2, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC8B4, CRL_REG_LEN_16BIT, 0xB5B0, AR023Z_I2C_ADDRESS }, + { 0xC8B6, CRL_REG_LEN_16BIT, 0x0C46, AR023Z_I2C_ADDRESS }, + { 0xC8B8, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC8BA, CRL_REG_LEN_16BIT, 0x913D, AR023Z_I2C_ADDRESS }, + { 0xC8BC, CRL_REG_LEN_16BIT, 0x7708, AR023Z_I2C_ADDRESS }, + { 0xC8BE, CRL_REG_LEN_16BIT, 0x9500, AR023Z_I2C_ADDRESS }, + { 0xC8C0, CRL_REG_LEN_16BIT, 0x9521, AR023Z_I2C_ADDRESS }, + { 0xC8C2, CRL_REG_LEN_16BIT, 0x0A16, AR023Z_I2C_ADDRESS }, + { 0xC8C4, CRL_REG_LEN_16BIT, 0x13E0, AR023Z_I2C_ADDRESS }, + { 0xC8C6, CRL_REG_LEN_16BIT, 0x9547, AR023Z_I2C_ADDRESS }, + { 0xC8C8, CRL_REG_LEN_16BIT, 0x7608, AR023Z_I2C_ADDRESS }, + { 0xC8CA, CRL_REG_LEN_16BIT, 0x70E9, AR023Z_I2C_ADDRESS }, + { 0xC8CC, CRL_REG_LEN_16BIT, 0x0A56, AR023Z_I2C_ADDRESS }, + { 0xC8CE, CRL_REG_LEN_16BIT, 0x10E0, AR023Z_I2C_ADDRESS }, + { 0xC8D0, CRL_REG_LEN_16BIT, 0xD908, AR023Z_I2C_ADDRESS }, + { 0xC8D2, CRL_REG_LEN_16BIT, 0x7508, AR023Z_I2C_ADDRESS }, + { 0xC8D4, CRL_REG_LEN_16BIT, 0x2582, AR023Z_I2C_ADDRESS }, + { 0xC8D6, CRL_REG_LEN_16BIT, 0x101C, AR023Z_I2C_ADDRESS }, + { 0xC8D8, CRL_REG_LEN_16BIT, 0x70C9, AR023Z_I2C_ADDRESS }, + { 0xC8DA, CRL_REG_LEN_16BIT, 0x0A4A, AR023Z_I2C_ADDRESS }, + { 0xC8DC, CRL_REG_LEN_16BIT, 0x10E0, AR023Z_I2C_ADDRESS }, + { 0xC8DE, CRL_REG_LEN_16BIT, 0xD908, AR023Z_I2C_ADDRESS }, + { 0xC8E0, CRL_REG_LEN_16BIT, 0x03C1, AR023Z_I2C_ADDRESS }, + { 0xC8E2, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC8E4, CRL_REG_LEN_16BIT, 0x60B8, AR023Z_I2C_ADDRESS }, + { 0xC8E6, CRL_REG_LEN_16BIT, 0x78E0, AR023Z_I2C_ADDRESS }, + { 0xC8E8, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC8EA, CRL_REG_LEN_16BIT, 0x0B4E, AR023Z_I2C_ADDRESS }, + { 0xC8EC, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC8EE, CRL_REG_LEN_16BIT, 0x77CF, AR023Z_I2C_ADDRESS }, + { 0xC8F0, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC8F2, CRL_REG_LEN_16BIT, 0x8004, AR023Z_I2C_ADDRESS }, + { 0xC8F4, CRL_REG_LEN_16BIT, 0x0BC2, AR023Z_I2C_ADDRESS }, + { 0xC8F6, CRL_REG_LEN_16BIT, 0x03C0, AR023Z_I2C_ADDRESS }, + { 0xC8F8, CRL_REG_LEN_16BIT, 0x75CF, AR023Z_I2C_ADDRESS }, + { 0xC8FA, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC8FC, CRL_REG_LEN_16BIT, 0xAC00, AR023Z_I2C_ADDRESS }, + { 0xC8FE, CRL_REG_LEN_16BIT, 0x7608, AR023Z_I2C_ADDRESS }, + { 0xC900, CRL_REG_LEN_16BIT, 0x8F05, AR023Z_I2C_ADDRESS }, + { 0xC902, CRL_REG_LEN_16BIT, 0x9522, AR023Z_I2C_ADDRESS }, + { 0xC904, CRL_REG_LEN_16BIT, 0x7610, AR023Z_I2C_ADDRESS }, + { 0xC906, CRL_REG_LEN_16BIT, 0x21D1, AR023Z_I2C_ADDRESS }, + { 0xC908, CRL_REG_LEN_16BIT, 0x80A2, AR023Z_I2C_ADDRESS }, + { 0xC90A, CRL_REG_LEN_16BIT, 0xF213, AR023Z_I2C_ADDRESS }, + { 0xC90C, CRL_REG_LEN_16BIT, 0xE680, AR023Z_I2C_ADDRESS }, + { 0xC90E, CRL_REG_LEN_16BIT, 0x26CC, AR023Z_I2C_ADDRESS }, + { 0xC910, CRL_REG_LEN_16BIT, 0x9062, AR023Z_I2C_ADDRESS }, + { 0xC912, CRL_REG_LEN_16BIT, 0xF40F, AR023Z_I2C_ADDRESS }, + { 0xC914, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC916, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC918, CRL_REG_LEN_16BIT, 0xB960, AR023Z_I2C_ADDRESS }, + { 0xC91A, CRL_REG_LEN_16BIT, 0xFFE3, AR023Z_I2C_ADDRESS }, + { 0xC91C, CRL_REG_LEN_16BIT, 0xB504, AR023Z_I2C_ADDRESS }, + { 0xC91E, CRL_REG_LEN_16BIT, 0x08DE, AR023Z_I2C_ADDRESS }, + { 0xC920, CRL_REG_LEN_16BIT, 0x0220, AR023Z_I2C_ADDRESS }, + { 0xC922, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC924, CRL_REG_LEN_16BIT, 0xD801, AR023Z_I2C_ADDRESS }, + { 0xC926, CRL_REG_LEN_16BIT, 0xAD0E, AR023Z_I2C_ADDRESS }, + { 0xC928, CRL_REG_LEN_16BIT, 0xAFC5, AR023Z_I2C_ADDRESS }, + { 0xC92A, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC92C, CRL_REG_LEN_16BIT, 0xF005, AR023Z_I2C_ADDRESS }, + { 0xC92E, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC930, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC932, CRL_REG_LEN_16BIT, 0x0F7E, AR023Z_I2C_ADDRESS }, + { 0xC934, CRL_REG_LEN_16BIT, 0x7840, AR023Z_I2C_ADDRESS }, + { 0xC936, CRL_REG_LEN_16BIT, 0x036D, AR023Z_I2C_ADDRESS }, + { 0xC938, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC93A, CRL_REG_LEN_16BIT, 0x78E0, AR023Z_I2C_ADDRESS }, + { 0xC93C, CRL_REG_LEN_16BIT, 0xD900, AR023Z_I2C_ADDRESS }, + { 0xC93E, CRL_REG_LEN_16BIT, 0xF00A, AR023Z_I2C_ADDRESS }, + { 0xC940, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC942, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC944, CRL_REG_LEN_16BIT, 0xC958, AR023Z_I2C_ADDRESS }, + { 0xC946, CRL_REG_LEN_16BIT, 0x7835, AR023Z_I2C_ADDRESS }, + { 0xC948, CRL_REG_LEN_16BIT, 0x8041, AR023Z_I2C_ADDRESS }, + { 0xC94A, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0xC94C, CRL_REG_LEN_16BIT, 0xE102, AR023Z_I2C_ADDRESS }, + { 0xC94E, CRL_REG_LEN_16BIT, 0xA040, AR023Z_I2C_ADDRESS }, + { 0xC950, CRL_REG_LEN_16BIT, 0x09F1, AR023Z_I2C_ADDRESS }, + { 0xC952, CRL_REG_LEN_16BIT, 0x8194, AR023Z_I2C_ADDRESS }, + { 0xC954, CRL_REG_LEN_16BIT, 0x7FE0, AR023Z_I2C_ADDRESS }, + { 0xC956, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC958, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC95A, CRL_REG_LEN_16BIT, 0xC164, AR023Z_I2C_ADDRESS }, + { 0xC95C, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC95E, CRL_REG_LEN_16BIT, 0xC7A4, AR023Z_I2C_ADDRESS }, + { 0xC960, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC962, CRL_REG_LEN_16BIT, 0xC198, AR023Z_I2C_ADDRESS }, + { 0xC964, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC966, CRL_REG_LEN_16BIT, 0xC7E4, AR023Z_I2C_ADDRESS }, + { 0xC968, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC96A, CRL_REG_LEN_16BIT, 0xBB6C, AR023Z_I2C_ADDRESS }, + { 0xC96C, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC96E, CRL_REG_LEN_16BIT, 0xC8E8, AR023Z_I2C_ADDRESS }, + { 0x098E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x01EC, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0240, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xA103, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0204, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x01CC, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8702, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8701, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x0220, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x005C, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8706, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0982, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0x098A, CRL_REG_LEN_16BIT, 0x4970, AR023Z_I2C_ADDRESS }, + { 0xC970, CRL_REG_LEN_16BIT, 0xC0F1, AR023Z_I2C_ADDRESS }, + { 0xC972, CRL_REG_LEN_16BIT, 0x0ACA, AR023Z_I2C_ADDRESS }, + { 0xC974, CRL_REG_LEN_16BIT, 0x1340, AR023Z_I2C_ADDRESS }, + { 0xC976, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC978, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC97A, CRL_REG_LEN_16BIT, 0x2896, AR023Z_I2C_ADDRESS }, + { 0xC97C, CRL_REG_LEN_16BIT, 0x7940, AR023Z_I2C_ADDRESS }, + { 0xC97E, CRL_REG_LEN_16BIT, 0x250A, AR023Z_I2C_ADDRESS }, + { 0xC980, CRL_REG_LEN_16BIT, 0x9000, AR023Z_I2C_ADDRESS }, + { 0xC982, CRL_REG_LEN_16BIT, 0x76CF, AR023Z_I2C_ADDRESS }, + { 0xC984, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC986, CRL_REG_LEN_16BIT, 0xB51C, AR023Z_I2C_ADDRESS }, + { 0xC988, CRL_REG_LEN_16BIT, 0xF407, AR023Z_I2C_ADDRESS }, + { 0xC98A, CRL_REG_LEN_16BIT, 0x0D4A, AR023Z_I2C_ADDRESS }, + { 0xC98C, CRL_REG_LEN_16BIT, 0x0B20, AR023Z_I2C_ADDRESS }, + { 0xC98E, CRL_REG_LEN_16BIT, 0x8E12, AR023Z_I2C_ADDRESS }, + { 0xC990, CRL_REG_LEN_16BIT, 0x0C6A, AR023Z_I2C_ADDRESS }, + { 0xC992, CRL_REG_LEN_16BIT, 0x0AE0, AR023Z_I2C_ADDRESS }, + { 0xC994, CRL_REG_LEN_16BIT, 0xD801, AR023Z_I2C_ADDRESS }, + { 0xC996, CRL_REG_LEN_16BIT, 0x0315, AR023Z_I2C_ADDRESS }, + { 0xC998, CRL_REG_LEN_16BIT, 0x1360, AR023Z_I2C_ADDRESS }, + { 0xC99A, CRL_REG_LEN_16BIT, 0x70A9, AR023Z_I2C_ADDRESS }, + { 0xC99C, CRL_REG_LEN_16BIT, 0xD900, AR023Z_I2C_ADDRESS }, + { 0xC99E, CRL_REG_LEN_16BIT, 0xF00A, AR023Z_I2C_ADDRESS }, + { 0xC9A0, CRL_REG_LEN_16BIT, 0x70CF, AR023Z_I2C_ADDRESS }, + { 0xC9A2, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC9A4, CRL_REG_LEN_16BIT, 0xC9C4, AR023Z_I2C_ADDRESS }, + { 0xC9A6, CRL_REG_LEN_16BIT, 0x7835, AR023Z_I2C_ADDRESS }, + { 0xC9A8, CRL_REG_LEN_16BIT, 0x8041, AR023Z_I2C_ADDRESS }, + { 0xC9AA, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0xC9AC, CRL_REG_LEN_16BIT, 0xE102, AR023Z_I2C_ADDRESS }, + { 0xC9AE, CRL_REG_LEN_16BIT, 0xA040, AR023Z_I2C_ADDRESS }, + { 0xC9B0, CRL_REG_LEN_16BIT, 0x09F1, AR023Z_I2C_ADDRESS }, + { 0xC9B2, CRL_REG_LEN_16BIT, 0x8094, AR023Z_I2C_ADDRESS }, + { 0xC9B4, CRL_REG_LEN_16BIT, 0x71CF, AR023Z_I2C_ADDRESS }, + { 0xC9B6, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC9B8, CRL_REG_LEN_16BIT, 0xB51C, AR023Z_I2C_ADDRESS }, + { 0xC9BA, CRL_REG_LEN_16BIT, 0xD808, AR023Z_I2C_ADDRESS }, + { 0xC9BC, CRL_REG_LEN_16BIT, 0xA912, AR023Z_I2C_ADDRESS }, + { 0xC9BE, CRL_REG_LEN_16BIT, 0x7FE0, AR023Z_I2C_ADDRESS }, + { 0xC9C0, CRL_REG_LEN_16BIT, 0xD800, AR023Z_I2C_ADDRESS }, + { 0xC9C2, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC9C4, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC9C6, CRL_REG_LEN_16BIT, 0xBFE4, AR023Z_I2C_ADDRESS }, + { 0xC9C8, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xC9CA, CRL_REG_LEN_16BIT, 0xC970, AR023Z_I2C_ADDRESS }, + { 0x098E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x024C, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0340, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xA103, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0204, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x005C, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8702, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8701, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xCC02, CRL_REG_LEN_16BIT, 0x0493, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D00, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D01, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3088, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0280, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C45, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x5872, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x9B4A, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x3143, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x428E, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x032A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1400, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C45, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x787B, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3DFF, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x3DFF, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x3DEA, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2A04, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x3D00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C10, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2A05, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x2A15, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x352A, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x053D, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x1045, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C2A, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x042A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x143D, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0xFF3D, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0xFF3D, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0xEA2A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x0400, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C62, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2A28, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x362A, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x083D, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x647A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x3D00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C04, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x442C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x4B8F, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x0043, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x0C2D, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x6343, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1600, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C8E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x032A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0xFC5C, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x1D57, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x5449, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x5F53, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x0500, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C53, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x074D, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x2BF8, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x1016, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4C08, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x5556, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2B00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0CB8, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2B98, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x4E11, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2904, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x2984, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2994, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x6000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C5C, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x195C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x1B45, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4845, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x0845, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8829, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0xB600, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C8E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x012A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0xF83E, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x022A, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0xFA3F, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x095C, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1B00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C29, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xB23F, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0C3E, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x023E, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x135C, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x133F, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1100, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C3E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0B5F, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x2B90, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2AF2, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x2B80, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x3E04, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x3F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C06, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x6029, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0xA229, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0xA35F, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4D19, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2AFA, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2900, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C83, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x45A8, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3E07, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2AFB, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x3E29, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4588, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2100, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C3E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x082A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0xFA5D, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2992, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x8810, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2B04, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8B00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C16, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x858D, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x484D, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4E2B, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x804C, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x0B60, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x3F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C28, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2AF2, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3F0F, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2982, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x2983, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2943, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5C00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C15, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x5F4D, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x192A, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0xFA45, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x588E, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x002A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x9800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C3F, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0612, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x444A, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x0443, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x1605, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C43, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x165A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x0643, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x1607, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C03, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2A9C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x4578, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x7B3F, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x072A, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x9D3E, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2E00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C45, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x5825, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3E06, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x8E01, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x2A98, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1200, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C44, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4B03, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x432D, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4643, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x16A3, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5D00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C0D, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2944, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x8810, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2B04, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x530D, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8B16, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8500, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C44, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8E03, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x2AFC, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x5C1D, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x8D60, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x5754, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x4900, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C5F, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x5305, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x5307, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4D2B, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0xF810, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x164C, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C55, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x562B, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0xB82B, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x984E, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x1129, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x0429, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8400, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C29, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x9460, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x5C19, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x5C1B, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4548, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4508, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x4500, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C88, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x29B6, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x8E01, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2AF8, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x3E02, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2AFA, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x3F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C09, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x5C1B, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x29B2, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x3F0C, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x3E02, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x3E13, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5C00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C13, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x3F11, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3E0B, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x5F2B, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x902A, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0xF22B, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C3E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x043F, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0660, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x29A2, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x29A3, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x5F4D, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1C00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C2A, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xFA29, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x8345, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0xA83E, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x072A, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0xFB3E, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2900, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C45, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8824, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3E08, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2AFA, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x5D29, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x9288, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C2B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x048B, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x1686, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x8D48, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4D4E, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2B80, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x4C00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C0B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x603F, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x282A, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0xF23F, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x0F29, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8229, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C29, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x435C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x155F, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4D1C, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x2AFA, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4558, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C00, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2A98, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3F06, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4A73, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x9D0A, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x0B00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C43, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x168E, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x032A, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x9C45, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x783F, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x072A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x9D00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C3E, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x1245, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x583F, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x048E, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x012A, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x988E, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C91, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x769C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x779C, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x4644, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x1616, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x907A, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1200, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C44, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4B4A, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x0043, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x1663, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x0843, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1600, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C50, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x6543, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x1666, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x4316, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8E03, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x2A00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C9C, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4578, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x3F07, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2A9D, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x5D0C, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x2944, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x8800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C10, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2B04, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x530D, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x8B16, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x863E, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x1F45, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x5800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0C28, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x3E06, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x8E01, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2A98, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8D60, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1200, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3086, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0444, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4B2C, AR023Z_I2C_ADDRESS }, + { 0xFC06, CRL_REG_LEN_16BIT, 0x2C00, AR023Z_I2C_ADDRESS }, + { 0xFC08, CRL_REG_LEN_16BIT, 0x2A98, AR023Z_I2C_ADDRESS }, + { 0xFC0A, CRL_REG_LEN_16BIT, 0x8E00, AR023Z_I2C_ADDRESS }, + { 0xFC0C, CRL_REG_LEN_16BIT, 0x8D60, AR023Z_I2C_ADDRESS }, + { 0xFC0E, CRL_REG_LEN_16BIT, 0x1200, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D02, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8E01, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xCCCC, CRL_REG_LEN_08BIT, 0x69, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D00, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D01, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3ED6, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0234, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xB300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x2436, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0E00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x320C, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0201, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x320E, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0203, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3210, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0205, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3204, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x020B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x6D00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x30FE, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3ED8, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x027B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x9900, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EDC, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x029B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0xA800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EDA, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x029B, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x9B00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3092, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x6F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EEC, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x021C, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0400, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x30BA, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0277, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x9C00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EF6, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x02A7, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3044, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0204, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x1000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3ED0, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x02FF, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x4400, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3ED4, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0203, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x1F00, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x30FE, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EE2, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0288, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x6600, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EE4, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0266, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3EE6, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0222, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x6300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x30E0, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0242, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x30F0, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0212, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x8300, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D02, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xCAC8, CRL_REG_LEN_08BIT, 0x41, AR023Z_I2C_ADDRESS }, + { 0xCACA, CRL_REG_LEN_16BIT, 0x022F, AR023Z_I2C_ADDRESS }, + { 0xCACE, CRL_REG_LEN_16BIT, 0x010E, AR023Z_I2C_ADDRESS }, + { 0xCAD0, CRL_REG_LEN_16BIT, 0x0033, AR023Z_I2C_ADDRESS }, + { 0xCAD4, CRL_REG_LEN_16BIT, 0x001F, AR023Z_I2C_ADDRESS }, + { 0xCAD4, CRL_REG_LEN_16BIT, 0x001F, AR023Z_I2C_ADDRESS }, + { 0xCAD8, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCADA, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC806, CRL_REG_LEN_16BIT, 0x000C, AR023Z_I2C_ADDRESS }, + { 0xC80A, CRL_REG_LEN_16BIT, 0x078B, AR023Z_I2C_ADDRESS }, + { 0xC804, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC808, CRL_REG_LEN_16BIT, 0x0437, AR023Z_I2C_ADDRESS }, + { 0xC838, CRL_REG_LEN_16BIT, 0x0302, AR023Z_I2C_ADDRESS }, + { 0xC83A, CRL_REG_LEN_16BIT, 0x211B, AR023Z_I2C_ADDRESS }, + { 0xC840, CRL_REG_LEN_16BIT, 0x010C, AR023Z_I2C_ADDRESS }, + { 0xC844, CRL_REG_LEN_16BIT, 0x0802, AR023Z_I2C_ADDRESS }, + { 0xC844, CRL_REG_LEN_16BIT, 0x0801, AR023Z_I2C_ADDRESS }, + { 0xC80C, CRL_REG_LEN_16BIT, 0x04BA, AR023Z_I2C_ADDRESS }, + { 0xC80E, CRL_REG_LEN_16BIT, 0x3674, AR023Z_I2C_ADDRESS }, + { 0xC814, CRL_REG_LEN_16BIT, 0x049E, AR023Z_I2C_ADDRESS }, + { 0xC816, CRL_REG_LEN_16BIT, 0x08BC, AR023Z_I2C_ADDRESS }, + { 0xC846, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC890, CRL_REG_LEN_08BIT, 0x00, AR023Z_I2C_ADDRESS }, + { 0xC8A0, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC8A2, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC8A4, CRL_REG_LEN_16BIT, 0x0780, AR023Z_I2C_ADDRESS }, + { 0xC8A6, CRL_REG_LEN_16BIT, 0x0438, AR023Z_I2C_ADDRESS }, + { 0xC9F8, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC9FA, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC9FC, CRL_REG_LEN_16BIT, 0x0780, AR023Z_I2C_ADDRESS }, + { 0xC9FE, CRL_REG_LEN_16BIT, 0x0438, AR023Z_I2C_ADDRESS }, + { 0xCA00, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCA02, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCA04, CRL_REG_LEN_16BIT, 0x0780, AR023Z_I2C_ADDRESS }, + { 0xCA06, CRL_REG_LEN_16BIT, 0x0438, AR023Z_I2C_ADDRESS }, + { 0xCAE4, CRL_REG_LEN_16BIT, 0x0780, AR023Z_I2C_ADDRESS }, + { 0xCAE6, CRL_REG_LEN_16BIT, 0x0438, AR023Z_I2C_ADDRESS }, + { 0xCAE8, CRL_REG_LEN_16BIT, 0x0011, AR023Z_I2C_ADDRESS }, + { 0xCAE8, CRL_REG_LEN_16BIT, 0x0011, AR023Z_I2C_ADDRESS }, + { 0xCAEA, CRL_REG_LEN_08BIT, 0x00, AR023Z_I2C_ADDRESS }, + { 0xCAEB, CRL_REG_LEN_08BIT, 0x00, AR023Z_I2C_ADDRESS }, + { 0xCAF4, CRL_REG_LEN_16BIT, 0x249F, AR023Z_I2C_ADDRESS }, + { 0xCAF8, CRL_REG_LEN_08BIT, 0x0E, AR023Z_I2C_ADDRESS }, + { 0xCAFC, CRL_REG_LEN_16BIT, 0x4201, AR023Z_I2C_ADDRESS }, + { 0xCAFE, CRL_REG_LEN_16BIT, 0x08BC, AR023Z_I2C_ADDRESS }, + { 0xCB00, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0x8C16, CRL_REG_LEN_08BIT, 0x19, AR023Z_I2C_ADDRESS }, + { 0xCAC4, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x3600, CRL_REG_LEN_16BIT, 0x00F0, AR023Z_I2C_ADDRESS }, + { 0x3602, CRL_REG_LEN_16BIT, 0xD789, AR023Z_I2C_ADDRESS }, + { 0x3604, CRL_REG_LEN_16BIT, 0x4D70, AR023Z_I2C_ADDRESS }, + { 0x3606, CRL_REG_LEN_16BIT, 0x6A8D, AR023Z_I2C_ADDRESS }, + { 0x3608, CRL_REG_LEN_16BIT, 0x7CEE, AR023Z_I2C_ADDRESS }, + { 0x360A, CRL_REG_LEN_16BIT, 0x00D0, AR023Z_I2C_ADDRESS }, + { 0x360C, CRL_REG_LEN_16BIT, 0x8F0B, AR023Z_I2C_ADDRESS }, + { 0x360E, CRL_REG_LEN_16BIT, 0x58B0, AR023Z_I2C_ADDRESS }, + { 0x3610, CRL_REG_LEN_16BIT, 0x2E2D, AR023Z_I2C_ADDRESS }, + { 0x3612, CRL_REG_LEN_16BIT, 0x0BCF, AR023Z_I2C_ADDRESS }, + { 0x3614, CRL_REG_LEN_16BIT, 0x00B0, AR023Z_I2C_ADDRESS }, + { 0x3616, CRL_REG_LEN_16BIT, 0xC149, AR023Z_I2C_ADDRESS }, + { 0x3618, CRL_REG_LEN_16BIT, 0x4950, AR023Z_I2C_ADDRESS }, + { 0x361A, CRL_REG_LEN_16BIT, 0x024E, AR023Z_I2C_ADDRESS }, + { 0x361C, CRL_REG_LEN_16BIT, 0x0B4E, AR023Z_I2C_ADDRESS }, + { 0x361E, CRL_REG_LEN_16BIT, 0x00D0, AR023Z_I2C_ADDRESS }, + { 0x3620, CRL_REG_LEN_16BIT, 0xD2E9, AR023Z_I2C_ADDRESS }, + { 0x3622, CRL_REG_LEN_16BIT, 0x4D10, AR023Z_I2C_ADDRESS }, + { 0x3624, CRL_REG_LEN_16BIT, 0x67ED, AR023Z_I2C_ADDRESS }, + { 0x3626, CRL_REG_LEN_16BIT, 0x1ACF, AR023Z_I2C_ADDRESS }, + { 0x3628, CRL_REG_LEN_16BIT, 0x406B, AR023Z_I2C_ADDRESS }, + { 0x362A, CRL_REG_LEN_16BIT, 0x1FC9, AR023Z_I2C_ADDRESS }, + { 0x362C, CRL_REG_LEN_16BIT, 0x6750, AR023Z_I2C_ADDRESS }, + { 0x362E, CRL_REG_LEN_16BIT, 0x4E0F, AR023Z_I2C_ADDRESS }, + { 0x3630, CRL_REG_LEN_16BIT, 0xBCF3, AR023Z_I2C_ADDRESS }, + { 0x3632, CRL_REG_LEN_16BIT, 0x138C, AR023Z_I2C_ADDRESS }, + { 0x3634, CRL_REG_LEN_16BIT, 0x366A, AR023Z_I2C_ADDRESS }, + { 0x3636, CRL_REG_LEN_16BIT, 0x6390, AR023Z_I2C_ADDRESS }, + { 0x3638, CRL_REG_LEN_16BIT, 0x2E2F, AR023Z_I2C_ADDRESS }, + { 0x363A, CRL_REG_LEN_16BIT, 0xB9D3, AR023Z_I2C_ADDRESS }, + { 0x363C, CRL_REG_LEN_16BIT, 0x2B4A, AR023Z_I2C_ADDRESS }, + { 0x363E, CRL_REG_LEN_16BIT, 0x008B, AR023Z_I2C_ADDRESS }, + { 0x3640, CRL_REG_LEN_16BIT, 0x6B30, AR023Z_I2C_ADDRESS }, + { 0x3642, CRL_REG_LEN_16BIT, 0x710F, AR023Z_I2C_ADDRESS }, + { 0x3644, CRL_REG_LEN_16BIT, 0xC413, AR023Z_I2C_ADDRESS }, + { 0x3646, CRL_REG_LEN_16BIT, 0x2A4B, AR023Z_I2C_ADDRESS }, + { 0x3648, CRL_REG_LEN_16BIT, 0x080A, AR023Z_I2C_ADDRESS }, + { 0x364A, CRL_REG_LEN_16BIT, 0x6BD0, AR023Z_I2C_ADDRESS }, + { 0x364C, CRL_REG_LEN_16BIT, 0x0050, AR023Z_I2C_ADDRESS }, + { 0x364E, CRL_REG_LEN_16BIT, 0xC4D3, AR023Z_I2C_ADDRESS }, + { 0x3650, CRL_REG_LEN_16BIT, 0x6F90, AR023Z_I2C_ADDRESS }, + { 0x3652, CRL_REG_LEN_16BIT, 0x5A2F, AR023Z_I2C_ADDRESS }, + { 0x3654, CRL_REG_LEN_16BIT, 0xE631, AR023Z_I2C_ADDRESS }, + { 0x3656, CRL_REG_LEN_16BIT, 0x8812, AR023Z_I2C_ADDRESS }, + { 0x3658, CRL_REG_LEN_16BIT, 0x2155, AR023Z_I2C_ADDRESS }, + { 0x365A, CRL_REG_LEN_16BIT, 0x6A30, AR023Z_I2C_ADDRESS }, + { 0x365C, CRL_REG_LEN_16BIT, 0x7FCF, AR023Z_I2C_ADDRESS }, + { 0x365E, CRL_REG_LEN_16BIT, 0xE291, AR023Z_I2C_ADDRESS }, + { 0x3660, CRL_REG_LEN_16BIT, 0x9C92, AR023Z_I2C_ADDRESS }, + { 0x3662, CRL_REG_LEN_16BIT, 0x2C75, AR023Z_I2C_ADDRESS }, + { 0x3664, CRL_REG_LEN_16BIT, 0x5F90, AR023Z_I2C_ADDRESS }, + { 0x3666, CRL_REG_LEN_16BIT, 0x618F, AR023Z_I2C_ADDRESS }, + { 0x3668, CRL_REG_LEN_16BIT, 0xED91, AR023Z_I2C_ADDRESS }, + { 0x366A, CRL_REG_LEN_16BIT, 0x9FB2, AR023Z_I2C_ADDRESS }, + { 0x366C, CRL_REG_LEN_16BIT, 0x1915, AR023Z_I2C_ADDRESS }, + { 0x366E, CRL_REG_LEN_16BIT, 0x6E90, AR023Z_I2C_ADDRESS }, + { 0x3670, CRL_REG_LEN_16BIT, 0x5C0F, AR023Z_I2C_ADDRESS }, + { 0x3672, CRL_REG_LEN_16BIT, 0xE111, AR023Z_I2C_ADDRESS }, + { 0x3674, CRL_REG_LEN_16BIT, 0x9352, AR023Z_I2C_ADDRESS }, + { 0x3676, CRL_REG_LEN_16BIT, 0x2135, AR023Z_I2C_ADDRESS }, + { 0x3678, CRL_REG_LEN_16BIT, 0x7230, AR023Z_I2C_ADDRESS }, + { 0x367A, CRL_REG_LEN_16BIT, 0x2D92, AR023Z_I2C_ADDRESS }, + { 0x367C, CRL_REG_LEN_16BIT, 0xEEB5, AR023Z_I2C_ADDRESS }, + { 0x367E, CRL_REG_LEN_16BIT, 0x8495, AR023Z_I2C_ADDRESS }, + { 0x3680, CRL_REG_LEN_16BIT, 0x3C38, AR023Z_I2C_ADDRESS }, + { 0x3682, CRL_REG_LEN_16BIT, 0x7B50, AR023Z_I2C_ADDRESS }, + { 0x3684, CRL_REG_LEN_16BIT, 0x2332, AR023Z_I2C_ADDRESS }, + { 0x3686, CRL_REG_LEN_16BIT, 0xED55, AR023Z_I2C_ADDRESS }, + { 0x3688, CRL_REG_LEN_16BIT, 0x8355, AR023Z_I2C_ADDRESS }, + { 0x368A, CRL_REG_LEN_16BIT, 0x3978, AR023Z_I2C_ADDRESS }, + { 0x368C, CRL_REG_LEN_16BIT, 0x74F0, AR023Z_I2C_ADDRESS }, + { 0x368E, CRL_REG_LEN_16BIT, 0x4032, AR023Z_I2C_ADDRESS }, + { 0x3690, CRL_REG_LEN_16BIT, 0xF9B5, AR023Z_I2C_ADDRESS }, + { 0x3692, CRL_REG_LEN_16BIT, 0x8D75, AR023Z_I2C_ADDRESS }, + { 0x3694, CRL_REG_LEN_16BIT, 0x4338, AR023Z_I2C_ADDRESS }, + { 0x3696, CRL_REG_LEN_16BIT, 0x7550, AR023Z_I2C_ADDRESS }, + { 0x3698, CRL_REG_LEN_16BIT, 0x2CB2, AR023Z_I2C_ADDRESS }, + { 0x369A, CRL_REG_LEN_16BIT, 0xF135, AR023Z_I2C_ADDRESS }, + { 0x369C, CRL_REG_LEN_16BIT, 0x80F5, AR023Z_I2C_ADDRESS }, + { 0x369E, CRL_REG_LEN_16BIT, 0x3B98, AR023Z_I2C_ADDRESS }, + { 0x36A0, CRL_REG_LEN_16BIT, 0x90F2, AR023Z_I2C_ADDRESS }, + { 0x36A2, CRL_REG_LEN_16BIT, 0xD4D2, AR023Z_I2C_ADDRESS }, + { 0x36A4, CRL_REG_LEN_16BIT, 0x35B7, AR023Z_I2C_ADDRESS }, + { 0x36A6, CRL_REG_LEN_16BIT, 0x1A75, AR023Z_I2C_ADDRESS }, + { 0x36A8, CRL_REG_LEN_16BIT, 0x9B5A, AR023Z_I2C_ADDRESS }, + { 0x36AA, CRL_REG_LEN_16BIT, 0xFF71, AR023Z_I2C_ADDRESS }, + { 0x36AC, CRL_REG_LEN_16BIT, 0xC832, AR023Z_I2C_ADDRESS }, + { 0x36AE, CRL_REG_LEN_16BIT, 0x3277, AR023Z_I2C_ADDRESS }, + { 0x36B0, CRL_REG_LEN_16BIT, 0x16F5, AR023Z_I2C_ADDRESS }, + { 0x36B2, CRL_REG_LEN_16BIT, 0x97BA, AR023Z_I2C_ADDRESS }, + { 0x36B4, CRL_REG_LEN_16BIT, 0x95B2, AR023Z_I2C_ADDRESS }, + { 0x36B6, CRL_REG_LEN_16BIT, 0x9373, AR023Z_I2C_ADDRESS }, + { 0x36B8, CRL_REG_LEN_16BIT, 0x3C77, AR023Z_I2C_ADDRESS }, + { 0x36BA, CRL_REG_LEN_16BIT, 0x6115, AR023Z_I2C_ADDRESS }, + { 0x36BC, CRL_REG_LEN_16BIT, 0xA0BA, AR023Z_I2C_ADDRESS }, + { 0x36BE, CRL_REG_LEN_16BIT, 0x95B2, AR023Z_I2C_ADDRESS }, + { 0x36C0, CRL_REG_LEN_16BIT, 0xC492, AR023Z_I2C_ADDRESS }, + { 0x36C2, CRL_REG_LEN_16BIT, 0x3517, AR023Z_I2C_ADDRESS }, + { 0x36C4, CRL_REG_LEN_16BIT, 0x15B5, AR023Z_I2C_ADDRESS }, + { 0x36C6, CRL_REG_LEN_16BIT, 0x9A9A, AR023Z_I2C_ADDRESS }, + { 0x36C8, CRL_REG_LEN_16BIT, 0x018A, AR023Z_I2C_ADDRESS }, + { 0x36CA, CRL_REG_LEN_16BIT, 0x03BE, AR023Z_I2C_ADDRESS }, + { 0xCAC4, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC91E, CRL_REG_LEN_16BIT, 0x0A8C, AR023Z_I2C_ADDRESS }, + { 0xC920, CRL_REG_LEN_16BIT, 0x0FA0, AR023Z_I2C_ADDRESS }, + { 0xC922, CRL_REG_LEN_16BIT, 0x1964, AR023Z_I2C_ADDRESS }, + { 0xC924, CRL_REG_LEN_16BIT, 0x09C4, AR023Z_I2C_ADDRESS }, + { 0xC926, CRL_REG_LEN_16BIT, 0x1964, AR023Z_I2C_ADDRESS }, + { 0xC912, CRL_REG_LEN_16BIT, 0x005F, AR023Z_I2C_ADDRESS }, + { 0xC914, CRL_REG_LEN_16BIT, 0x016D, AR023Z_I2C_ADDRESS }, + { 0xC916, CRL_REG_LEN_16BIT, 0x00AF, AR023Z_I2C_ADDRESS }, + { 0xC918, CRL_REG_LEN_16BIT, 0x0148, AR023Z_I2C_ADDRESS }, + { 0xC91A, CRL_REG_LEN_16BIT, 0x0096, AR023Z_I2C_ADDRESS }, + { 0xC91C, CRL_REG_LEN_16BIT, 0x00B4, AR023Z_I2C_ADDRESS }, + { 0xC982, CRL_REG_LEN_08BIT, 0x82, AR023Z_I2C_ADDRESS }, + { 0xC983, CRL_REG_LEN_08BIT, 0x80, AR023Z_I2C_ADDRESS }, + { 0xC984, CRL_REG_LEN_08BIT, 0x86, AR023Z_I2C_ADDRESS }, + { 0xC985, CRL_REG_LEN_08BIT, 0x84, AR023Z_I2C_ADDRESS }, + { 0xC986, CRL_REG_LEN_08BIT, 0x82, AR023Z_I2C_ADDRESS }, + { 0xC987, CRL_REG_LEN_08BIT, 0x80, AR023Z_I2C_ADDRESS }, + { 0xC980, CRL_REG_LEN_16BIT, 0x1450, AR023Z_I2C_ADDRESS }, + { 0xC8DC, CRL_REG_LEN_16BIT, 0x013E, AR023Z_I2C_ADDRESS }, + { 0xC8DE, CRL_REG_LEN_16BIT, 0xFFDB, AR023Z_I2C_ADDRESS }, + { 0xC8E0, CRL_REG_LEN_16BIT, 0xFFE7, AR023Z_I2C_ADDRESS }, + { 0xC8E2, CRL_REG_LEN_16BIT, 0xFF75, AR023Z_I2C_ADDRESS }, + { 0xC8E4, CRL_REG_LEN_16BIT, 0x01B8, AR023Z_I2C_ADDRESS }, + { 0xC8E6, CRL_REG_LEN_16BIT, 0xFFD2, AR023Z_I2C_ADDRESS }, + { 0xC8E8, CRL_REG_LEN_16BIT, 0xFF52, AR023Z_I2C_ADDRESS }, + { 0xC8EA, CRL_REG_LEN_16BIT, 0xFF1A, AR023Z_I2C_ADDRESS }, + { 0xC8EC, CRL_REG_LEN_16BIT, 0x0295, AR023Z_I2C_ADDRESS }, + { 0xC8EE, CRL_REG_LEN_16BIT, 0x01B0, AR023Z_I2C_ADDRESS }, + { 0xC8F0, CRL_REG_LEN_16BIT, 0xFF40, AR023Z_I2C_ADDRESS }, + { 0xC8F2, CRL_REG_LEN_16BIT, 0x0010, AR023Z_I2C_ADDRESS }, + { 0xC8F4, CRL_REG_LEN_16BIT, 0xFF87, AR023Z_I2C_ADDRESS }, + { 0xC8F6, CRL_REG_LEN_16BIT, 0x01A2, AR023Z_I2C_ADDRESS }, + { 0xC8F8, CRL_REG_LEN_16BIT, 0xFFD7, AR023Z_I2C_ADDRESS }, + { 0xC8FA, CRL_REG_LEN_16BIT, 0xFFD3, AR023Z_I2C_ADDRESS }, + { 0xC8FC, CRL_REG_LEN_16BIT, 0xFF63, AR023Z_I2C_ADDRESS }, + { 0xC8FE, CRL_REG_LEN_16BIT, 0x01CB, AR023Z_I2C_ADDRESS }, + { 0xC900, CRL_REG_LEN_16BIT, 0x0154, AR023Z_I2C_ADDRESS }, + { 0xC902, CRL_REG_LEN_16BIT, 0xFFCD, AR023Z_I2C_ADDRESS }, + { 0xC904, CRL_REG_LEN_16BIT, 0xFFDE, AR023Z_I2C_ADDRESS }, + { 0xC906, CRL_REG_LEN_16BIT, 0xFFB1, AR023Z_I2C_ADDRESS }, + { 0xC908, CRL_REG_LEN_16BIT, 0x013B, AR023Z_I2C_ADDRESS }, + { 0xC90A, CRL_REG_LEN_16BIT, 0xFFEC, AR023Z_I2C_ADDRESS }, + { 0xC90C, CRL_REG_LEN_16BIT, 0xFFD9, AR023Z_I2C_ADDRESS }, + { 0xC90E, CRL_REG_LEN_16BIT, 0xFF9C, AR023Z_I2C_ADDRESS }, + { 0xC910, CRL_REG_LEN_16BIT, 0x018B, AR023Z_I2C_ADDRESS }, + { 0xC97D, CRL_REG_LEN_08BIT, 0x10, AR023Z_I2C_ADDRESS }, + { 0xC92A, CRL_REG_LEN_16BIT, 0x0020, AR023Z_I2C_ADDRESS }, + { 0xC92C, CRL_REG_LEN_16BIT, 0x0018, AR023Z_I2C_ADDRESS }, + { 0xC92E, CRL_REG_LEN_16BIT, 0x0080, AR023Z_I2C_ADDRESS }, + { 0xC930, CRL_REG_LEN_16BIT, 0x0080, AR023Z_I2C_ADDRESS }, + { 0xC932, CRL_REG_LEN_16BIT, 0x0005, AR023Z_I2C_ADDRESS }, + { 0xC934, CRL_REG_LEN_16BIT, 0xFFE0, AR023Z_I2C_ADDRESS }, + { 0xC936, CRL_REG_LEN_08BIT, 0x33, AR023Z_I2C_ADDRESS }, + { 0xC937, CRL_REG_LEN_08BIT, 0x26, AR023Z_I2C_ADDRESS }, + { 0xC938, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC93A, CRL_REG_LEN_16BIT, 0x0047, AR023Z_I2C_ADDRESS }, + { 0xC93C, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC93E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC93E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC940, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC942, CRL_REG_LEN_16BIT, 0x0022, AR023Z_I2C_ADDRESS }, + { 0xC944, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC946, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC948, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC94A, CRL_REG_LEN_16BIT, 0x0002, AR023Z_I2C_ADDRESS }, + { 0xC94C, CRL_REG_LEN_16BIT, 0x3000, AR023Z_I2C_ADDRESS }, + { 0xC94E, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC950, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC952, CRL_REG_LEN_16BIT, 0x0123, AR023Z_I2C_ADDRESS }, + { 0xC954, CRL_REG_LEN_16BIT, 0x2000, AR023Z_I2C_ADDRESS }, + { 0xC956, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC958, CRL_REG_LEN_16BIT, 0x0150, AR023Z_I2C_ADDRESS }, + { 0xC95A, CRL_REG_LEN_16BIT, 0x5300, AR023Z_I2C_ADDRESS }, + { 0xC95C, CRL_REG_LEN_16BIT, 0x1112, AR023Z_I2C_ADDRESS }, + { 0xC95E, CRL_REG_LEN_16BIT, 0x2010, AR023Z_I2C_ADDRESS }, + { 0xC960, CRL_REG_LEN_16BIT, 0x5574, AR023Z_I2C_ADDRESS }, + { 0xC962, CRL_REG_LEN_16BIT, 0x5000, AR023Z_I2C_ADDRESS }, + { 0xC964, CRL_REG_LEN_16BIT, 0x0202, AR023Z_I2C_ADDRESS }, + { 0xC966, CRL_REG_LEN_16BIT, 0x5300, AR023Z_I2C_ADDRESS }, + { 0xC968, CRL_REG_LEN_16BIT, 0x0371, AR023Z_I2C_ADDRESS }, + { 0xC96A, CRL_REG_LEN_16BIT, 0x0400, AR023Z_I2C_ADDRESS }, + { 0xC96C, CRL_REG_LEN_16BIT, 0x0002, AR023Z_I2C_ADDRESS }, + { 0xC96E, CRL_REG_LEN_16BIT, 0x2000, AR023Z_I2C_ADDRESS }, + { 0xC970, CRL_REG_LEN_16BIT, 0x0023, AR023Z_I2C_ADDRESS }, + { 0xC972, CRL_REG_LEN_16BIT, 0x0330, AR023Z_I2C_ADDRESS }, + { 0xC974, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC976, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCC02, CRL_REG_LEN_16BIT, 0x0083, AR023Z_I2C_ADDRESS }, + { 0xC88C, CRL_REG_LEN_16BIT, 0x0080, AR023Z_I2C_ADDRESS }, + { 0xC84A, CRL_REG_LEN_16BIT, 0x0BA0, AR023Z_I2C_ADDRESS }, + { 0xC84C, CRL_REG_LEN_16BIT, 0x0FA0, AR023Z_I2C_ADDRESS }, + { 0xC84E, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0xCA0C, CRL_REG_LEN_16BIT, 0xF8C0, AR023Z_I2C_ADDRESS }, + { 0xC846, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCAE8, CRL_REG_LEN_16BIT, 0x0010, AR023Z_I2C_ADDRESS }, + { 0x3210, CRL_REG_LEN_16BIT, 0x0EB0, AR023Z_I2C_ADDRESS }, + { 0xBC02, CRL_REG_LEN_16BIT, 0x03C5, AR023Z_I2C_ADDRESS }, + { 0xA802, CRL_REG_LEN_16BIT, 0x001C, AR023Z_I2C_ADDRESS }, + { 0xA812, CRL_REG_LEN_08BIT, 0x08, AR023Z_I2C_ADDRESS }, + { 0xA81C, CRL_REG_LEN_08BIT, 0x8C, AR023Z_I2C_ADDRESS }, + { 0xC8CE, CRL_REG_LEN_16BIT, 0x0035, AR023Z_I2C_ADDRESS }, + { 0xC8CA, CRL_REG_LEN_16BIT, 0x0030, AR023Z_I2C_ADDRESS }, + { 0xC8CC, CRL_REG_LEN_16BIT, 0x0180, AR023Z_I2C_ADDRESS }, + { 0xC8C6, CRL_REG_LEN_16BIT, 0x008C, AR023Z_I2C_ADDRESS }, + { 0xC8C8, CRL_REG_LEN_16BIT, 0x03FF, AR023Z_I2C_ADDRESS }, + { 0xC8BE, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xA83C, CRL_REG_LEN_16BIT, 0x03E6, AR023Z_I2C_ADDRESS }, + { 0xA83E, CRL_REG_LEN_16BIT, 0x0300, AR023Z_I2C_ADDRESS }, + { 0xA840, CRL_REG_LEN_16BIT, 0x0133, AR023Z_I2C_ADDRESS }, + { 0xC988, CRL_REG_LEN_16BIT, 0x0E17, AR023Z_I2C_ADDRESS }, + { 0x2402, CRL_REG_LEN_16BIT, 0x0008, AR023Z_I2C_ADDRESS }, + { 0xBCBE, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + { 0xBCC0, CRL_REG_LEN_16BIT, 0x00C8, AR023Z_I2C_ADDRESS }, + { 0xBCBA, CRL_REG_LEN_16BIT, 0x0010, AR023Z_I2C_ADDRESS }, + { 0xBCBC, CRL_REG_LEN_16BIT, 0x0017, AR023Z_I2C_ADDRESS }, + { 0xBCC2, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xBCC4, CRL_REG_LEN_16BIT, 0x003B, AR023Z_I2C_ADDRESS }, + { 0xC9CC, CRL_REG_LEN_16BIT, 0xFD00, AR023Z_I2C_ADDRESS }, + { 0xC9CE, CRL_REG_LEN_16BIT, 0x0100, AR023Z_I2C_ADDRESS }, + { 0xC99A, CRL_REG_LEN_16BIT, 0x0600, AR023Z_I2C_ADDRESS }, + { 0xC99C, CRL_REG_LEN_16BIT, 0x0B00, AR023Z_I2C_ADDRESS }, + { 0xC9A0, CRL_REG_LEN_16BIT, 0x00C8, AR023Z_I2C_ADDRESS }, + { 0xC9A2, CRL_REG_LEN_16BIT, 0x0B54, AR023Z_I2C_ADDRESS }, + { 0x2414, CRL_REG_LEN_16BIT, 0x0BA0, AR023Z_I2C_ADDRESS }, + { 0x2416, CRL_REG_LEN_16BIT, 0x0FA0, AR023Z_I2C_ADDRESS }, + { 0x2418, CRL_REG_LEN_16BIT, 0xC350, AR023Z_I2C_ADDRESS }, + { 0x241A, CRL_REG_LEN_16BIT, 0xFA00, AR023Z_I2C_ADDRESS }, + { 0x241C, CRL_REG_LEN_16BIT, 0x0005, AR023Z_I2C_ADDRESS }, + { 0x241E, CRL_REG_LEN_16BIT, 0x0050, AR023Z_I2C_ADDRESS }, + { 0x2420, CRL_REG_LEN_16BIT, 0x00A5, AR023Z_I2C_ADDRESS }, + { 0x2422, CRL_REG_LEN_16BIT, 0x00A5, AR023Z_I2C_ADDRESS }, + { 0x2424, CRL_REG_LEN_16BIT, 0x00A5, AR023Z_I2C_ADDRESS }, + { 0x2426, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC996, CRL_REG_LEN_16BIT, 0x03E8, AR023Z_I2C_ADDRESS }, + { 0xC998, CRL_REG_LEN_16BIT, 0x03E8, AR023Z_I2C_ADDRESS }, + { 0xC98A, CRL_REG_LEN_16BIT, 0x000F, AR023Z_I2C_ADDRESS }, + { 0xC9E6, CRL_REG_LEN_16BIT, 0x0AF0, AR023Z_I2C_ADDRESS }, + { 0xCA2A, CRL_REG_LEN_08BIT, 0x32, AR023Z_I2C_ADDRESS }, + { 0xCA2B, CRL_REG_LEN_08BIT, 0x05, AR023Z_I2C_ADDRESS }, + { 0xCA2E, CRL_REG_LEN_08BIT, 0x32, AR023Z_I2C_ADDRESS }, + { 0xCA2F, CRL_REG_LEN_08BIT, 0x0A, AR023Z_I2C_ADDRESS }, + { 0x3222, CRL_REG_LEN_16BIT, 0x0912, AR023Z_I2C_ADDRESS }, + { 0x3224, CRL_REG_LEN_16BIT, 0x0612, AR023Z_I2C_ADDRESS }, + { 0xCAB4, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCAAE, CRL_REG_LEN_16BIT, 0x0022, AR023Z_I2C_ADDRESS }, + { 0x3414, CRL_REG_LEN_16BIT, 0x3700, AR023Z_I2C_ADDRESS }, + { 0x3408, CRL_REG_LEN_16BIT, 0x3700, AR023Z_I2C_ADDRESS }, + { 0x340C, CRL_REG_LEN_16BIT, 0x2A00, AR023Z_I2C_ADDRESS }, + { 0x3412, CRL_REG_LEN_16BIT, 0x0400, AR023Z_I2C_ADDRESS }, + { 0x3416, CRL_REG_LEN_16BIT, 0x0036, AR023Z_I2C_ADDRESS }, + { 0x341E, CRL_REG_LEN_16BIT, 0x0004, AR023Z_I2C_ADDRESS }, + { 0x3420, CRL_REG_LEN_16BIT, 0x2A3B, AR023Z_I2C_ADDRESS }, + { 0x341A, CRL_REG_LEN_16BIT, 0x0A00, AR023Z_I2C_ADDRESS }, + { 0x3400, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0x3402, CRL_REG_LEN_16BIT, 0x073B, AR023Z_I2C_ADDRESS }, + { 0x3406, CRL_REG_LEN_16BIT, 0x0500, AR023Z_I2C_ADDRESS }, + { 0x3404, CRL_REG_LEN_16BIT, 0x3E1E, AR023Z_I2C_ADDRESS }, + { 0x3454, CRL_REG_LEN_16BIT, 0x0004, AR023Z_I2C_ADDRESS }, + { 0x3432, CRL_REG_LEN_16BIT, 0x000B, AR023Z_I2C_ADDRESS }, + { 0x3452, CRL_REG_LEN_16BIT, 0x000B, AR023Z_I2C_ADDRESS }, + { 0x345A, CRL_REG_LEN_16BIT, 0x000B, AR023Z_I2C_ADDRESS }, + { 0x3462, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0x344A, CRL_REG_LEN_16BIT, 0x0007, AR023Z_I2C_ADDRESS }, + { 0x342E, CRL_REG_LEN_16BIT, 0x0006, AR023Z_I2C_ADDRESS }, + { 0xCA20, CRL_REG_LEN_16BIT, 0x0100, AR023Z_I2C_ADDRESS }, + { 0xCA22, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0xCA24, CRL_REG_LEN_16BIT, 0x0C80, AR023Z_I2C_ADDRESS }, + { 0xCA26, CRL_REG_LEN_16BIT, 0x002D, AR023Z_I2C_ADDRESS }, + { 0xCA78, CRL_REG_LEN_16BIT, 0x0030, AR023Z_I2C_ADDRESS }, + { 0xCA80, CRL_REG_LEN_16BIT, 0x0056, AR023Z_I2C_ADDRESS }, + { 0xCA88, CRL_REG_LEN_16BIT, 0x0100, AR023Z_I2C_ADDRESS }, + { 0xCA90, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xCA7A, CRL_REG_LEN_16BIT, 0x002D, AR023Z_I2C_ADDRESS }, + { 0xCA7C, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCA82, CRL_REG_LEN_16BIT, 0x0050, AR023Z_I2C_ADDRESS }, + { 0xCA84, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCA8A, CRL_REG_LEN_16BIT, 0x00B8, AR023Z_I2C_ADDRESS }, + { 0xCA8C, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCA92, CRL_REG_LEN_16BIT, 0x0173, AR023Z_I2C_ADDRESS }, + { 0xCA94, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCB20, CRL_REG_LEN_16BIT, 0x002D, AR023Z_I2C_ADDRESS }, + { 0xCB22, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCB24, CRL_REG_LEN_16BIT, 0x0050, AR023Z_I2C_ADDRESS }, + { 0xCB26, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCB28, CRL_REG_LEN_16BIT, 0x00B8, AR023Z_I2C_ADDRESS }, + { 0xCB2A, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCB2C, CRL_REG_LEN_16BIT, 0x0180, AR023Z_I2C_ADDRESS }, + { 0xCB2E, CRL_REG_LEN_16BIT, 0x007D, AR023Z_I2C_ADDRESS }, + { 0xCB40, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCB42, CRL_REG_LEN_16BIT, 0x07D0, AR023Z_I2C_ADDRESS }, + { 0xCB44, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xCB46, CRL_REG_LEN_16BIT, 0x0DAC, AR023Z_I2C_ADDRESS }, + { 0xCA70, CRL_REG_LEN_16BIT, 0x0003, AR023Z_I2C_ADDRESS }, + { 0xCA72, CRL_REG_LEN_16BIT, 0x0003, AR023Z_I2C_ADDRESS }, + { 0xCA74, CRL_REG_LEN_16BIT, 0x03E8, AR023Z_I2C_ADDRESS }, + { 0xCA76, CRL_REG_LEN_16BIT, 0x0D00, AR023Z_I2C_ADDRESS }, + { 0xCA42, CRL_REG_LEN_08BIT, 0x02, AR023Z_I2C_ADDRESS }, + { 0xCA43, CRL_REG_LEN_08BIT, 0x16, AR023Z_I2C_ADDRESS }, + { 0xCA48, CRL_REG_LEN_08BIT, 0x02, AR023Z_I2C_ADDRESS }, + { 0xCA49, CRL_REG_LEN_08BIT, 0x16, AR023Z_I2C_ADDRESS }, + { 0xCA4E, CRL_REG_LEN_08BIT, 0x14, AR023Z_I2C_ADDRESS }, + { 0xCA4F, CRL_REG_LEN_08BIT, 0x04, AR023Z_I2C_ADDRESS }, + { 0xCA5E, CRL_REG_LEN_08BIT, 0x01, AR023Z_I2C_ADDRESS }, + { 0xCA5F, CRL_REG_LEN_08BIT, 0x16, AR023Z_I2C_ADDRESS }, + { 0xCA64, CRL_REG_LEN_08BIT, 0x01, AR023Z_I2C_ADDRESS }, + { 0xCA65, CRL_REG_LEN_08BIT, 0x16, AR023Z_I2C_ADDRESS }, + { 0xCA6A, CRL_REG_LEN_08BIT, 0x1E, AR023Z_I2C_ADDRESS }, + { 0xCA6B, CRL_REG_LEN_08BIT, 0x05, AR023Z_I2C_ADDRESS }, + { 0xBC0A, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xBC0C, CRL_REG_LEN_16BIT, 0x000A, AR023Z_I2C_ADDRESS }, + { 0xBC0E, CRL_REG_LEN_16BIT, 0x000B, AR023Z_I2C_ADDRESS }, + { 0xBC10, CRL_REG_LEN_16BIT, 0x001A, AR023Z_I2C_ADDRESS }, + { 0xBC12, CRL_REG_LEN_16BIT, 0x0027, AR023Z_I2C_ADDRESS }, + { 0xBC14, CRL_REG_LEN_16BIT, 0x0032, AR023Z_I2C_ADDRESS }, + { 0xBC16, CRL_REG_LEN_16BIT, 0x003D, AR023Z_I2C_ADDRESS }, + { 0xBC18, CRL_REG_LEN_16BIT, 0x0046, AR023Z_I2C_ADDRESS }, + { 0xBC1A, CRL_REG_LEN_16BIT, 0x004F, AR023Z_I2C_ADDRESS }, + { 0xBC1C, CRL_REG_LEN_16BIT, 0x005F, AR023Z_I2C_ADDRESS }, + { 0xBC1E, CRL_REG_LEN_16BIT, 0x006D, AR023Z_I2C_ADDRESS }, + { 0xBC20, CRL_REG_LEN_16BIT, 0x007A, AR023Z_I2C_ADDRESS }, + { 0xBC22, CRL_REG_LEN_16BIT, 0x0087, AR023Z_I2C_ADDRESS }, + { 0xBC24, CRL_REG_LEN_16BIT, 0x009D, AR023Z_I2C_ADDRESS }, + { 0xBC26, CRL_REG_LEN_16BIT, 0x00B1, AR023Z_I2C_ADDRESS }, + { 0xBC28, CRL_REG_LEN_16BIT, 0x00C4, AR023Z_I2C_ADDRESS }, + { 0xBC2A, CRL_REG_LEN_16BIT, 0x00D6, AR023Z_I2C_ADDRESS }, + { 0xBC2C, CRL_REG_LEN_16BIT, 0x00F5, AR023Z_I2C_ADDRESS }, + { 0xBC2E, CRL_REG_LEN_16BIT, 0x0112, AR023Z_I2C_ADDRESS }, + { 0xBC30, CRL_REG_LEN_16BIT, 0x012D, AR023Z_I2C_ADDRESS }, + { 0xBC32, CRL_REG_LEN_16BIT, 0x0145, AR023Z_I2C_ADDRESS }, + { 0xBC34, CRL_REG_LEN_16BIT, 0x0172, AR023Z_I2C_ADDRESS }, + { 0xBC36, CRL_REG_LEN_16BIT, 0x019B, AR023Z_I2C_ADDRESS }, + { 0xBC38, CRL_REG_LEN_16BIT, 0x01C1, AR023Z_I2C_ADDRESS }, + { 0xBC3A, CRL_REG_LEN_16BIT, 0x01E3, AR023Z_I2C_ADDRESS }, + { 0xBC3C, CRL_REG_LEN_16BIT, 0x0223, AR023Z_I2C_ADDRESS }, + { 0xBC3E, CRL_REG_LEN_16BIT, 0x025D, AR023Z_I2C_ADDRESS }, + { 0xBC40, CRL_REG_LEN_16BIT, 0x0292, AR023Z_I2C_ADDRESS }, + { 0xBC42, CRL_REG_LEN_16BIT, 0x02C3, AR023Z_I2C_ADDRESS }, + { 0xBC44, CRL_REG_LEN_16BIT, 0x031D, AR023Z_I2C_ADDRESS }, + { 0xBC46, CRL_REG_LEN_16BIT, 0x036F, AR023Z_I2C_ADDRESS }, + { 0xBC48, CRL_REG_LEN_16BIT, 0x03B9, AR023Z_I2C_ADDRESS }, + { 0xBC4A, CRL_REG_LEN_16BIT, 0x03FF, AR023Z_I2C_ADDRESS }, + { 0xBC4C, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xBC4E, CRL_REG_LEN_16BIT, 0x0002, AR023Z_I2C_ADDRESS }, + { 0xBC50, CRL_REG_LEN_16BIT, 0x0004, AR023Z_I2C_ADDRESS }, + { 0xBC52, CRL_REG_LEN_16BIT, 0x0007, AR023Z_I2C_ADDRESS }, + { 0xBC54, CRL_REG_LEN_16BIT, 0x0009, AR023Z_I2C_ADDRESS }, + { 0xBC56, CRL_REG_LEN_16BIT, 0x000B, AR023Z_I2C_ADDRESS }, + { 0xBC58, CRL_REG_LEN_16BIT, 0x000D, AR023Z_I2C_ADDRESS }, + { 0xBC5A, CRL_REG_LEN_16BIT, 0x000F, AR023Z_I2C_ADDRESS }, + { 0xBC5C, CRL_REG_LEN_16BIT, 0x0011, AR023Z_I2C_ADDRESS }, + { 0xBC5E, CRL_REG_LEN_16BIT, 0x0016, AR023Z_I2C_ADDRESS }, + { 0xBC60, CRL_REG_LEN_16BIT, 0x001A, AR023Z_I2C_ADDRESS }, + { 0xBC62, CRL_REG_LEN_16BIT, 0x001F, AR023Z_I2C_ADDRESS }, + { 0xBC64, CRL_REG_LEN_16BIT, 0x0023, AR023Z_I2C_ADDRESS }, + { 0xBC66, CRL_REG_LEN_16BIT, 0x002C, AR023Z_I2C_ADDRESS }, + { 0xBC68, CRL_REG_LEN_16BIT, 0x0034, AR023Z_I2C_ADDRESS }, + { 0xBC6A, CRL_REG_LEN_16BIT, 0x003D, AR023Z_I2C_ADDRESS }, + { 0xBC6C, CRL_REG_LEN_16BIT, 0x0046, AR023Z_I2C_ADDRESS }, + { 0xBC6E, CRL_REG_LEN_16BIT, 0x0057, AR023Z_I2C_ADDRESS }, + { 0xBC70, CRL_REG_LEN_16BIT, 0x0069, AR023Z_I2C_ADDRESS }, + { 0xBC72, CRL_REG_LEN_16BIT, 0x007A, AR023Z_I2C_ADDRESS }, + { 0xBC74, CRL_REG_LEN_16BIT, 0x008C, AR023Z_I2C_ADDRESS }, + { 0xBC76, CRL_REG_LEN_16BIT, 0x00AF, AR023Z_I2C_ADDRESS }, + { 0xBC78, CRL_REG_LEN_16BIT, 0x00D2, AR023Z_I2C_ADDRESS }, + { 0xBC7A, CRL_REG_LEN_16BIT, 0x00F5, AR023Z_I2C_ADDRESS }, + { 0xBC7C, CRL_REG_LEN_16BIT, 0x0118, AR023Z_I2C_ADDRESS }, + { 0xBC7E, CRL_REG_LEN_16BIT, 0x015E, AR023Z_I2C_ADDRESS }, + { 0xBC80, CRL_REG_LEN_16BIT, 0x01A4, AR023Z_I2C_ADDRESS }, + { 0xBC82, CRL_REG_LEN_16BIT, 0x01EA, AR023Z_I2C_ADDRESS }, + { 0xBC84, CRL_REG_LEN_16BIT, 0x022F, AR023Z_I2C_ADDRESS }, + { 0xBC86, CRL_REG_LEN_16BIT, 0x02B4, AR023Z_I2C_ADDRESS }, + { 0xBC88, CRL_REG_LEN_16BIT, 0x032B, AR023Z_I2C_ADDRESS }, + { 0xBC8A, CRL_REG_LEN_16BIT, 0x0399, AR023Z_I2C_ADDRESS }, + { 0xBC8C, CRL_REG_LEN_16BIT, 0x03FF, AR023Z_I2C_ADDRESS }, + { 0xCA30, CRL_REG_LEN_16BIT, 0x0B00, AR023Z_I2C_ADDRESS }, + { 0xCA32, CRL_REG_LEN_16BIT, 0x0100, AR023Z_I2C_ADDRESS }, + { 0xCA08, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xC9C0, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xC9C2, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0xC9C8, CRL_REG_LEN_16BIT, 0x0080, AR023Z_I2C_ADDRESS }, + { 0xC9CA, CRL_REG_LEN_16BIT, 0x0800, AR023Z_I2C_ADDRESS }, + { 0xC9BC, CRL_REG_LEN_16BIT, 0x0028, AR023Z_I2C_ADDRESS }, + { 0xC9BE, CRL_REG_LEN_16BIT, 0x0023, AR023Z_I2C_ADDRESS }, + { 0xC9C4, CRL_REG_LEN_16BIT, 0x0046, AR023Z_I2C_ADDRESS }, + { 0xC9C6, CRL_REG_LEN_16BIT, 0x0046, AR023Z_I2C_ADDRESS }, + { 0xC9A4, CRL_REG_LEN_16BIT, 0x0002, AR023Z_I2C_ADDRESS }, + { 0xC9A6, CRL_REG_LEN_16BIT, 0x001E, AR023Z_I2C_ADDRESS }, + { 0xCA2C, CRL_REG_LEN_08BIT, 0x01, AR023Z_I2C_ADDRESS }, + { 0xCA2D, CRL_REG_LEN_08BIT, 0x03, AR023Z_I2C_ADDRESS }, + { 0xCA9C, CRL_REG_LEN_16BIT, 0x0700, AR023Z_I2C_ADDRESS }, + { 0xCAA8, CRL_REG_LEN_16BIT, 0x0100, AR023Z_I2C_ADDRESS }, + { 0xCAA4, CRL_REG_LEN_16BIT, 0x01C0, AR023Z_I2C_ADDRESS }, + { 0xCAB0, CRL_REG_LEN_16BIT, 0x00B3, AR023Z_I2C_ADDRESS }, + { 0xCA28, CRL_REG_LEN_08BIT, 0x5A, AR023Z_I2C_ADDRESS }, + { 0xA82C, CRL_REG_LEN_16BIT, 0x0880, AR023Z_I2C_ADDRESS }, + { 0xA82E, CRL_REG_LEN_16BIT, 0x095A, AR023Z_I2C_ADDRESS }, + { 0xA830, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xA832, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xA834, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xA836, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xA838, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xA83A, CRL_REG_LEN_16BIT, 0x0980, AR023Z_I2C_ADDRESS }, + { 0xC88C, CRL_REG_LEN_16BIT, 0x0080, AR023Z_I2C_ADDRESS }, + { 0xB00C, CRL_REG_LEN_08BIT, 0x00, AR023Z_I2C_ADDRESS }, + { 0xC8BE, CRL_REG_LEN_16BIT, 0x0000, AR023Z_I2C_ADDRESS }, + { 0xB00D, CRL_REG_LEN_08BIT, 0x1E, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D00, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D01, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x3028, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0200, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x2000, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D06, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D08, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8D02, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x0001, AR023Z_I2C_ADDRESS }, + { 0xFC02, CRL_REG_LEN_16BIT, 0x0101, AR023Z_I2C_ADDRESS }, + { 0xFC04, CRL_REG_LEN_16BIT, 0x0101, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8102, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, + { 0xFC00, CRL_REG_LEN_16BIT, 0x2800, AR023Z_I2C_ADDRESS }, + { 0x0040, CRL_REG_LEN_16BIT, 0x8100, AR023Z_I2C_ADDRESS }, + { 0x00, CRL_REG_LEN_DELAY, 20, 0x00 }, +}; + +struct crl_sensor_detect_config ar023z_sensor_detect_regset[] = { + { + .reg = { 0x0000, CRL_REG_LEN_16BIT, 0xFFFF, TC358778_I2C_ADDRESS }, + .width = 15, + }, + { + .reg = { 0x0000, CRL_REG_LEN_16BIT, 0xFFFF, AR023Z_I2C_ADDRESS }, + .width = 16, + }, +}; + +struct crl_pll_configuration ar023z_pll_configurations[] = { + { + .input_clk = 27000000, + .op_sys_clk = 317250000, + .bitsperpixel = 16, + .pixel_rate_csi = 79312500, + .pixel_rate_pa = 79312500, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +struct crl_sensor_subdev_config ar023z_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ar023z binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ar023z pixel array", + }, +}; + +struct crl_register_write_rep ar023z_poweroff_regset[] = { + {0xFC00, CRL_REG_LEN_16BIT, 0x5000, AR023Z_I2C_ADDRESS}, + {0x0040, CRL_REG_LEN_16BIT, 0x8100, AR023Z_I2C_ADDRESS}, + {0x0002, CRL_REG_LEN_16BIT, 0x0001, TC358778_I2C_ADDRESS}, +}; + +struct crl_register_write_rep ar023z_streamon_regs[] = { + /* Turn on D-Phy clock and enable MIPI lanes 2 and 3 */ + {0x0140, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS}, /* CLK On */ + {0x0144, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS}, /* lane 0 */ + {0x0148, CRL_REG_LEN_32BIT, 0x00000000, TC358778_I2C_ADDRESS}, /* lane 1 */ + {0x014C, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 2 */ + {0x0150, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 3 */ +}; + +struct crl_register_write_rep ar023z_streamoff_regs[] = { + {0x0140, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* CLK Off */ + {0x0144, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 0 */ + {0x0148, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 1 */ + {0x014C, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 2 */ + {0x0150, CRL_REG_LEN_32BIT, 0x00010000, TC358778_I2C_ADDRESS}, /* lane 3 */ +}; + +struct crl_subdev_rect_rep ar023z_1920_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + } +}; + +struct crl_mode_rep ar023z_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ar023z_1920_1080_rects), + .sd_rects = ar023z_1920_1080_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .min_llp = 2350, + .min_fll = 1320, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ar023z_1920_1080), + .mode_regs = ar023z_1920_1080, + }, +}; + +struct crl_csi_data_fmt ar023z_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, +}; + +struct crl_sensor_limits ar023z_mipi_bridge_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 320, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 380, + .max_line_length_pixels = 32752, +}; + +/* Power items, they are enabled in the order they are listed here */ +struct crl_power_seq_entity ar023z_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 27000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +struct crl_v4l2_ctrl ar023z_v4l2_ctrls[] ={ + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_sensor_configuration ar023z_crl_configuration = { + .power_items = ARRAY_SIZE(ar023z_power_items), + .power_entities = ar023z_power_items, + + .poweroff_regs_items = ARRAY_SIZE(ar023z_poweroff_regset), + .poweroff_regs = ar023z_poweroff_regset, + + .id_reg_items = ARRAY_SIZE(ar023z_sensor_detect_regset), + .id_regs = ar023z_sensor_detect_regset, + + .onetime_init_regs_items = 0, + .onetime_init_regs = NULL, + + .subdev_items = ARRAY_SIZE(ar023z_sensor_subdevs), + .subdevs = ar023z_sensor_subdevs, + + .sensor_limits = &ar023z_mipi_bridge_limits, + + .pll_config_items = ARRAY_SIZE(ar023z_pll_configurations), + .pll_configs = ar023z_pll_configurations, + + .modes_items = ARRAY_SIZE(ar023z_modes), + .modes = ar023z_modes, + + .streamon_regs_items = ARRAY_SIZE(ar023z_streamon_regs), + .streamon_regs = ar023z_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ar023z_streamoff_regs), + .streamoff_regs = ar023z_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ar023z_v4l2_ctrls), + .v4l2_ctrl_bank = ar023z_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ar023z_crl_csi_data_fmt), + .csi_fmts = ar023z_crl_csi_data_fmt, + + .flip_items = 0, + .flip_data = NULL, + + .frame_desc_entries = 0, + .frame_desc_type = 0, + .frame_desc = 0, +}; + +#endif /* __CRLMODULE_AR023Z_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx132_configuration.h b/drivers/media/i2c/crlmodule/crl_imx132_configuration.h new file mode 100644 index 000000000000..128ccb50e4f8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx132_configuration.h @@ -0,0 +1,699 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation */ + +#ifndef __CRLMODULE_IMX132_CONFIGURATION_H_ +#define __CRLMODULE_IMX132_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +struct crl_register_write_rep imx132_powerup_regset[] = { + { 0x3087, CRL_REG_LEN_08BIT, 0x53 }, + { 0x308B, CRL_REG_LEN_08BIT, 0x5A }, + { 0x3094, CRL_REG_LEN_08BIT, 0x11 }, + { 0x309D, CRL_REG_LEN_08BIT, 0xA4 }, + { 0x30AA, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30C6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30C7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3118, CRL_REG_LEN_08BIT, 0x2F }, + { 0x312A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x312B, CRL_REG_LEN_08BIT, 0x0B }, + { 0x312C, CRL_REG_LEN_08BIT, 0x0B }, + { 0x312D, CRL_REG_LEN_08BIT, 0x13 }, + { 0x303D, CRL_REG_LEN_08BIT, 0x10 }, + { 0x303E, CRL_REG_LEN_08BIT, 0x5A }, + { 0x3040, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3041, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3048, CRL_REG_LEN_08BIT, 0x00 }, + { 0x304C, CRL_REG_LEN_08BIT, 0x2F }, + { 0x304D, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3064, CRL_REG_LEN_08BIT, 0x92 }, + { 0x306A, CRL_REG_LEN_08BIT, 0x10 }, + { 0x309B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x309E, CRL_REG_LEN_08BIT, 0x41 }, + { 0x30A0, CRL_REG_LEN_08BIT, 0x10 }, + { 0x30A1, CRL_REG_LEN_08BIT, 0x0B }, + { 0x30B2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30D5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30D6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30D7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30D8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30D9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30DA, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30DB, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30DC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30DD, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30DE, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3102, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3103, CRL_REG_LEN_08BIT, 0x33 }, + { 0x3104, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3105, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3106, CRL_REG_LEN_08BIT, 0x65 }, + { 0x3107, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3108, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3109, CRL_REG_LEN_08BIT, 0x04 }, + { 0x310A, CRL_REG_LEN_08BIT, 0x04 }, + { 0x315C, CRL_REG_LEN_08BIT, 0x3D }, + { 0x315D, CRL_REG_LEN_08BIT, 0x3C }, + { 0x316E, CRL_REG_LEN_08BIT, 0x3E }, + { 0x316F, CRL_REG_LEN_08BIT, 0x3D }, + { 0x020e, CRL_REG_LEN_16BIT, 0x0100 }, + { 0x0210, CRL_REG_LEN_16BIT, 0x01a0 }, + { 0x0212, CRL_REG_LEN_16BIT, 0x0200 }, + { 0x0214, CRL_REG_LEN_16BIT, 0x0100 }, + { 0x0204, CRL_REG_LEN_16BIT, 0x0000 }, + { 0x0202, CRL_REG_LEN_16BIT, 0x0000 }, + { 0x0600, CRL_REG_LEN_16BIT, 0x0000 }, + { 0x0602, CRL_REG_LEN_16BIT, 0x03ff }, + { 0x0604, CRL_REG_LEN_16BIT, 0x03ff }, + { 0x0606, CRL_REG_LEN_16BIT, 0x03ff }, + { 0x0608, CRL_REG_LEN_16BIT, 0x03ff }, + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, +}; + +/* + .input_clk = 24000000, + .op_sys_clk = 405000000, + .bitsperpixel = 10, + .pixel_rate_csi = 810000000, + .pixel_rate_pa = 768000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx132_pll_384), + .pll_regs = imx132_pll_384, +*/ +struct crl_register_write_rep imx132_pll_405[] = { + /* PLL setting */ + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x87 }, + { 0x30A4, CRL_REG_LEN_08BIT, 0x01 }, + { 0x303C, CRL_REG_LEN_08BIT, 0x4B }, + /* Global timing */ + { 0x3304, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3305, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3306, CRL_REG_LEN_08BIT, 0x19 }, + { 0x3307, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3308, CRL_REG_LEN_08BIT, 0x0F }, + { 0x3309, CRL_REG_LEN_08BIT, 0x07 }, + { 0x330A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x330B, CRL_REG_LEN_08BIT, 0x06 }, + { 0x330C, CRL_REG_LEN_08BIT, 0x0B }, + { 0x330D, CRL_REG_LEN_08BIT, 0x07 }, + { 0x330E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3318, CRL_REG_LEN_08BIT, 0x62 }, + { 0x3322, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3342, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3348, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3301, CRL_REG_LEN_08BIT, 0x00 }, /* Lanes = 2*/ +}; + +struct crl_register_write_rep imx132_pll_312[] = { + /* PLL setting */ + { 0x0305, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x0D }, + { 0x30A4, CRL_REG_LEN_08BIT, 0x02 }, + { 0x303C, CRL_REG_LEN_08BIT, 0x4B }, + /* Global timing */ + { 0x3304, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3305, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3306, CRL_REG_LEN_08BIT, 0x19 }, + { 0x3307, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3308, CRL_REG_LEN_08BIT, 0x0F }, + { 0x3309, CRL_REG_LEN_08BIT, 0x07 }, + { 0x330A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x330B, CRL_REG_LEN_08BIT, 0x06 }, + { 0x330C, CRL_REG_LEN_08BIT, 0x0B }, + { 0x330D, CRL_REG_LEN_08BIT, 0x07 }, + { 0x330E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3318, CRL_REG_LEN_08BIT, 0x62 }, + { 0x3322, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3342, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3348, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3301, CRL_REG_LEN_08BIT, 0x01 }, /* Lanes = 1*/ +}; + +struct crl_register_write_rep imx132_mode_1080P[] = { + {0x0344, CRL_REG_LEN_08BIT, 0x00}, + {0x0345, CRL_REG_LEN_08BIT, 0x14}, + {0x0346, CRL_REG_LEN_08BIT, 0x00}, + {0x0347, CRL_REG_LEN_08BIT, 0x32}, + {0x0348, CRL_REG_LEN_08BIT, 0x07}, + {0x0349, CRL_REG_LEN_08BIT, 0xA3}, + {0x034A, CRL_REG_LEN_08BIT, 0x04}, + {0x034B, CRL_REG_LEN_08BIT, 0x79}, + {0x034C, CRL_REG_LEN_08BIT, 0x07}, + {0x034D, CRL_REG_LEN_08BIT, 0x90}, + {0x034E, CRL_REG_LEN_08BIT, 0x04}, + {0x034F, CRL_REG_LEN_08BIT, 0x48}, + {0x0381, CRL_REG_LEN_08BIT, 0x01}, + {0x0383, CRL_REG_LEN_08BIT, 0x01}, + {0x0385, CRL_REG_LEN_08BIT, 0x01}, + {0x0387, CRL_REG_LEN_08BIT, 0x01}, +}; + +struct crl_register_write_rep imx132_mode_1636x1096[] = { + {0x0344, CRL_REG_LEN_08BIT, 0x00}, + {0x0345, CRL_REG_LEN_08BIT, 0xAA}, + {0x0346, CRL_REG_LEN_08BIT, 0x00}, + {0x0347, CRL_REG_LEN_08BIT, 0x32}, + {0x0348, CRL_REG_LEN_08BIT, 0x07}, + {0x0349, CRL_REG_LEN_08BIT, 0x0D}, + {0x034A, CRL_REG_LEN_08BIT, 0x04}, + {0x034B, CRL_REG_LEN_08BIT, 0x79}, + {0x034C, CRL_REG_LEN_08BIT, 0x06}, + {0x034D, CRL_REG_LEN_08BIT, 0x64}, + {0x034E, CRL_REG_LEN_08BIT, 0x04}, + {0x034F, CRL_REG_LEN_08BIT, 0x48}, + {0x0381, CRL_REG_LEN_08BIT, 0x01}, + {0x0383, CRL_REG_LEN_08BIT, 0x01}, + {0x0385, CRL_REG_LEN_08BIT, 0x01}, + {0x0387, CRL_REG_LEN_08BIT, 0x01}, +}; + +struct crl_register_write_rep imx132_fll_regs[] = { + { 0x0340, CRL_REG_LEN_16BIT, 0x045c }, /* LLP and FLL */ +}; + +struct crl_register_write_rep imx132_llp_regs[] = { + { 0x0342, CRL_REG_LEN_16BIT, 0x08fc }, /* LLP and FLL */ +}; + +struct crl_register_write_rep imx132_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +struct crl_register_write_rep imx132_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +struct crl_register_write_rep imx132_data_fmt_width10[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0a0a } +}; + +struct crl_register_write_rep imx132_data_fmt_width8[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0808 } +}; + +struct crl_subdev_rect_rep imx132_1080P_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1976, + .in_rect.height = 1200, + .out_rect.left = 20, + .out_rect.top = 50, + .out_rect.width = 1936, + .out_rect.height = 1096, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1936, + .in_rect.height = 1096, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1936, + .out_rect.height = 1096, + }, +}; + +struct crl_subdev_rect_rep imx132_1636x1096_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1976, + .in_rect.height = 1200, + .out_rect.left = 170, + .out_rect.top = 50, + .out_rect.width = 1636, + .out_rect.height = 1096, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1636, + .in_rect.height = 1096, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1636, + .out_rect.height = 1096, + }, +}; + +struct crl_mode_rep imx132_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx132_1636x1096_rects), + .sd_rects = imx132_1636x1096_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1636, + .height = 1096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx132_mode_1636x1096), + .mode_regs = imx132_mode_1636x1096, + }, + { + .sd_rects_items = ARRAY_SIZE(imx132_1080P_rects), + .sd_rects = imx132_1080P_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1936, + .height = 1096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx132_mode_1080P), + .mode_regs = imx132_mode_1080P, + }, +}; + +struct crl_register_write_rep imx132_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + +struct crl_sensor_detect_config imx132_sensor_detect_regset[] = { + { + .reg = { 0x0003, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0000, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +struct crl_sensor_subdev_config imx132_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx132 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx132 pixel array", + }, +}; + +struct crl_pll_configuration imx132_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 312000000, + .bitsperpixel = 8, + .pixel_rate_csi = 624000000, + .pixel_rate_pa = 624000000, + .csi_lanes = 1, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx132_pll_312), + .pll_regs = imx132_pll_312, + }, + { + .input_clk = 24000000, + .op_sys_clk = 405000000, + .bitsperpixel = 10, + .pixel_rate_csi = 810000000, + .pixel_rate_pa = 810000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx132_pll_405), + .pll_regs = imx132_pll_405, + }, +}; + +struct crl_sensor_limits imx132_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1976, + .y_addr_max = 1200, + .min_frame_length_lines = 202, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 560, + .max_line_length_pixels = 65520, +}; + +struct crl_flip_data imx132_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +struct crl_csi_data_fmt imx132_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = imx132_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx132_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx132_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx132_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx132_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx132_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx132_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx132_data_fmt_width8, + }, +}; + +struct crl_dynamic_register_access imx132_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + }, +}; + + +struct crl_dynamic_register_access imx132_ana_gain_global_regs[] = { + { + .address = 0x0204, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + }, +}; + +struct crl_dynamic_register_access imx132_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + } +}; + +struct crl_dynamic_register_access imx132_vblank_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + }, +}; + +struct crl_dynamic_register_access imx132_hblank_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + }, +}; + +static struct crl_dynamic_register_access imx132_test_pattern_regs[] = { + { + .address = 0x0600, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + }, +}; + +static const char * const imx132_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Fade to Gray", + "PN9", +}; + +struct crl_v4l2_ctrl imx132_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 220, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_ana_gain_global_regs), + .regs = imx132_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_exposure_regs), + .regs = imx132_exposure_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_flip_regs), + .regs = imx132_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_flip_regs), + .regs = imx132_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VBLANK, + .name = "V4L2_CID_VBLANK", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = -65535, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_vblank_regs), + .regs = imx132_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_HBLANK, + .name = "V4L2_CID_HBLANK", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx132_hblank_regs), + .regs = imx132_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = imx132_test_patterns, + .data.v4l2_menu_items.size = ARRAY_SIZE(imx132_test_patterns), + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx132_test_pattern_regs), + .regs = imx132_test_pattern_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_sensor_configuration imx132_crl_configuration = { + + .powerup_regs_items = ARRAY_SIZE(imx132_powerup_regset), + .powerup_regs = imx132_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(imx132_poweroff_regset), + .poweroff_regs = imx132_poweroff_regset, + + .id_reg_items = ARRAY_SIZE(imx132_sensor_detect_regset), + .id_regs = imx132_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx132_sensor_subdevs), + .subdevs = imx132_sensor_subdevs, + + .sensor_limits = &imx132_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx132_pll_configurations), + .pll_configs = imx132_pll_configurations, + + .modes_items = ARRAY_SIZE(imx132_modes), + .modes = imx132_modes, + + .streamon_regs_items = ARRAY_SIZE(imx132_streamon_regs), + .streamon_regs = imx132_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx132_streamoff_regs), + .streamoff_regs = imx132_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx132_v4l2_ctrls), + .v4l2_ctrl_bank = imx132_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx132_crl_csi_data_fmt), + .csi_fmts = imx132_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx132_flip_configurations), + .flip_data = imx132_flip_configurations, +}; + +#endif /* __CRLMODULE_IMX132_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx135_configuration.h b/drivers/media/i2c/crlmodule/crl_imx135_configuration.h new file mode 100644 index 000000000000..26a38b14864a --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx135_configuration.h @@ -0,0 +1,779 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation */ + +#ifndef __CRLMODULE_IMX135_CONFIGURATION_H_ +#define __CRLMODULE_IMX135_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +/* MIPI 451.2MHz 902.4mbps PIXCLK: 360.96MHz */ +static struct crl_register_write_rep imx135_pll_451[] = { + { 0x011e, CRL_REG_LEN_08BIT, 0x13 }, /* This is not correct for 24MHz* */ + { 0x011f, CRL_REG_LEN_08BIT, 0x33 }, /* But it is that way in vendor sheets */ + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x0f }, + { 0x0309, CRL_REG_LEN_08BIT, 0x05 }, + { 0x030b, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030c, CRL_REG_LEN_08BIT, 0x02 }, + { 0x030d, CRL_REG_LEN_08BIT, 0x34 }, + { 0x030e, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3a06, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0830, CRL_REG_LEN_08BIT, 0x87 }, + { 0x0831, CRL_REG_LEN_08BIT, 0x3f }, + { 0x0832, CRL_REG_LEN_08BIT, 0x67 }, + { 0x0833, CRL_REG_LEN_08BIT, 0x3f }, + { 0x0834, CRL_REG_LEN_08BIT, 0x3f }, + { 0x0835, CRL_REG_LEN_08BIT, 0x4f }, + { 0x0836, CRL_REG_LEN_08BIT, 0xdf }, + { 0x0837, CRL_REG_LEN_08BIT, 0x47 }, + { 0x0839, CRL_REG_LEN_08BIT, 0x1f }, + { 0x083a, CRL_REG_LEN_08BIT, 0x17 }, + { 0x083b, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0108, CRL_REG_LEN_08BIT, 0x03 }, /* CSI lane */ +}; + + +static struct crl_register_write_rep imx135_powerup_regset[] = { + { 0x0101, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0105, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0110, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3302, CRL_REG_LEN_08BIT, 0x11 }, + { 0x3833, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3893, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3906, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3907, CRL_REG_LEN_08BIT, 0x01 }, + { 0x391B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3C09, CRL_REG_LEN_08BIT, 0x01 }, + { 0x600A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3008, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x320A, CRL_REG_LEN_08BIT, 0x01 }, + { 0x320D, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3216, CRL_REG_LEN_08BIT, 0x2E }, + { 0x322C, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3409, CRL_REG_LEN_08BIT, 0x0C }, + { 0x340C, CRL_REG_LEN_08BIT, 0x2D }, + { 0x3411, CRL_REG_LEN_08BIT, 0x39 }, + { 0x3414, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3427, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3480, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3484, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3488, CRL_REG_LEN_08BIT, 0x1E }, + { 0x348C, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3490, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3494, CRL_REG_LEN_08BIT, 0x1E }, + { 0x3511, CRL_REG_LEN_08BIT, 0x8F }, + { 0x364F, CRL_REG_LEN_08BIT, 0x2D }, + { 0x0700, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3a63, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4100, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x4203, CRL_REG_LEN_08BIT, 0xff }, + { 0x4344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4100, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x441c, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020e, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx135_mode_13M[] = { + { 0x0108, CRL_REG_LEN_08BIT, 0x03 }, /* lanes */ + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0390, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0391, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0392, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4082, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4083, CRL_REG_LEN_08BIT, 0x11 }, /* Sony settings do not work */ + { 0x4203, CRL_REG_LEN_08BIT, 0xFF }, + { 0x7006, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034B, CRL_REG_LEN_08BIT, 0x2F }, + { 0x034C, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034F, CRL_REG_LEN_08BIT, 0x30 }, + { 0x0350, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0351, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0352, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0353, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0354, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0355, CRL_REG_LEN_08BIT, 0x70 }, + { 0x0356, CRL_REG_LEN_08BIT, 0x0C }, + { 0x0357, CRL_REG_LEN_08BIT, 0x30 }, + { 0x301D, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3310, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3311, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3312, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3313, CRL_REG_LEN_08BIT, 0x30 }, + { 0x331C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x331D, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4084, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4085, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4086, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4087, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4400, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx135_mode_1936M_binn_scale[] = { + + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0390, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0391, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0392, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x11 }, + { 0x4082, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4083, CRL_REG_LEN_08BIT, 0x00 }, + { 0x7006, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x2E }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x8C }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x41 }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034B, CRL_REG_LEN_08BIT, 0xA7 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x90 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x48 }, + { 0x0350, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0351, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0352, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0353, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0354, CRL_REG_LEN_08BIT, 0x08 }, + { 0x0355, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0356, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0357, CRL_REG_LEN_08BIT, 0x8E }, + { 0x301D, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3310, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3311, CRL_REG_LEN_08BIT, 0x90 }, + { 0x3312, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3313, CRL_REG_LEN_08BIT, 0x48 }, + { 0x331C, CRL_REG_LEN_08BIT, 0x04 }, + { 0x331D, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x4084, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4085, CRL_REG_LEN_08BIT, 0x90 }, + { 0x4086, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4087, CRL_REG_LEN_08BIT, 0x48 }, + { 0x4400, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx135_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep imx135_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep imx135_data_fmt_width10[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0a0a } +}; + +static struct crl_register_write_rep imx135_data_fmt_width8[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0808 } +}; + +static struct crl_arithmetic_ops imx135_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access imx135_h_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx135_v_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx135_vflip_ops), + .ops = imx135_vflip_ops, + .mask = 0x2, + }, +}; + + +static struct crl_dynamic_register_access imx135_ana_gain_global_regs[] = { + { + .address = 0x0205, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access imx135_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access imx135_vblank_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx135_hblank_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; +static struct crl_sensor_detect_config imx135_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration imx135_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 451200000, + .pixel_rate_csi = 360960000, + .pixel_rate_pa = 360960000, + .bitsperpixel = 10, + .comp_items = 0, + .ctrl_data = 0, + .csi_lanes = 4, + .pll_regs_items = ARRAY_SIZE(imx135_pll_451), + .pll_regs = imx135_pll_451, + }, +}; + +static struct crl_subdev_rect_rep imx135_13M_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, +}; + +static struct crl_subdev_rect_rep imx135_mode_1936M_binn_scale_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 46, + .out_rect.top = 396, + .out_rect.width = 4116, + .out_rect.height = 2332, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4116, + .in_rect.height = 2332, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2058, + .out_rect.height = 1166, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 2058, + .in_rect.height = 1166, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1936, + .out_rect.height = 1096, + }, +}; + +static struct crl_mode_rep imx135_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx135_13M_rects), + .sd_rects = imx135_13M_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4208, + .height = 3120, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx135_mode_13M), + .mode_regs = imx135_mode_13M, + }, + { + .sd_rects_items = + ARRAY_SIZE(imx135_mode_1936M_binn_scale_rects), + .sd_rects = imx135_mode_1936M_binn_scale_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 17, + .width = 1936, + .height = 1096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx135_mode_1936M_binn_scale), + .mode_regs = imx135_mode_1936M_binn_scale, + }, +}; + +static struct crl_sensor_subdev_config imx135_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "imx135 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx135 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx135 pixel array", + }, +}; + +static struct crl_sensor_limits imx135_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 4208, + .y_addr_max = 3120, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 4572, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data imx135_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +static struct crl_csi_data_fmt imx135_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = imx135_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx135_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx135_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx135_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx135_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx135_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx135_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx135_data_fmt_width8, + }, +}; + +static struct crl_dynamic_register_access imx135_test_pattern_regs[] = { + { + .address = 0x0600, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static const char * const imx135_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", +}; + +static const s64 imx135_op_sys_clock[] = { 451200000 }; + +static struct crl_v4l2_ctrl imx135_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = + ARRAY_SIZE(imx135_pll_configurations) - 1, + .data.v4l2_int_menu.menu = imx135_op_sys_clock, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_ANALOGUE_GAIN", + .data.std_data.min = 0, + .data.std_data.max = 224, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_ana_gain_global_regs), + .regs = imx135_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_EXPOSURE", + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 4500, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_exposure_regs), + .regs = imx135_exposure_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_HFLIP", + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_h_flip_regs), + .regs = imx135_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_VFLIP", + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_v_flip_regs), + .regs = imx135_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = imx135_test_patterns, + .data.v4l2_menu_items.size = ARRAY_SIZE(imx135_test_patterns), + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_test_pattern_regs), + .regs = imx135_test_pattern_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 3800, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_vblank_regs), + .regs = imx135_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 4280, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 4600, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx135_hblank_regs), + .regs = imx135_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx135_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2700000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1100000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + .delay = 2000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 250, + }, +}; + + +static struct crl_sensor_configuration imx135_crl_configuration = { + + .powerup_regs_items = ARRAY_SIZE(imx135_powerup_regset), + .powerup_regs = imx135_powerup_regset, + + .power_items = ARRAY_SIZE(imx135_power_items), + .power_entities = imx135_power_items, + + .id_reg_items = ARRAY_SIZE(imx135_sensor_detect_regset), + .id_regs = imx135_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx135_sensor_subdevs), + .subdevs = imx135_sensor_subdevs, + + .sensor_limits = &imx135_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx135_pll_configurations), + .pll_configs = imx135_pll_configurations, + + .modes_items = ARRAY_SIZE(imx135_modes), + .modes = imx135_modes, + + .streamon_regs_items = ARRAY_SIZE(imx135_streamon_regs), + .streamon_regs = imx135_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx135_streamoff_regs), + .streamoff_regs = imx135_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx135_v4l2_ctrls), + .v4l2_ctrl_bank = imx135_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx135_crl_csi_data_fmt), + .csi_fmts = imx135_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx135_flip_configurations), + .flip_data = imx135_flip_configurations, +}; + + +#endif /* __CRLMODULE_IMX135_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx185_configuration.h b/drivers/media/i2c/crlmodule/crl_imx185_configuration.h new file mode 100644 index 000000000000..168455b63d20 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx185_configuration.h @@ -0,0 +1,1772 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Shuguang Gong + * + */ + +#ifndef __CRLMODULE_IMX185_CONFIGURATION_H_ +#define __CRLMODULE_IMX185_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define IMX185_REG_STANDBY 0x3000 +#define IMX185_REG_XMSTA 0x3002 +#define IMX185_REG_SW_RESET 0x3003 + +#define IMX185_HMAX 65535 +#define IMX185_VMAX 131071 +#define IMX185_MAX_SHS1 (IMX185_VMAX - 2) + +struct crl_ctrl_data_pair ctrl_data_modes[] = { + { + .ctrl_id = V4L2_CID_WDR_MODE, + .data = 0, + }, + { + .ctrl_id = V4L2_CID_WDR_MODE, + .data = 1, + }, +}; + +/* 111Mbps for imx185 720p 30fps */ +static struct crl_register_write_rep imx185_pll_111mbps[] = { + {0x3009, CRL_REG_LEN_08BIT, 0x02}, /* frame speed */ + {0x300A, CRL_REG_LEN_08BIT, 0x3C}, + {0x300C, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0xee}, + {0x3019, CRL_REG_LEN_08BIT, 0x02}, + {0x301b, CRL_REG_LEN_08BIT, 0xe4}, + {0x301c, CRL_REG_LEN_08BIT, 0x0C}, + {0x300F, CRL_REG_LEN_08BIT, 0x01}, + {0x3010, CRL_REG_LEN_08BIT, 0x39}, + {0x3012, CRL_REG_LEN_08BIT, 0x50}, + {0x3056, CRL_REG_LEN_08BIT, 0xC9}, + {0x3057, CRL_REG_LEN_08BIT, 0x64}, + {0x3065, CRL_REG_LEN_08BIT, 0x00}, + {0x3084, CRL_REG_LEN_08BIT, 0x0F}, + {0x3086, CRL_REG_LEN_08BIT, 0x10}, + {0x30CF, CRL_REG_LEN_08BIT, 0xE1}, + {0x30D0, CRL_REG_LEN_08BIT, 0x29}, + {0x30D2, CRL_REG_LEN_08BIT, 0x9B}, + {0x30D3, CRL_REG_LEN_08BIT, 0x01}, + {0x30E1, CRL_REG_LEN_08BIT, 0xFF}, + {0x3303, CRL_REG_LEN_08BIT, 0x20}, /* repetation */ + {0x3305, CRL_REG_LEN_08BIT, 0x03}, /* 1: 2lanes, 3: 4lanes */ + {0x332C, CRL_REG_LEN_08BIT, 0x28}, /* mipi timing */ + {0x332D, CRL_REG_LEN_08BIT, 0x20}, + {0x3341, CRL_REG_LEN_08BIT, 0x00}, + {0x3342, CRL_REG_LEN_08BIT, 0x1B}, + {0x3343, CRL_REG_LEN_08BIT, 0x58}, + {0x3344, CRL_REG_LEN_08BIT, 0x0C}, + {0x3345, CRL_REG_LEN_08BIT, 0x24}, + {0x3346, CRL_REG_LEN_08BIT, 0x10}, + {0x3347, CRL_REG_LEN_08BIT, 0x0B}, + {0x3348, CRL_REG_LEN_08BIT, 0x08}, + {0x3349, CRL_REG_LEN_08BIT, 0x30}, + {0x334A, CRL_REG_LEN_08BIT, 0x20}, +}; + +/* 222Mbps for imx185 1080p 30fps */ +static struct crl_register_write_rep imx185_pll_222mbps[] = { + {0x3009, CRL_REG_LEN_08BIT, 0x02}, /* frame speed */ + {0x300A, CRL_REG_LEN_08BIT, 0x3C}, + {0x300C, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0x98}, + {0x301c, CRL_REG_LEN_08BIT, 0x08}, + {0x300F, CRL_REG_LEN_08BIT, 0x01}, + {0x3010, CRL_REG_LEN_08BIT, 0x39}, + {0x3012, CRL_REG_LEN_08BIT, 0x50}, + {0x3056, CRL_REG_LEN_08BIT, 0xC9}, + {0x3057, CRL_REG_LEN_08BIT, 0x64}, + {0x3065, CRL_REG_LEN_08BIT, 0x00}, + {0x3084, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x01}, + {0x30CF, CRL_REG_LEN_08BIT, 0xD1}, + {0x30D0, CRL_REG_LEN_08BIT, 0x1B}, + {0x30D2, CRL_REG_LEN_08BIT, 0x5F}, + {0x30D3, CRL_REG_LEN_08BIT, 0x00}, + {0x30E1, CRL_REG_LEN_08BIT, 0xFF}, + {0x3303, CRL_REG_LEN_08BIT, 0x10}, /* repetation */ + {0x3305, CRL_REG_LEN_08BIT, 0x03}, /* 1: 2lanes, 3: 4lanes */ + {0x332C, CRL_REG_LEN_08BIT, 0x30}, /* mipi timing */ + {0x332D, CRL_REG_LEN_08BIT, 0x20}, + {0x3341, CRL_REG_LEN_08BIT, 0x00}, + {0x3342, CRL_REG_LEN_08BIT, 0x1B}, + {0x3343, CRL_REG_LEN_08BIT, 0x58}, + {0x3344, CRL_REG_LEN_08BIT, 0x10}, + {0x3345, CRL_REG_LEN_08BIT, 0x30}, + {0x3346, CRL_REG_LEN_08BIT, 0x18}, + {0x3347, CRL_REG_LEN_08BIT, 0x10}, + {0x3348, CRL_REG_LEN_08BIT, 0x10}, + {0x3349, CRL_REG_LEN_08BIT, 0x48}, + {0x334A, CRL_REG_LEN_08BIT, 0x28}, +}; + +/* 445Mbps for imx185 1080p 60fps */ +static struct crl_register_write_rep imx185_pll_445mbps[] = { + {0x3009, CRL_REG_LEN_08BIT, 0x01}, /* frame speed */ + {0x300A, CRL_REG_LEN_08BIT, 0x3C}, /* BLK */ + {0x300C, CRL_REG_LEN_08BIT, 0x00}, /* fixed settings */ + {0x3018, CRL_REG_LEN_08BIT, 0x65}, + {0x3019, CRL_REG_LEN_08BIT, 0x04}, + {0x301B, CRL_REG_LEN_08BIT, 0x4C}, + {0x301C, CRL_REG_LEN_08BIT, 0x04}, + {0x300F, CRL_REG_LEN_08BIT, 0x01}, + {0x3010, CRL_REG_LEN_08BIT, 0x39}, + {0x3012, CRL_REG_LEN_08BIT, 0x50}, + {0x3056, CRL_REG_LEN_08BIT, 0xC9}, + {0x3057, CRL_REG_LEN_08BIT, 0x64}, + {0x3065, CRL_REG_LEN_08BIT, 0x20}, + {0x3084, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x01}, + {0x30CF, CRL_REG_LEN_08BIT, 0xD1}, + {0x30D0, CRL_REG_LEN_08BIT, 0x1B}, + {0x30D2, CRL_REG_LEN_08BIT, 0x5F}, + {0x30D3, CRL_REG_LEN_08BIT, 0x00}, + {0x30E1, CRL_REG_LEN_08BIT, 0xFF}, + {0x3303, CRL_REG_LEN_08BIT, 0x00}, /* repetation */ + {0x3305, CRL_REG_LEN_08BIT, 0x03}, + {0x332C, CRL_REG_LEN_08BIT, 0x40}, /* mipi timing */ + {0x332D, CRL_REG_LEN_08BIT, 0x20}, + {0x3341, CRL_REG_LEN_08BIT, 0x00}, + {0x3342, CRL_REG_LEN_08BIT, 0x1B}, + {0x3343, CRL_REG_LEN_08BIT, 0x68}, + {0x3344, CRL_REG_LEN_08BIT, 0x20}, + {0x3345, CRL_REG_LEN_08BIT, 0x40}, + {0x3346, CRL_REG_LEN_08BIT, 0x28}, + {0x3347, CRL_REG_LEN_08BIT, 0x20}, + {0x3348, CRL_REG_LEN_08BIT, 0x18}, + {0x3349, CRL_REG_LEN_08BIT, 0x78}, + {0x334A, CRL_REG_LEN_08BIT, 0x28}, +}; + +static struct crl_register_write_rep imx185_fmt_raw10[] = { + {0x333E, CRL_REG_LEN_08BIT, 0x0a}, /* FMT RAW10 */ + {0x333F, CRL_REG_LEN_08BIT, 0x0a}, +}; + +static struct crl_register_write_rep imx185_fmt_raw12[] = { + {0x333E, CRL_REG_LEN_08BIT, 0x0c}, /* FMT RAW12 */ + {0x333F, CRL_REG_LEN_08BIT, 0x0c}, +}; + +static struct crl_register_write_rep imx185_powerup_standby[] = { + {0x3000, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 50, 0x00}, + {0x3002, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 200, 0x00}, +}; + +static struct crl_register_write_rep imx185_1312_728_27MHZ_CROPPING[] = { + /* 0x02h */ + {0x3005, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT: 10/12 ADBIT: + 10/12 , raw 10 */ + {0x3007, CRL_REG_LEN_08BIT, 0x60}, /* mode selection */ + {0x301D, CRL_REG_LEN_08BIT, 0x08}, + {0x301E, CRL_REG_LEN_08BIT, 0x02}, + {0x3044, CRL_REG_LEN_08BIT, 0xE1}, + {0x3048, CRL_REG_LEN_08BIT, 0x33}, + {0x305C, CRL_REG_LEN_08BIT, 0x2c}, + {0x305E, CRL_REG_LEN_08BIT, 0x21}, + {0x3063, CRL_REG_LEN_08BIT, 0x54}, + /* Crop settings */ + {0x3038, CRL_REG_LEN_08BIT, 0x00}, /* WPV = 0 */ + {0x3039, CRL_REG_LEN_08BIT, 0x00}, + {0x303A, CRL_REG_LEN_08BIT, 0xE0}, /* WV = PIC_SIZE + 8 */ + {0x303B, CRL_REG_LEN_08BIT, 0x02}, + {0x303C, CRL_REG_LEN_08BIT, 0x04}, /* WPH = 4 */ + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x1C}, /* Effective size = 1308*/ + {0x303F, CRL_REG_LEN_08BIT, 0x05}, + /* 0x03h */ + {0x311D, CRL_REG_LEN_08BIT, 0x0A}, + {0x3123, CRL_REG_LEN_08BIT, 0x0F}, + {0x3147, CRL_REG_LEN_08BIT, 0x87}, + {0x31E1, CRL_REG_LEN_08BIT, 0x9E}, + {0x31E2, CRL_REG_LEN_08BIT, 0x01}, + {0x31E5, CRL_REG_LEN_08BIT, 0x05}, + {0x31E6, CRL_REG_LEN_08BIT, 0x05}, + {0x31E7, CRL_REG_LEN_08BIT, 0x3A}, + {0x31E8, CRL_REG_LEN_08BIT, 0x3A}, + /* 0x04h */ + {0x3203, CRL_REG_LEN_08BIT, 0xC8}, + {0x3207, CRL_REG_LEN_08BIT, 0x54}, + {0x3213, CRL_REG_LEN_08BIT, 0x16}, + {0x3215, CRL_REG_LEN_08BIT, 0xF6}, + {0x321A, CRL_REG_LEN_08BIT, 0x14}, + {0x321B, CRL_REG_LEN_08BIT, 0x51}, + {0x3229, CRL_REG_LEN_08BIT, 0xE7}, + {0x322A, CRL_REG_LEN_08BIT, 0xF0}, + {0x322B, CRL_REG_LEN_08BIT, 0x10}, + {0x3231, CRL_REG_LEN_08BIT, 0xE7}, + {0x3232, CRL_REG_LEN_08BIT, 0xF0}, + {0x3233, CRL_REG_LEN_08BIT, 0x10}, + {0x323C, CRL_REG_LEN_08BIT, 0xE8}, + {0x323D, CRL_REG_LEN_08BIT, 0x70}, + {0x3243, CRL_REG_LEN_08BIT, 0x08}, + {0x3244, CRL_REG_LEN_08BIT, 0xE1}, + {0x3245, CRL_REG_LEN_08BIT, 0x10}, + {0x3247, CRL_REG_LEN_08BIT, 0xE7}, + {0x3248, CRL_REG_LEN_08BIT, 0x60}, + {0x3249, CRL_REG_LEN_08BIT, 0x1E}, + {0x324B, CRL_REG_LEN_08BIT, 0x00}, + {0x324C, CRL_REG_LEN_08BIT, 0x41}, + {0x3250, CRL_REG_LEN_08BIT, 0x30}, + {0x3251, CRL_REG_LEN_08BIT, 0x0A}, + {0x3252, CRL_REG_LEN_08BIT, 0xFF}, + {0x3253, CRL_REG_LEN_08BIT, 0xFF}, + {0x3254, CRL_REG_LEN_08BIT, 0xFF}, + {0x3255, CRL_REG_LEN_08BIT, 0x02}, + {0x3257, CRL_REG_LEN_08BIT, 0xF0}, + {0x325A, CRL_REG_LEN_08BIT, 0xA6}, + {0x325D, CRL_REG_LEN_08BIT, 0x14}, + {0x325E, CRL_REG_LEN_08BIT, 0x51}, + {0x3261, CRL_REG_LEN_08BIT, 0x61}, + {0x3266, CRL_REG_LEN_08BIT, 0x30}, + {0x3267, CRL_REG_LEN_08BIT, 0x05}, + {0x3275, CRL_REG_LEN_08BIT, 0xE7}, + {0x3281, CRL_REG_LEN_08BIT, 0xEA}, + {0x3282, CRL_REG_LEN_08BIT, 0x70}, + {0x3285, CRL_REG_LEN_08BIT, 0xFF}, + {0x328A, CRL_REG_LEN_08BIT, 0xF0}, + {0x328D, CRL_REG_LEN_08BIT, 0xB6}, + {0x328E, CRL_REG_LEN_08BIT, 0x40}, + {0x3290, CRL_REG_LEN_08BIT, 0x42}, + {0x3291, CRL_REG_LEN_08BIT, 0x51}, + {0x3292, CRL_REG_LEN_08BIT, 0x1E}, + {0x3294, CRL_REG_LEN_08BIT, 0xC4}, + {0x3295, CRL_REG_LEN_08BIT, 0x20}, + {0x3297, CRL_REG_LEN_08BIT, 0x50}, + {0x3298, CRL_REG_LEN_08BIT, 0x31}, + {0x3299, CRL_REG_LEN_08BIT, 0x1F}, + {0x329B, CRL_REG_LEN_08BIT, 0xC0}, + {0x329C, CRL_REG_LEN_08BIT, 0x60}, + {0x329E, CRL_REG_LEN_08BIT, 0x4C}, + {0x329F, CRL_REG_LEN_08BIT, 0x71}, + {0x32A0, CRL_REG_LEN_08BIT, 0x1F}, + {0x32A2, CRL_REG_LEN_08BIT, 0xB6}, + {0x32A3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32A4, CRL_REG_LEN_08BIT, 0x0B}, + {0x32A9, CRL_REG_LEN_08BIT, 0x24}, + {0x32AA, CRL_REG_LEN_08BIT, 0x41}, + {0x32B0, CRL_REG_LEN_08BIT, 0x25}, + {0x32B1, CRL_REG_LEN_08BIT, 0x51}, + {0x32B7, CRL_REG_LEN_08BIT, 0x1C}, + {0x32B8, CRL_REG_LEN_08BIT, 0xC1}, + {0x32B9, CRL_REG_LEN_08BIT, 0x12}, + {0x32BE, CRL_REG_LEN_08BIT, 0x1D}, + {0x32BF, CRL_REG_LEN_08BIT, 0xD1}, + {0x32C0, CRL_REG_LEN_08BIT, 0x12}, + {0x32C2, CRL_REG_LEN_08BIT, 0xA8}, + {0x32C3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32C4, CRL_REG_LEN_08BIT, 0x0A}, + {0x32C5, CRL_REG_LEN_08BIT, 0x1E}, + {0x32C6, CRL_REG_LEN_08BIT, 0x21}, + {0x32C9, CRL_REG_LEN_08BIT, 0xB0}, + {0x32CA, CRL_REG_LEN_08BIT, 0x40}, + {0x32CC, CRL_REG_LEN_08BIT, 0x26}, + {0x32CD, CRL_REG_LEN_08BIT, 0xA1}, + {0x32D0, CRL_REG_LEN_08BIT, 0xB6}, + {0x32D1, CRL_REG_LEN_08BIT, 0xC0}, + {0x32D2, CRL_REG_LEN_08BIT, 0x0B}, + {0x32D4, CRL_REG_LEN_08BIT, 0xE2}, + {0x32D5, CRL_REG_LEN_08BIT, 0x40}, + {0x32D8, CRL_REG_LEN_08BIT, 0x4E}, + {0x32D9, CRL_REG_LEN_08BIT, 0xA1}, + {0x32EC, CRL_REG_LEN_08BIT, 0xF0}, + /* 0x05h */ + {0x3316, CRL_REG_LEN_08BIT, 0x02}, + {0x3317, CRL_REG_LEN_08BIT, 0x02}, + {0x3318, CRL_REG_LEN_08BIT, 0xD8}, /* PIC_SIZE = 728 */ + {0x3319, CRL_REG_LEN_08BIT, 0x02}, + {0x334E, CRL_REG_LEN_08BIT, 0x3D}, /* INCL selection 27MHz */ + {0x334F, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep imx185_1952_1096_27MHZ[] = { + /* 0x02h */ + {0x3005, CRL_REG_LEN_08BIT, 0x01}, /* ADBIT: 10/12 */ + {0x3007, CRL_REG_LEN_08BIT, 0x10}, /* 1080p mode */ + {0x300A, CRL_REG_LEN_08BIT, 0xF0}, + {0x301D, CRL_REG_LEN_08BIT, 0x08}, + {0x301E, CRL_REG_LEN_08BIT, 0x02}, + {0x3048, CRL_REG_LEN_08BIT, 0x33}, + {0x305C, CRL_REG_LEN_08BIT, 0x2c}, /* INCLKSEL default */ + {0x305E, CRL_REG_LEN_08BIT, 0x21}, + {0x3063, CRL_REG_LEN_08BIT, 0x54}, + /* Crop settings */ + {0x3038, CRL_REG_LEN_08BIT, 0x00}, /* WPV = 0 */ + {0x3039, CRL_REG_LEN_08BIT, 0x00}, + {0x303A, CRL_REG_LEN_08BIT, 0xC0}, /* WV = PIC_SIZE + 8 */ + {0x303B, CRL_REG_LEN_08BIT, 0x04}, + {0x303C, CRL_REG_LEN_08BIT, 0x00}, /* WPH = 0 */ + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x9C}, + {0x303F, CRL_REG_LEN_08BIT, 0x07}, + /* 0x03h */ + {0x311D, CRL_REG_LEN_08BIT, 0x0A}, + {0x3123, CRL_REG_LEN_08BIT, 0x0F}, + {0x3126, CRL_REG_LEN_08BIT, 0xDF}, + {0x3147, CRL_REG_LEN_08BIT, 0x87}, + {0x31E0, CRL_REG_LEN_08BIT, 0x01}, + {0x31E1, CRL_REG_LEN_08BIT, 0x9E}, + {0x31E2, CRL_REG_LEN_08BIT, 0x01}, + {0x31E5, CRL_REG_LEN_08BIT, 0x05}, + {0x31E6, CRL_REG_LEN_08BIT, 0x05}, + {0x31E7, CRL_REG_LEN_08BIT, 0x3A}, + {0x31E8, CRL_REG_LEN_08BIT, 0x3A}, + /* 0x04h */ + {0x3203, CRL_REG_LEN_08BIT, 0xC8}, + {0x3207, CRL_REG_LEN_08BIT, 0x54}, + {0x3213, CRL_REG_LEN_08BIT, 0x16}, + {0x3215, CRL_REG_LEN_08BIT, 0xF6}, + {0x321A, CRL_REG_LEN_08BIT, 0x14}, + {0x321B, CRL_REG_LEN_08BIT, 0x51}, + {0x3229, CRL_REG_LEN_08BIT, 0xE7}, + {0x322A, CRL_REG_LEN_08BIT, 0xF0}, + {0x322B, CRL_REG_LEN_08BIT, 0x10}, + {0x3231, CRL_REG_LEN_08BIT, 0xE7}, + {0x3232, CRL_REG_LEN_08BIT, 0xF0}, + {0x3233, CRL_REG_LEN_08BIT, 0x10}, + {0x323C, CRL_REG_LEN_08BIT, 0xE8}, + {0x323D, CRL_REG_LEN_08BIT, 0x70}, + {0x3243, CRL_REG_LEN_08BIT, 0x08}, + {0x3244, CRL_REG_LEN_08BIT, 0xE1}, + {0x3245, CRL_REG_LEN_08BIT, 0x10}, + {0x3247, CRL_REG_LEN_08BIT, 0xE7}, + {0x3248, CRL_REG_LEN_08BIT, 0x60}, + {0x3249, CRL_REG_LEN_08BIT, 0x1E}, + {0x324B, CRL_REG_LEN_08BIT, 0x00}, + {0x324C, CRL_REG_LEN_08BIT, 0x41}, + {0x3250, CRL_REG_LEN_08BIT, 0x30}, + {0x3251, CRL_REG_LEN_08BIT, 0x0A}, + {0x3252, CRL_REG_LEN_08BIT, 0xFF}, + {0x3253, CRL_REG_LEN_08BIT, 0xFF}, + {0x3254, CRL_REG_LEN_08BIT, 0xFF}, + {0x3255, CRL_REG_LEN_08BIT, 0x02}, + {0x3257, CRL_REG_LEN_08BIT, 0xF0}, + {0x325A, CRL_REG_LEN_08BIT, 0xA6}, + {0x325D, CRL_REG_LEN_08BIT, 0x14}, + {0x325E, CRL_REG_LEN_08BIT, 0x51}, + {0x3261, CRL_REG_LEN_08BIT, 0x61}, + {0x3266, CRL_REG_LEN_08BIT, 0x30}, + {0x3267, CRL_REG_LEN_08BIT, 0x05}, + {0x3275, CRL_REG_LEN_08BIT, 0xE7}, + {0x3281, CRL_REG_LEN_08BIT, 0xEA}, + {0x3282, CRL_REG_LEN_08BIT, 0x70}, + {0x3285, CRL_REG_LEN_08BIT, 0xFF}, + {0x328A, CRL_REG_LEN_08BIT, 0xF0}, + {0x328D, CRL_REG_LEN_08BIT, 0xB6}, + {0x328E, CRL_REG_LEN_08BIT, 0x40}, + {0x3290, CRL_REG_LEN_08BIT, 0x42}, + {0x3291, CRL_REG_LEN_08BIT, 0x51}, + {0x3292, CRL_REG_LEN_08BIT, 0x1E}, + {0x3294, CRL_REG_LEN_08BIT, 0xC4}, + {0x3295, CRL_REG_LEN_08BIT, 0x20}, + {0x3297, CRL_REG_LEN_08BIT, 0x50}, + {0x3298, CRL_REG_LEN_08BIT, 0x31}, + {0x3299, CRL_REG_LEN_08BIT, 0x1F}, + {0x329B, CRL_REG_LEN_08BIT, 0xC0}, + {0x329C, CRL_REG_LEN_08BIT, 0x60}, + {0x329E, CRL_REG_LEN_08BIT, 0x4C}, + {0x329F, CRL_REG_LEN_08BIT, 0x71}, + {0x32A0, CRL_REG_LEN_08BIT, 0x1F}, + {0x32A2, CRL_REG_LEN_08BIT, 0xB6}, + {0x32A3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32A4, CRL_REG_LEN_08BIT, 0x0B}, + {0x32A9, CRL_REG_LEN_08BIT, 0x24}, + {0x32AA, CRL_REG_LEN_08BIT, 0x41}, + {0x32B0, CRL_REG_LEN_08BIT, 0x25}, + {0x32B1, CRL_REG_LEN_08BIT, 0x51}, + {0x32B7, CRL_REG_LEN_08BIT, 0x1C}, + {0x32B8, CRL_REG_LEN_08BIT, 0xC1}, + {0x32B9, CRL_REG_LEN_08BIT, 0x12}, + {0x32BE, CRL_REG_LEN_08BIT, 0x1D}, + {0x32BF, CRL_REG_LEN_08BIT, 0xD1}, + {0x32C0, CRL_REG_LEN_08BIT, 0x12}, + {0x32C2, CRL_REG_LEN_08BIT, 0xA8}, + {0x32C3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32C4, CRL_REG_LEN_08BIT, 0x0A}, + {0x32C5, CRL_REG_LEN_08BIT, 0x1E}, + {0x32C6, CRL_REG_LEN_08BIT, 0x21}, + {0x32C9, CRL_REG_LEN_08BIT, 0xB0}, + {0x32CA, CRL_REG_LEN_08BIT, 0x40}, + {0x32CC, CRL_REG_LEN_08BIT, 0x26}, + {0x32CD, CRL_REG_LEN_08BIT, 0xA1}, + {0x32D0, CRL_REG_LEN_08BIT, 0xB6}, + {0x32D1, CRL_REG_LEN_08BIT, 0xC0}, + {0x32D2, CRL_REG_LEN_08BIT, 0x0B}, + {0x32D4, CRL_REG_LEN_08BIT, 0xE2}, + {0x32D5, CRL_REG_LEN_08BIT, 0x40}, + {0x32D8, CRL_REG_LEN_08BIT, 0x4E}, + {0x32D9, CRL_REG_LEN_08BIT, 0xA1}, + {0x32EC, CRL_REG_LEN_08BIT, 0xF0}, + /* 0x05h */ + {0x3316, CRL_REG_LEN_08BIT, 0x04}, + {0x3317, CRL_REG_LEN_08BIT, 0x04}, + {0x3318, CRL_REG_LEN_08BIT, 0x48}, /* PIC_SIZE = 1096 */ + {0x3319, CRL_REG_LEN_08BIT, 0x04}, + {0x334E, CRL_REG_LEN_08BIT, 0x3D}, /* INCL selection 27MHz */ + {0x334F, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep imx185_1952_1096_BUILD_IN_WDR_27MHZ[] = { + /* 0x02h */ + {0x3005, CRL_REG_LEN_08BIT, 0x01}, /* ADBIT: 10/12 */ + {0x3007, CRL_REG_LEN_08BIT, 0x10}, /* mode selection */ + {0x300A, CRL_REG_LEN_08BIT, 0xF0}, + {0x300C, CRL_REG_LEN_08BIT, 0x02}, + {0x300F, CRL_REG_LEN_08BIT, 0x05}, + {0x3010, CRL_REG_LEN_08BIT, 0x38}, + {0x3012, CRL_REG_LEN_08BIT, 0x0F}, + {0x301B, CRL_REG_LEN_08BIT, 0x98}, + {0x301C, CRL_REG_LEN_08BIT, 0x08}, + {0x301D, CRL_REG_LEN_08BIT, 0x08}, + {0x301E, CRL_REG_LEN_08BIT, 0x02}, + {0x3048, CRL_REG_LEN_08BIT, 0x33}, + {0x3056, CRL_REG_LEN_08BIT, 0xC9}, + {0x3057, CRL_REG_LEN_08BIT, 0x64}, + {0x305C, CRL_REG_LEN_08BIT, 0x2c}, /* INCLKSEL default */ + {0x305E, CRL_REG_LEN_08BIT, 0x21}, + {0x3063, CRL_REG_LEN_08BIT, 0x54}, + /* Crop settings */ + {0x3038, CRL_REG_LEN_08BIT, 0x00}, /* WPV = 0 */ + {0x3039, CRL_REG_LEN_08BIT, 0x00}, + {0x303A, CRL_REG_LEN_08BIT, 0x4C}, /* WV = PIC_SIZE + 8 */ + {0x303B, CRL_REG_LEN_08BIT, 0x04}, + {0x303C, CRL_REG_LEN_08BIT, 0x00}, /* WPH = 0 */ + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x9C}, /* Effective size = 1948*/ + {0x303F, CRL_REG_LEN_08BIT, 0x07}, + /* 0x03h */ + {0x311D, CRL_REG_LEN_08BIT, 0x0A}, + {0x3123, CRL_REG_LEN_08BIT, 0x0F}, + {0x3126, CRL_REG_LEN_08BIT, 0xDF}, + {0x3147, CRL_REG_LEN_08BIT, 0x87}, + {0x31E0, CRL_REG_LEN_08BIT, 0x01}, + {0x31E1, CRL_REG_LEN_08BIT, 0x9E}, + {0x31E2, CRL_REG_LEN_08BIT, 0x01}, + {0x31E5, CRL_REG_LEN_08BIT, 0x05}, + {0x31E6, CRL_REG_LEN_08BIT, 0x05}, + {0x31E7, CRL_REG_LEN_08BIT, 0x3A}, + {0x31E8, CRL_REG_LEN_08BIT, 0x3A}, + /* 0x04h */ + {0x3203, CRL_REG_LEN_08BIT, 0xC8}, + {0x3207, CRL_REG_LEN_08BIT, 0x54}, + {0x3213, CRL_REG_LEN_08BIT, 0x16}, + {0x3215, CRL_REG_LEN_08BIT, 0xF6}, + {0x321A, CRL_REG_LEN_08BIT, 0x14}, + {0x321B, CRL_REG_LEN_08BIT, 0x51}, + {0x3229, CRL_REG_LEN_08BIT, 0xE7}, + {0x322A, CRL_REG_LEN_08BIT, 0xF0}, + {0x322B, CRL_REG_LEN_08BIT, 0x10}, + {0x3231, CRL_REG_LEN_08BIT, 0xE7}, + {0x3232, CRL_REG_LEN_08BIT, 0xF0}, + {0x3233, CRL_REG_LEN_08BIT, 0x10}, + {0x323C, CRL_REG_LEN_08BIT, 0xE8}, + {0x323D, CRL_REG_LEN_08BIT, 0x70}, + {0x3243, CRL_REG_LEN_08BIT, 0x08}, + {0x3244, CRL_REG_LEN_08BIT, 0xE1}, + {0x3245, CRL_REG_LEN_08BIT, 0x10}, + {0x3247, CRL_REG_LEN_08BIT, 0xE7}, + {0x3248, CRL_REG_LEN_08BIT, 0x60}, + {0x3249, CRL_REG_LEN_08BIT, 0x1E}, + {0x324B, CRL_REG_LEN_08BIT, 0x00}, + {0x324C, CRL_REG_LEN_08BIT, 0x41}, + {0x3250, CRL_REG_LEN_08BIT, 0x30}, + {0x3251, CRL_REG_LEN_08BIT, 0x0A}, + {0x3252, CRL_REG_LEN_08BIT, 0xFF}, + {0x3253, CRL_REG_LEN_08BIT, 0xFF}, + {0x3254, CRL_REG_LEN_08BIT, 0xFF}, + {0x3255, CRL_REG_LEN_08BIT, 0x02}, + {0x3257, CRL_REG_LEN_08BIT, 0xF0}, + {0x325A, CRL_REG_LEN_08BIT, 0xA6}, + {0x325D, CRL_REG_LEN_08BIT, 0x14}, + {0x325E, CRL_REG_LEN_08BIT, 0x51}, + {0x3261, CRL_REG_LEN_08BIT, 0x61}, + {0x3266, CRL_REG_LEN_08BIT, 0x30}, + {0x3267, CRL_REG_LEN_08BIT, 0x05}, + {0x3275, CRL_REG_LEN_08BIT, 0xE7}, + {0x3281, CRL_REG_LEN_08BIT, 0xEA}, + {0x3282, CRL_REG_LEN_08BIT, 0x70}, + {0x3285, CRL_REG_LEN_08BIT, 0xFF}, + {0x328A, CRL_REG_LEN_08BIT, 0xF0}, + {0x328D, CRL_REG_LEN_08BIT, 0xB6}, + {0x328E, CRL_REG_LEN_08BIT, 0x40}, + {0x3290, CRL_REG_LEN_08BIT, 0x42}, + {0x3291, CRL_REG_LEN_08BIT, 0x51}, + {0x3292, CRL_REG_LEN_08BIT, 0x1E}, + {0x3294, CRL_REG_LEN_08BIT, 0xC4}, + {0x3295, CRL_REG_LEN_08BIT, 0x20}, + {0x3297, CRL_REG_LEN_08BIT, 0x50}, + {0x3298, CRL_REG_LEN_08BIT, 0x31}, + {0x3299, CRL_REG_LEN_08BIT, 0x1F}, + {0x329B, CRL_REG_LEN_08BIT, 0xC0}, + {0x329C, CRL_REG_LEN_08BIT, 0x60}, + {0x329E, CRL_REG_LEN_08BIT, 0x4C}, + {0x329F, CRL_REG_LEN_08BIT, 0x71}, + {0x32A0, CRL_REG_LEN_08BIT, 0x1F}, + {0x32A2, CRL_REG_LEN_08BIT, 0xB6}, + {0x32A3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32A4, CRL_REG_LEN_08BIT, 0x0B}, + {0x32A9, CRL_REG_LEN_08BIT, 0x24}, + {0x32AA, CRL_REG_LEN_08BIT, 0x41}, + {0x32B0, CRL_REG_LEN_08BIT, 0x25}, + {0x32B1, CRL_REG_LEN_08BIT, 0x51}, + {0x32B7, CRL_REG_LEN_08BIT, 0x1C}, + {0x32B8, CRL_REG_LEN_08BIT, 0xC1}, + {0x32B9, CRL_REG_LEN_08BIT, 0x12}, + {0x32BE, CRL_REG_LEN_08BIT, 0x1D}, + {0x32BF, CRL_REG_LEN_08BIT, 0xD1}, + {0x32C0, CRL_REG_LEN_08BIT, 0x12}, + {0x32C2, CRL_REG_LEN_08BIT, 0xA8}, + {0x32C3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32C4, CRL_REG_LEN_08BIT, 0x0A}, + {0x32C5, CRL_REG_LEN_08BIT, 0x1E}, + {0x32C6, CRL_REG_LEN_08BIT, 0x21}, + {0x32C9, CRL_REG_LEN_08BIT, 0xB0}, + {0x32CA, CRL_REG_LEN_08BIT, 0x40}, + {0x32CC, CRL_REG_LEN_08BIT, 0x26}, + {0x32CD, CRL_REG_LEN_08BIT, 0xA1}, + {0x32D0, CRL_REG_LEN_08BIT, 0xB6}, + {0x32D1, CRL_REG_LEN_08BIT, 0xC0}, + {0x32D2, CRL_REG_LEN_08BIT, 0x0B}, + {0x32D4, CRL_REG_LEN_08BIT, 0xE2}, + {0x32D5, CRL_REG_LEN_08BIT, 0x40}, + {0x32D8, CRL_REG_LEN_08BIT, 0x4E}, + {0x32D9, CRL_REG_LEN_08BIT, 0xA1}, + {0x32EC, CRL_REG_LEN_08BIT, 0xF0}, + /* 0x05h */ + {0x3303, CRL_REG_LEN_08BIT, 0x10}, /* repetation wdr */ + {0x3314, CRL_REG_LEN_08BIT, 0x08}, + {0x3316, CRL_REG_LEN_08BIT, 0x04}, + {0x3317, CRL_REG_LEN_08BIT, 0x04}, + {0x3318, CRL_REG_LEN_08BIT, 0x48}, /* PIC_SIZE = 1096 */ + {0x3319, CRL_REG_LEN_08BIT, 0x04}, + {0x334E, CRL_REG_LEN_08BIT, 0x3D}, /* INCL selection 27MHz */ + {0x334F, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep imx185_1952_1208_27MHZ[] = { + /* 0x02h */ + {0x3005, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT: 10/12 */ + {0x3007, CRL_REG_LEN_08BIT, 0x00}, /* WUXGA cropping */ + {0x3018, CRL_REG_LEN_08BIT, 0x28}, + {0x3019, CRL_REG_LEN_08BIT, 0x05}, + {0x301B, CRL_REG_LEN_08BIT, 0x53}, + {0x301C, CRL_REG_LEN_08BIT, 0x07}, + {0x301D, CRL_REG_LEN_08BIT, 0x08}, + {0x301E, CRL_REG_LEN_08BIT, 0x02}, + {0x3048, CRL_REG_LEN_08BIT, 0x33}, + {0x305C, CRL_REG_LEN_08BIT, 0x2c}, /* INCLKSEL default */ + {0x305E, CRL_REG_LEN_08BIT, 0x21}, + {0x3063, CRL_REG_LEN_08BIT, 0x54}, + /* Crop settings */ + {0x3038, CRL_REG_LEN_08BIT, 0x00}, /* WPV = 0 */ + {0x3039, CRL_REG_LEN_08BIT, 0x00}, + {0x303A, CRL_REG_LEN_08BIT, 0xC0}, /* WV = PIC_SIZE + 8 */ + {0x303B, CRL_REG_LEN_08BIT, 0x04}, + {0x303C, CRL_REG_LEN_08BIT, 0x00}, /* WPH = 0 */ + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x9C}, + {0x303F, CRL_REG_LEN_08BIT, 0x07}, + /* 0x03h */ + {0x311D, CRL_REG_LEN_08BIT, 0x0A}, + {0x3123, CRL_REG_LEN_08BIT, 0x0F}, + {0x3126, CRL_REG_LEN_08BIT, 0x00}, + {0x3147, CRL_REG_LEN_08BIT, 0x87}, + {0x31E0, CRL_REG_LEN_08BIT, 0x00}, + {0x31E1, CRL_REG_LEN_08BIT, 0x9E}, + {0x31E2, CRL_REG_LEN_08BIT, 0x01}, + {0x31E5, CRL_REG_LEN_08BIT, 0x05}, + {0x31E6, CRL_REG_LEN_08BIT, 0x05}, + {0x31E7, CRL_REG_LEN_08BIT, 0x3A}, + {0x31E8, CRL_REG_LEN_08BIT, 0x3A}, + /* 0x04h */ + {0x3203, CRL_REG_LEN_08BIT, 0xC8}, + {0x3207, CRL_REG_LEN_08BIT, 0x54}, + {0x3213, CRL_REG_LEN_08BIT, 0x16}, + {0x3215, CRL_REG_LEN_08BIT, 0xF6}, + {0x321A, CRL_REG_LEN_08BIT, 0x14}, + {0x321B, CRL_REG_LEN_08BIT, 0x51}, + {0x3229, CRL_REG_LEN_08BIT, 0xE7}, + {0x322A, CRL_REG_LEN_08BIT, 0xF0}, + {0x322B, CRL_REG_LEN_08BIT, 0x10}, + {0x3231, CRL_REG_LEN_08BIT, 0xE7}, + {0x3232, CRL_REG_LEN_08BIT, 0xF0}, + {0x3233, CRL_REG_LEN_08BIT, 0x10}, + {0x323C, CRL_REG_LEN_08BIT, 0xE8}, + {0x323D, CRL_REG_LEN_08BIT, 0x70}, + {0x3243, CRL_REG_LEN_08BIT, 0x08}, + {0x3244, CRL_REG_LEN_08BIT, 0xE1}, + {0x3245, CRL_REG_LEN_08BIT, 0x10}, + {0x3247, CRL_REG_LEN_08BIT, 0xE7}, + {0x3248, CRL_REG_LEN_08BIT, 0x60}, + {0x3249, CRL_REG_LEN_08BIT, 0x1E}, + {0x324B, CRL_REG_LEN_08BIT, 0x00}, + {0x324C, CRL_REG_LEN_08BIT, 0x41}, + {0x3250, CRL_REG_LEN_08BIT, 0x30}, + {0x3251, CRL_REG_LEN_08BIT, 0x0A}, + {0x3252, CRL_REG_LEN_08BIT, 0xFF}, + {0x3253, CRL_REG_LEN_08BIT, 0xFF}, + {0x3254, CRL_REG_LEN_08BIT, 0xFF}, + {0x3255, CRL_REG_LEN_08BIT, 0x02}, + {0x3257, CRL_REG_LEN_08BIT, 0xF0}, + {0x325A, CRL_REG_LEN_08BIT, 0xA6}, + {0x325D, CRL_REG_LEN_08BIT, 0x14}, + {0x325E, CRL_REG_LEN_08BIT, 0x51}, + {0x3261, CRL_REG_LEN_08BIT, 0x61}, + {0x3266, CRL_REG_LEN_08BIT, 0x30}, + {0x3267, CRL_REG_LEN_08BIT, 0x05}, + {0x3275, CRL_REG_LEN_08BIT, 0xE7}, + {0x3281, CRL_REG_LEN_08BIT, 0xEA}, + {0x3282, CRL_REG_LEN_08BIT, 0x70}, + {0x3285, CRL_REG_LEN_08BIT, 0xFF}, + {0x328A, CRL_REG_LEN_08BIT, 0xF0}, + {0x328D, CRL_REG_LEN_08BIT, 0xB6}, + {0x328E, CRL_REG_LEN_08BIT, 0x40}, + {0x3290, CRL_REG_LEN_08BIT, 0x42}, + {0x3291, CRL_REG_LEN_08BIT, 0x51}, + {0x3292, CRL_REG_LEN_08BIT, 0x1E}, + {0x3294, CRL_REG_LEN_08BIT, 0xC4}, + {0x3295, CRL_REG_LEN_08BIT, 0x20}, + {0x3297, CRL_REG_LEN_08BIT, 0x50}, + {0x3298, CRL_REG_LEN_08BIT, 0x31}, + {0x3299, CRL_REG_LEN_08BIT, 0x1F}, + {0x329B, CRL_REG_LEN_08BIT, 0xC0}, + {0x329C, CRL_REG_LEN_08BIT, 0x60}, + {0x329E, CRL_REG_LEN_08BIT, 0x4C}, + {0x329F, CRL_REG_LEN_08BIT, 0x71}, + {0x32A0, CRL_REG_LEN_08BIT, 0x1F}, + {0x32A2, CRL_REG_LEN_08BIT, 0xB6}, + {0x32A3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32A4, CRL_REG_LEN_08BIT, 0x0B}, + {0x32A9, CRL_REG_LEN_08BIT, 0x24}, + {0x32AA, CRL_REG_LEN_08BIT, 0x41}, + {0x32B0, CRL_REG_LEN_08BIT, 0x25}, + {0x32B1, CRL_REG_LEN_08BIT, 0x51}, + {0x32B7, CRL_REG_LEN_08BIT, 0x1C}, + {0x32B8, CRL_REG_LEN_08BIT, 0xC1}, + {0x32B9, CRL_REG_LEN_08BIT, 0x12}, + {0x32BE, CRL_REG_LEN_08BIT, 0x1D}, + {0x32BF, CRL_REG_LEN_08BIT, 0xD1}, + {0x32C0, CRL_REG_LEN_08BIT, 0x12}, + {0x32C2, CRL_REG_LEN_08BIT, 0xA8}, + {0x32C3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32C4, CRL_REG_LEN_08BIT, 0x0A}, + {0x32C5, CRL_REG_LEN_08BIT, 0x1E}, + {0x32C6, CRL_REG_LEN_08BIT, 0x21}, + {0x32C9, CRL_REG_LEN_08BIT, 0xB0}, + {0x32CA, CRL_REG_LEN_08BIT, 0x40}, + {0x32CC, CRL_REG_LEN_08BIT, 0x26}, + {0x32CD, CRL_REG_LEN_08BIT, 0xA1}, + {0x32D0, CRL_REG_LEN_08BIT, 0xB6}, + {0x32D1, CRL_REG_LEN_08BIT, 0xC0}, + {0x32D2, CRL_REG_LEN_08BIT, 0x0B}, + {0x32D4, CRL_REG_LEN_08BIT, 0xE2}, + {0x32D5, CRL_REG_LEN_08BIT, 0x40}, + {0x32D8, CRL_REG_LEN_08BIT, 0x4E}, + {0x32D9, CRL_REG_LEN_08BIT, 0xA1}, + {0x32EC, CRL_REG_LEN_08BIT, 0xF0}, + /* 0x05h */ + {0x3316, CRL_REG_LEN_08BIT, 0x04}, + {0x3317, CRL_REG_LEN_08BIT, 0x04}, + {0x3318, CRL_REG_LEN_08BIT, 0xB8}, /* PIC_SIZE = 1208 */ + {0x3319, CRL_REG_LEN_08BIT, 0x04}, + {0x334E, CRL_REG_LEN_08BIT, 0x3D}, /* INCL selection 27MHz */ + {0x334F, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep imx185_1952_1208_BUILD_IN_WDR_27MHZ[] = { + /* 0x02h */ + {0x3005, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT: 10/12 */ + {0x3007, CRL_REG_LEN_08BIT, 0x00}, /* WUXGA cropping */ + {0x300C, CRL_REG_LEN_08BIT, 0x02}, + {0x300F, CRL_REG_LEN_08BIT, 0x05}, + {0x3010, CRL_REG_LEN_08BIT, 0x38}, + {0x3012, CRL_REG_LEN_08BIT, 0x0F}, + {0x3018, CRL_REG_LEN_08BIT, 0x98}, + {0x3019, CRL_REG_LEN_08BIT, 0x08}, + {0x301B, CRL_REG_LEN_08BIT, 0x65}, + {0x301C, CRL_REG_LEN_08BIT, 0x04}, + {0x301D, CRL_REG_LEN_08BIT, 0x08}, + {0x301E, CRL_REG_LEN_08BIT, 0x02}, + {0x3048, CRL_REG_LEN_08BIT, 0x33}, + {0x3056, CRL_REG_LEN_08BIT, 0xC9}, + {0x3057, CRL_REG_LEN_08BIT, 0x33}, + {0x305C, CRL_REG_LEN_08BIT, 0x2c}, /* INCLKSEL default */ + {0x305E, CRL_REG_LEN_08BIT, 0x21}, + {0x3063, CRL_REG_LEN_08BIT, 0x54}, + {0x30E1, CRL_REG_LEN_08BIT, 0xE1}, + /* Crop settings */ + {0x3038, CRL_REG_LEN_08BIT, 0x00}, /* WPV = 0 */ + {0x3039, CRL_REG_LEN_08BIT, 0x00}, + {0x303A, CRL_REG_LEN_08BIT, 0xC9}, + {0x303B, CRL_REG_LEN_08BIT, 0x04}, + {0x303C, CRL_REG_LEN_08BIT, 0x00}, + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x9C}, + {0x303F, CRL_REG_LEN_08BIT, 0x07}, + /* 0x03h */ + {0x311D, CRL_REG_LEN_08BIT, 0x0A}, + {0x3123, CRL_REG_LEN_08BIT, 0x0F}, + {0x3126, CRL_REG_LEN_08BIT, 0xDF}, + {0x3147, CRL_REG_LEN_08BIT, 0x87}, + {0x31E0, CRL_REG_LEN_08BIT, 0x01}, + {0x31E1, CRL_REG_LEN_08BIT, 0x9E}, + {0x31E2, CRL_REG_LEN_08BIT, 0x01}, + {0x31E5, CRL_REG_LEN_08BIT, 0x05}, + {0x31E6, CRL_REG_LEN_08BIT, 0x05}, + {0x31E7, CRL_REG_LEN_08BIT, 0x3A}, + {0x31E8, CRL_REG_LEN_08BIT, 0x3A}, + /* 0x04h */ + {0x3203, CRL_REG_LEN_08BIT, 0xC8}, + {0x3207, CRL_REG_LEN_08BIT, 0x54}, + {0x3213, CRL_REG_LEN_08BIT, 0x16}, + {0x3215, CRL_REG_LEN_08BIT, 0xF6}, + {0x321A, CRL_REG_LEN_08BIT, 0x14}, + {0x321B, CRL_REG_LEN_08BIT, 0x51}, + {0x3229, CRL_REG_LEN_08BIT, 0xE7}, + {0x322A, CRL_REG_LEN_08BIT, 0xF0}, + {0x322B, CRL_REG_LEN_08BIT, 0x10}, + {0x3231, CRL_REG_LEN_08BIT, 0xE7}, + {0x3232, CRL_REG_LEN_08BIT, 0xF0}, + {0x3233, CRL_REG_LEN_08BIT, 0x10}, + {0x323C, CRL_REG_LEN_08BIT, 0xE8}, + {0x323D, CRL_REG_LEN_08BIT, 0x70}, + {0x3243, CRL_REG_LEN_08BIT, 0x08}, + {0x3244, CRL_REG_LEN_08BIT, 0xE1}, + {0x3245, CRL_REG_LEN_08BIT, 0x10}, + {0x3247, CRL_REG_LEN_08BIT, 0xE7}, + {0x3248, CRL_REG_LEN_08BIT, 0x60}, + {0x3249, CRL_REG_LEN_08BIT, 0x1E}, + {0x324B, CRL_REG_LEN_08BIT, 0x00}, + {0x324C, CRL_REG_LEN_08BIT, 0x41}, + {0x3250, CRL_REG_LEN_08BIT, 0x30}, + {0x3251, CRL_REG_LEN_08BIT, 0x0A}, + {0x3252, CRL_REG_LEN_08BIT, 0xFF}, + {0x3253, CRL_REG_LEN_08BIT, 0xFF}, + {0x3254, CRL_REG_LEN_08BIT, 0xFF}, + {0x3255, CRL_REG_LEN_08BIT, 0x02}, + {0x3257, CRL_REG_LEN_08BIT, 0xF0}, + {0x325A, CRL_REG_LEN_08BIT, 0xA6}, + {0x325D, CRL_REG_LEN_08BIT, 0x14}, + {0x325E, CRL_REG_LEN_08BIT, 0x51}, + {0x3261, CRL_REG_LEN_08BIT, 0x61}, + {0x3266, CRL_REG_LEN_08BIT, 0x30}, + {0x3267, CRL_REG_LEN_08BIT, 0x05}, + {0x3275, CRL_REG_LEN_08BIT, 0xE7}, + {0x3281, CRL_REG_LEN_08BIT, 0xEA}, + {0x3282, CRL_REG_LEN_08BIT, 0x70}, + {0x3285, CRL_REG_LEN_08BIT, 0xFF}, + {0x328A, CRL_REG_LEN_08BIT, 0xF0}, + {0x328D, CRL_REG_LEN_08BIT, 0xB6}, + {0x328E, CRL_REG_LEN_08BIT, 0x40}, + {0x3290, CRL_REG_LEN_08BIT, 0x42}, + {0x3291, CRL_REG_LEN_08BIT, 0x51}, + {0x3292, CRL_REG_LEN_08BIT, 0x1E}, + {0x3294, CRL_REG_LEN_08BIT, 0xC4}, + {0x3295, CRL_REG_LEN_08BIT, 0x20}, + {0x3297, CRL_REG_LEN_08BIT, 0x50}, + {0x3298, CRL_REG_LEN_08BIT, 0x31}, + {0x3299, CRL_REG_LEN_08BIT, 0x1F}, + {0x329B, CRL_REG_LEN_08BIT, 0xC0}, + {0x329C, CRL_REG_LEN_08BIT, 0x60}, + {0x329E, CRL_REG_LEN_08BIT, 0x4C}, + {0x329F, CRL_REG_LEN_08BIT, 0x71}, + {0x32A0, CRL_REG_LEN_08BIT, 0x1F}, + {0x32A2, CRL_REG_LEN_08BIT, 0xB6}, + {0x32A3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32A4, CRL_REG_LEN_08BIT, 0x0B}, + {0x32A9, CRL_REG_LEN_08BIT, 0x24}, + {0x32AA, CRL_REG_LEN_08BIT, 0x41}, + {0x32B0, CRL_REG_LEN_08BIT, 0x25}, + {0x32B1, CRL_REG_LEN_08BIT, 0x51}, + {0x32B7, CRL_REG_LEN_08BIT, 0x1C}, + {0x32B8, CRL_REG_LEN_08BIT, 0xC1}, + {0x32B9, CRL_REG_LEN_08BIT, 0x12}, + {0x32BE, CRL_REG_LEN_08BIT, 0x1D}, + {0x32BF, CRL_REG_LEN_08BIT, 0xD1}, + {0x32C0, CRL_REG_LEN_08BIT, 0x12}, + {0x32C2, CRL_REG_LEN_08BIT, 0xA8}, + {0x32C3, CRL_REG_LEN_08BIT, 0xC0}, + {0x32C4, CRL_REG_LEN_08BIT, 0x0A}, + {0x32C5, CRL_REG_LEN_08BIT, 0x1E}, + {0x32C6, CRL_REG_LEN_08BIT, 0x21}, + {0x32C9, CRL_REG_LEN_08BIT, 0xB0}, + {0x32CA, CRL_REG_LEN_08BIT, 0x40}, + {0x32CC, CRL_REG_LEN_08BIT, 0x26}, + {0x32CD, CRL_REG_LEN_08BIT, 0xA1}, + {0x32D0, CRL_REG_LEN_08BIT, 0xB6}, + {0x32D1, CRL_REG_LEN_08BIT, 0xC0}, + {0x32D2, CRL_REG_LEN_08BIT, 0x0B}, + {0x32D4, CRL_REG_LEN_08BIT, 0xE2}, + {0x32D5, CRL_REG_LEN_08BIT, 0x40}, + {0x32D8, CRL_REG_LEN_08BIT, 0x4E}, + {0x32D9, CRL_REG_LEN_08BIT, 0xA1}, + {0x32EC, CRL_REG_LEN_08BIT, 0xF0}, + /* 0x05h */ + {0x3303, CRL_REG_LEN_08BIT, 0x00}, + {0x3314, CRL_REG_LEN_08BIT, 0x08}, + {0x3316, CRL_REG_LEN_08BIT, 0x04}, + {0x3317, CRL_REG_LEN_08BIT, 0x04}, + {0x3318, CRL_REG_LEN_08BIT, 0xB8}, /* PIC_SIZE = 1208 */ + {0x3319, CRL_REG_LEN_08BIT, 0x04}, + {0x334E, CRL_REG_LEN_08BIT, 0x3D}, /* INCL selection 27MHz */ + {0x334F, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep imx185_streamon_regs[] = { + {IMX185_REG_STANDBY, CRL_REG_LEN_08BIT, 0x00}, + {0x00, CRL_REG_LEN_DELAY, 30, 0x00}, /* Delay 30ms */ + {IMX185_REG_XMSTA, CRL_REG_LEN_08BIT, 0x00}, + {0x00, CRL_REG_LEN_DELAY, 30, 0x00}, /* Delay 30ms */ +}; + +static struct crl_register_write_rep imx185_streamoff_regs[] = { + {IMX185_REG_STANDBY, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 30, 0x00}, /* Delay 30ms */ + {IMX185_REG_XMSTA, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 30, 0x00}, /* Delay 30ms */ +}; + +static struct crl_arithmetic_ops imx185_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + } +}; + +/* shs1 = fll - exposure -1 */ +static struct crl_arithmetic_ops imx185_shs1_lsb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + } +}; + +static struct crl_arithmetic_ops imx185_shs1_msb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx185_shs1_hsb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 16, + } +}; + +/* shs2 = fll - exposure * 16 -1 */ +static struct crl_arithmetic_ops imx185_shs2_lsb_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 4, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + } +}; + +static struct crl_arithmetic_ops imx185_shs2_msb_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 4, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx185_shs2_hsb_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 4, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 16, + } +}; + +static struct crl_arithmetic_ops imx185_fll_msb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx185_llp_msb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx185_fll_hsb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 16, + } +}; + +static struct crl_dynamic_register_access imx185_h_flip_regs[] = { + { + .address = 0x3007, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx185_hflip_ops), + .ops = imx185_hflip_ops, + .mask = 0x2, + } +}; + +static struct crl_dynamic_register_access imx185_v_flip_regs[] = { + { + .address = 0x3007, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + } +}; + +static struct crl_dynamic_register_access imx185_ana_gain_global_regs[] = { + { + .address = 0x3014, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + } +}; + +static struct crl_dynamic_register_access imx185_shs_regs[] = { + /* + * Use 8bits access since 24bits or 32bits access will fail + * TODO: root cause the 24bits and 32bits access issues + */ + { + .address = 0x3020, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs1_lsb_ops), + .ops = imx185_shs1_lsb_ops, + .mask = 0xff, + }, + { + .address = 0x3021, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs1_msb_ops), + .ops = imx185_shs1_msb_ops, + .mask = 0xff, + }, + { + .address = 0x3022, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs1_hsb_ops), + .ops = imx185_shs1_hsb_ops, + .mask = 0x1, + }, + { + .address = 0x3023, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs2_lsb_ops), + .ops = imx185_shs2_lsb_ops, + .mask = 0xff, + }, + { + .address = 0x3024, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs2_msb_ops), + .ops = imx185_shs2_msb_ops, + .mask = 0xff, + }, + { + .address = 0x3025, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_shs2_hsb_ops), + .ops = imx185_shs2_hsb_ops, + .mask = 0x1, + } +}; + +static struct crl_dynamic_register_access imx185_fll_regs[] = { + /* + * Use 8bits access since 24bits or 32bits access will fail + * TODO: root cause the 24bits and 32bits access issues + */ + { + .address = 0x3018, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x3019, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_fll_msb_ops), + .ops = imx185_fll_msb_ops, + .mask = 0xff, + }, + { + .address = 0x301a, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_fll_hsb_ops), + .ops = imx185_fll_hsb_ops, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx185_llp_regs[] = { + { + .address = 0x301b, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x301c, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx185_llp_msb_ops), + .ops = imx185_llp_msb_ops, + .mask = 0xff, + }, +}; + +/* ctrl-val == 1 ? 1 * 0x02 : 0 * 0x02 -> 2 and 0 */ +static struct crl_arithmetic_ops imx185_wdr_switch_r300c_ops[] = { + { + .op = CRL_MULTIPLY, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x02, + } +}; + +/* ctrl-val == 1 ? (1 * 0x04 + 0x1) : (0 * 0x04 + 0x1) -> 0x05 and 0x01 */ +static struct crl_arithmetic_ops imx185_wdr_switch_r300f_ops[] = { + { + .op = CRL_MULTIPLY, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x04, + }, + { + .op = CRL_ADD, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x01, + } +}; + +/* ctrl-val == 1 ? (0x39 - 1 * 0x01) : (0x39 - 0 * 0x01) -> 0x38 and 0x39 */ +static struct crl_arithmetic_ops imx185_wdr_switch_r3010_ops[] = { + { + .op = CRL_MULTIPLY, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x01, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x39, + } +}; + +/* ctrl-val == 1 ? (0x50 - 1 * 0x41) : (0x50 - 0 * 0x41) -> 0x0f and 0x50 */ +static struct crl_arithmetic_ops imx185_wdr_switch_r3012_ops[] = { + { + .op = CRL_MULTIPLY, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x41, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x50, + } +}; + +static struct crl_dynamic_register_access imx185_wdr_switch_regs[] = { + { 0x300c, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx185_wdr_switch_r300c_ops), + imx185_wdr_switch_r300c_ops, 0 }, + { 0x300f, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx185_wdr_switch_r300f_ops), + imx185_wdr_switch_r300f_ops, 0 }, + { 0x3010, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx185_wdr_switch_r3010_ops), + imx185_wdr_switch_r3010_ops, 0 }, + { 0x3012, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx185_wdr_switch_r3012_ops), + imx185_wdr_switch_r3012_ops, 0 }, +}; + +/* Needed for acpi support for runtime detection */ +static struct crl_sensor_detect_config imx185_sensor_detect_regset[] = { + { + .reg = { 0x3385, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x3384, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + } +}; + +static struct crl_pll_configuration imx185_pll_configurations[] = { + { + .input_clk = 27000000, + .op_sys_clk = 56250000, + .bitsperpixel = 10, + .pixel_rate_csi = 45000000, + .pixel_rate_pa = 45000000, /* pixel_rate = MIPICLK*2 *4/10 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx185_pll_111mbps), + .pll_regs = imx185_pll_111mbps, + }, + { + .input_clk = 27000000, + .op_sys_clk = 112500000, + .bitsperpixel = 10, + .pixel_rate_csi = 90000000, + .pixel_rate_pa = 90000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx185_pll_222mbps), + .pll_regs = imx185_pll_222mbps, + }, + { + .input_clk = 27000000, + .op_sys_clk = 112500000, + .bitsperpixel = 12, + .pixel_rate_csi = 75000000, + .pixel_rate_pa = 75000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx185_pll_222mbps), + .pll_regs = imx185_pll_222mbps, + }, + { + .input_clk = 27000000, + .op_sys_clk = 225000000, + .bitsperpixel = 12, + .pixel_rate_csi = 150000000, + .pixel_rate_pa = 150000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx185_pll_445mbps), + .pll_regs = imx185_pll_445mbps, + } +}; + +static struct crl_subdev_rect_rep imx185_1952_1208_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1208, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1208, + } +}; + +static struct crl_subdev_rect_rep imx185_1952_1096_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1208, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1096, + } +}; + +static struct crl_subdev_rect_rep imx185_1312_728_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1208, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 1208, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1312, + .out_rect.height = 728, + } +}; + +static struct crl_mode_rep imx185_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx185_1952_1208_rects), + .sd_rects = imx185_1952_1208_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1952, + .height = 1208, + .min_llp = 2250, + .min_fll = 1333, + .comp_items = 1, + .ctrl_data = &ctrl_data_modes[0], + .mode_regs_items = ARRAY_SIZE(imx185_1952_1208_27MHZ), + .mode_regs = imx185_1952_1208_27MHZ, + }, + { + .sd_rects_items = ARRAY_SIZE(imx185_1952_1208_rects), + .sd_rects = imx185_1952_1208_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1952, + .height = 1208, + .min_llp = 2250, + .min_fll = 1333, + .comp_items = 1, + .ctrl_data = &ctrl_data_modes[1], + .mode_regs_items = + ARRAY_SIZE(imx185_1952_1208_BUILD_IN_WDR_27MHZ), + .mode_regs = imx185_1952_1208_BUILD_IN_WDR_27MHZ, + }, + { + .sd_rects_items = ARRAY_SIZE(imx185_1952_1096_rects), + .sd_rects = imx185_1952_1096_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1952, + .height = 1096, + .min_llp = 2200, + .min_fll = 1135, + .comp_items = 1, + .ctrl_data = &ctrl_data_modes[0], + .mode_regs_items = ARRAY_SIZE(imx185_1952_1096_27MHZ), + .mode_regs = imx185_1952_1096_27MHZ, + }, + { + .sd_rects_items = ARRAY_SIZE(imx185_1952_1096_rects), + .sd_rects = imx185_1952_1096_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1952, + .height = 1096, + .min_llp = 2200, + .min_fll = 1135, + .comp_items = 1, + .ctrl_data = &ctrl_data_modes[1], + .mode_regs_items = + ARRAY_SIZE(imx185_1952_1096_BUILD_IN_WDR_27MHZ), + .mode_regs = imx185_1952_1096_BUILD_IN_WDR_27MHZ, + }, + { + .sd_rects_items = ARRAY_SIZE(imx185_1312_728_rects), + .sd_rects = imx185_1312_728_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1312, + .height = 728, + .min_llp = 1300, + .min_fll = 787, + .comp_items = 1, + .ctrl_data = &ctrl_data_modes[0], + .mode_regs_items = ARRAY_SIZE(imx185_1312_728_27MHZ_CROPPING), + .mode_regs = imx185_1312_728_27MHZ_CROPPING, + } +}; + +static struct crl_sensor_subdev_config imx185_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx185 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx185 pixel array", + } +}; + +static struct crl_sensor_limits imx185_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1952, + .y_addr_max = 1208, + .min_frame_length_lines = 320, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 380, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data imx185_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + } +}; + +static struct crl_csi_data_fmt imx185_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx185_fmt_raw10), + .regs = imx185_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx185_fmt_raw10), + .regs = imx185_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx185_fmt_raw10), + .regs = imx185_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx185_fmt_raw10), + .regs = imx185_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx185_fmt_raw12), + .regs = imx185_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx185_fmt_raw12), + .regs = imx185_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx185_fmt_raw12), + .regs = imx185_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx185_fmt_raw12), + .regs = imx185_fmt_raw12, + } +}; + +static struct crl_v4l2_ctrl imx185_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_h_flip_regs), + .regs = imx185_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_v_flip_regs), + .regs = imx185_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 160, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_ana_gain_global_regs), + .regs = imx185_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = IMX185_MAX_SHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x47, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_shs_regs), + .regs = imx185_shs_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 720, + .data.std_data.max = IMX185_VMAX, + .data.std_data.step = 1, + .data.std_data.def = 0x465, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_fll_regs), + .regs = imx185_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x898, + .data.std_data.max = IMX185_HMAX, + .data.std_data.step = 1, + .data.std_data.def = 0x898, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_llp_regs), + .regs = imx185_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_WDR_MODE, + .name = "V4L2_CID_WDR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx185_wdr_switch_regs), + .regs = imx185_wdr_switch_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops imx185_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops imx185_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc imx185_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx185_frame_desc_width_ops), + .ops = imx185_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx185_frame_desc_height_ops), + .ops = imx185_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx185_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 27000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +struct crl_sensor_configuration imx185_crl_configuration = { + + .power_items = ARRAY_SIZE(imx185_power_items), + .power_entities = imx185_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx185_powerup_standby), + .powerup_regs = imx185_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx185_sensor_detect_regset), + .id_regs = imx185_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx185_sensor_subdevs), + .subdevs = imx185_sensor_subdevs, + + .sensor_limits = &imx185_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx185_pll_configurations), + .pll_configs = imx185_pll_configurations, + + .modes_items = ARRAY_SIZE(imx185_modes), + .modes = imx185_modes, + + .streamon_regs_items = ARRAY_SIZE(imx185_streamon_regs), + .streamon_regs = imx185_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx185_streamoff_regs), + .streamoff_regs = imx185_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx185_v4l2_ctrls), + .v4l2_ctrl_bank = imx185_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx185_crl_csi_data_fmt), + .csi_fmts = imx185_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx185_flip_configurations), + .flip_data = imx185_flip_configurations, + + .frame_desc_entries = ARRAY_SIZE(imx185_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx185_frame_desc, +}; + +#endif /* __CRLMODULE_IMX185_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx214_configuration.h b/drivers/media/i2c/crlmodule/crl_imx214_configuration.h new file mode 100644 index 000000000000..f49deefade25 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx214_configuration.h @@ -0,0 +1,1428 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_imx214_CONFIGURATION_H_ +#define __CRLMODULE_imx214_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep imx214_pll_1080mbps[] = { + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x87 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0a }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0821, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x66 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x66 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_pll_8_1080mbps[] = { + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x87 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x08 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0821, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_pll_1200mbps[] = { + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x96 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0a }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x12}, + { 0x0821, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x66 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x66 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_pll_8_1200mbps[] = { + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x96 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x08 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x12}, + { 0x0821, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_powerup_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /*24Mhz*/ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0101, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0105, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0106, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4550, CRL_REG_LEN_08BIT, 0x02 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4642, CRL_REG_LEN_08BIT, 0x05 }, + { 0x6227, CRL_REG_LEN_08BIT, 0x11 }, + { 0x6276, CRL_REG_LEN_08BIT, 0x00 }, + { 0x900E, CRL_REG_LEN_08BIT, 0x06 }, + { 0xA802, CRL_REG_LEN_08BIT, 0x90 }, + { 0xA803, CRL_REG_LEN_08BIT, 0x11 }, + { 0xA804, CRL_REG_LEN_08BIT, 0x62 }, + { 0xA805, CRL_REG_LEN_08BIT, 0x77 }, + { 0xA806, CRL_REG_LEN_08BIT, 0xAE }, + { 0xA807, CRL_REG_LEN_08BIT, 0x34 }, + { 0xA808, CRL_REG_LEN_08BIT, 0xAE }, + { 0xA809, CRL_REG_LEN_08BIT, 0x35 }, + { 0xA80A, CRL_REG_LEN_08BIT, 0x62 }, + { 0xA80B, CRL_REG_LEN_08BIT, 0x83 }, + { 0xAE33, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4174, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4175, CRL_REG_LEN_08BIT, 0x11 }, + { 0x4612, CRL_REG_LEN_08BIT, 0x29 }, + { 0x461B, CRL_REG_LEN_08BIT, 0x12 }, + { 0x461F, CRL_REG_LEN_08BIT, 0x06 }, + { 0x4635, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4637, CRL_REG_LEN_08BIT, 0x30 }, + { 0x463F, CRL_REG_LEN_08BIT, 0x18 }, + { 0x4641, CRL_REG_LEN_08BIT, 0x0D }, + { 0x465B, CRL_REG_LEN_08BIT, 0x12 }, + { 0x465F, CRL_REG_LEN_08BIT, 0x11 }, + { 0x4663, CRL_REG_LEN_08BIT, 0x11 }, + { 0x4667, CRL_REG_LEN_08BIT, 0x0F }, + { 0x466F, CRL_REG_LEN_08BIT, 0x0F }, + { 0x470E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x4909, CRL_REG_LEN_08BIT, 0xAB }, + { 0x490B, CRL_REG_LEN_08BIT, 0x95 }, + { 0x4915, CRL_REG_LEN_08BIT, 0x5D }, + { 0x4A5F, CRL_REG_LEN_08BIT, 0xFF }, + { 0x4A61, CRL_REG_LEN_08BIT, 0xFF }, + { 0x4A73, CRL_REG_LEN_08BIT, 0x62 }, + { 0x4A85, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4A87, CRL_REG_LEN_08BIT, 0xFF }, + { 0x583C, CRL_REG_LEN_08BIT, 0x04 }, + { 0x620E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x6EB2, CRL_REG_LEN_08BIT, 0x01 }, + { 0x6EB3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x9300, CRL_REG_LEN_08BIT, 0x02 }, +}; + +/* + * 0, 4207, 0, 3119 + * 4208, 3120 + * 4208x3120 + */ +static struct crl_register_write_rep imx214_mode_13m[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034B, CRL_REG_LEN_08BIT, 0x2F }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034F, CRL_REG_LEN_08BIT, 0x30 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x10 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0C }, + { 0x040F, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_mode_2k[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034B, CRL_REG_LEN_08BIT, 0x2F }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x38 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x06 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x18 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x38 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x06 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x18 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_mode_4k2k[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x78 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB8 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x40 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x10 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_mode_1120[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x38 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xB8 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB8 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x60 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x60 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x68 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_mode_1080[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x78 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB8 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x80 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x38 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x80 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x38 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0x68 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_mode_720[] = { + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0344, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x78 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x8E }, + { 0x0348, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0349, CRL_REG_LEN_08BIT, 0xF7 }, + { 0x034A, CRL_REG_LEN_08BIT, 0x07 }, + { 0x034B, CRL_REG_LEN_08BIT, 0xA1 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x35 }, + { 0x3054, CRL_REG_LEN_08BIT, 0x01 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x05 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x02 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xD0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x80 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A03, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3A04, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x3A05, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0B06, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A02, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3013, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4170, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4171, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4176, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4177, CRL_REG_LEN_08BIT, 0x3C }, + { 0xAE20, CRL_REG_LEN_08BIT, 0x04 }, + { 0xAE21, CRL_REG_LEN_08BIT, 0x5C }, + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx214_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep imx214_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep imx214_data_fmt_width10[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0a0a } +}; + +static struct crl_register_write_rep imx214_data_fmt_width8[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0808 } +}; + +static struct crl_arithmetic_ops imx214_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access imx214_h_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx214_v_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx214_vflip_ops), + .ops = imx214_vflip_ops, + .mask = 0x2, + }, +}; + +struct crl_register_write_rep imx214_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + + +static struct crl_dynamic_register_access imx214_ana_gain_global_regs[] = { + { + .address = 0x0204, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx214_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access imx214_vblank_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx214_hblank_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx214_test_pattern_regs[] = { + { + .address = 0x0600, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff + }, +}; + +static struct crl_sensor_detect_config imx214_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +const s64 imx214_op_sys_clock[] = { 504000000, 504000000, 600000000, + 600000000}; + +static struct crl_pll_configuration imx214_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 504000000, + .bitsperpixel = 8, + .pixel_rate_csi = 432000000, + .pixel_rate_pa = 432000000, + .comp_items = 0, + .ctrl_data = 0, + .csi_lanes = 4, + .pll_regs_items = ARRAY_SIZE(imx214_pll_8_1080mbps), + .pll_regs = imx214_pll_8_1080mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 504000000, + .bitsperpixel = 10, + .pixel_rate_csi = 432000000, + .pixel_rate_pa = 432000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx214_pll_1080mbps), + .pll_regs = imx214_pll_1080mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 600000000, + .bitsperpixel = 8, + .pixel_rate_csi = 480000000, + .pixel_rate_pa = 480000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx214_pll_8_1200mbps), + .pll_regs = imx214_pll_8_1200mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 600000000, + .bitsperpixel = 10, + .pixel_rate_csi = 480000000, + .pixel_rate_pa = 480000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx214_pll_1200mbps), + .pll_regs = imx214_pll_1200mbps, + }, + +}; + +/* + * 0,5343,448,3567 + * 5344, 3120 + * Dig Crop: (568,0)->4208x3120 + * Scale_m 16 + * 4208x3120 + */ + +static struct crl_subdev_rect_rep imx214_13m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, +}; + +static struct crl_subdev_rect_rep imx214_4k2k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 376, + .out_rect.width = 4208, + .out_rect.height = 2368, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 2368, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 2368, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 2368, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 2368, + }, +}; + +static struct crl_subdev_rect_rep imx214_2k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2104, + .out_rect.height = 1560, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 2104, + .in_rect.height = 1560, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2104, + .out_rect.height = 1560, + }, +}; + +static struct crl_subdev_rect_rep imx214_1120_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 56, + .out_rect.top = 440, + .out_rect.width = 4096, + .out_rect.height = 2240, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4096, + .in_rect.height = 2240, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2048, + .out_rect.height = 1120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 2048, + .in_rect.height = 1120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2048, + .out_rect.height = 1120, + }, +}; + +static struct crl_subdev_rect_rep imx214_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 376, + .out_rect.width = 4208, + .out_rect.height = 2368, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 2368, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2104, + .out_rect.height = 1184, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 2104, + .in_rect.height = 1184, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep imx214_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4208, + .in_rect.height = 3120, + .out_rect.left = 1400, + .out_rect.top = 1166, + .out_rect.width = 1408, + .out_rect.height = 788, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1408, + .in_rect.height = 788, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1408, + .out_rect.height = 788, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1408, + .in_rect.height = 788, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_mode_rep imx214_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx214_13m_rects), + .sd_rects = imx214_13m_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4208, + .height = 3120, + .min_llp = 5008, + .min_fll = 3180, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_13m), + .mode_regs = imx214_mode_13m, + }, + { + .sd_rects_items = ARRAY_SIZE(imx214_4k2k_rects), + .sd_rects = imx214_4k2k_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4208, + .height = 2368, + .min_llp = 5008, + .min_fll = 2408, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_4k2k), + .mode_regs = imx214_mode_4k2k, + }, + { + .sd_rects_items = ARRAY_SIZE(imx214_2k_rects), + .sd_rects = imx214_2k_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 2104, + .height = 1560, + .min_llp = 5008, + .min_fll = 1700, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_2k), + .mode_regs = imx214_mode_2k, + }, + { + .sd_rects_items = ARRAY_SIZE(imx214_1120_rects), + .sd_rects = imx214_1120_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 2048, + .height = 1120, + .min_llp = 5008, + .min_fll = 1600, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_1120), + .mode_regs = imx214_mode_1120, + }, + { + .sd_rects_items = ARRAY_SIZE(imx214_1080_rects), + .sd_rects = imx214_1080_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 1080, + .min_llp = 5008, + .min_fll = 1200, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_1080), + .mode_regs = imx214_mode_1080, + }, + { + .sd_rects_items = ARRAY_SIZE(imx214_720_rects), + .sd_rects = imx214_720_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .min_llp = 5008, + .min_fll = 828, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx214_mode_720), + .mode_regs = imx214_mode_720, + }, +}; + +static struct crl_sensor_subdev_config imx214_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "imx214 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx214 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx214 pixel array", + }, +}; + +static struct crl_sensor_limits imx214_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 4208, + .y_addr_max = 3120, + .min_frame_length_lines = 184, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 5008, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data imx214_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +static struct crl_csi_data_fmt imx214_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width10), + .regs = imx214_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width10), + .bits_per_pixel = 10, + .regs = imx214_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width10), + .bits_per_pixel = 10, + .regs = imx214_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width10), + .bits_per_pixel = 10, + .regs = imx214_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width8), + .bits_per_pixel = 8, + .regs = imx214_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width8), + .bits_per_pixel = 8, + .regs = imx214_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width8), + .bits_per_pixel = 8, + .regs = imx214_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = ARRAY_SIZE(imx214_data_fmt_width8), + .bits_per_pixel = 8, + .regs = imx214_data_fmt_width8, + }, +}; + + +static const char * const imx214_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", +}; + + +static struct crl_v4l2_ctrl imx214_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = + ARRAY_SIZE(imx214_pll_configurations) - 1, + .data.v4l2_int_menu.menu = imx214_op_sys_clock, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 480, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_ana_gain_global_regs), + .regs = imx214_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 1700, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_exposure_regs), + .regs = imx214_exposure_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_h_flip_regs), + .regs = imx214_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_v_flip_regs), + .regs = imx214_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 4130, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_vblank_regs), + .regs = imx214_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 5008, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 5008, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_hblank_regs), + .regs = imx214_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = imx214_test_patterns, + .data.v4l2_menu_items.size = ARRAY_SIZE(imx214_test_patterns), + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx214_test_pattern_regs), + .regs = imx214_test_pattern_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx214_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2700000, + .delay = 30000, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1100000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 0, + }, +}; + +static struct crl_nvm_blob imx214_nvm_blobs[] = { + { 0x50, 0x00, 0x100 }, + { 0x51, 0x00, 0x100 }, + { 0x52, 0x00, 0x20 }, +}; + +struct crl_sensor_configuration imx214_crl_configuration = { + + .power_items = ARRAY_SIZE(imx214_power_items), + .power_entities = imx214_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx214_powerup_regset), + .powerup_regs = imx214_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + + .id_reg_items = ARRAY_SIZE(imx214_sensor_detect_regset), + .id_regs = imx214_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx214_sensor_subdevs), + .subdevs = imx214_sensor_subdevs, + + .sensor_limits = &imx214_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx214_pll_configurations), + .pll_configs = imx214_pll_configurations, + + .modes_items = ARRAY_SIZE(imx214_modes), + .modes = imx214_modes, + .fail_safe_mode_index = 3, + + .streamon_regs_items = ARRAY_SIZE(imx214_streamon_regs), + .streamon_regs = imx214_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx214_streamoff_regs), + .streamoff_regs = imx214_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx214_v4l2_ctrls), + .v4l2_ctrl_bank = imx214_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx214_crl_csi_data_fmt), + .csi_fmts = imx214_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx214_flip_configurations), + .flip_data = imx214_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_8BIT, + .crl_nvm_info.nvm_preop_regs_items = 0, + .crl_nvm_info.nvm_postop_regs_items = 0, + .crl_nvm_info.nvm_blobs_items = ARRAY_SIZE(imx214_nvm_blobs), + .crl_nvm_info.nvm_config = imx214_nvm_blobs, +}; + + + + + + +#endif /* __CRLMODULE_DUMMY_imx230_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx230_configuration.h b/drivers/media/i2c/crlmodule/crl_imx230_configuration.h new file mode 100644 index 000000000000..c19afad589e5 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx230_configuration.h @@ -0,0 +1,2367 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_imx230_CONFIGURATION_H_ +#define __CRLMODULE_imx230_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + + +static struct crl_register_write_rep imx230_pll_1500mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* EXT clock 24 MHz*/ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0A }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x0F }, + { 0x030E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x030F, CRL_REG_LEN_08BIT, 0xa9 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, /* Mipi settings, 4 lane */ + { 0x0820, CRL_REG_LEN_08BIT, 0x17 }, /*Data rate setting*/ + { 0x0821, CRL_REG_LEN_08BIT, 0x6c }, + { 0x0822, CRL_REG_LEN_08BIT, 0xcc }, + { 0x0823, CRL_REG_LEN_08BIT, 0xcc }, + { 0x0808, CRL_REG_LEN_08BIT, 0x01 }, +}; + +/* PLL settings for CSI lanes: 4, RAW14 output */ +static struct crl_register_write_rep imx230_pll_4_14_1500mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* EXT clock 24 MHz*/ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0xbf }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0e }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x030E, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030F, CRL_REG_LEN_08BIT, 0xfa }, + { 0x0310, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, /* Mipi settings, 4 lane */ + { 0x0820, CRL_REG_LEN_08BIT, 0x17 }, /*Data rate setting*/ + { 0x0821, CRL_REG_LEN_08BIT, 0x70 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0808, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx230_pll_2_10_1500mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* EXT clock 24 MHz*/ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0A }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x0F }, + { 0x030E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x030F, CRL_REG_LEN_08BIT, 0xa9 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0114, CRL_REG_LEN_08BIT, 0x01 }, /* Mipi settings, 2 lane */ + { 0x0820, CRL_REG_LEN_08BIT, 0x09 }, /*Data rate setting*/ + { 0x0821, CRL_REG_LEN_08BIT, 0x60 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0808, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx230_pll_2_8_1500mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* EXT clock 24 MHz*/ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0307, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x0309, CRL_REG_LEN_08BIT, 0x08 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x0F }, + { 0x030E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x030F, CRL_REG_LEN_08BIT, 0xa9 }, + { 0x0310, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0114, CRL_REG_LEN_08BIT, 0x01 }, /* Mipi settings, 2 lane */ + { 0x0820, CRL_REG_LEN_08BIT, 0x09 }, /*Data rate setting*/ + { 0x0821, CRL_REG_LEN_08BIT, 0x60 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0808, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep imx230_powerup_regset[] = { + { 0x4800, CRL_REG_LEN_08BIT, 0x0E }, + { 0x4890, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4D1E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4D1F, CRL_REG_LEN_08BIT, 0xFF }, + { 0x4FA0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4FA1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4FA2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4FA3, CRL_REG_LEN_08BIT, 0x83 }, + { 0x6153, CRL_REG_LEN_08BIT, 0x01 }, + { 0x6156, CRL_REG_LEN_08BIT, 0x01 }, + { 0x69BB, CRL_REG_LEN_08BIT, 0x01 }, + { 0x69BC, CRL_REG_LEN_08BIT, 0x05 }, + { 0x69BD, CRL_REG_LEN_08BIT, 0x05 }, + { 0x69C1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x69C4, CRL_REG_LEN_08BIT, 0x01 }, + { 0x69C6, CRL_REG_LEN_08BIT, 0x01 }, + { 0x7300, CRL_REG_LEN_08BIT, 0x00 }, + { 0x9009, CRL_REG_LEN_08BIT, 0x1A }, + { 0xB040, CRL_REG_LEN_08BIT, 0x90 }, + { 0xB041, CRL_REG_LEN_08BIT, 0x14 }, + { 0xB042, CRL_REG_LEN_08BIT, 0x6B }, + { 0xB043, CRL_REG_LEN_08BIT, 0x43 }, + { 0xB044, CRL_REG_LEN_08BIT, 0x63 }, + { 0xB045, CRL_REG_LEN_08BIT, 0x2A }, + { 0xB046, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB047, CRL_REG_LEN_08BIT, 0x06 }, + { 0xB048, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB049, CRL_REG_LEN_08BIT, 0x07 }, + { 0xB04A, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB04B, CRL_REG_LEN_08BIT, 0x04 }, + { 0xB04C, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB04D, CRL_REG_LEN_08BIT, 0x05 }, + { 0xB04E, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB04F, CRL_REG_LEN_08BIT, 0x16 }, + { 0xB050, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB051, CRL_REG_LEN_08BIT, 0x17 }, + { 0xB052, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB053, CRL_REG_LEN_08BIT, 0x74 }, + { 0xB054, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB055, CRL_REG_LEN_08BIT, 0x75 }, + { 0xB056, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB057, CRL_REG_LEN_08BIT, 0x76 }, + { 0xB058, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB059, CRL_REG_LEN_08BIT, 0x77 }, + { 0xB05A, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB05B, CRL_REG_LEN_08BIT, 0x7A }, + { 0xB05C, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB05D, CRL_REG_LEN_08BIT, 0x7B }, + { 0xB05E, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB05F, CRL_REG_LEN_08BIT, 0x0A }, + { 0xB060, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB061, CRL_REG_LEN_08BIT, 0x0B }, + { 0xB062, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB063, CRL_REG_LEN_08BIT, 0x08 }, + { 0xB064, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB065, CRL_REG_LEN_08BIT, 0x09 }, + { 0xB066, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB067, CRL_REG_LEN_08BIT, 0x0E }, + { 0xB068, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB069, CRL_REG_LEN_08BIT, 0x0F }, + { 0xB06A, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB06B, CRL_REG_LEN_08BIT, 0x0C }, + { 0xB06C, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB06D, CRL_REG_LEN_08BIT, 0x0D }, + { 0xB06E, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB06F, CRL_REG_LEN_08BIT, 0x13 }, + { 0xB070, CRL_REG_LEN_08BIT, 0x68 }, + { 0xB071, CRL_REG_LEN_08BIT, 0x12 }, + { 0xB072, CRL_REG_LEN_08BIT, 0x90 }, + { 0xB073, CRL_REG_LEN_08BIT, 0x0E }, + { 0xD000, CRL_REG_LEN_08BIT, 0xDA }, + { 0xD001, CRL_REG_LEN_08BIT, 0xDA }, + { 0xD002, CRL_REG_LEN_08BIT, 0xAF }, + { 0xD003, CRL_REG_LEN_08BIT, 0xE1 }, + { 0xD004, CRL_REG_LEN_08BIT, 0x55 }, + { 0xD005, CRL_REG_LEN_08BIT, 0x34 }, + { 0xD006, CRL_REG_LEN_08BIT, 0x21 }, + { 0xD007, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD008, CRL_REG_LEN_08BIT, 0x1C }, + { 0xD009, CRL_REG_LEN_08BIT, 0x80 }, + { 0xD00A, CRL_REG_LEN_08BIT, 0xFE }, + { 0xD00B, CRL_REG_LEN_08BIT, 0xC5 }, + { 0xD00C, CRL_REG_LEN_08BIT, 0x55 }, + { 0xD00D, CRL_REG_LEN_08BIT, 0xDC }, + { 0xD00E, CRL_REG_LEN_08BIT, 0xB6 }, + { 0xD00F, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD010, CRL_REG_LEN_08BIT, 0x31 }, + { 0xD011, CRL_REG_LEN_08BIT, 0x02 }, + { 0xD012, CRL_REG_LEN_08BIT, 0x4A }, + { 0xD013, CRL_REG_LEN_08BIT, 0x0E }, + { 0xD014, CRL_REG_LEN_08BIT, 0x55 }, + { 0xD015, CRL_REG_LEN_08BIT, 0xF0 }, + { 0xD016, CRL_REG_LEN_08BIT, 0x1B }, + { 0xD017, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD018, CRL_REG_LEN_08BIT, 0xFA }, + { 0xD019, CRL_REG_LEN_08BIT, 0x2C }, + { 0xD01A, CRL_REG_LEN_08BIT, 0xF1 }, + { 0xD01B, CRL_REG_LEN_08BIT, 0x7E }, + { 0xD01C, CRL_REG_LEN_08BIT, 0x55 }, + { 0xD01D, CRL_REG_LEN_08BIT, 0x1C }, + { 0xD01E, CRL_REG_LEN_08BIT, 0xD8 }, + { 0xD01F, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD020, CRL_REG_LEN_08BIT, 0x76 }, + { 0xD021, CRL_REG_LEN_08BIT, 0xC1 }, + { 0xD022, CRL_REG_LEN_08BIT, 0xBF }, + { 0xD044, CRL_REG_LEN_08BIT, 0x40 }, + { 0xD045, CRL_REG_LEN_08BIT, 0xBA }, + { 0xD046, CRL_REG_LEN_08BIT, 0x70 }, + { 0xD047, CRL_REG_LEN_08BIT, 0x47 }, + { 0xD048, CRL_REG_LEN_08BIT, 0xC0 }, + { 0xD049, CRL_REG_LEN_08BIT, 0xBA }, + { 0xD04A, CRL_REG_LEN_08BIT, 0x70 }, + { 0xD04B, CRL_REG_LEN_08BIT, 0x47 }, + { 0xD04C, CRL_REG_LEN_08BIT, 0x82 }, + { 0xD04D, CRL_REG_LEN_08BIT, 0xF6 }, + { 0xD04E, CRL_REG_LEN_08BIT, 0xDA }, + { 0xD04F, CRL_REG_LEN_08BIT, 0xFA }, + { 0xD050, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD051, CRL_REG_LEN_08BIT, 0xF0 }, + { 0xD052, CRL_REG_LEN_08BIT, 0x02 }, + { 0xD053, CRL_REG_LEN_08BIT, 0xF8 }, + { 0xD054, CRL_REG_LEN_08BIT, 0x81 }, + { 0xD055, CRL_REG_LEN_08BIT, 0xF6 }, + { 0xD056, CRL_REG_LEN_08BIT, 0xCE }, + { 0xD057, CRL_REG_LEN_08BIT, 0xFD }, + { 0xD058, CRL_REG_LEN_08BIT, 0x10 }, + { 0xD059, CRL_REG_LEN_08BIT, 0xB5 }, + { 0xD05A, CRL_REG_LEN_08BIT, 0x0D }, + { 0xD05B, CRL_REG_LEN_08BIT, 0x48 }, + { 0xD05C, CRL_REG_LEN_08BIT, 0x40 }, + { 0xD05D, CRL_REG_LEN_08BIT, 0x7A }, + { 0xD05E, CRL_REG_LEN_08BIT, 0x01 }, + { 0xD05F, CRL_REG_LEN_08BIT, 0x28 }, + { 0xD060, CRL_REG_LEN_08BIT, 0x15 }, + { 0xD061, CRL_REG_LEN_08BIT, 0xD1 }, + { 0xD062, CRL_REG_LEN_08BIT, 0x0C }, + { 0xD063, CRL_REG_LEN_08BIT, 0x49 }, + { 0xD064, CRL_REG_LEN_08BIT, 0x0C }, + { 0xD065, CRL_REG_LEN_08BIT, 0x46 }, + { 0xD066, CRL_REG_LEN_08BIT, 0x40 }, + { 0xD067, CRL_REG_LEN_08BIT, 0x3C }, + { 0xD068, CRL_REG_LEN_08BIT, 0x48 }, + { 0xD069, CRL_REG_LEN_08BIT, 0x8A }, + { 0xD06A, CRL_REG_LEN_08BIT, 0x62 }, + { 0xD06B, CRL_REG_LEN_08BIT, 0x8A }, + { 0xD06C, CRL_REG_LEN_08BIT, 0x80 }, + { 0xD06D, CRL_REG_LEN_08BIT, 0x1A }, + { 0xD06E, CRL_REG_LEN_08BIT, 0x8A }, + { 0xD06F, CRL_REG_LEN_08BIT, 0x89 }, + { 0xD070, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD071, CRL_REG_LEN_08BIT, 0xB2 }, + { 0xD072, CRL_REG_LEN_08BIT, 0x10 }, + { 0xD073, CRL_REG_LEN_08BIT, 0x18 }, + { 0xD074, CRL_REG_LEN_08BIT, 0x0A }, + { 0xD075, CRL_REG_LEN_08BIT, 0x46 }, + { 0xD076, CRL_REG_LEN_08BIT, 0x20 }, + { 0xD077, CRL_REG_LEN_08BIT, 0x32 }, + { 0xD078, CRL_REG_LEN_08BIT, 0x12 }, + { 0xD079, CRL_REG_LEN_08BIT, 0x88 }, + { 0xD07A, CRL_REG_LEN_08BIT, 0x90 }, + { 0xD07B, CRL_REG_LEN_08BIT, 0x42 }, + { 0xD07C, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD07D, CRL_REG_LEN_08BIT, 0xDA }, + { 0xD07E, CRL_REG_LEN_08BIT, 0x10 }, + { 0xD07F, CRL_REG_LEN_08BIT, 0x46 }, + { 0xD080, CRL_REG_LEN_08BIT, 0x80 }, + { 0xD081, CRL_REG_LEN_08BIT, 0xB2 }, + { 0xD082, CRL_REG_LEN_08BIT, 0x88 }, + { 0xD083, CRL_REG_LEN_08BIT, 0x81 }, + { 0xD084, CRL_REG_LEN_08BIT, 0x84 }, + { 0xD085, CRL_REG_LEN_08BIT, 0xF6 }, + { 0xD086, CRL_REG_LEN_08BIT, 0x06 }, + { 0xD087, CRL_REG_LEN_08BIT, 0xF8 }, + { 0xD088, CRL_REG_LEN_08BIT, 0xE0 }, + { 0xD089, CRL_REG_LEN_08BIT, 0x67 }, + { 0xD08A, CRL_REG_LEN_08BIT, 0x85 }, + { 0xD08B, CRL_REG_LEN_08BIT, 0xF6 }, + { 0xD08C, CRL_REG_LEN_08BIT, 0x4B }, + { 0xD08D, CRL_REG_LEN_08BIT, 0xFC }, + { 0xD08E, CRL_REG_LEN_08BIT, 0x10 }, + { 0xD08F, CRL_REG_LEN_08BIT, 0xBD }, + { 0xD090, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD091, CRL_REG_LEN_08BIT, 0x18 }, + { 0xD092, CRL_REG_LEN_08BIT, 0x1E }, + { 0xD093, CRL_REG_LEN_08BIT, 0x78 }, + { 0xD094, CRL_REG_LEN_08BIT, 0x00 }, + { 0xD095, CRL_REG_LEN_08BIT, 0x18 }, + { 0xD096, CRL_REG_LEN_08BIT, 0x17 }, + { 0xD097, CRL_REG_LEN_08BIT, 0x98 }, + { 0x5869, CRL_REG_LEN_08BIT, 0x01 }, /*Global settings done*/ + { 0x0216, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0217, CRL_REG_LEN_08BIT, 0x00 }, + { 0x020E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x020F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0210, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0211, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0212, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0213, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0214, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0215, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A21, CRL_REG_LEN_08BIT, 0x00 }, /* LSC setting */ + { 0x3011, CRL_REG_LEN_08BIT, 0x00 }, /* STATS Calc enable/disable */ + { 0x3013, CRL_REG_LEN_08BIT, 0x00 }, /*stats output enable/disable */ + { 0x5041, CRL_REG_LEN_08BIT, 0x04 }, /*embedded data on/off, 4 lines */ + { 0x0138, CRL_REG_LEN_08BIT, 0x01 }, /* Temperature control enable */ +}; + +static struct crl_register_write_rep imx230_mode_2k2k[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034B, CRL_REG_LEN_08BIT, 0xAF }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0A }, /*Output*/ + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x07 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xD7 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x07 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xD7 }, + { 0x697D, CRL_REG_LEN_08BIT, 0x02 }, /* PAF settings */ + { 0x6985, CRL_REG_LEN_08BIT, 0x02 }, + { 0x698D, CRL_REG_LEN_08BIT, 0x0B }, + { 0x6995, CRL_REG_LEN_08BIT, 0x0B }, + { 0x699D, CRL_REG_LEN_08BIT, 0x16 }, + { 0x69A5, CRL_REG_LEN_08BIT, 0x16 }, + { 0x69AD, CRL_REG_LEN_08BIT, 0x1F }, + { 0x69B5, CRL_REG_LEN_08BIT, 0x1F }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xD8 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0F }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xAF }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +/* UHD Scale */ +static struct crl_register_write_rep imx230_mode_4k2k[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034D, CRL_REG_LEN_08BIT, 0x2E }, + { 0x034E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x88 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x16 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0B }, + { 0x040F, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0B }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0D }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + + +/* UHD crop*/ +static struct crl_register_write_rep imx230_mode_uhd_crop[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034B, CRL_REG_LEN_08BIT, 0x0F }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x70 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0F }, + { 0x040D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3A36, CRL_REG_LEN_08BIT, 0x0F }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + + +/*5344 x 40160*/ +static struct crl_register_write_rep imx230_mode_full_4_3[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034B, CRL_REG_LEN_08BIT, 0xAF }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x034D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034F, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0F }, + { 0x040F, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x00 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xAF }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A21, CRL_REG_LEN_08BIT, 0x02 }, +}; + +/*5344 x 4016*/ +static struct crl_register_write_rep imx230_mode_full_16_9[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x034D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x0B }, + { 0x034F, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0B }, + { 0x040F, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0B }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0D }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_3264x2448_crop[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034B, CRL_REG_LEN_08BIT, 0x9F }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034D, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x90 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x10 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0C }, + { 0x040D, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x90 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x09 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0x90 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3A36, CRL_REG_LEN_08BIT, 0x9F }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_3264x2448_scale[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034B, CRL_REG_LEN_08BIT, 0xAF }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034D, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x90 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x1A }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0F }, /*dig crop y*/ + { 0x040F, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xAF }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_3280x2460_scale[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034B, CRL_REG_LEN_08BIT, 0xAF }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0C }, + { 0x034D, CRL_REG_LEN_08BIT, 0xD0 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x9C }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x1A }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0F }, + { 0x040F, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xAF }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_3336x2502_scale[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034B, CRL_REG_LEN_08BIT, 0xAF }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034D, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x09 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xC6 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x19 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0F }, + { 0x040F, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xAF }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_2672x1504[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x74 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0D }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_1940x1092[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0347, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034B, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x07 } /*1920 x 1080*/, + { 0x034D, CRL_REG_LEN_08BIT, 0x94 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x44 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x16 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0D }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xB7 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep imx230_mode_1440[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x08 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x034B, CRL_REG_LEN_08BIT, 0xA7 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0A }, /* 2560 x 1440 */ + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x040D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0A }, + { 0x3A36, CRL_REG_LEN_08BIT, 0xA7 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + + +static struct crl_register_write_rep imx230_mode_720[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x18 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x14 }, + { 0x0349, CRL_REG_LEN_08BIT, 0xDF }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0D }, + { 0x034B, CRL_REG_LEN_08BIT, 0x97 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x44 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x305C, CRL_REG_LEN_08BIT, 0x11 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x05 } /* 1296 x 736 */, + { 0x034D, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x02 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x10 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x02 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A22, CRL_REG_LEN_08BIT, 0x20 }, /* DPC2D settings */ + { 0x3A23, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A24, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A25, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3A26, CRL_REG_LEN_08BIT, 0xE0 }, + { 0x3A2F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A30, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A31, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3A32, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3A33, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3A34, CRL_REG_LEN_08BIT, 0xDF }, + { 0x3A35, CRL_REG_LEN_08BIT, 0x0D }, + { 0x3A36, CRL_REG_LEN_08BIT, 0x97 }, + { 0x3A37, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3A38, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3A39, CRL_REG_LEN_08BIT, 0x00 }, +}; + + +static struct crl_register_write_rep imx230_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep imx230_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep imx230_data_fmt_width10[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0a0a } +}; + +static struct crl_register_write_rep imx230_data_fmt_width8[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0808 } +}; + +static struct crl_register_write_rep imx230_data_fmt_width14[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0e0e } +}; + +static struct crl_arithmetic_ops imx230_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access imx230_h_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx230_v_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx230_vflip_ops), + .ops = imx230_vflip_ops, + .mask = 0x2, + }, +}; + + +static struct crl_dynamic_register_access imx230_ana_gain_global_regs[] = { + { + .address = 0x0204, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx230_dig_gain_regs[] = { + { + .address = 0x020e, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xfff, + }, + { + .address = 0x0210, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xfff, + }, + { + .address = 0x0212, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xfff, + }, + { + .address = 0x0214, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xfff, + }, +}; + + + +static struct crl_dynamic_register_access imx230_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access imx230_fll_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx230_llp_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx230_hdr_et_ratio_regs[] = { + { 0x0222, CRL_REG_LEN_08BIT, 0xff, 0, NULL, 0 }, +}; + +static struct crl_register_write_rep imx230_hdr_mode_off[] = { + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x74 }, /* HDR output control */ + { 0x3001, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3006, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x02 }, + { 0x31e0, CRL_REG_LEN_08BIT, 0x03 }, + { 0x31e1, CRL_REG_LEN_08BIT, 0xff }, + { 0x31e4, CRL_REG_LEN_08BIT, 0x02 }, + { 0x30b4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30ba, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bb, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bc, CRL_REG_LEN_08BIT, 0x00 }, +}; + +/* HDR Type3 ZIGZAG */ +static struct crl_register_write_rep imx230_hdr_mode_type3[] = { + /* + * 0x220 HDR control register + * bit 0: 0:HDR Disable 1:HDR enable *1-> below + * bit 1: 0:Combined gain 1:separate gain *0-> below + * bit 5: 0:Use ET Ratio 1:Short exposure by direct control *0-> below + */ + { 0x0220, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + /* Enable ATR 0x3000 bit 0 */ + { 0x3000, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3001, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3006, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x02 }, + { 0x31e0, CRL_REG_LEN_08BIT, 0x03 }, + { 0x31e4, CRL_REG_LEN_08BIT, 0x02 }, + { 0x30b4, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30b5, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30b6, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30b7, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30b8, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30b9, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30ba, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30bb, CRL_REG_LEN_08BIT, 0x01 }, + { 0x30bc, CRL_REG_LEN_08BIT, 0x01 }, +}; + +/* HDR Type2 */ +static struct crl_register_write_rep imx230_hdr_mode_type2[] = { + /* + * 0x220 HDR control register + * bit 0: 0:HDR Disable 1:HDR enable *1-> below + * bit 1: 0:Combined gain 1:separate gain *0-> below + * bit 5: 0:Use ET Ratio 1:Short exposure by direct control *0-> below + */ + { 0x0220, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + + /* Disable ATR for Type 2 0x3000 bit 0 */ + { 0x3000, CRL_REG_LEN_08BIT, 0x64 }, + { 0x3001, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3006, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x01 }, + { 0x31e0, CRL_REG_LEN_08BIT, 0x3f }, + { 0x31e4, CRL_REG_LEN_08BIT, 0x02 }, + { 0x30b4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30ba, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bb, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bc, CRL_REG_LEN_08BIT, 0x00 }, +}; + +/* HDR Type1 */ +static struct crl_register_write_rep imx230_hdr_mode_type1[] = { + /* + * 0x220 HDR control register + * bit 0: 0:HDR Disable 1:HDR enable *1-> below + * bit 1: 0:Combined gain 1:separate gain *0-> below + * bit 5: 0:Use ET Ratio 1:Short exposure by direct control *0-> below + */ + { 0x0220, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0224, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0225, CRL_REG_LEN_08BIT, 0xF4 }, + /* ATR is enabled 0x3000 bit 0 */ + { 0x3000, CRL_REG_LEN_08BIT, 0x75 }, + { 0x3001, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3006, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x01 }, + { 0x31e0, CRL_REG_LEN_08BIT, 0x3f }, + { 0x31e4, CRL_REG_LEN_08BIT, 0x02 }, + { 0x30b4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30b9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30ba, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bb, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30bc, CRL_REG_LEN_08BIT, 0x00 }, +}; + +/* + * IMX230 HDR types + * Type 1 10bit output after HDR and ATR blocks + * Type 2 14bit RAW after HDR block + * Type 3 10bit ZIGZAG pattern + */ +static struct crl_dep_reg_list imx230_hdr_types_regs[] = { + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 0 }, + ARRAY_SIZE(imx230_hdr_mode_off), imx230_hdr_mode_off, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 1 }, + ARRAY_SIZE(imx230_hdr_mode_type1), imx230_hdr_mode_type1, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 2 }, + ARRAY_SIZE(imx230_hdr_mode_type2), imx230_hdr_mode_type2, 0, 0 }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 3 }, + ARRAY_SIZE(imx230_hdr_mode_type3), imx230_hdr_mode_type3, 0, 0 }, +}; + +/* PDAF ON -> 0X3121 = 1 when HDR is off and 0x3121 = 0 when HDR is on */ +static struct crl_arithmetic_ops imx230_reg3121_pdaf_on[] = { + { CRL_ASSIGNMENT, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, CRL_CID_IMX230_HDR_MODE } }, + { CRL_BITWISE_COMPLEMENT, { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 0 } }, + { CRL_BITWISE_AND, { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 1 } }, +}; + +/* PDAF ON -> 0X3001 = 0 when HDR is off and 0x3001 = 1 when HDR is on */ +static struct crl_arithmetic_ops imx230_reg3001_pdaf_on[] = { + { CRL_ASSIGNMENT, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, CRL_CID_IMX230_HDR_MODE } }, + { CRL_BITWISE_AND, { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 1 } }, +}; + +/* 0x3001 and 0x3121 behaves differently when HDR is ON or OFF */ +static struct crl_dynamic_register_access imx230_pdaf_on[] = { + { 0x3121, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx230_reg3121_pdaf_on), imx230_reg3121_pdaf_on, 0 }, + { 0x3001, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(imx230_reg3001_pdaf_on), imx230_reg3001_pdaf_on, 0 }, + { 0x3123, CRL_REG_LEN_08BIT, 0xff, 0, 0, 0 }, +}; + +/* All the following registers are set to 0 when PDAF is Off*/ +static struct crl_dynamic_register_access imx230_pdaf_off[] = { + { 0x3121, CRL_REG_LEN_08BIT, 0xff, 0, 0, 0 }, + { 0x3001, CRL_REG_LEN_08BIT, 0xff, 0, 0, 0 }, + { 0x3123, CRL_REG_LEN_08BIT, 0xff, 0, 0, 0 }, +}; + +/* + * There are two different registers to enable/disable PDAF with HDR On and Off + * + * PDAF On, HDR Off -> 0x3121: 1, 0x3001: 0, 0x3123: 1 + * PDAF Off, HDR Off-> 0x3121: 0, 0x3001: 0, 0x3123: 0 + * PDAF Off, HDR On -> 0x3121: 0, 0x3001: 0, 0x3123: 0 + * PDAF On, HDR On -> 0x3121: 0, 0x3001: 1, 0x3123: 1 + */ +static struct crl_dep_reg_list imx230_pdaf_ctrl_regs[] = { + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 1 }, 0, 0, + ARRAY_SIZE(imx230_pdaf_on), imx230_pdaf_on }, + { CRL_DEP_CTRL_CONDITION_EQUAL, + { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 0 }, 0, 0, + ARRAY_SIZE(imx230_pdaf_off), imx230_pdaf_off }, +}; + +/* PDAF enable controls are dependent on HDR on or OFF */ +struct crl_dep_ctrl_provision imx230_hdr_dep_controls[] = { + /* Self update PDAF settins after change in HDR settings */ + { CRL_CID_SENSOR_PDAF, CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL, 0, 0 }, +}; + +static struct crl_sensor_detect_config imx230_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0018, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +static struct crl_arithmetic_ops imx230_thermal_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL, + .operand.entity_val = 0x013a, + }, +}; + +static struct crl_dynamic_register_access imx230_thermal_regs[] = { + { + .address = 0x013a, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx230_thermal_ops), + .ops = imx230_thermal_ops, + .mask = 0xff, + }, +}; + +static struct crl_pll_configuration imx230_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 749600000, + .bitsperpixel = 10, + .pixel_rate_csi = 599680000, + .pixel_rate_pa = 600000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx230_pll_1500mbps), + .pll_regs = imx230_pll_1500mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 749600000, /* Actual value is 750000000 */ + .bitsperpixel = 14, + .pixel_rate_csi = 428570000, + .pixel_rate_pa = 573000000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx230_pll_4_14_1500mbps), + .pll_regs = imx230_pll_4_14_1500mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 749600000, + .bitsperpixel = 8, + .pixel_rate_csi = 374800000, + .pixel_rate_pa = 600000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx230_pll_2_8_1500mbps), + .pll_regs = imx230_pll_2_8_1500mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 749600000, + .bitsperpixel = 10, + .pixel_rate_csi = 299840000, + .pixel_rate_pa = 600000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx230_pll_2_10_1500mbps), + .pll_regs = imx230_pll_2_10_1500mbps, + }, +}; + +static struct crl_subdev_rect_rep imx230_full_4_3_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_3280x2460_s_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 3280, 2460 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_3264x2448_s_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 3264, 2448 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_3336x2502_s_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 3336, 2502 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_3264x2448_c_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 504, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 3264, 2448 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_full_16_9_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 504, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 5344, 3008 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_4k2k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 504, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 3886, 2184 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_uhd_crop_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 928, 5344, 2160 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 2160 }, + .out_rect = { 0, 0, 5344, 2160 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 2160 }, + .out_rect = { 0, 0, 3840, 2160 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_2k2k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 4016 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 2672, 2008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2672, 2008 }, + .out_rect = { 0, 0, 2672, 2008 }, + }, +}; + + +static struct crl_subdev_rect_rep imx230_1940x1092_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 504, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 2672, 1504 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2672, 1504 }, + .out_rect = { 0, 0, 1940, 1092 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_2672x1504_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 504, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 2672, 1504 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2672, 1504 }, + .out_rect = { 0, 0, 2672, 1504 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_1440_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 1288, 5344, 1440 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 1440 }, + .out_rect = { 0, 0, 5344, 1440 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5344, 1440 }, + .out_rect = { 0, 0, 2560, 1440 }, + }, +}; + +static struct crl_subdev_rect_rep imx230_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5344, 4016 }, + .out_rect = { 0, 0, 5344, 3008 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5344, 3008 }, + .out_rect = { 0, 0, 1336, 752 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 1336, 752 }, + .out_rect = { 0, 0, 1296, 736 }, + }, +}; + +static struct crl_mode_rep imx230_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx230_full_4_3_rects), + .sd_rects = imx230_full_4_3_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 5344, + .height = 4016, + .min_llp = 6024, + .min_fll = 4106, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_full_4_3), + .mode_regs = imx230_mode_full_4_3, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_3280x2460_s_rects), + .sd_rects = imx230_3280x2460_s_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 26, + .width = 3280, + .height = 2460, + .min_llp = 6024, + .min_fll = 4106, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_3280x2460_scale), + .mode_regs = imx230_mode_3280x2460_scale, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_3264x2448_s_rects), + .sd_rects = imx230_3264x2448_s_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 26, + .width = 3264, + .height = 2448, + .min_llp = 6024, + .min_fll = 4106, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_3264x2448_scale), + .mode_regs = imx230_mode_3264x2448_scale, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_3336x2502_s_rects), + .sd_rects = imx230_3336x2502_s_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 25, + .width = 3336, + .height = 2502, + .min_llp = 6024, + .min_fll = 4106, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_3336x2502_scale), + .mode_regs = imx230_mode_3336x2502_scale, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_3264x2448_c_rects), + .sd_rects = imx230_3264x2448_c_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3264, + .height = 2448, + .min_llp = 6024, + .min_fll = 2538, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_3264x2448_crop), + .mode_regs = imx230_mode_3264x2448_crop, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_full_16_9_rects), + .sd_rects = imx230_full_16_9_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 5344, + .height = 3008, + .min_llp = 6024, + .min_fll = 3098, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_full_16_9), + .mode_regs = imx230_mode_full_16_9, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_4k2k_rects), + .sd_rects = imx230_4k2k_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 22, + .width = 3886, + .height = 2184, + .min_llp = 6024, + .min_fll = 3300, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_4k2k), + .mode_regs = imx230_mode_4k2k, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_uhd_crop_rects), + .sd_rects = imx230_uhd_crop_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3840, + .height = 2160, + .min_llp = 6024, + .min_fll = 2250, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_uhd_crop), + .mode_regs = imx230_mode_uhd_crop, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_2k2k_rects), + .sd_rects = imx230_2k2k_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 2672, + .height = 2008, + .min_llp = 6024, + .min_fll = 2108, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_2k2k), + .mode_regs = imx230_mode_2k2k, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_2672x1504_rects), + .sd_rects = imx230_2672x1504_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 2672, + .height = 1504, + .min_llp = 6024, + .min_fll = 1660, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_2672x1504), + .mode_regs = imx230_mode_2672x1504, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_1940x1092_rects), + .sd_rects = imx230_1940x1092_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 22, + .width = 1940, + .height = 1092, + .min_llp = 6024, + .min_fll = 1660, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_1940x1092), + .mode_regs = imx230_mode_1940x1092, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_1440_rects), + .sd_rects = imx230_1440_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2560, + .height = 1440, + .min_llp = 6024, + .min_fll = 1530, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_1440), + .mode_regs = imx230_mode_1440, + }, + { + .sd_rects_items = ARRAY_SIZE(imx230_720_rects), + .sd_rects = imx230_720_rects, + .binn_hor = 4, + .binn_vert = 4, + .scale_m = 1, + .width = 1296, + .height = 736, + .min_llp = 6024, + .min_fll = 826, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx230_mode_720), + .mode_regs = imx230_mode_720, + }, +}; + +static struct crl_sensor_subdev_config imx230_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "imx230 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx230 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx230 pixel array", + }, +}; + +static struct crl_sensor_limits imx230_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 5344, + .y_addr_max = 4016, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data imx230_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +static struct crl_csi_data_fmt imx230_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = imx230_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx230_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx230_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx230_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx230_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx230_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx230_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx230_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = 1, + .bits_per_pixel = 14, + .regs = imx230_data_fmt_width14, + }, + { + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 14, + .regs = imx230_data_fmt_width14, + }, + { + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 14, + .regs = imx230_data_fmt_width14, + }, + { + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 14, + .regs = imx230_data_fmt_width14, + }, +}; + +static const char * const imx132_hdr_types[] = { + "HDR Off", + "HDR Type1", + "HDR Type2", + "HDRC Type3", +}; + +static struct crl_v4l2_ctrl imx230_vl42_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 448, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_ana_gain_global_regs), + .regs = imx230_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_exposure_regs), + .regs = imx230_exposure_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_h_flip_regs), + .regs = imx230_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_v_flip_regs), + .regs = imx230_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 4130, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_fll_regs), + .regs = imx230_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 6024, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_llp_regs), + .regs = imx230_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4095, + .data.std_data.step = 1, + .data.std_data.def = 256, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_dig_gain_regs), + .regs = imx230_dig_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_STREAMING, + .ctrl_id = CRL_CID_SENSOR_THERMAL_DATA, + .name = "Sensor Thermal Data", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_thermal_regs), + .regs = imx230_thermal_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, /* Cannot be set when streaming? */ + .ctrl_id = CRL_CID_IMX230_HDR_ET_RATIO, + .name = "imx230 HDR ET Ratio", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = 16, + .data.std_data.step = 1, + .data.std_data.def = 1, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx230_hdr_et_ratio_regs), + .regs = imx230_hdr_et_ratio_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = CRL_CID_IMX230_HDR_MODE, + .name = "imx230 HDR mode", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.v4l2_menu_items.menu = imx132_hdr_types, + .data.v4l2_menu_items.size = ARRAY_SIZE(imx132_hdr_types), + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = ARRAY_SIZE(imx230_hdr_dep_controls), + .dep_ctrls = imx230_hdr_dep_controls, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .crl_ctrl_dep_reg_list = ARRAY_SIZE(imx230_hdr_types_regs), + .dep_regs = imx230_hdr_types_regs, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_SENSOR_PDAF, + .name = "CRL_CID_SENSOR_PDAF", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = NULL, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .crl_ctrl_dep_reg_list = ARRAY_SIZE(imx230_pdaf_ctrl_regs), + .dep_regs = imx230_pdaf_ctrl_regs, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx230_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2500000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1100000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VIO", + .val = 1800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VAF", + .val = 3000000, + .delay = 2000, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 10700, + }, +}; + +static struct crl_nvm_blob imx230_nvm_blobs[] = { + { 0x54, 0x00, 0x100 }, + { 0x55, 0x00, 0x100 }, + { 0x56, 0x00, 0x021 }, +}; + +static struct crl_arithmetic_ops imx230_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops imx230_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 4, + }, +}; + +static struct crl_frame_desc imx230_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx230_frame_desc_width_ops), + .ops = imx230_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx230_frame_desc_height_ops), + .ops = imx230_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +struct crl_sensor_configuration imx230_crl_configuration = { + + + .power_items = ARRAY_SIZE(imx230_power_items), + .power_entities = imx230_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx230_powerup_regset), + .powerup_regs = imx230_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx230_sensor_detect_regset), + .id_regs = imx230_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx230_sensor_subdevs), + .subdevs = imx230_sensor_subdevs, + + .sensor_limits = &imx230_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx230_pll_configurations), + .pll_configs = imx230_pll_configurations, + + .modes_items = ARRAY_SIZE(imx230_modes), + .modes = imx230_modes, + .fail_safe_mode_index = 3, + + .streamon_regs_items = ARRAY_SIZE(imx230_streamon_regs), + .streamon_regs = imx230_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx230_streamoff_regs), + .streamoff_regs = imx230_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx230_vl42_ctrls), + .v4l2_ctrl_bank = imx230_vl42_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx230_crl_csi_data_fmt), + .csi_fmts = imx230_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx230_flip_configurations), + .flip_data = imx230_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_8BIT, + .crl_nvm_info.nvm_preop_regs_items = 0, + .crl_nvm_info.nvm_postop_regs_items = 0, + .crl_nvm_info.nvm_blobs_items = ARRAY_SIZE(imx230_nvm_blobs), + .crl_nvm_info.nvm_config = imx230_nvm_blobs, + + .frame_desc_entries = ARRAY_SIZE(imx230_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx230_frame_desc, + + .msr_file_name = "00imx230.bxt_rvp.drvb", +}; + +#endif /* __CRLMODULE_imx230_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx274_configuration.h b/drivers/media/i2c/crlmodule/crl_imx274_configuration.h new file mode 100644 index 000000000000..6ec84fb42a32 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx274_configuration.h @@ -0,0 +1,1272 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Yuning Pu + * + */ + +#ifndef __CRLMODULE_IMX274_CONFIGURATION_H_ +#define __CRLMODULE_IMX274_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define IMX274_REG_STANDBY 0x3000 /* STBLOGIC STBMIPI STBDV */ + +#define IMX274_HMAX 65535 +#define IMX274_VMAX 1048575 +#define IMX274_MAX_SHS1 65535 +#define IMX274_MAX_SHS2 65535 +#define IMX274_MAX_RHS1 65535 + +/* imx274 mode standby cancel sequence */ +static struct crl_register_write_rep imx274_powerup_standby[] = { + {IMX274_REG_STANDBY, CRL_REG_LEN_08BIT, 0x12}, +}; + +/* 1440Mbps for imx274 4K 30fps 1080p 60fps */ +static struct crl_register_write_rep imx274_pll_1440mbps[] = { + {0x3120, CRL_REG_LEN_08BIT, 0xF0}, + {0x3121, CRL_REG_LEN_08BIT, 0x00}, + {0x3122, CRL_REG_LEN_08BIT, 0x02}, + {0x3129, CRL_REG_LEN_08BIT, 0x9C}, + {0x312A, CRL_REG_LEN_08BIT, 0x02}, + {0x312D, CRL_REG_LEN_08BIT, 0x02}, + {0x310B, CRL_REG_LEN_08BIT, 0x00}, /* PLL standby */ + {0x304C, CRL_REG_LEN_08BIT, 0x00}, /* PLSTMG01 */ + {0x304D, CRL_REG_LEN_08BIT, 0x03}, + {0x331C, CRL_REG_LEN_08BIT, 0x1A}, + {0x331D, CRL_REG_LEN_08BIT, 0x00}, + {0x3502, CRL_REG_LEN_08BIT, 0x02}, + {0x3529, CRL_REG_LEN_08BIT, 0x0E}, + {0x352A, CRL_REG_LEN_08BIT, 0x0E}, + {0x352B, CRL_REG_LEN_08BIT, 0x0E}, + {0x3538, CRL_REG_LEN_08BIT, 0x0E}, + {0x3539, CRL_REG_LEN_08BIT, 0x0E}, + {0x3553, CRL_REG_LEN_08BIT, 0x00}, + {0x357D, CRL_REG_LEN_08BIT, 0x05}, + {0x357F, CRL_REG_LEN_08BIT, 0x05}, + {0x3581, CRL_REG_LEN_08BIT, 0x04}, + {0x3583, CRL_REG_LEN_08BIT, 0x76}, + {0x3587, CRL_REG_LEN_08BIT, 0x01}, + {0x35BB, CRL_REG_LEN_08BIT, 0x0E}, + {0x35BC, CRL_REG_LEN_08BIT, 0x0E}, + {0x35BD, CRL_REG_LEN_08BIT, 0x0E}, + {0x35BE, CRL_REG_LEN_08BIT, 0x0E}, + {0x35BF, CRL_REG_LEN_08BIT, 0x0E}, + {0x366E, CRL_REG_LEN_08BIT, 0x00}, + {0x366F, CRL_REG_LEN_08BIT, 0x00}, + {0x3670, CRL_REG_LEN_08BIT, 0x00}, + {0x3671, CRL_REG_LEN_08BIT, 0x00}, /* PLSTMG01 */ + {0x30EE, CRL_REG_LEN_08BIT, 0x01}, + {0x3304, CRL_REG_LEN_08BIT, 0x32}, /* For Mipi */ + {0x3305, CRL_REG_LEN_08BIT, 0x00}, + {0x3306, CRL_REG_LEN_08BIT, 0x32}, + {0x3307, CRL_REG_LEN_08BIT, 0x00}, + {0x3590, CRL_REG_LEN_08BIT, 0x32}, + {0x3591, CRL_REG_LEN_08BIT, 0x00}, + {0x3686, CRL_REG_LEN_08BIT, 0x32}, + {0x3687, CRL_REG_LEN_08BIT, 0x00}, +}; + +static struct crl_register_write_rep imx274_3864_2202_RAW12_NORMAL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x00}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0xAA}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x08}, + {0x3132, CRL_REG_LEN_08BIT, 0x9A}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x08}, + {0x3004, CRL_REG_LEN_08BIT, 0x01}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x07}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x02}, + {0x3A41, CRL_REG_LEN_08BIT, 0x10}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0xFF}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x01}, + {0x3344, CRL_REG_LEN_08BIT, 0xFF}, + {0x3345, CRL_REG_LEN_08BIT, 0x01}, + {0x3528, CRL_REG_LEN_08BIT, 0x0F}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x18}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x0F}, + {0x3554, CRL_REG_LEN_08BIT, 0x00}, + {0x3555, CRL_REG_LEN_08BIT, 0x00}, + {0x3556, CRL_REG_LEN_08BIT, 0x00}, + {0x3557, CRL_REG_LEN_08BIT, 0x00}, + {0x3558, CRL_REG_LEN_08BIT, 0x00}, + {0x3559, CRL_REG_LEN_08BIT, 0x1F}, + {0x355A, CRL_REG_LEN_08BIT, 0x1F}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0F}, + {0x366A, CRL_REG_LEN_08BIT, 0x00}, + {0x366B, CRL_REG_LEN_08BIT, 0x00}, + {0x366C, CRL_REG_LEN_08BIT, 0x00}, + {0x366D, CRL_REG_LEN_08BIT, 0x00}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x07}, /* MDPLS17 */ + {0x3019, CRL_REG_LEN_08BIT, 0x00}, /* Disable DOL */ +}; + +static struct crl_register_write_rep imx274_3864_2174_RAW10_NORMAL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x01}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0x86}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x08}, + {0x3132, CRL_REG_LEN_08BIT, 0x7E}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x08}, + {0x3004, CRL_REG_LEN_08BIT, 0x01}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x01}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x02}, + {0x3A41, CRL_REG_LEN_08BIT, 0x08}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0x0A}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x00}, + {0x3344, CRL_REG_LEN_08BIT, 0x16}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3528, CRL_REG_LEN_08BIT, 0x0E}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x18}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x0F}, + {0x3554, CRL_REG_LEN_08BIT, 0x1F}, + {0x3555, CRL_REG_LEN_08BIT, 0x01}, + {0x3556, CRL_REG_LEN_08BIT, 0x01}, + {0x3557, CRL_REG_LEN_08BIT, 0x01}, + {0x3558, CRL_REG_LEN_08BIT, 0x01}, + {0x3559, CRL_REG_LEN_08BIT, 0x00}, + {0x355A, CRL_REG_LEN_08BIT, 0x00}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0E}, + {0x366A, CRL_REG_LEN_08BIT, 0x1B}, + {0x366B, CRL_REG_LEN_08BIT, 0x1A}, + {0x366C, CRL_REG_LEN_08BIT, 0x19}, + {0x366D, CRL_REG_LEN_08BIT, 0x17}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x05}, /* MDPLS17 */ + {0x3019, CRL_REG_LEN_08BIT, 0x00}, /* Disable DOL */ +}; + +static struct crl_register_write_rep imx274_3868_4536_RAW10_DOL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x01}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0x86}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x08}, + {0x3132, CRL_REG_LEN_08BIT, 0x8E}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x08}, + {0x3004, CRL_REG_LEN_08BIT, 0x06}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x01}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x02}, + {0x3A41, CRL_REG_LEN_08BIT, 0x00}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0x0A}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x00}, + {0x3344, CRL_REG_LEN_08BIT, 0x16}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3528, CRL_REG_LEN_08BIT, 0x0E}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x1C}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x0F}, + {0x3554, CRL_REG_LEN_08BIT, 0x1F}, + {0x3555, CRL_REG_LEN_08BIT, 0x01}, + {0x3556, CRL_REG_LEN_08BIT, 0x01}, + {0x3557, CRL_REG_LEN_08BIT, 0x01}, + {0x3558, CRL_REG_LEN_08BIT, 0x01}, + {0x3559, CRL_REG_LEN_08BIT, 0x00}, + {0x355A, CRL_REG_LEN_08BIT, 0x00}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0E}, + {0x366A, CRL_REG_LEN_08BIT, 0x1B}, + {0x366B, CRL_REG_LEN_08BIT, 0x1A}, + {0x366C, CRL_REG_LEN_08BIT, 0x19}, + {0x366D, CRL_REG_LEN_08BIT, 0x17}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x05}, /* MDPLS17 */ + /* DOL mode settings */ + {0x3019, CRL_REG_LEN_08BIT, 0x01}, /* DOLMODE,DOLSCDEN,HINFOEN */ + {0x3041, CRL_REG_LEN_08BIT, 0x31}, /* DOLSET1 */ + {0x3042, CRL_REG_LEN_08BIT, 0x04}, /* HCYCLE */ + {0x3043, CRL_REG_LEN_08BIT, 0x01}, + {0x30E9, CRL_REG_LEN_08BIT, 0x01}, /* DOLSET2 */ +}; + +static struct crl_register_write_rep imx274_1932_1094_RAW10_NORMAL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x02}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0x4E}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x04}, + {0x3132, CRL_REG_LEN_08BIT, 0x46}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x04}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x21}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x11}, + {0x3A41, CRL_REG_LEN_08BIT, 0x08}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0x0A}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x00}, + {0x3344, CRL_REG_LEN_08BIT, 0x1A}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3528, CRL_REG_LEN_08BIT, 0x0E}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x8C}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x07}, + {0x3554, CRL_REG_LEN_08BIT, 0x00}, + {0x3555, CRL_REG_LEN_08BIT, 0x01}, + {0x3556, CRL_REG_LEN_08BIT, 0x01}, + {0x3557, CRL_REG_LEN_08BIT, 0x01}, + {0x3558, CRL_REG_LEN_08BIT, 0x01}, + {0x3559, CRL_REG_LEN_08BIT, 0x00}, + {0x355A, CRL_REG_LEN_08BIT, 0x00}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0E}, + {0x366A, CRL_REG_LEN_08BIT, 0x1B}, + {0x366B, CRL_REG_LEN_08BIT, 0x1A}, + {0x366C, CRL_REG_LEN_08BIT, 0x19}, + {0x366D, CRL_REG_LEN_08BIT, 0x17}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x05}, /* MDPLS17 */ + {0x3019, CRL_REG_LEN_08BIT, 0x00}, /* Disable DOL */ +}; + +static struct crl_register_write_rep imx274_1932_1094_RAW12_NORMAL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x02}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0x4E}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x04}, + {0x3132, CRL_REG_LEN_08BIT, 0x46}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x04}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x27}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x11}, + {0x3A41, CRL_REG_LEN_08BIT, 0x08}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0xFF}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x01}, + {0x3344, CRL_REG_LEN_08BIT, 0xFF}, + {0x3345, CRL_REG_LEN_08BIT, 0x01}, + {0x3528, CRL_REG_LEN_08BIT, 0x0F}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x8C}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x07}, + {0x3554, CRL_REG_LEN_08BIT, 0x00}, + {0x3555, CRL_REG_LEN_08BIT, 0x00}, + {0x3556, CRL_REG_LEN_08BIT, 0x00}, + {0x3557, CRL_REG_LEN_08BIT, 0x00}, + {0x3558, CRL_REG_LEN_08BIT, 0x00}, + {0x3559, CRL_REG_LEN_08BIT, 0x1F}, + {0x355A, CRL_REG_LEN_08BIT, 0x1F}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0F}, + {0x366A, CRL_REG_LEN_08BIT, 0x00}, + {0x366B, CRL_REG_LEN_08BIT, 0x00}, + {0x366C, CRL_REG_LEN_08BIT, 0x00}, + {0x366D, CRL_REG_LEN_08BIT, 0x00}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x07}, /* MDPLS17 */ + {0x3019, CRL_REG_LEN_08BIT, 0x00}, /* Disable DOL */ +}; + +static struct crl_register_write_rep imx274_1936_2376_RAW10_DOL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x02}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0x4E}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x04}, + {0x3132, CRL_REG_LEN_08BIT, 0x54}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x04}, + {0x3004, CRL_REG_LEN_08BIT, 0x07}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x21}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x11}, + {0x3A41, CRL_REG_LEN_08BIT, 0x08}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0x0A}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x00}, + {0x3344, CRL_REG_LEN_08BIT, 0x1A}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3528, CRL_REG_LEN_08BIT, 0x0E}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x90}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x07}, + {0x3554, CRL_REG_LEN_08BIT, 0x00}, + {0x3555, CRL_REG_LEN_08BIT, 0x01}, + {0x3556, CRL_REG_LEN_08BIT, 0x01}, + {0x3557, CRL_REG_LEN_08BIT, 0x01}, + {0x3558, CRL_REG_LEN_08BIT, 0x01}, + {0x3559, CRL_REG_LEN_08BIT, 0x00}, + {0x355A, CRL_REG_LEN_08BIT, 0x00}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0E}, + {0x366A, CRL_REG_LEN_08BIT, 0x1B}, + {0x366B, CRL_REG_LEN_08BIT, 0x1A}, + {0x366C, CRL_REG_LEN_08BIT, 0x19}, + {0x366D, CRL_REG_LEN_08BIT, 0x17}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x05}, /* MDPLS17 */ + /* DOL mode settings */ + {0x3019, CRL_REG_LEN_08BIT, 0x01}, /* DOLMODE,DOLSCDEN,HINFOEN */ + {0x3041, CRL_REG_LEN_08BIT, 0x31}, /* DOLSET1 */ + {0x3042, CRL_REG_LEN_08BIT, 0x04}, /* HCYCLE */ + {0x3043, CRL_REG_LEN_08BIT, 0x01}, + {0x30E9, CRL_REG_LEN_08BIT, 0x01}, /* DOLSET2 */ +}; + +static struct crl_register_write_rep imx274_1288_738_RAW10_NORMAL[] = { + {0x30E2, CRL_REG_LEN_08BIT, 0x03}, /* VCUTMODE */ + {0x3130, CRL_REG_LEN_08BIT, 0xE2}, /* WRITE_VSIZE */ + {0x3131, CRL_REG_LEN_08BIT, 0x02}, + {0x3132, CRL_REG_LEN_08BIT, 0xDE}, /* Y_OUT_SIZE */ + {0x3133, CRL_REG_LEN_08BIT, 0x02}, + {0x3004, CRL_REG_LEN_08BIT, 0x03}, /* MDSEL */ + {0x3005, CRL_REG_LEN_08BIT, 0x31}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x09}, + {0x3A41, CRL_REG_LEN_08BIT, 0x04}, /* MDSEL5 */ + {0x3342, CRL_REG_LEN_08BIT, 0x0A}, /* MDPLS01 */ + {0x3343, CRL_REG_LEN_08BIT, 0x00}, + {0x3344, CRL_REG_LEN_08BIT, 0x1B}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3528, CRL_REG_LEN_08BIT, 0x0E}, /* MDPLS03 */ + {0x3A54, CRL_REG_LEN_08BIT, 0x8C}, /* Metadata Size */ + {0x3A55, CRL_REG_LEN_08BIT, 0x00}, + {0x3554, CRL_REG_LEN_08BIT, 0x00}, + {0x3555, CRL_REG_LEN_08BIT, 0x01}, + {0x3556, CRL_REG_LEN_08BIT, 0x01}, + {0x3557, CRL_REG_LEN_08BIT, 0x01}, + {0x3558, CRL_REG_LEN_08BIT, 0x01}, + {0x3559, CRL_REG_LEN_08BIT, 0x00}, + {0x355A, CRL_REG_LEN_08BIT, 0x00}, + {0x35BA, CRL_REG_LEN_08BIT, 0x0E}, + {0x366A, CRL_REG_LEN_08BIT, 0x1B}, + {0x366B, CRL_REG_LEN_08BIT, 0x19}, + {0x366C, CRL_REG_LEN_08BIT, 0x17}, + {0x366D, CRL_REG_LEN_08BIT, 0x17}, + {0x33A6, CRL_REG_LEN_08BIT, 0x01}, + {0x306B, CRL_REG_LEN_08BIT, 0x05}, /* MDPLS17 */ + {0x3019, CRL_REG_LEN_08BIT, 0x00}, /* Disable DOL */ +}; + +static struct crl_register_write_rep imx274_streamon_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ + {IMX274_REG_STANDBY, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x02}, + {0x00, CRL_REG_LEN_DELAY, 7, 0x00}, /* Add a 7ms delay */ + {0x30F4, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x02}, +}; + +static struct crl_register_write_rep imx274_streamoff_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ + {IMX274_REG_STANDBY, CRL_REG_LEN_08BIT, 0x01}, + {0x303E, CRL_REG_LEN_08BIT, 0x02}, + {0x00, CRL_REG_LEN_DELAY, 7, 0x00}, /* Add a delay */ + {0x30F4, CRL_REG_LEN_08BIT, 0x01}, + {0x3018, CRL_REG_LEN_08BIT, 0x02}, +}; + +static struct crl_arithmetic_ops imx274_rshift8_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx274_rshift16_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 16, + } +}; + +static struct crl_arithmetic_ops imx274_nan_gain_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0x07, + } +}; + +/* imx274 use register PGC[10:0] 300A 300B to indicate analog gain */ +static struct crl_dynamic_register_access imx274_ana_gain_global_regs[] = { + { + .address = 0x300A, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + }, + { + .address = 0x300B, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_nan_gain_ops), + .ops = imx274_nan_gain_ops, + }, +}; + +static struct crl_dynamic_register_access imx274_dig_gain_regs[] = { + { + .address = 0x3012, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xf, + }, +}; + +/* shr = fll - exposure */ +static struct crl_arithmetic_ops imx274_shr_lsb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + } +}; + +static struct crl_arithmetic_ops imx274_shr_msb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 8, + } +}; + +static struct crl_dynamic_register_access imx274_shr_regs[] = { + { + .address = 0x300C, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_shr_lsb_ops), + .ops = imx274_shr_lsb_ops, + .mask = 0xff, + }, + { + .address = 0x300D, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_shr_msb_ops), + .ops = imx274_shr_msb_ops, + .mask = 0xff, + }, +}; + +/* Short exposure for DOL */ +static struct crl_dynamic_register_access imx274_shs1_regs[] = { + { + .address = 0x302E, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x302F, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift8_ops), + .ops = imx274_rshift8_ops, + .mask = 0xff, + }, +}; + +/* Long exposure for DOL */ +static struct crl_dynamic_register_access imx274_shs2_regs[] = { + { + .address = 0x3030, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x3031, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift8_ops), + .ops = imx274_rshift8_ops, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access imx274_rhs1_regs[] = { + { + .address = 0x3032, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x3033, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift8_ops), + .ops = imx274_rshift8_ops, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access imx274_fll_regs[] = { + /* + * Use 8bits access since 24bits or 32bits access will fail + * TODO: root cause the 24bits and 32bits access issues + */ + { + .address = 0x30F8, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x30F9, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift8_ops), + .ops = imx274_rshift8_ops, + .mask = 0xff, + }, + { + .address = 0x30FA, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift16_ops), + .ops = imx274_rshift16_ops, + .mask = 0xf, + }, +}; + +static struct crl_dynamic_register_access imx274_llp_regs[] = { + { + .address = 0x30F6, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x30F7, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx274_rshift8_ops), + .ops = imx274_rshift8_ops, + .mask = 0xff, + }, +}; + +static struct crl_sensor_detect_config imx274_sensor_detect_regset[] = { + { + .reg = { 0x30F8, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x30F9, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration imx274_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 720000000, /* 1440000000/2 */ + .bitsperpixel = 10, + .pixel_rate_csi = 72000000, + .pixel_rate_pa = 72000000, /* 72MHz */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx274_pll_1440mbps), + .pll_regs = imx274_pll_1440mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 720000000, /* 1440000000/2 */ + .bitsperpixel = 12, + .pixel_rate_csi = 72000000, + .pixel_rate_pa = 72000000, /* 72MHz */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx274_pll_1440mbps), + .pll_regs = imx274_pll_1440mbps, + } +}; + +static struct crl_subdev_rect_rep imx274_3864_2202_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3864, + .out_rect.height = 2202, + } +}; + +static struct crl_subdev_rect_rep imx274_3864_2174_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3864, + .out_rect.height = 2174, + } +}; + +/* DOL pixel array includes 4 pixel sync code each line */ +static struct crl_subdev_rect_rep imx274_3868_4536_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + } +}; + +static struct crl_subdev_rect_rep imx274_1932_1094_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1932, + .out_rect.height = 1094, + } +}; + +/* DOL pixel array includes 4 pixel sync code each line */ +static struct crl_subdev_rect_rep imx274_1936_2376_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1936, + .out_rect.height = 2376, + } +}; + +static struct crl_subdev_rect_rep imx274_1288_738_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3868, + .out_rect.height = 4536, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3868, + .in_rect.height = 4536, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1288, + .out_rect.height = 738, + } +}; + +static struct crl_mode_rep imx274_modes[] = { + { + /* mode 0 12bit all pixel scan per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_3864_2202_rects), + .sd_rects = imx274_3864_2202_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 3864, + .height = 2202, + .min_llp = 493, /* 01EDh */ + .min_fll = 4868, /* default 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = + ARRAY_SIZE(imx274_3864_2202_RAW12_NORMAL), + .mode_regs = imx274_3864_2202_RAW12_NORMAL, + }, + { + /* mode 1 10bit all pixel scan per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_3864_2174_rects), + .sd_rects = imx274_3864_2174_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 3864, + .height = 2174, + .min_llp = 493, /* 01EDh */ + .min_fll = 4868, /* default 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = + ARRAY_SIZE(imx274_3864_2174_RAW10_NORMAL), + .mode_regs = imx274_3864_2174_RAW10_NORMAL, + }, + { + /* mode 1 DOL 10bit per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_3868_4536_rects), + .sd_rects = imx274_3868_4536_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3868, + .height = 4536, /* 2*(2160+22+VBP) */ + .min_llp = 1052, /* 041Ch */ + .min_fll = 2281, /* 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = + ARRAY_SIZE(imx274_3868_4536_RAW10_DOL), + .mode_regs = imx274_3868_4536_RAW10_DOL, + }, + { + /* mode 3 10bit all pixel scan per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_1932_1094_rects), + .sd_rects = imx274_1932_1094_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 1932, + .height = 1094, + .min_llp = 493, /* 01EDh */ + .min_fll = 4868, /* default 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE( + imx274_1932_1094_RAW10_NORMAL), + .mode_regs = imx274_1932_1094_RAW10_NORMAL, + }, + { + /* mode 3 12bit all pixel scan per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_1932_1094_rects), + .sd_rects = imx274_1932_1094_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 1932, + .height = 1094, + .min_llp = 493, /* 01EDh */ + .min_fll = 4868, /* default 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE( + imx274_1932_1094_RAW12_NORMAL), + .mode_regs = imx274_1932_1094_RAW12_NORMAL, + }, + { + /* mode 3 DOL bit10 per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_1936_2376_rects), + .sd_rects = imx274_1936_2376_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1936, + .height = 2376, + .min_llp = 1052, /* 041Ch */ + .min_fll = 2281, /* 30fps */ + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE( + imx274_1936_2376_RAW10_DOL), + .mode_regs = imx274_1936_2376_RAW10_DOL, + }, + { + /* mode 5 bit10 per datasheet */ + .sd_rects_items = ARRAY_SIZE(imx274_1288_738_rects), + .sd_rects = imx274_1288_738_rects, + .binn_hor = 3, + .binn_vert = 6, + .scale_m = 1, + .width = 1288, + .height = 738, + .min_llp = 260, + .min_fll = 2310, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE( + imx274_1288_738_RAW10_NORMAL), + .mode_regs = imx274_1288_738_RAW10_NORMAL, + }, +}; + +struct crl_sensor_subdev_config imx274_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx274 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx274 pixel array", + } +}; + +static struct crl_sensor_limits imx274_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 3868, /* pixel area length and width */ + .y_addr_max = 4536, + .min_frame_length_lines = 1111, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 260, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data imx274_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + } +}; + +static struct crl_csi_data_fmt imx274_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, /* default order */ + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, /* default order */ + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + } +}; + +static struct crl_v4l2_ctrl imx274_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1111, + .data.std_data.max = IMX274_VMAX, + .data.std_data.step = 1, + .data.std_data.def = 1111, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_fll_regs), + .regs = imx274_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 260, + .data.std_data.max = IMX274_HMAX, + .data.std_data.step = 1, + .data.std_data.def = 260, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_llp_regs), + .regs = imx274_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 6, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_dig_gain_regs), + .regs = imx274_dig_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0x7A5, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_ana_gain_global_regs), + .regs = imx274_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 4, + .data.std_data.max = IMX274_MAX_SHS2, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_shr_regs), + .regs = imx274_shr_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .name = "CRL_CID_EXPOSURE_SHS1", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 4, + .data.std_data.max = IMX274_MAX_SHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x06, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_shs1_regs), + .regs = imx274_shs1_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS2, + .name = "CRL_CID_EXPOSURE_SHS2", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 10, + .data.std_data.max = IMX274_MAX_SHS2, + .data.std_data.step = 1, + .data.std_data.def = 0x2d, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_shs2_regs), + .regs = imx274_shs2_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_RHS1, + .name = "CRL_CID_EXPOSURE_RHS1", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6, + .data.std_data.max = IMX274_MAX_RHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x56, /* Fixed to 86 by default */ + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx274_rhs1_regs), + .regs = imx274_rhs1_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_SENSOR_MODE, + .name = "CRL_CID_SENSOR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = ARRAY_SIZE(imx274_modes) - 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops imx274_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops imx274_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc imx274_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx274_frame_desc_width_ops), + .ops = imx274_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx274_frame_desc_height_ops), + .ops = imx274_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +static struct crl_power_seq_entity imx274_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +struct crl_sensor_configuration imx274_crl_configuration = { + + .power_items = ARRAY_SIZE(imx274_power_items), + .power_entities = imx274_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx274_powerup_standby), + .powerup_regs = imx274_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx274_sensor_detect_regset), + .id_regs = imx274_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx274_sensor_subdevs), + .subdevs = imx274_sensor_subdevs, + + .sensor_limits = &imx274_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx274_pll_configurations), + .pll_configs = imx274_pll_configurations, + + .modes_items = ARRAY_SIZE(imx274_modes), + .modes = imx274_modes, + + .streamon_regs_items = ARRAY_SIZE(imx274_streamon_regs), + .streamon_regs = imx274_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx274_streamoff_regs), + .streamoff_regs = imx274_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx274_v4l2_ctrls), + .v4l2_ctrl_bank = imx274_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx274_crl_csi_data_fmt), + .csi_fmts = imx274_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx274_flip_configurations), + .flip_data = imx274_flip_configurations, + + .frame_desc_entries = ARRAY_SIZE(imx274_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx274_frame_desc, + +}; + +#endif /* __CRLMODULE_IMX274_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx290_configuration.h b/drivers/media/i2c/crlmodule/crl_imx290_configuration.h new file mode 100644 index 000000000000..6a3561bea27f --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx290_configuration.h @@ -0,0 +1,1078 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Yuning Pu + * + */ + +#ifndef __CRLMODULE_IMX290_CONFIGURATION_H_ +#define __CRLMODULE_IMX290_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define IMX290_REG_STANDBY 0x3000 +#define IMX290_REG_XMSTA 0x3002 + +#define IMX290_HMAX 65535 +#define IMX290_VMAX 131071 +#define IMX290_MAX_SHS1 (IMX290_VMAX - 2) + +static struct crl_register_write_rep imx290_pll_891mbps[] = { + {0x3405, CRL_REG_LEN_08BIT, 0x00}, /* repetition */ + {0x3407, CRL_REG_LEN_08BIT, 0x03}, /* physical lane num(fixed) */ + {0x3009, CRL_REG_LEN_08BIT, 0x00}, /* FRSEL FDG_SEL */ + {0x300F, CRL_REG_LEN_08BIT, 0x00}, /* fixed setting */ + {0x3010, CRL_REG_LEN_08BIT, 0x21}, + {0x3012, CRL_REG_LEN_08BIT, 0x64}, + {0x3414, CRL_REG_LEN_08BIT, 0x0A}, + {0x3415, CRL_REG_LEN_08BIT, 0x00}, + {0x3016, CRL_REG_LEN_08BIT, 0x09}, /* changed */ + {0x3119, CRL_REG_LEN_08BIT, 0x9E}, + {0x311C, CRL_REG_LEN_08BIT, 0x1E}, + {0x311E, CRL_REG_LEN_08BIT, 0x08}, + {0x3128, CRL_REG_LEN_08BIT, 0x05}, + {0x332C, CRL_REG_LEN_08BIT, 0xD3}, + {0x332D, CRL_REG_LEN_08BIT, 0x10}, + {0x332E, CRL_REG_LEN_08BIT, 0x0D}, + {0x313D, CRL_REG_LEN_08BIT, 0x83}, + {0x3443, CRL_REG_LEN_08BIT, 0x03}, /* csi_lane_mode(fixed) */ + {0x3444, CRL_REG_LEN_08BIT, 0x20}, /* extck_freq */ + {0x3445, CRL_REG_LEN_08BIT, 0x25}, + {0x3446, CRL_REG_LEN_08BIT, 0x77}, /* tclkpost */ + {0x3447, CRL_REG_LEN_08BIT, 0x00}, + {0x3448, CRL_REG_LEN_08BIT, 0x67}, /* thszero */ + {0x3449, CRL_REG_LEN_08BIT, 0x00}, + {0x344A, CRL_REG_LEN_08BIT, 0x47}, /* thsprepare */ + {0x344B, CRL_REG_LEN_08BIT, 0x00}, + {0x344C, CRL_REG_LEN_08BIT, 0x37}, /* thstrail */ + {0x344D, CRL_REG_LEN_08BIT, 0x00}, + {0x344E, CRL_REG_LEN_08BIT, 0x3F}, /* thstrail */ + {0x344F, CRL_REG_LEN_08BIT, 0x00}, + {0x3150, CRL_REG_LEN_08BIT, 0x03}, + {0x3450, CRL_REG_LEN_08BIT, 0xFF}, /* tclkzero */ + {0x3451, CRL_REG_LEN_08BIT, 0x00}, + {0x3452, CRL_REG_LEN_08BIT, 0x3F}, /* tclkprepare */ + {0x3453, CRL_REG_LEN_08BIT, 0x00}, + {0x3454, CRL_REG_LEN_08BIT, 0x37}, /* tlpx */ + {0x3455, CRL_REG_LEN_08BIT, 0x00}, + {0x3358, CRL_REG_LEN_08BIT, 0x06}, /* fixed setting */ + {0x3359, CRL_REG_LEN_08BIT, 0xE1}, + {0x335A, CRL_REG_LEN_08BIT, 0x11}, + {0x305C, CRL_REG_LEN_08BIT, 0x18}, /* incksel1 */ + {0x305D, CRL_REG_LEN_08BIT, 0x03}, /* incksel2 */ + {0x305E, CRL_REG_LEN_08BIT, 0x20}, /* incksel3 */ + {0x315E, CRL_REG_LEN_08BIT, 0x1A}, /* incksel5 */ + {0x305F, CRL_REG_LEN_08BIT, 0x01}, /* incksel4 */ + {0x3360, CRL_REG_LEN_08BIT, 0x1E}, + {0x3361, CRL_REG_LEN_08BIT, 0x61}, + {0x3362, CRL_REG_LEN_08BIT, 0x10}, + {0x3164, CRL_REG_LEN_08BIT, 0x1A}, /* incksel6 */ + {0x3070, CRL_REG_LEN_08BIT, 0x02}, + {0x3071, CRL_REG_LEN_08BIT, 0x11}, + {0x317E, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x49}, /* inclsel7 */ + {0x309B, CRL_REG_LEN_08BIT, 0x10}, + {0x309C, CRL_REG_LEN_08BIT, 0x22}, + {0x30A2, CRL_REG_LEN_08BIT, 0x02}, + {0x30A6, CRL_REG_LEN_08BIT, 0x20}, + {0x30A8, CRL_REG_LEN_08BIT, 0x20}, + {0x30AA, CRL_REG_LEN_08BIT, 0x20}, + {0x30AC, CRL_REG_LEN_08BIT, 0x20}, + {0x30B0, CRL_REG_LEN_08BIT, 0x43}, + {0x33B0, CRL_REG_LEN_08BIT, 0x50}, + {0x33B2, CRL_REG_LEN_08BIT, 0x1A}, + {0x33B3, CRL_REG_LEN_08BIT, 0x04}, + {0x32B8, CRL_REG_LEN_08BIT, 0x50}, + {0x32B9, CRL_REG_LEN_08BIT, 0x10}, + {0x32BA, CRL_REG_LEN_08BIT, 0x00}, + {0x32BB, CRL_REG_LEN_08BIT, 0x04}, + {0x32C8, CRL_REG_LEN_08BIT, 0x50}, + {0x32C9, CRL_REG_LEN_08BIT, 0x10}, + {0x32CA, CRL_REG_LEN_08BIT, 0x00}, + {0x32CB, CRL_REG_LEN_08BIT, 0x04}, +}; + +/* 445Mbps for imx290 1080p 30fps */ +static struct crl_register_write_rep imx290_pll_445mbps[] = { + {0x3405, CRL_REG_LEN_08BIT, 0x20}, /* repetition */ + {0x3407, CRL_REG_LEN_08BIT, 0x03}, /* physical lane num(fixed) */ + {0x3009, CRL_REG_LEN_08BIT, 0x02}, /* FRSEL FDG_SEL */ + {0x300F, CRL_REG_LEN_08BIT, 0x00}, /* fixed setting */ + {0x3010, CRL_REG_LEN_08BIT, 0x21}, + {0x3012, CRL_REG_LEN_08BIT, 0x64}, + {0x3414, CRL_REG_LEN_08BIT, 0x0A}, + {0x3016, CRL_REG_LEN_08BIT, 0x09}, /* changed */ + {0x3119, CRL_REG_LEN_08BIT, 0x9E}, + {0x311C, CRL_REG_LEN_08BIT, 0x1E}, + {0x311E, CRL_REG_LEN_08BIT, 0x08}, + {0x3128, CRL_REG_LEN_08BIT, 0x05}, + {0x332C, CRL_REG_LEN_08BIT, 0xD3}, + {0x332D, CRL_REG_LEN_08BIT, 0x10}, + {0x332E, CRL_REG_LEN_08BIT, 0x0D}, + {0x313D, CRL_REG_LEN_08BIT, 0x83}, + {0x3443, CRL_REG_LEN_08BIT, 0x03}, /* csi_lane_mode(fixed) */ + {0x3444, CRL_REG_LEN_08BIT, 0x20}, /* extck_freq */ + {0x3445, CRL_REG_LEN_08BIT, 0x25}, + {0x3446, CRL_REG_LEN_08BIT, 0x47}, /* tclkpost */ + {0x3447, CRL_REG_LEN_08BIT, 0x00}, + {0x3448, CRL_REG_LEN_08BIT, 0x1F}, /* thszero */ + {0x3449, CRL_REG_LEN_08BIT, 0x00}, + {0x344A, CRL_REG_LEN_08BIT, 0x17}, /* thsprepare */ + {0x344B, CRL_REG_LEN_08BIT, 0x00}, + {0x344C, CRL_REG_LEN_08BIT, 0x0F}, /* thstrail */ + {0x344D, CRL_REG_LEN_08BIT, 0x00}, + {0x344E, CRL_REG_LEN_08BIT, 0x17}, /* thstrail */ + {0x344F, CRL_REG_LEN_08BIT, 0x00}, + {0x3150, CRL_REG_LEN_08BIT, 0x03}, + {0x3450, CRL_REG_LEN_08BIT, 0x47}, /* tclkzero */ + {0x3451, CRL_REG_LEN_08BIT, 0x00}, + {0x3452, CRL_REG_LEN_08BIT, 0x0F}, /* tclkprepare */ + {0x3453, CRL_REG_LEN_08BIT, 0x00}, + {0x3454, CRL_REG_LEN_08BIT, 0x0F}, /* tlpx */ + {0x3455, CRL_REG_LEN_08BIT, 0x00}, + {0x3358, CRL_REG_LEN_08BIT, 0x06}, /* fixed setting */ + {0x3359, CRL_REG_LEN_08BIT, 0xE1}, + {0x335A, CRL_REG_LEN_08BIT, 0x11}, + {0x305C, CRL_REG_LEN_08BIT, 0x18}, /* incksel1 */ + {0x305D, CRL_REG_LEN_08BIT, 0x03}, /* incksel2 */ + {0x305E, CRL_REG_LEN_08BIT, 0x20}, /* incksel3 */ + {0x315E, CRL_REG_LEN_08BIT, 0x1A}, /* incksel5 */ + {0x305F, CRL_REG_LEN_08BIT, 0x01}, /* incksel4 */ + {0x3360, CRL_REG_LEN_08BIT, 0x1E}, + {0x3361, CRL_REG_LEN_08BIT, 0x61}, + {0x3362, CRL_REG_LEN_08BIT, 0x10}, + {0x3164, CRL_REG_LEN_08BIT, 0x1A}, /* incksel6 */ + {0x3070, CRL_REG_LEN_08BIT, 0x02}, + {0x3071, CRL_REG_LEN_08BIT, 0x11}, + {0x317E, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x49}, /* inclsel7 */ + {0x309B, CRL_REG_LEN_08BIT, 0x10}, + {0x309C, CRL_REG_LEN_08BIT, 0x22}, + {0x30A2, CRL_REG_LEN_08BIT, 0x02}, + {0x30A6, CRL_REG_LEN_08BIT, 0x20}, + {0x30A8, CRL_REG_LEN_08BIT, 0x20}, + {0x30AA, CRL_REG_LEN_08BIT, 0x20}, + {0x30AC, CRL_REG_LEN_08BIT, 0x20}, + {0x30B0, CRL_REG_LEN_08BIT, 0x43}, + {0x33B0, CRL_REG_LEN_08BIT, 0x50}, + {0x33B2, CRL_REG_LEN_08BIT, 0x1A}, + {0x33B3, CRL_REG_LEN_08BIT, 0x04}, + {0x32B8, CRL_REG_LEN_08BIT, 0x50}, + {0x32B9, CRL_REG_LEN_08BIT, 0x10}, + {0x32BA, CRL_REG_LEN_08BIT, 0x00}, + {0x32BB, CRL_REG_LEN_08BIT, 0x04}, + {0x32C8, CRL_REG_LEN_08BIT, 0x50}, + {0x32C9, CRL_REG_LEN_08BIT, 0x10}, + {0x32CA, CRL_REG_LEN_08BIT, 0x00}, + {0x32CB, CRL_REG_LEN_08BIT, 0x04}, +}; + +static struct crl_register_write_rep imx290_fmt_raw10[] = { + {0x3005, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT */ + {0x300A, CRL_REG_LEN_08BIT, 0x3C}, /* BLKLEVEL */ + {0x3129, CRL_REG_LEN_08BIT, 0x1D}, /* ADBIT1 */ + {0x3441, CRL_REG_LEN_08BIT, 0x0A}, /* CSI_DT_FMT */ + {0x3442, CRL_REG_LEN_08BIT, 0x0A}, + {0x3046, CRL_REG_LEN_08BIT, 0x00}, /* ODBIT OPORTSEL */ + {0x317C, CRL_REG_LEN_08BIT, 0x12}, /* ADBIT2 */ + {0x31EC, CRL_REG_LEN_08BIT, 0x37}, /* ADBIT3 */ +}; + +static struct crl_register_write_rep imx290_fmt_raw12[] = { + {0x3005, CRL_REG_LEN_08BIT, 0x01}, /* ADBIT */ + {0x300A, CRL_REG_LEN_08BIT, 0xF0}, /* BLKLEVEL */ + {0x3129, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT1 */ + {0x3441, CRL_REG_LEN_08BIT, 0x0C}, /* CSI_DT_FMT */ + {0x3442, CRL_REG_LEN_08BIT, 0x0C}, + {0x3046, CRL_REG_LEN_08BIT, 0x01}, /* ODBIT OPORTSEL */ + {0x317C, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT2 */ + {0x31EC, CRL_REG_LEN_08BIT, 0x0E}, /* ADBIT3 */ +}; + +static struct crl_register_write_rep imx290_powerup_standby[] = { + {IMX290_REG_STANDBY, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 20, 0x00}, + {IMX290_REG_XMSTA, CRL_REG_LEN_08BIT, 0x01}, +}; + +/* Horizontal dumpy added 1097(1094+3) */ +static struct crl_register_write_rep imx290_1948_1096_37MHZ_CROPPING[] = { + /*TODO need a test if necessary to open XMSTA*/ + {0x3000, CRL_REG_LEN_08BIT, 0x01}, /* reset to standby mode */ + {0x3002, CRL_REG_LEN_08BIT, 0x01}, /* default:reset slave mode */ + {0x3005, CRL_REG_LEN_08BIT, 0x01}, /* ADBIT */ + {0x3405, CRL_REG_LEN_08BIT, 0x20}, /* repetition */ + {0x3007, CRL_REG_LEN_08BIT, 0x04}, /* H/V verse and WINMODE */ + {0x3407, CRL_REG_LEN_08BIT, 0x03}, /* physical lane num(fixed) */ + {0x3009, CRL_REG_LEN_08BIT, 0x02}, /* FRSEL FDG_SEL */ + {0x300A, CRL_REG_LEN_08BIT, 0xF0}, /* BLKLEVEL */ + {0x300F, CRL_REG_LEN_08BIT, 0x00}, /* fixed setting */ + {0x3010, CRL_REG_LEN_08BIT, 0x21}, + {0x3012, CRL_REG_LEN_08BIT, 0x64}, + {0x3414, CRL_REG_LEN_08BIT, 0x0A}, /* OPB_SIZE_V */ + {0x3016, CRL_REG_LEN_08BIT, 0x09}, + {0x3018, CRL_REG_LEN_08BIT, 0x65}, /* VMAX */ + {0x3019, CRL_REG_LEN_08BIT, 0x04}, + {0x3418, CRL_REG_LEN_08BIT, 0x49}, /* Y_OUT_SIZE */ + {0x3419, CRL_REG_LEN_08BIT, 0x04}, + {0x3119, CRL_REG_LEN_08BIT, 0x9E}, + {0x301C, CRL_REG_LEN_08BIT, 0x30}, /* HMAX */ + {0x301D, CRL_REG_LEN_08BIT, 0x11}, + {0x311C, CRL_REG_LEN_08BIT, 0x1E}, + {0x311E, CRL_REG_LEN_08BIT, 0x08}, + {0x3128, CRL_REG_LEN_08BIT, 0x05}, + {0x3129, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT1 */ + {0x332C, CRL_REG_LEN_08BIT, 0xD3}, + {0x332D, CRL_REG_LEN_08BIT, 0x10}, + {0x332E, CRL_REG_LEN_08BIT, 0x0D}, + {0x313D, CRL_REG_LEN_08BIT, 0x83}, + {0x3441, CRL_REG_LEN_08BIT, 0x0C}, /* CSI_DT_FMT */ + {0x3442, CRL_REG_LEN_08BIT, 0x0C}, + {0x3443, CRL_REG_LEN_08BIT, 0x03}, /* csi_lane_mode(fixed) */ + {0x3444, CRL_REG_LEN_08BIT, 0x20}, /* extck_freq */ + {0x3445, CRL_REG_LEN_08BIT, 0x25}, + {0x3046, CRL_REG_LEN_08BIT, 0x01}, /* ODBIT OPORTSEL */ + {0x3446, CRL_REG_LEN_08BIT, 0x47}, /* tclkpost */ + {0x3447, CRL_REG_LEN_08BIT, 0x00}, + {0x3448, CRL_REG_LEN_08BIT, 0x1F}, /* thszero */ + {0x3449, CRL_REG_LEN_08BIT, 0x00}, + {0x304B, CRL_REG_LEN_08BIT, 0x0A}, /* XH/VS OUTSEL */ + {0x344A, CRL_REG_LEN_08BIT, 0x17}, /* thsprepare */ + {0x344B, CRL_REG_LEN_08BIT, 0x00}, + {0x344C, CRL_REG_LEN_08BIT, 0x0F}, /* thstrail */ + {0x344D, CRL_REG_LEN_08BIT, 0x00}, + {0x344E, CRL_REG_LEN_08BIT, 0x17}, /* thstrail */ + {0x344F, CRL_REG_LEN_08BIT, 0x00}, + {0x3150, CRL_REG_LEN_08BIT, 0x03}, + {0x3450, CRL_REG_LEN_08BIT, 0x47}, /* tclkzero */ + {0x3451, CRL_REG_LEN_08BIT, 0x00}, + {0x3452, CRL_REG_LEN_08BIT, 0x0F}, /* tclkprepare */ + {0x3453, CRL_REG_LEN_08BIT, 0x00}, + {0x3454, CRL_REG_LEN_08BIT, 0x0F}, /* tlpx */ + {0x3455, CRL_REG_LEN_08BIT, 0x00}, + {0x3358, CRL_REG_LEN_08BIT, 0x06}, /* fixed setting */ + {0x3359, CRL_REG_LEN_08BIT, 0xE1}, + {0x335A, CRL_REG_LEN_08BIT, 0x11}, + {0x305C, CRL_REG_LEN_08BIT, 0x18}, /* incksel1 */ + {0x305D, CRL_REG_LEN_08BIT, 0x03}, /* incksel2 */ + {0x305E, CRL_REG_LEN_08BIT, 0x20}, /* incksel3 */ + {0x315E, CRL_REG_LEN_08BIT, 0x1A}, /* incksel5 */ + {0x305F, CRL_REG_LEN_08BIT, 0x01}, /* incksel4 */ + {0x3360, CRL_REG_LEN_08BIT, 0x1E}, + {0x3361, CRL_REG_LEN_08BIT, 0x61}, + {0x3362, CRL_REG_LEN_08BIT, 0x10}, + {0x3164, CRL_REG_LEN_08BIT, 0x1A}, /* incksel6 */ + {0x3070, CRL_REG_LEN_08BIT, 0x02}, + {0x3071, CRL_REG_LEN_08BIT, 0x11}, + {0x3472, CRL_REG_LEN_08BIT, 0x9C}, /* X_OUT_SIZE */ + {0x3473, CRL_REG_LEN_08BIT, 0x07}, + {0x317C, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT2 */ + {0x317E, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x49}, /* inclsel7 */ + {0x309B, CRL_REG_LEN_08BIT, 0x10}, + {0x309C, CRL_REG_LEN_08BIT, 0x22}, + {0x30A2, CRL_REG_LEN_08BIT, 0x02}, + {0x30A6, CRL_REG_LEN_08BIT, 0x20}, + {0x30A8, CRL_REG_LEN_08BIT, 0x20}, + {0x30AA, CRL_REG_LEN_08BIT, 0x20}, + {0x30AC, CRL_REG_LEN_08BIT, 0x20}, + {0x30B0, CRL_REG_LEN_08BIT, 0x43}, + {0x33B0, CRL_REG_LEN_08BIT, 0x50}, + {0x33B2, CRL_REG_LEN_08BIT, 0x1A}, + {0x33B3, CRL_REG_LEN_08BIT, 0x04}, + {0x32B8, CRL_REG_LEN_08BIT, 0x50}, + {0x32B9, CRL_REG_LEN_08BIT, 0x10}, + {0x32BA, CRL_REG_LEN_08BIT, 0x00}, + {0x32BB, CRL_REG_LEN_08BIT, 0x04}, + {0x32C8, CRL_REG_LEN_08BIT, 0x50}, + {0x32C9, CRL_REG_LEN_08BIT, 0x10}, + {0x32CA, CRL_REG_LEN_08BIT, 0x00}, + {0x32CB, CRL_REG_LEN_08BIT, 0x04}, + {0x31EC, CRL_REG_LEN_08BIT, 0x0E}, /* ADBIT3 */ + /* WINDOW CROPPING */ + {0x303C, CRL_REG_LEN_08BIT, 0x01}, + {0x303D, CRL_REG_LEN_08BIT, 0x00}, + {0x303E, CRL_REG_LEN_08BIT, 0x48}, + {0x303F, CRL_REG_LEN_08BIT, 0x04}, +}; + +static struct crl_register_write_rep imx290_1952_3435_37MHZ_CROPPING[] = { + /*TODO need a test if necessary to open XMSTA*/ + {0x3000, CRL_REG_LEN_08BIT, 0x01}, /* reset to standby mode */ + {0x3002, CRL_REG_LEN_08BIT, 0x01}, /* default:reset to slave mode */ + {0x3005, CRL_REG_LEN_08BIT, 0x00}, /* ADBIT */ + {0x3405, CRL_REG_LEN_08BIT, 0x00}, /* repetition */ + {0x3106, CRL_REG_LEN_08BIT, 0x33}, + {0x3007, CRL_REG_LEN_08BIT, 0x00}, /* H/V verse and WINMODE */ + {0x3407, CRL_REG_LEN_08BIT, 0x03}, /* physical lane num(fixed) */ + {0x3009, CRL_REG_LEN_08BIT, 0x00}, /* FRSEL FDG_SEL */ + {0x300A, CRL_REG_LEN_08BIT, 0x3C}, /* BLKLEVEL */ + {0x300C, CRL_REG_LEN_08BIT, 0x21}, + {0x300F, CRL_REG_LEN_08BIT, 0x00}, /* fixed setting */ + {0x3010, CRL_REG_LEN_08BIT, 0x21}, + {0x3012, CRL_REG_LEN_08BIT, 0x64}, + {0x3414, CRL_REG_LEN_08BIT, 0x0A}, /* OPB_SIZE_V */ + {0x3415, CRL_REG_LEN_08BIT, 0x00}, + {0x3016, CRL_REG_LEN_08BIT, 0x09}, + {0x3018, CRL_REG_LEN_08BIT, 0x65}, /* VMAX */ + {0x3019, CRL_REG_LEN_08BIT, 0x04}, + {0x3418, CRL_REG_LEN_08BIT, 0x55}, /* Y_OUT_SIZE */ + {0x3419, CRL_REG_LEN_08BIT, 0x11}, + {0x3119, CRL_REG_LEN_08BIT, 0x9E}, + {0x301C, CRL_REG_LEN_08BIT, 0x4C}, /* HMAX */ + {0x301D, CRL_REG_LEN_08BIT, 0x04}, + {0x311C, CRL_REG_LEN_08BIT, 0x1E}, + {0x311E, CRL_REG_LEN_08BIT, 0x08}, + {0x3020, CRL_REG_LEN_08BIT, 0x04}, /* SHS1 */ + {0x3021, CRL_REG_LEN_08BIT, 0x00}, + {0x3024, CRL_REG_LEN_08BIT, 0x89}, /* SHS2 */ + {0x3025, CRL_REG_LEN_08BIT, 0x00}, + {0x3028, CRL_REG_LEN_08BIT, 0x93}, /* SHS3 */ + {0x3029, CRL_REG_LEN_08BIT, 0x01}, + {0x3128, CRL_REG_LEN_08BIT, 0x05}, + {0x3129, CRL_REG_LEN_08BIT, 0x1D}, /* ADBIT1 */ + {0x332C, CRL_REG_LEN_08BIT, 0xD3}, + {0x332D, CRL_REG_LEN_08BIT, 0x10}, + {0x332E, CRL_REG_LEN_08BIT, 0x0D}, + {0x3030, CRL_REG_LEN_08BIT, 0x85}, /* RHS1 */ + {0x3031, CRL_REG_LEN_08BIT, 0x00}, + {0x3034, CRL_REG_LEN_08BIT, 0x92}, /* RHS2 */ + {0x3035, CRL_REG_LEN_08BIT, 0x00}, + {0x313D, CRL_REG_LEN_08BIT, 0x83}, + {0x3441, CRL_REG_LEN_08BIT, 0x0A}, /* CSI_DT_FMT */ + {0x3442, CRL_REG_LEN_08BIT, 0x0A}, + {0x3443, CRL_REG_LEN_08BIT, 0x03}, /* csi_lane_mode(fixed) */ + {0x3444, CRL_REG_LEN_08BIT, 0x20}, /* extck_freq */ + {0x3045, CRL_REG_LEN_08BIT, 0x05}, /* DOL sp */ + {0x3445, CRL_REG_LEN_08BIT, 0x25}, + {0x3046, CRL_REG_LEN_08BIT, 0x00}, /* ODBIT OPORTSEL */ + {0x3446, CRL_REG_LEN_08BIT, 0x77}, /* tclkpost */ + {0x3447, CRL_REG_LEN_08BIT, 0x00}, + {0x3448, CRL_REG_LEN_08BIT, 0x67}, /* thszero */ + {0x3449, CRL_REG_LEN_08BIT, 0x00}, + {0x304B, CRL_REG_LEN_08BIT, 0x0A}, /* XH/VS OUTSEL */ + {0x344A, CRL_REG_LEN_08BIT, 0x47}, /* thsprepare */ + {0x344B, CRL_REG_LEN_08BIT, 0x00}, + {0x344C, CRL_REG_LEN_08BIT, 0x37}, /* thstrail */ + {0x344D, CRL_REG_LEN_08BIT, 0x00}, + {0x344E, CRL_REG_LEN_08BIT, 0x3F}, /* thstrail */ + {0x344F, CRL_REG_LEN_08BIT, 0x00}, + {0x3150, CRL_REG_LEN_08BIT, 0x03}, + {0x3450, CRL_REG_LEN_08BIT, 0xFF}, /* tclkzero */ + {0x3451, CRL_REG_LEN_08BIT, 0x00}, + {0x3452, CRL_REG_LEN_08BIT, 0x3F}, /* tclkprepare */ + {0x3453, CRL_REG_LEN_08BIT, 0x00}, + {0x3454, CRL_REG_LEN_08BIT, 0x37}, /* tlpx */ + {0x3455, CRL_REG_LEN_08BIT, 0x00}, + {0x3358, CRL_REG_LEN_08BIT, 0x06}, /* fixed setting */ + {0x3359, CRL_REG_LEN_08BIT, 0xE1}, + {0x335A, CRL_REG_LEN_08BIT, 0x11}, + {0x305C, CRL_REG_LEN_08BIT, 0x18}, /* incksel1 */ + {0x305D, CRL_REG_LEN_08BIT, 0x03}, /* incksel2 */ + {0x305E, CRL_REG_LEN_08BIT, 0x20}, /* incksel3 */ + {0x315E, CRL_REG_LEN_08BIT, 0x1A}, /* incksel5 */ + {0x305F, CRL_REG_LEN_08BIT, 0x01}, /* incksel4 */ + {0x3360, CRL_REG_LEN_08BIT, 0x1E}, + {0x3361, CRL_REG_LEN_08BIT, 0x61}, + {0x3362, CRL_REG_LEN_08BIT, 0x10}, + {0x3164, CRL_REG_LEN_08BIT, 0x1A}, /* incksel6 */ + {0x3070, CRL_REG_LEN_08BIT, 0x02}, + {0x3071, CRL_REG_LEN_08BIT, 0x11}, + {0x3472, CRL_REG_LEN_08BIT, 0xA0}, /* X_OUT_SIZE */ + {0x3473, CRL_REG_LEN_08BIT, 0x07}, + {0x347B, CRL_REG_LEN_08BIT, 0x23}, + {0x317C, CRL_REG_LEN_08BIT, 0x12}, /* ADBIT2 */ + {0x317E, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x49}, /* inclsel7 */ + {0x309B, CRL_REG_LEN_08BIT, 0x10}, + {0x309C, CRL_REG_LEN_08BIT, 0x22}, + {0x30A2, CRL_REG_LEN_08BIT, 0x02}, + {0x30A6, CRL_REG_LEN_08BIT, 0x20}, + {0x30A8, CRL_REG_LEN_08BIT, 0x20}, + {0x30AA, CRL_REG_LEN_08BIT, 0x20}, + {0x30AC, CRL_REG_LEN_08BIT, 0x20}, + {0x30B0, CRL_REG_LEN_08BIT, 0x43}, + {0x33B0, CRL_REG_LEN_08BIT, 0x50}, + {0x33B2, CRL_REG_LEN_08BIT, 0x1A}, + {0x33B3, CRL_REG_LEN_08BIT, 0x04}, + {0x32B8, CRL_REG_LEN_08BIT, 0x50}, + {0x32B9, CRL_REG_LEN_08BIT, 0x10}, + {0x32BA, CRL_REG_LEN_08BIT, 0x00}, + {0x32BB, CRL_REG_LEN_08BIT, 0x04}, + {0x32C8, CRL_REG_LEN_08BIT, 0x50}, + {0x32C9, CRL_REG_LEN_08BIT, 0x10}, + {0x32CA, CRL_REG_LEN_08BIT, 0x00}, + {0x32CB, CRL_REG_LEN_08BIT, 0x04}, + {0x31EC, CRL_REG_LEN_08BIT, 0x37}, /* ADBIT3 */ +}; + +static struct crl_register_write_rep imx290_streamon_regs[] = { + {IMX290_REG_STANDBY, CRL_REG_LEN_08BIT, 0x00}, + {0x00, CRL_REG_LEN_DELAY, 50, 0x00}, /* Add a 50ms delay */ + {IMX290_REG_XMSTA, CRL_REG_LEN_08BIT, 0x00}, +}; + +static struct crl_register_write_rep imx290_streamoff_regs[] = { + {IMX290_REG_STANDBY, CRL_REG_LEN_08BIT, 0x01}, + {IMX290_REG_XMSTA, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_arithmetic_ops imx290_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + } +}; + +static struct crl_dynamic_register_access imx290_h_flip_regs[] = { + { + .address = 0x3007, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx290_hflip_ops), + .ops = imx290_hflip_ops, + .mask = 0x2, + } +}; + +static struct crl_dynamic_register_access imx290_v_flip_regs[] = { + { + .address = 0x3007, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + } +}; + +static struct crl_dynamic_register_access imx290_ana_gain_global_regs[] = { + { + .address = 0x3014, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* shs1[17:0] = fll - exposure - 1 */ +static struct crl_arithmetic_ops imx290_shs1_lsb_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + } +}; + +static struct crl_arithmetic_ops imx290_shs1_msb0_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx290_shs1_msb1_ops[] = { + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + .operand.entity_val = V4L2_CID_FRAME_LENGTH_LINES, + }, + { + .op = CRL_SUBTRACT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 16, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0x03, + } +}; + +static struct crl_dynamic_register_access imx290_shs1_regs[] = { + { + .address = 0x3020, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx290_shs1_lsb_ops), + .ops = imx290_shs1_lsb_ops, + .mask = 0xff, + }, + { + .address = 0x3021, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx290_shs1_msb0_ops), + .ops = imx290_shs1_msb0_ops, + .mask = 0xff, + }, + { + .address = 0x3022, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx290_shs1_msb1_ops), + .ops = imx290_shs1_msb1_ops, + .mask = 0xff, + }, +}; + +static struct crl_arithmetic_ops imx290_fll_msb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + } +}; + +static struct crl_arithmetic_ops imx290_fll_hsb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 16, + } +}; + +static struct crl_dynamic_register_access imx290_fll_regs[] = { + /* + * Use 8bits access since 24bits or 32bits access will fail + * TODO: root cause the 24bits and 32bits access issues + */ + { + .address = 0x3018, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, + { + .address = 0x3019, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx290_fll_msb_ops), + .ops = imx290_fll_msb_ops, + .mask = 0xff, + }, + { + .address = 0x301A, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(imx290_fll_hsb_ops), + .ops = imx290_fll_hsb_ops, + .mask = 0x3, + }, +}; + +static struct crl_dynamic_register_access imx290_llp_regs[] = { + { + .address = 0x301C, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_sensor_detect_config imx290_sensor_detect_regset[] = { + { + .reg = { 0x348F, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x348E, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration imx290_pll_configurations[] = { + /* + * IMX290 supports only 37.125MHz and 72.25MHz input clocks. + * IPU4 supports up to 38.4MHz, however the sensor module we use + * has its own oscillator. + * The "input_clk" value is specified here for the reference. + */ + { + .input_clk = 37125000, + .op_sys_clk = 222750000,/* 445500000/2 */ + .bitsperpixel = 12, + .pixel_rate_csi = 148500000, + .pixel_rate_pa = 148500000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx290_pll_445mbps), + .pll_regs = imx290_pll_445mbps, + }, + { + .input_clk = 37125000, + .op_sys_clk = 445500000,/* 891000000/2 */ + .bitsperpixel = 10, + .pixel_rate_csi = 356400000, + .pixel_rate_pa = 356400000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx290_pll_891mbps), + .pll_regs = imx290_pll_891mbps, + } +}; + +/* Temporary use a single rect range */ +static struct crl_subdev_rect_rep imx290_1948_1096_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 3435, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 3435, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 3435, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1948, + .out_rect.height = 1096, + } +}; + +static struct crl_subdev_rect_rep imx290_1952_3435_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 3435, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 3435, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1952, + .in_rect.height = 3435, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 3435, + } +}; + +static struct crl_mode_rep imx290_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx290_1948_1096_rects), + .sd_rects = imx290_1948_1096_rects, + .binn_hor = 1, + .binn_vert = 3, + .scale_m = 1, + .width = 1948, + .height = 1096, + .min_llp = 2220, + .min_fll = 1112, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx290_1948_1096_37MHZ_CROPPING), + .mode_regs = imx290_1948_1096_37MHZ_CROPPING, + }, + { + .sd_rects_items = ARRAY_SIZE(imx290_1952_3435_rects), + .sd_rects = imx290_1952_3435_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1952, + .height = 3435, + .min_llp = 2220, + .min_fll = 1112, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx290_1952_3435_37MHZ_CROPPING), + .mode_regs = imx290_1952_3435_37MHZ_CROPPING, + }, +}; + +struct crl_sensor_subdev_config imx290_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx290 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx290 pixel array", + } +}; + +static struct crl_sensor_limits imx290_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1952, + .y_addr_max = 3435, + .min_frame_length_lines = 320, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 380, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data imx290_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + } +}; + +static struct crl_csi_data_fmt imx290_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx290_fmt_raw10), + .regs = imx290_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx290_fmt_raw10), + .regs = imx290_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx290_fmt_raw10), + .regs = imx290_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx290_fmt_raw10), + .regs = imx290_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx290_fmt_raw12), + .regs = imx290_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, /*default pixel order*/ + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx290_fmt_raw12), + .regs = imx290_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx290_fmt_raw12), + .regs = imx290_fmt_raw12, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx290_fmt_raw12), + .regs = imx290_fmt_raw12, + } +}; + +static struct crl_v4l2_ctrl imx290_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_h_flip_regs), + .regs = imx290_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_v_flip_regs), + .regs = imx290_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 240, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_ana_gain_global_regs), + .regs = imx290_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = IMX290_MAX_SHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x264, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_shs1_regs), + .regs = imx290_shs1_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 720, + .data.std_data.max = IMX290_VMAX, + .data.std_data.step = 1, + .data.std_data.def = 1097, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_fll_regs), + .regs = imx290_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1948, + .data.std_data.max = IMX290_HMAX, + .data.std_data.step = 1, + .data.std_data.def = 1948, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx290_llp_regs), + .regs = imx290_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops imx290_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops imx290_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc imx290_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx290_frame_desc_width_ops), + .ops = imx290_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx290_frame_desc_height_ops), + .ops = imx290_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +static struct crl_power_seq_entity imx290_power_items[] = { + /* If your sensor uses IPU reference clock, make sure it's enabled here. */ + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +struct crl_sensor_configuration imx290_crl_configuration = { + + .power_items = ARRAY_SIZE(imx290_power_items), + .power_entities = imx290_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx290_powerup_standby), + .powerup_regs = imx290_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx290_sensor_detect_regset), + .id_regs = imx290_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx290_sensor_subdevs), + .subdevs = imx290_sensor_subdevs, + + .sensor_limits = &imx290_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx290_pll_configurations), + .pll_configs = imx290_pll_configurations, + + .modes_items = ARRAY_SIZE(imx290_modes), + .modes = imx290_modes, + + .streamon_regs_items = ARRAY_SIZE(imx290_streamon_regs), + .streamon_regs = imx290_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx290_streamoff_regs), + .streamoff_regs = imx290_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx290_v4l2_ctrls), + .v4l2_ctrl_bank = imx290_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx290_crl_csi_data_fmt), + .csi_fmts = imx290_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx290_flip_configurations), + .flip_data = imx290_flip_configurations, + + .frame_desc_entries = ARRAY_SIZE(imx290_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx290_frame_desc, +}; + +#endif /* __CRLMODULE_IMX274_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx318_configuration.h b/drivers/media/i2c/crlmodule/crl_imx318_configuration.h new file mode 100644 index 000000000000..631a6d10400f --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx318_configuration.h @@ -0,0 +1,1050 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Jouni Ukkonen + * + */ +#ifndef __CRLMODULE_IMX318_CONFIGURATION_H_ +#define __CRLMODULE_IMX318_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +static const struct crl_register_write_rep imx318_pll_1164mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* 24 Mhz */ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0111, CRL_REG_LEN_08BIT, 0x02 }, /* 2 = DPHY, 3 = CPHY */ + { 0x0112, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0113, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x4D }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0A }, + { 0x030B, CRL_REG_LEN_08BIT, 0x02 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x030E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030F, CRL_REG_LEN_08BIT, 0x84 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x12 }, + { 0x0821, CRL_REG_LEN_08BIT, 0x30 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static const struct crl_register_write_rep imx318_pll_8_1164mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* 24 Mhz */ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0111, CRL_REG_LEN_08BIT, 0x02 }, /* 2 = DPHY, 3 = CPHY */ + { 0x0112, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0113, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x4D }, + { 0x0309, CRL_REG_LEN_08BIT, 0x08 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x02 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x030E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030F, CRL_REG_LEN_08BIT, 0x84 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x12 }, + { 0x0821, CRL_REG_LEN_08BIT, 0x30 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static const struct crl_register_write_rep imx318_pll_1920mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* 24 Mhz */ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0111, CRL_REG_LEN_08BIT, 0x02 }, /* 2 = DPHY, 3 = CPHY */ + { 0x0112, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0113, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x4D }, + { 0x0309, CRL_REG_LEN_08BIT, 0x0A }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x030E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030F, CRL_REG_LEN_08BIT, 0x40 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x1E }, + { 0x0821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static const struct crl_register_write_rep imx318_pll_8_1920mbps[] = { + { 0x0136, CRL_REG_LEN_08BIT, 0x18 }, /* 24 Mhz */ + { 0x0137, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0111, CRL_REG_LEN_08BIT, 0x02 }, /* 2 = DPHY, 3 = CPHY */ + { 0x0112, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0113, CRL_REG_LEN_08BIT, 0x0A }, + { 0x0114, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x05 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x4D }, + { 0x0309, CRL_REG_LEN_08BIT, 0x08 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x030E, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030F, CRL_REG_LEN_08BIT, 0x40 }, + { 0x0820, CRL_REG_LEN_08BIT, 0x1E }, + { 0x0821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0822, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0823, CRL_REG_LEN_08BIT, 0x00 }, +}; + + +static const struct crl_register_write_rep imx318_powerup_regset[] = { + { 0x3067, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x1B }, + { 0x46C2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4877, CRL_REG_LEN_08BIT, 0x11 }, + { 0x487B, CRL_REG_LEN_08BIT, 0x4D }, + { 0x487F, CRL_REG_LEN_08BIT, 0x3B }, + { 0x4883, CRL_REG_LEN_08BIT, 0xB4 }, + { 0x4C6F, CRL_REG_LEN_08BIT, 0x5E }, + { 0x5113, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x5115, CRL_REG_LEN_08BIT, 0xF6 }, + { 0x5125, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x5127, CRL_REG_LEN_08BIT, 0xF8 }, + { 0x51CF, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x51E9, CRL_REG_LEN_08BIT, 0xF4 }, + { 0x5483, CRL_REG_LEN_08BIT, 0x7A }, + { 0x5485, CRL_REG_LEN_08BIT, 0x7C }, + { 0x5495, CRL_REG_LEN_08BIT, 0x7A }, + { 0x5497, CRL_REG_LEN_08BIT, 0x7F }, + { 0x5515, CRL_REG_LEN_08BIT, 0xC3 }, + { 0x5517, CRL_REG_LEN_08BIT, 0xC7 }, + { 0x552B, CRL_REG_LEN_08BIT, 0x7A }, + { 0x5535, CRL_REG_LEN_08BIT, 0x7A }, + { 0x5A35, CRL_REG_LEN_08BIT, 0x1B }, + { 0x5C13, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5D89, CRL_REG_LEN_08BIT, 0xB1 }, + { 0x5D8B, CRL_REG_LEN_08BIT, 0x2C }, + { 0x5D8D, CRL_REG_LEN_08BIT, 0x61 }, + { 0x5D8F, CRL_REG_LEN_08BIT, 0xE1 }, + { 0x5D91, CRL_REG_LEN_08BIT, 0x4D }, + { 0x5D93, CRL_REG_LEN_08BIT, 0xB4 }, + { 0x5D95, CRL_REG_LEN_08BIT, 0x41 }, + { 0x5D97, CRL_REG_LEN_08BIT, 0x96 }, + { 0x5D99, CRL_REG_LEN_08BIT, 0x37 }, + { 0x5D9B, CRL_REG_LEN_08BIT, 0x81 }, + { 0x5D9D, CRL_REG_LEN_08BIT, 0x31 }, + { 0x5D9F, CRL_REG_LEN_08BIT, 0x71 }, + { 0x5DA1, CRL_REG_LEN_08BIT, 0x2B }, + { 0x5DA3, CRL_REG_LEN_08BIT, 0x64 }, + { 0x5DA5, CRL_REG_LEN_08BIT, 0x27 }, + { 0x5DA7, CRL_REG_LEN_08BIT, 0x5A }, + { 0x6009, CRL_REG_LEN_08BIT, 0x03 }, + { 0x613A, CRL_REG_LEN_08BIT, 0x05 }, + { 0x613C, CRL_REG_LEN_08BIT, 0x23 }, + { 0x6142, CRL_REG_LEN_08BIT, 0x02 }, + { 0x6143, CRL_REG_LEN_08BIT, 0x62 }, + { 0x6144, CRL_REG_LEN_08BIT, 0x89 }, + { 0x6145, CRL_REG_LEN_08BIT, 0x0A }, + { 0x6146, CRL_REG_LEN_08BIT, 0x24 }, + { 0x6147, CRL_REG_LEN_08BIT, 0x28 }, + { 0x6148, CRL_REG_LEN_08BIT, 0x90 }, + { 0x6149, CRL_REG_LEN_08BIT, 0xA2 }, + { 0x614A, CRL_REG_LEN_08BIT, 0x40 }, + { 0x614B, CRL_REG_LEN_08BIT, 0x8A }, + { 0x614C, CRL_REG_LEN_08BIT, 0x01 }, + { 0x614D, CRL_REG_LEN_08BIT, 0x12 }, + { 0x614E, CRL_REG_LEN_08BIT, 0x2C }, + { 0x614F, CRL_REG_LEN_08BIT, 0x98 }, + { 0x6150, CRL_REG_LEN_08BIT, 0xA2 }, + { 0x615D, CRL_REG_LEN_08BIT, 0x37 }, + { 0x615E, CRL_REG_LEN_08BIT, 0xE6 }, + { 0x615F, CRL_REG_LEN_08BIT, 0x4B }, + { 0x616C, CRL_REG_LEN_08BIT, 0x41 }, + { 0x616D, CRL_REG_LEN_08BIT, 0x05 }, + { 0x616E, CRL_REG_LEN_08BIT, 0x48 }, + { 0x616F, CRL_REG_LEN_08BIT, 0xC5 }, + { 0x6174, CRL_REG_LEN_08BIT, 0xB9 }, + { 0x6175, CRL_REG_LEN_08BIT, 0x42 }, + { 0x6176, CRL_REG_LEN_08BIT, 0x44 }, + { 0x6177, CRL_REG_LEN_08BIT, 0xC3 }, + { 0x6178, CRL_REG_LEN_08BIT, 0x81 }, + { 0x6179, CRL_REG_LEN_08BIT, 0x78 }, + { 0x6182, CRL_REG_LEN_08BIT, 0x15 }, + { 0x6A5F, CRL_REG_LEN_08BIT, 0x03 }, + { 0x9302, CRL_REG_LEN_08BIT, 0xFF }, +}; + +static const struct crl_register_write_rep imx318_mode_full[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x15 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034B, CRL_REG_LEN_08BIT, 0x0F }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3010, CRL_REG_LEN_08BIT, 0x65 }, + { 0x3011, CRL_REG_LEN_08BIT, 0x11 }, + { 0x30FC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30FD, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3194, CRL_REG_LEN_08BIT, 0x01 }, + { 0x31A0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4711, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6669, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x10 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x15 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x10 }, + { 0x040F, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034C, CRL_REG_LEN_08BIT, 0x15 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x70 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x10 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3033, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3035, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3037, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3039, CRL_REG_LEN_08BIT, 0x00 }, + { 0x303B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306E, CRL_REG_LEN_08BIT, 0x0D }, + { 0x306F, CRL_REG_LEN_08BIT, 0x56 }, + { 0x6636, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6637, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3066, CRL_REG_LEN_08BIT, 0x00 }, + { 0x7B63, CRL_REG_LEN_08BIT, 0x00 }, + { 0x56FB, CRL_REG_LEN_08BIT, 0x50 }, + { 0x56FF, CRL_REG_LEN_08BIT, 0x50 }, + { 0x9323, CRL_REG_LEN_08BIT, 0x10 }, +}; + + +static const struct crl_register_write_rep imx318_mode_uhd[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x15 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0E }, + { 0x034B, CRL_REG_LEN_08BIT, 0x0F }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3010, CRL_REG_LEN_08BIT, 0x65 }, + { 0x3011, CRL_REG_LEN_08BIT, 0x11 }, + { 0x30FC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30FD, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3194, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4711, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6669, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x16 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x68 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x3A }, + { 0x040C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x040D, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x0B }, + { 0x040F, CRL_REG_LEN_08BIT, 0x9C }, + { 0x034C, CRL_REG_LEN_08BIT, 0x0F }, + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3033, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3035, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3037, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3039, CRL_REG_LEN_08BIT, 0x00 }, + { 0x303B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306E, CRL_REG_LEN_08BIT, 0x0D }, + { 0x306F, CRL_REG_LEN_08BIT, 0x56 }, + { 0x6636, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6637, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3066, CRL_REG_LEN_08BIT, 0x00 }, + { 0x7B63, CRL_REG_LEN_08BIT, 0x00 }, + { 0x56FB, CRL_REG_LEN_08BIT, 0x33 }, + { 0x56FF, CRL_REG_LEN_08BIT, 0x33 }, + { 0x9323, CRL_REG_LEN_08BIT, 0x16 }, +}; + +static const struct crl_register_write_rep imx318_mode_1080[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x15 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0E }, + { 0x034B, CRL_REG_LEN_08BIT, 0x0F }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x22 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3010, CRL_REG_LEN_08BIT, 0x65 }, + { 0x3011, CRL_REG_LEN_08BIT, 0x11 }, + { 0x30FC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30FD, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3194, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4711, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6669, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x16 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x34 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x1C }, + { 0x040C, CRL_REG_LEN_08BIT, 0x0A }, + { 0x040D, CRL_REG_LEN_08BIT, 0x50 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xCE }, + { 0x034C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x80 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x034F, CRL_REG_LEN_08BIT, 0x38 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3033, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3035, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3037, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3039, CRL_REG_LEN_08BIT, 0x00 }, + { 0x303B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306E, CRL_REG_LEN_08BIT, 0x0D }, + { 0x306F, CRL_REG_LEN_08BIT, 0x56 }, + { 0x6636, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6637, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3066, CRL_REG_LEN_08BIT, 0x00 }, + { 0x7B63, CRL_REG_LEN_08BIT, 0x00 }, + { 0x56FB, CRL_REG_LEN_08BIT, 0x33 }, + { 0x56FF, CRL_REG_LEN_08BIT, 0x33 }, + { 0x9323, CRL_REG_LEN_08BIT, 0x16 }, +}; + +static const struct crl_register_write_rep imx318_mode_720[] = { + { 0x0344, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0345, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0346, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0347, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0348, CRL_REG_LEN_08BIT, 0x15 }, + { 0x0349, CRL_REG_LEN_08BIT, 0x6F }, + { 0x034A, CRL_REG_LEN_08BIT, 0x0E }, + { 0x034B, CRL_REG_LEN_08BIT, 0x13 }, + { 0x0220, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0221, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0222, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0381, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0383, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0385, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0387, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0900, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0901, CRL_REG_LEN_08BIT, 0x44 }, + { 0x0902, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3010, CRL_REG_LEN_08BIT, 0x65 }, + { 0x3011, CRL_REG_LEN_08BIT, 0x11 }, + { 0x30FC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x30FD, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3194, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x31A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4711, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6669, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0401, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0404, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0405, CRL_REG_LEN_08BIT, 0x11 }, + { 0x0408, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0409, CRL_REG_LEN_08BIT, 0x04 }, + { 0x040A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x040B, CRL_REG_LEN_08BIT, 0x02 }, + { 0x040C, CRL_REG_LEN_08BIT, 0x05 }, + { 0x040D, CRL_REG_LEN_08BIT, 0x52 }, + { 0x040E, CRL_REG_LEN_08BIT, 0x02 }, + { 0x040F, CRL_REG_LEN_08BIT, 0xFE }, + { 0x034C, CRL_REG_LEN_08BIT, 0x05 }, + { 0x034D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x034E, CRL_REG_LEN_08BIT, 0x02 }, + { 0x034F, CRL_REG_LEN_08BIT, 0xD0 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3033, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3035, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3037, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3039, CRL_REG_LEN_08BIT, 0x00 }, + { 0x303B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x306E, CRL_REG_LEN_08BIT, 0x0D }, + { 0x306F, CRL_REG_LEN_08BIT, 0x56 }, + { 0x6636, CRL_REG_LEN_08BIT, 0x00 }, + { 0x6637, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3066, CRL_REG_LEN_08BIT, 0x00 }, + { 0x7B63, CRL_REG_LEN_08BIT, 0x00 }, + { 0x56FB, CRL_REG_LEN_08BIT, 0x33 }, + { 0x56FF, CRL_REG_LEN_08BIT, 0x33 }, + { 0x9323, CRL_REG_LEN_08BIT, 0x16 }, +}; + +static struct crl_register_write_rep imx318_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep imx318_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep imx318_data_fmt_width10[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0a0a } +}; + +static struct crl_register_write_rep imx318_data_fmt_width8[] = { + { 0x0112, CRL_REG_LEN_16BIT, 0x0808 } +}; + +static struct crl_arithmetic_ops imx318_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access imx318_h_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx318_v_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx318_vflip_ops), + .ops = imx318_vflip_ops, + .mask = 0x2, + }, +}; + +static struct crl_dynamic_register_access imx318_ana_gain_global_regs[] = { + { + .address = 0x0204, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx318_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access imx318_fll_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx318_llp_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_sensor_detect_config imx318_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0018, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration imx318_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 582000000, + .bitsperpixel = 10, + .pixel_rate_csi = 465600000, + .pixel_rate_pa = 799206000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx318_pll_1164mbps), + .pll_regs = imx318_pll_1164mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 582000000, + .bitsperpixel = 8, + .pixel_rate_csi = 465600000, + .pixel_rate_pa = 799206000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx318_pll_8_1164mbps), + .pll_regs = imx318_pll_8_1164mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 960000000, + .bitsperpixel = 10, + .pixel_rate_csi = 768000000, + .pixel_rate_pa = 799206000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx318_pll_1920mbps), + .pll_regs = imx318_pll_1920mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 960000000, + .bitsperpixel = 8, + .pixel_rate_csi = 960000000, + .pixel_rate_pa = 799206000, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx318_pll_8_1920mbps), + .pll_regs = imx318_pll_8_1920mbps, + }, + +}; + +static struct crl_subdev_rect_rep imx318_full_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 0, 5488, 4112 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 0, 5488, 4112 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 0, 5488, 4112 }, + }, +}; + + +static struct crl_subdev_rect_rep imx318_uhd_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 512, 5280, 3088 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5280, 3088 }, + .out_rect = { 0, 0, 5280, 3088 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 5280, 3088 }, + .out_rect = { 0, 0, 3840, 2160 }, + }, +}; + + +static struct crl_subdev_rect_rep imx318_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 512, 5488, 3088 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5488, 3088 }, + .out_rect = { 0, 0, 2744, 1544 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2744, 1544 }, + .out_rect = { 0, 0, 1920, 1080 }, + }, +}; + +static struct crl_subdev_rect_rep imx318_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 5488, 4112 }, + .out_rect = { 0, 516, 5488, 3088 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 5488, 3088 }, + .out_rect = { 0, 0, 1372, 772 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 1372, 772 }, + .out_rect = { 0, 0, 1280, 720 }, + }, +}; + +static struct crl_mode_rep imx318_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(imx318_full_rects), + .sd_rects = imx318_full_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 5488, + .height = 4112, + .min_llp = 6224, + .min_fll = 4280, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx318_mode_full), + .mode_regs = imx318_mode_full, + }, + { + .sd_rects_items = ARRAY_SIZE(imx318_uhd_rects), + .sd_rects = imx318_uhd_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 22, + .width = 3840, + .height = 2160, + .min_llp = 6224, + .min_fll = 3622, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx318_mode_uhd), + .mode_regs = imx318_mode_uhd, + }, + { + .sd_rects_items = ARRAY_SIZE(imx318_1080_rects), + .sd_rects = imx318_1080_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 22, + .width = 1920, + .height = 1080, + .min_llp = 6224, + .min_fll = 1600, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx318_mode_1080), + .mode_regs = imx318_mode_1080, + }, + { + .sd_rects_items = ARRAY_SIZE(imx318_720_rects), + .sd_rects = imx318_720_rects, + .binn_hor = 4, + .binn_vert = 4, + .scale_m = 17, + .width = 1280, + .height = 720, + .min_llp = 6224, + .min_fll = 904, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx318_mode_720), + .mode_regs = imx318_mode_720, + }, +}; + +static struct crl_sensor_subdev_config imx318_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "imx318 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx318 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx318 pixel array", + }, +}; + +static struct crl_sensor_limits imx318_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 5488, + .y_addr_max = 4112, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6224, /*TBD*/ + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data imx318_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +static struct crl_csi_data_fmt imx318_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = imx318_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx318_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx318_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = imx318_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx318_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx318_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx318_data_fmt_width8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 8, + .regs = imx318_data_fmt_width8, + }, +}; + +static const s64 imx318_op_sys_clock[] = { 582000000, + 582000000, + 960000000, + 960000000, }; + +static struct crl_v4l2_ctrl imx318_vl42_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = + ARRAY_SIZE(imx318_pll_configurations) - 1, + .data.v4l2_int_menu.menu = imx318_op_sys_clock, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 480, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx318_ana_gain_global_regs), + .regs = imx318_ana_gain_global_regs, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx318_exposure_regs), + .regs = imx318_exposure_regs, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx318_h_flip_regs), + .regs = imx318_h_flip_regs, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx318_v_flip_regs), + .regs = imx318_v_flip_regs, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 4130, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx318_fll_regs), + .regs = imx318_fll_regs, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 6024, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(imx318_llp_regs), + .regs = imx318_llp_regs, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx318_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1050000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VIO", + .val = 1800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VAF", + .val = 3000000, + .delay = 2000, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 10700, + }, +}; + + +struct crl_sensor_configuration imx318_crl_configuration = { + + .power_items = ARRAY_SIZE(imx318_power_items), + .power_entities = imx318_power_items, + + .powerup_regs_items = ARRAY_SIZE(imx318_powerup_regset), + .powerup_regs = imx318_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx318_sensor_detect_regset), + .id_regs = imx318_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx318_sensor_subdevs), + .subdevs = imx318_sensor_subdevs, + + .sensor_limits = &imx318_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx318_pll_configurations), + .pll_configs = imx318_pll_configurations, + + .modes_items = ARRAY_SIZE(imx318_modes), + .modes = imx318_modes, + .fail_safe_mode_index = 0, + + .streamon_regs_items = ARRAY_SIZE(imx318_streamon_regs), + .streamon_regs = imx318_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx318_streamoff_regs), + .streamoff_regs = imx318_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx318_vl42_ctrls), + .v4l2_ctrl_bank = imx318_vl42_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx318_crl_csi_data_fmt), + .csi_fmts = imx318_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx318_flip_configurations), + .flip_data = imx318_flip_configurations, + +}; + +#endif /* __CRLMODULE_imx318_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx477_common_regs.h b/drivers/media/i2c/crlmodule/crl_imx477_common_regs.h new file mode 100644 index 000000000000..eebb884df8c8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx477_common_regs.h @@ -0,0 +1,1096 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Alexei Zavjalov + * + */ + +#ifndef __CRLMODULE_IMX477_COMMON_REGS_H_ +#define __CRLMODULE_IMX477_COMMON_REGS_H_ + +#include "crlmodule-sensor-ds.h" + +#define IMX477_CAPTURE_MODE_MAX 10 + +static struct crl_dynamic_register_access imx477_fll_regs[] = { + { + .address = 0x0340, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx477_llp_regs[] = { + { + .address = 0x0342, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access imx477_exposure_regs[] = { + { + .address = 0x0202, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access imx477_ana_gain_global_regs[] = { + { + .address = 0x0204, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xfff, + }, +}; + +static struct crl_dynamic_register_access imx477_wdr_switch_regs[] = { +}; + +static struct crl_arithmetic_ops imx477_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access imx477_h_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access imx477_v_flip_regs[] = { + { + .address = 0x0101, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(imx477_vflip_ops), + .ops = imx477_vflip_ops, + .mask = 0x2, + }, +}; + +static struct crl_dynamic_register_access imx477_test_pattern_regs[] = { + { + .address = 0x0600, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* 1st exposure for DOL */ +static struct crl_dynamic_register_access imx477_shs1_regs[] = { + { + .address = 0x00EA, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* 2nd exposure for DOL */ +static struct crl_dynamic_register_access imx477_shs2_regs[] = { + { + .address = 0x00EC, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* 3rd exposure for DOL */ +static struct crl_dynamic_register_access imx477_shs3_regs[] = { + { + .address = 0x00EE, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* Line number of 2nd frame readout start from XVS for DOL */ +static struct crl_dynamic_register_access imx477_rhs1_regs[] = { + { + .address = 0x00E6, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* Line number of 3rd frame readout start from XVS for DOL */ +static struct crl_dynamic_register_access imx477_rhs2_regs[] = { + { + .address = 0x00E8, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set analog gain for 1st HDR frame */ +static struct crl_dynamic_register_access imx477_ana_gain_1st_regs[] = { + { + .address = 0x00F0, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set analog gain for 2nd HDR frames */ +static struct crl_dynamic_register_access imx477_ana_gain_2nd_regs[] = { + { + .address = 0x00F2, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set analog gain for 3rd HDR frames */ +static struct crl_dynamic_register_access imx477_ana_gain_3rd_regs[] = { + { + .address = 0x00F4, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set digital gain for 1st HDR frames */ +static struct crl_dynamic_register_access imx477_dig_gain_1st_regs[] = { + { + .address = 0x00F6, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set digital gain for 2nd HDR frames */ +static struct crl_dynamic_register_access imx477_dig_gain_2nd_regs[] = { + { + .address = 0x00F8, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +/* set digital gain for 3rd HDR frames */ +static struct crl_dynamic_register_access imx477_dig_gain_3rd_regs[] = { + { + .address = 0x00FA, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static const char * const imx477_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Color Bars", + "Fade to Grey Color Bars", + "PN9", +}; + +static struct crl_v4l2_ctrl imx477_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 720, + .data.std_data.max = 131071, + .data.std_data.step = 1, + .data.std_data.def = 8209, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_fll_regs), + .regs = imx477_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1280, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 14612, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_llp_regs), + .regs = imx477_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_HFLIP", + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_h_flip_regs), + .regs = imx477_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_VFLIP", + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_v_flip_regs), + .regs = imx477_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + .data.v4l2_menu_items.menu = imx477_test_patterns, + .data.v4l2_menu_items.size = ARRAY_SIZE(imx477_test_patterns), + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_test_pattern_regs), + .regs = imx477_test_pattern_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_ANALOGUE_GAIN", + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_ana_gain_global_regs), + .regs = imx477_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .name = "V4L2_CID_EXPOSURE", + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 5500, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_exposure_regs), + .regs = imx477_exposure_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_WDR_MODE, + .name = "V4L2_CID_WDR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_wdr_switch_regs), + .regs = imx477_wdr_switch_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS1", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0X5500, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_shs1_regs), + .regs = imx477_shs1_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS2, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS2", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0X500, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_shs2_regs), + .regs = imx477_shs2_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS3, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS3", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0X1000, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_shs3_regs), + .regs = imx477_shs3_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_SENSOR_MODE, + .name = "CRL_CID_SENSOR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = IMX477_CAPTURE_MODE_MAX - 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_RHS1, + .name = "CRL_CID_EXPOSURE_RHS1", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0x1000, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_rhs1_regs), + .regs = imx477_rhs1_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_RHS2, + .name = "CRL_CID_EXPOSURE_RHS2", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0x1500, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_rhs2_regs), + .regs = imx477_rhs2_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_L, + .name = "CRL_CID_ANALOG_GAIN_L", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_ana_gain_1st_regs), + .regs = imx477_ana_gain_1st_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_S, + .name = "CRL_CID_ANALOG_GAIN_S", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_ana_gain_2nd_regs), + .regs = imx477_ana_gain_2nd_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_VS, + .name = "CRL_CID_ANALOG_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_ana_gain_3rd_regs), + .regs = imx477_ana_gain_3rd_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_L, + .name = "CRL_CID_DIGITAL_GAIN_L", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_dig_gain_1st_regs), + .regs = imx477_dig_gain_1st_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_S, + .name = "CRL_CID_DIGITAL_GAIN_S", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_dig_gain_2nd_regs), + .regs = imx477_dig_gain_2nd_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_VS, + .name = "CRL_CID_DIGITAL_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(imx477_dig_gain_3rd_regs), + .regs = imx477_dig_gain_3rd_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_register_write_rep imx477_streamon_regs[] = { + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x00, CRL_REG_LEN_DELAY, 20, 0x00}, /* Delay 20ms */ +}; + +static struct crl_register_write_rep imx477_streamoff_regs[] = { + {0x0100, CRL_REG_LEN_08BIT, 0x00}, + {0x00, CRL_REG_LEN_DELAY, 20, 0x00}, /* Delay 20ms */ +}; + +static struct crl_register_write_rep imx477_fmt_raw10[] = { + {0x0112, CRL_REG_LEN_08BIT, 0x0a}, /* FMT RAW10 */ + {0x0113, CRL_REG_LEN_08BIT, 0x0a}, + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, + {0x00FC, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FD, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FE, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FF, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ +}; + +static struct crl_register_write_rep imx477_fmt_raw12[] = { + {0x0112, CRL_REG_LEN_08BIT, 0x0c}, /* FMT RAW12 */ + {0x0113, CRL_REG_LEN_08BIT, 0x0c}, + {0x3F0D, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_csi_data_fmt imx477_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx477_fmt_raw10), + .regs = imx477_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx477_fmt_raw10), + .regs = imx477_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx477_fmt_raw10), + .regs = imx477_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + .regs_items = ARRAY_SIZE(imx477_fmt_raw10), + .regs = imx477_fmt_raw10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + .regs_items = ARRAY_SIZE(imx477_fmt_raw12), + .regs = imx477_fmt_raw12, + }, +}; + +static struct crl_subdev_rect_rep imx477_4056_3040_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + } +}; + +static struct crl_subdev_rect_rep imx477_4056_2288_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 2288, + } +}; + +static struct crl_subdev_rect_rep imx477_2832_1632_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2832, + .out_rect.height = 1632, + } +}; + +static struct crl_subdev_rect_rep imx477_2028_1128_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2028, + .out_rect.height = 1128, + } +}; + +static struct crl_subdev_rect_rep imx477_1296_768_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1296, + .out_rect.height = 768, + } +}; + +static struct crl_subdev_rect_rep imx477_656_512_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4056, + .out_rect.height = 3040, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4056, + .in_rect.height = 3040, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 656, + .out_rect.height = 512, + } +}; + +static struct crl_register_write_rep imx477_pll_1200mbps[] = { + /* MIPI Settings */ + {0x0114, CRL_REG_LEN_08BIT, 0x01}, /* 2-lane Mode */ + + /* Clock Setting */ + {0x0301, CRL_REG_LEN_08BIT, 0x05}, /* The Pixel Clock Divider for IVTS */ + {0x0303, CRL_REG_LEN_08BIT, 0x02}, /* The System Clock Divider for IVTS */ + {0x0305, CRL_REG_LEN_08BIT, 0x03}, /* The pre-PLL Clock Divider for IVTS */ + {0x0306, CRL_REG_LEN_08BIT, 0x01}, /* The PLL multiplier for IVTS [10:8] */ + {0x0307, CRL_REG_LEN_08BIT, 0x48}, /* The PLL multiplier for IVTS [7:0] */ + {0x0309, CRL_REG_LEN_08BIT, 0x0A}, /* The Pixel Clock Divider for IOPS */ + {0x030B, CRL_REG_LEN_08BIT, 0x01}, /* The System Clock Divider for IOPS */ + {0x030D, CRL_REG_LEN_08BIT, 0x02}, /* The pre-PLL Clock Divider for IOPS */ + {0x030E, CRL_REG_LEN_08BIT, 0x00}, /* The PLL multiplier for IOPS [10:8] */ + {0x030F, CRL_REG_LEN_08BIT, 0x7D}, /* The PLL multiplier for IOPS [7:0] */ + {0x0310, CRL_REG_LEN_08BIT, 0x01}, /* PLL mode select: Dual Mode */ + {0x0820, CRL_REG_LEN_08BIT, 0x09}, /* Output Data Rate, Mbps [31:24] */ + {0x0821, CRL_REG_LEN_08BIT, 0x60}, /* Output Data Rate, Mbps [23:16] */ + {0x0822, CRL_REG_LEN_08BIT, 0x00}, /* Output Data Rate, Mbps [15:8] */ + {0x0823, CRL_REG_LEN_08BIT, 0x00}, /* Output Data Rate, Mbps [7:0] */ + + /* Global Timing Setting */ + {0x080A, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (Tclk) [9:8] */ + {0x080B, CRL_REG_LEN_08BIT, 0x87}, /* MIPI Global Timing (Tclk) [7:0] */ + {0x080C, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (ths_prepare) */ + {0x080D, CRL_REG_LEN_08BIT, 0x4F}, /* MIPI Global Timing (ths_prepare) */ + {0x080E, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (ths_zero_min) */ + {0x080F, CRL_REG_LEN_08BIT, 0x87}, /* MIPI Global Timing (ths_zero_min) */ + {0x0810, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (ths_trail) */ + {0x0811, CRL_REG_LEN_08BIT, 0x5F}, /* MIPI Global Timing (ths_trail) */ + {0x0812, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (Tclk_trail_min)*/ + {0x0813, CRL_REG_LEN_08BIT, 0x5F}, /* MIPI Global Timing (Tclk_trail_min)*/ + {0x0814, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (Tclk_prepare) */ + {0x0815, CRL_REG_LEN_08BIT, 0x4F}, /* MIPI Global Timing (Tclk_prepare) */ + {0x0816, CRL_REG_LEN_08BIT, 0x01}, /* MIPI Global Timing (Tclk_zero) */ + {0x0817, CRL_REG_LEN_08BIT, 0x3F}, /* MIPI Global Timing (Tclk_zero) */ + {0x0818, CRL_REG_LEN_08BIT, 0x00}, /* MIPI Global Timing (Tlpx) */ + {0x0819, CRL_REG_LEN_08BIT, 0x3F}, /* MIPI Global Timing (Tlpx) */ + {0xE04C, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xE04D, CRL_REG_LEN_08BIT, 0x87}, /* Undocumented */ + {0xE04E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xE04F, CRL_REG_LEN_08BIT, 0x1F}, /* Undocumented */ + + /* Output Data Select Setting */ + {0x3E20, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x3E37, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* PowerSave Setting */ + {0x3F50, CRL_REG_LEN_08BIT, 0x00}, /* Power save: Disable */ + {0x3F56, CRL_REG_LEN_08BIT, 0x01}, + {0x3F57, CRL_REG_LEN_08BIT, 0x4F}, +}; + +static struct crl_pll_configuration imx477_pll_configurations[] = { + { + .input_clk = 19200000, + .op_sys_clk = 600000000, /* 1200mbps / 2 */ + .bitsperpixel = 10, + .pixel_rate_csi = 240000000, + /* pixel_rate = (MIPICLK*2 * CSILANES)/10 */ + .pixel_rate_pa = 240000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx477_pll_1200mbps), + .pll_regs = imx477_pll_1200mbps, + }, + { + .input_clk = 19200000, + .op_sys_clk = 600000000, /* 1200mbps / 2 */ + .bitsperpixel = 12, + .pixel_rate_csi = 240000000, + /* pixel_rate = (MIPICLK*2 * CSILANES)/10 */ + .pixel_rate_pa = 240000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(imx477_pll_1200mbps), + .pll_regs = imx477_pll_1200mbps, + }, +}; + +static struct crl_sensor_subdev_config imx477_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "imx477 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "imx477 pixel array", + } +}; + +static struct crl_sensor_limits imx477_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 4056, + .y_addr_max = 3040, + .min_frame_length_lines = 320, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 380, + .max_line_length_pixels = 32752, +}; + +static struct crl_sensor_detect_config imx477_sensor_detect_regset[] = { + { + .reg = { 0x0016, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x0017, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + } +}; + +static struct crl_register_write_rep imx477_powerup_standby[] = { + {0x0100, CRL_REG_LEN_08BIT, 0x00}, + {0x00, CRL_REG_LEN_DELAY, 20, 0x00}, /* Delay 20ms */ +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity imx477_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 19200000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 1, + }, +}; + +static struct crl_arithmetic_ops imx477_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops imx477_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc imx477_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_width_ops), + .ops = imx477_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_height_ops), + .ops = imx477_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_width_ops), + .ops = imx477_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_height_ops), + .ops = imx477_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 1, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_width_ops), + .ops = imx477_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(imx477_frame_desc_height_ops), + .ops = imx477_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 2, + .csi2_data_type.entity_val = 0x12, + }, +}; + +#endif /* __CRLMODULE_IMX477_COMMON_REGS_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx477_master_configuration.h b/drivers/media/i2c/crlmodule/crl_imx477_master_configuration.h new file mode 100644 index 000000000000..10be93b07215 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx477_master_configuration.h @@ -0,0 +1,1375 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Alexei Zavjalov + * + */ + +#ifndef __CRLMODULE_IMX477_MASTER_CONFIGURATION_H_ +#define __CRLMODULE_IMX477_MASTER_CONFIGURATION_H_ + +#include "crl_imx477_common_regs.h" + +static struct crl_register_write_rep imx477_onetime_init_regset_master[] = { + {0x0103, CRL_REG_LEN_08BIT, 0x01}, /* Software reset */ + + {0x3010, CRL_REG_LEN_08BIT, 0x01}, /* SLAVE_ADD_EN_2ND */ + {0x3011, CRL_REG_LEN_08BIT, 0x01}, /* SLAVE_ADD_ACKEN_2ND */ + + {0x3F0B, CRL_REG_LEN_08BIT, 0x01}, /* Multi camera mode: on */ + + {0x3041, CRL_REG_LEN_08BIT, 0x01}, /* Mode: Master */ + {0x3040, CRL_REG_LEN_08BIT, 0x01}, /* XVS pin: out */ + {0x4B81, CRL_REG_LEN_08BIT, 0x01}, /* Mode: Master */ + + {0x3042, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in lines [15:8] */ + {0x3043, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in lines [7:0] */ + {0x3044, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in clocks [15:8] */ + {0x3045, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in clocks [7:0] */ + {0x3045, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC thin down setting */ + + /* External Clock Setting */ + {0x0136, CRL_REG_LEN_08BIT, 0x13}, /* External clock freq (dec) [15:8] */ + {0x0137, CRL_REG_LEN_08BIT, 0x33}, /* External clock freq (dec) [7:0] */ + + /* Global Setting */ + {0x0808, CRL_REG_LEN_08BIT, 0x02}, /* MIPI Global Timing: Register Control */ + {0xE07A, CRL_REG_LEN_08BIT, 0x01}, + {0xE000, CRL_REG_LEN_08BIT, 0x00}, /* RUN/STOP of CSI2 during Frame Blanking: HS */ + {0x4AE9, CRL_REG_LEN_08BIT, 0x18}, + {0x4AEA, CRL_REG_LEN_08BIT, 0x08}, + {0xF61C, CRL_REG_LEN_08BIT, 0x04}, + {0xF61E, CRL_REG_LEN_08BIT, 0x04}, + {0x4AE9, CRL_REG_LEN_08BIT, 0x21}, + {0x4AEA, CRL_REG_LEN_08BIT, 0x80}, + {0x38A8, CRL_REG_LEN_08BIT, 0x1F}, + {0x38A9, CRL_REG_LEN_08BIT, 0xFF}, + {0x38AA, CRL_REG_LEN_08BIT, 0x1F}, + {0x38AB, CRL_REG_LEN_08BIT, 0xFF}, + {0x420B, CRL_REG_LEN_08BIT, 0x01}, + {0x55D4, CRL_REG_LEN_08BIT, 0x00}, + {0x55D5, CRL_REG_LEN_08BIT, 0x00}, + {0x55D6, CRL_REG_LEN_08BIT, 0x07}, + {0x55D7, CRL_REG_LEN_08BIT, 0xFF}, + {0x55E8, CRL_REG_LEN_08BIT, 0x07}, + {0x55E9, CRL_REG_LEN_08BIT, 0xFF}, + {0x55EA, CRL_REG_LEN_08BIT, 0x00}, + {0x55EB, CRL_REG_LEN_08BIT, 0x00}, + {0x574C, CRL_REG_LEN_08BIT, 0x07}, + {0x574D, CRL_REG_LEN_08BIT, 0xFF}, + {0x574E, CRL_REG_LEN_08BIT, 0x00}, + {0x574F, CRL_REG_LEN_08BIT, 0x00}, + {0x5754, CRL_REG_LEN_08BIT, 0x00}, + {0x5755, CRL_REG_LEN_08BIT, 0x00}, + {0x5756, CRL_REG_LEN_08BIT, 0x07}, + {0x5757, CRL_REG_LEN_08BIT, 0xFF}, + {0x5973, CRL_REG_LEN_08BIT, 0x04}, + {0x5974, CRL_REG_LEN_08BIT, 0x01}, + {0x5D13, CRL_REG_LEN_08BIT, 0xC3}, + {0x5D14, CRL_REG_LEN_08BIT, 0x58}, + {0x5D15, CRL_REG_LEN_08BIT, 0xA3}, + {0x5D16, CRL_REG_LEN_08BIT, 0x1D}, + {0x5D17, CRL_REG_LEN_08BIT, 0x65}, + {0x5D18, CRL_REG_LEN_08BIT, 0x8C}, + {0x5D1A, CRL_REG_LEN_08BIT, 0x06}, + {0x5D1B, CRL_REG_LEN_08BIT, 0xA9}, + {0x5D1C, CRL_REG_LEN_08BIT, 0x45}, + {0x5D1D, CRL_REG_LEN_08BIT, 0x3A}, + {0x5D1E, CRL_REG_LEN_08BIT, 0xAB}, + {0x5D1F, CRL_REG_LEN_08BIT, 0x15}, + {0x5D21, CRL_REG_LEN_08BIT, 0x0E}, + {0x5D22, CRL_REG_LEN_08BIT, 0x52}, + {0x5D23, CRL_REG_LEN_08BIT, 0xAA}, + {0x5D24, CRL_REG_LEN_08BIT, 0x7D}, + {0x5D25, CRL_REG_LEN_08BIT, 0x57}, + {0x5D26, CRL_REG_LEN_08BIT, 0xA8}, + {0x5D37, CRL_REG_LEN_08BIT, 0x5A}, + {0x5D38, CRL_REG_LEN_08BIT, 0x5A}, + {0x5D77, CRL_REG_LEN_08BIT, 0x7F}, + {0x7B7C, CRL_REG_LEN_08BIT, 0x00}, + {0x7B7D, CRL_REG_LEN_08BIT, 0x00}, + {0x8D1F, CRL_REG_LEN_08BIT, 0x00}, + {0x8D27, CRL_REG_LEN_08BIT, 0x00}, + {0x9004, CRL_REG_LEN_08BIT, 0x03}, + {0x9200, CRL_REG_LEN_08BIT, 0x50}, + {0x9201, CRL_REG_LEN_08BIT, 0x6C}, + {0x9202, CRL_REG_LEN_08BIT, 0x71}, + {0x9203, CRL_REG_LEN_08BIT, 0x00}, + {0x9204, CRL_REG_LEN_08BIT, 0x71}, + {0x9205, CRL_REG_LEN_08BIT, 0x01}, + {0x9371, CRL_REG_LEN_08BIT, 0x6A}, + {0x9373, CRL_REG_LEN_08BIT, 0x6A}, + {0x9375, CRL_REG_LEN_08BIT, 0x64}, + {0x990C, CRL_REG_LEN_08BIT, 0x00}, + {0x990D, CRL_REG_LEN_08BIT, 0x08}, + {0x9956, CRL_REG_LEN_08BIT, 0x8C}, + {0x9957, CRL_REG_LEN_08BIT, 0x64}, + {0x9958, CRL_REG_LEN_08BIT, 0x50}, + {0x9A48, CRL_REG_LEN_08BIT, 0x06}, + {0x9A49, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4A, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4B, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4C, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4D, CRL_REG_LEN_08BIT, 0x06}, + {0xA001, CRL_REG_LEN_08BIT, 0x0A}, + {0xA003, CRL_REG_LEN_08BIT, 0x0A}, + {0xA005, CRL_REG_LEN_08BIT, 0x0A}, + {0xA006, CRL_REG_LEN_08BIT, 0x01}, + {0xA007, CRL_REG_LEN_08BIT, 0xC0}, + {0xA009, CRL_REG_LEN_08BIT, 0xC0}, + + /* Image Tuning */ + {0x3D8A, CRL_REG_LEN_08BIT, 0x01}, + {0x7B3B, CRL_REG_LEN_08BIT, 0x01}, + {0x7B4C, CRL_REG_LEN_08BIT, 0x00}, + {0x9905, CRL_REG_LEN_08BIT, 0x00}, + {0x9907, CRL_REG_LEN_08BIT, 0x00}, + {0x9909, CRL_REG_LEN_08BIT, 0x00}, + {0x990B, CRL_REG_LEN_08BIT, 0x00}, + {0x9944, CRL_REG_LEN_08BIT, 0x3C}, + {0x9947, CRL_REG_LEN_08BIT, 0x3C}, + {0x994A, CRL_REG_LEN_08BIT, 0x8C}, + {0x994B, CRL_REG_LEN_08BIT, 0x50}, + {0x994C, CRL_REG_LEN_08BIT, 0x1B}, + {0x994D, CRL_REG_LEN_08BIT, 0x8C}, + {0x994E, CRL_REG_LEN_08BIT, 0x50}, + {0x994F, CRL_REG_LEN_08BIT, 0x1B}, + {0x9950, CRL_REG_LEN_08BIT, 0x8C}, + {0x9951, CRL_REG_LEN_08BIT, 0x1B}, + {0x9952, CRL_REG_LEN_08BIT, 0x0A}, + {0x9953, CRL_REG_LEN_08BIT, 0x8C}, + {0x9954, CRL_REG_LEN_08BIT, 0x1B}, + {0x9955, CRL_REG_LEN_08BIT, 0x0A}, + {0x9A13, CRL_REG_LEN_08BIT, 0x04}, + {0x9A14, CRL_REG_LEN_08BIT, 0x04}, + {0x9A19, CRL_REG_LEN_08BIT, 0x00}, + {0x9A1C, CRL_REG_LEN_08BIT, 0x04}, + {0x9A1D, CRL_REG_LEN_08BIT, 0x04}, + {0x9A26, CRL_REG_LEN_08BIT, 0x05}, + {0x9A27, CRL_REG_LEN_08BIT, 0x05}, + {0x9A2C, CRL_REG_LEN_08BIT, 0x01}, + {0x9A2D, CRL_REG_LEN_08BIT, 0x03}, + {0x9A2F, CRL_REG_LEN_08BIT, 0x05}, + {0x9A30, CRL_REG_LEN_08BIT, 0x05}, + {0x9A41, CRL_REG_LEN_08BIT, 0x00}, + {0x9A46, CRL_REG_LEN_08BIT, 0x00}, + {0x9A47, CRL_REG_LEN_08BIT, 0x00}, + {0x9C17, CRL_REG_LEN_08BIT, 0x35}, + {0x9C1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9C29, CRL_REG_LEN_08BIT, 0x50}, + {0x9C3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9C41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9C47, CRL_REG_LEN_08BIT, 0x2D}, + {0x9C4D, CRL_REG_LEN_08BIT, 0x40}, + {0x9C6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9C71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9C73, CRL_REG_LEN_08BIT, 0x32}, + {0x9C75, CRL_REG_LEN_08BIT, 0x04}, + {0x9C7D, CRL_REG_LEN_08BIT, 0x2D}, + {0x9C83, CRL_REG_LEN_08BIT, 0x40}, + {0x9C94, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C95, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C96, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C97, CRL_REG_LEN_08BIT, 0x00}, + {0x9C98, CRL_REG_LEN_08BIT, 0x00}, + {0x9C99, CRL_REG_LEN_08BIT, 0x00}, + {0x9C9A, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C9B, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C9C, CRL_REG_LEN_08BIT, 0x3F}, + {0x9CA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9CAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9CAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9CBD, CRL_REG_LEN_08BIT, 0x50}, + {0x9CBF, CRL_REG_LEN_08BIT, 0x50}, + {0x9CC1, CRL_REG_LEN_08BIT, 0x50}, + {0x9CC3, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC5, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC7, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9CCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9CCD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9D17, CRL_REG_LEN_08BIT, 0x35}, + {0x9D1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9D29, CRL_REG_LEN_08BIT, 0x50}, + {0x9D3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9D41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9D47, CRL_REG_LEN_08BIT, 0x42}, + {0x9D4D, CRL_REG_LEN_08BIT, 0x5A}, + {0x9D6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9D71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9D73, CRL_REG_LEN_08BIT, 0x32}, + {0x9D75, CRL_REG_LEN_08BIT, 0x04}, + {0x9D7D, CRL_REG_LEN_08BIT, 0x42}, + {0x9D83, CRL_REG_LEN_08BIT, 0x5A}, + {0x9D94, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D95, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D96, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D97, CRL_REG_LEN_08BIT, 0x00}, + {0x9D98, CRL_REG_LEN_08BIT, 0x00}, + {0x9D99, CRL_REG_LEN_08BIT, 0x00}, + {0x9D9A, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9B, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9C, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9D, CRL_REG_LEN_08BIT, 0x1F}, + {0x9D9E, CRL_REG_LEN_08BIT, 0x1F}, + {0x9D9F, CRL_REG_LEN_08BIT, 0x1F}, + {0x9DA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9DAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9DAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9DC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9DCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9DCD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9E17, CRL_REG_LEN_08BIT, 0x35}, + {0x9E1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9E29, CRL_REG_LEN_08BIT, 0x50}, + {0x9E3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9E41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9E47, CRL_REG_LEN_08BIT, 0x2D}, + {0x9E4D, CRL_REG_LEN_08BIT, 0x40}, + {0x9E6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9E71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9E73, CRL_REG_LEN_08BIT, 0x32}, + {0x9E75, CRL_REG_LEN_08BIT, 0x04}, + {0x9E94, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E95, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E96, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E97, CRL_REG_LEN_08BIT, 0x00}, + {0x9E98, CRL_REG_LEN_08BIT, 0x00}, + {0x9E99, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA6, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA7, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA8, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9EAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9EAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9EC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9ECB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9ECD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9F17, CRL_REG_LEN_08BIT, 0x35}, + {0x9F1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9F29, CRL_REG_LEN_08BIT, 0x50}, + {0x9F3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9F47, CRL_REG_LEN_08BIT, 0x42}, + {0x9F4D, CRL_REG_LEN_08BIT, 0x5A}, + {0x9F6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9F71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9F73, CRL_REG_LEN_08BIT, 0x32}, + {0x9F75, CRL_REG_LEN_08BIT, 0x04}, + {0x9F94, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F95, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F96, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F97, CRL_REG_LEN_08BIT, 0x00}, + {0x9F98, CRL_REG_LEN_08BIT, 0x00}, + {0x9F99, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9A, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9C, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9D, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9E, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9F, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9FAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9FAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9FC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9FCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9FCD, CRL_REG_LEN_08BIT, 0x0A}, + {0xA14B, CRL_REG_LEN_08BIT, 0xFF}, + {0xA151, CRL_REG_LEN_08BIT, 0x0C}, + {0xA153, CRL_REG_LEN_08BIT, 0x50}, + {0xA155, CRL_REG_LEN_08BIT, 0x02}, + {0xA157, CRL_REG_LEN_08BIT, 0x00}, + {0xA1AD, CRL_REG_LEN_08BIT, 0xFF}, + {0xA1B3, CRL_REG_LEN_08BIT, 0x0C}, + {0xA1B5, CRL_REG_LEN_08BIT, 0x50}, + {0xA1B9, CRL_REG_LEN_08BIT, 0x00}, + {0xA24B, CRL_REG_LEN_08BIT, 0xFF}, + {0xA257, CRL_REG_LEN_08BIT, 0x00}, + {0xA2AD, CRL_REG_LEN_08BIT, 0xFF}, + {0xA2B9, CRL_REG_LEN_08BIT, 0x00}, + {0xB21F, CRL_REG_LEN_08BIT, 0x04}, + {0xB35C, CRL_REG_LEN_08BIT, 0x00}, + {0xB35E, CRL_REG_LEN_08BIT, 0x08}, +}; + +static struct crl_register_write_rep imx477_4056_3040_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x0B}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xE0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x0B}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xE0}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_4056_3040_19MHZ_DOL_2f_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x01}, /* DOL-HDR Enable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x01}, /* DOL Mode: 2 frames in DOL-HDR */ + /* virtual channel ID of visible line and embedded line of DOL 2nd frame */ + {0x3E10, CRL_REG_LEN_08BIT, 0x01}, + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x0B}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xE0}, /* Height after cropping [7:0] */ + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x0B}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xE0}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_4056_3040_19MHZ_DOL_3f_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x01}, /* DOL-HDR Enable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x02}, /* DOL Mode: 2 frames in DOL-HDR */ + /* virtual channel ID of visible line and embedded line of DOL 2nd frame */ + {0x3E10, CRL_REG_LEN_08BIT, 0x01}, + /* virtual channel ID of visible line and embedded line of DOL 3rd frame */ + {0x3E11, CRL_REG_LEN_08BIT, 0x02}, + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x0B}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xE0}, /* Height after cropping [7:0] */ + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x0B}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xE0}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_4056_2288_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + /* (0,376) to (4055, 2664) */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x01}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x78}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0A}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0x68}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x08}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xF0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x08}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xF0}, /* Y output size [7:0] */ +}; + + +static struct crl_register_write_rep imx477_2832_1632_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + /* scale factor 16/22, 3894x2244 to 2832x1632 */ + {0x0401, CRL_REG_LEN_08BIT, 0x02}, /* Scaling mode: Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x16}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x52}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x01}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x8E}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0x36}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x08}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xC4}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0B}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0x10}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x06}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0x60}, /* Y output size [7:0] */ +}; + + +static struct crl_register_write_rep imx477_2028_1128_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x01}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x88}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0A}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0x58}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x01}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x22}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x07}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xEC}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x04}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0x68}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x07}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xEC}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x04}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0x68}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_1296_768_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + /* scale factor 16/50, 4050x2400 to 1296x768 */ + {0x0401, CRL_REG_LEN_08BIT, 0x02}, /* Scaling mode: Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x32}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x04}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x01}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x40}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD2}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x09}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0x60}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x05}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0x10}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x03}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0x00}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_656_512_19MHZ_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + /* scale factor 16/95, 3895x3040 to 656x512 */ + {0x0401, CRL_REG_LEN_08BIT, 0x02}, /* Scaling mode: Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x5F}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x50}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0x37}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x0B}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xE0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x02}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0x90}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x02}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0x00}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_4056_2288_19MHZ_DOL_2f_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + /* (0,376) to (4055, 2664) */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x01}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x78}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0A}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0x68}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x01}, /* DOL-HDR enabled */ + {0x00E4, CRL_REG_LEN_08BIT, 0x01}, /* DOL Mode: DOL2 */ + {0x00FC, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FD, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x3E10, CRL_REG_LEN_08BIT, 0x01}, /* VC ID of DOL 2nd frame */ + + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x08}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xF0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x08}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xF0}, /* Y output size [7:0] */ +}; + +static struct crl_register_write_rep imx477_4056_2288_19MHZ_DOL_3f_master[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + /* (0,376) to (4055, 2664) */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x01}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x78}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0A}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0x68}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x01}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x02}, /* DOL Mode: DOL3 */ + {0x00FC, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FD, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FE, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FF, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x3E10, CRL_REG_LEN_08BIT, 0x01}, /* VC ID of DOL 2nd frame */ + {0x3E11, CRL_REG_LEN_08BIT, 0x02}, /* VC ID of DOL 3rd frame */ + + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x08}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xF0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x08}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xF0}, /* Y output size [7:0] */ +}; + +static struct crl_mode_rep imx477_modes_master[] = { + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_3040_rects), + .sd_rects = imx477_4056_3040_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 3040, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_3040_19MHZ_master), + .mode_regs = imx477_4056_3040_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_3040_rects), + .sd_rects = imx477_4056_3040_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 3040, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_3040_19MHZ_DOL_2f_master), + .mode_regs = imx477_4056_3040_19MHZ_DOL_2f_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_3040_rects), + .sd_rects = imx477_4056_3040_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 3040, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_3040_19MHZ_DOL_3f_master), + .mode_regs = imx477_4056_3040_19MHZ_DOL_3f_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_2288_rects), + .sd_rects = imx477_4056_2288_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 2288, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_2288_19MHZ_master), + .mode_regs = imx477_4056_2288_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_2832_1632_rects), + .sd_rects = imx477_2832_1632_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2832, + .height = 1632, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_2832_1632_19MHZ_master), + .mode_regs = imx477_2832_1632_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_2028_1128_rects), + .sd_rects = imx477_2028_1128_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2028, + .height = 1128, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_2028_1128_19MHZ_master), + .mode_regs = imx477_2028_1128_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_1296_768_rects), + .sd_rects = imx477_1296_768_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1296, + .height = 768, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_1296_768_19MHZ_master), + .mode_regs = imx477_1296_768_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_656_512_rects), + .sd_rects = imx477_656_512_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 656, + .height = 512, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_656_512_19MHZ_master), + .mode_regs = imx477_656_512_19MHZ_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_2288_rects), + .sd_rects = imx477_4056_2288_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 2288, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_2288_19MHZ_DOL_2f_master), + .mode_regs = imx477_4056_2288_19MHZ_DOL_2f_master, + }, + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_2288_rects), + .sd_rects = imx477_4056_2288_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 2288, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_2288_19MHZ_DOL_3f_master), + .mode_regs = imx477_4056_2288_19MHZ_DOL_3f_master, + }, +}; + +static struct crl_flip_data imx477_flip_configurations_master[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + } +}; + +struct crl_sensor_configuration imx477_master_crl_configuration = { + + .power_items = ARRAY_SIZE(imx477_power_items), + .power_entities = imx477_power_items, + + .onetime_init_regs_items = ARRAY_SIZE(imx477_onetime_init_regset_master), + .onetime_init_regs = imx477_onetime_init_regset_master, + + .powerup_regs_items = ARRAY_SIZE(imx477_powerup_standby), + .powerup_regs = imx477_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx477_sensor_detect_regset), + .id_regs = imx477_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx477_sensor_subdevs), + .subdevs = imx477_sensor_subdevs, + + .sensor_limits = &imx477_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx477_pll_configurations), + .pll_configs = imx477_pll_configurations, + + .modes_items = ARRAY_SIZE(imx477_modes_master), + .modes = imx477_modes_master, + + .streamon_regs_items = ARRAY_SIZE(imx477_streamon_regs), + .streamon_regs = imx477_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx477_streamoff_regs), + .streamoff_regs = imx477_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx477_v4l2_ctrls), + .v4l2_ctrl_bank = imx477_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx477_crl_csi_data_fmt), + .csi_fmts = imx477_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx477_flip_configurations_master), + .flip_data = imx477_flip_configurations_master, + + .frame_desc_entries = ARRAY_SIZE(imx477_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx477_frame_desc, +}; + +#endif /* __CRLMODULE_IMX477_MASTER_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_imx477_slave_configuration.h b/drivers/media/i2c/crlmodule/crl_imx477_slave_configuration.h new file mode 100644 index 000000000000..b8dc15c0f1f8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_imx477_slave_configuration.h @@ -0,0 +1,509 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Alexei Zavjalov + * + */ + +#ifndef __CRLMODULE_IMX477_SLAVE_CONFIGURATION_H_ +#define __CRLMODULE_IMX477_SLAVE_CONFIGURATION_H_ + +#include "crl_imx477_common_regs.h" + +static struct crl_register_write_rep imx477_onetime_init_regset_slave[] = { + {0x0103, CRL_REG_LEN_08BIT, 0x01}, /* Software reset */ + + {0x3010, CRL_REG_LEN_08BIT, 0x01}, /* SLAVE_ADD_EN_2ND */ + {0x3011, CRL_REG_LEN_08BIT, 0x01}, /* SLAVE_ADD_ACKEN_2ND */ + + {0x3F0B, CRL_REG_LEN_08BIT, 0x01}, /* Multi camera mode: on */ + + {0x3041, CRL_REG_LEN_08BIT, 0x00}, /* Mode: Slave */ + {0x3040, CRL_REG_LEN_08BIT, 0x00}, /* XVS pin: in */ + {0x4B81, CRL_REG_LEN_08BIT, 0x00}, /* Mode: Slave */ + + {0x3042, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in lines [15:8] */ + {0x3043, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in lines [7:0] */ + {0x3044, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in clocks [15:8] */ + {0x3045, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC Delay in clocks [7:0] */ + {0x3045, CRL_REG_LEN_08BIT, 0x00}, /* VSYNC thin down setting */ + + /* External Clock Setting */ + {0x0136, CRL_REG_LEN_08BIT, 0x13}, /* External clock freq (dec) [15:8] */ + {0x0137, CRL_REG_LEN_08BIT, 0x33}, /* External clock freq (dec) [7:0] */ + + /* Global Setting */ + {0x0808, CRL_REG_LEN_08BIT, 0x02}, /* MIPI Global Timing: Register Control */ + {0xE07A, CRL_REG_LEN_08BIT, 0x01}, + {0xE000, CRL_REG_LEN_08BIT, 0x00}, /* RUN/STOP of CSI2 during Frame Blanking: HS */ + {0x4AE9, CRL_REG_LEN_08BIT, 0x18}, + {0x4AEA, CRL_REG_LEN_08BIT, 0x08}, + {0xF61C, CRL_REG_LEN_08BIT, 0x04}, + {0xF61E, CRL_REG_LEN_08BIT, 0x04}, + {0x4AE9, CRL_REG_LEN_08BIT, 0x21}, + {0x4AEA, CRL_REG_LEN_08BIT, 0x80}, + {0x38A8, CRL_REG_LEN_08BIT, 0x1F}, + {0x38A9, CRL_REG_LEN_08BIT, 0xFF}, + {0x38AA, CRL_REG_LEN_08BIT, 0x1F}, + {0x38AB, CRL_REG_LEN_08BIT, 0xFF}, + {0x420B, CRL_REG_LEN_08BIT, 0x01}, + {0x55D4, CRL_REG_LEN_08BIT, 0x00}, + {0x55D5, CRL_REG_LEN_08BIT, 0x00}, + {0x55D6, CRL_REG_LEN_08BIT, 0x07}, + {0x55D7, CRL_REG_LEN_08BIT, 0xFF}, + {0x55E8, CRL_REG_LEN_08BIT, 0x07}, + {0x55E9, CRL_REG_LEN_08BIT, 0xFF}, + {0x55EA, CRL_REG_LEN_08BIT, 0x00}, + {0x55EB, CRL_REG_LEN_08BIT, 0x00}, + {0x574C, CRL_REG_LEN_08BIT, 0x07}, + {0x574D, CRL_REG_LEN_08BIT, 0xFF}, + {0x574E, CRL_REG_LEN_08BIT, 0x00}, + {0x574F, CRL_REG_LEN_08BIT, 0x00}, + {0x5754, CRL_REG_LEN_08BIT, 0x00}, + {0x5755, CRL_REG_LEN_08BIT, 0x00}, + {0x5756, CRL_REG_LEN_08BIT, 0x07}, + {0x5757, CRL_REG_LEN_08BIT, 0xFF}, + {0x5973, CRL_REG_LEN_08BIT, 0x04}, + {0x5974, CRL_REG_LEN_08BIT, 0x01}, + {0x5D13, CRL_REG_LEN_08BIT, 0xC3}, + {0x5D14, CRL_REG_LEN_08BIT, 0x58}, + {0x5D15, CRL_REG_LEN_08BIT, 0xA3}, + {0x5D16, CRL_REG_LEN_08BIT, 0x1D}, + {0x5D17, CRL_REG_LEN_08BIT, 0x65}, + {0x5D18, CRL_REG_LEN_08BIT, 0x8C}, + {0x5D1A, CRL_REG_LEN_08BIT, 0x06}, + {0x5D1B, CRL_REG_LEN_08BIT, 0xA9}, + {0x5D1C, CRL_REG_LEN_08BIT, 0x45}, + {0x5D1D, CRL_REG_LEN_08BIT, 0x3A}, + {0x5D1E, CRL_REG_LEN_08BIT, 0xAB}, + {0x5D1F, CRL_REG_LEN_08BIT, 0x15}, + {0x5D21, CRL_REG_LEN_08BIT, 0x0E}, + {0x5D22, CRL_REG_LEN_08BIT, 0x52}, + {0x5D23, CRL_REG_LEN_08BIT, 0xAA}, + {0x5D24, CRL_REG_LEN_08BIT, 0x7D}, + {0x5D25, CRL_REG_LEN_08BIT, 0x57}, + {0x5D26, CRL_REG_LEN_08BIT, 0xA8}, + {0x5D37, CRL_REG_LEN_08BIT, 0x5A}, + {0x5D38, CRL_REG_LEN_08BIT, 0x5A}, + {0x5D77, CRL_REG_LEN_08BIT, 0x7F}, + {0x7B7C, CRL_REG_LEN_08BIT, 0x00}, + {0x7B7D, CRL_REG_LEN_08BIT, 0x00}, + {0x8D1F, CRL_REG_LEN_08BIT, 0x00}, + {0x8D27, CRL_REG_LEN_08BIT, 0x00}, + {0x9004, CRL_REG_LEN_08BIT, 0x03}, + {0x9200, CRL_REG_LEN_08BIT, 0x50}, + {0x9201, CRL_REG_LEN_08BIT, 0x6C}, + {0x9202, CRL_REG_LEN_08BIT, 0x71}, + {0x9203, CRL_REG_LEN_08BIT, 0x00}, + {0x9204, CRL_REG_LEN_08BIT, 0x71}, + {0x9205, CRL_REG_LEN_08BIT, 0x01}, + {0x9371, CRL_REG_LEN_08BIT, 0x6A}, + {0x9373, CRL_REG_LEN_08BIT, 0x6A}, + {0x9375, CRL_REG_LEN_08BIT, 0x64}, + {0x990C, CRL_REG_LEN_08BIT, 0x00}, + {0x990D, CRL_REG_LEN_08BIT, 0x08}, + {0x9956, CRL_REG_LEN_08BIT, 0x8C}, + {0x9957, CRL_REG_LEN_08BIT, 0x64}, + {0x9958, CRL_REG_LEN_08BIT, 0x50}, + {0x9A48, CRL_REG_LEN_08BIT, 0x06}, + {0x9A49, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4A, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4B, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4C, CRL_REG_LEN_08BIT, 0x06}, + {0x9A4D, CRL_REG_LEN_08BIT, 0x06}, + {0xA001, CRL_REG_LEN_08BIT, 0x0A}, + {0xA003, CRL_REG_LEN_08BIT, 0x0A}, + {0xA005, CRL_REG_LEN_08BIT, 0x0A}, + {0xA006, CRL_REG_LEN_08BIT, 0x01}, + {0xA007, CRL_REG_LEN_08BIT, 0xC0}, + {0xA009, CRL_REG_LEN_08BIT, 0xC0}, + + /* Image Tuning */ + {0x3D8A, CRL_REG_LEN_08BIT, 0x01}, + {0x7B3B, CRL_REG_LEN_08BIT, 0x01}, + {0x7B4C, CRL_REG_LEN_08BIT, 0x00}, + {0x9905, CRL_REG_LEN_08BIT, 0x00}, + {0x9907, CRL_REG_LEN_08BIT, 0x00}, + {0x9909, CRL_REG_LEN_08BIT, 0x00}, + {0x990B, CRL_REG_LEN_08BIT, 0x00}, + {0x9944, CRL_REG_LEN_08BIT, 0x3C}, + {0x9947, CRL_REG_LEN_08BIT, 0x3C}, + {0x994A, CRL_REG_LEN_08BIT, 0x8C}, + {0x994B, CRL_REG_LEN_08BIT, 0x50}, + {0x994C, CRL_REG_LEN_08BIT, 0x1B}, + {0x994D, CRL_REG_LEN_08BIT, 0x8C}, + {0x994E, CRL_REG_LEN_08BIT, 0x50}, + {0x994F, CRL_REG_LEN_08BIT, 0x1B}, + {0x9950, CRL_REG_LEN_08BIT, 0x8C}, + {0x9951, CRL_REG_LEN_08BIT, 0x1B}, + {0x9952, CRL_REG_LEN_08BIT, 0x0A}, + {0x9953, CRL_REG_LEN_08BIT, 0x8C}, + {0x9954, CRL_REG_LEN_08BIT, 0x1B}, + {0x9955, CRL_REG_LEN_08BIT, 0x0A}, + {0x9A13, CRL_REG_LEN_08BIT, 0x04}, + {0x9A14, CRL_REG_LEN_08BIT, 0x04}, + {0x9A19, CRL_REG_LEN_08BIT, 0x00}, + {0x9A1C, CRL_REG_LEN_08BIT, 0x04}, + {0x9A1D, CRL_REG_LEN_08BIT, 0x04}, + {0x9A26, CRL_REG_LEN_08BIT, 0x05}, + {0x9A27, CRL_REG_LEN_08BIT, 0x05}, + {0x9A2C, CRL_REG_LEN_08BIT, 0x01}, + {0x9A2D, CRL_REG_LEN_08BIT, 0x03}, + {0x9A2F, CRL_REG_LEN_08BIT, 0x05}, + {0x9A30, CRL_REG_LEN_08BIT, 0x05}, + {0x9A41, CRL_REG_LEN_08BIT, 0x00}, + {0x9A46, CRL_REG_LEN_08BIT, 0x00}, + {0x9A47, CRL_REG_LEN_08BIT, 0x00}, + {0x9C17, CRL_REG_LEN_08BIT, 0x35}, + {0x9C1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9C29, CRL_REG_LEN_08BIT, 0x50}, + {0x9C3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9C41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9C47, CRL_REG_LEN_08BIT, 0x2D}, + {0x9C4D, CRL_REG_LEN_08BIT, 0x40}, + {0x9C6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9C71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9C73, CRL_REG_LEN_08BIT, 0x32}, + {0x9C75, CRL_REG_LEN_08BIT, 0x04}, + {0x9C7D, CRL_REG_LEN_08BIT, 0x2D}, + {0x9C83, CRL_REG_LEN_08BIT, 0x40}, + {0x9C94, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C95, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C96, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C97, CRL_REG_LEN_08BIT, 0x00}, + {0x9C98, CRL_REG_LEN_08BIT, 0x00}, + {0x9C99, CRL_REG_LEN_08BIT, 0x00}, + {0x9C9A, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C9B, CRL_REG_LEN_08BIT, 0x3F}, + {0x9C9C, CRL_REG_LEN_08BIT, 0x3F}, + {0x9CA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9CA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9CA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9CA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9CAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9CAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9CAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9CBD, CRL_REG_LEN_08BIT, 0x50}, + {0x9CBF, CRL_REG_LEN_08BIT, 0x50}, + {0x9CC1, CRL_REG_LEN_08BIT, 0x50}, + {0x9CC3, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC5, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC7, CRL_REG_LEN_08BIT, 0x40}, + {0x9CC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9CCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9CCD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9D17, CRL_REG_LEN_08BIT, 0x35}, + {0x9D1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9D29, CRL_REG_LEN_08BIT, 0x50}, + {0x9D3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9D41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9D47, CRL_REG_LEN_08BIT, 0x42}, + {0x9D4D, CRL_REG_LEN_08BIT, 0x5A}, + {0x9D6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9D71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9D73, CRL_REG_LEN_08BIT, 0x32}, + {0x9D75, CRL_REG_LEN_08BIT, 0x04}, + {0x9D7D, CRL_REG_LEN_08BIT, 0x42}, + {0x9D83, CRL_REG_LEN_08BIT, 0x5A}, + {0x9D94, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D95, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D96, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D97, CRL_REG_LEN_08BIT, 0x00}, + {0x9D98, CRL_REG_LEN_08BIT, 0x00}, + {0x9D99, CRL_REG_LEN_08BIT, 0x00}, + {0x9D9A, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9B, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9C, CRL_REG_LEN_08BIT, 0x3F}, + {0x9D9D, CRL_REG_LEN_08BIT, 0x1F}, + {0x9D9E, CRL_REG_LEN_08BIT, 0x1F}, + {0x9D9F, CRL_REG_LEN_08BIT, 0x1F}, + {0x9DA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9DA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9DA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9DA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9DAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9DAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9DAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9DC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9DCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9DCD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9E17, CRL_REG_LEN_08BIT, 0x35}, + {0x9E1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9E29, CRL_REG_LEN_08BIT, 0x50}, + {0x9E3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9E41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9E47, CRL_REG_LEN_08BIT, 0x2D}, + {0x9E4D, CRL_REG_LEN_08BIT, 0x40}, + {0x9E6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9E71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9E73, CRL_REG_LEN_08BIT, 0x32}, + {0x9E75, CRL_REG_LEN_08BIT, 0x04}, + {0x9E94, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E95, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E96, CRL_REG_LEN_08BIT, 0x0F}, + {0x9E97, CRL_REG_LEN_08BIT, 0x00}, + {0x9E98, CRL_REG_LEN_08BIT, 0x00}, + {0x9E99, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9EA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9EA6, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA7, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA8, CRL_REG_LEN_08BIT, 0x3F}, + {0x9EA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9EAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9EAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9EAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9EC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9ECB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9ECD, CRL_REG_LEN_08BIT, 0x0A}, + {0x9F17, CRL_REG_LEN_08BIT, 0x35}, + {0x9F1D, CRL_REG_LEN_08BIT, 0x31}, + {0x9F29, CRL_REG_LEN_08BIT, 0x50}, + {0x9F3B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F41, CRL_REG_LEN_08BIT, 0x6B}, + {0x9F47, CRL_REG_LEN_08BIT, 0x42}, + {0x9F4D, CRL_REG_LEN_08BIT, 0x5A}, + {0x9F6B, CRL_REG_LEN_08BIT, 0x00}, + {0x9F71, CRL_REG_LEN_08BIT, 0xC8}, + {0x9F73, CRL_REG_LEN_08BIT, 0x32}, + {0x9F75, CRL_REG_LEN_08BIT, 0x04}, + {0x9F94, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F95, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F96, CRL_REG_LEN_08BIT, 0x0F}, + {0x9F97, CRL_REG_LEN_08BIT, 0x00}, + {0x9F98, CRL_REG_LEN_08BIT, 0x00}, + {0x9F99, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9A, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9B, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9C, CRL_REG_LEN_08BIT, 0x2F}, + {0x9F9D, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9E, CRL_REG_LEN_08BIT, 0x00}, + {0x9F9F, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA0, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA1, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA2, CRL_REG_LEN_08BIT, 0x0F}, + {0x9FA3, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA4, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA5, CRL_REG_LEN_08BIT, 0x00}, + {0x9FA6, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA7, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA8, CRL_REG_LEN_08BIT, 0x1E}, + {0x9FA9, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAA, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAB, CRL_REG_LEN_08BIT, 0x00}, + {0x9FAC, CRL_REG_LEN_08BIT, 0x09}, + {0x9FAD, CRL_REG_LEN_08BIT, 0x09}, + {0x9FAE, CRL_REG_LEN_08BIT, 0x09}, + {0x9FC9, CRL_REG_LEN_08BIT, 0x0A}, + {0x9FCB, CRL_REG_LEN_08BIT, 0x0A}, + {0x9FCD, CRL_REG_LEN_08BIT, 0x0A}, + {0xA14B, CRL_REG_LEN_08BIT, 0xFF}, + {0xA151, CRL_REG_LEN_08BIT, 0x0C}, + {0xA153, CRL_REG_LEN_08BIT, 0x50}, + {0xA155, CRL_REG_LEN_08BIT, 0x02}, + {0xA157, CRL_REG_LEN_08BIT, 0x00}, + {0xA1AD, CRL_REG_LEN_08BIT, 0xFF}, + {0xA1B3, CRL_REG_LEN_08BIT, 0x0C}, + {0xA1B5, CRL_REG_LEN_08BIT, 0x50}, + {0xA1B9, CRL_REG_LEN_08BIT, 0x00}, + {0xA24B, CRL_REG_LEN_08BIT, 0xFF}, + {0xA257, CRL_REG_LEN_08BIT, 0x00}, + {0xA2AD, CRL_REG_LEN_08BIT, 0xFF}, + {0xA2B9, CRL_REG_LEN_08BIT, 0x00}, + {0xB21F, CRL_REG_LEN_08BIT, 0x04}, + {0xB35C, CRL_REG_LEN_08BIT, 0x00}, + {0xB35E, CRL_REG_LEN_08BIT, 0x08}, +}; + +static struct crl_register_write_rep imx477_4056_3040_19MHZ_slave[] = { + /* Frame Horizontal Clock Count */ + {0x0342, CRL_REG_LEN_08BIT, 0x39}, /* Line length [15:8] */ + {0x0343, CRL_REG_LEN_08BIT, 0x14}, /* Line length [7:0] */ + + /* Frame Vertical Clock Count */ + {0x0340, CRL_REG_LEN_08BIT, 0x20}, /* Frame length [15:8] */ + {0x0341, CRL_REG_LEN_08BIT, 0x11}, /* Frame length [7:0] */ + + /* Visible Size */ + {0x0344, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [12:8] */ + {0x0345, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start X [7:0] */ + {0x0346, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [12:8] */ + {0x0347, CRL_REG_LEN_08BIT, 0x00}, /* Analog cropping start Y [7:0] */ + {0x0348, CRL_REG_LEN_08BIT, 0x0F}, /* Analog cropping end X [12:8] */ + {0x0349, CRL_REG_LEN_08BIT, 0xD7}, /* Analog cropping end X [7:0] */ + {0x034A, CRL_REG_LEN_08BIT, 0x0B}, /* Analog cropping end Y [12:8] */ + {0x034B, CRL_REG_LEN_08BIT, 0xDF}, /* Analog cropping end Y [7:0] */ + + /* Mode Setting */ + {0x00E3, CRL_REG_LEN_08BIT, 0x00}, /* DOL-HDR Disable */ + {0x00E4, CRL_REG_LEN_08BIT, 0x00}, /* DOL Mode: DOL-HDR Disable */ + {0x00FC, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FD, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FE, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x00FF, CRL_REG_LEN_08BIT, 0x0A}, /* The output data fmt for CSI: RAW10 */ + {0x0220, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x0221, CRL_REG_LEN_08BIT, 0x11}, /* Undocumented */ + {0x0381, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, even -> odd */ + {0x0383, CRL_REG_LEN_08BIT, 0x01}, /* Num of pixels skipped, odd -> even */ + {0x0385, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, even -> odd */ + {0x0387, CRL_REG_LEN_08BIT, 0x01}, /* Num of lines skipped, odd -> even */ + {0x0900, CRL_REG_LEN_08BIT, 0x00}, /* Binning mode: Disable */ + {0x0901, CRL_REG_LEN_08BIT, 0x11}, /* Binning Type for Horizontal */ + {0x0902, CRL_REG_LEN_08BIT, 0x02}, /* Binning Type for Vertical */ + {0x3140, CRL_REG_LEN_08BIT, 0x02}, /* Undocumented */ + {0x3C00, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x3C01, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x3C02, CRL_REG_LEN_08BIT, 0xDC}, /* Undocumented */ + {0x3F0D, CRL_REG_LEN_08BIT, 0x00}, /* AD converter: 10 bit */ + {0x5748, CRL_REG_LEN_08BIT, 0x07}, /* Undocumented */ + {0x5749, CRL_REG_LEN_08BIT, 0xFF}, /* Undocumented */ + {0x574A, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x574B, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x7B75, CRL_REG_LEN_08BIT, 0x0E}, /* Undocumented */ + {0x7B76, CRL_REG_LEN_08BIT, 0x09}, /* Undocumented */ + {0x7B77, CRL_REG_LEN_08BIT, 0x0C}, /* Undocumented */ + {0x7B78, CRL_REG_LEN_08BIT, 0x06}, /* Undocumented */ + {0x7B79, CRL_REG_LEN_08BIT, 0x3B}, /* Undocumented */ + {0x7B53, CRL_REG_LEN_08BIT, 0x01}, /* Undocumented */ + {0x9369, CRL_REG_LEN_08BIT, 0x5A}, /* Undocumented */ + {0x936B, CRL_REG_LEN_08BIT, 0x55}, /* Undocumented */ + {0x936D, CRL_REG_LEN_08BIT, 0x28}, /* Undocumented */ + {0x9304, CRL_REG_LEN_08BIT, 0x03}, /* Undocumented */ + {0x9305, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9A, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9B, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9C, CRL_REG_LEN_08BIT, 0x2F}, /* Undocumented */ + {0x9E9D, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9E, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0x9E9F, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + {0xA2A9, CRL_REG_LEN_08BIT, 0x60}, /* Undocumented */ + {0xA2B7, CRL_REG_LEN_08BIT, 0x00}, /* Undocumented */ + + /* Digital Crop & Scaling */ + {0x0401, CRL_REG_LEN_08BIT, 0x00}, /* Scaling mode: No Scaling */ + {0x0404, CRL_REG_LEN_08BIT, 0x00}, /* Down Scaling Factor M [8] */ + {0x0405, CRL_REG_LEN_08BIT, 0x10}, /* Down Scaling Factor M [7:0] */ + {0x0408, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [12:8] */ + {0x0409, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from X [7:0] */ + {0x040A, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [12:8] */ + {0x040B, CRL_REG_LEN_08BIT, 0x00}, /* Crop Offset from Y [7:0] */ + {0x040C, CRL_REG_LEN_08BIT, 0x0F}, /* Width after cropping [12:8] */ + {0x040D, CRL_REG_LEN_08BIT, 0xD8}, /* Width after cropping [7:0] */ + {0x040E, CRL_REG_LEN_08BIT, 0x0B}, /* Height after cropping [12:8] */ + {0x040F, CRL_REG_LEN_08BIT, 0xE0}, /* Height after cropping [7:0] */ + + /* Output Crop */ + {0x034C, CRL_REG_LEN_08BIT, 0x0F}, /* X output size [12:8] */ + {0x034D, CRL_REG_LEN_08BIT, 0xD8}, /* X output size [7:0] */ + {0x034E, CRL_REG_LEN_08BIT, 0x0B}, /* Y output size [12:8] */ + {0x034F, CRL_REG_LEN_08BIT, 0xE0}, /* Y output size [7:0] */ +}; + +static struct crl_mode_rep imx477_modes_slave[] = { + { + .sd_rects_items = ARRAY_SIZE(imx477_4056_3040_rects), + .sd_rects = imx477_4056_3040_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4056, + .height = 3040, + .min_llp = 14612, + .min_fll = 8209, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(imx477_4056_3040_19MHZ_slave), + .mode_regs = imx477_4056_3040_19MHZ_slave, + }, +}; + +static struct crl_flip_data imx477_flip_configurations_slave[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + } +}; + +struct crl_sensor_configuration imx477_slave_crl_configuration = { + + .power_items = ARRAY_SIZE(imx477_power_items), + .power_entities = imx477_power_items, + + .onetime_init_regs_items = ARRAY_SIZE(imx477_onetime_init_regset_slave), + .onetime_init_regs = imx477_onetime_init_regset_slave, + + .powerup_regs_items = ARRAY_SIZE(imx477_powerup_standby), + .powerup_regs = imx477_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(imx477_sensor_detect_regset), + .id_regs = imx477_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(imx477_sensor_subdevs), + .subdevs = imx477_sensor_subdevs, + + .sensor_limits = &imx477_sensor_limits, + + .pll_config_items = ARRAY_SIZE(imx477_pll_configurations), + .pll_configs = imx477_pll_configurations, + + .modes_items = ARRAY_SIZE(imx477_modes_slave), + .modes = imx477_modes_slave, + + .streamon_regs_items = ARRAY_SIZE(imx477_streamon_regs), + .streamon_regs = imx477_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(imx477_streamoff_regs), + .streamoff_regs = imx477_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(imx477_v4l2_ctrls), + .v4l2_ctrl_bank = imx477_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(imx477_crl_csi_data_fmt), + .csi_fmts = imx477_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(imx477_flip_configurations_slave), + .flip_data = imx477_flip_configurations_slave, + + .frame_desc_entries = ARRAY_SIZE(imx477_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = imx477_frame_desc, +}; + +#endif /* __CRLMODULE_IMX477_SLAVE_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_magna_configuration.h b/drivers/media/i2c/crlmodule/crl_magna_configuration.h new file mode 100644 index 000000000000..cd1e316a2cab --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_magna_configuration.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Kishore Bodke + * + */ + +#ifndef __CRLMODULE_MAGNA_CONFIGURATION_H_ +#define __CRLMODULE_MAGNA_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_pll_configuration magna_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 10, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 20, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + } +}; + +static struct crl_subdev_rect_rep magna_1280_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_mode_rep magna_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(magna_1280_720_rects), + .sd_rects = magna_1280_720_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .min_llp = 2250, + .min_fll = 1320, + }, +}; + +static struct crl_sensor_subdev_config magna_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "magna binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "magna pixel array", + } +}; + +static struct crl_sensor_limits magna_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1280, + .y_addr_max = 720, + .min_frame_length_lines = 240, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 320, + .max_line_length_pixels = 32752, +}; + +static struct crl_csi_data_fmt magna_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, +}; + +static struct crl_v4l2_ctrl magna_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_sensor_configuration magna_crl_configuration = { + + .subdev_items = ARRAY_SIZE(magna_sensor_subdevs), + .subdevs = magna_sensor_subdevs, + + .pll_config_items = ARRAY_SIZE(magna_pll_configurations), + .pll_configs = magna_pll_configurations, + + .sensor_limits = &magna_sensor_limits, + + .modes_items = ARRAY_SIZE(magna_modes), + .modes = magna_modes, + + .streamon_regs_items = 0, + .streamon_regs = 0, + + .streamoff_regs_items = 0, + .streamoff_regs = 0, + + .v4l2_ctrls_items = ARRAY_SIZE(magna_v4l2_ctrls), + .v4l2_ctrl_bank = magna_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(magna_crl_csi_data_fmt), + .csi_fmts = magna_crl_csi_data_fmt, + +}; + +#endif /* __CRLMODULE_MAGNA_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov10635_configuration.h b/drivers/media/i2c/crlmodule/crl_ov10635_configuration.h new file mode 100644 index 000000000000..0f81f7de90e6 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov10635_configuration.h @@ -0,0 +1,6368 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Yunliang Ding + * + */ + +#ifndef __CRLMODULE_OV10635_CONFIGURATION_H_ +#define __CRLMODULE_OV10635_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define OV10635_REG_RESET 0x0103 + +static struct crl_register_write_rep ov10635_1280_800_YUV_HDR[] = { + {0x301b, CRL_REG_LEN_08BIT, 0xff}, + {0x301c, CRL_REG_LEN_08BIT, 0xff}, + {0x301a, CRL_REG_LEN_08BIT, 0xff}, + {0x3011, CRL_REG_LEN_08BIT, 0x42}, + {0x6900, CRL_REG_LEN_08BIT, 0x0c}, + {0x6901, CRL_REG_LEN_08BIT, 0x11}, + {0x3503, CRL_REG_LEN_08BIT, 0x10}, + {0x3025, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x20}, + {0x3004, CRL_REG_LEN_08BIT, 0x21}, + {0x3005, CRL_REG_LEN_08BIT, 0x20}, + {0x3006, CRL_REG_LEN_08BIT, 0x91}, + {0x3600, CRL_REG_LEN_08BIT, 0x74}, + {0x3601, CRL_REG_LEN_08BIT, 0x2b}, + {0x3612, CRL_REG_LEN_08BIT, 0x00}, + {0x3611, CRL_REG_LEN_08BIT, 0x67}, + {0x3633, CRL_REG_LEN_08BIT, 0xca}, + {0x3602, CRL_REG_LEN_08BIT, 0x2f}, + {0x3603, CRL_REG_LEN_08BIT, 0x00}, + {0x3630, CRL_REG_LEN_08BIT, 0x28}, + {0x3631, CRL_REG_LEN_08BIT, 0x16}, + {0x3714, CRL_REG_LEN_08BIT, 0x10}, + {0x371d, CRL_REG_LEN_08BIT, 0x01}, + {0x3007, CRL_REG_LEN_08BIT, 0x01}, + {0x3024, CRL_REG_LEN_08BIT, 0x01}, + {0x3020, CRL_REG_LEN_08BIT, 0x0b}, + {0x3702, CRL_REG_LEN_08BIT, 0x20}, + {0x3703, CRL_REG_LEN_08BIT, 0x48}, + {0x3704, CRL_REG_LEN_08BIT, 0x32}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x370c, CRL_REG_LEN_08BIT, 0xc7}, + {0x370d, CRL_REG_LEN_08BIT, 0x80}, + {0x3712, CRL_REG_LEN_08BIT, 0x00}, + {0x3713, CRL_REG_LEN_08BIT, 0x20}, + {0x3715, CRL_REG_LEN_08BIT, 0x04}, + {0x381d, CRL_REG_LEN_08BIT, 0x40}, + {0x381c, CRL_REG_LEN_08BIT, 0x00}, + {0x3822, CRL_REG_LEN_08BIT, 0x50}, + {0x3824, CRL_REG_LEN_08BIT, 0x50}, + {0x3815, CRL_REG_LEN_08BIT, 0x8c}, + {0x3804, CRL_REG_LEN_08BIT, 0x05}, + {0x3805, CRL_REG_LEN_08BIT, 0x1f}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3806, CRL_REG_LEN_08BIT, 0x03}, + {0x3807, CRL_REG_LEN_08BIT, 0x29}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0x04}, + {0x3808, CRL_REG_LEN_08BIT, 0x05}, + {0x3809, CRL_REG_LEN_08BIT, 0x00}, + {0x380a, CRL_REG_LEN_08BIT, 0x03}, + {0x380b, CRL_REG_LEN_08BIT, 0x20}, + {0x380c, CRL_REG_LEN_08BIT, 0x07}, + {0x380d, CRL_REG_LEN_08BIT, 0x71}, + {0x6e42, CRL_REG_LEN_08BIT, 0x03}, + {0x6e43, CRL_REG_LEN_08BIT, 0x48}, + {0x380e, CRL_REG_LEN_08BIT, 0x03}, + {0x380f, CRL_REG_LEN_08BIT, 0x48}, + {0x3813, CRL_REG_LEN_08BIT, 0x02}, + {0x3811, CRL_REG_LEN_08BIT, 0x10}, + {0x381f, CRL_REG_LEN_08BIT, 0x0c}, + {0x3828, CRL_REG_LEN_08BIT, 0x03}, + {0x3829, CRL_REG_LEN_08BIT, 0x10}, + {0x382a, CRL_REG_LEN_08BIT, 0x10}, + {0x382b, CRL_REG_LEN_08BIT, 0x10}, + {0x3621, CRL_REG_LEN_08BIT, 0x64}, + {0x5005, CRL_REG_LEN_08BIT, 0x08}, + {0x56d5, CRL_REG_LEN_08BIT, 0x00}, + {0x56d6, CRL_REG_LEN_08BIT, 0x80}, + {0x56d7, CRL_REG_LEN_08BIT, 0x00}, + {0x56d8, CRL_REG_LEN_08BIT, 0x00}, + {0x56d9, CRL_REG_LEN_08BIT, 0x00}, + {0x56da, CRL_REG_LEN_08BIT, 0x80}, + {0x56db, CRL_REG_LEN_08BIT, 0x00}, + {0x56dc, CRL_REG_LEN_08BIT, 0x00}, + {0x56e8, CRL_REG_LEN_08BIT, 0x00}, + {0x56e9, CRL_REG_LEN_08BIT, 0x7f}, + {0x56ea, CRL_REG_LEN_08BIT, 0x00}, + {0x56eb, CRL_REG_LEN_08BIT, 0x7f}, + {0x5100, CRL_REG_LEN_08BIT, 0x00}, + {0x5101, CRL_REG_LEN_08BIT, 0x80}, + {0x5102, CRL_REG_LEN_08BIT, 0x00}, + {0x5103, CRL_REG_LEN_08BIT, 0x80}, + {0x5104, CRL_REG_LEN_08BIT, 0x00}, + {0x5105, CRL_REG_LEN_08BIT, 0x80}, + {0x5106, CRL_REG_LEN_08BIT, 0x00}, + {0x5107, CRL_REG_LEN_08BIT, 0x80}, + {0x5108, CRL_REG_LEN_08BIT, 0x00}, + {0x5109, CRL_REG_LEN_08BIT, 0x00}, + {0x510a, CRL_REG_LEN_08BIT, 0x00}, + {0x510b, CRL_REG_LEN_08BIT, 0x00}, + {0x510c, CRL_REG_LEN_08BIT, 0x00}, + {0x510d, CRL_REG_LEN_08BIT, 0x00}, + {0x510e, CRL_REG_LEN_08BIT, 0x00}, + {0x510f, CRL_REG_LEN_08BIT, 0x00}, + {0x5110, CRL_REG_LEN_08BIT, 0x00}, + {0x5111, CRL_REG_LEN_08BIT, 0x80}, + {0x5112, CRL_REG_LEN_08BIT, 0x00}, + {0x5113, CRL_REG_LEN_08BIT, 0x80}, + {0x5114, CRL_REG_LEN_08BIT, 0x00}, + {0x5115, CRL_REG_LEN_08BIT, 0x80}, + {0x5116, CRL_REG_LEN_08BIT, 0x00}, + {0x5117, CRL_REG_LEN_08BIT, 0x80}, + {0x5118, CRL_REG_LEN_08BIT, 0x00}, + {0x5119, CRL_REG_LEN_08BIT, 0x00}, + {0x511a, CRL_REG_LEN_08BIT, 0x00}, + {0x511b, CRL_REG_LEN_08BIT, 0x00}, + {0x511c, CRL_REG_LEN_08BIT, 0x00}, + {0x511d, CRL_REG_LEN_08BIT, 0x00}, + {0x511e, CRL_REG_LEN_08BIT, 0x00}, + {0x511f, CRL_REG_LEN_08BIT, 0x00}, + {0x56d0, CRL_REG_LEN_08BIT, 0x00}, + {0x5006, CRL_REG_LEN_08BIT, 0x24}, + {0x5608, CRL_REG_LEN_08BIT, 0x0d}, + {0x52d7, CRL_REG_LEN_08BIT, 0x06}, + {0x528d, CRL_REG_LEN_08BIT, 0x08}, + {0x5293, CRL_REG_LEN_08BIT, 0x12}, + {0x52d3, CRL_REG_LEN_08BIT, 0x12}, + {0x5288, CRL_REG_LEN_08BIT, 0x06}, + {0x5289, CRL_REG_LEN_08BIT, 0x20}, + {0x52c8, CRL_REG_LEN_08BIT, 0x06}, + {0x52c9, CRL_REG_LEN_08BIT, 0x20}, + {0x52cd, CRL_REG_LEN_08BIT, 0x04}, + {0x5381, CRL_REG_LEN_08BIT, 0x00}, + {0x5382, CRL_REG_LEN_08BIT, 0xff}, + {0x5589, CRL_REG_LEN_08BIT, 0x76}, + {0x558a, CRL_REG_LEN_08BIT, 0x47}, + {0x558b, CRL_REG_LEN_08BIT, 0xef}, + {0x558c, CRL_REG_LEN_08BIT, 0xc9}, + {0x558d, CRL_REG_LEN_08BIT, 0x49}, + {0x558e, CRL_REG_LEN_08BIT, 0x30}, + {0x558f, CRL_REG_LEN_08BIT, 0x67}, + {0x5590, CRL_REG_LEN_08BIT, 0x3f}, + {0x5591, CRL_REG_LEN_08BIT, 0xf0}, + {0x5592, CRL_REG_LEN_08BIT, 0x10}, + {0x55a2, CRL_REG_LEN_08BIT, 0x6d}, + {0x55a3, CRL_REG_LEN_08BIT, 0x55}, + {0x55a4, CRL_REG_LEN_08BIT, 0xc3}, + {0x55a5, CRL_REG_LEN_08BIT, 0xb5}, + {0x55a6, CRL_REG_LEN_08BIT, 0x43}, + {0x55a7, CRL_REG_LEN_08BIT, 0x38}, + {0x55a8, CRL_REG_LEN_08BIT, 0x5f}, + {0x55a9, CRL_REG_LEN_08BIT, 0x4b}, + {0x55aa, CRL_REG_LEN_08BIT, 0xf0}, + {0x55ab, CRL_REG_LEN_08BIT, 0x10}, + {0x5581, CRL_REG_LEN_08BIT, 0x52}, + {0x5300, CRL_REG_LEN_08BIT, 0x01}, + {0x5301, CRL_REG_LEN_08BIT, 0x00}, + {0x5302, CRL_REG_LEN_08BIT, 0x00}, + {0x5303, CRL_REG_LEN_08BIT, 0x0e}, + {0x5304, CRL_REG_LEN_08BIT, 0x00}, + {0x5305, CRL_REG_LEN_08BIT, 0x0e}, + {0x5306, CRL_REG_LEN_08BIT, 0x00}, + {0x5307, CRL_REG_LEN_08BIT, 0x36}, + {0x5308, CRL_REG_LEN_08BIT, 0x00}, + {0x5309, CRL_REG_LEN_08BIT, 0xd9}, + {0x530a, CRL_REG_LEN_08BIT, 0x00}, + {0x530b, CRL_REG_LEN_08BIT, 0x0f}, + {0x530c, CRL_REG_LEN_08BIT, 0x00}, + {0x530d, CRL_REG_LEN_08BIT, 0x2c}, + {0x530e, CRL_REG_LEN_08BIT, 0x00}, + {0x530f, CRL_REG_LEN_08BIT, 0x59}, + {0x5310, CRL_REG_LEN_08BIT, 0x00}, + {0x5311, CRL_REG_LEN_08BIT, 0x7b}, + {0x5312, CRL_REG_LEN_08BIT, 0x00}, + {0x5313, CRL_REG_LEN_08BIT, 0x22}, + {0x5314, CRL_REG_LEN_08BIT, 0x00}, + {0x5315, CRL_REG_LEN_08BIT, 0xd5}, + {0x5316, CRL_REG_LEN_08BIT, 0x00}, + {0x5317, CRL_REG_LEN_08BIT, 0x13}, + {0x5318, CRL_REG_LEN_08BIT, 0x00}, + {0x5319, CRL_REG_LEN_08BIT, 0x18}, + {0x531a, CRL_REG_LEN_08BIT, 0x00}, + {0x531b, CRL_REG_LEN_08BIT, 0x26}, + {0x531c, CRL_REG_LEN_08BIT, 0x00}, + {0x531d, CRL_REG_LEN_08BIT, 0xdc}, + {0x531e, CRL_REG_LEN_08BIT, 0x00}, + {0x531f, CRL_REG_LEN_08BIT, 0x02}, + {0x5320, CRL_REG_LEN_08BIT, 0x00}, + {0x5321, CRL_REG_LEN_08BIT, 0x24}, + {0x5322, CRL_REG_LEN_08BIT, 0x00}, + {0x5323, CRL_REG_LEN_08BIT, 0x56}, + {0x5324, CRL_REG_LEN_08BIT, 0x00}, + {0x5325, CRL_REG_LEN_08BIT, 0x85}, + {0x5326, CRL_REG_LEN_08BIT, 0x00}, + {0x5327, CRL_REG_LEN_08BIT, 0x20}, + {0x5609, CRL_REG_LEN_08BIT, 0x01}, + {0x560a, CRL_REG_LEN_08BIT, 0x40}, + {0x560b, CRL_REG_LEN_08BIT, 0x01}, + {0x560c, CRL_REG_LEN_08BIT, 0x40}, + {0x560d, CRL_REG_LEN_08BIT, 0x00}, + {0x560e, CRL_REG_LEN_08BIT, 0xfa}, + {0x560f, CRL_REG_LEN_08BIT, 0x00}, + {0x5610, CRL_REG_LEN_08BIT, 0xfa}, + {0x5611, CRL_REG_LEN_08BIT, 0x02}, + {0x5612, CRL_REG_LEN_08BIT, 0x80}, + {0x5613, CRL_REG_LEN_08BIT, 0x02}, + {0x5614, CRL_REG_LEN_08BIT, 0x80}, + {0x5615, CRL_REG_LEN_08BIT, 0x01}, + {0x5616, CRL_REG_LEN_08BIT, 0x2c}, + {0x5617, CRL_REG_LEN_08BIT, 0x01}, + {0x5618, CRL_REG_LEN_08BIT, 0x2c}, + {0x563b, CRL_REG_LEN_08BIT, 0x01}, + {0x563c, CRL_REG_LEN_08BIT, 0x01}, + {0x563d, CRL_REG_LEN_08BIT, 0x01}, + {0x563e, CRL_REG_LEN_08BIT, 0x01}, + {0x563f, CRL_REG_LEN_08BIT, 0x03}, + {0x5640, CRL_REG_LEN_08BIT, 0x03}, + {0x5641, CRL_REG_LEN_08BIT, 0x03}, + {0x5642, CRL_REG_LEN_08BIT, 0x05}, + {0x5643, CRL_REG_LEN_08BIT, 0x09}, + {0x5644, CRL_REG_LEN_08BIT, 0x05}, + {0x5645, CRL_REG_LEN_08BIT, 0x05}, + {0x5646, CRL_REG_LEN_08BIT, 0x05}, + {0x5647, CRL_REG_LEN_08BIT, 0x05}, + {0x5651, CRL_REG_LEN_08BIT, 0x00}, + {0x5652, CRL_REG_LEN_08BIT, 0x80}, + {0x521a, CRL_REG_LEN_08BIT, 0x01}, + {0x521b, CRL_REG_LEN_08BIT, 0x03}, + {0x521c, CRL_REG_LEN_08BIT, 0x06}, + {0x521d, CRL_REG_LEN_08BIT, 0x0a}, + {0x521e, CRL_REG_LEN_08BIT, 0x0e}, + {0x521f, CRL_REG_LEN_08BIT, 0x12}, + {0x5220, CRL_REG_LEN_08BIT, 0x16}, + {0x5223, CRL_REG_LEN_08BIT, 0x02}, + {0x5225, CRL_REG_LEN_08BIT, 0x04}, + {0x5227, CRL_REG_LEN_08BIT, 0x08}, + {0x5229, CRL_REG_LEN_08BIT, 0x0c}, + {0x522b, CRL_REG_LEN_08BIT, 0x12}, + {0x522d, CRL_REG_LEN_08BIT, 0x18}, + {0x522f, CRL_REG_LEN_08BIT, 0x1e}, + {0x5241, CRL_REG_LEN_08BIT, 0x04}, + {0x5242, CRL_REG_LEN_08BIT, 0x01}, + {0x5243, CRL_REG_LEN_08BIT, 0x03}, + {0x5244, CRL_REG_LEN_08BIT, 0x06}, + {0x5245, CRL_REG_LEN_08BIT, 0x0a}, + {0x5246, CRL_REG_LEN_08BIT, 0x0e}, + {0x5247, CRL_REG_LEN_08BIT, 0x12}, + {0x5248, CRL_REG_LEN_08BIT, 0x16}, + {0x524a, CRL_REG_LEN_08BIT, 0x03}, + {0x524c, CRL_REG_LEN_08BIT, 0x04}, + {0x524e, CRL_REG_LEN_08BIT, 0x08}, + {0x5250, CRL_REG_LEN_08BIT, 0x0c}, + {0x5252, CRL_REG_LEN_08BIT, 0x12}, + {0x5254, CRL_REG_LEN_08BIT, 0x18}, + {0x5256, CRL_REG_LEN_08BIT, 0x1e}, + {0x4606, CRL_REG_LEN_08BIT, 0x07}, + {0x4607, CRL_REG_LEN_08BIT, 0x71}, + {0x460a, CRL_REG_LEN_08BIT, 0x02}, + {0x460b, CRL_REG_LEN_08BIT, 0x70}, + {0x460c, CRL_REG_LEN_08BIT, 0x00}, + {0x4620, CRL_REG_LEN_08BIT, 0x0e}, + {0x4700, CRL_REG_LEN_08BIT, 0x04}, + {0x4701, CRL_REG_LEN_08BIT, 0x00}, + {0x4702, CRL_REG_LEN_08BIT, 0x01}, + {0x4004, CRL_REG_LEN_08BIT, 0x04}, + {0x4005, CRL_REG_LEN_08BIT, 0x18}, + {0x4001, CRL_REG_LEN_08BIT, 0x06}, + {0x4050, CRL_REG_LEN_08BIT, 0x22}, + {0x4051, CRL_REG_LEN_08BIT, 0x24}, + {0x4052, CRL_REG_LEN_08BIT, 0x02}, + {0x4057, CRL_REG_LEN_08BIT, 0x9c}, + {0x405a, CRL_REG_LEN_08BIT, 0x00}, + {0x3832, CRL_REG_LEN_08BIT, 0x00}, + {0x3833, CRL_REG_LEN_08BIT, 0x02}, + {0x3834, CRL_REG_LEN_08BIT, 0x03}, + {0x3835, CRL_REG_LEN_08BIT, 0x48}, + {0x302e, CRL_REG_LEN_08BIT, 0x00}, + {0x4202, CRL_REG_LEN_08BIT, 0x02}, + {0x3023, CRL_REG_LEN_08BIT, 0x10}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x6f10, CRL_REG_LEN_08BIT, 0x07}, + {0x6f11, CRL_REG_LEN_08BIT, 0x82}, + {0x6f12, CRL_REG_LEN_08BIT, 0x04}, + {0x6f13, CRL_REG_LEN_08BIT, 0x00}, + {0x6f14, CRL_REG_LEN_08BIT, 0x1f}, + {0x6f15, CRL_REG_LEN_08BIT, 0xdd}, + {0x6f16, CRL_REG_LEN_08BIT, 0x04}, + {0x6f17, CRL_REG_LEN_08BIT, 0x04}, + {0x6f18, CRL_REG_LEN_08BIT, 0x36}, + {0x6f19, CRL_REG_LEN_08BIT, 0x66}, + {0x6f1a, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1b, CRL_REG_LEN_08BIT, 0x08}, + {0x6f1c, CRL_REG_LEN_08BIT, 0x0c}, + {0x6f1d, CRL_REG_LEN_08BIT, 0xe7}, + {0x6f1e, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1f, CRL_REG_LEN_08BIT, 0x0c}, + {0xd000, CRL_REG_LEN_08BIT, 0x19}, + {0xd001, CRL_REG_LEN_08BIT, 0xa0}, + {0xd002, CRL_REG_LEN_08BIT, 0x00}, + {0xd003, CRL_REG_LEN_08BIT, 0x01}, + {0xd004, CRL_REG_LEN_08BIT, 0xa9}, + {0xd005, CRL_REG_LEN_08BIT, 0xad}, + {0xd006, CRL_REG_LEN_08BIT, 0x10}, + {0xd007, CRL_REG_LEN_08BIT, 0x40}, + {0xd008, CRL_REG_LEN_08BIT, 0x44}, + {0xd009, CRL_REG_LEN_08BIT, 0x00}, + {0xd00a, CRL_REG_LEN_08BIT, 0x68}, + {0xd00b, CRL_REG_LEN_08BIT, 0x00}, + {0xd00c, CRL_REG_LEN_08BIT, 0x15}, + {0xd00d, CRL_REG_LEN_08BIT, 0x00}, + {0xd00e, CRL_REG_LEN_08BIT, 0x00}, + {0xd00f, CRL_REG_LEN_08BIT, 0x00}, + {0xd010, CRL_REG_LEN_08BIT, 0x19}, + {0xd011, CRL_REG_LEN_08BIT, 0xa0}, + {0xd012, CRL_REG_LEN_08BIT, 0x00}, + {0xd013, CRL_REG_LEN_08BIT, 0x01}, + {0xd014, CRL_REG_LEN_08BIT, 0xa9}, + {0xd015, CRL_REG_LEN_08BIT, 0xad}, + {0xd016, CRL_REG_LEN_08BIT, 0x13}, + {0xd017, CRL_REG_LEN_08BIT, 0xd0}, + {0xd018, CRL_REG_LEN_08BIT, 0x44}, + {0xd019, CRL_REG_LEN_08BIT, 0x00}, + {0xd01a, CRL_REG_LEN_08BIT, 0x68}, + {0xd01b, CRL_REG_LEN_08BIT, 0x00}, + {0xd01c, CRL_REG_LEN_08BIT, 0x15}, + {0xd01d, CRL_REG_LEN_08BIT, 0x00}, + {0xd01e, CRL_REG_LEN_08BIT, 0x00}, + {0xd01f, CRL_REG_LEN_08BIT, 0x00}, + {0xd020, CRL_REG_LEN_08BIT, 0x19}, + {0xd021, CRL_REG_LEN_08BIT, 0xa0}, + {0xd022, CRL_REG_LEN_08BIT, 0x00}, + {0xd023, CRL_REG_LEN_08BIT, 0x01}, + {0xd024, CRL_REG_LEN_08BIT, 0xa9}, + {0xd025, CRL_REG_LEN_08BIT, 0xad}, + {0xd026, CRL_REG_LEN_08BIT, 0x14}, + {0xd027, CRL_REG_LEN_08BIT, 0xb8}, + {0xd028, CRL_REG_LEN_08BIT, 0x44}, + {0xd029, CRL_REG_LEN_08BIT, 0x00}, + {0xd02a, CRL_REG_LEN_08BIT, 0x68}, + {0xd02b, CRL_REG_LEN_08BIT, 0x00}, + {0xd02c, CRL_REG_LEN_08BIT, 0x15}, + {0xd02d, CRL_REG_LEN_08BIT, 0x00}, + {0xd02e, CRL_REG_LEN_08BIT, 0x00}, + {0xd02f, CRL_REG_LEN_08BIT, 0x00}, + {0xd030, CRL_REG_LEN_08BIT, 0x19}, + {0xd031, CRL_REG_LEN_08BIT, 0xa0}, + {0xd032, CRL_REG_LEN_08BIT, 0x00}, + {0xd033, CRL_REG_LEN_08BIT, 0x01}, + {0xd034, CRL_REG_LEN_08BIT, 0xa9}, + {0xd035, CRL_REG_LEN_08BIT, 0xad}, + {0xd036, CRL_REG_LEN_08BIT, 0x14}, + {0xd037, CRL_REG_LEN_08BIT, 0xdc}, + {0xd038, CRL_REG_LEN_08BIT, 0x44}, + {0xd039, CRL_REG_LEN_08BIT, 0x00}, + {0xd03a, CRL_REG_LEN_08BIT, 0x68}, + {0xd03b, CRL_REG_LEN_08BIT, 0x00}, + {0xd03c, CRL_REG_LEN_08BIT, 0x15}, + {0xd03d, CRL_REG_LEN_08BIT, 0x00}, + {0xd03e, CRL_REG_LEN_08BIT, 0x00}, + {0xd03f, CRL_REG_LEN_08BIT, 0x00}, + {0xd040, CRL_REG_LEN_08BIT, 0x9c}, + {0xd041, CRL_REG_LEN_08BIT, 0x21}, + {0xd042, CRL_REG_LEN_08BIT, 0xff}, + {0xd043, CRL_REG_LEN_08BIT, 0xe4}, + {0xd044, CRL_REG_LEN_08BIT, 0xd4}, + {0xd045, CRL_REG_LEN_08BIT, 0x01}, + {0xd046, CRL_REG_LEN_08BIT, 0x48}, + {0xd047, CRL_REG_LEN_08BIT, 0x00}, + {0xd048, CRL_REG_LEN_08BIT, 0xd4}, + {0xd049, CRL_REG_LEN_08BIT, 0x01}, + {0xd04a, CRL_REG_LEN_08BIT, 0x50}, + {0xd04b, CRL_REG_LEN_08BIT, 0x04}, + {0xd04c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd04d, CRL_REG_LEN_08BIT, 0x01}, + {0xd04e, CRL_REG_LEN_08BIT, 0x60}, + {0xd04f, CRL_REG_LEN_08BIT, 0x08}, + {0xd050, CRL_REG_LEN_08BIT, 0xd4}, + {0xd051, CRL_REG_LEN_08BIT, 0x01}, + {0xd052, CRL_REG_LEN_08BIT, 0x70}, + {0xd053, CRL_REG_LEN_08BIT, 0x0c}, + {0xd054, CRL_REG_LEN_08BIT, 0xd4}, + {0xd055, CRL_REG_LEN_08BIT, 0x01}, + {0xd056, CRL_REG_LEN_08BIT, 0x80}, + {0xd057, CRL_REG_LEN_08BIT, 0x10}, + {0xd058, CRL_REG_LEN_08BIT, 0x19}, + {0xd059, CRL_REG_LEN_08BIT, 0xc0}, + {0xd05a, CRL_REG_LEN_08BIT, 0x00}, + {0xd05b, CRL_REG_LEN_08BIT, 0x01}, + {0xd05c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd05d, CRL_REG_LEN_08BIT, 0xce}, + {0xd05e, CRL_REG_LEN_08BIT, 0x02}, + {0xd05f, CRL_REG_LEN_08BIT, 0xa4}, + {0xd060, CRL_REG_LEN_08BIT, 0x9c}, + {0xd061, CRL_REG_LEN_08BIT, 0xa0}, + {0xd062, CRL_REG_LEN_08BIT, 0x00}, + {0xd063, CRL_REG_LEN_08BIT, 0x00}, + {0xd064, CRL_REG_LEN_08BIT, 0x84}, + {0xd065, CRL_REG_LEN_08BIT, 0x6e}, + {0xd066, CRL_REG_LEN_08BIT, 0x00}, + {0xd067, CRL_REG_LEN_08BIT, 0x00}, + {0xd068, CRL_REG_LEN_08BIT, 0xd8}, + {0xd069, CRL_REG_LEN_08BIT, 0x03}, + {0xd06a, CRL_REG_LEN_08BIT, 0x28}, + {0xd06b, CRL_REG_LEN_08BIT, 0x76}, + {0xd06c, CRL_REG_LEN_08BIT, 0x1a}, + {0xd06d, CRL_REG_LEN_08BIT, 0x00}, + {0xd06e, CRL_REG_LEN_08BIT, 0x00}, + {0xd06f, CRL_REG_LEN_08BIT, 0x01}, + {0xd070, CRL_REG_LEN_08BIT, 0xaa}, + {0xd071, CRL_REG_LEN_08BIT, 0x10}, + {0xd072, CRL_REG_LEN_08BIT, 0x03}, + {0xd073, CRL_REG_LEN_08BIT, 0xf0}, + {0xd074, CRL_REG_LEN_08BIT, 0x18}, + {0xd075, CRL_REG_LEN_08BIT, 0x60}, + {0xd076, CRL_REG_LEN_08BIT, 0x00}, + {0xd077, CRL_REG_LEN_08BIT, 0x01}, + {0xd078, CRL_REG_LEN_08BIT, 0xa8}, + {0xd079, CRL_REG_LEN_08BIT, 0x63}, + {0xd07a, CRL_REG_LEN_08BIT, 0x07}, + {0xd07b, CRL_REG_LEN_08BIT, 0x80}, + {0xd07c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd07d, CRL_REG_LEN_08BIT, 0xa0}, + {0xd07e, CRL_REG_LEN_08BIT, 0x00}, + {0xd07f, CRL_REG_LEN_08BIT, 0x04}, + {0xd080, CRL_REG_LEN_08BIT, 0x18}, + {0xd081, CRL_REG_LEN_08BIT, 0xc0}, + {0xd082, CRL_REG_LEN_08BIT, 0x00}, + {0xd083, CRL_REG_LEN_08BIT, 0x00}, + {0xd084, CRL_REG_LEN_08BIT, 0xa8}, + {0xd085, CRL_REG_LEN_08BIT, 0xc6}, + {0xd086, CRL_REG_LEN_08BIT, 0x00}, + {0xd087, CRL_REG_LEN_08BIT, 0x00}, + {0xd088, CRL_REG_LEN_08BIT, 0x8c}, + {0xd089, CRL_REG_LEN_08BIT, 0x63}, + {0xd08a, CRL_REG_LEN_08BIT, 0x00}, + {0xd08b, CRL_REG_LEN_08BIT, 0x00}, + {0xd08c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd08d, CRL_REG_LEN_08BIT, 0x01}, + {0xd08e, CRL_REG_LEN_08BIT, 0x28}, + {0xd08f, CRL_REG_LEN_08BIT, 0x14}, + {0xd090, CRL_REG_LEN_08BIT, 0xd4}, + {0xd091, CRL_REG_LEN_08BIT, 0x01}, + {0xd092, CRL_REG_LEN_08BIT, 0x30}, + {0xd093, CRL_REG_LEN_08BIT, 0x18}, + {0xd094, CRL_REG_LEN_08BIT, 0x07}, + {0xd095, CRL_REG_LEN_08BIT, 0xff}, + {0xd096, CRL_REG_LEN_08BIT, 0xf8}, + {0xd097, CRL_REG_LEN_08BIT, 0xfd}, + {0xd098, CRL_REG_LEN_08BIT, 0x9c}, + {0xd099, CRL_REG_LEN_08BIT, 0x80}, + {0xd09a, CRL_REG_LEN_08BIT, 0x00}, + {0xd09b, CRL_REG_LEN_08BIT, 0x03}, + {0xd09c, CRL_REG_LEN_08BIT, 0xa5}, + {0xd09d, CRL_REG_LEN_08BIT, 0x6b}, + {0xd09e, CRL_REG_LEN_08BIT, 0x00}, + {0xd09f, CRL_REG_LEN_08BIT, 0xff}, + {0xd0a0, CRL_REG_LEN_08BIT, 0x18}, + {0xd0a1, CRL_REG_LEN_08BIT, 0xc0}, + {0xd0a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0a3, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a4, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0a5, CRL_REG_LEN_08BIT, 0xc6}, + {0xd0a6, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a7, CRL_REG_LEN_08BIT, 0x02}, + {0xd0a8, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0a9, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0aa, CRL_REG_LEN_08BIT, 0x58}, + {0xd0ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ac, CRL_REG_LEN_08BIT, 0x84}, + {0xd0ad, CRL_REG_LEN_08BIT, 0x8e}, + {0xd0ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd0af, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0b1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0b2, CRL_REG_LEN_08BIT, 0x30}, + {0xd0b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b4, CRL_REG_LEN_08BIT, 0x98}, + {0xd0b5, CRL_REG_LEN_08BIT, 0xb0}, + {0xd0b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0b9, CRL_REG_LEN_08BIT, 0x64}, + {0xd0ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd0bb, CRL_REG_LEN_08BIT, 0x6e}, + {0xd0bc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0bd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd0be, CRL_REG_LEN_08BIT, 0x18}, + {0xd0bf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c0, CRL_REG_LEN_08BIT, 0x10}, + {0xd0c1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c3, CRL_REG_LEN_08BIT, 0x06}, + {0xd0c4, CRL_REG_LEN_08BIT, 0x95}, + {0xd0c5, CRL_REG_LEN_08BIT, 0x8b}, + {0xd0c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c8, CRL_REG_LEN_08BIT, 0x94}, + {0xd0c9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd0ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd0cb, CRL_REG_LEN_08BIT, 0x70}, + {0xd0cc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0cd, CRL_REG_LEN_08BIT, 0x65}, + {0xd0ce, CRL_REG_LEN_08BIT, 0x60}, + {0xd0cf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd0d1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d3, CRL_REG_LEN_08BIT, 0x62}, + {0xd0d4, CRL_REG_LEN_08BIT, 0x15}, + {0xd0d5, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d8, CRL_REG_LEN_08BIT, 0x18}, + {0xd0d9, CRL_REG_LEN_08BIT, 0x60}, + {0xd0da, CRL_REG_LEN_08BIT, 0x80}, + {0xd0db, CRL_REG_LEN_08BIT, 0x06}, + {0xd0dc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0dd, CRL_REG_LEN_08BIT, 0x83}, + {0xd0de, CRL_REG_LEN_08BIT, 0x38}, + {0xd0df, CRL_REG_LEN_08BIT, 0x29}, + {0xd0e0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e1, CRL_REG_LEN_08BIT, 0xe3}, + {0xd0e2, CRL_REG_LEN_08BIT, 0x40}, + {0xd0e3, CRL_REG_LEN_08BIT, 0x08}, + {0xd0e4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0e5, CRL_REG_LEN_08BIT, 0x84}, + {0xd0e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e9, CRL_REG_LEN_08BIT, 0xa3}, + {0xd0ea, CRL_REG_LEN_08BIT, 0x40}, + {0xd0eb, CRL_REG_LEN_08BIT, 0x09}, + {0xd0ec, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0ed, CRL_REG_LEN_08BIT, 0xc3}, + {0xd0ee, CRL_REG_LEN_08BIT, 0x38}, + {0xd0ef, CRL_REG_LEN_08BIT, 0x2a}, + {0xd0f0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f1, CRL_REG_LEN_08BIT, 0x07}, + {0xd0f2, CRL_REG_LEN_08BIT, 0x20}, + {0xd0f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0f5, CRL_REG_LEN_08BIT, 0x66}, + {0xd0f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f8, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f9, CRL_REG_LEN_08BIT, 0x05}, + {0xd0fa, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd0fc, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fd, CRL_REG_LEN_08BIT, 0x60}, + {0xd0fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ff, CRL_REG_LEN_08BIT, 0x01}, + {0xd100, CRL_REG_LEN_08BIT, 0x98}, + {0xd101, CRL_REG_LEN_08BIT, 0x90}, + {0xd102, CRL_REG_LEN_08BIT, 0x00}, + {0xd103, CRL_REG_LEN_08BIT, 0x00}, + {0xd104, CRL_REG_LEN_08BIT, 0x84}, + {0xd105, CRL_REG_LEN_08BIT, 0xae}, + {0xd106, CRL_REG_LEN_08BIT, 0x00}, + {0xd107, CRL_REG_LEN_08BIT, 0x00}, + {0xd108, CRL_REG_LEN_08BIT, 0xa8}, + {0xd109, CRL_REG_LEN_08BIT, 0x63}, + {0xd10a, CRL_REG_LEN_08BIT, 0x06}, + {0xd10b, CRL_REG_LEN_08BIT, 0x4c}, + {0xd10c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd10d, CRL_REG_LEN_08BIT, 0xc0}, + {0xd10e, CRL_REG_LEN_08BIT, 0x00}, + {0xd10f, CRL_REG_LEN_08BIT, 0x00}, + {0xd110, CRL_REG_LEN_08BIT, 0xd8}, + {0xd111, CRL_REG_LEN_08BIT, 0x03}, + {0xd112, CRL_REG_LEN_08BIT, 0x30}, + {0xd113, CRL_REG_LEN_08BIT, 0x00}, + {0xd114, CRL_REG_LEN_08BIT, 0x8c}, + {0xd115, CRL_REG_LEN_08BIT, 0x65}, + {0xd116, CRL_REG_LEN_08BIT, 0x00}, + {0xd117, CRL_REG_LEN_08BIT, 0x6e}, + {0xd118, CRL_REG_LEN_08BIT, 0xe5}, + {0xd119, CRL_REG_LEN_08BIT, 0x84}, + {0xd11a, CRL_REG_LEN_08BIT, 0x18}, + {0xd11b, CRL_REG_LEN_08BIT, 0x00}, + {0xd11c, CRL_REG_LEN_08BIT, 0x10}, + {0xd11d, CRL_REG_LEN_08BIT, 0x00}, + {0xd11e, CRL_REG_LEN_08BIT, 0x00}, + {0xd11f, CRL_REG_LEN_08BIT, 0x07}, + {0xd120, CRL_REG_LEN_08BIT, 0x18}, + {0xd121, CRL_REG_LEN_08BIT, 0x80}, + {0xd122, CRL_REG_LEN_08BIT, 0x80}, + {0xd123, CRL_REG_LEN_08BIT, 0x06}, + {0xd124, CRL_REG_LEN_08BIT, 0x94}, + {0xd125, CRL_REG_LEN_08BIT, 0x65}, + {0xd126, CRL_REG_LEN_08BIT, 0x00}, + {0xd127, CRL_REG_LEN_08BIT, 0x70}, + {0xd128, CRL_REG_LEN_08BIT, 0xe5}, + {0xd129, CRL_REG_LEN_08BIT, 0x43}, + {0xd12a, CRL_REG_LEN_08BIT, 0x60}, + {0xd12b, CRL_REG_LEN_08BIT, 0x00}, + {0xd12c, CRL_REG_LEN_08BIT, 0x0c}, + {0xd12d, CRL_REG_LEN_08BIT, 0x00}, + {0xd12e, CRL_REG_LEN_08BIT, 0x00}, + {0xd12f, CRL_REG_LEN_08BIT, 0x3e}, + {0xd130, CRL_REG_LEN_08BIT, 0xa8}, + {0xd131, CRL_REG_LEN_08BIT, 0x64}, + {0xd132, CRL_REG_LEN_08BIT, 0x38}, + {0xd133, CRL_REG_LEN_08BIT, 0x24}, + {0xd134, CRL_REG_LEN_08BIT, 0x18}, + {0xd135, CRL_REG_LEN_08BIT, 0x80}, + {0xd136, CRL_REG_LEN_08BIT, 0x80}, + {0xd137, CRL_REG_LEN_08BIT, 0x06}, + {0xd138, CRL_REG_LEN_08BIT, 0xa8}, + {0xd139, CRL_REG_LEN_08BIT, 0x64}, + {0xd13a, CRL_REG_LEN_08BIT, 0x38}, + {0xd13b, CRL_REG_LEN_08BIT, 0x24}, + {0xd13c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd13d, CRL_REG_LEN_08BIT, 0x63}, + {0xd13e, CRL_REG_LEN_08BIT, 0x00}, + {0xd13f, CRL_REG_LEN_08BIT, 0x00}, + {0xd140, CRL_REG_LEN_08BIT, 0xa4}, + {0xd141, CRL_REG_LEN_08BIT, 0x63}, + {0xd142, CRL_REG_LEN_08BIT, 0x00}, + {0xd143, CRL_REG_LEN_08BIT, 0x40}, + {0xd144, CRL_REG_LEN_08BIT, 0xbc}, + {0xd145, CRL_REG_LEN_08BIT, 0x23}, + {0xd146, CRL_REG_LEN_08BIT, 0x00}, + {0xd147, CRL_REG_LEN_08BIT, 0x00}, + {0xd148, CRL_REG_LEN_08BIT, 0x0c}, + {0xd149, CRL_REG_LEN_08BIT, 0x00}, + {0xd14a, CRL_REG_LEN_08BIT, 0x00}, + {0xd14b, CRL_REG_LEN_08BIT, 0x2a}, + {0xd14c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd14d, CRL_REG_LEN_08BIT, 0x64}, + {0xd14e, CRL_REG_LEN_08BIT, 0x6e}, + {0xd14f, CRL_REG_LEN_08BIT, 0x44}, + {0xd150, CRL_REG_LEN_08BIT, 0x19}, + {0xd151, CRL_REG_LEN_08BIT, 0x00}, + {0xd152, CRL_REG_LEN_08BIT, 0x80}, + {0xd153, CRL_REG_LEN_08BIT, 0x06}, + {0xd154, CRL_REG_LEN_08BIT, 0xa8}, + {0xd155, CRL_REG_LEN_08BIT, 0xe8}, + {0xd156, CRL_REG_LEN_08BIT, 0x3d}, + {0xd157, CRL_REG_LEN_08BIT, 0x05}, + {0xd158, CRL_REG_LEN_08BIT, 0x8c}, + {0xd159, CRL_REG_LEN_08BIT, 0x67}, + {0xd15a, CRL_REG_LEN_08BIT, 0x00}, + {0xd15b, CRL_REG_LEN_08BIT, 0x00}, + {0xd15c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd15d, CRL_REG_LEN_08BIT, 0x63}, + {0xd15e, CRL_REG_LEN_08BIT, 0x00}, + {0xd15f, CRL_REG_LEN_08BIT, 0x18}, + {0xd160, CRL_REG_LEN_08BIT, 0xb8}, + {0xd161, CRL_REG_LEN_08BIT, 0x63}, + {0xd162, CRL_REG_LEN_08BIT, 0x00}, + {0xd163, CRL_REG_LEN_08BIT, 0x98}, + {0xd164, CRL_REG_LEN_08BIT, 0xbc}, + {0xd165, CRL_REG_LEN_08BIT, 0x03}, + {0xd166, CRL_REG_LEN_08BIT, 0x00}, + {0xd167, CRL_REG_LEN_08BIT, 0x00}, + {0xd168, CRL_REG_LEN_08BIT, 0x10}, + {0xd169, CRL_REG_LEN_08BIT, 0x00}, + {0xd16a, CRL_REG_LEN_08BIT, 0x00}, + {0xd16b, CRL_REG_LEN_08BIT, 0x10}, + {0xd16c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd16d, CRL_REG_LEN_08BIT, 0x48}, + {0xd16e, CRL_REG_LEN_08BIT, 0x67}, + {0xd16f, CRL_REG_LEN_08BIT, 0x02}, + {0xd170, CRL_REG_LEN_08BIT, 0xb8}, + {0xd171, CRL_REG_LEN_08BIT, 0xa3}, + {0xd172, CRL_REG_LEN_08BIT, 0x00}, + {0xd173, CRL_REG_LEN_08BIT, 0x19}, + {0xd174, CRL_REG_LEN_08BIT, 0x8c}, + {0xd175, CRL_REG_LEN_08BIT, 0x8a}, + {0xd176, CRL_REG_LEN_08BIT, 0x00}, + {0xd177, CRL_REG_LEN_08BIT, 0x00}, + {0xd178, CRL_REG_LEN_08BIT, 0xa9}, + {0xd179, CRL_REG_LEN_08BIT, 0x68}, + {0xd17a, CRL_REG_LEN_08BIT, 0x67}, + {0xd17b, CRL_REG_LEN_08BIT, 0x03}, + {0xd17c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd17d, CRL_REG_LEN_08BIT, 0xc4}, + {0xd17e, CRL_REG_LEN_08BIT, 0x00}, + {0xd17f, CRL_REG_LEN_08BIT, 0x08}, + {0xd180, CRL_REG_LEN_08BIT, 0x8c}, + {0xd181, CRL_REG_LEN_08BIT, 0x6b}, + {0xd182, CRL_REG_LEN_08BIT, 0x00}, + {0xd183, CRL_REG_LEN_08BIT, 0x00}, + {0xd184, CRL_REG_LEN_08BIT, 0xb8}, + {0xd185, CRL_REG_LEN_08BIT, 0x85}, + {0xd186, CRL_REG_LEN_08BIT, 0x00}, + {0xd187, CRL_REG_LEN_08BIT, 0x98}, + {0xd188, CRL_REG_LEN_08BIT, 0xe0}, + {0xd189, CRL_REG_LEN_08BIT, 0x63}, + {0xd18a, CRL_REG_LEN_08BIT, 0x30}, + {0xd18b, CRL_REG_LEN_08BIT, 0x04}, + {0xd18c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd18d, CRL_REG_LEN_08BIT, 0x64}, + {0xd18e, CRL_REG_LEN_08BIT, 0x18}, + {0xd18f, CRL_REG_LEN_08BIT, 0x00}, + {0xd190, CRL_REG_LEN_08BIT, 0xa4}, + {0xd191, CRL_REG_LEN_08BIT, 0x83}, + {0xd192, CRL_REG_LEN_08BIT, 0xff}, + {0xd193, CRL_REG_LEN_08BIT, 0xff}, + {0xd194, CRL_REG_LEN_08BIT, 0xb8}, + {0xd195, CRL_REG_LEN_08BIT, 0x64}, + {0xd196, CRL_REG_LEN_08BIT, 0x00}, + {0xd197, CRL_REG_LEN_08BIT, 0x48}, + {0xd198, CRL_REG_LEN_08BIT, 0xd8}, + {0xd199, CRL_REG_LEN_08BIT, 0x0a}, + {0xd19a, CRL_REG_LEN_08BIT, 0x18}, + {0xd19b, CRL_REG_LEN_08BIT, 0x00}, + {0xd19c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd19d, CRL_REG_LEN_08BIT, 0x0b}, + {0xd19e, CRL_REG_LEN_08BIT, 0x20}, + {0xd19f, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a0, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1a1, CRL_REG_LEN_08BIT, 0x60}, + {0xd1a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1a5, CRL_REG_LEN_08BIT, 0x07}, + {0xd1a6, CRL_REG_LEN_08BIT, 0x18}, + {0xd1a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1a9, CRL_REG_LEN_08BIT, 0x68}, + {0xd1aa, CRL_REG_LEN_08BIT, 0x38}, + {0xd1ab, CRL_REG_LEN_08BIT, 0x22}, + {0xd1ac, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ad, CRL_REG_LEN_08BIT, 0x80}, + {0xd1ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd1af, CRL_REG_LEN_08BIT, 0x70}, + {0xd1b0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1b1, CRL_REG_LEN_08BIT, 0xe8}, + {0xd1b2, CRL_REG_LEN_08BIT, 0x38}, + {0xd1b3, CRL_REG_LEN_08BIT, 0x43}, + {0xd1b4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1b5, CRL_REG_LEN_08BIT, 0x03}, + {0xd1b6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1b8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1b9, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bb, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1bd, CRL_REG_LEN_08BIT, 0xc8}, + {0xd1be, CRL_REG_LEN_08BIT, 0x38}, + {0xd1bf, CRL_REG_LEN_08BIT, 0x42}, + {0xd1c0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1c1, CRL_REG_LEN_08BIT, 0x66}, + {0xd1c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1c5, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c7, CRL_REG_LEN_08BIT, 0x01}, + {0xd1c8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd1c9, CRL_REG_LEN_08BIT, 0x83}, + {0xd1ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cb, CRL_REG_LEN_08BIT, 0x08}, + {0xd1cc, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1cd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cf, CRL_REG_LEN_08BIT, 0xff}, + {0xd1d0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1d1, CRL_REG_LEN_08BIT, 0x67}, + {0xd1d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd1d5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1d6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d8, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1d9, CRL_REG_LEN_08BIT, 0x63}, + {0xd1da, CRL_REG_LEN_08BIT, 0xff}, + {0xd1db, CRL_REG_LEN_08BIT, 0xff}, + {0xd1dc, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1dd, CRL_REG_LEN_08BIT, 0x43}, + {0xd1de, CRL_REG_LEN_08BIT, 0x00}, + {0xd1df, CRL_REG_LEN_08BIT, 0x07}, + {0xd1e0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd1e1, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e3, CRL_REG_LEN_08BIT, 0x5b}, + {0xd1e4, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1e5, CRL_REG_LEN_08BIT, 0x05}, + {0xd1e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e7, CRL_REG_LEN_08BIT, 0x02}, + {0xd1e8, CRL_REG_LEN_08BIT, 0x03}, + {0xd1e9, CRL_REG_LEN_08BIT, 0xff}, + {0xd1ea, CRL_REG_LEN_08BIT, 0xff}, + {0xd1eb, CRL_REG_LEN_08BIT, 0xf6}, + {0xd1ec, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd1ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f1, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1f2, CRL_REG_LEN_08BIT, 0x55}, + {0xd1f3, CRL_REG_LEN_08BIT, 0x86}, + {0xd1f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1f5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f9, CRL_REG_LEN_08BIT, 0xc4}, + {0xd1fa, CRL_REG_LEN_08BIT, 0x6e}, + {0xd1fb, CRL_REG_LEN_08BIT, 0x45}, + {0xd1fc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1fd, CRL_REG_LEN_08BIT, 0xe4}, + {0xd1fe, CRL_REG_LEN_08BIT, 0x55}, + {0xd1ff, CRL_REG_LEN_08BIT, 0x87}, + {0xd200, CRL_REG_LEN_08BIT, 0xd8}, + {0xd201, CRL_REG_LEN_08BIT, 0x05}, + {0xd202, CRL_REG_LEN_08BIT, 0x18}, + {0xd203, CRL_REG_LEN_08BIT, 0x00}, + {0xd204, CRL_REG_LEN_08BIT, 0x8c}, + {0xd205, CRL_REG_LEN_08BIT, 0x66}, + {0xd206, CRL_REG_LEN_08BIT, 0x00}, + {0xd207, CRL_REG_LEN_08BIT, 0x00}, + {0xd208, CRL_REG_LEN_08BIT, 0xa8}, + {0xd209, CRL_REG_LEN_08BIT, 0xa4}, + {0xd20a, CRL_REG_LEN_08BIT, 0x6e}, + {0xd20b, CRL_REG_LEN_08BIT, 0x46}, + {0xd20c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd20d, CRL_REG_LEN_08BIT, 0x07}, + {0xd20e, CRL_REG_LEN_08BIT, 0x18}, + {0xd20f, CRL_REG_LEN_08BIT, 0x00}, + {0xd210, CRL_REG_LEN_08BIT, 0xa8}, + {0xd211, CRL_REG_LEN_08BIT, 0x84}, + {0xd212, CRL_REG_LEN_08BIT, 0x55}, + {0xd213, CRL_REG_LEN_08BIT, 0x88}, + {0xd214, CRL_REG_LEN_08BIT, 0x8c}, + {0xd215, CRL_REG_LEN_08BIT, 0x65}, + {0xd216, CRL_REG_LEN_08BIT, 0x00}, + {0xd217, CRL_REG_LEN_08BIT, 0x00}, + {0xd218, CRL_REG_LEN_08BIT, 0xd8}, + {0xd219, CRL_REG_LEN_08BIT, 0x04}, + {0xd21a, CRL_REG_LEN_08BIT, 0x18}, + {0xd21b, CRL_REG_LEN_08BIT, 0x00}, + {0xd21c, CRL_REG_LEN_08BIT, 0x03}, + {0xd21d, CRL_REG_LEN_08BIT, 0xff}, + {0xd21e, CRL_REG_LEN_08BIT, 0xff}, + {0xd21f, CRL_REG_LEN_08BIT, 0xce}, + {0xd220, CRL_REG_LEN_08BIT, 0x19}, + {0xd221, CRL_REG_LEN_08BIT, 0x00}, + {0xd222, CRL_REG_LEN_08BIT, 0x80}, + {0xd223, CRL_REG_LEN_08BIT, 0x06}, + {0xd224, CRL_REG_LEN_08BIT, 0x8c}, + {0xd225, CRL_REG_LEN_08BIT, 0x63}, + {0xd226, CRL_REG_LEN_08BIT, 0x00}, + {0xd227, CRL_REG_LEN_08BIT, 0x00}, + {0xd228, CRL_REG_LEN_08BIT, 0xa4}, + {0xd229, CRL_REG_LEN_08BIT, 0x63}, + {0xd22a, CRL_REG_LEN_08BIT, 0x00}, + {0xd22b, CRL_REG_LEN_08BIT, 0x40}, + {0xd22c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd22d, CRL_REG_LEN_08BIT, 0x23}, + {0xd22e, CRL_REG_LEN_08BIT, 0x00}, + {0xd22f, CRL_REG_LEN_08BIT, 0x00}, + {0xd230, CRL_REG_LEN_08BIT, 0x13}, + {0xd231, CRL_REG_LEN_08BIT, 0xff}, + {0xd232, CRL_REG_LEN_08BIT, 0xff}, + {0xd233, CRL_REG_LEN_08BIT, 0xc8}, + {0xd234, CRL_REG_LEN_08BIT, 0x9d}, + {0xd235, CRL_REG_LEN_08BIT, 0x00}, + {0xd236, CRL_REG_LEN_08BIT, 0x00}, + {0xd237, CRL_REG_LEN_08BIT, 0x40}, + {0xd238, CRL_REG_LEN_08BIT, 0xa8}, + {0xd239, CRL_REG_LEN_08BIT, 0x64}, + {0xd23a, CRL_REG_LEN_08BIT, 0x55}, + {0xd23b, CRL_REG_LEN_08BIT, 0x86}, + {0xd23c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd23d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd23e, CRL_REG_LEN_08BIT, 0x55}, + {0xd23f, CRL_REG_LEN_08BIT, 0x87}, + {0xd240, CRL_REG_LEN_08BIT, 0xd8}, + {0xd241, CRL_REG_LEN_08BIT, 0x03}, + {0xd242, CRL_REG_LEN_08BIT, 0x40}, + {0xd243, CRL_REG_LEN_08BIT, 0x00}, + {0xd244, CRL_REG_LEN_08BIT, 0xa8}, + {0xd245, CRL_REG_LEN_08BIT, 0x64}, + {0xd246, CRL_REG_LEN_08BIT, 0x55}, + {0xd247, CRL_REG_LEN_08BIT, 0x88}, + {0xd248, CRL_REG_LEN_08BIT, 0xd8}, + {0xd249, CRL_REG_LEN_08BIT, 0x05}, + {0xd24a, CRL_REG_LEN_08BIT, 0x40}, + {0xd24b, CRL_REG_LEN_08BIT, 0x00}, + {0xd24c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd24d, CRL_REG_LEN_08BIT, 0x03}, + {0xd24e, CRL_REG_LEN_08BIT, 0x40}, + {0xd24f, CRL_REG_LEN_08BIT, 0x00}, + {0xd250, CRL_REG_LEN_08BIT, 0x03}, + {0xd251, CRL_REG_LEN_08BIT, 0xff}, + {0xd252, CRL_REG_LEN_08BIT, 0xff}, + {0xd253, CRL_REG_LEN_08BIT, 0xc1}, + {0xd254, CRL_REG_LEN_08BIT, 0x19}, + {0xd255, CRL_REG_LEN_08BIT, 0x00}, + {0xd256, CRL_REG_LEN_08BIT, 0x80}, + {0xd257, CRL_REG_LEN_08BIT, 0x06}, + {0xd258, CRL_REG_LEN_08BIT, 0x94}, + {0xd259, CRL_REG_LEN_08BIT, 0x84}, + {0xd25a, CRL_REG_LEN_08BIT, 0x00}, + {0xd25b, CRL_REG_LEN_08BIT, 0x72}, + {0xd25c, CRL_REG_LEN_08BIT, 0xe5}, + {0xd25d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd25e, CRL_REG_LEN_08BIT, 0x60}, + {0xd25f, CRL_REG_LEN_08BIT, 0x00}, + {0xd260, CRL_REG_LEN_08BIT, 0x0c}, + {0xd261, CRL_REG_LEN_08BIT, 0x00}, + {0xd262, CRL_REG_LEN_08BIT, 0x00}, + {0xd263, CRL_REG_LEN_08BIT, 0x3f}, + {0xd264, CRL_REG_LEN_08BIT, 0x9d}, + {0xd265, CRL_REG_LEN_08BIT, 0x60}, + {0xd266, CRL_REG_LEN_08BIT, 0x01}, + {0xd267, CRL_REG_LEN_08BIT, 0x00}, + {0xd268, CRL_REG_LEN_08BIT, 0x85}, + {0xd269, CRL_REG_LEN_08BIT, 0x4e}, + {0xd26a, CRL_REG_LEN_08BIT, 0x00}, + {0xd26b, CRL_REG_LEN_08BIT, 0x00}, + {0xd26c, CRL_REG_LEN_08BIT, 0x98}, + {0xd26d, CRL_REG_LEN_08BIT, 0x70}, + {0xd26e, CRL_REG_LEN_08BIT, 0x00}, + {0xd26f, CRL_REG_LEN_08BIT, 0x00}, + {0xd270, CRL_REG_LEN_08BIT, 0x8c}, + {0xd271, CRL_REG_LEN_08BIT, 0x8a}, + {0xd272, CRL_REG_LEN_08BIT, 0x00}, + {0xd273, CRL_REG_LEN_08BIT, 0x6f}, + {0xd274, CRL_REG_LEN_08BIT, 0xe5}, + {0xd275, CRL_REG_LEN_08BIT, 0x63}, + {0xd276, CRL_REG_LEN_08BIT, 0x20}, + {0xd277, CRL_REG_LEN_08BIT, 0x00}, + {0xd278, CRL_REG_LEN_08BIT, 0x10}, + {0xd279, CRL_REG_LEN_08BIT, 0x00}, + {0xd27a, CRL_REG_LEN_08BIT, 0x00}, + {0xd27b, CRL_REG_LEN_08BIT, 0x07}, + {0xd27c, CRL_REG_LEN_08BIT, 0x15}, + {0xd27d, CRL_REG_LEN_08BIT, 0x00}, + {0xd27e, CRL_REG_LEN_08BIT, 0x00}, + {0xd27f, CRL_REG_LEN_08BIT, 0x00}, + {0xd280, CRL_REG_LEN_08BIT, 0x8c}, + {0xd281, CRL_REG_LEN_08BIT, 0xaa}, + {0xd282, CRL_REG_LEN_08BIT, 0x00}, + {0xd283, CRL_REG_LEN_08BIT, 0x6e}, + {0xd284, CRL_REG_LEN_08BIT, 0xe0}, + {0xd285, CRL_REG_LEN_08BIT, 0x63}, + {0xd286, CRL_REG_LEN_08BIT, 0x28}, + {0xd287, CRL_REG_LEN_08BIT, 0x02}, + {0xd288, CRL_REG_LEN_08BIT, 0xe0}, + {0xd289, CRL_REG_LEN_08BIT, 0x84}, + {0xd28a, CRL_REG_LEN_08BIT, 0x28}, + {0xd28b, CRL_REG_LEN_08BIT, 0x02}, + {0xd28c, CRL_REG_LEN_08BIT, 0x07}, + {0xd28d, CRL_REG_LEN_08BIT, 0xff}, + {0xd28e, CRL_REG_LEN_08BIT, 0xf8}, + {0xd28f, CRL_REG_LEN_08BIT, 0x66}, + {0xd290, CRL_REG_LEN_08BIT, 0xe0}, + {0xd291, CRL_REG_LEN_08BIT, 0x63}, + {0xd292, CRL_REG_LEN_08BIT, 0x5b}, + {0xd293, CRL_REG_LEN_08BIT, 0x06}, + {0xd294, CRL_REG_LEN_08BIT, 0x8c}, + {0xd295, CRL_REG_LEN_08BIT, 0x6a}, + {0xd296, CRL_REG_LEN_08BIT, 0x00}, + {0xd297, CRL_REG_LEN_08BIT, 0x77}, + {0xd298, CRL_REG_LEN_08BIT, 0xe0}, + {0xd299, CRL_REG_LEN_08BIT, 0x63}, + {0xd29a, CRL_REG_LEN_08BIT, 0x5b}, + {0xd29b, CRL_REG_LEN_08BIT, 0x06}, + {0xd29c, CRL_REG_LEN_08BIT, 0xbd}, + {0xd29d, CRL_REG_LEN_08BIT, 0x63}, + {0xd29e, CRL_REG_LEN_08BIT, 0x00}, + {0xd29f, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2a1, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a3, CRL_REG_LEN_08BIT, 0x3c}, + {0xd2a4, CRL_REG_LEN_08BIT, 0x15}, + {0xd2a5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd2a9, CRL_REG_LEN_08BIT, 0x8a}, + {0xd2aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ab, CRL_REG_LEN_08BIT, 0x78}, + {0xd2ac, CRL_REG_LEN_08BIT, 0xb8}, + {0xd2ad, CRL_REG_LEN_08BIT, 0x63}, + {0xd2ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd2af, CRL_REG_LEN_08BIT, 0x88}, + {0xd2b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd2b1, CRL_REG_LEN_08BIT, 0x64}, + {0xd2b2, CRL_REG_LEN_08BIT, 0x5b}, + {0xd2b3, CRL_REG_LEN_08BIT, 0x06}, + {0xd2b4, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2b5, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b8, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2b9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd2bb, CRL_REG_LEN_08BIT, 0x34}, + {0xd2bc, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2bd, CRL_REG_LEN_08BIT, 0x01}, + {0xd2be, CRL_REG_LEN_08BIT, 0x18}, + {0xd2bf, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c0, CRL_REG_LEN_08BIT, 0xb9}, + {0xd2c1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c3, CRL_REG_LEN_08BIT, 0x88}, + {0xd2c4, CRL_REG_LEN_08BIT, 0x85}, + {0xd2c5, CRL_REG_LEN_08BIT, 0x01}, + {0xd2c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2c9, CRL_REG_LEN_08BIT, 0x68}, + {0xd2ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cc, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2cd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cf, CRL_REG_LEN_08BIT, 0x2c}, + {0xd2d0, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2d1, CRL_REG_LEN_08BIT, 0x01}, + {0xd2d2, CRL_REG_LEN_08BIT, 0x58}, + {0xd2d3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2d4, CRL_REG_LEN_08BIT, 0x84}, + {0xd2d5, CRL_REG_LEN_08BIT, 0x81}, + {0xd2d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2d7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2d8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2d9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd2da, CRL_REG_LEN_08BIT, 0x01}, + {0xd2db, CRL_REG_LEN_08BIT, 0x00}, + {0xd2dc, CRL_REG_LEN_08BIT, 0x10}, + {0xd2dd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2de, CRL_REG_LEN_08BIT, 0x00}, + {0xd2df, CRL_REG_LEN_08BIT, 0x05}, + {0xd2e0, CRL_REG_LEN_08BIT, 0x84}, + {0xd2e1, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2e4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd2e5, CRL_REG_LEN_08BIT, 0xa0}, + {0xd2e6, CRL_REG_LEN_08BIT, 0x01}, + {0xd2e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e8, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xd2ea, CRL_REG_LEN_08BIT, 0x28}, + {0xd2eb, CRL_REG_LEN_08BIT, 0x14}, + {0xd2ec, CRL_REG_LEN_08BIT, 0x84}, + {0xd2ed, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ef, CRL_REG_LEN_08BIT, 0x18}, + {0xd2f0, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2f1, CRL_REG_LEN_08BIT, 0x66}, + {0xd2f2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f4, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f7, CRL_REG_LEN_08BIT, 0x20}, + {0xd2f8, CRL_REG_LEN_08BIT, 0x9d}, + {0xd2f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fc, CRL_REG_LEN_08BIT, 0x84}, + {0xd2fd, CRL_REG_LEN_08BIT, 0x61}, + {0xd2fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ff, CRL_REG_LEN_08BIT, 0x18}, + {0xd300, CRL_REG_LEN_08BIT, 0xbd}, + {0xd301, CRL_REG_LEN_08BIT, 0xa3}, + {0xd302, CRL_REG_LEN_08BIT, 0x01}, + {0xd303, CRL_REG_LEN_08BIT, 0x00}, + {0xd304, CRL_REG_LEN_08BIT, 0x10}, + {0xd305, CRL_REG_LEN_08BIT, 0x00}, + {0xd306, CRL_REG_LEN_08BIT, 0x00}, + {0xd307, CRL_REG_LEN_08BIT, 0x03}, + {0xd308, CRL_REG_LEN_08BIT, 0x9c}, + {0xd309, CRL_REG_LEN_08BIT, 0x80}, + {0xd30a, CRL_REG_LEN_08BIT, 0x01}, + {0xd30b, CRL_REG_LEN_08BIT, 0x00}, + {0xd30c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd30d, CRL_REG_LEN_08BIT, 0x01}, + {0xd30e, CRL_REG_LEN_08BIT, 0x20}, + {0xd30f, CRL_REG_LEN_08BIT, 0x18}, + {0xd310, CRL_REG_LEN_08BIT, 0x18}, + {0xd311, CRL_REG_LEN_08BIT, 0x60}, + {0xd312, CRL_REG_LEN_08BIT, 0x80}, + {0xd313, CRL_REG_LEN_08BIT, 0x06}, + {0xd314, CRL_REG_LEN_08BIT, 0x85}, + {0xd315, CRL_REG_LEN_08BIT, 0x01}, + {0xd316, CRL_REG_LEN_08BIT, 0x00}, + {0xd317, CRL_REG_LEN_08BIT, 0x14}, + {0xd318, CRL_REG_LEN_08BIT, 0xa8}, + {0xd319, CRL_REG_LEN_08BIT, 0x83}, + {0xd31a, CRL_REG_LEN_08BIT, 0x38}, + {0xd31b, CRL_REG_LEN_08BIT, 0x29}, + {0xd31c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd31d, CRL_REG_LEN_08BIT, 0xc3}, + {0xd31e, CRL_REG_LEN_08BIT, 0x40}, + {0xd31f, CRL_REG_LEN_08BIT, 0x08}, + {0xd320, CRL_REG_LEN_08BIT, 0x8c}, + {0xd321, CRL_REG_LEN_08BIT, 0x84}, + {0xd322, CRL_REG_LEN_08BIT, 0x00}, + {0xd323, CRL_REG_LEN_08BIT, 0x00}, + {0xd324, CRL_REG_LEN_08BIT, 0xa8}, + {0xd325, CRL_REG_LEN_08BIT, 0xa3}, + {0xd326, CRL_REG_LEN_08BIT, 0x38}, + {0xd327, CRL_REG_LEN_08BIT, 0x2a}, + {0xd328, CRL_REG_LEN_08BIT, 0xa8}, + {0xd329, CRL_REG_LEN_08BIT, 0xe3}, + {0xd32a, CRL_REG_LEN_08BIT, 0x40}, + {0xd32b, CRL_REG_LEN_08BIT, 0x09}, + {0xd32c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd32d, CRL_REG_LEN_08BIT, 0x64}, + {0xd32e, CRL_REG_LEN_08BIT, 0x40}, + {0xd32f, CRL_REG_LEN_08BIT, 0x00}, + {0xd330, CRL_REG_LEN_08BIT, 0xd8}, + {0xd331, CRL_REG_LEN_08BIT, 0x06}, + {0xd332, CRL_REG_LEN_08BIT, 0x18}, + {0xd333, CRL_REG_LEN_08BIT, 0x00}, + {0xd334, CRL_REG_LEN_08BIT, 0x8c}, + {0xd335, CRL_REG_LEN_08BIT, 0x65}, + {0xd336, CRL_REG_LEN_08BIT, 0x00}, + {0xd337, CRL_REG_LEN_08BIT, 0x00}, + {0xd338, CRL_REG_LEN_08BIT, 0x84}, + {0xd339, CRL_REG_LEN_08BIT, 0x81}, + {0xd33a, CRL_REG_LEN_08BIT, 0x00}, + {0xd33b, CRL_REG_LEN_08BIT, 0x18}, + {0xd33c, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33d, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33e, CRL_REG_LEN_08BIT, 0x20}, + {0xd33f, CRL_REG_LEN_08BIT, 0x00}, + {0xd340, CRL_REG_LEN_08BIT, 0xd8}, + {0xd341, CRL_REG_LEN_08BIT, 0x07}, + {0xd342, CRL_REG_LEN_08BIT, 0xf8}, + {0xd343, CRL_REG_LEN_08BIT, 0x00}, + {0xd344, CRL_REG_LEN_08BIT, 0x03}, + {0xd345, CRL_REG_LEN_08BIT, 0xff}, + {0xd346, CRL_REG_LEN_08BIT, 0xff}, + {0xd347, CRL_REG_LEN_08BIT, 0x6f}, + {0xd348, CRL_REG_LEN_08BIT, 0x18}, + {0xd349, CRL_REG_LEN_08BIT, 0x60}, + {0xd34a, CRL_REG_LEN_08BIT, 0x00}, + {0xd34b, CRL_REG_LEN_08BIT, 0x01}, + {0xd34c, CRL_REG_LEN_08BIT, 0x0f}, + {0xd34d, CRL_REG_LEN_08BIT, 0xff}, + {0xd34e, CRL_REG_LEN_08BIT, 0xff}, + {0xd34f, CRL_REG_LEN_08BIT, 0x9d}, + {0xd350, CRL_REG_LEN_08BIT, 0x18}, + {0xd351, CRL_REG_LEN_08BIT, 0x60}, + {0xd352, CRL_REG_LEN_08BIT, 0x80}, + {0xd353, CRL_REG_LEN_08BIT, 0x06}, + {0xd354, CRL_REG_LEN_08BIT, 0x00}, + {0xd355, CRL_REG_LEN_08BIT, 0x00}, + {0xd356, CRL_REG_LEN_08BIT, 0x00}, + {0xd357, CRL_REG_LEN_08BIT, 0x11}, + {0xd358, CRL_REG_LEN_08BIT, 0xa8}, + {0xd359, CRL_REG_LEN_08BIT, 0x83}, + {0xd35a, CRL_REG_LEN_08BIT, 0x6e}, + {0xd35b, CRL_REG_LEN_08BIT, 0x43}, + {0xd35c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd35d, CRL_REG_LEN_08BIT, 0x6c}, + {0xd35e, CRL_REG_LEN_08BIT, 0x28}, + {0xd35f, CRL_REG_LEN_08BIT, 0x02}, + {0xd360, CRL_REG_LEN_08BIT, 0xe0}, + {0xd361, CRL_REG_LEN_08BIT, 0x84}, + {0xd362, CRL_REG_LEN_08BIT, 0x28}, + {0xd363, CRL_REG_LEN_08BIT, 0x02}, + {0xd364, CRL_REG_LEN_08BIT, 0x07}, + {0xd365, CRL_REG_LEN_08BIT, 0xff}, + {0xd366, CRL_REG_LEN_08BIT, 0xf8}, + {0xd367, CRL_REG_LEN_08BIT, 0x30}, + {0xd368, CRL_REG_LEN_08BIT, 0xb8}, + {0xd369, CRL_REG_LEN_08BIT, 0x63}, + {0xd36a, CRL_REG_LEN_08BIT, 0x00}, + {0xd36b, CRL_REG_LEN_08BIT, 0x08}, + {0xd36c, CRL_REG_LEN_08BIT, 0x03}, + {0xd36d, CRL_REG_LEN_08BIT, 0xff}, + {0xd36e, CRL_REG_LEN_08BIT, 0xff}, + {0xd36f, CRL_REG_LEN_08BIT, 0xc0}, + {0xd370, CRL_REG_LEN_08BIT, 0x85}, + {0xd371, CRL_REG_LEN_08BIT, 0x4e}, + {0xd372, CRL_REG_LEN_08BIT, 0x00}, + {0xd373, CRL_REG_LEN_08BIT, 0x00}, + {0xd374, CRL_REG_LEN_08BIT, 0x03}, + {0xd375, CRL_REG_LEN_08BIT, 0xff}, + {0xd376, CRL_REG_LEN_08BIT, 0xff}, + {0xd377, CRL_REG_LEN_08BIT, 0xe7}, + {0xd378, CRL_REG_LEN_08BIT, 0xd4}, + {0xd379, CRL_REG_LEN_08BIT, 0x01}, + {0xd37a, CRL_REG_LEN_08BIT, 0x40}, + {0xd37b, CRL_REG_LEN_08BIT, 0x18}, + {0xd37c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd37d, CRL_REG_LEN_08BIT, 0x60}, + {0xd37e, CRL_REG_LEN_08BIT, 0x00}, + {0xd37f, CRL_REG_LEN_08BIT, 0x00}, + {0xd380, CRL_REG_LEN_08BIT, 0x03}, + {0xd381, CRL_REG_LEN_08BIT, 0xff}, + {0xd382, CRL_REG_LEN_08BIT, 0xff}, + {0xd383, CRL_REG_LEN_08BIT, 0xdb}, + {0xd384, CRL_REG_LEN_08BIT, 0xd4}, + {0xd385, CRL_REG_LEN_08BIT, 0x01}, + {0xd386, CRL_REG_LEN_08BIT, 0x18}, + {0xd387, CRL_REG_LEN_08BIT, 0x14}, + {0xd388, CRL_REG_LEN_08BIT, 0x03}, + {0xd389, CRL_REG_LEN_08BIT, 0xff}, + {0xd38a, CRL_REG_LEN_08BIT, 0xff}, + {0xd38b, CRL_REG_LEN_08BIT, 0xce}, + {0xd38c, CRL_REG_LEN_08BIT, 0x9d}, + {0xd38d, CRL_REG_LEN_08BIT, 0x6b}, + {0xd38e, CRL_REG_LEN_08BIT, 0x00}, + {0xd38f, CRL_REG_LEN_08BIT, 0xff}, + {0xd390, CRL_REG_LEN_08BIT, 0x03}, + {0xd391, CRL_REG_LEN_08BIT, 0xff}, + {0xd392, CRL_REG_LEN_08BIT, 0xff}, + {0xd393, CRL_REG_LEN_08BIT, 0xc6}, + {0xd394, CRL_REG_LEN_08BIT, 0x9c}, + {0xd395, CRL_REG_LEN_08BIT, 0x63}, + {0xd396, CRL_REG_LEN_08BIT, 0x00}, + {0xd397, CRL_REG_LEN_08BIT, 0xff}, + {0xd398, CRL_REG_LEN_08BIT, 0xa8}, + {0xd399, CRL_REG_LEN_08BIT, 0xe3}, + {0xd39a, CRL_REG_LEN_08BIT, 0x38}, + {0xd39b, CRL_REG_LEN_08BIT, 0x0f}, + {0xd39c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd39d, CRL_REG_LEN_08BIT, 0x84}, + {0xd39e, CRL_REG_LEN_08BIT, 0x00}, + {0xd39f, CRL_REG_LEN_08BIT, 0x00}, + {0xd3a0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3a1, CRL_REG_LEN_08BIT, 0xa3}, + {0xd3a2, CRL_REG_LEN_08BIT, 0x38}, + {0xd3a3, CRL_REG_LEN_08BIT, 0x0e}, + {0xd3a4, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3a5, CRL_REG_LEN_08BIT, 0xc3}, + {0xd3a6, CRL_REG_LEN_08BIT, 0x6e}, + {0xd3a7, CRL_REG_LEN_08BIT, 0x42}, + {0xd3a8, CRL_REG_LEN_08BIT, 0xd8}, + {0xd3a9, CRL_REG_LEN_08BIT, 0x07}, + {0xd3aa, CRL_REG_LEN_08BIT, 0x20}, + {0xd3ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ac, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3ad, CRL_REG_LEN_08BIT, 0x66}, + {0xd3ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd3af, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd3b1, CRL_REG_LEN_08BIT, 0x05}, + {0xd3b2, CRL_REG_LEN_08BIT, 0x18}, + {0xd3b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b4, CRL_REG_LEN_08BIT, 0x85}, + {0xd3b5, CRL_REG_LEN_08BIT, 0x21}, + {0xd3b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b8, CRL_REG_LEN_08BIT, 0x85}, + {0xd3b9, CRL_REG_LEN_08BIT, 0x41}, + {0xd3ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd3bb, CRL_REG_LEN_08BIT, 0x04}, + {0xd3bc, CRL_REG_LEN_08BIT, 0x85}, + {0xd3bd, CRL_REG_LEN_08BIT, 0x81}, + {0xd3be, CRL_REG_LEN_08BIT, 0x00}, + {0xd3bf, CRL_REG_LEN_08BIT, 0x08}, + {0xd3c0, CRL_REG_LEN_08BIT, 0x85}, + {0xd3c1, CRL_REG_LEN_08BIT, 0xc1}, + {0xd3c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd3c3, CRL_REG_LEN_08BIT, 0x0c}, + {0xd3c4, CRL_REG_LEN_08BIT, 0x86}, + {0xd3c5, CRL_REG_LEN_08BIT, 0x01}, + {0xd3c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3c7, CRL_REG_LEN_08BIT, 0x10}, + {0xd3c8, CRL_REG_LEN_08BIT, 0x44}, + {0xd3c9, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ca, CRL_REG_LEN_08BIT, 0x48}, + {0xd3cb, CRL_REG_LEN_08BIT, 0x00}, + {0xd3cc, CRL_REG_LEN_08BIT, 0x9c}, + {0xd3cd, CRL_REG_LEN_08BIT, 0x21}, + {0xd3ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd3cf, CRL_REG_LEN_08BIT, 0x1c}, + {0xd3d0, CRL_REG_LEN_08BIT, 0x9c}, + {0xd3d1, CRL_REG_LEN_08BIT, 0x21}, + {0xd3d2, CRL_REG_LEN_08BIT, 0xff}, + {0xd3d3, CRL_REG_LEN_08BIT, 0xfc}, + {0xd3d4, CRL_REG_LEN_08BIT, 0xd4}, + {0xd3d5, CRL_REG_LEN_08BIT, 0x01}, + {0xd3d6, CRL_REG_LEN_08BIT, 0x48}, + {0xd3d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3d8, CRL_REG_LEN_08BIT, 0x18}, + {0xd3d9, CRL_REG_LEN_08BIT, 0x60}, + {0xd3da, CRL_REG_LEN_08BIT, 0x00}, + {0xd3db, CRL_REG_LEN_08BIT, 0x01}, + {0xd3dc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3dd, CRL_REG_LEN_08BIT, 0x63}, + {0xd3de, CRL_REG_LEN_08BIT, 0x07}, + {0xd3df, CRL_REG_LEN_08BIT, 0x80}, + {0xd3e0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3e1, CRL_REG_LEN_08BIT, 0x63}, + {0xd3e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e3, CRL_REG_LEN_08BIT, 0x68}, + {0xd3e4, CRL_REG_LEN_08BIT, 0xbc}, + {0xd3e5, CRL_REG_LEN_08BIT, 0x03}, + {0xd3e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e8, CRL_REG_LEN_08BIT, 0x10}, + {0xd3e9, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd3eb, CRL_REG_LEN_08BIT, 0x0c}, + {0xd3ec, CRL_REG_LEN_08BIT, 0x15}, + {0xd3ed, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f0, CRL_REG_LEN_08BIT, 0x07}, + {0xd3f1, CRL_REG_LEN_08BIT, 0xff}, + {0xd3f2, CRL_REG_LEN_08BIT, 0xd9}, + {0xd3f3, CRL_REG_LEN_08BIT, 0x98}, + {0xd3f4, CRL_REG_LEN_08BIT, 0x15}, + {0xd3f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f8, CRL_REG_LEN_08BIT, 0x18}, + {0xd3f9, CRL_REG_LEN_08BIT, 0x60}, + {0xd3fa, CRL_REG_LEN_08BIT, 0x80}, + {0xd3fb, CRL_REG_LEN_08BIT, 0x06}, + {0xd3fc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3fd, CRL_REG_LEN_08BIT, 0x63}, + {0xd3fe, CRL_REG_LEN_08BIT, 0xc4}, + {0xd3ff, CRL_REG_LEN_08BIT, 0xb8}, + {0xd400, CRL_REG_LEN_08BIT, 0x8c}, + {0xd401, CRL_REG_LEN_08BIT, 0x63}, + {0xd402, CRL_REG_LEN_08BIT, 0x00}, + {0xd403, CRL_REG_LEN_08BIT, 0x00}, + {0xd404, CRL_REG_LEN_08BIT, 0xbc}, + {0xd405, CRL_REG_LEN_08BIT, 0x23}, + {0xd406, CRL_REG_LEN_08BIT, 0x00}, + {0xd407, CRL_REG_LEN_08BIT, 0x01}, + {0xd408, CRL_REG_LEN_08BIT, 0x10}, + {0xd409, CRL_REG_LEN_08BIT, 0x00}, + {0xd40a, CRL_REG_LEN_08BIT, 0x00}, + {0xd40b, CRL_REG_LEN_08BIT, 0x25}, + {0xd40c, CRL_REG_LEN_08BIT, 0x9d}, + {0xd40d, CRL_REG_LEN_08BIT, 0x00}, + {0xd40e, CRL_REG_LEN_08BIT, 0x00}, + {0xd40f, CRL_REG_LEN_08BIT, 0x00}, + {0xd410, CRL_REG_LEN_08BIT, 0x00}, + {0xd411, CRL_REG_LEN_08BIT, 0x00}, + {0xd412, CRL_REG_LEN_08BIT, 0x00}, + {0xd413, CRL_REG_LEN_08BIT, 0x0b}, + {0xd414, CRL_REG_LEN_08BIT, 0xb8}, + {0xd415, CRL_REG_LEN_08BIT, 0xe8}, + {0xd416, CRL_REG_LEN_08BIT, 0x00}, + {0xd417, CRL_REG_LEN_08BIT, 0x02}, + {0xd418, CRL_REG_LEN_08BIT, 0x07}, + {0xd419, CRL_REG_LEN_08BIT, 0xff}, + {0xd41a, CRL_REG_LEN_08BIT, 0xd6}, + {0xd41b, CRL_REG_LEN_08BIT, 0x24}, + {0xd41c, CRL_REG_LEN_08BIT, 0x15}, + {0xd41d, CRL_REG_LEN_08BIT, 0x00}, + {0xd41e, CRL_REG_LEN_08BIT, 0x00}, + {0xd41f, CRL_REG_LEN_08BIT, 0x00}, + {0xd420, CRL_REG_LEN_08BIT, 0x18}, + {0xd421, CRL_REG_LEN_08BIT, 0x60}, + {0xd422, CRL_REG_LEN_08BIT, 0x80}, + {0xd423, CRL_REG_LEN_08BIT, 0x06}, + {0xd424, CRL_REG_LEN_08BIT, 0xa8}, + {0xd425, CRL_REG_LEN_08BIT, 0x63}, + {0xd426, CRL_REG_LEN_08BIT, 0xc4}, + {0xd427, CRL_REG_LEN_08BIT, 0xb8}, + {0xd428, CRL_REG_LEN_08BIT, 0x8c}, + {0xd429, CRL_REG_LEN_08BIT, 0x63}, + {0xd42a, CRL_REG_LEN_08BIT, 0x00}, + {0xd42b, CRL_REG_LEN_08BIT, 0x00}, + {0xd42c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd42d, CRL_REG_LEN_08BIT, 0x23}, + {0xd42e, CRL_REG_LEN_08BIT, 0x00}, + {0xd42f, CRL_REG_LEN_08BIT, 0x01}, + {0xd430, CRL_REG_LEN_08BIT, 0x10}, + {0xd431, CRL_REG_LEN_08BIT, 0x00}, + {0xd432, CRL_REG_LEN_08BIT, 0x00}, + {0xd433, CRL_REG_LEN_08BIT, 0x1b}, + {0xd434, CRL_REG_LEN_08BIT, 0x9d}, + {0xd435, CRL_REG_LEN_08BIT, 0x00}, + {0xd436, CRL_REG_LEN_08BIT, 0x00}, + {0xd437, CRL_REG_LEN_08BIT, 0x00}, + {0xd438, CRL_REG_LEN_08BIT, 0xb8}, + {0xd439, CRL_REG_LEN_08BIT, 0xe8}, + {0xd43a, CRL_REG_LEN_08BIT, 0x00}, + {0xd43b, CRL_REG_LEN_08BIT, 0x02}, + {0xd43c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd43d, CRL_REG_LEN_08BIT, 0xc0}, + {0xd43e, CRL_REG_LEN_08BIT, 0x00}, + {0xd43f, CRL_REG_LEN_08BIT, 0x00}, + {0xd440, CRL_REG_LEN_08BIT, 0x18}, + {0xd441, CRL_REG_LEN_08BIT, 0xa0}, + {0xd442, CRL_REG_LEN_08BIT, 0x80}, + {0xd443, CRL_REG_LEN_08BIT, 0x06}, + {0xd444, CRL_REG_LEN_08BIT, 0xe0}, + {0xd445, CRL_REG_LEN_08BIT, 0x67}, + {0xd446, CRL_REG_LEN_08BIT, 0x30}, + {0xd447, CRL_REG_LEN_08BIT, 0x00}, + {0xd448, CRL_REG_LEN_08BIT, 0xa8}, + {0xd449, CRL_REG_LEN_08BIT, 0xa5}, + {0xd44a, CRL_REG_LEN_08BIT, 0xce}, + {0xd44b, CRL_REG_LEN_08BIT, 0xb0}, + {0xd44c, CRL_REG_LEN_08BIT, 0x19}, + {0xd44d, CRL_REG_LEN_08BIT, 0x60}, + {0xd44e, CRL_REG_LEN_08BIT, 0x00}, + {0xd44f, CRL_REG_LEN_08BIT, 0x01}, + {0xd450, CRL_REG_LEN_08BIT, 0xa9}, + {0xd451, CRL_REG_LEN_08BIT, 0x6b}, + {0xd452, CRL_REG_LEN_08BIT, 0x06}, + {0xd453, CRL_REG_LEN_08BIT, 0x14}, + {0xd454, CRL_REG_LEN_08BIT, 0xe0}, + {0xd455, CRL_REG_LEN_08BIT, 0x83}, + {0xd456, CRL_REG_LEN_08BIT, 0x28}, + {0xd457, CRL_REG_LEN_08BIT, 0x00}, + {0xd458, CRL_REG_LEN_08BIT, 0x9c}, + {0xd459, CRL_REG_LEN_08BIT, 0xc6}, + {0xd45a, CRL_REG_LEN_08BIT, 0x00}, + {0xd45b, CRL_REG_LEN_08BIT, 0x01}, + {0xd45c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd45d, CRL_REG_LEN_08BIT, 0x63}, + {0xd45e, CRL_REG_LEN_08BIT, 0x18}, + {0xd45f, CRL_REG_LEN_08BIT, 0x00}, + {0xd460, CRL_REG_LEN_08BIT, 0x8c}, + {0xd461, CRL_REG_LEN_08BIT, 0x84}, + {0xd462, CRL_REG_LEN_08BIT, 0x00}, + {0xd463, CRL_REG_LEN_08BIT, 0x00}, + {0xd464, CRL_REG_LEN_08BIT, 0xe0}, + {0xd465, CRL_REG_LEN_08BIT, 0xa3}, + {0xd466, CRL_REG_LEN_08BIT, 0x58}, + {0xd467, CRL_REG_LEN_08BIT, 0x00}, + {0xd468, CRL_REG_LEN_08BIT, 0xa4}, + {0xd469, CRL_REG_LEN_08BIT, 0xc6}, + {0xd46a, CRL_REG_LEN_08BIT, 0x00}, + {0xd46b, CRL_REG_LEN_08BIT, 0xff}, + {0xd46c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd46d, CRL_REG_LEN_08BIT, 0x64}, + {0xd46e, CRL_REG_LEN_08BIT, 0x00}, + {0xd46f, CRL_REG_LEN_08BIT, 0x18}, + {0xd470, CRL_REG_LEN_08BIT, 0xbc}, + {0xd471, CRL_REG_LEN_08BIT, 0x46}, + {0xd472, CRL_REG_LEN_08BIT, 0x00}, + {0xd473, CRL_REG_LEN_08BIT, 0x03}, + {0xd474, CRL_REG_LEN_08BIT, 0x94}, + {0xd475, CRL_REG_LEN_08BIT, 0x85}, + {0xd476, CRL_REG_LEN_08BIT, 0x00}, + {0xd477, CRL_REG_LEN_08BIT, 0x00}, + {0xd478, CRL_REG_LEN_08BIT, 0xb8}, + {0xd479, CRL_REG_LEN_08BIT, 0x63}, + {0xd47a, CRL_REG_LEN_08BIT, 0x00}, + {0xd47b, CRL_REG_LEN_08BIT, 0x98}, + {0xd47c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd47d, CRL_REG_LEN_08BIT, 0x64}, + {0xd47e, CRL_REG_LEN_08BIT, 0x18}, + {0xd47f, CRL_REG_LEN_08BIT, 0x00}, + {0xd480, CRL_REG_LEN_08BIT, 0x0f}, + {0xd481, CRL_REG_LEN_08BIT, 0xff}, + {0xd482, CRL_REG_LEN_08BIT, 0xff}, + {0xd483, CRL_REG_LEN_08BIT, 0xf0}, + {0xd484, CRL_REG_LEN_08BIT, 0xdc}, + {0xd485, CRL_REG_LEN_08BIT, 0x05}, + {0xd486, CRL_REG_LEN_08BIT, 0x18}, + {0xd487, CRL_REG_LEN_08BIT, 0x00}, + {0xd488, CRL_REG_LEN_08BIT, 0x9c}, + {0xd489, CRL_REG_LEN_08BIT, 0x68}, + {0xd48a, CRL_REG_LEN_08BIT, 0x00}, + {0xd48b, CRL_REG_LEN_08BIT, 0x01}, + {0xd48c, CRL_REG_LEN_08BIT, 0xa5}, + {0xd48d, CRL_REG_LEN_08BIT, 0x03}, + {0xd48e, CRL_REG_LEN_08BIT, 0x00}, + {0xd48f, CRL_REG_LEN_08BIT, 0xff}, + {0xd490, CRL_REG_LEN_08BIT, 0xbc}, + {0xd491, CRL_REG_LEN_08BIT, 0x48}, + {0xd492, CRL_REG_LEN_08BIT, 0x00}, + {0xd493, CRL_REG_LEN_08BIT, 0x01}, + {0xd494, CRL_REG_LEN_08BIT, 0x0f}, + {0xd495, CRL_REG_LEN_08BIT, 0xff}, + {0xd496, CRL_REG_LEN_08BIT, 0xff}, + {0xd497, CRL_REG_LEN_08BIT, 0xea}, + {0xd498, CRL_REG_LEN_08BIT, 0xb8}, + {0xd499, CRL_REG_LEN_08BIT, 0xe8}, + {0xd49a, CRL_REG_LEN_08BIT, 0x00}, + {0xd49b, CRL_REG_LEN_08BIT, 0x02}, + {0xd49c, CRL_REG_LEN_08BIT, 0x18}, + {0xd49d, CRL_REG_LEN_08BIT, 0x60}, + {0xd49e, CRL_REG_LEN_08BIT, 0x00}, + {0xd49f, CRL_REG_LEN_08BIT, 0x01}, + {0xd4a0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4a1, CRL_REG_LEN_08BIT, 0x63}, + {0xd4a2, CRL_REG_LEN_08BIT, 0x06}, + {0xd4a3, CRL_REG_LEN_08BIT, 0x14}, + {0xd4a4, CRL_REG_LEN_08BIT, 0x07}, + {0xd4a5, CRL_REG_LEN_08BIT, 0xff}, + {0xd4a6, CRL_REG_LEN_08BIT, 0xe4}, + {0xd4a7, CRL_REG_LEN_08BIT, 0x05}, + {0xd4a8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4a9, CRL_REG_LEN_08BIT, 0x83}, + {0xd4aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ab, CRL_REG_LEN_08BIT, 0x10}, + {0xd4ac, CRL_REG_LEN_08BIT, 0x85}, + {0xd4ad, CRL_REG_LEN_08BIT, 0x21}, + {0xd4ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd4af, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b0, CRL_REG_LEN_08BIT, 0x44}, + {0xd4b1, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b2, CRL_REG_LEN_08BIT, 0x48}, + {0xd4b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4b5, CRL_REG_LEN_08BIT, 0x21}, + {0xd4b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b7, CRL_REG_LEN_08BIT, 0x04}, + {0xd4b8, CRL_REG_LEN_08BIT, 0x18}, + {0xd4b9, CRL_REG_LEN_08BIT, 0x60}, + {0xd4ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd4bb, CRL_REG_LEN_08BIT, 0x01}, + {0xd4bc, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4bd, CRL_REG_LEN_08BIT, 0x80}, + {0xd4be, CRL_REG_LEN_08BIT, 0xff}, + {0xd4bf, CRL_REG_LEN_08BIT, 0xff}, + {0xd4c0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4c1, CRL_REG_LEN_08BIT, 0x63}, + {0xd4c2, CRL_REG_LEN_08BIT, 0x09}, + {0xd4c3, CRL_REG_LEN_08BIT, 0xef}, + {0xd4c4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd4c5, CRL_REG_LEN_08BIT, 0x03}, + {0xd4c6, CRL_REG_LEN_08BIT, 0x20}, + {0xd4c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4c8, CRL_REG_LEN_08BIT, 0x18}, + {0xd4c9, CRL_REG_LEN_08BIT, 0x60}, + {0xd4ca, CRL_REG_LEN_08BIT, 0x80}, + {0xd4cb, CRL_REG_LEN_08BIT, 0x06}, + {0xd4cc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4cd, CRL_REG_LEN_08BIT, 0x63}, + {0xd4ce, CRL_REG_LEN_08BIT, 0xc9}, + {0xd4cf, CRL_REG_LEN_08BIT, 0xef}, + {0xd4d0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd4d1, CRL_REG_LEN_08BIT, 0x03}, + {0xd4d2, CRL_REG_LEN_08BIT, 0x20}, + {0xd4d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d4, CRL_REG_LEN_08BIT, 0x44}, + {0xd4d5, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d6, CRL_REG_LEN_08BIT, 0x48}, + {0xd4d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d8, CRL_REG_LEN_08BIT, 0x15}, + {0xd4d9, CRL_REG_LEN_08BIT, 0x00}, + {0xd4da, CRL_REG_LEN_08BIT, 0x00}, + {0xd4db, CRL_REG_LEN_08BIT, 0x00}, + {0xd4dc, CRL_REG_LEN_08BIT, 0x18}, + {0xd4dd, CRL_REG_LEN_08BIT, 0x80}, + {0xd4de, CRL_REG_LEN_08BIT, 0x00}, + {0xd4df, CRL_REG_LEN_08BIT, 0x01}, + {0xd4e0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4e1, CRL_REG_LEN_08BIT, 0x84}, + {0xd4e2, CRL_REG_LEN_08BIT, 0x0a}, + {0xd4e3, CRL_REG_LEN_08BIT, 0x12}, + {0xd4e4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd4e5, CRL_REG_LEN_08BIT, 0x64}, + {0xd4e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e8, CRL_REG_LEN_08BIT, 0xbc}, + {0xd4e9, CRL_REG_LEN_08BIT, 0x03}, + {0xd4ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd4eb, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ec, CRL_REG_LEN_08BIT, 0x13}, + {0xd4ed, CRL_REG_LEN_08BIT, 0xff}, + {0xd4ee, CRL_REG_LEN_08BIT, 0xff}, + {0xd4ef, CRL_REG_LEN_08BIT, 0xfe}, + {0xd4f0, CRL_REG_LEN_08BIT, 0x15}, + {0xd4f1, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f2, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f4, CRL_REG_LEN_08BIT, 0x44}, + {0xd4f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f6, CRL_REG_LEN_08BIT, 0x48}, + {0xd4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f8, CRL_REG_LEN_08BIT, 0x15}, + {0xd4f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fc, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fd, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ff, CRL_REG_LEN_08BIT, 0x00}, + {0xd500, CRL_REG_LEN_08BIT, 0x00}, + {0xd501, CRL_REG_LEN_08BIT, 0x00}, + {0xd502, CRL_REG_LEN_08BIT, 0x00}, + {0xd503, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0e, CRL_REG_LEN_08BIT, 0x33}, + {0x6f0f, CRL_REG_LEN_08BIT, 0x33}, + {0x460e, CRL_REG_LEN_08BIT, 0x08}, + {0x460f, CRL_REG_LEN_08BIT, 0x01}, + {0x4610, CRL_REG_LEN_08BIT, 0x00}, + {0x4611, CRL_REG_LEN_08BIT, 0x01}, + {0x4612, CRL_REG_LEN_08BIT, 0x00}, + {0x4613, CRL_REG_LEN_08BIT, 0x01}, + {0x4605, CRL_REG_LEN_08BIT, 0x08}, + {0x4608, CRL_REG_LEN_08BIT, 0x00}, + {0x4609, CRL_REG_LEN_08BIT, 0x08}, + {0x6804, CRL_REG_LEN_08BIT, 0x00}, + {0x6805, CRL_REG_LEN_08BIT, 0x06}, + {0x6806, CRL_REG_LEN_08BIT, 0x00}, + {0x5120, CRL_REG_LEN_08BIT, 0x00}, + {0x3510, CRL_REG_LEN_08BIT, 0x00}, + {0x3504, CRL_REG_LEN_08BIT, 0x00}, + {0x6800, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0d, CRL_REG_LEN_08BIT, 0x0f}, + {0x5000, CRL_REG_LEN_08BIT, 0xff}, + {0x5001, CRL_REG_LEN_08BIT, 0xbf}, + {0x5002, CRL_REG_LEN_08BIT, 0x7e}, + {0x5003, CRL_REG_LEN_08BIT, 0x0c}, + {0x503d, CRL_REG_LEN_08BIT, 0x00}, + {0xc450, CRL_REG_LEN_08BIT, 0x01}, + {0xc452, CRL_REG_LEN_08BIT, 0x04}, + {0xc453, CRL_REG_LEN_08BIT, 0x00}, + {0xc454, CRL_REG_LEN_08BIT, 0x01}, + {0xc455, CRL_REG_LEN_08BIT, 0x00}, + {0xc456, CRL_REG_LEN_08BIT, 0x00}, + {0xc457, CRL_REG_LEN_08BIT, 0x00}, + {0xc458, CRL_REG_LEN_08BIT, 0x00}, + {0xc459, CRL_REG_LEN_08BIT, 0x00}, + {0xc45b, CRL_REG_LEN_08BIT, 0x00}, + {0xc45c, CRL_REG_LEN_08BIT, 0x00}, + {0xc45d, CRL_REG_LEN_08BIT, 0x00}, + {0xc45e, CRL_REG_LEN_08BIT, 0x02}, + {0xc45f, CRL_REG_LEN_08BIT, 0x01}, + {0xc460, CRL_REG_LEN_08BIT, 0x01}, + {0xc461, CRL_REG_LEN_08BIT, 0x01}, + {0xc462, CRL_REG_LEN_08BIT, 0x01}, + {0xc464, CRL_REG_LEN_08BIT, 0x88}, + {0xc465, CRL_REG_LEN_08BIT, 0x00}, + {0xc466, CRL_REG_LEN_08BIT, 0x8a}, + {0xc467, CRL_REG_LEN_08BIT, 0x00}, + {0xc468, CRL_REG_LEN_08BIT, 0x86}, + {0xc469, CRL_REG_LEN_08BIT, 0x00}, + {0xc46a, CRL_REG_LEN_08BIT, 0x40}, + {0xc46b, CRL_REG_LEN_08BIT, 0x50}, + {0xc46c, CRL_REG_LEN_08BIT, 0x30}, + {0xc46d, CRL_REG_LEN_08BIT, 0x28}, + {0xc46e, CRL_REG_LEN_08BIT, 0x60}, + {0xc46f, CRL_REG_LEN_08BIT, 0x40}, + {0xc47c, CRL_REG_LEN_08BIT, 0x01}, + {0xc47d, CRL_REG_LEN_08BIT, 0x38}, + {0xc47e, CRL_REG_LEN_08BIT, 0x00}, + {0xc47f, CRL_REG_LEN_08BIT, 0x00}, + {0xc480, CRL_REG_LEN_08BIT, 0x00}, + {0xc481, CRL_REG_LEN_08BIT, 0xff}, + {0xc482, CRL_REG_LEN_08BIT, 0x00}, + {0xc483, CRL_REG_LEN_08BIT, 0x40}, + {0xc484, CRL_REG_LEN_08BIT, 0x00}, + {0xc485, CRL_REG_LEN_08BIT, 0x18}, + {0xc486, CRL_REG_LEN_08BIT, 0x00}, + {0xc487, CRL_REG_LEN_08BIT, 0x18}, + {0xc488, CRL_REG_LEN_08BIT, 0x34}, + {0xc489, CRL_REG_LEN_08BIT, 0x00}, + {0xc48a, CRL_REG_LEN_08BIT, 0x34}, + {0xc48b, CRL_REG_LEN_08BIT, 0x00}, + {0xc48c, CRL_REG_LEN_08BIT, 0x00}, + {0xc48d, CRL_REG_LEN_08BIT, 0x04}, + {0xc48e, CRL_REG_LEN_08BIT, 0x00}, + {0xc48f, CRL_REG_LEN_08BIT, 0x04}, + {0xc490, CRL_REG_LEN_08BIT, 0x07}, + {0xc492, CRL_REG_LEN_08BIT, 0x20}, + {0xc493, CRL_REG_LEN_08BIT, 0x08}, + {0xc498, CRL_REG_LEN_08BIT, 0x02}, + {0xc499, CRL_REG_LEN_08BIT, 0x00}, + {0xc49a, CRL_REG_LEN_08BIT, 0x02}, + {0xc49b, CRL_REG_LEN_08BIT, 0x00}, + {0xc49c, CRL_REG_LEN_08BIT, 0x02}, + {0xc49d, CRL_REG_LEN_08BIT, 0x00}, + {0xc49e, CRL_REG_LEN_08BIT, 0x02}, + {0xc49f, CRL_REG_LEN_08BIT, 0x60}, + {0xc4a0, CRL_REG_LEN_08BIT, 0x03}, + {0xc4a1, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a2, CRL_REG_LEN_08BIT, 0x04}, + {0xc4a3, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a5, CRL_REG_LEN_08BIT, 0x10}, + {0xc4a6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4a8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a9, CRL_REG_LEN_08BIT, 0x80}, + {0xc4aa, CRL_REG_LEN_08BIT, 0x0d}, + {0xc4ab, CRL_REG_LEN_08BIT, 0x00}, + {0xc4ac, CRL_REG_LEN_08BIT, 0x03}, + {0xc4ad, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4b4, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b5, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b7, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b9, CRL_REG_LEN_08BIT, 0x01}, + {0xc4ba, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0xc4be, CRL_REG_LEN_08BIT, 0x02}, + {0xc4bf, CRL_REG_LEN_08BIT, 0x33}, + {0xc4c8, CRL_REG_LEN_08BIT, 0x03}, + {0xc4c9, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4ca, CRL_REG_LEN_08BIT, 0x0e}, + {0xc4cb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4cc, CRL_REG_LEN_08BIT, 0x10}, + {0xc4cd, CRL_REG_LEN_08BIT, 0x18}, + {0xc4ce, CRL_REG_LEN_08BIT, 0x10}, + {0xc4cf, CRL_REG_LEN_08BIT, 0x18}, + {0xc4d0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4d1, CRL_REG_LEN_08BIT, 0x80}, + {0xc4e0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4e1, CRL_REG_LEN_08BIT, 0x02}, + {0xc4e2, CRL_REG_LEN_08BIT, 0x01}, + {0xc4e4, CRL_REG_LEN_08BIT, 0x10}, + {0xc4e5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4e6, CRL_REG_LEN_08BIT, 0x30}, + {0xc4e7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4e8, CRL_REG_LEN_08BIT, 0x50}, + {0xc4e9, CRL_REG_LEN_08BIT, 0x60}, + {0xc4ea, CRL_REG_LEN_08BIT, 0x70}, + {0xc4eb, CRL_REG_LEN_08BIT, 0x80}, + {0xc4ec, CRL_REG_LEN_08BIT, 0x90}, + {0xc4ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xc4ee, CRL_REG_LEN_08BIT, 0xb0}, + {0xc4ef, CRL_REG_LEN_08BIT, 0xc0}, + {0xc4f0, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4f1, CRL_REG_LEN_08BIT, 0xe0}, + {0xc4f2, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4f3, CRL_REG_LEN_08BIT, 0x80}, + {0xc4f4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4f6, CRL_REG_LEN_08BIT, 0x02}, + {0xc4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f8, CRL_REG_LEN_08BIT, 0x04}, + {0xc4f9, CRL_REG_LEN_08BIT, 0x0b}, + {0xc4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4fd, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fe, CRL_REG_LEN_08BIT, 0x04}, + {0xc4ff, CRL_REG_LEN_08BIT, 0x02}, + {0xc500, CRL_REG_LEN_08BIT, 0x48}, + {0xc501, CRL_REG_LEN_08BIT, 0x74}, + {0xc502, CRL_REG_LEN_08BIT, 0x58}, + {0xc503, CRL_REG_LEN_08BIT, 0x80}, + {0xc504, CRL_REG_LEN_08BIT, 0x05}, + {0xc505, CRL_REG_LEN_08BIT, 0x80}, + {0xc506, CRL_REG_LEN_08BIT, 0x03}, + {0xc507, CRL_REG_LEN_08BIT, 0x80}, + {0xc508, CRL_REG_LEN_08BIT, 0x01}, + {0xc509, CRL_REG_LEN_08BIT, 0xc0}, + {0xc50a, CRL_REG_LEN_08BIT, 0x01}, + {0xc50b, CRL_REG_LEN_08BIT, 0xa0}, + {0xc50c, CRL_REG_LEN_08BIT, 0x01}, + {0xc50d, CRL_REG_LEN_08BIT, 0x2c}, + {0xc50e, CRL_REG_LEN_08BIT, 0x01}, + {0xc50f, CRL_REG_LEN_08BIT, 0x0a}, + {0xc510, CRL_REG_LEN_08BIT, 0x00}, + {0xc511, CRL_REG_LEN_08BIT, 0x01}, + {0xc512, CRL_REG_LEN_08BIT, 0x01}, + {0xc513, CRL_REG_LEN_08BIT, 0x80}, + {0xc514, CRL_REG_LEN_08BIT, 0x04}, + {0xc515, CRL_REG_LEN_08BIT, 0x00}, + {0xc518, CRL_REG_LEN_08BIT, 0x03}, + {0xc519, CRL_REG_LEN_08BIT, 0x48}, + {0xc51a, CRL_REG_LEN_08BIT, 0x07}, + {0xc51b, CRL_REG_LEN_08BIT, 0x70}, + {0xc2e0, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e1, CRL_REG_LEN_08BIT, 0x51}, + {0xc2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e3, CRL_REG_LEN_08BIT, 0xd6}, + {0xc2e4, CRL_REG_LEN_08BIT, 0x01}, + {0xc2e5, CRL_REG_LEN_08BIT, 0x5e}, + {0xc2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xc2ea, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2eb, CRL_REG_LEN_08BIT, 0x90}, + {0xc2ed, CRL_REG_LEN_08BIT, 0x00}, + {0xc2ee, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2ef, CRL_REG_LEN_08BIT, 0x64}, + {0xc308, CRL_REG_LEN_08BIT, 0x00}, + {0xc309, CRL_REG_LEN_08BIT, 0x00}, + {0xc30a, CRL_REG_LEN_08BIT, 0x00}, + {0xc30c, CRL_REG_LEN_08BIT, 0x00}, + {0xc30d, CRL_REG_LEN_08BIT, 0x01}, + {0xc30e, CRL_REG_LEN_08BIT, 0x00}, + {0xc30f, CRL_REG_LEN_08BIT, 0x00}, + {0xc310, CRL_REG_LEN_08BIT, 0x01}, + {0xc311, CRL_REG_LEN_08BIT, 0x60}, + {0xc312, CRL_REG_LEN_08BIT, 0xff}, + {0xc313, CRL_REG_LEN_08BIT, 0x08}, + {0xc314, CRL_REG_LEN_08BIT, 0x01}, + {0xc315, CRL_REG_LEN_08BIT, 0x7f}, + {0xc316, CRL_REG_LEN_08BIT, 0xff}, + {0xc317, CRL_REG_LEN_08BIT, 0x0b}, + {0xc318, CRL_REG_LEN_08BIT, 0x00}, + {0xc319, CRL_REG_LEN_08BIT, 0x0c}, + {0xc31a, CRL_REG_LEN_08BIT, 0x00}, + {0xc31b, CRL_REG_LEN_08BIT, 0xe0}, + {0xc31c, CRL_REG_LEN_08BIT, 0x00}, + {0xc31d, CRL_REG_LEN_08BIT, 0x14}, + {0xc31e, CRL_REG_LEN_08BIT, 0x00}, + {0xc31f, CRL_REG_LEN_08BIT, 0xc5}, + {0xc320, CRL_REG_LEN_08BIT, 0xff}, + {0xc321, CRL_REG_LEN_08BIT, 0x4b}, + {0xc322, CRL_REG_LEN_08BIT, 0xff}, + {0xc323, CRL_REG_LEN_08BIT, 0xf0}, + {0xc324, CRL_REG_LEN_08BIT, 0xff}, + {0xc325, CRL_REG_LEN_08BIT, 0xe8}, + {0xc326, CRL_REG_LEN_08BIT, 0x00}, + {0xc327, CRL_REG_LEN_08BIT, 0x46}, + {0xc328, CRL_REG_LEN_08BIT, 0xff}, + {0xc329, CRL_REG_LEN_08BIT, 0xd2}, + {0xc32a, CRL_REG_LEN_08BIT, 0xff}, + {0xc32b, CRL_REG_LEN_08BIT, 0xe4}, + {0xc32c, CRL_REG_LEN_08BIT, 0xff}, + {0xc32d, CRL_REG_LEN_08BIT, 0xbb}, + {0xc32e, CRL_REG_LEN_08BIT, 0x00}, + {0xc32f, CRL_REG_LEN_08BIT, 0x61}, + {0xc330, CRL_REG_LEN_08BIT, 0xff}, + {0xc331, CRL_REG_LEN_08BIT, 0xf9}, + {0xc332, CRL_REG_LEN_08BIT, 0x00}, + {0xc333, CRL_REG_LEN_08BIT, 0xd9}, + {0xc334, CRL_REG_LEN_08BIT, 0x00}, + {0xc335, CRL_REG_LEN_08BIT, 0x2e}, + {0xc336, CRL_REG_LEN_08BIT, 0x00}, + {0xc337, CRL_REG_LEN_08BIT, 0xb1}, + {0xc338, CRL_REG_LEN_08BIT, 0xff}, + {0xc339, CRL_REG_LEN_08BIT, 0x64}, + {0xc33a, CRL_REG_LEN_08BIT, 0xff}, + {0xc33b, CRL_REG_LEN_08BIT, 0xeb}, + {0xc33c, CRL_REG_LEN_08BIT, 0xff}, + {0xc33d, CRL_REG_LEN_08BIT, 0xe8}, + {0xc33e, CRL_REG_LEN_08BIT, 0x00}, + {0xc33f, CRL_REG_LEN_08BIT, 0x48}, + {0xc340, CRL_REG_LEN_08BIT, 0xff}, + {0xc341, CRL_REG_LEN_08BIT, 0xd0}, + {0xc342, CRL_REG_LEN_08BIT, 0xff}, + {0xc343, CRL_REG_LEN_08BIT, 0xed}, + {0xc344, CRL_REG_LEN_08BIT, 0xff}, + {0xc345, CRL_REG_LEN_08BIT, 0xad}, + {0xc346, CRL_REG_LEN_08BIT, 0x00}, + {0xc347, CRL_REG_LEN_08BIT, 0x66}, + {0xc348, CRL_REG_LEN_08BIT, 0x01}, + {0xc349, CRL_REG_LEN_08BIT, 0x00}, + {0x6700, CRL_REG_LEN_08BIT, 0x04}, + {0x6701, CRL_REG_LEN_08BIT, 0x7b}, + {0x6702, CRL_REG_LEN_08BIT, 0xfd}, + {0x6703, CRL_REG_LEN_08BIT, 0xf9}, + {0x6704, CRL_REG_LEN_08BIT, 0x3d}, + {0x6705, CRL_REG_LEN_08BIT, 0x71}, + {0x6706, CRL_REG_LEN_08BIT, 0x78}, + {0x6708, CRL_REG_LEN_08BIT, 0x05}, + {0x6f06, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f07, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0a, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f0b, CRL_REG_LEN_08BIT, 0x00}, + {0x6f00, CRL_REG_LEN_08BIT, 0x03}, + {0xc34c, CRL_REG_LEN_08BIT, 0x01}, + {0xc34d, CRL_REG_LEN_08BIT, 0x00}, + {0xc34e, CRL_REG_LEN_08BIT, 0x46}, + {0xc34f, CRL_REG_LEN_08BIT, 0x55}, + {0xc350, CRL_REG_LEN_08BIT, 0x00}, + {0xc351, CRL_REG_LEN_08BIT, 0x40}, + {0xc352, CRL_REG_LEN_08BIT, 0x00}, + {0xc353, CRL_REG_LEN_08BIT, 0xff}, + {0xc354, CRL_REG_LEN_08BIT, 0x04}, + {0xc355, CRL_REG_LEN_08BIT, 0x08}, + {0xc356, CRL_REG_LEN_08BIT, 0x01}, + {0xc357, CRL_REG_LEN_08BIT, 0xef}, + {0xc358, CRL_REG_LEN_08BIT, 0x30}, + {0xc359, CRL_REG_LEN_08BIT, 0x01}, + {0xc35a, CRL_REG_LEN_08BIT, 0x64}, + {0xc35b, CRL_REG_LEN_08BIT, 0x46}, + {0xc35c, CRL_REG_LEN_08BIT, 0x00}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x302e, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0xf0}, + {0x301c, CRL_REG_LEN_08BIT, 0xf0}, + {0x301a, CRL_REG_LEN_08BIT, 0xf0}, + {0xceb0, CRL_REG_LEN_08BIT, 0x00}, + {0xceb1, CRL_REG_LEN_08BIT, 0x00}, + {0xceb2, CRL_REG_LEN_08BIT, 0x00}, + {0xceb3, CRL_REG_LEN_08BIT, 0x00}, + {0xceb4, CRL_REG_LEN_08BIT, 0x00}, + {0xceb5, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb6, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb7, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, +}; + +static struct crl_register_write_rep ov10635_1280_720_YUV_HDR_BT656[] = { + {0x0103, CRL_REG_LEN_08BIT, 0x01}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x301b, CRL_REG_LEN_08BIT, 0xff}, + {0x301c, CRL_REG_LEN_08BIT, 0xff}, + {0x301a, CRL_REG_LEN_08BIT, 0xff}, + {0x3011, CRL_REG_LEN_08BIT, 0x42}, + {0x6900, CRL_REG_LEN_08BIT, 0x0c}, + {0x6901, CRL_REG_LEN_08BIT, 0x11}, + {0x3503, CRL_REG_LEN_08BIT, 0x10}, + {0x3025, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x14}, + {0x3004, CRL_REG_LEN_08BIT, 0x11}, + {0x3005, CRL_REG_LEN_08BIT, 0x20}, + {0x3006, CRL_REG_LEN_08BIT, 0x91}, + {0x3600, CRL_REG_LEN_08BIT, 0x74}, + {0x3601, CRL_REG_LEN_08BIT, 0x2b}, + {0x3612, CRL_REG_LEN_08BIT, 0x00}, + {0x3611, CRL_REG_LEN_08BIT, 0x67}, + {0x3633, CRL_REG_LEN_08BIT, 0xca}, + {0x3602, CRL_REG_LEN_08BIT, 0x2f}, + {0x3603, CRL_REG_LEN_08BIT, 0x00}, + {0x3630, CRL_REG_LEN_08BIT, 0x28}, + {0x3631, CRL_REG_LEN_08BIT, 0x16}, + {0x3714, CRL_REG_LEN_08BIT, 0x10}, + {0x371d, CRL_REG_LEN_08BIT, 0x01}, + {0x4300, CRL_REG_LEN_08BIT, 0x3a}, + {0x3007, CRL_REG_LEN_08BIT, 0x01}, + {0x3024, CRL_REG_LEN_08BIT, 0x01}, + {0x3020, CRL_REG_LEN_08BIT, 0x0b}, + {0x3702, CRL_REG_LEN_08BIT, 0x1a}, + {0x3703, CRL_REG_LEN_08BIT, 0x40}, + {0x3704, CRL_REG_LEN_08BIT, 0x2a}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x370c, CRL_REG_LEN_08BIT, 0xc7}, + {0x370d, CRL_REG_LEN_08BIT, 0x80}, + {0x3712, CRL_REG_LEN_08BIT, 0x00}, + {0x3713, CRL_REG_LEN_08BIT, 0x20}, + {0x3715, CRL_REG_LEN_08BIT, 0x04}, + {0x381d, CRL_REG_LEN_08BIT, 0x40}, + {0x381c, CRL_REG_LEN_08BIT, 0x00}, + {0x3822, CRL_REG_LEN_08BIT, 0x50}, + {0x3824, CRL_REG_LEN_08BIT, 0x50}, + {0x3815, CRL_REG_LEN_08BIT, 0x8c}, + {0x3804, CRL_REG_LEN_08BIT, 0x05}, + {0x3805, CRL_REG_LEN_08BIT, 0x1f}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3806, CRL_REG_LEN_08BIT, 0x02}, + {0x3807, CRL_REG_LEN_08BIT, 0xfd}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0x2c}, + {0x3808, CRL_REG_LEN_08BIT, 0x05}, + {0x3809, CRL_REG_LEN_08BIT, 0x00}, + {0x380a, CRL_REG_LEN_08BIT, 0x02}, + {0x380b, CRL_REG_LEN_08BIT, 0xd0}, + {0x380c, CRL_REG_LEN_08BIT, 0x06}, + {0x380d, CRL_REG_LEN_08BIT, 0xf6}, + {0x6e42, CRL_REG_LEN_08BIT, 0x02}, + {0x6e43, CRL_REG_LEN_08BIT, 0xec}, + {0x380e, CRL_REG_LEN_08BIT, 0x02}, + {0x380f, CRL_REG_LEN_08BIT, 0xec}, + {0x3813, CRL_REG_LEN_08BIT, 0x02}, + {0x3811, CRL_REG_LEN_08BIT, 0x10}, + {0x381f, CRL_REG_LEN_08BIT, 0x0c}, + {0x3828, CRL_REG_LEN_08BIT, 0x03}, + {0x3829, CRL_REG_LEN_08BIT, 0x10}, + {0x382a, CRL_REG_LEN_08BIT, 0x10}, + {0x382b, CRL_REG_LEN_08BIT, 0x10}, + {0x3621, CRL_REG_LEN_08BIT, 0x64}, + {0x5005, CRL_REG_LEN_08BIT, 0x08}, + {0x56d5, CRL_REG_LEN_08BIT, 0x00}, + {0x56d6, CRL_REG_LEN_08BIT, 0x80}, + {0x56d7, CRL_REG_LEN_08BIT, 0x00}, + {0x56d8, CRL_REG_LEN_08BIT, 0x00}, + {0x56d9, CRL_REG_LEN_08BIT, 0x00}, + {0x56da, CRL_REG_LEN_08BIT, 0x80}, + {0x56db, CRL_REG_LEN_08BIT, 0x00}, + {0x56dc, CRL_REG_LEN_08BIT, 0x00}, + {0x56e8, CRL_REG_LEN_08BIT, 0x00}, + {0x56e9, CRL_REG_LEN_08BIT, 0x7f}, + {0x56ea, CRL_REG_LEN_08BIT, 0x00}, + {0x56eb, CRL_REG_LEN_08BIT, 0x7f}, + {0x5100, CRL_REG_LEN_08BIT, 0x00}, + {0x5101, CRL_REG_LEN_08BIT, 0x80}, + {0x5102, CRL_REG_LEN_08BIT, 0x00}, + {0x5103, CRL_REG_LEN_08BIT, 0x80}, + {0x5104, CRL_REG_LEN_08BIT, 0x00}, + {0x5105, CRL_REG_LEN_08BIT, 0x80}, + {0x5106, CRL_REG_LEN_08BIT, 0x00}, + {0x5107, CRL_REG_LEN_08BIT, 0x80}, + {0x5108, CRL_REG_LEN_08BIT, 0x00}, + {0x5109, CRL_REG_LEN_08BIT, 0x00}, + {0x510a, CRL_REG_LEN_08BIT, 0x00}, + {0x510b, CRL_REG_LEN_08BIT, 0x00}, + {0x510c, CRL_REG_LEN_08BIT, 0x00}, + {0x510d, CRL_REG_LEN_08BIT, 0x00}, + {0x510e, CRL_REG_LEN_08BIT, 0x00}, + {0x510f, CRL_REG_LEN_08BIT, 0x00}, + {0x5110, CRL_REG_LEN_08BIT, 0x00}, + {0x5111, CRL_REG_LEN_08BIT, 0x80}, + {0x5112, CRL_REG_LEN_08BIT, 0x00}, + {0x5113, CRL_REG_LEN_08BIT, 0x80}, + {0x5114, CRL_REG_LEN_08BIT, 0x00}, + {0x5115, CRL_REG_LEN_08BIT, 0x80}, + {0x5116, CRL_REG_LEN_08BIT, 0x00}, + {0x5117, CRL_REG_LEN_08BIT, 0x80}, + {0x5118, CRL_REG_LEN_08BIT, 0x00}, + {0x5119, CRL_REG_LEN_08BIT, 0x00}, + {0x511a, CRL_REG_LEN_08BIT, 0x00}, + {0x511b, CRL_REG_LEN_08BIT, 0x00}, + {0x511c, CRL_REG_LEN_08BIT, 0x00}, + {0x511d, CRL_REG_LEN_08BIT, 0x00}, + {0x511e, CRL_REG_LEN_08BIT, 0x00}, + {0x511f, CRL_REG_LEN_08BIT, 0x00}, + {0x56d0, CRL_REG_LEN_08BIT, 0x00}, + {0x5006, CRL_REG_LEN_08BIT, 0x24}, + {0x5608, CRL_REG_LEN_08BIT, 0x0e}, + {0x52d7, CRL_REG_LEN_08BIT, 0x06}, + {0x528d, CRL_REG_LEN_08BIT, 0x08}, + {0x5293, CRL_REG_LEN_08BIT, 0x12}, + {0x52d3, CRL_REG_LEN_08BIT, 0x12}, + {0x5288, CRL_REG_LEN_08BIT, 0x06}, + {0x5289, CRL_REG_LEN_08BIT, 0x20}, + {0x52c8, CRL_REG_LEN_08BIT, 0x06}, + {0x52c9, CRL_REG_LEN_08BIT, 0x20}, + {0x52cd, CRL_REG_LEN_08BIT, 0x04}, + {0x5381, CRL_REG_LEN_08BIT, 0x00}, + {0x5382, CRL_REG_LEN_08BIT, 0xff}, + {0x5589, CRL_REG_LEN_08BIT, 0x76}, + {0x558a, CRL_REG_LEN_08BIT, 0x47}, + {0x558b, CRL_REG_LEN_08BIT, 0xef}, + {0x558c, CRL_REG_LEN_08BIT, 0xc9}, + {0x558d, CRL_REG_LEN_08BIT, 0x49}, + {0x558e, CRL_REG_LEN_08BIT, 0x30}, + {0x558f, CRL_REG_LEN_08BIT, 0x67}, + {0x5590, CRL_REG_LEN_08BIT, 0x3f}, + {0x5591, CRL_REG_LEN_08BIT, 0xf0}, + {0x5592, CRL_REG_LEN_08BIT, 0x10}, + {0x55a2, CRL_REG_LEN_08BIT, 0x6d}, + {0x55a3, CRL_REG_LEN_08BIT, 0x55}, + {0x55a4, CRL_REG_LEN_08BIT, 0xc3}, + {0x55a5, CRL_REG_LEN_08BIT, 0xb5}, + {0x55a6, CRL_REG_LEN_08BIT, 0x43}, + {0x55a7, CRL_REG_LEN_08BIT, 0x38}, + {0x55a8, CRL_REG_LEN_08BIT, 0x5f}, + {0x55a9, CRL_REG_LEN_08BIT, 0x4b}, + {0x55aa, CRL_REG_LEN_08BIT, 0xf0}, + {0x55ab, CRL_REG_LEN_08BIT, 0x10}, + {0x5581, CRL_REG_LEN_08BIT, 0x52}, + {0x5300, CRL_REG_LEN_08BIT, 0x01}, + {0x5301, CRL_REG_LEN_08BIT, 0x00}, + {0x5302, CRL_REG_LEN_08BIT, 0x00}, + {0x5303, CRL_REG_LEN_08BIT, 0x0e}, + {0x5304, CRL_REG_LEN_08BIT, 0x00}, + {0x5305, CRL_REG_LEN_08BIT, 0x0e}, + {0x5306, CRL_REG_LEN_08BIT, 0x00}, + {0x5307, CRL_REG_LEN_08BIT, 0x36}, + {0x5308, CRL_REG_LEN_08BIT, 0x00}, + {0x5309, CRL_REG_LEN_08BIT, 0xd9}, + {0x530a, CRL_REG_LEN_08BIT, 0x00}, + {0x530b, CRL_REG_LEN_08BIT, 0x0f}, + {0x530c, CRL_REG_LEN_08BIT, 0x00}, + {0x530d, CRL_REG_LEN_08BIT, 0x2c}, + {0x530e, CRL_REG_LEN_08BIT, 0x00}, + {0x530f, CRL_REG_LEN_08BIT, 0x59}, + {0x5310, CRL_REG_LEN_08BIT, 0x00}, + {0x5311, CRL_REG_LEN_08BIT, 0x7b}, + {0x5312, CRL_REG_LEN_08BIT, 0x00}, + {0x5313, CRL_REG_LEN_08BIT, 0x22}, + {0x5314, CRL_REG_LEN_08BIT, 0x00}, + {0x5315, CRL_REG_LEN_08BIT, 0xd5}, + {0x5316, CRL_REG_LEN_08BIT, 0x00}, + {0x5317, CRL_REG_LEN_08BIT, 0x13}, + {0x5318, CRL_REG_LEN_08BIT, 0x00}, + {0x5319, CRL_REG_LEN_08BIT, 0x18}, + {0x531a, CRL_REG_LEN_08BIT, 0x00}, + {0x531b, CRL_REG_LEN_08BIT, 0x26}, + {0x531c, CRL_REG_LEN_08BIT, 0x00}, + {0x531d, CRL_REG_LEN_08BIT, 0xdc}, + {0x531e, CRL_REG_LEN_08BIT, 0x00}, + {0x531f, CRL_REG_LEN_08BIT, 0x02}, + {0x5320, CRL_REG_LEN_08BIT, 0x00}, + {0x5321, CRL_REG_LEN_08BIT, 0x24}, + {0x5322, CRL_REG_LEN_08BIT, 0x00}, + {0x5323, CRL_REG_LEN_08BIT, 0x56}, + {0x5324, CRL_REG_LEN_08BIT, 0x00}, + {0x5325, CRL_REG_LEN_08BIT, 0x85}, + {0x5326, CRL_REG_LEN_08BIT, 0x00}, + {0x5327, CRL_REG_LEN_08BIT, 0x20}, + {0x5609, CRL_REG_LEN_08BIT, 0x01}, + {0x560a, CRL_REG_LEN_08BIT, 0x40}, + {0x560b, CRL_REG_LEN_08BIT, 0x01}, + {0x560c, CRL_REG_LEN_08BIT, 0x40}, + {0x560d, CRL_REG_LEN_08BIT, 0x00}, + {0x560e, CRL_REG_LEN_08BIT, 0xfa}, + {0x560f, CRL_REG_LEN_08BIT, 0x00}, + {0x5610, CRL_REG_LEN_08BIT, 0xfa}, + {0x5611, CRL_REG_LEN_08BIT, 0x02}, + {0x5612, CRL_REG_LEN_08BIT, 0x80}, + {0x5613, CRL_REG_LEN_08BIT, 0x02}, + {0x5614, CRL_REG_LEN_08BIT, 0x80}, + {0x5615, CRL_REG_LEN_08BIT, 0x01}, + {0x5616, CRL_REG_LEN_08BIT, 0x2c}, + {0x5617, CRL_REG_LEN_08BIT, 0x01}, + {0x5618, CRL_REG_LEN_08BIT, 0x2c}, + {0x563b, CRL_REG_LEN_08BIT, 0x01}, + {0x563c, CRL_REG_LEN_08BIT, 0x01}, + {0x563d, CRL_REG_LEN_08BIT, 0x01}, + {0x563e, CRL_REG_LEN_08BIT, 0x01}, + {0x563f, CRL_REG_LEN_08BIT, 0x03}, + {0x5640, CRL_REG_LEN_08BIT, 0x03}, + {0x5641, CRL_REG_LEN_08BIT, 0x03}, + {0x5642, CRL_REG_LEN_08BIT, 0x05}, + {0x5643, CRL_REG_LEN_08BIT, 0x09}, + {0x5644, CRL_REG_LEN_08BIT, 0x05}, + {0x5645, CRL_REG_LEN_08BIT, 0x05}, + {0x5646, CRL_REG_LEN_08BIT, 0x05}, + {0x5647, CRL_REG_LEN_08BIT, 0x05}, + {0x5651, CRL_REG_LEN_08BIT, 0x00}, + {0x5652, CRL_REG_LEN_08BIT, 0x80}, + {0x521a, CRL_REG_LEN_08BIT, 0x01}, + {0x521b, CRL_REG_LEN_08BIT, 0x03}, + {0x521c, CRL_REG_LEN_08BIT, 0x06}, + {0x521d, CRL_REG_LEN_08BIT, 0x0a}, + {0x521e, CRL_REG_LEN_08BIT, 0x0e}, + {0x521f, CRL_REG_LEN_08BIT, 0x12}, + {0x5220, CRL_REG_LEN_08BIT, 0x16}, + {0x5223, CRL_REG_LEN_08BIT, 0x02}, + {0x5225, CRL_REG_LEN_08BIT, 0x04}, + {0x5227, CRL_REG_LEN_08BIT, 0x08}, + {0x5229, CRL_REG_LEN_08BIT, 0x0c}, + {0x522b, CRL_REG_LEN_08BIT, 0x12}, + {0x522d, CRL_REG_LEN_08BIT, 0x18}, + {0x522f, CRL_REG_LEN_08BIT, 0x1e}, + {0x5241, CRL_REG_LEN_08BIT, 0x04}, + {0x5242, CRL_REG_LEN_08BIT, 0x01}, + {0x5243, CRL_REG_LEN_08BIT, 0x03}, + {0x5244, CRL_REG_LEN_08BIT, 0x06}, + {0x5245, CRL_REG_LEN_08BIT, 0x0a}, + {0x5246, CRL_REG_LEN_08BIT, 0x0e}, + {0x5247, CRL_REG_LEN_08BIT, 0x12}, + {0x5248, CRL_REG_LEN_08BIT, 0x16}, + {0x524a, CRL_REG_LEN_08BIT, 0x03}, + {0x524c, CRL_REG_LEN_08BIT, 0x04}, + {0x524e, CRL_REG_LEN_08BIT, 0x08}, + {0x5250, CRL_REG_LEN_08BIT, 0x0c}, + {0x5252, CRL_REG_LEN_08BIT, 0x12}, + {0x5254, CRL_REG_LEN_08BIT, 0x18}, + {0x5256, CRL_REG_LEN_08BIT, 0x1e}, + {0x4606, CRL_REG_LEN_08BIT, 0x07}, + {0x4607, CRL_REG_LEN_08BIT, 0x71}, + {0x460a, CRL_REG_LEN_08BIT, 0x03}, + {0x460b, CRL_REG_LEN_08BIT, 0xe7}, + {0x460c, CRL_REG_LEN_08BIT, 0x40}, + {0x4620, CRL_REG_LEN_08BIT, 0x0e}, + {0x4700, CRL_REG_LEN_08BIT, 0x06}, + {0x4701, CRL_REG_LEN_08BIT, 0x00}, + {0x4702, CRL_REG_LEN_08BIT, 0x01}, + {0x4004, CRL_REG_LEN_08BIT, 0x04}, + {0x4005, CRL_REG_LEN_08BIT, 0x18}, + {0x4001, CRL_REG_LEN_08BIT, 0x06}, + {0x4050, CRL_REG_LEN_08BIT, 0x22}, + {0x4051, CRL_REG_LEN_08BIT, 0x24}, + {0x4052, CRL_REG_LEN_08BIT, 0x02}, + {0x4057, CRL_REG_LEN_08BIT, 0x9c}, + {0x405a, CRL_REG_LEN_08BIT, 0x00}, + {0x4302, CRL_REG_LEN_08BIT, 0x03}, + {0x4303, CRL_REG_LEN_08BIT, 0xff}, + {0x4304, CRL_REG_LEN_08BIT, 0x00}, + {0x4305, CRL_REG_LEN_08BIT, 0x10}, + {0x4306, CRL_REG_LEN_08BIT, 0x03}, + {0x4307, CRL_REG_LEN_08BIT, 0xff}, + {0x4308, CRL_REG_LEN_08BIT, 0x00}, + {0x4309, CRL_REG_LEN_08BIT, 0x10}, + {0x4202, CRL_REG_LEN_08BIT, 0x02}, + {0x3023, CRL_REG_LEN_08BIT, 0x10}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x6f10, CRL_REG_LEN_08BIT, 0x07}, + {0x6f11, CRL_REG_LEN_08BIT, 0x82}, + {0x6f12, CRL_REG_LEN_08BIT, 0x04}, + {0x6f13, CRL_REG_LEN_08BIT, 0x00}, + {0x6f14, CRL_REG_LEN_08BIT, 0x1f}, + {0x6f15, CRL_REG_LEN_08BIT, 0xdd}, + {0x6f16, CRL_REG_LEN_08BIT, 0x04}, + {0x6f17, CRL_REG_LEN_08BIT, 0x04}, + {0x6f18, CRL_REG_LEN_08BIT, 0x36}, + {0x6f19, CRL_REG_LEN_08BIT, 0x66}, + {0x6f1a, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1b, CRL_REG_LEN_08BIT, 0x08}, + {0x6f1c, CRL_REG_LEN_08BIT, 0x0c}, + {0x6f1d, CRL_REG_LEN_08BIT, 0xe7}, + {0x6f1e, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1f, CRL_REG_LEN_08BIT, 0x0c}, + {0xd000, CRL_REG_LEN_08BIT, 0x19}, + {0xd001, CRL_REG_LEN_08BIT, 0xa0}, + {0xd002, CRL_REG_LEN_08BIT, 0x00}, + {0xd003, CRL_REG_LEN_08BIT, 0x01}, + {0xd004, CRL_REG_LEN_08BIT, 0xa9}, + {0xd005, CRL_REG_LEN_08BIT, 0xad}, + {0xd006, CRL_REG_LEN_08BIT, 0x10}, + {0xd007, CRL_REG_LEN_08BIT, 0x40}, + {0xd008, CRL_REG_LEN_08BIT, 0x44}, + {0xd009, CRL_REG_LEN_08BIT, 0x00}, + {0xd00a, CRL_REG_LEN_08BIT, 0x68}, + {0xd00b, CRL_REG_LEN_08BIT, 0x00}, + {0xd00c, CRL_REG_LEN_08BIT, 0x15}, + {0xd00d, CRL_REG_LEN_08BIT, 0x00}, + {0xd00e, CRL_REG_LEN_08BIT, 0x00}, + {0xd00f, CRL_REG_LEN_08BIT, 0x00}, + {0xd010, CRL_REG_LEN_08BIT, 0x19}, + {0xd011, CRL_REG_LEN_08BIT, 0xa0}, + {0xd012, CRL_REG_LEN_08BIT, 0x00}, + {0xd013, CRL_REG_LEN_08BIT, 0x01}, + {0xd014, CRL_REG_LEN_08BIT, 0xa9}, + {0xd015, CRL_REG_LEN_08BIT, 0xad}, + {0xd016, CRL_REG_LEN_08BIT, 0x14}, + {0xd017, CRL_REG_LEN_08BIT, 0x40}, + {0xd018, CRL_REG_LEN_08BIT, 0x44}, + {0xd019, CRL_REG_LEN_08BIT, 0x00}, + {0xd01a, CRL_REG_LEN_08BIT, 0x68}, + {0xd01b, CRL_REG_LEN_08BIT, 0x00}, + {0xd01c, CRL_REG_LEN_08BIT, 0x15}, + {0xd01d, CRL_REG_LEN_08BIT, 0x00}, + {0xd01e, CRL_REG_LEN_08BIT, 0x00}, + {0xd01f, CRL_REG_LEN_08BIT, 0x00}, + {0xd020, CRL_REG_LEN_08BIT, 0x19}, + {0xd021, CRL_REG_LEN_08BIT, 0xa0}, + {0xd022, CRL_REG_LEN_08BIT, 0x00}, + {0xd023, CRL_REG_LEN_08BIT, 0x01}, + {0xd024, CRL_REG_LEN_08BIT, 0xa9}, + {0xd025, CRL_REG_LEN_08BIT, 0xad}, + {0xd026, CRL_REG_LEN_08BIT, 0x15}, + {0xd027, CRL_REG_LEN_08BIT, 0x28}, + {0xd028, CRL_REG_LEN_08BIT, 0x44}, + {0xd029, CRL_REG_LEN_08BIT, 0x00}, + {0xd02a, CRL_REG_LEN_08BIT, 0x68}, + {0xd02b, CRL_REG_LEN_08BIT, 0x00}, + {0xd02c, CRL_REG_LEN_08BIT, 0x15}, + {0xd02d, CRL_REG_LEN_08BIT, 0x00}, + {0xd02e, CRL_REG_LEN_08BIT, 0x00}, + {0xd02f, CRL_REG_LEN_08BIT, 0x00}, + {0xd030, CRL_REG_LEN_08BIT, 0x19}, + {0xd031, CRL_REG_LEN_08BIT, 0xa0}, + {0xd032, CRL_REG_LEN_08BIT, 0x00}, + {0xd033, CRL_REG_LEN_08BIT, 0x01}, + {0xd034, CRL_REG_LEN_08BIT, 0xa9}, + {0xd035, CRL_REG_LEN_08BIT, 0xad}, + {0xd036, CRL_REG_LEN_08BIT, 0x15}, + {0xd037, CRL_REG_LEN_08BIT, 0x4c}, + {0xd038, CRL_REG_LEN_08BIT, 0x44}, + {0xd039, CRL_REG_LEN_08BIT, 0x00}, + {0xd03a, CRL_REG_LEN_08BIT, 0x68}, + {0xd03b, CRL_REG_LEN_08BIT, 0x00}, + {0xd03c, CRL_REG_LEN_08BIT, 0x15}, + {0xd03d, CRL_REG_LEN_08BIT, 0x00}, + {0xd03e, CRL_REG_LEN_08BIT, 0x00}, + {0xd03f, CRL_REG_LEN_08BIT, 0x00}, + {0xd040, CRL_REG_LEN_08BIT, 0x9c}, + {0xd041, CRL_REG_LEN_08BIT, 0x21}, + {0xd042, CRL_REG_LEN_08BIT, 0xff}, + {0xd043, CRL_REG_LEN_08BIT, 0xe4}, + {0xd044, CRL_REG_LEN_08BIT, 0xd4}, + {0xd045, CRL_REG_LEN_08BIT, 0x01}, + {0xd046, CRL_REG_LEN_08BIT, 0x48}, + {0xd047, CRL_REG_LEN_08BIT, 0x00}, + {0xd048, CRL_REG_LEN_08BIT, 0xd4}, + {0xd049, CRL_REG_LEN_08BIT, 0x01}, + {0xd04a, CRL_REG_LEN_08BIT, 0x50}, + {0xd04b, CRL_REG_LEN_08BIT, 0x04}, + {0xd04c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd04d, CRL_REG_LEN_08BIT, 0x01}, + {0xd04e, CRL_REG_LEN_08BIT, 0x60}, + {0xd04f, CRL_REG_LEN_08BIT, 0x08}, + {0xd050, CRL_REG_LEN_08BIT, 0xd4}, + {0xd051, CRL_REG_LEN_08BIT, 0x01}, + {0xd052, CRL_REG_LEN_08BIT, 0x70}, + {0xd053, CRL_REG_LEN_08BIT, 0x0c}, + {0xd054, CRL_REG_LEN_08BIT, 0xd4}, + {0xd055, CRL_REG_LEN_08BIT, 0x01}, + {0xd056, CRL_REG_LEN_08BIT, 0x80}, + {0xd057, CRL_REG_LEN_08BIT, 0x10}, + {0xd058, CRL_REG_LEN_08BIT, 0x19}, + {0xd059, CRL_REG_LEN_08BIT, 0xc0}, + {0xd05a, CRL_REG_LEN_08BIT, 0x00}, + {0xd05b, CRL_REG_LEN_08BIT, 0x01}, + {0xd05c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd05d, CRL_REG_LEN_08BIT, 0xce}, + {0xd05e, CRL_REG_LEN_08BIT, 0x02}, + {0xd05f, CRL_REG_LEN_08BIT, 0xa4}, + {0xd060, CRL_REG_LEN_08BIT, 0x9c}, + {0xd061, CRL_REG_LEN_08BIT, 0xa0}, + {0xd062, CRL_REG_LEN_08BIT, 0x00}, + {0xd063, CRL_REG_LEN_08BIT, 0x00}, + {0xd064, CRL_REG_LEN_08BIT, 0x84}, + {0xd065, CRL_REG_LEN_08BIT, 0x6e}, + {0xd066, CRL_REG_LEN_08BIT, 0x00}, + {0xd067, CRL_REG_LEN_08BIT, 0x00}, + {0xd068, CRL_REG_LEN_08BIT, 0xd8}, + {0xd069, CRL_REG_LEN_08BIT, 0x03}, + {0xd06a, CRL_REG_LEN_08BIT, 0x28}, + {0xd06b, CRL_REG_LEN_08BIT, 0x76}, + {0xd06c, CRL_REG_LEN_08BIT, 0x1a}, + {0xd06d, CRL_REG_LEN_08BIT, 0x00}, + {0xd06e, CRL_REG_LEN_08BIT, 0x00}, + {0xd06f, CRL_REG_LEN_08BIT, 0x01}, + {0xd070, CRL_REG_LEN_08BIT, 0xaa}, + {0xd071, CRL_REG_LEN_08BIT, 0x10}, + {0xd072, CRL_REG_LEN_08BIT, 0x03}, + {0xd073, CRL_REG_LEN_08BIT, 0xf0}, + {0xd074, CRL_REG_LEN_08BIT, 0x18}, + {0xd075, CRL_REG_LEN_08BIT, 0x60}, + {0xd076, CRL_REG_LEN_08BIT, 0x00}, + {0xd077, CRL_REG_LEN_08BIT, 0x01}, + {0xd078, CRL_REG_LEN_08BIT, 0xa8}, + {0xd079, CRL_REG_LEN_08BIT, 0x63}, + {0xd07a, CRL_REG_LEN_08BIT, 0x07}, + {0xd07b, CRL_REG_LEN_08BIT, 0x80}, + {0xd07c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd07d, CRL_REG_LEN_08BIT, 0xa0}, + {0xd07e, CRL_REG_LEN_08BIT, 0x00}, + {0xd07f, CRL_REG_LEN_08BIT, 0x04}, + {0xd080, CRL_REG_LEN_08BIT, 0x18}, + {0xd081, CRL_REG_LEN_08BIT, 0xc0}, + {0xd082, CRL_REG_LEN_08BIT, 0x00}, + {0xd083, CRL_REG_LEN_08BIT, 0x00}, + {0xd084, CRL_REG_LEN_08BIT, 0xa8}, + {0xd085, CRL_REG_LEN_08BIT, 0xc6}, + {0xd086, CRL_REG_LEN_08BIT, 0x00}, + {0xd087, CRL_REG_LEN_08BIT, 0x00}, + {0xd088, CRL_REG_LEN_08BIT, 0x8c}, + {0xd089, CRL_REG_LEN_08BIT, 0x63}, + {0xd08a, CRL_REG_LEN_08BIT, 0x00}, + {0xd08b, CRL_REG_LEN_08BIT, 0x00}, + {0xd08c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd08d, CRL_REG_LEN_08BIT, 0x01}, + {0xd08e, CRL_REG_LEN_08BIT, 0x28}, + {0xd08f, CRL_REG_LEN_08BIT, 0x14}, + {0xd090, CRL_REG_LEN_08BIT, 0xd4}, + {0xd091, CRL_REG_LEN_08BIT, 0x01}, + {0xd092, CRL_REG_LEN_08BIT, 0x30}, + {0xd093, CRL_REG_LEN_08BIT, 0x18}, + {0xd094, CRL_REG_LEN_08BIT, 0x07}, + {0xd095, CRL_REG_LEN_08BIT, 0xff}, + {0xd096, CRL_REG_LEN_08BIT, 0xf8}, + {0xd097, CRL_REG_LEN_08BIT, 0xfd}, + {0xd098, CRL_REG_LEN_08BIT, 0x9c}, + {0xd099, CRL_REG_LEN_08BIT, 0x80}, + {0xd09a, CRL_REG_LEN_08BIT, 0x00}, + {0xd09b, CRL_REG_LEN_08BIT, 0x03}, + {0xd09c, CRL_REG_LEN_08BIT, 0xa5}, + {0xd09d, CRL_REG_LEN_08BIT, 0x6b}, + {0xd09e, CRL_REG_LEN_08BIT, 0x00}, + {0xd09f, CRL_REG_LEN_08BIT, 0xff}, + {0xd0a0, CRL_REG_LEN_08BIT, 0x18}, + {0xd0a1, CRL_REG_LEN_08BIT, 0xc0}, + {0xd0a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0a3, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a4, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0a5, CRL_REG_LEN_08BIT, 0xc6}, + {0xd0a6, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a7, CRL_REG_LEN_08BIT, 0x02}, + {0xd0a8, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0a9, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0aa, CRL_REG_LEN_08BIT, 0x58}, + {0xd0ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ac, CRL_REG_LEN_08BIT, 0x84}, + {0xd0ad, CRL_REG_LEN_08BIT, 0x8e}, + {0xd0ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd0af, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0b1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0b2, CRL_REG_LEN_08BIT, 0x30}, + {0xd0b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b4, CRL_REG_LEN_08BIT, 0x98}, + {0xd0b5, CRL_REG_LEN_08BIT, 0xb0}, + {0xd0b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0b9, CRL_REG_LEN_08BIT, 0x64}, + {0xd0ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd0bb, CRL_REG_LEN_08BIT, 0x6e}, + {0xd0bc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0bd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd0be, CRL_REG_LEN_08BIT, 0x18}, + {0xd0bf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c0, CRL_REG_LEN_08BIT, 0x10}, + {0xd0c1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c3, CRL_REG_LEN_08BIT, 0x06}, + {0xd0c4, CRL_REG_LEN_08BIT, 0x95}, + {0xd0c5, CRL_REG_LEN_08BIT, 0x8b}, + {0xd0c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c8, CRL_REG_LEN_08BIT, 0x94}, + {0xd0c9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd0ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd0cb, CRL_REG_LEN_08BIT, 0x70}, + {0xd0cc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0cd, CRL_REG_LEN_08BIT, 0x65}, + {0xd0ce, CRL_REG_LEN_08BIT, 0x60}, + {0xd0cf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd0d1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d3, CRL_REG_LEN_08BIT, 0x62}, + {0xd0d4, CRL_REG_LEN_08BIT, 0x15}, + {0xd0d5, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d8, CRL_REG_LEN_08BIT, 0x18}, + {0xd0d9, CRL_REG_LEN_08BIT, 0x60}, + {0xd0da, CRL_REG_LEN_08BIT, 0x80}, + {0xd0db, CRL_REG_LEN_08BIT, 0x06}, + {0xd0dc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0dd, CRL_REG_LEN_08BIT, 0x83}, + {0xd0de, CRL_REG_LEN_08BIT, 0x38}, + {0xd0df, CRL_REG_LEN_08BIT, 0x29}, + {0xd0e0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e1, CRL_REG_LEN_08BIT, 0xe3}, + {0xd0e2, CRL_REG_LEN_08BIT, 0x40}, + {0xd0e3, CRL_REG_LEN_08BIT, 0x08}, + {0xd0e4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0e5, CRL_REG_LEN_08BIT, 0x84}, + {0xd0e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e9, CRL_REG_LEN_08BIT, 0xa3}, + {0xd0ea, CRL_REG_LEN_08BIT, 0x40}, + {0xd0eb, CRL_REG_LEN_08BIT, 0x09}, + {0xd0ec, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0ed, CRL_REG_LEN_08BIT, 0xc3}, + {0xd0ee, CRL_REG_LEN_08BIT, 0x38}, + {0xd0ef, CRL_REG_LEN_08BIT, 0x2a}, + {0xd0f0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f1, CRL_REG_LEN_08BIT, 0x07}, + {0xd0f2, CRL_REG_LEN_08BIT, 0x20}, + {0xd0f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0f5, CRL_REG_LEN_08BIT, 0x66}, + {0xd0f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f8, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f9, CRL_REG_LEN_08BIT, 0x05}, + {0xd0fa, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd0fc, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fd, CRL_REG_LEN_08BIT, 0x60}, + {0xd0fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ff, CRL_REG_LEN_08BIT, 0x01}, + {0xd100, CRL_REG_LEN_08BIT, 0x98}, + {0xd101, CRL_REG_LEN_08BIT, 0x90}, + {0xd102, CRL_REG_LEN_08BIT, 0x00}, + {0xd103, CRL_REG_LEN_08BIT, 0x00}, + {0xd104, CRL_REG_LEN_08BIT, 0x84}, + {0xd105, CRL_REG_LEN_08BIT, 0xae}, + {0xd106, CRL_REG_LEN_08BIT, 0x00}, + {0xd107, CRL_REG_LEN_08BIT, 0x00}, + {0xd108, CRL_REG_LEN_08BIT, 0xa8}, + {0xd109, CRL_REG_LEN_08BIT, 0x63}, + {0xd10a, CRL_REG_LEN_08BIT, 0x06}, + {0xd10b, CRL_REG_LEN_08BIT, 0x4c}, + {0xd10c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd10d, CRL_REG_LEN_08BIT, 0xc0}, + {0xd10e, CRL_REG_LEN_08BIT, 0x00}, + {0xd10f, CRL_REG_LEN_08BIT, 0x00}, + {0xd110, CRL_REG_LEN_08BIT, 0xd8}, + {0xd111, CRL_REG_LEN_08BIT, 0x03}, + {0xd112, CRL_REG_LEN_08BIT, 0x30}, + {0xd113, CRL_REG_LEN_08BIT, 0x00}, + {0xd114, CRL_REG_LEN_08BIT, 0x8c}, + {0xd115, CRL_REG_LEN_08BIT, 0x65}, + {0xd116, CRL_REG_LEN_08BIT, 0x00}, + {0xd117, CRL_REG_LEN_08BIT, 0x6e}, + {0xd118, CRL_REG_LEN_08BIT, 0xe5}, + {0xd119, CRL_REG_LEN_08BIT, 0x84}, + {0xd11a, CRL_REG_LEN_08BIT, 0x18}, + {0xd11b, CRL_REG_LEN_08BIT, 0x00}, + {0xd11c, CRL_REG_LEN_08BIT, 0x10}, + {0xd11d, CRL_REG_LEN_08BIT, 0x00}, + {0xd11e, CRL_REG_LEN_08BIT, 0x00}, + {0xd11f, CRL_REG_LEN_08BIT, 0x07}, + {0xd120, CRL_REG_LEN_08BIT, 0x18}, + {0xd121, CRL_REG_LEN_08BIT, 0x80}, + {0xd122, CRL_REG_LEN_08BIT, 0x80}, + {0xd123, CRL_REG_LEN_08BIT, 0x06}, + {0xd124, CRL_REG_LEN_08BIT, 0x94}, + {0xd125, CRL_REG_LEN_08BIT, 0x65}, + {0xd126, CRL_REG_LEN_08BIT, 0x00}, + {0xd127, CRL_REG_LEN_08BIT, 0x70}, + {0xd128, CRL_REG_LEN_08BIT, 0xe5}, + {0xd129, CRL_REG_LEN_08BIT, 0x43}, + {0xd12a, CRL_REG_LEN_08BIT, 0x60}, + {0xd12b, CRL_REG_LEN_08BIT, 0x00}, + {0xd12c, CRL_REG_LEN_08BIT, 0x0c}, + {0xd12d, CRL_REG_LEN_08BIT, 0x00}, + {0xd12e, CRL_REG_LEN_08BIT, 0x00}, + {0xd12f, CRL_REG_LEN_08BIT, 0x3e}, + {0xd130, CRL_REG_LEN_08BIT, 0xa8}, + {0xd131, CRL_REG_LEN_08BIT, 0x64}, + {0xd132, CRL_REG_LEN_08BIT, 0x38}, + {0xd133, CRL_REG_LEN_08BIT, 0x24}, + {0xd134, CRL_REG_LEN_08BIT, 0x18}, + {0xd135, CRL_REG_LEN_08BIT, 0x80}, + {0xd136, CRL_REG_LEN_08BIT, 0x80}, + {0xd137, CRL_REG_LEN_08BIT, 0x06}, + {0xd138, CRL_REG_LEN_08BIT, 0xa8}, + {0xd139, CRL_REG_LEN_08BIT, 0x64}, + {0xd13a, CRL_REG_LEN_08BIT, 0x38}, + {0xd13b, CRL_REG_LEN_08BIT, 0x24}, + {0xd13c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd13d, CRL_REG_LEN_08BIT, 0x63}, + {0xd13e, CRL_REG_LEN_08BIT, 0x00}, + {0xd13f, CRL_REG_LEN_08BIT, 0x00}, + {0xd140, CRL_REG_LEN_08BIT, 0xa4}, + {0xd141, CRL_REG_LEN_08BIT, 0x63}, + {0xd142, CRL_REG_LEN_08BIT, 0x00}, + {0xd143, CRL_REG_LEN_08BIT, 0x40}, + {0xd144, CRL_REG_LEN_08BIT, 0xbc}, + {0xd145, CRL_REG_LEN_08BIT, 0x23}, + {0xd146, CRL_REG_LEN_08BIT, 0x00}, + {0xd147, CRL_REG_LEN_08BIT, 0x00}, + {0xd148, CRL_REG_LEN_08BIT, 0x0c}, + {0xd149, CRL_REG_LEN_08BIT, 0x00}, + {0xd14a, CRL_REG_LEN_08BIT, 0x00}, + {0xd14b, CRL_REG_LEN_08BIT, 0x2a}, + {0xd14c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd14d, CRL_REG_LEN_08BIT, 0x64}, + {0xd14e, CRL_REG_LEN_08BIT, 0x6e}, + {0xd14f, CRL_REG_LEN_08BIT, 0x44}, + {0xd150, CRL_REG_LEN_08BIT, 0x19}, + {0xd151, CRL_REG_LEN_08BIT, 0x00}, + {0xd152, CRL_REG_LEN_08BIT, 0x80}, + {0xd153, CRL_REG_LEN_08BIT, 0x06}, + {0xd154, CRL_REG_LEN_08BIT, 0xa8}, + {0xd155, CRL_REG_LEN_08BIT, 0xe8}, + {0xd156, CRL_REG_LEN_08BIT, 0x3d}, + {0xd157, CRL_REG_LEN_08BIT, 0x05}, + {0xd158, CRL_REG_LEN_08BIT, 0x8c}, + {0xd159, CRL_REG_LEN_08BIT, 0x67}, + {0xd15a, CRL_REG_LEN_08BIT, 0x00}, + {0xd15b, CRL_REG_LEN_08BIT, 0x00}, + {0xd15c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd15d, CRL_REG_LEN_08BIT, 0x63}, + {0xd15e, CRL_REG_LEN_08BIT, 0x00}, + {0xd15f, CRL_REG_LEN_08BIT, 0x18}, + {0xd160, CRL_REG_LEN_08BIT, 0xb8}, + {0xd161, CRL_REG_LEN_08BIT, 0x63}, + {0xd162, CRL_REG_LEN_08BIT, 0x00}, + {0xd163, CRL_REG_LEN_08BIT, 0x98}, + {0xd164, CRL_REG_LEN_08BIT, 0xbc}, + {0xd165, CRL_REG_LEN_08BIT, 0x03}, + {0xd166, CRL_REG_LEN_08BIT, 0x00}, + {0xd167, CRL_REG_LEN_08BIT, 0x00}, + {0xd168, CRL_REG_LEN_08BIT, 0x10}, + {0xd169, CRL_REG_LEN_08BIT, 0x00}, + {0xd16a, CRL_REG_LEN_08BIT, 0x00}, + {0xd16b, CRL_REG_LEN_08BIT, 0x10}, + {0xd16c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd16d, CRL_REG_LEN_08BIT, 0x48}, + {0xd16e, CRL_REG_LEN_08BIT, 0x67}, + {0xd16f, CRL_REG_LEN_08BIT, 0x02}, + {0xd170, CRL_REG_LEN_08BIT, 0xb8}, + {0xd171, CRL_REG_LEN_08BIT, 0xa3}, + {0xd172, CRL_REG_LEN_08BIT, 0x00}, + {0xd173, CRL_REG_LEN_08BIT, 0x19}, + {0xd174, CRL_REG_LEN_08BIT, 0x8c}, + {0xd175, CRL_REG_LEN_08BIT, 0x8a}, + {0xd176, CRL_REG_LEN_08BIT, 0x00}, + {0xd177, CRL_REG_LEN_08BIT, 0x00}, + {0xd178, CRL_REG_LEN_08BIT, 0xa9}, + {0xd179, CRL_REG_LEN_08BIT, 0x68}, + {0xd17a, CRL_REG_LEN_08BIT, 0x67}, + {0xd17b, CRL_REG_LEN_08BIT, 0x03}, + {0xd17c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd17d, CRL_REG_LEN_08BIT, 0xc4}, + {0xd17e, CRL_REG_LEN_08BIT, 0x00}, + {0xd17f, CRL_REG_LEN_08BIT, 0x08}, + {0xd180, CRL_REG_LEN_08BIT, 0x8c}, + {0xd181, CRL_REG_LEN_08BIT, 0x6b}, + {0xd182, CRL_REG_LEN_08BIT, 0x00}, + {0xd183, CRL_REG_LEN_08BIT, 0x00}, + {0xd184, CRL_REG_LEN_08BIT, 0xb8}, + {0xd185, CRL_REG_LEN_08BIT, 0x85}, + {0xd186, CRL_REG_LEN_08BIT, 0x00}, + {0xd187, CRL_REG_LEN_08BIT, 0x98}, + {0xd188, CRL_REG_LEN_08BIT, 0xe0}, + {0xd189, CRL_REG_LEN_08BIT, 0x63}, + {0xd18a, CRL_REG_LEN_08BIT, 0x30}, + {0xd18b, CRL_REG_LEN_08BIT, 0x04}, + {0xd18c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd18d, CRL_REG_LEN_08BIT, 0x64}, + {0xd18e, CRL_REG_LEN_08BIT, 0x18}, + {0xd18f, CRL_REG_LEN_08BIT, 0x00}, + {0xd190, CRL_REG_LEN_08BIT, 0xa4}, + {0xd191, CRL_REG_LEN_08BIT, 0x83}, + {0xd192, CRL_REG_LEN_08BIT, 0xff}, + {0xd193, CRL_REG_LEN_08BIT, 0xff}, + {0xd194, CRL_REG_LEN_08BIT, 0xb8}, + {0xd195, CRL_REG_LEN_08BIT, 0x64}, + {0xd196, CRL_REG_LEN_08BIT, 0x00}, + {0xd197, CRL_REG_LEN_08BIT, 0x48}, + {0xd198, CRL_REG_LEN_08BIT, 0xd8}, + {0xd199, CRL_REG_LEN_08BIT, 0x0a}, + {0xd19a, CRL_REG_LEN_08BIT, 0x18}, + {0xd19b, CRL_REG_LEN_08BIT, 0x00}, + {0xd19c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd19d, CRL_REG_LEN_08BIT, 0x0b}, + {0xd19e, CRL_REG_LEN_08BIT, 0x20}, + {0xd19f, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a0, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1a1, CRL_REG_LEN_08BIT, 0x60}, + {0xd1a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1a5, CRL_REG_LEN_08BIT, 0x07}, + {0xd1a6, CRL_REG_LEN_08BIT, 0x18}, + {0xd1a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1a9, CRL_REG_LEN_08BIT, 0x68}, + {0xd1aa, CRL_REG_LEN_08BIT, 0x38}, + {0xd1ab, CRL_REG_LEN_08BIT, 0x22}, + {0xd1ac, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ad, CRL_REG_LEN_08BIT, 0x80}, + {0xd1ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd1af, CRL_REG_LEN_08BIT, 0x70}, + {0xd1b0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1b1, CRL_REG_LEN_08BIT, 0xe8}, + {0xd1b2, CRL_REG_LEN_08BIT, 0x38}, + {0xd1b3, CRL_REG_LEN_08BIT, 0x43}, + {0xd1b4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1b5, CRL_REG_LEN_08BIT, 0x03}, + {0xd1b6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1b8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1b9, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bb, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1bd, CRL_REG_LEN_08BIT, 0xc8}, + {0xd1be, CRL_REG_LEN_08BIT, 0x38}, + {0xd1bf, CRL_REG_LEN_08BIT, 0x42}, + {0xd1c0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1c1, CRL_REG_LEN_08BIT, 0x66}, + {0xd1c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1c5, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c7, CRL_REG_LEN_08BIT, 0x01}, + {0xd1c8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd1c9, CRL_REG_LEN_08BIT, 0x83}, + {0xd1ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cb, CRL_REG_LEN_08BIT, 0x08}, + {0xd1cc, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1cd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cf, CRL_REG_LEN_08BIT, 0xff}, + {0xd1d0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1d1, CRL_REG_LEN_08BIT, 0x67}, + {0xd1d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd1d5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1d6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d8, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1d9, CRL_REG_LEN_08BIT, 0x63}, + {0xd1da, CRL_REG_LEN_08BIT, 0xff}, + {0xd1db, CRL_REG_LEN_08BIT, 0xff}, + {0xd1dc, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1dd, CRL_REG_LEN_08BIT, 0x43}, + {0xd1de, CRL_REG_LEN_08BIT, 0x00}, + {0xd1df, CRL_REG_LEN_08BIT, 0x07}, + {0xd1e0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd1e1, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e3, CRL_REG_LEN_08BIT, 0x5b}, + {0xd1e4, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1e5, CRL_REG_LEN_08BIT, 0x05}, + {0xd1e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e7, CRL_REG_LEN_08BIT, 0x02}, + {0xd1e8, CRL_REG_LEN_08BIT, 0x03}, + {0xd1e9, CRL_REG_LEN_08BIT, 0xff}, + {0xd1ea, CRL_REG_LEN_08BIT, 0xff}, + {0xd1eb, CRL_REG_LEN_08BIT, 0xf6}, + {0xd1ec, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd1ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f1, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1f2, CRL_REG_LEN_08BIT, 0x55}, + {0xd1f3, CRL_REG_LEN_08BIT, 0x86}, + {0xd1f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1f5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f9, CRL_REG_LEN_08BIT, 0xc4}, + {0xd1fa, CRL_REG_LEN_08BIT, 0x6e}, + {0xd1fb, CRL_REG_LEN_08BIT, 0x45}, + {0xd1fc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1fd, CRL_REG_LEN_08BIT, 0xe4}, + {0xd1fe, CRL_REG_LEN_08BIT, 0x55}, + {0xd1ff, CRL_REG_LEN_08BIT, 0x87}, + {0xd200, CRL_REG_LEN_08BIT, 0xd8}, + {0xd201, CRL_REG_LEN_08BIT, 0x05}, + {0xd202, CRL_REG_LEN_08BIT, 0x18}, + {0xd203, CRL_REG_LEN_08BIT, 0x00}, + {0xd204, CRL_REG_LEN_08BIT, 0x8c}, + {0xd205, CRL_REG_LEN_08BIT, 0x66}, + {0xd206, CRL_REG_LEN_08BIT, 0x00}, + {0xd207, CRL_REG_LEN_08BIT, 0x00}, + {0xd208, CRL_REG_LEN_08BIT, 0xa8}, + {0xd209, CRL_REG_LEN_08BIT, 0xa4}, + {0xd20a, CRL_REG_LEN_08BIT, 0x6e}, + {0xd20b, CRL_REG_LEN_08BIT, 0x46}, + {0xd20c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd20d, CRL_REG_LEN_08BIT, 0x07}, + {0xd20e, CRL_REG_LEN_08BIT, 0x18}, + {0xd20f, CRL_REG_LEN_08BIT, 0x00}, + {0xd210, CRL_REG_LEN_08BIT, 0xa8}, + {0xd211, CRL_REG_LEN_08BIT, 0x84}, + {0xd212, CRL_REG_LEN_08BIT, 0x55}, + {0xd213, CRL_REG_LEN_08BIT, 0x88}, + {0xd214, CRL_REG_LEN_08BIT, 0x8c}, + {0xd215, CRL_REG_LEN_08BIT, 0x65}, + {0xd216, CRL_REG_LEN_08BIT, 0x00}, + {0xd217, CRL_REG_LEN_08BIT, 0x00}, + {0xd218, CRL_REG_LEN_08BIT, 0xd8}, + {0xd219, CRL_REG_LEN_08BIT, 0x04}, + {0xd21a, CRL_REG_LEN_08BIT, 0x18}, + {0xd21b, CRL_REG_LEN_08BIT, 0x00}, + {0xd21c, CRL_REG_LEN_08BIT, 0x03}, + {0xd21d, CRL_REG_LEN_08BIT, 0xff}, + {0xd21e, CRL_REG_LEN_08BIT, 0xff}, + {0xd21f, CRL_REG_LEN_08BIT, 0xce}, + {0xd220, CRL_REG_LEN_08BIT, 0x19}, + {0xd221, CRL_REG_LEN_08BIT, 0x00}, + {0xd222, CRL_REG_LEN_08BIT, 0x80}, + {0xd223, CRL_REG_LEN_08BIT, 0x06}, + {0xd224, CRL_REG_LEN_08BIT, 0x8c}, + {0xd225, CRL_REG_LEN_08BIT, 0x63}, + {0xd226, CRL_REG_LEN_08BIT, 0x00}, + {0xd227, CRL_REG_LEN_08BIT, 0x00}, + {0xd228, CRL_REG_LEN_08BIT, 0xa4}, + {0xd229, CRL_REG_LEN_08BIT, 0x63}, + {0xd22a, CRL_REG_LEN_08BIT, 0x00}, + {0xd22b, CRL_REG_LEN_08BIT, 0x40}, + {0xd22c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd22d, CRL_REG_LEN_08BIT, 0x23}, + {0xd22e, CRL_REG_LEN_08BIT, 0x00}, + {0xd22f, CRL_REG_LEN_08BIT, 0x00}, + {0xd230, CRL_REG_LEN_08BIT, 0x13}, + {0xd231, CRL_REG_LEN_08BIT, 0xff}, + {0xd232, CRL_REG_LEN_08BIT, 0xff}, + {0xd233, CRL_REG_LEN_08BIT, 0xc8}, + {0xd234, CRL_REG_LEN_08BIT, 0x9d}, + {0xd235, CRL_REG_LEN_08BIT, 0x00}, + {0xd236, CRL_REG_LEN_08BIT, 0x00}, + {0xd237, CRL_REG_LEN_08BIT, 0x40}, + {0xd238, CRL_REG_LEN_08BIT, 0xa8}, + {0xd239, CRL_REG_LEN_08BIT, 0x64}, + {0xd23a, CRL_REG_LEN_08BIT, 0x55}, + {0xd23b, CRL_REG_LEN_08BIT, 0x86}, + {0xd23c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd23d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd23e, CRL_REG_LEN_08BIT, 0x55}, + {0xd23f, CRL_REG_LEN_08BIT, 0x87}, + {0xd240, CRL_REG_LEN_08BIT, 0xd8}, + {0xd241, CRL_REG_LEN_08BIT, 0x03}, + {0xd242, CRL_REG_LEN_08BIT, 0x40}, + {0xd243, CRL_REG_LEN_08BIT, 0x00}, + {0xd244, CRL_REG_LEN_08BIT, 0xa8}, + {0xd245, CRL_REG_LEN_08BIT, 0x64}, + {0xd246, CRL_REG_LEN_08BIT, 0x55}, + {0xd247, CRL_REG_LEN_08BIT, 0x88}, + {0xd248, CRL_REG_LEN_08BIT, 0xd8}, + {0xd249, CRL_REG_LEN_08BIT, 0x05}, + {0xd24a, CRL_REG_LEN_08BIT, 0x40}, + {0xd24b, CRL_REG_LEN_08BIT, 0x00}, + {0xd24c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd24d, CRL_REG_LEN_08BIT, 0x03}, + {0xd24e, CRL_REG_LEN_08BIT, 0x40}, + {0xd24f, CRL_REG_LEN_08BIT, 0x00}, + {0xd250, CRL_REG_LEN_08BIT, 0x03}, + {0xd251, CRL_REG_LEN_08BIT, 0xff}, + {0xd252, CRL_REG_LEN_08BIT, 0xff}, + {0xd253, CRL_REG_LEN_08BIT, 0xc1}, + {0xd254, CRL_REG_LEN_08BIT, 0x19}, + {0xd255, CRL_REG_LEN_08BIT, 0x00}, + {0xd256, CRL_REG_LEN_08BIT, 0x80}, + {0xd257, CRL_REG_LEN_08BIT, 0x06}, + {0xd258, CRL_REG_LEN_08BIT, 0x94}, + {0xd259, CRL_REG_LEN_08BIT, 0x84}, + {0xd25a, CRL_REG_LEN_08BIT, 0x00}, + {0xd25b, CRL_REG_LEN_08BIT, 0x72}, + {0xd25c, CRL_REG_LEN_08BIT, 0xe5}, + {0xd25d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd25e, CRL_REG_LEN_08BIT, 0x60}, + {0xd25f, CRL_REG_LEN_08BIT, 0x00}, + {0xd260, CRL_REG_LEN_08BIT, 0x0c}, + {0xd261, CRL_REG_LEN_08BIT, 0x00}, + {0xd262, CRL_REG_LEN_08BIT, 0x00}, + {0xd263, CRL_REG_LEN_08BIT, 0x4d}, + {0xd264, CRL_REG_LEN_08BIT, 0x9d}, + {0xd265, CRL_REG_LEN_08BIT, 0x60}, + {0xd266, CRL_REG_LEN_08BIT, 0x01}, + {0xd267, CRL_REG_LEN_08BIT, 0x00}, + {0xd268, CRL_REG_LEN_08BIT, 0x85}, + {0xd269, CRL_REG_LEN_08BIT, 0x4e}, + {0xd26a, CRL_REG_LEN_08BIT, 0x00}, + {0xd26b, CRL_REG_LEN_08BIT, 0x00}, + {0xd26c, CRL_REG_LEN_08BIT, 0x98}, + {0xd26d, CRL_REG_LEN_08BIT, 0x70}, + {0xd26e, CRL_REG_LEN_08BIT, 0x00}, + {0xd26f, CRL_REG_LEN_08BIT, 0x00}, + {0xd270, CRL_REG_LEN_08BIT, 0x8c}, + {0xd271, CRL_REG_LEN_08BIT, 0x8a}, + {0xd272, CRL_REG_LEN_08BIT, 0x00}, + {0xd273, CRL_REG_LEN_08BIT, 0x6f}, + {0xd274, CRL_REG_LEN_08BIT, 0xe5}, + {0xd275, CRL_REG_LEN_08BIT, 0x63}, + {0xd276, CRL_REG_LEN_08BIT, 0x20}, + {0xd277, CRL_REG_LEN_08BIT, 0x00}, + {0xd278, CRL_REG_LEN_08BIT, 0x10}, + {0xd279, CRL_REG_LEN_08BIT, 0x00}, + {0xd27a, CRL_REG_LEN_08BIT, 0x00}, + {0xd27b, CRL_REG_LEN_08BIT, 0x07}, + {0xd27c, CRL_REG_LEN_08BIT, 0x15}, + {0xd27d, CRL_REG_LEN_08BIT, 0x00}, + {0xd27e, CRL_REG_LEN_08BIT, 0x00}, + {0xd27f, CRL_REG_LEN_08BIT, 0x00}, + {0xd280, CRL_REG_LEN_08BIT, 0x8c}, + {0xd281, CRL_REG_LEN_08BIT, 0xaa}, + {0xd282, CRL_REG_LEN_08BIT, 0x00}, + {0xd283, CRL_REG_LEN_08BIT, 0x6e}, + {0xd284, CRL_REG_LEN_08BIT, 0xe0}, + {0xd285, CRL_REG_LEN_08BIT, 0x63}, + {0xd286, CRL_REG_LEN_08BIT, 0x28}, + {0xd287, CRL_REG_LEN_08BIT, 0x02}, + {0xd288, CRL_REG_LEN_08BIT, 0xe0}, + {0xd289, CRL_REG_LEN_08BIT, 0x84}, + {0xd28a, CRL_REG_LEN_08BIT, 0x28}, + {0xd28b, CRL_REG_LEN_08BIT, 0x02}, + {0xd28c, CRL_REG_LEN_08BIT, 0x07}, + {0xd28d, CRL_REG_LEN_08BIT, 0xff}, + {0xd28e, CRL_REG_LEN_08BIT, 0xf8}, + {0xd28f, CRL_REG_LEN_08BIT, 0x66}, + {0xd290, CRL_REG_LEN_08BIT, 0xe0}, + {0xd291, CRL_REG_LEN_08BIT, 0x63}, + {0xd292, CRL_REG_LEN_08BIT, 0x5b}, + {0xd293, CRL_REG_LEN_08BIT, 0x06}, + {0xd294, CRL_REG_LEN_08BIT, 0x8c}, + {0xd295, CRL_REG_LEN_08BIT, 0x6a}, + {0xd296, CRL_REG_LEN_08BIT, 0x00}, + {0xd297, CRL_REG_LEN_08BIT, 0x77}, + {0xd298, CRL_REG_LEN_08BIT, 0xe0}, + {0xd299, CRL_REG_LEN_08BIT, 0x63}, + {0xd29a, CRL_REG_LEN_08BIT, 0x5b}, + {0xd29b, CRL_REG_LEN_08BIT, 0x06}, + {0xd29c, CRL_REG_LEN_08BIT, 0xbd}, + {0xd29d, CRL_REG_LEN_08BIT, 0x63}, + {0xd29e, CRL_REG_LEN_08BIT, 0x00}, + {0xd29f, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2a1, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a3, CRL_REG_LEN_08BIT, 0x5a}, + {0xd2a4, CRL_REG_LEN_08BIT, 0x15}, + {0xd2a5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd2a9, CRL_REG_LEN_08BIT, 0x8a}, + {0xd2aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ab, CRL_REG_LEN_08BIT, 0x78}, + {0xd2ac, CRL_REG_LEN_08BIT, 0xb8}, + {0xd2ad, CRL_REG_LEN_08BIT, 0x63}, + {0xd2ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd2af, CRL_REG_LEN_08BIT, 0x88}, + {0xd2b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd2b1, CRL_REG_LEN_08BIT, 0x64}, + {0xd2b2, CRL_REG_LEN_08BIT, 0x5b}, + {0xd2b3, CRL_REG_LEN_08BIT, 0x06}, + {0xd2b4, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2b5, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b8, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2b9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd2bb, CRL_REG_LEN_08BIT, 0x59}, + {0xd2bc, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2bd, CRL_REG_LEN_08BIT, 0x01}, + {0xd2be, CRL_REG_LEN_08BIT, 0x18}, + {0xd2bf, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c0, CRL_REG_LEN_08BIT, 0xb9}, + {0xd2c1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c3, CRL_REG_LEN_08BIT, 0x88}, + {0xd2c4, CRL_REG_LEN_08BIT, 0x85}, + {0xd2c5, CRL_REG_LEN_08BIT, 0x01}, + {0xd2c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2c9, CRL_REG_LEN_08BIT, 0x68}, + {0xd2ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cc, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2cd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cf, CRL_REG_LEN_08BIT, 0x51}, + {0xd2d0, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2d1, CRL_REG_LEN_08BIT, 0x01}, + {0xd2d2, CRL_REG_LEN_08BIT, 0x58}, + {0xd2d3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2d4, CRL_REG_LEN_08BIT, 0x84}, + {0xd2d5, CRL_REG_LEN_08BIT, 0x81}, + {0xd2d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2d7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2d8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2d9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd2da, CRL_REG_LEN_08BIT, 0x01}, + {0xd2db, CRL_REG_LEN_08BIT, 0x00}, + {0xd2dc, CRL_REG_LEN_08BIT, 0x10}, + {0xd2dd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2de, CRL_REG_LEN_08BIT, 0x00}, + {0xd2df, CRL_REG_LEN_08BIT, 0x05}, + {0xd2e0, CRL_REG_LEN_08BIT, 0x84}, + {0xd2e1, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2e4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd2e5, CRL_REG_LEN_08BIT, 0xa0}, + {0xd2e6, CRL_REG_LEN_08BIT, 0x01}, + {0xd2e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e8, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xd2ea, CRL_REG_LEN_08BIT, 0x28}, + {0xd2eb, CRL_REG_LEN_08BIT, 0x14}, + {0xd2ec, CRL_REG_LEN_08BIT, 0x84}, + {0xd2ed, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ef, CRL_REG_LEN_08BIT, 0x18}, + {0xd2f0, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2f1, CRL_REG_LEN_08BIT, 0x66}, + {0xd2f2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f4, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f7, CRL_REG_LEN_08BIT, 0x43}, + {0xd2f8, CRL_REG_LEN_08BIT, 0x9d}, + {0xd2f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fc, CRL_REG_LEN_08BIT, 0x84}, + {0xd2fd, CRL_REG_LEN_08BIT, 0x61}, + {0xd2fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ff, CRL_REG_LEN_08BIT, 0x18}, + {0xd300, CRL_REG_LEN_08BIT, 0xbd}, + {0xd301, CRL_REG_LEN_08BIT, 0xa3}, + {0xd302, CRL_REG_LEN_08BIT, 0x01}, + {0xd303, CRL_REG_LEN_08BIT, 0x00}, + {0xd304, CRL_REG_LEN_08BIT, 0x10}, + {0xd305, CRL_REG_LEN_08BIT, 0x00}, + {0xd306, CRL_REG_LEN_08BIT, 0x00}, + {0xd307, CRL_REG_LEN_08BIT, 0x03}, + {0xd308, CRL_REG_LEN_08BIT, 0x9c}, + {0xd309, CRL_REG_LEN_08BIT, 0x80}, + {0xd30a, CRL_REG_LEN_08BIT, 0x01}, + {0xd30b, CRL_REG_LEN_08BIT, 0x00}, + {0xd30c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd30d, CRL_REG_LEN_08BIT, 0x01}, + {0xd30e, CRL_REG_LEN_08BIT, 0x20}, + {0xd30f, CRL_REG_LEN_08BIT, 0x18}, + {0xd310, CRL_REG_LEN_08BIT, 0x18}, + {0xd311, CRL_REG_LEN_08BIT, 0x60}, + {0xd312, CRL_REG_LEN_08BIT, 0x80}, + {0xd313, CRL_REG_LEN_08BIT, 0x06}, + {0xd314, CRL_REG_LEN_08BIT, 0x85}, + {0xd315, CRL_REG_LEN_08BIT, 0x01}, + {0xd316, CRL_REG_LEN_08BIT, 0x00}, + {0xd317, CRL_REG_LEN_08BIT, 0x14}, + {0xd318, CRL_REG_LEN_08BIT, 0xa8}, + {0xd319, CRL_REG_LEN_08BIT, 0x83}, + {0xd31a, CRL_REG_LEN_08BIT, 0x38}, + {0xd31b, CRL_REG_LEN_08BIT, 0x29}, + {0xd31c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd31d, CRL_REG_LEN_08BIT, 0xc3}, + {0xd31e, CRL_REG_LEN_08BIT, 0x40}, + {0xd31f, CRL_REG_LEN_08BIT, 0x08}, + {0xd320, CRL_REG_LEN_08BIT, 0x8c}, + {0xd321, CRL_REG_LEN_08BIT, 0x84}, + {0xd322, CRL_REG_LEN_08BIT, 0x00}, + {0xd323, CRL_REG_LEN_08BIT, 0x00}, + {0xd324, CRL_REG_LEN_08BIT, 0xa8}, + {0xd325, CRL_REG_LEN_08BIT, 0xa3}, + {0xd326, CRL_REG_LEN_08BIT, 0x38}, + {0xd327, CRL_REG_LEN_08BIT, 0x2a}, + {0xd328, CRL_REG_LEN_08BIT, 0xa8}, + {0xd329, CRL_REG_LEN_08BIT, 0xe3}, + {0xd32a, CRL_REG_LEN_08BIT, 0x40}, + {0xd32b, CRL_REG_LEN_08BIT, 0x09}, + {0xd32c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd32d, CRL_REG_LEN_08BIT, 0x64}, + {0xd32e, CRL_REG_LEN_08BIT, 0x40}, + {0xd32f, CRL_REG_LEN_08BIT, 0x00}, + {0xd330, CRL_REG_LEN_08BIT, 0xd8}, + {0xd331, CRL_REG_LEN_08BIT, 0x06}, + {0xd332, CRL_REG_LEN_08BIT, 0x18}, + {0xd333, CRL_REG_LEN_08BIT, 0x00}, + {0xd334, CRL_REG_LEN_08BIT, 0x8c}, + {0xd335, CRL_REG_LEN_08BIT, 0x65}, + {0xd336, CRL_REG_LEN_08BIT, 0x00}, + {0xd337, CRL_REG_LEN_08BIT, 0x00}, + {0xd338, CRL_REG_LEN_08BIT, 0x84}, + {0xd339, CRL_REG_LEN_08BIT, 0x81}, + {0xd33a, CRL_REG_LEN_08BIT, 0x00}, + {0xd33b, CRL_REG_LEN_08BIT, 0x18}, + {0xd33c, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33d, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33e, CRL_REG_LEN_08BIT, 0x20}, + {0xd33f, CRL_REG_LEN_08BIT, 0x00}, + {0xd340, CRL_REG_LEN_08BIT, 0xd8}, + {0xd341, CRL_REG_LEN_08BIT, 0x07}, + {0xd342, CRL_REG_LEN_08BIT, 0xf8}, + {0xd343, CRL_REG_LEN_08BIT, 0x00}, + {0xd344, CRL_REG_LEN_08BIT, 0x03}, + {0xd345, CRL_REG_LEN_08BIT, 0xff}, + {0xd346, CRL_REG_LEN_08BIT, 0xff}, + {0xd347, CRL_REG_LEN_08BIT, 0x6f}, + {0xd348, CRL_REG_LEN_08BIT, 0x18}, + {0xd349, CRL_REG_LEN_08BIT, 0x60}, + {0xd34a, CRL_REG_LEN_08BIT, 0x00}, + {0xd34b, CRL_REG_LEN_08BIT, 0x01}, + {0xd34c, CRL_REG_LEN_08BIT, 0x0f}, + {0xd34d, CRL_REG_LEN_08BIT, 0xff}, + {0xd34e, CRL_REG_LEN_08BIT, 0xff}, + {0xd34f, CRL_REG_LEN_08BIT, 0x9d}, + {0xd350, CRL_REG_LEN_08BIT, 0x18}, + {0xd351, CRL_REG_LEN_08BIT, 0x60}, + {0xd352, CRL_REG_LEN_08BIT, 0x80}, + {0xd353, CRL_REG_LEN_08BIT, 0x06}, + {0xd354, CRL_REG_LEN_08BIT, 0xa8}, + {0xd355, CRL_REG_LEN_08BIT, 0x83}, + {0xd356, CRL_REG_LEN_08BIT, 0x6e}, + {0xd357, CRL_REG_LEN_08BIT, 0x43}, + {0xd358, CRL_REG_LEN_08BIT, 0xa8}, + {0xd359, CRL_REG_LEN_08BIT, 0xa3}, + {0xd35a, CRL_REG_LEN_08BIT, 0x38}, + {0xd35b, CRL_REG_LEN_08BIT, 0x0f}, + {0xd35c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd35d, CRL_REG_LEN_08BIT, 0x84}, + {0xd35e, CRL_REG_LEN_08BIT, 0x00}, + {0xd35f, CRL_REG_LEN_08BIT, 0x00}, + {0xd360, CRL_REG_LEN_08BIT, 0xa8}, + {0xd361, CRL_REG_LEN_08BIT, 0xc3}, + {0xd362, CRL_REG_LEN_08BIT, 0x38}, + {0xd363, CRL_REG_LEN_08BIT, 0x0e}, + {0xd364, CRL_REG_LEN_08BIT, 0xa8}, + {0xd365, CRL_REG_LEN_08BIT, 0xe3}, + {0xd366, CRL_REG_LEN_08BIT, 0x6e}, + {0xd367, CRL_REG_LEN_08BIT, 0x42}, + {0xd368, CRL_REG_LEN_08BIT, 0xd8}, + {0xd369, CRL_REG_LEN_08BIT, 0x05}, + {0xd36a, CRL_REG_LEN_08BIT, 0x20}, + {0xd36b, CRL_REG_LEN_08BIT, 0x00}, + {0xd36c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd36d, CRL_REG_LEN_08BIT, 0x67}, + {0xd36e, CRL_REG_LEN_08BIT, 0x00}, + {0xd36f, CRL_REG_LEN_08BIT, 0x00}, + {0xd370, CRL_REG_LEN_08BIT, 0xd8}, + {0xd371, CRL_REG_LEN_08BIT, 0x06}, + {0xd372, CRL_REG_LEN_08BIT, 0x18}, + {0xd373, CRL_REG_LEN_08BIT, 0x00}, + {0xd374, CRL_REG_LEN_08BIT, 0x18}, + {0xd375, CRL_REG_LEN_08BIT, 0x60}, + {0xd376, CRL_REG_LEN_08BIT, 0x80}, + {0xd377, CRL_REG_LEN_08BIT, 0x01}, + {0xd378, CRL_REG_LEN_08BIT, 0xa8}, + {0xd379, CRL_REG_LEN_08BIT, 0x63}, + {0xd37a, CRL_REG_LEN_08BIT, 0x00}, + {0xd37b, CRL_REG_LEN_08BIT, 0xc8}, + {0xd37c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd37d, CRL_REG_LEN_08BIT, 0x63}, + {0xd37e, CRL_REG_LEN_08BIT, 0x00}, + {0xd37f, CRL_REG_LEN_08BIT, 0x00}, + {0xd380, CRL_REG_LEN_08BIT, 0xbc}, + {0xd381, CRL_REG_LEN_08BIT, 0x23}, + {0xd382, CRL_REG_LEN_08BIT, 0x00}, + {0xd383, CRL_REG_LEN_08BIT, 0x01}, + {0xd384, CRL_REG_LEN_08BIT, 0x10}, + {0xd385, CRL_REG_LEN_08BIT, 0x00}, + {0xd386, CRL_REG_LEN_08BIT, 0x00}, + {0xd387, CRL_REG_LEN_08BIT, 0x28}, + {0xd388, CRL_REG_LEN_08BIT, 0x9c}, + {0xd389, CRL_REG_LEN_08BIT, 0xa0}, + {0xd38a, CRL_REG_LEN_08BIT, 0x00}, + {0xd38b, CRL_REG_LEN_08BIT, 0x00}, + {0xd38c, CRL_REG_LEN_08BIT, 0x00}, + {0xd38d, CRL_REG_LEN_08BIT, 0x00}, + {0xd38e, CRL_REG_LEN_08BIT, 0x00}, + {0xd38f, CRL_REG_LEN_08BIT, 0x08}, + {0xd390, CRL_REG_LEN_08BIT, 0x15}, + {0xd391, CRL_REG_LEN_08BIT, 0x00}, + {0xd392, CRL_REG_LEN_08BIT, 0x00}, + {0xd393, CRL_REG_LEN_08BIT, 0x00}, + {0xd394, CRL_REG_LEN_08BIT, 0xe0}, + {0xd395, CRL_REG_LEN_08BIT, 0x6c}, + {0xd396, CRL_REG_LEN_08BIT, 0x28}, + {0xd397, CRL_REG_LEN_08BIT, 0x02}, + {0xd398, CRL_REG_LEN_08BIT, 0xe0}, + {0xd399, CRL_REG_LEN_08BIT, 0x84}, + {0xd39a, CRL_REG_LEN_08BIT, 0x28}, + {0xd39b, CRL_REG_LEN_08BIT, 0x02}, + {0xd39c, CRL_REG_LEN_08BIT, 0x07}, + {0xd39d, CRL_REG_LEN_08BIT, 0xff}, + {0xd39e, CRL_REG_LEN_08BIT, 0xf8}, + {0xd39f, CRL_REG_LEN_08BIT, 0x22}, + {0xd3a0, CRL_REG_LEN_08BIT, 0xb8}, + {0xd3a1, CRL_REG_LEN_08BIT, 0x63}, + {0xd3a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd3a3, CRL_REG_LEN_08BIT, 0x08}, + {0xd3a4, CRL_REG_LEN_08BIT, 0x03}, + {0xd3a5, CRL_REG_LEN_08BIT, 0xff}, + {0xd3a6, CRL_REG_LEN_08BIT, 0xff}, + {0xd3a7, CRL_REG_LEN_08BIT, 0xb2}, + {0xd3a8, CRL_REG_LEN_08BIT, 0x85}, + {0xd3a9, CRL_REG_LEN_08BIT, 0x4e}, + {0xd3aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ac, CRL_REG_LEN_08BIT, 0x18}, + {0xd3ad, CRL_REG_LEN_08BIT, 0xe0}, + {0xd3ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd3af, CRL_REG_LEN_08BIT, 0x01}, + {0xd3b0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3b1, CRL_REG_LEN_08BIT, 0xe7}, + {0xd3b2, CRL_REG_LEN_08BIT, 0x06}, + {0xd3b3, CRL_REG_LEN_08BIT, 0x55}, + {0xd3b4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3b5, CRL_REG_LEN_08BIT, 0x87}, + {0xd3b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd3b9, CRL_REG_LEN_08BIT, 0x64}, + {0xd3ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd3bb, CRL_REG_LEN_08BIT, 0x02}, + {0xd3bc, CRL_REG_LEN_08BIT, 0x19}, + {0xd3bd, CRL_REG_LEN_08BIT, 0x00}, + {0xd3be, CRL_REG_LEN_08BIT, 0x80}, + {0xd3bf, CRL_REG_LEN_08BIT, 0x06}, + {0xd3c0, CRL_REG_LEN_08BIT, 0xe0}, + {0xd3c1, CRL_REG_LEN_08BIT, 0x63}, + {0xd3c2, CRL_REG_LEN_08BIT, 0x20}, + {0xd3c3, CRL_REG_LEN_08BIT, 0x00}, + {0xd3c4, CRL_REG_LEN_08BIT, 0xa9}, + {0xd3c5, CRL_REG_LEN_08BIT, 0x08}, + {0xd3c6, CRL_REG_LEN_08BIT, 0x56}, + {0xd3c7, CRL_REG_LEN_08BIT, 0x01}, + {0xd3c8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd3c9, CRL_REG_LEN_08BIT, 0x63}, + {0xd3ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd3cb, CRL_REG_LEN_08BIT, 0x04}, + {0xd3cc, CRL_REG_LEN_08BIT, 0x18}, + {0xd3cd, CRL_REG_LEN_08BIT, 0x80}, + {0xd3ce, CRL_REG_LEN_08BIT, 0x80}, + {0xd3cf, CRL_REG_LEN_08BIT, 0x01}, + {0xd3d0, CRL_REG_LEN_08BIT, 0xe0}, + {0xd3d1, CRL_REG_LEN_08BIT, 0xc5}, + {0xd3d2, CRL_REG_LEN_08BIT, 0x40}, + {0xd3d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd3d4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd3d5, CRL_REG_LEN_08BIT, 0x63}, + {0xd3d6, CRL_REG_LEN_08BIT, 0x28}, + {0xd3d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3d8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3d9, CRL_REG_LEN_08BIT, 0x84}, + {0xd3da, CRL_REG_LEN_08BIT, 0x1d}, + {0xd3db, CRL_REG_LEN_08BIT, 0x00}, + {0xd3dc, CRL_REG_LEN_08BIT, 0x9c}, + {0xd3dd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd3de, CRL_REG_LEN_08BIT, 0x00}, + {0xd3df, CRL_REG_LEN_08BIT, 0x01}, + {0xd3e0, CRL_REG_LEN_08BIT, 0xe0}, + {0xd3e1, CRL_REG_LEN_08BIT, 0x63}, + {0xd3e2, CRL_REG_LEN_08BIT, 0x20}, + {0xd3e3, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e4, CRL_REG_LEN_08BIT, 0xbd}, + {0xd3e5, CRL_REG_LEN_08BIT, 0x45}, + {0xd3e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e7, CRL_REG_LEN_08BIT, 0x48}, + {0xd3e8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3e9, CRL_REG_LEN_08BIT, 0x63}, + {0xd3ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd3eb, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ec, CRL_REG_LEN_08BIT, 0xd8}, + {0xd3ed, CRL_REG_LEN_08BIT, 0x06}, + {0xd3ee, CRL_REG_LEN_08BIT, 0x18}, + {0xd3ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f0, CRL_REG_LEN_08BIT, 0x0f}, + {0xd3f1, CRL_REG_LEN_08BIT, 0xff}, + {0xd3f2, CRL_REG_LEN_08BIT, 0xff}, + {0xd3f3, CRL_REG_LEN_08BIT, 0xf1}, + {0xd3f4, CRL_REG_LEN_08BIT, 0x15}, + {0xd3f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f8, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd3fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd3fb, CRL_REG_LEN_08BIT, 0x0b}, + {0xd3fc, CRL_REG_LEN_08BIT, 0x15}, + {0xd3fd, CRL_REG_LEN_08BIT, 0x00}, + {0xd3fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ff, CRL_REG_LEN_08BIT, 0x00}, + {0xd400, CRL_REG_LEN_08BIT, 0x03}, + {0xd401, CRL_REG_LEN_08BIT, 0xff}, + {0xd402, CRL_REG_LEN_08BIT, 0xff}, + {0xd403, CRL_REG_LEN_08BIT, 0xc4}, + {0xd404, CRL_REG_LEN_08BIT, 0xd4}, + {0xd405, CRL_REG_LEN_08BIT, 0x01}, + {0xd406, CRL_REG_LEN_08BIT, 0x40}, + {0xd407, CRL_REG_LEN_08BIT, 0x18}, + {0xd408, CRL_REG_LEN_08BIT, 0x03}, + {0xd409, CRL_REG_LEN_08BIT, 0xff}, + {0xd40a, CRL_REG_LEN_08BIT, 0xff}, + {0xd40b, CRL_REG_LEN_08BIT, 0xa8}, + {0xd40c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd40d, CRL_REG_LEN_08BIT, 0x63}, + {0xd40e, CRL_REG_LEN_08BIT, 0x00}, + {0xd40f, CRL_REG_LEN_08BIT, 0xff}, + {0xd410, CRL_REG_LEN_08BIT, 0x9c}, + {0xd411, CRL_REG_LEN_08BIT, 0x60}, + {0xd412, CRL_REG_LEN_08BIT, 0x00}, + {0xd413, CRL_REG_LEN_08BIT, 0x00}, + {0xd414, CRL_REG_LEN_08BIT, 0x03}, + {0xd415, CRL_REG_LEN_08BIT, 0xff}, + {0xd416, CRL_REG_LEN_08BIT, 0xff}, + {0xd417, CRL_REG_LEN_08BIT, 0xb6}, + {0xd418, CRL_REG_LEN_08BIT, 0xd4}, + {0xd419, CRL_REG_LEN_08BIT, 0x01}, + {0xd41a, CRL_REG_LEN_08BIT, 0x18}, + {0xd41b, CRL_REG_LEN_08BIT, 0x14}, + {0xd41c, CRL_REG_LEN_08BIT, 0x03}, + {0xd41d, CRL_REG_LEN_08BIT, 0xff}, + {0xd41e, CRL_REG_LEN_08BIT, 0xff}, + {0xd41f, CRL_REG_LEN_08BIT, 0xa9}, + {0xd420, CRL_REG_LEN_08BIT, 0x9d}, + {0xd421, CRL_REG_LEN_08BIT, 0x6b}, + {0xd422, CRL_REG_LEN_08BIT, 0x00}, + {0xd423, CRL_REG_LEN_08BIT, 0xff}, + {0xd424, CRL_REG_LEN_08BIT, 0x85}, + {0xd425, CRL_REG_LEN_08BIT, 0x21}, + {0xd426, CRL_REG_LEN_08BIT, 0x00}, + {0xd427, CRL_REG_LEN_08BIT, 0x00}, + {0xd428, CRL_REG_LEN_08BIT, 0x85}, + {0xd429, CRL_REG_LEN_08BIT, 0x41}, + {0xd42a, CRL_REG_LEN_08BIT, 0x00}, + {0xd42b, CRL_REG_LEN_08BIT, 0x04}, + {0xd42c, CRL_REG_LEN_08BIT, 0x85}, + {0xd42d, CRL_REG_LEN_08BIT, 0x81}, + {0xd42e, CRL_REG_LEN_08BIT, 0x00}, + {0xd42f, CRL_REG_LEN_08BIT, 0x08}, + {0xd430, CRL_REG_LEN_08BIT, 0x85}, + {0xd431, CRL_REG_LEN_08BIT, 0xc1}, + {0xd432, CRL_REG_LEN_08BIT, 0x00}, + {0xd433, CRL_REG_LEN_08BIT, 0x0c}, + {0xd434, CRL_REG_LEN_08BIT, 0x86}, + {0xd435, CRL_REG_LEN_08BIT, 0x01}, + {0xd436, CRL_REG_LEN_08BIT, 0x00}, + {0xd437, CRL_REG_LEN_08BIT, 0x10}, + {0xd438, CRL_REG_LEN_08BIT, 0x44}, + {0xd439, CRL_REG_LEN_08BIT, 0x00}, + {0xd43a, CRL_REG_LEN_08BIT, 0x48}, + {0xd43b, CRL_REG_LEN_08BIT, 0x00}, + {0xd43c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd43d, CRL_REG_LEN_08BIT, 0x21}, + {0xd43e, CRL_REG_LEN_08BIT, 0x00}, + {0xd43f, CRL_REG_LEN_08BIT, 0x1c}, + {0xd440, CRL_REG_LEN_08BIT, 0x9c}, + {0xd441, CRL_REG_LEN_08BIT, 0x21}, + {0xd442, CRL_REG_LEN_08BIT, 0xff}, + {0xd443, CRL_REG_LEN_08BIT, 0xfc}, + {0xd444, CRL_REG_LEN_08BIT, 0xd4}, + {0xd445, CRL_REG_LEN_08BIT, 0x01}, + {0xd446, CRL_REG_LEN_08BIT, 0x48}, + {0xd447, CRL_REG_LEN_08BIT, 0x00}, + {0xd448, CRL_REG_LEN_08BIT, 0x18}, + {0xd449, CRL_REG_LEN_08BIT, 0x60}, + {0xd44a, CRL_REG_LEN_08BIT, 0x00}, + {0xd44b, CRL_REG_LEN_08BIT, 0x01}, + {0xd44c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd44d, CRL_REG_LEN_08BIT, 0x63}, + {0xd44e, CRL_REG_LEN_08BIT, 0x07}, + {0xd44f, CRL_REG_LEN_08BIT, 0x80}, + {0xd450, CRL_REG_LEN_08BIT, 0x8c}, + {0xd451, CRL_REG_LEN_08BIT, 0x63}, + {0xd452, CRL_REG_LEN_08BIT, 0x00}, + {0xd453, CRL_REG_LEN_08BIT, 0x68}, + {0xd454, CRL_REG_LEN_08BIT, 0xbc}, + {0xd455, CRL_REG_LEN_08BIT, 0x03}, + {0xd456, CRL_REG_LEN_08BIT, 0x00}, + {0xd457, CRL_REG_LEN_08BIT, 0x00}, + {0xd458, CRL_REG_LEN_08BIT, 0x10}, + {0xd459, CRL_REG_LEN_08BIT, 0x00}, + {0xd45a, CRL_REG_LEN_08BIT, 0x00}, + {0xd45b, CRL_REG_LEN_08BIT, 0x0c}, + {0xd45c, CRL_REG_LEN_08BIT, 0x15}, + {0xd45d, CRL_REG_LEN_08BIT, 0x00}, + {0xd45e, CRL_REG_LEN_08BIT, 0x00}, + {0xd45f, CRL_REG_LEN_08BIT, 0x00}, + {0xd460, CRL_REG_LEN_08BIT, 0x07}, + {0xd461, CRL_REG_LEN_08BIT, 0xff}, + {0xd462, CRL_REG_LEN_08BIT, 0xd9}, + {0xd463, CRL_REG_LEN_08BIT, 0x7c}, + {0xd464, CRL_REG_LEN_08BIT, 0x15}, + {0xd465, CRL_REG_LEN_08BIT, 0x00}, + {0xd466, CRL_REG_LEN_08BIT, 0x00}, + {0xd467, CRL_REG_LEN_08BIT, 0x00}, + {0xd468, CRL_REG_LEN_08BIT, 0x18}, + {0xd469, CRL_REG_LEN_08BIT, 0x60}, + {0xd46a, CRL_REG_LEN_08BIT, 0x80}, + {0xd46b, CRL_REG_LEN_08BIT, 0x06}, + {0xd46c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd46d, CRL_REG_LEN_08BIT, 0x63}, + {0xd46e, CRL_REG_LEN_08BIT, 0xc4}, + {0xd46f, CRL_REG_LEN_08BIT, 0xb8}, + {0xd470, CRL_REG_LEN_08BIT, 0x8c}, + {0xd471, CRL_REG_LEN_08BIT, 0x63}, + {0xd472, CRL_REG_LEN_08BIT, 0x00}, + {0xd473, CRL_REG_LEN_08BIT, 0x00}, + {0xd474, CRL_REG_LEN_08BIT, 0xbc}, + {0xd475, CRL_REG_LEN_08BIT, 0x23}, + {0xd476, CRL_REG_LEN_08BIT, 0x00}, + {0xd477, CRL_REG_LEN_08BIT, 0x01}, + {0xd478, CRL_REG_LEN_08BIT, 0x10}, + {0xd479, CRL_REG_LEN_08BIT, 0x00}, + {0xd47a, CRL_REG_LEN_08BIT, 0x00}, + {0xd47b, CRL_REG_LEN_08BIT, 0x25}, + {0xd47c, CRL_REG_LEN_08BIT, 0x9d}, + {0xd47d, CRL_REG_LEN_08BIT, 0x00}, + {0xd47e, CRL_REG_LEN_08BIT, 0x00}, + {0xd47f, CRL_REG_LEN_08BIT, 0x00}, + {0xd480, CRL_REG_LEN_08BIT, 0x00}, + {0xd481, CRL_REG_LEN_08BIT, 0x00}, + {0xd482, CRL_REG_LEN_08BIT, 0x00}, + {0xd483, CRL_REG_LEN_08BIT, 0x0b}, + {0xd484, CRL_REG_LEN_08BIT, 0xb8}, + {0xd485, CRL_REG_LEN_08BIT, 0xe8}, + {0xd486, CRL_REG_LEN_08BIT, 0x00}, + {0xd487, CRL_REG_LEN_08BIT, 0x02}, + {0xd488, CRL_REG_LEN_08BIT, 0x07}, + {0xd489, CRL_REG_LEN_08BIT, 0xff}, + {0xd48a, CRL_REG_LEN_08BIT, 0xd6}, + {0xd48b, CRL_REG_LEN_08BIT, 0x08}, + {0xd48c, CRL_REG_LEN_08BIT, 0x15}, + {0xd48d, CRL_REG_LEN_08BIT, 0x00}, + {0xd48e, CRL_REG_LEN_08BIT, 0x00}, + {0xd48f, CRL_REG_LEN_08BIT, 0x00}, + {0xd490, CRL_REG_LEN_08BIT, 0x18}, + {0xd491, CRL_REG_LEN_08BIT, 0x60}, + {0xd492, CRL_REG_LEN_08BIT, 0x80}, + {0xd493, CRL_REG_LEN_08BIT, 0x06}, + {0xd494, CRL_REG_LEN_08BIT, 0xa8}, + {0xd495, CRL_REG_LEN_08BIT, 0x63}, + {0xd496, CRL_REG_LEN_08BIT, 0xc4}, + {0xd497, CRL_REG_LEN_08BIT, 0xb8}, + {0xd498, CRL_REG_LEN_08BIT, 0x8c}, + {0xd499, CRL_REG_LEN_08BIT, 0x63}, + {0xd49a, CRL_REG_LEN_08BIT, 0x00}, + {0xd49b, CRL_REG_LEN_08BIT, 0x00}, + {0xd49c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd49d, CRL_REG_LEN_08BIT, 0x23}, + {0xd49e, CRL_REG_LEN_08BIT, 0x00}, + {0xd49f, CRL_REG_LEN_08BIT, 0x01}, + {0xd4a0, CRL_REG_LEN_08BIT, 0x10}, + {0xd4a1, CRL_REG_LEN_08BIT, 0x00}, + {0xd4a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd4a3, CRL_REG_LEN_08BIT, 0x1b}, + {0xd4a4, CRL_REG_LEN_08BIT, 0x9d}, + {0xd4a5, CRL_REG_LEN_08BIT, 0x00}, + {0xd4a6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4a8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd4a9, CRL_REG_LEN_08BIT, 0xe8}, + {0xd4aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ab, CRL_REG_LEN_08BIT, 0x02}, + {0xd4ac, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4ad, CRL_REG_LEN_08BIT, 0xc0}, + {0xd4ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd4af, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b0, CRL_REG_LEN_08BIT, 0x18}, + {0xd4b1, CRL_REG_LEN_08BIT, 0xa0}, + {0xd4b2, CRL_REG_LEN_08BIT, 0x80}, + {0xd4b3, CRL_REG_LEN_08BIT, 0x06}, + {0xd4b4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd4b5, CRL_REG_LEN_08BIT, 0x67}, + {0xd4b6, CRL_REG_LEN_08BIT, 0x30}, + {0xd4b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4b9, CRL_REG_LEN_08BIT, 0xa5}, + {0xd4ba, CRL_REG_LEN_08BIT, 0xce}, + {0xd4bb, CRL_REG_LEN_08BIT, 0xb0}, + {0xd4bc, CRL_REG_LEN_08BIT, 0x19}, + {0xd4bd, CRL_REG_LEN_08BIT, 0x60}, + {0xd4be, CRL_REG_LEN_08BIT, 0x00}, + {0xd4bf, CRL_REG_LEN_08BIT, 0x01}, + {0xd4c0, CRL_REG_LEN_08BIT, 0xa9}, + {0xd4c1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd4c2, CRL_REG_LEN_08BIT, 0x06}, + {0xd4c3, CRL_REG_LEN_08BIT, 0x14}, + {0xd4c4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd4c5, CRL_REG_LEN_08BIT, 0x83}, + {0xd4c6, CRL_REG_LEN_08BIT, 0x28}, + {0xd4c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4c8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4c9, CRL_REG_LEN_08BIT, 0xc6}, + {0xd4ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd4cb, CRL_REG_LEN_08BIT, 0x01}, + {0xd4cc, CRL_REG_LEN_08BIT, 0xe0}, + {0xd4cd, CRL_REG_LEN_08BIT, 0x63}, + {0xd4ce, CRL_REG_LEN_08BIT, 0x18}, + {0xd4cf, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd4d1, CRL_REG_LEN_08BIT, 0x84}, + {0xd4d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd4d5, CRL_REG_LEN_08BIT, 0xa3}, + {0xd4d6, CRL_REG_LEN_08BIT, 0x58}, + {0xd4d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d8, CRL_REG_LEN_08BIT, 0xa4}, + {0xd4d9, CRL_REG_LEN_08BIT, 0xc6}, + {0xd4da, CRL_REG_LEN_08BIT, 0x00}, + {0xd4db, CRL_REG_LEN_08BIT, 0xff}, + {0xd4dc, CRL_REG_LEN_08BIT, 0xb8}, + {0xd4dd, CRL_REG_LEN_08BIT, 0x64}, + {0xd4de, CRL_REG_LEN_08BIT, 0x00}, + {0xd4df, CRL_REG_LEN_08BIT, 0x18}, + {0xd4e0, CRL_REG_LEN_08BIT, 0xbc}, + {0xd4e1, CRL_REG_LEN_08BIT, 0x46}, + {0xd4e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e3, CRL_REG_LEN_08BIT, 0x03}, + {0xd4e4, CRL_REG_LEN_08BIT, 0x94}, + {0xd4e5, CRL_REG_LEN_08BIT, 0x85}, + {0xd4e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd4e9, CRL_REG_LEN_08BIT, 0x63}, + {0xd4ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd4eb, CRL_REG_LEN_08BIT, 0x98}, + {0xd4ec, CRL_REG_LEN_08BIT, 0xe0}, + {0xd4ed, CRL_REG_LEN_08BIT, 0x64}, + {0xd4ee, CRL_REG_LEN_08BIT, 0x18}, + {0xd4ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f0, CRL_REG_LEN_08BIT, 0x0f}, + {0xd4f1, CRL_REG_LEN_08BIT, 0xff}, + {0xd4f2, CRL_REG_LEN_08BIT, 0xff}, + {0xd4f3, CRL_REG_LEN_08BIT, 0xf0}, + {0xd4f4, CRL_REG_LEN_08BIT, 0xdc}, + {0xd4f5, CRL_REG_LEN_08BIT, 0x05}, + {0xd4f6, CRL_REG_LEN_08BIT, 0x18}, + {0xd4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4f9, CRL_REG_LEN_08BIT, 0x68}, + {0xd4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fb, CRL_REG_LEN_08BIT, 0x01}, + {0xd4fc, CRL_REG_LEN_08BIT, 0xa5}, + {0xd4fd, CRL_REG_LEN_08BIT, 0x03}, + {0xd4fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ff, CRL_REG_LEN_08BIT, 0xff}, + {0xd500, CRL_REG_LEN_08BIT, 0xbc}, + {0xd501, CRL_REG_LEN_08BIT, 0x48}, + {0xd502, CRL_REG_LEN_08BIT, 0x00}, + {0xd503, CRL_REG_LEN_08BIT, 0x01}, + {0xd504, CRL_REG_LEN_08BIT, 0x0f}, + {0xd505, CRL_REG_LEN_08BIT, 0xff}, + {0xd506, CRL_REG_LEN_08BIT, 0xff}, + {0xd507, CRL_REG_LEN_08BIT, 0xea}, + {0xd508, CRL_REG_LEN_08BIT, 0xb8}, + {0xd509, CRL_REG_LEN_08BIT, 0xe8}, + {0xd50a, CRL_REG_LEN_08BIT, 0x00}, + {0xd50b, CRL_REG_LEN_08BIT, 0x02}, + {0xd50c, CRL_REG_LEN_08BIT, 0x18}, + {0xd50d, CRL_REG_LEN_08BIT, 0x60}, + {0xd50e, CRL_REG_LEN_08BIT, 0x00}, + {0xd50f, CRL_REG_LEN_08BIT, 0x01}, + {0xd510, CRL_REG_LEN_08BIT, 0xa8}, + {0xd511, CRL_REG_LEN_08BIT, 0x63}, + {0xd512, CRL_REG_LEN_08BIT, 0x06}, + {0xd513, CRL_REG_LEN_08BIT, 0x14}, + {0xd514, CRL_REG_LEN_08BIT, 0x07}, + {0xd515, CRL_REG_LEN_08BIT, 0xff}, + {0xd516, CRL_REG_LEN_08BIT, 0xe3}, + {0xd517, CRL_REG_LEN_08BIT, 0xe9}, + {0xd518, CRL_REG_LEN_08BIT, 0x9c}, + {0xd519, CRL_REG_LEN_08BIT, 0x83}, + {0xd51a, CRL_REG_LEN_08BIT, 0x00}, + {0xd51b, CRL_REG_LEN_08BIT, 0x10}, + {0xd51c, CRL_REG_LEN_08BIT, 0x85}, + {0xd51d, CRL_REG_LEN_08BIT, 0x21}, + {0xd51e, CRL_REG_LEN_08BIT, 0x00}, + {0xd51f, CRL_REG_LEN_08BIT, 0x00}, + {0xd520, CRL_REG_LEN_08BIT, 0x44}, + {0xd521, CRL_REG_LEN_08BIT, 0x00}, + {0xd522, CRL_REG_LEN_08BIT, 0x48}, + {0xd523, CRL_REG_LEN_08BIT, 0x00}, + {0xd524, CRL_REG_LEN_08BIT, 0x9c}, + {0xd525, CRL_REG_LEN_08BIT, 0x21}, + {0xd526, CRL_REG_LEN_08BIT, 0x00}, + {0xd527, CRL_REG_LEN_08BIT, 0x04}, + {0xd528, CRL_REG_LEN_08BIT, 0x18}, + {0xd529, CRL_REG_LEN_08BIT, 0x60}, + {0xd52a, CRL_REG_LEN_08BIT, 0x00}, + {0xd52b, CRL_REG_LEN_08BIT, 0x01}, + {0xd52c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd52d, CRL_REG_LEN_08BIT, 0x80}, + {0xd52e, CRL_REG_LEN_08BIT, 0xff}, + {0xd52f, CRL_REG_LEN_08BIT, 0xff}, + {0xd530, CRL_REG_LEN_08BIT, 0xa8}, + {0xd531, CRL_REG_LEN_08BIT, 0x63}, + {0xd532, CRL_REG_LEN_08BIT, 0x09}, + {0xd533, CRL_REG_LEN_08BIT, 0xef}, + {0xd534, CRL_REG_LEN_08BIT, 0xd8}, + {0xd535, CRL_REG_LEN_08BIT, 0x03}, + {0xd536, CRL_REG_LEN_08BIT, 0x20}, + {0xd537, CRL_REG_LEN_08BIT, 0x00}, + {0xd538, CRL_REG_LEN_08BIT, 0x18}, + {0xd539, CRL_REG_LEN_08BIT, 0x60}, + {0xd53a, CRL_REG_LEN_08BIT, 0x80}, + {0xd53b, CRL_REG_LEN_08BIT, 0x06}, + {0xd53c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd53d, CRL_REG_LEN_08BIT, 0x63}, + {0xd53e, CRL_REG_LEN_08BIT, 0xc9}, + {0xd53f, CRL_REG_LEN_08BIT, 0xef}, + {0xd540, CRL_REG_LEN_08BIT, 0xd8}, + {0xd541, CRL_REG_LEN_08BIT, 0x03}, + {0xd542, CRL_REG_LEN_08BIT, 0x20}, + {0xd543, CRL_REG_LEN_08BIT, 0x00}, + {0xd544, CRL_REG_LEN_08BIT, 0x44}, + {0xd545, CRL_REG_LEN_08BIT, 0x00}, + {0xd546, CRL_REG_LEN_08BIT, 0x48}, + {0xd547, CRL_REG_LEN_08BIT, 0x00}, + {0xd548, CRL_REG_LEN_08BIT, 0x15}, + {0xd549, CRL_REG_LEN_08BIT, 0x00}, + {0xd54a, CRL_REG_LEN_08BIT, 0x00}, + {0xd54b, CRL_REG_LEN_08BIT, 0x00}, + {0xd54c, CRL_REG_LEN_08BIT, 0x18}, + {0xd54d, CRL_REG_LEN_08BIT, 0x80}, + {0xd54e, CRL_REG_LEN_08BIT, 0x00}, + {0xd54f, CRL_REG_LEN_08BIT, 0x01}, + {0xd550, CRL_REG_LEN_08BIT, 0xa8}, + {0xd551, CRL_REG_LEN_08BIT, 0x84}, + {0xd552, CRL_REG_LEN_08BIT, 0x0a}, + {0xd553, CRL_REG_LEN_08BIT, 0x12}, + {0xd554, CRL_REG_LEN_08BIT, 0x8c}, + {0xd555, CRL_REG_LEN_08BIT, 0x64}, + {0xd556, CRL_REG_LEN_08BIT, 0x00}, + {0xd557, CRL_REG_LEN_08BIT, 0x00}, + {0xd558, CRL_REG_LEN_08BIT, 0xbc}, + {0xd559, CRL_REG_LEN_08BIT, 0x03}, + {0xd55a, CRL_REG_LEN_08BIT, 0x00}, + {0xd55b, CRL_REG_LEN_08BIT, 0x00}, + {0xd55c, CRL_REG_LEN_08BIT, 0x13}, + {0xd55d, CRL_REG_LEN_08BIT, 0xff}, + {0xd55e, CRL_REG_LEN_08BIT, 0xff}, + {0xd55f, CRL_REG_LEN_08BIT, 0xfe}, + {0xd560, CRL_REG_LEN_08BIT, 0x15}, + {0xd561, CRL_REG_LEN_08BIT, 0x00}, + {0xd562, CRL_REG_LEN_08BIT, 0x00}, + {0xd563, CRL_REG_LEN_08BIT, 0x00}, + {0xd564, CRL_REG_LEN_08BIT, 0x44}, + {0xd565, CRL_REG_LEN_08BIT, 0x00}, + {0xd566, CRL_REG_LEN_08BIT, 0x48}, + {0xd567, CRL_REG_LEN_08BIT, 0x00}, + {0xd568, CRL_REG_LEN_08BIT, 0x15}, + {0xd569, CRL_REG_LEN_08BIT, 0x00}, + {0xd56a, CRL_REG_LEN_08BIT, 0x00}, + {0xd56b, CRL_REG_LEN_08BIT, 0x00}, + {0xd56c, CRL_REG_LEN_08BIT, 0x00}, + {0xd56d, CRL_REG_LEN_08BIT, 0x00}, + {0xd56e, CRL_REG_LEN_08BIT, 0x00}, + {0xd56f, CRL_REG_LEN_08BIT, 0x00}, + {0xd570, CRL_REG_LEN_08BIT, 0x00}, + {0xd571, CRL_REG_LEN_08BIT, 0x00}, + {0xd572, CRL_REG_LEN_08BIT, 0x00}, + {0xd573, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0e, CRL_REG_LEN_08BIT, 0x33}, + {0x6f0f, CRL_REG_LEN_08BIT, 0x33}, + {0x460e, CRL_REG_LEN_08BIT, 0x08}, + {0x460f, CRL_REG_LEN_08BIT, 0x01}, + {0x4610, CRL_REG_LEN_08BIT, 0x00}, + {0x4611, CRL_REG_LEN_08BIT, 0x01}, + {0x4612, CRL_REG_LEN_08BIT, 0x00}, + {0x4613, CRL_REG_LEN_08BIT, 0x01}, + {0x4605, CRL_REG_LEN_08BIT, 0x0b}, + {0x4608, CRL_REG_LEN_08BIT, 0x00}, + {0x4609, CRL_REG_LEN_08BIT, 0x08}, + {0x4602, CRL_REG_LEN_08BIT, 0x02}, + {0x4603, CRL_REG_LEN_08BIT, 0xd8}, + {0x6804, CRL_REG_LEN_08BIT, 0x00}, + {0x6805, CRL_REG_LEN_08BIT, 0x06}, + {0x6806, CRL_REG_LEN_08BIT, 0x00}, + {0x5120, CRL_REG_LEN_08BIT, 0x00}, + {0x3510, CRL_REG_LEN_08BIT, 0x00}, + {0x3504, CRL_REG_LEN_08BIT, 0x00}, + {0x6800, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0d, CRL_REG_LEN_08BIT, 0x0f}, + {0x5000, CRL_REG_LEN_08BIT, 0xff}, + {0x5001, CRL_REG_LEN_08BIT, 0xbf}, + {0x5002, CRL_REG_LEN_08BIT, 0x7e}, + {0x5003, CRL_REG_LEN_08BIT, 0x0c}, + {0x503d, CRL_REG_LEN_08BIT, 0x00}, + {0xc450, CRL_REG_LEN_08BIT, 0x01}, + {0xc452, CRL_REG_LEN_08BIT, 0x04}, + {0xc453, CRL_REG_LEN_08BIT, 0x00}, + {0xc454, CRL_REG_LEN_08BIT, 0x00}, + {0xc455, CRL_REG_LEN_08BIT, 0x00}, + {0xc456, CRL_REG_LEN_08BIT, 0x00}, + {0xc457, CRL_REG_LEN_08BIT, 0x00}, + {0xc458, CRL_REG_LEN_08BIT, 0x00}, + {0xc459, CRL_REG_LEN_08BIT, 0x00}, + {0xc45b, CRL_REG_LEN_08BIT, 0x00}, + {0xc45c, CRL_REG_LEN_08BIT, 0x00}, + {0xc45d, CRL_REG_LEN_08BIT, 0x00}, + {0xc45e, CRL_REG_LEN_08BIT, 0x02}, + {0xc45f, CRL_REG_LEN_08BIT, 0x01}, + {0xc460, CRL_REG_LEN_08BIT, 0x01}, + {0xc461, CRL_REG_LEN_08BIT, 0x01}, + {0xc462, CRL_REG_LEN_08BIT, 0x01}, + {0xc464, CRL_REG_LEN_08BIT, 0x88}, + {0xc465, CRL_REG_LEN_08BIT, 0x00}, + {0xc466, CRL_REG_LEN_08BIT, 0x8a}, + {0xc467, CRL_REG_LEN_08BIT, 0x00}, + {0xc468, CRL_REG_LEN_08BIT, 0x86}, + {0xc469, CRL_REG_LEN_08BIT, 0x00}, + {0xc46a, CRL_REG_LEN_08BIT, 0x40}, + {0xc46b, CRL_REG_LEN_08BIT, 0x50}, + {0xc46c, CRL_REG_LEN_08BIT, 0x30}, + {0xc46d, CRL_REG_LEN_08BIT, 0x28}, + {0xc46e, CRL_REG_LEN_08BIT, 0x60}, + {0xc46f, CRL_REG_LEN_08BIT, 0x40}, + {0xc47c, CRL_REG_LEN_08BIT, 0x01}, + {0xc47d, CRL_REG_LEN_08BIT, 0x38}, + {0xc47e, CRL_REG_LEN_08BIT, 0x00}, + {0xc47f, CRL_REG_LEN_08BIT, 0x00}, + {0xc480, CRL_REG_LEN_08BIT, 0x00}, + {0xc481, CRL_REG_LEN_08BIT, 0xff}, + {0xc482, CRL_REG_LEN_08BIT, 0x00}, + {0xc483, CRL_REG_LEN_08BIT, 0x40}, + {0xc484, CRL_REG_LEN_08BIT, 0x00}, + {0xc485, CRL_REG_LEN_08BIT, 0x18}, + {0xc486, CRL_REG_LEN_08BIT, 0x00}, + {0xc487, CRL_REG_LEN_08BIT, 0x18}, + {0xc488, CRL_REG_LEN_08BIT, 0x2e}, + {0xc489, CRL_REG_LEN_08BIT, 0x40}, + {0xc48a, CRL_REG_LEN_08BIT, 0x2e}, + {0xc48b, CRL_REG_LEN_08BIT, 0x40}, + {0xc48c, CRL_REG_LEN_08BIT, 0x00}, + {0xc48d, CRL_REG_LEN_08BIT, 0x04}, + {0xc48e, CRL_REG_LEN_08BIT, 0x00}, + {0xc48f, CRL_REG_LEN_08BIT, 0x04}, + {0xc490, CRL_REG_LEN_08BIT, 0x07}, + {0xc492, CRL_REG_LEN_08BIT, 0x20}, + {0xc493, CRL_REG_LEN_08BIT, 0x08}, + {0xc498, CRL_REG_LEN_08BIT, 0x02}, + {0xc499, CRL_REG_LEN_08BIT, 0x00}, + {0xc49a, CRL_REG_LEN_08BIT, 0x02}, + {0xc49b, CRL_REG_LEN_08BIT, 0x00}, + {0xc49c, CRL_REG_LEN_08BIT, 0x02}, + {0xc49d, CRL_REG_LEN_08BIT, 0x00}, + {0xc49e, CRL_REG_LEN_08BIT, 0x02}, + {0xc49f, CRL_REG_LEN_08BIT, 0x60}, + {0xc4a0, CRL_REG_LEN_08BIT, 0x03}, + {0xc4a1, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a2, CRL_REG_LEN_08BIT, 0x04}, + {0xc4a3, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a5, CRL_REG_LEN_08BIT, 0x10}, + {0xc4a6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4a8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a9, CRL_REG_LEN_08BIT, 0x80}, + {0xc4aa, CRL_REG_LEN_08BIT, 0x0d}, + {0xc4ab, CRL_REG_LEN_08BIT, 0x00}, + {0xc4ac, CRL_REG_LEN_08BIT, 0x03}, + {0xc4ad, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4b4, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b5, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b7, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b9, CRL_REG_LEN_08BIT, 0x01}, + {0xc4ba, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0xc4be, CRL_REG_LEN_08BIT, 0x02}, + {0xc4bf, CRL_REG_LEN_08BIT, 0x33}, + {0xc4c8, CRL_REG_LEN_08BIT, 0x03}, + {0xc4c9, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4ca, CRL_REG_LEN_08BIT, 0x0e}, + {0xc4cb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4cc, CRL_REG_LEN_08BIT, 0x0e}, + {0xc4cd, CRL_REG_LEN_08BIT, 0x51}, + {0xc4ce, CRL_REG_LEN_08BIT, 0x0e}, + {0xc4cf, CRL_REG_LEN_08BIT, 0x51}, + {0xc4d0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4d1, CRL_REG_LEN_08BIT, 0x80}, + {0xc4e0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4e1, CRL_REG_LEN_08BIT, 0x02}, + {0xc4e2, CRL_REG_LEN_08BIT, 0x01}, + {0xc4e4, CRL_REG_LEN_08BIT, 0x10}, + {0xc4e5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4e6, CRL_REG_LEN_08BIT, 0x30}, + {0xc4e7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4e8, CRL_REG_LEN_08BIT, 0x50}, + {0xc4e9, CRL_REG_LEN_08BIT, 0x60}, + {0xc4ea, CRL_REG_LEN_08BIT, 0x70}, + {0xc4eb, CRL_REG_LEN_08BIT, 0x80}, + {0xc4ec, CRL_REG_LEN_08BIT, 0x90}, + {0xc4ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xc4ee, CRL_REG_LEN_08BIT, 0xb0}, + {0xc4ef, CRL_REG_LEN_08BIT, 0xc0}, + {0xc4f0, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4f1, CRL_REG_LEN_08BIT, 0xe0}, + {0xc4f2, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4f3, CRL_REG_LEN_08BIT, 0x80}, + {0xc4f4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4f6, CRL_REG_LEN_08BIT, 0x02}, + {0xc4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f8, CRL_REG_LEN_08BIT, 0x04}, + {0xc4f9, CRL_REG_LEN_08BIT, 0x0b}, + {0xc4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4fd, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fe, CRL_REG_LEN_08BIT, 0x04}, + {0xc4ff, CRL_REG_LEN_08BIT, 0x02}, + {0xc500, CRL_REG_LEN_08BIT, 0x48}, + {0xc501, CRL_REG_LEN_08BIT, 0x74}, + {0xc502, CRL_REG_LEN_08BIT, 0x58}, + {0xc503, CRL_REG_LEN_08BIT, 0x80}, + {0xc504, CRL_REG_LEN_08BIT, 0x05}, + {0xc505, CRL_REG_LEN_08BIT, 0x80}, + {0xc506, CRL_REG_LEN_08BIT, 0x03}, + {0xc507, CRL_REG_LEN_08BIT, 0x80}, + {0xc508, CRL_REG_LEN_08BIT, 0x01}, + {0xc509, CRL_REG_LEN_08BIT, 0xc0}, + {0xc50a, CRL_REG_LEN_08BIT, 0x01}, + {0xc50b, CRL_REG_LEN_08BIT, 0xa0}, + {0xc50c, CRL_REG_LEN_08BIT, 0x01}, + {0xc50d, CRL_REG_LEN_08BIT, 0x2c}, + {0xc50e, CRL_REG_LEN_08BIT, 0x01}, + {0xc50f, CRL_REG_LEN_08BIT, 0x0a}, + {0xc510, CRL_REG_LEN_08BIT, 0x00}, + {0xc511, CRL_REG_LEN_08BIT, 0x00}, + {0xc512, CRL_REG_LEN_08BIT, 0xe5}, + {0xc513, CRL_REG_LEN_08BIT, 0x14}, + {0xc514, CRL_REG_LEN_08BIT, 0x04}, + {0xc515, CRL_REG_LEN_08BIT, 0x00}, + {0xc518, CRL_REG_LEN_08BIT, 0x03}, + {0xc519, CRL_REG_LEN_08BIT, 0x48}, + {0xc51a, CRL_REG_LEN_08BIT, 0x07}, + {0xc51b, CRL_REG_LEN_08BIT, 0x70}, + {0xc2e0, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e1, CRL_REG_LEN_08BIT, 0x51}, + {0xc2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e3, CRL_REG_LEN_08BIT, 0xd6}, + {0xc2e4, CRL_REG_LEN_08BIT, 0x01}, + {0xc2e5, CRL_REG_LEN_08BIT, 0x5e}, + {0xc2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xc2ea, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2eb, CRL_REG_LEN_08BIT, 0x90}, + {0xc2ed, CRL_REG_LEN_08BIT, 0x00}, + {0xc2ee, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2ef, CRL_REG_LEN_08BIT, 0x64}, + {0xc308, CRL_REG_LEN_08BIT, 0x00}, + {0xc309, CRL_REG_LEN_08BIT, 0x00}, + {0xc30a, CRL_REG_LEN_08BIT, 0x00}, + {0xc30c, CRL_REG_LEN_08BIT, 0x00}, + {0xc30d, CRL_REG_LEN_08BIT, 0x01}, + {0xc30e, CRL_REG_LEN_08BIT, 0x00}, + {0xc30f, CRL_REG_LEN_08BIT, 0x00}, + {0xc310, CRL_REG_LEN_08BIT, 0x01}, + {0xc311, CRL_REG_LEN_08BIT, 0x60}, + {0xc312, CRL_REG_LEN_08BIT, 0xff}, + {0xc313, CRL_REG_LEN_08BIT, 0x08}, + {0xc314, CRL_REG_LEN_08BIT, 0x01}, + {0xc315, CRL_REG_LEN_08BIT, 0x7f}, + {0xc316, CRL_REG_LEN_08BIT, 0xff}, + {0xc317, CRL_REG_LEN_08BIT, 0x0b}, + {0xc318, CRL_REG_LEN_08BIT, 0x00}, + {0xc319, CRL_REG_LEN_08BIT, 0x0c}, + {0xc31a, CRL_REG_LEN_08BIT, 0x00}, + {0xc31b, CRL_REG_LEN_08BIT, 0xe0}, + {0xc31c, CRL_REG_LEN_08BIT, 0x00}, + {0xc31d, CRL_REG_LEN_08BIT, 0x14}, + {0xc31e, CRL_REG_LEN_08BIT, 0x00}, + {0xc31f, CRL_REG_LEN_08BIT, 0xc5}, + {0xc320, CRL_REG_LEN_08BIT, 0xff}, + {0xc321, CRL_REG_LEN_08BIT, 0x4b}, + {0xc322, CRL_REG_LEN_08BIT, 0xff}, + {0xc323, CRL_REG_LEN_08BIT, 0xf0}, + {0xc324, CRL_REG_LEN_08BIT, 0xff}, + {0xc325, CRL_REG_LEN_08BIT, 0xe8}, + {0xc326, CRL_REG_LEN_08BIT, 0x00}, + {0xc327, CRL_REG_LEN_08BIT, 0x46}, + {0xc328, CRL_REG_LEN_08BIT, 0xff}, + {0xc329, CRL_REG_LEN_08BIT, 0xd2}, + {0xc32a, CRL_REG_LEN_08BIT, 0xff}, + {0xc32b, CRL_REG_LEN_08BIT, 0xe4}, + {0xc32c, CRL_REG_LEN_08BIT, 0xff}, + {0xc32d, CRL_REG_LEN_08BIT, 0xbb}, + {0xc32e, CRL_REG_LEN_08BIT, 0x00}, + {0xc32f, CRL_REG_LEN_08BIT, 0x61}, + {0xc330, CRL_REG_LEN_08BIT, 0xff}, + {0xc331, CRL_REG_LEN_08BIT, 0xf9}, + {0xc332, CRL_REG_LEN_08BIT, 0x00}, + {0xc333, CRL_REG_LEN_08BIT, 0xd9}, + {0xc334, CRL_REG_LEN_08BIT, 0x00}, + {0xc335, CRL_REG_LEN_08BIT, 0x2e}, + {0xc336, CRL_REG_LEN_08BIT, 0x00}, + {0xc337, CRL_REG_LEN_08BIT, 0xb1}, + {0xc338, CRL_REG_LEN_08BIT, 0xff}, + {0xc339, CRL_REG_LEN_08BIT, 0x64}, + {0xc33a, CRL_REG_LEN_08BIT, 0xff}, + {0xc33b, CRL_REG_LEN_08BIT, 0xeb}, + {0xc33c, CRL_REG_LEN_08BIT, 0xff}, + {0xc33d, CRL_REG_LEN_08BIT, 0xe8}, + {0xc33e, CRL_REG_LEN_08BIT, 0x00}, + {0xc33f, CRL_REG_LEN_08BIT, 0x48}, + {0xc340, CRL_REG_LEN_08BIT, 0xff}, + {0xc341, CRL_REG_LEN_08BIT, 0xd0}, + {0xc342, CRL_REG_LEN_08BIT, 0xff}, + {0xc343, CRL_REG_LEN_08BIT, 0xed}, + {0xc344, CRL_REG_LEN_08BIT, 0xff}, + {0xc345, CRL_REG_LEN_08BIT, 0xad}, + {0xc346, CRL_REG_LEN_08BIT, 0x00}, + {0xc347, CRL_REG_LEN_08BIT, 0x66}, + {0xc348, CRL_REG_LEN_08BIT, 0x01}, + {0xc349, CRL_REG_LEN_08BIT, 0x00}, + {0x6700, CRL_REG_LEN_08BIT, 0x04}, + {0x6701, CRL_REG_LEN_08BIT, 0x7b}, + {0x6702, CRL_REG_LEN_08BIT, 0xfd}, + {0x6703, CRL_REG_LEN_08BIT, 0xf9}, + {0x6704, CRL_REG_LEN_08BIT, 0x3d}, + {0x6705, CRL_REG_LEN_08BIT, 0x71}, + {0x6706, CRL_REG_LEN_08BIT, 0x78}, + {0x6708, CRL_REG_LEN_08BIT, 0x05}, + {0x6f06, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f07, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0a, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f0b, CRL_REG_LEN_08BIT, 0x00}, + {0x6f00, CRL_REG_LEN_08BIT, 0x03}, + {0xc34c, CRL_REG_LEN_08BIT, 0x01}, + {0xc34d, CRL_REG_LEN_08BIT, 0x00}, + {0xc34e, CRL_REG_LEN_08BIT, 0x46}, + {0xc34f, CRL_REG_LEN_08BIT, 0x55}, + {0xc350, CRL_REG_LEN_08BIT, 0x00}, + {0xc351, CRL_REG_LEN_08BIT, 0x40}, + {0xc352, CRL_REG_LEN_08BIT, 0x00}, + {0xc353, CRL_REG_LEN_08BIT, 0xff}, + {0xc354, CRL_REG_LEN_08BIT, 0x04}, + {0xc355, CRL_REG_LEN_08BIT, 0x08}, + {0xc356, CRL_REG_LEN_08BIT, 0x01}, + {0xc357, CRL_REG_LEN_08BIT, 0xef}, + {0xc358, CRL_REG_LEN_08BIT, 0x30}, + {0xc359, CRL_REG_LEN_08BIT, 0x01}, + {0xc35a, CRL_REG_LEN_08BIT, 0x64}, + {0xc35b, CRL_REG_LEN_08BIT, 0x46}, + {0xc35c, CRL_REG_LEN_08BIT, 0x00}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x301b, CRL_REG_LEN_08BIT, 0xf0}, + {0x301c, CRL_REG_LEN_08BIT, 0xf0}, + {0x301a, CRL_REG_LEN_08BIT, 0xf0}, + {0xceb0, CRL_REG_LEN_08BIT, 0x00}, + {0xceb1, CRL_REG_LEN_08BIT, 0x00}, + {0xceb2, CRL_REG_LEN_08BIT, 0x00}, + {0xceb3, CRL_REG_LEN_08BIT, 0x00}, + {0xceb4, CRL_REG_LEN_08BIT, 0x00}, + {0xceb5, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb6, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb7, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4a0, CRL_REG_LEN_08BIT, 0x03}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4a2, CRL_REG_LEN_08BIT, 0x04}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0x3011, CRL_REG_LEN_08BIT, 0x42}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0x5608, CRL_REG_LEN_08BIT, 0x0d}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, +}; + +static struct crl_register_write_rep ov10635_640_480_YUV_HDR[] = { + {0x301b, CRL_REG_LEN_08BIT, 0xff}, + {0x301c, CRL_REG_LEN_08BIT, 0xff}, + {0x301a, CRL_REG_LEN_08BIT, 0xff}, + {0x3011, CRL_REG_LEN_08BIT, 0x42}, + {0x6900, CRL_REG_LEN_08BIT, 0x0c}, + {0x6901, CRL_REG_LEN_08BIT, 0x11}, + {0x3503, CRL_REG_LEN_08BIT, 0x10}, + {0x3025, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x14}, + {0x3004, CRL_REG_LEN_08BIT, 0x23}, + {0x3005, CRL_REG_LEN_08BIT, 0x20}, + {0x3006, CRL_REG_LEN_08BIT, 0x91}, + {0x3600, CRL_REG_LEN_08BIT, 0x74}, + {0x3601, CRL_REG_LEN_08BIT, 0x2b}, + {0x3612, CRL_REG_LEN_08BIT, 0x00}, + {0x3611, CRL_REG_LEN_08BIT, 0x67}, + {0x3633, CRL_REG_LEN_08BIT, 0xca}, + {0x3602, CRL_REG_LEN_08BIT, 0x2f}, + {0x3603, CRL_REG_LEN_08BIT, 0x00}, + {0x3630, CRL_REG_LEN_08BIT, 0x28}, + {0x3631, CRL_REG_LEN_08BIT, 0x16}, + {0x3714, CRL_REG_LEN_08BIT, 0x10}, + {0x371d, CRL_REG_LEN_08BIT, 0x01}, + {0x3007, CRL_REG_LEN_08BIT, 0x01}, + {0x3024, CRL_REG_LEN_08BIT, 0x01}, + {0x3020, CRL_REG_LEN_08BIT, 0x0b}, + {0x3702, CRL_REG_LEN_08BIT, 0x0a}, + {0x3703, CRL_REG_LEN_08BIT, 0x17}, + {0x3704, CRL_REG_LEN_08BIT, 0x0f}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x3709, CRL_REG_LEN_08BIT, 0xa8}, + {0x370c, CRL_REG_LEN_08BIT, 0xc7}, + {0x370d, CRL_REG_LEN_08BIT, 0x80}, + {0x3712, CRL_REG_LEN_08BIT, 0x00}, + {0x3713, CRL_REG_LEN_08BIT, 0x20}, + {0x3715, CRL_REG_LEN_08BIT, 0x04}, + {0x381d, CRL_REG_LEN_08BIT, 0x40}, + {0x381c, CRL_REG_LEN_08BIT, 0x00}, + {0x3822, CRL_REG_LEN_08BIT, 0x50}, + {0x3824, CRL_REG_LEN_08BIT, 0x50}, + {0x3815, CRL_REG_LEN_08BIT, 0x8c}, + {0x3804, CRL_REG_LEN_08BIT, 0x05}, + {0x3805, CRL_REG_LEN_08BIT, 0x1f}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3806, CRL_REG_LEN_08BIT, 0x02}, + {0x3807, CRL_REG_LEN_08BIT, 0x89}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0xa4}, + {0x3808, CRL_REG_LEN_08BIT, 0x02}, + {0x3809, CRL_REG_LEN_08BIT, 0x80}, + {0x380a, CRL_REG_LEN_08BIT, 0x01}, + {0x380b, CRL_REG_LEN_08BIT, 0xe0}, + {0x380c, CRL_REG_LEN_08BIT, 0x03}, + {0x380d, CRL_REG_LEN_08BIT, 0xc0}, + {0x6e42, CRL_REG_LEN_08BIT, 0x02}, + {0x6e43, CRL_REG_LEN_08BIT, 0x08}, + {0x380e, CRL_REG_LEN_08BIT, 0x02}, + {0x380f, CRL_REG_LEN_08BIT, 0x08}, + {0x3813, CRL_REG_LEN_08BIT, 0x02}, + {0x3811, CRL_REG_LEN_08BIT, 0x08}, + {0x381f, CRL_REG_LEN_08BIT, 0x0c}, + {0x3828, CRL_REG_LEN_08BIT, 0x03}, + {0x3829, CRL_REG_LEN_08BIT, 0x10}, + {0x382a, CRL_REG_LEN_08BIT, 0x10}, + {0x382b, CRL_REG_LEN_08BIT, 0x10}, + {0x3621, CRL_REG_LEN_08BIT, 0x74}, + {0x5005, CRL_REG_LEN_08BIT, 0x08}, + {0x56d5, CRL_REG_LEN_08BIT, 0x00}, + {0x56d6, CRL_REG_LEN_08BIT, 0x80}, + {0x56d7, CRL_REG_LEN_08BIT, 0x00}, + {0x56d8, CRL_REG_LEN_08BIT, 0x00}, + {0x56d9, CRL_REG_LEN_08BIT, 0x00}, + {0x56da, CRL_REG_LEN_08BIT, 0x80}, + {0x56db, CRL_REG_LEN_08BIT, 0x00}, + {0x56dc, CRL_REG_LEN_08BIT, 0x00}, + {0x56e8, CRL_REG_LEN_08BIT, 0x00}, + {0x56e9, CRL_REG_LEN_08BIT, 0x7f}, + {0x56ea, CRL_REG_LEN_08BIT, 0x00}, + {0x56eb, CRL_REG_LEN_08BIT, 0x7f}, + {0x5100, CRL_REG_LEN_08BIT, 0x00}, + {0x5101, CRL_REG_LEN_08BIT, 0x80}, + {0x5102, CRL_REG_LEN_08BIT, 0x00}, + {0x5103, CRL_REG_LEN_08BIT, 0x80}, + {0x5104, CRL_REG_LEN_08BIT, 0x00}, + {0x5105, CRL_REG_LEN_08BIT, 0x80}, + {0x5106, CRL_REG_LEN_08BIT, 0x00}, + {0x5107, CRL_REG_LEN_08BIT, 0x80}, + {0x5108, CRL_REG_LEN_08BIT, 0x00}, + {0x5109, CRL_REG_LEN_08BIT, 0x00}, + {0x510a, CRL_REG_LEN_08BIT, 0x00}, + {0x510b, CRL_REG_LEN_08BIT, 0x00}, + {0x510c, CRL_REG_LEN_08BIT, 0x00}, + {0x510d, CRL_REG_LEN_08BIT, 0x00}, + {0x510e, CRL_REG_LEN_08BIT, 0x00}, + {0x510f, CRL_REG_LEN_08BIT, 0x00}, + {0x5110, CRL_REG_LEN_08BIT, 0x00}, + {0x5111, CRL_REG_LEN_08BIT, 0x80}, + {0x5112, CRL_REG_LEN_08BIT, 0x00}, + {0x5113, CRL_REG_LEN_08BIT, 0x80}, + {0x5114, CRL_REG_LEN_08BIT, 0x00}, + {0x5115, CRL_REG_LEN_08BIT, 0x80}, + {0x5116, CRL_REG_LEN_08BIT, 0x00}, + {0x5117, CRL_REG_LEN_08BIT, 0x80}, + {0x5118, CRL_REG_LEN_08BIT, 0x00}, + {0x5119, CRL_REG_LEN_08BIT, 0x00}, + {0x511a, CRL_REG_LEN_08BIT, 0x00}, + {0x511b, CRL_REG_LEN_08BIT, 0x00}, + {0x511c, CRL_REG_LEN_08BIT, 0x00}, + {0x511d, CRL_REG_LEN_08BIT, 0x00}, + {0x511e, CRL_REG_LEN_08BIT, 0x00}, + {0x511f, CRL_REG_LEN_08BIT, 0x00}, + {0x56d0, CRL_REG_LEN_08BIT, 0x00}, + {0x5006, CRL_REG_LEN_08BIT, 0x24}, + {0x5608, CRL_REG_LEN_08BIT, 0x19}, + {0x52d7, CRL_REG_LEN_08BIT, 0x06}, + {0x528d, CRL_REG_LEN_08BIT, 0x08}, + {0x5293, CRL_REG_LEN_08BIT, 0x12}, + {0x52d3, CRL_REG_LEN_08BIT, 0x12}, + {0x5288, CRL_REG_LEN_08BIT, 0x06}, + {0x5289, CRL_REG_LEN_08BIT, 0x20}, + {0x52c8, CRL_REG_LEN_08BIT, 0x06}, + {0x52c9, CRL_REG_LEN_08BIT, 0x20}, + {0x52cd, CRL_REG_LEN_08BIT, 0x04}, + {0x5381, CRL_REG_LEN_08BIT, 0x00}, + {0x5382, CRL_REG_LEN_08BIT, 0xff}, + {0x5589, CRL_REG_LEN_08BIT, 0x76}, + {0x558a, CRL_REG_LEN_08BIT, 0x47}, + {0x558b, CRL_REG_LEN_08BIT, 0xef}, + {0x558c, CRL_REG_LEN_08BIT, 0xc9}, + {0x558d, CRL_REG_LEN_08BIT, 0x49}, + {0x558e, CRL_REG_LEN_08BIT, 0x30}, + {0x558f, CRL_REG_LEN_08BIT, 0x67}, + {0x5590, CRL_REG_LEN_08BIT, 0x3f}, + {0x5591, CRL_REG_LEN_08BIT, 0xf0}, + {0x5592, CRL_REG_LEN_08BIT, 0x10}, + {0x55a2, CRL_REG_LEN_08BIT, 0x6d}, + {0x55a3, CRL_REG_LEN_08BIT, 0x55}, + {0x55a4, CRL_REG_LEN_08BIT, 0xc3}, + {0x55a5, CRL_REG_LEN_08BIT, 0xb5}, + {0x55a6, CRL_REG_LEN_08BIT, 0x43}, + {0x55a7, CRL_REG_LEN_08BIT, 0x38}, + {0x55a8, CRL_REG_LEN_08BIT, 0x5f}, + {0x55a9, CRL_REG_LEN_08BIT, 0x4b}, + {0x55aa, CRL_REG_LEN_08BIT, 0xf0}, + {0x55ab, CRL_REG_LEN_08BIT, 0x10}, + {0x5581, CRL_REG_LEN_08BIT, 0x52}, + {0x5300, CRL_REG_LEN_08BIT, 0x01}, + {0x5301, CRL_REG_LEN_08BIT, 0x00}, + {0x5302, CRL_REG_LEN_08BIT, 0x00}, + {0x5303, CRL_REG_LEN_08BIT, 0x0e}, + {0x5304, CRL_REG_LEN_08BIT, 0x00}, + {0x5305, CRL_REG_LEN_08BIT, 0x0e}, + {0x5306, CRL_REG_LEN_08BIT, 0x00}, + {0x5307, CRL_REG_LEN_08BIT, 0x36}, + {0x5308, CRL_REG_LEN_08BIT, 0x00}, + {0x5309, CRL_REG_LEN_08BIT, 0xd9}, + {0x530a, CRL_REG_LEN_08BIT, 0x00}, + {0x530b, CRL_REG_LEN_08BIT, 0x0f}, + {0x530c, CRL_REG_LEN_08BIT, 0x00}, + {0x530d, CRL_REG_LEN_08BIT, 0x2c}, + {0x530e, CRL_REG_LEN_08BIT, 0x00}, + {0x530f, CRL_REG_LEN_08BIT, 0x59}, + {0x5310, CRL_REG_LEN_08BIT, 0x00}, + {0x5311, CRL_REG_LEN_08BIT, 0x7b}, + {0x5312, CRL_REG_LEN_08BIT, 0x00}, + {0x5313, CRL_REG_LEN_08BIT, 0x22}, + {0x5314, CRL_REG_LEN_08BIT, 0x00}, + {0x5315, CRL_REG_LEN_08BIT, 0xd5}, + {0x5316, CRL_REG_LEN_08BIT, 0x00}, + {0x5317, CRL_REG_LEN_08BIT, 0x13}, + {0x5318, CRL_REG_LEN_08BIT, 0x00}, + {0x5319, CRL_REG_LEN_08BIT, 0x18}, + {0x531a, CRL_REG_LEN_08BIT, 0x00}, + {0x531b, CRL_REG_LEN_08BIT, 0x26}, + {0x531c, CRL_REG_LEN_08BIT, 0x00}, + {0x531d, CRL_REG_LEN_08BIT, 0xdc}, + {0x531e, CRL_REG_LEN_08BIT, 0x00}, + {0x531f, CRL_REG_LEN_08BIT, 0x02}, + {0x5320, CRL_REG_LEN_08BIT, 0x00}, + {0x5321, CRL_REG_LEN_08BIT, 0x24}, + {0x5322, CRL_REG_LEN_08BIT, 0x00}, + {0x5323, CRL_REG_LEN_08BIT, 0x56}, + {0x5324, CRL_REG_LEN_08BIT, 0x00}, + {0x5325, CRL_REG_LEN_08BIT, 0x85}, + {0x5326, CRL_REG_LEN_08BIT, 0x00}, + {0x5327, CRL_REG_LEN_08BIT, 0x20}, + {0x5609, CRL_REG_LEN_08BIT, 0x01}, + {0x560a, CRL_REG_LEN_08BIT, 0x40}, + {0x560b, CRL_REG_LEN_08BIT, 0x01}, + {0x560c, CRL_REG_LEN_08BIT, 0x40}, + {0x560d, CRL_REG_LEN_08BIT, 0x00}, + {0x560e, CRL_REG_LEN_08BIT, 0xfa}, + {0x560f, CRL_REG_LEN_08BIT, 0x00}, + {0x5610, CRL_REG_LEN_08BIT, 0xfa}, + {0x5611, CRL_REG_LEN_08BIT, 0x02}, + {0x5612, CRL_REG_LEN_08BIT, 0x80}, + {0x5613, CRL_REG_LEN_08BIT, 0x02}, + {0x5614, CRL_REG_LEN_08BIT, 0x80}, + {0x5615, CRL_REG_LEN_08BIT, 0x01}, + {0x5616, CRL_REG_LEN_08BIT, 0x2c}, + {0x5617, CRL_REG_LEN_08BIT, 0x01}, + {0x5618, CRL_REG_LEN_08BIT, 0x2c}, + {0x563b, CRL_REG_LEN_08BIT, 0x01}, + {0x563c, CRL_REG_LEN_08BIT, 0x01}, + {0x563d, CRL_REG_LEN_08BIT, 0x01}, + {0x563e, CRL_REG_LEN_08BIT, 0x01}, + {0x563f, CRL_REG_LEN_08BIT, 0x03}, + {0x5640, CRL_REG_LEN_08BIT, 0x03}, + {0x5641, CRL_REG_LEN_08BIT, 0x03}, + {0x5642, CRL_REG_LEN_08BIT, 0x05}, + {0x5643, CRL_REG_LEN_08BIT, 0x09}, + {0x5644, CRL_REG_LEN_08BIT, 0x05}, + {0x5645, CRL_REG_LEN_08BIT, 0x05}, + {0x5646, CRL_REG_LEN_08BIT, 0x05}, + {0x5647, CRL_REG_LEN_08BIT, 0x05}, + {0x5651, CRL_REG_LEN_08BIT, 0x00}, + {0x5652, CRL_REG_LEN_08BIT, 0x80}, + {0x521a, CRL_REG_LEN_08BIT, 0x01}, + {0x521b, CRL_REG_LEN_08BIT, 0x03}, + {0x521c, CRL_REG_LEN_08BIT, 0x06}, + {0x521d, CRL_REG_LEN_08BIT, 0x0a}, + {0x521e, CRL_REG_LEN_08BIT, 0x0e}, + {0x521f, CRL_REG_LEN_08BIT, 0x12}, + {0x5220, CRL_REG_LEN_08BIT, 0x16}, + {0x5223, CRL_REG_LEN_08BIT, 0x02}, + {0x5225, CRL_REG_LEN_08BIT, 0x04}, + {0x5227, CRL_REG_LEN_08BIT, 0x08}, + {0x5229, CRL_REG_LEN_08BIT, 0x0c}, + {0x522b, CRL_REG_LEN_08BIT, 0x12}, + {0x522d, CRL_REG_LEN_08BIT, 0x18}, + {0x522f, CRL_REG_LEN_08BIT, 0x1e}, + {0x5241, CRL_REG_LEN_08BIT, 0x04}, + {0x5242, CRL_REG_LEN_08BIT, 0x01}, + {0x5243, CRL_REG_LEN_08BIT, 0x03}, + {0x5244, CRL_REG_LEN_08BIT, 0x06}, + {0x5245, CRL_REG_LEN_08BIT, 0x0a}, + {0x5246, CRL_REG_LEN_08BIT, 0x0e}, + {0x5247, CRL_REG_LEN_08BIT, 0x12}, + {0x5248, CRL_REG_LEN_08BIT, 0x16}, + {0x524a, CRL_REG_LEN_08BIT, 0x03}, + {0x524c, CRL_REG_LEN_08BIT, 0x04}, + {0x524e, CRL_REG_LEN_08BIT, 0x08}, + {0x5250, CRL_REG_LEN_08BIT, 0x0c}, + {0x5252, CRL_REG_LEN_08BIT, 0x12}, + {0x5254, CRL_REG_LEN_08BIT, 0x18}, + {0x5256, CRL_REG_LEN_08BIT, 0x1e}, + {0x4606, CRL_REG_LEN_08BIT, 0x07}, + {0x4607, CRL_REG_LEN_08BIT, 0x71}, + {0x460a, CRL_REG_LEN_08BIT, 0x02}, + {0x460b, CRL_REG_LEN_08BIT, 0x70}, + {0x460c, CRL_REG_LEN_08BIT, 0x00}, + {0x4620, CRL_REG_LEN_08BIT, 0x0e}, + {0x4700, CRL_REG_LEN_08BIT, 0x04}, + {0x4701, CRL_REG_LEN_08BIT, 0x00}, + {0x4702, CRL_REG_LEN_08BIT, 0x01}, + {0x4004, CRL_REG_LEN_08BIT, 0x04}, + {0x4005, CRL_REG_LEN_08BIT, 0x18}, + {0x4001, CRL_REG_LEN_08BIT, 0x06}, + {0x4050, CRL_REG_LEN_08BIT, 0x22}, + {0x4051, CRL_REG_LEN_08BIT, 0x24}, + {0x4052, CRL_REG_LEN_08BIT, 0x02}, + {0x4057, CRL_REG_LEN_08BIT, 0x9c}, + {0x405a, CRL_REG_LEN_08BIT, 0x00}, + /*FSIN enable*/ + {0x3832, CRL_REG_LEN_08BIT, 0x00}, + {0x3833, CRL_REG_LEN_08BIT, 0x02}, + {0x3834, CRL_REG_LEN_08BIT, 0x02}, + {0x3835, CRL_REG_LEN_08BIT, 0x08}, + {0x302e, CRL_REG_LEN_08BIT, 0x00}, + /*FSIN end*/ + {0x4202, CRL_REG_LEN_08BIT, 0x02}, + {0x3023, CRL_REG_LEN_08BIT, 0x10}, + {0x3003, CRL_REG_LEN_08BIT, 0x20}, + {0x3004, CRL_REG_LEN_08BIT, 0x21}, + {0x3005, CRL_REG_LEN_08BIT, 0x14}, + {0x3006, CRL_REG_LEN_08BIT, 0x11}, + {0x3024, CRL_REG_LEN_08BIT, 0x01}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x0100, CRL_REG_LEN_08BIT, 0x01}, + {0x6f10, CRL_REG_LEN_08BIT, 0x07}, + {0x6f11, CRL_REG_LEN_08BIT, 0x82}, + {0x6f12, CRL_REG_LEN_08BIT, 0x04}, + {0x6f13, CRL_REG_LEN_08BIT, 0x00}, + {0x6f14, CRL_REG_LEN_08BIT, 0x1f}, + {0x6f15, CRL_REG_LEN_08BIT, 0xdd}, + {0x6f16, CRL_REG_LEN_08BIT, 0x04}, + {0x6f17, CRL_REG_LEN_08BIT, 0x04}, + {0x6f18, CRL_REG_LEN_08BIT, 0x36}, + {0x6f19, CRL_REG_LEN_08BIT, 0x66}, + {0x6f1a, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1b, CRL_REG_LEN_08BIT, 0x08}, + {0x6f1c, CRL_REG_LEN_08BIT, 0x0c}, + {0x6f1d, CRL_REG_LEN_08BIT, 0xe7}, + {0x6f1e, CRL_REG_LEN_08BIT, 0x04}, + {0x6f1f, CRL_REG_LEN_08BIT, 0x0c}, + {0xd000, CRL_REG_LEN_08BIT, 0x19}, + {0xd001, CRL_REG_LEN_08BIT, 0xa0}, + {0xd002, CRL_REG_LEN_08BIT, 0x00}, + {0xd003, CRL_REG_LEN_08BIT, 0x01}, + {0xd004, CRL_REG_LEN_08BIT, 0xa9}, + {0xd005, CRL_REG_LEN_08BIT, 0xad}, + {0xd006, CRL_REG_LEN_08BIT, 0x10}, + {0xd007, CRL_REG_LEN_08BIT, 0x40}, + {0xd008, CRL_REG_LEN_08BIT, 0x44}, + {0xd009, CRL_REG_LEN_08BIT, 0x00}, + {0xd00a, CRL_REG_LEN_08BIT, 0x68}, + {0xd00b, CRL_REG_LEN_08BIT, 0x00}, + {0xd00c, CRL_REG_LEN_08BIT, 0x15}, + {0xd00d, CRL_REG_LEN_08BIT, 0x00}, + {0xd00e, CRL_REG_LEN_08BIT, 0x00}, + {0xd00f, CRL_REG_LEN_08BIT, 0x00}, + {0xd010, CRL_REG_LEN_08BIT, 0x19}, + {0xd011, CRL_REG_LEN_08BIT, 0xa0}, + {0xd012, CRL_REG_LEN_08BIT, 0x00}, + {0xd013, CRL_REG_LEN_08BIT, 0x01}, + {0xd014, CRL_REG_LEN_08BIT, 0xa9}, + {0xd015, CRL_REG_LEN_08BIT, 0xad}, + {0xd016, CRL_REG_LEN_08BIT, 0x13}, + {0xd017, CRL_REG_LEN_08BIT, 0xd0}, + {0xd018, CRL_REG_LEN_08BIT, 0x44}, + {0xd019, CRL_REG_LEN_08BIT, 0x00}, + {0xd01a, CRL_REG_LEN_08BIT, 0x68}, + {0xd01b, CRL_REG_LEN_08BIT, 0x00}, + {0xd01c, CRL_REG_LEN_08BIT, 0x15}, + {0xd01d, CRL_REG_LEN_08BIT, 0x00}, + {0xd01e, CRL_REG_LEN_08BIT, 0x00}, + {0xd01f, CRL_REG_LEN_08BIT, 0x00}, + {0xd020, CRL_REG_LEN_08BIT, 0x19}, + {0xd021, CRL_REG_LEN_08BIT, 0xa0}, + {0xd022, CRL_REG_LEN_08BIT, 0x00}, + {0xd023, CRL_REG_LEN_08BIT, 0x01}, + {0xd024, CRL_REG_LEN_08BIT, 0xa9}, + {0xd025, CRL_REG_LEN_08BIT, 0xad}, + {0xd026, CRL_REG_LEN_08BIT, 0x14}, + {0xd027, CRL_REG_LEN_08BIT, 0xb8}, + {0xd028, CRL_REG_LEN_08BIT, 0x44}, + {0xd029, CRL_REG_LEN_08BIT, 0x00}, + {0xd02a, CRL_REG_LEN_08BIT, 0x68}, + {0xd02b, CRL_REG_LEN_08BIT, 0x00}, + {0xd02c, CRL_REG_LEN_08BIT, 0x15}, + {0xd02d, CRL_REG_LEN_08BIT, 0x00}, + {0xd02e, CRL_REG_LEN_08BIT, 0x00}, + {0xd02f, CRL_REG_LEN_08BIT, 0x00}, + {0xd030, CRL_REG_LEN_08BIT, 0x19}, + {0xd031, CRL_REG_LEN_08BIT, 0xa0}, + {0xd032, CRL_REG_LEN_08BIT, 0x00}, + {0xd033, CRL_REG_LEN_08BIT, 0x01}, + {0xd034, CRL_REG_LEN_08BIT, 0xa9}, + {0xd035, CRL_REG_LEN_08BIT, 0xad}, + {0xd036, CRL_REG_LEN_08BIT, 0x14}, + {0xd037, CRL_REG_LEN_08BIT, 0xdc}, + {0xd038, CRL_REG_LEN_08BIT, 0x44}, + {0xd039, CRL_REG_LEN_08BIT, 0x00}, + {0xd03a, CRL_REG_LEN_08BIT, 0x68}, + {0xd03b, CRL_REG_LEN_08BIT, 0x00}, + {0xd03c, CRL_REG_LEN_08BIT, 0x15}, + {0xd03d, CRL_REG_LEN_08BIT, 0x00}, + {0xd03e, CRL_REG_LEN_08BIT, 0x00}, + {0xd03f, CRL_REG_LEN_08BIT, 0x00}, + {0xd040, CRL_REG_LEN_08BIT, 0x9c}, + {0xd041, CRL_REG_LEN_08BIT, 0x21}, + {0xd042, CRL_REG_LEN_08BIT, 0xff}, + {0xd043, CRL_REG_LEN_08BIT, 0xe4}, + {0xd044, CRL_REG_LEN_08BIT, 0xd4}, + {0xd045, CRL_REG_LEN_08BIT, 0x01}, + {0xd046, CRL_REG_LEN_08BIT, 0x48}, + {0xd047, CRL_REG_LEN_08BIT, 0x00}, + {0xd048, CRL_REG_LEN_08BIT, 0xd4}, + {0xd049, CRL_REG_LEN_08BIT, 0x01}, + {0xd04a, CRL_REG_LEN_08BIT, 0x50}, + {0xd04b, CRL_REG_LEN_08BIT, 0x04}, + {0xd04c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd04d, CRL_REG_LEN_08BIT, 0x01}, + {0xd04e, CRL_REG_LEN_08BIT, 0x60}, + {0xd04f, CRL_REG_LEN_08BIT, 0x08}, + {0xd050, CRL_REG_LEN_08BIT, 0xd4}, + {0xd051, CRL_REG_LEN_08BIT, 0x01}, + {0xd052, CRL_REG_LEN_08BIT, 0x70}, + {0xd053, CRL_REG_LEN_08BIT, 0x0c}, + {0xd054, CRL_REG_LEN_08BIT, 0xd4}, + {0xd055, CRL_REG_LEN_08BIT, 0x01}, + {0xd056, CRL_REG_LEN_08BIT, 0x80}, + {0xd057, CRL_REG_LEN_08BIT, 0x10}, + {0xd058, CRL_REG_LEN_08BIT, 0x19}, + {0xd059, CRL_REG_LEN_08BIT, 0xc0}, + {0xd05a, CRL_REG_LEN_08BIT, 0x00}, + {0xd05b, CRL_REG_LEN_08BIT, 0x01}, + {0xd05c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd05d, CRL_REG_LEN_08BIT, 0xce}, + {0xd05e, CRL_REG_LEN_08BIT, 0x02}, + {0xd05f, CRL_REG_LEN_08BIT, 0xa4}, + {0xd060, CRL_REG_LEN_08BIT, 0x9c}, + {0xd061, CRL_REG_LEN_08BIT, 0xa0}, + {0xd062, CRL_REG_LEN_08BIT, 0x00}, + {0xd063, CRL_REG_LEN_08BIT, 0x00}, + {0xd064, CRL_REG_LEN_08BIT, 0x84}, + {0xd065, CRL_REG_LEN_08BIT, 0x6e}, + {0xd066, CRL_REG_LEN_08BIT, 0x00}, + {0xd067, CRL_REG_LEN_08BIT, 0x00}, + {0xd068, CRL_REG_LEN_08BIT, 0xd8}, + {0xd069, CRL_REG_LEN_08BIT, 0x03}, + {0xd06a, CRL_REG_LEN_08BIT, 0x28}, + {0xd06b, CRL_REG_LEN_08BIT, 0x76}, + {0xd06c, CRL_REG_LEN_08BIT, 0x1a}, + {0xd06d, CRL_REG_LEN_08BIT, 0x00}, + {0xd06e, CRL_REG_LEN_08BIT, 0x00}, + {0xd06f, CRL_REG_LEN_08BIT, 0x01}, + {0xd070, CRL_REG_LEN_08BIT, 0xaa}, + {0xd071, CRL_REG_LEN_08BIT, 0x10}, + {0xd072, CRL_REG_LEN_08BIT, 0x03}, + {0xd073, CRL_REG_LEN_08BIT, 0xf0}, + {0xd074, CRL_REG_LEN_08BIT, 0x18}, + {0xd075, CRL_REG_LEN_08BIT, 0x60}, + {0xd076, CRL_REG_LEN_08BIT, 0x00}, + {0xd077, CRL_REG_LEN_08BIT, 0x01}, + {0xd078, CRL_REG_LEN_08BIT, 0xa8}, + {0xd079, CRL_REG_LEN_08BIT, 0x63}, + {0xd07a, CRL_REG_LEN_08BIT, 0x07}, + {0xd07b, CRL_REG_LEN_08BIT, 0x80}, + {0xd07c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd07d, CRL_REG_LEN_08BIT, 0xa0}, + {0xd07e, CRL_REG_LEN_08BIT, 0x00}, + {0xd07f, CRL_REG_LEN_08BIT, 0x04}, + {0xd080, CRL_REG_LEN_08BIT, 0x18}, + {0xd081, CRL_REG_LEN_08BIT, 0xc0}, + {0xd082, CRL_REG_LEN_08BIT, 0x00}, + {0xd083, CRL_REG_LEN_08BIT, 0x00}, + {0xd084, CRL_REG_LEN_08BIT, 0xa8}, + {0xd085, CRL_REG_LEN_08BIT, 0xc6}, + {0xd086, CRL_REG_LEN_08BIT, 0x00}, + {0xd087, CRL_REG_LEN_08BIT, 0x00}, + {0xd088, CRL_REG_LEN_08BIT, 0x8c}, + {0xd089, CRL_REG_LEN_08BIT, 0x63}, + {0xd08a, CRL_REG_LEN_08BIT, 0x00}, + {0xd08b, CRL_REG_LEN_08BIT, 0x00}, + {0xd08c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd08d, CRL_REG_LEN_08BIT, 0x01}, + {0xd08e, CRL_REG_LEN_08BIT, 0x28}, + {0xd08f, CRL_REG_LEN_08BIT, 0x14}, + {0xd090, CRL_REG_LEN_08BIT, 0xd4}, + {0xd091, CRL_REG_LEN_08BIT, 0x01}, + {0xd092, CRL_REG_LEN_08BIT, 0x30}, + {0xd093, CRL_REG_LEN_08BIT, 0x18}, + {0xd094, CRL_REG_LEN_08BIT, 0x07}, + {0xd095, CRL_REG_LEN_08BIT, 0xff}, + {0xd096, CRL_REG_LEN_08BIT, 0xf8}, + {0xd097, CRL_REG_LEN_08BIT, 0xfd}, + {0xd098, CRL_REG_LEN_08BIT, 0x9c}, + {0xd099, CRL_REG_LEN_08BIT, 0x80}, + {0xd09a, CRL_REG_LEN_08BIT, 0x00}, + {0xd09b, CRL_REG_LEN_08BIT, 0x03}, + {0xd09c, CRL_REG_LEN_08BIT, 0xa5}, + {0xd09d, CRL_REG_LEN_08BIT, 0x6b}, + {0xd09e, CRL_REG_LEN_08BIT, 0x00}, + {0xd09f, CRL_REG_LEN_08BIT, 0xff}, + {0xd0a0, CRL_REG_LEN_08BIT, 0x18}, + {0xd0a1, CRL_REG_LEN_08BIT, 0xc0}, + {0xd0a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0a3, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a4, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0a5, CRL_REG_LEN_08BIT, 0xc6}, + {0xd0a6, CRL_REG_LEN_08BIT, 0x01}, + {0xd0a7, CRL_REG_LEN_08BIT, 0x02}, + {0xd0a8, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0a9, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0aa, CRL_REG_LEN_08BIT, 0x58}, + {0xd0ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ac, CRL_REG_LEN_08BIT, 0x84}, + {0xd0ad, CRL_REG_LEN_08BIT, 0x8e}, + {0xd0ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd0af, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd0b1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd0b2, CRL_REG_LEN_08BIT, 0x30}, + {0xd0b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b4, CRL_REG_LEN_08BIT, 0x98}, + {0xd0b5, CRL_REG_LEN_08BIT, 0xb0}, + {0xd0b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0b8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0b9, CRL_REG_LEN_08BIT, 0x64}, + {0xd0ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd0bb, CRL_REG_LEN_08BIT, 0x6e}, + {0xd0bc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0bd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd0be, CRL_REG_LEN_08BIT, 0x18}, + {0xd0bf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c0, CRL_REG_LEN_08BIT, 0x10}, + {0xd0c1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c3, CRL_REG_LEN_08BIT, 0x06}, + {0xd0c4, CRL_REG_LEN_08BIT, 0x95}, + {0xd0c5, CRL_REG_LEN_08BIT, 0x8b}, + {0xd0c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0c8, CRL_REG_LEN_08BIT, 0x94}, + {0xd0c9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd0ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd0cb, CRL_REG_LEN_08BIT, 0x70}, + {0xd0cc, CRL_REG_LEN_08BIT, 0xe5}, + {0xd0cd, CRL_REG_LEN_08BIT, 0x65}, + {0xd0ce, CRL_REG_LEN_08BIT, 0x60}, + {0xd0cf, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd0d1, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d3, CRL_REG_LEN_08BIT, 0x62}, + {0xd0d4, CRL_REG_LEN_08BIT, 0x15}, + {0xd0d5, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0d8, CRL_REG_LEN_08BIT, 0x18}, + {0xd0d9, CRL_REG_LEN_08BIT, 0x60}, + {0xd0da, CRL_REG_LEN_08BIT, 0x80}, + {0xd0db, CRL_REG_LEN_08BIT, 0x06}, + {0xd0dc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0dd, CRL_REG_LEN_08BIT, 0x83}, + {0xd0de, CRL_REG_LEN_08BIT, 0x38}, + {0xd0df, CRL_REG_LEN_08BIT, 0x29}, + {0xd0e0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e1, CRL_REG_LEN_08BIT, 0xe3}, + {0xd0e2, CRL_REG_LEN_08BIT, 0x40}, + {0xd0e3, CRL_REG_LEN_08BIT, 0x08}, + {0xd0e4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0e5, CRL_REG_LEN_08BIT, 0x84}, + {0xd0e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0e8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0e9, CRL_REG_LEN_08BIT, 0xa3}, + {0xd0ea, CRL_REG_LEN_08BIT, 0x40}, + {0xd0eb, CRL_REG_LEN_08BIT, 0x09}, + {0xd0ec, CRL_REG_LEN_08BIT, 0xa8}, + {0xd0ed, CRL_REG_LEN_08BIT, 0xc3}, + {0xd0ee, CRL_REG_LEN_08BIT, 0x38}, + {0xd0ef, CRL_REG_LEN_08BIT, 0x2a}, + {0xd0f0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f1, CRL_REG_LEN_08BIT, 0x07}, + {0xd0f2, CRL_REG_LEN_08BIT, 0x20}, + {0xd0f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd0f5, CRL_REG_LEN_08BIT, 0x66}, + {0xd0f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd0f8, CRL_REG_LEN_08BIT, 0xd8}, + {0xd0f9, CRL_REG_LEN_08BIT, 0x05}, + {0xd0fa, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd0fc, CRL_REG_LEN_08BIT, 0x18}, + {0xd0fd, CRL_REG_LEN_08BIT, 0x60}, + {0xd0fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd0ff, CRL_REG_LEN_08BIT, 0x01}, + {0xd100, CRL_REG_LEN_08BIT, 0x98}, + {0xd101, CRL_REG_LEN_08BIT, 0x90}, + {0xd102, CRL_REG_LEN_08BIT, 0x00}, + {0xd103, CRL_REG_LEN_08BIT, 0x00}, + {0xd104, CRL_REG_LEN_08BIT, 0x84}, + {0xd105, CRL_REG_LEN_08BIT, 0xae}, + {0xd106, CRL_REG_LEN_08BIT, 0x00}, + {0xd107, CRL_REG_LEN_08BIT, 0x00}, + {0xd108, CRL_REG_LEN_08BIT, 0xa8}, + {0xd109, CRL_REG_LEN_08BIT, 0x63}, + {0xd10a, CRL_REG_LEN_08BIT, 0x06}, + {0xd10b, CRL_REG_LEN_08BIT, 0x4c}, + {0xd10c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd10d, CRL_REG_LEN_08BIT, 0xc0}, + {0xd10e, CRL_REG_LEN_08BIT, 0x00}, + {0xd10f, CRL_REG_LEN_08BIT, 0x00}, + {0xd110, CRL_REG_LEN_08BIT, 0xd8}, + {0xd111, CRL_REG_LEN_08BIT, 0x03}, + {0xd112, CRL_REG_LEN_08BIT, 0x30}, + {0xd113, CRL_REG_LEN_08BIT, 0x00}, + {0xd114, CRL_REG_LEN_08BIT, 0x8c}, + {0xd115, CRL_REG_LEN_08BIT, 0x65}, + {0xd116, CRL_REG_LEN_08BIT, 0x00}, + {0xd117, CRL_REG_LEN_08BIT, 0x6e}, + {0xd118, CRL_REG_LEN_08BIT, 0xe5}, + {0xd119, CRL_REG_LEN_08BIT, 0x84}, + {0xd11a, CRL_REG_LEN_08BIT, 0x18}, + {0xd11b, CRL_REG_LEN_08BIT, 0x00}, + {0xd11c, CRL_REG_LEN_08BIT, 0x10}, + {0xd11d, CRL_REG_LEN_08BIT, 0x00}, + {0xd11e, CRL_REG_LEN_08BIT, 0x00}, + {0xd11f, CRL_REG_LEN_08BIT, 0x07}, + {0xd120, CRL_REG_LEN_08BIT, 0x18}, + {0xd121, CRL_REG_LEN_08BIT, 0x80}, + {0xd122, CRL_REG_LEN_08BIT, 0x80}, + {0xd123, CRL_REG_LEN_08BIT, 0x06}, + {0xd124, CRL_REG_LEN_08BIT, 0x94}, + {0xd125, CRL_REG_LEN_08BIT, 0x65}, + {0xd126, CRL_REG_LEN_08BIT, 0x00}, + {0xd127, CRL_REG_LEN_08BIT, 0x70}, + {0xd128, CRL_REG_LEN_08BIT, 0xe5}, + {0xd129, CRL_REG_LEN_08BIT, 0x43}, + {0xd12a, CRL_REG_LEN_08BIT, 0x60}, + {0xd12b, CRL_REG_LEN_08BIT, 0x00}, + {0xd12c, CRL_REG_LEN_08BIT, 0x0c}, + {0xd12d, CRL_REG_LEN_08BIT, 0x00}, + {0xd12e, CRL_REG_LEN_08BIT, 0x00}, + {0xd12f, CRL_REG_LEN_08BIT, 0x3e}, + {0xd130, CRL_REG_LEN_08BIT, 0xa8}, + {0xd131, CRL_REG_LEN_08BIT, 0x64}, + {0xd132, CRL_REG_LEN_08BIT, 0x38}, + {0xd133, CRL_REG_LEN_08BIT, 0x24}, + {0xd134, CRL_REG_LEN_08BIT, 0x18}, + {0xd135, CRL_REG_LEN_08BIT, 0x80}, + {0xd136, CRL_REG_LEN_08BIT, 0x80}, + {0xd137, CRL_REG_LEN_08BIT, 0x06}, + {0xd138, CRL_REG_LEN_08BIT, 0xa8}, + {0xd139, CRL_REG_LEN_08BIT, 0x64}, + {0xd13a, CRL_REG_LEN_08BIT, 0x38}, + {0xd13b, CRL_REG_LEN_08BIT, 0x24}, + {0xd13c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd13d, CRL_REG_LEN_08BIT, 0x63}, + {0xd13e, CRL_REG_LEN_08BIT, 0x00}, + {0xd13f, CRL_REG_LEN_08BIT, 0x00}, + {0xd140, CRL_REG_LEN_08BIT, 0xa4}, + {0xd141, CRL_REG_LEN_08BIT, 0x63}, + {0xd142, CRL_REG_LEN_08BIT, 0x00}, + {0xd143, CRL_REG_LEN_08BIT, 0x40}, + {0xd144, CRL_REG_LEN_08BIT, 0xbc}, + {0xd145, CRL_REG_LEN_08BIT, 0x23}, + {0xd146, CRL_REG_LEN_08BIT, 0x00}, + {0xd147, CRL_REG_LEN_08BIT, 0x00}, + {0xd148, CRL_REG_LEN_08BIT, 0x0c}, + {0xd149, CRL_REG_LEN_08BIT, 0x00}, + {0xd14a, CRL_REG_LEN_08BIT, 0x00}, + {0xd14b, CRL_REG_LEN_08BIT, 0x2a}, + {0xd14c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd14d, CRL_REG_LEN_08BIT, 0x64}, + {0xd14e, CRL_REG_LEN_08BIT, 0x6e}, + {0xd14f, CRL_REG_LEN_08BIT, 0x44}, + {0xd150, CRL_REG_LEN_08BIT, 0x19}, + {0xd151, CRL_REG_LEN_08BIT, 0x00}, + {0xd152, CRL_REG_LEN_08BIT, 0x80}, + {0xd153, CRL_REG_LEN_08BIT, 0x06}, + {0xd154, CRL_REG_LEN_08BIT, 0xa8}, + {0xd155, CRL_REG_LEN_08BIT, 0xe8}, + {0xd156, CRL_REG_LEN_08BIT, 0x3d}, + {0xd157, CRL_REG_LEN_08BIT, 0x05}, + {0xd158, CRL_REG_LEN_08BIT, 0x8c}, + {0xd159, CRL_REG_LEN_08BIT, 0x67}, + {0xd15a, CRL_REG_LEN_08BIT, 0x00}, + {0xd15b, CRL_REG_LEN_08BIT, 0x00}, + {0xd15c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd15d, CRL_REG_LEN_08BIT, 0x63}, + {0xd15e, CRL_REG_LEN_08BIT, 0x00}, + {0xd15f, CRL_REG_LEN_08BIT, 0x18}, + {0xd160, CRL_REG_LEN_08BIT, 0xb8}, + {0xd161, CRL_REG_LEN_08BIT, 0x63}, + {0xd162, CRL_REG_LEN_08BIT, 0x00}, + {0xd163, CRL_REG_LEN_08BIT, 0x98}, + {0xd164, CRL_REG_LEN_08BIT, 0xbc}, + {0xd165, CRL_REG_LEN_08BIT, 0x03}, + {0xd166, CRL_REG_LEN_08BIT, 0x00}, + {0xd167, CRL_REG_LEN_08BIT, 0x00}, + {0xd168, CRL_REG_LEN_08BIT, 0x10}, + {0xd169, CRL_REG_LEN_08BIT, 0x00}, + {0xd16a, CRL_REG_LEN_08BIT, 0x00}, + {0xd16b, CRL_REG_LEN_08BIT, 0x10}, + {0xd16c, CRL_REG_LEN_08BIT, 0xa9}, + {0xd16d, CRL_REG_LEN_08BIT, 0x48}, + {0xd16e, CRL_REG_LEN_08BIT, 0x67}, + {0xd16f, CRL_REG_LEN_08BIT, 0x02}, + {0xd170, CRL_REG_LEN_08BIT, 0xb8}, + {0xd171, CRL_REG_LEN_08BIT, 0xa3}, + {0xd172, CRL_REG_LEN_08BIT, 0x00}, + {0xd173, CRL_REG_LEN_08BIT, 0x19}, + {0xd174, CRL_REG_LEN_08BIT, 0x8c}, + {0xd175, CRL_REG_LEN_08BIT, 0x8a}, + {0xd176, CRL_REG_LEN_08BIT, 0x00}, + {0xd177, CRL_REG_LEN_08BIT, 0x00}, + {0xd178, CRL_REG_LEN_08BIT, 0xa9}, + {0xd179, CRL_REG_LEN_08BIT, 0x68}, + {0xd17a, CRL_REG_LEN_08BIT, 0x67}, + {0xd17b, CRL_REG_LEN_08BIT, 0x03}, + {0xd17c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd17d, CRL_REG_LEN_08BIT, 0xc4}, + {0xd17e, CRL_REG_LEN_08BIT, 0x00}, + {0xd17f, CRL_REG_LEN_08BIT, 0x08}, + {0xd180, CRL_REG_LEN_08BIT, 0x8c}, + {0xd181, CRL_REG_LEN_08BIT, 0x6b}, + {0xd182, CRL_REG_LEN_08BIT, 0x00}, + {0xd183, CRL_REG_LEN_08BIT, 0x00}, + {0xd184, CRL_REG_LEN_08BIT, 0xb8}, + {0xd185, CRL_REG_LEN_08BIT, 0x85}, + {0xd186, CRL_REG_LEN_08BIT, 0x00}, + {0xd187, CRL_REG_LEN_08BIT, 0x98}, + {0xd188, CRL_REG_LEN_08BIT, 0xe0}, + {0xd189, CRL_REG_LEN_08BIT, 0x63}, + {0xd18a, CRL_REG_LEN_08BIT, 0x30}, + {0xd18b, CRL_REG_LEN_08BIT, 0x04}, + {0xd18c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd18d, CRL_REG_LEN_08BIT, 0x64}, + {0xd18e, CRL_REG_LEN_08BIT, 0x18}, + {0xd18f, CRL_REG_LEN_08BIT, 0x00}, + {0xd190, CRL_REG_LEN_08BIT, 0xa4}, + {0xd191, CRL_REG_LEN_08BIT, 0x83}, + {0xd192, CRL_REG_LEN_08BIT, 0xff}, + {0xd193, CRL_REG_LEN_08BIT, 0xff}, + {0xd194, CRL_REG_LEN_08BIT, 0xb8}, + {0xd195, CRL_REG_LEN_08BIT, 0x64}, + {0xd196, CRL_REG_LEN_08BIT, 0x00}, + {0xd197, CRL_REG_LEN_08BIT, 0x48}, + {0xd198, CRL_REG_LEN_08BIT, 0xd8}, + {0xd199, CRL_REG_LEN_08BIT, 0x0a}, + {0xd19a, CRL_REG_LEN_08BIT, 0x18}, + {0xd19b, CRL_REG_LEN_08BIT, 0x00}, + {0xd19c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd19d, CRL_REG_LEN_08BIT, 0x0b}, + {0xd19e, CRL_REG_LEN_08BIT, 0x20}, + {0xd19f, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a0, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1a1, CRL_REG_LEN_08BIT, 0x60}, + {0xd1a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1a5, CRL_REG_LEN_08BIT, 0x07}, + {0xd1a6, CRL_REG_LEN_08BIT, 0x18}, + {0xd1a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1a8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1a9, CRL_REG_LEN_08BIT, 0x68}, + {0xd1aa, CRL_REG_LEN_08BIT, 0x38}, + {0xd1ab, CRL_REG_LEN_08BIT, 0x22}, + {0xd1ac, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ad, CRL_REG_LEN_08BIT, 0x80}, + {0xd1ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd1af, CRL_REG_LEN_08BIT, 0x70}, + {0xd1b0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1b1, CRL_REG_LEN_08BIT, 0xe8}, + {0xd1b2, CRL_REG_LEN_08BIT, 0x38}, + {0xd1b3, CRL_REG_LEN_08BIT, 0x43}, + {0xd1b4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd1b5, CRL_REG_LEN_08BIT, 0x03}, + {0xd1b6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1b8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1b9, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bb, CRL_REG_LEN_08BIT, 0x00}, + {0xd1bc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1bd, CRL_REG_LEN_08BIT, 0xc8}, + {0xd1be, CRL_REG_LEN_08BIT, 0x38}, + {0xd1bf, CRL_REG_LEN_08BIT, 0x42}, + {0xd1c0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1c1, CRL_REG_LEN_08BIT, 0x66}, + {0xd1c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1c5, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1c7, CRL_REG_LEN_08BIT, 0x01}, + {0xd1c8, CRL_REG_LEN_08BIT, 0xb8}, + {0xd1c9, CRL_REG_LEN_08BIT, 0x83}, + {0xd1ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cb, CRL_REG_LEN_08BIT, 0x08}, + {0xd1cc, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1cd, CRL_REG_LEN_08BIT, 0xa5}, + {0xd1ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd1cf, CRL_REG_LEN_08BIT, 0xff}, + {0xd1d0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1d1, CRL_REG_LEN_08BIT, 0x67}, + {0xd1d2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d4, CRL_REG_LEN_08BIT, 0xe0}, + {0xd1d5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1d6, CRL_REG_LEN_08BIT, 0x20}, + {0xd1d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1d8, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1d9, CRL_REG_LEN_08BIT, 0x63}, + {0xd1da, CRL_REG_LEN_08BIT, 0xff}, + {0xd1db, CRL_REG_LEN_08BIT, 0xff}, + {0xd1dc, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1dd, CRL_REG_LEN_08BIT, 0x43}, + {0xd1de, CRL_REG_LEN_08BIT, 0x00}, + {0xd1df, CRL_REG_LEN_08BIT, 0x07}, + {0xd1e0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd1e1, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e3, CRL_REG_LEN_08BIT, 0x5b}, + {0xd1e4, CRL_REG_LEN_08BIT, 0xbc}, + {0xd1e5, CRL_REG_LEN_08BIT, 0x05}, + {0xd1e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1e7, CRL_REG_LEN_08BIT, 0x02}, + {0xd1e8, CRL_REG_LEN_08BIT, 0x03}, + {0xd1e9, CRL_REG_LEN_08BIT, 0xff}, + {0xd1ea, CRL_REG_LEN_08BIT, 0xff}, + {0xd1eb, CRL_REG_LEN_08BIT, 0xf6}, + {0xd1ec, CRL_REG_LEN_08BIT, 0x9c}, + {0xd1ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xd1ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd1ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f1, CRL_REG_LEN_08BIT, 0xa4}, + {0xd1f2, CRL_REG_LEN_08BIT, 0x55}, + {0xd1f3, CRL_REG_LEN_08BIT, 0x86}, + {0xd1f4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd1f5, CRL_REG_LEN_08BIT, 0x63}, + {0xd1f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd1f8, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1f9, CRL_REG_LEN_08BIT, 0xc4}, + {0xd1fa, CRL_REG_LEN_08BIT, 0x6e}, + {0xd1fb, CRL_REG_LEN_08BIT, 0x45}, + {0xd1fc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd1fd, CRL_REG_LEN_08BIT, 0xe4}, + {0xd1fe, CRL_REG_LEN_08BIT, 0x55}, + {0xd1ff, CRL_REG_LEN_08BIT, 0x87}, + {0xd200, CRL_REG_LEN_08BIT, 0xd8}, + {0xd201, CRL_REG_LEN_08BIT, 0x05}, + {0xd202, CRL_REG_LEN_08BIT, 0x18}, + {0xd203, CRL_REG_LEN_08BIT, 0x00}, + {0xd204, CRL_REG_LEN_08BIT, 0x8c}, + {0xd205, CRL_REG_LEN_08BIT, 0x66}, + {0xd206, CRL_REG_LEN_08BIT, 0x00}, + {0xd207, CRL_REG_LEN_08BIT, 0x00}, + {0xd208, CRL_REG_LEN_08BIT, 0xa8}, + {0xd209, CRL_REG_LEN_08BIT, 0xa4}, + {0xd20a, CRL_REG_LEN_08BIT, 0x6e}, + {0xd20b, CRL_REG_LEN_08BIT, 0x46}, + {0xd20c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd20d, CRL_REG_LEN_08BIT, 0x07}, + {0xd20e, CRL_REG_LEN_08BIT, 0x18}, + {0xd20f, CRL_REG_LEN_08BIT, 0x00}, + {0xd210, CRL_REG_LEN_08BIT, 0xa8}, + {0xd211, CRL_REG_LEN_08BIT, 0x84}, + {0xd212, CRL_REG_LEN_08BIT, 0x55}, + {0xd213, CRL_REG_LEN_08BIT, 0x88}, + {0xd214, CRL_REG_LEN_08BIT, 0x8c}, + {0xd215, CRL_REG_LEN_08BIT, 0x65}, + {0xd216, CRL_REG_LEN_08BIT, 0x00}, + {0xd217, CRL_REG_LEN_08BIT, 0x00}, + {0xd218, CRL_REG_LEN_08BIT, 0xd8}, + {0xd219, CRL_REG_LEN_08BIT, 0x04}, + {0xd21a, CRL_REG_LEN_08BIT, 0x18}, + {0xd21b, CRL_REG_LEN_08BIT, 0x00}, + {0xd21c, CRL_REG_LEN_08BIT, 0x03}, + {0xd21d, CRL_REG_LEN_08BIT, 0xff}, + {0xd21e, CRL_REG_LEN_08BIT, 0xff}, + {0xd21f, CRL_REG_LEN_08BIT, 0xce}, + {0xd220, CRL_REG_LEN_08BIT, 0x19}, + {0xd221, CRL_REG_LEN_08BIT, 0x00}, + {0xd222, CRL_REG_LEN_08BIT, 0x80}, + {0xd223, CRL_REG_LEN_08BIT, 0x06}, + {0xd224, CRL_REG_LEN_08BIT, 0x8c}, + {0xd225, CRL_REG_LEN_08BIT, 0x63}, + {0xd226, CRL_REG_LEN_08BIT, 0x00}, + {0xd227, CRL_REG_LEN_08BIT, 0x00}, + {0xd228, CRL_REG_LEN_08BIT, 0xa4}, + {0xd229, CRL_REG_LEN_08BIT, 0x63}, + {0xd22a, CRL_REG_LEN_08BIT, 0x00}, + {0xd22b, CRL_REG_LEN_08BIT, 0x40}, + {0xd22c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd22d, CRL_REG_LEN_08BIT, 0x23}, + {0xd22e, CRL_REG_LEN_08BIT, 0x00}, + {0xd22f, CRL_REG_LEN_08BIT, 0x00}, + {0xd230, CRL_REG_LEN_08BIT, 0x13}, + {0xd231, CRL_REG_LEN_08BIT, 0xff}, + {0xd232, CRL_REG_LEN_08BIT, 0xff}, + {0xd233, CRL_REG_LEN_08BIT, 0xc8}, + {0xd234, CRL_REG_LEN_08BIT, 0x9d}, + {0xd235, CRL_REG_LEN_08BIT, 0x00}, + {0xd236, CRL_REG_LEN_08BIT, 0x00}, + {0xd237, CRL_REG_LEN_08BIT, 0x40}, + {0xd238, CRL_REG_LEN_08BIT, 0xa8}, + {0xd239, CRL_REG_LEN_08BIT, 0x64}, + {0xd23a, CRL_REG_LEN_08BIT, 0x55}, + {0xd23b, CRL_REG_LEN_08BIT, 0x86}, + {0xd23c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd23d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd23e, CRL_REG_LEN_08BIT, 0x55}, + {0xd23f, CRL_REG_LEN_08BIT, 0x87}, + {0xd240, CRL_REG_LEN_08BIT, 0xd8}, + {0xd241, CRL_REG_LEN_08BIT, 0x03}, + {0xd242, CRL_REG_LEN_08BIT, 0x40}, + {0xd243, CRL_REG_LEN_08BIT, 0x00}, + {0xd244, CRL_REG_LEN_08BIT, 0xa8}, + {0xd245, CRL_REG_LEN_08BIT, 0x64}, + {0xd246, CRL_REG_LEN_08BIT, 0x55}, + {0xd247, CRL_REG_LEN_08BIT, 0x88}, + {0xd248, CRL_REG_LEN_08BIT, 0xd8}, + {0xd249, CRL_REG_LEN_08BIT, 0x05}, + {0xd24a, CRL_REG_LEN_08BIT, 0x40}, + {0xd24b, CRL_REG_LEN_08BIT, 0x00}, + {0xd24c, CRL_REG_LEN_08BIT, 0xd8}, + {0xd24d, CRL_REG_LEN_08BIT, 0x03}, + {0xd24e, CRL_REG_LEN_08BIT, 0x40}, + {0xd24f, CRL_REG_LEN_08BIT, 0x00}, + {0xd250, CRL_REG_LEN_08BIT, 0x03}, + {0xd251, CRL_REG_LEN_08BIT, 0xff}, + {0xd252, CRL_REG_LEN_08BIT, 0xff}, + {0xd253, CRL_REG_LEN_08BIT, 0xc1}, + {0xd254, CRL_REG_LEN_08BIT, 0x19}, + {0xd255, CRL_REG_LEN_08BIT, 0x00}, + {0xd256, CRL_REG_LEN_08BIT, 0x80}, + {0xd257, CRL_REG_LEN_08BIT, 0x06}, + {0xd258, CRL_REG_LEN_08BIT, 0x94}, + {0xd259, CRL_REG_LEN_08BIT, 0x84}, + {0xd25a, CRL_REG_LEN_08BIT, 0x00}, + {0xd25b, CRL_REG_LEN_08BIT, 0x72}, + {0xd25c, CRL_REG_LEN_08BIT, 0xe5}, + {0xd25d, CRL_REG_LEN_08BIT, 0xa4}, + {0xd25e, CRL_REG_LEN_08BIT, 0x60}, + {0xd25f, CRL_REG_LEN_08BIT, 0x00}, + {0xd260, CRL_REG_LEN_08BIT, 0x0c}, + {0xd261, CRL_REG_LEN_08BIT, 0x00}, + {0xd262, CRL_REG_LEN_08BIT, 0x00}, + {0xd263, CRL_REG_LEN_08BIT, 0x3f}, + {0xd264, CRL_REG_LEN_08BIT, 0x9d}, + {0xd265, CRL_REG_LEN_08BIT, 0x60}, + {0xd266, CRL_REG_LEN_08BIT, 0x01}, + {0xd267, CRL_REG_LEN_08BIT, 0x00}, + {0xd268, CRL_REG_LEN_08BIT, 0x85}, + {0xd269, CRL_REG_LEN_08BIT, 0x4e}, + {0xd26a, CRL_REG_LEN_08BIT, 0x00}, + {0xd26b, CRL_REG_LEN_08BIT, 0x00}, + {0xd26c, CRL_REG_LEN_08BIT, 0x98}, + {0xd26d, CRL_REG_LEN_08BIT, 0x70}, + {0xd26e, CRL_REG_LEN_08BIT, 0x00}, + {0xd26f, CRL_REG_LEN_08BIT, 0x00}, + {0xd270, CRL_REG_LEN_08BIT, 0x8c}, + {0xd271, CRL_REG_LEN_08BIT, 0x8a}, + {0xd272, CRL_REG_LEN_08BIT, 0x00}, + {0xd273, CRL_REG_LEN_08BIT, 0x6f}, + {0xd274, CRL_REG_LEN_08BIT, 0xe5}, + {0xd275, CRL_REG_LEN_08BIT, 0x63}, + {0xd276, CRL_REG_LEN_08BIT, 0x20}, + {0xd277, CRL_REG_LEN_08BIT, 0x00}, + {0xd278, CRL_REG_LEN_08BIT, 0x10}, + {0xd279, CRL_REG_LEN_08BIT, 0x00}, + {0xd27a, CRL_REG_LEN_08BIT, 0x00}, + {0xd27b, CRL_REG_LEN_08BIT, 0x07}, + {0xd27c, CRL_REG_LEN_08BIT, 0x15}, + {0xd27d, CRL_REG_LEN_08BIT, 0x00}, + {0xd27e, CRL_REG_LEN_08BIT, 0x00}, + {0xd27f, CRL_REG_LEN_08BIT, 0x00}, + {0xd280, CRL_REG_LEN_08BIT, 0x8c}, + {0xd281, CRL_REG_LEN_08BIT, 0xaa}, + {0xd282, CRL_REG_LEN_08BIT, 0x00}, + {0xd283, CRL_REG_LEN_08BIT, 0x6e}, + {0xd284, CRL_REG_LEN_08BIT, 0xe0}, + {0xd285, CRL_REG_LEN_08BIT, 0x63}, + {0xd286, CRL_REG_LEN_08BIT, 0x28}, + {0xd287, CRL_REG_LEN_08BIT, 0x02}, + {0xd288, CRL_REG_LEN_08BIT, 0xe0}, + {0xd289, CRL_REG_LEN_08BIT, 0x84}, + {0xd28a, CRL_REG_LEN_08BIT, 0x28}, + {0xd28b, CRL_REG_LEN_08BIT, 0x02}, + {0xd28c, CRL_REG_LEN_08BIT, 0x07}, + {0xd28d, CRL_REG_LEN_08BIT, 0xff}, + {0xd28e, CRL_REG_LEN_08BIT, 0xf8}, + {0xd28f, CRL_REG_LEN_08BIT, 0x66}, + {0xd290, CRL_REG_LEN_08BIT, 0xe0}, + {0xd291, CRL_REG_LEN_08BIT, 0x63}, + {0xd292, CRL_REG_LEN_08BIT, 0x5b}, + {0xd293, CRL_REG_LEN_08BIT, 0x06}, + {0xd294, CRL_REG_LEN_08BIT, 0x8c}, + {0xd295, CRL_REG_LEN_08BIT, 0x6a}, + {0xd296, CRL_REG_LEN_08BIT, 0x00}, + {0xd297, CRL_REG_LEN_08BIT, 0x77}, + {0xd298, CRL_REG_LEN_08BIT, 0xe0}, + {0xd299, CRL_REG_LEN_08BIT, 0x63}, + {0xd29a, CRL_REG_LEN_08BIT, 0x5b}, + {0xd29b, CRL_REG_LEN_08BIT, 0x06}, + {0xd29c, CRL_REG_LEN_08BIT, 0xbd}, + {0xd29d, CRL_REG_LEN_08BIT, 0x63}, + {0xd29e, CRL_REG_LEN_08BIT, 0x00}, + {0xd29f, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a0, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2a1, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a3, CRL_REG_LEN_08BIT, 0x3c}, + {0xd2a4, CRL_REG_LEN_08BIT, 0x15}, + {0xd2a5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2a8, CRL_REG_LEN_08BIT, 0x8c}, + {0xd2a9, CRL_REG_LEN_08BIT, 0x8a}, + {0xd2aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ab, CRL_REG_LEN_08BIT, 0x78}, + {0xd2ac, CRL_REG_LEN_08BIT, 0xb8}, + {0xd2ad, CRL_REG_LEN_08BIT, 0x63}, + {0xd2ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd2af, CRL_REG_LEN_08BIT, 0x88}, + {0xd2b0, CRL_REG_LEN_08BIT, 0xe1}, + {0xd2b1, CRL_REG_LEN_08BIT, 0x64}, + {0xd2b2, CRL_REG_LEN_08BIT, 0x5b}, + {0xd2b3, CRL_REG_LEN_08BIT, 0x06}, + {0xd2b4, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2b5, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2b8, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2b9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd2bb, CRL_REG_LEN_08BIT, 0x34}, + {0xd2bc, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2bd, CRL_REG_LEN_08BIT, 0x01}, + {0xd2be, CRL_REG_LEN_08BIT, 0x18}, + {0xd2bf, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c0, CRL_REG_LEN_08BIT, 0xb9}, + {0xd2c1, CRL_REG_LEN_08BIT, 0x6b}, + {0xd2c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c3, CRL_REG_LEN_08BIT, 0x88}, + {0xd2c4, CRL_REG_LEN_08BIT, 0x85}, + {0xd2c5, CRL_REG_LEN_08BIT, 0x01}, + {0xd2c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2c7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2c8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2c9, CRL_REG_LEN_08BIT, 0x68}, + {0xd2ca, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cc, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2cd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd2cf, CRL_REG_LEN_08BIT, 0x2c}, + {0xd2d0, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2d1, CRL_REG_LEN_08BIT, 0x01}, + {0xd2d2, CRL_REG_LEN_08BIT, 0x58}, + {0xd2d3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2d4, CRL_REG_LEN_08BIT, 0x84}, + {0xd2d5, CRL_REG_LEN_08BIT, 0x81}, + {0xd2d6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2d7, CRL_REG_LEN_08BIT, 0x14}, + {0xd2d8, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2d9, CRL_REG_LEN_08BIT, 0xa4}, + {0xd2da, CRL_REG_LEN_08BIT, 0x01}, + {0xd2db, CRL_REG_LEN_08BIT, 0x00}, + {0xd2dc, CRL_REG_LEN_08BIT, 0x10}, + {0xd2dd, CRL_REG_LEN_08BIT, 0x00}, + {0xd2de, CRL_REG_LEN_08BIT, 0x00}, + {0xd2df, CRL_REG_LEN_08BIT, 0x05}, + {0xd2e0, CRL_REG_LEN_08BIT, 0x84}, + {0xd2e1, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e3, CRL_REG_LEN_08BIT, 0x18}, + {0xd2e4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd2e5, CRL_REG_LEN_08BIT, 0xa0}, + {0xd2e6, CRL_REG_LEN_08BIT, 0x01}, + {0xd2e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd2e8, CRL_REG_LEN_08BIT, 0xd4}, + {0xd2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xd2ea, CRL_REG_LEN_08BIT, 0x28}, + {0xd2eb, CRL_REG_LEN_08BIT, 0x14}, + {0xd2ec, CRL_REG_LEN_08BIT, 0x84}, + {0xd2ed, CRL_REG_LEN_08BIT, 0xc1}, + {0xd2ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ef, CRL_REG_LEN_08BIT, 0x18}, + {0xd2f0, CRL_REG_LEN_08BIT, 0xbd}, + {0xd2f1, CRL_REG_LEN_08BIT, 0x66}, + {0xd2f2, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f4, CRL_REG_LEN_08BIT, 0x0c}, + {0xd2f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd2f7, CRL_REG_LEN_08BIT, 0x20}, + {0xd2f8, CRL_REG_LEN_08BIT, 0x9d}, + {0xd2f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd2fc, CRL_REG_LEN_08BIT, 0x84}, + {0xd2fd, CRL_REG_LEN_08BIT, 0x61}, + {0xd2fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd2ff, CRL_REG_LEN_08BIT, 0x18}, + {0xd300, CRL_REG_LEN_08BIT, 0xbd}, + {0xd301, CRL_REG_LEN_08BIT, 0xa3}, + {0xd302, CRL_REG_LEN_08BIT, 0x01}, + {0xd303, CRL_REG_LEN_08BIT, 0x00}, + {0xd304, CRL_REG_LEN_08BIT, 0x10}, + {0xd305, CRL_REG_LEN_08BIT, 0x00}, + {0xd306, CRL_REG_LEN_08BIT, 0x00}, + {0xd307, CRL_REG_LEN_08BIT, 0x03}, + {0xd308, CRL_REG_LEN_08BIT, 0x9c}, + {0xd309, CRL_REG_LEN_08BIT, 0x80}, + {0xd30a, CRL_REG_LEN_08BIT, 0x01}, + {0xd30b, CRL_REG_LEN_08BIT, 0x00}, + {0xd30c, CRL_REG_LEN_08BIT, 0xd4}, + {0xd30d, CRL_REG_LEN_08BIT, 0x01}, + {0xd30e, CRL_REG_LEN_08BIT, 0x20}, + {0xd30f, CRL_REG_LEN_08BIT, 0x18}, + {0xd310, CRL_REG_LEN_08BIT, 0x18}, + {0xd311, CRL_REG_LEN_08BIT, 0x60}, + {0xd312, CRL_REG_LEN_08BIT, 0x80}, + {0xd313, CRL_REG_LEN_08BIT, 0x06}, + {0xd314, CRL_REG_LEN_08BIT, 0x85}, + {0xd315, CRL_REG_LEN_08BIT, 0x01}, + {0xd316, CRL_REG_LEN_08BIT, 0x00}, + {0xd317, CRL_REG_LEN_08BIT, 0x14}, + {0xd318, CRL_REG_LEN_08BIT, 0xa8}, + {0xd319, CRL_REG_LEN_08BIT, 0x83}, + {0xd31a, CRL_REG_LEN_08BIT, 0x38}, + {0xd31b, CRL_REG_LEN_08BIT, 0x29}, + {0xd31c, CRL_REG_LEN_08BIT, 0xa8}, + {0xd31d, CRL_REG_LEN_08BIT, 0xc3}, + {0xd31e, CRL_REG_LEN_08BIT, 0x40}, + {0xd31f, CRL_REG_LEN_08BIT, 0x08}, + {0xd320, CRL_REG_LEN_08BIT, 0x8c}, + {0xd321, CRL_REG_LEN_08BIT, 0x84}, + {0xd322, CRL_REG_LEN_08BIT, 0x00}, + {0xd323, CRL_REG_LEN_08BIT, 0x00}, + {0xd324, CRL_REG_LEN_08BIT, 0xa8}, + {0xd325, CRL_REG_LEN_08BIT, 0xa3}, + {0xd326, CRL_REG_LEN_08BIT, 0x38}, + {0xd327, CRL_REG_LEN_08BIT, 0x2a}, + {0xd328, CRL_REG_LEN_08BIT, 0xa8}, + {0xd329, CRL_REG_LEN_08BIT, 0xe3}, + {0xd32a, CRL_REG_LEN_08BIT, 0x40}, + {0xd32b, CRL_REG_LEN_08BIT, 0x09}, + {0xd32c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd32d, CRL_REG_LEN_08BIT, 0x64}, + {0xd32e, CRL_REG_LEN_08BIT, 0x40}, + {0xd32f, CRL_REG_LEN_08BIT, 0x00}, + {0xd330, CRL_REG_LEN_08BIT, 0xd8}, + {0xd331, CRL_REG_LEN_08BIT, 0x06}, + {0xd332, CRL_REG_LEN_08BIT, 0x18}, + {0xd333, CRL_REG_LEN_08BIT, 0x00}, + {0xd334, CRL_REG_LEN_08BIT, 0x8c}, + {0xd335, CRL_REG_LEN_08BIT, 0x65}, + {0xd336, CRL_REG_LEN_08BIT, 0x00}, + {0xd337, CRL_REG_LEN_08BIT, 0x00}, + {0xd338, CRL_REG_LEN_08BIT, 0x84}, + {0xd339, CRL_REG_LEN_08BIT, 0x81}, + {0xd33a, CRL_REG_LEN_08BIT, 0x00}, + {0xd33b, CRL_REG_LEN_08BIT, 0x18}, + {0xd33c, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33d, CRL_REG_LEN_08BIT, 0xe3}, + {0xd33e, CRL_REG_LEN_08BIT, 0x20}, + {0xd33f, CRL_REG_LEN_08BIT, 0x00}, + {0xd340, CRL_REG_LEN_08BIT, 0xd8}, + {0xd341, CRL_REG_LEN_08BIT, 0x07}, + {0xd342, CRL_REG_LEN_08BIT, 0xf8}, + {0xd343, CRL_REG_LEN_08BIT, 0x00}, + {0xd344, CRL_REG_LEN_08BIT, 0x03}, + {0xd345, CRL_REG_LEN_08BIT, 0xff}, + {0xd346, CRL_REG_LEN_08BIT, 0xff}, + {0xd347, CRL_REG_LEN_08BIT, 0x6f}, + {0xd348, CRL_REG_LEN_08BIT, 0x18}, + {0xd349, CRL_REG_LEN_08BIT, 0x60}, + {0xd34a, CRL_REG_LEN_08BIT, 0x00}, + {0xd34b, CRL_REG_LEN_08BIT, 0x01}, + {0xd34c, CRL_REG_LEN_08BIT, 0x0f}, + {0xd34d, CRL_REG_LEN_08BIT, 0xff}, + {0xd34e, CRL_REG_LEN_08BIT, 0xff}, + {0xd34f, CRL_REG_LEN_08BIT, 0x9d}, + {0xd350, CRL_REG_LEN_08BIT, 0x18}, + {0xd351, CRL_REG_LEN_08BIT, 0x60}, + {0xd352, CRL_REG_LEN_08BIT, 0x80}, + {0xd353, CRL_REG_LEN_08BIT, 0x06}, + {0xd354, CRL_REG_LEN_08BIT, 0x00}, + {0xd355, CRL_REG_LEN_08BIT, 0x00}, + {0xd356, CRL_REG_LEN_08BIT, 0x00}, + {0xd357, CRL_REG_LEN_08BIT, 0x11}, + {0xd358, CRL_REG_LEN_08BIT, 0xa8}, + {0xd359, CRL_REG_LEN_08BIT, 0x83}, + {0xd35a, CRL_REG_LEN_08BIT, 0x6e}, + {0xd35b, CRL_REG_LEN_08BIT, 0x43}, + {0xd35c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd35d, CRL_REG_LEN_08BIT, 0x6c}, + {0xd35e, CRL_REG_LEN_08BIT, 0x28}, + {0xd35f, CRL_REG_LEN_08BIT, 0x02}, + {0xd360, CRL_REG_LEN_08BIT, 0xe0}, + {0xd361, CRL_REG_LEN_08BIT, 0x84}, + {0xd362, CRL_REG_LEN_08BIT, 0x28}, + {0xd363, CRL_REG_LEN_08BIT, 0x02}, + {0xd364, CRL_REG_LEN_08BIT, 0x07}, + {0xd365, CRL_REG_LEN_08BIT, 0xff}, + {0xd366, CRL_REG_LEN_08BIT, 0xf8}, + {0xd367, CRL_REG_LEN_08BIT, 0x30}, + {0xd368, CRL_REG_LEN_08BIT, 0xb8}, + {0xd369, CRL_REG_LEN_08BIT, 0x63}, + {0xd36a, CRL_REG_LEN_08BIT, 0x00}, + {0xd36b, CRL_REG_LEN_08BIT, 0x08}, + {0xd36c, CRL_REG_LEN_08BIT, 0x03}, + {0xd36d, CRL_REG_LEN_08BIT, 0xff}, + {0xd36e, CRL_REG_LEN_08BIT, 0xff}, + {0xd36f, CRL_REG_LEN_08BIT, 0xc0}, + {0xd370, CRL_REG_LEN_08BIT, 0x85}, + {0xd371, CRL_REG_LEN_08BIT, 0x4e}, + {0xd372, CRL_REG_LEN_08BIT, 0x00}, + {0xd373, CRL_REG_LEN_08BIT, 0x00}, + {0xd374, CRL_REG_LEN_08BIT, 0x03}, + {0xd375, CRL_REG_LEN_08BIT, 0xff}, + {0xd376, CRL_REG_LEN_08BIT, 0xff}, + {0xd377, CRL_REG_LEN_08BIT, 0xe7}, + {0xd378, CRL_REG_LEN_08BIT, 0xd4}, + {0xd379, CRL_REG_LEN_08BIT, 0x01}, + {0xd37a, CRL_REG_LEN_08BIT, 0x40}, + {0xd37b, CRL_REG_LEN_08BIT, 0x18}, + {0xd37c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd37d, CRL_REG_LEN_08BIT, 0x60}, + {0xd37e, CRL_REG_LEN_08BIT, 0x00}, + {0xd37f, CRL_REG_LEN_08BIT, 0x00}, + {0xd380, CRL_REG_LEN_08BIT, 0x03}, + {0xd381, CRL_REG_LEN_08BIT, 0xff}, + {0xd382, CRL_REG_LEN_08BIT, 0xff}, + {0xd383, CRL_REG_LEN_08BIT, 0xdb}, + {0xd384, CRL_REG_LEN_08BIT, 0xd4}, + {0xd385, CRL_REG_LEN_08BIT, 0x01}, + {0xd386, CRL_REG_LEN_08BIT, 0x18}, + {0xd387, CRL_REG_LEN_08BIT, 0x14}, + {0xd388, CRL_REG_LEN_08BIT, 0x03}, + {0xd389, CRL_REG_LEN_08BIT, 0xff}, + {0xd38a, CRL_REG_LEN_08BIT, 0xff}, + {0xd38b, CRL_REG_LEN_08BIT, 0xce}, + {0xd38c, CRL_REG_LEN_08BIT, 0x9d}, + {0xd38d, CRL_REG_LEN_08BIT, 0x6b}, + {0xd38e, CRL_REG_LEN_08BIT, 0x00}, + {0xd38f, CRL_REG_LEN_08BIT, 0xff}, + {0xd390, CRL_REG_LEN_08BIT, 0x03}, + {0xd391, CRL_REG_LEN_08BIT, 0xff}, + {0xd392, CRL_REG_LEN_08BIT, 0xff}, + {0xd393, CRL_REG_LEN_08BIT, 0xc6}, + {0xd394, CRL_REG_LEN_08BIT, 0x9c}, + {0xd395, CRL_REG_LEN_08BIT, 0x63}, + {0xd396, CRL_REG_LEN_08BIT, 0x00}, + {0xd397, CRL_REG_LEN_08BIT, 0xff}, + {0xd398, CRL_REG_LEN_08BIT, 0xa8}, + {0xd399, CRL_REG_LEN_08BIT, 0xe3}, + {0xd39a, CRL_REG_LEN_08BIT, 0x38}, + {0xd39b, CRL_REG_LEN_08BIT, 0x0f}, + {0xd39c, CRL_REG_LEN_08BIT, 0x8c}, + {0xd39d, CRL_REG_LEN_08BIT, 0x84}, + {0xd39e, CRL_REG_LEN_08BIT, 0x00}, + {0xd39f, CRL_REG_LEN_08BIT, 0x00}, + {0xd3a0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3a1, CRL_REG_LEN_08BIT, 0xa3}, + {0xd3a2, CRL_REG_LEN_08BIT, 0x38}, + {0xd3a3, CRL_REG_LEN_08BIT, 0x0e}, + {0xd3a4, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3a5, CRL_REG_LEN_08BIT, 0xc3}, + {0xd3a6, CRL_REG_LEN_08BIT, 0x6e}, + {0xd3a7, CRL_REG_LEN_08BIT, 0x42}, + {0xd3a8, CRL_REG_LEN_08BIT, 0xd8}, + {0xd3a9, CRL_REG_LEN_08BIT, 0x07}, + {0xd3aa, CRL_REG_LEN_08BIT, 0x20}, + {0xd3ab, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ac, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3ad, CRL_REG_LEN_08BIT, 0x66}, + {0xd3ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd3af, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd3b1, CRL_REG_LEN_08BIT, 0x05}, + {0xd3b2, CRL_REG_LEN_08BIT, 0x18}, + {0xd3b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b4, CRL_REG_LEN_08BIT, 0x85}, + {0xd3b5, CRL_REG_LEN_08BIT, 0x21}, + {0xd3b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3b8, CRL_REG_LEN_08BIT, 0x85}, + {0xd3b9, CRL_REG_LEN_08BIT, 0x41}, + {0xd3ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd3bb, CRL_REG_LEN_08BIT, 0x04}, + {0xd3bc, CRL_REG_LEN_08BIT, 0x85}, + {0xd3bd, CRL_REG_LEN_08BIT, 0x81}, + {0xd3be, CRL_REG_LEN_08BIT, 0x00}, + {0xd3bf, CRL_REG_LEN_08BIT, 0x08}, + {0xd3c0, CRL_REG_LEN_08BIT, 0x85}, + {0xd3c1, CRL_REG_LEN_08BIT, 0xc1}, + {0xd3c2, CRL_REG_LEN_08BIT, 0x00}, + {0xd3c3, CRL_REG_LEN_08BIT, 0x0c}, + {0xd3c4, CRL_REG_LEN_08BIT, 0x86}, + {0xd3c5, CRL_REG_LEN_08BIT, 0x01}, + {0xd3c6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3c7, CRL_REG_LEN_08BIT, 0x10}, + {0xd3c8, CRL_REG_LEN_08BIT, 0x44}, + {0xd3c9, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ca, CRL_REG_LEN_08BIT, 0x48}, + {0xd3cb, CRL_REG_LEN_08BIT, 0x00}, + {0xd3cc, CRL_REG_LEN_08BIT, 0x9c}, + {0xd3cd, CRL_REG_LEN_08BIT, 0x21}, + {0xd3ce, CRL_REG_LEN_08BIT, 0x00}, + {0xd3cf, CRL_REG_LEN_08BIT, 0x1c}, + {0xd3d0, CRL_REG_LEN_08BIT, 0x9c}, + {0xd3d1, CRL_REG_LEN_08BIT, 0x21}, + {0xd3d2, CRL_REG_LEN_08BIT, 0xff}, + {0xd3d3, CRL_REG_LEN_08BIT, 0xfc}, + {0xd3d4, CRL_REG_LEN_08BIT, 0xd4}, + {0xd3d5, CRL_REG_LEN_08BIT, 0x01}, + {0xd3d6, CRL_REG_LEN_08BIT, 0x48}, + {0xd3d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3d8, CRL_REG_LEN_08BIT, 0x18}, + {0xd3d9, CRL_REG_LEN_08BIT, 0x60}, + {0xd3da, CRL_REG_LEN_08BIT, 0x00}, + {0xd3db, CRL_REG_LEN_08BIT, 0x01}, + {0xd3dc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3dd, CRL_REG_LEN_08BIT, 0x63}, + {0xd3de, CRL_REG_LEN_08BIT, 0x07}, + {0xd3df, CRL_REG_LEN_08BIT, 0x80}, + {0xd3e0, CRL_REG_LEN_08BIT, 0x8c}, + {0xd3e1, CRL_REG_LEN_08BIT, 0x63}, + {0xd3e2, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e3, CRL_REG_LEN_08BIT, 0x68}, + {0xd3e4, CRL_REG_LEN_08BIT, 0xbc}, + {0xd3e5, CRL_REG_LEN_08BIT, 0x03}, + {0xd3e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3e8, CRL_REG_LEN_08BIT, 0x10}, + {0xd3e9, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd3eb, CRL_REG_LEN_08BIT, 0x0c}, + {0xd3ec, CRL_REG_LEN_08BIT, 0x15}, + {0xd3ed, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ee, CRL_REG_LEN_08BIT, 0x00}, + {0xd3ef, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f0, CRL_REG_LEN_08BIT, 0x07}, + {0xd3f1, CRL_REG_LEN_08BIT, 0xff}, + {0xd3f2, CRL_REG_LEN_08BIT, 0xd9}, + {0xd3f3, CRL_REG_LEN_08BIT, 0x98}, + {0xd3f4, CRL_REG_LEN_08BIT, 0x15}, + {0xd3f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f6, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd3f8, CRL_REG_LEN_08BIT, 0x18}, + {0xd3f9, CRL_REG_LEN_08BIT, 0x60}, + {0xd3fa, CRL_REG_LEN_08BIT, 0x80}, + {0xd3fb, CRL_REG_LEN_08BIT, 0x06}, + {0xd3fc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd3fd, CRL_REG_LEN_08BIT, 0x63}, + {0xd3fe, CRL_REG_LEN_08BIT, 0xc4}, + {0xd3ff, CRL_REG_LEN_08BIT, 0xb8}, + {0xd400, CRL_REG_LEN_08BIT, 0x8c}, + {0xd401, CRL_REG_LEN_08BIT, 0x63}, + {0xd402, CRL_REG_LEN_08BIT, 0x00}, + {0xd403, CRL_REG_LEN_08BIT, 0x00}, + {0xd404, CRL_REG_LEN_08BIT, 0xbc}, + {0xd405, CRL_REG_LEN_08BIT, 0x23}, + {0xd406, CRL_REG_LEN_08BIT, 0x00}, + {0xd407, CRL_REG_LEN_08BIT, 0x01}, + {0xd408, CRL_REG_LEN_08BIT, 0x10}, + {0xd409, CRL_REG_LEN_08BIT, 0x00}, + {0xd40a, CRL_REG_LEN_08BIT, 0x00}, + {0xd40b, CRL_REG_LEN_08BIT, 0x25}, + {0xd40c, CRL_REG_LEN_08BIT, 0x9d}, + {0xd40d, CRL_REG_LEN_08BIT, 0x00}, + {0xd40e, CRL_REG_LEN_08BIT, 0x00}, + {0xd40f, CRL_REG_LEN_08BIT, 0x00}, + {0xd410, CRL_REG_LEN_08BIT, 0x00}, + {0xd411, CRL_REG_LEN_08BIT, 0x00}, + {0xd412, CRL_REG_LEN_08BIT, 0x00}, + {0xd413, CRL_REG_LEN_08BIT, 0x0b}, + {0xd414, CRL_REG_LEN_08BIT, 0xb8}, + {0xd415, CRL_REG_LEN_08BIT, 0xe8}, + {0xd416, CRL_REG_LEN_08BIT, 0x00}, + {0xd417, CRL_REG_LEN_08BIT, 0x02}, + {0xd418, CRL_REG_LEN_08BIT, 0x07}, + {0xd419, CRL_REG_LEN_08BIT, 0xff}, + {0xd41a, CRL_REG_LEN_08BIT, 0xd6}, + {0xd41b, CRL_REG_LEN_08BIT, 0x24}, + {0xd41c, CRL_REG_LEN_08BIT, 0x15}, + {0xd41d, CRL_REG_LEN_08BIT, 0x00}, + {0xd41e, CRL_REG_LEN_08BIT, 0x00}, + {0xd41f, CRL_REG_LEN_08BIT, 0x00}, + {0xd420, CRL_REG_LEN_08BIT, 0x18}, + {0xd421, CRL_REG_LEN_08BIT, 0x60}, + {0xd422, CRL_REG_LEN_08BIT, 0x80}, + {0xd423, CRL_REG_LEN_08BIT, 0x06}, + {0xd424, CRL_REG_LEN_08BIT, 0xa8}, + {0xd425, CRL_REG_LEN_08BIT, 0x63}, + {0xd426, CRL_REG_LEN_08BIT, 0xc4}, + {0xd427, CRL_REG_LEN_08BIT, 0xb8}, + {0xd428, CRL_REG_LEN_08BIT, 0x8c}, + {0xd429, CRL_REG_LEN_08BIT, 0x63}, + {0xd42a, CRL_REG_LEN_08BIT, 0x00}, + {0xd42b, CRL_REG_LEN_08BIT, 0x00}, + {0xd42c, CRL_REG_LEN_08BIT, 0xbc}, + {0xd42d, CRL_REG_LEN_08BIT, 0x23}, + {0xd42e, CRL_REG_LEN_08BIT, 0x00}, + {0xd42f, CRL_REG_LEN_08BIT, 0x01}, + {0xd430, CRL_REG_LEN_08BIT, 0x10}, + {0xd431, CRL_REG_LEN_08BIT, 0x00}, + {0xd432, CRL_REG_LEN_08BIT, 0x00}, + {0xd433, CRL_REG_LEN_08BIT, 0x1b}, + {0xd434, CRL_REG_LEN_08BIT, 0x9d}, + {0xd435, CRL_REG_LEN_08BIT, 0x00}, + {0xd436, CRL_REG_LEN_08BIT, 0x00}, + {0xd437, CRL_REG_LEN_08BIT, 0x00}, + {0xd438, CRL_REG_LEN_08BIT, 0xb8}, + {0xd439, CRL_REG_LEN_08BIT, 0xe8}, + {0xd43a, CRL_REG_LEN_08BIT, 0x00}, + {0xd43b, CRL_REG_LEN_08BIT, 0x02}, + {0xd43c, CRL_REG_LEN_08BIT, 0x9c}, + {0xd43d, CRL_REG_LEN_08BIT, 0xc0}, + {0xd43e, CRL_REG_LEN_08BIT, 0x00}, + {0xd43f, CRL_REG_LEN_08BIT, 0x00}, + {0xd440, CRL_REG_LEN_08BIT, 0x18}, + {0xd441, CRL_REG_LEN_08BIT, 0xa0}, + {0xd442, CRL_REG_LEN_08BIT, 0x80}, + {0xd443, CRL_REG_LEN_08BIT, 0x06}, + {0xd444, CRL_REG_LEN_08BIT, 0xe0}, + {0xd445, CRL_REG_LEN_08BIT, 0x67}, + {0xd446, CRL_REG_LEN_08BIT, 0x30}, + {0xd447, CRL_REG_LEN_08BIT, 0x00}, + {0xd448, CRL_REG_LEN_08BIT, 0xa8}, + {0xd449, CRL_REG_LEN_08BIT, 0xa5}, + {0xd44a, CRL_REG_LEN_08BIT, 0xce}, + {0xd44b, CRL_REG_LEN_08BIT, 0xb0}, + {0xd44c, CRL_REG_LEN_08BIT, 0x19}, + {0xd44d, CRL_REG_LEN_08BIT, 0x60}, + {0xd44e, CRL_REG_LEN_08BIT, 0x00}, + {0xd44f, CRL_REG_LEN_08BIT, 0x01}, + {0xd450, CRL_REG_LEN_08BIT, 0xa9}, + {0xd451, CRL_REG_LEN_08BIT, 0x6b}, + {0xd452, CRL_REG_LEN_08BIT, 0x06}, + {0xd453, CRL_REG_LEN_08BIT, 0x14}, + {0xd454, CRL_REG_LEN_08BIT, 0xe0}, + {0xd455, CRL_REG_LEN_08BIT, 0x83}, + {0xd456, CRL_REG_LEN_08BIT, 0x28}, + {0xd457, CRL_REG_LEN_08BIT, 0x00}, + {0xd458, CRL_REG_LEN_08BIT, 0x9c}, + {0xd459, CRL_REG_LEN_08BIT, 0xc6}, + {0xd45a, CRL_REG_LEN_08BIT, 0x00}, + {0xd45b, CRL_REG_LEN_08BIT, 0x01}, + {0xd45c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd45d, CRL_REG_LEN_08BIT, 0x63}, + {0xd45e, CRL_REG_LEN_08BIT, 0x18}, + {0xd45f, CRL_REG_LEN_08BIT, 0x00}, + {0xd460, CRL_REG_LEN_08BIT, 0x8c}, + {0xd461, CRL_REG_LEN_08BIT, 0x84}, + {0xd462, CRL_REG_LEN_08BIT, 0x00}, + {0xd463, CRL_REG_LEN_08BIT, 0x00}, + {0xd464, CRL_REG_LEN_08BIT, 0xe0}, + {0xd465, CRL_REG_LEN_08BIT, 0xa3}, + {0xd466, CRL_REG_LEN_08BIT, 0x58}, + {0xd467, CRL_REG_LEN_08BIT, 0x00}, + {0xd468, CRL_REG_LEN_08BIT, 0xa4}, + {0xd469, CRL_REG_LEN_08BIT, 0xc6}, + {0xd46a, CRL_REG_LEN_08BIT, 0x00}, + {0xd46b, CRL_REG_LEN_08BIT, 0xff}, + {0xd46c, CRL_REG_LEN_08BIT, 0xb8}, + {0xd46d, CRL_REG_LEN_08BIT, 0x64}, + {0xd46e, CRL_REG_LEN_08BIT, 0x00}, + {0xd46f, CRL_REG_LEN_08BIT, 0x18}, + {0xd470, CRL_REG_LEN_08BIT, 0xbc}, + {0xd471, CRL_REG_LEN_08BIT, 0x46}, + {0xd472, CRL_REG_LEN_08BIT, 0x00}, + {0xd473, CRL_REG_LEN_08BIT, 0x03}, + {0xd474, CRL_REG_LEN_08BIT, 0x94}, + {0xd475, CRL_REG_LEN_08BIT, 0x85}, + {0xd476, CRL_REG_LEN_08BIT, 0x00}, + {0xd477, CRL_REG_LEN_08BIT, 0x00}, + {0xd478, CRL_REG_LEN_08BIT, 0xb8}, + {0xd479, CRL_REG_LEN_08BIT, 0x63}, + {0xd47a, CRL_REG_LEN_08BIT, 0x00}, + {0xd47b, CRL_REG_LEN_08BIT, 0x98}, + {0xd47c, CRL_REG_LEN_08BIT, 0xe0}, + {0xd47d, CRL_REG_LEN_08BIT, 0x64}, + {0xd47e, CRL_REG_LEN_08BIT, 0x18}, + {0xd47f, CRL_REG_LEN_08BIT, 0x00}, + {0xd480, CRL_REG_LEN_08BIT, 0x0f}, + {0xd481, CRL_REG_LEN_08BIT, 0xff}, + {0xd482, CRL_REG_LEN_08BIT, 0xff}, + {0xd483, CRL_REG_LEN_08BIT, 0xf0}, + {0xd484, CRL_REG_LEN_08BIT, 0xdc}, + {0xd485, CRL_REG_LEN_08BIT, 0x05}, + {0xd486, CRL_REG_LEN_08BIT, 0x18}, + {0xd487, CRL_REG_LEN_08BIT, 0x00}, + {0xd488, CRL_REG_LEN_08BIT, 0x9c}, + {0xd489, CRL_REG_LEN_08BIT, 0x68}, + {0xd48a, CRL_REG_LEN_08BIT, 0x00}, + {0xd48b, CRL_REG_LEN_08BIT, 0x01}, + {0xd48c, CRL_REG_LEN_08BIT, 0xa5}, + {0xd48d, CRL_REG_LEN_08BIT, 0x03}, + {0xd48e, CRL_REG_LEN_08BIT, 0x00}, + {0xd48f, CRL_REG_LEN_08BIT, 0xff}, + {0xd490, CRL_REG_LEN_08BIT, 0xbc}, + {0xd491, CRL_REG_LEN_08BIT, 0x48}, + {0xd492, CRL_REG_LEN_08BIT, 0x00}, + {0xd493, CRL_REG_LEN_08BIT, 0x01}, + {0xd494, CRL_REG_LEN_08BIT, 0x0f}, + {0xd495, CRL_REG_LEN_08BIT, 0xff}, + {0xd496, CRL_REG_LEN_08BIT, 0xff}, + {0xd497, CRL_REG_LEN_08BIT, 0xea}, + {0xd498, CRL_REG_LEN_08BIT, 0xb8}, + {0xd499, CRL_REG_LEN_08BIT, 0xe8}, + {0xd49a, CRL_REG_LEN_08BIT, 0x00}, + {0xd49b, CRL_REG_LEN_08BIT, 0x02}, + {0xd49c, CRL_REG_LEN_08BIT, 0x18}, + {0xd49d, CRL_REG_LEN_08BIT, 0x60}, + {0xd49e, CRL_REG_LEN_08BIT, 0x00}, + {0xd49f, CRL_REG_LEN_08BIT, 0x01}, + {0xd4a0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4a1, CRL_REG_LEN_08BIT, 0x63}, + {0xd4a2, CRL_REG_LEN_08BIT, 0x06}, + {0xd4a3, CRL_REG_LEN_08BIT, 0x14}, + {0xd4a4, CRL_REG_LEN_08BIT, 0x07}, + {0xd4a5, CRL_REG_LEN_08BIT, 0xff}, + {0xd4a6, CRL_REG_LEN_08BIT, 0xe4}, + {0xd4a7, CRL_REG_LEN_08BIT, 0x05}, + {0xd4a8, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4a9, CRL_REG_LEN_08BIT, 0x83}, + {0xd4aa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ab, CRL_REG_LEN_08BIT, 0x10}, + {0xd4ac, CRL_REG_LEN_08BIT, 0x85}, + {0xd4ad, CRL_REG_LEN_08BIT, 0x21}, + {0xd4ae, CRL_REG_LEN_08BIT, 0x00}, + {0xd4af, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b0, CRL_REG_LEN_08BIT, 0x44}, + {0xd4b1, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b2, CRL_REG_LEN_08BIT, 0x48}, + {0xd4b3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b4, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4b5, CRL_REG_LEN_08BIT, 0x21}, + {0xd4b6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4b7, CRL_REG_LEN_08BIT, 0x04}, + {0xd4b8, CRL_REG_LEN_08BIT, 0x18}, + {0xd4b9, CRL_REG_LEN_08BIT, 0x60}, + {0xd4ba, CRL_REG_LEN_08BIT, 0x00}, + {0xd4bb, CRL_REG_LEN_08BIT, 0x01}, + {0xd4bc, CRL_REG_LEN_08BIT, 0x9c}, + {0xd4bd, CRL_REG_LEN_08BIT, 0x80}, + {0xd4be, CRL_REG_LEN_08BIT, 0xff}, + {0xd4bf, CRL_REG_LEN_08BIT, 0xff}, + {0xd4c0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4c1, CRL_REG_LEN_08BIT, 0x63}, + {0xd4c2, CRL_REG_LEN_08BIT, 0x09}, + {0xd4c3, CRL_REG_LEN_08BIT, 0xef}, + {0xd4c4, CRL_REG_LEN_08BIT, 0xd8}, + {0xd4c5, CRL_REG_LEN_08BIT, 0x03}, + {0xd4c6, CRL_REG_LEN_08BIT, 0x20}, + {0xd4c7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4c8, CRL_REG_LEN_08BIT, 0x18}, + {0xd4c9, CRL_REG_LEN_08BIT, 0x60}, + {0xd4ca, CRL_REG_LEN_08BIT, 0x80}, + {0xd4cb, CRL_REG_LEN_08BIT, 0x06}, + {0xd4cc, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4cd, CRL_REG_LEN_08BIT, 0x63}, + {0xd4ce, CRL_REG_LEN_08BIT, 0xc9}, + {0xd4cf, CRL_REG_LEN_08BIT, 0xef}, + {0xd4d0, CRL_REG_LEN_08BIT, 0xd8}, + {0xd4d1, CRL_REG_LEN_08BIT, 0x03}, + {0xd4d2, CRL_REG_LEN_08BIT, 0x20}, + {0xd4d3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d4, CRL_REG_LEN_08BIT, 0x44}, + {0xd4d5, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d6, CRL_REG_LEN_08BIT, 0x48}, + {0xd4d7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4d8, CRL_REG_LEN_08BIT, 0x15}, + {0xd4d9, CRL_REG_LEN_08BIT, 0x00}, + {0xd4da, CRL_REG_LEN_08BIT, 0x00}, + {0xd4db, CRL_REG_LEN_08BIT, 0x00}, + {0xd4dc, CRL_REG_LEN_08BIT, 0x18}, + {0xd4dd, CRL_REG_LEN_08BIT, 0x80}, + {0xd4de, CRL_REG_LEN_08BIT, 0x00}, + {0xd4df, CRL_REG_LEN_08BIT, 0x01}, + {0xd4e0, CRL_REG_LEN_08BIT, 0xa8}, + {0xd4e1, CRL_REG_LEN_08BIT, 0x84}, + {0xd4e2, CRL_REG_LEN_08BIT, 0x0a}, + {0xd4e3, CRL_REG_LEN_08BIT, 0x12}, + {0xd4e4, CRL_REG_LEN_08BIT, 0x8c}, + {0xd4e5, CRL_REG_LEN_08BIT, 0x64}, + {0xd4e6, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4e8, CRL_REG_LEN_08BIT, 0xbc}, + {0xd4e9, CRL_REG_LEN_08BIT, 0x03}, + {0xd4ea, CRL_REG_LEN_08BIT, 0x00}, + {0xd4eb, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ec, CRL_REG_LEN_08BIT, 0x13}, + {0xd4ed, CRL_REG_LEN_08BIT, 0xff}, + {0xd4ee, CRL_REG_LEN_08BIT, 0xff}, + {0xd4ef, CRL_REG_LEN_08BIT, 0xfe}, + {0xd4f0, CRL_REG_LEN_08BIT, 0x15}, + {0xd4f1, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f2, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f3, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f4, CRL_REG_LEN_08BIT, 0x44}, + {0xd4f5, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f6, CRL_REG_LEN_08BIT, 0x48}, + {0xd4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xd4f8, CRL_REG_LEN_08BIT, 0x15}, + {0xd4f9, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fb, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fc, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fd, CRL_REG_LEN_08BIT, 0x00}, + {0xd4fe, CRL_REG_LEN_08BIT, 0x00}, + {0xd4ff, CRL_REG_LEN_08BIT, 0x00}, + {0xd500, CRL_REG_LEN_08BIT, 0x00}, + {0xd501, CRL_REG_LEN_08BIT, 0x00}, + {0xd502, CRL_REG_LEN_08BIT, 0x00}, + {0xd503, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0e, CRL_REG_LEN_08BIT, 0x33}, + {0x6f0f, CRL_REG_LEN_08BIT, 0x33}, + {0x460e, CRL_REG_LEN_08BIT, 0x08}, + {0x460f, CRL_REG_LEN_08BIT, 0x01}, + {0x4610, CRL_REG_LEN_08BIT, 0x00}, + {0x4611, CRL_REG_LEN_08BIT, 0x01}, + {0x4612, CRL_REG_LEN_08BIT, 0x00}, + {0x4613, CRL_REG_LEN_08BIT, 0x01}, + {0x4605, CRL_REG_LEN_08BIT, 0x08},/*YUV 8bit*/ + {0x4608, CRL_REG_LEN_08BIT, 0x00}, + {0x4609, CRL_REG_LEN_08BIT, 0x08}, + {0x6804, CRL_REG_LEN_08BIT, 0x00}, + {0x6805, CRL_REG_LEN_08BIT, 0x06}, + {0x6806, CRL_REG_LEN_08BIT, 0x00}, + {0x5120, CRL_REG_LEN_08BIT, 0x00}, + {0x3510, CRL_REG_LEN_08BIT, 0x00}, + {0x3504, CRL_REG_LEN_08BIT, 0x00}, + {0x6800, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0d, CRL_REG_LEN_08BIT, 0x0f}, + {0x5000, CRL_REG_LEN_08BIT, 0xff}, + {0x5001, CRL_REG_LEN_08BIT, 0xbf}, + {0x5002, CRL_REG_LEN_08BIT, 0x7e}, + {0x5003, CRL_REG_LEN_08BIT, 0x0c}, + {0x503d, CRL_REG_LEN_08BIT, 0x00}, + {0xc450, CRL_REG_LEN_08BIT, 0x01}, + {0xc452, CRL_REG_LEN_08BIT, 0x04}, + {0xc453, CRL_REG_LEN_08BIT, 0x00}, + {0xc454, CRL_REG_LEN_08BIT, 0x01}, + {0xc455, CRL_REG_LEN_08BIT, 0x00}, + {0xc456, CRL_REG_LEN_08BIT, 0x00}, + {0xc457, CRL_REG_LEN_08BIT, 0x00}, + {0xc458, CRL_REG_LEN_08BIT, 0x00}, + {0xc459, CRL_REG_LEN_08BIT, 0x00}, + {0xc45b, CRL_REG_LEN_08BIT, 0x00}, + {0xc45c, CRL_REG_LEN_08BIT, 0x00}, + {0xc45d, CRL_REG_LEN_08BIT, 0x00}, + {0xc45e, CRL_REG_LEN_08BIT, 0x02}, + {0xc45f, CRL_REG_LEN_08BIT, 0x01}, + {0xc460, CRL_REG_LEN_08BIT, 0x01}, + {0xc461, CRL_REG_LEN_08BIT, 0x01}, + {0xc462, CRL_REG_LEN_08BIT, 0x01}, + {0xc464, CRL_REG_LEN_08BIT, 0x88}, + {0xc465, CRL_REG_LEN_08BIT, 0x00}, + {0xc466, CRL_REG_LEN_08BIT, 0x8a}, + {0xc467, CRL_REG_LEN_08BIT, 0x00}, + {0xc468, CRL_REG_LEN_08BIT, 0x86}, + {0xc469, CRL_REG_LEN_08BIT, 0x00}, + {0xc46a, CRL_REG_LEN_08BIT, 0x40}, + {0xc46b, CRL_REG_LEN_08BIT, 0x50}, + {0xc46c, CRL_REG_LEN_08BIT, 0x30}, + {0xc46d, CRL_REG_LEN_08BIT, 0x28}, + {0xc46e, CRL_REG_LEN_08BIT, 0x60}, + {0xc46f, CRL_REG_LEN_08BIT, 0x40}, + {0xc47c, CRL_REG_LEN_08BIT, 0x01}, + {0xc47d, CRL_REG_LEN_08BIT, 0x38}, + {0xc47e, CRL_REG_LEN_08BIT, 0x00}, + {0xc47f, CRL_REG_LEN_08BIT, 0x00}, + {0xc480, CRL_REG_LEN_08BIT, 0x00}, + {0xc481, CRL_REG_LEN_08BIT, 0xff}, + {0xc482, CRL_REG_LEN_08BIT, 0x00}, + {0xc483, CRL_REG_LEN_08BIT, 0x40}, + {0xc484, CRL_REG_LEN_08BIT, 0x00}, + {0xc485, CRL_REG_LEN_08BIT, 0x18}, + {0xc486, CRL_REG_LEN_08BIT, 0x00}, + {0xc487, CRL_REG_LEN_08BIT, 0x18}, + {0xc488, CRL_REG_LEN_08BIT, 0x20}, + {0xc489, CRL_REG_LEN_08BIT, 0x00}, + {0xc48a, CRL_REG_LEN_08BIT, 0x20}, + {0xc48b, CRL_REG_LEN_08BIT, 0x00}, + {0xc48c, CRL_REG_LEN_08BIT, 0x00}, + {0xc48d, CRL_REG_LEN_08BIT, 0x04}, + {0xc48e, CRL_REG_LEN_08BIT, 0x00}, + {0xc48f, CRL_REG_LEN_08BIT, 0x04}, + {0xc490, CRL_REG_LEN_08BIT, 0x07}, + {0xc492, CRL_REG_LEN_08BIT, 0x20}, + {0xc493, CRL_REG_LEN_08BIT, 0x08}, + {0xc498, CRL_REG_LEN_08BIT, 0x02}, + {0xc499, CRL_REG_LEN_08BIT, 0x00}, + {0xc49a, CRL_REG_LEN_08BIT, 0x02}, + {0xc49b, CRL_REG_LEN_08BIT, 0x00}, + {0xc49c, CRL_REG_LEN_08BIT, 0x02}, + {0xc49d, CRL_REG_LEN_08BIT, 0x00}, + {0xc49e, CRL_REG_LEN_08BIT, 0x02}, + {0xc49f, CRL_REG_LEN_08BIT, 0x60}, + {0xc4a0, CRL_REG_LEN_08BIT, 0x03}, + {0xc4a1, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a2, CRL_REG_LEN_08BIT, 0x04}, + {0xc4a3, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a5, CRL_REG_LEN_08BIT, 0x10}, + {0xc4a6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4a8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4a9, CRL_REG_LEN_08BIT, 0x80}, + {0xc4aa, CRL_REG_LEN_08BIT, 0x0d}, + {0xc4ab, CRL_REG_LEN_08BIT, 0x00}, + {0xc4ac, CRL_REG_LEN_08BIT, 0x03}, + {0xc4ad, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4b4, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b5, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b6, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b7, CRL_REG_LEN_08BIT, 0x01}, + {0xc4b8, CRL_REG_LEN_08BIT, 0x00}, + {0xc4b9, CRL_REG_LEN_08BIT, 0x01}, + {0xc4ba, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0xc4be, CRL_REG_LEN_08BIT, 0x02}, + {0xc4bf, CRL_REG_LEN_08BIT, 0x33}, + {0xc4c8, CRL_REG_LEN_08BIT, 0x03}, + {0xc4c9, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4ca, CRL_REG_LEN_08BIT, 0x0e}, + {0xc4cb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4cc, CRL_REG_LEN_08BIT, 0x04}, + {0xc4cd, CRL_REG_LEN_08BIT, 0xd8}, + {0xc4ce, CRL_REG_LEN_08BIT, 0x04}, + {0xc4cf, CRL_REG_LEN_08BIT, 0xd8}, + {0xc4d0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4d1, CRL_REG_LEN_08BIT, 0x80}, + {0xc4e0, CRL_REG_LEN_08BIT, 0x04}, + {0xc4e1, CRL_REG_LEN_08BIT, 0x02}, + {0xc4e2, CRL_REG_LEN_08BIT, 0x01}, + {0xc4e4, CRL_REG_LEN_08BIT, 0x10}, + {0xc4e5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4e6, CRL_REG_LEN_08BIT, 0x30}, + {0xc4e7, CRL_REG_LEN_08BIT, 0x40}, + {0xc4e8, CRL_REG_LEN_08BIT, 0x50}, + {0xc4e9, CRL_REG_LEN_08BIT, 0x60}, + {0xc4ea, CRL_REG_LEN_08BIT, 0x70}, + {0xc4eb, CRL_REG_LEN_08BIT, 0x80}, + {0xc4ec, CRL_REG_LEN_08BIT, 0x90}, + {0xc4ed, CRL_REG_LEN_08BIT, 0xa0}, + {0xc4ee, CRL_REG_LEN_08BIT, 0xb0}, + {0xc4ef, CRL_REG_LEN_08BIT, 0xc0}, + {0xc4f0, CRL_REG_LEN_08BIT, 0xd0}, + {0xc4f1, CRL_REG_LEN_08BIT, 0xe0}, + {0xc4f2, CRL_REG_LEN_08BIT, 0xf0}, + {0xc4f3, CRL_REG_LEN_08BIT, 0x80}, + {0xc4f4, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f5, CRL_REG_LEN_08BIT, 0x20}, + {0xc4f6, CRL_REG_LEN_08BIT, 0x02}, + {0xc4f7, CRL_REG_LEN_08BIT, 0x00}, + {0xc4f8, CRL_REG_LEN_08BIT, 0x04}, + {0xc4f9, CRL_REG_LEN_08BIT, 0x0b}, + {0xc4fa, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fb, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fc, CRL_REG_LEN_08BIT, 0x01}, + {0xc4fd, CRL_REG_LEN_08BIT, 0x00}, + {0xc4fe, CRL_REG_LEN_08BIT, 0x04}, + {0xc4ff, CRL_REG_LEN_08BIT, 0x02}, + {0xc500, CRL_REG_LEN_08BIT, 0x48}, + {0xc501, CRL_REG_LEN_08BIT, 0x74}, + {0xc502, CRL_REG_LEN_08BIT, 0x58}, + {0xc503, CRL_REG_LEN_08BIT, 0x80}, + {0xc504, CRL_REG_LEN_08BIT, 0x05}, + {0xc505, CRL_REG_LEN_08BIT, 0x80}, + {0xc506, CRL_REG_LEN_08BIT, 0x03}, + {0xc507, CRL_REG_LEN_08BIT, 0x80}, + {0xc508, CRL_REG_LEN_08BIT, 0x01}, + {0xc509, CRL_REG_LEN_08BIT, 0xc0}, + {0xc50a, CRL_REG_LEN_08BIT, 0x01}, + {0xc50b, CRL_REG_LEN_08BIT, 0xa0}, + {0xc50c, CRL_REG_LEN_08BIT, 0x01}, + {0xc50d, CRL_REG_LEN_08BIT, 0x2c}, + {0xc50e, CRL_REG_LEN_08BIT, 0x01}, + {0xc50f, CRL_REG_LEN_08BIT, 0x0a}, + {0xc510, CRL_REG_LEN_08BIT, 0x00}, + {0xc511, CRL_REG_LEN_08BIT, 0x00}, + {0xc512, CRL_REG_LEN_08BIT, 0x4d}, + {0xc513, CRL_REG_LEN_08BIT, 0x84}, + {0xc514, CRL_REG_LEN_08BIT, 0x04}, + {0xc515, CRL_REG_LEN_08BIT, 0x00}, + {0xc518, CRL_REG_LEN_08BIT, 0x03}, + {0xc519, CRL_REG_LEN_08BIT, 0x48}, + {0xc51a, CRL_REG_LEN_08BIT, 0x07}, + {0xc51b, CRL_REG_LEN_08BIT, 0x70}, + {0xc2e0, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e1, CRL_REG_LEN_08BIT, 0x51}, + {0xc2e2, CRL_REG_LEN_08BIT, 0x00}, + {0xc2e3, CRL_REG_LEN_08BIT, 0xd6}, + {0xc2e4, CRL_REG_LEN_08BIT, 0x01}, + {0xc2e5, CRL_REG_LEN_08BIT, 0x5e}, + {0xc2e9, CRL_REG_LEN_08BIT, 0x01}, + {0xc2ea, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2eb, CRL_REG_LEN_08BIT, 0x90}, + {0xc2ed, CRL_REG_LEN_08BIT, 0x00}, + {0xc2ee, CRL_REG_LEN_08BIT, 0x7a}, + {0xc2ef, CRL_REG_LEN_08BIT, 0x64}, + {0xc308, CRL_REG_LEN_08BIT, 0x00}, + {0xc309, CRL_REG_LEN_08BIT, 0x00}, + {0xc30a, CRL_REG_LEN_08BIT, 0x00}, + {0xc30c, CRL_REG_LEN_08BIT, 0x00}, + {0xc30d, CRL_REG_LEN_08BIT, 0x01}, + {0xc30e, CRL_REG_LEN_08BIT, 0x00}, + {0xc30f, CRL_REG_LEN_08BIT, 0x00}, + {0xc310, CRL_REG_LEN_08BIT, 0x01}, + {0xc311, CRL_REG_LEN_08BIT, 0x60}, + {0xc312, CRL_REG_LEN_08BIT, 0xff}, + {0xc313, CRL_REG_LEN_08BIT, 0x08}, + {0xc314, CRL_REG_LEN_08BIT, 0x01}, + {0xc315, CRL_REG_LEN_08BIT, 0x7f}, + {0xc316, CRL_REG_LEN_08BIT, 0xff}, + {0xc317, CRL_REG_LEN_08BIT, 0x0b}, + {0xc318, CRL_REG_LEN_08BIT, 0x00}, + {0xc319, CRL_REG_LEN_08BIT, 0x0c}, + {0xc31a, CRL_REG_LEN_08BIT, 0x00}, + {0xc31b, CRL_REG_LEN_08BIT, 0xe0}, + {0xc31c, CRL_REG_LEN_08BIT, 0x00}, + {0xc31d, CRL_REG_LEN_08BIT, 0x14}, + {0xc31e, CRL_REG_LEN_08BIT, 0x00}, + {0xc31f, CRL_REG_LEN_08BIT, 0xc5}, + {0xc320, CRL_REG_LEN_08BIT, 0xff}, + {0xc321, CRL_REG_LEN_08BIT, 0x4b}, + {0xc322, CRL_REG_LEN_08BIT, 0xff}, + {0xc323, CRL_REG_LEN_08BIT, 0xf0}, + {0xc324, CRL_REG_LEN_08BIT, 0xff}, + {0xc325, CRL_REG_LEN_08BIT, 0xe8}, + {0xc326, CRL_REG_LEN_08BIT, 0x00}, + {0xc327, CRL_REG_LEN_08BIT, 0x46}, + {0xc328, CRL_REG_LEN_08BIT, 0xff}, + {0xc329, CRL_REG_LEN_08BIT, 0xd2}, + {0xc32a, CRL_REG_LEN_08BIT, 0xff}, + {0xc32b, CRL_REG_LEN_08BIT, 0xe4}, + {0xc32c, CRL_REG_LEN_08BIT, 0xff}, + {0xc32d, CRL_REG_LEN_08BIT, 0xbb}, + {0xc32e, CRL_REG_LEN_08BIT, 0x00}, + {0xc32f, CRL_REG_LEN_08BIT, 0x61}, + {0xc330, CRL_REG_LEN_08BIT, 0xff}, + {0xc331, CRL_REG_LEN_08BIT, 0xf9}, + {0xc332, CRL_REG_LEN_08BIT, 0x00}, + {0xc333, CRL_REG_LEN_08BIT, 0xd9}, + {0xc334, CRL_REG_LEN_08BIT, 0x00}, + {0xc335, CRL_REG_LEN_08BIT, 0x2e}, + {0xc336, CRL_REG_LEN_08BIT, 0x00}, + {0xc337, CRL_REG_LEN_08BIT, 0xb1}, + {0xc338, CRL_REG_LEN_08BIT, 0xff}, + {0xc339, CRL_REG_LEN_08BIT, 0x64}, + {0xc33a, CRL_REG_LEN_08BIT, 0xff}, + {0xc33b, CRL_REG_LEN_08BIT, 0xeb}, + {0xc33c, CRL_REG_LEN_08BIT, 0xff}, + {0xc33d, CRL_REG_LEN_08BIT, 0xe8}, + {0xc33e, CRL_REG_LEN_08BIT, 0x00}, + {0xc33f, CRL_REG_LEN_08BIT, 0x48}, + {0xc340, CRL_REG_LEN_08BIT, 0xff}, + {0xc341, CRL_REG_LEN_08BIT, 0xd0}, + {0xc342, CRL_REG_LEN_08BIT, 0xff}, + {0xc343, CRL_REG_LEN_08BIT, 0xed}, + {0xc344, CRL_REG_LEN_08BIT, 0xff}, + {0xc345, CRL_REG_LEN_08BIT, 0xad}, + {0xc346, CRL_REG_LEN_08BIT, 0x00}, + {0xc347, CRL_REG_LEN_08BIT, 0x66}, + {0xc348, CRL_REG_LEN_08BIT, 0x01}, + {0xc349, CRL_REG_LEN_08BIT, 0x00}, + {0x6700, CRL_REG_LEN_08BIT, 0x04}, + {0x6701, CRL_REG_LEN_08BIT, 0x7b}, + {0x6702, CRL_REG_LEN_08BIT, 0xfd}, + {0x6703, CRL_REG_LEN_08BIT, 0xf9}, + {0x6704, CRL_REG_LEN_08BIT, 0x3d}, + {0x6705, CRL_REG_LEN_08BIT, 0x71}, + {0x6706, CRL_REG_LEN_08BIT, 0x78}, + {0x6708, CRL_REG_LEN_08BIT, 0x05}, + {0x6f06, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f07, CRL_REG_LEN_08BIT, 0x00}, + {0x6f0a, CRL_REG_LEN_08BIT, 0x6f}, + {0x6f0b, CRL_REG_LEN_08BIT, 0x00}, + {0x6f00, CRL_REG_LEN_08BIT, 0x03}, + {0xc34c, CRL_REG_LEN_08BIT, 0x01}, + {0xc34d, CRL_REG_LEN_08BIT, 0x00}, + {0xc34e, CRL_REG_LEN_08BIT, 0x46}, + {0xc34f, CRL_REG_LEN_08BIT, 0x55}, + {0xc350, CRL_REG_LEN_08BIT, 0x00}, + {0xc351, CRL_REG_LEN_08BIT, 0x40}, + {0xc352, CRL_REG_LEN_08BIT, 0x00}, + {0xc353, CRL_REG_LEN_08BIT, 0xff}, + {0xc354, CRL_REG_LEN_08BIT, 0x04}, + {0xc355, CRL_REG_LEN_08BIT, 0x08}, + {0xc356, CRL_REG_LEN_08BIT, 0x01}, + {0xc357, CRL_REG_LEN_08BIT, 0xef}, + {0xc358, CRL_REG_LEN_08BIT, 0x30}, + {0xc359, CRL_REG_LEN_08BIT, 0x01}, + {0xc35a, CRL_REG_LEN_08BIT, 0x64}, + {0xc35b, CRL_REG_LEN_08BIT, 0x46}, + {0xc35c, CRL_REG_LEN_08BIT, 0x00}, + {0x3621, CRL_REG_LEN_08BIT, 0x73}, + {0x3702, CRL_REG_LEN_08BIT, 0x20}, + {0x3703, CRL_REG_LEN_08BIT, 0x48}, + {0x3704, CRL_REG_LEN_08BIT, 0x32}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0xA4}, + {0x3804, CRL_REG_LEN_08BIT, 0x00}, + {0x3805, CRL_REG_LEN_08BIT, 0xFF}, + {0x3806, CRL_REG_LEN_08BIT, 0x02}, + {0x3807, CRL_REG_LEN_08BIT, 0x89}, + {0x3808, CRL_REG_LEN_08BIT, 0x02}, + {0x3809, CRL_REG_LEN_08BIT, 0x80}, + {0x380a, CRL_REG_LEN_08BIT, 0x01}, + {0x380b, CRL_REG_LEN_08BIT, 0xE0}, + {0x380c, CRL_REG_LEN_08BIT, 0x04}, + {0x380d, CRL_REG_LEN_08BIT, 0xAC}, + {0x6e42, CRL_REG_LEN_08BIT, 0x05}, + {0x6e43, CRL_REG_LEN_08BIT, 0x3A}, + {0x3810, CRL_REG_LEN_08BIT, 0x00}, + {0x3811, CRL_REG_LEN_08BIT, 0x08}, + {0x3812, CRL_REG_LEN_08BIT, 0x00}, + {0x3813, CRL_REG_LEN_08BIT, 0x02}, + {0x381c, CRL_REG_LEN_08BIT, 0x00}, + {0x381e, CRL_REG_LEN_08BIT, 0x00}, + {0x381f, CRL_REG_LEN_08BIT, 0x0C}, + {0x4001, CRL_REG_LEN_08BIT, 0x06}, + {0x4004, CRL_REG_LEN_08BIT, 0x04}, + {0x4050, CRL_REG_LEN_08BIT, 0x22}, + {0x4051, CRL_REG_LEN_08BIT, 0x24}, + {0x4605, CRL_REG_LEN_08BIT, 0x08}, + {0x4606, CRL_REG_LEN_08BIT, 0x09}, + {0x4607, CRL_REG_LEN_08BIT, 0x58}, + {0xc488, CRL_REG_LEN_08BIT, 0x53}, + {0xc489, CRL_REG_LEN_08BIT, 0x20}, + {0xc48a, CRL_REG_LEN_08BIT, 0x53}, + {0xc48b, CRL_REG_LEN_08BIT, 0x20}, + {0xc4cc, CRL_REG_LEN_08BIT, 0x04}, + {0xc4cd, CRL_REG_LEN_08BIT, 0xD8}, + {0xc4ce, CRL_REG_LEN_08BIT, 0x04}, + {0xc4cf, CRL_REG_LEN_08BIT, 0xD8}, + {0xc510, CRL_REG_LEN_08BIT, 0x00}, + {0xc511, CRL_REG_LEN_08BIT, 0x00}, + {0xc512, CRL_REG_LEN_08BIT, 0x4D}, + {0xc513, CRL_REG_LEN_08BIT, 0x84}, + {0x5005, CRL_REG_LEN_08BIT, 0x08}, + {0x3007, CRL_REG_LEN_08BIT, 0x01}, + {0xc518, CRL_REG_LEN_08BIT, 0x05}, + {0xc519, CRL_REG_LEN_08BIT, 0x3A}, + {0xc51a, CRL_REG_LEN_08BIT, 0x04}, + {0xc51b, CRL_REG_LEN_08BIT, 0xAC}, + {0x5608, CRL_REG_LEN_08BIT, 0x15}, + {0x3815, CRL_REG_LEN_08BIT, 0x8C}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x3042, CRL_REG_LEN_08BIT, 0xf0}, + {0x302e, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0xf0}, + {0x301c, CRL_REG_LEN_08BIT, 0xf0}, + {0x301a, CRL_REG_LEN_08BIT, 0xf0}, + {0xceb0, CRL_REG_LEN_08BIT, 0x00}, + {0xceb1, CRL_REG_LEN_08BIT, 0x00}, + {0xceb2, CRL_REG_LEN_08BIT, 0x00}, + {0xceb3, CRL_REG_LEN_08BIT, 0x00}, + {0xceb4, CRL_REG_LEN_08BIT, 0x00}, + {0xceb5, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb6, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xceb7, CRL_REG_LEN_08BIT, 0x00}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bc, CRL_REG_LEN_08BIT, 0x01}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, + {0xc4bd, CRL_REG_LEN_08BIT, 0x60}, + {0x0000, CRL_REG_LEN_DELAY, 0x0c}, +}; + +static struct crl_dynamic_register_access ov10635_h_flip_regs[] = { + { + .address = 0x381d, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x3, + } +}; + +static struct crl_dynamic_register_access ov10635_v_flip_regs[] = { + { + .address = 0x381c, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0xc0, + } +}; + +/* Needed for acpi support for runtime detection */ +static struct crl_sensor_detect_config ov10635_sensor_detect_regset[] = { + { + .reg = { 0x300A, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 8, + }, + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 8, + } +}; + +static struct crl_pll_configuration ov10635_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 10, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 20, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + } +}; + +static struct crl_subdev_rect_rep ov10635_1280_800_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 800, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 800, + }, +}; + +static struct crl_subdev_rect_rep ov10635_1280_720_rects_BT656[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep ov10635_640_480_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 800, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_register_write_rep ov10635_powerup_regs[] = { + {OV10635_REG_RESET, CRL_REG_LEN_08BIT, 0x01}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, + {0x300c, CRL_REG_LEN_08BIT, 0x61}, +}; + +static struct crl_register_write_rep ov10635_poweroff_regs[] = { + {OV10635_REG_RESET, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_power_seq_entity ov10635_power_items[] = { + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 0, + }, +}; + +static struct crl_mode_rep ov10635_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov10635_1280_800_rects), + .sd_rects = ov10635_1280_800_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 800, + .min_llp = 2250, + .min_fll = 1320, + .mode_regs_items = ARRAY_SIZE(ov10635_1280_800_YUV_HDR), + .mode_regs = ov10635_1280_800_YUV_HDR, + }, + { + .sd_rects_items = ARRAY_SIZE(ov10635_1280_720_rects_BT656), + .sd_rects = ov10635_1280_720_rects_BT656, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .min_llp = 2250, + .min_fll = 1320, + .mode_regs_items = ARRAY_SIZE(ov10635_1280_720_YUV_HDR_BT656), + .mode_regs = ov10635_1280_720_YUV_HDR_BT656, + }, + { + .sd_rects_items = ARRAY_SIZE(ov10635_640_480_rects), + .sd_rects = ov10635_640_480_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 640, + .height = 480, + .min_llp = 2250, + .min_fll = 1320, + .mode_regs_items = ARRAY_SIZE(ov10635_640_480_YUV_HDR), + .mode_regs = ov10635_640_480_YUV_HDR, + }, +}; + +static struct crl_sensor_subdev_config ov10635_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov10635 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov10635 pixel array", + } +}; + +static struct crl_sensor_limits ov10635_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1280, + .y_addr_max = 800, + .min_frame_length_lines = 240, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 320, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data ov10635_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + } +}; + +static struct crl_register_write_rep ov10635_yuyv_regs[] = { + {0x4300, CRL_REG_LEN_08BIT, 0x38}, +}; + +static struct crl_register_write_rep ov10635_uyvy_regs[] = { + {0x4300, CRL_REG_LEN_08BIT, 0x3a}, +}; + +static struct crl_csi_data_fmt ov10635_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(ov10635_yuyv_regs), + .regs = ov10635_yuyv_regs, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(ov10635_uyvy_regs), + .regs = ov10635_uyvy_regs, + }, +}; + +static struct crl_v4l2_ctrl ov10635_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10635_h_flip_regs), + .regs = ov10635_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10635_v_flip_regs), + .regs = ov10635_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_sensor_configuration ov10635_crl_configuration = { + + .powerup_regs_items = ARRAY_SIZE(ov10635_powerup_regs), + .powerup_regs = ov10635_powerup_regs, + + .poweroff_regs_items = ARRAY_SIZE(ov10635_poweroff_regs), + .poweroff_regs = ov10635_poweroff_regs, + + .power_items = ARRAY_SIZE(ov10635_power_items), + .power_entities = ov10635_power_items, + + .id_reg_items = ARRAY_SIZE(ov10635_sensor_detect_regset), + .id_regs = ov10635_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov10635_sensor_subdevs), + .subdevs = ov10635_sensor_subdevs, + + .pll_config_items = ARRAY_SIZE(ov10635_pll_configurations), + .pll_configs = ov10635_pll_configurations, + + .sensor_limits = &ov10635_sensor_limits, + + .modes_items = ARRAY_SIZE(ov10635_modes), + .modes = ov10635_modes, + + .streamon_regs_items = 0, + .streamon_regs = 0, + + .streamoff_regs_items = 0, + .streamoff_regs = 0, + + .v4l2_ctrls_items = ARRAY_SIZE(ov10635_v4l2_ctrls), + .v4l2_ctrl_bank = ov10635_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov10635_crl_csi_data_fmt), + .csi_fmts = ov10635_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov10635_flip_configurations), + .flip_data = ov10635_flip_configurations, +}; + +#endif /* __CRLMODULE_OV10635_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov10640_configuration.h b/drivers/media/i2c/crlmodule/crl_ov10640_configuration.h new file mode 100644 index 000000000000..ab8378bc0988 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov10640_configuration.h @@ -0,0 +1,3235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Shuguang Gong + * + */ + +#ifndef __CRLMODULE_OV10640_CONFIGURATION_H_ +#define __CRLMODULE_OV10640_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define OV10640_REG_STREAM 0x3012 +#define OV10640_REG_RESET 0x3013 + +#define OV10640_HMAX 65535 +#define OV10640_VMAX 65535 +#define OV10640_MAX_SHS1 (OV10640_VMAX - 6) +#define OV10640_MAX_SHS3 0x7F +#define OV10640_MAX_DGAIN 0x3FFF + +/* 800Mbps for ov10640 1280x1080 30fps */ +static struct crl_register_write_rep ov10640_pll_800mbps[] = { + {0x3000, CRL_REG_LEN_08BIT, 0x03}, + {0x3001, CRL_REG_LEN_08BIT, 0x48}, + {0x3002, CRL_REG_LEN_08BIT, 0x07}, + {0x3004, CRL_REG_LEN_08BIT, 0x03}, + {0x3005, CRL_REG_LEN_08BIT, 0x48}, + {0x3006, CRL_REG_LEN_08BIT, 0x07}, + {0x3007, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep ov10640_powerup_standby[] = { + {OV10640_REG_RESET, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_power_seq_entity ov10640_power_items[] = { + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 0, + }, +}; + +static struct crl_register_write_rep ov10640_1280_1080_LONG_RAW[] = { + {0x328a, CRL_REG_LEN_08BIT, 0x11}, + {0x313f, CRL_REG_LEN_08BIT, 0x80}, + {0x3132, CRL_REG_LEN_08BIT, 0x24}, + {0x3014, CRL_REG_LEN_08BIT, 0x03}, + {0x3023, CRL_REG_LEN_08BIT, 0x05}, + {0x3032, CRL_REG_LEN_08BIT, 0x35}, + {0x3033, CRL_REG_LEN_08BIT, 0x04}, + {0x3054, CRL_REG_LEN_08BIT, 0x00}, + {0x3055, CRL_REG_LEN_08BIT, 0x08}, + {0x3056, CRL_REG_LEN_08BIT, 0x01}, + {0x3057, CRL_REG_LEN_08BIT, 0xff}, + {0x3058, CRL_REG_LEN_08BIT, 0xaf}, + {0x3059, CRL_REG_LEN_08BIT, 0x44}, + {0x305a, CRL_REG_LEN_08BIT, 0x02}, + {0x305b, CRL_REG_LEN_08BIT, 0x00}, + {0x305c, CRL_REG_LEN_08BIT, 0x30}, + {0x305d, CRL_REG_LEN_08BIT, 0x9e}, + {0x305e, CRL_REG_LEN_08BIT, 0x19}, + {0x305f, CRL_REG_LEN_08BIT, 0x18}, + {0x3060, CRL_REG_LEN_08BIT, 0xf9}, + {0x3061, CRL_REG_LEN_08BIT, 0xf0}, + {0x308c, CRL_REG_LEN_08BIT, 0xB3}, + {0x308f, CRL_REG_LEN_08BIT, 0x10}, + {0x3091, CRL_REG_LEN_08BIT, 0x00}, + {0x3093, CRL_REG_LEN_08BIT, 0x01}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30ad, CRL_REG_LEN_08BIT, 0x03}, + {0x30ae, CRL_REG_LEN_08BIT, 0x80}, + {0x30af, CRL_REG_LEN_08BIT, 0x80}, + {0x30b0, CRL_REG_LEN_08BIT, 0xff}, + {0x30b1, CRL_REG_LEN_08BIT, 0x3f}, + {0x30b2, CRL_REG_LEN_08BIT, 0x22}, + {0x30b9, CRL_REG_LEN_08BIT, 0x22}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x00}, + {0x30be, CRL_REG_LEN_08BIT, 0x00}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x00}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x00}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x80}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c6, CRL_REG_LEN_08BIT, 0x80}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x80}, + {0x3119, CRL_REG_LEN_08BIT, 0x45}, + {0x311a, CRL_REG_LEN_08BIT, 0x01}, + {0x311b, CRL_REG_LEN_08BIT, 0x4a}, + {0x3074, CRL_REG_LEN_08BIT, 0x00}, + {0x3075, CRL_REG_LEN_08BIT, 0x00}, + {0x3076, CRL_REG_LEN_08BIT, 0x00}, + {0x3077, CRL_REG_LEN_08BIT, 0x02}, + {0x3078, CRL_REG_LEN_08BIT, 0x05}, + {0x3079, CRL_REG_LEN_08BIT, 0x07}, + {0x307a, CRL_REG_LEN_08BIT, 0x04}, + {0x307b, CRL_REG_LEN_08BIT, 0x41}, + {0x307c, CRL_REG_LEN_08BIT, 0x05}, + {0x307d, CRL_REG_LEN_08BIT, 0x00}, + {0x307e, CRL_REG_LEN_08BIT, 0x04}, + {0x307f, CRL_REG_LEN_08BIT, 0x38}, + {0x3084, CRL_REG_LEN_08BIT, 0x00}, + {0x3085, CRL_REG_LEN_08BIT, 0x04}, + {0x3086, CRL_REG_LEN_08BIT, 0x00}, + {0x3087, CRL_REG_LEN_08BIT, 0x04}, + {0x3088, CRL_REG_LEN_08BIT, 0x00}, + {0x3089, CRL_REG_LEN_08BIT, 0x40}, + {0x308d, CRL_REG_LEN_08BIT, 0x92}, + {0x3094, CRL_REG_LEN_08BIT, 0xa5}, + {0x30fa, CRL_REG_LEN_08BIT, 0x06}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x01}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3127, CRL_REG_LEN_08BIT, 0x63}, + {0x3128, CRL_REG_LEN_08BIT, 0xc0}, + {0x3129, CRL_REG_LEN_08BIT, 0x00}, + {0x31be, CRL_REG_LEN_08BIT, 0x01}, + {0x30a5, CRL_REG_LEN_08BIT, 0x78}, + {0x30a6, CRL_REG_LEN_08BIT, 0x40}, + {0x30a7, CRL_REG_LEN_08BIT, 0x78}, + {0x30a8, CRL_REG_LEN_08BIT, 0x80}, + {0x30a9, CRL_REG_LEN_08BIT, 0x78}, + {0x30aa, CRL_REG_LEN_08BIT, 0xe0}, + {0x30ab, CRL_REG_LEN_08BIT, 0xf9}, + {0x30ac, CRL_REG_LEN_08BIT, 0xc0}, + {0x3440, CRL_REG_LEN_08BIT, 0x04}, + {0x3444, CRL_REG_LEN_08BIT, 0x28}, + {0x344e, CRL_REG_LEN_08BIT, 0x2c}, + {0x3457, CRL_REG_LEN_08BIT, 0x33}, + {0x345e, CRL_REG_LEN_08BIT, 0x38}, + {0x3461, CRL_REG_LEN_08BIT, 0xa8}, + {0x7002, CRL_REG_LEN_08BIT, 0xaa}, + {0x7001, CRL_REG_LEN_08BIT, 0xdf}, + {0x7048, CRL_REG_LEN_08BIT, 0x00}, + {0x7049, CRL_REG_LEN_08BIT, 0x02}, + {0x704a, CRL_REG_LEN_08BIT, 0x02}, + {0x704b, CRL_REG_LEN_08BIT, 0x00}, + {0x704c, CRL_REG_LEN_08BIT, 0x01}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x7043, CRL_REG_LEN_08BIT, 0x04}, + {0x7040, CRL_REG_LEN_08BIT, 0x3c}, + {0x7047, CRL_REG_LEN_08BIT, 0x00}, + {0x7044, CRL_REG_LEN_08BIT, 0x01}, + {0x7000, CRL_REG_LEN_08BIT, 0x1f}, + {0x7084, CRL_REG_LEN_08BIT, 0x01}, + {0x7085, CRL_REG_LEN_08BIT, 0x03}, + {0x7086, CRL_REG_LEN_08BIT, 0x02}, + {0x7087, CRL_REG_LEN_08BIT, 0x40}, + {0x7088, CRL_REG_LEN_08BIT, 0x01}, + {0x7089, CRL_REG_LEN_08BIT, 0x20}, + {0x707f, CRL_REG_LEN_08BIT, 0x04}, + {0x707c, CRL_REG_LEN_08BIT, 0x3c}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0x01}, + {0x7003, CRL_REG_LEN_08BIT, 0xdf}, + {0x70c0, CRL_REG_LEN_08BIT, 0x00}, + {0x70c1, CRL_REG_LEN_08BIT, 0x02}, + {0x70c2, CRL_REG_LEN_08BIT, 0x02}, + {0x70c3, CRL_REG_LEN_08BIT, 0x00}, + {0x70c4, CRL_REG_LEN_08BIT, 0x01}, + {0x70c5, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0x03}, + {0x70b9, CRL_REG_LEN_08BIT, 0x98}, + {0x70bc, CRL_REG_LEN_08BIT, 0x00}, + {0x70bd, CRL_REG_LEN_08BIT, 0x80}, + {0x7004, CRL_REG_LEN_08BIT, 0x02}, + {0x7005, CRL_REG_LEN_08BIT, 0x00}, + {0x7006, CRL_REG_LEN_08BIT, 0x01}, + {0x7007, CRL_REG_LEN_08BIT, 0x80}, + {0x7008, CRL_REG_LEN_08BIT, 0x02}, + {0x7009, CRL_REG_LEN_08BIT, 0x00}, + {0x700a, CRL_REG_LEN_08BIT, 0x04}, + {0x700b, CRL_REG_LEN_08BIT, 0x00}, + {0x700e, CRL_REG_LEN_08BIT, 0x00}, + {0x700f, CRL_REG_LEN_08BIT, 0x60}, + {0x701a, CRL_REG_LEN_08BIT, 0x02}, + {0x701b, CRL_REG_LEN_08BIT, 0x00}, + {0x701c, CRL_REG_LEN_08BIT, 0x01}, + {0x701d, CRL_REG_LEN_08BIT, 0x80}, + {0x701e, CRL_REG_LEN_08BIT, 0x02}, + {0x701f, CRL_REG_LEN_08BIT, 0x00}, + {0x7020, CRL_REG_LEN_08BIT, 0x04}, + {0x7021, CRL_REG_LEN_08BIT, 0x00}, + {0x7024, CRL_REG_LEN_08BIT, 0x00}, + {0x7025, CRL_REG_LEN_08BIT, 0x60}, + {0x70e7, CRL_REG_LEN_08BIT, 0x00}, + {0x70e4, CRL_REG_LEN_08BIT, 0x10}, + {0x70e5, CRL_REG_LEN_08BIT, 0x00}, + {0x70e6, CRL_REG_LEN_08BIT, 0x00}, + {0x70eb, CRL_REG_LEN_08BIT, 0x00}, + {0x70e8, CRL_REG_LEN_08BIT, 0x10}, + {0x70e9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ea, CRL_REG_LEN_08BIT, 0x00}, + {0x70ef, CRL_REG_LEN_08BIT, 0x00}, + {0x70ec, CRL_REG_LEN_08BIT, 0xfd}, + {0x70ed, CRL_REG_LEN_08BIT, 0x00}, + {0x70ee, CRL_REG_LEN_08BIT, 0x00}, + {0x70eb, CRL_REG_LEN_08BIT, 0x00}, + {0x70f0, CRL_REG_LEN_08BIT, 0xfd}, + {0x70f1, CRL_REG_LEN_08BIT, 0x00}, + {0x70f2, CRL_REG_LEN_08BIT, 0x00}, + {0x30fb, CRL_REG_LEN_08BIT, 0x06}, + {0x30fc, CRL_REG_LEN_08BIT, 0x80}, + {0x30fd, CRL_REG_LEN_08BIT, 0x02}, + {0x30fe, CRL_REG_LEN_08BIT, 0x93}, + {0x6000, CRL_REG_LEN_08BIT, 0xc1}, + {0x6001, CRL_REG_LEN_08BIT, 0xb9}, + {0x6002, CRL_REG_LEN_08BIT, 0xba}, + {0x6003, CRL_REG_LEN_08BIT, 0xa4}, + {0x6004, CRL_REG_LEN_08BIT, 0xb5}, + {0x6005, CRL_REG_LEN_08BIT, 0xa0}, + {0x6006, CRL_REG_LEN_08BIT, 0x82}, + {0x6007, CRL_REG_LEN_08BIT, 0xa7}, + {0x6008, CRL_REG_LEN_08BIT, 0xb7}, + {0x6009, CRL_REG_LEN_08BIT, 0x5c}, + {0x600a, CRL_REG_LEN_08BIT, 0x9e}, + {0x600b, CRL_REG_LEN_08BIT, 0xc0}, + {0x600c, CRL_REG_LEN_08BIT, 0xd2}, + {0x600d, CRL_REG_LEN_08BIT, 0x33}, + {0x600e, CRL_REG_LEN_08BIT, 0xcc}, + {0x600f, CRL_REG_LEN_08BIT, 0xe2}, + {0x6010, CRL_REG_LEN_08BIT, 0xc1}, + {0x6011, CRL_REG_LEN_08BIT, 0xab}, + {0x6012, CRL_REG_LEN_08BIT, 0xb7}, + {0x6013, CRL_REG_LEN_08BIT, 0x00}, + {0x6014, CRL_REG_LEN_08BIT, 0x00}, + {0x6015, CRL_REG_LEN_08BIT, 0x00}, + {0x6016, CRL_REG_LEN_08BIT, 0x00}, + {0x6017, CRL_REG_LEN_08BIT, 0x00}, + {0x6018, CRL_REG_LEN_08BIT, 0x00}, + {0x6019, CRL_REG_LEN_08BIT, 0x00}, + {0x601a, CRL_REG_LEN_08BIT, 0x00}, + {0x601b, CRL_REG_LEN_08BIT, 0x00}, + {0x601c, CRL_REG_LEN_08BIT, 0x00}, + {0x601d, CRL_REG_LEN_08BIT, 0x00}, + {0x601e, CRL_REG_LEN_08BIT, 0x9c}, + {0x601f, CRL_REG_LEN_08BIT, 0x94}, + {0x6020, CRL_REG_LEN_08BIT, 0x90}, + {0x6021, CRL_REG_LEN_08BIT, 0xc5}, + {0x6022, CRL_REG_LEN_08BIT, 0x01}, + {0x6023, CRL_REG_LEN_08BIT, 0x54}, + {0x6024, CRL_REG_LEN_08BIT, 0x2a}, + {0x6025, CRL_REG_LEN_08BIT, 0x61}, + {0x6026, CRL_REG_LEN_08BIT, 0xd2}, + {0x6027, CRL_REG_LEN_08BIT, 0xcc}, + {0x6028, CRL_REG_LEN_08BIT, 0x04}, + {0x6029, CRL_REG_LEN_08BIT, 0x35}, + {0x602a, CRL_REG_LEN_08BIT, 0xb1}, + {0x602b, CRL_REG_LEN_08BIT, 0xb2}, + {0x602c, CRL_REG_LEN_08BIT, 0xb3}, + {0x602d, CRL_REG_LEN_08BIT, 0xd2}, + {0x602e, CRL_REG_LEN_08BIT, 0xd3}, + {0x602f, CRL_REG_LEN_08BIT, 0x12}, + {0x6030, CRL_REG_LEN_08BIT, 0x31}, + {0x6031, CRL_REG_LEN_08BIT, 0xcc}, + {0x6032, CRL_REG_LEN_08BIT, 0x06}, + {0x6033, CRL_REG_LEN_08BIT, 0xd2}, + {0x6034, CRL_REG_LEN_08BIT, 0xc4}, + {0x6035, CRL_REG_LEN_08BIT, 0xce}, + {0x6036, CRL_REG_LEN_08BIT, 0x18}, + {0x6037, CRL_REG_LEN_08BIT, 0xcf}, + {0x6038, CRL_REG_LEN_08BIT, 0x1e}, + {0x6039, CRL_REG_LEN_08BIT, 0xd0}, + {0x603a, CRL_REG_LEN_08BIT, 0x24}, + {0x603b, CRL_REG_LEN_08BIT, 0xc5}, + {0x603c, CRL_REG_LEN_08BIT, 0xd2}, + {0x603d, CRL_REG_LEN_08BIT, 0xbc}, + {0x603e, CRL_REG_LEN_08BIT, 0xcc}, + {0x603f, CRL_REG_LEN_08BIT, 0x52}, + {0x6040, CRL_REG_LEN_08BIT, 0x2b}, + {0x6041, CRL_REG_LEN_08BIT, 0xd2}, + {0x6042, CRL_REG_LEN_08BIT, 0xd3}, + {0x6043, CRL_REG_LEN_08BIT, 0x02}, + {0x6044, CRL_REG_LEN_08BIT, 0xcc}, + {0x6045, CRL_REG_LEN_08BIT, 0x0a}, + {0x6046, CRL_REG_LEN_08BIT, 0xd2}, + {0x6047, CRL_REG_LEN_08BIT, 0xd3}, + {0x6048, CRL_REG_LEN_08BIT, 0x0f}, + {0x6049, CRL_REG_LEN_08BIT, 0x1a}, + {0x604a, CRL_REG_LEN_08BIT, 0x2a}, + {0x604b, CRL_REG_LEN_08BIT, 0xd4}, + {0x604c, CRL_REG_LEN_08BIT, 0xf6}, + {0x604d, CRL_REG_LEN_08BIT, 0xba}, + {0x604e, CRL_REG_LEN_08BIT, 0x56}, + {0x604f, CRL_REG_LEN_08BIT, 0xd3}, + {0x6050, CRL_REG_LEN_08BIT, 0x2e}, + {0x6051, CRL_REG_LEN_08BIT, 0x54}, + {0x6052, CRL_REG_LEN_08BIT, 0x26}, + {0x6053, CRL_REG_LEN_08BIT, 0xd2}, + {0x6054, CRL_REG_LEN_08BIT, 0xcc}, + {0x6055, CRL_REG_LEN_08BIT, 0x60}, + {0x6056, CRL_REG_LEN_08BIT, 0xd2}, + {0x6057, CRL_REG_LEN_08BIT, 0xd3}, + {0x6058, CRL_REG_LEN_08BIT, 0x27}, + {0x6059, CRL_REG_LEN_08BIT, 0x27}, + {0x605a, CRL_REG_LEN_08BIT, 0x08}, + {0x605b, CRL_REG_LEN_08BIT, 0x1a}, + {0x605c, CRL_REG_LEN_08BIT, 0xcc}, + {0x605d, CRL_REG_LEN_08BIT, 0x88}, + {0x605e, CRL_REG_LEN_08BIT, 0x00}, + {0x605f, CRL_REG_LEN_08BIT, 0x12}, + {0x6060, CRL_REG_LEN_08BIT, 0x2c}, + {0x6061, CRL_REG_LEN_08BIT, 0x60}, + {0x6062, CRL_REG_LEN_08BIT, 0xc2}, + {0x6063, CRL_REG_LEN_08BIT, 0xb9}, + {0x6064, CRL_REG_LEN_08BIT, 0xa5}, + {0x6065, CRL_REG_LEN_08BIT, 0xb5}, + {0x6066, CRL_REG_LEN_08BIT, 0xa0}, + {0x6067, CRL_REG_LEN_08BIT, 0x82}, + {0x6068, CRL_REG_LEN_08BIT, 0x5c}, + {0x6069, CRL_REG_LEN_08BIT, 0xd4}, + {0x606a, CRL_REG_LEN_08BIT, 0xbe}, + {0x606b, CRL_REG_LEN_08BIT, 0xd4}, + {0x606c, CRL_REG_LEN_08BIT, 0xbe}, + {0x606d, CRL_REG_LEN_08BIT, 0xd3}, + {0x606e, CRL_REG_LEN_08BIT, 0x01}, + {0x606f, CRL_REG_LEN_08BIT, 0x7c}, + {0x6070, CRL_REG_LEN_08BIT, 0x74}, + {0x6071, CRL_REG_LEN_08BIT, 0x00}, + {0x6072, CRL_REG_LEN_08BIT, 0x61}, + {0x6073, CRL_REG_LEN_08BIT, 0x2a}, + {0x6074, CRL_REG_LEN_08BIT, 0xd2}, + {0x6075, CRL_REG_LEN_08BIT, 0xcc}, + {0x6076, CRL_REG_LEN_08BIT, 0xdf}, + {0x6077, CRL_REG_LEN_08BIT, 0xc6}, + {0x6078, CRL_REG_LEN_08BIT, 0x35}, + {0x6079, CRL_REG_LEN_08BIT, 0xd2}, + {0x607a, CRL_REG_LEN_08BIT, 0xcc}, + {0x607b, CRL_REG_LEN_08BIT, 0x06}, + {0x607c, CRL_REG_LEN_08BIT, 0x31}, + {0x607d, CRL_REG_LEN_08BIT, 0xd2}, + {0x607e, CRL_REG_LEN_08BIT, 0xc5}, + {0x607f, CRL_REG_LEN_08BIT, 0xbb}, + {0x6080, CRL_REG_LEN_08BIT, 0xcc}, + {0x6081, CRL_REG_LEN_08BIT, 0x18}, + {0x6082, CRL_REG_LEN_08BIT, 0xc6}, + {0x6083, CRL_REG_LEN_08BIT, 0xd2}, + {0x6084, CRL_REG_LEN_08BIT, 0xbd}, + {0x6085, CRL_REG_LEN_08BIT, 0xcc}, + {0x6086, CRL_REG_LEN_08BIT, 0x52}, + {0x6087, CRL_REG_LEN_08BIT, 0x2b}, + {0x6088, CRL_REG_LEN_08BIT, 0xd2}, + {0x6089, CRL_REG_LEN_08BIT, 0xd3}, + {0x608a, CRL_REG_LEN_08BIT, 0x01}, + {0x608b, CRL_REG_LEN_08BIT, 0xcc}, + {0x608c, CRL_REG_LEN_08BIT, 0x0a}, + {0x608d, CRL_REG_LEN_08BIT, 0xd2}, + {0x608e, CRL_REG_LEN_08BIT, 0xd3}, + {0x608f, CRL_REG_LEN_08BIT, 0x0f}, + {0x6090, CRL_REG_LEN_08BIT, 0x1a}, + {0x6091, CRL_REG_LEN_08BIT, 0x71}, + {0x6092, CRL_REG_LEN_08BIT, 0x2a}, + {0x6093, CRL_REG_LEN_08BIT, 0xd4}, + {0x6094, CRL_REG_LEN_08BIT, 0xf6}, + {0x6095, CRL_REG_LEN_08BIT, 0xd3}, + {0x6096, CRL_REG_LEN_08BIT, 0x22}, + {0x6097, CRL_REG_LEN_08BIT, 0x70}, + {0x6098, CRL_REG_LEN_08BIT, 0xca}, + {0x6099, CRL_REG_LEN_08BIT, 0x26}, + {0x609a, CRL_REG_LEN_08BIT, 0xd2}, + {0x609b, CRL_REG_LEN_08BIT, 0xcc}, + {0x609c, CRL_REG_LEN_08BIT, 0x60}, + {0x609d, CRL_REG_LEN_08BIT, 0xd2}, + {0x609e, CRL_REG_LEN_08BIT, 0xd3}, + {0x609f, CRL_REG_LEN_08BIT, 0x27}, + {0x60a0, CRL_REG_LEN_08BIT, 0x27}, + {0x60a1, CRL_REG_LEN_08BIT, 0x08}, + {0x60a2, CRL_REG_LEN_08BIT, 0x1a}, + {0x60a3, CRL_REG_LEN_08BIT, 0xcc}, + {0x60a4, CRL_REG_LEN_08BIT, 0x88}, + {0x60a5, CRL_REG_LEN_08BIT, 0x12}, + {0x60a6, CRL_REG_LEN_08BIT, 0x2c}, + {0x60a7, CRL_REG_LEN_08BIT, 0x60}, + {0x60a8, CRL_REG_LEN_08BIT, 0x00}, + {0x60a9, CRL_REG_LEN_08BIT, 0x00}, + {0x60aa, CRL_REG_LEN_08BIT, 0xc0}, + {0x60ab, CRL_REG_LEN_08BIT, 0xb9}, + {0x60ac, CRL_REG_LEN_08BIT, 0xa3}, + {0x60ad, CRL_REG_LEN_08BIT, 0xb5}, + {0x60ae, CRL_REG_LEN_08BIT, 0x00}, + {0x60af, CRL_REG_LEN_08BIT, 0xa0}, + {0x60b0, CRL_REG_LEN_08BIT, 0x82}, + {0x60b1, CRL_REG_LEN_08BIT, 0x5c}, + {0x60b2, CRL_REG_LEN_08BIT, 0xd4}, + {0x60b3, CRL_REG_LEN_08BIT, 0xa0}, + {0x60b4, CRL_REG_LEN_08BIT, 0x9d}, + {0x60b5, CRL_REG_LEN_08BIT, 0xd3}, + {0x60b6, CRL_REG_LEN_08BIT, 0x26}, + {0x60b7, CRL_REG_LEN_08BIT, 0xb0}, + {0x60b8, CRL_REG_LEN_08BIT, 0xb7}, + {0x60b9, CRL_REG_LEN_08BIT, 0x00}, + {0x60ba, CRL_REG_LEN_08BIT, 0xd3}, + {0x60bb, CRL_REG_LEN_08BIT, 0x0a}, + {0x60bc, CRL_REG_LEN_08BIT, 0xd3}, + {0x60bd, CRL_REG_LEN_08BIT, 0x10}, + {0x60be, CRL_REG_LEN_08BIT, 0x9c}, + {0x60bf, CRL_REG_LEN_08BIT, 0x94}, + {0x60c0, CRL_REG_LEN_08BIT, 0x90}, + {0x60c1, CRL_REG_LEN_08BIT, 0xc8}, + {0x60c2, CRL_REG_LEN_08BIT, 0xba}, + {0x60c3, CRL_REG_LEN_08BIT, 0x7c}, + {0x60c4, CRL_REG_LEN_08BIT, 0x74}, + {0x60c5, CRL_REG_LEN_08BIT, 0x00}, + {0x60c6, CRL_REG_LEN_08BIT, 0x61}, + {0x60c7, CRL_REG_LEN_08BIT, 0x2a}, + {0x60c8, CRL_REG_LEN_08BIT, 0x00}, + {0x60c9, CRL_REG_LEN_08BIT, 0xd2}, + {0x60ca, CRL_REG_LEN_08BIT, 0xcc}, + {0x60cb, CRL_REG_LEN_08BIT, 0xdf}, + {0x60cc, CRL_REG_LEN_08BIT, 0xc4}, + {0x60cd, CRL_REG_LEN_08BIT, 0x35}, + {0x60ce, CRL_REG_LEN_08BIT, 0xd2}, + {0x60cf, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d0, CRL_REG_LEN_08BIT, 0x06}, + {0x60d1, CRL_REG_LEN_08BIT, 0x31}, + {0x60d2, CRL_REG_LEN_08BIT, 0xd2}, + {0x60d3, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d4, CRL_REG_LEN_08BIT, 0x15}, + {0x60d5, CRL_REG_LEN_08BIT, 0xd2}, + {0x60d6, CRL_REG_LEN_08BIT, 0xbb}, + {0x60d7, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d8, CRL_REG_LEN_08BIT, 0x1a}, + {0x60d9, CRL_REG_LEN_08BIT, 0xd2}, + {0x60da, CRL_REG_LEN_08BIT, 0xbe}, + {0x60db, CRL_REG_LEN_08BIT, 0xce}, + {0x60dc, CRL_REG_LEN_08BIT, 0x52}, + {0x60dd, CRL_REG_LEN_08BIT, 0xcf}, + {0x60de, CRL_REG_LEN_08BIT, 0x56}, + {0x60df, CRL_REG_LEN_08BIT, 0xd0}, + {0x60e0, CRL_REG_LEN_08BIT, 0x5b}, + {0x60e1, CRL_REG_LEN_08BIT, 0x2b}, + {0x60e2, CRL_REG_LEN_08BIT, 0xd2}, + {0x60e3, CRL_REG_LEN_08BIT, 0xd3}, + {0x60e4, CRL_REG_LEN_08BIT, 0x01}, + {0x60e5, CRL_REG_LEN_08BIT, 0xcc}, + {0x60e6, CRL_REG_LEN_08BIT, 0x0a}, + {0x60e7, CRL_REG_LEN_08BIT, 0xd2}, + {0x60e8, CRL_REG_LEN_08BIT, 0xd3}, + {0x60e9, CRL_REG_LEN_08BIT, 0x0f}, + {0x60ea, CRL_REG_LEN_08BIT, 0xd9}, + {0x60eb, CRL_REG_LEN_08BIT, 0xc7}, + {0x60ec, CRL_REG_LEN_08BIT, 0xda}, + {0x60ed, CRL_REG_LEN_08BIT, 0xce}, + {0x60ee, CRL_REG_LEN_08BIT, 0x1a}, + {0x60ef, CRL_REG_LEN_08BIT, 0xd4}, + {0x60f0, CRL_REG_LEN_08BIT, 0xf6}, + {0x60f1, CRL_REG_LEN_08BIT, 0xd4}, + {0x60f2, CRL_REG_LEN_08BIT, 0xa9}, + {0x60f3, CRL_REG_LEN_08BIT, 0x27}, + {0x60f4, CRL_REG_LEN_08BIT, 0x00}, + {0x60f5, CRL_REG_LEN_08BIT, 0xd2}, + {0x60f6, CRL_REG_LEN_08BIT, 0xcc}, + {0x60f7, CRL_REG_LEN_08BIT, 0x60}, + {0x60f8, CRL_REG_LEN_08BIT, 0xd2}, + {0x60f9, CRL_REG_LEN_08BIT, 0xd3}, + {0x60fa, CRL_REG_LEN_08BIT, 0x2d}, + {0x60fb, CRL_REG_LEN_08BIT, 0xd9}, + {0x60fc, CRL_REG_LEN_08BIT, 0xdf}, + {0x60fd, CRL_REG_LEN_08BIT, 0xda}, + {0x60fe, CRL_REG_LEN_08BIT, 0xe5}, + {0x60ff, CRL_REG_LEN_08BIT, 0x1a}, + {0x6100, CRL_REG_LEN_08BIT, 0x12}, + {0x6101, CRL_REG_LEN_08BIT, 0xcc}, + {0x6102, CRL_REG_LEN_08BIT, 0x88}, + {0x6103, CRL_REG_LEN_08BIT, 0xd6}, + {0x6104, CRL_REG_LEN_08BIT, 0xb1}, + {0x6105, CRL_REG_LEN_08BIT, 0xb9}, + {0x6106, CRL_REG_LEN_08BIT, 0xba}, + {0x6107, CRL_REG_LEN_08BIT, 0xaf}, + {0x6108, CRL_REG_LEN_08BIT, 0xdc}, + {0x6109, CRL_REG_LEN_08BIT, 0x00}, + {0x610a, CRL_REG_LEN_08BIT, 0xcb}, + {0x610b, CRL_REG_LEN_08BIT, 0xc3}, + {0x610c, CRL_REG_LEN_08BIT, 0xb9}, + {0x610d, CRL_REG_LEN_08BIT, 0xa4}, + {0x610e, CRL_REG_LEN_08BIT, 0xb5}, + {0x610f, CRL_REG_LEN_08BIT, 0x5c}, + {0x6110, CRL_REG_LEN_08BIT, 0x12}, + {0x6111, CRL_REG_LEN_08BIT, 0x2a}, + {0x6112, CRL_REG_LEN_08BIT, 0x61}, + {0x6113, CRL_REG_LEN_08BIT, 0xd2}, + {0x6114, CRL_REG_LEN_08BIT, 0xcc}, + {0x6115, CRL_REG_LEN_08BIT, 0xdf}, + {0x6116, CRL_REG_LEN_08BIT, 0xc7}, + {0x6117, CRL_REG_LEN_08BIT, 0x35}, + {0x6118, CRL_REG_LEN_08BIT, 0xd2}, + {0x6119, CRL_REG_LEN_08BIT, 0xcc}, + {0x611a, CRL_REG_LEN_08BIT, 0x06}, + {0x611b, CRL_REG_LEN_08BIT, 0x31}, + {0x611c, CRL_REG_LEN_08BIT, 0xc6}, + {0x611d, CRL_REG_LEN_08BIT, 0xbb}, + {0x611e, CRL_REG_LEN_08BIT, 0xd2}, + {0x611f, CRL_REG_LEN_08BIT, 0xcc}, + {0x6120, CRL_REG_LEN_08BIT, 0x18}, + {0x6121, CRL_REG_LEN_08BIT, 0xd2}, + {0x6122, CRL_REG_LEN_08BIT, 0xbe}, + {0x6123, CRL_REG_LEN_08BIT, 0xcc}, + {0x6124, CRL_REG_LEN_08BIT, 0x52}, + {0x6125, CRL_REG_LEN_08BIT, 0xc7}, + {0x6126, CRL_REG_LEN_08BIT, 0xd2}, + {0x6127, CRL_REG_LEN_08BIT, 0xcc}, + {0x6128, CRL_REG_LEN_08BIT, 0x0a}, + {0x6129, CRL_REG_LEN_08BIT, 0xb4}, + {0x612a, CRL_REG_LEN_08BIT, 0xb7}, + {0x612b, CRL_REG_LEN_08BIT, 0x94}, + {0x612c, CRL_REG_LEN_08BIT, 0xd2}, + {0x612d, CRL_REG_LEN_08BIT, 0x12}, + {0x612e, CRL_REG_LEN_08BIT, 0x26}, + {0x612f, CRL_REG_LEN_08BIT, 0x42}, + {0x6130, CRL_REG_LEN_08BIT, 0x46}, + {0x6131, CRL_REG_LEN_08BIT, 0x42}, + {0x6132, CRL_REG_LEN_08BIT, 0xd3}, + {0x6133, CRL_REG_LEN_08BIT, 0x20}, + {0x6134, CRL_REG_LEN_08BIT, 0x27}, + {0x6135, CRL_REG_LEN_08BIT, 0x00}, + {0x6136, CRL_REG_LEN_08BIT, 0x1a}, + {0x6137, CRL_REG_LEN_08BIT, 0xcc}, + {0x6138, CRL_REG_LEN_08BIT, 0x88}, + {0x6139, CRL_REG_LEN_08BIT, 0x60}, + {0x613a, CRL_REG_LEN_08BIT, 0x2c}, + {0x613b, CRL_REG_LEN_08BIT, 0x12}, + {0x613c, CRL_REG_LEN_08BIT, 0x40}, + {0x613d, CRL_REG_LEN_08BIT, 0xb8}, + {0x613e, CRL_REG_LEN_08BIT, 0x90}, + {0x613f, CRL_REG_LEN_08BIT, 0xd5}, + {0x6140, CRL_REG_LEN_08BIT, 0xba}, + {0x6141, CRL_REG_LEN_08BIT, 0x00}, + {0x6142, CRL_REG_LEN_08BIT, 0x00}, + {0x6143, CRL_REG_LEN_08BIT, 0x00}, + {0x6144, CRL_REG_LEN_08BIT, 0x00}, + {0x6145, CRL_REG_LEN_08BIT, 0x00}, + {0x6146, CRL_REG_LEN_08BIT, 0x00}, + {0x6147, CRL_REG_LEN_08BIT, 0xaa}, + {0x6148, CRL_REG_LEN_08BIT, 0xb7}, + {0x6149, CRL_REG_LEN_08BIT, 0x00}, + {0x614a, CRL_REG_LEN_08BIT, 0x00}, + {0x614b, CRL_REG_LEN_08BIT, 0x00}, + {0x614c, CRL_REG_LEN_08BIT, 0x00}, + {0x614d, CRL_REG_LEN_08BIT, 0xa6}, + {0x614e, CRL_REG_LEN_08BIT, 0xb7}, + {0x614f, CRL_REG_LEN_08BIT, 0x00}, + {0x6150, CRL_REG_LEN_08BIT, 0xd5}, + {0x6151, CRL_REG_LEN_08BIT, 0x00}, + {0x6152, CRL_REG_LEN_08BIT, 0x71}, + {0x6153, CRL_REG_LEN_08BIT, 0xd3}, + {0x6154, CRL_REG_LEN_08BIT, 0x30}, + {0x6155, CRL_REG_LEN_08BIT, 0xba}, + {0x6156, CRL_REG_LEN_08BIT, 0x00}, + {0x6157, CRL_REG_LEN_08BIT, 0x00}, + {0x6158, CRL_REG_LEN_08BIT, 0x00}, + {0x6159, CRL_REG_LEN_08BIT, 0x00}, + {0x615a, CRL_REG_LEN_08BIT, 0xd3}, + {0x615b, CRL_REG_LEN_08BIT, 0x10}, + {0x615c, CRL_REG_LEN_08BIT, 0x70}, + {0x615d, CRL_REG_LEN_08BIT, 0x00}, + {0x615e, CRL_REG_LEN_08BIT, 0x00}, + {0x615f, CRL_REG_LEN_08BIT, 0x00}, + {0x6160, CRL_REG_LEN_08BIT, 0x00}, + {0x6161, CRL_REG_LEN_08BIT, 0xd5}, + {0x6162, CRL_REG_LEN_08BIT, 0xba}, + {0x6163, CRL_REG_LEN_08BIT, 0xb0}, + {0x6164, CRL_REG_LEN_08BIT, 0xb7}, + {0x6165, CRL_REG_LEN_08BIT, 0x00}, + {0x6166, CRL_REG_LEN_08BIT, 0x9d}, + {0x6167, CRL_REG_LEN_08BIT, 0xd3}, + {0x6168, CRL_REG_LEN_08BIT, 0x0a}, + {0x6169, CRL_REG_LEN_08BIT, 0x9d}, + {0x616a, CRL_REG_LEN_08BIT, 0x9d}, + {0x616b, CRL_REG_LEN_08BIT, 0xd3}, + {0x616c, CRL_REG_LEN_08BIT, 0x10}, + {0x616d, CRL_REG_LEN_08BIT, 0x9c}, + {0x616e, CRL_REG_LEN_08BIT, 0x94}, + {0x616f, CRL_REG_LEN_08BIT, 0x90}, + {0x6170, CRL_REG_LEN_08BIT, 0xc8}, + {0x6171, CRL_REG_LEN_08BIT, 0xba}, + {0x6172, CRL_REG_LEN_08BIT, 0xd2}, + {0x6173, CRL_REG_LEN_08BIT, 0x60}, + {0x6174, CRL_REG_LEN_08BIT, 0x2c}, + {0x6175, CRL_REG_LEN_08BIT, 0x50}, + {0x6176, CRL_REG_LEN_08BIT, 0x11}, + {0x6177, CRL_REG_LEN_08BIT, 0xcc}, + {0x6178, CRL_REG_LEN_08BIT, 0x00}, + {0x6179, CRL_REG_LEN_08BIT, 0x30}, + {0x617a, CRL_REG_LEN_08BIT, 0xd5}, + {0x617b, CRL_REG_LEN_08BIT, 0x00}, + {0x617c, CRL_REG_LEN_08BIT, 0xba}, + {0x617d, CRL_REG_LEN_08BIT, 0xb0}, + {0x617e, CRL_REG_LEN_08BIT, 0xb7}, + {0x617f, CRL_REG_LEN_08BIT, 0x00}, + {0x6180, CRL_REG_LEN_08BIT, 0x9d}, + {0x6181, CRL_REG_LEN_08BIT, 0xd3}, + {0x6182, CRL_REG_LEN_08BIT, 0x0a}, + {0x6183, CRL_REG_LEN_08BIT, 0x9d}, + {0x6184, CRL_REG_LEN_08BIT, 0x9d}, + {0x6185, CRL_REG_LEN_08BIT, 0xd3}, + {0x6186, CRL_REG_LEN_08BIT, 0x10}, + {0x6187, CRL_REG_LEN_08BIT, 0x9c}, + {0x6188, CRL_REG_LEN_08BIT, 0x94}, + {0x6189, CRL_REG_LEN_08BIT, 0x90}, + {0x618a, CRL_REG_LEN_08BIT, 0xc8}, + {0x618b, CRL_REG_LEN_08BIT, 0xba}, + {0x618c, CRL_REG_LEN_08BIT, 0xd5}, + {0x618d, CRL_REG_LEN_08BIT, 0x00}, + {0x618e, CRL_REG_LEN_08BIT, 0x01}, + {0x618f, CRL_REG_LEN_08BIT, 0x1a}, + {0x6190, CRL_REG_LEN_08BIT, 0xcc}, + {0x6191, CRL_REG_LEN_08BIT, 0x12}, + {0x6192, CRL_REG_LEN_08BIT, 0x12}, + {0x6193, CRL_REG_LEN_08BIT, 0x00}, + {0x6194, CRL_REG_LEN_08BIT, 0xcc}, + {0x6195, CRL_REG_LEN_08BIT, 0x9c}, + {0x6196, CRL_REG_LEN_08BIT, 0xd2}, + {0x6197, CRL_REG_LEN_08BIT, 0xcc}, + {0x6198, CRL_REG_LEN_08BIT, 0x60}, + {0x6199, CRL_REG_LEN_08BIT, 0xd2}, + {0x619a, CRL_REG_LEN_08BIT, 0x04}, + {0x619b, CRL_REG_LEN_08BIT, 0xd5}, + {0x619c, CRL_REG_LEN_08BIT, 0x1a}, + {0x619d, CRL_REG_LEN_08BIT, 0xcc}, + {0x619e, CRL_REG_LEN_08BIT, 0x12}, + {0x619f, CRL_REG_LEN_08BIT, 0x00}, + {0x61a0, CRL_REG_LEN_08BIT, 0x12}, + {0x61a1, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a2, CRL_REG_LEN_08BIT, 0x9c}, + {0x61a3, CRL_REG_LEN_08BIT, 0xd2}, + {0x61a4, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a5, CRL_REG_LEN_08BIT, 0x60}, + {0x61a6, CRL_REG_LEN_08BIT, 0xd2}, + {0x61a7, CRL_REG_LEN_08BIT, 0x1a}, + {0x61a8, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a9, CRL_REG_LEN_08BIT, 0x12}, + {0x61aa, CRL_REG_LEN_08BIT, 0x00}, + {0x61ab, CRL_REG_LEN_08BIT, 0x12}, + {0x61ac, CRL_REG_LEN_08BIT, 0xcc}, + {0x61ad, CRL_REG_LEN_08BIT, 0x9c}, + {0x61ae, CRL_REG_LEN_08BIT, 0xd2}, + {0x61af, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b0, CRL_REG_LEN_08BIT, 0x60}, + {0x61b1, CRL_REG_LEN_08BIT, 0xd2}, + {0x61b2, CRL_REG_LEN_08BIT, 0x1a}, + {0x61b3, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b4, CRL_REG_LEN_08BIT, 0x12}, + {0x61b5, CRL_REG_LEN_08BIT, 0x00}, + {0x61b6, CRL_REG_LEN_08BIT, 0x12}, + {0x61b7, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b8, CRL_REG_LEN_08BIT, 0x9c}, + {0x61b9, CRL_REG_LEN_08BIT, 0xd2}, + {0x61ba, CRL_REG_LEN_08BIT, 0xcc}, + {0x61bb, CRL_REG_LEN_08BIT, 0x60}, + {0x61bc, CRL_REG_LEN_08BIT, 0xd2}, + {0x61bd, CRL_REG_LEN_08BIT, 0xd5}, + {0x61be, CRL_REG_LEN_08BIT, 0x1a}, + {0x61bf, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c0, CRL_REG_LEN_08BIT, 0x12}, + {0x61c1, CRL_REG_LEN_08BIT, 0x12}, + {0x61c2, CRL_REG_LEN_08BIT, 0x00}, + {0x61c3, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c4, CRL_REG_LEN_08BIT, 0x8a}, + {0x61c5, CRL_REG_LEN_08BIT, 0xd2}, + {0x61c6, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c7, CRL_REG_LEN_08BIT, 0x74}, + {0x61c8, CRL_REG_LEN_08BIT, 0xd2}, + {0x61c9, CRL_REG_LEN_08BIT, 0xd5}, + {0x61ca, CRL_REG_LEN_08BIT, 0x1a}, + {0x61cb, CRL_REG_LEN_08BIT, 0xcc}, + {0x61cc, CRL_REG_LEN_08BIT, 0x12}, + {0x61cd, CRL_REG_LEN_08BIT, 0x00}, + {0x61ce, CRL_REG_LEN_08BIT, 0x12}, + {0x61cf, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d0, CRL_REG_LEN_08BIT, 0x8a}, + {0x61d1, CRL_REG_LEN_08BIT, 0xd2}, + {0x61d2, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d3, CRL_REG_LEN_08BIT, 0x74}, + {0x61d4, CRL_REG_LEN_08BIT, 0xd2}, + {0x61d5, CRL_REG_LEN_08BIT, 0x1a}, + {0x61d6, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d7, CRL_REG_LEN_08BIT, 0x12}, + {0x61d8, CRL_REG_LEN_08BIT, 0x00}, + {0x61d9, CRL_REG_LEN_08BIT, 0x12}, + {0x61da, CRL_REG_LEN_08BIT, 0xcc}, + {0x61db, CRL_REG_LEN_08BIT, 0x8a}, + {0x61dc, CRL_REG_LEN_08BIT, 0xd2}, + {0x61dd, CRL_REG_LEN_08BIT, 0xcc}, + {0x61de, CRL_REG_LEN_08BIT, 0x74}, + {0x61df, CRL_REG_LEN_08BIT, 0xd2}, + {0x61e0, CRL_REG_LEN_08BIT, 0x1a}, + {0x61e1, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e2, CRL_REG_LEN_08BIT, 0x12}, + {0x61e3, CRL_REG_LEN_08BIT, 0x00}, + {0x61e4, CRL_REG_LEN_08BIT, 0x12}, + {0x61e5, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e6, CRL_REG_LEN_08BIT, 0x8a}, + {0x61e7, CRL_REG_LEN_08BIT, 0xd2}, + {0x61e8, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e9, CRL_REG_LEN_08BIT, 0x74}, + {0x61ea, CRL_REG_LEN_08BIT, 0xd2}, + {0x61eb, CRL_REG_LEN_08BIT, 0xd5}, + {0x61ec, CRL_REG_LEN_08BIT, 0xcc}, + {0x61ed, CRL_REG_LEN_08BIT, 0x12}, + {0x61ee, CRL_REG_LEN_08BIT, 0x00}, + {0x61ef, CRL_REG_LEN_08BIT, 0x12}, + {0x61f0, CRL_REG_LEN_08BIT, 0xcc}, + {0x61f1, CRL_REG_LEN_08BIT, 0x9c}, + {0x61f2, CRL_REG_LEN_08BIT, 0xd5}, + {0x6400, CRL_REG_LEN_08BIT, 0x04}, + {0x6401, CRL_REG_LEN_08BIT, 0x04}, + {0x6402, CRL_REG_LEN_08BIT, 0x00}, + {0x6403, CRL_REG_LEN_08BIT, 0xff}, + {0x6404, CRL_REG_LEN_08BIT, 0x00}, + {0x6405, CRL_REG_LEN_08BIT, 0x08}, + {0x6406, CRL_REG_LEN_08BIT, 0x00}, + {0x6407, CRL_REG_LEN_08BIT, 0xff}, + {0x6408, CRL_REG_LEN_08BIT, 0x04}, + {0x6409, CRL_REG_LEN_08BIT, 0x70}, + {0x640a, CRL_REG_LEN_08BIT, 0x00}, + {0x640b, CRL_REG_LEN_08BIT, 0xff}, + {0x640c, CRL_REG_LEN_08BIT, 0x05}, + {0x640d, CRL_REG_LEN_08BIT, 0x14}, + {0x640e, CRL_REG_LEN_08BIT, 0x04}, + {0x640f, CRL_REG_LEN_08BIT, 0x71}, + {0x6410, CRL_REG_LEN_08BIT, 0x05}, + {0x6411, CRL_REG_LEN_08BIT, 0x74}, + {0x6412, CRL_REG_LEN_08BIT, 0x00}, + {0x6413, CRL_REG_LEN_08BIT, 0xff}, + {0x6414, CRL_REG_LEN_08BIT, 0x05}, + {0x6415, CRL_REG_LEN_08BIT, 0x54}, + {0x6416, CRL_REG_LEN_08BIT, 0x05}, + {0x6417, CRL_REG_LEN_08BIT, 0x44}, + {0x6418, CRL_REG_LEN_08BIT, 0x04}, + {0x6419, CRL_REG_LEN_08BIT, 0x30}, + {0x641a, CRL_REG_LEN_08BIT, 0x05}, + {0x641b, CRL_REG_LEN_08BIT, 0x46}, + {0x641c, CRL_REG_LEN_08BIT, 0x00}, + {0x641d, CRL_REG_LEN_08BIT, 0xff}, + {0x641e, CRL_REG_LEN_08BIT, 0x04}, + {0x641f, CRL_REG_LEN_08BIT, 0x31}, + {0x6420, CRL_REG_LEN_08BIT, 0x04}, + {0x6421, CRL_REG_LEN_08BIT, 0x30}, + {0x6422, CRL_REG_LEN_08BIT, 0x00}, + {0x6423, CRL_REG_LEN_08BIT, 0xff}, + {0x6424, CRL_REG_LEN_08BIT, 0x04}, + {0x6425, CRL_REG_LEN_08BIT, 0x20}, + {0x6426, CRL_REG_LEN_08BIT, 0x05}, + {0x6427, CRL_REG_LEN_08BIT, 0x06}, + {0x6428, CRL_REG_LEN_08BIT, 0x00}, + {0x6429, CRL_REG_LEN_08BIT, 0xff}, + {0x642a, CRL_REG_LEN_08BIT, 0x08}, + {0x642b, CRL_REG_LEN_08BIT, 0x2a}, + {0x642c, CRL_REG_LEN_08BIT, 0x08}, + {0x642d, CRL_REG_LEN_08BIT, 0x31}, + {0x642e, CRL_REG_LEN_08BIT, 0x00}, + {0x642f, CRL_REG_LEN_08BIT, 0xff}, + {0x6430, CRL_REG_LEN_08BIT, 0x08}, + {0x6431, CRL_REG_LEN_08BIT, 0x2a}, + {0x6432, CRL_REG_LEN_08BIT, 0x08}, + {0x6433, CRL_REG_LEN_08BIT, 0x31}, + {0x6434, CRL_REG_LEN_08BIT, 0x06}, + {0x6435, CRL_REG_LEN_08BIT, 0x20}, + {0x6436, CRL_REG_LEN_08BIT, 0x07}, + {0x6437, CRL_REG_LEN_08BIT, 0x00}, + {0x6438, CRL_REG_LEN_08BIT, 0x08}, + {0x6439, CRL_REG_LEN_08BIT, 0x40}, + {0x643a, CRL_REG_LEN_08BIT, 0x00}, + {0x643b, CRL_REG_LEN_08BIT, 0xff}, + {0x643c, CRL_REG_LEN_08BIT, 0x08}, + {0x643d, CRL_REG_LEN_08BIT, 0x2a}, + {0x643e, CRL_REG_LEN_08BIT, 0x08}, + {0x643f, CRL_REG_LEN_08BIT, 0x36}, + {0x6440, CRL_REG_LEN_08BIT, 0x06}, + {0x6441, CRL_REG_LEN_08BIT, 0x10}, + {0x6442, CRL_REG_LEN_08BIT, 0x07}, + {0x6443, CRL_REG_LEN_08BIT, 0x00}, + {0x6444, CRL_REG_LEN_08BIT, 0x08}, + {0x6445, CRL_REG_LEN_08BIT, 0x40}, + {0x6446, CRL_REG_LEN_08BIT, 0x00}, + {0x6447, CRL_REG_LEN_08BIT, 0xff}, + {0x6448, CRL_REG_LEN_08BIT, 0x08}, + {0x6449, CRL_REG_LEN_08BIT, 0x2a}, + {0x644a, CRL_REG_LEN_08BIT, 0x08}, + {0x644b, CRL_REG_LEN_08BIT, 0x3b}, + {0x644c, CRL_REG_LEN_08BIT, 0x06}, + {0x644d, CRL_REG_LEN_08BIT, 0x00}, + {0x644e, CRL_REG_LEN_08BIT, 0x07}, + {0x644f, CRL_REG_LEN_08BIT, 0x00}, + {0x6450, CRL_REG_LEN_08BIT, 0x08}, + {0x6451, CRL_REG_LEN_08BIT, 0x40}, + {0x6452, CRL_REG_LEN_08BIT, 0x00}, + {0x6453, CRL_REG_LEN_08BIT, 0xff}, + {0x6454, CRL_REG_LEN_08BIT, 0x06}, + {0x6455, CRL_REG_LEN_08BIT, 0x00}, + {0x6456, CRL_REG_LEN_08BIT, 0x07}, + {0x6457, CRL_REG_LEN_08BIT, 0x05}, + {0x6458, CRL_REG_LEN_08BIT, 0x01}, + {0x6459, CRL_REG_LEN_08BIT, 0xaf}, + {0x645a, CRL_REG_LEN_08BIT, 0x01}, + {0x645b, CRL_REG_LEN_08BIT, 0x0f}, + {0x645c, CRL_REG_LEN_08BIT, 0x01}, + {0x645d, CRL_REG_LEN_08BIT, 0x90}, + {0x645e, CRL_REG_LEN_08BIT, 0x01}, + {0x645f, CRL_REG_LEN_08BIT, 0xc8}, + {0x6460, CRL_REG_LEN_08BIT, 0x00}, + {0x6461, CRL_REG_LEN_08BIT, 0xff}, + {0x6462, CRL_REG_LEN_08BIT, 0x01}, + {0x6463, CRL_REG_LEN_08BIT, 0xac}, + {0x6464, CRL_REG_LEN_08BIT, 0x01}, + {0x6465, CRL_REG_LEN_08BIT, 0x0c}, + {0x6466, CRL_REG_LEN_08BIT, 0x01}, + {0x6467, CRL_REG_LEN_08BIT, 0x90}, + {0x6468, CRL_REG_LEN_08BIT, 0x01}, + {0x6469, CRL_REG_LEN_08BIT, 0xe8}, + {0x646a, CRL_REG_LEN_08BIT, 0x00}, + {0x646b, CRL_REG_LEN_08BIT, 0xff}, + {0x646c, CRL_REG_LEN_08BIT, 0x01}, + {0x646d, CRL_REG_LEN_08BIT, 0xad}, + {0x646e, CRL_REG_LEN_08BIT, 0x01}, + {0x646f, CRL_REG_LEN_08BIT, 0x0d}, + {0x6470, CRL_REG_LEN_08BIT, 0x01}, + {0x6471, CRL_REG_LEN_08BIT, 0x90}, + {0x6472, CRL_REG_LEN_08BIT, 0x01}, + {0x6473, CRL_REG_LEN_08BIT, 0xe8}, + {0x6474, CRL_REG_LEN_08BIT, 0x00}, + {0x6475, CRL_REG_LEN_08BIT, 0xff}, + {0x6476, CRL_REG_LEN_08BIT, 0x01}, + {0x6477, CRL_REG_LEN_08BIT, 0xae}, + {0x6478, CRL_REG_LEN_08BIT, 0x01}, + {0x6479, CRL_REG_LEN_08BIT, 0x0e}, + {0x647a, CRL_REG_LEN_08BIT, 0x01}, + {0x647b, CRL_REG_LEN_08BIT, 0x90}, + {0x647c, CRL_REG_LEN_08BIT, 0x01}, + {0x647d, CRL_REG_LEN_08BIT, 0xe8}, + {0x647e, CRL_REG_LEN_08BIT, 0x00}, + {0x647f, CRL_REG_LEN_08BIT, 0xff}, + {0x6480, CRL_REG_LEN_08BIT, 0x01}, + {0x6481, CRL_REG_LEN_08BIT, 0xb0}, + {0x6482, CRL_REG_LEN_08BIT, 0x01}, + {0x6483, CRL_REG_LEN_08BIT, 0xb1}, + {0x6484, CRL_REG_LEN_08BIT, 0x01}, + {0x6485, CRL_REG_LEN_08BIT, 0xb2}, + {0x6486, CRL_REG_LEN_08BIT, 0x01}, + {0x6487, CRL_REG_LEN_08BIT, 0xb3}, + {0x6488, CRL_REG_LEN_08BIT, 0x01}, + {0x6489, CRL_REG_LEN_08BIT, 0xb4}, + {0x648a, CRL_REG_LEN_08BIT, 0x01}, + {0x648b, CRL_REG_LEN_08BIT, 0xb5}, + {0x648c, CRL_REG_LEN_08BIT, 0x01}, + {0x648d, CRL_REG_LEN_08BIT, 0xb6}, + {0x648e, CRL_REG_LEN_08BIT, 0x01}, + {0x648f, CRL_REG_LEN_08BIT, 0xb7}, + {0x6490, CRL_REG_LEN_08BIT, 0x01}, + {0x6491, CRL_REG_LEN_08BIT, 0xb8}, + {0x6492, CRL_REG_LEN_08BIT, 0x01}, + {0x6493, CRL_REG_LEN_08BIT, 0xb9}, + {0x6494, CRL_REG_LEN_08BIT, 0x01}, + {0x6495, CRL_REG_LEN_08BIT, 0xba}, + {0x6496, CRL_REG_LEN_08BIT, 0x01}, + {0x6497, CRL_REG_LEN_08BIT, 0xbb}, + {0x6498, CRL_REG_LEN_08BIT, 0x01}, + {0x6499, CRL_REG_LEN_08BIT, 0xbc}, + {0x649a, CRL_REG_LEN_08BIT, 0x01}, + {0x649b, CRL_REG_LEN_08BIT, 0xbd}, + {0x649c, CRL_REG_LEN_08BIT, 0x01}, + {0x649d, CRL_REG_LEN_08BIT, 0xbe}, + {0x649e, CRL_REG_LEN_08BIT, 0x01}, + {0x649f, CRL_REG_LEN_08BIT, 0xbf}, + {0x64a0, CRL_REG_LEN_08BIT, 0x01}, + {0x64a1, CRL_REG_LEN_08BIT, 0xc0}, + {0x64a2, CRL_REG_LEN_08BIT, 0x00}, + {0x64a3, CRL_REG_LEN_08BIT, 0xff}, + {0x64a4, CRL_REG_LEN_08BIT, 0x06}, + {0x64a5, CRL_REG_LEN_08BIT, 0x00}, + {0x64a6, CRL_REG_LEN_08BIT, 0x01}, + {0x64a7, CRL_REG_LEN_08BIT, 0xf6}, + {0x64a8, CRL_REG_LEN_08BIT, 0x04}, + {0x64a9, CRL_REG_LEN_08BIT, 0x30}, + {0x64aa, CRL_REG_LEN_08BIT, 0x00}, + {0x64ab, CRL_REG_LEN_08BIT, 0xff}, + {0x64ac, CRL_REG_LEN_08BIT, 0x06}, + {0x64ad, CRL_REG_LEN_08BIT, 0x10}, + {0x64ae, CRL_REG_LEN_08BIT, 0x01}, + {0x64af, CRL_REG_LEN_08BIT, 0xf6}, + {0x64b0, CRL_REG_LEN_08BIT, 0x04}, + {0x64b1, CRL_REG_LEN_08BIT, 0x30}, + {0x64b2, CRL_REG_LEN_08BIT, 0x06}, + {0x64b3, CRL_REG_LEN_08BIT, 0x00}, + {0x64b4, CRL_REG_LEN_08BIT, 0x00}, + {0x64b5, CRL_REG_LEN_08BIT, 0xff}, + {0x64b6, CRL_REG_LEN_08BIT, 0x06}, + {0x64b7, CRL_REG_LEN_08BIT, 0x20}, + {0x64b8, CRL_REG_LEN_08BIT, 0x01}, + {0x64b9, CRL_REG_LEN_08BIT, 0xf6}, + {0x64ba, CRL_REG_LEN_08BIT, 0x04}, + {0x64bb, CRL_REG_LEN_08BIT, 0x30}, + {0x64bc, CRL_REG_LEN_08BIT, 0x06}, + {0x64bd, CRL_REG_LEN_08BIT, 0x00}, + {0x64be, CRL_REG_LEN_08BIT, 0x00}, + {0x64bf, CRL_REG_LEN_08BIT, 0xff}, + {0x64c0, CRL_REG_LEN_08BIT, 0x04}, + {0x64c1, CRL_REG_LEN_08BIT, 0x31}, + {0x64c2, CRL_REG_LEN_08BIT, 0x04}, + {0x64c3, CRL_REG_LEN_08BIT, 0x30}, + {0x64c4, CRL_REG_LEN_08BIT, 0x01}, + {0x64c5, CRL_REG_LEN_08BIT, 0x20}, + {0x64c6, CRL_REG_LEN_08BIT, 0x01}, + {0x64c7, CRL_REG_LEN_08BIT, 0x31}, + {0x64c8, CRL_REG_LEN_08BIT, 0x01}, + {0x64c9, CRL_REG_LEN_08BIT, 0x32}, + {0x64ca, CRL_REG_LEN_08BIT, 0x01}, + {0x64cb, CRL_REG_LEN_08BIT, 0x33}, + {0x64cc, CRL_REG_LEN_08BIT, 0x01}, + {0x64cd, CRL_REG_LEN_08BIT, 0x34}, + {0x64ce, CRL_REG_LEN_08BIT, 0x01}, + {0x64cf, CRL_REG_LEN_08BIT, 0x35}, + {0x64d0, CRL_REG_LEN_08BIT, 0x01}, + {0x64d1, CRL_REG_LEN_08BIT, 0x36}, + {0x64d2, CRL_REG_LEN_08BIT, 0x01}, + {0x64d3, CRL_REG_LEN_08BIT, 0x37}, + {0x64d4, CRL_REG_LEN_08BIT, 0x01}, + {0x64d5, CRL_REG_LEN_08BIT, 0x38}, + {0x64d6, CRL_REG_LEN_08BIT, 0x01}, + {0x64d7, CRL_REG_LEN_08BIT, 0x39}, + {0x64d8, CRL_REG_LEN_08BIT, 0x01}, + {0x64d9, CRL_REG_LEN_08BIT, 0x3a}, + {0x64da, CRL_REG_LEN_08BIT, 0x01}, + {0x64db, CRL_REG_LEN_08BIT, 0x3b}, + {0x64dc, CRL_REG_LEN_08BIT, 0x01}, + {0x64dd, CRL_REG_LEN_08BIT, 0x3c}, + {0x64de, CRL_REG_LEN_08BIT, 0x01}, + {0x64df, CRL_REG_LEN_08BIT, 0x3d}, + {0x64e0, CRL_REG_LEN_08BIT, 0x01}, + {0x64e1, CRL_REG_LEN_08BIT, 0x3e}, + {0x64e2, CRL_REG_LEN_08BIT, 0x01}, + {0x64e3, CRL_REG_LEN_08BIT, 0x3f}, + {0x64e4, CRL_REG_LEN_08BIT, 0x02}, + {0x64e5, CRL_REG_LEN_08BIT, 0xa0}, + {0x64e6, CRL_REG_LEN_08BIT, 0x00}, + {0x64e7, CRL_REG_LEN_08BIT, 0xff}, + {0x64e8, CRL_REG_LEN_08BIT, 0x04}, + {0x64e9, CRL_REG_LEN_08BIT, 0x31}, + {0x64ea, CRL_REG_LEN_08BIT, 0x04}, + {0x64eb, CRL_REG_LEN_08BIT, 0x30}, + {0x64ec, CRL_REG_LEN_08BIT, 0x01}, + {0x64ed, CRL_REG_LEN_08BIT, 0x00}, + {0x64ee, CRL_REG_LEN_08BIT, 0x01}, + {0x64ef, CRL_REG_LEN_08BIT, 0x11}, + {0x64f0, CRL_REG_LEN_08BIT, 0x01}, + {0x64f1, CRL_REG_LEN_08BIT, 0x12}, + {0x64f2, CRL_REG_LEN_08BIT, 0x01}, + {0x64f3, CRL_REG_LEN_08BIT, 0x13}, + {0x64f4, CRL_REG_LEN_08BIT, 0x01}, + {0x64f5, CRL_REG_LEN_08BIT, 0x14}, + {0x64f6, CRL_REG_LEN_08BIT, 0x01}, + {0x64f7, CRL_REG_LEN_08BIT, 0x15}, + {0x64f8, CRL_REG_LEN_08BIT, 0x01}, + {0x64f9, CRL_REG_LEN_08BIT, 0x16}, + {0x64fa, CRL_REG_LEN_08BIT, 0x01}, + {0x64fb, CRL_REG_LEN_08BIT, 0x17}, + {0x64fc, CRL_REG_LEN_08BIT, 0x01}, + {0x64fd, CRL_REG_LEN_08BIT, 0x18}, + {0x64fe, CRL_REG_LEN_08BIT, 0x01}, + {0x64ff, CRL_REG_LEN_08BIT, 0x19}, + {0x6500, CRL_REG_LEN_08BIT, 0x01}, + {0x6501, CRL_REG_LEN_08BIT, 0x1a}, + {0x6502, CRL_REG_LEN_08BIT, 0x01}, + {0x6503, CRL_REG_LEN_08BIT, 0x1b}, + {0x6504, CRL_REG_LEN_08BIT, 0x01}, + {0x6505, CRL_REG_LEN_08BIT, 0x1c}, + {0x6506, CRL_REG_LEN_08BIT, 0x01}, + {0x6507, CRL_REG_LEN_08BIT, 0x1d}, + {0x6508, CRL_REG_LEN_08BIT, 0x01}, + {0x6509, CRL_REG_LEN_08BIT, 0x1e}, + {0x650a, CRL_REG_LEN_08BIT, 0x01}, + {0x650b, CRL_REG_LEN_08BIT, 0x1f}, + {0x650c, CRL_REG_LEN_08BIT, 0x02}, + {0x650d, CRL_REG_LEN_08BIT, 0xa0}, + {0x650e, CRL_REG_LEN_08BIT, 0x00}, + {0x650f, CRL_REG_LEN_08BIT, 0xff}, + {0x6510, CRL_REG_LEN_08BIT, 0x04}, + {0x6511, CRL_REG_LEN_08BIT, 0x20}, + {0x6512, CRL_REG_LEN_08BIT, 0x05}, + {0x6513, CRL_REG_LEN_08BIT, 0x86}, + {0x6514, CRL_REG_LEN_08BIT, 0x03}, + {0x6515, CRL_REG_LEN_08BIT, 0x0b}, + {0x6516, CRL_REG_LEN_08BIT, 0x05}, + {0x6517, CRL_REG_LEN_08BIT, 0x86}, + {0x6518, CRL_REG_LEN_08BIT, 0x00}, + {0x6519, CRL_REG_LEN_08BIT, 0x00}, + {0x651a, CRL_REG_LEN_08BIT, 0x05}, + {0x651b, CRL_REG_LEN_08BIT, 0x06}, + {0x651c, CRL_REG_LEN_08BIT, 0x00}, + {0x651d, CRL_REG_LEN_08BIT, 0x04}, + {0x651e, CRL_REG_LEN_08BIT, 0x05}, + {0x651f, CRL_REG_LEN_08BIT, 0x04}, + {0x6520, CRL_REG_LEN_08BIT, 0x00}, + {0x6521, CRL_REG_LEN_08BIT, 0x04}, + {0x6522, CRL_REG_LEN_08BIT, 0x05}, + {0x6523, CRL_REG_LEN_08BIT, 0x00}, + {0x6524, CRL_REG_LEN_08BIT, 0x05}, + {0x6525, CRL_REG_LEN_08BIT, 0x0a}, + {0x6526, CRL_REG_LEN_08BIT, 0x03}, + {0x6527, CRL_REG_LEN_08BIT, 0x9a}, + {0x6528, CRL_REG_LEN_08BIT, 0x05}, + {0x6529, CRL_REG_LEN_08BIT, 0x86}, + {0x652a, CRL_REG_LEN_08BIT, 0x00}, + {0x652b, CRL_REG_LEN_08BIT, 0x00}, + {0x652c, CRL_REG_LEN_08BIT, 0x05}, + {0x652d, CRL_REG_LEN_08BIT, 0x06}, + {0x652e, CRL_REG_LEN_08BIT, 0x00}, + {0x652f, CRL_REG_LEN_08BIT, 0x01}, + {0x6530, CRL_REG_LEN_08BIT, 0x05}, + {0x6531, CRL_REG_LEN_08BIT, 0x04}, + {0x6532, CRL_REG_LEN_08BIT, 0x00}, + {0x6533, CRL_REG_LEN_08BIT, 0x04}, + {0x6534, CRL_REG_LEN_08BIT, 0x05}, + {0x6535, CRL_REG_LEN_08BIT, 0x00}, + {0x6536, CRL_REG_LEN_08BIT, 0x05}, + {0x6537, CRL_REG_LEN_08BIT, 0x0a}, + {0x6538, CRL_REG_LEN_08BIT, 0x03}, + {0x6539, CRL_REG_LEN_08BIT, 0x99}, + {0x653a, CRL_REG_LEN_08BIT, 0x05}, + {0x653b, CRL_REG_LEN_08BIT, 0x06}, + {0x653c, CRL_REG_LEN_08BIT, 0x00}, + {0x653d, CRL_REG_LEN_08BIT, 0x00}, + {0x653e, CRL_REG_LEN_08BIT, 0x05}, + {0x653f, CRL_REG_LEN_08BIT, 0x04}, + {0x6540, CRL_REG_LEN_08BIT, 0x00}, + {0x6541, CRL_REG_LEN_08BIT, 0x04}, + {0x6542, CRL_REG_LEN_08BIT, 0x05}, + {0x6543, CRL_REG_LEN_08BIT, 0x00}, + {0x6544, CRL_REG_LEN_08BIT, 0x05}, + {0x6545, CRL_REG_LEN_08BIT, 0x0a}, + {0x6546, CRL_REG_LEN_08BIT, 0x03}, + {0x6547, CRL_REG_LEN_08BIT, 0x98}, + {0x6548, CRL_REG_LEN_08BIT, 0x05}, + {0x6549, CRL_REG_LEN_08BIT, 0x06}, + {0x654a, CRL_REG_LEN_08BIT, 0x00}, + {0x654b, CRL_REG_LEN_08BIT, 0x00}, + {0x654c, CRL_REG_LEN_08BIT, 0x05}, + {0x654d, CRL_REG_LEN_08BIT, 0x04}, + {0x654e, CRL_REG_LEN_08BIT, 0x00}, + {0x654f, CRL_REG_LEN_08BIT, 0x04}, + {0x6550, CRL_REG_LEN_08BIT, 0x05}, + {0x6551, CRL_REG_LEN_08BIT, 0x00}, + {0x6552, CRL_REG_LEN_08BIT, 0x05}, + {0x6553, CRL_REG_LEN_08BIT, 0x0a}, + {0x6554, CRL_REG_LEN_08BIT, 0x03}, + {0x6555, CRL_REG_LEN_08BIT, 0x97}, + {0x6556, CRL_REG_LEN_08BIT, 0x05}, + {0x6557, CRL_REG_LEN_08BIT, 0x06}, + {0x6558, CRL_REG_LEN_08BIT, 0x05}, + {0x6559, CRL_REG_LEN_08BIT, 0x04}, + {0x655a, CRL_REG_LEN_08BIT, 0x00}, + {0x655b, CRL_REG_LEN_08BIT, 0x04}, + {0x655c, CRL_REG_LEN_08BIT, 0x05}, + {0x655d, CRL_REG_LEN_08BIT, 0x00}, + {0x655e, CRL_REG_LEN_08BIT, 0x05}, + {0x655f, CRL_REG_LEN_08BIT, 0x0a}, + {0x6560, CRL_REG_LEN_08BIT, 0x03}, + {0x6561, CRL_REG_LEN_08BIT, 0x96}, + {0x6562, CRL_REG_LEN_08BIT, 0x05}, + {0x6563, CRL_REG_LEN_08BIT, 0x06}, + {0x6564, CRL_REG_LEN_08BIT, 0x05}, + {0x6565, CRL_REG_LEN_08BIT, 0x04}, + {0x6566, CRL_REG_LEN_08BIT, 0x00}, + {0x6567, CRL_REG_LEN_08BIT, 0x04}, + {0x6568, CRL_REG_LEN_08BIT, 0x05}, + {0x6569, CRL_REG_LEN_08BIT, 0x00}, + {0x656a, CRL_REG_LEN_08BIT, 0x05}, + {0x656b, CRL_REG_LEN_08BIT, 0x0a}, + {0x656c, CRL_REG_LEN_08BIT, 0x03}, + {0x656d, CRL_REG_LEN_08BIT, 0x95}, + {0x656e, CRL_REG_LEN_08BIT, 0x05}, + {0x656f, CRL_REG_LEN_08BIT, 0x06}, + {0x6570, CRL_REG_LEN_08BIT, 0x05}, + {0x6571, CRL_REG_LEN_08BIT, 0x04}, + {0x6572, CRL_REG_LEN_08BIT, 0x00}, + {0x6573, CRL_REG_LEN_08BIT, 0x04}, + {0x6574, CRL_REG_LEN_08BIT, 0x05}, + {0x6575, CRL_REG_LEN_08BIT, 0x00}, + {0x6576, CRL_REG_LEN_08BIT, 0x05}, + {0x6577, CRL_REG_LEN_08BIT, 0x0a}, + {0x6578, CRL_REG_LEN_08BIT, 0x03}, + {0x6579, CRL_REG_LEN_08BIT, 0x94}, + {0x657a, CRL_REG_LEN_08BIT, 0x05}, + {0x657b, CRL_REG_LEN_08BIT, 0x06}, + {0x657c, CRL_REG_LEN_08BIT, 0x00}, + {0x657d, CRL_REG_LEN_08BIT, 0x00}, + {0x657e, CRL_REG_LEN_08BIT, 0x05}, + {0x657f, CRL_REG_LEN_08BIT, 0x04}, + {0x6580, CRL_REG_LEN_08BIT, 0x00}, + {0x6581, CRL_REG_LEN_08BIT, 0x04}, + {0x6582, CRL_REG_LEN_08BIT, 0x05}, + {0x6583, CRL_REG_LEN_08BIT, 0x00}, + {0x6584, CRL_REG_LEN_08BIT, 0x05}, + {0x6585, CRL_REG_LEN_08BIT, 0x0a}, + {0x6586, CRL_REG_LEN_08BIT, 0x03}, + {0x6587, CRL_REG_LEN_08BIT, 0x93}, + {0x6588, CRL_REG_LEN_08BIT, 0x05}, + {0x6589, CRL_REG_LEN_08BIT, 0x06}, + {0x658a, CRL_REG_LEN_08BIT, 0x00}, + {0x658b, CRL_REG_LEN_08BIT, 0x00}, + {0x658c, CRL_REG_LEN_08BIT, 0x05}, + {0x658d, CRL_REG_LEN_08BIT, 0x04}, + {0x658e, CRL_REG_LEN_08BIT, 0x00}, + {0x658f, CRL_REG_LEN_08BIT, 0x04}, + {0x6590, CRL_REG_LEN_08BIT, 0x05}, + {0x6591, CRL_REG_LEN_08BIT, 0x00}, + {0x6592, CRL_REG_LEN_08BIT, 0x05}, + {0x6593, CRL_REG_LEN_08BIT, 0x0a}, + {0x6594, CRL_REG_LEN_08BIT, 0x03}, + {0x6595, CRL_REG_LEN_08BIT, 0x92}, + {0x6596, CRL_REG_LEN_08BIT, 0x05}, + {0x6597, CRL_REG_LEN_08BIT, 0x06}, + {0x6598, CRL_REG_LEN_08BIT, 0x05}, + {0x6599, CRL_REG_LEN_08BIT, 0x04}, + {0x659a, CRL_REG_LEN_08BIT, 0x00}, + {0x659b, CRL_REG_LEN_08BIT, 0x04}, + {0x659c, CRL_REG_LEN_08BIT, 0x05}, + {0x659d, CRL_REG_LEN_08BIT, 0x00}, + {0x659e, CRL_REG_LEN_08BIT, 0x05}, + {0x659f, CRL_REG_LEN_08BIT, 0x0a}, + {0x65a0, CRL_REG_LEN_08BIT, 0x03}, + {0x65a1, CRL_REG_LEN_08BIT, 0x91}, + {0x65a2, CRL_REG_LEN_08BIT, 0x05}, + {0x65a3, CRL_REG_LEN_08BIT, 0x06}, + {0x65a4, CRL_REG_LEN_08BIT, 0x05}, + {0x65a5, CRL_REG_LEN_08BIT, 0x04}, + {0x65a6, CRL_REG_LEN_08BIT, 0x00}, + {0x65a7, CRL_REG_LEN_08BIT, 0x04}, + {0x65a8, CRL_REG_LEN_08BIT, 0x05}, + {0x65a9, CRL_REG_LEN_08BIT, 0x00}, + {0x65aa, CRL_REG_LEN_08BIT, 0x05}, + {0x65ab, CRL_REG_LEN_08BIT, 0x0a}, + {0x65ac, CRL_REG_LEN_08BIT, 0x03}, + {0x65ad, CRL_REG_LEN_08BIT, 0x90}, + {0x65ae, CRL_REG_LEN_08BIT, 0x05}, + {0x65af, CRL_REG_LEN_08BIT, 0x06}, + {0x65b0, CRL_REG_LEN_08BIT, 0x05}, + {0x65b1, CRL_REG_LEN_08BIT, 0x04}, + {0x65b2, CRL_REG_LEN_08BIT, 0x00}, + {0x65b3, CRL_REG_LEN_08BIT, 0x04}, + {0x65b4, CRL_REG_LEN_08BIT, 0x05}, + {0x65b5, CRL_REG_LEN_08BIT, 0x00}, + {0x65b6, CRL_REG_LEN_08BIT, 0x05}, + {0x65b7, CRL_REG_LEN_08BIT, 0x0a}, + {0x65b8, CRL_REG_LEN_08BIT, 0x02}, + {0x65b9, CRL_REG_LEN_08BIT, 0x90}, + {0x65ba, CRL_REG_LEN_08BIT, 0x05}, + {0x65bb, CRL_REG_LEN_08BIT, 0x06}, + {0x65bc, CRL_REG_LEN_08BIT, 0x00}, + {0x65bd, CRL_REG_LEN_08BIT, 0xff}, + {0x65be, CRL_REG_LEN_08BIT, 0x04}, + {0x65bf, CRL_REG_LEN_08BIT, 0x70}, + {0x65c0, CRL_REG_LEN_08BIT, 0x08}, + {0x65c1, CRL_REG_LEN_08BIT, 0x76}, + {0x65c2, CRL_REG_LEN_08BIT, 0x00}, + {0x65c3, CRL_REG_LEN_08BIT, 0xff}, + {0x65c4, CRL_REG_LEN_08BIT, 0x08}, + {0x65c5, CRL_REG_LEN_08BIT, 0x76}, + {0x65c6, CRL_REG_LEN_08BIT, 0x04}, + {0x65c7, CRL_REG_LEN_08BIT, 0x0c}, + {0x65c8, CRL_REG_LEN_08BIT, 0x05}, + {0x65c9, CRL_REG_LEN_08BIT, 0x07}, + {0x65ca, CRL_REG_LEN_08BIT, 0x04}, + {0x65cb, CRL_REG_LEN_08BIT, 0x04}, + {0x65cc, CRL_REG_LEN_08BIT, 0x00}, + {0x65cd, CRL_REG_LEN_08BIT, 0xff}, + {0x65ce, CRL_REG_LEN_08BIT, 0x00}, + {0x65cf, CRL_REG_LEN_08BIT, 0xff}, + {0x65d0, CRL_REG_LEN_08BIT, 0x00}, + {0x65d1, CRL_REG_LEN_08BIT, 0xff}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x7f}, + {0x303c, CRL_REG_LEN_08BIT, 0xfe}, + {0x303d, CRL_REG_LEN_08BIT, 0x19}, + {0x303e, CRL_REG_LEN_08BIT, 0xd7}, + {0x303f, CRL_REG_LEN_08BIT, 0x09}, + {0x3040, CRL_REG_LEN_08BIT, 0x78}, + {0x3042, CRL_REG_LEN_08BIT, 0x05}, + {0x328a, CRL_REG_LEN_08BIT, 0x10}, +}; + +static struct crl_register_write_rep ov10640_1280_1088_LONG_RAW[] = { + {0x328a, CRL_REG_LEN_08BIT, 0x11}, + {0x313f, CRL_REG_LEN_08BIT, 0x80}, + {0x3132, CRL_REG_LEN_08BIT, 0x24}, + {0x3014, CRL_REG_LEN_08BIT, 0x03}, + {0x3023, CRL_REG_LEN_08BIT, 0x05}, + {0x3032, CRL_REG_LEN_08BIT, 0x35}, + {0x3033, CRL_REG_LEN_08BIT, 0x04}, + {0x3054, CRL_REG_LEN_08BIT, 0x00}, + {0x3055, CRL_REG_LEN_08BIT, 0x08}, + {0x3056, CRL_REG_LEN_08BIT, 0x01}, + {0x3057, CRL_REG_LEN_08BIT, 0xff}, + {0x3058, CRL_REG_LEN_08BIT, 0xaf}, + {0x3059, CRL_REG_LEN_08BIT, 0x44}, + {0x305a, CRL_REG_LEN_08BIT, 0x02}, + {0x305b, CRL_REG_LEN_08BIT, 0x00}, + {0x305c, CRL_REG_LEN_08BIT, 0x30}, + {0x305d, CRL_REG_LEN_08BIT, 0x9e}, + {0x305e, CRL_REG_LEN_08BIT, 0x19}, + {0x305f, CRL_REG_LEN_08BIT, 0x18}, + {0x3060, CRL_REG_LEN_08BIT, 0xf9}, + {0x3061, CRL_REG_LEN_08BIT, 0xf0}, + {0x308c, CRL_REG_LEN_08BIT, 0xB3}, + {0x308f, CRL_REG_LEN_08BIT, 0x10}, + {0x3091, CRL_REG_LEN_08BIT, 0x00}, + {0x3093, CRL_REG_LEN_08BIT, 0x01}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30ad, CRL_REG_LEN_08BIT, 0x03}, + {0x30ae, CRL_REG_LEN_08BIT, 0x80}, + {0x30af, CRL_REG_LEN_08BIT, 0x80}, + {0x30b0, CRL_REG_LEN_08BIT, 0xff}, + {0x30b1, CRL_REG_LEN_08BIT, 0x3f}, + {0x30b2, CRL_REG_LEN_08BIT, 0x22}, + {0x30b9, CRL_REG_LEN_08BIT, 0x22}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x00}, + {0x30be, CRL_REG_LEN_08BIT, 0x00}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x00}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x00}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x80}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c6, CRL_REG_LEN_08BIT, 0x80}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x80}, + {0x3119, CRL_REG_LEN_08BIT, 0x45}, + {0x311a, CRL_REG_LEN_08BIT, 0x01}, + {0x311b, CRL_REG_LEN_08BIT, 0x4a}, + {0x3074, CRL_REG_LEN_08BIT, 0x00}, + {0x3075, CRL_REG_LEN_08BIT, 0x00}, + {0x3076, CRL_REG_LEN_08BIT, 0x00}, + {0x3077, CRL_REG_LEN_08BIT, 0x02}, + {0x3078, CRL_REG_LEN_08BIT, 0x05}, + {0x3079, CRL_REG_LEN_08BIT, 0x07}, + {0x307a, CRL_REG_LEN_08BIT, 0x04}, + {0x307b, CRL_REG_LEN_08BIT, 0x45}, + {0x307c, CRL_REG_LEN_08BIT, 0x05}, + {0x307d, CRL_REG_LEN_08BIT, 0x00}, + {0x307e, CRL_REG_LEN_08BIT, 0x04}, + {0x307f, CRL_REG_LEN_08BIT, 0x40}, + {0x3084, CRL_REG_LEN_08BIT, 0x00}, + {0x3085, CRL_REG_LEN_08BIT, 0x04}, + {0x3086, CRL_REG_LEN_08BIT, 0x00}, + {0x3087, CRL_REG_LEN_08BIT, 0x04}, + {0x3088, CRL_REG_LEN_08BIT, 0x00}, + {0x3089, CRL_REG_LEN_08BIT, 0x40}, + {0x308d, CRL_REG_LEN_08BIT, 0x92}, + {0x3094, CRL_REG_LEN_08BIT, 0xa5}, + {0x30fa, CRL_REG_LEN_08BIT, 0x06}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x01}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3127, CRL_REG_LEN_08BIT, 0x63}, + {0x3128, CRL_REG_LEN_08BIT, 0xc0}, + {0x3129, CRL_REG_LEN_08BIT, 0x00}, + {0x31be, CRL_REG_LEN_08BIT, 0x01}, + {0x30a5, CRL_REG_LEN_08BIT, 0x78}, + {0x30a6, CRL_REG_LEN_08BIT, 0x40}, + {0x30a7, CRL_REG_LEN_08BIT, 0x78}, + {0x30a8, CRL_REG_LEN_08BIT, 0x80}, + {0x30a9, CRL_REG_LEN_08BIT, 0x78}, + {0x30aa, CRL_REG_LEN_08BIT, 0xe0}, + {0x30ab, CRL_REG_LEN_08BIT, 0xf9}, + {0x30ac, CRL_REG_LEN_08BIT, 0xc0}, + {0x3440, CRL_REG_LEN_08BIT, 0x04}, + {0x3444, CRL_REG_LEN_08BIT, 0x28}, + {0x344e, CRL_REG_LEN_08BIT, 0x2c}, + {0x3457, CRL_REG_LEN_08BIT, 0x33}, + {0x345e, CRL_REG_LEN_08BIT, 0x38}, + {0x3461, CRL_REG_LEN_08BIT, 0xa8}, + {0x7002, CRL_REG_LEN_08BIT, 0xaa}, + {0x7001, CRL_REG_LEN_08BIT, 0xdf}, + {0x7048, CRL_REG_LEN_08BIT, 0x00}, + {0x7049, CRL_REG_LEN_08BIT, 0x02}, + {0x704a, CRL_REG_LEN_08BIT, 0x02}, + {0x704b, CRL_REG_LEN_08BIT, 0x00}, + {0x704c, CRL_REG_LEN_08BIT, 0x01}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x7043, CRL_REG_LEN_08BIT, 0x04}, + {0x7040, CRL_REG_LEN_08BIT, 0x3c}, + {0x7047, CRL_REG_LEN_08BIT, 0x00}, + {0x7044, CRL_REG_LEN_08BIT, 0x01}, + {0x7000, CRL_REG_LEN_08BIT, 0x1f}, + {0x7084, CRL_REG_LEN_08BIT, 0x01}, + {0x7085, CRL_REG_LEN_08BIT, 0x03}, + {0x7086, CRL_REG_LEN_08BIT, 0x02}, + {0x7087, CRL_REG_LEN_08BIT, 0x40}, + {0x7088, CRL_REG_LEN_08BIT, 0x01}, + {0x7089, CRL_REG_LEN_08BIT, 0x20}, + {0x707f, CRL_REG_LEN_08BIT, 0x04}, + {0x707c, CRL_REG_LEN_08BIT, 0x3c}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0x01}, + {0x7003, CRL_REG_LEN_08BIT, 0xdf}, + {0x70c0, CRL_REG_LEN_08BIT, 0x00}, + {0x70c1, CRL_REG_LEN_08BIT, 0x02}, + {0x70c2, CRL_REG_LEN_08BIT, 0x02}, + {0x70c3, CRL_REG_LEN_08BIT, 0x00}, + {0x70c4, CRL_REG_LEN_08BIT, 0x01}, + {0x70c5, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0x03}, + {0x70b9, CRL_REG_LEN_08BIT, 0x98}, + {0x70bc, CRL_REG_LEN_08BIT, 0x00}, + {0x70bd, CRL_REG_LEN_08BIT, 0x80}, + {0x7004, CRL_REG_LEN_08BIT, 0x02}, + {0x7005, CRL_REG_LEN_08BIT, 0x00}, + {0x7006, CRL_REG_LEN_08BIT, 0x01}, + {0x7007, CRL_REG_LEN_08BIT, 0x80}, + {0x7008, CRL_REG_LEN_08BIT, 0x02}, + {0x7009, CRL_REG_LEN_08BIT, 0x00}, + {0x700a, CRL_REG_LEN_08BIT, 0x04}, + {0x700b, CRL_REG_LEN_08BIT, 0x00}, + {0x700e, CRL_REG_LEN_08BIT, 0x00}, + {0x700f, CRL_REG_LEN_08BIT, 0x60}, + {0x701a, CRL_REG_LEN_08BIT, 0x02}, + {0x701b, CRL_REG_LEN_08BIT, 0x00}, + {0x701c, CRL_REG_LEN_08BIT, 0x01}, + {0x701d, CRL_REG_LEN_08BIT, 0x80}, + {0x701e, CRL_REG_LEN_08BIT, 0x02}, + {0x701f, CRL_REG_LEN_08BIT, 0x00}, + {0x7020, CRL_REG_LEN_08BIT, 0x04}, + {0x7021, CRL_REG_LEN_08BIT, 0x00}, + {0x7024, CRL_REG_LEN_08BIT, 0x00}, + {0x7025, CRL_REG_LEN_08BIT, 0x60}, + {0x70e7, CRL_REG_LEN_08BIT, 0x00}, + {0x70e4, CRL_REG_LEN_08BIT, 0x10}, + {0x70e5, CRL_REG_LEN_08BIT, 0x00}, + {0x70e6, CRL_REG_LEN_08BIT, 0x00}, + {0x70eb, CRL_REG_LEN_08BIT, 0x00}, + {0x70e8, CRL_REG_LEN_08BIT, 0x10}, + {0x70e9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ea, CRL_REG_LEN_08BIT, 0x00}, + {0x70ef, CRL_REG_LEN_08BIT, 0x00}, + {0x70ec, CRL_REG_LEN_08BIT, 0xfd}, + {0x70ed, CRL_REG_LEN_08BIT, 0x00}, + {0x70ee, CRL_REG_LEN_08BIT, 0x00}, + {0x70eb, CRL_REG_LEN_08BIT, 0x00}, + {0x70f0, CRL_REG_LEN_08BIT, 0xfd}, + {0x70f1, CRL_REG_LEN_08BIT, 0x00}, + {0x70f2, CRL_REG_LEN_08BIT, 0x00}, + {0x30fb, CRL_REG_LEN_08BIT, 0x06}, + {0x30fc, CRL_REG_LEN_08BIT, 0x80}, + {0x30fd, CRL_REG_LEN_08BIT, 0x02}, + {0x30fe, CRL_REG_LEN_08BIT, 0x93}, + {0x6000, CRL_REG_LEN_08BIT, 0xc1}, + {0x6001, CRL_REG_LEN_08BIT, 0xb9}, + {0x6002, CRL_REG_LEN_08BIT, 0xba}, + {0x6003, CRL_REG_LEN_08BIT, 0xa4}, + {0x6004, CRL_REG_LEN_08BIT, 0xb5}, + {0x6005, CRL_REG_LEN_08BIT, 0xa0}, + {0x6006, CRL_REG_LEN_08BIT, 0x82}, + {0x6007, CRL_REG_LEN_08BIT, 0xa7}, + {0x6008, CRL_REG_LEN_08BIT, 0xb7}, + {0x6009, CRL_REG_LEN_08BIT, 0x5c}, + {0x600a, CRL_REG_LEN_08BIT, 0x9e}, + {0x600b, CRL_REG_LEN_08BIT, 0xc0}, + {0x600c, CRL_REG_LEN_08BIT, 0xd2}, + {0x600d, CRL_REG_LEN_08BIT, 0x33}, + {0x600e, CRL_REG_LEN_08BIT, 0xcc}, + {0x600f, CRL_REG_LEN_08BIT, 0xe2}, + {0x6010, CRL_REG_LEN_08BIT, 0xc1}, + {0x6011, CRL_REG_LEN_08BIT, 0xab}, + {0x6012, CRL_REG_LEN_08BIT, 0xb7}, + {0x6013, CRL_REG_LEN_08BIT, 0x00}, + {0x6014, CRL_REG_LEN_08BIT, 0x00}, + {0x6015, CRL_REG_LEN_08BIT, 0x00}, + {0x6016, CRL_REG_LEN_08BIT, 0x00}, + {0x6017, CRL_REG_LEN_08BIT, 0x00}, + {0x6018, CRL_REG_LEN_08BIT, 0x00}, + {0x6019, CRL_REG_LEN_08BIT, 0x00}, + {0x601a, CRL_REG_LEN_08BIT, 0x00}, + {0x601b, CRL_REG_LEN_08BIT, 0x00}, + {0x601c, CRL_REG_LEN_08BIT, 0x00}, + {0x601d, CRL_REG_LEN_08BIT, 0x00}, + {0x601e, CRL_REG_LEN_08BIT, 0x9c}, + {0x601f, CRL_REG_LEN_08BIT, 0x94}, + {0x6020, CRL_REG_LEN_08BIT, 0x90}, + {0x6021, CRL_REG_LEN_08BIT, 0xc5}, + {0x6022, CRL_REG_LEN_08BIT, 0x01}, + {0x6023, CRL_REG_LEN_08BIT, 0x54}, + {0x6024, CRL_REG_LEN_08BIT, 0x2a}, + {0x6025, CRL_REG_LEN_08BIT, 0x61}, + {0x6026, CRL_REG_LEN_08BIT, 0xd2}, + {0x6027, CRL_REG_LEN_08BIT, 0xcc}, + {0x6028, CRL_REG_LEN_08BIT, 0x04}, + {0x6029, CRL_REG_LEN_08BIT, 0x35}, + {0x602a, CRL_REG_LEN_08BIT, 0xb1}, + {0x602b, CRL_REG_LEN_08BIT, 0xb2}, + {0x602c, CRL_REG_LEN_08BIT, 0xb3}, + {0x602d, CRL_REG_LEN_08BIT, 0xd2}, + {0x602e, CRL_REG_LEN_08BIT, 0xd3}, + {0x602f, CRL_REG_LEN_08BIT, 0x12}, + {0x6030, CRL_REG_LEN_08BIT, 0x31}, + {0x6031, CRL_REG_LEN_08BIT, 0xcc}, + {0x6032, CRL_REG_LEN_08BIT, 0x06}, + {0x6033, CRL_REG_LEN_08BIT, 0xd2}, + {0x6034, CRL_REG_LEN_08BIT, 0xc4}, + {0x6035, CRL_REG_LEN_08BIT, 0xce}, + {0x6036, CRL_REG_LEN_08BIT, 0x18}, + {0x6037, CRL_REG_LEN_08BIT, 0xcf}, + {0x6038, CRL_REG_LEN_08BIT, 0x1e}, + {0x6039, CRL_REG_LEN_08BIT, 0xd0}, + {0x603a, CRL_REG_LEN_08BIT, 0x24}, + {0x603b, CRL_REG_LEN_08BIT, 0xc5}, + {0x603c, CRL_REG_LEN_08BIT, 0xd2}, + {0x603d, CRL_REG_LEN_08BIT, 0xbc}, + {0x603e, CRL_REG_LEN_08BIT, 0xcc}, + {0x603f, CRL_REG_LEN_08BIT, 0x52}, + {0x6040, CRL_REG_LEN_08BIT, 0x2b}, + {0x6041, CRL_REG_LEN_08BIT, 0xd2}, + {0x6042, CRL_REG_LEN_08BIT, 0xd3}, + {0x6043, CRL_REG_LEN_08BIT, 0x02}, + {0x6044, CRL_REG_LEN_08BIT, 0xcc}, + {0x6045, CRL_REG_LEN_08BIT, 0x0a}, + {0x6046, CRL_REG_LEN_08BIT, 0xd2}, + {0x6047, CRL_REG_LEN_08BIT, 0xd3}, + {0x6048, CRL_REG_LEN_08BIT, 0x0f}, + {0x6049, CRL_REG_LEN_08BIT, 0x1a}, + {0x604a, CRL_REG_LEN_08BIT, 0x2a}, + {0x604b, CRL_REG_LEN_08BIT, 0xd4}, + {0x604c, CRL_REG_LEN_08BIT, 0xf6}, + {0x604d, CRL_REG_LEN_08BIT, 0xba}, + {0x604e, CRL_REG_LEN_08BIT, 0x56}, + {0x604f, CRL_REG_LEN_08BIT, 0xd3}, + {0x6050, CRL_REG_LEN_08BIT, 0x2e}, + {0x6051, CRL_REG_LEN_08BIT, 0x54}, + {0x6052, CRL_REG_LEN_08BIT, 0x26}, + {0x6053, CRL_REG_LEN_08BIT, 0xd2}, + {0x6054, CRL_REG_LEN_08BIT, 0xcc}, + {0x6055, CRL_REG_LEN_08BIT, 0x60}, + {0x6056, CRL_REG_LEN_08BIT, 0xd2}, + {0x6057, CRL_REG_LEN_08BIT, 0xd3}, + {0x6058, CRL_REG_LEN_08BIT, 0x27}, + {0x6059, CRL_REG_LEN_08BIT, 0x27}, + {0x605a, CRL_REG_LEN_08BIT, 0x08}, + {0x605b, CRL_REG_LEN_08BIT, 0x1a}, + {0x605c, CRL_REG_LEN_08BIT, 0xcc}, + {0x605d, CRL_REG_LEN_08BIT, 0x88}, + {0x605e, CRL_REG_LEN_08BIT, 0x00}, + {0x605f, CRL_REG_LEN_08BIT, 0x12}, + {0x6060, CRL_REG_LEN_08BIT, 0x2c}, + {0x6061, CRL_REG_LEN_08BIT, 0x60}, + {0x6062, CRL_REG_LEN_08BIT, 0xc2}, + {0x6063, CRL_REG_LEN_08BIT, 0xb9}, + {0x6064, CRL_REG_LEN_08BIT, 0xa5}, + {0x6065, CRL_REG_LEN_08BIT, 0xb5}, + {0x6066, CRL_REG_LEN_08BIT, 0xa0}, + {0x6067, CRL_REG_LEN_08BIT, 0x82}, + {0x6068, CRL_REG_LEN_08BIT, 0x5c}, + {0x6069, CRL_REG_LEN_08BIT, 0xd4}, + {0x606a, CRL_REG_LEN_08BIT, 0xbe}, + {0x606b, CRL_REG_LEN_08BIT, 0xd4}, + {0x606c, CRL_REG_LEN_08BIT, 0xbe}, + {0x606d, CRL_REG_LEN_08BIT, 0xd3}, + {0x606e, CRL_REG_LEN_08BIT, 0x01}, + {0x606f, CRL_REG_LEN_08BIT, 0x7c}, + {0x6070, CRL_REG_LEN_08BIT, 0x74}, + {0x6071, CRL_REG_LEN_08BIT, 0x00}, + {0x6072, CRL_REG_LEN_08BIT, 0x61}, + {0x6073, CRL_REG_LEN_08BIT, 0x2a}, + {0x6074, CRL_REG_LEN_08BIT, 0xd2}, + {0x6075, CRL_REG_LEN_08BIT, 0xcc}, + {0x6076, CRL_REG_LEN_08BIT, 0xdf}, + {0x6077, CRL_REG_LEN_08BIT, 0xc6}, + {0x6078, CRL_REG_LEN_08BIT, 0x35}, + {0x6079, CRL_REG_LEN_08BIT, 0xd2}, + {0x607a, CRL_REG_LEN_08BIT, 0xcc}, + {0x607b, CRL_REG_LEN_08BIT, 0x06}, + {0x607c, CRL_REG_LEN_08BIT, 0x31}, + {0x607d, CRL_REG_LEN_08BIT, 0xd2}, + {0x607e, CRL_REG_LEN_08BIT, 0xc5}, + {0x607f, CRL_REG_LEN_08BIT, 0xbb}, + {0x6080, CRL_REG_LEN_08BIT, 0xcc}, + {0x6081, CRL_REG_LEN_08BIT, 0x18}, + {0x6082, CRL_REG_LEN_08BIT, 0xc6}, + {0x6083, CRL_REG_LEN_08BIT, 0xd2}, + {0x6084, CRL_REG_LEN_08BIT, 0xbd}, + {0x6085, CRL_REG_LEN_08BIT, 0xcc}, + {0x6086, CRL_REG_LEN_08BIT, 0x52}, + {0x6087, CRL_REG_LEN_08BIT, 0x2b}, + {0x6088, CRL_REG_LEN_08BIT, 0xd2}, + {0x6089, CRL_REG_LEN_08BIT, 0xd3}, + {0x608a, CRL_REG_LEN_08BIT, 0x01}, + {0x608b, CRL_REG_LEN_08BIT, 0xcc}, + {0x608c, CRL_REG_LEN_08BIT, 0x0a}, + {0x608d, CRL_REG_LEN_08BIT, 0xd2}, + {0x608e, CRL_REG_LEN_08BIT, 0xd3}, + {0x608f, CRL_REG_LEN_08BIT, 0x0f}, + {0x6090, CRL_REG_LEN_08BIT, 0x1a}, + {0x6091, CRL_REG_LEN_08BIT, 0x71}, + {0x6092, CRL_REG_LEN_08BIT, 0x2a}, + {0x6093, CRL_REG_LEN_08BIT, 0xd4}, + {0x6094, CRL_REG_LEN_08BIT, 0xf6}, + {0x6095, CRL_REG_LEN_08BIT, 0xd3}, + {0x6096, CRL_REG_LEN_08BIT, 0x22}, + {0x6097, CRL_REG_LEN_08BIT, 0x70}, + {0x6098, CRL_REG_LEN_08BIT, 0xca}, + {0x6099, CRL_REG_LEN_08BIT, 0x26}, + {0x609a, CRL_REG_LEN_08BIT, 0xd2}, + {0x609b, CRL_REG_LEN_08BIT, 0xcc}, + {0x609c, CRL_REG_LEN_08BIT, 0x60}, + {0x609d, CRL_REG_LEN_08BIT, 0xd2}, + {0x609e, CRL_REG_LEN_08BIT, 0xd3}, + {0x609f, CRL_REG_LEN_08BIT, 0x27}, + {0x60a0, CRL_REG_LEN_08BIT, 0x27}, + {0x60a1, CRL_REG_LEN_08BIT, 0x08}, + {0x60a2, CRL_REG_LEN_08BIT, 0x1a}, + {0x60a3, CRL_REG_LEN_08BIT, 0xcc}, + {0x60a4, CRL_REG_LEN_08BIT, 0x88}, + {0x60a5, CRL_REG_LEN_08BIT, 0x12}, + {0x60a6, CRL_REG_LEN_08BIT, 0x2c}, + {0x60a7, CRL_REG_LEN_08BIT, 0x60}, + {0x60a8, CRL_REG_LEN_08BIT, 0x00}, + {0x60a9, CRL_REG_LEN_08BIT, 0x00}, + {0x60aa, CRL_REG_LEN_08BIT, 0xc0}, + {0x60ab, CRL_REG_LEN_08BIT, 0xb9}, + {0x60ac, CRL_REG_LEN_08BIT, 0xa3}, + {0x60ad, CRL_REG_LEN_08BIT, 0xb5}, + {0x60ae, CRL_REG_LEN_08BIT, 0x00}, + {0x60af, CRL_REG_LEN_08BIT, 0xa0}, + {0x60b0, CRL_REG_LEN_08BIT, 0x82}, + {0x60b1, CRL_REG_LEN_08BIT, 0x5c}, + {0x60b2, CRL_REG_LEN_08BIT, 0xd4}, + {0x60b3, CRL_REG_LEN_08BIT, 0xa0}, + {0x60b4, CRL_REG_LEN_08BIT, 0x9d}, + {0x60b5, CRL_REG_LEN_08BIT, 0xd3}, + {0x60b6, CRL_REG_LEN_08BIT, 0x26}, + {0x60b7, CRL_REG_LEN_08BIT, 0xb0}, + {0x60b8, CRL_REG_LEN_08BIT, 0xb7}, + {0x60b9, CRL_REG_LEN_08BIT, 0x00}, + {0x60ba, CRL_REG_LEN_08BIT, 0xd3}, + {0x60bb, CRL_REG_LEN_08BIT, 0x0a}, + {0x60bc, CRL_REG_LEN_08BIT, 0xd3}, + {0x60bd, CRL_REG_LEN_08BIT, 0x10}, + {0x60be, CRL_REG_LEN_08BIT, 0x9c}, + {0x60bf, CRL_REG_LEN_08BIT, 0x94}, + {0x60c0, CRL_REG_LEN_08BIT, 0x90}, + {0x60c1, CRL_REG_LEN_08BIT, 0xc8}, + {0x60c2, CRL_REG_LEN_08BIT, 0xba}, + {0x60c3, CRL_REG_LEN_08BIT, 0x7c}, + {0x60c4, CRL_REG_LEN_08BIT, 0x74}, + {0x60c5, CRL_REG_LEN_08BIT, 0x00}, + {0x60c6, CRL_REG_LEN_08BIT, 0x61}, + {0x60c7, CRL_REG_LEN_08BIT, 0x2a}, + {0x60c8, CRL_REG_LEN_08BIT, 0x00}, + {0x60c9, CRL_REG_LEN_08BIT, 0xd2}, + {0x60ca, CRL_REG_LEN_08BIT, 0xcc}, + {0x60cb, CRL_REG_LEN_08BIT, 0xdf}, + {0x60cc, CRL_REG_LEN_08BIT, 0xc4}, + {0x60cd, CRL_REG_LEN_08BIT, 0x35}, + {0x60ce, CRL_REG_LEN_08BIT, 0xd2}, + {0x60cf, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d0, CRL_REG_LEN_08BIT, 0x06}, + {0x60d1, CRL_REG_LEN_08BIT, 0x31}, + {0x60d2, CRL_REG_LEN_08BIT, 0xd2}, + {0x60d3, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d4, CRL_REG_LEN_08BIT, 0x15}, + {0x60d5, CRL_REG_LEN_08BIT, 0xd2}, + {0x60d6, CRL_REG_LEN_08BIT, 0xbb}, + {0x60d7, CRL_REG_LEN_08BIT, 0xcc}, + {0x60d8, CRL_REG_LEN_08BIT, 0x1a}, + {0x60d9, CRL_REG_LEN_08BIT, 0xd2}, + {0x60da, CRL_REG_LEN_08BIT, 0xbe}, + {0x60db, CRL_REG_LEN_08BIT, 0xce}, + {0x60dc, CRL_REG_LEN_08BIT, 0x52}, + {0x60dd, CRL_REG_LEN_08BIT, 0xcf}, + {0x60de, CRL_REG_LEN_08BIT, 0x56}, + {0x60df, CRL_REG_LEN_08BIT, 0xd0}, + {0x60e0, CRL_REG_LEN_08BIT, 0x5b}, + {0x60e1, CRL_REG_LEN_08BIT, 0x2b}, + {0x60e2, CRL_REG_LEN_08BIT, 0xd2}, + {0x60e3, CRL_REG_LEN_08BIT, 0xd3}, + {0x60e4, CRL_REG_LEN_08BIT, 0x01}, + {0x60e5, CRL_REG_LEN_08BIT, 0xcc}, + {0x60e6, CRL_REG_LEN_08BIT, 0x0a}, + {0x60e7, CRL_REG_LEN_08BIT, 0xd2}, + {0x60e8, CRL_REG_LEN_08BIT, 0xd3}, + {0x60e9, CRL_REG_LEN_08BIT, 0x0f}, + {0x60ea, CRL_REG_LEN_08BIT, 0xd9}, + {0x60eb, CRL_REG_LEN_08BIT, 0xc7}, + {0x60ec, CRL_REG_LEN_08BIT, 0xda}, + {0x60ed, CRL_REG_LEN_08BIT, 0xce}, + {0x60ee, CRL_REG_LEN_08BIT, 0x1a}, + {0x60ef, CRL_REG_LEN_08BIT, 0xd4}, + {0x60f0, CRL_REG_LEN_08BIT, 0xf6}, + {0x60f1, CRL_REG_LEN_08BIT, 0xd4}, + {0x60f2, CRL_REG_LEN_08BIT, 0xa9}, + {0x60f3, CRL_REG_LEN_08BIT, 0x27}, + {0x60f4, CRL_REG_LEN_08BIT, 0x00}, + {0x60f5, CRL_REG_LEN_08BIT, 0xd2}, + {0x60f6, CRL_REG_LEN_08BIT, 0xcc}, + {0x60f7, CRL_REG_LEN_08BIT, 0x60}, + {0x60f8, CRL_REG_LEN_08BIT, 0xd2}, + {0x60f9, CRL_REG_LEN_08BIT, 0xd3}, + {0x60fa, CRL_REG_LEN_08BIT, 0x2d}, + {0x60fb, CRL_REG_LEN_08BIT, 0xd9}, + {0x60fc, CRL_REG_LEN_08BIT, 0xdf}, + {0x60fd, CRL_REG_LEN_08BIT, 0xda}, + {0x60fe, CRL_REG_LEN_08BIT, 0xe5}, + {0x60ff, CRL_REG_LEN_08BIT, 0x1a}, + {0x6100, CRL_REG_LEN_08BIT, 0x12}, + {0x6101, CRL_REG_LEN_08BIT, 0xcc}, + {0x6102, CRL_REG_LEN_08BIT, 0x88}, + {0x6103, CRL_REG_LEN_08BIT, 0xd6}, + {0x6104, CRL_REG_LEN_08BIT, 0xb1}, + {0x6105, CRL_REG_LEN_08BIT, 0xb9}, + {0x6106, CRL_REG_LEN_08BIT, 0xba}, + {0x6107, CRL_REG_LEN_08BIT, 0xaf}, + {0x6108, CRL_REG_LEN_08BIT, 0xdc}, + {0x6109, CRL_REG_LEN_08BIT, 0x00}, + {0x610a, CRL_REG_LEN_08BIT, 0xcb}, + {0x610b, CRL_REG_LEN_08BIT, 0xc3}, + {0x610c, CRL_REG_LEN_08BIT, 0xb9}, + {0x610d, CRL_REG_LEN_08BIT, 0xa4}, + {0x610e, CRL_REG_LEN_08BIT, 0xb5}, + {0x610f, CRL_REG_LEN_08BIT, 0x5c}, + {0x6110, CRL_REG_LEN_08BIT, 0x12}, + {0x6111, CRL_REG_LEN_08BIT, 0x2a}, + {0x6112, CRL_REG_LEN_08BIT, 0x61}, + {0x6113, CRL_REG_LEN_08BIT, 0xd2}, + {0x6114, CRL_REG_LEN_08BIT, 0xcc}, + {0x6115, CRL_REG_LEN_08BIT, 0xdf}, + {0x6116, CRL_REG_LEN_08BIT, 0xc7}, + {0x6117, CRL_REG_LEN_08BIT, 0x35}, + {0x6118, CRL_REG_LEN_08BIT, 0xd2}, + {0x6119, CRL_REG_LEN_08BIT, 0xcc}, + {0x611a, CRL_REG_LEN_08BIT, 0x06}, + {0x611b, CRL_REG_LEN_08BIT, 0x31}, + {0x611c, CRL_REG_LEN_08BIT, 0xc6}, + {0x611d, CRL_REG_LEN_08BIT, 0xbb}, + {0x611e, CRL_REG_LEN_08BIT, 0xd2}, + {0x611f, CRL_REG_LEN_08BIT, 0xcc}, + {0x6120, CRL_REG_LEN_08BIT, 0x18}, + {0x6121, CRL_REG_LEN_08BIT, 0xd2}, + {0x6122, CRL_REG_LEN_08BIT, 0xbe}, + {0x6123, CRL_REG_LEN_08BIT, 0xcc}, + {0x6124, CRL_REG_LEN_08BIT, 0x52}, + {0x6125, CRL_REG_LEN_08BIT, 0xc7}, + {0x6126, CRL_REG_LEN_08BIT, 0xd2}, + {0x6127, CRL_REG_LEN_08BIT, 0xcc}, + {0x6128, CRL_REG_LEN_08BIT, 0x0a}, + {0x6129, CRL_REG_LEN_08BIT, 0xb4}, + {0x612a, CRL_REG_LEN_08BIT, 0xb7}, + {0x612b, CRL_REG_LEN_08BIT, 0x94}, + {0x612c, CRL_REG_LEN_08BIT, 0xd2}, + {0x612d, CRL_REG_LEN_08BIT, 0x12}, + {0x612e, CRL_REG_LEN_08BIT, 0x26}, + {0x612f, CRL_REG_LEN_08BIT, 0x42}, + {0x6130, CRL_REG_LEN_08BIT, 0x46}, + {0x6131, CRL_REG_LEN_08BIT, 0x42}, + {0x6132, CRL_REG_LEN_08BIT, 0xd3}, + {0x6133, CRL_REG_LEN_08BIT, 0x20}, + {0x6134, CRL_REG_LEN_08BIT, 0x27}, + {0x6135, CRL_REG_LEN_08BIT, 0x00}, + {0x6136, CRL_REG_LEN_08BIT, 0x1a}, + {0x6137, CRL_REG_LEN_08BIT, 0xcc}, + {0x6138, CRL_REG_LEN_08BIT, 0x88}, + {0x6139, CRL_REG_LEN_08BIT, 0x60}, + {0x613a, CRL_REG_LEN_08BIT, 0x2c}, + {0x613b, CRL_REG_LEN_08BIT, 0x12}, + {0x613c, CRL_REG_LEN_08BIT, 0x40}, + {0x613d, CRL_REG_LEN_08BIT, 0xb8}, + {0x613e, CRL_REG_LEN_08BIT, 0x90}, + {0x613f, CRL_REG_LEN_08BIT, 0xd5}, + {0x6140, CRL_REG_LEN_08BIT, 0xba}, + {0x6141, CRL_REG_LEN_08BIT, 0x00}, + {0x6142, CRL_REG_LEN_08BIT, 0x00}, + {0x6143, CRL_REG_LEN_08BIT, 0x00}, + {0x6144, CRL_REG_LEN_08BIT, 0x00}, + {0x6145, CRL_REG_LEN_08BIT, 0x00}, + {0x6146, CRL_REG_LEN_08BIT, 0x00}, + {0x6147, CRL_REG_LEN_08BIT, 0xaa}, + {0x6148, CRL_REG_LEN_08BIT, 0xb7}, + {0x6149, CRL_REG_LEN_08BIT, 0x00}, + {0x614a, CRL_REG_LEN_08BIT, 0x00}, + {0x614b, CRL_REG_LEN_08BIT, 0x00}, + {0x614c, CRL_REG_LEN_08BIT, 0x00}, + {0x614d, CRL_REG_LEN_08BIT, 0xa6}, + {0x614e, CRL_REG_LEN_08BIT, 0xb7}, + {0x614f, CRL_REG_LEN_08BIT, 0x00}, + {0x6150, CRL_REG_LEN_08BIT, 0xd5}, + {0x6151, CRL_REG_LEN_08BIT, 0x00}, + {0x6152, CRL_REG_LEN_08BIT, 0x71}, + {0x6153, CRL_REG_LEN_08BIT, 0xd3}, + {0x6154, CRL_REG_LEN_08BIT, 0x30}, + {0x6155, CRL_REG_LEN_08BIT, 0xba}, + {0x6156, CRL_REG_LEN_08BIT, 0x00}, + {0x6157, CRL_REG_LEN_08BIT, 0x00}, + {0x6158, CRL_REG_LEN_08BIT, 0x00}, + {0x6159, CRL_REG_LEN_08BIT, 0x00}, + {0x615a, CRL_REG_LEN_08BIT, 0xd3}, + {0x615b, CRL_REG_LEN_08BIT, 0x10}, + {0x615c, CRL_REG_LEN_08BIT, 0x70}, + {0x615d, CRL_REG_LEN_08BIT, 0x00}, + {0x615e, CRL_REG_LEN_08BIT, 0x00}, + {0x615f, CRL_REG_LEN_08BIT, 0x00}, + {0x6160, CRL_REG_LEN_08BIT, 0x00}, + {0x6161, CRL_REG_LEN_08BIT, 0xd5}, + {0x6162, CRL_REG_LEN_08BIT, 0xba}, + {0x6163, CRL_REG_LEN_08BIT, 0xb0}, + {0x6164, CRL_REG_LEN_08BIT, 0xb7}, + {0x6165, CRL_REG_LEN_08BIT, 0x00}, + {0x6166, CRL_REG_LEN_08BIT, 0x9d}, + {0x6167, CRL_REG_LEN_08BIT, 0xd3}, + {0x6168, CRL_REG_LEN_08BIT, 0x0a}, + {0x6169, CRL_REG_LEN_08BIT, 0x9d}, + {0x616a, CRL_REG_LEN_08BIT, 0x9d}, + {0x616b, CRL_REG_LEN_08BIT, 0xd3}, + {0x616c, CRL_REG_LEN_08BIT, 0x10}, + {0x616d, CRL_REG_LEN_08BIT, 0x9c}, + {0x616e, CRL_REG_LEN_08BIT, 0x94}, + {0x616f, CRL_REG_LEN_08BIT, 0x90}, + {0x6170, CRL_REG_LEN_08BIT, 0xc8}, + {0x6171, CRL_REG_LEN_08BIT, 0xba}, + {0x6172, CRL_REG_LEN_08BIT, 0xd2}, + {0x6173, CRL_REG_LEN_08BIT, 0x60}, + {0x6174, CRL_REG_LEN_08BIT, 0x2c}, + {0x6175, CRL_REG_LEN_08BIT, 0x50}, + {0x6176, CRL_REG_LEN_08BIT, 0x11}, + {0x6177, CRL_REG_LEN_08BIT, 0xcc}, + {0x6178, CRL_REG_LEN_08BIT, 0x00}, + {0x6179, CRL_REG_LEN_08BIT, 0x30}, + {0x617a, CRL_REG_LEN_08BIT, 0xd5}, + {0x617b, CRL_REG_LEN_08BIT, 0x00}, + {0x617c, CRL_REG_LEN_08BIT, 0xba}, + {0x617d, CRL_REG_LEN_08BIT, 0xb0}, + {0x617e, CRL_REG_LEN_08BIT, 0xb7}, + {0x617f, CRL_REG_LEN_08BIT, 0x00}, + {0x6180, CRL_REG_LEN_08BIT, 0x9d}, + {0x6181, CRL_REG_LEN_08BIT, 0xd3}, + {0x6182, CRL_REG_LEN_08BIT, 0x0a}, + {0x6183, CRL_REG_LEN_08BIT, 0x9d}, + {0x6184, CRL_REG_LEN_08BIT, 0x9d}, + {0x6185, CRL_REG_LEN_08BIT, 0xd3}, + {0x6186, CRL_REG_LEN_08BIT, 0x10}, + {0x6187, CRL_REG_LEN_08BIT, 0x9c}, + {0x6188, CRL_REG_LEN_08BIT, 0x94}, + {0x6189, CRL_REG_LEN_08BIT, 0x90}, + {0x618a, CRL_REG_LEN_08BIT, 0xc8}, + {0x618b, CRL_REG_LEN_08BIT, 0xba}, + {0x618c, CRL_REG_LEN_08BIT, 0xd5}, + {0x618d, CRL_REG_LEN_08BIT, 0x00}, + {0x618e, CRL_REG_LEN_08BIT, 0x01}, + {0x618f, CRL_REG_LEN_08BIT, 0x1a}, + {0x6190, CRL_REG_LEN_08BIT, 0xcc}, + {0x6191, CRL_REG_LEN_08BIT, 0x12}, + {0x6192, CRL_REG_LEN_08BIT, 0x12}, + {0x6193, CRL_REG_LEN_08BIT, 0x00}, + {0x6194, CRL_REG_LEN_08BIT, 0xcc}, + {0x6195, CRL_REG_LEN_08BIT, 0x9c}, + {0x6196, CRL_REG_LEN_08BIT, 0xd2}, + {0x6197, CRL_REG_LEN_08BIT, 0xcc}, + {0x6198, CRL_REG_LEN_08BIT, 0x60}, + {0x6199, CRL_REG_LEN_08BIT, 0xd2}, + {0x619a, CRL_REG_LEN_08BIT, 0x04}, + {0x619b, CRL_REG_LEN_08BIT, 0xd5}, + {0x619c, CRL_REG_LEN_08BIT, 0x1a}, + {0x619d, CRL_REG_LEN_08BIT, 0xcc}, + {0x619e, CRL_REG_LEN_08BIT, 0x12}, + {0x619f, CRL_REG_LEN_08BIT, 0x00}, + {0x61a0, CRL_REG_LEN_08BIT, 0x12}, + {0x61a1, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a2, CRL_REG_LEN_08BIT, 0x9c}, + {0x61a3, CRL_REG_LEN_08BIT, 0xd2}, + {0x61a4, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a5, CRL_REG_LEN_08BIT, 0x60}, + {0x61a6, CRL_REG_LEN_08BIT, 0xd2}, + {0x61a7, CRL_REG_LEN_08BIT, 0x1a}, + {0x61a8, CRL_REG_LEN_08BIT, 0xcc}, + {0x61a9, CRL_REG_LEN_08BIT, 0x12}, + {0x61aa, CRL_REG_LEN_08BIT, 0x00}, + {0x61ab, CRL_REG_LEN_08BIT, 0x12}, + {0x61ac, CRL_REG_LEN_08BIT, 0xcc}, + {0x61ad, CRL_REG_LEN_08BIT, 0x9c}, + {0x61ae, CRL_REG_LEN_08BIT, 0xd2}, + {0x61af, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b0, CRL_REG_LEN_08BIT, 0x60}, + {0x61b1, CRL_REG_LEN_08BIT, 0xd2}, + {0x61b2, CRL_REG_LEN_08BIT, 0x1a}, + {0x61b3, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b4, CRL_REG_LEN_08BIT, 0x12}, + {0x61b5, CRL_REG_LEN_08BIT, 0x00}, + {0x61b6, CRL_REG_LEN_08BIT, 0x12}, + {0x61b7, CRL_REG_LEN_08BIT, 0xcc}, + {0x61b8, CRL_REG_LEN_08BIT, 0x9c}, + {0x61b9, CRL_REG_LEN_08BIT, 0xd2}, + {0x61ba, CRL_REG_LEN_08BIT, 0xcc}, + {0x61bb, CRL_REG_LEN_08BIT, 0x60}, + {0x61bc, CRL_REG_LEN_08BIT, 0xd2}, + {0x61bd, CRL_REG_LEN_08BIT, 0xd5}, + {0x61be, CRL_REG_LEN_08BIT, 0x1a}, + {0x61bf, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c0, CRL_REG_LEN_08BIT, 0x12}, + {0x61c1, CRL_REG_LEN_08BIT, 0x12}, + {0x61c2, CRL_REG_LEN_08BIT, 0x00}, + {0x61c3, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c4, CRL_REG_LEN_08BIT, 0x8a}, + {0x61c5, CRL_REG_LEN_08BIT, 0xd2}, + {0x61c6, CRL_REG_LEN_08BIT, 0xcc}, + {0x61c7, CRL_REG_LEN_08BIT, 0x74}, + {0x61c8, CRL_REG_LEN_08BIT, 0xd2}, + {0x61c9, CRL_REG_LEN_08BIT, 0xd5}, + {0x61ca, CRL_REG_LEN_08BIT, 0x1a}, + {0x61cb, CRL_REG_LEN_08BIT, 0xcc}, + {0x61cc, CRL_REG_LEN_08BIT, 0x12}, + {0x61cd, CRL_REG_LEN_08BIT, 0x00}, + {0x61ce, CRL_REG_LEN_08BIT, 0x12}, + {0x61cf, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d0, CRL_REG_LEN_08BIT, 0x8a}, + {0x61d1, CRL_REG_LEN_08BIT, 0xd2}, + {0x61d2, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d3, CRL_REG_LEN_08BIT, 0x74}, + {0x61d4, CRL_REG_LEN_08BIT, 0xd2}, + {0x61d5, CRL_REG_LEN_08BIT, 0x1a}, + {0x61d6, CRL_REG_LEN_08BIT, 0xcc}, + {0x61d7, CRL_REG_LEN_08BIT, 0x12}, + {0x61d8, CRL_REG_LEN_08BIT, 0x00}, + {0x61d9, CRL_REG_LEN_08BIT, 0x12}, + {0x61da, CRL_REG_LEN_08BIT, 0xcc}, + {0x61db, CRL_REG_LEN_08BIT, 0x8a}, + {0x61dc, CRL_REG_LEN_08BIT, 0xd2}, + {0x61dd, CRL_REG_LEN_08BIT, 0xcc}, + {0x61de, CRL_REG_LEN_08BIT, 0x74}, + {0x61df, CRL_REG_LEN_08BIT, 0xd2}, + {0x61e0, CRL_REG_LEN_08BIT, 0x1a}, + {0x61e1, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e2, CRL_REG_LEN_08BIT, 0x12}, + {0x61e3, CRL_REG_LEN_08BIT, 0x00}, + {0x61e4, CRL_REG_LEN_08BIT, 0x12}, + {0x61e5, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e6, CRL_REG_LEN_08BIT, 0x8a}, + {0x61e7, CRL_REG_LEN_08BIT, 0xd2}, + {0x61e8, CRL_REG_LEN_08BIT, 0xcc}, + {0x61e9, CRL_REG_LEN_08BIT, 0x74}, + {0x61ea, CRL_REG_LEN_08BIT, 0xd2}, + {0x61eb, CRL_REG_LEN_08BIT, 0xd5}, + {0x61ec, CRL_REG_LEN_08BIT, 0xcc}, + {0x61ed, CRL_REG_LEN_08BIT, 0x12}, + {0x61ee, CRL_REG_LEN_08BIT, 0x00}, + {0x61ef, CRL_REG_LEN_08BIT, 0x12}, + {0x61f0, CRL_REG_LEN_08BIT, 0xcc}, + {0x61f1, CRL_REG_LEN_08BIT, 0x9c}, + {0x61f2, CRL_REG_LEN_08BIT, 0xd5}, + {0x6400, CRL_REG_LEN_08BIT, 0x04}, + {0x6401, CRL_REG_LEN_08BIT, 0x04}, + {0x6402, CRL_REG_LEN_08BIT, 0x00}, + {0x6403, CRL_REG_LEN_08BIT, 0xff}, + {0x6404, CRL_REG_LEN_08BIT, 0x00}, + {0x6405, CRL_REG_LEN_08BIT, 0x08}, + {0x6406, CRL_REG_LEN_08BIT, 0x00}, + {0x6407, CRL_REG_LEN_08BIT, 0xff}, + {0x6408, CRL_REG_LEN_08BIT, 0x04}, + {0x6409, CRL_REG_LEN_08BIT, 0x70}, + {0x640a, CRL_REG_LEN_08BIT, 0x00}, + {0x640b, CRL_REG_LEN_08BIT, 0xff}, + {0x640c, CRL_REG_LEN_08BIT, 0x05}, + {0x640d, CRL_REG_LEN_08BIT, 0x14}, + {0x640e, CRL_REG_LEN_08BIT, 0x04}, + {0x640f, CRL_REG_LEN_08BIT, 0x71}, + {0x6410, CRL_REG_LEN_08BIT, 0x05}, + {0x6411, CRL_REG_LEN_08BIT, 0x74}, + {0x6412, CRL_REG_LEN_08BIT, 0x00}, + {0x6413, CRL_REG_LEN_08BIT, 0xff}, + {0x6414, CRL_REG_LEN_08BIT, 0x05}, + {0x6415, CRL_REG_LEN_08BIT, 0x54}, + {0x6416, CRL_REG_LEN_08BIT, 0x05}, + {0x6417, CRL_REG_LEN_08BIT, 0x44}, + {0x6418, CRL_REG_LEN_08BIT, 0x04}, + {0x6419, CRL_REG_LEN_08BIT, 0x30}, + {0x641a, CRL_REG_LEN_08BIT, 0x05}, + {0x641b, CRL_REG_LEN_08BIT, 0x46}, + {0x641c, CRL_REG_LEN_08BIT, 0x00}, + {0x641d, CRL_REG_LEN_08BIT, 0xff}, + {0x641e, CRL_REG_LEN_08BIT, 0x04}, + {0x641f, CRL_REG_LEN_08BIT, 0x31}, + {0x6420, CRL_REG_LEN_08BIT, 0x04}, + {0x6421, CRL_REG_LEN_08BIT, 0x30}, + {0x6422, CRL_REG_LEN_08BIT, 0x00}, + {0x6423, CRL_REG_LEN_08BIT, 0xff}, + {0x6424, CRL_REG_LEN_08BIT, 0x04}, + {0x6425, CRL_REG_LEN_08BIT, 0x20}, + {0x6426, CRL_REG_LEN_08BIT, 0x05}, + {0x6427, CRL_REG_LEN_08BIT, 0x06}, + {0x6428, CRL_REG_LEN_08BIT, 0x00}, + {0x6429, CRL_REG_LEN_08BIT, 0xff}, + {0x642a, CRL_REG_LEN_08BIT, 0x08}, + {0x642b, CRL_REG_LEN_08BIT, 0x2a}, + {0x642c, CRL_REG_LEN_08BIT, 0x08}, + {0x642d, CRL_REG_LEN_08BIT, 0x31}, + {0x642e, CRL_REG_LEN_08BIT, 0x00}, + {0x642f, CRL_REG_LEN_08BIT, 0xff}, + {0x6430, CRL_REG_LEN_08BIT, 0x08}, + {0x6431, CRL_REG_LEN_08BIT, 0x2a}, + {0x6432, CRL_REG_LEN_08BIT, 0x08}, + {0x6433, CRL_REG_LEN_08BIT, 0x31}, + {0x6434, CRL_REG_LEN_08BIT, 0x06}, + {0x6435, CRL_REG_LEN_08BIT, 0x20}, + {0x6436, CRL_REG_LEN_08BIT, 0x07}, + {0x6437, CRL_REG_LEN_08BIT, 0x00}, + {0x6438, CRL_REG_LEN_08BIT, 0x08}, + {0x6439, CRL_REG_LEN_08BIT, 0x40}, + {0x643a, CRL_REG_LEN_08BIT, 0x00}, + {0x643b, CRL_REG_LEN_08BIT, 0xff}, + {0x643c, CRL_REG_LEN_08BIT, 0x08}, + {0x643d, CRL_REG_LEN_08BIT, 0x2a}, + {0x643e, CRL_REG_LEN_08BIT, 0x08}, + {0x643f, CRL_REG_LEN_08BIT, 0x36}, + {0x6440, CRL_REG_LEN_08BIT, 0x06}, + {0x6441, CRL_REG_LEN_08BIT, 0x10}, + {0x6442, CRL_REG_LEN_08BIT, 0x07}, + {0x6443, CRL_REG_LEN_08BIT, 0x00}, + {0x6444, CRL_REG_LEN_08BIT, 0x08}, + {0x6445, CRL_REG_LEN_08BIT, 0x40}, + {0x6446, CRL_REG_LEN_08BIT, 0x00}, + {0x6447, CRL_REG_LEN_08BIT, 0xff}, + {0x6448, CRL_REG_LEN_08BIT, 0x08}, + {0x6449, CRL_REG_LEN_08BIT, 0x2a}, + {0x644a, CRL_REG_LEN_08BIT, 0x08}, + {0x644b, CRL_REG_LEN_08BIT, 0x3b}, + {0x644c, CRL_REG_LEN_08BIT, 0x06}, + {0x644d, CRL_REG_LEN_08BIT, 0x00}, + {0x644e, CRL_REG_LEN_08BIT, 0x07}, + {0x644f, CRL_REG_LEN_08BIT, 0x00}, + {0x6450, CRL_REG_LEN_08BIT, 0x08}, + {0x6451, CRL_REG_LEN_08BIT, 0x40}, + {0x6452, CRL_REG_LEN_08BIT, 0x00}, + {0x6453, CRL_REG_LEN_08BIT, 0xff}, + {0x6454, CRL_REG_LEN_08BIT, 0x06}, + {0x6455, CRL_REG_LEN_08BIT, 0x00}, + {0x6456, CRL_REG_LEN_08BIT, 0x07}, + {0x6457, CRL_REG_LEN_08BIT, 0x05}, + {0x6458, CRL_REG_LEN_08BIT, 0x01}, + {0x6459, CRL_REG_LEN_08BIT, 0xaf}, + {0x645a, CRL_REG_LEN_08BIT, 0x01}, + {0x645b, CRL_REG_LEN_08BIT, 0x0f}, + {0x645c, CRL_REG_LEN_08BIT, 0x01}, + {0x645d, CRL_REG_LEN_08BIT, 0x90}, + {0x645e, CRL_REG_LEN_08BIT, 0x01}, + {0x645f, CRL_REG_LEN_08BIT, 0xc8}, + {0x6460, CRL_REG_LEN_08BIT, 0x00}, + {0x6461, CRL_REG_LEN_08BIT, 0xff}, + {0x6462, CRL_REG_LEN_08BIT, 0x01}, + {0x6463, CRL_REG_LEN_08BIT, 0xac}, + {0x6464, CRL_REG_LEN_08BIT, 0x01}, + {0x6465, CRL_REG_LEN_08BIT, 0x0c}, + {0x6466, CRL_REG_LEN_08BIT, 0x01}, + {0x6467, CRL_REG_LEN_08BIT, 0x90}, + {0x6468, CRL_REG_LEN_08BIT, 0x01}, + {0x6469, CRL_REG_LEN_08BIT, 0xe8}, + {0x646a, CRL_REG_LEN_08BIT, 0x00}, + {0x646b, CRL_REG_LEN_08BIT, 0xff}, + {0x646c, CRL_REG_LEN_08BIT, 0x01}, + {0x646d, CRL_REG_LEN_08BIT, 0xad}, + {0x646e, CRL_REG_LEN_08BIT, 0x01}, + {0x646f, CRL_REG_LEN_08BIT, 0x0d}, + {0x6470, CRL_REG_LEN_08BIT, 0x01}, + {0x6471, CRL_REG_LEN_08BIT, 0x90}, + {0x6472, CRL_REG_LEN_08BIT, 0x01}, + {0x6473, CRL_REG_LEN_08BIT, 0xe8}, + {0x6474, CRL_REG_LEN_08BIT, 0x00}, + {0x6475, CRL_REG_LEN_08BIT, 0xff}, + {0x6476, CRL_REG_LEN_08BIT, 0x01}, + {0x6477, CRL_REG_LEN_08BIT, 0xae}, + {0x6478, CRL_REG_LEN_08BIT, 0x01}, + {0x6479, CRL_REG_LEN_08BIT, 0x0e}, + {0x647a, CRL_REG_LEN_08BIT, 0x01}, + {0x647b, CRL_REG_LEN_08BIT, 0x90}, + {0x647c, CRL_REG_LEN_08BIT, 0x01}, + {0x647d, CRL_REG_LEN_08BIT, 0xe8}, + {0x647e, CRL_REG_LEN_08BIT, 0x00}, + {0x647f, CRL_REG_LEN_08BIT, 0xff}, + {0x6480, CRL_REG_LEN_08BIT, 0x01}, + {0x6481, CRL_REG_LEN_08BIT, 0xb0}, + {0x6482, CRL_REG_LEN_08BIT, 0x01}, + {0x6483, CRL_REG_LEN_08BIT, 0xb1}, + {0x6484, CRL_REG_LEN_08BIT, 0x01}, + {0x6485, CRL_REG_LEN_08BIT, 0xb2}, + {0x6486, CRL_REG_LEN_08BIT, 0x01}, + {0x6487, CRL_REG_LEN_08BIT, 0xb3}, + {0x6488, CRL_REG_LEN_08BIT, 0x01}, + {0x6489, CRL_REG_LEN_08BIT, 0xb4}, + {0x648a, CRL_REG_LEN_08BIT, 0x01}, + {0x648b, CRL_REG_LEN_08BIT, 0xb5}, + {0x648c, CRL_REG_LEN_08BIT, 0x01}, + {0x648d, CRL_REG_LEN_08BIT, 0xb6}, + {0x648e, CRL_REG_LEN_08BIT, 0x01}, + {0x648f, CRL_REG_LEN_08BIT, 0xb7}, + {0x6490, CRL_REG_LEN_08BIT, 0x01}, + {0x6491, CRL_REG_LEN_08BIT, 0xb8}, + {0x6492, CRL_REG_LEN_08BIT, 0x01}, + {0x6493, CRL_REG_LEN_08BIT, 0xb9}, + {0x6494, CRL_REG_LEN_08BIT, 0x01}, + {0x6495, CRL_REG_LEN_08BIT, 0xba}, + {0x6496, CRL_REG_LEN_08BIT, 0x01}, + {0x6497, CRL_REG_LEN_08BIT, 0xbb}, + {0x6498, CRL_REG_LEN_08BIT, 0x01}, + {0x6499, CRL_REG_LEN_08BIT, 0xbc}, + {0x649a, CRL_REG_LEN_08BIT, 0x01}, + {0x649b, CRL_REG_LEN_08BIT, 0xbd}, + {0x649c, CRL_REG_LEN_08BIT, 0x01}, + {0x649d, CRL_REG_LEN_08BIT, 0xbe}, + {0x649e, CRL_REG_LEN_08BIT, 0x01}, + {0x649f, CRL_REG_LEN_08BIT, 0xbf}, + {0x64a0, CRL_REG_LEN_08BIT, 0x01}, + {0x64a1, CRL_REG_LEN_08BIT, 0xc0}, + {0x64a2, CRL_REG_LEN_08BIT, 0x00}, + {0x64a3, CRL_REG_LEN_08BIT, 0xff}, + {0x64a4, CRL_REG_LEN_08BIT, 0x06}, + {0x64a5, CRL_REG_LEN_08BIT, 0x00}, + {0x64a6, CRL_REG_LEN_08BIT, 0x01}, + {0x64a7, CRL_REG_LEN_08BIT, 0xf6}, + {0x64a8, CRL_REG_LEN_08BIT, 0x04}, + {0x64a9, CRL_REG_LEN_08BIT, 0x30}, + {0x64aa, CRL_REG_LEN_08BIT, 0x00}, + {0x64ab, CRL_REG_LEN_08BIT, 0xff}, + {0x64ac, CRL_REG_LEN_08BIT, 0x06}, + {0x64ad, CRL_REG_LEN_08BIT, 0x10}, + {0x64ae, CRL_REG_LEN_08BIT, 0x01}, + {0x64af, CRL_REG_LEN_08BIT, 0xf6}, + {0x64b0, CRL_REG_LEN_08BIT, 0x04}, + {0x64b1, CRL_REG_LEN_08BIT, 0x30}, + {0x64b2, CRL_REG_LEN_08BIT, 0x06}, + {0x64b3, CRL_REG_LEN_08BIT, 0x00}, + {0x64b4, CRL_REG_LEN_08BIT, 0x00}, + {0x64b5, CRL_REG_LEN_08BIT, 0xff}, + {0x64b6, CRL_REG_LEN_08BIT, 0x06}, + {0x64b7, CRL_REG_LEN_08BIT, 0x20}, + {0x64b8, CRL_REG_LEN_08BIT, 0x01}, + {0x64b9, CRL_REG_LEN_08BIT, 0xf6}, + {0x64ba, CRL_REG_LEN_08BIT, 0x04}, + {0x64bb, CRL_REG_LEN_08BIT, 0x30}, + {0x64bc, CRL_REG_LEN_08BIT, 0x06}, + {0x64bd, CRL_REG_LEN_08BIT, 0x00}, + {0x64be, CRL_REG_LEN_08BIT, 0x00}, + {0x64bf, CRL_REG_LEN_08BIT, 0xff}, + {0x64c0, CRL_REG_LEN_08BIT, 0x04}, + {0x64c1, CRL_REG_LEN_08BIT, 0x31}, + {0x64c2, CRL_REG_LEN_08BIT, 0x04}, + {0x64c3, CRL_REG_LEN_08BIT, 0x30}, + {0x64c4, CRL_REG_LEN_08BIT, 0x01}, + {0x64c5, CRL_REG_LEN_08BIT, 0x20}, + {0x64c6, CRL_REG_LEN_08BIT, 0x01}, + {0x64c7, CRL_REG_LEN_08BIT, 0x31}, + {0x64c8, CRL_REG_LEN_08BIT, 0x01}, + {0x64c9, CRL_REG_LEN_08BIT, 0x32}, + {0x64ca, CRL_REG_LEN_08BIT, 0x01}, + {0x64cb, CRL_REG_LEN_08BIT, 0x33}, + {0x64cc, CRL_REG_LEN_08BIT, 0x01}, + {0x64cd, CRL_REG_LEN_08BIT, 0x34}, + {0x64ce, CRL_REG_LEN_08BIT, 0x01}, + {0x64cf, CRL_REG_LEN_08BIT, 0x35}, + {0x64d0, CRL_REG_LEN_08BIT, 0x01}, + {0x64d1, CRL_REG_LEN_08BIT, 0x36}, + {0x64d2, CRL_REG_LEN_08BIT, 0x01}, + {0x64d3, CRL_REG_LEN_08BIT, 0x37}, + {0x64d4, CRL_REG_LEN_08BIT, 0x01}, + {0x64d5, CRL_REG_LEN_08BIT, 0x38}, + {0x64d6, CRL_REG_LEN_08BIT, 0x01}, + {0x64d7, CRL_REG_LEN_08BIT, 0x39}, + {0x64d8, CRL_REG_LEN_08BIT, 0x01}, + {0x64d9, CRL_REG_LEN_08BIT, 0x3a}, + {0x64da, CRL_REG_LEN_08BIT, 0x01}, + {0x64db, CRL_REG_LEN_08BIT, 0x3b}, + {0x64dc, CRL_REG_LEN_08BIT, 0x01}, + {0x64dd, CRL_REG_LEN_08BIT, 0x3c}, + {0x64de, CRL_REG_LEN_08BIT, 0x01}, + {0x64df, CRL_REG_LEN_08BIT, 0x3d}, + {0x64e0, CRL_REG_LEN_08BIT, 0x01}, + {0x64e1, CRL_REG_LEN_08BIT, 0x3e}, + {0x64e2, CRL_REG_LEN_08BIT, 0x01}, + {0x64e3, CRL_REG_LEN_08BIT, 0x3f}, + {0x64e4, CRL_REG_LEN_08BIT, 0x02}, + {0x64e5, CRL_REG_LEN_08BIT, 0xa0}, + {0x64e6, CRL_REG_LEN_08BIT, 0x00}, + {0x64e7, CRL_REG_LEN_08BIT, 0xff}, + {0x64e8, CRL_REG_LEN_08BIT, 0x04}, + {0x64e9, CRL_REG_LEN_08BIT, 0x31}, + {0x64ea, CRL_REG_LEN_08BIT, 0x04}, + {0x64eb, CRL_REG_LEN_08BIT, 0x30}, + {0x64ec, CRL_REG_LEN_08BIT, 0x01}, + {0x64ed, CRL_REG_LEN_08BIT, 0x00}, + {0x64ee, CRL_REG_LEN_08BIT, 0x01}, + {0x64ef, CRL_REG_LEN_08BIT, 0x11}, + {0x64f0, CRL_REG_LEN_08BIT, 0x01}, + {0x64f1, CRL_REG_LEN_08BIT, 0x12}, + {0x64f2, CRL_REG_LEN_08BIT, 0x01}, + {0x64f3, CRL_REG_LEN_08BIT, 0x13}, + {0x64f4, CRL_REG_LEN_08BIT, 0x01}, + {0x64f5, CRL_REG_LEN_08BIT, 0x14}, + {0x64f6, CRL_REG_LEN_08BIT, 0x01}, + {0x64f7, CRL_REG_LEN_08BIT, 0x15}, + {0x64f8, CRL_REG_LEN_08BIT, 0x01}, + {0x64f9, CRL_REG_LEN_08BIT, 0x16}, + {0x64fa, CRL_REG_LEN_08BIT, 0x01}, + {0x64fb, CRL_REG_LEN_08BIT, 0x17}, + {0x64fc, CRL_REG_LEN_08BIT, 0x01}, + {0x64fd, CRL_REG_LEN_08BIT, 0x18}, + {0x64fe, CRL_REG_LEN_08BIT, 0x01}, + {0x64ff, CRL_REG_LEN_08BIT, 0x19}, + {0x6500, CRL_REG_LEN_08BIT, 0x01}, + {0x6501, CRL_REG_LEN_08BIT, 0x1a}, + {0x6502, CRL_REG_LEN_08BIT, 0x01}, + {0x6503, CRL_REG_LEN_08BIT, 0x1b}, + {0x6504, CRL_REG_LEN_08BIT, 0x01}, + {0x6505, CRL_REG_LEN_08BIT, 0x1c}, + {0x6506, CRL_REG_LEN_08BIT, 0x01}, + {0x6507, CRL_REG_LEN_08BIT, 0x1d}, + {0x6508, CRL_REG_LEN_08BIT, 0x01}, + {0x6509, CRL_REG_LEN_08BIT, 0x1e}, + {0x650a, CRL_REG_LEN_08BIT, 0x01}, + {0x650b, CRL_REG_LEN_08BIT, 0x1f}, + {0x650c, CRL_REG_LEN_08BIT, 0x02}, + {0x650d, CRL_REG_LEN_08BIT, 0xa0}, + {0x650e, CRL_REG_LEN_08BIT, 0x00}, + {0x650f, CRL_REG_LEN_08BIT, 0xff}, + {0x6510, CRL_REG_LEN_08BIT, 0x04}, + {0x6511, CRL_REG_LEN_08BIT, 0x20}, + {0x6512, CRL_REG_LEN_08BIT, 0x05}, + {0x6513, CRL_REG_LEN_08BIT, 0x86}, + {0x6514, CRL_REG_LEN_08BIT, 0x03}, + {0x6515, CRL_REG_LEN_08BIT, 0x0b}, + {0x6516, CRL_REG_LEN_08BIT, 0x05}, + {0x6517, CRL_REG_LEN_08BIT, 0x86}, + {0x6518, CRL_REG_LEN_08BIT, 0x00}, + {0x6519, CRL_REG_LEN_08BIT, 0x00}, + {0x651a, CRL_REG_LEN_08BIT, 0x05}, + {0x651b, CRL_REG_LEN_08BIT, 0x06}, + {0x651c, CRL_REG_LEN_08BIT, 0x00}, + {0x651d, CRL_REG_LEN_08BIT, 0x04}, + {0x651e, CRL_REG_LEN_08BIT, 0x05}, + {0x651f, CRL_REG_LEN_08BIT, 0x04}, + {0x6520, CRL_REG_LEN_08BIT, 0x00}, + {0x6521, CRL_REG_LEN_08BIT, 0x04}, + {0x6522, CRL_REG_LEN_08BIT, 0x05}, + {0x6523, CRL_REG_LEN_08BIT, 0x00}, + {0x6524, CRL_REG_LEN_08BIT, 0x05}, + {0x6525, CRL_REG_LEN_08BIT, 0x0a}, + {0x6526, CRL_REG_LEN_08BIT, 0x03}, + {0x6527, CRL_REG_LEN_08BIT, 0x9a}, + {0x6528, CRL_REG_LEN_08BIT, 0x05}, + {0x6529, CRL_REG_LEN_08BIT, 0x86}, + {0x652a, CRL_REG_LEN_08BIT, 0x00}, + {0x652b, CRL_REG_LEN_08BIT, 0x00}, + {0x652c, CRL_REG_LEN_08BIT, 0x05}, + {0x652d, CRL_REG_LEN_08BIT, 0x06}, + {0x652e, CRL_REG_LEN_08BIT, 0x00}, + {0x652f, CRL_REG_LEN_08BIT, 0x01}, + {0x6530, CRL_REG_LEN_08BIT, 0x05}, + {0x6531, CRL_REG_LEN_08BIT, 0x04}, + {0x6532, CRL_REG_LEN_08BIT, 0x00}, + {0x6533, CRL_REG_LEN_08BIT, 0x04}, + {0x6534, CRL_REG_LEN_08BIT, 0x05}, + {0x6535, CRL_REG_LEN_08BIT, 0x00}, + {0x6536, CRL_REG_LEN_08BIT, 0x05}, + {0x6537, CRL_REG_LEN_08BIT, 0x0a}, + {0x6538, CRL_REG_LEN_08BIT, 0x03}, + {0x6539, CRL_REG_LEN_08BIT, 0x99}, + {0x653a, CRL_REG_LEN_08BIT, 0x05}, + {0x653b, CRL_REG_LEN_08BIT, 0x06}, + {0x653c, CRL_REG_LEN_08BIT, 0x00}, + {0x653d, CRL_REG_LEN_08BIT, 0x00}, + {0x653e, CRL_REG_LEN_08BIT, 0x05}, + {0x653f, CRL_REG_LEN_08BIT, 0x04}, + {0x6540, CRL_REG_LEN_08BIT, 0x00}, + {0x6541, CRL_REG_LEN_08BIT, 0x04}, + {0x6542, CRL_REG_LEN_08BIT, 0x05}, + {0x6543, CRL_REG_LEN_08BIT, 0x00}, + {0x6544, CRL_REG_LEN_08BIT, 0x05}, + {0x6545, CRL_REG_LEN_08BIT, 0x0a}, + {0x6546, CRL_REG_LEN_08BIT, 0x03}, + {0x6547, CRL_REG_LEN_08BIT, 0x98}, + {0x6548, CRL_REG_LEN_08BIT, 0x05}, + {0x6549, CRL_REG_LEN_08BIT, 0x06}, + {0x654a, CRL_REG_LEN_08BIT, 0x00}, + {0x654b, CRL_REG_LEN_08BIT, 0x00}, + {0x654c, CRL_REG_LEN_08BIT, 0x05}, + {0x654d, CRL_REG_LEN_08BIT, 0x04}, + {0x654e, CRL_REG_LEN_08BIT, 0x00}, + {0x654f, CRL_REG_LEN_08BIT, 0x04}, + {0x6550, CRL_REG_LEN_08BIT, 0x05}, + {0x6551, CRL_REG_LEN_08BIT, 0x00}, + {0x6552, CRL_REG_LEN_08BIT, 0x05}, + {0x6553, CRL_REG_LEN_08BIT, 0x0a}, + {0x6554, CRL_REG_LEN_08BIT, 0x03}, + {0x6555, CRL_REG_LEN_08BIT, 0x97}, + {0x6556, CRL_REG_LEN_08BIT, 0x05}, + {0x6557, CRL_REG_LEN_08BIT, 0x06}, + {0x6558, CRL_REG_LEN_08BIT, 0x05}, + {0x6559, CRL_REG_LEN_08BIT, 0x04}, + {0x655a, CRL_REG_LEN_08BIT, 0x00}, + {0x655b, CRL_REG_LEN_08BIT, 0x04}, + {0x655c, CRL_REG_LEN_08BIT, 0x05}, + {0x655d, CRL_REG_LEN_08BIT, 0x00}, + {0x655e, CRL_REG_LEN_08BIT, 0x05}, + {0x655f, CRL_REG_LEN_08BIT, 0x0a}, + {0x6560, CRL_REG_LEN_08BIT, 0x03}, + {0x6561, CRL_REG_LEN_08BIT, 0x96}, + {0x6562, CRL_REG_LEN_08BIT, 0x05}, + {0x6563, CRL_REG_LEN_08BIT, 0x06}, + {0x6564, CRL_REG_LEN_08BIT, 0x05}, + {0x6565, CRL_REG_LEN_08BIT, 0x04}, + {0x6566, CRL_REG_LEN_08BIT, 0x00}, + {0x6567, CRL_REG_LEN_08BIT, 0x04}, + {0x6568, CRL_REG_LEN_08BIT, 0x05}, + {0x6569, CRL_REG_LEN_08BIT, 0x00}, + {0x656a, CRL_REG_LEN_08BIT, 0x05}, + {0x656b, CRL_REG_LEN_08BIT, 0x0a}, + {0x656c, CRL_REG_LEN_08BIT, 0x03}, + {0x656d, CRL_REG_LEN_08BIT, 0x95}, + {0x656e, CRL_REG_LEN_08BIT, 0x05}, + {0x656f, CRL_REG_LEN_08BIT, 0x06}, + {0x6570, CRL_REG_LEN_08BIT, 0x05}, + {0x6571, CRL_REG_LEN_08BIT, 0x04}, + {0x6572, CRL_REG_LEN_08BIT, 0x00}, + {0x6573, CRL_REG_LEN_08BIT, 0x04}, + {0x6574, CRL_REG_LEN_08BIT, 0x05}, + {0x6575, CRL_REG_LEN_08BIT, 0x00}, + {0x6576, CRL_REG_LEN_08BIT, 0x05}, + {0x6577, CRL_REG_LEN_08BIT, 0x0a}, + {0x6578, CRL_REG_LEN_08BIT, 0x03}, + {0x6579, CRL_REG_LEN_08BIT, 0x94}, + {0x657a, CRL_REG_LEN_08BIT, 0x05}, + {0x657b, CRL_REG_LEN_08BIT, 0x06}, + {0x657c, CRL_REG_LEN_08BIT, 0x00}, + {0x657d, CRL_REG_LEN_08BIT, 0x00}, + {0x657e, CRL_REG_LEN_08BIT, 0x05}, + {0x657f, CRL_REG_LEN_08BIT, 0x04}, + {0x6580, CRL_REG_LEN_08BIT, 0x00}, + {0x6581, CRL_REG_LEN_08BIT, 0x04}, + {0x6582, CRL_REG_LEN_08BIT, 0x05}, + {0x6583, CRL_REG_LEN_08BIT, 0x00}, + {0x6584, CRL_REG_LEN_08BIT, 0x05}, + {0x6585, CRL_REG_LEN_08BIT, 0x0a}, + {0x6586, CRL_REG_LEN_08BIT, 0x03}, + {0x6587, CRL_REG_LEN_08BIT, 0x93}, + {0x6588, CRL_REG_LEN_08BIT, 0x05}, + {0x6589, CRL_REG_LEN_08BIT, 0x06}, + {0x658a, CRL_REG_LEN_08BIT, 0x00}, + {0x658b, CRL_REG_LEN_08BIT, 0x00}, + {0x658c, CRL_REG_LEN_08BIT, 0x05}, + {0x658d, CRL_REG_LEN_08BIT, 0x04}, + {0x658e, CRL_REG_LEN_08BIT, 0x00}, + {0x658f, CRL_REG_LEN_08BIT, 0x04}, + {0x6590, CRL_REG_LEN_08BIT, 0x05}, + {0x6591, CRL_REG_LEN_08BIT, 0x00}, + {0x6592, CRL_REG_LEN_08BIT, 0x05}, + {0x6593, CRL_REG_LEN_08BIT, 0x0a}, + {0x6594, CRL_REG_LEN_08BIT, 0x03}, + {0x6595, CRL_REG_LEN_08BIT, 0x92}, + {0x6596, CRL_REG_LEN_08BIT, 0x05}, + {0x6597, CRL_REG_LEN_08BIT, 0x06}, + {0x6598, CRL_REG_LEN_08BIT, 0x05}, + {0x6599, CRL_REG_LEN_08BIT, 0x04}, + {0x659a, CRL_REG_LEN_08BIT, 0x00}, + {0x659b, CRL_REG_LEN_08BIT, 0x04}, + {0x659c, CRL_REG_LEN_08BIT, 0x05}, + {0x659d, CRL_REG_LEN_08BIT, 0x00}, + {0x659e, CRL_REG_LEN_08BIT, 0x05}, + {0x659f, CRL_REG_LEN_08BIT, 0x0a}, + {0x65a0, CRL_REG_LEN_08BIT, 0x03}, + {0x65a1, CRL_REG_LEN_08BIT, 0x91}, + {0x65a2, CRL_REG_LEN_08BIT, 0x05}, + {0x65a3, CRL_REG_LEN_08BIT, 0x06}, + {0x65a4, CRL_REG_LEN_08BIT, 0x05}, + {0x65a5, CRL_REG_LEN_08BIT, 0x04}, + {0x65a6, CRL_REG_LEN_08BIT, 0x00}, + {0x65a7, CRL_REG_LEN_08BIT, 0x04}, + {0x65a8, CRL_REG_LEN_08BIT, 0x05}, + {0x65a9, CRL_REG_LEN_08BIT, 0x00}, + {0x65aa, CRL_REG_LEN_08BIT, 0x05}, + {0x65ab, CRL_REG_LEN_08BIT, 0x0a}, + {0x65ac, CRL_REG_LEN_08BIT, 0x03}, + {0x65ad, CRL_REG_LEN_08BIT, 0x90}, + {0x65ae, CRL_REG_LEN_08BIT, 0x05}, + {0x65af, CRL_REG_LEN_08BIT, 0x06}, + {0x65b0, CRL_REG_LEN_08BIT, 0x05}, + {0x65b1, CRL_REG_LEN_08BIT, 0x04}, + {0x65b2, CRL_REG_LEN_08BIT, 0x00}, + {0x65b3, CRL_REG_LEN_08BIT, 0x04}, + {0x65b4, CRL_REG_LEN_08BIT, 0x05}, + {0x65b5, CRL_REG_LEN_08BIT, 0x00}, + {0x65b6, CRL_REG_LEN_08BIT, 0x05}, + {0x65b7, CRL_REG_LEN_08BIT, 0x0a}, + {0x65b8, CRL_REG_LEN_08BIT, 0x02}, + {0x65b9, CRL_REG_LEN_08BIT, 0x90}, + {0x65ba, CRL_REG_LEN_08BIT, 0x05}, + {0x65bb, CRL_REG_LEN_08BIT, 0x06}, + {0x65bc, CRL_REG_LEN_08BIT, 0x00}, + {0x65bd, CRL_REG_LEN_08BIT, 0xff}, + {0x65be, CRL_REG_LEN_08BIT, 0x04}, + {0x65bf, CRL_REG_LEN_08BIT, 0x70}, + {0x65c0, CRL_REG_LEN_08BIT, 0x08}, + {0x65c1, CRL_REG_LEN_08BIT, 0x76}, + {0x65c2, CRL_REG_LEN_08BIT, 0x00}, + {0x65c3, CRL_REG_LEN_08BIT, 0xff}, + {0x65c4, CRL_REG_LEN_08BIT, 0x08}, + {0x65c5, CRL_REG_LEN_08BIT, 0x76}, + {0x65c6, CRL_REG_LEN_08BIT, 0x04}, + {0x65c7, CRL_REG_LEN_08BIT, 0x0c}, + {0x65c8, CRL_REG_LEN_08BIT, 0x05}, + {0x65c9, CRL_REG_LEN_08BIT, 0x07}, + {0x65ca, CRL_REG_LEN_08BIT, 0x04}, + {0x65cb, CRL_REG_LEN_08BIT, 0x04}, + {0x65cc, CRL_REG_LEN_08BIT, 0x00}, + {0x65cd, CRL_REG_LEN_08BIT, 0xff}, + {0x65ce, CRL_REG_LEN_08BIT, 0x00}, + {0x65cf, CRL_REG_LEN_08BIT, 0xff}, + {0x65d0, CRL_REG_LEN_08BIT, 0x00}, + {0x65d1, CRL_REG_LEN_08BIT, 0xff}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x7f}, + {0x303c, CRL_REG_LEN_08BIT, 0xfe}, + {0x303d, CRL_REG_LEN_08BIT, 0x19}, + {0x303e, CRL_REG_LEN_08BIT, 0xd7}, + {0x303f, CRL_REG_LEN_08BIT, 0x09}, + {0x3040, CRL_REG_LEN_08BIT, 0x78}, + {0x3042, CRL_REG_LEN_08BIT, 0x05}, + {0x328a, CRL_REG_LEN_08BIT, 0x10}, +}; + +static struct crl_register_write_rep ov10640_streamon_regs[] = { + {OV10640_REG_STREAM, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep ov10640_streamoff_regs[] = { + {OV10640_REG_STREAM, CRL_REG_LEN_08BIT, 0x00}, +}; + +static struct crl_arithmetic_ops ov10640_ls2_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + } +}; + +static struct crl_dynamic_register_access ov10640_h_flip_regs[] = { + { + .address = 0x3090, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov10640_ls2_ops), + .ops = ov10640_ls2_ops, + .mask = 0x04, + } +}; + +static struct crl_arithmetic_ops ov10640_ls3_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 3, + } +}; + +static struct crl_dynamic_register_access ov10640_v_flip_regs[] = { + { + .address = 0x3090, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov10640_ls3_ops), + .ops = ov10640_ls3_ops, + .mask = 0x08, + } +}; + +static struct crl_arithmetic_ops ov10640_hsb_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + } +}; + +static struct crl_dynamic_register_access ov10640_llp_regs[] = { + { + .address = 0x3080, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0xff, + }, + { + .address = 0x3081, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov10640_fll_regs[] = { + { + .address = 0x3082, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0xff, + }, + { + .address = 0x3083, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov10640_ana_gain_regs[] = { + { + .address = 0x30EB, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + } +}; + +/* Long digital gain register */ +static struct crl_dynamic_register_access ov10640_gl_regs[] = { + { + .address = 0x30EC, /* High Byte */ + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0x3f, + }, + { + .address = 0x30ED, /* Low Byte */ + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_arithmetic_ops ov10640_ls1_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + } +}; + +static struct crl_arithmetic_ops ov10640_ls5_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 5, + } +}; + +/* enable ae debug */ +static struct crl_dynamic_register_access ov10640_ae_debug_regs[] = { + { + .address = 0x30FA, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov10640_ls5_ops), + .ops = ov10640_ls5_ops, + .mask = 0x60, + }, +}; + +/* Short digital gain register */ +static struct crl_dynamic_register_access ov10640_gs_regs[] = { + { + .address = 0x30EE, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0x3f, + }, + { + .address = 0x30EF, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* Very short digital gain register */ +static struct crl_dynamic_register_access ov10640_gvs_regs[] = { + { + .address = 0x30F0, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0x3f, + }, + { + .address = 0x30F1, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* Long exposure register, also used in linear(non-HDR) mode */ +static struct crl_dynamic_register_access ov10640_el_regs[] = { + { + .address = 0x30E6, /* High Byte */ + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0xff, + }, + { + .address = 0x30E7, /* Low Byte */ + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* Short exposure register */ +static struct crl_dynamic_register_access ov10640_es_regs[] = { + { + .address = 0x30E8, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_hsb_ops), + .ops = ov10640_hsb_ops, + .mask = 0xff, + }, + { + .address = 0x30E9, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* Very short exposure register */ +static struct crl_dynamic_register_access ov10640_evs_regs[] = { + { + .address = 0x30EA, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* MSB register */ +static struct crl_dynamic_register_access ov10640_msb_regs[] = { + { + .address = 0x328a, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov10640_ls1_ops), + .ops = ov10640_ls1_ops, + .mask = 0x02, + }, +}; + +/* Needed for acpi support for runtime detection */ +static struct crl_sensor_detect_config ov10640_sensor_detect_regset[] = { + { + .reg = { 0x300A, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 8, + }, + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 8, + }, +}; + +/* ctrl-val == 1 ? (1 * 0x0F + 0x45) : (0 * 0x0F + 0x45) -> 0x54 and 0x45 */ +static struct crl_arithmetic_ops ov10640_wdr_ops[] = { + { + .op = CRL_MULTIPLY, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x0F, + }, + { + .op = CRL_ADD, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x45, + } +}; + +static struct crl_dynamic_register_access ov10640_wdr_regs[] = { + { 0x3119, CRL_REG_LEN_08BIT, 0xff, + ARRAY_SIZE(ov10640_wdr_ops), + ov10640_wdr_ops, 0 }, +}; + +static struct crl_arithmetic_ops ov10640_linear_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL, + .operand.entity_val = 0x31BE, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 0x01, + }, +}; + +static struct crl_dynamic_register_access ov10640_linear_regs[] = { + { + .address = 0x31BE, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov10640_linear_ops), + .ops = ov10640_linear_ops, + .mask = 0xff, + }, +}; + +static struct crl_pll_configuration ov10640_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 12, + .pixel_rate_csi = 72000000, /* Ignore the value here, no use */ + .pixel_rate_pa = 72000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov10640_pll_800mbps), + .pll_regs = ov10640_pll_800mbps, + } +}; + +static struct crl_subdev_rect_rep ov10640_1280_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = {0, 0, 1280, 1080}, + .out_rect = {0, 0, 1280, 1080}, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = {0, 0, 1280, 1080}, + .out_rect = {0, 0, 1280, 1080}, + } +}; + +static struct crl_subdev_rect_rep ov10640_1280_1088_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = {0, 0, 1280, 1088}, + .out_rect = {0, 0, 1280, 1088}, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = {0, 0, 1280, 1088}, + .out_rect = {0, 0, 1280, 1088}, + } +}; + +static struct crl_mode_rep ov10640_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov10640_1280_1080_rects), + .sd_rects = ov10640_1280_1080_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 1080, + .min_llp = 2000, + .min_fll = 1200, + .mode_regs_items = ARRAY_SIZE(ov10640_1280_1080_LONG_RAW), + .mode_regs = ov10640_1280_1080_LONG_RAW, + }, + { + .sd_rects_items = ARRAY_SIZE(ov10640_1280_1088_rects), + .sd_rects = ov10640_1280_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 1088, + .min_llp = 2000, + .min_fll = 1200, + .mode_regs_items = ARRAY_SIZE(ov10640_1280_1088_LONG_RAW), + .mode_regs = ov10640_1280_1088_LONG_RAW, + }, +}; + +static struct crl_sensor_subdev_config ov10640_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov10640 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov10640 pixel array", + } +}; + +static struct crl_sensor_limits ov10640_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1280, + .y_addr_max = 1088, + .min_frame_length_lines = 320, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 380, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data ov10640_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + } +}; + +static struct crl_csi_data_fmt ov10640_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + } +}; + +static struct crl_v4l2_ctrl ov10640_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_h_flip_regs), + .regs = ov10640_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 1, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_v_flip_regs), + .regs = ov10640_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1280, + .data.std_data.max = OV10640_HMAX, + .data.std_data.step = 1, + .data.std_data.def = 2000, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_llp_regs), + .regs = ov10640_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1088, + .data.std_data.max = OV10640_VMAX, + .data.std_data.step = 1, + .data.std_data.def = 1200, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_fll_regs), + .regs = ov10640_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 160, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_ana_gain_regs), + .regs = ov10640_ana_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_AUTO_EXPOSURE_DEBUG, + .name = "CRL_CID_AUTO_EXPOSURE_DEBUG", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 3, + .data.std_data.step = 1, + .data.std_data.def = 0x0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_ae_debug_regs), + .regs = ov10640_ae_debug_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 1, + .data.std_data.max = OV10640_MAX_SHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x040, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_el_regs), + .regs = ov10640_el_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = OV10640_MAX_DGAIN, + .data.std_data.step = 1, + .data.std_data.def = 0x100, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_gl_regs), + .regs = ov10640_gl_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_S, + .name = "CRL_CID_DIGITAL_GAIN_S", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = OV10640_MAX_DGAIN, + .data.std_data.step = 1, + .data.std_data.def = 0x100, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_gs_regs), + .regs = ov10640_gs_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_VS, + .name = "CRL_CID_DIGITAL_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = OV10640_MAX_DGAIN, + .data.std_data.step = 1, + .data.std_data.def = 0x100, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_gvs_regs), + .regs = ov10640_gvs_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_STREAMING, + .ctrl_id = CRL_CID_SENSOR_BIT_LINEAR, + .name = "Sensor bit linear", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_linear_regs), + .regs = ov10640_linear_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_MSB_ALIGN, + .name = "CRL_CID_MSB_ALIGN", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 1, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_msb_regs), + .regs = ov10640_msb_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .name = "CRL_CID_EXPOSURE_SHS1", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = OV10640_MAX_SHS1, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_es_regs), + .regs = ov10640_es_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS3, + .name = "CRL_CID_EXPOSURE_SHS3", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x11, + .data.std_data.max = OV10640_MAX_SHS3, + .data.std_data.step = 1, + .data.std_data.def = 0x20, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_evs_regs), + .regs = ov10640_evs_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_WDR_MODE, + .name = "V4L2_CID_WDR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov10640_wdr_regs), + .regs = ov10640_wdr_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +#define OV10640_OTP_BLANK0_START_ADDR 0x349E +#define OV10640_OTP_BLANK0_END_ADDR 0x34AD +#define OV10640_OTP_BLANK1_START_ADDR 0x34AE +#define OV10640_OTP_BLANK1_END_ADDR 0x34BD +#define OV10640_OTP_BLANK0_LEN (OV10640_OTP_BLANK0_END_ADDR - \ + OV10640_OTP_BLANK0_START_ADDR + 1) +#define OV10640_OTP_BLANK1_LEN (OV10640_OTP_BLANK1_END_ADDR - \ + OV10640_OTP_BLANK1_START_ADDR + 1) + +static struct crl_register_write_rep ov10640_nvm_preop_regset[] = { + /* Start streaming */ + {OV10640_REG_STREAM, CRL_REG_LEN_08BIT, 0x01}, + /* clear blank 0 data registers buffer */ + { 0x349E, CRL_REG_LEN_08BIT, 0x00 }, + { 0x349F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34A9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34AA, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34AB, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34AC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34AD, CRL_REG_LEN_08BIT, 0x00 }, + /* set registers buffer range */ + { 0x3496, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3497, CRL_REG_LEN_08BIT, 0x0F }, + /* select blank 0 */ + { 0x3495, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0x00, 0x01 }, + /* enable read strobe */ + { 0x349C, CRL_REG_LEN_08BIT, 0x01 }, + /* Wait for the data to load into the buffer */ + { 0x0000, CRL_REG_LEN_DELAY, 25 }, + + /* clear blank 1 data registers buffer */ + { 0x34AE, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34AF, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34B9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34BA, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34BB, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34BC, CRL_REG_LEN_08BIT, 0x00 }, + { 0x34BD, CRL_REG_LEN_08BIT, 0x00 }, + /* set registers buffer range */ + { 0x3496, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3497, CRL_REG_LEN_08BIT, 0x0F }, + /* select blank 1 */ + { 0x3495, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x01, 0x00, 0x01 }, + /* enable read strobe */ + { 0x349C, CRL_REG_LEN_08BIT, 0x01 }, + /* Wait for the data to load into the buffer */ + { 0x0000, CRL_REG_LEN_DELAY, 25 }, +}; + +static struct crl_register_write_rep ov10640_nvm_postop_regset[] = { + {OV10640_REG_STREAM, CRL_REG_LEN_08BIT, 0x00} /* Stop streaming */ +}; + +static struct crl_nvm_blob ov10640_nvm_blobs[] = { + {CRL_I2C_ADDRESS_NO_OVERRIDE, OV10640_OTP_BLANK0_START_ADDR, OV10640_OTP_BLANK0_LEN}, + {CRL_I2C_ADDRESS_NO_OVERRIDE, OV10640_OTP_BLANK1_START_ADDR, OV10640_OTP_BLANK1_LEN}, +}; + +struct crl_sensor_configuration ov10640_crl_configuration = { + .powerup_regs_items = ARRAY_SIZE(ov10640_powerup_standby), + .powerup_regs = ov10640_powerup_standby, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .power_items = ARRAY_SIZE(ov10640_power_items), + .power_entities = ov10640_power_items, + + .id_reg_items = ARRAY_SIZE(ov10640_sensor_detect_regset), + .id_regs = ov10640_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov10640_sensor_subdevs), + .subdevs = ov10640_sensor_subdevs, + + .sensor_limits = &ov10640_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov10640_pll_configurations), + .pll_configs = ov10640_pll_configurations, + + .modes_items = ARRAY_SIZE(ov10640_modes), + .modes = ov10640_modes, + + .streamon_regs_items = ARRAY_SIZE(ov10640_streamon_regs), + .streamon_regs = ov10640_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov10640_streamoff_regs), + .streamoff_regs = ov10640_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov10640_v4l2_ctrls), + .v4l2_ctrl_bank = ov10640_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov10640_crl_csi_data_fmt), + .csi_fmts = ov10640_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov10640_flip_configurations), + .flip_data = ov10640_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_8BIT, + .crl_nvm_info.nvm_preop_regs_items = + ARRAY_SIZE(ov10640_nvm_preop_regset), + .crl_nvm_info.nvm_preop_regs = ov10640_nvm_preop_regset, + .crl_nvm_info.nvm_postop_regs_items = + ARRAY_SIZE(ov10640_nvm_postop_regset), + .crl_nvm_info.nvm_postop_regs = ov10640_nvm_postop_regset, + .crl_nvm_info.nvm_blobs_items = ARRAY_SIZE(ov10640_nvm_blobs), + .crl_nvm_info.nvm_config = ov10640_nvm_blobs, +}; + +#endif /* __CRLMODULE_OV10640_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov13860_configuration.h b/drivers/media/i2c/crlmodule/crl_ov13860_configuration.h new file mode 100644 index 000000000000..5b701f44fea3 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov13860_configuration.h @@ -0,0 +1,1537 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Author: Kamal Ramamoorthy + * + */ + +#ifndef __CRLMODULE_OV13860_CONFIGURATION_H_ +#define __CRLMODULE_OV13860_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep ov13860_pll_600mbps[] = { + { 0x0300, CRL_REG_LEN_08BIT, 0x00 },/* pll1_pre_div = default*/ + { 0x0301, CRL_REG_LEN_08BIT, 0x00 },/* pll1_multiplier Bit[8-9] = default */ + { 0x0302, CRL_REG_LEN_08BIT, 0x32 },/* pll1_multiplier Bit[0-7] = default */ + { 0x0303, CRL_REG_LEN_08BIT, 0x01 },/* pll1_divm = /(1 + 1) */ + { 0x0304, CRL_REG_LEN_08BIT, 0x07 },/* pll1_div_mipi = default */ + { 0x0305, CRL_REG_LEN_08BIT, 0x01 },/* pll1 pix clock div */ + { 0x0306, CRL_REG_LEN_08BIT, 0x01 },/* pll1 sys clock div */ + { 0x0308, CRL_REG_LEN_08BIT, 0x00 },/* pll1 bypass = default */ + { 0x0309, CRL_REG_LEN_08BIT, 0x01 },/* pll1 cp = default */ + { 0x030A, CRL_REG_LEN_08BIT, 0x00 },/* pll1 ctr = default */ + { 0x030B, CRL_REG_LEN_08BIT, 0x00 },/* pll2_pre_div = default */ + { 0x030c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x28 },/* pll2_r_divp = default */ + { 0x030E, CRL_REG_LEN_08BIT, 0x02 },/* pll2_r_divs = default */ + { 0x030F, CRL_REG_LEN_08BIT, 0x07 },/* pll2_r_divsp = default */ + { 0x0310, CRL_REG_LEN_08BIT, 0x01 },/* pll2_cp = default */ + { 0x0311, CRL_REG_LEN_08BIT, 0x00 },/* pll2_cp = default */ + { 0x0312, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0313, CRL_REG_LEN_08BIT, 0x03 }, + { 0x031B, CRL_REG_LEN_08BIT, 0x00 },/* pll1 rst = default */ + { 0x031C, CRL_REG_LEN_08BIT, 0x00 },/* pll2 rst = default */ + { 0x031E, CRL_REG_LEN_08BIT, 0x01 },/* pll ctr::mipi_bitsel_man = 1 */ + { 0x4837, CRL_REG_LEN_08BIT, 0x1a },/* pclk period */ +}; + +static struct crl_register_write_rep ov13860_pll_1200mbps[] = { + { 0x0300, CRL_REG_LEN_08BIT, 0x00 },/* pll1_pre_div = default*/ + { 0x0301, CRL_REG_LEN_08BIT, 0x00 },/* pll1_multiplier Bit[8-9] = default */ + { 0x0302, CRL_REG_LEN_08BIT, 0x32 },/* pll1_multiplier Bit[0-7] = default */ + { 0x0303, CRL_REG_LEN_08BIT, 0x00 },/* pll1_divm = /(1 + 0) */ + { 0x0304, CRL_REG_LEN_08BIT, 0x07 },/* pll1_div_mipi = default */ + { 0x0305, CRL_REG_LEN_08BIT, 0x01 },/* pll1 pix clock div */ + { 0x0306, CRL_REG_LEN_08BIT, 0x01 },/* pll1 sys clock div */ + { 0x0308, CRL_REG_LEN_08BIT, 0x00 },/* pll1 bypass = default */ + { 0x0309, CRL_REG_LEN_08BIT, 0x01 },/* pll1 cp = default */ + { 0x030A, CRL_REG_LEN_08BIT, 0x00 },/* pll1 ctr = default */ + { 0x030B, CRL_REG_LEN_08BIT, 0x00 },/* pll2_pre_div = default */ + { 0x030c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x28 },/* pll2_r_divp = default */ + { 0x030E, CRL_REG_LEN_08BIT, 0x02 },/* pll2_r_divs = default */ + { 0x030F, CRL_REG_LEN_08BIT, 0x07 },/* pll2_r_divsp = default */ + { 0x0310, CRL_REG_LEN_08BIT, 0x01 },/* pll2_cp = default */ + { 0x0311, CRL_REG_LEN_08BIT, 0x00 },/* pll2_cp = default */ + { 0x0312, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0313, CRL_REG_LEN_08BIT, 0x03 }, + { 0x031B, CRL_REG_LEN_08BIT, 0x00 },/* pll1 rst = default */ + { 0x031C, CRL_REG_LEN_08BIT, 0x00 },/* pll2 rst = default */ + { 0x031E, CRL_REG_LEN_08BIT, 0x01 },/* pll ctr::mipi_bitsel_man = 1 */ + { 0x4837, CRL_REG_LEN_08BIT, 0x0d },/* pclk period */ +}; + +static struct crl_register_write_rep ov13860_powerup_regset[] = { + { 0x3010, CRL_REG_LEN_08BIT, 0x01 }, /* MIPI PHY1 = 1 */ + + /* + * MIPI sc ctrl = 1 + * Bit [7:4] num lane + * Bit [0] phy pad enable + */ + { 0x3012, CRL_REG_LEN_08BIT, 0x21 }, + + { 0x340C, CRL_REG_LEN_08BIT, 0xff }, + { 0x340D, CRL_REG_LEN_08BIT, 0xff }, + + /* + * R Manual + * Bit 0:aec_manual, Bit 1:acg_manual, Bit 2 vts manual + * Bit 4:delay option, Bit 5:gain delay option + */ + { 0x3503, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x3507, CRL_REG_LEN_08BIT, 0x00 },/* MEC Median Exposure Bit[15:8] */ + { 0x3508, CRL_REG_LEN_08BIT, 0x00 },/* MEC Median Exposure Bit[7:8] */ + + { 0x3509, CRL_REG_LEN_08BIT, 0x12 },/* R CTRL9 = default */ + + { 0x350A, CRL_REG_LEN_08BIT, 0x00 },/* MEC Long gain [10:8] */ + { 0x350B, CRL_REG_LEN_08BIT, 0xff },/* MEC Long gain [7:0] */ + + { 0x350F, CRL_REG_LEN_08BIT, 0x10 },/* Median gain [7:0] */ + + { 0x3541, CRL_REG_LEN_08BIT, 0x02 },/* MEC Short exposure [15:8] */ + { 0x3542, CRL_REG_LEN_08BIT, 0x00 },/* Median gain [7:0] */ + { 0x3543, CRL_REG_LEN_08BIT, 0x00 },/* Magic */ + + /* + * HDR related setting + */ + { 0x3547, CRL_REG_LEN_08BIT, 0x00 },/* Very short exposure */ + { 0x3548, CRL_REG_LEN_08BIT, 0x00 },/* Very short exposure */ + { 0x3549, CRL_REG_LEN_08BIT, 0x12 },/* Magic */ + { 0x354B, CRL_REG_LEN_08BIT, 0x10 },/* MEC short gain [7:0] */ + { 0x354F, CRL_REG_LEN_08BIT, 0x10 },/* MEC very short gain [7:0] */ + + /* Analog setting control */ + { 0x3600, CRL_REG_LEN_08BIT, 0x41 }, + { 0x3601, CRL_REG_LEN_08BIT, 0xd4 }, + { 0x3603, CRL_REG_LEN_08BIT, 0x97 }, + { 0x3604, CRL_REG_LEN_08BIT, 0x08 }, + { 0x360A, CRL_REG_LEN_08BIT, 0x35 }, + { 0x360C, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x360D, CRL_REG_LEN_08BIT, 0x53 }, + { 0x3618, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3620, CRL_REG_LEN_08BIT, 0x55 }, + { 0x3622, CRL_REG_LEN_08BIT, 0x8C }, + { 0x3623, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3628, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3660, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x3662, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3663, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3664, CRL_REG_LEN_08BIT, 0x04 }, + { 0x366B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3701, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3702, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3703, CRL_REG_LEN_08BIT, 0x3B }, + { 0x3704, CRL_REG_LEN_08BIT, 0x26 }, + { 0x3705, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3706, CRL_REG_LEN_08BIT, 0x3F }, + { 0x3708, CRL_REG_LEN_08BIT, 0x3C }, + { 0x3709, CRL_REG_LEN_08BIT, 0x18 }, + { 0x370E, CRL_REG_LEN_08BIT, 0x32 }, + { 0x3710, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3712, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3714, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3717, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3719, CRL_REG_LEN_08BIT, 0x03 }, + { 0x371E, CRL_REG_LEN_08BIT, 0x31 }, + { 0x371F, CRL_REG_LEN_08BIT, 0x7F }, + { 0x3720, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3721, CRL_REG_LEN_08BIT, 0x0A }, + { 0x3726, CRL_REG_LEN_08BIT, 0x22 }, + { 0x3727, CRL_REG_LEN_08BIT, 0x44 }, + { 0x3728, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3729, CRL_REG_LEN_08BIT, 0x00 }, + { 0x372A, CRL_REG_LEN_08BIT, 0x20 }, + { 0x372B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x372E, CRL_REG_LEN_08BIT, 0x2B }, + { 0x3730, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3731, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3732, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3733, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3734, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3735, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3736, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3737, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3744, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3745, CRL_REG_LEN_08BIT, 0x5E }, + { 0x3746, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3747, CRL_REG_LEN_08BIT, 0x1F }, + { 0x3748, CRL_REG_LEN_08BIT, 0x00 }, + { 0x374A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3760, CRL_REG_LEN_08BIT, 0xD1 }, + { 0x3761, CRL_REG_LEN_08BIT, 0x31 }, + { 0x3762, CRL_REG_LEN_08BIT, 0x53 }, + { 0x3763, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3767, CRL_REG_LEN_08BIT, 0x24 }, + { 0x3768, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3769, CRL_REG_LEN_08BIT, 0x24 }, + { 0x376C, CRL_REG_LEN_08BIT, 0x43 }, + { 0x376D, CRL_REG_LEN_08BIT, 0x01 }, + { 0x376E, CRL_REG_LEN_08BIT, 0x53 }, + { 0x378C, CRL_REG_LEN_08BIT, 0x1F }, + { 0x378D, CRL_REG_LEN_08BIT, 0x13 }, + { 0x378F, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3790, CRL_REG_LEN_08BIT, 0x5A }, + { 0x3791, CRL_REG_LEN_08BIT, 0x5A }, + { 0x3792, CRL_REG_LEN_08BIT, 0x21 }, + { 0x3794, CRL_REG_LEN_08BIT, 0x71 }, + { 0x3796, CRL_REG_LEN_08BIT, 0x01 }, + { 0x379F, CRL_REG_LEN_08BIT, 0x3E }, + { 0x37A0, CRL_REG_LEN_08BIT, 0x44 }, + { 0x37A1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37A2, CRL_REG_LEN_08BIT, 0x44 }, + { 0x37A3, CRL_REG_LEN_08BIT, 0x41 }, + { 0x37A4, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37A5, CRL_REG_LEN_08BIT, 0xA9 }, + { 0x37B3, CRL_REG_LEN_08BIT, 0xDC }, + { 0x37B4, CRL_REG_LEN_08BIT, 0x0E }, + { 0x37B7, CRL_REG_LEN_08BIT, 0x84 }, + { 0x37B9, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x04 }, + { 0x382A, CRL_REG_LEN_08BIT, 0x04 }, + { 0x382F, CRL_REG_LEN_08BIT, 0x84 }, + { 0x3835, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3837, CRL_REG_LEN_08BIT, 0x02 }, + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, + { 0x383D, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3845, CRL_REG_LEN_08BIT, 0x10 }, + + { 0x3D85, CRL_REG_LEN_08BIT, 0x16 },/* OTP_REGS */ + { 0x3D8C, CRL_REG_LEN_08BIT, 0x79 },/* OTP_REGS */ + { 0x3D8D, CRL_REG_LEN_08BIT, 0x7F },/* OTP_REGS */ + + { 0x4000, CRL_REG_LEN_08BIT, 0x17 },/* BLC_00 */ + + /* + * Magic Registers + */ + { 0x400F, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4011, CRL_REG_LEN_08BIT, 0xFB }, + { 0x4017, CRL_REG_LEN_08BIT, 0x08 }, + { 0x401A, CRL_REG_LEN_08BIT, 0x0E }, + { 0x4020, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4022, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4024, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4026, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4028, CRL_REG_LEN_08BIT, 0x08 }, + { 0x402A, CRL_REG_LEN_08BIT, 0x08 }, + { 0x402C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x402E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4030, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4032, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4034, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4036, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4038, CRL_REG_LEN_08BIT, 0x08 }, + { 0x403A, CRL_REG_LEN_08BIT, 0x08 }, + { 0x403C, CRL_REG_LEN_08BIT, 0x08 }, + { 0x403E, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4052, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4053, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4054, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4055, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4056, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4057, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4058, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4059, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4202, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4203, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4d00, CRL_REG_LEN_08BIT, 0x05 }, + { 0x4d01, CRL_REG_LEN_08BIT, 0x05 }, + { 0x4d02, CRL_REG_LEN_08BIT, 0xCA }, + { 0x4d03, CRL_REG_LEN_08BIT, 0xD7 }, + { 0x4d04, CRL_REG_LEN_08BIT, 0xAE }, + { 0x4d05, CRL_REG_LEN_08BIT, 0x13 }, + { 0x4813, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4815, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4837, CRL_REG_LEN_08BIT, 0x0D }, + { 0x486E, CRL_REG_LEN_08BIT, 0x03 }, + { 0x4B01, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4B06, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4C01, CRL_REG_LEN_08BIT, 0xDF }, + + /* + * DSP control related registers required for RAW + * Sensor path + */ + { 0x5001, CRL_REG_LEN_08BIT, 0x40 }, + { 0x5002, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5003, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5004, CRL_REG_LEN_08BIT, 0x80 }, + { 0x5005, CRL_REG_LEN_08BIT, 0x00 }, + { 0x501D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x501F, CRL_REG_LEN_08BIT, 0x06 }, + { 0x5021, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5022, CRL_REG_LEN_08BIT, 0x13 }, + { 0x5058, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5200, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5209, CRL_REG_LEN_08BIT, 0x00 }, + { 0x520A, CRL_REG_LEN_08BIT, 0x80 }, + { 0x520B, CRL_REG_LEN_08BIT, 0x04 }, + { 0x520C, CRL_REG_LEN_08BIT, 0x01 }, + { 0x520E, CRL_REG_LEN_08BIT, 0x34 }, + { 0x5210, CRL_REG_LEN_08BIT, 0x10 }, + { 0x5211, CRL_REG_LEN_08BIT, 0xA0 }, + { 0x5280, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5292, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5C80, CRL_REG_LEN_08BIT, 0x05 }, + { 0x5C81, CRL_REG_LEN_08BIT, 0x90 }, + { 0x5C82, CRL_REG_LEN_08BIT, 0x09 }, + { 0x5C83, CRL_REG_LEN_08BIT, 0x5F }, + { 0x5D00, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4001, CRL_REG_LEN_08BIT, 0x60 }, /* BLC control */ + + /* + * Magic Registers + */ + { 0x560F, CRL_REG_LEN_08BIT, 0xFC }, + { 0x5610, CRL_REG_LEN_08BIT, 0xF0 }, + { 0x5611, CRL_REG_LEN_08BIT, 0x10 }, + { 0x562F, CRL_REG_LEN_08BIT, 0xFC }, + { 0x5630, CRL_REG_LEN_08BIT, 0xF0 }, + { 0x5631, CRL_REG_LEN_08BIT, 0x10 }, + { 0x564F, CRL_REG_LEN_08BIT, 0xFC }, + { 0x5650, CRL_REG_LEN_08BIT, 0xF0 }, + { 0x5651, CRL_REG_LEN_08BIT, 0x10 }, + { 0x566F, CRL_REG_LEN_08BIT, 0xFC }, + { 0x5670, CRL_REG_LEN_08BIT, 0xF0 }, + { 0x5671, CRL_REG_LEN_08BIT, 0x10 }, +}; + +static struct crl_register_write_rep ov13860_mode_13m[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x0D },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x88 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x23 }, + { 0x372F, CRL_REG_LEN_08BIT, 0xA0 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x10 },/* h_output_size high 4208 x 3120 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x70 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x0C },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x30 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x11 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0x99 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x13 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x18 }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x03 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x04 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_8m[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x0D },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x88 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x23 }, + { 0x372F, CRL_REG_LEN_08BIT, 0xA0 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3264 x 2448 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xC0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x09 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x90 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x11 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0x99 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x13 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x18 }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x03 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x04 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_4k2k[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x0D },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x88 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x23 }, + { 0x372F, CRL_REG_LEN_08BIT, 0xA0 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x10 },/* h_output_size high 4096 x 2160 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x00 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x08 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x70 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x11 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0x99 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x13 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x18 }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x03 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x04 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_uhd[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x0D },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x88 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x23 }, + { 0x372F, CRL_REG_LEN_08BIT, 0xA0 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0F },/* h_output_size high 3840 x 2160 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x00 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x08 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x70 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x11 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0x99 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x13 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x18 }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x03 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x04 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_6m[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x0D },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x88 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x23 }, + { 0x372F, CRL_REG_LEN_08BIT, 0xA0 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3264 x 1836 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xC0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x07 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x2C },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x11 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x00 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0x99 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x88 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x13 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x18 }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x03 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x04 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_3m[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x06 },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0xB8 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x63 }, + { 0x372F, CRL_REG_LEN_08BIT, 0x90 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x08 },/* h_output_size high 2048 x 1536 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x00 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x06 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x00 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x31 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x02 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x40 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0xD9 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x48 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x09 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x0C }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x71 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_1952_1088[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x06 },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0xB8 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x63 }, + { 0x372F, CRL_REG_LEN_08BIT, 0x90 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x07 },/* h_output_size high 1952 x 1088 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xA0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x04 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x40 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x31 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x02 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x40 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0xD9 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x48 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x09 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x0C }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x71 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_720[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x03 },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x44 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x63 }, + { 0x372F, CRL_REG_LEN_08BIT, 0x90 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x05 },/* h_output_size high 1280 x 720 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x00 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x02 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0xD0 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x31 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x02 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x40 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0xD9 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x48 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x09 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x0C }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x71 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_mode_480[] = { + + /* + * Exposure & Gain + */ + { 0x3501, CRL_REG_LEN_08BIT, 0x03 },/* Long Exposure */ + { 0x3502, CRL_REG_LEN_08BIT, 0x44 },/* Long Exposure */ + + { 0x370A, CRL_REG_LEN_08BIT, 0x63 }, + { 0x372F, CRL_REG_LEN_08BIT, 0x90 }, + + /* + * Windowing + */ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x14 },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x10 },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0x8B },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0x43 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x02 },/* h_output_size high 640 x 480 */ + { 0x3809, CRL_REG_LEN_08BIT, 0x80 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x01 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0xE0 },/* v_output_size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* Manual horizontal window offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* Manual horizontal window offset low */ + { 0x3813, CRL_REG_LEN_08BIT, 0x04 },/* Manual vertical window offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x11 },/* Horizontal sub-sample odd inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x31 },/* Vertical sub-sample odd inc */ + { 0x383D, CRL_REG_LEN_08BIT, 0xFF },/* Vertical sub-sample odd inc */ + + { 0x3820, CRL_REG_LEN_08BIT, 0x02 },/* Binning */ + { 0x3842, CRL_REG_LEN_08BIT, 0x40 },/* Binning */ + { 0x5000, CRL_REG_LEN_08BIT, 0xD9 },/* Binning */ + + { 0x3836, CRL_REG_LEN_08BIT, 0x0C }, /* ablc_use_num */ + { 0x383C, CRL_REG_LEN_08BIT, 0x48 }, /* Boundary Pix num */ + + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + { 0x4009, CRL_REG_LEN_08BIT, 0x09 }, /* Magic */ + { 0x4019, CRL_REG_LEN_08BIT, 0x0C }, /* Magic */ + { 0x4051, CRL_REG_LEN_08BIT, 0x01 }, /* Magic */ + { 0x4066, CRL_REG_LEN_08BIT, 0x02 }, /* Magic */ + + { 0x5201, CRL_REG_LEN_08BIT, 0x71 }, /* Magic */ + { 0x5204, CRL_REG_LEN_08BIT, 0x00 }, /* Magic */ + { 0x5205, CRL_REG_LEN_08BIT, 0x80 }, /* Magic */ +}; + +static struct crl_register_write_rep ov13860_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep ov13860_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_arithmetic_ops ov13860_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov13860_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_dynamic_register_access ov13860_v_flip_regs[] = { + { + .address = 0x3820, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov13860_vflip_ops), + .ops = ov13860_vflip_ops, + .mask = 0x2, + }, +}; + +static struct crl_dynamic_register_access ov13860_h_flip_regs[] = { + { + .address = 0x3821, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov13860_hflip_ops), + .ops = ov13860_hflip_ops, + .mask = 0x2, + }, +}; + +struct crl_register_write_rep ov13860_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_dynamic_register_access ov13860_ana_gain_global_regs[] = { + { + .address = 0x350A, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0x7ff, + }, +}; + +static struct crl_dynamic_register_access ov13860_exposure_regs[] = { + { + .address = 0x3501, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + } +}; + +static struct crl_dynamic_register_access ov13860_vblank_regs[] = { + { + .address = 0x380E, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ov13860_hblank_regs[] = { + { + .address = 0x380C, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_sensor_detect_config ov13860_sensor_detect_regset[] = { + { + .reg = { 0x300A, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300C, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration ov13860_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 600000000, + .bitsperpixel = 10, + .pixel_rate_csi = 150000000, + .pixel_rate_pa = 240000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov13860_pll_1200mbps), + .pll_regs = ov13860_pll_1200mbps, + }, + { + .input_clk = 24000000, + .op_sys_clk = 300000000, + .bitsperpixel = 10, + .pixel_rate_csi = 75000000, + .pixel_rate_pa = 240000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov13860_pll_600mbps), + .pll_regs = ov13860_pll_600mbps, + } +}; + +static struct crl_subdev_rect_rep ov13860_13m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4208, + .out_rect.height = 3120, + }, +}; + +static struct crl_subdev_rect_rep ov13860_8m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2448, + }, +}; + +static struct crl_subdev_rect_rep ov13860_4k2k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4096, + .out_rect.height = 2160, + }, +}; + +static struct crl_subdev_rect_rep ov13860_uhd_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3840, + .out_rect.height = 2160, + }, +}; + +static struct crl_subdev_rect_rep ov13860_6m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 1836, + }, +}; + +static struct crl_subdev_rect_rep ov13860_3m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 2048, + .out_rect.height = 1536, + }, +}; + +static struct crl_subdev_rect_rep ov13860_1952_1088_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1952, + .out_rect.height = 1088, + }, +}; + +static struct crl_subdev_rect_rep ov13860_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep ov13860_480_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 4224, + .out_rect.height = 3120, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 4224, + .in_rect.height = 3120, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_mode_rep ov13860_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov13860_13m_rects), + .sd_rects = ov13860_13m_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4208, + .height = 3120, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_13m), + .mode_regs = ov13860_mode_13m, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_8m_rects), + .sd_rects = ov13860_8m_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3280, + .height = 2448, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_8m), + .mode_regs = ov13860_mode_8m, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_4k2k_rects), + .sd_rects = ov13860_4k2k_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 4096, + .height = 2160, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_4k2k), + .mode_regs = ov13860_mode_4k2k, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_uhd_rects), + .sd_rects = ov13860_uhd_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3840, + .height = 2160, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_uhd), + .mode_regs = ov13860_mode_uhd, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_6m_rects), + .sd_rects = ov13860_6m_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3280, + .height = 1836, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_6m), + .mode_regs = ov13860_mode_6m, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_3m_rects), + .sd_rects = ov13860_3m_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 2048, + .height = 1536, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_3m), + .mode_regs = ov13860_mode_3m, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_1952_1088_rects), + .sd_rects = ov13860_1952_1088_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 1952, + .height = 1088, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_1952_1088), + .mode_regs = ov13860_mode_1952_1088, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_720_rects), + .sd_rects = ov13860_720_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 1280, + .height = 720, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_720), + .mode_regs = ov13860_mode_720, + }, + { + .sd_rects_items = ARRAY_SIZE(ov13860_480_rects), + .sd_rects = ov13860_480_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov13860_mode_480), + .mode_regs = ov13860_mode_480, + }, +}; + +static struct crl_sensor_subdev_config ov13860_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov13860 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov13860 pixel array", + }, +}; + +static struct crl_sensor_limits ov13860_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 4224, + .y_addr_max = 3120, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 16, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data ov13860_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, +}; + +static struct crl_csi_data_fmt ov13860_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = 0, + }, +}; + +static struct crl_v4l2_ctrl ov13860_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_ana_gain_global_regs), + .regs = ov13860_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_exposure_regs), + .regs = ov13860_exposure_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_h_flip_regs), + .regs = ov13860_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_v_flip_regs), + .regs = ov13860_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VBLANK, + .name = "V4L2_CID_VBLANK", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_vblank_regs), + .regs = ov13860_vblank_regs, + .dep_items = 0, /* FLL changed automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HBLANK, + .name = "V4L2_CID_HBLANK", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov13860_hblank_regs), + .regs = ov13860_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity ov13860_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +struct crl_sensor_configuration ov13860_crl_configuration = { + + .power_items = ARRAY_SIZE(ov13860_power_items), + .power_entities = ov13860_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov13860_powerup_regset), + .powerup_regs = ov13860_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(ov13860_sensor_detect_regset), + .id_regs = ov13860_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov13860_sensor_subdevs), + .subdevs = ov13860_sensor_subdevs, + + .sensor_limits = &ov13860_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov13860_pll_configurations), + .pll_configs = ov13860_pll_configurations, + + .modes_items = ARRAY_SIZE(ov13860_modes), + .modes = ov13860_modes, + + .streamon_regs_items = ARRAY_SIZE(ov13860_streamon_regs), + .streamon_regs = ov13860_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov13860_streamoff_regs), + .streamoff_regs = ov13860_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov13860_v4l2_ctrls), + .v4l2_ctrl_bank = ov13860_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov13860_crl_csi_data_fmt), + .csi_fmts = ov13860_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov13860_flip_configurations), + .flip_data = ov13860_flip_configurations, +}; + +#endif /* __CRLMODULE_OV13860_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov2740_configuration.h b/drivers/media/i2c/crlmodule/crl_ov2740_configuration.h new file mode 100644 index 000000000000..1060d6b62f04 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov2740_configuration.h @@ -0,0 +1,761 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Roy Yang + * + */ + +#ifndef __CRLMODULE_OV2740_CONFIGURATION_H_ +#define __CRLMODULE_OV2740_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep ov2740_powerup_regset[] = { + /*Reset*/ + {0x0103, CRL_REG_LEN_08BIT, 0x01}, + {0x0302, CRL_REG_LEN_08BIT, 0x4b},/* 26;1e */ + {0x030d, CRL_REG_LEN_08BIT, 0x4b},/* 26;1e */ + {0x030e, CRL_REG_LEN_08BIT, 0x02}, + {0x030a, CRL_REG_LEN_08BIT, 0x01}, + {0x0312, CRL_REG_LEN_08BIT, 0x11},/* 01 */ + {0x3000, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x32},/* 12(2 lane for 32; 1lane for 12) */ + {0x3031, CRL_REG_LEN_08BIT, 0x0a}, + {0x3080, CRL_REG_LEN_08BIT, 0x08}, + {0x3083, CRL_REG_LEN_08BIT, 0xB4}, + {0x3103, CRL_REG_LEN_08BIT, 0x00}, + {0x3104, CRL_REG_LEN_08BIT, 0x01}, + {0x3106, CRL_REG_LEN_08BIT, 0x01}, + {0x3500, CRL_REG_LEN_08BIT, 0x00}, + {0x3501, CRL_REG_LEN_08BIT, 0x44}, + {0x3502, CRL_REG_LEN_08BIT, 0x40}, + {0x3503, CRL_REG_LEN_08BIT, 0x88}, + {0x3507, CRL_REG_LEN_08BIT, 0x00}, + {0x3508, CRL_REG_LEN_08BIT, 0x00}, + {0x3509, CRL_REG_LEN_08BIT, 0x80}, + {0x350c, CRL_REG_LEN_08BIT, 0x00}, + {0x350d, CRL_REG_LEN_08BIT, 0x80}, + {0x3510, CRL_REG_LEN_08BIT, 0x00}, + {0x3511, CRL_REG_LEN_08BIT, 0x00}, + {0x3512, CRL_REG_LEN_08BIT, 0x20}, + {0x3632, CRL_REG_LEN_08BIT, 0x00}, + {0x3633, CRL_REG_LEN_08BIT, 0x10}, + {0x3634, CRL_REG_LEN_08BIT, 0x10}, + {0x3635, CRL_REG_LEN_08BIT, 0x10}, + {0x3645, CRL_REG_LEN_08BIT, 0x13}, + {0x3646, CRL_REG_LEN_08BIT, 0x81}, + {0x3636, CRL_REG_LEN_08BIT, 0x10}, + {0x3651, CRL_REG_LEN_08BIT, 0x0a}, + {0x3656, CRL_REG_LEN_08BIT, 0x02}, + {0x3659, CRL_REG_LEN_08BIT, 0x04}, + {0x365a, CRL_REG_LEN_08BIT, 0xda}, + {0x365b, CRL_REG_LEN_08BIT, 0xa2}, + {0x365c, CRL_REG_LEN_08BIT, 0x04}, + {0x365d, CRL_REG_LEN_08BIT, 0x1d}, + {0x365e, CRL_REG_LEN_08BIT, 0x1a}, + {0x3662, CRL_REG_LEN_08BIT, 0xd7}, + {0x3667, CRL_REG_LEN_08BIT, 0x78}, + {0x3669, CRL_REG_LEN_08BIT, 0x0a}, + {0x366a, CRL_REG_LEN_08BIT, 0x92}, + {0x3700, CRL_REG_LEN_08BIT, 0x54}, + {0x3702, CRL_REG_LEN_08BIT, 0x10}, + {0x3706, CRL_REG_LEN_08BIT, 0x42}, + {0x3709, CRL_REG_LEN_08BIT, 0x30}, + {0x370b, CRL_REG_LEN_08BIT, 0xc2}, + {0x3714, CRL_REG_LEN_08BIT, 0x63}, + {0x3715, CRL_REG_LEN_08BIT, 0x01}, + {0x3716, CRL_REG_LEN_08BIT, 0x00}, + {0x371a, CRL_REG_LEN_08BIT, 0x3e}, + {0x3732, CRL_REG_LEN_08BIT, 0x0e}, + {0x3733, CRL_REG_LEN_08BIT, 0x10}, + {0x375f, CRL_REG_LEN_08BIT, 0x0e}, + {0x3768, CRL_REG_LEN_08BIT, 0x30}, + {0x3769, CRL_REG_LEN_08BIT, 0x44}, + {0x376a, CRL_REG_LEN_08BIT, 0x22}, + {0x377b, CRL_REG_LEN_08BIT, 0x20}, + {0x377c, CRL_REG_LEN_08BIT, 0x00}, + {0x377d, CRL_REG_LEN_08BIT, 0x0c}, + {0x3798, CRL_REG_LEN_08BIT, 0x00}, + {0x37a1, CRL_REG_LEN_08BIT, 0x55}, + {0x37a8, CRL_REG_LEN_08BIT, 0x6d}, + {0x37c2, CRL_REG_LEN_08BIT, 0x04}, + {0x37c5, CRL_REG_LEN_08BIT, 0x00}, + {0x37c8, CRL_REG_LEN_08BIT, 0x00}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0x00}, + {0x3804, CRL_REG_LEN_08BIT, 0x07}, + {0x3805, CRL_REG_LEN_08BIT, 0x8f}, + {0x3806, CRL_REG_LEN_08BIT, 0x04}, + {0x3807, CRL_REG_LEN_08BIT, 0x47}, + {0x3808, CRL_REG_LEN_08BIT, 0x07}, + {0x3809, CRL_REG_LEN_08BIT, 0x88}, + {0x380a, CRL_REG_LEN_08BIT, 0x04}, + {0x380b, CRL_REG_LEN_08BIT, 0x40}, + {0x380c, CRL_REG_LEN_08BIT, 0x08}, + {0x380d, CRL_REG_LEN_08BIT, 0x70}, + {0x380e, CRL_REG_LEN_08BIT, 0x04}, + {0x380f, CRL_REG_LEN_08BIT, 0x56}, + {0x3810, CRL_REG_LEN_08BIT, 0x00}, + {0x3811, CRL_REG_LEN_08BIT, 0x04}, + {0x3812, CRL_REG_LEN_08BIT, 0x00}, + {0x3813, CRL_REG_LEN_08BIT, 0x04}, + {0x3814, CRL_REG_LEN_08BIT, 0x01}, + {0x3815, CRL_REG_LEN_08BIT, 0x01}, + {0x3820, CRL_REG_LEN_08BIT, 0x80}, + {0x3821, CRL_REG_LEN_08BIT, 0x46}, + {0x3822, CRL_REG_LEN_08BIT, 0x84}, + {0x3829, CRL_REG_LEN_08BIT, 0x00}, + {0x382a, CRL_REG_LEN_08BIT, 0x01}, + {0x382b, CRL_REG_LEN_08BIT, 0x01}, + {0x3830, CRL_REG_LEN_08BIT, 0x04}, + {0x3836, CRL_REG_LEN_08BIT, 0x01}, + {0x3837, CRL_REG_LEN_08BIT, 0x08}, + {0x3839, CRL_REG_LEN_08BIT, 0x01}, + {0x383a, CRL_REG_LEN_08BIT, 0x00}, + {0x383b, CRL_REG_LEN_08BIT, 0x08}, + {0x383c, CRL_REG_LEN_08BIT, 0x00}, + {0x3f0b, CRL_REG_LEN_08BIT, 0x00}, + {0x4001, CRL_REG_LEN_08BIT, 0x20}, + {0x4009, CRL_REG_LEN_08BIT, 0x07}, + {0x4003, CRL_REG_LEN_08BIT, 0x10}, + {0x4010, CRL_REG_LEN_08BIT, 0xe0}, + {0x4016, CRL_REG_LEN_08BIT, 0x00}, + {0x4017, CRL_REG_LEN_08BIT, 0x10}, + {0x4044, CRL_REG_LEN_08BIT, 0x02}, + {0x4304, CRL_REG_LEN_08BIT, 0x08}, + {0x4307, CRL_REG_LEN_08BIT, 0x30}, + {0x4320, CRL_REG_LEN_08BIT, 0x80}, + {0x4322, CRL_REG_LEN_08BIT, 0x00}, + {0x4323, CRL_REG_LEN_08BIT, 0x00}, + {0x4324, CRL_REG_LEN_08BIT, 0x00}, + {0x4325, CRL_REG_LEN_08BIT, 0x00}, + {0x4326, CRL_REG_LEN_08BIT, 0x00}, + {0x4327, CRL_REG_LEN_08BIT, 0x00}, + {0x4328, CRL_REG_LEN_08BIT, 0x00}, + {0x4329, CRL_REG_LEN_08BIT, 0x00}, + {0x432c, CRL_REG_LEN_08BIT, 0x03}, + {0x432d, CRL_REG_LEN_08BIT, 0x81}, + {0x4501, CRL_REG_LEN_08BIT, 0x84}, + {0x4502, CRL_REG_LEN_08BIT, 0x40}, + {0x4503, CRL_REG_LEN_08BIT, 0x18}, + {0x4504, CRL_REG_LEN_08BIT, 0x04}, + {0x4508, CRL_REG_LEN_08BIT, 0x02}, + {0x4601, CRL_REG_LEN_08BIT, 0x10}, + {0x4800, CRL_REG_LEN_08BIT, 0x00}, + {0x4816, CRL_REG_LEN_08BIT, 0x52}, + {0x4837, CRL_REG_LEN_08BIT, 0x16}, + {0x5000, CRL_REG_LEN_08BIT, 0x7f}, + {0x5001, CRL_REG_LEN_08BIT, 0x00}, + {0x5005, CRL_REG_LEN_08BIT, 0x38}, + {0x501e, CRL_REG_LEN_08BIT, 0x0d}, + {0x5040, CRL_REG_LEN_08BIT, 0x00}, + {0x5901, CRL_REG_LEN_08BIT, 0x00}, + {0x3800, CRL_REG_LEN_08BIT, 0x00}, + {0x3801, CRL_REG_LEN_08BIT, 0x00}, + {0x3802, CRL_REG_LEN_08BIT, 0x00}, + {0x3803, CRL_REG_LEN_08BIT, 0x00}, + {0x3804, CRL_REG_LEN_08BIT, 0x07}, + {0x3805, CRL_REG_LEN_08BIT, 0x8f}, + {0x3806, CRL_REG_LEN_08BIT, 0x04}, + {0x3807, CRL_REG_LEN_08BIT, 0x47}, + {0x3808, CRL_REG_LEN_08BIT, 0x07}, + {0x3809, CRL_REG_LEN_08BIT, 0x8c}, + {0x380a, CRL_REG_LEN_08BIT, 0x04}, + {0x380b, CRL_REG_LEN_08BIT, 0x44}, + {0x3810, CRL_REG_LEN_08BIT, 0x00}, + {0x3811, CRL_REG_LEN_08BIT, 0x00},/* 00 */ + {0x3812, CRL_REG_LEN_08BIT, 0x00}, + {0x3813, CRL_REG_LEN_08BIT, 0x02},/* 00 */ + {0x4003, CRL_REG_LEN_08BIT, 0x40},/* set Black level to 0x40 */ +}; + +static struct crl_register_write_rep ov2740_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep ov2740_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep ov2740_data_fmt_width10[] = { + { 0x3031, CRL_REG_LEN_08BIT, 0x0a } +}; + +static struct crl_arithmetic_ops ov2740_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov2740_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov2740_hblank_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov2740_exposure_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, +}; + +static struct crl_dynamic_register_access ov2740_v_flip_regs[] = { + { + .address = 0x3820, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2740_vflip_ops), + .ops = ov2740_vflip_ops, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access ov2740_h_flip_regs[] = { + { + .address = 0x3821, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2740_hflip_ops), + .ops = ov2740_hflip_ops, + .mask = 0x1, + }, +}; + +static struct crl_dynamic_register_access ov2740_dig_gain_regs[] = { + { + .address = 0x500A, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, + { + .address = 0x500C, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, + { + .address = 0x500E, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +struct crl_register_write_rep ov2740_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_dynamic_register_access ov2740_ana_gain_global_regs[] = { + { + .address = 0x3508, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0x7ff, + }, +}; + +static struct crl_dynamic_register_access ov2740_exposure_regs[] = { + { + .address = 0x3500, + .len = CRL_REG_LEN_24BIT, + .ops_items = ARRAY_SIZE(ov2740_exposure_ops), + .ops = ov2740_exposure_ops, + .mask = 0x0ffff0, + }, +}; + +static struct crl_dynamic_register_access ov2740_vblank_regs[] = { + { + .address = 0x380E, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ov2740_hblank_regs[] = { + { + .address = 0x380C, + .len = CRL_REG_LEN_16BIT, + .ops_items = ARRAY_SIZE(ov2740_hblank_ops), + .ops = ov2740_hblank_ops, + .mask = 0xffff, + }, +}; + +static struct crl_sensor_detect_config ov2740_sensor_detect_regset[] = { + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300C, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration ov2740_pll_configurations[] = { + { + .input_clk = 19200000, + .op_sys_clk = 72000000, + .bitsperpixel = 10, + .pixel_rate_csi = 28800000, + .pixel_rate_pa = 28800000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, + +}; + +static struct crl_subdev_rect_rep ov2740_1932x1092_rects_native[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1932, + .in_rect.height = 1092, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1932, + .out_rect.height = 1092, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1932, + .in_rect.height = 1092, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1932, + .out_rect.height = 1092, + }, +}; + +static struct crl_mode_rep ov2740_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov2740_1932x1092_rects_native), + .sd_rects = ov2740_1932x1092_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1932, + .height = 1092, + .min_llp = 2160, + .min_fll = 1110, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = 0, + }, +}; + +static struct crl_sensor_subdev_config ov2740_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov2740 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov2740 pixel array", + }, +}; + +static struct crl_sensor_limits ov2740_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1932, + .y_addr_max = 1092, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 2160, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data ov2740_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, +}; + +static struct crl_csi_data_fmt ov2740_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = ov2740_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov2740_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov2740_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov2740_data_fmt_width10, + }, +}; + +static struct crl_v4l2_ctrl ov2740_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_ana_gain_global_regs), + .regs = ov2740_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_exposure_regs), + .regs = ov2740_exposure_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_h_flip_regs), + .regs = ov2740_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_v_flip_regs), + .regs = ov2740_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 1110, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_vblank_regs), + .regs = ov2740_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 2160, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_hblank_regs), + .regs = ov2740_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4095, + .data.std_data.step = 1, + .data.std_data.def = 1024, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2740_dig_gain_regs), + .regs = ov2740_dig_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_WDR_MODE, + .name = "V4L2_CID_WDR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops ov2740_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops ov2740_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc ov2740_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov2740_frame_desc_width_ops), + .ops = ov2740_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov2740_frame_desc_height_ops), + .ops = ov2740_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity ov2740_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 19200000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 0, + .delay = 1000, + }, +}; + +static struct crl_sensor_configuration ov2740_crl_configuration = { + + .power_items = ARRAY_SIZE(ov2740_power_items), + .power_entities = ov2740_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov2740_powerup_regset), + .powerup_regs = ov2740_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + + .id_reg_items = ARRAY_SIZE(ov2740_sensor_detect_regset), + .id_regs = ov2740_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov2740_sensor_subdevs), + .subdevs = ov2740_sensor_subdevs, + + .sensor_limits = &ov2740_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov2740_pll_configurations), + .pll_configs = ov2740_pll_configurations, + + .modes_items = ARRAY_SIZE(ov2740_modes), + .modes = ov2740_modes, + + .streamon_regs_items = ARRAY_SIZE(ov2740_streamon_regs), + .streamon_regs = ov2740_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov2740_streamoff_regs), + .streamoff_regs = ov2740_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov2740_v4l2_ctrls), + .v4l2_ctrl_bank = ov2740_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov2740_crl_csi_data_fmt), + .csi_fmts = ov2740_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov2740_flip_configurations), + .flip_data = ov2740_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_16BIT, + .crl_nvm_info.nvm_preop_regs_items = 0, + .crl_nvm_info.nvm_postop_regs_items = 0, + .crl_nvm_info.nvm_blobs_items = 0, + + .frame_desc_entries = ARRAY_SIZE(ov2740_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = ov2740_frame_desc, + + .msr_file_name = "", +}; + +#endif /* __CRLMODULE_OV2740_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov2775_configuration.h b/drivers/media/i2c/crlmodule/crl_ov2775_configuration.h new file mode 100644 index 000000000000..cffc0cccb865 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov2775_configuration.h @@ -0,0 +1,8177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation + * + * Author: Leo Zhao + * Author: Mingda Xu + * + */ + +#ifndef __CRLMODULE_OV2775_CONFIGURATION_H_ +#define __CRLMODULE_OV2775_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +#define OV2775_CAPTURE_MODE_MAX 4 + +static struct crl_register_write_rep + ov2775_linear_hcg_30fps_mipi960_regset[] = { + {0x3013, CRL_REG_LEN_08BIT, 0x01}, + {0x3000, CRL_REG_LEN_08BIT, 0x02}, + {0x3001, CRL_REG_LEN_08BIT, 0x28}, + {0x3002, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x01}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, + {0x3005, CRL_REG_LEN_08BIT, 0x26}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x07}, + {0x3008, CRL_REG_LEN_08BIT, 0x01}, + {0x3009, CRL_REG_LEN_08BIT, 0x00}, + {0x300c, CRL_REG_LEN_08BIT, 0x6c}, + {0x300e, CRL_REG_LEN_08BIT, 0x80}, + {0x300f, CRL_REG_LEN_08BIT, 0x00}, + {0x3012, CRL_REG_LEN_08BIT, 0x00}, + {0x3013, CRL_REG_LEN_08BIT, 0x00}, + {0x3014, CRL_REG_LEN_08BIT, 0xc4}, + {0x3015, CRL_REG_LEN_08BIT, 0x00}, + {0x3017, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x00}, + {0x3019, CRL_REG_LEN_08BIT, 0x00}, + {0x301a, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0x0e}, + {0x301e, CRL_REG_LEN_08BIT, 0x17}, + {0x301f, CRL_REG_LEN_08BIT, 0xe1}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x3031, CRL_REG_LEN_08BIT, 0x62}, + {0x3032, CRL_REG_LEN_08BIT, 0xf0}, + {0x3033, CRL_REG_LEN_08BIT, 0x30}, + {0x3034, CRL_REG_LEN_08BIT, 0x3f}, + {0x3035, CRL_REG_LEN_08BIT, 0x5f}, + {0x3036, CRL_REG_LEN_08BIT, 0x02}, + {0x3037, CRL_REG_LEN_08BIT, 0x9f}, + {0x3038, CRL_REG_LEN_08BIT, 0x04}, + {0x3039, CRL_REG_LEN_08BIT, 0xb7}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x07}, + {0x303c, CRL_REG_LEN_08BIT, 0xf0}, + {0x303d, CRL_REG_LEN_08BIT, 0x00}, + {0x303e, CRL_REG_LEN_08BIT, 0x0b}, + {0x303f, CRL_REG_LEN_08BIT, 0xe3}, + {0x3040, CRL_REG_LEN_08BIT, 0xf3}, + {0x3041, CRL_REG_LEN_08BIT, 0x29}, + {0x3042, CRL_REG_LEN_08BIT, 0xf6}, + {0x3043, CRL_REG_LEN_08BIT, 0x65}, + {0x3044, CRL_REG_LEN_08BIT, 0x06}, + {0x3045, CRL_REG_LEN_08BIT, 0x0f}, + {0x3046, CRL_REG_LEN_08BIT, 0x59}, + {0x3047, CRL_REG_LEN_08BIT, 0x07}, + {0x3048, CRL_REG_LEN_08BIT, 0x82}, + {0x3049, CRL_REG_LEN_08BIT, 0xcf}, + {0x304a, CRL_REG_LEN_08BIT, 0x12}, + {0x304b, CRL_REG_LEN_08BIT, 0x40}, + {0x304c, CRL_REG_LEN_08BIT, 0x33}, + {0x304d, CRL_REG_LEN_08BIT, 0xa4}, + {0x304e, CRL_REG_LEN_08BIT, 0x0b}, + {0x304f, CRL_REG_LEN_08BIT, 0x3d}, + {0x3050, CRL_REG_LEN_08BIT, 0x10}, + {0x3060, CRL_REG_LEN_08BIT, 0x00}, + {0x3061, CRL_REG_LEN_08BIT, 0x64}, + {0x3062, CRL_REG_LEN_08BIT, 0x00}, + {0x3063, CRL_REG_LEN_08BIT, 0xe4}, + {0x3066, CRL_REG_LEN_08BIT, 0x80}, + {0x3080, CRL_REG_LEN_08BIT, 0x00}, + {0x3081, CRL_REG_LEN_08BIT, 0x00}, + {0x3082, CRL_REG_LEN_08BIT, 0x01}, + {0x3083, CRL_REG_LEN_08BIT, 0xe3}, + {0x3084, CRL_REG_LEN_08BIT, 0x06}, + {0x3085, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x10}, + {0x3087, CRL_REG_LEN_08BIT, 0x10}, + {0x3089, CRL_REG_LEN_08BIT, 0x00}, + {0x308a, CRL_REG_LEN_08BIT, 0x01}, + {0x3093, CRL_REG_LEN_08BIT, 0x00}, + {0x30a0, CRL_REG_LEN_08BIT, 0x00}, + {0x30a1, CRL_REG_LEN_08BIT, 0x04}, + {0x30a2, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30a4, CRL_REG_LEN_08BIT, 0x07}, + {0x30a5, CRL_REG_LEN_08BIT, 0x8b}, + {0x30a6, CRL_REG_LEN_08BIT, 0x04}, + {0x30a7, CRL_REG_LEN_08BIT, 0x3f}, + {0x30a8, CRL_REG_LEN_08BIT, 0x00}, + {0x30a9, CRL_REG_LEN_08BIT, 0x04}, + {0x30aa, CRL_REG_LEN_08BIT, 0x00}, + {0x30ab, CRL_REG_LEN_08BIT, 0x00}, + {0x30ac, CRL_REG_LEN_08BIT, 0x07}, + {0x30ad, CRL_REG_LEN_08BIT, 0x80}, + {0x30ae, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x08}, + {0x30b1, CRL_REG_LEN_08BIT, 0x98}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x65}, + {0x30b4, CRL_REG_LEN_08BIT, 0x00}, + {0x30b5, CRL_REG_LEN_08BIT, 0x00}, + {0x30b6, CRL_REG_LEN_08BIT, 0x00}, + {0x30b7, CRL_REG_LEN_08BIT, 0x10}, + {0x30b8, CRL_REG_LEN_08BIT, 0x00}, + {0x30b9, CRL_REG_LEN_08BIT, 0x02}, + {0x30ba, CRL_REG_LEN_08BIT, 0x10}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x03}, + {0x30be, CRL_REG_LEN_08BIT, 0x5c}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x01}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x20}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x4a}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x00}, + {0x30d1, CRL_REG_LEN_08BIT, 0x00}, + {0x30d2, CRL_REG_LEN_08BIT, 0x00}, + {0x30d3, CRL_REG_LEN_08BIT, 0x80}, + {0x30d4, CRL_REG_LEN_08BIT, 0x00}, + {0x30d9, CRL_REG_LEN_08BIT, 0x09}, + {0x30da, CRL_REG_LEN_08BIT, 0x64}, + {0x30dd, CRL_REG_LEN_08BIT, 0x00}, + {0x30de, CRL_REG_LEN_08BIT, 0x16}, + {0x30df, CRL_REG_LEN_08BIT, 0x00}, + {0x30e0, CRL_REG_LEN_08BIT, 0x17}, + {0x30e1, CRL_REG_LEN_08BIT, 0x00}, + {0x30e2, CRL_REG_LEN_08BIT, 0x18}, + {0x30e3, CRL_REG_LEN_08BIT, 0x10}, + {0x30e4, CRL_REG_LEN_08BIT, 0x04}, + {0x30e5, CRL_REG_LEN_08BIT, 0x00}, + {0x30e6, CRL_REG_LEN_08BIT, 0x00}, + {0x30e7, CRL_REG_LEN_08BIT, 0x00}, + {0x30e8, CRL_REG_LEN_08BIT, 0x00}, + {0x30e9, CRL_REG_LEN_08BIT, 0x00}, + {0x30ea, CRL_REG_LEN_08BIT, 0x00}, + {0x30eb, CRL_REG_LEN_08BIT, 0x00}, + {0x30ec, CRL_REG_LEN_08BIT, 0x00}, + {0x30ed, CRL_REG_LEN_08BIT, 0x00}, + {0x3101, CRL_REG_LEN_08BIT, 0x00}, + {0x3102, CRL_REG_LEN_08BIT, 0x00}, + {0x3103, CRL_REG_LEN_08BIT, 0x00}, + {0x3104, CRL_REG_LEN_08BIT, 0x00}, + {0x3105, CRL_REG_LEN_08BIT, 0x8c}, + {0x3106, CRL_REG_LEN_08BIT, 0x87}, + {0x3107, CRL_REG_LEN_08BIT, 0xc0}, + {0x3108, CRL_REG_LEN_08BIT, 0x9d}, + {0x3109, CRL_REG_LEN_08BIT, 0x8d}, + {0x310a, CRL_REG_LEN_08BIT, 0x8d}, + {0x310b, CRL_REG_LEN_08BIT, 0x6a}, + {0x310c, CRL_REG_LEN_08BIT, 0x3a}, + {0x310d, CRL_REG_LEN_08BIT, 0x5a}, + {0x310e, CRL_REG_LEN_08BIT, 0x00}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x00}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3123, CRL_REG_LEN_08BIT, 0xf0}, + {0x3124, CRL_REG_LEN_08BIT, 0x00}, + {0x3125, CRL_REG_LEN_08BIT, 0x70}, + {0x3126, CRL_REG_LEN_08BIT, 0x1f}, + {0x3127, CRL_REG_LEN_08BIT, 0x0f}, + {0x3128, CRL_REG_LEN_08BIT, 0x00}, + {0x3129, CRL_REG_LEN_08BIT, 0x3a}, + {0x312a, CRL_REG_LEN_08BIT, 0x02}, + {0x312b, CRL_REG_LEN_08BIT, 0x0f}, + {0x312c, CRL_REG_LEN_08BIT, 0x00}, + {0x312d, CRL_REG_LEN_08BIT, 0x0f}, + {0x312e, CRL_REG_LEN_08BIT, 0x1d}, + {0x312f, CRL_REG_LEN_08BIT, 0x00}, + {0x3130, CRL_REG_LEN_08BIT, 0x00}, + {0x3131, CRL_REG_LEN_08BIT, 0x00}, + {0x3132, CRL_REG_LEN_08BIT, 0x00}, + {0x3140, CRL_REG_LEN_08BIT, 0x0a}, + {0x3141, CRL_REG_LEN_08BIT, 0x03}, + {0x3142, CRL_REG_LEN_08BIT, 0x00}, + {0x3143, CRL_REG_LEN_08BIT, 0x00}, + {0x3144, CRL_REG_LEN_08BIT, 0x00}, + {0x3145, CRL_REG_LEN_08BIT, 0x00}, + {0x3146, CRL_REG_LEN_08BIT, 0x00}, + {0x3147, CRL_REG_LEN_08BIT, 0x00}, + {0x3148, CRL_REG_LEN_08BIT, 0x00}, + {0x3149, CRL_REG_LEN_08BIT, 0x00}, + {0x314a, CRL_REG_LEN_08BIT, 0x00}, + {0x314b, CRL_REG_LEN_08BIT, 0x00}, + {0x314c, CRL_REG_LEN_08BIT, 0x00}, + {0x314d, CRL_REG_LEN_08BIT, 0x00}, + {0x314e, CRL_REG_LEN_08BIT, 0x1c}, + {0x314f, CRL_REG_LEN_08BIT, 0xff}, + {0x3150, CRL_REG_LEN_08BIT, 0xff}, + {0x3151, CRL_REG_LEN_08BIT, 0xff}, + {0x3152, CRL_REG_LEN_08BIT, 0x10}, + {0x3153, CRL_REG_LEN_08BIT, 0x10}, + {0x3154, CRL_REG_LEN_08BIT, 0x10}, + {0x3155, CRL_REG_LEN_08BIT, 0x00}, + {0x3156, CRL_REG_LEN_08BIT, 0x03}, + {0x3157, CRL_REG_LEN_08BIT, 0x00}, + {0x3158, CRL_REG_LEN_08BIT, 0x0f}, + {0x3159, CRL_REG_LEN_08BIT, 0xff}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3160, CRL_REG_LEN_08BIT, 0x01}, + {0x3161, CRL_REG_LEN_08BIT, 0x00}, + {0x3162, CRL_REG_LEN_08BIT, 0x01}, + {0x3163, CRL_REG_LEN_08BIT, 0x00}, + {0x3164, CRL_REG_LEN_08BIT, 0x01}, + {0x3165, CRL_REG_LEN_08BIT, 0x00}, + {0x3190, CRL_REG_LEN_08BIT, 0x08}, + {0x3191, CRL_REG_LEN_08BIT, 0x99}, + {0x3193, CRL_REG_LEN_08BIT, 0x08}, + {0x3194, CRL_REG_LEN_08BIT, 0x13}, + {0x3195, CRL_REG_LEN_08BIT, 0x33}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x10}, + {0x3198, CRL_REG_LEN_08BIT, 0x00}, + {0x3199, CRL_REG_LEN_08BIT, 0x7f}, + {0x319a, CRL_REG_LEN_08BIT, 0x80}, + {0x319b, CRL_REG_LEN_08BIT, 0xff}, + {0x319c, CRL_REG_LEN_08BIT, 0x80}, + {0x319d, CRL_REG_LEN_08BIT, 0xbf}, + {0x319e, CRL_REG_LEN_08BIT, 0xc0}, + {0x319f, CRL_REG_LEN_08BIT, 0xff}, + {0x31a0, CRL_REG_LEN_08BIT, 0x24}, + {0x31a1, CRL_REG_LEN_08BIT, 0x55}, + {0x31a2, CRL_REG_LEN_08BIT, 0x00}, + {0x31a3, CRL_REG_LEN_08BIT, 0x08}, + {0x31a6, CRL_REG_LEN_08BIT, 0x00}, + {0x31a7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b0, CRL_REG_LEN_08BIT, 0x00}, + {0x31b1, CRL_REG_LEN_08BIT, 0x00}, + {0x31b2, CRL_REG_LEN_08BIT, 0x02}, + {0x31b3, CRL_REG_LEN_08BIT, 0x00}, + {0x31b4, CRL_REG_LEN_08BIT, 0x00}, + {0x31b5, CRL_REG_LEN_08BIT, 0x01}, + {0x31b6, CRL_REG_LEN_08BIT, 0x00}, + {0x31b7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b8, CRL_REG_LEN_08BIT, 0x00}, + {0x31b9, CRL_REG_LEN_08BIT, 0x00}, + {0x31ba, CRL_REG_LEN_08BIT, 0x00}, + {0x31d0, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d1, CRL_REG_LEN_08BIT, 0x34}, + {0x31d2, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d3, CRL_REG_LEN_08BIT, 0x00}, + {0x31d4, CRL_REG_LEN_08BIT, 0x2d}, + {0x31d5, CRL_REG_LEN_08BIT, 0x00}, + {0x31d6, CRL_REG_LEN_08BIT, 0x01}, + {0x31d7, CRL_REG_LEN_08BIT, 0x06}, + {0x31d8, CRL_REG_LEN_08BIT, 0x00}, + {0x31d9, CRL_REG_LEN_08BIT, 0x64}, + {0x31da, CRL_REG_LEN_08BIT, 0x00}, + {0x31db, CRL_REG_LEN_08BIT, 0x30}, + {0x31dc, CRL_REG_LEN_08BIT, 0x04}, + {0x31dd, CRL_REG_LEN_08BIT, 0x69}, + {0x31de, CRL_REG_LEN_08BIT, 0x0a}, + {0x31df, CRL_REG_LEN_08BIT, 0x3c}, + {0x31e0, CRL_REG_LEN_08BIT, 0x04}, + {0x31e1, CRL_REG_LEN_08BIT, 0x32}, + {0x31e2, CRL_REG_LEN_08BIT, 0x00}, + {0x31e3, CRL_REG_LEN_08BIT, 0x00}, + {0x31e4, CRL_REG_LEN_08BIT, 0x08}, + {0x31e5, CRL_REG_LEN_08BIT, 0x80}, + {0x31e6, CRL_REG_LEN_08BIT, 0x00}, + {0x31e7, CRL_REG_LEN_08BIT, 0x2c}, + {0x31e8, CRL_REG_LEN_08BIT, 0x6c}, + {0x31e9, CRL_REG_LEN_08BIT, 0xac}, + {0x31ea, CRL_REG_LEN_08BIT, 0xec}, + {0x31eb, CRL_REG_LEN_08BIT, 0x3f}, + {0x31ec, CRL_REG_LEN_08BIT, 0x0f}, + {0x31ed, CRL_REG_LEN_08BIT, 0x20}, + {0x31ee, CRL_REG_LEN_08BIT, 0x04}, + {0x31ef, CRL_REG_LEN_08BIT, 0x48}, + {0x31f0, CRL_REG_LEN_08BIT, 0x07}, + {0x31f1, CRL_REG_LEN_08BIT, 0x90}, + {0x31f2, CRL_REG_LEN_08BIT, 0x04}, + {0x31f3, CRL_REG_LEN_08BIT, 0x48}, + {0x31f4, CRL_REG_LEN_08BIT, 0x07}, + {0x31f5, CRL_REG_LEN_08BIT, 0x90}, + {0x31f6, CRL_REG_LEN_08BIT, 0x04}, + {0x31f7, CRL_REG_LEN_08BIT, 0x48}, + {0x31f8, CRL_REG_LEN_08BIT, 0x07}, + {0x31f9, CRL_REG_LEN_08BIT, 0x90}, + {0x31fa, CRL_REG_LEN_08BIT, 0x04}, + {0x31fb, CRL_REG_LEN_08BIT, 0x48}, + {0x31fd, CRL_REG_LEN_08BIT, 0xcb}, + {0x31fe, CRL_REG_LEN_08BIT, 0x01}, + {0x31ff, CRL_REG_LEN_08BIT, 0x03}, + {0x3200, CRL_REG_LEN_08BIT, 0x00}, + {0x3201, CRL_REG_LEN_08BIT, 0xff}, + {0x3202, CRL_REG_LEN_08BIT, 0x00}, + {0x3203, CRL_REG_LEN_08BIT, 0xff}, + {0x3204, CRL_REG_LEN_08BIT, 0xff}, + {0x3205, CRL_REG_LEN_08BIT, 0xff}, + {0x3206, CRL_REG_LEN_08BIT, 0xff}, + {0x3207, CRL_REG_LEN_08BIT, 0xff}, + {0x3208, CRL_REG_LEN_08BIT, 0xff}, + {0x3209, CRL_REG_LEN_08BIT, 0xff}, + {0x320a, CRL_REG_LEN_08BIT, 0xff}, + {0x320b, CRL_REG_LEN_08BIT, 0x1b}, + {0x320c, CRL_REG_LEN_08BIT, 0x1f}, + {0x320d, CRL_REG_LEN_08BIT, 0x1e}, + {0x320e, CRL_REG_LEN_08BIT, 0x30}, + {0x320f, CRL_REG_LEN_08BIT, 0x2d}, + {0x3210, CRL_REG_LEN_08BIT, 0x2c}, + {0x3211, CRL_REG_LEN_08BIT, 0x2b}, + {0x3212, CRL_REG_LEN_08BIT, 0x2a}, + {0x3213, CRL_REG_LEN_08BIT, 0x24}, + {0x3214, CRL_REG_LEN_08BIT, 0x22}, + {0x3215, CRL_REG_LEN_08BIT, 0x00}, + {0x3216, CRL_REG_LEN_08BIT, 0x04}, + {0x3217, CRL_REG_LEN_08BIT, 0x2c}, + {0x3218, CRL_REG_LEN_08BIT, 0x6c}, + {0x3219, CRL_REG_LEN_08BIT, 0xac}, + {0x321a, CRL_REG_LEN_08BIT, 0xec}, + {0x321b, CRL_REG_LEN_08BIT, 0x00}, + {0x3230, CRL_REG_LEN_08BIT, 0x3a}, + {0x3231, CRL_REG_LEN_08BIT, 0x00}, + {0x3232, CRL_REG_LEN_08BIT, 0x80}, + {0x3233, CRL_REG_LEN_08BIT, 0x00}, + {0x3234, CRL_REG_LEN_08BIT, 0x10}, + {0x3235, CRL_REG_LEN_08BIT, 0xaa}, + {0x3236, CRL_REG_LEN_08BIT, 0x55}, + {0x3237, CRL_REG_LEN_08BIT, 0x99}, + {0x3238, CRL_REG_LEN_08BIT, 0x66}, + {0x3239, CRL_REG_LEN_08BIT, 0x08}, + {0x323a, CRL_REG_LEN_08BIT, 0x88}, + {0x323b, CRL_REG_LEN_08BIT, 0x00}, + {0x323c, CRL_REG_LEN_08BIT, 0x00}, + {0x323d, CRL_REG_LEN_08BIT, 0x03}, + {0x3250, CRL_REG_LEN_08BIT, 0x33}, + {0x3251, CRL_REG_LEN_08BIT, 0x00}, + {0x3252, CRL_REG_LEN_08BIT, 0x20}, + {0x3253, CRL_REG_LEN_08BIT, 0x00}, + {0x3254, CRL_REG_LEN_08BIT, 0x00}, + {0x3255, CRL_REG_LEN_08BIT, 0x01}, + {0x3256, CRL_REG_LEN_08BIT, 0x00}, + {0x3257, CRL_REG_LEN_08BIT, 0x00}, + {0x3258, CRL_REG_LEN_08BIT, 0x00}, + {0x3270, CRL_REG_LEN_08BIT, 0x01}, + {0x3271, CRL_REG_LEN_08BIT, 0xc0}, + {0x3272, CRL_REG_LEN_08BIT, 0xf0}, + {0x3273, CRL_REG_LEN_08BIT, 0x01}, + {0x3274, CRL_REG_LEN_08BIT, 0x00}, + {0x3275, CRL_REG_LEN_08BIT, 0x40}, + {0x3276, CRL_REG_LEN_08BIT, 0x02}, + {0x3277, CRL_REG_LEN_08BIT, 0x08}, + {0x3278, CRL_REG_LEN_08BIT, 0x10}, + {0x3279, CRL_REG_LEN_08BIT, 0x04}, + {0x327a, CRL_REG_LEN_08BIT, 0x00}, + {0x327b, CRL_REG_LEN_08BIT, 0x03}, + {0x327c, CRL_REG_LEN_08BIT, 0x10}, + {0x327d, CRL_REG_LEN_08BIT, 0x60}, + {0x327e, CRL_REG_LEN_08BIT, 0xc0}, + {0x327f, CRL_REG_LEN_08BIT, 0x06}, + {0x3288, CRL_REG_LEN_08BIT, 0x10}, + {0x3289, CRL_REG_LEN_08BIT, 0x00}, + {0x328a, CRL_REG_LEN_08BIT, 0x08}, + {0x328b, CRL_REG_LEN_08BIT, 0x00}, + {0x328c, CRL_REG_LEN_08BIT, 0x04}, + {0x328d, CRL_REG_LEN_08BIT, 0x00}, + {0x328e, CRL_REG_LEN_08BIT, 0x02}, + {0x328f, CRL_REG_LEN_08BIT, 0x00}, + {0x3290, CRL_REG_LEN_08BIT, 0x20}, + {0x3291, CRL_REG_LEN_08BIT, 0x00}, + {0x3292, CRL_REG_LEN_08BIT, 0x10}, + {0x3293, CRL_REG_LEN_08BIT, 0x00}, + {0x3294, CRL_REG_LEN_08BIT, 0x08}, + {0x3295, CRL_REG_LEN_08BIT, 0x00}, + {0x3296, CRL_REG_LEN_08BIT, 0x04}, + {0x3297, CRL_REG_LEN_08BIT, 0x00}, + {0x3298, CRL_REG_LEN_08BIT, 0x40}, + {0x3299, CRL_REG_LEN_08BIT, 0x00}, + {0x329a, CRL_REG_LEN_08BIT, 0x20}, + {0x329b, CRL_REG_LEN_08BIT, 0x00}, + {0x329c, CRL_REG_LEN_08BIT, 0x10}, + {0x329d, CRL_REG_LEN_08BIT, 0x00}, + {0x329e, CRL_REG_LEN_08BIT, 0x08}, + {0x329f, CRL_REG_LEN_08BIT, 0x00}, + {0x32a0, CRL_REG_LEN_08BIT, 0x7f}, + {0x32a1, CRL_REG_LEN_08BIT, 0xff}, + {0x32a2, CRL_REG_LEN_08BIT, 0x40}, + {0x32a3, CRL_REG_LEN_08BIT, 0x00}, + {0x32a4, CRL_REG_LEN_08BIT, 0x20}, + {0x32a5, CRL_REG_LEN_08BIT, 0x00}, + {0x32a6, CRL_REG_LEN_08BIT, 0x10}, + {0x32a7, CRL_REG_LEN_08BIT, 0x00}, + {0x32a8, CRL_REG_LEN_08BIT, 0x00}, + {0x32a9, CRL_REG_LEN_08BIT, 0x00}, + {0x32aa, CRL_REG_LEN_08BIT, 0x00}, + {0x32ab, CRL_REG_LEN_08BIT, 0x00}, + {0x32ac, CRL_REG_LEN_08BIT, 0x00}, + {0x32ad, CRL_REG_LEN_08BIT, 0x00}, + {0x32ae, CRL_REG_LEN_08BIT, 0x00}, + {0x32af, CRL_REG_LEN_08BIT, 0x00}, + {0x32b0, CRL_REG_LEN_08BIT, 0x00}, + {0x32b1, CRL_REG_LEN_08BIT, 0x00}, + {0x32b2, CRL_REG_LEN_08BIT, 0x00}, + {0x32b3, CRL_REG_LEN_08BIT, 0x00}, + {0x32b4, CRL_REG_LEN_08BIT, 0x00}, + {0x32b5, CRL_REG_LEN_08BIT, 0x00}, + {0x32b6, CRL_REG_LEN_08BIT, 0x00}, + {0x32b7, CRL_REG_LEN_08BIT, 0x00}, + {0x32b8, CRL_REG_LEN_08BIT, 0x00}, + {0x32b9, CRL_REG_LEN_08BIT, 0x00}, + {0x32ba, CRL_REG_LEN_08BIT, 0x00}, + {0x32bb, CRL_REG_LEN_08BIT, 0x00}, + {0x32bc, CRL_REG_LEN_08BIT, 0x00}, + {0x32bd, CRL_REG_LEN_08BIT, 0x00}, + {0x32be, CRL_REG_LEN_08BIT, 0x00}, + {0x32bf, CRL_REG_LEN_08BIT, 0x00}, + {0x32c0, CRL_REG_LEN_08BIT, 0x00}, + {0x32c1, CRL_REG_LEN_08BIT, 0x00}, + {0x32c2, CRL_REG_LEN_08BIT, 0x00}, + {0x32c3, CRL_REG_LEN_08BIT, 0x00}, + {0x32c4, CRL_REG_LEN_08BIT, 0x00}, + {0x32c5, CRL_REG_LEN_08BIT, 0x00}, + {0x32c6, CRL_REG_LEN_08BIT, 0x00}, + {0x32c7, CRL_REG_LEN_08BIT, 0x00}, + {0x32c8, CRL_REG_LEN_08BIT, 0x87}, + {0x32c9, CRL_REG_LEN_08BIT, 0x00}, + {0x3330, CRL_REG_LEN_08BIT, 0x03}, + {0x3331, CRL_REG_LEN_08BIT, 0xc8}, + {0x3332, CRL_REG_LEN_08BIT, 0x02}, + {0x3333, CRL_REG_LEN_08BIT, 0x24}, + {0x3334, CRL_REG_LEN_08BIT, 0x00}, + {0x3335, CRL_REG_LEN_08BIT, 0x00}, + {0x3336, CRL_REG_LEN_08BIT, 0x00}, + {0x3337, CRL_REG_LEN_08BIT, 0x00}, + {0x3338, CRL_REG_LEN_08BIT, 0x03}, + {0x3339, CRL_REG_LEN_08BIT, 0xc8}, + {0x333a, CRL_REG_LEN_08BIT, 0x02}, + {0x333b, CRL_REG_LEN_08BIT, 0x24}, + {0x333c, CRL_REG_LEN_08BIT, 0x00}, + {0x333d, CRL_REG_LEN_08BIT, 0x00}, + {0x333e, CRL_REG_LEN_08BIT, 0x00}, + {0x333f, CRL_REG_LEN_08BIT, 0x00}, + {0x3340, CRL_REG_LEN_08BIT, 0x03}, + {0x3341, CRL_REG_LEN_08BIT, 0xc8}, + {0x3342, CRL_REG_LEN_08BIT, 0x02}, + {0x3343, CRL_REG_LEN_08BIT, 0x24}, + {0x3344, CRL_REG_LEN_08BIT, 0x00}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3346, CRL_REG_LEN_08BIT, 0x00}, + {0x3347, CRL_REG_LEN_08BIT, 0x00}, + {0x3348, CRL_REG_LEN_08BIT, 0x40}, + {0x3349, CRL_REG_LEN_08BIT, 0x00}, + {0x334a, CRL_REG_LEN_08BIT, 0x00}, + {0x334b, CRL_REG_LEN_08BIT, 0x00}, + {0x334c, CRL_REG_LEN_08BIT, 0x00}, + {0x334d, CRL_REG_LEN_08BIT, 0x00}, + {0x334e, CRL_REG_LEN_08BIT, 0x80}, + {0x3360, CRL_REG_LEN_08BIT, 0x01}, + {0x3361, CRL_REG_LEN_08BIT, 0x00}, + {0x3362, CRL_REG_LEN_08BIT, 0x01}, + {0x3363, CRL_REG_LEN_08BIT, 0x00}, + {0x3364, CRL_REG_LEN_08BIT, 0x01}, + {0x3365, CRL_REG_LEN_08BIT, 0x00}, + {0x3366, CRL_REG_LEN_08BIT, 0x01}, + {0x3367, CRL_REG_LEN_08BIT, 0x00}, + {0x3368, CRL_REG_LEN_08BIT, 0x01}, + {0x3369, CRL_REG_LEN_08BIT, 0x00}, + {0x336a, CRL_REG_LEN_08BIT, 0x01}, + {0x336b, CRL_REG_LEN_08BIT, 0x00}, + {0x336c, CRL_REG_LEN_08BIT, 0x01}, + {0x336d, CRL_REG_LEN_08BIT, 0x00}, + {0x336e, CRL_REG_LEN_08BIT, 0x01}, + {0x336f, CRL_REG_LEN_08BIT, 0x00}, + {0x3370, CRL_REG_LEN_08BIT, 0x01}, + {0x3371, CRL_REG_LEN_08BIT, 0x00}, + {0x3372, CRL_REG_LEN_08BIT, 0x01}, + {0x3373, CRL_REG_LEN_08BIT, 0x00}, + {0x3374, CRL_REG_LEN_08BIT, 0x01}, + {0x3375, CRL_REG_LEN_08BIT, 0x00}, + {0x3376, CRL_REG_LEN_08BIT, 0x01}, + {0x3377, CRL_REG_LEN_08BIT, 0x00}, + {0x3378, CRL_REG_LEN_08BIT, 0x00}, + {0x3379, CRL_REG_LEN_08BIT, 0x00}, + {0x337a, CRL_REG_LEN_08BIT, 0x00}, + {0x337b, CRL_REG_LEN_08BIT, 0x00}, + {0x337c, CRL_REG_LEN_08BIT, 0x00}, + {0x337d, CRL_REG_LEN_08BIT, 0x00}, + {0x337e, CRL_REG_LEN_08BIT, 0x00}, + {0x337f, CRL_REG_LEN_08BIT, 0x00}, + {0x3380, CRL_REG_LEN_08BIT, 0x00}, + {0x3381, CRL_REG_LEN_08BIT, 0x00}, + {0x3382, CRL_REG_LEN_08BIT, 0x00}, + {0x3383, CRL_REG_LEN_08BIT, 0x00}, + {0x3384, CRL_REG_LEN_08BIT, 0x00}, + {0x3385, CRL_REG_LEN_08BIT, 0x00}, + {0x3386, CRL_REG_LEN_08BIT, 0x00}, + {0x3387, CRL_REG_LEN_08BIT, 0x00}, + {0x3388, CRL_REG_LEN_08BIT, 0x00}, + {0x3389, CRL_REG_LEN_08BIT, 0x00}, + {0x338a, CRL_REG_LEN_08BIT, 0x00}, + {0x338b, CRL_REG_LEN_08BIT, 0x00}, + {0x338c, CRL_REG_LEN_08BIT, 0x00}, + {0x338d, CRL_REG_LEN_08BIT, 0x00}, + {0x338e, CRL_REG_LEN_08BIT, 0x00}, + {0x338f, CRL_REG_LEN_08BIT, 0x00}, + {0x3390, CRL_REG_LEN_08BIT, 0x00}, + {0x3391, CRL_REG_LEN_08BIT, 0x00}, + {0x3392, CRL_REG_LEN_08BIT, 0x00}, + {0x3393, CRL_REG_LEN_08BIT, 0x00}, + {0x3394, CRL_REG_LEN_08BIT, 0x00}, + {0x3395, CRL_REG_LEN_08BIT, 0x00}, + {0x3396, CRL_REG_LEN_08BIT, 0x00}, + {0x3397, CRL_REG_LEN_08BIT, 0x00}, + {0x3398, CRL_REG_LEN_08BIT, 0x00}, + {0x3399, CRL_REG_LEN_08BIT, 0x00}, + {0x339a, CRL_REG_LEN_08BIT, 0x00}, + {0x339b, CRL_REG_LEN_08BIT, 0x00}, + {0x33b0, CRL_REG_LEN_08BIT, 0x00}, + {0x33b1, CRL_REG_LEN_08BIT, 0x50}, + {0x33b2, CRL_REG_LEN_08BIT, 0x01}, + {0x33b3, CRL_REG_LEN_08BIT, 0xff}, + {0x33b4, CRL_REG_LEN_08BIT, 0xe0}, + {0x33b5, CRL_REG_LEN_08BIT, 0x6b}, + {0x33b6, CRL_REG_LEN_08BIT, 0x00}, + {0x33b7, CRL_REG_LEN_08BIT, 0x00}, + {0x33b8, CRL_REG_LEN_08BIT, 0x00}, + {0x33b9, CRL_REG_LEN_08BIT, 0x00}, + {0x33ba, CRL_REG_LEN_08BIT, 0x00}, + {0x33bb, CRL_REG_LEN_08BIT, 0x1f}, + {0x33bc, CRL_REG_LEN_08BIT, 0x01}, + {0x33bd, CRL_REG_LEN_08BIT, 0x01}, + {0x33be, CRL_REG_LEN_08BIT, 0x01}, + {0x33bf, CRL_REG_LEN_08BIT, 0x01}, + {0x33c0, CRL_REG_LEN_08BIT, 0x00}, + {0x33c1, CRL_REG_LEN_08BIT, 0x00}, + {0x33c2, CRL_REG_LEN_08BIT, 0x00}, + {0x33c3, CRL_REG_LEN_08BIT, 0x00}, + {0x33e0, CRL_REG_LEN_08BIT, 0x14}, + {0x33e1, CRL_REG_LEN_08BIT, 0x0f}, + {0x33e2, CRL_REG_LEN_08BIT, 0x02}, + {0x33e3, CRL_REG_LEN_08BIT, 0x01}, + {0x33e4, CRL_REG_LEN_08BIT, 0x01}, + {0x33e5, CRL_REG_LEN_08BIT, 0x01}, + {0x33e6, CRL_REG_LEN_08BIT, 0x00}, + {0x33e7, CRL_REG_LEN_08BIT, 0x04}, + {0x33e8, CRL_REG_LEN_08BIT, 0x0c}, + {0x33e9, CRL_REG_LEN_08BIT, 0x02}, + {0x33ea, CRL_REG_LEN_08BIT, 0x02}, + {0x33eb, CRL_REG_LEN_08BIT, 0x02}, + {0x33ec, CRL_REG_LEN_08BIT, 0x03}, + {0x33ed, CRL_REG_LEN_08BIT, 0x01}, + {0x33ee, CRL_REG_LEN_08BIT, 0x02}, + {0x33ef, CRL_REG_LEN_08BIT, 0x08}, + {0x33f0, CRL_REG_LEN_08BIT, 0x08}, + {0x33f1, CRL_REG_LEN_08BIT, 0x04}, + {0x33f2, CRL_REG_LEN_08BIT, 0x04}, + {0x33f3, CRL_REG_LEN_08BIT, 0x00}, + {0x33f4, CRL_REG_LEN_08BIT, 0x03}, + {0x33f5, CRL_REG_LEN_08BIT, 0x14}, + {0x33f6, CRL_REG_LEN_08BIT, 0x0f}, + {0x33f7, CRL_REG_LEN_08BIT, 0x02}, + {0x33f8, CRL_REG_LEN_08BIT, 0x01}, + {0x33f9, CRL_REG_LEN_08BIT, 0x01}, + {0x33fa, CRL_REG_LEN_08BIT, 0x01}, + {0x33fb, CRL_REG_LEN_08BIT, 0x00}, + {0x33fc, CRL_REG_LEN_08BIT, 0x04}, + {0x33fd, CRL_REG_LEN_08BIT, 0x0c}, + {0x33fe, CRL_REG_LEN_08BIT, 0x02}, + {0x33ff, CRL_REG_LEN_08BIT, 0x02}, + {0x3400, CRL_REG_LEN_08BIT, 0x02}, + {0x3401, CRL_REG_LEN_08BIT, 0x03}, + {0x3402, CRL_REG_LEN_08BIT, 0x01}, + {0x3403, CRL_REG_LEN_08BIT, 0x02}, + {0x3404, CRL_REG_LEN_08BIT, 0x08}, + {0x3405, CRL_REG_LEN_08BIT, 0x08}, + {0x3406, CRL_REG_LEN_08BIT, 0x04}, + {0x3407, CRL_REG_LEN_08BIT, 0x04}, + {0x3408, CRL_REG_LEN_08BIT, 0x00}, + {0x3409, CRL_REG_LEN_08BIT, 0x03}, + {0x340a, CRL_REG_LEN_08BIT, 0x14}, + {0x340b, CRL_REG_LEN_08BIT, 0x0f}, + {0x340c, CRL_REG_LEN_08BIT, 0x04}, + {0x340d, CRL_REG_LEN_08BIT, 0x02}, + {0x340e, CRL_REG_LEN_08BIT, 0x01}, + {0x340f, CRL_REG_LEN_08BIT, 0x01}, + {0x3410, CRL_REG_LEN_08BIT, 0x00}, + {0x3411, CRL_REG_LEN_08BIT, 0x04}, + {0x3412, CRL_REG_LEN_08BIT, 0x0c}, + {0x3413, CRL_REG_LEN_08BIT, 0x02}, + {0x3414, CRL_REG_LEN_08BIT, 0x02}, + {0x3415, CRL_REG_LEN_08BIT, 0x02}, + {0x3416, CRL_REG_LEN_08BIT, 0x03}, + {0x3417, CRL_REG_LEN_08BIT, 0x02}, + {0x3418, CRL_REG_LEN_08BIT, 0x05}, + {0x3419, CRL_REG_LEN_08BIT, 0x0a}, + {0x341a, CRL_REG_LEN_08BIT, 0x08}, + {0x341b, CRL_REG_LEN_08BIT, 0x04}, + {0x341c, CRL_REG_LEN_08BIT, 0x04}, + {0x341d, CRL_REG_LEN_08BIT, 0x00}, + {0x341e, CRL_REG_LEN_08BIT, 0x03}, + {0x3440, CRL_REG_LEN_08BIT, 0x00}, + {0x3441, CRL_REG_LEN_08BIT, 0x00}, + {0x3442, CRL_REG_LEN_08BIT, 0x00}, + {0x3443, CRL_REG_LEN_08BIT, 0x00}, + {0x3444, CRL_REG_LEN_08BIT, 0x02}, + {0x3445, CRL_REG_LEN_08BIT, 0xf0}, + {0x3446, CRL_REG_LEN_08BIT, 0x02}, + {0x3447, CRL_REG_LEN_08BIT, 0x08}, + {0x3448, CRL_REG_LEN_08BIT, 0x00}, + {0x3460, CRL_REG_LEN_08BIT, 0x40}, + {0x3461, CRL_REG_LEN_08BIT, 0x40}, + {0x3462, CRL_REG_LEN_08BIT, 0x40}, + {0x3463, CRL_REG_LEN_08BIT, 0x40}, + {0x3464, CRL_REG_LEN_08BIT, 0x03}, + {0x3465, CRL_REG_LEN_08BIT, 0x01}, + {0x3466, CRL_REG_LEN_08BIT, 0x01}, + {0x3467, CRL_REG_LEN_08BIT, 0x02}, + {0x3468, CRL_REG_LEN_08BIT, 0x30}, + {0x3469, CRL_REG_LEN_08BIT, 0x00}, + {0x346a, CRL_REG_LEN_08BIT, 0x33}, + {0x346b, CRL_REG_LEN_08BIT, 0xbf}, + {0x3480, CRL_REG_LEN_08BIT, 0x40}, + {0x3481, CRL_REG_LEN_08BIT, 0x00}, + {0x3482, CRL_REG_LEN_08BIT, 0x00}, + {0x3483, CRL_REG_LEN_08BIT, 0x00}, + {0x3484, CRL_REG_LEN_08BIT, 0x0d}, + {0x3485, CRL_REG_LEN_08BIT, 0x00}, + {0x3486, CRL_REG_LEN_08BIT, 0x00}, + {0x3487, CRL_REG_LEN_08BIT, 0x00}, + {0x3488, CRL_REG_LEN_08BIT, 0x00}, + {0x3489, CRL_REG_LEN_08BIT, 0x00}, + {0x348a, CRL_REG_LEN_08BIT, 0x00}, + {0x348b, CRL_REG_LEN_08BIT, 0x04}, + {0x348c, CRL_REG_LEN_08BIT, 0x00}, + {0x348d, CRL_REG_LEN_08BIT, 0x01}, + {0x348f, CRL_REG_LEN_08BIT, 0x01}, + {0x3030, CRL_REG_LEN_08BIT, 0x0a}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x7000, CRL_REG_LEN_08BIT, 0x58}, + {0x7001, CRL_REG_LEN_08BIT, 0x7a}, + {0x7002, CRL_REG_LEN_08BIT, 0x1a}, + {0x7003, CRL_REG_LEN_08BIT, 0xc1}, + {0x7004, CRL_REG_LEN_08BIT, 0x03}, + {0x7005, CRL_REG_LEN_08BIT, 0xda}, + {0x7006, CRL_REG_LEN_08BIT, 0xbd}, + {0x7007, CRL_REG_LEN_08BIT, 0x03}, + {0x7008, CRL_REG_LEN_08BIT, 0xbd}, + {0x7009, CRL_REG_LEN_08BIT, 0x06}, + {0x700a, CRL_REG_LEN_08BIT, 0xe6}, + {0x700b, CRL_REG_LEN_08BIT, 0xec}, + {0x700c, CRL_REG_LEN_08BIT, 0xbc}, + {0x700d, CRL_REG_LEN_08BIT, 0xff}, + {0x700e, CRL_REG_LEN_08BIT, 0xbc}, + {0x700f, CRL_REG_LEN_08BIT, 0x73}, + {0x7010, CRL_REG_LEN_08BIT, 0xda}, + {0x7011, CRL_REG_LEN_08BIT, 0x72}, + {0x7012, CRL_REG_LEN_08BIT, 0x76}, + {0x7013, CRL_REG_LEN_08BIT, 0xb6}, + {0x7014, CRL_REG_LEN_08BIT, 0xee}, + {0x7015, CRL_REG_LEN_08BIT, 0xcf}, + {0x7016, CRL_REG_LEN_08BIT, 0xac}, + {0x7017, CRL_REG_LEN_08BIT, 0xd0}, + {0x7018, CRL_REG_LEN_08BIT, 0xac}, + {0x7019, CRL_REG_LEN_08BIT, 0xd1}, + {0x701a, CRL_REG_LEN_08BIT, 0x50}, + {0x701b, CRL_REG_LEN_08BIT, 0xac}, + {0x701c, CRL_REG_LEN_08BIT, 0xd2}, + {0x701d, CRL_REG_LEN_08BIT, 0xbc}, + {0x701e, CRL_REG_LEN_08BIT, 0x2e}, + {0x701f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7020, CRL_REG_LEN_08BIT, 0x00}, + {0x7021, CRL_REG_LEN_08BIT, 0xdc}, + {0x7022, CRL_REG_LEN_08BIT, 0xdf}, + {0x7023, CRL_REG_LEN_08BIT, 0xb0}, + {0x7024, CRL_REG_LEN_08BIT, 0x6e}, + {0x7025, CRL_REG_LEN_08BIT, 0xbd}, + {0x7026, CRL_REG_LEN_08BIT, 0x01}, + {0x7027, CRL_REG_LEN_08BIT, 0xd7}, + {0x7028, CRL_REG_LEN_08BIT, 0xed}, + {0x7029, CRL_REG_LEN_08BIT, 0xe1}, + {0x702a, CRL_REG_LEN_08BIT, 0x36}, + {0x702b, CRL_REG_LEN_08BIT, 0x30}, + {0x702c, CRL_REG_LEN_08BIT, 0xd3}, + {0x702d, CRL_REG_LEN_08BIT, 0x2e}, + {0x702e, CRL_REG_LEN_08BIT, 0x54}, + {0x702f, CRL_REG_LEN_08BIT, 0x46}, + {0x7030, CRL_REG_LEN_08BIT, 0xbc}, + {0x7031, CRL_REG_LEN_08BIT, 0x22}, + {0x7032, CRL_REG_LEN_08BIT, 0x66}, + {0x7033, CRL_REG_LEN_08BIT, 0xbc}, + {0x7034, CRL_REG_LEN_08BIT, 0x24}, + {0x7035, CRL_REG_LEN_08BIT, 0x2c}, + {0x7036, CRL_REG_LEN_08BIT, 0x28}, + {0x7037, CRL_REG_LEN_08BIT, 0xbc}, + {0x7038, CRL_REG_LEN_08BIT, 0x3c}, + {0x7039, CRL_REG_LEN_08BIT, 0xa1}, + {0x703a, CRL_REG_LEN_08BIT, 0xac}, + {0x703b, CRL_REG_LEN_08BIT, 0xd8}, + {0x703c, CRL_REG_LEN_08BIT, 0xd6}, + {0x703d, CRL_REG_LEN_08BIT, 0xb4}, + {0x703e, CRL_REG_LEN_08BIT, 0x04}, + {0x703f, CRL_REG_LEN_08BIT, 0x46}, + {0x7040, CRL_REG_LEN_08BIT, 0xb7}, + {0x7041, CRL_REG_LEN_08BIT, 0x04}, + {0x7042, CRL_REG_LEN_08BIT, 0xbe}, + {0x7043, CRL_REG_LEN_08BIT, 0x08}, + {0x7044, CRL_REG_LEN_08BIT, 0xc3}, + {0x7045, CRL_REG_LEN_08BIT, 0xd9}, + {0x7046, CRL_REG_LEN_08BIT, 0xad}, + {0x7047, CRL_REG_LEN_08BIT, 0xc3}, + {0x7048, CRL_REG_LEN_08BIT, 0xbc}, + {0x7049, CRL_REG_LEN_08BIT, 0x19}, + {0x704a, CRL_REG_LEN_08BIT, 0xc1}, + {0x704b, CRL_REG_LEN_08BIT, 0x27}, + {0x704c, CRL_REG_LEN_08BIT, 0xe7}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x704e, CRL_REG_LEN_08BIT, 0x50}, + {0x704f, CRL_REG_LEN_08BIT, 0x20}, + {0x7050, CRL_REG_LEN_08BIT, 0xb8}, + {0x7051, CRL_REG_LEN_08BIT, 0x02}, + {0x7052, CRL_REG_LEN_08BIT, 0xbc}, + {0x7053, CRL_REG_LEN_08BIT, 0x17}, + {0x7054, CRL_REG_LEN_08BIT, 0xdb}, + {0x7055, CRL_REG_LEN_08BIT, 0xc7}, + {0x7056, CRL_REG_LEN_08BIT, 0xb8}, + {0x7057, CRL_REG_LEN_08BIT, 0x00}, + {0x7058, CRL_REG_LEN_08BIT, 0x28}, + {0x7059, CRL_REG_LEN_08BIT, 0x54}, + {0x705a, CRL_REG_LEN_08BIT, 0xb4}, + {0x705b, CRL_REG_LEN_08BIT, 0x14}, + {0x705c, CRL_REG_LEN_08BIT, 0xab}, + {0x705d, CRL_REG_LEN_08BIT, 0xbe}, + {0x705e, CRL_REG_LEN_08BIT, 0x06}, + {0x705f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7060, CRL_REG_LEN_08BIT, 0xd6}, + {0x7061, CRL_REG_LEN_08BIT, 0x00}, + {0x7062, CRL_REG_LEN_08BIT, 0xb4}, + {0x7063, CRL_REG_LEN_08BIT, 0xc7}, + {0x7064, CRL_REG_LEN_08BIT, 0x07}, + {0x7065, CRL_REG_LEN_08BIT, 0xb9}, + {0x7066, CRL_REG_LEN_08BIT, 0x05}, + {0x7067, CRL_REG_LEN_08BIT, 0xee}, + {0x7068, CRL_REG_LEN_08BIT, 0xe6}, + {0x7069, CRL_REG_LEN_08BIT, 0xad}, + {0x706a, CRL_REG_LEN_08BIT, 0xb4}, + {0x706b, CRL_REG_LEN_08BIT, 0x26}, + {0x706c, CRL_REG_LEN_08BIT, 0x19}, + {0x706d, CRL_REG_LEN_08BIT, 0xc1}, + {0x706e, CRL_REG_LEN_08BIT, 0x3a}, + {0x706f, CRL_REG_LEN_08BIT, 0xc3}, + {0x7070, CRL_REG_LEN_08BIT, 0xaf}, + {0x7071, CRL_REG_LEN_08BIT, 0x00}, + {0x7072, CRL_REG_LEN_08BIT, 0xc0}, + {0x7073, CRL_REG_LEN_08BIT, 0x3c}, + {0x7074, CRL_REG_LEN_08BIT, 0xc3}, + {0x7075, CRL_REG_LEN_08BIT, 0xbe}, + {0x7076, CRL_REG_LEN_08BIT, 0xe7}, + {0x7077, CRL_REG_LEN_08BIT, 0x00}, + {0x7078, CRL_REG_LEN_08BIT, 0x15}, + {0x7079, CRL_REG_LEN_08BIT, 0xc2}, + {0x707a, CRL_REG_LEN_08BIT, 0x40}, + {0x707b, CRL_REG_LEN_08BIT, 0xc3}, + {0x707c, CRL_REG_LEN_08BIT, 0xa4}, + {0x707d, CRL_REG_LEN_08BIT, 0xc0}, + {0x707e, CRL_REG_LEN_08BIT, 0x3c}, + {0x707f, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0xb9}, + {0x7081, CRL_REG_LEN_08BIT, 0x64}, + {0x7082, CRL_REG_LEN_08BIT, 0x29}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7084, CRL_REG_LEN_08BIT, 0xb8}, + {0x7085, CRL_REG_LEN_08BIT, 0x12}, + {0x7086, CRL_REG_LEN_08BIT, 0xbe}, + {0x7087, CRL_REG_LEN_08BIT, 0x01}, + {0x7088, CRL_REG_LEN_08BIT, 0xd0}, + {0x7089, CRL_REG_LEN_08BIT, 0xbc}, + {0x708a, CRL_REG_LEN_08BIT, 0x01}, + {0x708b, CRL_REG_LEN_08BIT, 0xac}, + {0x708c, CRL_REG_LEN_08BIT, 0x37}, + {0x708d, CRL_REG_LEN_08BIT, 0xd2}, + {0x708e, CRL_REG_LEN_08BIT, 0xac}, + {0x708f, CRL_REG_LEN_08BIT, 0x45}, + {0x7090, CRL_REG_LEN_08BIT, 0xad}, + {0x7091, CRL_REG_LEN_08BIT, 0x28}, + {0x7092, CRL_REG_LEN_08BIT, 0x00}, + {0x7093, CRL_REG_LEN_08BIT, 0xb8}, + {0x7094, CRL_REG_LEN_08BIT, 0x00}, + {0x7095, CRL_REG_LEN_08BIT, 0xbc}, + {0x7096, CRL_REG_LEN_08BIT, 0x01}, + {0x7097, CRL_REG_LEN_08BIT, 0x36}, + {0x7098, CRL_REG_LEN_08BIT, 0xd3}, + {0x7099, CRL_REG_LEN_08BIT, 0x30}, + {0x709a, CRL_REG_LEN_08BIT, 0x04}, + {0x709b, CRL_REG_LEN_08BIT, 0xe0}, + {0x709c, CRL_REG_LEN_08BIT, 0xd8}, + {0x709d, CRL_REG_LEN_08BIT, 0xb4}, + {0x709e, CRL_REG_LEN_08BIT, 0xe9}, + {0x709f, CRL_REG_LEN_08BIT, 0x00}, + {0x70a0, CRL_REG_LEN_08BIT, 0xbe}, + {0x70a1, CRL_REG_LEN_08BIT, 0x05}, + {0x70a2, CRL_REG_LEN_08BIT, 0x62}, + {0x70a3, CRL_REG_LEN_08BIT, 0x07}, + {0x70a4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70a5, CRL_REG_LEN_08BIT, 0x05}, + {0x70a6, CRL_REG_LEN_08BIT, 0xad}, + {0x70a7, CRL_REG_LEN_08BIT, 0xc3}, + {0x70a8, CRL_REG_LEN_08BIT, 0xcf}, + {0x70a9, CRL_REG_LEN_08BIT, 0x00}, + {0x70aa, CRL_REG_LEN_08BIT, 0x15}, + {0x70ab, CRL_REG_LEN_08BIT, 0xc2}, + {0x70ac, CRL_REG_LEN_08BIT, 0x59}, + {0x70ad, CRL_REG_LEN_08BIT, 0xc3}, + {0x70ae, CRL_REG_LEN_08BIT, 0xc9}, + {0x70af, CRL_REG_LEN_08BIT, 0xc0}, + {0x70b0, CRL_REG_LEN_08BIT, 0x55}, + {0x70b1, CRL_REG_LEN_08BIT, 0x00}, + {0x70b2, CRL_REG_LEN_08BIT, 0x46}, + {0x70b3, CRL_REG_LEN_08BIT, 0xa1}, + {0x70b4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70b5, CRL_REG_LEN_08BIT, 0x64}, + {0x70b6, CRL_REG_LEN_08BIT, 0x29}, + {0x70b7, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70b9, CRL_REG_LEN_08BIT, 0x02}, + {0x70ba, CRL_REG_LEN_08BIT, 0xbe}, + {0x70bb, CRL_REG_LEN_08BIT, 0x02}, + {0x70bc, CRL_REG_LEN_08BIT, 0xd0}, + {0x70bd, CRL_REG_LEN_08BIT, 0xdc}, + {0x70be, CRL_REG_LEN_08BIT, 0xac}, + {0x70bf, CRL_REG_LEN_08BIT, 0xbc}, + {0x70c0, CRL_REG_LEN_08BIT, 0x01}, + {0x70c1, CRL_REG_LEN_08BIT, 0x37}, + {0x70c2, CRL_REG_LEN_08BIT, 0xac}, + {0x70c3, CRL_REG_LEN_08BIT, 0xd2}, + {0x70c4, CRL_REG_LEN_08BIT, 0x45}, + {0x70c5, CRL_REG_LEN_08BIT, 0xad}, + {0x70c6, CRL_REG_LEN_08BIT, 0x28}, + {0x70c7, CRL_REG_LEN_08BIT, 0x00}, + {0x70c8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70c9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ca, CRL_REG_LEN_08BIT, 0xbc}, + {0x70cb, CRL_REG_LEN_08BIT, 0x01}, + {0x70cc, CRL_REG_LEN_08BIT, 0x36}, + {0x70cd, CRL_REG_LEN_08BIT, 0x30}, + {0x70ce, CRL_REG_LEN_08BIT, 0xe0}, + {0x70cf, CRL_REG_LEN_08BIT, 0xd8}, + {0x70d0, CRL_REG_LEN_08BIT, 0xb5}, + {0x70d1, CRL_REG_LEN_08BIT, 0x0b}, + {0x70d2, CRL_REG_LEN_08BIT, 0xd6}, + {0x70d3, CRL_REG_LEN_08BIT, 0xbe}, + {0x70d4, CRL_REG_LEN_08BIT, 0x07}, + {0x70d5, CRL_REG_LEN_08BIT, 0x00}, + {0x70d6, CRL_REG_LEN_08BIT, 0x62}, + {0x70d7, CRL_REG_LEN_08BIT, 0x07}, + {0x70d8, CRL_REG_LEN_08BIT, 0xb9}, + {0x70d9, CRL_REG_LEN_08BIT, 0x05}, + {0x70da, CRL_REG_LEN_08BIT, 0xad}, + {0x70db, CRL_REG_LEN_08BIT, 0xc3}, + {0x70dc, CRL_REG_LEN_08BIT, 0xcf}, + {0x70dd, CRL_REG_LEN_08BIT, 0x46}, + {0x70de, CRL_REG_LEN_08BIT, 0xcd}, + {0x70df, CRL_REG_LEN_08BIT, 0x07}, + {0x70e0, CRL_REG_LEN_08BIT, 0xcd}, + {0x70e1, CRL_REG_LEN_08BIT, 0x00}, + {0x70e2, CRL_REG_LEN_08BIT, 0xe3}, + {0x70e3, CRL_REG_LEN_08BIT, 0x18}, + {0x70e4, CRL_REG_LEN_08BIT, 0xc2}, + {0x70e5, CRL_REG_LEN_08BIT, 0xa2}, + {0x70e6, CRL_REG_LEN_08BIT, 0xb9}, + {0x70e7, CRL_REG_LEN_08BIT, 0x64}, + {0x70e8, CRL_REG_LEN_08BIT, 0xd1}, + {0x70e9, CRL_REG_LEN_08BIT, 0xdd}, + {0x70ea, CRL_REG_LEN_08BIT, 0xac}, + {0x70eb, CRL_REG_LEN_08BIT, 0xcf}, + {0x70ec, CRL_REG_LEN_08BIT, 0xdf}, + {0x70ed, CRL_REG_LEN_08BIT, 0xb5}, + {0x70ee, CRL_REG_LEN_08BIT, 0x19}, + {0x70ef, CRL_REG_LEN_08BIT, 0x46}, + {0x70f0, CRL_REG_LEN_08BIT, 0x50}, + {0x70f1, CRL_REG_LEN_08BIT, 0xb6}, + {0x70f2, CRL_REG_LEN_08BIT, 0xee}, + {0x70f3, CRL_REG_LEN_08BIT, 0xe8}, + {0x70f4, CRL_REG_LEN_08BIT, 0xe6}, + {0x70f5, CRL_REG_LEN_08BIT, 0xbc}, + {0x70f6, CRL_REG_LEN_08BIT, 0x31}, + {0x70f7, CRL_REG_LEN_08BIT, 0xe1}, + {0x70f8, CRL_REG_LEN_08BIT, 0x36}, + {0x70f9, CRL_REG_LEN_08BIT, 0x30}, + {0x70fa, CRL_REG_LEN_08BIT, 0xd3}, + {0x70fb, CRL_REG_LEN_08BIT, 0x2e}, + {0x70fc, CRL_REG_LEN_08BIT, 0x54}, + {0x70fd, CRL_REG_LEN_08BIT, 0xbd}, + {0x70fe, CRL_REG_LEN_08BIT, 0x03}, + {0x70ff, CRL_REG_LEN_08BIT, 0xec}, + {0x7100, CRL_REG_LEN_08BIT, 0x2c}, + {0x7101, CRL_REG_LEN_08BIT, 0x50}, + {0x7102, CRL_REG_LEN_08BIT, 0x20}, + {0x7103, CRL_REG_LEN_08BIT, 0x04}, + {0x7104, CRL_REG_LEN_08BIT, 0xb8}, + {0x7105, CRL_REG_LEN_08BIT, 0x02}, + {0x7106, CRL_REG_LEN_08BIT, 0xbc}, + {0x7107, CRL_REG_LEN_08BIT, 0x18}, + {0x7108, CRL_REG_LEN_08BIT, 0xc7}, + {0x7109, CRL_REG_LEN_08BIT, 0xb8}, + {0x710a, CRL_REG_LEN_08BIT, 0x00}, + {0x710b, CRL_REG_LEN_08BIT, 0x28}, + {0x710c, CRL_REG_LEN_08BIT, 0x54}, + {0x710d, CRL_REG_LEN_08BIT, 0xbc}, + {0x710e, CRL_REG_LEN_08BIT, 0x02}, + {0x710f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7110, CRL_REG_LEN_08BIT, 0xda}, + {0x7111, CRL_REG_LEN_08BIT, 0xbe}, + {0x7112, CRL_REG_LEN_08BIT, 0x04}, + {0x7113, CRL_REG_LEN_08BIT, 0xd6}, + {0x7114, CRL_REG_LEN_08BIT, 0xd8}, + {0x7115, CRL_REG_LEN_08BIT, 0xab}, + {0x7116, CRL_REG_LEN_08BIT, 0x00}, + {0x7117, CRL_REG_LEN_08BIT, 0x62}, + {0x7118, CRL_REG_LEN_08BIT, 0x07}, + {0x7119, CRL_REG_LEN_08BIT, 0xb9}, + {0x711a, CRL_REG_LEN_08BIT, 0x05}, + {0x711b, CRL_REG_LEN_08BIT, 0xad}, + {0x711c, CRL_REG_LEN_08BIT, 0xc3}, + {0x711d, CRL_REG_LEN_08BIT, 0xbc}, + {0x711e, CRL_REG_LEN_08BIT, 0xe7}, + {0x711f, CRL_REG_LEN_08BIT, 0xb9}, + {0x7120, CRL_REG_LEN_08BIT, 0x64}, + {0x7121, CRL_REG_LEN_08BIT, 0x29}, + {0x7122, CRL_REG_LEN_08BIT, 0x00}, + {0x7123, CRL_REG_LEN_08BIT, 0xb8}, + {0x7124, CRL_REG_LEN_08BIT, 0x02}, + {0x7125, CRL_REG_LEN_08BIT, 0xbe}, + {0x7126, CRL_REG_LEN_08BIT, 0x00}, + {0x7127, CRL_REG_LEN_08BIT, 0x45}, + {0x7128, CRL_REG_LEN_08BIT, 0xad}, + {0x7129, CRL_REG_LEN_08BIT, 0xe2}, + {0x712a, CRL_REG_LEN_08BIT, 0x28}, + {0x712b, CRL_REG_LEN_08BIT, 0x00}, + {0x712c, CRL_REG_LEN_08BIT, 0xb8}, + {0x712d, CRL_REG_LEN_08BIT, 0x00}, + {0x712e, CRL_REG_LEN_08BIT, 0xe0}, + {0x712f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7130, CRL_REG_LEN_08BIT, 0xb4}, + {0x7131, CRL_REG_LEN_08BIT, 0xe9}, + {0x7132, CRL_REG_LEN_08BIT, 0xbe}, + {0x7133, CRL_REG_LEN_08BIT, 0x03}, + {0x7134, CRL_REG_LEN_08BIT, 0x00}, + {0x7135, CRL_REG_LEN_08BIT, 0x30}, + {0x7136, CRL_REG_LEN_08BIT, 0x62}, + {0x7137, CRL_REG_LEN_08BIT, 0x07}, + {0x7138, CRL_REG_LEN_08BIT, 0xb9}, + {0x7139, CRL_REG_LEN_08BIT, 0x05}, + {0x713a, CRL_REG_LEN_08BIT, 0xad}, + {0x713b, CRL_REG_LEN_08BIT, 0xc3}, + {0x713c, CRL_REG_LEN_08BIT, 0xcf}, + {0x713d, CRL_REG_LEN_08BIT, 0x42}, + {0x713e, CRL_REG_LEN_08BIT, 0xe4}, + {0x713f, CRL_REG_LEN_08BIT, 0xcd}, + {0x7140, CRL_REG_LEN_08BIT, 0x07}, + {0x7141, CRL_REG_LEN_08BIT, 0xcd}, + {0x7142, CRL_REG_LEN_08BIT, 0x00}, + {0x7143, CRL_REG_LEN_08BIT, 0x00}, + {0x7144, CRL_REG_LEN_08BIT, 0x17}, + {0x7145, CRL_REG_LEN_08BIT, 0xc2}, + {0x7146, CRL_REG_LEN_08BIT, 0xbb}, + {0x7147, CRL_REG_LEN_08BIT, 0xde}, + {0x7148, CRL_REG_LEN_08BIT, 0xcf}, + {0x7149, CRL_REG_LEN_08BIT, 0xdf}, + {0x714a, CRL_REG_LEN_08BIT, 0xac}, + {0x714b, CRL_REG_LEN_08BIT, 0xd1}, + {0x714c, CRL_REG_LEN_08BIT, 0x44}, + {0x714d, CRL_REG_LEN_08BIT, 0xac}, + {0x714e, CRL_REG_LEN_08BIT, 0xb9}, + {0x714f, CRL_REG_LEN_08BIT, 0x76}, + {0x7150, CRL_REG_LEN_08BIT, 0xb8}, + {0x7151, CRL_REG_LEN_08BIT, 0x08}, + {0x7152, CRL_REG_LEN_08BIT, 0xb6}, + {0x7153, CRL_REG_LEN_08BIT, 0xfe}, + {0x7154, CRL_REG_LEN_08BIT, 0xb4}, + {0x7155, CRL_REG_LEN_08BIT, 0xca}, + {0x7156, CRL_REG_LEN_08BIT, 0xd6}, + {0x7157, CRL_REG_LEN_08BIT, 0xd8}, + {0x7158, CRL_REG_LEN_08BIT, 0xab}, + {0x7159, CRL_REG_LEN_08BIT, 0x00}, + {0x715a, CRL_REG_LEN_08BIT, 0xe1}, + {0x715b, CRL_REG_LEN_08BIT, 0x36}, + {0x715c, CRL_REG_LEN_08BIT, 0x30}, + {0x715d, CRL_REG_LEN_08BIT, 0xd3}, + {0x715e, CRL_REG_LEN_08BIT, 0xbc}, + {0x715f, CRL_REG_LEN_08BIT, 0x29}, + {0x7160, CRL_REG_LEN_08BIT, 0xb4}, + {0x7161, CRL_REG_LEN_08BIT, 0x1f}, + {0x7162, CRL_REG_LEN_08BIT, 0xaa}, + {0x7163, CRL_REG_LEN_08BIT, 0xbd}, + {0x7164, CRL_REG_LEN_08BIT, 0x01}, + {0x7165, CRL_REG_LEN_08BIT, 0xb8}, + {0x7166, CRL_REG_LEN_08BIT, 0x0c}, + {0x7167, CRL_REG_LEN_08BIT, 0x45}, + {0x7168, CRL_REG_LEN_08BIT, 0xa4}, + {0x7169, CRL_REG_LEN_08BIT, 0xbd}, + {0x716a, CRL_REG_LEN_08BIT, 0x03}, + {0x716b, CRL_REG_LEN_08BIT, 0xec}, + {0x716c, CRL_REG_LEN_08BIT, 0xbc}, + {0x716d, CRL_REG_LEN_08BIT, 0x3d}, + {0x716e, CRL_REG_LEN_08BIT, 0xc3}, + {0x716f, CRL_REG_LEN_08BIT, 0xcf}, + {0x7170, CRL_REG_LEN_08BIT, 0x42}, + {0x7171, CRL_REG_LEN_08BIT, 0xb8}, + {0x7172, CRL_REG_LEN_08BIT, 0x00}, + {0x7173, CRL_REG_LEN_08BIT, 0xe4}, + {0x7174, CRL_REG_LEN_08BIT, 0xd5}, + {0x7175, CRL_REG_LEN_08BIT, 0x00}, + {0x7176, CRL_REG_LEN_08BIT, 0xb6}, + {0x7177, CRL_REG_LEN_08BIT, 0x00}, + {0x7178, CRL_REG_LEN_08BIT, 0x74}, + {0x7179, CRL_REG_LEN_08BIT, 0xbd}, + {0x717a, CRL_REG_LEN_08BIT, 0x03}, + {0x717b, CRL_REG_LEN_08BIT, 0xb5}, + {0x717c, CRL_REG_LEN_08BIT, 0x39}, + {0x717d, CRL_REG_LEN_08BIT, 0x40}, + {0x717e, CRL_REG_LEN_08BIT, 0x58}, + {0x717f, CRL_REG_LEN_08BIT, 0xdd}, + {0x7180, CRL_REG_LEN_08BIT, 0x19}, + {0x7181, CRL_REG_LEN_08BIT, 0xc1}, + {0x7182, CRL_REG_LEN_08BIT, 0xc8}, + {0x7183, CRL_REG_LEN_08BIT, 0xbd}, + {0x7184, CRL_REG_LEN_08BIT, 0x06}, + {0x7185, CRL_REG_LEN_08BIT, 0x17}, + {0x7186, CRL_REG_LEN_08BIT, 0xc1}, + {0x7187, CRL_REG_LEN_08BIT, 0xc6}, + {0x7188, CRL_REG_LEN_08BIT, 0xe8}, + {0x7189, CRL_REG_LEN_08BIT, 0x00}, + {0x718a, CRL_REG_LEN_08BIT, 0xc0}, + {0x718b, CRL_REG_LEN_08BIT, 0xc8}, + {0x718c, CRL_REG_LEN_08BIT, 0xe6}, + {0x718d, CRL_REG_LEN_08BIT, 0x95}, + {0x718e, CRL_REG_LEN_08BIT, 0x15}, + {0x718f, CRL_REG_LEN_08BIT, 0x00}, + {0x7190, CRL_REG_LEN_08BIT, 0xbc}, + {0x7191, CRL_REG_LEN_08BIT, 0x19}, + {0x7192, CRL_REG_LEN_08BIT, 0xb9}, + {0x7193, CRL_REG_LEN_08BIT, 0xf6}, + {0x7194, CRL_REG_LEN_08BIT, 0x14}, + {0x7195, CRL_REG_LEN_08BIT, 0xc1}, + {0x7196, CRL_REG_LEN_08BIT, 0xd0}, + {0x7197, CRL_REG_LEN_08BIT, 0xd1}, + {0x7198, CRL_REG_LEN_08BIT, 0xac}, + {0x7199, CRL_REG_LEN_08BIT, 0x37}, + {0x719a, CRL_REG_LEN_08BIT, 0xbc}, + {0x719b, CRL_REG_LEN_08BIT, 0x35}, + {0x719c, CRL_REG_LEN_08BIT, 0x36}, + {0x719d, CRL_REG_LEN_08BIT, 0x30}, + {0x719e, CRL_REG_LEN_08BIT, 0xe1}, + {0x719f, CRL_REG_LEN_08BIT, 0xd3}, + {0x71a0, CRL_REG_LEN_08BIT, 0x7a}, + {0x71a1, CRL_REG_LEN_08BIT, 0xb6}, + {0x71a2, CRL_REG_LEN_08BIT, 0x0c}, + {0x71a3, CRL_REG_LEN_08BIT, 0xff}, + {0x71a4, CRL_REG_LEN_08BIT, 0xb4}, + {0x71a5, CRL_REG_LEN_08BIT, 0xc7}, + {0x71a6, CRL_REG_LEN_08BIT, 0xd9}, + {0x71a7, CRL_REG_LEN_08BIT, 0x00}, + {0x71a8, CRL_REG_LEN_08BIT, 0xbd}, + {0x71a9, CRL_REG_LEN_08BIT, 0x01}, + {0x71aa, CRL_REG_LEN_08BIT, 0x56}, + {0x71ab, CRL_REG_LEN_08BIT, 0xc0}, + {0x71ac, CRL_REG_LEN_08BIT, 0xda}, + {0x71ad, CRL_REG_LEN_08BIT, 0xb4}, + {0x71ae, CRL_REG_LEN_08BIT, 0x1f}, + {0x71af, CRL_REG_LEN_08BIT, 0x56}, + {0x71b0, CRL_REG_LEN_08BIT, 0xaa}, + {0x71b1, CRL_REG_LEN_08BIT, 0xbc}, + {0x71b2, CRL_REG_LEN_08BIT, 0x08}, + {0x71b3, CRL_REG_LEN_08BIT, 0x00}, + {0x71b4, CRL_REG_LEN_08BIT, 0x57}, + {0x71b5, CRL_REG_LEN_08BIT, 0xe8}, + {0x71b6, CRL_REG_LEN_08BIT, 0xb5}, + {0x71b7, CRL_REG_LEN_08BIT, 0x36}, + {0x71b8, CRL_REG_LEN_08BIT, 0x00}, + {0x71b9, CRL_REG_LEN_08BIT, 0x54}, + {0x71ba, CRL_REG_LEN_08BIT, 0xe7}, + {0x71bb, CRL_REG_LEN_08BIT, 0xc8}, + {0x71bc, CRL_REG_LEN_08BIT, 0xb4}, + {0x71bd, CRL_REG_LEN_08BIT, 0x1f}, + {0x71be, CRL_REG_LEN_08BIT, 0x56}, + {0x71bf, CRL_REG_LEN_08BIT, 0xaa}, + {0x71c0, CRL_REG_LEN_08BIT, 0xbc}, + {0x71c1, CRL_REG_LEN_08BIT, 0x08}, + {0x71c2, CRL_REG_LEN_08BIT, 0x57}, + {0x71c3, CRL_REG_LEN_08BIT, 0x00}, + {0x71c4, CRL_REG_LEN_08BIT, 0xb5}, + {0x71c5, CRL_REG_LEN_08BIT, 0x36}, + {0x71c6, CRL_REG_LEN_08BIT, 0x00}, + {0x71c7, CRL_REG_LEN_08BIT, 0x54}, + {0x71c8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71c9, CRL_REG_LEN_08BIT, 0xb5}, + {0x71ca, CRL_REG_LEN_08BIT, 0x18}, + {0x71cb, CRL_REG_LEN_08BIT, 0xd9}, + {0x71cc, CRL_REG_LEN_08BIT, 0x00}, + {0x71cd, CRL_REG_LEN_08BIT, 0xbd}, + {0x71ce, CRL_REG_LEN_08BIT, 0x01}, + {0x71cf, CRL_REG_LEN_08BIT, 0x56}, + {0x71d0, CRL_REG_LEN_08BIT, 0x08}, + {0x71d1, CRL_REG_LEN_08BIT, 0x57}, + {0x71d2, CRL_REG_LEN_08BIT, 0xe8}, + {0x71d3, CRL_REG_LEN_08BIT, 0xb4}, + {0x71d4, CRL_REG_LEN_08BIT, 0x42}, + {0x71d5, CRL_REG_LEN_08BIT, 0x00}, + {0x71d6, CRL_REG_LEN_08BIT, 0x54}, + {0x71d7, CRL_REG_LEN_08BIT, 0xe7}, + {0x71d8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71d9, CRL_REG_LEN_08BIT, 0xab}, + {0x71da, CRL_REG_LEN_08BIT, 0x00}, + {0x71db, CRL_REG_LEN_08BIT, 0x66}, + {0x71dc, CRL_REG_LEN_08BIT, 0x62}, + {0x71dd, CRL_REG_LEN_08BIT, 0x06}, + {0x71de, CRL_REG_LEN_08BIT, 0x74}, + {0x71df, CRL_REG_LEN_08BIT, 0xb9}, + {0x71e0, CRL_REG_LEN_08BIT, 0x05}, + {0x71e1, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e2, CRL_REG_LEN_08BIT, 0x14}, + {0x71e3, CRL_REG_LEN_08BIT, 0x0e}, + {0x71e4, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e5, CRL_REG_LEN_08BIT, 0x04}, + {0x71e6, CRL_REG_LEN_08BIT, 0xc8}, + {0x7600, CRL_REG_LEN_08BIT, 0x04}, + {0x7601, CRL_REG_LEN_08BIT, 0x80}, + {0x7602, CRL_REG_LEN_08BIT, 0x07}, + {0x7603, CRL_REG_LEN_08BIT, 0x44}, + {0x7604, CRL_REG_LEN_08BIT, 0x05}, + {0x7605, CRL_REG_LEN_08BIT, 0x33}, + {0x7606, CRL_REG_LEN_08BIT, 0x0f}, + {0x7607, CRL_REG_LEN_08BIT, 0x00}, + {0x7608, CRL_REG_LEN_08BIT, 0x07}, + {0x7609, CRL_REG_LEN_08BIT, 0x40}, + {0x760a, CRL_REG_LEN_08BIT, 0x04}, + {0x760b, CRL_REG_LEN_08BIT, 0xe5}, + {0x760c, CRL_REG_LEN_08BIT, 0x06}, + {0x760d, CRL_REG_LEN_08BIT, 0x50}, + {0x760e, CRL_REG_LEN_08BIT, 0x04}, + {0x760f, CRL_REG_LEN_08BIT, 0xe4}, + {0x7610, CRL_REG_LEN_08BIT, 0x00}, + {0x7611, CRL_REG_LEN_08BIT, 0x00}, + {0x7612, CRL_REG_LEN_08BIT, 0x06}, + {0x7613, CRL_REG_LEN_08BIT, 0x5c}, + {0x7614, CRL_REG_LEN_08BIT, 0x00}, + {0x7615, CRL_REG_LEN_08BIT, 0x0f}, + {0x7616, CRL_REG_LEN_08BIT, 0x06}, + {0x7617, CRL_REG_LEN_08BIT, 0x1c}, + {0x7618, CRL_REG_LEN_08BIT, 0x00}, + {0x7619, CRL_REG_LEN_08BIT, 0x02}, + {0x761a, CRL_REG_LEN_08BIT, 0x06}, + {0x761b, CRL_REG_LEN_08BIT, 0xa2}, + {0x761c, CRL_REG_LEN_08BIT, 0x00}, + {0x761d, CRL_REG_LEN_08BIT, 0x01}, + {0x761e, CRL_REG_LEN_08BIT, 0x06}, + {0x761f, CRL_REG_LEN_08BIT, 0xae}, + {0x7620, CRL_REG_LEN_08BIT, 0x00}, + {0x7621, CRL_REG_LEN_08BIT, 0x0e}, + {0x7622, CRL_REG_LEN_08BIT, 0x05}, + {0x7623, CRL_REG_LEN_08BIT, 0x30}, + {0x7624, CRL_REG_LEN_08BIT, 0x07}, + {0x7625, CRL_REG_LEN_08BIT, 0x00}, + {0x7626, CRL_REG_LEN_08BIT, 0x0f}, + {0x7627, CRL_REG_LEN_08BIT, 0x00}, + {0x7628, CRL_REG_LEN_08BIT, 0x04}, + {0x7629, CRL_REG_LEN_08BIT, 0xe5}, + {0x762a, CRL_REG_LEN_08BIT, 0x05}, + {0x762b, CRL_REG_LEN_08BIT, 0x33}, + {0x762c, CRL_REG_LEN_08BIT, 0x06}, + {0x762d, CRL_REG_LEN_08BIT, 0x12}, + {0x762e, CRL_REG_LEN_08BIT, 0x00}, + {0x762f, CRL_REG_LEN_08BIT, 0x01}, + {0x7630, CRL_REG_LEN_08BIT, 0x06}, + {0x7631, CRL_REG_LEN_08BIT, 0x52}, + {0x7632, CRL_REG_LEN_08BIT, 0x00}, + {0x7633, CRL_REG_LEN_08BIT, 0x01}, + {0x7634, CRL_REG_LEN_08BIT, 0x06}, + {0x7635, CRL_REG_LEN_08BIT, 0x5e}, + {0x7636, CRL_REG_LEN_08BIT, 0x04}, + {0x7637, CRL_REG_LEN_08BIT, 0xe4}, + {0x7638, CRL_REG_LEN_08BIT, 0x00}, + {0x7639, CRL_REG_LEN_08BIT, 0x01}, + {0x763a, CRL_REG_LEN_08BIT, 0x05}, + {0x763b, CRL_REG_LEN_08BIT, 0x30}, + {0x763c, CRL_REG_LEN_08BIT, 0x0f}, + {0x763d, CRL_REG_LEN_08BIT, 0x00}, + {0x763e, CRL_REG_LEN_08BIT, 0x06}, + {0x763f, CRL_REG_LEN_08BIT, 0xa6}, + {0x7640, CRL_REG_LEN_08BIT, 0x00}, + {0x7641, CRL_REG_LEN_08BIT, 0x02}, + {0x7642, CRL_REG_LEN_08BIT, 0x06}, + {0x7643, CRL_REG_LEN_08BIT, 0x26}, + {0x7644, CRL_REG_LEN_08BIT, 0x00}, + {0x7645, CRL_REG_LEN_08BIT, 0x02}, + {0x7646, CRL_REG_LEN_08BIT, 0x05}, + {0x7647, CRL_REG_LEN_08BIT, 0x33}, + {0x7648, CRL_REG_LEN_08BIT, 0x06}, + {0x7649, CRL_REG_LEN_08BIT, 0x20}, + {0x764a, CRL_REG_LEN_08BIT, 0x0f}, + {0x764b, CRL_REG_LEN_08BIT, 0x00}, + {0x764c, CRL_REG_LEN_08BIT, 0x06}, + {0x764d, CRL_REG_LEN_08BIT, 0x56}, + {0x764e, CRL_REG_LEN_08BIT, 0x00}, + {0x764f, CRL_REG_LEN_08BIT, 0x02}, + {0x7650, CRL_REG_LEN_08BIT, 0x06}, + {0x7651, CRL_REG_LEN_08BIT, 0x16}, + {0x7652, CRL_REG_LEN_08BIT, 0x05}, + {0x7653, CRL_REG_LEN_08BIT, 0x33}, + {0x7654, CRL_REG_LEN_08BIT, 0x06}, + {0x7655, CRL_REG_LEN_08BIT, 0x10}, + {0x7656, CRL_REG_LEN_08BIT, 0x0f}, + {0x7657, CRL_REG_LEN_08BIT, 0x00}, + {0x7658, CRL_REG_LEN_08BIT, 0x06}, + {0x7659, CRL_REG_LEN_08BIT, 0x10}, + {0x765a, CRL_REG_LEN_08BIT, 0x0f}, + {0x765b, CRL_REG_LEN_08BIT, 0x00}, + {0x765c, CRL_REG_LEN_08BIT, 0x06}, + {0x765d, CRL_REG_LEN_08BIT, 0x20}, + {0x765e, CRL_REG_LEN_08BIT, 0x0f}, + {0x765f, CRL_REG_LEN_08BIT, 0x00}, + {0x7660, CRL_REG_LEN_08BIT, 0x00}, + {0x7661, CRL_REG_LEN_08BIT, 0x00}, + {0x7662, CRL_REG_LEN_08BIT, 0x00}, + {0x7663, CRL_REG_LEN_08BIT, 0x02}, + {0x7664, CRL_REG_LEN_08BIT, 0x04}, + {0x7665, CRL_REG_LEN_08BIT, 0xe5}, + {0x7666, CRL_REG_LEN_08BIT, 0x04}, + {0x7667, CRL_REG_LEN_08BIT, 0xe4}, + {0x7668, CRL_REG_LEN_08BIT, 0x0f}, + {0x7669, CRL_REG_LEN_08BIT, 0x00}, + {0x766a, CRL_REG_LEN_08BIT, 0x00}, + {0x766b, CRL_REG_LEN_08BIT, 0x00}, + {0x766c, CRL_REG_LEN_08BIT, 0x00}, + {0x766d, CRL_REG_LEN_08BIT, 0x01}, + {0x766e, CRL_REG_LEN_08BIT, 0x04}, + {0x766f, CRL_REG_LEN_08BIT, 0xe5}, + {0x7670, CRL_REG_LEN_08BIT, 0x04}, + {0x7671, CRL_REG_LEN_08BIT, 0xe4}, + {0x7672, CRL_REG_LEN_08BIT, 0x0f}, + {0x7673, CRL_REG_LEN_08BIT, 0x00}, + {0x7674, CRL_REG_LEN_08BIT, 0x00}, + {0x7675, CRL_REG_LEN_08BIT, 0x02}, + {0x7676, CRL_REG_LEN_08BIT, 0x04}, + {0x7677, CRL_REG_LEN_08BIT, 0xe4}, + {0x7678, CRL_REG_LEN_08BIT, 0x00}, + {0x7679, CRL_REG_LEN_08BIT, 0x02}, + {0x767a, CRL_REG_LEN_08BIT, 0x04}, + {0x767b, CRL_REG_LEN_08BIT, 0xc4}, + {0x767c, CRL_REG_LEN_08BIT, 0x00}, + {0x767d, CRL_REG_LEN_08BIT, 0x02}, + {0x767e, CRL_REG_LEN_08BIT, 0x04}, + {0x767f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7680, CRL_REG_LEN_08BIT, 0x05}, + {0x7681, CRL_REG_LEN_08BIT, 0x83}, + {0x7682, CRL_REG_LEN_08BIT, 0x0f}, + {0x7683, CRL_REG_LEN_08BIT, 0x00}, + {0x7684, CRL_REG_LEN_08BIT, 0x00}, + {0x7685, CRL_REG_LEN_08BIT, 0x02}, + {0x7686, CRL_REG_LEN_08BIT, 0x04}, + {0x7687, CRL_REG_LEN_08BIT, 0xe4}, + {0x7688, CRL_REG_LEN_08BIT, 0x00}, + {0x7689, CRL_REG_LEN_08BIT, 0x02}, + {0x768a, CRL_REG_LEN_08BIT, 0x04}, + {0x768b, CRL_REG_LEN_08BIT, 0xc4}, + {0x768c, CRL_REG_LEN_08BIT, 0x00}, + {0x768d, CRL_REG_LEN_08BIT, 0x02}, + {0x768e, CRL_REG_LEN_08BIT, 0x04}, + {0x768f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7690, CRL_REG_LEN_08BIT, 0x05}, + {0x7691, CRL_REG_LEN_08BIT, 0x83}, + {0x7692, CRL_REG_LEN_08BIT, 0x03}, + {0x7693, CRL_REG_LEN_08BIT, 0x0b}, + {0x7694, CRL_REG_LEN_08BIT, 0x05}, + {0x7695, CRL_REG_LEN_08BIT, 0x83}, + {0x7696, CRL_REG_LEN_08BIT, 0x00}, + {0x7697, CRL_REG_LEN_08BIT, 0x07}, + {0x7698, CRL_REG_LEN_08BIT, 0x05}, + {0x7699, CRL_REG_LEN_08BIT, 0x03}, + {0x769a, CRL_REG_LEN_08BIT, 0x00}, + {0x769b, CRL_REG_LEN_08BIT, 0x05}, + {0x769c, CRL_REG_LEN_08BIT, 0x05}, + {0x769d, CRL_REG_LEN_08BIT, 0x32}, + {0x769e, CRL_REG_LEN_08BIT, 0x05}, + {0x769f, CRL_REG_LEN_08BIT, 0x30}, + {0x76a0, CRL_REG_LEN_08BIT, 0x00}, + {0x76a1, CRL_REG_LEN_08BIT, 0x02}, + {0x76a2, CRL_REG_LEN_08BIT, 0x05}, + {0x76a3, CRL_REG_LEN_08BIT, 0x78}, + {0x76a4, CRL_REG_LEN_08BIT, 0x00}, + {0x76a5, CRL_REG_LEN_08BIT, 0x01}, + {0x76a6, CRL_REG_LEN_08BIT, 0x05}, + {0x76a7, CRL_REG_LEN_08BIT, 0x7c}, + {0x76a8, CRL_REG_LEN_08BIT, 0x03}, + {0x76a9, CRL_REG_LEN_08BIT, 0x9a}, + {0x76aa, CRL_REG_LEN_08BIT, 0x05}, + {0x76ab, CRL_REG_LEN_08BIT, 0x83}, + {0x76ac, CRL_REG_LEN_08BIT, 0x00}, + {0x76ad, CRL_REG_LEN_08BIT, 0x04}, + {0x76ae, CRL_REG_LEN_08BIT, 0x05}, + {0x76af, CRL_REG_LEN_08BIT, 0x03}, + {0x76b0, CRL_REG_LEN_08BIT, 0x00}, + {0x76b1, CRL_REG_LEN_08BIT, 0x03}, + {0x76b2, CRL_REG_LEN_08BIT, 0x05}, + {0x76b3, CRL_REG_LEN_08BIT, 0x32}, + {0x76b4, CRL_REG_LEN_08BIT, 0x05}, + {0x76b5, CRL_REG_LEN_08BIT, 0x30}, + {0x76b6, CRL_REG_LEN_08BIT, 0x00}, + {0x76b7, CRL_REG_LEN_08BIT, 0x02}, + {0x76b8, CRL_REG_LEN_08BIT, 0x05}, + {0x76b9, CRL_REG_LEN_08BIT, 0x78}, + {0x76ba, CRL_REG_LEN_08BIT, 0x00}, + {0x76bb, CRL_REG_LEN_08BIT, 0x01}, + {0x76bc, CRL_REG_LEN_08BIT, 0x05}, + {0x76bd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76be, CRL_REG_LEN_08BIT, 0x03}, + {0x76bf, CRL_REG_LEN_08BIT, 0x99}, + {0x76c0, CRL_REG_LEN_08BIT, 0x05}, + {0x76c1, CRL_REG_LEN_08BIT, 0x83}, + {0x76c2, CRL_REG_LEN_08BIT, 0x00}, + {0x76c3, CRL_REG_LEN_08BIT, 0x03}, + {0x76c4, CRL_REG_LEN_08BIT, 0x05}, + {0x76c5, CRL_REG_LEN_08BIT, 0x03}, + {0x76c6, CRL_REG_LEN_08BIT, 0x00}, + {0x76c7, CRL_REG_LEN_08BIT, 0x01}, + {0x76c8, CRL_REG_LEN_08BIT, 0x05}, + {0x76c9, CRL_REG_LEN_08BIT, 0x32}, + {0x76ca, CRL_REG_LEN_08BIT, 0x05}, + {0x76cb, CRL_REG_LEN_08BIT, 0x30}, + {0x76cc, CRL_REG_LEN_08BIT, 0x00}, + {0x76cd, CRL_REG_LEN_08BIT, 0x02}, + {0x76ce, CRL_REG_LEN_08BIT, 0x05}, + {0x76cf, CRL_REG_LEN_08BIT, 0x78}, + {0x76d0, CRL_REG_LEN_08BIT, 0x00}, + {0x76d1, CRL_REG_LEN_08BIT, 0x01}, + {0x76d2, CRL_REG_LEN_08BIT, 0x05}, + {0x76d3, CRL_REG_LEN_08BIT, 0x7c}, + {0x76d4, CRL_REG_LEN_08BIT, 0x03}, + {0x76d5, CRL_REG_LEN_08BIT, 0x98}, + {0x76d6, CRL_REG_LEN_08BIT, 0x05}, + {0x76d7, CRL_REG_LEN_08BIT, 0x83}, + {0x76d8, CRL_REG_LEN_08BIT, 0x00}, + {0x76d9, CRL_REG_LEN_08BIT, 0x00}, + {0x76da, CRL_REG_LEN_08BIT, 0x05}, + {0x76db, CRL_REG_LEN_08BIT, 0x03}, + {0x76dc, CRL_REG_LEN_08BIT, 0x00}, + {0x76dd, CRL_REG_LEN_08BIT, 0x01}, + {0x76de, CRL_REG_LEN_08BIT, 0x05}, + {0x76df, CRL_REG_LEN_08BIT, 0x32}, + {0x76e0, CRL_REG_LEN_08BIT, 0x05}, + {0x76e1, CRL_REG_LEN_08BIT, 0x30}, + {0x76e2, CRL_REG_LEN_08BIT, 0x00}, + {0x76e3, CRL_REG_LEN_08BIT, 0x02}, + {0x76e4, CRL_REG_LEN_08BIT, 0x05}, + {0x76e5, CRL_REG_LEN_08BIT, 0x78}, + {0x76e6, CRL_REG_LEN_08BIT, 0x00}, + {0x76e7, CRL_REG_LEN_08BIT, 0x01}, + {0x76e8, CRL_REG_LEN_08BIT, 0x05}, + {0x76e9, CRL_REG_LEN_08BIT, 0x7c}, + {0x76ea, CRL_REG_LEN_08BIT, 0x03}, + {0x76eb, CRL_REG_LEN_08BIT, 0x97}, + {0x76ec, CRL_REG_LEN_08BIT, 0x05}, + {0x76ed, CRL_REG_LEN_08BIT, 0x83}, + {0x76ee, CRL_REG_LEN_08BIT, 0x00}, + {0x76ef, CRL_REG_LEN_08BIT, 0x00}, + {0x76f0, CRL_REG_LEN_08BIT, 0x05}, + {0x76f1, CRL_REG_LEN_08BIT, 0x03}, + {0x76f2, CRL_REG_LEN_08BIT, 0x05}, + {0x76f3, CRL_REG_LEN_08BIT, 0x32}, + {0x76f4, CRL_REG_LEN_08BIT, 0x05}, + {0x76f5, CRL_REG_LEN_08BIT, 0x30}, + {0x76f6, CRL_REG_LEN_08BIT, 0x00}, + {0x76f7, CRL_REG_LEN_08BIT, 0x02}, + {0x76f8, CRL_REG_LEN_08BIT, 0x05}, + {0x76f9, CRL_REG_LEN_08BIT, 0x78}, + {0x76fa, CRL_REG_LEN_08BIT, 0x00}, + {0x76fb, CRL_REG_LEN_08BIT, 0x01}, + {0x76fc, CRL_REG_LEN_08BIT, 0x05}, + {0x76fd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76fe, CRL_REG_LEN_08BIT, 0x03}, + {0x76ff, CRL_REG_LEN_08BIT, 0x96}, + {0x7700, CRL_REG_LEN_08BIT, 0x05}, + {0x7701, CRL_REG_LEN_08BIT, 0x83}, + {0x7702, CRL_REG_LEN_08BIT, 0x05}, + {0x7703, CRL_REG_LEN_08BIT, 0x03}, + {0x7704, CRL_REG_LEN_08BIT, 0x05}, + {0x7705, CRL_REG_LEN_08BIT, 0x32}, + {0x7706, CRL_REG_LEN_08BIT, 0x05}, + {0x7707, CRL_REG_LEN_08BIT, 0x30}, + {0x7708, CRL_REG_LEN_08BIT, 0x00}, + {0x7709, CRL_REG_LEN_08BIT, 0x02}, + {0x770a, CRL_REG_LEN_08BIT, 0x05}, + {0x770b, CRL_REG_LEN_08BIT, 0x78}, + {0x770c, CRL_REG_LEN_08BIT, 0x00}, + {0x770d, CRL_REG_LEN_08BIT, 0x01}, + {0x770e, CRL_REG_LEN_08BIT, 0x05}, + {0x770f, CRL_REG_LEN_08BIT, 0x7c}, + {0x7710, CRL_REG_LEN_08BIT, 0x03}, + {0x7711, CRL_REG_LEN_08BIT, 0x95}, + {0x7712, CRL_REG_LEN_08BIT, 0x05}, + {0x7713, CRL_REG_LEN_08BIT, 0x83}, + {0x7714, CRL_REG_LEN_08BIT, 0x05}, + {0x7715, CRL_REG_LEN_08BIT, 0x03}, + {0x7716, CRL_REG_LEN_08BIT, 0x05}, + {0x7717, CRL_REG_LEN_08BIT, 0x32}, + {0x7718, CRL_REG_LEN_08BIT, 0x05}, + {0x7719, CRL_REG_LEN_08BIT, 0x30}, + {0x771a, CRL_REG_LEN_08BIT, 0x00}, + {0x771b, CRL_REG_LEN_08BIT, 0x02}, + {0x771c, CRL_REG_LEN_08BIT, 0x05}, + {0x771d, CRL_REG_LEN_08BIT, 0x78}, + {0x771e, CRL_REG_LEN_08BIT, 0x00}, + {0x771f, CRL_REG_LEN_08BIT, 0x01}, + {0x7720, CRL_REG_LEN_08BIT, 0x05}, + {0x7721, CRL_REG_LEN_08BIT, 0x7c}, + {0x7722, CRL_REG_LEN_08BIT, 0x03}, + {0x7723, CRL_REG_LEN_08BIT, 0x94}, + {0x7724, CRL_REG_LEN_08BIT, 0x05}, + {0x7725, CRL_REG_LEN_08BIT, 0x83}, + {0x7726, CRL_REG_LEN_08BIT, 0x00}, + {0x7727, CRL_REG_LEN_08BIT, 0x01}, + {0x7728, CRL_REG_LEN_08BIT, 0x05}, + {0x7729, CRL_REG_LEN_08BIT, 0x03}, + {0x772a, CRL_REG_LEN_08BIT, 0x00}, + {0x772b, CRL_REG_LEN_08BIT, 0x01}, + {0x772c, CRL_REG_LEN_08BIT, 0x05}, + {0x772d, CRL_REG_LEN_08BIT, 0x32}, + {0x772e, CRL_REG_LEN_08BIT, 0x05}, + {0x772f, CRL_REG_LEN_08BIT, 0x30}, + {0x7730, CRL_REG_LEN_08BIT, 0x00}, + {0x7731, CRL_REG_LEN_08BIT, 0x02}, + {0x7732, CRL_REG_LEN_08BIT, 0x05}, + {0x7733, CRL_REG_LEN_08BIT, 0x78}, + {0x7734, CRL_REG_LEN_08BIT, 0x00}, + {0x7735, CRL_REG_LEN_08BIT, 0x01}, + {0x7736, CRL_REG_LEN_08BIT, 0x05}, + {0x7737, CRL_REG_LEN_08BIT, 0x7c}, + {0x7738, CRL_REG_LEN_08BIT, 0x03}, + {0x7739, CRL_REG_LEN_08BIT, 0x93}, + {0x773a, CRL_REG_LEN_08BIT, 0x05}, + {0x773b, CRL_REG_LEN_08BIT, 0x83}, + {0x773c, CRL_REG_LEN_08BIT, 0x00}, + {0x773d, CRL_REG_LEN_08BIT, 0x00}, + {0x773e, CRL_REG_LEN_08BIT, 0x05}, + {0x773f, CRL_REG_LEN_08BIT, 0x03}, + {0x7740, CRL_REG_LEN_08BIT, 0x00}, + {0x7741, CRL_REG_LEN_08BIT, 0x00}, + {0x7742, CRL_REG_LEN_08BIT, 0x05}, + {0x7743, CRL_REG_LEN_08BIT, 0x32}, + {0x7744, CRL_REG_LEN_08BIT, 0x05}, + {0x7745, CRL_REG_LEN_08BIT, 0x30}, + {0x7746, CRL_REG_LEN_08BIT, 0x00}, + {0x7747, CRL_REG_LEN_08BIT, 0x02}, + {0x7748, CRL_REG_LEN_08BIT, 0x05}, + {0x7749, CRL_REG_LEN_08BIT, 0x78}, + {0x774a, CRL_REG_LEN_08BIT, 0x00}, + {0x774b, CRL_REG_LEN_08BIT, 0x01}, + {0x774c, CRL_REG_LEN_08BIT, 0x05}, + {0x774d, CRL_REG_LEN_08BIT, 0x7c}, + {0x774e, CRL_REG_LEN_08BIT, 0x03}, + {0x774f, CRL_REG_LEN_08BIT, 0x92}, + {0x7750, CRL_REG_LEN_08BIT, 0x05}, + {0x7751, CRL_REG_LEN_08BIT, 0x83}, + {0x7752, CRL_REG_LEN_08BIT, 0x05}, + {0x7753, CRL_REG_LEN_08BIT, 0x03}, + {0x7754, CRL_REG_LEN_08BIT, 0x00}, + {0x7755, CRL_REG_LEN_08BIT, 0x00}, + {0x7756, CRL_REG_LEN_08BIT, 0x05}, + {0x7757, CRL_REG_LEN_08BIT, 0x32}, + {0x7758, CRL_REG_LEN_08BIT, 0x05}, + {0x7759, CRL_REG_LEN_08BIT, 0x30}, + {0x775a, CRL_REG_LEN_08BIT, 0x00}, + {0x775b, CRL_REG_LEN_08BIT, 0x02}, + {0x775c, CRL_REG_LEN_08BIT, 0x05}, + {0x775d, CRL_REG_LEN_08BIT, 0x78}, + {0x775e, CRL_REG_LEN_08BIT, 0x00}, + {0x775f, CRL_REG_LEN_08BIT, 0x01}, + {0x7760, CRL_REG_LEN_08BIT, 0x05}, + {0x7761, CRL_REG_LEN_08BIT, 0x7c}, + {0x7762, CRL_REG_LEN_08BIT, 0x03}, + {0x7763, CRL_REG_LEN_08BIT, 0x91}, + {0x7764, CRL_REG_LEN_08BIT, 0x05}, + {0x7765, CRL_REG_LEN_08BIT, 0x83}, + {0x7766, CRL_REG_LEN_08BIT, 0x05}, + {0x7767, CRL_REG_LEN_08BIT, 0x03}, + {0x7768, CRL_REG_LEN_08BIT, 0x05}, + {0x7769, CRL_REG_LEN_08BIT, 0x32}, + {0x776a, CRL_REG_LEN_08BIT, 0x05}, + {0x776b, CRL_REG_LEN_08BIT, 0x30}, + {0x776c, CRL_REG_LEN_08BIT, 0x00}, + {0x776d, CRL_REG_LEN_08BIT, 0x02}, + {0x776e, CRL_REG_LEN_08BIT, 0x05}, + {0x776f, CRL_REG_LEN_08BIT, 0x78}, + {0x7770, CRL_REG_LEN_08BIT, 0x00}, + {0x7771, CRL_REG_LEN_08BIT, 0x01}, + {0x7772, CRL_REG_LEN_08BIT, 0x05}, + {0x7773, CRL_REG_LEN_08BIT, 0x7c}, + {0x7774, CRL_REG_LEN_08BIT, 0x03}, + {0x7775, CRL_REG_LEN_08BIT, 0x90}, + {0x7776, CRL_REG_LEN_08BIT, 0x05}, + {0x7777, CRL_REG_LEN_08BIT, 0x83}, + {0x7778, CRL_REG_LEN_08BIT, 0x05}, + {0x7779, CRL_REG_LEN_08BIT, 0x03}, + {0x777a, CRL_REG_LEN_08BIT, 0x05}, + {0x777b, CRL_REG_LEN_08BIT, 0x32}, + {0x777c, CRL_REG_LEN_08BIT, 0x05}, + {0x777d, CRL_REG_LEN_08BIT, 0x30}, + {0x777e, CRL_REG_LEN_08BIT, 0x00}, + {0x777f, CRL_REG_LEN_08BIT, 0x02}, + {0x7780, CRL_REG_LEN_08BIT, 0x05}, + {0x7781, CRL_REG_LEN_08BIT, 0x78}, + {0x7782, CRL_REG_LEN_08BIT, 0x00}, + {0x7783, CRL_REG_LEN_08BIT, 0x01}, + {0x7784, CRL_REG_LEN_08BIT, 0x05}, + {0x7785, CRL_REG_LEN_08BIT, 0x7c}, + {0x7786, CRL_REG_LEN_08BIT, 0x02}, + {0x7787, CRL_REG_LEN_08BIT, 0x90}, + {0x7788, CRL_REG_LEN_08BIT, 0x05}, + {0x7789, CRL_REG_LEN_08BIT, 0x03}, + {0x778a, CRL_REG_LEN_08BIT, 0x07}, + {0x778b, CRL_REG_LEN_08BIT, 0x00}, + {0x778c, CRL_REG_LEN_08BIT, 0x0f}, + {0x778d, CRL_REG_LEN_08BIT, 0x00}, + {0x778e, CRL_REG_LEN_08BIT, 0x08}, + {0x778f, CRL_REG_LEN_08BIT, 0x30}, + {0x7790, CRL_REG_LEN_08BIT, 0x08}, + {0x7791, CRL_REG_LEN_08BIT, 0xee}, + {0x7792, CRL_REG_LEN_08BIT, 0x0f}, + {0x7793, CRL_REG_LEN_08BIT, 0x00}, + {0x7794, CRL_REG_LEN_08BIT, 0x05}, + {0x7795, CRL_REG_LEN_08BIT, 0x33}, + {0x7796, CRL_REG_LEN_08BIT, 0x04}, + {0x7797, CRL_REG_LEN_08BIT, 0xe5}, + {0x7798, CRL_REG_LEN_08BIT, 0x06}, + {0x7799, CRL_REG_LEN_08BIT, 0x52}, + {0x779a, CRL_REG_LEN_08BIT, 0x04}, + {0x779b, CRL_REG_LEN_08BIT, 0xe4}, + {0x779c, CRL_REG_LEN_08BIT, 0x00}, + {0x779d, CRL_REG_LEN_08BIT, 0x00}, + {0x779e, CRL_REG_LEN_08BIT, 0x06}, + {0x779f, CRL_REG_LEN_08BIT, 0x5e}, + {0x77a0, CRL_REG_LEN_08BIT, 0x00}, + {0x77a1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77a2, CRL_REG_LEN_08BIT, 0x06}, + {0x77a3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77a4, CRL_REG_LEN_08BIT, 0x00}, + {0x77a5, CRL_REG_LEN_08BIT, 0x02}, + {0x77a6, CRL_REG_LEN_08BIT, 0x06}, + {0x77a7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77a8, CRL_REG_LEN_08BIT, 0x00}, + {0x77a9, CRL_REG_LEN_08BIT, 0x01}, + {0x77aa, CRL_REG_LEN_08BIT, 0x06}, + {0x77ab, CRL_REG_LEN_08BIT, 0xae}, + {0x77ac, CRL_REG_LEN_08BIT, 0x00}, + {0x77ad, CRL_REG_LEN_08BIT, 0x03}, + {0x77ae, CRL_REG_LEN_08BIT, 0x05}, + {0x77af, CRL_REG_LEN_08BIT, 0x30}, + {0x77b0, CRL_REG_LEN_08BIT, 0x09}, + {0x77b1, CRL_REG_LEN_08BIT, 0x19}, + {0x77b2, CRL_REG_LEN_08BIT, 0x0f}, + {0x77b3, CRL_REG_LEN_08BIT, 0x00}, + {0x77b4, CRL_REG_LEN_08BIT, 0x05}, + {0x77b5, CRL_REG_LEN_08BIT, 0x33}, + {0x77b6, CRL_REG_LEN_08BIT, 0x04}, + {0x77b7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77b8, CRL_REG_LEN_08BIT, 0x06}, + {0x77b9, CRL_REG_LEN_08BIT, 0x52}, + {0x77ba, CRL_REG_LEN_08BIT, 0x04}, + {0x77bb, CRL_REG_LEN_08BIT, 0xe4}, + {0x77bc, CRL_REG_LEN_08BIT, 0x00}, + {0x77bd, CRL_REG_LEN_08BIT, 0x00}, + {0x77be, CRL_REG_LEN_08BIT, 0x06}, + {0x77bf, CRL_REG_LEN_08BIT, 0x5e}, + {0x77c0, CRL_REG_LEN_08BIT, 0x00}, + {0x77c1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77c2, CRL_REG_LEN_08BIT, 0x06}, + {0x77c3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77c4, CRL_REG_LEN_08BIT, 0x00}, + {0x77c5, CRL_REG_LEN_08BIT, 0x02}, + {0x77c6, CRL_REG_LEN_08BIT, 0x06}, + {0x77c7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77c8, CRL_REG_LEN_08BIT, 0x00}, + {0x77c9, CRL_REG_LEN_08BIT, 0x01}, + {0x77ca, CRL_REG_LEN_08BIT, 0x06}, + {0x77cb, CRL_REG_LEN_08BIT, 0xae}, + {0x77cc, CRL_REG_LEN_08BIT, 0x00}, + {0x77cd, CRL_REG_LEN_08BIT, 0x03}, + {0x77ce, CRL_REG_LEN_08BIT, 0x05}, + {0x77cf, CRL_REG_LEN_08BIT, 0x30}, + {0x77d0, CRL_REG_LEN_08BIT, 0x0f}, + {0x77d1, CRL_REG_LEN_08BIT, 0x00}, + {0x77d2, CRL_REG_LEN_08BIT, 0x00}, + {0x77d3, CRL_REG_LEN_08BIT, 0x00}, + {0x77d4, CRL_REG_LEN_08BIT, 0x00}, + {0x77d5, CRL_REG_LEN_08BIT, 0x02}, + {0x77d6, CRL_REG_LEN_08BIT, 0x04}, + {0x77d7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77d8, CRL_REG_LEN_08BIT, 0x04}, + {0x77d9, CRL_REG_LEN_08BIT, 0xe4}, + {0x77da, CRL_REG_LEN_08BIT, 0x05}, + {0x77db, CRL_REG_LEN_08BIT, 0x33}, + {0x77dc, CRL_REG_LEN_08BIT, 0x07}, + {0x77dd, CRL_REG_LEN_08BIT, 0x10}, + {0x77de, CRL_REG_LEN_08BIT, 0x00}, + {0x77df, CRL_REG_LEN_08BIT, 0x00}, + {0x77e0, CRL_REG_LEN_08BIT, 0x01}, + {0x77e1, CRL_REG_LEN_08BIT, 0xbb}, + {0x77e2, CRL_REG_LEN_08BIT, 0x00}, + {0x77e3, CRL_REG_LEN_08BIT, 0x00}, + {0x77e4, CRL_REG_LEN_08BIT, 0x01}, + {0x77e5, CRL_REG_LEN_08BIT, 0xaa}, + {0x77e6, CRL_REG_LEN_08BIT, 0x00}, + {0x77e7, CRL_REG_LEN_08BIT, 0x00}, + {0x77e8, CRL_REG_LEN_08BIT, 0x01}, + {0x77e9, CRL_REG_LEN_08BIT, 0x99}, + {0x77ea, CRL_REG_LEN_08BIT, 0x00}, + {0x77eb, CRL_REG_LEN_08BIT, 0x00}, + {0x77ec, CRL_REG_LEN_08BIT, 0x01}, + {0x77ed, CRL_REG_LEN_08BIT, 0x88}, + {0x77ee, CRL_REG_LEN_08BIT, 0x00}, + {0x77ef, CRL_REG_LEN_08BIT, 0x00}, + {0x77f0, CRL_REG_LEN_08BIT, 0x01}, + {0x77f1, CRL_REG_LEN_08BIT, 0x77}, + {0x77f2, CRL_REG_LEN_08BIT, 0x00}, + {0x77f3, CRL_REG_LEN_08BIT, 0x00}, + {0x77f4, CRL_REG_LEN_08BIT, 0x01}, + {0x77f5, CRL_REG_LEN_08BIT, 0x66}, + {0x77f6, CRL_REG_LEN_08BIT, 0x00}, + {0x77f7, CRL_REG_LEN_08BIT, 0x00}, + {0x77f8, CRL_REG_LEN_08BIT, 0x01}, + {0x77f9, CRL_REG_LEN_08BIT, 0x55}, + {0x77fa, CRL_REG_LEN_08BIT, 0x00}, + {0x77fb, CRL_REG_LEN_08BIT, 0x00}, + {0x77fc, CRL_REG_LEN_08BIT, 0x01}, + {0x77fd, CRL_REG_LEN_08BIT, 0x44}, + {0x77fe, CRL_REG_LEN_08BIT, 0x00}, + {0x77ff, CRL_REG_LEN_08BIT, 0x00}, + {0x7800, CRL_REG_LEN_08BIT, 0x01}, + {0x7801, CRL_REG_LEN_08BIT, 0x33}, + {0x7802, CRL_REG_LEN_08BIT, 0x00}, + {0x7803, CRL_REG_LEN_08BIT, 0x00}, + {0x7804, CRL_REG_LEN_08BIT, 0x01}, + {0x7805, CRL_REG_LEN_08BIT, 0x22}, + {0x7806, CRL_REG_LEN_08BIT, 0x00}, + {0x7807, CRL_REG_LEN_08BIT, 0x00}, + {0x7808, CRL_REG_LEN_08BIT, 0x01}, + {0x7809, CRL_REG_LEN_08BIT, 0x11}, + {0x780a, CRL_REG_LEN_08BIT, 0x00}, + {0x780b, CRL_REG_LEN_08BIT, 0x00}, + {0x780c, CRL_REG_LEN_08BIT, 0x01}, + {0x780d, CRL_REG_LEN_08BIT, 0x00}, + {0x780e, CRL_REG_LEN_08BIT, 0x01}, + {0x780f, CRL_REG_LEN_08BIT, 0xff}, + {0x7810, CRL_REG_LEN_08BIT, 0x07}, + {0x7811, CRL_REG_LEN_08BIT, 0x00}, + {0x7812, CRL_REG_LEN_08BIT, 0x02}, + {0x7813, CRL_REG_LEN_08BIT, 0xa0}, + {0x7814, CRL_REG_LEN_08BIT, 0x0f}, + {0x7815, CRL_REG_LEN_08BIT, 0x00}, + {0x7816, CRL_REG_LEN_08BIT, 0x08}, + {0x7817, CRL_REG_LEN_08BIT, 0x35}, + {0x7818, CRL_REG_LEN_08BIT, 0x06}, + {0x7819, CRL_REG_LEN_08BIT, 0x52}, + {0x781a, CRL_REG_LEN_08BIT, 0x04}, + {0x781b, CRL_REG_LEN_08BIT, 0xe4}, + {0x781c, CRL_REG_LEN_08BIT, 0x00}, + {0x781d, CRL_REG_LEN_08BIT, 0x00}, + {0x781e, CRL_REG_LEN_08BIT, 0x06}, + {0x781f, CRL_REG_LEN_08BIT, 0x5e}, + {0x7820, CRL_REG_LEN_08BIT, 0x05}, + {0x7821, CRL_REG_LEN_08BIT, 0x33}, + {0x7822, CRL_REG_LEN_08BIT, 0x09}, + {0x7823, CRL_REG_LEN_08BIT, 0x19}, + {0x7824, CRL_REG_LEN_08BIT, 0x06}, + {0x7825, CRL_REG_LEN_08BIT, 0x1e}, + {0x7826, CRL_REG_LEN_08BIT, 0x05}, + {0x7827, CRL_REG_LEN_08BIT, 0x33}, + {0x7828, CRL_REG_LEN_08BIT, 0x00}, + {0x7829, CRL_REG_LEN_08BIT, 0x01}, + {0x782a, CRL_REG_LEN_08BIT, 0x06}, + {0x782b, CRL_REG_LEN_08BIT, 0x24}, + {0x782c, CRL_REG_LEN_08BIT, 0x06}, + {0x782d, CRL_REG_LEN_08BIT, 0x20}, + {0x782e, CRL_REG_LEN_08BIT, 0x0f}, + {0x782f, CRL_REG_LEN_08BIT, 0x00}, + {0x7830, CRL_REG_LEN_08BIT, 0x08}, + {0x7831, CRL_REG_LEN_08BIT, 0x35}, + {0x7832, CRL_REG_LEN_08BIT, 0x07}, + {0x7833, CRL_REG_LEN_08BIT, 0x10}, + {0x7834, CRL_REG_LEN_08BIT, 0x00}, + {0x7835, CRL_REG_LEN_08BIT, 0x00}, + {0x7836, CRL_REG_LEN_08BIT, 0x01}, + {0x7837, CRL_REG_LEN_08BIT, 0xbb}, + {0x7838, CRL_REG_LEN_08BIT, 0x00}, + {0x7839, CRL_REG_LEN_08BIT, 0x00}, + {0x783a, CRL_REG_LEN_08BIT, 0x01}, + {0x783b, CRL_REG_LEN_08BIT, 0xaa}, + {0x783c, CRL_REG_LEN_08BIT, 0x00}, + {0x783d, CRL_REG_LEN_08BIT, 0x00}, + {0x783e, CRL_REG_LEN_08BIT, 0x01}, + {0x783f, CRL_REG_LEN_08BIT, 0x99}, + {0x7840, CRL_REG_LEN_08BIT, 0x00}, + {0x7841, CRL_REG_LEN_08BIT, 0x00}, + {0x7842, CRL_REG_LEN_08BIT, 0x01}, + {0x7843, CRL_REG_LEN_08BIT, 0x88}, + {0x7844, CRL_REG_LEN_08BIT, 0x00}, + {0x7845, CRL_REG_LEN_08BIT, 0x00}, + {0x7846, CRL_REG_LEN_08BIT, 0x01}, + {0x7847, CRL_REG_LEN_08BIT, 0x77}, + {0x7848, CRL_REG_LEN_08BIT, 0x00}, + {0x7849, CRL_REG_LEN_08BIT, 0x00}, + {0x784a, CRL_REG_LEN_08BIT, 0x01}, + {0x784b, CRL_REG_LEN_08BIT, 0x66}, + {0x784c, CRL_REG_LEN_08BIT, 0x00}, + {0x784d, CRL_REG_LEN_08BIT, 0x00}, + {0x784e, CRL_REG_LEN_08BIT, 0x01}, + {0x784f, CRL_REG_LEN_08BIT, 0x55}, + {0x7850, CRL_REG_LEN_08BIT, 0x00}, + {0x7851, CRL_REG_LEN_08BIT, 0x00}, + {0x7852, CRL_REG_LEN_08BIT, 0x01}, + {0x7853, CRL_REG_LEN_08BIT, 0x44}, + {0x7854, CRL_REG_LEN_08BIT, 0x00}, + {0x7855, CRL_REG_LEN_08BIT, 0x00}, + {0x7856, CRL_REG_LEN_08BIT, 0x01}, + {0x7857, CRL_REG_LEN_08BIT, 0x33}, + {0x7858, CRL_REG_LEN_08BIT, 0x00}, + {0x7859, CRL_REG_LEN_08BIT, 0x00}, + {0x785a, CRL_REG_LEN_08BIT, 0x01}, + {0x785b, CRL_REG_LEN_08BIT, 0x22}, + {0x785c, CRL_REG_LEN_08BIT, 0x00}, + {0x785d, CRL_REG_LEN_08BIT, 0x00}, + {0x785e, CRL_REG_LEN_08BIT, 0x01}, + {0x785f, CRL_REG_LEN_08BIT, 0x11}, + {0x7860, CRL_REG_LEN_08BIT, 0x00}, + {0x7861, CRL_REG_LEN_08BIT, 0x00}, + {0x7862, CRL_REG_LEN_08BIT, 0x01}, + {0x7863, CRL_REG_LEN_08BIT, 0x00}, + {0x7864, CRL_REG_LEN_08BIT, 0x07}, + {0x7865, CRL_REG_LEN_08BIT, 0x00}, + {0x7866, CRL_REG_LEN_08BIT, 0x01}, + {0x7867, CRL_REG_LEN_08BIT, 0xff}, + {0x7868, CRL_REG_LEN_08BIT, 0x02}, + {0x7869, CRL_REG_LEN_08BIT, 0xa0}, + {0x786a, CRL_REG_LEN_08BIT, 0x0f}, + {0x786b, CRL_REG_LEN_08BIT, 0x00}, + {0x786c, CRL_REG_LEN_08BIT, 0x08}, + {0x786d, CRL_REG_LEN_08BIT, 0x3a}, + {0x786e, CRL_REG_LEN_08BIT, 0x08}, + {0x786f, CRL_REG_LEN_08BIT, 0x6a}, + {0x7870, CRL_REG_LEN_08BIT, 0x0f}, + {0x7871, CRL_REG_LEN_08BIT, 0x00}, + {0x7872, CRL_REG_LEN_08BIT, 0x04}, + {0x7873, CRL_REG_LEN_08BIT, 0xc0}, + {0x7874, CRL_REG_LEN_08BIT, 0x09}, + {0x7875, CRL_REG_LEN_08BIT, 0x19}, + {0x7876, CRL_REG_LEN_08BIT, 0x04}, + {0x7877, CRL_REG_LEN_08BIT, 0x99}, + {0x7878, CRL_REG_LEN_08BIT, 0x07}, + {0x7879, CRL_REG_LEN_08BIT, 0x14}, + {0x787a, CRL_REG_LEN_08BIT, 0x00}, + {0x787b, CRL_REG_LEN_08BIT, 0x01}, + {0x787c, CRL_REG_LEN_08BIT, 0x04}, + {0x787d, CRL_REG_LEN_08BIT, 0xa4}, + {0x787e, CRL_REG_LEN_08BIT, 0x00}, + {0x787f, CRL_REG_LEN_08BIT, 0x07}, + {0x7880, CRL_REG_LEN_08BIT, 0x04}, + {0x7881, CRL_REG_LEN_08BIT, 0xa6}, + {0x7882, CRL_REG_LEN_08BIT, 0x00}, + {0x7883, CRL_REG_LEN_08BIT, 0x00}, + {0x7884, CRL_REG_LEN_08BIT, 0x04}, + {0x7885, CRL_REG_LEN_08BIT, 0xa0}, + {0x7886, CRL_REG_LEN_08BIT, 0x04}, + {0x7887, CRL_REG_LEN_08BIT, 0x80}, + {0x7888, CRL_REG_LEN_08BIT, 0x04}, + {0x7889, CRL_REG_LEN_08BIT, 0x00}, + {0x788a, CRL_REG_LEN_08BIT, 0x05}, + {0x788b, CRL_REG_LEN_08BIT, 0x03}, + {0x788c, CRL_REG_LEN_08BIT, 0x06}, + {0x788d, CRL_REG_LEN_08BIT, 0x00}, + {0x788e, CRL_REG_LEN_08BIT, 0x0f}, + {0x788f, CRL_REG_LEN_08BIT, 0x00}, + {0x7890, CRL_REG_LEN_08BIT, 0x0f}, + {0x7891, CRL_REG_LEN_08BIT, 0x00}, + {0x7892, CRL_REG_LEN_08BIT, 0x0f}, + {0x7893, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x00}, + {0x30a7, CRL_REG_LEN_08BIT, 0x48}, + {0x30ab, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x3001, CRL_REG_LEN_08BIT, 0x32}, + {0x3005, CRL_REG_LEN_08BIT, 0x13}, + {0x3014, CRL_REG_LEN_08BIT, 0x44}, + {0x30b0, CRL_REG_LEN_08BIT, 0x1d}, + {0x30b1, CRL_REG_LEN_08BIT, 0xe2}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x60}, + {0x30b6, CRL_REG_LEN_08BIT, 0x04}, + {0x30b7, CRL_REG_LEN_08BIT, 0x5c}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x0a}, + {0x3195, CRL_REG_LEN_08BIT, 0x04}, + {0x31e3, CRL_REG_LEN_08BIT, 0x02}, + {0x31e4, CRL_REG_LEN_08BIT, 0x10}, + {0x30bb, CRL_REG_LEN_08BIT, 0x40}, + {0x3250, CRL_REG_LEN_08BIT, 0xf7}, + {0x3012, CRL_REG_LEN_08BIT, 0x01}, +}; + +/* ov2775_1928x1088_linearlcg_30fps_mipi960_regset */ +static struct crl_register_write_rep + ov2775_linear_lcg_30fps_mipi960_regset[] = { + {0x3013, CRL_REG_LEN_08BIT, 0x01}, + {0x3000, CRL_REG_LEN_08BIT, 0x02}, + {0x3001, CRL_REG_LEN_08BIT, 0x28}, + {0x3002, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x01}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, + {0x3005, CRL_REG_LEN_08BIT, 0x26}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x07}, + {0x3008, CRL_REG_LEN_08BIT, 0x01}, + {0x3009, CRL_REG_LEN_08BIT, 0x00}, + {0x300c, CRL_REG_LEN_08BIT, 0x6c}, + {0x300e, CRL_REG_LEN_08BIT, 0x80}, + {0x300f, CRL_REG_LEN_08BIT, 0x00}, + {0x3012, CRL_REG_LEN_08BIT, 0x00}, + {0x3013, CRL_REG_LEN_08BIT, 0x00}, + {0x3014, CRL_REG_LEN_08BIT, 0xc4}, + {0x3015, CRL_REG_LEN_08BIT, 0x00}, + {0x3017, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x00}, + {0x3019, CRL_REG_LEN_08BIT, 0x00}, + {0x301a, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0x0e}, + {0x301e, CRL_REG_LEN_08BIT, 0x17}, + {0x301f, CRL_REG_LEN_08BIT, 0xe1}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x3031, CRL_REG_LEN_08BIT, 0x62}, + {0x3032, CRL_REG_LEN_08BIT, 0xf0}, + {0x3033, CRL_REG_LEN_08BIT, 0x30}, + {0x3034, CRL_REG_LEN_08BIT, 0x3f}, + {0x3035, CRL_REG_LEN_08BIT, 0x5f}, + {0x3036, CRL_REG_LEN_08BIT, 0x02}, + {0x3037, CRL_REG_LEN_08BIT, 0x9f}, + {0x3038, CRL_REG_LEN_08BIT, 0x04}, + {0x3039, CRL_REG_LEN_08BIT, 0xb7}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x07}, + {0x303c, CRL_REG_LEN_08BIT, 0xf0}, + {0x303d, CRL_REG_LEN_08BIT, 0x00}, + {0x303e, CRL_REG_LEN_08BIT, 0x0b}, + {0x303f, CRL_REG_LEN_08BIT, 0xe3}, + {0x3040, CRL_REG_LEN_08BIT, 0xf3}, + {0x3041, CRL_REG_LEN_08BIT, 0x29}, + {0x3042, CRL_REG_LEN_08BIT, 0xf6}, + {0x3043, CRL_REG_LEN_08BIT, 0x65}, + {0x3044, CRL_REG_LEN_08BIT, 0x06}, + {0x3045, CRL_REG_LEN_08BIT, 0x0f}, + {0x3046, CRL_REG_LEN_08BIT, 0x59}, + {0x3047, CRL_REG_LEN_08BIT, 0x07}, + {0x3048, CRL_REG_LEN_08BIT, 0x82}, + {0x3049, CRL_REG_LEN_08BIT, 0xcf}, + {0x304a, CRL_REG_LEN_08BIT, 0x12}, + {0x304b, CRL_REG_LEN_08BIT, 0x40}, + {0x304c, CRL_REG_LEN_08BIT, 0x33}, + {0x304d, CRL_REG_LEN_08BIT, 0xa4}, + {0x304e, CRL_REG_LEN_08BIT, 0x0b}, + {0x304f, CRL_REG_LEN_08BIT, 0x3d}, + {0x3050, CRL_REG_LEN_08BIT, 0x10}, + {0x3060, CRL_REG_LEN_08BIT, 0x00}, + {0x3061, CRL_REG_LEN_08BIT, 0x64}, + {0x3062, CRL_REG_LEN_08BIT, 0x00}, + {0x3063, CRL_REG_LEN_08BIT, 0xe4}, + {0x3066, CRL_REG_LEN_08BIT, 0x80}, + {0x3080, CRL_REG_LEN_08BIT, 0x00}, + {0x3081, CRL_REG_LEN_08BIT, 0x00}, + {0x3082, CRL_REG_LEN_08BIT, 0x01}, + {0x3083, CRL_REG_LEN_08BIT, 0xe3}, + {0x3084, CRL_REG_LEN_08BIT, 0x06}, + {0x3085, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x10}, + {0x3087, CRL_REG_LEN_08BIT, 0x10}, + {0x3089, CRL_REG_LEN_08BIT, 0x00}, + {0x308a, CRL_REG_LEN_08BIT, 0x01}, + {0x3093, CRL_REG_LEN_08BIT, 0x00}, + {0x30a0, CRL_REG_LEN_08BIT, 0x00}, + {0x30a1, CRL_REG_LEN_08BIT, 0x04}, + {0x30a2, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30a4, CRL_REG_LEN_08BIT, 0x07}, + {0x30a5, CRL_REG_LEN_08BIT, 0x8b}, + {0x30a6, CRL_REG_LEN_08BIT, 0x04}, + {0x30a7, CRL_REG_LEN_08BIT, 0x3f}, + {0x30a8, CRL_REG_LEN_08BIT, 0x00}, + {0x30a9, CRL_REG_LEN_08BIT, 0x04}, + {0x30aa, CRL_REG_LEN_08BIT, 0x00}, + {0x30ab, CRL_REG_LEN_08BIT, 0x00}, + {0x30ac, CRL_REG_LEN_08BIT, 0x07}, + {0x30ad, CRL_REG_LEN_08BIT, 0x80}, + {0x30ae, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x08}, + {0x30b1, CRL_REG_LEN_08BIT, 0x98}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x65}, + {0x30b4, CRL_REG_LEN_08BIT, 0x00}, + {0x30b5, CRL_REG_LEN_08BIT, 0x00}, + {0x30b6, CRL_REG_LEN_08BIT, 0x00}, + {0x30b7, CRL_REG_LEN_08BIT, 0x10}, + {0x30b8, CRL_REG_LEN_08BIT, 0x00}, + {0x30b9, CRL_REG_LEN_08BIT, 0x02}, + {0x30ba, CRL_REG_LEN_08BIT, 0x10}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x03}, + {0x30be, CRL_REG_LEN_08BIT, 0x5c}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x01}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x20}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x4a}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x00}, + {0x30d1, CRL_REG_LEN_08BIT, 0x00}, + {0x30d2, CRL_REG_LEN_08BIT, 0x00}, + {0x30d3, CRL_REG_LEN_08BIT, 0x80}, + {0x30d4, CRL_REG_LEN_08BIT, 0x00}, + {0x30d9, CRL_REG_LEN_08BIT, 0x09}, + {0x30da, CRL_REG_LEN_08BIT, 0x64}, + {0x30dd, CRL_REG_LEN_08BIT, 0x00}, + {0x30de, CRL_REG_LEN_08BIT, 0x16}, + {0x30df, CRL_REG_LEN_08BIT, 0x00}, + {0x30e0, CRL_REG_LEN_08BIT, 0x17}, + {0x30e1, CRL_REG_LEN_08BIT, 0x00}, + {0x30e2, CRL_REG_LEN_08BIT, 0x18}, + {0x30e3, CRL_REG_LEN_08BIT, 0x10}, + {0x30e4, CRL_REG_LEN_08BIT, 0x04}, + {0x30e5, CRL_REG_LEN_08BIT, 0x00}, + {0x30e6, CRL_REG_LEN_08BIT, 0x00}, + {0x30e7, CRL_REG_LEN_08BIT, 0x00}, + {0x30e8, CRL_REG_LEN_08BIT, 0x00}, + {0x30e9, CRL_REG_LEN_08BIT, 0x00}, + {0x30ea, CRL_REG_LEN_08BIT, 0x00}, + {0x30eb, CRL_REG_LEN_08BIT, 0x00}, + {0x30ec, CRL_REG_LEN_08BIT, 0x00}, + {0x30ed, CRL_REG_LEN_08BIT, 0x00}, + {0x3101, CRL_REG_LEN_08BIT, 0x00}, + {0x3102, CRL_REG_LEN_08BIT, 0x00}, + {0x3103, CRL_REG_LEN_08BIT, 0x00}, + {0x3104, CRL_REG_LEN_08BIT, 0x00}, + {0x3105, CRL_REG_LEN_08BIT, 0x8c}, + {0x3106, CRL_REG_LEN_08BIT, 0x87}, + {0x3107, CRL_REG_LEN_08BIT, 0xc0}, + {0x3108, CRL_REG_LEN_08BIT, 0x9d}, + {0x3109, CRL_REG_LEN_08BIT, 0x8d}, + {0x310a, CRL_REG_LEN_08BIT, 0x8d}, + {0x310b, CRL_REG_LEN_08BIT, 0x6a}, + {0x310c, CRL_REG_LEN_08BIT, 0x3a}, + {0x310d, CRL_REG_LEN_08BIT, 0x5a}, + {0x310e, CRL_REG_LEN_08BIT, 0x00}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x00}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3123, CRL_REG_LEN_08BIT, 0xf0}, + {0x3124, CRL_REG_LEN_08BIT, 0x00}, + {0x3125, CRL_REG_LEN_08BIT, 0x70}, + {0x3126, CRL_REG_LEN_08BIT, 0x1f}, + {0x3127, CRL_REG_LEN_08BIT, 0x0f}, + {0x3128, CRL_REG_LEN_08BIT, 0x00}, + {0x3129, CRL_REG_LEN_08BIT, 0x3a}, + {0x312a, CRL_REG_LEN_08BIT, 0x02}, + {0x312b, CRL_REG_LEN_08BIT, 0x0f}, + {0x312c, CRL_REG_LEN_08BIT, 0x00}, + {0x312d, CRL_REG_LEN_08BIT, 0x0f}, + {0x312e, CRL_REG_LEN_08BIT, 0x1d}, + {0x312f, CRL_REG_LEN_08BIT, 0x00}, + {0x3130, CRL_REG_LEN_08BIT, 0x00}, + {0x3131, CRL_REG_LEN_08BIT, 0x00}, + {0x3132, CRL_REG_LEN_08BIT, 0x00}, + {0x3140, CRL_REG_LEN_08BIT, 0x0a}, + {0x3141, CRL_REG_LEN_08BIT, 0x03}, + {0x3142, CRL_REG_LEN_08BIT, 0x00}, + {0x3143, CRL_REG_LEN_08BIT, 0x00}, + {0x3144, CRL_REG_LEN_08BIT, 0x00}, + {0x3145, CRL_REG_LEN_08BIT, 0x00}, + {0x3146, CRL_REG_LEN_08BIT, 0x00}, + {0x3147, CRL_REG_LEN_08BIT, 0x00}, + {0x3148, CRL_REG_LEN_08BIT, 0x00}, + {0x3149, CRL_REG_LEN_08BIT, 0x00}, + {0x314a, CRL_REG_LEN_08BIT, 0x00}, + {0x314b, CRL_REG_LEN_08BIT, 0x00}, + {0x314c, CRL_REG_LEN_08BIT, 0x00}, + {0x314d, CRL_REG_LEN_08BIT, 0x00}, + {0x314e, CRL_REG_LEN_08BIT, 0x1c}, + {0x314f, CRL_REG_LEN_08BIT, 0xff}, + {0x3150, CRL_REG_LEN_08BIT, 0xff}, + {0x3151, CRL_REG_LEN_08BIT, 0xff}, + {0x3152, CRL_REG_LEN_08BIT, 0x10}, + {0x3153, CRL_REG_LEN_08BIT, 0x10}, + {0x3154, CRL_REG_LEN_08BIT, 0x10}, + {0x3155, CRL_REG_LEN_08BIT, 0x00}, + {0x3156, CRL_REG_LEN_08BIT, 0x03}, + {0x3157, CRL_REG_LEN_08BIT, 0x00}, + {0x3158, CRL_REG_LEN_08BIT, 0x0f}, + {0x3159, CRL_REG_LEN_08BIT, 0xff}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3160, CRL_REG_LEN_08BIT, 0x01}, + {0x3161, CRL_REG_LEN_08BIT, 0x00}, + {0x3162, CRL_REG_LEN_08BIT, 0x01}, + {0x3163, CRL_REG_LEN_08BIT, 0x00}, + {0x3164, CRL_REG_LEN_08BIT, 0x01}, + {0x3165, CRL_REG_LEN_08BIT, 0x00}, + {0x3190, CRL_REG_LEN_08BIT, 0x08}, + {0x3191, CRL_REG_LEN_08BIT, 0x99}, + {0x3193, CRL_REG_LEN_08BIT, 0x08}, + {0x3194, CRL_REG_LEN_08BIT, 0x13}, + {0x3195, CRL_REG_LEN_08BIT, 0x33}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x10}, + {0x3198, CRL_REG_LEN_08BIT, 0x00}, + {0x3199, CRL_REG_LEN_08BIT, 0x7f}, + {0x319a, CRL_REG_LEN_08BIT, 0x80}, + {0x319b, CRL_REG_LEN_08BIT, 0xff}, + {0x319c, CRL_REG_LEN_08BIT, 0x80}, + {0x319d, CRL_REG_LEN_08BIT, 0xbf}, + {0x319e, CRL_REG_LEN_08BIT, 0xc0}, + {0x319f, CRL_REG_LEN_08BIT, 0xff}, + {0x31a0, CRL_REG_LEN_08BIT, 0x24}, + {0x31a1, CRL_REG_LEN_08BIT, 0x55}, + {0x31a2, CRL_REG_LEN_08BIT, 0x00}, + {0x31a3, CRL_REG_LEN_08BIT, 0x08}, + {0x31a6, CRL_REG_LEN_08BIT, 0x00}, + {0x31a7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b0, CRL_REG_LEN_08BIT, 0x00}, + {0x31b1, CRL_REG_LEN_08BIT, 0x00}, + {0x31b2, CRL_REG_LEN_08BIT, 0x02}, + {0x31b3, CRL_REG_LEN_08BIT, 0x00}, + {0x31b4, CRL_REG_LEN_08BIT, 0x00}, + {0x31b5, CRL_REG_LEN_08BIT, 0x01}, + {0x31b6, CRL_REG_LEN_08BIT, 0x00}, + {0x31b7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b8, CRL_REG_LEN_08BIT, 0x00}, + {0x31b9, CRL_REG_LEN_08BIT, 0x00}, + {0x31ba, CRL_REG_LEN_08BIT, 0x00}, + {0x31d0, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d1, CRL_REG_LEN_08BIT, 0x34}, + {0x31d2, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d3, CRL_REG_LEN_08BIT, 0x00}, + {0x31d4, CRL_REG_LEN_08BIT, 0x2d}, + {0x31d5, CRL_REG_LEN_08BIT, 0x00}, + {0x31d6, CRL_REG_LEN_08BIT, 0x01}, + {0x31d7, CRL_REG_LEN_08BIT, 0x06}, + {0x31d8, CRL_REG_LEN_08BIT, 0x00}, + {0x31d9, CRL_REG_LEN_08BIT, 0x64}, + {0x31da, CRL_REG_LEN_08BIT, 0x00}, + {0x31db, CRL_REG_LEN_08BIT, 0x30}, + {0x31dc, CRL_REG_LEN_08BIT, 0x04}, + {0x31dd, CRL_REG_LEN_08BIT, 0x69}, + {0x31de, CRL_REG_LEN_08BIT, 0x0a}, + {0x31df, CRL_REG_LEN_08BIT, 0x3c}, + {0x31e0, CRL_REG_LEN_08BIT, 0x04}, + {0x31e1, CRL_REG_LEN_08BIT, 0x32}, + {0x31e2, CRL_REG_LEN_08BIT, 0x00}, + {0x31e3, CRL_REG_LEN_08BIT, 0x00}, + {0x31e4, CRL_REG_LEN_08BIT, 0x08}, + {0x31e5, CRL_REG_LEN_08BIT, 0x80}, + {0x31e6, CRL_REG_LEN_08BIT, 0x00}, + {0x31e7, CRL_REG_LEN_08BIT, 0x2c}, + {0x31e8, CRL_REG_LEN_08BIT, 0x6c}, + {0x31e9, CRL_REG_LEN_08BIT, 0xac}, + {0x31ea, CRL_REG_LEN_08BIT, 0xec}, + {0x31eb, CRL_REG_LEN_08BIT, 0x3f}, + {0x31ec, CRL_REG_LEN_08BIT, 0x0f}, + {0x31ed, CRL_REG_LEN_08BIT, 0x20}, + {0x31ee, CRL_REG_LEN_08BIT, 0x04}, + {0x31ef, CRL_REG_LEN_08BIT, 0x48}, + {0x31f0, CRL_REG_LEN_08BIT, 0x07}, + {0x31f1, CRL_REG_LEN_08BIT, 0x90}, + {0x31f2, CRL_REG_LEN_08BIT, 0x04}, + {0x31f3, CRL_REG_LEN_08BIT, 0x48}, + {0x31f4, CRL_REG_LEN_08BIT, 0x07}, + {0x31f5, CRL_REG_LEN_08BIT, 0x90}, + {0x31f6, CRL_REG_LEN_08BIT, 0x04}, + {0x31f7, CRL_REG_LEN_08BIT, 0x48}, + {0x31f8, CRL_REG_LEN_08BIT, 0x07}, + {0x31f9, CRL_REG_LEN_08BIT, 0x90}, + {0x31fa, CRL_REG_LEN_08BIT, 0x04}, + {0x31fb, CRL_REG_LEN_08BIT, 0x48}, + {0x31fd, CRL_REG_LEN_08BIT, 0xcb}, + {0x31fe, CRL_REG_LEN_08BIT, 0x01}, + {0x31ff, CRL_REG_LEN_08BIT, 0x03}, + {0x3200, CRL_REG_LEN_08BIT, 0x00}, + {0x3201, CRL_REG_LEN_08BIT, 0xff}, + {0x3202, CRL_REG_LEN_08BIT, 0x00}, + {0x3203, CRL_REG_LEN_08BIT, 0xff}, + {0x3204, CRL_REG_LEN_08BIT, 0xff}, + {0x3205, CRL_REG_LEN_08BIT, 0xff}, + {0x3206, CRL_REG_LEN_08BIT, 0xff}, + {0x3207, CRL_REG_LEN_08BIT, 0xff}, + {0x3208, CRL_REG_LEN_08BIT, 0xff}, + {0x3209, CRL_REG_LEN_08BIT, 0xff}, + {0x320a, CRL_REG_LEN_08BIT, 0xff}, + {0x320b, CRL_REG_LEN_08BIT, 0x1b}, + {0x320c, CRL_REG_LEN_08BIT, 0x1f}, + {0x320d, CRL_REG_LEN_08BIT, 0x1e}, + {0x320e, CRL_REG_LEN_08BIT, 0x30}, + {0x320f, CRL_REG_LEN_08BIT, 0x2d}, + {0x3210, CRL_REG_LEN_08BIT, 0x2c}, + {0x3211, CRL_REG_LEN_08BIT, 0x2b}, + {0x3212, CRL_REG_LEN_08BIT, 0x2a}, + {0x3213, CRL_REG_LEN_08BIT, 0x24}, + {0x3214, CRL_REG_LEN_08BIT, 0x22}, + {0x3215, CRL_REG_LEN_08BIT, 0x00}, + {0x3216, CRL_REG_LEN_08BIT, 0x04}, + {0x3217, CRL_REG_LEN_08BIT, 0x2c}, + {0x3218, CRL_REG_LEN_08BIT, 0x6c}, + {0x3219, CRL_REG_LEN_08BIT, 0xac}, + {0x321a, CRL_REG_LEN_08BIT, 0xec}, + {0x321b, CRL_REG_LEN_08BIT, 0x00}, + {0x3230, CRL_REG_LEN_08BIT, 0x3a}, + {0x3231, CRL_REG_LEN_08BIT, 0x00}, + {0x3232, CRL_REG_LEN_08BIT, 0x80}, + {0x3233, CRL_REG_LEN_08BIT, 0x00}, + {0x3234, CRL_REG_LEN_08BIT, 0x10}, + {0x3235, CRL_REG_LEN_08BIT, 0xaa}, + {0x3236, CRL_REG_LEN_08BIT, 0x55}, + {0x3237, CRL_REG_LEN_08BIT, 0x99}, + {0x3238, CRL_REG_LEN_08BIT, 0x66}, + {0x3239, CRL_REG_LEN_08BIT, 0x08}, + {0x323a, CRL_REG_LEN_08BIT, 0x88}, + {0x323b, CRL_REG_LEN_08BIT, 0x00}, + {0x323c, CRL_REG_LEN_08BIT, 0x00}, + {0x323d, CRL_REG_LEN_08BIT, 0x03}, + {0x3250, CRL_REG_LEN_08BIT, 0x33}, + {0x3251, CRL_REG_LEN_08BIT, 0x00}, + {0x3252, CRL_REG_LEN_08BIT, 0x20}, + {0x3253, CRL_REG_LEN_08BIT, 0x00}, + {0x3254, CRL_REG_LEN_08BIT, 0x00}, + {0x3255, CRL_REG_LEN_08BIT, 0x01}, + {0x3256, CRL_REG_LEN_08BIT, 0x00}, + {0x3257, CRL_REG_LEN_08BIT, 0x00}, + {0x3258, CRL_REG_LEN_08BIT, 0x00}, + {0x3270, CRL_REG_LEN_08BIT, 0x01}, + {0x3271, CRL_REG_LEN_08BIT, 0xc0}, + {0x3272, CRL_REG_LEN_08BIT, 0xf0}, + {0x3273, CRL_REG_LEN_08BIT, 0x01}, + {0x3274, CRL_REG_LEN_08BIT, 0x00}, + {0x3275, CRL_REG_LEN_08BIT, 0x40}, + {0x3276, CRL_REG_LEN_08BIT, 0x02}, + {0x3277, CRL_REG_LEN_08BIT, 0x08}, + {0x3278, CRL_REG_LEN_08BIT, 0x10}, + {0x3279, CRL_REG_LEN_08BIT, 0x04}, + {0x327a, CRL_REG_LEN_08BIT, 0x00}, + {0x327b, CRL_REG_LEN_08BIT, 0x03}, + {0x327c, CRL_REG_LEN_08BIT, 0x10}, + {0x327d, CRL_REG_LEN_08BIT, 0x60}, + {0x327e, CRL_REG_LEN_08BIT, 0xc0}, + {0x327f, CRL_REG_LEN_08BIT, 0x06}, + {0x3288, CRL_REG_LEN_08BIT, 0x10}, + {0x3289, CRL_REG_LEN_08BIT, 0x00}, + {0x328a, CRL_REG_LEN_08BIT, 0x08}, + {0x328b, CRL_REG_LEN_08BIT, 0x00}, + {0x328c, CRL_REG_LEN_08BIT, 0x04}, + {0x328d, CRL_REG_LEN_08BIT, 0x00}, + {0x328e, CRL_REG_LEN_08BIT, 0x02}, + {0x328f, CRL_REG_LEN_08BIT, 0x00}, + {0x3290, CRL_REG_LEN_08BIT, 0x20}, + {0x3291, CRL_REG_LEN_08BIT, 0x00}, + {0x3292, CRL_REG_LEN_08BIT, 0x10}, + {0x3293, CRL_REG_LEN_08BIT, 0x00}, + {0x3294, CRL_REG_LEN_08BIT, 0x08}, + {0x3295, CRL_REG_LEN_08BIT, 0x00}, + {0x3296, CRL_REG_LEN_08BIT, 0x04}, + {0x3297, CRL_REG_LEN_08BIT, 0x00}, + {0x3298, CRL_REG_LEN_08BIT, 0x40}, + {0x3299, CRL_REG_LEN_08BIT, 0x00}, + {0x329a, CRL_REG_LEN_08BIT, 0x20}, + {0x329b, CRL_REG_LEN_08BIT, 0x00}, + {0x329c, CRL_REG_LEN_08BIT, 0x10}, + {0x329d, CRL_REG_LEN_08BIT, 0x00}, + {0x329e, CRL_REG_LEN_08BIT, 0x08}, + {0x329f, CRL_REG_LEN_08BIT, 0x00}, + {0x32a0, CRL_REG_LEN_08BIT, 0x7f}, + {0x32a1, CRL_REG_LEN_08BIT, 0xff}, + {0x32a2, CRL_REG_LEN_08BIT, 0x40}, + {0x32a3, CRL_REG_LEN_08BIT, 0x00}, + {0x32a4, CRL_REG_LEN_08BIT, 0x20}, + {0x32a5, CRL_REG_LEN_08BIT, 0x00}, + {0x32a6, CRL_REG_LEN_08BIT, 0x10}, + {0x32a7, CRL_REG_LEN_08BIT, 0x00}, + {0x32a8, CRL_REG_LEN_08BIT, 0x00}, + {0x32a9, CRL_REG_LEN_08BIT, 0x00}, + {0x32aa, CRL_REG_LEN_08BIT, 0x00}, + {0x32ab, CRL_REG_LEN_08BIT, 0x00}, + {0x32ac, CRL_REG_LEN_08BIT, 0x00}, + {0x32ad, CRL_REG_LEN_08BIT, 0x00}, + {0x32ae, CRL_REG_LEN_08BIT, 0x00}, + {0x32af, CRL_REG_LEN_08BIT, 0x00}, + {0x32b0, CRL_REG_LEN_08BIT, 0x00}, + {0x32b1, CRL_REG_LEN_08BIT, 0x00}, + {0x32b2, CRL_REG_LEN_08BIT, 0x00}, + {0x32b3, CRL_REG_LEN_08BIT, 0x00}, + {0x32b4, CRL_REG_LEN_08BIT, 0x00}, + {0x32b5, CRL_REG_LEN_08BIT, 0x00}, + {0x32b6, CRL_REG_LEN_08BIT, 0x00}, + {0x32b7, CRL_REG_LEN_08BIT, 0x00}, + {0x32b8, CRL_REG_LEN_08BIT, 0x00}, + {0x32b9, CRL_REG_LEN_08BIT, 0x00}, + {0x32ba, CRL_REG_LEN_08BIT, 0x00}, + {0x32bb, CRL_REG_LEN_08BIT, 0x00}, + {0x32bc, CRL_REG_LEN_08BIT, 0x00}, + {0x32bd, CRL_REG_LEN_08BIT, 0x00}, + {0x32be, CRL_REG_LEN_08BIT, 0x00}, + {0x32bf, CRL_REG_LEN_08BIT, 0x00}, + {0x32c0, CRL_REG_LEN_08BIT, 0x00}, + {0x32c1, CRL_REG_LEN_08BIT, 0x00}, + {0x32c2, CRL_REG_LEN_08BIT, 0x00}, + {0x32c3, CRL_REG_LEN_08BIT, 0x00}, + {0x32c4, CRL_REG_LEN_08BIT, 0x00}, + {0x32c5, CRL_REG_LEN_08BIT, 0x00}, + {0x32c6, CRL_REG_LEN_08BIT, 0x00}, + {0x32c7, CRL_REG_LEN_08BIT, 0x00}, + {0x32c8, CRL_REG_LEN_08BIT, 0x87}, + {0x32c9, CRL_REG_LEN_08BIT, 0x00}, + {0x3330, CRL_REG_LEN_08BIT, 0x03}, + {0x3331, CRL_REG_LEN_08BIT, 0xc8}, + {0x3332, CRL_REG_LEN_08BIT, 0x02}, + {0x3333, CRL_REG_LEN_08BIT, 0x24}, + {0x3334, CRL_REG_LEN_08BIT, 0x00}, + {0x3335, CRL_REG_LEN_08BIT, 0x00}, + {0x3336, CRL_REG_LEN_08BIT, 0x00}, + {0x3337, CRL_REG_LEN_08BIT, 0x00}, + {0x3338, CRL_REG_LEN_08BIT, 0x03}, + {0x3339, CRL_REG_LEN_08BIT, 0xc8}, + {0x333a, CRL_REG_LEN_08BIT, 0x02}, + {0x333b, CRL_REG_LEN_08BIT, 0x24}, + {0x333c, CRL_REG_LEN_08BIT, 0x00}, + {0x333d, CRL_REG_LEN_08BIT, 0x00}, + {0x333e, CRL_REG_LEN_08BIT, 0x00}, + {0x333f, CRL_REG_LEN_08BIT, 0x00}, + {0x3340, CRL_REG_LEN_08BIT, 0x03}, + {0x3341, CRL_REG_LEN_08BIT, 0xc8}, + {0x3342, CRL_REG_LEN_08BIT, 0x02}, + {0x3343, CRL_REG_LEN_08BIT, 0x24}, + {0x3344, CRL_REG_LEN_08BIT, 0x00}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3346, CRL_REG_LEN_08BIT, 0x00}, + {0x3347, CRL_REG_LEN_08BIT, 0x00}, + {0x3348, CRL_REG_LEN_08BIT, 0x40}, + {0x3349, CRL_REG_LEN_08BIT, 0x00}, + {0x334a, CRL_REG_LEN_08BIT, 0x00}, + {0x334b, CRL_REG_LEN_08BIT, 0x00}, + {0x334c, CRL_REG_LEN_08BIT, 0x00}, + {0x334d, CRL_REG_LEN_08BIT, 0x00}, + {0x334e, CRL_REG_LEN_08BIT, 0x80}, + {0x3360, CRL_REG_LEN_08BIT, 0x01}, + {0x3361, CRL_REG_LEN_08BIT, 0x00}, + {0x3362, CRL_REG_LEN_08BIT, 0x01}, + {0x3363, CRL_REG_LEN_08BIT, 0x00}, + {0x3364, CRL_REG_LEN_08BIT, 0x01}, + {0x3365, CRL_REG_LEN_08BIT, 0x00}, + {0x3366, CRL_REG_LEN_08BIT, 0x01}, + {0x3367, CRL_REG_LEN_08BIT, 0x00}, + {0x3368, CRL_REG_LEN_08BIT, 0x01}, + {0x3369, CRL_REG_LEN_08BIT, 0x00}, + {0x336a, CRL_REG_LEN_08BIT, 0x01}, + {0x336b, CRL_REG_LEN_08BIT, 0x00}, + {0x336c, CRL_REG_LEN_08BIT, 0x01}, + {0x336d, CRL_REG_LEN_08BIT, 0x00}, + {0x336e, CRL_REG_LEN_08BIT, 0x01}, + {0x336f, CRL_REG_LEN_08BIT, 0x00}, + {0x3370, CRL_REG_LEN_08BIT, 0x01}, + {0x3371, CRL_REG_LEN_08BIT, 0x00}, + {0x3372, CRL_REG_LEN_08BIT, 0x01}, + {0x3373, CRL_REG_LEN_08BIT, 0x00}, + {0x3374, CRL_REG_LEN_08BIT, 0x01}, + {0x3375, CRL_REG_LEN_08BIT, 0x00}, + {0x3376, CRL_REG_LEN_08BIT, 0x01}, + {0x3377, CRL_REG_LEN_08BIT, 0x00}, + {0x3378, CRL_REG_LEN_08BIT, 0x00}, + {0x3379, CRL_REG_LEN_08BIT, 0x00}, + {0x337a, CRL_REG_LEN_08BIT, 0x00}, + {0x337b, CRL_REG_LEN_08BIT, 0x00}, + {0x337c, CRL_REG_LEN_08BIT, 0x00}, + {0x337d, CRL_REG_LEN_08BIT, 0x00}, + {0x337e, CRL_REG_LEN_08BIT, 0x00}, + {0x337f, CRL_REG_LEN_08BIT, 0x00}, + {0x3380, CRL_REG_LEN_08BIT, 0x00}, + {0x3381, CRL_REG_LEN_08BIT, 0x00}, + {0x3382, CRL_REG_LEN_08BIT, 0x00}, + {0x3383, CRL_REG_LEN_08BIT, 0x00}, + {0x3384, CRL_REG_LEN_08BIT, 0x00}, + {0x3385, CRL_REG_LEN_08BIT, 0x00}, + {0x3386, CRL_REG_LEN_08BIT, 0x00}, + {0x3387, CRL_REG_LEN_08BIT, 0x00}, + {0x3388, CRL_REG_LEN_08BIT, 0x00}, + {0x3389, CRL_REG_LEN_08BIT, 0x00}, + {0x338a, CRL_REG_LEN_08BIT, 0x00}, + {0x338b, CRL_REG_LEN_08BIT, 0x00}, + {0x338c, CRL_REG_LEN_08BIT, 0x00}, + {0x338d, CRL_REG_LEN_08BIT, 0x00}, + {0x338e, CRL_REG_LEN_08BIT, 0x00}, + {0x338f, CRL_REG_LEN_08BIT, 0x00}, + {0x3390, CRL_REG_LEN_08BIT, 0x00}, + {0x3391, CRL_REG_LEN_08BIT, 0x00}, + {0x3392, CRL_REG_LEN_08BIT, 0x00}, + {0x3393, CRL_REG_LEN_08BIT, 0x00}, + {0x3394, CRL_REG_LEN_08BIT, 0x00}, + {0x3395, CRL_REG_LEN_08BIT, 0x00}, + {0x3396, CRL_REG_LEN_08BIT, 0x00}, + {0x3397, CRL_REG_LEN_08BIT, 0x00}, + {0x3398, CRL_REG_LEN_08BIT, 0x00}, + {0x3399, CRL_REG_LEN_08BIT, 0x00}, + {0x339a, CRL_REG_LEN_08BIT, 0x00}, + {0x339b, CRL_REG_LEN_08BIT, 0x00}, + {0x33b0, CRL_REG_LEN_08BIT, 0x00}, + {0x33b1, CRL_REG_LEN_08BIT, 0x50}, + {0x33b2, CRL_REG_LEN_08BIT, 0x01}, + {0x33b3, CRL_REG_LEN_08BIT, 0xff}, + {0x33b4, CRL_REG_LEN_08BIT, 0xe0}, + {0x33b5, CRL_REG_LEN_08BIT, 0x6b}, + {0x33b6, CRL_REG_LEN_08BIT, 0x00}, + {0x33b7, CRL_REG_LEN_08BIT, 0x00}, + {0x33b8, CRL_REG_LEN_08BIT, 0x00}, + {0x33b9, CRL_REG_LEN_08BIT, 0x00}, + {0x33ba, CRL_REG_LEN_08BIT, 0x00}, + {0x33bb, CRL_REG_LEN_08BIT, 0x1f}, + {0x33bc, CRL_REG_LEN_08BIT, 0x01}, + {0x33bd, CRL_REG_LEN_08BIT, 0x01}, + {0x33be, CRL_REG_LEN_08BIT, 0x01}, + {0x33bf, CRL_REG_LEN_08BIT, 0x01}, + {0x33c0, CRL_REG_LEN_08BIT, 0x00}, + {0x33c1, CRL_REG_LEN_08BIT, 0x00}, + {0x33c2, CRL_REG_LEN_08BIT, 0x00}, + {0x33c3, CRL_REG_LEN_08BIT, 0x00}, + {0x33e0, CRL_REG_LEN_08BIT, 0x14}, + {0x33e1, CRL_REG_LEN_08BIT, 0x0f}, + {0x33e2, CRL_REG_LEN_08BIT, 0x02}, + {0x33e3, CRL_REG_LEN_08BIT, 0x01}, + {0x33e4, CRL_REG_LEN_08BIT, 0x01}, + {0x33e5, CRL_REG_LEN_08BIT, 0x01}, + {0x33e6, CRL_REG_LEN_08BIT, 0x00}, + {0x33e7, CRL_REG_LEN_08BIT, 0x04}, + {0x33e8, CRL_REG_LEN_08BIT, 0x0c}, + {0x33e9, CRL_REG_LEN_08BIT, 0x02}, + {0x33ea, CRL_REG_LEN_08BIT, 0x02}, + {0x33eb, CRL_REG_LEN_08BIT, 0x02}, + {0x33ec, CRL_REG_LEN_08BIT, 0x03}, + {0x33ed, CRL_REG_LEN_08BIT, 0x01}, + {0x33ee, CRL_REG_LEN_08BIT, 0x02}, + {0x33ef, CRL_REG_LEN_08BIT, 0x08}, + {0x33f0, CRL_REG_LEN_08BIT, 0x08}, + {0x33f1, CRL_REG_LEN_08BIT, 0x04}, + {0x33f2, CRL_REG_LEN_08BIT, 0x04}, + {0x33f3, CRL_REG_LEN_08BIT, 0x00}, + {0x33f4, CRL_REG_LEN_08BIT, 0x03}, + {0x33f5, CRL_REG_LEN_08BIT, 0x14}, + {0x33f6, CRL_REG_LEN_08BIT, 0x0f}, + {0x33f7, CRL_REG_LEN_08BIT, 0x02}, + {0x33f8, CRL_REG_LEN_08BIT, 0x01}, + {0x33f9, CRL_REG_LEN_08BIT, 0x01}, + {0x33fa, CRL_REG_LEN_08BIT, 0x01}, + {0x33fb, CRL_REG_LEN_08BIT, 0x00}, + {0x33fc, CRL_REG_LEN_08BIT, 0x04}, + {0x33fd, CRL_REG_LEN_08BIT, 0x0c}, + {0x33fe, CRL_REG_LEN_08BIT, 0x02}, + {0x33ff, CRL_REG_LEN_08BIT, 0x02}, + {0x3400, CRL_REG_LEN_08BIT, 0x02}, + {0x3401, CRL_REG_LEN_08BIT, 0x03}, + {0x3402, CRL_REG_LEN_08BIT, 0x01}, + {0x3403, CRL_REG_LEN_08BIT, 0x02}, + {0x3404, CRL_REG_LEN_08BIT, 0x08}, + {0x3405, CRL_REG_LEN_08BIT, 0x08}, + {0x3406, CRL_REG_LEN_08BIT, 0x04}, + {0x3407, CRL_REG_LEN_08BIT, 0x04}, + {0x3408, CRL_REG_LEN_08BIT, 0x00}, + {0x3409, CRL_REG_LEN_08BIT, 0x03}, + {0x340a, CRL_REG_LEN_08BIT, 0x14}, + {0x340b, CRL_REG_LEN_08BIT, 0x0f}, + {0x340c, CRL_REG_LEN_08BIT, 0x04}, + {0x340d, CRL_REG_LEN_08BIT, 0x02}, + {0x340e, CRL_REG_LEN_08BIT, 0x01}, + {0x340f, CRL_REG_LEN_08BIT, 0x01}, + {0x3410, CRL_REG_LEN_08BIT, 0x00}, + {0x3411, CRL_REG_LEN_08BIT, 0x04}, + {0x3412, CRL_REG_LEN_08BIT, 0x0c}, + {0x3413, CRL_REG_LEN_08BIT, 0x02}, + {0x3414, CRL_REG_LEN_08BIT, 0x02}, + {0x3415, CRL_REG_LEN_08BIT, 0x02}, + {0x3416, CRL_REG_LEN_08BIT, 0x03}, + {0x3417, CRL_REG_LEN_08BIT, 0x02}, + {0x3418, CRL_REG_LEN_08BIT, 0x05}, + {0x3419, CRL_REG_LEN_08BIT, 0x0a}, + {0x341a, CRL_REG_LEN_08BIT, 0x08}, + {0x341b, CRL_REG_LEN_08BIT, 0x04}, + {0x341c, CRL_REG_LEN_08BIT, 0x04}, + {0x341d, CRL_REG_LEN_08BIT, 0x00}, + {0x341e, CRL_REG_LEN_08BIT, 0x03}, + {0x3440, CRL_REG_LEN_08BIT, 0x00}, + {0x3441, CRL_REG_LEN_08BIT, 0x00}, + {0x3442, CRL_REG_LEN_08BIT, 0x00}, + {0x3443, CRL_REG_LEN_08BIT, 0x00}, + {0x3444, CRL_REG_LEN_08BIT, 0x02}, + {0x3445, CRL_REG_LEN_08BIT, 0xf0}, + {0x3446, CRL_REG_LEN_08BIT, 0x02}, + {0x3447, CRL_REG_LEN_08BIT, 0x08}, + {0x3448, CRL_REG_LEN_08BIT, 0x00}, + {0x3460, CRL_REG_LEN_08BIT, 0x40}, + {0x3461, CRL_REG_LEN_08BIT, 0x40}, + {0x3462, CRL_REG_LEN_08BIT, 0x40}, + {0x3463, CRL_REG_LEN_08BIT, 0x40}, + {0x3464, CRL_REG_LEN_08BIT, 0x03}, + {0x3465, CRL_REG_LEN_08BIT, 0x01}, + {0x3466, CRL_REG_LEN_08BIT, 0x01}, + {0x3467, CRL_REG_LEN_08BIT, 0x02}, + {0x3468, CRL_REG_LEN_08BIT, 0x30}, + {0x3469, CRL_REG_LEN_08BIT, 0x00}, + {0x346a, CRL_REG_LEN_08BIT, 0x33}, + {0x346b, CRL_REG_LEN_08BIT, 0xbf}, + {0x3480, CRL_REG_LEN_08BIT, 0x40}, + {0x3481, CRL_REG_LEN_08BIT, 0x00}, + {0x3482, CRL_REG_LEN_08BIT, 0x00}, + {0x3483, CRL_REG_LEN_08BIT, 0x00}, + {0x3484, CRL_REG_LEN_08BIT, 0x0d}, + {0x3485, CRL_REG_LEN_08BIT, 0x00}, + {0x3486, CRL_REG_LEN_08BIT, 0x00}, + {0x3487, CRL_REG_LEN_08BIT, 0x00}, + {0x3488, CRL_REG_LEN_08BIT, 0x00}, + {0x3489, CRL_REG_LEN_08BIT, 0x00}, + {0x348a, CRL_REG_LEN_08BIT, 0x00}, + {0x348b, CRL_REG_LEN_08BIT, 0x04}, + {0x348c, CRL_REG_LEN_08BIT, 0x00}, + {0x348d, CRL_REG_LEN_08BIT, 0x01}, + {0x348f, CRL_REG_LEN_08BIT, 0x01}, + {0x3030, CRL_REG_LEN_08BIT, 0x0a}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x7000, CRL_REG_LEN_08BIT, 0x58}, + {0x7001, CRL_REG_LEN_08BIT, 0x7a}, + {0x7002, CRL_REG_LEN_08BIT, 0x1a}, + {0x7003, CRL_REG_LEN_08BIT, 0xc1}, + {0x7004, CRL_REG_LEN_08BIT, 0x03}, + {0x7005, CRL_REG_LEN_08BIT, 0xda}, + {0x7006, CRL_REG_LEN_08BIT, 0xbd}, + {0x7007, CRL_REG_LEN_08BIT, 0x03}, + {0x7008, CRL_REG_LEN_08BIT, 0xbd}, + {0x7009, CRL_REG_LEN_08BIT, 0x06}, + {0x700a, CRL_REG_LEN_08BIT, 0xe6}, + {0x700b, CRL_REG_LEN_08BIT, 0xec}, + {0x700c, CRL_REG_LEN_08BIT, 0xbc}, + {0x700d, CRL_REG_LEN_08BIT, 0xff}, + {0x700e, CRL_REG_LEN_08BIT, 0xbc}, + {0x700f, CRL_REG_LEN_08BIT, 0x73}, + {0x7010, CRL_REG_LEN_08BIT, 0xda}, + {0x7011, CRL_REG_LEN_08BIT, 0x72}, + {0x7012, CRL_REG_LEN_08BIT, 0x76}, + {0x7013, CRL_REG_LEN_08BIT, 0xb6}, + {0x7014, CRL_REG_LEN_08BIT, 0xee}, + {0x7015, CRL_REG_LEN_08BIT, 0xcf}, + {0x7016, CRL_REG_LEN_08BIT, 0xac}, + {0x7017, CRL_REG_LEN_08BIT, 0xd0}, + {0x7018, CRL_REG_LEN_08BIT, 0xac}, + {0x7019, CRL_REG_LEN_08BIT, 0xd1}, + {0x701a, CRL_REG_LEN_08BIT, 0x50}, + {0x701b, CRL_REG_LEN_08BIT, 0xac}, + {0x701c, CRL_REG_LEN_08BIT, 0xd2}, + {0x701d, CRL_REG_LEN_08BIT, 0xbc}, + {0x701e, CRL_REG_LEN_08BIT, 0x2e}, + {0x701f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7020, CRL_REG_LEN_08BIT, 0x00}, + {0x7021, CRL_REG_LEN_08BIT, 0xdc}, + {0x7022, CRL_REG_LEN_08BIT, 0xdf}, + {0x7023, CRL_REG_LEN_08BIT, 0xb0}, + {0x7024, CRL_REG_LEN_08BIT, 0x6e}, + {0x7025, CRL_REG_LEN_08BIT, 0xbd}, + {0x7026, CRL_REG_LEN_08BIT, 0x01}, + {0x7027, CRL_REG_LEN_08BIT, 0xd7}, + {0x7028, CRL_REG_LEN_08BIT, 0xed}, + {0x7029, CRL_REG_LEN_08BIT, 0xe1}, + {0x702a, CRL_REG_LEN_08BIT, 0x36}, + {0x702b, CRL_REG_LEN_08BIT, 0x30}, + {0x702c, CRL_REG_LEN_08BIT, 0xd3}, + {0x702d, CRL_REG_LEN_08BIT, 0x2e}, + {0x702e, CRL_REG_LEN_08BIT, 0x54}, + {0x702f, CRL_REG_LEN_08BIT, 0x46}, + {0x7030, CRL_REG_LEN_08BIT, 0xbc}, + {0x7031, CRL_REG_LEN_08BIT, 0x22}, + {0x7032, CRL_REG_LEN_08BIT, 0x66}, + {0x7033, CRL_REG_LEN_08BIT, 0xbc}, + {0x7034, CRL_REG_LEN_08BIT, 0x24}, + {0x7035, CRL_REG_LEN_08BIT, 0x2c}, + {0x7036, CRL_REG_LEN_08BIT, 0x28}, + {0x7037, CRL_REG_LEN_08BIT, 0xbc}, + {0x7038, CRL_REG_LEN_08BIT, 0x3c}, + {0x7039, CRL_REG_LEN_08BIT, 0xa1}, + {0x703a, CRL_REG_LEN_08BIT, 0xac}, + {0x703b, CRL_REG_LEN_08BIT, 0xd8}, + {0x703c, CRL_REG_LEN_08BIT, 0xd6}, + {0x703d, CRL_REG_LEN_08BIT, 0xb4}, + {0x703e, CRL_REG_LEN_08BIT, 0x04}, + {0x703f, CRL_REG_LEN_08BIT, 0x46}, + {0x7040, CRL_REG_LEN_08BIT, 0xb7}, + {0x7041, CRL_REG_LEN_08BIT, 0x04}, + {0x7042, CRL_REG_LEN_08BIT, 0xbe}, + {0x7043, CRL_REG_LEN_08BIT, 0x08}, + {0x7044, CRL_REG_LEN_08BIT, 0xc3}, + {0x7045, CRL_REG_LEN_08BIT, 0xd9}, + {0x7046, CRL_REG_LEN_08BIT, 0xad}, + {0x7047, CRL_REG_LEN_08BIT, 0xc3}, + {0x7048, CRL_REG_LEN_08BIT, 0xbc}, + {0x7049, CRL_REG_LEN_08BIT, 0x19}, + {0x704a, CRL_REG_LEN_08BIT, 0xc1}, + {0x704b, CRL_REG_LEN_08BIT, 0x27}, + {0x704c, CRL_REG_LEN_08BIT, 0xe7}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x704e, CRL_REG_LEN_08BIT, 0x50}, + {0x704f, CRL_REG_LEN_08BIT, 0x20}, + {0x7050, CRL_REG_LEN_08BIT, 0xb8}, + {0x7051, CRL_REG_LEN_08BIT, 0x02}, + {0x7052, CRL_REG_LEN_08BIT, 0xbc}, + {0x7053, CRL_REG_LEN_08BIT, 0x17}, + {0x7054, CRL_REG_LEN_08BIT, 0xdb}, + {0x7055, CRL_REG_LEN_08BIT, 0xc7}, + {0x7056, CRL_REG_LEN_08BIT, 0xb8}, + {0x7057, CRL_REG_LEN_08BIT, 0x00}, + {0x7058, CRL_REG_LEN_08BIT, 0x28}, + {0x7059, CRL_REG_LEN_08BIT, 0x54}, + {0x705a, CRL_REG_LEN_08BIT, 0xb4}, + {0x705b, CRL_REG_LEN_08BIT, 0x14}, + {0x705c, CRL_REG_LEN_08BIT, 0xab}, + {0x705d, CRL_REG_LEN_08BIT, 0xbe}, + {0x705e, CRL_REG_LEN_08BIT, 0x06}, + {0x705f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7060, CRL_REG_LEN_08BIT, 0xd6}, + {0x7061, CRL_REG_LEN_08BIT, 0x00}, + {0x7062, CRL_REG_LEN_08BIT, 0xb4}, + {0x7063, CRL_REG_LEN_08BIT, 0xc7}, + {0x7064, CRL_REG_LEN_08BIT, 0x07}, + {0x7065, CRL_REG_LEN_08BIT, 0xb9}, + {0x7066, CRL_REG_LEN_08BIT, 0x05}, + {0x7067, CRL_REG_LEN_08BIT, 0xee}, + {0x7068, CRL_REG_LEN_08BIT, 0xe6}, + {0x7069, CRL_REG_LEN_08BIT, 0xad}, + {0x706a, CRL_REG_LEN_08BIT, 0xb4}, + {0x706b, CRL_REG_LEN_08BIT, 0x26}, + {0x706c, CRL_REG_LEN_08BIT, 0x19}, + {0x706d, CRL_REG_LEN_08BIT, 0xc1}, + {0x706e, CRL_REG_LEN_08BIT, 0x3a}, + {0x706f, CRL_REG_LEN_08BIT, 0xc3}, + {0x7070, CRL_REG_LEN_08BIT, 0xaf}, + {0x7071, CRL_REG_LEN_08BIT, 0x00}, + {0x7072, CRL_REG_LEN_08BIT, 0xc0}, + {0x7073, CRL_REG_LEN_08BIT, 0x3c}, + {0x7074, CRL_REG_LEN_08BIT, 0xc3}, + {0x7075, CRL_REG_LEN_08BIT, 0xbe}, + {0x7076, CRL_REG_LEN_08BIT, 0xe7}, + {0x7077, CRL_REG_LEN_08BIT, 0x00}, + {0x7078, CRL_REG_LEN_08BIT, 0x15}, + {0x7079, CRL_REG_LEN_08BIT, 0xc2}, + {0x707a, CRL_REG_LEN_08BIT, 0x40}, + {0x707b, CRL_REG_LEN_08BIT, 0xc3}, + {0x707c, CRL_REG_LEN_08BIT, 0xa4}, + {0x707d, CRL_REG_LEN_08BIT, 0xc0}, + {0x707e, CRL_REG_LEN_08BIT, 0x3c}, + {0x707f, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0xb9}, + {0x7081, CRL_REG_LEN_08BIT, 0x64}, + {0x7082, CRL_REG_LEN_08BIT, 0x29}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7084, CRL_REG_LEN_08BIT, 0xb8}, + {0x7085, CRL_REG_LEN_08BIT, 0x12}, + {0x7086, CRL_REG_LEN_08BIT, 0xbe}, + {0x7087, CRL_REG_LEN_08BIT, 0x01}, + {0x7088, CRL_REG_LEN_08BIT, 0xd0}, + {0x7089, CRL_REG_LEN_08BIT, 0xbc}, + {0x708a, CRL_REG_LEN_08BIT, 0x01}, + {0x708b, CRL_REG_LEN_08BIT, 0xac}, + {0x708c, CRL_REG_LEN_08BIT, 0x37}, + {0x708d, CRL_REG_LEN_08BIT, 0xd2}, + {0x708e, CRL_REG_LEN_08BIT, 0xac}, + {0x708f, CRL_REG_LEN_08BIT, 0x45}, + {0x7090, CRL_REG_LEN_08BIT, 0xad}, + {0x7091, CRL_REG_LEN_08BIT, 0x28}, + {0x7092, CRL_REG_LEN_08BIT, 0x00}, + {0x7093, CRL_REG_LEN_08BIT, 0xb8}, + {0x7094, CRL_REG_LEN_08BIT, 0x00}, + {0x7095, CRL_REG_LEN_08BIT, 0xbc}, + {0x7096, CRL_REG_LEN_08BIT, 0x01}, + {0x7097, CRL_REG_LEN_08BIT, 0x36}, + {0x7098, CRL_REG_LEN_08BIT, 0xd3}, + {0x7099, CRL_REG_LEN_08BIT, 0x30}, + {0x709a, CRL_REG_LEN_08BIT, 0x04}, + {0x709b, CRL_REG_LEN_08BIT, 0xe0}, + {0x709c, CRL_REG_LEN_08BIT, 0xd8}, + {0x709d, CRL_REG_LEN_08BIT, 0xb4}, + {0x709e, CRL_REG_LEN_08BIT, 0xe9}, + {0x709f, CRL_REG_LEN_08BIT, 0x00}, + {0x70a0, CRL_REG_LEN_08BIT, 0xbe}, + {0x70a1, CRL_REG_LEN_08BIT, 0x05}, + {0x70a2, CRL_REG_LEN_08BIT, 0x62}, + {0x70a3, CRL_REG_LEN_08BIT, 0x07}, + {0x70a4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70a5, CRL_REG_LEN_08BIT, 0x05}, + {0x70a6, CRL_REG_LEN_08BIT, 0xad}, + {0x70a7, CRL_REG_LEN_08BIT, 0xc3}, + {0x70a8, CRL_REG_LEN_08BIT, 0xcf}, + {0x70a9, CRL_REG_LEN_08BIT, 0x00}, + {0x70aa, CRL_REG_LEN_08BIT, 0x15}, + {0x70ab, CRL_REG_LEN_08BIT, 0xc2}, + {0x70ac, CRL_REG_LEN_08BIT, 0x59}, + {0x70ad, CRL_REG_LEN_08BIT, 0xc3}, + {0x70ae, CRL_REG_LEN_08BIT, 0xc9}, + {0x70af, CRL_REG_LEN_08BIT, 0xc0}, + {0x70b0, CRL_REG_LEN_08BIT, 0x55}, + {0x70b1, CRL_REG_LEN_08BIT, 0x00}, + {0x70b2, CRL_REG_LEN_08BIT, 0x46}, + {0x70b3, CRL_REG_LEN_08BIT, 0xa1}, + {0x70b4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70b5, CRL_REG_LEN_08BIT, 0x64}, + {0x70b6, CRL_REG_LEN_08BIT, 0x29}, + {0x70b7, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70b9, CRL_REG_LEN_08BIT, 0x02}, + {0x70ba, CRL_REG_LEN_08BIT, 0xbe}, + {0x70bb, CRL_REG_LEN_08BIT, 0x02}, + {0x70bc, CRL_REG_LEN_08BIT, 0xd0}, + {0x70bd, CRL_REG_LEN_08BIT, 0xdc}, + {0x70be, CRL_REG_LEN_08BIT, 0xac}, + {0x70bf, CRL_REG_LEN_08BIT, 0xbc}, + {0x70c0, CRL_REG_LEN_08BIT, 0x01}, + {0x70c1, CRL_REG_LEN_08BIT, 0x37}, + {0x70c2, CRL_REG_LEN_08BIT, 0xac}, + {0x70c3, CRL_REG_LEN_08BIT, 0xd2}, + {0x70c4, CRL_REG_LEN_08BIT, 0x45}, + {0x70c5, CRL_REG_LEN_08BIT, 0xad}, + {0x70c6, CRL_REG_LEN_08BIT, 0x28}, + {0x70c7, CRL_REG_LEN_08BIT, 0x00}, + {0x70c8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70c9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ca, CRL_REG_LEN_08BIT, 0xbc}, + {0x70cb, CRL_REG_LEN_08BIT, 0x01}, + {0x70cc, CRL_REG_LEN_08BIT, 0x36}, + {0x70cd, CRL_REG_LEN_08BIT, 0x30}, + {0x70ce, CRL_REG_LEN_08BIT, 0xe0}, + {0x70cf, CRL_REG_LEN_08BIT, 0xd8}, + {0x70d0, CRL_REG_LEN_08BIT, 0xb5}, + {0x70d1, CRL_REG_LEN_08BIT, 0x0b}, + {0x70d2, CRL_REG_LEN_08BIT, 0xd6}, + {0x70d3, CRL_REG_LEN_08BIT, 0xbe}, + {0x70d4, CRL_REG_LEN_08BIT, 0x07}, + {0x70d5, CRL_REG_LEN_08BIT, 0x00}, + {0x70d6, CRL_REG_LEN_08BIT, 0x62}, + {0x70d7, CRL_REG_LEN_08BIT, 0x07}, + {0x70d8, CRL_REG_LEN_08BIT, 0xb9}, + {0x70d9, CRL_REG_LEN_08BIT, 0x05}, + {0x70da, CRL_REG_LEN_08BIT, 0xad}, + {0x70db, CRL_REG_LEN_08BIT, 0xc3}, + {0x70dc, CRL_REG_LEN_08BIT, 0xcf}, + {0x70dd, CRL_REG_LEN_08BIT, 0x46}, + {0x70de, CRL_REG_LEN_08BIT, 0xcd}, + {0x70df, CRL_REG_LEN_08BIT, 0x07}, + {0x70e0, CRL_REG_LEN_08BIT, 0xcd}, + {0x70e1, CRL_REG_LEN_08BIT, 0x00}, + {0x70e2, CRL_REG_LEN_08BIT, 0xe3}, + {0x70e3, CRL_REG_LEN_08BIT, 0x18}, + {0x70e4, CRL_REG_LEN_08BIT, 0xc2}, + {0x70e5, CRL_REG_LEN_08BIT, 0xa2}, + {0x70e6, CRL_REG_LEN_08BIT, 0xb9}, + {0x70e7, CRL_REG_LEN_08BIT, 0x64}, + {0x70e8, CRL_REG_LEN_08BIT, 0xd1}, + {0x70e9, CRL_REG_LEN_08BIT, 0xdd}, + {0x70ea, CRL_REG_LEN_08BIT, 0xac}, + {0x70eb, CRL_REG_LEN_08BIT, 0xcf}, + {0x70ec, CRL_REG_LEN_08BIT, 0xdf}, + {0x70ed, CRL_REG_LEN_08BIT, 0xb5}, + {0x70ee, CRL_REG_LEN_08BIT, 0x19}, + {0x70ef, CRL_REG_LEN_08BIT, 0x46}, + {0x70f0, CRL_REG_LEN_08BIT, 0x50}, + {0x70f1, CRL_REG_LEN_08BIT, 0xb6}, + {0x70f2, CRL_REG_LEN_08BIT, 0xee}, + {0x70f3, CRL_REG_LEN_08BIT, 0xe8}, + {0x70f4, CRL_REG_LEN_08BIT, 0xe6}, + {0x70f5, CRL_REG_LEN_08BIT, 0xbc}, + {0x70f6, CRL_REG_LEN_08BIT, 0x31}, + {0x70f7, CRL_REG_LEN_08BIT, 0xe1}, + {0x70f8, CRL_REG_LEN_08BIT, 0x36}, + {0x70f9, CRL_REG_LEN_08BIT, 0x30}, + {0x70fa, CRL_REG_LEN_08BIT, 0xd3}, + {0x70fb, CRL_REG_LEN_08BIT, 0x2e}, + {0x70fc, CRL_REG_LEN_08BIT, 0x54}, + {0x70fd, CRL_REG_LEN_08BIT, 0xbd}, + {0x70fe, CRL_REG_LEN_08BIT, 0x03}, + {0x70ff, CRL_REG_LEN_08BIT, 0xec}, + {0x7100, CRL_REG_LEN_08BIT, 0x2c}, + {0x7101, CRL_REG_LEN_08BIT, 0x50}, + {0x7102, CRL_REG_LEN_08BIT, 0x20}, + {0x7103, CRL_REG_LEN_08BIT, 0x04}, + {0x7104, CRL_REG_LEN_08BIT, 0xb8}, + {0x7105, CRL_REG_LEN_08BIT, 0x02}, + {0x7106, CRL_REG_LEN_08BIT, 0xbc}, + {0x7107, CRL_REG_LEN_08BIT, 0x18}, + {0x7108, CRL_REG_LEN_08BIT, 0xc7}, + {0x7109, CRL_REG_LEN_08BIT, 0xb8}, + {0x710a, CRL_REG_LEN_08BIT, 0x00}, + {0x710b, CRL_REG_LEN_08BIT, 0x28}, + {0x710c, CRL_REG_LEN_08BIT, 0x54}, + {0x710d, CRL_REG_LEN_08BIT, 0xbc}, + {0x710e, CRL_REG_LEN_08BIT, 0x02}, + {0x710f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7110, CRL_REG_LEN_08BIT, 0xda}, + {0x7111, CRL_REG_LEN_08BIT, 0xbe}, + {0x7112, CRL_REG_LEN_08BIT, 0x04}, + {0x7113, CRL_REG_LEN_08BIT, 0xd6}, + {0x7114, CRL_REG_LEN_08BIT, 0xd8}, + {0x7115, CRL_REG_LEN_08BIT, 0xab}, + {0x7116, CRL_REG_LEN_08BIT, 0x00}, + {0x7117, CRL_REG_LEN_08BIT, 0x62}, + {0x7118, CRL_REG_LEN_08BIT, 0x07}, + {0x7119, CRL_REG_LEN_08BIT, 0xb9}, + {0x711a, CRL_REG_LEN_08BIT, 0x05}, + {0x711b, CRL_REG_LEN_08BIT, 0xad}, + {0x711c, CRL_REG_LEN_08BIT, 0xc3}, + {0x711d, CRL_REG_LEN_08BIT, 0xbc}, + {0x711e, CRL_REG_LEN_08BIT, 0xe7}, + {0x711f, CRL_REG_LEN_08BIT, 0xb9}, + {0x7120, CRL_REG_LEN_08BIT, 0x64}, + {0x7121, CRL_REG_LEN_08BIT, 0x29}, + {0x7122, CRL_REG_LEN_08BIT, 0x00}, + {0x7123, CRL_REG_LEN_08BIT, 0xb8}, + {0x7124, CRL_REG_LEN_08BIT, 0x02}, + {0x7125, CRL_REG_LEN_08BIT, 0xbe}, + {0x7126, CRL_REG_LEN_08BIT, 0x00}, + {0x7127, CRL_REG_LEN_08BIT, 0x45}, + {0x7128, CRL_REG_LEN_08BIT, 0xad}, + {0x7129, CRL_REG_LEN_08BIT, 0xe2}, + {0x712a, CRL_REG_LEN_08BIT, 0x28}, + {0x712b, CRL_REG_LEN_08BIT, 0x00}, + {0x712c, CRL_REG_LEN_08BIT, 0xb8}, + {0x712d, CRL_REG_LEN_08BIT, 0x00}, + {0x712e, CRL_REG_LEN_08BIT, 0xe0}, + {0x712f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7130, CRL_REG_LEN_08BIT, 0xb4}, + {0x7131, CRL_REG_LEN_08BIT, 0xe9}, + {0x7132, CRL_REG_LEN_08BIT, 0xbe}, + {0x7133, CRL_REG_LEN_08BIT, 0x03}, + {0x7134, CRL_REG_LEN_08BIT, 0x00}, + {0x7135, CRL_REG_LEN_08BIT, 0x30}, + {0x7136, CRL_REG_LEN_08BIT, 0x62}, + {0x7137, CRL_REG_LEN_08BIT, 0x07}, + {0x7138, CRL_REG_LEN_08BIT, 0xb9}, + {0x7139, CRL_REG_LEN_08BIT, 0x05}, + {0x713a, CRL_REG_LEN_08BIT, 0xad}, + {0x713b, CRL_REG_LEN_08BIT, 0xc3}, + {0x713c, CRL_REG_LEN_08BIT, 0xcf}, + {0x713d, CRL_REG_LEN_08BIT, 0x42}, + {0x713e, CRL_REG_LEN_08BIT, 0xe4}, + {0x713f, CRL_REG_LEN_08BIT, 0xcd}, + {0x7140, CRL_REG_LEN_08BIT, 0x07}, + {0x7141, CRL_REG_LEN_08BIT, 0xcd}, + {0x7142, CRL_REG_LEN_08BIT, 0x00}, + {0x7143, CRL_REG_LEN_08BIT, 0x00}, + {0x7144, CRL_REG_LEN_08BIT, 0x17}, + {0x7145, CRL_REG_LEN_08BIT, 0xc2}, + {0x7146, CRL_REG_LEN_08BIT, 0xbb}, + {0x7147, CRL_REG_LEN_08BIT, 0xde}, + {0x7148, CRL_REG_LEN_08BIT, 0xcf}, + {0x7149, CRL_REG_LEN_08BIT, 0xdf}, + {0x714a, CRL_REG_LEN_08BIT, 0xac}, + {0x714b, CRL_REG_LEN_08BIT, 0xd1}, + {0x714c, CRL_REG_LEN_08BIT, 0x44}, + {0x714d, CRL_REG_LEN_08BIT, 0xac}, + {0x714e, CRL_REG_LEN_08BIT, 0xb9}, + {0x714f, CRL_REG_LEN_08BIT, 0x76}, + {0x7150, CRL_REG_LEN_08BIT, 0xb8}, + {0x7151, CRL_REG_LEN_08BIT, 0x08}, + {0x7152, CRL_REG_LEN_08BIT, 0xb6}, + {0x7153, CRL_REG_LEN_08BIT, 0xfe}, + {0x7154, CRL_REG_LEN_08BIT, 0xb4}, + {0x7155, CRL_REG_LEN_08BIT, 0xca}, + {0x7156, CRL_REG_LEN_08BIT, 0xd6}, + {0x7157, CRL_REG_LEN_08BIT, 0xd8}, + {0x7158, CRL_REG_LEN_08BIT, 0xab}, + {0x7159, CRL_REG_LEN_08BIT, 0x00}, + {0x715a, CRL_REG_LEN_08BIT, 0xe1}, + {0x715b, CRL_REG_LEN_08BIT, 0x36}, + {0x715c, CRL_REG_LEN_08BIT, 0x30}, + {0x715d, CRL_REG_LEN_08BIT, 0xd3}, + {0x715e, CRL_REG_LEN_08BIT, 0xbc}, + {0x715f, CRL_REG_LEN_08BIT, 0x29}, + {0x7160, CRL_REG_LEN_08BIT, 0xb4}, + {0x7161, CRL_REG_LEN_08BIT, 0x1f}, + {0x7162, CRL_REG_LEN_08BIT, 0xaa}, + {0x7163, CRL_REG_LEN_08BIT, 0xbd}, + {0x7164, CRL_REG_LEN_08BIT, 0x01}, + {0x7165, CRL_REG_LEN_08BIT, 0xb8}, + {0x7166, CRL_REG_LEN_08BIT, 0x0c}, + {0x7167, CRL_REG_LEN_08BIT, 0x45}, + {0x7168, CRL_REG_LEN_08BIT, 0xa4}, + {0x7169, CRL_REG_LEN_08BIT, 0xbd}, + {0x716a, CRL_REG_LEN_08BIT, 0x03}, + {0x716b, CRL_REG_LEN_08BIT, 0xec}, + {0x716c, CRL_REG_LEN_08BIT, 0xbc}, + {0x716d, CRL_REG_LEN_08BIT, 0x3d}, + {0x716e, CRL_REG_LEN_08BIT, 0xc3}, + {0x716f, CRL_REG_LEN_08BIT, 0xcf}, + {0x7170, CRL_REG_LEN_08BIT, 0x42}, + {0x7171, CRL_REG_LEN_08BIT, 0xb8}, + {0x7172, CRL_REG_LEN_08BIT, 0x00}, + {0x7173, CRL_REG_LEN_08BIT, 0xe4}, + {0x7174, CRL_REG_LEN_08BIT, 0xd5}, + {0x7175, CRL_REG_LEN_08BIT, 0x00}, + {0x7176, CRL_REG_LEN_08BIT, 0xb6}, + {0x7177, CRL_REG_LEN_08BIT, 0x00}, + {0x7178, CRL_REG_LEN_08BIT, 0x74}, + {0x7179, CRL_REG_LEN_08BIT, 0xbd}, + {0x717a, CRL_REG_LEN_08BIT, 0x03}, + {0x717b, CRL_REG_LEN_08BIT, 0xb5}, + {0x717c, CRL_REG_LEN_08BIT, 0x39}, + {0x717d, CRL_REG_LEN_08BIT, 0x40}, + {0x717e, CRL_REG_LEN_08BIT, 0x58}, + {0x717f, CRL_REG_LEN_08BIT, 0xdd}, + {0x7180, CRL_REG_LEN_08BIT, 0x19}, + {0x7181, CRL_REG_LEN_08BIT, 0xc1}, + {0x7182, CRL_REG_LEN_08BIT, 0xc8}, + {0x7183, CRL_REG_LEN_08BIT, 0xbd}, + {0x7184, CRL_REG_LEN_08BIT, 0x06}, + {0x7185, CRL_REG_LEN_08BIT, 0x17}, + {0x7186, CRL_REG_LEN_08BIT, 0xc1}, + {0x7187, CRL_REG_LEN_08BIT, 0xc6}, + {0x7188, CRL_REG_LEN_08BIT, 0xe8}, + {0x7189, CRL_REG_LEN_08BIT, 0x00}, + {0x718a, CRL_REG_LEN_08BIT, 0xc0}, + {0x718b, CRL_REG_LEN_08BIT, 0xc8}, + {0x718c, CRL_REG_LEN_08BIT, 0xe6}, + {0x718d, CRL_REG_LEN_08BIT, 0x95}, + {0x718e, CRL_REG_LEN_08BIT, 0x15}, + {0x718f, CRL_REG_LEN_08BIT, 0x00}, + {0x7190, CRL_REG_LEN_08BIT, 0xbc}, + {0x7191, CRL_REG_LEN_08BIT, 0x19}, + {0x7192, CRL_REG_LEN_08BIT, 0xb9}, + {0x7193, CRL_REG_LEN_08BIT, 0xf6}, + {0x7194, CRL_REG_LEN_08BIT, 0x14}, + {0x7195, CRL_REG_LEN_08BIT, 0xc1}, + {0x7196, CRL_REG_LEN_08BIT, 0xd0}, + {0x7197, CRL_REG_LEN_08BIT, 0xd1}, + {0x7198, CRL_REG_LEN_08BIT, 0xac}, + {0x7199, CRL_REG_LEN_08BIT, 0x37}, + {0x719a, CRL_REG_LEN_08BIT, 0xbc}, + {0x719b, CRL_REG_LEN_08BIT, 0x35}, + {0x719c, CRL_REG_LEN_08BIT, 0x36}, + {0x719d, CRL_REG_LEN_08BIT, 0x30}, + {0x719e, CRL_REG_LEN_08BIT, 0xe1}, + {0x719f, CRL_REG_LEN_08BIT, 0xd3}, + {0x71a0, CRL_REG_LEN_08BIT, 0x7a}, + {0x71a1, CRL_REG_LEN_08BIT, 0xb6}, + {0x71a2, CRL_REG_LEN_08BIT, 0x0c}, + {0x71a3, CRL_REG_LEN_08BIT, 0xff}, + {0x71a4, CRL_REG_LEN_08BIT, 0xb4}, + {0x71a5, CRL_REG_LEN_08BIT, 0xc7}, + {0x71a6, CRL_REG_LEN_08BIT, 0xd9}, + {0x71a7, CRL_REG_LEN_08BIT, 0x00}, + {0x71a8, CRL_REG_LEN_08BIT, 0xbd}, + {0x71a9, CRL_REG_LEN_08BIT, 0x01}, + {0x71aa, CRL_REG_LEN_08BIT, 0x56}, + {0x71ab, CRL_REG_LEN_08BIT, 0xc0}, + {0x71ac, CRL_REG_LEN_08BIT, 0xda}, + {0x71ad, CRL_REG_LEN_08BIT, 0xb4}, + {0x71ae, CRL_REG_LEN_08BIT, 0x1f}, + {0x71af, CRL_REG_LEN_08BIT, 0x56}, + {0x71b0, CRL_REG_LEN_08BIT, 0xaa}, + {0x71b1, CRL_REG_LEN_08BIT, 0xbc}, + {0x71b2, CRL_REG_LEN_08BIT, 0x08}, + {0x71b3, CRL_REG_LEN_08BIT, 0x00}, + {0x71b4, CRL_REG_LEN_08BIT, 0x57}, + {0x71b5, CRL_REG_LEN_08BIT, 0xe8}, + {0x71b6, CRL_REG_LEN_08BIT, 0xb5}, + {0x71b7, CRL_REG_LEN_08BIT, 0x36}, + {0x71b8, CRL_REG_LEN_08BIT, 0x00}, + {0x71b9, CRL_REG_LEN_08BIT, 0x54}, + {0x71ba, CRL_REG_LEN_08BIT, 0xe7}, + {0x71bb, CRL_REG_LEN_08BIT, 0xc8}, + {0x71bc, CRL_REG_LEN_08BIT, 0xb4}, + {0x71bd, CRL_REG_LEN_08BIT, 0x1f}, + {0x71be, CRL_REG_LEN_08BIT, 0x56}, + {0x71bf, CRL_REG_LEN_08BIT, 0xaa}, + {0x71c0, CRL_REG_LEN_08BIT, 0xbc}, + {0x71c1, CRL_REG_LEN_08BIT, 0x08}, + {0x71c2, CRL_REG_LEN_08BIT, 0x57}, + {0x71c3, CRL_REG_LEN_08BIT, 0x00}, + {0x71c4, CRL_REG_LEN_08BIT, 0xb5}, + {0x71c5, CRL_REG_LEN_08BIT, 0x36}, + {0x71c6, CRL_REG_LEN_08BIT, 0x00}, + {0x71c7, CRL_REG_LEN_08BIT, 0x54}, + {0x71c8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71c9, CRL_REG_LEN_08BIT, 0xb5}, + {0x71ca, CRL_REG_LEN_08BIT, 0x18}, + {0x71cb, CRL_REG_LEN_08BIT, 0xd9}, + {0x71cc, CRL_REG_LEN_08BIT, 0x00}, + {0x71cd, CRL_REG_LEN_08BIT, 0xbd}, + {0x71ce, CRL_REG_LEN_08BIT, 0x01}, + {0x71cf, CRL_REG_LEN_08BIT, 0x56}, + {0x71d0, CRL_REG_LEN_08BIT, 0x08}, + {0x71d1, CRL_REG_LEN_08BIT, 0x57}, + {0x71d2, CRL_REG_LEN_08BIT, 0xe8}, + {0x71d3, CRL_REG_LEN_08BIT, 0xb4}, + {0x71d4, CRL_REG_LEN_08BIT, 0x42}, + {0x71d5, CRL_REG_LEN_08BIT, 0x00}, + {0x71d6, CRL_REG_LEN_08BIT, 0x54}, + {0x71d7, CRL_REG_LEN_08BIT, 0xe7}, + {0x71d8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71d9, CRL_REG_LEN_08BIT, 0xab}, + {0x71da, CRL_REG_LEN_08BIT, 0x00}, + {0x71db, CRL_REG_LEN_08BIT, 0x66}, + {0x71dc, CRL_REG_LEN_08BIT, 0x62}, + {0x71dd, CRL_REG_LEN_08BIT, 0x06}, + {0x71de, CRL_REG_LEN_08BIT, 0x74}, + {0x71df, CRL_REG_LEN_08BIT, 0xb9}, + {0x71e0, CRL_REG_LEN_08BIT, 0x05}, + {0x71e1, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e2, CRL_REG_LEN_08BIT, 0x14}, + {0x71e3, CRL_REG_LEN_08BIT, 0x0e}, + {0x71e4, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e5, CRL_REG_LEN_08BIT, 0x04}, + {0x71e6, CRL_REG_LEN_08BIT, 0xc8}, + {0x7600, CRL_REG_LEN_08BIT, 0x04}, + {0x7601, CRL_REG_LEN_08BIT, 0x80}, + {0x7602, CRL_REG_LEN_08BIT, 0x07}, + {0x7603, CRL_REG_LEN_08BIT, 0x44}, + {0x7604, CRL_REG_LEN_08BIT, 0x05}, + {0x7605, CRL_REG_LEN_08BIT, 0x33}, + {0x7606, CRL_REG_LEN_08BIT, 0x0f}, + {0x7607, CRL_REG_LEN_08BIT, 0x00}, + {0x7608, CRL_REG_LEN_08BIT, 0x07}, + {0x7609, CRL_REG_LEN_08BIT, 0x40}, + {0x760a, CRL_REG_LEN_08BIT, 0x04}, + {0x760b, CRL_REG_LEN_08BIT, 0xe5}, + {0x760c, CRL_REG_LEN_08BIT, 0x06}, + {0x760d, CRL_REG_LEN_08BIT, 0x50}, + {0x760e, CRL_REG_LEN_08BIT, 0x04}, + {0x760f, CRL_REG_LEN_08BIT, 0xe4}, + {0x7610, CRL_REG_LEN_08BIT, 0x00}, + {0x7611, CRL_REG_LEN_08BIT, 0x00}, + {0x7612, CRL_REG_LEN_08BIT, 0x06}, + {0x7613, CRL_REG_LEN_08BIT, 0x5c}, + {0x7614, CRL_REG_LEN_08BIT, 0x00}, + {0x7615, CRL_REG_LEN_08BIT, 0x0f}, + {0x7616, CRL_REG_LEN_08BIT, 0x06}, + {0x7617, CRL_REG_LEN_08BIT, 0x1c}, + {0x7618, CRL_REG_LEN_08BIT, 0x00}, + {0x7619, CRL_REG_LEN_08BIT, 0x02}, + {0x761a, CRL_REG_LEN_08BIT, 0x06}, + {0x761b, CRL_REG_LEN_08BIT, 0xa2}, + {0x761c, CRL_REG_LEN_08BIT, 0x00}, + {0x761d, CRL_REG_LEN_08BIT, 0x01}, + {0x761e, CRL_REG_LEN_08BIT, 0x06}, + {0x761f, CRL_REG_LEN_08BIT, 0xae}, + {0x7620, CRL_REG_LEN_08BIT, 0x00}, + {0x7621, CRL_REG_LEN_08BIT, 0x0e}, + {0x7622, CRL_REG_LEN_08BIT, 0x05}, + {0x7623, CRL_REG_LEN_08BIT, 0x30}, + {0x7624, CRL_REG_LEN_08BIT, 0x07}, + {0x7625, CRL_REG_LEN_08BIT, 0x00}, + {0x7626, CRL_REG_LEN_08BIT, 0x0f}, + {0x7627, CRL_REG_LEN_08BIT, 0x00}, + {0x7628, CRL_REG_LEN_08BIT, 0x04}, + {0x7629, CRL_REG_LEN_08BIT, 0xe5}, + {0x762a, CRL_REG_LEN_08BIT, 0x05}, + {0x762b, CRL_REG_LEN_08BIT, 0x33}, + {0x762c, CRL_REG_LEN_08BIT, 0x06}, + {0x762d, CRL_REG_LEN_08BIT, 0x12}, + {0x762e, CRL_REG_LEN_08BIT, 0x00}, + {0x762f, CRL_REG_LEN_08BIT, 0x01}, + {0x7630, CRL_REG_LEN_08BIT, 0x06}, + {0x7631, CRL_REG_LEN_08BIT, 0x52}, + {0x7632, CRL_REG_LEN_08BIT, 0x00}, + {0x7633, CRL_REG_LEN_08BIT, 0x01}, + {0x7634, CRL_REG_LEN_08BIT, 0x06}, + {0x7635, CRL_REG_LEN_08BIT, 0x5e}, + {0x7636, CRL_REG_LEN_08BIT, 0x04}, + {0x7637, CRL_REG_LEN_08BIT, 0xe4}, + {0x7638, CRL_REG_LEN_08BIT, 0x00}, + {0x7639, CRL_REG_LEN_08BIT, 0x01}, + {0x763a, CRL_REG_LEN_08BIT, 0x05}, + {0x763b, CRL_REG_LEN_08BIT, 0x30}, + {0x763c, CRL_REG_LEN_08BIT, 0x0f}, + {0x763d, CRL_REG_LEN_08BIT, 0x00}, + {0x763e, CRL_REG_LEN_08BIT, 0x06}, + {0x763f, CRL_REG_LEN_08BIT, 0xa6}, + {0x7640, CRL_REG_LEN_08BIT, 0x00}, + {0x7641, CRL_REG_LEN_08BIT, 0x02}, + {0x7642, CRL_REG_LEN_08BIT, 0x06}, + {0x7643, CRL_REG_LEN_08BIT, 0x26}, + {0x7644, CRL_REG_LEN_08BIT, 0x00}, + {0x7645, CRL_REG_LEN_08BIT, 0x02}, + {0x7646, CRL_REG_LEN_08BIT, 0x05}, + {0x7647, CRL_REG_LEN_08BIT, 0x33}, + {0x7648, CRL_REG_LEN_08BIT, 0x06}, + {0x7649, CRL_REG_LEN_08BIT, 0x20}, + {0x764a, CRL_REG_LEN_08BIT, 0x0f}, + {0x764b, CRL_REG_LEN_08BIT, 0x00}, + {0x764c, CRL_REG_LEN_08BIT, 0x06}, + {0x764d, CRL_REG_LEN_08BIT, 0x56}, + {0x764e, CRL_REG_LEN_08BIT, 0x00}, + {0x764f, CRL_REG_LEN_08BIT, 0x02}, + {0x7650, CRL_REG_LEN_08BIT, 0x06}, + {0x7651, CRL_REG_LEN_08BIT, 0x16}, + {0x7652, CRL_REG_LEN_08BIT, 0x05}, + {0x7653, CRL_REG_LEN_08BIT, 0x33}, + {0x7654, CRL_REG_LEN_08BIT, 0x06}, + {0x7655, CRL_REG_LEN_08BIT, 0x10}, + {0x7656, CRL_REG_LEN_08BIT, 0x0f}, + {0x7657, CRL_REG_LEN_08BIT, 0x00}, + {0x7658, CRL_REG_LEN_08BIT, 0x06}, + {0x7659, CRL_REG_LEN_08BIT, 0x10}, + {0x765a, CRL_REG_LEN_08BIT, 0x0f}, + {0x765b, CRL_REG_LEN_08BIT, 0x00}, + {0x765c, CRL_REG_LEN_08BIT, 0x06}, + {0x765d, CRL_REG_LEN_08BIT, 0x20}, + {0x765e, CRL_REG_LEN_08BIT, 0x0f}, + {0x765f, CRL_REG_LEN_08BIT, 0x00}, + {0x7660, CRL_REG_LEN_08BIT, 0x00}, + {0x7661, CRL_REG_LEN_08BIT, 0x00}, + {0x7662, CRL_REG_LEN_08BIT, 0x00}, + {0x7663, CRL_REG_LEN_08BIT, 0x02}, + {0x7664, CRL_REG_LEN_08BIT, 0x04}, + {0x7665, CRL_REG_LEN_08BIT, 0xe5}, + {0x7666, CRL_REG_LEN_08BIT, 0x04}, + {0x7667, CRL_REG_LEN_08BIT, 0xe4}, + {0x7668, CRL_REG_LEN_08BIT, 0x0f}, + {0x7669, CRL_REG_LEN_08BIT, 0x00}, + {0x766a, CRL_REG_LEN_08BIT, 0x00}, + {0x766b, CRL_REG_LEN_08BIT, 0x00}, + {0x766c, CRL_REG_LEN_08BIT, 0x00}, + {0x766d, CRL_REG_LEN_08BIT, 0x01}, + {0x766e, CRL_REG_LEN_08BIT, 0x04}, + {0x766f, CRL_REG_LEN_08BIT, 0xe5}, + {0x7670, CRL_REG_LEN_08BIT, 0x04}, + {0x7671, CRL_REG_LEN_08BIT, 0xe4}, + {0x7672, CRL_REG_LEN_08BIT, 0x0f}, + {0x7673, CRL_REG_LEN_08BIT, 0x00}, + {0x7674, CRL_REG_LEN_08BIT, 0x00}, + {0x7675, CRL_REG_LEN_08BIT, 0x02}, + {0x7676, CRL_REG_LEN_08BIT, 0x04}, + {0x7677, CRL_REG_LEN_08BIT, 0xe4}, + {0x7678, CRL_REG_LEN_08BIT, 0x00}, + {0x7679, CRL_REG_LEN_08BIT, 0x02}, + {0x767a, CRL_REG_LEN_08BIT, 0x04}, + {0x767b, CRL_REG_LEN_08BIT, 0xc4}, + {0x767c, CRL_REG_LEN_08BIT, 0x00}, + {0x767d, CRL_REG_LEN_08BIT, 0x02}, + {0x767e, CRL_REG_LEN_08BIT, 0x04}, + {0x767f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7680, CRL_REG_LEN_08BIT, 0x05}, + {0x7681, CRL_REG_LEN_08BIT, 0x83}, + {0x7682, CRL_REG_LEN_08BIT, 0x0f}, + {0x7683, CRL_REG_LEN_08BIT, 0x00}, + {0x7684, CRL_REG_LEN_08BIT, 0x00}, + {0x7685, CRL_REG_LEN_08BIT, 0x02}, + {0x7686, CRL_REG_LEN_08BIT, 0x04}, + {0x7687, CRL_REG_LEN_08BIT, 0xe4}, + {0x7688, CRL_REG_LEN_08BIT, 0x00}, + {0x7689, CRL_REG_LEN_08BIT, 0x02}, + {0x768a, CRL_REG_LEN_08BIT, 0x04}, + {0x768b, CRL_REG_LEN_08BIT, 0xc4}, + {0x768c, CRL_REG_LEN_08BIT, 0x00}, + {0x768d, CRL_REG_LEN_08BIT, 0x02}, + {0x768e, CRL_REG_LEN_08BIT, 0x04}, + {0x768f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7690, CRL_REG_LEN_08BIT, 0x05}, + {0x7691, CRL_REG_LEN_08BIT, 0x83}, + {0x7692, CRL_REG_LEN_08BIT, 0x03}, + {0x7693, CRL_REG_LEN_08BIT, 0x0b}, + {0x7694, CRL_REG_LEN_08BIT, 0x05}, + {0x7695, CRL_REG_LEN_08BIT, 0x83}, + {0x7696, CRL_REG_LEN_08BIT, 0x00}, + {0x7697, CRL_REG_LEN_08BIT, 0x07}, + {0x7698, CRL_REG_LEN_08BIT, 0x05}, + {0x7699, CRL_REG_LEN_08BIT, 0x03}, + {0x769a, CRL_REG_LEN_08BIT, 0x00}, + {0x769b, CRL_REG_LEN_08BIT, 0x05}, + {0x769c, CRL_REG_LEN_08BIT, 0x05}, + {0x769d, CRL_REG_LEN_08BIT, 0x32}, + {0x769e, CRL_REG_LEN_08BIT, 0x05}, + {0x769f, CRL_REG_LEN_08BIT, 0x30}, + {0x76a0, CRL_REG_LEN_08BIT, 0x00}, + {0x76a1, CRL_REG_LEN_08BIT, 0x02}, + {0x76a2, CRL_REG_LEN_08BIT, 0x05}, + {0x76a3, CRL_REG_LEN_08BIT, 0x78}, + {0x76a4, CRL_REG_LEN_08BIT, 0x00}, + {0x76a5, CRL_REG_LEN_08BIT, 0x01}, + {0x76a6, CRL_REG_LEN_08BIT, 0x05}, + {0x76a7, CRL_REG_LEN_08BIT, 0x7c}, + {0x76a8, CRL_REG_LEN_08BIT, 0x03}, + {0x76a9, CRL_REG_LEN_08BIT, 0x9a}, + {0x76aa, CRL_REG_LEN_08BIT, 0x05}, + {0x76ab, CRL_REG_LEN_08BIT, 0x83}, + {0x76ac, CRL_REG_LEN_08BIT, 0x00}, + {0x76ad, CRL_REG_LEN_08BIT, 0x04}, + {0x76ae, CRL_REG_LEN_08BIT, 0x05}, + {0x76af, CRL_REG_LEN_08BIT, 0x03}, + {0x76b0, CRL_REG_LEN_08BIT, 0x00}, + {0x76b1, CRL_REG_LEN_08BIT, 0x03}, + {0x76b2, CRL_REG_LEN_08BIT, 0x05}, + {0x76b3, CRL_REG_LEN_08BIT, 0x32}, + {0x76b4, CRL_REG_LEN_08BIT, 0x05}, + {0x76b5, CRL_REG_LEN_08BIT, 0x30}, + {0x76b6, CRL_REG_LEN_08BIT, 0x00}, + {0x76b7, CRL_REG_LEN_08BIT, 0x02}, + {0x76b8, CRL_REG_LEN_08BIT, 0x05}, + {0x76b9, CRL_REG_LEN_08BIT, 0x78}, + {0x76ba, CRL_REG_LEN_08BIT, 0x00}, + {0x76bb, CRL_REG_LEN_08BIT, 0x01}, + {0x76bc, CRL_REG_LEN_08BIT, 0x05}, + {0x76bd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76be, CRL_REG_LEN_08BIT, 0x03}, + {0x76bf, CRL_REG_LEN_08BIT, 0x99}, + {0x76c0, CRL_REG_LEN_08BIT, 0x05}, + {0x76c1, CRL_REG_LEN_08BIT, 0x83}, + {0x76c2, CRL_REG_LEN_08BIT, 0x00}, + {0x76c3, CRL_REG_LEN_08BIT, 0x03}, + {0x76c4, CRL_REG_LEN_08BIT, 0x05}, + {0x76c5, CRL_REG_LEN_08BIT, 0x03}, + {0x76c6, CRL_REG_LEN_08BIT, 0x00}, + {0x76c7, CRL_REG_LEN_08BIT, 0x01}, + {0x76c8, CRL_REG_LEN_08BIT, 0x05}, + {0x76c9, CRL_REG_LEN_08BIT, 0x32}, + {0x76ca, CRL_REG_LEN_08BIT, 0x05}, + {0x76cb, CRL_REG_LEN_08BIT, 0x30}, + {0x76cc, CRL_REG_LEN_08BIT, 0x00}, + {0x76cd, CRL_REG_LEN_08BIT, 0x02}, + {0x76ce, CRL_REG_LEN_08BIT, 0x05}, + {0x76cf, CRL_REG_LEN_08BIT, 0x78}, + {0x76d0, CRL_REG_LEN_08BIT, 0x00}, + {0x76d1, CRL_REG_LEN_08BIT, 0x01}, + {0x76d2, CRL_REG_LEN_08BIT, 0x05}, + {0x76d3, CRL_REG_LEN_08BIT, 0x7c}, + {0x76d4, CRL_REG_LEN_08BIT, 0x03}, + {0x76d5, CRL_REG_LEN_08BIT, 0x98}, + {0x76d6, CRL_REG_LEN_08BIT, 0x05}, + {0x76d7, CRL_REG_LEN_08BIT, 0x83}, + {0x76d8, CRL_REG_LEN_08BIT, 0x00}, + {0x76d9, CRL_REG_LEN_08BIT, 0x00}, + {0x76da, CRL_REG_LEN_08BIT, 0x05}, + {0x76db, CRL_REG_LEN_08BIT, 0x03}, + {0x76dc, CRL_REG_LEN_08BIT, 0x00}, + {0x76dd, CRL_REG_LEN_08BIT, 0x01}, + {0x76de, CRL_REG_LEN_08BIT, 0x05}, + {0x76df, CRL_REG_LEN_08BIT, 0x32}, + {0x76e0, CRL_REG_LEN_08BIT, 0x05}, + {0x76e1, CRL_REG_LEN_08BIT, 0x30}, + {0x76e2, CRL_REG_LEN_08BIT, 0x00}, + {0x76e3, CRL_REG_LEN_08BIT, 0x02}, + {0x76e4, CRL_REG_LEN_08BIT, 0x05}, + {0x76e5, CRL_REG_LEN_08BIT, 0x78}, + {0x76e6, CRL_REG_LEN_08BIT, 0x00}, + {0x76e7, CRL_REG_LEN_08BIT, 0x01}, + {0x76e8, CRL_REG_LEN_08BIT, 0x05}, + {0x76e9, CRL_REG_LEN_08BIT, 0x7c}, + {0x76ea, CRL_REG_LEN_08BIT, 0x03}, + {0x76eb, CRL_REG_LEN_08BIT, 0x97}, + {0x76ec, CRL_REG_LEN_08BIT, 0x05}, + {0x76ed, CRL_REG_LEN_08BIT, 0x83}, + {0x76ee, CRL_REG_LEN_08BIT, 0x00}, + {0x76ef, CRL_REG_LEN_08BIT, 0x00}, + {0x76f0, CRL_REG_LEN_08BIT, 0x05}, + {0x76f1, CRL_REG_LEN_08BIT, 0x03}, + {0x76f2, CRL_REG_LEN_08BIT, 0x05}, + {0x76f3, CRL_REG_LEN_08BIT, 0x32}, + {0x76f4, CRL_REG_LEN_08BIT, 0x05}, + {0x76f5, CRL_REG_LEN_08BIT, 0x30}, + {0x76f6, CRL_REG_LEN_08BIT, 0x00}, + {0x76f7, CRL_REG_LEN_08BIT, 0x02}, + {0x76f8, CRL_REG_LEN_08BIT, 0x05}, + {0x76f9, CRL_REG_LEN_08BIT, 0x78}, + {0x76fa, CRL_REG_LEN_08BIT, 0x00}, + {0x76fb, CRL_REG_LEN_08BIT, 0x01}, + {0x76fc, CRL_REG_LEN_08BIT, 0x05}, + {0x76fd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76fe, CRL_REG_LEN_08BIT, 0x03}, + {0x76ff, CRL_REG_LEN_08BIT, 0x96}, + {0x7700, CRL_REG_LEN_08BIT, 0x05}, + {0x7701, CRL_REG_LEN_08BIT, 0x83}, + {0x7702, CRL_REG_LEN_08BIT, 0x05}, + {0x7703, CRL_REG_LEN_08BIT, 0x03}, + {0x7704, CRL_REG_LEN_08BIT, 0x05}, + {0x7705, CRL_REG_LEN_08BIT, 0x32}, + {0x7706, CRL_REG_LEN_08BIT, 0x05}, + {0x7707, CRL_REG_LEN_08BIT, 0x30}, + {0x7708, CRL_REG_LEN_08BIT, 0x00}, + {0x7709, CRL_REG_LEN_08BIT, 0x02}, + {0x770a, CRL_REG_LEN_08BIT, 0x05}, + {0x770b, CRL_REG_LEN_08BIT, 0x78}, + {0x770c, CRL_REG_LEN_08BIT, 0x00}, + {0x770d, CRL_REG_LEN_08BIT, 0x01}, + {0x770e, CRL_REG_LEN_08BIT, 0x05}, + {0x770f, CRL_REG_LEN_08BIT, 0x7c}, + {0x7710, CRL_REG_LEN_08BIT, 0x03}, + {0x7711, CRL_REG_LEN_08BIT, 0x95}, + {0x7712, CRL_REG_LEN_08BIT, 0x05}, + {0x7713, CRL_REG_LEN_08BIT, 0x83}, + {0x7714, CRL_REG_LEN_08BIT, 0x05}, + {0x7715, CRL_REG_LEN_08BIT, 0x03}, + {0x7716, CRL_REG_LEN_08BIT, 0x05}, + {0x7717, CRL_REG_LEN_08BIT, 0x32}, + {0x7718, CRL_REG_LEN_08BIT, 0x05}, + {0x7719, CRL_REG_LEN_08BIT, 0x30}, + {0x771a, CRL_REG_LEN_08BIT, 0x00}, + {0x771b, CRL_REG_LEN_08BIT, 0x02}, + {0x771c, CRL_REG_LEN_08BIT, 0x05}, + {0x771d, CRL_REG_LEN_08BIT, 0x78}, + {0x771e, CRL_REG_LEN_08BIT, 0x00}, + {0x771f, CRL_REG_LEN_08BIT, 0x01}, + {0x7720, CRL_REG_LEN_08BIT, 0x05}, + {0x7721, CRL_REG_LEN_08BIT, 0x7c}, + {0x7722, CRL_REG_LEN_08BIT, 0x03}, + {0x7723, CRL_REG_LEN_08BIT, 0x94}, + {0x7724, CRL_REG_LEN_08BIT, 0x05}, + {0x7725, CRL_REG_LEN_08BIT, 0x83}, + {0x7726, CRL_REG_LEN_08BIT, 0x00}, + {0x7727, CRL_REG_LEN_08BIT, 0x01}, + {0x7728, CRL_REG_LEN_08BIT, 0x05}, + {0x7729, CRL_REG_LEN_08BIT, 0x03}, + {0x772a, CRL_REG_LEN_08BIT, 0x00}, + {0x772b, CRL_REG_LEN_08BIT, 0x01}, + {0x772c, CRL_REG_LEN_08BIT, 0x05}, + {0x772d, CRL_REG_LEN_08BIT, 0x32}, + {0x772e, CRL_REG_LEN_08BIT, 0x05}, + {0x772f, CRL_REG_LEN_08BIT, 0x30}, + {0x7730, CRL_REG_LEN_08BIT, 0x00}, + {0x7731, CRL_REG_LEN_08BIT, 0x02}, + {0x7732, CRL_REG_LEN_08BIT, 0x05}, + {0x7733, CRL_REG_LEN_08BIT, 0x78}, + {0x7734, CRL_REG_LEN_08BIT, 0x00}, + {0x7735, CRL_REG_LEN_08BIT, 0x01}, + {0x7736, CRL_REG_LEN_08BIT, 0x05}, + {0x7737, CRL_REG_LEN_08BIT, 0x7c}, + {0x7738, CRL_REG_LEN_08BIT, 0x03}, + {0x7739, CRL_REG_LEN_08BIT, 0x93}, + {0x773a, CRL_REG_LEN_08BIT, 0x05}, + {0x773b, CRL_REG_LEN_08BIT, 0x83}, + {0x773c, CRL_REG_LEN_08BIT, 0x00}, + {0x773d, CRL_REG_LEN_08BIT, 0x00}, + {0x773e, CRL_REG_LEN_08BIT, 0x05}, + {0x773f, CRL_REG_LEN_08BIT, 0x03}, + {0x7740, CRL_REG_LEN_08BIT, 0x00}, + {0x7741, CRL_REG_LEN_08BIT, 0x00}, + {0x7742, CRL_REG_LEN_08BIT, 0x05}, + {0x7743, CRL_REG_LEN_08BIT, 0x32}, + {0x7744, CRL_REG_LEN_08BIT, 0x05}, + {0x7745, CRL_REG_LEN_08BIT, 0x30}, + {0x7746, CRL_REG_LEN_08BIT, 0x00}, + {0x7747, CRL_REG_LEN_08BIT, 0x02}, + {0x7748, CRL_REG_LEN_08BIT, 0x05}, + {0x7749, CRL_REG_LEN_08BIT, 0x78}, + {0x774a, CRL_REG_LEN_08BIT, 0x00}, + {0x774b, CRL_REG_LEN_08BIT, 0x01}, + {0x774c, CRL_REG_LEN_08BIT, 0x05}, + {0x774d, CRL_REG_LEN_08BIT, 0x7c}, + {0x774e, CRL_REG_LEN_08BIT, 0x03}, + {0x774f, CRL_REG_LEN_08BIT, 0x92}, + {0x7750, CRL_REG_LEN_08BIT, 0x05}, + {0x7751, CRL_REG_LEN_08BIT, 0x83}, + {0x7752, CRL_REG_LEN_08BIT, 0x05}, + {0x7753, CRL_REG_LEN_08BIT, 0x03}, + {0x7754, CRL_REG_LEN_08BIT, 0x00}, + {0x7755, CRL_REG_LEN_08BIT, 0x00}, + {0x7756, CRL_REG_LEN_08BIT, 0x05}, + {0x7757, CRL_REG_LEN_08BIT, 0x32}, + {0x7758, CRL_REG_LEN_08BIT, 0x05}, + {0x7759, CRL_REG_LEN_08BIT, 0x30}, + {0x775a, CRL_REG_LEN_08BIT, 0x00}, + {0x775b, CRL_REG_LEN_08BIT, 0x02}, + {0x775c, CRL_REG_LEN_08BIT, 0x05}, + {0x775d, CRL_REG_LEN_08BIT, 0x78}, + {0x775e, CRL_REG_LEN_08BIT, 0x00}, + {0x775f, CRL_REG_LEN_08BIT, 0x01}, + {0x7760, CRL_REG_LEN_08BIT, 0x05}, + {0x7761, CRL_REG_LEN_08BIT, 0x7c}, + {0x7762, CRL_REG_LEN_08BIT, 0x03}, + {0x7763, CRL_REG_LEN_08BIT, 0x91}, + {0x7764, CRL_REG_LEN_08BIT, 0x05}, + {0x7765, CRL_REG_LEN_08BIT, 0x83}, + {0x7766, CRL_REG_LEN_08BIT, 0x05}, + {0x7767, CRL_REG_LEN_08BIT, 0x03}, + {0x7768, CRL_REG_LEN_08BIT, 0x05}, + {0x7769, CRL_REG_LEN_08BIT, 0x32}, + {0x776a, CRL_REG_LEN_08BIT, 0x05}, + {0x776b, CRL_REG_LEN_08BIT, 0x30}, + {0x776c, CRL_REG_LEN_08BIT, 0x00}, + {0x776d, CRL_REG_LEN_08BIT, 0x02}, + {0x776e, CRL_REG_LEN_08BIT, 0x05}, + {0x776f, CRL_REG_LEN_08BIT, 0x78}, + {0x7770, CRL_REG_LEN_08BIT, 0x00}, + {0x7771, CRL_REG_LEN_08BIT, 0x01}, + {0x7772, CRL_REG_LEN_08BIT, 0x05}, + {0x7773, CRL_REG_LEN_08BIT, 0x7c}, + {0x7774, CRL_REG_LEN_08BIT, 0x03}, + {0x7775, CRL_REG_LEN_08BIT, 0x90}, + {0x7776, CRL_REG_LEN_08BIT, 0x05}, + {0x7777, CRL_REG_LEN_08BIT, 0x83}, + {0x7778, CRL_REG_LEN_08BIT, 0x05}, + {0x7779, CRL_REG_LEN_08BIT, 0x03}, + {0x777a, CRL_REG_LEN_08BIT, 0x05}, + {0x777b, CRL_REG_LEN_08BIT, 0x32}, + {0x777c, CRL_REG_LEN_08BIT, 0x05}, + {0x777d, CRL_REG_LEN_08BIT, 0x30}, + {0x777e, CRL_REG_LEN_08BIT, 0x00}, + {0x777f, CRL_REG_LEN_08BIT, 0x02}, + {0x7780, CRL_REG_LEN_08BIT, 0x05}, + {0x7781, CRL_REG_LEN_08BIT, 0x78}, + {0x7782, CRL_REG_LEN_08BIT, 0x00}, + {0x7783, CRL_REG_LEN_08BIT, 0x01}, + {0x7784, CRL_REG_LEN_08BIT, 0x05}, + {0x7785, CRL_REG_LEN_08BIT, 0x7c}, + {0x7786, CRL_REG_LEN_08BIT, 0x02}, + {0x7787, CRL_REG_LEN_08BIT, 0x90}, + {0x7788, CRL_REG_LEN_08BIT, 0x05}, + {0x7789, CRL_REG_LEN_08BIT, 0x03}, + {0x778a, CRL_REG_LEN_08BIT, 0x07}, + {0x778b, CRL_REG_LEN_08BIT, 0x00}, + {0x778c, CRL_REG_LEN_08BIT, 0x0f}, + {0x778d, CRL_REG_LEN_08BIT, 0x00}, + {0x778e, CRL_REG_LEN_08BIT, 0x08}, + {0x778f, CRL_REG_LEN_08BIT, 0x30}, + {0x7790, CRL_REG_LEN_08BIT, 0x08}, + {0x7791, CRL_REG_LEN_08BIT, 0xee}, + {0x7792, CRL_REG_LEN_08BIT, 0x0f}, + {0x7793, CRL_REG_LEN_08BIT, 0x00}, + {0x7794, CRL_REG_LEN_08BIT, 0x05}, + {0x7795, CRL_REG_LEN_08BIT, 0x33}, + {0x7796, CRL_REG_LEN_08BIT, 0x04}, + {0x7797, CRL_REG_LEN_08BIT, 0xe5}, + {0x7798, CRL_REG_LEN_08BIT, 0x06}, + {0x7799, CRL_REG_LEN_08BIT, 0x52}, + {0x779a, CRL_REG_LEN_08BIT, 0x04}, + {0x779b, CRL_REG_LEN_08BIT, 0xe4}, + {0x779c, CRL_REG_LEN_08BIT, 0x00}, + {0x779d, CRL_REG_LEN_08BIT, 0x00}, + {0x779e, CRL_REG_LEN_08BIT, 0x06}, + {0x779f, CRL_REG_LEN_08BIT, 0x5e}, + {0x77a0, CRL_REG_LEN_08BIT, 0x00}, + {0x77a1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77a2, CRL_REG_LEN_08BIT, 0x06}, + {0x77a3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77a4, CRL_REG_LEN_08BIT, 0x00}, + {0x77a5, CRL_REG_LEN_08BIT, 0x02}, + {0x77a6, CRL_REG_LEN_08BIT, 0x06}, + {0x77a7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77a8, CRL_REG_LEN_08BIT, 0x00}, + {0x77a9, CRL_REG_LEN_08BIT, 0x01}, + {0x77aa, CRL_REG_LEN_08BIT, 0x06}, + {0x77ab, CRL_REG_LEN_08BIT, 0xae}, + {0x77ac, CRL_REG_LEN_08BIT, 0x00}, + {0x77ad, CRL_REG_LEN_08BIT, 0x03}, + {0x77ae, CRL_REG_LEN_08BIT, 0x05}, + {0x77af, CRL_REG_LEN_08BIT, 0x30}, + {0x77b0, CRL_REG_LEN_08BIT, 0x09}, + {0x77b1, CRL_REG_LEN_08BIT, 0x19}, + {0x77b2, CRL_REG_LEN_08BIT, 0x0f}, + {0x77b3, CRL_REG_LEN_08BIT, 0x00}, + {0x77b4, CRL_REG_LEN_08BIT, 0x05}, + {0x77b5, CRL_REG_LEN_08BIT, 0x33}, + {0x77b6, CRL_REG_LEN_08BIT, 0x04}, + {0x77b7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77b8, CRL_REG_LEN_08BIT, 0x06}, + {0x77b9, CRL_REG_LEN_08BIT, 0x52}, + {0x77ba, CRL_REG_LEN_08BIT, 0x04}, + {0x77bb, CRL_REG_LEN_08BIT, 0xe4}, + {0x77bc, CRL_REG_LEN_08BIT, 0x00}, + {0x77bd, CRL_REG_LEN_08BIT, 0x00}, + {0x77be, CRL_REG_LEN_08BIT, 0x06}, + {0x77bf, CRL_REG_LEN_08BIT, 0x5e}, + {0x77c0, CRL_REG_LEN_08BIT, 0x00}, + {0x77c1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77c2, CRL_REG_LEN_08BIT, 0x06}, + {0x77c3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77c4, CRL_REG_LEN_08BIT, 0x00}, + {0x77c5, CRL_REG_LEN_08BIT, 0x02}, + {0x77c6, CRL_REG_LEN_08BIT, 0x06}, + {0x77c7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77c8, CRL_REG_LEN_08BIT, 0x00}, + {0x77c9, CRL_REG_LEN_08BIT, 0x01}, + {0x77ca, CRL_REG_LEN_08BIT, 0x06}, + {0x77cb, CRL_REG_LEN_08BIT, 0xae}, + {0x77cc, CRL_REG_LEN_08BIT, 0x00}, + {0x77cd, CRL_REG_LEN_08BIT, 0x03}, + {0x77ce, CRL_REG_LEN_08BIT, 0x05}, + {0x77cf, CRL_REG_LEN_08BIT, 0x30}, + {0x77d0, CRL_REG_LEN_08BIT, 0x0f}, + {0x77d1, CRL_REG_LEN_08BIT, 0x00}, + {0x77d2, CRL_REG_LEN_08BIT, 0x00}, + {0x77d3, CRL_REG_LEN_08BIT, 0x00}, + {0x77d4, CRL_REG_LEN_08BIT, 0x00}, + {0x77d5, CRL_REG_LEN_08BIT, 0x02}, + {0x77d6, CRL_REG_LEN_08BIT, 0x04}, + {0x77d7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77d8, CRL_REG_LEN_08BIT, 0x04}, + {0x77d9, CRL_REG_LEN_08BIT, 0xe4}, + {0x77da, CRL_REG_LEN_08BIT, 0x05}, + {0x77db, CRL_REG_LEN_08BIT, 0x33}, + {0x77dc, CRL_REG_LEN_08BIT, 0x07}, + {0x77dd, CRL_REG_LEN_08BIT, 0x10}, + {0x77de, CRL_REG_LEN_08BIT, 0x00}, + {0x77df, CRL_REG_LEN_08BIT, 0x00}, + {0x77e0, CRL_REG_LEN_08BIT, 0x01}, + {0x77e1, CRL_REG_LEN_08BIT, 0xbb}, + {0x77e2, CRL_REG_LEN_08BIT, 0x00}, + {0x77e3, CRL_REG_LEN_08BIT, 0x00}, + {0x77e4, CRL_REG_LEN_08BIT, 0x01}, + {0x77e5, CRL_REG_LEN_08BIT, 0xaa}, + {0x77e6, CRL_REG_LEN_08BIT, 0x00}, + {0x77e7, CRL_REG_LEN_08BIT, 0x00}, + {0x77e8, CRL_REG_LEN_08BIT, 0x01}, + {0x77e9, CRL_REG_LEN_08BIT, 0x99}, + {0x77ea, CRL_REG_LEN_08BIT, 0x00}, + {0x77eb, CRL_REG_LEN_08BIT, 0x00}, + {0x77ec, CRL_REG_LEN_08BIT, 0x01}, + {0x77ed, CRL_REG_LEN_08BIT, 0x88}, + {0x77ee, CRL_REG_LEN_08BIT, 0x00}, + {0x77ef, CRL_REG_LEN_08BIT, 0x00}, + {0x77f0, CRL_REG_LEN_08BIT, 0x01}, + {0x77f1, CRL_REG_LEN_08BIT, 0x77}, + {0x77f2, CRL_REG_LEN_08BIT, 0x00}, + {0x77f3, CRL_REG_LEN_08BIT, 0x00}, + {0x77f4, CRL_REG_LEN_08BIT, 0x01}, + {0x77f5, CRL_REG_LEN_08BIT, 0x66}, + {0x77f6, CRL_REG_LEN_08BIT, 0x00}, + {0x77f7, CRL_REG_LEN_08BIT, 0x00}, + {0x77f8, CRL_REG_LEN_08BIT, 0x01}, + {0x77f9, CRL_REG_LEN_08BIT, 0x55}, + {0x77fa, CRL_REG_LEN_08BIT, 0x00}, + {0x77fb, CRL_REG_LEN_08BIT, 0x00}, + {0x77fc, CRL_REG_LEN_08BIT, 0x01}, + {0x77fd, CRL_REG_LEN_08BIT, 0x44}, + {0x77fe, CRL_REG_LEN_08BIT, 0x00}, + {0x77ff, CRL_REG_LEN_08BIT, 0x00}, + {0x7800, CRL_REG_LEN_08BIT, 0x01}, + {0x7801, CRL_REG_LEN_08BIT, 0x33}, + {0x7802, CRL_REG_LEN_08BIT, 0x00}, + {0x7803, CRL_REG_LEN_08BIT, 0x00}, + {0x7804, CRL_REG_LEN_08BIT, 0x01}, + {0x7805, CRL_REG_LEN_08BIT, 0x22}, + {0x7806, CRL_REG_LEN_08BIT, 0x00}, + {0x7807, CRL_REG_LEN_08BIT, 0x00}, + {0x7808, CRL_REG_LEN_08BIT, 0x01}, + {0x7809, CRL_REG_LEN_08BIT, 0x11}, + {0x780a, CRL_REG_LEN_08BIT, 0x00}, + {0x780b, CRL_REG_LEN_08BIT, 0x00}, + {0x780c, CRL_REG_LEN_08BIT, 0x01}, + {0x780d, CRL_REG_LEN_08BIT, 0x00}, + {0x780e, CRL_REG_LEN_08BIT, 0x01}, + {0x780f, CRL_REG_LEN_08BIT, 0xff}, + {0x7810, CRL_REG_LEN_08BIT, 0x07}, + {0x7811, CRL_REG_LEN_08BIT, 0x00}, + {0x7812, CRL_REG_LEN_08BIT, 0x02}, + {0x7813, CRL_REG_LEN_08BIT, 0xa0}, + {0x7814, CRL_REG_LEN_08BIT, 0x0f}, + {0x7815, CRL_REG_LEN_08BIT, 0x00}, + {0x7816, CRL_REG_LEN_08BIT, 0x08}, + {0x7817, CRL_REG_LEN_08BIT, 0x35}, + {0x7818, CRL_REG_LEN_08BIT, 0x06}, + {0x7819, CRL_REG_LEN_08BIT, 0x52}, + {0x781a, CRL_REG_LEN_08BIT, 0x04}, + {0x781b, CRL_REG_LEN_08BIT, 0xe4}, + {0x781c, CRL_REG_LEN_08BIT, 0x00}, + {0x781d, CRL_REG_LEN_08BIT, 0x00}, + {0x781e, CRL_REG_LEN_08BIT, 0x06}, + {0x781f, CRL_REG_LEN_08BIT, 0x5e}, + {0x7820, CRL_REG_LEN_08BIT, 0x05}, + {0x7821, CRL_REG_LEN_08BIT, 0x33}, + {0x7822, CRL_REG_LEN_08BIT, 0x09}, + {0x7823, CRL_REG_LEN_08BIT, 0x19}, + {0x7824, CRL_REG_LEN_08BIT, 0x06}, + {0x7825, CRL_REG_LEN_08BIT, 0x1e}, + {0x7826, CRL_REG_LEN_08BIT, 0x05}, + {0x7827, CRL_REG_LEN_08BIT, 0x33}, + {0x7828, CRL_REG_LEN_08BIT, 0x00}, + {0x7829, CRL_REG_LEN_08BIT, 0x01}, + {0x782a, CRL_REG_LEN_08BIT, 0x06}, + {0x782b, CRL_REG_LEN_08BIT, 0x24}, + {0x782c, CRL_REG_LEN_08BIT, 0x06}, + {0x782d, CRL_REG_LEN_08BIT, 0x20}, + {0x782e, CRL_REG_LEN_08BIT, 0x0f}, + {0x782f, CRL_REG_LEN_08BIT, 0x00}, + {0x7830, CRL_REG_LEN_08BIT, 0x08}, + {0x7831, CRL_REG_LEN_08BIT, 0x35}, + {0x7832, CRL_REG_LEN_08BIT, 0x07}, + {0x7833, CRL_REG_LEN_08BIT, 0x10}, + {0x7834, CRL_REG_LEN_08BIT, 0x00}, + {0x7835, CRL_REG_LEN_08BIT, 0x00}, + {0x7836, CRL_REG_LEN_08BIT, 0x01}, + {0x7837, CRL_REG_LEN_08BIT, 0xbb}, + {0x7838, CRL_REG_LEN_08BIT, 0x00}, + {0x7839, CRL_REG_LEN_08BIT, 0x00}, + {0x783a, CRL_REG_LEN_08BIT, 0x01}, + {0x783b, CRL_REG_LEN_08BIT, 0xaa}, + {0x783c, CRL_REG_LEN_08BIT, 0x00}, + {0x783d, CRL_REG_LEN_08BIT, 0x00}, + {0x783e, CRL_REG_LEN_08BIT, 0x01}, + {0x783f, CRL_REG_LEN_08BIT, 0x99}, + {0x7840, CRL_REG_LEN_08BIT, 0x00}, + {0x7841, CRL_REG_LEN_08BIT, 0x00}, + {0x7842, CRL_REG_LEN_08BIT, 0x01}, + {0x7843, CRL_REG_LEN_08BIT, 0x88}, + {0x7844, CRL_REG_LEN_08BIT, 0x00}, + {0x7845, CRL_REG_LEN_08BIT, 0x00}, + {0x7846, CRL_REG_LEN_08BIT, 0x01}, + {0x7847, CRL_REG_LEN_08BIT, 0x77}, + {0x7848, CRL_REG_LEN_08BIT, 0x00}, + {0x7849, CRL_REG_LEN_08BIT, 0x00}, + {0x784a, CRL_REG_LEN_08BIT, 0x01}, + {0x784b, CRL_REG_LEN_08BIT, 0x66}, + {0x784c, CRL_REG_LEN_08BIT, 0x00}, + {0x784d, CRL_REG_LEN_08BIT, 0x00}, + {0x784e, CRL_REG_LEN_08BIT, 0x01}, + {0x784f, CRL_REG_LEN_08BIT, 0x55}, + {0x7850, CRL_REG_LEN_08BIT, 0x00}, + {0x7851, CRL_REG_LEN_08BIT, 0x00}, + {0x7852, CRL_REG_LEN_08BIT, 0x01}, + {0x7853, CRL_REG_LEN_08BIT, 0x44}, + {0x7854, CRL_REG_LEN_08BIT, 0x00}, + {0x7855, CRL_REG_LEN_08BIT, 0x00}, + {0x7856, CRL_REG_LEN_08BIT, 0x01}, + {0x7857, CRL_REG_LEN_08BIT, 0x33}, + {0x7858, CRL_REG_LEN_08BIT, 0x00}, + {0x7859, CRL_REG_LEN_08BIT, 0x00}, + {0x785a, CRL_REG_LEN_08BIT, 0x01}, + {0x785b, CRL_REG_LEN_08BIT, 0x22}, + {0x785c, CRL_REG_LEN_08BIT, 0x00}, + {0x785d, CRL_REG_LEN_08BIT, 0x00}, + {0x785e, CRL_REG_LEN_08BIT, 0x01}, + {0x785f, CRL_REG_LEN_08BIT, 0x11}, + {0x7860, CRL_REG_LEN_08BIT, 0x00}, + {0x7861, CRL_REG_LEN_08BIT, 0x00}, + {0x7862, CRL_REG_LEN_08BIT, 0x01}, + {0x7863, CRL_REG_LEN_08BIT, 0x00}, + {0x7864, CRL_REG_LEN_08BIT, 0x07}, + {0x7865, CRL_REG_LEN_08BIT, 0x00}, + {0x7866, CRL_REG_LEN_08BIT, 0x01}, + {0x7867, CRL_REG_LEN_08BIT, 0xff}, + {0x7868, CRL_REG_LEN_08BIT, 0x02}, + {0x7869, CRL_REG_LEN_08BIT, 0xa0}, + {0x786a, CRL_REG_LEN_08BIT, 0x0f}, + {0x786b, CRL_REG_LEN_08BIT, 0x00}, + {0x786c, CRL_REG_LEN_08BIT, 0x08}, + {0x786d, CRL_REG_LEN_08BIT, 0x3a}, + {0x786e, CRL_REG_LEN_08BIT, 0x08}, + {0x786f, CRL_REG_LEN_08BIT, 0x6a}, + {0x7870, CRL_REG_LEN_08BIT, 0x0f}, + {0x7871, CRL_REG_LEN_08BIT, 0x00}, + {0x7872, CRL_REG_LEN_08BIT, 0x04}, + {0x7873, CRL_REG_LEN_08BIT, 0xc0}, + {0x7874, CRL_REG_LEN_08BIT, 0x09}, + {0x7875, CRL_REG_LEN_08BIT, 0x19}, + {0x7876, CRL_REG_LEN_08BIT, 0x04}, + {0x7877, CRL_REG_LEN_08BIT, 0x99}, + {0x7878, CRL_REG_LEN_08BIT, 0x07}, + {0x7879, CRL_REG_LEN_08BIT, 0x14}, + {0x787a, CRL_REG_LEN_08BIT, 0x00}, + {0x787b, CRL_REG_LEN_08BIT, 0x01}, + {0x787c, CRL_REG_LEN_08BIT, 0x04}, + {0x787d, CRL_REG_LEN_08BIT, 0xa4}, + {0x787e, CRL_REG_LEN_08BIT, 0x00}, + {0x787f, CRL_REG_LEN_08BIT, 0x07}, + {0x7880, CRL_REG_LEN_08BIT, 0x04}, + {0x7881, CRL_REG_LEN_08BIT, 0xa6}, + {0x7882, CRL_REG_LEN_08BIT, 0x00}, + {0x7883, CRL_REG_LEN_08BIT, 0x00}, + {0x7884, CRL_REG_LEN_08BIT, 0x04}, + {0x7885, CRL_REG_LEN_08BIT, 0xa0}, + {0x7886, CRL_REG_LEN_08BIT, 0x04}, + {0x7887, CRL_REG_LEN_08BIT, 0x80}, + {0x7888, CRL_REG_LEN_08BIT, 0x04}, + {0x7889, CRL_REG_LEN_08BIT, 0x00}, + {0x788a, CRL_REG_LEN_08BIT, 0x05}, + {0x788b, CRL_REG_LEN_08BIT, 0x03}, + {0x788c, CRL_REG_LEN_08BIT, 0x06}, + {0x788d, CRL_REG_LEN_08BIT, 0x00}, + {0x788e, CRL_REG_LEN_08BIT, 0x0f}, + {0x788f, CRL_REG_LEN_08BIT, 0x00}, + {0x7890, CRL_REG_LEN_08BIT, 0x0f}, + {0x7891, CRL_REG_LEN_08BIT, 0x00}, + {0x7892, CRL_REG_LEN_08BIT, 0x0f}, + {0x7893, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x00}, + {0x30a7, CRL_REG_LEN_08BIT, 0x48}, + {0x30ab, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x3001, CRL_REG_LEN_08BIT, 0x32}, + {0x3005, CRL_REG_LEN_08BIT, 0x13}, + {0x3014, CRL_REG_LEN_08BIT, 0x44}, + {0x30b0, CRL_REG_LEN_08BIT, 0x1d}, + {0x30b1, CRL_REG_LEN_08BIT, 0xe2}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x60}, + {0x30b6, CRL_REG_LEN_08BIT, 0x04}, + {0x30b7, CRL_REG_LEN_08BIT, 0x5c}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x0a}, + {0x3195, CRL_REG_LEN_08BIT, 0x04}, + {0x31e3, CRL_REG_LEN_08BIT, 0x02}, + {0x31e4, CRL_REG_LEN_08BIT, 0x10}, + {0x3250, CRL_REG_LEN_08BIT, 0xf7}, + {0x3012, CRL_REG_LEN_08BIT, 0x01}, +}; + +/* ov2775_1928x1088_2x12_30fps_mipi960_regset */ +static struct crl_register_write_rep ov2775_2x12_30fps_mipi960_regset[] = { + {0x3013, CRL_REG_LEN_08BIT, 0x01}, + {0x3000, CRL_REG_LEN_08BIT, 0x02}, + {0x3001, CRL_REG_LEN_08BIT, 0x28}, + {0x3002, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x01}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, + {0x3005, CRL_REG_LEN_08BIT, 0x26}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x07}, + {0x3008, CRL_REG_LEN_08BIT, 0x01}, + {0x3009, CRL_REG_LEN_08BIT, 0x00}, + {0x300c, CRL_REG_LEN_08BIT, 0x6c}, + {0x300e, CRL_REG_LEN_08BIT, 0x80}, + {0x300f, CRL_REG_LEN_08BIT, 0x00}, + {0x3012, CRL_REG_LEN_08BIT, 0x00}, + {0x3013, CRL_REG_LEN_08BIT, 0x00}, + {0x3014, CRL_REG_LEN_08BIT, 0xc4}, + {0x3015, CRL_REG_LEN_08BIT, 0x00}, + {0x3017, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x00}, + {0x3019, CRL_REG_LEN_08BIT, 0x00}, + {0x301a, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0x0e}, + {0x301e, CRL_REG_LEN_08BIT, 0x17}, + {0x301f, CRL_REG_LEN_08BIT, 0xe1}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x3031, CRL_REG_LEN_08BIT, 0x62}, + {0x3032, CRL_REG_LEN_08BIT, 0xf0}, + {0x3033, CRL_REG_LEN_08BIT, 0x30}, + {0x3034, CRL_REG_LEN_08BIT, 0x3f}, + {0x3035, CRL_REG_LEN_08BIT, 0x5f}, + {0x3036, CRL_REG_LEN_08BIT, 0x02}, + {0x3037, CRL_REG_LEN_08BIT, 0x9f}, + {0x3038, CRL_REG_LEN_08BIT, 0x04}, + {0x3039, CRL_REG_LEN_08BIT, 0xb7}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x07}, + {0x303c, CRL_REG_LEN_08BIT, 0xf0}, + {0x303d, CRL_REG_LEN_08BIT, 0x00}, + {0x303e, CRL_REG_LEN_08BIT, 0x0b}, + {0x303f, CRL_REG_LEN_08BIT, 0xe3}, + {0x3040, CRL_REG_LEN_08BIT, 0xf3}, + {0x3041, CRL_REG_LEN_08BIT, 0x29}, + {0x3042, CRL_REG_LEN_08BIT, 0xf6}, + {0x3043, CRL_REG_LEN_08BIT, 0x65}, + {0x3044, CRL_REG_LEN_08BIT, 0x06}, + {0x3045, CRL_REG_LEN_08BIT, 0x0f}, + {0x3046, CRL_REG_LEN_08BIT, 0x59}, + {0x3047, CRL_REG_LEN_08BIT, 0x07}, + {0x3048, CRL_REG_LEN_08BIT, 0x82}, + {0x3049, CRL_REG_LEN_08BIT, 0xcf}, + {0x304a, CRL_REG_LEN_08BIT, 0x12}, + {0x304b, CRL_REG_LEN_08BIT, 0x40}, + {0x304c, CRL_REG_LEN_08BIT, 0x33}, + {0x304d, CRL_REG_LEN_08BIT, 0xa4}, + {0x304e, CRL_REG_LEN_08BIT, 0x0b}, + {0x304f, CRL_REG_LEN_08BIT, 0x3d}, + {0x3050, CRL_REG_LEN_08BIT, 0x10}, + {0x3060, CRL_REG_LEN_08BIT, 0x00}, + {0x3061, CRL_REG_LEN_08BIT, 0x64}, + {0x3062, CRL_REG_LEN_08BIT, 0x00}, + {0x3063, CRL_REG_LEN_08BIT, 0xe4}, + {0x3066, CRL_REG_LEN_08BIT, 0x80}, + {0x3080, CRL_REG_LEN_08BIT, 0x00}, + {0x3081, CRL_REG_LEN_08BIT, 0x00}, + {0x3082, CRL_REG_LEN_08BIT, 0x01}, + {0x3083, CRL_REG_LEN_08BIT, 0xe3}, + {0x3084, CRL_REG_LEN_08BIT, 0x06}, + {0x3085, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x10}, + {0x3087, CRL_REG_LEN_08BIT, 0x10}, + {0x3089, CRL_REG_LEN_08BIT, 0x00}, + {0x308a, CRL_REG_LEN_08BIT, 0x01}, + {0x3093, CRL_REG_LEN_08BIT, 0x00}, + {0x30a0, CRL_REG_LEN_08BIT, 0x00}, + {0x30a1, CRL_REG_LEN_08BIT, 0x04}, + {0x30a2, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30a4, CRL_REG_LEN_08BIT, 0x07}, + {0x30a5, CRL_REG_LEN_08BIT, 0x8b}, + {0x30a6, CRL_REG_LEN_08BIT, 0x04}, + {0x30a7, CRL_REG_LEN_08BIT, 0x3f}, + {0x30a8, CRL_REG_LEN_08BIT, 0x00}, + {0x30a9, CRL_REG_LEN_08BIT, 0x04}, + {0x30aa, CRL_REG_LEN_08BIT, 0x00}, + {0x30ab, CRL_REG_LEN_08BIT, 0x00}, + {0x30ac, CRL_REG_LEN_08BIT, 0x07}, + {0x30ad, CRL_REG_LEN_08BIT, 0x80}, + {0x30ae, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x08}, + {0x30b1, CRL_REG_LEN_08BIT, 0x98}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x65}, + {0x30b4, CRL_REG_LEN_08BIT, 0x00}, + {0x30b5, CRL_REG_LEN_08BIT, 0x00}, + {0x30b6, CRL_REG_LEN_08BIT, 0x00}, + {0x30b7, CRL_REG_LEN_08BIT, 0x10}, + {0x30b8, CRL_REG_LEN_08BIT, 0x00}, + {0x30b9, CRL_REG_LEN_08BIT, 0x02}, + {0x30ba, CRL_REG_LEN_08BIT, 0x10}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x03}, + {0x30be, CRL_REG_LEN_08BIT, 0x5c}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x01}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x20}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x4a}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x00}, + {0x30d1, CRL_REG_LEN_08BIT, 0x00}, + {0x30d2, CRL_REG_LEN_08BIT, 0x00}, + {0x30d3, CRL_REG_LEN_08BIT, 0x80}, + {0x30d4, CRL_REG_LEN_08BIT, 0x00}, + {0x30d9, CRL_REG_LEN_08BIT, 0x09}, + {0x30da, CRL_REG_LEN_08BIT, 0x64}, + {0x30dd, CRL_REG_LEN_08BIT, 0x00}, + {0x30de, CRL_REG_LEN_08BIT, 0x16}, + {0x30df, CRL_REG_LEN_08BIT, 0x00}, + {0x30e0, CRL_REG_LEN_08BIT, 0x17}, + {0x30e1, CRL_REG_LEN_08BIT, 0x00}, + {0x30e2, CRL_REG_LEN_08BIT, 0x18}, + {0x30e3, CRL_REG_LEN_08BIT, 0x10}, + {0x30e4, CRL_REG_LEN_08BIT, 0x04}, + {0x30e5, CRL_REG_LEN_08BIT, 0x00}, + {0x30e6, CRL_REG_LEN_08BIT, 0x00}, + {0x30e7, CRL_REG_LEN_08BIT, 0x00}, + {0x30e8, CRL_REG_LEN_08BIT, 0x00}, + {0x30e9, CRL_REG_LEN_08BIT, 0x00}, + {0x30ea, CRL_REG_LEN_08BIT, 0x00}, + {0x30eb, CRL_REG_LEN_08BIT, 0x00}, + {0x30ec, CRL_REG_LEN_08BIT, 0x00}, + {0x30ed, CRL_REG_LEN_08BIT, 0x00}, + {0x3101, CRL_REG_LEN_08BIT, 0x00}, + {0x3102, CRL_REG_LEN_08BIT, 0x00}, + {0x3103, CRL_REG_LEN_08BIT, 0x00}, + {0x3104, CRL_REG_LEN_08BIT, 0x00}, + {0x3105, CRL_REG_LEN_08BIT, 0x8c}, + {0x3106, CRL_REG_LEN_08BIT, 0x87}, + {0x3107, CRL_REG_LEN_08BIT, 0xc0}, + {0x3108, CRL_REG_LEN_08BIT, 0x9d}, + {0x3109, CRL_REG_LEN_08BIT, 0x8d}, + {0x310a, CRL_REG_LEN_08BIT, 0x8d}, + {0x310b, CRL_REG_LEN_08BIT, 0x6a}, + {0x310c, CRL_REG_LEN_08BIT, 0x3a}, + {0x310d, CRL_REG_LEN_08BIT, 0x5a}, + {0x310e, CRL_REG_LEN_08BIT, 0x00}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x00}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3123, CRL_REG_LEN_08BIT, 0x00}, + {0x3124, CRL_REG_LEN_08BIT, 0x00}, + {0x3125, CRL_REG_LEN_08BIT, 0x70}, + {0x3126, CRL_REG_LEN_08BIT, 0x1f}, + {0x3127, CRL_REG_LEN_08BIT, 0x0f}, + {0x3128, CRL_REG_LEN_08BIT, 0x00}, + {0x3129, CRL_REG_LEN_08BIT, 0x3a}, + {0x312a, CRL_REG_LEN_08BIT, 0x02}, + {0x312b, CRL_REG_LEN_08BIT, 0x0f}, + {0x312c, CRL_REG_LEN_08BIT, 0x00}, + {0x312d, CRL_REG_LEN_08BIT, 0x0f}, + {0x312e, CRL_REG_LEN_08BIT, 0x1d}, + {0x312f, CRL_REG_LEN_08BIT, 0x00}, + {0x3130, CRL_REG_LEN_08BIT, 0x00}, + {0x3131, CRL_REG_LEN_08BIT, 0x00}, + {0x3132, CRL_REG_LEN_08BIT, 0x00}, + {0x3140, CRL_REG_LEN_08BIT, 0x0a}, + {0x3141, CRL_REG_LEN_08BIT, 0x03}, + {0x3142, CRL_REG_LEN_08BIT, 0x00}, + {0x3143, CRL_REG_LEN_08BIT, 0x00}, + {0x3144, CRL_REG_LEN_08BIT, 0x00}, + {0x3145, CRL_REG_LEN_08BIT, 0x00}, + {0x3146, CRL_REG_LEN_08BIT, 0x00}, + {0x3147, CRL_REG_LEN_08BIT, 0x00}, + {0x3148, CRL_REG_LEN_08BIT, 0x00}, + {0x3149, CRL_REG_LEN_08BIT, 0x00}, + {0x314a, CRL_REG_LEN_08BIT, 0x00}, + {0x314b, CRL_REG_LEN_08BIT, 0x00}, + {0x314c, CRL_REG_LEN_08BIT, 0x00}, + {0x314d, CRL_REG_LEN_08BIT, 0x00}, + {0x314e, CRL_REG_LEN_08BIT, 0x1c}, + {0x314f, CRL_REG_LEN_08BIT, 0xff}, + {0x3150, CRL_REG_LEN_08BIT, 0xff}, + {0x3151, CRL_REG_LEN_08BIT, 0xff}, + {0x3152, CRL_REG_LEN_08BIT, 0x10}, + {0x3153, CRL_REG_LEN_08BIT, 0x10}, + {0x3154, CRL_REG_LEN_08BIT, 0x10}, + {0x3155, CRL_REG_LEN_08BIT, 0x00}, + {0x3156, CRL_REG_LEN_08BIT, 0x03}, + {0x3157, CRL_REG_LEN_08BIT, 0x00}, + {0x3158, CRL_REG_LEN_08BIT, 0x0f}, + {0x3159, CRL_REG_LEN_08BIT, 0xff}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3160, CRL_REG_LEN_08BIT, 0x01}, + {0x3161, CRL_REG_LEN_08BIT, 0x00}, + {0x3162, CRL_REG_LEN_08BIT, 0x01}, + {0x3163, CRL_REG_LEN_08BIT, 0x00}, + {0x3164, CRL_REG_LEN_08BIT, 0x01}, + {0x3165, CRL_REG_LEN_08BIT, 0x00}, + {0x3190, CRL_REG_LEN_08BIT, 0x01}, + {0x3191, CRL_REG_LEN_08BIT, 0x99}, + {0x3193, CRL_REG_LEN_08BIT, 0x08}, + {0x3194, CRL_REG_LEN_08BIT, 0x13}, + {0x3195, CRL_REG_LEN_08BIT, 0x33}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x10}, + {0x3198, CRL_REG_LEN_08BIT, 0x00}, + {0x3199, CRL_REG_LEN_08BIT, 0x3f}, + {0x319a, CRL_REG_LEN_08BIT, 0x40}, + {0x319b, CRL_REG_LEN_08BIT, 0x7f}, + {0x319c, CRL_REG_LEN_08BIT, 0x80}, + {0x319d, CRL_REG_LEN_08BIT, 0xbf}, + {0x319e, CRL_REG_LEN_08BIT, 0xc0}, + {0x319f, CRL_REG_LEN_08BIT, 0xff}, + {0x31a0, CRL_REG_LEN_08BIT, 0x24}, + {0x31a1, CRL_REG_LEN_08BIT, 0x55}, + {0x31a2, CRL_REG_LEN_08BIT, 0x00}, + /* vfifo manual override */ + {0x31a3, CRL_REG_LEN_08BIT, 0x08}, + {0x31a6, CRL_REG_LEN_08BIT, 0x00}, + {0x31a7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b0, CRL_REG_LEN_08BIT, 0x00}, + {0x31b1, CRL_REG_LEN_08BIT, 0x00}, + {0x31b2, CRL_REG_LEN_08BIT, 0x02}, + {0x31b3, CRL_REG_LEN_08BIT, 0x00}, + {0x31b4, CRL_REG_LEN_08BIT, 0x00}, + {0x31b5, CRL_REG_LEN_08BIT, 0x01}, + {0x31b6, CRL_REG_LEN_08BIT, 0x00}, + {0x31b7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b8, CRL_REG_LEN_08BIT, 0x00}, + {0x31b9, CRL_REG_LEN_08BIT, 0x00}, + {0x31ba, CRL_REG_LEN_08BIT, 0x00}, + {0x31d0, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d1, CRL_REG_LEN_08BIT, 0x34}, + {0x31d2, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d3, CRL_REG_LEN_08BIT, 0x00}, + {0x31d4, CRL_REG_LEN_08BIT, 0x2d}, + {0x31d5, CRL_REG_LEN_08BIT, 0x00}, + {0x31d6, CRL_REG_LEN_08BIT, 0x01}, + {0x31d7, CRL_REG_LEN_08BIT, 0x06}, + {0x31d8, CRL_REG_LEN_08BIT, 0x00}, + {0x31d9, CRL_REG_LEN_08BIT, 0x64}, + {0x31da, CRL_REG_LEN_08BIT, 0x00}, + {0x31db, CRL_REG_LEN_08BIT, 0x30}, + {0x31dc, CRL_REG_LEN_08BIT, 0x04}, + {0x31dd, CRL_REG_LEN_08BIT, 0x69}, + {0x31de, CRL_REG_LEN_08BIT, 0x0a}, + {0x31df, CRL_REG_LEN_08BIT, 0x3c}, + {0x31e0, CRL_REG_LEN_08BIT, 0x04}, + {0x31e1, CRL_REG_LEN_08BIT, 0x32}, + {0x31e2, CRL_REG_LEN_08BIT, 0x00}, + {0x31e3, CRL_REG_LEN_08BIT, 0x00}, + {0x31e4, CRL_REG_LEN_08BIT, 0x08}, + {0x31e5, CRL_REG_LEN_08BIT, 0x80}, + {0x31e6, CRL_REG_LEN_08BIT, 0x00}, + /* MIPI data type, 0x31e7-0x31eb */ + {0x31e7, CRL_REG_LEN_08BIT, 0x2c}, + {0x31e8, CRL_REG_LEN_08BIT, 0x6c}, + {0x31e9, CRL_REG_LEN_08BIT, 0xac}, + {0x31ea, CRL_REG_LEN_08BIT, 0xec}, + {0x31eb, CRL_REG_LEN_08BIT, 0x3f}, + {0x31ec, CRL_REG_LEN_08BIT, 0x0f}, + {0x31ed, CRL_REG_LEN_08BIT, 0x20}, + {0x31ee, CRL_REG_LEN_08BIT, 0x04}, + {0x31ef, CRL_REG_LEN_08BIT, 0x48}, + {0x31f0, CRL_REG_LEN_08BIT, 0x07}, + {0x31f1, CRL_REG_LEN_08BIT, 0x90}, + {0x31f2, CRL_REG_LEN_08BIT, 0x04}, + {0x31f3, CRL_REG_LEN_08BIT, 0x48}, + {0x31f4, CRL_REG_LEN_08BIT, 0x07}, + {0x31f5, CRL_REG_LEN_08BIT, 0x90}, + {0x31f6, CRL_REG_LEN_08BIT, 0x04}, + {0x31f7, CRL_REG_LEN_08BIT, 0x48}, + {0x31f8, CRL_REG_LEN_08BIT, 0x07}, + {0x31f9, CRL_REG_LEN_08BIT, 0x90}, + {0x31fa, CRL_REG_LEN_08BIT, 0x04}, + {0x31fb, CRL_REG_LEN_08BIT, 0x48}, + {0x31fd, CRL_REG_LEN_08BIT, 0xcb}, + {0x31fe, CRL_REG_LEN_08BIT, 0x01}, + {0x31ff, CRL_REG_LEN_08BIT, 0x03}, + {0x3200, CRL_REG_LEN_08BIT, 0x00}, + {0x3201, CRL_REG_LEN_08BIT, 0xff}, + {0x3202, CRL_REG_LEN_08BIT, 0x00}, + {0x3203, CRL_REG_LEN_08BIT, 0xff}, + {0x3204, CRL_REG_LEN_08BIT, 0xff}, + {0x3205, CRL_REG_LEN_08BIT, 0xff}, + {0x3206, CRL_REG_LEN_08BIT, 0xff}, + {0x3207, CRL_REG_LEN_08BIT, 0xff}, + {0x3208, CRL_REG_LEN_08BIT, 0xff}, + {0x3209, CRL_REG_LEN_08BIT, 0xff}, + {0x320a, CRL_REG_LEN_08BIT, 0xff}, + {0x320b, CRL_REG_LEN_08BIT, 0x1b}, + {0x320c, CRL_REG_LEN_08BIT, 0x1f}, + {0x320d, CRL_REG_LEN_08BIT, 0x1e}, + {0x320e, CRL_REG_LEN_08BIT, 0x30}, + {0x320f, CRL_REG_LEN_08BIT, 0x2d}, + {0x3210, CRL_REG_LEN_08BIT, 0x2c}, + {0x3211, CRL_REG_LEN_08BIT, 0x2b}, + {0x3212, CRL_REG_LEN_08BIT, 0x2a}, + {0x3213, CRL_REG_LEN_08BIT, 0x24}, + {0x3214, CRL_REG_LEN_08BIT, 0x22}, + {0x3215, CRL_REG_LEN_08BIT, 0x00}, + {0x3216, CRL_REG_LEN_08BIT, 0x04}, + /* mipi data tag, 0x3217-0x321a */ + {0x3217, CRL_REG_LEN_08BIT, 0x2c}, + {0x3218, CRL_REG_LEN_08BIT, 0x6c}, + {0x3219, CRL_REG_LEN_08BIT, 0xac}, + {0x321a, CRL_REG_LEN_08BIT, 0xec}, + {0x321b, CRL_REG_LEN_08BIT, 0x00}, + {0x3230, CRL_REG_LEN_08BIT, 0x3a}, + {0x3231, CRL_REG_LEN_08BIT, 0x00}, + {0x3232, CRL_REG_LEN_08BIT, 0x80}, + {0x3233, CRL_REG_LEN_08BIT, 0x00}, + {0x3234, CRL_REG_LEN_08BIT, 0x10}, + {0x3235, CRL_REG_LEN_08BIT, 0xaa}, + {0x3236, CRL_REG_LEN_08BIT, 0x55}, + {0x3237, CRL_REG_LEN_08BIT, 0x99}, + {0x3238, CRL_REG_LEN_08BIT, 0x66}, + {0x3239, CRL_REG_LEN_08BIT, 0x08}, + {0x323a, CRL_REG_LEN_08BIT, 0x88}, + {0x323b, CRL_REG_LEN_08BIT, 0x00}, + {0x323c, CRL_REG_LEN_08BIT, 0x00}, + {0x323d, CRL_REG_LEN_08BIT, 0x03}, + {0x3250, CRL_REG_LEN_08BIT, 0x33}, + {0x3251, CRL_REG_LEN_08BIT, 0x00}, + {0x3252, CRL_REG_LEN_08BIT, 0x20}, + {0x3253, CRL_REG_LEN_08BIT, 0x00}, + {0x3254, CRL_REG_LEN_08BIT, 0x11}, + {0x3255, CRL_REG_LEN_08BIT, 0x01}, + {0x3256, CRL_REG_LEN_08BIT, 0x00}, + {0x3257, CRL_REG_LEN_08BIT, 0x00}, + {0x3258, CRL_REG_LEN_08BIT, 0x00}, + {0x3270, CRL_REG_LEN_08BIT, 0x01}, + {0x3271, CRL_REG_LEN_08BIT, 0xc0}, + {0x3272, CRL_REG_LEN_08BIT, 0xf0}, + {0x3273, CRL_REG_LEN_08BIT, 0x01}, + {0x3274, CRL_REG_LEN_08BIT, 0x00}, + {0x3275, CRL_REG_LEN_08BIT, 0x40}, + {0x3276, CRL_REG_LEN_08BIT, 0x02}, + {0x3277, CRL_REG_LEN_08BIT, 0x08}, + {0x3278, CRL_REG_LEN_08BIT, 0x10}, + {0x3279, CRL_REG_LEN_08BIT, 0x04}, + {0x327a, CRL_REG_LEN_08BIT, 0x00}, + {0x327b, CRL_REG_LEN_08BIT, 0x03}, + {0x327c, CRL_REG_LEN_08BIT, 0x10}, + {0x327d, CRL_REG_LEN_08BIT, 0x60}, + {0x327e, CRL_REG_LEN_08BIT, 0xc0}, + {0x327f, CRL_REG_LEN_08BIT, 0x06}, + {0x3288, CRL_REG_LEN_08BIT, 0x10}, + {0x3289, CRL_REG_LEN_08BIT, 0x00}, + {0x328a, CRL_REG_LEN_08BIT, 0x08}, + {0x328b, CRL_REG_LEN_08BIT, 0x00}, + {0x328c, CRL_REG_LEN_08BIT, 0x04}, + {0x328d, CRL_REG_LEN_08BIT, 0x00}, + {0x328e, CRL_REG_LEN_08BIT, 0x02}, + {0x328f, CRL_REG_LEN_08BIT, 0x00}, + {0x3290, CRL_REG_LEN_08BIT, 0x20}, + {0x3291, CRL_REG_LEN_08BIT, 0x00}, + {0x3292, CRL_REG_LEN_08BIT, 0x10}, + {0x3293, CRL_REG_LEN_08BIT, 0x00}, + {0x3294, CRL_REG_LEN_08BIT, 0x08}, + {0x3295, CRL_REG_LEN_08BIT, 0x00}, + {0x3296, CRL_REG_LEN_08BIT, 0x04}, + {0x3297, CRL_REG_LEN_08BIT, 0x00}, + {0x3298, CRL_REG_LEN_08BIT, 0x40}, + {0x3299, CRL_REG_LEN_08BIT, 0x00}, + {0x329a, CRL_REG_LEN_08BIT, 0x20}, + {0x329b, CRL_REG_LEN_08BIT, 0x00}, + {0x329c, CRL_REG_LEN_08BIT, 0x10}, + {0x329d, CRL_REG_LEN_08BIT, 0x00}, + {0x329e, CRL_REG_LEN_08BIT, 0x08}, + {0x329f, CRL_REG_LEN_08BIT, 0x00}, + {0x32a0, CRL_REG_LEN_08BIT, 0x7f}, + {0x32a1, CRL_REG_LEN_08BIT, 0xff}, + {0x32a2, CRL_REG_LEN_08BIT, 0x40}, + {0x32a3, CRL_REG_LEN_08BIT, 0x00}, + {0x32a4, CRL_REG_LEN_08BIT, 0x20}, + {0x32a5, CRL_REG_LEN_08BIT, 0x00}, + {0x32a6, CRL_REG_LEN_08BIT, 0x10}, + {0x32a7, CRL_REG_LEN_08BIT, 0x00}, + {0x32a8, CRL_REG_LEN_08BIT, 0x00}, + {0x32a9, CRL_REG_LEN_08BIT, 0x00}, + {0x32aa, CRL_REG_LEN_08BIT, 0x00}, + {0x32ab, CRL_REG_LEN_08BIT, 0x00}, + {0x32ac, CRL_REG_LEN_08BIT, 0x00}, + {0x32ad, CRL_REG_LEN_08BIT, 0x00}, + {0x32ae, CRL_REG_LEN_08BIT, 0x00}, + {0x32af, CRL_REG_LEN_08BIT, 0x00}, + {0x32b0, CRL_REG_LEN_08BIT, 0x00}, + {0x32b1, CRL_REG_LEN_08BIT, 0x00}, + {0x32b2, CRL_REG_LEN_08BIT, 0x00}, + {0x32b3, CRL_REG_LEN_08BIT, 0x00}, + {0x32b4, CRL_REG_LEN_08BIT, 0x00}, + {0x32b5, CRL_REG_LEN_08BIT, 0x00}, + {0x32b6, CRL_REG_LEN_08BIT, 0x00}, + {0x32b7, CRL_REG_LEN_08BIT, 0x00}, + {0x32b8, CRL_REG_LEN_08BIT, 0x00}, + {0x32b9, CRL_REG_LEN_08BIT, 0x00}, + {0x32ba, CRL_REG_LEN_08BIT, 0x00}, + {0x32bb, CRL_REG_LEN_08BIT, 0x00}, + {0x32bc, CRL_REG_LEN_08BIT, 0x00}, + {0x32bd, CRL_REG_LEN_08BIT, 0x00}, + {0x32be, CRL_REG_LEN_08BIT, 0x00}, + {0x32bf, CRL_REG_LEN_08BIT, 0x00}, + {0x32c0, CRL_REG_LEN_08BIT, 0x00}, + {0x32c1, CRL_REG_LEN_08BIT, 0x00}, + {0x32c2, CRL_REG_LEN_08BIT, 0x00}, + {0x32c3, CRL_REG_LEN_08BIT, 0x00}, + {0x32c4, CRL_REG_LEN_08BIT, 0x00}, + {0x32c5, CRL_REG_LEN_08BIT, 0x00}, + {0x32c6, CRL_REG_LEN_08BIT, 0x00}, + {0x32c7, CRL_REG_LEN_08BIT, 0x00}, + {0x32c8, CRL_REG_LEN_08BIT, 0x87}, + {0x32c9, CRL_REG_LEN_08BIT, 0x00}, + {0x3330, CRL_REG_LEN_08BIT, 0x03}, + {0x3331, CRL_REG_LEN_08BIT, 0xc8}, + {0x3332, CRL_REG_LEN_08BIT, 0x02}, + {0x3333, CRL_REG_LEN_08BIT, 0x24}, + {0x3334, CRL_REG_LEN_08BIT, 0x00}, + {0x3335, CRL_REG_LEN_08BIT, 0x00}, + {0x3336, CRL_REG_LEN_08BIT, 0x00}, + {0x3337, CRL_REG_LEN_08BIT, 0x00}, + {0x3338, CRL_REG_LEN_08BIT, 0x03}, + {0x3339, CRL_REG_LEN_08BIT, 0xc8}, + {0x333a, CRL_REG_LEN_08BIT, 0x02}, + {0x333b, CRL_REG_LEN_08BIT, 0x24}, + {0x333c, CRL_REG_LEN_08BIT, 0x00}, + {0x333d, CRL_REG_LEN_08BIT, 0x00}, + {0x333e, CRL_REG_LEN_08BIT, 0x00}, + {0x333f, CRL_REG_LEN_08BIT, 0x00}, + {0x3340, CRL_REG_LEN_08BIT, 0x03}, + {0x3341, CRL_REG_LEN_08BIT, 0xc8}, + {0x3342, CRL_REG_LEN_08BIT, 0x02}, + {0x3343, CRL_REG_LEN_08BIT, 0x24}, + {0x3344, CRL_REG_LEN_08BIT, 0x00}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3346, CRL_REG_LEN_08BIT, 0x00}, + {0x3347, CRL_REG_LEN_08BIT, 0x00}, + {0x3348, CRL_REG_LEN_08BIT, 0x40}, + {0x3349, CRL_REG_LEN_08BIT, 0x00}, + {0x334a, CRL_REG_LEN_08BIT, 0x00}, + {0x334b, CRL_REG_LEN_08BIT, 0x00}, + {0x334c, CRL_REG_LEN_08BIT, 0x00}, + {0x334d, CRL_REG_LEN_08BIT, 0x00}, + {0x334e, CRL_REG_LEN_08BIT, 0x80}, + {0x3360, CRL_REG_LEN_08BIT, 0x01}, + {0x3361, CRL_REG_LEN_08BIT, 0x00}, + {0x3362, CRL_REG_LEN_08BIT, 0x01}, + {0x3363, CRL_REG_LEN_08BIT, 0x00}, + {0x3364, CRL_REG_LEN_08BIT, 0x01}, + {0x3365, CRL_REG_LEN_08BIT, 0x00}, + {0x3366, CRL_REG_LEN_08BIT, 0x01}, + {0x3367, CRL_REG_LEN_08BIT, 0x00}, + {0x3368, CRL_REG_LEN_08BIT, 0x01}, + {0x3369, CRL_REG_LEN_08BIT, 0x00}, + {0x336a, CRL_REG_LEN_08BIT, 0x01}, + {0x336b, CRL_REG_LEN_08BIT, 0x00}, + {0x336c, CRL_REG_LEN_08BIT, 0x01}, + {0x336d, CRL_REG_LEN_08BIT, 0x00}, + {0x336e, CRL_REG_LEN_08BIT, 0x01}, + {0x336f, CRL_REG_LEN_08BIT, 0x00}, + {0x3370, CRL_REG_LEN_08BIT, 0x01}, + {0x3371, CRL_REG_LEN_08BIT, 0x00}, + {0x3372, CRL_REG_LEN_08BIT, 0x01}, + {0x3373, CRL_REG_LEN_08BIT, 0x00}, + {0x3374, CRL_REG_LEN_08BIT, 0x01}, + {0x3375, CRL_REG_LEN_08BIT, 0x00}, + {0x3376, CRL_REG_LEN_08BIT, 0x01}, + {0x3377, CRL_REG_LEN_08BIT, 0x00}, + {0x3378, CRL_REG_LEN_08BIT, 0x00}, + {0x3379, CRL_REG_LEN_08BIT, 0x00}, + {0x337a, CRL_REG_LEN_08BIT, 0x00}, + {0x337b, CRL_REG_LEN_08BIT, 0x00}, + {0x337c, CRL_REG_LEN_08BIT, 0x00}, + {0x337d, CRL_REG_LEN_08BIT, 0x00}, + {0x337e, CRL_REG_LEN_08BIT, 0x00}, + {0x337f, CRL_REG_LEN_08BIT, 0x00}, + {0x3380, CRL_REG_LEN_08BIT, 0x00}, + {0x3381, CRL_REG_LEN_08BIT, 0x00}, + {0x3382, CRL_REG_LEN_08BIT, 0x00}, + {0x3383, CRL_REG_LEN_08BIT, 0x00}, + {0x3384, CRL_REG_LEN_08BIT, 0x00}, + {0x3385, CRL_REG_LEN_08BIT, 0x00}, + {0x3386, CRL_REG_LEN_08BIT, 0x00}, + {0x3387, CRL_REG_LEN_08BIT, 0x00}, + {0x3388, CRL_REG_LEN_08BIT, 0x00}, + {0x3389, CRL_REG_LEN_08BIT, 0x00}, + {0x338a, CRL_REG_LEN_08BIT, 0x00}, + {0x338b, CRL_REG_LEN_08BIT, 0x00}, + {0x338c, CRL_REG_LEN_08BIT, 0x00}, + {0x338d, CRL_REG_LEN_08BIT, 0x00}, + {0x338e, CRL_REG_LEN_08BIT, 0x00}, + {0x338f, CRL_REG_LEN_08BIT, 0x00}, + {0x3390, CRL_REG_LEN_08BIT, 0x00}, + {0x3391, CRL_REG_LEN_08BIT, 0x00}, + {0x3392, CRL_REG_LEN_08BIT, 0x00}, + {0x3393, CRL_REG_LEN_08BIT, 0x00}, + {0x3394, CRL_REG_LEN_08BIT, 0x00}, + {0x3395, CRL_REG_LEN_08BIT, 0x00}, + {0x3396, CRL_REG_LEN_08BIT, 0x00}, + {0x3397, CRL_REG_LEN_08BIT, 0x00}, + {0x3398, CRL_REG_LEN_08BIT, 0x00}, + {0x3399, CRL_REG_LEN_08BIT, 0x00}, + {0x339a, CRL_REG_LEN_08BIT, 0x00}, + {0x339b, CRL_REG_LEN_08BIT, 0x00}, + {0x33b0, CRL_REG_LEN_08BIT, 0x00}, + {0x33b1, CRL_REG_LEN_08BIT, 0x50}, + {0x33b2, CRL_REG_LEN_08BIT, 0x01}, + {0x33b3, CRL_REG_LEN_08BIT, 0xff}, + {0x33b4, CRL_REG_LEN_08BIT, 0xe0}, + {0x33b5, CRL_REG_LEN_08BIT, 0x6b}, + {0x33b6, CRL_REG_LEN_08BIT, 0x00}, + {0x33b7, CRL_REG_LEN_08BIT, 0x00}, + {0x33b8, CRL_REG_LEN_08BIT, 0x00}, + {0x33b9, CRL_REG_LEN_08BIT, 0x00}, + {0x33ba, CRL_REG_LEN_08BIT, 0x00}, + {0x33bb, CRL_REG_LEN_08BIT, 0x1f}, + {0x33bc, CRL_REG_LEN_08BIT, 0x01}, + {0x33bd, CRL_REG_LEN_08BIT, 0x01}, + {0x33be, CRL_REG_LEN_08BIT, 0x01}, + {0x33bf, CRL_REG_LEN_08BIT, 0x01}, + {0x33c0, CRL_REG_LEN_08BIT, 0x00}, + {0x33c1, CRL_REG_LEN_08BIT, 0x00}, + {0x33c2, CRL_REG_LEN_08BIT, 0x00}, + {0x33c3, CRL_REG_LEN_08BIT, 0x00}, + {0x33e0, CRL_REG_LEN_08BIT, 0x14}, + {0x33e1, CRL_REG_LEN_08BIT, 0x0f}, + {0x33e2, CRL_REG_LEN_08BIT, 0x04}, + {0x33e3, CRL_REG_LEN_08BIT, 0x02}, + {0x33e4, CRL_REG_LEN_08BIT, 0x01}, + {0x33e5, CRL_REG_LEN_08BIT, 0x01}, + {0x33e6, CRL_REG_LEN_08BIT, 0x00}, + {0x33e7, CRL_REG_LEN_08BIT, 0x04}, + {0x33e8, CRL_REG_LEN_08BIT, 0x0c}, + {0x33e9, CRL_REG_LEN_08BIT, 0x02}, + {0x33ea, CRL_REG_LEN_08BIT, 0x02}, + {0x33eb, CRL_REG_LEN_08BIT, 0x02}, + {0x33ec, CRL_REG_LEN_08BIT, 0x03}, + {0x33ed, CRL_REG_LEN_08BIT, 0x02}, + {0x33ee, CRL_REG_LEN_08BIT, 0x05}, + {0x33ef, CRL_REG_LEN_08BIT, 0x0a}, + {0x33f0, CRL_REG_LEN_08BIT, 0x08}, + {0x33f1, CRL_REG_LEN_08BIT, 0x04}, + {0x33f2, CRL_REG_LEN_08BIT, 0x04}, + {0x33f3, CRL_REG_LEN_08BIT, 0x00}, + {0x33f4, CRL_REG_LEN_08BIT, 0x03}, + {0x33f5, CRL_REG_LEN_08BIT, 0x14}, + {0x33f6, CRL_REG_LEN_08BIT, 0x0f}, + {0x33f7, CRL_REG_LEN_08BIT, 0x02}, + {0x33f8, CRL_REG_LEN_08BIT, 0x01}, + {0x33f9, CRL_REG_LEN_08BIT, 0x01}, + {0x33fa, CRL_REG_LEN_08BIT, 0x01}, + {0x33fb, CRL_REG_LEN_08BIT, 0x00}, + {0x33fc, CRL_REG_LEN_08BIT, 0x04}, + {0x33fd, CRL_REG_LEN_08BIT, 0x0c}, + {0x33fe, CRL_REG_LEN_08BIT, 0x02}, + {0x33ff, CRL_REG_LEN_08BIT, 0x02}, + {0x3400, CRL_REG_LEN_08BIT, 0x02}, + {0x3401, CRL_REG_LEN_08BIT, 0x03}, + {0x3402, CRL_REG_LEN_08BIT, 0x01}, + {0x3403, CRL_REG_LEN_08BIT, 0x02}, + {0x3404, CRL_REG_LEN_08BIT, 0x08}, + {0x3405, CRL_REG_LEN_08BIT, 0x08}, + {0x3406, CRL_REG_LEN_08BIT, 0x04}, + {0x3407, CRL_REG_LEN_08BIT, 0x04}, + {0x3408, CRL_REG_LEN_08BIT, 0x00}, + {0x3409, CRL_REG_LEN_08BIT, 0x03}, + {0x340a, CRL_REG_LEN_08BIT, 0x14}, + {0x340b, CRL_REG_LEN_08BIT, 0x0f}, + {0x340c, CRL_REG_LEN_08BIT, 0x04}, + {0x340d, CRL_REG_LEN_08BIT, 0x02}, + {0x340e, CRL_REG_LEN_08BIT, 0x01}, + {0x340f, CRL_REG_LEN_08BIT, 0x01}, + {0x3410, CRL_REG_LEN_08BIT, 0x00}, + {0x3411, CRL_REG_LEN_08BIT, 0x04}, + {0x3412, CRL_REG_LEN_08BIT, 0x0c}, + {0x3413, CRL_REG_LEN_08BIT, 0x02}, + {0x3414, CRL_REG_LEN_08BIT, 0x02}, + {0x3415, CRL_REG_LEN_08BIT, 0x02}, + {0x3416, CRL_REG_LEN_08BIT, 0x03}, + {0x3417, CRL_REG_LEN_08BIT, 0x02}, + {0x3418, CRL_REG_LEN_08BIT, 0x05}, + {0x3419, CRL_REG_LEN_08BIT, 0x0a}, + {0x341a, CRL_REG_LEN_08BIT, 0x08}, + {0x341b, CRL_REG_LEN_08BIT, 0x04}, + {0x341c, CRL_REG_LEN_08BIT, 0x04}, + {0x341d, CRL_REG_LEN_08BIT, 0x00}, + {0x341e, CRL_REG_LEN_08BIT, 0x03}, + {0x3440, CRL_REG_LEN_08BIT, 0x00}, + {0x3441, CRL_REG_LEN_08BIT, 0x00}, + {0x3442, CRL_REG_LEN_08BIT, 0x00}, + {0x3443, CRL_REG_LEN_08BIT, 0x00}, + {0x3444, CRL_REG_LEN_08BIT, 0x02}, + {0x3445, CRL_REG_LEN_08BIT, 0xf0}, + {0x3446, CRL_REG_LEN_08BIT, 0x02}, + {0x3447, CRL_REG_LEN_08BIT, 0x08}, + {0x3448, CRL_REG_LEN_08BIT, 0x00}, + {0x3460, CRL_REG_LEN_08BIT, 0x40}, + {0x3461, CRL_REG_LEN_08BIT, 0x40}, + {0x3462, CRL_REG_LEN_08BIT, 0x40}, + {0x3463, CRL_REG_LEN_08BIT, 0x40}, + {0x3464, CRL_REG_LEN_08BIT, 0x03}, + {0x3465, CRL_REG_LEN_08BIT, 0x01}, + {0x3466, CRL_REG_LEN_08BIT, 0x01}, + {0x3467, CRL_REG_LEN_08BIT, 0x02}, + {0x3468, CRL_REG_LEN_08BIT, 0x30}, + {0x3469, CRL_REG_LEN_08BIT, 0x00}, + {0x346a, CRL_REG_LEN_08BIT, 0x35}, + {0x346b, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x40}, + {0x3481, CRL_REG_LEN_08BIT, 0x00}, + {0x3482, CRL_REG_LEN_08BIT, 0x00}, + {0x3483, CRL_REG_LEN_08BIT, 0x00}, + {0x3484, CRL_REG_LEN_08BIT, 0x0d}, + {0x3485, CRL_REG_LEN_08BIT, 0x00}, + {0x3486, CRL_REG_LEN_08BIT, 0x00}, + {0x3487, CRL_REG_LEN_08BIT, 0x00}, + {0x3488, CRL_REG_LEN_08BIT, 0x00}, + {0x3489, CRL_REG_LEN_08BIT, 0x00}, + {0x348a, CRL_REG_LEN_08BIT, 0x00}, + {0x348b, CRL_REG_LEN_08BIT, 0x04}, + {0x348c, CRL_REG_LEN_08BIT, 0x00}, + {0x348d, CRL_REG_LEN_08BIT, 0x01}, + {0x348f, CRL_REG_LEN_08BIT, 0x01}, + {0x3030, CRL_REG_LEN_08BIT, 0x0a}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x7000, CRL_REG_LEN_08BIT, 0x58}, + {0x7001, CRL_REG_LEN_08BIT, 0x7a}, + {0x7002, CRL_REG_LEN_08BIT, 0x1a}, + {0x7003, CRL_REG_LEN_08BIT, 0xc1}, + {0x7004, CRL_REG_LEN_08BIT, 0x03}, + {0x7005, CRL_REG_LEN_08BIT, 0xda}, + {0x7006, CRL_REG_LEN_08BIT, 0xbd}, + {0x7007, CRL_REG_LEN_08BIT, 0x03}, + {0x7008, CRL_REG_LEN_08BIT, 0xbd}, + {0x7009, CRL_REG_LEN_08BIT, 0x06}, + {0x700a, CRL_REG_LEN_08BIT, 0xe6}, + {0x700b, CRL_REG_LEN_08BIT, 0xec}, + {0x700c, CRL_REG_LEN_08BIT, 0xbc}, + {0x700d, CRL_REG_LEN_08BIT, 0xff}, + {0x700e, CRL_REG_LEN_08BIT, 0xbc}, + {0x700f, CRL_REG_LEN_08BIT, 0x73}, + {0x7010, CRL_REG_LEN_08BIT, 0xda}, + {0x7011, CRL_REG_LEN_08BIT, 0x72}, + {0x7012, CRL_REG_LEN_08BIT, 0x76}, + {0x7013, CRL_REG_LEN_08BIT, 0xb6}, + {0x7014, CRL_REG_LEN_08BIT, 0xee}, + {0x7015, CRL_REG_LEN_08BIT, 0xcf}, + {0x7016, CRL_REG_LEN_08BIT, 0xac}, + {0x7017, CRL_REG_LEN_08BIT, 0xd0}, + {0x7018, CRL_REG_LEN_08BIT, 0xac}, + {0x7019, CRL_REG_LEN_08BIT, 0xd1}, + {0x701a, CRL_REG_LEN_08BIT, 0x50}, + {0x701b, CRL_REG_LEN_08BIT, 0xac}, + {0x701c, CRL_REG_LEN_08BIT, 0xd2}, + {0x701d, CRL_REG_LEN_08BIT, 0xbc}, + {0x701e, CRL_REG_LEN_08BIT, 0x2e}, + {0x701f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7020, CRL_REG_LEN_08BIT, 0x00}, + {0x7021, CRL_REG_LEN_08BIT, 0xdc}, + {0x7022, CRL_REG_LEN_08BIT, 0xdf}, + {0x7023, CRL_REG_LEN_08BIT, 0xb0}, + {0x7024, CRL_REG_LEN_08BIT, 0x6e}, + {0x7025, CRL_REG_LEN_08BIT, 0xbd}, + {0x7026, CRL_REG_LEN_08BIT, 0x01}, + {0x7027, CRL_REG_LEN_08BIT, 0xd7}, + {0x7028, CRL_REG_LEN_08BIT, 0xed}, + {0x7029, CRL_REG_LEN_08BIT, 0xe1}, + {0x702a, CRL_REG_LEN_08BIT, 0x36}, + {0x702b, CRL_REG_LEN_08BIT, 0x30}, + {0x702c, CRL_REG_LEN_08BIT, 0xd3}, + {0x702d, CRL_REG_LEN_08BIT, 0x2e}, + {0x702e, CRL_REG_LEN_08BIT, 0x54}, + {0x702f, CRL_REG_LEN_08BIT, 0x46}, + {0x7030, CRL_REG_LEN_08BIT, 0xbc}, + {0x7031, CRL_REG_LEN_08BIT, 0x22}, + {0x7032, CRL_REG_LEN_08BIT, 0x66}, + {0x7033, CRL_REG_LEN_08BIT, 0xbc}, + {0x7034, CRL_REG_LEN_08BIT, 0x24}, + {0x7035, CRL_REG_LEN_08BIT, 0x2c}, + {0x7036, CRL_REG_LEN_08BIT, 0x28}, + {0x7037, CRL_REG_LEN_08BIT, 0xbc}, + {0x7038, CRL_REG_LEN_08BIT, 0x3c}, + {0x7039, CRL_REG_LEN_08BIT, 0xa1}, + {0x703a, CRL_REG_LEN_08BIT, 0xac}, + {0x703b, CRL_REG_LEN_08BIT, 0xd8}, + {0x703c, CRL_REG_LEN_08BIT, 0xd6}, + {0x703d, CRL_REG_LEN_08BIT, 0xb4}, + {0x703e, CRL_REG_LEN_08BIT, 0x04}, + {0x703f, CRL_REG_LEN_08BIT, 0x46}, + {0x7040, CRL_REG_LEN_08BIT, 0xb7}, + {0x7041, CRL_REG_LEN_08BIT, 0x04}, + {0x7042, CRL_REG_LEN_08BIT, 0xbe}, + {0x7043, CRL_REG_LEN_08BIT, 0x08}, + {0x7044, CRL_REG_LEN_08BIT, 0xc3}, + {0x7045, CRL_REG_LEN_08BIT, 0xd9}, + {0x7046, CRL_REG_LEN_08BIT, 0xad}, + {0x7047, CRL_REG_LEN_08BIT, 0xc3}, + {0x7048, CRL_REG_LEN_08BIT, 0xbc}, + {0x7049, CRL_REG_LEN_08BIT, 0x19}, + {0x704a, CRL_REG_LEN_08BIT, 0xc1}, + {0x704b, CRL_REG_LEN_08BIT, 0x27}, + {0x704c, CRL_REG_LEN_08BIT, 0xe7}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x704e, CRL_REG_LEN_08BIT, 0x50}, + {0x704f, CRL_REG_LEN_08BIT, 0x20}, + {0x7050, CRL_REG_LEN_08BIT, 0xb8}, + {0x7051, CRL_REG_LEN_08BIT, 0x02}, + {0x7052, CRL_REG_LEN_08BIT, 0xbc}, + {0x7053, CRL_REG_LEN_08BIT, 0x17}, + {0x7054, CRL_REG_LEN_08BIT, 0xdb}, + {0x7055, CRL_REG_LEN_08BIT, 0xc7}, + {0x7056, CRL_REG_LEN_08BIT, 0xb8}, + {0x7057, CRL_REG_LEN_08BIT, 0x00}, + {0x7058, CRL_REG_LEN_08BIT, 0x28}, + {0x7059, CRL_REG_LEN_08BIT, 0x54}, + {0x705a, CRL_REG_LEN_08BIT, 0xb4}, + {0x705b, CRL_REG_LEN_08BIT, 0x14}, + {0x705c, CRL_REG_LEN_08BIT, 0xab}, + {0x705d, CRL_REG_LEN_08BIT, 0xbe}, + {0x705e, CRL_REG_LEN_08BIT, 0x06}, + {0x705f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7060, CRL_REG_LEN_08BIT, 0xd6}, + {0x7061, CRL_REG_LEN_08BIT, 0x00}, + {0x7062, CRL_REG_LEN_08BIT, 0xb4}, + {0x7063, CRL_REG_LEN_08BIT, 0xc7}, + {0x7064, CRL_REG_LEN_08BIT, 0x07}, + {0x7065, CRL_REG_LEN_08BIT, 0xb9}, + {0x7066, CRL_REG_LEN_08BIT, 0x05}, + {0x7067, CRL_REG_LEN_08BIT, 0xee}, + {0x7068, CRL_REG_LEN_08BIT, 0xe6}, + {0x7069, CRL_REG_LEN_08BIT, 0xad}, + {0x706a, CRL_REG_LEN_08BIT, 0xb4}, + {0x706b, CRL_REG_LEN_08BIT, 0x26}, + {0x706c, CRL_REG_LEN_08BIT, 0x19}, + {0x706d, CRL_REG_LEN_08BIT, 0xc1}, + {0x706e, CRL_REG_LEN_08BIT, 0x3a}, + {0x706f, CRL_REG_LEN_08BIT, 0xc3}, + {0x7070, CRL_REG_LEN_08BIT, 0xaf}, + {0x7071, CRL_REG_LEN_08BIT, 0x00}, + {0x7072, CRL_REG_LEN_08BIT, 0xc0}, + {0x7073, CRL_REG_LEN_08BIT, 0x3c}, + {0x7074, CRL_REG_LEN_08BIT, 0xc3}, + {0x7075, CRL_REG_LEN_08BIT, 0xbe}, + {0x7076, CRL_REG_LEN_08BIT, 0xe7}, + {0x7077, CRL_REG_LEN_08BIT, 0x00}, + {0x7078, CRL_REG_LEN_08BIT, 0x15}, + {0x7079, CRL_REG_LEN_08BIT, 0xc2}, + {0x707a, CRL_REG_LEN_08BIT, 0x40}, + {0x707b, CRL_REG_LEN_08BIT, 0xc3}, + {0x707c, CRL_REG_LEN_08BIT, 0xa4}, + {0x707d, CRL_REG_LEN_08BIT, 0xc0}, + {0x707e, CRL_REG_LEN_08BIT, 0x3c}, + {0x707f, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0xb9}, + {0x7081, CRL_REG_LEN_08BIT, 0x64}, + {0x7082, CRL_REG_LEN_08BIT, 0x29}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7084, CRL_REG_LEN_08BIT, 0xb8}, + {0x7085, CRL_REG_LEN_08BIT, 0x12}, + {0x7086, CRL_REG_LEN_08BIT, 0xbe}, + {0x7087, CRL_REG_LEN_08BIT, 0x01}, + {0x7088, CRL_REG_LEN_08BIT, 0xd0}, + {0x7089, CRL_REG_LEN_08BIT, 0xbc}, + {0x708a, CRL_REG_LEN_08BIT, 0x01}, + {0x708b, CRL_REG_LEN_08BIT, 0xac}, + {0x708c, CRL_REG_LEN_08BIT, 0x37}, + {0x708d, CRL_REG_LEN_08BIT, 0xd2}, + {0x708e, CRL_REG_LEN_08BIT, 0xac}, + {0x708f, CRL_REG_LEN_08BIT, 0x45}, + {0x7090, CRL_REG_LEN_08BIT, 0xad}, + {0x7091, CRL_REG_LEN_08BIT, 0x28}, + {0x7092, CRL_REG_LEN_08BIT, 0x00}, + {0x7093, CRL_REG_LEN_08BIT, 0xb8}, + {0x7094, CRL_REG_LEN_08BIT, 0x00}, + {0x7095, CRL_REG_LEN_08BIT, 0xbc}, + {0x7096, CRL_REG_LEN_08BIT, 0x01}, + {0x7097, CRL_REG_LEN_08BIT, 0x36}, + {0x7098, CRL_REG_LEN_08BIT, 0xd3}, + {0x7099, CRL_REG_LEN_08BIT, 0x30}, + {0x709a, CRL_REG_LEN_08BIT, 0x04}, + {0x709b, CRL_REG_LEN_08BIT, 0xe0}, + {0x709c, CRL_REG_LEN_08BIT, 0xd8}, + {0x709d, CRL_REG_LEN_08BIT, 0xb4}, + {0x709e, CRL_REG_LEN_08BIT, 0xe9}, + {0x709f, CRL_REG_LEN_08BIT, 0x00}, + {0x70a0, CRL_REG_LEN_08BIT, 0xbe}, + {0x70a1, CRL_REG_LEN_08BIT, 0x05}, + {0x70a2, CRL_REG_LEN_08BIT, 0x62}, + {0x70a3, CRL_REG_LEN_08BIT, 0x07}, + {0x70a4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70a5, CRL_REG_LEN_08BIT, 0x05}, + {0x70a6, CRL_REG_LEN_08BIT, 0xad}, + {0x70a7, CRL_REG_LEN_08BIT, 0xc3}, + {0x70a8, CRL_REG_LEN_08BIT, 0xcf}, + {0x70a9, CRL_REG_LEN_08BIT, 0x00}, + {0x70aa, CRL_REG_LEN_08BIT, 0x15}, + {0x70ab, CRL_REG_LEN_08BIT, 0xc2}, + {0x70ac, CRL_REG_LEN_08BIT, 0x59}, + {0x70ad, CRL_REG_LEN_08BIT, 0xc3}, + {0x70ae, CRL_REG_LEN_08BIT, 0xc9}, + {0x70af, CRL_REG_LEN_08BIT, 0xc0}, + {0x70b0, CRL_REG_LEN_08BIT, 0x55}, + {0x70b1, CRL_REG_LEN_08BIT, 0x00}, + {0x70b2, CRL_REG_LEN_08BIT, 0x46}, + {0x70b3, CRL_REG_LEN_08BIT, 0xa1}, + {0x70b4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70b5, CRL_REG_LEN_08BIT, 0x64}, + {0x70b6, CRL_REG_LEN_08BIT, 0x29}, + {0x70b7, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70b9, CRL_REG_LEN_08BIT, 0x02}, + {0x70ba, CRL_REG_LEN_08BIT, 0xbe}, + {0x70bb, CRL_REG_LEN_08BIT, 0x02}, + {0x70bc, CRL_REG_LEN_08BIT, 0xd0}, + {0x70bd, CRL_REG_LEN_08BIT, 0xdc}, + {0x70be, CRL_REG_LEN_08BIT, 0xac}, + {0x70bf, CRL_REG_LEN_08BIT, 0xbc}, + {0x70c0, CRL_REG_LEN_08BIT, 0x01}, + {0x70c1, CRL_REG_LEN_08BIT, 0x37}, + {0x70c2, CRL_REG_LEN_08BIT, 0xac}, + {0x70c3, CRL_REG_LEN_08BIT, 0xd2}, + {0x70c4, CRL_REG_LEN_08BIT, 0x45}, + {0x70c5, CRL_REG_LEN_08BIT, 0xad}, + {0x70c6, CRL_REG_LEN_08BIT, 0x28}, + {0x70c7, CRL_REG_LEN_08BIT, 0x00}, + {0x70c8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70c9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ca, CRL_REG_LEN_08BIT, 0xbc}, + {0x70cb, CRL_REG_LEN_08BIT, 0x01}, + {0x70cc, CRL_REG_LEN_08BIT, 0x36}, + {0x70cd, CRL_REG_LEN_08BIT, 0x30}, + {0x70ce, CRL_REG_LEN_08BIT, 0xe0}, + {0x70cf, CRL_REG_LEN_08BIT, 0xd8}, + {0x70d0, CRL_REG_LEN_08BIT, 0xb5}, + {0x70d1, CRL_REG_LEN_08BIT, 0x0b}, + {0x70d2, CRL_REG_LEN_08BIT, 0xd6}, + {0x70d3, CRL_REG_LEN_08BIT, 0xbe}, + {0x70d4, CRL_REG_LEN_08BIT, 0x07}, + {0x70d5, CRL_REG_LEN_08BIT, 0x00}, + {0x70d6, CRL_REG_LEN_08BIT, 0x62}, + {0x70d7, CRL_REG_LEN_08BIT, 0x07}, + {0x70d8, CRL_REG_LEN_08BIT, 0xb9}, + {0x70d9, CRL_REG_LEN_08BIT, 0x05}, + {0x70da, CRL_REG_LEN_08BIT, 0xad}, + {0x70db, CRL_REG_LEN_08BIT, 0xc3}, + {0x70dc, CRL_REG_LEN_08BIT, 0xcf}, + {0x70dd, CRL_REG_LEN_08BIT, 0x46}, + {0x70de, CRL_REG_LEN_08BIT, 0xcd}, + {0x70df, CRL_REG_LEN_08BIT, 0x07}, + {0x70e0, CRL_REG_LEN_08BIT, 0xcd}, + {0x70e1, CRL_REG_LEN_08BIT, 0x00}, + {0x70e2, CRL_REG_LEN_08BIT, 0xe3}, + {0x70e3, CRL_REG_LEN_08BIT, 0x18}, + {0x70e4, CRL_REG_LEN_08BIT, 0xc2}, + {0x70e5, CRL_REG_LEN_08BIT, 0xa2}, + {0x70e6, CRL_REG_LEN_08BIT, 0xb9}, + {0x70e7, CRL_REG_LEN_08BIT, 0x64}, + {0x70e8, CRL_REG_LEN_08BIT, 0xd1}, + {0x70e9, CRL_REG_LEN_08BIT, 0xdd}, + {0x70ea, CRL_REG_LEN_08BIT, 0xac}, + {0x70eb, CRL_REG_LEN_08BIT, 0xcf}, + {0x70ec, CRL_REG_LEN_08BIT, 0xdf}, + {0x70ed, CRL_REG_LEN_08BIT, 0xb5}, + {0x70ee, CRL_REG_LEN_08BIT, 0x19}, + {0x70ef, CRL_REG_LEN_08BIT, 0x46}, + {0x70f0, CRL_REG_LEN_08BIT, 0x50}, + {0x70f1, CRL_REG_LEN_08BIT, 0xb6}, + {0x70f2, CRL_REG_LEN_08BIT, 0xee}, + {0x70f3, CRL_REG_LEN_08BIT, 0xe8}, + {0x70f4, CRL_REG_LEN_08BIT, 0xe6}, + {0x70f5, CRL_REG_LEN_08BIT, 0xbc}, + {0x70f6, CRL_REG_LEN_08BIT, 0x31}, + {0x70f7, CRL_REG_LEN_08BIT, 0xe1}, + {0x70f8, CRL_REG_LEN_08BIT, 0x36}, + {0x70f9, CRL_REG_LEN_08BIT, 0x30}, + {0x70fa, CRL_REG_LEN_08BIT, 0xd3}, + {0x70fb, CRL_REG_LEN_08BIT, 0x2e}, + {0x70fc, CRL_REG_LEN_08BIT, 0x54}, + {0x70fd, CRL_REG_LEN_08BIT, 0xbd}, + {0x70fe, CRL_REG_LEN_08BIT, 0x03}, + {0x70ff, CRL_REG_LEN_08BIT, 0xec}, + {0x7100, CRL_REG_LEN_08BIT, 0x2c}, + {0x7101, CRL_REG_LEN_08BIT, 0x50}, + {0x7102, CRL_REG_LEN_08BIT, 0x20}, + {0x7103, CRL_REG_LEN_08BIT, 0x04}, + {0x7104, CRL_REG_LEN_08BIT, 0xb8}, + {0x7105, CRL_REG_LEN_08BIT, 0x02}, + {0x7106, CRL_REG_LEN_08BIT, 0xbc}, + {0x7107, CRL_REG_LEN_08BIT, 0x18}, + {0x7108, CRL_REG_LEN_08BIT, 0xc7}, + {0x7109, CRL_REG_LEN_08BIT, 0xb8}, + {0x710a, CRL_REG_LEN_08BIT, 0x00}, + {0x710b, CRL_REG_LEN_08BIT, 0x28}, + {0x710c, CRL_REG_LEN_08BIT, 0x54}, + {0x710d, CRL_REG_LEN_08BIT, 0xbc}, + {0x710e, CRL_REG_LEN_08BIT, 0x02}, + {0x710f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7110, CRL_REG_LEN_08BIT, 0xda}, + {0x7111, CRL_REG_LEN_08BIT, 0xbe}, + {0x7112, CRL_REG_LEN_08BIT, 0x04}, + {0x7113, CRL_REG_LEN_08BIT, 0xd6}, + {0x7114, CRL_REG_LEN_08BIT, 0xd8}, + {0x7115, CRL_REG_LEN_08BIT, 0xab}, + {0x7116, CRL_REG_LEN_08BIT, 0x00}, + {0x7117, CRL_REG_LEN_08BIT, 0x62}, + {0x7118, CRL_REG_LEN_08BIT, 0x07}, + {0x7119, CRL_REG_LEN_08BIT, 0xb9}, + {0x711a, CRL_REG_LEN_08BIT, 0x05}, + {0x711b, CRL_REG_LEN_08BIT, 0xad}, + {0x711c, CRL_REG_LEN_08BIT, 0xc3}, + {0x711d, CRL_REG_LEN_08BIT, 0xbc}, + {0x711e, CRL_REG_LEN_08BIT, 0xe7}, + {0x711f, CRL_REG_LEN_08BIT, 0xb9}, + {0x7120, CRL_REG_LEN_08BIT, 0x64}, + {0x7121, CRL_REG_LEN_08BIT, 0x29}, + {0x7122, CRL_REG_LEN_08BIT, 0x00}, + {0x7123, CRL_REG_LEN_08BIT, 0xb8}, + {0x7124, CRL_REG_LEN_08BIT, 0x02}, + {0x7125, CRL_REG_LEN_08BIT, 0xbe}, + {0x7126, CRL_REG_LEN_08BIT, 0x00}, + {0x7127, CRL_REG_LEN_08BIT, 0x45}, + {0x7128, CRL_REG_LEN_08BIT, 0xad}, + {0x7129, CRL_REG_LEN_08BIT, 0xe2}, + {0x712a, CRL_REG_LEN_08BIT, 0x28}, + {0x712b, CRL_REG_LEN_08BIT, 0x00}, + {0x712c, CRL_REG_LEN_08BIT, 0xb8}, + {0x712d, CRL_REG_LEN_08BIT, 0x00}, + {0x712e, CRL_REG_LEN_08BIT, 0xe0}, + {0x712f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7130, CRL_REG_LEN_08BIT, 0xb4}, + {0x7131, CRL_REG_LEN_08BIT, 0xe9}, + {0x7132, CRL_REG_LEN_08BIT, 0xbe}, + {0x7133, CRL_REG_LEN_08BIT, 0x03}, + {0x7134, CRL_REG_LEN_08BIT, 0x00}, + {0x7135, CRL_REG_LEN_08BIT, 0x30}, + {0x7136, CRL_REG_LEN_08BIT, 0x62}, + {0x7137, CRL_REG_LEN_08BIT, 0x07}, + {0x7138, CRL_REG_LEN_08BIT, 0xb9}, + {0x7139, CRL_REG_LEN_08BIT, 0x05}, + {0x713a, CRL_REG_LEN_08BIT, 0xad}, + {0x713b, CRL_REG_LEN_08BIT, 0xc3}, + {0x713c, CRL_REG_LEN_08BIT, 0xcf}, + {0x713d, CRL_REG_LEN_08BIT, 0x42}, + {0x713e, CRL_REG_LEN_08BIT, 0xe4}, + {0x713f, CRL_REG_LEN_08BIT, 0xcd}, + {0x7140, CRL_REG_LEN_08BIT, 0x07}, + {0x7141, CRL_REG_LEN_08BIT, 0xcd}, + {0x7142, CRL_REG_LEN_08BIT, 0x00}, + {0x7143, CRL_REG_LEN_08BIT, 0x00}, + {0x7144, CRL_REG_LEN_08BIT, 0x17}, + {0x7145, CRL_REG_LEN_08BIT, 0xc2}, + {0x7146, CRL_REG_LEN_08BIT, 0xbb}, + {0x7147, CRL_REG_LEN_08BIT, 0xde}, + {0x7148, CRL_REG_LEN_08BIT, 0xcf}, + {0x7149, CRL_REG_LEN_08BIT, 0xdf}, + {0x714a, CRL_REG_LEN_08BIT, 0xac}, + {0x714b, CRL_REG_LEN_08BIT, 0xd1}, + {0x714c, CRL_REG_LEN_08BIT, 0x44}, + {0x714d, CRL_REG_LEN_08BIT, 0xac}, + {0x714e, CRL_REG_LEN_08BIT, 0xb9}, + {0x714f, CRL_REG_LEN_08BIT, 0x76}, + {0x7150, CRL_REG_LEN_08BIT, 0xb8}, + {0x7151, CRL_REG_LEN_08BIT, 0x08}, + {0x7152, CRL_REG_LEN_08BIT, 0xb6}, + {0x7153, CRL_REG_LEN_08BIT, 0xfe}, + {0x7154, CRL_REG_LEN_08BIT, 0xb4}, + {0x7155, CRL_REG_LEN_08BIT, 0xca}, + {0x7156, CRL_REG_LEN_08BIT, 0xd6}, + {0x7157, CRL_REG_LEN_08BIT, 0xd8}, + {0x7158, CRL_REG_LEN_08BIT, 0xab}, + {0x7159, CRL_REG_LEN_08BIT, 0x00}, + {0x715a, CRL_REG_LEN_08BIT, 0xe1}, + {0x715b, CRL_REG_LEN_08BIT, 0x36}, + {0x715c, CRL_REG_LEN_08BIT, 0x30}, + {0x715d, CRL_REG_LEN_08BIT, 0xd3}, + {0x715e, CRL_REG_LEN_08BIT, 0xbc}, + {0x715f, CRL_REG_LEN_08BIT, 0x29}, + {0x7160, CRL_REG_LEN_08BIT, 0xb4}, + {0x7161, CRL_REG_LEN_08BIT, 0x1f}, + {0x7162, CRL_REG_LEN_08BIT, 0xaa}, + {0x7163, CRL_REG_LEN_08BIT, 0xbd}, + {0x7164, CRL_REG_LEN_08BIT, 0x01}, + {0x7165, CRL_REG_LEN_08BIT, 0xb8}, + {0x7166, CRL_REG_LEN_08BIT, 0x0c}, + {0x7167, CRL_REG_LEN_08BIT, 0x45}, + {0x7168, CRL_REG_LEN_08BIT, 0xa4}, + {0x7169, CRL_REG_LEN_08BIT, 0xbd}, + {0x716a, CRL_REG_LEN_08BIT, 0x03}, + {0x716b, CRL_REG_LEN_08BIT, 0xec}, + {0x716c, CRL_REG_LEN_08BIT, 0xbc}, + {0x716d, CRL_REG_LEN_08BIT, 0x3d}, + {0x716e, CRL_REG_LEN_08BIT, 0xc3}, + {0x716f, CRL_REG_LEN_08BIT, 0xcf}, + {0x7170, CRL_REG_LEN_08BIT, 0x42}, + {0x7171, CRL_REG_LEN_08BIT, 0xb8}, + {0x7172, CRL_REG_LEN_08BIT, 0x00}, + {0x7173, CRL_REG_LEN_08BIT, 0xe4}, + {0x7174, CRL_REG_LEN_08BIT, 0xd5}, + {0x7175, CRL_REG_LEN_08BIT, 0x00}, + {0x7176, CRL_REG_LEN_08BIT, 0xb6}, + {0x7177, CRL_REG_LEN_08BIT, 0x00}, + {0x7178, CRL_REG_LEN_08BIT, 0x74}, + {0x7179, CRL_REG_LEN_08BIT, 0xbd}, + {0x717a, CRL_REG_LEN_08BIT, 0x03}, + {0x717b, CRL_REG_LEN_08BIT, 0xb5}, + {0x717c, CRL_REG_LEN_08BIT, 0x39}, + {0x717d, CRL_REG_LEN_08BIT, 0x40}, + {0x717e, CRL_REG_LEN_08BIT, 0x58}, + {0x717f, CRL_REG_LEN_08BIT, 0xdd}, + {0x7180, CRL_REG_LEN_08BIT, 0x19}, + {0x7181, CRL_REG_LEN_08BIT, 0xc1}, + {0x7182, CRL_REG_LEN_08BIT, 0xc8}, + {0x7183, CRL_REG_LEN_08BIT, 0xbd}, + {0x7184, CRL_REG_LEN_08BIT, 0x06}, + {0x7185, CRL_REG_LEN_08BIT, 0x17}, + {0x7186, CRL_REG_LEN_08BIT, 0xc1}, + {0x7187, CRL_REG_LEN_08BIT, 0xc6}, + {0x7188, CRL_REG_LEN_08BIT, 0xe8}, + {0x7189, CRL_REG_LEN_08BIT, 0x00}, + {0x718a, CRL_REG_LEN_08BIT, 0xc0}, + {0x718b, CRL_REG_LEN_08BIT, 0xc8}, + {0x718c, CRL_REG_LEN_08BIT, 0xe6}, + {0x718d, CRL_REG_LEN_08BIT, 0x95}, + {0x718e, CRL_REG_LEN_08BIT, 0x15}, + {0x718f, CRL_REG_LEN_08BIT, 0x00}, + {0x7190, CRL_REG_LEN_08BIT, 0xbc}, + {0x7191, CRL_REG_LEN_08BIT, 0x19}, + {0x7192, CRL_REG_LEN_08BIT, 0xb9}, + {0x7193, CRL_REG_LEN_08BIT, 0xf6}, + {0x7194, CRL_REG_LEN_08BIT, 0x14}, + {0x7195, CRL_REG_LEN_08BIT, 0xc1}, + {0x7196, CRL_REG_LEN_08BIT, 0xd0}, + {0x7197, CRL_REG_LEN_08BIT, 0xd1}, + {0x7198, CRL_REG_LEN_08BIT, 0xac}, + {0x7199, CRL_REG_LEN_08BIT, 0x37}, + {0x719a, CRL_REG_LEN_08BIT, 0xbc}, + {0x719b, CRL_REG_LEN_08BIT, 0x35}, + {0x719c, CRL_REG_LEN_08BIT, 0x36}, + {0x719d, CRL_REG_LEN_08BIT, 0x30}, + {0x719e, CRL_REG_LEN_08BIT, 0xe1}, + {0x719f, CRL_REG_LEN_08BIT, 0xd3}, + {0x71a0, CRL_REG_LEN_08BIT, 0x7a}, + {0x71a1, CRL_REG_LEN_08BIT, 0xb6}, + {0x71a2, CRL_REG_LEN_08BIT, 0x0c}, + {0x71a3, CRL_REG_LEN_08BIT, 0xff}, + {0x71a4, CRL_REG_LEN_08BIT, 0xb4}, + {0x71a5, CRL_REG_LEN_08BIT, 0xc7}, + {0x71a6, CRL_REG_LEN_08BIT, 0xd9}, + {0x71a7, CRL_REG_LEN_08BIT, 0x00}, + {0x71a8, CRL_REG_LEN_08BIT, 0xbd}, + {0x71a9, CRL_REG_LEN_08BIT, 0x01}, + {0x71aa, CRL_REG_LEN_08BIT, 0x56}, + {0x71ab, CRL_REG_LEN_08BIT, 0xc0}, + {0x71ac, CRL_REG_LEN_08BIT, 0xda}, + {0x71ad, CRL_REG_LEN_08BIT, 0xb4}, + {0x71ae, CRL_REG_LEN_08BIT, 0x1f}, + {0x71af, CRL_REG_LEN_08BIT, 0x56}, + {0x71b0, CRL_REG_LEN_08BIT, 0xaa}, + {0x71b1, CRL_REG_LEN_08BIT, 0xbc}, + {0x71b2, CRL_REG_LEN_08BIT, 0x08}, + {0x71b3, CRL_REG_LEN_08BIT, 0x00}, + {0x71b4, CRL_REG_LEN_08BIT, 0x57}, + {0x71b5, CRL_REG_LEN_08BIT, 0xe8}, + {0x71b6, CRL_REG_LEN_08BIT, 0xb5}, + {0x71b7, CRL_REG_LEN_08BIT, 0x36}, + {0x71b8, CRL_REG_LEN_08BIT, 0x00}, + {0x71b9, CRL_REG_LEN_08BIT, 0x54}, + {0x71ba, CRL_REG_LEN_08BIT, 0xe7}, + {0x71bb, CRL_REG_LEN_08BIT, 0xc8}, + {0x71bc, CRL_REG_LEN_08BIT, 0xb4}, + {0x71bd, CRL_REG_LEN_08BIT, 0x1f}, + {0x71be, CRL_REG_LEN_08BIT, 0x56}, + {0x71bf, CRL_REG_LEN_08BIT, 0xaa}, + {0x71c0, CRL_REG_LEN_08BIT, 0xbc}, + {0x71c1, CRL_REG_LEN_08BIT, 0x08}, + {0x71c2, CRL_REG_LEN_08BIT, 0x57}, + {0x71c3, CRL_REG_LEN_08BIT, 0x00}, + {0x71c4, CRL_REG_LEN_08BIT, 0xb5}, + {0x71c5, CRL_REG_LEN_08BIT, 0x36}, + {0x71c6, CRL_REG_LEN_08BIT, 0x00}, + {0x71c7, CRL_REG_LEN_08BIT, 0x54}, + {0x71c8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71c9, CRL_REG_LEN_08BIT, 0xb5}, + {0x71ca, CRL_REG_LEN_08BIT, 0x18}, + {0x71cb, CRL_REG_LEN_08BIT, 0xd9}, + {0x71cc, CRL_REG_LEN_08BIT, 0x00}, + {0x71cd, CRL_REG_LEN_08BIT, 0xbd}, + {0x71ce, CRL_REG_LEN_08BIT, 0x01}, + {0x71cf, CRL_REG_LEN_08BIT, 0x56}, + {0x71d0, CRL_REG_LEN_08BIT, 0x08}, + {0x71d1, CRL_REG_LEN_08BIT, 0x57}, + {0x71d2, CRL_REG_LEN_08BIT, 0xe8}, + {0x71d3, CRL_REG_LEN_08BIT, 0xb4}, + {0x71d4, CRL_REG_LEN_08BIT, 0x42}, + {0x71d5, CRL_REG_LEN_08BIT, 0x00}, + {0x71d6, CRL_REG_LEN_08BIT, 0x54}, + {0x71d7, CRL_REG_LEN_08BIT, 0xe7}, + {0x71d8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71d9, CRL_REG_LEN_08BIT, 0xab}, + {0x71da, CRL_REG_LEN_08BIT, 0x00}, + {0x71db, CRL_REG_LEN_08BIT, 0x66}, + {0x71dc, CRL_REG_LEN_08BIT, 0x62}, + {0x71dd, CRL_REG_LEN_08BIT, 0x06}, + {0x71de, CRL_REG_LEN_08BIT, 0x74}, + {0x71df, CRL_REG_LEN_08BIT, 0xb9}, + {0x71e0, CRL_REG_LEN_08BIT, 0x05}, + {0x71e1, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e2, CRL_REG_LEN_08BIT, 0x14}, + {0x71e3, CRL_REG_LEN_08BIT, 0x0e}, + {0x71e4, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e5, CRL_REG_LEN_08BIT, 0x04}, + {0x71e6, CRL_REG_LEN_08BIT, 0xc8}, + {0x7600, CRL_REG_LEN_08BIT, 0x04}, + {0x7601, CRL_REG_LEN_08BIT, 0x80}, + {0x7602, CRL_REG_LEN_08BIT, 0x07}, + {0x7603, CRL_REG_LEN_08BIT, 0x44}, + {0x7604, CRL_REG_LEN_08BIT, 0x05}, + {0x7605, CRL_REG_LEN_08BIT, 0x33}, + {0x7606, CRL_REG_LEN_08BIT, 0x0f}, + {0x7607, CRL_REG_LEN_08BIT, 0x00}, + {0x7608, CRL_REG_LEN_08BIT, 0x07}, + {0x7609, CRL_REG_LEN_08BIT, 0x40}, + {0x760a, CRL_REG_LEN_08BIT, 0x04}, + {0x760b, CRL_REG_LEN_08BIT, 0xe5}, + {0x760c, CRL_REG_LEN_08BIT, 0x06}, + {0x760d, CRL_REG_LEN_08BIT, 0x50}, + {0x760e, CRL_REG_LEN_08BIT, 0x04}, + {0x760f, CRL_REG_LEN_08BIT, 0xe4}, + {0x7610, CRL_REG_LEN_08BIT, 0x00}, + {0x7611, CRL_REG_LEN_08BIT, 0x00}, + {0x7612, CRL_REG_LEN_08BIT, 0x06}, + {0x7613, CRL_REG_LEN_08BIT, 0x5c}, + {0x7614, CRL_REG_LEN_08BIT, 0x00}, + {0x7615, CRL_REG_LEN_08BIT, 0x0f}, + {0x7616, CRL_REG_LEN_08BIT, 0x06}, + {0x7617, CRL_REG_LEN_08BIT, 0x1c}, + {0x7618, CRL_REG_LEN_08BIT, 0x00}, + {0x7619, CRL_REG_LEN_08BIT, 0x02}, + {0x761a, CRL_REG_LEN_08BIT, 0x06}, + {0x761b, CRL_REG_LEN_08BIT, 0xa2}, + {0x761c, CRL_REG_LEN_08BIT, 0x00}, + {0x761d, CRL_REG_LEN_08BIT, 0x01}, + {0x761e, CRL_REG_LEN_08BIT, 0x06}, + {0x761f, CRL_REG_LEN_08BIT, 0xae}, + {0x7620, CRL_REG_LEN_08BIT, 0x00}, + {0x7621, CRL_REG_LEN_08BIT, 0x0e}, + {0x7622, CRL_REG_LEN_08BIT, 0x05}, + {0x7623, CRL_REG_LEN_08BIT, 0x30}, + {0x7624, CRL_REG_LEN_08BIT, 0x07}, + {0x7625, CRL_REG_LEN_08BIT, 0x00}, + {0x7626, CRL_REG_LEN_08BIT, 0x0f}, + {0x7627, CRL_REG_LEN_08BIT, 0x00}, + {0x7628, CRL_REG_LEN_08BIT, 0x04}, + {0x7629, CRL_REG_LEN_08BIT, 0xe5}, + {0x762a, CRL_REG_LEN_08BIT, 0x05}, + {0x762b, CRL_REG_LEN_08BIT, 0x33}, + {0x762c, CRL_REG_LEN_08BIT, 0x06}, + {0x762d, CRL_REG_LEN_08BIT, 0x12}, + {0x762e, CRL_REG_LEN_08BIT, 0x00}, + {0x762f, CRL_REG_LEN_08BIT, 0x01}, + {0x7630, CRL_REG_LEN_08BIT, 0x06}, + {0x7631, CRL_REG_LEN_08BIT, 0x52}, + {0x7632, CRL_REG_LEN_08BIT, 0x00}, + {0x7633, CRL_REG_LEN_08BIT, 0x01}, + {0x7634, CRL_REG_LEN_08BIT, 0x06}, + {0x7635, CRL_REG_LEN_08BIT, 0x5e}, + {0x7636, CRL_REG_LEN_08BIT, 0x04}, + {0x7637, CRL_REG_LEN_08BIT, 0xe4}, + {0x7638, CRL_REG_LEN_08BIT, 0x00}, + {0x7639, CRL_REG_LEN_08BIT, 0x01}, + {0x763a, CRL_REG_LEN_08BIT, 0x05}, + {0x763b, CRL_REG_LEN_08BIT, 0x30}, + {0x763c, CRL_REG_LEN_08BIT, 0x0f}, + {0x763d, CRL_REG_LEN_08BIT, 0x00}, + {0x763e, CRL_REG_LEN_08BIT, 0x06}, + {0x763f, CRL_REG_LEN_08BIT, 0xa6}, + {0x7640, CRL_REG_LEN_08BIT, 0x00}, + {0x7641, CRL_REG_LEN_08BIT, 0x02}, + {0x7642, CRL_REG_LEN_08BIT, 0x06}, + {0x7643, CRL_REG_LEN_08BIT, 0x26}, + {0x7644, CRL_REG_LEN_08BIT, 0x00}, + {0x7645, CRL_REG_LEN_08BIT, 0x02}, + {0x7646, CRL_REG_LEN_08BIT, 0x05}, + {0x7647, CRL_REG_LEN_08BIT, 0x33}, + {0x7648, CRL_REG_LEN_08BIT, 0x06}, + {0x7649, CRL_REG_LEN_08BIT, 0x20}, + {0x764a, CRL_REG_LEN_08BIT, 0x0f}, + {0x764b, CRL_REG_LEN_08BIT, 0x00}, + {0x764c, CRL_REG_LEN_08BIT, 0x06}, + {0x764d, CRL_REG_LEN_08BIT, 0x56}, + {0x764e, CRL_REG_LEN_08BIT, 0x00}, + {0x764f, CRL_REG_LEN_08BIT, 0x02}, + {0x7650, CRL_REG_LEN_08BIT, 0x06}, + {0x7651, CRL_REG_LEN_08BIT, 0x16}, + {0x7652, CRL_REG_LEN_08BIT, 0x05}, + {0x7653, CRL_REG_LEN_08BIT, 0x33}, + {0x7654, CRL_REG_LEN_08BIT, 0x06}, + {0x7655, CRL_REG_LEN_08BIT, 0x10}, + {0x7656, CRL_REG_LEN_08BIT, 0x0f}, + {0x7657, CRL_REG_LEN_08BIT, 0x00}, + {0x7658, CRL_REG_LEN_08BIT, 0x06}, + {0x7659, CRL_REG_LEN_08BIT, 0x10}, + {0x765a, CRL_REG_LEN_08BIT, 0x0f}, + {0x765b, CRL_REG_LEN_08BIT, 0x00}, + {0x765c, CRL_REG_LEN_08BIT, 0x06}, + {0x765d, CRL_REG_LEN_08BIT, 0x20}, + {0x765e, CRL_REG_LEN_08BIT, 0x0f}, + {0x765f, CRL_REG_LEN_08BIT, 0x00}, + {0x7660, CRL_REG_LEN_08BIT, 0x00}, + {0x7661, CRL_REG_LEN_08BIT, 0x00}, + {0x7662, CRL_REG_LEN_08BIT, 0x00}, + {0x7663, CRL_REG_LEN_08BIT, 0x02}, + {0x7664, CRL_REG_LEN_08BIT, 0x04}, + {0x7665, CRL_REG_LEN_08BIT, 0xe5}, + {0x7666, CRL_REG_LEN_08BIT, 0x04}, + {0x7667, CRL_REG_LEN_08BIT, 0xe4}, + {0x7668, CRL_REG_LEN_08BIT, 0x0f}, + {0x7669, CRL_REG_LEN_08BIT, 0x00}, + {0x766a, CRL_REG_LEN_08BIT, 0x00}, + {0x766b, CRL_REG_LEN_08BIT, 0x00}, + {0x766c, CRL_REG_LEN_08BIT, 0x00}, + {0x766d, CRL_REG_LEN_08BIT, 0x01}, + {0x766e, CRL_REG_LEN_08BIT, 0x04}, + {0x766f, CRL_REG_LEN_08BIT, 0xe5}, + {0x7670, CRL_REG_LEN_08BIT, 0x04}, + {0x7671, CRL_REG_LEN_08BIT, 0xe4}, + {0x7672, CRL_REG_LEN_08BIT, 0x0f}, + {0x7673, CRL_REG_LEN_08BIT, 0x00}, + {0x7674, CRL_REG_LEN_08BIT, 0x00}, + {0x7675, CRL_REG_LEN_08BIT, 0x02}, + {0x7676, CRL_REG_LEN_08BIT, 0x04}, + {0x7677, CRL_REG_LEN_08BIT, 0xe4}, + {0x7678, CRL_REG_LEN_08BIT, 0x00}, + {0x7679, CRL_REG_LEN_08BIT, 0x02}, + {0x767a, CRL_REG_LEN_08BIT, 0x04}, + {0x767b, CRL_REG_LEN_08BIT, 0xc4}, + {0x767c, CRL_REG_LEN_08BIT, 0x00}, + {0x767d, CRL_REG_LEN_08BIT, 0x02}, + {0x767e, CRL_REG_LEN_08BIT, 0x04}, + {0x767f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7680, CRL_REG_LEN_08BIT, 0x05}, + {0x7681, CRL_REG_LEN_08BIT, 0x83}, + {0x7682, CRL_REG_LEN_08BIT, 0x0f}, + {0x7683, CRL_REG_LEN_08BIT, 0x00}, + {0x7684, CRL_REG_LEN_08BIT, 0x00}, + {0x7685, CRL_REG_LEN_08BIT, 0x02}, + {0x7686, CRL_REG_LEN_08BIT, 0x04}, + {0x7687, CRL_REG_LEN_08BIT, 0xe4}, + {0x7688, CRL_REG_LEN_08BIT, 0x00}, + {0x7689, CRL_REG_LEN_08BIT, 0x02}, + {0x768a, CRL_REG_LEN_08BIT, 0x04}, + {0x768b, CRL_REG_LEN_08BIT, 0xc4}, + {0x768c, CRL_REG_LEN_08BIT, 0x00}, + {0x768d, CRL_REG_LEN_08BIT, 0x02}, + {0x768e, CRL_REG_LEN_08BIT, 0x04}, + {0x768f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7690, CRL_REG_LEN_08BIT, 0x05}, + {0x7691, CRL_REG_LEN_08BIT, 0x83}, + {0x7692, CRL_REG_LEN_08BIT, 0x03}, + {0x7693, CRL_REG_LEN_08BIT, 0x0b}, + {0x7694, CRL_REG_LEN_08BIT, 0x05}, + {0x7695, CRL_REG_LEN_08BIT, 0x83}, + {0x7696, CRL_REG_LEN_08BIT, 0x00}, + {0x7697, CRL_REG_LEN_08BIT, 0x07}, + {0x7698, CRL_REG_LEN_08BIT, 0x05}, + {0x7699, CRL_REG_LEN_08BIT, 0x03}, + {0x769a, CRL_REG_LEN_08BIT, 0x00}, + {0x769b, CRL_REG_LEN_08BIT, 0x05}, + {0x769c, CRL_REG_LEN_08BIT, 0x05}, + {0x769d, CRL_REG_LEN_08BIT, 0x32}, + {0x769e, CRL_REG_LEN_08BIT, 0x05}, + {0x769f, CRL_REG_LEN_08BIT, 0x30}, + {0x76a0, CRL_REG_LEN_08BIT, 0x00}, + {0x76a1, CRL_REG_LEN_08BIT, 0x02}, + {0x76a2, CRL_REG_LEN_08BIT, 0x05}, + {0x76a3, CRL_REG_LEN_08BIT, 0x78}, + {0x76a4, CRL_REG_LEN_08BIT, 0x00}, + {0x76a5, CRL_REG_LEN_08BIT, 0x01}, + {0x76a6, CRL_REG_LEN_08BIT, 0x05}, + {0x76a7, CRL_REG_LEN_08BIT, 0x7c}, + {0x76a8, CRL_REG_LEN_08BIT, 0x03}, + {0x76a9, CRL_REG_LEN_08BIT, 0x9a}, + {0x76aa, CRL_REG_LEN_08BIT, 0x05}, + {0x76ab, CRL_REG_LEN_08BIT, 0x83}, + {0x76ac, CRL_REG_LEN_08BIT, 0x00}, + {0x76ad, CRL_REG_LEN_08BIT, 0x04}, + {0x76ae, CRL_REG_LEN_08BIT, 0x05}, + {0x76af, CRL_REG_LEN_08BIT, 0x03}, + {0x76b0, CRL_REG_LEN_08BIT, 0x00}, + {0x76b1, CRL_REG_LEN_08BIT, 0x03}, + {0x76b2, CRL_REG_LEN_08BIT, 0x05}, + {0x76b3, CRL_REG_LEN_08BIT, 0x32}, + {0x76b4, CRL_REG_LEN_08BIT, 0x05}, + {0x76b5, CRL_REG_LEN_08BIT, 0x30}, + {0x76b6, CRL_REG_LEN_08BIT, 0x00}, + {0x76b7, CRL_REG_LEN_08BIT, 0x02}, + {0x76b8, CRL_REG_LEN_08BIT, 0x05}, + {0x76b9, CRL_REG_LEN_08BIT, 0x78}, + {0x76ba, CRL_REG_LEN_08BIT, 0x00}, + {0x76bb, CRL_REG_LEN_08BIT, 0x01}, + {0x76bc, CRL_REG_LEN_08BIT, 0x05}, + {0x76bd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76be, CRL_REG_LEN_08BIT, 0x03}, + {0x76bf, CRL_REG_LEN_08BIT, 0x99}, + {0x76c0, CRL_REG_LEN_08BIT, 0x05}, + {0x76c1, CRL_REG_LEN_08BIT, 0x83}, + {0x76c2, CRL_REG_LEN_08BIT, 0x00}, + {0x76c3, CRL_REG_LEN_08BIT, 0x03}, + {0x76c4, CRL_REG_LEN_08BIT, 0x05}, + {0x76c5, CRL_REG_LEN_08BIT, 0x03}, + {0x76c6, CRL_REG_LEN_08BIT, 0x00}, + {0x76c7, CRL_REG_LEN_08BIT, 0x01}, + {0x76c8, CRL_REG_LEN_08BIT, 0x05}, + {0x76c9, CRL_REG_LEN_08BIT, 0x32}, + {0x76ca, CRL_REG_LEN_08BIT, 0x05}, + {0x76cb, CRL_REG_LEN_08BIT, 0x30}, + {0x76cc, CRL_REG_LEN_08BIT, 0x00}, + {0x76cd, CRL_REG_LEN_08BIT, 0x02}, + {0x76ce, CRL_REG_LEN_08BIT, 0x05}, + {0x76cf, CRL_REG_LEN_08BIT, 0x78}, + {0x76d0, CRL_REG_LEN_08BIT, 0x00}, + {0x76d1, CRL_REG_LEN_08BIT, 0x01}, + {0x76d2, CRL_REG_LEN_08BIT, 0x05}, + {0x76d3, CRL_REG_LEN_08BIT, 0x7c}, + {0x76d4, CRL_REG_LEN_08BIT, 0x03}, + {0x76d5, CRL_REG_LEN_08BIT, 0x98}, + {0x76d6, CRL_REG_LEN_08BIT, 0x05}, + {0x76d7, CRL_REG_LEN_08BIT, 0x83}, + {0x76d8, CRL_REG_LEN_08BIT, 0x00}, + {0x76d9, CRL_REG_LEN_08BIT, 0x00}, + {0x76da, CRL_REG_LEN_08BIT, 0x05}, + {0x76db, CRL_REG_LEN_08BIT, 0x03}, + {0x76dc, CRL_REG_LEN_08BIT, 0x00}, + {0x76dd, CRL_REG_LEN_08BIT, 0x01}, + {0x76de, CRL_REG_LEN_08BIT, 0x05}, + {0x76df, CRL_REG_LEN_08BIT, 0x32}, + {0x76e0, CRL_REG_LEN_08BIT, 0x05}, + {0x76e1, CRL_REG_LEN_08BIT, 0x30}, + {0x76e2, CRL_REG_LEN_08BIT, 0x00}, + {0x76e3, CRL_REG_LEN_08BIT, 0x02}, + {0x76e4, CRL_REG_LEN_08BIT, 0x05}, + {0x76e5, CRL_REG_LEN_08BIT, 0x78}, + {0x76e6, CRL_REG_LEN_08BIT, 0x00}, + {0x76e7, CRL_REG_LEN_08BIT, 0x01}, + {0x76e8, CRL_REG_LEN_08BIT, 0x05}, + {0x76e9, CRL_REG_LEN_08BIT, 0x7c}, + {0x76ea, CRL_REG_LEN_08BIT, 0x03}, + {0x76eb, CRL_REG_LEN_08BIT, 0x97}, + {0x76ec, CRL_REG_LEN_08BIT, 0x05}, + {0x76ed, CRL_REG_LEN_08BIT, 0x83}, + {0x76ee, CRL_REG_LEN_08BIT, 0x00}, + {0x76ef, CRL_REG_LEN_08BIT, 0x00}, + {0x76f0, CRL_REG_LEN_08BIT, 0x05}, + {0x76f1, CRL_REG_LEN_08BIT, 0x03}, + {0x76f2, CRL_REG_LEN_08BIT, 0x05}, + {0x76f3, CRL_REG_LEN_08BIT, 0x32}, + {0x76f4, CRL_REG_LEN_08BIT, 0x05}, + {0x76f5, CRL_REG_LEN_08BIT, 0x30}, + {0x76f6, CRL_REG_LEN_08BIT, 0x00}, + {0x76f7, CRL_REG_LEN_08BIT, 0x02}, + {0x76f8, CRL_REG_LEN_08BIT, 0x05}, + {0x76f9, CRL_REG_LEN_08BIT, 0x78}, + {0x76fa, CRL_REG_LEN_08BIT, 0x00}, + {0x76fb, CRL_REG_LEN_08BIT, 0x01}, + {0x76fc, CRL_REG_LEN_08BIT, 0x05}, + {0x76fd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76fe, CRL_REG_LEN_08BIT, 0x03}, + {0x76ff, CRL_REG_LEN_08BIT, 0x96}, + {0x7700, CRL_REG_LEN_08BIT, 0x05}, + {0x7701, CRL_REG_LEN_08BIT, 0x83}, + {0x7702, CRL_REG_LEN_08BIT, 0x05}, + {0x7703, CRL_REG_LEN_08BIT, 0x03}, + {0x7704, CRL_REG_LEN_08BIT, 0x05}, + {0x7705, CRL_REG_LEN_08BIT, 0x32}, + {0x7706, CRL_REG_LEN_08BIT, 0x05}, + {0x7707, CRL_REG_LEN_08BIT, 0x30}, + {0x7708, CRL_REG_LEN_08BIT, 0x00}, + {0x7709, CRL_REG_LEN_08BIT, 0x02}, + {0x770a, CRL_REG_LEN_08BIT, 0x05}, + {0x770b, CRL_REG_LEN_08BIT, 0x78}, + {0x770c, CRL_REG_LEN_08BIT, 0x00}, + {0x770d, CRL_REG_LEN_08BIT, 0x01}, + {0x770e, CRL_REG_LEN_08BIT, 0x05}, + {0x770f, CRL_REG_LEN_08BIT, 0x7c}, + {0x7710, CRL_REG_LEN_08BIT, 0x03}, + {0x7711, CRL_REG_LEN_08BIT, 0x95}, + {0x7712, CRL_REG_LEN_08BIT, 0x05}, + {0x7713, CRL_REG_LEN_08BIT, 0x83}, + {0x7714, CRL_REG_LEN_08BIT, 0x05}, + {0x7715, CRL_REG_LEN_08BIT, 0x03}, + {0x7716, CRL_REG_LEN_08BIT, 0x05}, + {0x7717, CRL_REG_LEN_08BIT, 0x32}, + {0x7718, CRL_REG_LEN_08BIT, 0x05}, + {0x7719, CRL_REG_LEN_08BIT, 0x30}, + {0x771a, CRL_REG_LEN_08BIT, 0x00}, + {0x771b, CRL_REG_LEN_08BIT, 0x02}, + {0x771c, CRL_REG_LEN_08BIT, 0x05}, + {0x771d, CRL_REG_LEN_08BIT, 0x78}, + {0x771e, CRL_REG_LEN_08BIT, 0x00}, + {0x771f, CRL_REG_LEN_08BIT, 0x01}, + {0x7720, CRL_REG_LEN_08BIT, 0x05}, + {0x7721, CRL_REG_LEN_08BIT, 0x7c}, + {0x7722, CRL_REG_LEN_08BIT, 0x03}, + {0x7723, CRL_REG_LEN_08BIT, 0x94}, + {0x7724, CRL_REG_LEN_08BIT, 0x05}, + {0x7725, CRL_REG_LEN_08BIT, 0x83}, + {0x7726, CRL_REG_LEN_08BIT, 0x00}, + {0x7727, CRL_REG_LEN_08BIT, 0x01}, + {0x7728, CRL_REG_LEN_08BIT, 0x05}, + {0x7729, CRL_REG_LEN_08BIT, 0x03}, + {0x772a, CRL_REG_LEN_08BIT, 0x00}, + {0x772b, CRL_REG_LEN_08BIT, 0x01}, + {0x772c, CRL_REG_LEN_08BIT, 0x05}, + {0x772d, CRL_REG_LEN_08BIT, 0x32}, + {0x772e, CRL_REG_LEN_08BIT, 0x05}, + {0x772f, CRL_REG_LEN_08BIT, 0x30}, + {0x7730, CRL_REG_LEN_08BIT, 0x00}, + {0x7731, CRL_REG_LEN_08BIT, 0x02}, + {0x7732, CRL_REG_LEN_08BIT, 0x05}, + {0x7733, CRL_REG_LEN_08BIT, 0x78}, + {0x7734, CRL_REG_LEN_08BIT, 0x00}, + {0x7735, CRL_REG_LEN_08BIT, 0x01}, + {0x7736, CRL_REG_LEN_08BIT, 0x05}, + {0x7737, CRL_REG_LEN_08BIT, 0x7c}, + {0x7738, CRL_REG_LEN_08BIT, 0x03}, + {0x7739, CRL_REG_LEN_08BIT, 0x93}, + {0x773a, CRL_REG_LEN_08BIT, 0x05}, + {0x773b, CRL_REG_LEN_08BIT, 0x83}, + {0x773c, CRL_REG_LEN_08BIT, 0x00}, + {0x773d, CRL_REG_LEN_08BIT, 0x00}, + {0x773e, CRL_REG_LEN_08BIT, 0x05}, + {0x773f, CRL_REG_LEN_08BIT, 0x03}, + {0x7740, CRL_REG_LEN_08BIT, 0x00}, + {0x7741, CRL_REG_LEN_08BIT, 0x00}, + {0x7742, CRL_REG_LEN_08BIT, 0x05}, + {0x7743, CRL_REG_LEN_08BIT, 0x32}, + {0x7744, CRL_REG_LEN_08BIT, 0x05}, + {0x7745, CRL_REG_LEN_08BIT, 0x30}, + {0x7746, CRL_REG_LEN_08BIT, 0x00}, + {0x7747, CRL_REG_LEN_08BIT, 0x02}, + {0x7748, CRL_REG_LEN_08BIT, 0x05}, + {0x7749, CRL_REG_LEN_08BIT, 0x78}, + {0x774a, CRL_REG_LEN_08BIT, 0x00}, + {0x774b, CRL_REG_LEN_08BIT, 0x01}, + {0x774c, CRL_REG_LEN_08BIT, 0x05}, + {0x774d, CRL_REG_LEN_08BIT, 0x7c}, + {0x774e, CRL_REG_LEN_08BIT, 0x03}, + {0x774f, CRL_REG_LEN_08BIT, 0x92}, + {0x7750, CRL_REG_LEN_08BIT, 0x05}, + {0x7751, CRL_REG_LEN_08BIT, 0x83}, + {0x7752, CRL_REG_LEN_08BIT, 0x05}, + {0x7753, CRL_REG_LEN_08BIT, 0x03}, + {0x7754, CRL_REG_LEN_08BIT, 0x00}, + {0x7755, CRL_REG_LEN_08BIT, 0x00}, + {0x7756, CRL_REG_LEN_08BIT, 0x05}, + {0x7757, CRL_REG_LEN_08BIT, 0x32}, + {0x7758, CRL_REG_LEN_08BIT, 0x05}, + {0x7759, CRL_REG_LEN_08BIT, 0x30}, + {0x775a, CRL_REG_LEN_08BIT, 0x00}, + {0x775b, CRL_REG_LEN_08BIT, 0x02}, + {0x775c, CRL_REG_LEN_08BIT, 0x05}, + {0x775d, CRL_REG_LEN_08BIT, 0x78}, + {0x775e, CRL_REG_LEN_08BIT, 0x00}, + {0x775f, CRL_REG_LEN_08BIT, 0x01}, + {0x7760, CRL_REG_LEN_08BIT, 0x05}, + {0x7761, CRL_REG_LEN_08BIT, 0x7c}, + {0x7762, CRL_REG_LEN_08BIT, 0x03}, + {0x7763, CRL_REG_LEN_08BIT, 0x91}, + {0x7764, CRL_REG_LEN_08BIT, 0x05}, + {0x7765, CRL_REG_LEN_08BIT, 0x83}, + {0x7766, CRL_REG_LEN_08BIT, 0x05}, + {0x7767, CRL_REG_LEN_08BIT, 0x03}, + {0x7768, CRL_REG_LEN_08BIT, 0x05}, + {0x7769, CRL_REG_LEN_08BIT, 0x32}, + {0x776a, CRL_REG_LEN_08BIT, 0x05}, + {0x776b, CRL_REG_LEN_08BIT, 0x30}, + {0x776c, CRL_REG_LEN_08BIT, 0x00}, + {0x776d, CRL_REG_LEN_08BIT, 0x02}, + {0x776e, CRL_REG_LEN_08BIT, 0x05}, + {0x776f, CRL_REG_LEN_08BIT, 0x78}, + {0x7770, CRL_REG_LEN_08BIT, 0x00}, + {0x7771, CRL_REG_LEN_08BIT, 0x01}, + {0x7772, CRL_REG_LEN_08BIT, 0x05}, + {0x7773, CRL_REG_LEN_08BIT, 0x7c}, + {0x7774, CRL_REG_LEN_08BIT, 0x03}, + {0x7775, CRL_REG_LEN_08BIT, 0x90}, + {0x7776, CRL_REG_LEN_08BIT, 0x05}, + {0x7777, CRL_REG_LEN_08BIT, 0x83}, + {0x7778, CRL_REG_LEN_08BIT, 0x05}, + {0x7779, CRL_REG_LEN_08BIT, 0x03}, + {0x777a, CRL_REG_LEN_08BIT, 0x05}, + {0x777b, CRL_REG_LEN_08BIT, 0x32}, + {0x777c, CRL_REG_LEN_08BIT, 0x05}, + {0x777d, CRL_REG_LEN_08BIT, 0x30}, + {0x777e, CRL_REG_LEN_08BIT, 0x00}, + {0x777f, CRL_REG_LEN_08BIT, 0x02}, + {0x7780, CRL_REG_LEN_08BIT, 0x05}, + {0x7781, CRL_REG_LEN_08BIT, 0x78}, + {0x7782, CRL_REG_LEN_08BIT, 0x00}, + {0x7783, CRL_REG_LEN_08BIT, 0x01}, + {0x7784, CRL_REG_LEN_08BIT, 0x05}, + {0x7785, CRL_REG_LEN_08BIT, 0x7c}, + {0x7786, CRL_REG_LEN_08BIT, 0x02}, + {0x7787, CRL_REG_LEN_08BIT, 0x90}, + {0x7788, CRL_REG_LEN_08BIT, 0x05}, + {0x7789, CRL_REG_LEN_08BIT, 0x03}, + {0x778a, CRL_REG_LEN_08BIT, 0x07}, + {0x778b, CRL_REG_LEN_08BIT, 0x00}, + {0x778c, CRL_REG_LEN_08BIT, 0x0f}, + {0x778d, CRL_REG_LEN_08BIT, 0x00}, + {0x778e, CRL_REG_LEN_08BIT, 0x08}, + {0x778f, CRL_REG_LEN_08BIT, 0x30}, + {0x7790, CRL_REG_LEN_08BIT, 0x08}, + {0x7791, CRL_REG_LEN_08BIT, 0xee}, + {0x7792, CRL_REG_LEN_08BIT, 0x0f}, + {0x7793, CRL_REG_LEN_08BIT, 0x00}, + {0x7794, CRL_REG_LEN_08BIT, 0x05}, + {0x7795, CRL_REG_LEN_08BIT, 0x33}, + {0x7796, CRL_REG_LEN_08BIT, 0x04}, + {0x7797, CRL_REG_LEN_08BIT, 0xe5}, + {0x7798, CRL_REG_LEN_08BIT, 0x06}, + {0x7799, CRL_REG_LEN_08BIT, 0x52}, + {0x779a, CRL_REG_LEN_08BIT, 0x04}, + {0x779b, CRL_REG_LEN_08BIT, 0xe4}, + {0x779c, CRL_REG_LEN_08BIT, 0x00}, + {0x779d, CRL_REG_LEN_08BIT, 0x00}, + {0x779e, CRL_REG_LEN_08BIT, 0x06}, + {0x779f, CRL_REG_LEN_08BIT, 0x5e}, + {0x77a0, CRL_REG_LEN_08BIT, 0x00}, + {0x77a1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77a2, CRL_REG_LEN_08BIT, 0x06}, + {0x77a3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77a4, CRL_REG_LEN_08BIT, 0x00}, + {0x77a5, CRL_REG_LEN_08BIT, 0x02}, + {0x77a6, CRL_REG_LEN_08BIT, 0x06}, + {0x77a7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77a8, CRL_REG_LEN_08BIT, 0x00}, + {0x77a9, CRL_REG_LEN_08BIT, 0x01}, + {0x77aa, CRL_REG_LEN_08BIT, 0x06}, + {0x77ab, CRL_REG_LEN_08BIT, 0xae}, + {0x77ac, CRL_REG_LEN_08BIT, 0x00}, + {0x77ad, CRL_REG_LEN_08BIT, 0x03}, + {0x77ae, CRL_REG_LEN_08BIT, 0x05}, + {0x77af, CRL_REG_LEN_08BIT, 0x30}, + {0x77b0, CRL_REG_LEN_08BIT, 0x09}, + {0x77b1, CRL_REG_LEN_08BIT, 0x19}, + {0x77b2, CRL_REG_LEN_08BIT, 0x0f}, + {0x77b3, CRL_REG_LEN_08BIT, 0x00}, + {0x77b4, CRL_REG_LEN_08BIT, 0x05}, + {0x77b5, CRL_REG_LEN_08BIT, 0x33}, + {0x77b6, CRL_REG_LEN_08BIT, 0x04}, + {0x77b7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77b8, CRL_REG_LEN_08BIT, 0x06}, + {0x77b9, CRL_REG_LEN_08BIT, 0x52}, + {0x77ba, CRL_REG_LEN_08BIT, 0x04}, + {0x77bb, CRL_REG_LEN_08BIT, 0xe4}, + {0x77bc, CRL_REG_LEN_08BIT, 0x00}, + {0x77bd, CRL_REG_LEN_08BIT, 0x00}, + {0x77be, CRL_REG_LEN_08BIT, 0x06}, + {0x77bf, CRL_REG_LEN_08BIT, 0x5e}, + {0x77c0, CRL_REG_LEN_08BIT, 0x00}, + {0x77c1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77c2, CRL_REG_LEN_08BIT, 0x06}, + {0x77c3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77c4, CRL_REG_LEN_08BIT, 0x00}, + {0x77c5, CRL_REG_LEN_08BIT, 0x02}, + {0x77c6, CRL_REG_LEN_08BIT, 0x06}, + {0x77c7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77c8, CRL_REG_LEN_08BIT, 0x00}, + {0x77c9, CRL_REG_LEN_08BIT, 0x01}, + {0x77ca, CRL_REG_LEN_08BIT, 0x06}, + {0x77cb, CRL_REG_LEN_08BIT, 0xae}, + {0x77cc, CRL_REG_LEN_08BIT, 0x00}, + {0x77cd, CRL_REG_LEN_08BIT, 0x03}, + {0x77ce, CRL_REG_LEN_08BIT, 0x05}, + {0x77cf, CRL_REG_LEN_08BIT, 0x30}, + {0x77d0, CRL_REG_LEN_08BIT, 0x0f}, + {0x77d1, CRL_REG_LEN_08BIT, 0x00}, + {0x77d2, CRL_REG_LEN_08BIT, 0x00}, + {0x77d3, CRL_REG_LEN_08BIT, 0x00}, + {0x77d4, CRL_REG_LEN_08BIT, 0x00}, + {0x77d5, CRL_REG_LEN_08BIT, 0x02}, + {0x77d6, CRL_REG_LEN_08BIT, 0x04}, + {0x77d7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77d8, CRL_REG_LEN_08BIT, 0x04}, + {0x77d9, CRL_REG_LEN_08BIT, 0xe4}, + {0x77da, CRL_REG_LEN_08BIT, 0x05}, + {0x77db, CRL_REG_LEN_08BIT, 0x33}, + {0x77dc, CRL_REG_LEN_08BIT, 0x07}, + {0x77dd, CRL_REG_LEN_08BIT, 0x10}, + {0x77de, CRL_REG_LEN_08BIT, 0x00}, + {0x77df, CRL_REG_LEN_08BIT, 0x00}, + {0x77e0, CRL_REG_LEN_08BIT, 0x01}, + {0x77e1, CRL_REG_LEN_08BIT, 0xbb}, + {0x77e2, CRL_REG_LEN_08BIT, 0x00}, + {0x77e3, CRL_REG_LEN_08BIT, 0x00}, + {0x77e4, CRL_REG_LEN_08BIT, 0x01}, + {0x77e5, CRL_REG_LEN_08BIT, 0xaa}, + {0x77e6, CRL_REG_LEN_08BIT, 0x00}, + {0x77e7, CRL_REG_LEN_08BIT, 0x00}, + {0x77e8, CRL_REG_LEN_08BIT, 0x01}, + {0x77e9, CRL_REG_LEN_08BIT, 0x99}, + {0x77ea, CRL_REG_LEN_08BIT, 0x00}, + {0x77eb, CRL_REG_LEN_08BIT, 0x00}, + {0x77ec, CRL_REG_LEN_08BIT, 0x01}, + {0x77ed, CRL_REG_LEN_08BIT, 0x88}, + {0x77ee, CRL_REG_LEN_08BIT, 0x00}, + {0x77ef, CRL_REG_LEN_08BIT, 0x00}, + {0x77f0, CRL_REG_LEN_08BIT, 0x01}, + {0x77f1, CRL_REG_LEN_08BIT, 0x77}, + {0x77f2, CRL_REG_LEN_08BIT, 0x00}, + {0x77f3, CRL_REG_LEN_08BIT, 0x00}, + {0x77f4, CRL_REG_LEN_08BIT, 0x01}, + {0x77f5, CRL_REG_LEN_08BIT, 0x66}, + {0x77f6, CRL_REG_LEN_08BIT, 0x00}, + {0x77f7, CRL_REG_LEN_08BIT, 0x00}, + {0x77f8, CRL_REG_LEN_08BIT, 0x01}, + {0x77f9, CRL_REG_LEN_08BIT, 0x55}, + {0x77fa, CRL_REG_LEN_08BIT, 0x00}, + {0x77fb, CRL_REG_LEN_08BIT, 0x00}, + {0x77fc, CRL_REG_LEN_08BIT, 0x01}, + {0x77fd, CRL_REG_LEN_08BIT, 0x44}, + {0x77fe, CRL_REG_LEN_08BIT, 0x00}, + {0x77ff, CRL_REG_LEN_08BIT, 0x00}, + {0x7800, CRL_REG_LEN_08BIT, 0x01}, + {0x7801, CRL_REG_LEN_08BIT, 0x33}, + {0x7802, CRL_REG_LEN_08BIT, 0x00}, + {0x7803, CRL_REG_LEN_08BIT, 0x00}, + {0x7804, CRL_REG_LEN_08BIT, 0x01}, + {0x7805, CRL_REG_LEN_08BIT, 0x22}, + {0x7806, CRL_REG_LEN_08BIT, 0x00}, + {0x7807, CRL_REG_LEN_08BIT, 0x00}, + {0x7808, CRL_REG_LEN_08BIT, 0x01}, + {0x7809, CRL_REG_LEN_08BIT, 0x11}, + {0x780a, CRL_REG_LEN_08BIT, 0x00}, + {0x780b, CRL_REG_LEN_08BIT, 0x00}, + {0x780c, CRL_REG_LEN_08BIT, 0x01}, + {0x780d, CRL_REG_LEN_08BIT, 0x00}, + {0x780e, CRL_REG_LEN_08BIT, 0x01}, + {0x780f, CRL_REG_LEN_08BIT, 0xff}, + {0x7810, CRL_REG_LEN_08BIT, 0x07}, + {0x7811, CRL_REG_LEN_08BIT, 0x00}, + {0x7812, CRL_REG_LEN_08BIT, 0x02}, + {0x7813, CRL_REG_LEN_08BIT, 0xa0}, + {0x7814, CRL_REG_LEN_08BIT, 0x0f}, + {0x7815, CRL_REG_LEN_08BIT, 0x00}, + {0x7816, CRL_REG_LEN_08BIT, 0x08}, + {0x7817, CRL_REG_LEN_08BIT, 0x35}, + {0x7818, CRL_REG_LEN_08BIT, 0x06}, + {0x7819, CRL_REG_LEN_08BIT, 0x52}, + {0x781a, CRL_REG_LEN_08BIT, 0x04}, + {0x781b, CRL_REG_LEN_08BIT, 0xe4}, + {0x781c, CRL_REG_LEN_08BIT, 0x00}, + {0x781d, CRL_REG_LEN_08BIT, 0x00}, + {0x781e, CRL_REG_LEN_08BIT, 0x06}, + {0x781f, CRL_REG_LEN_08BIT, 0x5e}, + {0x7820, CRL_REG_LEN_08BIT, 0x05}, + {0x7821, CRL_REG_LEN_08BIT, 0x33}, + {0x7822, CRL_REG_LEN_08BIT, 0x09}, + {0x7823, CRL_REG_LEN_08BIT, 0x19}, + {0x7824, CRL_REG_LEN_08BIT, 0x06}, + {0x7825, CRL_REG_LEN_08BIT, 0x1e}, + {0x7826, CRL_REG_LEN_08BIT, 0x05}, + {0x7827, CRL_REG_LEN_08BIT, 0x33}, + {0x7828, CRL_REG_LEN_08BIT, 0x00}, + {0x7829, CRL_REG_LEN_08BIT, 0x01}, + {0x782a, CRL_REG_LEN_08BIT, 0x06}, + {0x782b, CRL_REG_LEN_08BIT, 0x24}, + {0x782c, CRL_REG_LEN_08BIT, 0x06}, + {0x782d, CRL_REG_LEN_08BIT, 0x20}, + {0x782e, CRL_REG_LEN_08BIT, 0x0f}, + {0x782f, CRL_REG_LEN_08BIT, 0x00}, + {0x7830, CRL_REG_LEN_08BIT, 0x08}, + {0x7831, CRL_REG_LEN_08BIT, 0x35}, + {0x7832, CRL_REG_LEN_08BIT, 0x07}, + {0x7833, CRL_REG_LEN_08BIT, 0x10}, + {0x7834, CRL_REG_LEN_08BIT, 0x00}, + {0x7835, CRL_REG_LEN_08BIT, 0x00}, + {0x7836, CRL_REG_LEN_08BIT, 0x01}, + {0x7837, CRL_REG_LEN_08BIT, 0xbb}, + {0x7838, CRL_REG_LEN_08BIT, 0x00}, + {0x7839, CRL_REG_LEN_08BIT, 0x00}, + {0x783a, CRL_REG_LEN_08BIT, 0x01}, + {0x783b, CRL_REG_LEN_08BIT, 0xaa}, + {0x783c, CRL_REG_LEN_08BIT, 0x00}, + {0x783d, CRL_REG_LEN_08BIT, 0x00}, + {0x783e, CRL_REG_LEN_08BIT, 0x01}, + {0x783f, CRL_REG_LEN_08BIT, 0x99}, + {0x7840, CRL_REG_LEN_08BIT, 0x00}, + {0x7841, CRL_REG_LEN_08BIT, 0x00}, + {0x7842, CRL_REG_LEN_08BIT, 0x01}, + {0x7843, CRL_REG_LEN_08BIT, 0x88}, + {0x7844, CRL_REG_LEN_08BIT, 0x00}, + {0x7845, CRL_REG_LEN_08BIT, 0x00}, + {0x7846, CRL_REG_LEN_08BIT, 0x01}, + {0x7847, CRL_REG_LEN_08BIT, 0x77}, + {0x7848, CRL_REG_LEN_08BIT, 0x00}, + {0x7849, CRL_REG_LEN_08BIT, 0x00}, + {0x784a, CRL_REG_LEN_08BIT, 0x01}, + {0x784b, CRL_REG_LEN_08BIT, 0x66}, + {0x784c, CRL_REG_LEN_08BIT, 0x00}, + {0x784d, CRL_REG_LEN_08BIT, 0x00}, + {0x784e, CRL_REG_LEN_08BIT, 0x01}, + {0x784f, CRL_REG_LEN_08BIT, 0x55}, + {0x7850, CRL_REG_LEN_08BIT, 0x00}, + {0x7851, CRL_REG_LEN_08BIT, 0x00}, + {0x7852, CRL_REG_LEN_08BIT, 0x01}, + {0x7853, CRL_REG_LEN_08BIT, 0x44}, + {0x7854, CRL_REG_LEN_08BIT, 0x00}, + {0x7855, CRL_REG_LEN_08BIT, 0x00}, + {0x7856, CRL_REG_LEN_08BIT, 0x01}, + {0x7857, CRL_REG_LEN_08BIT, 0x33}, + {0x7858, CRL_REG_LEN_08BIT, 0x00}, + {0x7859, CRL_REG_LEN_08BIT, 0x00}, + {0x785a, CRL_REG_LEN_08BIT, 0x01}, + {0x785b, CRL_REG_LEN_08BIT, 0x22}, + {0x785c, CRL_REG_LEN_08BIT, 0x00}, + {0x785d, CRL_REG_LEN_08BIT, 0x00}, + {0x785e, CRL_REG_LEN_08BIT, 0x01}, + {0x785f, CRL_REG_LEN_08BIT, 0x11}, + {0x7860, CRL_REG_LEN_08BIT, 0x00}, + {0x7861, CRL_REG_LEN_08BIT, 0x00}, + {0x7862, CRL_REG_LEN_08BIT, 0x01}, + {0x7863, CRL_REG_LEN_08BIT, 0x00}, + {0x7864, CRL_REG_LEN_08BIT, 0x07}, + {0x7865, CRL_REG_LEN_08BIT, 0x00}, + {0x7866, CRL_REG_LEN_08BIT, 0x01}, + {0x7867, CRL_REG_LEN_08BIT, 0xff}, + {0x7868, CRL_REG_LEN_08BIT, 0x02}, + {0x7869, CRL_REG_LEN_08BIT, 0xa0}, + {0x786a, CRL_REG_LEN_08BIT, 0x0f}, + {0x786b, CRL_REG_LEN_08BIT, 0x00}, + {0x786c, CRL_REG_LEN_08BIT, 0x08}, + {0x786d, CRL_REG_LEN_08BIT, 0x3a}, + {0x786e, CRL_REG_LEN_08BIT, 0x08}, + {0x786f, CRL_REG_LEN_08BIT, 0x6a}, + {0x7870, CRL_REG_LEN_08BIT, 0x0f}, + {0x7871, CRL_REG_LEN_08BIT, 0x00}, + {0x7872, CRL_REG_LEN_08BIT, 0x04}, + {0x7873, CRL_REG_LEN_08BIT, 0xc0}, + {0x7874, CRL_REG_LEN_08BIT, 0x09}, + {0x7875, CRL_REG_LEN_08BIT, 0x19}, + {0x7876, CRL_REG_LEN_08BIT, 0x04}, + {0x7877, CRL_REG_LEN_08BIT, 0x99}, + {0x7878, CRL_REG_LEN_08BIT, 0x07}, + {0x7879, CRL_REG_LEN_08BIT, 0x14}, + {0x787a, CRL_REG_LEN_08BIT, 0x00}, + {0x787b, CRL_REG_LEN_08BIT, 0x01}, + {0x787c, CRL_REG_LEN_08BIT, 0x04}, + {0x787d, CRL_REG_LEN_08BIT, 0xa4}, + {0x787e, CRL_REG_LEN_08BIT, 0x00}, + {0x787f, CRL_REG_LEN_08BIT, 0x07}, + {0x7880, CRL_REG_LEN_08BIT, 0x04}, + {0x7881, CRL_REG_LEN_08BIT, 0xa6}, + {0x7882, CRL_REG_LEN_08BIT, 0x00}, + {0x7883, CRL_REG_LEN_08BIT, 0x00}, + {0x7884, CRL_REG_LEN_08BIT, 0x04}, + {0x7885, CRL_REG_LEN_08BIT, 0xa0}, + {0x7886, CRL_REG_LEN_08BIT, 0x04}, + {0x7887, CRL_REG_LEN_08BIT, 0x80}, + {0x7888, CRL_REG_LEN_08BIT, 0x04}, + {0x7889, CRL_REG_LEN_08BIT, 0x00}, + {0x788a, CRL_REG_LEN_08BIT, 0x05}, + {0x788b, CRL_REG_LEN_08BIT, 0x03}, + {0x788c, CRL_REG_LEN_08BIT, 0x06}, + {0x788d, CRL_REG_LEN_08BIT, 0x00}, + {0x788e, CRL_REG_LEN_08BIT, 0x0f}, + {0x788f, CRL_REG_LEN_08BIT, 0x00}, + {0x7890, CRL_REG_LEN_08BIT, 0x0f}, + {0x7891, CRL_REG_LEN_08BIT, 0x00}, + {0x7892, CRL_REG_LEN_08BIT, 0x0f}, + {0x7893, CRL_REG_LEN_08BIT, 0x00}, + {0x3001, CRL_REG_LEN_08BIT, 0x32}, + {0x3005, CRL_REG_LEN_08BIT, 0x13}, + {0x3014, CRL_REG_LEN_08BIT, 0x44}, + {0x30a3, CRL_REG_LEN_08BIT, 0x00}, + {0x30a7, CRL_REG_LEN_08BIT, 0x48}, + {0x30ab, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x3b}, + {0x30b1, CRL_REG_LEN_08BIT, 0x92}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x64}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x00}, + {0x3195, CRL_REG_LEN_08BIT, 0x04}, + {0x31e3, CRL_REG_LEN_08BIT, 0x03}, + {0x31e4, CRL_REG_LEN_08BIT, 0x13}, + {0x30bb, CRL_REG_LEN_08BIT, 0x1a}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3250, CRL_REG_LEN_08BIT, 0xf7}, + {0x3012, CRL_REG_LEN_08BIT, 0x01}, +}; + +/* ov2775_1928x1088_3x12_30fps_mipi960_regset */ +static struct crl_register_write_rep ov2775_3x12_30fps_mipi960_regset[] = { + {0x3013, CRL_REG_LEN_08BIT, 0x01}, + {0x3000, CRL_REG_LEN_08BIT, 0x02}, + {0x3001, CRL_REG_LEN_08BIT, 0x28}, + {0x3002, CRL_REG_LEN_08BIT, 0x03}, + {0x3003, CRL_REG_LEN_08BIT, 0x01}, + {0x3004, CRL_REG_LEN_08BIT, 0x02}, + {0x3005, CRL_REG_LEN_08BIT, 0x26}, + {0x3006, CRL_REG_LEN_08BIT, 0x00}, + {0x3007, CRL_REG_LEN_08BIT, 0x07}, + {0x3008, CRL_REG_LEN_08BIT, 0x01}, + {0x3009, CRL_REG_LEN_08BIT, 0x00}, + {0x300c, CRL_REG_LEN_08BIT, 0x6c}, + {0x300e, CRL_REG_LEN_08BIT, 0x80}, + {0x300f, CRL_REG_LEN_08BIT, 0x00}, + {0x3012, CRL_REG_LEN_08BIT, 0x00}, + {0x3013, CRL_REG_LEN_08BIT, 0x00}, + {0x3014, CRL_REG_LEN_08BIT, 0xc4}, + {0x3015, CRL_REG_LEN_08BIT, 0x00}, + {0x3017, CRL_REG_LEN_08BIT, 0x00}, + {0x3018, CRL_REG_LEN_08BIT, 0x00}, + {0x3019, CRL_REG_LEN_08BIT, 0x00}, + {0x301a, CRL_REG_LEN_08BIT, 0x00}, + {0x301b, CRL_REG_LEN_08BIT, 0x0e}, + {0x301e, CRL_REG_LEN_08BIT, 0x17}, + {0x301f, CRL_REG_LEN_08BIT, 0xe1}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x3031, CRL_REG_LEN_08BIT, 0x62}, + {0x3032, CRL_REG_LEN_08BIT, 0xf0}, + {0x3033, CRL_REG_LEN_08BIT, 0x30}, + {0x3034, CRL_REG_LEN_08BIT, 0x3f}, + {0x3035, CRL_REG_LEN_08BIT, 0x5f}, + {0x3036, CRL_REG_LEN_08BIT, 0x02}, + {0x3037, CRL_REG_LEN_08BIT, 0x9f}, + {0x3038, CRL_REG_LEN_08BIT, 0x04}, + {0x3039, CRL_REG_LEN_08BIT, 0xb7}, + {0x303a, CRL_REG_LEN_08BIT, 0x04}, + {0x303b, CRL_REG_LEN_08BIT, 0x07}, + {0x303c, CRL_REG_LEN_08BIT, 0xf0}, + {0x303d, CRL_REG_LEN_08BIT, 0x00}, + {0x303e, CRL_REG_LEN_08BIT, 0x0b}, + {0x303f, CRL_REG_LEN_08BIT, 0xe3}, + {0x3040, CRL_REG_LEN_08BIT, 0xf3}, + {0x3041, CRL_REG_LEN_08BIT, 0x29}, + {0x3042, CRL_REG_LEN_08BIT, 0xf6}, + {0x3043, CRL_REG_LEN_08BIT, 0x65}, + {0x3044, CRL_REG_LEN_08BIT, 0x06}, + {0x3045, CRL_REG_LEN_08BIT, 0x0f}, + {0x3046, CRL_REG_LEN_08BIT, 0x59}, + {0x3047, CRL_REG_LEN_08BIT, 0x07}, + {0x3048, CRL_REG_LEN_08BIT, 0x82}, + {0x3049, CRL_REG_LEN_08BIT, 0xcf}, + {0x304a, CRL_REG_LEN_08BIT, 0x12}, + {0x304b, CRL_REG_LEN_08BIT, 0x40}, + {0x304c, CRL_REG_LEN_08BIT, 0x33}, + {0x304d, CRL_REG_LEN_08BIT, 0xa4}, + {0x304e, CRL_REG_LEN_08BIT, 0x0b}, + {0x304f, CRL_REG_LEN_08BIT, 0x3d}, + {0x3050, CRL_REG_LEN_08BIT, 0x10}, + {0x3060, CRL_REG_LEN_08BIT, 0x00}, + {0x3061, CRL_REG_LEN_08BIT, 0x64}, + {0x3062, CRL_REG_LEN_08BIT, 0x00}, + {0x3063, CRL_REG_LEN_08BIT, 0xe4}, + {0x3066, CRL_REG_LEN_08BIT, 0x80}, + {0x3080, CRL_REG_LEN_08BIT, 0x00}, + {0x3081, CRL_REG_LEN_08BIT, 0x00}, + {0x3082, CRL_REG_LEN_08BIT, 0x01}, + {0x3083, CRL_REG_LEN_08BIT, 0xe3}, + {0x3084, CRL_REG_LEN_08BIT, 0x06}, + {0x3085, CRL_REG_LEN_08BIT, 0x00}, + {0x3086, CRL_REG_LEN_08BIT, 0x10}, + {0x3087, CRL_REG_LEN_08BIT, 0x10}, + {0x3089, CRL_REG_LEN_08BIT, 0x00}, + {0x308a, CRL_REG_LEN_08BIT, 0x01}, + {0x3093, CRL_REG_LEN_08BIT, 0x00}, + {0x30a0, CRL_REG_LEN_08BIT, 0x00}, + {0x30a1, CRL_REG_LEN_08BIT, 0x04}, + {0x30a2, CRL_REG_LEN_08BIT, 0x00}, + {0x30a3, CRL_REG_LEN_08BIT, 0x08}, + {0x30a4, CRL_REG_LEN_08BIT, 0x07}, + {0x30a5, CRL_REG_LEN_08BIT, 0x8b}, + {0x30a6, CRL_REG_LEN_08BIT, 0x04}, + {0x30a7, CRL_REG_LEN_08BIT, 0x3f}, + {0x30a8, CRL_REG_LEN_08BIT, 0x00}, + {0x30a9, CRL_REG_LEN_08BIT, 0x04}, + {0x30aa, CRL_REG_LEN_08BIT, 0x00}, + {0x30ab, CRL_REG_LEN_08BIT, 0x00}, + {0x30ac, CRL_REG_LEN_08BIT, 0x07}, + {0x30ad, CRL_REG_LEN_08BIT, 0x80}, + {0x30ae, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x08}, + {0x30b1, CRL_REG_LEN_08BIT, 0x98}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x65}, + {0x30b4, CRL_REG_LEN_08BIT, 0x00}, + {0x30b5, CRL_REG_LEN_08BIT, 0x00}, + {0x30b6, CRL_REG_LEN_08BIT, 0x00}, + {0x30b7, CRL_REG_LEN_08BIT, 0x10}, + {0x30b8, CRL_REG_LEN_08BIT, 0x00}, + {0x30b9, CRL_REG_LEN_08BIT, 0x02}, + {0x30ba, CRL_REG_LEN_08BIT, 0x10}, + {0x30bb, CRL_REG_LEN_08BIT, 0x00}, + {0x30bc, CRL_REG_LEN_08BIT, 0x00}, + {0x30bd, CRL_REG_LEN_08BIT, 0x03}, + {0x30be, CRL_REG_LEN_08BIT, 0x5c}, + {0x30bf, CRL_REG_LEN_08BIT, 0x00}, + {0x30c0, CRL_REG_LEN_08BIT, 0x01}, + {0x30c1, CRL_REG_LEN_08BIT, 0x00}, + {0x30c2, CRL_REG_LEN_08BIT, 0x20}, + {0x30c3, CRL_REG_LEN_08BIT, 0x00}, + {0x30c4, CRL_REG_LEN_08BIT, 0x4a}, + {0x30c5, CRL_REG_LEN_08BIT, 0x00}, + {0x30c7, CRL_REG_LEN_08BIT, 0x00}, + {0x30c8, CRL_REG_LEN_08BIT, 0x00}, + {0x30d1, CRL_REG_LEN_08BIT, 0x00}, + {0x30d2, CRL_REG_LEN_08BIT, 0x00}, + {0x30d3, CRL_REG_LEN_08BIT, 0x80}, + {0x30d4, CRL_REG_LEN_08BIT, 0x00}, + {0x30d9, CRL_REG_LEN_08BIT, 0x09}, + {0x30da, CRL_REG_LEN_08BIT, 0x64}, + {0x30dd, CRL_REG_LEN_08BIT, 0x00}, + {0x30de, CRL_REG_LEN_08BIT, 0x16}, + {0x30df, CRL_REG_LEN_08BIT, 0x00}, + {0x30e0, CRL_REG_LEN_08BIT, 0x17}, + {0x30e1, CRL_REG_LEN_08BIT, 0x00}, + {0x30e2, CRL_REG_LEN_08BIT, 0x18}, + {0x30e3, CRL_REG_LEN_08BIT, 0x10}, + {0x30e4, CRL_REG_LEN_08BIT, 0x04}, + {0x30e5, CRL_REG_LEN_08BIT, 0x00}, + {0x30e6, CRL_REG_LEN_08BIT, 0x00}, + {0x30e7, CRL_REG_LEN_08BIT, 0x00}, + {0x30e8, CRL_REG_LEN_08BIT, 0x00}, + {0x30e9, CRL_REG_LEN_08BIT, 0x00}, + {0x30ea, CRL_REG_LEN_08BIT, 0x00}, + {0x30eb, CRL_REG_LEN_08BIT, 0x00}, + {0x30ec, CRL_REG_LEN_08BIT, 0x00}, + {0x30ed, CRL_REG_LEN_08BIT, 0x00}, + {0x3101, CRL_REG_LEN_08BIT, 0x00}, + {0x3102, CRL_REG_LEN_08BIT, 0x00}, + {0x3103, CRL_REG_LEN_08BIT, 0x00}, + {0x3104, CRL_REG_LEN_08BIT, 0x00}, + {0x3105, CRL_REG_LEN_08BIT, 0x8c}, + {0x3106, CRL_REG_LEN_08BIT, 0x87}, + {0x3107, CRL_REG_LEN_08BIT, 0xc0}, + {0x3108, CRL_REG_LEN_08BIT, 0x9d}, + {0x3109, CRL_REG_LEN_08BIT, 0x8d}, + {0x310a, CRL_REG_LEN_08BIT, 0x8d}, + {0x310b, CRL_REG_LEN_08BIT, 0x6a}, + {0x310c, CRL_REG_LEN_08BIT, 0x3a}, + {0x310d, CRL_REG_LEN_08BIT, 0x5a}, + {0x310e, CRL_REG_LEN_08BIT, 0x00}, + {0x3120, CRL_REG_LEN_08BIT, 0x00}, + {0x3121, CRL_REG_LEN_08BIT, 0x00}, + {0x3122, CRL_REG_LEN_08BIT, 0x00}, + {0x3123, CRL_REG_LEN_08BIT, 0x00}, + {0x3124, CRL_REG_LEN_08BIT, 0x00}, + {0x3125, CRL_REG_LEN_08BIT, 0x70}, + {0x3126, CRL_REG_LEN_08BIT, 0x1f}, + {0x3127, CRL_REG_LEN_08BIT, 0x0f}, + {0x3128, CRL_REG_LEN_08BIT, 0x00}, + {0x3129, CRL_REG_LEN_08BIT, 0x3a}, + {0x312a, CRL_REG_LEN_08BIT, 0x02}, + {0x312b, CRL_REG_LEN_08BIT, 0x0f}, + {0x312c, CRL_REG_LEN_08BIT, 0x00}, + {0x312d, CRL_REG_LEN_08BIT, 0x0f}, + {0x312e, CRL_REG_LEN_08BIT, 0x1d}, + {0x312f, CRL_REG_LEN_08BIT, 0x00}, + {0x3130, CRL_REG_LEN_08BIT, 0x00}, + {0x3131, CRL_REG_LEN_08BIT, 0x00}, + {0x3132, CRL_REG_LEN_08BIT, 0x00}, + {0x3140, CRL_REG_LEN_08BIT, 0x0a}, + {0x3141, CRL_REG_LEN_08BIT, 0x03}, + {0x3142, CRL_REG_LEN_08BIT, 0x00}, + {0x3143, CRL_REG_LEN_08BIT, 0x00}, + {0x3144, CRL_REG_LEN_08BIT, 0x00}, + {0x3145, CRL_REG_LEN_08BIT, 0x00}, + {0x3146, CRL_REG_LEN_08BIT, 0x00}, + {0x3147, CRL_REG_LEN_08BIT, 0x00}, + {0x3148, CRL_REG_LEN_08BIT, 0x00}, + {0x3149, CRL_REG_LEN_08BIT, 0x00}, + {0x314a, CRL_REG_LEN_08BIT, 0x00}, + {0x314b, CRL_REG_LEN_08BIT, 0x00}, + {0x314c, CRL_REG_LEN_08BIT, 0x00}, + {0x314d, CRL_REG_LEN_08BIT, 0x00}, + {0x314e, CRL_REG_LEN_08BIT, 0x1c}, + {0x314f, CRL_REG_LEN_08BIT, 0xff}, + {0x3150, CRL_REG_LEN_08BIT, 0xff}, + {0x3151, CRL_REG_LEN_08BIT, 0xff}, + {0x3152, CRL_REG_LEN_08BIT, 0x10}, + {0x3153, CRL_REG_LEN_08BIT, 0x10}, + {0x3154, CRL_REG_LEN_08BIT, 0x10}, + {0x3155, CRL_REG_LEN_08BIT, 0x00}, + {0x3156, CRL_REG_LEN_08BIT, 0x03}, + {0x3157, CRL_REG_LEN_08BIT, 0x00}, + {0x3158, CRL_REG_LEN_08BIT, 0x0f}, + {0x3159, CRL_REG_LEN_08BIT, 0xff}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3160, CRL_REG_LEN_08BIT, 0x01}, + {0x3161, CRL_REG_LEN_08BIT, 0x00}, + {0x3162, CRL_REG_LEN_08BIT, 0x01}, + {0x3163, CRL_REG_LEN_08BIT, 0x00}, + {0x3164, CRL_REG_LEN_08BIT, 0x01}, + {0x3165, CRL_REG_LEN_08BIT, 0x00}, + {0x3190, CRL_REG_LEN_08BIT, 0x05}, + {0x3191, CRL_REG_LEN_08BIT, 0x99}, + {0x3193, CRL_REG_LEN_08BIT, 0x08}, + {0x3194, CRL_REG_LEN_08BIT, 0x13}, + {0x3195, CRL_REG_LEN_08BIT, 0x33}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x10}, + {0x3198, CRL_REG_LEN_08BIT, 0x00}, + {0x3199, CRL_REG_LEN_08BIT, 0x3f}, + {0x319a, CRL_REG_LEN_08BIT, 0x40}, + {0x319b, CRL_REG_LEN_08BIT, 0x7f}, + {0x319c, CRL_REG_LEN_08BIT, 0x80}, + {0x319d, CRL_REG_LEN_08BIT, 0xbf}, + {0x319e, CRL_REG_LEN_08BIT, 0xc0}, + {0x319f, CRL_REG_LEN_08BIT, 0xff}, + {0x31a0, CRL_REG_LEN_08BIT, 0x24}, + {0x31a1, CRL_REG_LEN_08BIT, 0x55}, + {0x31a2, CRL_REG_LEN_08BIT, 0x00}, + {0x31a3, CRL_REG_LEN_08BIT, 0x08}, + {0x31a6, CRL_REG_LEN_08BIT, 0x00}, + {0x31a7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b0, CRL_REG_LEN_08BIT, 0x00}, + {0x31b1, CRL_REG_LEN_08BIT, 0x00}, + {0x31b2, CRL_REG_LEN_08BIT, 0x02}, + {0x31b3, CRL_REG_LEN_08BIT, 0x00}, + {0x31b4, CRL_REG_LEN_08BIT, 0x00}, + {0x31b5, CRL_REG_LEN_08BIT, 0x01}, + {0x31b6, CRL_REG_LEN_08BIT, 0x00}, + {0x31b7, CRL_REG_LEN_08BIT, 0x00}, + {0x31b8, CRL_REG_LEN_08BIT, 0x00}, + {0x31b9, CRL_REG_LEN_08BIT, 0x00}, + {0x31ba, CRL_REG_LEN_08BIT, 0x00}, + {0x31d0, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d1, CRL_REG_LEN_08BIT, 0x34}, + {0x31d2, CRL_REG_LEN_08BIT, 0x3c}, + {0x31d3, CRL_REG_LEN_08BIT, 0x00}, + {0x31d4, CRL_REG_LEN_08BIT, 0x2d}, + {0x31d5, CRL_REG_LEN_08BIT, 0x00}, + {0x31d6, CRL_REG_LEN_08BIT, 0x01}, + {0x31d7, CRL_REG_LEN_08BIT, 0x06}, + {0x31d8, CRL_REG_LEN_08BIT, 0x00}, + {0x31d9, CRL_REG_LEN_08BIT, 0x64}, + {0x31da, CRL_REG_LEN_08BIT, 0x00}, + {0x31db, CRL_REG_LEN_08BIT, 0x30}, + {0x31dc, CRL_REG_LEN_08BIT, 0x04}, + {0x31dd, CRL_REG_LEN_08BIT, 0x69}, + {0x31de, CRL_REG_LEN_08BIT, 0x0a}, + {0x31df, CRL_REG_LEN_08BIT, 0x3c}, + {0x31e0, CRL_REG_LEN_08BIT, 0x04}, + {0x31e1, CRL_REG_LEN_08BIT, 0x32}, + {0x31e2, CRL_REG_LEN_08BIT, 0x00}, + {0x31e3, CRL_REG_LEN_08BIT, 0x00}, + {0x31e4, CRL_REG_LEN_08BIT, 0x08}, + {0x31e5, CRL_REG_LEN_08BIT, 0x80}, + {0x31e6, CRL_REG_LEN_08BIT, 0x00}, + {0x31e7, CRL_REG_LEN_08BIT, 0x2c}, + {0x31e8, CRL_REG_LEN_08BIT, 0x6c}, + {0x31e9, CRL_REG_LEN_08BIT, 0xac}, + {0x31ea, CRL_REG_LEN_08BIT, 0xec}, + {0x31eb, CRL_REG_LEN_08BIT, 0x3f}, + {0x31ec, CRL_REG_LEN_08BIT, 0x0f}, + {0x31ed, CRL_REG_LEN_08BIT, 0x20}, + {0x31ee, CRL_REG_LEN_08BIT, 0x04}, + {0x31ef, CRL_REG_LEN_08BIT, 0x48}, + {0x31f0, CRL_REG_LEN_08BIT, 0x07}, + {0x31f1, CRL_REG_LEN_08BIT, 0x90}, + {0x31f2, CRL_REG_LEN_08BIT, 0x04}, + {0x31f3, CRL_REG_LEN_08BIT, 0x48}, + {0x31f4, CRL_REG_LEN_08BIT, 0x07}, + {0x31f5, CRL_REG_LEN_08BIT, 0x90}, + {0x31f6, CRL_REG_LEN_08BIT, 0x04}, + {0x31f7, CRL_REG_LEN_08BIT, 0x48}, + {0x31f8, CRL_REG_LEN_08BIT, 0x07}, + {0x31f9, CRL_REG_LEN_08BIT, 0x90}, + {0x31fa, CRL_REG_LEN_08BIT, 0x04}, + {0x31fb, CRL_REG_LEN_08BIT, 0x48}, + {0x31fd, CRL_REG_LEN_08BIT, 0xcb}, + {0x31fe, CRL_REG_LEN_08BIT, 0x01}, + {0x31ff, CRL_REG_LEN_08BIT, 0x03}, + {0x3200, CRL_REG_LEN_08BIT, 0x00}, + {0x3201, CRL_REG_LEN_08BIT, 0xff}, + {0x3202, CRL_REG_LEN_08BIT, 0x00}, + {0x3203, CRL_REG_LEN_08BIT, 0xff}, + {0x3204, CRL_REG_LEN_08BIT, 0xff}, + {0x3205, CRL_REG_LEN_08BIT, 0xff}, + {0x3206, CRL_REG_LEN_08BIT, 0xff}, + {0x3207, CRL_REG_LEN_08BIT, 0xff}, + {0x3208, CRL_REG_LEN_08BIT, 0xff}, + {0x3209, CRL_REG_LEN_08BIT, 0xff}, + {0x320a, CRL_REG_LEN_08BIT, 0xff}, + {0x320b, CRL_REG_LEN_08BIT, 0x1b}, + {0x320c, CRL_REG_LEN_08BIT, 0x1f}, + {0x320d, CRL_REG_LEN_08BIT, 0x1e}, + {0x320e, CRL_REG_LEN_08BIT, 0x30}, + {0x320f, CRL_REG_LEN_08BIT, 0x2d}, + {0x3210, CRL_REG_LEN_08BIT, 0x2c}, + {0x3211, CRL_REG_LEN_08BIT, 0x2b}, + {0x3212, CRL_REG_LEN_08BIT, 0x2a}, + {0x3213, CRL_REG_LEN_08BIT, 0x24}, + {0x3214, CRL_REG_LEN_08BIT, 0x22}, + {0x3215, CRL_REG_LEN_08BIT, 0x00}, + {0x3216, CRL_REG_LEN_08BIT, 0x04}, + {0x3217, CRL_REG_LEN_08BIT, 0x2c}, + {0x3218, CRL_REG_LEN_08BIT, 0x6c}, + {0x3219, CRL_REG_LEN_08BIT, 0xac}, + {0x321a, CRL_REG_LEN_08BIT, 0xec}, + {0x321b, CRL_REG_LEN_08BIT, 0x00}, + {0x3230, CRL_REG_LEN_08BIT, 0x3a}, + {0x3231, CRL_REG_LEN_08BIT, 0x00}, + {0x3232, CRL_REG_LEN_08BIT, 0x80}, + {0x3233, CRL_REG_LEN_08BIT, 0x00}, + {0x3234, CRL_REG_LEN_08BIT, 0x10}, + {0x3235, CRL_REG_LEN_08BIT, 0xaa}, + {0x3236, CRL_REG_LEN_08BIT, 0x55}, + {0x3237, CRL_REG_LEN_08BIT, 0x99}, + {0x3238, CRL_REG_LEN_08BIT, 0x66}, + {0x3239, CRL_REG_LEN_08BIT, 0x08}, + {0x323a, CRL_REG_LEN_08BIT, 0x88}, + {0x323b, CRL_REG_LEN_08BIT, 0x00}, + {0x323c, CRL_REG_LEN_08BIT, 0x00}, + {0x323d, CRL_REG_LEN_08BIT, 0x03}, + {0x3250, CRL_REG_LEN_08BIT, 0x33}, + {0x3251, CRL_REG_LEN_08BIT, 0x00}, + {0x3252, CRL_REG_LEN_08BIT, 0x20}, + {0x3253, CRL_REG_LEN_08BIT, 0x00}, + {0x3254, CRL_REG_LEN_08BIT, 0x11}, + {0x3255, CRL_REG_LEN_08BIT, 0x01}, + {0x3256, CRL_REG_LEN_08BIT, 0x00}, + {0x3257, CRL_REG_LEN_08BIT, 0x00}, + {0x3258, CRL_REG_LEN_08BIT, 0x00}, + {0x3270, CRL_REG_LEN_08BIT, 0x01}, + {0x3271, CRL_REG_LEN_08BIT, 0xc0}, + {0x3272, CRL_REG_LEN_08BIT, 0xf0}, + {0x3273, CRL_REG_LEN_08BIT, 0x01}, + {0x3274, CRL_REG_LEN_08BIT, 0x00}, + {0x3275, CRL_REG_LEN_08BIT, 0x40}, + {0x3276, CRL_REG_LEN_08BIT, 0x02}, + {0x3277, CRL_REG_LEN_08BIT, 0x08}, + {0x3278, CRL_REG_LEN_08BIT, 0x10}, + {0x3279, CRL_REG_LEN_08BIT, 0x04}, + {0x327a, CRL_REG_LEN_08BIT, 0x00}, + {0x327b, CRL_REG_LEN_08BIT, 0x03}, + {0x327c, CRL_REG_LEN_08BIT, 0x10}, + {0x327d, CRL_REG_LEN_08BIT, 0x60}, + {0x327e, CRL_REG_LEN_08BIT, 0xc0}, + {0x327f, CRL_REG_LEN_08BIT, 0x06}, + {0x3288, CRL_REG_LEN_08BIT, 0x10}, + {0x3289, CRL_REG_LEN_08BIT, 0x00}, + {0x328a, CRL_REG_LEN_08BIT, 0x08}, + {0x328b, CRL_REG_LEN_08BIT, 0x00}, + {0x328c, CRL_REG_LEN_08BIT, 0x04}, + {0x328d, CRL_REG_LEN_08BIT, 0x00}, + {0x328e, CRL_REG_LEN_08BIT, 0x02}, + {0x328f, CRL_REG_LEN_08BIT, 0x00}, + {0x3290, CRL_REG_LEN_08BIT, 0x20}, + {0x3291, CRL_REG_LEN_08BIT, 0x00}, + {0x3292, CRL_REG_LEN_08BIT, 0x10}, + {0x3293, CRL_REG_LEN_08BIT, 0x00}, + {0x3294, CRL_REG_LEN_08BIT, 0x08}, + {0x3295, CRL_REG_LEN_08BIT, 0x00}, + {0x3296, CRL_REG_LEN_08BIT, 0x04}, + {0x3297, CRL_REG_LEN_08BIT, 0x00}, + {0x3298, CRL_REG_LEN_08BIT, 0x40}, + {0x3299, CRL_REG_LEN_08BIT, 0x00}, + {0x329a, CRL_REG_LEN_08BIT, 0x20}, + {0x329b, CRL_REG_LEN_08BIT, 0x00}, + {0x329c, CRL_REG_LEN_08BIT, 0x10}, + {0x329d, CRL_REG_LEN_08BIT, 0x00}, + {0x329e, CRL_REG_LEN_08BIT, 0x08}, + {0x329f, CRL_REG_LEN_08BIT, 0x00}, + {0x32a0, CRL_REG_LEN_08BIT, 0x7f}, + {0x32a1, CRL_REG_LEN_08BIT, 0xff}, + {0x32a2, CRL_REG_LEN_08BIT, 0x40}, + {0x32a3, CRL_REG_LEN_08BIT, 0x00}, + {0x32a4, CRL_REG_LEN_08BIT, 0x20}, + {0x32a5, CRL_REG_LEN_08BIT, 0x00}, + {0x32a6, CRL_REG_LEN_08BIT, 0x10}, + {0x32a7, CRL_REG_LEN_08BIT, 0x00}, + {0x32a8, CRL_REG_LEN_08BIT, 0x00}, + {0x32a9, CRL_REG_LEN_08BIT, 0x00}, + {0x32aa, CRL_REG_LEN_08BIT, 0x00}, + {0x32ab, CRL_REG_LEN_08BIT, 0x00}, + {0x32ac, CRL_REG_LEN_08BIT, 0x00}, + {0x32ad, CRL_REG_LEN_08BIT, 0x00}, + {0x32ae, CRL_REG_LEN_08BIT, 0x00}, + {0x32af, CRL_REG_LEN_08BIT, 0x00}, + {0x32b0, CRL_REG_LEN_08BIT, 0x00}, + {0x32b1, CRL_REG_LEN_08BIT, 0x00}, + {0x32b2, CRL_REG_LEN_08BIT, 0x00}, + {0x32b3, CRL_REG_LEN_08BIT, 0x00}, + {0x32b4, CRL_REG_LEN_08BIT, 0x00}, + {0x32b5, CRL_REG_LEN_08BIT, 0x00}, + {0x32b6, CRL_REG_LEN_08BIT, 0x00}, + {0x32b7, CRL_REG_LEN_08BIT, 0x00}, + {0x32b8, CRL_REG_LEN_08BIT, 0x00}, + {0x32b9, CRL_REG_LEN_08BIT, 0x00}, + {0x32ba, CRL_REG_LEN_08BIT, 0x00}, + {0x32bb, CRL_REG_LEN_08BIT, 0x00}, + {0x32bc, CRL_REG_LEN_08BIT, 0x00}, + {0x32bd, CRL_REG_LEN_08BIT, 0x00}, + {0x32be, CRL_REG_LEN_08BIT, 0x00}, + {0x32bf, CRL_REG_LEN_08BIT, 0x00}, + {0x32c0, CRL_REG_LEN_08BIT, 0x00}, + {0x32c1, CRL_REG_LEN_08BIT, 0x00}, + {0x32c2, CRL_REG_LEN_08BIT, 0x00}, + {0x32c3, CRL_REG_LEN_08BIT, 0x00}, + {0x32c4, CRL_REG_LEN_08BIT, 0x00}, + {0x32c5, CRL_REG_LEN_08BIT, 0x00}, + {0x32c6, CRL_REG_LEN_08BIT, 0x00}, + {0x32c7, CRL_REG_LEN_08BIT, 0x00}, + {0x32c8, CRL_REG_LEN_08BIT, 0x87}, + {0x32c9, CRL_REG_LEN_08BIT, 0x00}, + {0x3330, CRL_REG_LEN_08BIT, 0x03}, + {0x3331, CRL_REG_LEN_08BIT, 0xc8}, + {0x3332, CRL_REG_LEN_08BIT, 0x02}, + {0x3333, CRL_REG_LEN_08BIT, 0x24}, + {0x3334, CRL_REG_LEN_08BIT, 0x00}, + {0x3335, CRL_REG_LEN_08BIT, 0x00}, + {0x3336, CRL_REG_LEN_08BIT, 0x00}, + {0x3337, CRL_REG_LEN_08BIT, 0x00}, + {0x3338, CRL_REG_LEN_08BIT, 0x03}, + {0x3339, CRL_REG_LEN_08BIT, 0xc8}, + {0x333a, CRL_REG_LEN_08BIT, 0x02}, + {0x333b, CRL_REG_LEN_08BIT, 0x24}, + {0x333c, CRL_REG_LEN_08BIT, 0x00}, + {0x333d, CRL_REG_LEN_08BIT, 0x00}, + {0x333e, CRL_REG_LEN_08BIT, 0x00}, + {0x333f, CRL_REG_LEN_08BIT, 0x00}, + {0x3340, CRL_REG_LEN_08BIT, 0x03}, + {0x3341, CRL_REG_LEN_08BIT, 0xc8}, + {0x3342, CRL_REG_LEN_08BIT, 0x02}, + {0x3343, CRL_REG_LEN_08BIT, 0x24}, + {0x3344, CRL_REG_LEN_08BIT, 0x00}, + {0x3345, CRL_REG_LEN_08BIT, 0x00}, + {0x3346, CRL_REG_LEN_08BIT, 0x00}, + {0x3347, CRL_REG_LEN_08BIT, 0x00}, + {0x3348, CRL_REG_LEN_08BIT, 0x40}, + {0x3349, CRL_REG_LEN_08BIT, 0x00}, + {0x334a, CRL_REG_LEN_08BIT, 0x00}, + {0x334b, CRL_REG_LEN_08BIT, 0x00}, + {0x334c, CRL_REG_LEN_08BIT, 0x00}, + {0x334d, CRL_REG_LEN_08BIT, 0x00}, + {0x334e, CRL_REG_LEN_08BIT, 0x80}, + {0x3360, CRL_REG_LEN_08BIT, 0x01}, + {0x3361, CRL_REG_LEN_08BIT, 0x00}, + {0x3362, CRL_REG_LEN_08BIT, 0x01}, + {0x3363, CRL_REG_LEN_08BIT, 0x00}, + {0x3364, CRL_REG_LEN_08BIT, 0x01}, + {0x3365, CRL_REG_LEN_08BIT, 0x00}, + {0x3366, CRL_REG_LEN_08BIT, 0x01}, + {0x3367, CRL_REG_LEN_08BIT, 0x00}, + {0x3368, CRL_REG_LEN_08BIT, 0x01}, + {0x3369, CRL_REG_LEN_08BIT, 0x00}, + {0x336a, CRL_REG_LEN_08BIT, 0x01}, + {0x336b, CRL_REG_LEN_08BIT, 0x00}, + {0x336c, CRL_REG_LEN_08BIT, 0x01}, + {0x336d, CRL_REG_LEN_08BIT, 0x00}, + {0x336e, CRL_REG_LEN_08BIT, 0x01}, + {0x336f, CRL_REG_LEN_08BIT, 0x00}, + {0x3370, CRL_REG_LEN_08BIT, 0x01}, + {0x3371, CRL_REG_LEN_08BIT, 0x00}, + {0x3372, CRL_REG_LEN_08BIT, 0x01}, + {0x3373, CRL_REG_LEN_08BIT, 0x00}, + {0x3374, CRL_REG_LEN_08BIT, 0x01}, + {0x3375, CRL_REG_LEN_08BIT, 0x00}, + {0x3376, CRL_REG_LEN_08BIT, 0x01}, + {0x3377, CRL_REG_LEN_08BIT, 0x00}, + {0x3378, CRL_REG_LEN_08BIT, 0x00}, + {0x3379, CRL_REG_LEN_08BIT, 0x00}, + {0x337a, CRL_REG_LEN_08BIT, 0x00}, + {0x337b, CRL_REG_LEN_08BIT, 0x00}, + {0x337c, CRL_REG_LEN_08BIT, 0x00}, + {0x337d, CRL_REG_LEN_08BIT, 0x00}, + {0x337e, CRL_REG_LEN_08BIT, 0x00}, + {0x337f, CRL_REG_LEN_08BIT, 0x00}, + {0x3380, CRL_REG_LEN_08BIT, 0x00}, + {0x3381, CRL_REG_LEN_08BIT, 0x00}, + {0x3382, CRL_REG_LEN_08BIT, 0x00}, + {0x3383, CRL_REG_LEN_08BIT, 0x00}, + {0x3384, CRL_REG_LEN_08BIT, 0x00}, + {0x3385, CRL_REG_LEN_08BIT, 0x00}, + {0x3386, CRL_REG_LEN_08BIT, 0x00}, + {0x3387, CRL_REG_LEN_08BIT, 0x00}, + {0x3388, CRL_REG_LEN_08BIT, 0x00}, + {0x3389, CRL_REG_LEN_08BIT, 0x00}, + {0x338a, CRL_REG_LEN_08BIT, 0x00}, + {0x338b, CRL_REG_LEN_08BIT, 0x00}, + {0x338c, CRL_REG_LEN_08BIT, 0x00}, + {0x338d, CRL_REG_LEN_08BIT, 0x00}, + {0x338e, CRL_REG_LEN_08BIT, 0x00}, + {0x338f, CRL_REG_LEN_08BIT, 0x00}, + {0x3390, CRL_REG_LEN_08BIT, 0x00}, + {0x3391, CRL_REG_LEN_08BIT, 0x00}, + {0x3392, CRL_REG_LEN_08BIT, 0x00}, + {0x3393, CRL_REG_LEN_08BIT, 0x00}, + {0x3394, CRL_REG_LEN_08BIT, 0x00}, + {0x3395, CRL_REG_LEN_08BIT, 0x00}, + {0x3396, CRL_REG_LEN_08BIT, 0x00}, + {0x3397, CRL_REG_LEN_08BIT, 0x00}, + {0x3398, CRL_REG_LEN_08BIT, 0x00}, + {0x3399, CRL_REG_LEN_08BIT, 0x00}, + {0x339a, CRL_REG_LEN_08BIT, 0x00}, + {0x339b, CRL_REG_LEN_08BIT, 0x00}, + {0x33b0, CRL_REG_LEN_08BIT, 0x00}, + {0x33b1, CRL_REG_LEN_08BIT, 0x50}, + {0x33b2, CRL_REG_LEN_08BIT, 0x01}, + {0x33b3, CRL_REG_LEN_08BIT, 0xff}, + {0x33b4, CRL_REG_LEN_08BIT, 0xe0}, + {0x33b5, CRL_REG_LEN_08BIT, 0x6b}, + {0x33b6, CRL_REG_LEN_08BIT, 0x00}, + {0x33b7, CRL_REG_LEN_08BIT, 0x00}, + {0x33b8, CRL_REG_LEN_08BIT, 0x00}, + {0x33b9, CRL_REG_LEN_08BIT, 0x00}, + {0x33ba, CRL_REG_LEN_08BIT, 0x00}, + {0x33bb, CRL_REG_LEN_08BIT, 0x1f}, + {0x33bc, CRL_REG_LEN_08BIT, 0x01}, + {0x33bd, CRL_REG_LEN_08BIT, 0x01}, + {0x33be, CRL_REG_LEN_08BIT, 0x01}, + {0x33bf, CRL_REG_LEN_08BIT, 0x01}, + {0x33c0, CRL_REG_LEN_08BIT, 0x00}, + {0x33c1, CRL_REG_LEN_08BIT, 0x00}, + {0x33c2, CRL_REG_LEN_08BIT, 0x00}, + {0x33c3, CRL_REG_LEN_08BIT, 0x00}, + {0x33e0, CRL_REG_LEN_08BIT, 0x14}, + {0x33e1, CRL_REG_LEN_08BIT, 0x0f}, + {0x33e2, CRL_REG_LEN_08BIT, 0x04}, + {0x33e3, CRL_REG_LEN_08BIT, 0x02}, + {0x33e4, CRL_REG_LEN_08BIT, 0x01}, + {0x33e5, CRL_REG_LEN_08BIT, 0x01}, + {0x33e6, CRL_REG_LEN_08BIT, 0x00}, + {0x33e7, CRL_REG_LEN_08BIT, 0x04}, + {0x33e8, CRL_REG_LEN_08BIT, 0x0c}, + {0x33e9, CRL_REG_LEN_08BIT, 0x02}, + {0x33ea, CRL_REG_LEN_08BIT, 0x02}, + {0x33eb, CRL_REG_LEN_08BIT, 0x02}, + {0x33ec, CRL_REG_LEN_08BIT, 0x03}, + {0x33ed, CRL_REG_LEN_08BIT, 0x02}, + {0x33ee, CRL_REG_LEN_08BIT, 0x05}, + {0x33ef, CRL_REG_LEN_08BIT, 0x0a}, + {0x33f0, CRL_REG_LEN_08BIT, 0x08}, + {0x33f1, CRL_REG_LEN_08BIT, 0x04}, + {0x33f2, CRL_REG_LEN_08BIT, 0x04}, + {0x33f3, CRL_REG_LEN_08BIT, 0x00}, + {0x33f4, CRL_REG_LEN_08BIT, 0x03}, + {0x33f5, CRL_REG_LEN_08BIT, 0x14}, + {0x33f6, CRL_REG_LEN_08BIT, 0x0f}, + {0x33f7, CRL_REG_LEN_08BIT, 0x02}, + {0x33f8, CRL_REG_LEN_08BIT, 0x01}, + {0x33f9, CRL_REG_LEN_08BIT, 0x01}, + {0x33fa, CRL_REG_LEN_08BIT, 0x01}, + {0x33fb, CRL_REG_LEN_08BIT, 0x00}, + {0x33fc, CRL_REG_LEN_08BIT, 0x04}, + {0x33fd, CRL_REG_LEN_08BIT, 0x0c}, + {0x33fe, CRL_REG_LEN_08BIT, 0x02}, + {0x33ff, CRL_REG_LEN_08BIT, 0x02}, + {0x3400, CRL_REG_LEN_08BIT, 0x02}, + {0x3401, CRL_REG_LEN_08BIT, 0x03}, + {0x3402, CRL_REG_LEN_08BIT, 0x01}, + {0x3403, CRL_REG_LEN_08BIT, 0x02}, + {0x3404, CRL_REG_LEN_08BIT, 0x08}, + {0x3405, CRL_REG_LEN_08BIT, 0x08}, + {0x3406, CRL_REG_LEN_08BIT, 0x04}, + {0x3407, CRL_REG_LEN_08BIT, 0x04}, + {0x3408, CRL_REG_LEN_08BIT, 0x00}, + {0x3409, CRL_REG_LEN_08BIT, 0x03}, + {0x340a, CRL_REG_LEN_08BIT, 0x14}, + {0x340b, CRL_REG_LEN_08BIT, 0x0f}, + {0x340c, CRL_REG_LEN_08BIT, 0x04}, + {0x340d, CRL_REG_LEN_08BIT, 0x02}, + {0x340e, CRL_REG_LEN_08BIT, 0x01}, + {0x340f, CRL_REG_LEN_08BIT, 0x01}, + {0x3410, CRL_REG_LEN_08BIT, 0x00}, + {0x3411, CRL_REG_LEN_08BIT, 0x04}, + {0x3412, CRL_REG_LEN_08BIT, 0x0c}, + {0x3413, CRL_REG_LEN_08BIT, 0x02}, + {0x3414, CRL_REG_LEN_08BIT, 0x02}, + {0x3415, CRL_REG_LEN_08BIT, 0x02}, + {0x3416, CRL_REG_LEN_08BIT, 0x03}, + {0x3417, CRL_REG_LEN_08BIT, 0x02}, + {0x3418, CRL_REG_LEN_08BIT, 0x05}, + {0x3419, CRL_REG_LEN_08BIT, 0x0a}, + {0x341a, CRL_REG_LEN_08BIT, 0x08}, + {0x341b, CRL_REG_LEN_08BIT, 0x04}, + {0x341c, CRL_REG_LEN_08BIT, 0x04}, + {0x341d, CRL_REG_LEN_08BIT, 0x00}, + {0x341e, CRL_REG_LEN_08BIT, 0x03}, + {0x3440, CRL_REG_LEN_08BIT, 0x00}, + {0x3441, CRL_REG_LEN_08BIT, 0x00}, + {0x3442, CRL_REG_LEN_08BIT, 0x00}, + {0x3443, CRL_REG_LEN_08BIT, 0x00}, + {0x3444, CRL_REG_LEN_08BIT, 0x02}, + {0x3445, CRL_REG_LEN_08BIT, 0xf0}, + {0x3446, CRL_REG_LEN_08BIT, 0x02}, + {0x3447, CRL_REG_LEN_08BIT, 0x08}, + {0x3448, CRL_REG_LEN_08BIT, 0x00}, + {0x3460, CRL_REG_LEN_08BIT, 0x40}, + {0x3461, CRL_REG_LEN_08BIT, 0x40}, + {0x3462, CRL_REG_LEN_08BIT, 0x40}, + {0x3463, CRL_REG_LEN_08BIT, 0x40}, + {0x3464, CRL_REG_LEN_08BIT, 0x03}, + {0x3465, CRL_REG_LEN_08BIT, 0x01}, + {0x3466, CRL_REG_LEN_08BIT, 0x01}, + {0x3467, CRL_REG_LEN_08BIT, 0x02}, + {0x3468, CRL_REG_LEN_08BIT, 0x30}, + {0x3469, CRL_REG_LEN_08BIT, 0x00}, + {0x346a, CRL_REG_LEN_08BIT, 0x35}, + {0x346b, CRL_REG_LEN_08BIT, 0x00}, + {0x3480, CRL_REG_LEN_08BIT, 0x40}, + {0x3481, CRL_REG_LEN_08BIT, 0x00}, + {0x3482, CRL_REG_LEN_08BIT, 0x00}, + {0x3483, CRL_REG_LEN_08BIT, 0x00}, + {0x3484, CRL_REG_LEN_08BIT, 0x0d}, + {0x3485, CRL_REG_LEN_08BIT, 0x00}, + {0x3486, CRL_REG_LEN_08BIT, 0x00}, + {0x3487, CRL_REG_LEN_08BIT, 0x00}, + {0x3488, CRL_REG_LEN_08BIT, 0x00}, + {0x3489, CRL_REG_LEN_08BIT, 0x00}, + {0x348a, CRL_REG_LEN_08BIT, 0x00}, + {0x348b, CRL_REG_LEN_08BIT, 0x04}, + {0x348c, CRL_REG_LEN_08BIT, 0x00}, + {0x348d, CRL_REG_LEN_08BIT, 0x01}, + {0x348f, CRL_REG_LEN_08BIT, 0x01}, + {0x3030, CRL_REG_LEN_08BIT, 0x0a}, + {0x3030, CRL_REG_LEN_08BIT, 0x02}, + {0x7000, CRL_REG_LEN_08BIT, 0x58}, + {0x7001, CRL_REG_LEN_08BIT, 0x7a}, + {0x7002, CRL_REG_LEN_08BIT, 0x1a}, + {0x7003, CRL_REG_LEN_08BIT, 0xc1}, + {0x7004, CRL_REG_LEN_08BIT, 0x03}, + {0x7005, CRL_REG_LEN_08BIT, 0xda}, + {0x7006, CRL_REG_LEN_08BIT, 0xbd}, + {0x7007, CRL_REG_LEN_08BIT, 0x03}, + {0x7008, CRL_REG_LEN_08BIT, 0xbd}, + {0x7009, CRL_REG_LEN_08BIT, 0x06}, + {0x700a, CRL_REG_LEN_08BIT, 0xe6}, + {0x700b, CRL_REG_LEN_08BIT, 0xec}, + {0x700c, CRL_REG_LEN_08BIT, 0xbc}, + {0x700d, CRL_REG_LEN_08BIT, 0xff}, + {0x700e, CRL_REG_LEN_08BIT, 0xbc}, + {0x700f, CRL_REG_LEN_08BIT, 0x73}, + {0x7010, CRL_REG_LEN_08BIT, 0xda}, + {0x7011, CRL_REG_LEN_08BIT, 0x72}, + {0x7012, CRL_REG_LEN_08BIT, 0x76}, + {0x7013, CRL_REG_LEN_08BIT, 0xb6}, + {0x7014, CRL_REG_LEN_08BIT, 0xee}, + {0x7015, CRL_REG_LEN_08BIT, 0xcf}, + {0x7016, CRL_REG_LEN_08BIT, 0xac}, + {0x7017, CRL_REG_LEN_08BIT, 0xd0}, + {0x7018, CRL_REG_LEN_08BIT, 0xac}, + {0x7019, CRL_REG_LEN_08BIT, 0xd1}, + {0x701a, CRL_REG_LEN_08BIT, 0x50}, + {0x701b, CRL_REG_LEN_08BIT, 0xac}, + {0x701c, CRL_REG_LEN_08BIT, 0xd2}, + {0x701d, CRL_REG_LEN_08BIT, 0xbc}, + {0x701e, CRL_REG_LEN_08BIT, 0x2e}, + {0x701f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7020, CRL_REG_LEN_08BIT, 0x00}, + {0x7021, CRL_REG_LEN_08BIT, 0xdc}, + {0x7022, CRL_REG_LEN_08BIT, 0xdf}, + {0x7023, CRL_REG_LEN_08BIT, 0xb0}, + {0x7024, CRL_REG_LEN_08BIT, 0x6e}, + {0x7025, CRL_REG_LEN_08BIT, 0xbd}, + {0x7026, CRL_REG_LEN_08BIT, 0x01}, + {0x7027, CRL_REG_LEN_08BIT, 0xd7}, + {0x7028, CRL_REG_LEN_08BIT, 0xed}, + {0x7029, CRL_REG_LEN_08BIT, 0xe1}, + {0x702a, CRL_REG_LEN_08BIT, 0x36}, + {0x702b, CRL_REG_LEN_08BIT, 0x30}, + {0x702c, CRL_REG_LEN_08BIT, 0xd3}, + {0x702d, CRL_REG_LEN_08BIT, 0x2e}, + {0x702e, CRL_REG_LEN_08BIT, 0x54}, + {0x702f, CRL_REG_LEN_08BIT, 0x46}, + {0x7030, CRL_REG_LEN_08BIT, 0xbc}, + {0x7031, CRL_REG_LEN_08BIT, 0x22}, + {0x7032, CRL_REG_LEN_08BIT, 0x66}, + {0x7033, CRL_REG_LEN_08BIT, 0xbc}, + {0x7034, CRL_REG_LEN_08BIT, 0x24}, + {0x7035, CRL_REG_LEN_08BIT, 0x2c}, + {0x7036, CRL_REG_LEN_08BIT, 0x28}, + {0x7037, CRL_REG_LEN_08BIT, 0xbc}, + {0x7038, CRL_REG_LEN_08BIT, 0x3c}, + {0x7039, CRL_REG_LEN_08BIT, 0xa1}, + {0x703a, CRL_REG_LEN_08BIT, 0xac}, + {0x703b, CRL_REG_LEN_08BIT, 0xd8}, + {0x703c, CRL_REG_LEN_08BIT, 0xd6}, + {0x703d, CRL_REG_LEN_08BIT, 0xb4}, + {0x703e, CRL_REG_LEN_08BIT, 0x04}, + {0x703f, CRL_REG_LEN_08BIT, 0x46}, + {0x7040, CRL_REG_LEN_08BIT, 0xb7}, + {0x7041, CRL_REG_LEN_08BIT, 0x04}, + {0x7042, CRL_REG_LEN_08BIT, 0xbe}, + {0x7043, CRL_REG_LEN_08BIT, 0x08}, + {0x7044, CRL_REG_LEN_08BIT, 0xc3}, + {0x7045, CRL_REG_LEN_08BIT, 0xd9}, + {0x7046, CRL_REG_LEN_08BIT, 0xad}, + {0x7047, CRL_REG_LEN_08BIT, 0xc3}, + {0x7048, CRL_REG_LEN_08BIT, 0xbc}, + {0x7049, CRL_REG_LEN_08BIT, 0x19}, + {0x704a, CRL_REG_LEN_08BIT, 0xc1}, + {0x704b, CRL_REG_LEN_08BIT, 0x27}, + {0x704c, CRL_REG_LEN_08BIT, 0xe7}, + {0x704d, CRL_REG_LEN_08BIT, 0x00}, + {0x704e, CRL_REG_LEN_08BIT, 0x50}, + {0x704f, CRL_REG_LEN_08BIT, 0x20}, + {0x7050, CRL_REG_LEN_08BIT, 0xb8}, + {0x7051, CRL_REG_LEN_08BIT, 0x02}, + {0x7052, CRL_REG_LEN_08BIT, 0xbc}, + {0x7053, CRL_REG_LEN_08BIT, 0x17}, + {0x7054, CRL_REG_LEN_08BIT, 0xdb}, + {0x7055, CRL_REG_LEN_08BIT, 0xc7}, + {0x7056, CRL_REG_LEN_08BIT, 0xb8}, + {0x7057, CRL_REG_LEN_08BIT, 0x00}, + {0x7058, CRL_REG_LEN_08BIT, 0x28}, + {0x7059, CRL_REG_LEN_08BIT, 0x54}, + {0x705a, CRL_REG_LEN_08BIT, 0xb4}, + {0x705b, CRL_REG_LEN_08BIT, 0x14}, + {0x705c, CRL_REG_LEN_08BIT, 0xab}, + {0x705d, CRL_REG_LEN_08BIT, 0xbe}, + {0x705e, CRL_REG_LEN_08BIT, 0x06}, + {0x705f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7060, CRL_REG_LEN_08BIT, 0xd6}, + {0x7061, CRL_REG_LEN_08BIT, 0x00}, + {0x7062, CRL_REG_LEN_08BIT, 0xb4}, + {0x7063, CRL_REG_LEN_08BIT, 0xc7}, + {0x7064, CRL_REG_LEN_08BIT, 0x07}, + {0x7065, CRL_REG_LEN_08BIT, 0xb9}, + {0x7066, CRL_REG_LEN_08BIT, 0x05}, + {0x7067, CRL_REG_LEN_08BIT, 0xee}, + {0x7068, CRL_REG_LEN_08BIT, 0xe6}, + {0x7069, CRL_REG_LEN_08BIT, 0xad}, + {0x706a, CRL_REG_LEN_08BIT, 0xb4}, + {0x706b, CRL_REG_LEN_08BIT, 0x26}, + {0x706c, CRL_REG_LEN_08BIT, 0x19}, + {0x706d, CRL_REG_LEN_08BIT, 0xc1}, + {0x706e, CRL_REG_LEN_08BIT, 0x3a}, + {0x706f, CRL_REG_LEN_08BIT, 0xc3}, + {0x7070, CRL_REG_LEN_08BIT, 0xaf}, + {0x7071, CRL_REG_LEN_08BIT, 0x00}, + {0x7072, CRL_REG_LEN_08BIT, 0xc0}, + {0x7073, CRL_REG_LEN_08BIT, 0x3c}, + {0x7074, CRL_REG_LEN_08BIT, 0xc3}, + {0x7075, CRL_REG_LEN_08BIT, 0xbe}, + {0x7076, CRL_REG_LEN_08BIT, 0xe7}, + {0x7077, CRL_REG_LEN_08BIT, 0x00}, + {0x7078, CRL_REG_LEN_08BIT, 0x15}, + {0x7079, CRL_REG_LEN_08BIT, 0xc2}, + {0x707a, CRL_REG_LEN_08BIT, 0x40}, + {0x707b, CRL_REG_LEN_08BIT, 0xc3}, + {0x707c, CRL_REG_LEN_08BIT, 0xa4}, + {0x707d, CRL_REG_LEN_08BIT, 0xc0}, + {0x707e, CRL_REG_LEN_08BIT, 0x3c}, + {0x707f, CRL_REG_LEN_08BIT, 0x00}, + {0x7080, CRL_REG_LEN_08BIT, 0xb9}, + {0x7081, CRL_REG_LEN_08BIT, 0x64}, + {0x7082, CRL_REG_LEN_08BIT, 0x29}, + {0x7083, CRL_REG_LEN_08BIT, 0x00}, + {0x7084, CRL_REG_LEN_08BIT, 0xb8}, + {0x7085, CRL_REG_LEN_08BIT, 0x12}, + {0x7086, CRL_REG_LEN_08BIT, 0xbe}, + {0x7087, CRL_REG_LEN_08BIT, 0x01}, + {0x7088, CRL_REG_LEN_08BIT, 0xd0}, + {0x7089, CRL_REG_LEN_08BIT, 0xbc}, + {0x708a, CRL_REG_LEN_08BIT, 0x01}, + {0x708b, CRL_REG_LEN_08BIT, 0xac}, + {0x708c, CRL_REG_LEN_08BIT, 0x37}, + {0x708d, CRL_REG_LEN_08BIT, 0xd2}, + {0x708e, CRL_REG_LEN_08BIT, 0xac}, + {0x708f, CRL_REG_LEN_08BIT, 0x45}, + {0x7090, CRL_REG_LEN_08BIT, 0xad}, + {0x7091, CRL_REG_LEN_08BIT, 0x28}, + {0x7092, CRL_REG_LEN_08BIT, 0x00}, + {0x7093, CRL_REG_LEN_08BIT, 0xb8}, + {0x7094, CRL_REG_LEN_08BIT, 0x00}, + {0x7095, CRL_REG_LEN_08BIT, 0xbc}, + {0x7096, CRL_REG_LEN_08BIT, 0x01}, + {0x7097, CRL_REG_LEN_08BIT, 0x36}, + {0x7098, CRL_REG_LEN_08BIT, 0xd3}, + {0x7099, CRL_REG_LEN_08BIT, 0x30}, + {0x709a, CRL_REG_LEN_08BIT, 0x04}, + {0x709b, CRL_REG_LEN_08BIT, 0xe0}, + {0x709c, CRL_REG_LEN_08BIT, 0xd8}, + {0x709d, CRL_REG_LEN_08BIT, 0xb4}, + {0x709e, CRL_REG_LEN_08BIT, 0xe9}, + {0x709f, CRL_REG_LEN_08BIT, 0x00}, + {0x70a0, CRL_REG_LEN_08BIT, 0xbe}, + {0x70a1, CRL_REG_LEN_08BIT, 0x05}, + {0x70a2, CRL_REG_LEN_08BIT, 0x62}, + {0x70a3, CRL_REG_LEN_08BIT, 0x07}, + {0x70a4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70a5, CRL_REG_LEN_08BIT, 0x05}, + {0x70a6, CRL_REG_LEN_08BIT, 0xad}, + {0x70a7, CRL_REG_LEN_08BIT, 0xc3}, + {0x70a8, CRL_REG_LEN_08BIT, 0xcf}, + {0x70a9, CRL_REG_LEN_08BIT, 0x00}, + {0x70aa, CRL_REG_LEN_08BIT, 0x15}, + {0x70ab, CRL_REG_LEN_08BIT, 0xc2}, + {0x70ac, CRL_REG_LEN_08BIT, 0x59}, + {0x70ad, CRL_REG_LEN_08BIT, 0xc3}, + {0x70ae, CRL_REG_LEN_08BIT, 0xc9}, + {0x70af, CRL_REG_LEN_08BIT, 0xc0}, + {0x70b0, CRL_REG_LEN_08BIT, 0x55}, + {0x70b1, CRL_REG_LEN_08BIT, 0x00}, + {0x70b2, CRL_REG_LEN_08BIT, 0x46}, + {0x70b3, CRL_REG_LEN_08BIT, 0xa1}, + {0x70b4, CRL_REG_LEN_08BIT, 0xb9}, + {0x70b5, CRL_REG_LEN_08BIT, 0x64}, + {0x70b6, CRL_REG_LEN_08BIT, 0x29}, + {0x70b7, CRL_REG_LEN_08BIT, 0x00}, + {0x70b8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70b9, CRL_REG_LEN_08BIT, 0x02}, + {0x70ba, CRL_REG_LEN_08BIT, 0xbe}, + {0x70bb, CRL_REG_LEN_08BIT, 0x02}, + {0x70bc, CRL_REG_LEN_08BIT, 0xd0}, + {0x70bd, CRL_REG_LEN_08BIT, 0xdc}, + {0x70be, CRL_REG_LEN_08BIT, 0xac}, + {0x70bf, CRL_REG_LEN_08BIT, 0xbc}, + {0x70c0, CRL_REG_LEN_08BIT, 0x01}, + {0x70c1, CRL_REG_LEN_08BIT, 0x37}, + {0x70c2, CRL_REG_LEN_08BIT, 0xac}, + {0x70c3, CRL_REG_LEN_08BIT, 0xd2}, + {0x70c4, CRL_REG_LEN_08BIT, 0x45}, + {0x70c5, CRL_REG_LEN_08BIT, 0xad}, + {0x70c6, CRL_REG_LEN_08BIT, 0x28}, + {0x70c7, CRL_REG_LEN_08BIT, 0x00}, + {0x70c8, CRL_REG_LEN_08BIT, 0xb8}, + {0x70c9, CRL_REG_LEN_08BIT, 0x00}, + {0x70ca, CRL_REG_LEN_08BIT, 0xbc}, + {0x70cb, CRL_REG_LEN_08BIT, 0x01}, + {0x70cc, CRL_REG_LEN_08BIT, 0x36}, + {0x70cd, CRL_REG_LEN_08BIT, 0x30}, + {0x70ce, CRL_REG_LEN_08BIT, 0xe0}, + {0x70cf, CRL_REG_LEN_08BIT, 0xd8}, + {0x70d0, CRL_REG_LEN_08BIT, 0xb5}, + {0x70d1, CRL_REG_LEN_08BIT, 0x0b}, + {0x70d2, CRL_REG_LEN_08BIT, 0xd6}, + {0x70d3, CRL_REG_LEN_08BIT, 0xbe}, + {0x70d4, CRL_REG_LEN_08BIT, 0x07}, + {0x70d5, CRL_REG_LEN_08BIT, 0x00}, + {0x70d6, CRL_REG_LEN_08BIT, 0x62}, + {0x70d7, CRL_REG_LEN_08BIT, 0x07}, + {0x70d8, CRL_REG_LEN_08BIT, 0xb9}, + {0x70d9, CRL_REG_LEN_08BIT, 0x05}, + {0x70da, CRL_REG_LEN_08BIT, 0xad}, + {0x70db, CRL_REG_LEN_08BIT, 0xc3}, + {0x70dc, CRL_REG_LEN_08BIT, 0xcf}, + {0x70dd, CRL_REG_LEN_08BIT, 0x46}, + {0x70de, CRL_REG_LEN_08BIT, 0xcd}, + {0x70df, CRL_REG_LEN_08BIT, 0x07}, + {0x70e0, CRL_REG_LEN_08BIT, 0xcd}, + {0x70e1, CRL_REG_LEN_08BIT, 0x00}, + {0x70e2, CRL_REG_LEN_08BIT, 0xe3}, + {0x70e3, CRL_REG_LEN_08BIT, 0x18}, + {0x70e4, CRL_REG_LEN_08BIT, 0xc2}, + {0x70e5, CRL_REG_LEN_08BIT, 0xa2}, + {0x70e6, CRL_REG_LEN_08BIT, 0xb9}, + {0x70e7, CRL_REG_LEN_08BIT, 0x64}, + {0x70e8, CRL_REG_LEN_08BIT, 0xd1}, + {0x70e9, CRL_REG_LEN_08BIT, 0xdd}, + {0x70ea, CRL_REG_LEN_08BIT, 0xac}, + {0x70eb, CRL_REG_LEN_08BIT, 0xcf}, + {0x70ec, CRL_REG_LEN_08BIT, 0xdf}, + {0x70ed, CRL_REG_LEN_08BIT, 0xb5}, + {0x70ee, CRL_REG_LEN_08BIT, 0x19}, + {0x70ef, CRL_REG_LEN_08BIT, 0x46}, + {0x70f0, CRL_REG_LEN_08BIT, 0x50}, + {0x70f1, CRL_REG_LEN_08BIT, 0xb6}, + {0x70f2, CRL_REG_LEN_08BIT, 0xee}, + {0x70f3, CRL_REG_LEN_08BIT, 0xe8}, + {0x70f4, CRL_REG_LEN_08BIT, 0xe6}, + {0x70f5, CRL_REG_LEN_08BIT, 0xbc}, + {0x70f6, CRL_REG_LEN_08BIT, 0x31}, + {0x70f7, CRL_REG_LEN_08BIT, 0xe1}, + {0x70f8, CRL_REG_LEN_08BIT, 0x36}, + {0x70f9, CRL_REG_LEN_08BIT, 0x30}, + {0x70fa, CRL_REG_LEN_08BIT, 0xd3}, + {0x70fb, CRL_REG_LEN_08BIT, 0x2e}, + {0x70fc, CRL_REG_LEN_08BIT, 0x54}, + {0x70fd, CRL_REG_LEN_08BIT, 0xbd}, + {0x70fe, CRL_REG_LEN_08BIT, 0x03}, + {0x70ff, CRL_REG_LEN_08BIT, 0xec}, + {0x7100, CRL_REG_LEN_08BIT, 0x2c}, + {0x7101, CRL_REG_LEN_08BIT, 0x50}, + {0x7102, CRL_REG_LEN_08BIT, 0x20}, + {0x7103, CRL_REG_LEN_08BIT, 0x04}, + {0x7104, CRL_REG_LEN_08BIT, 0xb8}, + {0x7105, CRL_REG_LEN_08BIT, 0x02}, + {0x7106, CRL_REG_LEN_08BIT, 0xbc}, + {0x7107, CRL_REG_LEN_08BIT, 0x18}, + {0x7108, CRL_REG_LEN_08BIT, 0xc7}, + {0x7109, CRL_REG_LEN_08BIT, 0xb8}, + {0x710a, CRL_REG_LEN_08BIT, 0x00}, + {0x710b, CRL_REG_LEN_08BIT, 0x28}, + {0x710c, CRL_REG_LEN_08BIT, 0x54}, + {0x710d, CRL_REG_LEN_08BIT, 0xbc}, + {0x710e, CRL_REG_LEN_08BIT, 0x02}, + {0x710f, CRL_REG_LEN_08BIT, 0xb4}, + {0x7110, CRL_REG_LEN_08BIT, 0xda}, + {0x7111, CRL_REG_LEN_08BIT, 0xbe}, + {0x7112, CRL_REG_LEN_08BIT, 0x04}, + {0x7113, CRL_REG_LEN_08BIT, 0xd6}, + {0x7114, CRL_REG_LEN_08BIT, 0xd8}, + {0x7115, CRL_REG_LEN_08BIT, 0xab}, + {0x7116, CRL_REG_LEN_08BIT, 0x00}, + {0x7117, CRL_REG_LEN_08BIT, 0x62}, + {0x7118, CRL_REG_LEN_08BIT, 0x07}, + {0x7119, CRL_REG_LEN_08BIT, 0xb9}, + {0x711a, CRL_REG_LEN_08BIT, 0x05}, + {0x711b, CRL_REG_LEN_08BIT, 0xad}, + {0x711c, CRL_REG_LEN_08BIT, 0xc3}, + {0x711d, CRL_REG_LEN_08BIT, 0xbc}, + {0x711e, CRL_REG_LEN_08BIT, 0xe7}, + {0x711f, CRL_REG_LEN_08BIT, 0xb9}, + {0x7120, CRL_REG_LEN_08BIT, 0x64}, + {0x7121, CRL_REG_LEN_08BIT, 0x29}, + {0x7122, CRL_REG_LEN_08BIT, 0x00}, + {0x7123, CRL_REG_LEN_08BIT, 0xb8}, + {0x7124, CRL_REG_LEN_08BIT, 0x02}, + {0x7125, CRL_REG_LEN_08BIT, 0xbe}, + {0x7126, CRL_REG_LEN_08BIT, 0x00}, + {0x7127, CRL_REG_LEN_08BIT, 0x45}, + {0x7128, CRL_REG_LEN_08BIT, 0xad}, + {0x7129, CRL_REG_LEN_08BIT, 0xe2}, + {0x712a, CRL_REG_LEN_08BIT, 0x28}, + {0x712b, CRL_REG_LEN_08BIT, 0x00}, + {0x712c, CRL_REG_LEN_08BIT, 0xb8}, + {0x712d, CRL_REG_LEN_08BIT, 0x00}, + {0x712e, CRL_REG_LEN_08BIT, 0xe0}, + {0x712f, CRL_REG_LEN_08BIT, 0xd8}, + {0x7130, CRL_REG_LEN_08BIT, 0xb4}, + {0x7131, CRL_REG_LEN_08BIT, 0xe9}, + {0x7132, CRL_REG_LEN_08BIT, 0xbe}, + {0x7133, CRL_REG_LEN_08BIT, 0x03}, + {0x7134, CRL_REG_LEN_08BIT, 0x00}, + {0x7135, CRL_REG_LEN_08BIT, 0x30}, + {0x7136, CRL_REG_LEN_08BIT, 0x62}, + {0x7137, CRL_REG_LEN_08BIT, 0x07}, + {0x7138, CRL_REG_LEN_08BIT, 0xb9}, + {0x7139, CRL_REG_LEN_08BIT, 0x05}, + {0x713a, CRL_REG_LEN_08BIT, 0xad}, + {0x713b, CRL_REG_LEN_08BIT, 0xc3}, + {0x713c, CRL_REG_LEN_08BIT, 0xcf}, + {0x713d, CRL_REG_LEN_08BIT, 0x42}, + {0x713e, CRL_REG_LEN_08BIT, 0xe4}, + {0x713f, CRL_REG_LEN_08BIT, 0xcd}, + {0x7140, CRL_REG_LEN_08BIT, 0x07}, + {0x7141, CRL_REG_LEN_08BIT, 0xcd}, + {0x7142, CRL_REG_LEN_08BIT, 0x00}, + {0x7143, CRL_REG_LEN_08BIT, 0x00}, + {0x7144, CRL_REG_LEN_08BIT, 0x17}, + {0x7145, CRL_REG_LEN_08BIT, 0xc2}, + {0x7146, CRL_REG_LEN_08BIT, 0xbb}, + {0x7147, CRL_REG_LEN_08BIT, 0xde}, + {0x7148, CRL_REG_LEN_08BIT, 0xcf}, + {0x7149, CRL_REG_LEN_08BIT, 0xdf}, + {0x714a, CRL_REG_LEN_08BIT, 0xac}, + {0x714b, CRL_REG_LEN_08BIT, 0xd1}, + {0x714c, CRL_REG_LEN_08BIT, 0x44}, + {0x714d, CRL_REG_LEN_08BIT, 0xac}, + {0x714e, CRL_REG_LEN_08BIT, 0xb9}, + {0x714f, CRL_REG_LEN_08BIT, 0x76}, + {0x7150, CRL_REG_LEN_08BIT, 0xb8}, + {0x7151, CRL_REG_LEN_08BIT, 0x08}, + {0x7152, CRL_REG_LEN_08BIT, 0xb6}, + {0x7153, CRL_REG_LEN_08BIT, 0xfe}, + {0x7154, CRL_REG_LEN_08BIT, 0xb4}, + {0x7155, CRL_REG_LEN_08BIT, 0xca}, + {0x7156, CRL_REG_LEN_08BIT, 0xd6}, + {0x7157, CRL_REG_LEN_08BIT, 0xd8}, + {0x7158, CRL_REG_LEN_08BIT, 0xab}, + {0x7159, CRL_REG_LEN_08BIT, 0x00}, + {0x715a, CRL_REG_LEN_08BIT, 0xe1}, + {0x715b, CRL_REG_LEN_08BIT, 0x36}, + {0x715c, CRL_REG_LEN_08BIT, 0x30}, + {0x715d, CRL_REG_LEN_08BIT, 0xd3}, + {0x715e, CRL_REG_LEN_08BIT, 0xbc}, + {0x715f, CRL_REG_LEN_08BIT, 0x29}, + {0x7160, CRL_REG_LEN_08BIT, 0xb4}, + {0x7161, CRL_REG_LEN_08BIT, 0x1f}, + {0x7162, CRL_REG_LEN_08BIT, 0xaa}, + {0x7163, CRL_REG_LEN_08BIT, 0xbd}, + {0x7164, CRL_REG_LEN_08BIT, 0x01}, + {0x7165, CRL_REG_LEN_08BIT, 0xb8}, + {0x7166, CRL_REG_LEN_08BIT, 0x0c}, + {0x7167, CRL_REG_LEN_08BIT, 0x45}, + {0x7168, CRL_REG_LEN_08BIT, 0xa4}, + {0x7169, CRL_REG_LEN_08BIT, 0xbd}, + {0x716a, CRL_REG_LEN_08BIT, 0x03}, + {0x716b, CRL_REG_LEN_08BIT, 0xec}, + {0x716c, CRL_REG_LEN_08BIT, 0xbc}, + {0x716d, CRL_REG_LEN_08BIT, 0x3d}, + {0x716e, CRL_REG_LEN_08BIT, 0xc3}, + {0x716f, CRL_REG_LEN_08BIT, 0xcf}, + {0x7170, CRL_REG_LEN_08BIT, 0x42}, + {0x7171, CRL_REG_LEN_08BIT, 0xb8}, + {0x7172, CRL_REG_LEN_08BIT, 0x00}, + {0x7173, CRL_REG_LEN_08BIT, 0xe4}, + {0x7174, CRL_REG_LEN_08BIT, 0xd5}, + {0x7175, CRL_REG_LEN_08BIT, 0x00}, + {0x7176, CRL_REG_LEN_08BIT, 0xb6}, + {0x7177, CRL_REG_LEN_08BIT, 0x00}, + {0x7178, CRL_REG_LEN_08BIT, 0x74}, + {0x7179, CRL_REG_LEN_08BIT, 0xbd}, + {0x717a, CRL_REG_LEN_08BIT, 0x03}, + {0x717b, CRL_REG_LEN_08BIT, 0xb5}, + {0x717c, CRL_REG_LEN_08BIT, 0x39}, + {0x717d, CRL_REG_LEN_08BIT, 0x40}, + {0x717e, CRL_REG_LEN_08BIT, 0x58}, + {0x717f, CRL_REG_LEN_08BIT, 0xdd}, + {0x7180, CRL_REG_LEN_08BIT, 0x19}, + {0x7181, CRL_REG_LEN_08BIT, 0xc1}, + {0x7182, CRL_REG_LEN_08BIT, 0xc8}, + {0x7183, CRL_REG_LEN_08BIT, 0xbd}, + {0x7184, CRL_REG_LEN_08BIT, 0x06}, + {0x7185, CRL_REG_LEN_08BIT, 0x17}, + {0x7186, CRL_REG_LEN_08BIT, 0xc1}, + {0x7187, CRL_REG_LEN_08BIT, 0xc6}, + {0x7188, CRL_REG_LEN_08BIT, 0xe8}, + {0x7189, CRL_REG_LEN_08BIT, 0x00}, + {0x718a, CRL_REG_LEN_08BIT, 0xc0}, + {0x718b, CRL_REG_LEN_08BIT, 0xc8}, + {0x718c, CRL_REG_LEN_08BIT, 0xe6}, + {0x718d, CRL_REG_LEN_08BIT, 0x95}, + {0x718e, CRL_REG_LEN_08BIT, 0x15}, + {0x718f, CRL_REG_LEN_08BIT, 0x00}, + {0x7190, CRL_REG_LEN_08BIT, 0xbc}, + {0x7191, CRL_REG_LEN_08BIT, 0x19}, + {0x7192, CRL_REG_LEN_08BIT, 0xb9}, + {0x7193, CRL_REG_LEN_08BIT, 0xf6}, + {0x7194, CRL_REG_LEN_08BIT, 0x14}, + {0x7195, CRL_REG_LEN_08BIT, 0xc1}, + {0x7196, CRL_REG_LEN_08BIT, 0xd0}, + {0x7197, CRL_REG_LEN_08BIT, 0xd1}, + {0x7198, CRL_REG_LEN_08BIT, 0xac}, + {0x7199, CRL_REG_LEN_08BIT, 0x37}, + {0x719a, CRL_REG_LEN_08BIT, 0xbc}, + {0x719b, CRL_REG_LEN_08BIT, 0x35}, + {0x719c, CRL_REG_LEN_08BIT, 0x36}, + {0x719d, CRL_REG_LEN_08BIT, 0x30}, + {0x719e, CRL_REG_LEN_08BIT, 0xe1}, + {0x719f, CRL_REG_LEN_08BIT, 0xd3}, + {0x71a0, CRL_REG_LEN_08BIT, 0x7a}, + {0x71a1, CRL_REG_LEN_08BIT, 0xb6}, + {0x71a2, CRL_REG_LEN_08BIT, 0x0c}, + {0x71a3, CRL_REG_LEN_08BIT, 0xff}, + {0x71a4, CRL_REG_LEN_08BIT, 0xb4}, + {0x71a5, CRL_REG_LEN_08BIT, 0xc7}, + {0x71a6, CRL_REG_LEN_08BIT, 0xd9}, + {0x71a7, CRL_REG_LEN_08BIT, 0x00}, + {0x71a8, CRL_REG_LEN_08BIT, 0xbd}, + {0x71a9, CRL_REG_LEN_08BIT, 0x01}, + {0x71aa, CRL_REG_LEN_08BIT, 0x56}, + {0x71ab, CRL_REG_LEN_08BIT, 0xc0}, + {0x71ac, CRL_REG_LEN_08BIT, 0xda}, + {0x71ad, CRL_REG_LEN_08BIT, 0xb4}, + {0x71ae, CRL_REG_LEN_08BIT, 0x1f}, + {0x71af, CRL_REG_LEN_08BIT, 0x56}, + {0x71b0, CRL_REG_LEN_08BIT, 0xaa}, + {0x71b1, CRL_REG_LEN_08BIT, 0xbc}, + {0x71b2, CRL_REG_LEN_08BIT, 0x08}, + {0x71b3, CRL_REG_LEN_08BIT, 0x00}, + {0x71b4, CRL_REG_LEN_08BIT, 0x57}, + {0x71b5, CRL_REG_LEN_08BIT, 0xe8}, + {0x71b6, CRL_REG_LEN_08BIT, 0xb5}, + {0x71b7, CRL_REG_LEN_08BIT, 0x36}, + {0x71b8, CRL_REG_LEN_08BIT, 0x00}, + {0x71b9, CRL_REG_LEN_08BIT, 0x54}, + {0x71ba, CRL_REG_LEN_08BIT, 0xe7}, + {0x71bb, CRL_REG_LEN_08BIT, 0xc8}, + {0x71bc, CRL_REG_LEN_08BIT, 0xb4}, + {0x71bd, CRL_REG_LEN_08BIT, 0x1f}, + {0x71be, CRL_REG_LEN_08BIT, 0x56}, + {0x71bf, CRL_REG_LEN_08BIT, 0xaa}, + {0x71c0, CRL_REG_LEN_08BIT, 0xbc}, + {0x71c1, CRL_REG_LEN_08BIT, 0x08}, + {0x71c2, CRL_REG_LEN_08BIT, 0x57}, + {0x71c3, CRL_REG_LEN_08BIT, 0x00}, + {0x71c4, CRL_REG_LEN_08BIT, 0xb5}, + {0x71c5, CRL_REG_LEN_08BIT, 0x36}, + {0x71c6, CRL_REG_LEN_08BIT, 0x00}, + {0x71c7, CRL_REG_LEN_08BIT, 0x54}, + {0x71c8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71c9, CRL_REG_LEN_08BIT, 0xb5}, + {0x71ca, CRL_REG_LEN_08BIT, 0x18}, + {0x71cb, CRL_REG_LEN_08BIT, 0xd9}, + {0x71cc, CRL_REG_LEN_08BIT, 0x00}, + {0x71cd, CRL_REG_LEN_08BIT, 0xbd}, + {0x71ce, CRL_REG_LEN_08BIT, 0x01}, + {0x71cf, CRL_REG_LEN_08BIT, 0x56}, + {0x71d0, CRL_REG_LEN_08BIT, 0x08}, + {0x71d1, CRL_REG_LEN_08BIT, 0x57}, + {0x71d2, CRL_REG_LEN_08BIT, 0xe8}, + {0x71d3, CRL_REG_LEN_08BIT, 0xb4}, + {0x71d4, CRL_REG_LEN_08BIT, 0x42}, + {0x71d5, CRL_REG_LEN_08BIT, 0x00}, + {0x71d6, CRL_REG_LEN_08BIT, 0x54}, + {0x71d7, CRL_REG_LEN_08BIT, 0xe7}, + {0x71d8, CRL_REG_LEN_08BIT, 0xc8}, + {0x71d9, CRL_REG_LEN_08BIT, 0xab}, + {0x71da, CRL_REG_LEN_08BIT, 0x00}, + {0x71db, CRL_REG_LEN_08BIT, 0x66}, + {0x71dc, CRL_REG_LEN_08BIT, 0x62}, + {0x71dd, CRL_REG_LEN_08BIT, 0x06}, + {0x71de, CRL_REG_LEN_08BIT, 0x74}, + {0x71df, CRL_REG_LEN_08BIT, 0xb9}, + {0x71e0, CRL_REG_LEN_08BIT, 0x05}, + {0x71e1, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e2, CRL_REG_LEN_08BIT, 0x14}, + {0x71e3, CRL_REG_LEN_08BIT, 0x0e}, + {0x71e4, CRL_REG_LEN_08BIT, 0xb7}, + {0x71e5, CRL_REG_LEN_08BIT, 0x04}, + {0x71e6, CRL_REG_LEN_08BIT, 0xc8}, + {0x7600, CRL_REG_LEN_08BIT, 0x04}, + {0x7601, CRL_REG_LEN_08BIT, 0x80}, + {0x7602, CRL_REG_LEN_08BIT, 0x07}, + {0x7603, CRL_REG_LEN_08BIT, 0x44}, + {0x7604, CRL_REG_LEN_08BIT, 0x05}, + {0x7605, CRL_REG_LEN_08BIT, 0x33}, + {0x7606, CRL_REG_LEN_08BIT, 0x0f}, + {0x7607, CRL_REG_LEN_08BIT, 0x00}, + {0x7608, CRL_REG_LEN_08BIT, 0x07}, + {0x7609, CRL_REG_LEN_08BIT, 0x40}, + {0x760a, CRL_REG_LEN_08BIT, 0x04}, + {0x760b, CRL_REG_LEN_08BIT, 0xe5}, + {0x760c, CRL_REG_LEN_08BIT, 0x06}, + {0x760d, CRL_REG_LEN_08BIT, 0x50}, + {0x760e, CRL_REG_LEN_08BIT, 0x04}, + {0x760f, CRL_REG_LEN_08BIT, 0xe4}, + {0x7610, CRL_REG_LEN_08BIT, 0x00}, + {0x7611, CRL_REG_LEN_08BIT, 0x00}, + {0x7612, CRL_REG_LEN_08BIT, 0x06}, + {0x7613, CRL_REG_LEN_08BIT, 0x5c}, + {0x7614, CRL_REG_LEN_08BIT, 0x00}, + {0x7615, CRL_REG_LEN_08BIT, 0x0f}, + {0x7616, CRL_REG_LEN_08BIT, 0x06}, + {0x7617, CRL_REG_LEN_08BIT, 0x1c}, + {0x7618, CRL_REG_LEN_08BIT, 0x00}, + {0x7619, CRL_REG_LEN_08BIT, 0x02}, + {0x761a, CRL_REG_LEN_08BIT, 0x06}, + {0x761b, CRL_REG_LEN_08BIT, 0xa2}, + {0x761c, CRL_REG_LEN_08BIT, 0x00}, + {0x761d, CRL_REG_LEN_08BIT, 0x01}, + {0x761e, CRL_REG_LEN_08BIT, 0x06}, + {0x761f, CRL_REG_LEN_08BIT, 0xae}, + {0x7620, CRL_REG_LEN_08BIT, 0x00}, + {0x7621, CRL_REG_LEN_08BIT, 0x0e}, + {0x7622, CRL_REG_LEN_08BIT, 0x05}, + {0x7623, CRL_REG_LEN_08BIT, 0x30}, + {0x7624, CRL_REG_LEN_08BIT, 0x07}, + {0x7625, CRL_REG_LEN_08BIT, 0x00}, + {0x7626, CRL_REG_LEN_08BIT, 0x0f}, + {0x7627, CRL_REG_LEN_08BIT, 0x00}, + {0x7628, CRL_REG_LEN_08BIT, 0x04}, + {0x7629, CRL_REG_LEN_08BIT, 0xe5}, + {0x762a, CRL_REG_LEN_08BIT, 0x05}, + {0x762b, CRL_REG_LEN_08BIT, 0x33}, + {0x762c, CRL_REG_LEN_08BIT, 0x06}, + {0x762d, CRL_REG_LEN_08BIT, 0x12}, + {0x762e, CRL_REG_LEN_08BIT, 0x00}, + {0x762f, CRL_REG_LEN_08BIT, 0x01}, + {0x7630, CRL_REG_LEN_08BIT, 0x06}, + {0x7631, CRL_REG_LEN_08BIT, 0x52}, + {0x7632, CRL_REG_LEN_08BIT, 0x00}, + {0x7633, CRL_REG_LEN_08BIT, 0x01}, + {0x7634, CRL_REG_LEN_08BIT, 0x06}, + {0x7635, CRL_REG_LEN_08BIT, 0x5e}, + {0x7636, CRL_REG_LEN_08BIT, 0x04}, + {0x7637, CRL_REG_LEN_08BIT, 0xe4}, + {0x7638, CRL_REG_LEN_08BIT, 0x00}, + {0x7639, CRL_REG_LEN_08BIT, 0x01}, + {0x763a, CRL_REG_LEN_08BIT, 0x05}, + {0x763b, CRL_REG_LEN_08BIT, 0x30}, + {0x763c, CRL_REG_LEN_08BIT, 0x0f}, + {0x763d, CRL_REG_LEN_08BIT, 0x00}, + {0x763e, CRL_REG_LEN_08BIT, 0x06}, + {0x763f, CRL_REG_LEN_08BIT, 0xa6}, + {0x7640, CRL_REG_LEN_08BIT, 0x00}, + {0x7641, CRL_REG_LEN_08BIT, 0x02}, + {0x7642, CRL_REG_LEN_08BIT, 0x06}, + {0x7643, CRL_REG_LEN_08BIT, 0x26}, + {0x7644, CRL_REG_LEN_08BIT, 0x00}, + {0x7645, CRL_REG_LEN_08BIT, 0x02}, + {0x7646, CRL_REG_LEN_08BIT, 0x05}, + {0x7647, CRL_REG_LEN_08BIT, 0x33}, + {0x7648, CRL_REG_LEN_08BIT, 0x06}, + {0x7649, CRL_REG_LEN_08BIT, 0x20}, + {0x764a, CRL_REG_LEN_08BIT, 0x0f}, + {0x764b, CRL_REG_LEN_08BIT, 0x00}, + {0x764c, CRL_REG_LEN_08BIT, 0x06}, + {0x764d, CRL_REG_LEN_08BIT, 0x56}, + {0x764e, CRL_REG_LEN_08BIT, 0x00}, + {0x764f, CRL_REG_LEN_08BIT, 0x02}, + {0x7650, CRL_REG_LEN_08BIT, 0x06}, + {0x7651, CRL_REG_LEN_08BIT, 0x16}, + {0x7652, CRL_REG_LEN_08BIT, 0x05}, + {0x7653, CRL_REG_LEN_08BIT, 0x33}, + {0x7654, CRL_REG_LEN_08BIT, 0x06}, + {0x7655, CRL_REG_LEN_08BIT, 0x10}, + {0x7656, CRL_REG_LEN_08BIT, 0x0f}, + {0x7657, CRL_REG_LEN_08BIT, 0x00}, + {0x7658, CRL_REG_LEN_08BIT, 0x06}, + {0x7659, CRL_REG_LEN_08BIT, 0x10}, + {0x765a, CRL_REG_LEN_08BIT, 0x0f}, + {0x765b, CRL_REG_LEN_08BIT, 0x00}, + {0x765c, CRL_REG_LEN_08BIT, 0x06}, + {0x765d, CRL_REG_LEN_08BIT, 0x20}, + {0x765e, CRL_REG_LEN_08BIT, 0x0f}, + {0x765f, CRL_REG_LEN_08BIT, 0x00}, + {0x7660, CRL_REG_LEN_08BIT, 0x00}, + {0x7661, CRL_REG_LEN_08BIT, 0x00}, + {0x7662, CRL_REG_LEN_08BIT, 0x00}, + {0x7663, CRL_REG_LEN_08BIT, 0x02}, + {0x7664, CRL_REG_LEN_08BIT, 0x04}, + {0x7665, CRL_REG_LEN_08BIT, 0xe5}, + {0x7666, CRL_REG_LEN_08BIT, 0x04}, + {0x7667, CRL_REG_LEN_08BIT, 0xe4}, + {0x7668, CRL_REG_LEN_08BIT, 0x0f}, + {0x7669, CRL_REG_LEN_08BIT, 0x00}, + {0x766a, CRL_REG_LEN_08BIT, 0x00}, + {0x766b, CRL_REG_LEN_08BIT, 0x00}, + {0x766c, CRL_REG_LEN_08BIT, 0x00}, + {0x766d, CRL_REG_LEN_08BIT, 0x01}, + {0x766e, CRL_REG_LEN_08BIT, 0x04}, + {0x766f, CRL_REG_LEN_08BIT, 0xe5}, + {0x7670, CRL_REG_LEN_08BIT, 0x04}, + {0x7671, CRL_REG_LEN_08BIT, 0xe4}, + {0x7672, CRL_REG_LEN_08BIT, 0x0f}, + {0x7673, CRL_REG_LEN_08BIT, 0x00}, + {0x7674, CRL_REG_LEN_08BIT, 0x00}, + {0x7675, CRL_REG_LEN_08BIT, 0x02}, + {0x7676, CRL_REG_LEN_08BIT, 0x04}, + {0x7677, CRL_REG_LEN_08BIT, 0xe4}, + {0x7678, CRL_REG_LEN_08BIT, 0x00}, + {0x7679, CRL_REG_LEN_08BIT, 0x02}, + {0x767a, CRL_REG_LEN_08BIT, 0x04}, + {0x767b, CRL_REG_LEN_08BIT, 0xc4}, + {0x767c, CRL_REG_LEN_08BIT, 0x00}, + {0x767d, CRL_REG_LEN_08BIT, 0x02}, + {0x767e, CRL_REG_LEN_08BIT, 0x04}, + {0x767f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7680, CRL_REG_LEN_08BIT, 0x05}, + {0x7681, CRL_REG_LEN_08BIT, 0x83}, + {0x7682, CRL_REG_LEN_08BIT, 0x0f}, + {0x7683, CRL_REG_LEN_08BIT, 0x00}, + {0x7684, CRL_REG_LEN_08BIT, 0x00}, + {0x7685, CRL_REG_LEN_08BIT, 0x02}, + {0x7686, CRL_REG_LEN_08BIT, 0x04}, + {0x7687, CRL_REG_LEN_08BIT, 0xe4}, + {0x7688, CRL_REG_LEN_08BIT, 0x00}, + {0x7689, CRL_REG_LEN_08BIT, 0x02}, + {0x768a, CRL_REG_LEN_08BIT, 0x04}, + {0x768b, CRL_REG_LEN_08BIT, 0xc4}, + {0x768c, CRL_REG_LEN_08BIT, 0x00}, + {0x768d, CRL_REG_LEN_08BIT, 0x02}, + {0x768e, CRL_REG_LEN_08BIT, 0x04}, + {0x768f, CRL_REG_LEN_08BIT, 0xc4}, + {0x7690, CRL_REG_LEN_08BIT, 0x05}, + {0x7691, CRL_REG_LEN_08BIT, 0x83}, + {0x7692, CRL_REG_LEN_08BIT, 0x03}, + {0x7693, CRL_REG_LEN_08BIT, 0x0b}, + {0x7694, CRL_REG_LEN_08BIT, 0x05}, + {0x7695, CRL_REG_LEN_08BIT, 0x83}, + {0x7696, CRL_REG_LEN_08BIT, 0x00}, + {0x7697, CRL_REG_LEN_08BIT, 0x07}, + {0x7698, CRL_REG_LEN_08BIT, 0x05}, + {0x7699, CRL_REG_LEN_08BIT, 0x03}, + {0x769a, CRL_REG_LEN_08BIT, 0x00}, + {0x769b, CRL_REG_LEN_08BIT, 0x05}, + {0x769c, CRL_REG_LEN_08BIT, 0x05}, + {0x769d, CRL_REG_LEN_08BIT, 0x32}, + {0x769e, CRL_REG_LEN_08BIT, 0x05}, + {0x769f, CRL_REG_LEN_08BIT, 0x30}, + {0x76a0, CRL_REG_LEN_08BIT, 0x00}, + {0x76a1, CRL_REG_LEN_08BIT, 0x02}, + {0x76a2, CRL_REG_LEN_08BIT, 0x05}, + {0x76a3, CRL_REG_LEN_08BIT, 0x78}, + {0x76a4, CRL_REG_LEN_08BIT, 0x00}, + {0x76a5, CRL_REG_LEN_08BIT, 0x01}, + {0x76a6, CRL_REG_LEN_08BIT, 0x05}, + {0x76a7, CRL_REG_LEN_08BIT, 0x7c}, + {0x76a8, CRL_REG_LEN_08BIT, 0x03}, + {0x76a9, CRL_REG_LEN_08BIT, 0x9a}, + {0x76aa, CRL_REG_LEN_08BIT, 0x05}, + {0x76ab, CRL_REG_LEN_08BIT, 0x83}, + {0x76ac, CRL_REG_LEN_08BIT, 0x00}, + {0x76ad, CRL_REG_LEN_08BIT, 0x04}, + {0x76ae, CRL_REG_LEN_08BIT, 0x05}, + {0x76af, CRL_REG_LEN_08BIT, 0x03}, + {0x76b0, CRL_REG_LEN_08BIT, 0x00}, + {0x76b1, CRL_REG_LEN_08BIT, 0x03}, + {0x76b2, CRL_REG_LEN_08BIT, 0x05}, + {0x76b3, CRL_REG_LEN_08BIT, 0x32}, + {0x76b4, CRL_REG_LEN_08BIT, 0x05}, + {0x76b5, CRL_REG_LEN_08BIT, 0x30}, + {0x76b6, CRL_REG_LEN_08BIT, 0x00}, + {0x76b7, CRL_REG_LEN_08BIT, 0x02}, + {0x76b8, CRL_REG_LEN_08BIT, 0x05}, + {0x76b9, CRL_REG_LEN_08BIT, 0x78}, + {0x76ba, CRL_REG_LEN_08BIT, 0x00}, + {0x76bb, CRL_REG_LEN_08BIT, 0x01}, + {0x76bc, CRL_REG_LEN_08BIT, 0x05}, + {0x76bd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76be, CRL_REG_LEN_08BIT, 0x03}, + {0x76bf, CRL_REG_LEN_08BIT, 0x99}, + {0x76c0, CRL_REG_LEN_08BIT, 0x05}, + {0x76c1, CRL_REG_LEN_08BIT, 0x83}, + {0x76c2, CRL_REG_LEN_08BIT, 0x00}, + {0x76c3, CRL_REG_LEN_08BIT, 0x03}, + {0x76c4, CRL_REG_LEN_08BIT, 0x05}, + {0x76c5, CRL_REG_LEN_08BIT, 0x03}, + {0x76c6, CRL_REG_LEN_08BIT, 0x00}, + {0x76c7, CRL_REG_LEN_08BIT, 0x01}, + {0x76c8, CRL_REG_LEN_08BIT, 0x05}, + {0x76c9, CRL_REG_LEN_08BIT, 0x32}, + {0x76ca, CRL_REG_LEN_08BIT, 0x05}, + {0x76cb, CRL_REG_LEN_08BIT, 0x30}, + {0x76cc, CRL_REG_LEN_08BIT, 0x00}, + {0x76cd, CRL_REG_LEN_08BIT, 0x02}, + {0x76ce, CRL_REG_LEN_08BIT, 0x05}, + {0x76cf, CRL_REG_LEN_08BIT, 0x78}, + {0x76d0, CRL_REG_LEN_08BIT, 0x00}, + {0x76d1, CRL_REG_LEN_08BIT, 0x01}, + {0x76d2, CRL_REG_LEN_08BIT, 0x05}, + {0x76d3, CRL_REG_LEN_08BIT, 0x7c}, + {0x76d4, CRL_REG_LEN_08BIT, 0x03}, + {0x76d5, CRL_REG_LEN_08BIT, 0x98}, + {0x76d6, CRL_REG_LEN_08BIT, 0x05}, + {0x76d7, CRL_REG_LEN_08BIT, 0x83}, + {0x76d8, CRL_REG_LEN_08BIT, 0x00}, + {0x76d9, CRL_REG_LEN_08BIT, 0x00}, + {0x76da, CRL_REG_LEN_08BIT, 0x05}, + {0x76db, CRL_REG_LEN_08BIT, 0x03}, + {0x76dc, CRL_REG_LEN_08BIT, 0x00}, + {0x76dd, CRL_REG_LEN_08BIT, 0x01}, + {0x76de, CRL_REG_LEN_08BIT, 0x05}, + {0x76df, CRL_REG_LEN_08BIT, 0x32}, + {0x76e0, CRL_REG_LEN_08BIT, 0x05}, + {0x76e1, CRL_REG_LEN_08BIT, 0x30}, + {0x76e2, CRL_REG_LEN_08BIT, 0x00}, + {0x76e3, CRL_REG_LEN_08BIT, 0x02}, + {0x76e4, CRL_REG_LEN_08BIT, 0x05}, + {0x76e5, CRL_REG_LEN_08BIT, 0x78}, + {0x76e6, CRL_REG_LEN_08BIT, 0x00}, + {0x76e7, CRL_REG_LEN_08BIT, 0x01}, + {0x76e8, CRL_REG_LEN_08BIT, 0x05}, + {0x76e9, CRL_REG_LEN_08BIT, 0x7c}, + {0x76ea, CRL_REG_LEN_08BIT, 0x03}, + {0x76eb, CRL_REG_LEN_08BIT, 0x97}, + {0x76ec, CRL_REG_LEN_08BIT, 0x05}, + {0x76ed, CRL_REG_LEN_08BIT, 0x83}, + {0x76ee, CRL_REG_LEN_08BIT, 0x00}, + {0x76ef, CRL_REG_LEN_08BIT, 0x00}, + {0x76f0, CRL_REG_LEN_08BIT, 0x05}, + {0x76f1, CRL_REG_LEN_08BIT, 0x03}, + {0x76f2, CRL_REG_LEN_08BIT, 0x05}, + {0x76f3, CRL_REG_LEN_08BIT, 0x32}, + {0x76f4, CRL_REG_LEN_08BIT, 0x05}, + {0x76f5, CRL_REG_LEN_08BIT, 0x30}, + {0x76f6, CRL_REG_LEN_08BIT, 0x00}, + {0x76f7, CRL_REG_LEN_08BIT, 0x02}, + {0x76f8, CRL_REG_LEN_08BIT, 0x05}, + {0x76f9, CRL_REG_LEN_08BIT, 0x78}, + {0x76fa, CRL_REG_LEN_08BIT, 0x00}, + {0x76fb, CRL_REG_LEN_08BIT, 0x01}, + {0x76fc, CRL_REG_LEN_08BIT, 0x05}, + {0x76fd, CRL_REG_LEN_08BIT, 0x7c}, + {0x76fe, CRL_REG_LEN_08BIT, 0x03}, + {0x76ff, CRL_REG_LEN_08BIT, 0x96}, + {0x7700, CRL_REG_LEN_08BIT, 0x05}, + {0x7701, CRL_REG_LEN_08BIT, 0x83}, + {0x7702, CRL_REG_LEN_08BIT, 0x05}, + {0x7703, CRL_REG_LEN_08BIT, 0x03}, + {0x7704, CRL_REG_LEN_08BIT, 0x05}, + {0x7705, CRL_REG_LEN_08BIT, 0x32}, + {0x7706, CRL_REG_LEN_08BIT, 0x05}, + {0x7707, CRL_REG_LEN_08BIT, 0x30}, + {0x7708, CRL_REG_LEN_08BIT, 0x00}, + {0x7709, CRL_REG_LEN_08BIT, 0x02}, + {0x770a, CRL_REG_LEN_08BIT, 0x05}, + {0x770b, CRL_REG_LEN_08BIT, 0x78}, + {0x770c, CRL_REG_LEN_08BIT, 0x00}, + {0x770d, CRL_REG_LEN_08BIT, 0x01}, + {0x770e, CRL_REG_LEN_08BIT, 0x05}, + {0x770f, CRL_REG_LEN_08BIT, 0x7c}, + {0x7710, CRL_REG_LEN_08BIT, 0x03}, + {0x7711, CRL_REG_LEN_08BIT, 0x95}, + {0x7712, CRL_REG_LEN_08BIT, 0x05}, + {0x7713, CRL_REG_LEN_08BIT, 0x83}, + {0x7714, CRL_REG_LEN_08BIT, 0x05}, + {0x7715, CRL_REG_LEN_08BIT, 0x03}, + {0x7716, CRL_REG_LEN_08BIT, 0x05}, + {0x7717, CRL_REG_LEN_08BIT, 0x32}, + {0x7718, CRL_REG_LEN_08BIT, 0x05}, + {0x7719, CRL_REG_LEN_08BIT, 0x30}, + {0x771a, CRL_REG_LEN_08BIT, 0x00}, + {0x771b, CRL_REG_LEN_08BIT, 0x02}, + {0x771c, CRL_REG_LEN_08BIT, 0x05}, + {0x771d, CRL_REG_LEN_08BIT, 0x78}, + {0x771e, CRL_REG_LEN_08BIT, 0x00}, + {0x771f, CRL_REG_LEN_08BIT, 0x01}, + {0x7720, CRL_REG_LEN_08BIT, 0x05}, + {0x7721, CRL_REG_LEN_08BIT, 0x7c}, + {0x7722, CRL_REG_LEN_08BIT, 0x03}, + {0x7723, CRL_REG_LEN_08BIT, 0x94}, + {0x7724, CRL_REG_LEN_08BIT, 0x05}, + {0x7725, CRL_REG_LEN_08BIT, 0x83}, + {0x7726, CRL_REG_LEN_08BIT, 0x00}, + {0x7727, CRL_REG_LEN_08BIT, 0x01}, + {0x7728, CRL_REG_LEN_08BIT, 0x05}, + {0x7729, CRL_REG_LEN_08BIT, 0x03}, + {0x772a, CRL_REG_LEN_08BIT, 0x00}, + {0x772b, CRL_REG_LEN_08BIT, 0x01}, + {0x772c, CRL_REG_LEN_08BIT, 0x05}, + {0x772d, CRL_REG_LEN_08BIT, 0x32}, + {0x772e, CRL_REG_LEN_08BIT, 0x05}, + {0x772f, CRL_REG_LEN_08BIT, 0x30}, + {0x7730, CRL_REG_LEN_08BIT, 0x00}, + {0x7731, CRL_REG_LEN_08BIT, 0x02}, + {0x7732, CRL_REG_LEN_08BIT, 0x05}, + {0x7733, CRL_REG_LEN_08BIT, 0x78}, + {0x7734, CRL_REG_LEN_08BIT, 0x00}, + {0x7735, CRL_REG_LEN_08BIT, 0x01}, + {0x7736, CRL_REG_LEN_08BIT, 0x05}, + {0x7737, CRL_REG_LEN_08BIT, 0x7c}, + {0x7738, CRL_REG_LEN_08BIT, 0x03}, + {0x7739, CRL_REG_LEN_08BIT, 0x93}, + {0x773a, CRL_REG_LEN_08BIT, 0x05}, + {0x773b, CRL_REG_LEN_08BIT, 0x83}, + {0x773c, CRL_REG_LEN_08BIT, 0x00}, + {0x773d, CRL_REG_LEN_08BIT, 0x00}, + {0x773e, CRL_REG_LEN_08BIT, 0x05}, + {0x773f, CRL_REG_LEN_08BIT, 0x03}, + {0x7740, CRL_REG_LEN_08BIT, 0x00}, + {0x7741, CRL_REG_LEN_08BIT, 0x00}, + {0x7742, CRL_REG_LEN_08BIT, 0x05}, + {0x7743, CRL_REG_LEN_08BIT, 0x32}, + {0x7744, CRL_REG_LEN_08BIT, 0x05}, + {0x7745, CRL_REG_LEN_08BIT, 0x30}, + {0x7746, CRL_REG_LEN_08BIT, 0x00}, + {0x7747, CRL_REG_LEN_08BIT, 0x02}, + {0x7748, CRL_REG_LEN_08BIT, 0x05}, + {0x7749, CRL_REG_LEN_08BIT, 0x78}, + {0x774a, CRL_REG_LEN_08BIT, 0x00}, + {0x774b, CRL_REG_LEN_08BIT, 0x01}, + {0x774c, CRL_REG_LEN_08BIT, 0x05}, + {0x774d, CRL_REG_LEN_08BIT, 0x7c}, + {0x774e, CRL_REG_LEN_08BIT, 0x03}, + {0x774f, CRL_REG_LEN_08BIT, 0x92}, + {0x7750, CRL_REG_LEN_08BIT, 0x05}, + {0x7751, CRL_REG_LEN_08BIT, 0x83}, + {0x7752, CRL_REG_LEN_08BIT, 0x05}, + {0x7753, CRL_REG_LEN_08BIT, 0x03}, + {0x7754, CRL_REG_LEN_08BIT, 0x00}, + {0x7755, CRL_REG_LEN_08BIT, 0x00}, + {0x7756, CRL_REG_LEN_08BIT, 0x05}, + {0x7757, CRL_REG_LEN_08BIT, 0x32}, + {0x7758, CRL_REG_LEN_08BIT, 0x05}, + {0x7759, CRL_REG_LEN_08BIT, 0x30}, + {0x775a, CRL_REG_LEN_08BIT, 0x00}, + {0x775b, CRL_REG_LEN_08BIT, 0x02}, + {0x775c, CRL_REG_LEN_08BIT, 0x05}, + {0x775d, CRL_REG_LEN_08BIT, 0x78}, + {0x775e, CRL_REG_LEN_08BIT, 0x00}, + {0x775f, CRL_REG_LEN_08BIT, 0x01}, + {0x7760, CRL_REG_LEN_08BIT, 0x05}, + {0x7761, CRL_REG_LEN_08BIT, 0x7c}, + {0x7762, CRL_REG_LEN_08BIT, 0x03}, + {0x7763, CRL_REG_LEN_08BIT, 0x91}, + {0x7764, CRL_REG_LEN_08BIT, 0x05}, + {0x7765, CRL_REG_LEN_08BIT, 0x83}, + {0x7766, CRL_REG_LEN_08BIT, 0x05}, + {0x7767, CRL_REG_LEN_08BIT, 0x03}, + {0x7768, CRL_REG_LEN_08BIT, 0x05}, + {0x7769, CRL_REG_LEN_08BIT, 0x32}, + {0x776a, CRL_REG_LEN_08BIT, 0x05}, + {0x776b, CRL_REG_LEN_08BIT, 0x30}, + {0x776c, CRL_REG_LEN_08BIT, 0x00}, + {0x776d, CRL_REG_LEN_08BIT, 0x02}, + {0x776e, CRL_REG_LEN_08BIT, 0x05}, + {0x776f, CRL_REG_LEN_08BIT, 0x78}, + {0x7770, CRL_REG_LEN_08BIT, 0x00}, + {0x7771, CRL_REG_LEN_08BIT, 0x01}, + {0x7772, CRL_REG_LEN_08BIT, 0x05}, + {0x7773, CRL_REG_LEN_08BIT, 0x7c}, + {0x7774, CRL_REG_LEN_08BIT, 0x03}, + {0x7775, CRL_REG_LEN_08BIT, 0x90}, + {0x7776, CRL_REG_LEN_08BIT, 0x05}, + {0x7777, CRL_REG_LEN_08BIT, 0x83}, + {0x7778, CRL_REG_LEN_08BIT, 0x05}, + {0x7779, CRL_REG_LEN_08BIT, 0x03}, + {0x777a, CRL_REG_LEN_08BIT, 0x05}, + {0x777b, CRL_REG_LEN_08BIT, 0x32}, + {0x777c, CRL_REG_LEN_08BIT, 0x05}, + {0x777d, CRL_REG_LEN_08BIT, 0x30}, + {0x777e, CRL_REG_LEN_08BIT, 0x00}, + {0x777f, CRL_REG_LEN_08BIT, 0x02}, + {0x7780, CRL_REG_LEN_08BIT, 0x05}, + {0x7781, CRL_REG_LEN_08BIT, 0x78}, + {0x7782, CRL_REG_LEN_08BIT, 0x00}, + {0x7783, CRL_REG_LEN_08BIT, 0x01}, + {0x7784, CRL_REG_LEN_08BIT, 0x05}, + {0x7785, CRL_REG_LEN_08BIT, 0x7c}, + {0x7786, CRL_REG_LEN_08BIT, 0x02}, + {0x7787, CRL_REG_LEN_08BIT, 0x90}, + {0x7788, CRL_REG_LEN_08BIT, 0x05}, + {0x7789, CRL_REG_LEN_08BIT, 0x03}, + {0x778a, CRL_REG_LEN_08BIT, 0x07}, + {0x778b, CRL_REG_LEN_08BIT, 0x00}, + {0x778c, CRL_REG_LEN_08BIT, 0x0f}, + {0x778d, CRL_REG_LEN_08BIT, 0x00}, + {0x778e, CRL_REG_LEN_08BIT, 0x08}, + {0x778f, CRL_REG_LEN_08BIT, 0x30}, + {0x7790, CRL_REG_LEN_08BIT, 0x08}, + {0x7791, CRL_REG_LEN_08BIT, 0xee}, + {0x7792, CRL_REG_LEN_08BIT, 0x0f}, + {0x7793, CRL_REG_LEN_08BIT, 0x00}, + {0x7794, CRL_REG_LEN_08BIT, 0x05}, + {0x7795, CRL_REG_LEN_08BIT, 0x33}, + {0x7796, CRL_REG_LEN_08BIT, 0x04}, + {0x7797, CRL_REG_LEN_08BIT, 0xe5}, + {0x7798, CRL_REG_LEN_08BIT, 0x06}, + {0x7799, CRL_REG_LEN_08BIT, 0x52}, + {0x779a, CRL_REG_LEN_08BIT, 0x04}, + {0x779b, CRL_REG_LEN_08BIT, 0xe4}, + {0x779c, CRL_REG_LEN_08BIT, 0x00}, + {0x779d, CRL_REG_LEN_08BIT, 0x00}, + {0x779e, CRL_REG_LEN_08BIT, 0x06}, + {0x779f, CRL_REG_LEN_08BIT, 0x5e}, + {0x77a0, CRL_REG_LEN_08BIT, 0x00}, + {0x77a1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77a2, CRL_REG_LEN_08BIT, 0x06}, + {0x77a3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77a4, CRL_REG_LEN_08BIT, 0x00}, + {0x77a5, CRL_REG_LEN_08BIT, 0x02}, + {0x77a6, CRL_REG_LEN_08BIT, 0x06}, + {0x77a7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77a8, CRL_REG_LEN_08BIT, 0x00}, + {0x77a9, CRL_REG_LEN_08BIT, 0x01}, + {0x77aa, CRL_REG_LEN_08BIT, 0x06}, + {0x77ab, CRL_REG_LEN_08BIT, 0xae}, + {0x77ac, CRL_REG_LEN_08BIT, 0x00}, + {0x77ad, CRL_REG_LEN_08BIT, 0x03}, + {0x77ae, CRL_REG_LEN_08BIT, 0x05}, + {0x77af, CRL_REG_LEN_08BIT, 0x30}, + {0x77b0, CRL_REG_LEN_08BIT, 0x09}, + {0x77b1, CRL_REG_LEN_08BIT, 0x19}, + {0x77b2, CRL_REG_LEN_08BIT, 0x0f}, + {0x77b3, CRL_REG_LEN_08BIT, 0x00}, + {0x77b4, CRL_REG_LEN_08BIT, 0x05}, + {0x77b5, CRL_REG_LEN_08BIT, 0x33}, + {0x77b6, CRL_REG_LEN_08BIT, 0x04}, + {0x77b7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77b8, CRL_REG_LEN_08BIT, 0x06}, + {0x77b9, CRL_REG_LEN_08BIT, 0x52}, + {0x77ba, CRL_REG_LEN_08BIT, 0x04}, + {0x77bb, CRL_REG_LEN_08BIT, 0xe4}, + {0x77bc, CRL_REG_LEN_08BIT, 0x00}, + {0x77bd, CRL_REG_LEN_08BIT, 0x00}, + {0x77be, CRL_REG_LEN_08BIT, 0x06}, + {0x77bf, CRL_REG_LEN_08BIT, 0x5e}, + {0x77c0, CRL_REG_LEN_08BIT, 0x00}, + {0x77c1, CRL_REG_LEN_08BIT, 0x0f}, + {0x77c2, CRL_REG_LEN_08BIT, 0x06}, + {0x77c3, CRL_REG_LEN_08BIT, 0x1e}, + {0x77c4, CRL_REG_LEN_08BIT, 0x00}, + {0x77c5, CRL_REG_LEN_08BIT, 0x02}, + {0x77c6, CRL_REG_LEN_08BIT, 0x06}, + {0x77c7, CRL_REG_LEN_08BIT, 0xa2}, + {0x77c8, CRL_REG_LEN_08BIT, 0x00}, + {0x77c9, CRL_REG_LEN_08BIT, 0x01}, + {0x77ca, CRL_REG_LEN_08BIT, 0x06}, + {0x77cb, CRL_REG_LEN_08BIT, 0xae}, + {0x77cc, CRL_REG_LEN_08BIT, 0x00}, + {0x77cd, CRL_REG_LEN_08BIT, 0x03}, + {0x77ce, CRL_REG_LEN_08BIT, 0x05}, + {0x77cf, CRL_REG_LEN_08BIT, 0x30}, + {0x77d0, CRL_REG_LEN_08BIT, 0x0f}, + {0x77d1, CRL_REG_LEN_08BIT, 0x00}, + {0x77d2, CRL_REG_LEN_08BIT, 0x00}, + {0x77d3, CRL_REG_LEN_08BIT, 0x00}, + {0x77d4, CRL_REG_LEN_08BIT, 0x00}, + {0x77d5, CRL_REG_LEN_08BIT, 0x02}, + {0x77d6, CRL_REG_LEN_08BIT, 0x04}, + {0x77d7, CRL_REG_LEN_08BIT, 0xe5}, + {0x77d8, CRL_REG_LEN_08BIT, 0x04}, + {0x77d9, CRL_REG_LEN_08BIT, 0xe4}, + {0x77da, CRL_REG_LEN_08BIT, 0x05}, + {0x77db, CRL_REG_LEN_08BIT, 0x33}, + {0x77dc, CRL_REG_LEN_08BIT, 0x07}, + {0x77dd, CRL_REG_LEN_08BIT, 0x10}, + {0x77de, CRL_REG_LEN_08BIT, 0x00}, + {0x77df, CRL_REG_LEN_08BIT, 0x00}, + {0x77e0, CRL_REG_LEN_08BIT, 0x01}, + {0x77e1, CRL_REG_LEN_08BIT, 0xbb}, + {0x77e2, CRL_REG_LEN_08BIT, 0x00}, + {0x77e3, CRL_REG_LEN_08BIT, 0x00}, + {0x77e4, CRL_REG_LEN_08BIT, 0x01}, + {0x77e5, CRL_REG_LEN_08BIT, 0xaa}, + {0x77e6, CRL_REG_LEN_08BIT, 0x00}, + {0x77e7, CRL_REG_LEN_08BIT, 0x00}, + {0x77e8, CRL_REG_LEN_08BIT, 0x01}, + {0x77e9, CRL_REG_LEN_08BIT, 0x99}, + {0x77ea, CRL_REG_LEN_08BIT, 0x00}, + {0x77eb, CRL_REG_LEN_08BIT, 0x00}, + {0x77ec, CRL_REG_LEN_08BIT, 0x01}, + {0x77ed, CRL_REG_LEN_08BIT, 0x88}, + {0x77ee, CRL_REG_LEN_08BIT, 0x00}, + {0x77ef, CRL_REG_LEN_08BIT, 0x00}, + {0x77f0, CRL_REG_LEN_08BIT, 0x01}, + {0x77f1, CRL_REG_LEN_08BIT, 0x77}, + {0x77f2, CRL_REG_LEN_08BIT, 0x00}, + {0x77f3, CRL_REG_LEN_08BIT, 0x00}, + {0x77f4, CRL_REG_LEN_08BIT, 0x01}, + {0x77f5, CRL_REG_LEN_08BIT, 0x66}, + {0x77f6, CRL_REG_LEN_08BIT, 0x00}, + {0x77f7, CRL_REG_LEN_08BIT, 0x00}, + {0x77f8, CRL_REG_LEN_08BIT, 0x01}, + {0x77f9, CRL_REG_LEN_08BIT, 0x55}, + {0x77fa, CRL_REG_LEN_08BIT, 0x00}, + {0x77fb, CRL_REG_LEN_08BIT, 0x00}, + {0x77fc, CRL_REG_LEN_08BIT, 0x01}, + {0x77fd, CRL_REG_LEN_08BIT, 0x44}, + {0x77fe, CRL_REG_LEN_08BIT, 0x00}, + {0x77ff, CRL_REG_LEN_08BIT, 0x00}, + {0x7800, CRL_REG_LEN_08BIT, 0x01}, + {0x7801, CRL_REG_LEN_08BIT, 0x33}, + {0x7802, CRL_REG_LEN_08BIT, 0x00}, + {0x7803, CRL_REG_LEN_08BIT, 0x00}, + {0x7804, CRL_REG_LEN_08BIT, 0x01}, + {0x7805, CRL_REG_LEN_08BIT, 0x22}, + {0x7806, CRL_REG_LEN_08BIT, 0x00}, + {0x7807, CRL_REG_LEN_08BIT, 0x00}, + {0x7808, CRL_REG_LEN_08BIT, 0x01}, + {0x7809, CRL_REG_LEN_08BIT, 0x11}, + {0x780a, CRL_REG_LEN_08BIT, 0x00}, + {0x780b, CRL_REG_LEN_08BIT, 0x00}, + {0x780c, CRL_REG_LEN_08BIT, 0x01}, + {0x780d, CRL_REG_LEN_08BIT, 0x00}, + {0x780e, CRL_REG_LEN_08BIT, 0x01}, + {0x780f, CRL_REG_LEN_08BIT, 0xff}, + {0x7810, CRL_REG_LEN_08BIT, 0x07}, + {0x7811, CRL_REG_LEN_08BIT, 0x00}, + {0x7812, CRL_REG_LEN_08BIT, 0x02}, + {0x7813, CRL_REG_LEN_08BIT, 0xa0}, + {0x7814, CRL_REG_LEN_08BIT, 0x0f}, + {0x7815, CRL_REG_LEN_08BIT, 0x00}, + {0x7816, CRL_REG_LEN_08BIT, 0x08}, + {0x7817, CRL_REG_LEN_08BIT, 0x35}, + {0x7818, CRL_REG_LEN_08BIT, 0x06}, + {0x7819, CRL_REG_LEN_08BIT, 0x52}, + {0x781a, CRL_REG_LEN_08BIT, 0x04}, + {0x781b, CRL_REG_LEN_08BIT, 0xe4}, + {0x781c, CRL_REG_LEN_08BIT, 0x00}, + {0x781d, CRL_REG_LEN_08BIT, 0x00}, + {0x781e, CRL_REG_LEN_08BIT, 0x06}, + {0x781f, CRL_REG_LEN_08BIT, 0x5e}, + {0x7820, CRL_REG_LEN_08BIT, 0x05}, + {0x7821, CRL_REG_LEN_08BIT, 0x33}, + {0x7822, CRL_REG_LEN_08BIT, 0x09}, + {0x7823, CRL_REG_LEN_08BIT, 0x19}, + {0x7824, CRL_REG_LEN_08BIT, 0x06}, + {0x7825, CRL_REG_LEN_08BIT, 0x1e}, + {0x7826, CRL_REG_LEN_08BIT, 0x05}, + {0x7827, CRL_REG_LEN_08BIT, 0x33}, + {0x7828, CRL_REG_LEN_08BIT, 0x00}, + {0x7829, CRL_REG_LEN_08BIT, 0x01}, + {0x782a, CRL_REG_LEN_08BIT, 0x06}, + {0x782b, CRL_REG_LEN_08BIT, 0x24}, + {0x782c, CRL_REG_LEN_08BIT, 0x06}, + {0x782d, CRL_REG_LEN_08BIT, 0x20}, + {0x782e, CRL_REG_LEN_08BIT, 0x0f}, + {0x782f, CRL_REG_LEN_08BIT, 0x00}, + {0x7830, CRL_REG_LEN_08BIT, 0x08}, + {0x7831, CRL_REG_LEN_08BIT, 0x35}, + {0x7832, CRL_REG_LEN_08BIT, 0x07}, + {0x7833, CRL_REG_LEN_08BIT, 0x10}, + {0x7834, CRL_REG_LEN_08BIT, 0x00}, + {0x7835, CRL_REG_LEN_08BIT, 0x00}, + {0x7836, CRL_REG_LEN_08BIT, 0x01}, + {0x7837, CRL_REG_LEN_08BIT, 0xbb}, + {0x7838, CRL_REG_LEN_08BIT, 0x00}, + {0x7839, CRL_REG_LEN_08BIT, 0x00}, + {0x783a, CRL_REG_LEN_08BIT, 0x01}, + {0x783b, CRL_REG_LEN_08BIT, 0xaa}, + {0x783c, CRL_REG_LEN_08BIT, 0x00}, + {0x783d, CRL_REG_LEN_08BIT, 0x00}, + {0x783e, CRL_REG_LEN_08BIT, 0x01}, + {0x783f, CRL_REG_LEN_08BIT, 0x99}, + {0x7840, CRL_REG_LEN_08BIT, 0x00}, + {0x7841, CRL_REG_LEN_08BIT, 0x00}, + {0x7842, CRL_REG_LEN_08BIT, 0x01}, + {0x7843, CRL_REG_LEN_08BIT, 0x88}, + {0x7844, CRL_REG_LEN_08BIT, 0x00}, + {0x7845, CRL_REG_LEN_08BIT, 0x00}, + {0x7846, CRL_REG_LEN_08BIT, 0x01}, + {0x7847, CRL_REG_LEN_08BIT, 0x77}, + {0x7848, CRL_REG_LEN_08BIT, 0x00}, + {0x7849, CRL_REG_LEN_08BIT, 0x00}, + {0x784a, CRL_REG_LEN_08BIT, 0x01}, + {0x784b, CRL_REG_LEN_08BIT, 0x66}, + {0x784c, CRL_REG_LEN_08BIT, 0x00}, + {0x784d, CRL_REG_LEN_08BIT, 0x00}, + {0x784e, CRL_REG_LEN_08BIT, 0x01}, + {0x784f, CRL_REG_LEN_08BIT, 0x55}, + {0x7850, CRL_REG_LEN_08BIT, 0x00}, + {0x7851, CRL_REG_LEN_08BIT, 0x00}, + {0x7852, CRL_REG_LEN_08BIT, 0x01}, + {0x7853, CRL_REG_LEN_08BIT, 0x44}, + {0x7854, CRL_REG_LEN_08BIT, 0x00}, + {0x7855, CRL_REG_LEN_08BIT, 0x00}, + {0x7856, CRL_REG_LEN_08BIT, 0x01}, + {0x7857, CRL_REG_LEN_08BIT, 0x33}, + {0x7858, CRL_REG_LEN_08BIT, 0x00}, + {0x7859, CRL_REG_LEN_08BIT, 0x00}, + {0x785a, CRL_REG_LEN_08BIT, 0x01}, + {0x785b, CRL_REG_LEN_08BIT, 0x22}, + {0x785c, CRL_REG_LEN_08BIT, 0x00}, + {0x785d, CRL_REG_LEN_08BIT, 0x00}, + {0x785e, CRL_REG_LEN_08BIT, 0x01}, + {0x785f, CRL_REG_LEN_08BIT, 0x11}, + {0x7860, CRL_REG_LEN_08BIT, 0x00}, + {0x7861, CRL_REG_LEN_08BIT, 0x00}, + {0x7862, CRL_REG_LEN_08BIT, 0x01}, + {0x7863, CRL_REG_LEN_08BIT, 0x00}, + {0x7864, CRL_REG_LEN_08BIT, 0x07}, + {0x7865, CRL_REG_LEN_08BIT, 0x00}, + {0x7866, CRL_REG_LEN_08BIT, 0x01}, + {0x7867, CRL_REG_LEN_08BIT, 0xff}, + {0x7868, CRL_REG_LEN_08BIT, 0x02}, + {0x7869, CRL_REG_LEN_08BIT, 0xa0}, + {0x786a, CRL_REG_LEN_08BIT, 0x0f}, + {0x786b, CRL_REG_LEN_08BIT, 0x00}, + {0x786c, CRL_REG_LEN_08BIT, 0x08}, + {0x786d, CRL_REG_LEN_08BIT, 0x3a}, + {0x786e, CRL_REG_LEN_08BIT, 0x08}, + {0x786f, CRL_REG_LEN_08BIT, 0x6a}, + {0x7870, CRL_REG_LEN_08BIT, 0x0f}, + {0x7871, CRL_REG_LEN_08BIT, 0x00}, + {0x7872, CRL_REG_LEN_08BIT, 0x04}, + {0x7873, CRL_REG_LEN_08BIT, 0xc0}, + {0x7874, CRL_REG_LEN_08BIT, 0x09}, + {0x7875, CRL_REG_LEN_08BIT, 0x19}, + {0x7876, CRL_REG_LEN_08BIT, 0x04}, + {0x7877, CRL_REG_LEN_08BIT, 0x99}, + {0x7878, CRL_REG_LEN_08BIT, 0x07}, + {0x7879, CRL_REG_LEN_08BIT, 0x14}, + {0x787a, CRL_REG_LEN_08BIT, 0x00}, + {0x787b, CRL_REG_LEN_08BIT, 0x01}, + {0x787c, CRL_REG_LEN_08BIT, 0x04}, + {0x787d, CRL_REG_LEN_08BIT, 0xa4}, + {0x787e, CRL_REG_LEN_08BIT, 0x00}, + {0x787f, CRL_REG_LEN_08BIT, 0x07}, + {0x7880, CRL_REG_LEN_08BIT, 0x04}, + {0x7881, CRL_REG_LEN_08BIT, 0xa6}, + {0x7882, CRL_REG_LEN_08BIT, 0x00}, + {0x7883, CRL_REG_LEN_08BIT, 0x00}, + {0x7884, CRL_REG_LEN_08BIT, 0x04}, + {0x7885, CRL_REG_LEN_08BIT, 0xa0}, + {0x7886, CRL_REG_LEN_08BIT, 0x04}, + {0x7887, CRL_REG_LEN_08BIT, 0x80}, + {0x7888, CRL_REG_LEN_08BIT, 0x04}, + {0x7889, CRL_REG_LEN_08BIT, 0x00}, + {0x788a, CRL_REG_LEN_08BIT, 0x05}, + {0x788b, CRL_REG_LEN_08BIT, 0x03}, + {0x788c, CRL_REG_LEN_08BIT, 0x06}, + {0x788d, CRL_REG_LEN_08BIT, 0x00}, + {0x788e, CRL_REG_LEN_08BIT, 0x0f}, + {0x788f, CRL_REG_LEN_08BIT, 0x00}, + {0x7890, CRL_REG_LEN_08BIT, 0x0f}, + {0x7891, CRL_REG_LEN_08BIT, 0x00}, + {0x7892, CRL_REG_LEN_08BIT, 0x0f}, + {0x7893, CRL_REG_LEN_08BIT, 0x00}, + {0x3001, CRL_REG_LEN_08BIT, 0x23}, + {0x3005, CRL_REG_LEN_08BIT, 0x13}, + {0x3014, CRL_REG_LEN_08BIT, 0x44}, + {0x30a3, CRL_REG_LEN_08BIT, 0x00}, + {0x30a7, CRL_REG_LEN_08BIT, 0x48}, + {0x30ab, CRL_REG_LEN_08BIT, 0x04}, + {0x30af, CRL_REG_LEN_08BIT, 0x40}, + {0x30b0, CRL_REG_LEN_08BIT, 0x3e}, + {0x30b1, CRL_REG_LEN_08BIT, 0x9e}, + {0x30b2, CRL_REG_LEN_08BIT, 0x04}, + {0x30b3, CRL_REG_LEN_08BIT, 0x5a}, + {0x3196, CRL_REG_LEN_08BIT, 0x00}, + {0x3197, CRL_REG_LEN_08BIT, 0x00}, + {0x3195, CRL_REG_LEN_08BIT, 0x04}, + {0x31e3, CRL_REG_LEN_08BIT, 0x03}, + {0x31e4, CRL_REG_LEN_08BIT, 0x13}, + {0x30bb, CRL_REG_LEN_08BIT, 0x1a}, + {0x315a, CRL_REG_LEN_08BIT, 0x01}, + {0x315b, CRL_REG_LEN_08BIT, 0x00}, + {0x315c, CRL_REG_LEN_08BIT, 0x01}, + {0x315d, CRL_REG_LEN_08BIT, 0x00}, + {0x315e, CRL_REG_LEN_08BIT, 0x01}, + {0x315f, CRL_REG_LEN_08BIT, 0x00}, + {0x3250, CRL_REG_LEN_08BIT, 0xf7}, + {0x3012, CRL_REG_LEN_08BIT, 0x01}, +}; + +static struct crl_register_write_rep ov2775_powerup_standby_regset[] = { + { 0x3012, CRL_REG_LEN_08BIT, 0x00 } +}; + +struct crl_register_write_rep ov2775_poweroff_regset[] = { + { 0x3012, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep ov2775_streamon_regs[] = { + { 0x3012, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep ov2775_streamoff_regs[] = { + { 0x3012, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_arithmetic_ops ov2775_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 3, + }, +}; + +static struct crl_arithmetic_ops ov2775_vblank_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, +}; + +static struct crl_arithmetic_ops ov2775_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + }, +}; + +static struct crl_arithmetic_ops ov2775_hblank_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, +}; + +static struct crl_arithmetic_ops ov2775_exposure_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, +}; + +static struct crl_arithmetic_ops ov2775_ana_gain_l_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + }, +}; + +static struct crl_arithmetic_ops ov2775_ana_gain_vs_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, +}; + +static struct crl_arithmetic_ops ov2775_digital_gain_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, +}; + +static struct crl_dynamic_register_access ov2775_v_flip_regs[] = { + { + .address = 0x30C0, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2775_vflip_ops), + .ops = ov2775_vflip_ops, + .mask = 0x08, + }, +}; + +static struct crl_dynamic_register_access ov2775_h_flip_regs[] = { + { + .address = 0x30C0, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2775_hflip_ops), + .ops = ov2775_hflip_ops, + .mask = 0x04, + }, +}; + +/* 0: 1x, 1: 2x, 2: 4x, 3: 8x + * linear mode analog gain uses ana_gain_h + */ +static struct crl_dynamic_register_access ov2775_ana_gain_h_regs[] = { + { + .address = 0x30BB, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x03, + }, +}; + +static struct crl_dynamic_register_access ov2775_ana_gain_l_regs[] = { + { + .address = 0x30BB, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2775_ana_gain_l_ops), + .ops = ov2775_ana_gain_l_ops, + .mask = 0x0c, + }, +}; + +static struct crl_dynamic_register_access ov2775_ana_gain_vs_regs[] = { + { + .address = 0x30BB, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov2775_ana_gain_vs_ops), + .ops = ov2775_ana_gain_vs_ops, + .mask = 0x30, + }, +}; + +static struct crl_dynamic_register_access ov2775_digital_gain_h_regs[] = { + { + .address = 0x315A, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_digital_gain_ops), + .ops = ov2775_digital_gain_ops, + .mask = 0xff, + }, + { + .address = 0x315B, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov2775_digital_gain_l_regs[] = { + { + .address = 0x315C, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_digital_gain_ops), + .ops = ov2775_digital_gain_ops, + .mask = 0xff, + }, + { + .address = 0x315D, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov2775_digital_gain_vs_regs[] = { + { + .address = 0x315E, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_digital_gain_ops), + .ops = ov2775_digital_gain_ops, + .mask = 0xff, + }, + { + .address = 0x315F, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov2775_exposure_dcg_regs[] = { + { + .address = 0x30B6, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_exposure_ops), + .ops = ov2775_exposure_ops, + .mask = 0xff, + }, + { + .address = 0x30B7, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +/* 03B8 and 03B9 are integer part, 03BA is fractional part with N/32 + * just use integer part + */ +static struct crl_dynamic_register_access ov2775_exposure_vs_regs[] = { + { + .address = 0x30B8, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_exposure_ops), + .ops = ov2775_exposure_ops, + .mask = 0xff, + }, + { + .address = 0x30B9, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov2775_vblank_regs[] = { + { + .address = 0x30B2, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_vblank_ops), + .ops = ov2775_vblank_ops, + .mask = 0xff, + }, + { + .address = 0x30B3, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_dynamic_register_access ov2775_hblank_regs[] = { + { + .address = 0x30B0, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov2775_hblank_ops), + .ops = ov2775_hblank_ops, + .mask = 0xff, + }, + { + .address = 0x30B1, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_sensor_detect_config ov2775_sensor_detect_regset[] = { + { + .reg = { 0x300A, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +/* pixel_rate = op_sys_clk*2 * csi_lanes / bitsperpixel */ +static struct crl_pll_configuration ov2775_pll_configurations[] = { + { + .input_clk = 19200000, + .op_sys_clk = 202000000, + .bitsperpixel = 10, + .pixel_rate_csi = 50200000, + .pixel_rate_pa = 50200000, + .csi_lanes = 1, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, + { + .input_clk = 19200000, + .op_sys_clk = 202000000, + .bitsperpixel = 12, + .pixel_rate_csi = 50200000, + .pixel_rate_pa = 50200000, + .csi_lanes = 1, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, + { + .input_clk = 24000000, + .op_sys_clk = 480000000, + .bitsperpixel = 10, + .pixel_rate_csi = 80000000, + .pixel_rate_pa = 80000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, + { + .input_clk = 24000000, + .op_sys_clk = 480000000, + .bitsperpixel = 12, + .pixel_rate_csi = 80000000, + .pixel_rate_pa = 80000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, +}; + +static struct crl_subdev_rect_rep ov2775_1920x1088_rects_native[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + }, +}; + +static struct crl_mode_rep ov2775_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov2775_1920x1088_rects_native), + .sd_rects = ov2775_1920x1088_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 3550, + .min_fll = 1126, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = + ARRAY_SIZE(ov2775_linear_hcg_30fps_mipi960_regset), + .mode_regs = ov2775_linear_hcg_30fps_mipi960_regset, + }, + { + .sd_rects_items = ARRAY_SIZE(ov2775_1920x1088_rects_native), + .sd_rects = ov2775_1920x1088_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 3550, + .min_fll = 1126, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = + ARRAY_SIZE(ov2775_linear_lcg_30fps_mipi960_regset), + .mode_regs = ov2775_linear_lcg_30fps_mipi960_regset, + }, + { + .sd_rects_items = ARRAY_SIZE(ov2775_1920x1088_rects_native), + .sd_rects = ov2775_1920x1088_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 3550, + .min_fll = 1126, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov2775_2x12_30fps_mipi960_regset), + .mode_regs = ov2775_2x12_30fps_mipi960_regset, + }, + { + .sd_rects_items = ARRAY_SIZE(ov2775_1920x1088_rects_native), + .sd_rects = ov2775_1920x1088_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .min_llp = 3550, + .min_fll = 1126, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov2775_3x12_30fps_mipi960_regset), + .mode_regs = ov2775_3x12_30fps_mipi960_regset, + }, +}; + +static struct crl_sensor_subdev_config ov2775_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov2775 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov2775 pixel array", + }, +}; + +static struct crl_sensor_limits ov2775_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1936, + .y_addr_max = 1096, + .min_frame_length_lines = 1126, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 3550, + .max_line_length_pixels = 32752, +}; + +static struct crl_flip_data ov2775_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, +}; + +static struct crl_csi_data_fmt ov2775_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, +}; + +static struct crl_v4l2_ctrl ov2775_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_S, + .name = "CRL_CID_ANALOG_GAIN_HCG", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_ana_gain_h_regs), + .regs = ov2775_ana_gain_h_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_L, + .name = "CRL_CID_ANALOG_GAIN_LCG", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_ana_gain_l_regs), + .regs = ov2775_ana_gain_l_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_VS, + .name = "CRL_CID_ANALOG_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_ana_gain_vs_regs), + .regs = ov2775_ana_gain_vs_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_L, + .name = "CRL_CID_DIGITAL_GAIN_LCG", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_digital_gain_l_regs), + .regs = ov2775_digital_gain_l_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_S, + .name = "CRL_CID_DIGITAL_GAIN_HCG", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_digital_gain_h_regs), + .regs = ov2775_digital_gain_h_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_VS, + .name = "CRL_CID_DIGITAL_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_digital_gain_vs_regs), + .regs = ov2775_digital_gain_vs_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .name = "CRL_CID_EXPOSURE_DCG", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 1, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_exposure_dcg_regs), + .regs = ov2775_exposure_dcg_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS2, + .name = "CRL_CID_EXPOSURE_VS", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 1, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_exposure_vs_regs), + .regs = ov2775_exposure_vs_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_h_flip_regs), + .regs = ov2775_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_v_flip_regs), + .regs = ov2775_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 1126, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_vblank_regs), + .regs = ov2775_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 3550, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov2775_hblank_regs), + .regs = ov2775_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_SENSOR_MODE, + .name = "CRL_CID_SENSOR_MODE", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = OV2775_CAPTURE_MODE_MAX - 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops ov2775_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops ov2775_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc ov2775_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_width_ops), + .ops = ov2775_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_height_ops), + .ops = ov2775_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_width_ops), + .ops = ov2775_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_height_ops), + .ops = ov2775_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 1, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_width_ops), + .ops = ov2775_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov2775_frame_desc_height_ops), + .ops = ov2775_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 2, + .csi2_data_type.entity_val = 0x12, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity ov2775_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + .delay = 1000, + }, + { + .type = CRL_POWER_ETY_GPIO_CUSTOM, + .ent_number = 284, /* PWDN pin on CNL, 268 + 16 */ + .val = 1, + .undo_val = 1, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .undo_val = 1, + .delay = 5000, + }, +}; + +static struct crl_sensor_configuration ov2775_crl_configuration = { + + .power_items = ARRAY_SIZE(ov2775_power_items), + .power_entities = ov2775_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov2775_powerup_standby_regset), + .powerup_regs = ov2775_powerup_standby_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(ov2775_sensor_detect_regset), + .id_regs = ov2775_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov2775_sensor_subdevs), + .subdevs = ov2775_sensor_subdevs, + + .sensor_limits = &ov2775_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov2775_pll_configurations), + .pll_configs = ov2775_pll_configurations, + + .modes_items = ARRAY_SIZE(ov2775_modes), + .modes = ov2775_modes, + + .streamon_regs_items = ARRAY_SIZE(ov2775_streamon_regs), + .streamon_regs = ov2775_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov2775_streamoff_regs), + .streamoff_regs = ov2775_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov2775_v4l2_ctrls), + .v4l2_ctrl_bank = ov2775_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov2775_crl_csi_data_fmt), + .csi_fmts = ov2775_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov2775_flip_configurations), + .flip_data = ov2775_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_16BIT, + .crl_nvm_info.nvm_preop_regs_items = 0, + .crl_nvm_info.nvm_postop_regs_items = 0, + .crl_nvm_info.nvm_blobs_items = 0, + + .frame_desc_entries = ARRAY_SIZE(ov2775_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = ov2775_frame_desc, +}; + +#endif /* __CRLMODULE_OV2775_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov5670_configuration.h b/drivers/media/i2c/crlmodule/crl_ov5670_configuration.h new file mode 100644 index 000000000000..7badb609dd45 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov5670_configuration.h @@ -0,0 +1,1136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Tommi Franttila + * + */ + +#ifndef __CRLMODULE_ov5670_CONFIGURATION_H_ +#define __CRLMODULE_ov5670_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep ov5670_pll_840mbps[] = { + { 0x030a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0300, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0302, CRL_REG_LEN_08BIT, 0x78 }, + { 0x0304, CRL_REG_LEN_08BIT, 0x03 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0312, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030d, CRL_REG_LEN_08BIT, 0x1e }, + { 0x030f, CRL_REG_LEN_08BIT, 0x06 }, + { 0x030e, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep ov5670_powerup_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3000, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3002, CRL_REG_LEN_08BIT, 0x21 }, + { 0x3005, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3015, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3018, CRL_REG_LEN_08BIT, 0x32 }, + { 0x301a, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301b, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301c, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301d, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301e, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3021, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3030, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x0a }, + { 0x303c, CRL_REG_LEN_08BIT, 0xff }, + { 0x303e, CRL_REG_LEN_08BIT, 0xff }, + { 0x3040, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3041, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3042, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3106, CRL_REG_LEN_08BIT, 0x11 }, + { 0x3500, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3502, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3503, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3504, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3505, CRL_REG_LEN_08BIT, 0x83 }, + { 0x3508, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3509, CRL_REG_LEN_08BIT, 0x00 }, + { 0x350e, CRL_REG_LEN_08BIT, 0x04 }, + { 0x350f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3510, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3511, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3512, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3601, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x3610, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3612, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3614, CRL_REG_LEN_08BIT, 0x5b }, + { 0x3615, CRL_REG_LEN_08BIT, 0x96 }, + { 0x3621, CRL_REG_LEN_08BIT, 0xd0 }, + { 0x3622, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3623, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3633, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3634, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3635, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3636, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3645, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3646, CRL_REG_LEN_08BIT, 0x82 }, + { 0x3650, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3652, CRL_REG_LEN_08BIT, 0xff }, + { 0x3655, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3656, CRL_REG_LEN_08BIT, 0xff }, + { 0x365a, CRL_REG_LEN_08BIT, 0xff }, + { 0x365e, CRL_REG_LEN_08BIT, 0xff }, + { 0x3668, CRL_REG_LEN_08BIT, 0x00 }, + { 0x366a, CRL_REG_LEN_08BIT, 0x07 }, + { 0x366e, CRL_REG_LEN_08BIT, 0x10 }, + { 0x366d, CRL_REG_LEN_08BIT, 0x00 }, + { 0x366f, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3700, CRL_REG_LEN_08BIT, 0x28 }, + { 0x3701, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3702, CRL_REG_LEN_08BIT, 0x3a }, + { 0x3703, CRL_REG_LEN_08BIT, 0x19 }, + { 0x3704, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3705, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3706, CRL_REG_LEN_08BIT, 0x66 }, + { 0x3707, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3708, CRL_REG_LEN_08BIT, 0x34 }, + { 0x3709, CRL_REG_LEN_08BIT, 0x40 }, + { 0x370a, CRL_REG_LEN_08BIT, 0x01 }, + { 0x370b, CRL_REG_LEN_08BIT, 0x1b }, + { 0x3714, CRL_REG_LEN_08BIT, 0x24 }, + { 0x371a, CRL_REG_LEN_08BIT, 0x3e }, + { 0x3733, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3734, CRL_REG_LEN_08BIT, 0x00 }, + { 0x373a, CRL_REG_LEN_08BIT, 0x05 }, + { 0x373b, CRL_REG_LEN_08BIT, 0x06 }, + { 0x373c, CRL_REG_LEN_08BIT, 0x0a }, + { 0x373f, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x3755, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3758, CRL_REG_LEN_08BIT, 0x00 }, + { 0x375b, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3766, CRL_REG_LEN_08BIT, 0x5f }, + { 0x3768, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3769, CRL_REG_LEN_08BIT, 0x22 }, + { 0x3773, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3774, CRL_REG_LEN_08BIT, 0x1f }, + { 0x3776, CRL_REG_LEN_08BIT, 0x06 }, + { 0x37a0, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37a1, CRL_REG_LEN_08BIT, 0x5c }, + { 0x37a7, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37a8, CRL_REG_LEN_08BIT, 0x70 }, + { 0x37aa, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37ab, CRL_REG_LEN_08BIT, 0x48 }, + { 0x37b3, CRL_REG_LEN_08BIT, 0x66 }, + { 0x37c2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x37c5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37c8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3800, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3801, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3802, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3803, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3804, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3805, CRL_REG_LEN_08BIT, 0x33 }, + { 0x3806, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3807, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3811, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3813, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3815, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3816, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3817, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3818, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3819, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3820, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3822, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3826, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3827, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3830, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3836, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3837, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3838, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3841, CRL_REG_LEN_08BIT, 0xff }, /* Auto size function enabled */ + { 0x3846, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3861, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3862, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3863, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3a11, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3a12, CRL_REG_LEN_08BIT, 0x78 }, + { 0x3b00, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b02, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b03, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b04, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b05, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c00, CRL_REG_LEN_08BIT, 0x89 }, + { 0x3c01, CRL_REG_LEN_08BIT, 0xab }, + { 0x3c02, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3c03, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c04, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c05, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3c06, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c07, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3c0c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c0d, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c0e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c0f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c40, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3c41, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3c43, CRL_REG_LEN_08BIT, 0x7d }, + { 0x3c45, CRL_REG_LEN_08BIT, 0xd7 }, + { 0x3c47, CRL_REG_LEN_08BIT, 0xfc }, + { 0x3c50, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3c52, CRL_REG_LEN_08BIT, 0xaa }, + { 0x3c54, CRL_REG_LEN_08BIT, 0x71 }, + { 0x3c56, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3d85, CRL_REG_LEN_08BIT, 0x17 }, + { 0x3d8d, CRL_REG_LEN_08BIT, 0xea }, + { 0x3f03, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3f0a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3f0b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4001, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4009, CRL_REG_LEN_08BIT, 0x0d }, + { 0x4017, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4020, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4021, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4022, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4023, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4024, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4025, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4026, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4027, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4028, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4029, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402d, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4040, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4041, CRL_REG_LEN_08BIT, 0x03 }, + { 0x4042, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4043, CRL_REG_LEN_08BIT, 0x7A }, + { 0x4044, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4045, CRL_REG_LEN_08BIT, 0x7A }, + { 0x4046, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4047, CRL_REG_LEN_08BIT, 0x7A }, + { 0x4048, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4049, CRL_REG_LEN_08BIT, 0x7A }, + { 0x4303, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4307, CRL_REG_LEN_08BIT, 0x30 }, + { 0x4500, CRL_REG_LEN_08BIT, 0x58 }, + { 0x4501, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4502, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4503, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4508, CRL_REG_LEN_08BIT, 0xaa }, + { 0x4509, CRL_REG_LEN_08BIT, 0xaa }, + { 0x450a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x450b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4700, CRL_REG_LEN_08BIT, 0xa4 }, + { 0x4800, CRL_REG_LEN_08BIT, 0x4c }, + { 0x4816, CRL_REG_LEN_08BIT, 0x53 }, + { 0x481f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4837, CRL_REG_LEN_08BIT, 0x13 }, + { 0x5000, CRL_REG_LEN_08BIT, 0x56 }, + { 0x5001, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5002, CRL_REG_LEN_08BIT, 0x28 }, + { 0x5004, CRL_REG_LEN_08BIT, 0x0c }, + { 0x5006, CRL_REG_LEN_08BIT, 0x0c }, + { 0x5007, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x5008, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5009, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x5901, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5a01, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5a03, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5a04, CRL_REG_LEN_08BIT, 0x0c }, + { 0x5a05, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x5a06, CRL_REG_LEN_08BIT, 0x09 }, + { 0x5a07, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x5a08, CRL_REG_LEN_08BIT, 0x06 }, + { 0x5e00, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3734, CRL_REG_LEN_08BIT, 0x40 }, + { 0x5b00, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5b01, CRL_REG_LEN_08BIT, 0x10 }, + { 0x5b02, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5b03, CRL_REG_LEN_08BIT, 0xdb }, + { 0x3d8c, CRL_REG_LEN_08BIT, 0x71 }, + { 0x370b, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3618, CRL_REG_LEN_08BIT, 0x2a }, + { 0x5780, CRL_REG_LEN_08BIT, 0x3e }, + { 0x5781, CRL_REG_LEN_08BIT, 0x0f }, + { 0x5782, CRL_REG_LEN_08BIT, 0x44 }, + { 0x5783, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5784, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5785, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5786, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5787, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5788, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5789, CRL_REG_LEN_08BIT, 0x0f }, + { 0x578a, CRL_REG_LEN_08BIT, 0xfd }, + { 0x578b, CRL_REG_LEN_08BIT, 0xf5 }, + { 0x578c, CRL_REG_LEN_08BIT, 0xf5 }, + { 0x578d, CRL_REG_LEN_08BIT, 0x03 }, + { 0x578e, CRL_REG_LEN_08BIT, 0x08 }, + { 0x578f, CRL_REG_LEN_08BIT, 0x0c }, + { 0x5790, CRL_REG_LEN_08BIT, 0x08 }, + { 0x5791, CRL_REG_LEN_08BIT, 0x06 }, + { 0x5792, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5793, CRL_REG_LEN_08BIT, 0x52 }, + { 0x5794, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3503, CRL_REG_LEN_08BIT, 0x00 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x04 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x60 }, + { 0x3002, CRL_REG_LEN_08BIT, 0x61 }, + { 0x3010, CRL_REG_LEN_08BIT, 0x40 }, + { 0x300D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5045, CRL_REG_LEN_08BIT, 0x05 }, + { 0x5048, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3610, CRL_REG_LEN_08BIT, 0xa8 }, + { 0x3733, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3734, CRL_REG_LEN_08BIT, 0x40 }, +}; + +static struct crl_register_write_rep ov5670_mode_1944[] = { + /* Auto size function in use, but no cropping in this mode */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3809, CRL_REG_LEN_08BIT, 0x20 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x07 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x98 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x03 }, +}; + +static struct crl_register_write_rep ov5670_mode_1940[] = { + /* Auto size function in use, cropping from the centre of the image */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3809, CRL_REG_LEN_08BIT, 0x00 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x07 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x94 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep ov5670_mode_1458[] = { + /* Auto size function in use, cropping from the centre of the image */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3809, CRL_REG_LEN_08BIT, 0x20 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380b, CRL_REG_LEN_08BIT, 0xB2 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x03 }, +}; + +static struct crl_register_write_rep ov5670_mode_1456[] = { + /* Auto size function in use, cropping from the centre of the image */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3809, CRL_REG_LEN_08BIT, 0x00 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380b, CRL_REG_LEN_08BIT, 0xB0 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep ov5670_mode_1152[] = { + /* Auto size function in use, cropping from the centre of the image */ + { 0x3808, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3809, CRL_REG_LEN_08BIT, 0xC0 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x04 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4601, CRL_REG_LEN_08BIT, 0xc6 }, +}; + +static struct crl_register_write_rep ov5670_mode_1080[] = { + /* Auto size function in use, cropping from the centre of the image */ + { 0x3808, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3809, CRL_REG_LEN_08BIT, 0x80 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x04 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x38 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4601, CRL_REG_LEN_08BIT, 0xc0 }, +}; + +static struct crl_register_write_rep ov5670_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep ov5670_streamoff_regs[] = { + /* MIPI stream off when current frame finish */ + { 0x4202, CRL_REG_LEN_08BIT, 0x0f }, + /* Wait to finish the current frame */ + { 0x0000, CRL_REG_LEN_DELAY, 0x40 }, + /* Sensor to standby */ + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep ov5670_data_fmt_width10[] = { + { 0x3031, CRL_REG_LEN_08BIT, 0x0a } +}; + +static struct crl_arithmetic_ops ov5670_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov5670_swap_flip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 5, + }, +}; + +static struct crl_arithmetic_ops ov5670_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov5670_hblank_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov5670_exposure_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, +}; + +static struct crl_dynamic_register_access ov5670_v_flip_regs[] = { + { + .address = 0x3820, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov5670_vflip_ops), + .ops = ov5670_vflip_ops, + .mask = 0x2, + }, + { + .address = 0x450B, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov5670_swap_flip_ops), + .ops = ov5670_swap_flip_ops, + .mask = 0x20, + }, +}; + +static struct crl_dynamic_register_access ov5670_h_flip_regs[] = { + { + .address = 0x3821, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov5670_hflip_ops), + .ops = ov5670_hflip_ops, + .mask = 0x2, + }, +}; + +struct crl_register_write_rep ov5670_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_dynamic_register_access ov5670_ana_gain_global_regs[] = { + { + .address = 0x3508, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0x7ff, + }, +}; + +static struct crl_dynamic_register_access ov5670_exposure_regs[] = { + { + .address = 0x3500, + .len = CRL_REG_LEN_24BIT, + .ops_items = ARRAY_SIZE(ov5670_exposure_ops), + .ops = ov5670_exposure_ops, + .mask = 0x0ffff0, + }, +}; + +static struct crl_dynamic_register_access ov5670_vblank_regs[] = { + { + .address = 0x380E, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ov5670_hblank_regs[] = { + { + .address = 0x380C, + .len = CRL_REG_LEN_16BIT, + .ops_items = ARRAY_SIZE(ov5670_hblank_ops), + .ops = ov5670_hblank_ops, + .mask = 0xffff, + }, +}; + +static struct crl_sensor_detect_config ov5670_sensor_detect_regset[] = { + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300C, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static const s64 ov5670_op_sys_clock[] = { 420000000, }; + +static struct crl_pll_configuration ov5670_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 420000000, + .bitsperpixel = 10, + .pixel_rate_csi = 240000000, + .pixel_rate_pa = 199180800, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov5670_pll_840mbps), + .pll_regs = ov5670_pll_840mbps, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1944_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 0, 0, 2592, 1944 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 0, 0, 2592, 1944 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 0, 0, 2592, 1944 }, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1940_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 16, 2, 2560, 1940 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 2560, 1940 }, + .out_rect = { 0, 0, 2560, 1940 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2560, 1940 }, + .out_rect = { 0, 0, 2560, 1940 }, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1458_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 0, 244, 2592, 1458 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 2592, 1458 }, + .out_rect = { 0, 0, 2592, 1458 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2592, 1458 }, + .out_rect = { 0, 0, 2592, 1458 }, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1456_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 16, 244, 2560, 1456 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 2560, 1456 }, + .out_rect = { 0, 0, 2560, 1456 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 2560, 1456 }, + .out_rect = { 0, 0, 2560, 1456 }, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1152_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 304, 396, 1984, 1152 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 1984, 1152 }, + .out_rect = { 0, 0, 1984, 1152 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 1984, 1152 }, + .out_rect = { 0, 0, 1984, 1152 }, + }, +}; + +static struct crl_subdev_rect_rep ov5670_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 2592, 1944 }, + .out_rect = { 336, 432, 1920, 1080 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect = { 0, 0, 1920, 1080 }, + .out_rect = { 0, 0, 1920, 1080 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 1920, 1080 }, + .out_rect = { 0, 0, 1920, 1080 }, + }, +}; + +static struct crl_mode_rep ov5670_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov5670_1944_rects), + .sd_rects = ov5670_1944_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2592, + .height = 1944, + .min_llp = 3360, + .min_fll = 1976, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1944), + .mode_regs = ov5670_mode_1944, + }, + { + .sd_rects_items = ARRAY_SIZE(ov5670_1940_rects), + .sd_rects = ov5670_1940_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2560, + .height = 1940, + .min_llp = 3366, + .min_fll = 1972, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1940), + .mode_regs = ov5670_mode_1940, + }, + { + .sd_rects_items = ARRAY_SIZE(ov5670_1458_rects), + .sd_rects = ov5670_1458_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2592, + .height = 1458, + .min_llp = 4455, + .min_fll = 1490, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1458), + .mode_regs = ov5670_mode_1458, + }, + { + .sd_rects_items = ARRAY_SIZE(ov5670_1456_rects), + .sd_rects = ov5670_1456_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 2560, + .height = 1456, + .min_llp = 4461, + .min_fll = 1488, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1456), + .mode_regs = ov5670_mode_1456, + }, + + { + .sd_rects_items = ARRAY_SIZE(ov5670_1152_rects), + .sd_rects = ov5670_1152_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1984, + .height = 1152, + .min_llp = 2803, + .min_fll = 1184, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1152), + .mode_regs = ov5670_mode_1152, + }, + { + .sd_rects_items = ARRAY_SIZE(ov5670_1080_rects), + .sd_rects = ov5670_1080_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .min_llp = 2985, + .min_fll = 1112, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov5670_mode_1080), + .mode_regs = ov5670_mode_1080, + }, +}; + +static struct crl_sensor_subdev_config ov5670_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "ov5670 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov5670 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov5670 pixel array", + }, +}; + +static struct crl_sensor_limits ov5670_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 2592, + .y_addr_max = 1944, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 2700, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data ov5670_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, +}; + +static struct crl_csi_data_fmt ov5670_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = ov5670_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov5670_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov5670_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov5670_data_fmt_width10, + }, +}; + +static struct crl_v4l2_ctrl ov5670_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = + ARRAY_SIZE(ov5670_pll_configurations) - 1, + .data.v4l2_int_menu.menu = ov5670_op_sys_clock, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_ana_gain_global_regs), + .regs = ov5670_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_exposure_regs), + .regs = ov5670_exposure_regs, + .dep_items = 0, /* FLL is changed automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_h_flip_regs), + .regs = ov5670_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_v_flip_regs), + .regs = ov5670_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 2474, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_vblank_regs), + .regs = ov5670_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 3880, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov5670_hblank_regs), + .regs = ov5670_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +#define ov5670_OTP_START_ADDR 0x7010 +#define ov5670_OTP_END_ADDR 0x7063 + +#define ov5670_OTP_LEN (ov5670_OTP_END_ADDR - ov5670_OTP_START_ADDR + 1) +#define ov5670_OTP_L_ADDR(x) (x & 0xff) +#define ov5670_OTP_H_ADDR(x) ((x >> 8) & 0xff) + +static struct crl_register_write_rep ov5670_nvm_preop_regset[] = { + /* Start streaming */ + { 0x0100, CRL_REG_LEN_08BIT, 0x01 }, + /* Manual mode, program disable */ + { 0x3D84, CRL_REG_LEN_08BIT, 0xC0 }, + /* Manual OTP start address for access */ + { 0x3D88, CRL_REG_LEN_08BIT, ov5670_OTP_H_ADDR(ov5670_OTP_START_ADDR)}, + { 0x3D89, CRL_REG_LEN_08BIT, ov5670_OTP_L_ADDR(ov5670_OTP_START_ADDR)}, + /* Manual OTP end address for access */ + { 0x3D8A, CRL_REG_LEN_08BIT, ov5670_OTP_H_ADDR(ov5670_OTP_END_ADDR)}, + { 0x3D8B, CRL_REG_LEN_08BIT, ov5670_OTP_L_ADDR(ov5670_OTP_END_ADDR)}, + /* OTP load enable */ + { 0x3D81, CRL_REG_LEN_08BIT, 0x01 }, + /* Wait for the data to load into the buffer */ + { 0x0000, CRL_REG_LEN_DELAY, 0x05 }, +}; + +static struct crl_register_write_rep ov5670_nvm_postop_regset[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, /* Stop streaming */ +}; + +static struct crl_nvm_blob ov5670_nvm_blobs[] = { + {CRL_I2C_ADDRESS_NO_OVERRIDE, ov5670_OTP_START_ADDR, ov5670_OTP_LEN }, +}; + +static struct crl_arithmetic_ops ov5670_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops ov5670_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc ov5670_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov5670_frame_desc_width_ops), + .ops = ov5670_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov5670_frame_desc_height_ops), + .ops = ov5670_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static const struct crl_power_seq_entity ov5670_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1200000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VIO", + .val = 1800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VAF", + .val = 3000000, + .delay = 2000, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 10700, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA_BY_NUMBER, + }, + +}; + +static struct crl_sensor_configuration ov5670_crl_configuration = { + + .power_items = ARRAY_SIZE(ov5670_power_items), + .power_entities = ov5670_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov5670_powerup_regset), + .powerup_regs = ov5670_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(ov5670_sensor_detect_regset), + .id_regs = ov5670_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov5670_sensor_subdevs), + .subdevs = ov5670_sensor_subdevs, + + .sensor_limits = &ov5670_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov5670_pll_configurations), + .pll_configs = ov5670_pll_configurations, + + .modes_items = ARRAY_SIZE(ov5670_modes), + .modes = ov5670_modes, + + .streamon_regs_items = ARRAY_SIZE(ov5670_streamon_regs), + .streamon_regs = ov5670_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov5670_streamoff_regs), + .streamoff_regs = ov5670_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov5670_v4l2_ctrls), + .v4l2_ctrl_bank = ov5670_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov5670_crl_csi_data_fmt), + .csi_fmts = ov5670_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov5670_flip_configurations), + .flip_data = ov5670_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_16BIT, + .crl_nvm_info.nvm_preop_regs_items = + ARRAY_SIZE(ov5670_nvm_preop_regset), + .crl_nvm_info.nvm_preop_regs = ov5670_nvm_preop_regset, + .crl_nvm_info.nvm_postop_regs_items = + ARRAY_SIZE(ov5670_nvm_postop_regset), + .crl_nvm_info.nvm_postop_regs = ov5670_nvm_postop_regset, + .crl_nvm_info.nvm_blobs_items = ARRAY_SIZE(ov5670_nvm_blobs), + .crl_nvm_info.nvm_config = ov5670_nvm_blobs, + + .frame_desc_entries = ARRAY_SIZE(ov5670_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = ov5670_frame_desc, +}; + +#endif /* __CRLMODULE_ov5670_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov8858_configuration.h b/drivers/media/i2c/crlmodule/crl_ov8858_configuration.h new file mode 100644 index 000000000000..63faedcf85ce --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov8858_configuration.h @@ -0,0 +1,1429 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_OV8858_CONFIGURATION_H_ +#define __CRLMODULE_OV8858_CONFIGURATION_H_ + +#include "crlmodule-nvm.h" +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep ov8858_pll_360mbps[] = { + { 0x0300, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0301, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0302, CRL_REG_LEN_08BIT, 0x1e },/* pll1_multiplier = 30 */ + { 0x0303, CRL_REG_LEN_08BIT, 0x00 },/* pll1_divm = /(1 + 0) */ + { 0x0304, CRL_REG_LEN_08BIT, 0x03 },/* pll1_div_mipi = /8 */ + { 0x0305, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0306, CRL_REG_LEN_08BIT, 0x01 }, + { 0x030A, CRL_REG_LEN_08BIT, 0x02 }, + { 0x030B, CRL_REG_LEN_08BIT, 0x01 },/* pll2_pre_div = /2 */ + { 0x030c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x030D, CRL_REG_LEN_08BIT, 0x44 },/* pll2_r_divp = 30 */ + { 0x030E, CRL_REG_LEN_08BIT, 0x01 },/* pll2_r_divs = /2 */ + { 0x030F, CRL_REG_LEN_08BIT, 0x04 },/* pll2_r_divsp = /(1 + 4) */ + /* pll2_pre_div0 = /1, pll2_r_divdac = /(1 + 1) */ + { 0x0312, CRL_REG_LEN_08BIT, 0x02 }, + /* mipi_lane_mode = 1+3, mipi_lvds_sel = 1 = MIPI enable, + * r_phy_pd_mipi_man = 0, lane_dis_option = 0 + */ + { 0x3018, CRL_REG_LEN_08BIT, 0x72 }, +}; + + +static struct crl_register_write_rep ov8858_powerup_regset[] = { + /*Reset*/ + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3007, CRL_REG_LEN_08BIT, 0x80 }, + /* Npump clock div = /2, Ppump clock div = /4 */ + { 0x3015, CRL_REG_LEN_08BIT, 0x01 }, + /* Clock switch output = normal, pclk_div = /1 */ + { 0x3020, CRL_REG_LEN_08BIT, 0x93 }, + { 0x3031, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3032, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3022, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3034, CRL_REG_LEN_08BIT, 0x00 }, + /* sclk_div = /1, sclk_pre_div = /1, chip debug = 1 */ + { 0x3106, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3305, CRL_REG_LEN_08BIT, 0xF1 }, + { 0x3307, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3308, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3309, CRL_REG_LEN_08BIT, 0x28 }, + { 0x330A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x330B, CRL_REG_LEN_08BIT, 0x20 }, + { 0x330C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x330D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x330E, CRL_REG_LEN_08BIT, 0x00 }, + { 0x330F, CRL_REG_LEN_08BIT, 0x40 }, + /* + * Digital fraction gain delay option = Delay 1 frame, + * Gain change delay option = Delay 1 frame, + * Gain delay option = Delay 1 frame, + * Gain manual as sensor gain = Input gain as real gain format, + * Exposure delay option (must be 0 = Delay 1 frame, + * Exposure change delay option (must be 0) = Delay 1 frame + */ + { 0x3503, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3505, CRL_REG_LEN_08BIT, 0x80 },/* gain conversation option */ + /* + * [10:7] are integer gain, [6:0] are fraction gain. For example: + * 0x80 is 1x gain, CRL_REG_LEN_08BIT, 0x100 is 2x gain, + * CRL_REG_LEN_08BIT, 0x1C0 is 3.5x gain + */ + { 0x3508, CRL_REG_LEN_08BIT, 0x02 },/* long gain = 0x0200 */ + { 0x3509, CRL_REG_LEN_08BIT, 0x00 },/* long gain = 0x0200 */ + { 0x350C, CRL_REG_LEN_08BIT, 0x00 },/* short gain = 0x0080 */ + { 0x350D, CRL_REG_LEN_08BIT, 0x80 },/* short gain = 0x0080 */ + { 0x3510, CRL_REG_LEN_08BIT, 0x00 },/* short exposure = 0x000200 */ + { 0x3511, CRL_REG_LEN_08BIT, 0x02 },/* short exposure = 0x000200 */ + { 0x3512, CRL_REG_LEN_08BIT, 0x00 },/* short exposure = 0x000200 */ + { 0x3600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3601, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3602, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3603, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3604, CRL_REG_LEN_08BIT, 0x22 }, + { 0x3605, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3606, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3607, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3608, CRL_REG_LEN_08BIT, 0x11 }, + { 0x3609, CRL_REG_LEN_08BIT, 0x28 }, + { 0x360A, CRL_REG_LEN_08BIT, 0x00 }, + { 0x360B, CRL_REG_LEN_08BIT, 0x06 }, + { 0x360C, CRL_REG_LEN_08BIT, 0xD4 }, + { 0x360D, CRL_REG_LEN_08BIT, 0x40 }, + { 0x360E, CRL_REG_LEN_08BIT, 0x0C }, + { 0x360F, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3610, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3611, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3612, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3613, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3614, CRL_REG_LEN_08BIT, 0x58 }, + { 0x3615, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3616, CRL_REG_LEN_08BIT, 0x4A }, + { 0x3617, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3618, CRL_REG_LEN_08BIT, 0x5a }, + { 0x3619, CRL_REG_LEN_08BIT, 0x70 }, + { 0x361A, CRL_REG_LEN_08BIT, 0x99 }, + { 0x361B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x361C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x361D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x361E, CRL_REG_LEN_08BIT, 0x00 }, + { 0x361F, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3638, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3633, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3634, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3635, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3636, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3645, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3646, CRL_REG_LEN_08BIT, 0x83 }, + { 0x364A, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3700, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3701, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3702, CRL_REG_LEN_08BIT, 0x50 }, + { 0x3703, CRL_REG_LEN_08BIT, 0x32 }, + { 0x3704, CRL_REG_LEN_08BIT, 0x28 }, + { 0x3705, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3706, CRL_REG_LEN_08BIT, 0x82 }, + { 0x3707, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3708, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3709, CRL_REG_LEN_08BIT, 0x66 }, + { 0x370A, CRL_REG_LEN_08BIT, 0x01 }, + { 0x370B, CRL_REG_LEN_08BIT, 0x82 }, + { 0x370C, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3712, CRL_REG_LEN_08BIT, 0x44 }, + { 0x3714, CRL_REG_LEN_08BIT, 0x24 }, + { 0x3718, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3719, CRL_REG_LEN_08BIT, 0x31 }, + { 0x371E, CRL_REG_LEN_08BIT, 0x31 }, + { 0x371F, CRL_REG_LEN_08BIT, 0x7F }, + { 0x3720, CRL_REG_LEN_08BIT, 0x0A }, + { 0x3721, CRL_REG_LEN_08BIT, 0x0A }, + { 0x3724, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3725, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3726, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3728, CRL_REG_LEN_08BIT, 0x0A }, + { 0x3729, CRL_REG_LEN_08BIT, 0x03 }, + { 0x372A, CRL_REG_LEN_08BIT, 0x06 }, + { 0x372B, CRL_REG_LEN_08BIT, 0xA6 }, + { 0x372C, CRL_REG_LEN_08BIT, 0xA6 }, + { 0x372D, CRL_REG_LEN_08BIT, 0xA6 }, + { 0x372E, CRL_REG_LEN_08BIT, 0x0C }, + { 0x372F, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3730, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3731, CRL_REG_LEN_08BIT, 0x0C }, + { 0x3732, CRL_REG_LEN_08BIT, 0x28 }, + { 0x3733, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3734, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3736, CRL_REG_LEN_08BIT, 0x30 }, + { 0x373A, CRL_REG_LEN_08BIT, 0x0A }, + { 0x373B, CRL_REG_LEN_08BIT, 0x0B }, + { 0x373C, CRL_REG_LEN_08BIT, 0x14 }, + { 0x373E, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3750, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3751, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3755, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3758, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3759, CRL_REG_LEN_08BIT, 0x4C }, + { 0x375A, CRL_REG_LEN_08BIT, 0x0C }, + { 0x375B, CRL_REG_LEN_08BIT, 0x26 }, + { 0x375C, CRL_REG_LEN_08BIT, 0x20 }, + { 0x375D, CRL_REG_LEN_08BIT, 0x04 }, + { 0x375E, CRL_REG_LEN_08BIT, 0x00 }, + { 0x375F, CRL_REG_LEN_08BIT, 0x28 }, + { 0x3760, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3761, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3762, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3763, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3766, CRL_REG_LEN_08BIT, 0xFF }, + { 0x376B, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3772, CRL_REG_LEN_08BIT, 0x46 }, + { 0x3773, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3774, CRL_REG_LEN_08BIT, 0x2C }, + { 0x3775, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3776, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3777, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3778, CRL_REG_LEN_08BIT, 0x17 }, + { 0x37A0, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37A1, CRL_REG_LEN_08BIT, 0x7A }, + { 0x37A2, CRL_REG_LEN_08BIT, 0x7A }, + { 0x37A3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37A4, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37A5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37A6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37A7, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37A8, CRL_REG_LEN_08BIT, 0x98 }, + { 0x37A9, CRL_REG_LEN_08BIT, 0x98 }, + { 0x37AA, CRL_REG_LEN_08BIT, 0x88 }, + { 0x37AB, CRL_REG_LEN_08BIT, 0x5C }, + { 0x37AC, CRL_REG_LEN_08BIT, 0x5C }, + { 0x37AD, CRL_REG_LEN_08BIT, 0x55 }, + { 0x37AE, CRL_REG_LEN_08BIT, 0x19 }, + { 0x37AF, CRL_REG_LEN_08BIT, 0x19 }, + { 0x37B0, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B3, CRL_REG_LEN_08BIT, 0x84 }, + { 0x37B4, CRL_REG_LEN_08BIT, 0x84 }, + { 0x37B5, CRL_REG_LEN_08BIT, 0x60 }, + { 0x37B6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B8, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37B9, CRL_REG_LEN_08BIT, 0xFF }, + { 0x3800, CRL_REG_LEN_08BIT, 0x00 },/* h_crop_start high */ + { 0x3801, CRL_REG_LEN_08BIT, 0x0C },/* h_crop_start low */ + { 0x3802, CRL_REG_LEN_08BIT, 0x00 },/* v_crop_start high */ + { 0x3803, CRL_REG_LEN_08BIT, 0x0C },/* v_crop_start low */ + { 0x3804, CRL_REG_LEN_08BIT, 0x0C },/* h_crop_end high */ + { 0x3805, CRL_REG_LEN_08BIT, 0xD3 },/* h_crop_end low */ + { 0x3806, CRL_REG_LEN_08BIT, 0x09 },/* v_crop_end high */ + { 0x3807, CRL_REG_LEN_08BIT, 0xA3 },/* v_crop_end low */ + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high */ + { 0x3809, CRL_REG_LEN_08BIT, 0xC0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x09 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x90 },/* v_output_size low */ + { 0x380C, CRL_REG_LEN_08BIT, 0x07 },/* horizontal timing size high */ + { 0x380D, CRL_REG_LEN_08BIT, 0x94 },/* horizontal timing size low */ + { 0x380E, CRL_REG_LEN_08BIT, 0x0A },/* vertical timing size high */ + { 0x380F, CRL_REG_LEN_08BIT, 0x0D },/* vertical timing size low */ + { 0x3810, CRL_REG_LEN_08BIT, 0x00 },/* h_win offset high */ + { 0x3811, CRL_REG_LEN_08BIT, 0x04 },/* h_win offset low */ + { 0x3812, CRL_REG_LEN_08BIT, 0x00 },/* v_win offset high */ + { 0x3813, CRL_REG_LEN_08BIT, 0x02 },/* v_win offset low */ + { 0x3814, CRL_REG_LEN_08BIT, 0x01 },/* h_odd_inc */ + { 0x3815, CRL_REG_LEN_08BIT, 0x01 },/* h_even_inc */ + { 0x3820, CRL_REG_LEN_08BIT, 0x00 },/* format1 */ + { 0x3821, CRL_REG_LEN_08BIT, 0x40 },/* format2 */ + { 0x382A, CRL_REG_LEN_08BIT, 0x01 },/* v_odd_inc */ + { 0x382B, CRL_REG_LEN_08BIT, 0x01 },/* v_even_inc */ + { 0x3830, CRL_REG_LEN_08BIT, 0x06 }, + { 0x3836, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3837, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3841, CRL_REG_LEN_08BIT, 0xFF },/* AUTO_SIZE_CTRL */ + { 0x3846, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3D85, CRL_REG_LEN_08BIT, 0x16 },/* OTP_REG85 */ + { 0x3D8C, CRL_REG_LEN_08BIT, 0x73 }, + { 0x3D8D, CRL_REG_LEN_08BIT, 0xde }, + { 0x3F08, CRL_REG_LEN_08BIT, 0x10 },/* PSRAM control register */ + { 0x4000, CRL_REG_LEN_08BIT, 0xF1 },/* BLC CTRL00 = default */ + { 0x4001, CRL_REG_LEN_08BIT, 0x00 },/* BLC CTRL01 */ + { 0x4002, CRL_REG_LEN_08BIT, 0x27 },/* BLC offset = 0x27 */ + { 0x4005, CRL_REG_LEN_08BIT, 0x10 },/* BLC target = 0x0010 */ + { 0x4009, CRL_REG_LEN_08BIT, 0x81 },/* BLC CTRL09 */ + { 0x400B, CRL_REG_LEN_08BIT, 0x0C },/* BLC CTRL0B = default */ + { 0x4011, CRL_REG_LEN_08BIT, 0x20 }, + { 0x401B, CRL_REG_LEN_08BIT, 0x00 },/* Zero line R coeff. = 0x0000 */ + { 0x401D, CRL_REG_LEN_08BIT, 0x00 },/* Zero line T coeff. = 0x0000 */ + { 0x401F, CRL_REG_LEN_08BIT, 0x00 },/* BLC CTRL1F */ + { 0x4020, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4021, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4022, CRL_REG_LEN_08BIT, 0x0C }, + { 0x4023, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4024, CRL_REG_LEN_08BIT, 0x0f }, + { 0x4025, CRL_REG_LEN_08BIT, 0x36 }, + { 0x4026, CRL_REG_LEN_08BIT, 0x0f }, + { 0x4027, CRL_REG_LEN_08BIT, 0x37 }, + { 0x4028, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4029, CRL_REG_LEN_08BIT, 0x02 }, + { 0x402A, CRL_REG_LEN_08BIT, 0x04 }, + { 0x402B, CRL_REG_LEN_08BIT, 0x08 }, + { 0x402C, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402D, CRL_REG_LEN_08BIT, 0x02 }, + { 0x402E, CRL_REG_LEN_08BIT, 0x04 }, + { 0x402F, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4034, CRL_REG_LEN_08BIT, 0x3F }, + { 0x403D, CRL_REG_LEN_08BIT, 0x04 },/* BLC CTRL3D */ + { 0x4300, CRL_REG_LEN_08BIT, 0xFF },/* clip_max[11:4] = 0xFFF */ + { 0x4301, CRL_REG_LEN_08BIT, 0x00 },/* clip_min[11:4] = 0 */ + { 0x4302, CRL_REG_LEN_08BIT, 0x0F },/* clip_min/max[3:0] */ + { 0x4316, CRL_REG_LEN_08BIT, 0x00 },/* CTRL16 = default */ + { 0x4503, CRL_REG_LEN_08BIT, 0x18 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x97 }, + /* wkup_dly = Mark1 wakeup delay/2^10 = 0x25 */ + { 0x4808, CRL_REG_LEN_08BIT, 0x25 }, + { 0x4816, CRL_REG_LEN_08BIT, 0x12 },/* Embedded data type */ + { 0x5A08, CRL_REG_LEN_08BIT, 0x02 },/* Data in beginning of the frame */ + { 0x5041, CRL_REG_LEN_08BIT, 0x01 },/* ISP CTRL41 - embedded data=on */ + { 0x4307, CRL_REG_LEN_08BIT, 0x31 },/* Embedded_en */ + { 0x481F, CRL_REG_LEN_08BIT, 0x32 },/* clk_prepare_min = 0x32 */ + { 0x4837, CRL_REG_LEN_08BIT, 0x16 },/* pclk_period = 0x14 */ + { 0x4850, CRL_REG_LEN_08BIT, 0x10 },/* LANE SEL01 */ + { 0x4851, CRL_REG_LEN_08BIT, 0x32 },/* LANE SEL02 */ + { 0x4B00, CRL_REG_LEN_08BIT, 0x2A }, + { 0x4B0D, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4D00, CRL_REG_LEN_08BIT, 0x04 },/* TPM_CTRL_REG */ + { 0x4D01, CRL_REG_LEN_08BIT, 0x18 },/* TPM_CTRL_REG */ + { 0x4D02, CRL_REG_LEN_08BIT, 0xC3 },/* TPM_CTRL_REG */ + { 0x4D03, CRL_REG_LEN_08BIT, 0xFF },/* TPM_CTRL_REG */ + { 0x4D04, CRL_REG_LEN_08BIT, 0xFF },/* TPM_CTRL_REG */ + { 0x4D05, CRL_REG_LEN_08BIT, 0xFF },/* TPM_CTRL_REG */ + /* + * Lens correction (LENC) function enable = 0 + * Slave sensor AWB Gain function enable = 1 + * Slave sensor AWB Statistics function enable = 1 + * Master sensor AWB Gain function enable = 1 + * Master sensor AWB Statistics function enable = 1 + * Black DPC function enable = 1 + * White DPC function enable =1 + */ + { 0x5000, CRL_REG_LEN_08BIT, 0x7E }, + { 0x5001, CRL_REG_LEN_08BIT, 0x01 },/* BLC function enable = 1 */ + /* + * Horizontal scale function enable = 0 + * WBMATCH bypass mode = Select slave sensor's gain + * WBMATCH function enable = 0 + * Master MWB gain support RGBC = 0 + * OTP_DPC function enable = 1 + * Manual mode of VarioPixel function enable = 0 + * Manual enable of VarioPixel function enable = 0 + * Use VSYNC to latch ISP modules's function enable signals = 0 + */ + { 0x5002, CRL_REG_LEN_08BIT, 0x08 }, + /* + * Bypass all ISP modules after BLC module = 0 + * DPC_DBC buffer control enable = 1 + * WBMATCH VSYNC selection = Select master sensor's VSYNC fall + * Select master AWB gain to embed line = AWB gain before manual mode + * Enable BLC's input flip_i signal = 0 + */ + { 0x5003, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5041, CRL_REG_LEN_08BIT, 0x1D },/* ISP CTRL41 - embedded data=on */ + { 0x5046, CRL_REG_LEN_08BIT, 0x12 },/* ISP CTRL46 = default */ + /* + * Tail enable = 1 + * Saturate cross cluster enable = 1 + * Remove cross cluster enable = 1 + * Enable to remove connected defect pixels in same channel = 1 + * Enable to remove connected defect pixels in different channel = 1 + * Smooth enable, use average G for recovery = 1 + * Black/white sensor mode enable = 0 + * Manual mode enable = 0 + */ + { 0x5780, CRL_REG_LEN_08BIT, 0x3e }, + { 0x5781, CRL_REG_LEN_08BIT, 0x0f }, + { 0x5782, CRL_REG_LEN_08BIT, 0x44 }, + { 0x5783, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5784, CRL_REG_LEN_08BIT, 0x01 },/* DPC CTRL04 */ + { 0x5785, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5786, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5787, CRL_REG_LEN_08BIT, 0x04 },/* DPC CTRL07 */ + { 0x5788, CRL_REG_LEN_08BIT, 0x02 },/* DPC CTRL08 */ + { 0x5789, CRL_REG_LEN_08BIT, 0x0f }, + { 0x578A, CRL_REG_LEN_08BIT, 0xfd },/* DPC CTRL0A */ + { 0x578B, CRL_REG_LEN_08BIT, 0xf5 },/* DPC CTRL0B */ + { 0x578C, CRL_REG_LEN_08BIT, 0xf5 },/* DPC CTRL0C */ + { 0x578D, CRL_REG_LEN_08BIT, 0x03 },/* DPC CTRL0D */ + { 0x578E, CRL_REG_LEN_08BIT, 0x08 },/* DPC CTRL0E */ + { 0x578F, CRL_REG_LEN_08BIT, 0x0c },/* DPC CTRL0F */ + { 0x5790, CRL_REG_LEN_08BIT, 0x08 },/* DPC CTRL10 */ + { 0x5791, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5792, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5793, CRL_REG_LEN_08BIT, 0x52 }, + { 0x5794, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x586E, CRL_REG_LEN_08BIT, 0x10 }, + { 0x586F, CRL_REG_LEN_08BIT, 0x08 }, + { 0x58F8, CRL_REG_LEN_08BIT, 0x3d }, + { 0x5871, CRL_REG_LEN_08BIT, 0x0d }, + { 0x5870, CRL_REG_LEN_08BIT, 0x18 }, + { 0x5901, CRL_REG_LEN_08BIT, 0x00 },/* VAP CTRL01 = default */ + { 0x5B00, CRL_REG_LEN_08BIT, 0x02 },/* OTP CTRL00 */ + { 0x5B01, CRL_REG_LEN_08BIT, 0x10 },/* OTP CTRL01 */ + { 0x5B02, CRL_REG_LEN_08BIT, 0x03 },/* OTP CTRL02 */ + { 0x5B03, CRL_REG_LEN_08BIT, 0xCF },/* OTP CTRL03 */ + { 0x5B05, CRL_REG_LEN_08BIT, 0x6C },/* OTP CTRL05 = default */ + { 0x5E00, CRL_REG_LEN_08BIT, 0x00 },/* PRE CTRL00 = default */ + { 0x5E01, CRL_REG_LEN_08BIT, 0x41 },/* PRE_CTRL01 = default */ + { 0x4825, CRL_REG_LEN_08BIT, 0x3a }, + { 0x4826, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4808, CRL_REG_LEN_08BIT, 0x25 }, + { 0x3763, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3768, CRL_REG_LEN_08BIT, 0xcc }, + { 0x470b, CRL_REG_LEN_08BIT, 0x28 }, + { 0x4202, CRL_REG_LEN_08BIT, 0x00 }, + { 0x400d, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4040, CRL_REG_LEN_08BIT, 0x07 }, + { 0x403e, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4041, CRL_REG_LEN_08BIT, 0xc6 }, + { 0x400a, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep ov8858_mode_8m_native[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3264 x 2448 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xc0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x09 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x90 },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x0C }, + { 0x4023, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x97 }, +}; + +static struct crl_register_write_rep ov8858_mode_6m_native[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3264 x 1836 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xc0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x07 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x2c },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x0C }, + { 0x4023, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x97 }, +}; + +static struct crl_register_write_rep ov8858_mode_8m_full[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3280 x 2464 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xD0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x09 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0xA0 },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x0C }, + { 0x4023, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x97 }, +}; + +static struct crl_register_write_rep ov8858_mode_6m_full[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x0C },/* h_output_size high 3280 x 1852 */ + { 0x3809, CRL_REG_LEN_08BIT, 0xD0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x07 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x3c },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x0C }, + { 0x4023, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x01 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x97 }, +}; + +static struct crl_register_write_rep ov8858_mode_1080[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x07 },/* h_output_size high*/ + { 0x3809, CRL_REG_LEN_08BIT, 0x80 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x04 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x38 },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4023, CRL_REG_LEN_08BIT, 0x20 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4601, CRL_REG_LEN_08BIT, 0xef }, +}; + +static struct crl_register_write_rep ov8858_mode_1920x1440_crop[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x07 },/* h_output_size high*/ + { 0x3809, CRL_REG_LEN_08BIT, 0x80 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x05 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0xA0 },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4023, CRL_REG_LEN_08BIT, 0x20 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4601, CRL_REG_LEN_08BIT, 0xef }, +}; + +static struct crl_register_write_rep ov8858_mode_1984x1116_crop[] = { + { 0x382d, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x3808, CRL_REG_LEN_08BIT, 0x07 },/* h_output_size high*/ + { 0x3809, CRL_REG_LEN_08BIT, 0xC0 },/* h_output_size low */ + { 0x380A, CRL_REG_LEN_08BIT, 0x04 },/* v_output_size high */ + { 0x380B, CRL_REG_LEN_08BIT, 0x5C },/* v_output_size low */ + { 0x4022, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4023, CRL_REG_LEN_08BIT, 0x20 }, + { 0x4600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4601, CRL_REG_LEN_08BIT, 0xef }, +}; + +static struct crl_register_write_rep ov8858_streamon_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x01 } +}; + +static struct crl_register_write_rep ov8858_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_register_write_rep ov8858_data_fmt_width10[] = { + { 0x3031, CRL_REG_LEN_08BIT, 0x0a } +}; + +static struct crl_arithmetic_ops ov8858_vflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov8858_hflip_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov8858_hblank_ops[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 1, + }, +}; + +static struct crl_arithmetic_ops ov8858_exposure_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, +}; + +static struct crl_dynamic_register_access ov8858_v_flip_regs[] = { + { + .address = 0x3820, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov8858_vflip_ops), + .ops = ov8858_vflip_ops, + .mask = 0x2, + }, +}; + +static struct crl_dynamic_register_access ov8858_dig_gain_regs[] = { + { + .address = 0x5032, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, + { + .address = 0x5034, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, + { + .address = 0x5036, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ov8858_h_flip_regs[] = { + { + .address = 0x3821, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ov8858_hflip_ops), + .ops = ov8858_hflip_ops, + .mask = 0x2, + }, +}; + +struct crl_register_write_rep ov8858_poweroff_regset[] = { + { 0x0103, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_dynamic_register_access ov8858_ana_gain_global_regs[] = { + { + .address = 0x3508, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0x7ff, + }, +}; + +static struct crl_dynamic_register_access ov8858_exposure_regs[] = { + { + .address = 0x3500, + .len = CRL_REG_LEN_24BIT, + .ops_items = ARRAY_SIZE(ov8858_exposure_ops), + .ops = ov8858_exposure_ops, + .mask = 0x0ffff0, + }, +}; + +static struct crl_dynamic_register_access ov8858_vblank_regs[] = { + { + .address = 0x380E, + .len = CRL_REG_LEN_16BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xffff, + }, +}; + +static struct crl_dynamic_register_access ov8858_hblank_regs[] = { + { + .address = 0x380C, + .len = CRL_REG_LEN_16BIT, + .ops_items = ARRAY_SIZE(ov8858_hblank_ops), + .ops = ov8858_hblank_ops, + .mask = 0xffff, + }, +}; + +static struct crl_sensor_detect_config ov8858_sensor_detect_regset[] = { + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300C, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration ov8858_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 360000000, + .bitsperpixel = 10, + .pixel_rate_csi = 180000000, + .pixel_rate_pa = 290133334, + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov8858_pll_360mbps), + .pll_regs = ov8858_pll_360mbps, + }, + +}; + +static struct crl_subdev_rect_rep ov8858_8m_rects_native[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, +}; + +static struct crl_subdev_rect_rep ov8858_6m_rects_native[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 1836, + }, +}; + + +static struct crl_subdev_rect_rep ov8858_8m_rects_full[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2464, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2464, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2464, + }, +}; + +static struct crl_subdev_rect_rep ov8858_6m_rects_full[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2464, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 2464, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3280, + .in_rect.height = 2464, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3280, + .out_rect.height = 1852, + }, +}; + +static struct crl_subdev_rect_rep ov8858_1080_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep ov8858_1920x1440_rects_crop[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1440, + }, +}; + +static struct crl_subdev_rect_rep ov8858_1984x1116_rects_crop[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 3264, + .out_rect.height = 2448, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 3264, + .in_rect.height = 2448, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1984, + .out_rect.height = 1116, + }, +}; + +static struct crl_mode_rep ov8858_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov8858_8m_rects_native), + .sd_rects = ov8858_8m_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3264, + .height = 2448, + .min_llp = 3880, + .min_fll = 2474, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_8m_native), + .mode_regs = ov8858_mode_8m_native, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_6m_rects_native), + .sd_rects = ov8858_6m_rects_native, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3264, + .height = 1836, + .min_llp = 5132, + .min_fll = 1872, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_6m_native), + .mode_regs = ov8858_mode_6m_native, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_8m_rects_full), + .sd_rects = ov8858_8m_rects_full, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3280, + .height = 2464, + .min_llp = 3880, + .min_fll = 2474, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_8m_full), + .mode_regs = ov8858_mode_8m_full, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_6m_rects_full), + .sd_rects = ov8858_6m_rects_full, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 3280, + .height = 1852, + .min_llp = 5132, + .min_fll = 1872, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_6m_full), + .mode_regs = ov8858_mode_6m_full, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_1080_rects), + .sd_rects = ov8858_1080_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .min_llp = 4284, + .min_fll = 1120, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_1080), + .mode_regs = ov8858_mode_1080, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_1920x1440_rects_crop), + .sd_rects = ov8858_1920x1440_rects_crop, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1440, + .min_llp = 3880, + .min_fll = 1480, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_1920x1440_crop), + .mode_regs = ov8858_mode_1920x1440_crop, + }, + { + .sd_rects_items = ARRAY_SIZE(ov8858_1984x1116_rects_crop), + .sd_rects = ov8858_1984x1116_rects_crop, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1984, + .height = 1116, + .min_llp = 3880, + .min_fll = 1120, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov8858_mode_1984x1116_crop), + .mode_regs = ov8858_mode_1984x1116_crop, + }, + +}; + +static struct crl_sensor_subdev_config ov8858_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "ov8858 scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov8858 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov8858 pixel array", + }, +}; + +static struct crl_sensor_limits ov8858_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 3280, + .y_addr_max = 2464, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 3880, + .max_line_length_pixels = 32752, + .scaler_m_min = 16, + .scaler_m_max = 255, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data ov8858_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + }, +}; + +static struct crl_csi_data_fmt ov8858_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 1, + .regs = ov8858_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov8858_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov8858_data_fmt_width10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .regs_items = 1, + .bits_per_pixel = 10, + .regs = ov8858_data_fmt_width10, + }, +}; + +static struct crl_v4l2_ctrl ov8858_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_ana_gain_global_regs), + .regs = ov8858_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_exposure_regs), + .regs = ov8858_exposure_regs, + .dep_items = 0, /* FLL is changes automatically */ + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_h_flip_regs), + .regs = ov8858_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_v_flip_regs), + .regs = ov8858_v_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 2474, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_vblank_regs), + .regs = ov8858_vblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 3880, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_hblank_regs), + .regs = ov8858_hblank_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4095, + .data.std_data.step = 1, + .data.std_data.def = 1024, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov8858_dig_gain_regs), + .regs = ov8858_dig_gain_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +#define OV8858_OTP_START_ADDR 0x7010 +#define OV8858_OTP_END_ADDR 0x7186 + +#define OV8858_OTP_LEN (OV8858_OTP_END_ADDR - OV8858_OTP_START_ADDR + 1) +#define OV8858_OTP_L_ADDR(x) (x & 0xff) +#define OV8858_OTP_H_ADDR(x) ((x >> 8) & 0xff) + +static struct crl_register_write_rep ov8858_nvm_preop_regset[] = { + /* Start streaming */ + { 0x0100, CRL_REG_LEN_08BIT, 0x01 }, + /* Manual mode, program disable */ + { 0x3D84, CRL_REG_LEN_08BIT, 0xC0 }, + /* Manual OTP start address for access */ + { 0x3D88, CRL_REG_LEN_08BIT, OV8858_OTP_H_ADDR(OV8858_OTP_START_ADDR)}, + { 0x0D89, CRL_REG_LEN_08BIT, OV8858_OTP_L_ADDR(OV8858_OTP_START_ADDR)}, + /* Manual OTP end address for access */ + { 0x3D8A, CRL_REG_LEN_08BIT, OV8858_OTP_H_ADDR(OV8858_OTP_END_ADDR)}, + { 0x3D8B, CRL_REG_LEN_08BIT, OV8858_OTP_L_ADDR(OV8858_OTP_END_ADDR)}, + /* OTP load enable */ + { 0x3D81, CRL_REG_LEN_08BIT, 0x01 }, + /* Wait for the data to load into the buffer */ + { 0x0000, CRL_REG_LEN_DELAY, 0x05 }, +}; + +static struct crl_register_write_rep ov8858_nvm_postop_regset[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, /* Stop streaming */ +}; + +static struct crl_nvm_blob ov8858_nvm_blobs[] = { + {CRL_I2C_ADDRESS_NO_OVERRIDE, OV8858_OTP_START_ADDR, OV8858_OTP_LEN }, +}; + +static struct crl_arithmetic_ops ov8858_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops ov8858_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc ov8858_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ov8858_frame_desc_width_ops), + .ops = ov8858_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ov8858_frame_desc_height_ops), + .ops = ov8858_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity ov8858_power_items[] = { + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VANA", + .val = 2800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VDIG", + .val = 1200000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_REGULATOR_FRAMEWORK, + .ent_name = "VIO", + .val = 1800000, + .delay = 0, + }, + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 10000, + }, +}; + +static struct crl_sensor_configuration ov8858_crl_configuration = { + + .power_items = ARRAY_SIZE(ov8858_power_items), + .power_entities = ov8858_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov8858_powerup_regset), + .powerup_regs = ov8858_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + + .id_reg_items = ARRAY_SIZE(ov8858_sensor_detect_regset), + .id_regs = ov8858_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov8858_sensor_subdevs), + .subdevs = ov8858_sensor_subdevs, + + .sensor_limits = &ov8858_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov8858_pll_configurations), + .pll_configs = ov8858_pll_configurations, + + .modes_items = ARRAY_SIZE(ov8858_modes), + .modes = ov8858_modes, + + .streamon_regs_items = ARRAY_SIZE(ov8858_streamon_regs), + .streamon_regs = ov8858_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov8858_streamoff_regs), + .streamoff_regs = ov8858_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov8858_v4l2_ctrls), + .v4l2_ctrl_bank = ov8858_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov8858_crl_csi_data_fmt), + .csi_fmts = ov8858_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov8858_flip_configurations), + .flip_data = ov8858_flip_configurations, + + .crl_nvm_info.nvm_flags = CRL_NVM_ADDR_MODE_16BIT, + .crl_nvm_info.nvm_preop_regs_items = + ARRAY_SIZE(ov8858_nvm_preop_regset), + .crl_nvm_info.nvm_preop_regs = ov8858_nvm_preop_regset, + .crl_nvm_info.nvm_postop_regs_items = + ARRAY_SIZE(ov8858_nvm_postop_regset), + .crl_nvm_info.nvm_postop_regs = ov8858_nvm_postop_regset, + .crl_nvm_info.nvm_blobs_items = ARRAY_SIZE(ov8858_nvm_blobs), + .crl_nvm_info.nvm_config = ov8858_nvm_blobs, + + .frame_desc_entries = ARRAY_SIZE(ov8858_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = ov8858_frame_desc, + + .msr_file_name = "00ov8858.bxt_rvp.drvb", +}; + +#endif /* __CRLMODULE_OV8858_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ov9281_configuration.h b/drivers/media/i2c/crlmodule/crl_ov9281_configuration.h new file mode 100644 index 000000000000..5fb1987ed08f --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ov9281_configuration.h @@ -0,0 +1,520 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation + * + * Author: Wu Xia + * + */ + +#ifndef __CRLMODULE_OV9281_CONFIGURATION_H_ +#define __CRLMODULE_OV9281_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep ov9281_pll_800mbps[] = { + { 0x0302, CRL_REG_LEN_08BIT, 0x32 }, + { 0x030d, CRL_REG_LEN_08BIT, 0x50 }, + { 0x030e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ +}; + +static struct crl_register_write_rep ov9281_powerup_regset[] = { + { 0x4f00, CRL_REG_LEN_08BIT, 0x00 }, + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ +}; + +static struct crl_register_write_rep ov9281_mode_1m[] = { + { 0x3001, CRL_REG_LEN_08BIT, 0x60 }, + { 0x3004, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x3005, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3006, CRL_REG_LEN_08BIT, 0x04 }, + + { 0x3011, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3013, CRL_REG_LEN_08BIT, 0x18 }, + { 0x3022, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3030, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3039, CRL_REG_LEN_08BIT, 0x32 }, + { 0x303a, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x3503, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3505, CRL_REG_LEN_08BIT, 0x8c }, + { 0x3507, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3508, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3610, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3611, CRL_REG_LEN_08BIT, 0xa0 }, + + { 0x3620, CRL_REG_LEN_08BIT, 0x6f }, + { 0x3632, CRL_REG_LEN_08BIT, 0x56 }, + { 0x3633, CRL_REG_LEN_08BIT, 0x78 }, + { 0x3662, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3666, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x366f, CRL_REG_LEN_08BIT, 0x5a }, + { 0x3680, CRL_REG_LEN_08BIT, 0x84 }, + + { 0x3712, CRL_REG_LEN_08BIT, 0x80 }, + { 0x372d, CRL_REG_LEN_08BIT, 0x22 }, + { 0x3731, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3732, CRL_REG_LEN_08BIT, 0x30 }, + { 0x3778, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x377d, CRL_REG_LEN_08BIT, 0x22 }, + { 0x3788, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3789, CRL_REG_LEN_08BIT, 0xa4 }, + { 0x378a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x378b, CRL_REG_LEN_08BIT, 0x4a }, + { 0x3799, CRL_REG_LEN_08BIT, 0x20 }, + + /* window setting*/ + { 0x3800, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3801, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3802, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3803, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3804, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3805, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3806, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3807, CRL_REG_LEN_08BIT, 0x27 }, + + { 0x3808, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3809, CRL_REG_LEN_08BIT, 0x00 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x03 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x20 }, + + { 0x380c, CRL_REG_LEN_08BIT, 0x02 }, + { 0x380d, CRL_REG_LEN_08BIT, 0xd8 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x03 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x8e }, + + { 0x3810, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3811, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x3812, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3813, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3814, CRL_REG_LEN_08BIT, 0x11 }, + { 0x3815, CRL_REG_LEN_08BIT, 0x11 }, + { 0x3820, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3821, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3881, CRL_REG_LEN_08BIT, 0x42 }, + { 0x38b1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3920, CRL_REG_LEN_08BIT, 0xff }, + { 0x4003, CRL_REG_LEN_08BIT, 0x40 }, + + { 0x4008, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4009, CRL_REG_LEN_08BIT, 0x0b }, + { 0x400c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x400d, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4010, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4043, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4307, CRL_REG_LEN_08BIT, 0x30 }, + + { 0x4317, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4501, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4507, CRL_REG_LEN_08BIT, 0x00 }, + + { 0x4509, CRL_REG_LEN_08BIT, 0x00 }, + { 0x450a, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4601, CRL_REG_LEN_08BIT, 0x04 }, + { 0x470f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4f07, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4800, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5000, CRL_REG_LEN_08BIT, 0x9f }, + { 0x5001, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5e00, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5d00, CRL_REG_LEN_08BIT, 0x07 }, + { 0x5d01, CRL_REG_LEN_08BIT, 0x00 }, +}; + +static struct crl_register_write_rep ov9281_streamon_regs[] = { + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ + { 0x0100, CRL_REG_LEN_08BIT, 0x01 }, + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ +}; + +static struct crl_register_write_rep ov9281_streamoff_regs[] = { + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ + { 0x0100, CRL_REG_LEN_08BIT, 0x00 }, + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ +}; + +struct crl_register_write_rep ov9281_poweroff_regset[] = { + { 0x4f00, CRL_REG_LEN_08BIT, 0x01 }, + { 0x00, CRL_REG_LEN_DELAY, 10, 0x00}, /* Add a pre 10ms delay */ +}; + +static struct crl_dynamic_register_access ov9281_ana_gain_global_regs[] = { + { + .address = 0x3509, + .len = CRL_REG_LEN_08BIT, + .ops_items = 0, + .ops = 0, + .mask = 0xff, + }, +}; + +static struct crl_arithmetic_ops ov9281_expol_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, +}; + +static struct crl_arithmetic_ops ov9281_expom_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, +}; + +static struct crl_arithmetic_ops ov9281_expoh_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 4, + }, + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 16, + }, +}; + +static struct crl_dynamic_register_access ov9281_exposure_regs[] = { + { + .address = 0x3502, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov9281_expol_ops), + .ops = ov9281_expol_ops, + .mask = 0xff, + }, + { + .address = 0x3501, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov9281_expom_ops), + .ops = ov9281_expom_ops, + .mask = 0xff, + }, + { + .address = 0x3500, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(ov9281_expoh_ops), + .ops = ov9281_expoh_ops, + .mask = 0x0f, + }, +}; + +static struct crl_sensor_detect_config ov9281_sensor_detect_regset[] = { + { + .reg = { 0x300A, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, + { + .reg = { 0x300B, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration ov9281_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 10, + .pixel_rate_csi = 80000000, + .pixel_rate_pa = 80000000, + .csi_lanes = 2, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = ARRAY_SIZE(ov9281_pll_800mbps), + .pll_regs = ov9281_pll_800mbps, + }, +}; + +static struct crl_subdev_rect_rep ov9281_1m_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 800, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 800, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 800, + }, +}; + +static struct crl_mode_rep ov9281_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ov9281_1m_rects), + .sd_rects = ov9281_1m_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 800, + .min_llp = 728, + .min_fll = 910, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(ov9281_mode_1m), + .mode_regs = ov9281_mode_1m, + }, +}; + +static struct crl_sensor_subdev_config ov9281_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ov9281 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ov9281 pixel array", + }, +}; + +static struct crl_sensor_limits ov9281_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1280, + .y_addr_max = 800, + .min_frame_length_lines = 910, + .max_frame_length_lines = 910, + .min_line_length_pixels = 728, + .max_line_length_pixels = 728, + .scaler_m_min = 16, + .scaler_m_max = 16, + .scaler_n_min = 16, + .scaler_n_max = 16, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 3, +}; + +static struct crl_flip_data ov9281_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, +}; + +static struct crl_csi_data_fmt ov9281_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + .regs_items = 0, + .regs = 0, + }, +}; + +static struct crl_v4l2_ctrl ov9281_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0xFF, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov9281_ana_gain_global_regs), + .regs = ov9281_ana_gain_global_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 885, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ov9281_exposure_regs), + .regs = ov9281_exposure_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 910, + .data.std_data.max = 910, + .data.std_data.step = 1, + .data.std_data.def = 910, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 728, + .data.std_data.max = 728, + .data.std_data.step = 1, + .data.std_data.def = 728, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity ov9281_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + .delay = 500, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + .delay = 5000, + }, +}; + +struct crl_sensor_configuration ov9281_crl_configuration = { + + .power_items = ARRAY_SIZE(ov9281_power_items), + .power_entities = ov9281_power_items, + + .powerup_regs_items = ARRAY_SIZE(ov9281_powerup_regset), + .powerup_regs = ov9281_powerup_regset, + + .poweroff_regs_items = 0, + .poweroff_regs = 0, + + .id_reg_items = ARRAY_SIZE(ov9281_sensor_detect_regset), + .id_regs = ov9281_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ov9281_sensor_subdevs), + .subdevs = ov9281_sensor_subdevs, + + .sensor_limits = &ov9281_sensor_limits, + + .pll_config_items = ARRAY_SIZE(ov9281_pll_configurations), + .pll_configs = ov9281_pll_configurations, + + .modes_items = ARRAY_SIZE(ov9281_modes), + .modes = ov9281_modes, + + .streamon_regs_items = ARRAY_SIZE(ov9281_streamon_regs), + .streamon_regs = ov9281_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(ov9281_streamoff_regs), + .streamoff_regs = ov9281_streamoff_regs, + + .v4l2_ctrls_items = ARRAY_SIZE(ov9281_v4l2_ctrls), + .v4l2_ctrl_bank = ov9281_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ov9281_crl_csi_data_fmt), + .csi_fmts = ov9281_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ov9281_flip_configurations), + .flip_data = ov9281_flip_configurations, +}; + +#endif /* __CRLMODULE_OV9281_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_ox03a10_configuration.h b/drivers/media/i2c/crlmodule/crl_ox03a10_configuration.h new file mode 100644 index 000000000000..6b3981522a8f --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_ox03a10_configuration.h @@ -0,0 +1,1709 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation + * + * Author: Chang Ying + * + */ + +#ifndef __CRLMODULE_OX03A10_CONFIGURATION_H_ +#define __CRLMODULE_OX03A10_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +struct crl_sensor_detect_config ox03a10_sensor_detect_regset[] = { + { + .reg = {0x300A, CRL_REG_LEN_08BIT, 0x58}, + .width = 12, + }, + { + .reg = {0x300B, CRL_REG_LEN_08BIT, 0x03}, + .width = 12, + }, + { + .reg = {0x300C, CRL_REG_LEN_08BIT, 0x41}, + .width = 12, + } +}; + +struct crl_sensor_subdev_config ox03a10_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ox03a10 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "ox03a10 pixel array", + } +}; + +struct crl_sensor_limits ox03a10_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1088, +}; + +struct crl_subdev_rect_rep ox03a10_1920_1088_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1088, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1088, + } +}; + +static struct crl_register_write_rep ox03a10_1920_1088_12DCG[] = { + { 0x4d09, CRL_REG_LEN_08BIT, 0x5f }, + { 0x0104, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x32 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x01 }, + { 0x0316, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0317, CRL_REG_LEN_08BIT, 0x12 }, + { 0x0323, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0325, CRL_REG_LEN_08BIT, 0x6c }, + { 0x0326, CRL_REG_LEN_08BIT, 0x00 }, + { 0x032b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0400, CRL_REG_LEN_08BIT, 0xe7 }, + { 0x0401, CRL_REG_LEN_08BIT, 0xff }, + { 0x0404, CRL_REG_LEN_08BIT, 0x2b }, + { 0x0405, CRL_REG_LEN_08BIT, 0x32 }, + { 0x0406, CRL_REG_LEN_08BIT, 0x33 }, + { 0x0407, CRL_REG_LEN_08BIT, 0x8f }, + { 0x0408, CRL_REG_LEN_08BIT, 0x0c }, + { 0x0410, CRL_REG_LEN_08BIT, 0xe7 }, + { 0x0411, CRL_REG_LEN_08BIT, 0xff }, + { 0x0414, CRL_REG_LEN_08BIT, 0x2b }, + { 0x0415, CRL_REG_LEN_08BIT, 0x32 }, + { 0x0416, CRL_REG_LEN_08BIT, 0x33 }, + { 0x0417, CRL_REG_LEN_08BIT, 0x8f }, + { 0x0418, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3002, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3012, CRL_REG_LEN_08BIT, 0x41 }, + { 0x3016, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3017, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3018, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3019, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301a, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301b, CRL_REG_LEN_08BIT, 0xb4 }, + { 0x301e, CRL_REG_LEN_08BIT, 0xb8 }, + { 0x301f, CRL_REG_LEN_08BIT, 0xe1 }, + { 0x3022, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x3023, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3024, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3028, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3029, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3706, CRL_REG_LEN_08BIT, 0x39 }, + { 0x370a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x370b, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3712, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3713, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3716, CRL_REG_LEN_08BIT, 0x04 }, + { 0x371d, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3721, CRL_REG_LEN_08BIT, 0x1c }, + { 0x372c, CRL_REG_LEN_08BIT, 0x17 }, + { 0x3733, CRL_REG_LEN_08BIT, 0x41 }, + { 0x3741, CRL_REG_LEN_08BIT, 0x44 }, + { 0x3742, CRL_REG_LEN_08BIT, 0x34 }, + { 0x3746, CRL_REG_LEN_08BIT, 0x03 }, + { 0x374b, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3755, CRL_REG_LEN_08BIT, 0x00 }, + { 0x376c, CRL_REG_LEN_08BIT, 0x15 }, + { 0x376d, CRL_REG_LEN_08BIT, 0x08 }, + { 0x376f, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3770, CRL_REG_LEN_08BIT, 0x91 }, + { 0x3771, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3774, CRL_REG_LEN_08BIT, 0x82 }, + { 0x3777, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3779, CRL_REG_LEN_08BIT, 0x22 }, + { 0x377a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x377b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x377c, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3785, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3790, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3793, CRL_REG_LEN_08BIT, 0x04 }, + { 0x379c, CRL_REG_LEN_08BIT, 0x01 }, + { 0x37a1, CRL_REG_LEN_08BIT, 0x80 }, + { 0x37b3, CRL_REG_LEN_08BIT, 0x0a }, + { 0x37bb, CRL_REG_LEN_08BIT, 0x08 }, + { 0x37be, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x37bf, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37c6, CRL_REG_LEN_08BIT, 0x48 }, + { 0x37c7, CRL_REG_LEN_08BIT, 0x38 }, + { 0x37c9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37ca, CRL_REG_LEN_08BIT, 0x08 }, + { 0x37cb, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37cc, CRL_REG_LEN_08BIT, 0x40 }, + { 0x37d1, CRL_REG_LEN_08BIT, 0x39 }, + { 0x37d2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37d3, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x37d5, CRL_REG_LEN_08BIT, 0x39 }, + { 0x37d6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37d7, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3c06, CRL_REG_LEN_08BIT, 0x29 }, + { 0x3c0b, CRL_REG_LEN_08BIT, 0xa8 }, + { 0x3c12, CRL_REG_LEN_08BIT, 0x89 }, + { 0x3c14, CRL_REG_LEN_08BIT, 0x81 }, + { 0x3c18, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3c3b, CRL_REG_LEN_08BIT, 0x38 }, + { 0x3c53, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3c55, CRL_REG_LEN_08BIT, 0xeb }, + { 0x3101, CRL_REG_LEN_08BIT, 0x32 }, + { 0x3192, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3193, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3206, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x3216, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3304, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3400, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3409, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3601, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3602, CRL_REG_LEN_08BIT, 0x42 }, + { 0x3603, CRL_REG_LEN_08BIT, 0xe3 }, + { 0x3604, CRL_REG_LEN_08BIT, 0x93 }, + { 0x3605, CRL_REG_LEN_08BIT, 0xff }, + { 0x3606, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3607, CRL_REG_LEN_08BIT, 0x4a }, + { 0x3608, CRL_REG_LEN_08BIT, 0x98 }, + { 0x3609, CRL_REG_LEN_08BIT, 0x70 }, + { 0x360a, CRL_REG_LEN_08BIT, 0x90 }, + { 0x360b, CRL_REG_LEN_08BIT, 0x0a }, + { 0x360e, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3610, CRL_REG_LEN_08BIT, 0x89 }, + { 0x3611, CRL_REG_LEN_08BIT, 0x4b }, + { 0x3612, CRL_REG_LEN_08BIT, 0x4e }, + { 0x3619, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3620, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3621, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3626, CRL_REG_LEN_08BIT, 0x0e }, + { 0x362c, CRL_REG_LEN_08BIT, 0x0e }, + { 0x362d, CRL_REG_LEN_08BIT, 0x12 }, + { 0x362e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x362f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3630, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3631, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3632, CRL_REG_LEN_08BIT, 0x99 }, + { 0x3633, CRL_REG_LEN_08BIT, 0x99 }, + { 0x3643, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3644, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3645, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3646, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3647, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3648, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3649, CRL_REG_LEN_08BIT, 0x11 }, + { 0x364a, CRL_REG_LEN_08BIT, 0x12 }, + { 0x364c, CRL_REG_LEN_08BIT, 0x0e }, + { 0x364d, CRL_REG_LEN_08BIT, 0x0e }, + { 0x364e, CRL_REG_LEN_08BIT, 0x12 }, + { 0x364f, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3652, CRL_REG_LEN_08BIT, 0xc5 }, + { 0x3654, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3656, CRL_REG_LEN_08BIT, 0xcf }, + { 0x3657, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3658, CRL_REG_LEN_08BIT, 0x08 }, + { 0x365a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x365b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x365c, CRL_REG_LEN_08BIT, 0x00 }, + { 0x365d, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3660, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3661, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3662, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3663, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3665, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3666, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3667, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3668, CRL_REG_LEN_08BIT, 0x95 }, + { 0x3669, CRL_REG_LEN_08BIT, 0x16 }, + { 0x366f, CRL_REG_LEN_08BIT, 0xc4 }, + { 0x3671, CRL_REG_LEN_08BIT, 0x37 }, + { 0x3673, CRL_REG_LEN_08BIT, 0x6a }, + { 0x3678, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3800, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3801, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3802, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3803, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3804, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3805, CRL_REG_LEN_08BIT, 0x8f }, + { 0x3806, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3807, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3808, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3809, CRL_REG_LEN_08BIT, 0x80 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x04 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x40 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3810, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3813, CRL_REG_LEN_08BIT, 0x04 }, + { 0x381c, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3820, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3822, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3832, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3833, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3834, CRL_REG_LEN_08BIT, 0x00 }, + { 0x383d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x384c, CRL_REG_LEN_08BIT, 0x02 }, + { 0x384d, CRL_REG_LEN_08BIT, 0x14 }, + { 0x384e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x384f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3850, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3851, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3852, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3853, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3854, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3855, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3856, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3857, CRL_REG_LEN_08BIT, 0x33 }, + { 0x3858, CRL_REG_LEN_08BIT, 0x7c }, + { 0x3859, CRL_REG_LEN_08BIT, 0x00 }, + { 0x385a, CRL_REG_LEN_08BIT, 0x03 }, + { 0x385b, CRL_REG_LEN_08BIT, 0x05 }, + { 0x385c, CRL_REG_LEN_08BIT, 0x32 }, + { 0x385f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3860, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3861, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3862, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3863, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3864, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3865, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3866, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3b40, CRL_REG_LEN_08BIT, 0x3e }, + { 0x3b41, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b42, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3b43, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b44, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b45, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3b46, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b47, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3b84, CRL_REG_LEN_08BIT, 0x36 }, + { 0x3b85, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b86, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b87, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3b88, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b89, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3b8a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b8b, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3b8e, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3b8f, CRL_REG_LEN_08BIT, 0xe8 }, + { 0x3d85, CRL_REG_LEN_08BIT, 0x0b }, + { 0x3d8c, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d8d, CRL_REG_LEN_08BIT, 0x26 }, + { 0x3d97, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d98, CRL_REG_LEN_08BIT, 0x24 }, + { 0x3d99, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d9a, CRL_REG_LEN_08BIT, 0x6d }, + { 0x3d9b, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d9c, CRL_REG_LEN_08BIT, 0x6e }, + { 0x3d9d, CRL_REG_LEN_08BIT, 0x73 }, + { 0x3d9e, CRL_REG_LEN_08BIT, 0xff }, + { 0x3e07, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3f00, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4000, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x4001, CRL_REG_LEN_08BIT, 0xeb }, + { 0x4004, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4005, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, + { 0x4009, CRL_REG_LEN_08BIT, 0x0d }, + { 0x400a, CRL_REG_LEN_08BIT, 0x08 }, + { 0x400b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x400f, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4010, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4011, CRL_REG_LEN_08BIT, 0xff }, + { 0x4016, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4017, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4018, CRL_REG_LEN_08BIT, 0x18 }, + { 0x401a, CRL_REG_LEN_08BIT, 0x58 }, + { 0x4028, CRL_REG_LEN_08BIT, 0x4f }, + { 0x402e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4030, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4031, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4032, CRL_REG_LEN_08BIT, 0x9e }, + { 0x4033, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4308, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4501, CRL_REG_LEN_08BIT, 0x18 }, + { 0x4502, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4507, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4580, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x4581, CRL_REG_LEN_08BIT, 0xc7 }, + { 0x4582, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4602, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4603, CRL_REG_LEN_08BIT, 0x01 }, + { 0x460a, CRL_REG_LEN_08BIT, 0x36 }, + { 0x460c, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4700, CRL_REG_LEN_08BIT, 0x2a }, + { 0x470a, CRL_REG_LEN_08BIT, 0x08 }, + { 0x470b, CRL_REG_LEN_08BIT, 0x88 }, + { 0x4800, CRL_REG_LEN_08BIT, 0x04 }, + { 0x480e, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4813, CRL_REG_LEN_08BIT, 0xd2 }, + { 0x4815, CRL_REG_LEN_08BIT, 0x2b }, + { 0x4837, CRL_REG_LEN_08BIT, 0x28 }, + { 0x484a, CRL_REG_LEN_08BIT, 0x3f }, + { 0x484b, CRL_REG_LEN_08BIT, 0x67 }, + { 0x4850, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4861, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x4886, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4900, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4903, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4f00, CRL_REG_LEN_08BIT, 0xff }, + { 0x4f01, CRL_REG_LEN_08BIT, 0xff }, + { 0x4f05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5180, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5181, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5182, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5183, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5184, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5185, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5186, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5187, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a0, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a6, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c0, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c6, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5380, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5381, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5382, CRL_REG_LEN_08BIT, 0x2e }, + { 0x53a0, CRL_REG_LEN_08BIT, 0x41 }, + { 0x53a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x53a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x53a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x53a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x53a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5400, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5401, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5402, CRL_REG_LEN_08BIT, 0x2e }, + { 0x5420, CRL_REG_LEN_08BIT, 0x41 }, + { 0x5422, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5423, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5424, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5425, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5427, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5480, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5481, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5482, CRL_REG_LEN_08BIT, 0x2e }, + { 0x54a0, CRL_REG_LEN_08BIT, 0x41 }, + { 0x54a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x54a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x54a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x54a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x54a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5800, CRL_REG_LEN_08BIT, 0x31 }, + { 0x5801, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5804, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5805, CRL_REG_LEN_08BIT, 0x40 }, + { 0x5806, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5807, CRL_REG_LEN_08BIT, 0x00 }, + { 0x580e, CRL_REG_LEN_08BIT, 0x10 }, + { 0x5812, CRL_REG_LEN_08BIT, 0x34 }, + { 0x5000, CRL_REG_LEN_08BIT, 0x89 }, + { 0x5001, CRL_REG_LEN_08BIT, 0x42 }, + { 0x5002, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5003, CRL_REG_LEN_08BIT, 0x16 }, + { 0x503e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x503f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5602, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5603, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5604, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5605, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5606, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5607, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5608, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5609, CRL_REG_LEN_08BIT, 0x20 }, + { 0x560a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x560b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x560c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x560d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x560e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x560f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5610, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5611, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5612, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5613, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5614, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5615, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5616, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5617, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5618, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5619, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5642, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5643, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5644, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5645, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5646, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5647, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5648, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5649, CRL_REG_LEN_08BIT, 0x20 }, + { 0x564a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x564b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x564c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x564d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x564e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x564f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5650, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5651, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5652, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5653, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5654, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5655, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5656, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5657, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5658, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5659, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5682, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5683, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5684, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5685, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5686, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5687, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5688, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5689, CRL_REG_LEN_08BIT, 0x20 }, + { 0x568a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x568b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x568c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x568d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x568e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x568f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5690, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5691, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5692, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5693, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5694, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5695, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5696, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5697, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5698, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5699, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5709, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5749, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5789, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5200, CRL_REG_LEN_08BIT, 0x70 }, + { 0x5201, CRL_REG_LEN_08BIT, 0x70 }, + { 0x5202, CRL_REG_LEN_08BIT, 0x73 }, + { 0x5203, CRL_REG_LEN_08BIT, 0xff }, + { 0x5205, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5285, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5305, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5082, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x50c2, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x5102, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x34 }, + { 0x380c, CRL_REG_LEN_08BIT, 0x08 }, + { 0x380d, CRL_REG_LEN_08BIT, 0x78 }, + { 0x384c, CRL_REG_LEN_08BIT, 0x02 }, + { 0x384d, CRL_REG_LEN_08BIT, 0x14 }, + { 0x460a, CRL_REG_LEN_08BIT, 0x0e }, + { 0x0100, CRL_REG_LEN_08BIT, 0x01 }, +}; + +static struct crl_register_write_rep ox03a10_1920_1088_12DCG_12VS[] = { + { 0x4d09, CRL_REG_LEN_08BIT, 0x5f }, + { 0x0104, CRL_REG_LEN_08BIT, 0x04 }, + { 0x0303, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0305, CRL_REG_LEN_08BIT, 0x36 }, + { 0x0307, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0316, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0317, CRL_REG_LEN_08BIT, 0x12 }, + { 0x0323, CRL_REG_LEN_08BIT, 0x02 }, + { 0x0325, CRL_REG_LEN_08BIT, 0x6c }, + { 0x0326, CRL_REG_LEN_08BIT, 0x00 }, + { 0x032b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x0400, CRL_REG_LEN_08BIT, 0xe7 }, + { 0x0401, CRL_REG_LEN_08BIT, 0xff }, + { 0x0404, CRL_REG_LEN_08BIT, 0x2b }, + { 0x0405, CRL_REG_LEN_08BIT, 0x32 }, + { 0x0406, CRL_REG_LEN_08BIT, 0x33 }, + { 0x0407, CRL_REG_LEN_08BIT, 0x8f }, + { 0x0408, CRL_REG_LEN_08BIT, 0x0c }, + { 0x0410, CRL_REG_LEN_08BIT, 0xe7 }, + { 0x0411, CRL_REG_LEN_08BIT, 0xff }, + { 0x0414, CRL_REG_LEN_08BIT, 0x2b }, + { 0x0415, CRL_REG_LEN_08BIT, 0x32 }, + { 0x0416, CRL_REG_LEN_08BIT, 0x33 }, + { 0x0417, CRL_REG_LEN_08BIT, 0x8f }, + { 0x0418, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3002, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3012, CRL_REG_LEN_08BIT, 0x41 }, + { 0x3016, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3017, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3018, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3019, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301a, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x301b, CRL_REG_LEN_08BIT, 0xb4 }, + { 0x301e, CRL_REG_LEN_08BIT, 0xb8 }, + { 0x301f, CRL_REG_LEN_08BIT, 0xe1 }, + { 0x3022, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x3023, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3024, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3028, CRL_REG_LEN_08BIT, 0xf0 }, + { 0x3029, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3706, CRL_REG_LEN_08BIT, 0x39 }, + { 0x370a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x370b, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3712, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3713, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3716, CRL_REG_LEN_08BIT, 0x04 }, + { 0x371d, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3721, CRL_REG_LEN_08BIT, 0x1c }, + { 0x372c, CRL_REG_LEN_08BIT, 0x17 }, + { 0x3733, CRL_REG_LEN_08BIT, 0x41 }, + { 0x3741, CRL_REG_LEN_08BIT, 0x44 }, + { 0x3742, CRL_REG_LEN_08BIT, 0x34 }, + { 0x3746, CRL_REG_LEN_08BIT, 0x03 }, + { 0x374b, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3755, CRL_REG_LEN_08BIT, 0x01 }, + { 0x376c, CRL_REG_LEN_08BIT, 0x15 }, + { 0x376d, CRL_REG_LEN_08BIT, 0x08 }, + { 0x376f, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3770, CRL_REG_LEN_08BIT, 0x91 }, + { 0x3771, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3774, CRL_REG_LEN_08BIT, 0x82 }, + { 0x3777, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3779, CRL_REG_LEN_08BIT, 0x22 }, + { 0x377a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x377b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x377c, CRL_REG_LEN_08BIT, 0x48 }, + { 0x3785, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3790, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3793, CRL_REG_LEN_08BIT, 0x00 }, + { 0x379c, CRL_REG_LEN_08BIT, 0x01 }, + { 0x37a1, CRL_REG_LEN_08BIT, 0x80 }, + { 0x37b3, CRL_REG_LEN_08BIT, 0x0a }, + { 0x37bb, CRL_REG_LEN_08BIT, 0x08 }, + { 0x37be, CRL_REG_LEN_08BIT, 0xe0 }, + { 0x37bf, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37c6, CRL_REG_LEN_08BIT, 0x48 }, + { 0x37c7, CRL_REG_LEN_08BIT, 0x38 }, + { 0x37c9, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37ca, CRL_REG_LEN_08BIT, 0x08 }, + { 0x37cb, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37cc, CRL_REG_LEN_08BIT, 0x40 }, + { 0x37d1, CRL_REG_LEN_08BIT, 0x39 }, + { 0x37d2, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37d3, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x37d5, CRL_REG_LEN_08BIT, 0x39 }, + { 0x37d6, CRL_REG_LEN_08BIT, 0x00 }, + { 0x37d7, CRL_REG_LEN_08BIT, 0xa3 }, + { 0x3c06, CRL_REG_LEN_08BIT, 0x29 }, + { 0x3c0b, CRL_REG_LEN_08BIT, 0xa8 }, + { 0x3c12, CRL_REG_LEN_08BIT, 0x89 }, + { 0x3c14, CRL_REG_LEN_08BIT, 0x81 }, + { 0x3c18, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3c3b, CRL_REG_LEN_08BIT, 0x38 }, + { 0x3c53, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3c55, CRL_REG_LEN_08BIT, 0xeb }, + { 0x3101, CRL_REG_LEN_08BIT, 0x32 }, + { 0x3192, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3193, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3206, CRL_REG_LEN_08BIT, 0xc8 }, + { 0x3216, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3304, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3400, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3409, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3600, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3601, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3602, CRL_REG_LEN_08BIT, 0x42 }, + { 0x3603, CRL_REG_LEN_08BIT, 0xe3 }, + { 0x3604, CRL_REG_LEN_08BIT, 0x93 }, + { 0x3605, CRL_REG_LEN_08BIT, 0xff }, + { 0x3606, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3607, CRL_REG_LEN_08BIT, 0x4a }, + { 0x3608, CRL_REG_LEN_08BIT, 0x98 }, + { 0x3609, CRL_REG_LEN_08BIT, 0x70 }, + { 0x360a, CRL_REG_LEN_08BIT, 0xd0 }, + { 0x360b, CRL_REG_LEN_08BIT, 0x0b }, + { 0x360e, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3610, CRL_REG_LEN_08BIT, 0x89 }, + { 0x3611, CRL_REG_LEN_08BIT, 0x4b }, + { 0x3612, CRL_REG_LEN_08BIT, 0x4e }, + { 0x3619, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3620, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3621, CRL_REG_LEN_08BIT, 0x80 }, + { 0x3626, CRL_REG_LEN_08BIT, 0x0e }, + { 0x362c, CRL_REG_LEN_08BIT, 0x0e }, + { 0x362d, CRL_REG_LEN_08BIT, 0x12 }, + { 0x362e, CRL_REG_LEN_08BIT, 0x0a }, + { 0x362f, CRL_REG_LEN_08BIT, 0x17 }, + { 0x3630, CRL_REG_LEN_08BIT, 0x2e }, + { 0x3631, CRL_REG_LEN_08BIT, 0x3f }, + { 0x3632, CRL_REG_LEN_08BIT, 0x99 }, + { 0x3633, CRL_REG_LEN_08BIT, 0x99 }, + { 0x3643, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3644, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3645, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3646, CRL_REG_LEN_08BIT, 0x0f }, + { 0x3647, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3648, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3649, CRL_REG_LEN_08BIT, 0x11 }, + { 0x364a, CRL_REG_LEN_08BIT, 0x12 }, + { 0x364c, CRL_REG_LEN_08BIT, 0x0e }, + { 0x364d, CRL_REG_LEN_08BIT, 0x0e }, + { 0x364e, CRL_REG_LEN_08BIT, 0x12 }, + { 0x364f, CRL_REG_LEN_08BIT, 0x0e }, + { 0x3652, CRL_REG_LEN_08BIT, 0xc5 }, + { 0x3654, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3656, CRL_REG_LEN_08BIT, 0xcf }, + { 0x3657, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3658, CRL_REG_LEN_08BIT, 0x08 }, + { 0x365a, CRL_REG_LEN_08BIT, 0x3f }, + { 0x365b, CRL_REG_LEN_08BIT, 0x2e }, + { 0x365c, CRL_REG_LEN_08BIT, 0x17 }, + { 0x365d, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3660, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3661, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3662, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3663, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3665, CRL_REG_LEN_08BIT, 0x12 }, + { 0x3666, CRL_REG_LEN_08BIT, 0x13 }, + { 0x3667, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3668, CRL_REG_LEN_08BIT, 0x95 }, + { 0x3669, CRL_REG_LEN_08BIT, 0x16 }, + { 0x366f, CRL_REG_LEN_08BIT, 0xc4 }, + { 0x3671, CRL_REG_LEN_08BIT, 0x37 }, + { 0x3673, CRL_REG_LEN_08BIT, 0x6a }, + { 0x3678, CRL_REG_LEN_08BIT, 0x88 }, + { 0x3800, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3801, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3802, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3803, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3804, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3805, CRL_REG_LEN_08BIT, 0x8f }, + { 0x3806, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3807, CRL_REG_LEN_08BIT, 0x0c }, + { 0x3808, CRL_REG_LEN_08BIT, 0x07 }, + { 0x3809, CRL_REG_LEN_08BIT, 0x80 }, + { 0x380a, CRL_REG_LEN_08BIT, 0x04 }, + { 0x380b, CRL_REG_LEN_08BIT, 0x40 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3810, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3813, CRL_REG_LEN_08BIT, 0x04 }, + { 0x381c, CRL_REG_LEN_08BIT, 0x08 }, + { 0x3820, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3822, CRL_REG_LEN_08BIT, 0x14 }, + { 0x3832, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3833, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3834, CRL_REG_LEN_08BIT, 0x00 }, + { 0x383d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x384c, CRL_REG_LEN_08BIT, 0x02 }, + { 0x384d, CRL_REG_LEN_08BIT, 0x14 }, + { 0x384e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x384f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3850, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3851, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3852, CRL_REG_LEN_08BIT, 0x01 }, + { 0x3853, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3854, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3855, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3856, CRL_REG_LEN_08BIT, 0x05 }, + { 0x3857, CRL_REG_LEN_08BIT, 0x33 }, + { 0x3858, CRL_REG_LEN_08BIT, 0x7c }, + { 0x3859, CRL_REG_LEN_08BIT, 0x00 }, + { 0x385a, CRL_REG_LEN_08BIT, 0x03 }, + { 0x385b, CRL_REG_LEN_08BIT, 0x05 }, + { 0x385c, CRL_REG_LEN_08BIT, 0x32 }, + { 0x385f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3860, CRL_REG_LEN_08BIT, 0x10 }, + { 0x3861, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3862, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3863, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3864, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3865, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3866, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3b40, CRL_REG_LEN_08BIT, 0x3e }, + { 0x3b41, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b42, CRL_REG_LEN_08BIT, 0x02 }, + { 0x3b43, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b44, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b45, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3b46, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b47, CRL_REG_LEN_08BIT, 0x20 }, + { 0x3b84, CRL_REG_LEN_08BIT, 0x36 }, + { 0x3b85, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b86, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b87, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3b88, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b89, CRL_REG_LEN_08BIT, 0x04 }, + { 0x3b8a, CRL_REG_LEN_08BIT, 0x00 }, + { 0x3b8b, CRL_REG_LEN_08BIT, 0x0a }, + { 0x3b8e, CRL_REG_LEN_08BIT, 0x03 }, + { 0x3b8f, CRL_REG_LEN_08BIT, 0xe8 }, + { 0x3d85, CRL_REG_LEN_08BIT, 0x0b }, + { 0x3d8c, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d8d, CRL_REG_LEN_08BIT, 0x26 }, + { 0x3d97, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d98, CRL_REG_LEN_08BIT, 0x24 }, + { 0x3d99, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d9a, CRL_REG_LEN_08BIT, 0x6d }, + { 0x3d9b, CRL_REG_LEN_08BIT, 0x70 }, + { 0x3d9c, CRL_REG_LEN_08BIT, 0x6e }, + { 0x3d9d, CRL_REG_LEN_08BIT, 0x73 }, + { 0x3d9e, CRL_REG_LEN_08BIT, 0xff }, + { 0x3e07, CRL_REG_LEN_08BIT, 0x40 }, + { 0x3f00, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4000, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x4001, CRL_REG_LEN_08BIT, 0xeb }, + { 0x4004, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4005, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4008, CRL_REG_LEN_08BIT, 0x02 }, + { 0x4009, CRL_REG_LEN_08BIT, 0x0d }, + { 0x400a, CRL_REG_LEN_08BIT, 0x08 }, + { 0x400b, CRL_REG_LEN_08BIT, 0x00 }, + { 0x400f, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4010, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4011, CRL_REG_LEN_08BIT, 0xff }, + { 0x4016, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4017, CRL_REG_LEN_08BIT, 0x10 }, + { 0x4018, CRL_REG_LEN_08BIT, 0x18 }, + { 0x401a, CRL_REG_LEN_08BIT, 0x58 }, + { 0x4028, CRL_REG_LEN_08BIT, 0x4f }, + { 0x402e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x402f, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4030, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4031, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4032, CRL_REG_LEN_08BIT, 0x9e }, + { 0x4033, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4308, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4501, CRL_REG_LEN_08BIT, 0x18 }, + { 0x4502, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4507, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4580, CRL_REG_LEN_08BIT, 0xf8 }, + { 0x4581, CRL_REG_LEN_08BIT, 0xc7 }, + { 0x4582, CRL_REG_LEN_08BIT, 0x07 }, + { 0x4602, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4603, CRL_REG_LEN_08BIT, 0x01 }, + { 0x460a, CRL_REG_LEN_08BIT, 0x36 }, + { 0x460c, CRL_REG_LEN_08BIT, 0x60 }, + { 0x4700, CRL_REG_LEN_08BIT, 0x2a }, + { 0x470a, CRL_REG_LEN_08BIT, 0x08 }, + { 0x470b, CRL_REG_LEN_08BIT, 0x88 }, + { 0x4800, CRL_REG_LEN_08BIT, 0x04 }, + { 0x480e, CRL_REG_LEN_08BIT, 0x04 }, + { 0x4813, CRL_REG_LEN_08BIT, 0xd2 }, + { 0x4815, CRL_REG_LEN_08BIT, 0x2b }, + { 0x4837, CRL_REG_LEN_08BIT, 0x18 }, + { 0x484a, CRL_REG_LEN_08BIT, 0x3f }, + { 0x484b, CRL_REG_LEN_08BIT, 0x67 }, + { 0x4850, CRL_REG_LEN_08BIT, 0x40 }, + { 0x4861, CRL_REG_LEN_08BIT, 0xa0 }, + { 0x4886, CRL_REG_LEN_08BIT, 0x00 }, + { 0x4900, CRL_REG_LEN_08BIT, 0x08 }, + { 0x4903, CRL_REG_LEN_08BIT, 0x80 }, + { 0x4f00, CRL_REG_LEN_08BIT, 0xff }, + { 0x4f01, CRL_REG_LEN_08BIT, 0xff }, + { 0x4f05, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5180, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5181, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5182, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5183, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5184, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5185, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5186, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5187, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a0, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51a6, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c0, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c1, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x51c6, CRL_REG_LEN_08BIT, 0x04 }, + { 0x51c7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5380, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5381, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5382, CRL_REG_LEN_08BIT, 0x2e }, + { 0x53a0, CRL_REG_LEN_08BIT, 0x41 }, + { 0x53a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x53a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x53a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x53a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x53a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5400, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5401, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5402, CRL_REG_LEN_08BIT, 0x2e }, + { 0x5420, CRL_REG_LEN_08BIT, 0x41 }, + { 0x5422, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5423, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5424, CRL_REG_LEN_08BIT, 0x04 }, + { 0x5425, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5427, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5480, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5481, CRL_REG_LEN_08BIT, 0x94 }, + { 0x5482, CRL_REG_LEN_08BIT, 0x2e }, + { 0x54a0, CRL_REG_LEN_08BIT, 0x41 }, + { 0x54a2, CRL_REG_LEN_08BIT, 0x04 }, + { 0x54a3, CRL_REG_LEN_08BIT, 0x00 }, + { 0x54a4, CRL_REG_LEN_08BIT, 0x04 }, + { 0x54a5, CRL_REG_LEN_08BIT, 0x00 }, + { 0x54a7, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5800, CRL_REG_LEN_08BIT, 0x31 }, + { 0x5801, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5804, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5805, CRL_REG_LEN_08BIT, 0x40 }, + { 0x5806, CRL_REG_LEN_08BIT, 0x01 }, + { 0x5807, CRL_REG_LEN_08BIT, 0x00 }, + { 0x580e, CRL_REG_LEN_08BIT, 0x10 }, + { 0x5812, CRL_REG_LEN_08BIT, 0x34 }, + { 0x5000, CRL_REG_LEN_08BIT, 0x89 }, + { 0x5001, CRL_REG_LEN_08BIT, 0x42 }, + { 0x5002, CRL_REG_LEN_08BIT, 0x19 }, + { 0x5003, CRL_REG_LEN_08BIT, 0x16 }, + { 0x503e, CRL_REG_LEN_08BIT, 0x00 }, + { 0x503f, CRL_REG_LEN_08BIT, 0x00 }, + { 0x5602, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5603, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5604, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5605, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5606, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5607, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5608, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5609, CRL_REG_LEN_08BIT, 0x20 }, + { 0x560a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x560b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x560c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x560d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x560e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x560f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5610, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5611, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5612, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5613, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5614, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5615, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5616, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5617, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5618, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5619, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5642, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5643, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5644, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5645, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5646, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5647, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5648, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5649, CRL_REG_LEN_08BIT, 0x20 }, + { 0x564a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x564b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x564c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x564d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x564e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x564f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5650, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5651, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5652, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5653, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5654, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5655, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5656, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5657, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5658, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5659, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5682, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5683, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5684, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5685, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5686, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5687, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5688, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5689, CRL_REG_LEN_08BIT, 0x20 }, + { 0x568a, CRL_REG_LEN_08BIT, 0x02 }, + { 0x568b, CRL_REG_LEN_08BIT, 0x58 }, + { 0x568c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x568d, CRL_REG_LEN_08BIT, 0x20 }, + { 0x568e, CRL_REG_LEN_08BIT, 0x02 }, + { 0x568f, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5690, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5691, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5692, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5693, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5694, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5695, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5696, CRL_REG_LEN_08BIT, 0x02 }, + { 0x5697, CRL_REG_LEN_08BIT, 0x58 }, + { 0x5698, CRL_REG_LEN_08BIT, 0x03 }, + { 0x5699, CRL_REG_LEN_08BIT, 0x20 }, + { 0x5709, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5749, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5789, CRL_REG_LEN_08BIT, 0x0e }, + { 0x5200, CRL_REG_LEN_08BIT, 0x70 }, + { 0x5201, CRL_REG_LEN_08BIT, 0x70 }, + { 0x5202, CRL_REG_LEN_08BIT, 0x73 }, + { 0x5203, CRL_REG_LEN_08BIT, 0xff }, + { 0x5205, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5285, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5305, CRL_REG_LEN_08BIT, 0x6c }, + { 0x5082, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x50c2, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x5102, CRL_REG_LEN_08BIT, 0xb0 }, + { 0x380e, CRL_REG_LEN_08BIT, 0x05 }, + { 0x380f, CRL_REG_LEN_08BIT, 0x34 }, + { 0x380c, CRL_REG_LEN_08BIT, 0x06 }, + { 0x380d, CRL_REG_LEN_08BIT, 0xcc }, + { 0x384c, CRL_REG_LEN_08BIT, 0x03 }, + { 0x384d, CRL_REG_LEN_08BIT, 0xc0 }, + { 0x460c, CRL_REG_LEN_08BIT, 0x40 }, + { 0x0100, CRL_REG_LEN_08BIT, 0x01 }, +}; + +struct crl_ctrl_data_pair ox03a10_ctrl_data_modes[] = { + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 0, + }, + { + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .data = 1, + } +}; + +struct crl_mode_rep ox03a10_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(ox03a10_1920_1088_rects), + .sd_rects = ox03a10_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .comp_items = 1, + .ctrl_data = &ox03a10_ctrl_data_modes[0], + .mode_regs_items = ARRAY_SIZE(ox03a10_1920_1088_12DCG), + .mode_regs = ox03a10_1920_1088_12DCG, + }, + { + .sd_rects_items = ARRAY_SIZE(ox03a10_1920_1088_rects), + .sd_rects = ox03a10_1920_1088_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1088, + .comp_items = 1, + .ctrl_data = &ox03a10_ctrl_data_modes[1], + .mode_regs_items = ARRAY_SIZE(ox03a10_1920_1088_12DCG_12VS), + .mode_regs = ox03a10_1920_1088_12DCG_12VS, + }, +}; + +static struct crl_arithmetic_ops bits_5_0[] = { + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0x3F, + }, + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + } +}; + +static struct crl_arithmetic_ops bits_10_6[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 6, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0x1F, + } +}; + +static struct crl_arithmetic_ops bits_13_10[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 10, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0xF, + } +}; + +static struct crl_arithmetic_ops bits_9_2[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 2, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0xFF, + } +}; + +static struct crl_arithmetic_ops bits_1_0[] = { + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0x3, + }, + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 6, + } +}; + +static struct crl_arithmetic_ops bits_15_8[] = { + { + .op = CRL_BITWISE_RSHIFT, + .operand.entity_val = 8, + }, + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0xff, + } +}; + +static struct crl_arithmetic_ops bits_7_0[] = { + { + .op = CRL_BITWISE_AND, + .operand.entity_val = 0xff, + } +}; + +static struct crl_dynamic_register_access ox03a10_hcg_real_gain[] = { + { + .address = 0x3508, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_10_6), + .ops = bits_10_6, + }, + { + .address = 0x3509, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_5_0), + .ops = bits_5_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_hcg_digital_gain[] = { + { + .address = 0x350a, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_13_10), + .ops = bits_13_10, + }, + { + .address = 0x350b, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_9_2), + .ops = bits_9_2, + }, + { + .address = 0x350c, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_1_0), + .ops = bits_1_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_lcg_real_gain[] = { + { + .address = 0x3548, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_10_6), + .ops = bits_10_6, + }, + { + .address = 0x3549, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_5_0), + .ops = bits_5_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_lcg_digital_gain[] = { + { + .address = 0x354a, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_13_10), + .ops = bits_13_10, + }, + { + .address = 0x354b, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_9_2), + .ops = bits_9_2, + }, + { + .address = 0x354c, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_1_0), + .ops = bits_1_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_dcg_exposure_coarse[] = { + { + .address = 0x3501, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_15_8), + .ops = bits_15_8, + }, + { + .address = 0x3502, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_7_0), + .ops = bits_7_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_vs_real_gain[] = { + { + .address = 0x3588, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_10_6), + .ops = bits_10_6, + }, + { + .address = 0x3589, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_5_0), + .ops = bits_5_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_vs_digital_gain[] = { + { + .address = 0x358a, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_13_10), + .ops = bits_13_10, + }, + { + .address = 0x358b, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_9_2), + .ops = bits_9_2, + }, + { + .address = 0x358c, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_1_0), + .ops = bits_1_0, + } +}; + +static struct crl_dynamic_register_access ox03a10_vs_exposure_coarse[] = { + { + .address = 0x3581, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_15_8), + .ops = bits_15_8, + }, + { + .address = 0x3582, + .len = CRL_REG_LEN_08BIT, + .ops_items = ARRAY_SIZE(bits_7_0), + .ops = bits_7_0, + } +}; + +static struct crl_arithmetic_ops ox03a10_mirror_ops[] = { + { + .op = CRL_BITWISE_LSHIFT, + .operand.entity_val = 2, + }, + { + .op = CRL_BITWISE_OR, + .operand.entity_val = 0x20, + }, +}; + +static struct crl_dynamic_register_access ox03a10_h_flip_regs[] = { + { + .address = 0x3821, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = ARRAY_SIZE(ox03a10_mirror_ops), + .ops = ox03a10_mirror_ops, + .mask = 0x24, + }, + { + .address = 0x3811, + .len = CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, + .ops_items = 0, + .ops = 0, + .mask = 0x1, + }, +}; + +/* keep GRBG no change during flip, for tuning file handle GRBG only */ +static struct crl_flip_data ox03a10_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + }, +}; + +struct crl_v4l2_ctrl ox03a10_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "DCG exposure", + .ctrl_id = V4L2_CID_EXPOSURE, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = 1280, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_dcg_exposure_coarse), + .regs = ox03a10_dcg_exposure_coarse, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "VS exposure", + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1, + .data.std_data.max = 1280, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_vs_exposure_coarse), + .regs = ox03a10_vs_exposure_coarse, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "HCG digital gain", + .ctrl_id = V4L2_CID_GAIN, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x400, + .data.std_data.max = 0x3FFF, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_hcg_digital_gain), + .regs = ox03a10_hcg_digital_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "HCG analog gain", + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x40, + .data.std_data.max = 0x400, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_hcg_real_gain), + .regs = ox03a10_hcg_real_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "HCG digital gain", + .ctrl_id = V4L2_CID_DIGITAL_GAIN, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x400, + .data.std_data.max = 0x3FFF, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_hcg_digital_gain), + .regs = ox03a10_hcg_digital_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "LCG analog gain", + .ctrl_id = CRL_CID_ANALOG_GAIN_S, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x40, + .data.std_data.max = 0x400, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_lcg_real_gain), + .regs = ox03a10_lcg_real_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "LCG digital gain", + .ctrl_id = CRL_CID_DIGITAL_GAIN_S, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x400, + .data.std_data.max = 0x3FFF, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_lcg_digital_gain), + .regs = ox03a10_lcg_digital_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "LCG analog gain", + .ctrl_id = CRL_CID_ANALOG_GAIN_L, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x40, + .data.std_data.max = 0x400, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_lcg_real_gain), + .regs = ox03a10_lcg_real_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "LCG digital gain", + .ctrl_id = CRL_CID_DIGITAL_GAIN_L, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x400, + .data.std_data.max = 0x3FFF, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_lcg_digital_gain), + .regs = ox03a10_lcg_digital_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "VS analog gain", + .ctrl_id = CRL_CID_ANALOG_GAIN_VS, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x40, + .data.std_data.max = 0x400, + .data.std_data.step = 1, + .data.std_data.def = 0x40, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_vs_real_gain), + .regs = ox03a10_vs_real_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "VS digital gain", + .ctrl_id = CRL_CID_DIGITAL_GAIN_VS, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0x400, + .data.std_data.max = 0x3FFF, + .data.std_data.step = 1, + .data.std_data.def = 0x400, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = ARRAY_SIZE(ox03a10_vs_digital_gain), + .regs = ox03a10_vs_digital_gain, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .name = "CRL_CID_EXPOSURE_MODE", + .ctrl_id = CRL_CID_EXPOSURE_MODE, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_MODE_SELECTION, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1920, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 2700, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_llp_regs), + .regs = ar0231at_llp_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame Length Lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 1088, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 1480, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ar0231at_fll_regs), + .regs = ar0231at_fll_regs, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = ARRAY_SIZE(ox03a10_h_flip_regs), + .regs = ox03a10_h_flip_regs, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_csi_data_fmt ox03a10_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + .regs_items = 0, + .regs = 0, + }, +}; + +struct crl_pll_configuration ox03a10_pll_configurations[] = { + { + .input_clk = 27000000, + .op_sys_clk = 108000000, + .bitsperpixel = 12, + .pixel_rate_csi = 108000000, + .pixel_rate_pa = 108000000, /* pixel_rate = op_sys_clk*2 *csi_lanes/bitsperpixel */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = 0, + }, +}; + +static struct crl_register_write_rep ox03a10_streamoff_regs[] = { + { 0x0100, CRL_REG_LEN_08BIT, 0x00 } +}; + +static struct crl_arithmetic_ops ox03a10_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops ox03a10_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc ox03a10_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ox03a10_frame_desc_width_ops), + .ops = ox03a10_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ox03a10_frame_desc_height_ops), + .ops = ox03a10_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(ox03a10_frame_desc_width_ops), + .ops = ox03a10_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(ox03a10_frame_desc_height_ops), + .ops = ox03a10_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 1, + .csi2_data_type.entity_val = 0x12, + }, +}; + +struct crl_sensor_configuration ox03a10_crl_configuration = { + .pll_config_items = ARRAY_SIZE(ox03a10_pll_configurations), + .pll_configs = ox03a10_pll_configurations, + + .id_reg_items = ARRAY_SIZE(ox03a10_sensor_detect_regset), + .id_regs = ox03a10_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(ox03a10_sensor_subdevs), + .subdevs = ox03a10_sensor_subdevs, + + .sensor_limits = &ox03a10_sensor_limits, + + .modes_items = ARRAY_SIZE(ox03a10_modes), + .modes = ox03a10_modes, + + .v4l2_ctrls_items = ARRAY_SIZE(ox03a10_v4l2_ctrls), + .v4l2_ctrl_bank = ox03a10_v4l2_ctrls, + + .csi_fmts_items = ARRAY_SIZE(ox03a10_crl_csi_data_fmt), + .csi_fmts = ox03a10_crl_csi_data_fmt, + + .flip_items = ARRAY_SIZE(ox03a10_flip_configurations), + .flip_data = ox03a10_flip_configurations, + + .streamoff_regs_items = ARRAY_SIZE(ox03a10_streamoff_regs), + .streamoff_regs = ox03a10_streamoff_regs, + + .frame_desc_entries = ARRAY_SIZE(ox03a10_frame_desc), + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, + .frame_desc = ox03a10_frame_desc, +}; + +#endif /* __CRLMODULE_OX03A10_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crl_pixter_stub_configuration.h b/drivers/media/i2c/crlmodule/crl_pixter_stub_configuration.h new file mode 100644 index 000000000000..09f03193ca22 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crl_pixter_stub_configuration.h @@ -0,0 +1,1386 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Wang, Zaikuo + * + */ + +#ifndef __CRLMODULE_PIXTER_STUB_CONFIGURATION_H_ +#define __CRLMODULE_PIXTER_STUB_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_pll_configuration pixter_stub_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 8, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 10, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 12, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 20, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 24, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +/* resolutions for linux pss with yuv/rgb pass-through */ +static struct crl_subdev_rect_rep pixter_stub_vga_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 640, 480 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1280, 720 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1920, 1080 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_4p5_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 2816, 1600 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_4k_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 3840, 2160 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 720, 240 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 720, 288 }, + }, +}; + +static struct crl_subdev_rect_rep pixter_stub_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1920, 540 }, + }, +}; + +/* vga for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_vga_pad1_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 672, 512 }, + }, +}; + +/* vga for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_vga_pad2_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 768, 576 }, + }, +}; + + +/* 720p for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_720p_pad1_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1312, 768 }, + }, +}; + +/* 720p for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_720p_pad2_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1312, 984 }, + }, +}; + + +/* 1080p for linux pss with imx135 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_1080p_pad1_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 1952, 1120 }, + }, +}; + +/* 1080p for linux pss with imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_1080p_pad2_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 2048, 1128 }, + }, +}; + + +/* 1080p for linux pss with imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_1080p_pad3_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 2048, 1536 }, + }, +}; + + +/* 2816x1600 for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_4p5_pad1_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 2848, 1632 }, + }, +}; +/* 2816x1600 for linux pss with imx135/imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_4p5_pad2_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 2848, 2136 }, + }, +}; + + +/* 4k for linux pss with imx135 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_4k_pad1_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 3872, 2208 }, + }, +}; + +/* 4k for linux pss with imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_4k_pad2_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4064, 2288 }, + }, +}; + +/* 4k for linux pss with imx477 input simulation */ +static struct crl_subdev_rect_rep pixter_stub_4k_pad3_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4032, 2288 }, + }, +}; + +/* 4096x3072 for linux pss with imx135/imx477 full input simulation */ +static struct crl_subdev_rect_rep pixter_stub_full_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .in_rect = { 0, 0, 4096, 3072 }, + .out_rect = { 0, 0, 4096, 3072 }, + }, +}; + + +static struct crl_mode_rep pixter_stub_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_vga_rects), + .sd_rects = pixter_stub_vga_rects, + .scale_m = 1, + .width = 640, + .height = 480, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_720p_rects), + .sd_rects = pixter_stub_720p_rects, + .scale_m = 1, + .width = 1280, + .height = 720, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_1080p_rects), + .sd_rects = pixter_stub_1080p_rects, + .scale_m = 1, + .width = 1920, + .height = 1080, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4p5_rects), + .sd_rects = pixter_stub_4p5_rects, + .scale_m = 1, + .width = 2816, + .height = 1600, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4k_rects), + .sd_rects = pixter_stub_4k_rects, + .scale_m = 1, + .width = 3840, + .height = 2160, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_vga_pad1_rects), + .sd_rects = pixter_stub_vga_pad1_rects, + .scale_m = 1, + .width = 672, + .height = 512, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_vga_pad2_rects), + .sd_rects = pixter_stub_vga_pad2_rects, + .scale_m = 1, + .width = 768, + .height = 576, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_720p_pad1_rects), + .sd_rects = pixter_stub_720p_pad1_rects, + .scale_m = 1, + .width = 1312, + .height = 768, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_720p_pad2_rects), + .sd_rects = pixter_stub_720p_pad2_rects, + .scale_m = 1, + .width = 1312, + .height = 984, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_1080p_pad1_rects), + .sd_rects = pixter_stub_1080p_pad1_rects, + .scale_m = 1, + .width = 1952, + .height = 1120, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_1080p_pad2_rects), + .sd_rects = pixter_stub_1080p_pad2_rects, + .scale_m = 1, + .width = 2048, + .height = 1128, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_1080p_pad3_rects), + .sd_rects = pixter_stub_1080p_pad3_rects, + .scale_m = 1, + .width = 2048, + .height = 1536, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4p5_pad1_rects), + .sd_rects = pixter_stub_4p5_pad1_rects, + .scale_m = 1, + .width = 2848, + .height = 1632, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4p5_pad2_rects), + .sd_rects = pixter_stub_4p5_pad2_rects, + .scale_m = 1, + .width = 2848, + .height = 2136, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4k_pad1_rects), + .sd_rects = pixter_stub_4k_pad1_rects, + .scale_m = 1, + .width = 3872, + .height = 2208, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4k_pad2_rects), + .sd_rects = pixter_stub_4k_pad2_rects, + .scale_m = 1, + .width = 4064, + .height = 2288, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_4k_pad3_rects), + .sd_rects = pixter_stub_4k_pad3_rects, + .scale_m = 1, + .width = 4032, + .height = 2288, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_full_rects), + .sd_rects = pixter_stub_full_rects, + .scale_m = 1, + .width = 4096, + .height = 3072, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_480i_rects), + .sd_rects = pixter_stub_480i_rects, + .scale_m = 1, + .width = 720, + .height = 240, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_576i_rects), + .sd_rects = pixter_stub_576i_rects, + .scale_m = 1, + .width = 720, + .height = 288, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(pixter_stub_1080i_rects), + .sd_rects = pixter_stub_1080i_rects, + .scale_m = 1, + .width = 1920, + .height = 540, + .min_llp = 6024, + .min_fll = 4096, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = NULL, + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stub scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stub pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_b_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubB scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubB pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_c_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubC scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubC pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_d_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubD scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubD pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_e_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubE scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubE pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_f_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubF scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubF pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_g_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubG scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubG pixel array", + }, +}; + +static struct crl_sensor_subdev_config pixter_stub_h_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_SCALER, + .name = "pixter_stubH scaler", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "pixter_stubH pixel array", + }, +}; + +static struct crl_sensor_limits pixter_stub_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 4096, + .y_addr_max = 3072, + .min_frame_length_lines = 160, + .max_frame_length_lines = 8192, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 8192, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +/* no flip for pixter stub as no real sensor HW */ +static struct crl_flip_data pixter_stub_flip_configurations[] = { + { + .flip = CRL_FLIP_DEFAULT_NONE, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_HFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, + { + .flip = CRL_FLIP_HFLIP_VFLIP, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + }, +}; + +static struct crl_csi_data_fmt pixter_stub_crl_csi_data_fmt[] = { + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 8, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, + { + .code = MEDIA_BUS_FMT_RGB565_1X16, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 24, + }, + { + .code = MEDIA_BUS_FMT_YUYV10_1X20, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 20, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 12, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_order = CRL_PIXEL_ORDER_RGGB, + .bits_per_pixel = 12, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_order = CRL_PIXEL_ORDER_BGGR, + .bits_per_pixel = 12, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_order = CRL_PIXEL_ORDER_GBRG, + .bits_per_pixel = 12, + }, +}; + +static struct crl_v4l2_ctrl pixter_stub_v4l2_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = CRL_V4L2_CTRL_TYPE_MENU_INT, + .data.v4l2_int_menu.def = 0, + .data.v4l2_int_menu.max = 0, + .data.v4l2_int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_PA", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 800000000, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_SCALER, + .op_type = CRL_V4L2_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_PIXEL_RATE, + .name = "V4L2_CID_PIXEL_RATE_CSI", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 800000000, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_ANALOGUE_GAIN, + .name = "V4L2_CID_ANALOGUE_GAIN", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4096, + .data.std_data.step = 1, + .data.std_data.def = 128, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_EXPOSURE, + .name = "V4L2_CID_EXPOSURE", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS1, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS1", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0x5500, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS2, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS2", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0x500, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_EXPOSURE_SHS3, + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .name = "CRL_CID_EXPOSURE_SHS3", + .data.std_data.min = 4, + .data.std_data.max = 65500, + .data.std_data.step = 1, + .data.std_data.def = 0x1000, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_HFLIP, + .name = "V4L2_CID_HFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_VFLIP, + .name = "V4L2_CID_VFLIP", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 1, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_FRAME_LENGTH_LINES, + .name = "Frame length lines", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 160, + .data.std_data.max = 65535, + .data.std_data.step = 1, + .data.std_data.def = 4130, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_LINE_LENGTH_PIXELS, + .name = "Line Length Pixels", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 6024, + .data.std_data.max = 65520, + .data.std_data.step = 1, + .data.std_data.def = 6024, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = V4L2_CID_GAIN, + .name = "Digital Gain", + .type = CRL_V4L2_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 4095, + .data.std_data.step = 1, + .data.std_data.def = 1024, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_L, + .name = "CRL_CID_ANALOG_GAIN_L", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_S, + .name = "CRL_CID_ANALOG_GAIN_S", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_ANALOG_GAIN_VS, + .name = "CRL_CID_ANALOG_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_L, + .name = "CRL_CID_DIGITAL_GAIN_L", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_S, + .name = "CRL_CID_DIGITAL_GAIN_S", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_V4L2_CTRL_SET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = CRL_CID_DIGITAL_GAIN_VS, + .name = "CRL_CID_DIGITAL_GAIN_VS", + .type = CRL_V4L2_CTRL_TYPE_CUSTOM, + .data.std_data.min = 0, + .data.std_data.max = 0x978, + .data.std_data.step = 1, + .data.std_data.def = 64, + .flags = V4L2_CTRL_FLAG_UPDATE, + .impact = CRL_IMPACTS_NO_IMPACT, + .ctrl = 0, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + }, +}; + +static struct crl_arithmetic_ops pixter_stub_frame_desc_width_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .operand.entity_val = CRL_VAR_REF_OUTPUT_WIDTH, + }, +}; + +static struct crl_arithmetic_ops pixter_stub_frame_desc_height_ops[] = { + { + .op = CRL_ASSIGNMENT, + .operand.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, + .operand.entity_val = 1, + }, +}; + +static struct crl_frame_desc pixter_stub_frame_desc[] = { + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_width_ops), + .ops = pixter_stub_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_height_ops), + .ops = pixter_stub_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 0, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_width_ops), + .ops = pixter_stub_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_height_ops), + .ops = pixter_stub_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 1, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_width_ops), + .ops = pixter_stub_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_height_ops), + .ops = pixter_stub_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 2, + .csi2_data_type.entity_val = 0x12, + }, + { + .flags.entity_val = 0, + .bpp.entity_type = CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + .bpp.entity_val = CRL_VAR_REF_BITSPERPIXEL, + .pixelcode.entity_val = MEDIA_BUS_FMT_FIXED, + .length.entity_val = 0, + .start_line.entity_val = 0, + .start_pixel.entity_val = 0, + .width = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_width_ops), + .ops = pixter_stub_frame_desc_width_ops, + }, + .height = { + .ops_items = ARRAY_SIZE(pixter_stub_frame_desc_height_ops), + .ops = pixter_stub_frame_desc_height_ops, + }, + .csi2_channel.entity_val = 3, + .csi2_data_type.entity_val = 0x12, + }, +}; + +#define DEFINE_PIXTER_CRL_CONFIGURATION(port) \ +static struct crl_sensor_configuration pixter_##port##_crl_configuration = { \ + .powerup_regs_items = 0, \ + .powerup_regs = NULL, \ +\ + .poweroff_regs_items = 0, \ + .poweroff_regs = NULL, \ +\ + .id_reg_items = 0, \ + .id_regs = NULL, \ +\ + .subdev_items = ARRAY_SIZE(pixter_##port##_sensor_subdevs), \ + .subdevs = pixter_##port##_sensor_subdevs, \ +\ + .sensor_limits = &pixter_stub_sensor_limits, \ +\ + .pll_config_items = ARRAY_SIZE(pixter_stub_pll_configurations), \ + .pll_configs = pixter_stub_pll_configurations, \ +\ + .modes_items = ARRAY_SIZE(pixter_stub_modes), \ + .modes = pixter_stub_modes, \ +\ + .streamon_regs_items = 0, \ + .streamon_regs = NULL, \ +\ + .streamoff_regs_items = 0, \ + .streamoff_regs = NULL, \ +\ + .v4l2_ctrls_items = ARRAY_SIZE(pixter_stub_v4l2_ctrls), \ + .v4l2_ctrl_bank = pixter_stub_v4l2_ctrls, \ +\ + .flip_items = ARRAY_SIZE(pixter_stub_flip_configurations), \ + .flip_data = pixter_stub_flip_configurations, \ +\ + .frame_desc_entries = ARRAY_SIZE(pixter_stub_frame_desc), \ + .frame_desc_type = CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, \ + .frame_desc = pixter_stub_frame_desc, \ +\ + .csi_fmts_items = ARRAY_SIZE(pixter_stub_crl_csi_data_fmt), \ + .csi_fmts = pixter_stub_crl_csi_data_fmt, \ +} +DEFINE_PIXTER_CRL_CONFIGURATION(stub); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_b); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_c); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_d); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_e); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_f); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_g); +DEFINE_PIXTER_CRL_CONFIGURATION(stub_h); + + +#endif /* __CRLMODULE_PIXTER_STUB_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule/crlmodule-core.c b/drivers/media/i2c/crlmodule/crlmodule-core.c new file mode 100644 index 000000000000..7d63252c607c --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-core.c @@ -0,0 +1,3519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" +#include "crlmodule-msrlist.h" + +static void crlmodule_update_current_mode(struct crl_sensor *sensor); + +static int __crlmodule_get_variable_ref(struct crl_sensor *sensor, + enum crl_member_data_reference_ids ref, + u32 *val) +{ + switch (ref) { + case CRL_VAR_REF_OUTPUT_WIDTH: + *val = sensor->src->crop[CRL_PAD_SRC].width; + break; + case CRL_VAR_REF_OUTPUT_HEIGHT: + *val = sensor->src->crop[CRL_PAD_SRC].height; + break; + case CRL_VAR_REF_BITSPERPIXEL: + *val = sensor->sensor_ds->csi_fmts[ + sensor->fmt_index].bits_per_pixel; + break; + default: + return -EINVAL; + }; + + return 0; +} + +/* + * Get the data format index from the configuration definition data + */ +static int __crlmodule_get_data_fmt_index(struct crl_sensor *sensor, + u32 code) +{ + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->csi_fmts_items; i++) { + if (sensor->sensor_ds->csi_fmts[i].code == code) + return i; + } + + return -EINVAL; +} + +/* + * Find the index of the v4l2 ctrl pointer from the array of v4l2 ctrls + * maintained by the CRL module based on the ctrl id. + */ +static int __crlmodule_get_crl_ctrl_index(struct crl_sensor *sensor, + u32 id, unsigned int *index) +{ + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) + if (sensor->v4l2_ctrl_bank[i].ctrl_id == id) + break; + + if (i >= sensor->sensor_ds->v4l2_ctrls_items) + return -EINVAL; + + *index = i; + return 0; +} + +/* + * Finds the value of a specific v4l2 ctrl based on the ctrl-id + */ +static int __crlmodule_get_ctrl_value(struct crl_sensor *sensor, + u32 id, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_ctrl *ctrl; + unsigned int i; + int ret; + + ret = __crlmodule_get_crl_ctrl_index(sensor, id, &i); + if (ret) + return ret; + + /* If no corresponding v4l2 ctrl created, return */ + if (!sensor->v4l2_ctrl_bank[i].ctrl) { + dev_dbg(&client->dev, + "%s ctrl_id: 0x%x desc: %s not ready\n", __func__, id, + sensor->v4l2_ctrl_bank[i].name); + return -ENODATA; + } + + ctrl = sensor->v4l2_ctrl_bank[i].ctrl; + switch (sensor->v4l2_ctrl_bank[i].type) { + case CRL_V4L2_CTRL_TYPE_MENU_INT: + *val = ctrl->qmenu_int[ctrl->val]; + break; + case CRL_V4L2_CTRL_TYPE_INTEGER: + default: + *val = ctrl->val; + } + + dev_dbg(&client->dev, "%s ctrl_id: 0x%x desc: %s val: %d\n", + __func__, id, + sensor->v4l2_ctrl_bank[i].name, *val); + return 0; +} + +/* + * Finds the v4l2 ctrl based on the control id + */ +static struct v4l2_ctrl *__crlmodule_get_v4l2_ctrl(struct crl_sensor *sensor, + u32 id) +{ + unsigned int i; + + if (__crlmodule_get_crl_ctrl_index(sensor, id, &i)) + return NULL; + + return sensor->v4l2_ctrl_bank[i].ctrl; +} + +/* + * Grab / Release controls based on the ctrl update context + */ +static void __crlmodule_grab_v4l2_ctrl(struct crl_sensor *sensor, + enum crl_v4l2ctrl_update_context ctxt, + bool action) +{ + struct crl_v4l2_ctrl *crl_ctrl; + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) { + crl_ctrl = &sensor->v4l2_ctrl_bank[i]; + + if (crl_ctrl->context == ctxt) + v4l2_ctrl_grab(crl_ctrl->ctrl, action); + } +} + +/* + * Checks if the v4l2 ctrl sepecific data is satisfied in the mode and PLL + * selection logic. + */ +static bool __crlmodule_compare_ctrl_specific_data( + struct crl_sensor *sensor, + unsigned int items, + struct crl_ctrl_data_pair *ctrl_val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int ret; + + /* Go through all the controls associated with this config */ + for (i = 0; i < items; i++) { + /* Get the value set for the control */ + ret = __crlmodule_get_ctrl_value(sensor, ctrl_val[i].ctrl_id, + &val); + if (ret) { + dev_err(&client->dev, "%s ctrl_id: 0x%x not found\n", + __func__, ctrl_val[i].ctrl_id); + return false; + } + + /* Compare the value from the sensor definition file config */ + if (val != ctrl_val[i].data) { + dev_dbg(&client->dev, + "%s ctrl_id: 0x%x value not match %d != %d\n", + __func__, ctrl_val[i].ctrl_id, val, + ctrl_val[i].data); + return false; + } + } + + dev_dbg(&client->dev, "%s success\n", __func__); + return true; +} + +/* + * Finds the correct PLL settings index based on the parameters + */ +static int __crlmodule_update_pll_index(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_pll_configuration *pll_config; + const struct crl_csi_data_fmt *fmts = + &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + u32 link_freq; + unsigned int i; + + link_freq = sensor->link_freq->qmenu_int[sensor->link_freq->val]; + + dev_dbg(&client->dev, "%s PLL Items: %d link_freq: %d\n", __func__, + sensor->sensor_ds->pll_config_items, link_freq); + + for (i = 0; i < sensor->sensor_ds->pll_config_items; i++) { + pll_config = &sensor->sensor_ds->pll_configs[i]; + + if (pll_config->op_sys_clk != link_freq) + continue; + + if (pll_config->input_clk != sensor->platform_data->ext_clk) + continue; + + /* if pll_config->csi_lanes == 0, lanes do not matter */ + if (pll_config->csi_lanes) + if (sensor->platform_data->lanes != + pll_config->csi_lanes) + continue; + + /* PLL config must match to bpps*/ + if (fmts->bits_per_pixel != pll_config->bitsperpixel) + continue; + + /* Check if there are any dynamic compare items */ + if (sensor->ext_ctrl_impacts_pll_selection && + !__crlmodule_compare_ctrl_specific_data(sensor, + pll_config->comp_items, + pll_config->ctrl_data)) + continue; + + /* Found PLL index */ + dev_dbg(&client->dev, "%s Found PLL index: %d for freq: %d\n", + __func__, i, link_freq); + + sensor->pll_index = i; + + /* Update the control values for pixelrate_pa and csi */ + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_pa, + pll_config->pixel_rate_pa); + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, + pll_config->pixel_rate_csi); + return 0; + } + + dev_err(&client->dev, "%s no configuration found for freq: %d\n", + __func__, link_freq); + return -EINVAL; +} + +/* + * Perform the action for the dependency control + */ +static void __crlmodule_dep_ctrl_perform_action( + struct crl_sensor *sensor, + struct crl_dep_ctrl_provision *prov, + u32 *val, u32 *dep_val) +{ + enum crl_dep_ctrl_condition cond; + unsigned int i; + u32 temp; + + if (*val > *dep_val) + cond = CRL_DEP_CTRL_CONDITION_GREATER; + else if (*val < *dep_val) + cond = CRL_DEP_CTRL_CONDITION_LESSER; + else + cond = CRL_DEP_CTRL_CONDITION_EQUAL; + + for (i = 0; i < prov->action_items; i++) { + if (prov->action[i].cond == cond) + break; + } + + /* No handler found-. Return completed */ + if (i >= prov->action_items) + return; + + /* if this is dependency control, switch val and dep val */ + if (prov->action_type == CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + temp = *val; + *val = *dep_val; + *dep_val = temp; + } + + switch (prov->action[i].action) { + case CRL_DEP_CTRL_CONDITION_ADD: + *val = *dep_val + prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_SUBTRACT: + *val = *dep_val - prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_MULTIPLY: + *val = *dep_val * prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_DIVIDE: + *val = *dep_val / prov->action[i].action_value; + break; + } + + /* if this is dependency control, switch val and dep val back*/ + if (prov->action_type == CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + temp = *val; + *val = *dep_val; + *dep_val = temp; + } + + return; +} + +/* + * Parse the dynamic entity based on the Operand type + */ +static int __crlmodule_parse_dynamic_entity(struct crl_sensor *sensor, + struct crl_dynamic_entity entity, + u32 *val) +{ + switch (entity.entity_type) { + case CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST: + *val = entity.entity_val; + return 0; + case CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF: + return __crlmodule_get_variable_ref(sensor, + entity.entity_val, val); + case CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL: + return __crlmodule_get_ctrl_value(sensor, + entity.entity_val, val); + case CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL: { + struct crl_register_read_rep reg; + + /* Note: Only 8bit registers are supported. */ + reg.address = entity.entity_val; + reg.len = CRL_REG_LEN_08BIT; + reg.mask = 0xff; + reg.dev_i2c_addr = CRL_I2C_ADDRESS_NO_OVERRIDE; + return crlmodule_read_reg(sensor, reg, val); + } + default: + break; + }; + + return -EINVAL; +} + +static int __crlmodule_calc_dynamic_entity_values( + struct crl_sensor *sensor, + unsigned int ops_items, + struct crl_arithmetic_ops *ops_arr, + unsigned int *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + + /* perform the bitwise operation on val one by one */ + for (i = 0; i < ops_items; i++) { + struct crl_arithmetic_ops *ops = &ops_arr[i]; + u32 operand; + int ret = __crlmodule_parse_dynamic_entity(sensor, ops->operand, + &operand); + if (ret) { + dev_dbg(&client->dev, + "%s failed to parse dynamic entity: %d %d\n", + __func__, ops->operand.entity_type, + ops->operand.entity_val); + return ret; + } + + switch (ops->op) { + case CRL_BITWISE_AND: + *val &= operand; + break; + case CRL_BITWISE_OR: + *val |= operand; + break; + case CRL_BITWISE_LSHIFT: + *val <<= operand; + break; + case CRL_BITWISE_RSHIFT: + *val >>= operand; + break; + case CRL_BITWISE_XOR: + *val ^= operand; + break; + case CRL_BITWISE_COMPLEMENT: + *val = ~(*val); + break; + case CRL_ADD: + *val += operand; + break; + case CRL_SUBTRACT: + *val = *val > operand ? *val - operand : operand - *val; + break; + case CRL_MULTIPLY: + *val *= operand; + break; + case CRL_DIV: + *val /= operand; + break; + case CRL_ASSIGNMENT: + *val = operand; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +/* + * Dynamic registers' value is not direct but depends on a referrence value. + * This kind of registers are mainly used in crlmodule's v4l2 ctrl logic. + * + * This is to handle cases like the below examples, where mutliple registers + * need to be modified based on the input value "val" + * R3000 = val & 0xff and R3001 = val >> 8 & 0xff and R3002 = val >> 16 & 0xff + * R4001 = val and R4002 = val or + * R2800 = FLL - val and R2802 = LLP - val + */ +static int __crlmodule_parse_and_write_dynamic_reg(struct crl_sensor *sensor, + struct crl_dynamic_register_access *reg, + unsigned int val) +{ + int ret; + + /* + * Get the value associated with the dynamic entity. "val" might + * change after this call based on the arithmetic operations added for + * this group + */ + ret = __crlmodule_calc_dynamic_entity_values(sensor, reg->ops_items, + reg->ops, &val); + if (ret) + return ret; + + /* Now ready to write the value */ + return crlmodule_write_reg(sensor, reg->dev_i2c_addr, reg->address, + reg->len, reg->mask, val); +} + +static int __crlmodule_update_dynamic_regs(struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + unsigned int val) +{ + unsigned int i; + int ret; + + for (i = 0; i < crl_ctrl->regs_items; i++) { + /* + * Each register group must start from the initial value, not + * as a continuation of the previous calculations. The sensor + * configurations must take care of this restriction. + */ + ret = __crlmodule_parse_and_write_dynamic_reg(sensor, + &crl_ctrl->regs[i], val); + if (ret) + return ret; + } + + return 0; +} + +/* + * Perform the action for the dependent register lists + */ +static int __crlmodule_handle_dependency_regs( + struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + unsigned int val) +{ + unsigned int i; + int ret; + + for (i = 0; i < crl_ctrl->crl_ctrl_dep_reg_list; i++) { + struct crl_dep_reg_list *list = &crl_ctrl->dep_regs[i]; + enum crl_dep_ctrl_condition condition; + unsigned int j; + u32 dep_val; + + /* Parse the condition value */ + ret = __crlmodule_parse_dynamic_entity(sensor, list->cond_value, + &dep_val); + if (ret) + return ret; + + /* Get the kind of condition for this value */ + if (val > dep_val) + condition = CRL_DEP_CTRL_CONDITION_GREATER; + else if (val < dep_val) + condition = CRL_DEP_CTRL_CONDITION_LESSER; + else + condition = CRL_DEP_CTRL_CONDITION_EQUAL; + + /* + * Compare the register list specific condition and if matching + * write the corresponding register lists to the sensor. + */ + if (condition == list->reg_cond) { + /* Handle the direct registers if any */ + if (list->no_direct_regs && list->direct_regs) { + ret = crlmodule_write_regs(sensor, + list->direct_regs, list->no_direct_regs); + if (ret) + return ret; + } + + /* Handle the dynamic registers if any */ + for (j = 0; j < list->no_dyn_items; j++) { + ret = __crlmodule_parse_and_write_dynamic_reg( + sensor, &list->dyn_regs[j], val); + if (ret) + return ret; + } + break; + } + } + + return 0; +} + +/* + * Handles the dependency control actions. Dependency control is a control + * which' value depends on the current control. This information is encoded in + * the sensor configuration file. + */ +static int __crlmodule_handle_dependency_ctrl( + struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + unsigned int *val, + enum crl_dep_ctrl_action_type type) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_v4l2_ctrl *dep_crl_ctrl; + struct crl_dep_ctrl_provision *dep_prov; + unsigned int i, idx; + u32 dep_val; + int ret; + + dev_dbg(&client->dev, "%s ctrl_id: 0x%x dependency controls: %d\n", + __func__, crl_ctrl->ctrl_id, + crl_ctrl->dep_items); + + for (i = 0; i < crl_ctrl->dep_items; i++) { + dep_prov = &crl_ctrl->dep_ctrls[i]; + + /* If not the type, continue */ + if (dep_prov->action_type != type) + continue; + + /* Get the value from the dependency ctrl */ + ret = __crlmodule_get_ctrl_value(sensor, dep_prov->ctrl_id, + &dep_val); + if (ret) { + dev_err(&client->dev, "%s ctrl_id: 0x%x not found\n", + __func__, dep_prov->ctrl_id); + /* TODO! Shoud continue? */ + continue; + } + + /* Perform the action */ + __crlmodule_dep_ctrl_perform_action(sensor, dep_prov, val, + &dep_val); + + /* if this is dependency control, update the register */ + if (dep_prov->action_type == + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + ret = __crlmodule_get_crl_ctrl_index(sensor, + dep_prov->ctrl_id, &idx); + if (ret) + continue; + + dep_crl_ctrl = &sensor->v4l2_ctrl_bank[idx]; + + /* Update the dynamic registers for the dep control */ + ret = __crlmodule_update_dynamic_regs(sensor, + dep_crl_ctrl, dep_val); + if (ret) + dev_info(&client->dev, + "%s dynamic reg update failed for %s\n", + __func__, dep_crl_ctrl->name); + + /* Handle dependened register lists for dep control */ + ret = __crlmodule_handle_dependency_regs(sensor, + dep_crl_ctrl, dep_val); + if (ret) + dev_info(&client->dev, + "%s handle dep regs failed for %s\n", + __func__, dep_crl_ctrl->name); + } + } + + return 0; +} + +static int crlmodule_get_fmt_index(struct crl_sensor *sensor, + u8 pixel_order, u8 bpp) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_csi_data_fmt *f; + int i; + + /* + * Go through the fmt list and check if this format with matching bpp + * is supported by this module definition file + */ + for (i = 0; i < sensor->sensor_ds->csi_fmts_items; i++) { + f = &sensor->sensor_ds->csi_fmts[i]; + + if (f->pixel_order == pixel_order && f->bits_per_pixel == bpp) + return i; + } + + dev_err(&client->dev, "%s no supported format for order: %d bpp: %d\n", + __func__, pixel_order, bpp); + + return -EINVAL; +} + +static int __crlmodule_update_flip_info(struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + struct v4l2_ctrl *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_csi_data_fmt *fmt = + &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + u8 bpp = fmt->bits_per_pixel; + u8 flip_info = sensor->flip_info; + u8 new_order; + int i, ret; + + dev_dbg(&client->dev, "%s current flip_info: %d curr index: %d\n", + __func__, flip_info, sensor->fmt_index); + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + flip_info &= CRL_FLIP_HFLIP_MASK; + flip_info |= ctrl->val > 0 ? CRL_FLIP_HFLIP : 0; + break; + case V4L2_CID_VFLIP: + flip_info &= CRL_FLIP_VFLIP_MASK; + flip_info |= ctrl->val > 0 ? CRL_FLIP_VFLIP : 0; + break; + } + + dev_dbg(&client->dev, "%s flip success new flip_info: %d\n", + __func__, flip_info); + + /* First check if the module actually supports any pixelorder changes */ + for (i = 0; i < sensor->sensor_ds->flip_items; i++) { + if (flip_info == sensor->sensor_ds->flip_data[i].flip) { + new_order = sensor->sensor_ds->flip_data[i].pixel_order; + break; + } + } + + if (i >= sensor->sensor_ds->flip_items) { + dev_err(&client->dev, "%s flip not supported %d\n", + __func__, flip_info); + return -EINVAL; + } + + /* Skip format re-selection if pixel order is unrelated to flipping. */ + if (new_order == CRL_PIXEL_ORDER_IGNORE) + return 0; + + /* + * Flip changes only pixel order. So check if the supported format list + * has any format with new pixel order and current bits per pixel + */ + i = crlmodule_get_fmt_index(sensor, new_order, bpp); + if (i < 0) { + dev_err(&client->dev, "%s no format found order: %d bpp: %d\n", + __func__, new_order, bpp); + return -EINVAL; + } + + ret = __crlmodule_update_dynamic_regs(sensor, crl_ctrl, ctrl->val); + if (ret) { + dev_err(&client->dev, "%s register access failed\n", __func__); + return ret; + } + + /* New format found. Update info */ + sensor->fmt_index = i; + sensor->flip_info = flip_info; + + dev_dbg(&client->dev, "%s flip success flip: %d new fmt index: %d\n", + __func__, flip_info, i); + + return 0; +} +static int __crlmodule_update_framesize(struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + struct v4l2_ctrl *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_mode_rep *mode = sensor->current_mode; + unsigned int val; + int ret; + + switch (ctrl->id) { + case V4L2_CID_FRAME_LENGTH_LINES: + val = max(ctrl->val, mode->min_fll); + break; + case V4L2_CID_LINE_LENGTH_PIXELS: + val = max(ctrl->val, mode->min_llp); + break; + default: + return -EINVAL; + } + + ret = __crlmodule_update_dynamic_regs(sensor, crl_ctrl, val); + if (ret) + return ret; + + ctrl->val = val; + ctrl->cur.val = val; + dev_dbg(&client->dev, "%s: set v4l2 id:0x%0x value %d\n", + __func__, ctrl->id, val); + + return 0; +} +static int __crlmodule_update_blanking(struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl, + struct v4l2_ctrl *ctrl) +{ + unsigned int val; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + val = sensor->pixel_array->crop[CRL_PA_PAD_SRC].width + + ctrl->val; + break; + case V4L2_CID_VBLANK: + val = sensor->pixel_array->crop[CRL_PA_PAD_SRC].height + + ctrl->val; + break; + default: + return -EINVAL; + } + + return __crlmodule_update_dynamic_regs(sensor, crl_ctrl, val); +} + +static void __crlmodule_update_selection_impact_flags( + struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl) +{ + if (crl_ctrl->impact & CRL_IMPACTS_PLL_SELECTION) + sensor->ext_ctrl_impacts_pll_selection = true; + + if (crl_ctrl->impact & CRL_IMPACTS_MODE_SELECTION) + sensor->ext_ctrl_impacts_mode_selection = true; +} + +static struct crl_v4l2_ctrl *__crlmodule_find_crlctrl( + struct crl_sensor *sensor, + struct v4l2_ctrl *ctrl) +{ + struct crl_v4l2_ctrl *crl_ctrl; + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) { + crl_ctrl = &sensor->v4l2_ctrl_bank[i]; + if (crl_ctrl->ctrl == ctrl) + return crl_ctrl; + } + + return NULL; +} + +static int crlmodule_reset_crlctrl_value(struct crl_sensor *sensor, + unsigned int new_mode) +{ + struct crl_v4l2_ctrl *crl_ctrl; + const struct crl_mode_rep *this; + unsigned int i; + + if (!sensor->v4l2_ctrl_bank) + return -EINVAL; + + this = &sensor->sensor_ds->modes[new_mode]; + + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) { + crl_ctrl = &sensor->v4l2_ctrl_bank[i]; + + switch (crl_ctrl->ctrl_id) { + case V4L2_CID_FRAME_LENGTH_LINES: + if (crl_ctrl->ctrl) { + crl_ctrl->ctrl->val = this->min_fll; + crl_ctrl->ctrl->cur.val = this->min_fll; + } + break; + case V4L2_CID_LINE_LENGTH_PIXELS: + if (crl_ctrl->ctrl) { + crl_ctrl->ctrl->val = this->min_llp; + crl_ctrl->ctrl->cur.val = this->min_llp; + } + break; + } + } + + return 0; +} + +static int crlmodule_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct crl_sensor *sensor = container_of(ctrl->handler, + struct crl_subdev, ctrl_handler)->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_v4l2_ctrl *crl_ctrl = NULL; + int ret = 0; + + dev_dbg(&client->dev, "%s id:0x%0x val:%d\n", __func__, ctrl->id, + ctrl->val); + + /* + * Need to find the corresponding crlmodule wrapper for this v4l2_ctrl. + * This is needed because all the register information is associated + * with the crlmodule's wrapper v4l2ctrl. + */ + crl_ctrl = __crlmodule_find_crlctrl(sensor, ctrl); + if (!crl_ctrl) { + dev_err(&client->dev, "%s ctrl :0x%x not supported\n", + __func__, ctrl->id); + return -EINVAL; + } + + dev_dbg(&client->dev, "%s id:0x%x name:%s\n", __func__, ctrl->id, + crl_ctrl->name); + + /* Then go through the mandatory controls */ + switch (ctrl->id) { + case V4L2_CID_LINK_FREQ: + /* Go through the supported list and compare the values */ + ret = __crlmodule_update_pll_index(sensor); + goto out; + }; + + /* update the selection impacts flags */ + __crlmodule_update_selection_impact_flags(sensor, crl_ctrl); + + /* + * Dependency control is a control whose value is affected by the value + * for the current control. For example, vblank can be a dependency + * control for exposure. Whenever exposure changes, the sensor can + * automatically adjust the vblank or rely on manual adjustment. In + * case of manual adjustment the sensor configuration file needs to + * specify the dependency control, the condition for an action and + * typs of action. + * + * Now check if there is any dependency controls for this. And if there + * are any we need to split the action to two. First if the current + * control needs to be changed, then do it before updating the register. + * If some other control is affected, then do it after wrriting the + * current values + * + * Now check in the dependency control list, if the action type is + * "self" and update the value accordingly now + */ + __crlmodule_handle_dependency_ctrl(sensor, crl_ctrl, &ctrl->val, + CRL_DEP_CTRL_ACTION_TYPE_SELF); + + /* Handle specific controls */ + switch (ctrl->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = __crlmodule_update_flip_info(sensor, crl_ctrl, ctrl); + goto out; + + case V4L2_CID_VBLANK: + case V4L2_CID_HBLANK: + if (sensor->blanking_ctrl_not_use) { + dev_info(&client->dev, "%s Blanking controls are \ + not used in this configuration, setting them has \ + no effect\n", __func__); + /* Disable control*/ + v4l2_ctrl_activate(ctrl, false); + + } else { + ret = __crlmodule_update_blanking(sensor, + crl_ctrl, ctrl); + } + goto out; + + case V4L2_CID_FRAME_LENGTH_LINES: + case V4L2_CID_LINE_LENGTH_PIXELS: + ret = __crlmodule_update_framesize(sensor, crl_ctrl, ctrl); + goto out; + + case CRL_CID_SENSOR_MODE: + /* + * If sensor mode is changed, some v4l2 ctrls value need + * to be reset to default value, or else the value set in + * previous mode will influence the setting in the current + * mode. Especially for llp and fll. + */ + if (sensor->sensor_mode != ctrl->val) + crlmodule_reset_crlctrl_value(sensor, ctrl->val); + + sensor->sensor_mode = ctrl->val; + crlmodule_update_current_mode(sensor); + goto out; + } + + ret = __crlmodule_update_dynamic_regs(sensor, crl_ctrl, ctrl->val); + if (ret) + goto out; + + ret = __crlmodule_handle_dependency_regs(sensor, crl_ctrl, + ctrl->val); + +out: + /* + * Now check in the dependency control list, if the action type is + * "dependency control" and update the value accordingly now + */ + if (!ret && crl_ctrl) + __crlmodule_handle_dependency_ctrl(sensor, crl_ctrl, &ctrl->val, + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL); + return ret; +} + +static int crlmodule_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct crl_sensor *sensor = container_of(ctrl->handler, + struct crl_subdev, ctrl_handler)->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_v4l2_ctrl *crl_ctrl; + struct crl_dynamic_register_access *reg; + + /* + * Need to find the corresponding crlmodule wrapper for this v4l2_ctrl. + * This is needed because all the register information is associated + * with the crlmodule's wrapper v4l2ctrl. + */ + crl_ctrl = __crlmodule_find_crlctrl(sensor, ctrl); + if (!crl_ctrl) { + dev_err(&client->dev, "%s ctrl :0x%x not supported\n", + __func__, ctrl->id); + return -EINVAL; + } + + dev_dbg(&client->dev, "%s id:0x%x name:%s\n", __func__, ctrl->id, + crl_ctrl->name); + + /* cannot handle if the V4L2_CTRL_FLAG_READ_ONLY flag is not set */ + if (!(ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) { + dev_err(&client->dev, "%s Control id:0x%x is not read only\n", + __func__, ctrl->id); + return -EINVAL; + } + + /* + * Found the crl control wrapper. Use the dynamic entity information + * to calculate the value for this control. For get control, there + * could be only one item in the crl_dynamic_register_access. ctrl-> + * regs_items must be 1. Also the crl_dynamic_register_access.address + * and crl_dynamic_register_access.len are not used. + * Instead the values to be found or calculated need to be encoded into + * crl_dynamic_register_access.crl_arithmetic_ops. It has possibility + * to read from registers, existing control values and simple arithmetic + * operations etc. + */ + if (crl_ctrl->regs_items > 1) + dev_warn(&client->dev, + "%s multiple dynamic entities, will skip the rest\n", + __func__); + reg = &crl_ctrl->regs[0]; + + /* Get the value associated with the dynamic entity */ + return __crlmodule_calc_dynamic_entity_values(sensor, reg->ops_items, + reg->ops, &ctrl->val); +} + +static const struct v4l2_ctrl_ops crlmodule_ctrl_ops = { + .s_ctrl = crlmodule_set_ctrl, + .g_volatile_ctrl = crlmodule_get_ctrl, +}; + +static struct v4l2_ctrl_handler *__crlmodule_get_sd_ctrl_handler( + struct crl_sensor *sensor, + enum crl_subdev_type sd_type) +{ + switch (sd_type) { + case CRL_SUBDEV_TYPE_SCALER: + case CRL_SUBDEV_TYPE_BINNER: + return &sensor->src->ctrl_handler; + + case CRL_SUBDEV_TYPE_PIXEL_ARRAY: + if (sensor->pixel_array) + return &sensor->pixel_array->ctrl_handler; + break; + }; + + return NULL; +} + +static int __crlmodule_init_link_freq_ctrl_menu( + struct crl_sensor *sensor, + struct crl_v4l2_ctrl *crl_ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int items = 0; + unsigned int i; + + /* Cannot handle if the control type is not integer menu */ + if (crl_ctrl->type != CRL_V4L2_CTRL_TYPE_MENU_INT) + return 0; + + /* If the menu contents exist, skip filling it dynamically */ + if (crl_ctrl->data.v4l2_int_menu.menu) + return 0; + + sensor->link_freq_menu = devm_kzalloc(&client->dev, sizeof(s64) * + sensor->sensor_ds->pll_config_items, + GFP_KERNEL); + if (!sensor->link_freq_menu) + return -ENOMEM; + + for (i = 0; i < sensor->sensor_ds->pll_config_items; i++) { + bool dup = false; + unsigned int j; + + /* + * Skip the duplicate entries. We are using the value to match + * not the index + */ + for (j = 0; j < items && !dup; j++) + dup = (sensor->link_freq_menu[j] == + sensor->sensor_ds->pll_configs[i].op_sys_clk); + if (dup) + continue; + + sensor->link_freq_menu[items] = + sensor->sensor_ds->pll_configs[i].op_sys_clk; + items++; + } + + crl_ctrl->data.v4l2_int_menu.menu = sensor->link_freq_menu; + + /* items will not be 0 as there will be atleast one pll_config_item */ + crl_ctrl->data.v4l2_int_menu.max = items - 1; + + return 0; +} + +static int crlmodule_init_controls(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int pa_ctrls = 0; + unsigned int src_ctrls = 0; + struct crl_v4l2_ctrl *crl_ctrl; + struct v4l2_ctrl_handler *ctrl_handler; + struct v4l2_ctrl_config cfg = { 0 }; + unsigned int i; + int rval; + + sensor->v4l2_ctrl_bank = devm_kzalloc(&client->dev, + sizeof(struct crl_v4l2_ctrl) * + sensor->sensor_ds->v4l2_ctrls_items, + GFP_KERNEL); + if (!sensor->v4l2_ctrl_bank) + return -ENOMEM; + + /* Prepare to initialise the v4l2_ctrls from the crl wrapper */ + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) { + /* + * First copy the v4l2_ctrls to the sensor as there could be + * more than one similar sensors in a product which could share + * the same configuration files + */ + sensor->v4l2_ctrl_bank[i] = + sensor->sensor_ds->v4l2_ctrl_bank[i]; + + crl_ctrl = &sensor->v4l2_ctrl_bank[i]; + if (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_PIXEL_ARRAY) + pa_ctrls++; + + if (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER) + src_ctrls++; + + /* populate the v4l2_ctrl for the Link_freq dynamically */ + if (crl_ctrl->ctrl_id == V4L2_CID_LINK_FREQ && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) { + rval = __crlmodule_init_link_freq_ctrl_menu(sensor, + crl_ctrl); + if (rval) + return rval; + } + } + dev_dbg(&client->dev, "%s pa_ctrls: %d src_ctrls: %d\n", __func__, + pa_ctrls, src_ctrls); + + if (pa_ctrls) { + rval = v4l2_ctrl_handler_init( + &sensor->pixel_array->ctrl_handler, + pa_ctrls); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + } + + if (src_ctrls) { + rval = v4l2_ctrl_handler_init( + &sensor->src->ctrl_handler, + src_ctrls); + if (rval) + return rval; + sensor->src->ctrl_handler.lock = &sensor->mutex; + } + + for (i = 0; i < sensor->sensor_ds->v4l2_ctrls_items; i++) { + crl_ctrl = &sensor->v4l2_ctrl_bank[i]; + ctrl_handler = __crlmodule_get_sd_ctrl_handler(sensor, + crl_ctrl->sd_type); + + if (!ctrl_handler) + continue; + + switch (crl_ctrl->type) { + case CRL_V4L2_CTRL_TYPE_MENU_ITEMS: + crl_ctrl->ctrl = v4l2_ctrl_new_std_menu_items( + ctrl_handler, &crlmodule_ctrl_ops, + crl_ctrl->ctrl_id, + crl_ctrl->data.v4l2_menu_items.size, + 0, 0, + crl_ctrl->data.v4l2_menu_items.menu); + break; + case CRL_V4L2_CTRL_TYPE_MENU_INT: + crl_ctrl->ctrl = v4l2_ctrl_new_int_menu(ctrl_handler, + &crlmodule_ctrl_ops, crl_ctrl->ctrl_id, + crl_ctrl->data.v4l2_int_menu.max, + crl_ctrl->data.v4l2_int_menu.def, + crl_ctrl->data.v4l2_int_menu.menu); + break; + case CRL_V4L2_CTRL_TYPE_INTEGER64: + case CRL_V4L2_CTRL_TYPE_INTEGER: + crl_ctrl->ctrl = v4l2_ctrl_new_std(ctrl_handler, + &crlmodule_ctrl_ops, crl_ctrl->ctrl_id, + crl_ctrl->data.std_data.min, + crl_ctrl->data.std_data.max, + crl_ctrl->data.std_data.step, + crl_ctrl->data.std_data.def); + break; + case CRL_V4L2_CTRL_TYPE_CUSTOM: + cfg.ops = &crlmodule_ctrl_ops; + cfg.id = crl_ctrl->ctrl_id; + cfg.name = crl_ctrl->name; + cfg.type = crl_ctrl->v4l2_type; + if ((crl_ctrl->v4l2_type == V4L2_CTRL_TYPE_INTEGER) || + (crl_ctrl->v4l2_type == + V4L2_CTRL_TYPE_INTEGER64)) { + cfg.max = crl_ctrl->data.std_data.max; + cfg.min = crl_ctrl->data.std_data.min; + cfg.step = crl_ctrl->data.std_data.step; + cfg.def = crl_ctrl->data.std_data.def; + cfg.qmenu = 0; + cfg.elem_size = 0; + } else if (crl_ctrl->v4l2_type == V4L2_CTRL_TYPE_MENU) { + cfg.max = crl_ctrl->data.v4l2_menu_items.size + - 1; + cfg.min = 0; + cfg.step = 0; + cfg.def = 0; + cfg.qmenu = crl_ctrl->data.v4l2_menu_items.menu; + cfg.elem_size = 0; + } else { + dev_dbg(&client->dev, + "%s Custom Control: type %d\n", + __func__, crl_ctrl->v4l2_type); + continue; + } + crl_ctrl->ctrl = v4l2_ctrl_new_custom(ctrl_handler, + &cfg, NULL); + break; + case CRL_V4L2_CTRL_TYPE_BOOLEAN: + case CRL_V4L2_CTRL_TYPE_BUTTON: + case CRL_V4L2_CTRL_TYPE_CTRL_CLASS: + default: + dev_err(&client->dev, + "%s Invalid control type\n", __func__); + continue; + break; + } + + if (!crl_ctrl->ctrl) + continue; + /* + * Blanking and framesize controls access to same register, + * Blank controls are disabled if framesize controls exists. + */ + if (crl_ctrl->ctrl_id == V4L2_CID_FRAME_LENGTH_LINES || + crl_ctrl->ctrl_id == V4L2_CID_LINE_LENGTH_PIXELS) + sensor->blanking_ctrl_not_use = 1; + + if (crl_ctrl->ctrl_id == CRL_CID_SENSOR_MODE) + sensor->direct_mode_in_use = 1; + + /* Save mandatory control references - link_freq in src sd */ + if (crl_ctrl->ctrl_id == V4L2_CID_LINK_FREQ && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) + sensor->link_freq = crl_ctrl->ctrl; + + /* Save mandatory control references - pixel_rate_pa PA sd */ + if (crl_ctrl->ctrl_id == V4L2_CID_PIXEL_RATE && + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_PIXEL_ARRAY) + sensor->pixel_rate_pa = crl_ctrl->ctrl; + + /* Save mandatory control references - pixel_rate_csi src sd */ + if (crl_ctrl->ctrl_id == V4L2_CID_PIXEL_RATE && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) + sensor->pixel_rate_csi = crl_ctrl->ctrl; + + crl_ctrl->ctrl->flags |= crl_ctrl->flags; + + dev_dbg(&client->dev, + "%s idx: %d ctrl_id: 0x%x ctrl_name: %s\n ctrl: 0x%p", + __func__, i, crl_ctrl->ctrl_id, crl_ctrl->name, + crl_ctrl->ctrl); + + if (ctrl_handler->error) { + dev_err(&client->dev, + "%s controls initialization failed (%d)\n", + __func__, ctrl_handler->error); + rval = ctrl_handler->error; + goto error; + } + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static bool __crlmodule_rect_matches(struct i2c_client *client, + const struct v4l2_rect *const rect1, + const struct v4l2_rect *const rect2) +{ + dev_dbg(&client->dev, "%s rect1 l:%d t:%d w:%d h:%d\n", __func__, + rect1->left, rect1->top, rect1->width, rect1->height); + dev_dbg(&client->dev, "%s rect2 l:%d t:%d w:%d h:%d\n", __func__, + rect2->left, rect2->top, rect2->width, rect2->height); + + return (rect1->left == rect2->left && + rect1->top == rect2->top && + rect1->width == rect2->width && + rect1->height == rect2->height); +} + +static unsigned int __crlmodule_get_mode_min_llp(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = + sensor->sensor_ds->sensor_limits; + unsigned int width = sensor->pixel_array->crop[CRL_PA_PAD_SRC].width; + unsigned int min_llp; + + if (mode->min_llp) + min_llp = mode->min_llp; /* mode specific limit */ + else if (limits->min_line_length_pixels) + min_llp = limits->min_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + min_llp = width; + + return min_llp; +} + +static unsigned int __crlmodule_get_mode_max_llp(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = + sensor->sensor_ds->sensor_limits; + unsigned int max_llp; + + if (mode->max_llp) + max_llp = mode->max_llp; /* mode specific limit */ + else if (limits->min_line_length_pixels) + max_llp = limits->max_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + max_llp = USHRT_MAX; + + return max_llp; +} + +static unsigned int __crlmodule_get_mode_min_fll(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = + sensor->sensor_ds->sensor_limits; + unsigned int height = sensor->pixel_array->crop[CRL_PA_PAD_SRC].height; + unsigned int min_fll; + + if (mode->min_fll) + min_fll = mode->min_fll; /* mode specific limit */ + else if (limits->min_frame_length_lines) + min_fll = limits->min_frame_length_lines; /* sensor limit */ + else /* No restrictions */ + min_fll = height; + + return min_fll; +} + +static unsigned int __crlmodule_get_mode_max_fll(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = + sensor->sensor_ds->sensor_limits; + unsigned int max_fll; + + if (mode->max_fll) + max_fll = mode->max_fll; /* mode specific limit */ + else if (limits->min_line_length_pixels) + max_fll = limits->max_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + max_fll = USHRT_MAX; + + return max_fll; +} + +static void crlmodule_update_framesize(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int min_llp, max_llp, min_fll, max_fll; + struct v4l2_ctrl *llength; + struct v4l2_ctrl *flength; + + llength = __crlmodule_get_v4l2_ctrl(sensor, + V4L2_CID_LINE_LENGTH_PIXELS); + flength = __crlmodule_get_v4l2_ctrl(sensor, + V4L2_CID_FRAME_LENGTH_LINES); + + if (llength) { + min_llp = __crlmodule_get_mode_min_llp(sensor); + max_llp = __crlmodule_get_mode_max_llp(sensor); + + llength->minimum = min_llp; + llength->maximum = max_llp; + llength->default_value = llength->minimum; + dev_dbg(&client->dev, "%s llp:%d\n", __func__, llength->val); + } + + if (flength) { + min_fll = __crlmodule_get_mode_min_fll(sensor); + max_fll = __crlmodule_get_mode_max_fll(sensor); + flength->minimum = min_fll; + flength->maximum = max_fll; + flength->default_value = flength->minimum; + dev_dbg(&client->dev, "%s fll:%d\n", __func__, flength->val); + } +} + +static int crlmodule_update_frame_blanking(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int width = sensor->pixel_array->crop[CRL_PA_PAD_SRC].width; + unsigned int height = sensor->pixel_array->crop[CRL_PA_PAD_SRC].height; + unsigned int min_llp, max_llp, min_fll, max_fll; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + vblank = __crlmodule_get_v4l2_ctrl(sensor, V4L2_CID_VBLANK); + hblank = __crlmodule_get_v4l2_ctrl(sensor, V4L2_CID_HBLANK); + + if (hblank) { + min_llp = __crlmodule_get_mode_min_llp(sensor); + max_llp = __crlmodule_get_mode_max_llp(sensor); + + hblank->minimum = min_llp - width; + hblank->maximum = max_llp - width; + hblank->default_value = hblank->minimum; + dev_dbg(&client->dev, "%s hblank:%d\n", __func__, hblank->val); + } + + if (vblank) { + min_fll = __crlmodule_get_mode_min_fll(sensor); + max_fll = __crlmodule_get_mode_max_fll(sensor); + + vblank->minimum = min_fll - height; + vblank->maximum = max_fll - height; + vblank->default_value = vblank->minimum; + dev_dbg(&client->dev, "%s vblank:%d\n", __func__, vblank->val); + } + + return 0; +} + +static int __crlmodule_rect_index(enum crl_subdev_type type, + const struct crl_mode_rep *mode) +{ + int i; + + for (i = 0; i < mode->sd_rects_items; i++) { + if (type == mode->sd_rects[i].subdev_type) + return i; + } + + return -1; +} + +static void crlmodule_update_mode_bysel(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_mode_rep *this; + unsigned int i; + int rect_index; + + dev_dbg(&client->dev, "%s look for w: %d, h: %d, in [%d] modes\n", + __func__, sensor->src->crop[CRL_PAD_SRC].width, + sensor->src->crop[CRL_PAD_SRC].height, + sensor->sensor_ds->modes_items); + + for (i = 0; i < sensor->sensor_ds->modes_items; i++) { + this = &sensor->sensor_ds->modes[i]; + + dev_dbg(&client->dev, "%s check mode list[%d] w: %d, h: %d\n", + __func__, i, this->width, this->height); + if (this->width != sensor->src->crop[CRL_PAD_SRC].width || + this->height != sensor->src->crop[CRL_PAD_SRC].height) + continue; + + if (sensor->pixel_array) { + dev_dbg(&client->dev, "%s Compare PA out rect\n", + __func__); + rect_index = + __crlmodule_rect_index(CRL_SUBDEV_TYPE_PIXEL_ARRAY, + this); + if (rect_index < 0) + continue; + if (!__crlmodule_rect_matches(client, + &sensor->pixel_array->crop[CRL_PA_PAD_SRC], + &this->sd_rects[rect_index].out_rect)) + continue; + } + if (sensor->binner) { + dev_dbg(&client->dev, "%s binning hor: %d vs. %d\n", + __func__, + sensor->binning_horizontal, + this->binn_hor); + if (sensor->binning_horizontal != this->binn_hor) + continue; + + dev_dbg(&client->dev, "%s binning vert: %d vs. %d\n", + __func__, + sensor->binning_vertical, + this->binn_vert); + if (sensor->binning_vertical != this->binn_vert) + continue; + + dev_dbg(&client->dev, "%s binner in rect\n", __func__); + rect_index = + __crlmodule_rect_index(CRL_SUBDEV_TYPE_BINNER, + this); + if (rect_index < 0) + continue; + if (!__crlmodule_rect_matches(client, + &sensor->binner->crop[CRL_PAD_SINK], + &this->sd_rects[rect_index].in_rect)) + continue; + + dev_dbg(&client->dev, "%s binner out rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->binner->crop[CRL_PAD_SRC], + &this->sd_rects[rect_index].out_rect)) + continue; + } + + if (sensor->scaler) { + dev_dbg(&client->dev, "%s scaler scale_m %d vs. %d\n", + __func__, sensor->scale_m, + this->scale_m); + if (sensor->scale_m != this->scale_m) + continue; + + rect_index = + __crlmodule_rect_index(CRL_SUBDEV_TYPE_SCALER, + this); + if (rect_index < 0) + continue; + + dev_dbg(&client->dev, "%s scaler in rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->scaler->crop[CRL_PAD_SINK], + &this->sd_rects[rect_index].in_rect)) + continue; + + dev_dbg(&client->dev, "%s scaler out rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->scaler->crop[CRL_PAD_SRC], + &this->sd_rects[rect_index].out_rect)) + continue; + } + + /* Check if there are any dynamic compare items */ + if (sensor->ext_ctrl_impacts_mode_selection && + !__crlmodule_compare_ctrl_specific_data(sensor, + this->comp_items, + this->ctrl_data)) + continue; + + /* Found a perfect match! */ + dev_dbg(&client->dev, "%s found mode. idx: %d\n", __func__, i); + break; + } + + /* If no modes found, fall back to the fail safe mode index */ + if (i >= sensor->sensor_ds->modes_items) { + i = sensor->sensor_ds->fail_safe_mode_index; + this = &sensor->sensor_ds->modes[i]; + dev_dbg(&client->dev, + "%s no matching mode, set to default: %d\n", + __func__, i); + } + + sensor->current_mode = this; +} + +static void crlmodule_update_mode_v4l2ctrl(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_mode_rep *this; + int i; + + dev_dbg(&client->dev, "%s Sensor Mode :%d\n", + __func__, sensor->sensor_mode); + /* point to selected mode */ + this = &sensor->sensor_ds->modes[sensor->sensor_mode]; + sensor->current_mode = this; + + for (i = 0; i < this->sd_rects_items; i++) { + + if (CRL_SUBDEV_TYPE_PIXEL_ARRAY == + this->sd_rects[i].subdev_type) { + sensor->pixel_array->crop[CRL_PA_PAD_SRC] = + this->sd_rects[i].out_rect; + } + + if (CRL_SUBDEV_TYPE_BINNER == + this->sd_rects[i].subdev_type) { + sensor->binner->sink_fmt = + this->sd_rects[i].in_rect; + sensor->binner->crop[CRL_PAD_SINK] = + this->sd_rects[i].in_rect; + sensor->binner->crop[CRL_PAD_SRC] = + this->sd_rects[i].out_rect; + sensor->binning_vertical = this->binn_vert; + sensor->binning_horizontal = this->binn_hor; + if (this->binn_vert > 1) + sensor->binner->compose = + this->sd_rects[i].out_rect; + } + + if (CRL_SUBDEV_TYPE_SCALER == + this->sd_rects[i].subdev_type) { + sensor->scaler->crop[CRL_PAD_SINK] = + this->sd_rects[i].in_rect; + sensor->scaler->crop[CRL_PAD_SRC] = + this->sd_rects[i].out_rect; + sensor->scaler->sink_fmt = + this->sd_rects[i].in_rect; + sensor->scale_m = this->scale_m; + if (this->scale_m != 1) + sensor->scaler->compose = + this->sd_rects[i].out_rect; + } + } + + /* Set source */ + sensor->src->crop[CRL_PAD_SRC].width = this->width; + sensor->src->crop[CRL_PAD_SRC].height = this->height; +} + +static void crlmodule_update_current_mode(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *this; + int i; + + if (sensor->direct_mode_in_use) + crlmodule_update_mode_v4l2ctrl(sensor); + else + crlmodule_update_mode_bysel(sensor); + + /* + * We have a valid mode now. If there are any mode specific "get" + * controls defined in the configuration it could be queried by the + * user space for any mode specific information. So go through the + * mode specific v4l2_ctrls and update its value from the selected mode. + */ + + this = sensor->current_mode; + + for (i = 0; i < this->comp_items; i++) { + struct crl_ctrl_data_pair *ctrl_comp = &this->ctrl_data[i]; + unsigned int idx; + + /* Get the v4l2_ctrl pointer corresponding ctrl id */ + if (__crlmodule_get_crl_ctrl_index(sensor, ctrl_comp->ctrl_id, + &idx)) + /* If not found, move to the next ctrl */ + continue; + + /* No need to update this control, if this is a set op ctrl */ + if (sensor->v4l2_ctrl_bank[idx].op_type == CRL_V4L2_CTRL_SET_OP) + continue; + + /* Update the control value */ + __v4l2_ctrl_s_ctrl(sensor->v4l2_ctrl_bank[idx].ctrl, + ctrl_comp->data); + } + + if (sensor->blanking_ctrl_not_use) + crlmodule_update_framesize(sensor); + else + crlmodule_update_frame_blanking(sensor); +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int __crlmodule_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct v4l2_rect *r; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, + fmt->pad); + return 0; + } + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.width = r->width; + fmt->format.height = r->height; + fmt->format.code = + sensor->sensor_ds->csi_fmts[sensor->fmt_index].code; + fmt->format.field = (ssd->field == V4L2_FIELD_ANY) ? + V4L2_FIELD_NONE : ssd->field; + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + if (code->index >= sensor->sensor_ds->csi_fmts_items) + return -EINVAL; + + code->code = sensor->sensor_ds->csi_fmts[code->index].code; + + return 0; +} + +static int crlmodule_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(sd); + + if (fse->index >= sensor->sensor_ds->modes_items) + return -EINVAL; + + fse->min_width = sensor->sensor_ds->modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = sensor->sensor_ds->modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __crlmodule_get_format(subdev, cfg, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int __crlmodule_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + if (ssd == sensor->pixel_array + && sel->pad == CRL_PA_PAD_SRC) { + switch (sel->target) { + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + return 0; + } + } + if (ssd == sensor->binner) { + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == CRL_PAD_SINK) + return 0; + } + } + if (ssd == sensor->scaler) { + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (sel->pad == CRL_PAD_SRC) + return 0; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == CRL_PAD_SINK) + return 0; + } + } + return -EINVAL; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static void crlmodule_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + unsigned int i; + + /* Currently we support only 2 pads */ + BUG_ON(subdev->entity.num_pads > CRL_PADS); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(subdev, + cfg, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(subdev, cfg, + CRL_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct v4l2_rect *comp, *crops[CRL_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __crlmodule_sel_supported(subdev, sel); + if (ret) + return ret; + + crlmodule_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad); + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + if (ssd == sensor->pixel_array) { + sel->r.left = sel->r.top = 0; + sel->r.width = + sensor->sensor_ds->sensor_limits->x_addr_max; + sel->r.height = + sensor->sensor_ds->sensor_limits->y_addr_max; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *comp; + break; + } + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static void crlmodule_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, int which, + int target) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct v4l2_rect *comp, *crops[CRL_PADS]; + + crlmodule_get_crop_compose(subdev, cfg, crops, &comp, which); + + switch (target) { + case V4L2_SEL_TGT_CROP: + comp->width = crops[CRL_PAD_SINK]->width; + comp->height = crops[CRL_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = 1; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SEL_TGT_COMPOSE: + *crops[CRL_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct v4l2_rect *comp, *crops[CRL_PADS]; + + crlmodule_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) { + sensor->binning_horizontal = crops[CRL_PAD_SINK]->width / + sel->r.width; + sensor->binning_vertical = crops[CRL_PAD_SINK]->height / + sel->r.height; + } else { + sensor->scale_m = crops[CRL_PAD_SINK]->width * + sensor->sensor_ds->sensor_limits->scaler_m_min / + sel->r.width; + } + + *comp = sel->r; + + crlmodule_propagate(subdev, cfg, sel->which, + V4L2_SEL_TGT_COMPOSE); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + crlmodule_update_current_mode(sensor); + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct v4l2_rect *src_size, *crops[CRL_PADS]; + struct v4l2_rect _r; + + crlmodule_get_crop_compose(subdev, cfg, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(subdev, + cfg, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(subdev, + cfg, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose(subdev, cfg, + ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == CRL_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min_t(s32, sel->r.left, src_size->width - sel->r.width); + sel->r.top = min_t(s32, sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == CRL_PAD_SINK) + crlmodule_propagate(subdev, cfg, sel->which, + V4L2_SEL_TGT_CROP); + + /* TODO! Should we short list supported mode? */ + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Modified based on the CRL Module changes + */ +static int crlmodule_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_rect *crops[CRL_PADS]; + + dev_dbg(&client->dev, "%s sd_name: %s pad: %d w: %d, h: %d code: 0x%x", + __func__, ssd->sd.name, fmt->pad, + fmt->format.width, fmt->format.height, + fmt->format.code); + + mutex_lock(&sensor->mutex); + + /* Currently we only support ALTERNATE interlaced mode. */ + if (fmt->format.field != V4L2_FIELD_ALTERNATE) + fmt->format.field = V4L2_FIELD_NONE; + ssd->field = fmt->format.field; + + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __crlmodule_get_format(subdev, cfg, fmt); + + if (!rval && subdev == &sensor->src->sd) { + /* Check if this code is supported, if yes get index */ + int idx = __crlmodule_get_data_fmt_index(sensor, code); + + if (idx < 0) { + dev_err(&client->dev, "%s invalid format\n", + __func__); + mutex_unlock(&sensor->mutex); + return -EINVAL; + } + + sensor->fmt_index = idx; + /* TODO! validate PLL? */ + } + mutex_unlock(&sensor->mutex); + return rval; + } + + fmt->format.width = + clamp_t(uint32_t, fmt->format.width, + sensor->sensor_ds->sensor_limits->x_addr_min, + sensor->sensor_ds->sensor_limits->x_addr_max); + fmt->format.height = + clamp_t(uint32_t, fmt->format.height, + sensor->sensor_ds->sensor_limits->y_addr_min, + sensor->sensor_ds->sensor_limits->y_addr_max); + + crlmodule_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + + crlmodule_propagate(subdev, cfg, fmt->which, + V4L2_SEL_TGT_CROP); + + crlmodule_update_current_mode(sensor); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int ret; + + dev_dbg(&client->dev, "%s sd_name: %s sel w: %d, h: %d target: %d", + __func__, ssd->sd.name, sel->r.width, + sel->r.height, sel->target); + + ret = __crlmodule_sel_supported(subdev, sel); + if (ret) { + dev_dbg(&client->dev, + "%s sd_name: %s w: %d, h: %d target: %d not supported", + __func__, ssd->sd.name, sel->r.width, + sel->r.height, sel->target); + return ret; + } + + mutex_lock(&sensor->mutex); + + sel->r.width = max_t(unsigned int, + sensor->sensor_ds->sensor_limits->x_addr_min, + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->sensor_ds->sensor_limits->y_addr_min, + sel->r.height); + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + ret = crlmodule_set_crop(subdev, cfg, sel); + break; + case V4L2_SEL_TGT_COMPOSE: + ret = crlmodule_set_compose(subdev, cfg, sel); + break; + default: + ret = -EINVAL; + } + + crlmodule_update_current_mode(sensor); + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int crlmodule_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + /* TODO Handle this */ + return 0; +} + +static int crlmodule_start_streaming(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct crl_pll_configuration *pll; + const struct crl_csi_data_fmt *fmt; + int rval; + + dev_dbg(&client->dev, "%s start streaming pll_idx: %d fmt_idx: %d\n", + __func__, sensor->pll_index, + sensor->fmt_index); + + pll = &sensor->sensor_ds->pll_configs[sensor->pll_index]; + fmt = &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + + crlmodule_update_current_mode(sensor); + + rval = crlmodule_write_regs(sensor, fmt->regs, fmt->regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set format\n", __func__); + return rval; + } + + rval = crlmodule_write_regs(sensor, pll->pll_regs, pll->pll_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set plls\n", __func__); + return rval; + } + + /* Write mode list */ + rval = crlmodule_write_regs(sensor, + sensor->current_mode->mode_regs, + sensor->current_mode->mode_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return rval; + } + + /* Write stream on list */ + rval = crlmodule_write_regs(sensor, + sensor->sensor_ds->streamon_regs, + sensor->sensor_ds->streamon_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set stream\n", __func__); + return rval; + } + + return 0; +} + +static int crlmodule_stop_streaming(struct crl_sensor *sensor) +{ + return crlmodule_write_regs(sensor, + sensor->sensor_ds->streamoff_regs, + sensor->sensor_ds->streamoff_regs_items); +} + +static int crlmodule_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval = 0; + + mutex_lock(&sensor->mutex); + + if (sensor->streaming == enable) + goto out; + + if (enable) { + + if (sensor->msr_list) { + rval = crlmodule_apply_msrlist(client, + sensor->msr_list); + if (rval) + dev_warn(&client->dev, "msrlist write error %d\n", + rval); + } + rval = crlmodule_start_streaming(sensor); + if (!rval) + sensor->streaming = 1; + } else { + rval = crlmodule_stop_streaming(sensor); + sensor->streaming = 0; + } + +out: + mutex_unlock(&sensor->mutex); + + /* SENSOR_IDLE control cannot be set when streaming*/ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_IDLE, enable); + + /* SENSOR_STREAMING controls cannot be set when not streaming */ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_STREAMING, !enable); + + /* SENSOR_POWERED_ON controls does not matter about streaming. */ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_POWERED_ON, false); + + return rval; +} + +static int crlmodule_identify_module(struct v4l2_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int size = 0, pos; + char *id_string; + const char *expect_id; + int i, ret; + u32 val; + + for (i = 0; i < sensor->sensor_ds->id_reg_items; i++) + size += sensor->sensor_ds->id_regs[i].width + 1; + + /* TODO! If no ID! return success? */ + if (!size) + return 0; + + expect_id = sensor->platform_data->id_string; + /* Create string variabel to append module ID */ + id_string = kzalloc(size, GFP_KERNEL); + if (!id_string) + return -ENOMEM; + *id_string = '\0'; + + /* Go through each regs in the list and append to id_string */ + for (i = 0; i < sensor->sensor_ds->id_reg_items; i++) { + ret = crlmodule_read_reg(sensor, + sensor->sensor_ds->id_regs[i].reg, + &val); + if (ret) + goto out; + + if (i) + pos += snprintf(id_string + pos, size - pos, " 0x%x", val); + else + pos = snprintf(id_string, size, "0x%x", val); + if (pos >= size) + break; + } + + /* Check here if this module in the supported list + * Ideally the module manufacturer and id should be in platform + * data or ACPI and here the driver should read the value from the + * register and check if this matches to any in the supported + * platform data + */ + if (expect_id && + (strnlen(id_string, size) != strnlen(expect_id, size + 1) || + strncmp(id_string, expect_id, size))) { + dev_err(&client->dev, + "Sensor detection failed: expect \"%s\" actual \"%s\"", + expect_id, id_string); + ret = -ENODEV; + } + +out: + dev_dbg(&client->dev, "%s module: %s expected id: %s\n", + __func__, id_string, + (expect_id) ? expect_id : "not specified"); + kfree(id_string); + if (ret) + dev_err(&client->dev, "sensor detection failed\n"); + return ret; +} + +static int crlmodule_get_frame_desc(struct v4l2_subdev *subdev, + unsigned int pad, + struct v4l2_mbus_frame_desc *desc) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_frame_desc *crl_desc = sensor->sensor_ds->frame_desc; + unsigned int i; + + desc->num_entries = sensor->sensor_ds->frame_desc_entries; + if (desc->num_entries) + desc->type = sensor->sensor_ds->frame_desc_type; + + /* + * By any chance the sensor configuration has more than the maximum + * supported, clip the number of entries to the MAX supported. + */ + if (desc->num_entries > V4L2_FRAME_DESC_ENTRY_MAX) + desc->num_entries = V4L2_FRAME_DESC_ENTRY_MAX; + + for (i = 0; i < desc->num_entries; i++) { + int ret; + u32 val; + + ret = __crlmodule_parse_dynamic_entity(sensor, + crl_desc[i].flags, &val); + if (ret) + return ret; + desc->entry[i].flags = (u16)val; + + ret = __crlmodule_parse_dynamic_entity(sensor, crl_desc[i].bpp, + &val); + if (ret) + return ret; + desc->entry[i].bpp = (u8)val; + + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].pixelcode, &val); + if (ret) + return ret; + desc->entry[i].pixelcode = val; + + if (desc->entry[i].flags & V4L2_MBUS_FRAME_DESC_FL_BLOB) { + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].length, &val); + if (ret) + return ret; + desc->entry[i].length = val; + } else { + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].start_line, &val); + if (ret) + return ret; + desc->entry[i].two_dim.start_line = + (u16)val; + + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].start_pixel, &val); + if (ret) + return ret; + desc->entry[i].two_dim.start_pixel = + (u16)val; + + ret = __crlmodule_calc_dynamic_entity_values( + sensor, crl_desc[i].height.ops_items, + crl_desc[i].height.ops, &val); + if (ret) + return ret; + desc->entry[i].two_dim.height = (u16)val; + + ret = __crlmodule_calc_dynamic_entity_values( + sensor, crl_desc[i].width.ops_items, + crl_desc[i].width.ops, &val); + if (ret) + return ret; + desc->entry[i].two_dim.width = (u16)val; + } + + if (desc->type == CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].csi2_channel, &val); + if (ret) + return ret; + desc->entry[i].bus.csi2.channel = (u8)val; + + ret = __crlmodule_parse_dynamic_entity( + sensor, crl_desc[i].csi2_data_type, &val); + if (ret) + return ret; + desc->entry[i].bus.csi2.data_type = (u8)val; + } + } + + return 0; +} + + +static int crlmodule_get_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + int i; + + if (!route) + return -EINVAL; + + if (ssd != sensor->src || + sensor->sensor_ds->frame_desc_entries <= 1) + return -ENOIOCTLCMD; + + for (i = 0; i < min(sensor->sensor_ds->frame_desc_entries, + route->num_routes); i++) { + route->routes[i].sink_pad = CRL_PAD_SINK; + route->routes[i].sink_stream = 0; + route->routes[i].source_pad = CRL_PAD_SRC; + route->routes[i].source_stream = i; + route->routes[i].flags = sensor->src->route_flags[i]; + } + + route->num_routes = i; + return 0; +} + +static int crlmodule_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + const unsigned int stream_nr = sensor->sensor_ds->frame_desc_entries; + struct v4l2_subdev_route *t; + int i, ret = 0; + + if (!route) + return -EINVAL; + + if (ssd != sensor->src || + sensor->sensor_ds->frame_desc_entries <= 1) + return -ENOIOCTLCMD; + + for (i = 0; i < min(stream_nr, route->num_routes); ++i) { + t = &route->routes[i]; + + if (t->source_stream > stream_nr - 1) + continue; + + if (t->source_pad != CRL_PAD_SRC || + t->sink_pad != CRL_PAD_SINK) + continue; + + if (sensor->src->route_flags[t->source_stream] & + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE) + continue; + + if (t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + sensor->src->route_flags[t->source_stream] |= + V4L2_SUBDEV_ROUTE_FL_ACTIVE; + else if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + sensor->src->route_flags[t->source_stream] &= + (~V4L2_SUBDEV_ROUTE_FL_ACTIVE); + } + + return ret; +} + +/* + * This function executes the initialisation routines after the power on + * is successfully completed. Following operations are done + * + * Initiases registers after sensor power up - if any such list is configured + * V4l2 Ctrl handler framework intialisation + */ +static int crlmodule_run_poweron_init(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + dev_dbg(&client->dev, "%s set power up registers: %d\n", + __func__, sensor->sensor_ds->powerup_regs_items); + + /* Write the power up registers */ + rval = crlmodule_write_regs(sensor, sensor->sensor_ds->powerup_regs, + sensor->sensor_ds->powerup_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set powerup registers\n", + __func__); + return rval; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + dev_dbg(&client->dev, "%s init v4l2 controls", __func__); + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) { + dev_err(&client->dev, "%s PA v4l2_ctrl_handler failed\n", + __func__); + return rval; + } + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + dev_err(&client->dev, "%s SRC v4l2_ctrl_handler failed\n", + __func__); + + /* SENSOR_IDLE control can be set only when not streaming*/ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_IDLE, false); + + /* SENSOR_STREAMING controls can be set only when streaming */ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_STREAMING, true); + + /* SENSOR_POWERED_ON controls can be set after power on */ + __crlmodule_grab_v4l2_ctrl(sensor, SENSOR_POWERED_ON, false); + + mutex_lock(&sensor->mutex); + crlmodule_update_current_mode(sensor); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int custom_gpio_request(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; i < CRL_MAX_CUSTOM_GPIO_AMOUNT; i++) { + if (sensor->platform_data->custom_gpio[i].name[0] == '\0') + break; + if (devm_gpio_request_one( + &client->dev, + sensor->platform_data->custom_gpio[i].number, 0, + sensor->platform_data->custom_gpio[i].name) != 0) { + dev_err(&client->dev, + "unable to acquire %s %d\n", + sensor->platform_data->custom_gpio[i].name, + sensor->platform_data->custom_gpio[i].number); + return -ENODEV; + } + } + return 0; +} + +static void custom_gpio_ctrl(struct crl_sensor *sensor, bool set) +{ + int i; + unsigned int val; + + for (i = 0; i < CRL_MAX_CUSTOM_GPIO_AMOUNT; i++) { + if (sensor->platform_data->custom_gpio[i].name[0] == '\0') + break; + if (set) + val = sensor->platform_data->custom_gpio[i].val; + else + val = sensor->platform_data->custom_gpio[i].undo_val; + + gpio_set_value( + sensor->platform_data->custom_gpio[i].number, val); + } +} + +/* + * This function handles sensor power up routine failure because of any failed + * step in the routine. The index "i" is the index to last successfull power + * sequence entity successfull completed. This function executes the power + * senquence entities in the reverse or with undo value. + */ +static void crlmodule_undo_poweron_entities( + struct crl_sensor *sensor, + int rev_idx) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_power_seq_entity *entity; + int idx; + + for (idx = rev_idx; idx >= 0; idx--) { + entity = &sensor->pwr_entity[idx]; + dev_dbg(&client->dev, "%s power type %d index %d\n", + __func__, entity->type, idx); + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + gpio_set_value(sensor->platform_data->xshutdown, + entity->undo_val); + break; + case CRL_POWER_ETY_GPIO_FROM_PDATA_BY_NUMBER: + custom_gpio_ctrl(sensor, false); + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + if (entity->gpiod_priv) { + if (gpiod_cansleep(entity->gpiod_priv)) + gpiod_set_raw_value_cansleep( + entity->gpiod_priv, + entity->undo_val); + else + gpiod_set_raw_value(entity->gpiod_priv, + entity->undo_val); + } else { + gpio_set_value(entity->ent_number, + entity->undo_val); + } + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + regulator_disable(entity->regulator_priv); + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + clk_disable_unprepare(sensor->xclk); + break; + default: + dev_err(&client->dev, "%s Invalid power type\n", + __func__); + break; + } + + if (entity->delay) + usleep_range(entity->delay, entity->delay + 10); + } +} + +static int __crlmodule_powerup_sequence(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_power_seq_entity *entity; + unsigned idx; + int rval; + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) { + entity = &sensor->pwr_entity[idx]; + dev_dbg(&client->dev, "%s power type %d index %d\n", + __func__, entity->type, idx); + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + gpio_set_value(sensor->platform_data->xshutdown, + entity->val); + break; + case CRL_POWER_ETY_GPIO_FROM_PDATA_BY_NUMBER: + custom_gpio_ctrl(sensor, true); + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + if (entity->gpiod_priv) { + if (gpiod_cansleep(entity->gpiod_priv)) + gpiod_set_raw_value_cansleep( + entity->gpiod_priv, + entity->val); + else + gpiod_set_raw_value(entity->gpiod_priv, + entity->val); + } else { + gpio_set_value(entity->ent_number, entity->val); + } + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + rval = regulator_enable(entity->regulator_priv); + if (rval) { + dev_err(&client->dev, + "Failed to enable regulator: %d\n", + rval); + devm_regulator_put(entity->regulator_priv); + entity->regulator_priv = NULL; + goto error; + } + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + rval = clk_set_rate(sensor->xclk, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock freq to %u\n", + sensor->platform_data->ext_clk); + goto error; + } + if (clk_get_rate(sensor->xclk) != + sensor->platform_data->ext_clk) + dev_warn(&client->dev, + "warning: unable to set \ + accurate clock freq %u\n", + sensor->platform_data->ext_clk); + rval = clk_prepare_enable(sensor->xclk); + if (rval) { + dev_err(&client->dev, "Failed to enable \ + clock: %d\n", rval); + goto error; + } + break; + default: + dev_err(&client->dev, "Invalid power type\n"); + rval = -ENODEV; + goto error; + } + + if (entity->delay) + usleep_range(entity->delay, entity->delay + 10); + } + + return 0; +error: + dev_err(&client->dev, "Error:Power sequece failed\n"); + if (idx > 0) + crlmodule_undo_poweron_entities(sensor, idx-1); + return rval; +} + +static int crlmodule_set_power(struct v4l2_subdev *subdev, int on) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int ret = 0; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put(&client->dev); + return ret; + } + } + + mutex_lock(&sensor->power_mutex); + if (on && !sensor->power_count) { + usleep_range(2000, 3000); + ret = crlmodule_run_poweron_init(sensor); + if (ret < 0) { + pm_runtime_put(&client->dev); + goto out; + } + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + + if (!on) + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_subdev_ops crlmodule_ops; +static const struct v4l2_subdev_internal_ops crlmodule_internal_ops; +static const struct media_entity_operations crlmodule_entity_ops; + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Modified based on the CRL Module changes + */ +static int crlmodule_init_subdevs(struct v4l2_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crlmodule_platform_data *platform_data = sensor->platform_data; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_subdev *prev_sd = NULL; + int i = 0, j; + struct crl_subdev *sd; + int rval = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + + /* + * The scaler, binner and PA order matters. Sensor configuration file + * must maintain this order. PA sub dev is a must and binner and + * scaler can be omitted based on the sensor. But if scaler is present + * it must be the first sd. + */ + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_SCALER) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_BINNER) { + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_PIXEL_ARRAY) { + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + /* CRL MediaCTL IF driver can't handle if none of these sd's present! */ + if (!sensor->ssds_used) { + dev_err(&client->dev, "%s no subdevs present\n", __func__); + return -ENODEV; + } + + if (!sensor->sensor_ds->pll_config_items) { + dev_err(&client->dev, "%s no pll configurations\n", __func__); + return -ENODEV; + } + + /* TODO validate rest of the settings from the sensor definition file */ + + dev_dbg(&client->dev, "%s subdevs: %d\n", __func__, i); + + for (i = 0; i < sensor->ssds_used; i++) { + bool has_substreams = false; + + sd = &sensor->ssds[i]; + + if (sd != sensor->src) + v4l2_subdev_init(&sd->sd, &crlmodule_ops); + else if (sensor->sensor_ds->frame_desc_entries > 1) + has_substreams = true; + + sd->sensor = sensor; + + if (sd == sensor->pixel_array) { + sd->npads = 1; + } else { + sd->npads = 2; + sd->source_pad = 1; + } + + if (platform_data->suffix) + snprintf(sd->sd.name, + sizeof(sd->sd.name), "%s %c", + sensor->sensor_ds->subdevs[i].name, + platform_data->suffix); + else + snprintf(sd->sd.name, + sizeof(sd->sd.name), "%s %d-%4.4x", + sensor->sensor_ds->subdevs[i].name, + i2c_adapter_id(client->adapter), + client->addr); + + + sd->sink_fmt.width = + sensor->sensor_ds->sensor_limits->x_addr_max; + sd->sink_fmt.height = + sensor->sensor_ds->sensor_limits->y_addr_max; + sd->compose.width = sd->sink_fmt.width; + sd->compose.height = sd->sink_fmt.height; + sd->crop[sd->source_pad] = sd->compose; + sd->pads[sd->source_pad].flags = MEDIA_PAD_FL_SOURCE | + (has_substreams ? MEDIA_PAD_FL_MULTIPLEX : 0); + if (sd != sensor->pixel_array) { + sd->crop[sd->sink_pad] = sd->compose; + sd->pads[sd->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + if (has_substreams) { + sd->route_flags = devm_kzalloc(&client->dev, + sizeof(unsigned int) * + sensor->sensor_ds->frame_desc_entries, + GFP_KERNEL); + if (!sd->route_flags) + return -ENOMEM; + for (j = 0; j < sensor->sensor_ds->frame_desc_entries; + j++) + sd->route_flags[j] = + V4L2_SUBDEV_ROUTE_FL_SOURCE; + sd->route_flags[0] |= + V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE; + } + + sd->sd.entity.ops = &crlmodule_entity_ops; + + if (prev_sd == NULL) { + prev_sd = sd; + continue; + } + + sd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->sd.internal_ops = &crlmodule_internal_ops; + sd->sd.owner = THIS_MODULE; + v4l2_set_subdevdata(&sd->sd, client); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_init(&sd->sd.entity, sd->npads, + sd->pads, 0); +#else + rval = media_entity_pads_init(&sd->sd.entity, sd->npads, + sd->pads); +#endif + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + return rval; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &sd->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + return rval; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_create_link(&sd->sd.entity, +#else + rval = media_create_pad_link(&sd->sd.entity, +#endif + sd->source_pad, + &prev_sd->sd.entity, + prev_sd->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + return rval; + } + + prev_sd = sd; + } + + return rval; +} + +static int __init_power_resources(struct v4l2_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_power_seq_entity *entity; + unsigned idx; + + sensor->pwr_entity = devm_kzalloc(&client->dev, + sizeof(struct crl_power_seq_entity) * + sensor->sensor_ds->power_items, GFP_KERNEL); + + if (!sensor->pwr_entity) + return -ENOMEM; + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) + sensor->pwr_entity[idx] = + sensor->sensor_ds->power_entities[idx]; + + dev_dbg(&client->dev, "%s\n", __func__); + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) { + int rval; + + entity = &sensor->pwr_entity[idx]; + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + if (devm_gpio_request_one(&client->dev, + sensor->platform_data->xshutdown, 0, + "CRL xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire xshutdown %d\n", + sensor->platform_data->xshutdown); + return -ENODEV; + } + break; + case CRL_POWER_ETY_GPIO_FROM_PDATA_BY_NUMBER: + rval = custom_gpio_request(sensor); + if (rval < 0) + return rval; + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + if (entity->ent_name[0]) { + entity->gpiod_priv = gpiod_get(NULL, + entity->ent_name, GPIOD_OUT_LOW); + if (IS_ERR(entity->gpiod_priv)) { + dev_err(&client->dev, + "unable to acquire custom gpio %s\n", + entity->ent_name); + entity->gpiod_priv = NULL; + return -ENODEV; + } + } else { + if (devm_gpio_request_one(&client->dev, + entity->ent_number, 0, + "CRL Custom") != 0) { + dev_err(&client->dev, + "unable to acquire custom gpio %d\n", + entity->ent_number); + return -ENODEV; + } + } + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + entity->regulator_priv = devm_regulator_get( + &client->dev, entity->ent_name); + if (IS_ERR(entity->regulator_priv)) { + dev_err(&client->dev, + "Failed to get regulator: %s\n", + entity->ent_name); + entity->regulator_priv = NULL; + return -ENODEV; + } + rval = regulator_set_voltage(entity->regulator_priv, + entity->val, + entity->val); + /* Not all regulator supports voltage change */ + if (rval < 0) + dev_info(&client->dev, + "Failed to set voltage %s %d\n", + entity->ent_name, entity->val); + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + sensor->xclk = devm_clk_get(&client->dev, + entity->ent_name[0] ? entity->ent_name : NULL); + if (IS_ERR(sensor->xclk)) { + dev_err(&client->dev, + "Cannot get sensor clk\n"); + return -ENODEV; + } + break; + default: + dev_err(&client->dev, "Invalid Power item\n"); + return -ENODEV; + } + } + + return 0; +} + +static int crl_request_gpio_irq(struct crl_sensor *sensor) +{ + int rval; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int irq_pin = sensor->platform_data->crl_irq_pin; + + if (!gpio_is_valid(irq_pin)) { + dev_err(&client->dev, "%s: GPIO pin %d is invalid!\n", + __func__, irq_pin); + return -ENODEV; + } + dev_dbg(&client->dev, + "%s: IRQ GPIO %d is valid.\n", __func__, irq_pin); + + rval = devm_gpio_request(&client->dev, irq_pin, + sensor->platform_data->irq_pin_name); + if (rval) { + dev_err(&client->dev, + "%s:IRQ GPIO pin request failed!\n", __func__); + return rval; + } + + gpio_direction_input(irq_pin); + sensor->irq = gpio_to_irq(irq_pin); + rval = devm_request_threaded_irq(&client->dev, sensor->irq, + sensor->sensor_ds->crl_irq_fn, + sensor->sensor_ds->crl_threaded_irq_fn, + sensor->platform_data->irq_pin_flags, + sensor->platform_data->irq_pin_name, + sensor); + + dev_dbg(&client->dev, "%s: GPIO register GPIO IRQ result: %d\n", + __func__, rval); + + return rval; +} + +static int crlmodule_registered(struct v4l2_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + int rval; + + rval = __init_power_resources(subdev); + if (rval) + return -ENODEV; + + pm_runtime_enable(&client->dev); + + /* Power up the sensor */ + if (pm_runtime_get_sync(&client->dev) < 0) { + rval = -ENODEV; + goto out; + } + + /* init GPIO IRQ */ + if (sensor->sensor_ds->irq_in_use == true) { + rval = crl_request_gpio_irq(sensor); + if (rval) { + rval = -ENODEV; + goto out; + } + } + + /* one time init */ + rval = crlmodule_write_regs(sensor, + sensor->sensor_ds->onetime_init_regs, + sensor->sensor_ds->onetime_init_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set powerup registers\n", + __func__); + rval = -ENODEV; + goto out; + } + + /* sensor specific init */ + if (sensor->sensor_ds->sensor_init) { + rval = sensor->sensor_ds->sensor_init(client); + + if (rval) { + dev_err(&client->dev, + "%s failed to run sensor specific init\n", + __func__); + rval = -ENODEV; + goto out; + } + } + /* Identify the module */ + rval = crlmodule_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out; + } + + rval = crlmodule_init_subdevs(subdev); + if (rval) + goto out; + + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + sensor->scale_m = 1; + sensor->flip_info = CRL_FLIP_DEFAULT_NONE; + sensor->ext_ctrl_impacts_pll_selection = false; + sensor->ext_ctrl_impacts_mode_selection = false; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; +#else + sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; +#endif + + rval = crlmodule_init_controls(sensor); + if (rval) + goto out; + + mutex_lock(&sensor->mutex); + crlmodule_update_current_mode(sensor); + mutex_unlock(&sensor->mutex); + rval = crlmodule_nvm_init(sensor); + +out: + dev_dbg(&client->dev, "%s rval: %d\n", __func__, rval); + /* crlmodule_power_off(sensor); */ + pm_runtime_put(&client->dev); + + return rval; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10; + unsigned int i; + int rval; + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(sd, + fh->pad, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->sensor_ds->sensor_limits->x_addr_max; + try_fmt->height = sensor->sensor_ds->sensor_limits->y_addr_max; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + + rval = pm_runtime_get_sync(&client->dev); + if (rval < 0) + pm_runtime_put(&client->dev); + return rval; +} + +static int crlmodule_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + pm_runtime_put(&client->dev); + + return 0; +} + +static int crlmodule_get_registers(struct v4l2_subdev *sd, struct crl_registers_info *info) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct crl_register_read_rep reg; + int i; + int ret = 0; + + if (info->number > REGS_BUF_SIZE) { + dev_err(&client->dev, "error: max register's numbers than %d\n", REGS_BUF_SIZE); + return -1; + } + + for (i = 0; i < info->number; i++) { + reg.address = info->start_address + i; + reg.dev_i2c_addr = CRL_I2C_ADDRESS_NO_OVERRIDE; + reg.len = info->len; + reg.mask = 0xff; + ret = crlmodule_read_reg(sensor, reg, &info->regs[i]); + if (ret < 0) + return ret; + } + + return ret; +} + +static int crlmodule_set_registers(struct v4l2_subdev *sd, struct crl_registers_info *info) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + int ret = 0; + + if (info->number > REGS_BUF_SIZE) { + dev_err(&client->dev, "error: max register's numbers than %d\n", REGS_BUF_SIZE); + return -1; + } + + for (i = 0; i < info->number; i++) { + ret = crlmodule_write_reg(sensor, CRL_I2C_ADDRESS_NO_OVERRIDE, + info->start_address + i, info->len, 0xff, info->regs[i]); + if (ret < 0) + return ret; + } + + return ret; +} + +static long crlmodule_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret; + + switch (cmd) { + case CRL_G_REGISTERS: + ret = crlmodule_get_registers(sd, arg); + break; + case CRL_S_REGISTERS: + ret = crlmodule_set_registers(sd, arg); + break; + default: + ret = -1; + break; + }; + + return ret; +} + +static const struct v4l2_subdev_video_ops crlmodule_video_ops = { + .s_stream = crlmodule_set_stream, +}; + +static const struct v4l2_subdev_core_ops crlmodule_core_ops = { + .s_power = crlmodule_set_power, + .ioctl = crlmodule_ioctl, +}; + +static const struct v4l2_subdev_pad_ops crlmodule_pad_ops = { + .enum_mbus_code = crlmodule_enum_mbus_code, + .get_fmt = crlmodule_get_format, + .set_fmt = crlmodule_set_format, + .get_selection = crlmodule_get_selection, + .set_selection = crlmodule_set_selection, + .enum_frame_size = crlmodule_enum_frame_size, + .get_frame_desc = crlmodule_get_frame_desc, + .get_routing = crlmodule_get_routing, + .set_routing = crlmodule_set_routing, +}; + +static const struct v4l2_subdev_sensor_ops crlmodule_sensor_ops = { + .g_skip_frames = crlmodule_get_skip_frames, +}; + +static const struct v4l2_subdev_ops crlmodule_ops = { + .core = &crlmodule_core_ops, + .video = &crlmodule_video_ops, + .pad = &crlmodule_pad_ops, + .sensor = &crlmodule_sensor_ops, +}; + +static const struct media_entity_operations crlmodule_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops crlmodule_internal_src_ops = { + .registered = crlmodule_registered, + .open = crlmodule_open, + .close = crlmodule_close, +}; + +static const struct v4l2_subdev_internal_ops crlmodule_internal_ops = { + .open = crlmodule_open, + .close = crlmodule_close, +}; + +#ifdef CONFIG_PM + +static int crlmodule_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(sd); + + crlmodule_undo_poweron_entities(sensor, + sensor->sensor_ds->power_items - 1); + return 0; +} + +static int crlmodule_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(sd); + + return __crlmodule_powerup_sequence(sensor); +} + +static int crlmodule_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + + if (sensor->streaming) + crlmodule_stop_streaming(sensor); + + if (sensor->power_count > 0) + crlmodule_undo_poweron_entities(sensor, + sensor->sensor_ds->power_items - 1); + return 0; +} + +static int crlmodule_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + int rval = 0; + + if (sensor->power_count > 0) { + rval = __crlmodule_powerup_sequence(sensor); + if (!rval) + rval = crlmodule_run_poweron_init(sensor); + } + + if (!rval && sensor->streaming) + rval = crlmodule_start_streaming(sensor); + + return rval; +} +#else +#define crlmodule_runtime_suspend NULL +#define crlmodule_runtime_resume NULL +#define crlmodule_suspend NULL +#define crlmodule_resume NULL +#endif /* CONFIG_PM */ + +static int crlmodule_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct crl_sensor *sensor; + int ret; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + /* TODO! Create the sensor based on the interface */ + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + + ret = crlmodule_populate_ds(sensor, &client->dev); + if (ret) + return -ENODEV; + + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &crlmodule_ops); + sensor->src->sd.internal_ops = &crlmodule_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + if (sensor->sensor_ds->frame_desc_entries > 1) + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_SUBSTREAMS; + + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + if (sensor->sensor_ds->frame_desc_entries > 1) + sensor->src->sd.flags |= MEDIA_PAD_FL_MULTIPLEX; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + ret = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); +#else + ret = media_entity_pads_init(&sensor->src->sd.entity, 2, + sensor->src->pads); +#endif + if (ret < 0) + goto cleanup; + ret = v4l2_async_register_subdev(&sensor->src->sd); + if (ret < 0) + goto cleanup; + + /* Load IQ tuning registers from drvb file*/ + if (sensor->sensor_ds->msr_file_name) { + ret = crlmodule_load_msrlist(client, + sensor->sensor_ds->msr_file_name, + &sensor->msr_list); + if (ret) + dev_warn(&client->dev, + "msrlist loading failed. Ignore, move on\n"); + } else { + /* sensor will still continue streaming */ + dev_warn(&client->dev, "No msrlists associated with sensor\n"); + } + + return 0; + +cleanup: + media_entity_cleanup(&sensor->src->sd.entity); + crlmodule_release_ds(sensor); + return ret; +} + +static void crlmodule_free_controls(struct crl_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int crlmodule_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + unsigned int i; + + if (sensor->sensor_ds->sensor_cleanup) + sensor->sensor_ds->sensor_cleanup(client); + + v4l2_async_unregister_subdev(&sensor->src->sd); + for (i = 0; i < sensor->ssds_used; i++) { + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + media_entity_cleanup(&sensor->ssds[i].sd.entity); + } + + for (i = 0; i < sensor->sensor_ds->power_items; i++) { + struct crl_power_seq_entity *entity = + &sensor->pwr_entity[i]; + + if (entity->type == CRL_POWER_ETY_GPIO_CUSTOM && + entity->gpiod_priv) + gpiod_put(entity->gpiod_priv); + } + + crlmodule_nvm_deinit(sensor); + crlmodule_release_ds(sensor); + crlmodule_free_controls(sensor); + crlmodule_release_msrlist(&sensor->msr_list); + + pm_runtime_disable(&client->dev); + + return 0; +} + + +static const struct i2c_device_id crlmodule_id_table[] = { + { CRLMODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, crlmodule_id_table); + +static const struct dev_pm_ops crlmodule_pm_ops = { + .runtime_suspend = crlmodule_runtime_suspend, + .runtime_resume = crlmodule_runtime_resume, + .suspend = crlmodule_suspend, + .resume = crlmodule_resume, +}; + +static struct i2c_driver crlmodule_i2c_driver = { + .driver = { + .name = CRLMODULE_NAME, + .pm = &crlmodule_pm_ops, + }, + .probe = crlmodule_probe, + .remove = crlmodule_remove, + .id_table = crlmodule_id_table, +}; + +module_i2c_driver(crlmodule_i2c_driver); + +MODULE_AUTHOR("Vinod Govindapillai "); +MODULE_AUTHOR("Jouni Ukkonen "); +MODULE_AUTHOR("Tommi Franttila "); +MODULE_DESCRIPTION("Generic driver for common register list based \ + camera sensor modules"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/crlmodule/crlmodule-data.c b/drivers/media/i2c/crlmodule/crlmodule-data.c new file mode 100644 index 000000000000..d4143a53a7fa --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-data.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#include "crlmodule.h" +#include "crl_imx132_configuration.h" +#include "crl_imx214_configuration.h" +#include "crl_imx135_configuration.h" +#include "crl_imx230_configuration.h" +#include "crl_imx318_configuration.h" +#include "crl_ov8858_configuration.h" +#include "crl_ov13860_configuration.h" +#include "crl_adv7481_cvbs_configuration.h" +#include "crl_adv7481_hdmi_configuration.h" +#include "crl_adv7481_eval_configuration.h" +#include "crl_imx185_configuration.h" +#include "crl_ov10635_configuration.h" +#include "crl_ar0231at_configuration.h" +#include "crl_ov10640_configuration.h" +#include "crl_imx477_master_configuration.h" +#include "crl_imx477_slave_configuration.h" +#include "crl_imx274_configuration.h" +#include "crl_ov5670_configuration.h" +#include "crl_imx290_configuration.h" +#include "crl_pixter_stub_configuration.h" +#include "crl_ov2740_configuration.h" +#include "crl_ov9281_configuration.h" +#include "crl_magna_configuration.h" +#include "crl_ar023z_configuration.h" +#include "crl_ov2775_configuration.h" +#include "crl_ox03a10_configuration.h" + +static const struct crlmodule_sensors supported_sensors[] = { + { "i2c-SONY214A:00", "imx214", &imx214_crl_configuration }, + { "IMX214", "imx214", &imx214_crl_configuration }, + { "i2c-SONY132A:00", "imx132", &imx132_crl_configuration }, + { "i2c-INT3471:00", "imx135", &imx135_crl_configuration }, + { "i2c-SONY230A:00", "imx230", &imx230_crl_configuration }, + { "i2c-INT3477:00", "ov8858", &ov8858_crl_configuration }, + { "i2c-OV5670AA:00", "ov5670", &ov5670_crl_configuration }, + { "IMX185", "imx185", &imx185_crl_configuration }, + { "IMX477-MASTER", "imx477", &imx477_master_crl_configuration }, + { "IMX477-SLAVE-1", "imx477", &imx477_slave_crl_configuration }, + { "OV13860", "ov13860", &ov13860_crl_configuration }, + { "OV9281", "ov9281", &ov9281_crl_configuration }, + { "ADV7481 CVBS", "adv7481_cvbs", &adv7481_cvbs_crl_configuration }, + { "ADV7481 HDMI", "adv7481_hdmi", &adv7481_hdmi_crl_configuration }, + { "ADV7481_EVAL", "adv7481_eval", &adv7481_eval_crl_configuration }, + { "ADV7481B_EVAL", "adv7481b_eval", &adv7481b_eval_crl_configuration }, + { "SONY318A", "imx318", &imx318_crl_configuration }, + { "OV10635", "ov10635", &ov10635_crl_configuration }, + { "AR0231AT", "ar0231at", &ar0231at_crl_configuration }, + { "OV10640", "ov10640", &ov10640_crl_configuration }, + { "IMX274", "imx274", &imx274_crl_configuration }, + { "OV5670", "ov5670", &ov5670_crl_configuration }, + { "IMX290", "imx290", &imx290_crl_configuration}, + { "PIXTER_STUB", "pixter_stub", &pixter_stub_crl_configuration}, + { "PIXTER_STUB_B", "pixter_stub_b", &pixter_stub_b_crl_configuration}, + { "PIXTER_STUB_C", "pixter_stub_c", &pixter_stub_c_crl_configuration}, + { "PIXTER_STUB_D", "pixter_stub_d", &pixter_stub_d_crl_configuration}, + { "PIXTER_STUB_E", "pixter_stub_e", &pixter_stub_e_crl_configuration}, + { "PIXTER_STUB_F", "pixter_stub_f", &pixter_stub_f_crl_configuration}, + { "PIXTER_STUB_G", "pixter_stub_g", &pixter_stub_g_crl_configuration}, + { "PIXTER_STUB_H", "pixter_stub_h", &pixter_stub_h_crl_configuration}, + { "INT3474", "ov2740", &ov2740_crl_configuration }, + { "MAGNA", "magna", &magna_crl_configuration }, + { "AR023Z", "ar023z", &ar023z_crl_configuration }, + { "OV2775", "ov2775", &ov2775_crl_configuration }, + { "OX03A10", "ox03a10", &ox03a10_crl_configuration }, +}; + +/* + * Function to populate the CRL data structure from the sensor configuration + * definition file + */ +int crlmodule_populate_ds(struct crl_sensor *sensor, struct device *dev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_sensors); i++) { + /* Check the ACPI supported modules */ + if (!strcmp(dev_name(dev), supported_sensors[i].pname)) { + sensor->sensor_ds = supported_sensors[i].ds; + dev_info(dev, "%s %s selected\n", + __func__, supported_sensors[i].name); + return 0; + }; + + /* Check the non ACPI modules */ + if (!strcmp(sensor->platform_data->module_name, + supported_sensors[i].pname)) { + sensor->sensor_ds = supported_sensors[i].ds; + dev_info(dev, "%s %s selected\n", + __func__, supported_sensors[i].name); + return 0; + }; + } + + dev_err(dev, "%s No suitable configuration found for %s\n", + __func__, dev_name(dev)); + return -EINVAL; +} + +/* + * Function validate the contents CRL data structure to check if all the + * required fields are filled and are according to the limits. + */ +int crlmodule_validate_ds(struct crl_sensor *sensor) +{ + /* TODO! Revisit this. */ + return 0; +} + +/* Function to free all resources allocated for the CRL data structure */ +void crlmodule_release_ds(struct crl_sensor *sensor) +{ + /* + * TODO! Revisit this. + * Place for cleaning all the resources used for the generation + * of CRL data structure. + */ +} diff --git a/drivers/media/i2c/crlmodule/crlmodule-msrlist.c b/drivers/media/i2c/crlmodule/crlmodule-msrlist.c new file mode 100644 index 000000000000..a15b76b921d2 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-msrlist.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2016 - 2018 Intel Corporation + +#include +#include +#include "crlmodule-msrlist.h" +#include "crlmodule.h" + +/* + * + * DRVB file is part of the old structure of tagged + * binary container, which is used as such in crlmodule. + * Changes needs to be done in cameralibs to remove the + * tagged structure and convert to untagged drvb format. + * Below are the tagged binary data container structure + * definitions. Most of it is copied from libmsrlisthelper.c + * and some changes done for crlmodule. + * + */ + +static int crlmodule_write_msrlist(struct i2c_client *client, u8 *bufptr, + unsigned int size) +{ + /* + * + * The configuration data contains any number of sequences where + * the first byte (that is, u8) that marks the number of bytes + * in the sequence to follow, is indeed followed by the indicated + * number of bytes of actual data to be written to sensor. + * By convention, the first two bytes of actual data should be + * understood as an address in the sensor address space (hibyte + * followed by lobyte) where the remaining data in the sequence + * will be written. + * + */ + + u8 *ptr = bufptr; + int ret; + + while (ptr < bufptr + size) { + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + }; + + msg.len = *ptr++; + msg.buf = ptr; + ptr += msg.len; + + if (ptr > bufptr + size) + return -EINVAL; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "i2c write error: %d", ret); + return ret; + } + } + return 0; +} + +static int crlmodule_parse_msrlist(struct i2c_client *client, u8 *buffer, + unsigned int size) +{ + u8 *endptr8 = buffer + size; + int ret; + unsigned int dataset = 0; + struct tbd_data_record_header *header = + (struct tbd_data_record_header *)buffer; + + do { + + if ((u8 *)header + sizeof(*header) > endptr8) + return -EINVAL; + + if ((u8 *)header + header->data_offset + + header->data_size > endptr8) + return -EINVAL; + + dataset++; + + if (header->data_size && (header->flags & 1)) { + + ret = crlmodule_write_msrlist(client, + buffer + header->data_offset, + header->data_size); + if (ret) + return ret; + } + header = (struct tbd_data_record_header *)(buffer + + header->next_offset); + } while (header->next_offset); + + return 0; +} + + +int crlmodule_apply_msrlist(struct i2c_client *client, + const struct firmware *fw) +{ + struct tbd_header *header; + struct tbd_record_header *record; + + header = (struct tbd_header *)fw->data; + record = (struct tbd_record_header *)(header + 1); + + if (record->size && record->class_id != TBD_CLASS_DRV_ID) + return -EINVAL; + + return crlmodule_parse_msrlist(client, (u8 *)(record + 1), + record->size); +} + + +int crlmodule_load_msrlist(struct i2c_client *client, char *name, + const struct firmware **fw) +{ + + struct tbd_header *header; + struct tbd_record_header *record; + int ret = -ENOENT; + + ret = request_firmware(fw, name, &client->dev); + if (ret) { + dev_err(&client->dev, + "Error %d while requesting firmware %s\n", + ret, name); + return ret; + } + header = (struct tbd_header *)(*fw)->data; + + if (sizeof(*header) > (*fw)->size) + goto out; + + /* Check that we have drvb block. */ + if (memcmp(&header->tag, "DRVB", 4)) + goto out; + + if (header->size != (*fw)->size) + goto out; + + if (sizeof(*header) + sizeof(*record) > (*fw)->size) + goto out; + + + return 0; + +out: + crlmodule_release_msrlist(fw); + return ret; +} + + +void crlmodule_release_msrlist(const struct firmware **fw) +{ + release_firmware(*fw); + *fw = NULL; +} diff --git a/drivers/media/i2c/crlmodule/crlmodule-msrlist.h b/drivers/media/i2c/crlmodule/crlmodule-msrlist.h new file mode 100644 index 000000000000..013469bfec1a --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-msrlist.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef __CRLMODULE_MSRLIST_H__ +#define __CRLMODULE_MSRLIST_H__ + +#define TBD_CLASS_DRV_ID 2 + +struct i2c_client; +struct firmware; + +struct tbd_header { + /* Tag identifier, also checks endianness */ + u32 tag; + /* Container size including this header */ + u32 size; + /* Version, format 0xYYMMDDVV */ + u32 version; + /* Revision, format 0xYYMMDDVV */ + u32 revision; + /* Configuration flag bits set */ + u32 config_bits; + /* Global checksum, header included */ + u32 checksum; +} __packed; + +struct tbd_record_header { + /* Size of record including header */ + u32 size; + /* tbd_format_t enumeration values used */ + u8 format_id; + /* Packing method; 0 = no packing */ + u8 packing_key; + /* tbd_class_t enumeration values used */ + u16 class_id; +} __packed; + +struct tbd_data_record_header { + u16 next_offset; + u16 flags; + u16 data_offset; + u16 data_size; +} __packed; + +int crlmodule_load_msrlist(struct i2c_client *client, char *name, + const struct firmware **fw); +int crlmodule_apply_msrlist(struct i2c_client *client, + const struct firmware *fw); +void crlmodule_release_msrlist(const struct firmware **fw); + +#endif /* ifndef __CRLMODULE_MSRLIST_H__ */ diff --git a/drivers/media/i2c/crlmodule/crlmodule-nvm.c b/drivers/media/i2c/crlmodule/crlmodule-nvm.c new file mode 100644 index 000000000000..50d984804739 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-nvm.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2016 - 2018 Intel Corporation + * + * Author: Tommi Franttila + * + */ + +#include +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" + +static ssize_t crlmodule_sysfs_nvm_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + memcpy(buf, sensor->nvm_data, min_t(unsigned long, PAGE_SIZE, + sensor->nvm_size)); + return sensor->nvm_size; +} + +DEVICE_ATTR(nvm, S_IRUGO, crlmodule_sysfs_nvm_read, NULL); + +static unsigned int crlmodule_get_nvm_size(struct crl_sensor *sensor) +{ + + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i, size = 0; + + for (i = 0; i < sensor->sensor_ds->crl_nvm_info.nvm_blobs_items; i++) + size += sensor->sensor_ds->crl_nvm_info.nvm_config[i].size; + + if (size > PAGE_SIZE) { + dev_err(&client->dev, "nvm size too big\n"); + size = 0; + } + return size; +} + +static int crlmodule_get_nvm_data(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + int rval = 0; + + u8 *nvm_data = sensor->nvm_data; + + if (sensor->sensor_ds->crl_nvm_info.nvm_preop_regs_items) { + dev_dbg(&client->dev, + "%s perform pre-operations\n", __func__); + + rval = crlmodule_write_regs( + sensor, + sensor->sensor_ds->crl_nvm_info.nvm_preop_regs, + sensor->sensor_ds->crl_nvm_info.nvm_preop_regs_items); + if (rval) { + dev_err(&client->dev, + "failed to perform nvm pre-operations\n"); + return rval; + } + } + + for (i = 0; i < sensor->sensor_ds->crl_nvm_info.nvm_blobs_items; i++) { + + dev_dbg(&client->dev, + "%s read blob %d dev_addr: 0x%x start_addr: 0x%x size: %d", + __func__, i, + sensor->sensor_ds->crl_nvm_info.nvm_config->dev_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->start_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->size); + + crlmodule_block_read(sensor, + sensor->sensor_ds->crl_nvm_info.nvm_config->dev_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->start_addr, + sensor->sensor_ds->crl_nvm_info.nvm_flags + & CRL_NVM_ADDR_MODE_MASK, + sensor->sensor_ds->crl_nvm_info.nvm_config->size, + nvm_data); + + nvm_data += sensor->sensor_ds->crl_nvm_info.nvm_config->size; + sensor->sensor_ds->crl_nvm_info.nvm_config++; + } + + if (sensor->sensor_ds->crl_nvm_info.nvm_postop_regs_items) { + dev_dbg(&client->dev, "%s perform post-operations\n", + __func__); + rval = crlmodule_write_regs( + sensor, + sensor->sensor_ds->crl_nvm_info.nvm_postop_regs, + sensor->sensor_ds->crl_nvm_info.nvm_postop_regs_items); + if (rval) { + dev_err(&client->dev, + "failed to perform nvm post-operations\n"); + return rval; + } + } + return rval; +} + +int crlmodule_nvm_init(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int size = crlmodule_get_nvm_size(sensor); + int rval; + + if (size) { + sensor->nvm_data = devm_kzalloc(&client->dev, size, GFP_KERNEL); + if (sensor->nvm_data == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + return -ENOMEM; + } + sensor->nvm_size = size; + + rval = crlmodule_get_nvm_data(sensor); + if (rval) + goto err; + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto err; + } + } + + return 0; +err: + sensor->nvm_size = 0; + return rval; +} + +void crlmodule_nvm_deinit(struct crl_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + if (sensor->nvm_size) { + device_remove_file(&client->dev, &dev_attr_nvm); + sensor->nvm_size = 0; + } +} diff --git a/drivers/media/i2c/crlmodule/crlmodule-nvm.h b/drivers/media/i2c/crlmodule/crlmodule-nvm.h new file mode 100644 index 000000000000..42d462d321cc --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-nvm.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Author: Tommi Franttila + * + */ + +#ifndef __CRLMODULE_NVM_H_ +#define __CRLMODULE_NVM_H_ + +#include "crlmodule.h" + +#define CRL_NVM_ADDR_MODE_8BIT 0x00000001 +#define CRL_NVM_ADDR_MODE_16BIT 0x00000002 + +#define CRL_NVM_ADDR_MODE_MASK (CRL_NVM_ADDR_MODE_8BIT | \ + CRL_NVM_ADDR_MODE_16BIT) + + +int crlmodule_nvm_init(struct crl_sensor *sensor); +void crlmodule_nvm_deinit(struct crl_sensor *sensor); + +#endif /* __CRLMODULE_NVM_H_ */ diff --git a/drivers/media/i2c/crlmodule/crlmodule-regs.c b/drivers/media/i2c/crlmodule/crlmodule-regs.c new file mode 100644 index 000000000000..e4b8c8aa36e8 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-regs.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2017 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#include +#include + +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" + +static DEFINE_MUTEX(crl_i2c_mutex); + +static bool reg_verify; +module_param(reg_verify, bool, 0444); +MODULE_PARM_DESC(reg_verify, "enable/disable registers write value and read value checking"); + +static int crlmodule_i2c_read(struct crl_sensor *sensor, u16 dev_i2c_addr, + u16 reg, u8 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg[2]; + unsigned char data[4]; + int r; + + dev_dbg(&client->dev, "%s reg, len: [0x%04x, %d]", __func__, reg, len); + + if (len != CRL_REG_LEN_08BIT && len != CRL_REG_LEN_16BIT && + len != CRL_REG_LEN_24BIT && len != CRL_REG_LEN_32BIT) + return -EINVAL; + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) + msg[0].addr = client->addr; + else + msg[0].addr = dev_i2c_addr; + + msg[1].addr = msg[0].addr; + + msg[0].flags = 0; + msg[0].buf = data; + + if (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) { + /* change address to 7bit format */ + msg[0].addr = msg[0].addr >> 1; + msg[1].addr = msg[1].addr >> 1; + } + if ((sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) || + (sensor->sensor_ds->addr_len == CRL_ADDR_8BIT)) { + data[0] = (u8) (reg & 0xff); + msg[0].len = 1; + } else { + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + msg[0].len = 2; + } + + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + msg[1].len = len; + + r = i2c_transfer(client->adapter, msg, 2); + + if (r < 0) + goto err; + + *val = 0; + /* high byte comes first */ + switch (len) { + case CRL_REG_LEN_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case CRL_REG_LEN_24BIT: + *val = (data[0] << 16) + (data[1] << 8) + data[2]; + break; + case CRL_REG_LEN_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case CRL_REG_LEN_08BIT: + *val = data[0]; + break; + } + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); + + return r; +} + +static int crlmodule_i2c_write(struct crl_sensor *sensor, u16 dev_i2c_addr, + u16 reg, u8 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + int r; + int ret; + u32 rval; + unsigned char *data_offset; + + if (len != CRL_REG_LEN_08BIT && len != CRL_REG_LEN_16BIT && + len != CRL_REG_LEN_24BIT && len != CRL_REG_LEN_32BIT) + return -EINVAL; + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) + msg.addr = client->addr; + else + msg.addr = dev_i2c_addr; + + msg.flags = 0; /* Write */ + msg.buf = data; + + if (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) + msg.addr = msg.addr >> 1; + + if ((sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) || + (sensor->sensor_ds->addr_len == CRL_ADDR_8BIT)) { + data[0] = (u8) (reg & 0xff); + msg.len = 1 + len; + data_offset = &data[1]; + } else { + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + msg.len = 2 + len; + data_offset = &data[2]; + } + + dev_dbg(&client->dev, "%s len reg, val: [%d, 0x%04x, 0x%04x]", + __func__, len, reg, val); + + switch (len) { + case CRL_REG_LEN_08BIT: + val = val & 0xFF; + data_offset[0] = val; + break; + case CRL_REG_LEN_16BIT: + val = val & 0xFFFF; + data_offset[0] = val >> 8; + data_offset[1] = val; + break; + case CRL_REG_LEN_24BIT: + val = val & 0xFFFFFF; + data_offset[0] = val >> 16; + data_offset[1] = val >> 8; + data_offset[2] = val; + break; + case CRL_REG_LEN_32BIT: + data_offset[0] = val >> 24; + data_offset[1] = val >> 16; + data_offset[2] = val >> 8; + data_offset[3] = val; + break; + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. retries: %d\n", + retries); + + if (reg_verify) { + ret = crlmodule_i2c_read(sensor, dev_i2c_addr, reg, len, &rval); + if (ret < 0) + dev_err(&client->dev, "i2c read error\n"); + else if (rval != val) { + dev_warn(&client->dev, + "reg:0x%x write val(0x%x), read val(0x%x)", + reg, val, rval); + } + } + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, reg, r); + + return r; +} + +int crlmodule_read_reg(struct crl_sensor *sensor, + const struct crl_register_read_rep reg, u32 *val) +{ + return crlmodule_i2c_read(sensor, reg.dev_i2c_addr, reg.address, + reg.len, val); +} + +int crlmodule_write_reg(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 reg, + u8 len, u32 mask, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int ret; + u32 val2; + + /* + * Sensor setting sequence may need some delay. + * delay value is specified by reg.val field + */ + if (len == CRL_REG_LEN_DELAY) { + msleep(val); + return 0; + } + + /* + * If the same register is being used for two settings, updating + * one value should not overwrite the other one. Such registers + * must be marked as CRL_REG_READ_AND_UPDATE. For such registers + * first read the register and update it + */ + + if (len & CRL_REG_READ_AND_UPDATE) { + u32 tmp; + /* Some rare cases 2 different devices can + * make i2c accesses to same physical i2c address, + * those read modify writes must be protected by static + * mutex + */ + if (sensor->sensor_ds->i2c_mutex_in_use) + mutex_lock(&crl_i2c_mutex); + + ret = crlmodule_i2c_read(sensor, dev_i2c_addr, reg, + len & CRL_REG_LEN_READ_MASK, &val2); + if (ret) { + if (sensor->sensor_ds->i2c_mutex_in_use) + mutex_unlock(&crl_i2c_mutex); + return ret; + } + + tmp = val2 & ~mask; + tmp |= val & mask; + val = tmp; + } + + ret = crlmodule_i2c_write(sensor, dev_i2c_addr, reg, + len & CRL_REG_LEN_READ_MASK, val); + + if ((sensor->sensor_ds->i2c_mutex_in_use) + && (len & CRL_REG_READ_AND_UPDATE)) + mutex_unlock(&crl_i2c_mutex); + + if (ret < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + ret, reg, val); + return ret; + } + + return 0; +} + +int crlmodule_write_regs(struct crl_sensor *sensor, + const struct crl_register_write_rep *regs, int len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = crlmodule_write_reg(sensor, + regs[i].dev_i2c_addr, + regs[i].address, + regs[i].len, + regs[i].mask, + regs[i].val); + if (ret < 0) + return ret; + }; + + return 0; +} + +int crlmodule_block_read(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 addr, + u8 addr_mode, u16 len, u8 *buf) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg[2]; + u8 data[2]; + u16 offset = 0; + int r; + + memset(msg, 0, sizeof(msg)); + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) { + msg[0].addr = client->addr; + msg[1].addr = client->addr; + } else { + msg[0].addr = dev_i2c_addr; + msg[1].addr = dev_i2c_addr; + } + + if (addr_mode & CRL_NVM_ADDR_MODE_8BIT) + msg[0].len = 1; + else if (addr_mode & CRL_NVM_ADDR_MODE_16BIT) + msg[0].len = 2; + else + return -EINVAL; + + msg[0].flags = 0; + msg[1].flags = I2C_M_RD; + + while (offset < len) { + if (addr_mode & CRL_NVM_ADDR_MODE_8BIT) { + data[0] = addr & 0xff; + } else { + data[0] = (addr >> 8) & 0xff; + data[1] = addr & 0xff; + } + + msg[0].buf = data; + msg[1].len = min(CRLMODULE_I2C_BLOCK_SIZE, len - offset); + msg[1].buf = &buf[offset]; + r = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (r != ARRAY_SIZE(msg)) { + if (r >= 0) + r = -EIO; + goto err; + } + addr += msg[1].len; + offset += msg[1].len; + } + return 0; +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + return r; +} diff --git a/drivers/media/i2c/crlmodule/crlmodule-regs.h b/drivers/media/i2c/crlmodule/crlmodule-regs.h new file mode 100644 index 000000000000..6d84486e1ae1 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-regs.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_REGS_H_ +#define __CRLMODULE_REGS_H_ + +struct crl_sensor; +struct crl_register_read_rep; +struct crl_register_write_rep; + +#define CRLMODULE_I2C_BLOCK_SIZE 0x20 + +int crlmodule_read_reg(struct crl_sensor *sensor, + const struct crl_register_read_rep reg, u32 *val); +int crlmodule_write_regs(struct crl_sensor *sensor, + const struct crl_register_write_rep *regs, int len); +int crlmodule_write_reg(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 reg, + u8 len, u32 mask, u32 val); +int crlmodule_block_read(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 addr, + u8 addr_mode, u16 len, u8 *buf); + +#endif /* __CRLMODULE_REGS_H_ */ diff --git a/drivers/media/i2c/crlmodule/crlmodule-sensor-ds.h b/drivers/media/i2c/crlmodule/crlmodule-sensor-ds.h new file mode 100644 index 000000000000..ff03185b1025 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule-sensor-ds.h @@ -0,0 +1,622 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_SENSOR_DS_H_ +#define __CRLMODULE_SENSOR_DS_H_ + +#include +#include "crlmodule.h" + +#define CRL_REG_LEN_08BIT 1 +#define CRL_REG_LEN_16BIT 2 +#define CRL_REG_LEN_24BIT 3 +#define CRL_REG_LEN_32BIT 4 + +#define CRL_REG_READ_AND_UPDATE (1 << 3) +#define CRL_REG_LEN_READ_MASK 0x07 +#define CRL_REG_LEN_DELAY 0x10 + +#define CRL_FLIP_DEFAULT_NONE 0 +#define CRL_FLIP_HFLIP 1 +#define CRL_FLIP_VFLIP 2 +#define CRL_FLIP_HFLIP_VFLIP 3 + +#define CRL_FLIP_HFLIP_MASK 0xfe +#define CRL_FLIP_VFLIP_MASK 0xfd + +#define CRL_PIXEL_ORDER_GRBG 0 +#define CRL_PIXEL_ORDER_RGGB 1 +#define CRL_PIXEL_ORDER_BGGR 2 +#define CRL_PIXEL_ORDER_GBRG 3 +#define CRL_PIXEL_ORDER_IGNORE 255 + +/* Flag to notify configuration selction imact from V4l2 Ctrls */ +#define CRL_IMPACTS_NO_IMPACT 0 +#define CRL_IMPACTS_PLL_SELECTION (1 << 1) +#define CRL_IMPACTS_MODE_SELECTION (1 << 2) + +/* + * In crl_dynamic_entity::entity_type is denoted by bits 6 and 7 + * 0 -> crl_dynamic_entity:entity_value is a constant + * 1 -> crl_dynamic_entity:entity_value is a referene to variable + * 2 -> crl_dynamic_entity:entity_value is a v4l2_ctrl value + * 3 -> crl_dynamic_entity:entity_value is a 8 bit register address + */ +enum crl_dynamic_entity_type { + CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST = 0, + CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL, /* Only 8bit registers */ +}; + +/* + * For some combo device which has some devices inside itself with different + * i2c address, adding flag to specify whether current device needs i2c + * address override. + * For back-compatibility, making flag equals 0. So existing sensor configure + * doesn't need to be modified. + */ +#define CRL_I2C_ADDRESS_NO_OVERRIDE 0 + +struct crl_sensor; +struct i2c_client; + +enum crl_subdev_type { + CRL_SUBDEV_TYPE_SCALER, + CRL_SUBDEV_TYPE_BINNER, + CRL_SUBDEV_TYPE_PIXEL_ARRAY, +}; + +enum crl_v4l2ctrl_op_type { + CRL_V4L2_CTRL_SET_OP, + CRL_V4L2_CTRL_GET_OP, +}; + +enum crl_v4l2ctrl_update_context { + SENSOR_IDLE, /* Powered on. But not streamind */ + SENSOR_STREAMING, /* Sensor streaming */ + SENSOR_POWERED_ON, /* streaming or idle */ +}; + +enum crl_operators { + CRL_BITWISE_AND = 0, + CRL_BITWISE_OR, + CRL_BITWISE_LSHIFT, + CRL_BITWISE_RSHIFT, + CRL_BITWISE_XOR, + CRL_BITWISE_COMPLEMENT, + CRL_ADD, + CRL_SUBTRACT, + CRL_MULTIPLY, + CRL_DIV, + CRL_ASSIGNMENT, +}; + +/* Replicated from videodev2.h */ +enum crl_v4l2_ctrl_type { + CRL_V4L2_CTRL_TYPE_INTEGER = 1, + CRL_V4L2_CTRL_TYPE_BOOLEAN, + CRL_V4L2_CTRL_TYPE_MENU_INT, + CRL_V4L2_CTRL_TYPE_MENU_ITEMS, + CRL_V4L2_CTRL_TYPE_BUTTON, + CRL_V4L2_CTRL_TYPE_INTEGER64, + CRL_V4L2_CTRL_TYPE_CTRL_CLASS, + CRL_V4L2_CTRL_TYPE_CUSTOM, +}; + +enum crl_addr_len { + CRL_ADDR_16BIT = 0, + CRL_ADDR_8BIT, + CRL_ADDR_7BIT, +}; + +enum crl_operands { + CRL_CONSTANT = 0, + CRL_VARIABLE, + CRL_CONTROL, +}; + +/* References to the CRL driver member variables */ +enum crl_member_data_reference_ids { + CRL_VAR_REF_OUTPUT_WIDTH = 1, + CRL_VAR_REF_OUTPUT_HEIGHT, + CRL_VAR_REF_PA_CROP_WIDTH, + CRL_VAR_REF_PA_CROP_HEIGHT, + CRL_VAR_REF_FRAME_TIMING_WIDTH, + CRL_VAR_REF_FRAME_TIMING_HEIGHT, + CRL_VAR_REF_BINNER_WIDTH, + CRL_VAR_REF_BINNER_HEIGHT, + CRL_VAR_REF_H_BINN_FACTOR, + CRL_VAR_REF_V_BINN_FACTOR, + CRL_VAR_REF_SCALE_FACTOR, + CRL_VAR_REF_BITSPERPIXEL, + CRL_VAR_REF_PIXELRATE_PA, + CRL_VAR_REF_PIXELRATE_CSI, + CRL_VAR_REF_PIXELRATE_LINK_FREQ, +}; + +enum crl_frame_desc_type { + CRL_V4L2_MBUS_FRAME_DESC_TYPE_PLATFORM, + CRL_V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL, + CRL_V4L2_MBUS_FRAME_DESC_TYPE_CCP2, + CRL_V4L2_MBUS_FRAME_DESC_TYPE_CSI2, +}; + +enum crl_pwr_ent_type { + CRL_POWER_ETY_GPIO_FROM_PDATA = 1, + CRL_POWER_ETY_GPIO_FROM_PDATA_BY_NUMBER, + CRL_POWER_ETY_GPIO_CUSTOM, + CRL_POWER_ETY_REGULATOR_FRAMEWORK, + CRL_POWER_ETY_CLK_FRAMEWORK, +}; + +struct crl_dynamic_entity { + enum crl_dynamic_entity_type entity_type; + u32 entity_val; +}; + +struct crl_arithmetic_ops { + enum crl_operators op; + struct crl_dynamic_entity operand; +}; + +struct crl_dynamic_calculated_entity { + u8 ops_items; + struct crl_arithmetic_ops *ops; +}; + +struct crl_register_write_rep { + u16 address; + u8 len; + u32 val; + u16 dev_i2c_addr; + u32 mask; +}; + +struct crl_register_read_rep { + u16 address; + u8 len; + u32 mask; + u16 dev_i2c_addr; +}; + +/* + * crl_dynamic_register_access is used mainly in the v4l2_ctrl context. + * This is intended to provide some generic arithmetic operations on the values + * to be written to a control's register or on the values read from a register. + * These arithmetic operations are controlled using struct crl_arithmetic_ops. + * + * One important information is that this structure behave differently for the + * set controls and volatile get controls. + * + * For the set control operation, the usage of the members are straight forward. + * The set control can result into multiple register write operations. Hence + * there can be more than one crl_dynamic_register_access entries associated + * with a control which results into separate register writes. + * + * But for the volatile get control operation, where a v4l2 control is used + * to query read only information from the sensor, there could be only one + * crl_dynamic_register_access entry. Because the result of a get control is + * a single value. crl_dynamic_register_access.address, len and mask values are + * not used in volatile get control context. Instead all the needed information + * must be encoded into member -> ops (struct crl_arithmetic_ops) + */ +struct crl_dynamic_register_access { + u16 address; + u8 len; + u32 mask; + u8 ops_items; + struct crl_arithmetic_ops *ops; + u16 dev_i2c_addr; +}; + +struct crl_sensor_detect_config { + struct crl_register_read_rep reg; /* Register to read */ + unsigned int width; /* width of the value in chars*/ +}; + +struct crl_sensor_subdev_config { + enum crl_subdev_type subdev_type; + char name[32]; +}; + +/* + * The ctrl id value pair which should be compared when selecting a + * configuration. This gives flexibility to provide any data through set ctrl + * and provide selection mechanism for a particular configuration + */ +struct crl_ctrl_data_pair { + u32 ctrl_id; + u32 data; +}; + +enum crl_dep_ctrl_action_type { + CRL_DEP_CTRL_ACTION_TYPE_SELF = 0, + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL, +}; + +enum crl_dep_ctrl_condition { + CRL_DEP_CTRL_CONDITION_GREATER = 0, + CRL_DEP_CTRL_CONDITION_LESSER, + CRL_DEP_CTRL_CONDITION_EQUAL, +}; + +enum crl_dep_ctrl_action { + CRL_DEP_CTRL_CONDITION_ADD = 0, + CRL_DEP_CTRL_CONDITION_SUBTRACT, + CRL_DEP_CTRL_CONDITION_MULTIPLY, + CRL_DEP_CTRL_CONDITION_DIVIDE, +}; + +struct crl_dep_ctrl_cond_action { + enum crl_dep_ctrl_condition cond; + u32 cond_value; + enum crl_dep_ctrl_action action; + u32 action_value; +}; + +/* Dependency control provision */ +struct crl_dep_ctrl_provision { + u32 ctrl_id; + enum crl_dep_ctrl_action_type action_type; + unsigned int action_items; + struct crl_dep_ctrl_cond_action *action; +}; + +/* + * Multiple set of register lists can be written to + * the sensor configuration based on the control's value + * struct crl_dep_reg_list introduces a provision for this + * purpose. + * + * struct crl_dep_reg_list *dep_regs; + * + * In dep_regs, a "condition" and "value" is added which is + * compared with ctrl->val and the register list that is to + * be written to the sensor. + * + * Example: For a v4l2_ctrl, if we need to set + * reg_list A when ctrl->val > 60 + * reg_list B when ctrl->val < 60 + * and reg_list C when ctrl->val == 60 + * + * So dep_regs block should be like this in the sensor + * specific configuration file: + * + * dep_regs = { + * { + * reg_condition = CRL_DEP_CTRL_CONDITION_GREATER, + * cond_value = { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 60 }, + * no_direct_regs = sizeof(X) + * direct_regs = X + * no_dyn_items = sizeof(A) + * dyn_regs = A + * }, + * { + * reg_condition = CRL_DEP_CTRL_CONDITION_LESSER, + * cond_value = { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 60 }, + * no_direct_regs = 0 + * direct_regs = 0 + * no_dyn_items = sizeof(B) + * dyn_regs = B + * }, + * { + * reg_condition = CRL_DEP_CTRL_CONDITION_EQUAL, + * cond_value = { CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST, 60 }, + * no_direct_regs = sizeof(Z) + * direct_regs = Z + * no_dyn_items = size(C) + * dyn_regs = C + * }, + * } + * cond_value is defined as dynamic entity, which can be a constant, + * another control value or a reference to the pre-defined set of variables + * or a register value. + * + * CRL driver will execute the above dep_regs in the same order + * as it is written. care must be taken for eample in the cases + * like, ctrl->val > 60, reg_list A. and if ctrl_val > 80, + * reg_list D etc. + */ + +struct crl_dep_reg_list { + enum crl_dep_ctrl_condition reg_cond; + struct crl_dynamic_entity cond_value; + unsigned int no_direct_regs; + struct crl_register_write_rep *direct_regs; + unsigned int no_dyn_items; + struct crl_dynamic_register_access *dyn_regs; +}; + +struct crl_sensor_limits { + unsigned int x_addr_max; + unsigned int y_addr_max; + unsigned int x_addr_min; + unsigned int y_addr_min; + unsigned int min_frame_length_lines; + unsigned int max_frame_length_lines; + unsigned int min_line_length_pixels; + unsigned int max_line_length_pixels; + u8 scaler_m_min; + u8 scaler_m_max; + u8 scaler_n_min; + u8 scaler_n_max; + u8 min_even_inc; + u8 max_even_inc; + u8 min_odd_inc; + u8 max_odd_inc; +}; + +struct crl_v4l2_ctrl_data_std { + s64 min; + s64 max; + u64 step; + s64 def; +}; + +struct crl_v4l2_ctrl_data_menu_items { + const char *const *menu; + unsigned int size; +}; + +struct crl_v4l2_ctrl_data_std_menu { + const int64_t *std_menu; + unsigned int size; +}; + +struct crl_v4l2_ctrl_data_int_menu { + const s64 *menu; + s64 max; + s64 def; +}; + +union crl_v4l2_ctrl_data_types { + struct crl_v4l2_ctrl_data_std std_data; + struct crl_v4l2_ctrl_data_menu_items v4l2_menu_items; + struct crl_v4l2_ctrl_data_std_menu v4l2_std_menu; + struct crl_v4l2_ctrl_data_int_menu v4l2_int_menu; +}; + +/* + * Please note a difference in the usage of "regs" member in case of a + * volatile get control for read only purpose. Please check the + * "struct crl_dynamic_register_access" declaration comments for more details. + * + * Read only controls must have "flags" V4L2_CTRL_FLAG_READ_ONLY set. + */ +struct crl_v4l2_ctrl { + enum crl_subdev_type sd_type; + enum crl_v4l2ctrl_op_type op_type; + enum crl_v4l2ctrl_update_context context; + char name[32]; + u32 ctrl_id; + enum crl_v4l2_ctrl_type type; + union crl_v4l2_ctrl_data_types data; + unsigned long flags; + u32 impact; /* If this control impact any config selection */ + struct v4l2_ctrl *ctrl; + unsigned int regs_items; + struct crl_dynamic_register_access *regs; + unsigned int dep_items; + struct crl_dep_ctrl_provision *dep_ctrls; + enum v4l2_ctrl_type v4l2_type; + unsigned int crl_ctrl_dep_reg_list; /* contains no. of dep_regs */ + struct crl_dep_reg_list *dep_regs; +}; + +struct crl_pll_configuration { + s64 input_clk; + s64 op_sys_clk; + u8 bitsperpixel; + u32 pixel_rate_csi; + u32 pixel_rate_pa; + u8 csi_lanes; + unsigned int comp_items; + struct crl_ctrl_data_pair *ctrl_data; + unsigned int pll_regs_items; + const struct crl_register_write_rep *pll_regs; +}; + +struct crl_subdev_rect_rep { + enum crl_subdev_type subdev_type; + struct v4l2_rect in_rect; + struct v4l2_rect out_rect; +}; + +struct crl_mode_rep { + unsigned int sd_rects_items; + const struct crl_subdev_rect_rep *sd_rects; + u8 binn_hor; + u8 binn_vert; + u8 scale_m; + s32 width; + s32 height; + unsigned int comp_items; + struct crl_ctrl_data_pair *ctrl_data; + unsigned int mode_regs_items; + const struct crl_register_write_rep *mode_regs; + + /* + * Minimum and maximum value for line length pixels and frame length + * lines are added for modes. This facilitates easy handling of + * modes which binning skipping and affects the calculation of + * vblank and hblank values. + * + * The blank values are limited based on the following logic + * + * If mode specific limits are available + * vblank = clamp(min_llp - PA_width, max_llp - PA_width) + * hblank = clamp(min_fll - PA_Height, max_fll - PA_Height + * + * If mode specific blanking limits are not available, then the sensor + * limits will be used in the same manner. + * + * If sensor mode limits are not available, then the values will be + * written directly to the associated control registers. + */ + s32 min_llp; /* minimum/maximum value for line length pixels */ + s32 max_llp; + s32 min_fll; + s32 max_fll; /* minimum/maximum value for frame length lines */ +}; + +struct crl_csi_data_fmt { + u32 code; + u8 pixel_order; + u8 bits_per_pixel; + unsigned int regs_items; + const struct crl_register_write_rep *regs; +}; + +struct crl_flip_data { + u8 flip; + u8 pixel_order; +}; + +struct crl_power_seq_entity { + enum crl_pwr_ent_type type; + char ent_name[12]; + int ent_number; + u16 address; + unsigned int val; + unsigned int undo_val; /* Undo value if any previous step failed */ + unsigned int delay; /* delay in micro seconds */ + struct regulator *regulator_priv; /* R/W */ + struct gpio_desc *gpiod_priv; +}; + +struct crl_nvm_blob { + u8 dev_addr; + u16 start_addr; + u16 size; +}; + +struct crl_nvm { + unsigned int nvm_preop_regs_items; + const struct crl_register_write_rep *nvm_preop_regs; + + unsigned int nvm_postop_regs_items; + const struct crl_register_write_rep *nvm_postop_regs; + + unsigned int nvm_blobs_items; + struct crl_nvm_blob *nvm_config; + u32 nvm_flags; +}; + +/* Representation for v4l2_mbus_frame_desc_entry */ +struct crl_frame_desc { + struct crl_dynamic_entity flags; + struct crl_dynamic_entity bpp; + struct crl_dynamic_entity pixelcode; + struct crl_dynamic_entity start_line; + struct crl_dynamic_entity start_pixel; + struct crl_dynamic_calculated_entity width; + struct crl_dynamic_calculated_entity height; + struct crl_dynamic_entity length; + struct crl_dynamic_entity csi2_channel; + struct crl_dynamic_entity csi2_data_type; +}; + +typedef int (*sensor_specific_init)(struct i2c_client *); +typedef int (*sensor_specific_cleanup)(struct i2c_client *); + +struct crl_sensor_configuration { + + const struct crl_clock_entity *clock_entity; + + const unsigned int power_items; + const struct crl_power_seq_entity *power_entities; + const unsigned int power_delay; /* in micro seconds */ + + const unsigned int onetime_init_regs_items; + const struct crl_register_write_rep *onetime_init_regs; + + const unsigned int powerup_regs_items; + const struct crl_register_write_rep *powerup_regs; + + const unsigned int poweroff_regs_items; + const struct crl_register_write_rep *poweroff_regs; + + const unsigned int id_reg_items; + const struct crl_sensor_detect_config *id_regs; + + const unsigned int subdev_items; + const struct crl_sensor_subdev_config *subdevs; + + const struct crl_sensor_limits *sensor_limits; + + const unsigned int pll_config_items; + const struct crl_pll_configuration *pll_configs; + + const unsigned int modes_items; + const struct crl_mode_rep *modes; + /* + * Fail safe mode should be the largest resolution available in the + * mode list. If none of the mode parameters are matched, the driver + * will select this mode for streaming. + */ + const unsigned int fail_safe_mode_index; + + const unsigned int streamon_regs_items; + const struct crl_register_write_rep *streamon_regs; + + const unsigned int streamoff_regs_items; + const struct crl_register_write_rep *streamoff_regs; + + const unsigned int v4l2_ctrls_items; + const struct crl_v4l2_ctrl *v4l2_ctrl_bank; + + const unsigned int csi_fmts_items; + const struct crl_csi_data_fmt *csi_fmts; + + const unsigned int flip_items; + const struct crl_flip_data *flip_data; + + struct crl_nvm crl_nvm_info; + + enum crl_addr_len addr_len; + + unsigned int frame_desc_entries; + enum crl_frame_desc_type frame_desc_type; + struct crl_frame_desc *frame_desc; + char *msr_file_name; + + sensor_specific_init sensor_init; + sensor_specific_cleanup sensor_cleanup; + /* + * Irq handlers for threaded irq. These are needed if driver need to + * handle gpio interrupt. crl_threaded_irq_fn is then mandatory. Irq + * pin configuration is in platform data. + */ + irqreturn_t (*crl_irq_fn)(int irq, void *sensor_struct); + irqreturn_t (*crl_threaded_irq_fn)(int irq, void *sensor_struct); + const bool irq_in_use; + const bool i2c_mutex_in_use; +}; + +struct crlmodule_sensors { + char *pname; + char *name; + struct crl_sensor_configuration *ds; +}; + +/* + * Function to populate the CRL data structure from the sensor configuration + * definition file + */ +int crlmodule_populate_ds(struct crl_sensor *sensor, struct device *dev); + +/* + * Function validate the contents CRL data structure to check if all the + * required fields are filled and are according to the limits. + */ +int crlmodule_validate_ds(struct crl_sensor *sensor); + +/* Function to free all resources allocated for the CRL data structure */ +void crlmodule_release_ds(struct crl_sensor *sensor); + +#endif /* __CRLMODULE_SENSOR_DS_H_ */ diff --git a/drivers/media/i2c/crlmodule/crlmodule.h b/drivers/media/i2c/crlmodule/crlmodule.h new file mode 100644 index 000000000000..e68e82fd0634 --- /dev/null +++ b/drivers/media/i2c/crlmodule/crlmodule.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * + * Author: Vinod Govindapillai + * + */ + +#ifndef __CRLMODULE_PRIV_H_ +#define __CRLMODULE_PRIV_H_ + +#include +#include +#include +#include +#include "../../../../include/media/crlmodule.h" +#include +#include +#include "../../../../include/uapi/linux/crlmodule.h" +#include "crlmodule-sensor-ds.h" + +#define CRL_SUBDEVS 3 + +#define CRL_PA_PAD_SRC 0 +#define CRL_PAD_SINK 0 +#define CRL_PAD_SRC 1 +#define CRL_PADS 2 + +struct crl_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct crl_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; + unsigned int field; + unsigned int *route_flags; +}; + +struct crl_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power mutex became necessity because of the v4l2_ctrl_handler_setup + * is being called from power on function which needs to be serialised + * but v4l2_ctrl_handler setup uses "mutex" so it cannot be used. + */ + struct mutex power_mutex; + + struct crl_subdev ssds[CRL_SUBDEVS]; + u32 ssds_used; + struct crl_subdev *src; + struct crl_subdev *binner; + struct crl_subdev *scaler; + struct crl_subdev *pixel_array; + + struct crlmodule_platform_data *platform_data; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 sensor_mode; + u8 scale_m; + u8 fmt_index; + u8 flip_info; + u8 pll_index; + + + int power_count; + + bool streaming; + + struct crl_sensor_configuration *sensor_ds; + struct crl_v4l2_ctrl *v4l2_ctrl_bank; + + /* These are mandatory controls. So good to have reference to these */ + struct v4l2_ctrl *pixel_rate_pa; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; + + s64 *link_freq_menu; + + /* If extra v4l2 contrl has an impact on PLL selection */ + bool ext_ctrl_impacts_pll_selection; + bool ext_ctrl_impacts_mode_selection; + bool blanking_ctrl_not_use; + bool direct_mode_in_use; + const struct crl_mode_rep *current_mode; + + struct clk *xclk; + struct crl_power_seq_entity *pwr_entity; + unsigned int irq; + + u8 *nvm_data; + u16 nvm_size; + + /* Pointer to binary file which contains + * tunable IQ parameters like NR, DPC, BLC + * Not all MSR's are moved to the binary + * at the moment. + */ + const struct firmware *msr_list; + /* + * Pointer to store sensor specific data structure, that + * can be used for example in interrupt specific code. + */ + void *sensor_specific_data; +}; + +#define to_crlmodule_subdev(_sd) \ + container_of(_sd, struct crl_subdev, sd) + +#define to_crlmodule_sensor(_sd) \ + (to_crlmodule_subdev(_sd)->sensor) + +#endif /* __CRLMODULE_PRIV_H_ */ diff --git a/include/media/crlmodule.h b/include/media/crlmodule.h new file mode 100644 index 000000000000..ead1beba73f2 --- /dev/null +++ b/include/media/crlmodule.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation + * Generic driver for common register list based camera sensor modules + * + */ + +#ifndef __CRLMODULE_H +#define __CRLMODULE_H + +#include + +#define CRLMODULE_NAME "crlmodule" + +#define CRL_MAX_CUSTOM_GPIO_AMOUNT 3 + +struct crl_custom_gpio { + char name[16]; + int number; + unsigned int val; + unsigned int undo_val; +}; + +struct crlmodule_platform_data { + unsigned short i2c_addr; + unsigned short i2c_adapter; + + unsigned int ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + const s64 *op_sys_clock; + + int xshutdown; /* gpio */ + struct crl_custom_gpio custom_gpio[CRL_MAX_CUSTOM_GPIO_AMOUNT]; + char module_name[16]; /* module name from ACPI */ + int crl_irq_pin; + unsigned int irq_pin_flags; + char irq_pin_name[16]; + const char *id_string; + char suffix; /* suffix to identify multi sensors, abcd.. */ + unsigned int high_framevalid_flags; /* high framevaild flags*/ +}; + +#endif /* __CRLMODULE_H */ diff --git a/include/uapi/linux/crlmodule.h b/include/uapi/linux/crlmodule.h new file mode 100644 index 000000000000..8bcb8c92830f --- /dev/null +++ b/include/uapi/linux/crlmodule.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef UAPI_LINUX_CRLMODULE_H +#define UAPI_LINUX_CRLMODULE_H + +#define REGS_BUF_SIZE 32 +/** + * struct crl_registers_info - the information for register. + * @start_address: sensor i2c start address. + * @number: the number of registers to be read/write. + * @len: register length, 1:8bit, 2:16bit, 3:24bit, 4:32bit. + * @regs: the array of registers. + */ +struct crl_registers_info { + unsigned int start_address; + unsigned int number; + unsigned int len; + unsigned int regs[REGS_BUF_SIZE]; +}; + +#define CRL_G_REGISTERS _IOWR('C', 1, struct crl_registers_info) +#define CRL_S_REGISTERS _IOW('C', 2, struct crl_registers_info) + +#define V4L2_CID_CRLMODULE_BASE (V4L2_CID_USER_BASE + 0x2050) + +#define V4L2_CID_FRAME_LENGTH_LINES (V4L2_CID_CRLMODULE_BASE + 1) +#define V4L2_CID_LINE_LENGTH_PIXELS (V4L2_CID_CRLMODULE_BASE + 2) +#define CRL_CID_SENSOR_THERMAL_DATA (V4L2_CID_CRLMODULE_BASE + 3) + +/* + * Select sensor mode directly, driver programs media pad + * formats as in configuration file + */ +#define CRL_CID_SENSOR_MODE (V4L2_CID_CRLMODULE_BASE + 4) + +/* IMX230 HDR specific controls */ +#define CRL_CID_IMX230_HDR_MODE (V4L2_CID_CRLMODULE_BASE + 5) +#define CRL_CID_IMX230_HDR_ET_RATIO (V4L2_CID_CRLMODULE_BASE + 6) + +/* Set multi-exposure frame in HDR with different exposure value */ +#define CRL_CID_EXPOSURE_SHS1 (V4L2_CID_CRLMODULE_BASE + 8) +#define CRL_CID_EXPOSURE_SHS2 (V4L2_CID_CRLMODULE_BASE + 9) +#define CRL_CID_EXPOSURE_SHS3 (V4L2_CID_CRLMODULE_BASE + 10) +#define CRL_CID_EXPOSURE_RHS1 (V4L2_CID_CRLMODULE_BASE + 11) +#define CRL_CID_EXPOSURE_RHS2 (V4L2_CID_CRLMODULE_BASE + 12) + +/* Switch to enable/disable PDAF settings */ +#define CRL_CID_SENSOR_PDAF (V4L2_CID_CRLMODULE_BASE + 13) + +/* Set multi-digital gain */ +#define CRL_CID_DIGITAL_GAIN_L (V4L2_CID_CRLMODULE_BASE + 14) +#define CRL_CID_DIGITAL_GAIN_S (V4L2_CID_CRLMODULE_BASE + 15) +#define CRL_CID_DIGITAL_GAIN_VS (V4L2_CID_CRLMODULE_BASE + 16) + +/* Get sensor bit linear */ +#define CRL_CID_SENSOR_BIT_LINEAR (V4L2_CID_CRLMODULE_BASE + 17) + +/* set sensor msb align*/ +#define CRL_CID_MSB_ALIGN (V4L2_CID_CRLMODULE_BASE + 18) + +/* enable/disable auto exposure */ +#define CRL_CID_AUTO_EXPOSURE_DEBUG (V4L2_CID_CRLMODULE_BASE + 19) + +/* set analog gain for HDR frames */ +#define CRL_CID_ANALOG_GAIN_L (V4L2_CID_CRLMODULE_BASE + 20) +#define CRL_CID_ANALOG_GAIN_S (V4L2_CID_CRLMODULE_BASE + 21) +#define CRL_CID_ANALOG_GAIN_VS (V4L2_CID_CRLMODULE_BASE + 22) + +/* Set exposure mode: Linear mode or 2-/3-/4-HDR mode */ +#define CRL_CID_EXPOSURE_MODE (V4L2_CID_CRLMODULE_BASE + 23) + +/* Set HDR mode exposure ratio */ +#define CRL_CID_EXPOSURE_HDR_RATIO (V4L2_CID_CRLMODULE_BASE + 24) + +#endif /* UAPI_LINUX_CRLMODULE_H */ From c6dd6dff1bff24122a95a373f92d8da33d53ad5d Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:48 +0800 Subject: [PATCH 1142/1276] media: i2c: add TI964/913 SER/DES driver TI964 is a versatile camera hub capable of connecting serialized camera data received from 4 independent video datastreams via an FPD-Link III interface. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/i2c/Kconfig | 6 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ti964-reg.h | 128 +++ drivers/media/i2c/ti964.c | 1368 +++++++++++++++++++++++++++++++++ include/media/ti964.h | 59 ++ 5 files changed, 1562 insertions(+) create mode 100644 drivers/media/i2c/ti964-reg.h create mode 100644 drivers/media/i2c/ti964.c create mode 100644 include/media/ti964.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 304ee02e2497..2681d2bc74ff 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1066,6 +1066,12 @@ config VIDEO_I2C To compile this driver as a module, choose M here: the module will be called video-i2c +config VIDEO_TI964 + tristate "TI964 driver support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a driver for TI964 camera. + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index db6e8021b947..e0d41d364c45 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -112,3 +112,4 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_SDR_MAX2175) += max2175.o obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule/ +obj-$(CONFIG_VIDEO_TI964) += ti964.o diff --git a/drivers/media/i2c/ti964-reg.h b/drivers/media/i2c/ti964-reg.h new file mode 100644 index 000000000000..e916c41b74a1 --- /dev/null +++ b/drivers/media/i2c/ti964-reg.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef TI964_REG_H +#define TI964_REG_H + +struct ti964_register_write { + u8 reg; + u8 val; +}; + +static const struct ti964_register_write ti964_frame_sync_settings[2][5] = { + { + {0x18, 0x00}, /* Disable frame sync. */ + {0x19, 0x00}, + {0x1a, 0x02}, + {0x1b, 0x0a}, + {0x1c, 0xd3}, + }, + { + {0x19, 0x01}, /* Frame sync high time.*/ + {0x1a, 0x15}, + {0x1b, 0x09}, /* Frame sync low time. */ + {0x1c, 0xC3}, + {0x18, 0x01}, /* Enable frame sync. and use high/low mode */ + } +}; + +static const struct ti964_register_write ti964_init_settings[] = { + {0x8, 0x1c}, + {0xa, 0x79}, + {0xb, 0x79}, + {0xd, 0xb9}, + {0x10, 0x91}, + {0x11, 0x85}, + {0x12, 0x89}, + {0x13, 0xc1}, + {0x17, 0xe1}, + {0x18, 0x0}, /* Disable frame sync. */ + {0x19, 0x0}, /* Frame sync high time. */ + {0x1a, 0x2}, + {0x1b, 0xa}, /* Frame sync low time. */ + {0x1c, 0xd3}, + {0x21, 0x43}, /* Enable best effort mode. */ + {0xb0, 0x10}, + {0xb1, 0x14}, + {0xb2, 0x1f}, + {0xb3, 0x8}, + {0x32, 0x1}, /* Select CSI port 0 */ + {0x4c, 0x1}, /* Select RX port 0 */ + {0x58, 0x58}, + {0x5c, 0x18}, /* TI913 alias addr 0xc */ + {0x6d, 0x7f}, + {0x70, 0x1e}, /* YUV422_8 */ + {0x7c, 0x81}, /* Use RAW10 8bit mode */ + {0xd2, 0x84}, + {0x4c, 0x12}, /* Select RX port 1 */ + {0x58, 0x58}, + {0x5c, 0x1a}, /* TI913 alias addr 0xd */ + {0x6d, 0x7f}, + {0x70, 0x5e}, /* YUV422_8 */ + {0x7c, 0x81}, /* Use RAW10 8bit mode */ + {0xd2, 0x84}, + {0x4c, 0x24}, /* Select RX port 2*/ + {0x58, 0x58}, + {0x5c, 0x1c}, /* TI913 alias addr 0xe */ + {0x6d, 0x7f}, + {0x70, 0x9e}, /* YUV422_8 */ + {0x7c, 0x81}, /* Use RAW10 8bit mode */ + {0xd2, 0x84}, + {0x4c, 0x38}, /* Select RX port3 */ + {0x58, 0x58}, + {0x5c, 0x1e}, /* TI913 alias addr 0xf */ + {0x6d, 0x7f}, + {0x70, 0xde}, /* YUV422_8 */ + {0x7c, 0x81}, /* Use RAW10 8bit mode */ + {0xd2, 0x84}, + {0xbc, 0x00}, +}; + +static const struct ti964_register_write ti964_tp_settings[] = { + {0xb0, 0x0}, + {0xb1, 0x02}, + {0xb2, 0xb3}, + {0xb1, 0x01}, +}; +/*register definition */ +#define TI964_DEVID 0x0 +#define TI964_RESET 0x1 +#define TI964_CSI_PLL_CTL 0x1f +#define TI964_FS_CTL 0x18 +#define TI964_FWD_CTL1 0x20 +#define TI964_RX_PORT_SEL 0x4c +#define TI964_SLAVE_ID0 0x5d +#define TI964_SLAVE_ALIAS_ID0 0x65 +#define TI964_PORT_CONFIG 0x6d +#define TI964_BC_GPIO_CTL0 0x6e +#define TI964_RAW10_ID 0x70 +#define TI964_RAW12_ID 0x71 +#define TI964_PORT_CONFIG2 0x7c + +#define TI964_IND_ACC_DATA 0xb2 +#define TI964_CSI_CTL 0x33 + +/* register value definition */ +#define TI964_POWER_ON 0x1 +#define TI964_POWER_OFF 0x20 +#define TI964_FPD3_RAW10_100MHz 0x7f +#define TI964_FPD3_RAW12_50MHz 0x7d +#define TI964_FPD3_RAW12_75MHz 0x7e +#define TI964_RAW12 0x41 +#define TI964_RAW10_NORMAL 0x1 +#define TI964_RAW10_8BIT 0x81 +#define TI964_GPIO0_HIGH 0x09 +#define TI964_GPIO0_LOW 0x08 +#define TI964_GPIO1_HIGH 0x90 +#define TI964_GPIO1_LOW 0x80 +#define TI964_GPIO0_FSIN 0x0a +#define TI964_GPIO1_FSIN 0xa0 +#define TI964_GPIO0_MASK 0x0f +#define TI964_GPIO1_MASK 0xf0 +#define TI964_MIPI_800MBPS 0x2 +#define TI964_MIPI_1600MBPS 0x0 +#define TI964_CSI_ENABLE 0x1 +#define TI964_CSI_CONTS_CLOCK 0x2 +#define TI964_CSI_SKEWCAL 0x40 +#define TI964_FSIN_ENABLE 0x1 +#endif diff --git a/drivers/media/i2c/ti964.c b/drivers/media/i2c/ti964.c new file mode 100644 index 000000000000..606eef257ca6 --- /dev/null +++ b/drivers/media/i2c/ti964.c @@ -0,0 +1,1368 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2016 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ti964-reg.h" + +struct ti964_subdev { + struct v4l2_subdev *sd; + unsigned short rx_port; + unsigned short fsin_gpio; + unsigned short phy_i2c_addr; + unsigned short alias_i2c_addr; + char sd_name[16]; +}; + +struct ti964 { + struct v4l2_subdev sd; + struct media_pad pad[NR_OF_TI964_PADS]; + struct v4l2_ctrl_handler ctrl_handler; + struct ti964_pdata *pdata; + struct ti964_subdev sub_devs[NR_OF_TI964_SINK_PADS]; + struct crlmodule_platform_data subdev_pdata[NR_OF_TI964_SINK_PADS]; + const char *name; + + struct mutex mutex; + + struct regmap *regmap8; + struct regmap *regmap16; + + struct v4l2_mbus_framefmt *ffmts[NR_OF_TI964_PADS]; + struct rect *crop; + struct rect *compose; + + struct { + unsigned int *stream_id; + } *stream; /* stream enable/disable status, indexed by pad */ + struct { + unsigned int sink; + unsigned int source; + int flags; + } *route; /* pad level info, indexed by stream */ + + unsigned int nsinks; + unsigned int nsources; + unsigned int nstreams; + unsigned int npads; + + struct gpio_chip gc; + + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; +}; + +#define to_ti964(_sd) container_of(_sd, struct ti964, sd) + +static const s64 ti964_op_sys_clock[] = {400000000, 800000000}; +static const u8 ti964_op_sys_clock_reg_val[] = { + TI964_MIPI_800MBPS, + TI964_MIPI_1600MBPS +}; + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct ti964_csi_data_format va_csi_data_formats[] = { + { MEDIA_BUS_FMT_YUYV8_1X16, 16, 16, PIXEL_ORDER_GBRG, 0x1e }, + { MEDIA_BUS_FMT_UYVY8_1X16, 16, 16, PIXEL_ORDER_GBRG, 0x1e }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, PIXEL_ORDER_GRBG, 0x2c }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, PIXEL_ORDER_RGGB, 0x2c }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, PIXEL_ORDER_BGGR, 0x2c }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, PIXEL_ORDER_GBRG, 0x2c }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, PIXEL_ORDER_GRBG, 0x2b }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, PIXEL_ORDER_RGGB, 0x2b }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, PIXEL_ORDER_BGGR, 0x2b }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, PIXEL_ORDER_GBRG, 0x2b }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, PIXEL_ORDER_GRBG, 0x2a }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, PIXEL_ORDER_RGGB, 0x2a }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, PIXEL_ORDER_BGGR, 0x2a }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, PIXEL_ORDER_GBRG, 0x2a }, +}; + +static const uint32_t ti964_supported_codes_pad[] = { + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +static const uint32_t *ti964_supported_codes[] = { + ti964_supported_codes_pad, +}; + +static struct regmap_config ti964_reg_config8 = { + .reg_bits = 8, + .val_bits = 8, +}; + +static struct regmap_config ti964_reg_config16 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int ti964_reg_set_bit(struct ti964 *va, unsigned char reg, + unsigned char bit, unsigned char val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(va->regmap8, reg, ®_val); + if (ret) + return ret; + if (val) + reg_val |= 1 << bit; + else + reg_val &= ~(1 << bit); + + return regmap_write(va->regmap8, reg, reg_val); +} + +static int ti964_map_phy_i2c_addr(struct ti964 *va, unsigned short rx_port, + unsigned short addr) +{ + int rval; + + rval = regmap_write(va->regmap8, TI964_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + return regmap_write(va->regmap8, TI964_SLAVE_ID0, addr); +} + +static int ti964_map_alias_i2c_addr(struct ti964 *va, unsigned short rx_port, + unsigned short addr) +{ + int rval; + + rval = regmap_write(va->regmap8, TI964_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + return regmap_write(va->regmap8, TI964_SLAVE_ALIAS_ID0, addr); +} + +static int ti964_fsin_gpio_init(struct ti964 *va, unsigned short rx_port, + unsigned short fsin_gpio) +{ + int rval; + int reg_val; + + rval = regmap_read(va->regmap8, TI964_FS_CTL, ®_val); + if (rval) { + dev_dbg(va->sd.dev, "Failed to read gpio status.\n"); + return rval; + } + + if (!reg_val & TI964_FSIN_ENABLE) { + dev_dbg(va->sd.dev, "FSIN not enabled, skip config FSIN GPIO.\n"); + return 0; + } + + rval = regmap_write(va->regmap8, TI964_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + rval = regmap_read(va->regmap8, TI964_BC_GPIO_CTL0, ®_val); + if (rval) { + dev_dbg(va->sd.dev, "Failed to read gpio status.\n"); + return rval; + } + + if (fsin_gpio == 0) { + reg_val &= ~TI964_GPIO0_MASK; + reg_val |= TI964_GPIO0_FSIN; + } else { + reg_val &= ~TI964_GPIO1_MASK; + reg_val |= TI964_GPIO1_FSIN; + } + + rval = regmap_write(va->regmap8, TI964_BC_GPIO_CTL0, reg_val); + if (rval) + dev_dbg(va->sd.dev, "Failed to set gpio.\n"); + + return rval; +} + +static int ti964_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ti964 *va = to_ti964(sd); + int i; + + for (i = 0; i < min(va->nstreams, route->num_routes); ++i) { + unsigned int sink = va->route[i].sink; + unsigned int source = va->route[i].source; + + route->routes[i].sink_pad = sink; + route->routes[i].sink_stream = + va->stream[sink].stream_id[0]; + route->routes[i].source_pad = source; + route->routes[i].source_stream = + va->stream[source].stream_id[sink]; + route->routes[i].flags = va->route[i].flags; + } + + route->num_routes = i; + + return 0; +} + +static int ti964_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ti964 *va = to_ti964(sd); + int i, j, ret = 0; + + for (i = 0; i < min(route->num_routes, va->nstreams); ++i) { + struct v4l2_subdev_route *t = &route->routes[i]; + unsigned int sink = t->sink_pad; + unsigned int source = t->source_pad; + + if (t->sink_stream > va->nstreams - 1 || + t->source_stream > va->nstreams - 1) + continue; + + if (t->source_pad != TI964_PAD_SOURCE) + continue; + + for (j = 0; j < va->nstreams; j++) { + if (sink == va->route[j].sink && + source == va->route[j].source) + break; + } + + if (j == va->nstreams) + continue; + + va->stream[sink].stream_id[0] = t->sink_stream; + va->stream[source].stream_id[sink] = t->source_stream; + + if (t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + va->route[j].flags |= + V4L2_SUBDEV_ROUTE_FL_ACTIVE; + else if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + va->route[j].flags &= + (~V4L2_SUBDEV_ROUTE_FL_ACTIVE); + } + + return ret; +} + +static int ti964_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ti964 *va = to_ti964(sd); + const uint32_t *supported_code = + ti964_supported_codes[code->pad]; + bool next_stream = false; + int i; + + if (code->stream & V4L2_SUBDEV_FLAG_NEXT_STREAM) { + next_stream = true; + code->stream &= ~V4L2_SUBDEV_FLAG_NEXT_STREAM; + } + + if (code->stream > va->nstreams) + return -EINVAL; + + if (next_stream) { + if (!(va->pad[code->pad].flags & MEDIA_PAD_FL_MULTIPLEX)) + return -EINVAL; + if (code->stream < va->nstreams - 1) { + code->stream++; + return 0; + } else { + return -EINVAL; + } + } + + for (i = 0; supported_code[i]; i++) { + if (i == code->index) { + code->code = supported_code[i]; + return 0; + } + } + + return -EINVAL; +} + +static const struct ti964_csi_data_format + *ti964_validate_csi_data_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(va_csi_data_formats); i++) { + if (va_csi_data_formats[i].code == code) + return &va_csi_data_formats[i]; + } + + return &va_csi_data_formats[0]; +} + +static int ti964_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) +{ + struct ti964 *va = to_ti964(sd); + struct v4l2_mbus_frame_desc_entry *entry = desc->entry; + u8 vc = 0; + int i; + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + desc->num_entries = min_t(int, va->nstreams, V4L2_FRAME_DESC_ENTRY_MAX); + + for (i = 0; i < desc->num_entries; i++) { + struct v4l2_mbus_framefmt *ffmt = + &va->ffmts[TI964_PAD_SOURCE][i]; + const struct ti964_csi_data_format *csi_format = + ti964_validate_csi_data_format(ffmt->code); + + entry->two_dim.width = ffmt->width; + entry->two_dim.height = ffmt->height; + entry->pixelcode = ffmt->code; + entry->bus.csi2.channel = vc++; + entry->bpp = csi_format->compressed; + entry++; + } + + return 0; +} + +static struct v4l2_mbus_framefmt * +__ti964_get_ffmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int which, + unsigned int stream) +{ + struct ti964 *va = to_ti964(subdev); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(subdev, cfg, pad); + else + return &va->ffmts[pad][stream]; +} + +static int ti964_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ti964 *va = to_ti964(subdev); + + if (fmt->stream > va->nstreams) + return -EINVAL; + + mutex_lock(&va->mutex); + fmt->format = *__ti964_get_ffmt(subdev, cfg, fmt->pad, + fmt->which, fmt->stream); + mutex_unlock(&va->mutex); + + dev_dbg(subdev->dev, "subdev_format: which: %s, pad: %d, stream: %d.\n", + fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE ? + "V4L2_SUBDEV_FORMAT_ACTIVE" : "V4L2_SUBDEV_FORMAT_TRY", + fmt->pad, fmt->stream); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + fmt->format.width, fmt->format.height, fmt->format.code); + + return 0; +} + +static int ti964_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ti964 *va = to_ti964(subdev); + const struct ti964_csi_data_format *csi_format; + struct v4l2_mbus_framefmt *ffmt; + + if (fmt->stream > va->nstreams) + return -EINVAL; + + csi_format = ti964_validate_csi_data_format( + fmt->format.code); + + mutex_lock(&va->mutex); + ffmt = __ti964_get_ffmt(subdev, cfg, fmt->pad, fmt->which, + fmt->stream); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = csi_format->code; + } + fmt->format = *ffmt; + mutex_unlock(&va->mutex); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + ffmt->width, ffmt->height, ffmt->code); + + return 0; +} + +static int ti964_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(subdev, fh->pad, 0); + + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = TI964_PAD_SOURCE, + .format = { + .width = TI964_MAX_WIDTH, + .height = TI964_MAX_HEIGHT, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + }, + .stream = 0, + }; + + *try_fmt = fmt.format; + + return 0; +} + +static int ti964_registered(struct v4l2_subdev *subdev) +{ + struct ti964 *va = to_ti964(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int i, j, k, l, rval; + + for (i = 0, k = 0; i < va->pdata->subdev_num; i++) { + struct ti964_subdev_info *info = + &va->pdata->subdev_info[i]; + struct crlmodule_platform_data *pdata = + (struct crlmodule_platform_data *) + info->board_info.platform_data; + + if (k >= va->nsinks) + break; + + /* + * The sensors should not share the same pdata structure. + * Clone the pdata for each sensor. + */ + memcpy(&va->subdev_pdata[k], pdata, sizeof(*pdata)); + if (va->subdev_pdata[k].xshutdown != 0 && + va->subdev_pdata[k].xshutdown != 1) { + dev_err(va->sd.dev, "xshutdown(%d) must be 0 or 1 to connect.\n", + va->subdev_pdata[k].xshutdown); + return -EINVAL; + } + + /* If 0 is xshutdown, then 1 would be FSIN, vice versa. */ + va->sub_devs[k].fsin_gpio = 1 - va->subdev_pdata[k].xshutdown; + + /* Spin sensor subdev suffix name */ + va->subdev_pdata[k].suffix = info->suffix; + + /* + * Change the gpio value to have xshutdown + * and rx port included, so in gpio_set those + * can be caculated from it. + */ + va->subdev_pdata[k].xshutdown += va->gc.base + + info->rx_port * NR_OF_GPIOS_PER_PORT; + info->board_info.platform_data = &va->subdev_pdata[k]; + + if (!info->phy_i2c_addr || !info->board_info.addr) { + dev_err(va->sd.dev, "can't find the physical and alias addr.\n"); + return -EINVAL; + } + + /* Map PHY I2C address. */ + rval = ti964_map_phy_i2c_addr(va, info->rx_port, + info->phy_i2c_addr); + if (rval) + return rval; + + /* Map 7bit ALIAS I2C address. */ + rval = ti964_map_alias_i2c_addr(va, info->rx_port, + info->board_info.addr << 1); + if (rval) + return rval; + + /* aggre and subdves share the same i2c bus */ + va->sub_devs[k].sd = v4l2_i2c_new_subdev_board( + va->sd.v4l2_dev, client->adapter, + &info->board_info, 0); + if (!va->sub_devs[k].sd) { + dev_err(va->sd.dev, + "can't create new i2c subdev %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + continue; + } + va->sub_devs[k].rx_port = info->rx_port; + va->sub_devs[k].phy_i2c_addr = info->phy_i2c_addr; + va->sub_devs[k].alias_i2c_addr = info->board_info.addr; + memcpy(va->sub_devs[k].sd_name, + va->subdev_pdata[k].module_name, + min(sizeof(va->sub_devs[k].sd_name) - 1, + sizeof(va->subdev_pdata[k].module_name) - 1)); + + for (j = 0; j < va->sub_devs[k].sd->entity.num_pads; j++) { + if (va->sub_devs[k].sd->entity.pads[j].flags & + MEDIA_PAD_FL_SOURCE) + break; + } + + if (j == va->sub_devs[k].sd->entity.num_pads) { + dev_warn(va->sd.dev, + "no source pad in subdev %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + return -ENOENT; + } + + for (l = 0; l < va->nsinks; l++) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_create_link( +#else + rval = media_create_pad_link( +#endif + &va->sub_devs[k].sd->entity, j, + &va->sd.entity, l, 0); + if (rval) { + dev_err(va->sd.dev, + "can't create link to %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + return -EINVAL; + } + } + k++; + } + + return 0; +} + +static int ti964_set_power(struct v4l2_subdev *subdev, int on) +{ + struct ti964 *va = to_ti964(subdev); + int ret; + u8 val; + + ret = regmap_write(va->regmap8, TI964_RESET, + (on) ? TI964_POWER_ON : TI964_POWER_OFF); + if (ret || !on) + return ret; + + /* Configure MIPI clock bsaed on control value. */ + ret = regmap_write(va->regmap8, TI964_CSI_PLL_CTL, + ti964_op_sys_clock_reg_val[ + v4l2_ctrl_g_ctrl(va->link_freq)]); + if (ret) + return ret; + val = TI964_CSI_ENABLE; + val |= TI964_CSI_CONTS_CLOCK; + /* Enable skew calculation when 1.6Gbps output is enabled. */ + if (v4l2_ctrl_g_ctrl(va->link_freq)) + val |= TI964_CSI_SKEWCAL; + return regmap_write(va->regmap8, TI964_CSI_CTL, val); +} + +static bool ti964_broadcast_mode(struct v4l2_subdev *subdev) +{ + struct ti964 *va = to_ti964(subdev); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_subdev *sd; + char *sd_name = NULL; + bool first = true; + unsigned int h = 0, w = 0, code = 0; + bool single_stream = true; + int i, rval; + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + struct media_pad *remote_pad = + media_entity_remote_pad(&va->pad[i]); + + if (!remote_pad) + continue; + + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = remote_pad->index; + fmt.stream = 0; + + rval = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (rval) + return false; + + if (first) { + sd_name = va->sub_devs[i].sd_name; + h = fmt.format.height; + w = fmt.format.width; + code = fmt.format.code; + first = false; + } else { + if (strncmp(sd_name, va->sub_devs[i].sd_name, 16)) + return false; + + if (h != fmt.format.height || w != fmt.format.width + || code != fmt.format.code) + return false; + + single_stream = false; + } + } + + if (single_stream) + return false; + + return true; +} + +static int ti964_tp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ti964 *va = to_ti964(subdev); + int i, rval; + + dev_dbg(va->sd.dev, "TI964 starts to stream test pattern.\n"); + for (i = 0; i < ARRAY_SIZE(ti964_tp_settings); i++) { + rval = regmap_write(va->regmap8, + ti964_tp_settings[i].reg, + ti964_tp_settings[i].val); + if (rval) { + dev_err(va->sd.dev, "Register write error.\n"); + return rval; + } + } + + rval = regmap_write(va->regmap8, TI964_IND_ACC_DATA, enable); + if (rval) { + dev_err(va->sd.dev, "Register write error.\n"); + return rval; + } + + return 0; +} + +static int ti964_rx_port_config(struct ti964 *va, int sink, int rx_port) +{ + int rval; + u8 bpp; + int port_cfg2_val; + int vc_mode_reg_index; + int vc_mode_reg_val; + int mipi_dt_type; + int high_fv_flags = va->subdev_pdata[sink].high_framevalid_flags; + + /* Select RX port. */ + rval = regmap_write(va->regmap8, TI964_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) { + dev_err(va->sd.dev, "Failed to select RX port.\n"); + return rval; + } + + /* Set RX port mode. */ + bpp = ti964_validate_csi_data_format( + va->ffmts[sink][0].code)->width; + rval = regmap_write(va->regmap8, TI964_PORT_CONFIG, + (bpp == 12) ? + TI964_FPD3_RAW12_75MHz : TI964_FPD3_RAW10_100MHz); + if (rval) { + dev_err(va->sd.dev, "Failed to set port config.\n"); + return rval; + } + + mipi_dt_type = ti964_validate_csi_data_format( + va->ffmts[sink][0].code)->mipi_dt_code; + /* + * RAW8 and YUV422 need to enable RAW10 bit mode. + * RAW12 need to set the RAW10_8bit to reserved. + */ + switch (bpp) { + case 8: + case 16: + port_cfg2_val = TI964_RAW10_8BIT & (~high_fv_flags); + vc_mode_reg_index = TI964_RAW10_ID; + break; + case 12: + port_cfg2_val = TI964_RAW12; + vc_mode_reg_index = TI964_RAW12_ID; + break; + default: + port_cfg2_val = TI964_RAW10_NORMAL & (~high_fv_flags); + vc_mode_reg_index = TI964_RAW10_ID; + break; + } + + vc_mode_reg_val = mipi_dt_type | sink << 6; + rval = regmap_write(va->regmap8, vc_mode_reg_index, vc_mode_reg_val); + if (rval) { + dev_err(va->sd.dev, "Failed to set virtual channel & data type.\n"); + return rval; + } + + rval = regmap_write(va->regmap8, TI964_PORT_CONFIG2, port_cfg2_val); + if (rval) { + dev_err(va->sd.dev, "Failed to set port config2.\n"); + return rval; + } + + return 0; +} + +static int ti964_map_subdevs_addr(struct ti964 *va) +{ + unsigned short rx_port, phy_i2c_addr, alias_i2c_addr; + int i, rval; + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + rx_port = va->sub_devs[i].rx_port; + phy_i2c_addr = va->sub_devs[i].phy_i2c_addr; + alias_i2c_addr = va->sub_devs[i].alias_i2c_addr; + + if (!phy_i2c_addr || !alias_i2c_addr) + continue; + + rval = ti964_map_phy_i2c_addr(va, rx_port, phy_i2c_addr); + if (rval) + return rval; + + /* set 7bit alias i2c addr */ + rval = ti964_map_alias_i2c_addr(va, rx_port, + alias_i2c_addr << 1); + if (rval) + return rval; + } + + return 0; +} + +static int ti964_find_subdev_index(struct ti964 *va, struct v4l2_subdev *sd) +{ + int i; + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + if (va->sub_devs[i].sd == sd) + return i; + } + + WARN_ON(1); + + return -EINVAL; +} + +static int ti964_set_frame_sync(struct ti964 *va, int enable) +{ + int i, rval; + int index = !!enable; + + for (i = 0; i < ARRAY_SIZE(ti964_frame_sync_settings[index]); i++) { + rval = regmap_write(va->regmap8, + ti964_frame_sync_settings[index][i].reg, + ti964_frame_sync_settings[index][i].val); + if (rval) { + dev_err(va->sd.dev, "Failed to %s frame sync\n", + enable ? "enable" : "disable"); + return rval; + } + } + + return 0; +} + +static int ti964_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ti964 *va = to_ti964(subdev); + struct v4l2_subdev *sd; + int i, j, rval; + bool broadcast; + unsigned int rx_port; + int sd_idx = -1; + DECLARE_BITMAP(rx_port_enabled, 32); + + dev_dbg(va->sd.dev, "TI964 set stream, enable %d\n", enable); + + if (v4l2_ctrl_g_ctrl(va->test_pattern)) + return ti964_tp_set_stream(subdev, enable); + + broadcast = ti964_broadcast_mode(subdev); + if (enable) + dev_info(va->sd.dev, "TI964 in %s mode", + broadcast ? "broadcast" : "non broadcast"); + + bitmap_zero(rx_port_enabled, 32); + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + struct media_pad *remote_pad = + media_entity_remote_pad(&va->pad[i]); + + if (!remote_pad) + continue; + + /* Find ti964 subdev */ + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + j = ti964_find_subdev_index(va, sd); + if (j < 0) + return -EINVAL; + rx_port = va->sub_devs[j].rx_port; + rval = ti964_rx_port_config(va, i, rx_port); + if (rval < 0) + return rval; + + bitmap_set(rx_port_enabled, rx_port, 1); + + if (broadcast && sd_idx == -1) { + sd_idx = j; + } else if (broadcast) { + rval = ti964_map_alias_i2c_addr(va, rx_port, + va->sub_devs[sd_idx].alias_i2c_addr << 1); + if (rval < 0) + return rval; + } else { + /* Stream on/off sensor */ + rval = v4l2_subdev_call(sd, video, s_stream, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set stream for %s, enable %d\n", + sd->name, enable); + return rval; + } + + /* RX port fordward */ + rval = ti964_reg_set_bit(va, TI964_FWD_CTL1, + rx_port + 4, !enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to forward RX port%d. enable %d\n", + i, enable); + return rval; + } + + } + } + + if (broadcast) { + if (sd_idx < 0) { + dev_err(va->sd.dev, "No sensor connected!\n"); + return -ENODEV; + } + sd = va->sub_devs[sd_idx].sd; + rval = v4l2_subdev_call(sd, video, s_stream, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set stream for %s. enable %d\n", + sd->name, enable); + return rval; + } + + rval = ti964_set_frame_sync(va, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set frame sync.\n"); + return rval; + } + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + if (enable && test_bit(i, rx_port_enabled)) { + rval = ti964_fsin_gpio_init(va, + va->sub_devs[i].rx_port, + va->sub_devs[i].fsin_gpio); + if (rval) { + dev_err(va->sd.dev, + "Failed to enable frame sync gpio init.\n"); + return rval; + } + } + } + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + if (!test_bit(i, rx_port_enabled)) + continue; + + /* RX port fordward */ + rval = ti964_reg_set_bit(va, TI964_FWD_CTL1, + i + 4, !enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to forward RX port%d. enable %d\n", + i, enable); + return rval; + } + } + + /* + * Restore each subdev i2c address as we may + * touch it later. + */ + rval = ti964_map_subdevs_addr(va); + if (rval) + return rval; + } + + return 0; +} + +static struct v4l2_subdev_internal_ops ti964_sd_internal_ops = { + .open = ti964_open, + .registered = ti964_registered, +}; + +static bool ti964_sd_has_route(struct media_entity *entity, + unsigned int pad0, unsigned int pad1, int *stream) +{ + struct ti964 *va = to_ti964(media_entity_to_v4l2_subdev(entity)); + + if (va == NULL || stream == NULL || + *stream >= va->nstreams || *stream < 0) + return false; + + if ((va->route[*stream].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) && + ((va->route[*stream].source == pad0 && + va->route[*stream].sink == pad1) || + (va->route[*stream].source == pad1 && + va->route[*stream].sink == pad0))) + return true; + + return false; +} + +static const struct media_entity_operations ti964_sd_entity_ops = { + .has_route = ti964_sd_has_route, +}; + +static const struct v4l2_subdev_video_ops ti964_sd_video_ops = { + .s_stream = ti964_set_stream, +}; + +static const struct v4l2_subdev_core_ops ti964_core_subdev_ops = { + .s_power = ti964_set_power, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, +#endif +}; + +static int ti964_s_ctrl(struct v4l2_ctrl *ctrl) +{ + return 0; +} + +static const struct v4l2_ctrl_ops ti964_ctrl_ops = { + .s_ctrl = ti964_s_ctrl, +}; + +static const struct v4l2_ctrl_config ti964_controls[] = { + { + .ops = &ti964_ctrl_ops, + .id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(ti964_op_sys_clock) - 1, + .min = 0, + .step = 0, + .def = 0, + .qmenu_int = ti964_op_sys_clock, + }, + { + .ops = &ti964_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 1, + .min = 0, + .step = 1, + .def = 0, + }, +}; + +static const struct v4l2_subdev_pad_ops ti964_sd_pad_ops = { + .get_fmt = ti964_get_format, + .set_fmt = ti964_set_format, + .get_frame_desc = ti964_get_frame_desc, + .enum_mbus_code = ti964_enum_mbus_code, + .set_routing = ti964_set_routing, + .get_routing = ti964_get_routing, +}; + +static struct v4l2_subdev_ops ti964_sd_ops = { + .core = &ti964_core_subdev_ops, + .video = &ti964_sd_video_ops, + .pad = &ti964_sd_pad_ops, +}; + +static int ti964_register_subdev(struct ti964 *va) +{ + int i, rval; + struct i2c_client *client = v4l2_get_subdevdata(&va->sd); + + v4l2_subdev_init(&va->sd, &ti964_sd_ops); + snprintf(va->sd.name, sizeof(va->sd.name), "TI964 %c", + va->pdata->suffix); + + va->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_SUBSTREAMS; + + va->sd.internal_ops = &ti964_sd_internal_ops; + va->sd.entity.ops = &ti964_sd_entity_ops; + + v4l2_set_subdevdata(&va->sd, client); + + v4l2_ctrl_handler_init(&va->ctrl_handler, + ARRAY_SIZE(ti964_controls)); + + if (va->ctrl_handler.error) { + dev_err(va->sd.dev, + "Failed to init ti964 controls. ERR: %d!\n", + va->ctrl_handler.error); + return va->ctrl_handler.error; + } + + va->sd.ctrl_handler = &va->ctrl_handler; + + for (i = 0; i < ARRAY_SIZE(ti964_controls); i++) { + const struct v4l2_ctrl_config *cfg = + &ti964_controls[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&va->ctrl_handler, cfg, NULL); + if (!ctrl) { + dev_err(va->sd.dev, + "Failed to create ctrl %s!\n", cfg->name); + rval = va->ctrl_handler.error; + goto failed_out; + } + } + + va->link_freq = v4l2_ctrl_find(&va->ctrl_handler, V4L2_CID_LINK_FREQ); + va->test_pattern = v4l2_ctrl_find(&va->ctrl_handler, + V4L2_CID_TEST_PATTERN); + + for (i = 0; i < va->nsinks; i++) + va->pad[i].flags = MEDIA_PAD_FL_SINK; + va->pad[TI964_PAD_SOURCE].flags = + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MULTIPLEX; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_init(&va->sd.entity, NR_OF_TI964_PADS, va->pad, 0); +#else + rval = media_entity_pads_init(&va->sd.entity, + NR_OF_TI964_PADS, va->pad); +#endif + if (rval) { + dev_err(va->sd.dev, + "Failed to init media entity for ti964!\n"); + goto failed_out; + } + + return 0; + +failed_out: + v4l2_ctrl_handler_free(&va->ctrl_handler); + return rval; +} + +static int ti964_init(struct ti964 *va) +{ + unsigned int reset_gpio = va->pdata->reset_gpio; + int i, rval; + unsigned int val; + + gpio_set_value(reset_gpio, 1); + usleep_range(2000, 3000); + dev_dbg(va->sd.dev, "Setting reset gpio %d to 1.\n", reset_gpio); + + rval = regmap_read(va->regmap8, TI964_DEVID, &val); + if (rval) { + dev_err(va->sd.dev, "Failed to read device ID of TI964!\n"); + return rval; + } + dev_info(va->sd.dev, "TI964 device ID: 0x%X\n", val); + + for (i = 0; i < ARRAY_SIZE(ti964_init_settings); i++) { + rval = regmap_write(va->regmap8, + ti964_init_settings[i].reg, + ti964_init_settings[i].val); + if (rval) + return rval; + } + + rval = ti964_map_subdevs_addr(va); + if (rval) + return rval; + + return 0; +} + +static void ti964_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + struct i2c_client *client = to_i2c_client(chip->dev); +#else + struct i2c_client *client = to_i2c_client(chip->parent); +#endif + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ti964 *va = to_ti964(subdev); + unsigned int reg_val; + int rx_port, gpio_port; + int ret; + + if (gpio >= NR_OF_TI964_GPIOS) + return; + + rx_port = gpio / NR_OF_GPIOS_PER_PORT; + gpio_port = gpio % NR_OF_GPIOS_PER_PORT; + + ret = regmap_write(va->regmap8, TI964_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (ret) { + dev_dbg(&client->dev, "Failed to select RX port.\n"); + return; + } + ret = regmap_read(va->regmap8, TI964_BC_GPIO_CTL0, ®_val); + if (ret) { + dev_dbg(&client->dev, "Failed to read gpio status.\n"); + return; + } + + if (gpio_port == 0) { + reg_val &= ~TI964_GPIO0_MASK; + reg_val |= value ? TI964_GPIO0_HIGH : TI964_GPIO0_LOW; + } else { + reg_val &= ~TI964_GPIO1_MASK; + reg_val |= value ? TI964_GPIO1_HIGH : TI964_GPIO1_LOW; + } + + ret = regmap_write(va->regmap8, TI964_BC_GPIO_CTL0, reg_val); + if (ret) + dev_dbg(&client->dev, "Failed to set gpio.\n"); +} + +static int ti964_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int level) +{ + return 0; +} + +static int ti964_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct ti964 *va; + int i, rval = 0; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + va = devm_kzalloc(&client->dev, sizeof(*va), GFP_KERNEL); + if (!va) + return -ENOMEM; + + va->pdata = client->dev.platform_data; + + va->nsources = NR_OF_TI964_SOURCE_PADS; + va->nsinks = NR_OF_TI964_SINK_PADS; + va->npads = NR_OF_TI964_PADS; + va->nstreams = NR_OF_TI964_STREAMS; + + va->crop = devm_kcalloc(&client->dev, va->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + + va->compose = devm_kcalloc(&client->dev, va->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + + va->route = devm_kcalloc(&client->dev, va->nstreams, + sizeof(*va->route), GFP_KERNEL); + + va->stream = devm_kcalloc(&client->dev, va->npads, + sizeof(*va->stream), GFP_KERNEL); + + if (!va->crop || !va->compose || !va->route || !va->stream) + return -ENOMEM; + + for (i = 0; i < va->npads; i++) { + va->ffmts[i] = devm_kcalloc(&client->dev, va->nstreams, + sizeof(struct v4l2_mbus_framefmt), + GFP_KERNEL); + if (!va->ffmts[i]) + return -ENOMEM; + + va->stream[i].stream_id = + devm_kcalloc(&client->dev, va->nsinks, + sizeof(*va->stream[i].stream_id), GFP_KERNEL); + if (!va->stream[i].stream_id) + return -ENOMEM; + } + + for (i = 0; i < va->nstreams; i++) { + va->route[i].sink = i; + va->route[i].source = TI964_PAD_SOURCE; + va->route[i].flags = 0; + } + + for (i = 0; i < va->nsinks; i++) { + va->stream[i].stream_id[0] = i; + va->stream[TI964_PAD_SOURCE].stream_id[i] = i; + } + + va->regmap8 = devm_regmap_init_i2c(client, + &ti964_reg_config8); + if (IS_ERR(va->regmap8)) { + dev_err(&client->dev, "Failed to init regmap8!\n"); + return -EIO; + } + + va->regmap16 = devm_regmap_init_i2c(client, + &ti964_reg_config16); + if (IS_ERR(va->regmap16)) { + dev_err(&client->dev, "Failed to init regmap16!\n"); + return -EIO; + } + + mutex_init(&va->mutex); + v4l2_i2c_subdev_init(&va->sd, client, &ti964_sd_ops); + rval = ti964_register_subdev(va); + if (rval) { + dev_err(&client->dev, "Failed to register va subdevice!\n"); + return rval; + } + + if (devm_gpio_request_one(va->sd.dev, va->pdata->reset_gpio, 0, + "ti964 reset") != 0) { + dev_err(va->sd.dev, "Unable to acquire gpio %d\n", + va->pdata->reset_gpio); + return -ENODEV; + } + + rval = ti964_init(va); + if (rval) { + dev_err(&client->dev, "Failed to init TI964!\n"); + return rval; + } + + /* + * TI964 has several back channel GPIOs. + * We export GPIO0 and GPIO1 to control reset or fsin. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + va->gc.dev = &client->dev; +#else + va->gc.parent = &client->dev; +#endif + va->gc.owner = THIS_MODULE; + va->gc.label = "TI964 GPIO"; + va->gc.ngpio = NR_OF_TI964_GPIOS; + va->gc.base = -1; + va->gc.set = ti964_gpio_set; + va->gc.direction_output = ti964_gpio_direction_output; + rval = gpiochip_add(&va->gc); + if (rval) { + dev_err(&client->dev, "Failed to add gpio chip!\n"); + return -EIO; + } + + return 0; +} + +static int ti964_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ti964 *va = to_ti964(subdev); + int i; + + if (!va) + return 0; + + mutex_destroy(&va->mutex); + v4l2_ctrl_handler_free(&va->ctrl_handler); + v4l2_device_unregister_subdev(&va->sd); + media_entity_cleanup(&va->sd.entity); + + for (i = 0; i < NR_OF_TI964_SINK_PADS; i++) { + if (va->sub_devs[i].sd) { + struct i2c_client *sub_client = + v4l2_get_subdevdata(va->sub_devs[i].sd); + + i2c_unregister_device(sub_client); + } + va->sub_devs[i].sd = NULL; + } + + gpiochip_remove(&va->gc); + + return 0; +} + +#ifdef CONFIG_PM +static int ti964_suspend(struct device *dev) +{ + return 0; +} + +static int ti964_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ti964 *va = to_ti964(subdev); + + return ti964_init(va); +} +#else +#define ti964_suspend NULL +#define ti964_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id ti964_id_table[] = { + { TI964_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ti964_id_table); + +static const struct dev_pm_ops ti964_pm_ops = { + .suspend = ti964_suspend, + .resume = ti964_resume, +}; + +static struct i2c_driver ti964_i2c_driver = { + .driver = { + .name = TI964_NAME, + .pm = &ti964_pm_ops, + }, + .probe = ti964_probe, + .remove = ti964_remove, + .id_table = ti964_id_table, +}; +module_i2c_driver(ti964_i2c_driver); + +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI964 CSI2-Aggregator driver"); diff --git a/include/media/ti964.h b/include/media/ti964.h new file mode 100644 index 000000000000..ab5ee210efe4 --- /dev/null +++ b/include/media/ti964.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef TI964_H +#define TI964_H + +#include +#include +#include +#include +#include + +#define TI964_NAME "ti964" + +#define PIXEL_ORDER_GRBG 0 +#define PIXEL_ORDER_RGGB 1 +#define PIXEL_ORDER_BGGR 2 +#define PIXEL_ORDER_GBRG 3 + +#define NR_OF_TI964_STREAMS 4 +#define NR_OF_TI964_SOURCE_PADS 1 +#define NR_OF_TI964_SINK_PADS 4 +#define NR_OF_TI964_PADS \ + (NR_OF_TI964_SOURCE_PADS + NR_OF_TI964_SINK_PADS) +#define NR_OF_GPIOS_PER_PORT 2 +#define NR_OF_TI964_GPIOS \ + (NR_OF_TI964_SINK_PADS * NR_OF_GPIOS_PER_PORT) + +#define TI964_PAD_SOURCE 4 + +#define TI964_MIN_WIDTH 640 +#define TI964_MIN_HEIGHT 480 +#define TI964_MAX_WIDTH 1920 +#define TI964_MAX_HEIGHT 1080 + +struct ti964_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; + u8 mipi_dt_code; +}; + +struct ti964_subdev_info { + struct i2c_board_info board_info; + int i2c_adapter_id; + unsigned short rx_port; + unsigned short phy_i2c_addr; + const char suffix; /* suffix for subdevs */ +}; + +struct ti964_pdata { + unsigned int subdev_num; + struct ti964_subdev_info *subdev_info; + unsigned int reset_gpio; + const char suffix; /* suffix for multi aggregators, abcd... */ +}; + +#endif From f8e9f63f2389780c747501e443df7a6832771ea6 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:52 +0800 Subject: [PATCH 1143/1276] media: i2c: add MAX9286/96705 SER/DES driver The MAX9286 GMSL deserializer receives data from up to four GMSL serializers over STP cables and output data on four CSI-2 lanes. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/i2c/Kconfig | 6 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max9286-reg-settings.h | 113 ++ drivers/media/i2c/max9286.c | 1254 ++++++++++++++++++++++ include/media/max9286.h | 53 + 5 files changed, 1427 insertions(+) create mode 100644 drivers/media/i2c/max9286-reg-settings.h create mode 100644 drivers/media/i2c/max9286.c create mode 100644 include/media/max9286.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2681d2bc74ff..be954b4d9425 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1072,6 +1072,12 @@ config VIDEO_TI964 ---help--- This is a driver for TI964 camera. +config VIDEO_MAX9286 + tristate "MAX96705/MAX9286 Serializer/Deserializer" + depends on I2C && VIDEO_V4L2 && REGMAP_I2C + ---help--- + This is a MAXIM 96705 Serializer and MAXIM 9286 CSI-2 Deserializer driver. + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e0d41d364c45..96ec00673960 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -113,3 +113,4 @@ obj-$(CONFIG_SDR_MAX2175) += max2175.o obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule/ obj-$(CONFIG_VIDEO_TI964) += ti964.o +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o diff --git a/drivers/media/i2c/max9286-reg-settings.h b/drivers/media/i2c/max9286-reg-settings.h new file mode 100644 index 000000000000..f03e6fc1260a --- /dev/null +++ b/drivers/media/i2c/max9286-reg-settings.h @@ -0,0 +1,113 @@ +/* SPDX-LIcense_Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef MAX9286_REG_H +#define MAX9286_REG_H + +#include + +#define DS_ADDR_MAX9286 0x48 +#define S_ADDR_MAX96705 0x40 +#define S_ADDR_MAX96705_BROADCAST (S_ADDR_MAX96705 + NR_OF_MAX_STREAMS + 1) + +#define ADDR_AR0231AT_SENSOR 0x10 + +/* Deserializer: MAX9286 registers */ +#define DS_LINK_ENABLE 0x00 +#define DS_FSYNCMODE 0x01 +#define DS_FSYNC_PERIOD_LOW 0x06 +#define DS_FSYNC_PERIOD_MIDDLE 0x07 +#define DS_FSYNC_PERIOD_HIGH 0x08 +#define DS_FWDCCEN_REVCCEN 0x0A +#define DS_LINK_OUTORD 0x0B +#define DS_HS_VS 0x0C +#define DS_CSI_DBL_DT 0x12 +#define DS_CSI_VC_CTL 0x15 +#define DS_ENEQ 0x1B +#define DS_HIGHIMM 0x1C +#define DS_MAX9286_DEVID 0x1E +#define DS_FSYNC_LOCKED 0x31 +#define DS_I2CLOCACK 0x34 +#define DS_FPL_RT 0x3B +#define DS_ENCRC_FPL 0x3F +#define DS_CONFIGL_VIDEOL_DET 0x49 +#define DS_OVERLAP_WIN_LOW 0x63 +#define DS_OVERLAP_WIN_HIGH 0x64 +#define DS_ATUO_MASK_LINK 0x69 + +/* Serializer: MAX96705 registers */ +#define S_SERADDR 0x00 +#define S_MAIN_CTL 0x04 +#define S_CMLLVL_PREEMP 0x06 +#define S_CONFIG 0x07 +#define S_RSVD_8 0x08 +#define S_I2C_SOURCE_IS 0x09 +#define S_I2C_DST_IS 0x0A +#define S_I2C_SOURCE_SER 0x0B +#define S_I2C_DST_SER 0x0C +#define S_INPUT_STATUS 0x15 +#define S_SYNC_GEN_CONFIG 0x43 +#define S_VS_DLY_2 0x44 +#define S_VS_DLY_1 0x45 +#define S_VS_H_2 0x47 +#define S_VS_H_1 0x48 +#define S_VS_H_0 0x49 +#define S_DBL_ALIGN_TO 0x67 +#define S_RSVD_97 0x97 + +struct max9286_register_write { + u8 reg; + u8 val; +}; + +static const struct max9286_register_write max9286_byte_order_settings_12bit[] = { + {0x20, 0x0B}, + {0x21, 0x0A}, + {0x22, 0x09}, + {0x23, 0x08}, + {0x24, 0x07}, + {0x25, 0x06}, + {0x26, 0x05}, + {0x27, 0x04}, + {0x28, 0x03}, + {0x29, 0x02}, + {0x2A, 0x01}, + {0x2B, 0x00}, + {0x30, 0x1B}, + {0x31, 0x1A}, + {0x32, 0x19}, + {0x33, 0x18}, + {0x34, 0x17}, + {0x35, 0x16}, + {0x36, 0x15}, + {0x37, 0x14}, + {0x38, 0x13}, + {0x39, 0x12}, + {0x3A, 0x11}, + {0x3B, 0x10}, +}; + +static const struct max9286_register_write max9286_byte_order_settings_10bit[] = { + {0x20, 0x09}, + {0x21, 0x08}, + {0x22, 0x07}, + {0x23, 0x06}, + {0x24, 0x05}, + {0x25, 0x04}, + {0x26, 0x03}, + {0x27, 0x02}, + {0x28, 0x01}, + {0x29, 0x00}, + {0x30, 0x19}, + {0x31, 0x18}, + {0x32, 0x17}, + {0x33, 0x16}, + {0x34, 0x15}, + {0x35, 0x14}, + {0x36, 0x13}, + {0x37, 0x12}, + {0x38, 0x11}, + {0x39, 0x10}, +}; + +#endif diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c new file mode 100644 index 000000000000..2a973ff6529b --- /dev/null +++ b/drivers/media/i2c/max9286.c @@ -0,0 +1,1254 @@ +/* SPDX-LIcense_Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "max9286-reg-settings.h" + +struct max9286 { + struct v4l2_subdev v4l2_sd; + struct max9286_pdata *pdata; + struct media_pad pad[NR_OF_MAX_PADS]; + unsigned char sensor_present; + unsigned int total_sensor_num; + unsigned int nsources; + unsigned int nsinks; + unsigned int npads; + unsigned int nstreams; + const char *name; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_subdev *sub_devs[NR_OF_MAX_SINK_PADS]; + struct v4l2_mbus_framefmt *ffmts[NR_OF_MAX_PADS]; + struct rect *crop; + struct rect *compose; + struct { + unsigned int *stream_id; + } *stream; /* stream enable/disable status, indexed by pad */ + struct { + unsigned int sink; + unsigned int source; + int flags; + } *route; /* pad level info, indexed by stream */ + + struct regmap *regmap8; + struct mutex max_mutex; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; +}; + +#define to_max_9286(_sd) container_of(_sd, struct max9286, v4l2_sd) + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct max9286_csi_data_format max_csi_data_formats[] = { + { MEDIA_BUS_FMT_YUYV8_1X16, 16, 16, PIXEL_ORDER_GBRG, 0x1e }, + { MEDIA_BUS_FMT_UYVY8_1X16, 16, 16, PIXEL_ORDER_GBRG, 0x1e }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, PIXEL_ORDER_GRBG, 0x2c }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, PIXEL_ORDER_RGGB, 0x2c }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, PIXEL_ORDER_BGGR, 0x2c }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, PIXEL_ORDER_GBRG, 0x2c }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, PIXEL_ORDER_GRBG, 0x2b }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, PIXEL_ORDER_RGGB, 0x2b }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, PIXEL_ORDER_BGGR, 0x2b }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, PIXEL_ORDER_GBRG, 0x2b }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, PIXEL_ORDER_GRBG, 0x2a }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, PIXEL_ORDER_RGGB, 0x2a }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, PIXEL_ORDER_BGGR, 0x2a }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, PIXEL_ORDER_GBRG, 0x2a }, +}; + +static const uint32_t max9286_supported_codes_pad[] = { + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + 0, +}; + +static const uint32_t *max9286_supported_codes[] = { + max9286_supported_codes_pad, +}; + +static struct regmap_config max9286_reg_config8 = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* Serializer register write */ +static int max96705_write_register(struct max9286 *max, + unsigned int offset, u8 reg, u8 val) +{ + int ret; + int retry, timeout = 10; + struct i2c_client *client = v4l2_get_subdevdata(&max->v4l2_sd); + + client->addr = S_ADDR_MAX96705 + offset; + for (retry = 0; retry < timeout; retry++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (val < 0) + usleep_range(5000, 6000); + else + break; + } + + client->addr = DS_ADDR_MAX9286; + if (retry >= timeout) { + dev_err(max->v4l2_sd.dev, + "%s:write reg failed: reg=%2x\n", __func__, reg); + return -EREMOTEIO; + } + + return 0; +} + +/* Serializer register read */ +static int +max96705_read_register(struct max9286 *max, unsigned int i, u8 reg) +{ + int val; + int retry, timeout = 10; + struct i2c_client *client = v4l2_get_subdevdata(&max->v4l2_sd); + + client->addr = S_ADDR_MAX96705 + i; + for (retry = 0; retry < timeout; retry++) { + val = i2c_smbus_read_byte_data(client, reg); + if (val >= 0) + break; + usleep_range(5000, 6000); + } + + client->addr = DS_ADDR_MAX9286; + if (retry >= timeout) { + dev_err(max->v4l2_sd.dev, + "%s:read reg failed: reg=%2x\n", __func__, reg); + return -EREMOTEIO; + } + + return val; +} + +/* Validate csi_data_format */ +static const struct max9286_csi_data_format * +max9286_validate_csi_data_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(max_csi_data_formats); i++) { + if (max_csi_data_formats[i].code == code) + return &max_csi_data_formats[i]; + } + + return &max_csi_data_formats[0]; +} + +/* Initialize image sensors and set stream on registers */ +static int max9286_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct max9286 *max = to_max_9286(subdev); + struct media_pad *remote_pad; + struct v4l2_subdev *sd; + int i, rval, j; + unsigned int val; + u8 slval = 0xE0; + u8 dtval = 0xF7; + const struct max9286_register_write *max9286_byte_order_settings; + + dev_dbg(max->v4l2_sd.dev, "MAX9286 set stream. enable = %d\n", enable); + + /* Disable I2C ACK */ + rval = regmap_write(max->regmap8, DS_I2CLOCACK, 0xB6); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to disable I2C ACK!\n"); + return rval; + } + + for (i = 0; i < NR_OF_MAX_SINK_PADS; i++) { + if (((0x01 << (i)) & max->sensor_present) == 0) + continue; + + /* Find the pad at the remote end of the link */ + remote_pad = media_entity_remote_pad(&max->pad[i]); + + if (!remote_pad) + continue; + + if (enable) { + /* + * Enable CSI-2 lanes D0, D1, D2, D3 + * Enable CSI-2 DBL (Double Input Mode) + * Enable GMSL DBL for RAWx2 + * Enable RAW10/RAW12 data type + */ + u8 bpp = max9286_validate_csi_data_format( + max->ffmts[i][0].code)->width; + + if (bpp == 10) { + dtval = 0xF6; + max9286_byte_order_settings = + &max9286_byte_order_settings_10bit[0]; + } else if (bpp == 12) { + dtval = 0xF7; + max9286_byte_order_settings = + &max9286_byte_order_settings_12bit[0]; + } else { + dev_err(max->v4l2_sd.dev, + "Only support RAW10/12, current bpp is %d!\n", bpp); + return -EINVAL; + } + + rval = regmap_write(max->regmap8, DS_CSI_DBL_DT, dtval); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to set data type!\n"); + return rval; + } + + for (j = 0; j < bpp * 2; j++) { + rval = max96705_write_register(max, + S_ADDR_MAX96705_BROADCAST - S_ADDR_MAX96705, + (max9286_byte_order_settings + j)->reg, + (max9286_byte_order_settings + j)->val); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to set max9286 byte order\n"); + return rval; + } + } + usleep_range(2000, 3000); + } + + /* Enable link */ + slval |= (0x0F & (1 << i)); + rval = regmap_write(max->regmap8, DS_LINK_ENABLE, slval); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to enable GMSL links!\n"); + return rval; + } + + rval = regmap_write(max->regmap8, DS_ATUO_MASK_LINK, 0x30); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to write 0x69\n"); + return rval; + } + /* Calls sensor set stream */ + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + rval = v4l2_subdev_call(sd, video, s_stream, enable); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to set stream for %s. enable = %d\n", + sd->name, enable); + return rval; + } + } + + /* Enable I2C ACK */ + rval = regmap_write(max->regmap8, DS_I2CLOCACK, 0x36); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to enable I2C ACK!\n"); + return rval; + } + + /* Check if valid PCLK is available for the links */ + for (i = 1; i <= NR_OF_MAX_SINK_PADS; i++) { + if (((0x01 << (i - 1)) & max->sensor_present) == 0) + continue; + + val = max96705_read_register(max, i, S_INPUT_STATUS); + if ((val != -EREMOTEIO) && (val & 0x01)) + dev_info(max->v4l2_sd.dev, + "Valid PCLK detected for link %d\n", i); + else if (val != -EREMOTEIO) + dev_info(max->v4l2_sd.dev, + "Failed to read PCLK reg for link %d\n", i); + } + + /* Set preemphasis settings for all serializers (set to 3.3dB)*/ + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_CMLLVL_PREEMP, 0xAA); + usleep_range(5000, 6000); + + /* Set VSYNC Delay */ + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_SYNC_GEN_CONFIG, 0x21); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_VS_DLY_2, 0x06); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_VS_DLY_1, 0xD8); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_VS_H_2, 0x26); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_VS_H_1, 0x00); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_VS_H_0, 0x00); + usleep_range(5000, 6000); + + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_DBL_ALIGN_TO, 0xC4); + usleep_range(5000, 6000); + + /* Enable link equalizers */ + rval = regmap_write(max->regmap8, DS_ENEQ, 0x0F); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to automatically detect serial data rate!\n"); + return rval; + } + usleep_range(5000, 6000); + rval = regmap_write(max->regmap8, DS_HS_VS, 0x91); + + /* Enable serial links and desable configuration */ + max96705_write_register(max, S_ADDR_MAX96705_BROADCAST - + S_ADDR_MAX96705, S_MAIN_CTL, 0x83); + /* Wait for more than 2 Frames time from each sensor */ + usleep_range(100000, 101000); + + /* + * Poll frame synchronization bit of deserializer + * All the cameras should work in SYNC mode + * MAX9286 sends a pulse to each camera, then each camera sends out + * one frame. The VSYNC for each camera should appear in almost same + * time for the deserializer to lock FSYNC + */ + rval = regmap_read(max->regmap8, DS_FSYNC_LOCKED, &val); + if (rval) { + dev_info(max->v4l2_sd.dev, "Frame SYNC not locked!\n"); + return rval; + } else if (val & (0x01 << 6)) + dev_info(max->v4l2_sd.dev, "Deserializer Frame SYNC locked\n"); + + /* + * Enable/set bit[7] of DS_CSI_VC_CTL register for VC operation + * Set VC according to the link number + * Enable CSI-2 output + */ + if (!enable) { + rval = regmap_write(max->regmap8, DS_CSI_VC_CTL, 0x93); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to disable CSI output!\n"); + return rval; + } + } else { + rval = regmap_write(max->regmap8, DS_CSI_VC_CTL, 0x9B); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to enable CSI output!\n"); + return rval; + } + } + + return 0; +} + +/* Get the media bus format */ +static struct v4l2_mbus_framefmt * +__max9286_get_ffmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int which, + unsigned int stream) +{ + struct max9286 *max = to_max_9286(subdev); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(subdev, cfg, pad); + else + return &max->ffmts[pad][stream]; +} + +/* callback for VIDIOC_SUBDEV_G_FMT ioctl handler code */ +static int max9286_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct max9286 *max = to_max_9286(subdev); + + if (fmt->stream > max->nstreams) + return -EINVAL; + + mutex_lock(&max->max_mutex); + fmt->format = *__max9286_get_ffmt(subdev, cfg, fmt->pad, fmt->which, + fmt->stream); + mutex_unlock(&max->max_mutex); + + dev_dbg(subdev->dev, "subdev_format: which: %s, pad: %d, stream: %d.\n", + fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE ? + "V4L2_SUBDEV_FORMAT_ACTIVE" : "V4L2_SUBDEV_FORMAT_TRY", + fmt->pad, fmt->stream); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + fmt->format.width, fmt->format.height, fmt->format.code); + + return 0; +} + +/* callback for VIDIOC_SUBDEV_S_FMT ioctl handler code */ +static int max9286_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct max9286 *max = to_max_9286(subdev); + const struct max9286_csi_data_format *csi_format; + struct v4l2_mbus_framefmt *ffmt; + + if (fmt->stream > max->nstreams) + return -EINVAL; + + csi_format = max9286_validate_csi_data_format(fmt->format.code); + + mutex_lock(&max->max_mutex); + ffmt = __max9286_get_ffmt(subdev, cfg, fmt->pad, fmt->which, + fmt->stream); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = csi_format->code; + } + + fmt->format = *ffmt; + mutex_unlock(&max->max_mutex); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + ffmt->width, ffmt->height, ffmt->code); + + return 0; +} + +/* get the current low level media bus frame parameters */ +static int max9286_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) +{ + struct max9286 *max = to_max_9286(sd); + struct v4l2_mbus_frame_desc_entry *entry = desc->entry; + u8 vc = 0; + int i; + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + for (i = 0; i < min_t(int, max->nstreams, desc->num_entries); i++) { + struct v4l2_mbus_framefmt *ffmt = + &max->ffmts[i][MAX_PAD_SOURCE]; + const struct max9286_csi_data_format *csi_format = + max9286_validate_csi_data_format(ffmt->code); + + entry->two_dim.width = ffmt->width; + entry->two_dim.height = ffmt->height; + entry->pixelcode = ffmt->code; + entry->bus.csi2.channel = vc++; + entry->bpp = csi_format->compressed; + entry++; + } + + return 0; +} + +/* Enumerate media bus formats available at a given sub-device pad */ +static int max9286_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct max9286 *max = to_max_9286(sd); + const uint32_t *supported_code = max9286_supported_codes[code->pad]; + bool next_stream = false; + int i; + + if (code->stream & V4L2_SUBDEV_FLAG_NEXT_STREAM) { + next_stream = true; + code->stream &= ~V4L2_SUBDEV_FLAG_NEXT_STREAM; + } + + if (code->stream > max->nstreams) + return -EINVAL; + + if (next_stream) { + if (!(max->pad[code->pad].flags & MEDIA_PAD_FL_MULTIPLEX)) + return -EINVAL; + + if (code->stream < max->nstreams - 1) { + code->stream++; + return 0; + } else + return -EINVAL; + } + + for (i = 0; supported_code[i]; i++) { + if (i == code->index) { + code->code = supported_code[i]; + return 0; + } + } + + return -EINVAL; +} + +/* Configure Media Controller routing */ +static int max9286_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct max9286 *max = to_max_9286(sd); + int i, j, ret = 0; + + for (i = 0; i < min(route->num_routes, max->nstreams); ++i) { + struct v4l2_subdev_route *t = &route->routes[i]; + unsigned int sink = t->sink_pad; + unsigned int source = t->source_pad; + + if (t->sink_stream > max->nstreams - 1 || + t->source_stream > max->nstreams - 1) + continue; + + if (t->source_pad != MAX_PAD_SOURCE) + continue; + + for (j = 0; j < max->nstreams; j++) { + if (sink == max->route[j].sink && + source == max->route[j].source) + break; + } + + if (j == max->nstreams) + continue; + + max->stream[sink].stream_id[0] = t->sink_stream; + max->stream[source].stream_id[sink] = t->source_stream; + + if (t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + max->route[j].flags |= V4L2_SUBDEV_ROUTE_FL_ACTIVE; + else if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + max->route[j].flags &= (~V4L2_SUBDEV_ROUTE_FL_ACTIVE); + } + + return ret; +} + +/* Configure Media Controller routing */ +static int max9286_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct max9286 *max = to_max_9286(sd); + int i; + + for (i = 0; i < min(max->nstreams, route->num_routes); ++i) { + unsigned int sink = max->route[i].sink; + unsigned int source = max->route[i].source; + + route->routes[i].sink_pad = sink; + route->routes[i].sink_stream = + max->stream[sink].stream_id[0]; + route->routes[i].source_pad = source; + route->routes[i].source_stream = + max->stream[source].stream_id[sink]; + route->routes[i].flags = max->route[i].flags; + } + + route->num_routes = i; + + return 0; +} + +/* called when the subdev device node is opened by an application */ +static int max9286_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(subdev, fh->pad, 0); + + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = MAX_PAD_SOURCE, + .format = { + .width = MAX9286_MAX_WIDTH, + .height = MAX9286_MAX_HEIGHT, + + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + }, + .stream = 0, + }; + + *try_fmt = fmt.format; + + return 0; +} + +/* + * called when this subdev is registered. When called the v4l2_dev field is + * set to the correct v4l2_device. + */ +static int max9286_registered(struct v4l2_subdev *subdev) +{ + struct max9286 *max = to_max_9286(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int i, j, k, l, rval, num, nsinks; + + num = max->pdata->subdev_num; + nsinks = max->nsinks; + for (i = 0, k = 0; (i < num) && (k < nsinks); i++, k++) { + struct max9286_subdev_i2c_info *info = + &max->pdata->subdev_info[i]; + struct crlmodule_platform_data *pdata = + (struct crlmodule_platform_data *) + info->board_info.platform_data; + + /* Spin the sensor subdev name suffix */ + pdata->suffix = info->suffix; + + /* aggre and subdves share the same i2c bus */ + max->sub_devs[k] = v4l2_i2c_new_subdev_board( + max->v4l2_sd.v4l2_dev, client->adapter, + &info->board_info, 0); + if (!max->sub_devs[k]) { + dev_err(max->v4l2_sd.dev, + "can't create new i2c subdev %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + continue; + } + + for (j = 0; j < max->sub_devs[k]->entity.num_pads; j++) { + if (max->sub_devs[k]->entity.pads[j].flags & + MEDIA_PAD_FL_SOURCE) + break; + } + + if (j == max->sub_devs[k]->entity.num_pads) { + dev_warn(max->v4l2_sd.dev, + "no source pad in subdev %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + return -ENOENT; + } + + for (l = 0; l < max->nsinks; l++) { + rval = media_create_pad_link( + &max->sub_devs[k]->entity, j, + &max->v4l2_sd.entity, l, 0); + if (rval) { + dev_err(max->v4l2_sd.dev, + "can't create link to %d-%04x\n", + info->i2c_adapter_id, + info->board_info.addr); + return -EINVAL; + } + } + } + + return 0; +} + +static int max9286_set_power(struct v4l2_subdev *subdev, int on) +{ + return 0; +} + +static const struct v4l2_subdev_core_ops max9286_core_subdev_ops = { + .s_power = max9286_set_power, +}; + +static bool max9286_sd_has_route(struct media_entity *entity, + unsigned int pad0, unsigned int pad1, int *stream) +{ + struct max9286 *va = to_max_9286(media_entity_to_v4l2_subdev(entity)); + + if (stream == NULL || *stream >= va->nstreams) + return false; + + if ((va->route[*stream].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) && + ((va->route[*stream].source == pad0 && + va->route[*stream].sink == pad1) || + (va->route[*stream].source == pad1 && + va->route[*stream].sink == pad0))) + return true; + + return false; +} + +static const struct v4l2_subdev_video_ops max9286_sd_video_ops = { + .s_stream = max9286_set_stream, +}; + +static const struct media_entity_operations max9286_sd_entity_ops = { + .has_route = max9286_sd_has_route, +}; + +static const struct v4l2_subdev_pad_ops max9286_sd_pad_ops = { + .get_fmt = max9286_get_format, + .set_fmt = max9286_set_format, + .get_frame_desc = max9286_get_frame_desc, + .enum_mbus_code = max9286_enum_mbus_code, + .set_routing = max9286_set_routing, + .get_routing = max9286_get_routing, +}; + +static struct v4l2_subdev_ops max9286_sd_ops = { + .core = &max9286_core_subdev_ops, + .video = &max9286_sd_video_ops, + .pad = &max9286_sd_pad_ops, +}; + +static struct v4l2_subdev_internal_ops max9286_sd_internal_ops = { + .open = max9286_open, + .registered = max9286_registered, +}; + +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) +{ + return 0; +} + +static const struct v4l2_ctrl_ops max9286_ctrl_ops = { + .s_ctrl = max9286_s_ctrl, +}; + +static const s64 max9286_op_sys_clock[] = { 87750000, }; +static const struct v4l2_ctrl_config max9286_controls[] = { + { + .ops = &max9286_ctrl_ops, + .id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(max9286_op_sys_clock) - 1, + .min = 0, + .step = 0, + .def = 0, + .qmenu_int = max9286_op_sys_clock, + }, + { + .ops = &max9286_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 1, + .min = 0, + .step = 1, + .def = 0, + }, +}; + +/* Registers MAX9286 sub-devices (Image sensors) */ +static int max9286_register_subdev(struct max9286 *max) +{ + int i, rval; + struct i2c_client *client = v4l2_get_subdevdata(&max->v4l2_sd); + + /* subdevice driver initializes v4l2 subdev */ + v4l2_subdev_init(&max->v4l2_sd, &max9286_sd_ops); + snprintf(max->v4l2_sd.name, sizeof(max->v4l2_sd.name), + "MAX9286 %c", max->pdata->suffix); + + max->v4l2_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_SUBSTREAMS; + + max->v4l2_sd.internal_ops = &max9286_sd_internal_ops; + max->v4l2_sd.entity.ops = &max9286_sd_entity_ops; + v4l2_set_subdevdata(&max->v4l2_sd, client); + + v4l2_ctrl_handler_init(&max->ctrl_handler, + ARRAY_SIZE(max9286_controls)); + + if (max->ctrl_handler.error) { + dev_err(max->v4l2_sd.dev, + "Failed to init max9286 controls. ERR: %d!\n", + max->ctrl_handler.error); + return max->ctrl_handler.error; + } + + max->v4l2_sd.ctrl_handler = &max->ctrl_handler; + + for (i = 0; i < ARRAY_SIZE(max9286_controls); i++) { + const struct v4l2_ctrl_config *cfg = + &max9286_controls[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&max->ctrl_handler, cfg, NULL); + if (!ctrl) { + dev_err(max->v4l2_sd.dev, + "Failed to create ctrl %s!\n", cfg->name); + rval = max->ctrl_handler.error; + goto failed_out; + } + } + + max->link_freq = v4l2_ctrl_find(&max->ctrl_handler, V4L2_CID_LINK_FREQ); + max->test_pattern = v4l2_ctrl_find(&max->ctrl_handler, + V4L2_CID_TEST_PATTERN); + + for (i = 0; i < max->nsinks; i++) + max->pad[i].flags = MEDIA_PAD_FL_SINK; + max->pad[MAX_PAD_SOURCE].flags = + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MULTIPLEX; + rval = media_entity_pads_init(&max->v4l2_sd.entity, + NR_OF_MAX_PADS, max->pad); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to init media entity for max9286!\n"); + goto failed_out; + } + + return 0; +failed_out: + media_entity_cleanup(&max->v4l2_sd.entity); + v4l2_ctrl_handler_free(&max->ctrl_handler); + return rval; +} + +/* + * Get the output link order + * By default: + * bits[7:6] 11: Link 3 is 4th in the CSI-2 output order + * bits[5:4] 10: Link 2 is 3rd in the CSI-2 output order + * bits[3:2] 01: Link 1 is 2nd in the CSI-2 output order + * bits[1:0] 00: Link 0 is 1st in the CSI-2 output order + */ +u8 get_output_link_order(struct max9286 *max) +{ + u8 val = 0xE4, i; + u8 order_config[14][3] = { + {1, 8, 0x27}, + {1, 4, 0xC6}, + {1, 2, 0xE1}, + {1, 1, 0xE4}, + {2, 0xC, 0x4E}, + {2, 0xA, 0x72}, + {2, 0x9, 0x78}, + {2, 0x6, 0xD2}, + {2, 0x5, 0xD8}, + {2, 0x3, 0xE4}, + {3, 0xE, 0x93}, + {3, 0xD, 0x9C}, + {3, 0xB, 0xB4}, + {3, 0x7, 0xE4}, + }; + + if (max->total_sensor_num < 4) { + for (i = 0; i < 14; i++) { + if ((max->total_sensor_num == order_config[i][0]) + && (max->sensor_present == order_config[i][1])) + return order_config[i][2]; + } + } + + /* sensor_num = 4 will return 0xE4 */ + return val; +} + +/* MAX9286 initial setup and Reverse channel setup */ +static int max9286_init(struct max9286 *max, struct i2c_client *client) +{ + int i, rval; + unsigned int val, lval; + u8 mval, slval, tmval; + + usleep_range(10000, 11000); + + rval = regmap_read(max->regmap8, DS_MAX9286_DEVID, &val); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to read device ID of MAX9286!\n"); + return rval; + } + dev_info(max->v4l2_sd.dev, "MAX9286 device ID: 0x%X\n", val); + + rval = regmap_write(max->regmap8, DS_CSI_VC_CTL, 0x93); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to disable CSI output!\n"); + return rval; + } + /* All the links are working in Legacy reverse control-channel mode */ + /* Enable Custom Reverse Channel and First Pulse Length */ + rval = regmap_write(max->regmap8, DS_ENCRC_FPL, 0x4F); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to disable PRBS test!\n"); + return rval; + } + /* + * 2ms of delay is required after any analog change to reverse control + * channel for bus timeout and I2C state machine to settle from any + * glitches + */ + usleep_range(2000, 3000); + /* First pulse length rise time changed from 300ns to 200ns */ + rval = regmap_write(max->regmap8, DS_FPL_RT, 0x1E); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to disable PRBS test!\n"); + return rval; + } + usleep_range(2000, 3000); + + /* Enable configuration links */ + max96705_write_register(max, 0, S_MAIN_CTL, 0x43); + usleep_range(5000, 6000); + + /* + * Enable high threshold for reverse channel input buffer + * This increases immunity to power supply noise when the + * coaxial link is used for power as well as signal + */ + max96705_write_register(max, 0, S_RSVD_8, 0x01); + /* Enable change of reverse control parameters */ + + max96705_write_register(max, 0, S_RSVD_97, 0x5F); + + /* Wait 2ms after any change to reverse control channel */ + usleep_range(2000, 3000); + + /* Increase reverse amplitude from 100mV to 170mV to compensate for + * higher threshold + */ + rval = regmap_write(max->regmap8, DS_FPL_RT, 0x19); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to disable PRBS test!\n"); + return rval; + } + usleep_range(2000, 3000); + + /* + * Enable CSI-2 lanes D0, D1, D2, D3 + * Enable CSI-2 DBL (Double Input Mode) + * Enable GMSL DBL for RAWx2 + * Enable RAW12 data type by default + */ + rval = regmap_write(max->regmap8, DS_CSI_DBL_DT, 0xF7); //RAW12 + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to set data type!\n"); + return rval; + } + usleep_range(2000, 3000); + + /* Enable Frame sync Auto-mode for row/column reset on frame sync + * sensors + */ + rval = regmap_write(max->regmap8, DS_FSYNCMODE, 0x00); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to set frame sync mode!\n"); + return rval; + } + usleep_range(2000, 3000); + rval = regmap_write(max->regmap8, DS_OVERLAP_WIN_LOW, 0x00); + rval = regmap_write(max->regmap8, DS_OVERLAP_WIN_HIGH, 0x00); + + rval = regmap_write(max->regmap8, DS_FSYNC_PERIOD_LOW, 0x55); + rval = regmap_write(max->regmap8, DS_FSYNC_PERIOD_MIDDLE, 0xc2); + rval = regmap_write(max->regmap8, DS_FSYNC_PERIOD_HIGH, 0x2C); + + rval = regmap_write(max->regmap8, DS_HIGHIMM, 0x06); + + /* + * Enable DBL + * Edge select: Rising Edge + * Enable HS/VS encoding + */ + max96705_write_register(max, 0, S_CONFIG, 0xD4); + usleep_range(2000, 3000); + + for (i = 0; i < ARRAY_SIZE(max9286_byte_order_settings_12bit); i++) { + rval = max96705_write_register(max, 0, + max9286_byte_order_settings_12bit[i].reg, + max9286_byte_order_settings_12bit[i].val); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to set max9286 byte order\n"); + return rval; + } + } + + /* Detect video links */ + rval = regmap_read(max->regmap8, DS_CONFIGL_VIDEOL_DET, &lval); + if (rval) { + dev_err(max->v4l2_sd.dev, "Failed to read register 0x49!\n"); + return rval; + } + + /* + * Check on which links the sensors are connected + * And also check total number of sensors connected to the deserializer + */ + max->sensor_present = ((lval >> 4) & 0xF) | (lval & 0xF); + + for (i = 0; i < NR_OF_MAX_STREAMS; i++) { + if (max->sensor_present & (0x1 << i)) { + dev_info(max->v4l2_sd.dev, + "Sensor present on deserializer link %d\n", i); + max->total_sensor_num += 1; + } + } + + dev_info(max->v4l2_sd.dev, + "total sensor present = %d", max->total_sensor_num); + dev_info(max->v4l2_sd.dev, + "sensor present on links = %d", max->sensor_present); + + if (!max->total_sensor_num) { + dev_err(max->v4l2_sd.dev, "No sensors connected!\n"); + } else { + dev_info(max->v4l2_sd.dev, + "Total number of sensors connected = %d\n", + max->total_sensor_num); + } + + slval = get_output_link_order(max); + + /* Set link output order */ + rval = regmap_write(max->regmap8, DS_LINK_OUTORD, slval); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to set Link output order!\n"); + return rval; + } + + slval = 0xE0 | max->sensor_present; + + mval = 0; + tmval = 0; + /* + * Setup each serializer individually and their respective I2C slave + * address changed to a unique value by enabling one reverse channel + * at a time via deserializer's DS_FWDCCEN_REVCCEN control register. + * Also create broadcast slave address for MAX96705 serializer. + * After this stage, i2cdetect on I2C-ADAPTER should display the + * below devices + * 10: Sensor address + * 11, 12, 13, 14: Sensors alias addresses + * 41, 42, 43, 44: Serializers alias addresses + * 45: Serializer's broadcast address + * 48: Deserializer's address + */ + + for (i = 1; i <= NR_OF_MAX_SINK_PADS; i++) { + /* Setup the link when the sensor is connected to the link */ + if (((0x1 << (i - 1)) & max->sensor_present) == 0) + continue; + + /* Enable only one reverse channel at a time */ + mval = (0x11 << (i - 1)); + tmval |= (0x11 << (i - 1)); + rval = regmap_write(max->regmap8, DS_FWDCCEN_REVCCEN, mval); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to enable channel for %d!\n", i); + return rval; + } + /* Wait 2ms after enabling reverse channel */ + usleep_range(2000, 3000); + + /* Change Serializer slave address */ + max96705_write_register(max, 0, S_SERADDR, + (S_ADDR_MAX96705 + i) << 1); + /* Unique link 'i' image sensor slave address */ + max96705_write_register(max, i, S_I2C_SOURCE_IS, + (ADDR_AR0231AT_SENSOR + i) << 1); + /* Link 'i' image sensor slave address */ + max96705_write_register(max, i, S_I2C_DST_IS, + ADDR_AR0231AT_SENSOR << 1); + /* Serializer broadcast address */ + max96705_write_register(max, i, S_I2C_SOURCE_SER, + S_ADDR_MAX96705_BROADCAST << 1); + /* Link 'i' serializer address */ + max96705_write_register(max, i, S_I2C_DST_SER, + (S_ADDR_MAX96705 + i) << 1); + } + + /* Enable I2c reverse channels */ + rval = regmap_write(max->regmap8, DS_FWDCCEN_REVCCEN, tmval); + if (rval) { + dev_err(max->v4l2_sd.dev, + "Failed to enable channel for %d!\n", i); + return rval; + } + usleep_range(2000, 3000); + + return 0; +} + +/* Unbind the MAX9286 device driver from the I2C client */ +static int max9286_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct max9286 *max = to_max_9286(subdev); + int i; + + mutex_destroy(&max->max_mutex); + v4l2_ctrl_handler_free(&max->ctrl_handler); + v4l2_device_unregister_subdev(&max->v4l2_sd); + media_entity_cleanup(&max->v4l2_sd.entity); + + for (i = 0; i < NR_OF_MAX_SINK_PADS; i++) { + if (max->sub_devs[i]) { + struct i2c_client *sub_client = + v4l2_get_subdevdata(max->sub_devs[i]); + + i2c_unregister_device(sub_client); + } + max->sub_devs[i] = NULL; + } + + return 0; +} + +/* Called by I2C probe */ +static int max9286_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct max9286 *max; + int i = 0; + int rval = 0; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL); + if (!max) + return -ENOMEM; + + max->pdata = client->dev.platform_data; + + max->nsources = NR_OF_MAX_SOURCE_PADS; + max->nsinks = NR_OF_MAX_SINK_PADS; + max->npads = NR_OF_MAX_PADS; + max->nstreams = NR_OF_MAX_STREAMS; + + max->crop = devm_kcalloc(&client->dev, max->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + max->compose = devm_kcalloc(&client->dev, max->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + max->route = devm_kcalloc(&client->dev, max->nstreams, + sizeof(*max->route), GFP_KERNEL); + max->stream = devm_kcalloc(&client->dev, max->npads, + sizeof(*max->stream), GFP_KERNEL); + + if (!max->crop || !max->compose || !max->route || !max->stream) + return -ENOMEM; + + for (i = 0; i < max->npads; i++) { + max->ffmts[i] = + devm_kcalloc(&client->dev, max->nstreams, + sizeof(struct v4l2_mbus_framefmt), GFP_KERNEL); + if (!max->ffmts[i]) + return -ENOMEM; + + max->stream[i].stream_id = + devm_kcalloc(&client->dev, max->nsinks, + sizeof(int), GFP_KERNEL); + if (!max->stream[i].stream_id) + return -ENOMEM; + } + + for (i = 0; i < max->nstreams; i++) { + max->route[i].sink = i; + max->route[i].source = MAX_PAD_SOURCE; + max->route[i].flags = 0; + } + + for (i = 0; i < max->nsinks; i++) { + max->stream[i].stream_id[0] = i; + max->stream[MAX_PAD_SOURCE].stream_id[i] = i; + } + + max->regmap8 = devm_regmap_init_i2c(client, &max9286_reg_config8); + if (IS_ERR(max->regmap8)) { + dev_err(&client->dev, "Failed to init regmap8!\n"); + return -EIO; + } + + mutex_init(&max->max_mutex); + + v4l2_i2c_subdev_init(&max->v4l2_sd, client, &max9286_sd_ops); + + rval = max9286_register_subdev(max); + if (rval) { + dev_err(&client->dev, + "Failed to register MAX9286 subdevice!\n"); + goto error_mutex_destroy; + } + + rval = max9286_init(max, client); + if (rval) { + dev_err(&client->dev, "Failed to initialise MAX9286!\n"); + goto error_media_entity; + } + + return 0; + +error_media_entity: + media_entity_cleanup(&max->v4l2_sd.entity); + v4l2_ctrl_handler_free(&max->ctrl_handler); +error_mutex_destroy: + mutex_destroy(&max->max_mutex); + + return rval; +} + +#ifdef CONFIG_PM +static int max9286_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct max9286 *max = to_max_9286(subdev); + + return max9286_init(max, client); +} +#else +#define max9286_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id max9286_id_table[] = { + { MAX9286_NAME, 0 }, + {}, +}; + +static const struct dev_pm_ops max9286_pm_ops = { + .resume = max9286_resume, +}; + +static struct i2c_driver max9286_i2c_driver = { + .driver = { + .name = MAX9286_NAME, + .pm = &max9286_pm_ops, + }, + .probe = max9286_probe, + .remove = max9286_remove, + .id_table = max9286_id_table, +}; + +module_i2c_driver(max9286_i2c_driver); + +MODULE_AUTHOR("Kiran Kumar "); +MODULE_AUTHOR("Kun Jiang "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Maxim96705 serializer and Maxim9286 deserializer driver"); diff --git a/include/media/max9286.h b/include/media/max9286.h new file mode 100644 index 000000000000..68d38560a77c --- /dev/null +++ b/include/media/max9286.h @@ -0,0 +1,53 @@ +/* SPDX-LIcense_Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef MAX9286_H +#define MAX9286_H + +#include +#include +#include +#include +#include + +#define MAX9286_NAME "max9286" + +#define PIXEL_ORDER_GRBG 0 +#define PIXEL_ORDER_RGGB 1 +#define PIXEL_ORDER_BGGR 2 +#define PIXEL_ORDER_GBRG 3 + +#define NR_OF_MAX_STREAMS 4 +#define NR_OF_MAX_SOURCE_PADS 1 +#define NR_OF_MAX_SINK_PADS 4 +#define NR_OF_MAX_PADS (NR_OF_MAX_SOURCE_PADS + NR_OF_MAX_SINK_PADS) + +#define MAX_PAD_SOURCE 4 + +#define MAX9286_MIN_WIDTH 640 +#define MAX9286_MIN_HEIGHT 480 +#define MAX9286_MAX_WIDTH 1280 +#define MAX9286_MAX_HEIGHT 800 + +struct max9286_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; + u8 mipi_dt_code; +}; + +struct max9286_subdev_i2c_info { + struct i2c_board_info board_info; + int i2c_adapter_id; + const char suffix; /* suffix for subdevs */ +}; + +struct max9286_pdata { + unsigned int subdev_num; + struct max9286_subdev_i2c_info *subdev_info; + unsigned int reset_gpio; + const char suffix; /* suffix for multi aggregators, abcd... */ +}; + +#endif From c506c872177c02936841a179ba5404efc3b753f8 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:52:57 +0800 Subject: [PATCH 1144/1276] media: i2c: add TI960/953 SER/DES driver TI960 is a versatile sensor hub capable of connecting serialized sensor data received from four independent video data streams through an FPD-Link III interface. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/i2c/Kconfig | 6 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ti960-reg.h | 209 +++++ drivers/media/i2c/ti960.c | 1344 +++++++++++++++++++++++++++++++++ include/media/ti960.h | 62 ++ 5 files changed, 1622 insertions(+) create mode 100644 drivers/media/i2c/ti960-reg.h create mode 100644 drivers/media/i2c/ti960.c create mode 100644 include/media/ti960.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index be954b4d9425..dbcb2131ddf7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1078,6 +1078,12 @@ config VIDEO_MAX9286 ---help--- This is a MAXIM 96705 Serializer and MAXIM 9286 CSI-2 Deserializer driver. +config VIDEO_TI960 + tristate "TI960 driver support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a driver for TI960 Deserializer. + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 96ec00673960..4ad43e933238 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -114,3 +114,4 @@ obj-$(CONFIG_SDR_MAX2175) += max2175.o obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule/ obj-$(CONFIG_VIDEO_TI964) += ti964.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +obj-$(CONFIG_VIDEO_TI960) += ti960.o diff --git a/drivers/media/i2c/ti960-reg.h b/drivers/media/i2c/ti960-reg.h new file mode 100644 index 000000000000..260c17884c7d --- /dev/null +++ b/drivers/media/i2c/ti960-reg.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef TI960_REG_H +#define TI960_REG_H + +struct ti960_register_write { + u8 reg; + u8 val; +}; + +static const struct ti960_register_write ti960_frame_sync_settings[2][5] = { + { + {0x18, 0x00}, /* Disable frame sync. */ + {0x19, 0x00}, + {0x1a, 0x02}, + {0x1b, 0x0a}, + {0x1c, 0xd3}, + }, + { + {0x19, 0x01}, /* Frame sync high time.*/ + {0x1a, 0x15}, + {0x1b, 0x09}, /* Frame sync low time. */ + {0x1c, 0xC3}, + {0x18, 0x01}, /* Enable frame sync. and use high/low mode */ + } +}; + +static const struct ti960_register_write ti960_gpio_settings[] = { + {0x10, 0x81}, + {0x11, 0x85}, + {0x12, 0x89}, + {0x13, 0x8d}, +}; + +static const struct ti960_register_write ti960_init_settings[] = { + {0x0c, 0x0f}, /* RX_PORT_CTL */ + {0x1f, 0x06}, /* CSI_PLL_CTL */ + {0x4c, 0x01}, /* FPD3_PORT_SEL */ + {0x58, 0x5e}, /* BCC_CONFIG */ + {0x5c, 0xb0}, /* SER_ALIAS_ID */ + {0x5d, 0x6c}, /* SlaveID[0] */ + {0x65, 0x60}, /* SlaveAlias[0] */ + {0x6d, 0x7c}, /* PORT_CONFIG */ + {0x7c, 0x01}, /* PORT_CONFIG2 */ + {0x70, 0x2b}, /* RAW10_ID */ + {0x71, 0x2c}, /* RAW12_ID */ + {0x72, 0xe4}, /* CSI_VC_MAP */ + {0x4c, 0x12}, /* FPD3_PORT_SEL */ + {0x58, 0x5e}, + {0x5c, 0xb2}, + {0x5d, 0x6c}, + {0x65, 0x62}, + {0x6d, 0x7c}, + {0x7c, 0x01}, + {0x70, 0x2b}, + {0x71, 0x2c}, + {0x72, 0xee}, /* CSI_VC_MAP */ + {0x4c, 0x24}, /* FPD3_PORT_SEL */ + {0x58, 0x5e}, + {0x5c, 0xb4}, + {0x5d, 0x6c}, + {0x65, 0x64}, + {0x6d, 0x7c}, + {0x7c, 0x01}, + {0x70, 0x2b}, + {0x71, 0x2c}, + {0x72, 0xe4}, + {0x4c, 0x38}, /* FPD3_PORT_SEL */ + {0x58, 0x5e}, + {0x5c, 0xb6}, + {0x5d, 0x6c}, + {0x65, 0x66}, + {0x6d, 0x7c}, + {0x7c, 0x01}, + {0x70, 0x2b}, + {0x71, 0x2c}, + {0x72, 0xe4}, +}; + +static const struct ti960_register_write ti953_init_settings[] = { + {0x4c, 0x01}, + {0xb0, 0x04}, + {0xb1, 0x03}, + {0xb2, 0x25}, + {0xb1, 0x13}, + {0xb2, 0x25}, + {0xb0, 0x04}, + {0xb1, 0x04}, + {0xb2, 0x30}, + {0xb1, 0x14}, + {0xb2, 0x30}, + {0xb0, 0x04}, + {0xb1, 0x06}, + {0xb2, 0x40}, + {0x42, 0x01}, /* SLAVE_ID_ALIAS_1 */ + {0x41, 0x93}, /* SLAVE_ID_ALIAS_0 */ + {0x4c, 0x12}, + {0xb0, 0x08}, + {0xb1, 0x03}, + {0xb2, 0x25}, + {0xb1, 0x13}, + {0xb2, 0x25}, + {0xb0, 0x08}, + {0xb1, 0x04}, + {0xb2, 0x30}, + {0xb1, 0x14}, + {0xb2, 0x30}, + {0xb0, 0x08}, + {0xb1, 0x06}, + {0xb2, 0x40}, + {0x42, 0x01}, + {0x41, 0x93}, + {0x4c, 0x24}, + {0xb0, 0x0c}, + {0xb1, 0x03}, + {0xb2, 0x25}, + {0xb1, 0x13}, + {0xb2, 0x25}, + {0xb0, 0x0c}, + {0xb1, 0x04}, + {0xb2, 0x30}, + {0xb1, 0x14}, + {0xb2, 0x30}, + {0xb0, 0x0c}, + {0xb1, 0x06}, + {0xb2, 0x40}, + {0x42, 0x01}, + {0x41, 0x93}, + {0x4c, 0x38}, + {0xb0, 0x10}, + {0xb1, 0x03}, + {0xb2, 0x25}, + {0xb1, 0x13}, + {0xb2, 0x25}, + {0xb0, 0x10}, + {0xb1, 0x04}, + {0xb2, 0x30}, + {0xb1, 0x14}, + {0xb2, 0x30}, + {0xb0, 0x10}, + {0xb1, 0x06}, + {0xb2, 0x40}, + {0x42, 0x01}, + {0x41, 0x93}, +}; + +static const struct ti960_register_write ti960_init_settings_2[] = { + {0xb0, 0x14}, + {0xb1, 0x03}, + {0xb2, 0x04}, + {0xb1, 0x04}, + {0xb2, 0x04}, + {0x4c, 0x01}, + {0x32, 0x01}, + {0x33, 0x03}, + {0x32, 0x12}, + {0x33, 0x03}, + {0x20, 0x00}, + {0x21, 0x03}, +}; + +static const struct ti960_register_write ti953_init_settings_2[] = { + {0x06, 0x41}, + {0x07, 0x28}, + {0x0e, 0xf0}, +}; + +/* register definition */ +#define TI960_DEVID 0x0 +#define TI960_RESET 0x1 +#define TI960_CSI_PLL_CTL 0x1f +#define TI960_FS_CTL 0x18 +#define TI960_FWD_CTL1 0x20 +#define TI960_RX_PORT_SEL 0x4c +#define TI960_SLAVE_ID0 0x5d +#define TI960_SLAVE_ALIAS_ID0 0x65 +#define TI960_PORT_CONFIG 0x6d +#define TI960_BC_GPIO_CTL0 0x6e +#define TI960_RAW10_ID 0x70 +#define TI960_RAW12_ID 0x71 +#define TI960_PORT_CONFIG2 0x7c +#define TI960_CSI_CTL 0x33 + +/* register value definition */ +#define TI960_POWER_ON 0x1 +#define TI960_POWER_OFF 0x20 +#define TI960_FPD3_RAW10_100MHz 0x7f +#define TI960_FPD3_RAW12_50MHz 0x7d +#define TI960_FPD3_RAW12_75MHz 0x7e +#define TI960_FPD3_CSI 0x7c +#define TI960_RAW12 0x41 +#define TI960_RAW10_NORMAL 0x1 +#define TI960_RAW10_8BIT 0x81 +#define TI960_GPIO0_HIGH 0x09 +#define TI960_GPIO0_LOW 0x08 +#define TI960_GPIO1_HIGH 0x90 +#define TI960_GPIO1_LOW 0x80 +#define TI960_GPIO0_FSIN 0x0a +#define TI960_GPIO1_FSIN 0xa0 +#define TI960_GPIO0_MASK 0x0f +#define TI960_GPIO1_MASK 0xf0 +#define TI960_MIPI_800MBPS 0x2 +#define TI960_MIPI_1600MBPS 0x0 +#define TI960_CSI_ENABLE 0x1 +#define TI960_CSI_CONTS_CLOCK 0x2 +#define TI960_CSI_SKEWCAL 0x40 +#define TI960_FSIN_ENABLE 0x1 +#endif diff --git a/drivers/media/i2c/ti960.c b/drivers/media/i2c/ti960.c new file mode 100644 index 000000000000..2761dbe17bc8 --- /dev/null +++ b/drivers/media/i2c/ti960.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ti960-reg.h" + +struct ti960_subdev { + struct v4l2_subdev *sd; + unsigned short rx_port; + unsigned short fsin_gpio; + unsigned short phy_i2c_addr; + unsigned short alias_i2c_addr; + char sd_name[16]; +}; + +struct ti960 { + struct v4l2_subdev sd; + struct media_pad pad[NR_OF_TI960_PADS]; + struct v4l2_ctrl_handler ctrl_handler; + struct ti960_pdata *pdata; + struct ti960_subdev sub_devs[NR_OF_TI960_SINK_PADS]; + struct crlmodule_platform_data subdev_pdata[NR_OF_TI960_SINK_PADS]; + const char *name; + + struct mutex mutex; + + struct regmap *regmap8; + struct regmap *regmap16; + + struct v4l2_mbus_framefmt *ffmts[NR_OF_TI960_PADS]; + struct rect *crop; + struct rect *compose; + + struct v4l2_subdev_route *ti960_route; + + unsigned int nsinks; + unsigned int nsources; + unsigned int nstreams; + unsigned int npads; + + struct gpio_chip gc; + + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; +}; + +#define to_ti960(_sd) container_of(_sd, struct ti960, sd) + +static const s64 ti960_op_sys_clock[] = {400000000, 800000000}; +static const u8 ti960_op_sys_clock_reg_val[] = { + TI960_MIPI_800MBPS, + TI960_MIPI_1600MBPS +}; + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct ti960_csi_data_format va_csi_data_formats[] = { + { MEDIA_BUS_FMT_SGRBG16_1X16, 16, 16, PIXEL_ORDER_GRBG, 0x2e }, + { MEDIA_BUS_FMT_SRGGB16_1X16, 16, 16, PIXEL_ORDER_RGGB, 0x2e }, + { MEDIA_BUS_FMT_SBGGR16_1X16, 16, 16, PIXEL_ORDER_BGGR, 0x2e }, + { MEDIA_BUS_FMT_SGBRG16_1X16, 16, 16, PIXEL_ORDER_GBRG, 0x2e }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, PIXEL_ORDER_GRBG, 0x2c }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, PIXEL_ORDER_RGGB, 0x2c }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, PIXEL_ORDER_BGGR, 0x2c }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, PIXEL_ORDER_GBRG, 0x2c }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, PIXEL_ORDER_GRBG, 0x2b }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, PIXEL_ORDER_RGGB, 0x2b }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, PIXEL_ORDER_BGGR, 0x2b }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, PIXEL_ORDER_GBRG, 0x2b }, +}; + +static const uint32_t ti960_supported_codes_pad[] = { + MEDIA_BUS_FMT_SBGGR16_1X16, + MEDIA_BUS_FMT_SGBRG16_1X16, + MEDIA_BUS_FMT_SGRBG16_1X16, + MEDIA_BUS_FMT_SRGGB16_1X16, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + 0, +}; + +static const uint32_t *ti960_supported_codes[] = { + ti960_supported_codes_pad, +}; + +static struct regmap_config ti960_reg_config8 = { + .reg_bits = 8, + .val_bits = 8, +}; + +static struct regmap_config ti960_reg_config16 = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int ti953_reg_write(struct ti960 *va, unsigned short rx_port, + unsigned char reg, unsigned char val) +{ + int ret; + int retry, timeout = 10; + struct i2c_client *client = v4l2_get_subdevdata(&va->sd); + unsigned short ser_alias = va->pdata->subdev_info[rx_port].ser_alias; + + client->addr = ser_alias; + for (retry = 0; retry < timeout; retry++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) { + dev_err(va->sd.dev, "ti953 reg write ret=%x", ret); + usleep_range(5000, 6000); + } else + break; + } + + client->addr = TI960_I2C_ADDRESS; + if (retry >= timeout) { + dev_err(va->sd.dev, + "%s:write reg failed: port=%2x, addr=%2x, reg=%2x\n", + __func__, rx_port, ser_alias, reg); + return -EREMOTEIO; + } + + return 0; +} + +static int ti960_reg_read(struct ti960 *va, unsigned char reg, unsigned int *val) +{ + int ret, retry, timeout = 10; + + for (retry = 0; retry < timeout; retry++) { + ret = regmap_read(va->regmap8, reg, val); + if (ret < 0) { + dev_err(va->sd.dev, "960 reg read ret=%x", ret); + usleep_range(5000, 6000); + } else { + break; + } + } + + if (retry >= timeout) { + dev_err(va->sd.dev, + "%s:devid read failed: reg=%2x, ret=%d\n", + __func__, reg, ret); + return -EREMOTEIO; + } + + return 0; +} + +static int ti960_reg_set_bit(struct ti960 *va, unsigned char reg, + unsigned char bit, unsigned char val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(va->regmap8, reg, ®_val); + if (ret) + return ret; + if (val) + reg_val |= 1 << bit; + else + reg_val &= ~(1 << bit); + + return regmap_write(va->regmap8, reg, reg_val); +} + +static int ti960_map_phy_i2c_addr(struct ti960 *va, unsigned short rx_port, + unsigned short addr) +{ + int rval; + + rval = regmap_write(va->regmap8, TI960_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + return regmap_write(va->regmap8, TI960_SLAVE_ID0, addr); +} + +static int ti960_map_alias_i2c_addr(struct ti960 *va, unsigned short rx_port, + unsigned short addr) +{ + int rval; + + rval = regmap_write(va->regmap8, TI960_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + return regmap_write(va->regmap8, TI960_SLAVE_ALIAS_ID0, addr); +} + +static int ti960_fsin_gpio_init(struct ti960 *va, unsigned short rx_port, + unsigned short fsin_gpio) +{ + int rval; + int reg_val; + + dev_dbg(va->sd.dev, "%s\n", __func__); + rval = regmap_read(va->regmap8, TI960_FS_CTL, ®_val); + if (rval) { + dev_dbg(va->sd.dev, "Failed to read gpio status.\n"); + return rval; + } + + if (!reg_val & TI960_FSIN_ENABLE) { + dev_dbg(va->sd.dev, "FSIN not enabled, skip config FSIN GPIO.\n"); + return 0; + } + + rval = regmap_write(va->regmap8, TI960_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) + return rval; + + rval = regmap_read(va->regmap8, TI960_BC_GPIO_CTL0, ®_val); + if (rval) { + dev_dbg(va->sd.dev, "Failed to read gpio status.\n"); + return rval; + } + + if (fsin_gpio == 0) { + reg_val &= ~TI960_GPIO0_MASK; + reg_val |= TI960_GPIO0_FSIN; + } else { + reg_val &= ~TI960_GPIO1_MASK; + reg_val |= TI960_GPIO1_FSIN; + } + + rval = regmap_write(va->regmap8, TI960_BC_GPIO_CTL0, reg_val); + if (rval) + dev_dbg(va->sd.dev, "Failed to set gpio.\n"); + + return rval; +} + +static int ti960_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ti960 *va = to_ti960(sd); + int i; + + for (i = 0; i < min(va->nstreams, route->num_routes); ++i) { + route->routes[i].sink_pad = va->ti960_route[i].sink_pad; + route->routes[i].sink_stream = va->ti960_route[i].sink_stream; + route->routes[i].source_pad = va->ti960_route[i].source_pad; + route->routes[i].source_stream = va->ti960_route[i].source_stream; + route->routes[i].flags = va->ti960_route[i].flags; + } + + route->num_routes = i; + + return 0; +} + +static int ti960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ti960 *va = to_ti960(sd); + int i, j, ret = 0; + + for (i = 0; i < min(route->num_routes, va->nstreams); ++i) { + struct v4l2_subdev_route *t = &route->routes[i]; + + if (t->sink_stream > va->nstreams - 1 || + t->source_stream > va->nstreams - 1) + continue; + + if (t->source_pad != TI960_PAD_SOURCE) + continue; + + for (j = 0; j < va->nstreams; j++) { + if (t->sink_pad == va->ti960_route[j].sink_pad && + t->source_pad == va->ti960_route[j].source_pad && + t->sink_stream == va->ti960_route[j].sink_stream && + t->source_stream == va->ti960_route[j].source_stream) + break; + } + + if (j == va->nstreams) + continue; + + if (t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + va->ti960_route[j].flags |= + V4L2_SUBDEV_ROUTE_FL_ACTIVE; + else if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + va->ti960_route[j].flags &= + (~V4L2_SUBDEV_ROUTE_FL_ACTIVE); + } + + return ret; +} + +static int ti960_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ti960 *va = to_ti960(sd); + const uint32_t *supported_code = + ti960_supported_codes[code->pad]; + bool next_stream = false; + int i; + + if (code->stream & V4L2_SUBDEV_FLAG_NEXT_STREAM) { + next_stream = true; + code->stream &= ~V4L2_SUBDEV_FLAG_NEXT_STREAM; + } + + if (code->stream > va->nstreams) + return -EINVAL; + + if (next_stream) { + if (!(va->pad[code->pad].flags & MEDIA_PAD_FL_MULTIPLEX)) + return -EINVAL; + if (code->stream < va->nstreams - 1) { + code->stream++; + return 0; + } else { + return -EINVAL; + } + } + + for (i = 0; supported_code[i]; i++) { + if (i == code->index) { + code->code = supported_code[i]; + return 0; + } + } + + return -EINVAL; +} + +static const struct ti960_csi_data_format + *ti960_validate_csi_data_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(va_csi_data_formats); i++) { + if (va_csi_data_formats[i].code == code) + return &va_csi_data_formats[i]; + } + + return &va_csi_data_formats[0]; +} + +static int ti960_get_frame_desc(struct v4l2_subdev *sd, + unsigned int pad, struct v4l2_mbus_frame_desc *desc) +{ + struct ti960 *va = to_ti960(sd); + struct v4l2_mbus_frame_desc_entry *entry = desc->entry; + u8 vc = 0; + int i; + + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + desc->num_entries = min_t(int, va->nstreams, V4L2_FRAME_DESC_ENTRY_MAX); + + for (i = 0; i < desc->num_entries; i++) { + struct v4l2_mbus_framefmt *ffmt = + &va->ffmts[TI960_PAD_SOURCE][i]; + const struct ti960_csi_data_format *csi_format = + ti960_validate_csi_data_format(ffmt->code); + + entry->two_dim.width = ffmt->width; + entry->two_dim.height = ffmt->height; + entry->pixelcode = ffmt->code; + entry->bus.csi2.channel = vc++; + entry->bpp = csi_format->compressed; + entry++; + } + + return 0; +} + +static struct v4l2_mbus_framefmt * +__ti960_get_ffmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, unsigned int which, + unsigned int stream) +{ + struct ti960 *va = to_ti960(subdev); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(subdev, cfg, pad); + else + return &va->ffmts[pad][stream]; +} + +static int ti960_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ti960 *va = to_ti960(subdev); + + if (fmt->stream > va->nstreams) + return -EINVAL; + + mutex_lock(&va->mutex); + fmt->format = *__ti960_get_ffmt(subdev, cfg, fmt->pad, + fmt->which, fmt->stream); + mutex_unlock(&va->mutex); + + dev_dbg(subdev->dev, "subdev_format: which: %s, pad: %d, stream: %d.\n", + fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE ? + "V4L2_SUBDEV_FORMAT_ACTIVE" : "V4L2_SUBDEV_FORMAT_TRY", + fmt->pad, fmt->stream); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + fmt->format.width, fmt->format.height, fmt->format.code); + + return 0; +} + +static int ti960_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ti960 *va = to_ti960(subdev); + const struct ti960_csi_data_format *csi_format; + struct v4l2_mbus_framefmt *ffmt; + + if (fmt->stream > va->nstreams) + return -EINVAL; + + csi_format = ti960_validate_csi_data_format( + fmt->format.code); + + mutex_lock(&va->mutex); + ffmt = __ti960_get_ffmt(subdev, cfg, fmt->pad, fmt->which, + fmt->stream); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = csi_format->code; + } + fmt->format = *ffmt; + mutex_unlock(&va->mutex); + + dev_dbg(subdev->dev, "framefmt: width: %d, height: %d, code: 0x%x.\n", + ffmt->width, ffmt->height, ffmt->code); + + return 0; +} + +static int ti960_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(subdev, fh->pad, 0); + + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = TI960_PAD_SOURCE, + .format = { + .width = TI960_MAX_WIDTH, + .height = TI960_MAX_HEIGHT, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + }, + .stream = 0, + }; + + *try_fmt = fmt.format; + + return 0; +} + +static int ti960_registered(struct v4l2_subdev *subdev) +{ + struct ti960 *va = to_ti960(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int i, j, k, l, rval; + + for (i = 0, k = 0; i < va->pdata->subdev_num; i++) { + struct ti960_subdev_info *info = + &va->pdata->subdev_info[i]; + struct crlmodule_platform_data *pdata = + (struct crlmodule_platform_data *) + info->board_info.platform_data; + + if (k >= va->nsinks) + break; + + /* + * The sensors should not share the same pdata structure. + * Clone the pdata for each sensor. + */ + memcpy(&va->subdev_pdata[k], pdata, sizeof(*pdata)); + if (va->subdev_pdata[k].xshutdown != 0 && + va->subdev_pdata[k].xshutdown != 1) { + dev_err(va->sd.dev, "xshutdown(%d) must be 0 or 1 to connect.\n", + va->subdev_pdata[k].xshutdown); + return -EINVAL; + } + + /* If 0 is xshutdown, then 1 would be FSIN, vice versa. */ + va->sub_devs[k].fsin_gpio = 1 - va->subdev_pdata[k].xshutdown; + + /* Spin sensor subdev suffix name */ + va->subdev_pdata[k].suffix = info->suffix; + + /* + * Change the gpio value to have xshutdown + * and rx port included, so in gpio_set those + * can be caculated from it. + */ + va->subdev_pdata[k].xshutdown += va->gc.base + + info->rx_port * NR_OF_GPIOS_PER_PORT; + info->board_info.platform_data = &va->subdev_pdata[k]; + + if (!info->phy_i2c_addr || !info->board_info.addr) { + dev_err(va->sd.dev, "can't find the physical and alias addr.\n"); + return -EINVAL; + } + + /* Map PHY I2C address. */ + rval = ti960_map_phy_i2c_addr(va, info->rx_port, + info->phy_i2c_addr); + if (rval) + return rval; + + /* Map 7bit ALIAS I2C address. */ + rval = ti960_map_alias_i2c_addr(va, info->rx_port, + info->board_info.addr << 1); + if (rval) + return rval; + + va->sub_devs[k].sd = v4l2_i2c_new_subdev_board( + va->sd.v4l2_dev, client->adapter, + &info->board_info, 0); + if (!va->sub_devs[k].sd) { + dev_err(va->sd.dev, + "can't create new i2c subdev %c\n", + info->suffix); + continue; + } + va->sub_devs[k].rx_port = info->rx_port; + va->sub_devs[k].phy_i2c_addr = info->phy_i2c_addr; + va->sub_devs[k].alias_i2c_addr = info->board_info.addr; + memcpy(va->sub_devs[k].sd_name, + va->subdev_pdata[k].module_name, + min(sizeof(va->sub_devs[k].sd_name) - 1, + sizeof(va->subdev_pdata[k].module_name) - 1)); + + for (j = 0; j < va->sub_devs[k].sd->entity.num_pads; j++) { + if (va->sub_devs[k].sd->entity.pads[j].flags & + MEDIA_PAD_FL_SOURCE) + break; + } + + if (j == va->sub_devs[k].sd->entity.num_pads) { + dev_warn(va->sd.dev, + "no source pad in subdev %c\n", + info->suffix); + return -ENOENT; + } + + for (l = 0; l < va->nsinks; l++) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_create_link( +#else + rval = media_create_pad_link( +#endif + &va->sub_devs[k].sd->entity, j, + &va->sd.entity, l, 0); + if (rval) { + dev_err(va->sd.dev, + "can't create link to %c\n", + info->suffix); + return -EINVAL; + } + } + k++; + } + + return 0; +} + +static int ti960_set_power(struct v4l2_subdev *subdev, int on) +{ + struct ti960 *va = to_ti960(subdev); + int ret; + u8 val; + + ret = regmap_write(va->regmap8, TI960_RESET, + (on) ? TI960_POWER_ON : TI960_POWER_OFF); + if (ret || !on) + return ret; + + /* Configure MIPI clock bsaed on control value. */ + ret = regmap_write(va->regmap8, TI960_CSI_PLL_CTL, + ti960_op_sys_clock_reg_val[ + v4l2_ctrl_g_ctrl(va->link_freq)]); + if (ret) + return ret; + val = TI960_CSI_ENABLE; + val |= TI960_CSI_CONTS_CLOCK; + /* Enable skew calculation when 1.6Gbps output is enabled. */ + if (v4l2_ctrl_g_ctrl(va->link_freq)) + val |= TI960_CSI_SKEWCAL; + return regmap_write(va->regmap8, TI960_CSI_CTL, val); +} + +static bool ti960_broadcast_mode(struct v4l2_subdev *subdev) +{ + struct ti960 *va = to_ti960(subdev); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_subdev *sd; + char *sd_name = NULL; + bool first = true; + unsigned int h = 0, w = 0, code = 0; + bool single_stream = true; + int i, rval; + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + struct media_pad *remote_pad = + media_entity_remote_pad(&va->pad[i]); + + if (!remote_pad) + continue; + + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = remote_pad->index; + fmt.stream = 0; + + rval = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (rval) + return false; + + if (first) { + sd_name = va->sub_devs[i].sd_name; + h = fmt.format.height; + w = fmt.format.width; + code = fmt.format.code; + first = false; + } else { + if (strncmp(sd_name, va->sub_devs[i].sd_name, 16)) + return false; + + if (h != fmt.format.height || w != fmt.format.width + || code != fmt.format.code) + return false; + + single_stream = false; + } + } + + if (single_stream) + return false; + + return true; +} + +static int ti960_rx_port_config(struct ti960 *va, int sink, int rx_port) +{ + int rval; + + /* Select RX port. */ + rval = regmap_write(va->regmap8, TI960_RX_PORT_SEL, + (rx_port << 4) + (1 << rx_port)); + if (rval) { + dev_err(va->sd.dev, "Failed to select RX port.\n"); + return rval; + } + + rval = regmap_write(va->regmap8, TI960_PORT_CONFIG, + TI960_FPD3_CSI); + if (rval) { + dev_err(va->sd.dev, "Failed to set port config.\n"); + return rval; + } + + /* + * TODO: CSI VC MAPPING. + */ + + return 0; +} + +static int ti960_map_subdevs_addr(struct ti960 *va) +{ + unsigned short rx_port, phy_i2c_addr, alias_i2c_addr; + int i, rval; + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + rx_port = va->sub_devs[i].rx_port; + phy_i2c_addr = va->sub_devs[i].phy_i2c_addr; + alias_i2c_addr = va->sub_devs[i].alias_i2c_addr; + + if (!phy_i2c_addr || !alias_i2c_addr) + continue; + + rval = ti960_map_phy_i2c_addr(va, rx_port, phy_i2c_addr); + if (rval) + return rval; + + /* set 7bit alias i2c addr */ + rval = ti960_map_alias_i2c_addr(va, rx_port, + alias_i2c_addr << 1); + if (rval) + return rval; + } + + return 0; +} + +static int ti960_find_subdev_index(struct ti960 *va, struct v4l2_subdev *sd) +{ + int i; + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + if (va->sub_devs[i].sd == sd) + return i; + } + + WARN_ON(1); + + return -EINVAL; +} + +static int ti960_set_frame_sync(struct ti960 *va, int enable) +{ + int i, rval; + int index = !!enable; + + for (i = 0; i < ARRAY_SIZE(ti960_frame_sync_settings[index]); i++) { + rval = regmap_write(va->regmap8, + ti960_frame_sync_settings[index][i].reg, + ti960_frame_sync_settings[index][i].val); + if (rval) { + dev_err(va->sd.dev, "Failed to %s frame sync\n", + enable ? "enable" : "disable"); + return rval; + } + } + + return 0; +} + +static int ti960_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ti960 *va = to_ti960(subdev); + struct v4l2_subdev *sd; + int i, j, rval; + bool broadcast; + unsigned int rx_port; + int sd_idx = -1; + DECLARE_BITMAP(rx_port_enabled, 32); + + dev_dbg(va->sd.dev, "TI960 set stream, enable %d\n", enable); + + broadcast = ti960_broadcast_mode(subdev); + if (enable) + dev_info(va->sd.dev, "TI960 in %s mode", + broadcast ? "broadcast" : "non broadcast"); + + bitmap_zero(rx_port_enabled, 32); + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + struct media_pad *remote_pad = + media_entity_remote_pad(&va->pad[i]); + + if (!remote_pad) + continue; + + /* Find ti960 subdev */ + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + j = ti960_find_subdev_index(va, sd); + if (j < 0) + return -EINVAL; + rx_port = va->sub_devs[j].rx_port; + rval = ti960_rx_port_config(va, i, rx_port); + if (rval < 0) + return rval; + + bitmap_set(rx_port_enabled, rx_port, 1); + + if (broadcast && sd_idx == -1) { + sd_idx = j; + } else if (broadcast) { + rval = ti960_map_alias_i2c_addr(va, rx_port, + va->sub_devs[sd_idx].alias_i2c_addr << 1); + if (rval < 0) + return rval; + } else { + /* Stream on/off sensor */ + dev_err(va->sd.dev, + "set stream for %s, enable %d\n", + sd->name, enable); + rval = v4l2_subdev_call(sd, video, s_stream, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set stream for %s, enable %d\n", + sd->name, enable); + return rval; + } + + /* RX port fordward */ + rval = ti960_reg_set_bit(va, TI960_FWD_CTL1, + rx_port + 4, !enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to forward RX port%d. enable %d\n", + i, enable); + return rval; + } + + } + } + + if (broadcast) { + if (sd_idx < 0) { + dev_err(va->sd.dev, "No sensor connected!\n"); + return -ENODEV; + } + sd = va->sub_devs[sd_idx].sd; + rval = v4l2_subdev_call(sd, video, s_stream, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set stream for %s. enable %d\n", + sd->name, enable); + return rval; + } + + rval = ti960_set_frame_sync(va, enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to set frame sync.\n"); + return rval; + } + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + if (enable && test_bit(i, rx_port_enabled)) { + rval = ti960_fsin_gpio_init(va, + va->sub_devs[i].rx_port, + va->sub_devs[i].fsin_gpio); + if (rval) { + dev_err(va->sd.dev, + "Failed to enable frame sync gpio init.\n"); + return rval; + } + } + } + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + if (!test_bit(i, rx_port_enabled)) + continue; + + /* RX port fordward */ + rval = ti960_reg_set_bit(va, TI960_FWD_CTL1, + i + 4, !enable); + if (rval) { + dev_err(va->sd.dev, + "Failed to forward RX port%d. enable %d\n", + i, enable); + return rval; + } + } + + /* + * Restore each subdev i2c address as we may + * touch it later. + */ + rval = ti960_map_subdevs_addr(va); + if (rval) + return rval; + } + + return 0; +} + +static struct v4l2_subdev_internal_ops ti960_sd_internal_ops = { + .open = ti960_open, + .registered = ti960_registered, +}; + +static bool ti960_sd_has_route(struct media_entity *entity, + unsigned int pad0, unsigned int pad1, int *stream) +{ + struct ti960 *va = to_ti960(media_entity_to_v4l2_subdev(entity)); + int i; + + if (va == NULL || stream == NULL || + *stream >= va->nstreams || *stream < 0) + return false; + + for (i = 0; i < va->nstreams; ++i) { + if ((va->ti960_route[*stream].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) && + ((va->ti960_route[*stream].source_pad == pad0 && + va->ti960_route[*stream].sink_pad == pad1) || + (va->ti960_route[*stream].source_pad == pad1 && + va->ti960_route[*stream].sink_pad == pad0))) + return true; + } + + return false; +} + +static const struct media_entity_operations ti960_sd_entity_ops = { + .has_route = ti960_sd_has_route, +}; + +static const struct v4l2_subdev_video_ops ti960_sd_video_ops = { + .s_stream = ti960_set_stream, +}; + +static const struct v4l2_subdev_core_ops ti960_core_subdev_ops = { + .s_power = ti960_set_power, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .queryctrl = v4l2_subdev_queryctrl, +#endif +}; + +static int ti960_s_ctrl(struct v4l2_ctrl *ctrl) +{ + return 0; +} + +static const struct v4l2_ctrl_ops ti960_ctrl_ops = { + .s_ctrl = ti960_s_ctrl, +}; + +static const struct v4l2_ctrl_config ti960_controls[] = { + { + .ops = &ti960_ctrl_ops, + .id = V4L2_CID_LINK_FREQ, + .name = "V4L2_CID_LINK_FREQ", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .max = ARRAY_SIZE(ti960_op_sys_clock) - 1, + .min = 0, + .step = 0, + .def = 0, + .qmenu_int = ti960_op_sys_clock, + }, + { + .ops = &ti960_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .name = "V4L2_CID_TEST_PATTERN", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 1, + .min = 0, + .step = 1, + .def = 0, + }, +}; + +static const struct v4l2_subdev_pad_ops ti960_sd_pad_ops = { + .get_fmt = ti960_get_format, + .set_fmt = ti960_set_format, + .get_frame_desc = ti960_get_frame_desc, + .enum_mbus_code = ti960_enum_mbus_code, + .set_routing = ti960_set_routing, + .get_routing = ti960_get_routing, +}; + +static struct v4l2_subdev_ops ti960_sd_ops = { + .core = &ti960_core_subdev_ops, + .video = &ti960_sd_video_ops, + .pad = &ti960_sd_pad_ops, +}; + +static int ti960_register_subdev(struct ti960 *va) +{ + int i, rval; + struct i2c_client *client = v4l2_get_subdevdata(&va->sd); + + v4l2_subdev_init(&va->sd, &ti960_sd_ops); + snprintf(va->sd.name, sizeof(va->sd.name), "TI960 %c", + va->pdata->suffix); + + va->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_SUBSTREAMS; + + va->sd.internal_ops = &ti960_sd_internal_ops; + va->sd.entity.ops = &ti960_sd_entity_ops; + + v4l2_set_subdevdata(&va->sd, client); + + v4l2_ctrl_handler_init(&va->ctrl_handler, + ARRAY_SIZE(ti960_controls)); + + if (va->ctrl_handler.error) { + dev_err(va->sd.dev, + "Failed to init ti960 controls. ERR: %d!\n", + va->ctrl_handler.error); + return va->ctrl_handler.error; + } + + va->sd.ctrl_handler = &va->ctrl_handler; + + for (i = 0; i < ARRAY_SIZE(ti960_controls); i++) { + const struct v4l2_ctrl_config *cfg = + &ti960_controls[i]; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&va->ctrl_handler, cfg, NULL); + if (!ctrl) { + dev_err(va->sd.dev, + "Failed to create ctrl %s!\n", cfg->name); + rval = va->ctrl_handler.error; + goto failed_out; + } + } + + va->link_freq = v4l2_ctrl_find(&va->ctrl_handler, V4L2_CID_LINK_FREQ); + va->test_pattern = v4l2_ctrl_find(&va->ctrl_handler, + V4L2_CID_TEST_PATTERN); + + for (i = 0; i < va->nsinks; i++) + va->pad[i].flags = MEDIA_PAD_FL_SINK; + va->pad[TI960_PAD_SOURCE].flags = + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MULTIPLEX; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + rval = media_entity_init(&va->sd.entity, NR_OF_TI960_PADS, va->pad, 0); +#else + rval = media_entity_pads_init(&va->sd.entity, + NR_OF_TI960_PADS, va->pad); +#endif + if (rval) { + dev_err(va->sd.dev, + "Failed to init media entity for ti960!\n"); + goto failed_out; + } + + return 0; + +failed_out: + v4l2_ctrl_handler_free(&va->ctrl_handler); + return rval; +} + +static int ti960_init(struct ti960 *va) +{ + unsigned int reset_gpio = va->pdata->reset_gpio; + int i, rval; + unsigned int val; + + gpio_set_value(reset_gpio, 1); + usleep_range(2000, 3000); + dev_err(va->sd.dev, "Setting reset gpio %d to 1.\n", reset_gpio); + + rval = ti960_reg_read(va, TI960_DEVID, &val); + if (rval) { + dev_err(va->sd.dev, "Failed to read device ID of TI960!\n"); + return rval; + } + dev_info(va->sd.dev, "TI960 device ID: 0x%X\n", val); + + for (i = 0; i < ARRAY_SIZE(ti960_gpio_settings); i++) { + rval = regmap_write(va->regmap8, + ti960_gpio_settings[i].reg, + ti960_gpio_settings[i].val); + if (rval) { + dev_err(va->sd.dev, + "Failed to write TI960 gpio setting, reg %2x, val %2x\n", + ti960_gpio_settings[i].reg, ti960_gpio_settings[i].val); + return rval; + } + } + usleep_range(10000, 11000); + + for (i = 0; i < ARRAY_SIZE(ti960_init_settings); i++) { + rval = regmap_write(va->regmap8, + ti960_init_settings[i].reg, + ti960_init_settings[i].val); + if (rval) { + dev_err(va->sd.dev, + "Failed to write TI960 init setting, reg %2x, val %2x\n", + ti960_init_settings[i].reg, ti960_init_settings[i].val); + return rval; + } + } + + /* wait for ti953 ready */ + usleep_range(200000, 300000); + + for (i = 0; i < ARRAY_SIZE(ti953_init_settings); i++) { + rval = ti953_reg_write(va, 0, + ti953_init_settings[i].reg, + ti953_init_settings[i].val); + if (rval) { + dev_err(va->sd.dev, "port %d, ti953 write timeout %d\n", 0, rval); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(ti960_init_settings_2); i++) { + rval = regmap_write(va->regmap8, + ti960_init_settings_2[i].reg, + ti960_init_settings_2[i].val); + if (rval) { + dev_err(va->sd.dev, + "Failed to write TI960 init setting 2, reg %2x, val %2x\n", + ti960_init_settings_2[i].reg, ti960_init_settings_2[i].val); + return rval; + } + } + + for (i = 0; i < ARRAY_SIZE(ti953_init_settings_2); i++) { + rval = ti953_reg_write(va, 0, + ti953_init_settings_2[i].reg, + ti953_init_settings_2[i].val); + if (rval) { + dev_err(va->sd.dev, "port %d, ti953 write timeout %d\n", 0, rval); + break; + } + } + + /* reset and power for ti953 */ + ti953_reg_write(va, 0, 0x0d, 00); + usleep_range(50000, 60000); + ti953_reg_write(va, 0, 0x0d, 0x3); + + rval = ti960_map_subdevs_addr(va); + if (rval) + return rval; + + return 0; +} + +static void ti960_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) +{ +} + +static int ti960_gpio_direction_output(struct gpio_chip *chip, + unsigned int gpio, int level) +{ + return 0; +} + +static int ti960_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct ti960 *va; + int i, rval = 0; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + va = devm_kzalloc(&client->dev, sizeof(*va), GFP_KERNEL); + if (!va) + return -ENOMEM; + + va->pdata = client->dev.platform_data; + + va->nsources = NR_OF_TI960_SOURCE_PADS; + va->nsinks = NR_OF_TI960_SINK_PADS; + va->npads = NR_OF_TI960_PADS; + va->nstreams = NR_OF_TI960_STREAMS; + + va->crop = devm_kcalloc(&client->dev, va->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + + va->compose = devm_kcalloc(&client->dev, va->npads, + sizeof(struct v4l2_rect), GFP_KERNEL); + + if (!va->crop || !va->compose) + return -ENOMEM; + + for (i = 0; i < va->npads; i++) { + va->ffmts[i] = devm_kcalloc(&client->dev, va->nstreams, + sizeof(struct v4l2_mbus_framefmt), + GFP_KERNEL); + if (!va->ffmts[i]) + return -ENOMEM; + } + + va->ti960_route = devm_kcalloc(&client->dev, NR_OF_TI960_STREAMS, + sizeof(struct v4l2_subdev_routing), GFP_KERNEL); + + if (!va->ti960_route) + return -ENOMEM; + + for (i = 0; i < va->nstreams; i++) { + va->ti960_route[i].sink_pad = i / 2; + va->ti960_route[i].sink_stream = i % 2; + va->ti960_route[i].source_pad = TI960_PAD_SOURCE; + va->ti960_route[i].source_stream = i % 2; + va->ti960_route[i].flags = MEDIA_PAD_FL_MULTIPLEX; + } + + va->regmap8 = devm_regmap_init_i2c(client, + &ti960_reg_config8); + if (IS_ERR(va->regmap8)) { + dev_err(&client->dev, "Failed to init regmap8!\n"); + return -EIO; + } + + va->regmap16 = devm_regmap_init_i2c(client, + &ti960_reg_config16); + if (IS_ERR(va->regmap16)) { + dev_err(&client->dev, "Failed to init regmap16!\n"); + return -EIO; + } + + mutex_init(&va->mutex); + v4l2_i2c_subdev_init(&va->sd, client, &ti960_sd_ops); + rval = ti960_register_subdev(va); + if (rval) { + dev_err(&client->dev, "Failed to register va subdevice!\n"); + return rval; + } + + if (devm_gpio_request_one(va->sd.dev, va->pdata->reset_gpio, 0, + "ti960 reset") != 0) { + dev_err(va->sd.dev, "Unable to acquire gpio %d\n", + va->pdata->reset_gpio); + return -ENODEV; + } + + rval = ti960_init(va); + if (rval) { + dev_err(&client->dev, "Failed to init TI960!\n"); + return rval; + } + + /* + * TI960 has several back channel GPIOs. + * We export GPIO0 and GPIO1 to control reset or fsin. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + va->gc.dev = &client->dev; +#else + va->gc.parent = &client->dev; +#endif + va->gc.owner = THIS_MODULE; + va->gc.label = "TI960 GPIO"; + va->gc.ngpio = NR_OF_TI960_GPIOS; + va->gc.base = -1; + va->gc.set = ti960_gpio_set; + va->gc.direction_output = ti960_gpio_direction_output; + rval = gpiochip_add(&va->gc); + if (rval) { + dev_err(&client->dev, "Failed to add gpio chip!\n"); + return -EIO; + } + + return 0; +} + +static int ti960_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ti960 *va = to_ti960(subdev); + int i; + + if (!va) + return 0; + + mutex_destroy(&va->mutex); + v4l2_ctrl_handler_free(&va->ctrl_handler); + v4l2_device_unregister_subdev(&va->sd); + media_entity_cleanup(&va->sd.entity); + + for (i = 0; i < NR_OF_TI960_SINK_PADS; i++) { + if (va->sub_devs[i].sd) { + struct i2c_client *sub_client = + v4l2_get_subdevdata(va->sub_devs[i].sd); + + i2c_unregister_device(sub_client); + } + va->sub_devs[i].sd = NULL; + } + + gpiochip_remove(&va->gc); + + return 0; +} + +#ifdef CONFIG_PM +static int ti960_suspend(struct device *dev) +{ + return 0; +} + +static int ti960_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ti960 *va = to_ti960(subdev); + + return ti960_init(va); +} +#else +#define ti960_suspend NULL +#define ti960_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id ti960_id_table[] = { + { TI960_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ti960_id_table); + +static const struct dev_pm_ops ti960_pm_ops = { + .suspend = ti960_suspend, + .resume = ti960_resume, +}; + +static struct i2c_driver ti960_i2c_driver = { + .driver = { + .name = TI960_NAME, + .pm = &ti960_pm_ops, + }, + .probe = ti960_probe, + .remove = ti960_remove, + .id_table = ti960_id_table, +}; +module_i2c_driver(ti960_i2c_driver); + +MODULE_AUTHOR("Chen Meng J "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI960 CSI2-Aggregator driver"); diff --git a/include/media/ti960.h b/include/media/ti960.h new file mode 100644 index 000000000000..60e134fce6c5 --- /dev/null +++ b/include/media/ti960.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef TI960_H +#define TI960_H + +#include +#include +#include +#include +#include + +#define TI960_NAME "ti960" + +#define TI960_I2C_ADDRESS 0x38 + +#define PIXEL_ORDER_GRBG 0 +#define PIXEL_ORDER_RGGB 1 +#define PIXEL_ORDER_BGGR 2 +#define PIXEL_ORDER_GBRG 3 + +#define NR_OF_TI960_STREAMS 4 +#define NR_OF_TI960_SOURCE_PADS 1 +#define NR_OF_TI960_SINK_PADS 4 +#define NR_OF_TI960_PADS \ + (NR_OF_TI960_SOURCE_PADS + NR_OF_TI960_SINK_PADS) +#define NR_OF_GPIOS_PER_PORT 2 +#define NR_OF_TI960_GPIOS \ + (NR_OF_TI960_SINK_PADS * NR_OF_GPIOS_PER_PORT) + +#define TI960_PAD_SOURCE 4 + +#define TI960_MIN_WIDTH 640 +#define TI960_MIN_HEIGHT 480 +#define TI960_MAX_WIDTH 1920 +#define TI960_MAX_HEIGHT 1080 + +struct ti960_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; + u8 mipi_dt_code; +}; + +struct ti960_subdev_info { + struct i2c_board_info board_info; + int i2c_adapter_id; + unsigned short rx_port; + unsigned short phy_i2c_addr; + unsigned short ser_alias; + const char suffix; /* suffix for subdevs */ +}; + +struct ti960_pdata { + unsigned int subdev_num; + struct ti960_subdev_info *subdev_info; + unsigned int reset_gpio; + const char suffix; +}; + +#endif From c59834cd3f861f30a1fae87bdb4eda14ae403461 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:53:02 +0800 Subject: [PATCH 1145/1276] media: pci: add IPU driver for BXT platform Add Image Processing Unit (IPU) driver for Broxton Platform. IPU receives data stream from camera sensor and do various image processing work. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/pci/Kconfig | 1 + drivers/media/pci/intel/Kconfig | 59 + drivers/media/pci/intel/Makefile | 15 +- drivers/media/pci/intel/ipu-bus.c | 471 +++++ drivers/media/pci/intel/ipu-bus.h | 70 + drivers/media/pci/intel/ipu-buttress.c | 1834 ++++++++++++++++ drivers/media/pci/intel/ipu-buttress.h | 138 ++ drivers/media/pci/intel/ipu-cpd.c | 478 +++++ drivers/media/pci/intel/ipu-cpd.h | 108 + drivers/media/pci/intel/ipu-dma.c | 448 ++++ drivers/media/pci/intel/ipu-dma.h | 19 + drivers/media/pci/intel/ipu-fw-com.c | 480 +++++ drivers/media/pci/intel/ipu-fw-com.h | 43 + drivers/media/pci/intel/ipu-fw-isys.c | 214 ++ drivers/media/pci/intel/ipu-fw-isys.h | 838 ++++++++ drivers/media/pci/intel/ipu-fw-psys.c | 312 +++ drivers/media/pci/intel/ipu-fw-psys.h | 350 +++ .../media/pci/intel/ipu-isys-csi2-be-soc.c | 364 ++++ drivers/media/pci/intel/ipu-isys-csi2-be.c | 307 +++ drivers/media/pci/intel/ipu-isys-csi2-be.h | 74 + drivers/media/pci/intel/ipu-isys-csi2.c | 939 +++++++++ drivers/media/pci/intel/ipu-isys-csi2.h | 177 ++ drivers/media/pci/intel/ipu-isys-media.h | 91 + drivers/media/pci/intel/ipu-isys-queue.c | 1530 ++++++++++++++ drivers/media/pci/intel/ipu-isys-queue.h | 174 ++ drivers/media/pci/intel/ipu-isys-subdev.c | 1017 +++++++++ drivers/media/pci/intel/ipu-isys-subdev.h | 210 ++ drivers/media/pci/intel/ipu-isys-tpg.c | 355 ++++ drivers/media/pci/intel/ipu-isys-tpg.h | 97 + drivers/media/pci/intel/ipu-isys-video.c | 1854 ++++++++++++++++ drivers/media/pci/intel/ipu-isys-video.h | 169 ++ drivers/media/pci/intel/ipu-isys.c | 1466 +++++++++++++ drivers/media/pci/intel/ipu-isys.h | 174 ++ drivers/media/pci/intel/ipu-mmu.c | 876 ++++++++ drivers/media/pci/intel/ipu-mmu.h | 62 + drivers/media/pci/intel/ipu-pdata.h | 283 +++ drivers/media/pci/intel/ipu-psys-compat32.c | 226 ++ drivers/media/pci/intel/ipu-psys.c | 1649 +++++++++++++++ drivers/media/pci/intel/ipu-psys.h | 203 ++ drivers/media/pci/intel/ipu-trace-event.h | 100 + drivers/media/pci/intel/ipu-trace.c | 915 ++++++++ drivers/media/pci/intel/ipu-trace.h | 312 +++ drivers/media/pci/intel/ipu-wrapper.c | 514 +++++ drivers/media/pci/intel/ipu-wrapper.h | 17 + drivers/media/pci/intel/ipu.c | 763 +++++++ drivers/media/pci/intel/ipu.h | 105 + drivers/media/pci/intel/ipu4/Makefile | 131 ++ .../intel/ipu4/ipu-platform-buttress-regs.h | 282 +++ .../intel/ipu4/ipu-platform-isys-csi2-reg.h | 222 ++ .../media/pci/intel/ipu4/ipu-platform-isys.h | 21 + .../media/pci/intel/ipu4/ipu-platform-psys.h | 30 + .../media/pci/intel/ipu4/ipu-platform-regs.h | 263 +++ .../pci/intel/ipu4/ipu-platform-resources.h | 224 ++ drivers/media/pci/intel/ipu4/ipu-platform.h | 51 + .../intel/ipu4/ipu4-css/Makefile.ipu4isys_inc | 26 + .../intel/ipu4/ipu4-css/Makefile.ipu4isys_src | 19 + .../intel/ipu4/ipu4-css/Makefile.ipu4psys_inc | 52 + .../intel/ipu4/ipu4-css/Makefile.ipu4psys_src | 32 + .../pci/intel/ipu4/ipu4-css/Makefile.isyslib | 47 + .../pci/intel/ipu4/ipu4-css/Makefile.psyslib | 14 + .../ipu4/ipu4-css/ia_css_fw_pkg_release.h | 14 + .../pci/intel/ipu4/ipu4-css/ipu-wrapper.c | 514 +++++ .../ipu4/ipu4-css/lib2600/buffer/buffer.mk | 43 + .../lib2600/buffer/interface/buffer_access.h | 36 + .../lib2600/buffer/interface/buffer_type.h | 29 + .../buffer/interface/ia_css_buffer_address.h | 24 + .../buffer/interface/ia_css_input_buffer.h | 51 + .../interface/ia_css_input_buffer_cpu.h | 49 + .../buffer/interface/ia_css_output_buffer.h | 30 + .../interface/ia_css_output_buffer_cpu.h | 48 + .../buffer/interface/ia_css_return_token.h | 54 + .../buffer/interface/ia_css_shared_buffer.h | 32 + .../interface/ia_css_shared_buffer_cpu.h | 51 + .../lib2600/buffer/src/cpu/buffer_access.c | 39 + .../lib2600/buffer/src/cpu/ia_css_buffer.c | 51 + .../lib2600/buffer/src/cpu/ia_css_buffer.h | 58 + .../buffer/src/cpu/ia_css_input_buffer.c | 184 ++ .../buffer/src/cpu/ia_css_output_buffer.c | 181 ++ .../buffer/src/cpu/ia_css_shared_buffer.c | 187 ++ .../intel/ipu4/ipu4-css/lib2600/cell/cell.mk | 43 + .../lib2600/cell/interface/ia_css_cell.h | 112 + .../lib2600/cell/src/ia_css_cell_impl.h | 272 +++ .../lib2600/config/isys/subsystem_bxtB0.mk | 60 + .../ipu4-css/lib2600/config/system_bxtB0.mk | 88 + .../lib2600/device_access/device_access.mk | 40 + .../device_access/interface/ia_css_cmem.h | 58 + .../device_access/interface/ia_css_xmem.h | 65 + .../interface/ia_css_xmem_cmem.h | 35 + .../device_access/src/ia_css_cmem_host.h | 121 ++ .../device_access/src/ia_css_xmem_cmem_impl.h | 79 + .../device_access/src/ia_css_xmem_host.h | 84 + .../ipu_device_buttress_properties_struct.h | 68 + .../interface/ipu_device_cell_properties.h | 76 + .../ipu_device_cell_properties_func.h | 164 ++ .../ipu_device_cell_properties_struct.h | 51 + .../ipu_device_cell_type_properties.h | 69 + .../isys/bxtB0/ipu_device_cell_devices.h | 27 + .../bxtB0/ipu_device_cell_properties_defs.h | 22 + .../bxtB0/ipu_device_cell_properties_impl.h | 57 + ...pu_device_sp2600_control_properties_impl.h | 136 ++ .../cpu/fw_abi_cpu_types.mk | 24 + .../cpu/ia_css_terminal_base_types.h | 42 + .../cpu/ia_css_terminal_manifest_base_types.h | 42 + .../fw_abi_common_types/ia_css_base_types.h | 38 + .../ia_css_terminal_defs.h | 105 + .../interface/ia_css_isys_fw_bridged_types.h | 402 ++++ .../isysapi/interface/ia_css_isysapi.h | 321 +++ .../interface/ia_css_isysapi_fw_types.h | 512 +++++ .../interface/ia_css_isysapi_fw_version.h | 21 + .../ia_css_isysapi_proxy_region_defs.h | 113 + .../ia_css_isysapi_proxy_region_types.h | 24 + .../isysapi/interface/ia_css_isysapi_types.h | 349 +++ .../ipu4/ipu4-css/lib2600/isysapi/isysapi.mk | 77 + .../lib2600/isysapi/src/ia_css_isys_private.c | 980 +++++++++ .../lib2600/isysapi/src/ia_css_isys_private.h | 156 ++ .../lib2600/isysapi/src/ia_css_isys_public.c | 1283 +++++++++++ .../isysapi/src/ia_css_isys_public_trace.c | 379 ++++ .../isysapi/src/ia_css_isys_public_trace.h | 55 + .../isysapi/src/ia_css_isysapi_trace.h | 79 + .../pkg_dir/interface/ia_css_pkg_dir.h | 99 + .../pkg_dir/interface/ia_css_pkg_dir_iunit.h | 46 + .../interface/ia_css_pkg_dir_storage_class.h | 29 + .../pkg_dir/interface/ia_css_pkg_dir_types.h | 41 + .../ipu4/ipu4-css/lib2600/pkg_dir/pkg_dir.mk | 29 + .../lib2600/pkg_dir/src/ia_css_pkg_dir.c | 27 + .../lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h | 201 ++ .../lib2600/pkg_dir/src/ia_css_pkg_dir_int.h | 49 + .../lib2600/port/interface/port_env_struct.h | 24 + .../ipu4-css/lib2600/port/interface/queue.h | 40 + .../lib2600/port/interface/queue_struct.h | 47 + .../lib2600/port/interface/recv_port.h | 34 + .../lib2600/port/interface/recv_port_struct.h | 32 + .../lib2600/port/interface/send_port.h | 52 + .../lib2600/port/interface/send_port_struct.h | 32 + .../intel/ipu4/ipu4-css/lib2600/port/port.mk | 31 + .../ipu4/ipu4-css/lib2600/port/src/queue.c | 47 + .../ipu4-css/lib2600/port/src/recv_port.c | 95 + .../ipu4-css/lib2600/port/src/send_port.c | 94 + .../bxtB0_gen_reg_dump/ia_css_debug_dump.c | 15 + .../bxtB0_gen_reg_dump/ia_css_debug_dump.h | 17 + .../reg_dump/src/reg_dump_generic_bridge.c | 39 + .../lib2600/regmem/interface/regmem_access.h | 67 + .../ipu4/ipu4-css/lib2600/regmem/regmem.mk | 32 + .../lib2600/regmem/src/regmem_access_host.h | 41 + .../lib2600/regmem/src/regmem_const.h | 28 + .../ipu4-css/lib2600/support/assert_support.h | 197 ++ .../lib2600/support/cpu_mem_support.h | 233 ++ .../ipu4-css/lib2600/support/error_support.h | 110 + .../ipu4-css/lib2600/support/math_support.h | 314 +++ .../ipu4-css/lib2600/support/misc_support.h | 76 + .../lib2600/support/platform_support.h | 146 ++ .../ipu4-css/lib2600/support/print_support.h | 90 + .../ipu4-css/lib2600/support/storage_class.h | 51 + .../ipu4-css/lib2600/support/type_support.h | 80 + .../lib2600/syscom/interface/ia_css_syscom.h | 247 +++ .../syscom/interface/ia_css_syscom_config.h | 97 + .../syscom/interface/ia_css_syscom_trace.h | 51 + .../lib2600/syscom/src/ia_css_syscom.c | 650 ++++++ .../syscom/src/ia_css_syscom_config_fw.h | 69 + .../syscom/src/ia_css_syscom_context.h | 65 + .../ipu4/ipu4-css/lib2600/syscom/syscom.mk | 42 + .../lib2600/trace/interface/ia_css_trace.h | 883 ++++++++ .../ipu4/ipu4-css/lib2600/trace/trace.mk | 40 + .../lib2600/utils/system_defs/system_const.h | 26 + .../lib2600/vied/vied/shared_memory_access.h | 139 ++ .../lib2600/vied/vied/shared_memory_map.h | 53 + .../ipu4-css/lib2600/vied/vied/vied_config.h | 33 + .../vied/vied/vied_memory_access_types.h | 36 + .../lib2600/vied/vied/vied_subsystem_access.h | 70 + .../vied_subsystem_access_initialization.h | 44 + .../vied/vied/vied_subsystem_access_types.h | 34 + .../ipu4-css/lib2600/vied/vied/vied_types.h | 45 + .../intel/ipu4/ipu4-css/lib2600psys/Makefile | 49 + .../ia_css_fw_pkg_release.h | 14 + .../ipu4-css/lib2600psys/lib/buffer/buffer.mk | 43 + .../lib/buffer/interface/buffer_access.h | 36 + .../lib/buffer/interface/buffer_type.h | 29 + .../buffer/interface/ia_css_buffer_address.h | 24 + .../buffer/interface/ia_css_input_buffer.h | 51 + .../interface/ia_css_input_buffer_cpu.h | 49 + .../buffer/interface/ia_css_output_buffer.h | 30 + .../interface/ia_css_output_buffer_cpu.h | 48 + .../buffer/interface/ia_css_shared_buffer.h | 32 + .../interface/ia_css_shared_buffer_cpu.h | 51 + .../lib/buffer/src/cpu/buffer_access.c | 39 + .../lib/buffer/src/cpu/ia_css_buffer.c | 51 + .../lib/buffer/src/cpu/ia_css_buffer.h | 58 + .../lib/buffer/src/cpu/ia_css_input_buffer.c | 184 ++ .../lib/buffer/src/cpu/ia_css_output_buffer.c | 181 ++ .../lib/buffer/src/cpu/ia_css_shared_buffer.c | 187 ++ .../ipu4-css/lib2600psys/lib/cell/cell.mk | 43 + .../lib/cell/interface/ia_css_cell.h | 112 + .../lib/cell/src/ia_css_cell_impl.h | 272 +++ .../client_pkg/interface/ia_css_client_pkg.h | 60 + .../ia_css_client_pkg_storage_class.h | 28 + .../interface/ia_css_client_pkg_types.h | 44 + .../lib/client_pkg/src/ia_css_client_pkg.c | 20 + .../client_pkg/src/ia_css_client_pkg_impl.h | 161 ++ .../lib/config/psys/subsystem_bxtB0.mk | 109 + .../lib2600psys/lib/config/system_bxtB0.mk | 88 + .../lib/cpd/cpd_component/cpd_component.mk | 28 + .../interface/ia_css_cpd_component_types.h | 90 + .../lib/cpd/cpd_metadata/cpd_metadata.mk | 29 + .../interface/ia_css_cpd_metadata_types.h | 111 + .../lib/device_access/device_access.mk | 40 + .../lib/device_access/interface/ia_css_cmem.h | 58 + .../lib/device_access/interface/ia_css_xmem.h | 65 + .../interface/ia_css_xmem_cmem.h | 35 + .../lib/device_access/src/ia_css_cmem_host.h | 121 ++ .../device_access/src/ia_css_xmem_cmem_impl.h | 79 + .../lib/device_access/src/ia_css_xmem_host.h | 84 + .../ipu_device_buttress_properties_struct.h | 68 + .../interface/ipu_device_cell_properties.h | 76 + .../ipu_device_cell_properties_func.h | 164 ++ .../ipu_device_cell_properties_struct.h | 51 + .../ipu_device_cell_type_properties.h | 69 + .../interface/ipu_device_gp_properties.h | 26 + .../ipu_device_gp_properties_types.h | 103 + .../psys/bxtB0/ipu_device_acb_devices.h | 43 + .../psys/bxtB0/ipu_device_cell_devices.h | 38 + .../bxtB0/ipu_device_cell_properties_defs.h | 65 + .../bxtB0/ipu_device_cell_properties_impl.h | 193 ++ .../psys/bxtB0/ipu_device_ff_devices.h | 55 + .../psys/bxtB0/ipu_device_gp_devices.h | 67 + .../src/ipu_device_isp2600_properties_impl.h | 151 ++ ...pu_device_sp2600_control_properties_impl.h | 136 ++ .../ipu_device_sp2600_fp_properties_impl.h | 140 ++ .../ipu_device_sp2600_proxy_properties_impl.h | 138 ++ .../cpu/fw_abi_cpu_types.mk | 24 + .../cpu/ia_css_terminal_base_types.h | 42 + .../cpu/ia_css_terminal_manifest_base_types.h | 42 + .../fw_abi_common_types/ia_css_base_types.h | 38 + .../ia_css_terminal_defs.h | 105 + .../lib/pkg_dir/interface/ia_css_pkg_dir.h | 99 + .../pkg_dir/interface/ia_css_pkg_dir_iunit.h | 46 + .../interface/ia_css_pkg_dir_storage_class.h | 29 + .../pkg_dir/interface/ia_css_pkg_dir_types.h | 41 + .../lib2600psys/lib/pkg_dir/pkg_dir.mk | 29 + .../lib/pkg_dir/src/ia_css_pkg_dir.c | 27 + .../lib/pkg_dir/src/ia_css_pkg_dir_impl.h | 201 ++ .../lib/pkg_dir/src/ia_css_pkg_dir_int.h | 49 + .../lib/port/interface/port_env_struct.h | 24 + .../lib2600psys/lib/port/interface/queue.h | 40 + .../lib/port/interface/queue_struct.h | 47 + .../lib/port/interface/recv_port.h | 34 + .../lib/port/interface/recv_port_struct.h | 32 + .../lib/port/interface/send_port.h | 52 + .../lib/port/interface/send_port_struct.h | 32 + .../ipu4-css/lib2600psys/lib/port/port.mk | 31 + .../ipu4-css/lib2600psys/lib/port/src/queue.c | 47 + .../lib2600psys/lib/port/src/recv_port.c | 95 + .../lib2600psys/lib/port/src/send_port.c | 94 + .../interface/ia_css_psys_private_pg_data.h | 43 + .../interface/ia_css_bxt_spctrl_trace.h | 107 + .../lib/psys_server/psys_server.mk | 81 + .../src/bxt_spctrl_process_group_cmd_impl.c | 332 +++ .../interface/ia_css_program_group_data.h | 418 ++++ .../ia_css_program_group_data_defs.h | 196 ++ .../ia_css_psys_data_storage_class.h | 28 + .../data/interface/ia_css_psys_data_trace.h | 102 + .../data/src/ia_css_program_group_data.c | 26 + .../data/src/ia_css_program_group_data_impl.h | 455 ++++ .../bxtB0/ia_css_psys_transport_dep.h | 35 + .../device/interface/ia_css_psys_device.h | 516 +++++ .../interface/ia_css_psys_device_trace.h | 103 + .../device/interface/ia_css_psys_init.h | 37 + .../device/interface/ia_css_psys_transport.h | 92 + .../psysapi/device/src/ia_css_psys_device.c | 853 ++++++++ .../interface/ia_css_psys_buffer_set.h | 174 ++ .../ia_css_psys_dynamic_storage_class.h | 28 + .../interface/ia_css_psys_dynamic_trace.h | 103 + .../dynamic/interface/ia_css_psys_process.h | 396 ++++ .../ia_css_psys_process.hsys.kernel.h | 144 ++ .../interface/ia_css_psys_process.hsys.user.h | 85 + .../interface/ia_css_psys_process.psys.h | 53 + .../interface/ia_css_psys_process_group.h | 366 ++++ .../ia_css_psys_process_group.hsys.kernel.h | 324 +++ .../ia_css_psys_process_group.hsys.user.h | 199 ++ .../ia_css_psys_process_group.psys.h | 60 + .../ia_css_psys_process_group_cmd_impl.h | 178 ++ .../interface/ia_css_psys_process_types.h | 95 + .../dynamic/interface/ia_css_psys_terminal.h | 316 +++ .../ia_css_psys_terminal.hsys.user.h | 255 +++ .../dynamic/src/ia_css_psys_buffer_set.c | 111 + .../dynamic/src/ia_css_psys_buffer_set_impl.h | 241 +++ .../psysapi/dynamic/src/ia_css_psys_process.c | 1147 ++++++++++ .../dynamic/src/ia_css_psys_process_group.c | 886 ++++++++ .../src/ia_css_psys_process_group_impl.h | 1538 ++++++++++++++ .../dynamic/src/ia_css_psys_process_impl.h | 637 ++++++ .../src/ia_css_psys_process_private_types.h | 87 + .../dynamic/src/ia_css_psys_terminal.c | 604 ++++++ .../dynamic/src/ia_css_psys_terminal_impl.h | 1868 +++++++++++++++++ .../src/ia_css_psys_terminal_private_types.h | 186 ++ .../lib/psysapi/interface/ia_css_psysapi.h | 23 + .../interface/ia_css_psysapi_fw_version.h | 33 + .../psysapi/interface/ia_css_psysapi_trace.h | 78 + .../kernel/interface/ia_css_kernel_bitmap.h | 223 ++ .../interface/ia_css_psys_kernel_trace.h | 103 + .../psysapi/kernel/src/ia_css_kernel_bitmap.c | 413 ++++ .../interface/ia_css_program_group_param.h | 293 +++ .../ia_css_program_group_param.sim.h | 153 ++ .../ia_css_program_group_param_types.h | 64 + .../param/interface/ia_css_psys_param_trace.h | 102 + .../param/src/ia_css_program_group_param.c | 771 +++++++ .../src/ia_css_program_group_param_private.h | 80 + .../bxtB0/ia_css_psys_server_manifest.c | 50 + .../bxtB0/ia_css_psys_server_manifest.h | 29 + .../lib2600psys/lib/psysapi/psysapi.mk | 122 ++ .../bxtB0/vied_nci_psys_resource_model.c | 322 +++ .../bxtB0/vied_nci_psys_resource_model.h | 300 +++ .../sim/interface/ia_css_psys_sim_data.h | 50 + .../interface/ia_css_psys_sim_storage_class.h | 28 + .../sim/interface/ia_css_psys_sim_trace.h | 95 + .../interface/vied_nci_psys_system_global.h | 180 ++ .../psysapi/sim/src/ia_css_psys_sim_data.c | 91 + .../psysapi/sim/src/psys_system_global_impl.h | 485 +++++ .../psysapi/sim/src/vied_nci_psys_system.c | 26 + .../interface/ia_css_psys_manifest_types.h | 102 + .../ia_css_psys_program_group_manifest.h | 311 +++ ...ss_psys_program_group_manifest.hsys.user.h | 69 + .../ia_css_psys_program_group_manifest.sim.h | 127 ++ .../interface/ia_css_psys_program_manifest.h | 488 +++++ ...ia_css_psys_program_manifest.hsys.kernel.h | 96 + .../ia_css_psys_program_manifest.hsys.user.h | 38 + .../ia_css_psys_program_manifest.sim.h | 61 + .../ia_css_psys_static_storage_class.h | 28 + .../interface/ia_css_psys_static_trace.h | 103 + .../interface/ia_css_psys_terminal_manifest.h | 423 ++++ .../ia_css_psys_terminal_manifest.hsys.user.h | 38 + .../ia_css_psys_terminal_manifest.sim.h | 48 + .../src/ia_css_psys_program_group_manifest.c | 1038 +++++++++ .../ia_css_psys_program_group_manifest_impl.h | 415 ++++ .../src/ia_css_psys_program_group_private.h | 212 ++ .../static/src/ia_css_psys_program_manifest.c | 1240 +++++++++++ .../src/ia_css_psys_terminal_manifest.c | 1137 ++++++++++ .../bxtB0_gen_reg_dump/ia_css_debug_dump.c | 15 + .../bxtB0_gen_reg_dump/ia_css_debug_dump.h | 17 + .../reg_dump/src/reg_dump_generic_bridge.c | 39 + .../lib/regmem/interface/regmem_access.h | 67 + .../ipu4-css/lib2600psys/lib/regmem/regmem.mk | 32 + .../lib/regmem/src/regmem_access_host.h | 41 + .../lib2600psys/lib/regmem/src/regmem_const.h | 28 + .../lib/routing_bitmap/interface/ia_css_rbm.h | 173 ++ .../interface/ia_css_rbm_manifest.h | 133 ++ .../interface/ia_css_rbm_manifest_types.h | 95 + .../interface/ia_css_rbm_storage_class.h | 36 + .../interface/ia_css_rbm_trace.h | 77 + .../lib/routing_bitmap/routing_bitmap.mk | 39 + .../lib/routing_bitmap/src/ia_css_rbm.c | 17 + .../lib/routing_bitmap/src/ia_css_rbm_impl.h | 338 +++ .../routing_bitmap/src/ia_css_rbm_manifest.c | 224 ++ .../src/ia_css_rbm_manifest_impl.h | 108 + .../lib2600psys/lib/support/assert_support.h | 197 ++ .../lib2600psys/lib/support/cpu_mem_support.h | 233 ++ .../lib2600psys/lib/support/error_support.h | 110 + .../lib2600psys/lib/support/math_support.h | 314 +++ .../lib2600psys/lib/support/misc_support.h | 76 + .../lib/support/platform_support.h | 146 ++ .../lib2600psys/lib/support/print_support.h | 90 + .../lib2600psys/lib/support/storage_class.h | 51 + .../lib2600psys/lib/support/type_support.h | 80 + .../lib/syscom/interface/ia_css_syscom.h | 247 +++ .../syscom/interface/ia_css_syscom_config.h | 97 + .../syscom/interface/ia_css_syscom_trace.h | 51 + .../lib/syscom/src/ia_css_syscom.c | 650 ++++++ .../lib/syscom/src/ia_css_syscom_config_fw.h | 69 + .../lib/syscom/src/ia_css_syscom_context.h | 65 + .../ipu4-css/lib2600psys/lib/syscom/syscom.mk | 42 + .../lib/trace/interface/ia_css_trace.h | 883 ++++++++ .../ipu4-css/lib2600psys/lib/trace/trace.mk | 40 + .../lib/vied/vied/shared_memory_access.h | 139 ++ .../lib/vied/vied/shared_memory_map.h | 53 + .../lib2600psys/lib/vied/vied/vied_config.h | 33 + .../lib/vied/vied/vied_memory_access_types.h | 36 + .../lib/vied/vied/vied_subsystem_access.h | 70 + .../vied_subsystem_access_initialization.h | 44 + .../vied/vied/vied_subsystem_access_types.h | 34 + .../lib2600psys/lib/vied/vied/vied_types.h | 45 + .../interface/vied_nci_acb_route_type.h | 39 + .../interface/ia_css_param_storage_class.h | 28 + .../interface/ia_css_terminal.h | 188 ++ .../interface/ia_css_terminal_manifest.h | 109 + .../ia_css_terminal_manifest_types.h | 342 +++ .../interface/ia_css_terminal_types.h | 351 ++++ .../lib/vied_parameters/src/ia_css_terminal.c | 20 + .../src/ia_css_terminal_impl.h | 495 +++++ .../src/ia_css_terminal_manifest.c | 20 + .../src/ia_css_terminal_manifest_impl.h | 347 +++ .../lib/vied_parameters/vied_parameters.mk | 76 + .../ipu4-css/lib2600psys/libcsspsys2600.c | 480 +++++ .../ipu4-css/lib2600psys/libcsspsys2600.h | 26 + .../pci/intel/ipu4/ipu4-css/libintel-ipu4.c | 392 ++++ .../media/pci/intel/ipu4/ipu4-fw-resources.c | 332 +++ drivers/media/pci/intel/ipu4/ipu4-isys-csi2.c | 713 +++++++ drivers/media/pci/intel/ipu4/ipu4-isys-isa.c | 1074 ++++++++++ drivers/media/pci/intel/ipu4/ipu4-isys-isa.h | 85 + drivers/media/pci/intel/ipu4/ipu4-isys.c | 451 ++++ drivers/media/pci/intel/ipu4/ipu4-psys.c | 1109 ++++++++++ drivers/media/pci/intel/ipu4/ipu4-resources.c | 461 ++++ drivers/media/pci/intel/ipu4/ipu4.c | 572 +++++ .../ipu4/ipu4p-css/Makefile.ipu4pisys_inc | 26 + .../ipu4/ipu4p-css/Makefile.ipu4pisys_src | 19 + .../ipu4/ipu4p-css/Makefile.ipu4ppsys_inc | 52 + .../ipu4/ipu4p-css/Makefile.ipu4ppsys_src | 32 + .../pci/intel/ipu4/ipu4p-css/Makefile.isyslib | 47 + .../pci/intel/ipu4/ipu4p-css/Makefile.psyslib | 14 + .../ipu4/ipu4p-css/ia_css_fw_pkg_release.h | 14 + .../ipu4/ipu4p-css/lib2600/buffer/buffer.mk | 43 + .../lib2600/buffer/interface/buffer_access.h | 36 + .../lib2600/buffer/interface/buffer_type.h | 29 + .../buffer/interface/ia_css_buffer_address.h | 24 + .../buffer/interface/ia_css_input_buffer.h | 51 + .../interface/ia_css_input_buffer_cpu.h | 49 + .../buffer/interface/ia_css_output_buffer.h | 30 + .../interface/ia_css_output_buffer_cpu.h | 48 + .../buffer/interface/ia_css_return_token.h | 54 + .../buffer/interface/ia_css_shared_buffer.h | 32 + .../interface/ia_css_shared_buffer_cpu.h | 51 + .../lib2600/buffer/src/cpu/buffer_access.c | 39 + .../lib2600/buffer/src/cpu/ia_css_buffer.c | 51 + .../lib2600/buffer/src/cpu/ia_css_buffer.h | 58 + .../buffer/src/cpu/ia_css_input_buffer.c | 184 ++ .../buffer/src/cpu/ia_css_output_buffer.c | 181 ++ .../buffer/src/cpu/ia_css_shared_buffer.c | 187 ++ .../intel/ipu4/ipu4p-css/lib2600/cell/cell.mk | 43 + .../lib2600/cell/interface/ia_css_cell.h | 112 + .../lib2600/cell/src/ia_css_cell_impl.h | 272 +++ .../lib2600/config/isys/subsystem_cnlB0.mk | 75 + .../ipu4p-css/lib2600/config/system_cnlB0.mk | 96 + .../cpd_binary/ia_css_fw_pkg_release.h | 14 + .../lib2600/device_access/device_access.mk | 40 + .../device_access/interface/ia_css_cmem.h | 58 + .../device_access/interface/ia_css_xmem.h | 65 + .../interface/ia_css_xmem_cmem.h | 35 + .../device_access/src/ia_css_cmem_host.h | 121 ++ .../device_access/src/ia_css_xmem_cmem_impl.h | 79 + .../device_access/src/ia_css_xmem_host.h | 84 + .../ipu_device_buttress_properties_struct.h | 68 + .../interface/ipu_device_cell_properties.h | 76 + .../ipu_device_cell_properties_func.h | 164 ++ .../ipu_device_cell_properties_struct.h | 51 + .../ipu_device_cell_type_properties.h | 69 + .../isys/cnlB0/ipu_device_cell_devices.h | 27 + .../cnlB0/ipu_device_cell_properties_defs.h | 22 + .../cnlB0/ipu_device_cell_properties_impl.h | 57 + ...pu_device_sp2600_control_properties_impl.h | 136 ++ .../cpu/fw_abi_cpu_types.mk | 24 + .../cpu/ia_css_terminal_base_types.h | 42 + .../cpu/ia_css_terminal_manifest_base_types.h | 42 + .../fw_abi_common_types/ia_css_base_types.h | 38 + .../ia_css_terminal_defs.h | 105 + .../interface/ia_css_isys_fw_bridged_types.h | 402 ++++ .../isysapi/interface/ia_css_isysapi.h | 321 +++ .../interface/ia_css_isysapi_fw_types.h | 512 +++++ .../interface/ia_css_isysapi_fw_version.h | 21 + .../ia_css_isysapi_proxy_region_defs.h | 113 + .../ia_css_isysapi_proxy_region_types.h | 24 + .../isysapi/interface/ia_css_isysapi_types.h | 349 +++ .../ipu4/ipu4p-css/lib2600/isysapi/isysapi.mk | 77 + .../lib2600/isysapi/src/ia_css_isys_private.c | 980 +++++++++ .../lib2600/isysapi/src/ia_css_isys_private.h | 156 ++ .../lib2600/isysapi/src/ia_css_isys_public.c | 1283 +++++++++++ .../isysapi/src/ia_css_isys_public_trace.c | 379 ++++ .../isysapi/src/ia_css_isys_public_trace.h | 55 + .../isysapi/src/ia_css_isysapi_trace.h | 79 + .../pkg_dir/interface/ia_css_pkg_dir.h | 99 + .../pkg_dir/interface/ia_css_pkg_dir_iunit.h | 46 + .../interface/ia_css_pkg_dir_storage_class.h | 29 + .../pkg_dir/interface/ia_css_pkg_dir_types.h | 41 + .../ipu4/ipu4p-css/lib2600/pkg_dir/pkg_dir.mk | 29 + .../lib2600/pkg_dir/src/ia_css_pkg_dir.c | 27 + .../lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h | 201 ++ .../lib2600/pkg_dir/src/ia_css_pkg_dir_int.h | 49 + .../lib2600/port/interface/port_env_struct.h | 24 + .../ipu4p-css/lib2600/port/interface/queue.h | 40 + .../lib2600/port/interface/queue_struct.h | 47 + .../lib2600/port/interface/recv_port.h | 34 + .../lib2600/port/interface/recv_port_struct.h | 32 + .../lib2600/port/interface/send_port.h | 52 + .../lib2600/port/interface/send_port_struct.h | 32 + .../intel/ipu4/ipu4p-css/lib2600/port/port.mk | 31 + .../ipu4/ipu4p-css/lib2600/port/src/queue.c | 47 + .../ipu4p-css/lib2600/port/src/recv_port.c | 95 + .../ipu4p-css/lib2600/port/src/send_port.c | 94 + .../cnlB0_gen_reg_dump/ia_css_debug_dump.c | 15 + .../cnlB0_gen_reg_dump/ia_css_debug_dump.h | 17 + .../reg_dump/src/reg_dump_generic_bridge.c | 39 + .../lib2600/regmem/interface/regmem_access.h | 67 + .../ipu4/ipu4p-css/lib2600/regmem/regmem.mk | 32 + .../lib2600/regmem/src/regmem_access_host.h | 41 + .../lib2600/regmem/src/regmem_const.h | 28 + .../lib2600/support/assert_support.h | 197 ++ .../lib2600/support/cpu_mem_support.h | 233 ++ .../ipu4p-css/lib2600/support/error_support.h | 110 + .../ipu4p-css/lib2600/support/math_support.h | 314 +++ .../ipu4p-css/lib2600/support/misc_support.h | 76 + .../lib2600/support/platform_support.h | 146 ++ .../ipu4p-css/lib2600/support/print_support.h | 90 + .../ipu4p-css/lib2600/support/storage_class.h | 51 + .../ipu4p-css/lib2600/support/type_support.h | 80 + .../lib2600/syscom/interface/ia_css_syscom.h | 247 +++ .../syscom/interface/ia_css_syscom_config.h | 97 + .../syscom/interface/ia_css_syscom_trace.h | 51 + .../lib2600/syscom/src/ia_css_syscom.c | 650 ++++++ .../syscom/src/ia_css_syscom_config_fw.h | 69 + .../syscom/src/ia_css_syscom_context.h | 65 + .../ipu4/ipu4p-css/lib2600/syscom/syscom.mk | 42 + .../lib2600/trace/interface/ia_css_trace.h | 883 ++++++++ .../ipu4/ipu4p-css/lib2600/trace/trace.mk | 40 + .../lib2600/utils/system_defs/system_const.h | 26 + .../lib2600/vied/vied/shared_memory_access.h | 139 ++ .../lib2600/vied/vied/shared_memory_map.h | 53 + .../ipu4p-css/lib2600/vied/vied/vied_config.h | 33 + .../vied/vied/vied_memory_access_types.h | 36 + .../lib2600/vied/vied/vied_subsystem_access.h | 70 + .../vied_subsystem_access_initialization.h | 44 + .../vied/vied/vied_subsystem_access_types.h | 34 + .../ipu4p-css/lib2600/vied/vied/vied_types.h | 45 + .../intel/ipu4/ipu4p-css/lib2600psys/Makefile | 52 + .../CNL_program_group/ia_css_fw_pkg_release.h | 14 + .../ICL_program_group/ia_css_fw_pkg_release.h | 14 + .../lib2600psys/lib/buffer/buffer.mk | 43 + .../lib/buffer/interface/buffer_access.h | 36 + .../lib/buffer/interface/buffer_type.h | 29 + .../buffer/interface/ia_css_buffer_address.h | 24 + .../buffer/interface/ia_css_input_buffer.h | 51 + .../interface/ia_css_input_buffer_cpu.h | 49 + .../buffer/interface/ia_css_output_buffer.h | 30 + .../interface/ia_css_output_buffer_cpu.h | 48 + .../buffer/interface/ia_css_shared_buffer.h | 32 + .../interface/ia_css_shared_buffer_cpu.h | 51 + .../lib/buffer/src/cpu/buffer_access.c | 39 + .../lib/buffer/src/cpu/ia_css_buffer.c | 51 + .../lib/buffer/src/cpu/ia_css_buffer.h | 58 + .../lib/buffer/src/cpu/ia_css_input_buffer.c | 184 ++ .../lib/buffer/src/cpu/ia_css_output_buffer.c | 181 ++ .../lib/buffer/src/cpu/ia_css_shared_buffer.c | 187 ++ .../ipu4p-css/lib2600psys/lib/cell/cell.mk | 43 + .../lib/cell/interface/ia_css_cell.h | 112 + .../lib/cell/src/ia_css_cell_impl.h | 272 +++ .../cell_program_load/cell_program_load.mk | 39 + .../ia_css_cell_program_group_load.h | 76 + .../interface/ia_css_cell_program_load.h | 114 + .../interface/ia_css_cell_program_load_prog.h | 84 + .../ia_css_cell_program_load_storage_class.h | 28 + .../interface/ia_css_cell_program_struct.h | 114 + .../src/ia_css_cell_program_group_load_impl.h | 128 ++ .../src/ia_css_cell_program_load.c | 31 + .../src/ia_css_cell_program_load_bin.h | 193 ++ .../src/ia_css_cell_program_load_impl.h | 134 ++ .../src/ia_css_cell_program_load_prog_impl.h | 76 + .../cell_program_load/src/ia_css_cell_regs.h | 78 + .../client_pkg/interface/ia_css_client_pkg.h | 60 + .../ia_css_client_pkg_storage_class.h | 28 + .../interface/ia_css_client_pkg_types.h | 44 + .../lib/client_pkg/src/ia_css_client_pkg.c | 20 + .../client_pkg/src/ia_css_client_pkg_impl.h | 161 ++ .../lib/config/psys/subsystem_cnlB0.mk | 138 ++ .../lib2600psys/lib/config/system_cnlB0.mk | 96 + .../lib/cpd/cpd_component/cpd_component.mk | 28 + .../interface/ia_css_cpd_component_types.h | 90 + .../lib/cpd/cpd_metadata/cpd_metadata.mk | 29 + .../interface/ia_css_cpd_metadata_types.h | 111 + .../lib/device_access/device_access.mk | 40 + .../lib/device_access/interface/ia_css_cmem.h | 58 + .../lib/device_access/interface/ia_css_xmem.h | 65 + .../interface/ia_css_xmem_cmem.h | 35 + .../lib/device_access/src/ia_css_cmem_host.h | 121 ++ .../device_access/src/ia_css_xmem_cmem_impl.h | 79 + .../lib/device_access/src/ia_css_xmem_host.h | 84 + .../ipu_device_buttress_properties_struct.h | 68 + .../interface/ipu_device_cell_properties.h | 76 + .../ipu_device_cell_properties_func.h | 164 ++ .../ipu_device_cell_properties_struct.h | 51 + .../ipu_device_cell_type_properties.h | 69 + .../interface/ipu_device_gp_properties.h | 26 + .../ipu_device_gp_properties_types.h | 103 + .../psys/cnlB0/ipu_device_acb_devices.h | 43 + .../psys/cnlB0/ipu_device_cell_devices.h | 38 + .../cnlB0/ipu_device_cell_properties_defs.h | 65 + .../cnlB0/ipu_device_cell_properties_impl.h | 193 ++ .../psys/cnlB0/ipu_device_ff_devices.h | 57 + .../psys/cnlB0/ipu_device_gp_devices.h | 67 + .../src/ipu_device_isp2600_properties_impl.h | 151 ++ ...pu_device_sp2600_control_properties_impl.h | 136 ++ .../ipu_device_sp2600_fp_properties_impl.h | 140 ++ .../ipu_device_sp2600_proxy_properties_impl.h | 138 ++ .../cpu/fw_abi_cpu_types.mk | 24 + .../cpu/ia_css_terminal_base_types.h | 42 + .../cpu/ia_css_terminal_manifest_base_types.h | 42 + .../fw_abi_common_types/ia_css_base_types.h | 38 + .../ia_css_terminal_defs.h | 105 + .../lib2600psys/lib/fw_load/fw_load.mk | 59 + .../lib/fw_load/interface/ia_css_fw_load.h | 155 ++ .../interface/ia_css_fw_load_storage_class.h | 28 + .../lib/fw_load/src/xmem/ia_css_fw_load.c | 29 + .../src/xmem/ia_css_fw_load_blocking_impl.h | 54 + .../fw_load/src/xmem/ia_css_fw_load_impl.h | 26 + .../ia_css_fw_load_non_blocking_host_state.h | 21 + .../xmem/ia_css_fw_load_non_blocking_impl.h | 125 ++ .../ia_css_fw_load_non_blocking_impl_host.h | 45 + .../lib/pkg_dir/interface/ia_css_pkg_dir.h | 99 + .../pkg_dir/interface/ia_css_pkg_dir_iunit.h | 46 + .../interface/ia_css_pkg_dir_storage_class.h | 29 + .../pkg_dir/interface/ia_css_pkg_dir_types.h | 41 + .../lib2600psys/lib/pkg_dir/pkg_dir.mk | 29 + .../lib/pkg_dir/src/ia_css_pkg_dir.c | 27 + .../lib/pkg_dir/src/ia_css_pkg_dir_impl.h | 201 ++ .../lib/pkg_dir/src/ia_css_pkg_dir_int.h | 49 + .../lib/port/interface/port_env_struct.h | 24 + .../lib2600psys/lib/port/interface/queue.h | 40 + .../lib/port/interface/queue_struct.h | 47 + .../lib/port/interface/recv_port.h | 34 + .../lib/port/interface/recv_port_struct.h | 32 + .../lib/port/interface/send_port.h | 52 + .../lib/port/interface/send_port_struct.h | 32 + .../ipu4p-css/lib2600psys/lib/port/port.mk | 31 + .../lib2600psys/lib/port/src/queue.c | 47 + .../lib2600psys/lib/port/src/recv_port.c | 95 + .../lib2600psys/lib/port/src/send_port.c | 94 + .../psys_infobits/interface/psys_infobits.h | 20 + .../lib/psys_infobits/psys_infobits.mk | 29 + .../lib/psys_infobits/src/psys_infobits.c | 107 + .../interface/ia_css_psys_private_pg_data.h | 43 + .../interface/ia_css_bxt_spctrl_trace.h | 107 + .../lib/psys_server/psys_server.mk | 81 + .../src/bxt_spctrl_process_group_cmd_impl.c | 332 +++ .../interface/ia_css_program_group_data.h | 418 ++++ .../ia_css_program_group_data_defs.h | 196 ++ .../ia_css_psys_data_storage_class.h | 28 + .../data/interface/ia_css_psys_data_trace.h | 102 + .../data/src/ia_css_program_group_data.c | 26 + .../data/src/ia_css_program_group_data_impl.h | 455 ++++ .../cnlB0/ia_css_psys_transport_dep.h | 35 + .../device/interface/ia_css_psys_device.h | 516 +++++ .../interface/ia_css_psys_device_trace.h | 103 + .../device/interface/ia_css_psys_init.h | 37 + .../device/interface/ia_css_psys_transport.h | 92 + .../psysapi/device/src/ia_css_psys_device.c | 853 ++++++++ .../interface/ia_css_psys_buffer_set.h | 174 ++ .../ia_css_psys_dynamic_storage_class.h | 28 + .../interface/ia_css_psys_dynamic_trace.h | 103 + .../dynamic/interface/ia_css_psys_process.h | 396 ++++ .../ia_css_psys_process.hsys.kernel.h | 144 ++ .../interface/ia_css_psys_process.hsys.user.h | 85 + .../interface/ia_css_psys_process.psys.h | 53 + .../interface/ia_css_psys_process_group.h | 366 ++++ .../ia_css_psys_process_group.hsys.kernel.h | 324 +++ .../ia_css_psys_process_group.hsys.user.h | 199 ++ .../ia_css_psys_process_group.psys.h | 60 + .../ia_css_psys_process_group_cmd_impl.h | 178 ++ .../interface/ia_css_psys_process_types.h | 95 + .../dynamic/interface/ia_css_psys_terminal.h | 316 +++ .../ia_css_psys_terminal.hsys.user.h | 255 +++ .../dynamic/src/ia_css_psys_buffer_set.c | 111 + .../dynamic/src/ia_css_psys_buffer_set_impl.h | 241 +++ .../psysapi/dynamic/src/ia_css_psys_process.c | 1147 ++++++++++ .../dynamic/src/ia_css_psys_process_group.c | 886 ++++++++ .../src/ia_css_psys_process_group_impl.h | 1538 ++++++++++++++ .../dynamic/src/ia_css_psys_process_impl.h | 637 ++++++ .../src/ia_css_psys_process_private_types.h | 87 + .../dynamic/src/ia_css_psys_terminal.c | 604 ++++++ .../dynamic/src/ia_css_psys_terminal_impl.h | 1868 +++++++++++++++++ .../src/ia_css_psys_terminal_private_types.h | 186 ++ .../lib/psysapi/interface/ia_css_psysapi.h | 23 + .../interface/ia_css_psysapi_fw_version.h | 33 + .../psysapi/interface/ia_css_psysapi_trace.h | 78 + .../kernel/interface/ia_css_kernel_bitmap.h | 223 ++ .../interface/ia_css_psys_kernel_trace.h | 103 + .../psysapi/kernel/src/ia_css_kernel_bitmap.c | 413 ++++ .../interface/ia_css_program_group_param.h | 293 +++ .../ia_css_program_group_param.sim.h | 153 ++ .../ia_css_program_group_param_types.h | 64 + .../param/interface/ia_css_psys_param_trace.h | 102 + .../param/src/ia_css_program_group_param.c | 771 +++++++ .../src/ia_css_program_group_param_private.h | 80 + .../cnlB0/ia_css_psys_server_manifest.c | 51 + .../cnlB0/ia_css_psys_server_manifest.h | 29 + .../lib2600psys/lib/psysapi/psysapi.mk | 122 ++ .../cnlB0/vied_nci_psys_resource_model.c | 323 +++ .../cnlB0/vied_nci_psys_resource_model.h | 300 +++ .../sim/interface/ia_css_psys_sim_data.h | 50 + .../interface/ia_css_psys_sim_storage_class.h | 28 + .../sim/interface/ia_css_psys_sim_trace.h | 95 + .../interface/vied_nci_psys_system_global.h | 180 ++ .../psysapi/sim/src/ia_css_psys_sim_data.c | 91 + .../psysapi/sim/src/psys_system_global_impl.h | 485 +++++ .../psysapi/sim/src/vied_nci_psys_system.c | 26 + .../interface/ia_css_psys_manifest_types.h | 102 + .../ia_css_psys_program_group_manifest.h | 311 +++ ...ss_psys_program_group_manifest.hsys.user.h | 69 + .../ia_css_psys_program_group_manifest.sim.h | 127 ++ .../interface/ia_css_psys_program_manifest.h | 488 +++++ ...ia_css_psys_program_manifest.hsys.kernel.h | 96 + .../ia_css_psys_program_manifest.hsys.user.h | 38 + .../ia_css_psys_program_manifest.sim.h | 61 + .../ia_css_psys_static_storage_class.h | 28 + .../interface/ia_css_psys_static_trace.h | 103 + .../interface/ia_css_psys_terminal_manifest.h | 423 ++++ .../ia_css_psys_terminal_manifest.hsys.user.h | 38 + .../ia_css_psys_terminal_manifest.sim.h | 48 + .../src/ia_css_psys_program_group_manifest.c | 1038 +++++++++ .../ia_css_psys_program_group_manifest_impl.h | 415 ++++ .../src/ia_css_psys_program_group_private.h | 212 ++ .../static/src/ia_css_psys_program_manifest.c | 1240 +++++++++++ .../src/ia_css_psys_terminal_manifest.c | 1137 ++++++++++ .../cnlB0_gen_reg_dump/ia_css_debug_dump.c | 15 + .../cnlB0_gen_reg_dump/ia_css_debug_dump.h | 17 + .../reg_dump/src/reg_dump_generic_bridge.c | 39 + .../lib/regmem/interface/regmem_access.h | 67 + .../lib2600psys/lib/regmem/regmem.mk | 32 + .../lib/regmem/src/regmem_access_host.h | 41 + .../lib2600psys/lib/regmem/src/regmem_const.h | 28 + .../lib/routing_bitmap/interface/ia_css_rbm.h | 173 ++ .../interface/ia_css_rbm_manifest.h | 133 ++ .../interface/ia_css_rbm_manifest_types.h | 95 + .../interface/ia_css_rbm_storage_class.h | 36 + .../interface/ia_css_rbm_trace.h | 77 + .../lib/routing_bitmap/routing_bitmap.mk | 39 + .../lib/routing_bitmap/src/ia_css_rbm.c | 17 + .../lib/routing_bitmap/src/ia_css_rbm_impl.h | 338 +++ .../routing_bitmap/src/ia_css_rbm_manifest.c | 224 ++ .../src/ia_css_rbm_manifest_impl.h | 108 + .../lib2600psys/lib/support/assert_support.h | 197 ++ .../lib2600psys/lib/support/cpu_mem_support.h | 233 ++ .../lib2600psys/lib/support/error_support.h | 110 + .../lib2600psys/lib/support/math_support.h | 314 +++ .../lib2600psys/lib/support/misc_support.h | 76 + .../lib/support/platform_support.h | 146 ++ .../lib2600psys/lib/support/print_support.h | 90 + .../lib2600psys/lib/support/storage_class.h | 51 + .../lib2600psys/lib/support/type_support.h | 80 + .../lib/syscom/interface/ia_css_syscom.h | 247 +++ .../syscom/interface/ia_css_syscom_config.h | 97 + .../syscom/interface/ia_css_syscom_trace.h | 51 + .../lib/syscom/src/ia_css_syscom.c | 650 ++++++ .../lib/syscom/src/ia_css_syscom_config_fw.h | 69 + .../lib/syscom/src/ia_css_syscom_context.h | 65 + .../lib2600psys/lib/syscom/syscom.mk | 42 + .../lib/trace/interface/ia_css_trace.h | 883 ++++++++ .../ipu4p-css/lib2600psys/lib/trace/trace.mk | 40 + .../lib/vied/vied/shared_memory_access.h | 139 ++ .../lib/vied/vied/shared_memory_map.h | 53 + .../lib2600psys/lib/vied/vied/vied_config.h | 33 + .../lib/vied/vied/vied_memory_access_types.h | 36 + .../lib/vied/vied/vied_subsystem_access.h | 70 + .../vied_subsystem_access_initialization.h | 44 + .../vied/vied/vied_subsystem_access_types.h | 34 + .../lib2600psys/lib/vied/vied/vied_types.h | 45 + .../interface/vied_nci_acb_route_type.h | 39 + .../interface/ia_css_param_storage_class.h | 28 + .../interface/ia_css_terminal.h | 188 ++ .../interface/ia_css_terminal_manifest.h | 109 + .../ia_css_terminal_manifest_types.h | 342 +++ .../interface/ia_css_terminal_types.h | 351 ++++ .../lib/vied_parameters/src/ia_css_terminal.c | 20 + .../src/ia_css_terminal_impl.h | 495 +++++ .../src/ia_css_terminal_manifest.c | 20 + .../src/ia_css_terminal_manifest_impl.h | 347 +++ .../lib/vied_parameters/vied_parameters.mk | 76 + .../ipu4p-css/lib2600psys/libcsspsys2600.c | 488 +++++ .../ipu4p-css/lib2600psys/libcsspsys2600.h | 26 + .../pci/intel/ipu4/ipu4p-css/libintel-ipu4p.c | 393 ++++ .../media/pci/intel/ipu4/ipu4p-isys-csi2.c | 426 ++++ include/media/ipu-isys.h | 37 + include/uapi/linux/ipu-isys-isa-fw.h | 112 + include/uapi/linux/ipu-isys.h | 28 + include/uapi/linux/ipu-psys.h | 108 + 768 files changed, 131811 insertions(+), 3 deletions(-) create mode 100644 drivers/media/pci/intel/Kconfig create mode 100644 drivers/media/pci/intel/ipu-bus.c create mode 100644 drivers/media/pci/intel/ipu-bus.h create mode 100644 drivers/media/pci/intel/ipu-buttress.c create mode 100644 drivers/media/pci/intel/ipu-buttress.h create mode 100644 drivers/media/pci/intel/ipu-cpd.c create mode 100644 drivers/media/pci/intel/ipu-cpd.h create mode 100644 drivers/media/pci/intel/ipu-dma.c create mode 100644 drivers/media/pci/intel/ipu-dma.h create mode 100644 drivers/media/pci/intel/ipu-fw-com.c create mode 100644 drivers/media/pci/intel/ipu-fw-com.h create mode 100644 drivers/media/pci/intel/ipu-fw-isys.c create mode 100644 drivers/media/pci/intel/ipu-fw-isys.h create mode 100644 drivers/media/pci/intel/ipu-fw-psys.c create mode 100644 drivers/media/pci/intel/ipu-fw-psys.h create mode 100644 drivers/media/pci/intel/ipu-isys-csi2-be-soc.c create mode 100644 drivers/media/pci/intel/ipu-isys-csi2-be.c create mode 100644 drivers/media/pci/intel/ipu-isys-csi2-be.h create mode 100644 drivers/media/pci/intel/ipu-isys-csi2.c create mode 100644 drivers/media/pci/intel/ipu-isys-csi2.h create mode 100644 drivers/media/pci/intel/ipu-isys-media.h create mode 100644 drivers/media/pci/intel/ipu-isys-queue.c create mode 100644 drivers/media/pci/intel/ipu-isys-queue.h create mode 100644 drivers/media/pci/intel/ipu-isys-subdev.c create mode 100644 drivers/media/pci/intel/ipu-isys-subdev.h create mode 100644 drivers/media/pci/intel/ipu-isys-tpg.c create mode 100644 drivers/media/pci/intel/ipu-isys-tpg.h create mode 100644 drivers/media/pci/intel/ipu-isys-video.c create mode 100644 drivers/media/pci/intel/ipu-isys-video.h create mode 100644 drivers/media/pci/intel/ipu-isys.c create mode 100644 drivers/media/pci/intel/ipu-isys.h create mode 100644 drivers/media/pci/intel/ipu-mmu.c create mode 100644 drivers/media/pci/intel/ipu-mmu.h create mode 100644 drivers/media/pci/intel/ipu-pdata.h create mode 100644 drivers/media/pci/intel/ipu-psys-compat32.c create mode 100644 drivers/media/pci/intel/ipu-psys.c create mode 100644 drivers/media/pci/intel/ipu-psys.h create mode 100644 drivers/media/pci/intel/ipu-trace-event.h create mode 100644 drivers/media/pci/intel/ipu-trace.c create mode 100644 drivers/media/pci/intel/ipu-trace.h create mode 100644 drivers/media/pci/intel/ipu-wrapper.c create mode 100644 drivers/media/pci/intel/ipu-wrapper.h create mode 100644 drivers/media/pci/intel/ipu.c create mode 100644 drivers/media/pci/intel/ipu.h create mode 100644 drivers/media/pci/intel/ipu4/Makefile create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-buttress-regs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-isys-csi2-reg.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-isys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-psys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-regs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform-resources.h create mode 100644 drivers/media/pci/intel/ipu4/ipu-platform.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_inc create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_src create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_inc create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_src create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.isyslib create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/Makefile.psyslib create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/ipu-wrapper.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/buffer.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_buffer_address.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_return_token.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/buffer_access.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/cell.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/interface/ia_css_cell.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/src/ia_css_cell_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/isys/subsystem_bxtB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/system_bxtB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/device_access.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_cmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_func.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_type_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/isysapi.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isysapi_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/pkg_dir.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/port_env_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/port.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/queue.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/recv_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/send_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/interface/regmem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/regmem.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_access_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_const.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/assert_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/cpu_mem_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/error_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/math_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/misc_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/platform_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/print_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/type_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_config_fw.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_context.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/syscom.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/interface/ia_css_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/trace.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/utils/system_defs/system_const.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_access.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_map.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_config.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_memory_access_types.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_initialization.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_types.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/Makefile create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/DSS_V2_program_group/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/buffer.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/cell.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/interface/ia_css_cell.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/psys/subsystem_bxtB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/system_bxtB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/device_access.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_acb_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_ff_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_gp_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/pkg_dir.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/port_env_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/port.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/queue.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/recv_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/send_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/psys_server.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/bxtB0/ia_css_psys_transport_dep.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psysapi.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/interface/regmem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/regmem.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_access_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_const.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/assert_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/cpu_mem_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/error_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/math_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/misc_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/platform_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/print_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/type_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/syscom.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/interface/ia_css_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/trace.mk create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_access.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_map.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_config.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h create mode 100755 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/vied_parameters.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-css/libintel-ipu4.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-fw-resources.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-isys-csi2.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-isys-isa.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-isys-isa.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4-isys.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-psys.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4-resources.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_inc create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_src create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_inc create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_src create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.isyslib create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.psyslib create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/buffer.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_buffer_address.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_return_token.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/buffer_access.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/cell.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/interface/ia_css_cell.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/src/ia_css_cell_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/isys/subsystem_cnlB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/system_cnlB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cpd_binary/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/device_access.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_cmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_func.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_type_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/isysapi.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isysapi_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/pkg_dir.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/port_env_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/port.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/queue.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/recv_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/send_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/interface/regmem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/regmem.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_access_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_const.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/assert_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/cpu_mem_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/error_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/math_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/misc_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/platform_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/print_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/type_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_config_fw.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_context.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/syscom.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/interface/ia_css_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/trace.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/utils/system_defs/system_const.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_map.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_memory_access_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_initialization.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/Makefile create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/CNL_program_group/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/ICL_program_group/ia_css_fw_pkg_release.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/buffer.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/cell.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/interface/ia_css_cell.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/cell_program_load.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_group_load.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_prog.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_group_load_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_bin.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_prog_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_regs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/psys/subsystem_cnlB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/system_cnlB0.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/device_access.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_acb_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_ff_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_gp_devices.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/fw_load.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_blocking_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_host_state.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/pkg_dir.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/port_env_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port_struct.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/port.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/queue.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/recv_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/send_port.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/interface/psys_infobits.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/psys_infobits.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/src/psys_infobits.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/psys_server.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/cnlB0/ia_css_psys_transport_dep.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psysapi.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/interface/regmem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/regmem.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_access_host.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_const.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/assert_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/cpu_mem_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/error_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/math_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/misc_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/platform_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/print_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/type_support.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/syscom.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/interface/ia_css_trace.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/trace.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_map.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_config.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/vied_parameters.mk create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.h create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-css/libintel-ipu4p.c create mode 100644 drivers/media/pci/intel/ipu4/ipu4p-isys-csi2.c create mode 100644 include/media/ipu-isys.h create mode 100644 include/uapi/linux/ipu-isys-isa-fw.h create mode 100644 include/uapi/linux/ipu-isys.h create mode 100644 include/uapi/linux/ipu-psys.h diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 1f09123e2bf9..8e6a0108d9c1 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -16,6 +16,7 @@ source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" +source "drivers/media/pci/intel/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig new file mode 100644 index 000000000000..f9fcbc6c72b2 --- /dev/null +++ b/drivers/media/pci/intel/Kconfig @@ -0,0 +1,59 @@ +config VIDEO_INTEL_IPU + tristate "Intel IPU driver" + depends on ACPI + select IOMMU_API + select IOMMU_IOVA + select X86_DEV_DMA_OPS if X86 + select VIDEOBUF2_DMA_CONTIG + select PHYS_ADDR_T_64BIT + select COMMON_CLK + ---help--- + Say Y here! + +choice + prompt "intel ipu generation type" + depends on VIDEO_INTEL_IPU + default VIDEO_INTEL_IPU4 + +config VIDEO_INTEL_IPU4 + bool "Compile for IPU4 driver" + ---help--- + Say Y here! + +config VIDEO_INTEL_IPU4P + bool "Compile for IPU4P driver" + ---help--- + Say Y here! + +endchoice + +choice + prompt "intel ipu hardware platform type" + depends on VIDEO_INTEL_IPU + default VIDEO_INTEL_IPU_SOC + +config VIDEO_INTEL_IPU_SOC + bool "Compile for SOC" + ---help--- + Select for SOC platform + +endchoice + +config VIDEO_INTEL_IPU_FW_LIB + bool "Compile firmware library" + ---help--- + If selected, the firmware hostlib css would be compiled + +config VIDEO_INTEL_IPU_WERROR + bool "Force GCC to throw an error instead of a warning when compiling" + depends on VIDEO_INTEL_IPU + depends on EXPERT + depends on !COMPILE_TEST + default n + help + Add -Werror to the build flags for (and only for) intel ipu module. + Do not enable this unless you are writing code for the ipu module. + + Recommended for driver developers only. + + If in doubt, say "N". diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile index 745c8b2a7819..f26961842450 100644 --- a/drivers/media/pci/intel/Makefile +++ b/drivers/media/pci/intel/Makefile @@ -1,5 +1,14 @@ -# -# Makefile for the IPU3 cio2 and ImGU drivers -# +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2018, Intel Corporation. +# force check the compile warning to make sure zero warnings +# note we may have build issue when gcc upgraded. +subdir-ccflags-y := -Wall -Wextra +subdir-ccflags-y += $(call cc-disable-warning, unused-parameter) +subdir-ccflags-y += $(call cc-disable-warning, implicit-fallthrough) +subdir-ccflags-y += $(call cc-disable-warning, missing-field-initializers) +subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU_WERROR) += -Werror + +obj-$(CONFIG_VIDEO_INTEL_IPU4) += ipu4/ +obj-$(CONFIG_VIDEO_INTEL_IPU4P) += ipu4/ obj-y += ipu3/ diff --git a/drivers/media/pci/intel/ipu-bus.c b/drivers/media/pci/intel/ipu-bus.c new file mode 100644 index 000000000000..30e50337d3b3 --- /dev/null +++ b/drivers/media/pci/intel/ipu-bus.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-platform.h" +#include "ipu-dma.h" +#include "ipu-mmu.h" + +#ifdef CONFIG_PM +static struct bus_type ipu_bus; + +static int bus_pm_suspend_child_dev(struct device *dev, void *p) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct device *parent = (struct device *)p; + + if (!ipu_bus_get_drvdata(adev)) + return 0; /* Device not attached to any driver yet */ + + if (dev->parent != parent || adev->ctrl) + return 0; + + return pm_generic_runtime_suspend(dev); +} + +static int bus_pm_runtime_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + int rval; + + if (!adev->ctrl) { + dev_dbg(dev, "has no buttress control info, bailing out\n"); + return 0; + } + + rval = bus_for_each_dev(&ipu_bus, NULL, dev, bus_pm_suspend_child_dev); + if (rval) { + dev_err(dev, "failed to suspend child device\n"); + return rval; + } + + rval = pm_generic_runtime_suspend(dev); + if (rval) + return rval; + + rval = ipu_buttress_power(dev, adev->ctrl, false); + dev_dbg(dev, "%s: buttress power down %d\n", __func__, rval); + if (!rval) + return 0; + + dev_err(dev, "power down failed!\n"); + + /* Powering down failed, attempt to resume device now */ + rval = pm_generic_runtime_resume(dev); + if (!rval) + return -EBUSY; + + return -EIO; +} + +static int bus_pm_resume_child_dev(struct device *dev, void *p) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct device *parent = (struct device *)p; + int r; + + if (!ipu_bus_get_drvdata(adev)) + return 0; /* Device not attached to any driver yet */ + + if (dev->parent != parent || adev->ctrl) + return 0; + + mutex_lock(&adev->resume_lock); + r = pm_generic_runtime_resume(dev); + mutex_unlock(&adev->resume_lock); + return r; +} + +static int bus_pm_runtime_resume(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + int rval; + + if (!adev->ctrl) { + dev_dbg(dev, "has no buttress control info, bailing out\n"); + return 0; + } + + rval = ipu_buttress_power(dev, adev->ctrl, true); + dev_dbg(dev, "%s: buttress power up %d\n", __func__, rval); + if (rval) + return rval; + + rval = pm_generic_runtime_resume(dev); + dev_dbg(dev, "%s: resume %d\n", __func__, rval); + if (rval) + goto out_err; + + /* + * It needs to be ensured that IPU child devices' resume/suspend are + * called only when the child devices' power is turned on/off by the + * parent device here. Therefore, children's suspend/resume are called + * from here, because that is the only way to guarantee it. + */ + rval = bus_for_each_dev(&ipu_bus, NULL, dev, bus_pm_resume_child_dev); + if (rval) { + dev_err(dev, "failed to resume child device - reset it\n"); + + rval = pm_generic_runtime_suspend(dev); + dev_dbg(dev, "%s: suspend %d\n", __func__, rval); + + rval = ipu_buttress_power(dev, adev->ctrl, false); + dev_dbg(dev, "%s: buttress power down %d\n", __func__, rval); + if (rval) + return rval; + + usleep_range(1000, 1100); + + rval = ipu_buttress_power(dev, adev->ctrl, true); + dev_dbg(dev, "%s: buttress power up %d\n", __func__, rval); + if (rval) + return rval; + + rval = pm_generic_runtime_resume(dev); + dev_dbg(dev, "%s: re-resume %d\n", __func__, rval); + if (rval) + goto out_err; + + rval = bus_for_each_dev(&ipu_bus, NULL, dev, + bus_pm_resume_child_dev); + + if (rval) { + dev_err(dev, "resume retry failed\n"); + goto out_err; + } + } + + return 0; + +out_err: + if (adev->ctrl) + ipu_buttress_power(dev, adev->ctrl, false); + + return -EBUSY; +} + +static const struct dev_pm_ops ipu_bus_pm_ops = { + .runtime_suspend = bus_pm_runtime_suspend, + .runtime_resume = bus_pm_runtime_resume, +}; + +#define IPU_BUS_PM_OPS (&ipu_bus_pm_ops) +#else +#define IPU_BUS_PM_OPS NULL +#endif + +static int ipu_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ipu_bus_driver *adrv = to_ipu_bus_driver(drv); + + dev_dbg(dev, "bus match: \"%s\" --- \"%s\"\n", dev_name(dev), + adrv->wanted); + + return !strncmp(dev_name(dev), adrv->wanted, strlen(adrv->wanted)); +} + +static struct ipu_dma_mapping *alloc_dma_mapping(struct device *dev) +{ + struct ipu_dma_mapping *dmap; + + dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); + if (!dmap) + return NULL; + + dmap->domain = iommu_domain_alloc(dev->bus); + if (!dmap->domain) { + kfree(dmap); + return NULL; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + init_iova_domain(&dmap->iovad, dma_get_mask(dev) >> PAGE_SHIFT); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) + init_iova_domain(&dmap->iovad, SZ_4K, 1, + dma_get_mask(dev) >> PAGE_SHIFT); +#else + init_iova_domain(&dmap->iovad, SZ_4K, 1); +#endif + + kref_init(&dmap->ref); + + pr_debug("alloc mapping\n"); + + iova_cache_get(); + + return dmap; +} + +static void free_dma_mapping(void *ptr) +{ + struct ipu_mmu *mmu = ptr; + struct ipu_dma_mapping *dmap = mmu->dmap; + + iommu_domain_free(dmap->domain); + mmu->set_mapping(mmu, NULL); + iova_cache_put(); + put_iova_domain(&dmap->iovad); + kfree(dmap); +} + +static struct iommu_group *ipu_bus_get_group(struct device *dev) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct iommu_group *group; + struct ipu_dma_mapping *dmap; + + if (!mmu) { + dev_err(dev, "%s: no iommu available\n", __func__); + return NULL; + } + + group = iommu_group_get(dev); + if (group) + return group; + + group = iommu_group_alloc(); + if (!group) { + dev_err(dev, "%s: can't alloc iommu group\n", __func__); + return NULL; + } + + dmap = alloc_dma_mapping(dev); + if (!dmap) { + dev_err(dev, "%s: can't alloc dma mapping\n", __func__); + iommu_group_put(group); + return NULL; + } + + iommu_group_set_iommudata(group, mmu, free_dma_mapping); + + /* + * Turn mmu on and off synchronously. Otherwise it may still be on + * at psys / isys probing phase and that may cause problems on + * develoment environments. + */ + pm_runtime_get_sync(aiommu); + mmu->set_mapping(mmu, dmap); + pm_runtime_put_sync(aiommu); + + return group; +} + +static int ipu_bus_probe(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_bus_driver *adrv = to_ipu_bus_driver(dev->driver); + struct iommu_group *group = NULL; + int rval; + + dev_dbg(dev, "bus probe dev %s\n", dev_name(dev)); + + if (adev->iommu) { + dev_dbg(dev, "iommu %s\n", dev_name(adev->iommu)); + + group = ipu_bus_get_group(dev); + if (!group) + return -EPROBE_DEFER; + + rval = iommu_group_add_device(group, dev); + if (rval) + goto out_err; + } + + adev->adrv = adrv; + if (adrv->probe) { + rval = adrv->probe(adev); + if (!rval) { + /* + * If the device power, after probe, is enabled + * (from the parent device), its resume needs to + * be called to initialize the device properly. + */ + if (!adev->ctrl && + !pm_runtime_status_suspended(dev->parent)) { + mutex_lock(&adev->resume_lock); + pm_generic_runtime_resume(dev); + mutex_unlock(&adev->resume_lock); + } + } + } else { + rval = -ENODEV; + } + + if (rval) + goto out_err; + + return 0; + +out_err: + ipu_bus_set_drvdata(adev, NULL); + adev->adrv = NULL; + iommu_group_remove_device(dev); + iommu_group_put(group); + return rval; +} + +static int ipu_bus_remove(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_bus_driver *adrv = to_ipu_bus_driver(dev->driver); + + if (adrv->remove) + adrv->remove(adev); + + if (adev->iommu) + iommu_group_remove_device(dev); + + return 0; +} + +static struct bus_type ipu_bus = { + .name = IPU_BUS_NAME, + .match = ipu_bus_match, + .probe = ipu_bus_probe, + .remove = ipu_bus_remove, + .pm = IPU_BUS_PM_OPS, +}; + +static struct mutex ipu_bus_mutex; + +static void ipu_bus_release(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + + kfree(adev); +} + +struct ipu_bus_device *ipu_bus_add_device(struct pci_dev *pdev, + struct device *parent, void *pdata, + struct device *iommu, + struct ipu_buttress_ctrl *ctrl, + char *name, unsigned int nr) +{ + struct ipu_bus_device *adev; + struct ipu_device *isp = pci_get_drvdata(pdev); + int rval; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + adev->dev.parent = parent; + adev->dev.bus = &ipu_bus; + adev->dev.release = ipu_bus_release; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 16) + adev->dev.dma_ops = &ipu_dma_ops; +#else + adev->dev.archdata.dma_ops = &ipu_dma_ops; +#endif + adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? + IPU_MMU_ADDRESS_BITS : + IPU_MMU_ADDRESS_BITS_NON_SECURE); + adev->dev.dma_mask = &adev->dma_mask; + adev->dev.coherent_dma_mask = adev->dma_mask; + adev->iommu = iommu; + adev->ctrl = ctrl; + adev->pdata = pdata; + adev->isp = isp; + mutex_init(&adev->resume_lock); + dev_set_name(&adev->dev, "%s%d", name, nr); + + rval = device_register(&adev->dev); + if (rval) { + put_device(&adev->dev); + return ERR_PTR(rval); + } + + mutex_lock(&ipu_bus_mutex); + list_add(&adev->list, &isp->devices); + mutex_unlock(&ipu_bus_mutex); + + return adev; +} + +void ipu_bus_del_devices(struct pci_dev *pdev) +{ + struct ipu_device *isp = pci_get_drvdata(pdev); + struct ipu_bus_device *adev, *save; + + mutex_lock(&ipu_bus_mutex); + + list_for_each_entry_safe(adev, save, &isp->devices, list) { + list_del(&adev->list); + device_unregister(&adev->dev); + } + + mutex_unlock(&ipu_bus_mutex); +} + +int ipu_bus_register_driver(struct ipu_bus_driver *adrv) +{ + adrv->drv.bus = &ipu_bus; + return driver_register(&adrv->drv); +} +EXPORT_SYMBOL(ipu_bus_register_driver); + +int ipu_bus_unregister_driver(struct ipu_bus_driver *adrv) +{ + driver_unregister(&adrv->drv); + return 0; +} +EXPORT_SYMBOL(ipu_bus_unregister_driver); + +int ipu_bus_register(void) +{ + mutex_init(&ipu_bus_mutex); + return bus_register(&ipu_bus); +} +EXPORT_SYMBOL(ipu_bus_register); + +void ipu_bus_unregister(void) +{ + mutex_destroy(&ipu_bus_mutex); + return bus_unregister(&ipu_bus); +} +EXPORT_SYMBOL(ipu_bus_unregister); + +int ipu_bus_set_iommu(struct iommu_ops *ops) +{ + if (iommu_present(&ipu_bus)) + return 0; + + return bus_set_iommu(&ipu_bus, ops); +} +EXPORT_SYMBOL(ipu_bus_set_iommu); + +static int flr_rpm_recovery(struct device *dev, void *p) +{ + dev_dbg(dev, "FLR recovery call\n"); + /* + * We are not necessarily going through device from child to + * parent. runtime PM refuses to change state for parent if the child + * is still active. At FLR (full reset for whole IPU) that doesn't + * matter. Everything has been power gated by HW during the FLR cycle + * and we are just cleaning up SW state. Thus, ignore child during + * set_suspended. + */ + pm_suspend_ignore_children(dev, true); + pm_runtime_set_suspended(dev); + pm_suspend_ignore_children(dev, false); + + return 0; +} + +int ipu_bus_flr_recovery(void) +{ + bus_for_each_dev(&ipu_bus, NULL, NULL, flr_rpm_recovery); + return 0; +} +EXPORT_SYMBOL(ipu_bus_flr_recovery); diff --git a/drivers/media/pci/intel/ipu-bus.h b/drivers/media/pci/intel/ipu-bus.h new file mode 100644 index 000000000000..4226b865fe99 --- /dev/null +++ b/drivers/media/pci/intel/ipu-bus.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_BUS_H +#define IPU_BUS_H + +#include +#include +#include +#include +#include + +#define IPU_BUS_NAME IPU_NAME "-bus" + +struct ipu_buttress_ctrl; +struct ipu_subsystem_trace_config; + +struct ipu_bus_device { + struct device dev; + struct list_head list; + void *pdata; + struct ipu_bus_driver *adrv; + struct device *iommu; + struct ipu_device *isp; + struct ipu_subsystem_trace_config *trace_cfg; + struct ipu_buttress_ctrl *ctrl; + u64 dma_mask; + /* Protect runtime_resume calls on the dev */ + struct mutex resume_lock; +}; + +#define to_ipu_bus_device(_dev) container_of(_dev, struct ipu_bus_device, dev) + +struct ipu_bus_driver { + struct device_driver drv; + char wanted[20]; + int (*probe)(struct ipu_bus_device *adev); + void (*remove)(struct ipu_bus_device *adev); + irqreturn_t (*isr)(struct ipu_bus_device *adev); + irqreturn_t (*isr_threaded)(struct ipu_bus_device *adev); + bool wake_isr_thread; +}; + +#define to_ipu_bus_driver(_drv) container_of(_drv, struct ipu_bus_driver, drv) + +struct ipu_bus_device *ipu_bus_add_device(struct pci_dev *pdev, + struct device *parent, void *pdata, + struct device *iommu, + struct ipu_buttress_ctrl *ctrl, + char *name, unsigned int nr); +void ipu_bus_del_devices(struct pci_dev *pdev); + +int ipu_bus_register_driver(struct ipu_bus_driver *adrv); +int ipu_bus_unregister_driver(struct ipu_bus_driver *adrv); + +int ipu_bus_register(void); +void ipu_bus_unregister(void); + +int ipu_bus_set_iommu(struct iommu_ops *ops); + +#define module_ipu_bus_driver(drv) \ + module_driver(drv, ipu_bus_register_driver, \ + ipu_bus_unregister_driver) + +#define ipu_bus_set_drvdata(adev, data) dev_set_drvdata(&(adev)->dev, data) +#define ipu_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->dev) + +int ipu_bus_flr_recovery(void); + +#endif diff --git a/drivers/media/pci/intel/ipu-buttress.c b/drivers/media/pci/intel/ipu-buttress.c new file mode 100644 index 000000000000..b823c0fc541e --- /dev/null +++ b/drivers/media/pci/intel/ipu-buttress.c @@ -0,0 +1,1834 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-buttress.h" +#include "ipu-platform-buttress-regs.h" +#include "ipu-cpd.h" +#define CREATE_TRACE_POINTS +#define IPU_PERF_REG_TRACE +#include "ipu-trace-event.h" + +#define BOOTLOADER_STATUS_OFFSET 0x8000 +#define BOOTLOADER_MAGIC_KEY 0xb00710ad + +#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 +#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 +#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE + +#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10 + +#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT 5000 +#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT 10000 +#define BUTTRESS_CSE_FWRESET_TIMEOUT 100 + +#define BUTTRESS_IPC_TX_TIMEOUT 1000 +#define BUTTRESS_IPC_RX_TIMEOUT 1000 +#define BUTTRESS_IPC_VALIDITY_TIMEOUT 1000 + +#define IPU_BUTTRESS_TSC_LIMIT 500 /* 26 us @ 19.2 MHz */ +#define IPU_BUTTRESS_TSC_RETRY 10 + +#define BUTTRESS_CSE_IPC_RESET_RETRY 4 + +#define BUTTRESS_IPC_CMD_SEND_RETRY 1 + +static const struct ipu_buttress_sensor_clk_freq sensor_clk_freqs[] = { + {6750000, BUTTRESS_SENSOR_CLK_FREQ_6P75MHZ}, + {8000000, BUTTRESS_SENSOR_CLK_FREQ_8MHZ}, + {9600000, BUTTRESS_SENSOR_CLK_FREQ_9P6MHZ}, + {12000000, BUTTRESS_SENSOR_CLK_FREQ_12MHZ}, + {13600000, BUTTRESS_SENSOR_CLK_FREQ_13P6MHZ}, + {14400000, BUTTRESS_SENSOR_CLK_FREQ_14P4MHZ}, + {15800000, BUTTRESS_SENSOR_CLK_FREQ_15P8MHZ}, + {16200000, BUTTRESS_SENSOR_CLK_FREQ_16P2MHZ}, + {17300000, BUTTRESS_SENSOR_CLK_FREQ_17P3MHZ}, + {18600000, BUTTRESS_SENSOR_CLK_FREQ_18P6MHZ}, + {19200000, BUTTRESS_SENSOR_CLK_FREQ_19P2MHZ}, + {24000000, BUTTRESS_SENSOR_CLK_FREQ_24MHZ}, + {26000000, BUTTRESS_SENSOR_CLK_FREQ_26MHZ}, + {27000000, BUTTRESS_SENSOR_CLK_FREQ_27MHZ} +}; + +static const u32 ipu_adev_irq_mask[] = { + BUTTRESS_ISR_IS_IRQ, BUTTRESS_ISR_PS_IRQ +}; + +int ipu_buttress_ipc_reset(struct ipu_device *isp, struct ipu_buttress_ipc *ipc) +{ + struct ipu_buttress *b = &isp->buttress; + unsigned long tout_jfs; + unsigned int tout = 500; + u32 val = 0, csr_in_clr; + + mutex_lock(&b->ipc_mutex); + + /* Clear-by-1 CSR (all bits), corresponding internal states. */ + val = readl(isp->base + ipc->csr_in); + writel(val, isp->base + ipc->csr_in); + + /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ + writel(ENTRY, isp->base + ipc->csr_out); + + /* + * Clear-by-1 all CSR bits EXCEPT following + * bits: + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * C. Possibly custom bits, depending on + * their role. + */ + csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | + BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | + BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; + + /* + * How long we should wait here? + */ + tout_jfs = jiffies + msecs_to_jiffies(tout); + do { + val = readl(isp->base + ipc->csr_in); + dev_dbg(&isp->pdev->dev, "%s: csr_in = %x\n", __func__, val); + if (val & ENTRY) { + if (val & EXIT) { + dev_dbg(&isp->pdev->dev, + "%s:%s & %s\n", + __func__, + "IPC_PEER_COMP_ACTIONS_RST_PHASE1", + "IPC_PEER_COMP_ACTIONS_RST_PHASE2"); + /* + * 1) Clear-by-1 CSR bits + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, + * IPC_PEER_COMP_ACTIONS_RST_PHASE2). + * 2) Set peer CSR bit + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. + */ + writel(ENTRY | EXIT, + isp->base + ipc->csr_in); + + writel(QUERY, isp->base + ipc->csr_out); + + tout_jfs = jiffies + msecs_to_jiffies(tout); + continue; + } else { + dev_dbg(&isp->pdev->dev, + "%s:IPC_PEER_COMP_ACTIONS_RST_PHASE1\n", + __func__); + /* + * 1) Clear-by-1 CSR bits + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). + * 2) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE1. + */ + writel(ENTRY | QUERY, + isp->base + ipc->csr_in); + + writel(ENTRY, isp->base + ipc->csr_out); + + tout_jfs = jiffies + msecs_to_jiffies(tout); + continue; + } + } else if (val & EXIT) { + dev_dbg(&isp->pdev->dev, + "%s: IPC_PEER_COMP_ACTIONS_RST_PHASE2\n", + __func__); + /* + * Clear-by-1 CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * 1) Clear incoming doorbell. + * 2) Clear-by-1 all CSR bits EXCEPT following + * bits: + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * C. Possibly custom bits, depending on + * their role. + * 3) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. + */ + writel(EXIT, isp->base + ipc->csr_in); + + writel(0 << BUTTRESS_IU2CSEDB0_BUSY_SHIFT, + isp->base + ipc->db0_in); + + writel(csr_in_clr, isp->base + ipc->csr_in); + + writel(EXIT, isp->base + ipc->csr_out); + + /* + * Read csr_in again to make sure if RST_PHASE2 is done. + * If csr_in is QUERY, it should be handled again. + */ + usleep_range(100, 500); + val = readl(isp->base + ipc->csr_in); + if (val & QUERY) { + dev_dbg(&isp->pdev->dev, + "%s: RST_PHASE2 retry csr_in = %x\n", + __func__, val); + continue; + } + + mutex_unlock(&b->ipc_mutex); + + return 0; + } else if (val & QUERY) { + dev_dbg(&isp->pdev->dev, + "%s: %s\n", + __func__, + "IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE"); + /* + * 1) Clear-by-1 CSR bit + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. + * 2) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE1 + */ + writel(QUERY, isp->base + ipc->csr_in); + + writel(ENTRY, isp->base + ipc->csr_out); + + tout_jfs = jiffies + msecs_to_jiffies(tout); + } + usleep_range(100, 500); + } while (!time_after(jiffies, tout_jfs)); + + mutex_unlock(&b->ipc_mutex); + + dev_err(&isp->pdev->dev, "Timed out while waiting for CSE!\n"); + + return -ETIMEDOUT; +} + +static void +ipu_buttress_ipc_validity_close(struct ipu_device *isp, + struct ipu_buttress_ipc *ipc) +{ + /* Set bit 5 in CSE CSR */ + writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, + isp->base + ipc->csr_out); +} + +static int +ipu_buttress_ipc_validity_open(struct ipu_device *isp, + struct ipu_buttress_ipc *ipc) +{ + unsigned long tout_jfs; + unsigned int tout = BUTTRESS_IPC_VALIDITY_TIMEOUT; + u32 val; + + /* Set bit 3 in CSE CSR */ + writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, + isp->base + ipc->csr_out); + + /* + * How long we should wait here? + */ + tout_jfs = jiffies + msecs_to_jiffies(tout); + do { + val = readl(isp->base + ipc->csr_in); + dev_dbg(&isp->pdev->dev, "%s: CSE/ISH2IUCSR = %x\n", + __func__, val); + + if (val & BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID) { + dev_dbg(&isp->pdev->dev, + "%s: Validity ack received from peer\n", + __func__); + return 0; + } + usleep_range(100, 1000); + } while (!time_after(jiffies, tout_jfs)); + + dev_err(&isp->pdev->dev, "Timed out while waiting for CSE!\n"); + + ipu_buttress_ipc_validity_close(isp, ipc); + + return -ETIMEDOUT; +} + +static void ipu_buttress_ipc_recv(struct ipu_device *isp, + struct ipu_buttress_ipc *ipc, u32 *ipc_msg) +{ + if (ipc_msg) + *ipc_msg = readl(isp->base + ipc->data0_in); + writel(0, isp->base + ipc->db0_in); +} + +int +ipu_buttress_ipc_send_bulk(struct ipu_device *isp, + enum ipu_buttress_ipc_domain ipc_domain, + struct ipu_ipc_buttress_bulk_msg *msgs, u32 size) +{ + struct ipu_buttress *b = &isp->buttress; + struct ipu_buttress_ipc *ipc; + unsigned long tx_timeout_jiffies, rx_timeout_jiffies; + u32 val; + int ret; + int tout; + unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY; + + ipc = ipc_domain == IPU_BUTTRESS_IPC_CSE ? &b->cse : &b->ish; + + mutex_lock(&b->ipc_mutex); + + ret = ipu_buttress_ipc_validity_open(isp, ipc); + if (ret) { + dev_err(&isp->pdev->dev, "IPC validity open failed\n"); + goto out; + } + + tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT); + rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT); + + for (i = 0; i < size; i++) { + reinit_completion(&ipc->send_complete); + if (msgs[i].require_resp) + reinit_completion(&ipc->recv_complete); + + dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n", + msgs[i].cmd); + writel(msgs[i].cmd, isp->base + ipc->data0_out); + + val = 1 << BUTTRESS_IU2CSEDB0_BUSY_SHIFT | msgs[i].cmd_size; + + writel(val, isp->base + ipc->db0_out); + + tout = wait_for_completion_timeout(&ipc->send_complete, + tx_timeout_jiffies); + if (!tout) { + dev_err(&isp->pdev->dev, "send IPC response timeout\n"); + if (!retry--) { + ret = -ETIMEDOUT; + goto out; + } + + /* + * WORKAROUND: Sometimes CSE is not + * responding on first try, try again. + */ + writel(0, isp->base + ipc->db0_out); + i--; + continue; + } + + retry = BUTTRESS_IPC_CMD_SEND_RETRY; + + if (!msgs[i].require_resp) + continue; + + tout = wait_for_completion_timeout(&ipc->recv_complete, + rx_timeout_jiffies); + if (!tout) { + dev_err(&isp->pdev->dev, "recv IPC response timeout\n"); + ret = -ETIMEDOUT; + goto out; + } + + if (ipc->nack_mask && + (ipc->recv_data & ipc->nack_mask) == ipc->nack) { + dev_err(&isp->pdev->dev, + "IPC NACK for cmd 0x%x\n", msgs[i].cmd); + ret = -ENODEV; + goto out; + } + + if (ipc->recv_data != msgs[i].expected_resp) { + dev_err(&isp->pdev->dev, + "expected resp: 0x%x, IPC response: 0x%x ", + msgs[i].expected_resp, ipc->recv_data); + ret = -EIO; + goto out; + } + } + + dev_dbg(&isp->pdev->dev, "bulk IPC commands completed\n"); + +out: + ipu_buttress_ipc_validity_close(isp, ipc); + mutex_unlock(&b->ipc_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ipu_buttress_ipc_send_bulk); + +static int +ipu_buttress_ipc_send(struct ipu_device *isp, + enum ipu_buttress_ipc_domain ipc_domain, + u32 ipc_msg, u32 size) +{ + struct ipu_ipc_buttress_bulk_msg msg = { + .cmd = ipc_msg, + .cmd_size = size, + }; + + return ipu_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1); +} + +static irqreturn_t ipu_buttress_call_isr(struct ipu_bus_device *adev) +{ + irqreturn_t ret = IRQ_WAKE_THREAD; + + if (!adev || !adev->adrv) + return IRQ_NONE; + + if (adev->adrv->isr) + ret = adev->adrv->isr(adev); + + if (ret == IRQ_WAKE_THREAD && !adev->adrv->isr_threaded) + ret = IRQ_NONE; + + adev->adrv->wake_isr_thread = (ret == IRQ_WAKE_THREAD); + + return ret; +} + +irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) +{ + struct ipu_device *isp = isp_ptr; + struct ipu_bus_device *adev[] = { isp->isys, isp->psys }; + struct ipu_buttress *b = &isp->buttress; + irqreturn_t ret = IRQ_NONE; + u32 disable_irqs = 0; + u32 irq_status; +#ifdef CONFIG_VIDEO_INTEL_IPU4P + u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS; +#else + u32 reg_irq_sts = BUTTRESS_REG_ISR_ENABLED_STATUS; +#endif + unsigned int i; + + dev_dbg(&isp->pdev->dev, "isr: Buttress interrupt handler\n"); + + pm_runtime_get(&isp->pdev->dev); + + if (!pm_runtime_active(&isp->pdev->dev)) { + irq_status = readl(isp->base + reg_irq_sts); + writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); + pm_runtime_put(&isp->pdev->dev); + return IRQ_HANDLED; + } + + trace_ipu_perf_reg(BUTTRESS_REG_IS_FREQ_CTL, + readl(isp->base + BUTTRESS_REG_IS_FREQ_CTL)); + trace_ipu_perf_reg(BUTTRESS_REG_PS_FREQ_CTL, + readl(isp->base + BUTTRESS_REG_PS_FREQ_CTL)); + + irq_status = readl(isp->base + reg_irq_sts); + if (!irq_status) { + pm_runtime_put(&isp->pdev->dev); + return IRQ_NONE; + } + + do { + writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); + + for (i = 0; i < ARRAY_SIZE(ipu_adev_irq_mask); i++) { + if (irq_status & ipu_adev_irq_mask[i]) { + irqreturn_t r = ipu_buttress_call_isr(adev[i]); + + if (r == IRQ_WAKE_THREAD) { + ret = IRQ_WAKE_THREAD; + disable_irqs |= ipu_adev_irq_mask[i]; + } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { + ret = IRQ_HANDLED; + } + } + } + + if (irq_status & (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | + BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | + BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | + BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | + BUTTRESS_ISR_SAI_VIOLATION) && + ret == IRQ_NONE) + ret = IRQ_HANDLED; + + if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) { + dev_dbg(&isp->pdev->dev, + "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n"); + ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); + complete(&b->cse.recv_complete); + } + + if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) { + dev_dbg(&isp->pdev->dev, + "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n"); + ipu_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data); + complete(&b->ish.recv_complete); + } + + if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) { + dev_dbg(&isp->pdev->dev, + "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); + complete(&b->cse.send_complete); + } + + if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) { + dev_dbg(&isp->pdev->dev, + "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); + complete(&b->ish.send_complete); + } + + if (irq_status & BUTTRESS_ISR_SAI_VIOLATION) { + dev_err(&isp->pdev->dev, + "BUTTRESS_ISR_SAI_VIOLATION\n"); + WARN_ON(1); + } + + irq_status = readl(isp->base + reg_irq_sts); + } while (irq_status && !isp->flr_done); + + if (disable_irqs) + writel(BUTTRESS_IRQS & ~disable_irqs, + isp->base + BUTTRESS_REG_ISR_ENABLE); + + pm_runtime_put(&isp->pdev->dev); + + return ret; +} + +irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) +{ + struct ipu_device *isp = isp_ptr; + struct ipu_bus_device *adev[] = { isp->isys, isp->psys }; + irqreturn_t ret = IRQ_NONE; + unsigned int i; + + dev_dbg(&isp->pdev->dev, "isr: Buttress threaded interrupt handler\n"); + + for (i = 0; i < ARRAY_SIZE(ipu_adev_irq_mask); i++) { + if (adev[i] && adev[i]->adrv && + adev[i]->adrv->wake_isr_thread && + adev[i]->adrv->isr_threaded(adev[i]) == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); + + return ret; +} + +int ipu_buttress_power(struct device *dev, + struct ipu_buttress_ctrl *ctrl, bool on) +{ + struct ipu_device *isp = to_ipu_bus_device(dev)->isp; + unsigned long tout_jfs; + u32 pwr_sts, val; + int ret = 0; + + if (!ctrl) + return 0; + + /* Until FLR completion nothing is expected to work */ + if (isp->flr_done) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + + if (!on) { + val = 0; + pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; + } else { + val = 1 << BUTTRESS_FREQ_CTL_START_SHIFT + | ctrl->divisor << ctrl->divisor_shift + | ctrl->qos_floor << BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT; + + pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; + } + + val |= ctrl->ovrd << ctrl->ovrd_shift; + writel(val, isp->base + ctrl->freq_ctl); + + tout_jfs = jiffies + msecs_to_jiffies(BUTTRESS_POWER_TIMEOUT); + do { + usleep_range(10, 40); + val = readl(isp->base + BUTTRESS_REG_PWR_STATE); + if ((val & ctrl->pwr_sts_mask) == pwr_sts) { + dev_dbg(&isp->pdev->dev, + "Rail state successfully changed\n"); + goto out; + } + } while (!time_after(jiffies, tout_jfs)); + + dev_err(&isp->pdev->dev, + "Timeout when trying to change state of the rail 0x%x\n", val); + + ret = -ETIMEDOUT; + +out: + ctrl->started = !ret && on; + + trace_ipu_perf_reg(BUTTRESS_REG_IS_FREQ_CTL, + readl(isp->base + BUTTRESS_REG_IS_FREQ_CTL)); + trace_ipu_perf_reg(BUTTRESS_REG_PS_FREQ_CTL, + readl(isp->base + BUTTRESS_REG_PS_FREQ_CTL)); + + mutex_unlock(&isp->buttress.power_mutex); + + return ret; +} + +static bool secure_mode_enable = 1; +module_param(secure_mode_enable, bool, 0660); +MODULE_PARM_DESC(secure_mode, "IPU secure mode enable"); + +void ipu_buttress_set_secure_mode(struct ipu_device *isp) +{ + u8 retry = 100; + u32 val, read; + + /* + * HACK to disable possible secure mode. This can be + * reverted when CSE is disabling the secure mode + */ + read = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + + if (secure_mode_enable) + val = read |= 1 << BUTTRESS_SECURITY_CTL_FW_SECURE_MODE_SHIFT; + else + val = read & ~(1 << BUTTRESS_SECURITY_CTL_FW_SECURE_MODE_SHIFT); + + if (val == read) + return; + + writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); + + /* In B0, for some registers in buttress, because of a hw bug, write + * might not succeed at first attempt. Write twice until the + * write is successful + */ + writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); + + while (retry--) { + read = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + if (read == val) + break; + + writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); + + if (retry == 0) + dev_err(&isp->pdev->dev, + "update security control register failed\n"); + } +} +EXPORT_SYMBOL_GPL(ipu_buttress_set_secure_mode); + +bool ipu_buttress_get_secure_mode(struct ipu_device *isp) +{ + u32 val; + + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + + return val & (1 << BUTTRESS_SECURITY_CTL_FW_SECURE_MODE_SHIFT); +} + +bool ipu_buttress_auth_done(struct ipu_device *isp) +{ + u32 val; + + if (!isp->secure_mode) + return 1; + + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + + return (val & BUTTRESS_SECURITY_CTL_FW_SETUP_MASK) == + BUTTRESS_SECURITY_CTL_AUTH_DONE; +} +EXPORT_SYMBOL(ipu_buttress_auth_done); + +static void ipu_buttress_set_psys_ratio(struct ipu_device *isp, + unsigned int psys_divisor, + unsigned int psys_qos_floor) +{ + struct ipu_buttress_ctrl *ctrl = isp->psys_iommu->ctrl; + + mutex_lock(&isp->buttress.power_mutex); + + if (ctrl->divisor == psys_divisor && ctrl->qos_floor == psys_qos_floor) + goto out_mutex_unlock; + + ctrl->divisor = psys_divisor; + ctrl->qos_floor = psys_qos_floor; + + if (ctrl->started) { + /* + * According to documentation driver initiates DVFS + * transition by writing wanted ratio, floor ratio and start + * bit. No need to stop PS first + */ + writel(1 << BUTTRESS_FREQ_CTL_START_SHIFT | + ctrl-> + qos_floor << BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT | + psys_divisor, isp->base + BUTTRESS_REG_PS_FREQ_CTL); + } + +out_mutex_unlock: + mutex_unlock(&isp->buttress.power_mutex); +} + +static void ipu_buttress_set_psys_freq(struct ipu_device *isp, + unsigned int freq) +{ + unsigned int psys_ratio = freq / BUTTRESS_PS_FREQ_STEP; + + if (isp->buttress.psys_force_ratio) + return; + + ipu_buttress_set_psys_ratio(isp, psys_ratio, psys_ratio); +} + +void +ipu_buttress_add_psys_constraint(struct ipu_device *isp, + struct ipu_buttress_constraint *constraint) +{ + struct ipu_buttress *b = &isp->buttress; + + mutex_lock(&b->cons_mutex); + list_add(&constraint->list, &b->constraints); + + if (constraint->min_freq > b->psys_min_freq) { + isp->buttress.psys_min_freq = min(constraint->min_freq, + b->psys_fused_freqs.max_freq); + ipu_buttress_set_psys_freq(isp, b->psys_min_freq); + } + mutex_unlock(&isp->buttress.cons_mutex); +} +EXPORT_SYMBOL_GPL(ipu_buttress_add_psys_constraint); + +void +ipu_buttress_remove_psys_constraint(struct ipu_device *isp, + struct ipu_buttress_constraint *constraint) +{ + struct ipu_buttress *b = &isp->buttress; + struct ipu_buttress_constraint *c; + unsigned int min_freq = 0; + + mutex_lock(&b->cons_mutex); + list_del(&constraint->list); + + if (constraint->min_freq >= b->psys_min_freq) { + list_for_each_entry(c, &b->constraints, list) + if (c->min_freq > min_freq) + min_freq = c->min_freq; + + b->psys_min_freq = clamp(min_freq, + b->psys_fused_freqs.efficient_freq, + b->psys_fused_freqs.max_freq); + ipu_buttress_set_psys_freq(isp, b->psys_min_freq); + } + mutex_unlock(&b->cons_mutex); +} +EXPORT_SYMBOL_GPL(ipu_buttress_remove_psys_constraint); + +int ipu_buttress_reset_authentication(struct ipu_device *isp) +{ + unsigned long tout_jfs; + u32 val; + + if (!isp->secure_mode) { + dev_dbg(&isp->pdev->dev, + "Non-secure mode -> skip authentication\n"); + return 0; + } + + writel(1 << BUTTRESS_FW_RESET_CTL_START_SHIFT, isp->base + + BUTTRESS_REG_FW_RESET_CTL); + + tout_jfs = jiffies + msecs_to_jiffies(BUTTRESS_CSE_FWRESET_TIMEOUT); + do { + val = readl(isp->base + BUTTRESS_REG_FW_RESET_CTL); + if (val & 1 << BUTTRESS_FW_RESET_CTL_DONE_SHIFT) { + dev_info(&isp->pdev->dev, + "FW reset for authentication done!\n"); + writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); + /* + * Leave some time for HW restore. + */ + usleep_range(100, 1000); + return 0; + } + usleep_range(100, 1000); + } while (!time_after(jiffies, tout_jfs)); + + dev_err(&isp->pdev->dev, + "Timed out while resetting authentication state!\n"); + + return -ETIMEDOUT; +} + +int ipu_buttress_map_fw_image(struct ipu_bus_device *sys, + const struct firmware *fw, struct sg_table *sgt) +{ + struct page **pages; + const void *addr; + unsigned long n_pages, i; + int rval; + + n_pages = PAGE_ALIGN(fw->size) >> PAGE_SHIFT; + + pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + addr = fw->data; + for (i = 0; i < n_pages; i++) { + struct page *p = vmalloc_to_page(addr); + + if (!p) { + rval = -ENODEV; + goto out; + } + pages[i] = p; + addr += PAGE_SIZE; + } + + rval = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size, + GFP_KERNEL); + if (rval) { + rval = -ENOMEM; + goto out; + } + + n_pages = dma_map_sg(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + if (n_pages != sgt->nents) { + rval = -ENOMEM; + sg_free_table(sgt); + goto out; + } + + dma_sync_sg_for_device(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + +out: + kfree(pages); + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_buttress_map_fw_image); + +int ipu_buttress_unmap_fw_image(struct ipu_bus_device *sys, + struct sg_table *sgt) +{ + dma_unmap_sg(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + sg_free_table(sgt); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_buttress_unmap_fw_image); + +int ipu_buttress_authenticate(struct ipu_device *isp) +{ + struct ipu_psys_pdata *psys_pdata; + struct ipu_buttress *b = &isp->buttress; + u32 data; + int rval; + unsigned long tout_jfs; + + if (!isp->secure_mode) { + dev_dbg(&isp->pdev->dev, + "Non-secure mode -> skip authentication\n"); + return 0; + } + + psys_pdata = isp->psys->pdata; + + mutex_lock(&b->auth_mutex); + + rval = pm_runtime_get_sync(&isp->psys_iommu->dev); + if (rval < 0) { + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); + goto iunit_power_off; + } + + if (ipu_buttress_auth_done(isp)) { + rval = 0; + goto iunit_power_off; + } + + /* + * Write address of FIT table to FW_SOURCE register + * Let's use fw address. I.e. not using FIT table yet + */ + data = lower_32_bits(isp->pkg_dir_dma_addr); + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); + + data = upper_32_bits(isp->pkg_dir_dma_addr); + writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); + + /* + * Write boot_load into IU2CSEDATA0 + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as + */ + dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n"); + rval = ipu_buttress_ipc_send(isp, IPU_BUTTRESS_IPC_CSE, + BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, 1); + if (rval) { + dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); + goto iunit_power_off; + } + + tout_jfs = jiffies + msecs_to_jiffies(BUTTRESS_CSE_BOOTLOAD_TIMEOUT); + do { + data = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + data &= BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; + if (data == BUTTRESS_SECURITY_CTL_FW_SETUP_DONE) { + dev_dbg(&isp->pdev->dev, "CSE boot_load done\n"); + break; + } else if (data == BUTTRESS_SECURITY_CTL_AUTH_FAILED) { + dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); + rval = -EINVAL; + goto iunit_power_off; + } + usleep_range(500, 1000); + } while (!time_after(jiffies, tout_jfs)); + + if (data != BUTTRESS_SECURITY_CTL_FW_SETUP_DONE) { + dev_err(&isp->pdev->dev, "CSE boot_load timed out\n"); + rval = -ETIMEDOUT; + goto iunit_power_off; + } + + tout_jfs = jiffies + msecs_to_jiffies(BUTTRESS_CSE_BOOTLOAD_TIMEOUT); + do { + data = readl(psys_pdata->base + BOOTLOADER_STATUS_OFFSET); + dev_dbg(&isp->pdev->dev, "%s: BOOTLOADER_STATUS 0x%x", + __func__, data); + if (data == BOOTLOADER_MAGIC_KEY) { + dev_dbg(&isp->pdev->dev, + "%s: Expected magic number found, breaking...", + __func__); + break; + } + usleep_range(500, 1000); + } while (!time_after(jiffies, tout_jfs)); + + if (data != BOOTLOADER_MAGIC_KEY) { + dev_dbg(&isp->pdev->dev, + "%s: CSE boot_load timed out...\n", __func__); + rval = -ETIMEDOUT; + goto iunit_power_off; + } + + /* + * Write authenticate_run into IU2CSEDATA0 + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as + */ + dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n"); + rval = ipu_buttress_ipc_send(isp, IPU_BUTTRESS_IPC_CSE, + BUTTRESS_IU2CSEDATA0_IPC_AUTHENTICATE_RUN, + 1); + if (rval) { + dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n"); + goto iunit_power_off; + } + + tout_jfs = jiffies; + tout_jfs += msecs_to_jiffies(BUTTRESS_CSE_AUTHENTICATE_TIMEOUT); + do { + data = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + data &= BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; + if (data == BUTTRESS_SECURITY_CTL_AUTH_DONE) { + dev_dbg(&isp->pdev->dev, "CSE authenticate_run done\n"); + break; + } else if (data == BUTTRESS_SECURITY_CTL_AUTH_FAILED) { + dev_err(&isp->pdev->dev, + "CSE authenticate_run failed\n"); + rval = -EINVAL; + goto iunit_power_off; + } + usleep_range(500, 1000); + } while (!time_after(jiffies, tout_jfs)); + + if (data != BUTTRESS_SECURITY_CTL_AUTH_DONE) { + dev_err(&isp->pdev->dev, "CSE authenticate_run timed out\n"); + rval = -ETIMEDOUT; + goto iunit_power_off; + } + +iunit_power_off: + pm_runtime_put(&isp->psys_iommu->dev); + + mutex_unlock(&b->auth_mutex); + + return rval; +} +EXPORT_SYMBOL(ipu_buttress_authenticate); + +static int ipu_buttress_send_tsc_request(struct ipu_device *isp) +{ + unsigned long tout_jfs = msecs_to_jiffies(5); + + writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC, + isp->base + BUTTRESS_REG_FABRIC_CMD); + + tout_jfs += jiffies; + do { + u32 val; + + val = readl(isp->base + BUTTRESS_REG_PWR_STATE); + val = (val & BUTTRESS_PWR_STATE_HH_STATUS_MASK) >> + BUTTRESS_PWR_STATE_HH_STATUS_SHIFT; + + switch (val) { + case BUTTRESS_PWR_STATE_HH_STATE_DONE: + dev_dbg(&isp->pdev->dev, "Start tsc sync completed!\n"); + return 0; + case BUTTRESS_PWR_STATE_HH_STATE_ERR: + dev_err(&isp->pdev->dev, "Start tsc sync failed!\n"); + return -EINVAL; + default: + usleep_range(500, 1000); + break; + } + } while (!time_after(jiffies, tout_jfs)); + + return -ETIMEDOUT; +} + +int ipu_buttress_start_tsc_sync(struct ipu_device *isp) +{ + unsigned int i; + + for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + int ret; + + ret = ipu_buttress_send_tsc_request(isp); + if (ret == -ETIMEDOUT) { + u32 val; + /* set tsw soft reset */ + val = readl(isp->base + BUTTRESS_REG_TSW_CTL); + val = val | BUTTRESS_TSW_CTL_SOFT_RESET; + writel(val, isp->base + BUTTRESS_REG_TSW_CTL); + /* clear tsw soft reset */ + val = val & (~BUTTRESS_TSW_CTL_SOFT_RESET); + writel(val, isp->base + BUTTRESS_REG_TSW_CTL); + + continue; + } + return ret; + } + + dev_err(&isp->pdev->dev, "TSC sync failed(timeout).\n"); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL(ipu_buttress_start_tsc_sync); + +struct clk_ipu_sensor { + struct ipu_device *isp; + struct clk_hw hw; + unsigned int id; + unsigned long rate; +}; + +#define to_clk_ipu_sensor(_hw) container_of(_hw, struct clk_ipu_sensor, hw) + +static int ipu_buttress_clk_pll_prepare(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + int ret; + + /* Workaround needed to get sensor clock running in some cases */ + ret = pm_runtime_get_sync(&ck->isp->isys->dev); + return ret >= 0 ? 0 : ret; +} + +static void ipu_buttress_clk_pll_unprepare(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + + /* Workaround needed to get sensor clock stopped in some cases */ + pm_runtime_put(&ck->isp->isys->dev); +} + +static int ipu_buttress_clk_pll_enable(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + u32 val; + unsigned int i; + + /* + * Start bit behaves like master clock request towards ICLK. + * It is needed regardless of the 24 MHz or per clock out pll + * setting. + */ + val = readl(ck->isp->base + BUTTRESS_REG_SENSOR_FREQ_CTL); + val |= 1 << BUTTRESS_FREQ_CTL_START_SHIFT; + val &= ~BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_MASK(ck->id); + for (i = 0; i < ARRAY_SIZE(sensor_clk_freqs); i++) + if (sensor_clk_freqs[i].rate == ck->rate) + break; + + if (i < ARRAY_SIZE(sensor_clk_freqs)) + val |= sensor_clk_freqs[i].val << + BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_SHIFT(ck->id); + else + val |= BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_DEFAULT(ck->id); + + writel(val, ck->isp->base + BUTTRESS_REG_SENSOR_FREQ_CTL); + + return 0; +} + +static void ipu_buttress_clk_pll_disable(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + u32 val; + int i; + + val = readl(ck->isp->base + BUTTRESS_REG_SENSOR_CLK_CTL); + for (i = 0; i < IPU_BUTTRESS_NUM_OF_SENS_CKS; i++) { + if (val & + (1 << BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_EN_SHIFT(i))) + return; + } + + /* See enable control above */ + val = readl(ck->isp->base + BUTTRESS_REG_SENSOR_FREQ_CTL); + val &= ~(1 << BUTTRESS_FREQ_CTL_START_SHIFT); + writel(val, ck->isp->base + BUTTRESS_REG_SENSOR_FREQ_CTL); +} + +static int ipu_buttress_clk_enable(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + u32 val; + + val = readl(ck->isp->base + BUTTRESS_REG_SENSOR_CLK_CTL); + val |= 1 << BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_EN_SHIFT(ck->id); + + /* Enable dynamic sensor clock */ + val |= 1 << BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_SEL_SHIFT(ck->id); + writel(val, ck->isp->base + BUTTRESS_REG_SENSOR_CLK_CTL); + + return 0; +} + +static void ipu_buttress_clk_disable(struct clk_hw *hw) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + u32 val; + + val = readl(ck->isp->base + BUTTRESS_REG_SENSOR_CLK_CTL); + val &= ~(1 << BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_EN_SHIFT(ck->id)); + writel(val, ck->isp->base + BUTTRESS_REG_SENSOR_CLK_CTL); +} + +static long ipu_buttress_clk_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long best = ULONG_MAX; + unsigned long round_rate = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(sensor_clk_freqs); i++) { + long diff = sensor_clk_freqs[i].rate - rate; + + if (diff == 0) + return rate; + + diff = abs(diff); + if (diff < best) { + best = diff; + round_rate = sensor_clk_freqs[i].rate; + } + } + + return round_rate; +} + +static unsigned long +ipu_buttress_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + + return ck->rate; +} + +static int ipu_buttress_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_ipu_sensor *ck = to_clk_ipu_sensor(hw); + + /* + * R N P PVD PLLout + * 1 45 128 2 6.75 + * 1 40 96 2 8 + * 1 40 80 2 9.6 + * 1 15 20 4 14.4 + * 1 40 32 2 24 + * 1 65 48 1 26 + * + */ + ck->rate = rate; + + return 0; +} + +static const struct clk_ops ipu_buttress_clk_sensor_ops = { + .enable = ipu_buttress_clk_enable, + .disable = ipu_buttress_clk_disable, +}; + +static const struct clk_ops ipu_buttress_clk_sensor_ops_parent = { + .enable = ipu_buttress_clk_pll_enable, + .disable = ipu_buttress_clk_pll_disable, + .prepare = ipu_buttress_clk_pll_prepare, + .unprepare = ipu_buttress_clk_pll_unprepare, + .round_rate = ipu_buttress_clk_round_rate, + .recalc_rate = ipu_buttress_clk_recalc_rate, + .set_rate = ipu_buttress_clk_set_rate, +}; + +static struct clk_init_data ipu_buttress_sensor_clk_data[] = { + { + .name = "OSC_CLK_OUT0", + .ops = &ipu_buttress_clk_sensor_ops, + .parent_names = (const char *[]){"ipu_sensor_pll0"}, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, + { + .name = "OSC_CLK_OUT1", + .ops = &ipu_buttress_clk_sensor_ops, + .parent_names = (const char *[]){"ipu_sensor_pll1"}, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, + { + .name = "OSC_CLK_OUT2", + .ops = &ipu_buttress_clk_sensor_ops, + .parent_names = (const char *[]){"ipu_sensor_pll2"}, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_init_data ipu_buttress_sensor_pll_data[] = { + { + .name = "ipu_sensor_pll0", + .ops = &ipu_buttress_clk_sensor_ops_parent, + }, + { + .name = "ipu_sensor_pll1", + .ops = &ipu_buttress_clk_sensor_ops_parent, + }, + { + .name = "ipu_sensor_pll2", + .ops = &ipu_buttress_clk_sensor_ops_parent, + }, +}; + +static void ipu_buttress_read_psys_fused_freqs(struct ipu_device *isp) +{ + struct ipu_buttress_fused_freqs *fused_freq = + &isp->buttress.psys_fused_freqs; + u32 reg_val, max_ratio, min_ratio, efficient_ratio; + + reg_val = readl(isp->base + BUTTRESS_REG_PS_FREQ_CAPABILITIES); + + min_ratio = (reg_val & + BUTTRESS_PS_FREQ_CAPABILITIES_MIN_RATIO_MASK) >> + BUTTRESS_PS_FREQ_CAPABILITIES_MIN_RATIO_SHIFT; + max_ratio = (reg_val & + BUTTRESS_PS_FREQ_CAPABILITIES_MAX_RATIO_MASK) >> + BUTTRESS_PS_FREQ_CAPABILITIES_MAX_RATIO_SHIFT; + efficient_ratio = + (reg_val & + BUTTRESS_PS_FREQ_CAPABILITIES_EFFICIENT_RATIO_MASK) >> + BUTTRESS_PS_FREQ_CAPABILITIES_EFFICIENT_RATIO_SHIFT; + + fused_freq->min_freq = min_ratio * BUTTRESS_PS_FREQ_STEP; + fused_freq->max_freq = max_ratio * BUTTRESS_PS_FREQ_STEP; + fused_freq->efficient_freq = efficient_ratio * BUTTRESS_PS_FREQ_STEP; +} + +#ifdef I2C_WA +/* + * The dev_id was hard code in platform data, as i2c bus number + * may change dynamiclly, we need to update this bus id + * accordingly. + * + * @adapter_id: hardware i2c adapter id, this was fixed in platform data + * return: i2c bus id registered in system + */ +int ipu_get_i2c_bus_id(int adapter_id) +{ + struct i2c_adapter *adapter; + char name[32]; + int i = 0; + + snprintf(name, sizeof(name), "i2c_designware.%d", adapter_id); + while ((adapter = i2c_get_adapter(i)) != NULL) { + struct device *parent = adapter->dev.parent; + + if (parent && !strncmp(name, dev_name(parent), sizeof(name))) + return i; + i++; + } + + /* Not found, should never happen! */ + WARN_ON_ONCE(1); + return -1; +} +EXPORT_SYMBOL_GPL(ipu_get_i2c_bus_id); +#endif + +static int ipu_buttress_clk_init(struct ipu_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + struct ipu_isys_subdev_pdata *pdata = isp->pdev->dev.platform_data; + struct ipu_isys_clk_mapping *clkmap = pdata ? pdata->clk_map : NULL; + struct clk_init_data *clk_data_parent; + struct clk_init_data *clk_data; + int i, rval; + unsigned int num_plls; + + ipu_buttress_read_psys_fused_freqs(isp); + isp->buttress.psys_min_freq = b->psys_fused_freqs.efficient_freq; + + clk_data_parent = ipu_buttress_sensor_pll_data; + + num_plls = ARRAY_SIZE(ipu_buttress_sensor_pll_data); + + for (i = 0; i < num_plls; i++) { + struct clk_ipu_sensor *parent_clk = + devm_kzalloc(&isp->pdev->dev, + sizeof(*parent_clk), GFP_KERNEL); + + if (!parent_clk) { + rval = -ENOMEM; + goto err; + } + + parent_clk->hw.init = &clk_data_parent[i]; + parent_clk->isp = isp; + parent_clk->id = i; + + b->pll_sensor[i] = clk_register(NULL, &parent_clk->hw); + if (IS_ERR(b->pll_sensor[i])) { + rval = PTR_ERR(b->pll_sensor[i]); + goto err; + } + } + + clk_data = ipu_buttress_sensor_clk_data; + + for (i = 0; i < IPU_BUTTRESS_NUM_OF_SENS_CKS; i++) { + char buffer[16]; /* max for clk_register_clkdev */ + unsigned int parent_index = 0; + struct clk_ipu_sensor *my_clk = + devm_kzalloc(&isp->pdev->dev, sizeof(*my_clk), + GFP_KERNEL); + + if (!my_clk) { + rval = -ENOMEM; + goto err; + } + + if (i < num_plls) + parent_index = i; + + my_clk->hw.init = &clk_data[i]; + + my_clk->id = i; + my_clk->isp = isp; + + b->clk_sensor[i] = clk_register(NULL, &my_clk->hw); + if (IS_ERR(b->clk_sensor[i])) { + rval = PTR_ERR(b->clk_sensor[i]); + goto err; + } + rval = clk_set_parent(b->clk_sensor[i], + b->pll_sensor[parent_index]); + if (rval) + goto err; + + /* Register generic clocks for sensor driver */ + snprintf(buffer, sizeof(buffer), "ipu_cam_clk%d", i); + rval = clk_register_clkdev(b->clk_sensor[i], buffer, NULL); + if (rval) + goto err; + } + + /* Now map sensor clocks */ + if (!clkmap) + return 0; + + while (clkmap->clkdev_data.dev_id) { +#ifdef I2C_WA + char *dev_id = kstrdup(clkmap->clkdev_data.dev_id, GFP_KERNEL); + int adapter_id = clkmap->clkdev_data.dev_id[0] - '0'; + char *addr = strpbrk(clkmap->clkdev_data.dev_id, "-"); + int bus_id = ipu_get_i2c_bus_id(adapter_id); + + snprintf(dev_id, PAGE_SIZE, "%d-%s", bus_id, addr + 1); +#endif + + /* + * Lookup table must be NULL terminated + * CLKDEV_INIT(NULL, NULL, NULL) + */ + for (i = 0; i < IPU_BUTTRESS_NUM_OF_SENS_CKS; i++) { + if (!strcmp(clkmap->platform_clock_name, + clk_data[i].name)) { + clkmap->clkdev_data.clk = b->clk_sensor[i]; +#ifdef I2C_WA + clkmap->clkdev_data.dev_id = dev_id; +#endif + clkdev_add(&clkmap->clkdev_data); + break; + } + } + clkmap++; + } + + return 0; + +err: + /* It is safe to call clk_unregister with null pointer */ + for (i = IPU_BUTTRESS_NUM_OF_SENS_CKS - 1; i >= 0; i--) + clk_unregister(b->clk_sensor[i]); + + for (i = num_plls - 1; i >= 0; i--) + clk_unregister(b->pll_sensor[i]); + + return rval; +} + +static void ipu_buttress_clk_exit(struct ipu_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + int i; + + /* It is safe to call clk_unregister with null pointer */ + for (i = 0; i < IPU_BUTTRESS_NUM_OF_SENS_CKS; i++) + clk_unregister(b->clk_sensor[i]); + + for (i = 0; i < ARRAY_SIZE(ipu_buttress_sensor_pll_data); i++) + clk_unregister(b->pll_sensor[i]); +} + +int ipu_buttress_tsc_read(struct ipu_device *isp, u64 *val) +{ + struct ipu_buttress *b = &isp->buttress; + u32 tsc_hi, tsc_lo_1, tsc_lo_2, tsc_lo_3, tsc_chk = 0; + unsigned long flags; + short retry = IPU_BUTTRESS_TSC_RETRY; + + do { + spin_lock_irqsave(&b->tsc_lock, flags); + tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); + + /* + * We are occasionally getting broken values from + * HH. Reading 3 times and doing sanity check as a WA + */ + tsc_lo_1 = readl(isp->base + BUTTRESS_REG_TSC_LO); + tsc_lo_2 = readl(isp->base + BUTTRESS_REG_TSC_LO); + tsc_lo_3 = readl(isp->base + BUTTRESS_REG_TSC_LO); + tsc_chk = readl(isp->base + BUTTRESS_REG_TSC_HI); + spin_unlock_irqrestore(&b->tsc_lock, flags); + if (tsc_chk == tsc_hi && tsc_lo_2 && + tsc_lo_2 - tsc_lo_1 <= IPU_BUTTRESS_TSC_LIMIT && + tsc_lo_3 - tsc_lo_2 <= IPU_BUTTRESS_TSC_LIMIT) { + *val = (u64)tsc_hi << 32 | tsc_lo_2; + return 0; + } + + /* + * Trace error only if limit checkings fails at least + * by two consecutive readings. + */ + if (retry < IPU_BUTTRESS_TSC_RETRY - 1 && tsc_lo_2) + dev_err(&isp->pdev->dev, + "%s = %u, %s = %u, %s = %u, %s = %u, %s = %u", + "failure: tsc_hi", tsc_hi, + "tsc_chk", tsc_chk, + "tsc_lo_1", tsc_lo_1, + "tsc_lo_2", tsc_lo_2, "tsc_lo_3", tsc_lo_3); + } while (retry--); + + if (!tsc_chk && !tsc_lo_2) + return -EIO; + + WARN_ON_ONCE(1); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ipu_buttress_tsc_read); + +#ifdef CONFIG_DEBUG_FS + +static int ipu_buttress_reg_open(struct inode *inode, struct file *file) +{ + if (!inode->i_private) + return -EACCES; + + file->private_data = inode->i_private; + return 0; +} + +static ssize_t ipu_buttress_reg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct debugfs_reg32 *reg = file->private_data; + u8 tmp[11]; + u32 val = readl((void __iomem *)reg->offset); + int len = scnprintf(tmp, sizeof(tmp), "0x%08x", val); + + return simple_read_from_buffer(buf, len, ppos, &tmp, len); +} + +static ssize_t ipu_buttress_reg_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct debugfs_reg32 *reg = file->private_data; + u32 val; + int rval; + + rval = kstrtou32_from_user(buf, count, 0, &val); + if (rval) + return rval; + + writel(val, (void __iomem *)reg->offset); + + return count; +} + +static struct debugfs_reg32 buttress_regs[] = { + {"IU2CSEDB0", BUTTRESS_REG_IU2CSEDB0}, + {"IU2CSEDATA0", BUTTRESS_REG_IU2CSEDATA0}, + {"CSE2IUDB0", BUTTRESS_REG_CSE2IUDB0}, + {"CSE2IUDATA0", BUTTRESS_REG_CSE2IUDATA0}, + {"CSE2IUCSR", BUTTRESS_REG_CSE2IUCSR}, + {"IU2CSECSR", BUTTRESS_REG_IU2CSECSR}, +}; + +static const struct file_operations ipu_buttress_reg_fops = { + .owner = THIS_MODULE, + .open = ipu_buttress_reg_open, + .read = ipu_buttress_reg_read, + .write = ipu_buttress_reg_write, +}; + +static int ipu_buttress_start_tsc_sync_set(void *data, u64 val) +{ + struct ipu_device *isp = data; + + return ipu_buttress_start_tsc_sync(isp); +} + +DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_start_tsc_sync_fops, NULL, + ipu_buttress_start_tsc_sync_set, "%llu\n"); + +u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks) +{ + u64 ns = ticks * 10000; + /* + * TSC clock frequency is 19.2MHz, + * converting TSC tick count to ns is calculated by: + * ns = ticks * 1000 000 000 / 19.2Mhz + * = ticks * 1000 000 000 / 19200000Hz + * = ticks * 10000 / 192 ns + */ + do_div(ns, 192); + + return ns; +} +EXPORT_SYMBOL_GPL(ipu_buttress_tsc_ticks_to_ns); + +static int ipu_buttress_tsc_get(void *data, u64 *val) +{ + return ipu_buttress_tsc_read(data, val); +} +DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_tsc_fops, ipu_buttress_tsc_get, + NULL, "%llu\n"); + +static int ipu_buttress_psys_force_freq_get(void *data, u64 *val) +{ + struct ipu_device *isp = data; + + *val = isp->buttress.psys_force_ratio * BUTTRESS_PS_FREQ_STEP; + + return 0; +} + +static int ipu_buttress_psys_force_freq_set(void *data, u64 val) +{ + struct ipu_device *isp = data; + + if (val && (val < BUTTRESS_MIN_FORCE_PS_FREQ || + val > BUTTRESS_MAX_FORCE_PS_FREQ)) + return -EINVAL; + + do_div(val, BUTTRESS_PS_FREQ_STEP); + isp->buttress.psys_force_ratio = val; + + if (isp->buttress.psys_force_ratio) + ipu_buttress_set_psys_ratio(isp, + isp->buttress.psys_force_ratio, + isp->buttress.psys_force_ratio); + else + ipu_buttress_set_psys_freq(isp, isp->buttress.psys_min_freq); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_force_freq_fops, + ipu_buttress_psys_force_freq_get, + ipu_buttress_psys_force_freq_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_freq_fops, + ipu_buttress_psys_freq_get, NULL, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_isys_freq_fops, + ipu_buttress_isys_freq_get, NULL, "%llu\n"); + +int ipu_buttress_debugfs_init(struct ipu_device *isp) +{ + struct debugfs_reg32 *reg = + devm_kcalloc(&isp->pdev->dev, ARRAY_SIZE(buttress_regs), + sizeof(*reg), GFP_KERNEL); + struct dentry *dir, *file; + int i; + + if (!reg) + return -ENOMEM; + + dir = debugfs_create_dir("buttress", isp->ipu_dir); + if (!dir) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(buttress_regs); i++, reg++) { + reg->offset = (unsigned long)isp->base + + buttress_regs[i].offset; + reg->name = buttress_regs[i].name; + file = debugfs_create_file(reg->name, 0700, + dir, reg, &ipu_buttress_reg_fops); + if (!file) + goto err; + } + + file = debugfs_create_file("start_tsc_sync", 0200, dir, isp, + &ipu_buttress_start_tsc_sync_fops); + if (!file) + goto err; + file = debugfs_create_file("tsc", 0400, dir, isp, + &ipu_buttress_tsc_fops); + if (!file) + goto err; + file = debugfs_create_file("psys_force_freq", 0700, dir, isp, + &ipu_buttress_psys_force_freq_fops); + if (!file) + goto err; + + file = debugfs_create_file("psys_freq", 0400, dir, isp, + &ipu_buttress_psys_freq_fops); + if (!file) + goto err; + + file = debugfs_create_file("isys_freq", 0400, dir, isp, + &ipu_buttress_isys_freq_fops); + if (!file) + goto err; + + return 0; +err: + debugfs_remove_recursive(dir); + return -ENOMEM; +} + +#endif /* CONFIG_DEBUG_FS */ + +static ssize_t +ipu_buttress_psys_fused_min_freq_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%u\n", + isp->buttress.psys_fused_freqs.min_freq); +} + +static DEVICE_ATTR(psys_fused_min_freq, 0444, + ipu_buttress_psys_fused_min_freq_get, NULL); + +static ssize_t +ipu_buttress_psys_fused_max_freq_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%u\n", + isp->buttress.psys_fused_freqs.max_freq); +} + +static DEVICE_ATTR(psys_fused_max_freq, 0444, + ipu_buttress_psys_fused_max_freq_get, NULL); + +static ssize_t +ipu_buttress_psys_fused_efficient_freq_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%u\n", + isp->buttress.psys_fused_freqs.efficient_freq); +} + +static DEVICE_ATTR(psys_fused_efficient_freq, 0444, + ipu_buttress_psys_fused_efficient_freq_get, NULL); + +int ipu_buttress_restore(struct ipu_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); + writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT); + + return 0; +} +EXPORT_SYMBOL(ipu_buttress_restore); + +int ipu_buttress_init(struct ipu_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + int rval, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; + + mutex_init(&b->power_mutex); + mutex_init(&b->auth_mutex); + mutex_init(&b->cons_mutex); + mutex_init(&b->ipc_mutex); + spin_lock_init(&b->tsc_lock); + init_completion(&b->ish.send_complete); + init_completion(&b->cse.send_complete); + init_completion(&b->ish.recv_complete); + init_completion(&b->cse.recv_complete); + + b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; + b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; + b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; + b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; + b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; + b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; + b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; + b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; + + b->ish.csr_in = BUTTRESS_REG_ISH2IUCSR; + b->ish.csr_out = BUTTRESS_REG_IU2ISHCSR; + b->ish.db0_in = BUTTRESS_REG_ISH2IUDB0; + b->ish.db0_out = BUTTRESS_REG_IU2ISHDB0; + b->ish.data0_in = BUTTRESS_REG_ISH2IUDATA0; + b->ish.data0_out = BUTTRESS_REG_IU2ISHDATA0; + INIT_LIST_HEAD(&b->constraints); + + rval = ipu_buttress_clk_init(isp); + if (rval) { + dev_err(&isp->pdev->dev, "Clock init failed\n"); + goto err_mutex_destroy; + } + + ipu_buttress_set_secure_mode(isp); + isp->secure_mode = ipu_buttress_get_secure_mode(isp); + if (isp->secure_mode != secure_mode_enable) + dev_warn(&isp->pdev->dev, "Unable to set secure mode!\n"); + + dev_info(&isp->pdev->dev, "IPU in %s mode\n", + isp->secure_mode ? "secure" : "non-secure"); + + b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); + + rval = device_create_file(&isp->pdev->dev, + &dev_attr_psys_fused_min_freq); + if (rval) { + dev_err(&isp->pdev->dev, "Create min freq file failed\n"); + goto err_clk_unregister; + } + + rval = device_create_file(&isp->pdev->dev, + &dev_attr_psys_fused_max_freq); + if (rval) { + dev_err(&isp->pdev->dev, "Create max freq file failed\n"); + goto err_remove_min_freq_file; + } + + rval = device_create_file(&isp->pdev->dev, + &dev_attr_psys_fused_efficient_freq); + if (rval) { + dev_err(&isp->pdev->dev, "Create efficient freq file failed\n"); + goto err_remove_max_freq_file; + } + + /* + * We want to retry couple of time in case CSE initialization + * is delayed for reason or another. + */ + do { + rval = ipu_buttress_ipc_reset(isp, &b->cse); + if (rval) { + dev_err(&isp->pdev->dev, + "IPC reset protocol failed, retry!\n"); + } else { + dev_dbg(&isp->pdev->dev, "IPC reset completed!\n"); + return 0; + } + } while (ipc_reset_retry--); + + dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); + +err_remove_max_freq_file: + device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_max_freq); +err_remove_min_freq_file: + device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_min_freq); +err_clk_unregister: + ipu_buttress_clk_exit(isp); +err_mutex_destroy: + mutex_destroy(&b->power_mutex); + mutex_destroy(&b->auth_mutex); + mutex_destroy(&b->cons_mutex); + mutex_destroy(&b->ipc_mutex); + + return rval; +} + +void ipu_buttress_exit(struct ipu_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + + writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE); + + device_remove_file(&isp->pdev->dev, + &dev_attr_psys_fused_efficient_freq); + device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_max_freq); + device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_min_freq); + + ipu_buttress_clk_exit(isp); + + mutex_destroy(&b->power_mutex); + mutex_destroy(&b->auth_mutex); + mutex_destroy(&b->cons_mutex); + mutex_destroy(&b->ipc_mutex); +} diff --git a/drivers/media/pci/intel/ipu-buttress.h b/drivers/media/pci/intel/ipu-buttress.h new file mode 100644 index 000000000000..2c5e93af6d54 --- /dev/null +++ b/drivers/media/pci/intel/ipu-buttress.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_BUTTRESS_H +#define IPU_BUTTRESS_H + +#include +#include +#include "ipu.h" + +#define IPU_BUTTRESS_NUM_OF_SENS_CKS 3 +#define IPU_BUTTRESS_NUM_OF_PLL_CKS 3 +#define IPU_BUTTRESS_TSC_CLK 19200000 + +#define BUTTRESS_POWER_TIMEOUT 200 + +#define BUTTRESS_PS_FREQ_STEP 25U +#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8) +#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32) + +struct ipu_buttress_ctrl { + u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; + union { + unsigned int divisor; + unsigned int ratio; + }; + union { + unsigned int divisor_shift; + unsigned int ratio_shift; + }; + unsigned int ovrd; + u32 ovrd_shift; + unsigned int qos_floor; + bool started; +}; + +struct ipu_buttress_fused_freqs { + unsigned int min_freq; + unsigned int max_freq; + unsigned int efficient_freq; +}; + +struct ipu_buttress_ipc { + struct completion send_complete; + struct completion recv_complete; + u32 nack; + u32 nack_mask; + u32 recv_data; + u32 csr_out; + u32 csr_in; + u32 db0_in; + u32 db0_out; + u32 data0_out; + u32 data0_in; +}; + +struct ipu_buttress { + struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; + spinlock_t tsc_lock; /* tsc lock */ + struct clk *clk_sensor[IPU_BUTTRESS_NUM_OF_SENS_CKS]; + struct clk *pll_sensor[IPU_BUTTRESS_NUM_OF_PLL_CKS]; + struct ipu_buttress_ipc cse; + struct ipu_buttress_ipc ish; + struct list_head constraints; + struct ipu_buttress_fused_freqs psys_fused_freqs; + unsigned int psys_min_freq; + u32 wdt_cached_value; + u8 psys_force_ratio; + bool force_suspend; + bool ps_started; +}; + +struct ipu_buttress_sensor_clk_freq { + unsigned int rate; + unsigned int val; +}; + +struct firmware; + +enum ipu_buttress_ipc_domain { + IPU_BUTTRESS_IPC_CSE, + IPU_BUTTRESS_IPC_ISH, +}; + +struct ipu_buttress_constraint { + struct list_head list; + unsigned int min_freq; +}; + +struct ipu_ipc_buttress_bulk_msg { + u32 cmd; + u32 expected_resp; + bool require_resp; + u8 cmd_size; +}; + +int ipu_buttress_ipc_reset(struct ipu_device *isp, + struct ipu_buttress_ipc *ipc); +int ipu_buttress_map_fw_image(struct ipu_bus_device *sys, + const struct firmware *fw, struct sg_table *sgt); +int ipu_buttress_unmap_fw_image(struct ipu_bus_device *sys, + struct sg_table *sgt); +int ipu_buttress_power(struct device *dev, + struct ipu_buttress_ctrl *ctrl, bool on); +void +ipu_buttress_add_psys_constraint(struct ipu_device *isp, + struct ipu_buttress_constraint *constraint); +void +ipu_buttress_remove_psys_constraint(struct ipu_device *isp, + struct ipu_buttress_constraint *constraint); +void ipu_buttress_set_secure_mode(struct ipu_device *isp); +bool ipu_buttress_get_secure_mode(struct ipu_device *isp); +int ipu_buttress_authenticate(struct ipu_device *isp); +int ipu_buttress_reset_authentication(struct ipu_device *isp); +bool ipu_buttress_auth_done(struct ipu_device *isp); +int ipu_buttress_start_tsc_sync(struct ipu_device *isp); +int ipu_buttress_tsc_read(struct ipu_device *isp, u64 *val); +u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks); + +irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); +irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); +int ipu_buttress_debugfs_init(struct ipu_device *isp); +int ipu_buttress_init(struct ipu_device *isp); +void ipu_buttress_exit(struct ipu_device *isp); +void ipu_buttress_csi_port_config(struct ipu_device *isp, + u32 legacy, u32 combo); +int ipu_buttress_restore(struct ipu_device *isp); + +int +ipu_buttress_ipc_send_bulk(struct ipu_device *isp, + enum ipu_buttress_ipc_domain ipc_domain, + struct ipu_ipc_buttress_bulk_msg *msgs, u32 size); +int ipu_buttress_psys_freq_get(void *data, u64 *val); +int ipu_buttress_isys_freq_get(void *data, u64 *val); +#ifdef I2C_WA +int ipu_get_i2c_bus_id(int adapter_id); +#endif /* I2C_WA */ +#endif /* IPU_BUTTRESS_H */ diff --git a/drivers/media/pci/intel/ipu-cpd.c b/drivers/media/pci/intel/ipu-cpd.c new file mode 100644 index 000000000000..dca03232aa4f --- /dev/null +++ b/drivers/media/pci/intel/ipu-cpd.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015 - 2018 Intel Corporation + +#include +#include + +#include "ipu.h" +#include "ipu-cpd.h" + +#include + +/* 15 entries + header*/ +#define MAX_PKG_DIR_ENT_CNT 16 +/* 2 qword per entry/header */ +#define PKG_DIR_ENT_LEN 2 +/* PKG_DIR size in bytes */ +#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \ + (PKG_DIR_ENT_LEN) * sizeof(u64)) +#define PKG_DIR_ID_SHIFT 48 +#define PKG_DIR_ID_MASK 0x7f +#define PKG_DIR_VERSION_SHIFT 32 +#define PKG_DIR_SIZE_MASK 0xfffff +/* _IUPKDR_ */ +#define PKG_DIR_HDR_MARK 0x5f4955504b44525f + +/* $CPD */ +#define CPD_HDR_MARK 0x44504324 + +/* Maximum size is 2K DWORDs */ +#define MAX_MANIFEST_SIZE (2 * 1024 * sizeof(u32)) + +/* Maximum size is 64k */ +#define MAX_METADATA_SIZE (64 * 1024) + +#define MAX_COMPONENT_ID 127 +#define MAX_COMPONENT_VERSION 0xffff + +#define CPD_MANIFEST_IDX 0 +#define CPD_METADATA_IDX 1 +#define CPD_MODULEDATA_IDX 2 + +#define ipu_cpd_get_entries(cpd) ((struct ipu_cpd_ent *) \ + ((struct ipu_cpd_hdr *)cpd + 1)) +#define ipu_cpd_get_entry(cpd, idx) (&ipu_cpd_get_entries(cpd)[idx]) +#define ipu_cpd_get_manifest(cpd) ipu_cpd_get_entry(cpd, CPD_MANIFEST_IDX) +#define ipu_cpd_get_metadata(cpd) ipu_cpd_get_entry(cpd, CPD_METADATA_IDX) +#define ipu_cpd_get_moduledata(cpd) ipu_cpd_get_entry(cpd, CPD_MODULEDATA_IDX) + +static bool fw_version_check = true; +module_param(fw_version_check, bool, 0444); +MODULE_PARM_DESC(fw_version_check, "enable/disable checking firmware version"); + +static const struct ipu_cpd_metadata_cmpnt * +ipu_cpd_metadata_get_cmpnt(struct ipu_device *isp, + const void *metadata, + unsigned int metadata_size, + u8 idx) +{ + const struct ipu_cpd_metadata_extn *extn; + const struct ipu_cpd_metadata_cmpnt *cmpnts; + int cmpnt_count; + + extn = metadata; + cmpnts = metadata + sizeof(*extn); + cmpnt_count = (metadata_size - sizeof(*extn)) / sizeof(*cmpnts); + + if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) { + dev_err(&isp->pdev->dev, "Component index out of range (%d)\n", + idx); + return ERR_PTR(-EINVAL); + } + + return &cmpnts[idx]; +} + +static u32 ipu_cpd_metadata_cmpnt_version(struct ipu_device *isp, + const void *metadata, + unsigned int metadata_size, u8 idx) +{ + const struct ipu_cpd_metadata_cmpnt *cmpnt = + ipu_cpd_metadata_get_cmpnt(isp, metadata, + metadata_size, idx); + + if (IS_ERR(cmpnt)) + return PTR_ERR(cmpnt); + + return cmpnt->ver; +} + +static int ipu_cpd_metadata_get_cmpnt_id(struct ipu_device *isp, + const void *metadata, + unsigned int metadata_size, u8 idx) +{ + const struct ipu_cpd_metadata_cmpnt *cmpnt = + ipu_cpd_metadata_get_cmpnt(isp, metadata, + metadata_size, idx); + + if (IS_ERR(cmpnt)) + return PTR_ERR(cmpnt); + + return cmpnt->id; +} + +static u32 +ipu_cpd_metadata_get_cmpnt_icache_base_offs(struct ipu_device *isp, + const void *metadata, + unsigned int metadata_size, u8 idx) +{ + const struct ipu_cpd_metadata_cmpnt *cmpnt = + ipu_cpd_metadata_get_cmpnt(isp, metadata, + metadata_size, idx); + + if (IS_ERR(cmpnt)) + return PTR_ERR(cmpnt); + + return cmpnt->icache_base_offs; +} + +static u32 +ipu_cpd_metadata_get_cmpnt_entry_point(struct ipu_device *isp, + const void *metadata, + unsigned int metadata_size, u8 idx) +{ + const struct ipu_cpd_metadata_cmpnt *cmpnt = + ipu_cpd_metadata_get_cmpnt(isp, metadata, + metadata_size, idx); + + if (IS_ERR(cmpnt)) + return PTR_ERR(cmpnt); + + return cmpnt->entry_point; +} + +static int ipu_cpd_parse_module_data(struct ipu_device *isp, + const void *module_data, + unsigned int module_data_size, + dma_addr_t dma_addr_module_data, + u64 *pkg_dir, + const void *metadata, + unsigned int metadata_size) +{ + const struct ipu_cpd_module_data_hdr *module_data_hdr; + const struct ipu_cpd_hdr *dir_hdr; + const struct ipu_cpd_ent *dir_ent; + int i; + + if (!module_data) + return -EINVAL; + + module_data_hdr = module_data; + dir_hdr = module_data + module_data_hdr->hdr_len; + dir_ent = (struct ipu_cpd_ent *)(dir_hdr + 1); + + pkg_dir[0] = PKG_DIR_HDR_MARK; + /* pkg_dir entry count = component count + pkg_dir header */ + pkg_dir[1] = dir_hdr->ent_cnt + 1; + + for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) { + u64 *p = &pkg_dir[PKG_DIR_ENT_LEN + i * PKG_DIR_ENT_LEN]; + int ver, id; + + *p++ = dma_addr_module_data + dir_ent->offset; + + id = ipu_cpd_metadata_get_cmpnt_id(isp, metadata, + metadata_size, i); + if (id < 0 || id > MAX_COMPONENT_ID) { + dev_err(&isp->pdev->dev, + "Failed to parse component id\n"); + return -EINVAL; + } + ver = ipu_cpd_metadata_cmpnt_version(isp, metadata, + metadata_size, i); + if (ver < 0 || ver > MAX_COMPONENT_VERSION) { + dev_err(&isp->pdev->dev, + "Failed to parse component version\n"); + return -EINVAL; + } + + /* + * PKG_DIR Entry (type == id) + * 63:56 55 54:48 47:32 31:24 23:0 + * Rsvd Rsvd Type Version Rsvd Size + */ + *p = dir_ent->len | (u64) id << PKG_DIR_ID_SHIFT | + (u64)ver << PKG_DIR_VERSION_SHIFT; + } + + return 0; +} + +void *ipu_cpd_create_pkg_dir(struct ipu_bus_device *adev, + const void *src, + dma_addr_t dma_addr_src, + dma_addr_t *dma_addr, unsigned int *pkg_dir_size) +{ + struct ipu_device *isp = adev->isp; + const struct ipu_cpd_ent *ent, *man_ent, *met_ent; + u64 *pkg_dir; + unsigned int man_sz, met_sz; + void *pkg_dir_pos; + int ret; + + man_ent = ipu_cpd_get_manifest(src); + man_sz = man_ent->len; + + met_ent = ipu_cpd_get_metadata(src); + met_sz = met_ent->len; + + *pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz; + pkg_dir = dma_alloc_attrs(&adev->dev, *pkg_dir_size, dma_addr, + GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + if (!pkg_dir) + return pkg_dir; + + /* + * pkg_dir entry/header: + * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0 + * N Address/Offset/"_IUPKDR_" + * N + 1 | rsvd | rsvd | type | ver | rsvd | size + * + * We can ignore other fields that size in N + 1 qword as they + * are 0 anyway. Just setting size for now. + */ + + ent = ipu_cpd_get_moduledata(src); + + ret = ipu_cpd_parse_module_data(isp, src + ent->offset, + ent->len, + dma_addr_src + ent->offset, + pkg_dir, + src + met_ent->offset, met_ent->len); + if (ret) { + dev_err(&isp->pdev->dev, + "Unable to parse module data section!\n"); + dma_free_attrs(&isp->psys->dev, *pkg_dir_size, pkg_dir, + *dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + return NULL; + } + + /* Copy manifest after pkg_dir */ + pkg_dir_pos = pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT; + memcpy(pkg_dir_pos, src + man_ent->offset, man_sz); + + /* Copy metadata after manifest */ + pkg_dir_pos += man_sz; + memcpy(pkg_dir_pos, src + met_ent->offset, met_sz); + + dma_sync_single_range_for_device(&adev->dev, *dma_addr, + 0, *pkg_dir_size, DMA_TO_DEVICE); + + return pkg_dir; +} +EXPORT_SYMBOL_GPL(ipu_cpd_create_pkg_dir); + +void ipu_cpd_free_pkg_dir(struct ipu_bus_device *adev, + u64 *pkg_dir, + dma_addr_t dma_addr, unsigned int pkg_dir_size) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_free_attrs(&adev->dev, pkg_dir_size, pkg_dir, dma_addr, NULL); +#else + dma_free_attrs(&adev->dev, pkg_dir_size, pkg_dir, dma_addr, 0); +#endif +} +EXPORT_SYMBOL_GPL(ipu_cpd_free_pkg_dir); + +u32 ipu_cpd_get_pg_icache_base(struct ipu_device *isp, + u8 idx, + const void *cpd_file, unsigned int cpd_file_size) +{ + const struct ipu_cpd_ent *metadata = ipu_cpd_get_metadata(cpd_file); + const void *metadata_addr = cpd_file + metadata->offset; + + return ipu_cpd_metadata_get_cmpnt_icache_base_offs(isp, + metadata_addr, + metadata->len, idx); +} +EXPORT_SYMBOL_GPL(ipu_cpd_get_pg_icache_base); + +u32 ipu_cpd_get_pg_entry_point(struct ipu_device *isp, + u8 idx, + const void *cpd_file, unsigned int cpd_file_size) +{ + const struct ipu_cpd_ent *metadata = ipu_cpd_get_metadata(cpd_file); + const void *metadata_addr = cpd_file + metadata->offset; + + return ipu_cpd_metadata_get_cmpnt_entry_point(isp, + metadata_addr, + metadata->len, idx); +} +EXPORT_SYMBOL_GPL(ipu_cpd_get_pg_entry_point); + +static int ipu_cpd_validate_cpd(struct ipu_device *isp, + const void *cpd, + unsigned long cpd_size, unsigned long data_size) +{ + const struct ipu_cpd_hdr *cpd_hdr = cpd; + struct ipu_cpd_ent *ent; + unsigned int i; + + /* Ensure cpd hdr is within moduledata */ + if (cpd_size < sizeof(*cpd_hdr)) { + dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); + return -EINVAL; + } + + /* Sanity check for CPD header */ + if ((cpd_size - sizeof(*cpd_hdr)) / sizeof(*ent) < cpd_hdr->ent_cnt) { + dev_err(&isp->pdev->dev, "Invalid CPD header\n"); + return -EINVAL; + } + + /* Ensure that all entries are within moduledata */ + ent = (struct ipu_cpd_ent *)(cpd_hdr + 1); + for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) { + if (data_size < ent->offset || + data_size - ent->offset < ent->len) { + dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int ipu_cpd_validate_moduledata(struct ipu_device *isp, + const void *moduledata, + u32 moduledata_size) +{ + const struct ipu_cpd_module_data_hdr *mod_hdr = moduledata; + int rval; + + /* Ensure moduledata hdr is within moduledata */ + if (moduledata_size < sizeof(*mod_hdr) || + moduledata_size < mod_hdr->hdr_len) { + dev_err(&isp->pdev->dev, "Invalid moduledata size\n"); + return -EINVAL; + } + + if (fw_version_check && mod_hdr->fw_pkg_date != IA_CSS_FW_PKG_RELEASE) { + dev_err(&isp->pdev->dev, + "Moduledata and library version mismatch (%x != %x)\n", + mod_hdr->fw_pkg_date, IA_CSS_FW_PKG_RELEASE); + return -EINVAL; + } + + dev_warn(&isp->pdev->dev, + "Moduledata version: %x, library version: %x\n", + mod_hdr->fw_pkg_date, IA_CSS_FW_PKG_RELEASE); + + dev_info(&isp->pdev->dev, "CSS release: %x\n", IA_CSS_FW_PKG_RELEASE); + rval = ipu_cpd_validate_cpd(isp, moduledata + + mod_hdr->hdr_len, + moduledata_size - + mod_hdr->hdr_len, moduledata_size); + if (rval) { + dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n"); + return -EINVAL; + } + + return 0; +} + +static int ipu_cpd_validate_metadata(struct ipu_device *isp, + const void *metadata, u32 meta_size) +{ + const struct ipu_cpd_metadata_extn *extn = metadata; + + /* Sanity check for metadata size */ + if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) { + dev_err(&isp->pdev->dev, "%s: Invalid metadata\n", __func__); + return -EINVAL; + } + + /* Validate extension and image types */ + if (extn->extn_type != IPU_CPD_METADATA_EXTN_TYPE_IUNIT || + extn->img_type != IPU_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) { + dev_err(&isp->pdev->dev, + "Invalid metadata descriptor img_type (%d)\n", + extn->img_type); + return -EINVAL; + } + + /* Validate metadata size multiple of metadata components */ + if ((meta_size - sizeof(*extn)) % + sizeof(struct ipu_cpd_metadata_cmpnt)) { + dev_err(&isp->pdev->dev, "%s: Invalid metadata size\n", + __func__); + return -EINVAL; + } + + return 0; +} + +int ipu_cpd_validate_cpd_file(struct ipu_device *isp, + const void *cpd_file, unsigned long cpd_file_size) +{ + const struct ipu_cpd_hdr *hdr = cpd_file; + struct ipu_cpd_ent *ent; + int rval; + + rval = ipu_cpd_validate_cpd(isp, cpd_file, + cpd_file_size, cpd_file_size); + if (rval) { + dev_err(&isp->pdev->dev, "Invalid CPD in file\n"); + return -EINVAL; + } + + /* Check for CPD file marker */ + if (hdr->hdr_mark != CPD_HDR_MARK) { + dev_err(&isp->pdev->dev, "Invalid CPD header\n"); + return -EINVAL; + } + + /* Sanity check for manifest size */ + ent = ipu_cpd_get_manifest(cpd_file); + if (ent->len > MAX_MANIFEST_SIZE) { + dev_err(&isp->pdev->dev, "Invalid manifest size\n"); + return -EINVAL; + } + + /* Validate metadata */ + ent = ipu_cpd_get_metadata(cpd_file); + rval = ipu_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len); + if (rval) { + dev_err(&isp->pdev->dev, "Invalid metadata\n"); + return rval; + } + + /* Validate moduledata */ + ent = ipu_cpd_get_moduledata(cpd_file); + rval = ipu_cpd_validate_moduledata(isp, cpd_file + ent->offset, + ent->len); + if (rval) { + dev_err(&isp->pdev->dev, "Invalid moduledata\n"); + return rval; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_cpd_validate_cpd_file); + +unsigned int ipu_cpd_pkg_dir_get_address(const u64 *pkg_dir, int pkg_dir_idx) +{ + return pkg_dir[++pkg_dir_idx * PKG_DIR_ENT_LEN]; +} +EXPORT_SYMBOL_GPL(ipu_cpd_pkg_dir_get_address); + +unsigned int ipu_cpd_pkg_dir_get_num_entries(const u64 *pkg_dir) +{ + return pkg_dir[1]; +} +EXPORT_SYMBOL_GPL(ipu_cpd_pkg_dir_get_num_entries); + +unsigned int ipu_cpd_pkg_dir_get_size(const u64 *pkg_dir, int pkg_dir_idx) +{ + return pkg_dir[++pkg_dir_idx * PKG_DIR_ENT_LEN + 1] & PKG_DIR_SIZE_MASK; +} +EXPORT_SYMBOL_GPL(ipu_cpd_pkg_dir_get_size); + +unsigned int ipu_cpd_pkg_dir_get_type(const u64 *pkg_dir, int pkg_dir_idx) +{ + return pkg_dir[++pkg_dir_idx * PKG_DIR_ENT_LEN + 1] >> + PKG_DIR_ID_SHIFT & PKG_DIR_ID_MASK; +} +EXPORT_SYMBOL_GPL(ipu_cpd_pkg_dir_get_type); diff --git a/drivers/media/pci/intel/ipu-cpd.h b/drivers/media/pci/intel/ipu-cpd.h new file mode 100644 index 000000000000..7033e90e135f --- /dev/null +++ b/drivers/media/pci/intel/ipu-cpd.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation */ + +#ifndef IPU_CPD_H +#define IPU_CPD_H + +#define IPU_CPD_SIZE_OF_FW_ARCH_VERSION 7 +#define IPU_CPD_SIZE_OF_SYSTEM_VERSION 11 +#define IPU_CPD_SIZE_OF_COMPONENT_NAME 12 + +#define IPU_CPD_METADATA_EXTN_TYPE_IUNIT 0x10 + +#define IPU_CPD_METADATA_IMAGE_TYPE_RESERVED 0 +#define IPU_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1 +#define IPU_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2 + +#define IPU_CPD_PKG_DIR_PSYS_SERVER_IDX 0 +#define IPU_CPD_PKG_DIR_ISYS_SERVER_IDX 1 + +#define IPU_CPD_PKG_DIR_CLIENT_PG_TYPE 3 + +struct __packed ipu_cpd_module_data_hdr { + u32 hdr_len; + u32 endian; + u32 fw_pkg_date; + u32 hive_sdk_date; + u32 compiler_date; + u32 target_platform_type; + u8 sys_ver[IPU_CPD_SIZE_OF_SYSTEM_VERSION]; + u8 fw_arch_ver[IPU_CPD_SIZE_OF_FW_ARCH_VERSION]; + u8 rsvd[2]; +}; + +struct __packed ipu_cpd_hdr { + u32 hdr_mark; + u32 ent_cnt; + u8 hdr_ver; + u8 ent_ver; + u8 hdr_len; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + u8 chksm; + u32 name; +#else + u8 rsvd; + u32 sub_partition_name; + u32 chksm; +#endif +}; + +struct __packed ipu_cpd_ent { + u8 name[IPU_CPD_SIZE_OF_COMPONENT_NAME]; + u32 offset; + u32 len; + u8 rsvd[4]; +}; + +struct __packed ipu_cpd_metadata_cmpnt { + u32 id; + u32 size; + u32 ver; + u8 sha2_hash[32]; + u32 entry_point; + u32 icache_base_offs; + u8 attrs[16]; +}; + +struct __packed ipu_cpd_metadata_extn { + u32 extn_type; + u32 len; + u32 img_type; + u8 rsvd[16]; +}; + +struct __packed ipu_cpd_client_pkg_hdr { + u32 prog_list_offs; + u32 prog_list_size; + u32 prog_desc_offs; + u32 prog_desc_size; + u32 pg_manifest_offs; + u32 pg_manifest_size; + u32 prog_bin_offs; + u32 prog_bin_size; +}; + +void *ipu_cpd_create_pkg_dir(struct ipu_bus_device *adev, + const void *src, + dma_addr_t dma_addr_src, + dma_addr_t *dma_addr, unsigned int *pkg_dir_size); +void ipu_cpd_free_pkg_dir(struct ipu_bus_device *adev, + u64 *pkg_dir, + dma_addr_t dma_addr, unsigned int pkg_dir_size); +u32 ipu_cpd_get_pg_icache_base(struct ipu_device *isp, + u8 idx, + const void *cpd_file, + unsigned int cpd_file_size); +u32 ipu_cpd_get_pg_entry_point(struct ipu_device *isp, + u8 idx, + const void *cpd_file, + unsigned int cpd_file_size); +int ipu_cpd_validate_cpd_file(struct ipu_device *isp, + const void *cpd_file, + unsigned long cpd_file_size); +unsigned int ipu_cpd_pkg_dir_get_address(const u64 *pkg_dir, int pkg_dir_idx); +unsigned int ipu_cpd_pkg_dir_get_num_entries(const u64 *pkg_dir); +unsigned int ipu_cpd_pkg_dir_get_size(const u64 *pkg_dir, int pkg_dir_idx); +unsigned int ipu_cpd_pkg_dir_get_type(const u64 *pkg_dir, int pkg_dir_idx); + +#endif /* IPU_CPD_H */ diff --git a/drivers/media/pci/intel/ipu-dma.c b/drivers/media/pci/intel/ipu-dma.c new file mode 100644 index 000000000000..a5a963dfcc74 --- /dev/null +++ b/drivers/media/pci/intel/ipu-dma.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu-dma.h" +#include "ipu-mmu.h" + +/* Begin of things adapted from arch/arm/mm/dma-mapping.c */ +static void __dma_clear_buffer(struct page *page, size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + /* + * Ensure that the allocated pages are zeroed, and that any data + * lurking in the kernel direct-mapped region is invalidated. + */ + if (PageHighMem(page)) { + while (size > 0) { + void *ptr = kmap_atomic(page); + + memset(ptr, 0, PAGE_SIZE); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + clflush_cache_range(ptr, PAGE_SIZE); + kunmap_atomic(ptr); + page++; + size -= PAGE_SIZE; + } + } else { + void *ptr = page_address(page); + + memset(ptr, 0, size); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + clflush_cache_range(ptr, size); + } +} + +static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, + gfp_t gfp, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct page **pages; + int count = size >> PAGE_SHIFT; + int array_size = count * sizeof(struct page *); + int i = 0; + + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, GFP_KERNEL); + else + pages = vzalloc(array_size); + if (!pages) + return NULL; + + gfp |= __GFP_NOWARN; + + while (count) { + int j, order = __fls(count); + + pages[i] = alloc_pages(gfp, order); + while (!pages[i] && order) + pages[i] = alloc_pages(gfp, --order); + if (!pages[i]) + goto error; + + if (order) { + split_page(pages[i], order); + j = 1 << order; + while (--j) + pages[i + j] = pages[i] + j; + } + + __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs); + i += 1 << order; + count -= 1 << order; + } + + return pages; +error: + while (i--) + if (pages[i]) + __free_pages(pages[i], 0); + if (array_size <= PAGE_SIZE) + kfree(pages); + else + vfree(pages); + return NULL; +} + +static int __iommu_free_buffer(struct device *dev, struct page **pages, + size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + int count = size >> PAGE_SHIFT; + int array_size = count * sizeof(struct page *); + int i; + + for (i = 0; i < count; i++) { + if (pages[i]) { + __dma_clear_buffer(pages[i], PAGE_SIZE, attrs); + __free_pages(pages[i], 0); + } + } + + if (array_size <= PAGE_SIZE) + kfree(pages); + else + vfree(pages); + return 0; +} + +/* End of things adapted from arch/arm/mm/dma-mapping.c */ + +static void ipu_dma_sync_single_for_cpu(struct device *dev, + dma_addr_t dma_handle, + size_t size, + enum dma_data_direction dir) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + unsigned long pa = iommu_iova_to_phys(mmu->dmap->domain, dma_handle); + + clflush_cache_range(phys_to_virt(pa), size); +} + +static void ipu_dma_sync_sg_for_cpu(struct device *dev, + struct scatterlist *sglist, + int nents, enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nents, i) + clflush_cache_range(page_to_virt(sg_page(sg)), sg->length); +} + +static void *ipu_dma_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct page **pages; + struct iova *iova; + struct vm_struct *area; + int i; + int rval; + + size = PAGE_ALIGN(size); + + iova = alloc_iova(&mmu->dmap->iovad, size >> PAGE_SHIFT, + dma_get_mask(dev) >> PAGE_SHIFT, 0); + if (!iova) + return NULL; + + pages = __iommu_alloc_buffer(dev, size, gfp, attrs); + if (!pages) + goto out_free_iova; + + for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { + rval = iommu_map(mmu->dmap->domain, + (iova->pfn_lo + i) << PAGE_SHIFT, + page_to_phys(pages[i]), PAGE_SIZE, 0); + if (rval) + goto out_unmap; + } + + area = __get_vm_area(size, 0, VMALLOC_START, VMALLOC_END); + if (!area) + goto out_unmap; + + area->pages = pages; + + if (map_vm_area(area, PAGE_KERNEL, pages)) + goto out_vunmap; + + *dma_handle = iova->pfn_lo << PAGE_SHIFT; + + mmu->tlb_invalidate(mmu); + + return area->addr; + +out_vunmap: + vunmap(area->addr); + +out_unmap: + for (i--; i >= 0; i--) { + iommu_unmap(mmu->dmap->domain, (iova->pfn_lo + i) << PAGE_SHIFT, + PAGE_SIZE); + } + __iommu_free_buffer(dev, pages, size, attrs); + +out_free_iova: + __free_iova(&mmu->dmap->iovad, iova); + + return NULL; +} + +static void ipu_dma_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct vm_struct *area = find_vm_area(vaddr); + struct page **pages; + struct iova *iova = find_iova(&mmu->dmap->iovad, + dma_handle >> PAGE_SHIFT); + + if (WARN_ON(!area)) + return; + + if (WARN_ON(!area->pages)) + return; + + WARN_ON(!iova); + + size = PAGE_ALIGN(size); + + pages = area->pages; + + vunmap(vaddr); + + iommu_unmap(mmu->dmap->domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + + __iommu_free_buffer(dev, pages, size, attrs); + + __free_iova(&mmu->dmap->iovad, iova); + + mmu->tlb_invalidate(mmu); +} + +static int ipu_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *addr, dma_addr_t iova, size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct vm_struct *area = find_vm_area(addr); + size_t count = PAGE_ALIGN(size) >> PAGE_SHIFT; + size_t i; + + if (!area) + return -EFAULT; + + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + + if (size > area->size) + return -EFAULT; + + for (i = 0; i < count; i++) + vm_insert_page(vma, vma->vm_start + (i << PAGE_SHIFT), + area->pages[i]); + + return 0; +} + +static void ipu_dma_unmap_sg(struct device *dev, + struct scatterlist *sglist, + int nents, enum dma_data_direction dir, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct iova *iova = find_iova(&mmu->dmap->iovad, + sg_dma_address(sglist) >> PAGE_SHIFT); + + if (!nents) + return; + + WARN_ON(!iova); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + ipu_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); + + iommu_unmap(mmu->dmap->domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + + mmu->tlb_invalidate(mmu); + + __free_iova(&mmu->dmap->iovad, iova); +} + +static int ipu_dma_map_sg(struct device *dev, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct scatterlist *sg; + struct iova *iova; + size_t size = 0; + u32 iova_addr; + int i; + + for_each_sg(sglist, sg, nents, i) + size += PAGE_ALIGN(sg->length) >> PAGE_SHIFT; + + dev_dbg(dev, "dmamap: mapping sg %d entries, %zu pages\n", nents, size); + + iova = alloc_iova(&mmu->dmap->iovad, size, + dma_get_mask(dev) >> PAGE_SHIFT, 0); + if (!iova) + return 0; + + dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, + iova->pfn_hi); + + iova_addr = iova->pfn_lo; + + for_each_sg(sglist, sg, nents, i) { + int rval; + + dev_dbg(dev, "mapping entry %d: iova 0x%8.8x,phy 0x%16.16llx\n", + i, iova_addr << PAGE_SHIFT, + (unsigned long long)page_to_phys(sg_page(sg))); + rval = iommu_map(mmu->dmap->domain, iova_addr << PAGE_SHIFT, + page_to_phys(sg_page(sg)), + PAGE_ALIGN(sg->length), 0); + if (rval) + goto out_fail; + sg_dma_address(sg) = iova_addr << PAGE_SHIFT; +#ifdef CONFIG_NEED_SG_DMA_LENGTH + sg_dma_len(sg) = sg->length; +#endif /* CONFIG_NEED_SG_DMA_LENGTH */ + + iova_addr += PAGE_ALIGN(sg->length) >> PAGE_SHIFT; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + ipu_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL); + + mmu->tlb_invalidate(mmu); + + return nents; + +out_fail: + ipu_dma_unmap_sg(dev, sglist, i, dir, attrs); + + return 0; +} + +/* + * Create scatter-list for the already allocated DMA buffer + */ +static int ipu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct vm_struct *area = find_vm_area(cpu_addr); + int n_pages; + int ret = 0; + + if (WARN_ON(!area->pages)) + return -ENOMEM; + + n_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + + ret = sg_alloc_table_from_pages(sgt, area->pages, n_pages, 0, size, + GFP_KERNEL); + if (ret) + dev_dbg(dev, "IPU get sgt table fail\n"); + + return ret; +} + +const struct dma_map_ops ipu_dma_ops = { + .alloc = ipu_dma_alloc, + .free = ipu_dma_free, + .mmap = ipu_dma_mmap, + .map_sg = ipu_dma_map_sg, + .unmap_sg = ipu_dma_unmap_sg, + .sync_single_for_cpu = ipu_dma_sync_single_for_cpu, + .sync_single_for_device = ipu_dma_sync_single_for_cpu, + .sync_sg_for_cpu = ipu_dma_sync_sg_for_cpu, + .sync_sg_for_device = ipu_dma_sync_sg_for_cpu, + .get_sgtable = ipu_dma_get_sgtable, +}; +EXPORT_SYMBOL_GPL(ipu_dma_ops); diff --git a/drivers/media/pci/intel/ipu-dma.h b/drivers/media/pci/intel/ipu-dma.h new file mode 100644 index 000000000000..9974b69fd6fd --- /dev/null +++ b/drivers/media/pci/intel/ipu-dma.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_DMA_H +#define IPU_DMA_H + +#include + +struct iommu_domain; + +struct ipu_dma_mapping { + struct iommu_domain *domain; + struct iova_domain iovad; + struct kref ref; +}; + +extern const struct dma_map_ops ipu_dma_ops; + +#endif /* IPU_DMA_H */ diff --git a/drivers/media/pci/intel/ipu-fw-com.c b/drivers/media/pci/intel/ipu-fw-com.c new file mode 100644 index 000000000000..4ddf1116a756 --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-com.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include + +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-fw-com.h" +#include "ipu-bus.h" + +/* + * FWCOM layer is a shared resource between FW and driver. It consist + * of token queues to both send and receive directions. Queue is simply + * an array of structures with read and write indexes to the queue. + * There are 1...n queues to both directions. Queues locates in + * system ram and are mapped to ISP MMU so that both CPU and ISP can + * see the same buffer. Indexes are located in ISP DMEM so that FW code + * can poll those with very low latency and cost. CPU access to indexes is + * more costly but that happens only at message sending time and + * interrupt trigged message handling. CPU doesn't need to poll indexes. + * wr_reg / rd_reg are offsets to those dmem location. They are not + * the indexes itself. + */ + +/* Shared structure between driver and FW - do not modify */ +struct ipu_fw_sys_queue { + u64 host_address; + u32 vied_address; + u32 size; + u32 token_size; + u32 wr_reg; /* reg no in subsystem's regmem */ + u32 rd_reg; + u32 _align; +}; + +struct ipu_fw_sys_queue_res { + u64 host_address; + u32 vied_address; + u32 reg; +}; + +enum syscom_state { + /* Program load or explicit host setting should init to this */ + SYSCOM_STATE_UNINIT = 0x57A7E000, + /* SP Syscom sets this when it is ready for use */ + SYSCOM_STATE_READY = 0x57A7E001, + /* SP Syscom sets this when no more syscom accesses will happen */ + SYSCOM_STATE_INACTIVE = 0x57A7E002 +}; + +enum syscom_cmd { + /* Program load or explicit host setting should init to this */ + SYSCOM_COMMAND_UNINIT = 0x57A7F000, + /* Host Syscom requests syscom to become inactive */ + SYSCOM_COMMAND_INACTIVE = 0x57A7F001 +}; + +/* firmware config: data that sent from the host to SP via DDR */ +/* Cell copies data into a context */ + +struct ipu_fw_syscom_config { + u32 firmware_address; + + u32 num_input_queues; + u32 num_output_queues; + + /* ISP pointers to an array of ipu_fw_sys_queue structures */ + u32 input_queue; + u32 output_queue; + + /* ISYS / PSYS private data */ + u32 specific_addr; + u32 specific_size; +}; + +/* End of shared structures / data */ + +struct ipu_fw_com_context { + struct ipu_bus_device *adev; + void __iomem *dmem_addr; + int (*cell_ready)(struct ipu_bus_device *adev); + void (*cell_start)(struct ipu_bus_device *adev); + + void *dma_buffer; + dma_addr_t dma_addr; + unsigned int dma_size; + unsigned long attrs; + + unsigned int num_input_queues; + unsigned int num_output_queues; + + struct ipu_fw_sys_queue *input_queue; /* array of host to SP queues */ + struct ipu_fw_sys_queue *output_queue; /* array of SP to host */ + + void *config_host_addr; + void *specific_host_addr; + u64 ibuf_host_addr; + u64 obuf_host_addr; + + u32 config_vied_addr; + u32 input_queue_vied_addr; + u32 output_queue_vied_addr; + u32 specific_vied_addr; + u32 ibuf_vied_addr; + u32 obuf_vied_addr; +}; + +#define FW_COM_WR_REG 0 +#define FW_COM_RD_REG 4 + +#define REGMEM_OFFSET 0 + +enum regmem_id { + /* pass pkg_dir address to SPC in non-secure mode */ + PKG_DIR_ADDR_REG = 0, + /* pass syscom configuration to SPC */ + SYSCOM_CONFIG_REG = 1, + /* syscom state - modified by SP */ + SYSCOM_STATE_REG = 2, + /* syscom commands - modified by the host */ + SYSCOM_COMMAND_REG = 3, + /* Store interrupt status - updated by SP */ + SYSCOM_IRQ_REG = 4, + /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/ + SYSCOM_VTL0_ADDR_MASK = 5, + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 6 +}; + +enum message_direction { + DIR_RECV = 0, + DIR_SEND +}; + +static unsigned int num_messages(unsigned int wr, unsigned int rd, + unsigned int size) +{ + if (wr < rd) + wr += size; + return wr - rd; +} + +static unsigned int num_free(unsigned int wr, unsigned int rd, + unsigned int size) +{ + return size - num_messages(wr, rd, size); +} + +static unsigned int curr_index(void __iomem *q_dmem, + enum message_direction dir) +{ + return readl(q_dmem + + (dir == DIR_RECV ? FW_COM_RD_REG : FW_COM_WR_REG)); +} + +static unsigned int inc_index(void __iomem *q_dmem, struct ipu_fw_sys_queue *q, + enum message_direction dir) +{ + unsigned int index; + + index = curr_index(q_dmem, dir) + 1; + return index >= q->size ? 0 : index; +} + +static unsigned int ipu_sys_queue_buf_size(unsigned int size, + unsigned int token_size) +{ + return (size + 1) * token_size; +} + +static void ipu_sys_queue_init(struct ipu_fw_sys_queue *q, unsigned int size, + unsigned int token_size, struct ipu_fw_sys_queue_res *res) +{ + unsigned int buf_size; + + q->size = size + 1; + q->token_size = token_size; + buf_size = ipu_sys_queue_buf_size(size, token_size); + + /* acquire the shared buffer space */ + q->host_address = res->host_address; + res->host_address += buf_size; + q->vied_address = res->vied_address; + res->vied_address += buf_size; + + /* acquire the shared read and writer pointers */ + q->wr_reg = res->reg; + res->reg++; + q->rd_reg = res->reg; + res->reg++; +} + +void *ipu_fw_com_prepare(struct ipu_fw_com_cfg *cfg, + struct ipu_bus_device *adev, void __iomem *base) +{ + struct ipu_fw_com_context *ctx; + struct ipu_fw_syscom_config *fw_cfg; + unsigned int i; + unsigned int sizeall, offset; + unsigned int sizeinput = 0, sizeoutput = 0; + unsigned long attrs = 0; + struct ipu_fw_sys_queue_res res; + + /* error handling */ + if (!cfg || !cfg->cell_start || !cfg->cell_ready) + return NULL; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET; + ctx->adev = adev; + ctx->cell_start = cfg->cell_start; + ctx->cell_ready = cfg->cell_ready; + + ctx->num_input_queues = cfg->num_input_queues; + ctx->num_output_queues = cfg->num_output_queues; + + /* + * Allocate DMA mapped memory. Allocate one big chunk. + */ + sizeall = + /* Base cfg for FW */ + roundup(sizeof(struct ipu_fw_syscom_config), 8) + + /* Descriptions of the queues */ + cfg->num_input_queues * sizeof(struct ipu_fw_sys_queue) + + cfg->num_output_queues * sizeof(struct ipu_fw_sys_queue) + + /* FW specific information structure */ + roundup(cfg->specific_size, 8); + + for (i = 0; i < cfg->num_input_queues; i++) + sizeinput += ipu_sys_queue_buf_size(cfg->input[i].queue_size, + cfg->input[i].token_size); + + for (i = 0; i < cfg->num_output_queues; i++) + sizeoutput += ipu_sys_queue_buf_size(cfg->output[i].queue_size, + cfg->output[i].token_size); + + sizeall += sizeinput + sizeoutput; + + ctx->dma_buffer = dma_alloc_attrs(&ctx->adev->dev, sizeall, + &ctx->dma_addr, GFP_KERNEL, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + attrs); + ctx->attrs = attrs; +#else + NULL); +#endif + if (!ctx->dma_buffer) { + dev_err(&ctx->adev->dev, "failed to allocate dma memory\n"); + return NULL; + } + + ctx->dma_size = sizeall; + + /* This is the address where FW starts to parse allocations */ + ctx->config_host_addr = ctx->dma_buffer; + ctx->config_vied_addr = ctx->dma_addr; + fw_cfg = (struct ipu_fw_syscom_config *)ctx->config_host_addr; + offset = roundup(sizeof(struct ipu_fw_syscom_config), 8); + + ctx->input_queue = ctx->dma_buffer + offset; + ctx->input_queue_vied_addr = ctx->dma_addr + offset; + offset += cfg->num_input_queues * sizeof(struct ipu_fw_sys_queue); + + ctx->output_queue = ctx->dma_buffer + offset; + ctx->output_queue_vied_addr = ctx->dma_addr + offset; + offset += cfg->num_output_queues * sizeof(struct ipu_fw_sys_queue); + + ctx->specific_host_addr = ctx->dma_buffer + offset; + ctx->specific_vied_addr = ctx->dma_addr + offset; + offset += roundup(cfg->specific_size, 8); + + ctx->ibuf_host_addr = (uintptr_t)(ctx->dma_buffer + offset); + ctx->ibuf_vied_addr = ctx->dma_addr + offset; + offset += sizeinput; + + ctx->obuf_host_addr = (uintptr_t)(ctx->dma_buffer + offset); + ctx->obuf_vied_addr = ctx->dma_addr + offset; + offset += sizeoutput; + + /* initialize input queues */ + res.reg = SYSCOM_QPR_BASE_REG; + res.host_address = ctx->ibuf_host_addr; + res.vied_address = ctx->ibuf_vied_addr; + for (i = 0; i < cfg->num_input_queues; i++) { + ipu_sys_queue_init(ctx->input_queue + i, + cfg->input[i].queue_size, + cfg->input[i].token_size, &res); + } + + /* initialize output queues */ + res.host_address = ctx->obuf_host_addr; + res.vied_address = ctx->obuf_vied_addr; + for (i = 0; i < cfg->num_output_queues; i++) { + ipu_sys_queue_init(ctx->output_queue + i, + cfg->output[i].queue_size, + cfg->output[i].token_size, &res); + } + + /* copy firmware specific data */ + if (cfg->specific_addr && cfg->specific_size) { + memcpy((void *)ctx->specific_host_addr, + cfg->specific_addr, cfg->specific_size); + } + + fw_cfg->num_input_queues = cfg->num_input_queues; + fw_cfg->num_output_queues = cfg->num_output_queues; + fw_cfg->input_queue = ctx->input_queue_vied_addr; + fw_cfg->output_queue = ctx->output_queue_vied_addr; + fw_cfg->specific_addr = ctx->specific_vied_addr; + fw_cfg->specific_size = cfg->specific_size; + + clflush_cache_range(ctx->dma_buffer, sizeall); + + return ctx; +} +EXPORT_SYMBOL_GPL(ipu_fw_com_prepare); + +int ipu_fw_com_open(struct ipu_fw_com_context *ctx) +{ + /* Check if SP is in valid state */ + if (!ctx->cell_ready(ctx->adev)) + return -EIO; + + /* store syscom uninitialized state */ + writel(SYSCOM_STATE_UNINIT, ctx->dmem_addr + SYSCOM_STATE_REG * 4); + /* store syscom uninitialized command */ + writel(SYSCOM_COMMAND_UNINIT, + ctx->dmem_addr + SYSCOM_COMMAND_REG * 4); + /* store firmware configuration address */ + writel(ctx->config_vied_addr, + ctx->dmem_addr + SYSCOM_CONFIG_REG * 4); + + ctx->cell_start(ctx->adev); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_com_open); + +int ipu_fw_com_close(struct ipu_fw_com_context *ctx) +{ + int state; + + state = readl(ctx->dmem_addr + 4 * SYSCOM_STATE_REG); + if (state != SYSCOM_STATE_READY) + return -EBUSY; + + /* set close request flag */ + writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr + + SYSCOM_COMMAND_REG * 4); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_com_close); + +int ipu_fw_com_release(struct ipu_fw_com_context *ctx, unsigned int force) +{ + /* check if release is forced, an verify cell state if it is not */ + if (!force && !ctx->cell_ready(ctx->adev)) + return -EBUSY; + + dma_free_attrs(&ctx->adev->dev, ctx->dma_size, + ctx->dma_buffer, ctx->dma_addr, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ctx->attrs); +#else + NULL); +#endif + kfree(ctx); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_com_release); + +int ipu_fw_com_ready(struct ipu_fw_com_context *ctx) +{ + int state; + + /* check if SP syscom is ready to open the queue */ + state = readl(ctx->dmem_addr + SYSCOM_STATE_REG * 4); + if (state != SYSCOM_STATE_READY) + return -EBUSY; /* SPC is not ready to handle messages yet */ + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_com_ready); + +static bool is_index_valid(struct ipu_fw_sys_queue *q, unsigned int index) +{ + if (index >= q->size) + return false; + return true; +} + +void *ipu_send_get_token(struct ipu_fw_com_context *ctx, int q_nbr) +{ + struct ipu_fw_sys_queue *q = &ctx->input_queue[q_nbr]; + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; + unsigned int wr, rd; + unsigned int packets; + unsigned int index; + + wr = readl(q_dmem + FW_COM_WR_REG); + rd = readl(q_dmem + FW_COM_RD_REG); + + /* Catch indexes in dmem */ + if (!is_index_valid(q, wr) || !is_index_valid(q, rd)) + return NULL; + + packets = num_free(wr + 1, rd, q->size); + if (packets <= 0) + return NULL; + + index = curr_index(q_dmem, DIR_SEND); + + return (void *)(unsigned long)q->host_address + (index * q->token_size); +} +EXPORT_SYMBOL_GPL(ipu_send_get_token); + +void ipu_send_put_token(struct ipu_fw_com_context *ctx, int q_nbr) +{ + struct ipu_fw_sys_queue *q = &ctx->input_queue[q_nbr]; + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; + int index = curr_index(q_dmem, DIR_SEND); + void *addr = (void *)(unsigned long)q->host_address + + (index * q->token_size); + + clflush_cache_range(addr, q->token_size); + + /* Increment index */ + index = inc_index(q_dmem, q, DIR_SEND); + + writel(index, q_dmem + FW_COM_WR_REG); +} +EXPORT_SYMBOL_GPL(ipu_send_put_token); + +void *ipu_recv_get_token(struct ipu_fw_com_context *ctx, int q_nbr) +{ + struct ipu_fw_sys_queue *q = &ctx->output_queue[q_nbr]; + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; + unsigned int wr, rd; + unsigned int packets; + void *addr; + + wr = readl(q_dmem + FW_COM_WR_REG); + rd = readl(q_dmem + FW_COM_RD_REG); + + /* Catch indexes in dmem? */ + if (!is_index_valid(q, wr) || !is_index_valid(q, rd)) + return NULL; + + packets = num_messages(wr, rd, q->size); + if (packets <= 0) + return NULL; + + addr = (void *)(unsigned long)q->host_address + (rd * q->token_size); + clflush_cache_range(addr, q->token_size); + + return addr; +} +EXPORT_SYMBOL_GPL(ipu_recv_get_token); + +void ipu_recv_put_token(struct ipu_fw_com_context *ctx, int q_nbr) +{ + struct ipu_fw_sys_queue *q = &ctx->output_queue[q_nbr]; + void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4; + unsigned int rd = inc_index(q_dmem, q, DIR_RECV); + + /* Release index */ + writel(rd, q_dmem + FW_COM_RD_REG); +} +EXPORT_SYMBOL_GPL(ipu_recv_put_token); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu fw comm library"); diff --git a/drivers/media/pci/intel/ipu-fw-com.h b/drivers/media/pci/intel/ipu-fw-com.h new file mode 100644 index 000000000000..de47455ea9a4 --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-com.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_FW_COM_H +#define IPU_FW_COM_H + +struct ipu_fw_com_context; +struct ipu_bus_device; + +struct ipu_fw_syscom_queue_config { + unsigned int queue_size; /* tokens per queue */ + unsigned int token_size; /* bytes per token */ +}; + +struct ipu_fw_com_cfg { + unsigned int num_input_queues; + unsigned int num_output_queues; + struct ipu_fw_syscom_queue_config *input; + struct ipu_fw_syscom_queue_config *output; + + unsigned int dmem_addr; + + /* firmware-specific configuration data */ + void *specific_addr; + unsigned int specific_size; + int (*cell_ready)(struct ipu_bus_device *adev); + void (*cell_start)(struct ipu_bus_device *adev); +}; + +void *ipu_fw_com_prepare(struct ipu_fw_com_cfg *cfg, + struct ipu_bus_device *adev, void __iomem *base); + +int ipu_fw_com_open(struct ipu_fw_com_context *ctx); +int ipu_fw_com_ready(struct ipu_fw_com_context *ctx); +int ipu_fw_com_close(struct ipu_fw_com_context *ctx); +int ipu_fw_com_release(struct ipu_fw_com_context *ctx, unsigned int force); + +void *ipu_recv_get_token(struct ipu_fw_com_context *ctx, int q_nbr); +void ipu_recv_put_token(struct ipu_fw_com_context *ctx, int q_nbr); +void *ipu_send_get_token(struct ipu_fw_com_context *ctx, int q_nbr); +void ipu_send_put_token(struct ipu_fw_com_context *ctx, int q_nbr); + +#endif diff --git a/drivers/media/pci/intel/ipu-fw-isys.c b/drivers/media/pci/intel/ipu-fw-isys.c new file mode 100644 index 000000000000..e68e03e3a752 --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-isys.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include + +#include +#include +#include "ipu-platform-regs.h" +#include "ipu-fw-isys.h" +#include "ipu-fw-com.h" +#include "ipu-isys.h" + +#define IPU_FW_UNSUPPORTED_DATA_TYPE 0 +static const uint32_t +extracted_bits_per_pixel_per_mipi_data_type[N_IPU_FW_ISYS_MIPI_DATA_TYPE] = { + + 64, /* [0x00] IPU_FW_ISYS_MIPI_DATA_TYPE_FRAME_START_CODE */ + 64, /* [0x01] IPU_FW_ISYS_MIPI_DATA_TYPE_FRAME_END_CODE */ + 64, /* [0x02] IPU_FW_ISYS_MIPI_DATA_TYPE_LINE_START_CODE */ + 64, /* [0x03] IPU_FW_ISYS_MIPI_DATA_TYPE_LINE_END_CODE */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x04] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x05] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x06] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x07] */ + 64, /* [0x08] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT1 */ + 64, /* [0x09] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT2 */ + 64, /* [0x0A] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT3 */ + 64, /* [0x0B] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT4 */ + 64, /* [0x0C] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT5 */ + 64, /* [0x0D] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT6 */ + 64, /* [0x0E] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT7 */ + 64, /* [0x0F] IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT8 */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x10] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x11] */ + 8, /* [0x12] IPU_FW_ISYS_MIPI_DATA_TYPE_EMBEDDED */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x13] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x14] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x15] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x16] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x17] */ + 12, /* [0x18] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8 */ + 15, /* [0x19] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_10 */ + 12, /* [0x1A] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8_LEGACY */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x1B] */ + 12, /* [0x1C] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8_SHIFT */ + 15, /* [0x1D] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_10_SHIFT */ + 16, /* [0x1E] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV422_8 */ + 20, /* [0x1F] IPU_FW_ISYS_MIPI_DATA_TYPE_YUV422_10 */ + 16, /* [0x20] IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_444 */ + 16, /* [0x21] IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_555 */ + 16, /* [0x22] IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_565 */ + 18, /* [0x23] IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_666 */ + 24, /* [0x24] IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_888 */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x25] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x26] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x27] */ + 6, /* [0x28] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_6 */ + 7, /* [0x29] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_7 */ + 8, /* [0x2A] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_8 */ + 10, /* [0x2B] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_10 */ + 12, /* [0x2C] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_12 */ + 14, /* [0x2D] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_14 */ + 16, /* [0x2E] IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_16 */ + 8, /* [0x2F] IPU_FW_ISYS_MIPI_DATA_TYPE_BINARY_8 */ + 8, /* [0x30] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF1 */ + 8, /* [0x31] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF2 */ + 8, /* [0x32] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF3 */ + 8, /* [0x33] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF4 */ + 8, /* [0x34] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF5 */ + 8, /* [0x35] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF6 */ + 8, /* [0x36] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF7 */ + 8, /* [0x37] IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF8 */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x38] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x39] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x3A] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x3B] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x3C] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x3D] */ + IPU_FW_UNSUPPORTED_DATA_TYPE, /* [0x3E] */ + IPU_FW_UNSUPPORTED_DATA_TYPE /* [0x3F] */ +}; + + +void ipu_fw_isys_set_params(struct ipu_fw_isys_stream_cfg_data_abi *stream_cfg) +{ + unsigned int i; + unsigned int idx; + + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + idx = stream_cfg->input_pins[i].dt; + stream_cfg->input_pins[i].bits_per_pix = + extracted_bits_per_pixel_per_mipi_data_type[idx]; + stream_cfg->input_pins[i].mapped_dt = + N_IPU_FW_ISYS_MIPI_DATA_TYPE; + } +} + +void +ipu_fw_isys_dump_stream_cfg(struct device *dev, + struct ipu_fw_isys_stream_cfg_data_abi *stream_cfg) +{ + unsigned int i; + + dev_dbg(dev, "---------------------------\n"); + dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); + dev_dbg(dev, "---------------------------\n"); + + dev_dbg(dev, "Source %d\n", stream_cfg->src); + dev_dbg(dev, "VC %d\n", stream_cfg->vc); + dev_dbg(dev, "Nof input pins %d\n", stream_cfg->nof_input_pins); + dev_dbg(dev, "Nof output pins %d\n", stream_cfg->nof_output_pins); + + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + dev_dbg(dev, "Input pin %d\n", i); + dev_dbg(dev, "Mipi data type 0x%0x\n", + stream_cfg->input_pins[i].dt); + dev_dbg(dev, "Mipi store mode %d\n", + stream_cfg->input_pins[i].mipi_store_mode); + dev_dbg(dev, "Bits per pixel %d\n", + stream_cfg->input_pins[i].bits_per_pix); + dev_dbg(dev, "Mapped data type 0x%0x\n", + stream_cfg->input_pins[i].mapped_dt); + dev_dbg(dev, "Input res width %d\n", + stream_cfg->input_pins[i].input_res.width); + dev_dbg(dev, "Input res height %d\n", + stream_cfg->input_pins[i].input_res.height); + } + + for (i = 0; i < N_IPU_FW_ISYS_CROPPING_LOCATION; i++) { + dev_dbg(dev, "Crop info %d\n", i); + dev_dbg(dev, "Crop.top_offset %d\n", + stream_cfg->crop[i].top_offset); + dev_dbg(dev, "Crop.left_offset %d\n", + stream_cfg->crop[i].left_offset); + dev_dbg(dev, "Crop.bottom_offset %d\n", + stream_cfg->crop[i].bottom_offset); + dev_dbg(dev, "Crop.right_offset %d\n", + stream_cfg->crop[i].right_offset); + dev_dbg(dev, "----------------\n"); + } + + for (i = 0; i < stream_cfg->nof_output_pins; i++) { + dev_dbg(dev, "Output pin %d\n", i); + dev_dbg(dev, "Output input pin id %d\n", + stream_cfg->output_pins[i].input_pin_id); + dev_dbg(dev, "Output res width %d\n", + stream_cfg->output_pins[i].output_res.width); + dev_dbg(dev, "Output res height %d\n", + stream_cfg->output_pins[i].output_res.height); + dev_dbg(dev, "Stride %d\n", stream_cfg->output_pins[i].stride); + dev_dbg(dev, "Pin type %d\n", stream_cfg->output_pins[i].pt); + dev_dbg(dev, "Ft %d\n", stream_cfg->output_pins[i].ft); + dev_dbg(dev, "Watermar in lines %d\n", + stream_cfg->output_pins[i].watermark_in_lines); + dev_dbg(dev, "Send irq %d\n", + stream_cfg->output_pins[i].send_irq); + dev_dbg(dev, "Reserve compression %d\n", + stream_cfg->output_pins[i].reserve_compression); + dev_dbg(dev, "----------------\n"); + } + + dev_dbg(dev, "Isl_use %d\n", stream_cfg->isl_use); + switch (stream_cfg->isl_use) { + case IPU_FW_ISYS_USE_SINGLE_ISA: + dev_dbg(dev, "ISA cfg:\n"); + dev_dbg(dev, "blc_enabled %d\n", stream_cfg->isa_cfg.cfg.blc); + dev_dbg(dev, "lsc_enabled %d\n", stream_cfg->isa_cfg.cfg.lsc); + dev_dbg(dev, "dpc_enabled %d\n", stream_cfg->isa_cfg.cfg.dpc); + dev_dbg(dev, "downscaler_enabled %d\n", + stream_cfg->isa_cfg.cfg.downscaler); + dev_dbg(dev, "awb_enabled %d\n", stream_cfg->isa_cfg.cfg.awb); + dev_dbg(dev, "af_enabled %d\n", stream_cfg->isa_cfg.cfg.af); + dev_dbg(dev, "ae_enabled %d\n", stream_cfg->isa_cfg.cfg.ae); + break; + case IPU_FW_ISYS_USE_SINGLE_DUAL_ISL: + case IPU_FW_ISYS_USE_NO_ISL_NO_ISA: + default: + break; + } +} + +void ipu_fw_isys_dump_frame_buff_set(struct device *dev, + struct ipu_fw_isys_frame_buff_set_abi *buf, + unsigned int outputs) +{ + unsigned int i; + + dev_dbg(dev, "--------------------------\n"); + dev_dbg(dev, "IPU_FW_ISYS_FRAME_BUFF_SET\n"); + dev_dbg(dev, "--------------------------\n"); + + for (i = 0; i < outputs; i++) { + dev_dbg(dev, "Output pin %d\n", i); + dev_dbg(dev, "out_buf_id %llu\n", + buf->output_pins[i].out_buf_id); + dev_dbg(dev, "addr 0x%x\n", buf->output_pins[i].addr); + dev_dbg(dev, "compress %u\n", buf->output_pins[i].compress); + + dev_dbg(dev, "----------------\n"); + } + + dev_dbg(dev, "process_group_light.addr 0x%x\n", + buf->process_group_light.addr); + dev_dbg(dev, "process_group_light.param_buf_id %llu\n", + buf->process_group_light.param_buf_id); + dev_dbg(dev, "send_irq_sof 0x%x\n", buf->send_irq_sof); + dev_dbg(dev, "send_irq_eof 0x%x\n", buf->send_irq_eof); + dev_dbg(dev, "send_resp_sof 0x%x\n", buf->send_resp_sof); + dev_dbg(dev, "send_resp_eof 0x%x\n", buf->send_resp_eof); +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + dev_dbg(dev, "send_irq_capture_ack 0x%x\n", buf->send_irq_capture_ack); + dev_dbg(dev, "send_irq_capture_done 0x%x\n", buf->send_irq_capture_done); +#endif +} diff --git a/drivers/media/pci/intel/ipu-fw-isys.h b/drivers/media/pci/intel/ipu-fw-isys.h new file mode 100644 index 000000000000..d338d32829c0 --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-isys.h @@ -0,0 +1,838 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_FW_ISYS_H +#define IPU_FW_ISYS_H + +#include "ipu-fw-com.h" + +/* Max number of Input/Output Pins */ +#define IPU_MAX_IPINS 4 + +/* worst case is ISA use where a single input pin produces: + * Mipi output, NS Pixel Output, and Scaled Pixel Output. + * This is how the 2 is calculated + */ +#define IPU_MAX_OPINS ((IPU_MAX_IPINS) + 2) + +/* Max number of supported virtual streams */ +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) +#define IPU_STREAM_ID_MAX 8 +#else +#define IPU_STREAM_ID_MAX 16 +#endif + +/* Aligned with the approach of having one dedicated per stream */ +#define IPU_N_MAX_MSG_SEND_QUEUES (IPU_STREAM_ID_MAX) +/* Single return queue for all streams/commands type */ +#define IPU_N_MAX_MSG_RECV_QUEUES 1 +/* Single device queue for high priority commands (bypass in-order queue) */ +#define IPU_N_MAX_DEV_SEND_QUEUES 1 +/* Single dedicated send queue for proxy interface */ +#define IPU_N_MAX_PROXY_SEND_QUEUES 1 +/* Single dedicated recv queue for proxy interface */ +#define IPU_N_MAX_PROXY_RECV_QUEUES 1 +/* Send queues layout */ +#define IPU_BASE_PROXY_SEND_QUEUES 0 +#define IPU_BASE_DEV_SEND_QUEUES \ + (IPU_BASE_PROXY_SEND_QUEUES + IPU_N_MAX_PROXY_SEND_QUEUES) +#define IPU_BASE_MSG_SEND_QUEUES \ + (IPU_BASE_DEV_SEND_QUEUES + IPU_N_MAX_DEV_SEND_QUEUES) +#define IPU_N_MAX_SEND_QUEUES \ + (IPU_BASE_MSG_SEND_QUEUES + IPU_N_MAX_MSG_SEND_QUEUES) +/* Recv queues layout */ +#define IPU_BASE_PROXY_RECV_QUEUES 0 +#define IPU_BASE_MSG_RECV_QUEUES \ + (IPU_BASE_PROXY_RECV_QUEUES + IPU_N_MAX_PROXY_RECV_QUEUES) +#define IPU_N_MAX_RECV_QUEUES \ + (IPU_BASE_MSG_RECV_QUEUES + IPU_N_MAX_MSG_RECV_QUEUES) + +/* Consider 1 slot per stream since driver is not expected to pipeline + * device commands for the same stream + */ +#define IPU_DEV_SEND_QUEUE_SIZE (IPU_STREAM_ID_MAX) + +/* Max number of supported SRAM buffer partitions. + * It refers to the size of stream partitions. + * These partitions are further subpartitioned internally + * by the FW, but by declaring statically the stream + * partitions we solve the buffer fragmentation issue + */ +#define IPU_NOF_SRAM_BLOCKS_MAX (IPU_STREAM_ID_MAX) + +/* Max number of supported input pins routed in ISL */ +#define IPU_MAX_IPINS_IN_ISL 2 + +/* Max number of planes for frame formats supported by the FW */ +#define IPU_PIN_PLANES_MAX 4 + +/** + * enum ipu_fw_isys_resp_type + */ +enum ipu_fw_isys_resp_type { + IPU_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0, + IPU_FW_ISYS_RESP_TYPE_STREAM_START_ACK, + IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, + IPU_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, + IPU_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, + IPU_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, + IPU_FW_ISYS_RESP_TYPE_PIN_DATA_READY, + IPU_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK, + IPU_FW_ISYS_RESP_TYPE_FRAME_SOF, + IPU_FW_ISYS_RESP_TYPE_FRAME_EOF, + IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, + IPU_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED, + IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED, + IPU_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED, + IPU_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED, + IPU_FW_ISYS_RESP_TYPE_STATS_DATA_READY, + N_IPU_FW_ISYS_RESP_TYPE +}; + +/** + * enum ipu_fw_isys_send_type + */ +enum ipu_fw_isys_send_type { + IPU_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0, + IPU_FW_ISYS_SEND_TYPE_STREAM_START, + IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE, + IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE, + IPU_FW_ISYS_SEND_TYPE_STREAM_STOP, + IPU_FW_ISYS_SEND_TYPE_STREAM_FLUSH, + IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE, + N_IPU_FW_ISYS_SEND_TYPE +}; + +/** + * enum ipu_fw_isys_queue_type + */ +enum ipu_fw_isys_queue_type { + IPU_FW_ISYS_QUEUE_TYPE_PROXY = 0, + IPU_FW_ISYS_QUEUE_TYPE_DEV, + IPU_FW_ISYS_QUEUE_TYPE_MSG, + N_IPU_FW_ISYS_QUEUE_TYPE +}; + +/** + * enum ipu_fw_isys_stream_source: Specifies a source for a stream + */ +enum ipu_fw_isys_stream_source { + IPU_FW_ISYS_STREAM_SRC_PORT_0 = 0, + IPU_FW_ISYS_STREAM_SRC_PORT_1, + IPU_FW_ISYS_STREAM_SRC_PORT_2, + IPU_FW_ISYS_STREAM_SRC_PORT_3, + IPU_FW_ISYS_STREAM_SRC_PORT_4, + IPU_FW_ISYS_STREAM_SRC_PORT_5, + IPU_FW_ISYS_STREAM_SRC_PORT_6, + IPU_FW_ISYS_STREAM_SRC_PORT_7, + IPU_FW_ISYS_STREAM_SRC_PORT_8, + IPU_FW_ISYS_STREAM_SRC_PORT_9, + IPU_FW_ISYS_STREAM_SRC_PORT_10, + IPU_FW_ISYS_STREAM_SRC_PORT_11, + IPU_FW_ISYS_STREAM_SRC_PORT_12, + IPU_FW_ISYS_STREAM_SRC_PORT_13, + IPU_FW_ISYS_STREAM_SRC_PORT_14, + IPU_FW_ISYS_STREAM_SRC_PORT_15, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_0, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_1, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_2, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_3, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_4, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_5, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_6, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_7, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_8, + IPU_FW_ISYS_STREAM_SRC_MIPIGEN_9, + N_IPU_FW_ISYS_STREAM_SRC +}; + +#define IPU_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU_FW_ISYS_STREAM_SRC_PORT_0 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU_FW_ISYS_STREAM_SRC_PORT_1 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU_FW_ISYS_STREAM_SRC_PORT_2 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU_FW_ISYS_STREAM_SRC_PORT_3 + +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU_FW_ISYS_STREAM_SRC_PORT_4 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU_FW_ISYS_STREAM_SRC_PORT_5 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 IPU_FW_ISYS_STREAM_SRC_PORT_6 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 IPU_FW_ISYS_STREAM_SRC_PORT_7 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 IPU_FW_ISYS_STREAM_SRC_PORT_8 +#define IPU_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 IPU_FW_ISYS_STREAM_SRC_PORT_9 + +#define IPU_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU_FW_ISYS_STREAM_SRC_MIPIGEN_0 +#define IPU_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU_FW_ISYS_STREAM_SRC_MIPIGEN_1 + +/** + * enum ipu_fw_isys_mipi_vc: MIPI csi2 spec + * supports up to 4 virtual per physical channel + */ +enum ipu_fw_isys_mipi_vc { + IPU_FW_ISYS_MIPI_VC_0 = 0, + IPU_FW_ISYS_MIPI_VC_1, + IPU_FW_ISYS_MIPI_VC_2, + IPU_FW_ISYS_MIPI_VC_3, + N_IPU_FW_ISYS_MIPI_VC +}; + +/** + * Supported Pixel Frame formats. Expandable if needed + */ +enum ipu_fw_isys_frame_format_type { + IPU_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */ + IPU_FW_ISYS_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */ + IPU_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */ + IPU_FW_ISYS_FRAME_FORMAT_NV12_TILEY, /* 12 bit YUV 420, + * Intel proprietary tiled format, + * TileY + */ + IPU_FW_ISYS_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */ + IPU_FW_ISYS_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */ + IPU_FW_ISYS_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */ + IPU_FW_ISYS_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */ + IPU_FW_ISYS_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */ + IPU_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */ + IPU_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */ + IPU_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */ + IPU_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */ + IPU_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */ + IPU_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */ + IPU_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */ + IPU_FW_ISYS_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */ + IPU_FW_ISYS_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */ + IPU_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */ + IPU_FW_ISYS_FRAME_FORMAT_YUV_LINE, /* Internal format, 2 y lines + * followed by a uvinterleaved line + */ + IPU_FW_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */ + IPU_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */ + IPU_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */ + IPU_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */ + IPU_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */ + IPU_FW_ISYS_FRAME_FORMAT_RGB565, /* 16 bit RGB, 1 plane. Each 3 sub + * pixels are packed into one 16 bit + * value, 5 bits for R, 6 bits + * for G and 5 bits for B. + */ + + IPU_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ + IPU_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, + * A=Alpha (alpha is unused) + */ + IPU_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */ + IPU_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */ + N_IPU_FW_ISYS_FRAME_FORMAT +}; + +/* Temporary for driver compatibility */ +#define IPU_FW_ISYS_FRAME_FORMAT_RAW (IPU_FW_ISYS_FRAME_FORMAT_RAW16) + +/** + * Supported MIPI data type. Keep in sync array in ipu_fw_isys_private.c + */ +enum ipu_fw_isys_mipi_data_type { + /** SYNCHRONIZATION SHORT PACKET DATA TYPES */ + IPU_FW_ISYS_MIPI_DATA_TYPE_FRAME_START_CODE = 0x00, + IPU_FW_ISYS_MIPI_DATA_TYPE_FRAME_END_CODE = 0x01, + IPU_FW_ISYS_MIPI_DATA_TYPE_LINE_START_CODE = 0x02, /* Optional */ + IPU_FW_ISYS_MIPI_DATA_TYPE_LINE_END_CODE = 0x03, /* Optional */ + /** Reserved 0x04-0x07 */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x04 = 0x04, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x05 = 0x05, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x06 = 0x06, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x07 = 0x07, + /** GENERIC SHORT PACKET DATA TYPES */ + /** They are used to keep the timing information for + * the opening/closing of shutters, + * triggering of flashes and etc. + */ + /* Generic Short Packet Codes 1 - 8 */ + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT1 = 0x08, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT2 = 0x09, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT3 = 0x0A, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT4 = 0x0B, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT5 = 0x0C, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT6 = 0x0D, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT7 = 0x0E, + IPU_FW_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT8 = 0x0F, + /** GENERIC LONG PACKET DATA TYPES */ + IPU_FW_ISYS_MIPI_DATA_TYPE_NULL = 0x10, + IPU_FW_ISYS_MIPI_DATA_TYPE_BLANKING_DATA = 0x11, + /* Embedded 8-bit non Image Data */ + IPU_FW_ISYS_MIPI_DATA_TYPE_EMBEDDED = 0x12, + /** Reserved 0x13-0x17 */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x13 = 0x13, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x14 = 0x14, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x15 = 0x15, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x16 = 0x16, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x17 = 0x17, + /** YUV DATA TYPES */ + /* 8 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8 = 0x18, + /* 10 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_10 = 0x19, + /* 8 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8_LEGACY = 0x1A, + /** Reserved 0x1B */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x1B = 0x1B, + /* YUV420 8-bit Chroma Shifted Pixel Sampling) */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_8_SHIFT = 0x1C, + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV420_10_SHIFT = 0x1D, + /* UYVY..UVYV, 8 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV422_8 = 0x1E, + /* UYVY..UVYV, 10 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_YUV422_10 = 0x1F, + /** RGB DATA TYPES */ + /* BGR..BGR, 4 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_444 = 0x20, + /* BGR..BGR, 5 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_555 = 0x21, + /* BGR..BGR, 5 bits B and R, 6 bits G */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_565 = 0x22, + /* BGR..BGR, 6 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_666 = 0x23, + /* BGR..BGR, 8 bits per subpixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RGB_888 = 0x24, + /** Reserved 0x25-0x27 */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x25 = 0x25, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x26 = 0x26, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x27 = 0x27, + /** RAW DATA TYPES */ + /* RAW data, 6 - 14 bits per pixel */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_6 = 0x28, + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_7 = 0x29, + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_8 = 0x2A, + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_10 = 0x2B, + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_12 = 0x2C, + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_14 = 0x2D, + /** Reserved 0x2E-2F are used with assigned meaning */ + /* RAW data, 16 bits per pixel, not specified in CSI-MIPI standard */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RAW_16 = 0x2E, + /* Binary byte stream, which is target at JPEG, + * not specified in CSI-MIPI standard + */ + IPU_FW_ISYS_MIPI_DATA_TYPE_BINARY_8 = 0x2F, + + /** USER DEFINED 8-BIT DATA TYPES */ + /** For example, the data transmitter (e.g. the SoC sensor) + * can keep the JPEG data as + * the User Defined Data Type 4 and the MPEG data as the + * User Defined Data Type 7. + */ + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF1 = 0x30, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF2 = 0x31, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF3 = 0x32, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF4 = 0x33, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF5 = 0x34, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF6 = 0x35, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF7 = 0x36, + IPU_FW_ISYS_MIPI_DATA_TYPE_USER_DEF8 = 0x37, + /** Reserved 0x38-0x3F */ + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x38 = 0x38, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x39 = 0x39, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3A = 0x3A, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3B = 0x3B, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3C = 0x3C, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3D = 0x3D, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3E = 0x3E, + IPU_FW_ISYS_MIPI_DATA_TYPE_RESERVED_0x3F = 0x3F, + + /* Keep always last and max value */ + N_IPU_FW_ISYS_MIPI_DATA_TYPE = 0x40 +}; + +/** enum ipu_fw_isys_pin_type: output pin buffer types. + * Buffers can be queued and de-queued to hand them over between IA and ISYS + */ +enum ipu_fw_isys_pin_type { + /* Captured as MIPI packets */ + IPU_FW_ISYS_PIN_TYPE_MIPI = 0, + /* Captured through the ISApf (with/without ISA) + * and the non-scaled output path + */ + IPU_FW_ISYS_PIN_TYPE_RAW_NS, + /* Captured through the ISApf + ISA and the scaled output path */ + IPU_FW_ISYS_PIN_TYPE_RAW_S, + /* Captured through the SoC path */ + IPU_FW_ISYS_PIN_TYPE_RAW_SOC, + /* Reserved for future use, maybe short packets */ + IPU_FW_ISYS_PIN_TYPE_METADATA_0, + /* Reserved for future use */ + IPU_FW_ISYS_PIN_TYPE_METADATA_1, + /* Legacy (non-PIV2), used for the AWB stats */ + IPU_FW_ISYS_PIN_TYPE_AWB_STATS, + /* Legacy (non-PIV2), used for the AF stats */ + IPU_FW_ISYS_PIN_TYPE_AF_STATS, + /* Legacy (non-PIV2), used for the AE stats */ + IPU_FW_ISYS_PIN_TYPE_HIST_STATS, + /* Used for the PAF FF */ + IPU_FW_ISYS_PIN_TYPE_PAF_FF, +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + /* Captured through the SoC path + * (2D mode where odd and even lines are handled separately) + */ + IPU_FW_ISYS_PIN_TYPE_RAW_DUAL_SOC, +#endif + /* Keep always last and max value */ + N_IPU_FW_ISYS_PIN_TYPE +}; + +/** + * enum ipu_fw_isys_isl_use + * Describes the ISL/ISA use + */ +enum ipu_fw_isys_isl_use { + IPU_FW_ISYS_USE_NO_ISL_NO_ISA = 0, + IPU_FW_ISYS_USE_SINGLE_DUAL_ISL, + IPU_FW_ISYS_USE_SINGLE_ISA, + N_IPU_FW_ISYS_USE +}; + +/** + * enum ipu_fw_isys_mipi_store_mode. Describes if long MIPI packets reach + * MIPI SRAM with the long packet header or + * if not, then only option is to capture it with pin type MIPI. + */ +enum ipu_fw_isys_mipi_store_mode { + IPU_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0, + IPU_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER, + N_IPU_FW_ISYS_MIPI_STORE_MODE +}; + +/** + * enum ipu_fw_isys_type_paf. Describes the Type of PAF enabled + */ +enum ipu_fw_isys_type_paf { + /* PAF data not present */ + IPU_FW_ISYS_TYPE_NO_PAF = 0, + /* Type 2 sensor types, PAF coming separately from Image Frame */ + /* PAF data in interleaved format(RLRL or LRLR) */ + IPU_FW_ISYS_TYPE_INTERLEAVED_PAF, + /* PAF data in non-interleaved format(LL/RR or RR/LL) */ + IPU_FW_ISYS_TYPE_NON_INTERLEAVED_PAF, + /* Type 3 sensor types , PAF data embedded in Image Frame */ + /* Frame Embedded PAF in interleaved format(RLRL or LRLR) */ + IPU_FW_ISYS_TYPE_FRAME_EMB_INTERLEAVED_PAF, + /* Frame Embedded PAF non-interleaved format(LL/RR or RR/LL) */ + IPU_FW_ISYS_TYPE_FRAME_EMB_NON_INTERLEAVED_PAF, + N_IPU_FW_ISYS_TYPE_PAF +}; + +/** + * enum ipu_fw_isys_cropping_location. Enumerates the cropping locations in ISYS + */ +enum ipu_fw_isys_cropping_location { + /* Cropping executed in ISAPF (mainly), + * ISAPF preproc (odd column) and MIPI STR2MMIO (odd row) + */ + IPU_FW_ISYS_CROPPING_LOCATION_PRE_ISA = 0, + /* Reserved for legacy mode which will never be implemented */ + IPU_FW_ISYS_CROPPING_LOCATION_RESERVED_1, + /* Cropping executed in StreamPifConv in the ISA output for + * RAW_NS pin + */ + IPU_FW_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED, + /* Cropping executed in StreamScaledPifConv + * in the ISA output for RAW_S pin + */ + IPU_FW_ISYS_CROPPING_LOCATION_POST_ISA_SCALED, + N_IPU_FW_ISYS_CROPPING_LOCATION +}; + +/** + * enum ipu_fw_isys_resolution_info. Describes the resolution, + * required to setup the various ISA GP registers. + */ +enum ipu_fw_isys_resolution_info { + /* Scaled ISA output resolution before + * the StreamScaledPifConv cropping + */ + IPU_FW_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED = 0, + /* Non-Scaled ISA output resolution before the StreamPifConv cropping */ + IPU_FW_ISYS_RESOLUTION_INFO_POST_ISA_SCALED, + N_IPU_FW_ISYS_RESOLUTION_INFO +}; + +/** + * enum ipu_fw_isys_error. Describes the error type detected by the FW + */ +enum ipu_fw_isys_error { + IPU_FW_ISYS_ERROR_NONE = 0, /* No details */ + IPU_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY, /* enum */ + IPU_FW_ISYS_ERROR_HW_CONSISTENCY, /* enum */ + IPU_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE, /* enum */ + IPU_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION, /* enum */ + IPU_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION, /* enum */ + IPU_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION, /* enum */ + IPU_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES, /* enum */ + IPU_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO, /* HW code */ + IPU_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO, /* HW code */ + IPU_FW_ISYS_ERROR_SENSOR_FW_SYNC, /* enum */ + IPU_FW_ISYS_ERROR_STREAM_IN_SUSPENSION, /* FW code */ + IPU_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL, /* FW code */ + N_IPU_FW_ISYS_ERROR +}; + +/** + * enum ipu_fw_proxy_error. Describes the error type for + * the proxy detected by the FW + */ +enum ipu_fw_proxy_error { + IPU_FW_PROXY_ERROR_NONE = 0, + IPU_FW_PROXY_ERROR_INVALID_WRITE_REGION, + IPU_FW_PROXY_ERROR_INVALID_WRITE_OFFSET, + N_IPU_FW_PROXY_ERROR +}; + +struct ipu_isys; + +/** + * struct ipu_fw_isys_buffer_partition_abi - buffer partition information + * @num_gda_pages: Number of virtual gda pages available for each virtual stream + */ +struct ipu_fw_isys_buffer_partition_abi { + u32 num_gda_pages[IPU_STREAM_ID_MAX]; +}; + +/** + * struct ipu_fw_isys_fw_config - contains the parts from + * ia_css_isys_device_cfg_data we need to transfer to the cell + */ +struct ipu_fw_isys_fw_config { + struct ipu_fw_isys_buffer_partition_abi buffer_partition; + u32 num_send_queues[N_IPU_FW_ISYS_QUEUE_TYPE]; + u32 num_recv_queues[N_IPU_FW_ISYS_QUEUE_TYPE]; +}; + +/** + * struct ipu_fw_isys_resolution_abi: Generic resolution structure. + * @Width + * @Height + */ +struct ipu_fw_isys_resolution_abi { + u32 width; + u32 height; +}; + +/** + * struct ipu_fw_isys_output_pin_payload_abi + * @out_buf_id: Points to output pin buffer - buffer identifier + * @addr: Points to output pin buffer - CSS Virtual Address + * @compress: Request frame compression (1), or not (0) + */ +struct ipu_fw_isys_output_pin_payload_abi { + u64 out_buf_id; + u32 addr; + u32 compress; +}; + +/** + * struct ipu_fw_isys_output_pin_info_abi + * @output_res: output pin resolution + * @stride: output stride in Bytes (not valid for statistics) + * @watermark_in_lines: pin watermark level in lines + * @payload_buf_size: minimum size in Bytes of all buffers that will be + * supplied for capture on this pin + * @send_irq: assert if pin event should trigger irq + * @pt: pin type -real format "enum ipu_fw_isys_pin_type" + * @ft: frame format type -real format "enum ipu_fw_isys_frame_format_type" + * @input_pin_id: related input pin id + * @reserve_compression: reserve compression resources for pin + */ +struct ipu_fw_isys_output_pin_info_abi { + struct ipu_fw_isys_resolution_abi output_res; + u32 stride; + u32 watermark_in_lines; + u32 payload_buf_size; + u8 send_irq; + u8 input_pin_id; + u8 pt; + u8 ft; + u8 reserved; + u8 reserve_compression; +}; + +/** + * struct ipu_fw_isys_param_pin_abi + * @param_buf_id: Points to param port buffer - buffer identifier + * @addr: Points to param pin buffer - CSS Virtual Address + */ +struct ipu_fw_isys_param_pin_abi { + u64 param_buf_id; + u32 addr; +}; + +/** + * struct ipu_fw_isys_input_pin_info_abi + * @input_res: input resolution + * @dt: mipi data type ((enum ipu_fw_isys_mipi_data_type) + * @mipi_store_mode: defines if legacy long packet header will be stored or + * discarded if discarded, output pin pin type for this + * input pin can only be MIPI + * (enum ipu_fw_isys_mipi_store_mode) + * @bits_per_pix: native bits per pixel + * @mapped_dt: actual data type from sensor +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + * @crop_first_and_last_lines Control whether to crop the + * first and last line of the + * input image. Crop done by HW + * device. +#endif + */ +struct ipu_fw_isys_input_pin_info_abi { + struct ipu_fw_isys_resolution_abi input_res; + u8 dt; + u8 mipi_store_mode; + u8 bits_per_pix; + u8 mapped_dt; +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + u8 crop_first_and_last_lines; +#endif +}; + +/** + * struct ipu_fw_isys_isa_cfg_abi. Describes the ISA cfg + */ +struct ipu_fw_isys_isa_cfg_abi { + struct ipu_fw_isys_resolution_abi + isa_res[N_IPU_FW_ISYS_RESOLUTION_INFO]; + struct { + unsigned int blc:1; + unsigned int lsc:1; + unsigned int dpc:1; + unsigned int downscaler:1; + unsigned int awb:1; + unsigned int af:1; + unsigned int ae:1; + unsigned int paf:8; + unsigned int send_irq_stats_ready:1; + unsigned int send_resp_stats_ready:1; + } cfg; +}; + +/** + * struct ipu_fw_isys_cropping_abi - cropping coordinates + */ +struct ipu_fw_isys_cropping_abi { + s32 top_offset; + s32 left_offset; + s32 bottom_offset; + s32 right_offset; +}; + +/** + * struct ipu_fw_isys_stream_cfg_data_abi + * ISYS stream configuration data structure + * @isa_cfg: details about what ACCs are active if ISA is used + * @crop: defines cropping resolution for the + * maximum number of input pins which can be cropped, + * it is directly mapped to the HW devices + * @input_pins: input pin descriptors + * @output_pins: output pin descriptors + * @compfmt: de-compression setting for User Defined Data + * @nof_input_pins: number of input pins + * @nof_output_pins: number of output pins + * @send_irq_sof_discarded: send irq on discarded frame sof response + * - if '1' it will override the send_resp_sof_discarded + * and send the response + * - if '0' the send_resp_sof_discarded will determine + * whether to send the response + * @send_irq_eof_discarded: send irq on discarded frame eof response + * - if '1' it will override the send_resp_eof_discarded + * and send the response + * - if '0' the send_resp_eof_discarded will determine + * whether to send the response + * @send_resp_sof_discarded: send response for discarded frame sof detected, + * used only when send_irq_sof_discarded is '0' + * @send_resp_eof_discarded: send response for discarded frame eof detected, + * used only when send_irq_eof_discarded is '0' + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) + * @isl_use: indicates whether stream requires ISL and how + */ +struct ipu_fw_isys_stream_cfg_data_abi { + struct ipu_fw_isys_isa_cfg_abi isa_cfg; + struct ipu_fw_isys_cropping_abi crop[N_IPU_FW_ISYS_CROPPING_LOCATION]; + struct ipu_fw_isys_input_pin_info_abi input_pins[IPU_MAX_IPINS]; + struct ipu_fw_isys_output_pin_info_abi output_pins[IPU_MAX_OPINS]; + u32 compfmt; + u8 nof_input_pins; + u8 nof_output_pins; + u8 send_irq_sof_discarded; + u8 send_irq_eof_discarded; + u8 send_resp_sof_discarded; + u8 send_resp_eof_discarded; + u8 src; + u8 vc; + u8 isl_use; +}; + +/** + * struct ipu_fw_isys_frame_buff_set - frame buffer set + * @output_pins: output pin addresses + * @process_group_light: process_group_light buffer address + * @send_irq_sof: send irq on frame sof response + * - if '1' it will override the send_resp_sof and + * send the response + * - if '0' the send_resp_sof will determine whether to + * send the response + * @send_irq_eof: send irq on frame eof response + * - if '1' it will override the send_resp_eof and + * send the response + * - if '0' the send_resp_eof will determine whether to + * send the response + * @send_resp_sof: send response for frame sof detected, + * used only when send_irq_sof is '0' + * @send_resp_eof: send response for frame eof detected, + * used only when send_irq_eof is '0' + */ +struct ipu_fw_isys_frame_buff_set_abi { + struct ipu_fw_isys_output_pin_payload_abi output_pins[IPU_MAX_OPINS]; + struct ipu_fw_isys_param_pin_abi process_group_light; + u8 send_irq_sof; + u8 send_irq_eof; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + u8 send_irq_capture_ack; + u8 send_irq_capture_done; +#endif + u8 send_resp_sof; + u8 send_resp_eof; + u8 reserved; +}; + +/** + * struct ipu_fw_isys_error_info_abi + * @error: error code if something went wrong + * @error_details: depending on error code, it may contain additional error info + */ +struct ipu_fw_isys_error_info_abi { + enum ipu_fw_isys_error error; + u32 error_details; +}; + +/** + * struct ipu_fw_isys_resp_info_comm + * @pin: this var is only valid for pin event related responses, + * contains pin addresses + * @process_group_light: this var is valid for stats ready related responses, + * contains process group addresses + * @error_info: error information from the FW + * @timestamp: Time information for event if available + * @stream_handle: stream id the response corresponds to + * @type: response type (enum ipu_fw_isys_resp_type) + * @pin_id: pin id that the pin payload corresponds to + * @acc_id: this var is valid for stats ready related responses, + * contains accelerator id that finished producing + * all related statistics + */ +struct ipu_fw_isys_resp_info_abi { + u64 buf_id; + struct ipu_fw_isys_output_pin_payload_abi pin; + struct ipu_fw_isys_param_pin_abi process_group_light; + struct ipu_fw_isys_error_info_abi error_info; + u32 timestamp[2]; + u8 stream_handle; + u8 type; + u8 pin_id; + u8 acc_id; + u16 reserved; +}; + +/** + * struct ipu_fw_isys_proxy_error_info_comm + * @proxy_error: error code if something went wrong + * @proxy_error_details: depending on error code, it may contain additional + * error info + */ +struct ipu_fw_isys_proxy_error_info_abi { + enum ipu_fw_proxy_error error; + u32 error_details; +}; + +struct ipu_fw_isys_proxy_resp_info_abi { + u32 request_id; + struct ipu_fw_isys_proxy_error_info_abi error_info; +}; + +/** + * struct ipu_fw_proxy_write_queue_token + * @request_id: update id for the specific proxy write request + * @region_index: Region id for the proxy write request + * @offset: Offset of the write request according to the base address + * of the region + * @value: Value that is requested to be written with the proxy write request + */ +struct ipu_fw_proxy_write_queue_token { + u32 request_id; + u32 region_index; + u32 offset; + u32 value; +}; + +/* From here on type defines not coming from the ISYSAPI interface */ + +/** + * struct ipu_fw_resp_queue_token + */ +struct ipu_fw_resp_queue_token { + struct ipu_fw_isys_resp_info_abi resp_info; +}; + +/** + * struct ipu_fw_send_queue_token + */ +struct ipu_fw_send_queue_token { + u64 buf_handle; + u32 payload; + u16 send_type; + u16 stream_id; +}; + +/** + * struct ipu_fw_proxy_resp_queue_token + */ +struct ipu_fw_proxy_resp_queue_token { + struct ipu_fw_isys_proxy_resp_info_abi proxy_resp_info; +}; + +/** + * struct ipu_fw_proxy_send_queue_token + */ +struct ipu_fw_proxy_send_queue_token { + u32 request_id; + u32 region_index; + u32 offset; + u32 value; +}; + +void ipu_fw_isys_set_params(struct ipu_fw_isys_stream_cfg_data_abi *stream_cfg); + +void ipu_fw_isys_dump_stream_cfg(struct device *dev, + struct ipu_fw_isys_stream_cfg_data_abi + *stream_cfg); +void ipu_fw_isys_dump_frame_buff_set(struct device *dev, + struct ipu_fw_isys_frame_buff_set_abi *buf, + unsigned int outputs); +int ipu_fw_isys_init(struct ipu_isys *isys, unsigned int num_streams); +int ipu_fw_isys_close(struct ipu_isys *isys); +int ipu_fw_isys_simple_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + enum ipu_fw_isys_send_type send_type); +int ipu_fw_isys_complex_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, enum ipu_fw_isys_send_type send_type); +int ipu_fw_isys_send_proxy_token(struct ipu_isys *isys, + unsigned int req_id, + unsigned int index, + unsigned int offset, u32 value); +void ipu_fw_isys_cleanup(struct ipu_isys *isys); +struct ipu_fw_isys_resp_info_abi *ipu_fw_isys_get_resp(void *context, + unsigned int queue, + struct + ipu_fw_isys_resp_info_abi + *response); +void ipu_fw_isys_put_resp(void *context, unsigned int queue); +#endif diff --git a/drivers/media/pci/intel/ipu-fw-psys.c b/drivers/media/pci/intel/ipu-fw-psys.c new file mode 100644 index 000000000000..5f0f601170ac --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-psys.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2016 - 2018 Intel Corporation + +#include + +#include + +#include "ipu-fw-com.h" +#include "ipu-fw-psys.h" +#include "ipu-psys.h" + +int ipu_fw_psys_pg_start(struct ipu_psys_kcmd *kcmd) +{ + kcmd->kpg->pg->state = IPU_FW_PSYS_PROCESS_GROUP_STARTED; + return 0; +} + +int ipu_fw_psys_pg_load_cycles(struct ipu_psys_kcmd *kcmd) +{ + return 0; +} + +int ipu_fw_psys_pg_init_cycles(struct ipu_psys_kcmd *kcmd) +{ + return 0; +} + +int ipu_fw_psys_pg_processing_cycles(struct ipu_psys_kcmd *kcmd) +{ + return 0; +} + +int ipu_fw_psys_pg_disown(struct ipu_psys_kcmd *kcmd) +{ + struct ipu_fw_psys_cmd *psys_cmd; + int ret = 0; + + psys_cmd = ipu_send_get_token(kcmd->fh->psys->fwcom, 0); + if (!psys_cmd) { + dev_err(&kcmd->fh->psys->adev->dev, "failed to get token!\n"); + kcmd->pg_user = NULL; + ret = -ENODATA; + goto out; + } + psys_cmd->command = IPU_FW_PSYS_PROCESS_GROUP_CMD_START; + psys_cmd->msg = 0; + psys_cmd->context_handle = kcmd->kpg->pg->ipu_virtual_address; + ipu_send_put_token(kcmd->fh->psys->fwcom, 0); + +out: + return ret; +} + + +int ipu_fw_psys_pg_abort(struct ipu_psys_kcmd *kcmd) +{ + struct ipu_fw_psys_cmd *psys_cmd; + int ret = 0; + + psys_cmd = ipu_send_get_token(kcmd->fh->psys->fwcom, 0); + if (!psys_cmd) { + dev_err(&kcmd->fh->psys->adev->dev, "failed to get token!\n"); + kcmd->pg_user = NULL; + ret = -ENODATA; + goto out; + } + psys_cmd->command = IPU_FW_PSYS_PROCESS_GROUP_CMD_STOP; + psys_cmd->msg = 0; + psys_cmd->context_handle = kcmd->kpg->pg->ipu_virtual_address; + ipu_send_put_token(kcmd->fh->psys->fwcom, 0); + +out: + return ret; +} + +int ipu_fw_psys_pg_submit(struct ipu_psys_kcmd *kcmd) +{ + kcmd->kpg->pg->state = IPU_FW_PSYS_PROCESS_GROUP_BLOCKED; + return 0; +} + +int ipu_fw_psys_rcv_event(struct ipu_psys *psys, + struct ipu_fw_psys_event *event) +{ + void *rcv; + + rcv = ipu_recv_get_token(psys->fwcom, 0); + if (!rcv) + return 0; + + memcpy(event, rcv, sizeof(*event)); + ipu_recv_put_token(psys->fwcom, 0); + return 1; +} + +int ipu_fw_psys_terminal_set(struct ipu_fw_psys_terminal *terminal, + int terminal_idx, + struct ipu_psys_kcmd *kcmd, + u32 buffer, unsigned int size) +{ + u32 type; + u32 buffer_state; + + type = terminal->terminal_type; + + switch (type) { + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SLICED_OUT: + case IPU_FW_PSYS_TERMINAL_TYPE_PROGRAM: + buffer_state = IPU_FW_PSYS_BUFFER_UNDEFINED; + break; + case IPU_FW_PSYS_TERMINAL_TYPE_PARAM_STREAM: + case IPU_FW_PSYS_TERMINAL_TYPE_DATA_IN: + case IPU_FW_PSYS_TERMINAL_TYPE_STATE_IN: + buffer_state = IPU_FW_PSYS_BUFFER_FULL; + break; + case IPU_FW_PSYS_TERMINAL_TYPE_DATA_OUT: + case IPU_FW_PSYS_TERMINAL_TYPE_STATE_OUT: + buffer_state = IPU_FW_PSYS_BUFFER_EMPTY; + break; + default: + dev_err(&kcmd->fh->psys->adev->dev, + "unknown terminal type: 0x%x\n", type); + return -EAGAIN; + } + + if (type == IPU_FW_PSYS_TERMINAL_TYPE_DATA_IN || + type == IPU_FW_PSYS_TERMINAL_TYPE_DATA_OUT) { + struct ipu_fw_psys_data_terminal *dterminal = + (struct ipu_fw_psys_data_terminal *)terminal; + dterminal->connection_type = IPU_FW_PSYS_CONNECTION_MEMORY; + dterminal->frame.data_bytes = size; + if (!ipu_fw_psys_pg_get_protocol(kcmd)) + dterminal->frame.data = buffer; + else + dterminal->frame.data_index = terminal_idx; + dterminal->frame.buffer_state = buffer_state; + } else { + struct ipu_fw_psys_param_terminal *pterminal = + (struct ipu_fw_psys_param_terminal *)terminal; + if (!ipu_fw_psys_pg_get_protocol(kcmd)) + pterminal->param_payload.buffer = buffer; + else + pterminal->param_payload.terminal_index = terminal_idx; + } + return 0; +} + +static int process_get_cell(struct ipu_fw_psys_process *process, int index) +{ + int cell; + +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + cell = process->cell_id; +#else + cell = process->cells[index]; +#endif + return cell; +} + +static u32 process_get_cells_bitmap(struct ipu_fw_psys_process *process) +{ + unsigned int i; + int cell_id; + u32 bitmap = 0; + + for (i = 0; i < IPU_FW_PSYS_PROCESS_MAX_CELLS; i++) { + cell_id = process_get_cell(process, i); + if (cell_id != IPU_FW_PSYS_N_CELL_ID) + bitmap |= (1 << cell_id); + } + return bitmap; +} + +void ipu_fw_psys_pg_dump(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd, const char *note) +{ + struct ipu_fw_psys_process_group *pg = kcmd->kpg->pg; + u32 pgid = pg->ID; + u8 processes = pg->process_count; + u16 *process_offset_table = (u16 *)((char *)pg + pg->processes_offset); + unsigned int p, chn, mem, mem_id; + int cell; + + dev_dbg(&psys->adev->dev, "%s %s pgid %i has %i processes:\n", + __func__, note, pgid, processes); + + for (p = 0; p < processes; p++) { + struct ipu_fw_psys_process *process = + (struct ipu_fw_psys_process *) + ((char *)pg + process_offset_table[p]); + cell = process_get_cell(process, 0); + dev_dbg(&psys->adev->dev, "\t process %i size=%u", + p, process->size); + dev_dbg(&psys->adev->dev, + "\t cell %i cell_bitmap=0x%x kernel_bitmap 0x%llx", + cell, process_get_cells_bitmap(process), + (u64) process->kernel_bitmap[1] << 32 | + (u64) process->kernel_bitmap[0]); + for (mem = 0; mem < IPU_FW_PSYS_N_DATA_MEM_TYPE_ID; mem++) { + mem_id = process->ext_mem_id[mem]; + if (mem_id != IPU_FW_PSYS_N_MEM_ID) + dev_dbg(&psys->adev->dev, + "\t mem type %u id %d offset=0x%x", + mem, mem_id, + process->ext_mem_offset[mem]); + } + for (chn = 0; chn < IPU_FW_PSYS_N_DEV_CHN_ID; chn++) { + if (process->dev_chn_offset[chn] != (u16)(-1)) + dev_dbg(&psys->adev->dev, + "\t dev_chn[%u]=0x%x\n", + chn, process->dev_chn_offset[chn]); + } + } +} + +int ipu_fw_psys_pg_get_id(struct ipu_psys_kcmd *kcmd) +{ + return kcmd->kpg->pg->ID; +} + +int ipu_fw_psys_pg_get_terminal_count(struct ipu_psys_kcmd *kcmd) +{ + return kcmd->kpg->pg->terminal_count; +} + +int ipu_fw_psys_pg_get_size(struct ipu_psys_kcmd *kcmd) +{ + return kcmd->kpg->pg->size; +} + +int ipu_fw_psys_pg_set_ipu_vaddress(struct ipu_psys_kcmd *kcmd, + dma_addr_t vaddress) +{ + kcmd->kpg->pg->ipu_virtual_address = vaddress; + return 0; +} + +struct ipu_fw_psys_terminal *ipu_fw_psys_pg_get_terminal(struct ipu_psys_kcmd + *kcmd, int index) +{ + struct ipu_fw_psys_terminal *terminal; + u16 *terminal_offset_table; + + terminal_offset_table = + (uint16_t *)((char *)kcmd->kpg->pg + + kcmd->kpg->pg->terminals_offset); + terminal = (struct ipu_fw_psys_terminal *) + ((char *)kcmd->kpg->pg + terminal_offset_table[index]); + return terminal; +} + +void ipu_fw_psys_pg_set_token(struct ipu_psys_kcmd *kcmd, u64 token) +{ + kcmd->kpg->pg->token = (u64)token; +} + +u64 ipu_fw_psys_pg_get_token(struct ipu_psys_kcmd *kcmd) +{ + return kcmd->kpg->pg->token; +} + +int ipu_fw_psys_pg_get_protocol(struct ipu_psys_kcmd *kcmd) +{ + return kcmd->kpg->pg->protocol_version; +} + + +int ipu_fw_psys_open(struct ipu_psys *psys) +{ + int retry = IPU_PSYS_OPEN_RETRY, retval; + + retval = ipu_fw_com_open(psys->fwcom); + if (retval) { + dev_err(&psys->adev->dev, "fw com open failed.\n"); + return retval; + } + + do { + usleep_range(IPU_PSYS_OPEN_TIMEOUT_US, + IPU_PSYS_OPEN_TIMEOUT_US + 10); + retval = ipu_fw_com_ready(psys->fwcom); + if (!retval) { + dev_dbg(&psys->adev->dev, "psys port open ready!\n"); + break; + } + } while (retry-- > 0); + + if (!retry && retval) { + dev_err(&psys->adev->dev, "psys port open ready failed %d\n", + retval); + ipu_fw_com_close(psys->fwcom); + return retval; + } + return 0; +} + +int ipu_fw_psys_close(struct ipu_psys *psys) +{ + int retval; + + retval = ipu_fw_com_close(psys->fwcom); + if (retval) { + dev_err(&psys->adev->dev, "fw com close failed.\n"); + return retval; + } + return retval; +} diff --git a/drivers/media/pci/intel/ipu-fw-psys.h b/drivers/media/pci/intel/ipu-fw-psys.h new file mode 100644 index 000000000000..1738f6cec768 --- /dev/null +++ b/drivers/media/pci/intel/ipu-fw-psys.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef IPU_FW_PSYS_H +#define IPU_FW_PSYS_H + +#include "ipu-platform-resources.h" + +/* ia_css_psys_device.c */ +#define IPU_FW_PSYS_CMD_QUEUE_SIZE 0x20 +#define IPU_FW_PSYS_EVENT_QUEUE_SIZE 0x40 + +/* ia_css_psys_transport.h */ +#define IPU_FW_PSYS_CMD_BITS 64 +#define IPU_FW_PSYS_EVENT_BITS 128 + +/* ia_css_psys_transport.h */ +enum { + IPU_FW_PSYS_EVENT_TYPE_SUCCESS = 0, + IPU_FW_PSYS_EVENT_TYPE_UNKNOWN_ERROR = 1, + IPU_FW_PSYS_EVENT_TYPE_RET_REM_OBJ_NOT_FOUND = 2, + IPU_FW_PSYS_EVENT_TYPE_RET_REM_OBJ_TOO_BIG = 3, + IPU_FW_PSYS_EVENT_TYPE_RET_REM_OBJ_DDR_TRANS_ERR = 4, + IPU_FW_PSYS_EVENT_TYPE_RET_REM_OBJ_NULL_PKG_DIR_ADDR = 5, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAME_ERR = 6, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAGMENT_ERR = 7, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_COUNT_ZERO = 8, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_INIT_ERR = 9, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_ABORT = 10, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_NULL = 11, + IPU_FW_PSYS_EVENT_TYPE_PROC_GRP_VALIDATION_ERR = 12 +}; + +enum { + IPU_FW_PSYS_EVENT_QUEUE_MAIN_ID, + IPU_FW_PSYS_N_PSYS_EVENT_QUEUE_ID +}; + +enum { + IPU_FW_PSYS_PROCESS_GROUP_ERROR = 0, + IPU_FW_PSYS_PROCESS_GROUP_CREATED, + IPU_FW_PSYS_PROCESS_GROUP_READY, + IPU_FW_PSYS_PROCESS_GROUP_BLOCKED, + IPU_FW_PSYS_PROCESS_GROUP_STARTED, + IPU_FW_PSYS_PROCESS_GROUP_RUNNING, + IPU_FW_PSYS_PROCESS_GROUP_STALLED, + IPU_FW_PSYS_PROCESS_GROUP_STOPPED, + IPU_FW_PSYS_N_PROCESS_GROUP_STATES +}; + +enum { + IPU_FW_PSYS_CONNECTION_MEMORY = 0, + IPU_FW_PSYS_CONNECTION_MEMORY_STREAM, + IPU_FW_PSYS_CONNECTION_STREAM, + IPU_FW_PSYS_N_CONNECTION_TYPES +}; + +enum { + IPU_FW_PSYS_BUFFER_NULL = 0, + IPU_FW_PSYS_BUFFER_UNDEFINED, + IPU_FW_PSYS_BUFFER_EMPTY, + IPU_FW_PSYS_BUFFER_NONEMPTY, + IPU_FW_PSYS_BUFFER_FULL, + IPU_FW_PSYS_N_BUFFER_STATES +}; + +enum { + IPU_FW_PSYS_TERMINAL_TYPE_DATA_IN = 0, + IPU_FW_PSYS_TERMINAL_TYPE_DATA_OUT, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_STREAM, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_CACHED_IN, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_CACHED_OUT, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SPATIAL_IN, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SLICED_IN, + IPU_FW_PSYS_TERMINAL_TYPE_PARAM_SLICED_OUT, + IPU_FW_PSYS_TERMINAL_TYPE_STATE_IN, + IPU_FW_PSYS_TERMINAL_TYPE_STATE_OUT, + IPU_FW_PSYS_TERMINAL_TYPE_PROGRAM, + IPU_FW_PSYS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT, + IPU_FW_PSYS_N_TERMINAL_TYPES +}; + +enum { + IPU_FW_PSYS_COL_DIMENSION = 0, + IPU_FW_PSYS_ROW_DIMENSION = 1, + IPU_FW_PSYS_N_DATA_DIMENSION = 2 +}; + +enum { + IPU_FW_PSYS_PROCESS_GROUP_CMD_NOP = 0, + IPU_FW_PSYS_PROCESS_GROUP_CMD_SUBMIT, + IPU_FW_PSYS_PROCESS_GROUP_CMD_ATTACH, + IPU_FW_PSYS_PROCESS_GROUP_CMD_DETACH, + IPU_FW_PSYS_PROCESS_GROUP_CMD_START, + IPU_FW_PSYS_PROCESS_GROUP_CMD_DISOWN, + IPU_FW_PSYS_PROCESS_GROUP_CMD_RUN, + IPU_FW_PSYS_PROCESS_GROUP_CMD_STOP, + IPU_FW_PSYS_PROCESS_GROUP_CMD_SUSPEND, + IPU_FW_PSYS_PROCESS_GROUP_CMD_RESUME, + IPU_FW_PSYS_PROCESS_GROUP_CMD_ABORT, + IPU_FW_PSYS_PROCESS_GROUP_CMD_RESET, + IPU_FW_PSYS_N_PROCESS_GROUP_CMDS +}; + +enum { + IPU_FW_PSYS_PROCESS_GROUP_PROTOCOL_LEGACY = 0, + IPU_FW_PSYS_PROCESS_GROUP_N_PROTOCOLS +}; + +/* ia_css_psys_process_group_cmd_impl.h */ +struct __packed ipu_fw_psys_process_group { + u64 token; + u64 private_token; + u32 routing_bitmap[IPU_FW_PSYS_RBM_NOF_ELEMS]; + u32 size; + u32 pg_load_start_ts; + u32 pg_load_cycles; + u32 pg_init_cycles; + u32 pg_processing_cycles; + u32 ID; + u32 state; + u32 ipu_virtual_address; + u32 resource_bitmap; + u16 fragment_count; + u16 fragment_state; + u16 fragment_limit; + u16 processes_offset; + u16 terminals_offset; + u8 process_count; + u8 terminal_count; + u8 subgraph_count; + u8 protocol_version; + u8 base_queue_id; + u8 num_queues; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT]; +}; + +/* ia_css_psys_init.h */ +struct ipu_fw_psys_srv_init { + void *host_ddr_pkg_dir; + u32 ddr_pkg_dir_address; + u32 pkg_dir_size; + + u32 icache_prefetch_sp; + u32 icache_prefetch_isp; +}; + +/* ia_css_psys_transport.h */ +struct __packed ipu_fw_psys_cmd { + u16 command; + u16 msg; + u32 context_handle; +}; + +struct __packed ipu_fw_psys_event { + u16 status; + u16 command; + u32 context_handle; + u64 token; +}; + +/* ia_css_terminal_base_types.h */ +struct ipu_fw_psys_terminal { + u32 terminal_type; + s16 parent_offset; + u16 size; + u16 tm_index; + u8 ID; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_TERMINAL_STRUCT]; +}; + +/* ia_css_terminal_types.h */ +struct ipu_fw_psys_param_payload { + u64 host_buffer; + u32 buffer; + u32 terminal_index; +}; + +/* ia_css_program_group_param_types.h */ +struct ipu_fw_psys_param_terminal { + struct ipu_fw_psys_terminal base; + struct ipu_fw_psys_param_payload param_payload; + u16 param_section_desc_offset; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT]; +}; + +struct ipu_fw_psys_frame { + u32 buffer_state; + u32 access_type; + u32 pointer_state; + u32 access_scope; + u32 data; + u32 data_index; + u32 data_bytes; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_FRAME_STRUCT]; +}; + +/* ia_css_program_group_data.h */ +struct ipu_fw_psys_frame_descriptor { + u32 frame_format_type; + u32 plane_count; + u32 plane_offsets[IPU_FW_PSYS_N_FRAME_PLANES]; + u32 stride[1]; + u16 dimension[2]; + u16 size; + u8 bpp; + u8 bpe; + u8 is_compressed; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_FRAME_DESC_STRUCT]; +}; + +struct ipu_fw_psys_stream { + u64 dummy; +}; + +/* ia_css_psys_terminal_private_types.h */ +struct ipu_fw_psys_data_terminal { + struct ipu_fw_psys_terminal base; + struct ipu_fw_psys_frame_descriptor frame_descriptor; + struct ipu_fw_psys_frame frame; + struct ipu_fw_psys_stream stream; + u32 reserved; + u32 connection_type; + u16 fragment_descriptor_offset; + u8 kernel_id; + u8 subgraph_id; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT]; +}; + +/* ia_css_psys_buffer_set.h */ +struct ipu_fw_psys_buffer_set { + u64 token; + u32 kernel_enable_bitmap[IPU_FW_PSYS_KERNEL_BITMAP_NOF_ELEMS]; + u32 ipu_virtual_address; + u32 process_group_handle; + u16 terminal_count; + u8 frame_counter; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_BUFFER_SET_STRUCT]; +}; + +struct ipu_fw_psys_program_group_manifest { + u32 kernel_bitmap[IPU_FW_PSYS_KERNEL_BITMAP_NOF_ELEMS]; + u32 ID; + u16 program_manifest_offset; + u16 terminal_manifest_offset; + u16 private_data_offset; + u16 rbm_manifest_offset; + u16 size; + u8 alignment; + u8 kernel_count; + u8 program_count; + u8 terminal_count; + u8 subgraph_count; + u8 reserved[5]; +}; + +struct ipu_fw_generic_program_manifest { + u16 *dev_chn_size; + u16 *dev_chn_offset; + u16 *ext_mem_size; + u16 *ext_mem_offset; + u8 cell_id; + u8 cells[IPU_FW_PSYS_PROCESS_MAX_CELLS]; + u8 cell_type_id; + u8 *is_dfm_relocatable; + u32 *dfm_port_bitmap; + u32 *dfm_active_port_bitmap; +}; + +struct ipu_fw_generic_process { + u16 ext_mem_id; + u16 ext_mem_offset; + u16 dev_chn_offset; + u16 cell_id; + u16 dfm_port_bitmap; + u16 dfm_active_port_bitmap; +}; + +struct ipu_fw_resource_definitions { + u32 num_cells; + u32 num_cells_type; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + const u32 *cells; +#else + const u8 *cells; +#endif + u32 num_dev_channels; + const u16 *dev_channels; + + u32 num_ext_mem_types; + u32 num_ext_mem_ids; + const u16 *ext_mem_ids; + + u32 num_dfm_ids; + const u16 *dfms; + + u32 cell_mem_row; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + const enum ipu_mem_id *cell_mem; +#else + const u8 *cell_mem; +#endif + struct ipu_fw_generic_process process; +}; + +struct ipu_psys_kcmd; +struct ipu_psys; +int ipu_fw_psys_pg_start(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_disown(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_abort(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_submit(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_load_cycles(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_init_cycles(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_processing_cycles(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_rcv_event(struct ipu_psys *psys, + struct ipu_fw_psys_event *event); +int ipu_fw_psys_terminal_set(struct ipu_fw_psys_terminal *terminal, + int terminal_idx, + struct ipu_psys_kcmd *kcmd, + u32 buffer, unsigned int size); +void ipu_fw_psys_pg_dump(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd, const char *note); +int ipu_fw_psys_pg_get_id(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_get_terminal_count(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_get_size(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_set_ipu_vaddress(struct ipu_psys_kcmd *kcmd, + dma_addr_t vaddress); +struct ipu_fw_psys_terminal *ipu_fw_psys_pg_get_terminal(struct ipu_psys_kcmd + *kcmd, int index); +void ipu_fw_psys_pg_set_token(struct ipu_psys_kcmd *kcmd, u64 token); +u64 ipu_fw_psys_pg_get_token(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_pg_get_protocol(struct ipu_psys_kcmd *kcmd); +int ipu_fw_psys_open(struct ipu_psys *psys); +int ipu_fw_psys_close(struct ipu_psys *psys); + +/* common resource interface for both abi and api mode */ +int ipu_fw_psys_set_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index, + u8 value); +u8 ipu_fw_psys_get_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index); +int ipu_fw_psys_clear_process_cell(struct ipu_fw_psys_process *ptr); +int ipu_fw_psys_set_process_dev_chn_offset(struct ipu_fw_psys_process *ptr, + u16 offset, u16 value); +int ipu_fw_psys_set_process_ext_mem(struct ipu_fw_psys_process *ptr, + u16 type_id, u16 mem_id, u16 offset); +int ipu_fw_psys_get_program_manifest_by_process( + struct ipu_fw_generic_program_manifest *gen_pm, + const struct ipu_fw_psys_program_group_manifest *pg_manifest, + struct ipu_fw_psys_process *process); +#endif /* IPU_FW_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c new file mode 100644 index 000000000000..4faaa96d4724 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include + +#include +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2-be.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" + +/* + * Raw bayer format pixel order MUST BE MAINTAINED in groups of four codes. + * Otherwise pixel order calculation below WILL BREAK! + */ +static const u32 csi2_be_soc_supported_codes_pad[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + /* YUV420 plannar */ + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +static const u32 *csi2_be_soc_supported_codes[NR_OF_CSI2_BE_SOC_PADS]; + +static struct v4l2_subdev_internal_ops csi2_be_soc_sd_internal_ops = { + .open = ipu_isys_subdev_open, + .close = ipu_isys_subdev_close, +}; + +static const struct v4l2_subdev_core_ops csi2_be_soc_sd_core_ops = { +}; + +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +static const struct v4l2_subdev_video_ops csi2_be_soc_sd_video_ops = { + .s_stream = set_stream, +}; + +static int +__subdev_link_validate(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + + ip->csi2_be_soc = to_ipu_isys_csi2_be_soc(sd); + return ipu_isys_subdev_link_validate(sd, link, source_fmt, sink_fmt); +} + +static int +ipu_isys_csi2_be_soc_set_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + struct media_pad *pad = &asd->sd.entity.pads[sel->pad]; + + if (sel->target == V4L2_SEL_TGT_CROP && + pad->flags & MEDIA_PAD_FL_SOURCE && + asd->valid_tgts[sel->pad].crop) { + struct v4l2_rect *r; + unsigned int sink_pad = 0; + int i; + + for (i = 0; i < asd->nstreams; i++) { + if (!(asd->route[i].flags & + V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + if (asd->route[i].source == sel->pad) { + sink_pad = asd->route[i].sink; + break; + } + } + + if (i == asd->nstreams) { + dev_dbg(&asd->isys->adev->dev, "No sink pad routed.\n"); + return -EINVAL; + } + r = __ipu_isys_get_selection(sd, cfg, sel->target, + sink_pad, sel->which); + + /* Cropping is not supported by SoC BE. + * Only horizontal padding is allowed. + */ + sel->r.top = r->top; + sel->r.left = r->left; + sel->r.width = clamp(sel->r.width, r->width, + IPU_ISYS_MAX_WIDTH); + sel->r.height = r->height; + + *__ipu_isys_get_selection(sd, cfg, sel->target, sel->pad, + sel->which) = sel->r; + ipu_isys_subdev_fmt_propagate(sd, cfg, NULL, &sel->r, + IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP, + sel->pad, sel->which); + return 0; + } + return -EINVAL; +} + +static const struct v4l2_subdev_pad_ops csi2_be_soc_sd_pad_ops = { + .link_validate = __subdev_link_validate, + .get_fmt = ipu_isys_subdev_get_ffmt, + .set_fmt = ipu_isys_subdev_set_ffmt, + .get_selection = ipu_isys_subdev_get_sel, + .set_selection = ipu_isys_csi2_be_soc_set_sel, + .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, + .set_routing = ipu_isys_subdev_set_routing, + .get_routing = ipu_isys_subdev_get_routing, +}; + +static struct v4l2_subdev_ops csi2_be_soc_sd_ops = { + .core = &csi2_be_soc_sd_core_ops, + .video = &csi2_be_soc_sd_video_ops, + .pad = &csi2_be_soc_sd_pad_ops, +}; + +static struct media_entity_operations csi2_be_soc_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_route = ipu_isys_subdev_has_route, +}; + +static void csi2_be_soc_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, + fmt->stream, + fmt->which); + +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + struct ipu_isys_csi2_be_soc *csi2_be_soc = + to_ipu_isys_csi2_be_soc(sd); +#endif + + if (sd->entity.pads[fmt->pad].flags & MEDIA_PAD_FL_SINK) { + if (fmt->format.field != V4L2_FIELD_ALTERNATE) + fmt->format.field = V4L2_FIELD_NONE; + *ffmt = fmt->format; + + ipu_isys_subdev_fmt_propagate(sd, cfg, &fmt->format, + NULL, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + fmt->pad, fmt->which); + } else if (sd->entity.pads[fmt->pad].flags & MEDIA_PAD_FL_SOURCE) { + struct v4l2_mbus_framefmt *sink_ffmt; + struct v4l2_rect *r; + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + unsigned int sink_pad = 0; + int i; + + for (i = 0; i < asd->nsinks; i++) + if (media_entity_has_route(&sd->entity, fmt->pad, i)) + break; + if (i != asd->nsinks) + sink_pad = i; + sink_ffmt = __ipu_isys_get_ffmt(sd, cfg, sink_pad, + fmt->stream, + fmt->which); + r = __ipu_isys_get_selection(sd, cfg, V4L2_SEL_TGT_CROP, + fmt->pad, fmt->which); + + ffmt->width = r->width; + ffmt->height = r->height; + ffmt->code = sink_ffmt->code; + ffmt->field = sink_ffmt->field; + +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + /* + * For new IPU special case, format changing in BE-SOC, + * from YUV422 to I420, which is used to adapt multiple + * YUV sensors and provide I420 to BB for partial processing. + * Use original source pad format from user space. + * And change pin type to RAW_DUAL_SOC for this special case + */ + if (fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8 && + (sink_ffmt->code == MEDIA_BUS_FMT_YUYV8_1X16 || + sink_ffmt->code == MEDIA_BUS_FMT_UYVY8_1X16)) { + ffmt->code = fmt->format.code; + + for (i = 0; i < NR_OF_CSI2_BE_SOC_SOURCE_PADS; i++) + csi2_be_soc->av[i].aq.css_pin_type = + IPU_FW_ISYS_PIN_TYPE_RAW_DUAL_SOC; + } +#endif + } +} + +void ipu_isys_csi2_be_soc_cleanup(struct ipu_isys_csi2_be_soc *csi2_be_soc) +{ + int i; + + v4l2_device_unregister_subdev(&csi2_be_soc->asd.sd); + ipu_isys_subdev_cleanup(&csi2_be_soc->asd); + for (i = 0; i < NR_OF_CSI2_BE_SOC_STREAMS; i++) + ipu_isys_video_cleanup(&csi2_be_soc->av[i]); +} + +int ipu_isys_csi2_be_soc_init(struct ipu_isys_csi2_be_soc *csi2_be_soc, + struct ipu_isys *isys) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CSI2_BE_SOC_PAD_SINK(0), + .format = { + .width = 4096, + .height = 3072, + }, + }; + int rval, i; + + csi2_be_soc->asd.sd.entity.ops = &csi2_be_soc_entity_ops; + csi2_be_soc->asd.isys = isys; + + rval = ipu_isys_subdev_init(&csi2_be_soc->asd, + &csi2_be_soc_sd_ops, 0, + NR_OF_CSI2_BE_SOC_PADS, + NR_OF_CSI2_BE_SOC_STREAMS, + NR_OF_CSI2_BE_SOC_SOURCE_PADS, + NR_OF_CSI2_BE_SOC_SINK_PADS, 0); + if (rval) + goto fail; + + for (i = CSI2_BE_SOC_PAD_SINK(0); i < NR_OF_CSI2_BE_SOC_SINK_PADS; i++) + csi2_be_soc->asd.pad[i].flags = MEDIA_PAD_FL_SINK; + + for (i = CSI2_BE_SOC_PAD_SOURCE(0); + i < NR_OF_CSI2_BE_SOC_SOURCE_PADS + CSI2_BE_SOC_PAD_SOURCE(0); + i++) { + csi2_be_soc->asd.pad[i].flags = MEDIA_PAD_FL_SOURCE; + csi2_be_soc->asd.valid_tgts[i].crop = true; + } + + for (i = 0; i < NR_OF_CSI2_BE_SOC_PADS; i++) + csi2_be_soc_supported_codes[i] = + csi2_be_soc_supported_codes_pad; + csi2_be_soc->asd.supported_codes = csi2_be_soc_supported_codes; + csi2_be_soc->asd.be_mode = IPU_BE_SOC; + csi2_be_soc->asd.isl_mode = IPU_ISL_OFF; + csi2_be_soc->asd.set_ffmt = csi2_be_soc_set_ffmt; + + for (i = CSI2_BE_SOC_PAD_SINK(0); i < NR_OF_CSI2_BE_SOC_SINK_PADS; + i++) { + fmt.pad = CSI2_BE_SOC_PAD_SINK(i); + ipu_isys_subdev_set_ffmt(&csi2_be_soc->asd.sd, NULL, &fmt); + } + + ipu_isys_subdev_set_ffmt(&csi2_be_soc->asd.sd, NULL, &fmt); + csi2_be_soc->asd.sd.internal_ops = &csi2_be_soc_sd_internal_ops; + + snprintf(csi2_be_soc->asd.sd.name, sizeof(csi2_be_soc->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " CSI2 BE SOC"); + + v4l2_set_subdevdata(&csi2_be_soc->asd.sd, &csi2_be_soc->asd); + + mutex_lock(&csi2_be_soc->asd.mutex); + rval = v4l2_device_register_subdev(&isys->v4l2_dev, + &csi2_be_soc->asd.sd); + if (rval) { + dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); + goto fail; + } + + /* create default route information */ + for (i = 0; i < NR_OF_CSI2_BE_SOC_STREAMS; i++) { + csi2_be_soc->asd.route[i].sink = CSI2_BE_SOC_PAD_SINK(i); + csi2_be_soc->asd.route[i].source = CSI2_BE_SOC_PAD_SOURCE(i); + csi2_be_soc->asd.route[i].flags = 0; + } + + for (i = 0; i < NR_OF_CSI2_BE_SOC_SOURCE_PADS; i++) { + csi2_be_soc->asd.stream[CSI2_BE_SOC_PAD_SINK(i)].stream_id[0] + = 0; + csi2_be_soc->asd.stream[CSI2_BE_SOC_PAD_SOURCE(i)].stream_id[0] + = 0; + } + for (i = 0; i < NR_OF_CSI2_BE_SOC_STREAMS; i++) { + csi2_be_soc->asd.route[i].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE; + bitmap_set(csi2_be_soc->asd.stream[CSI2_BE_SOC_PAD_SINK(i)]. + streams_stat, 0, 1); + bitmap_set(csi2_be_soc->asd.stream[CSI2_BE_SOC_PAD_SOURCE(i)]. + streams_stat, 0, 1); + } + csi2_be_soc->asd.route[0].flags |= V4L2_SUBDEV_ROUTE_FL_IMMUTABLE; + mutex_unlock(&csi2_be_soc->asd.mutex); + for (i = 0; i < NR_OF_CSI2_BE_SOC_SOURCE_PADS; i++) { + snprintf(csi2_be_soc->av[i].vdev.name, + sizeof(csi2_be_soc->av[i].vdev.name), + IPU_ISYS_ENTITY_PREFIX " BE SOC capture %d", i); + /* + * Pin type could be overwritten for YUV422 to I420 case, at + * set_format phase + */ + csi2_be_soc->av[i].aq.css_pin_type = + IPU_FW_ISYS_PIN_TYPE_RAW_SOC; + csi2_be_soc->av[i].isys = isys; + csi2_be_soc->av[i].pfmts = ipu_isys_pfmts_be_soc; + + csi2_be_soc->av[i].try_fmt_vid_mplane = + ipu_isys_video_try_fmt_vid_mplane_default; + csi2_be_soc->av[i].prepare_firmware_stream_cfg = + ipu_isys_prepare_firmware_stream_cfg_default; + csi2_be_soc->av[i].aq.buf_prepare = ipu_isys_buf_prepare; + csi2_be_soc->av[i].aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + csi2_be_soc->av[i].aq.link_fmt_validate = + ipu_isys_link_fmt_validate; + csi2_be_soc->av[i].aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&csi2_be_soc->av[i], + &csi2_be_soc->asd.sd.entity, + CSI2_BE_SOC_PAD_SOURCE(i), + MEDIA_PAD_FL_SINK, + MEDIA_LNK_FL_DYNAMIC); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + } + + return 0; + +fail: + ipu_isys_csi2_be_soc_cleanup(csi2_be_soc); + + return rval; +} diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be.c b/drivers/media/pci/intel/ipu-isys-csi2-be.c new file mode 100644 index 000000000000..deaf2a55362a --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-csi2-be.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include + +#include +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2-be.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" + +/* + * Raw bayer format pixel order MUST BE MAINTAINED in groups of four codes. + * Otherwise pixel order calculation below WILL BREAK! + */ +static const u32 csi2_be_supported_codes_pad[] = { + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +static const u32 *csi2_be_supported_codes[] = { + csi2_be_supported_codes_pad, + csi2_be_supported_codes_pad, +}; + +static struct v4l2_subdev_internal_ops csi2_be_sd_internal_ops = { + .open = ipu_isys_subdev_open, + .close = ipu_isys_subdev_close, +}; + +static const struct v4l2_subdev_core_ops csi2_be_sd_core_ops = { +}; + +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +static const struct v4l2_subdev_video_ops csi2_be_sd_video_ops = { + .s_stream = set_stream, +}; + +static int __subdev_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + + ip->csi2_be = to_ipu_isys_csi2_be(sd); + return ipu_isys_subdev_link_validate(sd, link, source_fmt, sink_fmt); +} + +static int get_supported_code_index(u32 code) +{ + int i; + + for (i = 0; csi2_be_supported_codes_pad[i]; i++) { + if (csi2_be_supported_codes_pad[i] == code) + return i; + } + return -EINVAL; +} + +static int ipu_isys_csi2_be_set_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + struct media_pad *pad = &asd->sd.entity.pads[sel->pad]; + + if (sel->target == V4L2_SEL_TGT_CROP && + pad->flags & MEDIA_PAD_FL_SOURCE && + asd->valid_tgts[CSI2_BE_PAD_SOURCE].crop) { + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, sel->pad, 0, sel->which); + struct v4l2_rect *r = __ipu_isys_get_selection + (sd, cfg, sel->target, CSI2_BE_PAD_SINK, sel->which); + + if (get_supported_code_index(ffmt->code) < 0) { + /* Non-bayer formats can't be single line cropped */ + sel->r.left &= ~1; + sel->r.top &= ~1; + + /* Non-bayer formats can't pe padded at all */ + sel->r.width = clamp(sel->r.width, + IPU_ISYS_MIN_WIDTH, r->width); + } else { + sel->r.width = clamp(sel->r.width, + IPU_ISYS_MIN_WIDTH, + IPU_ISYS_MAX_WIDTH); + } + + /* + * ISAPF can pad only horizontally, height is + * restricted by sink pad resolution. + */ + sel->r.height = clamp(sel->r.height, IPU_ISYS_MIN_HEIGHT, + r->height); + *__ipu_isys_get_selection(sd, cfg, sel->target, sel->pad, + sel->which) = sel->r; + ipu_isys_subdev_fmt_propagate + (sd, cfg, NULL, &sel->r, + IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP, + sel->pad, sel->which); + return 0; + } + return ipu_isys_subdev_set_sel(sd, cfg, sel); +} + +static const struct v4l2_subdev_pad_ops csi2_be_sd_pad_ops = { + .link_validate = __subdev_link_validate, + .get_fmt = ipu_isys_subdev_get_ffmt, + .set_fmt = ipu_isys_subdev_set_ffmt, + .get_selection = ipu_isys_subdev_get_sel, + .set_selection = ipu_isys_csi2_be_set_sel, + .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, +}; + +static struct v4l2_subdev_ops csi2_be_sd_ops = { + .core = &csi2_be_sd_core_ops, + .video = &csi2_be_sd_video_ops, + .pad = &csi2_be_sd_pad_ops, +}; + +static struct media_entity_operations csi2_be_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +static void csi2_be_set_ffmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *cfg, + struct v4l2_subdev_format *fmt) +#else +static void csi2_be_set_ffmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +#endif +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->stream, + fmt->which); + + switch (fmt->pad) { + case CSI2_BE_PAD_SINK: + if (fmt->format.field != V4L2_FIELD_ALTERNATE) + fmt->format.field = V4L2_FIELD_NONE; + *ffmt = fmt->format; + + ipu_isys_subdev_fmt_propagate + (sd, cfg, &fmt->format, NULL, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT, fmt->pad, fmt->which); + return; + case CSI2_BE_PAD_SOURCE: { + struct v4l2_mbus_framefmt *sink_ffmt = + __ipu_isys_get_ffmt(sd, cfg, CSI2_BE_PAD_SINK, + fmt->stream, fmt->which); + struct v4l2_rect *r = + __ipu_isys_get_selection(sd, cfg, V4L2_SEL_TGT_CROP, + CSI2_BE_PAD_SOURCE, + fmt->which); + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + u32 code = sink_ffmt->code; + int idx = get_supported_code_index(code); + + if (asd->valid_tgts[CSI2_BE_PAD_SOURCE].crop && idx >= 0) { + int crop_info = 0; + + if (r->top & 1) + crop_info |= CSI2_BE_CROP_VER; + if (r->left & 1) + crop_info |= CSI2_BE_CROP_HOR; + code = csi2_be_supported_codes_pad + [((idx & CSI2_BE_CROP_MASK) ^ crop_info) + + (idx & ~CSI2_BE_CROP_MASK)]; + } + ffmt->width = r->width; + ffmt->height = r->height; + ffmt->code = code; + ffmt->field = sink_ffmt->field; + return; + } + default: + dev_err(&csi2->isys->adev->dev, "Unknown pad type\n"); + WARN_ON(1); + } +} + +void ipu_isys_csi2_be_cleanup(struct ipu_isys_csi2_be *csi2_be) +{ + v4l2_device_unregister_subdev(&csi2_be->asd.sd); + ipu_isys_subdev_cleanup(&csi2_be->asd); + ipu_isys_video_cleanup(&csi2_be->av); +} + +int ipu_isys_csi2_be_init(struct ipu_isys_csi2_be *csi2_be, + struct ipu_isys *isys) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CSI2_BE_PAD_SINK, + .format = { + .width = 4096, + .height = 3072, + }, + }; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CSI2_BE_PAD_SOURCE, + .target = V4L2_SEL_TGT_CROP, + .r = { + .width = fmt.format.width, + .height = fmt.format.height, + }, + }; + int rval; + + csi2_be->asd.sd.entity.ops = &csi2_be_entity_ops; + csi2_be->asd.isys = isys; + + rval = ipu_isys_subdev_init(&csi2_be->asd, &csi2_be_sd_ops, 0, + NR_OF_CSI2_BE_PADS, + NR_OF_CSI2_BE_STREAMS, + NR_OF_CSI2_BE_SOURCE_PADS, + NR_OF_CSI2_BE_SINK_PADS, 0); + if (rval) + goto fail; + + csi2_be->asd.pad[CSI2_BE_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; + csi2_be->asd.pad[CSI2_BE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + csi2_be->asd.valid_tgts[CSI2_BE_PAD_SOURCE].crop = true; + csi2_be->asd.set_ffmt = csi2_be_set_ffmt; + csi2_be->asd.isys = isys; + + BUILD_BUG_ON(ARRAY_SIZE(csi2_be_supported_codes) != NR_OF_CSI2_BE_PADS); + csi2_be->asd.supported_codes = csi2_be_supported_codes; + csi2_be->asd.be_mode = IPU_BE_RAW; + csi2_be->asd.isl_mode = IPU_ISL_CSI2_BE; + + ipu_isys_subdev_set_ffmt(&csi2_be->asd.sd, NULL, &fmt); + ipu_isys_csi2_be_set_sel(&csi2_be->asd.sd, NULL, &sel); + + csi2_be->asd.sd.internal_ops = &csi2_be_sd_internal_ops; + snprintf(csi2_be->asd.sd.name, sizeof(csi2_be->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " CSI2 BE"); + snprintf(csi2_be->av.vdev.name, sizeof(csi2_be->av.vdev.name), + IPU_ISYS_ENTITY_PREFIX " CSI2 BE capture"); + csi2_be->av.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_RAW_NS; + v4l2_set_subdevdata(&csi2_be->asd.sd, &csi2_be->asd); + rval = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2_be->asd.sd); + if (rval) { + dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); + goto fail; + } + + csi2_be->av.isys = isys; + csi2_be->av.pfmts = ipu_isys_pfmts; + csi2_be->av.try_fmt_vid_mplane = + ipu_isys_video_try_fmt_vid_mplane_default; + csi2_be->av.prepare_firmware_stream_cfg = + ipu_isys_prepare_firmware_stream_cfg_default; + csi2_be->av.aq.buf_prepare = ipu_isys_buf_prepare; + csi2_be->av.aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + csi2_be->av.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + csi2_be->av.aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&csi2_be->av, &csi2_be->asd.sd.entity, + CSI2_BE_PAD_SOURCE, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + return 0; + +fail: + ipu_isys_csi2_be_cleanup(csi2_be); + + return rval; +} diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be.h b/drivers/media/pci/intel/ipu-isys-csi2-be.h new file mode 100644 index 000000000000..70a17833a9c4 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-csi2-be.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_CSI2_BE_H +#define IPU_ISYS_CSI2_BE_H + +#include +#include + +#include "ipu-isys-queue.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" +#include "ipu-platform-isys.h" + +struct ipu_isys_csi2_be_pdata; +struct ipu_isys; + +#define CSI2_BE_PAD_SINK 0 +#define CSI2_BE_PAD_SOURCE 1 + +#define NR_OF_CSI2_BE_PADS 2 +#define NR_OF_CSI2_BE_SOURCE_PADS 1 +#define NR_OF_CSI2_BE_SINK_PADS 1 + +#define NR_OF_CSI2_BE_STREAMS 1 +#define NR_OF_CSI2_BE_SOC_SOURCE_PADS NR_OF_CSI2_BE_SOC_STREAMS +#define NR_OF_CSI2_BE_SOC_SINK_PADS NR_OF_CSI2_BE_SOC_STREAMS +#define CSI2_BE_SOC_PAD_SINK(n) \ + ({ typeof(n) __n = (n); \ + (__n) >= NR_OF_CSI2_BE_SOC_SINK_PADS ? \ + (NR_OF_CSI2_BE_SOC_SINK_PADS) : (__n); }) +#define CSI2_BE_SOC_PAD_SOURCE(n) \ + ({ typeof(n) __n = (n); \ + (__n) >= NR_OF_CSI2_BE_SOC_SOURCE_PADS ? \ + (NR_OF_CSI2_BE_SOC_PADS - 1) : \ + ((__n) + NR_OF_CSI2_BE_SOC_SINK_PADS); }) +#define NR_OF_CSI2_BE_SOC_PADS \ + (NR_OF_CSI2_BE_SOC_SOURCE_PADS + NR_OF_CSI2_BE_SOC_SINK_PADS) + +#define CSI2_BE_CROP_HOR BIT(0) +#define CSI2_BE_CROP_VER BIT(1) +#define CSI2_BE_CROP_MASK (CSI2_BE_CROP_VER | CSI2_BE_CROP_HOR) + +/* + * struct ipu_isys_csi2_be + */ +struct ipu_isys_csi2_be { + struct ipu_isys_csi2_be_pdata *pdata; + struct ipu_isys_subdev asd; + struct ipu_isys_video av; +}; + +struct ipu_isys_csi2_be_soc { + struct ipu_isys_csi2_be_pdata *pdata; + struct ipu_isys_subdev asd; + struct ipu_isys_video av[NR_OF_CSI2_BE_SOC_SOURCE_PADS]; +}; + +#define to_ipu_isys_csi2_be(sd) \ + container_of(to_ipu_isys_subdev(sd), \ + struct ipu_isys_csi2_be, asd) + +#define to_ipu_isys_csi2_be_soc(sd) \ + container_of(to_ipu_isys_subdev(sd), \ + struct ipu_isys_csi2_be_soc, asd) + +int ipu_isys_csi2_be_init(struct ipu_isys_csi2_be *csi2_be, + struct ipu_isys *isys); +int ipu_isys_csi2_be_soc_init( + struct ipu_isys_csi2_be_soc *csi2_be_soc, struct ipu_isys *isys); +void ipu_isys_csi2_be_cleanup(struct ipu_isys_csi2_be *csi2_be); +void ipu_isys_csi2_be_soc_cleanup(struct ipu_isys_csi2_be_soc *csi2_be); + +#endif /* IPU_ISYS_CSI2_BE_H */ diff --git a/drivers/media/pci/intel/ipu-isys-csi2.c b/drivers/media/pci/intel/ipu-isys-csi2.c new file mode 100644 index 000000000000..87ecce7ce98d --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-csi2.c @@ -0,0 +1,939 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include + +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-buttress.h" +#include "ipu-isys.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" +#include "ipu-platform-regs.h" + +#define CREATE_TRACE_POINTS +#define IPU_SOF_SEQID_TRACE +#define IPU_EOF_SEQID_TRACE +#include "ipu-trace-event.h" + +static const u32 csi2_supported_codes_pad_sink[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YUYV10_1X20, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, + MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, + MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +static const u32 csi2_supported_codes_pad_source[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YUYV10_1X20, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +static const u32 csi2_supported_codes_pad_meta[] = { + MEDIA_BUS_FMT_FIXED, + 0, +}; + +static const u32 *csi2_supported_codes[NR_OF_CSI2_PADS]; + +static struct v4l2_subdev_internal_ops csi2_sd_internal_ops = { + .open = ipu_isys_subdev_open, + .close = ipu_isys_subdev_close, +}; + +int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, __s64 *link_freq) +{ + struct ipu_isys_pipeline *pipe = container_of(csi2->asd.sd.entity.pipe, + struct ipu_isys_pipeline, + pipe); + struct v4l2_subdev *ext_sd = + media_entity_to_v4l2_subdev(pipe->external->entity); + struct v4l2_ext_control c = {.id = V4L2_CID_LINK_FREQ, }; + struct v4l2_ext_controls cs = {.count = 1, + .controls = &c, + }; + struct v4l2_querymenu qm = {.id = c.id, }; + int rval; + + if (!ext_sd) { + WARN_ON(1); + return -ENODEV; + } + rval = v4l2_g_ext_ctrls(ext_sd->ctrl_handler, &cs); + if (rval) { + dev_info(&csi2->isys->adev->dev, "can't get link frequency\n"); + return rval; + } + + qm.index = c.value; + + rval = v4l2_querymenu(ext_sd->ctrl_handler, &qm); + if (rval) { + dev_info(&csi2->isys->adev->dev, "can't get menu item\n"); + return rval; + } + + dev_dbg(&csi2->isys->adev->dev, "%s: link frequency %lld\n", __func__, + qm.value); + + if (!qm.value) + return -EINVAL; + *link_freq = qm.value; + return 0; +} + +static int ipu_get_frame_desc_entry_by_dt(struct v4l2_subdev *sd, + struct v4l2_mbus_frame_desc_entry + *entry, u8 data_type) +{ + struct v4l2_mbus_frame_desc desc = { + .num_entries = V4L2_FRAME_DESC_ENTRY_MAX, + }; + int rval, i; + + rval = v4l2_subdev_call(sd, pad, get_frame_desc, 0, &desc); + if (rval) + return rval; + + for (i = 0; i < desc.num_entries; i++) { + if (desc.entry[i].bus.csi2.data_type != data_type) + continue; + *entry = desc.entry[i]; + return 0; + } + + return -EINVAL; +} + +static void csi2_meta_prepare_firmware_stream_cfg_default( + struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi *cfg) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct ipu_isys_queue *aq = &av->aq; + struct ipu_fw_isys_output_pin_info_abi *pin_info; + struct v4l2_mbus_frame_desc_entry entry; + int pin = cfg->nof_output_pins++; + int inpin = cfg->nof_input_pins++; + int rval; + + aq->fw_output = pin; + ip->output_pins[pin].pin_ready = ipu_isys_queue_buf_ready; + ip->output_pins[pin].aq = aq; + + pin_info = &cfg->output_pins[pin]; + pin_info->input_pin_id = inpin; + pin_info->output_res.width = av->mpix.width; + pin_info->output_res.height = av->mpix.height; + pin_info->stride = av->mpix.plane_fmt[0].bytesperline; + pin_info->pt = aq->css_pin_type; + pin_info->ft = av->pfmt->css_pixelformat; + pin_info->send_irq = 1; + + rval = + ipu_get_frame_desc_entry_by_dt(media_entity_to_v4l2_subdev + (ip->external->entity), &entry, + IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8); + if (!rval) { + cfg->input_pins[inpin].dt = IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8; + cfg->input_pins[inpin].input_res.width = + entry.two_dim.width * entry.bpp / BITS_PER_BYTE; + cfg->input_pins[inpin].input_res.height = + entry.two_dim.height; + } +} + +static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + + dev_dbg(&csi2->isys->adev->dev, "subscribe event(type %u id %u)\n", + sub->type, sub->id); + + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 10, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { + .subscribe_event = subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static struct ipu_isys_pixelformat csi2_meta_pfmts[] = { + {V4L2_FMT_IPU_ISYS_META, 8, 8, 0, MEDIA_BUS_FMT_FIXED, 0}, + {}, +}; + +/* + * The input system CSI2+ receiver has several + * parameters affecting the receiver timings. These depend + * on the MIPI bus frequency F in Hz (sensor transmitter rate) + * as follows: + * register value = (A/1e9 + B * UI) / COUNT_ACC + * where + * UI = 1 / (2 * F) in seconds + * COUNT_ACC = counter accuracy in seconds + * For IPU4, COUNT_ACC = 0.125 ns + * + * A and B are coefficients from the table below, + * depending whether the register minimum or maximum value is + * calculated. + * Minimum Maximum + * Clock lane A B A B + * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 + * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 + * Data lanes + * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 + * + * We use the minimum values of both A and B. + */ + +#define DIV_SHIFT 8 + +static uint32_t calc_timing(s32 a, int32_t b, int64_t link_freq, int32_t accinv) +{ + return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT) + / (int32_t)(link_freq >> DIV_SHIFT)); +} + +static int +ipu_isys_csi2_calc_timing(struct ipu_isys_csi2 *csi2, + struct ipu_isys_csi2_timing *timing, uint32_t accinv) +{ + __s64 link_freq; + int rval; + + rval = ipu_isys_csi2_get_link_freq(csi2, &link_freq); + if (rval) + return rval; + + timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A, + CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B, + link_freq, accinv); + timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A, + CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B, + link_freq, accinv); + dev_dbg(&csi2->isys->adev->dev, "ctermen %u\n", timing->ctermen); + dev_dbg(&csi2->isys->adev->dev, "csettle %u\n", timing->csettle); + + timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A, + CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B, + link_freq, accinv); + timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A, + CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B, + link_freq, accinv); + dev_dbg(&csi2->isys->adev->dev, "dtermen %u\n", timing->dtermen); + dev_dbg(&csi2->isys->adev->dev, "dsettle %u\n", timing->dsettle); + + return 0; +} + +#define CSI2_ACCINV 8 + +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + struct ipu_isys_csi2_config *cfg; + struct v4l2_subdev *ext_sd; + struct v4l2_control c = {.id = V4L2_CID_MIPI_LANES, }; + struct ipu_isys_csi2_timing timing; + unsigned int nlanes; + int rval; + + dev_dbg(&csi2->isys->adev->dev, "csi2 s_stream %d\n", enable); + + if (!ip->external->entity) { + WARN_ON(1); + return -ENODEV; + } + ext_sd = media_entity_to_v4l2_subdev(ip->external->entity); + cfg = v4l2_get_subdev_hostdata(ext_sd); + + if (!enable) { + csi2->stream_count--; + if (csi2->stream_count) + return 0; + + ipu_isys_csi2_set_stream(sd, timing, 0, enable); + return 0; + } + + ip->has_sof = true; + + if (csi2->stream_count) { + csi2->stream_count++; + return 0; + } + + rval = v4l2_g_ctrl(ext_sd->ctrl_handler, &c); + if (!rval && c.value > 0 && cfg->nlanes > c.value) { + nlanes = c.value; + dev_dbg(&csi2->isys->adev->dev, "lane nr %d.\n", nlanes); + } else { + nlanes = cfg->nlanes; + } + + rval = ipu_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); + if (rval) + return rval; + + ipu_isys_csi2_set_stream(sd, timing, nlanes, enable); + csi2->stream_count++; + + return 0; +} + +static void csi2_capture_done(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info) +{ + if (ip->interlaced && ip->isys->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) { + struct ipu_isys_buffer *ib; + unsigned long flags; + + spin_lock_irqsave(&ip->short_packet_queue_lock, flags); + if (!list_empty(&ip->short_packet_active)) { + ib = list_last_entry(&ip->short_packet_active, + struct ipu_isys_buffer, head); + list_move(&ib->head, &ip->short_packet_incoming); + } + spin_unlock_irqrestore(&ip->short_packet_queue_lock, flags); + } + if (ip->csi2) { + ipu_isys_csi2_error(ip->csi2); + } +} + +static int csi2_link_validate(struct media_link *link) +{ + struct ipu_isys_csi2 *csi2; + struct ipu_isys_pipeline *ip; + struct v4l2_subdev_route r[IPU_ISYS_MAX_STREAMS]; + struct v4l2_subdev_routing routing = { + .routes = r, + .num_routes = IPU_ISYS_MAX_STREAMS, + }; + unsigned int active = 0; + int i; + int rval; + + if (!link->sink->entity || + !link->sink->entity->pipe || !link->source->entity) + return -EINVAL; + csi2 = + to_ipu_isys_csi2(media_entity_to_v4l2_subdev(link->sink->entity)); + ip = to_ipu_isys_pipeline(link->sink->entity->pipe); + csi2->receiver_errors = 0; + ip->csi2 = csi2; + ipu_isys_video_add_capture_done(to_ipu_isys_pipeline + (link->sink->entity->pipe), + csi2_capture_done); + + rval = v4l2_subdev_link_validate(link); + if (rval) + return rval; + + if (!v4l2_ctrl_g_ctrl(csi2->store_csi2_header)) { + for (i = 0; i < NR_OF_CSI2_SOURCE_PADS; i++) { + struct media_pad *remote_pad = + media_entity_remote_pad(&csi2->asd. + pad[CSI2_PAD_SOURCE(i)]); + + if (remote_pad && + is_media_entity_v4l2_subdev(remote_pad->entity)) { + dev_err(&csi2->isys->adev->dev, + "CSI2 BE requires CSI2 headers.\n"); + return -EINVAL; + } + } + } + + rval = + v4l2_subdev_call(media_entity_to_v4l2_subdev(link->source->entity), + pad, get_routing, &routing); + + if (rval) { + csi2->remote_streams = 1; + return 0; + } + + for (i = 0; i < routing.num_routes; i++) { + if (routing.routes[i].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) + active++; + } + + if (active != + bitmap_weight(csi2->asd.stream[link->sink->index].streams_stat, 32)) + return -EINVAL; + + csi2->remote_streams = active; + + return 0; +} + +static bool csi2_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1, int *stream) +{ + if (pad0 == CSI2_PAD_META || pad1 == CSI2_PAD_META) + return true; + return ipu_isys_subdev_has_route(entity, pad0, pad1, stream); +} + +static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { + .s_stream = set_stream, +}; + +static int get_metadata_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct media_pad *pad = + media_entity_remote_pad(&sd->entity.pads[CSI2_PAD_SINK]); + struct v4l2_mbus_frame_desc_entry entry; + int rval; + + if (!pad) + return -EINVAL; + + rval = + ipu_get_frame_desc_entry_by_dt(media_entity_to_v4l2_subdev + (pad->entity), &entry, + IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8); + + if (!rval) { + fmt->format.width = + entry.two_dim.width * entry.bpp / BITS_PER_BYTE; + fmt->format.height = entry.two_dim.height; + fmt->format.code = entry.pixelcode; + fmt->format.field = V4L2_FIELD_NONE; + } + return rval; +} + +static int ipu_isys_csi2_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad == CSI2_PAD_META) + return get_metadata_fmt(sd, cfg, fmt); + return ipu_isys_subdev_get_ffmt(sd, cfg, fmt); +} + +static int ipu_isys_csi2_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad == CSI2_PAD_META) + return get_metadata_fmt(sd, cfg, fmt); + return ipu_isys_subdev_set_ffmt(sd, cfg, fmt); +} + +static int __subdev_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + + if (source_fmt->format.field == V4L2_FIELD_ALTERNATE) + ip->interlaced = true; + + return ipu_isys_subdev_link_validate(sd, link, source_fmt, sink_fmt); +} + +static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { + .link_validate = __subdev_link_validate, + .get_fmt = ipu_isys_csi2_get_fmt, + .set_fmt = ipu_isys_csi2_set_fmt, + .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, + .set_routing = ipu_isys_subdev_set_routing, + .get_routing = ipu_isys_subdev_get_routing, +}; + +static struct v4l2_subdev_ops csi2_sd_ops = { + .core = &csi2_sd_core_ops, + .video = &csi2_sd_video_ops, + .pad = &csi2_sd_pad_ops, +}; + +static struct media_entity_operations csi2_entity_ops = { + .link_validate = csi2_link_validate, + .has_route = csi2_has_route, +}; + +static void csi2_set_ffmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, + fmt->stream, + fmt->which); + + if (fmt->format.field != V4L2_FIELD_ALTERNATE) + fmt->format.field = V4L2_FIELD_NONE; + + if (fmt->pad == CSI2_PAD_SINK) { + *ffmt = fmt->format; + if (fmt->stream) + return; + ipu_isys_subdev_fmt_propagate( + sd, cfg, &fmt->format, NULL, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + fmt->pad, fmt->which); + return; + } + + if (fmt->pad == CSI2_PAD_META) { + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, + fmt->stream, + fmt->which); + struct media_pad *pad = media_entity_remote_pad( + &sd->entity.pads[CSI2_PAD_SINK]); + struct v4l2_mbus_frame_desc_entry entry; + int rval; + + if (!pad) { + ffmt->width = 0; + ffmt->height = 0; + ffmt->code = 0; + return; + } + + rval = ipu_get_frame_desc_entry_by_dt( + media_entity_to_v4l2_subdev(pad->entity), + &entry, + IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8); + + if (!rval) { + ffmt->width = entry.two_dim.width * entry.bpp + / BITS_PER_BYTE; + ffmt->height = entry.two_dim.height; + ffmt->code = entry.pixelcode; + ffmt->field = V4L2_FIELD_NONE; + } + + return; + } + if (sd->entity.pads[fmt->pad].flags & MEDIA_PAD_FL_SOURCE) { + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->field = fmt->format.field; + ffmt->code = + ipu_isys_subdev_code_to_uncompressed(fmt->format.code); + return; + } + + WARN_ON(1); +} + +static const struct ipu_isys_pixelformat * +csi2_try_fmt(struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(av->vdev.entity.links[0].source-> + entity); +#else + struct media_link *link = list_first_entry(&av->vdev.entity.links, + struct media_link, list); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); +#endif + struct ipu_isys_csi2 *csi2; + + if (!sd) + return NULL; + + csi2 = to_ipu_isys_csi2(sd); + + return ipu_isys_video_try_fmt_vid_mplane(av, mpix, + v4l2_ctrl_g_ctrl(csi2->store_csi2_header)); +} + +void ipu_isys_csi2_cleanup(struct ipu_isys_csi2 *csi2) +{ + int i; + + if (!csi2->isys) + return; + + v4l2_device_unregister_subdev(&csi2->asd.sd); + ipu_isys_subdev_cleanup(&csi2->asd); + for (i = 0; i < NR_OF_CSI2_SOURCE_PADS; i++) + ipu_isys_video_cleanup(&csi2->av[i]); + ipu_isys_video_cleanup(&csi2->av_meta); + csi2->isys = NULL; +} + +static void csi_ctrl_init(struct v4l2_subdev *sd) +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + + static const struct v4l2_ctrl_config cfg = { + .id = V4L2_CID_IPU_STORE_CSI2_HEADER, + .name = "Store CSI-2 Headers", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, + }; + + csi2->store_csi2_header = v4l2_ctrl_new_custom(&csi2->asd.ctrl_handler, + &cfg, NULL); +} + +int ipu_isys_csi2_init(struct ipu_isys_csi2 *csi2, + struct ipu_isys *isys, + void __iomem *base, unsigned int index) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CSI2_PAD_SINK, + .format = { + .width = 4096, + .height = 3072, + }, + }; + struct v4l2_subdev_format fmt_meta = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CSI2_PAD_META, + }; + int i, rval, src; + + csi2->isys = isys; + csi2->base = base; + csi2->index = index; + + csi2->asd.sd.entity.ops = &csi2_entity_ops; + csi2->asd.ctrl_init = csi_ctrl_init; + csi2->asd.isys = isys; + init_completion(&csi2->eof_completion); + csi2->remote_streams = 1; + csi2->stream_count = 0; + + rval = ipu_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, + NR_OF_CSI2_PADS, + NR_OF_CSI2_STREAMS, + NR_OF_CSI2_SOURCE_PADS, + NR_OF_CSI2_SINK_PADS, + V4L2_SUBDEV_FL_HAS_SUBSTREAMS); + if (rval) + goto fail; + + csi2->asd.pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT | MEDIA_PAD_FL_MULTIPLEX; + for (i = CSI2_PAD_SOURCE(0); + i < (NR_OF_CSI2_SOURCE_PADS + CSI2_PAD_SOURCE(0)); i++) + csi2->asd.pad[i].flags = MEDIA_PAD_FL_SOURCE; + + csi2->asd.pad[CSI2_PAD_META].flags = MEDIA_PAD_FL_SOURCE; + src = index; +#ifdef CONFIG_VIDEO_INTEL_IPU4P + src = index ? (index + 5) : (index + 3); +#endif + csi2->asd.source = IPU_FW_ISYS_STREAM_SRC_CSI2_PORT0 + src; + csi2_supported_codes[CSI2_PAD_SINK] = csi2_supported_codes_pad_sink; + + for (i = 0; i < NR_OF_CSI2_SOURCE_PADS; i++) + csi2_supported_codes[i + 1] = csi2_supported_codes_pad_source; + csi2_supported_codes[CSI2_PAD_META] = csi2_supported_codes_pad_meta; + csi2->asd.supported_codes = csi2_supported_codes; + csi2->asd.set_ffmt = csi2_set_ffmt; + + csi2->asd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + csi2->asd.sd.internal_ops = &csi2_sd_internal_ops; + snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " CSI-2 %u", index); + v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); + + mutex_lock(&csi2->asd.mutex); + rval = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); + if (rval) { + mutex_unlock(&csi2->asd.mutex); + dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); + goto fail; + } + + __ipu_isys_subdev_set_ffmt(&csi2->asd.sd, NULL, &fmt); + __ipu_isys_subdev_set_ffmt(&csi2->asd.sd, NULL, &fmt_meta); + + /* create default route information */ + for (i = 0; i < NR_OF_CSI2_STREAMS; i++) { + csi2->asd.route[i].sink = CSI2_PAD_SINK; + csi2->asd.route[i].source = CSI2_PAD_SOURCE(i); + csi2->asd.route[i].flags = 0; + } + + for (i = 0; i < NR_OF_CSI2_SOURCE_PADS; i++) { + csi2->asd.stream[CSI2_PAD_SINK].stream_id[i] = i; + csi2->asd.stream[CSI2_PAD_SOURCE(i)].stream_id[CSI2_PAD_SINK] + = i; + } + csi2->asd.route[0].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE; + bitmap_set(csi2->asd.stream[CSI2_PAD_SINK].streams_stat, 0, 1); + bitmap_set(csi2->asd.stream[CSI2_PAD_SOURCE(0)].streams_stat, 0, 1); + + mutex_unlock(&csi2->asd.mutex); + + for (i = 0; i < NR_OF_CSI2_SOURCE_PADS; i++) { + snprintf(csi2->av[i].vdev.name, sizeof(csi2->av[i].vdev.name), + IPU_ISYS_ENTITY_PREFIX " CSI-2 %u capture %d", + index, i); + csi2->av[i].isys = isys; + csi2->av[i].aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_MIPI; + csi2->av[i].pfmts = ipu_isys_pfmts_packed; + csi2->av[i].try_fmt_vid_mplane = csi2_try_fmt; + csi2->av[i].prepare_firmware_stream_cfg = + ipu_isys_prepare_firmware_stream_cfg_default; + csi2->av[i].packed = true; + csi2->av[i].line_header_length = + IPU_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; + csi2->av[i].line_footer_length = + IPU_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; + csi2->av[i].aq.buf_prepare = ipu_isys_buf_prepare; + csi2->av[i].aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + csi2->av[i].aq.link_fmt_validate = ipu_isys_link_fmt_validate; + csi2->av[i].aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&csi2->av[i], + &csi2->asd.sd.entity, + CSI2_PAD_SOURCE(i), + MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + } + + snprintf(csi2->av_meta.vdev.name, sizeof(csi2->av_meta.vdev.name), + IPU_ISYS_ENTITY_PREFIX " CSI-2 %u meta", index); + csi2->av_meta.isys = isys; + csi2->av_meta.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_MIPI; + csi2->av_meta.pfmts = csi2_meta_pfmts; + csi2->av_meta.try_fmt_vid_mplane = csi2_try_fmt; + csi2->av_meta.prepare_firmware_stream_cfg = + csi2_meta_prepare_firmware_stream_cfg_default; + csi2->av_meta.packed = true; + csi2->av_meta.line_header_length = + IPU_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; + csi2->av_meta.line_footer_length = + IPU_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; + csi2->av_meta.aq.buf_prepare = ipu_isys_buf_prepare; + csi2->av_meta.aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + csi2->av_meta.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + csi2->av_meta.aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&csi2->av_meta, &csi2->asd.sd.entity, + CSI2_PAD_META, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init metadata node\n"); + goto fail; + } + return 0; + +fail: + ipu_isys_csi2_cleanup(csi2); + + return rval; +} + +void ipu_isys_csi2_sof_event(struct ipu_isys_csi2 *csi2, unsigned int vc) +{ + struct ipu_isys_pipeline *ip = NULL; + struct v4l2_event ev = { + .type = V4L2_EVENT_FRAME_SYNC, + }; + struct video_device *vdev = csi2->asd.sd.devnode; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&csi2->isys->lock, flags); + csi2->in_frame[vc] = true; + + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (csi2->isys->pipes[i] && + csi2->isys->pipes[i]->vc == vc && + csi2->isys->pipes[i]->csi2 == csi2) { + ip = csi2->isys->pipes[i]; + break; + } + } + + /* Pipe already vanished */ + if (!ip) { + spin_unlock_irqrestore(&csi2->isys->lock, flags); + return; + } + + ev.u.frame_sync.frame_sequence = atomic_inc_return(&ip->sequence) - 1; + ev.id = ip->stream_id; + spin_unlock_irqrestore(&csi2->isys->lock, flags); + + trace_ipu_sof_seqid(ev.u.frame_sync.frame_sequence, csi2->index, vc); + v4l2_event_queue(vdev, &ev); + dev_dbg(&csi2->isys->adev->dev, + "sof_event::csi2-%i sequence: %i, vc: %d, stream_id: %d\n", + csi2->index, ev.u.frame_sync.frame_sequence, vc, ip->stream_id); +} + +void ipu_isys_csi2_eof_event(struct ipu_isys_csi2 *csi2, unsigned int vc) +{ + struct ipu_isys_pipeline *ip = NULL; + unsigned long flags; + unsigned int i; + u32 frame_sequence; + + spin_lock_irqsave(&csi2->isys->lock, flags); + csi2->in_frame[vc] = false; + if (csi2->wait_for_sync[vc]) + complete(&csi2->eof_completion); + spin_unlock_irqrestore(&csi2->isys->lock, flags); + + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (csi2->isys->pipes[i] && + csi2->isys->pipes[i]->vc == vc && + csi2->isys->pipes[i]->csi2 == csi2) { + ip = csi2->isys->pipes[i]; + break; + } + } + + if (ip) { + frame_sequence = atomic_read(&ip->sequence); + + trace_ipu_eof_seqid(frame_sequence, csi2->index, vc); + + dev_dbg(&csi2->isys->adev->dev, + "eof_event::csi2-%i sequence: %i, vc: %d, stream_id: %d\n", + csi2->index, frame_sequence, vc, ip->stream_id); + } +} + +/* Call this function only _after_ the sensor has been stopped */ +void ipu_isys_csi2_wait_last_eof(struct ipu_isys_csi2 *csi2) +{ + unsigned long flags, tout; + unsigned int i; + + for (i = 0; i < NR_OF_CSI2_VC; i++) { + spin_lock_irqsave(&csi2->isys->lock, flags); + + if (!csi2->in_frame[i]) { + spin_unlock_irqrestore(&csi2->isys->lock, flags); + continue; + } + + reinit_completion(&csi2->eof_completion); + csi2->wait_for_sync[i] = true; + spin_unlock_irqrestore(&csi2->isys->lock, flags); + tout = wait_for_completion_timeout(&csi2->eof_completion, + IPU_EOF_TIMEOUT_JIFFIES); + if (!tout) + dev_err(&csi2->isys->adev->dev, + "csi2-%d: timeout at sync to eof of vc %d\n", + csi2->index, i); + csi2->wait_for_sync[i] = false; + } +} + +struct ipu_isys_buffer *ipu_isys_csi2_get_short_packet_buffer(struct + ipu_isys_pipeline + *ip) +{ + struct ipu_isys_buffer *ib; + struct ipu_isys_private_buffer *pb; + struct ipu_isys_mipi_packet_header *ph; + + if (list_empty(&ip->short_packet_incoming)) + return NULL; + ib = list_last_entry(&ip->short_packet_incoming, + struct ipu_isys_buffer, head); + pb = ipu_isys_buffer_to_private_buffer(ib); + ph = (struct ipu_isys_mipi_packet_header *)pb->buffer; + + /* Fill the packet header with magic number. */ + ph->word_count = 0xffff; + ph->dtype = 0xff; + + dma_sync_single_for_cpu(&ip->isys->adev->dev, pb->dma_addr, + sizeof(*ph), DMA_BIDIRECTIONAL); + return ib; +} diff --git a/drivers/media/pci/intel/ipu-isys-csi2.h b/drivers/media/pci/intel/ipu-isys-csi2.h new file mode 100644 index 000000000000..d7f2df3eb805 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-csi2.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_CSI2_H +#define IPU_ISYS_CSI2_H + +#include +#include + +#include "ipu-isys-queue.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" +#include "ipu-platform-isys.h" + +struct ipu_isys_csi2_timing; +struct ipu_isys_csi2_pdata; +struct ipu_isys; + +#define NR_OF_CSI2_SINK_PADS 1 +#define CSI2_PAD_SINK 0 +#define NR_OF_CSI2_STREAMS NR_OF_CSI2_VC +#define NR_OF_CSI2_SOURCE_PADS NR_OF_CSI2_STREAMS +#define CSI2_PAD_SOURCE(n) \ + ({ typeof(n) __n = (n); \ + (__n >= NR_OF_CSI2_SOURCE_PADS ? \ + (NR_OF_CSI2_PADS - 2) : \ + (__n + NR_OF_CSI2_SINK_PADS)); }) +#define NR_OF_CSI2_META_PADS 1 +#define NR_OF_CSI2_PADS \ + (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SOURCE_PADS + NR_OF_CSI2_META_PADS) +#define CSI2_PAD_META (NR_OF_CSI2_PADS - 1) + +#define IPU_ISYS_SHORT_PACKET_BUFFER_NUM VIDEO_MAX_FRAME +#define IPU_ISYS_SHORT_PACKET_WIDTH 32 +#define IPU_ISYS_SHORT_PACKET_FRAME_PACKETS 2 +#define IPU_ISYS_SHORT_PACKET_EXTRA_PACKETS 64 +#define IPU_ISYS_SHORT_PACKET_UNITSIZE 8 +#define IPU_ISYS_SHORT_PACKET_GENERAL_DT 0 +#define IPU_ISYS_SHORT_PACKET_PT 0 +#define IPU_ISYS_SHORT_PACKET_FT 0 + +#define IPU_ISYS_SHORT_PACKET_STRIDE \ + (IPU_ISYS_SHORT_PACKET_WIDTH * \ + IPU_ISYS_SHORT_PACKET_UNITSIZE) +#define IPU_ISYS_SHORT_PACKET_NUM(num_lines) \ + ((num_lines) * 2 + IPU_ISYS_SHORT_PACKET_FRAME_PACKETS + \ + IPU_ISYS_SHORT_PACKET_EXTRA_PACKETS) +#define IPU_ISYS_SHORT_PACKET_PKT_LINES(num_lines) \ + DIV_ROUND_UP(IPU_ISYS_SHORT_PACKET_NUM(num_lines) * \ + IPU_ISYS_SHORT_PACKET_UNITSIZE, \ + IPU_ISYS_SHORT_PACKET_STRIDE) +#define IPU_ISYS_SHORT_PACKET_BUF_SIZE(num_lines) \ + (IPU_ISYS_SHORT_PACKET_WIDTH * \ + IPU_ISYS_SHORT_PACKET_PKT_LINES(num_lines) * \ + IPU_ISYS_SHORT_PACKET_UNITSIZE) + +#define IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER 256 +#define IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE 16 +#define IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE \ + (IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER * \ + IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE) + +#define IPU_ISYS_SHORT_PACKET_FROM_RECEIVER 0 +#define IPU_ISYS_SHORT_PACKET_FROM_TUNIT 1 + +#define IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT 100 +#define IPU_ISYS_SHORT_PACKET_TRACE_EVENT_MASK 0x2082 +#define IPU_SKEW_CAL_LIMIT_HZ (1500000000ul / 2) + +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0 +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8 + +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0 +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2 + +#define IPU_EOF_TIMEOUT 300 +#define IPU_EOF_TIMEOUT_JIFFIES msecs_to_jiffies(IPU_EOF_TIMEOUT) + +/* + * struct ipu_isys_csi2 + * + * @nlanes: number of lanes in the receiver + */ +struct ipu_isys_csi2 { + struct ipu_isys_csi2_pdata *pdata; + struct ipu_isys *isys; + struct ipu_isys_subdev asd; + struct ipu_isys_video av[NR_OF_CSI2_SOURCE_PADS]; + struct ipu_isys_video av_meta; + struct completion eof_completion; + + void __iomem *base; + u32 receiver_errors; + unsigned int nlanes; + unsigned int index; + atomic_t sof_sequence; + bool in_frame[NR_OF_CSI2_VC]; + bool wait_for_sync[NR_OF_CSI2_VC]; + + unsigned int remote_streams; + unsigned int stream_count; + + struct v4l2_ctrl *store_csi2_header; +}; + +struct ipu_isys_csi2_timing { + u32 ctermen; + u32 csettle; + u32 dtermen; + u32 dsettle; +}; + +/* + * This structure defines the MIPI packet header output + * from IPU MIPI receiver. Due to hardware conversion, + * this structure is not the same as defined in CSI-2 spec. + */ +struct ipu_isys_mipi_packet_header { + u32 word_count:16, dtype:13, sync:2, stype:1; + u32 sid:4, port_id:4, reserved:23, odd_even:1; +} __packed; + +/* + * This structure defines the trace message content + * for CSI2 receiver monitor messages. + */ +struct ipu_isys_csi2_monitor_message { + u64 fe:1, + fs:1, + pe:1, + ps:1, + le:1, + ls:1, + reserved1:2, + sequence:2, + reserved2:2, + flash_shutter:4, + error_cause:12, + fifo_overrun:1, + crc_error:2, + reserved3:1, + timestamp_l:16, + port:4, vc:2, reserved4:2, frame_sync:4, reserved5:4; + u64 reserved6:3, + cmd:2, reserved7:1, monitor_id:7, reserved8:1, timestamp_h:50; +} __packed; + +#define to_ipu_isys_csi2(sd) container_of(to_ipu_isys_subdev(sd), \ + struct ipu_isys_csi2, asd) + +int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, __s64 *link_freq); +int ipu_isys_csi2_init(struct ipu_isys_csi2 *csi2, + struct ipu_isys *isys, + void __iomem *base, unsigned int index); +void ipu_isys_csi2_cleanup(struct ipu_isys_csi2 *csi2); +struct ipu_isys_buffer * +ipu_isys_csi2_get_short_packet_buffer(struct ipu_isys_pipeline *ip); +void ipu_isys_csi2_sof_event(struct ipu_isys_csi2 *csi2, unsigned int vc); +void ipu_isys_csi2_eof_event(struct ipu_isys_csi2 *csi2, unsigned int vc); +void ipu_isys_csi2_wait_last_eof(struct ipu_isys_csi2 *csi2); + +/* interface for platform specific */ +int ipu_isys_csi2_set_stream(struct v4l2_subdev *sd, + struct ipu_isys_csi2_timing timing, + unsigned int nlanes, int enable); +unsigned int ipu_isys_csi2_get_current_field(struct ipu_isys_pipeline *ip, + unsigned int *timestamp); +void ipu_isys_csi2_isr(struct ipu_isys_csi2 *csi2); +void ipu_isys_csi2_error(struct ipu_isys_csi2 *csi2); +bool ipu_isys_csi2_skew_cal_required(struct ipu_isys_csi2 *csi2); +int ipu_isys_csi2_set_skew_cal(struct ipu_isys_csi2 *csi2, int enable); + +#endif /* IPU_ISYS_CSI2_H */ diff --git a/drivers/media/pci/intel/ipu-isys-media.h b/drivers/media/pci/intel/ipu-isys-media.h new file mode 100644 index 000000000000..823324ef4a16 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-media.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_MEDIA_H +#define IPU_ISYS_MEDIA_H + +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) +#define is_media_entity_v4l2_subdev(e) \ + (media_entity_type(e) == MEDIA_ENT_T_V4L2_SUBDEV) +#define is_media_entity_v4l2_io(e) \ + (media_entity_type(e) == MEDIA_ENT_T_DEVNODE) +#define media_create_pad_link(a, b, c, d, e) \ + media_entity_create_link(a, b, c, d, e) +#define media_entity_pads_init(a, b, c) \ + media_entity_init(a, b, c, 0) +#define media_entity_id(ent) ((ent)->id) +#define media_entity_graph_walk_init(a, b) 0 +#define media_entity_graph_walk_cleanup(a) do { } while (0) + +#define IPU_COMPAT_MAX_ENTITIES MEDIA_ENTITY_ENUM_MAX_ID + +struct media_entity_enum { + unsigned long *bmap; + int idx_max; +}; + +static inline int media_entity_enum_init(struct media_entity_enum *ent_enum, + struct media_device *mdev) +{ + int idx_max = IPU_COMPAT_MAX_ENTITIES; + + ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG), + sizeof(long), GFP_KERNEL); + if (!ent_enum->bmap) + return -ENOMEM; + + bitmap_zero(ent_enum->bmap, idx_max); + + ent_enum->idx_max = idx_max; + return 0; +} + +static inline void media_entity_enum_cleanup(struct media_entity_enum *ent_enum) +{ + kfree(ent_enum->bmap); +} + +static inline void media_entity_enum_set(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (media_entity_id(entity) >= ent_enum->idx_max) { + WARN_ON(1); + return; + } + __set_bit(media_entity_id(entity), ent_enum->bmap); +} + +static inline void media_entity_enum_zero(struct media_entity_enum *ent_enum) +{ + bitmap_zero(ent_enum->bmap, ent_enum->idx_max); +} + +static inline bool media_entity_enum_test(struct media_entity_enum *ent_enum, + struct media_entity *entity) +{ + if (media_entity_id(entity) >= ent_enum->idx_max) { + WARN_ON(1); + return false; + } + + return test_bit(media_entity_id(entity), ent_enum->bmap); +} +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#define media_pipeline_start(e, p) media_entity_pipeline_start(e, p) + +#define media_pipeline_stop(e) media_entity_pipeline_stop(e) + +#define media_graph_walk_init(g, d) media_entity_graph_walk_init(g, d) + +#define media_graph_walk_start(g, p) media_entity_graph_walk_start(g, p) + +#define media_graph_walk_next(g) media_entity_graph_walk_next(g) + +#define media_graph_walk_cleanup(g) media_entity_graph_walk_cleanup(g) +#endif + + +#endif /* IPU_ISYS_MEDIA_H */ diff --git a/drivers/media/pci/intel/ipu-isys-queue.c b/drivers/media/pci/intel/ipu-isys-queue.c new file mode 100644 index 000000000000..11f7da5dd0c7 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-queue.c @@ -0,0 +1,1530 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include + +#include +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-buttress.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2.h" +#include "ipu-isys-video.h" + +static bool wall_clock_ts_on; +module_param(wall_clock_ts_on, bool, 0660); +MODULE_PARM_DESC(wall_clock_ts_on, "Timestamp based on REALTIME clock"); + +static int queue_setup(struct vb2_queue *q, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + const struct v4l2_format *__fmt, +#endif + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + void *alloc_ctxs[] +#else + struct device *alloc_devs[] +#endif + ) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + const struct v4l2_format *fmt = __fmt; + const struct ipu_isys_pixelformat *pfmt; + struct v4l2_pix_format_mplane mpix; +#else + bool use_fmt = false; +#endif + unsigned int i; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + if (fmt) + mpix = fmt->fmt.pix_mp; + else + mpix = av->mpix; + + pfmt = av->try_fmt_vid_mplane(av, &mpix); + + *num_planes = mpix.num_planes; +#else + /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ + if (!*num_planes) { + use_fmt = true; + *num_planes = av->mpix.num_planes; + } +#endif + + for (i = 0; i < *num_planes; i++) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + sizes[i] = mpix.plane_fmt[i].sizeimage; +#else + if (use_fmt) + sizes[i] = av->mpix.plane_fmt[i].sizeimage; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + alloc_ctxs[i] = aq->ctx; +#else + alloc_devs[i] = aq->dev; +#endif + dev_dbg(&av->isys->adev->dev, + "%s: queue setup: plane %d size %u\n", + av->vdev.name, i, sizes[i]); + } + + return 0; +} + +void ipu_isys_queue_lock(struct vb2_queue *q) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&av->isys->adev->dev, "%s: queue lock\n", av->vdev.name); + mutex_lock(&av->mutex); +} + +void ipu_isys_queue_unlock(struct vb2_queue *q) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&av->isys->adev->dev, "%s: queue unlock\n", av->vdev.name); + mutex_unlock(&av->mutex); +} + +static int buf_init(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&av->isys->adev->dev, "buffer: %s: %s\n", av->vdev.name, + __func__); + + if (aq->buf_init) + return aq->buf_init(vb); + + return 0; +} + +int ipu_isys_buf_prepare(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&av->isys->adev->dev, + "buffer: %s: configured size %u, buffer size %lu\n", + av->vdev.name, + av->mpix.plane_fmt[0].sizeimage, vb2_plane_size(vb, 0)); + + if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0)) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline * + av->mpix.height); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_planes[0].data_offset = av->line_header_length / BITS_PER_BYTE; +#else + vb->planes[0].data_offset = av->line_header_length / BITS_PER_BYTE; +#endif + + return 0; +} + +static int buf_prepare(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_buffer *ib = vb2_buffer_to_ipu_isys_buffer(vb); + u32 request = +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + to_vb2_v4l2_buffer(vb)->request; +#else + vb->v4l2_buf.request; +#endif + struct media_device *mdev = &av->isys->media_dev; + struct ipu_isys_request *ireq; + u32 request_state; + unsigned long flags; + int rval; + if (av->isys->adev->isp->flr_done) + return -EIO; + + if (request) { + ib->req = media_device_request_find(&av->isys->media_dev, + request); + if (!ib->req) { + dev_dbg(&av->isys->adev->dev, + "can't find request %u\n", request); + return -ENOENT; + } + } + + rval = aq->buf_prepare(vb); + if (!request) + return rval; + if (rval) + goto out_put_request; + + ireq = to_ipu_isys_request(ib->req); + + spin_lock_irqsave(&ireq->lock, flags); + spin_lock(&mdev->req_lock); + request_state = ib->req->state; + if (request_state == MEDIA_DEVICE_REQUEST_STATE_IDLE) + list_add(&ib->req_head, &ireq->buffers); + spin_unlock(&mdev->req_lock); + spin_unlock_irqrestore(&ireq->lock, flags); + if (request_state != MEDIA_DEVICE_REQUEST_STATE_IDLE) { + dev_dbg(&av->isys->adev->dev, + "%s: request %u state %u\n", __func__, ib->req->id, + request_state); + rval = -EINVAL; + } else { + dev_dbg(&av->isys->adev->dev, + "%s: request %u\n", __func__, ib->req->id); + } + + if (!rval) + return 0; + +out_put_request: + media_device_request_put(ib->req); + ib->req = NULL; + + return rval; +} + +static void buf_finish(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_buffer *ib = vb2_buffer_to_ipu_isys_buffer(vb); + dev_dbg(&av->isys->adev->dev, "buffer: %s: %s\n", av->vdev.name, + __func__); + + if (ib->req) { + struct ipu_isys_request *ireq = to_ipu_isys_request(ib->req); + unsigned long flags; + bool done; + + spin_lock_irqsave(&ireq->lock, flags); + list_del(&ib->req_head); + done = list_empty(&ireq->buffers); + spin_unlock_irqrestore(&ireq->lock, flags); + dev_dbg(&av->isys->adev->dev, "request %u complete %s\n", + ib->req->id, done ? "true" : "false"); + if (done) { + media_device_request_complete(&av->isys->media_dev, + ib->req); + mutex_lock(&av->isys->stream_mutex); + list_del(&ireq->head); + mutex_unlock(&av->isys->stream_mutex); + } + media_device_request_put(ib->req); + ib->req = NULL; + } +} + +static void buf_cleanup(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&av->isys->adev->dev, "buffer: %s: %s\n", av->vdev.name, + __func__); + + if (aq->buf_cleanup) + return aq->buf_cleanup(vb); +} + +/* + * Queue a buffer list back to incoming or active queues. The buffers + * are removed from the buffer list. + */ +void ipu_isys_buffer_list_queue(struct ipu_isys_buffer_list *bl, + unsigned long op_flags, + enum vb2_buffer_state state) +{ + struct ipu_isys_buffer *ib, *ib_safe; + unsigned long flags; + bool first = true; + + if (!bl) + return; + + WARN_ON(!bl->nbufs); + WARN_ON(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && + op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); + + list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { + struct ipu_isys_video *av; + + if (ib->type == IPU_ISYS_VIDEO_BUFFER) { + struct vb2_buffer *vb = + ipu_isys_buffer_to_vb2_buffer(ib); + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + + av = ipu_isys_queue_to_video(aq); + spin_lock_irqsave(&aq->lock, flags); + list_del(&ib->head); + if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) + list_add(&ib->head, &aq->active); + else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) + list_add_tail(&ib->head, &aq->incoming); + spin_unlock_irqrestore(&aq->lock, flags); + + if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) + vb2_buffer_done(vb, state); + } else if (ib->type == IPU_ISYS_SHORT_PACKET_BUFFER) { + struct ipu_isys_private_buffer *pb = + ipu_isys_buffer_to_private_buffer(ib); + struct ipu_isys_pipeline *ip = pb->ip; + + av = container_of(ip, struct ipu_isys_video, ip); + spin_lock_irqsave(&ip->short_packet_queue_lock, flags); + list_del(&ib->head); + if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) + list_add(&ib->head, &ip->short_packet_active); + else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) + list_add(&ib->head, &ip->short_packet_incoming); + spin_unlock_irqrestore(&ip->short_packet_queue_lock, + flags); + } else { + WARN_ON(1); + return; + } + + if (first) { + dev_dbg(&av->isys->adev->dev, + "queue buffer list %p op_flags %lx, state %d, %d buffers\n", + bl, op_flags, state, bl->nbufs); + first = false; + } + + bl->nbufs--; + } + + WARN_ON(bl->nbufs); +} + +/* + * flush_firmware_streamon_fail() - Flush in cases where requests may + * have been queued to firmware and the *firmware streamon fails for a + * reason or another. + */ +static void flush_firmware_streamon_fail(struct ipu_isys_pipeline *ip) +{ + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + struct ipu_isys_queue *aq; + unsigned long flags; + + lockdep_assert_held(&pipe_av->mutex); + + list_for_each_entry(aq, &ip->queues, node) { + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_buffer *ib, *ib_safe; + + spin_lock_irqsave(&aq->lock, flags); + list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { + struct vb2_buffer *vb = + ipu_isys_buffer_to_vb2_buffer(ib); + + list_del(&ib->head); + if (av->streaming) { + dev_dbg(&av->isys->adev->dev, + "%s: queue buffer %u back to incoming\n", + av->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index); +#else + vb->index); +#endif + /* Queue already streaming, return to driver. */ + list_add(&ib->head, &aq->incoming); + continue; + } + /* Queue not yet streaming, return to user. */ + dev_dbg(&av->isys->adev->dev, + "%s: return %u back to videobuf2\n", + av->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index); +#else + vb->index); +#endif + vb2_buffer_done(ipu_isys_buffer_to_vb2_buffer(ib), + VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&aq->lock, flags); + } +} + +/* + * Attempt obtaining a buffer list from the incoming queues, a list of + * buffers that contains one entry from each video buffer queue. If + * all queues have no buffers, the buffers that were already dequeued + * are returned to their queues. + */ +static int buffer_list_get(struct ipu_isys_pipeline *ip, + struct ipu_isys_buffer_list *bl) +{ + struct ipu_isys_queue *aq; + struct ipu_isys_buffer *ib; + unsigned long flags; + int ret = 0; + + bl->nbufs = 0; + INIT_LIST_HEAD(&bl->head); + + list_for_each_entry(aq, &ip->queues, node) { + struct ipu_isys_buffer *ib; + + spin_lock_irqsave(&aq->lock, flags); + if (list_empty(&aq->incoming)) { + spin_unlock_irqrestore(&aq->lock, flags); + ret = -ENODATA; + goto error; + } + + ib = list_last_entry(&aq->incoming, + struct ipu_isys_buffer, head); + if (ib->req) { + spin_unlock_irqrestore(&aq->lock, flags); + ret = -ENODATA; + goto error; + } + + dev_dbg(&ip->isys->adev->dev, "buffer: %s: buffer %u\n", + ipu_isys_queue_to_video(aq)->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + ipu_isys_buffer_to_vb2_buffer(ib)->v4l2_buf.index +#else + ipu_isys_buffer_to_vb2_buffer(ib)->index +#endif + ); + list_del(&ib->head); + list_add(&ib->head, &bl->head); + spin_unlock_irqrestore(&aq->lock, flags); + + bl->nbufs++; + } + + list_for_each_entry(ib, &bl->head, head) { + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + + aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + if (aq->prepare_frame_buff_set) + aq->prepare_frame_buff_set(vb); + } + + /* Get short packet buffer. */ + if (ip->interlaced && ip->isys->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) { + spin_lock_irqsave(&ip->short_packet_queue_lock, flags); + ib = ipu_isys_csi2_get_short_packet_buffer(ip); + if (!ib) { + spin_unlock_irqrestore(&ip->short_packet_queue_lock, + flags); + ret = -ENODATA; + dev_err(&ip->isys->adev->dev, + "No more short packet buffers. Driver bug?"); + WARN_ON(1); + goto error; + } + list_move(&ib->head, &bl->head); + spin_unlock_irqrestore(&ip->short_packet_queue_lock, flags); + bl->nbufs++; + } + + dev_dbg(&ip->isys->adev->dev, "get buffer list %p, %u buffers\n", bl, + bl->nbufs); + return ret; + +error: + if (!list_empty(&bl->head)) + ipu_isys_buffer_list_queue(bl, + IPU_ISYS_BUFFER_LIST_FL_INCOMING, 0); + return ret; +} + +void ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin( + struct vb2_buffer *vb, + struct ipu_fw_isys_frame_buff_set_abi *set) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + + set->output_pins[aq->fw_output].addr = + vb2_dma_contig_plane_dma_addr(vb, 0); + set->output_pins[aq->fw_output].out_buf_id = +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index + 1; +#else + vb->index + 1; +#endif +} + +/* + * Convert a buffer list to a isys fw ABI framebuffer set. The + * buffer list is not modified. + */ +void ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set( + struct ipu_fw_isys_frame_buff_set_abi *set, + struct ipu_isys_pipeline *ip, + struct ipu_isys_buffer_list *bl) +{ + struct ipu_isys_buffer *ib; + + WARN_ON(!bl->nbufs); + + set->send_irq_sof = 1; + set->send_resp_sof = 1; + set->send_irq_eof = 1; + set->send_resp_eof = 1; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + set->send_irq_capture_ack = 1; + set->send_irq_capture_done = 1; +#endif + + list_for_each_entry(ib, &bl->head, head) { + if (ib->type == IPU_ISYS_VIDEO_BUFFER) { + struct vb2_buffer *vb = + ipu_isys_buffer_to_vb2_buffer(ib); + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + + if (aq->fill_frame_buff_set_pin) + aq->fill_frame_buff_set_pin(vb, set); + } else if (ib->type == IPU_ISYS_SHORT_PACKET_BUFFER) { + struct ipu_isys_private_buffer *pb = + ipu_isys_buffer_to_private_buffer(ib); + struct ipu_fw_isys_output_pin_payload_abi *output_pin = + &set->output_pins[ip->short_packet_output_pin]; + + output_pin->addr = pb->dma_addr; + output_pin->out_buf_id = pb->index + 1; + } else { + WARN_ON(1); + } + } +} + +static void +ipu_isys_req_dispatch(struct media_device *mdev, + struct ipu_isys_request *ireq, + struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_frame_buff_set_abi *set, + dma_addr_t dma_addr); + +struct ipu_isys_request *ipu_isys_next_queued_request(struct ipu_isys_pipeline + *ip) +{ + struct ipu_isys *isys = + container_of(ip, struct ipu_isys_video, ip)->isys; + struct ipu_isys_request *ireq; + struct ipu_isys_buffer *ib; + unsigned long flags; + + lockdep_assert_held(&isys->stream_mutex); + + if (list_empty(&isys->requests)) { + dev_dbg(&isys->adev->dev, "%s: no requests found\n", __func__); + return NULL; + } + + list_for_each_entry_reverse(ireq, &isys->requests, head) { + /* Does the request belong to this pipeline? */ + bool is_ours = false; + bool is_others = false; + + dev_dbg(&isys->adev->dev, "%s: checking request %u\n", + __func__, ireq->req.id); + + spin_lock_irqsave(&ireq->lock, flags); + list_for_each_entry(ib, &ireq->buffers, req_head) { + struct vb2_buffer *vb = + ipu_isys_buffer_to_vb2_buffer(ib); + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&isys->adev->dev, "%s: buffer in vdev %s\n", + __func__, av->vdev.name); + + if (media_entity_enum_test(&ip->entity_enum, + &av->vdev.entity)) + is_ours = true; + else + is_others = true; + } + spin_unlock_irqrestore(&ireq->lock, flags); + + dev_dbg(&isys->adev->dev, "%s: is%s ours, is%s others'\n", + __func__, is_ours ? "" : "n't", is_others ? "" : "n't"); + + if (!is_ours || WARN_ON(is_others)) + continue; + + list_del_init(&ireq->head); + + return ireq; + } + + return NULL; +} + +/* Start streaming for real. The buffer list must be available. */ +static int ipu_isys_stream_start(struct ipu_isys_pipeline *ip, + struct ipu_isys_buffer_list *bl, bool error) +{ + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + struct media_device *mdev = &pipe_av->isys->media_dev; + struct ipu_isys_buffer_list __bl; + struct ipu_isys_request *ireq; + int rval; + + mutex_lock(&pipe_av->isys->stream_mutex); + + rval = ipu_isys_video_set_streaming(pipe_av, 1, bl); + if (rval) { + mutex_unlock(&pipe_av->isys->stream_mutex); + goto out_requeue; + } + + ip->streaming = 1; + + dev_dbg(&pipe_av->isys->adev->dev, "dispatching queued requests\n"); + + while ((ireq = ipu_isys_next_queued_request(ip))) { + struct ipu_fw_isys_frame_buff_set_abi *set; + struct isys_fw_msgs *msg; + + msg = ipu_get_fw_msg_buf(ip); + if (!msg) { + /* TODO: A PROPER CLEAN UP */ + mutex_unlock(&pipe_av->isys->stream_mutex); + return -ENOMEM; + } + + set = to_frame_msg_buf(msg); + + rval = ipu_isys_req_prepare(mdev, ireq, ip, set); + if (rval) { + mutex_unlock(&pipe_av->isys->stream_mutex); + goto out_requeue; + } + + ipu_fw_isys_dump_frame_buff_set(&pipe_av->isys->adev->dev, set, + ip->nr_output_pins); + ipu_isys_req_dispatch(mdev, ireq, ip, set, to_dma_addr(msg)); + } + + dev_dbg(&pipe_av->isys->adev->dev, + "done dispatching queued requests\n"); + + mutex_unlock(&pipe_av->isys->stream_mutex); + + bl = &__bl; + + do { + struct ipu_fw_isys_frame_buff_set_abi *buf = NULL; + struct isys_fw_msgs *msg; + + rval = buffer_list_get(ip, bl); + if (rval == -EINVAL) + goto out_requeue; + else if (rval < 0) + break; + + msg = ipu_get_fw_msg_buf(ip); + if (!msg) + /* TODO: PROPER CLEANUP */ + return -ENOMEM; + + buf = to_frame_msg_buf(msg); + + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set(buf, ip, bl); + + ipu_fw_isys_dump_frame_buff_set(&pipe_av->isys->adev->dev, buf, + ip->nr_output_pins); + + ipu_isys_buffer_list_queue(bl, + IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + + rval = ipu_fw_isys_complex_cmd(pipe_av->isys, + ip->stream_handle, + buf, to_dma_addr(msg), + sizeof(*buf), + IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); + ipu_put_fw_mgs_buffer(pipe_av->isys, (uintptr_t) buf); + } while (!WARN_ON(rval)); + + return 0; + +out_requeue: + if (bl && bl->nbufs) + ipu_isys_buffer_list_queue(bl, + IPU_ISYS_BUFFER_LIST_FL_INCOMING | + (error ? + IPU_ISYS_BUFFER_LIST_FL_SET_STATE : + 0), + error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + flush_firmware_streamon_fail(ip); + + return rval; +} + +static void __buf_queue(struct vb2_buffer *vb, bool force) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_buffer *ib = vb2_buffer_to_ipu_isys_buffer(vb); + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct ipu_isys_buffer_list bl; + + struct ipu_fw_isys_frame_buff_set_abi *buf = NULL; + struct isys_fw_msgs *msg; + + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + unsigned long flags; + unsigned int i; + int rval; + + dev_dbg(&av->isys->adev->dev, "buffer: %s: buf_queue %u\n", + av->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index +#else + vb->index +#endif + ); + + for (i = 0; i < vb->num_planes; i++) + dev_dbg(&av->isys->adev->dev, "iova: plane %u iova 0x%x\n", i, + (u32) vb2_dma_contig_plane_dma_addr(vb, i)); + + spin_lock_irqsave(&aq->lock, flags); + list_add(&ib->head, &aq->incoming); + spin_unlock_irqrestore(&aq->lock, flags); + + if (ib->req) + return; + + if (!pipe_av || !vb->vb2_queue->streaming) { + dev_dbg(&av->isys->adev->dev, + "not pipe_av set, adding to incoming\n"); + return; + } + + mutex_unlock(&av->mutex); + mutex_lock(&pipe_av->mutex); + + if (!force && ip->nr_streaming != ip->nr_queues) { + dev_dbg(&av->isys->adev->dev, + "not streaming yet, adding to incoming\n"); + goto out; + } + + /* + * We just put one buffer to the incoming list of this queue + * (above). Let's see whether all queues in the pipeline would + * have a buffer. + */ + rval = buffer_list_get(ip, &bl); + if (rval < 0) { + if (rval == -EINVAL) { + dev_err(&av->isys->adev->dev, + "error: should not happen\n"); + WARN_ON(1); + } else { + dev_dbg(&av->isys->adev->dev, + "not enough buffers available\n"); + } + goto out; + } + + msg = ipu_get_fw_msg_buf(ip); + if (!msg) { + rval = -ENOMEM; + goto out; + } + buf = to_frame_msg_buf(msg); + + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set(buf, ip, &bl); + + ipu_fw_isys_dump_frame_buff_set(&pipe_av->isys->adev->dev, buf, + ip->nr_output_pins); + + if (!ip->streaming) { + dev_dbg(&av->isys->adev->dev, + "Wow! Got a buffer to start streaming!\n"); + rval = ipu_isys_stream_start(ip, &bl, true); + if (rval) + dev_err(&av->isys->adev->dev, + "Ouch. Stream start failed.\n"); + goto out; + } + + /* + * We must queue the buffers in the buffer list to the + * appropriate video buffer queues BEFORE passing them to the + * firmware since we could get a buffer event back before we + * have queued them ourselves to the active queue. + */ + ipu_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + + rval = ipu_fw_isys_complex_cmd(pipe_av->isys, + ip->stream_handle, + buf, to_dma_addr(msg), + sizeof(*buf), + IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); + ipu_put_fw_mgs_buffer(pipe_av->isys, (uintptr_t) buf); + /* + * FIXME: mark the buffers in the buffer list if the queue + * operation fails. + */ + if (!WARN_ON(rval < 0)) + dev_dbg(&av->isys->adev->dev, "queued buffer\n"); + +out: + mutex_unlock(&pipe_av->mutex); + mutex_lock(&av->mutex); +} + +static void buf_queue(struct vb2_buffer *vb) +{ + __buf_queue(vb, false); +} + +int ipu_isys_link_fmt_validate(struct ipu_isys_queue *aq) +{ + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct v4l2_subdev_format fmt = { 0 }; + struct media_pad *pad = media_entity_remote_pad(av->vdev.entity.pads); + struct v4l2_subdev *sd; + int rval; + + if (!pad) { + dev_dbg(&av->isys->adev->dev, + "video node %s pad not connected\n", av->vdev.name); + return -ENOTCONN; + } + + sd = media_entity_to_v4l2_subdev(pad->entity); + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = pad->index; + fmt.stream = 0; + rval = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (rval) + return rval; + + if (fmt.format.width != av->mpix.width || + fmt.format.height != av->mpix.height) { + dev_dbg(&av->isys->adev->dev, + "wrong width or height %ux%u (%ux%u expected)\n", + av->mpix.width, av->mpix.height, + fmt.format.width, fmt.format.height); + return -EINVAL; + } + + if (fmt.format.field != av->mpix.field) { + dev_dbg(&av->isys->adev->dev, + "wrong field value 0x%8.8x (0x%8.8x expected)\n", + av->mpix.field, fmt.format.field); + return -EINVAL; + } + + if (fmt.format.code != av->pfmt->code) { + dev_dbg(&av->isys->adev->dev, + "wrong media bus code 0x%8.8x (0x%8.8x expected)\n", + av->pfmt->code, fmt.format.code); + return -EINVAL; + } + + return 0; +} + +/* Return buffers back to videobuf2. */ +static void return_buffers(struct ipu_isys_queue *aq, + enum vb2_buffer_state state) +{ + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + int reset_needed = 0; + unsigned long flags; + + spin_lock_irqsave(&aq->lock, flags); + while (!list_empty(&aq->incoming)) { + struct ipu_isys_buffer *ib = list_first_entry(&aq->incoming, + struct + ipu_isys_buffer, + head); + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + vb2_buffer_done(vb, state); + + dev_dbg(&av->isys->adev->dev, + "%s: stop_streaming incoming %u\n", + ipu_isys_queue_to_video(vb2_queue_to_ipu_isys_queue + (vb->vb2_queue))->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index); +#else + vb->index); +#endif + + spin_lock_irqsave(&aq->lock, flags); + } + + /* + * Something went wrong (FW crash / HW hang / not all buffers + * returned from isys) if there are still buffers queued in active + * queue. We have to clean up places a bit. + */ + while (!list_empty(&aq->active)) { + struct ipu_isys_buffer *ib = list_first_entry(&aq->active, + struct + ipu_isys_buffer, + head); + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + vb2_buffer_done(vb, state); + + dev_warn(&av->isys->adev->dev, "%s: cleaning active queue %u\n", + ipu_isys_queue_to_video(vb2_queue_to_ipu_isys_queue + (vb->vb2_queue))->vdev.name, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index); +#else + vb->index); +#endif + + spin_lock_irqsave(&aq->lock, flags); + reset_needed = 1; + } + + spin_unlock_irqrestore(&aq->lock, flags); + + if (reset_needed) { + mutex_lock(&av->isys->mutex); + av->isys->reset_needed = true; + mutex_unlock(&av->isys->mutex); + } +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_video *pipe_av; + struct ipu_isys_pipeline *ip; + struct ipu_isys_buffer_list __bl, *bl = NULL; + bool first; + int rval; + + dev_dbg(&av->isys->adev->dev, + "stream: %s: width %u, height %u, css pixelformat %u\n", + av->vdev.name, av->mpix.width, av->mpix.height, + av->pfmt->css_pixelformat); + + mutex_lock(&av->isys->stream_mutex); + + first = !av->vdev.entity.pipe; + + if (first) { + rval = ipu_isys_video_prepare_streaming(av, 1); + if (rval) + goto out_return_buffers; + } + + mutex_unlock(&av->isys->stream_mutex); + + rval = aq->link_fmt_validate(aq); + if (rval) { + dev_dbg(&av->isys->adev->dev, + "%s: link format validation failed (%d)\n", + av->vdev.name, rval); + goto out_unprepare_streaming; + } + + ip = to_ipu_isys_pipeline(av->vdev.entity.pipe); + pipe_av = container_of(ip, struct ipu_isys_video, ip); + mutex_unlock(&av->mutex); + + mutex_lock(&pipe_av->mutex); + ip->nr_streaming++; + dev_dbg(&av->isys->adev->dev, "queue %u of %u\n", ip->nr_streaming, + ip->nr_queues); + list_add(&aq->node, &ip->queues); + if (ip->nr_streaming != ip->nr_queues) + goto out; + + if (list_empty(&av->isys->requests)) { + bl = &__bl; + rval = buffer_list_get(ip, bl); + if (rval == -EINVAL) { + goto out_stream_start; + } else if (rval < 0) { + dev_dbg(&av->isys->adev->dev, + "no request available --- postponing streamon\n"); + goto out; + } + } + + rval = ipu_isys_stream_start(ip, bl, false); + if (rval) + goto out_stream_start; + +out: + mutex_unlock(&pipe_av->mutex); + mutex_lock(&av->mutex); + + return 0; + +out_stream_start: + list_del(&aq->node); + ip->nr_streaming--; + mutex_unlock(&pipe_av->mutex); + mutex_lock(&av->mutex); + +out_unprepare_streaming: + mutex_lock(&av->isys->stream_mutex); + if (first) + ipu_isys_video_prepare_streaming(av, 0); + +out_return_buffers: + mutex_unlock(&av->isys->stream_mutex); + return_buffers(aq, VB2_BUF_STATE_QUEUED); + + return rval; +} + +static void stop_streaming(struct vb2_queue *q) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + + if (pipe_av != av) { + mutex_unlock(&av->mutex); + mutex_lock(&pipe_av->mutex); + } + + mutex_lock(&av->isys->stream_mutex); + if (ip->nr_streaming == ip->nr_queues && ip->streaming) + ipu_isys_video_set_streaming(av, 0, NULL); + if (ip->nr_streaming == 1) + ipu_isys_video_prepare_streaming(av, 0); + mutex_unlock(&av->isys->stream_mutex); + + ip->nr_streaming--; + list_del(&aq->node); + ip->streaming = 0; + + if (pipe_av != av) { + mutex_unlock(&pipe_av->mutex); + mutex_lock(&av->mutex); + } + + return_buffers(aq, VB2_BUF_STATE_ERROR); +} + +static unsigned int +get_sof_sequence_by_timestamp(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct ipu_isys *isys = + container_of(ip, struct ipu_isys_video, ip)->isys; + u64 time = (u64) info->timestamp[1] << 32 | info->timestamp[0]; + unsigned int i; + + for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) + if (time == ip->seq[i].timestamp) { + dev_dbg(&isys->adev->dev, + "sof: using sequence number %u for timestamp 0x%16.16llx\n", + ip->seq[i].sequence, time); + return ip->seq[i].sequence; + } + + dev_dbg(&isys->adev->dev, "SOF: looking for 0x%16.16llx\n", time); + for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) + dev_dbg(&isys->adev->dev, + "SOF: sequence %u, timestamp value 0x%16.16llx\n", + ip->seq[i].sequence, ip->seq[i].timestamp); + dev_dbg(&isys->adev->dev, "SOF sequence number not found\n"); + + return 0; +} + +static u64 get_sof_ns_delta(struct ipu_isys_video *av, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(&av->isys->adev->dev); + struct ipu_device *isp = adev->isp; + u64 delta, tsc_now; + + if (!ipu_buttress_tsc_read(isp, &tsc_now)) + delta = tsc_now - + ((u64) info->timestamp[1] << 32 | info->timestamp[0]); + else + delta = 0; + + return ipu_buttress_tsc_ticks_to_ns(delta); +} + +void +ipu_isys_buf_calc_sequence_time(struct ipu_isys_buffer *ib, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct timespec ts_now; +#endif + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + u64 ns; + u32 sequence; + + if (ip->has_sof) { + ns = (wall_clock_ts_on) ? ktime_get_real_ns() : ktime_get_ns(); + ns -= get_sof_ns_delta(av, info); + sequence = get_sof_sequence_by_timestamp(ip, info); + } else { + ns = ((wall_clock_ts_on) ? ktime_get_real_ns() : + ktime_get_ns()); + sequence = (atomic_inc_return(&ip->sequence) - 1) + / ip->nr_queues; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.sequence = sequence; + ts_now = ns_to_timespec(ns); + vb->v4l2_buf.timestamp.tv_sec = ts_now.tv_sec; + vb->v4l2_buf.timestamp.tv_usec = ts_now.tv_nsec / NSEC_PER_USEC; + + dev_dbg(&av->isys->adev->dev, "buffer: %s: buffer done %u\n", + av->vdev.name, vb->v4l2_buf.index); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + vbuf->sequence = sequence; + ts_now = ns_to_timespec(ns); + vbuf->timestamp.tv_sec = ts_now.tv_sec; + vbuf->timestamp.tv_usec = ts_now.tv_nsec / NSEC_PER_USEC; + + dev_dbg(&av->isys->adev->dev, "%s: buffer done %u\n", av->vdev.name, + vb->index); +#else + vbuf->vb2_buf.timestamp = ns; + vbuf->sequence = sequence; + + dev_dbg(&av->isys->adev->dev, "buffer: %s: buffer done %u\n", + av->vdev.name, vb->index); +#endif +} + +void ipu_isys_queue_buf_done(struct ipu_isys_buffer *ib) +{ + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + + if (atomic_read(&ib->str2mmio_flag)) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + /* + * Operation on buffer is ended with error and will be reported + * to the userspace when it is de-queued + */ + atomic_set(&ib->str2mmio_flag, 0); + } else { + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } +} + +void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct ipu_isys *isys = + container_of(ip, struct ipu_isys_video, ip)->isys; + struct ipu_isys_queue *aq = ip->output_pins[info->pin_id].aq; + struct ipu_isys_buffer *ib; + struct vb2_buffer *vb; + unsigned long flags; + bool first = true; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + struct v4l2_buffer *buf; +#else + struct vb2_v4l2_buffer *buf; +#endif + + dev_dbg(&isys->adev->dev, "buffer: %s: received buffer %8.8x\n", + ipu_isys_queue_to_video(aq)->vdev.name, info->pin.addr); + + spin_lock_irqsave(&aq->lock, flags); + if (list_empty(&aq->active)) { + spin_unlock_irqrestore(&aq->lock, flags); + dev_err(&isys->adev->dev, "active queue empty\n"); + return; + } + + list_for_each_entry_reverse(ib, &aq->active, head) { + dma_addr_t addr; + + vb = ipu_isys_buffer_to_vb2_buffer(ib); + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (info->pin.addr != addr) { + if (first) + dev_err(&isys->adev->dev, + "WARNING: buffer address %pad expected!\n", + &addr); + first = false; + continue; + } + + if (info->error_info.error == + IPU_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { + /* + * Check for error message: + * 'IPU_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO' + */ + atomic_set(&ib->str2mmio_flag, 1); + } + dev_dbg(&isys->adev->dev, "buffer: found buffer %pad\n", &addr); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + buf = &vb->v4l2_buf; +#else + buf = to_vb2_v4l2_buffer(vb); +#endif + buf->field = V4L2_FIELD_NONE; + + /* + * Use "reserved" field to pass csi2 index and vc. + * May need to change to other approach. + */ + buf->reserved &= 0xFFFFFF00; + if (ip->csi2) + buf->reserved |= ip->csi2->index << 4; + buf->reserved |= ip->vc; + + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + ipu_isys_buf_calc_sequence_time(ib, info); + + /* + * For interlaced buffers, the notification to user space + * is postponed to capture_done event since the field + * information is available only at that time. + */ + if (ip->interlaced) { + spin_lock_irqsave(&ip->short_packet_queue_lock, flags); + list_add(&ib->head, &ip->pending_interlaced_bufs); + spin_unlock_irqrestore(&ip->short_packet_queue_lock, + flags); + } else { + ipu_isys_queue_buf_done(ib); + } + + return; + } + + dev_err(&isys->adev->dev, + "WARNING: cannot find a matching video buffer!\n"); + + spin_unlock_irqrestore(&aq->lock, flags); +} + +void +ipu_isys_queue_short_packet_ready(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct ipu_isys *isys = + container_of(ip, struct ipu_isys_video, ip)->isys; + unsigned long flags; + + dev_dbg(&isys->adev->dev, "receive short packet buffer %8.8x\n", + info->pin.addr); + spin_lock_irqsave(&ip->short_packet_queue_lock, flags); + ip->cur_field = ipu_isys_csi2_get_current_field(ip, info->timestamp); + spin_unlock_irqrestore(&ip->short_packet_queue_lock, flags); +} + +void ipu_isys_req_free(struct media_device *mdev, + struct media_device_request *req) +{ + struct ipu_isys_request *ireq = to_ipu_isys_request(req); + + kfree(ireq); +} + +struct +media_device_request *ipu_isys_req_alloc(struct media_device *mdev) +{ + struct ipu_isys_request *ireq; + + ireq = kzalloc(sizeof(*ireq), GFP_KERNEL); + if (!ireq) + return NULL; + + INIT_LIST_HEAD(&ireq->buffers); + spin_lock_init(&ireq->lock); + INIT_LIST_HEAD(&ireq->head); + + return &ireq->req; +} + +int ipu_isys_req_prepare(struct media_device *mdev, + struct ipu_isys_request *ireq, + struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_frame_buff_set_abi *set) +{ + struct ipu_isys *isys = + container_of(ip, struct ipu_isys_video, ip)->isys; + struct media_device_request *req = &ireq->req; + struct ipu_isys_buffer *ib; + unsigned long flags; + + dev_dbg(&isys->adev->dev, "preparing request %u\n", req->id); + + set->send_irq_sof = 1; + set->send_resp_sof = 1; + set->send_irq_eof = 1; + set->send_resp_eof = 1; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + set->send_irq_capture_ack = 1; + set->send_irq_capture_done = 1; +#endif + + spin_lock_irqsave(&ireq->lock, flags); + + list_for_each_entry(ib, &ireq->buffers, req_head) { + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + + if (aq->prepare_frame_buff_set) + aq->prepare_frame_buff_set(vb); + + if (aq->fill_frame_buff_set_pin) + aq->fill_frame_buff_set_pin(vb, set); + + spin_lock(&aq->lock); + list_move(&ib->head, &aq->active); + spin_unlock(&aq->lock); + } + + spin_unlock_irqrestore(&ireq->lock, flags); + + return 0; +} + +static void +ipu_isys_req_dispatch(struct media_device *mdev, + struct ipu_isys_request *ireq, + struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_frame_buff_set_abi *set, + dma_addr_t dma_addr) +{ + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + int rval; + + rval = ipu_fw_isys_complex_cmd(pipe_av->isys, + ip->stream_handle, + set, dma_addr, sizeof(*set), + IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE); + ipu_put_fw_mgs_buffer(pipe_av->isys, (uintptr_t) set); + + WARN_ON(rval); +} + +int ipu_isys_req_queue(struct media_device *mdev, + struct media_device_request *req) +{ + struct ipu_isys *isys = container_of(mdev, struct ipu_isys, media_dev); + struct ipu_isys_request *ireq = to_ipu_isys_request(req); + struct ipu_isys_pipeline *ip; + struct ipu_isys_buffer *ib; + struct media_pipeline *pipe = NULL; + unsigned long flags; + bool no_pipe = false; + int rval = 0; + + spin_lock_irqsave(&ireq->lock, flags); + if (list_empty(&ireq->buffers)) { + rval = -ENODATA; + goto out_list_empty; + } + + /* Verify that all buffers are related to a single pipeline. */ + list_for_each_entry(ib, &ireq->buffers, req_head) { + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + dev_dbg(&isys->adev->dev, "%s: device %s, id %u\n", __func__, + av->vdev.name, vb-> +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + v4l2_buf. +#endif + index); + if (!pipe) { + if (!av->vdev.entity.pipe) { + no_pipe = true; + continue; + } + + pipe = av->vdev.entity.pipe; + dev_dbg(&isys->adev->dev, "%s: pipe %p\n", + av->vdev.name, pipe); + continue; + } + + if (av->vdev.entity.pipe != pipe) { + dev_dbg(&isys->adev->dev, + "request %u includes buffers in multiple pipelines\n", + req->id); + rval = -EINVAL; + goto out_list_empty; + } + } + + spin_unlock_irqrestore(&ireq->lock, flags); + + mutex_lock(&isys->stream_mutex); + + ip = to_ipu_isys_pipeline(pipe); + + if (pipe && ip->streaming) { + struct isys_fw_msgs *msg; + struct ipu_fw_isys_frame_buff_set_abi *set; + + msg = ipu_get_fw_msg_buf(ip); + if (!msg) { + rval = -ENOMEM; + goto out_mutex_unlock; + } + + set = to_frame_msg_buf(msg); + + if (no_pipe) { + dev_dbg(&isys->adev->dev, + "request %u includes buffers in and outside pipelines\n", + req->id); + rval = -EINVAL; + goto out_mutex_unlock; + } + + dev_dbg(&isys->adev->dev, + "request has a pipeline, dispatching\n"); + rval = ipu_isys_req_prepare(mdev, ireq, ip, set); + if (rval) + goto out_mutex_unlock; + + ipu_fw_isys_dump_frame_buff_set(&isys->adev->dev, set, + ip->nr_output_pins); + ipu_isys_req_dispatch(mdev, ireq, ip, set, to_dma_addr(msg)); + } else { + dev_dbg(&isys->adev->dev, + "%s: adding request %u to the mdev queue\n", __func__, + req->id); + + list_add(&ireq->head, &isys->requests); + } + +out_mutex_unlock: + mutex_unlock(&isys->stream_mutex); + + return rval; + +out_list_empty: + spin_unlock_irqrestore(&ireq->lock, flags); + + return rval; +} + +struct vb2_ops ipu_isys_queue_ops = { + .queue_setup = queue_setup, + .wait_prepare = ipu_isys_queue_unlock, + .wait_finish = ipu_isys_queue_lock, + .buf_init = buf_init, + .buf_prepare = buf_prepare, + .buf_finish = buf_finish, + .buf_cleanup = buf_cleanup, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .buf_queue = buf_queue, +}; + +int ipu_isys_queue_init(struct ipu_isys_queue *aq) +{ + struct ipu_isys *isys = ipu_isys_queue_to_video(aq)->isys; + int rval; + + if (!aq->vbq.io_modes) + aq->vbq.io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; + aq->vbq.drv_priv = aq; + aq->vbq.allow_requests = true; + aq->vbq.ops = &ipu_isys_queue_ops; + aq->vbq.mem_ops = &vb2_dma_contig_memops; + aq->vbq.timestamp_flags = (wall_clock_ts_on) ? + V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN : V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + rval = vb2_queue_init(&aq->vbq); + if (rval) + return rval; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + aq->ctx = vb2_dma_contig_init_ctx(&isys->adev->dev); + if (IS_ERR(aq->ctx)) { + vb2_queue_release(&aq->vbq); + return PTR_ERR(aq->ctx); + } +#else + aq->dev = &isys->adev->dev; + aq->vbq.dev = &isys->adev->dev; +#endif + spin_lock_init(&aq->lock); + INIT_LIST_HEAD(&aq->active); + INIT_LIST_HEAD(&aq->incoming); + + return 0; +} + +void ipu_isys_queue_cleanup(struct ipu_isys_queue *aq) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (IS_ERR_OR_NULL(aq->ctx)) + return; + + vb2_dma_contig_cleanup_ctx(aq->ctx); + aq->ctx = NULL; +#endif + vb2_queue_release(&aq->vbq); +} diff --git a/drivers/media/pci/intel/ipu-isys-queue.h b/drivers/media/pci/intel/ipu-isys-queue.h new file mode 100644 index 000000000000..5138077ed35a --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-queue.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_QUEUE_H +#define IPU_ISYS_QUEUE_H + +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) +#include +#else +#include +#endif + +#include "ipu-isys-media.h" + +struct ipu_isys_video; +struct ipu_isys_pipeline; +struct ipu_fw_isys_resp_info_abi; +struct ipu_fw_isys_frame_buff_set_abi; + +enum ipu_isys_buffer_type { + IPU_ISYS_VIDEO_BUFFER, + IPU_ISYS_SHORT_PACKET_BUFFER, +}; + +struct ipu_isys_queue { + struct list_head node; /* struct ipu_isys_pipeline.queues */ + struct vb2_queue vbq; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct vb2_alloc_ctx *ctx; +#else + struct device *dev; +#endif + /* + * @lock: serialise access to queued and pre_streamon_queued + */ + spinlock_t lock; + struct list_head active; + struct list_head incoming; + u32 css_pin_type; + unsigned int fw_output; + int (*buf_init)(struct vb2_buffer *vb); + void (*buf_cleanup)(struct vb2_buffer *vb); + int (*buf_prepare)(struct vb2_buffer *vb); + void (*prepare_frame_buff_set)(struct vb2_buffer *vb); + void (*fill_frame_buff_set_pin)(struct vb2_buffer *vb, + struct ipu_fw_isys_frame_buff_set_abi * + set); + int (*link_fmt_validate)(struct ipu_isys_queue *aq); +}; + +struct ipu_isys_buffer { + struct list_head head; + enum ipu_isys_buffer_type type; + struct list_head req_head; + struct media_device_request *req; + atomic_t str2mmio_flag; +}; + +struct ipu_isys_video_buffer { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + struct vb2_buffer vb; +#else + struct vb2_v4l2_buffer vb_v4l2; +#endif + struct ipu_isys_buffer ib; +}; + +struct ipu_isys_private_buffer { + struct ipu_isys_buffer ib; + struct ipu_isys_pipeline *ip; + unsigned int index; + unsigned int bytesused; + dma_addr_t dma_addr; + void *buffer; +}; + +#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) +#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) +#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) + +struct ipu_isys_buffer_list { + struct list_head head; + unsigned int nbufs; +}; + +#define vb2_queue_to_ipu_isys_queue(__vb2) \ + container_of(__vb2, struct ipu_isys_queue, vbq) + +#define ipu_isys_to_isys_video_buffer(__ib) \ + container_of(__ib, struct ipu_isys_video_buffer, ib) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) +#define vb2_buffer_to_ipu_isys_video_buffer(__vb) \ + container_of(__vb, struct ipu_isys_video_buffer, vb) + +#define ipu_isys_buffer_to_vb2_buffer(__ib) \ + (&ipu_isys_to_isys_video_buffer(__ib)->vb) +#else +#define vb2_buffer_to_ipu_isys_video_buffer(__vb) \ + container_of(to_vb2_v4l2_buffer(__vb), \ + struct ipu_isys_video_buffer, vb_v4l2) + +#define ipu_isys_buffer_to_vb2_buffer(__ib) \ + (&ipu_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) +#endif + +#define vb2_buffer_to_ipu_isys_buffer(__vb) \ + (&vb2_buffer_to_ipu_isys_video_buffer(__vb)->ib) + +#define ipu_isys_buffer_to_private_buffer(__ib) \ + container_of(__ib, struct ipu_isys_private_buffer, ib) + +struct ipu_isys_request { + struct media_device_request req; + /* serialise access to buffers */ + spinlock_t lock; + struct list_head buffers; /* struct ipu_isys_buffer.head */ + bool dispatched; + /* + * struct ipu_isys.requests; + * struct ipu_isys_pipeline.struct.* + */ + struct list_head head; +}; + +#define to_ipu_isys_request(__req) \ + container_of(__req, struct ipu_isys_request, req) + +void ipu_isys_queue_lock(struct vb2_queue *q); +void ipu_isys_queue_unlock(struct vb2_queue *q); + +int ipu_isys_buf_prepare(struct vb2_buffer *vb); + +void ipu_isys_buffer_list_queue(struct ipu_isys_buffer_list *bl, + unsigned long op_flags, + enum vb2_buffer_state state); +struct ipu_isys_request *ipu_isys_next_queued_request( + struct ipu_isys_pipeline *ip); +void ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin( + struct vb2_buffer *vb, + struct ipu_fw_isys_frame_buff_set_abi *set); +void ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set( + struct ipu_fw_isys_frame_buff_set_abi *set, + struct ipu_isys_pipeline *ip, + struct ipu_isys_buffer_list *bl); +int ipu_isys_link_fmt_validate(struct ipu_isys_queue *aq); + +void +ipu_isys_buf_calc_sequence_time(struct ipu_isys_buffer *ib, + struct ipu_fw_isys_resp_info_abi *info); +void ipu_isys_queue_buf_done(struct ipu_isys_buffer *ib); +void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info); +void +ipu_isys_queue_short_packet_ready(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *inf); + +void ipu_isys_req_free(struct media_device *mdev, + struct media_device_request *req); +struct media_device_request *ipu_isys_req_alloc(struct media_device *mdev); +int ipu_isys_req_prepare(struct media_device *mdev, + struct ipu_isys_request *ireq, + struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_frame_buff_set_abi *set); +int ipu_isys_req_queue(struct media_device *mdev, + struct media_device_request *req); + +int ipu_isys_queue_init(struct ipu_isys_queue *aq); +void ipu_isys_queue_cleanup(struct ipu_isys_queue *aq); + +#endif /* IPU_ISYS_QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu-isys-subdev.c b/drivers/media/pci/intel/ipu-isys-subdev.c new file mode 100644 index 000000000000..01d698528272 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-subdev.c @@ -0,0 +1,1017 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include + +#include + +#include + +#include "ipu-isys.h" +#include "ipu-isys-video.h" +#include "ipu-isys-subdev.h" + +unsigned int ipu_isys_mbus_code_to_bpp(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_RGB888_1X24: + return 24; + case MEDIA_BUS_FMT_YUYV10_1X20: + return 20; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + return 16; + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + return 14; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return 12; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return 10; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return 8; + default: + WARN_ON(1); + return -EINVAL; + } +} + +unsigned int ipu_isys_mbus_code_to_mipi(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_RGB565_1X16: + return IPU_ISYS_MIPI_CSI2_TYPE_RGB565; + case MEDIA_BUS_FMT_RGB888_1X24: + return IPU_ISYS_MIPI_CSI2_TYPE_RGB888; + case MEDIA_BUS_FMT_YUYV10_1X20: + return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_10; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_8; + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + return IPU_ISYS_MIPI_CSI2_TYPE_RAW14; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return IPU_ISYS_MIPI_CSI2_TYPE_RAW12; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return IPU_ISYS_MIPI_CSI2_TYPE_RAW10; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return IPU_ISYS_MIPI_CSI2_TYPE_RAW8; + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(1); + default: + WARN_ON(1); + return -EINVAL; + } +} + +enum ipu_isys_subdev_pixelorder ipu_isys_subdev_get_pixelorder(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + return IPU_ISYS_SUBDEV_PIXELORDER_BGGR; + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + return IPU_ISYS_SUBDEV_PIXELORDER_GBRG; + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + return IPU_ISYS_SUBDEV_PIXELORDER_GRBG; + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return IPU_ISYS_SUBDEV_PIXELORDER_RGGB; + default: + WARN_ON(1); + return -EINVAL; + } +} + +u32 ipu_isys_subdev_code_to_uncompressed(u32 sink_code) +{ + switch (sink_code) { + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + return MEDIA_BUS_FMT_SBGGR10_1X10; + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + return MEDIA_BUS_FMT_SGBRG10_1X10; + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + return MEDIA_BUS_FMT_SGRBG10_1X10; + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return MEDIA_BUS_FMT_SRGGB10_1X10; + default: + return sink_code; + } +} + +struct v4l2_mbus_framefmt *__ipu_isys_get_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config + *cfg, +#endif + unsigned int pad, + unsigned int stream, + unsigned int which) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &asd->ffmt[pad][stream]; + else + return v4l2_subdev_get_try_format( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, +#endif + cfg, pad); +} + +struct v4l2_rect *__ipu_isys_get_selection(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + unsigned int target, + unsigned int pad, unsigned int which) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + switch (target) { + case V4L2_SEL_TGT_CROP: + return &asd->crop[pad]; + case V4L2_SEL_TGT_COMPOSE: + return &asd->compose[pad]; + } + } else { + switch (target) { + case V4L2_SEL_TGT_CROP: + return v4l2_subdev_get_try_crop( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, +#endif + cfg, pad); + case V4L2_SEL_TGT_COMPOSE: + return v4l2_subdev_get_try_compose( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, +#endif + cfg, pad); + } + } + WARN_ON(1); + return NULL; +} + +static int target_valid(struct v4l2_subdev *sd, unsigned int target, + unsigned int pad) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + switch (target) { + case V4L2_SEL_TGT_CROP: + return asd->valid_tgts[pad].crop; + case V4L2_SEL_TGT_COMPOSE: + return asd->valid_tgts[pad].compose; + default: + return 0; + } +} + +void ipu_isys_subdev_fmt_propagate(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_mbus_framefmt *ffmt, + struct v4l2_rect *r, + enum isys_subdev_prop_tgt tgt, + unsigned int pad, unsigned int which) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + struct v4l2_mbus_framefmt *ffmts[sd->entity.num_pads]; + struct v4l2_rect *crops[sd->entity.num_pads]; + struct v4l2_rect *compose[sd->entity.num_pads]; + unsigned int i; + + if (tgt == IPU_ISYS_SUBDEV_PROP_TGT_NR_OF) + return; + + if (WARN_ON(pad >= sd->entity.num_pads)) + return; + + for (i = 0; i < sd->entity.num_pads; i++) { + ffmts[i] = __ipu_isys_get_ffmt(sd, cfg, i, 0, which); + crops[i] = __ipu_isys_get_selection( + sd, cfg, V4L2_SEL_TGT_CROP, i, which); + compose[i] = __ipu_isys_get_selection( + sd, cfg, V4L2_SEL_TGT_COMPOSE, i, which); + } + + switch (tgt) { + case IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT: + crops[pad]->left = 0; + crops[pad]->top = 0; + crops[pad]->width = ffmt->width; + crops[pad]->height = ffmt->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, ffmt, crops[pad], + tgt + 1, pad, which); + return; + case IPU_ISYS_SUBDEV_PROP_TGT_SINK_CROP: + if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return; + + compose[pad]->left = 0; + compose[pad]->top = 0; + compose[pad]->width = r->width; + compose[pad]->height = r->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, ffmt, + compose[pad], tgt + 1, + pad, which); + return; + case IPU_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE: + if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return; + + /* 1:n and 1:1 case: only propagate to the first source pad */ + if (asd->nsinks == 1 && asd->nsources >= 1) { + compose[asd->nsinks]->left = + compose[asd->nsinks]->top = 0; + compose[asd->nsinks]->width = r->width; + compose[asd->nsinks]->height = r->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, ffmt, + compose[asd->nsinks], + tgt + 1, asd->nsinks, + which); + /* n:n case: propagate according to route info */ + } else if (asd->nsinks == asd->nsources && asd->nsources > 1) { + for (i = asd->nsinks; i < sd->entity.num_pads; i++) + if (media_entity_has_route(&sd->entity, pad, i)) + break; + + if (i != sd->entity.num_pads) { + compose[i]->left = 0; + compose[i]->top = 0; + compose[i]->width = r->width; + compose[i]->height = r->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, ffmt, + compose[i], + tgt + 1, i, + which); + } + /* n:m case: propagate to all source pad */ + } else if (asd->nsinks != asd->nsources && asd->nsources > 1 && + asd->nsources > 1) { + for (i = 1; i < sd->entity.num_pads; i++) { + if (!(sd->entity.pads[i].flags & + MEDIA_PAD_FL_SOURCE)) + continue; + + compose[i]->left = 0; + compose[i]->top = 0; + compose[i]->width = r->width; + compose[i]->height = r->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, + ffmt, + compose[i], + tgt + 1, i, + which); + } + } + return; + case IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE: + if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)) + return; + + crops[pad]->left = 0; + crops[pad]->top = 0; + crops[pad]->width = r->width; + crops[pad]->height = r->height; + ipu_isys_subdev_fmt_propagate(sd, cfg, ffmt, + crops[pad], tgt + 1, pad, which); + return; + case IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP:{ + struct v4l2_subdev_format fmt = { + .which = which, + .pad = pad, + .format = { + .width = r->width, + .height = r->height, + /* + * Either use the code from sink pad + * or the current one. + */ + .code = + ffmt ? ffmt->code : ffmts[pad]->code, + .field = + ffmt ? ffmt->field : ffmts[pad]-> + field, + }, + }; + + asd->set_ffmt(sd, cfg, &fmt); + return; + } + } +} + +void ipu_isys_subdev_set_ffmt_default(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->stream, + fmt->which); + + /* No propagation for non-zero pads. */ + if (fmt->pad) { + struct v4l2_mbus_framefmt *sink_ffmt = + __ipu_isys_get_ffmt(sd, cfg, 0, fmt->stream, + fmt->which); + + ffmt->width = sink_ffmt->width; + ffmt->height = sink_ffmt->height; + ffmt->code = sink_ffmt->code; + ffmt->field = sink_ffmt->field; + return; + } + + ffmt->width = fmt->format.width; + ffmt->height = fmt->format.height; + ffmt->code = fmt->format.code; + ffmt->field = fmt->format.field; + + ipu_isys_subdev_fmt_propagate(sd, cfg, &fmt->format, NULL, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + fmt->pad, fmt->which); +} + +int __ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->stream, + fmt->which); + u32 code = asd->supported_codes[fmt->pad][0]; + unsigned int i; + + WARN_ON(!mutex_is_locked(&asd->mutex)); + + fmt->format.width = clamp(fmt->format.width, IPU_ISYS_MIN_WIDTH, + IPU_ISYS_MAX_WIDTH); + fmt->format.height = clamp(fmt->format.height, + IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); + + for (i = 0; asd->supported_codes[fmt->pad][i]; i++) { + if (asd->supported_codes[fmt->pad][i] == fmt->format.code) { + code = asd->supported_codes[fmt->pad][i]; + break; + } + } + + fmt->format.code = code; + + asd->set_ffmt(sd, cfg, fmt); + + fmt->format = *ffmt; + + return 0; +} + +int ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + int rval; + + if (fmt->stream >= asd->nstreams) + return -EINVAL; + + mutex_lock(&asd->mutex); + rval = __ipu_isys_subdev_set_ffmt(sd, cfg, fmt); + mutex_unlock(&asd->mutex); + + return rval; +} + +int ipu_isys_subdev_get_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + if (fmt->stream >= asd->nstreams) + return -EINVAL; + + mutex_lock(&asd->mutex); + fmt->format = *__ipu_isys_get_ffmt(sd, cfg, fmt->pad, + fmt->stream, + fmt->which); + mutex_unlock(&asd->mutex); + + return 0; +} + +int ipu_isys_subdev_get_frame_desc(struct v4l2_subdev *sd, + struct v4l2_mbus_frame_desc *desc) +{ + int i, rval = 0; + + for (i = 0; i < sd->entity.num_pads; i++) { + if (!(sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)) + continue; + + rval = v4l2_subdev_call(sd, pad, get_frame_desc, i, desc); + if (!rval) + return rval; + } + + if (i == sd->entity.num_pads) + rval = -EINVAL; + + return rval; +} + +bool ipu_isys_subdev_has_route(struct media_entity *entity, + unsigned int pad0, unsigned int pad1, int *stream) +{ + struct ipu_isys_subdev *asd; + int i; + + if (!entity) { + WARN_ON(1); + return false; + } + asd = to_ipu_isys_subdev(media_entity_to_v4l2_subdev(entity)); + + /* Two sinks are never connected together. */ + if (pad0 < asd->nsinks && pad1 < asd->nsinks) + return false; + + for (i = 0; i < asd->nstreams; i++) { + if ((asd->route[i].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) && + ((asd->route[i].sink == pad0 && + asd->route[i].source == pad1) || + (asd->route[i].sink == pad1 && + asd->route[i].source == pad0))) { + if (stream) + *stream = i; + return true; + } + } + + return false; +} + +int ipu_isys_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + int i, j, ret = 0; + + WARN_ON(!mutex_is_locked(&sd->entity. +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + parent +#else + graph_obj.mdev +#endif + ->graph_mutex)); + + for (i = 0; i < min(route->num_routes, asd->nstreams); ++i) { + struct v4l2_subdev_route *t = &route->routes[i]; + + if (t->sink_stream > asd->nstreams - 1 || + t->source_stream > asd->nstreams - 1) + continue; + + for (j = 0; j < asd->nstreams; j++) { + if (t->sink_pad == asd->route[j].sink && + t->source_pad == asd->route[j].source) + break; + } + + if (j == asd->nstreams) + continue; + + if (asd->route[j].flags & V4L2_SUBDEV_ROUTE_FL_IMMUTABLE) + continue; + + if ((t->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE) && asd->nsinks) + continue; + + if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE)) { + int source_pad = 0; + + if (sd->entity.pads[t->sink_pad].flags & + MEDIA_PAD_FL_MULTIPLEX) + source_pad = t->source_pad - asd->nsinks; + + asd->stream[t->sink_pad].stream_id[source_pad] = + t->sink_stream; + } + + if (sd->entity.pads[t->source_pad].flags & + MEDIA_PAD_FL_MULTIPLEX) + asd->stream[t->source_pad].stream_id[t->sink_pad] = + t->source_stream; + else + asd->stream[t->source_pad].stream_id[0] = + t->source_stream; + + if (t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) { + bitmap_set(asd->stream[t->source_pad].streams_stat, + t->source_stream, 1); + if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE)) + bitmap_set(asd->stream[t->sink_pad] + .streams_stat, t->sink_stream, 1); + asd->route[j].flags |= V4L2_SUBDEV_ROUTE_FL_ACTIVE; + } else if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { + bitmap_clear(asd->stream[t->source_pad].streams_stat, + t->source_stream, 1); + if (!(t->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE)) + bitmap_clear(asd->stream[t->sink_pad] + .streams_stat, t->sink_stream, 1); + asd->route[j].flags &= (~V4L2_SUBDEV_ROUTE_FL_ACTIVE); + } + } + + return ret; +} + +int ipu_isys_subdev_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + int i, j; + + for (i = 0, j = 0; i < min(asd->nstreams, route->num_routes); ++i) { + route->routes[j].sink_pad = asd->route[i].sink; + if (sd->entity.pads[asd->route[i].sink].flags & + MEDIA_PAD_FL_MULTIPLEX) { + int source_pad = asd->route[i].source - asd->nsinks; + + route->routes[j].sink_stream = + asd->stream[asd->route[i].sink]. + stream_id[source_pad]; + } else { + route->routes[j].sink_stream = + asd->stream[asd->route[i].sink].stream_id[0]; + } + + route->routes[j].source_pad = asd->route[i].source; + if (sd->entity.pads[asd->route[i].source].flags & + MEDIA_PAD_FL_MULTIPLEX) { + route->routes[j].source_stream = + asd->stream[asd->route[i].source].stream_id[asd-> + route + [i]. + sink]; + } else { + route->routes[j].source_stream = + asd->stream[asd->route[i].source].stream_id[0]; + } + route->routes[j++].flags = asd->route[i].flags; + } + + route->num_routes = j; + + return 0; +} + +int ipu_isys_subdev_set_sel(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_selection *sel) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + struct media_pad *pad = &asd->sd.entity.pads[sel->pad]; + struct v4l2_rect *r, __r = { 0 }; + unsigned int tgt; + + if (!target_valid(sd, sel->target, sel->pad)) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (pad->flags & MEDIA_PAD_FL_SINK) { + struct v4l2_mbus_framefmt *ffmt = + __ipu_isys_get_ffmt(sd, cfg, sel->pad, 0, + sel->which); + + __r.width = ffmt->width; + __r.height = ffmt->height; + r = &__r; + tgt = IPU_ISYS_SUBDEV_PROP_TGT_SINK_CROP; + } else { + /* 0 is the sink pad. */ + r = __ipu_isys_get_selection(sd, cfg, sel->target, 0, + sel->which); + tgt = IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP; + } + + break; + case V4L2_SEL_TGT_COMPOSE: + if (pad->flags & MEDIA_PAD_FL_SINK) { + r = __ipu_isys_get_selection(sd, cfg, V4L2_SEL_TGT_CROP, + sel->pad, sel->which); + tgt = IPU_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE; + } else { + r = __ipu_isys_get_selection(sd, cfg, + V4L2_SEL_TGT_COMPOSE, 0, + sel->which); + tgt = IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE; + } + break; + default: + return -EINVAL; + } + + sel->r.width = clamp(sel->r.width, IPU_ISYS_MIN_WIDTH, r->width); + sel->r.height = clamp(sel->r.height, IPU_ISYS_MIN_HEIGHT, r->height); + *__ipu_isys_get_selection(sd, cfg, sel->target, sel->pad, + sel->which) = sel->r; + ipu_isys_subdev_fmt_propagate(sd, cfg, NULL, &sel->r, tgt, + sel->pad, sel->which); + + return 0; +} + +int ipu_isys_subdev_get_sel(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_selection *sel) +{ + if (!target_valid(sd, sel->target, sel->pad)) + return -EINVAL; + + sel->r = *__ipu_isys_get_selection(sd, cfg, sel->target, + sel->pad, sel->which); + + return 0; +} + +int ipu_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + const u32 *supported_codes = asd->supported_codes[code->pad]; + u32 index; + bool next_stream = false; + + if (sd->entity.pads[code->pad].flags & MEDIA_PAD_FL_MULTIPLEX) { + if (code->stream & V4L2_SUBDEV_FLAG_NEXT_STREAM) { + next_stream = true; + code->stream &= ~V4L2_SUBDEV_FLAG_NEXT_STREAM; + } + + if (code->stream > asd->nstreams - 1) + return -EINVAL; + + if (next_stream && code->stream < asd->nstreams) { + code->stream++; + return 0; + } + + return -EINVAL; + } + + for (index = 0; supported_codes[index]; index++) { + if (index == code->index) { + code->code = supported_codes[index]; + return 0; + } + } + + return -EINVAL; +} + +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) +/* + * IPU private link validation + * In advanced IPU and special case, there will be format change between + * sink/source pads in ISYS. + * Format code checking is not necessary for these features. + */ +static int ipu_isys_subdev_link_validate_private( + struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + /* The width and height must match. */ + if (source_fmt->format.width != sink_fmt->format.width + || source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + /* + * The field order must match, or the sink field order must be NONE + * to support interlaced hardware connected to bridges that support + * progressive formats only. + */ + if (source_fmt->format.field != sink_fmt->format.field && + sink_fmt->format.field != V4L2_FIELD_NONE) + return -EPIPE; + + if (source_fmt->stream != sink_fmt->stream) + return -EINVAL; + + /* + * For new IPU special case, YUV format changing in BE-SOC, + * from YUV422 to I420, which is used to adapt multiple + * YUV sensors and provide I420 to BB for partial processing. + * If this entity doing format convert, ignore format check + */ + if (source_fmt->format.code != sink_fmt->format.code) { + if (source_fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8 && + (sink_fmt->format.code == MEDIA_BUS_FMT_YUYV8_1X16 || + sink_fmt->format.code == MEDIA_BUS_FMT_UYVY8_1X16)) + dev_warn(&asd->isys->adev->dev, + "YUV format change, ignore code check\n"); + else + return -EINVAL; + } + + return 0; +} +#endif + +/* + * Besides validating the link, figure out the external pad and the + * ISYS FW ABI source. + */ +int ipu_isys_subdev_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct v4l2_subdev *source_sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + + if (!source_sd) + return -ENODEV; + if (strncmp(source_sd->name, IPU_ISYS_ENTITY_PREFIX, + strlen(IPU_ISYS_ENTITY_PREFIX)) != 0) { + /* + * source_sd isn't ours --- sd must be the external + * sub-device. + */ + ip->external = link->source; + ip->source = to_ipu_isys_subdev(sd)->source; + dev_dbg(&asd->isys->adev->dev, "%s: using source %d\n", + sd->entity.name, ip->source); + } else if (source_sd->entity.num_pads == 1) { + /* All internal sources have a single pad. */ + ip->external = link->source; + ip->source = to_ipu_isys_subdev(source_sd)->source; + + dev_dbg(&asd->isys->adev->dev, "%s: using source %d\n", + sd->entity.name, ip->source); + } + + if (asd->isl_mode != IPU_ISL_OFF) + ip->isl_mode = asd->isl_mode; + +#if !defined(CONFIG_VIDEO_INTEL_IPU4) && !defined(CONFIG_VIDEO_INTEL_IPU4P) + return ipu_isys_subdev_link_validate_private(sd, link, source_fmt, + sink_fmt); +#else + return v4l2_subdev_link_validate_default(sd, link, source_fmt, + sink_fmt); +#endif +} + +int ipu_isys_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); + unsigned int i; + + mutex_lock(&asd->mutex); + + for (i = 0; i < asd->sd.entity.num_pads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, fh->pad, +#else + fh, +#endif + i); + struct v4l2_rect *try_crop = + v4l2_subdev_get_try_crop( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, + fh->pad, +#else + fh, +#endif + i); + struct v4l2_rect *try_compose = + v4l2_subdev_get_try_compose( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + sd, + fh->pad, +#else + fh, +#endif + i); + + *try_fmt = asd->ffmt[i][0]; + *try_crop = asd->crop[i]; + *try_compose = asd->compose[i]; + } + + mutex_unlock(&asd->mutex); + + return 0; +} + +int ipu_isys_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +int ipu_isys_subdev_init(struct ipu_isys_subdev *asd, + struct v4l2_subdev_ops *ops, + unsigned int nr_ctrls, + unsigned int num_pads, + unsigned int num_streams, + unsigned int num_source, + unsigned int num_sink, + unsigned int sd_flags) +{ + int i; + int rval = -EINVAL; + + mutex_init(&asd->mutex); + + v4l2_subdev_init(&asd->sd, ops); + + asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | sd_flags; + asd->sd.owner = THIS_MODULE; + + asd->nstreams = num_streams; + asd->nsources = num_source; + asd->nsinks = num_sink; + + asd->pad = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->pad), GFP_KERNEL); + + asd->ffmt = (struct v4l2_mbus_framefmt **) + devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(struct v4l2_mbus_framefmt *), + GFP_KERNEL); + + asd->crop = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->crop), GFP_KERNEL); + + asd->compose = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->compose), GFP_KERNEL); + + asd->valid_tgts = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->valid_tgts), GFP_KERNEL); + asd->route = devm_kcalloc(&asd->isys->adev->dev, num_streams, + sizeof(*asd->route), GFP_KERNEL); + + asd->stream = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->stream), GFP_KERNEL); + + if (!asd->pad || !asd->ffmt || !asd->crop || !asd->compose || + !asd->valid_tgts || !asd->route || !asd->stream) + return -ENOMEM; + + for (i = 0; i < num_pads; i++) { + asd->ffmt[i] = (struct v4l2_mbus_framefmt *) + devm_kcalloc(&asd->isys->adev->dev, num_streams, + sizeof(struct v4l2_mbus_framefmt), GFP_KERNEL); + if (!asd->ffmt[i]) + return -ENOMEM; + + asd->stream[i].stream_id = + devm_kcalloc(&asd->isys->adev->dev, num_source, + sizeof(*asd->stream[i].stream_id), GFP_KERNEL); + if (!asd->stream[i].stream_id) + return -ENOMEM; + } + + rval = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); + if (rval) + goto out_mutex_destroy; + + if (asd->ctrl_init) { + rval = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); + if (rval) + goto out_media_entity_cleanup; + + asd->ctrl_init(&asd->sd); + if (asd->ctrl_handler.error) { + rval = asd->ctrl_handler.error; + goto out_v4l2_ctrl_handler_free; + } + + asd->sd.ctrl_handler = &asd->ctrl_handler; + } + + asd->source = -1; + + return 0; + +out_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&asd->ctrl_handler); + +out_media_entity_cleanup: + media_entity_cleanup(&asd->sd.entity); + +out_mutex_destroy: + mutex_destroy(&asd->mutex); + + return rval; +} + +void ipu_isys_subdev_cleanup(struct ipu_isys_subdev *asd) +{ + media_entity_cleanup(&asd->sd.entity); + v4l2_ctrl_handler_free(&asd->ctrl_handler); + mutex_destroy(&asd->mutex); +} diff --git a/drivers/media/pci/intel/ipu-isys-subdev.h b/drivers/media/pci/intel/ipu-isys-subdev.h new file mode 100644 index 000000000000..0835a0f4ef6b --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-subdev.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_SUBDEV_H +#define IPU_ISYS_SUBDEV_H + +#include + +#include +#include +#include + +#include "ipu-isys-queue.h" + +#define IPU_ISYS_MIPI_CSI2_TYPE_NULL 0x10 +#define IPU_ISYS_MIPI_CSI2_TYPE_BLANKING 0x11 +#define IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8 0x12 +#define IPU_ISYS_MIPI_CSI2_TYPE_YUV422_8 0x1e +#define IPU_ISYS_MIPI_CSI2_TYPE_YUV422_10 0x1f +#define IPU_ISYS_MIPI_CSI2_TYPE_RGB565 0x22 +#define IPU_ISYS_MIPI_CSI2_TYPE_RGB888 0x24 +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW6 0x28 +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW7 0x29 +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW8 0x2a +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW10 0x2b +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW12 0x2c +#define IPU_ISYS_MIPI_CSI2_TYPE_RAW14 0x2d +/* 1-8 */ +#define IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i) - 1) + +#define FMT_ENTRY (struct ipu_isys_fmt_entry []) + +enum isys_subdev_prop_tgt { + IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_CROP, + IPU_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE, + IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE, + IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP, +}; + +#define IPU_ISYS_SUBDEV_PROP_TGT_NR_OF \ + (IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP + 1) + +enum ipu_isl_mode { + IPU_ISL_OFF = 0, /* IPU_FW_ISYS_USE_NO_ISL_NO_ISA */ + IPU_ISL_CSI2_BE, /* IPU_FW_ISYS_USE_SINGLE_DUAL_ISL */ + IPU_ISL_ISA /* IPU_FW_ISYS_USE_SINGLE_ISA */ +}; + +enum ipu_be_mode { + IPU_BE_RAW = 0, + IPU_BE_SOC +}; + +enum ipu_isys_subdev_pixelorder { + IPU_ISYS_SUBDEV_PIXELORDER_BGGR = 0, + IPU_ISYS_SUBDEV_PIXELORDER_GBRG, + IPU_ISYS_SUBDEV_PIXELORDER_GRBG, + IPU_ISYS_SUBDEV_PIXELORDER_RGGB, +}; + +struct ipu_isys; + +struct ipu_isys_subdev { + /* Serialise access to any other field in the struct */ + struct mutex mutex; + struct v4l2_subdev sd; + struct ipu_isys *isys; + u32 const *const *supported_codes; + struct media_pad *pad; + struct v4l2_mbus_framefmt **ffmt; + struct v4l2_rect *crop; + struct v4l2_rect *compose; + struct { + unsigned int *stream_id; + DECLARE_BITMAP(streams_stat, 32); + } *stream; /* stream enable/disable status, indexed by pad */ + struct { + unsigned int sink; + unsigned int source; + int flags; + } *route; /* pad level info, indexed by stream */ + unsigned int nstreams; + unsigned int nsinks; + unsigned int nsources; + struct v4l2_ctrl_handler ctrl_handler; + void (*ctrl_init)(struct v4l2_subdev *sd); + void (*set_ffmt)(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt); + struct { + bool crop; + bool compose; + } *valid_tgts; + enum ipu_isl_mode isl_mode; + enum ipu_be_mode be_mode; + int source; /* SSI stream source; -1 if unset */ +}; + +#define to_ipu_isys_subdev(__sd) \ + container_of(__sd, struct ipu_isys_subdev, sd) + +struct v4l2_mbus_framefmt *__ipu_isys_get_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config + *cfg, +#endif + unsigned int pad, + unsigned int stream, + unsigned int which); + +unsigned int ipu_isys_mbus_code_to_bpp(u32 code); +unsigned int ipu_isys_mbus_code_to_mipi(u32 code); +u32 ipu_isys_subdev_code_to_uncompressed(u32 sink_code); + +enum ipu_isys_subdev_pixelorder ipu_isys_subdev_get_pixelorder(u32 code); + +void ipu_isys_subdev_fmt_propagate(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_mbus_framefmt *ffmt, + struct v4l2_rect *r, + enum isys_subdev_prop_tgt tgt, + unsigned int pad, unsigned int which); + +void ipu_isys_subdev_set_ffmt_default(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt); +int __ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt); +struct v4l2_rect *__ipu_isys_get_selection(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + unsigned int target, + unsigned int pad, + unsigned int which); +int ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt); +int ipu_isys_subdev_get_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt); +int ipu_isys_subdev_get_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel); +int ipu_isys_subdev_set_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel); +int ipu_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_mbus_code_enum + *code); +int ipu_isys_subdev_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt); + +int ipu_isys_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); +int ipu_isys_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); +int ipu_isys_subdev_init(struct ipu_isys_subdev *asd, + struct v4l2_subdev_ops *ops, + unsigned int nr_ctrls, + unsigned int num_pads, + unsigned int num_streams, + unsigned int num_source, + unsigned int num_sink, + unsigned int sd_flags); +void ipu_isys_subdev_cleanup(struct ipu_isys_subdev *asd); +int ipu_isys_subdev_get_frame_desc(struct v4l2_subdev *sd, + struct v4l2_mbus_frame_desc *desc); +int ipu_isys_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); +int ipu_isys_subdev_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); +bool ipu_isys_subdev_has_route(struct media_entity *entity, + unsigned int pad0, unsigned int pad1, int *stream); +#endif /* IPU_ISYS_SUBDEV_H */ diff --git a/drivers/media/pci/intel/ipu-isys-tpg.c b/drivers/media/pci/intel/ipu-isys-tpg.c new file mode 100644 index 000000000000..446b445ad93f --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-tpg.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include + +#include +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-isys.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-tpg.h" +#include "ipu-isys-video.h" +#include "ipu-platform-isys-csi2-reg.h" + +static const u32 tpg_supported_codes_pad[] = { + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + 0, +}; + +static const u32 *tpg_supported_codes[] = { + tpg_supported_codes_pad, +}; + +static struct v4l2_subdev_internal_ops tpg_sd_internal_ops = { + .open = ipu_isys_subdev_open, + .close = ipu_isys_subdev_close, +}; + +static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { + .s_stream = tpg_set_stream, +}; + +static int ipu_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ipu_isys_tpg *tpg = container_of(container_of(ctrl->handler, + struct + ipu_isys_subdev, + ctrl_handler), + struct ipu_isys_tpg, asd); + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + writel(ctrl->val, tpg->base + MIPI_GEN_REG_SYNG_HBLANK_CYC); + break; + case V4L2_CID_VBLANK: + writel(ctrl->val, tpg->base + MIPI_GEN_REG_SYNG_VBLANK_CYC); + break; + case V4L2_CID_LINE_LENGTH_PIXELS: + if (ctrl->val > tpg->asd.ffmt[TPG_PAD_SOURCE][0].width) + writel(ctrl->val - + tpg->asd.ffmt[TPG_PAD_SOURCE][0].width, + tpg->base + MIPI_GEN_REG_SYNG_HBLANK_CYC); + break; + case V4L2_CID_FRAME_LENGTH_LINES: + if (ctrl->val > tpg->asd.ffmt[TPG_PAD_SOURCE][0].height) + writel(ctrl->val - + tpg->asd.ffmt[TPG_PAD_SOURCE][0].height, + tpg->base + MIPI_GEN_REG_SYNG_VBLANK_CYC); + break; + case V4L2_CID_TEST_PATTERN: + writel(ctrl->val, tpg->base + MIPI_GEN_REG_TPG_MODE); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ipu_isys_tpg_ctrl_ops = { + .s_ctrl = ipu_isys_tpg_s_ctrl, +}; + +static s64 ipu_isys_tpg_rate(struct ipu_isys_tpg *tpg, unsigned int bpp) +{ + return MIPI_GEN_PPC * IPU_ISYS_FREQ; +} + +static const char *const tpg_mode_items[] = { + "Ramp", + "Checkerboard", /* Does not work, disabled. */ + "Frame Based Colour", +}; + +static struct v4l2_ctrl_config tpg_mode = { + .ops = &ipu_isys_tpg_ctrl_ops, + .id = V4L2_CID_TEST_PATTERN, + .name = "Test Pattern", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = ARRAY_SIZE(tpg_mode_items) - 1, + .def = 0, + .menu_skip_mask = 0x2, + .qmenu = tpg_mode_items, +}; + +static const struct v4l2_ctrl_config csi2_header_cfg = { + .id = V4L2_CID_IPU_STORE_CSI2_HEADER, + .name = "Store CSI-2 Headers", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static void ipu_isys_tpg_init_controls(struct v4l2_subdev *sd) +{ + struct ipu_isys_tpg *tpg = to_ipu_isys_tpg(sd); + int hblank; + struct v4l2_ctrl_config cfg = { + .ops = &ipu_isys_tpg_ctrl_ops, + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 65535, + .min = 8, + .step = 1, + .qmenu = NULL, + .elem_size = 0, + }; + + hblank = 1024; + + tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, + &ipu_isys_tpg_ctrl_ops, + V4L2_CID_HBLANK, 8, 65535, 1, hblank); + + tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, + &ipu_isys_tpg_ctrl_ops, + V4L2_CID_VBLANK, 8, 65535, 1, 1024); + + cfg.id = V4L2_CID_LINE_LENGTH_PIXELS; + cfg.name = "Line Length Pixels"; + cfg.def = 1024 + 4096; + + tpg->llp = v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &cfg, NULL); + + cfg.id = V4L2_CID_FRAME_LENGTH_LINES; + cfg.name = "Frame Length Lines"; + cfg.def = 1024 + 3072; + tpg->fll = v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &cfg, NULL); + + tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, + &ipu_isys_tpg_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (tpg->pixel_rate) { + tpg->pixel_rate->cur.val = ipu_isys_tpg_rate(tpg, 8); + tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + } + + v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); + tpg->store_csi2_header = + v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &csi2_header_cfg, NULL); +} + +static void tpg_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + fmt->format.field = V4L2_FIELD_NONE; + *__ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->stream, + fmt->which) = fmt->format; +} + +static int ipu_isys_tpg_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct ipu_isys_tpg *tpg = to_ipu_isys_tpg(sd); + __u32 code = tpg->asd.ffmt[TPG_PAD_SOURCE][0].code; + unsigned int bpp = ipu_isys_mbus_code_to_bpp(code); + s64 tpg_rate = ipu_isys_tpg_rate(tpg, bpp); + int rval; + + mutex_lock(&tpg->asd.mutex); + rval = __ipu_isys_subdev_set_ffmt(sd, cfg, fmt); + mutex_unlock(&tpg->asd.mutex); + + if (rval || fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return rval; + + v4l2_ctrl_s_ctrl_int64(tpg->pixel_rate, tpg_rate); + + return 0; +} + +static const struct ipu_isys_pixelformat *ipu_isys_tpg_try_fmt( + struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct media_entity entity = av->vdev.entity; + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(entity.links[0].source->entity); +#else + struct media_link *link = list_first_entry(&av->vdev.entity.links, + struct media_link, list); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); +#endif + struct ipu_isys_tpg *tpg; + + if (!sd) + return NULL; + + tpg = to_ipu_isys_tpg(sd); + + return ipu_isys_video_try_fmt_vid_mplane(av, mpix, + v4l2_ctrl_g_ctrl(tpg->store_csi2_header)); +} + +static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { + .get_fmt = ipu_isys_subdev_get_ffmt, + .set_fmt = ipu_isys_tpg_set_ffmt, + .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, +}; + +static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +}; + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { + .subscribe_event = subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static struct v4l2_subdev_ops tpg_sd_ops = { + .core = &tpg_sd_core_ops, + .video = &tpg_sd_video_ops, + .pad = &tpg_sd_pad_ops, +}; + +static struct media_entity_operations tpg_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +void ipu_isys_tpg_cleanup(struct ipu_isys_tpg *tpg) +{ + v4l2_device_unregister_subdev(&tpg->asd.sd); + ipu_isys_subdev_cleanup(&tpg->asd); + ipu_isys_video_cleanup(&tpg->av); +} + +int ipu_isys_tpg_init(struct ipu_isys_tpg *tpg, + struct ipu_isys *isys, + void __iomem *base, void __iomem *sel, + unsigned int index) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = TPG_PAD_SOURCE, + .format = { + .width = 4096, + .height = 3072, + }, + }; + int rval; + + tpg->isys = isys; + tpg->base = base; + tpg->sel = sel; + tpg->index = index; + + tpg->asd.sd.entity.ops = &tpg_entity_ops; + tpg->asd.ctrl_init = ipu_isys_tpg_init_controls; + tpg->asd.isys = isys; + + rval = ipu_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, + NR_OF_TPG_PADS, + NR_OF_TPG_STREAMS, + NR_OF_TPG_SOURCE_PADS, + NR_OF_TPG_SINK_PADS, + V4L2_SUBDEV_FL_HAS_EVENTS); + if (rval) + return rval; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + tpg->asd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; +#else + tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; +#endif + tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + tpg->asd.source = IPU_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 + index; + tpg->asd.supported_codes = tpg_supported_codes; + tpg->asd.set_ffmt = tpg_set_ffmt; + ipu_isys_subdev_set_ffmt(&tpg->asd.sd, NULL, &fmt); + + tpg->asd.sd.internal_ops = &tpg_sd_internal_ops; + snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " TPG %u", index); + v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); + rval = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); + if (rval) { + dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); + goto fail; + } + + snprintf(tpg->av.vdev.name, sizeof(tpg->av.vdev.name), + IPU_ISYS_ENTITY_PREFIX " TPG %u capture", index); + tpg->av.isys = isys; + tpg->av.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_MIPI; + tpg->av.pfmts = ipu_isys_pfmts_packed; + tpg->av.try_fmt_vid_mplane = ipu_isys_tpg_try_fmt; + tpg->av.prepare_firmware_stream_cfg = + ipu_isys_prepare_firmware_stream_cfg_default; + tpg->av.packed = true; + tpg->av.line_header_length = IPU_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; + tpg->av.line_footer_length = IPU_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; + tpg->av.aq.buf_prepare = ipu_isys_buf_prepare; + tpg->av.aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + tpg->av.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + tpg->av.aq.vbq.buf_struct_size = sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&tpg->av, &tpg->asd.sd.entity, + TPG_PAD_SOURCE, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + return 0; + +fail: + ipu_isys_tpg_cleanup(tpg); + + return rval; +} diff --git a/drivers/media/pci/intel/ipu-isys-tpg.h b/drivers/media/pci/intel/ipu-isys-tpg.h new file mode 100644 index 000000000000..29ce5002219f --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-tpg.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_TPG_H +#define IPU_ISYS_TPG_H + +#include +#include +#include + +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" +#include "ipu-isys-queue.h" + +struct ipu_isys_tpg_pdata; +struct ipu_isys; + +#define TPG_PAD_SOURCE 0 +#define NR_OF_TPG_PADS 1 +#define NR_OF_TPG_SOURCE_PADS 1 +#define NR_OF_TPG_SINK_PADS 0 +#define NR_OF_TPG_STREAMS 1 + +/* + * PPC is 4 pixels for clock for RAW8, RAW10 and RAW12. + * Source: FW validation test code. + */ +#define MIPI_GEN_PPC 4 + +#define MIPI_GEN_REG_COM_ENABLE 0x0 +#define MIPI_GEN_REG_COM_DTYPE 0x4 +/* RAW8, RAW10 or RAW12 */ +#define MIPI_GEN_COM_DTYPE_RAW(n) (((n) - 8) / 2) +#define MIPI_GEN_REG_COM_VTYPE 0x8 +#define MIPI_GEN_REG_COM_VCHAN 0xc +#define MIPI_GEN_REG_COM_WCOUNT 0x10 +#define MIPI_GEN_REG_PRBS_RSTVAL0 0x14 +#define MIPI_GEN_REG_PRBS_RSTVAL1 0x18 +#define MIPI_GEN_REG_SYNG_FREE_RUN 0x1c +#define MIPI_GEN_REG_SYNG_PAUSE 0x20 +#define MIPI_GEN_REG_SYNG_NOF_FRAMES 0x24 +#define MIPI_GEN_REG_SYNG_NOF_PIXELS 0x28 +#define MIPI_GEN_REG_SYNG_NOF_LINES 0x2c +#define MIPI_GEN_REG_SYNG_HBLANK_CYC 0x30 +#define MIPI_GEN_REG_SYNG_VBLANK_CYC 0x34 +#define MIPI_GEN_REG_SYNG_STAT_HCNT 0x38 +#define MIPI_GEN_REG_SYNG_STAT_VCNT 0x3c +#define MIPI_GEN_REG_SYNG_STAT_FCNT 0x40 +#define MIPI_GEN_REG_SYNG_STAT_DONE 0x44 +#define MIPI_GEN_REG_TPG_MODE 0x48 +#define MIPI_GEN_REG_TPG_HCNT_MASK 0x4c +#define MIPI_GEN_REG_TPG_VCNT_MASK 0x50 +#define MIPI_GEN_REG_TPG_XYCNT_MASK 0x54 +#define MIPI_GEN_REG_TPG_HCNT_DELTA 0x58 +#define MIPI_GEN_REG_TPG_VCNT_DELTA 0x5c +#define MIPI_GEN_REG_TPG_R1 0x60 +#define MIPI_GEN_REG_TPG_G1 0x64 +#define MIPI_GEN_REG_TPG_B1 0x68 +#define MIPI_GEN_REG_TPG_R2 0x6c +#define MIPI_GEN_REG_TPG_G2 0x70 +#define MIPI_GEN_REG_TPG_B2 0x74 + +/* + * struct ipu_isys_tpg + * + * @nlanes: number of lanes in the receiver + */ +struct ipu_isys_tpg { + struct ipu_isys_tpg_pdata *pdata; + struct ipu_isys *isys; + struct ipu_isys_subdev asd; + struct ipu_isys_video av; + + void __iomem *base; + void __iomem *sel; + unsigned int index; + int streaming; + + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *llp; + struct v4l2_ctrl *fll; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *store_csi2_header; +}; + +#define to_ipu_isys_tpg(sd) \ + container_of(to_ipu_isys_subdev(sd), \ + struct ipu_isys_tpg, asd) +int ipu_isys_tpg_init(struct ipu_isys_tpg *tpg, + struct ipu_isys *isys, + void __iomem *base, void __iomem *sel, + unsigned int index); +void ipu_isys_tpg_cleanup(struct ipu_isys_tpg *tpg); +int tpg_set_stream(struct v4l2_subdev *sd, int enable); + +#endif /* IPU_ISYS_TPG_H */ diff --git a/drivers/media/pci/intel/ipu-isys-video.c b/drivers/media/pci/intel/ipu-isys-video.c new file mode 100644 index 000000000000..38fffcf777b5 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-video.c @@ -0,0 +1,1854 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#include +#else +#include +#endif + +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) +#include +#endif + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-cpd.h" +#include "ipu-isys.h" +#include "ipu-isys-video.h" +#include "ipu-platform.h" +#include "ipu-platform-regs.h" +#include "ipu-platform-buttress-regs.h" +#include "ipu-trace.h" +#include "ipu-fw-isys.h" +#include "ipu-fw-com.h" + +static unsigned int num_stream_support = IPU_ISYS_NUM_STREAMS; +module_param(num_stream_support, uint, 0660); +MODULE_PARM_DESC(num_stream_support, "IPU project support number of stream"); + +static bool use_stream_stop; +module_param(use_stream_stop, bool, 0660); +MODULE_PARM_DESC(use_stream_stop, "Use STOP command if running in CSI capture mode"); + +const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[] = { + {V4L2_PIX_FMT_Y10, 16, 10, 0, MEDIA_BUS_FMT_Y10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_UYVY, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_UYVY}, + {V4L2_PIX_FMT_YUYV, 16, 16, 0, MEDIA_BUS_FMT_YUYV8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_NV16, 16, 16, 8, MEDIA_BUS_FMT_YUYV8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_NV16}, + {V4L2_PIX_FMT_YUV420, 12, 0, 8, MEDIA_BUS_FMT_UYVY8_2X8, + IPU_FW_ISYS_FRAME_FORMAT_YUV420}, + {V4L2_PIX_FMT_XRGB32, 32, 32, 0, MEDIA_BUS_FMT_RGB565_1X16, + IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, + {V4L2_PIX_FMT_XBGR32, 32, 32, 0, MEDIA_BUS_FMT_RGB888_1X24, + IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, + /* Raw bayer formats. */ + {V4L2_PIX_FMT_SBGGR14, 16, 14, 0, MEDIA_BUS_FMT_SBGGR14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG14, 16, 14, 0, MEDIA_BUS_FMT_SGBRG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG14, 16, 14, 0, MEDIA_BUS_FMT_SGRBG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB14, 16, 14, 0, MEDIA_BUS_FMT_SRGGB14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR12, 16, 12, 0, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG12, 16, 12, 0, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG12, 16, 12, 0, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB12, 16, 12, 0, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR10, 16, 10, 0, MEDIA_BUS_FMT_SBGGR10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG10, 16, 10, 0, MEDIA_BUS_FMT_SGBRG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG10, 16, 10, 0, MEDIA_BUS_FMT_SGRBG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB10, 16, 10, 0, MEDIA_BUS_FMT_SRGGB10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR8, 8, 8, 0, MEDIA_BUS_FMT_SBGGR8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGBRG8, 8, 8, 0, MEDIA_BUS_FMT_SGBRG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGRBG8, 8, 8, 0, MEDIA_BUS_FMT_SGRBG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SRGGB8, 8, 8, 0, MEDIA_BUS_FMT_SRGGB8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {} +}; + +const struct ipu_isys_pixelformat ipu_isys_pfmts_packed[] = { + {V4L2_PIX_FMT_Y10, 10, 10, 0, MEDIA_BUS_FMT_Y10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_Y210, 20, 20, 0, MEDIA_BUS_FMT_YUYV10_1X20, + IPU_FW_ISYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_UYVY, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_UYVY}, + {V4L2_PIX_FMT_YUYV, 16, 16, 0, MEDIA_BUS_FMT_YUYV8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_RGB565, 16, 16, 0, MEDIA_BUS_FMT_RGB565_1X16, + IPU_FW_ISYS_FRAME_FORMAT_RGB565}, + {V4L2_PIX_FMT_BGR24, 24, 24, 0, MEDIA_BUS_FMT_RGB888_1X24, + IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, +#ifndef V4L2_PIX_FMT_SBGGR12P + {V4L2_PIX_FMT_SBGGR12, 12, 12, 0, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGBRG12, 12, 12, 0, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGRBG12, 12, 12, 0, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SRGGB12, 12, 12, 0, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SBGGR14, 14, 14, 0, MEDIA_BUS_FMT_SBGGR14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SGBRG14, 14, 14, 0, MEDIA_BUS_FMT_SGBRG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SGRBG14, 14, 14, 0, MEDIA_BUS_FMT_SGRBG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SRGGB14, 14, 14, 0, MEDIA_BUS_FMT_SRGGB14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, +#else /* V4L2_PIX_FMT_SBGGR12P */ + {V4L2_PIX_FMT_SBGGR12P, 12, 12, 0, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGBRG12P, 12, 12, 0, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGRBG12P, 12, 12, 0, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SRGGB12P, 12, 12, 0, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SBGGR14P, 14, 14, 0, MEDIA_BUS_FMT_SBGGR14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SGBRG14P, 14, 14, 0, MEDIA_BUS_FMT_SGBRG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SGRBG14P, 14, 14, 0, MEDIA_BUS_FMT_SGRBG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, + {V4L2_PIX_FMT_SRGGB14P, 14, 14, 0, MEDIA_BUS_FMT_SRGGB14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW14}, +#endif /* V4L2_PIX_FMT_SBGGR12P */ + {V4L2_PIX_FMT_SBGGR10P, 10, 10, 0, MEDIA_BUS_FMT_SBGGR10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SGBRG10P, 10, 10, 0, MEDIA_BUS_FMT_SGBRG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SGRBG10P, 10, 10, 0, MEDIA_BUS_FMT_SGRBG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SRGGB10P, 10, 10, 0, MEDIA_BUS_FMT_SRGGB10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SBGGR8, 8, 8, 0, MEDIA_BUS_FMT_SBGGR8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGBRG8, 8, 8, 0, MEDIA_BUS_FMT_SGBRG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGRBG8, 8, 8, 0, MEDIA_BUS_FMT_SGRBG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SRGGB8, 8, 8, 0, MEDIA_BUS_FMT_SRGGB8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {} +}; + +static int video_open(struct file *file) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct ipu_isys *isys = av->isys; + struct ipu_bus_device *adev = to_ipu_bus_device(&isys->adev->dev); + struct ipu_device *isp = adev->isp; + int rval; + + mutex_lock(&isys->mutex); + + if (isys->reset_needed || isp->flr_done) { + mutex_unlock(&isys->mutex); + dev_warn(&isys->adev->dev, "isys power cycle required\n"); + return -EIO; + } + mutex_unlock(&isys->mutex); + + rval = ipu_buttress_authenticate(isp); + if (rval) { + dev_err(&isys->adev->dev, "FW authentication failed\n"); + return rval; + } + + rval = pm_runtime_get_sync(&isys->adev->dev); + if (rval < 0) { + pm_runtime_put_noidle(&isys->adev->dev); + return rval; + } + + rval = v4l2_fh_open(file); + if (rval) + goto out_power_down; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + rval = ipu_pipeline_pm_use(&av->vdev.entity, 1); +#else + rval = v4l2_pipeline_pm_use(&av->vdev.entity, 1); +#endif + if (rval) + goto out_v4l2_fh_release; + + mutex_lock(&isys->mutex); + + if (isys->video_opened++) { + /* Already open */ + mutex_unlock(&isys->mutex); + return 0; + } + + ipu_configure_spc(adev->isp, + &isys->pdata->ipdata->hw_variant, + IPU_CPD_PKG_DIR_ISYS_SERVER_IDX, + isys->pdata->base, isys->pkg_dir, + isys->pkg_dir_dma_addr); + + /* + * Buffers could have been left to wrong queue at last closure. + * Move them now back to empty buffer queue. + */ + ipu_cleanup_fw_msg_bufs(isys); + + if (isys->fwcom) { + /* + * Something went wrong in previous shutdown. As we are now + * restarting isys we can safely delete old context. + */ + dev_err(&isys->adev->dev, "Clearing old context\n"); + ipu_fw_isys_cleanup(isys); + } + + + rval = ipu_fw_isys_init(av->isys, num_stream_support); + if (rval < 0) + goto out_lib_init; + + mutex_unlock(&isys->mutex); + + return 0; + +out_lib_init: + isys->video_opened--; + mutex_unlock(&isys->mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + ipu_pipeline_pm_use(&av->vdev.entity, 0); +#else + v4l2_pipeline_pm_use(&av->vdev.entity, 0); +#endif + +out_v4l2_fh_release: + v4l2_fh_release(file); +out_power_down: + pm_runtime_put(&isys->adev->dev); + + return rval; +} + +static int video_release(struct file *file) +{ + struct ipu_isys_video *av = video_drvdata(file); + int ret = 0; + + vb2_fop_release(file); + + mutex_lock(&av->isys->mutex); + + if (!--av->isys->video_opened) { + ipu_fw_isys_close(av->isys); + if (av->isys->fwcom) { + av->isys->reset_needed = true; + ret = -EIO; + } + } + + mutex_unlock(&av->isys->mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + ipu_pipeline_pm_use(&av->vdev.entity, 0); +#else + v4l2_pipeline_pm_use(&av->vdev.entity, 0); +#endif + + if (av->isys->reset_needed) + pm_runtime_put_sync(&av->isys->adev->dev); + else + pm_runtime_put(&av->isys->adev->dev); + + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static struct media_pad *other_pad(struct media_pad *pad) +{ + struct media_link *link; + + list_for_each_entry(link, &pad->entity->links, list) { + if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) + != MEDIA_LNK_FL_DATA_LINK) + continue; + + return link->source == pad ? link->sink : link->source; + } + + WARN_ON(1); + return NULL; +} +#endif + +const struct ipu_isys_pixelformat *ipu_isys_get_pixelformat( + struct ipu_isys_video *av, + u32 pixelformat) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct media_pad *pad = + av->vdev.entity.pads[0].flags & MEDIA_PAD_FL_SOURCE ? + av->vdev.entity.links[0].sink : av->vdev.entity.links[0].source; +#else + struct media_pad *pad = other_pad(&av->vdev.entity.pads[0]); +#endif + struct v4l2_subdev *sd; + const u32 *supported_codes; + const struct ipu_isys_pixelformat *pfmt; + + if (!pad || !pad->entity) { + WARN_ON(1); + return NULL; + } + + sd = media_entity_to_v4l2_subdev(pad->entity); + supported_codes = to_ipu_isys_subdev(sd)->supported_codes[pad->index]; + + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) { + unsigned int i; + + if (pfmt->pixelformat != pixelformat) + continue; + + for (i = 0; supported_codes[i]; i++) { + if (pfmt->code == supported_codes[i]) + return pfmt; + } + } + + /* Not found. Get the default, i.e. the first defined one. */ + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) { + if (pfmt->code == *supported_codes) + return pfmt; + } + + WARN_ON(1); + return NULL; +} + +int ipu_isys_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct ipu_isys_video *av = video_drvdata(file); + + strlcpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); + strlcpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + av->isys->media_dev.bus_info); + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING + | V4L2_CAP_DEVICE_CAPS; + + cap->device_caps = V4L2_CAP_STREAMING; + + switch (av->aq.vbq.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT_MPLANE; + break; + default: + WARN_ON(1); + } + + return 0; +} + +int ipu_isys_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct ipu_isys_video *av = video_drvdata(file); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct media_pad *pad = + av->vdev.entity.pads[0].flags & MEDIA_PAD_FL_SOURCE ? + av->vdev.entity.links[0].sink : av->vdev.entity.links[0].source; +#else + struct media_pad *pad = other_pad(&av->vdev.entity.pads[0]); +#endif + struct v4l2_subdev *sd; + const u32 *supported_codes; + const struct ipu_isys_pixelformat *pfmt; + u32 index; + + if (!pad || !pad->entity) + return -EINVAL; + sd = media_entity_to_v4l2_subdev(pad->entity); + supported_codes = to_ipu_isys_subdev(sd)->supported_codes[pad->index]; + + /* Walk the 0-terminated array for the f->index-th code. */ + for (index = f->index; *supported_codes && index; + index--, supported_codes++) { + }; + + if (!*supported_codes) + return -EINVAL; + + f->flags = 0; + + /* Code found */ + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) + if (pfmt->code == *supported_codes) + break; + + if (!pfmt->bpp) { + dev_warn(&av->isys->adev->dev, + "Format not found in mapping table."); + return -EINVAL; + } + + f->pixelformat = pfmt->pixelformat; + + return 0; +} + +static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct ipu_isys_video *av = video_drvdata(file); + + fmt->fmt.pix_mp = av->mpix; + + return 0; +} + +const struct ipu_isys_pixelformat * +ipu_isys_video_try_fmt_vid_mplane_default(struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix) +{ + return ipu_isys_video_try_fmt_vid_mplane(av, mpix, 0); +} + +const struct ipu_isys_pixelformat *ipu_isys_video_try_fmt_vid_mplane( + struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix, + int store_csi2_header) +{ + const struct ipu_isys_pixelformat *pfmt = + ipu_isys_get_pixelformat(av, mpix->pixelformat); + + if (!pfmt) + return NULL; + mpix->pixelformat = pfmt->pixelformat; + mpix->num_planes = 1; + + mpix->width = clamp(mpix->width, IPU_ISYS_MIN_WIDTH, + IPU_ISYS_MAX_WIDTH); + mpix->height = clamp(mpix->height, IPU_ISYS_MIN_HEIGHT, + IPU_ISYS_MAX_HEIGHT); + + if (!av->packed) + mpix->plane_fmt[0].bytesperline = + mpix->width * DIV_ROUND_UP(pfmt->bpp_planar ? + pfmt->bpp_planar : pfmt->bpp, + BITS_PER_BYTE); + else if (store_csi2_header) + mpix->plane_fmt[0].bytesperline = + DIV_ROUND_UP(av->line_header_length + + av->line_footer_length + + (unsigned int)mpix->width * pfmt->bpp, + BITS_PER_BYTE); + else + mpix->plane_fmt[0].bytesperline = + DIV_ROUND_UP((unsigned int)mpix->width * pfmt->bpp, + BITS_PER_BYTE); + + mpix->plane_fmt[0].bytesperline = ALIGN(mpix->plane_fmt[0].bytesperline, + av->isys->line_align); + if (pfmt->bpp_planar) + mpix->plane_fmt[0].bytesperline = + mpix->plane_fmt[0].bytesperline * + pfmt->bpp / pfmt->bpp_planar; + /* + * (height + 1) * bytesperline due to a hardware issue: the DMA unit + * is a power of two, and a line should be transferred as few units + * as possible. The result is that up to line length more data than + * the image size may be transferred to memory after the image. + * Another limition is the GDA allocation unit size. For low + * resolution it gives a bigger number. Use larger one to avoid + * memory corruption. + */ + mpix->plane_fmt[0].sizeimage = + max(max(mpix->plane_fmt[0].sizeimage, + mpix->plane_fmt[0].bytesperline * mpix->height + + max(mpix->plane_fmt[0].bytesperline, + av->isys->pdata->ipdata->isys_dma_overshoot)), 1U); + + memset(mpix->plane_fmt[0].reserved, 0, + sizeof(mpix->plane_fmt[0].reserved)); + + if (mpix->field == V4L2_FIELD_ANY) + mpix->field = V4L2_FIELD_NONE; + /* Use defaults */ + mpix->colorspace = V4L2_COLORSPACE_RAW; + mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mpix->quantization = V4L2_QUANTIZATION_DEFAULT; + mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return pfmt; +} + +static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + + if (av->aq.vbq.streaming) + return -EBUSY; + + av->pfmt = av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); + av->mpix = f->fmt.pix_mp; + + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + + av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); + + return 0; +} + +static void fmt_sp_to_mp(struct v4l2_pix_format_mplane *mpix, + struct v4l2_pix_format *pix) +{ + mpix->width = pix->width; + mpix->height = pix->height; + mpix->pixelformat = pix->pixelformat; + mpix->field = pix->field; + mpix->num_planes = 1; + mpix->plane_fmt[0].bytesperline = pix->bytesperline; + mpix->plane_fmt[0].sizeimage = pix->sizeimage; + mpix->flags = pix->flags; +} + +static void fmt_mp_to_sp(struct v4l2_pix_format *pix, + struct v4l2_pix_format_mplane *mpix) +{ + pix->width = mpix->width; + pix->height = mpix->height; + pix->pixelformat = mpix->pixelformat; + pix->field = mpix->field; + WARN_ON(mpix->num_planes != 1); + pix->bytesperline = mpix->plane_fmt[0].bytesperline; + pix->sizeimage = mpix->plane_fmt[0].sizeimage; + pix->flags = mpix->flags; + pix->colorspace = mpix->colorspace; + pix->ycbcr_enc = mpix->ycbcr_enc; + pix->quantization = mpix->quantization; + pix->xfer_func = mpix->xfer_func; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + + fmt_mp_to_sp(&f->fmt.pix, &av->mpix); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct v4l2_pix_format_mplane mpix = { 0 }; + + if (av->aq.vbq.streaming) + return -EBUSY; + + fmt_sp_to_mp(&mpix, &f->fmt.pix); + + av->pfmt = av->try_fmt_vid_mplane(av, &mpix); + av->mpix = mpix; + + fmt_mp_to_sp(&f->fmt.pix, &mpix); + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct v4l2_pix_format_mplane mpix = { 0 }; + + fmt_sp_to_mp(&mpix, &f->fmt.pix); + + av->try_fmt_vid_mplane(av, &mpix); + + fmt_mp_to_sp(&f->fmt.pix, &mpix); + + return 0; +} + +static long ipu_isys_vidioc_private(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, + void *arg) +{ + struct ipu_isys_video *av = video_drvdata(file); + int ret = 0; + + switch (cmd) { + case VIDIOC_IPU_GET_DRIVER_VERSION: + *(u32 *)arg = IPU_DRIVER_VERSION; + break; + + default: + dev_dbg(&av->isys->adev->dev, "unsupported private ioctl %x\n", + cmd); + } + + return ret; +} + +static int vidioc_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +/* + * Return true if an entity directly connected to an Iunit entity is + * an image source for the ISP. This can be any external directly + * connected entity or any of the test pattern generators in the + * Iunit. + */ +static bool is_external(struct ipu_isys_video *av, struct media_entity *entity) +{ + struct v4l2_subdev *sd; + unsigned int i; + + /* All video nodes are ours. */ + if (!is_media_entity_v4l2_subdev(entity)) + return false; + + sd = media_entity_to_v4l2_subdev(entity); + if (strncmp(sd->name, IPU_ISYS_ENTITY_PREFIX, + strlen(IPU_ISYS_ENTITY_PREFIX)) != 0) + return true; + + for (i = 0; i < av->isys->pdata->ipdata->tpg.ntpgs && + av->isys->tpg[i].isys; i++) + if (entity == &av->isys->tpg[i].asd.sd.entity) + return true; + + return false; +} + +static int link_validate(struct media_link *link) +{ + struct ipu_isys_video *av = + container_of(link->sink, struct ipu_isys_video, pad); + /* All sub-devices connected to a video node are ours. */ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct v4l2_subdev_route r[IPU_ISYS_MAX_STREAMS]; + struct v4l2_subdev_routing routing = { + .routes = r, + .num_routes = IPU_ISYS_MAX_STREAMS, + }; + int i, rval, active = 0; + struct v4l2_subdev *sd; + + if (!link->source->entity) + return -EINVAL; + sd = media_entity_to_v4l2_subdev(link->source->entity); + if (is_external(av, link->source->entity)) { + ip->external = media_entity_remote_pad(av->vdev.entity.pads); + ip->source = to_ipu_isys_subdev(sd)->source; + } + + rval = v4l2_subdev_call(sd, pad, get_routing, &routing); + if (rval) + goto err_subdev; + + for (i = 0; i < routing.num_routes; i++) { + if (!(routing.routes[i].flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (routing.routes[i].source_pad == link->source->index) + ip->stream_id = routing.routes[i].sink_stream; + + active++; + } + + if (ip->external) { + struct v4l2_mbus_frame_desc desc = { + .num_entries = V4L2_FRAME_DESC_ENTRY_MAX, + }; + + sd = media_entity_to_v4l2_subdev(ip->external->entity); + rval = ipu_isys_subdev_get_frame_desc(sd, &desc); + if (!rval && ip->stream_id < desc.num_entries) + ip->vc = desc.entry[ip->stream_id].bus.csi2.channel; + } + +err_subdev: + ip->nr_queues++; + + return 0; +} + +static void get_stream_opened(struct ipu_isys_video *av) +{ + unsigned long flags; + + spin_lock_irqsave(&av->isys->lock, flags); + av->isys->stream_opened++; + spin_unlock_irqrestore(&av->isys->lock, flags); +} + +static void put_stream_opened(struct ipu_isys_video *av) +{ + unsigned long flags; + + spin_lock_irqsave(&av->isys->lock, flags); + av->isys->stream_opened--; + spin_unlock_irqrestore(&av->isys->lock, flags); +} + +static int get_stream_handle(struct ipu_isys_video *av) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + unsigned int stream_handle; + unsigned long flags; + + spin_lock_irqsave(&av->isys->lock, flags); + for (stream_handle = 0; + stream_handle < IPU_ISYS_MAX_STREAMS; stream_handle++) + if (!av->isys->pipes[stream_handle]) + break; + if (stream_handle == IPU_ISYS_MAX_STREAMS) { + spin_unlock_irqrestore(&av->isys->lock, flags); + return -EBUSY; + } + av->isys->pipes[stream_handle] = ip; + ip->stream_handle = stream_handle; + spin_unlock_irqrestore(&av->isys->lock, flags); + return 0; +} + +static void put_stream_handle(struct ipu_isys_video *av) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + unsigned long flags; + + spin_lock_irqsave(&av->isys->lock, flags); + av->isys->pipes[ip->stream_handle] = NULL; + ip->stream_handle = -1; + spin_unlock_irqrestore(&av->isys->lock, flags); +} + +static int get_external_facing_format(struct ipu_isys_pipeline *ip, + struct v4l2_subdev_format *format) +{ + struct ipu_isys_video *av = container_of(ip, struct ipu_isys_video, ip); + struct v4l2_subdev *sd; + struct media_pad *external_facing; + + if (!ip->external->entity) { + WARN_ON(1); + return -ENODEV; + } + sd = media_entity_to_v4l2_subdev(ip->external->entity); + external_facing = (strncmp(sd->name, IPU_ISYS_ENTITY_PREFIX, + strlen(IPU_ISYS_ENTITY_PREFIX)) == 0) ? + ip->external : media_entity_remote_pad(ip->external); + if (WARN_ON(!external_facing)) { + dev_warn(&av->isys->adev->dev, + "no external facing pad --- driver bug?\n"); + return -EINVAL; + } + + format->which = V4L2_SUBDEV_FORMAT_ACTIVE; + format->pad = 0; + format->stream = ip->stream_id; + sd = media_entity_to_v4l2_subdev(external_facing->entity); + + return v4l2_subdev_call(sd, pad, get_fmt, NULL, format); +} + +static void short_packet_queue_destroy(struct ipu_isys_pipeline *ip) +{ + struct ipu_isys_video *av = container_of(ip, struct ipu_isys_video, ip); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + unsigned int i; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + if (!ip->short_packet_bufs) + return; + for (i = 0; i < IPU_ISYS_SHORT_PACKET_BUFFER_NUM; i++) { + if (ip->short_packet_bufs[i].buffer) + dma_free_attrs(&av->isys->adev->dev, + ip->short_packet_buffer_size, + ip->short_packet_bufs[i].buffer, + ip->short_packet_bufs[i].dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &attrs +#else + attrs +#endif + ); + } + kfree(ip->short_packet_bufs); + ip->short_packet_bufs = NULL; +} + +static int short_packet_queue_setup(struct ipu_isys_pipeline *ip) +{ + struct ipu_isys_video *av = container_of(ip, struct ipu_isys_video, ip); + struct v4l2_subdev_format source_fmt = { 0 }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + unsigned int i; + int rval; + size_t buf_size; + + INIT_LIST_HEAD(&ip->pending_interlaced_bufs); + ip->cur_field = V4L2_FIELD_TOP; + + if (ip->isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_TUNIT) { + ip->short_packet_trace_index = 0; + return 0; + } + + rval = get_external_facing_format(ip, &source_fmt); + if (rval) + return rval; + buf_size = IPU_ISYS_SHORT_PACKET_BUF_SIZE(source_fmt.format.height); + ip->short_packet_buffer_size = buf_size; + ip->num_short_packet_lines = + IPU_ISYS_SHORT_PACKET_PKT_LINES(source_fmt.format.height); + + /* Initialize short packet queue. */ + INIT_LIST_HEAD(&ip->short_packet_incoming); + INIT_LIST_HEAD(&ip->short_packet_active); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + + ip->short_packet_bufs = + kzalloc(sizeof(struct ipu_isys_private_buffer) * + IPU_ISYS_SHORT_PACKET_BUFFER_NUM, GFP_KERNEL); + if (!ip->short_packet_bufs) + return -ENOMEM; + + for (i = 0; i < IPU_ISYS_SHORT_PACKET_BUFFER_NUM; i++) { + struct ipu_isys_private_buffer *buf = &ip->short_packet_bufs[i]; + + buf->index = (unsigned int)i; + buf->ip = ip; + buf->ib.type = IPU_ISYS_SHORT_PACKET_BUFFER; + buf->bytesused = buf_size; + buf->buffer = dma_alloc_attrs(&av->isys->adev->dev, buf_size, + &buf->dma_addr, GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &attrs +#else + attrs +#endif + ); + if (!buf->buffer) { + short_packet_queue_destroy(ip); + return -ENOMEM; + } + list_add(&buf->ib.head, &ip->short_packet_incoming); + } + + return 0; +} + +static void csi_short_packet_prepare_firmware_stream_cfg( + struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_stream_cfg_data_abi *cfg) +{ + int input_pin = cfg->nof_input_pins++; + int output_pin = cfg->nof_output_pins++; + struct ipu_fw_isys_input_pin_info_abi *input_info = + &cfg->input_pins[input_pin]; + struct ipu_fw_isys_output_pin_info_abi *output_info = + &cfg->output_pins[output_pin]; + + /* + * Setting dt as IPU_ISYS_SHORT_PACKET_GENERAL_DT will cause + * MIPI receiver to receive all MIPI short packets. + */ + input_info->dt = IPU_ISYS_SHORT_PACKET_GENERAL_DT; + input_info->input_res.width = IPU_ISYS_SHORT_PACKET_WIDTH; + input_info->input_res.height = ip->num_short_packet_lines; + + ip->output_pins[output_pin].pin_ready = + ipu_isys_queue_short_packet_ready; + ip->output_pins[output_pin].aq = NULL; + ip->short_packet_output_pin = output_pin; + + output_info->input_pin_id = input_pin; + output_info->output_res.width = IPU_ISYS_SHORT_PACKET_WIDTH; + output_info->output_res.height = ip->num_short_packet_lines; + output_info->stride = IPU_ISYS_SHORT_PACKET_WIDTH * + IPU_ISYS_SHORT_PACKET_UNITSIZE; + output_info->pt = IPU_ISYS_SHORT_PACKET_PT; + output_info->ft = IPU_ISYS_SHORT_PACKET_FT; + output_info->send_irq = 1; +} + +void ipu_isys_prepare_firmware_stream_cfg_default( + struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi *cfg) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + + struct ipu_isys_queue *aq = &av->aq; + struct ipu_fw_isys_output_pin_info_abi *pin_info; + int pin = cfg->nof_output_pins++; + + aq->fw_output = pin; + ip->output_pins[pin].pin_ready = ipu_isys_queue_buf_ready; + ip->output_pins[pin].aq = aq; + + pin_info = &cfg->output_pins[pin]; + pin_info->input_pin_id = 0; + pin_info->output_res.width = av->mpix.width; + pin_info->output_res.height = av->mpix.height; + + if (!av->pfmt->bpp_planar) + pin_info->stride = av->mpix.plane_fmt[0].bytesperline; + else + pin_info->stride = ALIGN(DIV_ROUND_UP(av->mpix.width * + av->pfmt->bpp_planar, + BITS_PER_BYTE), + av->isys->line_align); + + pin_info->pt = aq->css_pin_type; + pin_info->ft = av->pfmt->css_pixelformat; + pin_info->send_irq = 1; + cfg->vc = ip->vc; +} + +static unsigned int ipu_isys_get_compression_scheme(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: + case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: + return 3; + default: + return 0; + } +} + +static unsigned int get_comp_format(u32 code) +{ + unsigned int predictor = 0; /* currently hard coded */ + unsigned int udt = ipu_isys_mbus_code_to_mipi(code); + unsigned int scheme = ipu_isys_get_compression_scheme(code); + + /* if data type is not user defined return here */ + if (udt < IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(1) || + udt > IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(8)) + return 0; + + /* + * For each user defined type (1..8) there is configuration bitfield for + * decompression. + * + * | bit 3 | bits 2:0 | + * | predictor | scheme | + * compression schemes: + * 000 = no compression + * 001 = 10 - 6 - 10 + * 010 = 10 - 7 - 10 + * 011 = 10 - 8 - 10 + * 100 = 12 - 6 - 12 + * 101 = 12 - 7 - 12 + * 110 = 12 - 8 - 12 + */ + + return ((predictor << 3) | scheme) << + ((udt - IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(1)) * 4); +} + +/* Create stream and start it using the CSS FW ABI. */ +static int start_stream_firmware(struct ipu_isys_video *av, + struct ipu_isys_buffer_list *bl) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct device *dev = &av->isys->adev->dev; + struct v4l2_subdev_selection sel_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .pad = CSI2_BE_PAD_SOURCE, + }; + struct ipu_fw_isys_stream_cfg_data_abi *stream_cfg; + struct isys_fw_msgs *msg = NULL; + struct ipu_fw_isys_frame_buff_set_abi *buf = NULL; + struct ipu_isys_queue *aq; + struct ipu_isys_video *isl_av = NULL; + struct ipu_isys_request *ireq = NULL; + struct v4l2_subdev_format source_fmt = { 0 }; + struct v4l2_subdev *be_sd = NULL; + struct media_pad *source_pad = media_entity_remote_pad(&av->pad); + int rval, rvalout, tout; + + rval = get_external_facing_format(ip, &source_fmt); + if (rval) + return rval; + + msg = ipu_get_fw_msg_buf(ip); + if (!msg) + return -ENOMEM; + + stream_cfg = to_stream_cfg_msg_buf(msg); + stream_cfg->compfmt = get_comp_format(source_fmt.format.code); + stream_cfg->input_pins[0].input_res.width = source_fmt.format.width; + stream_cfg->input_pins[0].input_res.height = source_fmt.format.height; + stream_cfg->input_pins[0].dt = + ipu_isys_mbus_code_to_mipi(source_fmt.format.code); + stream_cfg->input_pins[0].mapped_dt = N_IPU_FW_ISYS_MIPI_DATA_TYPE; + + if (ip->csi2 && !v4l2_ctrl_g_ctrl(ip->csi2->store_csi2_header)) + stream_cfg->input_pins[0].mipi_store_mode = + IPU_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER; + else if (ip->tpg && !v4l2_ctrl_g_ctrl(ip->tpg->store_csi2_header)) + stream_cfg->input_pins[0].mipi_store_mode = + IPU_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER; + + stream_cfg->src = ip->source; + stream_cfg->vc = 0; + stream_cfg->isl_use = ip->isl_mode; + stream_cfg->nof_input_pins = 1; + + /* + * Only CSI2-BE and SOC BE has the capability to do crop, + * so get the crop info from csi2-be or csi2-be-soc. + */ + if (ip->csi2_be) { + be_sd = &ip->csi2_be->asd.sd; + } else if (ip->csi2_be_soc) { + be_sd = &ip->csi2_be_soc->asd.sd; + if (source_pad) + sel_fmt.pad = source_pad->index; + } + if (be_sd && + !v4l2_subdev_call(be_sd, pad, get_selection, NULL, &sel_fmt)) { + stream_cfg->crop[0].left_offset = sel_fmt.r.left; + stream_cfg->crop[0].top_offset = sel_fmt.r.top; + stream_cfg->crop[0].right_offset = sel_fmt.r.left + + sel_fmt.r.width; + stream_cfg->crop[0].bottom_offset = sel_fmt.r.top + + sel_fmt.r.height; + + } else { + stream_cfg->crop[0].right_offset = source_fmt.format.width; + stream_cfg->crop[0].bottom_offset = source_fmt.format.height; + } + + /* + * If the CSI-2 backend's video node is part of the pipeline + * it must be arranged first in the output pin list. This is + * the most probably a firmware requirement. + */ + if (ip->isl_mode == IPU_ISL_CSI2_BE) + isl_av = &ip->csi2_be->av; + else if (ip->isl_mode == IPU_ISL_ISA) + isl_av = &av->isys->isa.av; + + if (isl_av) { + struct ipu_isys_queue *safe; + + list_for_each_entry_safe(aq, safe, &ip->queues, node) { + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + + if (av != isl_av) + continue; + + list_del(&aq->node); + list_add(&aq->node, &ip->queues); + break; + } + } + + list_for_each_entry(aq, &ip->queues, node) { + struct ipu_isys_video *__av = ipu_isys_queue_to_video(aq); + + __av->prepare_firmware_stream_cfg(__av, stream_cfg); + } + + if (ip->interlaced && ip->isys->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) + csi_short_packet_prepare_firmware_stream_cfg(ip, stream_cfg); + + ipu_fw_isys_dump_stream_cfg(dev, stream_cfg); + + ip->nr_output_pins = stream_cfg->nof_output_pins; + + rval = get_stream_handle(av); + if (rval) { + dev_dbg(dev, "Can't get stream_handle\n"); + return rval; + } + + reinit_completion(&ip->stream_open_completion); + + ipu_fw_isys_set_params(stream_cfg); + + rval = ipu_fw_isys_complex_cmd(av->isys, + ip->stream_handle, + stream_cfg, + to_dma_addr(msg), + sizeof(*stream_cfg), + IPU_FW_ISYS_SEND_TYPE_STREAM_OPEN); + ipu_put_fw_mgs_buffer(av->isys, (uintptr_t) stream_cfg); + + if (rval < 0) { + dev_err(dev, "can't open stream (%d)\n", rval); + goto out_put_stream_handle; + } + + get_stream_opened(av); + + tout = wait_for_completion_timeout(&ip->stream_open_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(dev, "stream open time out\n"); + rval = -ETIMEDOUT; + goto out_put_stream_opened; + } + if (ip->error) { + dev_err(dev, "stream open error: %d\n", ip->error); + rval = -EIO; + goto out_put_stream_opened; + } + dev_dbg(dev, "start stream: open complete\n"); + + ireq = ipu_isys_next_queued_request(ip); + + if (bl || ireq) { + msg = ipu_get_fw_msg_buf(ip); + if (!msg) { + rval = -ENOMEM; + goto out_put_stream_opened; + } + buf = to_frame_msg_buf(msg); + } + + if (bl) { + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set(buf, ip, bl); + ipu_isys_buffer_list_queue(bl, + IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + } else if (ireq) { + rval = ipu_isys_req_prepare(&av->isys->media_dev, + ireq, ip, buf); + if (rval) + goto out_put_stream_opened; + } + + reinit_completion(&ip->stream_start_completion); + + if (bl || ireq) { + ipu_fw_isys_dump_frame_buff_set(dev, buf, + stream_cfg->nof_output_pins); + rval = ipu_fw_isys_complex_cmd(av->isys, + ip->stream_handle, + buf, to_dma_addr(msg), + sizeof(*buf), + IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE); + ipu_put_fw_mgs_buffer(av->isys, (uintptr_t) buf); + } else { + rval = ipu_fw_isys_simple_cmd(av->isys, + ip->stream_handle, + IPU_FW_ISYS_SEND_TYPE_STREAM_START); + } + + if (rval < 0) { + dev_err(dev, "can't start streaming (%d)\n", rval); + goto out_stream_close; + } + + tout = wait_for_completion_timeout(&ip->stream_start_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(dev, "stream start time out\n"); + rval = -ETIMEDOUT; + goto out_stream_close; + } + if (ip->error) { + dev_err(dev, "stream start error: %d\n", ip->error); + rval = -EIO; + goto out_stream_close; + } + dev_dbg(dev, "start stream: complete\n"); + + return 0; + +out_stream_close: + reinit_completion(&ip->stream_close_completion); + + rvalout = ipu_fw_isys_simple_cmd(av->isys, + ip->stream_handle, + IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE); + if (rvalout < 0) { + dev_dbg(dev, "can't close stream (%d)\n", rvalout); + goto out_put_stream_opened; + } + + tout = wait_for_completion_timeout(&ip->stream_close_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream close time out\n"); + else if (ip->error) + dev_err(dev, "stream close error: %d\n", ip->error); + else + dev_dbg(dev, "stream close complete\n"); + +out_put_stream_opened: + put_stream_opened(av); + +out_put_stream_handle: + put_stream_handle(av); + return rval; +} + +static void stop_streaming_firmware(struct ipu_isys_video *av) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct device *dev = &av->isys->adev->dev; + int rval, tout; + enum ipu_fw_isys_send_type send_type = + IPU_FW_ISYS_SEND_TYPE_STREAM_FLUSH; + + reinit_completion(&ip->stream_stop_completion); + + /* Use STOP command if running in CSI capture mode */ + if (use_stream_stop) + send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_STOP; + + rval = ipu_fw_isys_simple_cmd(av->isys, ip->stream_handle, + send_type); + + if (rval < 0) { + dev_err(dev, "can't stop stream (%d)\n", rval); + return; + } + + tout = wait_for_completion_timeout(&ip->stream_stop_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream stop time out\n"); + else if (ip->error) + dev_err(dev, "stream stop error: %d\n", ip->error); + else + dev_dbg(dev, "stop stream: complete\n"); +} + +static void close_streaming_firmware(struct ipu_isys_video *av) +{ + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct device *dev = &av->isys->adev->dev; + int rval, tout; + + reinit_completion(&ip->stream_close_completion); + + rval = ipu_fw_isys_simple_cmd(av->isys, ip->stream_handle, + IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE); + if (rval < 0) { + dev_err(dev, "can't close stream (%d)\n", rval); + return; + } + + tout = wait_for_completion_timeout(&ip->stream_close_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream close time out\n"); + else if (ip->error) + dev_err(dev, "stream close error: %d\n", ip->error); + else + dev_dbg(dev, "close stream: complete\n"); + + put_stream_opened(av); + put_stream_handle(av); +} + +void +ipu_isys_video_add_capture_done(struct ipu_isys_pipeline *ip, + void (*capture_done) + (struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *resp)) +{ + unsigned int i; + + /* Different instances may register same function. Add only once */ + for (i = 0; i < IPU_NUM_CAPTURE_DONE; i++) + if (ip->capture_done[i] == capture_done) + return; + + for (i = 0; i < IPU_NUM_CAPTURE_DONE; i++) { + if (!ip->capture_done[i]) { + ip->capture_done[i] = capture_done; + return; + } + } + /* + * Too many call backs registered. Change to IPU_NUM_CAPTURE_DONE + * constant probably required. + */ + WARN_ON(1); +} + +int ipu_isys_video_prepare_streaming(struct ipu_isys_video *av, + unsigned int state) +{ + struct ipu_isys *isys = av->isys; + struct device *dev = &isys->adev->dev; + struct ipu_isys_pipeline *ip; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + struct media_graph graph; +#else + struct media_entity_graph graph; +#endif + struct media_entity *entity; + struct media_device *mdev = &av->isys->media_dev; + int rval; + unsigned int i; + + dev_dbg(dev, "prepare stream: %d\n", state); + + if (!state) { + ip = to_ipu_isys_pipeline(av->vdev.entity.pipe); + + if (ip->interlaced && isys->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) + short_packet_queue_destroy(ip); + media_pipeline_stop(&av->vdev.entity); + media_entity_enum_cleanup(&ip->entity_enum); + return 0; + } + + ip = &av->ip; + + WARN_ON(ip->nr_streaming); + ip->has_sof = false; + ip->nr_queues = 0; + ip->external = NULL; + atomic_set(&ip->sequence, 0); + ip->isl_mode = IPU_ISL_OFF; + + for (i = 0; i < IPU_NUM_CAPTURE_DONE; i++) + ip->capture_done[i] = NULL; + ip->csi2_be = NULL; + ip->csi2_be_soc = NULL; + ip->csi2 = NULL; + ip->tpg = NULL; + ip->seq_index = 0; + memset(ip->seq, 0, sizeof(ip->seq)); + + WARN_ON(!list_empty(&ip->queues)); + ip->interlaced = false; + + rval = media_entity_enum_init(&ip->entity_enum, mdev); + if (rval) + return rval; + + rval = media_pipeline_start(&av->vdev.entity, &ip->pipe); + if (rval < 0) { + dev_dbg(dev, "pipeline start failed\n"); + goto out_enum_cleanup; + } + + if (!ip->external) { + dev_err(dev, "no external entity set! Driver bug?\n"); + rval = -EINVAL; + goto out_pipeline_stop; + } + + rval = media_graph_walk_init(&graph, mdev); + if (rval) + goto out_pipeline_stop; + + /* Gather all entities in the graph. */ + mutex_lock(&mdev->graph_mutex); + media_graph_walk_start(&graph, &av->vdev.entity); + while ((entity = media_graph_walk_next(&graph))) + media_entity_enum_set(&ip->entity_enum, entity); + + mutex_unlock(&mdev->graph_mutex); + + media_graph_walk_cleanup(&graph); + + if (ip->interlaced) { + rval = short_packet_queue_setup(ip); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to setup short packet buffer.\n"); + goto out_pipeline_stop; + } + } + + dev_dbg(dev, "prepare stream: external entity %s\n", + ip->external->entity->name); + + return 0; + +out_pipeline_stop: + media_pipeline_stop(&av->vdev.entity); + +out_enum_cleanup: + media_entity_enum_cleanup(&ip->entity_enum); + + return rval; +} + +static int perform_skew_cal(struct ipu_isys_pipeline *ip) +{ + struct v4l2_subdev *ext_sd = + media_entity_to_v4l2_subdev(ip->external->entity); + int rval; + + if (!ext_sd) { + WARN_ON(1); + return -ENODEV; + } + ipu_isys_csi2_set_skew_cal(ip->csi2, true); + + rval = v4l2_subdev_call(ext_sd, video, s_stream, true); + if (rval) + goto turn_off_skew_cal; + + /* TODO: do we have a better way available than waiting for a while ? */ + msleep(50); + + rval = v4l2_subdev_call(ext_sd, video, s_stream, false); + +turn_off_skew_cal: + ipu_isys_csi2_set_skew_cal(ip->csi2, false); + + /* TODO: do we have a better way available than waiting for a while ? */ + msleep(50); + + return rval; +} + +int ipu_isys_video_set_streaming(struct ipu_isys_video *av, + unsigned int state, + struct ipu_isys_buffer_list *bl) +{ + struct device *dev = &av->isys->adev->dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct media_device *mdev = av->vdev.entity.parent; + struct media_entity_graph graph; +#else + struct media_device *mdev = av->vdev.entity.graph_obj.mdev; +#endif + struct media_entity_enum entities; + + struct media_entity *entity, *entity2; + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + struct v4l2_subdev *sd, *esd; + int rval = 0; + + dev_dbg(dev, "set stream: %d\n", state); + + if (!ip->external->entity) { + WARN_ON(1); + return -ENODEV; + } + esd = media_entity_to_v4l2_subdev(ip->external->entity); + + if (state) { + rval = media_graph_walk_init(&ip->graph, mdev); + if (rval) + return rval; + rval = media_entity_enum_init(&entities, mdev); + if (rval) + goto out_media_entity_graph_init; + } + + if (!state) { + stop_streaming_firmware(av); + + /* stop external sub-device now. */ + dev_err(dev, "s_stream %s (ext)\n", ip->external->entity->name); + + if (ip->csi2) { + if (ip->csi2->stream_count == 1) { + v4l2_subdev_call(esd, video, s_stream, state); + ipu_isys_csi2_wait_last_eof(ip->csi2); + } + } else { + v4l2_subdev_call(esd, video, s_stream, state); + } + } + + mutex_lock(&mdev->graph_mutex); + + media_graph_walk_start(& +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + ip-> +#endif + graph, + &av->vdev.entity); + + while ((entity = media_graph_walk_next(& +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + ip-> +#endif + graph))) { + sd = media_entity_to_v4l2_subdev(entity); + + dev_dbg(dev, "set stream: entity %s\n", entity->name); + + /* Non-subdev nodes can be safely ignored here. */ + if (!is_media_entity_v4l2_subdev(entity)) + continue; + + /* Don't start truly external devices quite yet. */ + if (strncmp(sd->name, IPU_ISYS_ENTITY_PREFIX, + strlen(IPU_ISYS_ENTITY_PREFIX)) != 0 || + ip->external->entity == entity) + continue; + + dev_dbg(dev, "s_stream %s\n", entity->name); + rval = v4l2_subdev_call(sd, video, s_stream, state); + if (!state) + continue; + if (rval && rval != -ENOIOCTLCMD) { + mutex_unlock(&mdev->graph_mutex); + goto out_media_entity_stop_streaming; + } + + media_entity_enum_set(&entities, entity); + } + + mutex_unlock(&mdev->graph_mutex); + + /* Oh crap */ + if (state) { + if (ipu_isys_csi2_skew_cal_required(ip->csi2) && + ip->csi2->remote_streams == ip->csi2->stream_count) + perform_skew_cal(ip); + + rval = start_stream_firmware(av, bl); + if (rval) + goto out_media_entity_stop_streaming; + + dev_dbg(dev, "set stream: source %d, stream_handle %d\n", + ip->source, ip->stream_handle); + + /* Start external sub-device now. */ + dev_dbg(dev, "set stream: s_stream %s (ext)\n", + ip->external->entity->name); + + if (ip->csi2 && + ip->csi2->remote_streams == ip->csi2->stream_count) + rval = v4l2_subdev_call(esd, video, s_stream, state); + else if (!ip->csi2) + rval = v4l2_subdev_call(esd, video, s_stream, state); + if (rval) + goto out_media_entity_stop_streaming_firmware; + } else { + close_streaming_firmware(av); + av->ip.stream_id = 0; + av->ip.vc = 0; + } + + if (state) + media_entity_enum_cleanup(&entities); + else + media_graph_walk_cleanup(&ip->graph); + av->streaming = state; + + return 0; + +out_media_entity_stop_streaming_firmware: + stop_streaming_firmware(av); + +out_media_entity_stop_streaming: + mutex_lock(&mdev->graph_mutex); + + media_graph_walk_start(& +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + ip-> +#endif + graph, + &av->vdev.entity); + + while (state && (entity2 = media_graph_walk_next(& +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + ip-> +#endif + graph)) && + entity2 != entity) { + sd = media_entity_to_v4l2_subdev(entity2); + + if (!media_entity_enum_test(&entities, entity2)) + continue; + + v4l2_subdev_call(sd, video, s_stream, 0); + } + + mutex_unlock(&mdev->graph_mutex); + + media_entity_enum_cleanup(&entities); + +out_media_entity_graph_init: + media_graph_walk_cleanup(&ip->graph); + + return rval; +} + +#ifdef CONFIG_COMPAT +static long ipu_isys_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long ret = -ENOIOCTLCMD; + void __user *up = compat_ptr(arg); + + /* + * at present, there is not any private IOCTL need to compat handle + */ + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)up); + + return ret; +} +#endif + +static const struct v4l2_ioctl_ops ioctl_ops_splane = { + .vidioc_querycap = ipu_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ipu_isys_vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_default = ipu_isys_vidioc_private, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, +}; + +static const struct v4l2_ioctl_ops ioctl_ops_mplane = { + .vidioc_querycap = ipu_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap_mplane = ipu_isys_vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_default = ipu_isys_vidioc_private, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, +}; + +static const struct media_entity_operations entity_ops = { + .link_validate = link_validate, +}; + +static const struct v4l2_file_operations isys_fops = { + .owner = THIS_MODULE, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ipu_isys_compat_ioctl, +#endif + .mmap = vb2_fop_mmap, + .open = video_open, + .release = video_release, +}; + +/* + * Do everything that's needed to initialise things related to video + * buffer queue, video node, and the related media entity. The caller + * is expected to assign isys field and set the name of the video + * device. + */ +int ipu_isys_video_init(struct ipu_isys_video *av, + struct media_entity *entity, + unsigned int pad, unsigned long pad_flags, + unsigned int flags) +{ + const struct v4l2_ioctl_ops *ioctl_ops = NULL; + int rval; + + mutex_init(&av->mutex); + init_completion(&av->ip.stream_open_completion); + init_completion(&av->ip.stream_close_completion); + init_completion(&av->ip.stream_start_completion); + init_completion(&av->ip.stream_stop_completion); + init_completion(&av->ip.capture_ack_completion); + INIT_LIST_HEAD(&av->ip.queues); + spin_lock_init(&av->ip.short_packet_queue_lock); + av->ip.isys = av->isys; + av->ip.stream_id = 0; + av->ip.vc = 0; + + if (pad_flags & MEDIA_PAD_FL_SINK) { + /* data_offset is available only for multi-plane buffers */ + if (av->line_header_length) { + av->aq.vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + ioctl_ops = &ioctl_ops_mplane; + } else { + av->aq.vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ioctl_ops = &ioctl_ops_splane; + } + av->vdev.vfl_dir = VFL_DIR_RX; + } else { + av->aq.vbq.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + av->vdev.vfl_dir = VFL_DIR_TX; + } + rval = ipu_isys_queue_init(&av->aq); + if (rval) + goto out_mutex_destroy; + + av->pad.flags = pad_flags | MEDIA_PAD_FL_MUST_CONNECT; + rval = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); + if (rval) + goto out_ipu_isys_queue_cleanup; + + av->vdev.entity.ops = &entity_ops; + av->vdev.release = video_device_release_empty; + av->vdev.fops = &isys_fops; + av->vdev.v4l2_dev = &av->isys->v4l2_dev; + if (!av->vdev.ioctl_ops) + av->vdev.ioctl_ops = ioctl_ops; + av->vdev.queue = &av->aq.vbq; + av->vdev.lock = &av->mutex; + set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); + video_set_drvdata(&av->vdev, av); + + mutex_lock(&av->mutex); + + rval = video_register_device(&av->vdev, VFL_TYPE_GRABBER, -1); + if (rval) + goto out_media_entity_cleanup; + + if (pad_flags & MEDIA_PAD_FL_SINK) + rval = media_create_pad_link(entity, pad, + &av->vdev.entity, 0, flags); + else + rval = media_create_pad_link(&av->vdev.entity, 0, entity, + pad, flags); + if (rval) { + dev_info(&av->isys->adev->dev, "can't create link\n"); + goto out_media_entity_cleanup; + } + + av->pfmt = av->try_fmt_vid_mplane(av, &av->mpix); + + mutex_unlock(&av->mutex); + + return rval; + +out_media_entity_cleanup: + video_unregister_device(&av->vdev); + mutex_unlock(&av->mutex); + media_entity_cleanup(&av->vdev.entity); + +out_ipu_isys_queue_cleanup: + ipu_isys_queue_cleanup(&av->aq); + +out_mutex_destroy: + mutex_destroy(&av->mutex); + + return rval; +} + +void ipu_isys_video_cleanup(struct ipu_isys_video *av) +{ + video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + mutex_destroy(&av->mutex); + ipu_isys_queue_cleanup(&av->aq); +} diff --git a/drivers/media/pci/intel/ipu-isys-video.h b/drivers/media/pci/intel/ipu-isys-video.h new file mode 100644 index 000000000000..c1375f70a897 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys-video.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_VIDEO_H +#define IPU_ISYS_VIDEO_H + +#include +#include +#include +#include +#include +#include + +#include "ipu-isys-queue.h" + +#define IPU_ISYS_OUTPUT_PINS 11 +#define IPU_NUM_CAPTURE_DONE 2 +#define IPU_ISYS_MAX_PARALLEL_SOF 2 + +struct ipu_isys; +struct ipu_isys_csi2_be_soc; +struct ipu_fw_isys_stream_cfg_data_abi; + +struct ipu_isys_pixelformat { + u32 pixelformat; + u32 bpp; + u32 bpp_packed; + u32 bpp_planar; + u32 code; + u32 css_pixelformat; +}; + +struct sequence_info { + unsigned int sequence; + u64 timestamp; +}; + +struct output_pin_data { + void (*pin_ready)(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info); + struct ipu_isys_queue *aq; +}; + +struct ipu_isys_pipeline { + struct media_pipeline pipe; + struct media_pad *external; + atomic_t sequence; + unsigned int seq_index; + struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; + int source; /* SSI stream source */ + int stream_handle; /* stream handle for CSS API */ + unsigned int nr_output_pins; /* How many firmware pins? */ + enum ipu_isl_mode isl_mode; + struct ipu_isys_csi2_be *csi2_be; + struct ipu_isys_csi2_be_soc *csi2_be_soc; + struct ipu_isys_csi2 *csi2; + struct ipu_isys_tpg *tpg; + /* + * Number of capture queues, write access serialised using struct + * ipu_isys.stream_mutex + */ + int nr_queues; + int nr_streaming; /* Number of capture queues streaming */ + int streaming; /* Has streaming been really started? */ + struct list_head queues; + struct completion stream_open_completion; + struct completion stream_close_completion; + struct completion stream_start_completion; + struct completion stream_stop_completion; + struct completion capture_ack_completion; + struct ipu_isys *isys; + + void (*capture_done[IPU_NUM_CAPTURE_DONE]) + (struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *resp); + struct output_pin_data output_pins[IPU_ISYS_OUTPUT_PINS]; + bool has_sof; + bool interlaced; + int error; + struct ipu_isys_private_buffer *short_packet_bufs; + size_t short_packet_buffer_size; + unsigned int num_short_packet_lines; + unsigned int short_packet_output_pin; + unsigned int cur_field; + struct list_head short_packet_incoming; + struct list_head short_packet_active; + /* Serialize access to short packet active and incoming lists */ + spinlock_t short_packet_queue_lock; + struct list_head pending_interlaced_bufs; + unsigned int short_packet_trace_index; + unsigned int vc; + unsigned int stream_id; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + struct media_graph graph; +#else + struct media_entity_graph graph; +#endif +#endif + struct media_entity_enum entity_enum; +}; + +#define to_ipu_isys_pipeline(__pipe) \ + container_of((__pipe), struct ipu_isys_pipeline, pipe) + +struct ipu_isys_video { + /* Serialise access to other fields in the struct. */ + struct mutex mutex; + struct media_pad pad; + struct video_device vdev; + struct v4l2_pix_format_mplane mpix; + const struct ipu_isys_pixelformat *pfmts; + const struct ipu_isys_pixelformat *pfmt; + struct ipu_isys_queue aq; + struct ipu_isys *isys; + struct ipu_isys_pipeline ip; + unsigned int streaming; + bool packed; + unsigned int line_header_length; /* bits */ + unsigned int line_footer_length; /* bits */ + const struct ipu_isys_pixelformat *(*try_fmt_vid_mplane)( + struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix); + void (*prepare_firmware_stream_cfg)(struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi *cfg); +}; + +#define ipu_isys_queue_to_video(__aq) \ + container_of(__aq, struct ipu_isys_video, aq) + +extern const struct ipu_isys_pixelformat ipu_isys_pfmts[]; +extern const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[]; +extern const struct ipu_isys_pixelformat ipu_isys_pfmts_packed[]; + +const struct ipu_isys_pixelformat * +ipu_isys_get_pixelformat(struct ipu_isys_video *av, u32 pixelformat); + +int ipu_isys_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap); + +int ipu_isys_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f); + +const struct ipu_isys_pixelformat * +ipu_isys_video_try_fmt_vid_mplane_default(struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix); + +const struct ipu_isys_pixelformat * +ipu_isys_video_try_fmt_vid_mplane(struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix, + int store_csi2_header); + +void ipu_isys_prepare_firmware_stream_cfg_default( + struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi *cfg); +int ipu_isys_video_prepare_streaming(struct ipu_isys_video *av, + unsigned int state); +int ipu_isys_video_set_streaming(struct ipu_isys_video *av, unsigned int state, + struct ipu_isys_buffer_list *bl); +int ipu_isys_video_init(struct ipu_isys_video *av, struct media_entity *source, + unsigned int source_pad, unsigned long pad_flags, + unsigned int flags); +void ipu_isys_video_cleanup(struct ipu_isys_video *av); +void ipu_isys_video_add_capture_done(struct ipu_isys_pipeline *ip, + void (*capture_done) + (struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *resp)); + +#endif /* IPU_ISYS_VIDEO_H */ diff --git a/drivers/media/pci/intel/ipu-isys.c b/drivers/media/pci/intel/ipu-isys.c new file mode 100644 index 000000000000..aa8dec010872 --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys.c @@ -0,0 +1,1466 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) +#include +#endif +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-cpd.h" +#include "ipu-mmu.h" +#include "ipu-dma.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2.h" +#include "ipu-isys-tpg.h" +#include "ipu-isys-video.h" +#include "ipu-platform-regs.h" +#include "ipu-buttress.h" +#include "ipu-platform.h" +#include "ipu-platform-buttress-regs.h" + +#define ISYS_PM_QOS_VALUE 300 + +/* + * The param was passed from module to indicate if port + * could be optimized. + */ +static bool csi2_port_optimized = true; +module_param(csi2_port_optimized, bool, 0660); +MODULE_PARM_DESC(csi2_port_optimized, "IPU CSI2 port optimization"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/* + * BEGIN adapted code from drivers/media/platform/omap3isp/isp.c. + * FIXME: This (in terms of functionality if not code) should be most + * likely generalised in the framework, and use made optional for + * drivers. + */ +/* + * ipu_pipeline_pm_use_count - Count the number of users of a pipeline + * @entity: The entity + * + * Return the total number of users of all video device nodes in the pipeline. + */ +static int ipu_pipeline_pm_use_count(struct media_pad *pad) +{ + struct media_entity_graph graph; + struct media_entity *entity = pad->entity; + int use = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_init(&graph, entity->graph_obj.mdev); +#endif + media_graph_walk_start(&graph, pad); + + while ((entity = media_graph_walk_next(&graph))) { + if (is_media_entity_v4l2_io(entity)) + use += entity->use_count; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_cleanup(&graph); +#endif + return use; +} + +/* + * ipu_pipeline_pm_power_one - Apply power change to an entity + * @entity: The entity + * @change: Use count change + * + * Change the entity use count by @change. If the entity is a subdev update its + * power state by calling the core::s_power operation when the use count goes + * from 0 to != 0 or from != 0 to 0. + * + * Return 0 on success or a negative error code on failure. + */ +static int ipu_pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + int ret; + + subdev = is_media_entity_v4l2_subdev(entity) + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev) { + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +/* + * ipu_get_linked_pad - Find internally connected pad for a given pad + * @entity: The entity + * @pad: Initial pad + * + * Return index of the linked pad. + */ +static int ipu_get_linked_pad(struct media_entity *entity, + struct media_pad *pad) +{ + int i; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *opposite_pad = &entity->pads[i]; + + if (opposite_pad == pad) + continue; + + if (media_entity_has_route(entity, pad->index, + opposite_pad->index)) + return opposite_pad->index; + } + + return 0; +} + +/* + * ipu_pipeline_pm_power - Apply power change to all entities + * in a pipeline + * @entity: The entity + * @change: Use count change + * @from_pad: Starting pad + * + * Walk the pipeline to update the use count and the power state of + * all non-node + * entities. + * + * Return 0 on success or a negative error code on failure. + */ +static int ipu_pipeline_pm_power(struct media_entity *entity, + int change, int from_pad) +{ + struct media_entity_graph graph; + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_init(&graph, entity->graph_obj.mdev); +#endif + media_graph_walk_start(&graph, &entity->pads[from_pad]); + + while (!ret && (entity = media_graph_walk_next(&graph))) + if (!is_media_entity_v4l2_io(entity)) + ret = ipu_pipeline_pm_power_one(entity, change); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_cleanup(&graph); +#endif + if (!ret) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_init(&graph, entity->graph_obj.mdev); +#endif + media_graph_walk_start(&graph, &first->pads[from_pad]); + + while ((first = media_graph_walk_next(&graph)) && + first != entity) + if (!is_media_entity_v4l2_io(first)) + ipu_pipeline_pm_power_one(first, -change); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_graph_walk_cleanup(&graph); +#endif + return ret; +} + +/* + * ipu_pipeline_pm_use - Update the use count of an entity + * @entity: The entity + * @use: Use (1) or stop using (0) the entity + * + * Update the use count of all entities in the pipeline and power entities + * on or off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. No failure can occur when the use parameter is + * set to 0. + */ +int ipu_pipeline_pm_use(struct media_entity *entity, int use) +{ + int change = use ? 1 : -1; + int ret; + + mutex_lock(&entity-> +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + parent +#else + graph_obj.mdev +#endif + ->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = ipu_pipeline_pm_power(entity, change, 0); + if (ret < 0) + entity->use_count -= change; + + mutex_unlock(&entity-> +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + parent +#else + graph_obj.mdev +#endif + ->graph_mutex); + + return ret; +} + +/* + * ipu_pipeline_link_notify - Link management notification callback + * @link: The link + * @flags: New link flags that will be applied + * @notification: The link's state change notification type + * (MEDIA_DEV_NOTIFY_*) + * + * React to link management on powered pipelines by updating the use count of + * all entities in the source and sink sides of the link. Entities are powered + * on or off accordingly. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. This function will not fail for disconnection + * events. + */ +static int ipu_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use = ipu_pipeline_pm_use_count(link->source); + int sink_use = ipu_pipeline_pm_use_count(link->sink); + int ret; + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + ipu_pipeline_pm_power(source, -sink_use, 0); + ipu_pipeline_pm_power(sink, -source_use, 0); + return 0; + } + + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + int from_pad = ipu_get_linked_pad(source, link->source); + + ret = ipu_pipeline_pm_power(source, sink_use, from_pad); + if (ret < 0) + return ret; + + ret = ipu_pipeline_pm_power(sink, source_use, 0); + if (ret < 0) + ipu_pipeline_pm_power(source, -sink_use, 0); + + return ret; + } + + return 0; +} + +/* END adapted code from drivers/media/platform/omap3isp/isp.c */ +#endif /* < v4.6 */ + +struct isys_i2c_test { + u8 bus_nr; + u16 addr; + struct i2c_client *client; +}; + +static int isys_i2c_test(struct device *dev, void *priv) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct isys_i2c_test *test = priv; + + if (!client) + return 0; + + if (i2c_adapter_id(client->adapter) != test->bus_nr || + client->addr != test->addr) + return 0; + + test->client = client; + + return 0; +} + +static struct +i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, + struct ipu_isys_subdev_info *sd_info) +{ + struct i2c_board_info *info = &sd_info->i2c.board_info; + struct isys_i2c_test test = { + .bus_nr = i2c_adapter_id(adapter), + .addr = info->addr, + }; + int rval; + + rval = i2c_for_each_dev(&test, isys_i2c_test); + if (rval || !test.client) + return NULL; + return test.client; +} + +static int +isys_complete_ext_device_registration(struct ipu_isys *isys, + struct v4l2_subdev *sd, + struct ipu_isys_csi2_config *csi2) +{ + unsigned int i; + int rval; + + v4l2_set_subdev_hostdata(sd, csi2); + + for (i = 0; i < sd->entity.num_pads; i++) { + if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + + if (i == sd->entity.num_pads) { + dev_warn(&isys->adev->dev, + "no source pad in external entity\n"); + rval = -ENOENT; + goto skip_unregister_subdev; + } + + rval = media_create_pad_link(&sd->entity, i, + &isys->csi2[csi2->port].asd.sd.entity, + 0, 0); + if (rval) { + dev_warn(&isys->adev->dev, "can't create link\n"); + goto skip_unregister_subdev; + } + + isys->csi2[csi2->port].nlanes = csi2->nlanes; + return 0; + +skip_unregister_subdev: + v4l2_device_unregister_subdev(sd); + return rval; +} + +static int isys_register_ext_subdev(struct ipu_isys *isys, + struct ipu_isys_subdev_info *sd_info) +{ + struct i2c_adapter *adapter; + struct v4l2_subdev *sd; + struct i2c_client *client; + int rval; + int bus; + +#ifdef I2C_WA + bus = ipu_get_i2c_bus_id(sd_info->i2c.i2c_adapter_id); + if (bus < 0) { + dev_err(&isys->adev->dev, "Failed to find adapter!"); + return -ENOENT; + } +#else + bus = sd_info->i2c.i2c_adapter_id; +#endif + adapter = i2c_get_adapter(bus); + if (!adapter) { + dev_warn(&isys->adev->dev, "can't find adapter\n"); + return -ENOENT; + } + + dev_info(&isys->adev->dev, + "creating new i2c subdev for %s (address %2.2x, bus %d)", + sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, + bus); + + if (sd_info->csi2) { + dev_info(&isys->adev->dev, "sensor device on CSI port: %d\n", + sd_info->csi2->port); + if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || + !isys->csi2[sd_info->csi2->port].isys) { + dev_warn(&isys->adev->dev, "invalid csi2 port %u\n", + sd_info->csi2->port); + rval = -EINVAL; + goto skip_put_adapter; + } + } else { + dev_info(&isys->adev->dev, "non camera subdevice\n"); + } + + client = isys_find_i2c_subdev(adapter, sd_info); + if (client) { + dev_dbg(&isys->adev->dev, "Device exists\n"); + rval = 0; + goto skip_put_adapter; + } + + sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, + &sd_info->i2c.board_info, NULL); + if (!sd) { + dev_warn(&isys->adev->dev, "can't create new i2c subdev\n"); + rval = -EINVAL; + goto skip_put_adapter; + } + + if (!sd_info->csi2) + return 0; + + return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); + +skip_put_adapter: + i2c_put_adapter(adapter); + + return rval; +} + +static void isys_register_ext_subdevs(struct ipu_isys *isys) +{ + struct ipu_isys_subdev_pdata *spdata = isys->pdata->spdata; + struct ipu_isys_subdev_info **sd_info; + + if (!spdata) { + dev_info(&isys->adev->dev, "no subdevice info provided\n"); + return; + } + for (sd_info = spdata->subdevs; *sd_info; sd_info++) + isys_register_ext_subdev(isys, *sd_info); +} + +static void isys_unregister_subdevices(struct ipu_isys *isys) +{ + const struct ipu_isys_internal_tpg_pdata *tpg = + &isys->pdata->ipdata->tpg; + const struct ipu_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + unsigned int i; + + ipu_isys_csi2_be_cleanup(&isys->csi2_be); + ipu_isys_csi2_be_soc_cleanup(&isys->csi2_be_soc); + + ipu_isys_isa_cleanup(&isys->isa); + + for (i = 0; i < tpg->ntpgs; i++) + ipu_isys_tpg_cleanup(&isys->tpg[i]); + + for (i = 0; i < csi2->nports; i++) + ipu_isys_csi2_cleanup(&isys->csi2[i]); +} + +static int isys_register_subdevices(struct ipu_isys *isys) +{ + const struct ipu_isys_internal_tpg_pdata *tpg = + &isys->pdata->ipdata->tpg; + const struct ipu_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + struct ipu_isys_subdev_pdata *spdata = isys->pdata->spdata; + struct ipu_isys_subdev_info **sd_info; + DECLARE_BITMAP(csi2_enable, 32); + unsigned int i, j, k; + int rval; + + /* + * Here is somewhat a workaround, let each platform decide + * if csi2 port can be optimized, which means only registered + * port from pdata would be enabled. + */ + if (csi2_port_optimized && spdata) { + bitmap_zero(csi2_enable, 32); + for (sd_info = spdata->subdevs; *sd_info; sd_info++) { + if ((*sd_info)->csi2) { + i = (*sd_info)->csi2->port; + if (i >= csi2->nports) { + dev_warn(&isys->adev->dev, + "invalid csi2 port %u\n", i); + continue; + } + bitmap_set(csi2_enable, i, 1); + } + } + } else { + bitmap_fill(csi2_enable, 32); + } + + isys->csi2 = devm_kcalloc(&isys->adev->dev, csi2->nports, + sizeof(*isys->csi2), GFP_KERNEL); + if (!isys->csi2) { + rval = -ENOMEM; + goto fail; + } + + for (i = 0; i < csi2->nports; i++) { + if (!test_bit(i, csi2_enable)) + continue; + + rval = ipu_isys_csi2_init(&isys->csi2[i], isys, + isys->pdata->base + + csi2->offsets[i], i); + if (rval) + goto fail; + + isys->isr_csi2_bits |= IPU_ISYS_UNISPART_IRQ_CSI2(i); + } + + isys->tpg = devm_kcalloc(&isys->adev->dev, tpg->ntpgs, + sizeof(*isys->tpg), GFP_KERNEL); + if (!isys->tpg) { + rval = -ENOMEM; + goto fail; + } + + for (i = 0; i < tpg->ntpgs; i++) { + rval = ipu_isys_tpg_init(&isys->tpg[i], isys, + isys->pdata->base + + tpg->offsets[i], + tpg->sels ? (isys->pdata->base + + tpg->sels[i]) : NULL, i); + if (rval) + goto fail; + } + + rval = ipu_isys_csi2_be_soc_init(&isys->csi2_be_soc, isys); + if (rval) { + dev_info(&isys->adev->dev, + "can't register soc csi2 be device\n"); + goto fail; + } + + rval = ipu_isys_csi2_be_init(&isys->csi2_be, isys); + if (rval) { + dev_info(&isys->adev->dev, + "can't register raw csi2 be device\n"); + goto fail; + } + rval = ipu_isys_isa_init(&isys->isa, isys, NULL); + if (rval) { + dev_info(&isys->adev->dev, "can't register isa device\n"); + goto fail; + } + + for (i = 0; i < csi2->nports; i++) { + if (!test_bit(i, csi2_enable)) + continue; + + for (j = CSI2_PAD_SOURCE(0); + j < (NR_OF_CSI2_SOURCE_PADS + CSI2_PAD_SOURCE(0)); j++) { + rval = + media_create_pad_link(&isys->csi2[i].asd.sd.entity, + j, + &isys->csi2_be.asd.sd.entity, + CSI2_BE_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link csi2 <=> csi2_be\n"); + goto fail; + } + + for (k = CSI2_BE_SOC_PAD_SINK(0); + k < NR_OF_CSI2_BE_SOC_SINK_PADS; k++) { + rval = + media_create_pad_link(&isys->csi2[i].asd.sd. + entity, j, + &isys->csi2_be_soc. + asd.sd.entity, k, + MEDIA_LNK_FL_DYNAMIC); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link csi2->be_soc\n"); + goto fail; + } + } + } + } + + for (i = 0; i < tpg->ntpgs; i++) { + rval = media_create_pad_link(&isys->tpg[i].asd.sd.entity, + TPG_PAD_SOURCE, + &isys->csi2_be.asd.sd.entity, + CSI2_BE_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between tpg and csi2_be\n"); + goto fail; + } + + for (k = CSI2_BE_SOC_PAD_SINK(0); + k < NR_OF_CSI2_BE_SOC_SINK_PADS; k++) { + rval = + media_create_pad_link(&isys->tpg[i].asd.sd.entity, + TPG_PAD_SOURCE, + &isys->csi2_be_soc.asd.sd. + entity, k, + MEDIA_LNK_FL_DYNAMIC); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link tpg->be_soc\n"); + goto fail; + } + } + } + + rval = media_create_pad_link(&isys->csi2_be.asd.sd.entity, + CSI2_BE_PAD_SOURCE, + &isys->isa.asd.sd.entity, ISA_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between CSI2 raw be and ISA\n"); + goto fail; + } + return 0; + +fail: + isys_unregister_subdevices(isys); + return rval; +} + +static struct media_device_ops isys_mdev_ops = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + .link_notify = ipu_pipeline_link_notify, +#else + .link_notify = v4l2_pipeline_link_notify, +#endif + .req_alloc = ipu_isys_req_alloc, + .req_free = ipu_isys_req_free, + .req_queue = ipu_isys_req_queue, +}; + +static int isys_register_devices(struct ipu_isys *isys) +{ + int rval; + + isys->media_dev.dev = &isys->adev->dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 12) + isys->media_dev.ops = &isys_mdev_ops; +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + isys->media_dev.link_notify = ipu_pipeline_link_notify; +#else + isys->media_dev.link_notify = v4l2_pipeline_link_notify; +#endif + strlcpy(isys->media_dev.model, + IPU_MEDIA_DEV_MODEL_NAME, sizeof(isys->media_dev.model)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + isys->media_dev.driver_version = LINUX_VERSION_CODE; +#endif + snprintf(isys->media_dev.bus_info, sizeof(isys->media_dev.bus_info), + "pci:%s", dev_name(isys->adev->dev.parent->parent)); + strlcpy(isys->v4l2_dev.name, isys->media_dev.model, + sizeof(isys->v4l2_dev.name)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_device_init(&isys->media_dev); +#endif + + rval = media_device_register(&isys->media_dev); + if (rval < 0) { + dev_info(&isys->adev->dev, "can't register media device\n"); + goto out_media_device_unregister; + } + + isys->v4l2_dev.mdev = &isys->media_dev; + + rval = v4l2_device_register(&isys->adev->dev, &isys->v4l2_dev); + if (rval < 0) { + dev_info(&isys->adev->dev, "can't register v4l2 device\n"); + goto out_media_device_unregister; + } + + rval = isys_register_subdevices(isys); + if (rval) + goto out_v4l2_device_unregister; + + isys_register_ext_subdevs(isys); + + rval = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); + if (rval) + goto out_isys_unregister_subdevices; + + return 0; + +out_isys_unregister_subdevices: + isys_unregister_subdevices(isys); + +out_v4l2_device_unregister: + v4l2_device_unregister(&isys->v4l2_dev); + +out_media_device_unregister: + media_device_unregister(&isys->media_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_device_cleanup(&isys->media_dev); +#endif + + return rval; +} + +static void isys_unregister_devices(struct ipu_isys *isys) +{ + isys_unregister_subdevices(isys); + v4l2_device_unregister(&isys->v4l2_dev); + media_device_unregister(&isys->media_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + media_device_cleanup(&isys->media_dev); +#endif +} + +#ifdef CONFIG_PM +static int isys_runtime_pm_resume(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_device *isp = adev->isp; + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + unsigned long flags; + int ret; + + if (!isys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + + ipu_trace_restore(dev); + + pm_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); + + ret = ipu_buttress_start_tsc_sync(isp); + if (ret) + return ret; + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 1; + spin_unlock_irqrestore(&isys->power_lock, flags); + + if (isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_TUNIT) { + mutex_lock(&isys->short_packet_tracing_mutex); + isys->short_packet_tracing_count = 0; + mutex_unlock(&isys->short_packet_tracing_mutex); + } + isys_setup_hw(isys); + + return 0; +} + +static int isys_runtime_pm_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + unsigned long flags; + + if (!isys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 0; + spin_unlock_irqrestore(&isys->power_lock, flags); + + ipu_trace_stop(dev); + mutex_lock(&isys->mutex); + isys->reset_needed = false; + mutex_unlock(&isys->mutex); + + pm_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + + return 0; +} + +static int isys_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + + /* If stream is open, refuse to suspend */ + if (isys->stream_opened) + return -EBUSY; + + return 0; +} + +static int isys_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops isys_pm_ops = { + .runtime_suspend = isys_runtime_pm_suspend, + .runtime_resume = isys_runtime_pm_resume, + .suspend = isys_suspend, + .resume = isys_resume, +}; + +#define ISYS_PM_OPS (&isys_pm_ops) +#else +#define ISYS_PM_OPS NULL +#endif + +static void isys_remove(struct ipu_bus_device *adev) +{ + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + struct ipu_device *isp = adev->isp; + struct isys_fw_msgs *fwmsg, *safe; + + dev_info(&adev->dev, "removed\n"); + if (isp->ipu_dir) + debugfs_remove_recursive(isys->debugfsdir); + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) { + dma_free_attrs(&adev->dev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + } + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) { + dma_free_attrs(&adev->dev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + } + + ipu_trace_uninit(&adev->dev); + isys_unregister_devices(isys); + pm_qos_remove_request(&isys->pm_qos); + + if (!isp->secure_mode) { + ipu_cpd_free_pkg_dir(adev, isys->pkg_dir, + isys->pkg_dir_dma_addr, + isys->pkg_dir_size); + ipu_buttress_unmap_fw_image(adev, &isys->fw_sgt); + release_firmware(isys->fw); + } + + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); + + if (isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_TUNIT) { + u32 trace_size = IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; + + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + dma_free_attrs(&adev->dev, trace_size, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, + &attrs); +#else + unsigned long attrs; + + attrs = DMA_ATTR_NON_CONSISTENT; + dma_free_attrs(&adev->dev, trace_size, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, attrs); +#endif + } +} + +static int ipu_isys_icache_prefetch_get(void *data, u64 *val) +{ + struct ipu_isys *isys = data; + + *val = isys->icache_prefetch; + return 0; +} + +static int ipu_isys_icache_prefetch_set(void *data, u64 val) +{ + struct ipu_isys *isys = data; + + if (val != !!val) + return -EINVAL; + + isys->icache_prefetch = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(isys_icache_prefetch_fops, + ipu_isys_icache_prefetch_get, + ipu_isys_icache_prefetch_set, "%llu\n"); + +static int ipu_isys_init_debugfs(struct ipu_isys *isys) +{ + struct dentry *file; + struct dentry *dir; + + dir = debugfs_create_dir("isys", isys->adev->isp->ipu_dir); + if (IS_ERR(dir)) + return -ENOMEM; + + file = debugfs_create_file("icache_prefetch", 0600, + dir, isys, &isys_icache_prefetch_fops); + if (IS_ERR(file)) + goto err; + + isys->debugfsdir = dir; + + + return 0; +err: + debugfs_remove_recursive(dir); + return -ENOMEM; +} + +static int alloc_fw_msg_buffers(struct ipu_isys *isys, int amount) +{ + dma_addr_t dma_addr; + struct isys_fw_msgs *addr; + unsigned int i; + unsigned long flags; + + for (i = 0; i < amount; i++) { + addr = dma_alloc_attrs(&isys->adev->dev, + sizeof(struct isys_fw_msgs), + &dma_addr, GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + if (!addr) + break; + addr->dma_addr = dma_addr; + + spin_lock_irqsave(&isys->listlock, flags); + list_add(&addr->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); + } + if (i == amount) + return 0; + spin_lock_irqsave(&isys->listlock, flags); + while (!list_empty(&isys->framebuflist)) { + addr = list_first_entry(&isys->framebuflist, + struct isys_fw_msgs, head); + list_del(&addr->head); + spin_unlock_irqrestore(&isys->listlock, flags); + dma_free_attrs(&isys->adev->dev, + sizeof(struct isys_fw_msgs), + addr, addr->dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + spin_lock_irqsave(&isys->listlock, flags); + } + spin_unlock_irqrestore(&isys->listlock, flags); + return -ENOMEM; +} + +struct isys_fw_msgs *ipu_get_fw_msg_buf(struct ipu_isys_pipeline *ip) +{ + struct ipu_isys_video *pipe_av = + container_of(ip, struct ipu_isys_video, ip); + struct ipu_isys *isys; + struct isys_fw_msgs *msg; + unsigned long flags; + + isys = pipe_av->isys; + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); + dev_dbg(&isys->adev->dev, "Frame list empty - Allocate more"); + + alloc_fw_msg_buffers(isys, 5); + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + dev_err(&isys->adev->dev, "Frame list empty"); + spin_unlock_irqrestore(&isys->listlock, flags); + return NULL; + } + } + msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); + list_move(&msg->head, &isys->framebuflist_fw); + spin_unlock_irqrestore(&isys->listlock, flags); + memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); + + return msg; +} + +void ipu_cleanup_fw_msg_bufs(struct ipu_isys *isys) +{ + struct isys_fw_msgs *fwmsg, *fwmsg0; + unsigned long flags; + + spin_lock_irqsave(&isys->listlock, flags); + list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) + list_move(&fwmsg->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); +} + +void ipu_put_fw_mgs_buffer(struct ipu_isys *isys, u64 data) +{ + struct isys_fw_msgs *msg; + u64 *ptr = (u64 *)(unsigned long)data; + + if (!ptr) + return; + + spin_lock(&isys->listlock); + msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); + list_move(&msg->head, &isys->framebuflist); + spin_unlock(&isys->listlock); +} +EXPORT_SYMBOL_GPL(ipu_put_fw_mgs_buffer); + +static int isys_probe(struct ipu_bus_device *adev) +{ + struct ipu_mmu *mmu = dev_get_drvdata(adev->iommu); + struct ipu_isys *isys; + struct ipu_device *isp = adev->isp; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + const u32 trace_size = IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE; + dma_addr_t *trace_dma_addr; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif +#endif + const struct firmware *uninitialized_var(fw); + int rval = 0; + + trace_printk("B|%d|TMWK\n", current->pid); + + /* Has the domain been attached? */ + if (!mmu || !isp->pkg_dir_dma_addr) { + trace_printk("E|TMWK\n"); + return -EPROBE_DEFER; + } + + isys = devm_kzalloc(&adev->dev, sizeof(*isys), GFP_KERNEL); + if (!isys) + return -ENOMEM; + + /* By default, short packet is captured from T-Unit. */ +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + isys->short_packet_source = IPU_ISYS_SHORT_PACKET_FROM_TUNIT; + trace_dma_addr = &isys->short_packet_trace_buffer_dma_addr; + mutex_init(&isys->short_packet_tracing_mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + isys->short_packet_trace_buffer = + dma_alloc_attrs(&adev->dev, trace_size, trace_dma_addr, + GFP_KERNEL, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; + isys->short_packet_trace_buffer = + dma_alloc_attrs(&adev->dev, trace_size, trace_dma_addr, + GFP_KERNEL, attrs); +#endif + if (!isys->short_packet_trace_buffer) + return -ENOMEM; +#else + isys->short_packet_source = IPU_ISYS_SHORT_PACKET_FROM_RECEIVER; +#endif + isys->adev = adev; + isys->pdata = adev->pdata; + + INIT_LIST_HEAD(&isys->requests); + + spin_lock_init(&isys->lock); + spin_lock_init(&isys->power_lock); + isys->power = 0; + + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); + mutex_init(&isys->lib_mutex); + + spin_lock_init(&isys->listlock); + INIT_LIST_HEAD(&isys->framebuflist); + INIT_LIST_HEAD(&isys->framebuflist_fw); + + dev_info(&adev->dev, "isys probe %p %p\n", adev, &adev->dev); + ipu_bus_set_drvdata(adev, isys); + + isys->line_align = IPU_ISYS_2600_MEM_LINE_ALIGN; +#ifdef CONFIG_VIDEO_INTEL_IPU4 + isys->icache_prefetch = is_ipu_hw_bxtp_e0(isp); +#else + isys->icache_prefetch = 0; +#endif + +#ifndef CONFIG_PM + isys_setup_hw(isys); +#endif + + if (!isp->secure_mode) { + fw = isp->cpd_fw; + rval = ipu_buttress_map_fw_image(adev, fw, &isys->fw_sgt); + if (rval) + goto release_firmware; + + isys->pkg_dir = ipu_cpd_create_pkg_dir(adev, isp->cpd_fw->data, + sg_dma_address(isys-> + fw_sgt. + sgl), + &isys->pkg_dir_dma_addr, + &isys->pkg_dir_size); + if (!isys->pkg_dir) { + rval = -ENOMEM; + goto remove_shared_buffer; + } + } + + /* Debug fs failure is not fatal. */ + ipu_isys_init_debugfs(isys); + + ipu_trace_init(adev->isp, isys->pdata->base, &adev->dev, + isys_trace_blocks); + + pm_qos_add_request(&isys->pm_qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + alloc_fw_msg_buffers(isys, 20); + + pm_runtime_allow(&adev->dev); + pm_runtime_enable(&adev->dev); + + rval = isys_register_devices(isys); + if (rval) + goto out_remove_pkg_dir_shared_buffer; + + trace_printk("E|TMWK\n"); + return 0; + +out_remove_pkg_dir_shared_buffer: + if (!isp->secure_mode) + ipu_cpd_free_pkg_dir(adev, isys->pkg_dir, + isys->pkg_dir_dma_addr, + isys->pkg_dir_size); +remove_shared_buffer: + if (!isp->secure_mode) + ipu_buttress_unmap_fw_image(adev, &isys->fw_sgt); +release_firmware: + if (!isp->secure_mode) + release_firmware(isys->fw); + ipu_trace_uninit(&adev->dev); + + trace_printk("E|TMWK\n"); + + mutex_destroy(&isys->mutex); + mutex_destroy(&isys->stream_mutex); + + if (isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_TUNIT) { + mutex_destroy(&isys->short_packet_tracing_mutex); +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + dma_free_attrs(&adev->dev, trace_size, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &attrs); +#else + attrs); +#endif +#endif + } + + return rval; +} + +struct fwmsg { + int type; + char *msg; + bool valid_ts; +}; + +static const struct fwmsg fw_msg[] = { + {IPU_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + "STREAM_START_AND_CAPTURE_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + "STREAM_START_AND_CAPTURE_DONE", 1}, + {IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1}, + {IPU_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1}, + {IPU_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1}, + {IPU_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1}, + {-1, "UNKNOWN MESSAGE", 0}, +}; + +static int resp_type_to_index(int type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fw_msg); i++) + if (fw_msg[i].type == type) + return i; + + return i - 1; +} + +int isys_isr_one(struct ipu_bus_device *adev) +{ + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + struct ipu_fw_isys_resp_info_abi resp_data; + struct ipu_fw_isys_resp_info_abi *resp; + struct ipu_isys_pipeline *pipe; + u64 ts; + unsigned int i; + + if (!isys->fwcom) + return 0; + + resp = ipu_fw_isys_get_resp(isys->fwcom, IPU_BASE_MSG_RECV_QUEUES, + &resp_data); + if (!resp) + return 1; + + ts = (u64) resp->timestamp[1] << 32 | resp->timestamp[0]; + + if (resp->error_info.error == IPU_FW_ISYS_ERROR_STREAM_IN_SUSPENSION) + /* Suspension is kind of special case: not enough buffers */ + dev_dbg(&adev->dev, + "hostlib: error resp %02d %s, stream %u, error SUSPENSION, details %d, timestamp 0x%16.16llx, pin %d\n", + resp->type, + fw_msg[resp_type_to_index(resp->type)].msg, + resp->stream_handle, + resp->error_info.error_details, + fw_msg[resp_type_to_index(resp->type)].valid_ts ? + ts : 0, resp->pin_id); + else if (resp->error_info.error) + dev_dbg(&adev->dev, + "hostlib: error resp %02d %s, stream %u, error %d, details %d, timestamp 0x%16.16llx, pin %d\n", + resp->type, + fw_msg[resp_type_to_index(resp->type)].msg, + resp->stream_handle, + resp->error_info.error, resp->error_info.error_details, + fw_msg[resp_type_to_index(resp->type)].valid_ts ? + ts : 0, resp->pin_id); + else + dev_dbg(&adev->dev, + "hostlib: resp %02d %s, stream %u, timestamp 0x%16.16llx, pin %d\n", + resp->type, + fw_msg[resp_type_to_index(resp->type)].msg, + resp->stream_handle, + fw_msg[resp_type_to_index(resp->type)].valid_ts ? + ts : 0, resp->pin_id); + + if (resp->stream_handle >= IPU_ISYS_MAX_STREAMS) { + dev_err(&adev->dev, "bad stream handle %u\n", + resp->stream_handle); + goto leave; + } + + pipe = isys->pipes[resp->stream_handle]; + if (!pipe) { + dev_err(&adev->dev, "no pipeline for stream %u\n", + resp->stream_handle); + goto leave; + } + pipe->error = resp->error_info.error; + + switch (resp->type) { + case IPU_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + ipu_put_fw_mgs_buffer(ipu_bus_get_drvdata(adev), resp->buf_id); + complete(&pipe->stream_open_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: + complete(&pipe->stream_close_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_START_ACK: + complete(&pipe->stream_start_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + ipu_put_fw_mgs_buffer(ipu_bus_get_drvdata(adev), resp->buf_id); + complete(&pipe->stream_start_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK: + complete(&pipe->stream_stop_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: + complete(&pipe->stream_stop_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_PIN_DATA_READY: + if (resp->pin_id < IPU_ISYS_OUTPUT_PINS && + pipe->output_pins[resp->pin_id].pin_ready) + pipe->output_pins[resp->pin_id].pin_ready(pipe, resp); + else + dev_err(&adev->dev, + "%d:No data pin ready handler for pin id %d\n", + resp->stream_handle, resp->pin_id); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + ipu_put_fw_mgs_buffer(ipu_bus_get_drvdata(adev), resp->buf_id); + complete(&pipe->capture_ack_completion); + break; + case IPU_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IPU_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: + if (pipe->interlaced) { + struct ipu_isys_buffer *ib, *ib_safe; + struct list_head list; + unsigned long flags; + + if (pipe->isys->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_TUNIT) + pipe->cur_field = + ipu_isys_csi2_get_current_field(pipe, + resp-> + timestamp); + /* + * Move the pending buffers to a local temp list. + * Then we do not need to handle the lock during + * the loop. + */ + spin_lock_irqsave(&pipe->short_packet_queue_lock, + flags); + list_cut_position(&list, + &pipe->pending_interlaced_bufs, + pipe->pending_interlaced_bufs.prev); + spin_unlock_irqrestore(&pipe->short_packet_queue_lock, + flags); + + list_for_each_entry_safe(ib, ib_safe, &list, head) { + struct vb2_buffer *vb; + + vb = ipu_isys_buffer_to_vb2_buffer(ib); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.field = pipe->cur_field; +#else + to_vb2_v4l2_buffer(vb)->field = pipe->cur_field; +#endif + list_del(&ib->head); + + ipu_isys_queue_buf_done(ib); + } + } + for (i = 0; i < IPU_NUM_CAPTURE_DONE; i++) + if (pipe->capture_done[i]) + pipe->capture_done[i] (pipe, resp); + + break; + case IPU_FW_ISYS_RESP_TYPE_FRAME_SOF: + pipe->seq[pipe->seq_index].sequence = + atomic_read(&pipe->sequence) - 1; + pipe->seq[pipe->seq_index].timestamp = ts; + dev_dbg(&adev->dev, + "sof: handle %d: (index %u), timestamp 0x%16.16llx\n", + resp->stream_handle, + pipe->seq[pipe->seq_index].sequence, ts); + pipe->seq_index = (pipe->seq_index + 1) + % IPU_ISYS_MAX_PARALLEL_SOF; + break; + case IPU_FW_ISYS_RESP_TYPE_FRAME_EOF: + + + dev_dbg(&adev->dev, + "eof: handle %d: (index %u), timestamp 0x%16.16llx\n", + resp->stream_handle, + pipe->seq[pipe->seq_index].sequence, ts); + break; + case IPU_FW_ISYS_RESP_TYPE_STATS_DATA_READY: + break; + default: + dev_err(&adev->dev, "%d:unknown response type %u\n", + resp->stream_handle, resp->type); + break; + } + +leave: + ipu_fw_isys_put_resp(isys->fwcom, IPU_BASE_MSG_RECV_QUEUES); + return 0; +} + +static void isys_isr_poll(struct ipu_bus_device *adev) +{ + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + + if (!isys->fwcom) { + dev_dbg(&isys->adev->dev, + "got interrupt but device not configured yet\n"); + return; + } + + mutex_lock(&isys->mutex); + isys_isr(adev); + mutex_unlock(&isys->mutex); +} + +int ipu_isys_isr_run(void *ptr) +{ + struct ipu_isys *isys = ptr; + + while (!kthread_should_stop()) { + usleep_range(500, 1000); + if (isys->stream_opened) + isys_isr_poll(isys->adev); + } + + return 0; +} + +static struct ipu_bus_driver isys_driver = { + .probe = isys_probe, + .remove = isys_remove, + .isr = isys_isr, + .wanted = IPU_ISYS_NAME, + .drv = { + .name = IPU_ISYS_NAME, + .owner = THIS_MODULE, + .pm = ISYS_PM_OPS, + }, +}; + +module_ipu_bus_driver(isys_driver); + +static const struct pci_device_id ipu_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU_PCI_ID)}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ipu_pci_tbl); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_AUTHOR("Samu Onkalo "); +MODULE_AUTHOR("Jouni Högander "); +MODULE_AUTHOR("Jouni Ukkonen "); +MODULE_AUTHOR("Jianxu Zheng "); +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Renwei Wu "); +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Yunliang Ding "); +MODULE_AUTHOR("Zaikuo Wang "); +MODULE_AUTHOR("Leifu Zhao "); +MODULE_AUTHOR("Xia Wu "); +MODULE_AUTHOR("Kun Jiang "); +MODULE_AUTHOR("Yu Xia "); +MODULE_AUTHOR("Jerry Hu "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu input system driver"); diff --git a/drivers/media/pci/intel/ipu-isys.h b/drivers/media/pci/intel/ipu-isys.h new file mode 100644 index 000000000000..30d3b9b53a2e --- /dev/null +++ b/drivers/media/pci/intel/ipu-isys.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_H +#define IPU_ISYS_H + +#include +#include + +#include +#include + +#include + +#include "ipu.h" +#include "ipu-isys-media.h" +#include "ipu-isys-csi2.h" +#include "ipu-isys-csi2-be.h" +#include "ipu-isys-tpg.h" +#include "ipu-isys-video.h" +#include "ipu-pdata.h" +#include "ipu-fw-isys.h" +#include "ipu-platform-isys.h" + +#define IPU_ISYS_2600_MEM_LINE_ALIGN 64 + +/* for TPG */ +#define IPU_ISYS_FREQ 533000000UL + +/* + * Current message queue configuration. These must be big enough + * so that they never gets full. Queues are located in system memory + */ +#define IPU_ISYS_SIZE_RECV_QUEUE 40 +#define IPU_ISYS_SIZE_SEND_QUEUE 40 +#define IPU_ISYS_SIZE_PROXY_RECV_QUEUE 5 +#define IPU_ISYS_SIZE_PROXY_SEND_QUEUE 5 +#define IPU_ISYS_NUM_RECV_QUEUE 1 + +/* + * Device close takes some time from last ack message to actual stopping + * of the SP processor. As long as the SP processor runs we can't proceed with + * clean up of resources. + */ +#define IPU_ISYS_OPEN_TIMEOUT_US 1000 +#define IPU_ISYS_OPEN_RETRY 1000 +#define IPU_ISYS_TURNOFF_DELAY_US 1000 +#define IPU_ISYS_TURNOFF_TIMEOUT 1000 +#define IPU_LIB_CALL_TIMEOUT_JIFFIES \ + msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) + +#define IPU_ISYS_CSI2_LONG_PACKET_HEADER_SIZE 32 +#define IPU_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE 32 + +#define IPU_ISYS_MIN_WIDTH 1U +#define IPU_ISYS_MIN_HEIGHT 1U +#define IPU_ISYS_MAX_WIDTH 16384U +#define IPU_ISYS_MAX_HEIGHT 16384U + +struct task_struct; + +/* + * struct ipu_isys + * + * @media_dev: Media device + * @v4l2_dev: V4L2 device + * @adev: ISYS bus device + * @power: Is ISYS powered on or not? + * @isr_bits: Which bits does the ISR handle? + * @power_lock: Serialise access to power (power state in general) + * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers + * @lock: serialise access to pipes + * @pipes: pipelines per stream ID + * @fwcom: fw communication layer private pointer + * or optional external library private pointer + * @line_align: line alignment in memory + * @reset_needed: Isys requires d0i0->i3 transition + * @video_opened: total number of opened file handles on video nodes + * @mutex: serialise access isys video open/release related operations + * @stream_mutex: serialise stream start and stop, queueing requests + * @lib_mutex: optional external library mutex + * @pdata: platform data pointer + * @csi2: CSI-2 receivers + * @tpg: test pattern generators + * @csi2_be: CSI-2 back-ends + * @isa: Input system accelerator + * @fw: ISYS firmware binary (unsecure firmware) + * @fw_sgt: fw scatterlist + * @pkg_dir: host pointer to pkg_dir + * @pkg_dir_dma_addr: I/O virtual address for pkg_dir + * @pkg_dir_size: size of pkg_dir in bytes + * @short_packet_source: select short packet capture mode + */ +struct ipu_isys { + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct ipu_bus_device *adev; + + int power; + spinlock_t power_lock; /* Serialise access to power */ + u32 isr_csi2_bits; + u32 csi2_rx_ctrl_cached; + spinlock_t lock; /* Serialise access to pipes */ + struct ipu_isys_pipeline *pipes[IPU_ISYS_MAX_STREAMS]; + void *fwcom; + unsigned int line_align; + bool reset_needed; + bool icache_prefetch; + bool csi2_cse_ipc_not_supported; + unsigned int video_opened; + unsigned int stream_opened; + struct dentry *debugfsdir; + struct mutex mutex; /* Serialise isys video open/release related */ + struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ + struct mutex lib_mutex; /* Serialise optional external library mutex */ + + struct ipu_isys_pdata *pdata; + + struct ipu_isys_csi2 *csi2; + struct ipu_isys_tpg *tpg; + struct ipu_isys_isa isa; + struct ipu_isys_csi2_be csi2_be; + struct ipu_isys_csi2_be_soc csi2_be_soc; + + const struct firmware *fw; + struct sg_table fw_sgt; + + u64 *pkg_dir; + dma_addr_t pkg_dir_dma_addr; + unsigned int pkg_dir_size; + + struct list_head requests; + struct pm_qos_request pm_qos; + unsigned int short_packet_source; + struct ipu_isys_csi2_monitor_message *short_packet_trace_buffer; + dma_addr_t short_packet_trace_buffer_dma_addr; + unsigned int short_packet_tracing_count; + struct mutex short_packet_tracing_mutex; /* For tracing count */ + u64 tsc_timer_base; + u64 tunit_timer_base; + spinlock_t listlock; /* Protect framebuflist */ + struct list_head framebuflist; + struct list_head framebuflist_fw; +}; + +struct isys_fw_msgs { + union { + u64 dummy; + struct ipu_fw_isys_frame_buff_set_abi frame; + struct ipu_fw_isys_stream_cfg_data_abi stream; + } fw_msg; + struct list_head head; + dma_addr_t dma_addr; +}; + +#define to_frame_msg_buf(a) (&(a)->fw_msg.frame) +#define to_stream_cfg_msg_buf(a) (&(a)->fw_msg.stream) +#define to_dma_addr(a) ((a)->dma_addr) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +int ipu_pipeline_pm_use(struct media_entity *entity, int use); +#endif +struct isys_fw_msgs *ipu_get_fw_msg_buf(struct ipu_isys_pipeline *ip); +void ipu_put_fw_mgs_buffer(struct ipu_isys *isys, u64 data); +void ipu_cleanup_fw_msg_bufs(struct ipu_isys *isys); + +extern const struct v4l2_ioctl_ops ipu_isys_ioctl_ops; + +void isys_setup_hw(struct ipu_isys *isys); +int isys_isr_one(struct ipu_bus_device *adev); +int ipu_isys_isr_run(void *ptr); +irqreturn_t isys_isr(struct ipu_bus_device *adev); + +#endif /* IPU_ISYS_H */ diff --git a/drivers/media/pci/intel/ipu-mmu.c b/drivers/media/pci/intel/ipu-mmu.c new file mode 100644 index 000000000000..7718732fe5e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu-mmu.c @@ -0,0 +1,876 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include + +#include +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-platform.h" +#include "ipu-bus.h" +#include "ipu-dma.h" +#include "ipu-mmu.h" +#include "ipu-platform-regs.h" + +#define ISP_PAGE_SHIFT 12 +#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) +#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1)) + +#define ISP_L1PT_SHIFT 22 +#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) + +#define ISP_L2PT_SHIFT 12 +#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) + +#define ISP_L1PT_PTES 1024 +#define ISP_L2PT_PTES 1024 + +#define ISP_PADDR_SHIFT 12 + +#define REG_TLB_INVALIDATE 0x0000 + +#define MMU0_TLB_INVALIDATE 1 + +#define MMU1_TLB_INVALIDATE 0xffff + +#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ +#define REG_INFO 0x0008 + +/* The range of stream ID i in L1 cache is from 0 to 15 */ +#define MMUV2_REG_L1_STREAMID(i) (0x0c + ((i) * 4)) + +/* The range of stream ID i in L2 cache is from 0 to 15 */ +#define MMUV2_REG_L2_STREAMID(i) (0x4c + ((i) * 4)) + +/* ZLW Enable for each stream in L1 MMU AT where i : 0..15 */ +#define MMUV2_AT_REG_L1_ZLW_EN_SID(i) (0x100 + ((i) * 0x20)) + +/* ZLW 1D mode Enable for each stream in L1 MMU AT where i : 0..15 */ +#define MMUV2_AT_REG_L1_ZLW_1DMODE_SID(i) (0x100 + ((i) * 0x20) + 0x0004) + +/* Set ZLW insertion N pages ahead per stream 1D where i : 0..15 */ +#define MMUV2_AT_REG_L1_ZLW_INS_N_AHEAD_SID(i) (0x100 + ((i) * 0x20) + 0x0008) + +/* ZLW 2D mode Enable for each stream in L1 MMU AT where i : 0..15 */ +#define MMUV2_AT_REG_L1_ZLW_2DMODE_SID(i) (0x100 + ((i) * 0x20) + 0x0010) + +/* ZLW Insertion for each stream in L1 MMU AT where i : 0..15 */ +#define MMUV2_AT_REG_L1_ZLW_INSERTION(i) (0x100 + ((i) * 0x20) + 0x000c) + +#define MMUV2_AT_REG_L1_FW_ZLW_FIFO (0x100 + \ + (IPU_MMU_MAX_TLB_L1_STREAMS * 0x20) + 0x003c) + +/* FW ZLW has prioty - needed for ZLW invalidations */ +#define MMUV2_AT_REG_L1_FW_ZLW_PRIO (0x100 + \ + (IPU_MMU_MAX_TLB_L1_STREAMS * 0x20)) + +#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) +#define TBL_VIRT_ADDR(a) phys_to_virt(TBL_PHYS_ADDR(a)) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#define to_ipu_mmu_domain(dom) ((dom)->priv) +#else +#define to_ipu_mmu_domain(dom) \ + container_of(dom, struct ipu_mmu_domain, domain) +#endif + +static void zlw_invalidate(struct ipu_mmu *mmu, struct ipu_mmu_hw *mmu_hw) +{ + unsigned int retry = 0; + unsigned int i, j; + int ret; + + for (i = 0; i < mmu_hw->nr_l1streams; i++) { + /* We need to invalidate only the zlw enabled stream IDs */ + if (mmu_hw->l1_zlw_en[i]) { + /* + * Maximum 16 blocks per L1 stream + * Write trash buffer iova offset to the FW_ZLW + * register. This will trigger pre-fetching of next 16 + * pages from the page table. So we need to increment + * iova address by 16 * 4K to trigger the next 16 pages. + * Once this loop is completed, the L1 cache will be + * filled with trash buffer translation. + * + * TODO: Instead of maximum 16 blocks, use the allocated + * block size + */ + for (j = 0; j < mmu_hw->l1_block_sz[i]; j++) + writel(mmu->iova_addr_trash + + j * MMUV2_TRASH_L1_BLOCK_OFFSET, + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_INSERTION(i)); + + /* + * Now we need to fill the L2 cache entry. L2 cache + * entries will be automatically updated, based on the + * L1 entry. The above loop for L1 will update only one + * of the two entries in L2 as the L1 is under 4MB + * range. To force the other entry in L2 to update, we + * just need to trigger another pre-fetch which is + * outside the above 4MB range. + */ + writel(mmu->iova_addr_trash + + MMUV2_TRASH_L2_BLOCK_OFFSET, + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_INSERTION(0)); + } + } + + /* + * Wait until AT is ready. FIFO read should return 2 when AT is ready. + * Retry value of 1000 is just by guess work to avoid the forever loop. + */ + do { + if (retry > 1000) { + dev_err(mmu->dev, "zlw invalidation failed\n"); + return; + } + ret = readl(mmu_hw->base + MMUV2_AT_REG_L1_FW_ZLW_FIFO); + retry++; + } while (ret != 2); +} + +static void tlb_invalidate(struct ipu_mmu *mmu) +{ + unsigned int i; + unsigned long flags; + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { + spin_unlock_irqrestore(&mmu->ready_lock, flags); + return; + } + + for (i = 0; i < mmu->nr_mmus; i++) { + u32 inv; + + /* + * To avoid the HW bug induced dead lock in some of the IPU4 + * MMUs on successive invalidate calls, we need to first do a + * read to the page table base before writing the invalidate + * register. MMUs which need to implement this WA, will have + * the insert_read_before_invalidate flasg set as true. + * Disregard the return value of the read. + */ + if (mmu->mmu_hw[i].insert_read_before_invalidate) + readl(mmu->mmu_hw[i].base + REG_L1_PHYS); + + /* Normal invalidate or zlw invalidate */ + if (mmu->mmu_hw[i].zlw_invalidate) { + /* trash buffer must be mapped by now, just in case! */ + WARN_ON(!mmu->iova_addr_trash); + + zlw_invalidate(mmu, &mmu->mmu_hw[i]); + } else { + if (mmu->mmu_hw[i].nr_l1streams == 32) + inv = 0xffffffff; + else if (mmu->mmu_hw[i].nr_l1streams == 0) + inv = MMU0_TLB_INVALIDATE; + else + inv = MMU1_TLB_INVALIDATE; + writel(inv, mmu->mmu_hw[i].base + + REG_TLB_INVALIDATE); + } + } + spin_unlock_irqrestore(&mmu->ready_lock, flags); +} + +#ifdef DEBUG +static void page_table_dump(struct ipu_mmu_domain *adom) +{ + u32 l1_idx; + + pr_debug("begin IOMMU page table dump\n"); + + for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { + u32 l2_idx; + u32 iova = (phys_addr_t) l1_idx << ISP_L1PT_SHIFT; + + if (adom->pgtbl[l1_idx] == adom->dummy_l2_tbl) + continue; + pr_debug("l1 entry %u; iovas 0x%8.8x--0x%8.8x, at %p\n", + l1_idx, iova, iova + ISP_PAGE_SIZE, + (void *)TBL_PHYS_ADDR(adom->pgtbl[l1_idx])); + + for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) { + u32 *l2_pt = TBL_VIRT_ADDR(adom->pgtbl[l1_idx]); + u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT); + + if (l2_pt[l2_idx] == adom->dummy_page) + continue; + + pr_debug("\tl2 entry %u; iova 0x%8.8x, phys %p\n", + l2_idx, iova2, + (void *)TBL_PHYS_ADDR(l2_pt[l2_idx])); + } + } + + pr_debug("end IOMMU page table dump\n"); +} +#endif /* DEBUG */ + +static u32 *alloc_page_table(struct ipu_mmu_domain *adom, bool l1) +{ + u32 *pt = (u32 *) __get_free_page(GFP_KERNEL | GFP_DMA32); + int i; + + if (!pt) + return NULL; + + pr_debug("__get_free_page() == %p\n", pt); + + for (i = 0; i < ISP_L1PT_PTES; i++) + pt[i] = l1 ? adom->dummy_l2_tbl : adom->dummy_page; + + return pt; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +static int ipu_mmu_domain_init(struct iommu_domain *domain) +{ + struct ipu_mmu_domain *adom; + void *ptr; + + adom = kzalloc(sizeof(*adom), GFP_KERNEL); + if (!adom) + return -ENOMEM; + + domain->priv = adom; + adom->domain = domain; + + ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); + if (!ptr) + goto err; + + adom->dummy_page = virt_to_phys(ptr) >> ISP_PAGE_SHIFT; + + ptr = alloc_page_table(adom, false); + if (!ptr) + goto err; + + adom->dummy_l2_tbl = virt_to_phys(ptr) >> ISP_PAGE_SHIFT; + + /* + * We always map the L1 page table (a single page as well as + * the L2 page tables). + */ + adom->pgtbl = alloc_page_table(adom, true); + if (!adom->pgtbl) + goto err; + + spin_lock_init(&adom->lock); + + pr_debug("domain initialised\n"); + pr_debug("ops %p\n", domain->ops); + + return 0; + +err: + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_page)); + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_l2_tbl)); + kfree(adom); + + return -ENOMEM; +} +#else +static struct iommu_domain *ipu_mmu_domain_alloc(unsigned int type) +{ + struct ipu_mmu_domain *adom; + void *ptr; + + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + + adom = kzalloc(sizeof(*adom), GFP_KERNEL); + if (!adom) + return NULL; + + adom->domain.geometry.aperture_start = 0; + adom->domain.geometry.aperture_end = DMA_BIT_MASK(IPU_MMU_ADDRESS_BITS); + adom->domain.geometry.force_aperture = true; + + ptr = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); + if (!ptr) + goto err_mem; + + adom->dummy_page = virt_to_phys(ptr) >> ISP_PAGE_SHIFT; + + ptr = alloc_page_table(adom, false); + if (!ptr) + goto err; + + adom->dummy_l2_tbl = virt_to_phys(ptr) >> ISP_PAGE_SHIFT; + + /* + * We always map the L1 page table (a single page as well as + * the L2 page tables). + */ + adom->pgtbl = alloc_page_table(adom, true); + if (!adom->pgtbl) + goto err; + + spin_lock_init(&adom->lock); + + pr_debug("domain initialised\n"); + pr_debug("ops %p\n", adom->domain.ops); + + return &adom->domain; + +err: + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_page)); + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_l2_tbl)); +err_mem: + kfree(adom); + + return NULL; +} +#endif + +static void ipu_mmu_domain_destroy(struct iommu_domain *domain) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + struct iova *iova; + u32 l1_idx; + + if (adom->iova_addr_trash) { + iova = find_iova(&adom->dmap->iovad, adom->iova_addr_trash >> + PAGE_SHIFT); + /* unmap and free the corresponding trash buffer iova */ + iommu_unmap(domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + __free_iova(&adom->dmap->iovad, iova); + + /* + * Set iova_addr_trash in mmu to 0, so that on next HW init + * this will be mapped again. + */ + adom->iova_addr_trash = 0; + } + + for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) + if (adom->pgtbl[l1_idx] != adom->dummy_l2_tbl) + free_page((unsigned long) + TBL_VIRT_ADDR(adom->pgtbl[l1_idx])); + + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_page)); + free_page((unsigned long)TBL_VIRT_ADDR(adom->dummy_l2_tbl)); + free_page((unsigned long)adom->pgtbl); + kfree(adom); +} + +static int ipu_mmu_attach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + + spin_lock(&adom->lock); + + adom->users++; + + dev_dbg(dev, "domain attached\n"); + + spin_unlock(&adom->lock); + + return 0; +} + +static void ipu_mmu_detach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + + spin_lock(&adom->lock); + + adom->users--; + dev_dbg(dev, "domain detached\n"); + + spin_unlock(&adom->lock); +} + +static int l2_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + u32 l1_idx = iova >> ISP_L1PT_SHIFT; + u32 l1_entry = adom->pgtbl[l1_idx]; + u32 *l2_pt; + u32 iova_start = iova; + unsigned int l2_idx; + unsigned long flags; + + pr_debug("mapping l2 page table for l1 index %u (iova %8.8x)\n", + l1_idx, (u32) iova); + + if (l1_entry == adom->dummy_l2_tbl) { + u32 *l2_virt = alloc_page_table(adom, false); + + if (!l2_virt) + return -ENOMEM; + + l1_entry = virt_to_phys(l2_virt) >> ISP_PADDR_SHIFT; + pr_debug("allocated page for l1_idx %u\n", l1_idx); + + spin_lock_irqsave(&adom->lock, flags); + if (adom->pgtbl[l1_idx] == adom->dummy_l2_tbl) { + adom->pgtbl[l1_idx] = l1_entry; +#ifdef CONFIG_X86 + clflush_cache_range(&adom->pgtbl[l1_idx], + sizeof(adom->pgtbl[l1_idx])); +#endif /* CONFIG_X86 */ + } else { + spin_unlock_irqrestore(&adom->lock, flags); + free_page((unsigned long)TBL_VIRT_ADDR(l1_entry)); + spin_lock_irqsave(&adom->lock, flags); + } + } else { + spin_lock_irqsave(&adom->lock, flags); + } + + l2_pt = TBL_VIRT_ADDR(adom->pgtbl[l1_idx]); + + pr_debug("l2_pt at %p\n", l2_pt); + + paddr = ALIGN(paddr, ISP_PAGE_SIZE); + + l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; + + pr_debug("l2_idx %u, phys 0x%8.8x\n", l2_idx, l2_pt[l2_idx]); + if (l2_pt[l2_idx] != adom->dummy_page) { + spin_unlock_irqrestore(&adom->lock, flags); + return -EBUSY; + } + + l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; + + spin_unlock_irqrestore(&adom->lock, flags); + +#ifdef CONFIG_X86 + clflush_cache_range(&l2_pt[l2_idx], sizeof(l2_pt[l2_idx])); +#endif /* CONFIG_X86 */ + + pr_debug("l2 index %u mapped as 0x%8.8x\n", l2_idx, l2_pt[l2_idx]); + + return 0; +} + +static int ipu_mmu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + u32 iova_start = round_down(iova, ISP_PAGE_SIZE); + u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); + + pr_debug + ("mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n", + iova_start, iova_end, size, paddr); + + return l2_map(domain, iova_start, paddr, size); +} + +static size_t l2_unmap(struct iommu_domain *domain, unsigned long iova, + phys_addr_t dummy, size_t size) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + u32 l1_idx = iova >> ISP_L1PT_SHIFT; + u32 *l2_pt = TBL_VIRT_ADDR(adom->pgtbl[l1_idx]); + u32 iova_start = iova; + unsigned int l2_idx; + size_t unmapped = 0; + + pr_debug("unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n", + l1_idx, iova); + + if (adom->pgtbl[l1_idx] == adom->dummy_l2_tbl) + return -EINVAL; + + pr_debug("l2_pt at %p\n", l2_pt); + + for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; + (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT) + < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) { + unsigned long flags; + + pr_debug("l2 index %u unmapped, was 0x%10.10llx\n", + l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx])); + spin_lock_irqsave(&adom->lock, flags); + l2_pt[l2_idx] = adom->dummy_page; + spin_unlock_irqrestore(&adom->lock, flags); +#ifdef CONFIG_X86 + clflush_cache_range(&l2_pt[l2_idx], sizeof(l2_pt[l2_idx])); +#endif /* CONFIG_X86 */ + unmapped++; + } + + return unmapped << ISP_PAGE_SHIFT; +} + +static size_t ipu_mmu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + return l2_unmap(domain, iova, 0, size); +} + +static phys_addr_t ipu_mmu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) +{ + struct ipu_mmu_domain *adom = to_ipu_mmu_domain(domain); + u32 *l2_pt = TBL_VIRT_ADDR(adom->pgtbl[iova >> ISP_L1PT_SHIFT]); + + return (phys_addr_t) l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT] + << ISP_PAGE_SHIFT; +} + +static int allocate_trash_buffer(struct ipu_bus_device *adev) +{ + struct ipu_mmu *mmu = ipu_bus_get_drvdata(adev); + unsigned int n_pages = PAGE_ALIGN(IPU_MMUV2_TRASH_RANGE) >> PAGE_SHIFT; + struct iova *iova; + u32 iova_addr; + unsigned int i; + int ret; + + /* Allocate 8MB in iova range */ + iova = alloc_iova(&mmu->dmap->iovad, n_pages, + dma_get_mask(mmu->dev) >> PAGE_SHIFT, 0); + if (!iova) { + dev_err(&adev->dev, "cannot allocate iova range for trash\n"); + return -ENOMEM; + } + + /* + * Map the 8MB iova address range to the same physical trash page + * mmu->trash_page which is already reserved at the probe + */ + iova_addr = iova->pfn_lo; + for (i = 0; i < n_pages; i++) { + ret = iommu_map(mmu->dmap->domain, iova_addr << PAGE_SHIFT, + page_to_phys(mmu->trash_page), PAGE_SIZE, 0); + if (ret) { + dev_err(&adev->dev, + "mapping trash buffer range failed\n"); + goto out_unmap; + } + + iova_addr++; + } + + /* save the address for the ZLW invalidation */ + mmu->iova_addr_trash = iova->pfn_lo << PAGE_SHIFT; + dev_info(&adev->dev, "iova trash buffer for MMUID: %d is %u\n", + mmu->mmid, (unsigned int)mmu->iova_addr_trash); + return 0; + +out_unmap: + iommu_unmap(mmu->dmap->domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + __free_iova(&mmu->dmap->iovad, iova); + return ret; +} + +static int ipu_mmu_hw_init(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_mmu *mmu = ipu_bus_get_drvdata(adev); + struct ipu_mmu_domain *adom; + unsigned int i; + unsigned long flags; + + dev_dbg(dev, "mmu hw init\n"); + /* + * FIXME: following fix for null pointer check is not a complete one. + * if mmu is not powered cycled before being used, the page table + * address will still not be set into HW. + */ + if (!mmu->dmap) { + dev_warn(dev, "mmu is not ready yet. skipping.\n"); + return 0; + } + adom = to_ipu_mmu_domain(mmu->dmap->domain); + + adom->dmap = mmu->dmap; + + /* Initialise the each MMU HW block */ + for (i = 0; i < mmu->nr_mmus; i++) { + struct ipu_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + bool zlw_invalidate = false; +#endif + unsigned int j; + u16 block_addr; + + /* Write page table address per MMU */ + writel((phys_addr_t) virt_to_phys(adom->pgtbl) + >> ISP_PADDR_SHIFT, + mmu->mmu_hw[i].base + REG_L1_PHYS); + + /* Set info bits per MMU */ + writel(mmu->mmu_hw[i].info_bits, + mmu->mmu_hw[i].base + REG_INFO); + + /* Configure MMU TLB stream configuration for L1 */ + for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams; + block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) { + if (block_addr > IPU_MAX_LI_BLOCK_ADDR) { + dev_err(dev, "invalid L1 configuration\n"); + return -EINVAL; + } + + /* Write block start address for each streams */ + writel(block_addr, mmu_hw->base + + mmu_hw->l1_stream_id_reg_offset + 4 * j); + +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + /* Enable ZLW for streams based on the init table */ + writel(mmu->mmu_hw[i].l1_zlw_en[j], + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_EN_SID(j)); + + /* To track if zlw is enabled in any streams */ + zlw_invalidate |= mmu->mmu_hw[i].l1_zlw_en[j]; + + /* Enable ZLW 1D mode for streams from the init table */ + writel(mmu->mmu_hw[i].l1_zlw_1d_mode[j], + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_1DMODE_SID(j)); + + /* Set when the ZLW insertion will happen */ + writel(mmu->mmu_hw[i].l1_ins_zlw_ahead_pages[j], + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_INS_N_AHEAD_SID(j)); + + /* Set if ZLW 2D mode active for each streams */ + writel(mmu->mmu_hw[i].l1_zlw_2d_mode[j], + mmu_hw->base + + MMUV2_AT_REG_L1_ZLW_2DMODE_SID(j)); +#endif + } + +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) + /* + * If ZLW invalidate is enabled even for one stream in a MMU1, + * we need to set the FW ZLW operations have higher priority + * on that MMU1 + */ + if (zlw_invalidate) + writel(1, mmu_hw->base + + MMUV2_AT_REG_L1_FW_ZLW_PRIO); +#endif + /* Configure MMU TLB stream configuration for L2 */ + for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams; + block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) { + if (block_addr > IPU_MAX_L2_BLOCK_ADDR) { + dev_err(dev, "invalid L2 configuration\n"); + return -EINVAL; + } + + writel(block_addr, mmu_hw->base + + mmu_hw->l2_stream_id_reg_offset + 4 * j); + } + } + + /* Allocate trash buffer, if not allocated. Only once per MMU */ + if (!mmu->iova_addr_trash) { + int ret; + + ret = allocate_trash_buffer(adev); + if (ret) { + dev_err(dev, "trash buffer allocation failed\n"); + return ret; + } + + /* + * Update the domain pointer to trash buffer to release it on + * domain destroy + */ + adom->iova_addr_trash = mmu->iova_addr_trash; + } + + spin_lock_irqsave(&mmu->ready_lock, flags); + mmu->ready = true; + spin_unlock_irqrestore(&mmu->ready_lock, flags); + + return 0; +} + +static void set_mapping(struct ipu_mmu *mmu, struct ipu_dma_mapping *dmap) +{ + mmu->dmap = dmap; + + if (!dmap) + return; + + pm_runtime_get_sync(mmu->dev); + ipu_mmu_hw_init(mmu->dev); + pm_runtime_put(mmu->dev); +} + +static int ipu_mmu_add_device(struct device *dev) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_dma_mapping *dmap; + int rval; + + if (!aiommu || !dev->iommu_group) + return 0; + + dmap = iommu_group_get_iommudata(dev->iommu_group); + if (!dmap) + return 0; + + pr_debug("attach dev %s\n", dev_name(dev)); + + rval = iommu_attach_device(dmap->domain, dev); + if (rval) + return rval; + + kref_get(&dmap->ref); + + return 0; +} + +static struct iommu_ops ipu_iommu_ops = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + .domain_init = ipu_mmu_domain_init, + .domain_destroy = ipu_mmu_domain_destroy, +#else + .domain_alloc = ipu_mmu_domain_alloc, + .domain_free = ipu_mmu_domain_destroy, +#endif + .attach_dev = ipu_mmu_attach_dev, + .detach_dev = ipu_mmu_detach_dev, + .map = ipu_mmu_map, + .unmap = ipu_mmu_unmap, + .iova_to_phys = ipu_mmu_iova_to_phys, + .add_device = ipu_mmu_add_device, + .pgsize_bitmap = SZ_4K, +}; + +static int ipu_mmu_probe(struct ipu_bus_device *adev) +{ + struct ipu_mmu_pdata *pdata; + struct ipu_mmu *mmu; + int rval; + + mmu = devm_kzalloc(&adev->dev, sizeof(*mmu), GFP_KERNEL); + if (!mmu) + return -ENOMEM; + + dev_dbg(&adev->dev, "mmu probe %p %p\n", adev, &adev->dev); + ipu_bus_set_drvdata(adev, mmu); + + rval = ipu_bus_set_iommu(&ipu_iommu_ops); + if (rval) + return rval; + + pdata = adev->pdata; + + mmu->mmid = pdata->mmid; + + mmu->mmu_hw = pdata->mmu_hw; + mmu->nr_mmus = pdata->nr_mmus; + mmu->tlb_invalidate = tlb_invalidate; + mmu->set_mapping = set_mapping; + mmu->dev = &adev->dev; + mmu->ready = false; + spin_lock_init(&mmu->ready_lock); + + /* + * Allocate 1 page of physical memory for the trash buffer + * + * TODO! Could be further optimized by allocating only one page per ipu + * instance instead of per mmu + */ + mmu->trash_page = alloc_page(GFP_KERNEL); + if (!mmu->trash_page) { + dev_err(&adev->dev, "insufficient memory for trash buffer\n"); + return -ENOMEM; + } + dev_info(&adev->dev, "MMU: %d, allocated page for trash: 0x%p\n", + mmu->mmid, mmu->trash_page); + + pm_runtime_allow(&adev->dev); + pm_runtime_enable(&adev->dev); + + /* + * FIXME: We can't unload this --- bus_set_iommu() will + * register a notifier which must stay until the devices are + * gone. + */ + __module_get(THIS_MODULE); + + return 0; +} + +/* + * Leave iommu ops as they were --- this means we must be called as + * the very last. + */ +static void ipu_mmu_remove(struct ipu_bus_device *adev) +{ + struct ipu_mmu *mmu = ipu_bus_get_drvdata(adev); + + __free_page(mmu->trash_page); + dev_dbg(&adev->dev, "removed\n"); +} + +static irqreturn_t ipu_mmu_isr(struct ipu_bus_device *adev) +{ + dev_info(&adev->dev, "Yeah!\n"); + return IRQ_NONE; +} + +#ifdef CONFIG_PM +static int ipu_mmu_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_mmu *mmu = ipu_bus_get_drvdata(adev); + unsigned long flags; + + spin_lock_irqsave(&mmu->ready_lock, flags); + mmu->ready = false; + spin_unlock_irqrestore(&mmu->ready_lock, flags); + + return 0; +} + +static const struct dev_pm_ops ipu_mmu_pm_ops = { + .resume = ipu_mmu_hw_init, + .suspend = ipu_mmu_suspend, + .runtime_resume = ipu_mmu_hw_init, + .runtime_suspend = ipu_mmu_suspend, +}; + +#define IPU_MMU_PM_OPS (&ipu_mmu_pm_ops) + +#else /* !CONFIG_PM */ + +#define IPU_MMU_PM_OPS NULL + +#endif /* !CONFIG_PM */ + +static struct ipu_bus_driver ipu_mmu_driver = { + .probe = ipu_mmu_probe, + .remove = ipu_mmu_remove, + .isr = ipu_mmu_isr, + .wanted = IPU_MMU_NAME, + .drv = { + .name = IPU_MMU_NAME, + .owner = THIS_MODULE, + .pm = IPU_MMU_PM_OPS, + }, +}; +module_ipu_bus_driver(ipu_mmu_driver); + +static const struct pci_device_id ipu_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU_PCI_ID)}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ipu_pci_tbl); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_AUTHOR("Samu Onkalo "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu mmu driver"); diff --git a/drivers/media/pci/intel/ipu-mmu.h b/drivers/media/pci/intel/ipu-mmu.h new file mode 100644 index 000000000000..0e8863a2f024 --- /dev/null +++ b/drivers/media/pci/intel/ipu-mmu.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_MMU_H +#define IPU_MMU_H + +#include + +#include "ipu.h" +#include "ipu-pdata.h" + +#define ISYS_MMID 1 +#define PSYS_MMID 0 + +/* + * @pgtbl: virtual address of the l1 page table (one page) + */ +struct ipu_mmu_domain { + u32 __iomem *pgtbl; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct iommu_domain *domain; +#else + struct iommu_domain domain; +#endif + spinlock_t lock; /* Serialize access to users */ + unsigned int users; + struct ipu_dma_mapping *dmap; + u32 dummy_l2_tbl; + u32 dummy_page; + + /* Reference to the trash address to unmap on domain destroy */ + dma_addr_t iova_addr_trash; +}; + +/* + * @pgtbl: physical address of the l1 page table + */ +struct ipu_mmu { + struct list_head node; + unsigned int users; + + struct ipu_mmu_hw *mmu_hw; + unsigned int nr_mmus; + int mmid; + + phys_addr_t pgtbl; + struct device *dev; + + struct ipu_dma_mapping *dmap; + + struct page *trash_page; + dma_addr_t iova_addr_trash; + + bool ready; + spinlock_t ready_lock; /* Serialize access to bool ready */ + + void (*tlb_invalidate)(struct ipu_mmu *mmu); + void (*set_mapping)(struct ipu_mmu *mmu, + struct ipu_dma_mapping *dmap); +}; + +#endif diff --git a/drivers/media/pci/intel/ipu-pdata.h b/drivers/media/pci/intel/ipu-pdata.h new file mode 100644 index 000000000000..66f111266f05 --- /dev/null +++ b/drivers/media/pci/intel/ipu-pdata.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_PDATA_H +#define IPU_PDATA_H + +#define IPU_MMU_NAME IPU_NAME "-mmu" +#define IPU_ISYS_CSI2_NAME IPU_NAME "-csi2" +#define IPU_ISYS_NAME IPU_NAME "-isys" +#define IPU_PSYS_NAME IPU_NAME "-psys" +#define IPU_BUTTRESS_NAME IPU_NAME "-buttress" + +#define IPU_MMU_MAX_DEVICES 4 +#define IPU_MMU_ADDRESS_BITS 32 +/* The firmware is accessible within the first 2 GiB only in non-secure mode. */ +#define IPU_MMU_ADDRESS_BITS_NON_SECURE 31 + +#if defined(CONFIG_VIDEO_INTEL_IPU4) || defined(CONFIG_VIDEO_INTEL_IPU4P) +#define IPU_MMU_MAX_TLB_L1_STREAMS 16 +#define IPU_MMU_MAX_TLB_L2_STREAMS 16 +#define IPU_MAX_LI_BLOCK_ADDR 64 +#define IPU_MAX_L2_BLOCK_ADDR 32 +#else +#define IPU_MMU_MAX_TLB_L1_STREAMS 32 +#define IPU_MMU_MAX_TLB_L2_STREAMS 32 +#define IPU_MAX_LI_BLOCK_ADDR 128 +#define IPU_MAX_L2_BLOCK_ADDR 64 +#endif + +#define IPU_ISYS_MAX_CSI2_LEGACY_PORTS 4 +#define IPU_ISYS_MAX_CSI2_COMBO_PORTS 2 + +#define IPU_MAX_FRAME_COUNTER 0xff + +/* + * To maximize the IOSF utlization, IPU need to send requests in bursts. + * At the DMA interface with the buttress, there are CDC FIFOs with burst + * collection capability. CDC FIFO burst collectors have a configurable + * threshold and is configured based on the outcome of performance measurements. + * + * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 + * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 + * + * Threshold values are pre-defined and are arrived at after performance + * evaluations on a type of IPU4 + */ +#define IPU_MAX_VC_IOSF_PORTS 4 + +/* + * IPU must configure correct arbitration mechanism related to the IOSF VC + * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on + * stall and 1 means stall until the request is completed. + */ +#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 +#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 + +/* Currently chosen arbitration mechanism for VC0 */ +#define IPU_BTRS_ARB_STALL_MODE_VC0 \ + IPU_BTRS_ARB_MODE_TYPE_REARB + +/* Currently chosen arbitration mechanism for VC1 */ +#define IPU_BTRS_ARB_STALL_MODE_VC1 \ + IPU_BTRS_ARB_MODE_TYPE_REARB + +struct ipu_isys_subdev_pdata; + +/* + * MMU Invalidation HW bug workaround by ZLW mechanism + * + * IPU4 MMUV2 has a bug in the invalidation mechanism which might result in + * wrong translation or replication of the translation. This will cause data + * corruption. So we cannot directly use the MMU V2 invalidation registers + * to invalidate the MMU. Instead, whenever an invalidate is called, we need to + * clear the TLB by evicting all the valid translations by filling it with trash + * buffer (which is guaranteed not to be used by any other processes). ZLW is + * used to fill the L1 and L2 caches with the trash buffer translations. ZLW + * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in + * advance to the L1 and L2 caches without triggering any memory operations. + * + * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream + * One L1 block has 16 entries, hence points to 16 * 4K pages + * L2 -> 16 streams and 32 blocks. 2 blocks per streams + * One L2 block maps to 1024 L1 entries, hence points to 4MB address range + * 2 blocks per L2 stream means, 1 stream points to 8MB range + * + * As we need to clear the caches and 8MB being the biggest cache size, we need + * to have trash buffer which points to 8MB address range. As these trash + * buffers are not used for any memory transactions, we need only the least + * amount of physical memory. So we reserve 8MB IOVA address range but only + * one page is reserved from physical memory. Each of this 8MB IOVA address + * range is then mapped to the same physical memory page. + */ +/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ +#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) +/* Max L2 blocks per stream */ +#define IPU_MMUV2_MAX_L2_BLOCKS 2 +/* Max L1 blocks per stream */ +#define IPU_MMUV2_MAX_L1_BLOCKS 16 +#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ + IPU_MMUV2_MAX_L2_BLOCKS) +/* Entries per L1 block */ +#define MMUV2_ENTRIES_PER_L1_BLOCK 16 +#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * \ + PAGE_SIZE) +#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE + +/* + * In some of the IPU4 MMUs, there is provision to configure L1 and L2 page + * table caches. Both these L1 and L2 caches are divided into multiple sections + * called streams. There is maximum 16 streams for both caches. Each of these + * sections are subdivided into multiple blocks. When nr_l1streams = 0 and + * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support + * L1/L2 page table caches. + * + * L1 stream per block sizes are configurable and varies per usecase. + * L2 has constant block sizes - 2 blocks per stream. + * + * MMU1 support pre-fetching of the pages to have less cache lookup misses. To + * enable the pre-fetching, MMU1 AT (Address Translator) device registers + * need to be configured. + * + * There are four types of memory accesses which requires ZLW configuration. + * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU. + * + * 1. Sequential Access or 1D mode + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 1 + * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where + * N is pre-defined and hardcoded in the platform data + * Set ZLW_2D -> 0 + * + * 2. ZLW 2D mode + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 1, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 1 + * + * 3. ZLW Enable (no 1D or 2D mode) + * Set ZLW_EN -> 1 + * set ZLW_PAGE_CROSS_1D -> 0, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 0 + * + * 4. ZLW disable + * Set ZLW_EN -> 0 + * set ZLW_PAGE_CROSS_1D -> 0, + * Set ZLW_N -> 0 + * Set ZLW_2D -> 0 + * + * To configure the ZLW for the above memory access, four registers are + * available. Hence to track these four settings, we have the following entries + * in the struct ipu_mmu_hw. Each of these entries are per stream and + * available only for the L1 streams. + * + * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN) + * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary + * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted + * Insert ZLW request N pages ahead address. + * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D) + * + * + * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined + * as per the usecase specific calculations. Any change to this pre-defined + * table has to happen in sync with IPU4 FW. + */ +struct ipu_mmu_hw { + union { + unsigned long offset; + void __iomem *base; + }; + unsigned int info_bits; + u8 nr_l1streams; + /* + * L1 has variable blocks per stream - total of 64 blocks and maximum of + * 16 blocks per stream. Configurable by using the block start address + * per stream. Block start address is calculated from the block size + */ + u8 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; + /* Is ZLW is enabled in each stream */ + bool l1_zlw_en[IPU_MMU_MAX_TLB_L1_STREAMS]; + bool l1_zlw_1d_mode[IPU_MMU_MAX_TLB_L1_STREAMS]; + u8 l1_ins_zlw_ahead_pages[IPU_MMU_MAX_TLB_L1_STREAMS]; + bool l1_zlw_2d_mode[IPU_MMU_MAX_TLB_L1_STREAMS]; + + u32 l1_stream_id_reg_offset; + u32 l2_stream_id_reg_offset; + + u8 nr_l2streams; + /* + * L2 has fixed 2 blocks per stream. Block address is calculated + * from the block size + */ + u8 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; + /* flag to track if WA is needed for successive invalidate HW bug */ + bool insert_read_before_invalidate; + /* flag to track if zlw based mmu invalidation is needed */ + bool zlw_invalidate; +}; + +struct ipu_mmu_pdata { + unsigned int nr_mmus; + struct ipu_mmu_hw mmu_hw[IPU_MMU_MAX_DEVICES]; + int mmid; +}; + +struct ipu_isys_csi2_pdata { + void __iomem *base; +}; + +#define IPU_EV_AUTO 0xff + +struct ipu_combo_receiver_params { + u8 crc_val; + u8 drc_val; + u8 drc_val_combined; + u8 ctle_val; +}; + +struct ipu_receiver_electrical_params { + u64 min_freq; + u64 max_freq; + unsigned short device; /* PCI DEVICE ID */ + u8 revision; /* PCI REVISION */ + /* base settings at first receiver power on */ + u8 rcomp_val_combo; + u8 rcomp_val_legacy; + + /* Combo per receiver settings */ + struct ipu_combo_receiver_params ports[2]; +}; + +struct ipu_isys_internal_csi2_pdata { + unsigned int nports; + unsigned int *offsets; + struct ipu_receiver_electrical_params *evparams; + u32 evsetmask0; + u32 evsetmask1; + unsigned char *evlanecombine; +}; + +struct ipu_isys_internal_tpg_pdata { + unsigned int ntpgs; + unsigned int *offsets; + unsigned int *sels; +}; + +/* + * One place to handle all the IPU HW variations + */ +struct ipu_hw_variants { + unsigned long offset; + unsigned int nr_mmus; + struct ipu_mmu_hw mmu_hw[IPU_MMU_MAX_DEVICES]; + u8 cdc_fifos; + u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; + u32 dmem_offset; + u32 spc_offset; /* SPC offset from psys base */ +}; + +struct ipu_isys_internal_pdata { + struct ipu_isys_internal_csi2_pdata csi2; + struct ipu_isys_internal_tpg_pdata tpg; + struct ipu_hw_variants hw_variant; + u32 num_parallel_streams; + u32 isys_dma_overshoot; +}; + +struct ipu_isys_pdata { + void __iomem *base; + const struct ipu_isys_internal_pdata *ipdata; + struct ipu_isys_subdev_pdata *spdata; +}; + +struct ipu_psys_internal_pdata { + struct ipu_hw_variants hw_variant; +}; + +struct ipu_psys_pdata { + void __iomem *base; + const struct ipu_psys_internal_pdata *ipdata; +}; + +#endif diff --git a/drivers/media/pci/intel/ipu-psys-compat32.c b/drivers/media/pci/intel/ipu-psys-compat32.c new file mode 100644 index 000000000000..0b9bb3ec28ac --- /dev/null +++ b/drivers/media/pci/intel/ipu-psys-compat32.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include + +#include + +#include "ipu-psys.h" + +static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret = -ENOTTY; + + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, arg); + + return ret; +} + +struct ipu_psys_buffer32 { + u64 len; + union { + int fd; + compat_uptr_t userptr; + u64 reserved; + } base; + u32 data_offset; + u32 bytes_used; + u32 flags; + u32 reserved[2]; +} __packed; + +struct ipu_psys_command32 { + u64 issue_id; + u64 user_token; + u32 priority; + compat_uptr_t pg_manifest; + compat_uptr_t buffers; + int pg; + u32 pg_manifest_size; + u32 bufcount; + u32 min_psys_freq; + u32 frame_counter; + u32 reserved[2]; +} __packed; + +struct ipu_psys_manifest32 { + u32 index; + u32 size; + compat_uptr_t manifest; + u32 reserved[5]; +} __packed; + +static int +get_ipu_psys_command32(struct ipu_psys_command *kp, + struct ipu_psys_command32 __user *up) +{ + compat_uptr_t pgm, bufs; + + if (!access_ok(VERIFY_READ, up, + sizeof(struct ipu_psys_command32)) || + get_user(kp->issue_id, &up->issue_id) || + get_user(kp->user_token, &up->user_token) || + get_user(kp->priority, &up->priority) || + get_user(pgm, &up->pg_manifest) || + get_user(bufs, &up->buffers) || + get_user(kp->pg, &up->pg) || + get_user(kp->pg_manifest_size, &up->pg_manifest_size) || + get_user(kp->bufcount, &up->bufcount) || + get_user(kp->min_psys_freq, &up->min_psys_freq) + || get_user(kp->frame_counter, &up->frame_counter) + ) + return -EFAULT; + + kp->pg_manifest = compat_ptr(pgm); + kp->buffers = compat_ptr(bufs); + + return 0; +} + +static int +get_ipu_psys_buffer32(struct ipu_psys_buffer *kp, + struct ipu_psys_buffer32 __user *up) +{ + compat_uptr_t ptr; + + if (!access_ok(VERIFY_READ, up, + sizeof(struct ipu_psys_buffer32)) || + get_user(kp->len, &up->len) || + get_user(ptr, &up->base.userptr) || + get_user(kp->data_offset, &up->data_offset) || + get_user(kp->bytes_used, &up->bytes_used) || + get_user(kp->flags, &up->flags)) + return -EFAULT; + + kp->base.userptr = compat_ptr(ptr); + + return 0; +} + +static int +put_ipu_psys_buffer32(struct ipu_psys_buffer *kp, + struct ipu_psys_buffer32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, + sizeof(struct ipu_psys_buffer32)) || + put_user(kp->len, &up->len) || + put_user(kp->base.fd, &up->base.fd) || + put_user(kp->data_offset, &up->data_offset) || + put_user(kp->bytes_used, &up->bytes_used) || + put_user(kp->flags, &up->flags)) + return -EFAULT; + + return 0; +} + +static int +get_ipu_psys_manifest32(struct ipu_psys_manifest *kp, + struct ipu_psys_manifest32 __user *up) +{ + compat_uptr_t ptr; + + if (!access_ok(VERIFY_READ, up, + sizeof(struct ipu_psys_manifest32)) || + get_user(kp->index, &up->index) || + get_user(kp->size, &up->size) || get_user(ptr, &up->manifest)) + return -EFAULT; + + kp->manifest = compat_ptr(ptr); + + return 0; +} + +static int +put_ipu_psys_manifest32(struct ipu_psys_manifest *kp, + struct ipu_psys_manifest32 __user *up) +{ + compat_uptr_t ptr = (u32)((unsigned long)kp->manifest); + + if (!access_ok(VERIFY_WRITE, up, + sizeof(struct ipu_psys_manifest32)) || + put_user(kp->index, &up->index) || + put_user(kp->size, &up->size) || put_user(ptr, &up->manifest)) + return -EFAULT; + + return 0; +} + +#define IPU_IOC_GETBUF32 _IOWR('A', 4, struct ipu_psys_buffer32) +#define IPU_IOC_PUTBUF32 _IOWR('A', 5, struct ipu_psys_buffer32) +#define IPU_IOC_QCMD32 _IOWR('A', 6, struct ipu_psys_command32) +#define IPU_IOC_CMD_CANCEL32 _IOWR('A', 8, struct ipu_psys_command32) +#define IPU_IOC_GET_MANIFEST32 _IOWR('A', 9, struct ipu_psys_manifest32) + +long ipu_psys_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg) +{ + union { + struct ipu_psys_buffer buf; + struct ipu_psys_command cmd; + struct ipu_psys_event ev; + struct ipu_psys_manifest m; + } karg; + int compatible_arg = 1; + int err = 0; + void __user *up = compat_ptr(arg); + + switch (cmd) { + case IPU_IOC_GETBUF32: + cmd = IPU_IOC_GETBUF; + break; + case IPU_IOC_PUTBUF32: + cmd = IPU_IOC_PUTBUF; + break; + case IPU_IOC_QCMD32: + cmd = IPU_IOC_QCMD; + break; + case IPU_IOC_GET_MANIFEST32: + cmd = IPU_IOC_GET_MANIFEST; + break; + } + + switch (cmd) { + case IPU_IOC_GETBUF: + case IPU_IOC_PUTBUF: + err = get_ipu_psys_buffer32(&karg.buf, up); + compatible_arg = 0; + break; + case IPU_IOC_QCMD: + err = get_ipu_psys_command32(&karg.cmd, up); + compatible_arg = 0; + break; + case IPU_IOC_GET_MANIFEST: + err = get_ipu_psys_manifest32(&karg.m, up); + compatible_arg = 0; + break; + } + if (err) + return err; + + if (compatible_arg) { + err = native_ioctl(file, cmd, (unsigned long)up); + } else { + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + err = native_ioctl(file, cmd, (unsigned long)&karg); + set_fs(old_fs); + } + + if (err) + return err; + + switch (cmd) { + case IPU_IOC_GETBUF: + err = put_ipu_psys_buffer32(&karg.buf, up); + break; + case IPU_IOC_GET_MANIFEST: + err = put_ipu_psys_manifest32(&karg.m, up); + break; + } + return err; +} +EXPORT_SYMBOL_GPL(ipu_psys_compat_ioctl32); diff --git a/drivers/media/pci/intel/ipu-psys.c b/drivers/media/pci/intel/ipu-psys.c new file mode 100644 index 000000000000..c5ae209d9df5 --- /dev/null +++ b/drivers/media/pci/intel/ipu-psys.c @@ -0,0 +1,1649 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#include +#else +#include +#endif +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) +#include +#else +#include +#endif + +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-platform.h" +#include "ipu-buttress.h" +#include "ipu-cpd.h" +#include "ipu-fw-psys.h" +#include "ipu-psys.h" +#include "ipu-platform-psys.h" +#include "ipu-platform-regs.h" +#include "ipu-fw-isys.h" +#include "ipu-fw-com.h" + +static bool async_fw_init; +module_param(async_fw_init, bool, 0664); +MODULE_PARM_DESC(async_fw_init, "Enable asynchronous firmware initialization"); + +#define IPU_PSYS_NUM_DEVICES 4 +#define IPU_PSYS_AUTOSUSPEND_DELAY 2000 + +#ifdef CONFIG_PM +static int psys_runtime_pm_resume(struct device *dev); +static int psys_runtime_pm_suspend(struct device *dev); +#else +#define pm_runtime_dont_use_autosuspend(d) +#define pm_runtime_use_autosuspend(d) +#define pm_runtime_set_autosuspend_delay(d, f) 0 +#define pm_runtime_get_sync(d) 0 +#define pm_runtime_put(d) 0 +#define pm_runtime_put_sync(d) 0 +#define pm_runtime_put_noidle(d) 0 +#define pm_runtime_put_autosuspend(d) 0 +#endif + +static dev_t ipu_psys_dev_t; +static DECLARE_BITMAP(ipu_psys_devices, IPU_PSYS_NUM_DEVICES); +static DEFINE_MUTEX(ipu_psys_mutex); + +static struct fw_init_task { + struct delayed_work work; + struct ipu_psys *psys; +} fw_init_task; + +static void ipu_psys_remove(struct ipu_bus_device *adev); + +static struct bus_type ipu_psys_bus = { + .name = IPU_PSYS_NAME, +}; + +static struct ipu_psys_capability caps = { + .version = 1, + .driver = "ipu-psys", +}; + +struct ipu_psys_pg *__get_pg_buf(struct ipu_psys *psys, size_t pg_size) +{ + struct ipu_psys_pg *kpg; + unsigned long flags; + + spin_lock_irqsave(&psys->pgs_lock, flags); + list_for_each_entry(kpg, &psys->pgs, list) { + if (!kpg->pg_size && kpg->size >= pg_size) { + kpg->pg_size = pg_size; + spin_unlock_irqrestore(&psys->pgs_lock, flags); + return kpg; + } + } + spin_unlock_irqrestore(&psys->pgs_lock, flags); + /* no big enough buffer available, allocate new one */ + kpg = kzalloc(sizeof(*kpg), GFP_KERNEL); + if (!kpg) + return NULL; + + kpg->pg = dma_alloc_attrs(&psys->adev->dev, pg_size, + &kpg->pg_dma_addr, GFP_KERNEL, + DMA_ATTR_NON_CONSISTENT); + if (!kpg->pg) { + kfree(kpg); + return NULL; + } + + kpg->pg_size = pg_size; + kpg->size = pg_size; + spin_lock_irqsave(&psys->pgs_lock, flags); + list_add(&kpg->list, &psys->pgs); + spin_unlock_irqrestore(&psys->pgs_lock, flags); + + return kpg; +} + +struct ipu_psys_kbuffer *ipu_psys_lookup_kbuffer(struct ipu_psys_fh *fh, int fd) +{ + struct ipu_psys_kbuffer *kbuffer; + + list_for_each_entry(kbuffer, &fh->bufmap, list) { + if (kbuffer->fd == fd) + return kbuffer; + } + + return NULL; +} + +struct ipu_psys_kbuffer * +ipu_psys_lookup_kbuffer_by_kaddr(struct ipu_psys_fh *fh, void *kaddr) +{ + struct ipu_psys_kbuffer *kbuffer; + + list_for_each_entry(kbuffer, &fh->bufmap, list) { + if (kbuffer->kaddr == kaddr) + return kbuffer; + } + + return NULL; +} + +static int ipu_psys_get_userpages(struct ipu_dma_buf_attach *attach) +{ + struct vm_area_struct *vma; + unsigned long start, end; + int npages, array_size; + struct page **pages; + struct sg_table *sgt; + int nr = 0; + int ret = -ENOMEM; + + start = (unsigned long)attach->userptr; + end = PAGE_ALIGN(start + attach->len); + npages = (end - (start & PAGE_MASK)) >> PAGE_SHIFT; + array_size = npages * sizeof(struct page *); + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, GFP_KERNEL); + else + pages = vzalloc(array_size); + if (!pages) + goto free_sgt; + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, start); + if (!vma) { + ret = -EFAULT; + goto error_up_read; + } + + if (vma->vm_end < start + attach->len) { + dev_err(attach->dev, + "vma at %lu is too small for %llu bytes\n", + start, attach->len); + ret = -EFAULT; + goto error_up_read; + } + + /* + * For buffers from Gralloc, VM_PFNMAP is expected, + * but VM_IO is set. Possibly bug in Gralloc. + */ + attach->vma_is_io = vma->vm_flags & (VM_IO | VM_PFNMAP); + + if (attach->vma_is_io) { + unsigned long io_start = start; + + for (nr = 0; nr < npages; nr++, io_start += PAGE_SIZE) { + unsigned long pfn; + + ret = follow_pfn(vma, io_start, &pfn); + if (ret) + goto error_up_read; + pages[nr] = pfn_to_page(pfn); + } + } else { + nr = get_user_pages( +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + current, current->mm, +#endif + start & PAGE_MASK, npages, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + 1, 0, +#else + FOLL_WRITE, +#endif + pages, NULL); + if (nr < npages) + goto error_up_read; + } + up_read(¤t->mm->mmap_sem); + + ret = sg_alloc_table_from_pages(sgt, pages, npages, + start & ~PAGE_MASK, attach->len, + GFP_KERNEL); + if (ret < 0) + goto error; + + attach->sgt = sgt; + attach->pages = pages; + attach->npages = npages; + + return 0; + +error_up_read: + up_read(¤t->mm->mmap_sem); +error: + if (!attach->vma_is_io) + while (nr > 0) + put_page(pages[--nr]); + + if (array_size <= PAGE_SIZE) + kfree(pages); + else + vfree(pages); +free_sgt: + kfree(sgt); + + dev_err(attach->dev, "failed to get userpages:%d\n", ret); + + return ret; +} + +static void ipu_psys_put_userpages(struct ipu_dma_buf_attach *attach) +{ + if (!attach || !attach->userptr || !attach->sgt) + return; + + if (!attach->vma_is_io) { + int i = attach->npages; + + while (--i >= 0) { + set_page_dirty_lock(attach->pages[i]); + put_page(attach->pages[i]); + } + } + + if (is_vmalloc_addr(attach->pages)) + vfree(attach->pages); + else + kfree(attach->pages); + + sg_free_table(attach->sgt); + kfree(attach->sgt); + attach->sgt = NULL; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +static int ipu_dma_buf_attach(struct dma_buf *dbuf, + struct dma_buf_attachment *attach) +#else +static int ipu_dma_buf_attach(struct dma_buf *dbuf, struct device *dev, + struct dma_buf_attachment *attach) +#endif +{ + struct ipu_psys_kbuffer *kbuf = dbuf->priv; + struct ipu_dma_buf_attach *ipu_attach; + + ipu_attach = kzalloc(sizeof(*ipu_attach), GFP_KERNEL); + if (!ipu_attach) + return -ENOMEM; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) + ipu_attach->dev = dev; +#endif + ipu_attach->len = kbuf->len; + ipu_attach->userptr = kbuf->userptr; + + attach->priv = ipu_attach; + return 0; +} + +static void ipu_dma_buf_detach(struct dma_buf *dbuf, + struct dma_buf_attachment *attach) +{ + struct ipu_dma_buf_attach *ipu_attach = attach->priv; + + kfree(ipu_attach); + attach->priv = NULL; +} + +static struct sg_table *ipu_dma_buf_map(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct ipu_dma_buf_attach *ipu_attach = attach->priv; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + DEFINE_DMA_ATTRS(attrs); +#else + unsigned long attrs; +#endif + int ret; + + ret = ipu_psys_get_userpages(ipu_attach); + if (ret) + return NULL; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + ret = dma_map_sg_attrs(attach->dev, ipu_attach->sgt->sgl, + ipu_attach->sgt->orig_nents, dir, &attrs); +#else + attrs = DMA_ATTR_SKIP_CPU_SYNC; + ret = dma_map_sg_attrs(attach->dev, ipu_attach->sgt->sgl, + ipu_attach->sgt->orig_nents, dir, attrs); +#endif + if (ret < ipu_attach->sgt->orig_nents) { + ipu_psys_put_userpages(ipu_attach); + dev_dbg(attach->dev, "buf map failed\n"); + + return ERR_PTR(-EIO); + } + + /* + * Initial cache flush to avoid writing dirty pages for buffers which + * are later marked as IPU_BUFFER_FLAG_NO_FLUSH. + */ + dma_sync_sg_for_device(attach->dev, ipu_attach->sgt->sgl, + ipu_attach->sgt->orig_nents, DMA_BIDIRECTIONAL); + + return ipu_attach->sgt; +} + +static void ipu_dma_buf_unmap(struct dma_buf_attachment *attach, + struct sg_table *sg, enum dma_data_direction dir) +{ + struct ipu_dma_buf_attach *ipu_attach = attach->priv; + + dma_unmap_sg(attach->dev, sg->sgl, sg->orig_nents, dir); + ipu_psys_put_userpages(ipu_attach); +} + +static int ipu_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) +{ + return -ENOTTY; +} + +static void *ipu_dma_buf_kmap(struct dma_buf *dbuf, unsigned long pgnum) +{ + return NULL; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) +static void *ipu_dma_buf_kmap_atomic(struct dma_buf *dbuf, unsigned long pgnum) +{ + return NULL; +} +#endif + +static void ipu_dma_buf_release(struct dma_buf *buf) +{ + struct ipu_psys_kbuffer *kbuf = buf->priv; + + if (!kbuf) + return; + + if (kbuf->db_attach) { + dev_dbg(kbuf->db_attach->dev, + "releasing buffer %d\n", kbuf->fd); + ipu_psys_put_userpages(kbuf->db_attach->priv); + } + kfree(kbuf); +} + +static int ipu_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + size_t start, size_t len, +#endif + enum dma_data_direction dir) +{ + return -ENOTTY; +} + +static void *ipu_dma_buf_vmap(struct dma_buf *dmabuf) +{ + struct dma_buf_attachment *attach; + struct ipu_dma_buf_attach *ipu_attach; + + if (list_empty(&dmabuf->attachments)) + return NULL; + + attach = list_last_entry(&dmabuf->attachments, + struct dma_buf_attachment, node); + ipu_attach = attach->priv; + + if (!ipu_attach || !ipu_attach->pages || !ipu_attach->npages) + return NULL; + + return vm_map_ram(ipu_attach->pages, + ipu_attach->npages, 0, PAGE_KERNEL); +} + +static void ipu_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) +{ + struct dma_buf_attachment *attach; + struct ipu_dma_buf_attach *ipu_attach; + + if (WARN_ON(list_empty(&dmabuf->attachments))) + return; + + attach = list_last_entry(&dmabuf->attachments, + struct dma_buf_attachment, node); + ipu_attach = attach->priv; + + if (WARN_ON(!ipu_attach || !ipu_attach->pages || !ipu_attach->npages)) + return; + + vm_unmap_ram(vaddr, ipu_attach->npages); +} + +static struct dma_buf_ops ipu_dma_buf_ops = { + .attach = ipu_dma_buf_attach, + .detach = ipu_dma_buf_detach, + .map_dma_buf = ipu_dma_buf_map, + .unmap_dma_buf = ipu_dma_buf_unmap, + .release = ipu_dma_buf_release, + .begin_cpu_access = ipu_dma_buf_begin_cpu_access, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + .kmap = ipu_dma_buf_kmap, + .kmap_atomic = ipu_dma_buf_kmap_atomic, +#else + .map = ipu_dma_buf_kmap, +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) + .map_atomic = ipu_dma_buf_kmap_atomic, +#endif + .mmap = ipu_dma_buf_mmap, + .vmap = ipu_dma_buf_vmap, + .vunmap = ipu_dma_buf_vunmap, +}; + +static int ipu_psys_open(struct inode *inode, struct file *file) +{ + struct ipu_psys *psys = inode_to_ipu_psys(inode); + struct ipu_device *isp = psys->adev->isp; + struct ipu_psys_fh *fh; + int rval; + + if (isp->flr_done) + return -EIO; + + rval = ipu_buttress_authenticate(isp); + if (rval) { + dev_err(&psys->adev->dev, "FW authentication failed\n"); + return rval; + } + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + fh->psys = psys; + file->private_data = fh; + + mutex_init(&fh->mutex); + INIT_LIST_HEAD(&fh->bufmap); + init_waitqueue_head(&fh->wait); + + rval = ipu_psys_fh_init(fh); + if (rval) + goto open_failed; + + mutex_lock(&psys->mutex); + list_add_tail(&fh->list, &psys->fhs); + mutex_unlock(&psys->mutex); + + return 0; + +open_failed: + mutex_destroy(&fh->mutex); + kfree(fh); + return rval; +} + +static int ipu_psys_release(struct inode *inode, struct file *file) +{ + struct ipu_psys *psys = inode_to_ipu_psys(inode); + struct ipu_psys_fh *fh = file->private_data; + struct ipu_psys_kbuffer *kbuf, *kbuf0; + + mutex_lock(&fh->mutex); + /* clean up buffers */ + if (!list_empty(&fh->bufmap)) { + list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { + list_del(&kbuf->list); + /* Unmap and release buffers */ + if (kbuf->dbuf && kbuf->db_attach) { + struct dma_buf *dbuf; + kbuf->valid = false; + dma_buf_vunmap(kbuf->dbuf, kbuf->kaddr); + dma_buf_unmap_attachment(kbuf->db_attach, + kbuf->sgt, + DMA_BIDIRECTIONAL); + dma_buf_detach(kbuf->dbuf, kbuf->db_attach); + dbuf = kbuf->dbuf; + kbuf->dbuf = NULL; + kbuf->db_attach = NULL; + dma_buf_put(dbuf); + } else { + if (kbuf->db_attach) + ipu_psys_put_userpages( + kbuf->db_attach->priv); + kfree(kbuf); + } + } + } + mutex_unlock(&fh->mutex); + + mutex_lock(&psys->mutex); + list_del(&fh->list); + + mutex_unlock(&psys->mutex); + + ipu_psys_fh_deinit(fh); + mutex_destroy(&fh->mutex); + kfree(fh); + + return 0; +} + +static int ipu_psys_getbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) +{ + struct ipu_psys_kbuffer *kbuf; + struct ipu_psys *psys = fh->psys; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); +#endif + struct dma_buf *dbuf; + int ret; + + if (!buf->base.userptr) { + dev_err(&psys->adev->dev, "Buffer allocation not supported\n"); + return -EINVAL; + } + + kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + kbuf->len = buf->len; + kbuf->userptr = buf->base.userptr; + kbuf->flags = buf->flags; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + exp_info.ops = &ipu_dma_buf_ops; + exp_info.size = kbuf->len; + exp_info.flags = O_RDWR; + exp_info.priv = kbuf; + + dbuf = dma_buf_export(&exp_info); +#else + dbuf = dma_buf_export(kbuf, &ipu_dma_buf_ops, kbuf->len, 0); +#endif + if (IS_ERR(dbuf)) { + kfree(kbuf); + return PTR_ERR(dbuf); + } + + ret = dma_buf_fd(dbuf, 0); + if (ret < 0) { + kfree(kbuf); + return ret; + } + + dev_dbg(&psys->adev->dev, "IOC_GETBUF: userptr %p", buf->base.userptr); + + kbuf->fd = ret; + buf->base.fd = ret; + kbuf->flags = buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; + kbuf->flags = buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; + + mutex_lock(&fh->mutex); + list_add_tail(&kbuf->list, &fh->bufmap); + mutex_unlock(&fh->mutex); + + dev_dbg(&psys->adev->dev, "to %d\n", buf->base.fd); + + return 0; +} + +static int ipu_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) +{ + return 0; +} + +static long ipu_psys_mapbuf(int fd, struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kbuffer *kbuf; + struct dma_buf *dbuf; + int ret; + + mutex_lock(&fh->mutex); + kbuf = ipu_psys_lookup_kbuffer(fh, fd); + + if (!kbuf) { + /* This fd isn't generated by ipu_psys_getbuf, it + * is a new fd. Create a new kbuf item for this fd, and + * add this kbuf to bufmap list. + */ + kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); + if (!kbuf) { + mutex_unlock(&fh->mutex); + return -ENOMEM; + } + + list_add_tail(&kbuf->list, &fh->bufmap); + } + + if (kbuf->sgt) { + dev_dbg(&psys->adev->dev, "has been mapped!\n"); + goto mapbuf_end; + } + + kbuf->dbuf = dma_buf_get(fd); + if (IS_ERR(kbuf->dbuf)) { + if (!kbuf->userptr) { + list_del(&kbuf->list); + kfree(kbuf); + } + mutex_unlock(&fh->mutex); + return -EINVAL; + } + + if (kbuf->len == 0) + kbuf->len = kbuf->dbuf->size; + + kbuf->fd = fd; + + kbuf->db_attach = dma_buf_attach(kbuf->dbuf, &psys->adev->dev); + if (IS_ERR(kbuf->db_attach)) { + ret = PTR_ERR(kbuf->db_attach); + goto error_put; + } + + kbuf->sgt = dma_buf_map_attachment(kbuf->db_attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(kbuf->sgt)) { + ret = -EINVAL; + kbuf->sgt = NULL; + dev_dbg(&psys->adev->dev, "map attachment failed\n"); + goto error_detach; + } + + kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); + + kbuf->kaddr = dma_buf_vmap(kbuf->dbuf); + if (!kbuf->kaddr) { + ret = -EINVAL; + goto error_unmap; + } + +mapbuf_end: + + kbuf->valid = true; + + mutex_unlock(&fh->mutex); + + dev_dbg(&psys->adev->dev, "IOC_MAPBUF: mapped fd %d\n", fd); + + return 0; + +error_unmap: + dma_buf_unmap_attachment(kbuf->db_attach, kbuf->sgt, DMA_BIDIRECTIONAL); +error_detach: + dma_buf_detach(kbuf->dbuf, kbuf->db_attach); + kbuf->db_attach = NULL; +error_put: + list_del(&kbuf->list); + dbuf = kbuf->dbuf; + + if (!kbuf->userptr) + kfree(kbuf); + + mutex_unlock(&fh->mutex); + dma_buf_put(dbuf); + + return ret; +} + +static long ipu_psys_unmapbuf(int fd, struct ipu_psys_fh *fh) +{ + struct ipu_psys_kbuffer *kbuf; + struct ipu_psys *psys = fh->psys; + struct dma_buf *dmabuf; + + mutex_lock(&fh->mutex); + kbuf = ipu_psys_lookup_kbuffer(fh, fd); + if (!kbuf) { + dev_dbg(&psys->adev->dev, "buffer %d not found\n", fd); + mutex_unlock(&fh->mutex); + return -EINVAL; + } + + /* From now on it is not safe to use this kbuffer */ + kbuf->valid = false; + + dma_buf_vunmap(kbuf->dbuf, kbuf->kaddr); + dma_buf_unmap_attachment(kbuf->db_attach, kbuf->sgt, DMA_BIDIRECTIONAL); + + dma_buf_detach(kbuf->dbuf, kbuf->db_attach); + + dmabuf = kbuf->dbuf; + + kbuf->db_attach = NULL; + kbuf->dbuf = NULL; + + list_del(&kbuf->list); + + if (!kbuf->userptr) + kfree(kbuf); + + mutex_unlock(&fh->mutex); + dma_buf_put(dmabuf); + + dev_dbg(&psys->adev->dev, "IOC_UNMAPBUF: fd %d\n", fd); + + return 0; +} + +static unsigned int ipu_psys_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct ipu_psys_fh *fh = file->private_data; + struct ipu_psys *psys = fh->psys; + unsigned int res = 0; + + dev_dbg(&psys->adev->dev, "ipu psys poll\n"); + + poll_wait(file, &fh->wait, wait); + + if (ipu_get_completed_kcmd(fh)) + res = POLLIN; + + dev_dbg(&psys->adev->dev, "ipu psys poll res %u\n", res); + + return res; +} + +static long ipu_get_manifest(struct ipu_psys_manifest *manifest, + struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_device *isp = psys->adev->isp; + struct ipu_cpd_client_pkg_hdr *client_pkg; + u32 entries; + void *host_fw_data; + dma_addr_t dma_fw_data; + u32 client_pkg_offset; + + host_fw_data = (void *)isp->cpd_fw->data; + dma_fw_data = sg_dma_address(psys->fw_sgt.sgl); + + entries = ipu_cpd_pkg_dir_get_num_entries(psys->pkg_dir); + if (!manifest || manifest->index > entries - 1) { + dev_err(&psys->adev->dev, "invalid argument\n"); + return -EINVAL; + } + + if (!ipu_cpd_pkg_dir_get_size(psys->pkg_dir, manifest->index) || + ipu_cpd_pkg_dir_get_type(psys->pkg_dir, manifest->index) < + IPU_CPD_PKG_DIR_CLIENT_PG_TYPE) { + dev_dbg(&psys->adev->dev, "invalid pkg dir entry\n"); + return -ENOENT; + } + + client_pkg_offset = ipu_cpd_pkg_dir_get_address(psys->pkg_dir, + manifest->index); + client_pkg_offset -= dma_fw_data; + + client_pkg = host_fw_data + client_pkg_offset; + manifest->size = client_pkg->pg_manifest_size; + + if (!manifest->manifest) + return 0; + + if (copy_to_user(manifest->manifest, + (uint8_t *) client_pkg + client_pkg->pg_manifest_offs, + manifest->size)) { + return -EFAULT; + } + + return 0; +} + +static long ipu_psys_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + union { + struct ipu_psys_buffer buf; + struct ipu_psys_command cmd; + struct ipu_psys_event ev; + struct ipu_psys_capability caps; + struct ipu_psys_manifest m; + } karg; + struct ipu_psys_fh *fh = file->private_data; + int err = 0; + void __user *up = (void __user *)arg; + bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF); + + if (copy) { + if (_IOC_SIZE(cmd) > sizeof(karg)) + return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + } + } + + switch (cmd) { + case IPU_IOC_MAPBUF: + err = ipu_psys_mapbuf(arg, fh); + break; + case IPU_IOC_UNMAPBUF: + err = ipu_psys_unmapbuf(arg, fh); + break; + case IPU_IOC_QUERYCAP: + karg.caps = caps; + break; + case IPU_IOC_GETBUF: + err = ipu_psys_getbuf(&karg.buf, fh); + break; + case IPU_IOC_PUTBUF: + err = ipu_psys_putbuf(&karg.buf, fh); + break; + case IPU_IOC_QCMD: + err = ipu_psys_kcmd_new(&karg.cmd, fh); + break; + case IPU_IOC_DQEVENT: + err = ipu_ioctl_dqevent(&karg.ev, fh, file->f_flags); + break; + case IPU_IOC_GET_MANIFEST: + err = ipu_get_manifest(&karg.m, fh); + break; + default: + err = -ENOTTY; + break; + } + + if (err) + return err; + + if (copy && _IOC_DIR(cmd) & _IOC_READ) + if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) + return -EFAULT; + + return 0; +} + +static const struct file_operations ipu_psys_fops = { + .open = ipu_psys_open, + .release = ipu_psys_release, + .unlocked_ioctl = ipu_psys_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ipu_psys_compat_ioctl32, +#endif + .poll = ipu_psys_poll, + .owner = THIS_MODULE, +}; + +static void ipu_psys_dev_release(struct device *dev) +{ +} + +#ifdef CONFIG_PM +static int psys_runtime_pm_resume(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + unsigned long flags; + int retval; + + if (!psys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + /* + * In runtime autosuspend mode, if the psys is in power on state, no + * need to resume again. + */ + spin_lock_irqsave(&psys->power_lock, flags); + if (psys->power) { + spin_unlock_irqrestore(&psys->power_lock, flags); + return 0; + } + spin_unlock_irqrestore(&psys->power_lock, flags); + + if (async_fw_init && !psys->fwcom) { + dev_err(dev, + "%s: asynchronous firmware init not finished, skipping\n", + __func__); + return 0; + } + + if (!ipu_buttress_auth_done(adev->isp)) { + dev_err(dev, "%s: not yet authenticated, skipping\n", __func__); + return 0; + } + + ipu_psys_setup_hw(psys); + + ipu_trace_restore(&psys->adev->dev); + + ipu_configure_spc(adev->isp, + &psys->pdata->ipdata->hw_variant, + IPU_CPD_PKG_DIR_PSYS_SERVER_IDX, + psys->pdata->base, psys->pkg_dir, + psys->pkg_dir_dma_addr); + + retval = ipu_fw_psys_open(psys); + if (retval) { + dev_err(&psys->adev->dev, "Failed to open abi.\n"); + return retval; + } + + spin_lock_irqsave(&psys->power_lock, flags); + psys->power = 1; + spin_unlock_irqrestore(&psys->power_lock, flags); + + return 0; +} + +static int psys_runtime_pm_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + unsigned long flags; + int rval; + + if (!psys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + + if (!psys->power) + return 0; + + spin_lock_irqsave(&psys->power_lock, flags); + psys->power = 0; + spin_unlock_irqrestore(&psys->power_lock, flags); + + /* + * We can trace failure but better to not return an error. + * At suspend we are progressing towards psys power gated state. + * Any hang / failure inside psys will be forgotten soon. + */ + rval = ipu_fw_psys_close(psys); + if (rval) + dev_err(dev, "Device close failure: %d\n", rval); + + return 0; +} + +static const struct dev_pm_ops psys_pm_ops = { + .runtime_suspend = psys_runtime_pm_suspend, + .runtime_resume = psys_runtime_pm_resume, +}; + +#define PSYS_PM_OPS (&psys_pm_ops) +#else +#define PSYS_PM_OPS NULL +#endif + +static int cpd_fw_reload(struct ipu_device *isp) +{ + struct ipu_psys *psys = ipu_bus_get_drvdata(isp->psys); + int rval; + + if (!isp->secure_mode) { + dev_warn(&isp->pdev->dev, + "CPD firmware reload was only supported for secure mode.\n"); + return -EINVAL; + } + + if (isp->cpd_fw) { + ipu_cpd_free_pkg_dir(isp->psys, psys->pkg_dir, + psys->pkg_dir_dma_addr, + psys->pkg_dir_size); + + ipu_buttress_unmap_fw_image(isp->psys, &psys->fw_sgt); + release_firmware(isp->cpd_fw); + isp->cpd_fw = NULL; + dev_info(&isp->pdev->dev, "Old FW removed\n"); + } + + rval = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, + &isp->pdev->dev); + if (rval) { + dev_err(&isp->pdev->dev, "Requesting firmware(%s) failed\n", + IPU_CPD_FIRMWARE_NAME); + return rval; + } + + rval = ipu_cpd_validate_cpd_file(isp, isp->cpd_fw->data, + isp->cpd_fw->size); + if (rval) { + dev_err(&isp->pdev->dev, "Failed to validate cpd file\n"); + goto out_release_firmware; + } + + rval = ipu_buttress_map_fw_image(isp->psys, isp->cpd_fw, &psys->fw_sgt); + if (rval) + goto out_release_firmware; + + psys->pkg_dir = ipu_cpd_create_pkg_dir(isp->psys, + isp->cpd_fw->data, + sg_dma_address(psys->fw_sgt.sgl), + &psys->pkg_dir_dma_addr, + &psys->pkg_dir_size); + + if (!psys->pkg_dir) { + rval = -EINVAL; + goto out_unmap_fw_image; + } + + isp->pkg_dir = psys->pkg_dir; + isp->pkg_dir_dma_addr = psys->pkg_dir_dma_addr; + isp->pkg_dir_size = psys->pkg_dir_size; + + if (!isp->secure_mode) + return 0; + + rval = ipu_fw_authenticate(isp, 1); + if (rval) + goto out_free_pkg_dir; + + return 0; + +out_free_pkg_dir: + ipu_cpd_free_pkg_dir(isp->psys, psys->pkg_dir, + psys->pkg_dir_dma_addr, psys->pkg_dir_size); +out_unmap_fw_image: + ipu_buttress_unmap_fw_image(isp->psys, &psys->fw_sgt); +out_release_firmware: + release_firmware(isp->cpd_fw); + isp->cpd_fw = NULL; + + return rval; +} + +static int ipu_psys_icache_prefetch_sp_get(void *data, u64 *val) +{ + struct ipu_psys *psys = data; + + *val = psys->icache_prefetch_sp; + return 0; +} + +static int ipu_psys_icache_prefetch_sp_set(void *data, u64 val) +{ + struct ipu_psys *psys = data; + + if (val != !!val) + return -EINVAL; + + psys->icache_prefetch_sp = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(psys_icache_prefetch_sp_fops, + ipu_psys_icache_prefetch_sp_get, + ipu_psys_icache_prefetch_sp_set, "%llu\n"); + +static int ipu_psys_icache_prefetch_isp_get(void *data, u64 *val) +{ + struct ipu_psys *psys = data; + + *val = psys->icache_prefetch_isp; + return 0; +} + +static int ipu_psys_icache_prefetch_isp_set(void *data, u64 val) +{ + struct ipu_psys *psys = data; + + if (val != !!val) + return -EINVAL; + + psys->icache_prefetch_isp = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(psys_icache_prefetch_isp_fops, + ipu_psys_icache_prefetch_isp_get, + ipu_psys_icache_prefetch_isp_set, "%llu\n"); + +static int ipu_psys_init_debugfs(struct ipu_psys *psys) +{ + struct dentry *file; + struct dentry *dir; + + dir = debugfs_create_dir("psys", psys->adev->isp->ipu_dir); + if (IS_ERR(dir)) + return -ENOMEM; + + file = debugfs_create_file("icache_prefetch_sp", 0600, + dir, psys, &psys_icache_prefetch_sp_fops); + if (IS_ERR(file)) + goto err; + + file = debugfs_create_file("icache_prefetch_isp", 0600, + dir, psys, &psys_icache_prefetch_isp_fops); + if (IS_ERR(file)) + goto err; + + psys->debugfsdir = dir; + + + return 0; +err: + debugfs_remove_recursive(dir); + return -ENOMEM; +} + +static int ipu_psys_sched_cmd(void *ptr) +{ + struct ipu_psys *psys = ptr; + size_t pending = 0; + + while (1) { + wait_event_interruptible(psys->sched_cmd_wq, + (kthread_should_stop() || (pending = + atomic_read(&psys->wakeup_sched_thread_count)))); + + if (kthread_should_stop()) + break; + + if (pending == 0) + continue; + + mutex_lock(&psys->mutex); + atomic_set(&psys->wakeup_sched_thread_count, 0); + ipu_psys_run_next(psys); + mutex_unlock(&psys->mutex); + } + + return 0; +} + +static void start_sp(struct ipu_bus_device *adev) +{ + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + void __iomem *spc_regs_base = psys->pdata->base + + psys->pdata->ipdata->hw_variant.spc_offset; + u32 val = 0; + + val |= IPU_ISYS_SPC_STATUS_START | + IPU_ISYS_SPC_STATUS_RUN | + IPU_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; + val |= psys->icache_prefetch_sp ? + IPU_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0; + writel(val, spc_regs_base + IPU_ISYS_REG_SPC_STATUS_CTRL); +} + +static int query_sp(struct ipu_bus_device *adev) +{ + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + void __iomem *spc_regs_base = psys->pdata->base + + psys->pdata->ipdata->hw_variant.spc_offset; + u32 val = readl(spc_regs_base + IPU_ISYS_REG_SPC_STATUS_CTRL); + + /* return true when READY == 1, START == 0 */ + val &= IPU_ISYS_SPC_STATUS_READY | IPU_ISYS_SPC_STATUS_START; + + return val == IPU_ISYS_SPC_STATUS_READY; +} + +static int ipu_psys_fw_init(struct ipu_psys *psys) +{ + struct ipu_fw_syscom_queue_config + fw_psys_cmd_queue_cfg[IPU_FW_PSYS_N_PSYS_CMD_QUEUE_ID]; + struct ipu_fw_syscom_queue_config fw_psys_event_queue_cfg[] = { + { + IPU_FW_PSYS_EVENT_QUEUE_SIZE, + sizeof(struct ipu_fw_psys_event) + } + }; + struct ipu_fw_psys_srv_init server_init = { + .ddr_pkg_dir_address = 0, + .host_ddr_pkg_dir = NULL, + .pkg_dir_size = 0, + .icache_prefetch_sp = psys->icache_prefetch_sp, + .icache_prefetch_isp = psys->icache_prefetch_isp, + }; + struct ipu_fw_com_cfg fwcom = { + .num_input_queues = IPU_FW_PSYS_N_PSYS_CMD_QUEUE_ID, + .num_output_queues = IPU_FW_PSYS_N_PSYS_EVENT_QUEUE_ID, + .output = fw_psys_event_queue_cfg, + .specific_addr = &server_init, + .specific_size = sizeof(server_init), + .cell_start = start_sp, + .cell_ready = query_sp, + }; + int rval, i; + + for (i = 0; i < IPU_FW_PSYS_N_PSYS_CMD_QUEUE_ID; i++) { + fw_psys_cmd_queue_cfg[i].queue_size = + IPU_FW_PSYS_CMD_QUEUE_SIZE; + fw_psys_cmd_queue_cfg[i].token_size = + sizeof(struct ipu_fw_psys_cmd); + } + + fwcom.input = fw_psys_cmd_queue_cfg; + + fwcom.dmem_addr = psys->pdata->ipdata->hw_variant.dmem_offset; + + rval = ipu_buttress_authenticate(psys->adev->isp); + if (rval) { + dev_err(&psys->adev->dev, "FW authentication failed(%d)\n", + rval); + return rval; + } + + psys->fwcom = ipu_fw_com_prepare(&fwcom, psys->adev, psys->pdata->base); + if (!psys->fwcom) { + dev_err(&psys->adev->dev, "psys fw com prepare failed\n"); + return -EIO; + } + + return 0; +} + +static void run_fw_init_work(struct work_struct *work) +{ + struct fw_init_task *task = (struct fw_init_task *)work; + struct ipu_psys *psys = task->psys; + int rval; + + rval = ipu_psys_fw_init(psys); + + if (rval) { + dev_err(&psys->adev->dev, "FW init failed(%d)\n", rval); + ipu_psys_remove(psys->adev); + } else { + dev_info(&psys->adev->dev, "FW init done\n"); + } +} + +static int ipu_psys_probe(struct ipu_bus_device *adev) +{ + struct ipu_mmu *mmu = dev_get_drvdata(adev->iommu); + struct ipu_device *isp = adev->isp; + struct ipu_psys_pg *kpg, *kpg0; + struct ipu_psys *psys; + const struct firmware *fw; + unsigned int minor; + int i, rval = -E2BIG; + + trace_printk("B|%d|TMWK\n", current->pid); + + /* Has the domain been attached? */ + if (!mmu) { + trace_printk("E|TMWK\n"); + return -EPROBE_DEFER; + } + + mutex_lock(&ipu_psys_mutex); + + minor = find_next_zero_bit(ipu_psys_devices, IPU_PSYS_NUM_DEVICES, 0); + if (minor == IPU_PSYS_NUM_DEVICES) { + dev_err(&adev->dev, "too many devices\n"); + goto out_unlock; + } + + psys = devm_kzalloc(&adev->dev, sizeof(*psys), GFP_KERNEL); + if (!psys) { + rval = -ENOMEM; + goto out_unlock; + } + + psys->adev = adev; + psys->pdata = adev->pdata; +#ifdef CONFIG_VIDEO_INTEL_IPU4 + psys->icache_prefetch_sp = is_ipu_hw_bxtp_e0(isp); +#else + psys->icache_prefetch_sp = 0; +#endif + + ipu_trace_init(adev->isp, psys->pdata->base, &adev->dev, + psys_trace_blocks); + + cdev_init(&psys->cdev, &ipu_psys_fops); + psys->cdev.owner = ipu_psys_fops.owner; + + rval = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu_psys_dev_t), minor), 1); + if (rval) { + dev_err(&adev->dev, "cdev_add failed (%d)\n", rval); + goto out_unlock; + } + + set_bit(minor, ipu_psys_devices); + + spin_lock_init(&psys->power_lock); + spin_lock_init(&psys->pgs_lock); + psys->power = 0; + psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; + + mutex_init(&psys->mutex); + INIT_LIST_HEAD(&psys->fhs); + INIT_LIST_HEAD(&psys->pgs); + INIT_LIST_HEAD(&psys->started_kcmds_list); + INIT_WORK(&psys->watchdog_work, ipu_psys_watchdog_work); + + init_waitqueue_head(&psys->sched_cmd_wq); + atomic_set(&psys->wakeup_sched_thread_count, 0); + /* + * Create a thread to schedule commands sent to IPU firmware. + * The thread reduces the coupling between the command scheduler + * and queueing commands from the user to driver. + */ + psys->sched_cmd_thread = kthread_run(ipu_psys_sched_cmd, psys, + "psys_sched_cmd"); + + if (IS_ERR(psys->sched_cmd_thread)) { + psys->sched_cmd_thread = NULL; + mutex_destroy(&psys->mutex); + goto out_unlock; + } + + ipu_bus_set_drvdata(adev, psys); + + rval = ipu_psys_resource_pool_init(&psys->resource_pool_started); + if (rval < 0) { + dev_err(&psys->dev, + "unable to alloc process group resources\n"); + goto out_mutex_destroy; + } + + rval = ipu_psys_resource_pool_init(&psys->resource_pool_running); + if (rval < 0) { + dev_err(&psys->dev, + "unable to alloc process group resources\n"); + goto out_resources_started_free; + } + + fw = adev->isp->cpd_fw; + + rval = ipu_buttress_map_fw_image(adev, fw, &psys->fw_sgt); + if (rval) + goto out_resources_running_free; + + psys->pkg_dir = ipu_cpd_create_pkg_dir(adev, fw->data, + sg_dma_address(psys->fw_sgt.sgl), + &psys->pkg_dir_dma_addr, + &psys->pkg_dir_size); + if (!psys->pkg_dir) { + rval = -ENOMEM; + goto out_unmap_fw_image; + } + + /* allocate and map memory for process groups */ + for (i = 0; i < IPU_PSYS_PG_POOL_SIZE; i++) { + kpg = kzalloc(sizeof(*kpg), GFP_KERNEL); + if (!kpg) + goto out_free_pgs; + kpg->pg = dma_alloc_attrs(&adev->dev, + IPU_PSYS_PG_MAX_SIZE, + &kpg->pg_dma_addr, + GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); + if (!kpg->pg) { + kfree(kpg); + goto out_free_pgs; + } + kpg->size = IPU_PSYS_PG_MAX_SIZE; + list_add(&kpg->list, &psys->pgs); + } + + isp->pkg_dir = psys->pkg_dir; + isp->pkg_dir_dma_addr = psys->pkg_dir_dma_addr; + isp->pkg_dir_size = psys->pkg_dir_size; + + caps.pg_count = ipu_cpd_pkg_dir_get_num_entries(psys->pkg_dir); + + dev_info(&adev->dev, "pkg_dir entry count:%d\n", caps.pg_count); + if (async_fw_init) { + INIT_DELAYED_WORK((struct delayed_work *)&fw_init_task, + run_fw_init_work); + fw_init_task.psys = psys; + schedule_delayed_work((struct delayed_work *)&fw_init_task, 0); + } else { + rval = ipu_psys_fw_init(psys); + } + + if (rval) { + dev_err(&adev->dev, "FW init failed(%d)\n", rval); + goto out_free_pgs; + } + + psys->dev.parent = &adev->dev; + psys->dev.bus = &ipu_psys_bus; + psys->dev.devt = MKDEV(MAJOR(ipu_psys_dev_t), minor); + psys->dev.release = ipu_psys_dev_release; + dev_set_name(&psys->dev, "ipu-psys%d", minor); + rval = device_register(&psys->dev); + if (rval < 0) { + dev_err(&psys->dev, "psys device_register failed\n"); + goto out_release_fw_com; + } + + /* Add the hw stepping information to caps */ + strlcpy(caps.dev_model, IPU_MEDIA_DEV_MODEL_NAME, + sizeof(caps.dev_model)); + + pm_runtime_allow(&adev->dev); + pm_runtime_enable(&adev->dev); + + pm_runtime_set_autosuspend_delay(&psys->adev->dev, + IPU_PSYS_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&psys->adev->dev); + pm_runtime_mark_last_busy(&psys->adev->dev); + + mutex_unlock(&ipu_psys_mutex); + + /* Debug fs failure is not fatal. */ + ipu_psys_init_debugfs(psys); + + adev->isp->cpd_fw_reload = &cpd_fw_reload; + + dev_info(&adev->dev, "psys probe minor: %d\n", minor); + + trace_printk("E|TMWK\n"); + return 0; + +out_release_fw_com: + ipu_fw_com_release(psys->fwcom, 1); +out_free_pgs: + list_for_each_entry_safe(kpg, kpg0, &psys->pgs, list) { + dma_free_attrs(&adev->dev, kpg->size, kpg->pg, + kpg->pg_dma_addr, DMA_ATTR_NON_CONSISTENT); + kfree(kpg); + } + + if (!isp->secure_mode) + ipu_cpd_free_pkg_dir(adev, psys->pkg_dir, + psys->pkg_dir_dma_addr, + psys->pkg_dir_size); +out_unmap_fw_image: + ipu_buttress_unmap_fw_image(adev, &psys->fw_sgt); +out_resources_running_free: + ipu_psys_resource_pool_cleanup(&psys->resource_pool_running); +out_resources_started_free: + ipu_psys_resource_pool_cleanup(&psys->resource_pool_started); +out_mutex_destroy: + mutex_destroy(&psys->mutex); + cdev_del(&psys->cdev); + if (psys->sched_cmd_thread) { + kthread_stop(psys->sched_cmd_thread); + psys->sched_cmd_thread = NULL; + } +out_unlock: + /* Safe to call even if the init is not called */ + ipu_trace_uninit(&adev->dev); + mutex_unlock(&ipu_psys_mutex); + + trace_printk("E|TMWK\n"); + return rval; +} + +static void ipu_psys_remove(struct ipu_bus_device *adev) +{ + struct ipu_device *isp = adev->isp; + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + struct ipu_psys_pg *kpg, *kpg0; + + if (isp->ipu_dir) + debugfs_remove_recursive(psys->debugfsdir); + + flush_workqueue(IPU_PSYS_WORK_QUEUE); + + if (psys->sched_cmd_thread) { + kthread_stop(psys->sched_cmd_thread); + psys->sched_cmd_thread = NULL; + } + + pm_runtime_dont_use_autosuspend(&psys->adev->dev); + + mutex_lock(&ipu_psys_mutex); + + list_for_each_entry_safe(kpg, kpg0, &psys->pgs, list) { + dma_free_attrs(&adev->dev, kpg->size, kpg->pg, + kpg->pg_dma_addr, DMA_ATTR_NON_CONSISTENT); + kfree(kpg); + } + + if (psys->fwcom && ipu_fw_com_release(psys->fwcom, 1)) + dev_err(&adev->dev, "fw com release failed.\n"); + + isp->pkg_dir = NULL; + isp->pkg_dir_dma_addr = 0; + isp->pkg_dir_size = 0; + + ipu_cpd_free_pkg_dir(adev, psys->pkg_dir, + psys->pkg_dir_dma_addr, psys->pkg_dir_size); + + ipu_buttress_unmap_fw_image(adev, &psys->fw_sgt); + + kfree(psys->server_init); + kfree(psys->syscom_config); + + ipu_trace_uninit(&adev->dev); + + ipu_psys_resource_pool_cleanup(&psys->resource_pool_started); + ipu_psys_resource_pool_cleanup(&psys->resource_pool_running); + + device_unregister(&psys->dev); + + clear_bit(MINOR(psys->cdev.dev), ipu_psys_devices); + cdev_del(&psys->cdev); + + mutex_unlock(&ipu_psys_mutex); + + mutex_destroy(&psys->mutex); + + dev_info(&adev->dev, "removed\n"); +} + +static irqreturn_t psys_isr_threaded(struct ipu_bus_device *adev) +{ + struct ipu_psys *psys = ipu_bus_get_drvdata(adev); + void __iomem *base = psys->pdata->base; + u32 status; + int r; + + mutex_lock(&psys->mutex); +#ifdef CONFIG_PM + if (!READ_ONCE(psys->power)) { + mutex_unlock(&psys->mutex); + return IRQ_NONE; + } + + r = pm_runtime_get_sync(&psys->adev->dev); + if (r < 0) { + pm_runtime_put(&psys->adev->dev); + mutex_unlock(&psys->mutex); + return IRQ_NONE; + } +#endif + + status = readl(base + IPU_REG_PSYS_GPDEV_IRQ_STATUS); + writel(status, base + IPU_REG_PSYS_GPDEV_IRQ_CLEAR); + + if (status & IPU_PSYS_GPDEV_IRQ_FWIRQ(IPU_PSYS_GPDEV_FWIRQ0)) { + writel(0, base + IPU_REG_PSYS_GPDEV_FWIRQ(0)); + ipu_psys_handle_events(psys); + } + + pm_runtime_mark_last_busy(&psys->adev->dev); + pm_runtime_put_autosuspend(&psys->adev->dev); + mutex_unlock(&psys->mutex); + + return status ? IRQ_HANDLED : IRQ_NONE; +} + + +static struct ipu_bus_driver ipu_psys_driver = { + .probe = ipu_psys_probe, + .remove = ipu_psys_remove, + .isr_threaded = psys_isr_threaded, + .wanted = IPU_PSYS_NAME, + .drv = { + .name = IPU_PSYS_NAME, + .owner = THIS_MODULE, + .pm = PSYS_PM_OPS, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +static int __init ipu_psys_init(void) +{ + int rval = alloc_chrdev_region(&ipu_psys_dev_t, 0, + IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); + if (rval) { + pr_err("can't alloc psys chrdev region (%d)\n", rval); + return rval; + } + + rval = bus_register(&ipu_psys_bus); + if (rval) { + pr_warn("can't register psys bus (%d)\n", rval); + goto out_bus_register; + } + + ipu_bus_register_driver(&ipu_psys_driver); + + return rval; + +out_bus_register: + unregister_chrdev_region(ipu_psys_dev_t, IPU_PSYS_NUM_DEVICES); + + return rval; +} + +static void __exit ipu_psys_exit(void) +{ + ipu_bus_unregister_driver(&ipu_psys_driver); + bus_unregister(&ipu_psys_bus); + unregister_chrdev_region(ipu_psys_dev_t, IPU_PSYS_NUM_DEVICES); +} + +static const struct pci_device_id ipu_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU_PCI_ID)}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ipu_pci_tbl); + +module_init(ipu_psys_init); +module_exit(ipu_psys_exit); + +MODULE_AUTHOR("Antti Laakso "); +MODULE_AUTHOR("Bin Han "); +MODULE_AUTHOR("Renwei Wu "); +MODULE_AUTHOR("Jianxu Zheng "); +MODULE_AUTHOR("Xia Wu "); +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Zaikuo Wang "); +MODULE_AUTHOR("Yunliang Ding "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu processing system driver"); diff --git a/drivers/media/pci/intel/ipu-psys.h b/drivers/media/pci/intel/ipu-psys.h new file mode 100644 index 000000000000..bf888b38b2fd --- /dev/null +++ b/drivers/media/pci/intel/ipu-psys.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_PSYS_H +#define IPU_PSYS_H + +#include +#include + +#include "ipu.h" +#include "ipu-pdata.h" +#include "ipu-fw-psys.h" +#include "ipu-platform-psys.h" + +#define IPU_PSYS_PG_POOL_SIZE 16 +#define IPU_PSYS_PG_MAX_SIZE 2048 +#define IPU_MAX_PSYS_CMD_BUFFERS 32 +#define IPU_PSYS_EVENT_CMD_COMPLETE IPU_FW_PSYS_EVENT_TYPE_SUCCESS +#define IPU_PSYS_EVENT_FRAGMENT_COMPLETE IPU_FW_PSYS_EVENT_TYPE_SUCCESS +#define IPU_PSYS_CLOSE_TIMEOUT_US 50 +#define IPU_PSYS_CLOSE_TIMEOUT (100000 / IPU_PSYS_CLOSE_TIMEOUT_US) +#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq +#define IPU_MAX_RESOURCES 128 + +/* Opaque structure. Do not access fields. */ +struct ipu_resource { + u32 id; + int elements; /* Number of elements available to allocation */ + unsigned long *bitmap; /* Allocation bitmap, a bit for each element */ +}; + +enum ipu_resource_type { + IPU_RESOURCE_DEV_CHN = 0, + IPU_RESOURCE_EXT_MEM, + IPU_RESOURCE_DFM +}; + +/* Allocation of resource(s) */ +/* Opaque structure. Do not access fields. */ +struct ipu_resource_alloc { + enum ipu_resource_type type; + struct ipu_resource *resource; + int elements; + int pos; +}; + +/* + * This struct represents all of the currently allocated + * resources from IPU model. It is used also for allocating + * resources for the next set of PGs to be run on IPU + * (ie. those PGs which are not yet being run and which don't + * yet reserve real IPU resources). + */ +#define IPU_PSYS_RESOURCE_OVERALLOC 2 /* Some room for ABI / ext lib delta */ +struct ipu_psys_resource_pool { + u32 cells; /* Bitmask of cells allocated */ + struct ipu_resource dev_channels[IPU_FW_PSYS_N_DEV_CHN_ID + + IPU_PSYS_RESOURCE_OVERALLOC]; + struct ipu_resource ext_memory[IPU_FW_PSYS_N_MEM_ID + + IPU_PSYS_RESOURCE_OVERALLOC]; + struct ipu_resource dfms[IPU_FW_PSYS_N_DEV_DFM_ID + + IPU_PSYS_RESOURCE_OVERALLOC]; +}; + +/* + * This struct keeps book of the resources allocated for a specific PG. + * It is used for freeing up resources from struct ipu_psys_resources + * when the PG is released from IPU4 (or model of IPU4). + */ +struct ipu_psys_resource_alloc { + u32 cells; /* Bitmask of cells needed */ + struct ipu_resource_alloc + resource_alloc[IPU_MAX_RESOURCES]; + int resources; +}; + +struct task_struct; +struct ipu_psys { + struct cdev cdev; + struct device dev; + + struct mutex mutex; /* Psys various */ + int power; + bool icache_prefetch_sp; + bool icache_prefetch_isp; + spinlock_t power_lock; /* Serialize access to power */ + spinlock_t pgs_lock; /* Protect pgs list access */ + struct list_head fhs; + struct list_head pgs; + struct list_head started_kcmds_list; + struct ipu_psys_pdata *pdata; + struct ipu_bus_device *adev; + struct ia_css_syscom_context *dev_ctx; + struct ia_css_syscom_config *syscom_config; + struct ia_css_psys_server_init *server_init; + struct task_struct *sched_cmd_thread; + struct work_struct watchdog_work; + wait_queue_head_t sched_cmd_wq; + atomic_t wakeup_sched_thread_count; + struct dentry *debugfsdir; + + /* Resources needed to be managed for process groups */ + struct ipu_psys_resource_pool resource_pool_running; + struct ipu_psys_resource_pool resource_pool_started; + + const struct firmware *fw; + struct sg_table fw_sgt; + u64 *pkg_dir; + dma_addr_t pkg_dir_dma_addr; + unsigned int pkg_dir_size; + unsigned long timeout; + + int active_kcmds, started_kcmds; + void *fwcom; +}; + +struct ipu_psys_fh { + struct ipu_psys *psys; + struct mutex mutex; /* Protects bufmap & kcmds fields */ + struct list_head list; + struct list_head bufmap; + wait_queue_head_t wait; + struct ipu_psys_scheduler sched; +}; + +struct ipu_psys_pg { + struct ipu_fw_psys_process_group *pg; + size_t size; + size_t pg_size; + dma_addr_t pg_dma_addr; + struct list_head list; + struct ipu_psys_resource_alloc resource_alloc; +}; + +struct ipu_psys_kcmd { + struct ipu_psys_fh *fh; + struct list_head list; + struct list_head started_list; + enum ipu_psys_cmd_state state; + void *pg_manifest; + size_t pg_manifest_size; + struct ipu_psys_kbuffer **kbufs; + struct ipu_psys_buffer *buffers; + size_t nbuffers; + struct ipu_fw_psys_process_group *pg_user; + struct ipu_psys_pg *kpg; + u64 user_token; + u64 issue_id; + u32 priority; + struct ipu_buttress_constraint constraint; + struct ipu_psys_event ev; + struct timer_list watchdog; +}; + +struct ipu_dma_buf_attach { + struct device *dev; + u64 len; + void *userptr; + struct sg_table *sgt; + bool vma_is_io; + struct page **pages; + size_t npages; +}; + +struct ipu_psys_kbuffer { + u64 len; + void *userptr; + u32 flags; + int fd; + void *kaddr; + struct list_head list; + dma_addr_t dma_addr; + struct sg_table *sgt; + struct dma_buf_attachment *db_attach; + struct dma_buf *dbuf; + bool valid; /* True when buffer is usable */ +}; + +#define inode_to_ipu_psys(inode) \ + container_of((inode)->i_cdev, struct ipu_psys, cdev) + +#ifdef CONFIG_COMPAT +long ipu_psys_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg); +#endif + +void ipu_psys_setup_hw(struct ipu_psys *psys); +void ipu_psys_handle_events(struct ipu_psys *psys); +int ipu_psys_kcmd_new(struct ipu_psys_command *cmd, struct ipu_psys_fh *fh); +void ipu_psys_run_next(struct ipu_psys *psys); +void ipu_psys_watchdog_work(struct work_struct *work); +struct ipu_psys_pg *__get_pg_buf(struct ipu_psys *psys, size_t pg_size); +struct ipu_psys_kbuffer * +ipu_psys_lookup_kbuffer(struct ipu_psys_fh *fh, int fd); +struct ipu_psys_kbuffer * +ipu_psys_lookup_kbuffer_by_kaddr(struct ipu_psys_fh *fh, void *kaddr); +int ipu_psys_resource_pool_init(struct ipu_psys_resource_pool *pool); +void ipu_psys_resource_pool_cleanup(struct ipu_psys_resource_pool *pool); +struct ipu_psys_kcmd *ipu_get_completed_kcmd(struct ipu_psys_fh *fh); +long ipu_ioctl_dqevent(struct ipu_psys_event *event, + struct ipu_psys_fh *fh, unsigned int f_flags); + +#endif /* IPU_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu-trace-event.h b/drivers/media/pci/intel/ipu-trace-event.h new file mode 100644 index 000000000000..b5e8d4be42ed --- /dev/null +++ b/drivers/media/pci/intel/ipu-trace-event.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ipu + +#if !defined(IPU_TRACE_EVENT_H) || defined(TRACE_HEADER_MULTI_READ) +#define IPU_EVENT_H + +#include + +#ifdef IPU_SOF_SEQID_TRACE +TRACE_EVENT(ipu_sof_seqid, + TP_PROTO(unsigned int seqid, unsigned int csiport, + unsigned int csivc), + TP_ARGS(seqid, csiport, csivc), + TP_STRUCT__entry(__field(unsigned int, seqid) + __field(unsigned int, csiport) + __field(unsigned int, csivc) + ), + TP_fast_assign(__entry->seqid = seqid; + __entry->csiport = csiport; + __entry->csivc = csivc;), + TP_printk("seqid<%u>,csiport<%u>,csivc<%u>", __entry->seqid, + __entry->csiport, __entry->csivc) + ); +#endif + +#ifdef IPU_EOF_SEQID_TRACE +TRACE_EVENT(ipu_eof_seqid, + TP_PROTO(unsigned int seqid, unsigned int csiport, + unsigned int csivc), + TP_ARGS(seqid, csiport, csivc), + TP_STRUCT__entry(__field(unsigned int, seqid) + __field(unsigned int, csiport) + __field(unsigned int, csivc) + ), + TP_fast_assign(__entry->seqid = seqid; + __entry->csiport = csiport; + __entry->csivc = csivc;), + TP_printk("seqid<%u>,csiport<%u>,csivc<%u>", __entry->seqid, + __entry->csiport, __entry->csivc) + ); +#endif + +#ifdef IPU_PERF_REG_TRACE +TRACE_EVENT(ipu_perf_reg, + TP_PROTO(unsigned int addr, unsigned int val), + TP_ARGS(addr, val), TP_STRUCT__entry(__field(unsigned int, addr) + __field(unsigned int, val) + ), + TP_fast_assign(__entry->addr = addr; + __entry->val = val;), + TP_printk("addr=%u,val=%u", __entry->addr, __entry->val) + ); +#endif + +#ifdef IPU_PG_KCMD_TRACE +TRACE_EVENT(ipu_pg_kcmd, + TP_PROTO(const char *func, unsigned int id, + unsigned long long issue_id, unsigned int pri, + unsigned int pg_id, unsigned int load_cycles, + unsigned int init_cycles, + unsigned int processing_cycles), + TP_ARGS(func, id, issue_id, pri, pg_id, load_cycles, + init_cycles, processing_cycles), + TP_STRUCT__entry(__field(const char *, func) + __field(unsigned int, id) + __field(unsigned long long, issue_id) + __field(unsigned int, pri) + __field(unsigned int, pg_id) + __field(unsigned int, load_cycles) + __field(unsigned int, init_cycles) + __field(unsigned int, processing_cycles) + ), + TP_fast_assign(__entry->func = func; + __entry->id = id; + __entry->issue_id = issue_id; + __entry->pri = pri; + __entry->pg_id = pg_id; + __entry->load_cycles = load_cycles; + __entry->init_cycles = init_cycles; + __entry->processing_cycles = processing_cycles;), + TP_printk + ("pg-kcmd: func=%s,id=%u,issue_id=0x%llx,pri=%u,pg_id=%d," + "load_cycles=%u,init_cycles=%u,processing_cycles=%u", + __entry->func, __entry->id, __entry->issue_id, __entry->pri, + __entry->pg_id, __entry->load_cycles, __entry->init_cycles, + __entry->processing_cycles) + ); + +#endif +#endif + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE ipu-trace-event +/* This part must be outside protection */ +#include diff --git a/drivers/media/pci/intel/ipu-trace.c b/drivers/media/pci/intel/ipu-trace.c new file mode 100644 index 000000000000..5e0795d78649 --- /dev/null +++ b/drivers/media/pci/intel/ipu-trace.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-platform-regs.h" +#include "ipu-trace.h" + +/* Input data processing states */ +enum config_file_parse_states { + STATE_FILL = 0, + STATE_COMMENT, + STATE_COMPLETE, +}; + +struct trace_register_range { + u32 start; + u32 end; +}; + +static u16 trace_unit_template[] = TRACE_REG_CREATE_TUN_REGISTER_LIST; +static u16 trace_monitor_template[] = TRACE_REG_CREATE_TM_REGISTER_LIST; +static u16 trace_gpc_template[] = TRACE_REG_CREATE_GPC_REGISTER_LIST; + +static struct trace_register_range trace_csi2_range_template[] = { + { + .start = TRACE_REG_CSI2_TM_RESET_REG_IDX, + .end = TRACE_REG_CSI2_TM_IRQ_ENABLE_REG_IDn(7) + }, + { + .start = TRACE_REG_END_MARK, + .end = TRACE_REG_END_MARK + } +}; + +static struct trace_register_range trace_csi2_3ph_range_template[] = { + { + .start = TRACE_REG_CSI2_3PH_TM_RESET_REG_IDX, + .end = TRACE_REG_CSI2_3PH_TM_IRQ_ENABLE_REG_IDn(7) + }, + { + .start = TRACE_REG_END_MARK, + .end = TRACE_REG_END_MARK + } +}; + +static struct trace_register_range trace_sig2cio_range_template[] = { + { + .start = TRACE_REG_SIG2CIO_ADDRESS, + .end = (TRACE_REG_SIG2CIO_STATUS + 8 * TRACE_REG_SIG2CIO_SIZE_OF) + }, + { + .start = TRACE_REG_END_MARK, + .end = TRACE_REG_END_MARK + } +}; + +#define LINE_MAX_LEN 128 +#define MEMORY_RING_BUFFER_SIZE (SZ_1M * 10) +#define TRACE_MESSAGE_SIZE 16 +/* + * It looks that the trace unit sometimes writes outside the given buffer. + * To avoid memory corruption one extra page is reserved at the end + * of the buffer. Read also the extra area since it may contain valid data. + */ +#define MEMORY_RING_BUFFER_GUARD PAGE_SIZE +#define MEMORY_RING_BUFFER_OVERREAD MEMORY_RING_BUFFER_GUARD +#define MAX_TRACE_REGISTERS 200 +#define TRACE_CONF_DUMP_BUFFER_SIZE (MAX_TRACE_REGISTERS * 2 * 32) + +#define IPU_TRACE_TIME_RETRY 5 + +struct config_value { + u32 reg; + u32 value; +}; + +struct ipu_trace_buffer { + dma_addr_t dma_handle; + void *memory_buffer; +}; + +struct ipu_subsystem_trace_config { + u32 offset; + void __iomem *base; + struct ipu_trace_buffer memory; /* ring buffer */ + struct device *dev; + struct ipu_trace_block *blocks; + unsigned int fill_level; /* Nbr of regs in config table below */ + bool running; + /* Cached register values */ + struct config_value config[MAX_TRACE_REGISTERS]; +}; + +/* + * State of the input data processing is kept in this structure. + * Only one user is supported at time. + */ +struct buf_state { + char line_buffer[LINE_MAX_LEN]; + enum config_file_parse_states state; + int offset; /* Offset to line_buffer */ +}; + +struct ipu_trace { + struct mutex lock; + bool open; + char *conf_dump_buffer; + int size_conf_dump; + struct buf_state buffer_state; + + struct ipu_subsystem_trace_config isys; + struct ipu_subsystem_trace_config psys; +}; + +int ipu_trace_get_timer(struct device *dev, u64 *timer) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_subsystem_trace_config *sys = adev->trace_cfg; + struct ipu_trace_block *blocks; + void __iomem *addr = NULL; + uint32_t time_hi1, time_hi2, time_lo, retry; + + if (!sys) + return -ENODEV; + /* Find trace unit base address */ + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_BLOCK_TUN) { + addr = sys->base + blocks->offset; + break; + } + blocks++; + } + if (!addr) + return -ENODEV; + + for (retry = 0; retry < IPU_TRACE_TIME_RETRY; retry++) { + time_hi1 = readl(addr + TRACE_REG_TUN_LOCAL_TIMER1); + time_lo = readl(addr + TRACE_REG_TUN_LOCAL_TIMER0); + time_hi2 = readl(addr + TRACE_REG_TUN_LOCAL_TIMER1); + *timer = (((u64) time_hi1) << 32) | time_lo; + if (time_hi1 == time_hi2) + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ipu_trace_get_timer); + +static void __ipu_trace_restore(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_device *isp = adev->isp; + struct ipu_trace *trace = isp->trace; + struct config_value *config; + struct ipu_subsystem_trace_config *sys = adev->trace_cfg; + struct ipu_trace_block *blocks; + uint32_t mapped_trace_buffer; + void __iomem *addr = NULL; + int i; + + if (trace->open) { + dev_info(dev, "Trace control file open. Skipping update\n"); + return; + } + + if (!sys) + return; + + /* leave if no trace configuration for this subsystem */ + if (sys->fill_level == 0) + return; + + /* Find trace unit base address */ + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_BLOCK_TUN) { + addr = sys->base + blocks->offset; + break; + } + blocks++; + } + if (!addr) + return; + + if (!sys->memory.memory_buffer) { + sys->memory.memory_buffer = + dma_alloc_attrs(dev, MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_GUARD, + &sys->memory.dma_handle, + GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); + } + + if (!sys->memory.memory_buffer) { + dev_err(dev, "No memory for tracing. Trace unit disabled\n"); + return; + } + + config = sys->config; + mapped_trace_buffer = sys->memory.dma_handle; + + /* ring buffer base */ + writel(mapped_trace_buffer, addr + TRACE_REG_TUN_DRAM_BASE_ADDR); + + /* ring buffer end */ + writel(mapped_trace_buffer + MEMORY_RING_BUFFER_SIZE - + TRACE_MESSAGE_SIZE, addr + TRACE_REG_TUN_DRAM_END_ADDR); + + /* Infobits for ddr trace */ + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + addr + TRACE_REG_TUN_DDR_INFO_VAL); + + /* Find trace timer reset address */ + addr = NULL; + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_TIMER_RST) { + addr = sys->base + blocks->offset; + break; + } + blocks++; + } + if (!addr) { + dev_err(dev, "No trace reset addr\n"); + return; + } + + /* Remove reset from trace timers */ + writel(TRACE_REG_GPREG_TRACE_TIMER_RST_OFF, addr); + + /* Register config received from userspace */ + for (i = 0; i < sys->fill_level; i++) { + dev_dbg(dev, + "Trace restore: reg 0x%08x, value 0x%08x\n", + config[i].reg, config[i].value); + writel(config[i].value, isp->base + config[i].reg); + } + + sys->running = true; +} + +void ipu_trace_restore(struct device *dev) +{ + struct ipu_trace *trace = to_ipu_bus_device(dev)->isp->trace; + + if (!trace) + return; + + mutex_lock(&trace->lock); + __ipu_trace_restore(dev); + mutex_unlock(&trace->lock); +} +EXPORT_SYMBOL_GPL(ipu_trace_restore); + +static void __ipu_trace_stop(struct device *dev) +{ + struct ipu_subsystem_trace_config *sys = + to_ipu_bus_device(dev)->trace_cfg; + struct ipu_trace_block *blocks; + + if (!sys) + return; + + if (!sys->running) + return; + sys->running = false; + + /* Turn off all the gpc blocks */ + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_BLOCK_GPC) { + writel(0, sys->base + blocks->offset + + TRACE_REG_GPC_OVERALL_ENABLE); + } + blocks++; + } + + /* Turn off all the trace monitors */ + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_BLOCK_TM) { + writel(0, sys->base + blocks->offset + + TRACE_REG_TM_TRACE_ENABLE_NPK); + + writel(0, sys->base + blocks->offset + + TRACE_REG_TM_TRACE_ENABLE_DDR); + } + blocks++; + } + + /* Turn off trace units */ + blocks = sys->blocks; + while (blocks->type != IPU_TRACE_BLOCK_END) { + if (blocks->type == IPU_TRACE_BLOCK_TUN) { + writel(0, sys->base + blocks->offset + + TRACE_REG_TUN_DDR_ENABLE); + writel(0, sys->base + blocks->offset + + TRACE_REG_TUN_NPK_ENABLE); + } + blocks++; + } +} + +void ipu_trace_stop(struct device *dev) +{ + struct ipu_trace *trace = to_ipu_bus_device(dev)->isp->trace; + + if (!trace) + return; + + mutex_lock(&trace->lock); + __ipu_trace_stop(dev); + mutex_unlock(&trace->lock); +} +EXPORT_SYMBOL_GPL(ipu_trace_stop); + +static int validate_register(u32 base, u32 reg, u16 *template) +{ + int i = 0; + + while (template[i] != TRACE_REG_END_MARK) { + if (template[i] + base != reg) { + i++; + continue; + } + /* This is a valid register */ + return 0; + } + return -EINVAL; +} + +static int validate_register_range(u32 base, u32 reg, + struct trace_register_range *template) +{ + unsigned int i = 0; + + if (!IS_ALIGNED(reg, sizeof(u32))) + return -EINVAL; + + while (template[i].start != TRACE_REG_END_MARK) { + if ((reg < template[i].start + base) || + (reg > template[i].end + base)) { + i++; + continue; + } + /* This is a valid register */ + return 0; + } + return -EINVAL; +} + +static int update_register_cache(struct ipu_device *isp, u32 reg, u32 value) +{ + struct ipu_trace *dctrl = isp->trace; + const struct ipu_trace_block *blocks; + struct ipu_subsystem_trace_config *sys; + struct device *dev; + u32 base = 0; + u16 *template = NULL; + struct trace_register_range *template_range = NULL; + int i, range; + int rval = -EINVAL; + + if (dctrl->isys.offset == dctrl->psys.offset) { + /* For the IPU with uniform address space */ + if (reg >= IPU_ISYS_OFFSET && + reg < IPU_ISYS_OFFSET + TRACE_REG_MAX_ISYS_OFFSET) + sys = &dctrl->isys; + else if (reg >= IPU_PSYS_OFFSET && + reg < IPU_PSYS_OFFSET + TRACE_REG_MAX_PSYS_OFFSET) + sys = &dctrl->psys; + else + goto error; + } else { + if (dctrl->isys.offset && + reg >= dctrl->isys.offset && + reg < dctrl->isys.offset + TRACE_REG_MAX_ISYS_OFFSET) + sys = &dctrl->isys; + else if (dctrl->psys.offset && + reg >= dctrl->psys.offset && + reg < dctrl->psys.offset + TRACE_REG_MAX_PSYS_OFFSET) + sys = &dctrl->psys; + else + goto error; + } + + blocks = sys->blocks; + dev = sys->dev; + + /* Check registers block by block */ + i = 0; + while (blocks[i].type != IPU_TRACE_BLOCK_END) { + base = blocks[i].offset + sys->offset; + if ((reg >= base && reg < base + TRACE_REG_MAX_BLOCK_SIZE)) + break; + i++; + } + + range = 0; + switch (blocks[i].type) { + case IPU_TRACE_BLOCK_TUN: + template = trace_unit_template; + break; + case IPU_TRACE_BLOCK_TM: + template = trace_monitor_template; + break; + case IPU_TRACE_BLOCK_GPC: + template = trace_gpc_template; + break; + case IPU_TRACE_CSI2: + range = 1; + template_range = trace_csi2_range_template; + break; + case IPU_TRACE_CSI2_3PH: + range = 1; + template_range = trace_csi2_3ph_range_template; + break; + case IPU_TRACE_SIG2CIOS: + range = 1; + template_range = trace_sig2cio_range_template; + break; + default: + goto error; + } + + if (range) + rval = validate_register_range(base, reg, template_range); + else + rval = validate_register(base, reg, template); + + if (rval) + goto error; + + if (sys->fill_level < MAX_TRACE_REGISTERS) { + dev_dbg(dev, + "Trace reg addr 0x%08x value 0x%08x\n", reg, value); + sys->config[sys->fill_level].reg = reg; + sys->config[sys->fill_level].value = value; + sys->fill_level++; + } else { + rval = -ENOMEM; + goto error; + } + return 0; +error: + dev_info(&isp->pdev->dev, + "Trace register address 0x%08x ignored as invalid register\n", + reg); + return rval; +} + +/* + * We don't know how much data is received this time. Process given data + * character by character. + * Fill the line buffer until either + * 1) new line is got -> go to decode + * or + * 2) line_buffer is full -> ignore rest of line and then try to decode + * or + * 3) Comment mark is found -> ignore rest of the line and then try to decode + * the data which was received before the comment mark + * + * Decode phase tries to find "reg = value" pairs and validates those + */ +static int process_buffer(struct ipu_device *isp, + char *buffer, int size, struct buf_state *state) +{ + int i, ret; + int curr_state = state->state; + u32 reg, value; + + for (i = 0; i < size; i++) { + /* + * Comment mark in any position turns on comment mode + * until end of line + */ + if (curr_state != STATE_COMMENT && buffer[i] == '#') { + state->line_buffer[state->offset] = '\0'; + curr_state = STATE_COMMENT; + continue; + } + + switch (curr_state) { + case STATE_COMMENT: + /* Only new line can break this mode */ + if (buffer[i] == '\n') + curr_state = STATE_COMPLETE; + break; + case STATE_FILL: + state->line_buffer[state->offset] = buffer[i]; + state->offset++; + + if (state->offset >= sizeof(state->line_buffer) - 1) { + /* Line buffer full - ignore rest */ + state->line_buffer[state->offset] = '\0'; + curr_state = STATE_COMMENT; + break; + } + + if (buffer[i] == '\n') { + state->line_buffer[state->offset] = '\0'; + curr_state = STATE_COMPLETE; + } + break; + default: + state->offset = 0; + state->line_buffer[state->offset] = '\0'; + curr_state = STATE_COMMENT; + } + + if (curr_state == STATE_COMPLETE) { + ret = sscanf(state->line_buffer, "%x = %x", + ®, &value); + if (ret == 2) + update_register_cache(isp, reg, value); + + state->offset = 0; + curr_state = STATE_FILL; + } + } + state->state = curr_state; + return 0; +} + +static void traceconf_dump(struct ipu_device *isp) +{ + struct ipu_subsystem_trace_config *sys[2] = { + &isp->trace->isys, + &isp->trace->psys + }; + int i, j, rem_size; + char *out; + + isp->trace->size_conf_dump = 0; + out = isp->trace->conf_dump_buffer; + rem_size = TRACE_CONF_DUMP_BUFFER_SIZE; + + for (j = 0; j < ARRAY_SIZE(sys); j++) { + for (i = 0; i < sys[j]->fill_level && rem_size > 0; i++) { + int bytes_print; + int n = snprintf(out, rem_size, "0x%08x = 0x%08x\n", + sys[j]->config[i].reg, + sys[j]->config[i].value); + + bytes_print = min(n, rem_size - 1); + rem_size -= bytes_print; + out += bytes_print; + } + } + isp->trace->size_conf_dump = out - isp->trace->conf_dump_buffer; +} + +static void clear_trace_buffer(struct ipu_subsystem_trace_config *sys) +{ + if (!sys->memory.memory_buffer) + return; + + memset(sys->memory.memory_buffer, 0, MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_OVERREAD); + + dma_sync_single_for_device(sys->dev, + sys->memory.dma_handle, + MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_GUARD, DMA_FROM_DEVICE); +} + +static int traceconf_open(struct inode *inode, struct file *file) +{ + int ret; + struct ipu_device *isp; + + if (!inode->i_private) + return -EACCES; + + isp = inode->i_private; + + ret = mutex_trylock(&isp->trace->lock); + if (!ret) + return -EBUSY; + + if (isp->trace->open) { + mutex_unlock(&isp->trace->lock); + return -EBUSY; + } + + file->private_data = isp; + isp->trace->open = 1; + if (file->f_mode & FMODE_WRITE) { + /* TBD: Allocate temp buffer for processing. + * Push validated buffer to active config + */ + + /* Forget old config if opened for write */ + isp->trace->isys.fill_level = 0; + isp->trace->psys.fill_level = 0; + } + + if (file->f_mode & FMODE_READ) { + isp->trace->conf_dump_buffer = + vzalloc(TRACE_CONF_DUMP_BUFFER_SIZE); + if (!isp->trace->conf_dump_buffer) { + isp->trace->open = 0; + mutex_unlock(&isp->trace->lock); + return -ENOMEM; + } + traceconf_dump(isp); + } + mutex_unlock(&isp->trace->lock); + return 0; +} + +static ssize_t traceconf_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct ipu_device *isp = file->private_data; + + return simple_read_from_buffer(buf, len, ppos, + isp->trace->conf_dump_buffer, + isp->trace->size_conf_dump); +} + +static ssize_t traceconf_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct ipu_device *isp = file->private_data; + char buffer[64]; + ssize_t bytes, count; + loff_t pos = *ppos; + + if (*ppos < 0) + return -EINVAL; + + count = min(len, sizeof(buffer)); + bytes = copy_from_user(buffer, buf, count); + if (bytes == count) + return -EFAULT; + + count -= bytes; + mutex_lock(&isp->trace->lock); + process_buffer(isp, buffer, count, &isp->trace->buffer_state); + mutex_unlock(&isp->trace->lock); + *ppos = pos + count; + + return count; +} + +static int traceconf_release(struct inode *inode, struct file *file) +{ + struct ipu_device *isp = file->private_data; + struct device *psys_dev = isp->psys ? &isp->psys->dev : NULL; + struct device *isys_dev = isp->isys ? &isp->isys->dev : NULL; + int pm_rval = -EINVAL; + + /* + * Turn devices on outside trace->lock mutex. PM transition may + * cause call to function which tries to take the same lock. + * Also do this before trace->open is set back to 0 to avoid + * double restore (one here and one in pm transition). We can't + * rely purely on the restore done by pm call backs since trace + * configuration can occur in any phase compared to other activity. + */ + + if (file->f_mode & FMODE_WRITE) { + if (isys_dev) + pm_rval = pm_runtime_get_sync(isys_dev); + + if (pm_rval >= 0) { + /* ISYS ok or missing */ + if (psys_dev) + pm_rval = pm_runtime_get_sync(psys_dev); + + if (pm_rval < 0) { + pm_runtime_put_noidle(psys_dev); + if (isys_dev) + pm_runtime_put(isys_dev); + } + } else { + pm_runtime_put_noidle(&isp->isys->dev); + } + } + + mutex_lock(&isp->trace->lock); + isp->trace->open = 0; + vfree(isp->trace->conf_dump_buffer); + isp->trace->conf_dump_buffer = NULL; + + if (pm_rval >= 0) { + /* Update new cfg to HW */ + if (isys_dev) { + __ipu_trace_stop(isys_dev); + clear_trace_buffer(isp->isys->trace_cfg); + __ipu_trace_restore(isys_dev); + } + + if (psys_dev) { + __ipu_trace_stop(psys_dev); + clear_trace_buffer(isp->psys->trace_cfg); + __ipu_trace_restore(psys_dev); + } + } + + mutex_unlock(&isp->trace->lock); + + if (pm_rval >= 0) { + /* Again - this must be done with trace->lock not taken */ + if (psys_dev) + pm_runtime_put(psys_dev); + if (isys_dev) + pm_runtime_put(isys_dev); + } + return 0; +} + +static const struct file_operations ipu_traceconf_fops = { + .owner = THIS_MODULE, + .open = traceconf_open, + .release = traceconf_release, + .read = traceconf_read, + .write = traceconf_write, + .llseek = no_llseek, +}; + +static int gettrace_open(struct inode *inode, struct file *file) +{ + struct ipu_subsystem_trace_config *sys = inode->i_private; + + if (!sys) + return -EACCES; + + if (!sys->memory.memory_buffer) + return -EACCES; + + dma_sync_single_for_cpu(sys->dev, + sys->memory.dma_handle, + MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_GUARD, DMA_FROM_DEVICE); + + file->private_data = sys; + return 0; +}; + +static ssize_t gettrace_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct ipu_subsystem_trace_config *sys = file->private_data; + + return simple_read_from_buffer(buf, len, ppos, + sys->memory.memory_buffer, + MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_OVERREAD); +} + +static ssize_t gettrace_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct ipu_subsystem_trace_config *sys = file->private_data; + const char str[] = "clear"; + char buffer[sizeof(str)] = { 0 }; + ssize_t ret; + + ret = simple_write_to_buffer(buffer, sizeof(buffer), ppos, buf, len); + if (ret < 0) + return ret; + + if (ret < sizeof(str) - 1) + return -EINVAL; + + if (!strncmp(str, buffer, sizeof(str) - 1)) { + clear_trace_buffer(sys); + return len; + } + + return -EINVAL; +} + +static int gettrace_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations ipu_gettrace_fops = { + .owner = THIS_MODULE, + .open = gettrace_open, + .release = gettrace_release, + .read = gettrace_read, + .write = gettrace_write, + .llseek = no_llseek, +}; + +int ipu_trace_init(struct ipu_device *isp, void __iomem *base, + struct device *dev, struct ipu_trace_block *blocks) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_trace *trace = isp->trace; + struct ipu_subsystem_trace_config *sys; + int ret = 0; + + if (!isp->trace) + return 0; + + mutex_lock(&isp->trace->lock); + + if (dev == &isp->isys->dev) { + sys = &trace->isys; + } else if (dev == &isp->psys->dev) { + sys = &trace->psys; + } else { + ret = -EINVAL; + goto leave; + } + + adev->trace_cfg = sys; + sys->dev = dev; + sys->offset = base - isp->base; /* sub system offset */ + sys->base = base; + sys->blocks = blocks; + +leave: + mutex_unlock(&isp->trace->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ipu_trace_init); + +void ipu_trace_uninit(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_device *isp = adev->isp; + struct ipu_trace *trace = isp->trace; + struct ipu_subsystem_trace_config *sys = adev->trace_cfg; + + if (!trace || !sys) + return; + + mutex_lock(&trace->lock); + + if (sys->memory.memory_buffer) + dma_free_attrs(sys->dev, + MEMORY_RING_BUFFER_SIZE + + MEMORY_RING_BUFFER_GUARD, + sys->memory.memory_buffer, + sys->memory.dma_handle, DMA_ATTR_NON_CONSISTENT); + + sys->dev = NULL; + sys->memory.memory_buffer = NULL; + + mutex_unlock(&trace->lock); +} +EXPORT_SYMBOL_GPL(ipu_trace_uninit); + +int ipu_trace_debugfs_add(struct ipu_device *isp, struct dentry *dir) +{ + struct dentry *files[3]; + int i = 0; + + files[i] = debugfs_create_file("traceconf", 0644, + dir, isp, &ipu_traceconf_fops); + if (!files[i]) + return -ENOMEM; + i++; + + files[i] = debugfs_create_file("getisystrace", 0444, + dir, + &isp->trace->isys, &ipu_gettrace_fops); + + if (!files[i]) + goto error; + i++; + + files[i] = debugfs_create_file("getpsystrace", 0444, + dir, + &isp->trace->psys, &ipu_gettrace_fops); + if (!files[i]) + goto error; + + return 0; + +error: + for (; i > 0; i--) + debugfs_remove(files[i - 1]); + return -ENOMEM; +} + +int ipu_trace_add(struct ipu_device *isp) +{ + isp->trace = devm_kzalloc(&isp->pdev->dev, + sizeof(struct ipu_trace), GFP_KERNEL); + if (!isp->trace) + return -ENOMEM; + + mutex_init(&isp->trace->lock); + + return 0; +} + +void ipu_trace_release(struct ipu_device *isp) +{ + if (!isp->trace) + return; + mutex_destroy(&isp->trace->lock); +} + +MODULE_AUTHOR("Samu Onkalo "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu trace support"); diff --git a/drivers/media/pci/intel/ipu-trace.h b/drivers/media/pci/intel/ipu-trace.h new file mode 100644 index 000000000000..9167c0400273 --- /dev/null +++ b/drivers/media/pci/intel/ipu-trace.h @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_TRACE_H +#define IPU_TRACE_H +#include + +#define TRACE_REG_MAX_BLOCK_SIZE 0x0fff + +#define TRACE_REG_END_MARK 0xffff + +#define TRACE_REG_CMD_TYPE_D64 0x0 +#define TRACE_REG_CMD_TYPE_D64M 0x1 +#define TRACE_REG_CMD_TYPE_D64TS 0x2 +#define TRACE_REG_CMD_TYPE_D64MTS 0x3 + +/* Trace unit register offsets */ +#define TRACE_REG_TUN_DDR_ENABLE 0x000 +#define TRACE_REG_TUN_NPK_ENABLE 0x004 +#define TRACE_REG_TUN_DDR_INFO_VAL 0x008 +#define TRACE_REG_TUN_NPK_ADDR 0x00C +#define TRACE_REG_TUN_DRAM_BASE_ADDR 0x010 +#define TRACE_REG_TUN_DRAM_END_ADDR 0x014 +#define TRACE_REG_TUN_LOCAL_TIMER0 0x018 +#define TRACE_REG_TUN_LOCAL_TIMER1 0x01C +#define TRACE_REG_TUN_WR_PTR 0x020 +#define TRACE_REG_TUN_RD_PTR 0x024 + +#define TRACE_REG_CREATE_TUN_REGISTER_LIST { \ + TRACE_REG_TUN_DDR_ENABLE, \ + TRACE_REG_TUN_NPK_ENABLE, \ + TRACE_REG_TUN_DDR_INFO_VAL, \ + TRACE_REG_TUN_NPK_ADDR, \ + TRACE_REG_END_MARK \ +} +/* + * Following registers are left out on purpose: + * TUN_LOCAL_TIMER0, TUN_LOCAL_TIMER1, TUN_DRAM_BASE_ADDR + * TUN_DRAM_END_ADDR, TUN_WR_PTR, TUN_RD_PTR + */ + +/* Trace monitor register offsets */ +#define TRACE_REG_TM_TRACE_ADDR_A 0x0900 +#define TRACE_REG_TM_TRACE_ADDR_B 0x0904 +#define TRACE_REG_TM_TRACE_ADDR_C 0x0908 +#define TRACE_REG_TM_TRACE_ADDR_D 0x090c +#define TRACE_REG_TM_TRACE_ENABLE_NPK 0x0910 +#define TRACE_REG_TM_TRACE_ENABLE_DDR 0x0914 +#define TRACE_REG_TM_TRACE_PER_PC 0x0918 +#define TRACE_REG_TM_TRACE_PER_BRANCH 0x091c +#define TRACE_REG_TM_TRACE_HEADER 0x0920 +#define TRACE_REG_TM_TRACE_CFG 0x0924 +#define TRACE_REG_TM_TRACE_LOST_PACKETS 0x0928 +#define TRACE_REG_TM_TRACE_LP_CLEAR 0x092c +#define TRACE_REG_TM_TRACE_LMRUN_MASK 0x0930 +#define TRACE_REG_TM_TRACE_LMRUN_PC_LOW 0x0934 +#define TRACE_REG_TM_TRACE_LMRUN_PC_HIGH 0x0938 +#define TRACE_REG_TM_TRACE_MMIO_SEL 0x093c +#define TRACE_REG_TM_TRACE_MMIO_WP0_LOW 0x0940 +#define TRACE_REG_TM_TRACE_MMIO_WP1_LOW 0x0944 +#define TRACE_REG_TM_TRACE_MMIO_WP2_LOW 0x0948 +#define TRACE_REG_TM_TRACE_MMIO_WP3_LOW 0x094c +#define TRACE_REG_TM_TRACE_MMIO_WP0_HIGH 0x0950 +#define TRACE_REG_TM_TRACE_MMIO_WP1_HIGH 0x0954 +#define TRACE_REG_TM_TRACE_MMIO_WP2_HIGH 0x0958 +#define TRACE_REG_TM_TRACE_MMIO_WP3_HIGH 0x095c +#define TRACE_REG_TM_FWTRACE_FIRST 0x0A00 +#define TRACE_REG_TM_FWTRACE_MIDDLE 0x0A04 +#define TRACE_REG_TM_FWTRACE_LAST 0x0A08 + +#define TRACE_REG_CREATE_TM_REGISTER_LIST { \ + TRACE_REG_TM_TRACE_ADDR_A, \ + TRACE_REG_TM_TRACE_ADDR_B, \ + TRACE_REG_TM_TRACE_ADDR_C, \ + TRACE_REG_TM_TRACE_ADDR_D, \ + TRACE_REG_TM_TRACE_ENABLE_NPK, \ + TRACE_REG_TM_TRACE_ENABLE_DDR, \ + TRACE_REG_TM_TRACE_PER_PC, \ + TRACE_REG_TM_TRACE_PER_BRANCH, \ + TRACE_REG_TM_TRACE_HEADER, \ + TRACE_REG_TM_TRACE_CFG, \ + TRACE_REG_TM_TRACE_LOST_PACKETS, \ + TRACE_REG_TM_TRACE_LP_CLEAR, \ + TRACE_REG_TM_TRACE_LMRUN_MASK, \ + TRACE_REG_TM_TRACE_LMRUN_PC_LOW, \ + TRACE_REG_TM_TRACE_LMRUN_PC_HIGH, \ + TRACE_REG_TM_TRACE_MMIO_SEL, \ + TRACE_REG_TM_TRACE_MMIO_WP0_LOW, \ + TRACE_REG_TM_TRACE_MMIO_WP1_LOW, \ + TRACE_REG_TM_TRACE_MMIO_WP2_LOW, \ + TRACE_REG_TM_TRACE_MMIO_WP3_LOW, \ + TRACE_REG_TM_TRACE_MMIO_WP0_HIGH, \ + TRACE_REG_TM_TRACE_MMIO_WP1_HIGH, \ + TRACE_REG_TM_TRACE_MMIO_WP2_HIGH, \ + TRACE_REG_TM_TRACE_MMIO_WP3_HIGH, \ + TRACE_REG_END_MARK \ +} + +/* + * Following exists only in (I)SP address space: + * TM_FWTRACE_FIRST, TM_FWTRACE_MIDDLE, TM_FWTRACE_LAST + */ + +#define TRACE_REG_GPC_RESET 0x000 +#define TRACE_REG_GPC_OVERALL_ENABLE 0x004 +#define TRACE_REG_GPC_TRACE_HEADER 0x008 +#define TRACE_REG_GPC_TRACE_ADDRESS 0x00C +#define TRACE_REG_GPC_TRACE_NPK_EN 0x010 +#define TRACE_REG_GPC_TRACE_DDR_EN 0x014 +#define TRACE_REG_GPC_TRACE_LPKT_CLEAR 0x018 +#define TRACE_REG_GPC_TRACE_LPKT 0x01C + +#define TRACE_REG_GPC_ENABLE_ID0 0x020 +#define TRACE_REG_GPC_ENABLE_ID1 0x024 +#define TRACE_REG_GPC_ENABLE_ID2 0x028 +#define TRACE_REG_GPC_ENABLE_ID3 0x02c + +#define TRACE_REG_GPC_VALUE_ID0 0x030 +#define TRACE_REG_GPC_VALUE_ID1 0x034 +#define TRACE_REG_GPC_VALUE_ID2 0x038 +#define TRACE_REG_GPC_VALUE_ID3 0x03c + +#define TRACE_REG_GPC_CNT_INPUT_SELECT_ID0 0x040 +#define TRACE_REG_GPC_CNT_INPUT_SELECT_ID1 0x044 +#define TRACE_REG_GPC_CNT_INPUT_SELECT_ID2 0x048 +#define TRACE_REG_GPC_CNT_INPUT_SELECT_ID3 0x04c + +#define TRACE_REG_GPC_CNT_START_SELECT_ID0 0x050 +#define TRACE_REG_GPC_CNT_START_SELECT_ID1 0x054 +#define TRACE_REG_GPC_CNT_START_SELECT_ID2 0x058 +#define TRACE_REG_GPC_CNT_START_SELECT_ID3 0x05c + +#define TRACE_REG_GPC_CNT_STOP_SELECT_ID0 0x060 +#define TRACE_REG_GPC_CNT_STOP_SELECT_ID1 0x064 +#define TRACE_REG_GPC_CNT_STOP_SELECT_ID2 0x068 +#define TRACE_REG_GPC_CNT_STOP_SELECT_ID3 0x06c + +#define TRACE_REG_GPC_CNT_MSG_SELECT_ID0 0x070 +#define TRACE_REG_GPC_CNT_MSG_SELECT_ID1 0x074 +#define TRACE_REG_GPC_CNT_MSG_SELECT_ID2 0x078 +#define TRACE_REG_GPC_CNT_MSG_SELECT_ID3 0x07c + +#define TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID0 0x080 +#define TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID1 0x084 +#define TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID2 0x088 +#define TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID3 0x08c + +#define TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID0 0x090 +#define TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID1 0x094 +#define TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID2 0x098 +#define TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID3 0x09c + +#define TRACE_REG_GPC_IRQ_TIMER_SELECT_ID0 0x0a0 +#define TRACE_REG_GPC_IRQ_TIMER_SELECT_ID1 0x0a4 +#define TRACE_REG_GPC_IRQ_TIMER_SELECT_ID2 0x0a8 +#define TRACE_REG_GPC_IRQ_TIMER_SELECT_ID3 0x0ac + +#define TRACE_REG_GPC_IRQ_ENABLE_ID0 0x0b0 +#define TRACE_REG_GPC_IRQ_ENABLE_ID1 0x0b4 +#define TRACE_REG_GPC_IRQ_ENABLE_ID2 0x0b8 +#define TRACE_REG_GPC_IRQ_ENABLE_ID3 0x0bc + +#define TRACE_REG_CREATE_GPC_REGISTER_LIST { \ + TRACE_REG_GPC_RESET, \ + TRACE_REG_GPC_OVERALL_ENABLE, \ + TRACE_REG_GPC_TRACE_HEADER, \ + TRACE_REG_GPC_TRACE_ADDRESS, \ + TRACE_REG_GPC_TRACE_NPK_EN, \ + TRACE_REG_GPC_TRACE_DDR_EN, \ + TRACE_REG_GPC_TRACE_LPKT_CLEAR, \ + TRACE_REG_GPC_TRACE_LPKT, \ + TRACE_REG_GPC_ENABLE_ID0, \ + TRACE_REG_GPC_ENABLE_ID1, \ + TRACE_REG_GPC_ENABLE_ID2, \ + TRACE_REG_GPC_ENABLE_ID3, \ + TRACE_REG_GPC_VALUE_ID0, \ + TRACE_REG_GPC_VALUE_ID1, \ + TRACE_REG_GPC_VALUE_ID2, \ + TRACE_REG_GPC_VALUE_ID3, \ + TRACE_REG_GPC_CNT_INPUT_SELECT_ID0, \ + TRACE_REG_GPC_CNT_INPUT_SELECT_ID1, \ + TRACE_REG_GPC_CNT_INPUT_SELECT_ID2, \ + TRACE_REG_GPC_CNT_INPUT_SELECT_ID3, \ + TRACE_REG_GPC_CNT_START_SELECT_ID0, \ + TRACE_REG_GPC_CNT_START_SELECT_ID1, \ + TRACE_REG_GPC_CNT_START_SELECT_ID2, \ + TRACE_REG_GPC_CNT_START_SELECT_ID3, \ + TRACE_REG_GPC_CNT_STOP_SELECT_ID0, \ + TRACE_REG_GPC_CNT_STOP_SELECT_ID1, \ + TRACE_REG_GPC_CNT_STOP_SELECT_ID2, \ + TRACE_REG_GPC_CNT_STOP_SELECT_ID3, \ + TRACE_REG_GPC_CNT_MSG_SELECT_ID0, \ + TRACE_REG_GPC_CNT_MSG_SELECT_ID1, \ + TRACE_REG_GPC_CNT_MSG_SELECT_ID2, \ + TRACE_REG_GPC_CNT_MSG_SELECT_ID3, \ + TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID0, \ + TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID1, \ + TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID2, \ + TRACE_REG_GPC_CNT_MSG_PLOAD_SELECT_ID3, \ + TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID0, \ + TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID1, \ + TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID2, \ + TRACE_REG_GPC_IRQ_TRIGGER_VALUE_ID3, \ + TRACE_REG_GPC_IRQ_TIMER_SELECT_ID0, \ + TRACE_REG_GPC_IRQ_TIMER_SELECT_ID1, \ + TRACE_REG_GPC_IRQ_TIMER_SELECT_ID2, \ + TRACE_REG_GPC_IRQ_TIMER_SELECT_ID3, \ + TRACE_REG_GPC_IRQ_ENABLE_ID0, \ + TRACE_REG_GPC_IRQ_ENABLE_ID1, \ + TRACE_REG_GPC_IRQ_ENABLE_ID2, \ + TRACE_REG_GPC_IRQ_ENABLE_ID3, \ + TRACE_REG_END_MARK \ +} + +/* CSI2 legacy receiver trace registers */ +#define TRACE_REG_CSI2_TM_RESET_REG_IDX 0x0000 +#define TRACE_REG_CSI2_TM_OVERALL_ENABLE_REG_IDX 0x0004 +#define TRACE_REG_CSI2_TM_TRACE_HEADER_REG_IDX 0x0008 +#define TRACE_REG_CSI2_TM_TRACE_ADDRESS_REG_IDX 0x000c +#define TRACE_REG_CSI2_TM_TRACE_HEADER_VAL 0xf +#define TRACE_REG_CSI2_TM_TRACE_ADDRESS_VAL 0x100218 +#define TRACE_REG_CSI2_TM_MONITOR_ID 0x8 + +/* 0 <= n <= 3 */ +#define TRACE_REG_CSI2_TM_TRACE_NPK_EN_REG_IDX_P(n) (0x0010 + (n) * 4) +#define TRACE_REG_CSI2_TM_TRACE_DDR_EN_REG_IDX_P(n) (0x0020 + (n) * 4) +#define TRACE_CSI2_TM_EVENT_FE(vc) (BIT(0) << (vc * 6)) +#define TRACE_CSI2_TM_EVENT_FS(vc) (BIT(1) << (vc * 6)) +#define TRACE_CSI2_TM_EVENT_PE(vc) (BIT(2) << (vc * 6)) +#define TRACE_CSI2_TM_EVENT_PS(vc) (BIT(3) << (vc * 6)) +#define TRACE_CSI2_TM_EVENT_LE(vc) (BIT(4) << (vc * 6)) +#define TRACE_CSI2_TM_EVENT_LS(vc) (BIT(5) << (vc * 6)) + +#define TRACE_REG_CSI2_TM_TRACE_LPKT_CLEAR_REG_IDX 0x0030 +#define TRACE_REG_CSI2_TM_TRACE_LPKT_REG_IDX 0x0034 + +/* 0 <= n <= 7 */ +#define TRACE_REG_CSI2_TM_ENABLE_REG_IDn(n) (0x0038 + (n) * 4) +#define TRACE_REG_CSI2_TM_VALUE_REG_IDn(n) (0x0058 + (n) * 4) +#define TRACE_REG_CSI2_TM_CNT_INPUT_SELECT_REG_IDn(n) (0x0078 + (n) * 4) +#define TRACE_REG_CSI2_TM_CNT_START_SELECT_REG_IDn(n) (0x0098 + (n) * 4) +#define TRACE_REG_CSI2_TM_CNT_STOP_SELECT_REG_IDn(n) (0x00b8 + (n) * 4) +#define TRACE_REG_CSI2_TM_IRQ_TRIGGER_VALUE_REG_IDn(n) (0x00d8 + (n) * 4) +#define TRACE_REG_CSI2_TM_IRQ_TIMER_SELECT_REG_IDn(n) (0x00f8 + (n) * 4) +#define TRACE_REG_CSI2_TM_IRQ_ENABLE_REG_IDn(n) (0x0118 + (n) * 4) + +/* CSI2_3PH combo receiver trace registers */ +#define TRACE_REG_CSI2_3PH_TM_RESET_REG_IDX 0x0000 +#define TRACE_REG_CSI2_3PH_TM_OVERALL_ENABLE_REG_IDX 0x0004 +#define TRACE_REG_CSI2_3PH_TM_TRACE_HEADER_REG_IDX 0x0008 +#define TRACE_REG_CSI2_3PH_TM_TRACE_ADDRESS_REG_IDX 0x000c +#define TRACE_REG_CSI2_3PH_TM_TRACE_ADDRESS_VAL 0x100258 +#define TRACE_REG_CSI2_3PH_TM_MONITOR_ID 0x9 + +/* 0 <= n <= 5 */ +#define TRACE_REG_CSI2_3PH_TM_TRACE_NPK_EN_REG_IDX_P(n) (0x0010 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_TRACE_DDR_EN_REG_IDX_P(n) (0x0028 + (n) * 4) + +#define TRACE_REG_CSI2_3PH_TM_TRACE_LPKT_CLEAR_REG_IDX 0x0040 +#define TRACE_REG_CSI2_3PH_TM_TRACE_LPKT_REG_IDX 0x0044 + +/* 0 <= n <= 7 */ +#define TRACE_REG_CSI2_3PH_TM_ENABLE_REG_IDn(n) (0x0048 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_VALUE_REG_IDn(n) (0x0068 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_CNT_INPUT_SELECT_REG_IDn(n) (0x0088 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_CNT_START_SELECT_REG_IDn(n) (0x00a8 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_CNT_STOP_SELECT_REG_IDn(n) (0x00c8 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_IRQ_TRIGGER_VALUE_REG_IDn(n) (0x00e8 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_IRQ_TIMER_SELECT_REG_IDn(n) (0x0108 + (n) * 4) +#define TRACE_REG_CSI2_3PH_TM_IRQ_ENABLE_REG_IDn(n) (0x0128 + (n) * 4) + +/* SIG2CIO trace monitors */ +#define TRACE_REG_SIG2CIO_ADDRESS 0x0000 +#define TRACE_REG_SIG2CIO_WDATA 0x0004 +#define TRACE_REG_SIG2CIO_MASK 0x0008 +#define TRACE_REG_SIG2CIO_GROUP_CFG 0x000c +#define TRACE_REG_SIG2CIO_STICKY 0x0010 +#define TRACE_REG_SIG2CIO_RST_STICKY 0x0014 +#define TRACE_REG_SIG2CIO_MANUAL_RST_STICKY 0x0018 +#define TRACE_REG_SIG2CIO_STATUS 0x001c +/* Size of on SIG2CIO block */ +#define TRACE_REG_SIG2CIO_SIZE_OF 0x0020 + +struct ipu_trace; +struct ipu_subsystem_trace_config; + +enum ipu_trace_block_type { + IPU_TRACE_BLOCK_TUN = 0, /* Trace unit */ + IPU_TRACE_BLOCK_TM, /* Trace monitor */ + IPU_TRACE_BLOCK_GPC, /* General purpose control */ + IPU_TRACE_CSI2, /* CSI2 legacy receiver */ + IPU_TRACE_CSI2_3PH, /* CSI2 combo receiver */ + IPU_TRACE_SIG2CIOS, + IPU_TRACE_TIMER_RST, /* Trace reset control timer */ + IPU_TRACE_BLOCK_END /* End of list */ +}; + +struct ipu_trace_block { + u32 offset; /* Offset to block inside subsystem */ + enum ipu_trace_block_type type; +}; + +int ipu_trace_add(struct ipu_device *isp); +int ipu_trace_debugfs_add(struct ipu_device *isp, struct dentry *dir); +void ipu_trace_release(struct ipu_device *isp); +int ipu_trace_init(struct ipu_device *isp, void __iomem *base, + struct device *dev, struct ipu_trace_block *blocks); +void ipu_trace_restore(struct device *dev); +void ipu_trace_uninit(struct device *dev); +void ipu_trace_stop(struct device *dev); +int ipu_trace_get_timer(struct device *dev, u64 *timer); +#endif diff --git a/drivers/media/pci/intel/ipu-wrapper.c b/drivers/media/pci/intel/ipu-wrapper.c new file mode 100644 index 000000000000..9e06887dd857 --- /dev/null +++ b/drivers/media/pci/intel/ipu-wrapper.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include + +#include +#include +#include +#include +#include + +#include "ipu-bus.h" +#include "ipu-dma.h" +#include "ipu-mmu.h" +#include "ipu-wrapper.h" +#include "vied_subsystem_access.h" +#include "vied_subsystem_access_initialization.h" +#include "shared_memory_map.h" +#include "shared_memory_access.h" + +struct wrapper_base { + void __iomem *sys_base; + const struct dma_map_ops *ops; + /* Protect shared memory buffers */ + spinlock_t lock; + struct list_head buffers; + u32 css_map_done; + struct device *dev; +}; + +static struct wrapper_base isys; +static struct wrapper_base psys; + +struct my_css_memory_buffer_item { + struct list_head list; + dma_addr_t iova; + unsigned long *addr; + size_t bytes; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif +}; + +static struct wrapper_base *get_mem_sub_system(int mmid) +{ + if (mmid == ISYS_MMID) + return &isys; + + if (mmid == PSYS_MMID) + return &psys; + WARN(1, "Invalid mem subsystem"); + return NULL; +} + +static struct wrapper_base *get_sub_system(int ssid) +{ + if (ssid == ISYS_SSID) + return &isys; + + if (ssid == PSYS_SSID) + return &psys; + WARN(1, "Invalid subsystem"); + return NULL; +} + +/* + * Subsystem access functions to access IUNIT MMIO space + */ +static void *host_addr(int ssid, u32 addr) +{ + if (ssid == ISYS_SSID) + return isys.sys_base + addr; + else if (ssid == PSYS_SSID) + return psys.sys_base + addr; + /* + * Calling WARN_ON is a bit brutal but better to capture wrong register + * accesses immediately. We have no way to return an error here. + */ + WARN_ON(1); + + return NULL; +} + +void vied_subsystem_store_32(unsigned int ssid, u32 addr, u32 data) +{ + writel(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store_16(unsigned int ssid, u32 addr, u16 data) +{ + writew(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store_8(unsigned int ssid, u32 addr, u8 data) +{ + writeb(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store(unsigned int ssid, + u32 addr, const void *data, unsigned int size) +{ + void *dst = host_addr(ssid, addr); + + dev_dbg(get_sub_system(ssid)->dev, "access: %s 0x%x size: %d\n", + __func__, addr, size); + + for (; size >= sizeof(u32); size -= sizeof(u32), + dst += sizeof(u32), data += sizeof(u32)) { + writel(*(u32 *) data, dst); + } + if (size >= sizeof(u16)) { + writew(*(u16 *) data, dst); + size -= sizeof(u16), dst += sizeof(u16), data += sizeof(u16); + } + if (size) + writeb(*(u8 *) data, dst); +} + +u32 vied_subsystem_load_32(unsigned int ssid, u32 addr) +{ + return readl(host_addr(ssid, addr)); +} + +u16 vied_subsystem_load_16(unsigned int ssid, u32 addr) +{ + return readw(host_addr(ssid, addr)); +} + +u8 vied_subsystem_load_8(unsigned int ssid, u32 addr) +{ + return readb(host_addr(ssid, addr)); +} + +void vied_subsystem_load(unsigned int ssid, u32 addr, + void *data, unsigned int size) +{ + void *src = host_addr(ssid, addr); + + dev_dbg(get_sub_system(ssid)->dev, "access: %s 0x%x size: %d\n", + __func__, addr, size); + + for (; size >= sizeof(u32); size -= sizeof(u32), + src += sizeof(u32), data += sizeof(u32)) + *(u32 *) data = readl(src); + if (size >= sizeof(u16)) { + *(u16 *) data = readw(src); + size -= sizeof(u16), src += sizeof(u16), data += sizeof(u16); + } + if (size) + *(u8 *) data = readb(src); +} + +/* + * Initialize base address for subsystem + */ +void vied_subsystem_access_initialize(unsigned int system) +{ +} + +/* + * Shared memory access codes written by Dash Biswait, + * copied from FPGA environment + */ + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param mmid: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(unsigned int mmid, u64 host_ddr_addr, + size_t memory_size, size_t ps) +{ + return 0; +} + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(unsigned int mmid) +{ +} + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param ssid: id of subsystem + * \param mmid: id of ddr memory + * \param mmu_ps: size of page in bits + * \param mmu_pnrs: page numbers + * \param ddr_addr: base address + * \param inv_tlb: invalidate tbl + * \param sbt: set l1 base address + */ +int shared_memory_map_initialize(unsigned int ssid, unsigned int mmid, + size_t mmu_ps, size_t mmu_pnrs, u64 ddr_addr, + shared_memory_invalidate_mmu_tlb inv_tlb, + shared_memory_set_page_table_base_address sbt) +{ + return 0; +} + +/** + * \brief De-initialize the shared memory interface administration on the host. + */ +void shared_memory_map_uninitialize(unsigned int ssid, unsigned int mmid) +{ +} + +static u8 alloc_cookie; + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. + * \Returns NULL when insufficient memory available + */ +u64 shared_memory_alloc(unsigned int mmid, size_t bytes) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf; + unsigned long flags; + + dev_dbg(mine->dev, "%s: in, size: %zu\n", __func__, bytes); + + if (!bytes) + return (unsigned long)&alloc_cookie; + + might_sleep(); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return 0; + + /*alloc using ipu dma driver */ + buf->bytes = PAGE_ALIGN(bytes); + + buf->addr = dma_alloc_attrs(mine->dev, buf->bytes, &buf->iova, + GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + if (!buf->addr) { + kfree(buf); + return 0; + } + + spin_lock_irqsave(&mine->lock, flags); + list_add(&buf->list, &mine->buffers); + spin_unlock_irqrestore(&mine->lock, flags); + + return (unsigned long)buf->addr; +} + +/** + * \brief Free (DDR) shared memory space. + */ +void shared_memory_free(unsigned int mmid, u64 addr) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf = NULL; + unsigned long flags; + + if ((void *)(unsigned long)addr == &alloc_cookie) + return; + + might_sleep(); + + dev_dbg(mine->dev, "looking for iova %8.8llx\n", addr); + + spin_lock_irqsave(&mine->lock, flags); + list_for_each_entry(buf, &mine->buffers, list) { + dev_dbg(mine->dev, "buffer addr %8.8lx\n", (long)buf->addr); + if ((long)buf->addr != addr) + continue; + + dev_dbg(mine->dev, "found it!\n"); + list_del(&buf->list); + spin_unlock_irqrestore(&mine->lock, flags); + dma_free_attrs(mine->dev, buf->bytes, buf->addr, buf->iova, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &buf->attrs +#else + buf->attrs +#endif + ); + kfree(buf); + return; + } + dev_warn(mine->dev, "Can't find mem object %8.8llx\n", addr); + spin_unlock_irqrestore(&mine->lock, flags); +} + +/** + * \brief Convert a host virtual address to a CSS virtual address and + * \update the MMU. + */ +u32 shared_memory_map(unsigned int ssid, unsigned int mmid, u64 addr) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf = NULL; + unsigned long flags; + + if ((void *)(unsigned long)addr == &alloc_cookie) + return 0; + + spin_lock_irqsave(&mine->lock, flags); + list_for_each_entry(buf, &mine->buffers, list) { + dev_dbg(mine->dev, "%s %8.8lx\n", __func__, (long)buf->addr); + if ((long)buf->addr != addr) + continue; + + dev_dbg(mine->dev, "mapped!!\n"); + spin_unlock_irqrestore(&mine->lock, flags); + return buf->iova; + } + dev_err(mine->dev, "Can't find mapped object %8.8llx\n", addr); + spin_unlock_irqrestore(&mine->lock, flags); + return 0; +} + +/** + * \brief Free a CSS virtual address and update the MMU. + */ +void shared_memory_unmap(unsigned int ssid, unsigned int mmid, u32 addr) +{ +} + +/** + * \brief Store a byte into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_8(unsigned int mmid, u64 addr, u8 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u8 *)(unsigned long) addr) = data; + /*Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u8)); +} + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_16(unsigned int mmid, u64 addr, u16 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u16 *)(unsigned long) addr) = data; + /*Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, sizeof(u16)); +} + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_32(unsigned int mmid, u64 addr, u32 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u32 *)(unsigned long) addr) = data; + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, sizeof(u32)); +} + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store(unsigned int mmid, u64 addr, const void *data, + size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%lx bytes = 0x%zx\n", __func__, + (unsigned long)addr, bytes); + + if (!data) { + dev_err(get_mem_sub_system(mmid)->dev, + "%s: data ptr is null\n", __func__); + } else { + const u8 *pdata = data; + u8 *paddr = (u8 *)(unsigned long)addr; + size_t i = 0; + + for (; i < bytes; ++i) + *paddr++ = *pdata++; + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, bytes); + } +} + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host + * \virtual address + */ +void shared_memory_zero(unsigned int mmid, u64 addr, size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%zx\n", + __func__, (unsigned long long)addr, bytes); + + memset((void *)(unsigned long)addr, 0, bytes); + clflush_cache_range((void *)(unsigned long)addr, bytes); +} + +/** + * \brief Load a byte from (DDR) shared memory space using a host + * \virtual address + */ +u8 shared_memory_load_8(unsigned int mmid, u64 addr) +{ + u8 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u8)); + data = *(u8 *)(unsigned long) addr; + return data; +} + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host + * \virtual address + */ +u16 shared_memory_load_16(unsigned int mmid, u64 addr) +{ + u16 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u16)); + data = *(u16 *)(unsigned long)addr; + return data; +} + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host + * \virtual address + */ +u32 shared_memory_load_32(unsigned int mmid, u64 addr) +{ + u32 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u32)); + data = *(u32 *)(unsigned long)addr; + return data; +} + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_load(unsigned int mmid, u64 addr, void *data, size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%lx bytes = 0x%zx\n", __func__, + (unsigned long)addr, bytes); + + if (!data) { + dev_err(get_mem_sub_system(mmid)->dev, + "%s: data ptr is null\n", __func__); + + } else { + u8 *pdata = data; + u8 *paddr = (u8 *)(unsigned long)addr; + size_t i = 0; + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, bytes); + for (; i < bytes; ++i) + *pdata++ = *paddr++; + } +} + +static int init_wrapper(struct wrapper_base *sys) +{ + INIT_LIST_HEAD(&sys->buffers); + spin_lock_init(&sys->lock); + return 0; +} + +/* + * Wrapper driver set base address for library use + */ +void ipu_wrapper_init(int mmid, struct device *dev, void __iomem *base) +{ + struct wrapper_base *sys = get_mem_sub_system(mmid); + + init_wrapper(sys); + sys->dev = dev; + sys->sys_base = base; +} diff --git a/drivers/media/pci/intel/ipu-wrapper.h b/drivers/media/pci/intel/ipu-wrapper.h new file mode 100644 index 000000000000..b7df285e1142 --- /dev/null +++ b/drivers/media/pci/intel/ipu-wrapper.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_WRAPPER_H +#define IPU_WRAPPER_H + +#define ISYS_SSID 1 +#define PSYS_SSID 0 + +#define ISYS_MMID 1 +#define PSYS_MMID 0 + +struct device; + +void ipu_wrapper_init(int mmid, struct device *dev, void __iomem *base); + +#endif /* IPU_WRAPPER_H */ diff --git a/drivers/media/pci/intel/ipu.c b/drivers/media/pci/intel/ipu.c new file mode 100644 index 000000000000..0172c1982c45 --- /dev/null +++ b/drivers/media/pci/intel/ipu.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-buttress.h" +#include "ipu-platform.h" +#include "ipu-platform-buttress-regs.h" +#include "ipu-cpd.h" +#include "ipu-pdata.h" +#include "ipu-bus.h" +#include "ipu-mmu.h" +#include "ipu-platform-regs.h" +#include "ipu-platform-isys-csi2-reg.h" +#include "ipu-trace.h" + +#define IPU_PCI_BAR 0 + +static struct ipu_bus_device *ipu_mmu_init(struct pci_dev *pdev, + struct device *parent, + struct ipu_buttress_ctrl *ctrl, + void __iomem *base, + const struct ipu_hw_variants *hw, + unsigned int nr, int mmid) +{ + struct ipu_mmu_pdata *pdata = + devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + unsigned int i; + + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (hw->nr_mmus > IPU_MMU_MAX_DEVICES) + return ERR_PTR(-EINVAL); + + for (i = 0; i < hw->nr_mmus; i++) { + struct ipu_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; + const struct ipu_mmu_hw *src_mmu = &hw->mmu_hw[i]; + + if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || + src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) + return ERR_PTR(-EINVAL); + + *pdata_mmu = *src_mmu; + pdata_mmu->base = base + src_mmu->offset; + } + + pdata->nr_mmus = hw->nr_mmus; + pdata->mmid = mmid; + + return ipu_bus_add_device(pdev, parent, pdata, NULL, ctrl, + IPU_MMU_NAME, nr); +} + +static struct ipu_bus_device *ipu_isys_init(struct pci_dev *pdev, + struct device *parent, + struct device *iommu, + void __iomem *base, + const struct ipu_isys_internal_pdata + *ipdata, + struct ipu_isys_subdev_pdata + *spdata, unsigned int nr) +{ + struct ipu_isys_pdata *pdata = + devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = base; + pdata->ipdata = ipdata; + pdata->spdata = spdata; + + return ipu_bus_add_device(pdev, parent, pdata, iommu, NULL, + IPU_ISYS_NAME, nr); +} + +static struct ipu_bus_device *ipu_psys_init(struct pci_dev *pdev, + struct device *parent, + struct device *iommu, + void __iomem *base, + const struct ipu_psys_internal_pdata + *ipdata, unsigned int nr) +{ + struct ipu_psys_pdata *pdata = + devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = base; + pdata->ipdata = ipdata; + return ipu_bus_add_device(pdev, parent, pdata, iommu, NULL, + IPU_PSYS_NAME, nr); +} + +int ipu_fw_authenticate(void *data, u64 val) +{ + struct ipu_device *isp = data; + int ret; + + if (!isp->secure_mode) + return -EINVAL; + + ret = ipu_buttress_reset_authentication(isp); + if (ret) { + dev_err(&isp->pdev->dev, "Failed to reset authentication!\n"); + return ret; + } + + return ipu_buttress_authenticate(isp); +} +EXPORT_SYMBOL(ipu_fw_authenticate); +DEFINE_SIMPLE_ATTRIBUTE(authenticate_fops, NULL, ipu_fw_authenticate, "%llu\n"); + +#ifdef CONFIG_DEBUG_FS +static int resume_ipu_bus_device(struct ipu_bus_device *adev) +{ + struct device *dev = &adev->dev; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->resume) + return -EIO; + + return pm->resume(dev); +} + +static int suspend_ipu_bus_device(struct ipu_bus_device *adev) +{ + struct device *dev = &adev->dev; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->suspend) + return -EIO; + + return pm->suspend(dev); +} + +static int force_suspend_get(void *data, u64 *val) +{ + struct ipu_device *isp = data; + struct ipu_buttress *b = &isp->buttress; + + *val = b->force_suspend; + return 0; +} + +static int force_suspend_set(void *data, u64 val) +{ + struct ipu_device *isp = data; + struct ipu_buttress *b = &isp->buttress; + int ret = 0; + + if (val == b->force_suspend) + return 0; + + if (val) { + b->force_suspend = 1; + ret = suspend_ipu_bus_device(isp->psys_iommu); + if (ret) { + dev_err(&isp->pdev->dev, "Failed to suspend psys\n"); + return ret; + } + ret = suspend_ipu_bus_device(isp->isys_iommu); + if (ret) { + dev_err(&isp->pdev->dev, "Failed to suspend isys\n"); + return ret; + } + ret = pci_set_power_state(isp->pdev, PCI_D3hot); + if (ret) { + dev_err(&isp->pdev->dev, + "Failed to suspend IUnit PCI device\n"); + return ret; + } + } else { + ret = pci_set_power_state(isp->pdev, PCI_D0); + if (ret) { + dev_err(&isp->pdev->dev, + "Failed to suspend IUnit PCI device\n"); + return ret; + } + ret = resume_ipu_bus_device(isp->isys_iommu); + if (ret) { + dev_err(&isp->pdev->dev, "Failed to resume isys\n"); + return ret; + } + ret = resume_ipu_bus_device(isp->psys_iommu); + if (ret) { + dev_err(&isp->pdev->dev, "Failed to resume psys\n"); + return ret; + } + b->force_suspend = 0; + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(force_suspend_fops, force_suspend_get, + force_suspend_set, "%llu\n"); +/* + * The sysfs interface for reloading cpd fw is there only for debug purpose, + * and it must not be used when either isys or psys is in use. + */ +static int cpd_fw_reload(void *data, u64 val) +{ + struct ipu_device *isp = data; + int rval = -EINVAL; + + if (isp->cpd_fw_reload) + rval = isp->cpd_fw_reload(isp); + if (!rval && isp->isys_fw_reload) + rval = isp->isys_fw_reload(isp); + + return rval; +} + +DEFINE_SIMPLE_ATTRIBUTE(cpd_fw_fops, NULL, cpd_fw_reload, "%llu\n"); + +#endif /* CONFIG_DEBUG_FS */ + +static int ipu_init_debugfs(struct ipu_device *isp) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *file; + struct dentry *dir; + + dir = debugfs_create_dir(pci_name(isp->pdev), NULL); + if (!dir) + return -ENOMEM; + + file = debugfs_create_file("force_suspend", 0700, dir, isp, + &force_suspend_fops); + if (!file) + goto err; + file = debugfs_create_file("authenticate", 0700, dir, isp, + &authenticate_fops); + if (!file) + goto err; + + file = debugfs_create_file("cpd_fw_reload", 0700, dir, isp, + &cpd_fw_fops); + if (!file) + goto err; + + if (ipu_trace_debugfs_add(isp, dir)) + goto err; + + isp->ipu_dir = dir; + + if (ipu_buttress_debugfs_init(isp)) + goto err; + + return 0; +err: + debugfs_remove_recursive(dir); + return -ENOMEM; +#else + return 0; +#endif /* CONFIG_DEBUG_FS */ +} + +static void ipu_remove_debugfs(struct ipu_device *isp) +{ + /* + * Since isys and psys debugfs dir will be created under ipu root dir, + * mark its dentry to NULL to avoid duplicate removal. + */ + debugfs_remove_recursive(isp->ipu_dir); + isp->ipu_dir = NULL; +} + +static int ipu_pci_config_setup(struct pci_dev *dev) +{ + u16 pci_command; + int rval = pci_enable_msi(dev); + + if (rval) { + dev_err(&dev->dev, "Failed to enable msi (%d)\n", rval); + return rval; + } + + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(dev, PCI_COMMAND, pci_command); + + return 0; +} + +static void ipu_configure_vc_mechanism(struct ipu_device *isp) +{ + u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL); + + if (IPU_BTRS_ARB_STALL_MODE_VC0 == IPU_BTRS_ARB_MODE_TYPE_STALL) + val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; + else + val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0; + + if (IPU_BTRS_ARB_STALL_MODE_VC1 == IPU_BTRS_ARB_MODE_TYPE_STALL) + val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; + else + val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1; + + writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL); +} + +int request_cpd_fw(const struct firmware **firmware_p, const char *name, + struct device *device) +{ + const struct firmware *fw; + struct firmware *tmp; + int ret; + + ret = request_firmware(&fw, name, device); + if (ret) + return ret; + + if (is_vmalloc_addr(fw->data)) { + *firmware_p = fw; + } else { + tmp = devm_kzalloc(device, sizeof(struct firmware), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + tmp->size = fw->size; + tmp->data = devm_kzalloc(device, fw->size, GFP_KERNEL); + memcpy((void *)tmp->data, fw->data, fw->size); + *firmware_p = tmp; + release_firmware(fw); + } + + return 0; +} +EXPORT_SYMBOL(request_cpd_fw); + +static int ipu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct ipu_device *isp; + phys_addr_t phys; + void __iomem *const *iomap; + void __iomem *isys_base = NULL; + void __iomem *psys_base = NULL; + struct ipu_buttress_ctrl *isys_ctrl, *psys_ctrl; + unsigned int dma_mask = IPU_DMA_MASK; + int rval; + + trace_printk("B|%d|TMWK\n", current->pid); + + isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->pdev = pdev; + INIT_LIST_HEAD(&isp->devices); + + rval = pcim_enable_device(pdev); + if (rval) { + dev_err(&pdev->dev, "Failed to enable CI ISP device (%d)\n", + rval); + trace_printk("E|TMWK\n"); + return rval; + } + + dev_info(&pdev->dev, "Device 0x%x (rev: 0x%x)\n", + pdev->device, pdev->revision); + + phys = pci_resource_start(pdev, IPU_PCI_BAR); + + rval = pcim_iomap_regions(pdev, + 1 << IPU_PCI_BAR, + pci_name(pdev)); + if (rval) { + dev_err(&pdev->dev, "Failed to I/O memory remapping (%d)\n", + rval); + trace_printk("E|TMWK\n"); + return rval; + } + dev_info(&pdev->dev, "physical base address 0x%llx\n", phys); + + iomap = pcim_iomap_table(pdev); + if (!iomap) { + dev_err(&pdev->dev, "Failed to iomap table (%d)\n", rval); + trace_printk("E|TMWK\n"); + return -ENODEV; + } + + isp->base = iomap[IPU_PCI_BAR]; + dev_info(&pdev->dev, "mapped as: 0x%p\n", isp->base); + + pci_set_drvdata(pdev, isp); + pci_set_master(pdev); + + isp->cpd_fw_name = IPU_CPD_FIRMWARE_NAME; + + isys_base = isp->base + isys_ipdata.hw_variant.offset; + psys_base = isp->base + psys_ipdata.hw_variant.offset; + + rval = pci_set_dma_mask(pdev, DMA_BIT_MASK(dma_mask)); + if (!rval) + rval = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(dma_mask)); + if (rval) { + dev_err(&pdev->dev, "Failed to set DMA mask (%d)\n", rval); + trace_printk("E|TMWK\n"); + return rval; + } + + rval = ipu_pci_config_setup(pdev); + if (rval) { + trace_printk("E|TMWK\n"); + return rval; + } + + rval = devm_request_threaded_irq(&pdev->dev, pdev->irq, + ipu_buttress_isr, + ipu_buttress_isr_threaded, + IRQF_SHARED, IPU_NAME, isp); + if (rval) { + dev_err(&pdev->dev, "Requesting irq failed(%d)\n", rval); + trace_printk("E|TMWK\n"); + return rval; + } + + rval = ipu_buttress_init(isp); + if (rval) { + trace_printk("E|TMWK\n"); + return rval; + } + + dev_info(&pdev->dev, "cpd file name: %s\n", isp->cpd_fw_name); + + rval = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, &pdev->dev); + if (rval) { + dev_err(&isp->pdev->dev, "Requesting signed firmware failed\n"); + trace_printk("E|TMWK\n"); + return rval; + } + + rval = ipu_cpd_validate_cpd_file(isp, isp->cpd_fw->data, + isp->cpd_fw->size); + if (rval) { + dev_err(&isp->pdev->dev, "Failed to validate cpd\n"); + goto out_ipu_bus_del_devices; + } + + rval = ipu_trace_add(isp); + if (rval) + dev_err(&pdev->dev, "Trace support not available\n"); + + /* + * NOTE Device hierarchy below is important to ensure proper + * runtime suspend and resume order. + * Also registration order is important to ensure proper + * suspend and resume order during system + * suspend. Registration order is as follows: + * isys_iommu->isys->psys_iommu->psys + */ + isys_ctrl = devm_kzalloc(&pdev->dev, sizeof(*isys_ctrl), GFP_KERNEL); + if (!isys_ctrl) { + rval = -ENOMEM; + goto out_ipu_bus_del_devices; + } + + /* Init butress control with default values based on the HW */ + memcpy(isys_ctrl, &isys_buttress_ctrl, sizeof(*isys_ctrl)); + + isp->isys_iommu = ipu_mmu_init(pdev, &pdev->dev, isys_ctrl, + isys_base, + &isys_ipdata.hw_variant, 0, ISYS_MMID); + rval = PTR_ERR(isp->isys_iommu); + if (IS_ERR(isp->isys_iommu)) { + dev_err(&pdev->dev, "can't create isys iommu device\n"); + rval = -ENOMEM; + goto out_ipu_bus_del_devices; + } + + isp->isys = ipu_isys_init(pdev, &isp->isys_iommu->dev, + &isp->isys_iommu->dev, isys_base, + &isys_ipdata, pdev->dev.platform_data, 0); + rval = PTR_ERR(isp->isys); + if (IS_ERR(isp->isys)) + goto out_ipu_bus_del_devices; + + psys_ctrl = devm_kzalloc(&pdev->dev, sizeof(*psys_ctrl), GFP_KERNEL); + if (!psys_ctrl) { + rval = -ENOMEM; + goto out_ipu_bus_del_devices; + } + + /* Init butress control with default values based on the HW */ + memcpy(psys_ctrl, &psys_buttress_ctrl, sizeof(*psys_ctrl)); + + isp->psys_iommu = ipu_mmu_init(pdev, + isp->isys_iommu ? + &isp->isys_iommu->dev : + &pdev->dev, psys_ctrl, psys_base, + &psys_ipdata.hw_variant, 1, PSYS_MMID); + rval = PTR_ERR(isp->psys_iommu); + if (IS_ERR(isp->psys_iommu)) { + dev_err(&pdev->dev, "can't create psys iommu device\n"); + goto out_ipu_bus_del_devices; + } + + isp->psys = ipu_psys_init(pdev, &isp->psys_iommu->dev, + &isp->psys_iommu->dev, psys_base, + &psys_ipdata, 0); + rval = PTR_ERR(isp->psys); + if (IS_ERR(isp->psys)) + goto out_ipu_bus_del_devices; + + rval = ipu_init_debugfs(isp); + if (rval) { + dev_err(&pdev->dev, "Failed to initialize debugfs"); + goto out_ipu_bus_del_devices; + } + + /* Configure the arbitration mechanisms for VC requests */ + ipu_configure_vc_mechanism(isp); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + dev_info(&pdev->dev, "IPU driver verion %d.%d\n", IPU_MAJOR_VERSION, + IPU_MINOR_VERSION); + + trace_printk("E|TMWK\n"); + return 0; + +out_ipu_bus_del_devices: + ipu_bus_del_devices(pdev); + ipu_buttress_exit(isp); + release_firmware(isp->cpd_fw); + + trace_printk("E|TMWK\n"); + return rval; +} + +static void ipu_pci_remove(struct pci_dev *pdev) +{ + struct ipu_device *isp = pci_get_drvdata(pdev); + + ipu_remove_debugfs(isp); + ipu_trace_release(isp); + + ipu_bus_del_devices(pdev); + + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + + ipu_buttress_exit(isp); + + release_firmware(isp->cpd_fw); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) +static void ipu_pci_reset_notify(struct pci_dev *pdev, bool prepare) +{ + struct ipu_device *isp = pci_get_drvdata(pdev); + + if (prepare) { + dev_err(&pdev->dev, "FLR prepare\n"); + pm_runtime_forbid(&isp->pdev->dev); + isp->flr_done = true; + return; + } + + ipu_buttress_restore(isp); + if (isp->secure_mode) + ipu_buttress_reset_authentication(isp); + + ipu_bus_flr_recovery(); + isp->ipc_reinit = true; + pm_runtime_allow(&isp->pdev->dev); + + dev_err(&pdev->dev, "FLR completed\n"); +} +#else +static void ipu_pci_reset_prepare(struct pci_dev *pdev) +{ + struct ipu_device *isp = pci_get_drvdata(pdev); + + dev_warn(&pdev->dev, "FLR prepare\n"); + pm_runtime_forbid(&isp->pdev->dev); + isp->flr_done = true; +} + +static void ipu_pci_reset_done(struct pci_dev *pdev) +{ + struct ipu_device *isp = pci_get_drvdata(pdev); + + ipu_buttress_restore(isp); + if (isp->secure_mode) + ipu_buttress_reset_authentication(isp); + + ipu_bus_flr_recovery(); + isp->ipc_reinit = true; + pm_runtime_allow(&isp->pdev->dev); + + dev_warn(&pdev->dev, "FLR completed\n"); +} +#endif + +#ifdef CONFIG_PM + +/* + * PCI base driver code requires driver to provide these to enable + * PCI device level PM state transitions (D0<->D3) + */ +static int ipu_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ipu_device *isp = pci_get_drvdata(pdev); + + isp->flr_done = false; + + return 0; +} + +static int ipu_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ipu_device *isp = pci_get_drvdata(pdev); + struct ipu_buttress *b = &isp->buttress; + int rval; + + /* Configure the arbitration mechanisms for VC requests */ + ipu_configure_vc_mechanism(isp); + + ipu_buttress_set_secure_mode(isp); + isp->secure_mode = ipu_buttress_get_secure_mode(isp); + dev_info(dev, "IPU in %s mode\n", + isp->secure_mode ? "secure" : "non-secure"); + + ipu_buttress_restore(isp); + + rval = ipu_buttress_ipc_reset(isp, &b->cse); + if (rval) + dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n"); + + return 0; +} + +static int ipu_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ipu_device *isp = pci_get_drvdata(pdev); + int rval; + + ipu_configure_vc_mechanism(isp); + ipu_buttress_restore(isp); + + if (isp->ipc_reinit) { + struct ipu_buttress *b = &isp->buttress; + + isp->ipc_reinit = false; + rval = ipu_buttress_ipc_reset(isp, &b->cse); + if (rval) + dev_err(&isp->pdev->dev, + "IPC reset protocol failed!\n"); + } + + return 0; +} + +static const struct dev_pm_ops ipu_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(&ipu_suspend, &ipu_resume) + SET_RUNTIME_PM_OPS(&ipu_suspend, /* Same as in suspend flow */ + &ipu_runtime_resume, + NULL) +}; + +#define IPU_PM (&ipu_pm_ops) +#else +#define IPU_PM NULL +#endif + +static const struct pci_device_id ipu_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU_PCI_ID)}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ipu_pci_tbl); + +static const struct pci_error_handlers pci_err_handlers = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + .reset_notify = ipu_pci_reset_notify, +#else + .reset_prepare = ipu_pci_reset_prepare, + .reset_done = ipu_pci_reset_done, +#endif +}; + +static struct pci_driver ipu_pci_driver = { + .name = IPU_NAME, + .id_table = ipu_pci_tbl, + .probe = ipu_pci_probe, + .remove = ipu_pci_remove, + .driver = { + .pm = IPU_PM, + }, + .err_handler = &pci_err_handlers, +}; + +static int __init ipu_init(void) +{ + int rval = ipu_bus_register(); + + if (rval) { + pr_warn("can't register ipu bus (%d)\n", rval); + return rval; + } + + rval = pci_register_driver(&ipu_pci_driver); + if (rval) { + pr_warn("can't register pci driver (%d)\n", rval); + goto out_pci_register_driver; + } + + return 0; + +out_pci_register_driver: + ipu_bus_unregister(); + + return rval; +} + +static void __exit ipu_exit(void) +{ + pci_unregister_driver(&ipu_pci_driver); + ipu_bus_unregister(); +} + +module_init(ipu_init); +module_exit(ipu_exit); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_AUTHOR("Jouni Högander "); +MODULE_AUTHOR("Antti Laakso "); +MODULE_AUTHOR("Samu Onkalo "); +MODULE_AUTHOR("Jianxu Zheng "); +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Renwei Wu "); +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Yunliang Ding "); +MODULE_AUTHOR("Zaikuo Wang "); +MODULE_AUTHOR("Leifu Zhao "); +MODULE_AUTHOR("Xia Wu "); +MODULE_AUTHOR("Kun Jiang "); +MODULE_AUTHOR("Intel"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu pci driver"); diff --git a/drivers/media/pci/intel/ipu.h b/drivers/media/pci/intel/ipu.h new file mode 100644 index 000000000000..96e9a2144133 --- /dev/null +++ b/drivers/media/pci/intel/ipu.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_H +#define IPU_H + +#include +#include +#include +#include + +#include "ipu-pdata.h" +#include "ipu-bus.h" +#include "ipu-buttress.h" +#include "ipu-trace.h" + +#if defined(CONFIG_VIDEO_INTEL_IPU4) +#define IPU_PCI_ID 0x5a88 +#elif defined(CONFIG_VIDEO_INTEL_IPU4P) +#define IPU_PCI_ID 0x8a19 +#endif + +/* + * IPU version definitions to reflect the IPU driver changes. + * Both ISYS and PSYS share the same version. + */ +#define IPU_MAJOR_VERSION 1 +#define IPU_MINOR_VERSION 0 +#define IPU_DRIVER_VERSION (IPU_MAJOR_VERSION << 16 | IPU_MINOR_VERSION) + +/* processing system frequency: 25Mhz x ratio, Legal values [8,32] */ +#define PS_FREQ_CTL_DEFAULT_RATIO 0x12 + +/* input system frequency: 1600Mhz / divisor. Legal values [2,8] */ +#define IS_FREQ_SOURCE 1600000000 +#define IS_FREQ_CTL_DIVISOR 0x4 + +/* + * ISYS DMA can overshoot. For higher resolutions over allocation is one line + * but it must be at minimum 1024 bytes. Value could be different in + * different versions / generations thus provide it via platform data. + */ +#define IPU_ISYS_OVERALLOC_MIN 1024 + +/* + * Physical pages in GDA 128 * 1K pages. + */ +#define IPU_DEVICE_GDA_NR_PAGES 128 + +/* + * Virtualization factor to calculate the available virtual pages. + */ +#if defined(CONFIG_VIDEO_INTEL_IPU4) +#define IPU_DEVICE_GDA_VIRT_FACTOR 8 +#elif defined(CONFIG_VIDEO_INTEL_IPU4P) +#define IPU_DEVICE_GDA_VIRT_FACTOR 32 +#else +#define IPU_DEVICE_GDA_VIRT_FACTOR 8 +#endif + +struct pci_dev; +struct list_head; +struct firmware; + +#define NR_OF_MMU_RESOURCES 2 + +struct ipu_device { + struct pci_dev *pdev; + struct list_head devices; + struct ipu_bus_device *isys_iommu, *isys; + struct ipu_bus_device *psys_iommu, *psys; + struct ipu_buttress buttress; + + const struct firmware *cpd_fw; + const char *cpd_fw_name; + u64 *pkg_dir; + dma_addr_t pkg_dir_dma_addr; + unsigned int pkg_dir_size; + + void __iomem *base; + void __iomem *base2; + struct dentry *ipu_dir; + struct ipu_trace *trace; + bool flr_done; + bool ipc_reinit; + bool secure_mode; + + int (*isys_fw_reload)(struct ipu_device *isp); + int (*cpd_fw_reload)(struct ipu_device *isp); +}; + +#define IPU_DMA_MASK 39 +#define IPU_LIB_CALL_TIMEOUT_MS 2000 +#define IPU_PSYS_CMD_TIMEOUT_MS 2000 +#define IPU_PSYS_OPEN_TIMEOUT_US 50 +#define IPU_PSYS_OPEN_RETRY (10000 / IPU_PSYS_OPEN_TIMEOUT_US) + +int ipu_fw_authenticate(void *data, u64 val); +void ipu_configure_spc(struct ipu_device *isp, + const struct ipu_hw_variants *hw_variant, + int pkg_dir_idx, void __iomem *base, u64 *pkg_dir, + dma_addr_t pkg_dir_dma_addr); +int request_cpd_fw(const struct firmware **firmware_p, const char *name, + struct device *device); +#endif /* IPU_H */ diff --git a/drivers/media/pci/intel/ipu4/Makefile b/drivers/media/pci/intel/ipu4/Makefile new file mode 100644 index 000000000000..dae16f35e7f2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/Makefile @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2018, Intel Corporation. + +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +ifdef CONFIG_VIDEO_INTEL_IPU4 +ccflags-y += -DHAS_DUAL_CMD_CTX_SUPPORT=0 -DIPU_VC_SUPPORT -DIPU_HAS_ISA -DIPU_PSYS_LEGACY +ccflags-y += -DIPU_META_DATA_SUPPORT -DI2C_WA + +intel-ipu4-objs += ../ipu.o \ + ../ipu-bus.o \ + ../ipu-dma.o \ + ../ipu-buttress.o \ + ../ipu-trace.o \ + ../ipu-cpd.o \ + ../ipu-fw-com.o \ + ipu4.o + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4.o + +intel-ipu4-mmu-objs += ../ipu-mmu.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-mmu.o + +intel-ipu4-isys-objs += ../ipu-isys.o \ + ../ipu-isys-csi2.o \ + ipu4-isys.o \ + ipu4-isys-csi2.o \ + ../ipu-isys-csi2-be-soc.o \ + ../ipu-isys-csi2-be.o \ + ../ipu-fw-isys.o \ + ipu4-isys-isa.o \ + ../ipu-isys-video.o \ + ../ipu-isys-queue.o \ + ../ipu-isys-subdev.o \ + ../ipu-isys-tpg.o + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-isys.o + +intel-ipu4-psys-objs += ../ipu-psys.o \ + ipu4-psys.o \ + ipu4-resources.o \ + +ifndef CONFIG_VIDEO_INTEL_IPU_FW_LIB +intel-ipu4-psys-objs += ipu4-fw-resources.o \ + ../ipu-fw-psys.o +endif + +ifeq ($(CONFIG_COMPAT),y) +intel-ipu4-psys-objs += ../ipu-psys-compat32.o +endif + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-psys.o + +ifdef CONFIG_VIDEO_INTEL_IPU_FW_LIB +include $(srcpath)/$(src)/ipu4-css/Makefile.isyslib +include $(srcpath)/$(src)/ipu4-css/Makefile.psyslib +endif + +ccflags-y += -I$(srcpath)/$(src)/../../../../../include/ +ccflags-y += -I$(srcpath)/$(src)/../ +ccflags-y += -I$(srcpath)/$(src)/ +ifdef CONFIG_VIDEO_INTEL_IPU_FW_LIB +ccflags-y += -I$(srcpath)/$(src)/ipu4-css +endif + +ccflags-y += -DPARAMETER_INTERFACE_V2 +endif + +ifdef CONFIG_VIDEO_INTEL_IPU4P +ccflags-y += -DHAS_DUAL_CMD_CTX_SUPPORT=0 -DIPU_VC_SUPPORT -DIPU_PSYS_LEGACY -DIPU_HAS_ISA +ccflags-y += -DIPU_META_DATA_SUPPORT + +intel-ipu4p-objs += ../ipu.o \ + ../ipu-bus.o \ + ../ipu-dma.o \ + ../ipu-buttress.o \ + ../ipu-trace.o \ + ../ipu-cpd.o \ + ../ipu-fw-com.o \ + ipu4.o + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p.o + +intel-ipu4p-mmu-objs += ../ipu-mmu.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p-mmu.o + +intel-ipu4p-isys-objs += ../ipu-isys.o \ + ../ipu-isys-csi2.o \ + ipu4-isys.o \ + ipu4p-isys-csi2.o \ + ../ipu-isys-csi2-be-soc.o \ + ../ipu-isys-csi2-be.o \ + ../ipu-fw-isys.o \ + ipu4-isys-isa.o \ + ../ipu-isys-video.o \ + ../ipu-isys-queue.o \ + ../ipu-isys-subdev.o \ + ../ipu-isys-tpg.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p-isys.o + +intel-ipu4p-psys-objs += ../ipu-psys.o \ + ipu4-psys.o \ + ipu4-resources.o \ + +ifndef CONFIG_VIDEO_INTEL_IPU_FW_LIB +intel-ipu4p-psys-objs += ipu4-fw-resources.o \ + ../ipu-fw-psys.o +endif + +ifeq ($(CONFIG_COMPAT),y) +intel-ipu4p-psys-objs += ../ipu-psys-compat32.o +endif + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p-psys.o + +ifdef CONFIG_VIDEO_INTEL_IPU_FW_LIB +include $(srcpath)/$(src)/ipu4p-css/Makefile.isyslib +include $(srcpath)/$(src)/ipu4p-css/Makefile.psyslib +endif + +ccflags-y += -I$(srcpath)/$(src)/../../../../../include/ +ccflags-y += -I$(srcpath)/$(src)/../ +ccflags-y += -I$(srcpath)/$(src)/ +ifdef CONFIG_VIDEO_INTEL_IPU_FW_LIB +ccflags-y += -I$(srcpath)/$(src)/ipu4p-css +endif + +ccflags-y += -DPARAMETER_INTERFACE_V2 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-buttress-regs.h b/drivers/media/pci/intel/ipu4/ipu-platform-buttress-regs.h new file mode 100644 index 000000000000..ffd770c88198 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-buttress-regs.h @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_BUTTRESS_REGS_H +#define IPU_PLATFORM_BUTTRESS_REGS_H + +#ifdef CONFIG_VIDEO_INTEL_IPU4P +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_SHIFT 20 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK (0x1f << 20) +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xc + +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_SHIFT 25 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK (0x1f << 25) +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0x10 + +#define BUTTRESS_REG_CSI_BSCAN_EXCLUDE 0x100d8 +#define CPHY0_DLL_OVRD_OFFSET 0x10100 +#define CPHY0_RX_CONTROL1_OFFSET 0x10110 +#define DPHY0_DLL_OVRD_OFFSET 0x1014c +#define DPHY0_RX_CNTRL_OFFSET 0x10158 +#define BB0_AFE_CONFIG_OFFSET 0x10174 + +#define BUTTRESS_REG_IS_FREQ_CTL_RATIO_SHIFT 1 +#define BUTTRESS_REG_PS_FREQ_CTL_OVRD_SHIFT 7 +#define BUTTRESS_REG_PS_FREQ_CTL_RATIO_SHIFT 8 + +#define BUTTRESS_REG_CPHYX_DLL_OVRD(x) \ + (CPHY0_DLL_OVRD_OFFSET + (x >> 1) * 0x100) +#define BUTTRESS_REG_CPHYX_RX_CONTROL1(x) \ + (CPHY0_RX_CONTROL1_OFFSET + (x >> 1) * 0x100) +#define BUTTRESS_REG_DPHYX_DLL_OVRD(x) \ + (DPHY0_DLL_OVRD_OFFSET + (x >> 1) * 0x100) +#define BUTTRESS_REG_DPHYX_RX_CNTRL(x) \ + (DPHY0_RX_CNTRL_OFFSET + (x >> 1) * 0x100) +#define BUTTRESS_REG_BBX_AFE_CONFIG(x) \ + (BB0_AFE_CONFIG_OFFSET + (x >> 1) * 0x100) +#endif /* CONFIG_VIDEO_INTEL_IPU4P */ + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_SHIFT 20 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK (0xf << 20) +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xa + +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_SHIFT 24 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK (0x1f << 24) +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0xf +#endif /* CONFIG_VIDEO_INTEL_IPU4 */ + +#define BUTTRESS_REG_WDT 0x8 +#define BUTTRESS_REG_BTRS_CTRL 0xc +#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0 BIT(0) +#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1 BIT(1) + +#define BUTTRESS_REG_FW_RESET_CTL 0x30 +#define BUTTRESS_FW_RESET_CTL_START_SHIFT 0 +#define BUTTRESS_FW_RESET_CTL_DONE_SHIFT 1 + +#define BUTTRESS_REG_IS_FREQ_CTL 0x34 +#define BUTTRESS_IS_FREQ_CTL_DIVISOR_MASK 0xf + +#define BUTTRESS_REG_PS_FREQ_CTL 0x38 +#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xff + +#define BUTTRESS_FREQ_CTL_START_SHIFT 31 +#define BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT 8 +#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK (0xff << 8) + +#define BUTTRESS_REG_PWR_STATE 0x5c +#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 4 +#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x7 << 4) + +#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 8 +#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x7 << 8) + +#define BUTTRESS_PWR_STATE_RESET 0x0 +#define BUTTRESS_PWR_STATE_PWR_ON_DONE 0x1 +#define BUTTRESS_PWR_STATE_PWR_RDY 0x3 +#define BUTTRESS_PWR_STATE_PWR_IDLE 0x4 + +#define BUTTRESS_PWR_STATE_HH_STATUS_SHIFT 12 +#define BUTTRESS_PWR_STATE_HH_STATUS_MASK (0x3 << 12) + +enum { + BUTTRESS_PWR_STATE_HH_STATE_IDLE, + BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS, + BUTTRESS_PWR_STATE_HH_STATE_DONE, + BUTTRESS_PWR_STATE_HH_STATE_ERR, +}; + +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP 0x1 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK 0x2 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK 0x3 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES 0x4 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1 0x5 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2 0x6 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES 0x7 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP 0x8 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT 0x9 +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED 0xb +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3 0xc +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD 0xd +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT 0xe +#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0 0xf +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY 0x1 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH 0x2 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD 0x3 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH 0x4 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO 0x5 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP 0x6 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK 0x7 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES 0x8 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1 0x9 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2 0xa +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES 0xb +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT 0xc +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT 0xd +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED 0x10 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3 0x11 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK 0x12 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND 0x13 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4 0x14 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP 0x15 +#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK 0x16 + +#define BUTTRESS_REG_SECURITY_CTL 0x300 + +#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE_SHIFT 16 +#define BUTTRESS_SECURITY_CTL_FW_SETUP_SHIFT 0 +#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK 0x1f + +#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE 0x1 +#define BUTTRESS_SECURITY_CTL_AUTH_DONE 0x2 +#define BUTTRESS_SECURITY_CTL_AUTH_FAILED 0x8 + +#define BUTTRESS_REG_SENSOR_FREQ_CTL 0x16c + +#define BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_DEFAULT(i) \ + (0x1b << ((i) * 10)) +#define BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_SHIFT(i) ((i) * 10) +#define BUTTRESS_SENSOR_FREQ_CTL_OSC_OUT_FREQ_MASK(i) \ + (0x1ff << ((i) * 10)) + +#define BUTTRESS_SENSOR_CLK_FREQ_6P75MHZ 0x176 +#define BUTTRESS_SENSOR_CLK_FREQ_8MHZ 0x164 +#define BUTTRESS_SENSOR_CLK_FREQ_9P6MHZ 0x2 +#define BUTTRESS_SENSOR_CLK_FREQ_12MHZ 0x1b2 +#define BUTTRESS_SENSOR_CLK_FREQ_13P6MHZ 0x1ac +#define BUTTRESS_SENSOR_CLK_FREQ_14P4MHZ 0x1cc +#define BUTTRESS_SENSOR_CLK_FREQ_15P8MHZ 0x1a6 +#define BUTTRESS_SENSOR_CLK_FREQ_16P2MHZ 0xca +#define BUTTRESS_SENSOR_CLK_FREQ_17P3MHZ 0x12e +#define BUTTRESS_SENSOR_CLK_FREQ_18P6MHZ 0x1c0 +#define BUTTRESS_SENSOR_CLK_FREQ_19P2MHZ 0x0 +#define BUTTRESS_SENSOR_CLK_FREQ_24MHZ 0xb2 +#define BUTTRESS_SENSOR_CLK_FREQ_26MHZ 0xae +#define BUTTRESS_SENSOR_CLK_FREQ_27MHZ 0x196 + +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_FB_RATIO_MASK 0xff +#define BUTTRESS_SENSOR_FREQ_CTL_SEL_MIPICLK_A_SHIFT 8 +#define BUTTRESS_SENSOR_FREQ_CTL_SEL_MIPICLK_A_MASK (0x2 << 8) +#define BUTTRESS_SENSOR_FREQ_CTL_SEL_MIPICLK_C_SHIFT 10 +#define BUTTRESS_SENSOR_FREQ_CTL_SEL_MIPICLK_C_MASK (0x2 << 10) +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_FORCE_OFF_SHIFT 12 +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_REF_RATIO_SHIFT 14 +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_REF_RATIO_MASK (0x2 << 14) +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_PVD_RATIO_SHIFT 16 +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_PVD_RATIO_MASK (0x2 << 16) +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_OUTPUT_RATIO_SHIFT 18 +#define BUTTRESS_SENSOR_FREQ_CTL_LJPLL_OUTPUT_RATIO_MASK (0x2 << 18) +#define BUTTRESS_SENSOR_FREQ_CTL_START_SHIFT 31 + +#define BUTTRESS_REG_SENSOR_CLK_CTL 0x170 + +/* 0 <= i <= 2 */ +#define BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_EN_SHIFT(i) ((i) * 2) +#define BUTTRESS_SENSOR_CLK_CTL_OSC_CLK_OUT_SEL_SHIFT(i) ((i) * 2 + 1) + +#define BUTTRESS_REG_FW_SOURCE_BASE_LO 0x78 +#define BUTTRESS_REG_FW_SOURCE_BASE_HI 0x7C +#define BUTTRESS_REG_FW_SOURCE_SIZE 0x80 + +#define BUTTRESS_REG_ISR_STATUS 0x90 +#define BUTTRESS_REG_ISR_ENABLED_STATUS 0x94 +#define BUTTRESS_REG_ISR_ENABLE 0x98 +#define BUTTRESS_REG_ISR_CLEAR 0x9C + +#define BUTTRESS_ISR_IS_IRQ BIT(0) +#define BUTTRESS_ISR_PS_IRQ BIT(1) +#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE BIT(2) +#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH BIT(3) +#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING BIT(4) +#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING BIT(5) +#define BUTTRESS_ISR_CSE_CSR_SET BIT(6) +#define BUTTRESS_ISR_ISH_CSR_SET BIT(7) +#define BUTTRESS_ISR_SPURIOUS_CMP BIT(8) +#define BUTTRESS_ISR_WATCHDOG_EXPIRED BIT(9) +#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ BIT(10) +#define BUTTRESS_ISR_SAI_VIOLATION BIT(11) +#define BUTTRESS_ISR_HW_ASSERTION BIT(12) + +#define BUTTRESS_REG_IU2CSEDB0 0x100 + +#define BUTTRESS_IU2CSEDB0_BUSY_SHIFT 31 +#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 +#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 +#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 + +#define BUTTRESS_REG_IU2CSEDATA0 0x104 + +#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 +#define BUTTRESS_IU2CSEDATA0_IPC_AUTHENTICATE_RUN 2 +#define BUTTRESS_IU2CSEDATA0_IPC_AUTHENTICATE_REPLACE 3 +#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 + +#define BUTTRESS_REG_IU2CSECSR 0x108 + +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) +#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) +#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) +#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) +#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) + +#define BUTTRESS_REG_CSE2IUDB0 0x304 +#define BUTTRESS_REG_CSE2IUCSR 0x30C +#define BUTTRESS_REG_CSE2IUDATA0 0x308 + +/* 0x20 == NACK, 0xf == unknown command */ +#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 +#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff + +#define BUTTRESS_REG_ISH2IUCSR 0x50 +#define BUTTRESS_REG_ISH2IUDB0 0x54 +#define BUTTRESS_REG_ISH2IUDATA0 0x58 + +#define BUTTRESS_REG_IU2ISHDB0 0x10C +#define BUTTRESS_REG_IU2ISHDATA0 0x110 +#define BUTTRESS_REG_IU2ISHDATA1 0x114 +#define BUTTRESS_REG_IU2ISHCSR 0x118 + +#define BUTTRESS_REG_ISH_START_DETECT 0x198 +#define BUTTRESS_REG_ISH_START_DETECT_MASK 0x19C + +#define BUTTRESS_REG_FABRIC_CMD 0x88 + +#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC BIT(0) +#define BUTTRESS_FABRIC_CMD_IS_DRAIN BIT(4) + +#define BUTTRESS_REG_TSW_CTL 0x120 +#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8) + +#define BUTTRESS_REG_TSC_LO 0x164 +#define BUTTRESS_REG_TSC_HI 0x168 + +#define BUTTRESS_REG_CSI2_PORT_CONFIG_AB 0x200 +#define BUTTRESS_CSI2_PORT_CONFIG_AB_MUX_MASK 0x1f +#define BUTTRESS_CSI2_PORT_CONFIG_AB_COMBO_SHIFT_B0 16 + +#define BUTTRESS_REG_PS_FREQ_CAPABILITIES 0xf7498 + +#define BUTTRESS_PS_FREQ_CAPABILITIES_LAST_RESOLVED_RATIO_SHIFT 24 +#define BUTTRESS_PS_FREQ_CAPABILITIES_LAST_RESOLVED_RATIO_MASK (0xff << 24) +#define BUTTRESS_PS_FREQ_CAPABILITIES_MAX_RATIO_SHIFT 16 +#define BUTTRESS_PS_FREQ_CAPABILITIES_MAX_RATIO_MASK (0xff << 16) +#define BUTTRESS_PS_FREQ_CAPABILITIES_EFFICIENT_RATIO_SHIFT 8 +#define BUTTRESS_PS_FREQ_CAPABILITIES_EFFICIENT_RATIO_MASK (0xff << 8) +#define BUTTRESS_PS_FREQ_CAPABILITIES_MIN_RATIO_SHIFT 0 +#define BUTTRESS_PS_FREQ_CAPABILITIES_MIN_RATIO_MASK (0xff) + +#define BUTTRESS_IRQS (BUTTRESS_ISR_SAI_VIOLATION | \ + BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \ + BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \ + BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \ + BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | \ + BUTTRESS_ISR_IS_IRQ | \ + BUTTRESS_ISR_PS_IRQ) + +#endif /* IPU_BUTTRESS_REGS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-isys-csi2-reg.h b/drivers/media/pci/intel/ipu4/ipu-platform-isys-csi2-reg.h new file mode 100644 index 000000000000..efdf287e38f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-isys-csi2-reg.h @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_ISYS_CSI2_REG_H +#define IPU_PLATFORM_ISYS_CSI2_REG_H + +#ifdef CONFIG_VIDEO_INTEL_IPU4P +/* CSI RX CPHY regs */ +#define CSI2_REG_CSI_RX_CPHY_NOF_ENABLED_LANES 0x04 +#define CSI2_REG_CSI_RX_CPHY_HBP_TESTMODE 0x08 +#define CSI2_REG_CSI_RX_CPHY_PH_CRC_CFG 0x0C +#define CSI2_REG_CSI_RX_CPHY_ERR_HANDLING 0x10 +#define CSI2_REG_CSI_RX_CPHY_PORTCFG_CTL 0x14 +#define CSI2_REG_CSI_RX_CPHY_PORTCFG_TIMEOUT_CNTR 0x18 +#define CSI2_REG_CSI_RX_CPHY_SYNC_CNTR_SEL 0x1C +#define CSI2_REG_CSI_RX_CPHY_STATS 0x20 + +#define CSI2_REG_CSI2PART_IRQ_EDGE 0xB00 +#define CSI2_REG_CSI2PART_IRQ_MASK 0xB04 +#define CSI2_REG_CSI2PART_IRQ_STATUS 0xB08 +#define CSI2_REG_CSI2PART_IRQ_CLEAR 0xB0c +#define CSI2_REG_CSI2PART_IRQ_ENABLE 0xB10 +#define CSI2_REG_CSI2PART_IRQ_LEVEL_NOT_PULSE 0xB14 +#define CSI2_CSI2PART_IRQ_CSIRX 0x10000 +#define CSI2_CSI2PART_IRQ_CSI2S2M 0x20000 + +#define CSI2_REG_CSIRX_IRQ_EDGE 0xC00 +#define CSI2_REG_CSIRX_IRQ_MASK 0xC04 +#define CSI2_REG_CSIRX_IRQ_STATUS 0xC08 +#define CSI2_REG_CSIRX_IRQ_CLEAR 0xC0c +#define CSI2_REG_CSIRX_IRQ_ENABLE 0xC10 +#define CSI2_REG_CSIRX_IRQ_LEVEL_NOT_PULSE 0xC14 +#define CSI2_CSIRX_HEADER_SINGLE_ERROR_CORRECTED BIT(0) +#define CSI2_CSIRX_HEADER_MULTIPLE_ERRORS_CORRECTED BIT(1) +#define CSI2_CSIRX_PAYLOAD_CRC_ERROR BIT(2) +#define CSI2_CSIRX_FIFO_OVERFLOW BIT(3) +#define CSI2_CSIRX_RESERVED_SHORT_PACKET_DATA_TYPE BIT(4) +#define CSI2_CSIRX_RESERVED_LONG_PACKET_DATA_TYPE BIT(5) +#define CSI2_CSIRX_INCOMPLETE_LONG_PACKET BIT(6) +#define CSI2_CSIRX_FRAME_SYNC_ERROR BIT(7) +#define CSI2_CSIRX_LINE_SYNC_ERROR BIT(8) +#define CSI2_CSIRX_DPHY_RECOVERABLE_SYNC_ERROR BIT(9) +#define CSI2_CSIRX_DPHY_NONRECOVERABLE_SYNC_ERROR BIT(10) +#define CSI2_CSIRX_ESCAPE_MODE_ERROR BIT(11) +#define CSI2_CSIRX_ESCAPE_MODE_TRIGGER_EVENT BIT(12) +#define CSI2_CSIRX_ESCAPE_MODE_ULTRALOW_POWER_DATA BIT(13) +#define CSI2_CSIRX_ESCAPE_MODE_ULTRALOW_POWER_EXIT_CLK BIT(14) +#define CSI2_CSIRX_INTER_FRAME_SHORT_PACKET_DISCARDED BIT(15) +#define CSI2_CSIRX_INTER_FRAME_LONG_PACKET_DISCARDED BIT(16) +#define CSI2_CSIRX_NUM_ERRORS 17 + +#define CSI2_REG_CSI2S2M_IRQ_EDGE 0xD00 +#define CSI2_REG_CSI2S2M_IRQ_MASK 0xD04 +#define CSI2_REG_CSI2S2M_IRQ_STATUS 0xD08 +#define CSI2_REG_CSI2S2M_IRQ_CLEAR 0xD0c +#define CSI2_REG_CSI2S2M_IRQ_ENABLE 0xD10 +#define CSI2_REG_CSI2S2M_IRQ_LEVEL_NOT_PULSE 0xD14 + +#ifdef IPU_VC_SUPPORT +#define CSI2_IRQ_FS_VC(chn) (0x10000 << ((chn) * 4)) +#define CSI2_IRQ_FE_VC(chn) (0x20000 << ((chn) * 4)) +#define CSI2_IRQ_LS_VC(chn) (0x40000 << ((chn) * 4)) +#define CSI2_IRQ_LE_VC(chn) (0x80000 << ((chn) * 4)) +#else +#define CSI2_IRQ_FS_VC 0x10000 +#define CSI2_IRQ_FE_VC 0x20000 +#define CSI2_IRQ_LS_VC 0x40000 +#define CSI2_IRQ_LE_VC 0x80000 +#endif /* IPU_VC_SUPPORT */ +#define CSI2_REG_CL0_IBUFCTL_EN_FLUSH_FOR_IDRAIN 0x6002c +#define CSI2_REG_CL1_IBUFCTL_EN_FLUSH_FOR_IDRAIN 0x6802c +#define IPU_REG_ISYS_IBUFCTL_EN_FLUSH_FOR_IDRAIN 0xb602c +#endif /* CONFIG_VIDEO_INTEL_IPU4P */ + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +/* IRQ-related registers specific to each of the four CSI receivers */ +#define CSI2_REG_CSI2PART_IRQ_EDGE 0x400 +#define CSI2_REG_CSI2PART_IRQ_MASK 0x404 +#define CSI2_REG_CSI2PART_IRQ_STATUS 0x408 +#define CSI2_REG_CSI2PART_IRQ_CLEAR 0x40c +#define CSI2_REG_CSI2PART_IRQ_ENABLE 0x410 +#define CSI2_REG_CSI2PART_IRQ_LEVEL_NOT_PULSE 0x414 +#define CSI2_CSI2PART_IRQ_CSIRX 0x10000 +#define CSI2_CSI2PART_IRQ_CSI2S2M 0x20000 + +#define CSI2_REG_CSIRX_IRQ_EDGE 0x500 +#define CSI2_REG_CSIRX_IRQ_MASK 0x504 +#define CSI2_REG_CSIRX_IRQ_STATUS 0x508 +#define CSI2_REG_CSIRX_IRQ_CLEAR 0x50c +#define CSI2_REG_CSIRX_IRQ_ENABLE 0x510 +#define CSI2_REG_CSIRX_IRQ_LEVEL_NOT_PULSE 0x514 +#define CSI2_CSIRX_HEADER_SINGLE_ERROR_CORRECTED BIT(0) +#define CSI2_CSIRX_HEADER_MULTIPLE_ERRORS_CORRECTED BIT(1) +#define CSI2_CSIRX_PAYLOAD_CRC_ERROR BIT(2) +#define CSI2_CSIRX_FIFO_OVERFLOW BIT(3) +#define CSI2_CSIRX_RESERVED_SHORT_PACKET_DATA_TYPE BIT(4) +#define CSI2_CSIRX_RESERVED_LONG_PACKET_DATA_TYPE BIT(5) +#define CSI2_CSIRX_INCOMPLETE_LONG_PACKET BIT(6) +#define CSI2_CSIRX_FRAME_SYNC_ERROR BIT(7) +#define CSI2_CSIRX_LINE_SYNC_ERROR BIT(8) +#define CSI2_CSIRX_DPHY_RECOVERABLE_SYNC_ERROR BIT(9) +#define CSI2_CSIRX_DPHY_NONRECOVERABLE_SYNC_ERROR BIT(10) +#define CSI2_CSIRX_ESCAPE_MODE_ERROR BIT(11) +#define CSI2_CSIRX_ESCAPE_MODE_TRIGGER_EVENT BIT(12) +#define CSI2_CSIRX_ESCAPE_MODE_ULTRALOW_POWER_DATA BIT(13) +#define CSI2_CSIRX_ESCAPE_MODE_ULTRALOW_POWER_EXIT_CLK BIT(14) +#define CSI2_CSIRX_INTER_FRAME_SHORT_PACKET_DISCARDED BIT(15) +#define CSI2_CSIRX_INTER_FRAME_LONG_PACKET_DISCARDED BIT(16) +#define CSI2_CSIRX_NUM_ERRORS 17 + +#define CSI2_REG_CSI2S2M_IRQ_EDGE 0x600 +#define CSI2_REG_CSI2S2M_IRQ_MASK 0x604 +#define CSI2_REG_CSI2S2M_IRQ_STATUS 0x608 +#define CSI2_REG_CSI2S2M_IRQ_CLEAR 0x60c +#define CSI2_REG_CSI2S2M_IRQ_ENABLE 0x610 +#define CSI2_REG_CSI2S2M_IRQ_LEVEL_NOT_PULSE 0x614 + +#ifdef IPU_VC_SUPPORT +#define CSI2_IRQ_FS_VC(chn) (1 << ((chn) * 4)) +#define CSI2_IRQ_FE_VC(chn) (2 << ((chn) * 4)) +#define CSI2_IRQ_LS_VC(chn) (4 << ((chn) * 4)) +#define CSI2_IRQ_LE_VC(chn) (8 << ((chn) * 4)) +#else +#define CSI2_IRQ_FS_VC 1 +#define CSI2_IRQ_FE_VC 2 +#define CSI2_IRQ_LS_VC 4 +#define CSI2_IRQ_LE_VC 8 +#endif /* IPU_VC_SUPPORT */ +#endif /* CONFIG_VIDEO_INTEL_IPU4 */ + +#define CSI2_REG_CSI_RX_ENABLE 0x00 +#define CSI2_CSI_RX_ENABLE_ENABLE 0x01 +/* Enabled lanes - 1 */ +#define CSI2_REG_CSI_RX_NOF_ENABLED_LANES 0x04 +#define CSI2_REG_CSI_RX_CONFIG 0x08 +#define CSI2_CSI_RX_CONFIG_RELEASE_LP11 0x1 +#define CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING 0x2 +#define CSI2_CSI_RX_CONFIG_SKEWCAL_ENABLE 0x4 +#define CSI2_REG_CSI_RX_HBP_TESTMODE_ENABLE 0x0c +#define CSI2_REG_CSI_RX_ERROR_HANDLING 0x10 +#define CSI2_REG_CSI_RX_SYNC_COUNTER_SEL 0x14 +#define CSI2_RX_SYNC_COUNTER_INTERNAL 0 +#define CSI2_RX_SYNC_COUNTER_EXTERNAL 3 +#define CSI2_REG_CSI_RX_SP_IF_CONFIG 0x18 +#define CSI2_REG_CSI_RX_LP_IF_CONFIG 0x1C +#define CSI2_REG_CSI_RX_STATUS 0x20 +#define CSI2_CSI_RX_STATUS_BUSY 0x01 +#define CSI2_REG_CSI_RX_STATUS_DLANE_HS 0x24 +#define CSI2_REG_CSI_RX_STATUS_DLANE_LP 0x28 +#define CSI2_REG_CSI_RX_DLY_CNT_TERMEN_CLANE 0x2c +#define CSI2_REG_CSI_RX_DLY_CNT_SETTLE_CLANE 0x30 +/* 0..3 */ +#define CSI2_REG_CSI_RX_DLY_CNT_TERMEN_DLANE(n) (0x34 + (n) * 8) +#define CSI2_REG_CSI_RX_DLY_CNT_SETTLE_DLANE(n) (0x38 + (n) * 8) + +/*General purposer registers, offset to gpreg base*/ +#define CSI2_REG_CSI_GPREG_SOFT_RESET 0 +#define CSI2_REG_CSI_GPREG_SOFT_RESET_SLV 0x4 +#define CSI2_REG_CSI_GPREG_HPLL_FREQ 0x8 +#define CSI2_REG_CSI_GPREG_ISCLK_RATIO 0xc +#define CSI2_REG_CSI_GPREG_HPLL_FREQ_ISCLK_RATIO_OVERRIDE 0x10 +#define CSI2_REG_CSI_GPREG_CR_PORT_CONFIG 0x14 +#define CSI2_REG_CSI_GPREG_RCOMP_TIMER_DISABLE 0x18 +#define CSI2_REG_CSI_GPREG_RCOMP_TIMER_VALUE 0x1c + +/* + * Following is the list of relevant registers and + * their offset within the legacy PHY endpoint. Accessible only via + * sideband bus. + * Register naming is a bit misleading. DPHY / CPHY / LANE0 / LANE1 + * all are required for DPHY configurations. + * Registers are accessible only via sideband bus. + */ + +/* Legacy receiver block */ +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY 0xb8 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT4_SHIFT 9 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT3_SHIFT 8 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT2_SHIFT 7 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT1_SHIFT 6 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_CODE_SHIFT 1 +#define CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_SHIFT 0 + +/* Combo receiver block */ +#define CSI2_SB_CSI_RCOMP_CONTROL_COMBO 0x08 +#define CSI2_SB_CSI_RCOMP_UPDATE_MODE_SHIFT 15 +#define CSI2_SB_CSI_RCOMP_OVR_ENABLE_SHIFT 6 +#define CSI2_SB_CSI_RCOMP_OVR_CODE_SHIFT 1 + +#define CSI2_SB_CPHY0_DLL_OVRD 0x18 +#define CSI2_SB_CPHY0_DLL_OVRD_CRCDC_FSM_DLANE0_SHIFT 1 +#define CSI2_SB_CPHY0_DLL_OVRD_LDEN_CRCDC_FSM_DLANE0 BIT(0) +#define CSI2_SB_CPHY2_DLL_OVRD 0x60 +#define CSI2_SB_CPHY2_DLL_OVRD_CRCDC_FSM_DLANE1_SHIFT 1 +#define CSI2_SB_CPHY2_DLL_OVRD_LDEN_CRCDC_FSM_DLANE1 BIT(0) + +#define CSI2_SB_CPHY0_RX_CONTROL1 0x28 +#define CSI2_SB_CPHY0_RX_CONTROL1_EQ_LANE0_SHIFT 27 +#define CSI2_SB_CPHY2_RX_CONTROL1 0x68 +#define CSI2_SB_CPHY2_RX_CONTROL1_EQ_LANE1_SHIFT 27 + +#define CSI2_SB_DPHY0_DLL_OVRD 0xA4 +#define CSI2_SB_DPHY0_DLL_OVRD_LDEN_DRC_FSM_SHIFT 0 +#define CSI2_SB_DPHY0_DLL_OVRD_DRC_FSM_OVRD_SHIFT 1 +#define CSI2_SB_DPHY1_DLL_OVRD 0xD0 +#define CSI2_SB_DPHY1_DLL_OVRD_LDEN_DRC_FSM_SHIFT 0 +#define CSI2_SB_DPHY1_DLL_OVRD_DRC_FSM_OVRD_SHIFT 1 + +#define CSI2_SB_DPHY0_RX_CNTRL 0xB0 +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE3_SHIFT 28 +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE2_SHIFT 26 +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE1_SHIFT 24 +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE0_SHIFT 22 +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE23_MASK \ + ((1 << CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE3_SHIFT) | \ + (1 << CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE2_SHIFT)) + +#define CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE01_MASK \ + ((1 << CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE1_SHIFT) | \ + (1 << CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE0_SHIFT)) + +#endif /* IPU_ISYS_CSI2_REG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-isys.h b/drivers/media/pci/intel/ipu4/ipu-platform-isys.h new file mode 100644 index 000000000000..dfd3799972e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-isys.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_ISYS_H +#define IPU_PLATFORM_ISYS_H + +#include "ipu4-isys-isa.h" + +#define IPU_ISYS_ENTITY_PREFIX "Intel IPU4" + +/* + * FW support max 8 streams + */ +#define IPU_ISYS_MAX_STREAMS 8 +#ifdef IPU_VC_SUPPORT + +#define NR_OF_CSI2_BE_SOC_STREAMS 8 +#define NR_OF_CSI2_VC 4 +#endif + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-psys.h b/drivers/media/pci/intel/ipu4/ipu-platform-psys.h new file mode 100644 index 000000000000..7826727f377a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-psys.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_PSYS_H +#define IPU_PLATFORM_PSYS_H + +#include + +struct ipu_psys_fh; +struct ipu_psys_kcmd; + +struct ipu_psys_scheduler { + struct list_head kcmds[IPU_PSYS_CMD_PRIORITY_NUM]; + struct ipu_psys_kcmd + *new_kcmd_tail[IPU_PSYS_CMD_PRIORITY_NUM]; +}; + +enum ipu_psys_cmd_state { + KCMD_STATE_NEW, + KCMD_STATE_START_PREPARED, + KCMD_STATE_STARTED, + KCMD_STATE_RUN_PREPARED, + KCMD_STATE_RUNNING, + KCMD_STATE_COMPLETE +}; + +int ipu_psys_fh_init(struct ipu_psys_fh *fh); +int ipu_psys_fh_deinit(struct ipu_psys_fh *fh); + +#endif /* IPU_PLATFORM_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-regs.h b/drivers/media/pci/intel/ipu4/ipu-platform-regs.h new file mode 100644 index 000000000000..e54b2b55afbf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-regs.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_REGS_H +#define IPU_PLATFORM_REGS_H + +#ifdef CONFIG_VIDEO_INTEL_IPU4P +#define IPU_ISYS_IOMMU0_OFFSET 0x000e0000 +#define IPU_ISYS_IOMMU1_OFFSET 0x000e0100 + +#define IPU_ISYS_OFFSET 0x00100000 +#define IPU_PSYS_OFFSET 0x00400000 + +#define IPU_PSYS_IOMMU0_OFFSET 0x000b0000 +#define IPU_PSYS_IOMMU1_OFFSET 0x000b0100 +#define IPU_PSYS_IOMMU1R_OFFSET 0x000b0600 + +/* the offset from IOMMU base register */ +#define IPU_MMU_L1_STREAM_ID_REG_OFFSET 0x0c +#define IPU_MMU_L2_STREAM_ID_REG_OFFSET 0x4c + +#define IPU_TPG0_ADDR_OFFSET 0x66c00 +#define IPU_TPG1_ADDR_OFFSET 0x6ec00 +#define IPU_CSI2BE_ADDR_OFFSET 0xba000 + +#define IPU_PSYS_MMU0_CTRL_OFFSET 0x08 + +#define IPU_GPOFFSET 0x66800 +#define IPU_COMBO_GPOFFSET 0x6e800 + +#define IPU_GPREG_MIPI_PKT_GEN0_SEL 0x1c +#define IPU_GPREG_MIPI_PKT_GEN1_SEL 0x1c + +#define IPU_REG_ISYS_ISA_ACC_IRQ_CTRL_BASE 0xb0c00 +#define IPU_REG_ISYS_A_IRQ_CTRL_BASE 0xbe200 +#define IPU_REG_ISYS_SIP0_IRQ_CTRL_BASE 0x66d00 +#define IPU_REG_ISYS_SIP1_IRQ_CTRL_BASE 0x6ed00 +#define IPU_REG_ISYS_SIP0_IRQ_CTRL_STATUS 0x66d08 +#define IPU_REG_ISYS_SIP1_IRQ_CTRL_STATUS 0x6ed08 +#define IPU_REG_ISYS_SIP0_IRQ_CTRL_CLEAR 0x66d0c +#define IPU_REG_ISYS_SIP1_IRQ_CTRL_CLEAR 0x6ed0c +#define IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(p) \ + ({ typeof(p) __p = (p); \ + __p > 0 ? (0x6cb00 + 0x800 * (__p - 1)) : (0x66300); }) +#define IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(p) \ + ({ typeof(p) __p = (p); \ + __p > 0 ? (0x6cc00 + 0x800 * (__p - 1)) : (0x66400); }) +#define IPU_ISYS_CSI2_A_IRQ_MASK GENMASK(0, 0) +#define IPU_ISYS_CSI2_B_IRQ_MASK GENMASK(1, 1) +#define IPU_ISYS_CSI2_C_IRQ_MASK GENMASK(2, 2) +#define IPU_ISYS_CSI2_D_IRQ_MASK GENMASK(3, 3) + +/* IRQ-related registers relative to ISYS_OFFSET */ +#define IPU_REG_ISYS_UNISPART_IRQ_EDGE 0x7c000 +#define IPU_REG_ISYS_UNISPART_IRQ_MASK 0x7c004 +#define IPU_REG_ISYS_UNISPART_IRQ_STATUS 0x7c008 +#define IPU_REG_ISYS_UNISPART_IRQ_CLEAR 0x7c00c +#define IPU_REG_ISYS_UNISPART_IRQ_ENABLE 0x7c010 +#define IPU_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x7c014 +#define IPU_REG_ISYS_UNISPART_SW_IRQ_REG 0x7c414 +#define IPU_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x7c418 +#define IPU_ISYS_UNISPART_IRQ_SW BIT(22) +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +#define IPU_ISYS_IOMMU0_OFFSET 0x000e0000 +#define IPU_ISYS_IOMMU1_OFFSET 0x000e0100 + +#define IPU_ISYS_OFFSET 0x00100000 +#define IPU_PSYS_OFFSET 0x00400000 + +#define IPU_PSYS_IOMMU0_OFFSET 0x000b0000 +#define IPU_PSYS_IOMMU1_OFFSET 0x000b0100 +#define IPU_PSYS_IOMMU1R_OFFSET 0x000b0600 + +/* the offset from IOMMU base register */ +#define IPU_MMU_L1_STREAM_ID_REG_OFFSET 0x0c +#define IPU_MMU_L2_STREAM_ID_REG_OFFSET 0x4c + +#define IPU_TPG0_ADDR_OFFSET 0x64800 +#define IPU_TPG1_ADDR_OFFSET 0x6f400 +#define IPU_CSI2BE_ADDR_OFFSET 0xba000 + +#define IPU_PSYS_MMU0_CTRL_OFFSET 0x08 + +#define IPU_GPOFFSET 0x67800 +#define IPU_COMBO_GPOFFSET 0x6f000 + +#define IPU_GPREG_MIPI_PKT_GEN0_SEL 0x24 +#define IPU_GPREG_MIPI_PKT_GEN1_SEL 0x1c + +/* IRQ-related registers relative to ISYS_OFFSET */ +#define IPU_REG_ISYS_UNISPART_IRQ_EDGE 0x7c000 +#define IPU_REG_ISYS_UNISPART_IRQ_MASK 0x7c004 +#define IPU_REG_ISYS_UNISPART_IRQ_STATUS 0x7c008 +#define IPU_REG_ISYS_UNISPART_IRQ_CLEAR 0x7c00c +#define IPU_REG_ISYS_UNISPART_IRQ_ENABLE 0x7c010 +#define IPU_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x7c014 +#define IPU_REG_ISYS_UNISPART_SW_IRQ_REG 0x7c414 +#define IPU_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x7c418 +#define IPU_ISYS_UNISPART_IRQ_SW BIT(30) +#endif /* CONFIG_VIDEO_INTEL_IPU4 */ + +#define IPU_ISYS_SPC_OFFSET 0x000000 +#define IPU_PSYS_SPC_OFFSET 0x000000 +#define IPU_ISYS_DMEM_OFFSET 0x008000 +#define IPU_PSYS_DMEM_OFFSET 0x008000 + +/* PKG DIR OFFSET in IMR in secure mode */ +#define IPU_PKG_DIR_IMR_OFFSET 0x40 + +/* PCI config registers */ +#define IPU_REG_PCI_PCIECAPHDR_PCIECAP 0x70 +#define IPU_REG_PCI_DEVICECAP 0x74 +#define IPU_REG_PCI_DEVICECTL_DEVICESTS 0x78 +#define IPU_REG_PCI_MSI_CAPID 0xac +#define IPU_REG_PCI_MSI_ADDRESS_LO 0xb0 +#define IPU_REG_PCI_MSI_ADDRESS_HI 0xb4 +#define IPU_REG_PCI_MSI_DATA 0xb8 +#define IPU_REG_PCI_PMCAP 0xd0 +#define IPU_REG_PCI_PMCS 0xd4 +#define IPU_REG_PCI_MANUFACTURING_ID 0xf8 +#define IPU_REG_PCI_IUNIT_ACCESS_CTRL_VIOL 0xfc + +/* ISYS registers */ +/* Isys DMA CIO info register */ +#define IPU_REG_ISYS_INFO_CIO_DMA0(a) (0x81810 + (a) * 0x40) +#define IPU_REG_ISYS_INFO_CIO_DMA1(a) (0x93010 + (a) * 0x40) +#define IPU_REG_ISYS_INFO_CIO_DMA_IS(a) (0xb0610 + (a) * 0x40) +#define IPU_ISYS_NUM_OF_DMA0_CHANNELS 16 +#define IPU_ISYS_NUM_OF_DMA1_CHANNELS 32 +#define IPU_ISYS_NUM_OF_IS_CHANNELS 4 +/*Isys Info register offsets*/ +#define IPU_REG_ISYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14 +#define IPU_REG_ISYS_INFO_SEG_CMEM_MASTER(a) (0x2C + (a * 12)) +#define IPU_REG_ISYS_INFO_SEG_XMEM_MASTER(a) (0x5C + (a * 12)) + +/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */ +#define IPU_REG_ISYS_CDC_THRESHOLD(i) (0x7c400 + ((i) * 4)) + +/*Iunit Info bits*/ +#define IPU_REG_PSYS_INFO_SEG_CMEM_MASTER(a) (0x2C + ((a) * 12)) +#define IPU_REG_PSYS_INFO_SEG_XMEM_MASTER(a) (0x5C + ((a) * 12)) +#define IPU_REG_PSYS_INFO_SEG_DATA_MASTER(a) (0x8C + ((a) * 12)) + +#define IPU_ISYS_REG_SPC_STATUS_CTRL 0x0 + +#define IPU_ISYS_SPC_STATUS_START BIT(1) +#define IPU_ISYS_SPC_STATUS_RUN BIT(3) +#define IPU_ISYS_SPC_STATUS_READY BIT(5) +#define IPU_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) +#define IPU_ISYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) + +#define IPU_PSYS_REG_SPC_STATUS_CTRL 0x0 + +#define IPU_PSYS_SPC_STATUS_START BIT(1) +#define IPU_PSYS_SPC_STATUS_RUN BIT(3) +#define IPU_PSYS_SPC_STATUS_READY BIT(5) +#define IPU_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12) +#define IPU_PSYS_SPC_STATUS_ICACHE_PREFETCH BIT(13) + +#define IPU_PSYS_REG_SPC_START_PC 0x4 +#define IPU_PSYS_REG_SPC_ICACHE_BASE 0x10 +#define IPU_PSYS_REG_SPP0_STATUS_CTRL 0x20000 +#define IPU_PSYS_REG_SPP1_STATUS_CTRL 0x30000 +#define IPU_PSYS_REG_SPF_STATUS_CTRL 0x40000 +#define IPU_PSYS_REG_ISP0_STATUS_CTRL 0x1C0000 +#define IPU_PSYS_REG_ISP1_STATUS_CTRL 0x240000 +#define IPU_PSYS_REG_ISP2_STATUS_CTRL 0x2C0000 +#define IPU_PSYS_REG_ISP3_STATUS_CTRL 0x340000 +#define IPU_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14 + +/* VC0 */ +#define IPU_INFO_ENABLE_SNOOP BIT(0) +#define IPU_INFO_IMR_DESTINED BIT(1) +#define IPU_INFO_REQUEST_DESTINATION_BUT_REGS 0 +#define IPU_INFO_REQUEST_DESTINATION_PRIMARY BIT(4) +#define IPU_INFO_REQUEST_DESTINATION_P2P (BIT(4) | BIT(5)) +/* VC1 */ +#define IPU_INFO_DEADLINE_PTR BIT(1) +#define IPU_INFO_ZLW BIT(2) +#define IPU_INFO_STREAM_ID_SET(a) ((a & 0xF) << 4) +#define IPU_INFO_ADDRESS_SWIZZ BIT(8) + +/* Trace unit related register definitions */ +#define TRACE_REG_MAX_ISYS_OFFSET 0x0fffff +#define TRACE_REG_MAX_PSYS_OFFSET 0xffffff +/* ISYS trace registers - offsets to isys base address */ +/* Trace unit base offset */ +#define TRACE_REG_IS_TRACE_UNIT_BASE 0x07d000 +/* Trace monitors */ +#define TRACE_REG_IS_SP_EVQ_BASE 0x001000 +/* GPC blocks */ +#define TRACE_REG_IS_SP_GPC_BASE 0x000800 +#define TRACE_REG_IS_ISL_GPC_BASE 0x0bd400 +#define TRACE_REG_IS_MMU_GPC_BASE 0x0e0B00 +/* CSI2 receivers */ +#define TRACE_REG_CSI2_TM_BASE 0x067a00 +#define TRACE_REG_CSI2_3PH_TM_BASE 0x06f200 +/* Trace timers */ +#define TRACE_REG_PS_GPREG_TRACE_TIMER_RST_N 0x060614 +#define TRACE_REG_IS_GPREG_TRACE_TIMER_RST_N 0x07c410 +#define TRACE_REG_GPREG_TRACE_TIMER_RST_OFF BIT(0) +/* SIG2CIO */ +/* 0 < n <= 8 */ +#define TRACE_REG_CSI2_SIG2SIO_GR_BASE(n) (0x067c00 + (n) * 0x20) +#define TRACE_REG_CSI2_SIG2SIO_GR_NUM 9 +/* 0 < n <= 8 */ +#define TRACE_REG_CSI2_PH3_SIG2SIO_GR_BASE(n) (0x06f600 + (n) * 0x20) +#define TRACE_REG_CSI2_PH3_SIG2SIO_GR_NUM 9 +/* PSYS trace registers - offsets to isys base address */ +/* Trace unit base offset */ +#define TRACE_REG_PS_TRACE_UNIT_BASE 0x3e0000 +/* Trace monitors */ +#define TRACE_REG_PS_SPC_EVQ_BASE 0x001000 +#define TRACE_REG_PS_SPP0_EVQ_BASE 0x021000 +#define TRACE_REG_PS_SPP1_EVQ_BASE 0x031000 +#define TRACE_REG_PS_SPF_EVQ_BASE 0x041000 +#define TRACE_REG_PS_ISP0_EVQ_BASE 0x1c1000 +#define TRACE_REG_PS_ISP1_EVQ_BASE 0x241000 +#define TRACE_REG_PS_ISP2_EVQ_BASE 0x2c1000 +#define TRACE_REG_PS_ISP3_EVQ_BASE 0x341000 +/* GPC blocks */ +#define TRACE_REG_PS_SPC_GPC_BASE 0x000800 +#define TRACE_REG_PS_SPP0_GPC_BASE 0x020800 +#define TRACE_REG_PS_SPP1_GPC_BASE 0x030800 +#define TRACE_REG_PS_SPF_GPC_BASE 0x040800 +#define TRACE_REG_PS_MMU_GPC_BASE 0x0b0b00 +#define TRACE_REG_PS_ISL_GPC_BASE 0x0fe800 +#define TRACE_REG_PS_ISP0_GPC_BASE 0x1c0800 +#define TRACE_REG_PS_ISP1_GPC_BASE 0x240800 +#define TRACE_REG_PS_ISP2_GPC_BASE 0x2c0800 +#define TRACE_REG_PS_ISP3_GPC_BASE 0x340800 + +/* common macros on each platform */ +#ifdef CONFIG_VIDEO_INTEL_IPU4 +#define IPU_ISYS_UNISPART_IRQ_CSI2(port) \ + ({ typeof(port) __port = (port); \ + __port < IPU_ISYS_MAX_CSI2_LEGACY_PORTS ? \ + ((0x8) << __port) : \ + (0x800 << (__port - IPU_ISYS_MAX_CSI2_LEGACY_PORTS)); }) +#define IPU_PSYS_GPDEV_IRQ_FWIRQ(n) (BIT(17) << (n)) +#endif +#ifdef CONFIG_VIDEO_INTEL_IPU4P +#define IPU_ISYS_UNISPART_IRQ_CSI2(port) \ + ((port) > 0 ? 0x10 : 0x8) +/* bit 20 for fw irqreg0 */ +#define IPU_PSYS_GPDEV_IRQ_FWIRQ(n) (BIT(20) << (n)) +#endif +/* IRQ-related registers in PSYS, relative to IPU_xx_PSYS_OFFSET */ +#define IPU_REG_PSYS_GPDEV_IRQ_EDGE 0x60200 +#define IPU_REG_PSYS_GPDEV_IRQ_MASK 0x60204 +#define IPU_REG_PSYS_GPDEV_IRQ_STATUS 0x60208 +#define IPU_REG_PSYS_GPDEV_IRQ_CLEAR 0x6020c +#define IPU_REG_PSYS_GPDEV_IRQ_ENABLE 0x60210 +#define IPU_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE 0x60214 +/* There are 8 FW interrupts, n = 0..7 */ +#define IPU_PSYS_GPDEV_FWIRQ0 0 +#define IPU_REG_PSYS_GPDEV_FWIRQ(n) (4 * (n) + 0x60100) +/* CDC Burst collector thresholds for psys - 4 FIFOs i= 0..3 */ +#define IPU_REG_PSYS_CDC_THRESHOLD(i) (0x60600 + ((i) * 4)) + +#endif /* IPU_REGS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu-platform-resources.h b/drivers/media/pci/intel/ipu4/ipu-platform-resources.h new file mode 100644 index 000000000000..59b2cd46c9f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform-resources.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_RESOURCES_H +#define IPU_PLATFORM_RESOURCES_H + +#include + +/* ia_css_psys_program_group_private.h */ +/* ia_css_psys_process_group_cmd_impl.h */ +#ifdef CONFIG_VIDEO_INTEL_IPU4P +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PROCESS_STRUCT 2 +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PROGRAM_MANIFEST 0 +#else +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PROCESS_STRUCT 4 +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PROGRAM_MANIFEST 4 +#endif +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT 4 + +/* ia_css_terminal_base_types.h */ +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_TERMINAL_STRUCT 5 + +/* ia_css_terminal_types.h */ +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT 6 + +/* ia_css_psys_terminal.c */ +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT 4 + +/* ia_css_program_group_data.h */ +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_FRAME_DESC_STRUCT 3 +#define IPU_FW_PSYS_N_FRAME_PLANES 6 +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_FRAME_STRUCT 4 + +/* ia_css_psys_buffer_set.h */ +#define IPU_FW_PSYS_N_PADDING_UINT8_IN_BUFFER_SET_STRUCT 5 + +enum { + IPU_FW_PSYS_CMD_QUEUE_COMMAND_ID, + IPU_FW_PSYS_CMD_QUEUE_DEVICE_ID, + IPU_FW_PSYS_CMD_QUEUE_PPG0_COMMAND_ID, + IPU_FW_PSYS_CMD_QUEUE_PPG1_COMMAND_ID, + IPU_FW_PSYS_N_PSYS_CMD_QUEUE_ID +}; + +enum { + IPU_FW_PSYS_GMEM_TYPE_ID = 0, + IPU_FW_PSYS_DMEM_TYPE_ID, + IPU_FW_PSYS_VMEM_TYPE_ID, + IPU_FW_PSYS_BAMEM_TYPE_ID, + IPU_FW_PSYS_PMEM_TYPE_ID, + IPU_FW_PSYS_N_MEM_TYPE_ID +}; + +enum ipu_mem_id { + IPU_FW_PSYS_VMEM0_ID = 0, + IPU_FW_PSYS_VMEM1_ID, + IPU_FW_PSYS_VMEM2_ID, + IPU_FW_PSYS_VMEM3_ID, + IPU_FW_PSYS_VMEM4_ID, + IPU_FW_PSYS_BAMEM0_ID, + IPU_FW_PSYS_BAMEM1_ID, + IPU_FW_PSYS_BAMEM2_ID, + IPU_FW_PSYS_BAMEM3_ID, + IPU_FW_PSYS_DMEM0_ID, + IPU_FW_PSYS_DMEM1_ID, + IPU_FW_PSYS_DMEM2_ID, + IPU_FW_PSYS_DMEM3_ID, + IPU_FW_PSYS_DMEM4_ID, + IPU_FW_PSYS_DMEM5_ID, + IPU_FW_PSYS_DMEM6_ID, + IPU_FW_PSYS_DMEM7_ID, + IPU_FW_PSYS_PMEM0_ID, + IPU_FW_PSYS_PMEM1_ID, + IPU_FW_PSYS_PMEM2_ID, + IPU_FW_PSYS_PMEM3_ID, + IPU_FW_PSYS_N_MEM_ID +}; + +enum { + IPU_FW_PSYS_DEV_CHN_DMA_EXT0_ID = 0, + IPU_FW_PSYS_DEV_CHN_GDC_ID, + IPU_FW_PSYS_DEV_CHN_DMA_EXT1_READ_ID, + IPU_FW_PSYS_DEV_CHN_DMA_EXT1_WRITE_ID, + IPU_FW_PSYS_DEV_CHN_DMA_INTERNAL_ID, + IPU_FW_PSYS_DEV_CHN_DMA_IPFD_ID, + IPU_FW_PSYS_DEV_CHN_DMA_ISA_ID, + IPU_FW_PSYS_DEV_CHN_DMA_FW_ID, +#ifdef CONFIG_VIDEO_INTEL_IPU4P + IPU_FW_PSYS_DEV_CHN_DMA_CMPRS_ID, +#endif + IPU_FW_PSYS_N_DEV_CHN_ID +}; + +enum { + IPU_FW_PSYS_SP_CTRL_TYPE_ID = 0, + IPU_FW_PSYS_SP_SERVER_TYPE_ID, + IPU_FW_PSYS_VP_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_ISA_TYPE_ID, + IPU_FW_PSYS_ACC_OSA_TYPE_ID, + IPU_FW_PSYS_GDC_TYPE_ID, + IPU_FW_PSYS_N_CELL_TYPE_ID +}; + +enum { + IPU_FW_PSYS_SP0_ID = 0, + IPU_FW_PSYS_SP1_ID, + IPU_FW_PSYS_SP2_ID, + IPU_FW_PSYS_VP0_ID, + IPU_FW_PSYS_VP1_ID, + IPU_FW_PSYS_VP2_ID, + IPU_FW_PSYS_VP3_ID, + IPU_FW_PSYS_ACC0_ID, + IPU_FW_PSYS_ACC1_ID, + IPU_FW_PSYS_ACC2_ID, + IPU_FW_PSYS_ACC3_ID, + IPU_FW_PSYS_ACC4_ID, + IPU_FW_PSYS_ACC5_ID, + IPU_FW_PSYS_ACC6_ID, + IPU_FW_PSYS_ACC7_ID, + IPU_FW_PSYS_GDC0_ID, + IPU_FW_PSYS_GDC1_ID, + IPU_FW_PSYS_N_CELL_ID +}; + +#define IPU_FW_PSYS_N_DEV_DFM_ID 0 +#define IPU_FW_PSYS_N_DATA_MEM_TYPE_ID (IPU_FW_PSYS_N_MEM_TYPE_ID - 1) +#define IPU_FW_PSYS_PROCESS_MAX_CELLS 1 +#define IPU_FW_PSYS_KERNEL_BITMAP_NOF_ELEMS 2 +#define IPU_FW_PSYS_RBM_NOF_ELEMS 2 + +#define IPU_FW_PSYS_DEV_CHN_DMA_EXT0_MAX_SIZE 30 +#define IPU_FW_PSYS_DEV_CHN_GDC_MAX_SIZE 4 +#define IPU_FW_PSYS_DEV_CHN_DMA_EXT1_READ_MAX_SIZE 30 +#define IPU_FW_PSYS_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE 20 +#define IPU_FW_PSYS_DEV_CHN_DMA_INTERNAL_MAX_SIZE 2 +#define IPU_FW_PSYS_DEV_CHN_DMA_IPFD_MAX_SIZE 5 +#define IPU_FW_PSYS_DEV_CHN_DMA_ISA_MAX_SIZE 2 +#define IPU_FW_PSYS_DEV_CHN_DMA_FW_MAX_SIZE 1 +#define IPU_FW_PSYS_DEV_CHN_DMA_CMPRS_MAX_SIZE 6 + +#define IPU_FW_PSYS_VMEM0_MAX_SIZE 0x0800 +#define IPU_FW_PSYS_VMEM1_MAX_SIZE 0x0800 +#define IPU_FW_PSYS_VMEM2_MAX_SIZE 0x0800 +#define IPU_FW_PSYS_VMEM3_MAX_SIZE 0x0800 +#define IPU_FW_PSYS_VMEM4_MAX_SIZE 0x0800 +#define IPU_FW_PSYS_BAMEM0_MAX_SIZE 0x0400 +#define IPU_FW_PSYS_BAMEM1_MAX_SIZE 0x0400 +#define IPU_FW_PSYS_BAMEM2_MAX_SIZE 0x0400 +#define IPU_FW_PSYS_BAMEM3_MAX_SIZE 0x0400 +#define IPU_FW_PSYS_DMEM0_MAX_SIZE 0x4000 +#define IPU_FW_PSYS_DMEM1_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM2_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM3_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM4_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM5_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM6_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_DMEM7_MAX_SIZE 0x1000 +#define IPU_FW_PSYS_PMEM0_MAX_SIZE 0x0500 +#define IPU_FW_PSYS_PMEM1_MAX_SIZE 0x0500 +#define IPU_FW_PSYS_PMEM2_MAX_SIZE 0x0500 +#define IPU_FW_PSYS_PMEM3_MAX_SIZE 0x0500 + +struct ipu_fw_psys_program_manifest { + u32 kernel_bitmap[IPU_FW_PSYS_KERNEL_BITMAP_NOF_ELEMS]; + u32 ID; + u32 program_type; + s32 parent_offset; + u32 program_dependency_offset; + u32 terminal_dependency_offset; + u16 size; + u16 int_mem_size[IPU_FW_PSYS_N_MEM_TYPE_ID]; + u16 ext_mem_size[IPU_FW_PSYS_N_DATA_MEM_TYPE_ID]; + u16 ext_mem_offset[IPU_FW_PSYS_N_DATA_MEM_TYPE_ID]; + u16 dev_chn_size[IPU_FW_PSYS_N_DEV_CHN_ID]; + u16 dev_chn_offset[IPU_FW_PSYS_N_DEV_CHN_ID]; + u8 cell_id; + u8 cell_type_id; + u8 program_dependency_count; + u8 terminal_dependency_count; +#ifndef CONFIG_VIDEO_INTEL_IPU4P + u8 reserved[IPU_FW_PSYS_N_PADDING_UINT8_IN_PROGRAM_MANIFEST]; +#endif +}; + +struct ipu_fw_psys_process { + u32 kernel_bitmap[IPU_FW_PSYS_KERNEL_BITMAP_NOF_ELEMS]; + u32 size; + u32 ID; + u32 program_idx; + u32 state; + s16 parent_offset; + u16 cell_dependencies_offset; + u16 terminal_dependencies_offset; + u16 int_mem_offset[IPU_FW_PSYS_N_MEM_TYPE_ID]; + u16 ext_mem_offset[IPU_FW_PSYS_N_DATA_MEM_TYPE_ID]; + u16 dev_chn_offset[IPU_FW_PSYS_N_DEV_CHN_ID]; + u8 cell_id; + u8 int_mem_id[IPU_FW_PSYS_N_MEM_TYPE_ID]; + u8 ext_mem_id[IPU_FW_PSYS_N_DATA_MEM_TYPE_ID]; + u8 cell_dependency_count; + u8 terminal_dependency_count; + u8 padding[IPU_FW_PSYS_N_PADDING_UINT8_IN_PROCESS_STRUCT]; +}; + +struct ipu_psys_resource_alloc; +struct ipu_fw_psys_process_group; +struct ipu_psys_resource_pool; +int ipu_psys_allocate_resources(const struct device *dev, + struct ipu_fw_psys_process_group *pg, + void *pg_manifest, + struct ipu_psys_resource_alloc *alloc, + struct ipu_psys_resource_pool *pool); +int ipu_psys_move_resources(const struct device *dev, + struct ipu_psys_resource_alloc *alloc, + struct ipu_psys_resource_pool *source_pool, + struct ipu_psys_resource_pool *target_pool); + +void ipu_psys_free_resources(struct ipu_psys_resource_alloc *alloc, + struct ipu_psys_resource_pool *pool); + +extern const struct ipu_fw_resource_definitions *res_defs; + +#endif /* IPU_PLATFORM_RESOURCES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu-platform.h b/drivers/media/pci/intel/ipu4/ipu-platform.h new file mode 100644 index 000000000000..32956125d5e7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu-platform.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef IPU_PLATFORM_H +#define IPU_PLATFORM_H + +#define IPU_NAME "intel-ipu4" +#define IPU_ISYS_NUM_STREAMS 8 /* Max 8 */ + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +#define IPU_CPD_FIRMWARE_NAME "ipu4_cpd_b0.bin" +#else +#define IPU_CPD_FIRMWARE_NAME "ipu4p_cpd.bin" +#endif + +/* + * The following definitions are encoded to the media_device's model field so + * that the software components which uses IPU driver can get the hw stepping + * information. + */ +#ifdef CONFIG_VIDEO_INTEL_IPU4 +#define IPU_MEDIA_DEV_MODEL_NAME "ipu4/Broxton B" +#else +#define IPU_MEDIA_DEV_MODEL_NAME "ipu4p" +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4 + +#define IPU_HW_BXT_P_B1_REV 0xa +#define IPU_HW_BXT_P_D0_REV 0xb +#define IPU_HW_BXT_P_E0_REV 0xc + +/* BXTP E0 has icache bug fixed */ +#define is_ipu_hw_bxtp_e0(isp) \ + ({ typeof(isp) __isp = (isp); \ + (__isp->pdev->device == IPU_PCI_ID && \ + __isp->pdev->revision == IPU_HW_BXT_P_E0_REV); }) +#endif + +/* declearations, definitions in ipu4.c */ +extern const struct ipu_isys_internal_pdata isys_ipdata; +extern const struct ipu_psys_internal_pdata psys_ipdata; +extern const struct ipu_buttress_ctrl isys_buttress_ctrl; +extern const struct ipu_buttress_ctrl psys_buttress_ctrl; + +/* definitions in ipu4-isys.c */ +extern struct ipu_trace_block isys_trace_blocks[]; +/* definitions in ipu4-psys.c */ +extern struct ipu_trace_block psys_trace_blocks[]; + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_inc b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_inc new file mode 100644 index 000000000000..48a4edea420f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_inc @@ -0,0 +1,26 @@ +IPU_ISYSLIB_INC = \ + -I$(IPU_ISYSLIB_ROOT)/buffer/interface \ + -I$(IPU_ISYSLIB_ROOT)/cell/interface \ + -I$(IPU_ISYSLIB_ROOT)/cell/src \ + -I$(IPU_ISYSLIB_ROOT)/device_access/interface \ + -I$(IPU_ISYSLIB_ROOT)/device_access/src \ + -I$(IPU_ISYSLIB_ROOT)/devices \ + -I$(IPU_ISYSLIB_ROOT)/devices/interface \ + -I$(IPU_ISYSLIB_ROOT)/devices/isys/bxtB0 \ + -I$(IPU_ISYSLIB_ROOT)/devices/src \ + -I$(IPU_ISYSLIB_ROOT)/fw_abi_common_types \ + -I$(IPU_ISYSLIB_ROOT)/fw_abi_common_types/cpu \ + -I$(IPU_ISYSLIB_ROOT)/isysapi/interface \ + -I$(IPU_ISYSLIB_ROOT)/pkg_dir/interface \ + -I$(IPU_ISYSLIB_ROOT)/pkg_dir/src \ + -I$(IPU_ISYSLIB_ROOT)/port/interface \ + -I$(IPU_ISYSLIB_ROOT)/reg_dump/src/isys/bxtB0_gen_reg_dump \ + -I$(IPU_ISYSLIB_ROOT)/regmem/interface \ + -I$(IPU_ISYSLIB_ROOT)/regmem/src \ + -I$(IPU_ISYSLIB_ROOT)/support \ + -I$(IPU_ISYSLIB_ROOT)/syscom/interface \ + -I$(IPU_ISYSLIB_ROOT)/syscom/src \ + -I$(IPU_ISYSLIB_ROOT)/trace/interface \ + -I$(IPU_ISYSLIB_ROOT)/utils/system_defs/ \ + -I$(IPU_ISYSLIB_ROOT)/vied \ + -I$(IPU_ISYSLIB_ROOT)/vied/vied/ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_src b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_src new file mode 100644 index 000000000000..c20760bdb5f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4isys_src @@ -0,0 +1,19 @@ +IPU_ISYSLIB_SRC = \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_private.o \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_public.o \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_public_trace.o + +ifeq ($(CONFIG_VIDEO_INTEL_IPU), m) +IPU_ISYSLIB_SRC += \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/buffer_access.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_input_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_output_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_shared_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/pkg_dir/src/ia_css_pkg_dir.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/queue.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/recv_port.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/send_port.o \ + $(IPU_ISYSLIB_ROOT_REL)/reg_dump/src/reg_dump_generic_bridge.o \ + $(IPU_ISYSLIB_ROOT_REL)/syscom/src/ia_css_syscom.o +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_inc b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_inc new file mode 100644 index 000000000000..abc61475e988 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_inc @@ -0,0 +1,52 @@ +IPU_PSYSLIB_INC = \ + -I$(IPU_PSYSLIB_ROOT)/buffer/interface \ + -I$(IPU_PSYSLIB_ROOT)/cell/interface \ + -I$(IPU_PSYSLIB_ROOT)/cell/src \ + -I$(IPU_PSYSLIB_ROOT)/client_pkg/interface \ + -I$(IPU_PSYSLIB_ROOT)/client_pkg/src \ + -I$(IPU_PSYSLIB_ROOT)/cpd/ \ + -I$(IPU_PSYSLIB_ROOT)/cpd/cpd_component/interface \ + -I$(IPU_PSYSLIB_ROOT)/cpd/cpd_metadata/interface \ + -I$(IPU_PSYSLIB_ROOT)/device_access/interface \ + -I$(IPU_PSYSLIB_ROOT)/device_access/src \ + -I$(IPU_PSYSLIB_ROOT)/devices \ + -I$(IPU_PSYSLIB_ROOT)/devices/interface \ + -I$(IPU_PSYSLIB_ROOT)/devices/psys/bxtB0 \ + -I$(IPU_PSYSLIB_ROOT)/devices/src \ + -I$(IPU_PSYSLIB_ROOT)/fw_abi_common_types \ + -I$(IPU_PSYSLIB_ROOT)/fw_abi_common_types/cpu \ + -I$(IPU_PSYSLIB_ROOT)/pkg_dir/interface \ + -I$(IPU_PSYSLIB_ROOT)/pkg_dir/src \ + -I$(IPU_PSYSLIB_ROOT)/port/interface \ + -I$(IPU_PSYSLIB_ROOT)/psys_private_pg/interface \ + -I$(IPU_PSYSLIB_ROOT)/psys_server/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/data/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/data/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/device/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/device/interface/bxtB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/dynamic/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/dynamic/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/kernel/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/param/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/param/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/psys_server_manifest/bxtB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/resource_model/bxtB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/sim/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/sim/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/static/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/static/src \ + -I$(IPU_PSYSLIB_ROOT)/reg_dump/src/psys/bxtB0_gen_reg_dump \ + -I$(IPU_PSYSLIB_ROOT)/regmem/interface \ + -I$(IPU_PSYSLIB_ROOT)/regmem/src \ + -I$(IPU_PSYSLIB_ROOT)/routing_bitmap/interface \ + -I$(IPU_PSYSLIB_ROOT)/routing_bitmap/src \ + -I$(IPU_PSYSLIB_ROOT)/support \ + -I$(IPU_PSYSLIB_ROOT)/syscom/interface \ + -I$(IPU_PSYSLIB_ROOT)/syscom/src \ + -I$(IPU_PSYSLIB_ROOT)/trace/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied \ + -I$(IPU_PSYSLIB_ROOT)/vied/vied/ \ + -I$(IPU_PSYSLIB_ROOT)/vied_nci_acb/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied_parameters/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied_parameters/src \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_src b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_src new file mode 100644 index 000000000000..8344bf569e13 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.ipu4psys_src @@ -0,0 +1,32 @@ +IPU_PSYSLIB_SRC = \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/buffer_access.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_input_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_output_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_shared_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/client_pkg/src/ia_css_client_pkg.o \ + $(IPU_PSYSLIB_ROOT_REL)/pkg_dir/src/ia_css_pkg_dir.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/queue.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/recv_port.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/send_port.o \ + $(IPU_PSYSLIB_ROOT_REL)/psys_server/src/bxt_spctrl_process_group_cmd_impl.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/data/src/ia_css_program_group_data.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/device/src/ia_css_psys_device.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_buffer_set.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_process.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_process_group.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_terminal.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/kernel/src/ia_css_kernel_bitmap.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/param/src/ia_css_program_group_param.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/sim/src/vied_nci_psys_system.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_program_group_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_program_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_terminal_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/reg_dump/src/reg_dump_generic_bridge.o \ + $(IPU_PSYSLIB_ROOT_REL)/routing_bitmap/src/ia_css_rbm.o \ + $(IPU_PSYSLIB_ROOT_REL)/routing_bitmap/src/ia_css_rbm_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/syscom/src/ia_css_syscom.o \ + $(IPU_PSYSLIB_ROOT_REL)/vied_parameters/src/ia_css_terminal.o \ + $(IPU_PSYSLIB_ROOT_REL)/vied_parameters/src/ia_css_terminal_manifest.o \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.isyslib b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.isyslib new file mode 100644 index 000000000000..c99c9a898c69 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.isyslib @@ -0,0 +1,47 @@ +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +PROGRAMS = isys_fw +SYSTEM = input_system_system +IPU_ISYSLIB_ROOT_REL = ipu4-css/lib2600 +IPU_ISYSLIB_ROOT = $(srcpath)/$(src)/$(IPU_ISYSLIB_ROOT_REL) + +include $(srcpath)/$(src)/ipu4-css/Makefile.ipu4isys_inc +include $(srcpath)/$(src)/ipu4-css/Makefile.ipu4isys_src + +# +# copy wrapper here only for isys usage, psys would use the original one +# +$(shell cp -f $(srcpath)/$(src)/../ipu-wrapper.c $(srcpath)/$(src)/ipu4-css/ipu-wrapper.c) + +intel-ipu4-isys-csslib-objs := \ + ipu4-css/libintel-ipu4.o \ + $(IPU_ISYSLIB_SRC) + +ifeq ($(CONFIG_VIDEO_INTEL_IPU), m) +intel-ipu4-isys-csslib-objs += ipu4-css/ipu-wrapper.o +endif +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-isys-csslib.o + +INCLUDES := -I$(srcpath)/$(src)/$(IPU_ISYSLIB_ROOT_REL) \ + -I$(srcpath)/$(src) \ + $(IPU_ISYSLIB_INC) + +DEFINES:= -D__HOST__ -D__KERNEL__ -DISYS_FPGA -DPSYS_FPGA + +DEFINES += -DSSID=1 +DEFINES += -DMMID=1 +DEFINES += -DPROGNAME=isys_fw +DEFINES += -DPROGMAP=\"isys_fw.map.h\" +DEFINES += -DSUBSYSTEM_INCLUDE=\ +DEFINES += -DCELL=input_system_unis_logic_sp_control_tile_sp +DEFINES += -DSPMAIN=isys_fw +DEFINES += -DRUN_INTEGRATION +DEFINES += -DDEBUG_SP_NCI +DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL=1 +DEFINES += -DHRT_ON_VIED_SUBSYSTEM_ACCESS=0 +DEFINES += -DHRT_USE_VIR_ADDRS +DEFINES += -DHRT_HW + +ccflags-y += $(INCLUDES) $(DEFINES) -fno-common diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.psyslib b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.psyslib new file mode 100644 index 000000000000..c93852bd09a1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/Makefile.psyslib @@ -0,0 +1,14 @@ +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +# note: this file only defines INCLUDES paths for psyslib +include $(srcpath)/$(src)/ipu4-css/Makefile.ipu4psys_inc + +IPU_PSYSLIB_ROOT = $(srcpath)/$(src)/ipu4-css/lib2600psys/lib +HOST_DEFINES += -DPSYS_SERVER_ON_SPC +HOST_DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL=1 + +ccflags-y += $(IPU_PSYSLIB_INC) $(HOST_DEFINES) + +obj-$(CONFIG_VIDEO_INTEL_IPU) += ipu4-css/lib2600psys/ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4-css/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/ipu-wrapper.c b/drivers/media/pci/intel/ipu4/ipu4-css/ipu-wrapper.c new file mode 100644 index 000000000000..9e06887dd857 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/ipu-wrapper.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include +#include + +#include +#include +#include +#include +#include + +#include "ipu-bus.h" +#include "ipu-dma.h" +#include "ipu-mmu.h" +#include "ipu-wrapper.h" +#include "vied_subsystem_access.h" +#include "vied_subsystem_access_initialization.h" +#include "shared_memory_map.h" +#include "shared_memory_access.h" + +struct wrapper_base { + void __iomem *sys_base; + const struct dma_map_ops *ops; + /* Protect shared memory buffers */ + spinlock_t lock; + struct list_head buffers; + u32 css_map_done; + struct device *dev; +}; + +static struct wrapper_base isys; +static struct wrapper_base psys; + +struct my_css_memory_buffer_item { + struct list_head list; + dma_addr_t iova; + unsigned long *addr; + size_t bytes; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif +}; + +static struct wrapper_base *get_mem_sub_system(int mmid) +{ + if (mmid == ISYS_MMID) + return &isys; + + if (mmid == PSYS_MMID) + return &psys; + WARN(1, "Invalid mem subsystem"); + return NULL; +} + +static struct wrapper_base *get_sub_system(int ssid) +{ + if (ssid == ISYS_SSID) + return &isys; + + if (ssid == PSYS_SSID) + return &psys; + WARN(1, "Invalid subsystem"); + return NULL; +} + +/* + * Subsystem access functions to access IUNIT MMIO space + */ +static void *host_addr(int ssid, u32 addr) +{ + if (ssid == ISYS_SSID) + return isys.sys_base + addr; + else if (ssid == PSYS_SSID) + return psys.sys_base + addr; + /* + * Calling WARN_ON is a bit brutal but better to capture wrong register + * accesses immediately. We have no way to return an error here. + */ + WARN_ON(1); + + return NULL; +} + +void vied_subsystem_store_32(unsigned int ssid, u32 addr, u32 data) +{ + writel(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store_16(unsigned int ssid, u32 addr, u16 data) +{ + writew(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store_8(unsigned int ssid, u32 addr, u8 data) +{ + writeb(data, host_addr(ssid, addr)); +} + +void vied_subsystem_store(unsigned int ssid, + u32 addr, const void *data, unsigned int size) +{ + void *dst = host_addr(ssid, addr); + + dev_dbg(get_sub_system(ssid)->dev, "access: %s 0x%x size: %d\n", + __func__, addr, size); + + for (; size >= sizeof(u32); size -= sizeof(u32), + dst += sizeof(u32), data += sizeof(u32)) { + writel(*(u32 *) data, dst); + } + if (size >= sizeof(u16)) { + writew(*(u16 *) data, dst); + size -= sizeof(u16), dst += sizeof(u16), data += sizeof(u16); + } + if (size) + writeb(*(u8 *) data, dst); +} + +u32 vied_subsystem_load_32(unsigned int ssid, u32 addr) +{ + return readl(host_addr(ssid, addr)); +} + +u16 vied_subsystem_load_16(unsigned int ssid, u32 addr) +{ + return readw(host_addr(ssid, addr)); +} + +u8 vied_subsystem_load_8(unsigned int ssid, u32 addr) +{ + return readb(host_addr(ssid, addr)); +} + +void vied_subsystem_load(unsigned int ssid, u32 addr, + void *data, unsigned int size) +{ + void *src = host_addr(ssid, addr); + + dev_dbg(get_sub_system(ssid)->dev, "access: %s 0x%x size: %d\n", + __func__, addr, size); + + for (; size >= sizeof(u32); size -= sizeof(u32), + src += sizeof(u32), data += sizeof(u32)) + *(u32 *) data = readl(src); + if (size >= sizeof(u16)) { + *(u16 *) data = readw(src); + size -= sizeof(u16), src += sizeof(u16), data += sizeof(u16); + } + if (size) + *(u8 *) data = readb(src); +} + +/* + * Initialize base address for subsystem + */ +void vied_subsystem_access_initialize(unsigned int system) +{ +} + +/* + * Shared memory access codes written by Dash Biswait, + * copied from FPGA environment + */ + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param mmid: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(unsigned int mmid, u64 host_ddr_addr, + size_t memory_size, size_t ps) +{ + return 0; +} + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(unsigned int mmid) +{ +} + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param ssid: id of subsystem + * \param mmid: id of ddr memory + * \param mmu_ps: size of page in bits + * \param mmu_pnrs: page numbers + * \param ddr_addr: base address + * \param inv_tlb: invalidate tbl + * \param sbt: set l1 base address + */ +int shared_memory_map_initialize(unsigned int ssid, unsigned int mmid, + size_t mmu_ps, size_t mmu_pnrs, u64 ddr_addr, + shared_memory_invalidate_mmu_tlb inv_tlb, + shared_memory_set_page_table_base_address sbt) +{ + return 0; +} + +/** + * \brief De-initialize the shared memory interface administration on the host. + */ +void shared_memory_map_uninitialize(unsigned int ssid, unsigned int mmid) +{ +} + +static u8 alloc_cookie; + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. + * \Returns NULL when insufficient memory available + */ +u64 shared_memory_alloc(unsigned int mmid, size_t bytes) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf; + unsigned long flags; + + dev_dbg(mine->dev, "%s: in, size: %zu\n", __func__, bytes); + + if (!bytes) + return (unsigned long)&alloc_cookie; + + might_sleep(); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return 0; + + /*alloc using ipu dma driver */ + buf->bytes = PAGE_ALIGN(bytes); + + buf->addr = dma_alloc_attrs(mine->dev, buf->bytes, &buf->iova, + GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + if (!buf->addr) { + kfree(buf); + return 0; + } + + spin_lock_irqsave(&mine->lock, flags); + list_add(&buf->list, &mine->buffers); + spin_unlock_irqrestore(&mine->lock, flags); + + return (unsigned long)buf->addr; +} + +/** + * \brief Free (DDR) shared memory space. + */ +void shared_memory_free(unsigned int mmid, u64 addr) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf = NULL; + unsigned long flags; + + if ((void *)(unsigned long)addr == &alloc_cookie) + return; + + might_sleep(); + + dev_dbg(mine->dev, "looking for iova %8.8llx\n", addr); + + spin_lock_irqsave(&mine->lock, flags); + list_for_each_entry(buf, &mine->buffers, list) { + dev_dbg(mine->dev, "buffer addr %8.8lx\n", (long)buf->addr); + if ((long)buf->addr != addr) + continue; + + dev_dbg(mine->dev, "found it!\n"); + list_del(&buf->list); + spin_unlock_irqrestore(&mine->lock, flags); + dma_free_attrs(mine->dev, buf->bytes, buf->addr, buf->iova, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &buf->attrs +#else + buf->attrs +#endif + ); + kfree(buf); + return; + } + dev_warn(mine->dev, "Can't find mem object %8.8llx\n", addr); + spin_unlock_irqrestore(&mine->lock, flags); +} + +/** + * \brief Convert a host virtual address to a CSS virtual address and + * \update the MMU. + */ +u32 shared_memory_map(unsigned int ssid, unsigned int mmid, u64 addr) +{ + struct wrapper_base *mine = get_mem_sub_system(mmid); + struct my_css_memory_buffer_item *buf = NULL; + unsigned long flags; + + if ((void *)(unsigned long)addr == &alloc_cookie) + return 0; + + spin_lock_irqsave(&mine->lock, flags); + list_for_each_entry(buf, &mine->buffers, list) { + dev_dbg(mine->dev, "%s %8.8lx\n", __func__, (long)buf->addr); + if ((long)buf->addr != addr) + continue; + + dev_dbg(mine->dev, "mapped!!\n"); + spin_unlock_irqrestore(&mine->lock, flags); + return buf->iova; + } + dev_err(mine->dev, "Can't find mapped object %8.8llx\n", addr); + spin_unlock_irqrestore(&mine->lock, flags); + return 0; +} + +/** + * \brief Free a CSS virtual address and update the MMU. + */ +void shared_memory_unmap(unsigned int ssid, unsigned int mmid, u32 addr) +{ +} + +/** + * \brief Store a byte into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_8(unsigned int mmid, u64 addr, u8 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u8 *)(unsigned long) addr) = data; + /*Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u8)); +} + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_16(unsigned int mmid, u64 addr, u16 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u16 *)(unsigned long) addr) = data; + /*Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, sizeof(u16)); +} + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store_32(unsigned int mmid, u64 addr, u32 data) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%x\n", + __func__, addr, data); + + *((u32 *)(unsigned long) addr) = data; + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, sizeof(u32)); +} + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_store(unsigned int mmid, u64 addr, const void *data, + size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%lx bytes = 0x%zx\n", __func__, + (unsigned long)addr, bytes); + + if (!data) { + dev_err(get_mem_sub_system(mmid)->dev, + "%s: data ptr is null\n", __func__); + } else { + const u8 *pdata = data; + u8 *paddr = (u8 *)(unsigned long)addr; + size_t i = 0; + + for (; i < bytes; ++i) + *paddr++ = *pdata++; + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long) addr, bytes); + } +} + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host + * \virtual address + */ +void shared_memory_zero(unsigned int mmid, u64 addr, size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx data = 0x%zx\n", + __func__, (unsigned long long)addr, bytes); + + memset((void *)(unsigned long)addr, 0, bytes); + clflush_cache_range((void *)(unsigned long)addr, bytes); +} + +/** + * \brief Load a byte from (DDR) shared memory space using a host + * \virtual address + */ +u8 shared_memory_load_8(unsigned int mmid, u64 addr) +{ + u8 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u8)); + data = *(u8 *)(unsigned long) addr; + return data; +} + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host + * \virtual address + */ +u16 shared_memory_load_16(unsigned int mmid, u64 addr) +{ + u16 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u16)); + data = *(u16 *)(unsigned long)addr; + return data; +} + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host + * \virtual address + */ +u32 shared_memory_load_32(unsigned int mmid, u64 addr) +{ + u32 data = 0; + + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%llx\n", __func__, addr); + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, sizeof(u32)); + data = *(u32 *)(unsigned long)addr; + return data; +} + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host + * \virtual address + */ +void shared_memory_load(unsigned int mmid, u64 addr, void *data, size_t bytes) +{ + dev_dbg(get_mem_sub_system(mmid)->dev, + "access: %s: Enter addr = 0x%lx bytes = 0x%zx\n", __func__, + (unsigned long)addr, bytes); + + if (!data) { + dev_err(get_mem_sub_system(mmid)->dev, + "%s: data ptr is null\n", __func__); + + } else { + u8 *pdata = data; + u8 *paddr = (u8 *)(unsigned long)addr; + size_t i = 0; + + /* Invalidate the cache lines to flush the content to ddr. */ + clflush_cache_range((void *)(unsigned long)addr, bytes); + for (; i < bytes; ++i) + *pdata++ = *paddr++; + } +} + +static int init_wrapper(struct wrapper_base *sys) +{ + INIT_LIST_HEAD(&sys->buffers); + spin_lock_init(&sys->lock); + return 0; +} + +/* + * Wrapper driver set base address for library use + */ +void ipu_wrapper_init(int mmid, struct device *dev, void __iomem *base) +{ + struct wrapper_base *sys = get_mem_sub_system(mmid); + + init_wrapper(sys); + sys->dev = dev; + sys->sys_base = base; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/buffer.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/buffer.mk new file mode 100644 index 000000000000..c00a1133b440 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/buffer.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is BUFFER + +ifdef _H_BUFFER_MK +$(error ERROR: buffer.mk included multiple times, please check makefile) +else +_H_BUFFER_MK=1 +endif + +BUFFER_DIR=$${MODULES_DIR}/buffer + +BUFFER_INTERFACE=$(BUFFER_DIR)/interface +BUFFER_SOURCES_CPU=$(BUFFER_DIR)/src/cpu +BUFFER_SOURCES_CSS=$(BUFFER_DIR)/src/css + +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_output_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_input_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_shared_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/buffer_access.c +BUFFER_HOST_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_HOST_CPPFLAGS += -I$${MODULES_DIR}/support + +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_input_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_output_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_shared_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/buffer_access.c + +BUFFER_FW_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_FW_CPPFLAGS += -I$${MODULES_DIR}/support diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_access.h new file mode 100644 index 000000000000..e5fe647742c9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_access.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_ACCESS_H +#define __BUFFER_ACCESS_H + +#include "buffer_type.h" +/* #def to keep consistent the buffer load interfaces for host and css */ +#define IDM 0 + +void +buffer_load( + buffer_address address, + void *data, + unsigned int size, + unsigned int mm_id); + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int size, + unsigned int mm_id); + +#endif /* __BUFFER_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_type.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_type.h new file mode 100644 index 000000000000..de51f2394158 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/buffer_type.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_TYPE_H +#define __BUFFER_TYPE_H + +/* portable access to buffers in DDR */ + +#ifdef __VIED_CELL +typedef unsigned int buffer_address; +#else +/* workaround needed because shared_memory_access.h uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_access.h" +typedef host_virtual_address_t buffer_address; +#endif + +#endif /* __BUFFER_TYPE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_buffer_address.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_buffer_address.h new file mode 100644 index 000000000000..137bfb1fda16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_buffer_address.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_ADDRESS_H +#define __IA_CSS_BUFFER_ADDRESS_H + +#include "type_support.h" + +typedef uint32_t ia_css_buffer_address; /* CSS virtual address */ + +#define ia_css_buffer_address_null ((ia_css_buffer_address)0) + +#endif /* __IA_CSS_BUFFER_ADDRESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer.h new file mode 100644 index 000000000000..4e92e35b6184 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_H +#define __IA_CSS_INPUT_BUFFER_H + + +/* Input Buffers */ + +/* A CSS input buffer is a buffer in DDR that can be written by the CPU, + * and that can be read by CSS hardware, after the buffer has been handed over. + * Examples: command buffer, input frame buffer, parameter buffer + * An input buffer must be mapped into the CPU address space before it can be + * written by the CPU. + * After mapping, writing, and unmapping, the buffer can be handed over to the + * firmware. An input buffer is handed over to the CSS by mapping it to the + * CSS address space (by the CPU), and by passing the resulting CSS (virtial) + * address of the input buffer to the DA CSS hardware. + * The firmware can read from an input buffer as soon as it has been received + * CSS virtual address. + * The firmware should not write into an input buffer. + * The firmware hands over the input buffer (back to the CPU) by sending the + * buffer handle via a response. The host should unmap the buffer, + * before reusing it. + * The firmware should not read from the input buffer after returning the + * buffer handle to the CPU. + * + * A buffer may be pre-mapped to the CPU and/or to the CSS upon allocation, + * depending on the allocator's preference. In case of pre-mapped buffers, + * the map and unmap functions will only manage read and write access. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_input_buffer; /* input buffer handle */ +typedef void *ia_css_input_buffer_cpu_address; /* CPU virtual address */ +/* CSS virtual address */ +typedef ia_css_buffer_address ia_css_input_buffer_css_address; + +#endif /* __IA_CSS_INPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h new file mode 100644 index 000000000000..d3d01353ce43 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_CPU_H +#define __IA_CSS_INPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_input_buffer.h" + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b); + + +#endif /* __IA_CSS_INPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer.h new file mode 100644 index 000000000000..2c310ea92c6a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer.h @@ -0,0 +1,30 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_H +#define __IA_CSS_OUTPUT_BUFFER_H + +/* Output Buffers */ +/* A CSS output buffer a buffer in DDR that can be written by CSS hardware + * and that can be read by the host, after the buffer has been handed over + * Examples: output frame buffer + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_output_buffer; +typedef void *ia_css_output_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_output_buffer_css_address; + +#endif /* __IA_CSS_OUTPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h new file mode 100644 index 000000000000..0299fc3b7eb6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h @@ -0,0 +1,48 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_CPU_H +#define __IA_CSS_OUTPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_output_buffer.h" + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b); +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b); + + +#endif /* __IA_CSS_OUTPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_return_token.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_return_token.h new file mode 100644 index 000000000000..440161d2f32b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_return_token.h @@ -0,0 +1,54 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RETURN_TOKEN_H +#define __IA_CSS_RETURN_TOKEN_H + +#include "storage_class.h" +#include "assert_support.h" /* For CT_ASSERT */ + +/* ia_css_return_token: data item of exacly 8 bytes (64 bits) + * which can be used to pass a return token back to the host +*/ +typedef unsigned long long ia_css_return_token; + +STORAGE_CLASS_INLINE void +ia_css_return_token_copy(ia_css_return_token *to, + const ia_css_return_token *from) +{ + /* copy a return token on VIED processor */ + int *dst = (int *)to; + int *src = (int *)from; + + dst[0] = src[0]; + dst[1] = src[1]; +} + +STORAGE_CLASS_INLINE void +ia_css_return_token_zero(ia_css_return_token *to) +{ + /* zero return token on VIED processor */ + int *dst = (int *)to; + + dst[0] = 0; + dst[1] = 0; +} + +STORAGE_CLASS_INLINE void _check_return_token_size(void) +{ + CT_ASSERT(sizeof(int) == 4); + CT_ASSERT(sizeof(ia_css_return_token) == 8); +} + +#endif /* __IA_CSS_RETURN_TOKEN_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer.h new file mode 100644 index 000000000000..558ec679f98a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_H +#define __IA_CSS_SHARED_BUFFER_H + +/* Shared Buffers */ +/* A CSS shared buffer is a buffer in DDR that can be read and written by the + * CPU and CSS. + * Both the CPU and CSS can have the buffer mapped simultaneously. + * Access rights are not managed by this interface, this could be done by means + * the read and write pointer of a queue, for example. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_shared_buffer; +typedef void *ia_css_shared_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_shared_buffer_css_address; + +#endif /* __IA_CSS_SHARED_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h new file mode 100644 index 000000000000..ff62914f99dc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_CPU_H +#define __IA_CSS_SHARED_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_shared_buffer.h" + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b); + +#endif /* __IA_CSS_SHARED_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/buffer_access.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/buffer_access.c new file mode 100644 index 000000000000..f0c617fe501a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/buffer_access.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* implementation of buffer access from the CPU */ +/* using shared_memory interface */ + +#include "buffer_access.h" +#include "vied/shared_memory_access.h" + +void +buffer_load( + buffer_address address, + void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_load(mm_id, address, data, bytes); +} + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_store(mm_id, address, data, bytes); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.c new file mode 100644 index 000000000000..146d4109de44 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.c @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* provided interface */ +#include "ia_css_buffer.h" + +/* used interfaces */ +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + +ia_css_buffer_t +ia_css_buffer_alloc(vied_subsystem_t sid, vied_memory_t mid, unsigned int size) +{ + ia_css_buffer_t b; + + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + + b->css_address = shared_memory_map(sid, mid, b->mem); + b->size = size; + return b; +} + + +void +ia_css_buffer_free(vied_subsystem_t sid, vied_memory_t mid, ia_css_buffer_t b) +{ + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.h new file mode 100644 index 000000000000..0f99a06e9a89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_buffer.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_H +#define __IA_CSS_BUFFER_H + +/* workaround: needed because uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_map.h" + +typedef enum { + buffer_unmapped, /* buffer is not accessible by cpu, nor css */ + buffer_write, /* output buffer: css has write access */ + /* input buffer: cpu has write access */ + buffer_read, /* input buffer: css has read access */ + /* output buffer: cpu has read access */ + buffer_cpu, /* shared buffer: cpu has read/write access */ + buffer_css /* shared buffer: css has read/write access */ +} buffer_state; + +struct ia_css_buffer_s { + /* number of bytes bytes allocated */ + unsigned int size; + /* allocated virtual memory object */ + host_virtual_address_t mem; + /* virtual address to be used on css/firmware */ + vied_virtual_address_t css_address; + /* virtual address to be used on cpu/host */ + void *cpu_address; + buffer_state state; +}; + +typedef struct ia_css_buffer_s *ia_css_buffer_t; + +ia_css_buffer_t +ia_css_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_buffer_t b); + +#endif /* __IA_CSS_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c new file mode 100644 index 000000000000..2a128795d03e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c @@ -0,0 +1,184 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_input_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_input_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map input buffer to CPU address space, acquire write access */ + b->state = buffer_write; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_write) + return NULL; + + /* unmap input buffer from CPU address space, release write access */ + b->state = buffer_unmapped; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + + /* now flush the cache */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_read) + return 0; + + /* unmap input buffer from CSS address space, release read access */ + b->state = buffer_unmapped; + + /* input buffer only, no need to invalidate cache */ + + return (ia_css_input_buffer_css_address)b->css_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c new file mode 100644 index 000000000000..30bc8d52a5a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c @@ -0,0 +1,181 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_output_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_output_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map output buffer to CSS address space, acquire write access */ + b->state = buffer_write; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_write) + return 0; + + /* unmap output buffer from CSS address space, release write access */ + b->state = buffer_unmapped; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* now invalidate the cache */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b->cpu_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b->cpu_address; +} + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_read) + return NULL; + + /* unmap output buffer from CPU address space, release read access */ + b->state = buffer_unmapped; + + /* output only, no need to flush cache */ + + return b->cpu_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c new file mode 100644 index 000000000000..92b7110644fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c @@ -0,0 +1,187 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_shared_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_shared_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map shared buffer to CPU address space */ + b->state = buffer_cpu; + + return b->cpu_address; +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_cpu) + return NULL; + + /* unmap shared buffer from CPU address space */ + b->state = buffer_unmapped; + + return b->cpu_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map shared buffer to CSS address space */ + b->state = buffer_css; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_css) + return 0; + + /* unmap shared buffer from CSS address space */ + b->state = buffer_unmapped; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to CSS after it was modified by the CPU */ + /* flush cache to ddr */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to the CPU after it has been modified by CSS */ +#ifndef HRT_HW + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* flush cache to ddr */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/cell.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/cell.mk new file mode 100644 index 000000000000..fa5e65022601 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/cell.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef _CELL_MK_ +_CELL_MK_ = 1 + + +CELL_DIR=$${MODULES_DIR}/cell +CELL_INTERFACE=$(CELL_DIR)/interface +CELL_SOURCES=$(CELL_DIR)/src + +CELL_HOST_FILES = +CELL_FW_FILES = + +CELL_HOST_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +CELL_FW_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +ifdef 0 +# Disabled until it is decided to go this way or not +include $(MODULES_DIR)/device_access/device_access.mk +CELL_HOST_FILES += $(DEVICE_ACCESS_HOST_FILES) +CELL_FW_FILES += $(DEVICE_ACCESS_FW_FILES) +CELL_HOST_CPPFLAGS += $(DEVICE_ACCESS_HOST_CPPFLAGS) +CELL_FW_CPPFLAGS += $(DEVICE_ACCESS_FW_CPPFLAGS) +endif + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/interface/ia_css_cell.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/interface/ia_css_cell.h new file mode 100644 index 000000000000..3fac3c791b6e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/interface/ia_css_cell.h @@ -0,0 +1,112 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_H +#define __IA_CSS_CELL_H + +#include "storage_class.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +#if 0 /* To be implemented after completing cell device properties */ +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_debug_pc(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stall_bits(unsigned int ssid, unsigned int cell_id); +#endif + +/* configure master ports */ + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value); + +/* Access memories */ + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr); + +/***********************************************************************/ + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch); + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id); + +/* include inline implementation */ +#include "ia_css_cell_impl.h" + +#endif /* __IA_CSS_CELL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/src/ia_css_cell_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/src/ia_css_cell_impl.h new file mode 100644 index 000000000000..60b2e234da1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/cell/src/ia_css_cell_impl.h @@ -0,0 +1,272 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_IMPL_H +#define __IA_CSS_CELL_IMPL_H + +#include "ia_css_cell.h" + +#include "ia_css_cmem.h" +#include "ipu_device_cell_properties.h" +#include "storage_class.h" +#include "assert_support.h" +#include "platform_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_regs_addr(unsigned int cell_id) +{ + /* mem_id 0 is for registers */ + return ipu_device_cell_memory_address(cell_id, 0); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_dmem_addr(unsigned int cell_id) +{ + /* mem_id 1 is for DMEM */ + return ipu_device_cell_memory_address(cell_id, 1); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ipu_device_cell_memory_address( + cell_id, mem_id) + addr, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr) +{ + return ia_css_cmem_load_32( + ssid, ipu_device_cell_memory_address(cell_id, mem_id) + addr); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id) +{ + return ia_css_cmem_load_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = ia_css_cell_get_stat_ctrl(ssid, cell_id); + /* READY must be 1, START must be 0 */ + return (reg & (1 << IPU_DEVICE_CELL_STAT_CTRL_READY_BIT)) && + ((~reg) & (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT)); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc) +{ + /* set start PC */ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_START_PC_REG_ADDRESS, pc); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = 1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + unsigned int reg; + + reg = value << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id) +{ + ia_css_cell_start_prefetch(ssid, cell_id, 0); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch) +{ + unsigned int reg = 0; + + /* Set run bit and start bit */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT); + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT); + /* Invalidate the icache */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); + /* Optionally enable prefetching */ + reg |= ((prefetch == 1) ? + (1 << IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT) : + 0); + + /* store into register */ + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id) +{ + do { + ia_css_sleep(); + } while (!ia_css_cell_is_ready(ssid, cell_id)); +}; + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS, value); +} + +/* master port configuration */ + + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) + +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments, segment_size; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + segment_size = ipu_device_cell_master_segment_size(cell, master); + + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + value += segment_size; + } +} + +#endif /* __IA_CSS_CELL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/isys/subsystem_bxtB0.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/isys/subsystem_bxtB0.mk new file mode 100644 index 000000000000..da142032349f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/isys/subsystem_bxtB0.mk @@ -0,0 +1,60 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +############################################################################ +# This file is used to specify versions and properties of ISYS firmware +# components. Please note that these are subsystem specific. System specific +# properties should go to system_$IPU_SYSVER.mk. Also the device versions +# should be defined under "devices" or should be taken from the SDK. +############################################################################ + +############################################################################ +# FIRMWARE RELATED VARIABLES +############################################################################ + +# Activate loading params and storing stats DDR<->REGs with DMA +ISYS_USE_ISA_DMA = 1 +# Used in ISA module +ISYS_ISL_DPC_DPC_V2 = 0 + +# Specification for Isys server's fixed globals' locations +REGMEM_OFFSET = 0 # Starting from 0 +REGMEM_SIZE = 34 +REGMEM_WORD_BYTES = 4 +FW_LOAD_NO_OF_REQUEST_OFFSET = 136 # Taken from REGMEM_OFFSET + REGMEM_SIZE_BYTES +FW_LOAD_NO_OF_REQUEST_SIZE_BYTES = 4 + +# Workarounds: + +# This WA is not to pipeline store frame commands for SID processors that control a Str2Vec (ISA output) +WA_HSD1304553438 = 1 + +# Larger than specified frames that complete mid-line +WA_HSD1209062354 = 1 + +# WA to disable clock gating for the devices in the CSI receivers needed for using the mipi_pkt_gen device +WA_HSD1805168877 = 0 + +# Support IBUF soft-reset at stream start +SOFT_RESET_IBUF_STREAM_START_SUPPORT = 1 + +############################################################################ +# TESTING RELATED VARIABLES +############################################################################ + +# TODO: This define should be entirely removed. +# Used in mipi_capture +ISYS_DISABLE_VERIFY_RECEIVED_SOF_EOF = 0 + +ISYS_ACCESS_BLOCKER_VERSION = v1 diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/system_bxtB0.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/system_bxtB0.mk new file mode 100644 index 000000000000..24d079b40516 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/config/system_bxtB0.mk @@ -0,0 +1,88 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +LOGICAL_FW_INPUT_SYSTEM = input_system_system +LOGICAL_FW_PROCESSING_SYSTEM = processing_system_system +LOGICAL_FW_IPU_SYSTEM = css_broxton_system +LOGICAL_FW_ISP_SYSTEM = isp2601_default_system +SP_CONTROL_CELL = sp2601_control +SP_PROXY_CELL = sp2601_proxy +SP_FP_CELL = sp2601_fp +ISP_CELL = isp2601 +# The non-capital define isp2601 is used in the sdk, in order to distinguish +# between different isp versions the ISP_CELL_IDENTIFIER define is added. +ISP_CELL_IDENTIFIER = ISP2601 +HAS_IPFD = 1 +HAS_S2M_IN_ISYS_ISL_NONSOC_PATH = 0 +HAS_S2V_IN_ISYS_ISL_NONSOC_PATH = 1 +# ISL-IS non-SoC path has ISA without PAF and DPC-Pext support for IPU4-B0 +HAS_ISA_IN_ISYS_ISL = 1 +HAS_PAF_IN_ISYS_ISL = 0 +HAS_DPC_PEXT_IN_ISYS_ISL = 0 +HAS_PMA_IF = 0 + +HAS_MIPIBE_IN_PSYS_ISL = 1 + +HAS_VPLESS_SUPPORT = 0 + +DLI_SYSTEM = hive_isp_css_2600_system +RESOURCE_MANAGER_VERSION = v1 +MEM_RESOURCE_VALIDATION_ERROR = 0 +OFS_SCALER_1_4K_TILEY_422_SUPPORT= 1 +PROGDESC_ACC_SYMBOLS_VERSION = v1 +DEVPROXY_INTERFACE_VERSION = v1 +FW_ABI_IPU_TYPES_VERSION = v1 + +HAS_ONLINE_MODE_SUPPORT_IN_ISYS_PSYS = 0 + +MMU_INTERFACE_VERSION = v1 +DEVICE_ACCESS_VERSION = v2 +PSYS_SERVER_VERSION = v2 +PSYS_SERVER_LOADER_VERSION = v1 +PSYS_HW_VERSION = BXT_B0_HW + +# Enable FW_DMA for loading firmware +PSYS_SERVER_ENABLE_FW_LOAD_DMA = 1 + +NCI_SPA_VERSION = v1 +MANIFEST_TOOL_VERSION = v2 +PSYS_CON_MGR_TOOL_VERSION = v1 +# TODO: Should be removed after performance issues OTF are solved +PSYS_PROC_MGR_VERSION = v1 +IPU_RESOURCES_VERSION = v1 + +HAS_ACC_CLUSTER_PAF_PAL = 0 +HAS_ACC_CLUSTER_PEXT_PAL = 0 +HAS_ACC_CLUSTER_GBL_PAL = 1 + +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PARAMBINTOOL_ISA_INIT_VERSION = bxtB0 + +# Select EQC2EQ version +# Version 1: uniform address space, equal EQ addresses regardless of EQC device +# Version 2: multiple addresses per EQ, depending on location of EQC device +EQC2EQ_VERSION = v1 + +# Select DMA instance for fw_load +FW_LOAD_DMA_INSTANCE = NCI_DMA_FW + +HAS_DMA_FW = 1 + +HAS_SIS = 0 +HAS_IDS = 1 + +PSYS_SERVER_ENABLE_TPROXY = 1 +PSYS_SERVER_ENABLE_DEVPROXY = 1 +NCI_OFS_VERSION = v1 diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/device_access.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/device_access.mk new file mode 100644 index 000000000000..1629d9af803b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/device_access.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifndef _DEVICE_ACCESS_MK_ +_DEVICE_ACCESS_MK_ = 1 + +# DEVICE_ACCESS_VERSION= +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk + +DEVICE_ACCESS_DIR=$${MODULES_DIR}/device_access +DEVICE_ACCESS_INTERFACE=$(DEVICE_ACCESS_DIR)/interface +DEVICE_ACCESS_SOURCES=$(DEVICE_ACCESS_DIR)/src + +DEVICE_ACCESS_HOST_FILES = + +DEVICE_ACCESS_FW_FILES = + +DEVICE_ACCESS_HOST_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS += \ + -I$(DEVICE_ACCESS_SOURCES)/$(DEVICE_ACCESS_VERSION) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_cmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_cmem.h new file mode 100644 index 000000000000..3dc47c29fcab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_cmem.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_H +#define __IA_CSS_CMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_cmem_address_t; +#else +#include +typedef vied_subsystem_address_t ia_css_cmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size); + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_cmem_cell.h" +#else +#include "ia_css_cmem_host.h" +#endif + +#endif /* __IA_CSS_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem.h new file mode 100644 index 000000000000..de2b94d8af54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_H +#define __IA_CSS_XMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_xmem_address_t; +#else +#include +typedef host_virtual_address_t ia_css_xmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_xmem_cell.h" +#else +#include "ia_css_xmem_host.h" +#endif + +#endif /* __IA_CSS_XMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem_cmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem_cmem.h new file mode 100644 index 000000000000..57aab3323c73 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/interface/ia_css_xmem_cmem.h @@ -0,0 +1,35 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_H +#define __IA_CSS_XMEM_CMEM_H + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size); + +/* include inline implementation */ +#include "ia_css_xmem_cmem_impl.h" + +#endif /* __IA_CSS_XMEM_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_cmem_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_cmem_host.h new file mode 100644 index 000000000000..22799e67214c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_cmem_host.h @@ -0,0 +1,121 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_HOST_H +#define __IA_CSS_CMEM_HOST_H + +/* This file is an inline implementation for the interface ia_css_cmem.h + * and should only be included there. */ + +#include "assert_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + return vied_subsystem_load_32(ssid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_cond_cmem_load_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + return vied_subsystem_load_32(ssid, address); + else + return 0; +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cond_cmem_store_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address, uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + *data32 = ia_css_cmem_load_32(ssid, address); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, *data32); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size) +{ + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, 0); + address += 4; + } +} + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p) +{ + NOT_USED(base_addr); + return (ia_css_cmem_address_t)(uintptr_t)p; +} + +#endif /* __IA_CSS_CMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h new file mode 100644 index 000000000000..adc178b75059 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_IMPL_H +#define __IA_CSS_XMEM_CMEM_IMPL_H + +#include "ia_css_xmem_cmem.h" + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_cmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_xmem_load_32(mmid, src); + ia_css_cmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + +/* Copy data from cmem to xmem */ + +STORAGE_CLASS_INLINE void +ia_css_cmem_to_xmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_cmem_address_t src, + ia_css_xmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_xmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_cmem_load_32(mmid, src); + ia_css_xmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + + +#endif /* __IA_CSS_XMEM_CMEM_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_host.h new file mode 100644 index 000000000000..d94991fc1114 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/device_access/src/ia_css_xmem_host.h @@ -0,0 +1,84 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_HOST_H +#define __IA_CSS_XMEM_HOST_H + +#include "ia_css_xmem.h" +#include +#include "assert_support.h" +#include + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address) +{ + return shared_memory_load_8(mmid, address); +} + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + return shared_memory_load_16(mmid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + return shared_memory_load_32(mmid, address); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size) +{ + shared_memory_load(mmid, address, data, size); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value) +{ + shared_memory_store_8(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + shared_memory_store_16(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + shared_memory_store_32(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes) +{ + shared_memory_store(mmid, address, data, bytes); +} + +#endif /* __IA_CSS_XMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h new file mode 100644 index 000000000000..5102f6e44d2f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h @@ -0,0 +1,68 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H + +/* Destination values for master port 0 and bitfield "request_dest" */ +enum cio_M0_btrs_dest { + DEST_IS_BUT_REGS = 0, + DEST_IS_DDR, + RESERVED, + DEST_IS_SUBSYSTEM, + N_BTRS_DEST +}; + +/* Bit-field positions for M0 info bits */ +enum ia_css_info_bits_m0_pos { + IA_CSS_INFO_BITS_M0_SNOOPABLE_POS = 0, + IA_CSS_INFO_BITS_M0_IMR_DESTINED_POS = 1, + IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS = 4 +}; + +#define IA_CSS_INFO_BITS_M0_DDR \ + (DEST_IS_DDR << IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS) +#define IA_CSS_INFO_BITS_M0_SNOOPABLE (1 << IA_CSS_INFO_BITS_M0_SNOOPABLE_POS) + +/* Info bits as expected by the buttress */ +/* Deprecated because bit fields are not portable */ + +/* For master port 0*/ +union cio_M0_t { + struct { + unsigned int snoopable : 1; + unsigned int imr_destined : 1; + unsigned int spare0 : 2; + unsigned int request_dest : 2; + unsigned int spare1 : 26; + } as_bitfield; + unsigned int as_word; +}; + +/* For master port 1*/ +union cio_M1_t { + struct { + unsigned int spare0 : 1; + unsigned int deadline_pointer : 1; + unsigned int reserved : 1; + unsigned int zlw : 1; + unsigned int stream_id : 4; + unsigned int address_swizzling : 1; + unsigned int spare1 : 23; + } as_bitfield; + unsigned int as_word; +}; + + +#endif /* __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties.h new file mode 100644 index 000000000000..e6e1e9dcbe80 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_H +#define __IPU_DEVICE_CELL_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_cell_type_properties.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id); + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id); +#endif + +#include "ipu_device_cell_properties_func.h" + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_func.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_func.h new file mode 100644 index 000000000000..481b0504a237 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_func.h @@ -0,0 +1,164 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_FUNC_H +#define __IPU_DEVICE_CELL_PROPERTIES_FUNC_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_cell_properties_impl.h" +#include "ipu_device_cell_devices.h" +#include "assert_support.h" +#include "storage_class.h" + +enum {IA_CSS_CELL_MASTER_ADDRESS_WIDTH = 32}; + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void) +{ + return NUM_CELLS; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_memories; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + mem_size[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].mem_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + assert(mem_id != 0); + return ipu_device_cell_properties[cell_id].mem_databus_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_master_ports; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].segment_bits; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << ipu_device_cell_master_segment_bits(cell_id, master_id); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << (IA_CSS_CELL_MASTER_ADDRESS_WIDTH - + ipu_device_cell_master_segment_bits(cell_id, master_id)); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].stride; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].base_address_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_override_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_override_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + icache_align; +} + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_map_cell_id_to_crun_proc_id[cell_id]; +} +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_FUNC_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h new file mode 100644 index 000000000000..63397dc0b7fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H + +/* definitions for all cell types */ + +struct ipu_device_cell_count_s { + unsigned int num_memories; + unsigned int num_master_ports; + unsigned int num_stall_bits; + unsigned int icache_align; +}; + +struct ipu_device_cell_master_properties_s { + unsigned int segment_bits; + unsigned int stride; /* offset to register of next segment */ + unsigned int base_address_register; /* address of first base address + register */ + unsigned int info_bits_register; + unsigned int info_override_bits_register; +}; + +struct ipu_device_cell_type_properties_s { + const struct ipu_device_cell_count_s *count; + const struct ipu_device_cell_master_properties_s *master; + const unsigned int *reg_offset; /* offsets of registers, some depend + on cell type */ + const unsigned int *mem_size; +}; + +struct ipu_device_cell_properties_s { + const struct ipu_device_cell_type_properties_s *type_properties; + const unsigned int *mem_address; + const unsigned int *mem_databus_address; + /* const cell_master_port_properties_s* master_port_properties; */ +}; + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_type_properties.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_type_properties.h new file mode 100644 index 000000000000..72caed3eef0c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/interface/ipu_device_cell_type_properties.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_TYPE_PROPERTIES_H +#define __IPU_DEVICE_CELL_TYPE_PROPERTIES_H + +#define IPU_DEVICE_INVALID_MEM_ADDRESS 0xFFFFFFFF + +enum ipu_device_cell_stat_ctrl_bit { + IPU_DEVICE_CELL_STAT_CTRL_RESET_BIT = 0, + IPU_DEVICE_CELL_STAT_CTRL_START_BIT = 1, + IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT = 3, + IPU_DEVICE_CELL_STAT_CTRL_READY_BIT = 5, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_BIT = 6, + IPU_DEVICE_CELL_STAT_CTRL_STALL_BIT = 7, + IPU_DEVICE_CELL_STAT_CTRL_CLEAR_IRQ_MASK_FLAG_BIT = 8, + IPU_DEVICE_CELL_STAT_CTRL_BROKEN_IRQ_MASK_FLAG_BIT = 9, + IPU_DEVICE_CELL_STAT_CTRL_READY_IRQ_MASK_FLAG_BIT = 10, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_IRQ_MASK_FLAG_BIT = 11, + IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT = 12, + IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT = 13 +}; + +enum ipu_device_cell_reg_addr { + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS = 0x0, + IPU_DEVICE_CELL_START_PC_REG_ADDRESS = 0x4, + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS = 0x10, + IPU_DEVICE_CELL_ICACHE_INFO_BITS_REG_ADDRESS = 0x14 +}; + +enum ipu_device_cell_reg { + IPU_DEVICE_CELL_STAT_CTRL_REG, + IPU_DEVICE_CELL_START_PC_REG, + IPU_DEVICE_CELL_ICACHE_BASE_REG, + IPU_DEVICE_CELL_DEBUG_PC_REG, + IPU_DEVICE_CELL_STALL_REG, + IPU_DEVICE_CELL_NUM_REGS +}; + +enum ipu_device_cell_mem { + IPU_DEVICE_CELL_REGS, /* memory id of registers */ + IPU_DEVICE_CELL_PMEM, /* memory id of pmem */ + IPU_DEVICE_CELL_DMEM, /* memory id of dmem */ + IPU_DEVICE_CELL_BAMEM, /* memory id of bamem */ + IPU_DEVICE_CELL_VMEM /* memory id of vmem */ +}; +#define IPU_DEVICE_CELL_NUM_MEMORIES (IPU_DEVICE_CELL_VMEM + 1) + +enum ipu_device_cell_master { + IPU_DEVICE_CELL_MASTER_ICACHE, /* master port id of icache */ + IPU_DEVICE_CELL_MASTER_QMEM, + IPU_DEVICE_CELL_MASTER_CMEM, + IPU_DEVICE_CELL_MASTER_XMEM, + IPU_DEVICE_CELL_MASTER_XVMEM +}; +#define IPU_DEVICE_CELL_MASTER_NUM_MASTERS (IPU_DEVICE_CELL_MASTER_XVMEM + 1) + +#endif /* __IPU_DEVICE_CELL_TYPE_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_devices.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_devices.h new file mode 100644 index 000000000000..bd672104db3b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_devices.h @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_DEVICES_H +#define __IPU_DEVICE_CELL_DEVICES_H + +/* define cell instances in ISYS */ + +#define SPC0_CELL input_system_unis_logic_sp_control_tile_sp + +enum ipu_device_isys_cell_id { + SPC0, + NUM_CELLS +}; + +#endif /* __IPU_DEVICE_CELL_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_defs.h new file mode 100644 index 000000000000..1b4df534a665 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_defs.h @@ -0,0 +1,22 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +/* Generated file - please do not edit. */ + +#ifndef _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define SPC0_REGS_CBUS_ADDRESS 0x0 +#define SPC0_DMEM_CBUS_ADDRESS 0x8000 +#define SPC0_DMEM_DBUS_ADDRESS 0x8000 +#define SPC0_DMEM_DMA_M0_ADDRESS 0x210000 +#endif /* _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_impl.h new file mode 100644 index 000000000000..5f8ab1ac928f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/isys/bxtB0/ipu_device_cell_properties_impl.h @@ -0,0 +1,57 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_CELL_PROPERTIES_IMPL_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_sp2600_control_properties_impl.h" +#include "ipu_device_cell_properties_defs.h" +#include "ipu_device_cell_devices.h" +#include "ipu_device_cell_type_properties.h"/* IPU_DEVICE_INVALID_MEM_ADDRESS */ + +static const unsigned int +ipu_device_spc0_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + SPC0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spc0_databus_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* regs not accessible from DBUS */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_DBUS_ADDRESS +}; + +static const struct +ipu_device_cell_properties_s ipu_device_cell_properties[NUM_CELLS] = { + { + &ipu_device_sp2600_control_properties, + ipu_device_spc0_mem_address, + ipu_device_spc0_databus_mem_address + } +}; + +#ifdef C_RUN + +/* Mapping between hrt_hive_processors enum and cell_id's used in FW */ +static const int ipu_device_map_cell_id_to_crun_proc_id[NUM_CELLS] = { + 0 /* SPC0 */ +}; + +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h new file mode 100644 index 000000000000..430295cd9d94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h @@ -0,0 +1,136 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H + +/* sp2600_control definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_control_registers { + /* control registers */ + IPU_DEVICE_SP2600_CONTROL_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_CONTROL_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_CONTROL_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_CONTROL_STALL = 0xA0 +}; + +enum ipu_device_sp2600_control_mems { + IPU_DEVICE_SP2600_CONTROL_REGS, + IPU_DEVICE_SP2600_CONTROL_PMEM, + IPU_DEVICE_SP2600_CONTROL_DMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_control_mem_size[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + 0x000AC, + 0x00000, + 0x10000 +}; + +enum ipu_device_sp2600_control_masters { + IPU_DEVICE_SP2600_CONTROL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_QMEM, + IPU_DEVICE_SP2600_CONTROL_CMEM, + IPU_DEVICE_SP2600_CONTROL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_control_masters[IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_control_stall_bits { + IPU_DEVICE_SP2600_CONTROL_STALL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_STALL_DMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_QMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_CMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_control_count = { + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS, + IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_control_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_control_properties = { + &ipu_device_sp2600_control_count, + ipu_device_sp2600_control_masters, + ipu_device_sp2600_control_reg_offset, + ipu_device_sp2600_control_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk new file mode 100644 index 000000000000..b1ffbf7ea21f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk @@ -0,0 +1,24 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +# MODULE is FW ABI COMMON TYPES + +FW_ABI_COMMON_TYPES_DIRS = -I$${MODULES_DIR}/fw_abi_common_types +FW_ABI_COMMON_TYPES_DIRS += -I$${MODULES_DIR}/fw_abi_common_types/cpu + +FW_ABI_COMMON_TYPES_HOST_FILES = +FW_ABI_COMMON_TYPES_HOST_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) + +FW_ABI_COMMON_TYPES_FW_FILES = +FW_ABI_COMMON_TYPES_FW_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h new file mode 100644 index 000000000000..21cc3f43f485 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_BASE_TYPES_H +#define __IA_CSS_TERMINAL_BASE_TYPES_H + + +#include "type_support.h" +#include "ia_css_terminal_defs.h" + +#define N_UINT16_IN_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_TERMINAL_STRUCT 5 + +#define SIZE_OF_TERMINAL_STRUCT_BITS \ + (IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + N_UINT16_IN_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* ==================== Base Terminal - START ==================== */ +struct ia_css_terminal_s { /**< Base terminal */ + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the process group */ + uint16_t size; /**< Size of this whole terminal layout-structure */ + uint16_t tm_index; /**< Index of the terminal manifest object */ + ia_css_terminal_ID_t ID; /**< Absolute referal ID for this terminal, valid ID's != 0 */ + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_STRUCT]; +}; +/* ==================== Base Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h new file mode 100644 index 000000000000..056e1b6d5d4b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H + +#include "ia_css_terminal_defs.h" + +#define N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT 5 +#define SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_UINT16_T_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_UINT32_T_BITS \ + + (N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT*IA_CSS_UINT8_T_BITS)) + +/* ==================== Base Terminal Manifest - START ==================== */ +struct ia_css_terminal_manifest_s { + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the program group manifest */ + uint16_t size; /**< Size of this whole terminal-manifest layout-structure */ + ia_css_terminal_ID_t ID; + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_terminal_manifest_s + ia_css_terminal_manifest_t; + +/* ==================== Base Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_base_types.h new file mode 100644 index 000000000000..3b80a17a6ad3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_base_types.h @@ -0,0 +1,38 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BASE_TYPES_H +#define __IA_CSS_BASE_TYPES_H + +#include "type_support.h" + +#define VIED_VADDRESS_BITS 32 +typedef uint32_t vied_vaddress_t; + +#define DEVICE_DESCRIPTOR_ID_BITS 32 +typedef struct { + uint8_t device_id; + uint8_t instance_id; + uint8_t channel_id; + uint8_t section_id; +} device_descriptor_fields_t; + +typedef union { + device_descriptor_fields_t fields; + uint32_t data; +} device_descriptor_id_t; + +typedef uint16_t ia_css_process_id_t; + +#endif /* __IA_CSS_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h new file mode 100644 index 000000000000..dbf1cf93756f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h @@ -0,0 +1,105 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_DEFS_H +#define __IA_CSS_TERMINAL_DEFS_H + + +#include "type_support.h" + +#define IA_CSS_TERMINAL_ID_BITS 8 +typedef uint8_t ia_css_terminal_ID_t; +#define IA_CSS_TERMINAL_INVALID_ID ((ia_css_terminal_ID_t)(-1)) + +/* + * Terminal Base Type + */ +typedef enum ia_css_terminal_type { + /**< Data input */ + IA_CSS_TERMINAL_TYPE_DATA_IN = 0, + /**< Data output */ + IA_CSS_TERMINAL_TYPE_DATA_OUT, + /**< Type 6 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_STREAM, + /**< Type 1-5 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN, + /**< Type 1-5 parameter output */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + /**< Represent the new type of terminal for the + * explicit slicing, when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN, + /**< Represent the new type of terminal for the + * explicit slicing, when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT, + /**< State (private data) input */ + IA_CSS_TERMINAL_TYPE_STATE_IN, + /**< State (private data) output */ + IA_CSS_TERMINAL_TYPE_STATE_OUT, + IA_CSS_TERMINAL_TYPE_PROGRAM, + IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT, + IA_CSS_N_TERMINAL_TYPES +} ia_css_terminal_type_t; + +#define IA_CSS_TERMINAL_TYPE_BITS 32 + +/* Temporary redirection needed to facilicate merging with the drivers + in a backwards compatible manner */ +#define IA_CSS_TERMINAL_TYPE_PARAM_CACHED IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN + +/* + * Dimensions of the data objects. Note that a C-style + * data order is assumed. Data stored by row. + */ +typedef enum ia_css_dimension { + /**< The number of columns, i.e. the size of the row */ + IA_CSS_COL_DIMENSION = 0, + /**< The number of rows, i.e. the size of the column */ + IA_CSS_ROW_DIMENSION = 1, + IA_CSS_N_DATA_DIMENSION = 2 +} ia_css_dimension_t; + +#define IA_CSS_N_COMMAND_COUNT (4) + +#ifndef PIPE_GENERATION +/* Don't include these complex enum structures in Genpipe, it can't handle and it does not need them */ +/* + * enum ia_css_isys_link_id. Lists the link IDs used by the FW for On The Fly feature + */ +typedef enum ia_css_isys_link_id { + IA_CSS_ISYS_LINK_OFFLINE = 0, + IA_CSS_ISYS_LINK_MAIN_OUTPUT = 1, + IA_CSS_ISYS_LINK_PDAF_OUTPUT = 2 +} ia_css_isys_link_id_t; +#define N_IA_CSS_ISYS_LINK_ID (IA_CSS_ISYS_LINK_PDAF_OUTPUT + 1) + +/* + * enum ia_css_data_barrier_link_id. Lists the link IDs used by the FW for data barrier feature + */ +typedef enum ia_css_data_barrier_link_id { + IA_CSS_DATA_BARRIER_LINK_MEMORY = N_IA_CSS_ISYS_LINK_ID, + N_IA_CSS_DATA_BARRIER_LINK_ID +} ia_css_data_barrier_link_id_t; + +#endif /* #ifndef PIPE_GENERATION */ +#endif /* __IA_CSS_TERMINAL_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h new file mode 100644 index 000000000000..5e47fe7026bd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h @@ -0,0 +1,402 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_FW_BRIDGED_TYPES_H +#define __IA_CSS_ISYS_FW_BRIDGED_TYPES_H + +#include "platform_support.h" + +#include "ia_css_isysapi_fw_types.h" + +/** + * struct ia_css_isys_buffer_partition_comm - buffer partition information + * @num_gda_pages: Number of virtual gda pages available for each + * virtual stream + */ +struct ia_css_isys_buffer_partition_comm { + aligned_uint32(unsigned int, num_gda_pages[STREAM_ID_MAX]); +}; + +/** + * struct ia_css_isys_fw_config - contains the parts from + * ia_css_isys_device_cfg_data + * we need to transfer to the cell + * @num_send_queues: Number of send queues per queue + * type(N_IA_CSS_ISYS_QUEUE_TYPE) + * @num_recv_queues: Number of receive queues per queue + * type(N_IA_CSS_ISYS_QUEUE_TYPE) + */ +struct ia_css_isys_fw_config { + aligned_struct(struct ia_css_isys_buffer_partition_comm, + buffer_partition); + aligned_uint32(unsigned int, + num_send_queues[N_IA_CSS_ISYS_QUEUE_TYPE]); + aligned_uint32(unsigned int, + num_recv_queues[N_IA_CSS_ISYS_QUEUE_TYPE]); +}; + +/** + * struct ia_css_isys_resolution_comm: Generic resolution structure. + * @Width + * @Height + */ +struct ia_css_isys_resolution_comm { + aligned_uint32(unsigned int, width); + aligned_uint32(unsigned int, height); +}; + +/** + * struct ia_css_isys_output_pin_payload_comm + * @out_buf_id: Points to output pin buffer - buffer identifier + * @addr: Points to output pin buffer - CSS Virtual Address + * @compress: Request frame compression (1), or not (0) + * This must be the same as ia_css_isys_output_pin_info_comm::reserve_compression + */ +struct ia_css_isys_output_pin_payload_comm { + aligned_uint64(ia_css_return_token, out_buf_id); + aligned_uint32(ia_css_output_buffer_css_address, addr); + aligned_uint32(unsigned int, compress); +}; + +/** + * struct ia_css_isys_output_pin_info_comm + * @input_pin_id: input pin id/index which is source of + * the data for this output pin + * @output_res: output pin resolution + * @stride: output stride in Bytes (not valid for statistics) + * @watermark_in_lines: pin watermark level in lines + * @payload_buf_size: Size in Bytes of all buffers that will be supplied for capture + * on this pin (i.e. addressed by ia_css_isys_output_pin_payload::addr) + * @send_irq: assert if pin event should trigger irq + * @pt: pin type + * @ft: frame format type + * @link_id: identifies PPG to connect to, link_id = 0 implies offline + * while link_id > 0 implies buffer_chasing or online mode + * can be entered. + * @reserve_compression: Reserve compression resources for pin. + */ +struct ia_css_isys_output_pin_info_comm { + aligned_struct(struct ia_css_isys_resolution_comm, output_res); + aligned_uint32(unsigned int, stride); + aligned_uint32(unsigned int, watermark_in_lines); + aligned_uint32(unsigned int, payload_buf_size); + aligned_uint8(unsigned int, send_irq); + aligned_uint8(unsigned int, input_pin_id); + aligned_uint8(enum ia_css_isys_pin_type, pt); + aligned_uint8(enum ia_css_isys_frame_format_type, ft); + aligned_uint8(enum ia_css_isys_link_id, link_id); + aligned_uint8(unsigned int, reserve_compression); +}; + +/** + * struct ia_css_isys_param_pin_comm + * @param_buf_id: Points to param port buffer - buffer identifier + * @addr: Points to param pin buffer - CSS Virtual Address + */ +struct ia_css_isys_param_pin_comm { + aligned_uint64(ia_css_return_token, param_buf_id); + aligned_uint32(ia_css_input_buffer_css_address, addr); +}; + +/** + * struct ia_css_isys_input_pin_info_comm + * @input_res: input resolution + * @dt: mipi data type + * @mipi_store_mode: defines if legacy long packet header will be stored or + * hdiscarded if discarded, output pin pin type for this + * input pin can only be MIPI + * @bits_per_pix: native bits per pixel + * @dt_rename: mapped_dt + */ +struct ia_css_isys_input_pin_info_comm { + aligned_struct(struct ia_css_isys_resolution_comm, input_res); + aligned_uint8(enum ia_css_isys_mipi_data_type, dt); + aligned_uint8(enum ia_css_isys_mipi_store_mode, mipi_store_mode); + aligned_uint8(unsigned int, bits_per_pix); + aligned_uint8(unsigned int, mapped_dt); +}; + +/** + * ISA configuration fields, definition and macros + */ +#define ISA_CFG_FIELD_BLC_EN_LEN 1 +#define ISA_CFG_FIELD_BLC_EN_SHIFT 0 + +#define ISA_CFG_FIELD_LSC_EN_LEN 1 +#define ISA_CFG_FIELD_LSC_EN_SHIFT 1 + +#define ISA_CFG_FIELD_DPC_EN_LEN 1 +#define ISA_CFG_FIELD_DPC_EN_SHIFT 2 + +#define ISA_CFG_FIELD_DOWNSCALER_EN_LEN 1 +#define ISA_CFG_FIELD_DOWNSCALER_EN_SHIFT 3 + +#define ISA_CFG_FIELD_AWB_EN_LEN 1 +#define ISA_CFG_FIELD_AWB_EN_SHIFT 4 + +#define ISA_CFG_FIELD_AF_EN_LEN 1 +#define ISA_CFG_FIELD_AF_EN_SHIFT 5 + +#define ISA_CFG_FIELD_AE_EN_LEN 1 +#define ISA_CFG_FIELD_AE_EN_SHIFT 6 + +#define ISA_CFG_FIELD_PAF_TYPE_LEN 8 +#define ISA_CFG_FIELD_PAF_TYPE_SHIFT 7 + +#define ISA_CFG_FIELD_SEND_IRQ_STATS_READY_LEN 1 +#define ISA_CFG_FIELD_SEND_IRQ_STATS_READY_SHIFT 15 + +#define ISA_CFG_FIELD_SEND_RESP_STATS_READY_LEN 1 +#define ISA_CFG_FIELD_SEND_RESP_STATS_READY_SHIFT 16 + +/* Helper macros */ +#define ISA_CFG_GET_MASK_FROM_LEN(len) ((1 << (len)) - 1) +#define ISA_CFG_GET_MASK_FROM_TAG(tag) \ + (ISA_CFG_GET_MASK_FROM_LEN(ISA_CFG_FIELD_##tag##_LEN)) +#define ISA_CFG_GET_SHIFT_FROM_TAG(tag) \ + (ISA_CFG_FIELD_##tag##_SHIFT) +/* Get/Set macros */ +#define ISA_CFG_FIELD_GET(tag, word) \ + ( \ + ((word) >> (ISA_CFG_GET_SHIFT_FROM_TAG(tag))) &\ + ISA_CFG_GET_MASK_FROM_TAG(tag) \ + ) +#define ISA_CFG_FIELD_SET(tag, word, value) \ + word |= ( \ + ((value) & ISA_CFG_GET_MASK_FROM_TAG(tag)) << \ + ISA_CFG_GET_SHIFT_FROM_TAG(tag) \ + ) + +/** + * struct ia_css_isys_isa_cfg_comm. Describes the ISA cfg + */ +struct ia_css_isys_isa_cfg_comm { + aligned_struct(struct ia_css_isys_resolution_comm, + isa_res[N_IA_CSS_ISYS_RESOLUTION_INFO]); + aligned_uint32(/* multi-field packing */, cfg_fields); +}; + + /** + * struct ia_css_isys_cropping_comm - cropping coordinates + */ +struct ia_css_isys_cropping_comm { + aligned_int32(int, top_offset); + aligned_int32(int, left_offset); + aligned_int32(int, bottom_offset); + aligned_int32(int, right_offset); +}; + + /** + * struct ia_css_isys_stream_cfg_data_comm + * ISYS stream configuration data structure + * @isa_cfg: details about what ACCs are active if ISA is used + * @crop: defines cropping resolution for the + * maximum number of input pins which can be cropped, + * it is directly mapped to the HW devices + * @input_pins: input pin descriptors + * @output_pins: output pin descriptors + * @compfmt: de-compression setting for User Defined Data + * @nof_input_pins: number of input pins + * @nof_output_pins: number of output pins + * @send_irq_sof_discarded: send irq on discarded frame sof response + * - if '1' it will override the send_resp_sof_discarded and send + * the response + * - if '0' the send_resp_sof_discarded will determine whether to + * send the response + * @send_irq_eof_discarded: send irq on discarded frame eof response + * - if '1' it will override the send_resp_eof_discarded and send + * the response + * - if '0' the send_resp_eof_discarded will determine whether to + * send the response + * @send_resp_sof_discarded: send response for discarded frame sof detected, + * used only when send_irq_sof_discarded is '0' + * @send_resp_eof_discarded: send response for discarded frame eof detected, + * used only when send_irq_eof_discarded is '0' + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) + * @isl_use: indicates whether stream requires ISL and how + */ +struct ia_css_isys_stream_cfg_data_comm { + aligned_struct(struct ia_css_isys_isa_cfg_comm, isa_cfg); + aligned_struct(struct ia_css_isys_cropping_comm, + crop[N_IA_CSS_ISYS_CROPPING_LOCATION]); + aligned_struct(struct ia_css_isys_input_pin_info_comm, + input_pins[MAX_IPINS]); + aligned_struct(struct ia_css_isys_output_pin_info_comm, + output_pins[MAX_OPINS]); + aligned_uint32(unsigned int, compfmt); + aligned_uint8(unsigned int, nof_input_pins); + aligned_uint8(unsigned int, nof_output_pins); + aligned_uint8(unsigned int, send_irq_sof_discarded); + aligned_uint8(unsigned int, send_irq_eof_discarded); + aligned_uint8(unsigned int, send_resp_sof_discarded); + aligned_uint8(unsigned int, send_resp_eof_discarded); + aligned_uint8(enum ia_css_isys_stream_source, src); + aligned_uint8(enum ia_css_isys_mipi_vc, vc); + aligned_uint8(enum ia_css_isys_isl_use, isl_use); +}; + +/** + * struct ia_css_isys_frame_buff_set - frame buffer set + * @output_pins: output pin addresses + * @process_group_light: process_group_light buffer address + * @send_irq_sof: send irq on frame sof response + * - if '1' it will override the send_resp_sof and send the + * response + * - if '0' the send_resp_sof will determine whether to send the + * response + * @send_irq_eof: send irq on frame eof response + * - if '1' it will override the send_resp_eof and send the + * response + * - if '0' the send_resp_eof will determine whether to send the + * response + * @send_resp_sof: send response for frame sof detected, used only when + * send_irq_sof is '0' + * @send_resp_eof: send response for frame eof detected, used only when + * send_irq_eof is '0' + * @frame_counter: frame number associated with this buffer set. + */ +struct ia_css_isys_frame_buff_set_comm { + aligned_struct(struct ia_css_isys_output_pin_payload_comm, + output_pins[MAX_OPINS]); + aligned_struct(struct ia_css_isys_param_pin_comm, process_group_light); + aligned_uint8(unsigned int, send_irq_sof); + aligned_uint8(unsigned int, send_irq_eof); + aligned_uint8(unsigned int, send_irq_capture_ack); + aligned_uint8(unsigned int, send_irq_capture_done); + aligned_uint8(unsigned int, send_resp_sof); + aligned_uint8(unsigned int, send_resp_eof); + aligned_uint8(unsigned int, frame_counter); +}; + +/** + * struct ia_css_isys_error_info_comm + * @error: error code if something went wrong + * @error_details: depending on error code, it may contain additional + * error info + */ +struct ia_css_isys_error_info_comm { + aligned_enum(enum ia_css_isys_error, error); + aligned_uint32(unsigned int, error_details); +}; + +/** + * struct ia_css_isys_resp_info_comm + * @pin: this var is only valid for pin event related responses, + * contains pin addresses + * @process_group_light: this var is valid for stats ready related responses, + * contains process group addresses + * @error_info: error information from the FW + * @timestamp: Time information for event if available + * @stream_handle: stream id the response corresponds to + * @type: response type + * @pin_id: pin id that the pin payload corresponds to + * @acc_id: this var is valid for stats ready related responses, + * contains accelerator id that finished producing + * all related statistics + * @frame_counter: valid for STREAM_START_AND_CAPTURE_DONE, + * STREAM_CAPTURE_DONE and STREAM_CAPTURE_DISCARDED, + * @written_direct: indicates if frame was written direct (online mode) or not. + * + */ + +struct ia_css_isys_resp_info_comm { + aligned_uint64(ia_css_return_token, buf_id); /* Used internally only */ + aligned_struct(struct ia_css_isys_output_pin_payload_comm, pin); + aligned_struct(struct ia_css_isys_param_pin_comm, process_group_light); + aligned_struct(struct ia_css_isys_error_info_comm, error_info); + aligned_uint32(unsigned int, timestamp[2]); + aligned_uint8(unsigned int, stream_handle); + aligned_uint8(enum ia_css_isys_resp_type, type); + aligned_uint8(unsigned int, pin_id); + aligned_uint8(unsigned int, acc_id); + aligned_uint8(unsigned int, frame_counter); + aligned_uint8(unsigned int, written_direct); +}; + +/** + * struct ia_css_isys_proxy_error_info_comm + * @proxy_error: error code if something went wrong + * @proxy_error_details: depending on error code, it may contain additional + * error info + */ +struct ia_css_isys_proxy_error_info_comm { + aligned_enum(enum ia_css_proxy_error, error); + aligned_uint32(unsigned int, error_details); +}; + +/** + * struct ia_css_isys_proxy_resp_info_comm + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @error_info: details in struct definition + */ +struct ia_css_isys_proxy_resp_info_comm { + aligned_uint32(uint32_t, request_id); + aligned_struct(struct ia_css_isys_proxy_error_info_comm, error_info); +}; + +/** + * struct ia_css_proxy_write_queue_token + * @request_id: update id for the specific proxy write request + * @region_index: Region id for the proxy write request + * @offset: Offset of the write request according to the base address of the + * region + * @value: Value that is requested to be written with the proxy write request + */ +struct ia_css_proxy_write_queue_token { + aligned_uint32(uint32_t, request_id); + aligned_uint32(uint32_t, region_index); + aligned_uint32(uint32_t, offset); + aligned_uint32(uint32_t, value); +}; + +/* From here on type defines not coming from the ISYSAPI interface */ + +/** + * struct resp_queue_token + */ +struct resp_queue_token { + aligned_struct(struct ia_css_isys_resp_info_comm, resp_info); +}; + +/** + * struct send_queue_token + */ +struct send_queue_token { + aligned_uint64(ia_css_return_token, buf_handle); + aligned_uint32(ia_css_input_buffer_css_address, payload); + aligned_uint16(enum ia_css_isys_send_type, send_type); + aligned_uint16(unsigned int, stream_id); +}; + +/** + * struct proxy_resp_queue_token + */ +struct proxy_resp_queue_token { + aligned_struct(struct ia_css_isys_proxy_resp_info_comm, + proxy_resp_info); +}; + +/** + * struct proxy_send_queue_token + */ +struct proxy_send_queue_token { + aligned_uint32(uint32_t, request_id); + aligned_uint32(uint32_t, region_index); + aligned_uint32(uint32_t, offset); + aligned_uint32(uint32_t, value); +}; + +#endif /* __IA_CSS_ISYS_FW_BRIDGED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi.h new file mode 100644 index 000000000000..abbc8b8d26ed --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi.h @@ -0,0 +1,321 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_H +#define __IA_CSS_ISYSAPI_H + + +/* The following is needed for the function arguments */ +#include "ia_css_isysapi_types.h" + +/* To define the HANDLE */ +#include "type_support.h" + + +/** + * ia_css_isys_device_open() - configure ISYS device + * @ context : device handle output parameter + * @config: device configuration data struct ptr as input parameter, + * read only by css fw until function return + * Ownership, ISYS will only access read my_device during fct call + * Prepares and Sends to PG server (SP) the syscom and isys context + * Executes the host level 0 and 1 boot sequence and starts the PG server (SP) + * All streams must be stopped when calling ia_css_isys_device_open() + * + * Return: int type error code (errno.h) + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +extern int ia_css_isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config +); +extern int ia_css_isys_context_store_dmem( + const HANDLE *context, + const struct ia_css_isys_device_cfg_data *config +); +extern bool ia_css_isys_ab_spc_ready( + HANDLE *context +); +extern int ia_css_isys_device_open( + const struct ia_css_isys_device_cfg_data *config +); +#else +extern int ia_css_isys_device_open( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config +); +#endif + +/** + * ia_css_isys_device_open_ready() - Complete ISYS device configuration + * @ context : device handle output parameter + * read only by css fw until function return + * Requires the boot failure to be completed before it can return + * successfully (includes syscom and isys context) + * Initialise Host/ISYS messaging queues + * Must be called multiple times until it succeeds or it is determined by + * the driver that the boot seuqence has failed. + * All streams must be stopped when calling ia_css_isys_device_open() + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_device_open_ready( + HANDLE context +); + + /** + * ia_css_isys_stream_open() - open and configure a virtual stream + * @ stream_handle: stream handle + * @ stream_cfg: stream configuration data struct pointer, which is + * "read only" by ISYS until function return + * ownership, ISYS will only read access stream_cfg during fct call + * Pre-conditions: + * Any Isys/Ssys interface changes must call ia_css_isys_stream_open() + * Post-condition: + * On successful call, ISYS hardware resource (IBFctrl, ISL, DMAs) + * are acquired and ISYS server is able to handle stream specific commands + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_open( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_stream_cfg_data *stream_cfg +); + +/** + * ia_css_isys_stream_close() - close virtual stream + * @ stream_handle: stream identifier + * release ISYS resources by freeing up stream HW resources + * output pin buffers ownership is returned to the driver + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_close( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_start() - starts handling a mipi virtual stream + * @ stream_handle: stream identifier + * @next_frame: + * if next_frame != NULL: apply next_frame + * settings asynchronously and start stream + * This mode ensures that the first frame is captured + * and thus a minimal start up latency + * (preconditions: sensor streaming must be switched off) + * + * if next_frame == NULL: sensor can be in a streaming state, + * all capture indicates commands will be + * processed synchronously (e.g. on mipi SOF events) + * + * To be called once ia_css_isys_stream_open() successly called + * On success, the stream's HW resources are in active state + * + * Object ownership: During this function call, + * next_frame struct must be read but not modified by the ISYS, + * and in addition the driver is not allowed to modify it + * on function exit next_frame ownership is returned to + * the driver and is no longer accesses by iSYS + * next_frame contains a collection of + * ia_css_isys_output_pin * and ia_css_isys_input_pin * + * which point to the frame's "output/input pin info & data buffers", + * + * Upon the ia_css_isys_stream_start() call, + * ia_css_isys_output_pin* or ia_css_isys_input_pin* + * will now be owned by the ISYS + * these ptr will enable runtime/dynamic ISYS configuration and also + * to store and write captured payload data + * at the address specified in ia_css_isys_output_pin_payload + * These ptrs should no longer be accessed by any other + * code until (ia_css_isys_output_pin) gets handed + * back to the driver via the response mechansim + * ia_css_isys_stream_handle_response() + * the driver is responsible for providing valid + * ia_css_isys_output_pin* or ia_css_isys_output_pin* + * Pointers set to NULL will simply not be used by the ISYS + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_start( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_stream_stop() - Stops a mipi virtual stream + * @ stream_handle: stream identifier + * stop both accepting new commands and processing + * submitted capture indication commands + * Support for Secure Touch + * Precondition: stream must be started + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_stop( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_flush() - stops a mipi virtual stream but + * completes processing cmd backlog + * @ stream_handle: stream identifier + * stop accepting commands, but process + * the already submitted capture indicates + * Precondition: stream must be started + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_flush( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_capture_indication() + * captures "next frame" on stream_handle + * @ stream_handle: stream identifier + * @ next_frame: frame pin payloads are provided atomically + * purpose: stream capture new frame command, Successfull calls will + * result in frame output pins being captured + * + * To be called once ia_css_isys_stream_start() is successly called + * On success, the stream's HW resources are in active state + * + * Object ownership: During this function call, + * next_frame struct must be read but not modified by the ISYS, + * and in addition the driver is not allowed to modify it + * on function exit next_frame ownership is returned to + * the driver and is no longer accesses by iSYS + * next_frame contains a collection of + * ia_css_isys_output_pin * and ia_css_isys_input_pin * + * which point to the frame's "output/input pin info & data buffers", + * + * Upon the ia_css_isys_stream_capture_indication() call, + * ia_css_isys_output_pin* or ia_css_isys_input_pin* + * will now be owned by the ISYS + * these ptr will enable runtime/dynamic ISYS configuration and also + * to store and write captured payload data + * at the address specified in ia_css_isys_output_pin_payload + * These ptrs should no longer be accessed by any other + * code until (ia_css_isys_output_pin) gets handed + * back to the driver via the response mechanism + * ia_css_isys_stream_handle_response() + * the driver is responsible for providing valid + * ia_css_isys_output_pin* or ia_css_isys_output_pin* + * Pointers set to NULL will simply not be used by the ISYS, and this + * refers specifically the following cases: + * - output pins from SOC path if the same datatype is also passed into ISAPF + * path or it has active MIPI output (not NULL) + * - full resolution pin from ISA (but not when bypassing ISA) + * - scaled pin from ISA (bypassing ISA for scaled pin is impossible) + * - output pins from MIPI path but only when the same datatype is also + * either forwarded to the ISAPF path based on the stream configuration + * (it is ok if the second output pin of this datatype is also skipped) + * or it has an active SOC output (not NULL) + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_capture_indication( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_stream_handle_response() - handle ISYS responses + * @received_response: provides response info from the + * "next response element" from ISYS server + * received_response will be written to during the fct call and + * can be read by the drv once fct is returned + * + * purpose: Allows the client to handle received ISYS responses + * Upon an IRQ event, the driver will call ia_css_isys_stream_handle_response() + * until the queue is emptied + * Responses returning IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY to the driver will + * hand back ia_css_isys_output_pin ownership to the drv + * ISYS FW will not write/read access ia_css_isys_output_pin + * once it belongs to the driver + * Pre-conditions: ISYS client must have sent a CMDs to ISYS srv + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_handle_response( + HANDLE context, + struct ia_css_isys_resp_info *received_response +); + +/** + * ia_css_isys_device_close() - close ISYS device + * @context : device handle output parameter + * Purpose: Request for the cell to close + * All streams must be stopped when calling ia_css_isys_device_close() + * + * Return: int type error code (errno.h) + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +extern int ia_css_isys_context_destroy( + HANDLE context +); +extern void ia_css_isys_device_close( + void +); +#else +extern int ia_css_isys_device_close( + HANDLE context +); +#endif + +/** + * ia_css_isys_device_release() - release ISYS device + * @context : device handle output parameter + * @force: forces release or verifies the state before releasing + * Purpose: Free context forcibly or not + * Must be called after ia_css_isys_device_close() + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_device_release( + HANDLE context, + unsigned int force +); + +/** + * ia_css_isys_proxy_write_req() - issue a isys proxy write request + * @context : device handle output parameter + * Purpose: Issues a write request for the regions that are exposed + * by proxy interface + * Can be called any time between ia_css_isys_device_open + * ia_css_isys_device_close + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_proxy_write_req( + HANDLE context, + const struct ia_css_proxy_write_req_val *write_req_val +); + +/** + * ia_css_isys_proxy_handle_write_response() + * - Handles isys proxy write request responses + * @context : device handle output parameter + * Purpose: Handling the responses that are created by FW upon the completion + * proxy interface write request + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_proxy_handle_write_response( + HANDLE context, + struct ia_css_proxy_write_req_resp *received_response +); + +#endif /* __IA_CSS_ISYSAPI_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h new file mode 100644 index 000000000000..938f726d1cfb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h @@ -0,0 +1,512 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_FW_TYPES_H +#define __IA_CSS_ISYSAPI_FW_TYPES_H + + +/* Max number of Input/Output Pins */ +#define MAX_IPINS (4) +/* worst case is ISA use where a single input pin produces: +* Mipi output, NS Pixel Output, and Scaled Pixel Output. +* This is how the 2 is calculated +*/ +#define MAX_OPINS ((MAX_IPINS) + 2) + +/* Max number of supported virtual streams */ +#define STREAM_ID_MAX (8) + +/* Aligned with the approach of having one dedicated per stream */ +#define N_MAX_MSG_SEND_QUEUES (STREAM_ID_MAX) +/* Single return queue for all streams/commands type */ +#define N_MAX_MSG_RECV_QUEUES (1) +/* Single device queue for high priority commands (bypass in-order queue) */ +#define N_MAX_DEV_SEND_QUEUES (1) +/* Single dedicated send queue for proxy interface */ +#define N_MAX_PROXY_SEND_QUEUES (1) +/* Single dedicated recv queue for proxy interface */ +#define N_MAX_PROXY_RECV_QUEUES (1) +/* Send queues layout */ +#define BASE_PROXY_SEND_QUEUES (0) +#define BASE_DEV_SEND_QUEUES (BASE_PROXY_SEND_QUEUES + N_MAX_PROXY_SEND_QUEUES) +#define BASE_MSG_SEND_QUEUES (BASE_DEV_SEND_QUEUES + N_MAX_DEV_SEND_QUEUES) +#define N_MAX_SEND_QUEUES (BASE_MSG_SEND_QUEUES + N_MAX_MSG_SEND_QUEUES) +/* Recv queues layout */ +#define BASE_PROXY_RECV_QUEUES (0) +#define BASE_MSG_RECV_QUEUES (BASE_PROXY_RECV_QUEUES + N_MAX_PROXY_RECV_QUEUES) +#define N_MAX_RECV_QUEUES (BASE_MSG_RECV_QUEUES + N_MAX_MSG_RECV_QUEUES) + +#define MAX_QUEUE_SIZE (256) +#define MIN_QUEUE_SIZE (1) + +/* Consider 1 slot per stream since driver is not expected to pipeline + * device commands for the same stream */ +#define DEV_SEND_QUEUE_SIZE (STREAM_ID_MAX) + +/* Max number of supported SRAM buffer partitions */ +/* It refers to the size of stream partitions */ +/* These partitions are further subpartitioned internally */ +/* by the FW, but by declaring statically the stream */ +/* partitions we solve the buffer fragmentation issue */ +#define NOF_SRAM_BLOCKS_MAX (STREAM_ID_MAX) + +/* Max number of supported input pins routed in ISL */ +#define MAX_IPINS_IN_ISL (2) + +/* Max number of planes for frame formats supported by the FW */ +#define PIN_PLANES_MAX (4) + +/** + * enum ia_css_isys_resp_type + */ +enum ia_css_isys_resp_type { + IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK, + IA_CSS_ISYS_RESP_TYPE_FRAME_SOF, + IA_CSS_ISYS_RESP_TYPE_FRAME_EOF, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED, + IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED, + IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED, + IA_CSS_ISYS_RESP_TYPE_STATS_DATA_READY, + N_IA_CSS_ISYS_RESP_TYPE +}; + +/** + * enum ia_css_isys_send_type + */ +enum ia_css_isys_send_type { + IA_CSS_ISYS_SEND_TYPE_STREAM_OPEN = 0, + IA_CSS_ISYS_SEND_TYPE_STREAM_START, + IA_CSS_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE, + IA_CSS_ISYS_SEND_TYPE_STREAM_CAPTURE, + IA_CSS_ISYS_SEND_TYPE_STREAM_STOP, + IA_CSS_ISYS_SEND_TYPE_STREAM_FLUSH, + IA_CSS_ISYS_SEND_TYPE_STREAM_CLOSE, + N_IA_CSS_ISYS_SEND_TYPE +}; + +/** + * enum ia_css_isys_queue_type + */ +enum ia_css_isys_queue_type { + IA_CSS_ISYS_QUEUE_TYPE_PROXY = 0, + IA_CSS_ISYS_QUEUE_TYPE_DEV, + IA_CSS_ISYS_QUEUE_TYPE_MSG, + N_IA_CSS_ISYS_QUEUE_TYPE +}; + +/** + * enum ia_css_isys_stream_source: Specifies a source for a stream + */ +enum ia_css_isys_stream_source { + IA_CSS_ISYS_STREAM_SRC_PORT_0 = 0, + IA_CSS_ISYS_STREAM_SRC_PORT_1, + IA_CSS_ISYS_STREAM_SRC_PORT_2, + IA_CSS_ISYS_STREAM_SRC_PORT_3, + IA_CSS_ISYS_STREAM_SRC_PORT_4, + IA_CSS_ISYS_STREAM_SRC_PORT_5, + IA_CSS_ISYS_STREAM_SRC_PORT_6, + IA_CSS_ISYS_STREAM_SRC_PORT_7, + IA_CSS_ISYS_STREAM_SRC_PORT_8, + IA_CSS_ISYS_STREAM_SRC_PORT_9, + IA_CSS_ISYS_STREAM_SRC_PORT_10, + IA_CSS_ISYS_STREAM_SRC_PORT_11, + IA_CSS_ISYS_STREAM_SRC_PORT_12, + IA_CSS_ISYS_STREAM_SRC_PORT_13, + IA_CSS_ISYS_STREAM_SRC_PORT_14, + IA_CSS_ISYS_STREAM_SRC_PORT_15, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_0, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_1, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_2, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_3, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_4, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_5, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_6, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_7, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_8, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_9, + N_IA_CSS_ISYS_STREAM_SRC +}; + +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT0 IA_CSS_ISYS_STREAM_SRC_PORT_0 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT1 IA_CSS_ISYS_STREAM_SRC_PORT_1 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT2 IA_CSS_ISYS_STREAM_SRC_PORT_2 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT3 IA_CSS_ISYS_STREAM_SRC_PORT_3 + +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_PORTA IA_CSS_ISYS_STREAM_SRC_PORT_4 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_PORTB IA_CSS_ISYS_STREAM_SRC_PORT_5 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 IA_CSS_ISYS_STREAM_SRC_PORT_6 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 IA_CSS_ISYS_STREAM_SRC_PORT_7 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 IA_CSS_ISYS_STREAM_SRC_PORT_8 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 IA_CSS_ISYS_STREAM_SRC_PORT_9 + +#define IA_CSS_ISYS_STREAM_SRC_MIPIGEN_PORT0 IA_CSS_ISYS_STREAM_SRC_MIPIGEN_0 +#define IA_CSS_ISYS_STREAM_SRC_MIPIGEN_PORT1 IA_CSS_ISYS_STREAM_SRC_MIPIGEN_1 + +/** + * enum ia_css_isys_mipi_vc: MIPI csi2 spec + * supports upto 4 virtual per physical channel + */ +enum ia_css_isys_mipi_vc { + IA_CSS_ISYS_MIPI_VC_0 = 0, + IA_CSS_ISYS_MIPI_VC_1, + IA_CSS_ISYS_MIPI_VC_2, + IA_CSS_ISYS_MIPI_VC_3, + N_IA_CSS_ISYS_MIPI_VC +}; + +/** + * Supported Pixel Frame formats. Expandable if needed + */ +enum ia_css_isys_frame_format_type { + IA_CSS_ISYS_FRAME_FORMAT_NV11 = 0,/* 12 bit YUV 411, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12,/* 12 bit YUV 420, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12_16,/* 16 bit YUV 420, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12_TILEY,/* 12 bit YUV 420, Intel + proprietary tiled format, + TileY + */ + IA_CSS_ISYS_FRAME_FORMAT_NV16,/* 16 bit YUV 422, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV21,/* 12 bit YUV 420, Y, VU plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV61,/* 16 bit YUV 422, Y, VU plane */ + IA_CSS_ISYS_FRAME_FORMAT_YV12,/* 12 bit YUV 420, Y, V, U plane */ + IA_CSS_ISYS_FRAME_FORMAT_YV16,/* 16 bit YUV 422, Y, V, U plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420,/* 12 bit YUV 420, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_10,/* yuv420, 10 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_12,/* yuv420, 12 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_14,/* yuv420, 14 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_16,/* yuv420, 16 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV422,/* 16 bit YUV 422, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV422_16,/* yuv422, 16 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_UYVY,/* 16 bit YUV 422, UYVY interleaved */ + IA_CSS_ISYS_FRAME_FORMAT_YUYV,/* 16 bit YUV 422, YUYV interleaved */ + IA_CSS_ISYS_FRAME_FORMAT_YUV444,/* 24 bit YUV 444, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV_LINE,/* Internal format, 2 y lines + followed by a uvinterleaved line + */ + IA_CSS_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RGB565,/* 16 bit RGB, 1 plane. Each 3 sub + pixels are packed into one 16 bit + value, 5 bits for R, 6 bits for G + and 5 bits for B. + */ + IA_CSS_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ + IA_CSS_ISYS_FRAME_FORMAT_RGBA888,/* 32 bit RGBA, 1 plane, + A=Alpha (alpha is unused) + */ + IA_CSS_ISYS_FRAME_FORMAT_QPLANE6,/* Internal, for advanced ISP */ + IA_CSS_ISYS_FRAME_FORMAT_BINARY_8,/* byte stream, used for jpeg. */ + N_IA_CSS_ISYS_FRAME_FORMAT +}; +/* Temporary for driver compatibility */ +#define IA_CSS_ISYS_FRAME_FORMAT_RAW (IA_CSS_ISYS_FRAME_FORMAT_RAW16) + + +/** + * Supported MIPI data type. Keep in sync array in ia_css_isys_private.c + */ +enum ia_css_isys_mipi_data_type { + /** SYNCHRONIZATION SHORT PACKET DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_FRAME_START_CODE = 0x00, + IA_CSS_ISYS_MIPI_DATA_TYPE_FRAME_END_CODE = 0x01, + IA_CSS_ISYS_MIPI_DATA_TYPE_LINE_START_CODE = 0x02, /* Optional */ + IA_CSS_ISYS_MIPI_DATA_TYPE_LINE_END_CODE = 0x03, /* Optional */ + /** Reserved 0x04-0x07 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x04 = 0x04, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x05 = 0x05, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x06 = 0x06, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x07 = 0x07, + /** GENERIC SHORT PACKET DATA TYPES */ + /** They are used to keep the timing information for the + * opening/closing of shutters, triggering of flashes and etc. + */ + /* Generic Short Packet Code 1 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT1 = 0x08, + /* Generic Short Packet Code 2 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT2 = 0x09, + /* Generic Short Packet Code 3 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT3 = 0x0A, + /* Generic Short Packet Code 4 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT4 = 0x0B, + /* Generic Short Packet Code 5 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT5 = 0x0C, + /* Generic Short Packet Code 6 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT6 = 0x0D, + /* Generic Short Packet Code 7 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT7 = 0x0E, + /* Generic Short Packet Code 8 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT8 = 0x0F, + /** GENERIC LONG PACKET DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_NULL = 0x10, + IA_CSS_ISYS_MIPI_DATA_TYPE_BLANKING_DATA = 0x11, + /* Embedded 8-bit non Image Data */ + IA_CSS_ISYS_MIPI_DATA_TYPE_EMBEDDED = 0x12, + /** Reserved 0x13-0x17 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x13 = 0x13, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x14 = 0x14, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x15 = 0x15, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x16 = 0x16, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x17 = 0x17, + /** YUV DATA TYPES */ + /* 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8 = 0x18, + /* 10 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_10 = 0x19, + /* 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8_LEGACY = 0x1A, + /** Reserved 0x1B */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x1B = 0x1B, + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8_SHIFT = 0x1C, + /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_10_SHIFT = 0x1D, + /* UYVY..UVYV, 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV422_8 = 0x1E, + /* UYVY..UVYV, 10 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV422_10 = 0x1F, + /** RGB DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_444 = 0x20, + /* BGR..BGR, 5 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_555 = 0x21, + /* BGR..BGR, 5 bits B and R, 6 bits G */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_565 = 0x22, + /* BGR..BGR, 6 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_666 = 0x23, + /* BGR..BGR, 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_888 = 0x24, + /** Reserved 0x25-0x27 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x25 = 0x25, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x26 = 0x26, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x27 = 0x27, + /** RAW DATA TYPES */ + /* RAW data, 6 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_6 = 0x28, + /* RAW data, 7 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_7 = 0x29, + /* RAW data, 8 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_8 = 0x2A, + /* RAW data, 10 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_10 = 0x2B, + /* RAW data, 12 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_12 = 0x2C, + /* RAW data, 14 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_14 = 0x2D, + /** Reserved 0x2E-2F are used with assigned meaning */ + /* RAW data, 16 bits per pixel, not specified in CSI-MIPI standard */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_16 = 0x2E, + /* Binary byte stream, which is target at JPEG, not specified in + * CSI-MIPI standard + */ + IA_CSS_ISYS_MIPI_DATA_TYPE_BINARY_8 = 0x2F, + /** USER DEFINED 8-BIT DATA TYPES */ + /** For example, the data transmitter (e.g. the SoC sensor) can keep + * the JPEG data as the User Defined Data Type 4 and the MPEG data as + * the User Defined Data Type 7. + */ + /* User defined 8-bit data type 1 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF1 = 0x30, + /* User defined 8-bit data type 2 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF2 = 0x31, + /* User defined 8-bit data type 3 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF3 = 0x32, + /* User defined 8-bit data type 4 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF4 = 0x33, + /* User defined 8-bit data type 5 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF5 = 0x34, + /* User defined 8-bit data type 6 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF6 = 0x35, + /* User defined 8-bit data type 7 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF7 = 0x36, + /* User defined 8-bit data type 8 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF8 = 0x37, + /** Reserved 0x38-0x3F */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x38 = 0x38, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x39 = 0x39, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3A = 0x3A, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3B = 0x3B, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3C = 0x3C, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3D = 0x3D, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3E = 0x3E, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3F = 0x3F, + + /* Keep always last and max value */ + N_IA_CSS_ISYS_MIPI_DATA_TYPE = 0x40 +}; + +/** enum ia_css_isys_pin_type: output pin buffer types. + * Buffers can be queued and de-queued to hand them over between IA and ISYS + */ +enum ia_css_isys_pin_type { + /* Captured as MIPI packets */ + IA_CSS_ISYS_PIN_TYPE_MIPI = 0, + /* Captured through the ISApf (with/without ISA) + * and the non-scaled output path + */ + IA_CSS_ISYS_PIN_TYPE_RAW_NS, + /* Captured through the ISApf + ISA and the scaled output path */ + IA_CSS_ISYS_PIN_TYPE_RAW_S, + /* Captured through the SoC path */ + IA_CSS_ISYS_PIN_TYPE_RAW_SOC, + /* Reserved for future use, maybe short packets */ + IA_CSS_ISYS_PIN_TYPE_METADATA_0, + /* Reserved for future use */ + IA_CSS_ISYS_PIN_TYPE_METADATA_1, + /* Legacy (non-PIV2), used for the AWB stats */ + IA_CSS_ISYS_PIN_TYPE_AWB_STATS, + /* Legacy (non-PIV2), used for the AF stats */ + IA_CSS_ISYS_PIN_TYPE_AF_STATS, + /* Legacy (non-PIV2), used for the AE stats */ + IA_CSS_ISYS_PIN_TYPE_HIST_STATS, + /* Used for the PAF FF*/ + IA_CSS_ISYS_PIN_TYPE_PAF_FF, + /* Keep always last and max value */ + N_IA_CSS_ISYS_PIN_TYPE +}; + +/** + * enum ia_css_isys_isl_use. Describes the ISL/ISA use + * (ISAPF path in after BXT A0) + */ +enum ia_css_isys_isl_use { + IA_CSS_ISYS_USE_NO_ISL_NO_ISA = 0, + IA_CSS_ISYS_USE_SINGLE_DUAL_ISL, + IA_CSS_ISYS_USE_SINGLE_ISA, + N_IA_CSS_ISYS_USE +}; + +/** + * enum ia_css_isys_mipi_store_mode. Describes if long MIPI packets reach MIPI + * SRAM with the long packet header or not. + * if not, then only option is to capture it with pin type MIPI. + */ +enum ia_css_isys_mipi_store_mode { + IA_CSS_ISYS_MIPI_STORE_MODE_NORMAL = 0, + IA_CSS_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER, + N_IA_CSS_ISYS_MIPI_STORE_MODE +}; + +/** + * enum ia_css_isys_mipi_dt_rename_mode. Describes if long MIPI packets have + * DT with some other DT format. + */ +enum ia_css_isys_mipi_dt_rename_mode { + IA_CSS_ISYS_MIPI_DT_NO_RENAME = 0, + IA_CSS_ISYS_MIPI_DT_RENAMED_MODE, + N_IA_CSS_ISYS_MIPI_DT_MODE +}; + +/** + * enum ia_css_isys_type_paf. Describes the Type of PAF enabled + * (PAF path in after cnlB0) + */ +enum ia_css_isys_type_paf { + /* PAF data not present */ + IA_CSS_ISYS_TYPE_NO_PAF = 0, + /* Type 2 sensor types, PAF coming separately from Image Frame */ + /* PAF data in interleaved format(RLRL or LRLR)*/ + IA_CSS_ISYS_TYPE_INTERLEAVED_PAF, + /* PAF data in non-interleaved format(LL/RR or RR/LL) */ + IA_CSS_ISYS_TYPE_NON_INTERLEAVED_PAF, + /* Type 3 sensor types , PAF data embedded in Image Frame*/ + /* Frame Embedded PAF in interleaved format(RLRL or LRLR)*/ + IA_CSS_ISYS_TYPE_FRAME_EMB_INTERLEAVED_PAF, + /* Frame Embedded PAF non-interleaved format(LL/RR or RR/LL)*/ + IA_CSS_ISYS_TYPE_FRAME_EMB_NON_INTERLEAVED_PAF, + N_IA_CSS_ISYS_TYPE_PAF +}; + +/** + * enum ia_css_isys_cropping_location. Enumerates the cropping locations + * in ISYS + */ +enum ia_css_isys_cropping_location { + /* Cropping executed in ISAPF (mainly), ISAPF preproc (odd column) and + * MIPI STR2MMIO (odd row) + */ + IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA = 0, + /* BXT A0 legacy mode which will never be implemented */ + IA_CSS_ISYS_CROPPING_LOCATION_RESERVED_1, + /* Cropping executed in StreamPifConv in the ISA output for + * RAW_NS pin + */ + IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED, + /* Cropping executed in StreamScaledPifConv in the ISA output for + * RAW_S pin + */ + IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED, + N_IA_CSS_ISYS_CROPPING_LOCATION +}; + +/** + * enum ia_css_isys_resolution_info. Describes the resolution, required to + * setup the various ISA GP registers. + */ +enum ia_css_isys_resolution_info { + /* Scaled ISA output resolution before the + * StreamScaledPifConv cropping + */ + IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED = 0, + /* Non-Scaled ISA output resolution before the + * StreamPifConv cropping + */ + IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED, + N_IA_CSS_ISYS_RESOLUTION_INFO +}; + +/** + * enum ia_css_isys_error. Describes the error type detected by the FW + */ +enum ia_css_isys_error { + IA_CSS_ISYS_ERROR_NONE = 0, /* No details */ + IA_CSS_ISYS_ERROR_FW_INTERNAL_CONSISTENCY, /* enum */ + IA_CSS_ISYS_ERROR_HW_CONSISTENCY, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_INSUFFICIENT_RESOURCES, /* enum */ + IA_CSS_ISYS_ERROR_HW_REPORTED_STR2MMIO, /* HW code */ + IA_CSS_ISYS_ERROR_HW_REPORTED_SIG2CIO, /* HW code */ + IA_CSS_ISYS_ERROR_SENSOR_FW_SYNC, /* enum */ + IA_CSS_ISYS_ERROR_STREAM_IN_SUSPENSION, /* FW code */ + IA_CSS_ISYS_ERROR_RESPONSE_QUEUE_FULL, /* FW code */ + N_IA_CSS_ISYS_ERROR +}; + +/** + * enum ia_css_proxy_error. Describes the error type for the proxy detected by + * the FW + */ +enum ia_css_proxy_error { + IA_CSS_PROXY_ERROR_NONE = 0, + IA_CSS_PROXY_ERROR_INVALID_WRITE_REGION, + IA_CSS_PROXY_ERROR_INVALID_WRITE_OFFSET, + N_IA_CSS_PROXY_ERROR +}; + +#endif /* __IA_CSS_ISYSAPI_FW_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h new file mode 100644 index 000000000000..bc056157cedb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h @@ -0,0 +1,21 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_FW_VERSION_H +#define __IA_CSS_ISYSAPI_FW_VERSION_H + +/* ISYSAPI FW VERSION is taken from Makefile for FW tests */ +#define BXT_FW_RELEASE_VERSION ISYS_FIRMWARE_VERSION + +#endif /* __IA_CSS_ISYSAPI_FW_VERSION_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h new file mode 100644 index 000000000000..c002b33bdfaf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h @@ -0,0 +1,113 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H +#define __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H + +#include "ia_css_isysapi_proxy_region_types.h" + +/* + * Definitions for IPU4_B0_PROXY_INT + */ + +#if defined(IPU4_B0_PROXY_INT) + +/** + * enum ipu4_b0_ia_css_proxy_write_region. Provides the list of regions for ipu4B0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4_b0_ia_css_proxy_write_region { + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_0_ERROR_FILL_RATE = 0, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_1_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_2_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_3_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_4_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_5_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_6_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_7_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_8_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_9_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_URGENT_THRESHOLD, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_CRITICAL_THRESHOLD, + N_IPU4_B0_IA_CSS_PROXY_WRITE_REGION +}; + +struct ia_css_proxy_write_region_description ipu4_b0_reg_write_desc[N_IPU4_B0_IA_CSS_PROXY_WRITE_REGION] = { + /* base_addr, offset */ + {0x64128, /*input_system_csi2_logic_s2m_a_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_0_ERROR_FILL_RATE*/ + {0x65128, /*input_system_csi2_logic_s2m_b_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_1_ERROR_FILL_RATE*/ + {0x66128, /*input_system_csi2_logic_s2m_c_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_2_ERROR_FILL_RATE*/ + {0x67128, /*input_system_csi2_logic_s2m_d_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_3_ERROR_FILL_RATE*/ + {0x6C128, /*input_system_csi2_3ph_logic_s2m_a_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_4_ERROR_FILL_RATE*/ + {0x6C928, /*input_system_csi2_3ph_logic_s2m_b_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_5_ERROR_FILL_RATE*/ + {0x6D128, /*input_system_csi2_3ph_logic_s2m_0_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_6_ERROR_FILL_RATE*/ + {0x6D928, /*input_system_csi2_3ph_logic_s2m_1_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_7_ERROR_FILL_RATE*/ + {0x6E128, /*input_system_csi2_3ph_logic_s2m_2_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_8_ERROR_FILL_RATE*/ + {0x6E928, /*input_system_csi2_3ph_logic_s2m_3_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_9_ERROR_FILL_RATE*/ + {0x7800C, /*input_system_unis_logic_gda_irq_urgent_threshold*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_URGENT_THRESHOLD*/ + {0x78010, /*input_system_unis_logic_gda_irq_critical_threshold*/ 4} /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_CRITICAL_THRESHOLD*/ +}; + +#endif /*defined(IPU4_B0_PROXY_INT)*/ + +/* + * Definitions for IPU4P_A0_PROXY_INT + */ + +#if defined(IPU4P_A0_PROXY_INT) + +/** + * enum ipu4p_a0_ia_css_proxy_write_region. Provides the list of regions for ipu4pA0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4p_a0_ia_css_proxy_write_region { + N_IPU4P_A0_IA_CSS_PROXY_WRITE_REGION +}; + +#define IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE + +#ifndef IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE +struct ia_css_proxy_write_region_description ipu4p_a0_reg_write_desc[N_IPU4P_A0_IA_CSS_PROXY_WRITE_REGION] = { +} +#endif /*IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE*/ + +#endif /*defined(IPU4P_A0_PROXY_INT)*/ + +/* + * Definitions for IPU4P_B0_PROXY_INT + */ + +#if defined(IPU4P_B0_PROXY_INT) + +/** + * enum ipu4p_b0_ia_css_proxy_write_region. Provides the list of regions for ipu4pB0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4p_b0_ia_css_proxy_write_region { + IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IWAKE_THRESHOLD = 0, + IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_ENABLE_IWAKE, + N_IPU4P_B0_IA_CSS_PROXY_WRITE_REGION +}; + +struct ia_css_proxy_write_region_description ipu4p_b0_reg_write_desc[N_IPU4P_B0_IA_CSS_PROXY_WRITE_REGION] = { + /* base_addr, max_offset */ + /*input_system_unis_logic_gda_iwake_threshold*/ + {0x78014, 4}, /*IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IWAKE_THRESHOLD*/ + /*input_system_unis_logic_gda_enable_iwake*/ + {0x7801C, 4} /*IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_ENABLE_IWAKE*/ +}; + +#endif /*defined(IPU4P_B0_PROXY_INT)*/ + +#endif /* __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h new file mode 100644 index 000000000000..045f089e5a4c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H +#define __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H + + +struct ia_css_proxy_write_region_description { + uint32_t base_addr; + uint32_t offset; +}; + +#endif /* __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_types.h new file mode 100644 index 000000000000..481a7dc7b481 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/interface/ia_css_isysapi_types.h @@ -0,0 +1,349 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_TYPES_H +#define __IA_CSS_ISYSAPI_TYPES_H + +#include "ia_css_isysapi_fw_types.h" +#include "type_support.h" + +#include "ia_css_return_token.h" +#include "ia_css_output_buffer.h" +#include "ia_css_input_buffer.h" +#include "ia_css_terminal_defs.h" + +/** + * struct ia_css_isys_buffer_partition - buffer partition information + * @num_gda_pages: Number of virtual gda pages available for each virtual stream + */ +struct ia_css_isys_buffer_partition { + unsigned int num_gda_pages[STREAM_ID_MAX]; +}; + +/** + * This should contain the driver specified info for sys + */ +struct ia_css_driver_sys_config { + unsigned int ssid; + unsigned int mmid; + unsigned int num_send_queues; /* # of MSG send queues */ + unsigned int num_recv_queues; /* # of MSG recv queues */ + unsigned int send_queue_size; /* max # tokens per queue */ + unsigned int recv_queue_size; /* max # tokens per queue */ + + unsigned int icache_prefetch; /* enable prefetching for SPC */ +}; + +/** + * This should contain the driver specified info for proxy write queues + */ +struct ia_css_driver_proxy_config { + /* max # tokens per PROXY send/recv queue. + * Proxy queues are used for write access purpose + */ + unsigned int proxy_write_queue_size; +}; + + /** + * struct ia_css_isys_device_cfg_data - ISYS device configuration data + * @driver_sys + * @buffer_partition: Information required for the virtual SRAM + * space partition of the streams. + * @driver_proxy + * @secure: Driver needs to set 'secure' to indicate the intention + * when invoking ia_css_isys_context_create() in + * HAS_DUAL_CMD_CTX_SUPPORT case. If 'true', it's for + * secure case. + */ +struct ia_css_isys_device_cfg_data { + struct ia_css_driver_sys_config driver_sys; + struct ia_css_isys_buffer_partition buffer_partition; + struct ia_css_driver_proxy_config driver_proxy; + bool secure; + unsigned vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +/** + * struct ia_css_isys_resolution: Generic resolution structure. + * @Width + * @Height + */ +struct ia_css_isys_resolution { + unsigned int width; + unsigned int height; +}; + +/** + * struct ia_css_isys_output_pin_payload + * @out_buf_id: Points to output pin buffer - buffer identifier + * @addr: Points to output pin buffer - CSS Virtual Address + * @compressed: Request frame compression (1), or not (0) + * This must be the same as ia_css_isys_output_pin_info::reserve_compression + */ +struct ia_css_isys_output_pin_payload { + ia_css_return_token out_buf_id; + ia_css_output_buffer_css_address addr; + unsigned int compress; +}; + +/** + * struct ia_css_isys_output_pin_info + * @input_pin_id: input pin id/index which is source of + * the data for this output pin + * @output_res: output pin resolution + * @stride: output stride in Bytes (not valid for statistics) + * @pt: pin type + * @ft: frame format type + * @watermark_in_lines: pin watermark level in lines + * @send_irq: assert if pin event should trigger irq + * @link_id: identifies PPG to connect to, link_id = 0 implies offline + * while link_id > 0 implies buffer_chasing or online mode + * can be entered. + * @reserve_compression: Reserve compression resources for pin. + * @payload_buf_size: Minimum size in Bytes of all buffers that will be supplied for capture + * on this pin (i.e. addressed by ia_css_isys_output_pin_payload::addr) + */ +struct ia_css_isys_output_pin_info { + unsigned int input_pin_id; + struct ia_css_isys_resolution output_res; + unsigned int stride; + enum ia_css_isys_pin_type pt; + enum ia_css_isys_frame_format_type ft; + unsigned int watermark_in_lines; + unsigned int send_irq; + enum ia_css_isys_link_id link_id; + unsigned int reserve_compression; + unsigned int payload_buf_size; +}; + +/** + * struct ia_css_isys_param_pin + * @param_buf_id: Points to param buffer - buffer identifier + * @addr: Points to param buffer - CSS Virtual Address + */ +struct ia_css_isys_param_pin { + ia_css_return_token param_buf_id; + ia_css_input_buffer_css_address addr; +}; + +/** + * struct ia_css_isys_input_pin_info + * @input_res: input resolution + * @dt: mipi data type + * @mipi_store_mode: defines if legacy long packet header will be stored or + * discarded if discarded, output pin pin type for this + * input pin can only be MIPI + * @dt_rename_mode: defines if MIPI data is encapsulated in some other + * data type + * @mapped_dt: Encapsulating in mipi data type(what sensor sends) + */ +struct ia_css_isys_input_pin_info { + struct ia_css_isys_resolution input_res; + enum ia_css_isys_mipi_data_type dt; + enum ia_css_isys_mipi_store_mode mipi_store_mode; + enum ia_css_isys_mipi_dt_rename_mode dt_rename_mode; + enum ia_css_isys_mipi_data_type mapped_dt; +}; + +/** + * struct ia_css_isys_isa_cfg. Describes the ISA cfg + */ +struct ia_css_isys_isa_cfg { + /* Following sets resolution information neeed by the IS GP registers, + * For index IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED, + * it is needed when there is RAW_NS pin + * For index IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED, + * it is needed when there is RAW_S pin + */ + struct ia_css_isys_resolution isa_res[N_IA_CSS_ISYS_RESOLUTION_INFO]; + /* acc id 0, set if process required */ + unsigned int blc_enabled; + /* acc id 1, set if process required */ + unsigned int lsc_enabled; + /* acc id 2, set if process required */ + unsigned int dpc_enabled; + /* acc id 3, set if process required */ + unsigned int downscaler_enabled; + /* acc id 4, set if process required */ + unsigned int awb_enabled; + /* acc id 5, set if process required */ + unsigned int af_enabled; + /* acc id 6, set if process required */ + unsigned int ae_enabled; + /* acc id 7, disabled, or type of paf enabled*/ + enum ia_css_isys_type_paf paf_type; + /* Send irq for any statistics buffers which got completed */ + unsigned int send_irq_stats_ready; + /* Send response for any statistics buffers which got completed */ + unsigned int send_resp_stats_ready; +}; + +/** + * struct ia_css_isys_cropping - cropping coordinates + * Left/Top offsets are INCLUDED + * Right/Bottom offsets are EXCLUDED + * Horizontal: [left_offset,right_offset) + * Vertical: [top_offset,bottom_offset) + * Padding is supported + */ +struct ia_css_isys_cropping { + int top_offset; + int left_offset; + int bottom_offset; + int right_offset; +}; + + /** + * struct ia_css_isys_stream_cfg_data + * ISYS stream configuration data structure + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) + * @isl_use: indicates whether stream requires ISL and how + * @compfmt: de-compression setting for User Defined Data + * @isa_cfg: details about what ACCs are active if ISA is used + * @crop: defines cropping resolution for the + * maximum number of input pins which can be cropped, + * it is directly mapped to the HW devices + * @send_irq_sof_discarded: send irq on discarded frame sof response + * - if '1' it will override the send_resp_sof_discarded and send + * the response + * - if '0' the send_resp_sof_discarded will determine whether to + * send the response + * @send_irq_eof_discarded: send irq on discarded frame eof response + * - if '1' it will override the send_resp_eof_discarded and send + * the response + * - if '0' the send_resp_eof_discarded will determine whether to + * send the response + * @send_resp_sof_discarded: send response for discarded frame sof detected, + * used only when send_irq_sof_discarded is '0' + * @send_resp_eof_discarded: send response for discarded frame eof detected, + * used only when send_irq_eof_discarded is '0' + * @the rest: input/output pin descriptors + */ +struct ia_css_isys_stream_cfg_data { + enum ia_css_isys_stream_source src; + enum ia_css_isys_mipi_vc vc; + enum ia_css_isys_isl_use isl_use; + unsigned int compfmt; + struct ia_css_isys_isa_cfg isa_cfg; + struct ia_css_isys_cropping crop[N_IA_CSS_ISYS_CROPPING_LOCATION]; + unsigned int send_irq_sof_discarded; + unsigned int send_irq_eof_discarded; + unsigned int send_resp_sof_discarded; + unsigned int send_resp_eof_discarded; + unsigned int nof_input_pins; + unsigned int nof_output_pins; + struct ia_css_isys_input_pin_info input_pins[MAX_IPINS]; + struct ia_css_isys_output_pin_info output_pins[MAX_OPINS]; +}; + +/** + * struct ia_css_isys_frame_buff_set - frame buffer set + * @output_pins: output pin addresses + * @process_group_light: process_group_light buffer address + * @send_irq_sof: send irq on frame sof response + * - if '1' it will override the send_resp_sof and send + * the response + * - if '0' the send_resp_sof will determine whether to send + * the response + * @send_irq_eof: send irq on frame eof response + * - if '1' it will override the send_resp_eof and send + * the response + * - if '0' the send_resp_eof will determine whether to send + * the response + * @send_resp_sof: send response for frame sof detected, + * used only when send_irq_sof is '0' + * @send_resp_eof: send response for frame eof detected, + * used only when send_irq_eof is '0' + * @frame_counter: frame number associated with this buffer set. + */ +struct ia_css_isys_frame_buff_set { + struct ia_css_isys_output_pin_payload output_pins[MAX_OPINS]; + struct ia_css_isys_param_pin process_group_light; + unsigned int send_irq_sof; + unsigned int send_irq_eof; + unsigned int send_irq_capture_ack; + unsigned int send_irq_capture_done; + unsigned int send_resp_sof; + unsigned int send_resp_eof; + uint8_t frame_counter; +}; + +/** + * struct ia_css_isys_resp_info + * @type: response type + * @stream_handle: stream id the response corresponds to + * @timestamp: Time information for event if available + * @error: error code if something went wrong + * @error_details: depending on error code, it may contain additional + * error info + * @pin: this var is valid for pin event related responses, + * contains pin addresses + * @pin_id: this var is valid for pin event related responses, + * contains pin id that the pin payload corresponds to + * @process_group_light: this var is valid for stats ready related responses, + * contains process group addresses + * @acc_id: this var is valid for stats ready related responses, + * contains accelerator id that finished producing + * all related statistics + * @frame_counter: valid for STREAM_START_AND_CAPTURE_DONE, + * STREAM_CAPTURE_DONE and STREAM_CAPTURE_DISCARDED + * @written_direct: indicates if frame was written direct (online mode) or to DDR. + */ +struct ia_css_isys_resp_info { + enum ia_css_isys_resp_type type; + unsigned int stream_handle; + unsigned int timestamp[2]; + enum ia_css_isys_error error; + unsigned int error_details; + struct ia_css_isys_output_pin_payload pin; + unsigned int pin_id; + struct ia_css_isys_param_pin process_group_light; + unsigned int acc_id; + uint8_t frame_counter; + uint8_t written_direct; +}; + +/** + * struct ia_css_proxy_write_req_val + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @region_index: region id for the write request + * @offset: Offset to the specific register within the region + * @value: Value to be written to register + */ +struct ia_css_proxy_write_req_val { + uint32_t request_id; + uint32_t region_index; + uint32_t offset; + uint32_t value; +}; + +/** + * struct ia_css_proxy_write_req_resp + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @error: error code if something went wrong + * @error_details: error detail includes either offset or region index + * information which caused proxy request to be rejected + * (invalid access request) + */ +struct ia_css_proxy_write_req_resp { + uint32_t request_id; + enum ia_css_proxy_error error; + uint32_t error_details; +}; + + +#endif /* __IA_CSS_ISYSAPI_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/isysapi.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/isysapi.mk new file mode 100644 index 000000000000..0d06298f9acb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/isysapi.mk @@ -0,0 +1,77 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is ISYSAPI + +include $(MODULES_DIR)/config/isys/subsystem_$(IPU_SYSVER).mk + +ISYSAPI_DIR=$${MODULES_DIR}/isysapi + +ISYSAPI_INTERFACE=$(ISYSAPI_DIR)/interface +ISYSAPI_SOURCES=$(ISYSAPI_DIR)/src +ISYSAPI_EXTINCLUDE=$${MODULES_DIR}/support +ISYSAPI_EXTINTERFACE=$${MODULES_DIR}/syscom/interface + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_public.c + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_private.c + +# ISYSAPI Trace Log Level = ISYSAPI_TRACE_LOG_LEVEL_NORMAL +# Other options are [ISYSAPI_TRACE_LOG_LEVEL_OFF, ISYSAPI_TRACE_LOG_LEVEL_DEBUG] +ifndef ISYSAPI_TRACE_CONFIG_HOST + ISYSAPI_TRACE_CONFIG_HOST=ISYSAPI_TRACE_LOG_LEVEL_NORMAL +endif +ifndef ISYSAPI_TRACE_CONFIG_FW + ISYSAPI_TRACE_CONFIG_FW=ISYSAPI_TRACE_LOG_LEVEL_NORMAL +endif + +ISYSAPI_HOST_CPPFLAGS += -DISYSAPI_TRACE_CONFIG=$(ISYSAPI_TRACE_CONFIG_HOST) +ISYSAPI_FW_CPPFLAGS += -DISYSAPI_TRACE_CONFIG=$(ISYSAPI_TRACE_CONFIG_FW) + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_public_trace.c + +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_INTERFACE) +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_EXTINCLUDE) +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_EXTINTERFACE) +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include/default_system +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/include/ipu/dai +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/include/ipu + +ISYSAPI_FW_FILES += $(ISYSAPI_SOURCES)/isys_fw.c +ISYSAPI_FW_FILES += $(ISYSAPI_SOURCES)/isys_fw_utils.c + +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_INTERFACE) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_SOURCES)/$(IPU_SYSVER) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_EXTINCLUDE) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_EXTINTERFACE) +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include/default_system +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/include/ipu/dai +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/include/ipu + +ISYSAPI_FW_CPPFLAGS += -DWA_HSD1805168877=$(WA_HSD1805168877) + +ISYSAPI_HOST_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) + +ifeq ($(ISYS_HAS_DUAL_CMD_CTX_SUPPORT), 1) +ISYSAPI_HOST_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(ISYS_HAS_DUAL_CMD_CTX_SUPPORT) +ISYSAPI_FW_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(ISYS_HAS_DUAL_CMD_CTX_SUPPORT) +endif + +ifdef AB_CONFIG_ARRAY_SIZE +ISYSAPI_FW_CPPFLAGS += -DAB_CONFIG_ARRAY_SIZE=$(AB_CONFIG_ARRAY_SIZE) +else +ISYSAPI_FW_CPPFLAGS += -DAB_CONFIG_ARRAY_SIZE=1 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.c new file mode 100644 index 000000000000..8297a1ff2d1b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.c @@ -0,0 +1,980 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_isys_private.h" +/* The following is needed for the contained data types */ +#include "ia_css_isys_fw_bridged_types.h" +#include "ia_css_isysapi_types.h" +#include "ia_css_syscom_config.h" +/* + * The following header file is needed for the + * stddef.h (NULL), + * limits.h (CHAR_BIT definition). + */ +#include "type_support.h" +#include "error_support.h" +#include "ia_css_isysapi_trace.h" +#include "misc_support.h" +#include "cpu_mem_support.h" +#include "storage_class.h" + +#include "ia_css_shared_buffer_cpu.h" + +/* + * defines how many stream cfg host may sent concurrently + * before receiving the stream ack + */ +#define STREAM_CFG_BUFS_PER_MSG_QUEUE (1) +#define NEXT_FRAME_BUFS_PER_MSG_QUEUE \ + (ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_MSG] + 4 + 1) +/* + * There is an edge case that host has filled the full queue + * with capture requests (ctx->send_queue_size), + * SP reads and HW-queues all of them (4), + * while in the meantime host continues queueing capture requests + * without checking for responses which SP will have sent with each HW-queue + * capture request (if it does then the 4 is much more improbable to appear, + * but still not impossible). + * After this, host tries to queue an extra capture request + * even though there is no space in the msg queue because msg queue + * is checked at a later point, so +1 is needed + */ + +/* + * A DT is supported assuming when the MIPI packets + * have the same size even when even/odd lines are different, + * and the size is the average per line + */ +#define IA_CSS_UNSUPPORTED_DATA_TYPE (0) +static const uint32_t +ia_css_isys_extracted_bits_per_pixel_per_mipi_data_type[ + N_IA_CSS_ISYS_MIPI_DATA_TYPE] = { + /* + * Remove Prefix "IA_CSS_ISYS_MIPI_DATA_TYPE_" in comments + * to align with Checkpatch 80 characters requirements + * For detailed comments of each field, please refer to + * definition of enum ia_css_isys_mipi_data_type{} in + * isysapi/interface/ia_css_isysapi_fw_types.h + */ + 64, /* [0x00] FRAME_START_CODE */ + 64, /* [0x01] FRAME_END_CODE */ + 64, /* [0x02] LINE_START_CODE Optional */ + 64, /* [0x03] LINE_END_CODE Optional */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x04] RESERVED_0x04 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x05] RESERVED_0x05 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x06] RESERVED_0x06 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x07] RESERVED_0x07 */ + 64, /* [0x08] GENERIC_SHORT1 */ + 64, /* [0x09] GENERIC_SHORT2 */ + 64, /* [0x0A] GENERIC_SHORT3 */ + 64, /* [0x0B] GENERIC_SHORT4 */ + 64, /* [0x0C] GENERIC_SHORT5 */ + 64, /* [0x0D] GENERIC_SHORT6 */ + 64, /* [0x0E] GENERIC_SHORT7 */ + 64, /* [0x0F] GENERIC_SHORT8 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x10] NULL To be ignored */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x11] BLANKING_DATA To be ignored */ + 8, /* [0x12] EMBEDDED non Image Data */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x13] RESERVED_0x13 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x14] RESERVED_0x14 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x15] RESERVED_0x15 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x16] RESERVED_0x16 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x17] RESERVED_0x17 */ + 12, /* [0x18] YUV420_8 */ + 15, /* [0x19] YUV420_10 */ + 12, /* [0x1A] YUV420_8_LEGACY */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x1B] RESERVED_0x1B */ + 12, /* [0x1C] YUV420_8_SHIFT */ + 15, /* [0x1D] YUV420_10_SHIFT */ + 16, /* [0x1E] YUV422_8 */ + 20, /* [0x1F] YUV422_10 */ + 16, /* [0x20] RGB_444 */ + 16, /* [0x21] RGB_555 */ + 16, /* [0x22] RGB_565 */ + 18, /* [0x23] RGB_666 */ + 24, /* [0x24] RGB_888 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x25] RESERVED_0x25 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x26] RESERVED_0x26 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x27] RESERVED_0x27 */ + 6, /* [0x28] RAW_6 */ + 7, /* [0x29] RAW_7 */ + 8, /* [0x2A] RAW_8 */ + 10, /* [0x2B] RAW_10 */ + 12, /* [0x2C] RAW_12 */ + 14, /* [0x2D] RAW_14 */ + 16, /* [0x2E] RAW_16 */ + 8, /* [0x2F] BINARY_8 */ + 8, /* [0x30] USER_DEF1 */ + 8, /* [0x31] USER_DEF2 */ + 8, /* [0x32] USER_DEF3 */ + 8, /* [0x33] USER_DEF4 */ + 8, /* [0x34] USER_DEF5 */ + 8, /* [0x35] USER_DEF6 */ + 8, /* [0x36] USER_DEF7 */ + 8, /* [0x37] USER_DEF8 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x38] RESERVED_0x38 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x39] RESERVED_0x39 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3A] RESERVED_0x3A */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3B] RESERVED_0x3B */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3C] RESERVED_0x3C */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3D] RESERVED_0x3D */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3E] RESERVED_0x3E */ + IA_CSS_UNSUPPORTED_DATA_TYPE /* [0x3F] RESERVED_0x3F */ +}; + +STORAGE_CLASS_INLINE int get_stream_cfg_buff_slot( + struct ia_css_isys_context *ctx, + int stream_handle, + int stream_cfg_buff_counter) +{ + NOT_USED(ctx); + return (stream_handle * STREAM_CFG_BUFS_PER_MSG_QUEUE) + + stream_cfg_buff_counter; +} + +STORAGE_CLASS_INLINE int get_next_frame_buff_slot( + struct ia_css_isys_context *ctx, + int stream_handle, + int next_frame_buff_counter) +{ + NOT_USED(ctx); + return (stream_handle * NEXT_FRAME_BUFS_PER_MSG_QUEUE) + + next_frame_buff_counter; +} + +STORAGE_CLASS_INLINE void free_comm_buff_shared_mem( + struct ia_css_isys_context *ctx, + int stream_handle, + int stream_cfg_buff_counter, + int next_frame_buff_counter) +{ + int buff_slot; + + /* Initialiser is the current value of stream_handle */ + for (; stream_handle >= 0; stream_handle--) { + /* + * Initialiser is the current value of stream_cfg_buff_counter + */ + for (; stream_cfg_buff_counter >= 0; + stream_cfg_buff_counter--) { + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, stream_cfg_buff_counter); + ia_css_shared_buffer_free( + ctx->ssid, ctx->mmid, + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot]); + } + /* Set for the next iteration */ + stream_cfg_buff_counter = STREAM_CFG_BUFS_PER_MSG_QUEUE - 1; + /* + * Initialiser is the current value of next_frame_buff_counter + */ + for (; next_frame_buff_counter >= 0; + next_frame_buff_counter--) { + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, next_frame_buff_counter); + ia_css_shared_buffer_free( + ctx->ssid, ctx->mmid, + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot]); + } + next_frame_buff_counter = NEXT_FRAME_BUFS_PER_MSG_QUEUE - 1; + } +} + +/* + * ia_css_isys_constr_comm_buff_queue() + */ +int ia_css_isys_constr_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + int stream_handle; + int stream_cfg_buff_counter; + int next_frame_buff_counter; + int buff_slot; + + verifret(ctx, EFAULT); /* Host Consistency */ + + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id = + (ia_css_shared_buffer *) + ia_css_cpu_mem_alloc(ctx-> + num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] * + STREAM_CFG_BUFS_PER_MSG_QUEUE * + sizeof(ia_css_shared_buffer)); + verifret(ctx->isys_comm_buffer_queue.pstream_cfg_buff_id != NULL, + EFAULT); + + ctx->isys_comm_buffer_queue.pnext_frame_buff_id = + (ia_css_shared_buffer *) + ia_css_cpu_mem_alloc(ctx-> + num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] * + NEXT_FRAME_BUFS_PER_MSG_QUEUE * + sizeof(ia_css_shared_buffer)); + if (ctx->isys_comm_buffer_queue.pnext_frame_buff_id == NULL) { + ia_css_cpu_mem_free( + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id); + verifret(0, EFAULT); /* return EFAULT; equivalent */ + } + + for (stream_handle = 0; stream_handle < + (int)ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + stream_handle++) { + /* Initialisation needs to happen here for both loops */ + stream_cfg_buff_counter = 0; + next_frame_buff_counter = 0; + + for (; stream_cfg_buff_counter < STREAM_CFG_BUFS_PER_MSG_QUEUE; + stream_cfg_buff_counter++) { + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, stream_cfg_buff_counter); + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot] = + ia_css_shared_buffer_alloc( + ctx->ssid, ctx->mmid, + sizeof(struct + ia_css_isys_stream_cfg_data_comm)); + if (ctx->isys_comm_buffer_queue.pstream_cfg_buff_id[ + buff_slot] == 0) { + goto SHARED_BUFF_ALLOC_FAILURE; + } + } + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] = 0; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] = 0; + for (; next_frame_buff_counter < + (int)NEXT_FRAME_BUFS_PER_MSG_QUEUE; + next_frame_buff_counter++) { + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, + next_frame_buff_counter); + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot] = + ia_css_shared_buffer_alloc( + ctx->ssid, ctx->mmid, + sizeof(struct + ia_css_isys_frame_buff_set_comm)); + if (ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot] == 0) { + goto SHARED_BUFF_ALLOC_FAILURE; + } + } + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] = 0; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] = 0; + } + + return 0; + +SHARED_BUFF_ALLOC_FAILURE: + /* stream_handle has correct value for calling the free function */ + /* prepare stream_cfg_buff_counter for calling the free function */ + stream_cfg_buff_counter--; + /* prepare next_frame_buff_counter for calling the free function */ + next_frame_buff_counter--; + free_comm_buff_shared_mem( + ctx, + stream_handle, + stream_cfg_buff_counter, + next_frame_buff_counter); + + verifret(0, EFAULT); /* return EFAULT; equivalent */ +} + +/* + * ia_css_isys_force_unmap_comm_buff_queue() + */ +int ia_css_isys_force_unmap_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + int stream_handle; + int buff_slot; + + verifret(ctx, EFAULT); /* Host Consistency */ + + IA_CSS_TRACE_0(ISYSAPI, WARNING, + "ia_css_isys_force_unmap_comm_buff_queue() called\n"); + for (stream_handle = 0; stream_handle < + (int)ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + stream_handle++) { + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]) <= + STREAM_CFG_BUFS_PER_MSG_QUEUE, EPROTO); + for (; ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] < + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle]; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]++) { + IA_CSS_TRACE_1(ISYSAPI, WARNING, + "CSS forced unmapping stream_cfg %d\n", + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]); + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + ia_css_shared_buffer_css_unmap( + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot]); + } + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]) <= + NEXT_FRAME_BUFS_PER_MSG_QUEUE, EPROTO); + for (; ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] < + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle]; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]++) { + IA_CSS_TRACE_1(ISYSAPI, WARNING, + "CSS forced unmapping next_frame %d\n", + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]); + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + ia_css_shared_buffer_css_unmap( + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot]); + } + } + + return 0; +} + +/* + * ia_css_isys_destr_comm_buff_queue() + */ +int ia_css_isys_destr_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + verifret(ctx, EFAULT); /* Host Consistency */ + + free_comm_buff_shared_mem( + ctx, + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] - 1, + STREAM_CFG_BUFS_PER_MSG_QUEUE - 1, + NEXT_FRAME_BUFS_PER_MSG_QUEUE - 1); + + ia_css_cpu_mem_free(ctx->isys_comm_buffer_queue.pnext_frame_buff_id); + ia_css_cpu_mem_free(ctx->isys_comm_buffer_queue.pstream_cfg_buff_id); + + return 0; +} + +STORAGE_CLASS_INLINE void resolution_host_to_css( + const struct ia_css_isys_resolution *resolution_host, + struct ia_css_isys_resolution_comm *resolution_css) +{ + resolution_css->width = resolution_host->width; + resolution_css->height = resolution_host->height; +} + +STORAGE_CLASS_INLINE void output_pin_payload_host_to_css( + const struct ia_css_isys_output_pin_payload *output_pin_payload_host, + struct ia_css_isys_output_pin_payload_comm *output_pin_payload_css) +{ + output_pin_payload_css->out_buf_id = + output_pin_payload_host->out_buf_id; + output_pin_payload_css->addr = output_pin_payload_host->addr; +#ifdef ENABLE_DEC400 + output_pin_payload_css->compress = output_pin_payload_host->compress; +#else + output_pin_payload_css->compress = 0; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void output_pin_info_host_to_css( + const struct ia_css_isys_output_pin_info *output_pin_info_host, + struct ia_css_isys_output_pin_info_comm *output_pin_info_css) +{ + output_pin_info_css->input_pin_id = output_pin_info_host->input_pin_id; + resolution_host_to_css( + &output_pin_info_host->output_res, + &output_pin_info_css->output_res); + output_pin_info_css->stride = output_pin_info_host->stride; + output_pin_info_css->pt = output_pin_info_host->pt; + output_pin_info_css->watermark_in_lines = + output_pin_info_host->watermark_in_lines; + output_pin_info_css->send_irq = output_pin_info_host->send_irq; + output_pin_info_css->ft = output_pin_info_host->ft; + output_pin_info_css->link_id = output_pin_info_host->link_id; +#ifdef ENABLE_DEC400 + output_pin_info_css->reserve_compression = output_pin_info_host->reserve_compression; + output_pin_info_css->payload_buf_size = output_pin_info_host->payload_buf_size; +#else + output_pin_info_css->reserve_compression = 0; + /* Though payload_buf_size was added for compression, set sane value for + * payload_buf_size, just in case... + */ + output_pin_info_css->payload_buf_size = + output_pin_info_host->stride * output_pin_info_host->output_res.height; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void param_pin_host_to_css( + const struct ia_css_isys_param_pin *param_pin_host, + struct ia_css_isys_param_pin_comm *param_pin_css) +{ + param_pin_css->param_buf_id = param_pin_host->param_buf_id; + param_pin_css->addr = param_pin_host->addr; +} + +STORAGE_CLASS_INLINE void input_pin_info_host_to_css( + const struct ia_css_isys_input_pin_info *input_pin_info_host, + struct ia_css_isys_input_pin_info_comm *input_pin_info_css) +{ + resolution_host_to_css( + &input_pin_info_host->input_res, + &input_pin_info_css->input_res); + if (input_pin_info_host->dt >= N_IA_CSS_ISYS_MIPI_DATA_TYPE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->dt out of range\n"); + return; + } + if (input_pin_info_host->dt_rename_mode >= N_IA_CSS_ISYS_MIPI_DT_MODE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->dt_rename_mode out of range\n"); + return; + } + /* Mapped DT check if data type renaming is being used*/ + if (input_pin_info_host->dt_rename_mode == IA_CSS_ISYS_MIPI_DT_RENAMED_MODE && + input_pin_info_host->mapped_dt >= N_IA_CSS_ISYS_MIPI_DATA_TYPE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->mapped_dt out of range\n"); + return; + } + input_pin_info_css->dt = input_pin_info_host->dt; + input_pin_info_css->mipi_store_mode = + input_pin_info_host->mipi_store_mode; + input_pin_info_css->bits_per_pix = + ia_css_isys_extracted_bits_per_pixel_per_mipi_data_type[ + input_pin_info_host->dt]; + if (input_pin_info_host->dt_rename_mode == IA_CSS_ISYS_MIPI_DT_RENAMED_MODE) { + input_pin_info_css->mapped_dt = input_pin_info_host->mapped_dt; + } + else { + input_pin_info_css->mapped_dt = N_IA_CSS_ISYS_MIPI_DATA_TYPE; + } +} + +STORAGE_CLASS_INLINE void isa_cfg_host_to_css( + const struct ia_css_isys_isa_cfg *isa_cfg_host, + struct ia_css_isys_isa_cfg_comm *isa_cfg_css) +{ + unsigned int i; + + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) { + resolution_host_to_css(&isa_cfg_host->isa_res[i], + &isa_cfg_css->isa_res[i]); + } + isa_cfg_css->cfg_fields = 0; + ISA_CFG_FIELD_SET(BLC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->blc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(LSC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->lsc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(DPC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->dpc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(DOWNSCALER_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->downscaler_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AWB_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->awb_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AF_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->af_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AE_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->ae_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(PAF_TYPE, isa_cfg_css->cfg_fields, + isa_cfg_host->paf_type); + ISA_CFG_FIELD_SET(SEND_IRQ_STATS_READY, isa_cfg_css->cfg_fields, + isa_cfg_host->send_irq_stats_ready ? 1 : 0); + ISA_CFG_FIELD_SET(SEND_RESP_STATS_READY, isa_cfg_css->cfg_fields, + (isa_cfg_host->send_irq_stats_ready || + isa_cfg_host->send_resp_stats_ready) ? 1 : 0); +} + +STORAGE_CLASS_INLINE void cropping_host_to_css( + const struct ia_css_isys_cropping *cropping_host, + struct ia_css_isys_cropping_comm *cropping_css) +{ + cropping_css->top_offset = cropping_host->top_offset; + cropping_css->left_offset = cropping_host->left_offset; + cropping_css->bottom_offset = cropping_host->bottom_offset; + cropping_css->right_offset = cropping_host->right_offset; + +} + +STORAGE_CLASS_INLINE int stream_cfg_data_host_to_css( + const struct ia_css_isys_stream_cfg_data *stream_cfg_data_host, + struct ia_css_isys_stream_cfg_data_comm *stream_cfg_data_css) +{ + unsigned int i; + + stream_cfg_data_css->src = stream_cfg_data_host->src; + stream_cfg_data_css->vc = stream_cfg_data_host->vc; + stream_cfg_data_css->isl_use = stream_cfg_data_host->isl_use; + stream_cfg_data_css->compfmt = stream_cfg_data_host->compfmt; + stream_cfg_data_css->isa_cfg.cfg_fields = 0; + + switch (stream_cfg_data_host->isl_use) { + case IA_CSS_ISYS_USE_SINGLE_ISA: + isa_cfg_host_to_css(&stream_cfg_data_host->isa_cfg, + &stream_cfg_data_css->isa_cfg); + /* deliberate fall-through */ + case IA_CSS_ISYS_USE_SINGLE_DUAL_ISL: + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) { + cropping_host_to_css(&stream_cfg_data_host->crop[i], + &stream_cfg_data_css->crop[i]); + } + break; + case IA_CSS_ISYS_USE_NO_ISL_NO_ISA: + break; + default: + break; + } + + stream_cfg_data_css->send_irq_sof_discarded = + stream_cfg_data_host->send_irq_sof_discarded ? 1 : 0; + stream_cfg_data_css->send_irq_eof_discarded = + stream_cfg_data_host->send_irq_eof_discarded ? 1 : 0; + stream_cfg_data_css->send_resp_sof_discarded = + stream_cfg_data_host->send_irq_sof_discarded ? + 1 : stream_cfg_data_host->send_resp_sof_discarded; + stream_cfg_data_css->send_resp_eof_discarded = + stream_cfg_data_host->send_irq_eof_discarded ? + 1 : stream_cfg_data_host->send_resp_eof_discarded; + stream_cfg_data_css->nof_input_pins = + stream_cfg_data_host->nof_input_pins; + stream_cfg_data_css->nof_output_pins = + stream_cfg_data_host->nof_output_pins; + for (i = 0; i < stream_cfg_data_host->nof_input_pins; i++) { + input_pin_info_host_to_css( + &stream_cfg_data_host->input_pins[i], + &stream_cfg_data_css->input_pins[i]); + verifret(stream_cfg_data_css->input_pins[i].bits_per_pix, + EINVAL); + } + for (i = 0; i < stream_cfg_data_host->nof_output_pins; i++) { + output_pin_info_host_to_css( + &stream_cfg_data_host->output_pins[i], + &stream_cfg_data_css->output_pins[i]); + } + return 0; +} + +STORAGE_CLASS_INLINE void frame_buff_set_host_to_css( + const struct ia_css_isys_frame_buff_set *frame_buff_set_host, + struct ia_css_isys_frame_buff_set_comm *frame_buff_set_css) +{ + int i; + + for (i = 0; i < MAX_OPINS; i++) { + output_pin_payload_host_to_css( + &frame_buff_set_host->output_pins[i], + &frame_buff_set_css->output_pins[i]); + } + + param_pin_host_to_css(&frame_buff_set_host->process_group_light, + &frame_buff_set_css->process_group_light); + frame_buff_set_css->send_irq_sof = + frame_buff_set_host->send_irq_sof ? 1 : 0; + frame_buff_set_css->send_irq_eof = + frame_buff_set_host->send_irq_eof ? 1 : 0; + frame_buff_set_css->send_irq_capture_done = + (uint8_t)frame_buff_set_host->send_irq_capture_done; + frame_buff_set_css->send_irq_capture_ack = + frame_buff_set_host->send_irq_capture_ack ? 1 : 0; + frame_buff_set_css->send_resp_sof = + frame_buff_set_host->send_irq_sof ? + 1 : frame_buff_set_host->send_resp_sof; + frame_buff_set_css->send_resp_eof = + frame_buff_set_host->send_irq_eof ? + 1 : frame_buff_set_host->send_resp_eof; + frame_buff_set_css->frame_counter = + frame_buff_set_host->frame_counter; +} + +STORAGE_CLASS_INLINE void buffer_partition_host_to_css( + const struct ia_css_isys_buffer_partition *buffer_partition_host, + struct ia_css_isys_buffer_partition_comm *buffer_partition_css) +{ + int i; + + for (i = 0; i < STREAM_ID_MAX; i++) { + buffer_partition_css->num_gda_pages[i] = + buffer_partition_host->num_gda_pages[i]; + } +} + +STORAGE_CLASS_INLINE void output_pin_payload_css_to_host( + const struct ia_css_isys_output_pin_payload_comm * + output_pin_payload_css, + struct ia_css_isys_output_pin_payload *output_pin_payload_host) +{ + output_pin_payload_host->out_buf_id = + output_pin_payload_css->out_buf_id; + output_pin_payload_host->addr = output_pin_payload_css->addr; +#ifdef ENABLE_DEC400 + output_pin_payload_host->compress = output_pin_payload_css->compress; +#else + output_pin_payload_host->compress = 0; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void param_pin_css_to_host( + const struct ia_css_isys_param_pin_comm *param_pin_css, + struct ia_css_isys_param_pin *param_pin_host) +{ + param_pin_host->param_buf_id = param_pin_css->param_buf_id; + param_pin_host->addr = param_pin_css->addr; + +} + +STORAGE_CLASS_INLINE void resp_info_css_to_host( + const struct ia_css_isys_resp_info_comm *resp_info_css, + struct ia_css_isys_resp_info *resp_info_host) +{ + resp_info_host->type = resp_info_css->type; + resp_info_host->timestamp[0] = resp_info_css->timestamp[0]; + resp_info_host->timestamp[1] = resp_info_css->timestamp[1]; + resp_info_host->stream_handle = resp_info_css->stream_handle; + resp_info_host->error = resp_info_css->error_info.error; + resp_info_host->error_details = + resp_info_css->error_info.error_details; + output_pin_payload_css_to_host( + &resp_info_css->pin, &resp_info_host->pin); + resp_info_host->pin_id = resp_info_css->pin_id; + param_pin_css_to_host(&resp_info_css->process_group_light, + &resp_info_host->process_group_light); + resp_info_host->acc_id = resp_info_css->acc_id; + resp_info_host->frame_counter = resp_info_css->frame_counter; + resp_info_host->written_direct = resp_info_css->written_direct; +} + +/* + * ia_css_isys_constr_fw_stream_cfg() + */ +int ia_css_isys_constr_fw_stream_cfg( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pstream_cfg_fw, + ia_css_shared_buffer *pbuf_stream_cfg_id, + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + ia_css_shared_buffer_cpu_address stream_cfg_cpu_addr; + ia_css_shared_buffer_css_address stream_cfg_css_addr; + int buff_slot; + int retval = 0; + unsigned int wrap_compensation; + const unsigned int wrap_condition = 0xFFFFFFFF; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(pstream_cfg_fw, EFAULT); /* Host Consistency */ + verifret(pbuf_stream_cfg_id, EFAULT); /* Host Consistency */ + verifret(stream_cfg, EFAULT); /* Host Consistency */ + + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]) < + STREAM_CFG_BUFS_PER_MSG_QUEUE, EPROTO); + buff_slot = get_stream_cfg_buff_slot(ctx, stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + *pbuf_stream_cfg_id = + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id[buff_slot]; + /* Host-FW Consistency */ + verifret(*pbuf_stream_cfg_id, EADDRNOTAVAIL); + + stream_cfg_cpu_addr = + ia_css_shared_buffer_cpu_map(*pbuf_stream_cfg_id); + /* Host-FW Consistency */ + verifret(stream_cfg_cpu_addr, EADDRINUSE); + + retval = stream_cfg_data_host_to_css(stream_cfg, stream_cfg_cpu_addr); + if (retval) + return retval; + + stream_cfg_cpu_addr = + ia_css_shared_buffer_cpu_unmap(*pbuf_stream_cfg_id); + /* Host Consistency */ + verifret(stream_cfg_cpu_addr, EADDRINUSE); + + stream_cfg_css_addr = + ia_css_shared_buffer_css_map(*pbuf_stream_cfg_id); + /* Host Consistency */ + verifret(stream_cfg_css_addr, EADDRINUSE); + + ia_css_shared_buffer_css_update(ctx->mmid, *pbuf_stream_cfg_id); + + *pstream_cfg_fw = stream_cfg_css_addr; + + /* + * cover head wrap around extreme case, + * in which case force tail to wrap around too + * while maintaining diff and modulo + */ + if (ctx->isys_comm_buffer_queue.stream_cfg_queue_head[stream_handle] == + wrap_condition) { + /* Value to be added to both head and tail */ + wrap_compensation = + /* + * Distance of wrap_condition to 0, + * will need to be added for wrapping around head to 0 + */ + (0 - wrap_condition) + + /* + * To force tail to also wrap around, + * since it has to happen concurrently + */ + STREAM_CFG_BUFS_PER_MSG_QUEUE + + /* To preserve the same modulo, + * since the previous will result in head modulo 0 + */ + (wrap_condition % STREAM_CFG_BUFS_PER_MSG_QUEUE); + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] += + wrap_compensation; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] += + wrap_compensation; + } + ctx->isys_comm_buffer_queue.stream_cfg_queue_head[stream_handle]++; + + return 0; +} + +/* + * ia_css_isys_constr_fw_next_frame() + */ +int ia_css_isys_constr_fw_next_frame( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pnext_frame_fw, + ia_css_shared_buffer *pbuf_next_frame_id, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + ia_css_shared_buffer_cpu_address next_frame_cpu_addr; + ia_css_shared_buffer_css_address next_frame_css_addr; + int buff_slot; + unsigned int wrap_compensation; + const unsigned int wrap_condition = 0xFFFFFFFF; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(pnext_frame_fw, EFAULT); /* Host Consistency */ + verifret(next_frame, EFAULT); /* Host Consistency */ + verifret(pbuf_next_frame_id, EFAULT); /* Host Consistency */ + + /* For some reason responses are not dequeued in time */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]) < + NEXT_FRAME_BUFS_PER_MSG_QUEUE, EPERM); + buff_slot = get_next_frame_buff_slot(ctx, stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + *pbuf_next_frame_id = + ctx->isys_comm_buffer_queue.pnext_frame_buff_id[buff_slot]; + /* Host-FW Consistency */ + verifret(*pbuf_next_frame_id, EADDRNOTAVAIL); + + /* map it in cpu */ + next_frame_cpu_addr = + ia_css_shared_buffer_cpu_map(*pbuf_next_frame_id); + /* Host-FW Consistency */ + verifret(next_frame_cpu_addr, EADDRINUSE); + + frame_buff_set_host_to_css(next_frame, next_frame_cpu_addr); + + /* unmap the buffer from cpu */ + next_frame_cpu_addr = + ia_css_shared_buffer_cpu_unmap(*pbuf_next_frame_id); + /* Host Consistency */ + verifret(next_frame_cpu_addr, EADDRINUSE); + + /* map it to css */ + next_frame_css_addr = + ia_css_shared_buffer_css_map(*pbuf_next_frame_id); + /* Host Consistency */ + verifret(next_frame_css_addr, EADDRINUSE); + + ia_css_shared_buffer_css_update(ctx->mmid, *pbuf_next_frame_id); + + *pnext_frame_fw = next_frame_css_addr; + + /* + * cover head wrap around extreme case, + * in which case force tail to wrap around too + * while maintaining diff and modulo + */ + if (ctx->isys_comm_buffer_queue.next_frame_queue_head[stream_handle] == + wrap_condition) { + /* Value to be added to both head and tail */ + wrap_compensation = + /* + * Distance of wrap_condition to 0, + * will need to be added for wrapping around head to 0 + */ + (0 - wrap_condition) + + /* + * To force tail to also wrap around, + * since it has to happen concurrently + */ + NEXT_FRAME_BUFS_PER_MSG_QUEUE + + /* + * To preserve the same modulo, + * since the previous will result in head modulo 0 + */ + (wrap_condition % NEXT_FRAME_BUFS_PER_MSG_QUEUE); + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] += + wrap_compensation; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] += + wrap_compensation; + } + ctx->isys_comm_buffer_queue.next_frame_queue_head[stream_handle]++; + + return 0; +} + +/* + * ia_css_isys_extract_fw_response() + */ +int ia_css_isys_extract_fw_response( + struct ia_css_isys_context *ctx, + const struct resp_queue_token *token, + struct ia_css_isys_resp_info *received_response) +{ + int buff_slot; + unsigned int css_address; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(token, EFAULT); /* Host Consistency */ + verifret(received_response, EFAULT); /* Host Consistency */ + + resp_info_css_to_host(&(token->resp_info), received_response); + + switch (token->resp_info.type) { + case IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[token->resp_info.stream_handle] - + ctx->isys_comm_buffer_queue.stream_cfg_queue_tail[ + token->resp_info.stream_handle]) > 0, EPROTO); + buff_slot = get_stream_cfg_buff_slot(ctx, + token->resp_info.stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[ + token->resp_info.stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + verifret((ia_css_shared_buffer)HOST_ADDRESS( + token->resp_info.buf_id) == + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot], EIO); + ctx->isys_comm_buffer_queue.stream_cfg_queue_tail[ + token->resp_info.stream_handle]++; + css_address = ia_css_shared_buffer_css_unmap( + (ia_css_shared_buffer) + HOST_ADDRESS(token->resp_info.buf_id)); + verifret(css_address, EADDRINUSE); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[token->resp_info.stream_handle] - + ctx->isys_comm_buffer_queue.next_frame_queue_tail[ + token->resp_info.stream_handle]) > 0, EPROTO); + buff_slot = get_next_frame_buff_slot(ctx, + token->resp_info.stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[ + token->resp_info.stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + verifret((ia_css_shared_buffer)HOST_ADDRESS( + token->resp_info.buf_id) == + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot], EIO); + ctx->isys_comm_buffer_queue.next_frame_queue_tail[ + token->resp_info.stream_handle]++; + css_address = ia_css_shared_buffer_css_unmap( + (ia_css_shared_buffer) + HOST_ADDRESS(token->resp_info.buf_id)); + verifret(css_address, EADDRINUSE); + break; + default: + break; + } + + return 0; +} + +/* + * ia_css_isys_extract_proxy_response() + */ +int ia_css_isys_extract_proxy_response( + const struct proxy_resp_queue_token *token, + struct ia_css_proxy_write_req_resp *preceived_response) +{ + verifret(token, EFAULT); /* Host Consistency */ + verifret(preceived_response, EFAULT); /* Host Consistency */ + + preceived_response->request_id = token->proxy_resp_info.request_id; + preceived_response->error = token->proxy_resp_info.error_info.error; + preceived_response->error_details = + token->proxy_resp_info.error_info.error_details; + + return 0; +} + +/* + * ia_css_isys_prepare_param() + */ +int ia_css_isys_prepare_param( + struct ia_css_isys_fw_config *isys_fw_cfg, + const struct ia_css_isys_buffer_partition *buf_partition, + const unsigned int num_send_queues[], + const unsigned int num_recv_queues[]) +{ + unsigned int i; + + verifret(isys_fw_cfg, EFAULT); /* Host Consistency */ + verifret(buf_partition, EFAULT); /* Host Consistency */ + verifret(num_send_queues, EFAULT); /* Host Consistency */ + verifret(num_recv_queues, EFAULT); /* Host Consistency */ + + buffer_partition_host_to_css(buf_partition, + &isys_fw_cfg->buffer_partition); + for (i = 0; i < N_IA_CSS_ISYS_QUEUE_TYPE; i++) { + isys_fw_cfg->num_send_queues[i] = num_send_queues[i]; + isys_fw_cfg->num_recv_queues[i] = num_recv_queues[i]; + } + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.h new file mode 100644 index 000000000000..d53fa53c9a81 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_private.h @@ -0,0 +1,156 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_PRIVATE_H +#define __IA_CSS_ISYS_PRIVATE_H + + +#include "type_support.h" +/* Needed for the structure member ia_css_sys_context * sys */ +#include "ia_css_syscom.h" +/* Needed for the definitions of STREAM_ID_MAX */ +#include "ia_css_isysapi.h" +/* The following is needed for the function arguments */ +#include "ia_css_isys_fw_bridged_types.h" + +#include "ia_css_shared_buffer.h" + + +/* Set for the respective error handling */ +#define VERIFY_DEVSTATE 1 + +#if (VERIFY_DEVSTATE != 0) +/** + * enum device_state + */ +enum device_state { + IA_CSS_ISYS_DEVICE_STATE_IDLE = 0, + IA_CSS_ISYS_DEVICE_STATE_CONFIGURED = 1, + IA_CSS_ISYS_DEVICE_STATE_READY = 2 +}; +#endif /* VERIFY_DEVSTATE */ + +/** + * enum stream_state + */ +enum stream_state { + IA_CSS_ISYS_STREAM_STATE_IDLE = 0, + IA_CSS_ISYS_STREAM_STATE_OPENED = 1, + IA_CSS_ISYS_STREAM_STATE_STARTED = 2 +}; + + +/** + * struct ia_css_isys_comm_buffer_queue + */ +struct ia_css_isys_comm_buffer_queue { + ia_css_shared_buffer *pstream_cfg_buff_id; + unsigned int stream_cfg_queue_head[STREAM_ID_MAX]; + unsigned int stream_cfg_queue_tail[STREAM_ID_MAX]; + ia_css_shared_buffer *pnext_frame_buff_id; + unsigned int next_frame_queue_head[STREAM_ID_MAX]; + unsigned int next_frame_queue_tail[STREAM_ID_MAX]; +}; + + +/** + * struct ia_css_isys_context + */ +struct ia_css_isys_context { + struct ia_css_syscom_context *sys; + /* add here any isys specific members that need + to be passed into the isys api functions as input */ + unsigned int ssid; + unsigned int mmid; + unsigned int num_send_queues[N_IA_CSS_ISYS_QUEUE_TYPE]; + unsigned int num_recv_queues[N_IA_CSS_ISYS_QUEUE_TYPE]; + unsigned int send_queue_size[N_IA_CSS_ISYS_QUEUE_TYPE]; + struct ia_css_isys_comm_buffer_queue isys_comm_buffer_queue; + unsigned int stream_nof_output_pins[STREAM_ID_MAX]; +#if (VERIFY_DEVSTATE != 0) + enum device_state dev_state; +#endif /* VERIFY_DEVSTATE */ + enum stream_state stream_state_array[STREAM_ID_MAX]; + /* If true, this context is created based on secure config */ + bool secure; +}; + + +/** + * ia_css_isys_constr_comm_buff_queue() + */ +extern int ia_css_isys_constr_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_force_unmap_comm_buff_queue() + */ +extern int ia_css_isys_force_unmap_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_destr_comm_buff_queue() + */ +extern int ia_css_isys_destr_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_constr_fw_stream_cfg() + */ +extern int ia_css_isys_constr_fw_stream_cfg( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pstream_cfg_fw, + ia_css_shared_buffer *pbuf_stream_cfg_id, + const struct ia_css_isys_stream_cfg_data *stream_cfg +); + +/** + * ia_css_isys_constr_fw_next_frame() + */ +extern int ia_css_isys_constr_fw_next_frame( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pnext_frame_fw, + ia_css_shared_buffer *pbuf_next_frame_id, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_extract_fw_response() + */ +extern int ia_css_isys_extract_fw_response( + struct ia_css_isys_context *ctx, + const struct resp_queue_token *token, + struct ia_css_isys_resp_info *received_response +); +extern int ia_css_isys_extract_proxy_response( + const struct proxy_resp_queue_token *token, + struct ia_css_proxy_write_req_resp *received_response +); + +/** + * ia_css_isys_prepare_param() + */ +extern int ia_css_isys_prepare_param( + struct ia_css_isys_fw_config *isys_fw_cfg, + const struct ia_css_isys_buffer_partition *buf_partition, + const unsigned int num_send_queues[], + const unsigned int num_recv_queues[] +); + +#endif /* __IA_CSS_ISYS_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public.c new file mode 100644 index 000000000000..478d49f51cdd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public.c @@ -0,0 +1,1283 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* TODO: REMOVE --> START IF EXTERNALLY INCLUDED/DEFINED */ +/* These are temporary, the correct numbers need to be inserted/linked */ +/* Until this happens, the following definitions stay here */ +#define INPUT_MIN_WIDTH 1 +#define INPUT_MAX_WIDTH 16384 +#define INPUT_MIN_HEIGHT 1 +#define INPUT_MAX_HEIGHT 16384 +#define OUTPUT_MIN_WIDTH 1 +#define OUTPUT_MAX_WIDTH 16384 +#define OUTPUT_MIN_HEIGHT 1 +#define OUTPUT_MAX_HEIGHT 16384 +/* REMOVE --> END IF EXTERNALLY INCLUDED/DEFINED */ + + +/* The FW bridged types are included through the following */ +#include "ia_css_isysapi.h" +/* The following provides the isys-sys context */ +#include "ia_css_isys_private.h" +/* The following provides the sys layer functions */ +#include "ia_css_syscom.h" + +#include "ia_css_cell.h" +#include "ipu_device_cell_properties.h" + +/* The following provides the tracing functions */ +#include "ia_css_isysapi_trace.h" +#include "ia_css_isys_public_trace.h" + +#include "ia_css_shared_buffer_cpu.h" +/* The following is needed for the + * stddef.h (NULL), + * limits.h (CHAR_BIT definition). + */ +#include "type_support.h" +#include "error_support.h" +#include "cpu_mem_support.h" +#include "math_support.h" +#include "misc_support.h" +#include "system_const.h" + +static int isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config); +static int isys_start_server( + const struct ia_css_isys_device_cfg_data *config); + +static int isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + int retval; + unsigned int stream_handle; + struct ia_css_isys_context *ctx; + struct ia_css_syscom_config sys; + /* Needs to be updated in case new type of queues are introduced */ + struct ia_css_syscom_queue_config input_queue_cfg[N_MAX_SEND_QUEUES]; + /* Needs to be updated in case new type of queues are introduced */ + struct ia_css_syscom_queue_config output_queue_cfg[N_MAX_RECV_QUEUES]; + struct ia_css_isys_fw_config isys_fw_cfg; + unsigned int proxy_write_queue_size; + unsigned int ssid; + unsigned int mmid; + unsigned int i; + + /* Printing "ENTRY isys_context_create" + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY isys_context_create\n"); + + verifret(config != NULL, EFAULT); + + /* Printing configuration information if tracing level = VERBOSE. */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_device_config_data(config); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + /* Runtime check for # of send and recv MSG queues */ + verifret(config->driver_sys.num_send_queues <= + N_MAX_MSG_SEND_QUEUES/*=STREAM_ID_MAX*/, EINVAL); + verifret(config->driver_sys.num_recv_queues <= + N_MAX_MSG_RECV_QUEUES, EINVAL); + + /* Runtime check for send and recv MSG queue sizes */ + verifret(config->driver_sys.send_queue_size <= MAX_QUEUE_SIZE, EINVAL); + verifret(config->driver_sys.recv_queue_size <= MAX_QUEUE_SIZE, EINVAL); + + /* TODO: return an error in case MAX_QUEUE_SIZE is exceeded + * (Similar to runtime check on MSG queue sizes) + */ + proxy_write_queue_size = uclip( + config->driver_proxy.proxy_write_queue_size, + MIN_QUEUE_SIZE, + MAX_QUEUE_SIZE); + + ctx = (struct ia_css_isys_context *) + ia_css_cpu_mem_alloc(sizeof(struct ia_css_isys_context)); + verifret(ctx != NULL, EFAULT); + *context = (HANDLE)ctx; + + /* Copy to the sys config the driver_sys config, + * and add the internal info (token sizes) + */ + ssid = config->driver_sys.ssid; + mmid = config->driver_sys.mmid; + sys.ssid = ssid; + sys.mmid = mmid; + + ctx->secure = config->secure; + /* Following operations need to be aligned with + * "enum ia_css_isys_queue_type" list (list of queue types) + */ + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + N_MAX_PROXY_SEND_QUEUES; + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + N_MAX_DEV_SEND_QUEUES; + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.num_send_queues; + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + N_MAX_PROXY_RECV_QUEUES; + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + 0; /* Common msg/dev return queue */ + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.num_recv_queues; + + sys.num_input_queues = + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + sys.num_output_queues = + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + + sys.input = input_queue_cfg; + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + i++) { + input_queue_cfg[BASE_PROXY_SEND_QUEUES + i].queue_size = + proxy_write_queue_size; + input_queue_cfg[BASE_PROXY_SEND_QUEUES + i].token_size = + sizeof(struct proxy_send_queue_token); + } + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + i++) { + input_queue_cfg[BASE_DEV_SEND_QUEUES + i].queue_size = + DEV_SEND_QUEUE_SIZE; + input_queue_cfg[BASE_DEV_SEND_QUEUES + i].token_size = + sizeof(struct send_queue_token); + } + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + i++) { + input_queue_cfg[BASE_MSG_SEND_QUEUES + i].queue_size = + config->driver_sys.send_queue_size; + input_queue_cfg[BASE_MSG_SEND_QUEUES + i].token_size = + sizeof(struct send_queue_token); + } + + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + proxy_write_queue_size; + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + DEV_SEND_QUEUE_SIZE; + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.send_queue_size; + + sys.output = output_queue_cfg; + for (i = 0; + i < ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + i++) { + output_queue_cfg[BASE_PROXY_RECV_QUEUES + i].queue_size = + proxy_write_queue_size; + output_queue_cfg[BASE_PROXY_RECV_QUEUES + i].token_size = + sizeof(struct proxy_resp_queue_token); + } + /* There is no recv DEV queue */ + for (i = 0; + i < ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + i++) { + output_queue_cfg[BASE_MSG_RECV_QUEUES + i].queue_size = + config->driver_sys.recv_queue_size; + output_queue_cfg[BASE_MSG_RECV_QUEUES + i].token_size = + sizeof(struct resp_queue_token); + } + + sys.regs_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_REGS); + sys.dmem_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_DMEM); + +#if HAS_DUAL_CMD_CTX_SUPPORT + sys.dmem_addr += config->secure ? REGMEM_SECURE_OFFSET : REGMEM_OFFSET; +#endif + + /* Prepare the param */ + ia_css_isys_prepare_param( + &isys_fw_cfg, + &config->buffer_partition, + ctx->num_send_queues, + ctx->num_recv_queues); + + /* parameter struct to be passed to fw */ + sys.specific_addr = &isys_fw_cfg; + /* parameters size */ + sys.specific_size = sizeof(isys_fw_cfg); + sys.secure = config->secure; + if (config->secure) { + sys.vtl0_addr_mask = config->vtl0_addr_mask; + } + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "isys_context_create || call ia_css_syscom_open()\n"); + /* The allocation of the queues will take place within this call and + * info will be stored in sys_context output + */ + ctx->sys = ia_css_syscom_open(&sys, NULL); + if (!ctx->sys) { + ia_css_cpu_mem_free(ctx); + return -EFAULT; + } + + /* Update the context with the id's */ + ctx->ssid = ssid; + ctx->mmid = mmid; + + for (stream_handle = 0; stream_handle < STREAM_ID_MAX; + stream_handle++) { + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_IDLE; + } + + retval = ia_css_isys_constr_comm_buff_queue(ctx); + if (retval) { + ia_css_syscom_close(ctx->sys); + ia_css_syscom_release(ctx->sys, 1); + ia_css_cpu_mem_free(ctx); + return retval; + } + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_CONFIGURED; +#endif /* VERIFY_DEVSTATE */ + + /* Printing device configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + /* Printing "LEAVE isys_context_create" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE isys_context_create\n"); + return 0; +} + +static int isys_start_server( + const struct ia_css_isys_device_cfg_data *config) +{ + verifret(config != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "isys_start_server || start SPC\n"); + /* The firmware is loaded and syscom is ready, start the SPC */ + ia_css_cell_start_prefetch(config->driver_sys.ssid, SPC0, + config->driver_sys.icache_prefetch); + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, "SPC prefetch: %d\n", + config->driver_sys.icache_prefetch); + return 0; +} + +/** + * ia_css_isys_device_open() - open and configure ISYS device + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + return isys_context_create(context, config); +} + +/* push context information to DMEM for FW to access */ +int ia_css_isys_context_store_dmem( + const HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *) *context; + + return ia_css_syscom_store_dmem(ctx->sys, config->driver_sys.ssid, config->vtl0_addr_mask); +} + +bool ia_css_isys_ab_spc_ready( + HANDLE * context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *) *context; + + return ia_css_syscom_is_ab_spc_ready(ctx->sys); +} + +int ia_css_isys_device_open( + const struct ia_css_isys_device_cfg_data *config) +{ + return isys_start_server(config); +} +#else +int ia_css_isys_device_open( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + int retval; + + retval = isys_context_create(context, config); + if (retval) { + IA_CSS_TRACE_1(ISYSAPI, ERROR, "ia_css_isys_device_open() failed (retval %d)\n", retval); + return retval; + } + + isys_start_server(config); + return 0; +} +#endif + +/** + * ia_css_isys_device_open_ready() - open and configure ISYS device + */ +int ia_css_isys_device_open_ready(HANDLE context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int i; + int retval; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_OPEN" + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_DEVICE_OPEN\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_CONFIGURED, EPERM); +#endif /* VERIFY_DEVSTATE */ + + /* Open the ports for all the non-MSG send queues (PROXY + DEV) */ + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + i++) { + retval = ia_css_syscom_send_port_open(ctx->sys, i); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + /* Open the ports for all the recv queues (PROXY + MSG) */ + for (i = 0; + i < (ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]); + i++) { + retval = ia_css_syscom_recv_port_open(ctx->sys, i); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_READY; +#endif /* VERIFY_DEVSTATE */ + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_OPEN_READY" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_DEVICE_OPEN_READY\n"); + return 0; +} + + + /** + * ia_css_isys_stream_open() - open and configure a virtual stream + */ +int ia_css_isys_stream_open( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int i; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address stream_cfg_fw = 0; + ia_css_shared_buffer buf_stream_cfg_id = (ia_css_shared_buffer)NULL; + /* Printing "ENTRY IA_CSS_ISYS_STREAM_OPEN" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_OPEN\n"); + + verifret(ctx, EFAULT); + + /* Printing stream configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_stream_config_data(stream_cfg); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_IDLE, EPERM); + + verifret(stream_cfg != NULL, EFAULT); + verifret(stream_cfg->src < N_IA_CSS_ISYS_STREAM_SRC, EINVAL); + verifret(stream_cfg->vc < N_IA_CSS_ISYS_MIPI_VC, EINVAL); + verifret(stream_cfg->isl_use < N_IA_CSS_ISYS_USE, EINVAL); + if (stream_cfg->isl_use != IA_CSS_ISYS_USE_NO_ISL_NO_ISA) { + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset >= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + OUTPUT_MIN_HEIGHT, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset <= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + OUTPUT_MAX_HEIGHT, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset >= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + OUTPUT_MIN_WIDTH, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset <= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + OUTPUT_MAX_WIDTH, EINVAL); + } + verifret(stream_cfg->nof_input_pins <= MAX_IPINS, EINVAL); + verifret(stream_cfg->nof_output_pins <= MAX_OPINS, EINVAL); + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + /* Verify input pin */ + verifret( + stream_cfg->input_pins[i].input_res.width >= + INPUT_MIN_WIDTH && + stream_cfg->input_pins[i].input_res.width <= + INPUT_MAX_WIDTH && + stream_cfg->input_pins[i].input_res.height >= + INPUT_MIN_HEIGHT && + stream_cfg->input_pins[i].input_res.height <= + INPUT_MAX_HEIGHT, EINVAL); + verifret(stream_cfg->input_pins[i].dt < + N_IA_CSS_ISYS_MIPI_DATA_TYPE, EINVAL); +/* #ifdef To be removed when driver inits the value */ +#ifdef DRIVER_INIT_MIPI_STORE_MODE + verifret(stream_cfg->input_pins[i].mipi_store_mode < + N_IA_CSS_ISYS_MIPI_STORE_MODE, EINVAL); +#endif /* DRIVER_INIT_MIPI_STORE_MODE */ + } + for (i = 0; i < stream_cfg->nof_output_pins; i++) { + /* Verify output pin */ + verifret(stream_cfg->output_pins[i].input_pin_id < + stream_cfg->nof_input_pins, EINVAL); + verifret(stream_cfg->output_pins[i].pt < + N_IA_CSS_ISYS_PIN_TYPE, EINVAL); + verifret(stream_cfg->output_pins[i].ft < + N_IA_CSS_ISYS_FRAME_FORMAT, EINVAL); + /* Verify that the stride is aligned to 64 bytes: HW spec */ + verifret(stream_cfg->output_pins[i].stride%(XMEM_WIDTH/8) == + 0, EINVAL); + verifret((stream_cfg->output_pins[i].output_res.width >= + OUTPUT_MIN_WIDTH) && + (stream_cfg->output_pins[i].output_res.width <= + OUTPUT_MAX_WIDTH) && + (stream_cfg->output_pins[i].output_res.height >= + OUTPUT_MIN_HEIGHT) && + (stream_cfg->output_pins[i].output_res.height <= + OUTPUT_MAX_HEIGHT), EINVAL); + verifret((stream_cfg->output_pins[i].pt == + IA_CSS_ISYS_PIN_TYPE_MIPI) || + (stream_cfg-> + input_pins[stream_cfg->output_pins[i].input_pin_id].mipi_store_mode != + IA_CSS_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER), EINVAL); + if (stream_cfg->isl_use == IA_CSS_ISYS_USE_SINGLE_ISA) { + switch (stream_cfg->output_pins[i].pt) { + case IA_CSS_ISYS_PIN_TYPE_RAW_NS: + /* Ensure the PIFCONV cropped resolution + * matches the RAW_NS output pin resolution + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].bottom_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].top_offset + + (int)stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].right_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].left_offset + + (int)stream_cfg->output_pins[i].output_res.width, EINVAL); + /* Ensure the ISAPF cropped resolution matches + * the Non-scaled ISA output resolution before + * the PIFCONV cropping, since nothing can + * modify the resolution in that part of + * the pipe + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset == + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].height, + EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset == + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].width, + EINVAL); + /* Ensure the Non-scaled ISA output resolution + * before the PIFCONV cropping bounds the + * RAW_NS pin output resolution since padding + * is not supported + */ + verifret(stream_cfg-> +isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].height >= +stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> +isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].width >= +stream_cfg->output_pins[i].output_res.width, EINVAL); + break; + case IA_CSS_ISYS_PIN_TYPE_RAW_S: + /* Ensure the ScaledPIFCONV cropped resolution + * matches the RAW_S output pin resolution + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].bottom_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].top_offset + + (int)stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].right_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].left_offset + + (int)stream_cfg->output_pins[i].output_res.width, EINVAL); + /* Ensure the ISAPF cropped resolution bounds + * the Scaled ISA output resolution before the + * ScaledPIFCONV cropping, since only IDS can + * modify the resolution, and this only to + * make it smaller + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset >= + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].height, + EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset >= + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].width, + EINVAL); + /* Ensure the Scaled ISA output resolution + * before the ScaledPIFCONV cropping bounds + * the RAW_S pin output resolution since + * padding is not supported + */ + verifret(stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].height >= + stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].width >= + stream_cfg->output_pins[i].output_res.width, EINVAL); + break; + default: + break; + } + } + } + + /* open 1 send queue/stream and a single receive queue + * if not existing + */ + retval = ia_css_syscom_send_port_open(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_OPEN; + retval = ia_css_isys_constr_fw_stream_cfg(ctx, stream_handle, + &stream_cfg_fw, &buf_stream_cfg_id, stream_cfg); + verifret(retval == 0, retval); + token.payload = stream_cfg_fw; + token.buf_handle = HOST_ADDRESS(buf_stream_cfg_id); + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_nof_output_pins[stream_handle] = + stream_cfg->nof_output_pins; + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_OPEN" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_OPEN\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_close() - close virtual stream + */ +int ia_css_isys_stream_close( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_CLOSE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_OPENED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_CLOSE; + token.stream_id = stream_handle; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + /* close 1 send queue/stream and the single receive queue + * if none is using it + */ + retval = ia_css_syscom_send_port_close(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + + ctx->stream_state_array[stream_handle] = IA_CSS_ISYS_STREAM_STATE_IDLE; + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_CLOSE\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_start() - starts handling a mipi virtual stream + */ +int ia_css_isys_stream_start( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address next_frame_fw = 0; + ia_css_shared_buffer buf_next_frame_id = (ia_css_shared_buffer)NULL; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_START" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_START\n"); + + verifret(ctx, EFAULT); + + /* Printing frame configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_isys_frame_buff_set(next_frame, + ctx->stream_nof_output_pins[stream_handle]); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_OPENED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + if (next_frame != NULL) { + token.send_type = + IA_CSS_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; + retval = ia_css_isys_constr_fw_next_frame(ctx, stream_handle, + &next_frame_fw, &buf_next_frame_id, next_frame); + verifret(retval == 0, retval); + token.payload = next_frame_fw; + token.buf_handle = HOST_ADDRESS(buf_next_frame_id); + } else { + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_START; + token.payload = 0; + token.buf_handle = 0; + } + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_STARTED; + /* Printing "LEAVE IA_CSS_ISYS_STREAM_START" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_START\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_stop() - Stops a mipi virtual stream + */ +int ia_css_isys_stream_stop( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_STOP" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_STOP\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_DEV_SEND_QUEUES)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_STOP; + token.stream_id = stream_handle; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_DEV_SEND_QUEUES), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_STOP" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_STOP\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_flush() - stops a mipi virtual stream but + * completes processing cmd backlog + */ +int ia_css_isys_stream_flush( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_FLUSH" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_FLUSH\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_FLUSH; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_FLUSH" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_FLUSH\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_capture_indication() + * - captures "next frame" on stream_handle + */ +int ia_css_isys_stream_capture_indication( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address next_frame_fw = 0; + ia_css_shared_buffer buf_next_frame_id = (ia_css_shared_buffer)NULL; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_CAPTURE_INDICATION" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_STREAM_CAPTURE_INDICATION\n"); + + verifret(ctx, EFAULT); + + /* Printing frame configuration and device handle context information + *if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_isys_frame_buff_set(next_frame, + ctx->stream_nof_output_pins[stream_handle]); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + verifret(next_frame != NULL, EFAULT); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + { + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_CAPTURE; + retval = ia_css_isys_constr_fw_next_frame(ctx, stream_handle, + &next_frame_fw, &buf_next_frame_id, next_frame); + verifret(retval == 0, retval); + token.payload = next_frame_fw; + token.buf_handle = HOST_ADDRESS(buf_next_frame_id); + } + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CAPTURE_INDICATION" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_STREAM_CAPTURE_INDICATION\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_handle_response() - handle ISYS responses + */ +int ia_css_isys_stream_handle_response( + HANDLE context, + struct ia_css_isys_resp_info *received_response) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct resp_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_HANDLE_RESPONSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_STREAM_HANDLE_RESPONSE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(received_response != NULL, EFAULT); + + packets = ia_css_syscom_recv_port_available( + ctx->sys, BASE_MSG_RECV_QUEUES); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + retval = ia_css_syscom_recv_port_transfer( + ctx->sys, BASE_MSG_RECV_QUEUES, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + retval = ia_css_isys_extract_fw_response( + ctx, &token, received_response); + verifret(retval == 0, retval); + + /* Printing received response information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_isys_resp_info(received_response); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + verifret(received_response->type < N_IA_CSS_ISYS_RESP_TYPE, EINVAL); + verifret(received_response->stream_handle < STREAM_ID_MAX, EINVAL); + + if (received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY || + received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK || + received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED) { + verifret(received_response->pin.addr != 0, EFAULT); + verifret(received_response->pin.out_buf_id != 0, EFAULT); + verifret(received_response->pin_id < + ctx->stream_nof_output_pins[received_response->stream_handle], + EINVAL); + } + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_HANDLE_RESPONSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_STREAM_HANDLE_RESPONSE\n"); + + return 0; +} + + +/** + * ia_css_isys_device_close() - close ISYS device + */ +static int isys_context_destroy(HANDLE context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int stream_handle; + unsigned int queue_id; + unsigned int nof_recv_queues; + int retval = 0; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY isys_context_destroy\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + nof_recv_queues = ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + /* Close the ports for all the recv queues (MSG and PROXY) */ + for (queue_id = 0; queue_id < nof_recv_queues; queue_id++) { + retval = ia_css_syscom_recv_port_close( + ctx->sys, queue_id); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + /* Close the ports for PROXY send queue(s) */ + for (queue_id = 0; + queue_id < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + queue_id++) { + retval = ia_css_syscom_send_port_close( + ctx->sys, queue_id); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + for (stream_handle = 0; stream_handle < STREAM_ID_MAX; + stream_handle++) { + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_IDLE, EPERM); + } + + retval = ia_css_syscom_close(ctx->sys); + verifret(retval == 0, EBUSY); + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_CONFIGURED; +#endif /* VERIFY_DEVSTATE */ + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE isys_context_destroy\n"); + + return 0; +} +/** + * ia_css_isys_device_close() - close ISYS device + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_isys_context_destroy(HANDLE context) +{ + return isys_context_destroy(context); +} + +void ia_css_isys_device_close(void) +{ + /* Created for legacy, nothing to perform here */ +} + +#else +int ia_css_isys_device_close(HANDLE context) +{ + return isys_context_destroy(context); +} +#endif + +/** + * ia_css_isys_device_release() - release ISYS device + */ +int ia_css_isys_device_release(HANDLE context, unsigned int force) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_RELEASE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_DEVICE_RELEASE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_CONFIGURED, EPERM); +#endif /* VERIFY_DEVSTATE */ + + retval = ia_css_syscom_release(ctx->sys, force); + verifret(retval == 0, EBUSY); + + /* If ia_css_isys_device_release called with force==1, this should + * happen after timeout, so no active transfers + * If ia_css_isys_device_release called with force==0, this should + * happen after SP has gone idle, so no active transfers + */ + ia_css_isys_force_unmap_comm_buff_queue(ctx); + ia_css_isys_destr_comm_buff_queue(ctx); + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_IDLE; +#endif /* VERIFY_DEVSTATE */ + + ia_css_cpu_mem_free(ctx); + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_RELEASE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_DEVICE_RELEASE\n"); + + return 0; +} + +/** + * ia_css_isys_proxy_write_req() - send ISYS proxy write requests + */ +int ia_css_isys_proxy_write_req( + HANDLE context, + const struct ia_css_proxy_write_req_val *write_req_val) +{ + + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + struct proxy_send_queue_token token; + int packets; + int retval = 0; + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_PROXY_WRITE_REQ\n"); + verifret(ctx, EFAULT); + verifret(write_req_val != NULL, EFAULT); + + packets = ia_css_syscom_send_port_available(ctx->sys, 0); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + token.request_id = write_req_val->request_id; + token.region_index = write_req_val->region_index; + token.offset = write_req_val->offset; + token.value = write_req_val->value; + + retval = ia_css_syscom_send_port_transfer(ctx->sys, 0, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_PROXY_WRITE_REQ\n"); + + return 0; +} + +/** + * ia_css_isys_proxy_handle_write_response() - handle ISYS proxy responses + */ +int ia_css_isys_proxy_handle_write_response( + HANDLE context, + struct ia_css_proxy_write_req_resp *received_response) +{ + + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + struct proxy_resp_queue_token token; + int retval = 0; + int packets; + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_PROXY_HANDLE_WRITE_RESPONSE\n"); + verifret(ctx, EFAULT); + verifret(received_response != NULL, EFAULT); + + packets = ia_css_syscom_recv_port_available(ctx->sys, 0); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + retval = ia_css_syscom_recv_port_transfer(ctx->sys, 0, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + + retval = ia_css_isys_extract_proxy_response(&token, received_response); + verifret(retval == 0, retval); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_PROXY_HANDLE_WRITE_RESPONSE\n"); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.c new file mode 100644 index 000000000000..d6500a0cb605 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.c @@ -0,0 +1,379 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_isysapi_trace.h" +#include "ia_css_isys_public_trace.h" +#include "ia_css_isysapi_types.h" +#include "ia_css_isysapi.h" +#include "ia_css_isys_private.h" +#include "error_support.h" +#include "ia_css_syscom.h" + +/** + * print_handle_context - formatted print function for + * struct ia_css_isys_context *ctx variable + */ +int print_handle_context(struct ia_css_isys_context *ctx) +{ + unsigned int i; + + verifret(ctx != NULL, EFAULT); + /* Print ctx->(ssid, mmid, dev_state) */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "Print ia_css_isys_context *ctx\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_3(ISYSAPI, VERBOSE, + "\tia_css_isys_context->ssid = %d\n" + "\t\t\tia_css_isys_context->mmid = %d\n" + "\t\t\tia_css_isys_context->device_state = %d\n" + , ctx->ssid + , ctx->mmid + , ctx->dev_state); + /* Print ctx->(stream_state_array, stream_nof_output_pins) */ + for (i = 0; i < STREAM_ID_MAX; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_context->stream_state[i = %d] = %d\n" + "\t\t\tia_css_isys_context->stream_nof_output_pins[i = %d] = %d\n" + , i + , ctx->stream_state_array[i] + , i + , ctx->stream_nof_output_pins[i]); + } + /* Print ctx->ia_css_syscom_context */ + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, + "\tia_css_isys_context->ia_css_syscom_context = %p\n" + , (struct ia_css_syscom_context *)(ctx->sys)); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_device_config_data - formatted print function for + * struct ia_css_isys_device_cfg_data *config variable + */ +int print_device_config_data(const struct ia_css_isys_device_cfg_data *config) +{ + verifret(config != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, + VERBOSE, + "Print ia_css_isys_device_cfg_data *config\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_7(ISYSAPI, + VERBOSE, + "\tia_css_isys_device_cfg_data->driver_sys.ssid = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.mmid = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.num_send_queues = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.num_recv_queues = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.send_queue_size = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.recv_queue_size = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_proxy.proxy_write_queue_size = %d\n", + config->driver_sys.ssid, + config->driver_sys.mmid, + config->driver_sys.num_send_queues, + config->driver_sys.num_recv_queues, + config->driver_sys.send_queue_size, + config->driver_sys.recv_queue_size, + config->driver_proxy.proxy_write_queue_size); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_stream_config_data - formatted print function for + * ia_css_isys_stream_cfg_data stream_cfg variable + */ +int print_stream_config_data( + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + unsigned int i; + + verifret(stream_cfg != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "Print ia_css_isys_stream_cfg_data stream_cfg\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_5(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isl_use = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_stream_source = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_mipi_vc = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->nof_input_pins = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->nof_output_pins = %d\n" + , stream_cfg->isl_use + , stream_cfg->src + , stream_cfg->vc + , stream_cfg->nof_input_pins + , stream_cfg->nof_output_pins); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->send_irq_sof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_irq_eof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_resp_sof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_resp_eof_discarded = %d\n" + , stream_cfg->send_irq_sof_discarded + , stream_cfg->send_irq_eof_discarded + , stream_cfg->send_resp_sof_discarded + , stream_cfg->send_resp_eof_discarded); + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_mipi_data_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_resolution.width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_resolution.height = %d\n" + , i + , stream_cfg->input_pins[i].dt + , i + , stream_cfg->input_pins[i].input_res.width + , i + , stream_cfg->input_pins[i].input_res.height); + IA_CSS_TRACE_2(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_mipi_store_mode = %d\n" + , i + , stream_cfg->input_pins[i].mipi_store_mode); + } + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].top_offset = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].left_offset = %d\n" + , i + , stream_cfg->crop[i].top_offset + , i + , stream_cfg->crop[i].left_offset); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].bottom_offset = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].right_offset = %d\n" + , i + , stream_cfg->crop[i].bottom_offset + , i + , stream_cfg->crop[i].right_offset); + } + for (i = 0; i < stream_cfg->nof_output_pins; i++) { + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_pin_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_frame_format_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].input_pin_id = %d\n" + , i + , stream_cfg->output_pins[i].pt + , i + , stream_cfg->output_pins[i].ft + , i + , stream_cfg->output_pins[i].input_pin_id); + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].watermark_in_lines = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].send_irq = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].stride = %d\n" + , i + , stream_cfg->output_pins[i].watermark_in_lines + , i + , stream_cfg->output_pins[i].send_irq + , i + , stream_cfg->output_pins[i].stride); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_resolution.width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_resolution.height = %d\n" + , i + , stream_cfg->output_pins[i].output_res.width + , i + , stream_cfg->output_pins[i].output_res.height); + } + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ia_css_isys_resolution[i = %d].width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ia_css_isys_resolution[i = %d].height = %d\n" + , i + , stream_cfg->isa_cfg.isa_res[i].width + , i + , stream_cfg->isa_cfg.isa_res[i].height); + } + IA_CSS_TRACE_7(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.blc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.lsc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.dpc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.downscaler_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.awb_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.af_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ae_enabled = %d\n" + , stream_cfg->isa_cfg.blc_enabled + , stream_cfg->isa_cfg.lsc_enabled + , stream_cfg->isa_cfg.dpc_enabled + , stream_cfg->isa_cfg.downscaler_enabled + , stream_cfg->isa_cfg.awb_enabled + , stream_cfg->isa_cfg.af_enabled + , stream_cfg->isa_cfg.ae_enabled); + + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.paf_type = %d\n" + , stream_cfg->isa_cfg.paf_type); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_isys_frame_buff_set - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_frame_buff_set( + const struct ia_css_isys_frame_buff_set *next_frame, + const unsigned int nof_output_pins) +{ + unsigned int i; + + verifret(next_frame != NULL, EFAULT); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "Print ia_css_isys_frame_buff_set *next_frame\n" + "-------------------------------------------------------\n"); + for (i = 0; i < nof_output_pins; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->ia_css_isys_output_pin_payload[i = %d].ia_css_return_token = %016lxu\n" + "\t\t\tia_css_isys_frame_buff_set->ia_css_isys_output_pin_payload[i = %d].ia_css_input_buffer_css_address = %08xu\n" + , i + , (unsigned long int) + next_frame->output_pins[i].out_buf_id + , i + , next_frame->output_pins[i].addr); + } + IA_CSS_TRACE_2(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->process_group_light.ia_css_return_token = %016lxu\n" + "\t\t\tia_css_isys_frame_buff_set->process_group_light.ia_css_input_buffer_css_address = %08xu\n" + , (unsigned long int) + next_frame->process_group_light.param_buf_id + , next_frame->process_group_light.addr); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->send_irq_sof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_irq_eof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_resp_sof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_resp_eof = %d\n" + , (int) next_frame->send_irq_sof + , (int) next_frame->send_irq_eof + , (int) next_frame->send_resp_sof + , (int) next_frame->send_resp_eof); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_isys_resp_info - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_resp_info(struct ia_css_isys_resp_info *received_response) +{ + verifret(received_response != NULL, EFAULT); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ISYS_RESPONSE_INFO\n" + "-------------------------------------------------------\n"); + switch (received_response->type) { + case IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_SOF: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_SOF\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_EOF: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_EOF\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED\n"); + break; + default: + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = INVALID\n"); + break; + } + + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.type = %d\n" + "\t\t\tia_css_isys_resp_info.stream_handle = %d\n" + "\t\t\tia_css_isys_resp_info.time_stamp[0] = %d\n" + "\t\t\tia_css_isys_resp_info.time_stamp[1] = %d\n", + received_response->type, + received_response->stream_handle, + received_response->timestamp[0], + received_response->timestamp[1]); + IA_CSS_TRACE_7(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.error = %d\n" + "\t\t\tia_css_isys_resp_info.error_details = %d\n" + "\t\t\tia_css_isys_resp_info.pin.out_buf_id = %016llxu\n" + "\t\t\tia_css_isys_resp_info.pin.addr = %016llxu\n" + "\t\t\tia_css_isys_resp_info.pin_id = %d\n" + "\t\t\tia_css_isys_resp_info.frame_counter = %d\n," + "\t\t\tia_css_isys_resp_info.written_direct = %d\n", + received_response->error, + received_response->error_details, + (unsigned long long)received_response->pin.out_buf_id, + (unsigned long long)received_response->pin.addr, + received_response->pin_id, + received_response->frame_counter, + received_response->written_direct); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "------------------------------------------------------\n"); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.h new file mode 100644 index 000000000000..5b6508058fd6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isys_public_trace.h @@ -0,0 +1,55 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_PUBLIC_TRACE_H +#define __IA_CSS_ISYS_PUBLIC_TRACE_H + +#include "ia_css_isysapi_trace.h" + +#include "ia_css_isysapi_types.h" + +#include "ia_css_isysapi.h" + +#include "ia_css_isys_private.h" +/** + * print_handle_context - formatted print function for + * struct ia_css_isys_context *ctx variable + */ +int print_handle_context(struct ia_css_isys_context *ctx); + +/** + * print_device_config_data - formatted print function for + * struct ia_css_isys_device_cfg_data *config variable + */ +int print_device_config_data(const struct ia_css_isys_device_cfg_data *config); +/** + * print_stream_config_data - formatted print function for + * ia_css_isys_stream_cfg_data stream_cfg variable + */ +int print_stream_config_data( + const struct ia_css_isys_stream_cfg_data *stream_cfg); +/** + * print_isys_frame_buff_set - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_frame_buff_set( + const struct ia_css_isys_frame_buff_set *next_frame, + const unsigned int nof_output_pins); +/** + * print_isys_isys_resp_info - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_resp_info(struct ia_css_isys_resp_info *received_response); + +#endif /* __IA_CSS_ISYS_PUBLIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isysapi_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isysapi_trace.h new file mode 100644 index 000000000000..c6b944f245b1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/isysapi/src/ia_css_isysapi_trace.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_TRACE_H +#define __IA_CSS_ISYSAPI_TRACE_H + +#include "ia_css_trace.h" + +#define ISYSAPI_TRACE_LOG_LEVEL_OFF 0 +#define ISYSAPI_TRACE_LOG_LEVEL_NORMAL 1 +#define ISYSAPI_TRACE_LOG_LEVEL_DEBUG 2 + +/* ISYSAPI and all the submodules in ISYSAPI will have + * the default tracing level set to this level + */ +#define ISYSAPI_TRACE_CONFIG_DEFAULT ISYSAPI_TRACE_LOG_LEVEL_NORMAL + +/* In case ISYSAPI_TRACE_CONFIG is not defined, set it to default level */ +#if !defined(ISYSAPI_TRACE_CONFIG) + #define ISYSAPI_TRACE_CONFIG ISYSAPI_TRACE_CONFIG_DEFAULT +#endif + +/* ISYSAPI Module tracing backend is mapped to + * TUNIT tracing for target platforms + */ +#ifdef IA_CSS_TRACE_PLATFORM_CELL + #ifndef HRT_CSIM + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE + #else + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #endif +#else + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(ISYSAPI_TRACE_CONFIG)) + /* TRACE_OFF */ + #if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_OFF + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + /* TRACE_NORMAL */ + #elif ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_NORMAL + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + /* TRACE_DEBUG */ + #elif ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No ISYSAPI_TRACE_CONFIG Tracing level defined" + #endif +#else + #error "ISYSAPI_TRACE_CONFIG not defined" +#endif + +#endif /* __IA_CSS_ISYSAPI_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h new file mode 100644 index 000000000000..a284d74bb4a6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h @@ -0,0 +1,99 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_H +#define __IA_CSS_PKG_DIR_H + +#include "ia_css_pkg_dir_storage_class.h" +#include "ia_css_pkg_dir_types.h" +#include "type_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +/* User is expected to call the verify function manually, + * other functions do not call it internally + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +int ia_css_pkg_dir_verify_header( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +enum ia_css_pkg_dir_version ia_css_pkg_dir_get_version( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_set_version( + ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version +); + + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_size( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_entry_get_version( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint8_t ia_css_pkg_dir_entry_get_type( + const ia_css_pkg_dir_entry_t *entry +); + +/* Get the address of the specified entry in the PKG_DIR + * Note: This function expects the complete PKG_DIR in the same memory space + * and the entries contains offsets and not addresses. + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +void *ia_css_pkg_dir_get_entry_address( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "ia_css_pkg_dir_impl.h" + +#endif + +#endif /* __IA_CSS_PKG_DIR_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h new file mode 100644 index 000000000000..ad194b0389eb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h @@ -0,0 +1,46 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IUNIT_H +#define __IA_CSS_PKG_DIR_IUNIT_H + +/* In bootflow, pkg_dir only supports upto 16 entries in pkg_dir + * pkg_dir_header + Psys_server pg + Isys_server pg + 13 Client pg + */ + +enum { + IA_CSS_PKG_DIR_SIZE = 16, + IA_CSS_PKG_DIR_ENTRIES = IA_CSS_PKG_DIR_SIZE - 1 +}; + +#define IUNIT_MAX_CLIENT_PKG_ENTRIES 13 + +/* Example assignment of unique identifiers for the FW components + * This should match the identifiers in the manifest + */ +enum ia_css_pkg_dir_entry_type { + IA_CSS_PKG_DIR_HEADER = 0, + IA_CSS_PKG_DIR_PSYS_SERVER_PG, + IA_CSS_PKG_DIR_ISYS_SERVER_PG, + IA_CSS_PKG_DIR_CLIENT_PG +}; + +/* Fixed entries in the package directory */ +enum ia_css_pkg_dir_index { + IA_CSS_PKG_DIR_PSYS_INDEX = 0, + IA_CSS_PKG_DIR_ISYS_INDEX = 1, + IA_CSS_PKG_DIR_CLIENT_0 = 2 +}; + +#endif /* __IA_CSS_PKG_DIR_IUNIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h new file mode 100644 index 000000000000..cb64172151f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_STORAGE_CLASS_H +#define __IA_CSS_PKG_DIR_STORAGE_CLASS_H + + +#include "storage_class.h" + +#ifndef __IA_CSS_PKG_DIR_INLINE__ +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C +#else +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PKG_DIR_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h new file mode 100644 index 000000000000..b024b3da2f9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_TYPES_H +#define __IA_CSS_PKG_DIR_TYPES_H + +#include "type_support.h" + +struct ia_css_pkg_dir_entry { + uint32_t address[2]; + uint32_t size; + uint16_t version; + uint8_t type; + uint8_t unused; +}; + +typedef void ia_css_pkg_dir_t; +typedef struct ia_css_pkg_dir_entry ia_css_pkg_dir_entry_t; + +/* The version field of the pkg_dir header defines + * if entries contain offsets or pointers + */ +/* This is temporary, until all pkg_dirs use pointers */ +enum ia_css_pkg_dir_version { + IA_CSS_PKG_DIR_POINTER, + IA_CSS_PKG_DIR_OFFSET +}; + + +#endif /* __IA_CSS_PKG_DIR_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/pkg_dir.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/pkg_dir.mk new file mode 100644 index 000000000000..32c8a68f3653 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/pkg_dir.mk @@ -0,0 +1,29 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PKG DIR + +PKG_DIR_DIR = $${MODULES_DIR}/pkg_dir +PKG_DIR_INTERFACE = $(PKG_DIR_DIR)/interface +PKG_DIR_SOURCES = $(PKG_DIR_DIR)/src + +PKG_DIR_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir.c +PKG_DIR_CPPFLAGS = -I$(PKG_DIR_INTERFACE) +PKG_DIR_CPPFLAGS += -I$(PKG_DIR_SOURCES) +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/../isp/kernels/io_ls/common +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu/$(FW_ABI_IPU_TYPES_VERSION) + +PKG_DIR_CREATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_create.c +PKG_DIR_UPDATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_update.c diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c new file mode 100644 index 000000000000..348b56833e06 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "storage_class.h" + +STORAGE_CLASS_INLINE int __ia_css_pkg_dir_avoid_warning_on_empty_file(void) +{ + return 0; +} + +#else +#include "ia_css_pkg_dir_impl.h" + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h new file mode 100644 index 000000000000..d5067d21398f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h @@ -0,0 +1,201 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IMPL_H +#define __IA_CSS_PKG_DIR_IMPL_H + +#include "ia_css_pkg_dir.h" +#include "ia_css_pkg_dir_int.h" +#include "error_support.h" +#include "type_support.h" +#include "assert_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + DECLARE_ERRVAL + struct ia_css_pkg_dir_entry *pkg_dir_header = NULL; + + verifexitval(pkg_dir != NULL, EFAULT); + + pkg_dir_header = (struct ia_css_pkg_dir_entry *)pkg_dir; + + /* First entry of the structure is the header, skip that */ + index++; + verifexitval(index < pkg_dir_header->size, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return NULL; + } + return &(pkg_dir_header[index]); +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +int ia_css_pkg_dir_verify_header(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + verifexitval(pkg_dir_header != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return -1; + } + return ((pkg_dir_header->address[0] == PKG_DIR_MAGIC_VAL_0) + && (pkg_dir_header->address[1] == PKG_DIR_MAGIC_VAL_1)) ? + 0 : -1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + uint32_t size = 0; + + verifexitval(pkg_dir_header != NULL, EFAULT); + size = pkg_dir_header->size; + verifexitval(size > 0, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return size - 1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +enum ia_css_pkg_dir_version +ia_css_pkg_dir_get_version(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + assert(pkg_dir_header != NULL); + return pkg_dir_header->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_set_version(ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 1; + } + pkg_dir_header->version = version; + return 0; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return sizeof(struct ia_css_pkg_dir_entry) * pkg_dir_header->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[0]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[1]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_size(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_entry_get_version(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint8_t ia_css_pkg_dir_entry_get_type(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->type; +} + + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +void *ia_css_pkg_dir_get_entry_address(const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + void *entry_blob = NULL; + const ia_css_pkg_dir_entry_t *pkg_dir_entry = + ia_css_pkg_dir_get_entry(pkg_dir, index-1); + + if ((pkg_dir_entry != NULL) && + (ia_css_pkg_dir_entry_get_size(pkg_dir_entry) > 0)) { + assert(ia_css_pkg_dir_entry_get_address_hi(pkg_dir_entry) == 0); + entry_blob = (void *)((char *)pkg_dir + + ia_css_pkg_dir_entry_get_address_lo(pkg_dir_entry)); + } + return entry_blob; +} + +#endif /* __IA_CSS_PKG_DIR_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h new file mode 100644 index 000000000000..203505fbee54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_INT_H +#define __IA_CSS_PKG_DIR_INT_H + +/* + * Package Dir structure as specified in CSE FAS + * + * PKG DIR Header + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * 0 "_IUPKDR_" + * 1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version of the Structure + * Size: Size of the entire table (including header) in 16 byte chunks + * Type: Must be 0 for header + * + * Figure 13: PKG DIR Header + * + * + * PKG DIR Entry + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * N Address/Offset + * N+1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version # of the Component + * Size: Size of the component in bytes + * Type: Component Identifier + */ + +#define PKG_DIR_SIZE_BITS 24 +#define PKG_DIR_TYPE_BITS 7 + +#define PKG_DIR_MAGIC_VAL_1 (('_' << 24) | ('I' << 16) | ('U' << 8) | 'P') +#define PKG_DIR_MAGIC_VAL_0 (('K' << 24) | ('D' << 16) | ('R' << 8) | '_') + +#endif /* __IA_CSS_PKG_DIR_INT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/port_env_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/port_env_struct.h new file mode 100644 index 000000000000..4d39a4739a8b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/port_env_struct.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PORT_ENV_STRUCT_H +#define __PORT_ENV_STRUCT_H + +struct port_env { + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __PORT_ENV_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue.h new file mode 100644 index 000000000000..b233ab3baf01 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue.h @@ -0,0 +1,40 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_H +#define __QUEUE_H + +#include "queue_struct.h" +#include "port_env_struct.h" + +/* + * SYS queues are created by the host + * SYS queues cannot be accessed through the queue interface + * To send data into a queue a send_port must be opened. + * To receive data from a queue, a recv_port must be opened. + */ + +/* return required buffer size for queue */ +unsigned int +sys_queue_buf_size(unsigned int size, unsigned int token_size); + +/* + * initialize a queue that can hold at least 'size' tokens of + * 'token_size' bytes. + */ +void +sys_queue_init(struct sys_queue *q, unsigned int size, + unsigned int token_size, struct sys_queue_res *res); + +#endif /* __QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue_struct.h new file mode 100644 index 000000000000..ef48fcfded2b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/queue_struct.h @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_STRUCT_H +#define __QUEUE_STRUCT_H + +/* queue description, shared between sender and receiver */ + +#include "type_support.h" + +#ifdef __VIED_CELL +typedef struct {uint32_t v[2]; } host_buffer_address_t; +#else +typedef uint64_t host_buffer_address_t; +#endif + +typedef uint32_t vied_buffer_address_t; + + +struct sys_queue { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* reg no in subsystem's regmem */ + unsigned int rd_reg; + unsigned int _align; +}; + +struct sys_queue_res { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int reg; +}; + +#endif /* __QUEUE_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port.h new file mode 100644 index 000000000000..cce253b26668 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port.h @@ -0,0 +1,34 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_H +#define __RECV_PORT_H + + +struct recv_port; +struct sys_queue; +struct port_env; + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env); + +unsigned int +recv_port_available(const struct recv_port *p); + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data); + + +#endif /* __RECV_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port_struct.h new file mode 100644 index 000000000000..52ec563b13cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/recv_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_STRUCT_H +#define __RECV_PORT_STRUCT_H + +#include "buffer_type.h" + +struct recv_port { + buffer_address buffer; /* address of buffer in DDR */ + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer located in regmem */ + unsigned int rd_reg; /* index read pointer located in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; /* address of memory containing regmem */ +}; + +#endif /* __RECV_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port.h new file mode 100644 index 000000000000..04a160f3f019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port.h @@ -0,0 +1,52 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_H +#define __SEND_PORT_H + + +/* + * A send port can be used to send tokens into a queue. + * The interface can be used on any type of processor (host, SP, ...) + */ + +struct send_port; +struct sys_queue; +struct port_env; + +/* + * Open a send port on a queue. After the port is opened, tokens can be sent + */ +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env); + +/* + * Determine how many tokens can be sent + */ +unsigned int +send_port_available(const struct send_port *p); + +/* + * Send a token via a send port. The function returns the number of + * tokens that have been sent: + * 1: the token was accepted + * 0: the token was not accepted (full queue) + * The size of a token is determined at initialization. + */ +unsigned int +send_port_transfer(const struct send_port *p, const void *data); + + +#endif /* __SEND_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port_struct.h new file mode 100644 index 000000000000..f834c62bc3db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/interface/send_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_STRUCT_H +#define __SEND_PORT_STRUCT_H + +#include "buffer_type.h" + +struct send_port { + buffer_address buffer; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer in regmem */ + unsigned int rd_reg; /* index of read pointer in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __SEND_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/port.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/port.mk new file mode 100644 index 000000000000..b3801247802e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/port.mk @@ -0,0 +1,31 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PORT + +PORT_DIR=$${MODULES_DIR}/port + +PORT_INTERFACE=$(PORT_DIR)/interface +PORT_SOURCES1=$(PORT_DIR)/src + +PORT_HOST_FILES += $(PORT_SOURCES1)/send_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/recv_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/queue.c + +PORT_HOST_CPPFLAGS += -I$(PORT_INTERFACE) + +PORT_FW_FILES += $(PORT_SOURCES1)/send_port.c +PORT_FW_FILES += $(PORT_SOURCES1)/recv_port.c + +PORT_FW_CPPFLAGS += -I$(PORT_INTERFACE) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/queue.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/queue.c new file mode 100644 index 000000000000..eeec99dfe2d0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/queue.c @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "queue.h" + +#include "regmem_access.h" +#include "port_env_struct.h" + +unsigned int sys_queue_buf_size(unsigned int size, unsigned int token_size) +{ + return (size + 1) * token_size; +} + +void +sys_queue_init(struct sys_queue *q, unsigned int size, unsigned int token_size, + struct sys_queue_res *res) +{ + unsigned int buf_size; + + q->size = size + 1; + q->token_size = token_size; + buf_size = sys_queue_buf_size(size, token_size); + + /* acquire the shared buffer space */ + q->host_address = res->host_address; + res->host_address += buf_size; + q->vied_address = res->vied_address; + res->vied_address += buf_size; + + /* acquire the shared read and writer pointers */ + q->wr_reg = res->reg; + res->reg++; + q->rd_reg = res->reg; + res->reg++; + +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/recv_port.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/recv_port.c new file mode 100644 index 000000000000..31b36e9ceafb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/recv_port.c @@ -0,0 +1,95 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "recv_port.h" +#include "port_env_struct.h" /* for port_env */ +#include "queue_struct.h" /* for sys_queue */ +#include "recv_port_struct.h" /* for recv_port */ +#include "buffer_access.h" /* for buffer_load, buffer_address */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_invalidate */ +#endif + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; + +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +recv_port_index(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(rd, i, p->size); +} + +unsigned int +recv_port_available(const struct recv_port *p) +{ + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(wr, -rd, p->size); +} + +STORAGE_CLASS_INLINE void +recv_port_copy(const struct recv_port *p, unsigned int i, void *data) +{ + unsigned int rd = recv_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (rd * token_size); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_invalidate((void *)HOST_ADDRESS(p->buffer), + token_size*p->size); +#endif + buffer_load(addr, data, token_size, p->mmid); +} + +STORAGE_CLASS_INLINE void +recv_port_release(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = recv_port_index(p, i); + + regmem_store_32(p->mem_addr, p->rd_reg, rd, p->ssid); +} + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data) +{ + if (!recv_port_available(p)) + return 0; + recv_port_copy(p, 0, data); + recv_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/send_port.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/send_port.c new file mode 100644 index 000000000000..8d1fba08c5d5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/port/src/send_port.c @@ -0,0 +1,94 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "send_port.h" +#include "queue_struct.h" /* for sys_queue */ +#include "send_port_struct.h" /* for send_port */ +#include "port_env_struct.h" /* for port_env */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "buffer_access.h" /* for buffer_store, buffer_address */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_flush */ +#endif + +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +send_port_index(const struct send_port *p, unsigned int i) +{ + unsigned int wr = regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(wr, i, p->size); +} + +unsigned int +send_port_available(const struct send_port *p) +{ + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(rd, -(wr+1), p->size); +} + +STORAGE_CLASS_INLINE void +send_port_copy(const struct send_port *p, unsigned int i, const void *data) +{ + unsigned int wr = send_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (wr * token_size); + + buffer_store(addr, data, token_size, p->mmid); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(addr), token_size); +#endif +} + +STORAGE_CLASS_INLINE void +send_port_release(const struct send_port *p, unsigned int i) +{ + unsigned int wr = send_port_index(p, i); + + regmem_store_32(p->mem_addr, p->wr_reg, wr, p->ssid); +} + +unsigned int +send_port_transfer(const struct send_port *p, const void *data) +{ + if (!send_port_available(p)) + return 0; + send_port_copy(p, 0, data); + send_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.c new file mode 100644 index 000000000000..c51d65c8cb64 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.c @@ -0,0 +1,15 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#include "ia_css_debug_dump.h" + void ia_css_debug_dump(void) {} \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.h new file mode 100644 index 000000000000..5dd23ddbd180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/isys/bxtB0_gen_reg_dump/ia_css_debug_dump.h @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#ifndef __IA_CSS_DEBUG_DUMP_H_ + #define __IA_CSS_DEBUG_DUMP_H_ + void ia_css_debug_dump(void); + #endif /* __IA_CSS_DEBUG_DUMP_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c new file mode 100644 index 000000000000..9b9161ae78cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include "ia_css_trace.h" +#ifdef USE_LOGICAL_SSIDS +/* + Logical names can be used to define the SSID + In order to resolve these names the following include file should be provided + and the define above should be enabled +*/ +#include +#endif + +#define REG_DUMP_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#define REG_DUMP_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + +/* SSID value is defined in test makefiles as either isys0 or psys0 */ +#define REG_DUMP_READ_REGISTER(addr) vied_subsystem_load_32(SSID, addr) + +#define REG_DUMP_PRINT_0(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_0(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define REG_DUMP_PRINT_1(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_1(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define EXPAND_VA_ARGS(x) x + +/* Including generated source code for reg_dump */ +#include "ia_css_debug_dump.c" diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/interface/regmem_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/interface/regmem_access.h new file mode 100644 index 000000000000..d4576af936f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/interface/regmem_access.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_H +#define __REGMEM_ACCESS_H + +#include "storage_class.h" + +enum regmem_id { + /* pass pkg_dir address to SPC in non-secure mode */ + PKG_DIR_ADDR_REG = 0, + /* pass syscom configuration to SPC */ + SYSCOM_CONFIG_REG = 1, + /* syscom state - modified by SP */ + SYSCOM_STATE_REG = 2, + /* syscom commands - modified by the host */ + SYSCOM_COMMAND_REG = 3, + /* Store interrupt status - updated by SP */ + SYSCOM_IRQ_REG = 4, + /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/ + SYSCOM_VTL0_ADDR_MASK = 5, +#if HAS_DUAL_CMD_CTX_SUPPORT + /* Initialized if trustlet exists - updated by host */ + TRUSTLET_STATUS = 6, + /* identify if SPC access blocker programming is completed - updated by SP */ + AB_SPC_STATUS = 7, + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 8 +#else + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 6 +#endif +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* Bit 0: for untrusted non-secure DRV driver on VTL0 + * Bit 1: for trusted secure TEE driver on VTL1 + */ +#define SYSCOM_IRQ_VTL0_MASK 0x1 +#define SYSCOM_IRQ_VTL1_MASK 0x2 +#endif + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_address, unsigned int reg, unsigned int ssid); + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_address, unsigned int reg, unsigned int value, + unsigned int ssid); + +#ifdef __VIED_CELL +#include "regmem_access_cell.h" +#else +#include "regmem_access_host.h" +#endif + +#endif /* __REGMEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/regmem.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/regmem.mk new file mode 100644 index 000000000000..24ebc1c325d8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/regmem.mk @@ -0,0 +1,32 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef REGMEM_MK +REGMEM_MK=1 + +# MODULE is REGMEM + +REGMEM_DIR=$${MODULES_DIR}/regmem + +REGMEM_INTERFACE=$(REGMEM_DIR)/interface +REGMEM_SOURCES=$(REGMEM_DIR)/src + +REGMEM_HOST_FILES = +REGMEM_FW_FILES = $(REGMEM_SOURCES)/regmem.c + +REGMEM_CPPFLAGS = -I$(REGMEM_INTERFACE) -I$(REGMEM_SOURCES) +REGMEM_HOST_CPPFLAGS = $(REGMEM_CPPFLAGS) +REGMEM_FW_CPPFLAGS = $(REGMEM_CPPFLAGS) + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_access_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_access_host.h new file mode 100644 index 000000000000..8878d7074fab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_access_host.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_HOST_H +#define __REGMEM_ACCESS_HOST_H + +#include "regmem_access.h" /* implemented interface */ + +#include "storage_class.h" +#include "regmem_const.h" +#include +#include "ia_css_cmem.h" + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_addr, unsigned int reg, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + return ia_css_cmem_load_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg)); +} + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_addr, unsigned int reg, + unsigned int value, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + ia_css_cmem_store_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg), + value); +} + +#endif /* __REGMEM_ACCESS_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_const.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_const.h new file mode 100644 index 000000000000..ac7e3a98a434 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/regmem/src/regmem_const.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_CONST_H +#define __REGMEM_CONST_H + +#ifndef REGMEM_SIZE +#define REGMEM_SIZE (16) +#endif /* REGMEM_SIZE */ +#ifndef REGMEM_OFFSET +#define REGMEM_OFFSET (0) +#endif /* REGMEM_OFFSET */ +#ifndef REGMEM_WORD_BYTES +#define REGMEM_WORD_BYTES (4) +#endif + +#endif /* __REGMEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/assert_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/assert_support.h new file mode 100644 index 000000000000..f904a494b53c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/assert_support.h @@ -0,0 +1,197 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ASSERT_SUPPORT_H +#define __ASSERT_SUPPORT_H + +/* This file provides support for run-time assertions + * and compile-time assertions. + * + * Run-time asstions are provided via the following syntax: + * assert(condition) + * Run-time assertions are disabled using the NDEBUG flag. + * + * Compile time assertions are provided via the following syntax: + * COMPILATION_ERROR_IF(condition); + * A compile-time assertion will fail to compile if the condition is false. + * The condition must be constant, such that it can be evaluated + * at compile time. + * + * OP___assert is deprecated. + */ + +#define IA_CSS_ASSERT(expr) assert(expr) + +#ifdef __KLOCWORK__ +/* Klocwork does not see that assert will lead to abortion + * as there is no good way to tell this to KW and the code + * should not depend on assert to function (actually the assert + * could be disabled in a release build) it was decided to + * disable the assert for KW scans (by defining NDEBUG) + */ +#define NDEBUG +#endif /* __KLOCWORK__ */ + +/** + * The following macro can help to test the size of a struct at compile + * time rather than at run-time. It does not work for all compilers; see + * below. + * + * Depending on the value of 'condition', the following macro is expanded to: + * - condition==true: + * an expression containing an array declaration with negative size, + * usually resulting in a compilation error + * - condition==false: + * (void) 1; // C statement with no effect + * + * example: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * SIZE_OF_HOST_SP_QUEUES_STRUCT); + * + * verify that the macro indeed triggers a compilation error with your compiler: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * (sizeof(struct host_sp_queues)+1) ); + * + * Not all compilers will trigger an error with this macro; + * use a search engine to search for BUILD_BUG_ON to find other methods. + */ +#define COMPILATION_ERROR_IF(condition) \ +((void)sizeof(char[1 - 2*!!(condition)])) + +/* Compile time assertion */ +#ifndef CT_ASSERT +#define CT_ASSERT(cnd) ((void)sizeof(char[(cnd)?1 : -1])) +#endif /* CT_ASSERT */ + +#ifdef NDEBUG + +#define assert(cnd) ((void)0) + +#else + +#include "storage_class.h" + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE +/* Windows kernel mode compilation */ +#include +#define assert(cnd) ASSERT(cnd) +#else +/* Windows usermode compilation */ +#include +#endif + +#elif defined(__HIVECC) + +/* + * target: assert disabled + * sched: assert enabled only when SCHED_DEBUG is defined + * unsched: assert enabled + */ +#if defined(HRT_HW) +#define assert(cnd) ((void)0) +#elif defined(HRT_SCHED) && !defined(DEBUG_SCHED) +#define assert(cnd) ((void)0) +#elif defined(PIPE_GENERATION) +#define assert(cnd) ((void)0) +#else +#include +#define assert(cnd) OP___csim_assert(cnd) +#endif + +#elif defined(__KERNEL__) +#include + +#ifndef KERNEL_ASSERT_TO_BUG +#ifndef KERNEL_ASSERT_TO_BUG_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#ifndef KERNEL_ASSERT_UNDEFINED +/* Default */ +#define KERNEL_ASSERT_TO_BUG +#endif /*KERNEL_ASSERT_UNDEFINED*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG +/* TODO: it would be cleaner to use this: + * #define assert(cnd) BUG_ON(cnd) + * but that causes many compiler warnings (==errors) under Android + * because it seems that the BUG_ON() macro is not seen as a check by + * gcc like the BUG() macro is. */ +#define assert(cnd) \ + do { \ + if (!(cnd)) { \ + BUG(); \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG_ON +#define assert(cnd) BUG_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON +#define assert(cnd) WARN_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#define assert(cnd) \ + do { \ + int not_cnd = !(cnd); \ + WARN_ON(not_cnd); \ + if (not_cnd) { \ + for (;;) { \ + } \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ + +#ifdef KERNEL_ASSERT_UNDEFINED +#include KERNEL_ASSERT_DEFINITION_FILESTRING +#endif /*KERNEL_ASSERT_UNDEFINED*/ + +#elif defined(__FIST__) || defined(__GNUC__) + +#include "assert.h" + +#else /* default is for unknown environments */ +#define assert(cnd) ((void)0) +#endif + +#endif /* NDEBUG */ + +#ifndef PIPE_GENERATION +/* Deprecated OP___assert, this is still used in ~1000 places + * in the code. This will be removed over time. + * The implementation for the pipe generation tool is in see support.isp.h */ +#define OP___assert(cnd) assert(cnd) + +#ifdef C_RUN +#define compile_time_assert(cond) OP___assert(cond) +#else +#include "storage_class.h" +extern void _compile_time_assert(void); +STORAGE_CLASS_INLINE void compile_time_assert(unsigned cond) +{ + /* Call undefined function if cond is false */ + if (!cond) + _compile_time_assert(); +} +#endif +#endif /* PIPE_GENERATION */ + +#endif /* __ASSERT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/cpu_mem_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/cpu_mem_support.h new file mode 100644 index 000000000000..fa349cac4b24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/cpu_mem_support.h @@ -0,0 +1,233 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __CPU_MEM_SUPPORT_H +#define __CPU_MEM_SUPPORT_H + +#include "storage_class.h" +#include "assert_support.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_copy(void *dst, const void *src, unsigned int size) +{ + /* memcpy cannot be used in in Windows (function is not allowed), + * and the safer function memcpy_s is not available on other platforms. + * Because usage of ia_css_cpu_mem_copy is minimal, we implement it here in an easy, + * but sub-optimal way. + */ + unsigned int i; + + assert(dst != NULL && src != NULL); + + if (!(dst != NULL && src != NULL)) { + return NULL; + } + for (i = 0; i < size; i++) { + ((char *)dst)[i] = ((char *)src)[i]; + } + return dst; +} + +#if defined(__KERNEL__) + +#include +#include +#include +#include + +/* TODO: remove, workaround for issue in hrt file ibuf_ctrl_2600_config.c + * error checking code added to SDK that uses calls to exit function + */ +#define exit(a) return + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + return ia_css_cpu_mem_alloc(size); /* todo: align to page size */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_protect(void *ptr, unsigned int size, int prot) +{ + /* nothing here yet */ +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); /* available in kernel in linux/string.h */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + kfree(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* parameter check here */ + if (ptr == NULL) + return; + + clflush_cache_range(ptr, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* for now same as flush */ + ia_css_cpu_mem_cache_flush(ptr, size); +} + +#elif defined(_MSC_VER) + +#include +#include +#include + +extern void *hrt_malloc(size_t bytes, int zero_mem); +extern void *hrt_free(void *ptr); +extern void hrt_mem_cache_flush(void *ptr, unsigned int size); +extern void hrt_mem_cache_invalidate(void *ptr, unsigned int size); + +#define malloc(a) hrt_malloc(a, 1) +#define free(a) hrt_free(a) + +#define CSS_PAGE_SIZE (1<<12) + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + unsigned int buffer_size = size; + + /* Currently hrt_malloc calls Windows ExAllocatePoolWithTag() routine + * to request system memory. If the number of bytes is equal or bigger + * than the page size, then the returned address is page aligned, + * but if it's smaller it's not necessarily page-aligned We agreed + * with Windows team that we allocate a full page + * if it's less than page size + */ + if (buffer_size < CSS_PAGE_SIZE) + buffer_size = CSS_PAGE_SIZE; + + return ia_css_cpu_mem_alloc(buffer_size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_flush(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_invalidate(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +#else + +#include +#include +#include +/* Needed for the MPROTECT */ +#include +#include +#include +#include + + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + int pagesize; + + pagesize = sysconf(_SC_PAGE_SIZE); + return memalign(pagesize, size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +#endif + +#endif /* __CPU_MEM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/error_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/error_support.h new file mode 100644 index 000000000000..9fe1f65125e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/error_support.h @@ -0,0 +1,110 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ERROR_SUPPORT_H +#define __ERROR_SUPPORT_H + +#if defined(__KERNEL__) +#include +#else +#include +#endif +#include + +/* OS-independent definition of IA_CSS errno values */ +/* #define IA_CSS_EINVAL 1 */ +/* #define IA_CSS_EFAULT 2 */ + +#ifdef __HIVECC +#define ERR_EMBEDDED 1 +#else +#define ERR_EMBEDDED 0 +#endif + +#if ERR_EMBEDDED +#define DECLARE_ERRVAL +#else +#define DECLARE_ERRVAL \ + int _errval = 0; +#endif + +/* Use "owl" in while to prevent compiler warnings in Windows */ +#define ALWAYS_FALSE ((void)0, 0) + +#define verifret(cond, error_type) \ +do { \ + if (!(cond)) { \ + return error_type; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmp(cond, error_tag) \ +do { \ + if (!(cond)) { \ + goto error_tag; \ + } \ +} while (ALWAYS_FALSE) + +#define verifexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#if ERR_EMBEDDED +#define verifexitval(cond, error_tag) \ +do { \ + assert(cond); \ +} while (ALWAYS_FALSE) +#else +#define verifexitval(cond, error_tag) \ +do { \ + if (!(cond)) { \ + _errval = (error_tag); \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) +#endif + +#if ERR_EMBEDDED +#define haserror(error_tag) (0) +#else +#define haserror(error_tag) \ + (_errval == (error_tag)) +#endif + +#if ERR_EMBEDDED +#define noerror() (1) +#else +#define noerror() \ + (_errval == 0) +#endif + +#define verifjmpexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmpexitsetretval(cond, retval) \ +do { \ + if (!(cond)) { \ + retval = -1; \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#endif /* __ERROR_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/math_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/math_support.h new file mode 100644 index 000000000000..633f86f1a1b0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/math_support.h @@ -0,0 +1,314 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MATH_SUPPORT_H +#define __MATH_SUPPORT_H + +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "type_support.h" +#include "assert_support.h" + +/* in case we have min/max/MIN/MAX macro's undefine them */ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#ifdef MIN /* also defined in include/hrt/numeric.h from SDK */ +#undef MIN +#endif +#ifdef MAX +#undef MAX +#endif + +#ifndef UINT16_MAX +#define UINT16_MAX (0xffffUL) +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffffUL) +#endif + +#define IS_ODD(a) ((a) & 0x1) +#define IS_EVEN(a) (!IS_ODD(a)) +#define IS_POWER2(a) (!((a)&((a)-1))) +#define IS_MASK_BITS_SET(a, b) ((a & b) != 0) + +/*To Find next power of 2 number from x */ +#define bit2(x) ((x) | ((x) >> 1)) +#define bit4(x) (bit2(x) | (bit2(x) >> 2)) +#define bit8(x) (bit4(x) | (bit4(x) >> 4)) +#define bit16(x) (bit8(x) | (bit8(x) >> 8)) +#define bit32(x) (bit16(x) | (bit16(x) >> 16)) +#define NEXT_POWER_OF_2(x) (bit32(x-1) + 1) + +/* force a value to a lower even value */ +#define EVEN_FLOOR(x) ((x) & ~1UL) + +/* A => B */ +#define IMPLIES(a, b) (!(a) || (b)) + +/* The ORIG_BITS th bit is the sign bit */ +/* Sign extends a ORIG_BITS bits long signed number to a 64-bit signed number */ +/* By type casting it can relimited to any valid type-size + * (32-bit signed or 16-bit or 8-bit) + */ +/* By masking it can be transformed to any arbitrary bit size */ +#define SIGN_EXTEND(VAL, ORIG_BITS) \ +((~(((VAL)&(1ULL<<((ORIG_BITS)-1)))-1))|(VAL)) + +#define EXTRACT_BIT(a, b) ((a >> b) & 1) + +/* for preprocessor and array sizing use MIN and MAX + otherwise use min and max */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CLIP(a, b, c) MIN((MAX((a), (b))), (c)) +/* Integer round-down division of a with b */ +#define FLOOR_DIV(a, b) ((b) ? ((a) / (b)) : 0) +/* Align a to the lower multiple of b */ +#define FLOOR_MUL(a, b) (FLOOR_DIV(a, b) * (b)) +/* Integer round-up division of a with b */ +#define CEIL_DIV(a, b) ((b) ? (((a) + (b) - 1) / (b)) : 0) +/* Align a to the upper multiple of b */ +#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) +/* Align a to the upper multiple of b - fast implementation + * for cases when b=pow(2,n) + */ +#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) +/* integer round-up division of a with pow(2,b) */ +#define CEIL_SHIFT(a, b) (((a) + (1UL << (b)) - 1) >> (b)) +/* Align a to the upper multiple of pow(2,b) */ +#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) +/* Absolute difference of a and b */ +#define ABS_DIF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define ABS(a) ABS_DIF(a, 0) +/* Square of x */ +#define SQR(x) ((x)*(x)) +/* Integer round-half-down division of a nad b */ +#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0) +/* Align a to the round-half-down multiple of b */ +#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) + +#define MAX3(a, b, c) MAX((a), MAX((b), (c))) +#define MIN3(a, b, c) MIN((a), MIN((b), (c))) +#define MAX4(a, b, c, d) MAX((MAX((a), (b))), (MAX((c), (d)))) +#define MIN4(a, b, c, d) MIN((MIN((a), (b))), (MIN((c), (d)))) + +/* min and max should not be macros as they will evaluate their arguments twice. + if you really need a macro (e.g. for CPP or for initializing an array) + use MIN() and MAX(), otherwise use min() and max() */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a)))) +#endif + +#ifndef BYTES +#define BYTES(bit) (((bit)+7)/8) +#endif + +#if !defined(PIPE_GENERATION) +STORAGE_CLASS_INLINE unsigned int max_value_bits(unsigned int bits) +{ + return (bits == 0) ? 0 : ((2 * ((1 << ((bits) - 1)) - 1)) + 1); +} +STORAGE_CLASS_INLINE unsigned int max_value_bytes(unsigned int bytes) +{ + return max_value_bits(IA_CSS_UINT8_T_BITS * bytes); +} +STORAGE_CLASS_INLINE int max(int a, int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE int min(int a, int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE int clip(int a, int b, int c) +{ + return min(max(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int umax(unsigned int a, unsigned int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE unsigned int umin(unsigned int a, unsigned int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uclip(unsigned int a, unsigned int b, + unsigned int c) +{ + return umin(umax(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int ceil_div(unsigned int a, unsigned int b) +{ + return CEIL_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul(unsigned int a, unsigned int b) +{ + return CEIL_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul2(unsigned int a, unsigned int b) +{ + return CEIL_MUL2(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift_mul(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT_MUL(a, b); +} + +STORAGE_CLASS_INLINE int abs_dif(int a, int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uabs_dif(unsigned int a, unsigned int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_div(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_mul(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_pow2(uint32_t a) +{ + unsigned int retval = 0; + + if (IS_POWER2(a)) { + retval = (unsigned int)a; + } else { + unsigned int v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + retval = (unsigned int)(v+1); + } + return retval; +} + +STORAGE_CLASS_INLINE unsigned int floor_log2(uint32_t a) +{ + static const uint8_t de_bruijn[] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + uint32_t v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + return (unsigned int)de_bruijn[(v*0x07C4ACDDU)>>27]; +} + +/* Divide by small power of two */ +STORAGE_CLASS_INLINE unsigned int +udiv2_small_i(uint32_t a, uint32_t b) +{ + assert(b <= 2); + return a >> (b-1); +} + +/* optimized divide for small results + * a will be divided by b + * outbits is the number of bits needed for the result + * the smaller the cheaper the function will be. + * if the result doesn't fit in the number of output bits + * the result is incorrect and the function will assert + */ +STORAGE_CLASS_INLINE unsigned int +udiv_medium(uint32_t a, uint32_t b, unsigned outbits) +{ + int bit; + unsigned res = 0; + unsigned mask; + +#ifdef VOLCANO +#pragma ipu unroll +#endif + for (bit = outbits-1 ; bit >= 0; bit--) { + mask = 1<= (b<= c ? a+b-c : a+b); +} + +/* + * For SP and ISP, SDK provides the definition of OP_asp_slor. + * We need it only for host + */ +STORAGE_CLASS_INLINE unsigned int OP_asp_slor(int a, int b, int c) +{ + return ((a << c) | b); +} +#else +#include "hive/customops.h" +#endif /* !defined(__VIED_CELL) */ + +#endif /* !defined(PIPE_GENERATION) */ + +#if !defined(__KERNEL__) +#define clamp(a, min_val, max_val) MIN(MAX((a), (min_val)), (max_val)) +#endif /* !defined(__KERNEL__) */ + +#endif /* __MATH_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/misc_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/misc_support.h new file mode 100644 index 000000000000..a2c2729e946d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/misc_support.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MISC_SUPPORT_H +#define __MISC_SUPPORT_H + +/* suppress compiler warnings on unused variables */ +#ifndef NOT_USED +#define NOT_USED(a) ((void)(a)) +#endif + +/* Calculate the total bytes for pow(2) byte alignment */ +#define tot_bytes_for_pow2_align(pow2, cur_bytes) \ + ((cur_bytes + (pow2 - 1)) & ~(pow2 - 1)) + +/* Display the macro value given a string */ +#define _STR(x) #x +#define STR(x) _STR(x) + +/* Concatenate */ +#ifndef CAT /* also defined in */ +#define _CAT(a, b) a ## b +#define CAT(a, b) _CAT(a, b) +#endif + +#define _CAT3(a, b, c) a ## b ## c +#define CAT3(a, b, c) _CAT3(a, b, c) + +/* NO_HOIST, NO_CSE, NO_ALIAS attributes must be ignored for host code */ +#ifndef __HIVECC +#ifndef NO_HOIST +#define NO_HOIST +#endif +#ifndef NO_CSE +#define NO_CSE +#endif +#ifndef NO_ALIAS +#define NO_ALIAS +#endif +#endif + +enum hive_method_id { + HIVE_METHOD_ID_CRUN, + HIVE_METHOD_ID_UNSCHED, + HIVE_METHOD_ID_SCHED, + HIVE_METHOD_ID_TARGET +}; + +/* Derive METHOD */ +#if defined(C_RUN) + #define HIVE_METHOD "crun" + #define HIVE_METHOD_ID HIVE_METHOD_ID_CRUN +#elif defined(HRT_UNSCHED) + #define HIVE_METHOD "unsched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_UNSCHED +#elif defined(HRT_SCHED) + #define HIVE_METHOD "sched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_SCHED +#else + #define HIVE_METHOD "target" + #define HIVE_METHOD_ID HIVE_METHOD_ID_TARGET + #define HRT_TARGET 1 +#endif + +#endif /* __MISC_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/platform_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/platform_support.h new file mode 100644 index 000000000000..1752efc7b4df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/platform_support.h @@ -0,0 +1,146 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PLATFORM_SUPPORT_H +#define __PLATFORM_SUPPORT_H + +#include "storage_class.h" + +#define MSEC_IN_SEC 1000 +#define NSEC_IN_MSEC 1000000 + +#if defined(_MSC_VER) +#include + +#define IA_CSS_EXTERN +#define SYNC_WITH(x) +#define CSS_ALIGN(d, a) _declspec(align(a)) d + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + /* Placeholder for driver team*/ +} + +STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) +{ + /* Placeholder for driver team*/ + (void)delay_time_ms; +} + +#elif defined(__HIVECC) +#include +#include + +#define IA_CSS_EXTERN extern +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + OP___schedule(); +} + +#elif defined(__KERNEL__) +#include +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __aligned(a) + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + usleep_range(1, 50); +} + +#elif defined(__GNUC__) +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) + +/* Define some __HIVECC specific macros to nothing to allow host code compilation */ +#ifndef NO_ALIAS +#define NO_ALIAS +#endif + +#ifndef SYNC_WITH +#define SYNC_WITH(x) +#endif + +#if defined(HRT_CSIM) + #include "hrt/host.h" /* Using hrt_sleep from hrt/host.h */ + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + /* For the SDK still using hrt_sleep */ + hrt_sleep(); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + /* For the SDK still using hrt_sleep */ + long unsigned int i = 0; + for (i = 0; i < delay_time_ms; i++) { + hrt_sleep(); + } + } +#else + #include + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + struct timespec delay_time; + + delay_time.tv_sec = 0; + delay_time.tv_nsec = 10; + nanosleep(&delay_time, NULL); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + struct timespec delay_time; + + if (delay_time_ms >= MSEC_IN_SEC) { + delay_time.tv_sec = delay_time_ms / MSEC_IN_SEC; + delay_time.tv_nsec = (delay_time_ms % MSEC_IN_SEC) * NSEC_IN_MSEC; + } else { + delay_time.tv_sec = 0; + delay_time.tv_nsec = delay_time_ms * NSEC_IN_MSEC; + } + nanosleep(&delay_time, NULL); + } +#endif + +#else +#include +#endif + +/*needed for the include in stdint.h for various environments */ +#include "type_support.h" +#include "storage_class.h" + +#define MAX_ALIGNMENT 8 +#define aligned_uint8(type, obj) CSS_ALIGN(uint8_t obj, 1) +#define aligned_int8(type, obj) CSS_ALIGN(int8_t obj, 1) +#define aligned_uint16(type, obj) CSS_ALIGN(uint16_t obj, 2) +#define aligned_int16(type, obj) CSS_ALIGN(int16_t obj, 2) +#define aligned_uint32(type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_int32(type, obj) CSS_ALIGN(int32_t obj, 4) + +/* needed as long as hivecc does not define the type (u)int64_t */ +#if defined(__HIVECC) +#define aligned_uint64(type, obj) CSS_ALIGN(unsigned long long obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(signed long long obj, 8) +#else +#define aligned_uint64(type, obj) CSS_ALIGN(uint64_t obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(int64_t obj, 8) +#endif +#define aligned_enum(enum_type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_struct(struct_type, obj) struct_type obj + +#endif /* __PLATFORM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/print_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/print_support.h new file mode 100644 index 000000000000..0b614f7ef12d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/print_support.h @@ -0,0 +1,90 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PRINT_SUPPORT_H +#define __PRINT_SUPPORT_H + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE + +/* TODO: Windows driver team to provide tracing mechanism for kernel mode + * e.g. DbgPrint and DbgPrintEx + */ +extern void FwTracePrintPWARN(const char *fmt, ...); +extern void FwTracePrintPRINT(const char *fmt, ...); +extern void FwTracePrintPERROR(const char *fmt, ...); +extern void FwTracePrintPDEBUG(const char *fmt, ...); + +#define PWARN(format, ...) FwTracePrintPWARN(format, __VA_ARGS__) +#define PRINT(format, ...) FwTracePrintPRINT(format, __VA_ARGS__) +#define PERROR(format, ...) FwTracePrintPERROR(format, __VA_ARGS__) +#define PDEBUG(format, ...) FwTracePrintPDEBUG(format, __VA_ARGS__) + +#else +/* Windows usermode compilation */ +#include + +/* To change the defines below, communicate with Windows team first + * to ensure they will not get flooded with prints + */ +/* This is temporary workaround to avoid flooding userspace + * Windows driver with prints + */ + +#define PWARN(format, ...) +#define PRINT(format, ...) +#define PERROR(format, ...) printf("error: " format, __VA_ARGS__) +#define PDEBUG(format, ...) + +#endif /* _KERNEL_MODE */ + +#elif defined(__HIVECC) +#include +/* To be revised + +#define PWARN(format) +#define PRINT(format) OP___printstring(format) +#define PERROR(variable) OP___dump(9999, arguments) +#define PDEBUG(variable) OP___dump(__LINE__, arguments) + +*/ + +#define PRINTSTRING(str) OP___printstring(str) + +#elif defined(__KERNEL__) +#include +#include + + +#define PWARN(format, arguments...) pr_debug(format, ##arguments) +#define PRINT(format, arguments...) pr_debug(format, ##arguments) +#define PERROR(format, arguments...) pr_debug(format, ##arguments) +#define PDEBUG(format, arguments...) pr_debug(format, ##arguments) + +#else +#include + +#define PRINT_HELPER(prefix, format, ...) printf(prefix format "%s", __VA_ARGS__) + +/* The trailing "" allows the edge case of printing single string */ +#define PWARN(...) PRINT_HELPER("warning: ", __VA_ARGS__, "") +#define PRINT(...) PRINT_HELPER("", __VA_ARGS__, "") +#define PERROR(...) PRINT_HELPER("error: ", __VA_ARGS__, "") +#define PDEBUG(...) PRINT_HELPER("debug: ", __VA_ARGS__, "") + +#define PRINTSTRING(str) PRINT(str) + +#endif + +#endif /* __PRINT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/storage_class.h new file mode 100644 index 000000000000..af19b4026220 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/storage_class.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __STORAGE_CLASS_H +#define __STORAGE_CLASS_H + +#define STORAGE_CLASS_EXTERN \ +extern + +#if defined(_MSC_VER) +#define STORAGE_CLASS_INLINE \ +static __inline +#elif defined(__HIVECC) +#define STORAGE_CLASS_INLINE \ +static inline +#else +#define STORAGE_CLASS_INLINE \ +static inline +#endif + +/* Register struct */ +#ifndef __register +#if defined(__HIVECC) && !defined(PIPE_GENERATION) +#define __register register +#else +#define __register +#endif +#endif + +/* Memory attribute */ +#ifndef MEM +#ifdef PIPE_GENERATION +#elif defined(__HIVECC) +#include +#else +#define MEM(any_mem) +#endif +#endif + +#endif /* __STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/type_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/type_support.h new file mode 100644 index 000000000000..a86da0e78941 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/support/type_support.h @@ -0,0 +1,80 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __TYPE_SUPPORT_H +#define __TYPE_SUPPORT_H + +/* Per the DLI spec, types are in "type_support.h" and + * "platform_support.h" is for unclassified/to be refactored + * platform specific definitions. + */ +#define IA_CSS_UINT8_T_BITS 8 +#define IA_CSS_UINT16_T_BITS 16 +#define IA_CSS_UINT32_T_BITS 32 +#define IA_CSS_INT32_T_BITS 32 +#define IA_CSS_UINT64_T_BITS 64 + + +#if defined(_MSC_VER) +#include +#include +#include +#include +#if defined(_M_X64) +#define HOST_ADDRESS(x) (unsigned long long)(x) +#else +#define HOST_ADDRESS(x) (unsigned long)(x) +#endif + +#elif defined(PARAM_GENERATION) +/* Nothing */ +#elif defined(__HIVECC) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif defined(__KERNEL__) +#include +#include + +#define CHAR_BIT (8) +#define HOST_ADDRESS(x) (unsigned long)(x) + +#elif defined(__GNUC__) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#else /* default is for the FIST environment */ +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#endif + +#if !defined(PIPE_GENERATION) && !defined(IO_GENERATION) +/* genpipe cannot handle the void* syntax */ +typedef void *HANDLE; +#endif + +#endif /* __TYPE_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom.h new file mode 100644 index 000000000000..5426d6d18e0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom.h @@ -0,0 +1,247 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_H +#define __IA_CSS_SYSCOM_H + + +/* + * The CSS Subsystem Communication Interface - Host side + * + * It provides subsystem initialzation, send ports and receive ports + * The PSYS and ISYS interfaces are implemented on top of this interface. + */ + +#include "ia_css_syscom_config.h" + +#define FW_ERROR_INVALID_PARAMETER (-1) +#define FW_ERROR_BAD_ADDRESS (-2) +#define FW_ERROR_BUSY (-3) +#define FW_ERROR_NO_MEMORY (-4) + +struct ia_css_syscom_context; + +/** + * ia_css_syscom_size() - provide syscom external buffer requirements + * @config: pointer to the configuration data (read) + * @size: pointer to the buffer size (write) + * + * Purpose: + * - Provide external buffer requirements + * - To be used for external buffer allocation + * + */ +extern void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size +); + +/** + * ia_css_syscom_open() - initialize a subsystem context + * @config: pointer to the configuration data (read) + * @buf: pointer to externally allocated buffers (read) + * @returns: struct ia_css_syscom_context* on success, 0 otherwise. + * + * Purpose: + * - initialize host side data structures + * - boot the subsystem? + * + */ +extern struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *config, + struct ia_css_syscom_buf *buf +); + +/** + * ia_css_syscom_close() - signal close to cell + * @context: pointer to the subsystem context + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if SPC is not ready yet. + * + * Purpose: + * Request from the Cell to terminate + */ +extern int +ia_css_syscom_close( + struct ia_css_syscom_context *context +); + +/** + * ia_css_syscom_release() - free context + * @context: pointer to the subsystem context + * @force: flag which specifies whether cell + * state will be checked before freeing the + * context. + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if cell + * is busy and call was not forced. + * + * Purpose: + * 2 modes, with first (force==true) immediately + * free context, and second (force==false) verifying + * that the cell state is ok and freeing context if so, + * returning error otherwise. + */ +extern int +ia_css_syscom_release( + struct ia_css_syscom_context *context, + unsigned int force +); + +/** + * Open a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be sent to a port without error. + * @context: pointer to the subsystem context + * @port: send port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Send a token to the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: send port index + * @token: pointer to the token value that is transferred to the subsystem + * @returns: number of tokens sent on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + const void *token +); + +/** + * Open a port for receiving tokens to the subsystem + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for receiving tokens to the subsystem + * Returns 0 on success, otherwise negative value of error code + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be received from a port without errors. + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Receive a token from the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: receive port index + * @token (output): pointer to (space for) the token to be received + * @returns: number of tokens received on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + void *token +); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/** + * ia_css_syscom_store_dmem() - store subsystem context information in DMEM + * @context: pointer to the subsystem context + * @ssid: subsystem id + * @vtl0_addr_mask: VTL0 address mask; only applicable when the passed in context is secure + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *context, + unsigned int ssid, + unsigned int vtl0_addr_mask +); + +/** + * ia_css_syscom_set_trustlet_status() - store truslet configuration setting + * @context: pointer to the subsystem context + * @trustlet_exist: 1 if trustlet exists + */ +extern void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +); + +/** + * ia_css_syscom_is_ab_spc_ready() - check if SPC access blocker programming is completed + * @context: pointer to the subsystem context + * @returns: 1 when status is ready. 0 otherwise + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +#endif /* __IA_CSS_SYSCOM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_config.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_config.h new file mode 100644 index 000000000000..2f5eb309df94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_config.h @@ -0,0 +1,97 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_H +#define __IA_CSS_SYSCOM_CONFIG_H + +#include +#include + +/* syscom size struct, output of ia_css_syscom_size, + * input for (external) allocation + */ +struct ia_css_syscom_size { + /* Size of host buffer */ + unsigned int cpu; + /* Size of shared config buffer (host to cell) */ + unsigned int shm; + /* Size of shared input queue buffers (host to cell) */ + unsigned int ibuf; + /* Size of shared output queue buffers (cell to host) */ + unsigned int obuf; +}; + +/* syscom buffer struct, output of (external) allocation, + * input for ia_css_syscom_open + */ +struct ia_css_syscom_buf { + char *cpu; /* host buffer */ + + /* shared memory buffer host address */ + host_virtual_address_t shm_host; + /* shared memory buffer cell address */ + vied_virtual_address_t shm_cell; + + /* input queue shared buffer host address */ + host_virtual_address_t ibuf_host; + /* input queue shared buffer cell address */ + vied_virtual_address_t ibuf_cell; + + /* output queue shared buffer host address */ + host_virtual_address_t obuf_host; + /* output queue shared buffer cell address */ + vied_virtual_address_t obuf_cell; +}; + +struct ia_css_syscom_queue_config { + unsigned int queue_size; /* tokens per queue */ + unsigned int token_size; /* bytes per token */ +}; + +/** + * Parameter struct for ia_css_syscom_open + */ +struct ia_css_syscom_config { + /* This member in no longer used in syscom. + It is kept to not break any driver builds, and will be removed when + all assignments have been removed from driver code */ + /* address of firmware in DDR/IMR */ + unsigned long long host_firmware_address; + + /* address of firmware in DDR, seen from SPC */ + unsigned int vied_firmware_address; + + unsigned int ssid; + unsigned int mmid; + + unsigned int num_input_queues; + unsigned int num_output_queues; + struct ia_css_syscom_queue_config *input; + struct ia_css_syscom_queue_config *output; + + unsigned int regs_addr; + unsigned int dmem_addr; + + /* firmware-specific configuration data */ + void *specific_addr; + unsigned int specific_size; + + /* if true; secure syscom in VTIO Case + * if false, non-secure syscom + */ + bool secure; + unsigned int vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_trace.h new file mode 100644 index 000000000000..2c32693c2a82 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/interface/ia_css_syscom_trace.h @@ -0,0 +1,51 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_SYSCOM_TRACE_H +#define __IA_CSS_SYSCOM_TRACE_H + +#include "ia_css_trace.h" + +#define SYSCOM_TRACE_LEVEL_DEFAULT 1 +#define SYSCOM_TRACE_LEVEL_DEBUG 2 + +/* Set to default level if no level is defined */ +#ifndef SYSCOM_TRACE_LEVEL +#define SYSCOM_TRACE_LEVEL SYSCOM_TRACE_LEVEL_DEFAULT +#endif /* SYSCOM_TRACE_LEVEL */ + +/* SYSCOM Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#define SYSCOM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + +#if (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEFAULT) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +#elif (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEBUG) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +#else +# error "Connection manager trace level not defined!" +#endif /* SYSCOM_TRACE_LEVEL */ + +#endif /* __IA_CSS_SYSCOM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom.c new file mode 100644 index 000000000000..cdf9df0531ff --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom.c @@ -0,0 +1,650 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_syscom.h" + +#include "ia_css_syscom_context.h" +#include "ia_css_syscom_config_fw.h" +#include "ia_css_syscom_trace.h" + +#include "queue.h" +#include "send_port.h" +#include "recv_port.h" +#include "regmem_access.h" + +#include "error_support.h" +#include "cpu_mem_support.h" + +#include "queue_struct.h" +#include "send_port_struct.h" +#include "recv_port_struct.h" + +#include "type_support.h" +#include +#include +#include "platform_support.h" + +#include "ia_css_cell.h" + +/* struct of internal buffer sizes */ +struct ia_css_syscom_size_intern { + unsigned int context; + unsigned int input_queue; + unsigned int output_queue; + unsigned int input_port; + unsigned int output_port; + + unsigned int fw_config; + unsigned int specific; + + unsigned int input_buffer; + unsigned int output_buffer; +}; + +/* Allocate buffers internally, when no buffers are provided */ +static int +ia_css_syscom_alloc( + unsigned int ssid, + unsigned int mmid, + const struct ia_css_syscom_size *size, + struct ia_css_syscom_buf *buf) +{ + /* zero the buffer to set all pointers to zero */ + memset(buf, 0, sizeof(*buf)); + + /* allocate cpu_mem */ + buf->cpu = (char *)ia_css_cpu_mem_alloc(size->cpu); + if (!buf->cpu) + goto EXIT7; + + /* allocate and map shared config buffer */ + buf->shm_host = shared_memory_alloc(mmid, size->shm); + if (!buf->shm_host) + goto EXIT6; + buf->shm_cell = shared_memory_map(ssid, mmid, buf->shm_host); + if (!buf->shm_cell) + goto EXIT5; + + /* allocate and map input queue buffer */ + buf->ibuf_host = shared_memory_alloc(mmid, size->ibuf); + if (!buf->ibuf_host) + goto EXIT4; + buf->ibuf_cell = shared_memory_map(ssid, mmid, buf->ibuf_host); + if (!buf->ibuf_cell) + goto EXIT3; + + /* allocate and map output queue buffer */ + buf->obuf_host = shared_memory_alloc(mmid, size->obuf); + if (!buf->obuf_host) + goto EXIT2; + buf->obuf_cell = shared_memory_map(ssid, mmid, buf->obuf_host); + if (!buf->obuf_cell) + goto EXIT1; + + return 0; + +EXIT1: shared_memory_free(mmid, buf->obuf_host); +EXIT2: shared_memory_unmap(ssid, mmid, buf->ibuf_cell); +EXIT3: shared_memory_free(mmid, buf->ibuf_host); +EXIT4: shared_memory_unmap(ssid, mmid, buf->shm_cell); +EXIT5: shared_memory_free(mmid, buf->shm_host); +EXIT6: ia_css_cpu_mem_free(buf->cpu); +EXIT7: return FW_ERROR_NO_MEMORY; +} + +static void +ia_css_syscom_size_intern( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size_intern *size) +{ + /* convert syscom config into syscom internal size struct */ + + unsigned int i; + + size->context = sizeof(struct ia_css_syscom_context); + size->input_queue = cfg->num_input_queues * sizeof(struct sys_queue); + size->output_queue = cfg->num_output_queues * sizeof(struct sys_queue); + size->input_port = cfg->num_input_queues * sizeof(struct send_port); + size->output_port = cfg->num_output_queues * sizeof(struct recv_port); + + size->fw_config = sizeof(struct ia_css_syscom_config_fw); + size->specific = cfg->specific_size; + + /* accumulate input queue buffer sizes */ + size->input_buffer = 0; + for (i = 0; i < cfg->num_input_queues; i++) { + size->input_buffer += + sys_queue_buf_size(cfg->input[i].queue_size, + cfg->input[i].token_size); + } + + /* accumulate outut queue buffer sizes */ + size->output_buffer = 0; + for (i = 0; i < cfg->num_output_queues; i++) { + size->output_buffer += + sys_queue_buf_size(cfg->output[i].queue_size, + cfg->output[i].token_size); + } +} + +static void +ia_css_syscom_size_extern( + const struct ia_css_syscom_size_intern *i, + struct ia_css_syscom_size *e) +{ + /* convert syscom internal size struct into external size struct */ + + e->cpu = i->context + i->input_queue + i->output_queue + + i->input_port + i->output_port; + e->shm = i->fw_config + i->input_queue + i->output_queue + i->specific; + e->ibuf = i->input_buffer; + e->obuf = i->output_buffer; +} + +/* Function that provides buffer sizes to be allocated */ +void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size) +{ + struct ia_css_syscom_size_intern i; + + ia_css_syscom_size_intern(cfg, &i); + ia_css_syscom_size_extern(&i, size); +} + +static struct ia_css_syscom_context* +ia_css_syscom_assign_buf( + const struct ia_css_syscom_size_intern *i, + const struct ia_css_syscom_buf *buf) +{ + struct ia_css_syscom_context *ctx; + char *cpu_mem_buf; + host_virtual_address_t shm_buf_host; + vied_virtual_address_t shm_buf_cell; + + /* host context */ + cpu_mem_buf = buf->cpu; + + ctx = (struct ia_css_syscom_context *)cpu_mem_buf; + ia_css_cpu_mem_set_zero(ctx, i->context); + cpu_mem_buf += i->context; + + ctx->input_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->input_queue; + + ctx->output_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->output_queue; + + ctx->send_port = (struct send_port *) cpu_mem_buf; + cpu_mem_buf += i->input_port; + + ctx->recv_port = (struct recv_port *) cpu_mem_buf; + + + /* cell config */ + shm_buf_host = buf->shm_host; + shm_buf_cell = buf->shm_cell; + + ctx->config_host_addr = shm_buf_host; + shm_buf_host += i->fw_config; + ctx->config_vied_addr = shm_buf_cell; + shm_buf_cell += i->fw_config; + + ctx->input_queue_host_addr = shm_buf_host; + shm_buf_host += i->input_queue; + ctx->input_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->input_queue; + + ctx->output_queue_host_addr = shm_buf_host; + shm_buf_host += i->output_queue; + ctx->output_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->output_queue; + + ctx->specific_host_addr = shm_buf_host; + ctx->specific_vied_addr = shm_buf_cell; + + ctx->ibuf_host_addr = buf->ibuf_host; + ctx->ibuf_vied_addr = buf->ibuf_cell; + + ctx->obuf_host_addr = buf->obuf_host; + ctx->obuf_vied_addr = buf->obuf_cell; + + return ctx; +} + +struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *cfg, + struct ia_css_syscom_buf *buf_extern +) +{ + struct ia_css_syscom_size_intern size_intern; + struct ia_css_syscom_size size; + struct ia_css_syscom_buf buf_intern; + struct ia_css_syscom_buf *buf; + struct ia_css_syscom_context *ctx; + struct ia_css_syscom_config_fw fw_cfg; + unsigned int i; + struct sys_queue_res res; + + IA_CSS_TRACE_0(SYSCOM, INFO, "Entered: ia_css_syscom_open\n"); + + /* error handling */ + if (cfg == NULL) + return NULL; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) start\n", cfg->secure); + + /* check members of cfg: TBD */ + + /* + * Check if SP is in valid state, have to wait if not ready. + * In some platform (Such as VP), it will need more time to wait due to system performance; + * If return NULL without wait for SPC0 ready, Driver load FW will failed + */ + ia_css_cell_wait(cfg->ssid, SPC0); + + ia_css_syscom_size_intern(cfg, &size_intern); + ia_css_syscom_size_extern(&size_intern, &size); + + if (buf_extern) { + /* use externally allocated buffers */ + buf = buf_extern; + } else { + /* use internally allocated buffers */ + buf = &buf_intern; + if (ia_css_syscom_alloc(cfg->ssid, cfg->mmid, &size, buf) != 0) + return NULL; + } + + /* assign buffer pointers */ + ctx = ia_css_syscom_assign_buf(&size_intern, buf); + /* only need to free internally allocated buffers */ + ctx->free_buf = !buf_extern; + + ctx->cell_regs_addr = cfg->regs_addr; + /* regmem is at cell_dmem_addr + REGMEM_OFFSET */ + ctx->cell_dmem_addr = cfg->dmem_addr; + + ctx->num_input_queues = cfg->num_input_queues; + ctx->num_output_queues = cfg->num_output_queues; + + ctx->env.mmid = cfg->mmid; + ctx->env.ssid = cfg->ssid; + ctx->env.mem_addr = cfg->dmem_addr; + + ctx->regmem_idx = SYSCOM_QPR_BASE_REG; + + /* initialize input queues */ + res.reg = SYSCOM_QPR_BASE_REG; + res.host_address = ctx->ibuf_host_addr; + res.vied_address = ctx->ibuf_vied_addr; + for (i = 0; i < cfg->num_input_queues; i++) { + sys_queue_init(ctx->input_queue + i, + cfg->input[i].queue_size, + cfg->input[i].token_size, &res); + } + + /* initialize output queues */ + res.host_address = ctx->obuf_host_addr; + res.vied_address = ctx->obuf_vied_addr; + for (i = 0; i < cfg->num_output_queues; i++) { + sys_queue_init(ctx->output_queue + i, + cfg->output[i].queue_size, + cfg->output[i].token_size, &res); + } + + /* fill shared queue structs */ + shared_memory_store(cfg->mmid, ctx->input_queue_host_addr, + ctx->input_queue, + cfg->num_input_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->input_queue_host_addr), + cfg->num_input_queues * sizeof(struct sys_queue)); + shared_memory_store(cfg->mmid, ctx->output_queue_host_addr, + ctx->output_queue, + cfg->num_output_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->output_queue_host_addr), + cfg->num_output_queues * sizeof(struct sys_queue)); + + /* Zero the queue buffers. Is this really needed? */ + shared_memory_zero(cfg->mmid, buf->ibuf_host, size.ibuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->ibuf_host), + size.ibuf); + shared_memory_zero(cfg->mmid, buf->obuf_host, size.obuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->obuf_host), + size.obuf); + + /* copy firmware specific data */ + if (cfg->specific_addr && cfg->specific_size) { + shared_memory_store(cfg->mmid, ctx->specific_host_addr, + cfg->specific_addr, cfg->specific_size); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->specific_host_addr), + cfg->specific_size); + } + + fw_cfg.num_input_queues = cfg->num_input_queues; + fw_cfg.num_output_queues = cfg->num_output_queues; + fw_cfg.input_queue = ctx->input_queue_vied_addr; + fw_cfg.output_queue = ctx->output_queue_vied_addr; + fw_cfg.specific_addr = ctx->specific_vied_addr; + fw_cfg.specific_size = cfg->specific_size; + + shared_memory_store(cfg->mmid, ctx->config_host_addr, + &fw_cfg, sizeof(struct ia_css_syscom_config_fw)); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(ctx->config_host_addr), + sizeof(struct ia_css_syscom_config_fw)); + +#if !HAS_DUAL_CMD_CTX_SUPPORT + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, cfg->ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, cfg->ssid); + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, cfg->ssid); +#endif + + /* Indicate if ctx is created for secure stream purpose */ + ctx->secure = cfg->secure; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) completed\n", cfg->secure); + return ctx; +} + + +int +ia_css_syscom_close( + struct ia_css_syscom_context *ctx +) { + int state; + + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle close request yet */ + return FW_ERROR_BUSY; + } + + /* set close request flag */ + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_INACTIVE, ctx->env.ssid); + + return 0; +} + +static void +ia_css_syscom_free(struct ia_css_syscom_context *ctx) +{ + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->ibuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->ibuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->obuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->obuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, + ctx->config_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->config_host_addr); + ia_css_cpu_mem_free(ctx); +} + +int +ia_css_syscom_release( + struct ia_css_syscom_context *ctx, + unsigned int force +) { + /* check if release is forced, an verify cell state if it is not */ + if (!force) { + if (!ia_css_cell_is_ready(ctx->env.ssid, SPC0)) + return FW_ERROR_BUSY; + } + + /* Reset the regmem idx */ + ctx->regmem_idx = 0; + + if (ctx->free_buf) + ia_css_syscom_free(ctx); + + return 0; +} + +int ia_css_syscom_send_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + send_port_open(ctx->send_port + port, + ctx->input_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_send_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +int ia_css_syscom_send_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_available(ctx->send_port + port); +} + +int ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + const void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_transfer(ctx->send_port + port, token); +} + +int ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, + SYSCOM_STATE_REG, ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + recv_port_open(ctx->recv_port + port, + ctx->output_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +/* + * Get the number of responses in the response queue + */ +int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_available(ctx->recv_port + port); +} + + +/* + * Dequeue the head of the response queue + * returns an error when the response queue is empty + */ +int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_transfer(ctx->recv_port + port, token); +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* + * store subsystem context information in DMEM + */ +int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *ctx, + unsigned int ssid, + unsigned int vtl0_addr_mask +) +{ + unsigned int read_back; + + NOT_USED(vtl0_addr_mask); + NOT_USED(read_back); + + if (ctx->secure) { + /* store VTL0 address mask in 'secure' context */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem VTL0_ADDR_MASK (%#x) @ dmem_addr %#x ssid %d\n", + vtl0_addr_mask, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_VTL0_ADDR_MASK, vtl0_addr_mask, ssid); + } + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, ssid); + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, ssid); + + return 0; +} + +/* + * store truslet configuration status setting + */ +void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +) +{ + unsigned int value; + + value = trustlet_exist ? TRUSTLET_EXIST : TRUSTLET_NOT_EXIST; + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_set_trustlet_status TRUSTLET_STATUS (%#x) @ dmem_addr %#x ssid %d\n", + value, dmem_addr, ssid); + regmem_store_32(dmem_addr, TRUSTLET_STATUS, value, ssid); +} + +/* + * check if SPC access blocker programming is completed + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +) +{ + unsigned int value; + + /* We only expect the call from non-secure context only */ + if (ctx->secure) { + IA_CSS_TRACE_0(SYSCOM, ERROR, "ia_css_syscom_is_spc_ab_ready - Please call from non-secure context\n"); + return false; + } + + value = regmem_load_32(ctx->cell_dmem_addr, AB_SPC_STATUS, ctx->env.ssid); + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_is_spc_ab_ready AB_SPC_STATUS @ dmem_addr %#x ssid %d - value %#x\n", + ctx->cell_dmem_addr, ctx->env.ssid, value); + + return (value == AB_SPC_READY); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_config_fw.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_config_fw.h new file mode 100644 index 000000000000..0cacd5a34934 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_config_fw.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_FW_H +#define __IA_CSS_SYSCOM_CONFIG_FW_H + +#include "type_support.h" + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_STATE_UNINIT = 0x57A7E000, + /* SP Syscom sets this when it is ready for use */ + SYSCOM_STATE_READY = 0x57A7E001, + /* SP Syscom sets this when no more syscom accesses will happen */ + SYSCOM_STATE_INACTIVE = 0x57A7E002 +}; + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_COMMAND_UNINIT = 0x57A7F000, + /* Host Syscom requests syscom to become inactive */ + SYSCOM_COMMAND_INACTIVE = 0x57A7F001 +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +enum { + /* Program load or explicit host setting should init to this */ + TRUSTLET_UNINIT = 0x57A8E000, + /* Host Syscom informs SP that Trustlet exists */ + TRUSTLET_EXIST = 0x57A8E001, + /* Host Syscom informs SP that Trustlet does not exist */ + TRUSTLET_NOT_EXIST = 0x57A8E002 +}; + +enum { + /* Program load or explicit setting initialized by SP */ + AB_SPC_NOT_READY = 0x57A8F000, + /* SP informs host that SPC access programming is completed */ + AB_SPC_READY = 0x57A8F001 +}; +#endif + +/* firmware config: data that sent from the host to SP via DDR */ +/* Cell copies data into a context */ + +struct ia_css_syscom_config_fw { + unsigned int firmware_address; + + unsigned int num_input_queues; + unsigned int num_output_queues; + unsigned int input_queue; /* hmm_ptr / struct queue* */ + unsigned int output_queue; /* hmm_ptr / struct queue* */ + + unsigned int specific_addr; /* vied virtual address */ + unsigned int specific_size; +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_FW_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_context.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_context.h new file mode 100644 index 000000000000..ecf22f6b7ac5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/src/ia_css_syscom_context.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONTEXT_H +#define __IA_CSS_SYSCOM_CONTEXT_H + +#include + +#include "port_env_struct.h" +#include + +/* host context */ +struct ia_css_syscom_context { + vied_virtual_address_t cell_firmware_addr; + unsigned int cell_regs_addr; + unsigned int cell_dmem_addr; + + struct port_env env; + + unsigned int num_input_queues; + unsigned int num_output_queues; + + /* array of input queues (from host to SP) */ + struct sys_queue *input_queue; + /* array of output queues (from SP to host) */ + struct sys_queue *output_queue; + + struct send_port *send_port; + struct recv_port *recv_port; + + unsigned int regmem_idx; + unsigned int free_buf; + + host_virtual_address_t config_host_addr; + host_virtual_address_t input_queue_host_addr; + host_virtual_address_t output_queue_host_addr; + host_virtual_address_t specific_host_addr; + host_virtual_address_t ibuf_host_addr; + host_virtual_address_t obuf_host_addr; + + vied_virtual_address_t config_vied_addr; + vied_virtual_address_t input_queue_vied_addr; + vied_virtual_address_t output_queue_vied_addr; + vied_virtual_address_t specific_vied_addr; + vied_virtual_address_t ibuf_vied_addr; + vied_virtual_address_t obuf_vied_addr; + + /* if true; secure syscom object as in VTIO Case + * if false, non-secure syscom + */ + bool secure; +}; + +#endif /* __IA_CSS_SYSCOM_CONTEXT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/syscom.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/syscom.mk new file mode 100644 index 000000000000..8d36b8928af5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/syscom/syscom.mk @@ -0,0 +1,42 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is SYSCOM + +SYSCOM_DIR=$${MODULES_DIR}/syscom + +SYSCOM_INTERFACE=$(SYSCOM_DIR)/interface +SYSCOM_SOURCES1=$(SYSCOM_DIR)/src + +SYSCOM_HOST_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom.c + +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_HOST_CPPFLAGS += -I$${MODULES_DIR}/devices +ifdef REGMEM_SECURE_OFFSET +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif + +SYSCOM_FW_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom_fw.c + +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_FW_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) +ifdef REGMEM_SECURE_OFFSET +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/interface/ia_css_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/interface/ia_css_trace.h new file mode 100644 index 000000000000..b85b1810f107 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/interface/ia_css_trace.h @@ -0,0 +1,883 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/*! \file */ + +#ifndef __IA_CSS_TRACE_H +#define __IA_CSS_TRACE_H + +/* +** Configurations +*/ + +/** + * STEP 1: Define {Module Name}_TRACE_METHOD to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * + * Example: + * #define NCI_DMA_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + */ + +/**< Use whatever method of tracing that best suits the platform + * this code is compiled for. + */ +#define IA_CSS_TRACE_METHOD_NATIVE 1 +/**< Use the Tracing NCI. */ +#define IA_CSS_TRACE_METHOD_TRACE 2 + +/** + * STEP 2: Define {Module Name}_TRACE_LEVEL_{Level} to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * {Level}, in decreasing order of severity, is one of the + * following values: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * + * Example: + * #define NCI_DMA_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + * #define NCI_DMA_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + */ +/**< Disables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_DISABLED 0 +/**< Enables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_ENABLED 1 + +/* + * Used in macro definition with do-while loop + * for removing checkpatch warnings + */ +#define IA_CSS_TRACE_FILE_DUMMY_DEFINE + +/** + * STEP 3: Define IA_CSS_TRACE_PRINT_FILE_LINE to have file name and + * line printed with every log message. + * + * Example: + * #define IA_CSS_TRACE_PRINT_FILE_LINE + */ + +/* +** Interface +*/ + +/* +** Static +*/ + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * at compile-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_0(module, severity, format) \ + IA_CSS_TRACE_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_1(module, severity, format, a1) \ + IA_CSS_TRACE_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_IMPL(module, 5, severity, format, a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_6(module, severity, format, a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_IMPL(module, 6, severity, format, a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_7(module, severity, format, a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Dynamic +*/ + +/** +* Declares, but does not define, dynamic tracing functions and variables +* for module \p module. For each module, place an instance of this macro +* in the compilation unit in which you want to use dynamic tracing facility +* so as to inform the compiler of the declaration of the available functions. +* An invocation of this function does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DEFINE +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) +/** +* Declares the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) + +/** +* Defines dynamic tracing functions and variables for module \p module. +* For each module, place an instance of this macro in one, and only one, +* of your SOURCE files so as to allow the linker resolve the related symbols. +* An invocation of this macro does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DECLARE +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) +/** +* Defines the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_DYNAMIC_0(module, severity, format) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_1(module, severity, format, a1) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 5, severity, format, \ + a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_6(module, severity, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 6, severity, format, \ + a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_7(module, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Implementation +*/ + +/* CAT */ +#define IA_CSS_TRACE_CAT_IMPL(a, b) a ## b +#define IA_CSS_TRACE_CAT(a, b) IA_CSS_TRACE_CAT_IMPL(a, b) + +/* Bridge */ +#if defined(__HIVECC) || defined(__GNUC__) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, arguments ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + ## arguments \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, \ + arguments ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + ## arguments); \ + } \ + } while (0) +#elif defined(_MSC_VER) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + __VA_ARGS__ \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + __VA_ARGS__); \ + } \ + } while (0) +#endif + +/* +** Native Backend +*/ + +#if defined(__HIVECC) + #define IA_CSS_TRACE_PLATFORM_CELL +#elif defined(__GNUC__) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, \ + format), ## arguments); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, \ + format), ## arguments); \ + } while (0) + +#elif defined(_MSC_VER) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) +#else + #error Unsupported platform! +#endif /* Platform */ + +#if defined(IA_CSS_TRACE_PLATFORM_CELL) + #include /* VOLATILE */ + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + do { \ + OP___printstring(__FILE__":") VOLATILE; \ + OP___printdec(__LINE__) VOLATILE; \ + OP___printstring("\n") VOLATILE; \ + } while (0) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + OP___printstring("["module"]:["severity"]:") \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_MSG_NATIVE(severity, module, format) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + OP___printstring("["module"]:["severity"]: "format) \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_ARG_NATIVE(module, severity, i, value) \ + do { \ + IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity); \ + OP___dump(i, value) VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 7, a7); \ + } while (0) + /* + ** Tracing Backend + */ +#if !defined(HRT_CSIM) && !defined(NO_TUNIT) + #include "vied_nci_tunit.h" +#endif + #define IA_CSS_TRACE_AUG_FORMAT_TRACE(format, module) \ + "[" module "]" format " : PID = %x : Timestamp = %d : PC = %x" + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + vied_nci_tunit_print(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + vied_nci_tunit_print1i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + vied_nci_tunit_print2i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + vied_nci_tunit_print3i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, a1, a2, a3, a4) \ + vied_nci_tunit_print4i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + vied_nci_tunit_print5i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + vied_nci_tunit_print6i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + vied_nci_tunit_print7i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6, a7) + +#elif defined(IA_CSS_TRACE_PLATFORM_HOST) + #include "print_support.h" + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + PRINT("%s:%d:\n", __FILE__, __LINE__) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, format) \ + "[" module "]:[" severity "]: " format + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) + + #define IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, format) \ + "["module"]:["severity"]: "format + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + IA_CSS_TRACE_TRACE(severity, module, format) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) +#endif + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_0(severity, module, format) +#define IA_CSS_TRACE_1_1_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_1_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_1_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_1_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_1 IA_CSS_TRACE_NATIVE_0 +#define IA_CSS_TRACE_1_1_1 IA_CSS_TRACE_NATIVE_1 +#define IA_CSS_TRACE_2_1_1 IA_CSS_TRACE_NATIVE_2 +#define IA_CSS_TRACE_3_1_1 IA_CSS_TRACE_NATIVE_3 +#define IA_CSS_TRACE_4_1_1 IA_CSS_TRACE_NATIVE_4 +#define IA_CSS_TRACE_5_1_1 IA_CSS_TRACE_NATIVE_5 +#define IA_CSS_TRACE_6_1_1 IA_CSS_TRACE_NATIVE_6 +#define IA_CSS_TRACE_7_1_1 IA_CSS_TRACE_NATIVE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_1 "Assert" +#define IA_CSS_TRACE_SEVERITY_ERROR_1 "Error" +#define IA_CSS_TRACE_SEVERITY_WARNING_1 "Warning" +#define IA_CSS_TRACE_SEVERITY_INFO_1 "Info" +#define IA_CSS_TRACE_SEVERITY_DEBUG_1 "Debug" +#define IA_CSS_TRACE_SEVERITY_VERBOSE_1 "Verbose" + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_0(severity, module, format) +#define IA_CSS_TRACE_1_2_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_2_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_2_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_2_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_1 IA_CSS_TRACE_TRACE_0 +#define IA_CSS_TRACE_1_2_1 IA_CSS_TRACE_TRACE_1 +#define IA_CSS_TRACE_2_2_1 IA_CSS_TRACE_TRACE_2 +#define IA_CSS_TRACE_3_2_1 IA_CSS_TRACE_TRACE_3 +#define IA_CSS_TRACE_4_2_1 IA_CSS_TRACE_TRACE_4 +#define IA_CSS_TRACE_5_2_1 IA_CSS_TRACE_TRACE_5 +#define IA_CSS_TRACE_6_2_1 IA_CSS_TRACE_TRACE_6 +#define IA_CSS_TRACE_7_2_1 IA_CSS_TRACE_TRACE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_2 VIED_NCI_TUNIT_MSG_SEVERITY_FATAL +#define IA_CSS_TRACE_SEVERITY_ERROR_2 VIED_NCI_TUNIT_MSG_SEVERITY_ERROR +#define IA_CSS_TRACE_SEVERITY_WARNING_2 VIED_NCI_TUNIT_MSG_SEVERITY_WARNING +#define IA_CSS_TRACE_SEVERITY_INFO_2 VIED_NCI_TUNIT_MSG_SEVERITY_NORMAL +#define IA_CSS_TRACE_SEVERITY_DEBUG_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER1 +#define IA_CSS_TRACE_SEVERITY_VERBOSE_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER2 + +/* +** Dynamicism +*/ + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) \ + do { \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void); \ + } while (0) + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + void IA_CSS_TRACE_CAT(module, _trace_configure)\ + (int argc, const char *const *argv); \ + } while (0) + +#include "platform_support.h" +#include "type_support.h" + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_assert); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_error); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_warning); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_info); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_debug); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_verbose); \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 0; \ + } + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) \ +void IA_CSS_TRACE_CAT(module, _trace_configure)(const int argc, \ + const char *const *const argv) \ +{ \ + int i = 1; \ + const char *levels = 0; \ + \ + while (i < argc) { \ + if (!strcmp(argv[i], "-" #module "_trace")) { \ + ++i; \ + \ + if (i < argc) { \ + levels = argv[i]; \ + \ + while (*levels) { \ + switch (*levels++) { \ + case 'a': \ + IA_CSS_TRACE_CAT \ + (module, _trace_assert_enable)(); \ + break; \ + \ + case 'e': \ + IA_CSS_TRACE_CAT \ + (module, _trace_error_enable)(); \ + break; \ + \ + case 'w': \ + IA_CSS_TRACE_CAT \ + (module, _trace_warning_enable)(); \ + break; \ + \ + case 'i': \ + IA_CSS_TRACE_CAT \ + (module, _trace_info_enable)(); \ + break; \ + \ + case 'd': \ + IA_CSS_TRACE_CAT \ + (module, _trace_debug_enable)(); \ + break; \ + \ + case 'v': \ + IA_CSS_TRACE_CAT \ + (module, _trace_verbose_enable)(); \ + break; \ + \ + default: \ + } \ + } \ + } \ + } \ + \ + ++i; \ + } \ +} + +#endif /* __IA_CSS_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/trace.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/trace.mk new file mode 100644 index 000000000000..b232880b882b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/trace/trace.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE Trace + +# Dependencies +IA_CSS_TRACE_SUPPORT = $${MODULES_DIR}/support + +# API +IA_CSS_TRACE = $${MODULES_DIR}/trace +IA_CSS_TRACE_INTERFACE = $(IA_CSS_TRACE)/interface + +# +# Host +# + +# Host CPP Flags +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules + +# +# Firmware +# + +# Firmware CPP Flags +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/utils/system_defs/system_const.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/utils/system_defs/system_const.h new file mode 100644 index 000000000000..161f28fced97 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/utils/system_defs/system_const.h @@ -0,0 +1,26 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SYSTEM_CONST_H +#define __SYSTEM_CONST_H + +/* The values included in this file should have been + * taken from system/device properties which + * are not currently available in SDK + */ + +#define XMEM_WIDTH (512) +#define MG_PPC (4) + +#endif /* __SYSTEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_access.h new file mode 100755 index 000000000000..1e81bad9f4ee --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_access.h @@ -0,0 +1,139 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_ACCESS_H +#define _SHARED_MEMORY_ACCESS_H + +#include +#include +#include + +typedef enum { + sm_esuccess, + sm_enomem, + sm_ezeroalloc, + sm_ebadvaddr, + sm_einternalerror, + sm_ecorruption, + sm_enocontiguousmem, + sm_enolocmem, + sm_emultiplefree, +} shared_memory_error; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the VIED subsystem + */ +typedef uint32_t vied_virtual_address_t; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the host + */ +typedef unsigned long long host_virtual_address_t; + +/** + * \brief List of physical addresses of (DDR) shared memory space. This is used to represent a list of physical pages. + */ +typedef struct shared_memory_physical_page_list_s *shared_memory_physical_page_list; +typedef struct shared_memory_physical_page_list_s +{ + shared_memory_physical_page_list next; + vied_physical_address_t address; +}shared_memory_physical_page_list_s; + + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param idm: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(vied_memory_t idm, vied_physical_address_t host_ddr_addr, size_t memory_size, size_t ps); + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(vied_memory_t idm); + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. Returns NULL when insufficient memory available + */ +host_virtual_address_t shared_memory_alloc(vied_memory_t idm, size_t bytes); + +/** + * \brief Free (DDR) shared memory space. +*/ +void shared_memory_free(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Translate a virtual host.address to a physical address. +*/ +vied_physical_address_t shared_memory_virtual_host_to_physical_address (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Return the allocated physical pages for a virtual host.address. +*/ +shared_memory_physical_page_list shared_memory_virtual_host_to_physical_pages (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Destroy a shared_memory_physical_page_list. +*/ +void shared_memory_physical_pages_list_destroy (shared_memory_physical_page_list ppl); + +/** + * \brief Store a byte into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_8 (vied_memory_t idm, host_virtual_address_t addr, uint8_t data); + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_16(vied_memory_t idm, host_virtual_address_t addr, uint16_t data); + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_32(vied_memory_t idm, host_virtual_address_t addr, uint32_t data); + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store(vied_memory_t idm, host_virtual_address_t addr, const void *data, size_t bytes); + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host virtual address + */ +void shared_memory_zero(vied_memory_t idm, host_virtual_address_t addr, size_t bytes); + +/** + * \brief Load a byte from (DDR) shared memory space using a host virtual address + */ +uint8_t shared_memory_load_8 (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host virtual address + */ +uint16_t shared_memory_load_16(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host virtual address + */ +uint32_t shared_memory_load_32(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host virtual address + */ +void shared_memory_load(vied_memory_t idm, host_virtual_address_t addr, void *data, size_t bytes); + +#endif /* _SHARED_MEMORY_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_map.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_map.h new file mode 100755 index 000000000000..1bbedcf9e7fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/shared_memory_map.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_MAP_H +#define _SHARED_MEMORY_MAP_H + +#include +#include +#include + +typedef void (*shared_memory_invalidate_mmu_tlb)(void); +typedef void (*shared_memory_set_page_table_base_address)(vied_physical_address_t); + +typedef void (*shared_memory_invalidate_mmu_tlb_ssid)(vied_subsystem_t id); +typedef void (*shared_memory_set_page_table_base_address_ssid)(vied_subsystem_t id, vied_physical_address_t); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will NOT be taken into account. +*/ +int shared_memory_map_initialize(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb inv_tlb, shared_memory_set_page_table_base_address sbt); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will be taken into account. +*/ +int shared_memory_map_initialize_ssid(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb_ssid inv_tlb, shared_memory_set_page_table_base_address_ssid sbt); + +/** + * \brief De-initialize the CSS virtual address system and MMU. +*/ +void shared_memory_map_uninitialize(vied_subsystem_t id, vied_memory_t idm); + +/** + * \brief Convert a host virtual address to a CSS virtual address and update the MMU. +*/ +vied_virtual_address_t shared_memory_map(vied_subsystem_t id, vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Free a CSS virtual address and update the MMU. +*/ +void shared_memory_unmap(vied_subsystem_t id, vied_memory_t idm, vied_virtual_address_t addr); + + +#endif /* _SHARED_MEMORY_MAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_config.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_config.h new file mode 100755 index 000000000000..912f016ead24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_config.h @@ -0,0 +1,33 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_CONFIG_H +#define _HRT_VIED_CONFIG_H + +/* Defines from the compiler: + * HRT_HOST - this is code running on the host + * HRT_CELL - this is code running on a cell + */ +#ifdef HRT_HOST +# define CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL 1 +# undef CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL + +#elif defined (HRT_CELL) +# undef CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +# define CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL 1 + +#else /* !HRT_CELL */ +/* Allow neither HRT_HOST nor HRT_CELL for testing purposes */ +#endif /* !HRT_CELL */ + +#endif /* _HRT_VIED_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_memory_access_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_memory_access_types.h new file mode 100755 index 000000000000..0b44492789e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_memory_access_types.h @@ -0,0 +1,36 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_MEMORY_ACCESS_TYPES_H +#define _HRT_VIED_MEMORY_ACCESS_TYPES_H + +/** Types for the VIED memory access interface */ + +#include "vied_types.h" + +/** + * \brief An identifier for a system memory. + * + * This identifier must be a compile-time constant. It is used in + * access to system memory. + */ +typedef unsigned int vied_memory_t; + +#ifndef __HIVECC +/** + * \brief The type for a physical address + */ +typedef unsigned long long vied_physical_address_t; +#endif + +#endif /* _HRT_VIED_MEMORY_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access.h new file mode 100755 index 000000000000..674f5fb5b0f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access.h @@ -0,0 +1,70 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_H + +#include +#include "vied_config.h" +#include "vied_subsystem_access_types.h" + +#if !defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) && \ + !defined(CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL) +#error Implementation selection macro for vied subsystem access not defined +#endif + +#if defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) +#ifndef __HIVECC +#error "Inline implementation of subsystem access not supported for host" +#endif +#define _VIED_SUBSYSTEM_ACCESS_INLINE static __inline +#include "vied_subsystem_access_impl.h" +#else +#define _VIED_SUBSYSTEM_ACCESS_INLINE +#endif + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr, uint8_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_16(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint16_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_32(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint32_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store(vied_subsystem_t dev, + vied_subsystem_address_t addr, + const void *data, unsigned int size); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint8_t vied_subsystem_load_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint16_t vied_subsystem_load_16(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint32_t vied_subsystem_load_32(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_load(vied_subsystem_t dev, + vied_subsystem_address_t addr, + void *data, unsigned int size); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_initialization.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_initialization.h new file mode 100755 index 000000000000..81f4d08d5ae0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_initialization.h @@ -0,0 +1,44 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H + +#include "vied_subsystem_access_types.h" + +/** @brief Initialises the access of a subsystem. + * @param[in] system The subsystem for which the access has to be initialised. + * + * vied_subsystem_access_initialize initilalises the access a subsystem. + * It sets the base address of the subsystem. This base address is extracted from the hsd file. + * + */ +void +vied_subsystem_access_initialize(vied_subsystem_t system); + + +/** @brief Initialises the access of multiple subsystems. + * @param[in] nr _subsystems The number of subsystems for which the access has to be initialised. + * @param[in] dev_base_addresses A pointer to an array of base addresses of subsystems. + * The size of this array must be "nr_subsystems". + * This array must be available during the accesses of the subsystem. + * + * vied_subsystems_access_initialize initilalises the access to multiple subsystems. + * It sets the base addresses of the subsystems that are provided by the array dev_base_addresses. + * + */ +void +vied_subsystems_access_initialize( unsigned int nr_subsystems + , const vied_subsystem_base_address_t *base_addresses); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_types.h new file mode 100755 index 000000000000..75fef6c4ddba --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_subsystem_access_types.h @@ -0,0 +1,34 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H + +/** Types for the VIED subsystem access interface */ +#include + +/** \brief An identifier for a VIED subsystem. + * + * This identifier must be a compile-time constant. It is used in + * access to a VIED subsystem. + */ +typedef unsigned int vied_subsystem_t; + + +/** \brief An address within a VIED subsystem */ +typedef uint32_t vied_subsystem_address_t; + +/** \brief A base address of a VIED subsystem seen from the host */ +typedef unsigned long long vied_subsystem_base_address_t; + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_types.h new file mode 100755 index 000000000000..0acfdbb00cfa --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600/vied/vied/vied_types.h @@ -0,0 +1,45 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_TYPES_H +#define _HRT_VIED_TYPES_H + +/** Types shared by VIED interfaces */ + +#include + +/** \brief An address within a VIED subsystem + * + * This will eventually replace teh vied_memory_address_t and vied_subsystem_address_t + */ +typedef uint32_t vied_address_t; + +/** \brief Memory address type + * + * A memory address is an offset within a memory. + */ +typedef uint32_t vied_memory_address_t; + +/** \brief Master port id */ +typedef int vied_master_port_id_t; + +/** + * \brief Require the existence of a certain type + * + * This macro can be used in interface header files to ensure that + * an implementation define type with a specified name exists. + */ +#define _VIED_REQUIRE_TYPE(T) enum { _VIED_SIZEOF_##T = sizeof(T) } + + +#endif /* _HRT_VIED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/Makefile b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/Makefile new file mode 100644 index 000000000000..d15887320a3d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/Makefile @@ -0,0 +1,49 @@ +# +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# + +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +include $(srcpath)/$(src)/../Makefile.ipu4psys_src +include $(srcpath)/$(src)/../Makefile.ipu4psys_inc + +SSID = 0 +MMID = 0 + +IPU_PSYSLIB_ROOT_REL = lib +IPU_PSYSLIB_ROOT = $(srcpath)/$(src)/$(IPU_PSYSLIB_ROOT_REL) + +ccflags-y += -I$(srcpath)/$(src)/../../../ +ccflags-y += -I$(srcpath)/$(src)/../../ +ccflags-y += -DHAS_DUAL_CMD_CTX_SUPPORT=0 -DHAS_LATE_BINDING_SUPPORT=0 -DIPU_PSYS_LEGACY + +IPU_PSYSLIB_SRC += libcsspsys2600.o + +#CFLAGS = -W -Wall -Wstrict-prototypes -Wmissing-prototypes -O2 -fomit-frame-pointer -Wno-unused-variable +HOST_DEFINES += -DSSID=$(SSID) +HOST_DEFINES += -DMMID=$(MMID) +HOST_DEFINES += -DHRT_ON_VIED_SUBSYSTEM_ACCESS=$(SSID) +HOST_DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +HOST_DEFINES += -DHRT_USE_VIR_ADDRS +HOST_DEFINES += -DHRT_HW +HOST_DEFINES += -DVIED_NCI_TUNIT_PSYS +HOST_DEFINES += -DFIRMWARE_RELEASE_VERSION +HOST_DEFINES += -DPSYS_SERVER_ON_SPC +HOST_DEFINES += -DAPI_SPLIT_START_STATE_UPDATE + +intel-ipu4-psys-csslib-objs := ../../../ipu-wrapper.o \ + $(IPU_PSYSLIB_SRC) + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-psys-csslib.o +ccflags-y += $(IPU_PSYSLIB_INC) $(HOST_DEFINES) -fno-common -v diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/DSS_V2_program_group/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/DSS_V2_program_group/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/DSS_V2_program_group/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/buffer.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/buffer.mk new file mode 100644 index 000000000000..c00a1133b440 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/buffer.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is BUFFER + +ifdef _H_BUFFER_MK +$(error ERROR: buffer.mk included multiple times, please check makefile) +else +_H_BUFFER_MK=1 +endif + +BUFFER_DIR=$${MODULES_DIR}/buffer + +BUFFER_INTERFACE=$(BUFFER_DIR)/interface +BUFFER_SOURCES_CPU=$(BUFFER_DIR)/src/cpu +BUFFER_SOURCES_CSS=$(BUFFER_DIR)/src/css + +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_output_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_input_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_shared_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/buffer_access.c +BUFFER_HOST_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_HOST_CPPFLAGS += -I$${MODULES_DIR}/support + +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_input_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_output_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_shared_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/buffer_access.c + +BUFFER_FW_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_FW_CPPFLAGS += -I$${MODULES_DIR}/support diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_access.h new file mode 100644 index 000000000000..e5fe647742c9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_access.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_ACCESS_H +#define __BUFFER_ACCESS_H + +#include "buffer_type.h" +/* #def to keep consistent the buffer load interfaces for host and css */ +#define IDM 0 + +void +buffer_load( + buffer_address address, + void *data, + unsigned int size, + unsigned int mm_id); + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int size, + unsigned int mm_id); + +#endif /* __BUFFER_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_type.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_type.h new file mode 100644 index 000000000000..de51f2394158 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/buffer_type.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_TYPE_H +#define __BUFFER_TYPE_H + +/* portable access to buffers in DDR */ + +#ifdef __VIED_CELL +typedef unsigned int buffer_address; +#else +/* workaround needed because shared_memory_access.h uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_access.h" +typedef host_virtual_address_t buffer_address; +#endif + +#endif /* __BUFFER_TYPE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h new file mode 100644 index 000000000000..137bfb1fda16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_ADDRESS_H +#define __IA_CSS_BUFFER_ADDRESS_H + +#include "type_support.h" + +typedef uint32_t ia_css_buffer_address; /* CSS virtual address */ + +#define ia_css_buffer_address_null ((ia_css_buffer_address)0) + +#endif /* __IA_CSS_BUFFER_ADDRESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h new file mode 100644 index 000000000000..4e92e35b6184 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_H +#define __IA_CSS_INPUT_BUFFER_H + + +/* Input Buffers */ + +/* A CSS input buffer is a buffer in DDR that can be written by the CPU, + * and that can be read by CSS hardware, after the buffer has been handed over. + * Examples: command buffer, input frame buffer, parameter buffer + * An input buffer must be mapped into the CPU address space before it can be + * written by the CPU. + * After mapping, writing, and unmapping, the buffer can be handed over to the + * firmware. An input buffer is handed over to the CSS by mapping it to the + * CSS address space (by the CPU), and by passing the resulting CSS (virtial) + * address of the input buffer to the DA CSS hardware. + * The firmware can read from an input buffer as soon as it has been received + * CSS virtual address. + * The firmware should not write into an input buffer. + * The firmware hands over the input buffer (back to the CPU) by sending the + * buffer handle via a response. The host should unmap the buffer, + * before reusing it. + * The firmware should not read from the input buffer after returning the + * buffer handle to the CPU. + * + * A buffer may be pre-mapped to the CPU and/or to the CSS upon allocation, + * depending on the allocator's preference. In case of pre-mapped buffers, + * the map and unmap functions will only manage read and write access. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_input_buffer; /* input buffer handle */ +typedef void *ia_css_input_buffer_cpu_address; /* CPU virtual address */ +/* CSS virtual address */ +typedef ia_css_buffer_address ia_css_input_buffer_css_address; + +#endif /* __IA_CSS_INPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h new file mode 100644 index 000000000000..d3d01353ce43 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_CPU_H +#define __IA_CSS_INPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_input_buffer.h" + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b); + + +#endif /* __IA_CSS_INPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h new file mode 100644 index 000000000000..2c310ea92c6a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h @@ -0,0 +1,30 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_H +#define __IA_CSS_OUTPUT_BUFFER_H + +/* Output Buffers */ +/* A CSS output buffer a buffer in DDR that can be written by CSS hardware + * and that can be read by the host, after the buffer has been handed over + * Examples: output frame buffer + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_output_buffer; +typedef void *ia_css_output_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_output_buffer_css_address; + +#endif /* __IA_CSS_OUTPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h new file mode 100644 index 000000000000..0299fc3b7eb6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h @@ -0,0 +1,48 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_CPU_H +#define __IA_CSS_OUTPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_output_buffer.h" + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b); +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b); + + +#endif /* __IA_CSS_OUTPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h new file mode 100644 index 000000000000..558ec679f98a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_H +#define __IA_CSS_SHARED_BUFFER_H + +/* Shared Buffers */ +/* A CSS shared buffer is a buffer in DDR that can be read and written by the + * CPU and CSS. + * Both the CPU and CSS can have the buffer mapped simultaneously. + * Access rights are not managed by this interface, this could be done by means + * the read and write pointer of a queue, for example. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_shared_buffer; +typedef void *ia_css_shared_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_shared_buffer_css_address; + +#endif /* __IA_CSS_SHARED_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h new file mode 100644 index 000000000000..ff62914f99dc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_CPU_H +#define __IA_CSS_SHARED_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_shared_buffer.h" + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b); + +#endif /* __IA_CSS_SHARED_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c new file mode 100644 index 000000000000..f0c617fe501a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* implementation of buffer access from the CPU */ +/* using shared_memory interface */ + +#include "buffer_access.h" +#include "vied/shared_memory_access.h" + +void +buffer_load( + buffer_address address, + void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_load(mm_id, address, data, bytes); +} + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_store(mm_id, address, data, bytes); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c new file mode 100644 index 000000000000..146d4109de44 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* provided interface */ +#include "ia_css_buffer.h" + +/* used interfaces */ +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + +ia_css_buffer_t +ia_css_buffer_alloc(vied_subsystem_t sid, vied_memory_t mid, unsigned int size) +{ + ia_css_buffer_t b; + + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + + b->css_address = shared_memory_map(sid, mid, b->mem); + b->size = size; + return b; +} + + +void +ia_css_buffer_free(vied_subsystem_t sid, vied_memory_t mid, ia_css_buffer_t b) +{ + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h new file mode 100644 index 000000000000..0f99a06e9a89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_H +#define __IA_CSS_BUFFER_H + +/* workaround: needed because uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_map.h" + +typedef enum { + buffer_unmapped, /* buffer is not accessible by cpu, nor css */ + buffer_write, /* output buffer: css has write access */ + /* input buffer: cpu has write access */ + buffer_read, /* input buffer: css has read access */ + /* output buffer: cpu has read access */ + buffer_cpu, /* shared buffer: cpu has read/write access */ + buffer_css /* shared buffer: css has read/write access */ +} buffer_state; + +struct ia_css_buffer_s { + /* number of bytes bytes allocated */ + unsigned int size; + /* allocated virtual memory object */ + host_virtual_address_t mem; + /* virtual address to be used on css/firmware */ + vied_virtual_address_t css_address; + /* virtual address to be used on cpu/host */ + void *cpu_address; + buffer_state state; +}; + +typedef struct ia_css_buffer_s *ia_css_buffer_t; + +ia_css_buffer_t +ia_css_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_buffer_t b); + +#endif /* __IA_CSS_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c new file mode 100644 index 000000000000..2a128795d03e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c @@ -0,0 +1,184 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_input_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_input_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map input buffer to CPU address space, acquire write access */ + b->state = buffer_write; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_write) + return NULL; + + /* unmap input buffer from CPU address space, release write access */ + b->state = buffer_unmapped; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + + /* now flush the cache */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_read) + return 0; + + /* unmap input buffer from CSS address space, release read access */ + b->state = buffer_unmapped; + + /* input buffer only, no need to invalidate cache */ + + return (ia_css_input_buffer_css_address)b->css_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c new file mode 100644 index 000000000000..30bc8d52a5a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c @@ -0,0 +1,181 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_output_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_output_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map output buffer to CSS address space, acquire write access */ + b->state = buffer_write; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_write) + return 0; + + /* unmap output buffer from CSS address space, release write access */ + b->state = buffer_unmapped; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* now invalidate the cache */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b->cpu_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b->cpu_address; +} + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_read) + return NULL; + + /* unmap output buffer from CPU address space, release read access */ + b->state = buffer_unmapped; + + /* output only, no need to flush cache */ + + return b->cpu_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c new file mode 100644 index 000000000000..92b7110644fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c @@ -0,0 +1,187 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_shared_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_shared_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map shared buffer to CPU address space */ + b->state = buffer_cpu; + + return b->cpu_address; +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_cpu) + return NULL; + + /* unmap shared buffer from CPU address space */ + b->state = buffer_unmapped; + + return b->cpu_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map shared buffer to CSS address space */ + b->state = buffer_css; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_css) + return 0; + + /* unmap shared buffer from CSS address space */ + b->state = buffer_unmapped; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to CSS after it was modified by the CPU */ + /* flush cache to ddr */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to the CPU after it has been modified by CSS */ +#ifndef HRT_HW + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* flush cache to ddr */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/cell.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/cell.mk new file mode 100644 index 000000000000..fa5e65022601 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/cell.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef _CELL_MK_ +_CELL_MK_ = 1 + + +CELL_DIR=$${MODULES_DIR}/cell +CELL_INTERFACE=$(CELL_DIR)/interface +CELL_SOURCES=$(CELL_DIR)/src + +CELL_HOST_FILES = +CELL_FW_FILES = + +CELL_HOST_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +CELL_FW_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +ifdef 0 +# Disabled until it is decided to go this way or not +include $(MODULES_DIR)/device_access/device_access.mk +CELL_HOST_FILES += $(DEVICE_ACCESS_HOST_FILES) +CELL_FW_FILES += $(DEVICE_ACCESS_FW_FILES) +CELL_HOST_CPPFLAGS += $(DEVICE_ACCESS_HOST_CPPFLAGS) +CELL_FW_CPPFLAGS += $(DEVICE_ACCESS_FW_CPPFLAGS) +endif + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/interface/ia_css_cell.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/interface/ia_css_cell.h new file mode 100644 index 000000000000..3fac3c791b6e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/interface/ia_css_cell.h @@ -0,0 +1,112 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_H +#define __IA_CSS_CELL_H + +#include "storage_class.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +#if 0 /* To be implemented after completing cell device properties */ +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_debug_pc(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stall_bits(unsigned int ssid, unsigned int cell_id); +#endif + +/* configure master ports */ + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value); + +/* Access memories */ + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr); + +/***********************************************************************/ + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch); + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id); + +/* include inline implementation */ +#include "ia_css_cell_impl.h" + +#endif /* __IA_CSS_CELL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h new file mode 100644 index 000000000000..60b2e234da1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h @@ -0,0 +1,272 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_IMPL_H +#define __IA_CSS_CELL_IMPL_H + +#include "ia_css_cell.h" + +#include "ia_css_cmem.h" +#include "ipu_device_cell_properties.h" +#include "storage_class.h" +#include "assert_support.h" +#include "platform_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_regs_addr(unsigned int cell_id) +{ + /* mem_id 0 is for registers */ + return ipu_device_cell_memory_address(cell_id, 0); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_dmem_addr(unsigned int cell_id) +{ + /* mem_id 1 is for DMEM */ + return ipu_device_cell_memory_address(cell_id, 1); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ipu_device_cell_memory_address( + cell_id, mem_id) + addr, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr) +{ + return ia_css_cmem_load_32( + ssid, ipu_device_cell_memory_address(cell_id, mem_id) + addr); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id) +{ + return ia_css_cmem_load_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = ia_css_cell_get_stat_ctrl(ssid, cell_id); + /* READY must be 1, START must be 0 */ + return (reg & (1 << IPU_DEVICE_CELL_STAT_CTRL_READY_BIT)) && + ((~reg) & (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT)); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc) +{ + /* set start PC */ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_START_PC_REG_ADDRESS, pc); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = 1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + unsigned int reg; + + reg = value << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id) +{ + ia_css_cell_start_prefetch(ssid, cell_id, 0); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch) +{ + unsigned int reg = 0; + + /* Set run bit and start bit */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT); + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT); + /* Invalidate the icache */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); + /* Optionally enable prefetching */ + reg |= ((prefetch == 1) ? + (1 << IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT) : + 0); + + /* store into register */ + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id) +{ + do { + ia_css_sleep(); + } while (!ia_css_cell_is_ready(ssid, cell_id)); +}; + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS, value); +} + +/* master port configuration */ + + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) + +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments, segment_size; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + segment_size = ipu_device_cell_master_segment_size(cell, master); + + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + value += segment_size; + } +} + +#endif /* __IA_CSS_CELL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h new file mode 100644 index 000000000000..e8b0a48b27e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h @@ -0,0 +1,60 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_H +#define __IA_CSS_CLIENT_PKG_H + +#include "type_support.h" +#include "ia_css_client_pkg_storage_class.h" +/* for ia_css_client_pkg_header_s (ptr only), ia_css_client_pkg_t */ +#include "ia_css_client_pkg_types.h" + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_pg_manifest_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_list_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_desc_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_bin_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_indexed_prog_desc_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t program_index, + uint32_t *offset, + uint32_t *size); + +#ifdef __INLINE_CLIENT_PKG__ +#include "ia_css_client_pkg_impl.h" +#endif + +#endif /* __IA_CSS_CLIENT_PKG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h new file mode 100644 index 000000000000..98af98d5d824 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +#define __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __INLINE_CLIENT_PKG__ +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +#else +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h new file mode 100644 index 000000000000..ff5bf01358f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h @@ -0,0 +1,44 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_TYPES_H +#define __IA_CSS_CLIENT_PKG_TYPES_H + +#include "type_support.h" + +typedef void ia_css_client_pkg_t; + +struct ia_css_client_pkg_header_s { + uint32_t prog_list_offset; + uint32_t prog_list_size; + uint32_t prog_desc_offset; + uint32_t prog_desc_size; + uint32_t pg_manifest_offset; + uint32_t pg_manifest_size; + uint32_t prog_bin_offset; + uint32_t prog_bin_size; +}; + +struct ia_css_client_pkg_prog_s { + uint32_t prog_id; + uint32_t prog_offset; + uint32_t prog_size; +}; + +struct ia_css_client_pkg_prog_list_s { + uint32_t prog_desc_count; + uint32_t prog_bin_count; +}; + +#endif /* __IA_CSS_CLIENT_PKG_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c new file mode 100644 index 000000000000..0b2fd86d09f3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_CLIENT_PKG__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_client_pkg_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_CLIENT_PKG__ */ +#include "ia_css_client_pkg_impl.h" +#endif /* __INLINE_CLIENT_PKG__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h new file mode 100644 index 000000000000..b79e5de02b89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h @@ -0,0 +1,161 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_IMPL_H +#define __IA_CSS_CLIENT_PKG_IMPL_H + +#include "ia_css_client_pkg.h" +#include "ia_css_client_pkg_types.h" +#include "error_support.h" + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_pg_manifest_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->pg_manifest_offset; + *(size) = client_pkg_header->pg_manifest_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_list_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->prog_list_offset; + *(size) = client_pkg_header->prog_list_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_desc_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->prog_desc_offset; + *(size) = client_pkg_header->prog_desc_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_bin_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t *offset, + uint32_t *size) +{ + uint8_t i; + int ret_val = -1; + struct ia_css_client_pkg_header_s *client_pkg_header = NULL; + const struct ia_css_client_pkg_prog_list_s *pkg_prog_list = NULL; + const struct ia_css_client_pkg_prog_s *pkg_prog_bin_entry = NULL; + + verifjmpexit(NULL != client_pkg); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + client_pkg_header = + (struct ia_css_client_pkg_header_s *)((uint8_t *)client_pkg); + pkg_prog_list = + (struct ia_css_client_pkg_prog_list_s *)((uint8_t *)client_pkg + + client_pkg_header->prog_list_offset); + pkg_prog_bin_entry = + (struct ia_css_client_pkg_prog_s *)((uint8_t *)pkg_prog_list + + sizeof(struct ia_css_client_pkg_prog_list_s)); + pkg_prog_bin_entry += pkg_prog_list->prog_desc_count; + + for (i = 0; i < pkg_prog_list->prog_bin_count; i++) { + if (program_id == pkg_prog_bin_entry->prog_id) { + *(offset) = pkg_prog_bin_entry->prog_offset; + *(size) = pkg_prog_bin_entry->prog_size; + ret_val = 0; + break; + } else if (0 == pkg_prog_bin_entry->prog_size) { + /* We can have a variable number of program descriptors. + * The first non-valid one will have size set to 0 + */ + break; + } + pkg_prog_bin_entry++; + } +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_indexed_prog_desc_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t program_index, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + struct ia_css_client_pkg_header_s *client_pkg_header = NULL; + const struct ia_css_client_pkg_prog_list_s *pkg_prog_list = NULL; + const struct ia_css_client_pkg_prog_s *pkg_prog_desc_entry = NULL; + + verifjmpexit(NULL != client_pkg); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + client_pkg_header = + (struct ia_css_client_pkg_header_s *)((uint8_t *)client_pkg); + pkg_prog_list = + (struct ia_css_client_pkg_prog_list_s *)((uint8_t *)client_pkg + + client_pkg_header->prog_list_offset); + pkg_prog_desc_entry = + (struct ia_css_client_pkg_prog_s *)((uint8_t *)pkg_prog_list + + sizeof(struct ia_css_client_pkg_prog_list_s)); + + verifjmpexit(program_index < pkg_prog_list->prog_desc_count); + verifjmpexit(program_id == pkg_prog_desc_entry[program_index].prog_id); + verifjmpexit(pkg_prog_desc_entry[program_index].prog_size > 0); + *(offset) = pkg_prog_desc_entry[program_index].prog_offset; + *(size) = pkg_prog_desc_entry[program_index].prog_size; + ret_val = 0; + +EXIT: + return ret_val; +} + +#endif /* __IA_CSS_CLIENT_PKG_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/psys/subsystem_bxtB0.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/psys/subsystem_bxtB0.mk new file mode 100644 index 000000000000..2f60853f0089 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/psys/subsystem_bxtB0.mk @@ -0,0 +1,109 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +############################################################################ +# This file is used to specify versions and properties of PSYS firmware +# components. Please note that these are subsystem specific. System specific +# properties should go to system_$IPU_SYSVER.mk. Also the device versions +# should be defined under "devices" or should be taken from the SDK. +############################################################################ + +# Activate loading params and storing stats DDR<->REGs with DMA +PSYS_USE_ISA_DMA = 1 + +# Used in ISA module +PSYS_ISL_DPC_DPC_V2 = 0 + +# Assume OFS will be running concurrently with IPF, and prioritize according to rates of services on devproxy +CONCURRENT_OFS_IPF_PRIORITY_OPTIMIZATION_ENABLED = 1 + +# Use the DMA for terminal loading in Psys server +PSYS_SERVER_ENABLE_TERMINAL_LOAD_DMA = 1 + +HAS_GMEM = 1 +# use DMA NCI for OFS Service to reduce load in tproxy +DMA_NCI_IN_OFS_SERVICE = 1 + +# See HSD 1805169230 +HAS_FWDMA_ALIGNMENT_ISSUE_SIGHTING = 1 + +HAS_SPC = 1 +HAS_SPP0 = 1 +HAS_SPP1 = 1 +HAS_ISP0 = 1 +HAS_ISP1 = 1 +HAS_ISP2 = 1 +HAS_ISP3 = 1 + +# Specification for Psys server's fixed globals' locations +REGMEM_OFFSET = 0 # Starting from 0 +REGMEM_SIZE = 18 +REGMEM_WORD_BYTES = 4 +REGMEM_SIZE_BYTES = 72 +GPC_ISP_PERF_DATA_OFFSET = 72 # Taken from REGMEM_OFFSET + REGMEM_SIZE_BYTES +GPC_ISP_PERF_DATA_SIZE_BYTES = 80 +FW_LOAD_NO_OF_REQUEST_OFFSET = 152 # Taken from GPC_ISP_PERF_DATA_OFFSET + GPC_ISP_PERF_DATA_SIZE_BYTES +FW_LOAD_NO_OF_REQUEST_SIZE_BYTES = 4 +DISPATCHER_SCRATCH_SPACE_OFFSET = 156 # Taken from FW_LOAD_NO_OF_REQUEST_OFFSET + FW_LOAD_NO_OF_REQUEST_SIZE_BYTES + +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PSYS_SERVER_MANIFEST_VERSION = bxtB0 +PSYS_RESOURCE_MODEL_VERSION = bxtB0 +PSYS_ACCESS_BLOCKER_VERSION = v1 + +# Disable support for PPG protocol to save codesize +PSYS_HAS_PPG_SUPPORT = 0 +# Disable support for late binding +PSYS_HAS_LATE_BINDING_SUPPORT = 0 + +# Specify PSYS server context spaces for caching context from DDR +PSYS_SERVER_NOF_CACHES = 4 +PSYS_SERVER_MAX_NUM_PROC_GRP = $(PSYS_SERVER_NOF_CACHES) +PSYS_SERVER_MAX_NUM_EXEC_PROC_GRP = 8 # Max PG's running, 4 running on Cores, 4 being updated on the host upon executing. +PSYS_SERVER_MAX_PROC_GRP_SIZE = 4052 +PSYS_SERVER_MAX_MANIFEST_SIZE = 3732 +PSYS_SERVER_MAX_CLIENT_PKG_SIZE = 2420 +PSYS_SERVER_MAX_BUFFER_SET_SIZE = 0 +PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_SECTIONS = 88 +PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS = 1 +# The caching scheme for this subsystem suits the method of queueing ahead separate PGs for frames in an interleaved +# fashion. As such there should be as many caches to support to heaviest two concurrent PGs, times two. This results +# in the following distribution of caches: two large ones for the maximum sized PG, two smaller ones for the +# second-largest sized PG. +PSYS_SERVER_CACHE_0_PROC_GRP_SIZE = $(PSYS_SERVER_MAX_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_0_MANIFEST_SIZE = $(PSYS_SERVER_MAX_MANIFEST_SIZE) +PSYS_SERVER_CACHE_0_CLIENT_PKG_SIZE = $(PSYS_SERVER_MAX_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_0_BUFFER_SET_SIZE = $(PSYS_SERVER_MAX_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +PSYS_SERVER_CACHE_1_PROC_GRP_SIZE = $(PSYS_SERVER_CACHE_0_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_1_MANIFEST_SIZE = $(PSYS_SERVER_CACHE_0_MANIFEST_SIZE) +PSYS_SERVER_CACHE_1_CLIENT_PKG_SIZE = $(PSYS_SERVER_CACHE_0_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_1_BUFFER_SET_SIZE = $(PSYS_SERVER_CACHE_0_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_1_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_1_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +PSYS_SERVER_CACHE_2_PROC_GRP_SIZE = 1800 +PSYS_SERVER_CACHE_2_MANIFEST_SIZE = 2344 +PSYS_SERVER_CACHE_2_CLIENT_PKG_SIZE = 1240 +PSYS_SERVER_CACHE_2_BUFFER_SET_SIZE = 0 +PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_SECTIONS = 45 +PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) + +PSYS_SERVER_CACHE_3_PROC_GRP_SIZE = $(PSYS_SERVER_CACHE_2_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_3_MANIFEST_SIZE = $(PSYS_SERVER_CACHE_2_MANIFEST_SIZE) +PSYS_SERVER_CACHE_3_CLIENT_PKG_SIZE = $(PSYS_SERVER_CACHE_2_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_3_BUFFER_SET_SIZE = $(PSYS_SERVER_CACHE_2_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_3_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_3_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/system_bxtB0.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/system_bxtB0.mk new file mode 100644 index 000000000000..24d079b40516 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/config/system_bxtB0.mk @@ -0,0 +1,88 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +LOGICAL_FW_INPUT_SYSTEM = input_system_system +LOGICAL_FW_PROCESSING_SYSTEM = processing_system_system +LOGICAL_FW_IPU_SYSTEM = css_broxton_system +LOGICAL_FW_ISP_SYSTEM = isp2601_default_system +SP_CONTROL_CELL = sp2601_control +SP_PROXY_CELL = sp2601_proxy +SP_FP_CELL = sp2601_fp +ISP_CELL = isp2601 +# The non-capital define isp2601 is used in the sdk, in order to distinguish +# between different isp versions the ISP_CELL_IDENTIFIER define is added. +ISP_CELL_IDENTIFIER = ISP2601 +HAS_IPFD = 1 +HAS_S2M_IN_ISYS_ISL_NONSOC_PATH = 0 +HAS_S2V_IN_ISYS_ISL_NONSOC_PATH = 1 +# ISL-IS non-SoC path has ISA without PAF and DPC-Pext support for IPU4-B0 +HAS_ISA_IN_ISYS_ISL = 1 +HAS_PAF_IN_ISYS_ISL = 0 +HAS_DPC_PEXT_IN_ISYS_ISL = 0 +HAS_PMA_IF = 0 + +HAS_MIPIBE_IN_PSYS_ISL = 1 + +HAS_VPLESS_SUPPORT = 0 + +DLI_SYSTEM = hive_isp_css_2600_system +RESOURCE_MANAGER_VERSION = v1 +MEM_RESOURCE_VALIDATION_ERROR = 0 +OFS_SCALER_1_4K_TILEY_422_SUPPORT= 1 +PROGDESC_ACC_SYMBOLS_VERSION = v1 +DEVPROXY_INTERFACE_VERSION = v1 +FW_ABI_IPU_TYPES_VERSION = v1 + +HAS_ONLINE_MODE_SUPPORT_IN_ISYS_PSYS = 0 + +MMU_INTERFACE_VERSION = v1 +DEVICE_ACCESS_VERSION = v2 +PSYS_SERVER_VERSION = v2 +PSYS_SERVER_LOADER_VERSION = v1 +PSYS_HW_VERSION = BXT_B0_HW + +# Enable FW_DMA for loading firmware +PSYS_SERVER_ENABLE_FW_LOAD_DMA = 1 + +NCI_SPA_VERSION = v1 +MANIFEST_TOOL_VERSION = v2 +PSYS_CON_MGR_TOOL_VERSION = v1 +# TODO: Should be removed after performance issues OTF are solved +PSYS_PROC_MGR_VERSION = v1 +IPU_RESOURCES_VERSION = v1 + +HAS_ACC_CLUSTER_PAF_PAL = 0 +HAS_ACC_CLUSTER_PEXT_PAL = 0 +HAS_ACC_CLUSTER_GBL_PAL = 1 + +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PARAMBINTOOL_ISA_INIT_VERSION = bxtB0 + +# Select EQC2EQ version +# Version 1: uniform address space, equal EQ addresses regardless of EQC device +# Version 2: multiple addresses per EQ, depending on location of EQC device +EQC2EQ_VERSION = v1 + +# Select DMA instance for fw_load +FW_LOAD_DMA_INSTANCE = NCI_DMA_FW + +HAS_DMA_FW = 1 + +HAS_SIS = 0 +HAS_IDS = 1 + +PSYS_SERVER_ENABLE_TPROXY = 1 +PSYS_SERVER_ENABLE_DEVPROXY = 1 +NCI_OFS_VERSION = v1 diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk new file mode 100644 index 000000000000..8ecc3e42e55d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk @@ -0,0 +1,28 @@ +## +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +## + +# MODULE is cpd/cpd_component + +CPD_DIR = $${MODULES_DIR}/cpd +CPD_COMPONENT_DIR = $${MODULES_DIR}/cpd/cpd_component +CPD_COMPONENT_INTERFACE = $(CPD_COMPONENT_DIR)/interface +CPD_COMPONENT_SOURCES = $(CPD_COMPONENT_DIR)/src + +CPD_COMPONENT_FILES = $(CPD_COMPONENT_SOURCES)/ia_css_cpd_component_create.c +CPD_COMPONENT_FILES += $(CPD_COMPONENT_SOURCES)/ia_css_cpd_component.c +CPD_COMPONENT_CPPFLAGS = -I$(CPD_COMPONENT_INTERFACE) +CPD_COMPONENT_CPPFLAGS += -I$(CPD_COMPONENT_SOURCES) +CPD_COMPONENT_CPPFLAGS += -I$(CPD_DIR) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h new file mode 100644 index 000000000000..7ad3070b2fd7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h @@ -0,0 +1,90 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_CPD_COMPONENT_TYPES_H +#define __IA_CSS_CPD_COMPONENT_TYPES_H + +/** @file + * This file contains datastructure related to generation of CPD file + */ + +#include "type_support.h" + +#define SIZE_OF_FW_ARCH_VERSION 7 +#define SIZE_OF_SYSTEM_VERSION 11 +#define SIZE_OF_COMPONENT_NAME 12 + +enum ia_css_cpd_component_endianness { + IA_CSSCPD_COMP_ENDIAN_RSVD, + IA_CSS_CPD_COMP_LITTLE_ENDIAN, + IA_CSS_CPD_COMP_BIG_ENDIAN +}; + +/** Module Data (components) Header + * Following data structure has been created using FAS section 5.25 + * Open : Should we add padding at the end of module directory + * (the component must be 512 aligned) + */ +typedef struct { + uint32_t header_size; + /**< Specifies endianness of the binary data */ + unsigned int endianness; + /**< fw_pkg_date is current date stored in 'binary decimal' + * representation e.g. 538248729 (0x20150619) + */ + uint32_t fw_pkg_date; + /**< hive_sdk_date is date of HIVE_SDK stored in + * 'binary decimal' representation + */ + uint32_t hive_sdk_date; + /**< compiler_date is date of ptools stored in + * 'binary decimal' representation + */ + uint32_t compiler_date; + /**< UNSCHED / SCHED / TARGET / CRUN */ + unsigned int target_platform_type; + /**< specifies the system version stored as string + * e.g. BXTB0_IPU4'\0' + */ + uint8_t system_version[SIZE_OF_SYSTEM_VERSION]; + /**< specifies fw architecture version e.g. for BXT CSS3.0'\0' */ + uint8_t fw_arch_version[SIZE_OF_FW_ARCH_VERSION]; + uint8_t rsvd[2]; +} ia_css_header_component_t; + +/** Module Data Directory = Directory Header + Directory Entry (0..n) + * Following two Data Structure has been taken from CSE Storage FAS (CPD desgin) + * Module Data Directory Header + */ +typedef struct { + uint32_t header_marker; + uint32_t number_of_entries; + uint8_t header_version; + uint8_t entry_version; + uint8_t header_length; /**< 0x10 (16) Fixed for this version*/ + uint8_t checksum; + uint32_t partition_name; +} ia_css_directory_header_component_t; + +/** Module Date Directory Entry + */ +typedef struct { + /**< character string describing the component name */ + uint8_t entry_name[SIZE_OF_COMPONENT_NAME]; + uint32_t offset; + uint32_t length; + uint32_t rsvd; /**< Must be 0 */ +} ia_css_directory_entry_component_t; + +#endif /* __IA_CSS_CPD_COMPONENT_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk new file mode 100644 index 000000000000..ac78815dfbd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk @@ -0,0 +1,29 @@ +## +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +## + + +# MODULE is CPD UTL (Metadata File Extension) + +CPD_DIR = $${MODULES_DIR}/cpd/ +CPD_METADATA_DIR = $${MODULES_DIR}/cpd/cpd_metadata +CPD_METADATA_INTERFACE = $(CPD_METADATA_DIR)/interface +CPD_METADATA_SOURCES = $(CPD_METADATA_DIR)/src + +CPD_METADATA_FILES = $(CPD_METADATA_SOURCES)/ia_css_cpd_metadata_create.c +CPD_METADATA_FILES += $(CPD_METADATA_SOURCES)/ia_css_cpd_metadata.c +CPD_METADATA_CPPFLAGS = -I$(CPD_METADATA_INTERFACE) \ + -I$(CPD_METADATA_SOURCES) \ + -I$(CPD_DIR) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h new file mode 100644 index 000000000000..a88c6aede08c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h @@ -0,0 +1,111 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_CPD_METADATA_TYPES_H +#define __IA_CSS_CPD_METADATA_TYPES_H + +/** @file + * This file contains data structures related to generation of + * metadata file extension + */ +#include + +/* As per v0.2 manifest document + * Header = Extension Type (4) + Extension Length (4) + + * iUnit Image Type (4) + Reserved (16) + */ +#define IPU_METADATA_HEADER_RSVD_SIZE 16 +#define IPU_METADATA_HEADER_FIELDS_SIZE 12 +#define IPU_METADATA_HEADER_SIZE \ + (IPU_METADATA_HEADER_FIELDS_SIZE + IPU_METADATA_HEADER_RSVD_SIZE) + +/* iUnit metadata extension tpye value */ +#define IPU_METADATA_EXTENSION_TYPE 16 + +/* Unique id for level 0 bootloader component */ +#define IA_CSS_IUNIT_BTLDR_ID 0 +/* Unique id for psys server program group component */ +#define IA_CSS_IUNIT_PSYS_SERVER_ID 1 +/* Unique id for isys server program group component */ +#define IA_CSS_IUNIT_ISYS_SERVER_ID 2 +/* Initial Identifier for client program group component */ +#define IA_CSS_IUNIT_CLIENT_ID 3 + +/* Use this to parse date from release version from the iUnit component + * e.g. 20150701 + */ +#define IA_CSS_IUNIT_COMP_DATE_SIZE 8 +/* offset of release version in program group binary + * e.g. release_version = "scci_gerrit_20150716_2117" + * In cpd file we only use date/version for the component + */ +#define IA_CSS_IUNIT_DATE_OFFSET 12 + +#define IPU_METADATA_HASH_KEY_SIZE 32 +#define IPU_METADATA_ATTRIBUTE_SIZE 16 +#define IA_CSE_METADATA_COMPONENT_ID_MAX 127 + +typedef enum { + IA_CSS_CPD_METADATA_IMAGE_TYPE_RESERVED, + IA_CSS_CPD_METADATA_IMAGE_TYPE_BOOTLOADER, + IA_CSS_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE +} ia_css_cpd_metadata_image_type_t; + +typedef enum { + IA_CSS_CPD_MAIN_FW_TYPE_RESERVED, + IA_CSS_CPD_MAIN_FW_TYPE_PSYS_SERVER, + IA_CSS_CPD_MAIN_FW_TYPE_ISYS_SERVER, + IA_CSS_CPD_MAIN_FW_TYPE_CLIENT +} ia_css_cpd_iunit_main_fw_type_t; + +/** Data structure for component specific information + * Following data structure has been taken from CSE Manifest v0.2 + */ +typedef struct { + /**< Component ID - unique for each component */ + uint32_t id; + /**< Size of the components */ + uint32_t size; + /**< Version/date of when the components is being generated/created */ + uint32_t version; + /**< SHA 256 Hash Key for component */ + uint8_t sha2_hash[IPU_METADATA_HASH_KEY_SIZE]; + /**< component sp entry point + * - Only valid for btldr/psys/isys server component + */ + uint32_t entry_point; + /**< component icache base address + * - Only valid for btldr/psys/isys server component + */ + uint32_t icache_base_offset; + /**< Resevred - must be 0 */ + uint8_t attributes[IPU_METADATA_ATTRIBUTE_SIZE]; +} ia_css_cpd_metadata_component_t; + +/** Data structure for Metadata File Extension Header + */ +typedef struct { + /**< Specifies the binary image type + * - could be bootloader or main firmware + */ + ia_css_cpd_metadata_image_type_t image_type; + /**< Number of components available in metadata file extension + * (For btldr always 1) + */ + uint32_t component_count; + /**< Component specific information */ + ia_css_cpd_metadata_component_t *components; +} ia_css_cpd_metadata_desc_t; + +#endif /* __IA_CSS_CPD_METADATA_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/device_access.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/device_access.mk new file mode 100644 index 000000000000..1629d9af803b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/device_access.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifndef _DEVICE_ACCESS_MK_ +_DEVICE_ACCESS_MK_ = 1 + +# DEVICE_ACCESS_VERSION= +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk + +DEVICE_ACCESS_DIR=$${MODULES_DIR}/device_access +DEVICE_ACCESS_INTERFACE=$(DEVICE_ACCESS_DIR)/interface +DEVICE_ACCESS_SOURCES=$(DEVICE_ACCESS_DIR)/src + +DEVICE_ACCESS_HOST_FILES = + +DEVICE_ACCESS_FW_FILES = + +DEVICE_ACCESS_HOST_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS += \ + -I$(DEVICE_ACCESS_SOURCES)/$(DEVICE_ACCESS_VERSION) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h new file mode 100644 index 000000000000..3dc47c29fcab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_H +#define __IA_CSS_CMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_cmem_address_t; +#else +#include +typedef vied_subsystem_address_t ia_css_cmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size); + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_cmem_cell.h" +#else +#include "ia_css_cmem_host.h" +#endif + +#endif /* __IA_CSS_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h new file mode 100644 index 000000000000..de2b94d8af54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_H +#define __IA_CSS_XMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_xmem_address_t; +#else +#include +typedef host_virtual_address_t ia_css_xmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_xmem_cell.h" +#else +#include "ia_css_xmem_host.h" +#endif + +#endif /* __IA_CSS_XMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h new file mode 100644 index 000000000000..57aab3323c73 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h @@ -0,0 +1,35 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_H +#define __IA_CSS_XMEM_CMEM_H + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size); + +/* include inline implementation */ +#include "ia_css_xmem_cmem_impl.h" + +#endif /* __IA_CSS_XMEM_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h new file mode 100644 index 000000000000..22799e67214c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h @@ -0,0 +1,121 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_HOST_H +#define __IA_CSS_CMEM_HOST_H + +/* This file is an inline implementation for the interface ia_css_cmem.h + * and should only be included there. */ + +#include "assert_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + return vied_subsystem_load_32(ssid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_cond_cmem_load_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + return vied_subsystem_load_32(ssid, address); + else + return 0; +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cond_cmem_store_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address, uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + *data32 = ia_css_cmem_load_32(ssid, address); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, *data32); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size) +{ + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, 0); + address += 4; + } +} + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p) +{ + NOT_USED(base_addr); + return (ia_css_cmem_address_t)(uintptr_t)p; +} + +#endif /* __IA_CSS_CMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h new file mode 100644 index 000000000000..adc178b75059 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_IMPL_H +#define __IA_CSS_XMEM_CMEM_IMPL_H + +#include "ia_css_xmem_cmem.h" + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_cmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_xmem_load_32(mmid, src); + ia_css_cmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + +/* Copy data from cmem to xmem */ + +STORAGE_CLASS_INLINE void +ia_css_cmem_to_xmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_cmem_address_t src, + ia_css_xmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_xmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_cmem_load_32(mmid, src); + ia_css_xmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + + +#endif /* __IA_CSS_XMEM_CMEM_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h new file mode 100644 index 000000000000..d94991fc1114 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h @@ -0,0 +1,84 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_HOST_H +#define __IA_CSS_XMEM_HOST_H + +#include "ia_css_xmem.h" +#include +#include "assert_support.h" +#include + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address) +{ + return shared_memory_load_8(mmid, address); +} + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + return shared_memory_load_16(mmid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + return shared_memory_load_32(mmid, address); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size) +{ + shared_memory_load(mmid, address, data, size); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value) +{ + shared_memory_store_8(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + shared_memory_store_16(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + shared_memory_store_32(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes) +{ + shared_memory_store(mmid, address, data, bytes); +} + +#endif /* __IA_CSS_XMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h new file mode 100644 index 000000000000..5102f6e44d2f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/bxtB0/ipu_device_buttress_properties_struct.h @@ -0,0 +1,68 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H + +/* Destination values for master port 0 and bitfield "request_dest" */ +enum cio_M0_btrs_dest { + DEST_IS_BUT_REGS = 0, + DEST_IS_DDR, + RESERVED, + DEST_IS_SUBSYSTEM, + N_BTRS_DEST +}; + +/* Bit-field positions for M0 info bits */ +enum ia_css_info_bits_m0_pos { + IA_CSS_INFO_BITS_M0_SNOOPABLE_POS = 0, + IA_CSS_INFO_BITS_M0_IMR_DESTINED_POS = 1, + IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS = 4 +}; + +#define IA_CSS_INFO_BITS_M0_DDR \ + (DEST_IS_DDR << IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS) +#define IA_CSS_INFO_BITS_M0_SNOOPABLE (1 << IA_CSS_INFO_BITS_M0_SNOOPABLE_POS) + +/* Info bits as expected by the buttress */ +/* Deprecated because bit fields are not portable */ + +/* For master port 0*/ +union cio_M0_t { + struct { + unsigned int snoopable : 1; + unsigned int imr_destined : 1; + unsigned int spare0 : 2; + unsigned int request_dest : 2; + unsigned int spare1 : 26; + } as_bitfield; + unsigned int as_word; +}; + +/* For master port 1*/ +union cio_M1_t { + struct { + unsigned int spare0 : 1; + unsigned int deadline_pointer : 1; + unsigned int reserved : 1; + unsigned int zlw : 1; + unsigned int stream_id : 4; + unsigned int address_swizzling : 1; + unsigned int spare1 : 23; + } as_bitfield; + unsigned int as_word; +}; + + +#endif /* __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h new file mode 100644 index 000000000000..e6e1e9dcbe80 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_H +#define __IPU_DEVICE_CELL_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_cell_type_properties.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id); + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id); +#endif + +#include "ipu_device_cell_properties_func.h" + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h new file mode 100644 index 000000000000..481b0504a237 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h @@ -0,0 +1,164 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_FUNC_H +#define __IPU_DEVICE_CELL_PROPERTIES_FUNC_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_cell_properties_impl.h" +#include "ipu_device_cell_devices.h" +#include "assert_support.h" +#include "storage_class.h" + +enum {IA_CSS_CELL_MASTER_ADDRESS_WIDTH = 32}; + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void) +{ + return NUM_CELLS; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_memories; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + mem_size[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].mem_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + assert(mem_id != 0); + return ipu_device_cell_properties[cell_id].mem_databus_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_master_ports; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].segment_bits; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << ipu_device_cell_master_segment_bits(cell_id, master_id); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << (IA_CSS_CELL_MASTER_ADDRESS_WIDTH - + ipu_device_cell_master_segment_bits(cell_id, master_id)); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].stride; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].base_address_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_override_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_override_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + icache_align; +} + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_map_cell_id_to_crun_proc_id[cell_id]; +} +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_FUNC_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h new file mode 100644 index 000000000000..63397dc0b7fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H + +/* definitions for all cell types */ + +struct ipu_device_cell_count_s { + unsigned int num_memories; + unsigned int num_master_ports; + unsigned int num_stall_bits; + unsigned int icache_align; +}; + +struct ipu_device_cell_master_properties_s { + unsigned int segment_bits; + unsigned int stride; /* offset to register of next segment */ + unsigned int base_address_register; /* address of first base address + register */ + unsigned int info_bits_register; + unsigned int info_override_bits_register; +}; + +struct ipu_device_cell_type_properties_s { + const struct ipu_device_cell_count_s *count; + const struct ipu_device_cell_master_properties_s *master; + const unsigned int *reg_offset; /* offsets of registers, some depend + on cell type */ + const unsigned int *mem_size; +}; + +struct ipu_device_cell_properties_s { + const struct ipu_device_cell_type_properties_s *type_properties; + const unsigned int *mem_address; + const unsigned int *mem_databus_address; + /* const cell_master_port_properties_s* master_port_properties; */ +}; + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h new file mode 100644 index 000000000000..72caed3eef0c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_TYPE_PROPERTIES_H +#define __IPU_DEVICE_CELL_TYPE_PROPERTIES_H + +#define IPU_DEVICE_INVALID_MEM_ADDRESS 0xFFFFFFFF + +enum ipu_device_cell_stat_ctrl_bit { + IPU_DEVICE_CELL_STAT_CTRL_RESET_BIT = 0, + IPU_DEVICE_CELL_STAT_CTRL_START_BIT = 1, + IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT = 3, + IPU_DEVICE_CELL_STAT_CTRL_READY_BIT = 5, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_BIT = 6, + IPU_DEVICE_CELL_STAT_CTRL_STALL_BIT = 7, + IPU_DEVICE_CELL_STAT_CTRL_CLEAR_IRQ_MASK_FLAG_BIT = 8, + IPU_DEVICE_CELL_STAT_CTRL_BROKEN_IRQ_MASK_FLAG_BIT = 9, + IPU_DEVICE_CELL_STAT_CTRL_READY_IRQ_MASK_FLAG_BIT = 10, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_IRQ_MASK_FLAG_BIT = 11, + IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT = 12, + IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT = 13 +}; + +enum ipu_device_cell_reg_addr { + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS = 0x0, + IPU_DEVICE_CELL_START_PC_REG_ADDRESS = 0x4, + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS = 0x10, + IPU_DEVICE_CELL_ICACHE_INFO_BITS_REG_ADDRESS = 0x14 +}; + +enum ipu_device_cell_reg { + IPU_DEVICE_CELL_STAT_CTRL_REG, + IPU_DEVICE_CELL_START_PC_REG, + IPU_DEVICE_CELL_ICACHE_BASE_REG, + IPU_DEVICE_CELL_DEBUG_PC_REG, + IPU_DEVICE_CELL_STALL_REG, + IPU_DEVICE_CELL_NUM_REGS +}; + +enum ipu_device_cell_mem { + IPU_DEVICE_CELL_REGS, /* memory id of registers */ + IPU_DEVICE_CELL_PMEM, /* memory id of pmem */ + IPU_DEVICE_CELL_DMEM, /* memory id of dmem */ + IPU_DEVICE_CELL_BAMEM, /* memory id of bamem */ + IPU_DEVICE_CELL_VMEM /* memory id of vmem */ +}; +#define IPU_DEVICE_CELL_NUM_MEMORIES (IPU_DEVICE_CELL_VMEM + 1) + +enum ipu_device_cell_master { + IPU_DEVICE_CELL_MASTER_ICACHE, /* master port id of icache */ + IPU_DEVICE_CELL_MASTER_QMEM, + IPU_DEVICE_CELL_MASTER_CMEM, + IPU_DEVICE_CELL_MASTER_XMEM, + IPU_DEVICE_CELL_MASTER_XVMEM +}; +#define IPU_DEVICE_CELL_MASTER_NUM_MASTERS (IPU_DEVICE_CELL_MASTER_XVMEM + 1) + +#endif /* __IPU_DEVICE_CELL_TYPE_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h new file mode 100644 index 000000000000..fd0c5a586c94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h @@ -0,0 +1,26 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_PROPERTIES_H +#define __IPU_DEVICE_GP_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_gp_properties_types.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_gp_mux_addr(const unsigned int device_id, const unsigned int mux_id); + +#include "ipu_device_gp_properties_func.h" + +#endif /* __IPU_DEVICE_GP_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h new file mode 100644 index 000000000000..3032273696ea --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h @@ -0,0 +1,103 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_PROPERTIES_TYPES_H +#define __IPU_DEVICE_GP_PROPERTIES_TYPES_H + +enum ipu_device_gp_isa_value { + /* ISA_MUX_SEL options */ + IPU_DEVICE_GP_ISA_MUX_SEL_ICA = 0, /* Enable output after FF ICA */ + IPU_DEVICE_GP_ISA_MUX_SEL_LSC = 1, /* Enable output after FF LSC */ + IPU_DEVICE_GP_ISA_MUX_SEL_DPC = 2, /* Enable output after FF DPC */ + /* ICA stream block options */ + /* UNBLOCK signal received from ICA */ + IPU_DEVICE_GP_ISA_ICA_UNBLOCK = 0, + /* BLOCK signal received from ICA */ + IPU_DEVICE_GP_ISA_ICA_BLOCK = 1, + /* LSC stream block options */ + /* UNBLOCK signal received from LSC */ + IPU_DEVICE_GP_ISA_LSC_UNBLOCK = 0, + /* BLOCK signal received from LSC */ + IPU_DEVICE_GP_ISA_LSC_BLOCK = 1, + /* DPC stream block options */ + /* UNBLOCK signal received from DPC */ + IPU_DEVICE_GP_ISA_DPC_UNBLOCK = 0, + /* BLOCK signal received from DPC */ + IPU_DEVICE_GP_ISA_DPC_BLOCK = 1, + /* Defines needed only for bxtB0 */ + /* ISA_AWB_MUX_SEL options */ + /* Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_SEL_ICA = 0, + /* DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_SEL_DPC = 1, + /* ISA_AWB_MUX_SEL options */ + /* UNBLOCK DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_ICA_UNBLOCK = 0, + /* BLOCK DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_ICA_BLOCK = 1, + /* ISA_AWB_MUX_SEL options */ + /* UNBLOCK Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_DPC_UNBLOCK = 0, + /* BLOCK Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_DPC_BLOCK = 1, + + /* PAF STRM options */ + /* Disable streaming to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_DISABLE_STREAM = 0, + /* Enable stream0 to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_ENABLE_STREAM0 = 1, + /* Enable stream1 to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_ENABLE_STREAM1 = 2, + /* PAF SRC SEL options */ + /* External channel input */ + IPU_DEVICE_GP_ISA_PAF_SRC_SEL0 = 0, + /* DPC extracted input */ + IPU_DEVICE_GP_ISA_PAF_SRC_SEL1 = 1, + /* PAF_GDDPC_BLK options */ + IPU_DEVICE_GP_ISA_PAF_GDDPC_PORT_BLK0 = 0, + IPU_DEVICE_GP_ISA_PAF_GDDPC_PORT_BLK1 = 1, + /* PAF ISA STR_PORT options */ + IPU_DEVICE_GP_ISA_PAF_STR_PORT0 = 0, + IPU_DEVICE_GP_ISA_PAF_STR_PORT1 = 1, + + /* sis port block options */ + IPU_DEVICE_GP_ISA_SIS_PORT_UNBLOCK = 0, + IPU_DEVICE_GP_ISA_SIS_PORT_BLOCK = 1, + IPU_DEVICE_GP_ISA_CONF_INVALID = 0xFF +}; + +enum ipu_device_gp_psa_value { + /* Defines needed for bxtB0 */ + /* PSA_STILLS_MODE_MUX */ + IPU_DEVICE_GP_PSA_MUX_POST_RYNR_ROUTE_WO_DM = 0, + IPU_DEVICE_GP_PSA_MUX_POST_RYNR_ROUTE_W_DM = 1, + /* PSA_ACM_DEMUX */ + IPU_DEVICE_GP_PSA_DEMUX_PRE_ACM_ROUTE_TO_ACM = 0, + IPU_DEVICE_GP_PSA_DEMUX_PRE_ACM_ROUTE_TO_S2V = 1, + /* PSA_S2V_RGB_F_MUX */ + IPU_DEVICE_GP_PSA_MUX_PRE_S2V_RGB_F_FROM_ACM = 0, + IPU_DEVICE_GP_PSA_MUX_PRE_S2V_RGB_F_FROM_DM_OR_SPLITTER = 1, + /* PSA_V2S_RGB_4_DEMUX */ + IPU_DEVICE_GP_PSA_DEMUX_POST_V2S_RGB_4_TO_GTM = 0, + IPU_DEVICE_GP_PSA_DEMUX_POST_V2S_RGB_4_TO_ACM = 1, +}; + +enum ipu_device_gp_isl_value { + /* choose and route pixel stream to CSI BE */ + IPU_DEVICE_GP_ISL_CSI_BE_IN_USE = 0, + /* choose and route pixel stream bypass CSI BE */ + IPU_DEVICE_GP_ISL_CSI_BE_BYPASS +}; + +#endif /* __IPU_DEVICE_GP_PROPERTIES_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_acb_devices.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_acb_devices.h new file mode 100644 index 000000000000..d9472a5d33ca --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_acb_devices.h @@ -0,0 +1,43 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_ACB_DEVICES_H +#define __IPU_DEVICE_ACB_DEVICES_H + +enum ipu_device_acb_id { + /* PSA accelerators */ + IPU_DEVICE_ACB_WBA_ID = 0, + IPU_DEVICE_ACB_RYNR_ID, + IPU_DEVICE_ACB_DEMOSAIC_ID, + IPU_DEVICE_ACB_ACM_ID, + IPU_DEVICE_ACB_GTC_ID, + IPU_DEVICE_ACB_YUV1_ID, + IPU_DEVICE_ACB_DVS_ID, + IPU_DEVICE_ACB_LACE_ID, + /* ISA accelerators */ + IPU_DEVICE_ACB_ICA_ID, + IPU_DEVICE_ACB_LSC_ID, + IPU_DEVICE_ACB_DPC_ID, + IPU_DEVICE_ACB_IDS_ID, + IPU_DEVICE_ACB_AWB_ID, + IPU_DEVICE_ACB_AF_ID, + IPU_DEVICE_ACB_AE_ID, + IPU_DEVICE_ACB_NUM_ACB +}; + +#define IPU_DEVICE_ACB_NUM_PSA_ACB (IPU_DEVICE_ACB_LACE_ID + 1) +#define IPU_DEVICE_ACB_NUM_ISA_ACB \ + (IPU_DEVICE_ACB_NUM_ACB - IPU_DEVICE_ACB_NUM_PSA_ACB) + +#endif /* __IPU_DEVICE_ACB_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_devices.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_devices.h new file mode 100644 index 000000000000..7a57967cb6eb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_devices.h @@ -0,0 +1,38 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_CELL_DEVICES_H +#define __IPU_DEVICE_CELL_DEVICES_H + +#define SPC0_CELL processing_system_sp_cluster_sp_cluster_logic_spc_tile_sp +#define SPP0_CELL processing_system_sp_cluster_sp_cluster_logic_spp_tile0_sp +#define SPP1_CELL processing_system_sp_cluster_sp_cluster_logic_spp_tile1_sp +#define ISP0_CELL processing_system_isp_tile0_logic_isp +#define ISP1_CELL processing_system_isp_tile1_logic_isp +#define ISP2_CELL processing_system_isp_tile2_logic_isp +#define ISP3_CELL processing_system_isp_tile3_logic_isp + +enum ipu_device_psys_cell_id { + SPC0, + SPP0, + SPP1, + ISP0, + ISP1, + ISP2, + ISP3, + NUM_CELLS +}; +#define NUM_ISP_CELLS 4 + +#endif /* __IPU_DEVICE_CELL_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_defs.h new file mode 100644 index 000000000000..2b80e2822a90 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_defs.h @@ -0,0 +1,65 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +/* Generated file - please do not edit. */ + +#ifndef _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define SPC0_REGS_CBUS_ADDRESS 0x00000000 +#define SPC0_DMEM_CBUS_ADDRESS 0x00008000 +#define SPC0_DMEM_DBUS_ADDRESS 0x02000000 +#define SPC0_DMEM_DMA_M0_ADDRESS SPC0_DMEM_DBUS_ADDRESS +#define SPC0_DMEM_INT_DMA_M0_ADDRESS SPC0_DMEM_DBUS_ADDRESS +#define SPP0_REGS_CBUS_ADDRESS 0x00020000 +#define SPP0_DMEM_CBUS_ADDRESS 0x00028000 +#define SPP0_DMEM_DBUS_ADDRESS 0x02020000 +#define SPP1_REGS_CBUS_ADDRESS 0x00030000 +#define SPP1_DMEM_CBUS_ADDRESS 0x00038000 +#define SPP1_DMEM_DBUS_ADDRESS 0x02030000 +#define ISP0_REGS_CBUS_ADDRESS 0x001C0000 +#define ISP0_PMEM_CBUS_ADDRESS 0x001D0000 +#define ISP0_DMEM_CBUS_ADDRESS 0x001F0000 +#define ISP0_BAMEM_CBUS_ADDRESS 0x00200000 +#define ISP0_VMEM_CBUS_ADDRESS 0x00220000 +#define ISP1_REGS_CBUS_ADDRESS 0x00240000 +#define ISP1_PMEM_CBUS_ADDRESS 0x00250000 +#define ISP1_DMEM_CBUS_ADDRESS 0x00270000 +#define ISP1_BAMEM_CBUS_ADDRESS 0x00280000 +#define ISP1_VMEM_CBUS_ADDRESS 0x002A0000 +#define ISP2_REGS_CBUS_ADDRESS 0x002C0000 +#define ISP2_PMEM_CBUS_ADDRESS 0x002D0000 +#define ISP2_DMEM_CBUS_ADDRESS 0x002F0000 +#define ISP2_BAMEM_CBUS_ADDRESS 0x00300000 +#define ISP2_VMEM_CBUS_ADDRESS 0x00320000 +#define ISP3_REGS_CBUS_ADDRESS 0x00340000 +#define ISP3_PMEM_CBUS_ADDRESS 0x00350000 +#define ISP3_DMEM_CBUS_ADDRESS 0x00370000 +#define ISP3_BAMEM_CBUS_ADDRESS 0x00380000 +#define ISP3_VMEM_CBUS_ADDRESS 0x003A0000 +#define ISP0_PMEM_DBUS_ADDRESS 0x08000000 +#define ISP0_DMEM_DBUS_ADDRESS 0x08400000 +#define ISP0_BAMEM_DBUS_ADDRESS 0x09000000 +#define ISP0_VMEM_DBUS_ADDRESS 0x08800000 +#define ISP1_PMEM_DBUS_ADDRESS 0x0A000000 +#define ISP1_DMEM_DBUS_ADDRESS 0x0A400000 +#define ISP1_BAMEM_DBUS_ADDRESS 0x0B000000 +#define ISP1_VMEM_DBUS_ADDRESS 0x0A800000 +#define ISP2_PMEM_DBUS_ADDRESS 0x0C000000 +#define ISP2_DMEM_DBUS_ADDRESS 0x0C400000 +#define ISP2_BAMEM_DBUS_ADDRESS 0x0D000000 +#define ISP2_VMEM_DBUS_ADDRESS 0x0C800000 +#define ISP3_PMEM_DBUS_ADDRESS 0x0E000000 +#define ISP3_DMEM_DBUS_ADDRESS 0x0E400000 +#define ISP3_BAMEM_DBUS_ADDRESS 0x0F000000 +#define ISP3_VMEM_DBUS_ADDRESS 0x0E800000 +#endif /* _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_impl.h new file mode 100644 index 000000000000..10c28983eeb6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_cell_properties_impl.h @@ -0,0 +1,193 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_CELL_PROPERTIES_IMPL_H + +#include "ipu_device_sp2600_control_properties_impl.h" +#include "ipu_device_sp2600_proxy_properties_impl.h" +#include "ipu_device_isp2600_properties_impl.h" +#include "ipu_device_cell_properties_defs.h" +#include "ipu_device_cell_devices.h" +#include "ipu_device_cell_type_properties.h"/* IPU_DEVICE_INVALID_MEM_ADDRESS */ + +static const unsigned int +ipu_device_spc0_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + SPC0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp0_mem_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + SPP0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp1_mem_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + SPP1_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP1_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_isp0_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP0_REGS_CBUS_ADDRESS, /* reg addr */ + ISP0_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP0_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP0_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP0_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp1_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP1_REGS_CBUS_ADDRESS, /* reg addr */ + ISP1_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP1_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP1_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP1_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp2_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP2_REGS_CBUS_ADDRESS, /* reg addr */ + ISP2_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP2_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP2_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP2_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp3_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP3_REGS_CBUS_ADDRESS, /* reg addr */ + ISP3_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP3_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP3_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP3_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_spc0_mem_databus_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp0_mem_databus_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP0_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp1_mem_databus_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP1_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_isp0_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP0_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP0_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP0_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP0_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp1_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP1_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP1_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP1_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP1_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp2_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP2_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP2_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP2_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP2_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp3_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP3_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP3_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP3_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP3_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const struct ipu_device_cell_properties_s +ipu_device_cell_properties[NUM_CELLS] = { + { + &ipu_device_sp2600_control_properties, + ipu_device_spc0_mem_address, + ipu_device_spc0_mem_databus_address + }, + { + &ipu_device_sp2600_proxy_properties, + ipu_device_spp0_mem_address, + ipu_device_spp0_mem_databus_address + }, + { + &ipu_device_sp2600_proxy_properties, + ipu_device_spp1_mem_address, + ipu_device_spp1_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp0_mem_address, + ipu_device_isp0_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp1_mem_address, + ipu_device_isp1_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp2_mem_address, + ipu_device_isp2_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp3_mem_address, + ipu_device_isp3_mem_databus_address + } +}; + +#ifdef C_RUN + +/* Mapping between hrt_hive_processors enum and cell_id's used in FW */ +static const int ipu_device_map_cell_id_to_crun_proc_id[NUM_CELLS] = { + 4, /* SPC0 */ + 5, /* SPP0 */ + 6, /* SPP1 */ + 0, /* ISP0 */ + 1, /* ISP1 */ + 2, /* ISP2 */ + 3 /* ISP3 */ +}; + +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_ff_devices.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_ff_devices.h new file mode 100644 index 000000000000..3af7ba63a364 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_ff_devices.h @@ -0,0 +1,55 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_FF_DEVICES_H +#define __IPU_DEVICE_FF_DEVICES_H + +enum ipu_device_ff_id { + /* PSA fixed functions */ + IPU_DEVICE_FF_WBA_WBA = 0, + IPU_DEVICE_FF_RYNR_SPLITTER, + IPU_DEVICE_FF_RYNR_COLLECTOR, + IPU_DEVICE_FF_RYNR_BNLM, + IPU_DEVICE_FF_RYNR_VCUD, + IPU_DEVICE_FF_DEMOSAIC_DEMOSAIC, + IPU_DEVICE_FF_ACM_CCM, + IPU_DEVICE_FF_ACM_ACM, + IPU_DEVICE_FF_GTC_CSC_CDS, + IPU_DEVICE_FF_GTC_GTM, + IPU_DEVICE_FF_YUV1_SPLITTER, + IPU_DEVICE_FF_YUV1_IEFD, + IPU_DEVICE_FF_YUV1_YDS, + IPU_DEVICE_FF_YUV1_TCC, + IPU_DEVICE_FF_DVS_YBIN, + IPU_DEVICE_FF_DVS_DVS, + IPU_DEVICE_FF_LACE_LACE, + /* ISA fixed functions */ + IPU_DEVICE_FF_ICA_INL, + IPU_DEVICE_FF_ICA_GBL, + IPU_DEVICE_FF_ICA_PCLN, + IPU_DEVICE_FF_LSC_LSC, + IPU_DEVICE_FF_DPC_DPC, + IPU_DEVICE_FF_IDS_SCALER, + IPU_DEVICE_FF_AWB_AWRG, + IPU_DEVICE_FF_AF_AF, + IPU_DEVICE_FF_AE_WGHT_HIST, + IPU_DEVICE_FF_AE_CCM, + IPU_DEVICE_FF_NUM_FF +}; + +#define IPU_DEVICE_FF_NUM_PSA_FF (IPU_DEVICE_FF_LACE_LACE + 1) +#define IPU_DEVICE_FF_NUM_ISA_FF \ + (IPU_DEVICE_FF_NUM_FF - IPU_DEVICE_FF_NUM_PSA_FF) + +#endif /* __IPU_DEVICE_FF_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_gp_devices.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_gp_devices.h new file mode 100644 index 000000000000..f6afd6003324 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/psys/bxtB0/ipu_device_gp_devices.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_DEVICES_H +#define __IPU_DEVICE_GP_DEVICES_H +#include "math_support.h" +#include "type_support.h" + +enum ipu_device_gp_id { + IPU_DEVICE_GP_PSA = 0, /* PSA */ + IPU_DEVICE_GP_ISA_STATIC, /* ISA Static */ + IPU_DEVICE_GP_ISA_RUNTIME, /* ISA Runtime */ + IPU_DEVICE_GP_ISL, /* ISL */ + IPU_DEVICE_GP_NUM_GP +}; + +enum ipu_device_gp_psa_mux_id { + /* Post RYNR/CCN: 0-To ACM (Video), 1-To Demosaic (Stills)*/ + IPU_DEVICE_GP_PSA_STILLS_MODE_MUX = 0, + /* Post Vec2Str 4: 0-To GTC, 1-To ACM */ + IPU_DEVICE_GP_PSA_V2S_RGB_4_DEMUX, + /* Post DM and pre ACM 0-CCM/ACM: 1-DM Component Splitter */ + IPU_DEVICE_GP_PSA_S2V_RGB_F_MUX, + /* Pre ACM/CCM: 0-To CCM/ACM, 1-To str2vec id_f */ + IPU_DEVICE_GP_PSA_ACM_DEMUX, + IPU_DEVICE_GP_PSA_MUX_NUM_MUX +}; + +enum ipu_device_gp_isa_static_mux_id { + IPU_DEVICE_GP_ISA_STATIC_MUX_SEL = 0, + IPU_DEVICE_GP_ISA_STATIC_PORTA_BLK, + IPU_DEVICE_GP_ISA_STATIC_PORTB_BLK, + IPU_DEVICE_GP_ISA_STATIC_PORTC_BLK, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_SEL, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_INPUT_CORR_PORT_BLK, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_DPC_PORT_BLK, + IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX +}; + +enum ipu_device_gp_isa_runtime_mux_id { + IPU_DEVICE_GP_ISA_RUNTIME_FRAME_SIZE = 0, + IPU_DEVICE_GP_ISA_RUNTIME_SCALED_FRAME_SIZE, + IPU_DEVICE_GP_ISA_RUNTIME_MUX_NUM_MUX +}; + +enum ipu_device_gp_isl_mux_id { + IPU_DEVICE_GP_ISL_MIPI_BE_MUX = 0, + IPU_DEVICE_GP_ISL_MUX_NUM_MUX +}; + +#define IPU_DEVICE_GP_MAX_NUM MAX4((uint32_t)IPU_DEVICE_GP_PSA_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISA_RUNTIME_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISL_MUX_NUM_MUX) + +#endif /* __IPU_DEVICE_GP_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h new file mode 100644 index 000000000000..de733be67998 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h @@ -0,0 +1,151 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H +#define __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H + +/* isp2600 definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_isp2600_registers { + /* control registers */ + IPU_DEVICE_ISP2600_STAT_CTRL = 0x0, + IPU_DEVICE_ISP2600_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_ISP2600_ICACHE_BASE = 0x10, + IPU_DEVICE_ISP2600_ICACHE_INFO = 0x14, + IPU_DEVICE_ISP2600_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_ISP2600_QMEM_BASE = 0x1C, + + IPU_DEVICE_ISP2600_CMEM_BASE = 0x28, + + IPU_DEVICE_ISP2600_XMEM_BASE = 0x88, + IPU_DEVICE_ISP2600_XMEM_INFO = 0x8C, + IPU_DEVICE_ISP2600_XMEM_INFO_OVERRIDE = 0x90, + + IPU_DEVICE_ISP2600_XVMEM_BASE = 0xB8, + + /* debug registers */ + IPU_DEVICE_ISP2600_DEBUG_PC = 0x130, + IPU_DEVICE_ISP2600_STALL = 0x134 +}; + + +enum ipu_device_isp2600_memories { + IPU_DEVICE_ISP2600_REGS, + IPU_DEVICE_ISP2600_PMEM, + IPU_DEVICE_ISP2600_DMEM, + IPU_DEVICE_ISP2600_BAMEM, + IPU_DEVICE_ISP2600_VMEM, + IPU_DEVICE_ISP2600_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_isp2600_mem_size[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + 0x00140, + 0x14000, + 0x04000, + 0x20000, + 0x20000 +}; + + +enum ipu_device_isp2600_masters { + IPU_DEVICE_ISP2600_ICACHE, + IPU_DEVICE_ISP2600_QMEM, + IPU_DEVICE_ISP2600_CMEM, + IPU_DEVICE_ISP2600_XMEM, + IPU_DEVICE_ISP2600_XVMEM, + IPU_DEVICE_ISP2600_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_isp2600_masters[IPU_DEVICE_ISP2600_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_ISP2600_ICACHE_BASE, + IPU_DEVICE_ISP2600_ICACHE_INFO, + IPU_DEVICE_ISP2600_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_ISP2600_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 3, + 0xC, + IPU_DEVICE_ISP2600_CMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_ISP2600_XMEM_BASE, + IPU_DEVICE_ISP2600_XMEM_INFO, + IPU_DEVICE_ISP2600_XMEM_INFO_OVERRIDE + }, + { + 3, + 0xC, + IPU_DEVICE_ISP2600_XVMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + } +}; + +enum ipu_device_isp2600_stall_bits { + IPU_DEVICE_ISP2600_STALL_ICACHE0, + IPU_DEVICE_ISP2600_STALL_ICACHE1, + IPU_DEVICE_ISP2600_STALL_DMEM, + IPU_DEVICE_ISP2600_STALL_QMEM, + IPU_DEVICE_ISP2600_STALL_CMEM, + IPU_DEVICE_ISP2600_STALL_XMEM, + IPU_DEVICE_ISP2600_STALL_BAMEM, + IPU_DEVICE_ISP2600_STALL_VMEM, + IPU_DEVICE_ISP2600_STALL_XVMEM, + IPU_DEVICE_ISP2600_NUM_STALL_BITS +}; + +#define IPU_DEVICE_ISP2600_ICACHE_WORD_SIZE 64 /* 512 bits per instruction */ +#define IPU_DEVICE_ISP2600_ICACHE_BURST_SIZE 8 /* 8 instructions per burst */ + +static const struct ipu_device_cell_count_s ipu_device_isp2600_count = { + IPU_DEVICE_ISP2600_NUM_MEMORIES, + IPU_DEVICE_ISP2600_NUM_MASTERS, + IPU_DEVICE_ISP2600_NUM_STALL_BITS, + IPU_DEVICE_ISP2600_ICACHE_WORD_SIZE * + IPU_DEVICE_ISP2600_ICACHE_BURST_SIZE +}; + +static const unsigned int ipu_device_isp2600_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x130, 0x134 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_isp2600_properties = { + &ipu_device_isp2600_count, + ipu_device_isp2600_masters, + ipu_device_isp2600_reg_offset, + ipu_device_isp2600_mem_size +}; + +#endif /* __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h new file mode 100644 index 000000000000..430295cd9d94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h @@ -0,0 +1,136 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H + +/* sp2600_control definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_control_registers { + /* control registers */ + IPU_DEVICE_SP2600_CONTROL_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_CONTROL_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_CONTROL_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_CONTROL_STALL = 0xA0 +}; + +enum ipu_device_sp2600_control_mems { + IPU_DEVICE_SP2600_CONTROL_REGS, + IPU_DEVICE_SP2600_CONTROL_PMEM, + IPU_DEVICE_SP2600_CONTROL_DMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_control_mem_size[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + 0x000AC, + 0x00000, + 0x10000 +}; + +enum ipu_device_sp2600_control_masters { + IPU_DEVICE_SP2600_CONTROL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_QMEM, + IPU_DEVICE_SP2600_CONTROL_CMEM, + IPU_DEVICE_SP2600_CONTROL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_control_masters[IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_control_stall_bits { + IPU_DEVICE_SP2600_CONTROL_STALL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_STALL_DMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_QMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_CMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_control_count = { + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS, + IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_control_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_control_properties = { + &ipu_device_sp2600_control_count, + ipu_device_sp2600_control_masters, + ipu_device_sp2600_control_reg_offset, + ipu_device_sp2600_control_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h new file mode 100644 index 000000000000..b3f120f9fea8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h @@ -0,0 +1,140 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H + +/* sp2600_fp definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_fp_registers { + /* control registers */ + IPU_DEVICE_SP2600_FP_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_FP_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_FP_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_FP_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_FP_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_FP_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_FP_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_FP_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_FP_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_FP_XMEM_BASE = 0x88, + IPU_DEVICE_SP2600_FP_XMEM_INFO = 0x8C, + IPU_DEVICE_SP2600_FP_XMEM_INFO_OVERRIDE = 0x90, + + /* debug registers */ + IPU_DEVICE_SP2600_FP_DEBUG_PC = 0xCC, + IPU_DEVICE_SP2600_FP_STALL = 0xD0 +}; + + +enum ipu_device_sp2600_fp_memories { + IPU_DEVICE_SP2600_FP_REGS, + IPU_DEVICE_SP2600_FP_PMEM, + IPU_DEVICE_SP2600_FP_DMEM, + IPU_DEVICE_SP2600_FP_DMEM1, + IPU_DEVICE_SP2600_FP_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_fp_mem_size[IPU_DEVICE_SP2600_FP_NUM_MEMORIES] = { + 0x000DC, + 0x00000, + 0x10000, + 0x08000 +}; + +enum ipu_device_sp2600_fp_masters { + IPU_DEVICE_SP2600_FP_ICACHE, + IPU_DEVICE_SP2600_FP_QMEM, + IPU_DEVICE_SP2600_FP_CMEM, + IPU_DEVICE_SP2600_FP_XMEM, + IPU_DEVICE_SP2600_FP_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_fp_masters[IPU_DEVICE_SP2600_FP_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_FP_ICACHE_BASE, + IPU_DEVICE_SP2600_FP_ICACHE_INFO, + IPU_DEVICE_SP2600_FP_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_FP_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 3, + 0xC, + IPU_DEVICE_SP2600_FP_CMEM_BASE, + IPU_DEVICE_SP2600_FP_CMEM_INFO, + IPU_DEVICE_SP2600_FP_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_FP_XMEM_BASE, + IPU_DEVICE_SP2600_FP_XMEM_INFO, + IPU_DEVICE_SP2600_FP_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_fp_stall_bits { + IPU_DEVICE_SP2600_FP_STALL_ICACHE, + IPU_DEVICE_SP2600_FP_STALL_DMEM, + IPU_DEVICE_SP2600_FP_STALL_QMEM, + IPU_DEVICE_SP2600_FP_STALL_CMEM, + IPU_DEVICE_SP2600_FP_STALL_XMEM, + IPU_DEVICE_SP2600_FP_STALL_DMEM1, + IPU_DEVICE_SP2600_FP_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_FP_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_FP_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_fp_count = { + IPU_DEVICE_SP2600_FP_NUM_MEMORIES, + IPU_DEVICE_SP2600_FP_NUM_MASTERS, + IPU_DEVICE_SP2600_FP_NUM_STALL_BITS, + IPU_DEVICE_SP2600_FP_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_FP_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_fp_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_fp_properties = { + &ipu_device_sp2600_fp_count, + ipu_device_sp2600_fp_masters, + ipu_device_sp2600_fp_reg_offset, + ipu_device_sp2600_fp_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h new file mode 100644 index 000000000000..6fdcd7faea9b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h @@ -0,0 +1,138 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H + +/* sp2600_proxy definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_proxy_registers { + /* control registers */ + IPU_DEVICE_SP2600_PROXY_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_PROXY_START_PC = 0x4, + + /* THESE ADDRESSES NEED TO BE CHECKED !!!! */ + /* master port registers */ + IPU_DEVICE_SP2600_PROXY_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_PROXY_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_PROXY_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_PROXY_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_PROXY_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_PROXY_STALL = 0xA0 +}; + + +enum ipu_device_sp2600_proxy_memories { + IPU_DEVICE_SP2600_PROXY_REGS, + IPU_DEVICE_SP2600_PROXY_PMEM, + IPU_DEVICE_SP2600_PROXY_DMEM, + IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_proxy_mem_size[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + 0x00AC, + 0x0000, + 0x4000 +}; + +enum ipu_device_sp2600_proxy_masters { + IPU_DEVICE_SP2600_PROXY_ICACHE, + IPU_DEVICE_SP2600_PROXY_QMEM, + IPU_DEVICE_SP2600_PROXY_CMEM, + IPU_DEVICE_SP2600_PROXY_XMEM, + IPU_DEVICE_SP2600_PROXY_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_proxy_masters[IPU_DEVICE_SP2600_PROXY_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_PROXY_ICACHE_BASE, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_PROXY_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_PROXY_CMEM_BASE, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_PROXY_XMEM_BASE, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_proxy_stall_bits { + IPU_DEVICE_SP2600_PROXY_STALL_ICACHE, + IPU_DEVICE_SP2600_PROXY_STALL_DMEM, + IPU_DEVICE_SP2600_PROXY_STALL_QMEM, + IPU_DEVICE_SP2600_PROXY_STALL_CMEM, + IPU_DEVICE_SP2600_PROXY_STALL_XMEM, + IPU_DEVICE_SP2600_PROXY_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_PROXY_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_PROXY_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_proxy_count = { + IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES, + IPU_DEVICE_SP2600_PROXY_NUM_MASTERS, + IPU_DEVICE_SP2600_PROXY_NUM_STALL_BITS, + IPU_DEVICE_SP2600_PROXY_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_PROXY_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_proxy_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0xCC, 0xD0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_proxy_properties = { + &ipu_device_sp2600_proxy_count, + ipu_device_sp2600_proxy_masters, + ipu_device_sp2600_proxy_reg_offset, + ipu_device_sp2600_proxy_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk new file mode 100644 index 000000000000..b1ffbf7ea21f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk @@ -0,0 +1,24 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +# MODULE is FW ABI COMMON TYPES + +FW_ABI_COMMON_TYPES_DIRS = -I$${MODULES_DIR}/fw_abi_common_types +FW_ABI_COMMON_TYPES_DIRS += -I$${MODULES_DIR}/fw_abi_common_types/cpu + +FW_ABI_COMMON_TYPES_HOST_FILES = +FW_ABI_COMMON_TYPES_HOST_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) + +FW_ABI_COMMON_TYPES_FW_FILES = +FW_ABI_COMMON_TYPES_FW_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h new file mode 100644 index 000000000000..21cc3f43f485 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_BASE_TYPES_H +#define __IA_CSS_TERMINAL_BASE_TYPES_H + + +#include "type_support.h" +#include "ia_css_terminal_defs.h" + +#define N_UINT16_IN_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_TERMINAL_STRUCT 5 + +#define SIZE_OF_TERMINAL_STRUCT_BITS \ + (IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + N_UINT16_IN_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* ==================== Base Terminal - START ==================== */ +struct ia_css_terminal_s { /**< Base terminal */ + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the process group */ + uint16_t size; /**< Size of this whole terminal layout-structure */ + uint16_t tm_index; /**< Index of the terminal manifest object */ + ia_css_terminal_ID_t ID; /**< Absolute referal ID for this terminal, valid ID's != 0 */ + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_STRUCT]; +}; +/* ==================== Base Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h new file mode 100644 index 000000000000..056e1b6d5d4b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H + +#include "ia_css_terminal_defs.h" + +#define N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT 5 +#define SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_UINT16_T_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_UINT32_T_BITS \ + + (N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT*IA_CSS_UINT8_T_BITS)) + +/* ==================== Base Terminal Manifest - START ==================== */ +struct ia_css_terminal_manifest_s { + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the program group manifest */ + uint16_t size; /**< Size of this whole terminal-manifest layout-structure */ + ia_css_terminal_ID_t ID; + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_terminal_manifest_s + ia_css_terminal_manifest_t; + +/* ==================== Base Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h new file mode 100644 index 000000000000..3b80a17a6ad3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h @@ -0,0 +1,38 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BASE_TYPES_H +#define __IA_CSS_BASE_TYPES_H + +#include "type_support.h" + +#define VIED_VADDRESS_BITS 32 +typedef uint32_t vied_vaddress_t; + +#define DEVICE_DESCRIPTOR_ID_BITS 32 +typedef struct { + uint8_t device_id; + uint8_t instance_id; + uint8_t channel_id; + uint8_t section_id; +} device_descriptor_fields_t; + +typedef union { + device_descriptor_fields_t fields; + uint32_t data; +} device_descriptor_id_t; + +typedef uint16_t ia_css_process_id_t; + +#endif /* __IA_CSS_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h new file mode 100644 index 000000000000..dbf1cf93756f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h @@ -0,0 +1,105 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_DEFS_H +#define __IA_CSS_TERMINAL_DEFS_H + + +#include "type_support.h" + +#define IA_CSS_TERMINAL_ID_BITS 8 +typedef uint8_t ia_css_terminal_ID_t; +#define IA_CSS_TERMINAL_INVALID_ID ((ia_css_terminal_ID_t)(-1)) + +/* + * Terminal Base Type + */ +typedef enum ia_css_terminal_type { + /**< Data input */ + IA_CSS_TERMINAL_TYPE_DATA_IN = 0, + /**< Data output */ + IA_CSS_TERMINAL_TYPE_DATA_OUT, + /**< Type 6 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_STREAM, + /**< Type 1-5 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN, + /**< Type 1-5 parameter output */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + /**< Represent the new type of terminal for the + * explicit slicing, when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN, + /**< Represent the new type of terminal for the + * explicit slicing, when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT, + /**< State (private data) input */ + IA_CSS_TERMINAL_TYPE_STATE_IN, + /**< State (private data) output */ + IA_CSS_TERMINAL_TYPE_STATE_OUT, + IA_CSS_TERMINAL_TYPE_PROGRAM, + IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT, + IA_CSS_N_TERMINAL_TYPES +} ia_css_terminal_type_t; + +#define IA_CSS_TERMINAL_TYPE_BITS 32 + +/* Temporary redirection needed to facilicate merging with the drivers + in a backwards compatible manner */ +#define IA_CSS_TERMINAL_TYPE_PARAM_CACHED IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN + +/* + * Dimensions of the data objects. Note that a C-style + * data order is assumed. Data stored by row. + */ +typedef enum ia_css_dimension { + /**< The number of columns, i.e. the size of the row */ + IA_CSS_COL_DIMENSION = 0, + /**< The number of rows, i.e. the size of the column */ + IA_CSS_ROW_DIMENSION = 1, + IA_CSS_N_DATA_DIMENSION = 2 +} ia_css_dimension_t; + +#define IA_CSS_N_COMMAND_COUNT (4) + +#ifndef PIPE_GENERATION +/* Don't include these complex enum structures in Genpipe, it can't handle and it does not need them */ +/* + * enum ia_css_isys_link_id. Lists the link IDs used by the FW for On The Fly feature + */ +typedef enum ia_css_isys_link_id { + IA_CSS_ISYS_LINK_OFFLINE = 0, + IA_CSS_ISYS_LINK_MAIN_OUTPUT = 1, + IA_CSS_ISYS_LINK_PDAF_OUTPUT = 2 +} ia_css_isys_link_id_t; +#define N_IA_CSS_ISYS_LINK_ID (IA_CSS_ISYS_LINK_PDAF_OUTPUT + 1) + +/* + * enum ia_css_data_barrier_link_id. Lists the link IDs used by the FW for data barrier feature + */ +typedef enum ia_css_data_barrier_link_id { + IA_CSS_DATA_BARRIER_LINK_MEMORY = N_IA_CSS_ISYS_LINK_ID, + N_IA_CSS_DATA_BARRIER_LINK_ID +} ia_css_data_barrier_link_id_t; + +#endif /* #ifndef PIPE_GENERATION */ +#endif /* __IA_CSS_TERMINAL_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h new file mode 100644 index 000000000000..a284d74bb4a6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h @@ -0,0 +1,99 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_H +#define __IA_CSS_PKG_DIR_H + +#include "ia_css_pkg_dir_storage_class.h" +#include "ia_css_pkg_dir_types.h" +#include "type_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +/* User is expected to call the verify function manually, + * other functions do not call it internally + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +int ia_css_pkg_dir_verify_header( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +enum ia_css_pkg_dir_version ia_css_pkg_dir_get_version( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_set_version( + ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version +); + + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_size( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_entry_get_version( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint8_t ia_css_pkg_dir_entry_get_type( + const ia_css_pkg_dir_entry_t *entry +); + +/* Get the address of the specified entry in the PKG_DIR + * Note: This function expects the complete PKG_DIR in the same memory space + * and the entries contains offsets and not addresses. + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +void *ia_css_pkg_dir_get_entry_address( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "ia_css_pkg_dir_impl.h" + +#endif + +#endif /* __IA_CSS_PKG_DIR_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h new file mode 100644 index 000000000000..ad194b0389eb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h @@ -0,0 +1,46 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IUNIT_H +#define __IA_CSS_PKG_DIR_IUNIT_H + +/* In bootflow, pkg_dir only supports upto 16 entries in pkg_dir + * pkg_dir_header + Psys_server pg + Isys_server pg + 13 Client pg + */ + +enum { + IA_CSS_PKG_DIR_SIZE = 16, + IA_CSS_PKG_DIR_ENTRIES = IA_CSS_PKG_DIR_SIZE - 1 +}; + +#define IUNIT_MAX_CLIENT_PKG_ENTRIES 13 + +/* Example assignment of unique identifiers for the FW components + * This should match the identifiers in the manifest + */ +enum ia_css_pkg_dir_entry_type { + IA_CSS_PKG_DIR_HEADER = 0, + IA_CSS_PKG_DIR_PSYS_SERVER_PG, + IA_CSS_PKG_DIR_ISYS_SERVER_PG, + IA_CSS_PKG_DIR_CLIENT_PG +}; + +/* Fixed entries in the package directory */ +enum ia_css_pkg_dir_index { + IA_CSS_PKG_DIR_PSYS_INDEX = 0, + IA_CSS_PKG_DIR_ISYS_INDEX = 1, + IA_CSS_PKG_DIR_CLIENT_0 = 2 +}; + +#endif /* __IA_CSS_PKG_DIR_IUNIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h new file mode 100644 index 000000000000..cb64172151f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_STORAGE_CLASS_H +#define __IA_CSS_PKG_DIR_STORAGE_CLASS_H + + +#include "storage_class.h" + +#ifndef __IA_CSS_PKG_DIR_INLINE__ +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C +#else +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PKG_DIR_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h new file mode 100644 index 000000000000..b024b3da2f9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_TYPES_H +#define __IA_CSS_PKG_DIR_TYPES_H + +#include "type_support.h" + +struct ia_css_pkg_dir_entry { + uint32_t address[2]; + uint32_t size; + uint16_t version; + uint8_t type; + uint8_t unused; +}; + +typedef void ia_css_pkg_dir_t; +typedef struct ia_css_pkg_dir_entry ia_css_pkg_dir_entry_t; + +/* The version field of the pkg_dir header defines + * if entries contain offsets or pointers + */ +/* This is temporary, until all pkg_dirs use pointers */ +enum ia_css_pkg_dir_version { + IA_CSS_PKG_DIR_POINTER, + IA_CSS_PKG_DIR_OFFSET +}; + + +#endif /* __IA_CSS_PKG_DIR_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/pkg_dir.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/pkg_dir.mk new file mode 100644 index 000000000000..32c8a68f3653 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/pkg_dir.mk @@ -0,0 +1,29 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PKG DIR + +PKG_DIR_DIR = $${MODULES_DIR}/pkg_dir +PKG_DIR_INTERFACE = $(PKG_DIR_DIR)/interface +PKG_DIR_SOURCES = $(PKG_DIR_DIR)/src + +PKG_DIR_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir.c +PKG_DIR_CPPFLAGS = -I$(PKG_DIR_INTERFACE) +PKG_DIR_CPPFLAGS += -I$(PKG_DIR_SOURCES) +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/../isp/kernels/io_ls/common +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu/$(FW_ABI_IPU_TYPES_VERSION) + +PKG_DIR_CREATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_create.c +PKG_DIR_UPDATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_update.c diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c new file mode 100644 index 000000000000..348b56833e06 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "storage_class.h" + +STORAGE_CLASS_INLINE int __ia_css_pkg_dir_avoid_warning_on_empty_file(void) +{ + return 0; +} + +#else +#include "ia_css_pkg_dir_impl.h" + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h new file mode 100644 index 000000000000..d5067d21398f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h @@ -0,0 +1,201 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IMPL_H +#define __IA_CSS_PKG_DIR_IMPL_H + +#include "ia_css_pkg_dir.h" +#include "ia_css_pkg_dir_int.h" +#include "error_support.h" +#include "type_support.h" +#include "assert_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + DECLARE_ERRVAL + struct ia_css_pkg_dir_entry *pkg_dir_header = NULL; + + verifexitval(pkg_dir != NULL, EFAULT); + + pkg_dir_header = (struct ia_css_pkg_dir_entry *)pkg_dir; + + /* First entry of the structure is the header, skip that */ + index++; + verifexitval(index < pkg_dir_header->size, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return NULL; + } + return &(pkg_dir_header[index]); +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +int ia_css_pkg_dir_verify_header(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + verifexitval(pkg_dir_header != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return -1; + } + return ((pkg_dir_header->address[0] == PKG_DIR_MAGIC_VAL_0) + && (pkg_dir_header->address[1] == PKG_DIR_MAGIC_VAL_1)) ? + 0 : -1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + uint32_t size = 0; + + verifexitval(pkg_dir_header != NULL, EFAULT); + size = pkg_dir_header->size; + verifexitval(size > 0, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return size - 1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +enum ia_css_pkg_dir_version +ia_css_pkg_dir_get_version(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + assert(pkg_dir_header != NULL); + return pkg_dir_header->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_set_version(ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 1; + } + pkg_dir_header->version = version; + return 0; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return sizeof(struct ia_css_pkg_dir_entry) * pkg_dir_header->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[0]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[1]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_size(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_entry_get_version(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint8_t ia_css_pkg_dir_entry_get_type(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->type; +} + + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +void *ia_css_pkg_dir_get_entry_address(const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + void *entry_blob = NULL; + const ia_css_pkg_dir_entry_t *pkg_dir_entry = + ia_css_pkg_dir_get_entry(pkg_dir, index-1); + + if ((pkg_dir_entry != NULL) && + (ia_css_pkg_dir_entry_get_size(pkg_dir_entry) > 0)) { + assert(ia_css_pkg_dir_entry_get_address_hi(pkg_dir_entry) == 0); + entry_blob = (void *)((char *)pkg_dir + + ia_css_pkg_dir_entry_get_address_lo(pkg_dir_entry)); + } + return entry_blob; +} + +#endif /* __IA_CSS_PKG_DIR_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h new file mode 100644 index 000000000000..203505fbee54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_INT_H +#define __IA_CSS_PKG_DIR_INT_H + +/* + * Package Dir structure as specified in CSE FAS + * + * PKG DIR Header + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * 0 "_IUPKDR_" + * 1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version of the Structure + * Size: Size of the entire table (including header) in 16 byte chunks + * Type: Must be 0 for header + * + * Figure 13: PKG DIR Header + * + * + * PKG DIR Entry + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * N Address/Offset + * N+1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version # of the Component + * Size: Size of the component in bytes + * Type: Component Identifier + */ + +#define PKG_DIR_SIZE_BITS 24 +#define PKG_DIR_TYPE_BITS 7 + +#define PKG_DIR_MAGIC_VAL_1 (('_' << 24) | ('I' << 16) | ('U' << 8) | 'P') +#define PKG_DIR_MAGIC_VAL_0 (('K' << 24) | ('D' << 16) | ('R' << 8) | '_') + +#endif /* __IA_CSS_PKG_DIR_INT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/port_env_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/port_env_struct.h new file mode 100644 index 000000000000..4d39a4739a8b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/port_env_struct.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PORT_ENV_STRUCT_H +#define __PORT_ENV_STRUCT_H + +struct port_env { + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __PORT_ENV_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue.h new file mode 100644 index 000000000000..b233ab3baf01 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue.h @@ -0,0 +1,40 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_H +#define __QUEUE_H + +#include "queue_struct.h" +#include "port_env_struct.h" + +/* + * SYS queues are created by the host + * SYS queues cannot be accessed through the queue interface + * To send data into a queue a send_port must be opened. + * To receive data from a queue, a recv_port must be opened. + */ + +/* return required buffer size for queue */ +unsigned int +sys_queue_buf_size(unsigned int size, unsigned int token_size); + +/* + * initialize a queue that can hold at least 'size' tokens of + * 'token_size' bytes. + */ +void +sys_queue_init(struct sys_queue *q, unsigned int size, + unsigned int token_size, struct sys_queue_res *res); + +#endif /* __QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue_struct.h new file mode 100644 index 000000000000..ef48fcfded2b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/queue_struct.h @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_STRUCT_H +#define __QUEUE_STRUCT_H + +/* queue description, shared between sender and receiver */ + +#include "type_support.h" + +#ifdef __VIED_CELL +typedef struct {uint32_t v[2]; } host_buffer_address_t; +#else +typedef uint64_t host_buffer_address_t; +#endif + +typedef uint32_t vied_buffer_address_t; + + +struct sys_queue { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* reg no in subsystem's regmem */ + unsigned int rd_reg; + unsigned int _align; +}; + +struct sys_queue_res { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int reg; +}; + +#endif /* __QUEUE_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port.h new file mode 100644 index 000000000000..cce253b26668 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port.h @@ -0,0 +1,34 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_H +#define __RECV_PORT_H + + +struct recv_port; +struct sys_queue; +struct port_env; + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env); + +unsigned int +recv_port_available(const struct recv_port *p); + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data); + + +#endif /* __RECV_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port_struct.h new file mode 100644 index 000000000000..52ec563b13cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/recv_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_STRUCT_H +#define __RECV_PORT_STRUCT_H + +#include "buffer_type.h" + +struct recv_port { + buffer_address buffer; /* address of buffer in DDR */ + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer located in regmem */ + unsigned int rd_reg; /* index read pointer located in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; /* address of memory containing regmem */ +}; + +#endif /* __RECV_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port.h new file mode 100644 index 000000000000..04a160f3f019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port.h @@ -0,0 +1,52 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_H +#define __SEND_PORT_H + + +/* + * A send port can be used to send tokens into a queue. + * The interface can be used on any type of processor (host, SP, ...) + */ + +struct send_port; +struct sys_queue; +struct port_env; + +/* + * Open a send port on a queue. After the port is opened, tokens can be sent + */ +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env); + +/* + * Determine how many tokens can be sent + */ +unsigned int +send_port_available(const struct send_port *p); + +/* + * Send a token via a send port. The function returns the number of + * tokens that have been sent: + * 1: the token was accepted + * 0: the token was not accepted (full queue) + * The size of a token is determined at initialization. + */ +unsigned int +send_port_transfer(const struct send_port *p, const void *data); + + +#endif /* __SEND_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port_struct.h new file mode 100644 index 000000000000..f834c62bc3db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/interface/send_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_STRUCT_H +#define __SEND_PORT_STRUCT_H + +#include "buffer_type.h" + +struct send_port { + buffer_address buffer; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer in regmem */ + unsigned int rd_reg; /* index of read pointer in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __SEND_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/port.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/port.mk new file mode 100644 index 000000000000..b3801247802e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/port.mk @@ -0,0 +1,31 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PORT + +PORT_DIR=$${MODULES_DIR}/port + +PORT_INTERFACE=$(PORT_DIR)/interface +PORT_SOURCES1=$(PORT_DIR)/src + +PORT_HOST_FILES += $(PORT_SOURCES1)/send_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/recv_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/queue.c + +PORT_HOST_CPPFLAGS += -I$(PORT_INTERFACE) + +PORT_FW_FILES += $(PORT_SOURCES1)/send_port.c +PORT_FW_FILES += $(PORT_SOURCES1)/recv_port.c + +PORT_FW_CPPFLAGS += -I$(PORT_INTERFACE) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/queue.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/queue.c new file mode 100644 index 000000000000..eeec99dfe2d0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/queue.c @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "queue.h" + +#include "regmem_access.h" +#include "port_env_struct.h" + +unsigned int sys_queue_buf_size(unsigned int size, unsigned int token_size) +{ + return (size + 1) * token_size; +} + +void +sys_queue_init(struct sys_queue *q, unsigned int size, unsigned int token_size, + struct sys_queue_res *res) +{ + unsigned int buf_size; + + q->size = size + 1; + q->token_size = token_size; + buf_size = sys_queue_buf_size(size, token_size); + + /* acquire the shared buffer space */ + q->host_address = res->host_address; + res->host_address += buf_size; + q->vied_address = res->vied_address; + res->vied_address += buf_size; + + /* acquire the shared read and writer pointers */ + q->wr_reg = res->reg; + res->reg++; + q->rd_reg = res->reg; + res->reg++; + +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/recv_port.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/recv_port.c new file mode 100644 index 000000000000..31b36e9ceafb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/recv_port.c @@ -0,0 +1,95 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "recv_port.h" +#include "port_env_struct.h" /* for port_env */ +#include "queue_struct.h" /* for sys_queue */ +#include "recv_port_struct.h" /* for recv_port */ +#include "buffer_access.h" /* for buffer_load, buffer_address */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_invalidate */ +#endif + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; + +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +recv_port_index(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(rd, i, p->size); +} + +unsigned int +recv_port_available(const struct recv_port *p) +{ + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(wr, -rd, p->size); +} + +STORAGE_CLASS_INLINE void +recv_port_copy(const struct recv_port *p, unsigned int i, void *data) +{ + unsigned int rd = recv_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (rd * token_size); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_invalidate((void *)HOST_ADDRESS(p->buffer), + token_size*p->size); +#endif + buffer_load(addr, data, token_size, p->mmid); +} + +STORAGE_CLASS_INLINE void +recv_port_release(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = recv_port_index(p, i); + + regmem_store_32(p->mem_addr, p->rd_reg, rd, p->ssid); +} + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data) +{ + if (!recv_port_available(p)) + return 0; + recv_port_copy(p, 0, data); + recv_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/send_port.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/send_port.c new file mode 100644 index 000000000000..8d1fba08c5d5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/port/src/send_port.c @@ -0,0 +1,94 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "send_port.h" +#include "queue_struct.h" /* for sys_queue */ +#include "send_port_struct.h" /* for send_port */ +#include "port_env_struct.h" /* for port_env */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "buffer_access.h" /* for buffer_store, buffer_address */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_flush */ +#endif + +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +send_port_index(const struct send_port *p, unsigned int i) +{ + unsigned int wr = regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(wr, i, p->size); +} + +unsigned int +send_port_available(const struct send_port *p) +{ + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(rd, -(wr+1), p->size); +} + +STORAGE_CLASS_INLINE void +send_port_copy(const struct send_port *p, unsigned int i, const void *data) +{ + unsigned int wr = send_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (wr * token_size); + + buffer_store(addr, data, token_size, p->mmid); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(addr), token_size); +#endif +} + +STORAGE_CLASS_INLINE void +send_port_release(const struct send_port *p, unsigned int i) +{ + unsigned int wr = send_port_index(p, i); + + regmem_store_32(p->mem_addr, p->wr_reg, wr, p->ssid); +} + +unsigned int +send_port_transfer(const struct send_port *p, const void *data) +{ + if (!send_port_available(p)) + return 0; + send_port_copy(p, 0, data); + send_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h new file mode 100644 index 000000000000..6b2387352ae3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h @@ -0,0 +1,43 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PRIVATE_PG_DATA_H +#define __IA_CSS_PSYS_PRIVATE_PG_DATA_H + +#include "ipu_device_acb_devices.h" +#include "ipu_device_gp_devices.h" +#include "type_support.h" +#include "vied_nci_acb_route_type.h" + +#define PRIV_CONF_INVALID 0xFF + +struct ia_css_psys_pg_buffer_information_s { + unsigned int buffer_base_addr; + unsigned int bpe; + unsigned int buffer_width; + unsigned int buffer_height; + unsigned int num_of_buffers; + unsigned int dfm_port_addr; +}; + +typedef struct ia_css_psys_pg_buffer_information_s ia_css_psys_pg_buffer_information_t; + +struct ia_css_psys_private_pg_data { + nci_acb_route_t acb_route[IPU_DEVICE_ACB_NUM_ACB]; + uint8_t psa_mux_conf[IPU_DEVICE_GP_PSA_MUX_NUM_MUX]; + uint8_t isa_mux_conf[IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX]; + ia_css_psys_pg_buffer_information_t input_buffer_info; +}; + +#endif /* __IA_CSS_PSYS_PRIVATE_PG_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h new file mode 100644 index 000000000000..eee1d6ab0a49 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h @@ -0,0 +1,107 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BXT_SPCTRL_TRACE_H +#define __IA_CSS_BXT_SPCTRL_TRACE_H + +#include "ia_css_trace.h" + +/* Not using 0 to identify wrong configuration being passed from + * the .mk file outside. + * Log levels not in the range below will cause a + * "No BXT_SPCTRL_TRACE_CONFIG Tracing level defined" + */ +#define BXT_SPCTRL_TRACE_LOG_LEVEL_OFF 1 +#define BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL 2 +#define BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG 3 + +/* BXT_SPCTRL and all the submodules in BXT_SPCTRL will have the + * default tracing level set to the BXT_SPCTRL_TRACE_CONFIG level. + * If not defined in the psysapi.mk fill it will be set by + * default to no trace (BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL) + */ +#define BXT_SPCTRL_TRACE_CONFIG_DEFAULT BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL + +#if !defined(BXT_SPCTRL_TRACE_CONFIG) +# define BXT_SPCTRL_TRACE_CONFIG BXT_SPCTRL_TRACE_CONFIG_DEFAULT +#endif + +/* BXT_SPCTRL Module tracing backend is mapped to TUNIT tracing for + * target platforms + */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(BXT_SPCTRL_TRACE_CONFIG)) + /* Module specific trace setting */ +# if BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_OFF + /* BXT_SPCTRL_TRACE_LOG_LEVEL_OFF */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED +# elif BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL + /* BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED +# elif BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG + /* BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED +# else +# error "No BXT_SPCTRL_TRACE_CONFIG Tracing level defined" +# endif +#else +# error "BXT_SPCTRL_TRACE_CONFIG not defined" +#endif + +/* Overriding submodules in BXT_SPCTRL with a specific tracing level */ +/* #define BXT_SPCTRL_DYNAMIC_TRACING_OVERRIDE TRACE_LOG_LEVEL_VERBOSE */ + +#endif /* __IA_CSS_BXT_SPCTRL_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/psys_server.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/psys_server.mk new file mode 100644 index 000000000000..c4462c984793 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/psys_server.mk @@ -0,0 +1,81 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PSYS_SERVER + +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk +include $(MODULES_DIR)/config/$(SUBSYSTEM)/subsystem_$(IPU_SYSVER).mk + +PSYS_SERVER_DIR=${MODULES_DIR}/psys_server + +# The watchdog should never be merged enabled +PSYS_SERVER_WATCHDOG_ENABLE ?= 0 + +PSYS_SERVER_INTERFACE=$(PSYS_SERVER_DIR)/interface +PSYS_SERVER_SOURCES=$(PSYS_SERVER_DIR)/src + +# PSYS API implementation files. Consider a new module for those to avoid +# having them together with firmware. +PSYS_SERVER_HOST_FILES += $${MODULES_DIR}/psysapi/device/src/ia_css_psys_device.c +PSYS_SERVER_HOST_FILES += $(PSYS_SERVER_SOURCES)/bxt_spctrl_process_group_cmd_impl.c + +PSYS_SERVER_HOST_CPPFLAGS += -I$(PSYS_SERVER_INTERFACE) + +PSYS_SERVER_HOST_CPPFLAGS += -DSSID=$(SSID) +PSYS_SERVER_HOST_CPPFLAGS += -DMMID=$(MMID) + + +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_cmd_queue_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_event_queue_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_init_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_process_group_fw.c + +# Files that server modules need to use +PSYS_SERVER_SUPPORT_FILES = $(PSYS_SERVER_SOURCES)/dev_access_conv/$(IPU_SYSVER)/ia_css_psys_server_dev_access_type_conv.c +PSYS_SERVER_SUPPORT_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_config.c + +# Include those to build the release firmware. Otherwise replace by test code. +PSYS_SERVER_RELEASE_FW_FILES = $(PSYS_SERVER_SOURCES)/psys_server.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_proxy.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dev_access.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_terminal_load.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_remote_obj_access.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dma_access.c +ifeq ($(HAS_DEC400), 1) +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dec400_access.c +endif +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SUPPORT_FILES) + +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_INTERFACE) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/$(IPU_SYSVER) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/$(PSYS_SERVER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/loader/$(PSYS_SERVER_LOADER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/access_blocker/$(PSYS_ACCESS_BLOCKER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/access_blocker/src + +PSYS_SERVER_FW_CPPFLAGS += -DSSID=$(SSID) +PSYS_SERVER_FW_CPPFLAGS += -DMMID=$(MMID) +PSYS_SERVER_FW_CPPFLAGS += -DHAS_DPCM=$(if $(HAS_DPCM),1,0) + +# PSYS server watchdog for debugging +ifeq ($(PSYS_SERVER_WATCHDOG_ENABLE), 1) + PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_watchdog.c + PSYS_SERVER_FW_CPPFLAGS += -DPSYS_SERVER_WATCHDOG_DEBUG +endif + +PSYS_SERVER_FW_CPPFLAGS += -D$(PSYS_HW_VERSION) + +PSYS_SERVER_FW_CPPFLAGS += -DENABLE_TPROXY=$(PSYS_SERVER_ENABLE_TPROXY) +PSYS_SERVER_FW_CPPFLAGS += -DENABLE_DEVPROXY=$(PSYS_SERVER_ENABLE_DEVPROXY) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c new file mode 100644 index 000000000000..6f8aea782464 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c @@ -0,0 +1,332 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_device.h" +#include "ia_css_psys_process_group_cmd_impl.h" +#include "ia_css_psysapi.h" +#include "ia_css_psys_terminal.h" +#include "ia_css_psys_process.h" +#include "ia_css_psys_process.psys.h" +#include "ia_css_psys_process_group.h" +#include "ia_css_psys_process_group.psys.h" +#include "ia_css_psys_program_group_manifest.h" +#include "type_support.h" +#include "error_support.h" +#include "misc_support.h" +#include "cpu_mem_support.h" +#include "ia_css_bxt_spctrl_trace.h" + +#if HAS_DUAL_CMD_CTX_SUPPORT +#define MAX_CLIENT_PGS 8 /* same as test_params.h */ +struct ia_css_process_group_context { + ia_css_process_group_t *pg; + bool secure; +}; +struct ia_css_process_group_context pg_contexts[MAX_CLIENT_PGS]; +static unsigned int num_of_pgs; + +STORAGE_CLASS_INLINE +struct ia_css_syscom_context *ia_css_process_group_get_context(ia_css_process_group_t *process_group) +{ + unsigned int i; + bool secure = false; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_get_context(): enter:\n"); + + for (i = 0; i < num_of_pgs; i++) { + if (pg_contexts[i].pg == process_group) { + secure = pg_contexts[i].secure; + break; + } + } + + IA_CSS_TRACE_1(BXT_SPCTRL, INFO, + "ia_css_process_group_get_context(): secure %d\n", secure); + return secure ? psys_syscom_secure : psys_syscom; +} + +int ia_css_process_group_store(ia_css_process_group_t *process_group, bool secure) +{ + IA_CSS_TRACE_2(BXT_SPCTRL, INFO, + "ia_css_process_group_store(): pg instance %d secure %d\n", num_of_pgs, secure); + + pg_contexts[num_of_pgs].pg = process_group; + pg_contexts[num_of_pgs].secure = secure; + num_of_pgs++; + return 0; +} +#else /* HAS_DUAL_CMD_CTX_SUPPORT */ +STORAGE_CLASS_INLINE +struct ia_css_syscom_context *ia_css_process_group_get_context(ia_css_process_group_t *process_group) +{ + NOT_USED(process_group); + + return psys_syscom; +} + +int ia_css_process_group_store(ia_css_process_group_t *process_group, bool secure) +{ + NOT_USED(process_group); + NOT_USED(secure); + + return 0; +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +int ia_css_process_group_on_create( + ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *program_group_manifest, + const ia_css_program_group_param_t *program_group_param) +{ + NOT_USED(process_group); + NOT_USED(program_group_manifest); + NOT_USED(program_group_param); + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_on_create(): enter:\n"); + + return 0; +} + +int ia_css_process_group_on_destroy( + ia_css_process_group_t *process_group) +{ + NOT_USED(process_group); + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_on_destroy(): enter:\n"); + + return 0; +} + +int ia_css_process_group_exec_cmd( + ia_css_process_group_t *process_group, + const ia_css_process_group_cmd_t cmd) +{ + int retval = -1; + ia_css_process_group_state_t state; + struct ia_css_psys_cmd_s psys_cmd; + bool cmd_queue_full; + unsigned int queue_id; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): enter:\n"); + + verifexit(process_group != NULL); + + state = ia_css_process_group_get_state(process_group); + + verifexit(state != IA_CSS_PROCESS_GROUP_ERROR); + verifexit(state < IA_CSS_N_PROCESS_GROUP_STATES); + + switch (cmd) { + case IA_CSS_PROCESS_GROUP_CMD_SUBMIT: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_SUBMIT:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_READY); + + /* External resource availability checks */ + verifexit(ia_css_can_process_group_submit(process_group)); + + process_group->state = IA_CSS_PROCESS_GROUP_BLOCKED; + break; + case IA_CSS_PROCESS_GROUP_CMD_START: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_START:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_BLOCKED); + + /* External resource state checks */ + verifexit(ia_css_can_process_group_start(process_group)); + + process_group->state = IA_CSS_PROCESS_GROUP_STARTED; + break; + case IA_CSS_PROCESS_GROUP_CMD_DISOWN: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_DISOWN:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_STARTED); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_START; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + verifexit(ia_css_process_group_print(process_group, NULL) == 0); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID, &psys_cmd); + verifexit(retval > 0); + break; + case IA_CSS_PROCESS_GROUP_CMD_STOP: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_STOP:\n"); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_STOP; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + queue_id = ia_css_process_group_get_base_queue_id(process_group); + verifexit(queue_id < IA_CSS_N_PSYS_CMD_QUEUE_ID); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + queue_id, &psys_cmd); + verifexit(retval > 0); + break; + case IA_CSS_PROCESS_GROUP_CMD_ABORT: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_ABORT:\n"); + + /* Once the flushing of shared buffers is fixed this verifexit + * should be changed to be state = IA_CSS_PROCESS_GROUP_STARTED + */ + verifexit(state == IA_CSS_PROCESS_GROUP_BLOCKED); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_ABORT; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_DEVICE_ID, &psys_cmd); + verifexit(retval > 0); + break; + default: + verifexit(false); + break; + } + + retval = 0; +EXIT: + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_process_group_exec_cmd failed (%i)\n", retval); + } + return retval; +} + +STORAGE_CLASS_INLINE int enqueue_buffer_set_cmd( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset, + uint16_t command + ) +{ + int retval = -1; + struct ia_css_psys_cmd_s psys_cmd; + bool cmd_queue_full; + unsigned int queue_id; + + verifexit(ia_css_process_group_get_state(process_group) + == IA_CSS_PROCESS_GROUP_STARTED); + + verifexit(queue_offset < + ia_css_process_group_get_num_queues(process_group)); + + queue_id = + ia_css_process_group_get_base_queue_id(process_group) + + queue_offset; + verifexit(queue_id < IA_CSS_N_PSYS_CMD_QUEUE_ID); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), queue_id); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = command; + psys_cmd.msg = 0; + psys_cmd.context_handle = + ia_css_buffer_set_get_ipu_address(buffer_set); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), queue_id, &psys_cmd); + verifexit(retval > 0); + + retval = 0; + +EXIT: + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "enqueue_buffer_set failed (%i)\n", retval); + } + return retval; +} + +int ia_css_enqueue_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_enqueue_buffer_set():\n"); + retval = enqueue_buffer_set_cmd( + process_group, + buffer_set, + queue_offset, + IA_CSS_PROCESS_GROUP_CMD_RUN); + + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_enqueue_buffer_set failed (%i)\n", retval); + } + return retval; +} + +int ia_css_enqueue_param_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *param_buffer_set) +{ +#if (HAS_LATE_BINDING_SUPPORT == 1) + int retval = -1; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_enqueue_param_buffer_set():\n"); + + retval = enqueue_buffer_set_cmd( + process_group, + param_buffer_set, + IA_CSS_PSYS_LATE_BINDING_QUEUE_OFFSET, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT); + + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_enqueue_param_buffer_set failed (%i)\n", retval); + } +#else + int retval = -1; + + NOT_USED(process_group); + NOT_USED(param_buffer_set); + IA_CSS_TRACE_0(BXT_SPCTRL, ERROR, + "ia_css_enqueue_param_buffer_set failed, no late binding supported\n"); +#endif /* (HAS_LATE_BINDING_SUPPORT == 1) */ + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h new file mode 100644 index 000000000000..6ccca1d9b69e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h @@ -0,0 +1,418 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_H +#define __IA_CSS_PROGRAM_GROUP_DATA_H + +#include "ia_css_psys_data_storage_class.h" + +/*! \file */ + +/** @file ia_css_program_group_data.h + * + * Define the data objects that are passed to the process groups + * i.e. frames and matrices with their sub-structures + * + * The data objects are separate from the process group terminal, + * although they are stored by value rather than by reference and + * make the process group terminal dependendent on its definition + * + * This frame definition overloads the current CSS frame definition + * they are the same object, just a slightly different implementation + */ + +#include /* vied_vaddress_t */ + +#include +#include "ia_css_program_group_data_defs.h" /* ia_css_frame_format_type */ + +#include "ia_css_terminal_defs.h" + +/* + * Frame buffer state used for sequencing + * (see FAS 5.5.3) + * + * The buffer can be in DDR or a handle to a stream + */ +typedef enum ia_css_buffer_state { + IA_CSS_BUFFER_NULL = 0, + IA_CSS_BUFFER_UNDEFINED, + IA_CSS_BUFFER_EMPTY, + IA_CSS_BUFFER_NONEMPTY, + IA_CSS_BUFFER_FULL, + IA_CSS_N_BUFFER_STATES +} ia_css_buffer_state_t; + +#define IA_CSS_BUFFER_STATE_IN_BITS 32 + +/* + * Pointer state used to signal MMU invalidation + */ +typedef enum ia_css_pointer_state { + IA_CSS_POINTER_INVALID = 0, + IA_CSS_POINTER_VALID, + IA_CSS_N_POINTER_STATES +} ia_css_pointer_state_t; + +#define IA_CSS_POINTER_STATE_IN_BITS 32 + +/* + * Access direction needed to select the access port + */ +typedef enum ia_css_access_type { + IA_CSS_ACCESS_LOCKED = 0, + IA_CSS_ACCESS_READ, + IA_CSS_ACCESS_WRITE, + IA_CSS_ACCESS_MODIFY, + IA_CSS_N_ACCESS_TYPES +} ia_css_access_type_t; + +#define IA_CSS_ACCESS_TYPE_IN_BITS 32 + +/* + * Access attribute needed to select the access port + * - public : snooped + * - private: non-snooped + * Naming is a bit awkward, lack of inspiration + */ +typedef enum ia_css_access_scope { + IA_CSS_ACCESS_PRIVATE = 0, + IA_CSS_ACCESS_PUBLIC, + IA_CSS_N_ACCESS_SCOPES +} ia_css_access_scopes_t; + +#define IA_CSS_ACCESS_SCOPES_IN_BITS 32 + +#define IA_CSS_N_FRAME_PLANES 6 + +#define IA_CSS_FRAME_FORMAT_BITMAP_BITS 64 +typedef uint64_t ia_css_frame_format_bitmap_t; + +typedef struct ia_css_param_frame_descriptor_s ia_css_param_frame_descriptor_t; +typedef struct ia_css_param_frame_s ia_css_param_frame_t; + +typedef struct ia_css_frame_descriptor_s ia_css_frame_descriptor_t; +typedef struct ia_css_frame_s ia_css_frame_t; +typedef struct ia_css_fragment_descriptor_s ia_css_fragment_descriptor_t; + +typedef struct ia_css_stream_s ia_css_stream_t; + + +#define N_UINT64_IN_STREAM_STRUCT 1 + +#define IA_CSS_STREAM_STRUCT_BITS \ + (N_UINT64_IN_STREAM_STRUCT * 64) + +struct ia_css_stream_s { + uint64_t dummy; +}; + +struct ia_css_param_frame_descriptor_s { + uint16_t size; /**< Size of the descriptor */ + uint32_t buffer_count; /**< Number of parameter buffers */ +}; + +struct ia_css_param_frame_s { + /*< Base virtual addresses to parameters in subsystem virtual + * memory space + */ + vied_vaddress_t *data; +}; + +#define N_UINT32_IN_FRAME_DESC_STRUCT \ + (1 + IA_CSS_N_FRAME_PLANES + (IA_CSS_N_DATA_DIMENSION - 1)) +#define N_UINT16_IN_FRAME_DESC_STRUCT (1 + IA_CSS_N_DATA_DIMENSION) +#define N_UINT8_IN_FRAME_DESC_STRUCT 3 +#define N_PADDING_UINT8_IN_FRAME_DESC_STRUCT 3 + +#define IA_CSS_FRAME_DESCRIPTOR_STRUCT_BITS \ + (IA_CSS_FRAME_FORMAT_TYPE_BITS \ + + (N_UINT32_IN_FRAME_DESC_STRUCT * 32) \ + + (N_UINT16_IN_FRAME_DESC_STRUCT * 16) \ + + (N_UINT8_IN_FRAME_DESC_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_FRAME_DESC_STRUCT * 8)) + +/* + * Structure defining the frame (size and access) properties for + * inbuild types only. + * + * The inbuild types like FourCC, MIPI and CSS private types are supported + * by FW all other types are custom types which interpretation must be encoded + * on the buffer itself or known by the source and sink + */ +struct ia_css_frame_descriptor_s { + /**< Indicates if this is a generic type or inbuild with + * variable size descriptor + */ + ia_css_frame_format_type_t frame_format_type; + /**< Number of data planes (pointers) */ + uint32_t plane_count; + /**< Plane offsets accounting for fragments */ + uint32_t plane_offsets[IA_CSS_N_FRAME_PLANES]; + /**< Physical size aspects */ + uint32_t stride[IA_CSS_N_DATA_DIMENSION - 1]; + /**< Logical dimensions */ + uint16_t dimension[IA_CSS_N_DATA_DIMENSION]; + /**< Size of this descriptor */ + uint16_t size; + /**< Bits per pixel */ + uint8_t bpp; + /**< Bits per element */ + uint8_t bpe; + /**< 1 if terminal uses compressed datatype, 0 otherwise */ + uint8_t is_compressed; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_DESC_STRUCT]; +}; + +#define N_UINT32_IN_FRAME_STRUCT 2 +#define N_PADDING_UINT8_IN_FRAME_STRUCT 4 + +#define IA_CSS_FRAME_STRUCT_BITS \ + (IA_CSS_BUFFER_STATE_IN_BITS \ + + IA_CSS_ACCESS_TYPE_IN_BITS \ + + IA_CSS_POINTER_STATE_IN_BITS \ + + IA_CSS_ACCESS_SCOPES_IN_BITS \ + + VIED_VADDRESS_BITS \ + + (N_UINT32_IN_FRAME_STRUCT * 32) \ + + (N_PADDING_UINT8_IN_FRAME_STRUCT * 8)) + + +/* + * Main frame structure holding the main store and auxilary access properties + * the "pointer_state" and "access_scope" should be encoded on the + * "vied_vaddress_t" type + */ +struct ia_css_frame_s { + /**< State of the frame for purpose of sequencing */ + ia_css_buffer_state_t buffer_state; + /**< Access direction, may change when buffer state changes */ + ia_css_access_type_t access_type; + /**< State of the pointer for purpose of embedded MMU coherency */ + ia_css_pointer_state_t pointer_state; + /**< Access to the pointer for purpose of host cache coherency */ + ia_css_access_scopes_t access_scope; + /**< Base virtual address to data in subsystem virtual memory space */ + vied_vaddress_t data; + /**< Offset to buffer address within external buffer set structure */ + uint32_t data_index; + /**< Total allocation size in bytes */ + uint32_t data_bytes; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_STRUCT]; +}; + +#define N_UINT16_IN_FRAGMENT_DESC_STRUCT (3 * IA_CSS_N_DATA_DIMENSION) +#define N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT 4 + +#define IA_CSS_FRAGMENT_DESCRIPTOR_STRUCT_BITS \ + ((N_UINT16_IN_FRAME_DESC_STRUCT * 16) \ + + (N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT * 8)) + +/* + * Structure defining the fragment (size and access) properties. + * + * All cropping and padding effects are described by the difference between + * the frame size and its location and the fragment size(s) and location(s) + */ +struct ia_css_fragment_descriptor_s { + /**< Logical dimensions of the fragment */ + uint16_t dimension[IA_CSS_N_DATA_DIMENSION]; + /**< Logical location of the fragment in the frame */ + uint16_t index[IA_CSS_N_DATA_DIMENSION]; + /**< Fractional start (phase) of the fragment in the access unit */ + uint16_t offset[IA_CSS_N_DATA_DIMENSION]; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT]; +}; + + +/*! Print the frame object to file/stream + + @param frame[in] frame object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_print( + const ia_css_frame_t *frame, void *fid); + +/*! Get the data buffer handle from the frame object + +@param frame[in] frame object + +@return buffer pointer, VIED_NULL on error +*/ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +const vied_vaddress_t *ia_css_frame_get_buffer_host_virtual_address( + const ia_css_frame_t *frame); + +/*! Get the data buffer handle from the frame object + + @param frame[in] frame object + + @return buffer pointer, VIED_NULL on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +vied_vaddress_t ia_css_frame_get_buffer(const ia_css_frame_t *frame); + +/*! Set the data buffer handle on the frame object + + @param frame[in] frame object + @param buffer[in] buffer pointer + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_buffer( + ia_css_frame_t *frame, vied_vaddress_t buffer); + +/*! Get the data buffer index in the frame object + + @param frame[in] frame object + + @return data buffer index on success, -1 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_get_data_index( + const ia_css_frame_t *frame); + +/*! Set the data buffer index in the frame object + + @param frame[in] frame object + @param data_index[in] data buffer index + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_data_index( + ia_css_frame_t *frame, + unsigned int data_index); + +/*! Set the data buffer size on the frame object + + @param frame[in] frame object + @param size[in] number of data bytes + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_data_bytes( + ia_css_frame_t *frame, unsigned size); + +/*! Get the data buffer state from the frame object + + @param frame[in] frame object + + @return buffer state, limit value on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_buffer_state_t ia_css_frame_get_buffer_state( + const ia_css_frame_t *frame); + +/*! Set the data buffer state of the frame object + + @param frame[in] frame object + @param buffer_state[in] buffer state + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_buffer_state(ia_css_frame_t *frame, + const ia_css_buffer_state_t buffer_state); + +/*! Get the data pointer state from the frame object + + @param frame[in] frame object + + @return pointer state, limit value on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_pointer_state_t ia_css_frame_get_pointer_state( + const ia_css_frame_t *frame); + +/*! Set the data pointer state of the frame object + + @param frame[in] frame object + @param pointer_state[in] pointer state + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_pointer_state(ia_css_frame_t *frame, + const ia_css_pointer_state_t pointer_state); + +/*! Print the frame descriptor object to file/stream + + @param frame_descriptor[in] frame descriptor object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_descriptor_print( + const ia_css_frame_descriptor_t *frame_descriptor, void *fid); + +/*! Print the fragment descriptor object to file/stream + + @param fragment_descriptor[in] fragment descriptor object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_fragment_descriptor_print( + const ia_css_fragment_descriptor_t *fragment_descriptor, void *fid); + +/*! Compute the bitmap for the frame format type + + @param frame_format_type[in] frame format type + + @return 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_frame_format_bitmap_t ia_css_frame_format_bit_mask( + const ia_css_frame_format_type_t frame_format_type); + +/*! clear frame format bitmap + + @return cleared bitmap + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_frame_format_bitmap_t ia_css_frame_format_bitmap_clear(void); + + +/*! Compute the size of storage required for the data descriptor object + * on a terminal + *@param plane_count[in] The number of data planes in the buffer + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +size_t ia_css_sizeof_frame_descriptor( + const uint8_t plane_count); +/*! Compute the size of storage required for the kernel parameter descriptor + * object on a terminal + + @param section_count[in] The number of parameter sections in the buffer + + @return 0 on error + */ +extern size_t ia_css_sizeof_kernel_param_descriptor( + const uint16_t section_count); + +#ifdef __IA_CSS_PSYS_DATA_INLINE__ +#include "ia_css_program_group_data_impl.h" +#endif /* __IA_CSS_PSYS_DATA_INLINE__ */ + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h new file mode 100644 index 000000000000..3f177a19b98b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h @@ -0,0 +1,196 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H +#define __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H + + +/* + * Pre-defined frame format + * + * Those formats have inbuild support of traffic + * and access functions + * + * Note that the formats are for terminals, so there + * is no distinction between input and output formats + * - Custom formats with ot without descriptor + * - 4CC formats such as YUV variants + * - MIPI (line) formats as produced by CSI receivers + * - MIPI (sensor) formats such as Bayer or RGBC + * - CSS internal formats (private types) + * - CSS parameters (type 1 - 6) + */ +#define IA_CSS_FRAME_FORMAT_TYPE_BITS 32 +typedef enum ia_css_frame_format_type { + IA_CSS_DATA_CUSTOM_NO_DESCRIPTOR = 0, + IA_CSS_DATA_CUSTOM, + + /* 12 bit YUV 411, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV11, + /* bpp bit YUV 420, Y, U, V 3-plane (bpp/1.5 bpe) */ + IA_CSS_DATA_FORMAT_YUV420, + /* 12 bit YUV 420, Y, V, U 3-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_YV12, + /* 12 bit YUV 420, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV12, + /* 16 bit YUV 420, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV12_16, + /* 12 bit YUV 420, Intel proprietary tiled format, TileY */ + IA_CSS_DATA_FORMAT_NV12_TILEY, + /* 12 bit YUV 420, Y, VU 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV21, + /* bpp bit YUV 422, Y, U, V 3-plane (bpp/2 bpe) */ + IA_CSS_DATA_FORMAT_YUV422, + /* 16 bit YUV 422, Y, V, U 3-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_YV16, + /* 16 bit YUV 422, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV16, + /* 16 bit YUV 422, Y, VU 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV61, + /* 16 bit YUV 422, UYVY 1-plane interleaved (8 bit per element) */ + IA_CSS_DATA_FORMAT_UYVY, + /* 16 bit YUV 422, YUYV 1-plane interleaved (8 bit per element) */ + IA_CSS_DATA_FORMAT_YUYV, + /* bpp bit YUV 444, Y, U, V 3-plane (bpp/3 bpe) */ + IA_CSS_DATA_FORMAT_YUV444, + /* 8 bit monochrome plane */ + IA_CSS_DATA_FORMAT_Y800, + + /* 5-6-5 bit packed (1-plane) RGB (16bpp, ~5 bpe) */ + IA_CSS_DATA_FORMAT_RGB565, + /* 24 bit RGB, 3 planes (8 bit per element) */ + IA_CSS_DATA_FORMAT_RGB888, + /* 32 bit RGB-Alpha, 1 plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_RGBA888, + + /* bpp bit raw, [[Gr, R];[B, Gb]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_GRBG, + /* bpp bit raw, [[R, Gr];[Gb, B]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_RGGB, + /* bpp bit raw, [[B, Gb];[Gr, R]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_BGGR, + /* bpp bit raw, [[Gb, B];[R, Gr]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_GBRG, + + /* bpp bit (NV12) YUV 420, Y, UV 2-plane derived 3-line, + * 2-Y, 1-UV (bpp/1.5 bpe): M420 format + */ + IA_CSS_DATA_FORMAT_YUV420_LINE, + /* Deprecated RAW, 1 plane */ + IA_CSS_DATA_FORMAT_RAW, + /* Deprecated RAW, 1 plane, packed */ + IA_CSS_DATA_FORMAT_RAW_PACKED, + /* Internal, for advanced ISP */ + IA_CSS_DATA_FORMAT_QPLANE6, + /* 1D byte stream, used for jpeg 1-plane */ + IA_CSS_DATA_FORMAT_BINARY_8, + /* Deprecated MIPI frame, 1D byte stream 1 plane */ + IA_CSS_DATA_FORMAT_MIPI, + /* 12 bit [[YY];[UYVY]] 1-plane interleaved 2-line + * (8 bit per element) + */ + IA_CSS_DATA_FORMAT_MIPI_YUV420_8, + /* 15 bit [[YY];[UYVY]] 1-plane interleaved 2-line + * (10 bit per element) + */ + IA_CSS_DATA_FORMAT_MIPI_YUV420_10, + /* 12 bit [[UY];[VY]] 1-plane interleaved 2-line (8 bit per element) */ + IA_CSS_DATA_FORMAT_MIPI_LEGACY_YUV420_8, + + /* Type 1-5 parameter, not fragmentable */ + IA_CSS_DATA_GENERIC_PARAMETER, + /* Video stabilisation Type 6 parameter, fragmentable */ + IA_CSS_DATA_DVS_PARAMETER, + /* Video stabilisation Type 6 parameter, coordinates */ + IA_CSS_DATA_DVS_COORDINATES, + /* Dead Pixel correction Type 6 parameter, fragmentable */ + IA_CSS_DATA_DPC_PARAMETER, + /* Lens Shading Correction Type 6 parameter, fragmentable */ + IA_CSS_DATA_LSC_PARAMETER, + /* 3A statistics output HI. */ + IA_CSS_DATA_S3A_STATISTICS_HI, + /* 3A statistics output LO. */ + IA_CSS_DATA_S3A_STATISTICS_LO, + /* histogram output */ + IA_CSS_DATA_S3A_HISTOGRAM, + /* GammaStar grid */ + IA_CSS_DATA_GAMMASTAR_GRID, + + /* Gr R B Gb Gr R B Gb in PIXELS (also called isys interleaved) */ + IA_CSS_DATA_FORMAT_BAYER_LINE_INTERLEAVED, + /* Gr R B Gb Gr R B Gb in VECTORS (VCC IMAGE, ISP NWAY depentdent) */ + IA_CSS_DATA_FORMAT_BAYER_VECTORIZED, + /* Gr R Gr R ... | B Gb B Gb .. in VECTORS (ISP NWAY depentdent) */ + IA_CSS_DATA_FORMAT_BAYER_GRBG_VECTORIZED, + + /* 16 bit YUV 420, Y even plane, Y uneven plane, + * UV plane vector interleaved + */ + IA_CSS_DATA_FORMAT_YUV420_VECTORIZED, + /* 16 bit YUV 420, YYUVYY vector interleaved */ + IA_CSS_DATA_FORMAT_YYUVYY_VECTORIZED, + + /* 12 bit YUV 420, Intel proprietary tiled format, TileYf */ + IA_CSS_DATA_FORMAT_NV12_TILEYF, + + /*Y samples appear first in the memory. All Y samples are array of WORDs; + * even number of lines ; + * Surface stride can be larger than the width of Y plane. + * This array is followed immediately by chroma array. + * Chroma array is an array of WORDs, with interleaved U/V samples. + * If the interleaved U/V plane is addresses as an * array of DWORDs, + * the least significant word contains U sample. The stride of the + * interleaved U/V plane is equal to Y plane. 10 bit data. + */ + IA_CSS_DATA_FORMAT_P010, + + /* MSB aligned version of P010*/ + IA_CSS_DATA_FORMAT_P010_MSB, + + /* P016/P012 Y samples appear first in the memory. + * All Y samples are array of WORDs; + * even number of lines ; + * Surface stride can be larger than the width of Y plane. + * This array is followed immediately by chroma array. + * Chroma array is an array of WORDs, with interleaved U/V samples. + * If the interleaved U/V plane is addresses as an * array of DWORDs, + * the least significant word contains U sample. The stride of the + * interleaved U/V plane is equal to Y plane. 12 bit data. + */ + IA_CSS_DATA_FORMAT_P016, + + /* MSB aligned version of P016*/ + IA_CSS_DATA_FORMAT_P016_MSB, + + /* TILEYYf representation of P010*/ + IA_CSS_DATA_FORMAT_P010_TILEYF, + + /* TILEYYf representation of P010 MSB aligned*/ + IA_CSS_DATA_FORMAT_P010_MSB_TILEYF, + + /* TILEYYf representation of P016*/ + IA_CSS_DATA_FORMAT_P016_TILEYF, + + /* TILEYYf representation of P016 MSB aligned*/ + IA_CSS_DATA_FORMAT_P016_MSB_TILEYF, + + /* consists of L and R PDAF pixel pairs. + * L and R can be interleaved or not. 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_PAF, + + IA_CSS_N_FRAME_FORMAT_TYPES +} ia_css_frame_format_type_t; + + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h new file mode 100644 index 000000000000..6a4e3a28e533 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DATA_STORAGE_CLASS_H +#define __IA_CSS_PSYS_DATA_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_DATA_INLINE__ +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_DATA_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h new file mode 100644 index 000000000000..49afed9ce9df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DATA_TRACE_H +#define __IA_CSS_PSYS_DATA_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DATA_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DATA_TRACING_OVERRIDE)) + #define PSYS_DATA_TRACE_LEVEL_CONFIG PSYS_DATA_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DATA_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DATA_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DATA_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DATA_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c new file mode 100644 index 000000000000..edf3e55e6c39 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c @@ -0,0 +1,26 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_data_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifdef __IA_CSS_PSYS_DATA_INLINE__ +STORAGE_CLASS_INLINE int +__ia_css_program_group_data_avoid_warning_on_empty_file(void) { return 0; } +#else /* __IA_CSS_PSYS_DATA_INLINE__ */ +#include "ia_css_program_group_data_impl.h" +#endif /* __IA_CSS_PSYS_DATA_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h new file mode 100644 index 000000000000..f08a057e4480 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h @@ -0,0 +1,455 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H +#define __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H + +#include "ia_css_program_group_data.h" +#include "ia_css_psys_data_trace.h" +#include "ia_css_terminal_defs.h" +#include /* for verifexit */ +#include /* for COMPILATION_ERROR_IF */ +#include /* for NOT_USED */ + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_print( + const ia_css_frame_t *frame, void *fid) +{ + int retval = -1; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, "ia_css_frame_print(): enter:\n"); + + verifexit(frame != NULL); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbuffer = %d\n", ia_css_frame_get_buffer(frame)); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbuffer_state = %d\n", ia_css_frame_get_buffer_state(frame)); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tbuffer_state = %s\n", + * ia_css_buffer_state_string(ia_css_frame_get_buffer_state(frame))); + */ + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tpointer_state = %d\n", ia_css_frame_get_pointer_state(frame)); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tpointer_state = %s\n", + * ia_css_pointer_state_string(ia_css_frame_get_pointer_state(frame))); + */ + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tdata_bytes = %d\n", frame->data_bytes); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +const vied_vaddress_t *ia_css_frame_get_buffer_host_virtual_address( + const ia_css_frame_t *frame) { + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer_host_virtual_address(): enter:\n"); + + verifexit(frame != NULL); + return &(frame->data); + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer_host_virtual_address invalid argument\n"); + } + return NULL; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +vied_vaddress_t ia_css_frame_get_buffer( + const ia_css_frame_t *frame) +{ + vied_vaddress_t buffer = VIED_NULL; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer(): enter:\n"); + + verifexit(frame != NULL); + buffer = frame->data; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer invalid argument\n"); + } + return buffer; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_buffer( + ia_css_frame_t *frame, + vied_vaddress_t buffer) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_buffer(): enter:\n"); + + verifexit(frame != NULL); + frame->data = buffer; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_buffer failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_get_data_index( + const ia_css_frame_t *frame) +{ + int data_index = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_data_index(): enter:\n"); + + verifexit(frame != NULL); + + data_index = frame->data_index; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_data_index invalid argument\n"); + } + return data_index; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_data_index( + ia_css_frame_t *frame, + unsigned int data_index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_data_index(): enter:\n"); + + verifexit(frame != NULL); + + frame->data_index = data_index; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_data_index failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_data_bytes( + ia_css_frame_t *frame, + unsigned int size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_data_bytes(): enter:\n"); + + verifexit(frame != NULL); + frame->data_bytes = size; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_data_bytes failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_buffer_state_t ia_css_frame_get_buffer_state( + const ia_css_frame_t *frame) +{ + ia_css_buffer_state_t buffer_state = IA_CSS_N_BUFFER_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer_state(): enter:\n"); + + verifexit(frame != NULL); + buffer_state = frame->buffer_state; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer_state invalid argument\n"); + } + return buffer_state; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_buffer_state( + ia_css_frame_t *frame, + const ia_css_buffer_state_t buffer_state) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_buffer_state(): enter:\n"); + + verifexit(frame != NULL); + frame->buffer_state = buffer_state; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_buffer_state failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_pointer_state_t ia_css_frame_get_pointer_state( + const ia_css_frame_t *frame) +{ + ia_css_pointer_state_t pointer_state = IA_CSS_N_POINTER_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_pointer_state(): enter:\n"); + + verifexit(frame != NULL); + pointer_state = frame->pointer_state; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_pointer_state invalid argument\n"); + } + return pointer_state; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_pointer_state( + ia_css_frame_t *frame, + const ia_css_pointer_state_t pointer_state) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_pointer_state(): enter:\n"); + + verifexit(frame != NULL); + frame->pointer_state = pointer_state; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_pointer_state failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_descriptor_print( + const ia_css_frame_descriptor_t *frame_descriptor, + void *fid) +{ + int retval = -1; + int i; + uint8_t frame_plane_count; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_frame_descriptor_print(): enter:\n"); + + COMPILATION_ERROR_IF(IA_CSS_N_DATA_DIMENSION <= 0); + + verifexit(frame_descriptor != NULL); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_frame_descriptor_print(): enter:\n"); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tframe_format_type = %d\n", + frame_descriptor->frame_format_type); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tframe_format_type = %s\n", + * ia_css_frame_format_string(frame_descriptor->frame_format_type)); + */ + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbpp = %d\n", frame_descriptor->bpp); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbpe = %d\n", frame_descriptor->bpe); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tis_compressed = %d\n", frame_descriptor->is_compressed); + + frame_plane_count = IA_CSS_N_FRAME_PLANES; + /* frame_plane_count = + * ia_css_frame_plane_count(frame_descriptor->frame_format_type); + */ + + verifexit(frame_plane_count > 0); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tplane_offsets[%d]: [\n", frame_plane_count); + for (i = 0; i < (int)frame_plane_count - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->plane_offsets[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d ]\n", frame_descriptor->plane_offsets[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tdimension[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->dimension[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", frame_descriptor->dimension[i]); + + COMPILATION_ERROR_IF(0 > (IA_CSS_N_DATA_DIMENSION - 2)); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tstride[%d] = {\n", IA_CSS_N_DATA_DIMENSION - 1); + i = 0; + if (IA_CSS_N_DATA_DIMENSION > 2) { + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 2; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->stride[i]); + } + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", frame_descriptor->stride[i]); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_descriptor_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_fragment_descriptor_print( + const ia_css_fragment_descriptor_t *fragment_descriptor, + void *fid) +{ + int retval = -1; + int i; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_fragment_descriptor_print(): enter:\n"); + + verifexit(fragment_descriptor != NULL); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "dimension[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->dimension[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", fragment_descriptor->dimension[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "index[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->index[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", fragment_descriptor->index[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "offset[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->offset[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\t%4d }\n", + fragment_descriptor->offset[i]); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_fragment_descriptor_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_frame_format_bitmap_t ia_css_frame_format_bit_mask( + const ia_css_frame_format_type_t frame_format_type) +{ + ia_css_frame_format_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_format_bit_mask(): enter:\n"); + + if ((frame_format_type < IA_CSS_N_FRAME_FORMAT_TYPES) && + (frame_format_type < IA_CSS_FRAME_FORMAT_BITMAP_BITS)) { + bit_mask = (ia_css_frame_format_bitmap_t)1 << frame_format_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_format_bit_mask invalid argument\n"); + } + + return bit_mask; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_frame_format_bitmap_t ia_css_frame_format_bitmap_clear(void) +{ + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_format_bitmap_clear(): enter:\n"); + + return 0; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +size_t ia_css_sizeof_frame_descriptor( + const uint8_t plane_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_sizeof_frame_descriptor(): enter:\n"); + + verifexit(plane_count > 0); + size += sizeof(ia_css_frame_descriptor_t); + size += plane_count * sizeof(uint32_t); + +EXIT: + if (0 == plane_count) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_sizeof_frame_descriptor invalid argument\n"); + } + return size; +} + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/bxtB0/ia_css_psys_transport_dep.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/bxtB0/ia_css_psys_transport_dep.h new file mode 100644 index 000000000000..7bb145c1b183 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/bxtB0/ia_css_psys_transport_dep.h @@ -0,0 +1,35 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TRANSPORT_DEP_H +#define __IA_CSS_PSYS_TRANSPORT_DEP_H + +/* + * The ID's of the Psys specific queues. + */ +typedef enum ia_css_psys_cmd_queues { + /**< The in-order queue for scheduled process groups */ + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID = 0, + /**< The in-order queue for commands changing psys or + * process group state + */ + IA_CSS_PSYS_CMD_QUEUE_DEVICE_ID, + /**< An in-order queue for dedicated PPG commands */ + IA_CSS_PSYS_CMD_QUEUE_PPG0_COMMAND_ID, + /**< An in-order queue for dedicated PPG commands */ + IA_CSS_PSYS_CMD_QUEUE_PPG1_COMMAND_ID, + IA_CSS_N_PSYS_CMD_QUEUE_ID +} ia_css_psys_cmd_queue_ID_t; + +#endif /* __IA_CSS_PSYS_TRANSPORT_DEP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h new file mode 100644 index 000000000000..dc8fa531b11e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h @@ -0,0 +1,516 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DEVICE_H +#define __IA_CSS_PSYS_DEVICE_H + +#include "ia_css_psys_init.h" +#include "ia_css_psys_transport.h" + +/*! \file */ + +/** @file ia_css_psys_device.h + * + * Define the interface to open the psys specific communication layer + * instance + */ + +#include /* vied_vaddress_t */ + +#include +#include + +#include +#include + +#define IA_CSS_PSYS_STATE_READY_PATTERN (0xF7F7F7F7) +#define IA_CSS_PSYS_STATE_RUNNING_PATTERN (0xE6E6E6E6) +#define IA_CSS_PSYS_STATE_STARTING_PATTERN (0xD5D5D5D5) +#define IA_CSS_PSYS_STATE_STARTED_PATTERN (0xC4C4C4C4) +#define IA_CSS_PSYS_STATE_INITIALIZING_PATTERN (0xB3B3B3B3) +#define IA_CSS_PSYS_STATE_INITIALIZED_PATTERN (0xA0A0A0A0) + +/* + * Defines the state of psys: + * - IA_CSS_PSYS_STATE_UNKNOWN = psys status is unknown (or not recognized) + * - IA_CSS_PSYS_STATE_INITIALING = some of the psys components are + * not initialized yet + * - IA_CSS_PSYS_STATE_INITIALIZED = psys components are initialized + * - IA_CSS_PSYS_STATE_STARTING = some of the psys components are initialized + * but not started yet + * - IA_CSS_PSYS_STATE_STARTED = psys components are started + * - IA_CSS_PSYS_STATE_RUNNING = some of the psys components are started + * but not ready yet + * - IA_CSS_PSYS_STATE_READY = psys is ready + * The state of psys can be obtained calling ia_css_psys_check_state() +*/ +typedef enum ia_css_psys_state { + IA_CSS_PSYS_STATE_UNKNOWN = 0, /**< psys state is unknown */ + /*< some of the psys components are not initialized yet*/ + IA_CSS_PSYS_STATE_INITIALIZING = IA_CSS_PSYS_STATE_INITIALIZING_PATTERN, + /**< psys components are initialized */ + IA_CSS_PSYS_STATE_INITIALIZED = IA_CSS_PSYS_STATE_INITIALIZED_PATTERN, + /**< some of the psys components are not started yet */ + IA_CSS_PSYS_STATE_STARTING = IA_CSS_PSYS_STATE_STARTING_PATTERN, + /**< psys components are started */ + IA_CSS_PSYS_STATE_STARTED = IA_CSS_PSYS_STATE_STARTED_PATTERN, + /**< some of the psys components are not ready yet */ + IA_CSS_PSYS_STATE_RUNNING = IA_CSS_PSYS_STATE_RUNNING_PATTERN, + /**< psys is ready */ + IA_CSS_PSYS_STATE_READY = IA_CSS_PSYS_STATE_READY_PATTERN, +} ia_css_psys_state_t; + +extern struct ia_css_syscom_context *psys_syscom; +#if HAS_DUAL_CMD_CTX_SUPPORT +extern struct ia_css_syscom_context *psys_syscom_secure; +#endif + +/*! Print the syscom creation descriptor to file/stream + + @param config[in] Psys syscom descriptor + @param fid[out] file/stream handle + + @return < 0 on error +*/ +extern int ia_css_psys_config_print( + const struct ia_css_syscom_config *config, void *fid); + +/*! Print the Psys syscom object to file/stream + + @param context[in] Psys syscom object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_psys_print( + const struct ia_css_syscom_context *context, void *fid); + +/*! Create the syscom creation descriptor + + @return NULL on error + */ +extern struct ia_css_syscom_config *ia_css_psys_specify(void); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Create the syscom creation descriptor for secure stream + + @param vtl0_addr_mask[in] VTL0 address mask that will be stored in 'secure' ctx + @return NULL on error + */ +extern struct ia_css_syscom_config *ia_css_psys_specify_secure(unsigned int vtl0_addr_mask); +#endif + +/*! Compute the size of storage required for allocating the Psys syscom object + + @param config[in] Psys syscom descriptor + + @return 0 on error + */ +extern size_t ia_css_sizeof_psys( + struct ia_css_syscom_config *config); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Open (and map the storage for) the Psys syscom object + This is the same as ia_css_psys_open() excluding server start. + Target for VTIO usage where multiple syscom objects need to be + created first before this API is invoked. + + @param buffer[in] storage buffers for the syscom object + in the kernel virtual memory space and + its Psys mapped version + @param config[in] Psys syscom descriptor + @return NULL on error + */ + +extern struct ia_css_syscom_context *ia_css_psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config); + +/*! Store the parameters of the Psys syscom object in DMEM, so + they can be communicated with FW. This step needs to be invoked + after SPC starts in ia_css_psys_open(), so SPC DMEM access blocker + programming already takes effective. + + @param context[in] Psys syscom object + @param config[in] Psys syscom descriptor + @return 0 if successful + */ +extern int ia_css_psys_context_store_dmem( + struct ia_css_syscom_context *context, + struct ia_css_syscom_config *config); + +/*! Start PSYS Server. Psys syscom object must have been created already. + Target for VTIO usage where multiple syscom objects need to be + created first before this API is invoked. + @param config[in] Psys syscom descriptor + + @return true if psys open started successfully + */ +extern int ia_css_psys_open( + struct ia_css_syscom_config *config); +#else +/*! Open (and map the storage for) the Psys syscom object + + @param buffer[in] storage buffers for the syscom object + in the kernel virtual memory space and + its Psys mapped version + @param config[in] Psys syscom descriptor + + Precondition(1): The buffer must be large enough to hold the syscom object. + Its size must be computed with the function "ia_css_sizeof_psys()". + The buffer must be created in the kernel memory space. + + Precondition(2): If buffer == NULL, the storage allocations and mapping + is performed in this function. Config must hold the handle to the Psys + virtual memory space + + Postcondition: The context is initialised in the provided/created buffer. + The syscom context pointer is the kernel space handle to the syscom object + + @return NULL on error + */ +extern struct ia_css_syscom_context *ia_css_psys_open( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +/*! completes the psys open procedure. Must be called multiple times + until it succeeds or driver determines the boot sequence has failed. + + @param context[in] Psys syscom object + + @return false if psys open has not completed successfully + */ +extern bool ia_css_psys_open_is_ready( + struct ia_css_syscom_context *context); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Request close of a PSYS context + * The functionatlity is the same as ia_css_psys_close() which closes PSYS syscom object. + * Counterpart of ia_css_psys_context_create() + * @param context[in]: Psys context + * @return NULL if close is successful context otherwise + */ +extern struct ia_css_syscom_context *ia_css_psys_context_destroy( + struct ia_css_syscom_context *context); + +/*! Request close of a PSYS device for VTIO case + * @param None + * @return 0 if successful + */ +extern int ia_css_psys_close(void); +#else +/*! Request close of a PSYS context + * @param context[in]: Psys context + * @return NULL if close is successful context otherwise + */ +extern struct ia_css_syscom_context *ia_css_psys_close( + struct ia_css_syscom_context *context); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT*/ + +/*! Unmap and free the storage of the PSYS context + * @param context[in] Psys context + * @param force[in] Force release even if device is busy + * @return 0 if release is successful + * EINVAL if context is invalid + * EBUSY if device is not yet idle, and force==0 + */ +extern int ia_css_psys_release( + struct ia_css_syscom_context *context, + bool force); + +/*! Checks the state of the Psys syscom object + + @param context[in] Psys syscom object + + @return State of the syscom object + */ +extern ia_css_psys_state_t ia_css_psys_check_state( + struct ia_css_syscom_context *context); + +/*!Indicate if the designated cmd queue in the Psys syscom object is full + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return false if the cmd queue is not full or on error + */ + +extern bool ia_css_is_psys_cmd_queue_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if the designated cmd queue in the Psys syscom object is notfull + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return false if the cmd queue is full on error + */ +extern bool ia_css_is_psys_cmd_queue_not_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if the designated cmd queue in the Psys syscom object holds N space + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + @param N[in] Number of messages + + @return false if the cmd queue space is unavailable or on error + */ +extern bool ia_css_has_psys_cmd_queue_N_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const unsigned int N); + +/*!Return the free space count in the designated cmd queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return the space, < 0 on error + */ +extern int ia_css_psys_cmd_queue_get_available_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if there are any messages pending in the Psys syscom + * object event queues + + @param context[in] Psys syscom object + + @return false if there are no messages or on error + */ +extern bool ia_css_any_psys_event_queue_not_empty( + struct ia_css_syscom_context *context); + +/*!Indicate if the designated event queue in the Psys syscom object is empty + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return false if the event queue is not empty or on error + */ +extern bool ia_css_is_psys_event_queue_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Indicate if the designated event queue in the Psys syscom object is not empty + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return false if the receive queue is empty or on error + */ +extern bool ia_css_is_psys_event_queue_not_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Indicate if the designated event queue + * in the Psys syscom object holds N items + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param N[in] Number of messages + + @return false if the event queue has insufficient messages + available or on error +*/ +extern bool ia_css_has_psys_event_queue_N_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + const unsigned int N); + +/*!Return the message count in the designated event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return the messages, < 0 on error + */ +extern int ia_css_psys_event_queue_get_available_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*! Send (pass by value) a command on a queue in the Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID +@param cmd_msg_buffer[in] pointer to the command message buffer + +Precondition: The command message buffer must be large enough + to hold the command + +Postcondition: Either 0 or 1 commands have been sent + +Note: The message size is fixed and determined on creation + + @return the number of sent commands (1), <= 0 on error + */ +extern int ia_css_psys_cmd_queue_send( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer); + +/*! Send (pass by value) N commands on a queue in the Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + @param cmd_msg_buffer[in] Pointer to the command message buffer +@param N[in] Number of commands + +Precondition: The command message buffer must be large enough + to hold the commands + +Postcondition: Either 0 or up to and including N commands have been sent + + Note: The message size is fixed and determined on creation + + @return the number of sent commands, <= 0 on error + */ +extern int ia_css_psys_cmd_queue_send_N( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer, + const unsigned int N); + +/*! Receive (pass by value) an event from an event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param event_msg_buffer[out] pointer to the event message buffer + + Precondition: The event message buffer must be large enough to hold the event + + Postcondition: Either 0 or 1 events have been received + + Note: The event size is fixed and determined on creation + + @return the number of received events (1), <= 0 on error + */ +extern int ia_css_psys_event_queue_receive( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer); + +/*! Receive (pass by value) N events from an event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param event_msg_buffer[out] pointer to the event message buffer + @param N[in] Number of events + + Precondition: The event buffer must be large enough to hold the events + + Postcondition: Either 0 or up to and including N events have been received + + Note: The message size is fixed and determined on creation + + @return the number of received event messages, <= 0 on error + */ +extern int ia_css_psys_event_queue_receive_N( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer, + const unsigned int N); + + +/* + * Access functions to query the object stats + */ + + +/*!Return the size of the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern size_t ia_css_psys_get_size( + const struct ia_css_syscom_context *context); + +/*!Return the number of cmd queues in the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern unsigned int ia_css_psys_get_cmd_queue_count( + const struct ia_css_syscom_context *context); + +/*!Return the number of event queues in the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern unsigned int ia_css_psys_get_event_queue_count( + const struct ia_css_syscom_context *context); + +/*!Return the size of the indicated Psys command queue + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + Note: The queue size is expressed in the number of fields + + @return 0 on error + */ +extern size_t ia_css_psys_get_cmd_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Return the size of the indicated Psys event queue + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + Note: The queue size is expressed in the number of fields + + @return 0 on error + */ +extern size_t ia_css_psys_get_event_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Return the command message size of the indicated Psys command queue + + @param context[in] Psys syscom object + + Note: The message size is expressed in uint8_t + + @return 0 on error + */ +extern size_t ia_css_psys_get_cmd_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Return the event message size of the indicated Psys event queue + + @param context[in] Psys syscom object + + Note: The message size is expressed in uint8_t + + @return 0 on error + */ +extern size_t ia_css_psys_get_event_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +#endif /* __IA_CSS_PSYS_DEVICE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h new file mode 100644 index 000000000000..8e5899bc66db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DEVICE_TRACE_H +#define __IA_CSS_PSYS_DEVICE_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DEVICE_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DEVICE_TRACING_OVERRIDE)) + #define PSYS_DEVICE_TRACE_LEVEL_CONFIG \ + PSYS_DEVICE_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DEVICE_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DEVICE_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DEVICE_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h new file mode 100644 index 000000000000..1120b357632c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h @@ -0,0 +1,37 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_INIT_H +#define __IA_CSS_PSYS_INIT_H + +#include /* vied_vaddress_t */ + +/* Init parameters passed to the fw on device open (non secure mode) */ +typedef struct ia_css_psys_server_init { + /* These members are used in PSS only and will be removed */ + /* Shared memory host address of pkg dir */ + unsigned long long host_ddr_pkg_dir; + /* Address of pkg_dir structure in DDR */ + vied_vaddress_t ddr_pkg_dir_address; + /* Size of Package dir in DDR */ + uint32_t pkg_dir_size; + + /* Prefetch configiration */ + /* enable prefetching on SPC, SPP0 and SPP1 */ + uint32_t icache_prefetch_sp; + /* enable prefetching on ISP0..N */ + uint32_t icache_prefetch_isp; +} ia_css_psys_server_init_t; + +#endif /* __IA_CSS_PSYS_INIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h new file mode 100644 index 000000000000..e0d1e935c221 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h @@ -0,0 +1,92 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TRANSPORT_H +#define __IA_CSS_PSYS_TRANSPORT_H + +#include /* ia_css_psys_cmd_queues */ +#include /* vied_vaddress_t */ + +#include + +typedef enum ia_css_psys_event_queues { + /**< The in-order queue for event returns */ + IA_CSS_PSYS_EVENT_QUEUE_MAIN_ID, + IA_CSS_N_PSYS_EVENT_QUEUE_ID +} ia_css_psys_event_queue_ID_t; + +typedef enum ia_css_psys_event_types { + /**< No error to report. */ + IA_CSS_PSYS_EVENT_TYPE_SUCCESS = 0, + /**< Unknown unhandled error */ + IA_CSS_PSYS_EVENT_TYPE_UNKNOWN_ERROR = 1, + /* Retrieving remote object: */ + /**< Object ID not found */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_NOT_FOUND = 2, + /**< Objects too big, or size is zero. */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_TOO_BIG = 3, + /**< Failed to load whole process group from tproxy/dma */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_DDR_TRANS_ERR = 4, + /**< The proper package could not be found */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_NULL_PKG_DIR_ADDR = 5, + /* Process group: */ + /**< Failed to run, error while loading frame */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAME_ERR = 6, + /**< Failed to run, error while loading fragment */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAGMENT_ERR = 7, + /**< The process count of the process group is zero */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_COUNT_ZERO = 8, + /**< Process(es) initialization */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_INIT_ERR = 9, + /**< Aborted (after host request) */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_ABORT = 10, + /**< NULL pointer in the process group */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_NULL = 11, + /**< Process group validation failed */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_VALIDATION_ERR = 12 +} ia_css_psys_event_type_t; + +#define IA_CSS_PSYS_CMD_BITS 64 +struct ia_css_psys_cmd_s { + /**< The command issued to the process group */ + uint16_t command; + /**< Message field of the command */ + uint16_t msg; + /**< The context reference (process group/buffer set/...) */ + uint32_t context_handle; +}; + +#define IA_CSS_PSYS_EVENT_BITS 128 +struct ia_css_psys_event_s { + /**< The (return) status of the command issued to + * the process group this event refers to + */ + uint16_t status; + /**< The command issued to the process group this event refers to */ + uint16_t command; + /**< The context reference (process group/buffer set/...) */ + uint32_t context_handle; + /**< This token (size) must match the token registered + * in a process group + */ + uint64_t token; +}; + +struct ia_css_psys_buffer_s { + /**< The in-order queue for scheduled process groups */ + void *host_buffer; + vied_vaddress_t *isp_buffer; +}; + +#endif /* __IA_CSS_PSYS_TRANSPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c new file mode 100644 index 000000000000..106fe0a0da85 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c @@ -0,0 +1,853 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_psys_device.h" +#include "ia_css_psys_device_trace.h" +#include "ia_css_psys_init.h" +#include "regmem_access.h" + +#include +#include +#include + +#include "ia_css_cell.h" + +#define IA_CSS_PSYS_CMD_QUEUE_SIZE 0x20 +#define IA_CSS_PSYS_EVENT_QUEUE_SIZE 0x40 + +static struct ia_css_syscom_queue_config ia_css_psys_cmd_queue_cfg[IA_CSS_N_PSYS_CMD_QUEUE_ID]; + +static struct ia_css_syscom_queue_config + ia_css_psys_event_queue_cfg[IA_CSS_N_PSYS_EVENT_QUEUE_ID] = { + {IA_CSS_PSYS_EVENT_QUEUE_SIZE, IA_CSS_PSYS_EVENT_BITS/8}, +}; + +static struct ia_css_syscom_config psys_syscom_config; +struct ia_css_syscom_context *psys_syscom; +#if HAS_DUAL_CMD_CTX_SUPPORT +static struct ia_css_syscom_config psys_syscom_config_secure; +struct ia_css_syscom_context *psys_syscom_secure; +#endif +static bool external_alloc = true; + +int ia_css_psys_config_print( + const struct ia_css_syscom_config *config, + void *fh) +{ + int retval = -1; + + NOT_USED(fh); + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_frame_print(): enter:\n"); + + verifexit(config != NULL); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DEVICE, ERROR, + "ia_css_frame_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_psys_print( + const struct ia_css_syscom_context *context, + void *fh) +{ + int retval = -1; + + NOT_USED(fh); + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_print(): enter:\n"); + + verifexit(context != NULL); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_print failed (%i)\n", retval); + } + return retval; +} + +static void set_syscom_config(struct ia_css_syscom_config *config) +{ + int i; + config->num_input_queues = IA_CSS_N_PSYS_CMD_QUEUE_ID; + config->num_output_queues = IA_CSS_N_PSYS_EVENT_QUEUE_ID; + /* The number of queues are different for different platforms + * so the array is initialized here + */ + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + ia_css_psys_cmd_queue_cfg[i].queue_size = IA_CSS_PSYS_CMD_QUEUE_SIZE; + ia_css_psys_cmd_queue_cfg[i].token_size = IA_CSS_PSYS_CMD_BITS/8; + } + config->input = ia_css_psys_cmd_queue_cfg; + config->output = ia_css_psys_event_queue_cfg; + config->vtl0_addr_mask = 0; +} + +struct ia_css_syscom_config *ia_css_psys_specify(void) +{ + struct ia_css_syscom_config *config = &psys_syscom_config; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_specify(): enter:\n"); + set_syscom_config(config); + config->secure = false; + + return config; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_config *ia_css_psys_specify_secure(unsigned int vtl0_addr_mask) +{ + struct ia_css_syscom_config *config = &psys_syscom_config_secure; + + IA_CSS_TRACE_1(PSYSAPI_DEVICE, INFO, "ia_css_psys_specify_secure(mask %#x): enter:\n", vtl0_addr_mask); + set_syscom_config(config); + config->secure = true; + config->vtl0_addr_mask = vtl0_addr_mask; + return config; +} +#endif + +size_t ia_css_sizeof_psys( + struct ia_css_syscom_config *config) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_sizeof_psys(): enter:\n"); + + NOT_USED(config); + + return size; +} + +/* Internal function to create syscom_context */ +static struct ia_css_syscom_context *psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + struct ia_css_syscom_context *context; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_context_create(): enter:\n"); + + if (config == NULL) + goto EXIT; + + if (buffer == NULL) { + /* Allocate locally */ + external_alloc = false; + } + + /* + * Here we would like to pass separately the sub-system ID + * and optionally the user pointer to be mapped, depending on + * where this open is called, and which virtual memory handles + * we see here. + */ + /* context = ia_css_syscom_open(get_virtual_memory_handle(vied_psys_ID), + * buffer, config); + */ + context = ia_css_syscom_open(config, NULL); + if (context == NULL) + goto EXIT; + + return context; + +EXIT: + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, "psys_context_create failed\n"); + return NULL; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_context *ia_css_psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + return psys_context_create(buffer, config); +} + +/* push context information to DMEM for FW to access */ +int ia_css_psys_context_store_dmem( + struct ia_css_syscom_context *context, + struct ia_css_syscom_config *config) +{ + return ia_css_syscom_store_dmem(context, config->ssid, config->vtl0_addr_mask); +} +#endif + +/* Internal function to start psys server */ +static int psys_start_server( + struct ia_css_syscom_config *config) +{ + ia_css_psys_server_init_t *server_config; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_start_server(): enter:\n"); + + /* Configure SPC icache prefetching and start SPC */ + server_config = (ia_css_psys_server_init_t *)config->specific_addr; + IA_CSS_TRACE_1(PSYSAPI_DEVICE, INFO, "SPC prefetch: %d\n", + server_config->icache_prefetch_sp); + ia_css_cell_start_prefetch(config->ssid, SPC0, + server_config->icache_prefetch_sp); + return 0; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_psys_open( + struct ia_css_syscom_config *config) +{ + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_open(): enter:\n"); + return psys_start_server(config); +} +#else +struct ia_css_syscom_context *ia_css_psys_open( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + struct ia_css_syscom_context *context; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_open(): enter:\n"); + + context = psys_context_create(buffer, config); + + /* Configure SPC icache prefetching and start SPC */ + psys_start_server(config); + + return context; +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +bool ia_css_psys_open_is_ready( + struct ia_css_syscom_context *context) +{ + int retval = -1; + bool ready = 0; + unsigned int i; + int syscom_retval; + + verifexit(context != NULL); + + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + syscom_retval = ia_css_syscom_send_port_open(context, i); + if (syscom_retval != 0) { + if (syscom_retval == FW_ERROR_BUSY) { + /* Do not print error */ + retval = 0; + } + /* Not ready yet */ + goto EXIT; + } + } + + for (i = 0; i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + syscom_retval = ia_css_syscom_recv_port_open(context, i); + if (syscom_retval != 0) { + if (syscom_retval == FW_ERROR_BUSY) { + /* Do not print error */ + retval = 0; + } + /* Not ready yet */ + goto EXIT; + } + } + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "ia_css_psys_open_is_ready(): complete:\n"); + + /* If this point reached, do not print error */ + retval = 0; + /* If this point reached, ready */ + ready = 1; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_open_is_ready failed\n"); + } + return ready; +} + +/* Internal function to close syscom_context */ +static struct ia_css_syscom_context *psys_context_destroy( + struct ia_css_syscom_context *context) +{ + /* Success: return NULL, Error: return context pointer value + * Intention is to change return type to int (errno), + * see commented values. + */ + + unsigned int i; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_context_destroy(): enter:\n"); + + /* NULL pointer check disabled, since there is no proper return value */ + + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + if (ia_css_syscom_send_port_close(context, i) != 0) + return context; /* EINVAL */ + } + + for (i = 0; i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + if (ia_css_syscom_recv_port_close(context, i) != 0) + return context; /* EINVAL */ + } + + /* request device close */ + if (ia_css_syscom_close(context) != 0) + return context; /* EBUSY */ + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "psys_context_destroy(): leave: OK\n"); + return NULL; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_context *ia_css_psys_context_destroy( + struct ia_css_syscom_context *context) +{ + return psys_context_destroy(context); +} + +int ia_css_psys_close() +{ + /* Intentionally left blank for now since syscom objects should have + * been destroyed already by prior ia_css_psys_context_destroy() calls. + */ + return 0; +} +#else +struct ia_css_syscom_context *ia_css_psys_close( + struct ia_css_syscom_context *context) +{ + return psys_context_destroy(context); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +int ia_css_psys_release( + struct ia_css_syscom_context *context, + bool force) +{ + if (context == NULL) + return -EFAULT; + + /* try to free resources */ + if (ia_css_syscom_release(context, force) != 0) + return -EBUSY; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "ia_css_psys_release(): leave: OK\n"); + return 0; +} + +ia_css_psys_state_t ia_css_psys_check_state( + struct ia_css_syscom_context *context) +{ + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_check_state(): enter:\n"); + + NOT_USED(context); + + /* For the time being, return the READY state to be used by SPC test */ + return IA_CSS_PSYS_STATE_READY; +} + +bool ia_css_is_psys_cmd_queue_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + bool is_full = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_cmd_queue_full(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_full = (num_tokens == 0); + retval = 0; +EXIT: + if (retval != 0) { + is_full = true; + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_cmd_queue_full failed\n"); + } + return is_full; +} + +bool ia_css_is_psys_cmd_queue_not_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + bool is_not_full = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_cmd_queue_not_full(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_not_full = (num_tokens != 0); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_cmd_queue_not_full failed\n"); + } + return is_not_full; +} + +bool ia_css_has_psys_cmd_queue_N_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const unsigned int N) +{ + bool has_N_space = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_has_psys_cmd_queue_N_space(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + has_N_space = ((unsigned int)num_tokens >= N); +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_has_psys_cmd_queue_N_space failed\n"); + } + return has_N_space; +} + +int ia_css_psys_cmd_queue_get_available_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + int N_space = -1; + int num_tokens; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_get_available_space(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + N_space = (int)(num_tokens); +EXIT: + if (N_space < 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_get_available_space failed\n"); + } + return N_space; +} + +bool ia_css_any_psys_event_queue_not_empty( + struct ia_css_syscom_context *context) +{ + ia_css_psys_event_queue_ID_t i; + bool any_msg = false; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_any_psys_event_queue_not_empty(): enter:\n"); + verifexit(context != NULL); + + for (i = (ia_css_psys_event_queue_ID_t)0; + i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + any_msg = + any_msg || ia_css_is_psys_event_queue_not_empty(context, i); + } + +EXIT: + return any_msg; +} + +bool ia_css_is_psys_event_queue_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + bool is_empty = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_event_queue_empty(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, (unsigned int)id); + verifexit(num_tokens >= 0); + + is_empty = (num_tokens == 0); + retval = 0; +EXIT: + if (retval != 0) { + is_empty = true; + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_event_queue_empty failed\n"); + } + return is_empty; +} + +bool ia_css_is_psys_event_queue_not_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + bool is_not_empty = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_event_queue_not_empty(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_not_empty = (num_tokens != 0); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_event_queue_not_empty failed\n"); + } + return is_not_empty; +} + +bool ia_css_has_psys_event_queue_N_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + const unsigned int N) +{ + bool has_N_msgs = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_has_psys_event_queue_N_msgs(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + has_N_msgs = ((unsigned int)num_tokens >= N); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_has_psys_event_queue_N_msgs failed\n"); + } + return has_N_msgs; +} + +int ia_css_psys_event_queue_get_available_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + int N_msgs = -1; + int num_tokens; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_get_available_msgs(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + N_msgs = (int)(num_tokens); +EXIT: + if (N_msgs < 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_get_available_msgs failed\n"); + } + return N_msgs; +} + +int ia_css_psys_cmd_queue_send( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer) +{ + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_send(): enter:\n"); + verifexit(context != NULL); + + verifexit(context != NULL); + /* The ~full check fails on receive queues */ + verifexit(ia_css_is_psys_cmd_queue_not_full(context, id)); + verifexit(cmd_msg_buffer != NULL); + + verifexit(ia_css_syscom_send_port_transfer(context, (unsigned int)id, + cmd_msg_buffer) >= 0); + + count = 1; +EXIT: + if (count == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_send failed\n"); + } + return count; +} + +int ia_css_psys_cmd_queue_send_N( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer, + const unsigned int N) +{ + struct ia_css_psys_cmd_s *cmd_msg_buffer_loc = + (struct ia_css_psys_cmd_s *)cmd_msg_buffer; + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_send_N(): enter:\n"); + verifexit(context != NULL); + + for (count = 0; count < (int)N; count++) { + int count_loc = ia_css_psys_cmd_queue_send(context, id, + (void *)(&cmd_msg_buffer_loc[count])); + + verifexit(count_loc == 1); + } + +EXIT: + if ((unsigned int) count < N) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_send_N failed\n"); + } + return count; +} + +int ia_css_psys_event_queue_receive( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer) +{ + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_receive(): enter:\n"); + + verifexit(context != NULL); + /* The ~empty check fails on send queues */ + verifexit(ia_css_is_psys_event_queue_not_empty(context, id)); + verifexit(event_msg_buffer != NULL); + + verifexit(ia_css_syscom_recv_port_transfer(context, (unsigned int)id, + event_msg_buffer) >= 0); + + count = 1; +EXIT: + if (count == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_receive failed\n"); + } + return count; +} + +int ia_css_psys_event_queue_receive_N( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer, + const unsigned int N) +{ + struct ia_css_psys_event_s *event_msg_buffer_loc; + int count; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_receive_N(): enter:\n"); + + event_msg_buffer_loc = (struct ia_css_psys_event_s *)event_msg_buffer; + + for (count = 0; count < (int)N; count++) { + int count_loc = ia_css_psys_event_queue_receive(context, id, + (void *)(&event_msg_buffer_loc[count])); + + verifexit(count_loc == 1); + } + +EXIT: + if ((unsigned int) count < N) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_receive_N failed\n"); + } + return count; +} + +size_t ia_css_psys_get_size( + const struct ia_css_syscom_context *context) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ +EXIT: + if (size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_size failed\n"); + } + return size; +} + +unsigned int ia_css_psys_get_cmd_queue_count( + const struct ia_css_syscom_context *context) +{ + unsigned int count = 0; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_queue_count(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + count = (unsigned int)IA_CSS_N_PSYS_CMD_QUEUE_ID; + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_queue_count failed\n"); + } + return count; +} + +unsigned int ia_css_psys_get_event_queue_count( + const struct ia_css_syscom_context *context) +{ + unsigned int count = 0; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_queue_count(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + count = (unsigned int)IA_CSS_N_PSYS_EVENT_QUEUE_ID; + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_event_queue_count failed\n"); + } + return count; +} + +size_t ia_css_psys_get_cmd_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + size_t queue_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_queue_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + queue_size = ia_css_psys_cmd_queue_cfg[id].queue_size; +EXIT: + if (queue_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_queue_size failed\n"); + } + return queue_size; +} + +size_t ia_css_psys_get_event_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + size_t queue_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_queue_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + queue_size = ia_css_psys_event_queue_cfg[id].queue_size; +EXIT: + if (queue_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_event_queue_size failed\n"); + } + return queue_size; +} + +size_t ia_css_psys_get_cmd_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + size_t msg_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_msg_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + msg_size = ia_css_psys_cmd_queue_cfg[id].token_size; +EXIT: + if (msg_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_msg_size failed\n"); + } + return msg_size; +} + +size_t ia_css_psys_get_event_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + size_t msg_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_msg_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + msg_size = ia_css_psys_event_queue_cfg[id].token_size; +EXIT: + if (msg_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_msg_size failed\n"); + } + return msg_size; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h new file mode 100644 index 000000000000..392b4359353f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h @@ -0,0 +1,174 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYS_BUFFER_SET_H +#define __IA_CSS_PSYS_BUFFER_SET_H + +#include "ia_css_base_types.h" +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_process_types.h" +#include "ia_css_terminal_types.h" + +#define N_UINT64_IN_BUFFER_SET_STRUCT 1 +#define N_UINT16_IN_BUFFER_SET_STRUCT 1 +#define N_UINT8_IN_BUFFER_SET_STRUCT 1 +#define N_PADDING_UINT8_IN_BUFFER_SET_STRUCT 5 +#define SIZE_OF_BUFFER_SET \ + (N_UINT64_IN_BUFFER_SET_STRUCT * IA_CSS_UINT64_T_BITS \ + + VIED_VADDRESS_BITS \ + + VIED_VADDRESS_BITS \ + + N_UINT16_IN_BUFFER_SET_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_UINT8_IN_BUFFER_SET_STRUCT * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_BUFFER_SET_STRUCT * IA_CSS_UINT8_T_BITS) + +typedef struct ia_css_buffer_set_s ia_css_buffer_set_t; + +struct ia_css_buffer_set_s { + /* Token for user context reference */ + uint64_t token; + /* IPU virtual address of this buffer set */ + vied_vaddress_t ipu_virtual_address; + /* IPU virtual address of the process group corresponding to this buffer set */ + vied_vaddress_t process_group_handle; + /* Number of terminal buffer addresses in this structure */ + uint16_t terminal_count; + /* Frame id to associate with this buffer set */ + uint8_t frame_counter; + /* Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_BUFFER_SET_STRUCT]; +}; + + +/*! Construct a buffer set object at specified location + + @param buffer_set_mem[in] memory location to create buffer set object + @param process_group[in] process group corresponding to this buffer set + @param frame_counter[in] frame number for this buffer set object + + @return pointer to buffer set object on success, NULL on error + */ +ia_css_buffer_set_t *ia_css_buffer_set_create( + void *buffer_set_mem, + const ia_css_process_group_t *process_group, + const unsigned int frame_counter); + +/*! Compute size (in bytes) required for full buffer set object + + @param process_group[in] process group corresponding to this buffer set + + @return size in bytes of buffer set object on success, 0 on error + */ +size_t ia_css_sizeof_buffer_set( + const ia_css_process_group_t *process_group); + +/*! Set a buffer address in a buffer set object + + @param buffer_set[in] buffer set object to set buffer in + @param terminal_index[in] terminal index to use as a reference between + buffer and terminal + @param buffer[in] buffer address to store + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_buffer( + ia_css_buffer_set_t *buffer_set, + const unsigned int terminal_index, + const vied_vaddress_t buffer); + +/*! Get virtual buffer address from a buffer set object and terminal object by + resolving the index used + + @param buffer_set[in] buffer set object to get buffer from + @param terminal[in] terminal object to get buffer of + + @return virtual buffer address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_buffer( + const ia_css_buffer_set_t *buffer_set, + const ia_css_terminal_t *terminal); + +/*! Set ipu virtual address of a buffer set object within the buffer set object + + @param buffer_set[in] buffer set object to set ipu address in + @param ipu_vaddress[in] ipu virtual address of the buffer set object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_ipu_address( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t ipu_vaddress); + +/*! Get ipu virtual address from a buffer set object + + @param buffer_set[in] buffer set object to get ipu address from + + @return virtual buffer set address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_ipu_address( + const ia_css_buffer_set_t *buffer_set); + +/*! Set process group handle in a buffer set object + + @param buffer_set[in] buffer set object to set handle in + @param process_group_handle[in] process group handle of the buffer set + object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_process_group_handle( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t process_group_handle); + +/*! Get process group handle from a buffer set object + + @param buffer_set[in] buffer set object to get handle from + + @return virtual process group address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_process_group_handle( + const ia_css_buffer_set_t *buffer_set); + +/*! Set token of a buffer set object within the buffer set object + + @param buffer_set[in] buffer set object to set ipu address in + @param token[in] token of the buffer set object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_token( + ia_css_buffer_set_t *buffer_set, + const uint64_t token); + +/*! Get token from a buffer set object + + @param buffer_set[in] buffer set object to get token from + + @return token on success, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint64_t ia_css_buffer_set_get_token( + const ia_css_buffer_set_t *buffer_set); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_buffer_set_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_BUFFER_SET_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h new file mode 100644 index 000000000000..9a1e3a7a1294 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +#define __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h new file mode 100644 index 000000000000..e8a979dfce0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DYNAMIC_TRACE_H +#define __IA_CSS_PSYS_DYNAMIC_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DYNAMIC_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DYNAMIC_TRACING_OVERRIDE)) + #define PSYS_DYNAMIC_TRACE_LEVEL_CONFIG \ + PSYS_DYNAMIC_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DYNAMIC_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DYNAMIC_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DYNAMIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h new file mode 100644 index 000000000000..f4ef80f74213 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h @@ -0,0 +1,396 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_H +#define __IA_CSS_PSYS_PROCESS_H + +/*! \file */ + +/** @file ia_css_psys_process.h + * + * Define the methods on the process object that are not part of + * a single interface + */ + +#include +#include + +#include + +#include /* uint8_t */ + +/* + * Creation + */ +#include + +/* + * Internal resources + */ +#include + +/* + * Process manager + */ +#include + +/* + * Command processor + */ + +/*! Execute a command locally or send it to be processed remotely + + @param process[in] process object + @param cmd[in] command + + @return < 0 on invalid argument(s) or process state + */ +extern int ia_css_process_cmd( + ia_css_process_t *process, + const ia_css_process_cmd_t cmd); + +/*! Get the internal memory offset of the process object + + @param process[in] process object + @param mem_id[in] memory id + + @return internal memory offset, + IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_int_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_id); + + +/*! Get the external memory offset of the process object + + @param process[in] process object + @param mem_id[in] memory id + + @return external memory offset, + IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_ext_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + + +/*! Get the stored size of the process object + + @param process[in] process object + + @return size, 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_process_get_size(const ia_css_process_t *process); + +/*! Get the (pointer to) the process group parent of the process object + + @param process[in] process object + + @return the pointer to the parent, NULL on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_t *ia_css_process_get_parent( + const ia_css_process_t *process); + +/*! Set the (pointer to) the process group parent of the process object + + @param process[in] process object + @param parent[in] (pointer to the) process group parent object + + @return < 0 on invalid argument(s) + */ +extern int ia_css_process_set_parent( + ia_css_process_t *process, + ia_css_process_group_t *parent); + +/*! Get the unique ID of program used by the process object + + @param process[in] process object + + @return ID, 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_ID_t ia_css_process_get_program_ID( + const ia_css_process_t *process); + +/*! Get the state of the process object + + @param process[in] process object + + @return state, limit value (IA_CSS_N_PROCESS_STATES) on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_state_t ia_css_process_get_state( + const ia_css_process_t *process); + +/*! Set the state of the process object + + @param process[in] process object + @param state[in] state of the process + + @return < 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_set_state( + ia_css_process_t *process, + ia_css_process_state_t state); + +/*! Get the assigned cell of the the process object + + @param process[in] process object + + @return cell ID, limit value (VIED_NCI_N_CELL_ID) on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_cell_ID_t ia_css_process_get_cell( + const ia_css_process_t *process); + +/*! Get the number of cells the process object depends on + + @param process[in] process object + + @return number of cells + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_cell_dependency_count( + const ia_css_process_t *process); + +/*! Get the number of terminals the process object depends on + + @param process[in] process object + + @return number of terminals + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_terminal_dependency_count( + const ia_css_process_t *process); + +/*! Set n-th cell dependency of a process object + + @param process[in] Process object + @param dep_index[in] dep index + @param id[in] dep id + + @return < 0 on invalid process argument + */ +extern int ia_css_process_set_cell_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id); + +/*! Get n-th cell dependency of a process object + + @param process[in] Process object + @param cell_num[in] n-th cell + + @return n-th cell dependency, + IA_CSS_PROCESS_INVALID_DEPENDENCY on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_id_t ia_css_process_get_cell_dependency( + const ia_css_process_t *process, + const unsigned int cell_num); + +/*! Set n-th terminal dependency of a process object + + @param process[in] Process object + @param dep_index[in] dep index + @param id[in] dep id + + @return < 0 on on invalid argument(s) + */ +extern int ia_css_process_set_terminal_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id); + +/*! Get n-th terminal dependency of a process object + + @param process[in] Process object + @param terminal_num[in] n-th cell + + @return n-th terminal dependency, + IA_CSS_PROCESS_INVALID_DEPENDENCY on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_terminal_dependency( + const ia_css_process_t *process, + const unsigned int terminal_num); + +/*! Get the kernel bitmap of the the process object + + @param process[in] process object + + @return process kernel bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_kernel_bitmap_t ia_css_process_get_kernel_bitmap( + const ia_css_process_t *process); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_port_bitmap_ptr( + ia_css_process_t *process); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_active_port_bitmap_ptr( + ia_css_process_t *process); + + +/*! Get the cells bitmap of the the process object + + @param process[in] process object + + @return process cells bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_cells_bitmap( + const ia_css_process_t *process); + +/*! Sets the dfm device resource allocation bitmap of + * the process object + + @param process[in] process object + @param dfm_dev_id[in] dfm device id + @param bitmap[in] resource bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dfm_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap); + + +/*! Sets the active dfm ports bitmap of + * the process object + + @param process[in] process object + @param dfm_dev_id[in] dfm device id + @param bitmap[in] active ports bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dfm_active_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get the dfm port bitmap of the the process object + + @param process[in] process object + @param dfm_res_id dfm resource id + + @return bitmap of all DFM ports used by process, corresponding to the input dfm resource id + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_dfm_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id); + +/*! Get the dfm active port bitmap of the the process object + + @param process[in] process object + @param dfm_res_id[in] dfm resource id + + @return bitmap of all active DFM ports used by the process, corresponding to the input + dfm resource id + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_dfm_active_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id); + + +/*! Sets the cells bitmap of + * the process object + + @param process[in] process object + @param bitmap[in] bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_cells_bitmap( + ia_css_process_t *process, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get the device channel id-n resource allocation offset of the process object + + @param process[in] process object + @param dev_chn_id[in] channel id + + @return resource offset, IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_dev_chn( + const ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Get the ext mem type-n resource id of the the process object + + @param process[in] process object + @param mem_type[in] mem type + + @return resource offset, IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_mem_ID_t ia_css_process_get_ext_mem_id( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type); + + +/*! Sets the device channel id-n resource allocation offset of + * the process object + + @param process[in] process object + @param dev_chn_id[in] channel id + @param offset[in] resource offset + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset); + +/*! Boolean test if the process object type is valid + + @param process[in] process object + @param p_manifest[in] program manifest + + @return true if the process object is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_process_valid( + const ia_css_process_t *process, + const ia_css_program_manifest_t *p_manifest); + +/*! Gets the program_idx from the process object + + @param process[in] process object + + @return program index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_get_program_idx( + const ia_css_process_t *process); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h new file mode 100644 index 000000000000..cab796560414 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h @@ -0,0 +1,144 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_process.hsys.kernel.h + * + * Define the methods on the process object: Hsys kernel interface + */ + +#include + +#include + +/* + * Internal resources + */ + +/*! Clear all resource (offset) specifications + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_clear_all(ia_css_process_t *process); + +/*! Set the cell ID resource specification + + @param process[in] process object + @param cell_id[in] cell ID + + @return < 0 on error + */ +extern int ia_css_process_set_cell( + ia_css_process_t *process, + const vied_nci_cell_ID_t cell_id); + +/*! Clear cell ID resource specification + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_clear_cell(ia_css_process_t *process); + +/*! Set the memory resource (offset) specification for a memory + that belongs to the cell that is assigned to the process + + @param process[in] process object + @param mem_type_id[in] mem type ID + @param offset[in] offset + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_set_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t offset); + +/*! Clear the memory resource (offset) specification for a memory + type that belongs to the cell that is assigned to the process + + @param process[in] process object + @param mem_id[in] mem ID + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_clear_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (offset) specification for a memory + that does not belong to the cell that is assigned to the process + + @param process[in] process object + @param mem_type_id[in] mem type ID + @param offset[in] offset + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_set_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_ID_t mem_id, + const vied_nci_resource_size_t offset); + +/*! Clear the memory resource (offset) specification for a memory + type that does not belong to the cell that is assigned to the process + + @param process[in] process object + @param mem_id[in] mem ID + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_clear_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set a device channel resource (offset) specification + + @param process[in] process object + @param dev_chn_id[in] device channel ID + @param offset[in] offset + + @return < 0 on error + */ +extern int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset); + +/*! Clear a device channel resource (offset) specification + + @param process[in] process object + @param dev_chn_id[in] device channel ID + + @return < 0 on error + */ +extern int ia_css_process_clear_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id); + +#endif /* __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h new file mode 100644 index 000000000000..015a60b0e1af --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h @@ -0,0 +1,85 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_HSYS_USER_H +#define __IA_CSS_PSYS_PROCESS_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_process.hsys.user.h + * + * Define the methods on the process object: Hsys user interface + */ + +#include /* ia_css_program_param_t */ + +#include +#include + +#include /* uint8_t */ + +/* + * Creation + */ + +/*! Compute the size of storage required for allocating the process object + + @param manifest[in] program manifest + @param param[in] program parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_process( + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param); + +/*! Create the process object + + @param raw_mem[in] pre allocated memory + @param manifest[in] program manifest + @param param[in] program parameters + + @return NULL on error + */ +extern ia_css_process_t *ia_css_process_create( + void *raw_mem, + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param, + const uint32_t program_idx); + +/*! Destroy (the storage of) the process object + + @param process[in] process object + + @return NULL + */ +extern ia_css_process_t *ia_css_process_destroy( + ia_css_process_t *process); + +/* + * Access functions + */ + +/*! Print the process object to file/stream + + @param process[in] process object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_process_print( + const ia_css_process_t *process, + void *fid); + +#endif /* __IA_CSS_PSYS_PROCESS_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h new file mode 100644 index 000000000000..ba1db574a438 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_PSYS_H +#define __IA_CSS_PSYS_PROCESS_PSYS_H + +/*! \file */ + +/** @file ia_css_psys_process.psys.h + * + * Define the methods on the process object: Psys embedded interface + */ + +#include + +/* + * Process manager + */ + +/*! Acquire the resources specificed in process object + + @param process[in] process object + + Postcondition: This is a try process if any of the + resources is not available, all succesfully acquired + ones will be release and the function will return an + error + + @return < 0 on error + */ +extern int ia_css_process_acquire(ia_css_process_t *process); + +/*! Release the resources specificed in process object + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_release(ia_css_process_t *process); + + +#endif /* __IA_CSS_PSYS_PROCESS_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h new file mode 100644 index 000000000000..c0f6901adeb0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h @@ -0,0 +1,366 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_H +#define __IA_CSS_PSYS_PROCESS_GROUP_H + +/*! \file */ + +/** @file ia_css_psys_process_group.h + * + * Define the methods on the process object that are not part of + * a single interface + */ +#include "ia_css_rbm.h" + +#include +#include + +#include /* uint8_t */ + +/* + * Creation + */ +#include + +/* + * Registration of user contexts / callback info + * External resources + * Sequencing resources + */ +#include + +/* + * Dispatcher + */ +#include + +/* + * Access to sub-structure handles / fields + */ + +#include "ia_css_terminal.h" + +/*! Get the number of fragments on the process group + + @param process_group[in] process group object + + Note: Future change is to have a fragment count per + independent subgraph + + @return the fragment count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_process_group_get_fragment_count( + const ia_css_process_group_t *process_group); + + +/*! Get the fragment state on the process group + + @param process_group[in] process group object + @param fragment_state[in] current fragment of processing + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_get_fragment_state( + const ia_css_process_group_t *process_group, + uint16_t *fragment_state); + +/*! Set the fragment state on the process group + + @param process_group[in] process group object + @param fragment_state[in] current fragment of processing + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_fragment_state( + ia_css_process_group_t *process_group, + uint16_t fragment_state); + +/*! Get the number of processes on the process group + + @param process_group[in] process group object + + @return the process count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_process_count( + const ia_css_process_group_t *process_group); + +/*! Get the number of terminals on the process group + + @param process_group[in] process group object + + Note: Future change is to have a terminal count per + independent subgraph + + @return the terminal count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_terminal_count( + const ia_css_process_group_t *process_group); + +/*! Get the PG load start timestamp + + @param process_group[in] process group object + + @return PG load start timestamp, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_load_start_ts( + const ia_css_process_group_t *process_group); + +/*! Get the PG load time in cycles + + @param process_group[in] process group object + + @return PG load time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_load_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the PG init time in cycles + + @param process_group[in] process group object + + @return PG init time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_init_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the PG processing time in cycles + + @param process_group[in] process group object + + @return PG processing time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_processing_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the (pointer to) the terminal of the process group object + + @param process_group[in] process group object + @param terminal_type[in] terminal type of terminal + + @return the pointer to the terminal, NULL on error + */ + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_t *ia_css_process_group_get_terminal_from_type( + const ia_css_process_group_t *process_group, + const ia_css_terminal_type_t terminal_type); + +/*! Get the (pointer to) the terminal of the process group object + * for terminals which have only a single instance + * (cached in, cached out, program, program_ctrl_init) + + @param process_group[in] process group object + @param terminal_type[in] terminal type of terminal + + @return the pointer to the terminal, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +const ia_css_terminal_t *ia_css_process_group_get_single_instance_terminal( + const ia_css_process_group_t *process_group, + ia_css_terminal_type_t term_type); + +/*! Get the (pointer to) the indexed terminal of the process group object + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + @return the pointer to the terminal, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_t *ia_css_process_group_get_terminal( + const ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/*! Get the (pointer to) the indexed process of the process group object + + @param process_group[in] process group object + @param process_index[in] index of the process + + @return the pointer to the process, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_t *ia_css_process_group_get_process( + const ia_css_process_group_t *process_group, + const unsigned int process_index); + +/*! Get the stored size of the process group object + + @param process_group[in] process group object + + @return size, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_process_group_get_size( + const ia_css_process_group_t *process_group); + +/*! Get the state of the the process group object + + @param process_group[in] process group object + + @return state, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_state_t ia_css_process_group_get_state( + const ia_css_process_group_t *process_group); + +/*! Get the unique ID of program group used by the process group object + + @param process_group[in] process group object + + @return ID, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_group_ID_t ia_css_process_group_get_program_group_ID( + const ia_css_process_group_t *process_group); + +/*! Get the resource bitmap of the process group + + @param process_group[in] process group object + + @return the reource bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_group_get_resource_bitmap( + const ia_css_process_group_t *process_group); + +/*! Set the resource bitmap of the process group + + @param process_group[in] process group object + @param resource_bitmap[in] the resource bitmap + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_resource_bitmap( + ia_css_process_group_t *process_group, + const vied_nci_resource_bitmap_t resource_bitmap); + +/*! Get the routing bitmap of the process group + + @param process_group[in] process group object + + @return routing bitmap (pointer) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +const ia_css_rbm_t *ia_css_process_group_get_routing_bitmap( + const ia_css_process_group_t *process_group); + +/*! Set the routing bitmap of the process group + + @param process_group[in] process group object + @param rbm[in] routing bitmap + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_routing_bitmap( + ia_css_process_group_t *process_group, + const ia_css_rbm_t rbm); + +/*! Get IPU virtual address of process group + + @param process_group[in] process group object + @param ipu_vaddress[in/out] process group ipu virtual address + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_get_ipu_vaddress( + const ia_css_process_group_t *process_group, + vied_vaddress_t *ipu_vaddress); + +/*! Set IPU virtual address of process group + + @param process_group[in] process group object + @param ipu_vaddress[in] process group ipu address + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_ipu_vaddress( + ia_css_process_group_t *process_group, + vied_vaddress_t ipu_vaddress); + +/*! Get protocol version used by a process group + + @param process_group[in] process group object + + @return invalid protocol version on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_protocol_version( + const ia_css_process_group_t *process_group); + +/*! Get base queue id used by a process group + + @param process_group[in] process group object + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_base_queue_id( + ia_css_process_group_t *process_group); + +/*! Set base queue id used by a process group + + @param process_group[in] process group object + @param queue_id[in] process group queue id + + @return invalid queue id on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_base_queue_id( + ia_css_process_group_t *process_group, + uint8_t queue_id); + +/*! Get number of queues used by a process group + + @param process_group[in] process group object + + @return invalid number of queues (0) on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_num_queues( + ia_css_process_group_t *process_group); + +/*! Set number of queues used by a process group + + @param process_group[in] process group object + @param num_queues[in] process group number of queues + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_num_queues( + ia_css_process_group_t *process_group, + uint8_t num_queues); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_process_group_has_vp(const ia_css_process_group_t *process_group); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_group_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h new file mode 100644 index 000000000000..93cce2555de9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h @@ -0,0 +1,324 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_process_group.hsys.kernel.h + * + * Define the methods on the process group object: Hsys kernel interface + */ + +#include + +#include +#include + +#include /* uint8_t */ + +/* + * Registration of user contexts / callback info + */ + +/*! Get the user (callback) token as registered in the process group + + @param process_group[in] process group object + + @return 0 on error + */ +extern uint64_t ia_css_process_group_get_token( + ia_css_process_group_t *process_group); + +/*! Set (register) a user (callback) token in the process group + + @param process_group[in] process group object + @param token[in] user token + + Note: The token value shall be non-zero. This token is + returned in each return message related to the process + group the token is registered with. + + @return < 0 on error + */ +extern int ia_css_process_group_set_token( + ia_css_process_group_t *process_group, + const uint64_t token); + +/* + * Passing of a (fragment) watermark + */ + +/*! Get the fragment progress limit of the process group + + @param process_group[in] process group object + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_process_group_get_fragment_limit( + const ia_css_process_group_t *process_group); + +/*! Set the new fragment progress limit of the process group + + @param process_group[in] process group object + @param fragment_limit[in] New limit value + + Note: The limit value must be less or equal to the fragment + count value. The process group will not make progress beyond + the limit value. The limit value can be modified asynchronously + If the limit value is reached before an update happens, the + process group will suspend and will not automatically resume. + + The limit is monotonically increasing. The default value is + equal to the fragment count + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_fragment_limit( + ia_css_process_group_t *process_group, + const uint16_t fragment_limit); + +/*! Clear the fragment progress limit of the process group + + @param process_group[in] process group object + + Note: This function sets the fragment limit to zero. + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_clear_fragment_limit( + ia_css_process_group_t *process_group); + +/* + * Commands + */ + +/*! Perform the start command on the process group + + @param process_group[in] process group object + + Note: Start is an action of the l-Scheduler it makes the + process group eligible for execution + + Precondition: The external resources that are attached to + the process group must be in the correct state, i.e. input + buffers are not-empty and output buffers not-full + + @return < 0 on error + */ +extern int ia_css_process_group_start( + ia_css_process_group_t *process_group); + +/*! Perform the suspend command on the process group + + @param process_group[in] process group object + + Note: Suspend indicates that the process group execution + is halted at the next fragment boundary. The process group + will not automatically resume + + Precondition: The process group must be running + + @return < 0 on error + */ +extern int ia_css_process_group_suspend( + ia_css_process_group_t *process_group); + +/*! Perform the resume command on the process group + + @param process_group[in] process group object + + Note: Resume indicates that the process group is again + eligible for execution + + Precondition: The process group must be started + + @return < 0 on error + */ +extern int ia_css_process_group_resume( + ia_css_process_group_t *process_group); + +/*! Perform the reset command on the process group + + @param process_group[in] process group object + + Note: Return the process group to the started state + + Precondition: The process group must be running or stopped + + @return < 0 on error + */ +extern int ia_css_process_group_reset( + ia_css_process_group_t *process_group); + +/*! Perform the abort command on the process group + + @param process_group[in] process group object + + Note: Force the process group to the stopped state + + Precondition: The process group must be running or started + + @return < 0 on error + */ +extern int ia_css_process_group_abort( + ia_css_process_group_t *process_group); + +/*! Release ownership of the process group + + @param process_group[in] process group object + + Note: Release notifies PSYS and hands over ownership of the + process group from SW to FW + + Precondition: The process group must be in the started state + + @return < 0 on error + */ +extern int ia_css_process_group_disown( + ia_css_process_group_t *process_group); + +/* + * External resources + */ + +/*! Set (register) a data buffer to the indexed terminal in the process group + + @param process_group[in] process group object + @param buffer[in] buffer handle + @param buffer_state[in] state of the buffer + @param terminal_index[in] index of the terminal + + Note: The buffer handle shall not be VIED_NULL, the buffer + state can be undefined; BUFFER_UNDEFINED + + Note: The buffer can be in memory or streaming over memory + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_attach_buffer( + ia_css_process_group_t *process_group, + vied_vaddress_t buffer, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index); + +/*! Get (unregister) the data buffer on the indexed terminal of + * the process group + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + Precondition: The process group must be stopped + + Postcondition: The buffer handle shall be reset to VIED_NULL, the buffer + state to BUFFER_NULL + + @return VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_process_group_detach_buffer( + ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/*! Set (register) a data buffer to the indexed terminal in the process group + + @param process_group[in] process group object + @param stream[in] stream handle + @param buffer_state[in] state of the buffer + @param terminal_index[in] index of the terminal + + Note: The stream handle shall not be zero, the buffer + state can be undefined; BUFFER_UNDEFINED + + Note: The stream is used exclusive to a buffer; the latter can be in memory + or streaming over memory + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_attach_stream( + ia_css_process_group_t *process_group, + uint32_t stream, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index); + +/*! Get (unregister) the stream handle on the indexed terminal of + * the process group + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + Precondition: The process group must be stopped + + Postcondition: The stream handle shall be reset to zero, the buffer + state to BUFFER_NULL + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_detach_stream( + ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/* + * Sequencing resources + */ + +/*! Set a(n artificial) blocking resource (barrier) in + * the process group resource map + + @param process_group[in] process group object + @param barrier_index[in] index of the barrier + + Note: The barriers have to be set to force sequence between started + process groups + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index); + +/*! Clear a previously set blocking resource (barrier) in + * the process group resource map + + @param process_group[in] process group object + @param barrier_index[in] index of the barrier + + Precondition: The barriers must have been set + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_clear_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index); + +/*! Boolean test if the process group preconditions for start are satisfied + + @param process_group[in] process group object + + @return true if the process group can be started + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_process_group_start( + const ia_css_process_group_t *process_group); + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h new file mode 100644 index 000000000000..dfbcc8815c1e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h @@ -0,0 +1,199 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H +#define __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_process_group.hsys.user.h + * + * Define the methods on the process group object: Hsys user interface + */ + +#include /* ia_css_program_group_param_t */ + +#include +#include +#include + +#include "ia_css_psys_dynamic_storage_class.h" + +#include /* uint8_t */ + +/* + * Creation + */ + +/*! Compute the size of storage required for allocating the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_process_group( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Create (the storage for) the process group object + + @param process_grp_mem[in/out] raw memory for process group + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return NULL on error + */ +extern ia_css_process_group_t *ia_css_process_group_create( + void *process_grp_mem, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Destroy (the storage of) the process group object + + @param process_group[in] process group object + + @return NULL + */ +extern ia_css_process_group_t *ia_css_process_group_destroy( + ia_css_process_group_t *process_group); + +/*! Print the process group object to file/stream + + @param process_group[in] process group object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_print( + const ia_css_process_group_t *process_group, + void *fid); + +/* + * Commands + */ + +/*! Perform the submit command on the process group + + @param process_group[in] process group object + + Note: Submit is an action of the h-Scheduler it makes the + process group eligible for the l-Scheduler + + Precondition: The external resources must be attached to + the process group + + @return < 0 on error + */ +extern int ia_css_process_group_submit( + ia_css_process_group_t *process_group); + +/*! Boolean test if the process group object type is valid + + @param process_group[in] process group object + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return true if the process group is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_process_group_valid( + const ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Boolean test if the process group preconditions for submit are satisfied + + @param process_group[in] process group object + + @return true if the process group can be submitted + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_process_group_submit( + const ia_css_process_group_t *process_group); + +/*! Boolean test if the preconditions on process group and buffer set are + satisfied for enqueuing buffer set + + @param process_group[in] process group object + @param buffer_set[in] buffer set object + + @return true if the buffer set can be enqueued + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_enqueue_buffer_set( + const ia_css_process_group_t *process_group, + const ia_css_buffer_set_t *buffer_set); + +/*! Compute the cyclecount required for executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_compute_cycle_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Compute the number of processes required for + * executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern uint8_t ia_css_process_group_compute_process_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Compute the number of terminals required for + * executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern uint8_t ia_css_process_group_compute_terminal_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Get private token as registered in the process group by the implementation + + @param process_group[in] process group object + + @return 0 on error + */ +extern uint64_t ia_css_process_group_get_private_token( + ia_css_process_group_t *process_group); + +/*! Set private token in the process group as needed by the implementation + + @param process_group[in] process group object + @param token[in] user token + + Note: The token value shall be non-zero. This token is private + to the implementation. This is in addition to the user token + + @return < 0 on error, 0 on success + */ +extern int ia_css_process_group_set_private_token( + ia_css_process_group_t *process_group, + const uint64_t token); + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h new file mode 100644 index 000000000000..6ceccfc2f9bc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h @@ -0,0 +1,60 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H +#define __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H + +/*! \file */ + +/** @file ia_css_psys_process_group.psys.h + * + * Define the methods on the process group object: Psys embedded interface + */ + +#include + +/* + * Dispatcher + */ + +/*! Perform the run command on the process group + + @param process_group[in] process group object + + Note: Run indicates that the process group will execute + + Precondition: The process group must be started or + suspended and the processes have acquired the necessary + internal resources + + @return < 0 on error + */ +extern int ia_css_process_group_run( + ia_css_process_group_t *process_group); + +/*! Perform the stop command on the process group + + @param process_group[in] process group object + + Note: Stop indicates that the process group has completed execution + + Postcondition: The external resoruces can now be detached + + @return < 0 on error + */ +extern int ia_css_process_group_stop( + ia_css_process_group_t *process_group); + + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h new file mode 100644 index 000000000000..530f93ef6ce0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h @@ -0,0 +1,178 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H + +#include "type_support.h" +#include "ia_css_psys_process_group.h" +#include "ia_css_rbm_manifest_types.h" + +#define N_UINT64_IN_PROCESS_GROUP_STRUCT 2 +#define N_UINT32_IN_PROCESS_GROUP_STRUCT 5 +#define N_UINT16_IN_PROCESS_GROUP_STRUCT 5 +#define N_UINT8_IN_PROCESS_GROUP_STRUCT 7 +#define N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT 3 + +#define SIZE_OF_PROCESS_GROUP_STRUCT_BITS \ + (IA_CSS_RBM_BITS \ + + N_UINT64_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT64_T_BITS \ + + N_UINT32_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT32_T_BITS \ + + IA_CSS_PROGRAM_GROUP_ID_BITS \ + + IA_CSS_PROCESS_GROUP_STATE_BITS \ + + VIED_VADDRESS_BITS \ + + VIED_NCI_RESOURCE_BITMAP_BITS \ + + N_UINT16_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_UINT8_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_process_group_s { + /**< User (callback) token / user context reference, + * zero is an error value + */ + uint64_t token; + /**< private token / context reference, zero is an error value */ + uint64_t private_token; + /**< PG routing bitmap used to set connection between programs >*/ + ia_css_rbm_t routing_bitmap; + /**< Size of this structure */ + uint32_t size; + /**< The timestamp when PG load starts */ + uint32_t pg_load_start_ts; + /**< PG load time in cycles */ + uint32_t pg_load_cycles; + /**< PG init time in cycles */ + uint32_t pg_init_cycles; + /**< PG processing time in cycles */ + uint32_t pg_processing_cycles; + /**< Referral ID to program group FW */ + ia_css_program_group_ID_t ID; + /**< State of the process group FSM */ + ia_css_process_group_state_t state; + /**< Virtual address of process group in IPU */ + vied_vaddress_t ipu_virtual_address; + /**< Bitmap of the compute resources used by the process group */ + vied_nci_resource_bitmap_t resource_bitmap; + /**< Number of fragments offered on each terminal */ + uint16_t fragment_count; + /**< Current fragment of processing */ + uint16_t fragment_state; + /**< Watermark to control fragment processing */ + uint16_t fragment_limit; + /**< Array[process_count] of process addresses in this process group */ + uint16_t processes_offset; + /**< Array[terminal_count] of terminal addresses on this process group */ + uint16_t terminals_offset; + /**< Parameter dependent number of processes in this process group */ + uint8_t process_count; + /**< Parameter dependent number of terminals on this process group */ + uint8_t terminal_count; + /**< Parameter dependent number of independent subgraphs in + * this process group + */ + uint8_t subgraph_count; + /**< Process group protocol version */ + uint8_t protocol_version; + /**< Dedicated base queue id used for enqueueing payload buffer sets */ + uint8_t base_queue_id; + /**< Number of dedicated queues used */ + uint8_t num_queues; + /**< Mask the send_pg_done IRQ */ + uint8_t mask_irq; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT]; +}; + +/*! Callback after process group is created. Implementations can provide + * suitable actions needed when process group is created. + + @param process_group[in] process group object + @param program_group_manifest[in] program group manifest + @param program_group_param[in] program group parameters + + @return 0 on success and non-zero on failure + */ +extern int ia_css_process_group_on_create( + ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *program_group_manifest, + const ia_css_program_group_param_t *program_group_param); + +/*! Callback before process group is about to be destoyed. Any implementation + * specific cleanups can be done here. + + @param process_group[in] process group object + + @return 0 on success and non-zero on failure + */ +extern int ia_css_process_group_on_destroy( + ia_css_process_group_t *process_group); + +/* + * Command processor + */ + +/*! Execute a command locally or send it to be processed remotely + + @param process_group[in] process group object + @param cmd[in] command + + @return < 0 on error + */ +extern int ia_css_process_group_exec_cmd( + ia_css_process_group_t *process_group, + const ia_css_process_group_cmd_t cmd); + + +/*! Enqueue a buffer set corresponding to a persistent program group by + * sending a command to subsystem. + + @param process_group[in] process group object + @param buffer_set[in] buffer set + @param queue_offset[in] offset to be used from the queue id + specified in the process group object + (0 for first buffer set for frame, 1 + for late binding) + + @return < 0 on error + */ +extern int ia_css_enqueue_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset); + +/*! Enqueue a parameter buffer set corresponding to a persistent program + * group by sending a command to subsystem. + + @param process_group[in] process group object + @param buffer_set[in] parameter buffer set + + @return < 0 on error + */ +extern int ia_css_enqueue_param_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set); + +/*! Need to store the 'secure' mode for each PG for FW test app only + * + * @param process_group[in] process group object + * @param secure[in] parameter buffer set + * + * @return < 0 on error + */ +extern int ia_css_process_group_store( + ia_css_process_group_t *process_group, + bool secure); + + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h new file mode 100644 index 000000000000..4fb064dc00df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_TYPES_H +#define __IA_CSS_PSYS_PROCESS_TYPES_H + +/*! \file */ + +/** @file ia_css_psys_process_types.h + * + * The types belonging to the terminal/process/process group dynamic module + */ + +#include +#include + +#include + +#define IA_CSS_PROCESS_INVALID_PROGRAM_IDX ((uint32_t)-1) + +/* private */ +typedef enum ia_css_process_group_cmd { + IA_CSS_PROCESS_GROUP_CMD_NOP = 0, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT, + IA_CSS_PROCESS_GROUP_CMD_ATTACH, + IA_CSS_PROCESS_GROUP_CMD_DETACH, + IA_CSS_PROCESS_GROUP_CMD_START, + IA_CSS_PROCESS_GROUP_CMD_DISOWN, + IA_CSS_PROCESS_GROUP_CMD_RUN, + IA_CSS_PROCESS_GROUP_CMD_STOP, + IA_CSS_PROCESS_GROUP_CMD_SUSPEND, + IA_CSS_PROCESS_GROUP_CMD_RESUME, + IA_CSS_PROCESS_GROUP_CMD_ABORT, + IA_CSS_PROCESS_GROUP_CMD_RESET, + IA_CSS_N_PROCESS_GROUP_CMDS +} ia_css_process_group_cmd_t; + +/* private */ +#define IA_CSS_PROCESS_GROUP_STATE_BITS 32 +typedef enum ia_css_process_group_state { + IA_CSS_PROCESS_GROUP_ERROR = 0, + IA_CSS_PROCESS_GROUP_CREATED, + IA_CSS_PROCESS_GROUP_READY, + IA_CSS_PROCESS_GROUP_BLOCKED, + IA_CSS_PROCESS_GROUP_STARTED, + IA_CSS_PROCESS_GROUP_RUNNING, + IA_CSS_PROCESS_GROUP_STALLED, + IA_CSS_PROCESS_GROUP_STOPPED, + IA_CSS_N_PROCESS_GROUP_STATES +} ia_css_process_group_state_t; + +/* private */ +typedef enum ia_css_process_cmd { + IA_CSS_PROCESS_CMD_NOP = 0, + IA_CSS_PROCESS_CMD_ACQUIRE, + IA_CSS_PROCESS_CMD_RELEASE, + IA_CSS_PROCESS_CMD_START, + IA_CSS_PROCESS_CMD_LOAD, + IA_CSS_PROCESS_CMD_STOP, + IA_CSS_PROCESS_CMD_SUSPEND, + IA_CSS_PROCESS_CMD_RESUME, + IA_CSS_N_PROCESS_CMDS +} ia_css_process_cmd_t; + +/* private */ +#define IA_CSS_PROCESS_STATE_BITS 32 +typedef enum ia_css_process_state { + IA_CSS_PROCESS_ERROR = 0, + IA_CSS_PROCESS_CREATED, + IA_CSS_PROCESS_READY, + IA_CSS_PROCESS_STARTED, + IA_CSS_PROCESS_RUNNING, + IA_CSS_PROCESS_STOPPED, + IA_CSS_PROCESS_SUSPENDED, + IA_CSS_N_PROCESS_STATES +} ia_css_process_state_t; + +/* public */ +typedef struct ia_css_process_group_s ia_css_process_group_t; +typedef struct ia_css_process_s ia_css_process_t; + +typedef struct ia_css_data_terminal_s ia_css_data_terminal_t; + +#endif /* __IA_CSS_PSYS_PROCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h new file mode 100644 index 000000000000..abf398299d16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h @@ -0,0 +1,316 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_H +#define __IA_CSS_PSYS_TERMINAL_H + +/*! \file */ + +/** @file ia_css_psys_terminal.h + * + * Define the methods on the terminal object that are not part of + * a single interface + */ + +#include /* ia_css_frame_t */ +#include /* ia_css_program_group_param_t */ + +#include +#include + +#include /* bool */ +#include /* FILE */ +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest_base_types.h" + +/* + * Creation + */ +#include + +/*! Boolean test if the terminal object type is input + + @param terminal[in] terminal object + + @return true if the terminal is input, false otherwise or on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_input( + const ia_css_terminal_t *terminal); + +/*! Get the stored size of the terminal object + + @param terminal[in] terminal object + + @return size, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_terminal_get_size( + const ia_css_terminal_t *terminal); + +/*! Get the type of the terminal object + + @param terminal[in] terminal object + + @return the type of the terminal, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_type_t ia_css_terminal_get_type( + const ia_css_terminal_t *terminal); + +/*! Set the type of the terminal object + + @param terminal[in] terminal object + @param terminal_type[in] type of the terminal + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_type( + ia_css_terminal_t *terminal, + const ia_css_terminal_type_t terminal_type); + +/*! Get the index of the terminal manifest object + + @param terminal[in] terminal object + + @return the index of the terminal manifest object, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_terminal_get_terminal_manifest_index( + const ia_css_terminal_t *terminal); + +/*! Set the index of the terminal manifest object + + @param terminal[in] terminal object + @param tm_index[in] terminal manifest index + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_terminal_manifest_index( + ia_css_terminal_t *terminal, + const uint16_t tm_index); + +/*! Get id of the terminal object + + @param terminal[in] terminal object + + @return id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_ID_t ia_css_terminal_get_ID( + const ia_css_terminal_t *terminal); + +/*! Get kernel id of the data terminal object + + @param dterminal[in] data terminal object + + @return kernel id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_get_kernel_id( + const ia_css_data_terminal_t *dterminal); + +/*! Get the connection type from the terminal object + + @param terminal[in] terminal object + + @return buffer type, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_connection_type_t ia_css_data_terminal_get_connection_type( + const ia_css_data_terminal_t *dterminal); + +/*! Set the connection type of the terminal object + + @param terminal[in] terminal object + @param connection_type[in] connection type + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_data_terminal_set_connection_type( + ia_css_data_terminal_t *dterminal, + const ia_css_connection_type_t connection_type); + +/*! Get link id of the data terminal object + + @param dterminal[in] data terminal object + + @return link id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_get_link_id( + const ia_css_data_terminal_t *dterminal); + + +/*! Set link id of the terminal object + + @param terminal[in] data terminal object + @param link_id[in] synchronization link id + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_data_terminal_set_link_id( + ia_css_data_terminal_t *dterminal, + const uint8_t link_id); + +/*! Get the (pointer to) the process group parent of the terminal object + + @param terminal[in] terminal object + + @return the pointer to the parent, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_t *ia_css_terminal_get_parent( + const ia_css_terminal_t *terminal); + +/*! Set the (pointer to) the process group parent of the terminal object + + @param terminal[in] terminal object + @param parent[in] (pointer to the) process group parent object + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_parent( + ia_css_terminal_t *terminal, + ia_css_process_group_t *parent); + +/*! Boolean test if the terminal object type is valid + + @param terminal[in] process terminal object + @param terminal_manifest[in] program terminal manifest + + @return true if the process terminal object is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest); + +/* ================= Program Control Init Terminal - START ================= */ + +/*! + * Gets the program init terminal descripor size + * @param manifest[in] program control init terminal manifest + * @return size, error if < 0. + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +unsigned int +ia_css_program_control_init_terminal_get_descriptor_size( + const ia_css_program_control_init_terminal_manifest_t *manifest); + +/*! + * Initialize program control init terminal + * @param nof_fragments[in] Number of fragments + * @param terminal[in] program control init terminal + * @param manifest[in] program control init terminal manifest + * @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int +ia_css_program_control_init_terminal_init( + ia_css_program_control_init_terminal_t *terminal, + const ia_css_program_control_init_terminal_manifest_t *manifest); + +/*! + * Get a program desc for a program control init terminal + * @param terminal[in] program control init terminal + * @param manifest[in] program control init terminal manifest + * @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_program_desc_t * +ia_css_program_control_init_terminal_get_program_desc( + const ia_css_program_control_init_terminal_t *prog_ctrl_init_terminal, + const unsigned int program_index +); + +/*! + * Pretty prints the program control init termnial + * @param terminal[in] program control init terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +void ia_css_program_control_init_terminal_print( + const ia_css_program_control_init_terminal_t *terminal); + +/*! + * Gets a load section desc for a program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param load_section_index[in] section index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_load_section_desc_t * +ia_css_program_control_init_terminal_get_load_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int load_section_index +); + +/*! + * Gets process_id from program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_id_t ia_css_program_control_init_terminal_get_process_id( + const ia_css_program_control_init_program_desc_t *program_desc); + +/*! + * Set control info of program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param process_id unique process id used to identify the process + * among all active process + * @param num_done_events number of events required to close the process + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +void ia_css_program_control_init_terminal_set_control_info( + ia_css_program_control_init_program_desc_t *program_desc, + ia_css_process_id_t process_id, + uint8_t num_done_events); + +/*! + * Gets num_done_events value from program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_program_control_init_terminal_get_num_done_events( + const ia_css_program_control_init_program_desc_t *program_desc); + +/*! + * Gets a connect section desc for a program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param connect_section_index[in] section index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_connect_section_desc_t * +ia_css_program_control_init_terminal_get_connect_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int connect_section_index +); + +/* ================= Program Control Init Terminal - END ================= */ + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_terminal_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_TERMINAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h new file mode 100644 index 000000000000..b8aa08c19754 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h @@ -0,0 +1,255 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_HSYS_USER_H +#define __IA_CSS_PSYS_TERMINAL_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_terminal.hsys.user.h + * + * Define the methods on the terminal object: Hsys user interface + */ + +#include /* ia_css_frame_t */ +#include /* ia_css_program_group_param_t */ + +#include +#include + +#include /* bool */ +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_kernel_bitmap.h" + +/* + * Creation + */ + +/* + * This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __VIED_CELL marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__VIED_CELL) +/*! Compute the size of storage required for allocating the terminal object + + @param manifest[in] terminal manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_terminal( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Create the terminal object + + @param raw_mem[in] pre allocated memory + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + @param enable_bitmap program group enable bitmap + + @return NULL on error + */ +extern ia_css_terminal_t *ia_css_terminal_create( + void *raw_mem, + const ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_param_t *terminal_param, + ia_css_kernel_bitmap_t enable_bitmap); + +/*! Destroy (the storage of) the process object + + @param terminal[in] terminal object + + @return NULL + */ +extern ia_css_terminal_t *ia_css_terminal_destroy( + ia_css_terminal_t *terminal); +#endif /* !defined(__VIED_CELL) */ + +/*! Print the terminal object to file/stream + + @param terminal[in] terminal object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_print( + const ia_css_terminal_t *terminal, + void *fid); + +/*! Get the (pointer to) the frame object in the terminal object + + @param terminal[in] terminal object + + @return the pointer to the frame, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_frame_t *ia_css_data_terminal_get_frame( + const ia_css_data_terminal_t *terminal); + +/*! Get the (pointer to) the frame descriptor object in the terminal object + + @param terminal[in] terminal object + + @return the pointer to the frame descriptor, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_frame_descriptor_t *ia_css_data_terminal_get_frame_descriptor( + const ia_css_data_terminal_t *dterminal); + +/*! Get the (pointer to) the fragment descriptor object in the terminal object + + @param terminal[in] terminal object + +@return the pointer to the fragment descriptor, NULL on error +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_fragment_descriptor_t + *ia_css_data_terminal_get_fragment_descriptor( + const ia_css_data_terminal_t *dterminal, + const unsigned int fragment_index); + +/*! Get the number of fragments on the terminal + + @param terminal[in] terminal object + + @return the fragment count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_data_terminal_get_fragment_count( + const ia_css_data_terminal_t *dterminal); + +/*! Get the number of section on the (param)terminal + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + + @return the section count, 0 on error + */ +extern uint16_t ia_css_param_terminal_compute_section_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Get the number of planes on the (data)terminal + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + + @return the plane count, 1(default) on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_compute_plane_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! check if given terminal is parameter terminal. + + @param terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_parameter_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is program terminal. + + @program terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_program_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is program control init terminal. + + @program control init terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_program_control_init_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is spatial parameter terminal. + + @spatial terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_spatial_parameter_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is data terminal. + + @param terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_data_terminal( + const ia_css_terminal_t *terminal); + +/*! obtain buffer out of terminal(both data & param terminals can call this) + + @param terminal[in] (base)terminal object of either data or param terminal. + + @return vied address of buffer stored in terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_terminal_get_buffer( + const ia_css_terminal_t *terminal); + +/*!store a buffer in the terminal. + + @param terminal[in] (base)terminal object of either data or param terminal. + @param buffer[in] buffer in vied (hrt address) space. + + @return 0 on success + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_buffer(ia_css_terminal_t *terminal, + vied_vaddress_t buffer); + +/*! Obtain terminal buffer index out of terminal object + + @param terminal[in] (base)terminal object of either data or param terminal. + + @return terminal buffer index stored in terminal object on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_get_terminal_index( + const ia_css_terminal_t *terminal); + +/*! Store a terminal buffer index in the terminal object + + @param terminal[in] (base)terminal object of either data or param terminal. + @param terminal_index[in] terminal buffer index + + @return 0 on success + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_index( + ia_css_terminal_t *terminal, + unsigned int terminal_index); + +#endif /* __IA_CSS_PSYS_TERMINAL_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c new file mode 100644 index 000000000000..82d53831f9a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c @@ -0,0 +1,111 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "assert_support.h" +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_buffer_set.h" +#include "ia_css_psys_process_group.h" + +/* + * Functions to possibly inline + */ +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_buffer_set_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +STORAGE_CLASS_INLINE void __buffer_set_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF(SIZE_OF_BUFFER_SET != + CHAR_BIT * sizeof(ia_css_buffer_set_t)); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_buffer_set_t) % sizeof(uint64_t)); +} + +/* + * Functions not to inline + */ + +/* The below functions are not to be compiled for firmware */ +#if !defined(__HIVECC) + +ia_css_buffer_set_t *ia_css_buffer_set_create( + void *buffer_set_mem, + const ia_css_process_group_t *process_group, + const unsigned int frame_counter) +{ + ia_css_buffer_set_t *buffer_set = NULL; + unsigned int i; + int ret = -1; + + verifexit(buffer_set_mem != NULL); + verifexit(process_group != NULL); + + buffer_set = (ia_css_buffer_set_t *)buffer_set_mem; + + /* + * Set base struct members + */ + buffer_set->ipu_virtual_address = VIED_NULL; + ia_css_process_group_get_ipu_vaddress(process_group, + &buffer_set->process_group_handle); + buffer_set->frame_counter = frame_counter; + buffer_set->terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + /* + * Initialize adjacent buffer addresses + */ + for (i = 0; i < buffer_set->terminal_count; i++) { + vied_vaddress_t *buffer = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + sizeof(vied_vaddress_t) * i); + + *buffer = VIED_NULL; + } + ret = 0; + +EXIT: + if (ret != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_create failed\n"); + } + return buffer_set; +} + +size_t ia_css_sizeof_buffer_set( + const ia_css_process_group_t *process_group) +{ + size_t size = 0; + + verifexit(process_group != NULL); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_buffer_set(): enter:\n"); + + size = sizeof(ia_css_buffer_set_t) + + ia_css_process_group_get_terminal_count(process_group) * + sizeof(vied_vaddress_t); + +EXIT: + if (size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_buffer_set failed\n"); + } + return size; +} + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h new file mode 100644 index 000000000000..0399d76f3331 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h @@ -0,0 +1,241 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYS_BUFFER_SET_IMPL_H +#define __IA_CSS_PSYS_BUFFER_SET_IMPL_H + +#include "error_support.h" +#include "ia_css_psys_dynamic_trace.h" +#include "vied_nci_psys_system_global.h" +#include "ia_css_psys_terminal.hsys.user.h" + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_buffer( + ia_css_buffer_set_t *buffer_set, + const unsigned int terminal_index, + const vied_vaddress_t buffer) +{ + DECLARE_ERRVAL + vied_vaddress_t *buffer_ptr; + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + verifexitval(terminal_index < buffer_set->terminal_count, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_buffer(): enter:\n"); + + /* + * Set address in buffer set object + */ + buffer_ptr = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + terminal_index * sizeof(vied_vaddress_t)); + *buffer_ptr = buffer; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_buffer: invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_buffer( + const ia_css_buffer_set_t *buffer_set, + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + vied_vaddress_t buffer = VIED_NULL; + vied_vaddress_t *buffer_ptr; + int terminal_index; + + verifexitval(buffer_set != NULL, EFAULT); + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_buffer(): enter:\n"); + + /* + * Retrieve terminal index from terminal object + */ + terminal_index = ia_css_terminal_get_terminal_index(terminal); + verifexitval(terminal_index >= 0, EFAULT); + verifexitval(terminal_index < buffer_set->terminal_count, EFAULT); + + /* + * Retrieve address from buffer set object + */ + buffer_ptr = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + terminal_index * sizeof(vied_vaddress_t)); + buffer = *buffer_ptr; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_buffer: invalid argument\n"); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_ipu_address( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t ipu_vaddress) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_ipu_address(): enter:\n"); + + buffer_set->ipu_virtual_address = ipu_vaddress; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_ipu_address invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_ipu_address( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + vied_vaddress_t ipu_virtual_address = VIED_NULL; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_ipu_address(): enter:\n"); + + ipu_virtual_address = buffer_set->ipu_virtual_address; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_ipu_address: invalid argument\n"); + } + return ipu_virtual_address; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_process_group_handle( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t process_group_handle) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_process_group_context(): enter:\n"); + + buffer_set->process_group_handle = process_group_handle; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_process_group_context invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_process_group_handle( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + vied_vaddress_t process_group_handle = VIED_NULL; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_process_group_handle(): enter:\n"); + + process_group_handle = buffer_set->process_group_handle; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_process_group_handle: invalid argument\n"); + } + return process_group_handle; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_token( + ia_css_buffer_set_t *buffer_set, + const uint64_t token) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_token(): enter:\n"); + + buffer_set->token = token; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_token invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint64_t ia_css_buffer_set_get_token( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + uint64_t token = 0; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_token(): enter:\n"); + + token = buffer_set->token; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_token: invalid argument\n"); + } + return token; +} + +#endif /* __IA_CSS_PSYS_BUFFER_SET_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c new file mode 100644 index 000000000000..cca0fa73fb37 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c @@ -0,0 +1,1147 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_process.h" +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_process_private_types.h" +#include /* for NOT_USED */ + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __HIVECC marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__HIVECC) +size_t ia_css_sizeof_process( + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param) +{ + size_t size = 0, tmp_size; + + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_process(): enter:\n"); + + COMPILATION_ERROR_IF( + SIZE_OF_PROCESS_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_process_t))); + + COMPILATION_ERROR_IF(0 != sizeof(ia_css_process_t)%sizeof(uint64_t)); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + size += sizeof(ia_css_process_t); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + tmp_size = program_dependency_count*sizeof(vied_nci_resource_id_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + tmp_size = terminal_dependency_count*sizeof(uint8_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_process invalid argument\n"); + } + return size; +} + +ia_css_process_t *ia_css_process_create( + void *raw_mem, + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param, + const uint32_t program_idx) +{ + size_t tmp_size; + int retval = -1; + ia_css_process_t *process = NULL; + char *process_raw_ptr = (char *) raw_mem; + + /* size_t size = ia_css_sizeof_process(manifest, param); */ + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_create(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + verifexit(process_raw_ptr != NULL); + + process = (ia_css_process_t *) process_raw_ptr; + verifexit(process != NULL); + + process->kernel_bitmap = + ia_css_program_manifest_get_kernel_bitmap(manifest); + process->state = IA_CSS_PROCESS_CREATED; + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + /* A process requires at least one input or output */ + verifexit((program_dependency_count + + terminal_dependency_count) != 0); + + process_raw_ptr += sizeof(ia_css_process_t); + if (program_dependency_count != 0) { + process->cell_dependencies_offset = + (uint16_t) (process_raw_ptr - (char *)process); + tmp_size = + program_dependency_count * sizeof(vied_nci_resource_id_t); + process_raw_ptr += + tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + } else { + process->cell_dependencies_offset = 0; + } + + if (terminal_dependency_count != 0) { + process->terminal_dependencies_offset = + (uint16_t) (process_raw_ptr - (char *)process); + } + + process->size = (uint32_t)ia_css_sizeof_process(manifest, param); + + process->ID = ia_css_program_manifest_get_program_ID(manifest); + verifexit(process->ID != 0); + process->program_idx = program_idx; + + process->cell_dependency_count = program_dependency_count; + process->terminal_dependency_count = terminal_dependency_count; + + process->parent_offset = 0; + + verifexit(ia_css_process_clear_all(process) == 0); + + process->state = IA_CSS_PROCESS_READY; + retval = 0; + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_create(): Created successfully process %p ID 0x%x\n", + process, process->ID); + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_create failed (%i)\n", retval); + process = ia_css_process_destroy(process); + } + return process; +} + +ia_css_process_t *ia_css_process_destroy( + ia_css_process_t *process) +{ + + return process; +} +#endif + +int ia_css_process_set_cell( + ia_css_process_t *process, + const vied_nci_cell_ID_t cell_id) +{ + int retval = -1; + vied_nci_resource_bitmap_t bit_mask; + vied_nci_resource_bitmap_t resource_bitmap; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cell(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + + verifexit(parent != NULL); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + +/* Some programs are mapped on a fixed cell, + * when the process group is created + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_CREATED) || + /* If the process group has already been created, but no VP cell + * has been assigned to this process (i.e. not fixed in + * manifest), then we need to set the cell of this process + * while its parent state is READY (the ready state is set at + * the end of ia_css_process_group_create) + */ + (parent_state == IA_CSS_PROCESS_GROUP_READY))); + verifexit(state == IA_CSS_PROCESS_READY); + +/* Some programs are mapped on a fixed cell, thus check is not secure, + * but it will detect a preset, the process manager will do the secure check + */ + verifexit(ia_css_process_get_cell(process) == + VIED_NCI_N_CELL_ID); + + bit_mask = vied_nci_cell_bit_mask(cell_id); + resource_bitmap = ia_css_process_group_get_resource_bitmap(parent); + + verifexit(bit_mask != 0); + verifexit(vied_nci_is_bitmap_clear(bit_mask, resource_bitmap)); + + ia_css_process_cells_clear(process); + ia_css_process_cells_set_cell(process, 0, cell_id); + + resource_bitmap = vied_nci_bitmap_set(resource_bitmap, bit_mask); + + retval = ia_css_process_group_set_resource_bitmap( + parent, resource_bitmap); +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_cell invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_cell failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_cell( + ia_css_process_t *process) +{ + int retval = -1; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_t *parent; + vied_nci_resource_bitmap_t resource_bitmap; + vied_nci_resource_bitmap_t bit_mask; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_cell(): enter:\n"); + verifexit(process != NULL); + + cell_id = ia_css_process_get_cell(process); + parent = ia_css_process_get_parent(process); + + verifexit(parent != NULL); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + + bit_mask = vied_nci_cell_bit_mask(cell_id); + resource_bitmap = ia_css_process_group_get_resource_bitmap(parent); + + verifexit(bit_mask != 0); + verifexit(vied_nci_is_bitmap_set(bit_mask, resource_bitmap)); + + ia_css_process_cells_clear(process); + + resource_bitmap = vied_nci_bitmap_clear(resource_bitmap, bit_mask); + + retval = ia_css_process_group_set_resource_bitmap( + parent, resource_bitmap); +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_cell invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_cell failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_int_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + if (vied_nci_is_cell_mem_of_type(cell_id, mem_type_id, mem_type_id)) { + vied_nci_mem_ID_t mem_id = + vied_nci_cell_get_mem(cell_id, mem_type_id); + + process->int_mem_id[mem_type_id] = mem_id; + process->int_mem_offset[mem_type_id] = offset; + retval = 0; + } +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_int_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + int retval = -1; + uint16_t mem_index; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_int_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + +/* We could just clear the field, but lets check the state for + * consistency first + */ + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + if (vied_nci_is_cell_mem_of_type( + cell_id, mem_index, mem_type_id)) { + vied_nci_mem_ID_t mem_id = + vied_nci_cell_get_mem(cell_id, mem_index); + int mem_of_type; + + mem_of_type = + vied_nci_is_mem_of_type(mem_id, mem_type_id); + + assert(mem_of_type); + assert((process->int_mem_id[mem_type_id] == mem_id) || + (process->int_mem_id[mem_type_id] == + VIED_NCI_N_MEM_ID)); + process->int_mem_id[mem_type_id] = VIED_NCI_N_MEM_ID; + process->int_mem_offset[mem_type_id] = + IA_CSS_PROCESS_INVALID_OFFSET; + retval = 0; + } + } + +EXIT: + if (NULL == process || mem_type_id >= VIED_NCI_N_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_int_mem invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_int_mem failed (%i)\n", retval); + } +return retval; +} + +int ia_css_process_set_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_ID_t mem_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + vied_nci_mem_type_ID_t mem_type_id; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_ext_mem(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + /* Check that the memory actually exists, "vied_nci_has_cell_mem_of_id()" + * will return false on error + */ + + mem_type_id = vied_nci_mem_get_type(mem_id); + if (((!vied_nci_has_cell_mem_of_id(cell_id, mem_id) && + (mem_type_id != VIED_NCI_PMEM_TYPE_ID)) + || vied_nci_mem_is_ext_type(mem_type_id)) && + (mem_id < VIED_NCI_N_MEM_ID)) { + + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + process->ext_mem_id[mem_type_id] = mem_id; + process->ext_mem_offset[mem_type_id] = offset; + retval = 0; + } + +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_ext_mem invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_ext_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_ext_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + verifexit(parent != NULL); + verifexit(state == IA_CSS_PROCESS_READY); + + parent_state = ia_css_process_group_get_state(parent); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + + process->ext_mem_id[mem_type_id] = VIED_NCI_N_MEM_ID; + process->ext_mem_offset[mem_type_id] = IA_CSS_PROCESS_INVALID_OFFSET; + + retval = 0; +EXIT: + if (NULL == process || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_ext_mem invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_ext_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_cells_bitmap( + ia_css_process_t *process, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + int array_index = 0; + int bit_index; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cells_bitmap(): enter:\n"); + + verifexit(process != NULL); + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_CREATED) || + (parent_state == IA_CSS_PROCESS_GROUP_READY))); + verifexit(state == IA_CSS_PROCESS_READY); + + for (bit_index = 0; bit_index < VIED_NCI_N_CELL_ID; bit_index++) { + if (vied_nci_is_bit_set_in_bitmap(bitmap, bit_index)) { + verifexit(array_index < IA_CSS_PROCESS_MAX_CELLS); + ia_css_process_cells_set_cell(process, + array_index, (vied_nci_cell_ID_t)bit_index); + array_index++; + } + } + for (; array_index < IA_CSS_PROCESS_MAX_CELLS; array_index++) { + ia_css_process_cells_set_cell(process, + array_index, VIED_NCI_N_CELL_ID); + } + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_cells_bitmap invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_cells_bitmap failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dev_chn(): enter:\n"); + + verifexit(process != NULL); + verifexit(dev_chn_id <= VIED_NCI_N_DEV_CHN_ID); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + process->dev_chn_offset[dev_chn_id] = offset; + + retval = 0; +EXIT: + if (NULL == process || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_dev_chn invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_dev_chn invalid argument\n"); + } + return retval; +} + +int ia_css_process_set_dfm_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dfm_port(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_dev_id <= VIED_NCI_N_DEV_DFM_ID); + process->dfm_port_bitmap[dfm_dev_id] = bitmap; +#else + (void)bitmap; + (void)dfm_dev_id; +#endif + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_dfm_port invalid argument\n"); + } + return retval; +} + +int ia_css_process_set_dfm_active_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dfm_active_port_bitmap(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_dev_id <= VIED_NCI_N_DEV_DFM_ID); + process->dfm_active_port_bitmap[dfm_dev_id] = bitmap; +#else + (void)bitmap; + (void)dfm_dev_id; +#endif + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_dfm_active_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_process_clear_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_dev_chn(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + + verifexit(dev_chn_id <= VIED_NCI_N_DEV_CHN_ID); + + process->dev_chn_offset[dev_chn_id] = IA_CSS_PROCESS_INVALID_OFFSET; + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_dev_chn invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_dev_chn failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_all( + ia_css_process_t *process) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + int mem_index; + int dev_chn_index; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_all(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + +/* Resource clear can only be called in excluded states contrary to set */ + verifexit((parent_state != IA_CSS_PROCESS_GROUP_RUNNING) || + (parent_state == IA_CSS_N_PROCESS_GROUP_STATES)); + verifexit((state == IA_CSS_PROCESS_CREATED) || + (state == IA_CSS_PROCESS_READY)); + + for (dev_chn_index = 0; dev_chn_index < VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + process->dev_chn_offset[dev_chn_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } +/* No difference whether a cell_id has been set or not, clear all */ + for (mem_index = 0; mem_index < VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + process->ext_mem_id[mem_index] = VIED_NCI_N_MEM_ID; + process->ext_mem_offset[mem_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + process->int_mem_id[mem_index] = VIED_NCI_N_MEM_ID; + process->int_mem_offset[mem_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } + + ia_css_process_cells_clear(process); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_all invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_all failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_acquire( + ia_css_process_t *process) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_acquire(): enter:\n"); + + verifexit(process != NULL); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_acquire invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_acquire failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_release( + ia_css_process_t *process) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_release(): enter:\n"); + + verifexit(process != NULL); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_t invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_release failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_print(const ia_css_process_t *process, void *fid) +{ + int retval = -1; + int i, dev_chn_index; + uint16_t mem_index; + uint8_t cell_dependency_count, terminal_dependency_count; + vied_nci_cell_ID_t cell_id = ia_css_process_get_cell(process); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_print(process %p): enter:\n", process); + + verifexit(process != NULL); + + IA_CSS_TRACE_6(PSYSAPI_DYNAMIC, INFO, + "\tprocess %p, sizeof %d, programID %d, state %d, parent %p, cell %d\n", + process, + (int)ia_css_process_get_size(process), + (int)ia_css_process_get_program_ID(process), + (int)ia_css_process_get_state(process), + (void *)ia_css_process_get_parent(process), + (int)ia_css_process_get_cell(process)); + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + vied_nci_mem_ID_t mem_id = + (vied_nci_mem_ID_t)(process->int_mem_id[mem_index]); + if (cell_id == VIED_NCI_N_CELL_ID) { + verifexit(mem_id == VIED_NCI_N_MEM_ID); + continue; + } + verifexit(((mem_id == vied_nci_cell_get_mem(cell_id, mem_index)) + || (mem_id == VIED_NCI_N_MEM_ID))); + + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tinternal index %d, type %d, id %d offset 0x%x\n", + mem_index, + (int)vied_nci_cell_get_mem_type(cell_id, mem_index), + (int)mem_id, + process->int_mem_offset[mem_index]); + } + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + vied_nci_mem_ID_t mem_id = + (vied_nci_mem_ID_t)(process->ext_mem_id[mem_index]); + /* TODO: in case of an cells_bitmap = [], + * vied_nci_cell_get_mem_type will return a wrong result. + */ + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\texternal index %d, type %d, id %d offset 0x%x\n", + mem_index, + (int)vied_nci_cell_get_mem_type(cell_id, mem_index), + (int)mem_id, + process->ext_mem_offset[mem_index]); + NOT_USED(mem_id); + } + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tdevice channel index %d, type %d, offset 0x%x\n", + dev_chn_index, + (int)dev_chn_index, + process->dev_chn_offset[dev_chn_index]); + } +#if HAS_DFM + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_DFM_ID; + dev_chn_index++) { + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tdfm device index %d, type %d, bitmap 0x%x active_ports_bitmap 0x%x\n", + dev_chn_index, dev_chn_index, + process->dfm_port_bitmap[dev_chn_index], + process->dfm_active_port_bitmap[dev_chn_index]); + } +#endif + + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\tcells[%d] = 0x%x\n", + i, ia_css_process_cells_get_cell(process, i)); + } + + cell_dependency_count = + ia_css_process_get_cell_dependency_count(process); + if (cell_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tcell_dependencies[%d] {};\n", cell_dependency_count); + } else { + vied_nci_resource_id_t cell_dependency; + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tcell_dependencies[%d] {", cell_dependency_count); + for (i = 0; i < (int)cell_dependency_count - 1; i++) { + cell_dependency = + ia_css_process_get_cell_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d, ", cell_dependency); + } + cell_dependency = + ia_css_process_get_cell_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d}\n", cell_dependency); + (void)cell_dependency; + } + + terminal_dependency_count = + ia_css_process_get_terminal_dependency_count(process); + if (terminal_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tterminal_dependencies[%d] {};\n", + terminal_dependency_count); + } else { + uint8_t terminal_dependency; + + terminal_dependency_count = + ia_css_process_get_terminal_dependency_count(process); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tterminal_dependencies[%d] {", + terminal_dependency_count); + for (i = 0; i < (int)terminal_dependency_count - 1; i++) { + terminal_dependency = + ia_css_process_get_terminal_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d, ", terminal_dependency); + } + terminal_dependency = + ia_css_process_get_terminal_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d}\n", terminal_dependency); + (void)terminal_dependency; + } + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_print invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_parent( + ia_css_process_t *process, + ia_css_process_group_t *parent) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_parent(): enter:\n"); + + verifexit(process != NULL); + verifexit(parent != NULL); + + process->parent_offset = (uint16_t) ((char *)parent - (char *)process); + retval = 0; +EXIT: + if (NULL == process || NULL == parent) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_parent invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_parent failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_cell_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id) +{ + int retval = -1; + uint8_t *process_dep_ptr; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cell_dependency(): enter:\n"); + verifexit(process != NULL); + + process_dep_ptr = + (uint8_t *)process + process->cell_dependencies_offset + + dep_index*sizeof(vied_nci_resource_id_t); + + + *process_dep_ptr = id; + retval = 0; +EXIT: + return retval; +} + +int ia_css_process_set_terminal_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id) +{ + int retval = -1; + uint8_t *terminal_dep_ptr; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_terminal_dependency(): enter:\n"); + verifexit(process != NULL); + verifexit(ia_css_process_get_terminal_dependency_count(process) > dep_index); + + terminal_dep_ptr = + (uint8_t *)process + process->terminal_dependencies_offset + + dep_index*sizeof(uint8_t); + + *terminal_dep_ptr = id; + retval = 0; +EXIT: + return retval; +} + +int ia_css_process_cmd( + ia_css_process_t *process, + const ia_css_process_cmd_t cmd) +{ + int retval = -1; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, "ia_css_process_cmd(): enter:\n"); + + verifexit(process != NULL); + + state = ia_css_process_get_state(process); + + verifexit(state != IA_CSS_PROCESS_ERROR); + verifexit(state < IA_CSS_N_PROCESS_STATES); + + switch (cmd) { + case IA_CSS_PROCESS_CMD_NOP: + break; + case IA_CSS_PROCESS_CMD_ACQUIRE: + verifexit(state == IA_CSS_PROCESS_READY); + break; + case IA_CSS_PROCESS_CMD_RELEASE: + verifexit(state == IA_CSS_PROCESS_READY); + break; + case IA_CSS_PROCESS_CMD_START: + verifexit((state == IA_CSS_PROCESS_READY) + || (state == IA_CSS_PROCESS_STOPPED)); + process->state = IA_CSS_PROCESS_STARTED; + break; + case IA_CSS_PROCESS_CMD_LOAD: + verifexit(state == IA_CSS_PROCESS_STARTED); + process->state = IA_CSS_PROCESS_RUNNING; + break; + case IA_CSS_PROCESS_CMD_STOP: + verifexit((state == IA_CSS_PROCESS_RUNNING) + || (state == IA_CSS_PROCESS_SUSPENDED)); + process->state = IA_CSS_PROCESS_STOPPED; + break; + case IA_CSS_PROCESS_CMD_SUSPEND: + verifexit(state == IA_CSS_PROCESS_RUNNING); + process->state = IA_CSS_PROCESS_SUSPENDED; + break; + case IA_CSS_PROCESS_CMD_RESUME: + verifexit(state == IA_CSS_PROCESS_SUSPENDED); + process->state = IA_CSS_PROCESS_RUNNING; + break; + case IA_CSS_N_PROCESS_CMDS: /* Fall through */ + default: + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_cmd invalid cmd (0x%x)\n", cmd); + goto EXIT; + } + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_cmd invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_cmd failed (%i)\n", retval); + } + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c new file mode 100644 index 000000000000..46bb82804153 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c @@ -0,0 +1,886 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_process_group.h" +#include "ia_css_psys_dynamic_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_group_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* This header is need for cpu memset to 0 +* and process groups are not created in SP +*/ +#if !defined(__VIED_CELL) +#include "cpu_mem_support.h" +#endif + +/* This source file is created with the intention of sharing and +* compiled for host and firmware. Since there is no native 64bit +* data type support for firmware this wouldn't compile for SP +* tile. The part of the file that is not compilable are marked +* with the following __VIED_CELL marker and this comment. Once we +* come up with a solution to address this issue this will be +* removed. +*/ +#if !defined(__VIED_CELL) +static bool ia_css_process_group_is_program_enabled( + const ia_css_program_manifest_t *program_manifest, + ia_css_kernel_bitmap_t enable_bitmap) +{ + ia_css_kernel_bitmap_t program_bitmap = + ia_css_program_manifest_get_kernel_bitmap(program_manifest); + ia_css_program_type_t program_type = + ia_css_program_manifest_get_type(program_manifest); + ia_css_kernel_bitmap_t program_enable_bitmap; + + if (!ia_css_is_kernel_bitmap_intersection_empty(enable_bitmap, + program_bitmap)) { + + if (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB || + program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER || + program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + /* + * EXCLUSIVE_SUB programs are subsets of + * EXCLUSIVE_SUPER so the bits of the enable_bitmap + * that refer to those are those of their + * EXCLUSIVE_SUPER program (on which the depend) and + * not the subset that their own program_bitmap has + */ + if (program_type == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB || + program_type == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + ia_css_kernel_bitmap_t super_program_bitmap; + + const ia_css_program_group_manifest_t * + prog_group_manifest = + ia_css_program_manifest_get_parent(program_manifest); + uint8_t super_prog_idx = + ia_css_program_manifest_get_program_dependency( + program_manifest, 0); + const ia_css_program_manifest_t * + super_program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + prog_group_manifest, super_prog_idx); + + verifexit(super_program_manifest != NULL); + if (((program_type == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (ia_css_program_manifest_get_type( + super_program_manifest) != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER)) + || ((program_type == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) && + (ia_css_program_manifest_get_type( + super_program_manifest) != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER))) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_is_program_enabled(): Error\n"); + verifexit(0); + } + + super_program_bitmap = + ia_css_program_manifest_get_kernel_bitmap( + super_program_manifest); + program_enable_bitmap = + ia_css_kernel_bitmap_intersection( + enable_bitmap, + super_program_bitmap); + } else { + program_enable_bitmap = + ia_css_kernel_bitmap_intersection( + enable_bitmap, program_bitmap); + } + + if (ia_css_is_kernel_bitmap_equal( + program_enable_bitmap, program_bitmap)) { + return true; + } + } else if (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER) { + /* + * Virtual super programs are not selectable + * only the virtual sub programs + */ + return false; + } else { + return true; + } + } + +EXIT: + return false; +} + +static bool ia_css_process_group_is_terminal_enabled( + const ia_css_terminal_manifest_t *terminal_manifest, + ia_css_kernel_bitmap_t enable_bitmap) +{ + ia_css_terminal_type_t terminal_type; + + verifjmpexit(NULL != terminal_manifest); + terminal_type = ia_css_terminal_manifest_get_type(terminal_manifest); + + if (ia_css_is_terminal_manifest_data_terminal(terminal_manifest)) { + ia_css_data_terminal_manifest_t *data_term_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; + ia_css_kernel_bitmap_t term_bitmap = + ia_css_data_terminal_manifest_get_kernel_bitmap( + data_term_manifest); + /* + * Terminals depend on a kernel, + * if the kernel is present the program it contains and + * the terminal the program depends on are active + */ + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_bitmap)) { + return true; + } + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest)) { + ia_css_kernel_bitmap_t term_kernel_bitmap = ia_css_kernel_bitmap_clear(); + ia_css_spatial_param_terminal_manifest_t *spatial_term_man = + (ia_css_spatial_param_terminal_manifest_t *) + terminal_manifest; + + term_kernel_bitmap = + ia_css_kernel_bitmap_set( + term_kernel_bitmap, + spatial_term_man->kernel_id); + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_kernel_bitmap)) { + return true; + } + + } else if (ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest) && terminal_type == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN) { + return true; + + } else if (ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest) && terminal_type == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + /* + * For parameter out terminals, we disable the terminals + * if ALL the corresponding kernels are disabled, + * for parameter in terminals we cannot do this; + * even if kernels are disabled, it may be required that + * (HW) parameters must be supplied via the parameter + * in terminal (e.g. bypass bits). + */ + ia_css_kernel_bitmap_t term_kernel_bitmap = ia_css_kernel_bitmap_clear(); + ia_css_param_terminal_manifest_t *param_term_man = + (ia_css_param_terminal_manifest_t *)terminal_manifest; + ia_css_param_manifest_section_desc_t *section_desc; + unsigned int section = 0; + + for (section = 0; section < param_term_man-> + param_manifest_section_desc_count; section++) { + section_desc = + ia_css_param_terminal_manifest_get_prm_sct_desc( + param_term_man, section); + verifjmpexit(section_desc != NULL); + term_kernel_bitmap = ia_css_kernel_bitmap_set( + term_kernel_bitmap, + section_desc->kernel_id); + } + + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_kernel_bitmap)) { + return true; + } + } else if (ia_css_is_terminal_manifest_program_terminal( + terminal_manifest)) { + return true; + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + terminal_manifest)) { + return true; + } +EXIT: + return false; +} + +size_t ia_css_sizeof_process_group( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = 0, tmp_size; + int i, error_val = -1; + uint8_t process_count, process_num; + uint8_t terminal_count; + ia_css_kernel_bitmap_t enable_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_process_group(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + COMPILATION_ERROR_IF( + SIZE_OF_PROCESS_GROUP_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_process_group_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_process_group_t) % sizeof(uint64_t)); + + process_count = + ia_css_process_group_compute_process_count(manifest, param); + terminal_count = + ia_css_process_group_compute_terminal_count(manifest, param); + + verifexit(process_count != 0); + verifexit(terminal_count != 0); + + size += sizeof(ia_css_process_group_t); + + tmp_size = process_count * sizeof(uint16_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + + tmp_size = terminal_count * sizeof(uint16_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + process_num = 0; + for (i = 0; i < (int)ia_css_program_group_manifest_get_program_count( + manifest); i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst(manifest, i); + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + + if (ia_css_process_group_is_program_enabled( + program_manifest, enable_bitmap)) { + verifexit(process_num < process_count); + size += ia_css_sizeof_process( + program_manifest, program_param); + process_num++; + } + } + + verifexit(process_num == process_count); + + for (i = 0; i < (int)ia_css_program_group_manifest_get_terminal_count( + manifest); i++) { + ia_css_terminal_manifest_t *terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + if (ia_css_process_group_is_terminal_enabled( + terminal_manifest, enable_bitmap)) { + size += ia_css_sizeof_terminal( + terminal_manifest, param); + } + } + + error_val = 0; + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_process_group invalid argument\n"); + } + if (error_val != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_process_group ERROR(%d)\n", error_val); + } + return size; +} + +ia_css_process_group_t *ia_css_process_group_create( + void *process_grp_mem, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = ia_css_sizeof_process_group(manifest, param); + int retval = -1; + int ret; + int i; + ia_css_process_group_t *process_group = NULL; + uint8_t process_count, process_num; + uint8_t terminal_count, terminal_num; + uint16_t fragment_count; + char *process_grp_raw_ptr; + uint16_t *process_tab_ptr, *terminal_tab_ptr; + ia_css_kernel_bitmap_t enable_bitmap; + uint8_t manifest_terminal_count; + + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(process_grp_mem %p, manifest %p, group_param %p): enter:\n", + process_grp_mem, manifest, param); + + verifexit(process_grp_mem != NULL); + verifexit(manifest != NULL); + verifexit(param != NULL); + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + + process_group = (ia_css_process_group_t *)process_grp_mem; + ia_css_cpu_mem_set_zero(process_group, size); + process_grp_raw_ptr = (char *) process_group; + + process_group->state = IA_CSS_PROCESS_GROUP_CREATED; + + process_group->protocol_version = + ia_css_program_group_param_get_protocol_version(param); + + fragment_count = ia_css_program_group_param_get_fragment_count(param); + process_count = + ia_css_process_group_compute_process_count(manifest, param); + terminal_count = + ia_css_process_group_compute_terminal_count(manifest, param); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + process_group->fragment_count = fragment_count; + process_group->process_count = process_count; + process_group->terminal_count = terminal_count; + + process_grp_raw_ptr += sizeof(ia_css_process_group_t); + process_tab_ptr = (uint16_t *) process_grp_raw_ptr; + process_group->processes_offset = + (uint16_t)(process_grp_raw_ptr - (char *)process_group); + + process_grp_raw_ptr += tot_bytes_for_pow2_align( + sizeof(uint64_t), process_count * sizeof(uint16_t)); + terminal_tab_ptr = (uint16_t *) process_grp_raw_ptr; + process_group->terminals_offset = + (uint16_t)(process_grp_raw_ptr - (char *)process_group); + + /* Move raw pointer to the first process */ + process_grp_raw_ptr += tot_bytes_for_pow2_align( + sizeof(uint64_t), terminal_count * sizeof(uint16_t)); + + /* Set default */ + verifexit(ia_css_process_group_set_fragment_limit( + process_group, fragment_count) == 0); + + /* Set process group terminal dependency list */ + /* This list is used during creating the process dependency list */ + manifest_terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + terminal_num = 0; + for (i = 0; i < (int)manifest_terminal_count; i++) { + ia_css_terminal_manifest_t *t_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + verifexit(NULL != t_manifest); + if (ia_css_process_group_is_terminal_enabled( + t_manifest, enable_bitmap)) { + ia_css_terminal_t *terminal = NULL; + ia_css_terminal_param_t *terminal_param = + ia_css_program_group_param_get_terminal_param( + param, i); + + verifexit(NULL != terminal_param); + terminal_tab_ptr[terminal_num] = + (uint16_t)(process_grp_raw_ptr - + (char *)process_group); + terminal = ia_css_terminal_create( + process_grp_raw_ptr, t_manifest, + terminal_param, enable_bitmap); + verifexit(terminal != NULL); + verifexit((ia_css_terminal_set_parent( + terminal, process_group) == 0)); + verifexit((ia_css_terminal_set_terminal_manifest_index( + terminal, i) == 0)); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create: terminal_manifest_index %d\n", + i); + + process_grp_raw_ptr += ia_css_terminal_get_size( + terminal); + terminal_num++; + } + } + verifexit(terminal_num == terminal_count); + + process_num = 0; + for (i = 0; i < (int)ia_css_program_group_manifest_get_program_count( + manifest); i++) { + ia_css_process_t *process = NULL; + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + unsigned int prog_dep_index, proc_dep_index; + unsigned int term_dep_index, term_index; + + if (ia_css_process_group_is_program_enabled( + program_manifest, enable_bitmap)) { + + verifexit(process_num < process_count); + + process_tab_ptr[process_num] = + (uint16_t)(process_grp_raw_ptr - + (char *)process_group); + process = ia_css_process_create( + process_grp_raw_ptr, + program_manifest, + program_param, + i); + verifexit(process != NULL); + + ia_css_process_set_parent(process, process_group); + if (ia_css_has_program_manifest_fixed_cell( + program_manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID( + program_manifest); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create: cell_id %d\n", + cell_id); + ia_css_process_set_cell(process, cell_id); + } + + process_grp_raw_ptr += ia_css_process_get_size( + process); + /* + * Set process dependencies of process derived + * from program manifest + */ + for (prog_dep_index = 0; prog_dep_index < + ia_css_program_manifest_get_program_dependency_count( + program_manifest); prog_dep_index++) { + uint8_t dep_prog_idx = + ia_css_program_manifest_get_program_dependency( + program_manifest, prog_dep_index); + const ia_css_program_manifest_t * + dep_prg_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, dep_prog_idx); + ia_css_program_ID_t id = + ia_css_program_manifest_get_program_ID( + dep_prg_manifest); + + verifexit(id != 0); + for (proc_dep_index = 0; + proc_dep_index < process_num; + proc_dep_index++) { + ia_css_process_t *dep_process = + ia_css_process_group_get_process( + process_group, + proc_dep_index); + + ia_css_process_set_cell_dependency( + process, + prog_dep_index, 0); + + if (ia_css_process_get_program_ID( + dep_process) == id) { + ia_css_process_set_cell_dependency( + process, + prog_dep_index, + proc_dep_index); + break; + } + } + } + process_num++; + + /* + * Set terminal dependencies of process derived + * from program manifest + */ + for (term_dep_index = 0; term_dep_index < + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest); term_dep_index++) { + uint8_t pm_term_index = + ia_css_program_manifest_get_terminal_dependency + (program_manifest, term_dep_index); + + verifexit(pm_term_index < manifest_terminal_count); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(): term_dep_index: %d, pm_term_index: %d\n", + term_dep_index, pm_term_index); + for (term_index = 0; + term_index < terminal_count; + term_index++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal( + process_group, + term_index); + + if (ia_css_terminal_get_terminal_manifest_index + (terminal) == pm_term_index) { + ia_css_process_set_terminal_dependency( + process, + term_dep_index, + term_index); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create() set_terminal_dependency(process: %d, dep_idx: %d, term_idx: %d)\n", + i, term_dep_index, term_index); + + break; + } + } + } + } + } + verifexit(process_num == process_count); + + process_group->size = + (uint32_t)ia_css_sizeof_process_group(manifest, param); + process_group->ID = + ia_css_program_group_manifest_get_program_group_ID(manifest); + + /* Initialize performance measurement fields to zero */ + process_group->pg_load_start_ts = 0; + process_group->pg_load_cycles = 0; + process_group->pg_init_cycles = 0; + process_group->pg_processing_cycles = 0; + + verifexit(process_group->ID != 0); + + ret = ia_css_process_group_on_create(process_group, manifest, param); + verifexit(ret == 0); + + process_group->state = IA_CSS_PROCESS_GROUP_READY; + retval = 0; + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(): Created successfully process group ID 0x%x\n", + process_group->ID); + +EXIT: + if (NULL == process_grp_mem || NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_create failed (%i)\n", retval); + process_group = ia_css_process_group_destroy(process_group); + } + return process_group; +} + +ia_css_process_group_t *ia_css_process_group_destroy( + ia_css_process_group_t *process_group) +{ + if (process_group != NULL) { + ia_css_process_group_on_destroy(process_group); + process_group = NULL; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_destroy invalid argument\n"); + } + return process_group; +} + +int ia_css_process_group_submit( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_submit(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT); +} + +int ia_css_process_group_start( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_start(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_START); +} + +int ia_css_process_group_stop( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_stop(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_STOP); +} + +int ia_css_process_group_run( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_run(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RUN); +} + +int ia_css_process_group_suspend( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_suspend(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_SUSPEND); +} + +int ia_css_process_group_resume( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_resume(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RESUME); +} + +int ia_css_process_group_reset( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_reset(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RESET); +} + +int ia_css_process_group_abort( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_abort(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_ABORT); +} + +int ia_css_process_group_disown( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_disown(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_DISOWN); +} + +extern uint64_t ia_css_process_group_get_token( + ia_css_process_group_t *process_group) +{ + uint64_t token = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_token(): enter:\n"); + + verifexit(process_group != NULL); + + token = process_group->token; + +EXIT: + if (NULL == process_group) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_get_token invalid argument\n"); + } + return token; +} + +int ia_css_process_group_set_token( + ia_css_process_group_t *process_group, + const uint64_t token) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_token(): enter:\n"); + + verifexit(process_group != NULL); + verifexit(token != 0); + + process_group->token = token; + + retval = 0; +EXIT: + if (NULL == process_group || 0 == token) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_set_token invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_token failed (%i)\n", + retval); + } + return retval; +} + +extern uint64_t ia_css_process_group_get_private_token( + ia_css_process_group_t *process_group) +{ + uint64_t token = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_private_token(): enter:\n"); + + verifexit(process_group != NULL); + + token = process_group->private_token; + +EXIT: + if (NULL == process_group) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_get_private_token invalid argument\n"); + } + return token; +} + +int ia_css_process_group_set_private_token( + ia_css_process_group_t *process_group, + const uint64_t token) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_private_token(): enter:\n"); + + verifexit(process_group != NULL); + verifexit(token != 0); + + process_group->private_token = token; + + retval = 0; +EXIT: + if (NULL == process_group || 0 == token) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_set_private_token invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_private_token failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_process_group_compute_process_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + uint8_t process_count = 0; + ia_css_kernel_bitmap_t total_bitmap; + ia_css_kernel_bitmap_t enable_bitmap; + int i; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_compute_process_count(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + verifexit(ia_css_is_kernel_bitmap_subset(total_bitmap, enable_bitmap)); + verifexit(!ia_css_is_kernel_bitmap_empty(enable_bitmap)); + + for (i = 0; i < + (int)ia_css_program_group_manifest_get_program_count(manifest); + i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_kernel_bitmap_t program_bitmap = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest); + /* + * Programs can be orthogonal, + * a mutually exclusive subset, + * or a concurrent subset + */ + if (!ia_css_is_kernel_bitmap_intersection_empty(enable_bitmap, + program_bitmap)) { + ia_css_program_type_t program_type = + ia_css_program_manifest_get_type( + program_manifest); + /* + * An exclusive subnode < exclusive supernode, + * so simply don't count it + */ + if (program_type != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB && + program_type != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + process_count++; + } + } + } + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_compute_process_count invalid argument\n"); + } + return process_count; +} + +uint8_t ia_css_process_group_compute_terminal_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + uint8_t terminal_count = 0; + ia_css_kernel_bitmap_t total_bitmap, enable_bitmap; + int i; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_compute_terminal_count(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + verifexit(ia_css_is_kernel_bitmap_subset(total_bitmap, enable_bitmap)); + verifexit(!ia_css_is_kernel_bitmap_empty(enable_bitmap)); + + for (i = 0; i < + (int)ia_css_program_group_manifest_get_terminal_count( + manifest); i++) { + ia_css_terminal_manifest_t *tmanifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + if (ia_css_process_group_is_terminal_enabled( + tmanifest, enable_bitmap)) { + terminal_count++; + } + } + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_compute_terminal_count invalid argument\n"); + } + return terminal_count; +} +#endif /* !defined(__VIED_CELL) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h new file mode 100644 index 000000000000..f99602dc3c9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h @@ -0,0 +1,1538 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H + +#include +#include +#include "ia_css_psys_process_group_cmd_impl.h" +#include +#include +#include +#include +#include +#include +#include "ia_css_terminal_manifest_types.h" + +#include "ia_css_rbm.h" + +#include /* ia_css_kernel_bitmap_t */ + +#include +#include +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +#include "ia_css_psys_dynamic_trace.h" + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_process_group_get_fragment_limit( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint16_t fragment_limit = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + fragment_limit = process_group->fragment_limit; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit invalid argument\n"); + } + return fragment_limit; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_fragment_limit( + ia_css_process_group_t *process_group, + const uint16_t fragment_limit) +{ + DECLARE_ERRVAL + int retval = -1; + uint16_t fragment_state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + retval = ia_css_process_group_get_fragment_state(process_group, + &fragment_state); + + verifexitval(retval == 0, EINVAL); + verifexitval(fragment_limit > fragment_state, EINVAL); + verifexitval(fragment_limit <= ia_css_process_group_get_fragment_count( + process_group), EINVAL); + + process_group->fragment_limit = fragment_limit; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_clear_fragment_limit( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_clear_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + process_group->fragment_limit = 0; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_fragment_limit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_fragment_limit failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_attach_buffer( + ia_css_process_group_t *process_group, + vied_vaddress_t buffer, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + ia_css_terminal_t *terminal = NULL; + + NOT_USED(buffer_state); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_attach_buffer(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal = ia_css_process_group_get_terminal( + process_group, terminal_index); + + verifexitval(terminal != NULL, EINVAL); + verifexitval(ia_css_process_group_get_state(process_group) == + IA_CSS_PROCESS_GROUP_READY, EINVAL); + verifexitval(process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY || + process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, EINVAL); + + if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY) { + /* + * Legacy flow: + * Terminal address is part of the process group structure + */ + retval = ia_css_terminal_set_buffer( + terminal, buffer); + } else if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG) { + /* + * PPG flow: + * Terminal address is part of external buffer set structure + */ + retval = ia_css_terminal_set_terminal_index( + terminal, terminal_index); + } + verifexitval(retval == 0, EFAULT); + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\tTerminal %p has buffer 0x%x\n", terminal, buffer); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + verifexitval(frame != NULL, EINVAL); + + retval = ia_css_frame_set_buffer_state(frame, buffer_state); + verifexitval(retval == 0, EINVAL); + } + + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_buffer invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_buffer failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_process_group_detach_buffer( + ia_css_process_group_t *process_group, + const unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_vaddress_t buffer = VIED_NULL; + + ia_css_terminal_t *terminal = NULL; + ia_css_process_group_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_detach_buffer(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal = + ia_css_process_group_get_terminal( + process_group, terminal_index); + state = ia_css_process_group_get_state(process_group); + + verifexitval(terminal != NULL, EINVAL); + verifexitval(state == IA_CSS_PROCESS_GROUP_READY, EINVAL); + + buffer = ia_css_terminal_get_buffer(terminal); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + verifexitval(frame != NULL, EINVAL); + + retval = ia_css_frame_set_buffer_state(frame, IA_CSS_BUFFER_NULL); + verifexitval(retval == 0, EINVAL); + } + ia_css_terminal_set_buffer(terminal, VIED_NULL); + + retval = 0; +EXIT: + /* + * buffer pointer will appear on output, + * regardless of subsequent fails to avoid memory leaks + */ + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_buffer invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_buffer failed (%i)\n", + retval); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_attach_stream( + ia_css_process_group_t *process_group, + uint32_t stream, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_attach_stream(): enter:\n"); + + NOT_USED(process_group); + NOT_USED(stream); + NOT_USED(buffer_state); + NOT_USED(terminal_index); + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_stream failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_detach_stream( + ia_css_process_group_t *process_group, + const unsigned int terminal_index) +{ + int retval = -1; + uint32_t stream = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_detach_stream(): enter:\n"); + + NOT_USED(process_group); + NOT_USED(terminal_index); + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_stream failed (%i)\n", + retval); + } + return stream; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_nci_resource_bitmap_t bit_mask; + vied_nci_resource_bitmap_t resource_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_barrier(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = + ia_css_process_group_get_resource_bitmap(process_group); + + bit_mask = vied_nci_barrier_bit_mask(barrier_index); + + verifexitval(bit_mask != 0, EINVAL); + verifexitval(vied_nci_is_bitmap_clear(bit_mask, resource_bitmap), EINVAL); + + resource_bitmap = vied_nci_bitmap_set(resource_bitmap, bit_mask); + + retval = + ia_css_process_group_set_resource_bitmap( + process_group, resource_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_barrier invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_barrier failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_clear_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_nci_resource_bitmap_t bit_mask, resource_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_clear_barrier(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = + ia_css_process_group_get_resource_bitmap(process_group); + + bit_mask = vied_nci_barrier_bit_mask(barrier_index); + + verifexitval(bit_mask != 0, EINVAL); + verifexitval(vied_nci_is_bitmap_set(bit_mask, resource_bitmap), EINVAL); + + resource_bitmap = vied_nci_bitmap_clear(resource_bitmap, bit_mask); + + retval = + ia_css_process_group_set_resource_bitmap( + process_group, resource_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_barrier invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_barrier failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_print( + const ia_css_process_group_t *process_group, + void *fid) +{ + DECLARE_ERRVAL + int retval = -1; + int i; + + uint8_t process_count; + uint8_t terminal_count; + vied_vaddress_t ipu_vaddress = VIED_NULL; + ia_css_rbm_t routing_bitmap; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_print(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + retval = ia_css_process_group_get_ipu_vaddress(process_group, &ipu_vaddress); + verifexitval(retval == 0, EINVAL); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "=============== Process group print start ===============\n"); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tprocess_group cpu address = %p\n", process_group); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tipu_virtual_address = %#x\n", ipu_vaddress); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tsizeof(process_group) = %d\n", + (int)ia_css_process_group_get_size(process_group)); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tfragment_count = %d\n", + (int)ia_css_process_group_get_fragment_count(process_group)); + + routing_bitmap = *ia_css_process_group_get_routing_bitmap(process_group); + for (i = 0; i < (int)IA_CSS_RBM_NOF_ELEMS; i++) { + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\trouting_bitmap[index = %d] = 0x%X\n", + i, (int)routing_bitmap.data[i]); + } + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tprogram_group(process_group) = %d\n", + (int)ia_css_process_group_get_program_group_ID(process_group)); + process_count = ia_css_process_group_get_process_count(process_group); + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\t%d processes\n", (int)process_count); + for (i = 0; i < (int)process_count; i++) { + ia_css_process_t *process = + ia_css_process_group_get_process(process_group, i); + + retval = ia_css_process_print(process, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\t%d terminals\n", (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + + retval = ia_css_terminal_print(terminal, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "=============== Process group print end ===============\n"); + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_print invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_process_group_valid( + const ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *pg_manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint8_t proc_idx; + uint8_t prog_idx; + uint8_t proc_term_idx; + uint8_t process_count; + uint8_t program_count; + uint8_t terminal_count; + uint8_t man_terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_process_group_valid(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(pg_manifest != NULL, EFAULT); + NOT_USED(param); + + process_count = process_group->process_count; + terminal_count = process_group->terminal_count; + program_count = + ia_css_program_group_manifest_get_program_count(pg_manifest); + man_terminal_count = + ia_css_program_group_manifest_get_terminal_count(pg_manifest); + + /* Validate process group */ + invalid_flag = invalid_flag || + !(program_count >= process_count) || + !(man_terminal_count >= terminal_count) || + !(process_group->size > process_group->processes_offset) || + !(process_group->size > process_group->terminals_offset); + + /* Validate processes */ + for (proc_idx = 0; proc_idx < process_count; proc_idx++) { + const ia_css_process_t *process; + ia_css_program_ID_t prog_id; + bool no_match_found = true; + + process = ia_css_process_group_get_process( + process_group, proc_idx); + verifexitval(NULL != process, EFAULT); + prog_id = ia_css_process_get_program_ID(process); + for (prog_idx = 0; prog_idx < program_count; prog_idx++) { + ia_css_program_manifest_t *p_manifest = NULL; + + p_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + pg_manifest, prog_idx); + if (prog_id == + ia_css_program_manifest_get_program_ID( + p_manifest)) { + invalid_flag = invalid_flag || + !ia_css_is_process_valid( + process, p_manifest); + no_match_found = false; + break; + } + } + invalid_flag = invalid_flag || no_match_found; + } + + /* Validate terminals */ + for (proc_term_idx = 0; proc_term_idx < terminal_count; + proc_term_idx++) { + int man_term_idx; + const ia_css_terminal_t *terminal; + const ia_css_terminal_manifest_t *terminal_manifest; + + terminal = + ia_css_process_group_get_terminal( + process_group, proc_term_idx); + verifexitval(NULL != terminal, EFAULT); + man_term_idx = + ia_css_terminal_get_terminal_manifest_index(terminal); + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + pg_manifest, man_term_idx); + invalid_flag = invalid_flag || + !ia_css_is_terminal_valid(terminal, terminal_manifest); + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_process_group_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_process_group_submit( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int i; + bool can_submit = false; + int retval = -1; + uint8_t terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_submit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + vied_vaddress_t buffer; + ia_css_buffer_state_t buffer_state; + + verifexitval(terminal != NULL, EINVAL); + + if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY) { + /* + * For legacy pg flow, buffer addresses are contained inside + * the process group structure, so these need to be validated + * on process group submission. + */ + buffer = ia_css_terminal_get_buffer(terminal); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tH: Terminal number(%d) is %p having buffer 0x%x\n", + i, terminal, buffer); + } + + /* buffer_state is applicable only for data terminals*/ + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EINVAL); + buffer_state = ia_css_frame_get_buffer_state(frame); + if ((buffer_state == IA_CSS_BUFFER_NULL) || + (buffer_state == IA_CSS_N_BUFFER_STATES)) { + break; + } + } else if ( + (ia_css_is_terminal_parameter_terminal(terminal) + != true) && + (ia_css_is_terminal_program_terminal(terminal) + != true) && + (ia_css_is_terminal_program_control_init_terminal(terminal) + != true) && + (ia_css_is_terminal_spatial_parameter_terminal( + terminal) != true)) { + /* neither data nor parameter terminal, so error.*/ + break; + } + + } + /* Only true if no check failed */ + can_submit = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit failed (%i)\n", + retval); + } + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_submit(): leave:\n"); + return can_submit; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_enqueue_buffer_set( + const ia_css_process_group_t *process_group, + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + int i; + bool can_enqueue = false; + int retval = -1; + uint8_t terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_enqueue_buffer_set(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(buffer_set != NULL, EFAULT); + + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + /* + * For ppg flow, buffer addresses are contained in the + * external buffer set structure, so these need to be + * validated before enqueueing. + */ + verifexitval(process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, EFAULT); + + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + vied_vaddress_t buffer; + ia_css_buffer_state_t buffer_state; + + verifexitval(terminal != NULL, EINVAL); + + buffer = ia_css_buffer_set_get_buffer(buffer_set, terminal); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tH: Terminal number(%d) is %p having buffer 0x%x\n", + i, terminal, buffer); + + /* buffer_state is applicable only for data terminals*/ + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EINVAL); + buffer_state = ia_css_frame_get_buffer_state(frame); + if ((buffer_state == IA_CSS_BUFFER_NULL) || + (buffer_state == IA_CSS_N_BUFFER_STATES)) { + break; + } + } else if ( + (ia_css_is_terminal_parameter_terminal(terminal) + != true) && + (ia_css_is_terminal_program_terminal(terminal) + != true) && + (ia_css_is_terminal_program_control_init_terminal(terminal) + != true) && + (ia_css_is_terminal_spatial_parameter_terminal( + terminal) != true)) { + /* neither data nor parameter terminal, so error.*/ + break; + } + } + /* Only true if no check failed */ + can_enqueue = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_enqueue_buffer_set invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_enqueue_buffer_set failed (%i)\n", + retval); + } + return can_enqueue; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_process_group_start( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int i; + bool can_start = false; + int retval = -1; + uint8_t terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_start(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + ia_css_buffer_state_t buffer_state; + bool ok = false; + + verifexitval(terminal != NULL, EINVAL); + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* + * buffer_state is applicable only for data terminals + */ + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + bool is_input = ia_css_is_terminal_input(terminal); + /* + * check for NULL here. + * then invoke next 2 statements + */ + verifexitval(frame != NULL, EINVAL); + IA_CSS_TRACE_5(PSYSAPI_DYNAMIC, VERBOSE, + "\tTerminal %d: buffer_state %u, access_type %u, data_bytes %u, data %u\n", + i, frame->buffer_state, frame->access_type, + frame->data_bytes, frame->data); + buffer_state = ia_css_frame_get_buffer_state(frame); + + ok = ((is_input && + (buffer_state == IA_CSS_BUFFER_FULL)) || + (!is_input && (buffer_state == + IA_CSS_BUFFER_EMPTY))); + + } else if (ia_css_is_terminal_parameter_terminal(terminal) == + true) { + /* + * FIXME: + * is there any pre-requisite for param_terminal? + */ + ok = true; + } else if (ia_css_is_terminal_program_terminal(terminal) == + true) { + ok = true; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) == + true) { + ok = true; + } else if (ia_css_is_terminal_spatial_parameter_terminal( + terminal) == true) { + ok = true; + } else { + /* neither data nor parameter terminal, so error.*/ + break; + } + + if (!ok) + break; + } + /* Only true if no check failed */ + can_start = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_start failed (%i)\n", + retval); + } + return can_start; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_process_group_get_size( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_size(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + size = process_group->size; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_state_t ia_css_process_group_get_state( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + ia_css_process_group_state_t state = IA_CSS_N_PROCESS_GROUP_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + state = process_group->state; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_state invalid argument\n"); + } + return state; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +const ia_css_rbm_t *ia_css_process_group_get_routing_bitmap( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + const ia_css_rbm_t *rbm = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_routing_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + rbm = &(process_group->routing_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_routing_bitmap invalid argument\n"); + } + return rbm; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_process_group_get_fragment_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + fragment_count = process_group->fragment_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_count invalid argument\n"); + } + return fragment_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_process_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t process_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_process_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_count = process_group->process_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_process_count invalid argument\n"); + } + return process_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_terminal_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal_count = process_group->terminal_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_load_start_ts( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_load_start_ts = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_load_start_ts(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_load_start_ts = process_group->pg_load_start_ts; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_load_start_ts invalid argument\n"); + } + return pg_load_start_ts; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_load_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_load_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_load_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_load_cycles = process_group->pg_load_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_load_cycles invalid argument\n"); + } + return pg_load_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_init_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_init_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_init_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_init_cycles = process_group->pg_init_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_init_cycles invalid argument\n"); + } + return pg_init_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_processing_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_processing_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_processing_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_processing_cycles = process_group->pg_processing_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_processing_cycles invalid argument\n"); + } + return pg_processing_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_t *ia_css_process_group_get_terminal_from_type( + const ia_css_process_group_t *process_group, + const ia_css_terminal_type_t terminal_type) +{ + unsigned int proc_cnt; + ia_css_terminal_t *terminal = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal_from_type(): enter:\n"); + + for (proc_cnt = 0; proc_cnt < (unsigned int)ia_css_process_group_get_terminal_count(process_group); proc_cnt++) { + terminal = ia_css_process_group_get_terminal(process_group, proc_cnt); + if (terminal == NULL) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal_from_type() Failed to get terminal %d", proc_cnt); + goto EXIT; + } + if (ia_css_terminal_get_type(terminal) == terminal_type) { + return terminal; + } + terminal = NULL; /* If not the expected type, return NULL */ + } +EXIT: + return terminal; +} + +/* Returns the terminal or NULL if it was not found + For some of those maybe valid to not exist at all in the process group */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +const ia_css_terminal_t *ia_css_process_group_get_single_instance_terminal( + const ia_css_process_group_t *process_group, + ia_css_terminal_type_t term_type) +{ + int i, term_count; + + assert(process_group != NULL); + + /* Those below have at most one instance per process group */ + assert(term_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + term_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT || + term_type == IA_CSS_TERMINAL_TYPE_PROGRAM || + term_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); + + term_count = ia_css_process_group_get_terminal_count(process_group); + + for (i = 0; i < term_count; i++) { + const ia_css_terminal_t *terminal = ia_css_process_group_get_terminal(process_group, i); + + if (ia_css_terminal_get_type(terminal) == term_type) { + /* Only one parameter terminal per process group */ + return terminal; + } + } + + return NULL; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_t *ia_css_process_group_get_terminal( + const ia_css_process_group_t *process_grp, + const unsigned int terminal_num) +{ + DECLARE_ERRVAL + ia_css_terminal_t *terminal_ptr = NULL; + uint16_t *terminal_offset_table; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal(): enter:\n"); + + verifexitval(process_grp != NULL, EFAULT); + verifexitval(terminal_num < process_grp->terminal_count, EINVAL); + + terminal_offset_table = + (uint16_t *)((char *)process_grp + + process_grp->terminals_offset); + terminal_ptr = + (ia_css_terminal_t *)((char *)process_grp + + terminal_offset_table[terminal_num]); + + verifexitval(terminal_ptr != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal invalid argument\n"); + } + return terminal_ptr; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_t *ia_css_process_group_get_process( + const ia_css_process_group_t *process_grp, + const unsigned int process_num) +{ + DECLARE_ERRVAL + ia_css_process_t *process_ptr = NULL; + uint16_t *process_offset_table; + + verifexitval(process_grp != NULL, EFAULT); + verifexitval(process_num < process_grp->process_count, EINVAL); + + process_offset_table = + (uint16_t *)((char *)process_grp + + process_grp->processes_offset); + process_ptr = + (ia_css_process_t *)((char *)process_grp + + process_offset_table[process_num]); + + verifexitval(process_ptr != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_process invalid argument\n"); + } + return process_ptr; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_group_ID_t ia_css_process_group_get_program_group_ID( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + ia_css_program_group_ID_t id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_program_group_ID(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + id = process_group->ID; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_program_group_ID invalid argument\n"); + } + return id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_group_get_resource_bitmap( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t resource_bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_resource_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = process_group->resource_bitmap; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_resource_bitmap invalid argument\n"); + } + return resource_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_resource_bitmap( + ia_css_process_group_t *process_group, + const vied_nci_resource_bitmap_t resource_bitmap) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_resource_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->resource_bitmap = resource_bitmap; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_resource_bitmap invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_resource_bitmap failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_routing_bitmap( + ia_css_process_group_t *process_group, + const ia_css_rbm_t rbm) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_routing_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + process_group->routing_bitmap = rbm; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_routing_bitmap invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_routing_bitmap failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_compute_cycle_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + uint32_t cycle_count = 0; + + NOT_USED(manifest); + NOT_USED(param); + + verifexitval(manifest != NULL, EFAULT); + verifexitval(param != NULL, EFAULT); + + cycle_count = 1; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_compute_cycle_count invalid argument\n"); + } + return cycle_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_fragment_state( + ia_css_process_group_t *process_group, + uint16_t fragment_state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_set_fragment_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(fragment_state <= ia_css_process_group_get_fragment_count( + process_group), EINVAL); + + process_group->fragment_state = fragment_state; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_fragment_state invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_fragment_state failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_get_fragment_state( + const ia_css_process_group_t *process_group, + uint16_t *fragment_state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(fragment_state != NULL, EFAULT); + + *fragment_state = process_group->fragment_state; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_state invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_state failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_get_ipu_vaddress( + const ia_css_process_group_t *process_group, + vied_vaddress_t *ipu_vaddress) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_ipu_vaddress(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(ipu_vaddress != NULL, EFAULT); + + *ipu_vaddress = process_group->ipu_virtual_address; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_ipu_vaddress invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_ipu_vaddress failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_ipu_vaddress( + ia_css_process_group_t *process_group, + vied_vaddress_t ipu_vaddress) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_ipu_vaddress(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->ipu_virtual_address = ipu_vaddress; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_ipu_vaddress invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_ipu_vaddress failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_protocol_version( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t protocol_version = IA_CSS_PROCESS_GROUP_N_PROTOCOLS; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_protocol_version(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + protocol_version = process_group->protocol_version; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_protocol_version invalid argument\n"); + } + return protocol_version; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_base_queue_id( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t queue_id = IA_CSS_N_PSYS_CMD_QUEUE_ID; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_base_queue_id(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + queue_id = process_group->base_queue_id; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_base_queue_id invalid argument\n"); + } + return queue_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_base_queue_id( + ia_css_process_group_t *process_group, + uint8_t queue_id) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_base_queue_id(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->base_queue_id = queue_id; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_base_queue_id invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_num_queues( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t num_queues = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_num_queues(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + num_queues = process_group->num_queues; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_num_queues invalid argument\n"); + } + return num_queues; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_num_queues( + ia_css_process_group_t *process_group, + uint8_t num_queues) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_num_queues(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->num_queues = num_queues; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_num_queues invalid argument\n"); + } + return retval; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_process_group_has_vp(const ia_css_process_group_t *process_group) +{ + bool has_vp = false; + uint32_t i; + + uint8_t process_count = ia_css_process_group_get_process_count(process_group); + + for (i = 0; i < process_count; i++) { + ia_css_process_t *process; + vied_nci_cell_ID_t cell_id; + + process = ia_css_process_group_get_process(process_group, i); + cell_id = ia_css_process_get_cell(process); + + if (VIED_NCI_VP_TYPE_ID == vied_nci_cell_get_type(cell_id)) { + has_vp = true; + break; + } + } + + return has_vp; +} + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h new file mode 100644 index 000000000000..5d0303012700 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h @@ -0,0 +1,637 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_IMPL_H +#define __IA_CSS_PSYS_PROCESS_IMPL_H + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_process_private_types.h" + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process.h */ +STORAGE_CLASS_INLINE vied_nci_cell_ID_t ia_css_process_cells_get_cell(const ia_css_process_t *process, int index) +{ + assert(index < IA_CSS_PROCESS_MAX_CELLS); + if (index >= IA_CSS_PROCESS_MAX_CELLS) { + return VIED_NCI_N_CELL_ID; + } +#if IA_CSS_PROCESS_MAX_CELLS == 1 + return process->cell_id; +#else + return process->cells[index]; +#endif +} + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process.h */ +STORAGE_CLASS_INLINE void ia_css_process_cells_set_cell(ia_css_process_t *process, int index, vied_nci_cell_ID_t cell_id) +{ + assert(index < IA_CSS_PROCESS_MAX_CELLS); + if (index >= IA_CSS_PROCESS_MAX_CELLS) { + return; + } +#if IA_CSS_PROCESS_MAX_CELLS == 1 + process->cell_id = cell_id; +#else + process->cells[index] = cell_id; +#endif +} + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process */ +STORAGE_CLASS_INLINE void ia_css_process_cells_clear(ia_css_process_t *process) +{ + int i; + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + ia_css_process_cells_set_cell(process, i, VIED_NCI_N_CELL_ID); + } +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_cell_ID_t ia_css_process_get_cell( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_cell_ID_t cell_id = VIED_NCI_N_CELL_ID; + int i = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +#if IA_CSS_PROCESS_MAX_CELLS > 1 + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + assert(VIED_NCI_N_CELL_ID == ia_css_process_cells_get_cell(process, i)); +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } +#else + (void)i; +#endif + cell_id = ia_css_process_cells_get_cell(process, 0); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell invalid argument\n"); + } + return cell_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_mem_ID_t ia_css_process_get_ext_mem_id( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_ext_mem(): enter:\n"); + + verifexitval(process != NULL && mem_type < VIED_NCI_N_DATA_MEM_TYPE_ID, EFAULT); + +EXIT: + if (!noerror()) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_ext_mem invalid argument\n"); + return IA_CSS_PROCESS_INVALID_OFFSET; + } + return process->ext_mem_id[mem_type]; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_get_program_idx( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_program_idx(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_program_idx invalid argument\n"); + return IA_CSS_PROCESS_INVALID_PROGRAM_IDX; + } + return process->program_idx; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_dev_chn( + const ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dev_chn(): enter:\n"); + + verifexitval(process != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID, EFAULT); + +EXIT: + if (!noerror()) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dev_chn(): invalid arguments\n"); + return IA_CSS_PROCESS_INVALID_OFFSET; + } + return process->dev_chn_offset[dev_chn_id]; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_int_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_id) +{ + DECLARE_ERRVAL + vied_nci_resource_size_t int_mem_offset = IA_CSS_PROCESS_INVALID_OFFSET; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_int_mem_offset(): enter:\n"); + + verifexitval(process != NULL && mem_id < VIED_NCI_N_MEM_TYPE_ID, EFAULT); + +EXIT: + if (noerror()) { + int_mem_offset = process->int_mem_offset[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_int_mem_offset invalid argument\n"); + } + + return int_mem_offset; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_ext_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + DECLARE_ERRVAL + vied_nci_resource_size_t ext_mem_offset = IA_CSS_PROCESS_INVALID_OFFSET; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_ext_mem_offset(): enter:\n"); + + verifexitval(process != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID, EFAULT); + +EXIT: + if (noerror()) { + ext_mem_offset = process->ext_mem_offset[mem_type_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_ext_mem_offset invalid argument\n"); + } + + return ext_mem_offset; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_process_get_size( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_size(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (noerror()) { + size = process->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_size invalid argument\n"); + } + + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_state_t ia_css_process_get_state( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_process_state_t state = IA_CSS_N_PROCESS_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_state(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (noerror()) { + state = process->state; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_state invalid argument\n"); + } + + return state; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_set_state( + ia_css_process_t *process, + ia_css_process_state_t state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_state(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + process->state = state; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_state invalid argument\n"); + } + + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_cell_dependency_count( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + uint8_t cell_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_dependency_count(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + cell_dependency_count = process->cell_dependency_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell_dependency_count invalid argument\n"); + } + return cell_dependency_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_terminal_dependency_count( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + uint8_t terminal_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_terminal_dependency_count(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + terminal_dependency_count = process->terminal_dependency_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_terminal_dependency_count invalid argument process\n"); + } + return terminal_dependency_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_t *ia_css_process_get_parent( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_parent(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + parent = + (ia_css_process_group_t *) ((char *)process + process->parent_offset); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_parent invalid argument process\n"); + } + return parent; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_ID_t ia_css_process_get_program_ID( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_program_ID_t id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_program_ID(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + id = process->ID; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_program_ID invalid argument process\n"); + } + return id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_id_t ia_css_process_get_cell_dependency( + const ia_css_process_t *process, + const unsigned int cell_num) +{ + DECLARE_ERRVAL + vied_nci_resource_id_t cell_dependency = + IA_CSS_PROCESS_INVALID_DEPENDENCY; + vied_nci_resource_id_t *cell_dep_ptr = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_dependency(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + verifexitval(cell_num < process->cell_dependency_count, EFAULT); + + cell_dep_ptr = + (vied_nci_resource_id_t *) + ((char *)process + process->cell_dependencies_offset); + cell_dependency = *(cell_dep_ptr + cell_num); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell_dependency invalid argument\n"); + } + return cell_dependency; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_terminal_dependency( + const ia_css_process_t *process, + const unsigned int terminal_num) +{ + DECLARE_ERRVAL + uint8_t *ter_dep_ptr = NULL; + uint8_t ter_dep = IA_CSS_PROCESS_INVALID_DEPENDENCY; + + verifexitval(process != NULL, EFAULT); + verifexitval(terminal_num < process->terminal_dependency_count, EFAULT); + + ter_dep_ptr = (uint8_t *) ((char *)process + + process->terminal_dependencies_offset); + + ter_dep = *(ter_dep_ptr + terminal_num); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_terminal_dependency invalid argument\n"); + } + return ter_dep; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_kernel_bitmap_t ia_css_process_get_kernel_bitmap( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_kernel_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + bitmap = process->kernel_bitmap; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_kernel_bitmap invalid argument process\n"); + } + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_cells_bitmap( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + vied_nci_cell_ID_t cell_id; + int i = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + cell_id = ia_css_process_cells_get_cell(process, i); + if (VIED_NCI_N_CELL_ID != cell_id) { + bitmap |= (1 << cell_id); + } +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cells_bitmap invalid argument process\n"); + } + + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_port_bitmap_ptr( + ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t *p_bitmap = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + p_bitmap = &process->dfm_port_bitmap[0]; +#else + p_bitmap = NULL; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return p_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_active_port_bitmap_ptr( + ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t *p_bitmap = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + p_bitmap = &process->dfm_active_port_bitmap[0]; +#else + p_bitmap = NULL; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return p_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_dfm_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexitval(dfm_res_id < VIED_NCI_N_DEV_DFM_ID, EFAULT); + bitmap = process->dfm_port_bitmap[dfm_res_id]; +#else + bitmap = 0; + (void)dfm_res_id; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_dfm_active_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_active_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexitval(dfm_res_id < VIED_NCI_N_DEV_DFM_ID, EFAULT); + bitmap = process->dfm_active_port_bitmap[dfm_res_id]; +#else + bitmap = 0; + (void)dfm_res_id; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_active_port_bitmap invalid argument process\n"); + } + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_process_valid( + const ia_css_process_t *process, + const ia_css_program_manifest_t *p_manifest) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + ia_css_program_ID_t prog_id; + ia_css_kernel_bitmap_t prog_kernel_bitmap; + + verifexitval(NULL != process, EFAULT); + verifexitval(NULL != p_manifest, EFAULT); + + prog_id = ia_css_process_get_program_ID(process); + verifjmpexit(prog_id == + ia_css_program_manifest_get_program_ID(p_manifest)); + + prog_kernel_bitmap = + ia_css_program_manifest_get_kernel_bitmap(p_manifest); + + invalid_flag = (process->size <= process->cell_dependencies_offset) || + (process->size <= process->terminal_dependencies_offset) || + !ia_css_is_kernel_bitmap_subset(prog_kernel_bitmap, + process->kernel_bitmap); + + if (ia_css_has_program_manifest_fixed_cell(p_manifest)) { + vied_nci_cell_ID_t cell_id; + + cell_id = ia_css_program_manifest_get_cell_ID(p_manifest); + invalid_flag = invalid_flag || + (cell_id != (vied_nci_cell_ID_t)(ia_css_process_get_cell(process))); + } + invalid_flag = invalid_flag || + ((process->cell_dependency_count + + process->terminal_dependency_count) == 0) || + (process->cell_dependency_count != + ia_css_program_manifest_get_program_dependency_count(p_manifest)) || + (process->terminal_dependency_count != + ia_css_program_manifest_get_terminal_dependency_count(p_manifest)); + + /* TODO: to be removed once all PGs pass validation */ + if (invalid_flag == true) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_is_process_valid(): false\n"); + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_process_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +#endif /* __IA_CSS_PSYS_PROCESS_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h new file mode 100644 index 000000000000..ae0affde9718 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h @@ -0,0 +1,87 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H +#define __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H + +#include "ia_css_psys_process_types.h" +#include "vied_nci_psys_resource_model.h" + +#define N_UINT32_IN_PROCESS_STRUCT 2 +#define N_UINT16_IN_PROCESS_STRUCT 3 +#define N_UINT8_IN_PROCESS_STRUCT 2 + +#define SIZE_OF_PROCESS_STRUCT_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + (N_UINT32_IN_PROCESS_STRUCT * 32) \ + + IA_CSS_PROGRAM_ID_BITS \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + IA_CSS_PROCESS_STATE_BITS \ + + (N_UINT16_IN_PROCESS_STRUCT * 16) \ + + (VIED_NCI_N_MEM_TYPE_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (VIED_NCI_N_DATA_MEM_TYPE_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (VIED_NCI_N_DEV_CHN_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (IA_CSS_PROCESS_MAX_CELLS * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_N_MEM_TYPE_ID * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_N_DATA_MEM_TYPE_ID * VIED_NCI_RESOURCE_ID_BITS) \ + + (N_UINT8_IN_PROCESS_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_PROCESS_STRUCT * 8)) + +struct ia_css_process_s { + /**< Indicate which kernels lead to this process being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + uint32_t size; /**< Size of this structure */ + ia_css_program_ID_t ID; /**< Referal ID to a specific program FW */ + uint32_t program_idx; /**< Program Index into the PG manifest */ +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM port allocated to this process */ + vied_nci_resource_bitmap_t dfm_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; + /**< Active DFM ports which need a kick */ + vied_nci_resource_bitmap_t dfm_active_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; +#endif + /**< State of the process FSM dependent on the parent FSM */ + ia_css_process_state_t state; + int16_t parent_offset; /**< Reference to the process group */ + /**< Array[dependency_count] of ID's of the cells that provide input */ + uint16_t cell_dependencies_offset; + /**< Array[terminal_dependency_count] of indices of connected terminals */ + uint16_t terminal_dependencies_offset; + /**< (internal) Memory allocation offset given to this process */ + vied_nci_resource_size_t int_mem_offset[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory allocation offset given to this process */ + vied_nci_resource_size_t ext_mem_offset[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Device channel allocation offset given to this process */ + vied_nci_resource_size_t dev_chn_offset[VIED_NCI_N_DEV_CHN_ID]; + /**< Cells (VP, ACB) allocated for the process*/ +#if IA_CSS_PROCESS_MAX_CELLS == 1 + vied_nci_resource_id_t cell_id; +#else + vied_nci_resource_id_t cells[IA_CSS_PROCESS_MAX_CELLS]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + /**< (internal) Memory ID; This is redundant, derived from cell_id */ + vied_nci_resource_id_t int_mem_id[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory ID */ + vied_nci_resource_id_t ext_mem_id[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Number of processes (mapped on cells) this process depends on */ + uint8_t cell_dependency_count; + /**< Number of terminals this process depends on */ + uint8_t terminal_dependency_count; + /**< Padding bytes for 64bit alignment*/ +#if (N_PADDING_UINT8_IN_PROCESS_STRUCT > 0) + uint8_t padding[N_PADDING_UINT8_IN_PROCESS_STRUCT]; +#endif /*(N_PADDING_UINT8_IN_PROCESS_STRUCT > 0)*/ +}; + +#endif /* __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c new file mode 100644 index 000000000000..ea406f229273 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c @@ -0,0 +1,604 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_terminal_private_types.h" +#include "ia_css_terminal_types.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_terminal_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +STORAGE_CLASS_INLINE void __terminal_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_terminal_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_section_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SPATIAL_PARAM_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_spatial_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_spatial_param_terminal_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAME_GRID_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_frame_grid_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != sizeof( + ia_css_frame_grid_param_section_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_GRID_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_grid_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_grid_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICED_PARAM_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_sliced_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_terminal_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAGMENT_SLICE_DESC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_slice_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_slice_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICE_PARAM_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_slice_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_slice_param_section_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_program_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_terminal_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_SEQ_INFO_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_info_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_SEQ_COMMANDS_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_kernel_fragment_sequencer_command_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_param_section_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_LOAD_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_load_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_load_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_CONNECT_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_connect_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_connect_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS != + (CHAR_BIT * + sizeof(struct ia_css_program_desc_control_info_s))); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_PROG_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_program_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_program_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_program_control_init_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_terminal_t) % + sizeof(uint64_t)); +} + +/* + * Functions not to inline + */ + +/* + * This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __VIED_CELL marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__VIED_CELL) +size_t ia_css_sizeof_terminal( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = 0; + uint16_t fragment_count = + ia_css_program_group_param_get_fragment_count(param); + + COMPILATION_ERROR_IF( + SIZE_OF_DATA_TERMINAL_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_data_terminal_t))); + + COMPILATION_ERROR_IF( + 0 != sizeof(ia_css_data_terminal_t)%sizeof(uint64_t)); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_terminal(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + if (ia_css_is_terminal_manifest_parameter_terminal(manifest)) { + const ia_css_param_terminal_manifest_t *param_term_man = + (const ia_css_param_terminal_manifest_t *)manifest; + if (ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN) { + size = ia_css_param_in_terminal_get_descriptor_size( + param_term_man->param_manifest_section_desc_count); + } else if (ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + size = ia_css_param_out_terminal_get_descriptor_size( + param_term_man->param_manifest_section_desc_count, + fragment_count); + } else { + assert(NULL == "Invalid parameter terminal type"); + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_terminal(): Invalid parameter terminal type:\n"); + verifjmpexit(0); + } + } else if (ia_css_is_terminal_manifest_data_terminal(manifest)) { + size += sizeof(ia_css_data_terminal_t); + size += fragment_count * sizeof(ia_css_fragment_descriptor_t); + } else if (ia_css_is_terminal_manifest_program_terminal(manifest)) { + ia_css_program_terminal_manifest_t *prog_term_man = + (ia_css_program_terminal_manifest_t *)manifest; + + size = ia_css_program_terminal_get_descriptor_size( + fragment_count, + prog_term_man-> + fragment_param_manifest_section_desc_count, + prog_term_man-> + kernel_fragment_sequencer_info_manifest_info_count, + (fragment_count * prog_term_man-> + max_kernel_fragment_sequencer_command_desc)); + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + manifest)) { + ia_css_spatial_param_terminal_manifest_t *spatial_param_term = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + size = ia_css_spatial_param_terminal_get_descriptor_size( + spatial_param_term-> + frame_grid_param_manifest_section_desc_count, + fragment_count); + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + manifest)) { + ia_css_program_control_init_terminal_manifest_t *progctrlinit_term_man = + (ia_css_program_control_init_terminal_manifest_t *)manifest; + + size = ia_css_program_control_init_terminal_get_descriptor_size( + progctrlinit_term_man); + } +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_terminal invalid argument\n"); + } + return size; +} + +ia_css_terminal_t *ia_css_terminal_create( + void *raw_mem, + const ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_param_t *terminal_param, + ia_css_kernel_bitmap_t enable_bitmap) +{ + char *terminal_raw_ptr; + ia_css_terminal_t *terminal = NULL; + uint16_t fragment_count; + int i, j; + int retval = -1; + ia_css_program_group_param_t *param; + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_create(manifest %p, terminal_param %p): enter:\n", + manifest, terminal_param); + + param = ia_css_terminal_param_get_parent(terminal_param); + fragment_count = ia_css_program_group_param_get_fragment_count(param); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + terminal_raw_ptr = (char *) raw_mem; + + terminal = (ia_css_terminal_t *) terminal_raw_ptr; + verifexit(terminal != NULL); + + terminal->size = (uint16_t)ia_css_sizeof_terminal(manifest, param); + verifexit(ia_css_terminal_set_type( + terminal, ia_css_terminal_manifest_get_type(manifest)) == 0); + + terminal->ID = ia_css_terminal_manifest_get_ID(manifest); + + verifexit(ia_css_terminal_set_buffer(terminal, + VIED_NULL) == 0); + + if (ia_css_is_terminal_manifest_data_terminal(manifest) == true) { + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + ia_css_kernel_bitmap_t intersection = + ia_css_kernel_bitmap_intersection(enable_bitmap, + ia_css_data_terminal_manifest_get_kernel_bitmap( + (const ia_css_data_terminal_manifest_t *)manifest)); + + verifexit(frame != NULL); + verifexit(ia_css_frame_set_buffer_state( + frame, IA_CSS_BUFFER_NULL) == 0); + verifexit(ia_css_is_kernel_bitmap_onehot(intersection) == + true); + + terminal_raw_ptr += sizeof(ia_css_data_terminal_t); + dterminal->fragment_descriptor_offset = + (uint16_t) (terminal_raw_ptr - (char *)terminal); + + dterminal->kernel_id = 0; + while (!ia_css_is_kernel_bitmap_empty(intersection)) { + intersection = ia_css_kernel_bitmap_shift( + intersection); + dterminal->kernel_id++; + } + assert(dterminal->kernel_id > 0); + dterminal->kernel_id -= 1; + + /* some terminal and fragment initialization */ + dterminal->frame_descriptor.frame_format_type = + terminal_param->frame_format_type; + for (i = 0; i < IA_CSS_N_DATA_DIMENSION; i++) { + dterminal->frame_descriptor.dimension[i] = + terminal_param->dimensions[i]; + } + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] = + terminal_param->stride; + dterminal->frame_descriptor.bpp = terminal_param->bpp; + dterminal->frame_descriptor.bpe = terminal_param->bpe; + switch (dterminal->frame_descriptor.frame_format_type) { + case IA_CSS_DATA_FORMAT_UYVY: + case IA_CSS_DATA_FORMAT_YUYV: + case IA_CSS_DATA_FORMAT_Y800: + case IA_CSS_DATA_FORMAT_RGB565: + case IA_CSS_DATA_FORMAT_RGBA888: + case IA_CSS_DATA_FORMAT_BAYER_GRBG: + case IA_CSS_DATA_FORMAT_BAYER_RGGB: + case IA_CSS_DATA_FORMAT_BAYER_BGGR: + case IA_CSS_DATA_FORMAT_BAYER_GBRG: + case IA_CSS_DATA_FORMAT_RAW: + case IA_CSS_DATA_FORMAT_RAW_PACKED: + case IA_CSS_DATA_FORMAT_YYUVYY_VECTORIZED: + case IA_CSS_DATA_FORMAT_PAF: + dterminal->frame_descriptor.plane_count = 1; + dterminal->frame_descriptor.plane_offsets[0] = 0; + break; + case IA_CSS_DATA_FORMAT_NV12: + case IA_CSS_DATA_FORMAT_NV21: + case IA_CSS_DATA_FORMAT_NV16: + case IA_CSS_DATA_FORMAT_NV61: + dterminal->frame_descriptor.plane_count = 2; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + break; + case IA_CSS_DATA_FORMAT_YUV444: + case IA_CSS_DATA_FORMAT_RGB888: + case IA_CSS_DATA_FORMAT_YUV420_VECTORIZED: + dterminal->frame_descriptor.plane_count = 3; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + dterminal->frame_descriptor.plane_offsets[2] = + dterminal->frame_descriptor.plane_offsets[1] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + break; + case IA_CSS_DATA_FORMAT_YUV420: + dterminal->frame_descriptor.plane_count = 3; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + dterminal->frame_descriptor.plane_offsets[2] = + dterminal->frame_descriptor.plane_offsets[1] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION]/2 * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]/2; + break; + default: + /* Unset, resulting in potential terminal connect issues */ + dterminal->frame_descriptor.plane_count = 1; + dterminal->frame_descriptor.plane_offsets[0] = 0; + break; + } + /* + * Initial solution for single fragment initialization + * TODO: + * where to get the fragment description params from??? + */ + if (fragment_count > 0) { + ia_css_fragment_descriptor_t *fragment_descriptor = + (ia_css_fragment_descriptor_t *) + terminal_raw_ptr; + + fragment_descriptor->index[IA_CSS_COL_DIMENSION] = + terminal_param->index[IA_CSS_COL_DIMENSION]; + fragment_descriptor->index[IA_CSS_ROW_DIMENSION] = + terminal_param->index[IA_CSS_ROW_DIMENSION]; + fragment_descriptor->offset[0] = + terminal_param->offset; + for (i = 0; i < IA_CSS_N_DATA_DIMENSION; i++) { + fragment_descriptor->dimension[i] = + terminal_param->fragment_dimensions[i]; + } + } + /* end fragment stuff */ + } else if (ia_css_is_terminal_manifest_parameter_terminal(manifest) == + true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + uint16_t section_count = + ((const ia_css_param_terminal_manifest_t *)manifest)-> + param_manifest_section_desc_count; + size_t curr_offset = 0; + + pterminal->param_section_desc_offset = + sizeof(ia_css_param_terminal_t); + + for (i = 0; i < section_count; i++) { + ia_css_param_section_desc_t *section = + ia_css_param_in_terminal_get_param_section_desc( + pterminal, i); + const ia_css_param_manifest_section_desc_t * + man_section = + ia_css_param_terminal_manifest_get_prm_sct_desc( + (const ia_css_param_terminal_manifest_t *)manifest, i); + + verifjmpexit(man_section != NULL); + verifjmpexit(section != NULL); + + section->mem_size = man_section->max_mem_size; + section->mem_offset = curr_offset; + curr_offset += man_section->max_mem_size; + } + } else if (ia_css_is_terminal_manifest_program_terminal(manifest) == + true && + ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PROGRAM) { /* for program terminal */ + ia_css_program_terminal_t *prog_terminal = + (ia_css_program_terminal_t *)terminal; + const ia_css_program_terminal_manifest_t *prog_terminal_man = + (const ia_css_program_terminal_manifest_t *)manifest; + ia_css_kernel_fragment_sequencer_info_desc_t + *sequencer_info_desc_base = NULL; + uint16_t section_count = prog_terminal_man-> + fragment_param_manifest_section_desc_count; + uint16_t manifest_info_count = + prog_terminal_man-> + kernel_fragment_sequencer_info_manifest_info_count; + /* information needs to come from user or manifest once + * the size sizeof function is updated. + */ + uint16_t nof_command_objs = 0; + size_t curr_offset = 0; + + prog_terminal->kernel_fragment_sequencer_info_desc_offset = + sizeof(ia_css_program_terminal_t); + prog_terminal->fragment_param_section_desc_offset = + prog_terminal-> + kernel_fragment_sequencer_info_desc_offset + + (fragment_count * manifest_info_count * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (nof_command_objs * + sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + NOT_USED(sequencer_info_desc_base); + for (i = 0; i < fragment_count; i++) { + for (j = 0; j < section_count; j++) { + ia_css_fragment_param_section_desc_t *section = + ia_css_program_terminal_get_frgmnt_prm_sct_desc( + prog_terminal, i, j, section_count); + const ia_css_fragment_param_manifest_section_desc_t * + man_section = +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc + (prog_terminal_man, j); + + verifjmpexit(man_section != NULL); + verifjmpexit(section != NULL); + + section->mem_size = man_section->max_mem_size; + section->mem_offset = curr_offset; + curr_offset += man_section->max_mem_size; + } + + sequencer_info_desc_base = + ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + prog_terminal, i, 0, + manifest_info_count); + + /* + * This offset cannot be initialized properly + * since the number of commands in every sequencer + * is not known at this point + */ + /*for (j = 0; j < manifest_info_count; j++) { + sequencer_info_desc_base[j]. + command_desc_offset = + prog_terminal-> + kernel_fragment_sequencer_info_desc_offset + + (manifest_info_count * + sizeof( + ia_css_kernel_fragment_sequencer_info_desc_t) + + (nof_command_objs * + sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t + )); + }*/ + } + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + manifest) == true) { + ia_css_spatial_param_terminal_t *spatial_param_terminal = + (ia_css_spatial_param_terminal_t *)terminal; + ia_css_spatial_param_terminal_manifest_t * + spatia_param_terminal_man = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + + /* Initialize the spatial terminal structure */ + spatial_param_terminal->fragment_grid_desc_offset = + sizeof(ia_css_spatial_param_terminal_t); + spatial_param_terminal->frame_grid_param_section_desc_offset = + spatial_param_terminal->fragment_grid_desc_offset + + (fragment_count * sizeof(ia_css_fragment_grid_desc_t)); + spatial_param_terminal->kernel_id = + spatia_param_terminal_man->kernel_id; + } else if (ia_css_is_terminal_manifest_sliced_terminal(manifest) == + true) { + ia_css_sliced_param_terminal_t *sliced_param_terminal = + (ia_css_sliced_param_terminal_t *)terminal; + ia_css_sliced_param_terminal_manifest_t + *sliced_param_terminal_man = + (ia_css_sliced_param_terminal_manifest_t *)manifest; + + /* Initialize the sliced terminal structure */ + sliced_param_terminal->fragment_slice_desc_offset = + sizeof(ia_css_sliced_param_terminal_t); + sliced_param_terminal->kernel_id = + sliced_param_terminal_man->kernel_id; + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + manifest) == true) { + verifjmpexit(ia_css_program_control_init_terminal_init( + (ia_css_program_control_init_terminal_t *) + terminal, + (const ia_css_program_control_init_terminal_manifest_t *) + manifest) == 0); + } else { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_create failed, not a data or param terminal. Returning (%i)\n", + EFAULT); + goto EXIT; + } + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_create(): Created successfully terminal %p\n", + terminal); + + retval = 0; +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_terminal_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_create failed (%i)\n", retval); + terminal = ia_css_terminal_destroy(terminal); + } + return terminal; +} + +ia_css_terminal_t *ia_css_terminal_destroy( + ia_css_terminal_t *terminal) +{ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_destroy(terminal %p): enter:\n", terminal); + return terminal; +} + +uint16_t ia_css_param_terminal_compute_section_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) /* Delete 2nd argument*/ +{ + uint16_t section_count = 0; + + NOT_USED(param); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_param_terminal_compute_section_count(): enter:\n"); + + verifexit(manifest != NULL); + section_count = ((const ia_css_param_terminal_manifest_t *)manifest)-> + param_manifest_section_desc_count; +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_param_terminal_compute_section_count: invalid argument\n"); + } + return section_count; +} +#endif /* !defined(__VIED_CELL) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h new file mode 100644 index 000000000000..36fb0f1d469a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h @@ -0,0 +1,1868 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_IMPL_H +#define __IA_CSS_PSYS_TERMINAL_IMPL_H + +#include + +#include +#include + +#include +#include + +#include + + +#include +#include /* for verifexit, verifjmpexit */ +#include /* for COMPILATION_ERROR_IF */ +#include /* for NOT_USED */ +#include "ia_css_psys_terminal_private_types.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_manifest_types.h" +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_types.h" + +STORAGE_CLASS_INLINE int ia_css_data_terminal_print(const ia_css_terminal_t *terminal, + void *fid) { + + DECLARE_ERRVAL + int retval = -1; + int i; + ia_css_data_terminal_t *dterminal = (ia_css_data_terminal_t *)terminal; + uint16_t fragment_count = + ia_css_data_terminal_get_fragment_count(dterminal); + verifexitval(fragment_count != 0, EINVAL); + + retval = ia_css_frame_descriptor_print( + ia_css_data_terminal_get_frame_descriptor(dterminal), + fid); + verifexitval(retval == 0, EINVAL); + + retval = ia_css_frame_print( + ia_css_data_terminal_get_frame(dterminal), fid); + verifexitval(retval == 0, EINVAL); + + for (i = 0; i < (int)fragment_count; i++) { + retval = ia_css_fragment_descriptor_print( + ia_css_data_terminal_get_fragment_descriptor( + dterminal, i), fid); + verifexitval(retval == 0, EINVAL); + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_print( + const ia_css_terminal_t *terminal, + void *fid) +{ + DECLARE_ERRVAL + int retval = -1; + ia_css_terminal_type_t term_type = ia_css_terminal_get_type(terminal); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_print(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tTerminal %p sizeof %d, typeof %d, parent %p\n", + terminal, + (int)ia_css_terminal_get_size(terminal), + (int)ia_css_terminal_get_type(terminal), + (void *)ia_css_terminal_get_parent(terminal)); + + switch (term_type) { + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + ia_css_program_control_init_terminal_print( + (ia_css_program_control_init_terminal_t *)terminal); + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + ia_css_data_terminal_print(terminal, fid); + break; + default: + /* other terminal prints are currently not supported */ + break; + } + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_input( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + bool is_input = false; + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_input(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_type = ia_css_terminal_get_type(terminal); + + switch (terminal_type) { + case IA_CSS_TERMINAL_TYPE_DATA_IN: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_STATE_IN: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_PARAM_STREAM: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PROGRAM: + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + is_input = true; + break; + case IA_CSS_TERMINAL_TYPE_DATA_OUT: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_STATE_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + is_input = false; + break; + default: + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_input: Unknown terminal type (%d)\n", + terminal_type); + goto EXIT; + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_input invalid argument\n"); + } + return is_input; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_terminal_get_size( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_size(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + size = terminal->size; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_type_t ia_css_terminal_get_type( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_type(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_type = terminal->terminal_type; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_type invalid argument\n"); + } + return terminal_type; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_type( + ia_css_terminal_t *terminal, + const ia_css_terminal_type_t terminal_type) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_type(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal->terminal_type = terminal_type; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_type invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_type failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_terminal_get_terminal_manifest_index( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + uint16_t terminal_manifest_index; + + terminal_manifest_index = 0xffff; + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_terminal_manifest_index(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_manifest_index = terminal->tm_index; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_terminal_manifest_index: invalid argument\n"); + } + return terminal_manifest_index; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_manifest_index( + ia_css_terminal_t *terminal, + const uint16_t terminal_manifest_index) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_terminal_manifest_index(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + terminal->tm_index = terminal_manifest_index; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_manifest_index: invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_manifest_index: failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_ID_t ia_css_terminal_get_ID( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_ID_t retval = IA_CSS_TERMINAL_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_ID(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + retval = terminal->ID; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_ID invalid argument\n"); + retval = 0; + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_get_kernel_id( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + uint8_t retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_kernel_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + retval = dterminal->kernel_id; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_kernel_id: invalid argument\n"); + retval = 0; + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_connection_type_t ia_css_data_terminal_get_connection_type( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_connection_type_t connection_type = IA_CSS_N_CONNECTION_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_connection_type(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + connection_type = dterminal->connection_type; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_connection_type: invalid argument\n"); + } + return connection_type; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_get_link_id( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + uint8_t link_id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_link_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + link_id = dterminal->link_id; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_link_id: invalid argument\n"); + } + return link_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_data_terminal_set_link_id( + ia_css_data_terminal_t *dterminal, + const uint8_t link_id) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_set_link_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + dterminal->link_id = link_id; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_link_id: invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_link_id: failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_data_terminal_set_connection_type( + ia_css_data_terminal_t *dterminal, + const ia_css_connection_type_t connection_type) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_set_connection_type(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + dterminal->connection_type = connection_type; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_connection_type: invalid argument dterminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_connection_type failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_t *ia_css_terminal_get_parent( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_parent(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + parent = (ia_css_process_group_t *) ((char *)terminal + + terminal->parent_offset); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_parent invalid argument\n"); + } + return parent; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_parent( + ia_css_terminal_t *terminal, + ia_css_process_group_t *parent) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_parent(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + verifexitval(parent != NULL, EFAULT); + + terminal->parent_offset = (uint16_t) ((char *)parent - + (char *)terminal); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_parent invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_parent failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_frame_t *ia_css_data_terminal_get_frame( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_frame_t *frame = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + frame = (ia_css_frame_t *)(&(dterminal->frame)); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame invalid argument\n"); + } + return frame; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_frame_descriptor_t *ia_css_data_terminal_get_frame_descriptor( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_frame_descriptor_t *frame_descriptor = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame_descriptor(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + frame_descriptor = + (ia_css_frame_descriptor_t *)(&(dterminal->frame_descriptor)); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame_descriptor: invalid argument\n"); + } + return frame_descriptor; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_fragment_descriptor_t *ia_css_data_terminal_get_fragment_descriptor( + const ia_css_data_terminal_t *dterminal, + const unsigned int fragment_index) +{ + DECLARE_ERRVAL + ia_css_fragment_descriptor_t *fragment_descriptor = NULL; + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame_descriptor(): enter:\n"); + + fragment_count = ia_css_data_terminal_get_fragment_count(dterminal); + + verifexitval(dterminal != NULL, EFAULT); + verifexitval(fragment_count != 0, EINVAL); + verifexitval(fragment_index < fragment_count, EINVAL); + + fragment_descriptor = (ia_css_fragment_descriptor_t *) + ((char *)dterminal + dterminal->fragment_descriptor_offset); + + fragment_descriptor += fragment_index; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame_descriptor: invalid argument\n"); + } + return fragment_descriptor; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_data_terminal_get_fragment_count( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent; + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_fragment_count(): enter:\n"); + + parent = ia_css_terminal_get_parent((ia_css_terminal_t *)dterminal); + + verifexitval(dterminal != NULL, EFAULT); + verifexitval(parent != NULL, EFAULT); + + fragment_count = ia_css_process_group_get_fragment_count(parent); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_fragment_count: invalid argument\n"); + } + return fragment_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_parameter_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_parameter_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_parameter_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_data_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_data_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_data_terminal invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_program_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_program_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_program_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_program_control_init_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_program_control_init_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_program_control_init_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_spatial_parameter_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_spatial_parameter_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_spatial_param_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_compute_plane_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + uint8_t plane_count = 1; + + NOT_USED(manifest); + NOT_USED(param); + + verifexitval(manifest != NULL, EFAULT); + verifexitval(param != NULL, EFAULT); + /* TODO: Implementation Missing*/ + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_compute_plane_count(): enter:\n"); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_compute_plane_count: invalid argument\n"); + } + return plane_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_terminal_get_buffer( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + vied_vaddress_t buffer = VIED_NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_buffer(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal)) { + ia_css_frame_t *frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EFAULT); + buffer = ia_css_frame_get_buffer(frame); + } else if (ia_css_is_terminal_parameter_terminal(terminal)) { + const ia_css_param_terminal_t *param_terminal = + (const ia_css_param_terminal_t *)terminal; + + buffer = param_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_program_terminal(terminal)) { + const ia_css_program_terminal_t *program_terminal = + (const ia_css_program_terminal_t *)terminal; + + buffer = program_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal)) { + const ia_css_program_control_init_terminal_t *program_ctrl_init_terminal = + (const ia_css_program_control_init_terminal_t *)terminal; + + buffer = program_ctrl_init_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal)) { + const ia_css_spatial_param_terminal_t *spatial_terminal = + (const ia_css_spatial_param_terminal_t *)terminal; + + buffer = spatial_terminal->param_payload.buffer; + } +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_buffer: invalid argument terminal\n"); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_buffer( + ia_css_terminal_t *terminal, + vied_vaddress_t buffer) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_buffer(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* Currently using Frames inside data terminal , + * TODO: start directly using data. + */ + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + + verifexitval(frame != NULL, EFAULT); + retval = ia_css_frame_set_buffer(frame, buffer); + verifexitval(retval == 0, EINVAL); + } else if (ia_css_is_terminal_parameter_terminal(terminal) == true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_program_terminal(terminal) == true) { + ia_css_program_terminal_t *pterminal = + (ia_css_program_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) == true) { + ia_css_program_control_init_terminal_t *pterminal = + (ia_css_program_control_init_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal) == + true) { + ia_css_spatial_param_terminal_t *pterminal = + (ia_css_spatial_param_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else { + return retval; + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_buffer failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_get_terminal_index( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + int terminal_index = -1; + + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_terminal_index(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal)) { + ia_css_frame_t *frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EFAULT); + terminal_index = ia_css_frame_get_data_index(frame); + } else { + if (ia_css_is_terminal_parameter_terminal(terminal)) { + const ia_css_param_terminal_t *param_terminal = + (const ia_css_param_terminal_t *)terminal; + + terminal_index = param_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_program_terminal(terminal)) { + const ia_css_program_terminal_t *program_terminal = + (const ia_css_program_terminal_t *)terminal; + + terminal_index = program_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal)) { + const ia_css_program_control_init_terminal_t *program_ctrl_init_terminal = + (const ia_css_program_control_init_terminal_t *)terminal; + + terminal_index = program_ctrl_init_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal)) { + const ia_css_spatial_param_terminal_t *spatial_terminal = + (const ia_css_spatial_param_terminal_t *)terminal; + + terminal_index = spatial_terminal->param_payload.terminal_index; + } else { + verifjmpexit(0); + } + } +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_terminal_index: invalid argument\n"); + } + return terminal_index; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_index( + ia_css_terminal_t *terminal, + unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_terminal_index(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* Currently using Frames inside data terminal , + * TODO: start directly using data. + */ + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + + verifexitval(frame != NULL, EFAULT); + retval = ia_css_frame_set_data_index(frame, terminal_index); + verifexitval(retval == 0, EINVAL); + } else { + if (ia_css_is_terminal_parameter_terminal(terminal) == true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_program_terminal(terminal) == true) { + ia_css_program_terminal_t *pterminal = + (ia_css_program_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) + == true) { + ia_css_program_control_init_terminal_t *pterminal = + (ia_css_program_control_init_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal) == + true) { + ia_css_spatial_param_terminal_t *pterminal = + (ia_css_spatial_param_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else { + return retval; + } + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_index failed (%i)\n", + retval); + } + return retval; +} + +STORAGE_CLASS_INLINE bool ia_css_is_data_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + + const ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + const ia_css_data_terminal_manifest_t *dt_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; + const ia_css_frame_descriptor_t *frame_descriptor; + ia_css_frame_format_bitmap_t man_frame_format_bitmap; + ia_css_frame_format_bitmap_t proc_frame_format_bitmap; + uint16_t max_value[IA_CSS_N_DATA_DIMENSION]; + uint16_t min_value[IA_CSS_N_DATA_DIMENSION]; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_data_terminal_valid enter\n"); + + frame_descriptor = + ia_css_data_terminal_get_frame_descriptor(dterminal); + verifexitval(frame_descriptor != NULL, EFAULT); + man_frame_format_bitmap = + ia_css_data_terminal_manifest_get_frame_format_bitmap( + dt_manifest); + proc_frame_format_bitmap = + ia_css_frame_format_bit_mask( + frame_descriptor->frame_format_type); + /* + * TODO: Replace by 'validation of frame format type'. + * Currently frame format type is not correctly set by manifest, + * waiting for HSD 1804260604 + */ + if (man_frame_format_bitmap > 0) { + if ((man_frame_format_bitmap & + proc_frame_format_bitmap) == 0) { + uint32_t *bitmap_arr = + (uint32_t *)&man_frame_format_bitmap; + + NOT_USED(bitmap_arr); + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Frame format type not defined in manifest\n"); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + " man bitmap_arr[]: %d,%d\n", + bitmap_arr[1], bitmap_arr[0]); + bitmap_arr = (uint32_t *)&proc_frame_format_bitmap; + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + " proc bitmap_arr[]: %d,%d\n", + bitmap_arr[1], bitmap_arr[0]); + } + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Frame format bitmap not defined in manifest\n"); + } + ia_css_data_terminal_manifest_get_min_size(dt_manifest, min_value); + /* + * TODO: Replace by validation of Minimal frame column dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if ((frame_descriptor->dimension[IA_CSS_COL_DIMENSION] < + min_value[IA_CSS_COL_DIMENSION])) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Minimal frame column dimensions not set correctly (by manifest)\n"); + } + /* + * TODO: Replace by validation of Minimal frame row dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_ROW_DIMENSION] < + min_value[IA_CSS_ROW_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Minimal frame row dimensions not set correctly (by manifest)\n"); + } + + ia_css_data_terminal_manifest_get_max_size(dt_manifest, max_value); + /* + * TODO: Replace by validation of Maximal frame column dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_COL_DIMENSION] > + max_value[IA_CSS_COL_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Maximal frame column dimensions not set correctly (by manifest)\n"); + } + /* + * TODO: Replace by validation of Maximal frame row dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_ROW_DIMENSION] > + max_value[IA_CSS_ROW_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Maximal frame row dimensions not set correctly (by manifest)\n"); + } + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "min_value: [%d,%d]\n", + min_value[IA_CSS_COL_DIMENSION], + min_value[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "max_value: [%d,%d]\n", + max_value[IA_CSS_COL_DIMENSION], + max_value[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "frame dim: [%d,%d]\n", + frame_descriptor->dimension[IA_CSS_COL_DIMENSION], + frame_descriptor->dimension[IA_CSS_ROW_DIMENSION]); + /* + * TODO: Add validation of fragment dimensions. + * Currently not set by manifest yet, waiting for HSD 1804260604 + */ + NOT_USED(nof_fragments); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_data_terminal_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +STORAGE_CLASS_INLINE void ia_css_program_terminal_seq_info_print( + const ia_css_kernel_fragment_sequencer_info_manifest_desc_t + *man_seq_info_desc, + const ia_css_kernel_fragment_sequencer_info_desc_t + *term_seq_info_desc) +{ + NOT_USED(man_seq_info_desc); + NOT_USED(term_seq_info_desc); + + /* slice dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + + /* slice dimension row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + + /* slice count column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_count: %d\n", + term_seq_info_desc-> + fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + + /* slice count row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_count: %d\n", + term_seq_info_desc-> + fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + + /* decimation factor column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_point_decimation_factor: %d\n", + term_seq_info_desc-> + fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION] + ); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION] + ); + + /* decimation factor row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_point_decimation_factor: %d\n", + term_seq_info_desc-> + fragment_grid_point_decimation_factor[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + + /* index column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_topleft_index: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + + /* index row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_topleft_index: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + + /* dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + + /* dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); +} + +STORAGE_CLASS_INLINE bool ia_css_is_program_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint16_t frag_idx; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_program_terminal_valid enter\n"); + + for (frag_idx = 0; frag_idx < nof_fragments; frag_idx++) { + uint16_t frag_seq_info_count, seq_idx; + const ia_css_program_terminal_t *prog_term; + const ia_css_program_terminal_manifest_t *prog_term_man; + + prog_term = (const ia_css_program_terminal_t *)terminal; + prog_term_man = + (const ia_css_program_terminal_manifest_t *) + terminal_manifest; + frag_seq_info_count = + prog_term_man-> + kernel_fragment_sequencer_info_manifest_info_count; + + for (seq_idx = 0; seq_idx < frag_seq_info_count; seq_idx++) { + const ia_css_kernel_fragment_sequencer_info_desc_t + *term_seq_info_desc; + const + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + man_seq_info_desc; + + term_seq_info_desc = + ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + prog_term, frag_idx, seq_idx, + frag_seq_info_count); + verifexitval(term_seq_info_desc != NULL, EFAULT); + man_seq_info_desc = + ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc + (prog_term_man, seq_idx); + verifexitval(man_seq_info_desc != NULL, EFAULT); + + ia_css_program_terminal_seq_info_print( + man_seq_info_desc, term_seq_info_desc); + /* slice dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION]); + + /* slice dimension row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + + /* slice count column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION]); + + /* slice count row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + + /* decimation factor column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION]); + + /* decimation factor row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + + /* index column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + + /* index row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + + /* dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + + /* dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + } + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_program_terminal_valid() invalid argument\n"); + return false; + } + if (invalid_flag == true) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_is_program_terminal_valid(): validation failed\n"); + /* TODO: program terminal parameters not correctly defined, + * disable validation result until issues has been solved + */ + return true; + } + return (!invalid_flag); +} + +STORAGE_CLASS_INLINE bool ia_css_is_sliced_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint16_t frag_idx; + + uint16_t slice_idx, section_idx; + + const ia_css_sliced_param_terminal_t *sliced_term = + (const ia_css_sliced_param_terminal_t *)terminal; + const ia_css_sliced_param_terminal_manifest_t *sliced_term_man = + (const ia_css_sliced_param_terminal_manifest_t *) + terminal_manifest; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_sliced_terminal_valid enter\n"); + + for (frag_idx = 0; frag_idx < nof_fragments; frag_idx++) { + const ia_css_fragment_slice_desc_t *fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_term, frag_idx); + + verifexitval(fragment_slice_desc != NULL, EFAULT); + + for (slice_idx = 0; + slice_idx < fragment_slice_desc->slice_count; + slice_idx++) { + for (section_idx = 0; + section_idx < + sliced_term_man->sliced_param_section_count; + section_idx++) { + const + ia_css_sliced_param_manifest_section_desc_t * + slice_man_section_desc; + const ia_css_slice_param_section_desc_t * + slice_section_desc; + + slice_man_section_desc = + ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + sliced_term_man, section_idx); + slice_section_desc = + ia_css_sliced_param_terminal_get_slice_param_section_desc( + sliced_term, frag_idx, + slice_idx, section_idx, + sliced_term_man-> + sliced_param_section_count); + verifexitval(slice_man_section_desc != NULL, EFAULT); + verifexitval(slice_section_desc != NULL, EFAULT); + + invalid_flag = invalid_flag || + (slice_section_desc->mem_size > + slice_man_section_desc->max_mem_size); + } + } + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_sliced_terminal_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } + +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest) +{ + DECLARE_ERRVAL + bool is_valid = false; + uint16_t nof_fragments; + ia_css_terminal_type_t terminal_type = IA_CSS_TERMINAL_INVALID_ID; + + verifexitval(NULL != terminal, EFAULT); + verifexitval(NULL != terminal_manifest, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_valid enter\n"); + + nof_fragments = ia_css_data_terminal_get_fragment_count( + (const ia_css_data_terminal_t *)terminal); + terminal_type = ia_css_terminal_get_type(terminal); + + switch (terminal_type) { + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + is_valid = ia_css_is_data_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + is_valid = ia_css_is_program_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + /* Nothing to be validated for cached and spatial + * parameters, return valid + */ + is_valid = true; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + is_valid = ia_css_is_sliced_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + default: + /* Terminal type unknown, return invalid */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, WARNING, + "ia_css_is_terminal_valid() Terminal type %x unknown\n", + (int)terminal_type); + is_valid = false; + break; + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_valid() invalid argument\n"); + return false; + } + /* TODO: to be removed once all PGs pass validation */ + if (is_valid == false) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_is_terminal_valid(): type: %d validation failed\n", + terminal_type); + } + return is_valid; +} + +/* ================= Program Control Init Terminal - START ================= */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int +ia_css_program_control_init_terminal_init( + ia_css_program_control_init_terminal_t *terminal, + const ia_css_program_control_init_terminal_manifest_t *manifest) +{ + int retval = -1; + unsigned int i; + unsigned int base_load_sec; + unsigned int base_connect_sec; + unsigned int load_index = 0; + unsigned int connect_index = 0; + unsigned int load_section_count = 0; + unsigned int connect_section_count = 0; + + ia_css_program_control_init_manifest_program_desc_t *man_progs; + + verifjmpexit(terminal != NULL); + + man_progs = + ia_css_program_control_init_terminal_manifest_get_program_desc(manifest, 0); + verifjmpexit(man_progs != NULL); + + for (i = 0; i < manifest->program_count; i++) { + load_section_count += man_progs[i].load_section_count; + connect_section_count += man_progs[i].connect_section_count; + } + + terminal->program_count = manifest->program_count; + terminal->program_section_desc_offset = + sizeof(ia_css_program_control_init_terminal_t); + + base_load_sec = /* base_load_sec relative to first program */ + terminal->program_count * + sizeof(ia_css_program_control_init_program_desc_t); + + base_connect_sec = base_load_sec + + load_section_count * + sizeof(ia_css_program_control_init_load_section_desc_t); + + for (i = 0; i < terminal->program_count; i++) { + ia_css_program_control_init_program_desc_t *prog; + + prog = ia_css_program_control_init_terminal_get_program_desc( + terminal, i); + verifjmpexit(prog != NULL); + + prog->load_section_count = man_progs[i].load_section_count; + prog->connect_section_count = man_progs[i].connect_section_count; + + prog->load_section_desc_offset = + base_load_sec + + load_index * + sizeof(ia_css_program_control_init_load_section_desc_t) - + i * sizeof(ia_css_program_control_init_program_desc_t); + prog->connect_section_desc_offset = + base_connect_sec + + connect_index * + sizeof(ia_css_program_control_init_connect_section_desc_t) - + i * sizeof(ia_css_program_control_init_program_desc_t); + + load_index += man_progs[i].load_section_count; + connect_index += man_progs[i].connect_section_count; + } + retval = 0; +EXIT: + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +unsigned int +ia_css_program_control_init_terminal_get_descriptor_size( + const ia_css_program_control_init_terminal_manifest_t *manifest) +{ + unsigned int i; + unsigned size = 0; + unsigned load_section_count = 0; + unsigned connect_section_count = 0; + ia_css_program_control_init_manifest_program_desc_t *man_progs; + verifjmpexit(manifest != NULL); + + man_progs = + ia_css_program_control_init_terminal_manifest_get_program_desc( + manifest, 0); + verifjmpexit(man_progs != NULL); + + for (i = 0; i < manifest->program_count; i++) { + load_section_count += man_progs[i].load_section_count; + connect_section_count += man_progs[i].connect_section_count; + } + + size = sizeof(ia_css_program_control_init_terminal_t) + + manifest->program_count * + sizeof(struct ia_css_program_control_init_program_desc_s) + + load_section_count * + sizeof(struct ia_css_program_control_init_load_section_desc_s) + + connect_section_count * + sizeof(struct ia_css_program_control_init_connect_section_desc_s); +EXIT: + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +void ia_css_program_control_init_terminal_print( + const ia_css_program_control_init_terminal_t *terminal) +{ + unsigned int prog_idx, sec_idx; + ia_css_program_control_init_program_desc_t *prog; + ia_css_program_control_init_load_section_desc_t *load_sec; + ia_css_program_control_init_connect_section_desc_t *connect_sec; + + verifjmpexit(terminal != NULL); + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "program_count: %d, payload_fragment_stride: %d\n", + terminal->program_count, + terminal->payload_fragment_stride); + + for (prog_idx = 0; prog_idx < terminal->program_count; prog_idx++) { + prog = ia_css_program_control_init_terminal_get_program_desc( + terminal, prog_idx); + verifjmpexit(prog != NULL); + + for (sec_idx = 0; sec_idx < prog->load_section_count; sec_idx++) { + load_sec = + ia_css_program_control_init_terminal_get_load_section_desc( + prog, sec_idx); + verifjmpexit(load_sec != NULL); + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "load_section>> device_descriptor_id: 0x%x, mem_offset: %d, " + "mem_size: %d, mode_bitmask: %x\n", + load_sec->device_descriptor_id.data, + load_sec->mem_offset, + load_sec->mem_size, + load_sec->mode_bitmask); + } + for (sec_idx = 0; sec_idx < prog->connect_section_count; sec_idx++) { + connect_sec = + ia_css_program_control_init_terminal_get_connect_section_desc( + prog, sec_idx); + verifjmpexit(connect_sec != NULL); + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "connect_section>> device_descriptor_id: 0x%x, " + "connect_terminal_ID: %d, connect_section_idx: %d, " + "mode_bitmask: %x\n", + connect_sec->device_descriptor_id.data, + connect_sec->connect_terminal_ID, + connect_sec->connect_section_idx, + connect_sec->mode_bitmask); + } + } +EXIT: + return; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_program_desc_t * +ia_css_program_control_init_terminal_get_program_desc( + const ia_css_program_control_init_terminal_t *prog_ctrl_init_terminal, + const unsigned int program_index) +{ + ia_css_program_control_init_program_desc_t *program_desc_base; + ia_css_program_control_init_program_desc_t *program_desc = NULL; + + verifjmpexit(prog_ctrl_init_terminal != NULL); + verifjmpexit(program_index < prog_ctrl_init_terminal->program_count); + + program_desc_base = (ia_css_program_control_init_program_desc_t *) + (((const char *)prog_ctrl_init_terminal) + + prog_ctrl_init_terminal->program_section_desc_offset); + program_desc = &(program_desc_base[program_index]); + +EXIT: + return program_desc; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_id_t ia_css_program_control_init_terminal_get_process_id( + const ia_css_program_control_init_program_desc_t *program_desc) +{ + ia_css_process_id_t process_id = 0; + + verifjmpexit(program_desc != NULL); + + process_id = program_desc->control_info.process_id; + +EXIT: + return process_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_program_control_init_terminal_get_num_done_events( + const ia_css_program_control_init_program_desc_t *program_desc) +{ + uint8_t num_done_events = 0; + + verifjmpexit(program_desc != NULL); + + num_done_events = program_desc->control_info.num_done_events; + +EXIT: + return num_done_events; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +void ia_css_program_control_init_terminal_set_control_info( + ia_css_program_control_init_program_desc_t *program_desc, + ia_css_process_id_t process_id, + uint8_t num_done_events) +{ + verifjmpexit(program_desc != NULL); + + program_desc->control_info.process_id = process_id; + program_desc->control_info.num_done_events = num_done_events; + +EXIT: + return; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_load_section_desc_t * +ia_css_program_control_init_terminal_get_load_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int load_section_index) +{ + ia_css_program_control_init_load_section_desc_t *load_section_desc_base; + ia_css_program_control_init_load_section_desc_t *load_section_desc = NULL; + + verifjmpexit(program_desc != NULL); + verifjmpexit(load_section_index < program_desc->load_section_count); + + load_section_desc_base = (ia_css_program_control_init_load_section_desc_t *) + (((const char *)program_desc) + + program_desc->load_section_desc_offset); + load_section_desc = &(load_section_desc_base[load_section_index]); + +EXIT: + return load_section_desc; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_connect_section_desc_t * +ia_css_program_control_init_terminal_get_connect_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int connect_section_index) +{ + ia_css_program_control_init_connect_section_desc_t *connect_sec_desc_base; + ia_css_program_control_init_connect_section_desc_t *connect_sec_desc = NULL; + + verifjmpexit(program_desc != NULL); + verifjmpexit(connect_section_index < program_desc->connect_section_count); + + connect_sec_desc_base = + (ia_css_program_control_init_connect_section_desc_t *) + (((const char *)program_desc) + + program_desc->connect_section_desc_offset); + connect_sec_desc = &(connect_sec_desc_base[connect_section_index]); + +EXIT: + return connect_sec_desc; +} + +/* ================= Program Control Init Terminal - END ================= */ + +#endif /* __IA_CSS_PSYS_TERMINAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h new file mode 100644 index 000000000000..68626561acb5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h @@ -0,0 +1,186 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H +#define __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H + +#include "ia_css_terminal_types.h" +#include "ia_css_program_group_data.h" +#include "ia_css_psys_manifest_types.h" + +#define N_UINT16_IN_DATA_TERMINAL_STRUCT 1 +#define N_UINT8_IN_DATA_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT 3 + +/* ========================= Data terminal - START ========================= */ + +#define SIZE_OF_DATA_TERMINAL_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_FRAME_DESCRIPTOR_STRUCT_BITS \ + + IA_CSS_FRAME_STRUCT_BITS \ + + IA_CSS_STREAM_STRUCT_BITS \ + + IA_CSS_UINT32_T_BITS \ + + IA_CSS_CONNECTION_TYPE_BITS \ + + (N_UINT16_IN_DATA_TERMINAL_STRUCT * 16) \ + + (N_UINT8_IN_DATA_TERMINAL_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT * 8)) + +/* + * The (data) terminal can be attached to a buffer or a stream. + * The stream interface is not necessarily limited to strict in-order access. + * For a stream the restriction is that contrary to a buffer it cannot be + * addressed directly, i.e. it behaves as a port, + * but it may support stream_pos() and/or seek() operations + */ +struct ia_css_data_terminal_s { + /**< Data terminal base */ + ia_css_terminal_t base; + /**< Properties of the data attached to the terminal */ + ia_css_frame_descriptor_t frame_descriptor; + /**< Data buffer handle attached to the terminal */ + ia_css_frame_t frame; + /**< (exclusive) Data stream handle attached to the terminal + * if the data is sourced over a device port + */ + ia_css_stream_t stream; + /**< Reserved */ + uint32_t reserved; + /**< Connection {buffer, stream, ...} */ + ia_css_connection_type_t connection_type; + /**< Array[fragment_count] (fragment_count being equal for all + * terminals in a subgraph) of fragment descriptors + */ + uint16_t fragment_descriptor_offset; + /**< Kernel id where this terminal is connected to */ + uint8_t kernel_id; + /**< Indicate to which subgraph this terminal belongs + * for common constraints + */ + uint8_t subgraph_id; + /* Link ID of the data terminal */ + uint8_t link_id; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT]; +}; +/* ========================== Data terminal - END ========================== */ + +/* ================= Program Control Init Terminal - START ================= */ +#define SIZE_OF_PROG_CONTROL_INIT_LOAD_SECTION_DESC_STRUCT_BITS \ + (DEVICE_DESCRIPTOR_ID_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + ) +struct ia_css_program_control_init_load_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; + /* Device descriptor */ + device_descriptor_id_t device_descriptor_id; /* 32 bits */ + /* (Applicable to) mode bitmask */ + uint32_t mode_bitmask; +}; + +#define MODE_BITMASK_MEMORY (1u << IA_CSS_CONNECTION_MEMORY) +#define MODE_BITMASK_MEMORY_STREAM (1u << IA_CSS_CONNECTION_MEMORY_STREAM) +#define MODE_BITMASK_STREAM (1u << IA_CSS_CONNECTION_STREAM) +#define MODE_BITMASK_DONT_CARE (MODE_BITMASK_MEMORY | MODE_BITMASK_MEMORY_STREAM | MODE_BITMASK_STREAM) + +#define N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT (5) +#define SIZE_OF_PROG_CONTROL_INIT_CONNECT_SECTION_DESC_STRUCT_BITS \ + (DEVICE_DESCRIPTOR_ID_BITS \ + + (1 * IA_CSS_UINT32_T_BITS) \ + + (1 * IA_CSS_UINT16_T_BITS) \ + + IA_CSS_TERMINAL_ID_BITS \ + + (N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT * \ + IA_CSS_UINT8_T_BITS) \ + ) +struct ia_css_program_control_init_connect_section_desc_s { + /* Device descriptor */ + device_descriptor_id_t device_descriptor_id; /* 32 bits */ + /* (Applicable to) mode bitmask */ + uint32_t mode_bitmask; + /* Connected terminal section (plane) index */ + uint16_t connect_section_idx; + /* Absolute referral ID for the connected terminal */ + ia_css_terminal_ID_t connect_terminal_ID; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT]; +}; + +#define N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO (1) +#define N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT (4) +#define SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS \ + (1 * IA_CSS_UINT16_T_BITS) \ + + (1 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO * IA_CSS_UINT8_T_BITS) + +#define SIZE_OF_PROG_CONTROL_INIT_PROG_DESC_STRUCT_BITS \ + (4 * IA_CSS_UINT16_T_BITS) \ + + (SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS) \ + + (N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT * \ + IA_CSS_UINT8_T_BITS) + +struct ia_css_program_desc_control_info_s { + /* 12-bit process identifier */ + ia_css_process_id_t process_id; + /* number of done acks required to close the process */ + uint8_t num_done_events; + uint8_t padding[N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO]; +}; + +struct ia_css_program_control_init_program_desc_s { + /* Number of load sections in this program */ + uint16_t load_section_count; + /* Points to variable size array of + * ia_css_program_control_init_load_section_desc_s + * in relation to its program_desc + */ + uint16_t load_section_desc_offset; + /* Number of connect sections in this program */ + uint16_t connect_section_count; + /* Points to variable size array of + * ia_css_program_control_init_connect_section_desc_s + * in relation to its program_desc + */ + uint16_t connect_section_desc_offset; + struct ia_css_program_desc_control_info_s control_info; + /* align to 64 bits */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT]; +}; + +#define SIZE_OF_PROG_CONTROL_INIT_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + (1 * IA_CSS_UINT32_T_BITS) \ + + (2 * IA_CSS_UINT16_T_BITS) \ + ) +struct ia_css_program_control_init_terminal_s { + /* Parameter terminal base */ + ia_css_terminal_t base; + /* Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Fragment stride for the payload, used to find the base + * of the payload for a given fragment + */ + uint32_t payload_fragment_stride; + /* Points to the variable array of + * ia_css_program_control_init_program_desc_s + */ + uint16_t program_section_desc_offset; + /* Number of instantiated programs in program group (processes) */ + uint16_t program_count; +}; +/* ================= Program Control Init Terminal - END ================= */ + +#endif /* __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h new file mode 100644 index 000000000000..4c8fd33b331c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h @@ -0,0 +1,23 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYSAPI_H +#define __IA_CSS_PSYSAPI_H + +#include +#include +#include +#include + +#endif /* __IA_CSS_PSYSAPI_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h new file mode 100644 index 000000000000..5658a2988a08 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h @@ -0,0 +1,33 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYSAPI_FW_VERSION_H +#define __IA_CSS_PSYSAPI_FW_VERSION_H + +/* PSYSAPI FW VERSION is taken from Makefile for FW tests */ +#define BXT_FW_RELEASE_VERSION PSYS_FIRMWARE_VERSION + +enum ia_css_process_group_protocol_version { + /* + * Legacy protocol + */ + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY = 0, + /* + * Persistent process group support protocol + */ + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, + IA_CSS_PROCESS_GROUP_N_PROTOCOLS +}; + +#endif /* __IA_CSS_PSYSAPI_FW_VERSION_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h new file mode 100644 index 000000000000..e35ec24c77b3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h @@ -0,0 +1,78 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYSAPI_TRACE_H +#define __IA_CSS_PSYSAPI_TRACE_H + +#include "ia_css_trace.h" + +#define PSYSAPI_TRACE_LOG_LEVEL_OFF 0 +#define PSYSAPI_TRACE_LOG_LEVEL_NORMAL 1 +#define PSYSAPI_TRACE_LOG_LEVEL_DEBUG 2 + +/* PSYSAPI and all the submodules in PSYSAPI will have the default tracing + * level set to the PSYSAPI_TRACE_CONFIG level. If not defined in the + * psysapi.mk fill it will be set by default to no trace + * (PSYSAPI_TRACE_LOG_LEVEL_OFF) + */ +#define PSYSAPI_TRACE_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +#if !defined(PSYSAPI_TRACE_CONFIG) + #define PSYSAPI_TRACE_CONFIG PSYSAPI_TRACE_CONFIG_DEFAULT +#endif + +/* Module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_TRACE_CONFIG)) + /* Module specific trace setting */ + #if PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_TRACE_CONFIG Tracing level defined" + #endif +#else + #error "PSYSAPI_TRACE_CONFIG not defined" +#endif + +/* Overriding submodules in PSYSAPI with a specific tracing level */ +/* #define PSYSAPI_DYNAMIC_TRACING_OVERRIDE TRACE_LOG_LEVEL_VERBOSE */ + +#endif /* __IA_CSS_PSYSAPI_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h new file mode 100644 index 000000000000..3fec775eb019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h @@ -0,0 +1,223 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_KERNEL_BITMAP_H +#define __IA_CSS_KERNEL_BITMAP_H + +/*! \file */ + +/** @file ia_css_kernel_bitmap.h + * + * The types and operations to make logic decisions given kernel bitmaps + * "ia_css_kernel_bitmap_t" can be larger than native types + */ + +#include +#include "vied_nci_psys_resource_model.h" + +#define IA_CSS_KERNEL_BITMAP_BITS 64 +#define IA_CSS_KERNEL_BITMAP_ELEM_TYPE uint32_t +#define IA_CSS_KERNEL_BITMAP_ELEM_BITS \ + (sizeof(IA_CSS_KERNEL_BITMAP_ELEM_TYPE)*8) +#define IA_CSS_KERNEL_BITMAP_NOF_ELEMS \ + ((IA_CSS_KERNEL_BITMAP_BITS) / (IA_CSS_KERNEL_BITMAP_ELEM_BITS)) + +/** An element is a 32 bit unsigned integer. 64 bit integers might cause + * problems in the compiler. + */ +typedef struct { + IA_CSS_KERNEL_BITMAP_ELEM_TYPE data[IA_CSS_KERNEL_BITMAP_NOF_ELEMS]; +} ia_css_kernel_bitmap_elems_t; + +/** Users should make no assumption about the actual type of + * ia_css_kernel_bitmap_t. + * Users should use IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS in + * case they erroneously assume that this type is uint64_t and they + * cannot change their implementation. + */ +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS +typedef ia_css_kernel_bitmap_elems_t ia_css_kernel_bitmap_t; +#else +typedef uint64_t ia_css_kernel_bitmap_t; +#if IA_CSS_KERNEL_BITMAP_BITS > 64 +#error IA_CSS_KERNEL_BITMAP_BITS > 64 not supported \ + with IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS +#endif +#endif + +/*! Print the bits of a kernel bitmap + + @return < 0 on error + */ +extern int ia_css_kernel_bitmap_print( + const ia_css_kernel_bitmap_t bitmap, + void *fid); + +/*! Create an empty kernel bitmap + + @return bitmap = 0 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_clear(void); + +/*! Creates the complement of a kernel bitmap + * @param bitmap[in] kernel bitmap + * @return ~bitmap + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_complement( + const ia_css_kernel_bitmap_t bitmap); + +/*! Create the union of two kernel bitmaps + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 | bitmap1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_union( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Create the intersection of two kernel bitmaps + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 & bitmap1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_intersection( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the kernel bitmaps is empty + + @param bitmap[in] kernel bitmap + + @return bitmap == 0 + */ +extern bool ia_css_is_kernel_bitmap_empty( + const ia_css_kernel_bitmap_t bitmap); + +/*! Check if the intersection of two kernel bitmaps is empty + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return (bitmap0 & bitmap1) == 0 + */ +extern bool ia_css_is_kernel_bitmap_intersection_empty( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the second kernel bitmap is a subset of the first (or equal) + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + Note: An empty set is always a subset, this function + returns true if bitmap 1 is empty + + @return (bitmap0 & bitmap1) == bitmap1 + */ +extern bool ia_css_is_kernel_bitmap_subset( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the kernel bitmaps are equal + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 == bitmap1 + */ +extern bool ia_css_is_kernel_bitmap_equal( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Right shift kernel bitmap + + @param bitmap0[in] kernel bitmap 0 + + @return bitmap0 >> 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_shift( + const ia_css_kernel_bitmap_t bitmap); + +/*! Check if the kernel bitmaps contains only a single element + + @param bitmap[in] kernel bitmap + + @return weight(bitmap) == 1 + */ +extern bool ia_css_is_kernel_bitmap_onehot( + const ia_css_kernel_bitmap_t bitmap); + +/*! Checks whether a specific kernel bit is set + * @return bitmap[index] == 1 + */ +extern int ia_css_is_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Create the union of a kernel bitmap with a onehot bitmap + * with a bit set at index + + @return bitmap[index] |= 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Creates kernel bitmap using a uint64 value. + * @return bitmap with the same bits set as in value (provided that width of bitmap is sufficient). + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_create_from_uint64( + const uint64_t value); + +/*! Converts an ia_css_kernel_bitmap_t type to uint64_t. Note that if + * ia_css_kernel_bitmap_t contains more then 64 bits, only the lowest 64 bits + * are returned. + * @return uint64_t representation of value +*/ +extern uint64_t ia_css_kernel_bitmap_to_uint64( + const ia_css_kernel_bitmap_t value); + +/*! Creates a kernel bitmap with the bit at index 'index' removed. + * @return ~(1 << index) & bitmap + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_unset( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Set a previously clear field of a kernel bitmap at index + + @return if bitmap[index] == 0, bitmap[index] -> 1, else 0 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set_unique( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Create a onehot kernel bitmap with a bit set at index + + @return bitmap[index] = 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bit_mask( + const unsigned int index); + +/*! Create a random bitmap + + @return bitmap[index] = 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_ran_bitmap(void); + +#endif /* __IA_CSS_KERNEL_BITMAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h new file mode 100644 index 000000000000..1ba29c7ab77e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_KERNEL_TRACE_H +#define __IA_CSS_PSYS_KERNEL_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_KERNEL_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_KERNEL_TRACING_OVERRIDE)) + #define PSYS_KERNEL_TRACE_LEVEL_CONFIG \ + PSYS_KERNEL_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_KERNEL_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_KERNEL_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_KERNEL_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c new file mode 100644 index 000000000000..5fd9496bc3cc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c @@ -0,0 +1,413 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include +#include +#include +#include "ia_css_psys_kernel_trace.h" + +static int ia_css_kernel_bitmap_compute_weight( + const ia_css_kernel_bitmap_t bitmap); + +bool ia_css_is_kernel_bitmap_intersection_empty( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + ia_css_kernel_bitmap_t intersection; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_intersection_empty(): enter:\n"); + + intersection = ia_css_kernel_bitmap_intersection(bitmap0, bitmap1); + return ia_css_is_kernel_bitmap_empty(intersection); +} + +bool ia_css_is_kernel_bitmap_empty( + const ia_css_kernel_bitmap_t bitmap) +{ + unsigned int i; + bool is_empty = true; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_empty(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + is_empty &= bitmap.data[i] == 0; + } +#else + NOT_USED(i); + is_empty = (bitmap == 0); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return is_empty; +} + +bool ia_css_is_kernel_bitmap_equal( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + bool is_equal = true; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_equal(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + is_equal = is_equal && (bitmap0.data[i] == bitmap1.data[i]); + } +#else + NOT_USED(i); + is_equal = (bitmap0 == bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return is_equal; +} + +bool ia_css_is_kernel_bitmap_onehot( + const ia_css_kernel_bitmap_t bitmap) +{ + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_onehot(): enter:\n"); + return ia_css_kernel_bitmap_compute_weight(bitmap) == 1; +} + +bool ia_css_is_kernel_bitmap_subset( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + ia_css_kernel_bitmap_t intersection; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_subset(): enter:\n"); + + intersection = ia_css_kernel_bitmap_intersection(bitmap0, bitmap1); + return ia_css_is_kernel_bitmap_equal(intersection, bitmap1); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_clear(void) +{ + unsigned int i; + ia_css_kernel_bitmap_t bitmap; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_clear(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + bitmap.data[i] = 0; + } +#else + NOT_USED(i); + bitmap = 0; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return bitmap; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_complement( + const ia_css_kernel_bitmap_t bitmap) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_complement(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = ~bitmap.data[i]; + } +#else + NOT_USED(i); + result = ~bitmap; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_union( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_union(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] | bitmap1.data[i]); + } +#else + NOT_USED(i); + result = (bitmap0 | bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_intersection( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_intersection(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] & bitmap1.data[i]); + } +#else + NOT_USED(i); + result = (bitmap0 & bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_set(): enter:\n"); + + bit_mask = ia_css_kernel_bit_mask(index); + return ia_css_kernel_bitmap_union(bitmap, bit_mask); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_create_from_uint64( + const uint64_t value) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_create_from_uint64(): enter:\n"); + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + result = ia_css_kernel_bitmap_clear(); + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + /* masking is done implictly, the MSB bits of casting will be chopped off */ + result.data[i] = (IA_CSS_KERNEL_BITMAP_ELEM_TYPE) + (value >> (i * IA_CSS_KERNEL_BITMAP_ELEM_BITS)); + } +#if IA_CSS_KERNEL_BITMAP_BITS < 64 + if ((value >> IA_CSS_KERNEL_BITMAP_BITS) != 0) { + IA_CSS_TRACE_0(PSYSAPI_KERNEL, ERROR, + "ia_css_kernel_bitmap_create_from_uint64(): " + "kernel bitmap is not wide enough to encode value\n"); + assert(0); + } +#endif +#else + NOT_USED(i); + result = value; +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ + return result; +} + +uint64_t ia_css_kernel_bitmap_to_uint64( + const ia_css_kernel_bitmap_t value) +{ + const unsigned int bits64 = sizeof(uint64_t) * 8; + const unsigned int nof_elems_bits64 = bits64 / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + unsigned int i; + uint64_t res = 0; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_to_uint64(): enter:\n"); + + assert((bits64 % IA_CSS_KERNEL_BITMAP_ELEM_BITS) == 0); + assert(nof_elems_bits64 > 0); + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < nof_elems_bits64; i++) { + res |= ((uint64_t)(value.data[i]) << (i * IA_CSS_KERNEL_BITMAP_ELEM_BITS)); + } + for (i = nof_elems_bits64; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + assert(value.data[i] == 0); + } + return res; +#else + (void)i; + (void)res; + (void)nof_elems_bits64; + return (uint64_t)value; +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_unset( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t result; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_unset(): enter:\n"); + + result = ia_css_kernel_bit_mask(index); + result = ia_css_kernel_bitmap_complement(result); + return ia_css_kernel_bitmap_intersection(bitmap, result); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set_unique( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t ret; + ia_css_kernel_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_set_unique(): enter:\n"); + + ret = ia_css_kernel_bitmap_clear(); + bit_mask = ia_css_kernel_bit_mask(index); + + if (ia_css_is_kernel_bitmap_intersection_empty(bitmap, bit_mask) + && !ia_css_is_kernel_bitmap_empty(bit_mask)) { + ret = ia_css_kernel_bitmap_union(bitmap, bit_mask); + } + return ret; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bit_mask( + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + ia_css_kernel_bitmap_t bit_mask = ia_css_kernel_bitmap_clear(); + + /* Assert disabled for staging, because some PGs do not satisfy this condition */ + /* assert(index < IA_CSS_KERNEL_BITMAP_BITS); */ + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bit_mask(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + if (index < IA_CSS_KERNEL_BITMAP_BITS) { + elem_index = index / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + elem_bit_index = index % IA_CSS_KERNEL_BITMAP_ELEM_BITS; + assert(elem_index < IA_CSS_KERNEL_BITMAP_NOF_ELEMS); + + bit_mask.data[elem_index] = 1 << elem_bit_index; + } +#else + NOT_USED(elem_index); + NOT_USED(elem_bit_index); + if (index < IA_CSS_KERNEL_BITMAP_BITS) { + bit_mask = (ia_css_kernel_bitmap_t)1 << index; + } +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return bit_mask; +} + + +static int ia_css_kernel_bitmap_compute_weight( + const ia_css_kernel_bitmap_t bitmap) +{ + ia_css_kernel_bitmap_t loc_bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_compute_weight(): enter:\n"); + + loc_bitmap = bitmap; + + /* In fact; do not need the iterator "i" */ + for (i = 0; (i < IA_CSS_KERNEL_BITMAP_BITS) && + !ia_css_is_kernel_bitmap_empty(loc_bitmap); i++) { + weight += ia_css_is_kernel_bitmap_set(loc_bitmap, 0); + loc_bitmap = ia_css_kernel_bitmap_shift(loc_bitmap); + } + + return weight; +} + +int ia_css_is_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_set(): enter:\n"); + + /* Assert disabled for staging, because some PGs do not satisfy this condition */ + /* assert(index < IA_CSS_KERNEL_BITMAP_BITS); */ + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + elem_index = index / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + elem_bit_index = index % IA_CSS_KERNEL_BITMAP_ELEM_BITS; + assert(elem_index < IA_CSS_KERNEL_BITMAP_NOF_ELEMS); + return (((bitmap.data[elem_index] >> elem_bit_index) & 0x1) == 1); +#else + NOT_USED(elem_index); + NOT_USED(elem_bit_index); + return (((bitmap >> index) & 0x1) == 1); +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_shift( + const ia_css_kernel_bitmap_t bitmap) +{ + int i; + unsigned int lsb_current_elem = 0; + unsigned int lsb_previous_elem = 0; + ia_css_kernel_bitmap_t loc_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_shift(): enter:\n"); + + loc_bitmap = bitmap; + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = IA_CSS_KERNEL_BITMAP_NOF_ELEMS - 1; i >= 0; i--) { + lsb_current_elem = bitmap.data[i] & 0x01; + loc_bitmap.data[i] >>= 1; + loc_bitmap.data[i] |= (lsb_previous_elem << (IA_CSS_KERNEL_BITMAP_ELEM_BITS - 1)); + lsb_previous_elem = lsb_current_elem; + } +#else + NOT_USED(i); + NOT_USED(lsb_current_elem); + NOT_USED(lsb_previous_elem); + loc_bitmap >>= 1; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return loc_bitmap; +} + +int ia_css_kernel_bitmap_print( + const ia_css_kernel_bitmap_t bitmap, + void *fid) +{ + int retval = -1; + int bit; + unsigned int bit_index = 0; + ia_css_kernel_bitmap_t loc_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, + "ia_css_kernel_bitmap_print(): enter:\n"); + + NOT_USED(fid); + NOT_USED(bit); + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, "kernel bitmap {\n"); + + loc_bitmap = bitmap; + + for (bit_index = 0; (bit_index < IA_CSS_KERNEL_BITMAP_BITS) && + !ia_css_is_kernel_bitmap_empty(loc_bitmap); bit_index++) { + + bit = ia_css_is_kernel_bitmap_set(loc_bitmap, 0); + loc_bitmap = ia_css_kernel_bitmap_shift(loc_bitmap); + IA_CSS_TRACE_2(PSYSAPI_KERNEL, INFO, "\t%d\t = %d\n", bit_index, bit); + } + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, "}\n"); + + retval = 0; + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h new file mode 100644 index 000000000000..485dd63e5a86 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h @@ -0,0 +1,293 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_H + +/*! \file */ + +/** @file ia_css_program_group_param.h + * + * Define the methods on the program group parameter object that are not part + * of a single interface + */ +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ + +#include + +/*! Get the stored size of the program group parameter object + + @param param[in] program group parameter object + + @return size, 0 on error + */ +extern size_t ia_css_program_group_param_get_size( + const ia_css_program_group_param_t *param); + +/*! initialize program_group_param + + @param blob[in] program group parameter object + @param program_count[in] number of terminals. + @param terminal_count[in] number of terminals. + @param fragment_count[in] number of terminals. + + @return 0 if success, else failure. + */ +extern int ia_css_program_group_param_init( + ia_css_program_group_param_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count, + const enum ia_css_frame_format_type *frame_format_types); +/*! Get the program parameter object from a program group parameter object + + @param program_group_param[in] program group parameter object + @param i[in] program parameter index + + @return program parameter pointer, NULL on error + */ +extern ia_css_program_param_t *ia_css_program_group_param_get_program_param( + const ia_css_program_group_param_t *param, + const int i); + +/*! Get the terminal parameter object from a program group parameter object + + @param program_group_param[in] program group parameter object + @param i[in] terminal parameter index + + @return terminal parameter pointer, NULL on error + */ +extern ia_css_terminal_param_t *ia_css_program_group_param_get_terminal_param( + const ia_css_program_group_param_t *param, + const int i); + +/*! Get the fragment count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return fragment count, 0 on error + */ +extern uint16_t ia_css_program_group_param_get_fragment_count( + const ia_css_program_group_param_t *param); + +/*! Get the program count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return program count, 0 on error + */ +extern uint8_t ia_css_program_group_param_get_program_count( + const ia_css_program_group_param_t *param); + +/*! Get the terminal count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return terminal count, 0 on error + */ +extern uint8_t ia_css_program_group_param_get_terminal_count( + const ia_css_program_group_param_t *param); + +/*! Set the protocol version in a program group parameter object + + @param program_group_param[in] program group parameter object + @param protocol_version[in] protocol version + + @return nonzero on error +*/ +extern int +ia_css_program_group_param_set_protocol_version( + ia_css_program_group_param_t *param, + uint8_t protocol_version); + +/*! Get the protocol version from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return protocol version +*/ +extern uint8_t +ia_css_program_group_param_get_protocol_version( + const ia_css_program_group_param_t *param); + +/*! Set the kernel enable bitmap from a program group parameter object + + @param param[in] program group parameter object + @param bitmap[in] kernel enable bitmap + + @return non-zero on error + */ +extern int ia_css_program_group_param_set_kernel_enable_bitmap( + ia_css_program_group_param_t *param, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the kernel enable bitmap from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return kernel enable bitmap, 0 on error +*/ +extern ia_css_kernel_bitmap_t +ia_css_program_group_param_get_kernel_enable_bitmap( + const ia_css_program_group_param_t *param); + +/*! Get the stored size of the program parameter object + + @param param[in] program parameter object + + @return size, 0 on error + */ +extern size_t ia_css_program_param_get_size( + const ia_css_program_param_t *param); + +/*! Set the kernel enable bitmap from a program parameter object + + @param program_param[in] program parameter object + @param bitmap[in] kernel enable bitmap + + @return non-zero on error + */ +extern int ia_css_program_param_set_kernel_enable_bitmap( + ia_css_program_param_t *program_param, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the kernel enable bitmap from a program parameter object + + @param program_param[in] program parameter object + + Note: This function returns in fact the kernel enable of the program group + parameters + + @return kernel enable bitmap, 0 on error + */ +extern ia_css_kernel_bitmap_t ia_css_program_param_get_kernel_enable_bitmap( + const ia_css_program_param_t *param); + +/*! Get the stored size of the terminal parameter object + + @param param[in] terminal parameter object + + @return size, 0 on error + */ +extern size_t ia_css_terminal_param_get_size( + const ia_css_terminal_param_t *param); + +/*! Get the kernel enable bitmap from a terminal parameter object + + @param terminal_param[in] terminal parameter object + + Note: This function returns in fact the kernel enable of the program group + parameters + + @return kernel enable bitmap, 0 on error + */ +extern ia_css_kernel_bitmap_t ia_css_terminal_param_get_kernel_enable_bitmap( + const ia_css_terminal_param_t *param); + +/*! Get the parent object for this terminal param. + + @param terminal_param[in] terminal parameter object + + @return parent program group param object + */ +extern ia_css_program_group_param_t *ia_css_terminal_param_get_parent( + const ia_css_terminal_param_t *param); + +/*! Get the data format type associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return data format type (ia_css_data_format_type_t) + */ +extern ia_css_frame_format_type_t ia_css_terminal_param_get_frame_format_type( + const ia_css_terminal_param_t *terminal_param); + +/*! Set the data format type associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param data_format_type[in] data format type + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_frame_format_type( + ia_css_terminal_param_t *terminal_param, + const ia_css_frame_format_type_t data_format_type); + +/*! Get bits per pixel on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return bits per pixel + */ +extern uint8_t ia_css_terminal_param_get_bpp( + const ia_css_terminal_param_t *terminal_param); + +/*! Set bits per pixel on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param bpp[in] bits per pixel + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_bpp( + ia_css_terminal_param_t *terminal_param, + const uint8_t bpp); + +/*! Get dimensions on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param dimensions[out] dimension array + + @return non-zero on error. + */ +extern int ia_css_terminal_param_get_dimensions( + const ia_css_terminal_param_t *terminal_param, + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]); + +/*! Set dimensions on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param dimensions[in] dimension array + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_dimensions( + ia_css_terminal_param_t *terminal_param, + const uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]); + +/*! Get stride on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return stride of the frame to be attached. + */ +extern uint32_t ia_css_terminal_param_get_stride( + const ia_css_terminal_param_t *terminal_param); + +/*! Set stride on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param stride[in] stride + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_stride( + ia_css_terminal_param_t *terminal_param, + const uint32_t stride); + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h new file mode 100644 index 000000000000..7821f8147a1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h @@ -0,0 +1,153 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H + +/*! \file */ + +/** @file ia_css_program_group_param.sim.h + * + * Define the methods on the program group parameter object: Simulation only + */ +#include + +#include + +#include + +/* Simulation */ + +/*! Create a program group parameter object from specification + + @param specification[in] specification (index) + @param manifest[in] program group manifest + + @return NULL on error + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_create( + const unsigned int specification, + const ia_css_program_group_manifest_t *manifest); + +/*! Destroy the program group parameter object + + @param program_group_param[in] program group parameter object + + @return NULL + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_destroy( + ia_css_program_group_param_t *param); + +/*! Compute the size of storage required for allocating + * the program group parameter object + + @param program_count[in] Number of programs in the process group + @param terminal_count[in] Number of terminals on the process group + @param fragment_count[in] Number of fragments on the terminals of + the process group + + @return 0 on error + */ +size_t ia_css_sizeof_program_group_param( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count); + +/*! Allocate (the store of) a program group parameter object + + @param program_count[in] Number of programs in the process group + @param terminal_count[in] Number of terminals on the process group + @param fragment_count[in] Number of fragments on the terminals of + the process group + + @return program group parameter pointer, NULL on error + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_alloc( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count); + +/*! Free (the store of) a program group parameter object + + @param program_group_param[in] program group parameter object + + @return NULL + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_free( + ia_css_program_group_param_t *param); + +/*! Print the program group parameter object to file/stream + + @param param[in] program group parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_param_print( + const ia_css_program_group_param_t *param, + void *fid); + +/*! Allocate (the store of) a program parameter object + + @return program parameter pointer, NULL on error + */ +extern ia_css_program_param_t *ia_css_program_param_alloc(void); + +/*! Free (the store of) a program parameter object + + @param param[in] program parameter object + + @return NULL + */ +extern ia_css_program_param_t *ia_css_program_param_free( + ia_css_program_param_t *param); + +/*! Print the program parameter object to file/stream + + @param param[in] program parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_param_print( + const ia_css_program_param_t *param, + void *fid); + +/*! Allocate (the store of) a terminal parameter object + + @return terminal parameter pointer, NULL on error + */ +extern ia_css_terminal_param_t *ia_css_terminal_param_alloc(void); + +/*! Free (the store of) a terminal parameter object + + @param param[in] terminal parameter object + + @return NULL + */ +extern ia_css_terminal_param_t *ia_css_terminal_param_free( + ia_css_terminal_param_t *param); + +/*! Print the terminal parameter object to file/stream + + @param param[in] terminal parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_terminal_param_print( + const ia_css_terminal_param_t *param, + void *fid); + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h new file mode 100644 index 000000000000..34f57584a227 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h @@ -0,0 +1,64 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H + +/*! \file */ + +/** @file ia_css_program_group_param_types.h + * + * Define the parameter objects that are necessary to create the process + * groups i.e. enable parameters and parameters to set-up frame descriptors + */ + +#include +#include /* ia_css_kernel_bitmap_t */ +#include + +#include +/*! make this public so that driver can populate, + * size, bpp, dimensions for all terminals. + * + * Currently one API is provided to get frame_format_type. + * + * frame_format_type is set during ia_css_terminal_param_init(). + * Value for that is const and binary specific. + */ +struct ia_css_terminal_param_s { + uint32_t size; /**< Size of this structure */ + /**< Indicates if this is a generic type or inbuild + * with variable size descriptor + */ + ia_css_frame_format_type_t frame_format_type; + /**< offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION];/**< Logical dimensions */ + /**< Mapping to the index field of the terminal descriptor */ + uint16_t index[IA_CSS_N_DATA_DIMENSION]; + /**< Logical fragment dimension, + * TODO: fragment dimensions can be different per fragment + */ + uint16_t fragment_dimensions[IA_CSS_N_DATA_DIMENSION]; + uint32_t stride;/**< Stride of a frame */ + uint16_t offset;/**< Offset in bytes to first fragment */ + uint8_t bpp; /**< Bits per pixel */ + uint8_t bpe; /**< Bits per element */ +}; + +typedef struct ia_css_program_group_param_s ia_css_program_group_param_t; +typedef struct ia_css_program_param_s ia_css_program_param_t; +typedef struct ia_css_terminal_param_s ia_css_terminal_param_t; + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h new file mode 100644 index 000000000000..f59dfbf165e4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PARAM_TRACE_H +#define __IA_CSS_PSYS_PARAM_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_PARAM_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_PARAM_TRACING_OVERRIDE)) + #define PSYS_PARAM_TRACE_LEVEL_CONFIG PSYS_PARAM_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_PARAM_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_PARAM_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_PARAM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c new file mode 100644 index 000000000000..067f69a4a01e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c @@ -0,0 +1,771 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ia_css_psys_param_trace.h" + +static int +ia_css_terminal_param_init(ia_css_terminal_param_t *terminal_param, + uint32_t offset, + enum ia_css_frame_format_type frame_format_type); + +static int +ia_css_program_param_init(ia_css_program_param_t *program_param, + int32_t offset); + +size_t ia_css_sizeof_program_group_param( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_sizeof_program_group_param(): enter:\n"); + + verifexit(program_count != 0); + verifexit(terminal_count != 0); + verifexit(fragment_count != 0); + + size += sizeof(ia_css_program_group_param_t); + size += program_count * fragment_count * sizeof(ia_css_program_param_t); + size += terminal_count * sizeof(ia_css_terminal_param_t); +EXIT: + if (0 == program_count || 0 == terminal_count || 0 == fragment_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_sizeof_program_group_param invalid argument\n"); + } + return size; +} + +size_t ia_css_program_group_param_get_size( + const ia_css_program_group_param_t *program_group_param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_size(): enter:\n"); + + if (program_group_param != NULL) { + size = program_group_param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_size invalid argument\n"); + } + return size; +} + +size_t ia_css_program_param_get_size( + const ia_css_program_param_t *param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_get_size(): enter:\n"); + + if (param != NULL) { + size = param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_param_get_size invalid argument\n"); + } + return size; +} + +ia_css_program_param_t *ia_css_program_group_param_get_program_param( + const ia_css_program_group_param_t *param, + const int i) +{ + ia_css_program_param_t *program_param = NULL; + ia_css_program_param_t *program_param_base; + int program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_program_param(): enter:\n"); + + verifexit(param != NULL); + + program_count = + (int)ia_css_program_group_param_get_program_count(param); + + verifexit(i < program_count); + + program_param_base = (ia_css_program_param_t *) + (((char *)param) + param->program_param_offset); + + program_param = &program_param_base[i]; + +EXIT: + if (NULL == param || i >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_program_param invalid argument\n"); + } + return program_param; +} + +size_t ia_css_terminal_param_get_size( + const ia_css_terminal_param_t *param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_size(): enter:\n"); + + if (param != NULL) { + size = param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_size invalid argument\n"); + } + + return size; +} + +ia_css_terminal_param_t *ia_css_program_group_param_get_terminal_param( + const ia_css_program_group_param_t *param, + const int i) +{ + ia_css_terminal_param_t *terminal_param = NULL; + ia_css_terminal_param_t *terminal_param_base; + int program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_terminal_param(): enter:\n"); + + verifexit(param != NULL); + + program_count = + (int)ia_css_program_group_param_get_terminal_count(param); + + verifexit(i < program_count); + + terminal_param_base = (ia_css_terminal_param_t *) + (((char *)param) + param->terminal_param_offset); + terminal_param = &terminal_param_base[i]; +EXIT: + if (NULL == param || i >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_terminal_param invalid argument\n"); + } + return terminal_param; +} + +uint8_t ia_css_program_group_param_get_program_count( + const ia_css_program_group_param_t *param) +{ + uint8_t program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_program_count(): enter:\n"); + + if (param != NULL) { + program_count = param->program_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_program_count invalid argument\n"); + } + return program_count; +} + +uint8_t ia_css_program_group_param_get_terminal_count( + const ia_css_program_group_param_t *param) +{ + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_terminal_count(): enter:\n"); + + if (param != NULL) { + terminal_count = param->terminal_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +uint16_t ia_css_program_group_param_get_fragment_count( + const ia_css_program_group_param_t *param) +{ + uint8_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_fragment_count(): enter:\n"); + + if (param != NULL) { + fragment_count = (uint8_t)param->fragment_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_fragment_count invalid argument\n"); + } + return fragment_count; +} + +int ia_css_program_group_param_set_protocol_version( + ia_css_program_group_param_t *param, + uint8_t protocol_version) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_set_protocol_version(): enter:\n"); + + if (param != NULL) { + param->protocol_version = protocol_version; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_set_protocol_version failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_program_group_param_get_protocol_version( + const ia_css_program_group_param_t *param) +{ + uint8_t protocol_version = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_protocol_version(): enter:\n"); + + if (param != NULL) { + protocol_version = param->protocol_version; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_protocol_version invalid argument\n"); + } + return protocol_version; +} + +int ia_css_program_group_param_set_kernel_enable_bitmap( + ia_css_program_group_param_t *param, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_set_kernel_enable_bitmap(): enter:\n"); + + if (param != NULL) { + param->kernel_enable_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_set_kernel_enable_bitmap failed (%i)\n", + retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_group_param_get_kernel_enable_bitmap( + const ia_css_program_group_param_t *param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_kernel_enable_bitmap(): enter:\n"); + + if (param != NULL) { + bitmap = param->kernel_enable_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_param_set_kernel_enable_bitmap( + ia_css_program_param_t *program_param, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_set_kernel_enable_bitmap(): enter:\n"); + + if (program_param != NULL) { + program_param->kernel_enable_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_set_kernel_enable_bitmap failed (%i)\n", + retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_param_get_kernel_enable_bitmap( + const ia_css_program_param_t *program_param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_get_kernel_enable_bitmap(): enter:\n"); + + verifexit(program_param != NULL); + verifexit(program_param->parent_offset != 0); + + base = (char *)((char *)program_param + program_param->parent_offset); + bitmap = ((ia_css_program_group_param_t *)base)->kernel_enable_bitmap; +EXIT: + if (NULL == program_param || 0 == program_param->parent_offset) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +ia_css_kernel_bitmap_t ia_css_terminal_param_get_kernel_enable_bitmap( + const ia_css_terminal_param_t *param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_kernel_enable_bitmap(): enter:\n"); + + verifexit(param != NULL); + verifexit(param->parent_offset != 0); + + base = (char *)((char *)param + param->parent_offset); + bitmap = ((ia_css_program_group_param_t *)base)->kernel_enable_bitmap; +EXIT: + if (NULL == param || 0 == param->parent_offset) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +ia_css_frame_format_type_t ia_css_terminal_param_get_frame_format_type( + const ia_css_terminal_param_t *param) +{ + ia_css_frame_format_type_t ft = IA_CSS_N_FRAME_FORMAT_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_frame_format_type(): enter:\n"); + + verifexit(param != NULL); + + ft = param->frame_format_type; +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_frame_format_type invalid argument\n"); + } + return ft; +} + +int ia_css_terminal_param_set_frame_format_type( + ia_css_terminal_param_t *param, + const ia_css_frame_format_type_t data_format_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_frame_format_type(): enter:\n"); + + if (param != NULL) { + param->frame_format_type = data_format_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_frame_format_type failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_terminal_param_get_bpp( + const ia_css_terminal_param_t *param) +{ + uint8_t bpp = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_bpp(): enter:\n"); + + verifexit(param != NULL); + + bpp = param->bpp; + +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_bpp invalid argument\n"); + } + return bpp; +} + +int ia_css_terminal_param_set_bpp( + ia_css_terminal_param_t *param, + const uint8_t bpp) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_bpp(): enter:\n"); + + if (param != NULL) { + param->bpp = bpp; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_bpp failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_get_dimensions( + const ia_css_terminal_param_t *param, + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_dimensions(): enter:\n"); + + if (param != NULL) { + dimensions[IA_CSS_COL_DIMENSION] = + param->dimensions[IA_CSS_COL_DIMENSION]; + dimensions[IA_CSS_ROW_DIMENSION] = + param->dimensions[IA_CSS_ROW_DIMENSION]; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_get_dimensions failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_set_dimensions( + ia_css_terminal_param_t *param, + const uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_dimensions(): enter:\n"); + + if (param != NULL) { + param->dimensions[IA_CSS_COL_DIMENSION] = + dimensions[IA_CSS_COL_DIMENSION]; + param->dimensions[IA_CSS_ROW_DIMENSION] = + dimensions[IA_CSS_ROW_DIMENSION]; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_dimensions failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_set_stride( + ia_css_terminal_param_t *param, + const uint32_t stride) +{ + int retval = -1; + + verifexit(param != NULL); + param->stride = stride; + retval = 0; + +EXIT: + return retval; +} + +uint32_t ia_css_terminal_param_get_stride( + const ia_css_terminal_param_t *param) +{ + uint32_t stride = 0; + + verifexit(param != NULL); + stride = param->stride; + +EXIT: + return stride; +} + + +static int ia_css_program_param_init( + ia_css_program_param_t *program_param, + int32_t offset) +{ + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_param_t))); + verifexit(program_param != NULL); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_param_init(): enter:\n"); + + program_param->size = sizeof(ia_css_program_param_t); + /* parent is at negative offset from current program.*/ + program_param->parent_offset = -offset; + /*TODO: Kernel_bitmap setting. ?*/ + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_init failed (%i)\n", retval); + } + return retval; +} + +static int +ia_css_terminal_param_init(ia_css_terminal_param_t *terminal_param, + uint32_t offset, + enum ia_css_frame_format_type frame_format_type) +{ + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_TERMINAL_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_terminal_param_t))); + verifexit(terminal_param != NULL); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_terminal_param_init(): enter:\n"); + + terminal_param->size = sizeof(ia_css_terminal_param_t); + /* parent is at negative offset from current program.*/ + terminal_param->parent_offset = -((int32_t)offset); + /*TODO: Kernel_bitmap setting. ?*/ + terminal_param->frame_format_type = frame_format_type; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_init failed (%i)\n", retval); + } + return retval; +} + +ia_css_program_group_param_t * +ia_css_terminal_param_get_parent( + const ia_css_terminal_param_t *param) +{ + ia_css_program_group_param_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_parent(): enter:\n"); + + verifexit(NULL != param); + + base = (char *)((char *)param + param->parent_offset); + + parent = (ia_css_program_group_param_t *)(base); +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_parent invalid argument\n"); + } + return parent; +} + +int ia_css_program_group_param_init( + ia_css_program_group_param_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count, + const enum ia_css_frame_format_type *frame_format_types) +{ + int i = 0; + char *param_base; + uint32_t offset; + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_GROUP_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_group_param_t))); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_group_param_init(): enter:\n"); + + assert(blob != 0); + + verifexit(blob != NULL); + verifexit(frame_format_types != NULL); + + blob->program_count = program_count; + blob->fragment_count = fragment_count; + blob->terminal_count = terminal_count; + blob->program_param_offset = sizeof(ia_css_program_group_param_t); + blob->terminal_param_offset = blob->program_param_offset + + sizeof(ia_css_program_param_t) * program_count; + + param_base = (char *)((char *)blob + blob->program_param_offset); + offset = blob->program_param_offset; + + for (i = 0; i < program_count; i++) { + ia_css_program_param_init( + (ia_css_program_param_t *)param_base, offset); + offset += sizeof(ia_css_program_param_t); + param_base += sizeof(ia_css_program_param_t); + } + + param_base = (char *)((char *)blob + blob->terminal_param_offset); + offset = blob->terminal_param_offset; + + for (i = 0; i < terminal_count; i++) { + ia_css_terminal_param_init( + (ia_css_terminal_param_t *)param_base, + offset, + frame_format_types[i]); + + offset += sizeof(ia_css_terminal_param_t); + param_base += sizeof(ia_css_terminal_param_t); + } + + /* + * For now, set legacy flow by default. This can be removed as soon + * as all hosts/drivers explicitly set the protocol version. + */ + blob->protocol_version = IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY; + + blob->size = (uint32_t)ia_css_sizeof_program_group_param(program_count, + terminal_count, + fragment_count); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_init failed (%i)\n", retval); + } + return retval; +} + +int ia_css_program_group_param_print( + const ia_css_program_group_param_t *param, + void *fid) +{ + int retval = -1; + int i; + uint8_t program_count, terminal_count; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_group_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "sizeof(program_group_param) = %d\n", + (int)ia_css_program_group_param_get_size(param)); + + program_count = ia_css_program_group_param_get_program_count(param); + terminal_count = ia_css_program_group_param_get_terminal_count(param); + + bitmap = ia_css_program_group_param_get_kernel_enable_bitmap(param); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "%d program params\n", (int)program_count); + for (i = 0; i < (int)program_count; i++) { + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + + retval = ia_css_program_param_print(program_param, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, "%d terminal params\n", + (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_param_t *terminal_param = + ia_css_program_group_param_get_terminal_param(param, i); + + retval = ia_css_terminal_param_print(terminal_param, fid); + verifjmpexit(retval == 0); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_print( + const ia_css_terminal_param_t *param, + void *fid) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_terminal_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "sizeof(terminal_param) = %d\n", + (int)ia_css_terminal_param_get_size(param)); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "\tframe_format_type = %d\n", param->frame_format_type); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_program_param_print( + const ia_css_program_param_t *param, + void *fid) +{ + int retval = -1; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, "sizeof(program_param) = %d\n", + (int)ia_css_program_param_get_size(param)); + + bitmap = ia_css_program_param_get_kernel_enable_bitmap(param); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_print failed (%i)\n", retval); + } + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h new file mode 100644 index 000000000000..6672737e51a1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h @@ -0,0 +1,80 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#include + +#define N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT 7 +#define SIZE_OF_PROGRAM_GROUP_PARAM_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + + IA_CSS_UINT16_T_BITS \ + + (3 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT * IA_CSS_UINT8_T_BITS)) + +/* tentative; co-design with ISP algorithm */ +struct ia_css_program_group_param_s { + /* The enable bits for each individual kernel */ + ia_css_kernel_bitmap_t kernel_enable_bitmap; + /* Size of this structure */ + uint32_t size; + uint32_t program_param_offset; + uint32_t terminal_param_offset; + /* Number of (explicit) fragments to use in a frame */ + uint16_t fragment_count; + /* Number of active programs */ + uint8_t program_count; + /* Number of active terminals */ + uint8_t terminal_count; + /* Program group protocol version */ + uint8_t protocol_version; + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT]; +}; + +#define SIZE_OF_PROGRAM_PARAM_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + IA_CSS_UINT32_T_BITS \ + + IA_CSS_INT32_T_BITS) + +/* private */ +struct ia_css_program_param_s { + /* What to use this one for ? */ + ia_css_kernel_bitmap_t kernel_enable_bitmap; + /* Size of this structure */ + uint32_t size; + /* offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; +}; + +#define SIZE_OF_TERMINAL_PARAM_STRUCT_IN_BITS \ + (IA_CSS_UINT32_T_BITS \ + + IA_CSS_FRAME_FORMAT_TYPE_BITS \ + + IA_CSS_INT32_T_BITS \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + IA_CSS_INT32_T_BITS \ + + IA_CSS_UINT16_T_BITS \ + + IA_CSS_UINT8_T_BITS \ + + (IA_CSS_UINT8_T_BITS * 1)) + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.c new file mode 100644 index 000000000000..a2dd8cbd1ba1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.c @@ -0,0 +1,50 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_server_manifest.h" + +/** + * Manifest of resources in use by PSYS itself + */ + +const vied_nci_resource_spec_t psys_server_manifest = { + /* internal memory */ + { /* resource id size offset*/ + {VIED_NCI_GMEM_TYPE_ID, 0, 0}, + {VIED_NCI_DMEM_TYPE_ID, VIED_NCI_DMEM0_MAX_SIZE, 0}, + {VIED_NCI_VMEM_TYPE_ID, 0, 0}, + {VIED_NCI_BAMEM_TYPE_ID, 0, 0}, + {VIED_NCI_PMEM_TYPE_ID, 0, 0} + }, + /* external memory */ + { /* resource id size offset*/ + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0}, + }, + /* device channel */ + { /* resource id size offset*/ + {VIED_NCI_DEV_CHN_DMA_EXT0_ID, + PSYS_SERVER_DMA_CHANNEL_SIZE, + PSYS_SERVER_DMA_CHANNEL_OFFSET}, + {VIED_NCI_DEV_CHN_GDC_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_EXT1_READ_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_INTERNAL_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_IPFD_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_ISA_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_FW_ID, 0, 0} + } +}; diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.h new file mode 100644 index 000000000000..b4c7fbc32d5b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psys_server_manifest/bxtB0/ia_css_psys_server_manifest.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SERVER_MANIFEST_H +#define __IA_CSS_PSYS_SERVER_MANIFEST_H + +#include "vied_nci_psys_resource_model.h" + +/** + * Manifest of resources in use by PSYS itself + */ + +#define PSYS_SERVER_DMA_CHANNEL_SIZE 2 +#define PSYS_SERVER_DMA_CHANNEL_OFFSET 28 + +extern const vied_nci_resource_spec_t psys_server_manifest; + +#endif /* __IA_CSS_PSYS_SERVER_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psysapi.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psysapi.mk new file mode 100644 index 000000000000..e1977cbe2ca2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/psysapi.mk @@ -0,0 +1,122 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PSYSAPI +# +ifdef _H_PSYSAPI_MK +$(error ERROR: psysapi.mk included multiple times, please check makefile) +else +_H_PSYSAPI_MK=1 +endif + +include $(MODULES_DIR)/config/psys/subsystem_$(IPU_SYSVER).mk + +PSYSAPI_DIR = $${MODULES_DIR}/psysapi + +PSYSAPI_PROCESS_HOST_FILES = $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process_group.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_buffer_set.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_terminal.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/param/src/ia_css_program_group_param.c + +# Use PSYS_MANIFEST_HOST_FILES when only accessing manifest functions +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_group_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_terminal_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/sim/src/vied_nci_psys_system.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/data/src/ia_css_program_group_data.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION)/ia_css_psys_server_manifest.c + +# Use only kernel bitmap functionality from PSYS API +PSYSAPI_KERNEL_BITMAP_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c +PSYSAPI_KERNEL_BITMAP_CPPFLAGS += -I$(PSYSAPI_DIR)/kernel/interface +PSYSAPI_KERNEL_BITMAP_CPPFLAGS += -I$(PSYSAPI_DIR)/interface + +# Use PSYSAPI_HOST_FILES when program and process group are both needed +PSYSAPI_HOST_FILES = $(PSYSAPI_PROCESS_HOST_FILES) $(PSYSAPI_MANIFEST_HOST_FILES) + +# Use PSYSAPI_PROCESS_GROUP_HOST_FILES when program and process group are both needed but there is no +# implementation (yet) of the user customization functions defined in ia_css_psys_process_group_cmd_impl.h. +# Dummy implementations are provided in $(PSYSAPI_DIR)/sim/src/ia_css_psys_process_group_cmd_impl.c +PSYSAPI_PROCESS_GROUP_HOST_FILES = $(PSYSAPI_HOST_FILES) +PSYSAPI_PROCESS_GROUP_HOST_FILES += $(PSYSAPI_DIR)/sim/src/ia_css_psys_process_group_cmd_impl.c + +# for now disabled, implementation for now provided by psys api impl +#PSYSAPI_HOST_FILES += $(PSYSAPI_DIR)/device/src/ia_css_psys_device.c + +PSYSAPI_HOST_CPPFLAGS = -I$(PSYSAPI_DIR)/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/device/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/device/interface/$(IPU_SYSVER) +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/dynamic/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/dynamic/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/data/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/data/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/static/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/static/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/kernel/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/param/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/param/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) + +PSYSAPI_FW_CPPFLAGS = $(PSYSAPI_HOST_CPPFLAGS) +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/static/interface +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/static/src +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/interface +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) + +# Defining the trace level for the PSYSAPI +PSYSAPI_HOST_CPPFLAGS += -DPSYSAPI_TRACE_CONFIG=PSYSAPI_TRACE_LOG_LEVEL_NORMAL +# Enable/Disable 'late binding' support and it's additional queues +PSYSAPI_HOST_CPPFLAGS += -DHAS_LATE_BINDING_SUPPORT=$(PSYS_HAS_LATE_BINDING_SUPPORT) + +#Example: how to switch to a different log level for a sub-module +#PSYSAPI_HOST_CPPFLAGS += -DPSYSAPI_DYNAMIC_TRACING_OVERRIDE=PSYSAPI_TRACE_LOG_LEVEL_DEBUG + +# enable host side implementation +# TODO: better name for the flag to enable the impl... +PSYSAPI_HOST_CPPFLAGS += -D__X86_SIM__ + +# Files for Firmware +PSYSAPI_FW_FILES = $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process_group.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_terminal.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_buffer_set.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/param/src/ia_css_program_group_param.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/data/src/ia_css_program_group_data.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/sim/src/vied_nci_psys_system.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/sim/src/ia_css_psys_sim_data.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_group_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_terminal_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION)/ia_css_psys_server_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c + +# resource model +PSYSAPI_RESOURCE_MODEL_FILES = $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c + +ifeq ($(PSYS_HAS_DUAL_CMD_CTX_SUPPORT), 1) +PSYSAPI_HOST_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(PSYS_HAS_DUAL_CMD_CTX_SUPPORT) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.c new file mode 100644 index 000000000000..03359e378d9b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.c @@ -0,0 +1,322 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "vied_nci_psys_resource_model.h" + +/* + * Cell types by cell IDs + */ +const vied_nci_cell_type_ID_t vied_nci_cell_type[VIED_NCI_N_CELL_ID] = { + VIED_NCI_SP_CTRL_TYPE_ID, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_ACC_ISA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_OSA_TYPE_ID, + VIED_NCI_GDC_TYPE_ID, + VIED_NCI_GDC_TYPE_ID +}; + +/* + * Memory types by memory IDs + */ +const vied_nci_mem_type_ID_t vied_nci_mem_type[VIED_NCI_N_MEM_ID] = { + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_GMEM_TYPE_ID,/* VMEM4 is GMEM according to vied_nci_cell_mem */ + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID +}; + +/* + * Cell mem count by cell type ID + */ +const uint16_t vied_nci_N_cell_mem[VIED_NCI_N_CELL_TYPE_ID] = { + VIED_NCI_N_SP_CTRL_MEM, + VIED_NCI_N_SP_SERVER_MEM, + VIED_NCI_N_VP_MEM, + VIED_NCI_N_ACC_PSA_MEM, + VIED_NCI_N_ACC_ISA_MEM, + VIED_NCI_N_ACC_OSA_MEM +}; + +/* + * Cell mem type by cell type ID and memory index + */ +const vied_nci_mem_type_ID_t +vied_nci_cell_mem_type[VIED_NCI_N_CELL_TYPE_ID][VIED_NCI_N_MEM_TYPE_ID] = { + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_GMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + } +}; + +/* + * Ext mem ID by memory index + */ +const vied_nci_mem_ID_t +vied_nci_ext_mem[VIED_NCI_N_MEM_TYPE_ID] = { + VIED_NCI_VMEM4_ID, /* VIED_NCI_GMEM_TYPE_ID */ + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID +}; + +/* + * Cell mem ID by cell ID and memory index + */ +const vied_nci_mem_ID_t +vied_nci_cell_mem[VIED_NCI_N_CELL_ID][VIED_NCI_N_MEM_TYPE_ID] = { + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM0_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM1_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM2_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM4_ID, + VIED_NCI_VMEM0_ID, + VIED_NCI_BAMEM0_ID, + VIED_NCI_PMEM0_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM5_ID, + VIED_NCI_VMEM1_ID, + VIED_NCI_BAMEM1_ID, + VIED_NCI_PMEM1_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM6_ID, + VIED_NCI_VMEM2_ID, + VIED_NCI_BAMEM2_ID, + VIED_NCI_PMEM2_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM7_ID, + VIED_NCI_VMEM3_ID, + VIED_NCI_BAMEM3_ID, + VIED_NCI_PMEM3_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + } +}; + +/* + * Memory sizes by mem ID + */ +const uint16_t vied_nci_mem_size[VIED_NCI_N_MEM_ID] = { + VIED_NCI_VMEM0_MAX_SIZE, + VIED_NCI_VMEM1_MAX_SIZE, + VIED_NCI_VMEM2_MAX_SIZE, + VIED_NCI_VMEM3_MAX_SIZE, + VIED_NCI_VMEM4_MAX_SIZE, + VIED_NCI_BAMEM0_MAX_SIZE, + VIED_NCI_BAMEM1_MAX_SIZE, + VIED_NCI_BAMEM2_MAX_SIZE, + VIED_NCI_BAMEM3_MAX_SIZE, + VIED_NCI_DMEM0_MAX_SIZE, + VIED_NCI_DMEM1_MAX_SIZE, + VIED_NCI_DMEM2_MAX_SIZE, + VIED_NCI_DMEM3_MAX_SIZE, + VIED_NCI_DMEM4_MAX_SIZE, + VIED_NCI_DMEM5_MAX_SIZE, + VIED_NCI_DMEM6_MAX_SIZE, + VIED_NCI_DMEM7_MAX_SIZE, + VIED_NCI_PMEM0_MAX_SIZE, + VIED_NCI_PMEM1_MAX_SIZE, + VIED_NCI_PMEM2_MAX_SIZE, + VIED_NCI_PMEM3_MAX_SIZE +}; + +/* + * Memory word sizes by mem type ID + */ +const uint16_t vied_nci_mem_word_size[VIED_NCI_N_DATA_MEM_TYPE_ID] = { + VIED_NCI_GMEM_WORD_SIZE, + VIED_NCI_DMEM_WORD_SIZE, + VIED_NCI_VMEM_WORD_SIZE, + VIED_NCI_BAMEM_WORD_SIZE +}; + +/* + * Number of channels by device ID + */ +const uint16_t vied_nci_dev_chn_size[VIED_NCI_N_DEV_CHN_ID] = { + VIED_NCI_DEV_CHN_DMA_EXT0_MAX_SIZE, + VIED_NCI_DEV_CHN_GDC_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_EXT1_READ_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_INTERNAL_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_IPFD_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_ISA_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_FW_MAX_SIZE +}; diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.h new file mode 100644 index 000000000000..1cb4e010d55d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/resource_model/bxtB0/vied_nci_psys_resource_model.h @@ -0,0 +1,300 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __VIED_NCI_PSYS_RESOURCE_MODEL_H +#define __VIED_NCI_PSYS_RESOURCE_MODEL_H + +#include "type_support.h" +#include "storage_class.h" + +#define HAS_DFM 0 +#define NON_RELOC_RESOURCE_SUPPORT 0 +#define IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + +/* Defines for the routing bitmap in the program group manifest. + */ +#define VIED_NCI_RBM_MAX_MUX_COUNT 0 +#define VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT 0 +#define VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT 0 +#define N_PADDING_UINT8_IN_RBM_MANIFEST 2 + +/* The amount of padding bytes needed to make + * ia_css_process_s structure 64 bit aligned + */ +#define N_PADDING_UINT8_IN_PROCESS_STRUCT 4 +#define N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST 4 + +/** + * Resource model for BXT B0 + */ + +/* + * Cell IDs + */ +typedef enum { + VIED_NCI_SP0_ID = 0, + VIED_NCI_SP1_ID, + VIED_NCI_SP2_ID, + VIED_NCI_VP0_ID, + VIED_NCI_VP1_ID, + VIED_NCI_VP2_ID, + VIED_NCI_VP3_ID, + VIED_NCI_ACC0_ID, + VIED_NCI_ACC1_ID, + VIED_NCI_ACC2_ID, + VIED_NCI_ACC3_ID, + VIED_NCI_ACC4_ID, + VIED_NCI_ACC5_ID, + VIED_NCI_ACC6_ID, + VIED_NCI_ACC7_ID, + VIED_NCI_GDC0_ID, + VIED_NCI_GDC1_ID, + VIED_NCI_N_CELL_ID +} vied_nci_cell_ID_t; + +/* + * Barrier bits (to model process group dependencies) + */ +typedef enum { + VIED_NCI_BARRIER0_ID, + VIED_NCI_BARRIER1_ID, + VIED_NCI_BARRIER2_ID, + VIED_NCI_BARRIER3_ID, + VIED_NCI_BARRIER4_ID, + VIED_NCI_BARRIER5_ID, + VIED_NCI_BARRIER6_ID, + VIED_NCI_BARRIER7_ID, + VIED_NCI_N_BARRIER_ID +} vied_nci_barrier_ID_t; + +/* + * Cell types + */ +typedef enum { + VIED_NCI_SP_CTRL_TYPE_ID = 0, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_ISA_TYPE_ID, + VIED_NCI_ACC_OSA_TYPE_ID, + VIED_NCI_GDC_TYPE_ID, + VIED_NCI_N_CELL_TYPE_ID +} vied_nci_cell_type_ID_t; + +/* + * Memory IDs + */ +typedef enum { + VIED_NCI_VMEM0_ID = 0, + VIED_NCI_VMEM1_ID, + VIED_NCI_VMEM2_ID, + VIED_NCI_VMEM3_ID, + VIED_NCI_VMEM4_ID, + VIED_NCI_BAMEM0_ID, + VIED_NCI_BAMEM1_ID, + VIED_NCI_BAMEM2_ID, + VIED_NCI_BAMEM3_ID, + VIED_NCI_DMEM0_ID, + VIED_NCI_DMEM1_ID, + VIED_NCI_DMEM2_ID, + VIED_NCI_DMEM3_ID, + VIED_NCI_DMEM4_ID, + VIED_NCI_DMEM5_ID, + VIED_NCI_DMEM6_ID, + VIED_NCI_DMEM7_ID, + VIED_NCI_PMEM0_ID, + VIED_NCI_PMEM1_ID, + VIED_NCI_PMEM2_ID, + VIED_NCI_PMEM3_ID, + VIED_NCI_N_MEM_ID +} vied_nci_mem_ID_t; + +/* + * Memory types + */ +typedef enum { + VIED_NCI_GMEM_TYPE_ID = 0, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID +} vied_nci_mem_type_ID_t; + +/* Excluding PMEM */ +#define VIED_NCI_N_DATA_MEM_TYPE_ID (VIED_NCI_N_MEM_TYPE_ID - 1) + +#define VIED_NCI_N_SP_CTRL_MEM 2 +#define VIED_NCI_N_SP_SERVER_MEM 2 +#define VIED_NCI_N_VP_MEM 4 +#define VIED_NCI_N_ACC_PSA_MEM 0 +#define VIED_NCI_N_ACC_ISA_MEM 0 +#define VIED_NCI_N_ACC_OSA_MEM 0 + +#define VIED_NCI_N_VP_CELL 4 +#define VIED_NCI_N_ACC_CELL 8 + +/* + * Device IDs + */ +typedef enum { + VIED_NCI_DEV_CHN_DMA_EXT0_ID = 0, + VIED_NCI_DEV_CHN_GDC_ID, + VIED_NCI_DEV_CHN_DMA_EXT1_READ_ID, + VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_ID, + VIED_NCI_DEV_CHN_DMA_INTERNAL_ID, + VIED_NCI_DEV_CHN_DMA_IPFD_ID, + VIED_NCI_DEV_CHN_DMA_ISA_ID, + VIED_NCI_DEV_CHN_DMA_FW_ID, + VIED_NCI_N_DEV_CHN_ID +} vied_nci_dev_chn_ID_t; + +typedef enum { + DFM_IS_NOT_AVAILABLE +} vied_nci_dev_dfm_id_t; + +#define VIED_NCI_N_DEV_DFM_ID 0 + + +/* + * Memory size (previously in vied_nci_psys_system.c) + * VMEM: in words, 64 Byte per word. + * BAMEM: in words, 64 Byte per word + * DMEM: in words, 4 Byte per word. + * PMEM: in words, 64 Byte per word. + */ +#define VIED_NCI_GMEM_WORD_SIZE 64 +#define VIED_NCI_DMEM_WORD_SIZE 4 +#define VIED_NCI_VMEM_WORD_SIZE 64 +#define VIED_NCI_BAMEM_WORD_SIZE 64 + +#define VIED_NCI_VMEM0_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM1_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM2_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM3_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM4_MAX_SIZE (0x0800) +#define VIED_NCI_BAMEM0_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM1_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM2_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM3_MAX_SIZE (0x0400) +#define VIED_NCI_DMEM0_MAX_SIZE (0x4000) +#define VIED_NCI_DMEM1_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM2_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM3_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM4_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM5_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM6_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM7_MAX_SIZE (0x1000) +#define VIED_NCI_PMEM0_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM1_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM2_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM3_MAX_SIZE (0x0500) + +/* + * Number of channels per device + */ +#define VIED_NCI_DEV_CHN_DMA_EXT0_MAX_SIZE (30) +#define VIED_NCI_DEV_CHN_GDC_MAX_SIZE (4) +#define VIED_NCI_DEV_CHN_DMA_EXT1_READ_MAX_SIZE (30) +#define VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE (20) +#define VIED_NCI_DEV_CHN_DMA_INTERNAL_MAX_SIZE (2) +#define VIED_NCI_DEV_CHN_DMA_IPFD_MAX_SIZE (5) +#define VIED_NCI_DEV_CHN_DMA_ISA_MAX_SIZE (2) +#define VIED_NCI_DEV_CHN_DMA_FW_MAX_SIZE (1) + +/* + * Storage of the resource and resource type enumerators + */ +#define VIED_NCI_RESOURCE_ID_BITS 8 +typedef uint8_t vied_nci_resource_id_t; + +#define VIED_NCI_RESOURCE_SIZE_BITS 16 +typedef uint16_t vied_nci_resource_size_t; + +#define VIED_NCI_RESOURCE_BITMAP_BITS 32 +typedef uint32_t vied_nci_resource_bitmap_t; + +#define IA_CSS_PROCESS_INVALID_DEPENDENCY ((vied_nci_resource_id_t)(-1)) +#define IA_CSS_PROCESS_INVALID_OFFSET ((vied_nci_resource_size_t)(-1)) +#define IA_CSS_PROCESS_MAX_CELLS 1 + +/* + * Resource specifications + * Note that the FAS uses the terminology local/remote memory. In the PSYS API, + * these are called internal/external memory. + */ + +/* resource spec for internal (local) memory */ +struct vied_nci_resource_spec_int_mem_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_int_mem_s + vied_nci_resource_spec_int_mem_t; + +/* resource spec for external (remote) memory */ +struct vied_nci_resource_spec_ext_mem_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_ext_mem_s + vied_nci_resource_spec_ext_mem_t; + +/* resource spec for device channel */ +struct vied_nci_resource_spec_dev_chn_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_dev_chn_s + vied_nci_resource_spec_dev_chn_t; + +/* resource spec for all contiguous resources */ +struct vied_nci_resource_spec_s { + vied_nci_resource_spec_int_mem_t int_mem[VIED_NCI_N_MEM_TYPE_ID]; + vied_nci_resource_spec_ext_mem_t ext_mem[VIED_NCI_N_DATA_MEM_TYPE_ID]; + vied_nci_resource_spec_dev_chn_t dev_chn[VIED_NCI_N_DEV_CHN_ID]; +}; + +typedef struct vied_nci_resource_spec_s vied_nci_resource_spec_t; + +#ifndef PIPE_GENERATION + +extern const vied_nci_cell_type_ID_t vied_nci_cell_type[VIED_NCI_N_CELL_ID]; +extern const vied_nci_mem_type_ID_t vied_nci_mem_type[VIED_NCI_N_MEM_ID]; +extern const uint16_t vied_nci_N_cell_mem[VIED_NCI_N_CELL_TYPE_ID]; +extern const vied_nci_mem_type_ID_t + vied_nci_cell_mem_type[VIED_NCI_N_CELL_TYPE_ID][VIED_NCI_N_MEM_TYPE_ID]; +extern const vied_nci_mem_ID_t + vied_nci_ext_mem[VIED_NCI_N_MEM_TYPE_ID]; +extern const vied_nci_mem_ID_t + vied_nci_cell_mem[VIED_NCI_N_CELL_ID][VIED_NCI_N_MEM_TYPE_ID]; +extern const uint16_t vied_nci_mem_size[VIED_NCI_N_MEM_ID]; +extern const uint16_t vied_nci_mem_word_size[VIED_NCI_N_DATA_MEM_TYPE_ID]; +extern const uint16_t vied_nci_dev_chn_size[VIED_NCI_N_DEV_CHN_ID]; + +STORAGE_CLASS_INLINE +uint32_t vied_nci_mem_is_ext_type(const vied_nci_mem_type_ID_t mem_type_id) +{ + return((mem_type_id == VIED_NCI_GMEM_TYPE_ID)); +} + +#endif /* PIPE_GENERATION */ + +#endif /* __VIED_NCI_PSYS_RESOURCE_MODEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h new file mode 100644 index 000000000000..5b053a27686b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h @@ -0,0 +1,50 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_DATA_H +#define __IA_CSS_PSYS_SIM_DATA_H + +/*! Set the seed if the random number generator + + @param seed[in] Random number generator seed + */ +extern void ia_css_psys_ran_set_seed(const unsigned int seed); + +/*! Generate a random number of a specified bit depth + + @param bit_depth[in] The number of bits of the random output + + @return out, weight(out) <= bit_depth, 0 on error + */ +extern unsigned int ia_css_psys_ran_var(const unsigned int bit_depth); + +/*! Generate a random number of a specified range + + @param range[in] The range of the random output + + @return 0 <= out < range, 0 on error + */ +extern unsigned int ia_css_psys_ran_val(const unsigned int range); + +/*! Generate a random number in a specified interval + + @param lo[in] The lower bound of the random output range + @param hi[in] The higher bound of the random output range + + @return lo <= out < hi, 0 on error + */ +extern unsigned int ia_css_psys_ran_interval(const unsigned int lo, + const unsigned int hi); + +#endif /* __IA_CSS_PSYS_SIM_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h new file mode 100644 index 000000000000..61095257ec55 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_STORAGE_CLASS_H +#define __IA_CSS_PSYS_SIM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_SIM_INLINE__ +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_SIM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h new file mode 100644 index 000000000000..423ff1980270 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_TRACE_H +#define __IA_CSS_PSYS_SIM_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_SIM_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_SIM_TRACING_OVERRIDE)) + #define PSYS_SIM_TRACE_LEVEL_CONFIG PSYS_SIM_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_SIM_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_SIM_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_SIM_TRACE_METHOD PSYSAPI_TRACE_METHOD + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_SIM_TRACE_LEVEL_INFO PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_SIM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h new file mode 100644 index 000000000000..529bea763cc2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h @@ -0,0 +1,180 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __VIED_NCI_PSYS_SYSTEM_GLOBAL_H +#define __VIED_NCI_PSYS_SYSTEM_GLOBAL_H + +#include +#include "ia_css_base_types.h" +#include "ia_css_psys_sim_storage_class.h" +#include "vied_nci_psys_resource_model.h" + +/* + * Key system types + */ +/* Subsystem internal physical address */ +#define VIED_ADDRESS_BITS 32 + +/* typedef uint32_t vied_address_t; */ + +/* Subsystem internal virtual address */ + +/* Subsystem internal data bus */ +#define VIED_DATA_BITS 32 +typedef uint32_t vied_data_t; + +#define VIED_NULL ((vied_vaddress_t)0) + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bit_mask( + const unsigned index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_empty( + const vied_nci_resource_bitmap_t bitmap); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bit_set_in_bitmap( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +int vied_nci_bitmap_compute_weight( + const vied_nci_resource_bitmap_t bitmap); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_union( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_intersection( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_xor( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitfield_mask( + const unsigned int position, + const unsigned int size); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set_bitfield( +const vied_nci_resource_bitmap_t bitmap, +const unsigned int index, +const unsigned int size); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bit_mask_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const unsigned index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_cell_bit_mask( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_barrier_bit_mask( + const vied_nci_barrier_ID_t barrier_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_cell_type_ID_t vied_nci_cell_get_type( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_mem_get_type( + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_mem_get_size( + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_dev_chn_get_size( + const vied_nci_dev_chn_ID_t dev_chn_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_cell_of_type( + const vied_nci_cell_ID_t cell_id, + const vied_nci_cell_type_ID_t cell_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_mem_of_type( + const vied_nci_mem_ID_t mem_id, + const vied_nci_mem_type_ID_t mem_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_cell_mem_of_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index, + const vied_nci_mem_type_ID_t mem_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_has_cell_mem_of_id( + const vied_nci_cell_ID_t cell_id, + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_cell_get_mem_count( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_cell_get_mem_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_ID_t vied_nci_cell_get_mem( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_cell_type_get_mem_type( + const vied_nci_cell_type_ID_t cell_type_id, + const uint16_t mem_index); + +#ifdef __IA_CSS_PSYS_SIM_INLINE__ +#include "psys_system_global_impl.h" +#endif /* __IA_CSS_PSYS_SIM_INLINE__ */ + +#endif /* __VIED_NCI_PSYS_SYSTEM_GLOBAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c new file mode 100644 index 000000000000..6dccac823871 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c @@ -0,0 +1,91 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include + +#include "ia_css_psys_sim_trace.h" + +static unsigned int ia_css_psys_ran_seed; + +void ia_css_psys_ran_set_seed(const unsigned int seed) +{ + ia_css_psys_ran_seed = seed; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_psys_ran_set_seed(): enter:\n"); + +} + +static unsigned int ia_css_psys_ran_int (void) +{ + ia_css_psys_ran_seed = 1664525UL * ia_css_psys_ran_seed + 1013904223UL; + return ia_css_psys_ran_seed; +} + +unsigned int ia_css_psys_ran_var(const unsigned int bit_depth) +{ + unsigned int out; + unsigned int tmp; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "ia_css_psys_ran_var(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if (bit_depth > 32) + out = tmp; + else if (bit_depth == 0) + out = 0; + else + out = (unsigned short)(tmp >> (32 - bit_depth)); + + return out; +} + +unsigned int ia_css_psys_ran_val(const unsigned int range) +{ + unsigned int out; + unsigned int tmp; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "ia_css_psys_ran_val(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if (range > 1) + out = tmp % range; + else + out = 0; + + return out; +} + +unsigned int ia_css_psys_ran_interval(const unsigned int lo, + const unsigned int hi) +{ + unsigned int out; + unsigned int tmp; + unsigned int range = hi - lo; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_psys_ran_interval(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if ((range > 1) && (lo < hi)) + out = lo + (tmp % range); + else + out = 0; + + return out; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h new file mode 100644 index 000000000000..ff51175548ec --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h @@ -0,0 +1,485 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PSYS_SYSTEM_GLOBAL_IMPL_H +#define __PSYS_SYSTEM_GLOBAL_IMPL_H + +#include + +#include "ia_css_psys_sim_trace.h" +#include + +/* Use vied_bits instead, however for test purposes we uses explicit type + * checking + */ +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bit_mask( + const unsigned int index) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bit_mask(): enter:\n"); + + if (index < VIED_NCI_RESOURCE_BITMAP_BITS) + bit_mask = (vied_nci_resource_bitmap_t)1 << index; + + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bitmap_set(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return bitmap | bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_clear(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return bitmap & (~bit_mask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitfield_mask( + const unsigned int position, + const unsigned int size) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + vied_nci_resource_bitmap_t ones = (vied_nci_resource_bitmap_t)-1; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitfield_mask(): enter:\n"); + + if (position < VIED_NCI_RESOURCE_BITMAP_BITS) + bit_mask = (ones >> (sizeof(vied_nci_resource_bitmap_t) - size)) << position; + + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set_bitfield( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index, + const unsigned int size) +{ + vied_nci_resource_bitmap_t ret = 0; + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bit_mask_set_bitfield(): enter:\n"); + + bit_mask = vied_nci_bitfield_mask(index, size); + ret = vied_nci_bitmap_set(bitmap, bit_mask); + + return ret; +} + + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + vied_nci_resource_bitmap_t ret = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_set_unique(): enter:\n"); + + if ((bitmap & bit_mask) == 0) + ret = bitmap | bit_mask; + + return ret; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bit_mask_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index) +{ + vied_nci_resource_bitmap_t ret = 0; + vied_nci_resource_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bit_mask_set_unique(): enter:\n"); + + bit_mask = vied_nci_bit_mask(index); + + if (((bitmap & bit_mask) == 0) && (bit_mask != 0)) + ret = bitmap | bit_mask; + + return ret; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_empty( + const vied_nci_resource_bitmap_t bitmap) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_empty(): enter:\n"); + + return (bitmap == 0); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_set(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return !vied_nci_is_bitmap_clear(bitmap, bit_mask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bit_set_in_bitmap( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index) +{ + + vied_nci_resource_bitmap_t bitmask; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bit_set_in_bitmap(): enter:\n"); + bitmask = vied_nci_bit_mask(index); + return vied_nci_is_bitmap_set(bitmap, bitmask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_clear(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return ((bitmap & bit_mask) == 0); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +int vied_nci_bitmap_compute_weight( + const vied_nci_resource_bitmap_t bitmap) +{ + vied_nci_resource_bitmap_t loc_bitmap = bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_compute_weight(): enter:\n"); + + /* Do not need the iterator "i" */ + for (i = 0; (i < VIED_NCI_RESOURCE_BITMAP_BITS) && + (loc_bitmap != 0); i++) { + weight += loc_bitmap & 0x01; + loc_bitmap >>= 1; + } + + return weight; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_union( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_union(): enter:\n"); + return (bitmap0 | bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_intersection( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_kernel_bitmap_intersection(): enter:\n"); + return (bitmap0 & bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_xor( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bitmap_xor(): enter:\n"); + return (bitmap0 ^ bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_cell_bit_mask( + const vied_nci_cell_ID_t cell_id) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_bit_mask(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (cell_id < VIED_NCI_RESOURCE_BITMAP_BITS)) { + bit_mask = (vied_nci_resource_bitmap_t)1 << cell_id; + } + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_barrier_bit_mask( + const vied_nci_barrier_ID_t barrier_id) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_barrier_bit_mask(): enter:\n"); + + if ((barrier_id < VIED_NCI_N_BARRIER_ID) && + ((barrier_id + VIED_NCI_N_CELL_ID) < VIED_NCI_RESOURCE_BITMAP_BITS)) { + bit_mask = (vied_nci_resource_bitmap_t)1 << + (barrier_id + VIED_NCI_N_CELL_ID); + } + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_cell_type_ID_t vied_nci_cell_get_type( + const vied_nci_cell_ID_t cell_id) +{ + vied_nci_cell_type_ID_t cell_type = VIED_NCI_N_CELL_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_type(): enter:\n"); + + if (cell_id < VIED_NCI_N_CELL_ID) { + cell_type = vied_nci_cell_type[cell_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_cell_get_type(): invalid argument\n"); + } + + return cell_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_mem_get_type( + const vied_nci_mem_ID_t mem_id) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_mem_get_type(): enter:\n"); + + if (mem_id < VIED_NCI_N_MEM_ID) { + mem_type = vied_nci_mem_type[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_mem_get_type(): invalid argument\n"); + } + + return mem_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_mem_get_size( + const vied_nci_mem_ID_t mem_id) +{ + uint16_t mem_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_mem_get_size(): enter:\n"); + + if (mem_id < VIED_NCI_N_MEM_ID) { + mem_size = vied_nci_mem_size[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_mem_get_size(): invalid argument\n"); + } + + return mem_size; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_dev_chn_get_size( + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + uint16_t dev_chn_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_dev_chn_get_size(): enter:\n"); + + if (dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + dev_chn_size = vied_nci_dev_chn_size[dev_chn_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_dev_chn_get_size(): invalid argument\n"); + } + + return dev_chn_size; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_cell_of_type( + const vied_nci_cell_ID_t cell_id, + const vied_nci_cell_type_ID_t cell_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_cell_of_type(): enter:\n"); + + return ((vied_nci_cell_get_type(cell_id) == + cell_type_id) && (cell_type_id != + VIED_NCI_N_CELL_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_mem_of_type( + const vied_nci_mem_ID_t mem_id, + const vied_nci_mem_type_ID_t mem_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_mem_of_type(): enter:\n"); + + return ((vied_nci_mem_get_type(mem_id) == mem_type_id) && + (mem_type_id != VIED_NCI_N_MEM_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_cell_mem_of_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index, + const vied_nci_mem_type_ID_t mem_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_cell_mem_of_type(): enter:\n"); + + return ((vied_nci_cell_get_mem_type(cell_id, mem_index) == mem_type_id) + && (mem_type_id != VIED_NCI_N_MEM_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_has_cell_mem_of_id( + const vied_nci_cell_ID_t cell_id, + const vied_nci_mem_ID_t mem_id) +{ + uint16_t mem_index; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_has_cell_mem_of_id(): enter:\n"); + + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + if ((vied_nci_cell_get_mem(cell_id, mem_index) == mem_id) && + (mem_id != VIED_NCI_N_MEM_ID)) { + break; + } + } + + return (mem_index < VIED_NCI_N_MEM_TYPE_ID); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_cell_get_mem_count( + const vied_nci_cell_ID_t cell_id) +{ + uint16_t mem_count = 0; + vied_nci_cell_type_ID_t cell_type; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem_count(): enter:\n"); + + cell_type = vied_nci_cell_get_type(cell_id); + + if (cell_type < VIED_NCI_N_CELL_TYPE_ID) + mem_count = vied_nci_N_cell_mem[cell_type]; + + return mem_count; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_cell_get_mem_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem_type(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_type = vied_nci_cell_mem_type[ + vied_nci_cell_get_type(cell_id)][mem_index]; + } + + return mem_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_ID_t vied_nci_cell_get_mem( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index) +{ + vied_nci_mem_ID_t mem_id = VIED_NCI_N_MEM_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_id = vied_nci_cell_mem[cell_id][mem_index]; + } + + return mem_id; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_cell_type_get_mem_type( + const vied_nci_cell_type_ID_t cell_type_id, + const uint16_t mem_index) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_type_get_mem_type(): enter:\n"); + + if ((cell_type_id < VIED_NCI_N_CELL_TYPE_ID) + && (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_type = vied_nci_cell_mem_type[cell_type_id][mem_index]; + } + + return mem_type; +} + +#endif /* __PSYS_SYSTEM_GLOBAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c new file mode 100644 index 000000000000..b0e0aebb6e77 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c @@ -0,0 +1,26 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_sim_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifdef __IA_CSS_PSYS_SIM_INLINE__ +STORAGE_CLASS_INLINE int +__ia_css_psys_system_global_avoid_warning_on_empty_file(void) { return 0; } +#else /* __IA_CSS_PSYS_SIM_INLINE__ */ +#include "psys_system_global_impl.h" +#endif /* __IA_CSS_PSYS_SIM_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h new file mode 100644 index 000000000000..4a2f96e9405e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_MANIFEST_TYPES_H +#define __IA_CSS_PSYS_MANIFEST_TYPES_H + +/*! \file */ + +/** @file ia_css_psys_manifest_types.h + * + * The types belonging to the terminal/program/ + * program group manifest static module + */ + +#include +#include "vied_nci_psys_resource_model.h" + + +/* This value is used in the manifest to indicate that the resource + * offset field must be ignored and the resource is relocatable + */ +#define IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE ((vied_nci_resource_size_t)(-1)) + +/* + * Connection type defining the interface source/sink + * + * Note that the connection type does not define the + * real-time configuration of the system, i.e. it + * does not describe whether a source and sink + * program group or sub-system operate synchronously + * that is a program script property {online, offline} + * (see FAS 5.16.3) + */ +#define IA_CSS_CONNECTION_BITMAP_BITS 8 +typedef uint8_t ia_css_connection_bitmap_t; + +#define IA_CSS_CONNECTION_TYPE_BITS 32 +typedef enum ia_css_connection_type { + /**< The terminal is in DDR */ + IA_CSS_CONNECTION_MEMORY = 0, + /**< The terminal is a (watermark) queued stream over DDR */ + IA_CSS_CONNECTION_MEMORY_STREAM, + /* The terminal is a device port */ + IA_CSS_CONNECTION_STREAM, + IA_CSS_N_CONNECTION_TYPES +} ia_css_connection_type_t; + +#define IA_CSS_PROGRAM_TYPE_BITS 32 +typedef enum ia_css_program_type { + IA_CSS_PROGRAM_TYPE_SINGULAR = 0, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER, + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER, + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB, + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER, +/* + * Future extension; A bitmap coding starts making more sense + * + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB_PARALLEL_SUPER, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER_PARALLEL_SUPER, + */ + IA_CSS_N_PROGRAM_TYPES +} ia_css_program_type_t; + +#define IA_CSS_PROGRAM_GROUP_ID_BITS 32 +typedef uint32_t ia_css_program_group_ID_t; +#define IA_CSS_PROGRAM_ID_BITS 32 +typedef uint32_t ia_css_program_ID_t; + +#define IA_CSS_PROGRAM_INVALID_ID ((uint32_t)(-1)) +#define IA_CSS_PROGRAM_GROUP_INVALID_ID ((uint32_t)(-1)) + +typedef struct ia_css_program_group_manifest_s +ia_css_program_group_manifest_t; +typedef struct ia_css_program_manifest_s +ia_css_program_manifest_t; +typedef struct ia_css_data_terminal_manifest_s +ia_css_data_terminal_manifest_t; + +/* ============ Program Control Init Terminal Manifest - START ============ */ +typedef struct ia_css_program_control_init_manifest_program_desc_s + ia_css_program_control_init_manifest_program_desc_t; + +typedef struct ia_css_program_control_init_terminal_manifest_s + ia_css_program_control_init_terminal_manifest_t; +/* ============ Program Control Init Terminal Manifest - END ============ */ + +#endif /* __IA_CSS_PSYS_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h new file mode 100644 index 000000000000..ee8321ea1f12 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h @@ -0,0 +1,311 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H + +#include "ia_css_psys_static_storage_class.h" + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.h + * + * Define the methods on the program group manifest object that are not part of + * a single interface + */ + +#include + +#include /* uint8_t */ + +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ +#include "ia_css_terminal_manifest.h" +#include "ia_css_rbm_manifest_types.h" + +#define IA_CSS_PROGRAM_GROUP_INVALID_ALIGNMENT ((uint8_t)(-1)) + +/*! Get the stored size of the program group manifest object + + @param manifest[in] program group manifest object + + @return size, 0 on invalid argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +size_t ia_css_program_group_manifest_get_size( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the program group ID of the program group manifest object + + @param manifest[in] program group manifest object + + @return program group ID, IA_CSS_PROGRAM_GROUP_INVALID_ID on invalid argument +*/ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_group_ID_t +ia_css_program_group_manifest_get_program_group_ID( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the program group ID of the program group manifest object + + @param manifest[in] program group manifest object + + @param program group ID + + @return 0 on success, -1 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +int ia_css_program_group_manifest_set_program_group_ID( + ia_css_program_group_manifest_t *manifest, + ia_css_program_group_ID_t id); + +/*! Get the storage alignment constraint of the program group binary data + + @param manifest[in] program group manifest object + + @return alignment, IA_CSS_PROGRAM_GROUP_INVALID_ALIGNMENT on invalid manifest + argument +*/ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_alignment( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the storage alignment constraint of the program group binary data + + @param manifest[in] program group manifest object + @param alignment[in] alignment desired + + @return < 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +int ia_css_program_group_manifest_set_alignment( + ia_css_program_group_manifest_t *manifest, + const uint8_t alignment); + +/*! Get the kernel enable bitmap of the program group + + @param manifest[in] program group manifest object + + @return bitmap, 0 on invalid manifest argument + */ +extern ia_css_kernel_bitmap_t +ia_css_program_group_manifest_get_kernel_bitmap( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the kernel enable bitmap of the program group + + @param manifest[in] program group manifest object + @param kernel bitmap[in] kernel enable bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_group_manifest_set_kernel_bitmap( + ia_css_program_group_manifest_t *manifest, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the number of programs in the program group manifest object + + @param manifest[in] program group manifest object + + @return program count, 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_program_count( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the number of terminals in the program group manifest object + + @param manifest[in] program group manifest object + + @return terminal count, 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_terminal_count( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) private data blob in the manifest + + @param manifest[in] program group manifest object + + @return private data blob, NULL on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +void *ia_css_program_group_manifest_get_private_data( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) routing bitmap (rbm) manifest + + @param manifest[in] program group manifest object + + @return rbm manifest, NULL on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_rbm_manifest_t * +ia_css_program_group_manifest_get_rbm_manifest( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) indexed program manifest in the program group manifest + * object + + @param manifest[in] program group manifest object + @param program_index[in] index of the program manifest object + + @return program manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_manifest_t * +ia_css_program_group_manifest_get_prgrm_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int program_index); + +/*! Get the (pointer to) indexed terminal manifest in the program group + * manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_terminal_manifest_t * +ia_css_program_group_manifest_get_term_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed data terminal manifest in the program group + * manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return data terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_data_terminal_manifest_t * +ia_css_program_group_manifest_get_data_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed parameter terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return parameter terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_param_terminal_manifest_t * +ia_css_program_group_manifest_get_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed spatial param terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return spatial param terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_spatial_param_terminal_manifest_t * +ia_css_program_group_manifest_get_spatial_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed sliced param terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return sliced param terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_sliced_param_terminal_manifest_t * +ia_css_program_group_manifest_get_sliced_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed program terminal manifest in the program group + * manifest object + + @parammanifest[in]program group manifest object + @paramprogram_index[in]index of the terminal manifest object + + @return program terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_terminal_manifest_t * +ia_css_program_group_manifest_get_program_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! initialize program group manifest + + @param manifest[in] program group manifest object + @param program_count[in] number of programs. + @param terminal_count[in] number of terminals. + @param program_deps[in] program dependencies for programs in pg. + @param terminal_deps[in] terminal dependencies for programs in pg. + @param terminal_type[in] array of terminal types, binary specific + static frame data + @param cached_in_param_section_count[in]Number of parameter terminal sections + @param cached_out_param_section_count[in] Number of parameter out terminal + @param spatial_param_section_count[in] Array[spatial_terminal_count] + with sections per cached out + terminal + @param sliced_in_param_section_count[in] Array[sliced_in_terminal_count] + with sections per sliced in + terminal + @param sliced_out_param_section_count[in] Array[sliced_out_terminal_count] + with sections per sliced out + terminal + @param fragment_param_section_count[in] Number of fragment parameter + sections of the program init + terminal, + @param kernel_fragment_seq_count[in] Number of kernel fragment + seqence info. + @param progctrlinit_load_section_counts[in] Number of progctrinit load + sections (size of array is program_count) + @param progctrlinit_connect_section_counts[in] Number of progctrinit connect + sections (size of array is program_count) + @return none; + */ +extern void ia_css_program_group_manifest_init( + ia_css_program_group_manifest_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependencies, + const uint8_t *terminal_dependencies, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_in_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts); + +#ifdef __IA_CSS_PSYS_STATIC_INLINE__ +#include "ia_css_psys_program_group_manifest_impl.h" +#endif /* __IA_CSS_PSYS_STATIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h new file mode 100644 index 000000000000..ce802ff5dd8d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h @@ -0,0 +1,69 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.hsys.user.h + * + * Define the methods on the program group manifest object: Hsys user interface + */ + +#include + +#include /* bool */ + +/*! Print the program group manifest object to file/stream + + @param manifest[in] program group manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_manifest_print( + const ia_css_program_group_manifest_t *manifest, + void *fid); + +/*! Read the program group manifest object from file/stream + + @param fid[in] file/stream handle + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_read( + void *fid); + +/*! Write the program group manifest object to file/stream + + @param manifest[in] program group manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_manifest_write( + const ia_css_program_group_manifest_t *manifest, + void *fid); + +/*! Boolean test if the program group manifest is valid + + @param manifest[in] program group manifest + + @return true if program group manifest is correct, false on error + */ +extern bool ia_css_is_program_group_manifest_valid( + const ia_css_program_group_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h new file mode 100644 index 000000000000..242f02108dd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h @@ -0,0 +1,127 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.sim.h + * + * Define the methods on the program group manifest object: Simulation only + */ + +#include + +#include /* uint8_t */ +#include "ia_css_terminal_defs.h" + +/*! Create a program group manifest object from specification + + @param specification[in] specification (index) + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_create( + const unsigned int specification); + +/*! Destroy the program group manifest object + + @param manifest[in] program group manifest + + @return NULL + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_destroy( + ia_css_program_group_manifest_t *manifest); + +/*! Compute the size of storage required for allocating + * the program group (PG) manifest object + + @param program_count[in] Number of programs in the PG + @param terminal_count[in] Number of terminals on the PG + @param program_dependency_count[in] Array[program_count] with the PG + @param terminal_dependency_count[in] Array[program_count] with the + terminal dependencies + @param terminal_type[in] Array[terminal_count] with the + terminal type + @param cached_in_param_section_count[in] Number of parameter + in terminal sections + @param cached_out_param_section_count[in] Number of parameter + out terminal sections + @param sliced_param_section_count[in] Array[sliced_terminal_count] + with sections per + sliced in terminal + @param sliced_out_param_section_count[in] Array[sliced_terminal_count] + with sections per + sliced out terminal + @param spatial_param_section_count[in] Array[spatial_terminal_count] + with sections per + spatial terminal + @param fragment_param_section_count[in] Number of fragment parameter + sections of the + program init terminal, + @param kernel_fragment_seq_count[in] Number of + kernel_fragment_seq_count. + @param progctrlinit_load_section_counts[in] Number of progctrinit load + sections (size of array is program_count) + @param progctrlinit_connect_section_counts[in] Number of progctrinit connect + sections (size of array is program_count) + @return 0 on error + */ +size_t ia_css_sizeof_program_group_manifest( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts); + +/*! Create (the storage for) the program group manifest object + + @param program_count[in] Number of programs in the program group + @param terminal_count[in] Number of terminals on the program group + @param program_dependency_count[in] Array[program_count] with the + program dependencies + @param terminal_dependency_count[in] Array[program_count] with the + terminal dependencies + @param terminal_type[in] Array[terminal_count] with the + terminal type + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_alloc( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type); + +/*! Free (the storage of) the program group manifest object + + @param manifest[in] program group manifest + + @return NULL + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_free( + ia_css_program_group_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h new file mode 100644 index 000000000000..b7333671ed4f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h @@ -0,0 +1,488 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.h + * + * Define the methods on the program manifest object that are not part of a + * single interface + */ + +#include + +#include /* uint8_t */ + +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ + +/* + * Resources needs + */ +#include + +#define IA_CSS_PROGRAM_INVALID_DEPENDENCY ((uint8_t)(-1)) + +/*! Check if the program manifest object specifies a fixed cell allocation + + @param manifest[in] program manifest object + + @return has_fixed_cell, false on invalid argument + */ +extern bool ia_css_has_program_manifest_fixed_cell( + const ia_css_program_manifest_t *manifest); + +/*! Get the stored size of the program manifest object + + @param manifest[in] program manifest object + + @return size, 0 on invalid argument + */ +extern size_t ia_css_program_manifest_get_size( + const ia_css_program_manifest_t *manifest); + +/*! Get the program ID of the program manifest object + + @param manifest[in] program manifest object + + @return program ID, IA_CSS_PROGRAM_INVALID_ID on invalid argument + */ +extern ia_css_program_ID_t ia_css_program_manifest_get_program_ID( + const ia_css_program_manifest_t *manifest); + +/*! Set the program ID of the program manifest object + + @param manifest[in] program manifest object + + @param program ID + + @return 0 on success, -1 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_program_ID( + ia_css_program_manifest_t *manifest, + ia_css_program_ID_t id); + +/*! Get the (pointer to) the program group manifest parent of the program + * manifest object + + @param manifest[in] program manifest object + + @return the pointer to the parent, NULL on invalid manifest argument + */ +extern ia_css_program_group_manifest_t *ia_css_program_manifest_get_parent( + const ia_css_program_manifest_t *manifest); + +/*! Set the (pointer to) the program group manifest parent of the program + * manifest object + + @param manifest[in] program manifest object + @param program_offset[in] this program's offset from + program_group_manifest's base address. + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_parent_offset( + ia_css_program_manifest_t *manifest, + int32_t program_offset); + +/*! Get the type of the program manifest object + + @param manifest[in] program manifest object + + @return program type, limit value (IA_CSS_N_PROGRAM_TYPES) on invalid manifest + argument +*/ +extern ia_css_program_type_t ia_css_program_manifest_get_type( + const ia_css_program_manifest_t *manifest); + +/*! Set the type of the program manifest object + + @param manifest[in] program manifest object + @param program_type[in] program type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_type( + ia_css_program_manifest_t *manifest, + const ia_css_program_type_t program_type); + +/*! Set the cell id of the program manifest object + + @param manifest[in] program manifest object + @param program_cell_id[in] program cell id + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_cell_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_ID_t cell_id); + +/*! Set the cell type of the program manifest object + + @param manifest[in] program manifest object + @param program_cell_type[in] program cell type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_cell_type_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_type_ID_t cell_type_id); + +/*! Set cells bitmap for the program + + @param manifest[in] program manifest object + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_cells_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get cells bitmap for the program + + @param manifest[in] program manifest object + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_cells_bitmap( + const ia_css_program_manifest_t *manifest); + +/*! Set DFM port bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_dfm_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get bitmap of DFM ports requested for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return DFM port bitmap + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + + +/*! Set active DFM port specification bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_dfm_active_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get active DFM port specification bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_active_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + +/*! Set DFM device relocatability specification for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param is_relocatable[in] 1 if dfm device ports are relocatable, 0 otherwise + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_is_dfm_relocatable( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const uint8_t is_relocatable); + +/*! Get DFM device relocatability specification for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return 1 if dfm device ports are relocatable, 0 otherwise + */ +extern uint8_t ia_css_program_manifest_get_is_dfm_relocatable( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + + +/*! Get the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param int_mem_size[in] internal memory size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_int_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t int_mem_size); + +/*! Get the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param ext_mem_size[in] external memory size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_ext_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_size); + +/*! Get a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Set a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + @param dev_chn_size[in] device channel size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_dev_chn_size( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_size); + +/*! Set a device channel resource (offset) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + @param dev_chn_offset[in] device channel offset + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_dev_chn_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_offset); + + +/*! Set the memory resource (offset) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param ext_mem_offset[in] external memory offset + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_ext_mem_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_offset); + +/*! Get a device channel resource (offset) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return Valid fixed offset (if value is greater or equal to 0) or + IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE if offset + is relocatable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Get the memory resource (offset) specification for a memory + that does not belong to the cell where the program will be mapped. + + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return Valid fixed offset (if value is greater or equal to 0) or + IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE if offset + is relocatable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + + +/*! Get the kernel composition of the program manifest object + + @param manifest[in] program manifest object + + @return bitmap, 0 on invalid arguments + */ +extern ia_css_kernel_bitmap_t ia_css_program_manifest_get_kernel_bitmap( + const ia_css_program_manifest_t *manifest); + +/*! Set the kernel dependency of the program manifest object + + @param manifest[in] program manifest object + @param kernel_bitmap[in] kernel composition bitmap + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_kernel_bitmap( + ia_css_program_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap); + +/*! Get the number of programs this programs depends on from the program group + * manifest object + + @param manifest[in] program manifest object + + @return program dependency count + */ +extern uint8_t ia_css_program_manifest_get_program_dependency_count( + const ia_css_program_manifest_t *manifest); + +/*! Get the index of the program which the programs at this index depends on + from the program manifest object + + @param manifest[in] program manifest object + + @return program dependency, + IA_CSS_PROGRAM_INVALID_DEPENDENCY on invalid arguments + */ +extern uint8_t ia_css_program_manifest_get_program_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index); + +/*! Set the index of the program which the programs at this index depends on + in the program manifest object + + @param manifest[in] program manifest object + + @return program dependency + */ +extern int ia_css_program_manifest_set_program_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t program_dependency, + const unsigned int index); + +/*! Get the number of terminals this programs depends on from the program group + * manifest object + + @param manifest[in] program manifest object + + @return program dependency count + */ +extern uint8_t ia_css_program_manifest_get_terminal_dependency_count( + const ia_css_program_manifest_t *manifest); + +/*! Get the index of the terminal which the programs at this index depends on + from the program manifest object + + @param manifest[in] program manifest object + + @return terminal dependency, IA_CSS_PROGRAM_INVALID_DEPENDENCY on error + */ +uint8_t ia_css_program_manifest_get_terminal_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index); + +/*! Set the index of the terminal which the programs at this index depends on + in the program manifest object + + @param manifest[in] program manifest object + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_terminal_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t terminal_dependency, + const unsigned int index); + +/*! Check if the program manifest object specifies a subnode program + + @param manifest[in] program manifest object + + @return is_subnode, false on invalid argument + */ +extern bool ia_css_is_program_manifest_subnode_program_type( + const ia_css_program_manifest_t *manifest); + +/*! Check if the program manifest object specifies a supernode program + + @param manifest[in] program manifest object + + @return is_supernode, false on invalid argument + */ +extern bool ia_css_is_program_manifest_supernode_program_type( + const ia_css_program_manifest_t *manifest); +/*! Check if the program manifest object specifies a singular program + + @param manifest[in] program manifest object + + @return is_singular, false on invalid argument + */ +extern bool ia_css_is_program_manifest_singular_program_type( + const ia_css_program_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h new file mode 100644 index 000000000000..9d737b75a576 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h @@ -0,0 +1,96 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.hsys.kernel.h + * + * Define the methods on the program manifest object: Hsys kernel interface + */ + +#include + +#include + +#include /* uint8_t */ + +/* + * Resources needs + */ + +/*! Get the cell ID from the program manifest object + + @param manifest[in] program manifest object + + Note: If the cell ID is specified, the program this manifest belongs to + must be mapped on that instance. If the cell ID is invalid (limit value) + then the cell type ID must be specified instead + + @return cell ID, limit value if not specified + */ +extern vied_nci_cell_ID_t ia_css_program_manifest_get_cell_ID( + const ia_css_program_manifest_t *manifest); + +/*! Get the cell type ID from the program manifest object + + @param manifest[in] program manifest object + + Note: If the cell type ID is specified, the program this manifest belongs + to can be mapped on any instance of this clee type. If the cell type ID is + invalid (limit value) then a specific cell ID must be specified instead + + @return cell ID, limit value if not specified + */ +extern vied_nci_cell_type_ID_t ia_css_program_manifest_get_cell_type_ID( + const ia_css_program_manifest_t *manifest); + +/*! Get the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Get the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Get a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h new file mode 100644 index 000000000000..087c84b7106e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h @@ -0,0 +1,38 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.hsys.user.h + * + * Define the methods on the program manifest object: Hsys user interface + */ + +#include + +/*! Print the program manifest object to file/stream + + @param manifest[in] program manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_manifest_print( + const ia_css_program_manifest_t *manifest, + void *fid); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h new file mode 100644 index 000000000000..0c2cef11f30e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h @@ -0,0 +1,61 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.sim.h + * + * Define the methods on the program manifest object: Simulation only + */ + +#include + +#include /* uint8_t */ + +/*! Compute the size of storage required for allocating + * the program manifest object + + @param program_dependency_count[in] Number of programs this one depends on + @param terminal_dependency_count[in] Number of terminals this one depends on + + @return 0 on error + */ +extern size_t ia_css_sizeof_program_manifest( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +/*! Create (the storage for) the program manifest object + + @param program_dependency_count[in] Number of programs this one depends on + @param terminal_dependency_count[in] Number of terminals this one depends on + + @return NULL on error + */ +extern ia_css_program_manifest_t *ia_css_program_manifest_alloc( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +/*! Destroy (the storage of) the program manifest object + + @param manifest[in] program manifest + + @return NULL + */ +extern ia_css_program_manifest_t *ia_css_program_manifest_free( + ia_css_program_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h new file mode 100644 index 000000000000..f3c832b5a4a3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +#define __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_STATIC_INLINE__ +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h new file mode 100644 index 000000000000..7c5612cd0969 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_STATIC_TRACE_H +#define __IA_CSS_PSYS_STATIC_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_STATIC_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_STATIC_TRACING_OVERRIDE)) + #define PSYS_STATIC_TRACE_LEVEL_CONFIG \ + PSYS_STATIC_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_STATIC_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_STATIC_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_STATIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h new file mode 100644 index 000000000000..0fa62b32e1a7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h @@ -0,0 +1,423 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_H + +/*! \file */ + +/** @file ia_css_psys_terminal_manifest.h + * + * Define the methods on the terminal manifest object that are not part of a + * single interface + */ + +#include + +#include + +#include + +#include /* ia_css_frame_format_bitmap_t */ +#include /* ia_css_kernel_bitmap_t */ + +#include /* size_t */ +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_manifest_base_types.h" + + +/*! Check if the terminal manifest object specifies a spatial param terminal + * type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_spatial_parameter_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a program terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_program_terminal( + const ia_css_terminal_manifest_t *manifest); + + +/*! Check if the terminal manifest object specifies a program control init terminal type + * + * @param manifest[in] terminal manifest object + * + * @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_program_control_init_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a (cached) parameter + * terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_parameter_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a (sliced) parameter + * terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_sliced_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a data terminal type + + @param manifest[in] terminal manifest object + + @return is_data_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_data_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the stored size of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return size, 0 on invalid manifest argument + */ +extern size_t ia_css_terminal_manifest_get_size( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the (pointer to) the program group manifest parent of the terminal + * manifest object + + @param manifest[in] terminal manifest object + + @return the pointer to the parent, NULL on invalid manifest argument + */ +extern ia_css_program_group_manifest_t *ia_css_terminal_manifest_get_parent( + const ia_css_terminal_manifest_t *manifest); + +/*! Set the (pointer to) the program group manifest parent of the terminal + * manifest object + + @param manifest[in] terminal manifest object + @param terminal_offset[in] this terminal's offset from + program_group_manifest base address. + + @return < 0 on invalid arguments + */ +extern int ia_css_terminal_manifest_set_parent_offset( + ia_css_terminal_manifest_t *manifest, + int32_t terminal_offset); + +/*! Get the type of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return terminal type, limit value (IA_CSS_N_TERMINAL_TYPES) on invalid + manifest argument +*/ +extern ia_css_terminal_type_t ia_css_terminal_manifest_get_type( + const ia_css_terminal_manifest_t *manifest); + +/*! Set the type of the terminal manifest object + + @param manifest[in] terminal manifest object + @param terminal_type[in] terminal type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_terminal_manifest_set_type( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_type_t terminal_type); + +/*! Set the ID of the terminal manifest object + + @param manifest[in] terminal manifest object + @param ID[in] terminal ID + + @return < 0 on invalid manifest argument + */ +int ia_css_terminal_manifest_set_ID( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_ID_t ID); + +/*! Get the type of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return terminal id, IA_CSS_TERMINAL_INVALID_ID on invalid manifest argument + */ +extern ia_css_terminal_ID_t ia_css_terminal_manifest_get_ID( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the supported frame types of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return frame format bitmap, 0 on invalid manifest argument +*/ +extern ia_css_frame_format_bitmap_t + ia_css_data_terminal_manifest_get_frame_format_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the chosen frame type for the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param bitmap[in] frame format bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_frame_format_bitmap( + ia_css_data_terminal_manifest_t *manifest, + ia_css_frame_format_bitmap_t bitmap); + +/*! Check if the (data) terminal manifest object supports compression + + @param manifest[in] (data) terminal manifest object + + @return compression_support, true if compression is supported + */ +extern bool ia_css_data_terminal_manifest_can_support_compression( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the compression support feature of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param compression_support[in] set true to support compression + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_compression_support( + ia_css_data_terminal_manifest_t *manifest, + bool compression_support); + +/*! Set the supported connection types of the terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param bitmap[in] connection bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_connection_bitmap( + ia_css_data_terminal_manifest_t *manifest, ia_css_connection_bitmap_t bitmap); + +/*! Get the connection bitmap of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return connection bitmap, 0 on invalid manifest argument +*/ +extern ia_css_connection_bitmap_t + ia_css_data_terminal_manifest_get_connection_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Get the kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return kernel bitmap, 0 on invalid manifest argument + */ +extern ia_css_kernel_bitmap_t ia_css_data_terminal_manifest_get_kernel_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param kernel_bitmap[in] kernel dependency bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_kernel_bitmap( + ia_css_data_terminal_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap); + +/*! Set the unique kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param index[in] kernel dependency bitmap index + + @return < 0 on invalid argument(s) + */ +extern int ia_css_data_terminal_manifest_set_kernel_bitmap_unique( + ia_css_data_terminal_manifest_t *manifest, + const unsigned int index); + +/*! Set the min size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_min_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the max size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_max_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the min size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_min_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the max size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_max_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the min fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_min_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the max fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_max_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the min fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_min_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the max fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_max_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! + * Get the program control init connect section count for program prog. + * @param prog[in] program control init terminal program desc + * @return number of connect section for program prog. + */ + +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_connect_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog); + + +/*! + * Get the program control init load section count for program prog. + * @param prog[in] program control init terminal program desc + * @return number of load section for program prog. + */ + +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_load_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog); + +/*! + * Get the program control init terminal manifest size. + * @param nof_programs[in] Number of programs. + * @param nof_load_sections[in] Array of size nof_programs, + * encoding the number of load sections. + * @param nof_connect_sections[in] Array of size nof_programs, + * encoding the number of connect sections. + * @return < 0 on invalid manifest argument + */ +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_size( + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections); + +/*! + * Get the program control init terminal manifest program desc. + * @param terminal[in] Program control init terminal. + * @param program[in] Number of programs. + * @return program control init terminal program desc (or NULL if error). + */ +extern +ia_css_program_control_init_manifest_program_desc_t * +ia_css_program_control_init_terminal_manifest_get_program_desc( + const ia_css_program_control_init_terminal_manifest_t *terminal, + unsigned int program); + +/*! + * Initialize the program control init terminal manifest. + * @param nof_programs[in] Number of programs + * @param nof_load_sections[in] Array of size nof_programs, + * encoding the number of load sections. + * @param nof_connect_sections[in] Array of size nof_programs, + * encoding the number of connect sections. + * @return < 0 on invalid manifest argument + */ +extern +int ia_css_program_control_init_terminal_manifest_init( + ia_css_program_control_init_terminal_manifest_t *terminal, + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections); + +/*! + * Pretty prints the program control init terminal manifest. + * @param terminal[in] Program control init terminal. + */ +extern +void ia_css_program_control_init_terminal_manifest_print( + ia_css_program_control_init_terminal_manifest_t *terminal); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h new file mode 100644 index 000000000000..1d2f06f3cbce --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h @@ -0,0 +1,38 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_terminal.hsys.user.h + * + * Define the methods on the termianl manifest object: Hsys user interface + */ + +#include + +/*! Print the terminal manifest object to file/stream + + @param manifest[in] terminal manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_terminal_manifest_print( + const ia_css_terminal_manifest_t *manifest, + void *fid); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h new file mode 100644 index 000000000000..f7da810d82f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h @@ -0,0 +1,48 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_terminal_manifest.sim.h + * + * Define the methods on the terminal manifest object: Simulation only + */ + +#include /* size_t */ +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_defs.h" + +/*! Create (the storage for) the terminal manifest object + + @param terminal_type[in] type of the terminal manifest {parameter, data} + + @return NULL on error + */ +extern ia_css_terminal_manifest_t *ia_css_terminal_manifest_alloc( + const ia_css_terminal_type_t terminal_type); + +/*! Destroy (the storage of) the terminal manifest object + + @param manifest[in] terminal manifest + + @return NULL + */ +extern ia_css_terminal_manifest_t *ia_css_terminal_manifest_free( + ia_css_terminal_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c new file mode 100644 index 000000000000..5af4de746310 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c @@ -0,0 +1,1038 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_static_storage_class.h" +#include "ia_css_psys_program_group_manifest.h" +#include "ia_css_rbm_manifest.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_STATIC_INLINE__ +#include "ia_css_psys_program_group_manifest_impl.h" +#endif /* __IA_CSS_PSYS_STATIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* + * We need to refactor those files in order to + * build in the firmware only what is needed, + * switches are put current to workaround compilation problems + * in the firmware (for example lack of uint64_t support) + * supported in the firmware + */ +#if !defined(__HIVECC) +size_t ia_css_sizeof_program_group_manifest( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts) +{ + size_t size = 0; + int i = 0; + int j = 0; + int m = 0; + int n = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_sizeof_program_group_manifest(): enter:\n"); + + verifexit(program_count != 0); + verifexit(program_dependency_count != NULL); + verifexit(terminal_dependency_count != NULL); + + size += sizeof(ia_css_program_group_manifest_t); + + /* Private payload in the program group manifest */ + size += ceil_mul(sizeof(struct ia_css_psys_private_pg_data), + sizeof(uint64_t)); + /* RBM manifest in the program group manifest */ + size += ceil_mul(sizeof(ia_css_rbm_manifest_t), + sizeof(uint64_t)); + + for (i = 0; i < (int)program_count; i++) { + size += ia_css_sizeof_program_manifest( + program_dependency_count[i], + terminal_dependency_count[i]); + } + + for (i = 0; i < (int)terminal_count; i++) { + switch (terminal_type[i]) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + size += ia_css_param_terminal_manifest_get_size( + cached_in_param_section_count); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + size += ia_css_param_terminal_manifest_get_size( + cached_out_param_section_count); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + size += ia_css_spatial_param_terminal_manifest_get_size( + spatial_param_section_count[j]); + j++; + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + size += ia_css_program_terminal_manifest_get_size( + fragment_param_section_count, + kernel_fragment_seq_count); + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + size += ia_css_program_control_init_terminal_manifest_get_size( + program_count, + progctrlinit_load_section_counts, + progctrlinit_connect_section_counts); + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + size += sizeof(ia_css_data_terminal_manifest_t); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + size += ia_css_sliced_param_terminal_manifest_get_size( + sliced_param_section_count[m]); + m++; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + size += ia_css_sliced_param_terminal_manifest_get_size( + sliced_out_param_section_count[n]); + n++; + break; + default: + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_sizeof_program_group_manifest invalid argument\n"); + } + } + +EXIT: + if (0 == program_count || 0 == terminal_count || + NULL == program_dependency_count || + NULL == terminal_dependency_count) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_sizeof_program_group_manifest invalid argument\n"); + } + return size; +} + +/* + * Currently, the design of XNR kernel inside the *_pregdc program group, + * does not fit the exact model as is being asserted on in + * ia_css_is_program_group_manifest_valid. We therefore disable some checks. + * Further investigation is needed to determine whether *_pregdc program group + * can be canged or that the model must be changed. + * #define USE_SIMPLIFIED_GRAPH_MODEL 1 allows multiple programs to be + * connected to the same terminal, and it allows a kernel be mapped over + * multiple programs. + */ +#define USE_SIMPLIFIED_GRAPH_MODEL 1 + +/* + * Model and/or check refinements + * - Parallel programs do not yet have mutual exclusive alternatives + * - The pgram dependencies do not need to be acyclic + * - Parallel programs need to have an equal kernel requirement + */ +bool ia_css_is_program_group_manifest_valid( + const ia_css_program_group_manifest_t *manifest) +{ + int i; + bool is_valid = false; + uint8_t terminal_count; + uint8_t program_count; + ia_css_kernel_bitmap_t total_bitmap; + ia_css_kernel_bitmap_t check_bitmap; + ia_css_kernel_bitmap_t terminal_bitmap; + /* + * Use a standard bitmap type for the minimum logic to check the DAG, + * generic functions can be used for the kernel enable bitmaps; Later + */ + vied_nci_resource_bitmap_t resource_bitmap; + int terminal_bitmap_weight; + bool has_parameter_terminal_in = false; + bool has_parameter_terminal_out = false; + bool has_program_control_init_terminal = false; + bool has_program_terminal = false; + bool has_program_terminal_sequencer_info = false; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_group_manifest_valid(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(ia_css_program_group_manifest_get_size(manifest) != 0); + verifexit(ia_css_program_group_manifest_get_alignment(manifest) != 0); + verifexit(ia_css_program_group_manifest_get_program_group_ID(manifest) != 0); + + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + check_bitmap = ia_css_kernel_bitmap_clear(); + resource_bitmap = vied_nci_bit_mask(VIED_NCI_RESOURCE_BITMAP_BITS); + terminal_bitmap = ia_css_kernel_bitmap_clear(); + + verifexit(program_count != 0); + verifexit(terminal_count != 0); + verifexit(!ia_css_is_kernel_bitmap_empty(total_bitmap)); + verifexit(vied_nci_is_bitmap_empty(resource_bitmap)); + + /* Check the kernel bitmaps for terminals */ + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_manifest_t *terminal_manifest_i = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + bool is_parameter_in = + (IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN == + ia_css_terminal_manifest_get_type( + terminal_manifest_i)); + bool is_parameter_out = + (IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT == + ia_css_terminal_manifest_get_type( + terminal_manifest_i)); + bool is_data = + ia_css_is_terminal_manifest_data_terminal( + terminal_manifest_i); + bool is_program = + ia_css_is_terminal_manifest_program_terminal( + terminal_manifest_i); + bool is_spatial_param = + ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest_i); + bool is_program_control_init = + ia_css_is_terminal_manifest_program_control_init_terminal( + terminal_manifest_i); + + if (is_parameter_in) { + /* + * There can be only one cached in parameter terminal + * it serves kernels, not programs + */ + verifexit(!has_parameter_terminal_in); + has_parameter_terminal_in = is_parameter_in; + } else if (is_parameter_out) { + /* + * There can be only one cached out parameter terminal + * it serves kernels, not programs + */ + verifexit(!has_parameter_terminal_out); + has_parameter_terminal_out = is_parameter_out; + } else if (is_data) { + ia_css_data_terminal_manifest_t *dterminal_manifest_i = + (ia_css_data_terminal_manifest_t *) + terminal_manifest_i; + ia_css_kernel_bitmap_t terminal_bitmap_i = + ia_css_data_terminal_manifest_get_kernel_bitmap( + dterminal_manifest_i); + /* + * A terminal must depend on kernels that are a subset + * of the total, correction, it can only depend on one + * kernel + */ + verifexit(!ia_css_is_kernel_bitmap_empty( + terminal_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, terminal_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_onehot( + terminal_bitmap_i)); + } else if (is_program) { + verifexit(!has_program_terminal); + verifexit(terminal_manifest_i); + has_program_terminal = is_program; + has_program_terminal_sequencer_info = + (((ia_css_program_terminal_manifest_t *) + terminal_manifest_i)-> + kernel_fragment_sequencer_info_manifest_info_count + != 0); + } else if (is_program_control_init) { + has_program_control_init_terminal = is_program_control_init; + } else { + const ia_css_spatial_param_terminal_manifest_t + *spatial_param_man = + (const ia_css_spatial_param_terminal_manifest_t *) + terminal_manifest_i; + verifexit(spatial_param_man); + verifexit(is_spatial_param); + + terminal_bitmap = + ia_css_kernel_bitmap_set(terminal_bitmap, + spatial_param_man->kernel_id); + verifexit(!ia_css_is_kernel_bitmap_empty(terminal_bitmap)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, terminal_bitmap)); + } + } + + /* Check the kernel bitmaps for programs */ + for (i = 0; i < (int)program_count; i++) { + int j; + ia_css_program_manifest_t *program_manifest_i = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_program_type_t program_type_i = + ia_css_program_manifest_get_type(program_manifest_i); + ia_css_kernel_bitmap_t program_bitmap_i = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_i); + uint8_t program_dependency_count_i = + ia_css_program_manifest_get_program_dependency_count( + program_manifest_i); + uint8_t terminal_dependency_count_i = + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest_i); + uint8_t program_dependency_i0 = + ia_css_program_manifest_get_program_dependency( + program_manifest_i, 0); + bool is_sub_i = + ia_css_is_program_manifest_subnode_program_type( + program_manifest_i); + bool is_exclusive_sub_i = + (program_type_i == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB); + bool is_virtual_sub_i = + (program_type_i == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); + bool is_super_i = + ia_css_is_program_manifest_supernode_program_type( + program_manifest_i); + + /* + * A program must have kernels that + * are a subset of the total + */ + verifexit(!ia_css_is_kernel_bitmap_empty( + program_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, program_bitmap_i)); + verifexit((program_type_i != IA_CSS_N_PROGRAM_TYPES)); + verifexit((program_dependency_count_i + terminal_dependency_count_i) != 0); + /* + * Checks for subnodes + * - Parallel subnodes cannot depend on terminals + * - Exclusive subnodes must depend on + * fewer terminals than the supernode + * - Subnodes only depend on a supernode of the same type + * - Must have a subset of the supernode's kernels + * (but not equal) + * - This tests only positive cases + * Checks for singular or supernodes + * - Cannot depend on exclusive subnodes + * - No intersection between kernels + * (too strict for multiple instances ?) + */ + if (is_sub_i) { + /* Subnode */ + ia_css_program_manifest_t *program_manifest_k = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, program_dependency_i0); + ia_css_program_type_t program_type_k = + ia_css_program_manifest_get_type( + program_manifest_k); + ia_css_kernel_bitmap_t program_bitmap_k = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_k); + + verifexit(program_dependency_count_i == 1); + if (is_exclusive_sub_i || is_virtual_sub_i) { + verifexit(terminal_dependency_count_i <= + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest_k)); + } else{ + verifexit(terminal_dependency_count_i == 0); + } + verifexit(program_type_k == + (is_exclusive_sub_i ? + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER : + is_virtual_sub_i ? + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER : + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER)); + verifexit(!ia_css_is_kernel_bitmap_equal( + program_bitmap_k, program_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + program_bitmap_k, program_bitmap_i)); + } else { + /* Singular or Supernode */ + int k; + + for (k = 0; k < program_dependency_count_i; k++) { + uint8_t program_dependency_k = + ia_css_program_manifest_get_program_dependency( + program_manifest_i, k); + ia_css_program_manifest_t *program_manifest_k = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, (int)program_dependency_k); + ia_css_program_type_t program_type_k = + ia_css_program_manifest_get_type( + program_manifest_k); + ia_css_kernel_bitmap_t program_bitmap_k = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_k); + + verifexit(program_dependency_k < + program_count); + verifexit((program_type_k != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_k != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)); +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_k)); +#else + (void)program_bitmap_k; +#endif + } + } + + /* Check for relations */ + for (j = 0; j < (int)program_count; j++) { + int k; + ia_css_program_manifest_t *program_manifest_j = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, j); + ia_css_program_type_t program_type_j = + ia_css_program_manifest_get_type(program_manifest_j); + ia_css_kernel_bitmap_t program_bitmap_j = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_j); + uint8_t program_dependency_count_j = + ia_css_program_manifest_get_program_dependency_count( + program_manifest_j); + uint8_t program_dependency_j0 = + ia_css_program_manifest_get_program_dependency( + program_manifest_j, 0); + bool is_sub_j = + ia_css_is_program_manifest_subnode_program_type( + program_manifest_j); + bool is_super_j = + ia_css_is_program_manifest_supernode_program_type( + program_manifest_j); + bool is_virtual_sub_j = + (program_type_j == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); + bool is_j_subset_i = + ia_css_is_kernel_bitmap_subset( + program_bitmap_i, program_bitmap_j); + bool is_i_subset_j = + ia_css_is_kernel_bitmap_subset( + program_bitmap_j, program_bitmap_i); + + /* Test below would fail for i==j */ + if (i == j) + continue; + + /* Empty sets are always subsets, but meaningless */ + verifexit(!ia_css_is_kernel_bitmap_empty( + program_bitmap_j)); + + /* + * Checks for mutual subnodes + * - Parallel subnodes must have an equal + * set of kernels + * - Exclusive and virtual subnodes must + * have an unequal set of kernels + * Checks for subnodes + * - Subnodes must have a subset of kernels + */ + if (((program_type_i == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB))) { + + verifexit(program_dependency_count_j == 1); + verifexit(program_dependency_i0 != i); + verifexit(program_dependency_j0 != i); + + if (program_dependency_i0 == + program_dependency_j0) { + verifexit(is_sub_i); + /* + * Subnodes are subsets, + * not for virtual nodes + */ + if (!is_virtual_sub_i) + verifexit( + ((is_j_subset_i || + is_i_subset_j))); + /* + * That must be equal for + * parallel subnodes, + * must be unequal for + * exlusive and virtual subnodes + */ + verifexit( + ((is_j_subset_i && is_i_subset_j) ^ + (is_exclusive_sub_i | + is_virtual_sub_i))); + + } + if (is_j_subset_i || is_i_subset_j) { + verifexit(program_dependency_i0 == + program_dependency_j0); + } + } + + if (((program_type_i == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB))) { + + verifexit(program_dependency_count_j == 1); + verifexit(!is_i_subset_j); + + if (program_dependency_j0 == i) { + verifexit(program_dependency_i0 != + program_dependency_j0); + verifexit(is_super_i); + verifexit(is_j_subset_i); + + } + if (is_j_subset_i) { + verifexit(program_dependency_j0 == i); + } + } + + /* + * Checks for dependent nodes + * - Cannot depend on exclusive subnodes + * - No intersection between kernels + * (too strict for multiple instances ?) + * unless a subnode + */ + for (k = 0; k < (int)program_dependency_count_j; k++) { + uint8_t program_dependency_k = + ia_css_program_manifest_get_program_dependency( + program_manifest_j, k); + + verifexit((program_dependency_k < + program_count)); + if (program_dependency_k == i) { + /* program[j] depends on program[i] */ + verifexit((i != j)); + verifexit((program_type_i != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_i != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)); + verifexit(USE_SIMPLIFIED_GRAPH_MODEL || + (ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_j) ^ is_sub_j)); + } + } + + /* + * Checks for supernodes and subnodes + * - Detect nodes that kernel-wise are subsets, + * but not connected to the correct supernode + * - We do not (yet) detect if programs properly + * depend on all parallel nodes + */ + if (!ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_j)) { + /* + * This test will pass if + * the program manifest is NULL, + * but that's no concern here + */ +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(!ia_css_is_program_manifest_singular_program_type( + program_manifest_i)); + verifexit(!ia_css_is_program_manifest_singular_program_type( + program_manifest_j)); + if (!is_virtual_sub_j) + verifexit((is_j_subset_i || is_i_subset_j)); +#else + (void)is_virtual_sub_j; +#endif + if (is_super_i) { + verifexit(is_sub_j); + verifexit(program_dependency_j0 == i); + } + if (is_super_j) { + verifexit(is_sub_i); + verifexit(program_dependency_i0 == j); + } + } + } + check_bitmap = ia_css_kernel_bitmap_union( + check_bitmap, program_bitmap_i); + /* + * A terminal can be bound to only a single + * (of multiple concurrent) program(s), + * i.e. the one that holds the iterator to control it + * Only singular and super nodes can depend on a terminal. + * This loop accumulates all terminal + * dependencies over all programs + */ + for (j = 0; j < (int)terminal_dependency_count_i; j++) { + uint8_t terminal_dependency = + ia_css_program_manifest_get_terminal_dependency( + program_manifest_i, j); + + verifexit(terminal_dependency < terminal_count); + if ((program_type_i != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_i != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)) { + /* If the subnode always came after the */ + /* supernode we could check for presence */ + resource_bitmap = + vied_nci_bit_mask_set_unique( + resource_bitmap, + terminal_dependency); +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(!vied_nci_is_bitmap_empty( + resource_bitmap)); +#endif + } + } + } + verifexit(ia_css_is_kernel_bitmap_equal( + total_bitmap, check_bitmap)); + + terminal_bitmap_weight = + vied_nci_bitmap_compute_weight(resource_bitmap); + verifexit(terminal_bitmap_weight >= 0); + if (has_parameter_terminal_in || + has_parameter_terminal_out || + has_program_terminal || + has_program_control_init_terminal) { + int skip_terminal_count = 0; + + if (has_parameter_terminal_in) + skip_terminal_count++; + if (has_parameter_terminal_out) + skip_terminal_count++; + if (has_program_control_init_terminal) { + skip_terminal_count++; + } + if (has_program_terminal) + skip_terminal_count++; + if (has_program_terminal_sequencer_info) + skip_terminal_count--; +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit((terminal_bitmap_weight == + (terminal_count - skip_terminal_count))); +#endif + } else + verifexit((terminal_bitmap_weight == terminal_count)); + + is_valid = true; +EXIT: + if (is_valid == false) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_is_program_group_manifest_valid: failed\n"); + } + return is_valid; +} + +int ia_css_program_group_manifest_set_kernel_bitmap( + ia_css_program_group_manifest_t *manifest, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_kernel_bitmap invalid argument\n"); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_group_manifest_get_kernel_bitmap( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_kernel_bitmap invalid argument\n"); + } + return bitmap; +} + +void ia_css_program_group_manifest_init( + ia_css_program_group_manifest_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependencies, + const uint8_t *terminal_dependencies, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_in_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts) +{ + int i = 0; + int j = 0; + int m = 0; + int n = 0; + int result; + uint32_t offset = 0; + char *prg_manifest_base, *terminal_manifest_base; + size_t program_size = 0; + + /* + * assert(blob != NULL); + */ + COMPILATION_ERROR_IF( + SIZE_OF_DATA_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_data_terminal_manifest_t))); + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_GROUP_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_group_manifest_t))); + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_manifest_t))); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_group_manifest_init(): enter:\n"); + + for (i = 0; i < (int)program_count; i++) { + program_size += + ia_css_sizeof_program_manifest(program_dependencies[i], + terminal_dependencies[i]); + } + + /* A program group ID cannot be zero */ + blob->ID = 1; + blob->program_count = program_count; + blob->terminal_count = terminal_count; + blob->program_manifest_offset = sizeof(ia_css_program_group_manifest_t); + blob->terminal_manifest_offset = + (uint32_t)blob->program_manifest_offset + program_size; + + prg_manifest_base = (char *) + (((char *)blob) + blob->program_manifest_offset); + offset = blob->program_manifest_offset; + for (i = 0; i < (int)program_count; i++) { + ia_css_program_manifest_init( + (ia_css_program_manifest_t *)prg_manifest_base, + program_dependencies[i], terminal_dependencies[i]); + ia_css_program_manifest_set_parent_offset( + (ia_css_program_manifest_t *)prg_manifest_base, offset); + program_size = + ia_css_sizeof_program_manifest(program_dependencies[i], + terminal_dependencies[i]); + prg_manifest_base += program_size; + offset += (uint32_t)program_size; + } + + offset = blob->terminal_manifest_offset; + terminal_manifest_base = (char *) (((char *)blob) + offset); + for (i = 0; i < (int)terminal_count; i++) { + size_t terminal_size = 0; + ia_css_terminal_manifest_t *term_manifest = + (ia_css_terminal_manifest_t *)terminal_manifest_base; + + ia_css_terminal_manifest_set_parent_offset( + (ia_css_terminal_manifest_t *) + terminal_manifest_base, + offset); + switch (terminal_type[i]) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + result = ia_css_param_terminal_manifest_init( + (ia_css_param_terminal_manifest_t *) + term_manifest, + cached_in_param_section_count); + if (0 == result) { + terminal_size = + ia_css_param_terminal_manifest_get_size( + cached_in_param_section_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init failed in cached in terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + result = ia_css_param_terminal_manifest_init( + (ia_css_param_terminal_manifest_t *) + term_manifest, + cached_out_param_section_count); + if (0 == result) { + terminal_size = + ia_css_param_terminal_manifest_get_size( + cached_out_param_section_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + result = ia_css_spatial_param_terminal_manifest_init( + (ia_css_spatial_param_terminal_manifest_t *) + term_manifest, + spatial_param_section_count[j]); + if (0 == result) { + terminal_size = + ia_css_spatial_param_terminal_manifest_get_size( + spatial_param_section_count[j]); + j++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_spatial_param_terminal_manifest_init failed in spatial terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + result = ia_css_program_terminal_manifest_init( + (ia_css_program_terminal_manifest_t *) + term_manifest, + fragment_param_section_count, + kernel_fragment_seq_count); + if (0 == result) { + terminal_size = + ia_css_program_terminal_manifest_get_size( + fragment_param_section_count, + kernel_fragment_seq_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_program_terminal_manifest_init failed in program terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + result = ia_css_program_control_init_terminal_manifest_init( + (ia_css_program_control_init_terminal_manifest_t *) + term_manifest, + program_count, + progctrlinit_load_section_counts, + progctrlinit_connect_section_counts); + if (0 == result) { + terminal_size = + ia_css_program_control_init_terminal_manifest_get_size( + program_count, + NULL, + NULL); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_program_control_init_terminal_manifest_init failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + terminal_size = sizeof(ia_css_data_terminal_manifest_t); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + result = ia_css_sliced_param_terminal_manifest_init( + (ia_css_sliced_param_terminal_manifest_t *) + term_manifest, + sliced_in_param_section_count[m]); + if (0 == result) { + terminal_size = + ia_css_sliced_param_terminal_manifest_get_size( + sliced_in_param_section_count[m]); + m++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init in sliced terminal failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + result = ia_css_sliced_param_terminal_manifest_init( + (ia_css_sliced_param_terminal_manifest_t *) + term_manifest, + sliced_out_param_section_count[n]); + if (0 == result) { + terminal_size = + ia_css_sliced_param_terminal_manifest_get_size( + sliced_out_param_section_count[n]); + n++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init in sliced out terminal failed\n"); + } + break; + default: + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_init invalid argument\n"); + } + term_manifest->size = (uint16_t)terminal_size; + term_manifest->terminal_type = terminal_type[i]; + terminal_manifest_base += terminal_size; + offset += (uint32_t)terminal_size; + } + + /* Set the private program group manifest blob offset */ + blob->private_data_offset = offset; + offset += ceil_mul(sizeof(struct ia_css_psys_private_pg_data), + sizeof(uint64_t)); + + /* Set the RBM manifest blob offset */ + blob->rbm_manifest_offset = offset; + offset += ceil_mul(sizeof(ia_css_rbm_manifest_t), + sizeof(uint64_t)); + + assert(offset <= UINT16_MAX); + blob->size = (uint16_t)offset; +} + +int ia_css_program_group_manifest_print( + const ia_css_program_group_manifest_t *manifest, + void *fid) +{ + int retval = -1; + int i; + uint8_t program_count, terminal_count; + ia_css_kernel_bitmap_t bitmap; + struct ia_css_psys_private_pg_data *priv_data; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_group_manifest_print(): enter:\n"); + + NOT_USED(fid); + + verifexit(manifest != NULL); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sizeof(manifest) = %d\n", + (int)ia_css_program_group_manifest_get_size(manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "alignment(manifest) = %d\n", + (int)ia_css_program_group_manifest_get_alignment(manifest)); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program group ID = %d\n", + (int)ia_css_program_group_manifest_get_program_group_ID( + manifest)); + + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + bitmap = ia_css_program_group_manifest_get_kernel_bitmap(manifest); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "%d program manifests\n", (int)program_count); + for (i = 0; i < (int)program_count; i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + + retval = ia_css_program_manifest_print(program_manifest, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "%d terminal manifests\n", (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_manifest_t *terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + retval = ia_css_terminal_manifest_print( + terminal_manifest, fid); + verifjmpexit(retval == 0); + } + + priv_data = + (struct ia_css_psys_private_pg_data *) + ia_css_program_group_manifest_get_private_data(manifest); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "private_data_offset %d\n", manifest->private_data_offset); + + for (i = 0; i < IPU_DEVICE_GP_PSA_MUX_NUM_MUX; i++) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "PSA MUX id %d mux val %d\n", i, + priv_data->psa_mux_conf[i]); + + } + + for (i = 0; i < IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX; i++) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "ISA MUX id %d mux val %d\n", i, + priv_data->isa_mux_conf[i]); + + } + + for (i = 0; i < IPU_DEVICE_ACB_NUM_ACB; i++) { + + if (priv_data->acb_route[i].in_select != + NCI_ACB_PORT_INVALID) { + + assert(priv_data->acb_route[i].in_select != + NCI_ACB_PORT_INVALID && + priv_data->acb_route[i].out_select != + NCI_ACB_PORT_INVALID); + + IA_CSS_TRACE_3(PSYSAPI_STATIC, INFO, + "Route Cell id %d In %d Out %d\n", i, + priv_data->acb_route[i].in_select, + priv_data->acb_route[i].out_select); + } + + } + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_base_addr 0x%x\n", + priv_data->input_buffer_info.buffer_base_addr); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: bpe = %d\n", + priv_data->input_buffer_info.bpe); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_width = %d\n", + priv_data->input_buffer_info.buffer_width); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_height = %d\n", + priv_data->input_buffer_info.buffer_height); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: num_of_buffers = %d\n", + priv_data->input_buffer_info.num_of_buffers); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: dfm_port_addr = 0x%x\n", + priv_data->input_buffer_info.dfm_port_addr); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_group_manifest_print failed (%i)\n", + retval); + } + return retval; +} +#endif /* !defined(__HIVECC) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h new file mode 100644 index 000000000000..527b8cc00dd1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h @@ -0,0 +1,415 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H + +#include +#include +#include +#include +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_psys_private_pg_data.h" +#include /* Safer bit mask functions */ +#include "ia_css_psys_static_trace.h" +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +size_t ia_css_program_group_manifest_get_size( + const ia_css_program_group_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_group_ID_t +ia_css_program_group_manifest_get_program_group_ID( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_program_group_ID_t id = IA_CSS_PROGRAM_GROUP_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_group_ID(): enter:\n"); + + if (manifest != NULL) { + id = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_program_group_ID invalid argument\n"); + } + return id; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +int ia_css_program_group_manifest_set_program_group_ID( + ia_css_program_group_manifest_t *manifest, + ia_css_program_group_ID_t id) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_program_group_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = id; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_program_group_ID invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +int ia_css_program_group_manifest_set_alignment( + ia_css_program_group_manifest_t *manifest, + const uint8_t alignment) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_alignment(): enter:\n"); + + if (manifest != NULL) { + manifest->alignment = alignment; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_alignment invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_alignment( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t alignment = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_alignment(): enter:\n"); + + if (manifest != NULL) { + alignment = manifest->alignment; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_alignment invalid argument\n"); + } + return alignment; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +void *ia_css_program_group_manifest_get_private_data( + const ia_css_program_group_manifest_t *manifest) +{ + void *private_data = NULL; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_private_data(%p): enter:\n", + manifest); + + verifexit(manifest != NULL); + + private_data = (void *)((const char *)manifest + + manifest->private_data_offset); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_private_data invalid argument\n"); + } + return private_data; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_rbm_manifest_t *ia_css_program_group_manifest_get_rbm_manifest( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_rbm_manifest_t *rbm_manifest = NULL; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_rbm_manifest(%p): enter:\n", + manifest); + + verifexit(manifest != NULL); + + rbm_manifest = (ia_css_rbm_manifest_t *)((const char *)manifest + + manifest->rbm_manifest_offset); + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_rbm_manifest invalid argument\n"); + } + return rbm_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_manifest_t * +ia_css_program_group_manifest_get_prgrm_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int program_index) +{ + ia_css_program_manifest_t *prg_manifest_base; + uint8_t *program_manifest = NULL; + uint8_t program_count; + unsigned int i; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_prgrm_mnfst(%p,%d): enter:\n", + manifest, program_index); + + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + + verifexit(manifest != NULL); + verifexit(program_index < program_count); + + prg_manifest_base = (ia_css_program_manifest_t *)((char *)manifest + + manifest->program_manifest_offset); + if (program_index < program_count) { + program_manifest = (uint8_t *)prg_manifest_base; + for (i = 0; i < program_index; i++) { + program_manifest += ((ia_css_program_manifest_t *) + program_manifest)->size; + } + } + +EXIT: + if (NULL == manifest || program_index >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_prgrm_mnfst invalid argument\n"); + } + return (ia_css_program_manifest_t *)program_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_data_terminal_manifest_t * +ia_css_program_group_manifest_get_data_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_data_terminal_manifest_t *data_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_data_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_data_terminal(terminal_manifest)); + + data_terminal_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; +EXIT: + return data_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_param_terminal_manifest_t * +ia_css_program_group_manifest_get_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_param_terminal_manifest_t *param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest)); + param_terminal_manifest = + (ia_css_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_spatial_param_terminal_manifest_t * +ia_css_program_group_manifest_get_spatial_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_spatial_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest)); + + spatial_param_terminal_manifest = + (ia_css_spatial_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return spatial_param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_sliced_param_terminal_manifest_t * +ia_css_program_group_manifest_get_sliced_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_sliced_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_sliced_terminal( + terminal_manifest)); + + sliced_param_terminal_manifest = + (ia_css_sliced_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return sliced_param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_terminal_manifest_t * +ia_css_program_group_manifest_get_program_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_program_terminal_manifest_t *program_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_program_terminal( + terminal_manifest)); + + program_terminal_manifest = + (ia_css_program_terminal_manifest_t *)terminal_manifest; + EXIT: + return program_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_terminal_manifest_t * +ia_css_program_group_manifest_get_term_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_terminal_manifest_t *terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest_base; + uint8_t terminal_count; + uint8_t i = 0; + uint32_t offset; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_term_mnfst(%p,%d): enter:\n", + manifest, (int)terminal_index); + + verifexit(manifest != NULL); + + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + verifexit(terminal_index < terminal_count); + + terminal_manifest_base = + (ia_css_terminal_manifest_t *)((char *)manifest + + manifest->terminal_manifest_offset); + terminal_manifest = terminal_manifest_base; + while (i < terminal_index) { + offset = + (uint32_t)ia_css_terminal_manifest_get_size(terminal_manifest); + terminal_manifest = (ia_css_terminal_manifest_t *) + ((char *)terminal_manifest + offset); + i++; + } +EXIT: + return terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_program_count( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_count(): enter:\n"); + + if (manifest != NULL) { + program_count = manifest->program_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_program_count invalid argument\n"); + } + return program_count; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_terminal_count( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_terminal_count(): enter:\n"); + + if (manifest != NULL) { + terminal_count = manifest->terminal_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h new file mode 100644 index 000000000000..502d59def6e9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h @@ -0,0 +1,212 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H + +#include "ia_css_psys_manifest_types.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_kernel_bitmap.h" +#include "ia_css_program_group_data.h" +#include "vied_nci_psys_resource_model.h" +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +#define SIZE_OF_PROGRAM_GROUP_MANIFEST_STRUCT_IN_BITS \ + ((IA_CSS_KERNEL_BITMAP_BITS) \ + + (IA_CSS_PROGRAM_GROUP_ID_BITS) \ + + (5 * IA_CSS_UINT16_T_BITS) \ + + (5 * IA_CSS_UINT8_T_BITS) \ + + (5 * IA_CSS_UINT8_T_BITS)) + +struct ia_css_program_group_manifest_s { + /**< Indicate kernels are present in this program group */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Referral ID to program group FW */ + ia_css_program_group_ID_t ID; + uint16_t program_manifest_offset; + uint16_t terminal_manifest_offset; + /**< Offset to private data (not part of the official API) */ + uint16_t private_data_offset; + /**< Offset to RBM manifest */ + uint16_t rbm_manifest_offset; + /**< Size of this structure */ + uint16_t size; + /**< Storage alignment requirement (in uint8_t) */ + uint8_t alignment; + /**< Total number of kernels in this program group */ + uint8_t kernel_count; + /**< Total number of program in this program group */ + uint8_t program_count; + /**< Total number of terminals on this program group */ + uint8_t terminal_count; + /**< Total number of independent subgraphs in this program group */ + uint8_t subgraph_count; + /**< Padding; esnures that rbm_manifest starts on 64bit alignment */ + uint8_t reserved[5]; +}; + +#define SIZE_OF_PROGRAM_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + IA_CSS_PROGRAM_ID_BITS \ + + IA_CSS_PROGRAM_TYPE_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + IA_CSS_UINT16_T_BITS \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_MEM_TYPE_ID) \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_DATA_MEM_TYPE_ID * 2) \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_DEV_CHN_ID * 2) \ + + (IA_CSS_UINT8_T_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (IA_CSS_PROCESS_MAX_CELLS * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_RESOURCE_ID_BITS) \ + + (2 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST * IA_CSS_UINT8_T_BITS)) +/* + * This structure contains only the information required for resource + * management and construction of the process group. + * The header for the program binary load is separate + */ + +struct ia_css_program_manifest_s { + /**< Indicate which kernels lead to this program being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Referral ID to a specific program FW, valid ID's != 0 */ + ia_css_program_ID_t ID; + /**< Specification of for exclusive or parallel programs */ + ia_css_program_type_t program_type; + /**< offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; + uint32_t program_dependency_offset; + uint32_t terminal_dependency_offset; +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM port allocation of this program */ + vied_nci_resource_bitmap_t dfm_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; + /**< Active DFM ports which need a kick + * If an empty port is configured to run in active mode, the empty + * port and the corresponding full port(s) in the stream must be kicked. + * The empty port must always be kicked aster the full port. + */ + vied_nci_resource_bitmap_t dfm_active_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; +#endif + /**< Size of this structure */ + uint16_t size; + /**< (internal) Memory allocation size needs of this program */ + vied_nci_resource_size_t int_mem_size[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory allocation size needs of this program */ + vied_nci_resource_size_t ext_mem_size[VIED_NCI_N_DATA_MEM_TYPE_ID]; + vied_nci_resource_size_t ext_mem_offset[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Device channel allocation size needs of this program */ + vied_nci_resource_size_t dev_chn_size[VIED_NCI_N_DEV_CHN_ID]; + vied_nci_resource_size_t dev_chn_offset[VIED_NCI_N_DEV_CHN_ID]; +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM ports are relocatable if value is set to 1. + * The flag is per dfm port type. + * This will not be supported for now. + */ + uint8_t is_dfm_relocatable[VIED_NCI_N_DEV_DFM_ID]; +#endif + /** Array of all the cells this program needs */ +#if IA_CSS_PROCESS_MAX_CELLS == 1 + vied_nci_resource_id_t cell_id; +#else + vied_nci_resource_id_t cells[IA_CSS_PROCESS_MAX_CELLS]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + /**< (exclusive) indication of a cell type to be used by this program */ + vied_nci_resource_id_t cell_type_id; + + /**< Number of programs this program depends on */ + uint8_t program_dependency_count; + /**< Number of terminals this program depends on */ + uint8_t terminal_dependency_count; + /**< Padding bytes for 64bit alignment*/ +#if N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST > 0 + /*hivecc does not allow an array of zero length*/ + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST]; +#endif +}; + +/* + *Calculation for manual size check for struct ia_css_data_terminal_manifest_s + */ +#define SIZE_OF_DATA_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + IA_CSS_FRAME_FORMAT_BITMAP_BITS \ + + IA_CSS_CONNECTION_BITMAP_BITS \ + + IA_CSS_KERNEL_BITMAP_BITS \ + + (4 * (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION)) \ + + IA_CSS_UINT16_T_BITS \ + + IA_CSS_UINT8_T_BITS \ + + (4*IA_CSS_UINT8_T_BITS)) +/* + * Inherited data terminal class + */ +struct ia_css_data_terminal_manifest_s { + /**< Data terminal base */ + ia_css_terminal_manifest_t base; + /**< Supported (4CC / MIPI / parameter) formats */ + ia_css_frame_format_bitmap_t frame_format_bitmap; + /**< Indicate which kernels lead to this terminal being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Minimum size of the frame */ + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]; + /**< Maximum size of the frame */ + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]; + /**< Minimum size of a fragment that the program port can accept */ + uint16_t min_fragment_size[IA_CSS_N_DATA_DIMENSION]; + /**< Maximum size of a fragment that the program port can accept */ + uint16_t max_fragment_size[IA_CSS_N_DATA_DIMENSION]; + /**< Indicate if this terminal is derived from a principal terminal */ + uint16_t terminal_dependency; + /**< Indicate what (streaming) interface types this terminal supports */ + ia_css_connection_bitmap_t connection_bitmap; + /**< Indicates if compression is supported on the data associated with + * this terminal. '1' indicates compression is supported, + * '0' otherwise + */ + uint8_t compression_support; + uint8_t reserved[4]; +}; + +/* ============ Program Control Init Terminal Manifest - START ============ */ +#define N_PADDING_UINT8_IN_PROGCTRLINIT_MANIFEST_PROGRAM_DESC_STRUCT 4 +struct ia_css_program_control_init_manifest_program_desc_s { + uint16_t load_section_count; + uint16_t connect_section_count; + uint8_t padding[N_PADDING_UINT8_IN_PROGCTRLINIT_MANIFEST_PROGRAM_DESC_STRUCT]; +}; + +#define N_PADDING_UINT8_IN_PROGCTRLINIT_TERMINAL_MANIFEST_STRUCT 2 +struct ia_css_program_control_init_terminal_manifest_s { + ia_css_terminal_manifest_t base; + /* Number of programs in program group */ + uint32_t program_count; + /* + * Points to array of ia_css_program_control_init_terminal_program_desc_t + * with size program_count. + */ + uint16_t program_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROGCTRLINIT_TERMINAL_MANIFEST_STRUCT]; +}; +/* ============ Program Control Init Terminal Manifest - END ============ */ + +extern void ia_css_program_manifest_init( + ia_css_program_manifest_t *blob, + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c new file mode 100644 index 000000000000..188f9d80193e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c @@ -0,0 +1,1240 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include +#include +/* for ia_css_kernel_bitmap_t, ia_css_kernel_bitmap_print */ +#include + +#include +#include "ia_css_psys_program_group_private.h" +#include "ia_css_psys_static_trace.h" + +#include +#include + +size_t ia_css_sizeof_program_manifest( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_sizeof_program_manifest(): enter:\n"); + + size += sizeof(ia_css_program_manifest_t); + size += program_dependency_count * sizeof(uint8_t); + size += terminal_dependency_count * sizeof(uint8_t); + size = ceil_mul(size, sizeof(uint64_t)); + + return size; +} + +bool ia_css_has_program_manifest_fixed_cell( + const ia_css_program_manifest_t *manifest) +{ + bool has_fixed_cell = false; + + vied_nci_cell_ID_t cell_id; + vied_nci_cell_type_ID_t cell_type_id; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_has_program_manifest_fixed_cell(): enter:\n"); + + verifexit(manifest != NULL); + + cell_id = ia_css_program_manifest_get_cell_ID(manifest); + cell_type_id = ia_css_program_manifest_get_cell_type_ID(manifest); + + has_fixed_cell = ((cell_id != VIED_NCI_N_CELL_ID) && + (cell_type_id == VIED_NCI_N_CELL_TYPE_ID)); + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_has_program_manifest_fixed_cell invalid argument\n"); + } + return has_fixed_cell; +} + +size_t ia_css_program_manifest_get_size( + const ia_css_program_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_size invalid argument\n"); + } + + return size; +} + +ia_css_program_ID_t ia_css_program_manifest_get_program_ID( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_ID_t program_id = IA_CSS_PROGRAM_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_ID(): enter:\n"); + + if (manifest != NULL) { + program_id = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_ID invalid argument\n"); + } + return program_id; +} + +int ia_css_program_manifest_set_program_ID( + ia_css_program_manifest_t *manifest, + ia_css_program_ID_t id) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_program_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = id; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_program_ID failed (%i)\n", ret); + } + return ret; +} + +ia_css_program_group_manifest_t *ia_css_program_manifest_get_parent( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_group_manifest_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_parent(): enter:\n"); + + verifexit(manifest != NULL); + + base = (char *)((char *)manifest + manifest->parent_offset); + + parent = (ia_css_program_group_manifest_t *) (base); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_parent invalid argument\n"); + } + return parent; +} + +int ia_css_program_manifest_set_parent_offset( + ia_css_program_manifest_t *manifest, + int32_t program_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_parent_offset(): enter:\n"); + + verifexit(manifest != NULL); + + /* parent is at negative offset away from current program offset*/ + manifest->parent_offset = -program_offset; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_parent_offset failed (%i)\n", + retval); + } + return retval; +} + +ia_css_program_type_t ia_css_program_manifest_get_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type = IA_CSS_N_PROGRAM_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_type(): enter:\n"); + + if (manifest != NULL) { + program_type = manifest->program_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_type invalid argument\n"); + } + return program_type; +} + +int ia_css_program_manifest_set_type( + ia_css_program_manifest_t *manifest, + const ia_css_program_type_t program_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_type(): enter:\n"); + + if (manifest != NULL) { + manifest->program_type = program_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_type failed (%i)\n", retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_manifest_get_kernel_bitmap( + const ia_css_program_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t kernel_bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + kernel_bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_kernel_bitmap invalid argument\n"); + } + return kernel_bitmap; +} + +int ia_css_program_manifest_set_kernel_bitmap( + ia_css_program_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = kernel_bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_kernel_bitmap failed (%i)\n", + retval); + } + return retval; +} + +vied_nci_cell_ID_t ia_css_program_manifest_get_cell_ID( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_cell_ID_t cell_id = VIED_NCI_N_CELL_ID; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cell_ID(): enter:\n"); + + verifexit(manifest != NULL); + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + cell_id = manifest->cell_id; +#else + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + assert(VIED_NCI_N_CELL_ID == manifest->cells[i]); +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } + cell_id = manifest->cells[0]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cell_ID invalid argument\n"); + } + return cell_id; +} + +int ia_css_program_manifest_set_cell_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_ID_t cell_id) +{ + int retval = -1; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cell_ID(): enter:\n"); + if (manifest != NULL) { +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = cell_id; +#else + manifest->cells[0] = cell_id; + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + manifest->cells[i] = VIED_NCI_N_CELL_ID; + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_cell_ID failed (%i)\n", retval); + } + return retval; +} + +vied_nci_cell_type_ID_t ia_css_program_manifest_get_cell_type_ID( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_cell_type_ID_t cell_type_id = VIED_NCI_N_CELL_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cell_type_ID(): enter:\n"); + + verifexit(manifest != NULL); + + cell_type_id = (vied_nci_cell_type_ID_t)(manifest->cell_type_id); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cell_type_ID invalid argument\n"); + } + return cell_type_id; +} + +int ia_css_program_manifest_set_cell_type_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_type_ID_t cell_type_id) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cell_type_ID(): enter:\n"); + if (manifest != NULL) { + manifest->cell_type_id = cell_type_id; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_cell_type_ID failed (%i)\n", + retval); + } + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t int_mem_size = 0; + vied_nci_cell_type_ID_t cell_type_id; + int mem_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_int_mem_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + + /* loop over vied_nci_cell_mem_type to verify mem_type_id for a + * specific cell_type_id + */ + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + if ((int)mem_type_id == + (int)vied_nci_cell_type_get_mem_type( + cell_type_id, mem_index)) { + int_mem_size = manifest->int_mem_size[mem_index]; + } + } + +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_int_mem_size invalid argument\n"); + } + return int_mem_size; +} + +int ia_css_program_manifest_set_cells_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + int array_index = 0; + int bit_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cells_bitmap(): enter:\n"); + + if (manifest != NULL) { + for (bit_index = 0; bit_index < VIED_NCI_N_CELL_ID; bit_index++) { + if (vied_nci_is_bit_set_in_bitmap(bitmap, bit_index)) { + verifexit(array_index < IA_CSS_PROCESS_MAX_CELLS); +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = (vied_nci_cell_ID_t)bit_index; +#else + manifest->cells[array_index] = (vied_nci_cell_ID_t)bit_index; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + array_index++; + } + } + for (; array_index < IA_CSS_PROCESS_MAX_CELLS; array_index++) { +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = VIED_NCI_N_CELL_ID; +#else + manifest->cells[array_index] = VIED_NCI_N_CELL_ID; +#endif /* IA_CSS_PROCESS_MAX_CELLS */ + } + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_cells_bitmap invalid argument\n"); + } +EXIT: + return retval; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_cells_bitmap( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_resource_bitmap_t bitmap = 0; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cells_bitmap(): enter:\n"); + + verifexit(manifest != NULL); + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + bitmap = (1 << manifest->cell_id); +#else + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + if (VIED_NCI_N_CELL_ID != manifest->cells[i]) { + bitmap |= (1 << manifest->cells[i]); + } +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cells_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_manifest_set_dfm_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dfm_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->dfm_port_bitmap[dfm_type_id] = bitmap; +#else + (void)bitmap; + (void)dfm_type_id; +#endif + retval = 0; + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dfm_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_program_manifest_set_dfm_active_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dfm_active_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->dfm_active_port_bitmap[dfm_type_id] = bitmap; +#else + (void)bitmap; + (void)dfm_type_id; +#endif + retval = 0; + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dfm_active_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_program_manifest_set_is_dfm_relocatable( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const uint8_t is_relocatable) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_is_dfm_relocatable(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->is_dfm_relocatable[dfm_type_id] = is_relocatable; +#else + (void)is_relocatable; + (void)dfm_type_id; +#endif + retval = 0; + + EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_is_dfm_relocatable invalid argument\n"); + } + + return retval; +} + +uint8_t ia_css_program_manifest_get_is_dfm_relocatable( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + uint8_t ret = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_is_dfm_relocatable(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + ret = manifest->is_dfm_relocatable[dfm_type_id]; +#else + ret = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_is_dfm_relocatable invalid argument\n"); + } + return ret; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dfm_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + bitmap = manifest->dfm_port_bitmap[dfm_type_id]; +#else + bitmap = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dfm_port_bitmap invalid argument\n"); + } + return bitmap; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_active_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dfm_active_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + bitmap = manifest->dfm_active_port_bitmap[dfm_type_id]; +#else + bitmap = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dfm_active_port_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_manifest_set_int_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t int_mem_size) +{ + int retval = -1; + vied_nci_cell_type_ID_t cell_type_id; + int mem_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_int_mem_size(): enter:\n"); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + + if (manifest != NULL && mem_type_id < VIED_NCI_N_MEM_TYPE_ID) { + /* loop over vied_nci_cell_mem_type to verify mem_type_id for + * a specific cell_type_id + */ + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + if ((int)mem_type_id == + (int)vied_nci_cell_type_get_mem_type( + cell_type_id, mem_index)) { + manifest->int_mem_size[mem_index] = + int_mem_size; + retval = 0; + } + } + } + if (retval != 0) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_int_mem_size cell_type_id %d has no mem_type_id %d\n", + (int)cell_type_id, (int)mem_type_id); + } + + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t ext_mem_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_ext_mem_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + ext_mem_size = manifest->ext_mem_size[mem_type_id]; +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_ext_mem_size invalid argument\n"); + } + return ext_mem_size; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t ext_mem_offset = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_ext_mem_offset(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + ext_mem_offset = manifest->ext_mem_offset[mem_type_id]; +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_ext_mem_offset invalid argument\n"); + } + return ext_mem_offset; +} + +int ia_css_program_manifest_set_ext_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_ext_mem_size(): enter:\n"); + + if (manifest != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID) { + manifest->ext_mem_size[mem_type_id] = ext_mem_size; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_ext_mem_size invalid argument\n"); + } + + return retval; +} + +int ia_css_program_manifest_set_ext_mem_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_ext_mem_offset(): enter:\n"); + + if (manifest != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID) { + manifest->ext_mem_offset[mem_type_id] = ext_mem_offset; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_ext_mem_offset invalid argument\n"); + } + + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + vied_nci_resource_size_t dev_chn_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dev_chn_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(dev_chn_id < VIED_NCI_N_DEV_CHN_ID); + + dev_chn_size = manifest->dev_chn_size[dev_chn_id]; +EXIT: + if (NULL == manifest || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dev_chn_size invalid argument\n"); + } + return dev_chn_size; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + vied_nci_resource_size_t dev_chn_offset = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dev_chn_offset(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(dev_chn_id < VIED_NCI_N_DEV_CHN_ID); + + dev_chn_offset = manifest->dev_chn_offset[dev_chn_id]; +EXIT: + if (NULL == manifest || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dev_chn_offset invalid argument\n"); + } + return dev_chn_offset; +} + +int ia_css_program_manifest_set_dev_chn_size( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dev_chn_size(): enter:\n"); + + if (manifest != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + manifest->dev_chn_size[dev_chn_id] = dev_chn_size; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dev_chn_size invalid argument\n"); + } + + return retval; +} + +int ia_css_program_manifest_set_dev_chn_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dev_chn_offset(): enter:\n"); + + if (manifest != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + manifest->dev_chn_offset[dev_chn_id] = dev_chn_offset; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dev_chn_offset invalid argument\n"); + } + + return retval; +} + +uint8_t ia_css_program_manifest_get_program_dependency_count( + const ia_css_program_manifest_t *manifest) +{ + uint8_t program_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_dependency_count(): enter:\n"); + + if (manifest != NULL) { + program_dependency_count = manifest->program_dependency_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_dependency_count invalid argument\n"); + } + return program_dependency_count; +} + +uint8_t ia_css_program_manifest_get_program_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index) +{ + uint8_t program_dependency = IA_CSS_PROGRAM_INVALID_DEPENDENCY; + uint8_t *program_dep_ptr; + uint8_t program_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_dependency(): enter:\n"); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + + if (index < program_dependency_count) { + program_dep_ptr = + (uint8_t *)((uint8_t *)manifest + + manifest->program_dependency_offset + + index * sizeof(uint8_t)); + program_dependency = *program_dep_ptr; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_dependency invalid argument\n"); + } + return program_dependency; +} + +int ia_css_program_manifest_set_program_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t program_dependency, + const unsigned int index) +{ + int retval = -1; + uint8_t *program_dep_ptr; + uint8_t program_dependency_count; + uint8_t program_count; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_program_dependency(): enter:\n"); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + program_count = + ia_css_program_group_manifest_get_program_count( + ia_css_program_manifest_get_parent(manifest)); + + if ((index < program_dependency_count) && + (program_dependency < program_count)) { + program_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->program_dependency_offset + + index*sizeof(uint8_t)); + *program_dep_ptr = program_dependency; + retval = 0; + } + + if (retval != 0) { + IA_CSS_TRACE_3(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_program_dependency(m, %d, %d) failed (%i)\n", + program_dependency, index, retval); + } + return retval; +} + +uint8_t ia_css_program_manifest_get_terminal_dependency_count( + const ia_css_program_manifest_t *manifest) +{ + uint8_t terminal_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_terminal_dependency_count(): enter:\n"); + + if (manifest != NULL) { + terminal_dependency_count = manifest->terminal_dependency_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_terminal_dependency_count invalid argument\n"); + } + return terminal_dependency_count; +} + +uint8_t ia_css_program_manifest_get_terminal_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index) +{ + uint8_t terminal_dependency = IA_CSS_PROGRAM_INVALID_DEPENDENCY; + uint8_t *terminal_dep_ptr; + uint8_t terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_terminal_dependency(): enter:\n"); + + if (index < terminal_dependency_count) { + terminal_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->terminal_dependency_offset + index); + terminal_dependency = *terminal_dep_ptr; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_terminal_dependency invalid argument\n"); + } + return terminal_dependency; +} + +int ia_css_program_manifest_set_terminal_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t terminal_dependency, + const unsigned int index) +{ + int retval = -1; + uint8_t *terminal_dep_ptr; + uint8_t terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + uint8_t terminal_count = + ia_css_program_group_manifest_get_terminal_count( + ia_css_program_manifest_get_parent(manifest)); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_terminal_dependency(): enter:\n"); + + if ((index < terminal_dependency_count) && + (terminal_dependency < terminal_count)) { + terminal_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->terminal_dependency_offset + index); + *terminal_dep_ptr = terminal_dependency; + retval = 0; + } + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_terminal_dependency failed (%i)\n", + retval); + } + return retval; +} + +bool ia_css_is_program_manifest_subnode_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_subnode_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_PARALLEL_SUB) || + (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) || + (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); +} + +bool ia_css_is_program_manifest_supernode_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_supernode_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); + +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER) || + (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER) || + (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER); +} + +bool ia_css_is_program_manifest_singular_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_singular_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); + +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_SINGULAR); +} + +void ia_css_program_manifest_init( + ia_css_program_manifest_t *blob, + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count) +{ + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_manifest_init(): enter:\n"); + + /*TODO: add assert*/ + if (!blob) + return; + + blob->ID = 1; + blob->program_dependency_count = program_dependency_count; + blob->terminal_dependency_count = terminal_dependency_count; + blob->program_dependency_offset = sizeof(ia_css_program_manifest_t); + blob->terminal_dependency_offset = blob->program_dependency_offset + + sizeof(uint8_t) * program_dependency_count; + blob->size = + (uint16_t)ia_css_sizeof_program_manifest( + program_dependency_count, + terminal_dependency_count); +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) + +#if defined(_MSC_VER) +/* WA for a visual studio compiler bug, refer to + developercommunity.visualstudio.com/content/problem/209359/ice-with-fpfast-in-156-and-msvc-daily-1413263051-p.html +*/ +#pragma optimize("", off) +#endif + +int ia_css_program_manifest_print( + const ia_css_program_manifest_t *manifest, + void *fid) +{ + int retval = -1; + int i, mem_index, dev_chn_index; + + vied_nci_cell_type_ID_t cell_type_id; + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_manifest_print(): enter:\n"); + + verifexit(manifest != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "sizeof(manifest) = %d\n", + (int)ia_css_program_manifest_get_size(manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "program ID = %d\n", + (int)ia_css_program_manifest_get_program_ID(manifest)); + + bitmap = ia_css_program_manifest_get_kernel_bitmap(manifest); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "cell(program) = %d\n", + (int)cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "cell type(program) = %d\n", + (int)cell_type_id); + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(internal mem) type = %d\n", + (int)vied_nci_cell_type_get_mem_type(cell_type_id, mem_index)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(internal mem) size = %d\n", + manifest->int_mem_size[mem_index]); + } + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) type = %d\n", + (int)(vied_nci_mem_type_ID_t)mem_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) size = %d\n", + manifest->ext_mem_size[mem_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) offset = %d\n", + manifest->ext_mem_offset[mem_index]); + } + + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) type = %d\n", + (int)dev_chn_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) size = %d\n", + manifest->dev_chn_size[dev_chn_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) offset = %d\n", + manifest->dev_chn_offset[dev_chn_index]); + } +#if HAS_DFM + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_DFM_ID; + dev_chn_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) type = %d\n", + (int)dev_chn_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) port_bitmap = %d\n", + manifest->dfm_port_bitmap[dev_chn_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) active_port_bitmap = %d\n", + manifest->dfm_active_port_bitmap[dev_chn_index]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) is_dfm_relocatable = %d\n", + manifest->is_dfm_relocatable[dev_chn_index]); + } +#endif + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(cells) bitmap = %d\n", + manifest->cell_id); +#else + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(cells) bitmap = %d\n", + manifest->cells[i]); + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + if (program_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program_dependencies[%d] {};\n", + program_dependency_count); + } else { + uint8_t prog_dep; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program_dependencies[%d] {\n", + program_dependency_count); + for (i = 0; i < (int)program_dependency_count - 1; i++) { + prog_dep = + ia_css_program_manifest_get_program_dependency( + manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t %4d,\n", prog_dep); + } + prog_dep = + ia_css_program_manifest_get_program_dependency(manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\t %4d }\n", prog_dep); + (void)prog_dep; + } + + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + if (terminal_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "terminal_dependencies[%d] {};\n", + terminal_dependency_count); + } else { + uint8_t term_dep; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "terminal_dependencies[%d] {\n", + terminal_dependency_count); + for (i = 0; i < (int)terminal_dependency_count - 1; i++) { + term_dep = + ia_css_program_manifest_get_terminal_dependency( + manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t %4d,\n", term_dep); + } + term_dep = + ia_css_program_manifest_get_terminal_dependency(manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\t %4d }\n", term_dep); + (void)term_dep; + } + (void)cell_type_id; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_print failed (%i)\n", retval); + } + return retval; +} + +#if defined(_MSC_VER) +/* WA for a visual studio compiler bug */ +#pragma optimize("", off) +#endif + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c new file mode 100644 index 000000000000..c890b8a71f2c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c @@ -0,0 +1,1137 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include + +/* Data object types on the terminals */ +#include +/* for ia_css_kernel_bitmap_t, ia_css_kernel_bitmap_clear, ia_css_... */ +#include + +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_manifest_types.h" + +#include +#include +#include +#include "ia_css_psys_static_trace.h" + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) +static const char *terminal_type_strings[IA_CSS_N_TERMINAL_TYPES + 1] = { + "IA_CSS_TERMINAL_TYPE_DATA_IN", + "IA_CSS_TERMINAL_TYPE_DATA_OUT", + "IA_CSS_TERMINAL_TYPE_PARAM_STREAM", + /**< Type 1-5 parameter input */ + "IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN", + /**< Type 1-5 parameter output */ + "IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT", + /**< Represent the new type of terminal for + * the "spatial dependent parameters", when params go in + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN", + /**< Represent the new type of terminal for + * the "spatial dependent parameters", when params go out + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT", + /**< Represent the new type of terminal for + * the explicit slicing, when params go in + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN", + /**< Represent the new type of terminal for + * the explicit slicing, when params go out + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT", + /**< State (private data) input */ + "IA_CSS_TERMINAL_TYPE_STATE_IN", + /**< State (private data) output */ + "IA_CSS_TERMINAL_TYPE_STATE_OUT", + "IA_CSS_TERMINAL_TYPE_PROGRAM", + "IA_CSS_TERMINAL_TYPR_PROGRAM_CONTROL_INIT", + "UNDEFINED_TERMINAL_TYPE"}; + +#endif + +bool ia_css_is_terminal_manifest_spatial_parameter_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT)); +} + +bool ia_css_is_terminal_manifest_program_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM); +} + +bool ia_css_is_terminal_manifest_program_control_init_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_program_control_init_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); +} + + +bool ia_css_is_terminal_manifest_parameter_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + /* will return an error value on error */ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT); +} + +bool ia_css_is_terminal_manifest_data_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + /* will return an error value on error */ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_data_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT)); +} + +bool ia_css_is_terminal_manifest_sliced_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_sliced_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT)); +} + +size_t ia_css_terminal_manifest_get_size( + const ia_css_terminal_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_terminal_manifest_get_size: invalid argument\n"); + } + return size; +} + +ia_css_terminal_type_t ia_css_terminal_manifest_get_type( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_type(): enter:\n"); + + if (manifest != NULL) { + terminal_type = manifest->terminal_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_terminal_manifest_get_type: invalid argument\n"); + } + return terminal_type; +} + +int ia_css_terminal_manifest_set_type( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_type_t terminal_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_type(): enter:\n"); + + if (manifest != NULL) { + manifest->terminal_type = terminal_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_type failed (%i)\n", + retval); + } + return retval; +} + +int ia_css_terminal_manifest_set_ID( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_ID_t ID) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = ID; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_ID failed (%i)\n", + retval); + } + return retval; +} + +ia_css_terminal_ID_t ia_css_terminal_manifest_get_ID( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_ID_t retval; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_ID(): enter:\n"); + + if (manifest != NULL) { + retval = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_get_ID failed\n"); + retval = IA_CSS_TERMINAL_INVALID_ID; + } + return retval; +} + +ia_css_program_group_manifest_t *ia_css_terminal_manifest_get_parent( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_program_group_manifest_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_parent(): enter:\n"); + + verifexit(manifest != NULL); + + base = (char *)((char *)manifest + manifest->parent_offset); + + parent = (ia_css_program_group_manifest_t *)(base); +EXIT: + return parent; +} + +int ia_css_terminal_manifest_set_parent_offset( + ia_css_terminal_manifest_t *manifest, + int32_t terminal_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_parent_offset(): enter:\n"); + + verifexit(manifest != NULL); + + /* parent is at negative offset away from current terminal offset*/ + manifest->parent_offset = -terminal_offset; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_parent_offset failed (%i)\n", + retval); + } + return retval; +} + +ia_css_frame_format_bitmap_t +ia_css_data_terminal_manifest_get_frame_format_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_frame_format_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_frame_format_bitmap(): enter:\n"); + + if (manifest != NULL) { + bitmap = manifest->frame_format_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_frame_format_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_data_terminal_manifest_set_frame_format_bitmap( + ia_css_data_terminal_manifest_t *manifest, + ia_css_frame_format_bitmap_t bitmap) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_frame_format_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->frame_format_bitmap = bitmap; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_frame_format_bitmap failed (%i)\n", + ret); + } + + return ret; +} + +bool ia_css_data_terminal_manifest_can_support_compression( + const ia_css_data_terminal_manifest_t *manifest) +{ + bool compression_support = false; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_compression_support(): enter:\n"); + + if (manifest != NULL) { + /* compression_support is used boolean encoded in uint8_t. + * So we only need to check + * if this is non-zero + */ + compression_support = (manifest->compression_support != 0); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_can_support_compression invalid argument\n"); + } + + return compression_support; +} + +int ia_css_data_terminal_manifest_set_compression_support( + ia_css_data_terminal_manifest_t *manifest, + bool compression_support) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_compression_support(): enter:\n"); + + if (manifest != NULL) { + manifest->compression_support = + (compression_support == true) ? 1 : 0; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_compression_support failed (%i)\n", + ret); + } + + return ret; +} + +ia_css_connection_bitmap_t ia_css_data_terminal_manifest_get_connection_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_connection_bitmap_t connection_bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_connection_bitmap(): enter:\n"); + + if (manifest != NULL) { + connection_bitmap = manifest->connection_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_connection_bitmap invalid argument\n"); + } + return connection_bitmap; +} + +int ia_css_data_terminal_manifest_set_connection_bitmap( + ia_css_data_terminal_manifest_t *manifest, ia_css_connection_bitmap_t bitmap) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_connection_bitmap(): enter:\n"); + + if (manifest != NULL) { + assert(bitmap != 0); /* zero means there is no connection, this is invalid. */ + assert((bitmap >> IA_CSS_N_CONNECTION_TYPES) == 0); + + manifest->connection_bitmap = bitmap; + ret = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_set_connection_bitmap invalid argument\n"); + } + return ret; +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) +ia_css_kernel_bitmap_t ia_css_data_terminal_manifest_get_kernel_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t kernel_bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + kernel_bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_kernel_bitmap: invalid argument\n"); + } + return kernel_bitmap; +} + +int ia_css_data_terminal_manifest_set_kernel_bitmap( + ia_css_data_terminal_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = kernel_bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_kernel_bitmap: failed (%i)\n", + retval); + } + + return retval; +} + +int ia_css_data_terminal_manifest_set_kernel_bitmap_unique( + ia_css_data_terminal_manifest_t *manifest, + const unsigned int index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_kernel_bitmap_unique(): enter:\n"); + + if (manifest != NULL) { + ia_css_kernel_bitmap_t kernel_bitmap = + ia_css_kernel_bitmap_clear(); + + kernel_bitmap = ia_css_kernel_bitmap_set(kernel_bitmap, index); + verifexit(!ia_css_is_kernel_bitmap_empty(kernel_bitmap)); + verifexit(ia_css_data_terminal_manifest_set_kernel_bitmap( + manifest, kernel_bitmap) == 0); + retval = 0; + } + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_kernel_bitmap_unique failed (%i)\n", + retval); + } + return retval; +} +#endif + +int ia_css_data_terminal_manifest_set_min_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_min_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->min_size[IA_CSS_COL_DIMENSION] = + min_size[IA_CSS_COL_DIMENSION]; + manifest->min_size[IA_CSS_ROW_DIMENSION] = + min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_min_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_max_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_max_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->max_size[IA_CSS_COL_DIMENSION] = + max_size[IA_CSS_COL_DIMENSION]; + manifest->max_size[IA_CSS_ROW_DIMENSION] = + max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_max_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_min_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_min_size(): enter:\n"); + + verifexit(manifest != NULL); + + min_size[IA_CSS_COL_DIMENSION] = + manifest->min_size[IA_CSS_COL_DIMENSION]; + min_size[IA_CSS_ROW_DIMENSION] = + manifest->min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_min_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_max_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_max_size(): enter:\n"); + + verifexit(manifest != NULL); + + max_size[IA_CSS_COL_DIMENSION] = + manifest->max_size[IA_CSS_COL_DIMENSION]; + max_size[IA_CSS_ROW_DIMENSION] = + manifest->max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_max_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_min_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_min_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->min_fragment_size[IA_CSS_COL_DIMENSION] = + min_size[IA_CSS_COL_DIMENSION]; + manifest->min_fragment_size[IA_CSS_ROW_DIMENSION] = + min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_min_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_max_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_max_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->max_fragment_size[IA_CSS_COL_DIMENSION] = + max_size[IA_CSS_COL_DIMENSION]; + manifest->max_fragment_size[IA_CSS_ROW_DIMENSION] = + max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_max_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_min_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_min_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + min_size[IA_CSS_COL_DIMENSION] = + manifest->min_fragment_size[IA_CSS_COL_DIMENSION]; + min_size[IA_CSS_ROW_DIMENSION] = + manifest->min_fragment_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_min_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_max_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_max_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + max_size[IA_CSS_COL_DIMENSION] = + manifest->max_fragment_size[IA_CSS_COL_DIMENSION]; + max_size[IA_CSS_ROW_DIMENSION] = + manifest->max_fragment_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_max_fragment_size invalid argument\n"); + } + return retval; +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) + +#define PRINT_DIMENSION(name, var) IA_CSS_TRACE_3(PSYSAPI_STATIC, \ + INFO, "%s:\t%d %d\n", \ + (name), \ + (var)[IA_CSS_COL_DIMENSION], \ + (var)[IA_CSS_ROW_DIMENSION]) + +int ia_css_terminal_manifest_print( + const ia_css_terminal_manifest_t *manifest, + void *fid) +{ + int retval = -1; + ia_css_terminal_type_t terminal_type = + ia_css_terminal_manifest_get_type(manifest); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_terminal_manifest_print(): enter:\n"); + + verifexit(manifest != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "sizeof(manifest) = %d\n", + (int)ia_css_terminal_manifest_get_size(manifest)); + + PRINT("typeof(manifest) = %s\n", terminal_type_strings[terminal_type]); + + if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + ia_css_param_terminal_manifest_t *pterminal_manifest = + (ia_css_param_terminal_manifest_t *)manifest; + uint16_t section_count = + pterminal_manifest->param_manifest_section_desc_count; + int i; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sections(manifest) = %d\n", (int)section_count); + for (i = 0; i < section_count; i++) { + const ia_css_param_manifest_section_desc_t *manifest = + ia_css_param_terminal_manifest_get_prm_sct_desc( + pterminal_manifest, i); + verifjmpexit(manifest != NULL); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_id = %d\n", (int)manifest->kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "mem_type_id = %d\n", + (int)manifest->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_mem_size = %d\n", + (int)manifest->max_mem_size); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "region_id = %d\n", + (int)manifest->region_id); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT) { + ia_css_sliced_param_terminal_manifest_t + *sliced_terminal_manifest = + (ia_css_sliced_param_terminal_manifest_t *)manifest; + uint32_t kernel_id; + uint16_t section_count; + uint16_t section_idx; + + kernel_id = sliced_terminal_manifest->kernel_id; + section_count = + sliced_terminal_manifest->sliced_param_section_count; + + NOT_USED(kernel_id); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_id = %d\n", (int)kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "section_count = %d\n", (int)section_count); + + for (section_idx = 0; section_idx < section_count; + section_idx++) { + ia_css_sliced_param_manifest_section_desc_t + *sliced_param_manifest_section_desc; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "section %d\n", (int)section_idx); + sliced_param_manifest_section_desc = + ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + sliced_terminal_manifest, section_idx); + verifjmpexit(sliced_param_manifest_section_desc != + NULL); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "mem_type_id = %d\n", + (int)sliced_param_manifest_section_desc->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "region_id = %d\n", + (int)sliced_param_manifest_section_desc->region_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_mem_size = %d\n", + (int)sliced_param_manifest_section_desc->max_mem_size); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM) { + ia_css_program_terminal_manifest_t *program_terminal_manifest = + (ia_css_program_terminal_manifest_t *)manifest; + uint32_t sequencer_info_kernel_id; + uint16_t max_kernel_fragment_sequencer_command_desc; + uint16_t kernel_fragment_sequencer_info_manifest_info_count; + uint16_t seq_info_idx; + + sequencer_info_kernel_id = + program_terminal_manifest->sequencer_info_kernel_id; + max_kernel_fragment_sequencer_command_desc = + program_terminal_manifest-> + max_kernel_fragment_sequencer_command_desc; + kernel_fragment_sequencer_info_manifest_info_count = + program_terminal_manifest-> + kernel_fragment_sequencer_info_manifest_info_count; + + NOT_USED(sequencer_info_kernel_id); + NOT_USED(max_kernel_fragment_sequencer_command_desc); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sequencer_info_kernel_id = %d\n", + (int)sequencer_info_kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_kernel_fragment_sequencer_command_desc = %d\n", + (int)max_kernel_fragment_sequencer_command_desc); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_fragment_sequencer_info_manifest_info_count = %d\n", + (int) + kernel_fragment_sequencer_info_manifest_info_count); + + for (seq_info_idx = 0; seq_info_idx < + kernel_fragment_sequencer_info_manifest_info_count; + seq_info_idx++) { + ia_css_kernel_fragment_sequencer_info_manifest_desc_t + *sequencer_info_manifest_desc; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sequencer info %d\n", (int)seq_info_idx); + sequencer_info_manifest_desc = + ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc + (program_terminal_manifest, seq_info_idx); + verifjmpexit(sequencer_info_manifest_desc != NULL); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_slice_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_slice_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_slice_count[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_slice_count[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_point_decimation_factor[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_point_decimation_factor[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_overlay_on_pixel_topleft_index[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_overlay_on_pixel_topleft_index[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_overlay_on_pixel_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_overlay_on_pixel_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT) { + ia_css_program_control_init_terminal_manifest_t *progctrlinit_man = + (ia_css_program_control_init_terminal_manifest_t *)manifest; + ia_css_program_control_init_terminal_manifest_print(progctrlinit_man); + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT) { + + ia_css_data_terminal_manifest_t *dterminal_manifest = + (ia_css_data_terminal_manifest_t *)manifest; + int i; + + NOT_USED(dterminal_manifest); + + verifexit(ia_css_kernel_bitmap_print( + ia_css_data_terminal_manifest_get_kernel_bitmap( + dterminal_manifest), fid) == 0); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "formats(manifest) = %04x\n", + (int)ia_css_data_terminal_manifest_get_frame_format_bitmap( + dterminal_manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "connection(manifest) = %04x\n", + (int)ia_css_data_terminal_manifest_get_connection_bitmap( + dterminal_manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "dependent(manifest) = %d\n", + (int)dterminal_manifest->terminal_dependency); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmin_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", dterminal_manifest->min_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", dterminal_manifest->min_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmax_size[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", dterminal_manifest->max_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", dterminal_manifest->max_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmin_fragment_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", + dterminal_manifest->min_fragment_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", + dterminal_manifest->min_fragment_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmax_fragment_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", + dterminal_manifest->max_fragment_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", + dterminal_manifest->max_fragment_size[i]); + + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT) { + + ia_css_spatial_param_terminal_manifest_t *stm = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + ia_css_frame_grid_param_manifest_section_desc_t *sec; + int sec_count = + stm->frame_grid_param_manifest_section_desc_count; + ia_css_fragment_grid_manifest_desc_t *fragd = + &stm->common_fragment_grid_desc; + ia_css_frame_grid_manifest_desc_t *framed = + &stm->frame_grid_desc; + int sec_index; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "kernel_id:\t\t%d\n", + stm->kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "compute_units_p_elem:\t%d\n", + stm->compute_units_p_elem); + + PRINT_DIMENSION("min_fragment_grid_dimension", + fragd->min_fragment_grid_dimension); + PRINT_DIMENSION("max_fragment_grid_dimension", + fragd->max_fragment_grid_dimension); + PRINT_DIMENSION("min_frame_grid_dimension", + framed->min_frame_grid_dimension); + PRINT_DIMENSION("max_frame_grid_dimension", + framed->max_frame_grid_dimension); + + NOT_USED(framed); + NOT_USED(fragd); + + for (sec_index = 0; sec_index < sec_count; sec_index++) { + sec = ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + stm, sec_index); + verifjmpexit(sec != NULL); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, "--------------------------\n"); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tmem_type_id:\t%d\n", + sec->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tregion_id:\t%d\n", + sec->region_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\telem_size:\t%d\n", + sec->elem_size); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tmax_mem_size:\t%d\n", + sec->max_mem_size); + } + } else if (terminal_type < IA_CSS_N_TERMINAL_TYPES) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "terminal type can not be pretty printed, not supported\n"); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_print failed (%i)\n", + retval); + } + return retval; +} + +/* Program control init Terminal */ +unsigned int ia_css_program_control_init_terminal_manifest_get_connect_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog) +{ + assert(prog); + return prog->connect_section_count; +} + + +unsigned int ia_css_program_control_init_terminal_manifest_get_load_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog) +{ + assert(prog); + return prog->load_section_count; +} + +unsigned int ia_css_program_control_init_terminal_manifest_get_size( + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections) +{ + (void)nof_load_sections; /* might be needed in future */ + (void)nof_connect_sections; /* might be needed in future */ + + return sizeof(ia_css_program_control_init_terminal_manifest_t) + + nof_programs * + sizeof(ia_css_program_control_init_manifest_program_desc_t); +} + +ia_css_program_control_init_manifest_program_desc_t * +ia_css_program_control_init_terminal_manifest_get_program_desc( + const ia_css_program_control_init_terminal_manifest_t *terminal, + unsigned int program) +{ + ia_css_program_control_init_manifest_program_desc_t *progs; + + assert(terminal != NULL); + assert(program < terminal->program_count); + + progs = (ia_css_program_control_init_manifest_program_desc_t *) + ((const char *)terminal + terminal->program_desc_offset); + + return &progs[program]; +} + +int ia_css_program_control_init_terminal_manifest_init( + ia_css_program_control_init_terminal_manifest_t *terminal, + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections) +{ + unsigned int i; + ia_css_program_control_init_manifest_program_desc_t *progs; + + if (terminal == NULL) { + return -EFAULT; + } + + terminal->program_count = nof_programs; + terminal->program_desc_offset = + sizeof(ia_css_program_control_init_terminal_manifest_t); + + progs = ia_css_program_control_init_terminal_manifest_get_program_desc( + terminal, 0); + + for (i = 0; i < nof_programs; i++) { + progs[i].load_section_count = nof_load_sections[i]; + progs[i].connect_section_count = nof_connect_sections[i]; + } + return 0; +} + +void ia_css_program_control_init_terminal_manifest_print( + ia_css_program_control_init_terminal_manifest_t *terminal) +{ + unsigned int i; + + ia_css_program_control_init_manifest_program_desc_t *progs; + + progs = ia_css_program_control_init_terminal_manifest_get_program_desc( + terminal, 0); + + assert(progs); + (void)progs; + + for (i = 0; i < terminal->program_count; i++) { + IA_CSS_TRACE_3(PSYSAPI_STATIC, INFO, + "program index: %d, load sec: %d, connect sec: %d\n", + i, + progs[i].load_section_count, + progs[i].connect_section_count); + } +} + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.c new file mode 100644 index 000000000000..c51d65c8cb64 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.c @@ -0,0 +1,15 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#include "ia_css_debug_dump.h" + void ia_css_debug_dump(void) {} \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.h new file mode 100644 index 000000000000..5dd23ddbd180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/psys/bxtB0_gen_reg_dump/ia_css_debug_dump.h @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#ifndef __IA_CSS_DEBUG_DUMP_H_ + #define __IA_CSS_DEBUG_DUMP_H_ + void ia_css_debug_dump(void); + #endif /* __IA_CSS_DEBUG_DUMP_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c new file mode 100644 index 000000000000..9b9161ae78cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include "ia_css_trace.h" +#ifdef USE_LOGICAL_SSIDS +/* + Logical names can be used to define the SSID + In order to resolve these names the following include file should be provided + and the define above should be enabled +*/ +#include +#endif + +#define REG_DUMP_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#define REG_DUMP_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + +/* SSID value is defined in test makefiles as either isys0 or psys0 */ +#define REG_DUMP_READ_REGISTER(addr) vied_subsystem_load_32(SSID, addr) + +#define REG_DUMP_PRINT_0(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_0(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define REG_DUMP_PRINT_1(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_1(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define EXPAND_VA_ARGS(x) x + +/* Including generated source code for reg_dump */ +#include "ia_css_debug_dump.c" diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/interface/regmem_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/interface/regmem_access.h new file mode 100644 index 000000000000..d4576af936f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/interface/regmem_access.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_H +#define __REGMEM_ACCESS_H + +#include "storage_class.h" + +enum regmem_id { + /* pass pkg_dir address to SPC in non-secure mode */ + PKG_DIR_ADDR_REG = 0, + /* pass syscom configuration to SPC */ + SYSCOM_CONFIG_REG = 1, + /* syscom state - modified by SP */ + SYSCOM_STATE_REG = 2, + /* syscom commands - modified by the host */ + SYSCOM_COMMAND_REG = 3, + /* Store interrupt status - updated by SP */ + SYSCOM_IRQ_REG = 4, + /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/ + SYSCOM_VTL0_ADDR_MASK = 5, +#if HAS_DUAL_CMD_CTX_SUPPORT + /* Initialized if trustlet exists - updated by host */ + TRUSTLET_STATUS = 6, + /* identify if SPC access blocker programming is completed - updated by SP */ + AB_SPC_STATUS = 7, + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 8 +#else + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 6 +#endif +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* Bit 0: for untrusted non-secure DRV driver on VTL0 + * Bit 1: for trusted secure TEE driver on VTL1 + */ +#define SYSCOM_IRQ_VTL0_MASK 0x1 +#define SYSCOM_IRQ_VTL1_MASK 0x2 +#endif + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_address, unsigned int reg, unsigned int ssid); + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_address, unsigned int reg, unsigned int value, + unsigned int ssid); + +#ifdef __VIED_CELL +#include "regmem_access_cell.h" +#else +#include "regmem_access_host.h" +#endif + +#endif /* __REGMEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/regmem.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/regmem.mk new file mode 100644 index 000000000000..24ebc1c325d8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/regmem.mk @@ -0,0 +1,32 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef REGMEM_MK +REGMEM_MK=1 + +# MODULE is REGMEM + +REGMEM_DIR=$${MODULES_DIR}/regmem + +REGMEM_INTERFACE=$(REGMEM_DIR)/interface +REGMEM_SOURCES=$(REGMEM_DIR)/src + +REGMEM_HOST_FILES = +REGMEM_FW_FILES = $(REGMEM_SOURCES)/regmem.c + +REGMEM_CPPFLAGS = -I$(REGMEM_INTERFACE) -I$(REGMEM_SOURCES) +REGMEM_HOST_CPPFLAGS = $(REGMEM_CPPFLAGS) +REGMEM_FW_CPPFLAGS = $(REGMEM_CPPFLAGS) + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_access_host.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_access_host.h new file mode 100644 index 000000000000..8878d7074fab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_access_host.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_HOST_H +#define __REGMEM_ACCESS_HOST_H + +#include "regmem_access.h" /* implemented interface */ + +#include "storage_class.h" +#include "regmem_const.h" +#include +#include "ia_css_cmem.h" + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_addr, unsigned int reg, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + return ia_css_cmem_load_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg)); +} + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_addr, unsigned int reg, + unsigned int value, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + ia_css_cmem_store_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg), + value); +} + +#endif /* __REGMEM_ACCESS_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_const.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_const.h new file mode 100644 index 000000000000..ac7e3a98a434 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/regmem/src/regmem_const.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_CONST_H +#define __REGMEM_CONST_H + +#ifndef REGMEM_SIZE +#define REGMEM_SIZE (16) +#endif /* REGMEM_SIZE */ +#ifndef REGMEM_OFFSET +#define REGMEM_OFFSET (0) +#endif /* REGMEM_OFFSET */ +#ifndef REGMEM_WORD_BYTES +#define REGMEM_WORD_BYTES (4) +#endif + +#endif /* __REGMEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h new file mode 100644 index 000000000000..4a04a9890326 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h @@ -0,0 +1,173 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_H +#define __IA_CSS_RBM_H + +#include "ia_css_rbm_storage_class.h" +#include + +#define IA_CSS_RBM_BITS 64 +/** An element is a 32 bit unsigned integer. 64 bit integers might cause + * problems in the compiler. + */ +#define IA_CSS_RBM_ELEM_TYPE uint32_t +#define IA_CSS_RBM_ELEM_BITS \ + (sizeof(IA_CSS_RBM_ELEM_TYPE)*8) +#define IA_CSS_RBM_NOF_ELEMS \ + ((IA_CSS_RBM_BITS) / (IA_CSS_RBM_ELEM_BITS)) + +/** Users should make no assumption about the actual type of + * ia_css_rbm_t. + */ +typedef struct { + IA_CSS_RBM_ELEM_TYPE data[IA_CSS_RBM_NOF_ELEMS]; +} ia_css_rbm_elems_t; +typedef ia_css_rbm_elems_t ia_css_rbm_t; + +/** Print the bits of a routing bitmap + * @return < 0 on error + */ +IA_CSS_RBM_STORAGE_CLASS_H +int ia_css_rbm_print( + const ia_css_rbm_t bitmap, + void *fid); + +/** Create an empty routing bitmap + * @return bitmap = 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_clear(void); + +/** Creates the complement of a routing bitmap + * @param bitmap[in] routing bitmap + * @return ~bitmap + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_complement( + const ia_css_rbm_t bitmap); + +/** Create the union of two routing bitmaps + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 | bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_union( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Create the intersection of two routing bitmaps + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 & bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_intersection( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the routing bitmaps is empty + * @param bitmap[in] routing bitmap + * @return bitmap == 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_empty( + const ia_css_rbm_t bitmap); + +/** Check if the intersection of two routing bitmaps is empty + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return (bitmap0 & bitmap1) == 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_intersection_empty( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the second routing bitmap is a subset of the first (or equal) + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in routing bitmap 1 + * Note: An empty set is always a subset, this function + * returns true if bitmap 1 is empty + * @return (bitmap0 & bitmap1) == bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_subset( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the routing bitmaps are equal + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 == bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_equal( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Checks whether a specific kernel bit is set + * @return bitmap[index] == 1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +int ia_css_is_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Create the union of a routing bitmap with a onehot bitmap + * with a bit set at index + * @return bitmap[index] |= 1 +*/ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Creates routing bitmap using a uint64 value. + * @return bitmap with the same bits set as in value (provided that width of bitmap is sufficient). + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_create_from_uint64( + const uint64_t value); + +/** Converts an ia_css_rbm_t type to uint64_t. Note that if + * ia_css_rbm_t contains more then 64 bits, only the lowest 64 bits + * are returned. + * @return uint64_t representation of value + */ +IA_CSS_RBM_STORAGE_CLASS_H +uint64_t ia_css_rbm_to_uint64( + const ia_css_rbm_t value); + +/** Creates a routing bitmap with the bit at index 'index' removed. + * @return ~(1 << index) & bitmap + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_unset( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Create a onehot routing bitmap with a bit set at index + * @return bitmap[index] = 1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_bit_mask( + const unsigned int index); + +#ifdef __IA_CSS_RBM_INLINE__ +#include "ia_css_rbm_impl.h" +#endif /* __IA_CSS_RBM_INLINE__ */ + +#endif /* __IA_CSS_RBM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h new file mode 100644 index 000000000000..f497a7de90a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h @@ -0,0 +1,133 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_MANIFEST_H +#define __IA_CSS_RBM_MANIFEST_H + +#include "type_support.h" +#include "ia_css_rbm_manifest_types.h" + +/** Returns the descriptor size of the RBM manifest. + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_size(void); + +/** Initializes the RBM manifest. + * @param rbm[in] Routing bitmap. + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +void +ia_css_rbm_manifest_init(struct ia_css_rbm_manifest_s *rbm); + +/** Returns a pointer to the array of mux descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_mux_desc_t * +ia_css_rbm_manifest_get_muxes(const ia_css_rbm_manifest_t *manifest); + +/** Returns the size of mux descriptors array. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_mux_count(const ia_css_rbm_manifest_t *manifest); + +/** Returns a pointer to the array of validation descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_validation_rule_t * +ia_css_rbm_manifest_get_validation_rules(const ia_css_rbm_manifest_t *manifest); + +/** Returns the size of the validation descriptor array. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_validation_rule_count(const ia_css_rbm_manifest_t *manifest); + +/** Returns a pointer to the array of terminal routing descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_terminal_routing_desc_t * +ia_css_rbm_manifest_get_terminal_routing_desc(const ia_css_rbm_manifest_t *manifest); + +/** \brief Returns the size of the terminal routing descriptor array. + * Note: pretty printing differs from on host and on IPU. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_terminal_routing_desc_count(const ia_css_rbm_manifest_t *manifest); + +/** Pretty prints the routing bitmap manifest. + * @param manifest[in] Routing bitmap manifest. + */ +void +ia_css_rbm_manifest_print(const ia_css_rbm_manifest_t *manifest); + +/** \brief Pretty prints a RBM (routing bitmap). + * Note: pretty printing differs from on host and on IPU. + * @param rbm[in] Routing bitmap. + * @param mux[in] List of mux descriptors corresponding to rbm. + * @param mux_desc_count[in] Number of muxes in list mux. + */ +void +ia_css_rbm_pretty_print( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count); + +/** \brief check for the validity of a routing bitmap. + * @param manifest[in] Routing bitmap manifest. + * @param rbm[in] Routing bitmap + * @return true on match. + */ +bool +ia_css_rbm_manifest_check_rbm_validity( + const ia_css_rbm_manifest_t *manifest, + const ia_css_rbm_t *rbm); + +/** \brief sets, using manifest info, the value of a mux in the routing bitmap. + * @param rbm[in] Routing bitmap. + * @param mux[in] List of mux descriptors corresponding to rbm. + * @param mux_count[in] Number of muxes in list mux. + * @param gp_dev_id[in] ID of sub system (PSA/ISA) where the mux is located. + * @param mux_id[in] ID of mux to set configuration for. + * @param value[in] Value of the mux. + * @return routing bitmap. + */ +ia_css_rbm_t +ia_css_rbm_set_mux( + ia_css_rbm_t rbm, + ia_css_rbm_mux_desc_t *mux, + unsigned int mux_count, + unsigned int gp_dev_id, + unsigned int mux_id, + unsigned int value); + +#ifdef __IA_CSS_RBM_MANIFEST_INLINE__ +#include "ia_css_rbm_manifest_impl.h" +#endif /* __IA_CSS_RBM_MANIFEST_INLINE__ */ + +#endif /* __IA_CSS_RBM_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h new file mode 100644 index 000000000000..ade20446b9f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_MANIFEST_TYPES_H +#define __IA_CSS_RBM_MANIFEST_TYPES_H + +#include "ia_css_rbm.h" +#include "vied_nci_psys_resource_model.h" + +#ifndef VIED_NCI_RBM_MAX_MUX_COUNT +#error Please define VIED_NCI_RBM_MAX_MUX_COUNT +#endif +#ifndef VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT +#error Please define VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT +#endif +#ifndef VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT +#error Please define VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT +#endif +#ifndef N_PADDING_UINT8_IN_RBM_MANIFEST +#error Please define N_PADDING_UINT8_IN_RBM_MANIFEST +#endif + +#define SIZE_OF_RBM_MUX_DESC_S ( \ + (4 * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_mux_desc_s { + uint8_t gp_dev_id; + uint8_t mux_id; + uint8_t offset; + uint8_t size_bits; +} ia_css_rbm_mux_desc_t; + +#define SIZE_OF_RBM_VALIDATION_RULE_DESC_S ( \ + (2 * IA_CSS_RBM_BITS) \ + + (1 * IA_CSS_UINT32_T_BITS)) + +typedef struct ia_css_rbm_validation_rule_s { + ia_css_rbm_t match; /* RBM is an array of 32 bit elements */ + ia_css_rbm_t mask; + uint32_t expected_value; +} ia_css_rbm_validation_rule_t; + +#define SIZE_OF_RBM_TERMINAL_ROUTING_DESC_S ( \ + (4 * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_terminal_routing_desc_s { + uint8_t terminal_id; + uint8_t connection_state; + uint8_t mux_id; + uint8_t state; +} ia_css_rbm_terminal_routing_desc_t; + +#define SIZE_OF_RBM_MANIFEST_S ( \ + (VIED_NCI_RBM_MAX_MUX_COUNT * SIZE_OF_RBM_MUX_DESC_S) \ + + (VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT * SIZE_OF_RBM_VALIDATION_RULE_DESC_S) \ + + (VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT * SIZE_OF_RBM_TERMINAL_ROUTING_DESC_S) \ + + (3 * IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_RBM_MANIFEST * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_manifest_s { +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT > 0 + ia_css_rbm_validation_rule_t + validation_rules[VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT]; +#endif + uint16_t mux_desc_count; + uint16_t validation_rule_count; + uint16_t terminal_routing_desc_count; + +#if VIED_NCI_RBM_MAX_MUX_COUNT > 0 + ia_css_rbm_mux_desc_t + mux_desc[VIED_NCI_RBM_MAX_MUX_COUNT]; +#endif + +#if VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT > 0 + ia_css_rbm_terminal_routing_desc_t + terminal_routing_desc[VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT]; +#endif + +#if N_PADDING_UINT8_IN_RBM_MANIFEST > 0 + uint8_t padding[N_PADDING_UINT8_IN_RBM_MANIFEST]; +#endif +} ia_css_rbm_manifest_t; + +#endif /* __IA_CSS_RBM_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h new file mode 100644 index 000000000000..9548e9a9fabb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_STORAGE_CLASS_H +#define __IA_CSS_RBM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_RBM_INLINE__ +#define IA_CSS_RBM_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_RBM_STORAGE_CLASS_C +#else +#define IA_CSS_RBM_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_RBM_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#ifndef __IA_CSS_RBM_MANIFEST_INLINE__ +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +#else +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_RBM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h new file mode 100644 index 000000000000..dd060323da5c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h @@ -0,0 +1,77 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_TRACE_H +#define __IA_CSS_RBM_TRACE_H + +#include "ia_css_trace.h" + +/* Not using 0 to identify wrong configuration being passed from the .mk file outside. +* Log levels not in the range below will cause a "No RBM_TRACE_CONFIG Tracing level defined" +*/ +#define RBM_TRACE_LOG_LEVEL_OFF 1 +#define RBM_TRACE_LOG_LEVEL_NORMAL 2 +#define RBM_TRACE_LOG_LEVEL_DEBUG 3 + +#define RBM_TRACE_CONFIG_DEFAULT RBM_TRACE_LOG_LEVEL_NORMAL + +#if !defined(RBM_TRACE_CONFIG) +# define RBM_TRACE_CONFIG RBM_TRACE_CONFIG_DEFAULT +#endif + +/* IPU_RESOURCE Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(RBM_TRACE_CONFIG)) +/* Module specific trace setting */ +# if RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_OFF +/* RBM_TRACE_LOG_LEVEL_OFF */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +# elif RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_NORMAL +/* RBM_TRACE_LOG_LEVEL_NORMAL */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +# elif RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_DEBUG +/* RBM_TRACE_LOG_LEVEL_DEBUG */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +# else +# error "No RBM_TRACE_CONFIG Tracing level defined" +# endif +#else +# error "RBM_TRACE_CONFIG not defined" +#endif + +#endif /* __RBM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk new file mode 100644 index 000000000000..f4251f9740fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk @@ -0,0 +1,39 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifdef _H_ROUTING_BITMAP_MK +$(error ERROR: routing_bitmap.mk included multiple times, please check makefile) +else +_H_ROUTING_BITMAP_MK=1 +endif + +ROUTING_BITMAP_FILES += $(ROUTING_BITMAP_DIR)/src/ia_css_rbm_manifest.c + +ROUTING_BITMAP_DIR = $(MODULES_DIR)/routing_bitmap +ROUTING_BITMAP_INTERFACE = $(ROUTING_BITMAP_DIR)/interface +ROUTING_BITMAP_SOURCES = $(ROUTING_BITMAP_DIR)/src + +ROUTING_BITMAP_CPPFLAGS = -I$(ROUTING_BITMAP_INTERFACE) +ROUTING_BITMAP_CPPFLAGS += -I$(ROUTING_BITMAP_SOURCES) + +ifeq ($(ROUTING_BITMAP_INLINE),1) +ROUTING_BITMAP_CPPFLAGS += -D__IA_CSS_RBM_INLINE__ +else +ROUTING_BITMAP_FILES += $(ROUTING_BITMAP_DIR)/src/ia_css_rbm.c +endif + +ifeq ($(ROUTING_BITMAP_MANIFEST_INLINE),1) +ROUTING_BITMAP_CPPFLAGS += -D__IA_CSS_RBM_MANIFEST_INLINE__ +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c new file mode 100644 index 000000000000..bc5bf14efbd7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_INLINE__ +#include "ia_css_rbm_impl.h" +#endif /* __IA_CSS_RBM_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h new file mode 100644 index 000000000000..c8cd78d416a1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h @@ -0,0 +1,338 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm.h" +#include "type_support.h" +#include "misc_support.h" +#include "assert_support.h" +#include "math_support.h" +#include "ia_css_rbm_trace.h" + +STORAGE_CLASS_INLINE int ia_css_rbm_compute_weight( + const ia_css_rbm_t bitmap); + +STORAGE_CLASS_INLINE ia_css_rbm_t ia_css_rbm_shift( + const ia_css_rbm_t bitmap); + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_intersection_empty( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + ia_css_rbm_t intersection; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_intersection_empty(): enter:\n"); + + intersection = ia_css_rbm_intersection(bitmap0, bitmap1); + return ia_css_is_rbm_empty(intersection); +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_empty( + const ia_css_rbm_t bitmap) +{ + unsigned int i; + bool is_empty = true; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_empty(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + is_empty &= bitmap.data[i] == 0; + } + return is_empty; +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_equal( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + bool is_equal = true; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_equal(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + is_equal = is_equal && (bitmap0.data[i] == bitmap1.data[i]); + } + return is_equal; +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_subset( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + ia_css_rbm_t intersection; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_subset(): enter:\n"); + + intersection = ia_css_rbm_intersection(bitmap0, bitmap1); + return ia_css_is_rbm_equal(intersection, bitmap1); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_clear(void) +{ + unsigned int i; + ia_css_rbm_t bitmap; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_clear(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + bitmap.data[i] = 0; + } + return bitmap; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_complement( + const ia_css_rbm_t bitmap) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_complement(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = ~bitmap.data[i]; + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_union( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_union(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] | bitmap1.data[i]); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_intersection( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_intersection(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] & bitmap1.data[i]); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + ia_css_rbm_t bit_mask; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_set(): enter:\n"); + + bit_mask = ia_css_rbm_bit_mask(index); + return ia_css_rbm_union(bitmap, bit_mask); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_create_from_uint64( + const uint64_t value) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_create_from_uint64(): enter:\n"); + + result = ia_css_rbm_clear(); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + /* masking is done implictly, the MSB bits of casting will be chopped off */ + result.data[i] = (IA_CSS_RBM_ELEM_TYPE) + (value >> (i * IA_CSS_RBM_ELEM_BITS)); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +uint64_t ia_css_rbm_to_uint64( + const ia_css_rbm_t value) +{ + const unsigned int bits64 = sizeof(uint64_t) * 8; + const unsigned int nof_elems_bits64 = bits64 / IA_CSS_RBM_ELEM_BITS; + unsigned int i; + uint64_t res = 0; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_to_uint64(): enter:\n"); + + assert((bits64 % IA_CSS_RBM_ELEM_BITS) == 0); + assert(nof_elems_bits64 > 0); + + for (i = 0; i < MIN(IA_CSS_RBM_NOF_ELEMS, nof_elems_bits64); i++) { + res |= ((uint64_t)(value.data[i]) << (i * IA_CSS_RBM_ELEM_BITS)); + } + for (i = nof_elems_bits64; i < IA_CSS_RBM_NOF_ELEMS; i++) { + assert(value.data[i] == 0); + } + return res; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_unset( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_unset(): enter:\n"); + + result = ia_css_rbm_bit_mask(index); + result = ia_css_rbm_complement(result); + return ia_css_rbm_intersection(bitmap, result); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_bit_mask( + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + ia_css_rbm_t bit_mask = ia_css_rbm_clear(); + + assert(index < IA_CSS_RBM_BITS); + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_bit_mask(): enter:\n"); + if (index < IA_CSS_RBM_BITS) { + elem_index = index / IA_CSS_RBM_ELEM_BITS; + elem_bit_index = index % IA_CSS_RBM_ELEM_BITS; + assert(elem_index < IA_CSS_RBM_NOF_ELEMS); + + bit_mask.data[elem_index] = 1 << elem_bit_index; + } + return bit_mask; +} + +STORAGE_CLASS_INLINE +int ia_css_rbm_compute_weight( + const ia_css_rbm_t bitmap) +{ + ia_css_rbm_t loc_bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_compute_weight(): enter:\n"); + + loc_bitmap = bitmap; + + /* In fact; do not need the iterator "i" */ + for (i = 0; (i < IA_CSS_RBM_BITS) && + !ia_css_is_rbm_empty(loc_bitmap); i++) { + weight += ia_css_is_rbm_set(loc_bitmap, 0); + loc_bitmap = ia_css_rbm_shift(loc_bitmap); + } + + return weight; +} + +IA_CSS_RBM_STORAGE_CLASS_C +int ia_css_is_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_set(): enter:\n"); + + assert(index < IA_CSS_RBM_BITS); + + elem_index = index / IA_CSS_RBM_ELEM_BITS; + elem_bit_index = index % IA_CSS_RBM_ELEM_BITS; + assert(elem_index < IA_CSS_RBM_NOF_ELEMS); + return (((bitmap.data[elem_index] >> elem_bit_index) & 0x1) == 1); +} + +STORAGE_CLASS_INLINE +ia_css_rbm_t ia_css_rbm_shift( + const ia_css_rbm_t bitmap) +{ + int i; + unsigned int lsb_current_elem = 0; + unsigned int lsb_previous_elem = 0; + ia_css_rbm_t loc_bitmap; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_shift(): enter:\n"); + + loc_bitmap = bitmap; + + for (i = IA_CSS_RBM_NOF_ELEMS - 1; i >= 0; i--) { + lsb_current_elem = bitmap.data[i] & 0x01; + loc_bitmap.data[i] >>= 1; + loc_bitmap.data[i] |= (lsb_previous_elem << (IA_CSS_RBM_ELEM_BITS - 1)); + lsb_previous_elem = lsb_current_elem; + } + return loc_bitmap; +} + +IA_CSS_RBM_STORAGE_CLASS_C +int ia_css_rbm_print( + const ia_css_rbm_t bitmap, + void *fid) +{ + int retval = -1; + int bit; + unsigned int bit_index = 0; + ia_css_rbm_t loc_bitmap; + + IA_CSS_TRACE_0(RBM, INFO, + "ia_css_rbm_print(): enter:\n"); + + NOT_USED(fid); + NOT_USED(bit); + + IA_CSS_TRACE_0(RBM, INFO, "kernel bitmap {\n"); + + loc_bitmap = bitmap; + + for (bit_index = 0; (bit_index < IA_CSS_RBM_BITS) && + !ia_css_is_rbm_empty(loc_bitmap); bit_index++) { + + bit = ia_css_is_rbm_set(loc_bitmap, 0); + loc_bitmap = ia_css_rbm_shift(loc_bitmap); + IA_CSS_TRACE_2(RBM, INFO, "\t%d\t = %d\n", bit_index, bit); + } + IA_CSS_TRACE_0(RBM, INFO, "}\n"); + + retval = 0; + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c new file mode 100644 index 000000000000..ef3beb8760b6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c @@ -0,0 +1,224 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm_manifest.h" +#include "ia_css_rbm.h" +#include "type_support.h" +#include "misc_support.h" +#include "assert_support.h" +#include "math_support.h" +#include "ia_css_rbm_trace.h" + +#ifndef __IA_CSS_RBM_MANIFEST_INLINE__ +#include "ia_css_rbm_manifest_impl.h" +#endif /* __IA_CSS_RBM_MANIFEST_INLINE__ */ + +STORAGE_CLASS_INLINE void +ia_css_rbm_print_with_header( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count, + bool print_header) +{ +#ifdef __HIVECC + ia_css_rbm_print(*rbm, NULL); + (void)print_header; + (void)mux_desc_count; + (void)mux; +#else + int i, j; + + assert(mux != NULL); + assert(rbm != NULL); + if (mux == NULL || rbm == NULL) + return; + + if (print_header) { + for (i = mux_desc_count - 1; i >= 0; i--) { + PRINT("%*d|", mux[i].size_bits, mux[i].mux_id); + } + PRINT("\n"); + } + for (i = mux_desc_count - 1; i >= 0; i--) { + for (j = mux[i].size_bits - 1; j >= 0; j--) { + PRINT("%d", ia_css_is_rbm_set(*rbm, j + mux[i].offset)); + } + PRINT("|"); + } +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_rbm_validation_rule_print( + ia_css_rbm_validation_rule_t *rule, + ia_css_rbm_mux_desc_t *mux_desc, + unsigned int mux_desc_count, + bool print_header) +{ + ia_css_rbm_print_with_header(&rule->match, mux_desc, mux_desc_count, print_header); +#ifdef __HIVECC + IA_CSS_TRACE_0(RBM, INFO, "Mask\n"); +#else + PRINT("\t"); +#endif + ia_css_rbm_print_with_header(&rule->mask, mux_desc, mux_desc_count, false); +#ifdef __HIVECC + IA_CSS_TRACE_1(RBM, INFO, "Rule expected_value: %d\n", rule->expected_value); +#else + PRINT("\t%d\n", rule->expected_value); +#endif +} + +void +ia_css_rbm_pretty_print( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count) +{ + ia_css_rbm_print_with_header(rbm, mux, mux_desc_count, false); +#ifndef __HIVECC + PRINT("\n"); +#endif +} + +void +ia_css_rbm_manifest_print( + const ia_css_rbm_manifest_t *manifest) +{ + int retval = -1; + unsigned int i; + bool print_header = true; + ia_css_rbm_mux_desc_t *muxes; + ia_css_rbm_validation_rule_t *validation_rule; + ia_css_rbm_terminal_routing_desc_t *terminal_routing_desc; + + verifjmpexit(manifest != NULL); + muxes = ia_css_rbm_manifest_get_muxes(manifest); + verifjmpexit(muxes != NULL || manifest->mux_desc_count == 0); + + for (i = 0; i < manifest->mux_desc_count; i++) { + IA_CSS_TRACE_4(RBM, INFO, "id: %d.%d offstet: %d size_bits: %d\n", + muxes[i].gp_dev_id, + muxes[i].mux_id, + muxes[i].offset, + muxes[i].size_bits); + } +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT != 0 + validation_rule = ia_css_rbm_manifest_get_validation_rules(manifest); + verifjmpexit(validation_rule != NULL || manifest->validation_rule_count == 0); + + for (i = 0; i < manifest->validation_rule_count; i++) { + ia_css_rbm_validation_rule_print(&validation_rule[i], muxes, manifest->mux_desc_count, print_header); + print_header = false; + } +#else + (void) validation_rule; + (void) print_header; +#endif + terminal_routing_desc = ia_css_rbm_manifest_get_terminal_routing_desc(manifest); + verifjmpexit(terminal_routing_desc != NULL || manifest->terminal_routing_desc_count == 0); + for (i = 0; i < manifest->terminal_routing_desc_count; i++) { + IA_CSS_TRACE_4(RBM, INFO, "terminal_id: %d connection_state: %d mux_id: %d state: %d\n", + terminal_routing_desc[i].terminal_id, + terminal_routing_desc[i].connection_state, + terminal_routing_desc[i].mux_id, + terminal_routing_desc[i].state); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(RBM, ERROR, "ia_css_rbm_manifest_print failed\n"); + } +} + +bool +ia_css_rbm_manifest_check_rbm_validity( + const ia_css_rbm_manifest_t *manifest, + const ia_css_rbm_t *rbm) +{ + unsigned int i; + ia_css_rbm_t res; + ia_css_rbm_t final_rbm = ia_css_rbm_clear(); + ia_css_rbm_validation_rule_t *rules; + bool matches_rules; + + verifjmpexit(manifest != NULL); + verifjmpexit(rbm != NULL); + + if (ia_css_is_rbm_empty(*rbm)) { + IA_CSS_TRACE_0(RBM, ERROR, "ia_css_rbm_manifest_check_rbm_validity failes: RBM is empty.\n"); + return false; + } + +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT != 0 + rules = ia_css_rbm_manifest_get_validation_rules(manifest); + verifjmpexit(rules != NULL || manifest->validation_rule_count == 0); + + for (i = 0; i < manifest->validation_rule_count; i++) { + res = ia_css_rbm_intersection(*rbm, rules[i].mask); + matches_rules = ia_css_is_rbm_equal(res, rules[i].match); + + if (!matches_rules) + continue; + + if (rules[i].expected_value == 1) { + final_rbm = ia_css_rbm_union(final_rbm, res); + } else { + IA_CSS_TRACE_1(RBM, INFO, "ia_css_rbm_manifest_check_rbm_validity failes on rule %d\n", 1); + return false; + } + } +#else + (void)matches_rules; + (void)i; + (void)rules; + (void)res; +#endif + return ia_css_is_rbm_equal(final_rbm, *rbm); +EXIT: + return false; +} + +ia_css_rbm_t +ia_css_rbm_set_mux( + ia_css_rbm_t rbm, + ia_css_rbm_mux_desc_t *mux, + unsigned int mux_count, + unsigned int gp_dev_id, + unsigned int mux_id, + unsigned int value) +{ + unsigned int i; + + verifjmpexit(mux != NULL); + + for (i = 0; i < mux_count; i++) { + if (mux[i].gp_dev_id == gp_dev_id && mux[i].mux_id == mux_id) + break; + } + if (i >= mux_count) { + IA_CSS_TRACE_2(RBM, ERROR, + "ia_css_rbm_set_mux mux with mux_id %d.%d not found\n", gp_dev_id, mux_id); + return rbm; + } + if (value >= mux[i].size_bits) { + IA_CSS_TRACE_3(RBM, ERROR, + "ia_css_rbm_set_mux mux mux_id %d.%d, value %d illegal\n", gp_dev_id, mux_id, value); + return rbm; + } + rbm = ia_css_rbm_set(rbm, mux[i].offset + value); +EXIT: + return rbm; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h new file mode 100644 index 000000000000..7059b6bc898e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h @@ -0,0 +1,108 @@ + + +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm_manifest.h" +#include "ia_css_rbm_trace.h" + +#include "type_support.h" +#include "math_support.h" +#include "error_support.h" +#include "assert_support.h" +#include "print_support.h" + +STORAGE_CLASS_INLINE +void __ia_css_rbm_manifest_check_struct(void) +{ + COMPILATION_ERROR_IF( + sizeof(ia_css_rbm_manifest_t) != (SIZE_OF_RBM_MANIFEST_S / IA_CSS_UINT8_T_BITS)); + COMPILATION_ERROR_IF( + (sizeof(ia_css_rbm_manifest_t) % 8 /* 64 bit */) != 0); +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_size(void) +{ + unsigned int size = sizeof(struct ia_css_rbm_manifest_s); + + return ceil_mul(size, sizeof(uint64_t)); +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +void +ia_css_rbm_manifest_init(struct ia_css_rbm_manifest_s *rbm) +{ + rbm->mux_desc_count = 0; + rbm->terminal_routing_desc_count = 0; + rbm->validation_rule_count = 0; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_mux_desc_t * +ia_css_rbm_manifest_get_muxes(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_MUX_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_mux_desc_t *)manifest->mux_desc; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_mux_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->mux_desc_count; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_validation_rule_t * +ia_css_rbm_manifest_get_validation_rules(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_validation_rule_t *)manifest->validation_rules; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_validation_rule_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->validation_rule_count; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_terminal_routing_desc_t * +ia_css_rbm_manifest_get_terminal_routing_desc(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_terminal_routing_desc_t *)manifest->terminal_routing_desc; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_terminal_routing_desc_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->terminal_routing_desc_count; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/assert_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/assert_support.h new file mode 100644 index 000000000000..f904a494b53c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/assert_support.h @@ -0,0 +1,197 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ASSERT_SUPPORT_H +#define __ASSERT_SUPPORT_H + +/* This file provides support for run-time assertions + * and compile-time assertions. + * + * Run-time asstions are provided via the following syntax: + * assert(condition) + * Run-time assertions are disabled using the NDEBUG flag. + * + * Compile time assertions are provided via the following syntax: + * COMPILATION_ERROR_IF(condition); + * A compile-time assertion will fail to compile if the condition is false. + * The condition must be constant, such that it can be evaluated + * at compile time. + * + * OP___assert is deprecated. + */ + +#define IA_CSS_ASSERT(expr) assert(expr) + +#ifdef __KLOCWORK__ +/* Klocwork does not see that assert will lead to abortion + * as there is no good way to tell this to KW and the code + * should not depend on assert to function (actually the assert + * could be disabled in a release build) it was decided to + * disable the assert for KW scans (by defining NDEBUG) + */ +#define NDEBUG +#endif /* __KLOCWORK__ */ + +/** + * The following macro can help to test the size of a struct at compile + * time rather than at run-time. It does not work for all compilers; see + * below. + * + * Depending on the value of 'condition', the following macro is expanded to: + * - condition==true: + * an expression containing an array declaration with negative size, + * usually resulting in a compilation error + * - condition==false: + * (void) 1; // C statement with no effect + * + * example: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * SIZE_OF_HOST_SP_QUEUES_STRUCT); + * + * verify that the macro indeed triggers a compilation error with your compiler: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * (sizeof(struct host_sp_queues)+1) ); + * + * Not all compilers will trigger an error with this macro; + * use a search engine to search for BUILD_BUG_ON to find other methods. + */ +#define COMPILATION_ERROR_IF(condition) \ +((void)sizeof(char[1 - 2*!!(condition)])) + +/* Compile time assertion */ +#ifndef CT_ASSERT +#define CT_ASSERT(cnd) ((void)sizeof(char[(cnd)?1 : -1])) +#endif /* CT_ASSERT */ + +#ifdef NDEBUG + +#define assert(cnd) ((void)0) + +#else + +#include "storage_class.h" + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE +/* Windows kernel mode compilation */ +#include +#define assert(cnd) ASSERT(cnd) +#else +/* Windows usermode compilation */ +#include +#endif + +#elif defined(__HIVECC) + +/* + * target: assert disabled + * sched: assert enabled only when SCHED_DEBUG is defined + * unsched: assert enabled + */ +#if defined(HRT_HW) +#define assert(cnd) ((void)0) +#elif defined(HRT_SCHED) && !defined(DEBUG_SCHED) +#define assert(cnd) ((void)0) +#elif defined(PIPE_GENERATION) +#define assert(cnd) ((void)0) +#else +#include +#define assert(cnd) OP___csim_assert(cnd) +#endif + +#elif defined(__KERNEL__) +#include + +#ifndef KERNEL_ASSERT_TO_BUG +#ifndef KERNEL_ASSERT_TO_BUG_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#ifndef KERNEL_ASSERT_UNDEFINED +/* Default */ +#define KERNEL_ASSERT_TO_BUG +#endif /*KERNEL_ASSERT_UNDEFINED*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG +/* TODO: it would be cleaner to use this: + * #define assert(cnd) BUG_ON(cnd) + * but that causes many compiler warnings (==errors) under Android + * because it seems that the BUG_ON() macro is not seen as a check by + * gcc like the BUG() macro is. */ +#define assert(cnd) \ + do { \ + if (!(cnd)) { \ + BUG(); \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG_ON +#define assert(cnd) BUG_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON +#define assert(cnd) WARN_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#define assert(cnd) \ + do { \ + int not_cnd = !(cnd); \ + WARN_ON(not_cnd); \ + if (not_cnd) { \ + for (;;) { \ + } \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ + +#ifdef KERNEL_ASSERT_UNDEFINED +#include KERNEL_ASSERT_DEFINITION_FILESTRING +#endif /*KERNEL_ASSERT_UNDEFINED*/ + +#elif defined(__FIST__) || defined(__GNUC__) + +#include "assert.h" + +#else /* default is for unknown environments */ +#define assert(cnd) ((void)0) +#endif + +#endif /* NDEBUG */ + +#ifndef PIPE_GENERATION +/* Deprecated OP___assert, this is still used in ~1000 places + * in the code. This will be removed over time. + * The implementation for the pipe generation tool is in see support.isp.h */ +#define OP___assert(cnd) assert(cnd) + +#ifdef C_RUN +#define compile_time_assert(cond) OP___assert(cond) +#else +#include "storage_class.h" +extern void _compile_time_assert(void); +STORAGE_CLASS_INLINE void compile_time_assert(unsigned cond) +{ + /* Call undefined function if cond is false */ + if (!cond) + _compile_time_assert(); +} +#endif +#endif /* PIPE_GENERATION */ + +#endif /* __ASSERT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/cpu_mem_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/cpu_mem_support.h new file mode 100644 index 000000000000..fa349cac4b24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/cpu_mem_support.h @@ -0,0 +1,233 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __CPU_MEM_SUPPORT_H +#define __CPU_MEM_SUPPORT_H + +#include "storage_class.h" +#include "assert_support.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_copy(void *dst, const void *src, unsigned int size) +{ + /* memcpy cannot be used in in Windows (function is not allowed), + * and the safer function memcpy_s is not available on other platforms. + * Because usage of ia_css_cpu_mem_copy is minimal, we implement it here in an easy, + * but sub-optimal way. + */ + unsigned int i; + + assert(dst != NULL && src != NULL); + + if (!(dst != NULL && src != NULL)) { + return NULL; + } + for (i = 0; i < size; i++) { + ((char *)dst)[i] = ((char *)src)[i]; + } + return dst; +} + +#if defined(__KERNEL__) + +#include +#include +#include +#include + +/* TODO: remove, workaround for issue in hrt file ibuf_ctrl_2600_config.c + * error checking code added to SDK that uses calls to exit function + */ +#define exit(a) return + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + return ia_css_cpu_mem_alloc(size); /* todo: align to page size */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_protect(void *ptr, unsigned int size, int prot) +{ + /* nothing here yet */ +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); /* available in kernel in linux/string.h */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + kfree(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* parameter check here */ + if (ptr == NULL) + return; + + clflush_cache_range(ptr, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* for now same as flush */ + ia_css_cpu_mem_cache_flush(ptr, size); +} + +#elif defined(_MSC_VER) + +#include +#include +#include + +extern void *hrt_malloc(size_t bytes, int zero_mem); +extern void *hrt_free(void *ptr); +extern void hrt_mem_cache_flush(void *ptr, unsigned int size); +extern void hrt_mem_cache_invalidate(void *ptr, unsigned int size); + +#define malloc(a) hrt_malloc(a, 1) +#define free(a) hrt_free(a) + +#define CSS_PAGE_SIZE (1<<12) + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + unsigned int buffer_size = size; + + /* Currently hrt_malloc calls Windows ExAllocatePoolWithTag() routine + * to request system memory. If the number of bytes is equal or bigger + * than the page size, then the returned address is page aligned, + * but if it's smaller it's not necessarily page-aligned We agreed + * with Windows team that we allocate a full page + * if it's less than page size + */ + if (buffer_size < CSS_PAGE_SIZE) + buffer_size = CSS_PAGE_SIZE; + + return ia_css_cpu_mem_alloc(buffer_size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_flush(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_invalidate(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +#else + +#include +#include +#include +/* Needed for the MPROTECT */ +#include +#include +#include +#include + + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + int pagesize; + + pagesize = sysconf(_SC_PAGE_SIZE); + return memalign(pagesize, size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +#endif + +#endif /* __CPU_MEM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/error_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/error_support.h new file mode 100644 index 000000000000..9fe1f65125e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/error_support.h @@ -0,0 +1,110 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ERROR_SUPPORT_H +#define __ERROR_SUPPORT_H + +#if defined(__KERNEL__) +#include +#else +#include +#endif +#include + +/* OS-independent definition of IA_CSS errno values */ +/* #define IA_CSS_EINVAL 1 */ +/* #define IA_CSS_EFAULT 2 */ + +#ifdef __HIVECC +#define ERR_EMBEDDED 1 +#else +#define ERR_EMBEDDED 0 +#endif + +#if ERR_EMBEDDED +#define DECLARE_ERRVAL +#else +#define DECLARE_ERRVAL \ + int _errval = 0; +#endif + +/* Use "owl" in while to prevent compiler warnings in Windows */ +#define ALWAYS_FALSE ((void)0, 0) + +#define verifret(cond, error_type) \ +do { \ + if (!(cond)) { \ + return error_type; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmp(cond, error_tag) \ +do { \ + if (!(cond)) { \ + goto error_tag; \ + } \ +} while (ALWAYS_FALSE) + +#define verifexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#if ERR_EMBEDDED +#define verifexitval(cond, error_tag) \ +do { \ + assert(cond); \ +} while (ALWAYS_FALSE) +#else +#define verifexitval(cond, error_tag) \ +do { \ + if (!(cond)) { \ + _errval = (error_tag); \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) +#endif + +#if ERR_EMBEDDED +#define haserror(error_tag) (0) +#else +#define haserror(error_tag) \ + (_errval == (error_tag)) +#endif + +#if ERR_EMBEDDED +#define noerror() (1) +#else +#define noerror() \ + (_errval == 0) +#endif + +#define verifjmpexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmpexitsetretval(cond, retval) \ +do { \ + if (!(cond)) { \ + retval = -1; \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#endif /* __ERROR_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/math_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/math_support.h new file mode 100644 index 000000000000..633f86f1a1b0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/math_support.h @@ -0,0 +1,314 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MATH_SUPPORT_H +#define __MATH_SUPPORT_H + +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "type_support.h" +#include "assert_support.h" + +/* in case we have min/max/MIN/MAX macro's undefine them */ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#ifdef MIN /* also defined in include/hrt/numeric.h from SDK */ +#undef MIN +#endif +#ifdef MAX +#undef MAX +#endif + +#ifndef UINT16_MAX +#define UINT16_MAX (0xffffUL) +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffffUL) +#endif + +#define IS_ODD(a) ((a) & 0x1) +#define IS_EVEN(a) (!IS_ODD(a)) +#define IS_POWER2(a) (!((a)&((a)-1))) +#define IS_MASK_BITS_SET(a, b) ((a & b) != 0) + +/*To Find next power of 2 number from x */ +#define bit2(x) ((x) | ((x) >> 1)) +#define bit4(x) (bit2(x) | (bit2(x) >> 2)) +#define bit8(x) (bit4(x) | (bit4(x) >> 4)) +#define bit16(x) (bit8(x) | (bit8(x) >> 8)) +#define bit32(x) (bit16(x) | (bit16(x) >> 16)) +#define NEXT_POWER_OF_2(x) (bit32(x-1) + 1) + +/* force a value to a lower even value */ +#define EVEN_FLOOR(x) ((x) & ~1UL) + +/* A => B */ +#define IMPLIES(a, b) (!(a) || (b)) + +/* The ORIG_BITS th bit is the sign bit */ +/* Sign extends a ORIG_BITS bits long signed number to a 64-bit signed number */ +/* By type casting it can relimited to any valid type-size + * (32-bit signed or 16-bit or 8-bit) + */ +/* By masking it can be transformed to any arbitrary bit size */ +#define SIGN_EXTEND(VAL, ORIG_BITS) \ +((~(((VAL)&(1ULL<<((ORIG_BITS)-1)))-1))|(VAL)) + +#define EXTRACT_BIT(a, b) ((a >> b) & 1) + +/* for preprocessor and array sizing use MIN and MAX + otherwise use min and max */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CLIP(a, b, c) MIN((MAX((a), (b))), (c)) +/* Integer round-down division of a with b */ +#define FLOOR_DIV(a, b) ((b) ? ((a) / (b)) : 0) +/* Align a to the lower multiple of b */ +#define FLOOR_MUL(a, b) (FLOOR_DIV(a, b) * (b)) +/* Integer round-up division of a with b */ +#define CEIL_DIV(a, b) ((b) ? (((a) + (b) - 1) / (b)) : 0) +/* Align a to the upper multiple of b */ +#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) +/* Align a to the upper multiple of b - fast implementation + * for cases when b=pow(2,n) + */ +#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) +/* integer round-up division of a with pow(2,b) */ +#define CEIL_SHIFT(a, b) (((a) + (1UL << (b)) - 1) >> (b)) +/* Align a to the upper multiple of pow(2,b) */ +#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) +/* Absolute difference of a and b */ +#define ABS_DIF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define ABS(a) ABS_DIF(a, 0) +/* Square of x */ +#define SQR(x) ((x)*(x)) +/* Integer round-half-down division of a nad b */ +#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0) +/* Align a to the round-half-down multiple of b */ +#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) + +#define MAX3(a, b, c) MAX((a), MAX((b), (c))) +#define MIN3(a, b, c) MIN((a), MIN((b), (c))) +#define MAX4(a, b, c, d) MAX((MAX((a), (b))), (MAX((c), (d)))) +#define MIN4(a, b, c, d) MIN((MIN((a), (b))), (MIN((c), (d)))) + +/* min and max should not be macros as they will evaluate their arguments twice. + if you really need a macro (e.g. for CPP or for initializing an array) + use MIN() and MAX(), otherwise use min() and max() */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a)))) +#endif + +#ifndef BYTES +#define BYTES(bit) (((bit)+7)/8) +#endif + +#if !defined(PIPE_GENERATION) +STORAGE_CLASS_INLINE unsigned int max_value_bits(unsigned int bits) +{ + return (bits == 0) ? 0 : ((2 * ((1 << ((bits) - 1)) - 1)) + 1); +} +STORAGE_CLASS_INLINE unsigned int max_value_bytes(unsigned int bytes) +{ + return max_value_bits(IA_CSS_UINT8_T_BITS * bytes); +} +STORAGE_CLASS_INLINE int max(int a, int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE int min(int a, int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE int clip(int a, int b, int c) +{ + return min(max(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int umax(unsigned int a, unsigned int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE unsigned int umin(unsigned int a, unsigned int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uclip(unsigned int a, unsigned int b, + unsigned int c) +{ + return umin(umax(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int ceil_div(unsigned int a, unsigned int b) +{ + return CEIL_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul(unsigned int a, unsigned int b) +{ + return CEIL_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul2(unsigned int a, unsigned int b) +{ + return CEIL_MUL2(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift_mul(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT_MUL(a, b); +} + +STORAGE_CLASS_INLINE int abs_dif(int a, int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uabs_dif(unsigned int a, unsigned int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_div(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_mul(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_pow2(uint32_t a) +{ + unsigned int retval = 0; + + if (IS_POWER2(a)) { + retval = (unsigned int)a; + } else { + unsigned int v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + retval = (unsigned int)(v+1); + } + return retval; +} + +STORAGE_CLASS_INLINE unsigned int floor_log2(uint32_t a) +{ + static const uint8_t de_bruijn[] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + uint32_t v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + return (unsigned int)de_bruijn[(v*0x07C4ACDDU)>>27]; +} + +/* Divide by small power of two */ +STORAGE_CLASS_INLINE unsigned int +udiv2_small_i(uint32_t a, uint32_t b) +{ + assert(b <= 2); + return a >> (b-1); +} + +/* optimized divide for small results + * a will be divided by b + * outbits is the number of bits needed for the result + * the smaller the cheaper the function will be. + * if the result doesn't fit in the number of output bits + * the result is incorrect and the function will assert + */ +STORAGE_CLASS_INLINE unsigned int +udiv_medium(uint32_t a, uint32_t b, unsigned outbits) +{ + int bit; + unsigned res = 0; + unsigned mask; + +#ifdef VOLCANO +#pragma ipu unroll +#endif + for (bit = outbits-1 ; bit >= 0; bit--) { + mask = 1<= (b<= c ? a+b-c : a+b); +} + +/* + * For SP and ISP, SDK provides the definition of OP_asp_slor. + * We need it only for host + */ +STORAGE_CLASS_INLINE unsigned int OP_asp_slor(int a, int b, int c) +{ + return ((a << c) | b); +} +#else +#include "hive/customops.h" +#endif /* !defined(__VIED_CELL) */ + +#endif /* !defined(PIPE_GENERATION) */ + +#if !defined(__KERNEL__) +#define clamp(a, min_val, max_val) MIN(MAX((a), (min_val)), (max_val)) +#endif /* !defined(__KERNEL__) */ + +#endif /* __MATH_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/misc_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/misc_support.h new file mode 100644 index 000000000000..a2c2729e946d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/misc_support.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MISC_SUPPORT_H +#define __MISC_SUPPORT_H + +/* suppress compiler warnings on unused variables */ +#ifndef NOT_USED +#define NOT_USED(a) ((void)(a)) +#endif + +/* Calculate the total bytes for pow(2) byte alignment */ +#define tot_bytes_for_pow2_align(pow2, cur_bytes) \ + ((cur_bytes + (pow2 - 1)) & ~(pow2 - 1)) + +/* Display the macro value given a string */ +#define _STR(x) #x +#define STR(x) _STR(x) + +/* Concatenate */ +#ifndef CAT /* also defined in */ +#define _CAT(a, b) a ## b +#define CAT(a, b) _CAT(a, b) +#endif + +#define _CAT3(a, b, c) a ## b ## c +#define CAT3(a, b, c) _CAT3(a, b, c) + +/* NO_HOIST, NO_CSE, NO_ALIAS attributes must be ignored for host code */ +#ifndef __HIVECC +#ifndef NO_HOIST +#define NO_HOIST +#endif +#ifndef NO_CSE +#define NO_CSE +#endif +#ifndef NO_ALIAS +#define NO_ALIAS +#endif +#endif + +enum hive_method_id { + HIVE_METHOD_ID_CRUN, + HIVE_METHOD_ID_UNSCHED, + HIVE_METHOD_ID_SCHED, + HIVE_METHOD_ID_TARGET +}; + +/* Derive METHOD */ +#if defined(C_RUN) + #define HIVE_METHOD "crun" + #define HIVE_METHOD_ID HIVE_METHOD_ID_CRUN +#elif defined(HRT_UNSCHED) + #define HIVE_METHOD "unsched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_UNSCHED +#elif defined(HRT_SCHED) + #define HIVE_METHOD "sched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_SCHED +#else + #define HIVE_METHOD "target" + #define HIVE_METHOD_ID HIVE_METHOD_ID_TARGET + #define HRT_TARGET 1 +#endif + +#endif /* __MISC_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/platform_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/platform_support.h new file mode 100644 index 000000000000..1752efc7b4df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/platform_support.h @@ -0,0 +1,146 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PLATFORM_SUPPORT_H +#define __PLATFORM_SUPPORT_H + +#include "storage_class.h" + +#define MSEC_IN_SEC 1000 +#define NSEC_IN_MSEC 1000000 + +#if defined(_MSC_VER) +#include + +#define IA_CSS_EXTERN +#define SYNC_WITH(x) +#define CSS_ALIGN(d, a) _declspec(align(a)) d + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + /* Placeholder for driver team*/ +} + +STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) +{ + /* Placeholder for driver team*/ + (void)delay_time_ms; +} + +#elif defined(__HIVECC) +#include +#include + +#define IA_CSS_EXTERN extern +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + OP___schedule(); +} + +#elif defined(__KERNEL__) +#include +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __aligned(a) + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + usleep_range(1, 50); +} + +#elif defined(__GNUC__) +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) + +/* Define some __HIVECC specific macros to nothing to allow host code compilation */ +#ifndef NO_ALIAS +#define NO_ALIAS +#endif + +#ifndef SYNC_WITH +#define SYNC_WITH(x) +#endif + +#if defined(HRT_CSIM) + #include "hrt/host.h" /* Using hrt_sleep from hrt/host.h */ + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + /* For the SDK still using hrt_sleep */ + hrt_sleep(); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + /* For the SDK still using hrt_sleep */ + long unsigned int i = 0; + for (i = 0; i < delay_time_ms; i++) { + hrt_sleep(); + } + } +#else + #include + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + struct timespec delay_time; + + delay_time.tv_sec = 0; + delay_time.tv_nsec = 10; + nanosleep(&delay_time, NULL); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + struct timespec delay_time; + + if (delay_time_ms >= MSEC_IN_SEC) { + delay_time.tv_sec = delay_time_ms / MSEC_IN_SEC; + delay_time.tv_nsec = (delay_time_ms % MSEC_IN_SEC) * NSEC_IN_MSEC; + } else { + delay_time.tv_sec = 0; + delay_time.tv_nsec = delay_time_ms * NSEC_IN_MSEC; + } + nanosleep(&delay_time, NULL); + } +#endif + +#else +#include +#endif + +/*needed for the include in stdint.h for various environments */ +#include "type_support.h" +#include "storage_class.h" + +#define MAX_ALIGNMENT 8 +#define aligned_uint8(type, obj) CSS_ALIGN(uint8_t obj, 1) +#define aligned_int8(type, obj) CSS_ALIGN(int8_t obj, 1) +#define aligned_uint16(type, obj) CSS_ALIGN(uint16_t obj, 2) +#define aligned_int16(type, obj) CSS_ALIGN(int16_t obj, 2) +#define aligned_uint32(type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_int32(type, obj) CSS_ALIGN(int32_t obj, 4) + +/* needed as long as hivecc does not define the type (u)int64_t */ +#if defined(__HIVECC) +#define aligned_uint64(type, obj) CSS_ALIGN(unsigned long long obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(signed long long obj, 8) +#else +#define aligned_uint64(type, obj) CSS_ALIGN(uint64_t obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(int64_t obj, 8) +#endif +#define aligned_enum(enum_type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_struct(struct_type, obj) struct_type obj + +#endif /* __PLATFORM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/print_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/print_support.h new file mode 100644 index 000000000000..0b614f7ef12d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/print_support.h @@ -0,0 +1,90 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PRINT_SUPPORT_H +#define __PRINT_SUPPORT_H + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE + +/* TODO: Windows driver team to provide tracing mechanism for kernel mode + * e.g. DbgPrint and DbgPrintEx + */ +extern void FwTracePrintPWARN(const char *fmt, ...); +extern void FwTracePrintPRINT(const char *fmt, ...); +extern void FwTracePrintPERROR(const char *fmt, ...); +extern void FwTracePrintPDEBUG(const char *fmt, ...); + +#define PWARN(format, ...) FwTracePrintPWARN(format, __VA_ARGS__) +#define PRINT(format, ...) FwTracePrintPRINT(format, __VA_ARGS__) +#define PERROR(format, ...) FwTracePrintPERROR(format, __VA_ARGS__) +#define PDEBUG(format, ...) FwTracePrintPDEBUG(format, __VA_ARGS__) + +#else +/* Windows usermode compilation */ +#include + +/* To change the defines below, communicate with Windows team first + * to ensure they will not get flooded with prints + */ +/* This is temporary workaround to avoid flooding userspace + * Windows driver with prints + */ + +#define PWARN(format, ...) +#define PRINT(format, ...) +#define PERROR(format, ...) printf("error: " format, __VA_ARGS__) +#define PDEBUG(format, ...) + +#endif /* _KERNEL_MODE */ + +#elif defined(__HIVECC) +#include +/* To be revised + +#define PWARN(format) +#define PRINT(format) OP___printstring(format) +#define PERROR(variable) OP___dump(9999, arguments) +#define PDEBUG(variable) OP___dump(__LINE__, arguments) + +*/ + +#define PRINTSTRING(str) OP___printstring(str) + +#elif defined(__KERNEL__) +#include +#include + + +#define PWARN(format, arguments...) pr_debug(format, ##arguments) +#define PRINT(format, arguments...) pr_debug(format, ##arguments) +#define PERROR(format, arguments...) pr_debug(format, ##arguments) +#define PDEBUG(format, arguments...) pr_debug(format, ##arguments) + +#else +#include + +#define PRINT_HELPER(prefix, format, ...) printf(prefix format "%s", __VA_ARGS__) + +/* The trailing "" allows the edge case of printing single string */ +#define PWARN(...) PRINT_HELPER("warning: ", __VA_ARGS__, "") +#define PRINT(...) PRINT_HELPER("", __VA_ARGS__, "") +#define PERROR(...) PRINT_HELPER("error: ", __VA_ARGS__, "") +#define PDEBUG(...) PRINT_HELPER("debug: ", __VA_ARGS__, "") + +#define PRINTSTRING(str) PRINT(str) + +#endif + +#endif /* __PRINT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/storage_class.h new file mode 100644 index 000000000000..af19b4026220 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/storage_class.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __STORAGE_CLASS_H +#define __STORAGE_CLASS_H + +#define STORAGE_CLASS_EXTERN \ +extern + +#if defined(_MSC_VER) +#define STORAGE_CLASS_INLINE \ +static __inline +#elif defined(__HIVECC) +#define STORAGE_CLASS_INLINE \ +static inline +#else +#define STORAGE_CLASS_INLINE \ +static inline +#endif + +/* Register struct */ +#ifndef __register +#if defined(__HIVECC) && !defined(PIPE_GENERATION) +#define __register register +#else +#define __register +#endif +#endif + +/* Memory attribute */ +#ifndef MEM +#ifdef PIPE_GENERATION +#elif defined(__HIVECC) +#include +#else +#define MEM(any_mem) +#endif +#endif + +#endif /* __STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/type_support.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/type_support.h new file mode 100644 index 000000000000..a86da0e78941 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/support/type_support.h @@ -0,0 +1,80 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __TYPE_SUPPORT_H +#define __TYPE_SUPPORT_H + +/* Per the DLI spec, types are in "type_support.h" and + * "platform_support.h" is for unclassified/to be refactored + * platform specific definitions. + */ +#define IA_CSS_UINT8_T_BITS 8 +#define IA_CSS_UINT16_T_BITS 16 +#define IA_CSS_UINT32_T_BITS 32 +#define IA_CSS_INT32_T_BITS 32 +#define IA_CSS_UINT64_T_BITS 64 + + +#if defined(_MSC_VER) +#include +#include +#include +#include +#if defined(_M_X64) +#define HOST_ADDRESS(x) (unsigned long long)(x) +#else +#define HOST_ADDRESS(x) (unsigned long)(x) +#endif + +#elif defined(PARAM_GENERATION) +/* Nothing */ +#elif defined(__HIVECC) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif defined(__KERNEL__) +#include +#include + +#define CHAR_BIT (8) +#define HOST_ADDRESS(x) (unsigned long)(x) + +#elif defined(__GNUC__) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#else /* default is for the FIST environment */ +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#endif + +#if !defined(PIPE_GENERATION) && !defined(IO_GENERATION) +/* genpipe cannot handle the void* syntax */ +typedef void *HANDLE; +#endif + +#endif /* __TYPE_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h new file mode 100644 index 000000000000..5426d6d18e0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h @@ -0,0 +1,247 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_H +#define __IA_CSS_SYSCOM_H + + +/* + * The CSS Subsystem Communication Interface - Host side + * + * It provides subsystem initialzation, send ports and receive ports + * The PSYS and ISYS interfaces are implemented on top of this interface. + */ + +#include "ia_css_syscom_config.h" + +#define FW_ERROR_INVALID_PARAMETER (-1) +#define FW_ERROR_BAD_ADDRESS (-2) +#define FW_ERROR_BUSY (-3) +#define FW_ERROR_NO_MEMORY (-4) + +struct ia_css_syscom_context; + +/** + * ia_css_syscom_size() - provide syscom external buffer requirements + * @config: pointer to the configuration data (read) + * @size: pointer to the buffer size (write) + * + * Purpose: + * - Provide external buffer requirements + * - To be used for external buffer allocation + * + */ +extern void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size +); + +/** + * ia_css_syscom_open() - initialize a subsystem context + * @config: pointer to the configuration data (read) + * @buf: pointer to externally allocated buffers (read) + * @returns: struct ia_css_syscom_context* on success, 0 otherwise. + * + * Purpose: + * - initialize host side data structures + * - boot the subsystem? + * + */ +extern struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *config, + struct ia_css_syscom_buf *buf +); + +/** + * ia_css_syscom_close() - signal close to cell + * @context: pointer to the subsystem context + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if SPC is not ready yet. + * + * Purpose: + * Request from the Cell to terminate + */ +extern int +ia_css_syscom_close( + struct ia_css_syscom_context *context +); + +/** + * ia_css_syscom_release() - free context + * @context: pointer to the subsystem context + * @force: flag which specifies whether cell + * state will be checked before freeing the + * context. + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if cell + * is busy and call was not forced. + * + * Purpose: + * 2 modes, with first (force==true) immediately + * free context, and second (force==false) verifying + * that the cell state is ok and freeing context if so, + * returning error otherwise. + */ +extern int +ia_css_syscom_release( + struct ia_css_syscom_context *context, + unsigned int force +); + +/** + * Open a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be sent to a port without error. + * @context: pointer to the subsystem context + * @port: send port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Send a token to the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: send port index + * @token: pointer to the token value that is transferred to the subsystem + * @returns: number of tokens sent on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + const void *token +); + +/** + * Open a port for receiving tokens to the subsystem + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for receiving tokens to the subsystem + * Returns 0 on success, otherwise negative value of error code + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be received from a port without errors. + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Receive a token from the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: receive port index + * @token (output): pointer to (space for) the token to be received + * @returns: number of tokens received on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + void *token +); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/** + * ia_css_syscom_store_dmem() - store subsystem context information in DMEM + * @context: pointer to the subsystem context + * @ssid: subsystem id + * @vtl0_addr_mask: VTL0 address mask; only applicable when the passed in context is secure + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *context, + unsigned int ssid, + unsigned int vtl0_addr_mask +); + +/** + * ia_css_syscom_set_trustlet_status() - store truslet configuration setting + * @context: pointer to the subsystem context + * @trustlet_exist: 1 if trustlet exists + */ +extern void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +); + +/** + * ia_css_syscom_is_ab_spc_ready() - check if SPC access blocker programming is completed + * @context: pointer to the subsystem context + * @returns: 1 when status is ready. 0 otherwise + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +#endif /* __IA_CSS_SYSCOM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h new file mode 100644 index 000000000000..2f5eb309df94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h @@ -0,0 +1,97 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_H +#define __IA_CSS_SYSCOM_CONFIG_H + +#include +#include + +/* syscom size struct, output of ia_css_syscom_size, + * input for (external) allocation + */ +struct ia_css_syscom_size { + /* Size of host buffer */ + unsigned int cpu; + /* Size of shared config buffer (host to cell) */ + unsigned int shm; + /* Size of shared input queue buffers (host to cell) */ + unsigned int ibuf; + /* Size of shared output queue buffers (cell to host) */ + unsigned int obuf; +}; + +/* syscom buffer struct, output of (external) allocation, + * input for ia_css_syscom_open + */ +struct ia_css_syscom_buf { + char *cpu; /* host buffer */ + + /* shared memory buffer host address */ + host_virtual_address_t shm_host; + /* shared memory buffer cell address */ + vied_virtual_address_t shm_cell; + + /* input queue shared buffer host address */ + host_virtual_address_t ibuf_host; + /* input queue shared buffer cell address */ + vied_virtual_address_t ibuf_cell; + + /* output queue shared buffer host address */ + host_virtual_address_t obuf_host; + /* output queue shared buffer cell address */ + vied_virtual_address_t obuf_cell; +}; + +struct ia_css_syscom_queue_config { + unsigned int queue_size; /* tokens per queue */ + unsigned int token_size; /* bytes per token */ +}; + +/** + * Parameter struct for ia_css_syscom_open + */ +struct ia_css_syscom_config { + /* This member in no longer used in syscom. + It is kept to not break any driver builds, and will be removed when + all assignments have been removed from driver code */ + /* address of firmware in DDR/IMR */ + unsigned long long host_firmware_address; + + /* address of firmware in DDR, seen from SPC */ + unsigned int vied_firmware_address; + + unsigned int ssid; + unsigned int mmid; + + unsigned int num_input_queues; + unsigned int num_output_queues; + struct ia_css_syscom_queue_config *input; + struct ia_css_syscom_queue_config *output; + + unsigned int regs_addr; + unsigned int dmem_addr; + + /* firmware-specific configuration data */ + void *specific_addr; + unsigned int specific_size; + + /* if true; secure syscom in VTIO Case + * if false, non-secure syscom + */ + bool secure; + unsigned int vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h new file mode 100644 index 000000000000..2c32693c2a82 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h @@ -0,0 +1,51 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_SYSCOM_TRACE_H +#define __IA_CSS_SYSCOM_TRACE_H + +#include "ia_css_trace.h" + +#define SYSCOM_TRACE_LEVEL_DEFAULT 1 +#define SYSCOM_TRACE_LEVEL_DEBUG 2 + +/* Set to default level if no level is defined */ +#ifndef SYSCOM_TRACE_LEVEL +#define SYSCOM_TRACE_LEVEL SYSCOM_TRACE_LEVEL_DEFAULT +#endif /* SYSCOM_TRACE_LEVEL */ + +/* SYSCOM Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#define SYSCOM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + +#if (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEFAULT) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +#elif (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEBUG) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +#else +# error "Connection manager trace level not defined!" +#endif /* SYSCOM_TRACE_LEVEL */ + +#endif /* __IA_CSS_SYSCOM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom.c new file mode 100644 index 000000000000..cdf9df0531ff --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom.c @@ -0,0 +1,650 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_syscom.h" + +#include "ia_css_syscom_context.h" +#include "ia_css_syscom_config_fw.h" +#include "ia_css_syscom_trace.h" + +#include "queue.h" +#include "send_port.h" +#include "recv_port.h" +#include "regmem_access.h" + +#include "error_support.h" +#include "cpu_mem_support.h" + +#include "queue_struct.h" +#include "send_port_struct.h" +#include "recv_port_struct.h" + +#include "type_support.h" +#include +#include +#include "platform_support.h" + +#include "ia_css_cell.h" + +/* struct of internal buffer sizes */ +struct ia_css_syscom_size_intern { + unsigned int context; + unsigned int input_queue; + unsigned int output_queue; + unsigned int input_port; + unsigned int output_port; + + unsigned int fw_config; + unsigned int specific; + + unsigned int input_buffer; + unsigned int output_buffer; +}; + +/* Allocate buffers internally, when no buffers are provided */ +static int +ia_css_syscom_alloc( + unsigned int ssid, + unsigned int mmid, + const struct ia_css_syscom_size *size, + struct ia_css_syscom_buf *buf) +{ + /* zero the buffer to set all pointers to zero */ + memset(buf, 0, sizeof(*buf)); + + /* allocate cpu_mem */ + buf->cpu = (char *)ia_css_cpu_mem_alloc(size->cpu); + if (!buf->cpu) + goto EXIT7; + + /* allocate and map shared config buffer */ + buf->shm_host = shared_memory_alloc(mmid, size->shm); + if (!buf->shm_host) + goto EXIT6; + buf->shm_cell = shared_memory_map(ssid, mmid, buf->shm_host); + if (!buf->shm_cell) + goto EXIT5; + + /* allocate and map input queue buffer */ + buf->ibuf_host = shared_memory_alloc(mmid, size->ibuf); + if (!buf->ibuf_host) + goto EXIT4; + buf->ibuf_cell = shared_memory_map(ssid, mmid, buf->ibuf_host); + if (!buf->ibuf_cell) + goto EXIT3; + + /* allocate and map output queue buffer */ + buf->obuf_host = shared_memory_alloc(mmid, size->obuf); + if (!buf->obuf_host) + goto EXIT2; + buf->obuf_cell = shared_memory_map(ssid, mmid, buf->obuf_host); + if (!buf->obuf_cell) + goto EXIT1; + + return 0; + +EXIT1: shared_memory_free(mmid, buf->obuf_host); +EXIT2: shared_memory_unmap(ssid, mmid, buf->ibuf_cell); +EXIT3: shared_memory_free(mmid, buf->ibuf_host); +EXIT4: shared_memory_unmap(ssid, mmid, buf->shm_cell); +EXIT5: shared_memory_free(mmid, buf->shm_host); +EXIT6: ia_css_cpu_mem_free(buf->cpu); +EXIT7: return FW_ERROR_NO_MEMORY; +} + +static void +ia_css_syscom_size_intern( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size_intern *size) +{ + /* convert syscom config into syscom internal size struct */ + + unsigned int i; + + size->context = sizeof(struct ia_css_syscom_context); + size->input_queue = cfg->num_input_queues * sizeof(struct sys_queue); + size->output_queue = cfg->num_output_queues * sizeof(struct sys_queue); + size->input_port = cfg->num_input_queues * sizeof(struct send_port); + size->output_port = cfg->num_output_queues * sizeof(struct recv_port); + + size->fw_config = sizeof(struct ia_css_syscom_config_fw); + size->specific = cfg->specific_size; + + /* accumulate input queue buffer sizes */ + size->input_buffer = 0; + for (i = 0; i < cfg->num_input_queues; i++) { + size->input_buffer += + sys_queue_buf_size(cfg->input[i].queue_size, + cfg->input[i].token_size); + } + + /* accumulate outut queue buffer sizes */ + size->output_buffer = 0; + for (i = 0; i < cfg->num_output_queues; i++) { + size->output_buffer += + sys_queue_buf_size(cfg->output[i].queue_size, + cfg->output[i].token_size); + } +} + +static void +ia_css_syscom_size_extern( + const struct ia_css_syscom_size_intern *i, + struct ia_css_syscom_size *e) +{ + /* convert syscom internal size struct into external size struct */ + + e->cpu = i->context + i->input_queue + i->output_queue + + i->input_port + i->output_port; + e->shm = i->fw_config + i->input_queue + i->output_queue + i->specific; + e->ibuf = i->input_buffer; + e->obuf = i->output_buffer; +} + +/* Function that provides buffer sizes to be allocated */ +void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size) +{ + struct ia_css_syscom_size_intern i; + + ia_css_syscom_size_intern(cfg, &i); + ia_css_syscom_size_extern(&i, size); +} + +static struct ia_css_syscom_context* +ia_css_syscom_assign_buf( + const struct ia_css_syscom_size_intern *i, + const struct ia_css_syscom_buf *buf) +{ + struct ia_css_syscom_context *ctx; + char *cpu_mem_buf; + host_virtual_address_t shm_buf_host; + vied_virtual_address_t shm_buf_cell; + + /* host context */ + cpu_mem_buf = buf->cpu; + + ctx = (struct ia_css_syscom_context *)cpu_mem_buf; + ia_css_cpu_mem_set_zero(ctx, i->context); + cpu_mem_buf += i->context; + + ctx->input_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->input_queue; + + ctx->output_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->output_queue; + + ctx->send_port = (struct send_port *) cpu_mem_buf; + cpu_mem_buf += i->input_port; + + ctx->recv_port = (struct recv_port *) cpu_mem_buf; + + + /* cell config */ + shm_buf_host = buf->shm_host; + shm_buf_cell = buf->shm_cell; + + ctx->config_host_addr = shm_buf_host; + shm_buf_host += i->fw_config; + ctx->config_vied_addr = shm_buf_cell; + shm_buf_cell += i->fw_config; + + ctx->input_queue_host_addr = shm_buf_host; + shm_buf_host += i->input_queue; + ctx->input_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->input_queue; + + ctx->output_queue_host_addr = shm_buf_host; + shm_buf_host += i->output_queue; + ctx->output_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->output_queue; + + ctx->specific_host_addr = shm_buf_host; + ctx->specific_vied_addr = shm_buf_cell; + + ctx->ibuf_host_addr = buf->ibuf_host; + ctx->ibuf_vied_addr = buf->ibuf_cell; + + ctx->obuf_host_addr = buf->obuf_host; + ctx->obuf_vied_addr = buf->obuf_cell; + + return ctx; +} + +struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *cfg, + struct ia_css_syscom_buf *buf_extern +) +{ + struct ia_css_syscom_size_intern size_intern; + struct ia_css_syscom_size size; + struct ia_css_syscom_buf buf_intern; + struct ia_css_syscom_buf *buf; + struct ia_css_syscom_context *ctx; + struct ia_css_syscom_config_fw fw_cfg; + unsigned int i; + struct sys_queue_res res; + + IA_CSS_TRACE_0(SYSCOM, INFO, "Entered: ia_css_syscom_open\n"); + + /* error handling */ + if (cfg == NULL) + return NULL; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) start\n", cfg->secure); + + /* check members of cfg: TBD */ + + /* + * Check if SP is in valid state, have to wait if not ready. + * In some platform (Such as VP), it will need more time to wait due to system performance; + * If return NULL without wait for SPC0 ready, Driver load FW will failed + */ + ia_css_cell_wait(cfg->ssid, SPC0); + + ia_css_syscom_size_intern(cfg, &size_intern); + ia_css_syscom_size_extern(&size_intern, &size); + + if (buf_extern) { + /* use externally allocated buffers */ + buf = buf_extern; + } else { + /* use internally allocated buffers */ + buf = &buf_intern; + if (ia_css_syscom_alloc(cfg->ssid, cfg->mmid, &size, buf) != 0) + return NULL; + } + + /* assign buffer pointers */ + ctx = ia_css_syscom_assign_buf(&size_intern, buf); + /* only need to free internally allocated buffers */ + ctx->free_buf = !buf_extern; + + ctx->cell_regs_addr = cfg->regs_addr; + /* regmem is at cell_dmem_addr + REGMEM_OFFSET */ + ctx->cell_dmem_addr = cfg->dmem_addr; + + ctx->num_input_queues = cfg->num_input_queues; + ctx->num_output_queues = cfg->num_output_queues; + + ctx->env.mmid = cfg->mmid; + ctx->env.ssid = cfg->ssid; + ctx->env.mem_addr = cfg->dmem_addr; + + ctx->regmem_idx = SYSCOM_QPR_BASE_REG; + + /* initialize input queues */ + res.reg = SYSCOM_QPR_BASE_REG; + res.host_address = ctx->ibuf_host_addr; + res.vied_address = ctx->ibuf_vied_addr; + for (i = 0; i < cfg->num_input_queues; i++) { + sys_queue_init(ctx->input_queue + i, + cfg->input[i].queue_size, + cfg->input[i].token_size, &res); + } + + /* initialize output queues */ + res.host_address = ctx->obuf_host_addr; + res.vied_address = ctx->obuf_vied_addr; + for (i = 0; i < cfg->num_output_queues; i++) { + sys_queue_init(ctx->output_queue + i, + cfg->output[i].queue_size, + cfg->output[i].token_size, &res); + } + + /* fill shared queue structs */ + shared_memory_store(cfg->mmid, ctx->input_queue_host_addr, + ctx->input_queue, + cfg->num_input_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->input_queue_host_addr), + cfg->num_input_queues * sizeof(struct sys_queue)); + shared_memory_store(cfg->mmid, ctx->output_queue_host_addr, + ctx->output_queue, + cfg->num_output_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->output_queue_host_addr), + cfg->num_output_queues * sizeof(struct sys_queue)); + + /* Zero the queue buffers. Is this really needed? */ + shared_memory_zero(cfg->mmid, buf->ibuf_host, size.ibuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->ibuf_host), + size.ibuf); + shared_memory_zero(cfg->mmid, buf->obuf_host, size.obuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->obuf_host), + size.obuf); + + /* copy firmware specific data */ + if (cfg->specific_addr && cfg->specific_size) { + shared_memory_store(cfg->mmid, ctx->specific_host_addr, + cfg->specific_addr, cfg->specific_size); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->specific_host_addr), + cfg->specific_size); + } + + fw_cfg.num_input_queues = cfg->num_input_queues; + fw_cfg.num_output_queues = cfg->num_output_queues; + fw_cfg.input_queue = ctx->input_queue_vied_addr; + fw_cfg.output_queue = ctx->output_queue_vied_addr; + fw_cfg.specific_addr = ctx->specific_vied_addr; + fw_cfg.specific_size = cfg->specific_size; + + shared_memory_store(cfg->mmid, ctx->config_host_addr, + &fw_cfg, sizeof(struct ia_css_syscom_config_fw)); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(ctx->config_host_addr), + sizeof(struct ia_css_syscom_config_fw)); + +#if !HAS_DUAL_CMD_CTX_SUPPORT + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, cfg->ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, cfg->ssid); + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, cfg->ssid); +#endif + + /* Indicate if ctx is created for secure stream purpose */ + ctx->secure = cfg->secure; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) completed\n", cfg->secure); + return ctx; +} + + +int +ia_css_syscom_close( + struct ia_css_syscom_context *ctx +) { + int state; + + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle close request yet */ + return FW_ERROR_BUSY; + } + + /* set close request flag */ + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_INACTIVE, ctx->env.ssid); + + return 0; +} + +static void +ia_css_syscom_free(struct ia_css_syscom_context *ctx) +{ + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->ibuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->ibuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->obuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->obuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, + ctx->config_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->config_host_addr); + ia_css_cpu_mem_free(ctx); +} + +int +ia_css_syscom_release( + struct ia_css_syscom_context *ctx, + unsigned int force +) { + /* check if release is forced, an verify cell state if it is not */ + if (!force) { + if (!ia_css_cell_is_ready(ctx->env.ssid, SPC0)) + return FW_ERROR_BUSY; + } + + /* Reset the regmem idx */ + ctx->regmem_idx = 0; + + if (ctx->free_buf) + ia_css_syscom_free(ctx); + + return 0; +} + +int ia_css_syscom_send_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + send_port_open(ctx->send_port + port, + ctx->input_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_send_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +int ia_css_syscom_send_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_available(ctx->send_port + port); +} + +int ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + const void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_transfer(ctx->send_port + port, token); +} + +int ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, + SYSCOM_STATE_REG, ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + recv_port_open(ctx->recv_port + port, + ctx->output_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +/* + * Get the number of responses in the response queue + */ +int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_available(ctx->recv_port + port); +} + + +/* + * Dequeue the head of the response queue + * returns an error when the response queue is empty + */ +int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_transfer(ctx->recv_port + port, token); +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* + * store subsystem context information in DMEM + */ +int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *ctx, + unsigned int ssid, + unsigned int vtl0_addr_mask +) +{ + unsigned int read_back; + + NOT_USED(vtl0_addr_mask); + NOT_USED(read_back); + + if (ctx->secure) { + /* store VTL0 address mask in 'secure' context */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem VTL0_ADDR_MASK (%#x) @ dmem_addr %#x ssid %d\n", + vtl0_addr_mask, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_VTL0_ADDR_MASK, vtl0_addr_mask, ssid); + } + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, ssid); + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, ssid); + + return 0; +} + +/* + * store truslet configuration status setting + */ +void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +) +{ + unsigned int value; + + value = trustlet_exist ? TRUSTLET_EXIST : TRUSTLET_NOT_EXIST; + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_set_trustlet_status TRUSTLET_STATUS (%#x) @ dmem_addr %#x ssid %d\n", + value, dmem_addr, ssid); + regmem_store_32(dmem_addr, TRUSTLET_STATUS, value, ssid); +} + +/* + * check if SPC access blocker programming is completed + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +) +{ + unsigned int value; + + /* We only expect the call from non-secure context only */ + if (ctx->secure) { + IA_CSS_TRACE_0(SYSCOM, ERROR, "ia_css_syscom_is_spc_ab_ready - Please call from non-secure context\n"); + return false; + } + + value = regmem_load_32(ctx->cell_dmem_addr, AB_SPC_STATUS, ctx->env.ssid); + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_is_spc_ab_ready AB_SPC_STATUS @ dmem_addr %#x ssid %d - value %#x\n", + ctx->cell_dmem_addr, ctx->env.ssid, value); + + return (value == AB_SPC_READY); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h new file mode 100644 index 000000000000..0cacd5a34934 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_FW_H +#define __IA_CSS_SYSCOM_CONFIG_FW_H + +#include "type_support.h" + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_STATE_UNINIT = 0x57A7E000, + /* SP Syscom sets this when it is ready for use */ + SYSCOM_STATE_READY = 0x57A7E001, + /* SP Syscom sets this when no more syscom accesses will happen */ + SYSCOM_STATE_INACTIVE = 0x57A7E002 +}; + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_COMMAND_UNINIT = 0x57A7F000, + /* Host Syscom requests syscom to become inactive */ + SYSCOM_COMMAND_INACTIVE = 0x57A7F001 +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +enum { + /* Program load or explicit host setting should init to this */ + TRUSTLET_UNINIT = 0x57A8E000, + /* Host Syscom informs SP that Trustlet exists */ + TRUSTLET_EXIST = 0x57A8E001, + /* Host Syscom informs SP that Trustlet does not exist */ + TRUSTLET_NOT_EXIST = 0x57A8E002 +}; + +enum { + /* Program load or explicit setting initialized by SP */ + AB_SPC_NOT_READY = 0x57A8F000, + /* SP informs host that SPC access programming is completed */ + AB_SPC_READY = 0x57A8F001 +}; +#endif + +/* firmware config: data that sent from the host to SP via DDR */ +/* Cell copies data into a context */ + +struct ia_css_syscom_config_fw { + unsigned int firmware_address; + + unsigned int num_input_queues; + unsigned int num_output_queues; + unsigned int input_queue; /* hmm_ptr / struct queue* */ + unsigned int output_queue; /* hmm_ptr / struct queue* */ + + unsigned int specific_addr; /* vied virtual address */ + unsigned int specific_size; +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_FW_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h new file mode 100644 index 000000000000..ecf22f6b7ac5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONTEXT_H +#define __IA_CSS_SYSCOM_CONTEXT_H + +#include + +#include "port_env_struct.h" +#include + +/* host context */ +struct ia_css_syscom_context { + vied_virtual_address_t cell_firmware_addr; + unsigned int cell_regs_addr; + unsigned int cell_dmem_addr; + + struct port_env env; + + unsigned int num_input_queues; + unsigned int num_output_queues; + + /* array of input queues (from host to SP) */ + struct sys_queue *input_queue; + /* array of output queues (from SP to host) */ + struct sys_queue *output_queue; + + struct send_port *send_port; + struct recv_port *recv_port; + + unsigned int regmem_idx; + unsigned int free_buf; + + host_virtual_address_t config_host_addr; + host_virtual_address_t input_queue_host_addr; + host_virtual_address_t output_queue_host_addr; + host_virtual_address_t specific_host_addr; + host_virtual_address_t ibuf_host_addr; + host_virtual_address_t obuf_host_addr; + + vied_virtual_address_t config_vied_addr; + vied_virtual_address_t input_queue_vied_addr; + vied_virtual_address_t output_queue_vied_addr; + vied_virtual_address_t specific_vied_addr; + vied_virtual_address_t ibuf_vied_addr; + vied_virtual_address_t obuf_vied_addr; + + /* if true; secure syscom object as in VTIO Case + * if false, non-secure syscom + */ + bool secure; +}; + +#endif /* __IA_CSS_SYSCOM_CONTEXT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/syscom.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/syscom.mk new file mode 100644 index 000000000000..8d36b8928af5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/syscom/syscom.mk @@ -0,0 +1,42 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is SYSCOM + +SYSCOM_DIR=$${MODULES_DIR}/syscom + +SYSCOM_INTERFACE=$(SYSCOM_DIR)/interface +SYSCOM_SOURCES1=$(SYSCOM_DIR)/src + +SYSCOM_HOST_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom.c + +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_HOST_CPPFLAGS += -I$${MODULES_DIR}/devices +ifdef REGMEM_SECURE_OFFSET +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif + +SYSCOM_FW_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom_fw.c + +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_FW_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) +ifdef REGMEM_SECURE_OFFSET +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/interface/ia_css_trace.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/interface/ia_css_trace.h new file mode 100644 index 000000000000..b85b1810f107 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/interface/ia_css_trace.h @@ -0,0 +1,883 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/*! \file */ + +#ifndef __IA_CSS_TRACE_H +#define __IA_CSS_TRACE_H + +/* +** Configurations +*/ + +/** + * STEP 1: Define {Module Name}_TRACE_METHOD to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * + * Example: + * #define NCI_DMA_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + */ + +/**< Use whatever method of tracing that best suits the platform + * this code is compiled for. + */ +#define IA_CSS_TRACE_METHOD_NATIVE 1 +/**< Use the Tracing NCI. */ +#define IA_CSS_TRACE_METHOD_TRACE 2 + +/** + * STEP 2: Define {Module Name}_TRACE_LEVEL_{Level} to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * {Level}, in decreasing order of severity, is one of the + * following values: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * + * Example: + * #define NCI_DMA_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + * #define NCI_DMA_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + */ +/**< Disables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_DISABLED 0 +/**< Enables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_ENABLED 1 + +/* + * Used in macro definition with do-while loop + * for removing checkpatch warnings + */ +#define IA_CSS_TRACE_FILE_DUMMY_DEFINE + +/** + * STEP 3: Define IA_CSS_TRACE_PRINT_FILE_LINE to have file name and + * line printed with every log message. + * + * Example: + * #define IA_CSS_TRACE_PRINT_FILE_LINE + */ + +/* +** Interface +*/ + +/* +** Static +*/ + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * at compile-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_0(module, severity, format) \ + IA_CSS_TRACE_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_1(module, severity, format, a1) \ + IA_CSS_TRACE_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_IMPL(module, 5, severity, format, a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_6(module, severity, format, a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_IMPL(module, 6, severity, format, a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_7(module, severity, format, a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Dynamic +*/ + +/** +* Declares, but does not define, dynamic tracing functions and variables +* for module \p module. For each module, place an instance of this macro +* in the compilation unit in which you want to use dynamic tracing facility +* so as to inform the compiler of the declaration of the available functions. +* An invocation of this function does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DEFINE +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) +/** +* Declares the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) + +/** +* Defines dynamic tracing functions and variables for module \p module. +* For each module, place an instance of this macro in one, and only one, +* of your SOURCE files so as to allow the linker resolve the related symbols. +* An invocation of this macro does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DECLARE +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) +/** +* Defines the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_DYNAMIC_0(module, severity, format) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_1(module, severity, format, a1) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 5, severity, format, \ + a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_6(module, severity, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 6, severity, format, \ + a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_7(module, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Implementation +*/ + +/* CAT */ +#define IA_CSS_TRACE_CAT_IMPL(a, b) a ## b +#define IA_CSS_TRACE_CAT(a, b) IA_CSS_TRACE_CAT_IMPL(a, b) + +/* Bridge */ +#if defined(__HIVECC) || defined(__GNUC__) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, arguments ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + ## arguments \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, \ + arguments ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + ## arguments); \ + } \ + } while (0) +#elif defined(_MSC_VER) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + __VA_ARGS__ \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + __VA_ARGS__); \ + } \ + } while (0) +#endif + +/* +** Native Backend +*/ + +#if defined(__HIVECC) + #define IA_CSS_TRACE_PLATFORM_CELL +#elif defined(__GNUC__) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, \ + format), ## arguments); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, \ + format), ## arguments); \ + } while (0) + +#elif defined(_MSC_VER) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) +#else + #error Unsupported platform! +#endif /* Platform */ + +#if defined(IA_CSS_TRACE_PLATFORM_CELL) + #include /* VOLATILE */ + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + do { \ + OP___printstring(__FILE__":") VOLATILE; \ + OP___printdec(__LINE__) VOLATILE; \ + OP___printstring("\n") VOLATILE; \ + } while (0) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + OP___printstring("["module"]:["severity"]:") \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_MSG_NATIVE(severity, module, format) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + OP___printstring("["module"]:["severity"]: "format) \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_ARG_NATIVE(module, severity, i, value) \ + do { \ + IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity); \ + OP___dump(i, value) VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 7, a7); \ + } while (0) + /* + ** Tracing Backend + */ +#if !defined(HRT_CSIM) && !defined(NO_TUNIT) + #include "vied_nci_tunit.h" +#endif + #define IA_CSS_TRACE_AUG_FORMAT_TRACE(format, module) \ + "[" module "]" format " : PID = %x : Timestamp = %d : PC = %x" + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + vied_nci_tunit_print(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + vied_nci_tunit_print1i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + vied_nci_tunit_print2i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + vied_nci_tunit_print3i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, a1, a2, a3, a4) \ + vied_nci_tunit_print4i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + vied_nci_tunit_print5i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + vied_nci_tunit_print6i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + vied_nci_tunit_print7i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6, a7) + +#elif defined(IA_CSS_TRACE_PLATFORM_HOST) + #include "print_support.h" + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + PRINT("%s:%d:\n", __FILE__, __LINE__) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, format) \ + "[" module "]:[" severity "]: " format + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) + + #define IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, format) \ + "["module"]:["severity"]: "format + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + IA_CSS_TRACE_TRACE(severity, module, format) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) +#endif + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_0(severity, module, format) +#define IA_CSS_TRACE_1_1_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_1_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_1_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_1_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_1 IA_CSS_TRACE_NATIVE_0 +#define IA_CSS_TRACE_1_1_1 IA_CSS_TRACE_NATIVE_1 +#define IA_CSS_TRACE_2_1_1 IA_CSS_TRACE_NATIVE_2 +#define IA_CSS_TRACE_3_1_1 IA_CSS_TRACE_NATIVE_3 +#define IA_CSS_TRACE_4_1_1 IA_CSS_TRACE_NATIVE_4 +#define IA_CSS_TRACE_5_1_1 IA_CSS_TRACE_NATIVE_5 +#define IA_CSS_TRACE_6_1_1 IA_CSS_TRACE_NATIVE_6 +#define IA_CSS_TRACE_7_1_1 IA_CSS_TRACE_NATIVE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_1 "Assert" +#define IA_CSS_TRACE_SEVERITY_ERROR_1 "Error" +#define IA_CSS_TRACE_SEVERITY_WARNING_1 "Warning" +#define IA_CSS_TRACE_SEVERITY_INFO_1 "Info" +#define IA_CSS_TRACE_SEVERITY_DEBUG_1 "Debug" +#define IA_CSS_TRACE_SEVERITY_VERBOSE_1 "Verbose" + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_0(severity, module, format) +#define IA_CSS_TRACE_1_2_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_2_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_2_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_2_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_1 IA_CSS_TRACE_TRACE_0 +#define IA_CSS_TRACE_1_2_1 IA_CSS_TRACE_TRACE_1 +#define IA_CSS_TRACE_2_2_1 IA_CSS_TRACE_TRACE_2 +#define IA_CSS_TRACE_3_2_1 IA_CSS_TRACE_TRACE_3 +#define IA_CSS_TRACE_4_2_1 IA_CSS_TRACE_TRACE_4 +#define IA_CSS_TRACE_5_2_1 IA_CSS_TRACE_TRACE_5 +#define IA_CSS_TRACE_6_2_1 IA_CSS_TRACE_TRACE_6 +#define IA_CSS_TRACE_7_2_1 IA_CSS_TRACE_TRACE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_2 VIED_NCI_TUNIT_MSG_SEVERITY_FATAL +#define IA_CSS_TRACE_SEVERITY_ERROR_2 VIED_NCI_TUNIT_MSG_SEVERITY_ERROR +#define IA_CSS_TRACE_SEVERITY_WARNING_2 VIED_NCI_TUNIT_MSG_SEVERITY_WARNING +#define IA_CSS_TRACE_SEVERITY_INFO_2 VIED_NCI_TUNIT_MSG_SEVERITY_NORMAL +#define IA_CSS_TRACE_SEVERITY_DEBUG_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER1 +#define IA_CSS_TRACE_SEVERITY_VERBOSE_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER2 + +/* +** Dynamicism +*/ + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) \ + do { \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void); \ + } while (0) + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + void IA_CSS_TRACE_CAT(module, _trace_configure)\ + (int argc, const char *const *argv); \ + } while (0) + +#include "platform_support.h" +#include "type_support.h" + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_assert); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_error); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_warning); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_info); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_debug); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_verbose); \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 0; \ + } + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) \ +void IA_CSS_TRACE_CAT(module, _trace_configure)(const int argc, \ + const char *const *const argv) \ +{ \ + int i = 1; \ + const char *levels = 0; \ + \ + while (i < argc) { \ + if (!strcmp(argv[i], "-" #module "_trace")) { \ + ++i; \ + \ + if (i < argc) { \ + levels = argv[i]; \ + \ + while (*levels) { \ + switch (*levels++) { \ + case 'a': \ + IA_CSS_TRACE_CAT \ + (module, _trace_assert_enable)(); \ + break; \ + \ + case 'e': \ + IA_CSS_TRACE_CAT \ + (module, _trace_error_enable)(); \ + break; \ + \ + case 'w': \ + IA_CSS_TRACE_CAT \ + (module, _trace_warning_enable)(); \ + break; \ + \ + case 'i': \ + IA_CSS_TRACE_CAT \ + (module, _trace_info_enable)(); \ + break; \ + \ + case 'd': \ + IA_CSS_TRACE_CAT \ + (module, _trace_debug_enable)(); \ + break; \ + \ + case 'v': \ + IA_CSS_TRACE_CAT \ + (module, _trace_verbose_enable)(); \ + break; \ + \ + default: \ + } \ + } \ + } \ + } \ + \ + ++i; \ + } \ +} + +#endif /* __IA_CSS_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/trace.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/trace.mk new file mode 100644 index 000000000000..b232880b882b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/trace/trace.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE Trace + +# Dependencies +IA_CSS_TRACE_SUPPORT = $${MODULES_DIR}/support + +# API +IA_CSS_TRACE = $${MODULES_DIR}/trace +IA_CSS_TRACE_INTERFACE = $(IA_CSS_TRACE)/interface + +# +# Host +# + +# Host CPP Flags +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules + +# +# Firmware +# + +# Firmware CPP Flags +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_access.h new file mode 100755 index 000000000000..1e81bad9f4ee --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_access.h @@ -0,0 +1,139 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_ACCESS_H +#define _SHARED_MEMORY_ACCESS_H + +#include +#include +#include + +typedef enum { + sm_esuccess, + sm_enomem, + sm_ezeroalloc, + sm_ebadvaddr, + sm_einternalerror, + sm_ecorruption, + sm_enocontiguousmem, + sm_enolocmem, + sm_emultiplefree, +} shared_memory_error; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the VIED subsystem + */ +typedef uint32_t vied_virtual_address_t; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the host + */ +typedef unsigned long long host_virtual_address_t; + +/** + * \brief List of physical addresses of (DDR) shared memory space. This is used to represent a list of physical pages. + */ +typedef struct shared_memory_physical_page_list_s *shared_memory_physical_page_list; +typedef struct shared_memory_physical_page_list_s +{ + shared_memory_physical_page_list next; + vied_physical_address_t address; +}shared_memory_physical_page_list_s; + + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param idm: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(vied_memory_t idm, vied_physical_address_t host_ddr_addr, size_t memory_size, size_t ps); + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(vied_memory_t idm); + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. Returns NULL when insufficient memory available + */ +host_virtual_address_t shared_memory_alloc(vied_memory_t idm, size_t bytes); + +/** + * \brief Free (DDR) shared memory space. +*/ +void shared_memory_free(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Translate a virtual host.address to a physical address. +*/ +vied_physical_address_t shared_memory_virtual_host_to_physical_address (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Return the allocated physical pages for a virtual host.address. +*/ +shared_memory_physical_page_list shared_memory_virtual_host_to_physical_pages (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Destroy a shared_memory_physical_page_list. +*/ +void shared_memory_physical_pages_list_destroy (shared_memory_physical_page_list ppl); + +/** + * \brief Store a byte into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_8 (vied_memory_t idm, host_virtual_address_t addr, uint8_t data); + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_16(vied_memory_t idm, host_virtual_address_t addr, uint16_t data); + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_32(vied_memory_t idm, host_virtual_address_t addr, uint32_t data); + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store(vied_memory_t idm, host_virtual_address_t addr, const void *data, size_t bytes); + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host virtual address + */ +void shared_memory_zero(vied_memory_t idm, host_virtual_address_t addr, size_t bytes); + +/** + * \brief Load a byte from (DDR) shared memory space using a host virtual address + */ +uint8_t shared_memory_load_8 (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host virtual address + */ +uint16_t shared_memory_load_16(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host virtual address + */ +uint32_t shared_memory_load_32(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host virtual address + */ +void shared_memory_load(vied_memory_t idm, host_virtual_address_t addr, void *data, size_t bytes); + +#endif /* _SHARED_MEMORY_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_map.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_map.h new file mode 100755 index 000000000000..1bbedcf9e7fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/shared_memory_map.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_MAP_H +#define _SHARED_MEMORY_MAP_H + +#include +#include +#include + +typedef void (*shared_memory_invalidate_mmu_tlb)(void); +typedef void (*shared_memory_set_page_table_base_address)(vied_physical_address_t); + +typedef void (*shared_memory_invalidate_mmu_tlb_ssid)(vied_subsystem_t id); +typedef void (*shared_memory_set_page_table_base_address_ssid)(vied_subsystem_t id, vied_physical_address_t); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will NOT be taken into account. +*/ +int shared_memory_map_initialize(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb inv_tlb, shared_memory_set_page_table_base_address sbt); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will be taken into account. +*/ +int shared_memory_map_initialize_ssid(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb_ssid inv_tlb, shared_memory_set_page_table_base_address_ssid sbt); + +/** + * \brief De-initialize the CSS virtual address system and MMU. +*/ +void shared_memory_map_uninitialize(vied_subsystem_t id, vied_memory_t idm); + +/** + * \brief Convert a host virtual address to a CSS virtual address and update the MMU. +*/ +vied_virtual_address_t shared_memory_map(vied_subsystem_t id, vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Free a CSS virtual address and update the MMU. +*/ +void shared_memory_unmap(vied_subsystem_t id, vied_memory_t idm, vied_virtual_address_t addr); + + +#endif /* _SHARED_MEMORY_MAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_config.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_config.h new file mode 100755 index 000000000000..912f016ead24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_config.h @@ -0,0 +1,33 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_CONFIG_H +#define _HRT_VIED_CONFIG_H + +/* Defines from the compiler: + * HRT_HOST - this is code running on the host + * HRT_CELL - this is code running on a cell + */ +#ifdef HRT_HOST +# define CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL 1 +# undef CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL + +#elif defined (HRT_CELL) +# undef CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +# define CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL 1 + +#else /* !HRT_CELL */ +/* Allow neither HRT_HOST nor HRT_CELL for testing purposes */ +#endif /* !HRT_CELL */ + +#endif /* _HRT_VIED_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h new file mode 100755 index 000000000000..0b44492789e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h @@ -0,0 +1,36 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_MEMORY_ACCESS_TYPES_H +#define _HRT_VIED_MEMORY_ACCESS_TYPES_H + +/** Types for the VIED memory access interface */ + +#include "vied_types.h" + +/** + * \brief An identifier for a system memory. + * + * This identifier must be a compile-time constant. It is used in + * access to system memory. + */ +typedef unsigned int vied_memory_t; + +#ifndef __HIVECC +/** + * \brief The type for a physical address + */ +typedef unsigned long long vied_physical_address_t; +#endif + +#endif /* _HRT_VIED_MEMORY_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h new file mode 100755 index 000000000000..674f5fb5b0f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h @@ -0,0 +1,70 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_H + +#include +#include "vied_config.h" +#include "vied_subsystem_access_types.h" + +#if !defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) && \ + !defined(CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL) +#error Implementation selection macro for vied subsystem access not defined +#endif + +#if defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) +#ifndef __HIVECC +#error "Inline implementation of subsystem access not supported for host" +#endif +#define _VIED_SUBSYSTEM_ACCESS_INLINE static __inline +#include "vied_subsystem_access_impl.h" +#else +#define _VIED_SUBSYSTEM_ACCESS_INLINE +#endif + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr, uint8_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_16(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint16_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_32(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint32_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store(vied_subsystem_t dev, + vied_subsystem_address_t addr, + const void *data, unsigned int size); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint8_t vied_subsystem_load_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint16_t vied_subsystem_load_16(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint32_t vied_subsystem_load_32(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_load(vied_subsystem_t dev, + vied_subsystem_address_t addr, + void *data, unsigned int size); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h new file mode 100755 index 000000000000..81f4d08d5ae0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h @@ -0,0 +1,44 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H + +#include "vied_subsystem_access_types.h" + +/** @brief Initialises the access of a subsystem. + * @param[in] system The subsystem for which the access has to be initialised. + * + * vied_subsystem_access_initialize initilalises the access a subsystem. + * It sets the base address of the subsystem. This base address is extracted from the hsd file. + * + */ +void +vied_subsystem_access_initialize(vied_subsystem_t system); + + +/** @brief Initialises the access of multiple subsystems. + * @param[in] nr _subsystems The number of subsystems for which the access has to be initialised. + * @param[in] dev_base_addresses A pointer to an array of base addresses of subsystems. + * The size of this array must be "nr_subsystems". + * This array must be available during the accesses of the subsystem. + * + * vied_subsystems_access_initialize initilalises the access to multiple subsystems. + * It sets the base addresses of the subsystems that are provided by the array dev_base_addresses. + * + */ +void +vied_subsystems_access_initialize( unsigned int nr_subsystems + , const vied_subsystem_base_address_t *base_addresses); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h new file mode 100755 index 000000000000..75fef6c4ddba --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h @@ -0,0 +1,34 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H + +/** Types for the VIED subsystem access interface */ +#include + +/** \brief An identifier for a VIED subsystem. + * + * This identifier must be a compile-time constant. It is used in + * access to a VIED subsystem. + */ +typedef unsigned int vied_subsystem_t; + + +/** \brief An address within a VIED subsystem */ +typedef uint32_t vied_subsystem_address_t; + +/** \brief A base address of a VIED subsystem seen from the host */ +typedef unsigned long long vied_subsystem_base_address_t; + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_types.h new file mode 100755 index 000000000000..0acfdbb00cfa --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied/vied/vied_types.h @@ -0,0 +1,45 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_TYPES_H +#define _HRT_VIED_TYPES_H + +/** Types shared by VIED interfaces */ + +#include + +/** \brief An address within a VIED subsystem + * + * This will eventually replace teh vied_memory_address_t and vied_subsystem_address_t + */ +typedef uint32_t vied_address_t; + +/** \brief Memory address type + * + * A memory address is an offset within a memory. + */ +typedef uint32_t vied_memory_address_t; + +/** \brief Master port id */ +typedef int vied_master_port_id_t; + +/** + * \brief Require the existence of a certain type + * + * This macro can be used in interface header files to ensure that + * an implementation define type with a specified name exists. + */ +#define _VIED_REQUIRE_TYPE(T) enum { _VIED_SIZEOF_##T = sizeof(T) } + + +#endif /* _HRT_VIED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h new file mode 100644 index 000000000000..b09d9f4d5d42 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h @@ -0,0 +1,39 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef VIED_NCI_ACB_ROUTE_TYPE_H_ +#define VIED_NCI_ACB_ROUTE_TYPE_H_ + +#include "type_support.h" + +typedef enum { + NCI_ACB_PORT_ISP = 0, + NCI_ACB_PORT_ACC = 1, + NCI_ACB_PORT_INVALID = 0xFF +} nci_acb_port_t; + +typedef struct { + /* 0 = ISP, 1 = Acc */ + nci_acb_port_t in_select; + /* 0 = ISP, 1 = Acc */ + nci_acb_port_t out_select; + /* When set, Ack will be sent only when Eof arrives */ + uint32_t ignore_line_num; + /* Fork adapter to enable streaming to both output + * (next acb out and isp out) + */ + uint32_t fork_acb_output; +} nci_acb_route_t; + +#endif /* VIED_NCI_ACB_ROUTE_TYPE_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h new file mode 100644 index 000000000000..1ea7e729078c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PARAM_STORAGE_CLASS_H +#define __IA_CSS_PARAM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __INLINE_PARAMETERS__ +#define IA_CSS_PARAMETERS_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PARAMETERS_STORAGE_CLASS_C +#else +#define IA_CSS_PARAMETERS_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PARAMETERS_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PARAM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h new file mode 100644 index 000000000000..4cc71be3fc38 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h @@ -0,0 +1,188 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_H +#define __IA_CSS_TERMINAL_H + +#include "type_support.h" +#include "ia_css_terminal_types.h" +#include "ia_css_param_storage_class.h" + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_in_terminal_get_descriptor_size( + const unsigned int nof_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_section_desc_t * +ia_css_param_in_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_out_terminal_get_descriptor_size( + const unsigned int nof_sections, + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_section_desc_t * +ia_css_param_out_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index, + const unsigned int nof_sections, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_param_terminal_create( + ia_css_param_terminal_t *param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_grid_desc_t * +ia_css_spatial_param_terminal_get_fragment_grid_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_frame_grid_param_section_desc_t * +ia_css_spatial_param_terminal_get_frame_grid_param_section_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_spatial_param_terminal_create( + ia_css_spatial_param_terminal_t *spatial_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_fragments, + const uint32_t kernel_id +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_sliced_param_terminal_get_descriptor_size( + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_slice_desc_t * +ia_css_sliced_param_terminal_get_fragment_slice_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_slice_param_section_desc_t * +ia_css_sliced_param_terminal_get_slice_param_section_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index, + const unsigned int slice_index, + const unsigned int section_index, + const unsigned int nof_slice_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_sliced_param_terminal_create( + ia_css_sliced_param_terminal_t *sliced_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments, + const uint32_t kernel_id +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_program_terminal_get_descriptor_size( + const unsigned int nof_fragments, + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_param_section_desc_t * +ia_css_program_terminal_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int section_index, + const unsigned int nof_fragment_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_kernel_fragment_sequencer_info_desc_t * +ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int info_index, + const unsigned int nof_kernel_fragment_sequencer_infos +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_create( + ia_css_program_terminal_t *program_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_get_command_base_offset( + const ia_css_program_terminal_t *program_terminal, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int commands_slots_used, + uint16_t *command_desc_offset +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +uint16_t *ia_css_program_terminal_get_line_count( + const ia_css_kernel_fragment_sequencer_command_desc_t + *kernel_fragment_sequencer_command_desc_base, + const unsigned int set_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments +); + +#ifdef __INLINE_PARAMETERS__ +#include "ia_css_terminal_impl.h" +#endif /* __INLINE_PARAMETERS__ */ + +#endif /* __IA_CSS_TERMINAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h new file mode 100644 index 000000000000..ca0a436082cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h @@ -0,0 +1,109 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_H +#define __IA_CSS_TERMINAL_MANIFEST_H + +#include "type_support.h" +#include "ia_css_param_storage_class.h" +#include "ia_css_terminal_manifest_types.h" + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_terminal_manifest_get_size( + const unsigned int nof_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_param_terminal_manifest_init( + ia_css_param_terminal_manifest_t *param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_manifest_section_desc_t * +ia_css_param_terminal_manifest_get_prm_sct_desc( + const ia_css_param_terminal_manifest_t *param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_manifest_get_size( + const unsigned int nof_frame_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_spatial_param_terminal_manifest_init( + ia_css_spatial_param_terminal_manifest_t *spatial_param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_frame_grid_param_manifest_section_desc_t * +ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + const ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_sliced_param_terminal_manifest_get_size( + const unsigned int nof_slice_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_sliced_param_terminal_manifest_init( + ia_css_sliced_param_terminal_manifest_t *sliced_param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_sliced_param_manifest_section_desc_t * +ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + const ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_program_terminal_manifest_get_size( + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_manifest_init( + ia_css_program_terminal_manifest_t *program_terminal, + const uint16_t fragment_param_section_count, + const uint16_t kernel_fragment_seq_info_section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_param_manifest_section_desc_t * +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_kernel_fragment_sequencer_info_manifest_desc_t * +ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int info_index +); + +#ifdef __INLINE_PARAMETERS__ +#include "ia_css_terminal_manifest_impl.h" +#endif /* __INLINE_PARAMETERS__ */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h new file mode 100644 index 000000000000..fe146395a8f4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h @@ -0,0 +1,342 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_TYPES_H + + +#include "ia_css_terminal_defs.h" +#include "type_support.h" +#include "ia_css_base_types.h" +#include "ia_css_terminal_manifest_base_types.h" + +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT 1 +#define SIZE_OF_PARAM_TERMINAL_MANIFEST_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* =============== Cached Param Terminal Manifest - START ============== */ +struct ia_css_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* Indication of the kernel this parameter belongs to */ + uint8_t kernel_id; + /* Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT]; +}; + +typedef struct ia_css_param_manifest_section_desc_s + ia_css_param_manifest_section_desc_t; + + +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT 4 +#define SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + (2*IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT * IA_CSS_UINT8_T_BITS)) + +/* Frame constant parameters terminal manifest */ +struct ia_css_param_terminal_manifest_s { + /* Parameter terminal manifest base */ + ia_css_terminal_manifest_t base; + /* + * Number of cached parameter sections, coming from manifest + * but also shared by the terminal + */ + uint16_t param_manifest_section_desc_count; + /* + * Points to the variable array of + * struct ia_css_param_section_desc_s + */ + uint16_t param_manifest_section_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_param_terminal_manifest_s + ia_css_param_terminal_manifest_t; +/* ================= Cached Param Terminal Manifest - End ================ */ + + +/* ================= Spatial Param Terminal Manifest - START ============= */ + +#define SIZE_OF_FRAG_GRID_MAN_STRUCT_IN_BITS \ + ((IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) \ + + (IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS)) + +struct ia_css_fragment_grid_manifest_desc_s { + /* Min resolution width/height of the spatial parameters + * for the fragment measured in compute units + */ + uint16_t min_fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Max resolution width/height of the spatial parameters + * for the fragment measured in compute units + */ + uint16_t max_fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_fragment_grid_manifest_desc_s + ia_css_fragment_grid_manifest_desc_t; + +#define N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT 1 +#define SIZE_OF_FRAME_GRID_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_frame_grid_param_manifest_section_desc_s { + /* Maximum buffer total size allowed for + * this frame of parameters + */ + uint32_t max_mem_size; + /* Memory space targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory space */ + uint8_t region_id; + /* size in bytes of each compute unit for + * the specified memory space and region + */ + uint8_t elem_size; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT]; +}; + +typedef struct ia_css_frame_grid_param_manifest_section_desc_s + ia_css_frame_grid_param_manifest_section_desc_t; + +#define SIZE_OF_FRAME_GRID_MAN_STRUCT_IN_BITS \ + ((IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) \ + + (IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS)) + +struct ia_css_frame_grid_manifest_desc_s { + /* Min resolution width/height of the spatial parameters for + * the frame measured in compute units + */ + uint16_t min_frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Max resolution width/height of the spatial parameters for + * the frame measured in compute units + */ + uint16_t max_frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_frame_grid_manifest_desc_s + ia_css_frame_grid_manifest_desc_t; + +#define N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT 2 +#define SIZE_OF_SPATIAL_PARAM_TERM_MAN_STRUCT_IN_BITS \ + ((SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS) \ + + (SIZE_OF_FRAME_GRID_MAN_STRUCT_IN_BITS) \ + + (SIZE_OF_FRAG_GRID_MAN_STRUCT_IN_BITS) \ + + (2 * IA_CSS_UINT16_T_BITS) \ + + (2 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT * \ + IA_CSS_UINT8_T_BITS)) + +struct ia_css_spatial_param_terminal_manifest_s { + /* Spatial Parameter terminal manifest base */ + ia_css_terminal_manifest_t base; + /* Contains limits for the frame spatial parameters */ + ia_css_frame_grid_manifest_desc_t frame_grid_desc; + /* + * Constains limits for the fragment spatial parameters + * - COMMON AMONG FRAGMENTS + */ + ia_css_fragment_grid_manifest_desc_t common_fragment_grid_desc; + /* + * Number of frame spatial parameter sections, they are set + * in slice-steps through frame processing + */ + uint16_t frame_grid_param_manifest_section_desc_count; + /* + * Points to the variable array of + * ia_css_frame_spatial_param_manifest_section_desc_t + */ + uint16_t frame_grid_param_manifest_section_desc_offset; + /* + * Indication of the kernel this spatial parameter terminal belongs to + * SHOULD MATCH TO INDEX AND BE USED ONLY FOR CHECK + */ + uint8_t kernel_id; + /* + * Groups together compute units in order to achieve alignment + * requirements for transfes and to achieve canonical frame + * representation + */ + uint8_t compute_units_p_elem; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT]; +}; + +typedef struct ia_css_spatial_param_terminal_manifest_s + ia_css_spatial_param_terminal_manifest_t; + +/* ================= Spatial Param Terminal Manifest - END ================ */ + +/* ================= Sliced Param Terminal Manifest - START =============== */ + +#define N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT (2) +#define SIZE_OF_SLICED_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 2 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_sliced_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* + * Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT]; +}; + +typedef struct ia_css_sliced_param_manifest_section_desc_s + ia_css_sliced_param_manifest_section_desc_t; + +#define N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT 3 +#define SIZE_OF_SLICED_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + 2 * IA_CSS_UINT16_T_BITS \ + + 1 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Frame constant parameters terminal manifest */ +struct ia_css_sliced_param_terminal_manifest_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_manifest_t base; + /* + * Number of the array elements + * sliced_param_section_offset points to + */ + uint16_t sliced_param_section_count; + /* + * Points to array of ia_css_sliced_param_manifest_section_desc_s + * which constain info for the slicing of the parameters + */ + uint16_t sliced_param_section_offset; + /* Kernel identifier */ + uint8_t kernel_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT]; +}; + +typedef struct ia_css_sliced_param_terminal_manifest_s + ia_css_sliced_param_terminal_manifest_t; + +/* ================= Slice Param Terminal Manifest - End =============== */ + +/* ================= Program Terminal Manifest - START ================= */ + +#define N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT 1 +#define SIZE_OF_FRAG_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Fragment constant parameters manifest */ +struct ia_css_fragment_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* Indication of the kernel this parameter belongs to */ + uint8_t kernel_id; + /* Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory space */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT]; +}; + +typedef struct ia_css_fragment_param_manifest_section_desc_s + ia_css_fragment_param_manifest_section_desc_t; + +#define SIZE_OF_KERNEL_FRAG_SEQ_INFO_MAN_STRUCT_IN_BITS \ + (10*IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) + +struct ia_css_kernel_fragment_sequencer_info_manifest_desc_s { + /* Slice dimensions */ + uint16_t min_fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Slice dimensions */ + uint16_t max_fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t min_fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t max_fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + min_fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + max_fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + min_fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + max_fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Dimension of grid */ + int16_t + min_fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Dimension of grid */ + int16_t + max_fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_kernel_fragment_sequencer_info_manifest_desc_s + ia_css_kernel_fragment_sequencer_info_manifest_desc_t; + +#define N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT 2 +#define SIZE_OF_PROG_TERM_MAN_STRUCT_IN_BITS \ + ((SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS) \ + + (IA_CSS_UINT32_T_BITS) \ + + (5*IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT * IA_CSS_UINT8_T_BITS)) + +struct ia_css_program_terminal_manifest_s { + ia_css_terminal_manifest_t base; + /* Connection manager passes seq info as single blob at the moment */ + uint32_t sequencer_info_kernel_id; + /* Maximum number of command secriptors supported + * by the program group + */ + uint16_t max_kernel_fragment_sequencer_command_desc; + uint16_t fragment_param_manifest_section_desc_count; + uint16_t fragment_param_manifest_section_desc_offset; + uint16_t kernel_fragment_sequencer_info_manifest_info_count; + uint16_t kernel_fragment_sequencer_info_manifest_info_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT]; +}; + +typedef struct ia_css_program_terminal_manifest_s + ia_css_program_terminal_manifest_t; + +/* ==================== Program Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h new file mode 100644 index 000000000000..c5c89fb7ec91 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h @@ -0,0 +1,351 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_TYPES_H +#define __IA_CSS_TERMINAL_TYPES_H + +#include "type_support.h" +#include "ia_css_base_types.h" +#include "ia_css_terminal_base_types.h" + + +typedef struct ia_css_program_control_init_load_section_desc_s + ia_css_program_control_init_load_section_desc_t; +typedef struct ia_css_program_control_init_connect_section_desc_s + ia_css_program_control_init_connect_section_desc_t; +typedef struct ia_css_program_control_init_program_desc_s + ia_css_program_control_init_program_desc_t; +typedef struct ia_css_program_control_init_terminal_s + ia_css_program_control_init_terminal_t; + +typedef struct ia_css_program_terminal_s ia_css_program_terminal_t; +typedef struct ia_css_fragment_param_section_desc_s + ia_css_fragment_param_section_desc_t; +typedef struct ia_css_kernel_fragment_sequencer_info_desc_s + ia_css_kernel_fragment_sequencer_info_desc_t; +typedef struct ia_css_kernel_fragment_sequencer_command_desc_s + ia_css_kernel_fragment_sequencer_command_desc_t; + +typedef struct ia_css_sliced_param_terminal_s ia_css_sliced_param_terminal_t; +typedef struct ia_css_fragment_slice_desc_s ia_css_fragment_slice_desc_t; +typedef struct ia_css_slice_param_section_desc_s + ia_css_slice_param_section_desc_t; + +typedef struct ia_css_spatial_param_terminal_s ia_css_spatial_param_terminal_t; +typedef struct ia_css_frame_grid_desc_s ia_css_frame_grid_desc_t; +typedef struct ia_css_frame_grid_param_section_desc_s + ia_css_frame_grid_param_section_desc_t; +typedef struct ia_css_fragment_grid_desc_s ia_css_fragment_grid_desc_t; + +typedef struct ia_css_param_terminal_s ia_css_param_terminal_t; +typedef struct ia_css_param_section_desc_s ia_css_param_section_desc_t; + +typedef struct ia_css_param_payload_s ia_css_param_payload_t; +typedef struct ia_css_terminal_s ia_css_terminal_t; + +/* =================== Generic Parameter Payload - START =================== */ +#define N_UINT64_IN_PARAM_PAYLOAD_STRUCT 1 +#define N_UINT32_IN_PARAM_PAYLOAD_STRUCT 1 + +#define IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + (N_UINT64_IN_PARAM_PAYLOAD_STRUCT * IA_CSS_UINT64_T_BITS \ + + VIED_VADDRESS_BITS \ + + N_UINT32_IN_PARAM_PAYLOAD_STRUCT * IA_CSS_UINT32_T_BITS) + +struct ia_css_param_payload_s { + /* + * Temporary variable holding the host address of the parameter buffer + * as PSYS is handling the parameters on the host side for the moment + */ + uint64_t host_buffer; + /* + * Base virtual addresses to parameters in subsystem virtual + * memory space + * NOTE: Used in legacy pg flow + */ + vied_vaddress_t buffer; + /* + * Offset to buffer address within external buffer set structure + * NOTE: Used in ppg flow + */ + uint32_t terminal_index; +}; +/* =================== Generic Parameter Payload - End ==================== */ + + +/* ==================== Cached Param Terminal - START ==================== */ +#define N_UINT32_IN_PARAM_SEC_STRUCT 2 + +#define SIZE_OF_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* Frame constant parameters section */ +struct ia_css_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_PARAM_TERMINAL_STRUCT 1 +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT 6 + +#define SIZE_OF_PARAM_TERMINAL_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT16_IN_PARAM_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Frame constant parameters terminal */ +struct ia_css_param_terminal_s { + /* Parameter terminal base */ + ia_css_terminal_t base; + /* Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Points to the variable array of ia_css_param_section_desc_t */ + uint16_t param_section_desc_offset; + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT]; +}; +/* ==================== Cached Param Terminal - End ==================== */ + + +/* ==================== Spatial Param Terminal - START ==================== */ +#define N_UINT16_IN_FRAG_GRID_STRUCT (2 * IA_CSS_N_DATA_DIMENSION) + +#define SIZE_OF_FRAG_GRID_STRUCT_BITS \ + (N_UINT16_IN_FRAG_GRID_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_fragment_grid_desc_s { + /* + * Offset width/height of the top-left compute unit of the + * fragment compared to the frame + */ + uint16_t fragment_grid_index[IA_CSS_N_DATA_DIMENSION]; + /* + * Resolution width/height of the spatial parameters that + * correspond to the fragment measured in compute units + */ + uint16_t fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +#define N_UINT32_IN_FRAME_GRID_PARAM_SEC_STRUCT 3 +#define N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT 4 + +#define SIZE_OF_FRAME_GRID_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_FRAME_GRID_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* + * A plane of parameters with spatial aspect + * (compute units correlated to pixel data) + */ +struct ia_css_frame_grid_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; + /* + * stride in bytes of each line of compute units for + * the specified memory space and region + */ + uint32_t stride; + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT]; +}; + +#define N_UINT16_IN_FRAME_GRID_STRUCT_STRUCT IA_CSS_N_DATA_DIMENSION +#define N_PADDING_UINT8_IN_FRAME_GRID_STRUCT 4 + +#define SIZE_OF_FRAME_GRID_STRUCT_BITS \ + (N_UINT16_IN_FRAME_GRID_STRUCT_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_frame_grid_desc_s { + /* Resolution width/height of the frame of + * spatial parameters measured in compute units + */ + uint16_t frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_STRUCT]; +}; + +#define N_UINT32_IN_SPATIAL_PARAM_TERM_STRUCT 1 +#define N_UINT16_IN_SPATIAL_PARAM_TERM_STRUCT 2 + +#define SIZE_OF_SPATIAL_PARAM_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + SIZE_OF_FRAME_GRID_STRUCT_BITS \ + + N_UINT32_IN_SPATIAL_PARAM_TERM_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_UINT16_IN_SPATIAL_PARAM_TERM_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_spatial_param_terminal_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_t base; + /* Spatial Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Contains info for the frame of spatial parameters */ + ia_css_frame_grid_desc_t frame_grid_desc; + /* Kernel identifier */ + uint32_t kernel_id; + /* + * Points to the variable array of + * ia_css_frame_grid_param_section_desc_t + */ + uint16_t frame_grid_param_section_desc_offset; + /* + * Points to array of ia_css_fragment_spatial_desc_t + * which constain info for the fragments of spatial parameters + */ + uint16_t fragment_grid_desc_offset; +}; +/* ==================== Spatial Param Terminal - END ==================== */ + + +/* ==================== Sliced Param Terminal - START ==================== */ +#define N_UINT32_IN_SLICE_PARAM_SECTION_DESC_STRUCT 2 + +#define SIZE_OF_SLICE_PARAM_SECTION_DESC_STRUCT_BITS \ + (N_UINT32_IN_SLICE_PARAM_SECTION_DESC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* A Slice of parameters ready to be trasferred from/to registers */ +struct ia_css_slice_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_FRAGMENT_SLICE_DESC_STRUCT 2 +#define N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT 4 + +#define SIZE_OF_FRAGMENT_SLICE_DESC_STRUCT_BITS \ + (N_UINT16_IN_FRAGMENT_SLICE_DESC_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_fragment_slice_desc_s { + /* + * Points to array of ia_css_slice_param_section_desc_t + * which constain info for each prameter slice + */ + uint16_t slice_section_desc_offset; + /* Number of slices for the parameters for this fragment */ + uint16_t slice_count; + uint8_t padding[N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT]; +}; + +#define N_UINT32_IN_SLICED_PARAM_TERMINAL_STRUCT 1 +#define N_UINT16_IN_SLICED_PARAM_TERMINAL_STRUCT 1 +#define N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT 2 + +#define SIZE_OF_SLICED_PARAM_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT32_IN_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_UINT16_IN_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_sliced_param_terminal_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_t base; + /* Spatial Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Kernel identifier */ + uint32_t kernel_id; + /* + * Points to array of ia_css_fragment_slice_desc_t + * which constain info for the slicing of the parameters + */ + uint16_t fragment_slice_desc_offset; + uint8_t padding[N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT]; +}; +/* ==================== Sliced Param Terminal - END ==================== */ + + +/* ==================== Program Terminal - START ==================== */ + +#define N_UINT32_IN_FRAG_PARAM_SEC_STRUCT 2 + +#define SIZE_OF_FRAG_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_FRAG_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* Fragment constant parameters section */ +struct ia_css_fragment_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_FRAG_SEQ_COMMAND_STRUCT IA_CSS_N_COMMAND_COUNT + +#define SIZE_OF_FRAG_SEQ_COMMANDS_STRUCT_BITS \ + (N_UINT16_IN_FRAG_SEQ_COMMAND_STRUCT * IA_CSS_UINT16_T_BITS) + +/* 4 commands packe together to save memory space */ +struct ia_css_kernel_fragment_sequencer_command_desc_s { + /* Contains the "(command_index%4) == index" command desc */ + uint16_t line_count[IA_CSS_N_COMMAND_COUNT]; +}; + +#define N_UINT16_IN_FRAG_SEQ_INFO_STRUCT (5 * IA_CSS_N_DATA_DIMENSION + 2) + +#define SIZE_OF_FRAG_SEQ_INFO_STRUCT_BITS \ + (N_UINT16_IN_FRAG_SEQ_INFO_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_kernel_fragment_sequencer_info_desc_s { + /* Slice dimensions */ + uint16_t fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Size of active fragment region */ + int16_t + fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; + /* If >0 it overrides the standard fragment sequencer info */ + uint16_t command_count; + /* + * To be used only if command_count>0, points to the descriptors + * for the commands (ia_css_kernel_fragment_sequencer_command_desc_s) + */ + uint16_t command_desc_offset; +}; + +#define N_UINT16_IN_PROG_TERM_STRUCT 2 +#define N_PADDING_UINT8_IN_PROG_TERM_STRUCT 4 + +#define SIZE_OF_PROG_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT16_IN_PROG_TERM_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_PROG_TERM_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_program_terminal_s { + /* Program terminal base */ + ia_css_terminal_t base; + /* Program terminal buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Points to array of ia_css_fragment_param_desc_s */ + uint16_t fragment_param_section_desc_offset; + /* Points to array of ia_css_kernel_fragment_sequencer_info_s */ + uint16_t kernel_fragment_sequencer_info_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_TERM_STRUCT]; +}; +/* ==================== Program Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c new file mode 100644 index 000000000000..683fb3a88cd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_PARAMETERS__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_param_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_PARAMETERS__ */ +#include "ia_css_terminal_impl.h" +#endif /* __INLINE_PARAMETERS__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h new file mode 100644 index 000000000000..9ccf3931e8e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h @@ -0,0 +1,495 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_IMPL_H +#define __IA_CSS_TERMINAL_IMPL_H + +#include "ia_css_terminal.h" +#include "ia_css_terminal_types.h" +#include "error_support.h" +#include "assert_support.h" +#include "storage_class.h" + +/* Param Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_in_terminal_get_descriptor_size( + const unsigned int nof_sections) +{ + return sizeof(ia_css_param_terminal_t) + + nof_sections*sizeof(ia_css_param_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_section_desc_t *ia_css_param_in_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index) +{ + ia_css_param_section_desc_t *param_section_base; + ia_css_param_section_desc_t *param_section_desc = NULL; + + verifjmpexit(param_terminal != NULL); + + param_section_base = + (ia_css_param_section_desc_t *) + (((const char *)param_terminal) + + param_terminal->param_section_desc_offset); + param_section_desc = &(param_section_base[section_index]); + +EXIT: + return param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_out_terminal_get_descriptor_size( + const unsigned int nof_sections, + const unsigned int nof_fragments) +{ + return sizeof(ia_css_param_terminal_t) + + nof_fragments*nof_sections*sizeof(ia_css_param_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_section_desc_t *ia_css_param_out_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index, + const unsigned int nof_sections, + const unsigned int fragment_index) +{ + ia_css_param_section_desc_t *param_section_base; + ia_css_param_section_desc_t *param_section_desc = NULL; + + verifjmpexit(param_terminal != NULL); + + param_section_base = + (ia_css_param_section_desc_t *) + (((const char *)param_terminal) + + param_terminal->param_section_desc_offset); + param_section_desc = + &(param_section_base[(nof_sections * fragment_index) + + section_index]); + +EXIT: + return param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_param_terminal_create( + ia_css_param_terminal_t *param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal) +{ + if (param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN : + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT; + param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + param_terminal->base.size = terminal_size; + param_terminal->param_section_desc_offset = + sizeof(ia_css_param_terminal_t); + + return 0; +} + +/* Spatial Param Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments) +{ + return sizeof(ia_css_spatial_param_terminal_t) + + nof_frame_param_sections * sizeof( + ia_css_frame_grid_param_section_desc_t) + + nof_fragments * sizeof(ia_css_fragment_grid_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_grid_desc_t * +ia_css_spatial_param_terminal_get_fragment_grid_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int fragment_index) +{ + ia_css_fragment_grid_desc_t *fragment_grid_desc_base; + ia_css_fragment_grid_desc_t *fragment_grid_desc = NULL; + + verifjmpexit(spatial_param_terminal != NULL); + + fragment_grid_desc_base = + (ia_css_fragment_grid_desc_t *) + (((const char *)spatial_param_terminal) + + spatial_param_terminal->fragment_grid_desc_offset); + fragment_grid_desc = &(fragment_grid_desc_base[fragment_index]); + +EXIT: + return fragment_grid_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_frame_grid_param_section_desc_t * +ia_css_spatial_param_terminal_get_frame_grid_param_section_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int section_index) +{ + ia_css_frame_grid_param_section_desc_t * + frame_grid_param_section_base; + ia_css_frame_grid_param_section_desc_t * + frame_grid_param_section_desc = NULL; + + verifjmpexit(spatial_param_terminal != NULL); + + frame_grid_param_section_base = + (ia_css_frame_grid_param_section_desc_t *) + (((const char *)spatial_param_terminal) + + spatial_param_terminal->frame_grid_param_section_desc_offset); + frame_grid_param_section_desc = + &(frame_grid_param_section_base[section_index]); + +EXIT: + return frame_grid_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_spatial_param_terminal_create( + ia_css_spatial_param_terminal_t *spatial_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_fragments, + const uint32_t kernel_id) +{ + if (spatial_param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + spatial_param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN : + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT; + spatial_param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + spatial_param_terminal->base.size = terminal_size; + spatial_param_terminal->kernel_id = kernel_id; + spatial_param_terminal->fragment_grid_desc_offset = + sizeof(ia_css_spatial_param_terminal_t); + spatial_param_terminal->frame_grid_param_section_desc_offset = + spatial_param_terminal->fragment_grid_desc_offset + + (nof_fragments * sizeof(ia_css_fragment_grid_desc_t)); + + return 0; +} + +/* Sliced terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_sliced_param_terminal_get_descriptor_size( + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments) +{ + unsigned int descriptor_size = 0; + unsigned int fragment_index; + unsigned int nof_slices_total = 0; + + verifjmpexit(nof_slices != NULL); + + for (fragment_index = 0; + fragment_index < nof_fragments; fragment_index++) { + nof_slices_total += nof_slices[fragment_index]; + } + + descriptor_size = + sizeof(ia_css_sliced_param_terminal_t) + + nof_fragments*sizeof(ia_css_fragment_slice_desc_t) + + nof_slices_total*nof_slice_param_sections*sizeof( + ia_css_fragment_param_section_desc_t); + +EXIT: + return descriptor_size; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_slice_desc_t * +ia_css_sliced_param_terminal_get_fragment_slice_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index +) +{ + ia_css_fragment_slice_desc_t *fragment_slice_desc_base; + ia_css_fragment_slice_desc_t *fragment_slice_desc = NULL; + + verifjmpexit(sliced_param_terminal != NULL); + + fragment_slice_desc_base = + (ia_css_fragment_slice_desc_t *) + (((const char *)sliced_param_terminal) + + sliced_param_terminal->fragment_slice_desc_offset); + fragment_slice_desc = &(fragment_slice_desc_base[fragment_index]); + +EXIT: + return fragment_slice_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_slice_param_section_desc_t * +ia_css_sliced_param_terminal_get_slice_param_section_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index, + const unsigned int slice_index, + const unsigned int section_index, + const unsigned int nof_slice_param_sections) +{ + ia_css_fragment_slice_desc_t *fragment_slice_desc; + ia_css_slice_param_section_desc_t *slice_param_section_desc_base; + ia_css_slice_param_section_desc_t *slice_param_section_desc = NULL; + + fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_param_terminal, + fragment_index + ); + verifjmpexit(fragment_slice_desc != NULL); + + slice_param_section_desc_base = + (ia_css_slice_param_section_desc_t *) + (((const char *)sliced_param_terminal) + + fragment_slice_desc->slice_section_desc_offset); + slice_param_section_desc = + &(slice_param_section_desc_base[( + slice_index * nof_slice_param_sections) + + section_index]); + +EXIT: + return slice_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_sliced_param_terminal_create( + ia_css_sliced_param_terminal_t *sliced_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments, + const uint32_t kernel_id) +{ + unsigned int fragment_index; + unsigned int nof_slices_total = 0; + + if (sliced_param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + sliced_param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN : + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT; + sliced_param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + sliced_param_terminal->base.size = terminal_size; + sliced_param_terminal->kernel_id = kernel_id; + /* set here to use below to find the pointer */ + sliced_param_terminal->fragment_slice_desc_offset = + sizeof(ia_css_sliced_param_terminal_t); + for (fragment_index = 0; + fragment_index < nof_fragments; fragment_index++) { + ia_css_fragment_slice_desc_t *fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_param_terminal, + fragment_index); + /* + * Error handling not required at this point + * since everything has been constructed/validated just above + */ + fragment_slice_desc->slice_count = nof_slices[fragment_index]; + fragment_slice_desc->slice_section_desc_offset = + sliced_param_terminal->fragment_slice_desc_offset + + (nof_fragments * sizeof( + ia_css_fragment_slice_desc_t)) + + (nof_slices_total * nof_slice_param_sections * sizeof( + ia_css_slice_param_section_desc_t)); + nof_slices_total += nof_slices[fragment_index]; + } + + return 0; +} + +/* Program terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_program_terminal_get_descriptor_size( + const unsigned int nof_fragments, + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs) +{ + return sizeof(ia_css_program_terminal_t) + + nof_fragments * nof_fragment_param_sections * + sizeof(ia_css_fragment_param_section_desc_t) + + nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t) + + nof_command_objs * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_param_section_desc_t * +ia_css_program_terminal_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int section_index, + const unsigned int nof_fragment_param_sections) +{ + ia_css_fragment_param_section_desc_t * + fragment_param_section_desc_base; + ia_css_fragment_param_section_desc_t * + fragment_param_section_desc = NULL; + + verifjmpexit(program_terminal != NULL); + verifjmpexit(section_index < nof_fragment_param_sections); + + fragment_param_section_desc_base = + (ia_css_fragment_param_section_desc_t *) + (((const char *)program_terminal) + + program_terminal->fragment_param_section_desc_offset); + fragment_param_section_desc = + &(fragment_param_section_desc_base[(fragment_index * + nof_fragment_param_sections) + section_index]); + +EXIT: + return fragment_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_kernel_fragment_sequencer_info_desc_t * +ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int info_index, + const unsigned int nof_kernel_fragment_sequencer_infos) +{ + ia_css_kernel_fragment_sequencer_info_desc_t * + kernel_fragment_sequencer_info_desc_base; + ia_css_kernel_fragment_sequencer_info_desc_t * + kernel_fragment_sequencer_info_desc = NULL; + + verifjmpexit(program_terminal != NULL); + if (nof_kernel_fragment_sequencer_infos > 0) { + verifjmpexit(info_index < nof_kernel_fragment_sequencer_infos); + } + + kernel_fragment_sequencer_info_desc_base = + (ia_css_kernel_fragment_sequencer_info_desc_t *) + (((const char *)program_terminal) + + program_terminal->kernel_fragment_sequencer_info_desc_offset); + kernel_fragment_sequencer_info_desc = + &(kernel_fragment_sequencer_info_desc_base[(fragment_index * + nof_kernel_fragment_sequencer_infos) + info_index]); + +EXIT: + return kernel_fragment_sequencer_info_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_create( + ia_css_program_terminal_t *program_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs) +{ + if (program_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + program_terminal->base.terminal_type = IA_CSS_TERMINAL_TYPE_PROGRAM; + program_terminal->base.parent_offset = 0-((int16_t)terminal_offset); + program_terminal->base.size = terminal_size; + program_terminal->kernel_fragment_sequencer_info_desc_offset = + sizeof(ia_css_program_terminal_t); + program_terminal->fragment_param_section_desc_offset = + program_terminal->kernel_fragment_sequencer_info_desc_offset + + (nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (nof_command_objs * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_get_command_base_offset( + const ia_css_program_terminal_t *program_terminal, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int commands_slots_used, + uint16_t *command_desc_offset) +{ + if (command_desc_offset == NULL) { + return -EFAULT; + } + + *command_desc_offset = 0; + + if (program_terminal == NULL) { + return -EFAULT; + } + + *command_desc_offset = + program_terminal->kernel_fragment_sequencer_info_desc_offset + + (nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (commands_slots_used * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +uint16_t *ia_css_program_terminal_get_line_count( + const ia_css_kernel_fragment_sequencer_command_desc_t + *kernel_fragment_sequencer_command_desc_base, + const unsigned int set_count) +{ + uint16_t *line_count = NULL; + + verifjmpexit(kernel_fragment_sequencer_command_desc_base != NULL); + line_count = + (uint16_t *)&(kernel_fragment_sequencer_command_desc_base[ + set_count >> 2].line_count[set_count & 0x00000003]); +EXIT: + return line_count; +} + +#endif /* __IA_CSS_TERMINAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c new file mode 100644 index 000000000000..53c4708c7fc9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_PARAMETERS__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_param_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_PARAMETERS__ */ +#include "ia_css_terminal_manifest_impl.h" +#endif /* __INLINE_PARAMETERS__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h new file mode 100644 index 000000000000..39734136b117 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h @@ -0,0 +1,347 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_IMPL_H +#define __IA_CSS_TERMINAL_MANIFEST_IMPL_H + +#include "ia_css_terminal_manifest.h" +#include "error_support.h" +#include "assert_support.h" +#include "storage_class.h" + +STORAGE_CLASS_INLINE void __terminal_manifest_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_terminal_manifest_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SPATIAL_PARAM_TERM_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_spatial_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_spatial_param_terminal_manifest_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAME_GRID_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_frame_grid_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_frame_grid_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_TERM_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_terminal_manifest_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_fragment_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_KERNEL_FRAG_SEQ_INFO_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_info_manifest_desc_t)) + ); + + COMPILATION_ERROR_IF(0 != sizeof( + ia_css_kernel_fragment_sequencer_info_manifest_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_sliced_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_terminal_manifest_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICED_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof + (ia_css_sliced_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_manifest_section_desc_t) % + sizeof(uint64_t)); +} + +/* Parameter Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_terminal_manifest_get_size( + const unsigned int nof_sections) +{ + + return sizeof(ia_css_param_terminal_manifest_t) + + nof_sections*sizeof(ia_css_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_param_terminal_manifest_init( + ia_css_param_terminal_manifest_t *param_terminal, + const uint16_t section_count) +{ + if (param_terminal == NULL) { + return -EFAULT; + } + + param_terminal->param_manifest_section_desc_count = section_count; + param_terminal->param_manifest_section_desc_offset = sizeof( + ia_css_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_manifest_section_desc_t * +ia_css_param_terminal_manifest_get_prm_sct_desc( + const ia_css_param_terminal_manifest_t *param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_param_manifest_section_desc_t *param_manifest_section_base; + ia_css_param_manifest_section_desc_t * + param_manifest_section_desc = NULL; + + verifjmpexit(param_terminal_manifest != NULL); + + param_manifest_section_base = + (ia_css_param_manifest_section_desc_t *) + (((const char *)param_terminal_manifest) + + param_terminal_manifest->param_manifest_section_desc_offset); + + param_manifest_section_desc = + &(param_manifest_section_base[section_index]); + +EXIT: + return param_manifest_section_desc; +} + +/* Spatial Parameter Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_spatial_param_terminal_manifest_get_size( + const unsigned int nof_frame_param_sections) +{ + return sizeof(ia_css_spatial_param_terminal_manifest_t) + + nof_frame_param_sections * sizeof( + ia_css_frame_grid_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_spatial_param_terminal_manifest_init( + ia_css_spatial_param_terminal_manifest_t *spatial_param_terminal, + const uint16_t section_count) +{ + if (spatial_param_terminal == NULL) { + return -EFAULT; + } + + spatial_param_terminal-> + frame_grid_param_manifest_section_desc_count = section_count; + spatial_param_terminal-> + frame_grid_param_manifest_section_desc_offset = + sizeof(ia_css_spatial_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_frame_grid_param_manifest_section_desc_t * +ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + const ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_frame_grid_param_manifest_section_desc_t * + frame_param_manifest_section_base; + ia_css_frame_grid_param_manifest_section_desc_t * + frame_param_manifest_section_desc = NULL; + + verifjmpexit(spatial_param_terminal_manifest != NULL); + + frame_param_manifest_section_base = + (ia_css_frame_grid_param_manifest_section_desc_t *) + (((const char *)spatial_param_terminal_manifest) + + spatial_param_terminal_manifest-> + frame_grid_param_manifest_section_desc_offset); + frame_param_manifest_section_desc = + &(frame_param_manifest_section_base[section_index]); + +EXIT: + return frame_param_manifest_section_desc; +} + +/* Sliced Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_sliced_param_terminal_manifest_get_size( + const unsigned int nof_slice_param_sections) +{ + return sizeof(ia_css_spatial_param_terminal_manifest_t) + + nof_slice_param_sections * + sizeof(ia_css_sliced_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_sliced_param_terminal_manifest_init( + ia_css_sliced_param_terminal_manifest_t *sliced_param_terminal, + const uint16_t section_count) +{ + if (sliced_param_terminal == NULL) { + return -EFAULT; + } + + sliced_param_terminal->sliced_param_section_count = section_count; + sliced_param_terminal->sliced_param_section_offset = + sizeof(ia_css_sliced_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_sliced_param_manifest_section_desc_t * +ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + const ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_sliced_param_manifest_section_desc_t * + sliced_param_manifest_section_base; + ia_css_sliced_param_manifest_section_desc_t * + sliced_param_manifest_section_desc = NULL; + + verifjmpexit(sliced_param_terminal_manifest != NULL); + + sliced_param_manifest_section_base = + (ia_css_sliced_param_manifest_section_desc_t *) + (((const char *)sliced_param_terminal_manifest) + + sliced_param_terminal_manifest-> + sliced_param_section_offset); + sliced_param_manifest_section_desc = + &(sliced_param_manifest_section_base[section_index]); + +EXIT: + return sliced_param_manifest_section_desc; +} + +/* Program Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_program_terminal_manifest_get_size( + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos) +{ + return sizeof(ia_css_program_terminal_manifest_t) + + nof_fragment_param_sections * + sizeof(ia_css_fragment_param_manifest_section_desc_t) + + nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_manifest_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_manifest_init( + ia_css_program_terminal_manifest_t *program_terminal, + const uint16_t fragment_param_section_count, + const uint16_t kernel_fragment_seq_info_section_count) +{ + if (program_terminal == NULL) { + return -EFAULT; + } + + program_terminal->fragment_param_manifest_section_desc_count = + fragment_param_section_count; + program_terminal->fragment_param_manifest_section_desc_offset = + sizeof(ia_css_program_terminal_manifest_t); + + program_terminal->kernel_fragment_sequencer_info_manifest_info_count = + kernel_fragment_seq_info_section_count; + program_terminal->kernel_fragment_sequencer_info_manifest_info_offset = + sizeof(ia_css_program_terminal_manifest_t) + + fragment_param_section_count*sizeof( + ia_css_fragment_param_manifest_section_desc_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_param_manifest_section_desc_t * +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int section_index) +{ + ia_css_fragment_param_manifest_section_desc_t * + fragment_param_manifest_section_base; + ia_css_fragment_param_manifest_section_desc_t * + fragment_param_manifest_section = NULL; + + verifjmpexit(program_terminal_manifest != NULL); + + fragment_param_manifest_section_base = + (ia_css_fragment_param_manifest_section_desc_t *) + (((const char *)program_terminal_manifest) + + program_terminal_manifest-> + fragment_param_manifest_section_desc_offset); + fragment_param_manifest_section = + &(fragment_param_manifest_section_base[section_index]); + +EXIT: + return fragment_param_manifest_section; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_kernel_fragment_sequencer_info_manifest_desc_t * +ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int info_index) +{ + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + kernel_manifest_fragment_sequencer_info_manifest_desc_base; + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + kernel_manifest_fragment_sequencer_info_manifest_desc = NULL; + + verifjmpexit(program_terminal_manifest != NULL); + + kernel_manifest_fragment_sequencer_info_manifest_desc_base = + (ia_css_kernel_fragment_sequencer_info_manifest_desc_t *) + (((const char *)program_terminal_manifest) + + program_terminal_manifest-> + kernel_fragment_sequencer_info_manifest_info_offset); + + kernel_manifest_fragment_sequencer_info_manifest_desc = + &(kernel_manifest_fragment_sequencer_info_manifest_desc_base[ + info_index]); + +EXIT: + return kernel_manifest_fragment_sequencer_info_manifest_desc; +} + +#endif /* __IA_CSS_TERMINAL_MANIFEST_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/vied_parameters.mk b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/vied_parameters.mk new file mode 100644 index 000000000000..834a1a4b2bab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/lib/vied_parameters/vied_parameters.mk @@ -0,0 +1,76 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is VIED_PARAMETERS + +VIED_PARAMETERS_DIR=$${MODULES_DIR}/vied_parameters + +VIED_PARAMETERS_INTERFACE=$(VIED_PARAMETERS_DIR)/interface +VIED_PARAMETERS_SOURCES=$(VIED_PARAMETERS_DIR)/src +VIED_PARAMETERS_EXTINCLUDE = $${MODULES_DIR}/support + +VIED_PARAMETERS_DYNAMIC_HOST_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal.c +VIED_PARAMETERS_STATIC_HOST_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal_manifest.c + +VIED_PARAMETERS_HOST_FILES = $(VIED_PARAMETERS_DYNAMIC_HOST_FILES) +VIED_PARAMETERS_HOST_FILES += $(VIED_PARAMETERS_STATIC_HOST_FILES) + +VIED_PARAMETERS_ISA_CLIENT_HOST_FILES = $(VIED_PARAMETERS_SOURCES)/ia_css_isys_process_group.c +VIED_PARAMETERS_ISA_CLIENT_HOST_FILES += $(VIED_PARAMETERS_DIR)/client/ia_css_isys_parameter_client.c + +VIED_PARAMETERS_DYNAMIC_FW_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal.c +VIED_PARAMETERS_STATIC_FW_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal_manifest.c + +VIED_PARAMETERS_FW_FILES = $(VIED_PARAMETERS_DYNAMIC_HOST_FILES) +VIED_PARAMETERS_FW_FILES += $(VIED_PARAMETERS_STATIC_HOST_FILES) +VIED_PARAMETERS_SUPPORT_CPPFLAGS = -I$(VIED_PARAMETERS_DIR)/support +VIED_PARAMETERS_SUPPORT_CPPFLAGS += -I$(VIED_PARAMETERS_DIR)/support/$(IPU_SYSVER) +VIED_PARAMETERS_ISA_CLIENT_HOST_CPPFLAGS = -I$(VIED_PARAMETERS_DIR)/client +VIED_PARAMETERS_PSA_UTILS_HOST_FILES = $(MODULES_DIR)/vied_parameters/support/ia_css_psys_parameter_utils.c +VIED_PARAMETERS_PSA_UTILS_HOST_FILES += $(MODULES_DIR)/vied_parameters/support/$(IPU_SYSVER)/ia_css_psys_parameter_utils_dep.c + +VIED_PARAMETERS_UTILS_HOST_CPPFLAGS = $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) + +VIED_PARAMETERS_ISA_UTILS_HOST_FILES = $(MODULES_DIR)/vied_parameters/support/ia_css_isys_parameter_utils.c +VIED_PARAMETERS_ISA_UTILS_HOST_FILES += $(MODULES_DIR)/vied_parameters/support/$(IPU_SYSVER)/ia_css_isys_parameter_utils_dep.c + +VIED_PARAMETERS_PRINT_CPPFLAGS += -I$(VIED_PARAMETERS_DIR)/print/interface +VIED_PARAMETERS_PRINT_FILES += $(VIED_PARAMETERS_DIR)/print/src/ia_css_terminal_print.c + +# VIED_PARAMETERS Trace Log Level = VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +# Other options are [VIED_PARAMETERS_TRACE_LOG_LEVEL_OFF, VIED_PARAMETERS_TRACE_LOG_LEVEL_DEBUG] +ifndef VIED_PARAMETERS_TRACE_CONFIG_HOST + VIED_PARAMETERS_TRACE_CONFIG_HOST=VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +endif +ifndef VIED_PARAMETERS_TRACE_CONFIG_FW + VIED_PARAMETERS_TRACE_CONFIG_FW=VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +endif + +VIED_PARAMETERS_HOST_CPPFLAGS += -DVIED_PARAMETERS_TRACE_CONFIG=$(VIED_PARAMETERS_TRACE_CONFIG_HOST) +VIED_PARAMETERS_FW_CPPFLAGS += -DVIED_PARAMETERS_TRACE_CONFIG=$(VIED_PARAMETERS_TRACE_CONFIG_FW) + +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_INTERFACE) +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_SOURCES) +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_EXTINCLUDE) +VIED_PARAMETERS_HOST_CPPFLAGS += $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_INTERFACE) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_SOURCES) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_EXTINCLUDE) +VIED_PARAMETERS_FW_CPPFLAGS += $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) + +#For IPU interface +include $(MODULES_DIR)/fw_abi_common_types/cpu/fw_abi_cpu_types.mk +VIED_PARAMETERS_HOST_CPPFLAGS += $(FW_ABI_COMMON_TYPES_HOST_CPPFLAGS) + +VIED_PARAMETERS_FW_CPPFLAGS += $(FW_ABI_COMMON_TYPES_FW_CPPFLAGS) diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.c b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.c new file mode 100644 index 000000000000..38893935bf37 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2015--2018 Intel Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include + +#include "ipu.h" +#include "ipu-mmu.h" +#include "ipu-psys.h" +#include "ipu-fw-psys.h" +#include "ipu-wrapper.h" +#include "libcsspsys2600.h" + +#include +#include +#include +#include +#include + +int ipu_fw_psys_pg_start(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_start((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_start); + +int ipu_fw_psys_pg_disown(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_disown((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_disown); + +int ipu_fw_psys_pg_abort(struct ipu_psys_kcmd *kcmd) +{ + int rval; + + rval = ia_css_process_group_stop((ia_css_process_group_t *) + kcmd->kpg->pg); + if (rval) { + dev_err(&kcmd->fh->psys->adev->dev, + "failed to abort kcmd!\n"); + kcmd->pg_user = NULL; + rval = -EIO; + /* TODO: need to reset PSYS by power cycling it */ + } + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_abort); + +int ipu_fw_psys_pg_submit(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_submit((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_submit); + +static void *syscom_buffer; +static struct ia_css_syscom_config *syscom_config; +static struct ia_css_psys_server_init *server_init; + +int ipu_fw_psys_rcv_event(struct ipu_psys *psys, + struct ipu_fw_psys_event *event) +{ + return ia_css_psys_event_queue_receive(psys_syscom, + IA_CSS_PSYS_EVENT_QUEUE_MAIN_ID, + (struct ia_css_psys_event_s *)event); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_rcv_event); + +int ipu_fw_psys_terminal_set(struct ipu_fw_psys_terminal *terminal, + int terminal_idx, + struct ipu_psys_kcmd *kcmd, + u32 buffer, + unsigned size) +{ + ia_css_terminal_type_t type; + u32 buffer_state; + + type = ia_css_terminal_get_type((ia_css_terminal_t *)terminal); + + switch (type) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + case IA_CSS_TERMINAL_TYPE_PROGRAM: + buffer_state = IA_CSS_BUFFER_UNDEFINED; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_STREAM: + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_STATE_IN: + buffer_state = IA_CSS_BUFFER_FULL; + break; + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + case IA_CSS_TERMINAL_TYPE_STATE_OUT: + buffer_state = IA_CSS_BUFFER_EMPTY; + break; + default: + dev_err(&kcmd->fh->psys->adev->dev, + "unknown terminal type: 0x%x\n", type); + return -EAGAIN; + } + + if (type == IA_CSS_TERMINAL_TYPE_DATA_IN || + type == IA_CSS_TERMINAL_TYPE_DATA_OUT) { + ia_css_frame_t *frame; + + if (ia_css_data_terminal_set_connection_type( + (ia_css_data_terminal_t *)terminal, + IA_CSS_CONNECTION_MEMORY)) + return -EIO; + frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + if (!frame) + return -EIO; + + if (ia_css_frame_set_data_bytes(frame, size)) + return -EIO; + } + + return -ia_css_process_group_attach_buffer( + (ia_css_process_group_t *)kcmd->kpg->pg, buffer, + buffer_state, terminal_idx); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_terminal_set); + +void ipu_fw_psys_pg_dump(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd, + const char *note) +{ + ia_css_process_group_t *pg = (ia_css_process_group_t *)kcmd->kpg->pg; + ia_css_program_group_ID_t pgid = + ia_css_process_group_get_program_group_ID(pg); + uint8_t processes = ia_css_process_group_get_process_count( + (ia_css_process_group_t *)kcmd->kpg->pg); + unsigned int p; + + dev_dbg(&psys->adev->dev, "%s %s pgid %i processes %i\n", + __func__, note, pgid, processes); + for (p = 0; p < processes; p++) { + ia_css_process_t *process = + ia_css_process_group_get_process(pg, p); + + dev_dbg(&psys->adev->dev, + "%s pgid %i process %i cell %i dev_chn: ext0 %i ext1r %i ext1w %i int %i ipfd %i isa %i\n", + __func__, pgid, p, + ia_css_process_get_cell(process), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_EXT0_ID), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_EXT1_READ_ID), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_ID), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_INTERNAL_ID), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_IPFD_ID), + ia_css_process_get_dev_chn(process, + VIED_NCI_DEV_CHN_DMA_ISA_ID)); + } +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_dump); + +int ipu_fw_psys_pg_get_id(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_program_group_ID( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_id); + +int ipu_fw_psys_pg_get_terminal_count(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_terminal_count( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_terminal_count); + +int ipu_fw_psys_pg_get_size(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_size((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_size); + +int ipu_fw_psys_pg_set_ipu_vaddress(struct ipu_psys_kcmd *kcmd, + dma_addr_t vaddress) +{ + return ia_css_process_group_set_ipu_vaddress((ia_css_process_group_t *) + kcmd->kpg->pg, vaddress); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_set_ipu_vaddress); + +int ipu_fw_psys_pg_load_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_load_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_load_cycles); + +int ipu_fw_psys_pg_init_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_init_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_init_cycles); + +int ipu_fw_psys_pg_processing_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_processing_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_processing_cycles); + +struct ipu_fw_psys_terminal * +ipu_fw_psys_pg_get_terminal(struct ipu_psys_kcmd *kcmd, int index) +{ + return (struct ipu_fw_psys_terminal *)ia_css_process_group_get_terminal( + (ia_css_process_group_t *)kcmd->kpg->pg, index); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_terminal); + +void ipu_fw_psys_pg_set_token(struct ipu_psys_kcmd *kcmd, u64 token) +{ + ia_css_process_group_set_token((ia_css_process_group_t *)kcmd->kpg->pg, + token); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_set_token); + +int ipu_fw_psys_pg_get_protocol( + struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_protocol_version( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_protocol); + +static int libcsspsys2600_init(void); +int ipu_fw_psys_open(struct ipu_psys *psys) +{ + bool opened; + int retry = IPU_PSYS_OPEN_RETRY; + + ipu_wrapper_init(PSYS_MMID, &psys->adev->dev, psys->pdata->base); + /* When fw psys open, make sure csslib init first */ + libcsspsys2600_init(); + + server_init->icache_prefetch_sp = psys->icache_prefetch_sp; + server_init->icache_prefetch_isp = psys->icache_prefetch_isp; + + psys_syscom = ia_css_psys_open(syscom_buffer, syscom_config); + if (!psys_syscom) { + dev_err(&psys->adev->dev, + "psys library open failed\n"); + return -ENODEV; + } + + do { + opened = ia_css_psys_open_is_ready(psys_syscom); + if (opened) + break; + usleep_range(IPU_PSYS_OPEN_TIMEOUT_US, + IPU_PSYS_OPEN_TIMEOUT_US + 10); + retry--; + } while (retry > 0); + + if (!retry && !opened) { + dev_err(&psys->adev->dev, + "psys library open ready failed\n"); + ia_css_psys_close(psys_syscom); + ia_css_psys_release(psys_syscom, 1); + psys_syscom = NULL; + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_open); + +int ipu_fw_psys_close(struct ipu_psys *psys) +{ + int rval; + unsigned int retry = IPU_PSYS_CLOSE_TIMEOUT; + + if (!psys_syscom) + return 0; + + if (ia_css_psys_close(psys_syscom)) { + dev_err(&psys->adev->dev, + "psys library close ready failed\n"); + return 0; + } + + do { + rval = ia_css_psys_release(psys_syscom, 0); + if (rval && rval != -EBUSY) { + dev_dbg(&psys->adev->dev, "psys library release failed\n"); + break; + } + usleep_range(IPU_PSYS_CLOSE_TIMEOUT_US, + IPU_PSYS_CLOSE_TIMEOUT_US + 10); + } while (rval && --retry); + + psys_syscom = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_close); + +u64 ipu_fw_psys_pg_get_token(struct ipu_psys_kcmd *kcmd) +{ + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_token); + +static const struct ipu_fw_resource_definitions default_defs = { + .cells = vied_nci_cell_type, + .num_cells = VIED_NCI_N_CELL_ID, + .num_cells_type = VIED_NCI_N_CELL_TYPE_ID, + .dev_channels = vied_nci_dev_chn_size, + .num_dev_channels = VIED_NCI_N_DEV_CHN_ID, + + .num_ext_mem_types = VIED_NCI_N_DATA_MEM_TYPE_ID, + .num_ext_mem_ids = VIED_NCI_N_MEM_ID, + .ext_mem_ids = vied_nci_mem_size, + + .cell_mem_row = VIED_NCI_N_MEM_TYPE_ID, + .cell_mem = (enum ipu_mem_id *)vied_nci_cell_mem, +}; + +const struct ipu_fw_resource_definitions *res_defs = &default_defs; +EXPORT_SYMBOL_GPL(res_defs); + +int ipu_fw_psys_set_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index, + u8 value) +{ + return ia_css_process_set_cell((ia_css_process_t *)ptr, + (vied_nci_cell_ID_t)value); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_cell_id); + +u8 ipu_fw_psys_get_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index) +{ + return ia_css_process_get_cell((ia_css_process_t *)ptr); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_get_process_cell_id); + +int ipu_fw_psys_clear_process_cell(struct ipu_fw_psys_process *ptr) +{ + return ia_css_process_clear_cell((ia_css_process_t *)ptr); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_clear_process_cell); + +int ipu_fw_psys_set_process_dev_chn_offset(struct ipu_fw_psys_process *ptr, + u16 offset, u16 value) +{ + return ia_css_process_set_dev_chn((ia_css_process_t *)ptr, + (vied_nci_dev_chn_ID_t)offset, + (vied_nci_resource_size_t)value); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_dev_chn_offset); + +int ipu_fw_psys_set_process_ext_mem(struct ipu_fw_psys_process *ptr, + u16 type_id, u16 mem_id, u16 offset) +{ + return ia_css_process_set_ext_mem((ia_css_process_t *)ptr, mem_id, offset); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_ext_mem); + +int ipu_fw_psys_get_program_manifest_by_process( + struct ipu_fw_generic_program_manifest *gen_pm, + const struct ipu_fw_psys_program_group_manifest *pg_manifest, + struct ipu_fw_psys_process *process) +{ + ia_css_program_ID_t process_id = + ia_css_process_get_program_ID( + (const ia_css_process_t *)process); + int programs = + ia_css_program_group_manifest_get_program_count( + (const ia_css_program_group_manifest_t *)pg_manifest); + int i; + + for (i = 0; i < programs; i++) { + ia_css_program_ID_t program_id; + ia_css_program_manifest_t *pm = + ia_css_program_group_manifest_get_prgrm_mnfst( + (const ia_css_program_group_manifest_t *) + pg_manifest, i); + if (!pm) + continue; + program_id = ia_css_program_manifest_get_program_ID(pm); + if (program_id == process_id) { + gen_pm->dev_chn_size = (u16 *)pm->dev_chn_size; + gen_pm->ext_mem_size = (u16 *)pm->ext_mem_size; + gen_pm->cell_id = pm->cell_id; + gen_pm->cell_type_id = pm->cell_type_id; + return 0; + } + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_get_program_manifest_by_process); + +static int libcsspsys2600_init(void) +{ + int rval; + static bool csslib_init; + + if (csslib_init) + return 0; + + syscom_buffer = kzalloc(ia_css_sizeof_psys(NULL), GFP_KERNEL); + if (!syscom_buffer) + return -ENOMEM; + + syscom_config = kzalloc(sizeof(struct ia_css_syscom_config), + GFP_KERNEL); + if (!syscom_config) { + rval = -ENOMEM; + goto out_syscom_buffer_free; + } + + server_init = kzalloc(sizeof(struct ia_css_psys_server_init), + GFP_KERNEL); + if (!server_init) { + rval = -ENOMEM; + goto out_syscom_config_free; + } + + server_init->ddr_pkg_dir_address = 0; + server_init->host_ddr_pkg_dir = 0; + server_init->pkg_dir_size = 0; + + *syscom_config = *ia_css_psys_specify(); + syscom_config->specific_addr = server_init; + syscom_config->specific_size = sizeof(struct ia_css_psys_server_init); + syscom_config->ssid = PSYS_SSID; + syscom_config->mmid = PSYS_MMID; + syscom_config->regs_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_REGS); + syscom_config->dmem_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_DMEM); + csslib_init = true; + + return 0; + +out_syscom_config_free: + kfree(syscom_config); +out_syscom_buffer_free: + kfree(syscom_buffer); + + return rval; +} + +static void __exit libcsspsys2600_exit(void) +{ + kfree(syscom_buffer); + kfree(syscom_config); + kfree(server_init); +} + +module_init(libcsspsys2600_init); +module_exit(libcsspsys2600_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu psys css library"); diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.h b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.h new file mode 100644 index 000000000000..b8d790f56180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/lib2600psys/libcsspsys2600.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015--2018 Intel Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LIBCSSPSYS2600_H +#define LIBCSSPSYS2600_H + +#include +#include +#include +#include +#include +#include +#include + +extern struct ia_css_syscom_context *psys_syscom; +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4-css/libintel-ipu4.c b/drivers/media/pci/intel/ipu4/ipu4-css/libintel-ipu4.c new file mode 100644 index 000000000000..a7128898e449 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-css/libintel-ipu4.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include +#include +#include "ipu-isys.h" +#include "ipu-wrapper.h" +#include + +#include "ipu-platform.h" + +#define ipu_lib_call_notrace_unlocked(func, isys, ...) \ + ({ \ + int rval; \ + \ + rval = -ia_css_isys_##func((isys)->fwcom, ##__VA_ARGS__); \ + \ + rval; \ + }) + +#define ipu_lib_call_notrace(func, isys, ...) \ + ({ \ + int rval; \ + \ + mutex_lock(&(isys)->lib_mutex); \ + \ + rval = ipu_lib_call_notrace_unlocked( \ + func, isys, ##__VA_ARGS__); \ + \ + mutex_unlock(&(isys)->lib_mutex); \ + \ + rval; \ + }) + +#define ipu_lib_call(func, isys, ...) \ + ({ \ + int rval; \ + dev_dbg(&(isys)->adev->dev, "hostlib: libcall %s\n", #func); \ + rval = ipu_lib_call_notrace(func, isys, ##__VA_ARGS__); \ + \ + rval; \ + }) + +static int wrapper_init_done; + +int ipu_fw_isys_close(struct ipu_isys *isys) +{ + struct device *dev = &isys->adev->dev; + int timeout = IPU_ISYS_TURNOFF_TIMEOUT; + int rval; + unsigned long flags; + + /* + * Ask library to stop the isys fw. Actual close takes + * some time as the FW must stop its actions including code fetch + * to SP icache. + */ + spin_lock_irqsave(&isys->power_lock, flags); + rval = ipu_lib_call(device_close, isys); + spin_unlock_irqrestore(&isys->power_lock, flags); + if (rval) + dev_err(dev, "Device close failure: %d\n", rval); + + /* release probably fails if the close failed. Let's try still */ + do { + usleep_range(IPU_ISYS_TURNOFF_DELAY_US, + 2 * IPU_ISYS_TURNOFF_DELAY_US); + rval = ipu_lib_call_notrace(device_release, isys, 0); + timeout--; + } while (rval != 0 && timeout); + + /* Spin lock to wait the interrupt handler to be finished */ + spin_lock_irqsave(&isys->power_lock, flags); + if (!rval) + isys->fwcom = NULL; /* No further actions needed */ + else + dev_err(dev, "Device release time out %d\n", rval); + spin_unlock_irqrestore(&isys->power_lock, flags); + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_close); + +int ipu_fw_isys_init(struct ipu_isys *isys, + unsigned int num_streams) +{ + int retry = IPU_ISYS_OPEN_RETRY; + unsigned int i; + + struct ia_css_isys_device_cfg_data isys_cfg = { + .driver_sys = { + .ssid = ISYS_SSID, + .mmid = ISYS_MMID, + .num_send_queues = clamp_t( + unsigned int, num_streams, 1, + IPU_ISYS_NUM_STREAMS), + .num_recv_queues = IPU_ISYS_NUM_RECV_QUEUE, + .send_queue_size = IPU_ISYS_SIZE_SEND_QUEUE, + .recv_queue_size = IPU_ISYS_SIZE_RECV_QUEUE, + .icache_prefetch = isys->icache_prefetch, + }, + }; + struct device *dev = &isys->adev->dev; + int rval; + + if (!wrapper_init_done) { + wrapper_init_done = true; + ipu_wrapper_init(ISYS_MMID, &isys->adev->dev, + isys->pdata->base); + } + + /* + * SRAM partitioning. Initially equal partitioning is set + * TODO: Fine tune the partitining based on the stream pixel load + */ + for (i = 0; i < min(IPU_NOF_SRAM_BLOCKS_MAX, NOF_SRAM_BLOCKS_MAX); i++) { + if (i < isys_cfg.driver_sys.num_send_queues) + isys_cfg.buffer_partition.num_gda_pages[i] = + (IPU_DEVICE_GDA_NR_PAGES * + IPU_DEVICE_GDA_VIRT_FACTOR) / + isys_cfg.driver_sys.num_send_queues; + else + isys_cfg.buffer_partition.num_gda_pages[i] = 0; + } + + rval = -ia_css_isys_device_open(&isys->fwcom, &isys_cfg); + if (rval < 0) { + dev_err(dev, "isys device open failed %d\n", rval); + return rval; + } + + do { + usleep_range(IPU_ISYS_OPEN_TIMEOUT_US, + IPU_ISYS_OPEN_TIMEOUT_US + 10); + rval = ipu_lib_call(device_open_ready, isys); + if (!rval) + break; + retry--; + } while (retry > 0); + + if (!retry && rval) { + dev_err(dev, "isys device open ready failed %d\n", rval); + ipu_fw_isys_close(isys); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_init); + +void ipu_fw_isys_cleanup(struct ipu_isys *isys) +{ + ipu_lib_call(device_release, isys, 1); + isys->fwcom = NULL; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_cleanup); + +struct ipu_fw_isys_resp_info_abi *ipu_fw_isys_get_resp( + void *context, unsigned int queue, + struct ipu_fw_isys_resp_info_abi *response) +{ + struct ia_css_isys_resp_info apiresp; + int rval; + + rval = -ia_css_isys_stream_handle_response(context, &apiresp); + if (rval < 0) + return NULL; + + response->buf_id = 0; + response->type = apiresp.type; + response->timestamp[0] = apiresp.timestamp[0]; + response->timestamp[1] = apiresp.timestamp[1]; + response->stream_handle = apiresp.stream_handle; + response->error_info.error = apiresp.error; + response->error_info.error_details = apiresp.error_details; + response->pin.out_buf_id = apiresp.pin.out_buf_id; + response->pin.addr = apiresp.pin.addr; + response->pin_id = apiresp.pin_id; + response->process_group_light.param_buf_id = + apiresp.process_group_light.param_buf_id; + response->process_group_light.addr = + apiresp.process_group_light.addr; + response->acc_id = apiresp.acc_id; +#ifdef IPU_OTF_SUPPORT + response->frame_counter = apiresp.frame_counter; + response->written_direct = apiresp.written_direct; +#endif + + return response; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_get_resp); + +void ipu_fw_isys_put_resp(void *context, unsigned int queue) +{ + /* Nothing to do here really */ +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_put_resp); + +int ipu_fw_isys_simple_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + enum ipu_fw_isys_send_type send_type) +{ + int rval = -1; + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_START: + rval = ipu_lib_call(stream_start, isys, stream_handle, + NULL); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_FLUSH: + rval = ipu_lib_call(stream_flush, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_STOP: + rval = ipu_lib_call(stream_stop, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE: + rval = ipu_lib_call(stream_close, isys, stream_handle); + break; + default: + WARN_ON(1); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_simple_cmd); + +static void resolution_abi_to_api(const struct ipu_fw_isys_resolution_abi *abi, + struct ia_css_isys_resolution *api) +{ + api->width = abi->width; + api->height = abi->height; +} + +static void output_pin_payload_abi_to_api( + struct ipu_fw_isys_output_pin_payload_abi *abi, + struct ia_css_isys_output_pin_payload *api) +{ + api->out_buf_id = abi->out_buf_id; + api->addr = abi->addr; +} + +static void output_pin_info_abi_to_api( + struct ipu_fw_isys_output_pin_info_abi *abi, + struct ia_css_isys_output_pin_info *api) +{ + api->input_pin_id = abi->input_pin_id; + resolution_abi_to_api(&abi->output_res, &api->output_res); + api->stride = abi->stride; + api->pt = abi->pt; + api->watermark_in_lines = abi->watermark_in_lines; + api->payload_buf_size = abi->payload_buf_size; + api->send_irq = abi->send_irq; + api->ft = abi->ft; +#ifdef IPU_OTF_SUPPORT + api->link_id = abi->link_id; +#endif + api->reserve_compression = abi->reserve_compression; +} + +static void param_pin_abi_to_api(struct ipu_fw_isys_param_pin_abi *abi, + struct ia_css_isys_param_pin *api) +{ + api->param_buf_id = abi->param_buf_id; + api->addr = abi->addr; +} + +static void input_pin_info_abi_to_api( + struct ipu_fw_isys_input_pin_info_abi *abi, + struct ia_css_isys_input_pin_info *api) +{ + resolution_abi_to_api(&abi->input_res, &api->input_res); + api->dt = abi->dt; + api->mipi_store_mode = abi->mipi_store_mode; + api->mapped_dt = abi->mapped_dt; +} + +static void isa_cfg_abi_to_api(const struct ipu_fw_isys_isa_cfg_abi *abi, + struct ia_css_isys_isa_cfg *api) +{ + unsigned int i; + + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) + resolution_abi_to_api(&abi->isa_res[i], &api->isa_res[i]); + + api->blc_enabled = abi->cfg.blc; + api->lsc_enabled = abi->cfg.lsc; + api->dpc_enabled = abi->cfg.dpc; + api->downscaler_enabled = abi->cfg.downscaler; + api->awb_enabled = abi->cfg.awb; + api->af_enabled = abi->cfg.af; + api->ae_enabled = abi->cfg.ae; + api->paf_type = abi->cfg.paf; + api->send_irq_stats_ready = abi->cfg.send_irq_stats_ready; + api->send_resp_stats_ready = abi->cfg.send_irq_stats_ready; +} + +static void cropping_abi_to_api(struct ipu_fw_isys_cropping_abi *abi, + struct ia_css_isys_cropping *api) +{ + api->top_offset = abi->top_offset; + api->left_offset = abi->left_offset; + api->bottom_offset = abi->bottom_offset; + api->right_offset = abi->right_offset; +} + +static void stream_cfg_abi_to_api(struct ipu_fw_isys_stream_cfg_data_abi *abi, + struct ia_css_isys_stream_cfg_data *api) +{ + unsigned int i; + + api->src = abi->src; + api->vc = abi->vc; + api->isl_use = abi->isl_use; + api->compfmt = abi->compfmt; + isa_cfg_abi_to_api(&abi->isa_cfg, &api->isa_cfg); + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) + cropping_abi_to_api(&abi->crop[i], &api->crop[i]); + + api->send_irq_sof_discarded = abi->send_irq_sof_discarded; + api->send_irq_eof_discarded = abi->send_irq_eof_discarded; + api->send_resp_sof_discarded = abi->send_irq_sof_discarded; + api->send_resp_eof_discarded = abi->send_irq_eof_discarded; + api->nof_input_pins = abi->nof_input_pins; + api->nof_output_pins = abi->nof_output_pins; + for (i = 0; i < abi->nof_input_pins; i++) + input_pin_info_abi_to_api(&abi->input_pins[i], + &api->input_pins[i]); + + for (i = 0; i < abi->nof_output_pins; i++) + output_pin_info_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); +} + +static void frame_buff_set_abi_to_api( + struct ipu_fw_isys_frame_buff_set_abi *abi, + struct ia_css_isys_frame_buff_set *api) +{ + int i; + + for (i = 0; i < min(IPU_MAX_OPINS, MAX_OPINS); i++) + output_pin_payload_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); + + param_pin_abi_to_api(&abi->process_group_light, + &api->process_group_light); + + api->send_irq_sof = abi->send_irq_sof; + api->send_irq_eof = abi->send_irq_eof; + api->send_irq_capture_ack = abi->send_irq_capture_ack; + api->send_irq_capture_done = abi->send_irq_capture_done; +} + +int ipu_fw_isys_complex_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, + enum ipu_fw_isys_send_type send_type) +{ + union { + struct ia_css_isys_stream_cfg_data stream_cfg; + struct ia_css_isys_frame_buff_set buf; + } param; + int rval = -1; + + memset(¶m, 0, sizeof(param)); + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_capture_indication, + isys, stream_handle, ¶m.buf); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_OPEN: + stream_cfg_abi_to_api(cpu_mapped_buf, ¶m.stream_cfg); + rval = ipu_lib_call(stream_open, isys, stream_handle, + ¶m.stream_cfg); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_start, isys, stream_handle, + ¶m.buf); + break; + default: + WARN_ON(1); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_complex_cmd); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu library"); diff --git a/drivers/media/pci/intel/ipu4/ipu4-fw-resources.c b/drivers/media/pci/intel/ipu4/ipu4-fw-resources.c new file mode 100644 index 000000000000..73089764b79a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-fw-resources.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015 - 2018 Intel Corporation + +#include "ipu-fw-psys.h" + +#include + +/* resources table */ +/* + * Cell types by cell IDs + */ +const u32 ipu_fw_psys_cell_types[IPU_FW_PSYS_N_CELL_ID] = { + IPU_FW_PSYS_SP_CTRL_TYPE_ID, + IPU_FW_PSYS_SP_SERVER_TYPE_ID, + IPU_FW_PSYS_SP_SERVER_TYPE_ID, + IPU_FW_PSYS_VP_TYPE_ID, + IPU_FW_PSYS_VP_TYPE_ID, + IPU_FW_PSYS_VP_TYPE_ID, + IPU_FW_PSYS_VP_TYPE_ID, + IPU_FW_PSYS_ACC_ISA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_PSA_TYPE_ID, + IPU_FW_PSYS_ACC_OSA_TYPE_ID, + IPU_FW_PSYS_GDC_TYPE_ID, + IPU_FW_PSYS_GDC_TYPE_ID +}; + +const u16 ipu_fw_num_dev_channels[IPU_FW_PSYS_N_DEV_CHN_ID] = { + IPU_FW_PSYS_DEV_CHN_DMA_EXT0_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_GDC_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_EXT1_READ_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_INTERNAL_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_IPFD_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_ISA_MAX_SIZE, + IPU_FW_PSYS_DEV_CHN_DMA_FW_MAX_SIZE, +#ifdef CONFIG_VIDEO_INTEL_IPU4P + IPU_FW_PSYS_DEV_CHN_DMA_CMPRS_MAX_SIZE +#endif +}; + +const u16 ipu_fw_psys_mem_size[IPU_FW_PSYS_N_MEM_ID] = { + IPU_FW_PSYS_VMEM0_MAX_SIZE, + IPU_FW_PSYS_VMEM1_MAX_SIZE, + IPU_FW_PSYS_VMEM2_MAX_SIZE, + IPU_FW_PSYS_VMEM3_MAX_SIZE, + IPU_FW_PSYS_VMEM4_MAX_SIZE, + IPU_FW_PSYS_BAMEM0_MAX_SIZE, + IPU_FW_PSYS_BAMEM1_MAX_SIZE, + IPU_FW_PSYS_BAMEM2_MAX_SIZE, + IPU_FW_PSYS_BAMEM3_MAX_SIZE, + IPU_FW_PSYS_DMEM0_MAX_SIZE, + IPU_FW_PSYS_DMEM1_MAX_SIZE, + IPU_FW_PSYS_DMEM2_MAX_SIZE, + IPU_FW_PSYS_DMEM3_MAX_SIZE, + IPU_FW_PSYS_DMEM4_MAX_SIZE, + IPU_FW_PSYS_DMEM5_MAX_SIZE, + IPU_FW_PSYS_DMEM6_MAX_SIZE, + IPU_FW_PSYS_DMEM7_MAX_SIZE, + IPU_FW_PSYS_PMEM0_MAX_SIZE, + IPU_FW_PSYS_PMEM1_MAX_SIZE, + IPU_FW_PSYS_PMEM2_MAX_SIZE, + IPU_FW_PSYS_PMEM3_MAX_SIZE +}; + +const enum ipu_mem_id +ipu_fw_psys_cell_mem[IPU_FW_PSYS_N_CELL_ID][IPU_FW_PSYS_N_MEM_TYPE_ID] = { + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_DMEM0_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_DMEM1_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_DMEM2_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_VMEM4_ID, + IPU_FW_PSYS_DMEM4_ID, + IPU_FW_PSYS_VMEM0_ID, + IPU_FW_PSYS_BAMEM0_ID, + IPU_FW_PSYS_PMEM0_ID + }, + { + IPU_FW_PSYS_VMEM4_ID, + IPU_FW_PSYS_DMEM5_ID, + IPU_FW_PSYS_VMEM1_ID, + IPU_FW_PSYS_BAMEM1_ID, + IPU_FW_PSYS_PMEM1_ID + }, + { + IPU_FW_PSYS_VMEM4_ID, + IPU_FW_PSYS_DMEM6_ID, + IPU_FW_PSYS_VMEM2_ID, + IPU_FW_PSYS_BAMEM2_ID, + IPU_FW_PSYS_PMEM2_ID, + }, + { + IPU_FW_PSYS_VMEM4_ID, + IPU_FW_PSYS_DMEM7_ID, + IPU_FW_PSYS_VMEM3_ID, + IPU_FW_PSYS_BAMEM3_ID, + IPU_FW_PSYS_PMEM3_ID, + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + }, + { + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID, + IPU_FW_PSYS_N_MEM_ID + } +}; + +static const struct ipu_fw_resource_definitions default_defs = { + .cells = ipu_fw_psys_cell_types, + .num_cells = IPU_FW_PSYS_N_CELL_ID, + .num_cells_type = IPU_FW_PSYS_N_CELL_TYPE_ID, + + .dev_channels = ipu_fw_num_dev_channels, + .num_dev_channels = IPU_FW_PSYS_N_DEV_CHN_ID, + + .num_ext_mem_types = IPU_FW_PSYS_N_DATA_MEM_TYPE_ID, + .num_ext_mem_ids = IPU_FW_PSYS_N_MEM_ID, + .ext_mem_ids = ipu_fw_psys_mem_size, + + .num_dfm_ids = IPU_FW_PSYS_N_DEV_DFM_ID, + + .cell_mem_row = IPU_FW_PSYS_N_MEM_TYPE_ID, + .cell_mem = &ipu_fw_psys_cell_mem[0][0], + + .process.ext_mem_id = offsetof(struct ipu_fw_psys_process, + ext_mem_id[0]), + .process.ext_mem_offset = offsetof(struct ipu_fw_psys_process, + ext_mem_offset[0]), + .process.dev_chn_offset = offsetof(struct ipu_fw_psys_process, + dev_chn_offset[0]), + .process.cell_id = offsetof(struct ipu_fw_psys_process, cell_id), +}; + +const struct ipu_fw_resource_definitions *res_defs = &default_defs; + +/********** Generic resource handling **********/ + +/* + * Extension library gives byte offsets to its internal structures. + * use those offsets to update fields. Without extension lib access + * structures directly. + */ +int ipu_fw_psys_set_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index, + u8 value) +{ + struct ipu_fw_psys_process_group *parent = + (struct ipu_fw_psys_process_group *) ((char *)ptr + + ptr->parent_offset); + + ptr->cell_id = value; + parent->resource_bitmap |= 1 << value; + + return 0; +} + +u8 ipu_fw_psys_get_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index) +{ + return ptr->cell_id; +} + +int ipu_fw_psys_clear_process_cell(struct ipu_fw_psys_process *ptr) +{ + struct ipu_fw_psys_process_group *parent; + u8 cell_id = ipu_fw_psys_get_process_cell_id(ptr, 0); + int retval = -1; + + parent = (struct ipu_fw_psys_process_group *) ((char *)ptr + + ptr->parent_offset); + if ((1 << cell_id) && ((1 << cell_id) & parent->resource_bitmap)) { + ipu_fw_psys_set_process_cell_id(ptr, 0, IPU_FW_PSYS_N_CELL_ID); + parent->resource_bitmap &= ~(1 << cell_id); + retval = 0; + } + + return retval; +} + +int ipu_fw_psys_set_process_dev_chn_offset(struct ipu_fw_psys_process *ptr, + u16 offset, u16 value) +{ + ptr->dev_chn_offset[offset] = value; + + return 0; +} + +int ipu_fw_psys_set_process_ext_mem(struct ipu_fw_psys_process *ptr, + u16 type_id, u16 mem_id, u16 offset) +{ + ptr->ext_mem_offset[type_id] = offset; + ptr->ext_mem_id[type_id] = mem_id; + + return 0; +} + +static struct ipu_fw_psys_program_manifest * +ipu_resource_get_program_manifest( + const struct ipu_fw_psys_program_group_manifest *manifest, + const unsigned int program_index) +{ + struct ipu_fw_psys_program_manifest *prg_manifest_base; + u8 *program_manifest = NULL; + u8 program_count; + unsigned int i; + + program_count = manifest->program_count; + + prg_manifest_base = (struct ipu_fw_psys_program_manifest *) + ((char *)manifest + manifest->program_manifest_offset); + if (program_index < program_count) { + program_manifest = (u8 *) prg_manifest_base; + for (i = 0; i < program_index; i++) + program_manifest += + ((struct ipu_fw_psys_program_manifest *) + program_manifest)->size; + } + + return (struct ipu_fw_psys_program_manifest *)program_manifest; +} + +int ipu_fw_psys_get_program_manifest_by_process( + struct ipu_fw_generic_program_manifest *gen_pm, + const struct ipu_fw_psys_program_group_manifest *pg_manifest, + struct ipu_fw_psys_process *process) +{ + u32 process_id = process->ID; + int programs = pg_manifest->program_count; + int i; + + for (i = 0; i < programs; i++) { + u32 program_id; + struct ipu_fw_psys_program_manifest *pm = + ipu_resource_get_program_manifest(pg_manifest, i); + if (!pm) + continue; + program_id = pm->ID; + if (program_id == process_id) { + gen_pm->dev_chn_size = pm->dev_chn_size; + gen_pm->dev_chn_offset = NULL; + gen_pm->ext_mem_offset = NULL; + gen_pm->cell_id = pm->cell_id; + gen_pm->cell_type_id = pm->cell_type_id; + gen_pm->ext_mem_size = pm->ext_mem_size; + return 0; + } + } + return -ENOENT; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-isys-csi2.c b/drivers/media/pci/intel/ipu4/ipu4-isys-csi2.c new file mode 100644 index 000000000000..50eb7a9ab6e4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-isys-csi2.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2013 - 2018 Intel Corporation + +#include "ipu.h" +#include "ipu-buttress.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2.h" +#include "ipu-platform-isys-csi2-reg.h" +#include "ipu-platform-regs.h" +#include "ipu-trace.h" +#include "ipu-isys-csi2.h" + +#define CSE_IPC_CMDPHYWRITEL 35 +#define CSE_IPC_CMDPHYWRITEH 36 +#define CSE_IPC_CMDLEGACYPHYWRITEL 39 +#define CSE_IPC_CMDLEGACYPHYWRITEH 40 + +#define NBR_BULK_MSGS 30 /* Space reservation for IPC messages */ + +#define CSI2_UPDATE_TIME_TRY_NUM 3 +#define CSI2_UPDATE_TIME_MAX_DIFF 20 + +static u32 +build_cse_ipc_commands(struct ipu_ipc_buttress_bulk_msg *target, + u32 nbr_msgs, u32 opcodel, u32 reg, u32 data) +{ + struct ipu_ipc_buttress_bulk_msg *msgs = &target[nbr_msgs]; + u32 opcodeh = opcodel == CSE_IPC_CMDPHYWRITEL ? + CSE_IPC_CMDPHYWRITEH : CSE_IPC_CMDLEGACYPHYWRITEH; + + /* + * Writing of 32 bits consist of 2 16 bit IPC messages to CSE. + * Messages must be in low-high order and nothing else between + * them. + * Register is in bits 8..15 as index (register value divided by 4) + */ + msgs->cmd = opcodel | (reg << (8 - 2)) | ((data & 0xffff) << 16); + msgs->expected_resp = opcodel; + msgs->require_resp = true; + msgs->cmd_size = 4; + msgs++; + + msgs->cmd = opcodeh | (reg << (8 - 2)) | (data & 0xffff0000); + msgs->expected_resp = opcodeh; + msgs->require_resp = true; + msgs->cmd_size = 4; + + nbr_msgs += 2; + + /* Hits only if code change introduces too many new IPC messages */ + WARN_ON(nbr_msgs > NBR_BULK_MSGS); + + return nbr_msgs; +} + +static int csi2_ev_correction_params(struct ipu_isys_csi2 *csi2, + unsigned int lanes) +{ + struct ipu_device *isp = csi2->isys->adev->isp; + struct ipu_ipc_buttress_bulk_msg *messages; + const struct ipu_receiver_electrical_params *ev_params; + const struct ipu_isys_internal_csi2_pdata *csi2_pdata; + + __s64 link_freq; + unsigned int i; + u32 val; + u32 nbr_msgs = 0; + int rval; + bool conf_set0; + bool conf_set1; + bool conf_combined = false; + + csi2_pdata = &csi2->isys->pdata->ipdata->csi2; + ev_params = csi2_pdata->evparams; + if (!ev_params) + return 0; + + if (csi2->isys->csi2_cse_ipc_not_supported) + return 0; + + rval = ipu_isys_csi2_get_link_freq(csi2, &link_freq); + if (rval) + return rval; + + i = 0; + while (ev_params[i].device) { + if (ev_params[i].device == isp->pdev->device && + ev_params[i].revision == isp->pdev->revision && + ev_params[i].min_freq < link_freq && + ev_params[i].max_freq >= link_freq) + break; + i++; + } + + if (!ev_params[i].device) { + dev_info(&csi2->isys->adev->dev, + "No rcomp value override for this HW revision\n"); + return 0; + } + + messages = kcalloc(NBR_BULK_MSGS, sizeof(*messages), GFP_KERNEL); + if (!messages) + return -ENOMEM; + + conf_set0 = csi2_pdata->evsetmask0 & (1 << csi2->index); + conf_set1 = csi2_pdata->evsetmask1 & (1 << csi2->index); + if (csi2_pdata->evlanecombine[csi2->index]) { + conf_combined = + lanes > csi2_pdata->evlanecombine[csi2->index] ? 1 : 0; + } + conf_set1 |= conf_combined; + + /* + * Note: There is no way to make R-M-W to these. Possible non-zero reset + * default is OR'd with the values + */ + val = 1 << CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT1_SHIFT | + 1 << CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT2_SHIFT | + 1 << CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT3_SHIFT | + 1 << CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_PORT4_SHIFT | + 1 << CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_ENABLE_SHIFT | + ev_params[i].rcomp_val_legacy << + CSI2_SB_CSI_RCOMP_CONTROL_LEGACY_OVR_CODE_SHIFT; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDLEGACYPHYWRITEL, + CSI2_SB_CSI_RCOMP_CONTROL_LEGACY, + val); + + val = 2 << CSI2_SB_CSI_RCOMP_UPDATE_MODE_SHIFT | + 1 << CSI2_SB_CSI_RCOMP_OVR_ENABLE_SHIFT | + ev_params[i].rcomp_val_combo << CSI2_SB_CSI_RCOMP_OVR_CODE_SHIFT; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_CSI_RCOMP_CONTROL_COMBO, val); + + if (conf_set0) { + val = 0x380078 | ev_params[i].ports[0].ctle_val << + CSI2_SB_CPHY0_RX_CONTROL1_EQ_LANE0_SHIFT; + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_CPHY0_RX_CONTROL1, + val); + val = 0x10000; + if (ev_params[i].ports[0].crc_val != IPU_EV_AUTO) + val |= ev_params[i].ports[0].crc_val << + CSI2_SB_CPHY0_DLL_OVRD_CRCDC_FSM_DLANE0_SHIFT | + CSI2_SB_CPHY0_DLL_OVRD_LDEN_CRCDC_FSM_DLANE0; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_CPHY0_DLL_OVRD, val); + } + + if (conf_set1) { + val = 0x380078 | ev_params[i].ports[1].ctle_val << + CSI2_SB_CPHY2_RX_CONTROL1_EQ_LANE1_SHIFT; + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_CPHY2_RX_CONTROL1, + val); + + val = 0x10000; + if (ev_params[i].ports[1].crc_val != IPU_EV_AUTO) + val |= ev_params[i].ports[1].crc_val << + CSI2_SB_CPHY2_DLL_OVRD_CRCDC_FSM_DLANE1_SHIFT | + CSI2_SB_CPHY2_DLL_OVRD_LDEN_CRCDC_FSM_DLANE1; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_CPHY2_DLL_OVRD, val); + } + + mutex_lock(&csi2->isys->mutex); + /* This register is shared between two receivers */ + val = csi2->isys->csi2_rx_ctrl_cached; + if (conf_set0) { + val &= ~CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE01_MASK; + if (ev_params[i].ports[0].drc_val != IPU_EV_AUTO) + val |= + CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE01_MASK; + } + + if (conf_set1) { + val &= ~CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE23_MASK; + if (ev_params[i].ports[1].drc_val != IPU_EV_AUTO) + val |= + CSI2_SB_DPHY0_RX_CNTRL_SKEWCAL_CR_SEL_DLANE23_MASK; + } + csi2->isys->csi2_rx_ctrl_cached = val; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY0_RX_CNTRL, val); + mutex_unlock(&csi2->isys->mutex); + + if (conf_set0 && ev_params[i].ports[0].drc_val != IPU_EV_AUTO) { + /* Write value with FSM disabled */ + val = (conf_combined ? + ev_params[i].ports[0].drc_val_combined : + ev_params[i].ports[0].drc_val) << + CSI2_SB_DPHY0_DLL_OVRD_DRC_FSM_OVRD_SHIFT; + + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY0_DLL_OVRD, val); + + /* Write value with FSM enabled */ + val |= 1 << CSI2_SB_DPHY1_DLL_OVRD_LDEN_DRC_FSM_SHIFT; + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY0_DLL_OVRD, val); + } else if (conf_set0 && ev_params[i].ports[0].drc_val == IPU_EV_AUTO) { + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY0_DLL_OVRD, 0); + } + + if (conf_set1 && ev_params[i].ports[1].drc_val != IPU_EV_AUTO) { + val = (conf_combined ? + ev_params[i].ports[1].drc_val_combined : + ev_params[i].ports[1].drc_val) << + CSI2_SB_DPHY0_DLL_OVRD_DRC_FSM_OVRD_SHIFT; + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY1_DLL_OVRD, val); + + val |= 1 << CSI2_SB_DPHY1_DLL_OVRD_LDEN_DRC_FSM_SHIFT; + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY1_DLL_OVRD, val); + } else if (conf_set1 && ev_params[i].ports[1].drc_val == IPU_EV_AUTO) { + nbr_msgs = build_cse_ipc_commands(messages, nbr_msgs, + CSE_IPC_CMDPHYWRITEL, + CSI2_SB_DPHY1_DLL_OVRD, 0); + } + + rval = ipu_buttress_ipc_send_bulk(isp, + IPU_BUTTRESS_IPC_CSE, + messages, nbr_msgs); + + if (rval == -ENODEV) + csi2->isys->csi2_cse_ipc_not_supported = true; + + kfree(messages); + return 0; +} + +static void ipu_isys_register_errors(struct ipu_isys_csi2 *csi2) +{ + u32 status = readl(csi2->base + CSI2_REG_CSIRX_IRQ_STATUS); + + writel(status, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR); + csi2->receiver_errors |= status; +} + +void ipu_isys_csi2_error(struct ipu_isys_csi2 *csi2) +{ + /* + * Strings corresponding to CSI-2 receiver errors are here. + * Corresponding macros are defined in the header file. + */ + static const struct ipu_isys_csi2_error { + const char *error_string; + bool is_info_only; + } errors[] = { + {"Single packet header error corrected", true}, + {"Multiple packet header errors detected", true}, + {"Payload checksum (CRC) error", true}, + {"FIFO overflow", false}, + {"Reserved short packet data type detected", true}, + {"Reserved long packet data type detected", true}, + {"Incomplete long packet detected", false}, + {"Frame sync error", false}, + {"Line sync error", false}, + {"DPHY recoverable synchronization error", true}, + {"DPHY non-recoverable synchronization error", false}, + {"Escape mode error", true}, + {"Escape mode trigger event", true}, + {"Escape mode ultra-low power state for data lane(s)", true}, + {"Escape mode ultra-low power state exit for clock lane", true}, + {"Inter-frame short packet discarded", true}, + {"Inter-frame long packet discarded", true}, + }; + u32 status; + unsigned int i; + + /* Register errors once more in case of error interrupts are disabled */ + ipu_isys_register_errors(csi2); + status = csi2->receiver_errors; + csi2->receiver_errors = 0; + + for (i = 0; i < ARRAY_SIZE(errors); i++) { + if (!(status & BIT(i))) + continue; + + if (errors[i].is_info_only) + dev_dbg(&csi2->isys->adev->dev, + "csi2-%i info: %s\n", + csi2->index, errors[i].error_string); + else + dev_err_ratelimited(&csi2->isys->adev->dev, + "csi2-%i error: %s\n", + csi2->index, + errors[i].error_string); + } +} + +static u64 tunit_time_to_us(struct ipu_isys *isys, u64 time) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 1000000; + + do_div(time, isys_clk); + + return time; +} + +static int update_timer_base(struct ipu_isys *isys) +{ + int rval, i; + u64 time; + + for (i = 0; i < CSI2_UPDATE_TIME_TRY_NUM; i++) { + rval = ipu_trace_get_timer(&isys->adev->dev, &time); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read Tunit timer.\n"); + return rval; + } + rval = ipu_buttress_tsc_read(isys->adev->isp, + &isys->tsc_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read TSC timer.\n"); + return rval; + } + rval = ipu_trace_get_timer(&isys->adev->dev, + &isys->tunit_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read Tunit timer.\n"); + return rval; + } + if (tunit_time_to_us(isys, isys->tunit_timer_base - time) < + CSI2_UPDATE_TIME_MAX_DIFF) + return 0; + } + dev_dbg(&isys->adev->dev, "Timer base values may not be accurate.\n"); + return 0; +} + +static int +ipu_isys_csi2_configure_tunit(struct ipu_isys_csi2 *csi2, bool enable) +{ + struct ipu_isys *isys = csi2->isys; + void __iomem *isys_base = isys->pdata->base; + void __iomem *tunit_base = isys_base + TRACE_REG_IS_TRACE_UNIT_BASE; + int i, ret = 0; + + mutex_lock(&isys->short_packet_tracing_mutex); + if (!enable) { + isys->short_packet_tracing_count--; + if (isys->short_packet_tracing_count == 0) + writel(0, tunit_base + TRACE_REG_TUN_DDR_ENABLE); + goto out_release_mutex; + } + + isys->short_packet_tracing_count++; + if (isys->short_packet_tracing_count > 1) + goto out_release_mutex; + + memset(isys->short_packet_trace_buffer, 0, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE); + dma_sync_single_for_device(&isys->adev->dev, + isys->short_packet_trace_buffer_dma_addr, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + /* ring buffer base */ + writel(isys->short_packet_trace_buffer_dma_addr, + tunit_base + TRACE_REG_TUN_DRAM_BASE_ADDR); + + /* ring buffer end */ + writel(isys->short_packet_trace_buffer_dma_addr + + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE - + IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE, + tunit_base + TRACE_REG_TUN_DRAM_END_ADDR); + + /* Infobits for ddr trace */ + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + tunit_base + TRACE_REG_TUN_DDR_INFO_VAL); + + /* Remove reset from trace timers */ + writel(TRACE_REG_GPREG_TRACE_TIMER_RST_OFF, + isys_base + TRACE_REG_IS_GPREG_TRACE_TIMER_RST_N); + + /* Reset CSI2 monitors */ + writel(1, isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_RESET_REG_IDX); + writel(1, isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_TM_RESET_REG_IDX); + + /* Set trace address register. */ + writel(TRACE_REG_CSI2_TM_TRACE_ADDRESS_VAL, + isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_ADDRESS_REG_IDX); + writel(TRACE_REG_CSI2_TM_TRACE_HEADER_VAL, + isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_HEADER_REG_IDX); + writel(TRACE_REG_CSI2_3PH_TM_TRACE_ADDRESS_VAL, + isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_ADDRESS_REG_IDX); + writel(TRACE_REG_CSI2_TM_TRACE_HEADER_VAL, + isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_HEADER_REG_IDX); + + /* Enable DDR trace. */ + writel(1, tunit_base + TRACE_REG_TUN_DDR_ENABLE); + + /* Enable trace for CSI2 port. */ + for (i = 0; i < IPU_ISYS_MAX_CSI2_LEGACY_PORTS + + IPU_ISYS_MAX_CSI2_COMBO_PORTS; i++) { + void __iomem *event_mask_reg = + (i < IPU_ISYS_MAX_CSI2_LEGACY_PORTS) ? + isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_DDR_EN_REG_IDX_P(i) : + isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_3PH_TM_TRACE_DDR_EN_REG_IDX_P(i); + + writel(IPU_ISYS_SHORT_PACKET_TRACE_EVENT_MASK, + event_mask_reg); + } + + /* Enable CSI2 receiver monitor */ + writel(1, isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_OVERALL_ENABLE_REG_IDX); + writel(1, isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_TM_OVERALL_ENABLE_REG_IDX); + + ret = update_timer_base(isys); + +out_release_mutex: + mutex_unlock(&isys->short_packet_tracing_mutex); + + return ret; +} + +int ipu_isys_csi2_set_stream(struct v4l2_subdev *sd, + struct ipu_isys_csi2_timing timing, + unsigned int nlanes, int enable) +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe, + struct ipu_isys_pipeline, + pipe); + unsigned int i; + int rval; + u32 val, csi2part = 0, csi2csirx; + + dev_dbg(&csi2->isys->adev->dev, "csi2 s_stream %d\n", enable); + + if (!enable) { + ipu_isys_csi2_error(csi2); + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + val &= ~(CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING | + CSI2_CSI_RX_CONFIG_RELEASE_LP11); + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + writel(0, csi2->base + CSI2_REG_CSI_RX_ENABLE); + + /* Disable interrupts */ + writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_MASK); + writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_ENABLE); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE); + if (ip->interlaced) + ipu_isys_csi2_configure_tunit(csi2, 0); + return 0; + } + + csi2_ev_correction_params(csi2, nlanes); + + writel(timing.ctermen, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_CLANE); + writel(timing.csettle, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_CLANE); + + for (i = 0; i < nlanes; i++) { + writel(timing.dtermen, + csi2->base + + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_DLANE(i)); + writel(timing.dsettle, + csi2->base + + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_DLANE(i)); + } + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + val |= CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING | + CSI2_CSI_RX_CONFIG_RELEASE_LP11; + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + writel(nlanes, csi2->base + CSI2_REG_CSI_RX_NOF_ENABLED_LANES); + writel(CSI2_CSI_RX_ENABLE_ENABLE, + csi2->base + CSI2_REG_CSI_RX_ENABLE); + +#ifdef IPU_VC_SUPPORT + /* SOF/EOF of VC0-VC3 enabled from CSI2PART register in B0 */ + for (i = 0; i < NR_OF_CSI2_VC; i++) + csi2part |= CSI2_IRQ_FS_VC(i) | CSI2_IRQ_FE_VC(i); +#else + /* SOF/EOF enabled from CSI2PART register in B0 */ + csi2part |= CSI2_IRQ_FS_VC | CSI2_IRQ_FE_VC; +#endif + + /* Enable csi2 receiver error interrupts */ + csi2csirx = BIT(CSI2_CSIRX_NUM_ERRORS) - 1; + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_EDGE); + writel(0, csi2->base + CSI2_REG_CSIRX_IRQ_LEVEL_NOT_PULSE); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_MASK); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_ENABLE); + + /* Enable csi2 error and SOF-related irqs */ + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_EDGE); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_LEVEL_NOT_PULSE); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_CLEAR); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE); + if (ip->interlaced) { + writel(CSI2_RX_SYNC_COUNTER_EXTERNAL, + csi2->base + CSI2_REG_CSI_RX_SYNC_COUNTER_SEL); + rval = ipu_isys_csi2_configure_tunit(csi2, 1); + if (rval) + return rval; + } + + return 0; +} + +void ipu_isys_csi2_isr(struct ipu_isys_csi2 *csi2) +{ + u32 status = readl(csi2->base + CSI2_REG_CSI2PART_IRQ_STATUS); + unsigned int i; + + writel(status, csi2->base + CSI2_REG_CSI2PART_IRQ_CLEAR); + + if (status & CSI2_CSI2PART_IRQ_CSIRX) + ipu_isys_register_errors(csi2); + +#ifdef IPU_VC_SUPPORT + for (i = 0; i < NR_OF_CSI2_VC; i++) { + if ((status & CSI2_IRQ_FS_VC(i))) + ipu_isys_csi2_sof_event(csi2, i); + + if ((status & CSI2_IRQ_FE_VC(i))) + ipu_isys_csi2_eof_event(csi2, i); + } +#else + if (status & CSI2_IRQ_FS_VC) + ipu_isys_csi2_sof_event(csi2); + if (status & CSI2_IRQ_FE_VC) + ipu_isys_csi2_eof_event(csi2); +#endif +} + +static u64 tsc_time_to_tunit_time(struct ipu_isys *isys, + u64 tsc_base, u64 tunit_base, u64 tsc_time) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 100000; + u64 tsc_clk = IPU_BUTTRESS_TSC_CLK / 100000; + + tsc_time *= isys_clk; + tsc_base *= isys_clk; + do_div(tsc_time, tsc_clk); + do_div(tsc_base, tsc_clk); + + return tunit_base + tsc_time - tsc_base; +} + +/* Extract the timestamp from trace message. + * The timestamp in the traces message contains two parts. + * The lower part contains bit0 ~ 15 of the total 64bit timestamp. + * The higher part contains bit14 ~ 63 of the 64bit timestamp. + * These two parts are sampled at different time. + * Two overlaped bits are used to identify if there's roll overs + * in the lower part during the two samples. + * If the two overlapped bits do not match, a fix is needed to + * handle the roll over. + */ +static u64 +extract_time_from_short_packet_msg(struct ipu_isys_csi2_monitor_message *msg) +{ + u64 time_h = msg->timestamp_h << 14; + u64 time_l = msg->timestamp_l; + u64 time_h_ovl = time_h & 0xc000; + u64 time_h_h = time_h & (~0xffff); + + /* Fix possible roll overs. */ + if (time_h_ovl >= (time_l & 0xc000)) + return time_h_h | time_l; + else + return (time_h_h - 0x10000) | time_l; +} + +unsigned int +ipu_isys_csi2_get_current_field(struct ipu_isys_pipeline *ip, + unsigned int *timestamp) +{ + struct ipu_isys_video *av = container_of(ip, struct ipu_isys_video, ip); + struct ipu_isys *isys = av->isys; + unsigned int field = V4L2_FIELD_TOP; + + /* + * Find the nearest message that has matched msg type, + * port id, virtual channel and packet type. + */ + unsigned int i = ip->short_packet_trace_index; + bool msg_matched = false; + unsigned int monitor_id; + + update_timer_base(isys); + + if (ip->csi2->index >= IPU_ISYS_MAX_CSI2_LEGACY_PORTS) + monitor_id = TRACE_REG_CSI2_3PH_TM_MONITOR_ID; + else + monitor_id = TRACE_REG_CSI2_TM_MONITOR_ID; + + dma_sync_single_for_cpu(&isys->adev->dev, + isys->short_packet_trace_buffer_dma_addr, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + do { + struct ipu_isys_csi2_monitor_message msg = + isys->short_packet_trace_buffer[i]; + u64 sof_time = tsc_time_to_tunit_time(isys, + isys->tsc_timer_base, + isys->tunit_timer_base, + (((u64) timestamp[1]) << + 32) | timestamp[0]); + u64 trace_time = extract_time_from_short_packet_msg(&msg); + u64 delta_time_us = tunit_time_to_us(isys, + (sof_time > trace_time) ? + sof_time - trace_time : + trace_time - sof_time); + + i = (i + 1) % IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER; + + if (msg.cmd == TRACE_REG_CMD_TYPE_D64MTS && + msg.monitor_id == monitor_id && + msg.fs == 1 && + msg.port == ip->csi2->index && +#ifdef IPU_VC_SUPPORT + msg.vc == ip->vc && +#endif + delta_time_us < IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT) { + field = (msg.sequence % 2) ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + ip->short_packet_trace_index = i; + msg_matched = true; + dev_dbg(&isys->adev->dev, + "Interlaced field ready. field = %d\n", field); + break; + } + } while (i != ip->short_packet_trace_index); + if (!msg_matched) + /* We have walked through the whole buffer. */ + dev_dbg(&isys->adev->dev, "No matched trace message found.\n"); + + return field; +} + +bool ipu_isys_csi2_skew_cal_required(struct ipu_isys_csi2 *csi2) +{ + __s64 link_freq; + int rval; + + if (!csi2) + return false; + + /* Not yet ? */ + if (csi2->remote_streams != csi2->stream_count) + return false; + + rval = ipu_isys_csi2_get_link_freq(csi2, &link_freq); + if (rval) + return false; + + if (link_freq <= IPU_SKEW_CAL_LIMIT_HZ) + return false; + + return true; +} + +int ipu_isys_csi2_set_skew_cal(struct ipu_isys_csi2 *csi2, int enable) +{ + u32 val; + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + + if (enable) + val |= CSI2_CSI_RX_CONFIG_SKEWCAL_ENABLE; + else + val &= ~CSI2_CSI_RX_CONFIG_SKEWCAL_ENABLE; + + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-isys-isa.c b/drivers/media/pci/intel/ipu4/ipu4-isys-isa.c new file mode 100644 index 000000000000..b81e62c1479e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-isys-isa.c @@ -0,0 +1,1074 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-isys.h" +#include "ipu4-isys-isa.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" + +static const u32 isa_supported_codes_pad_sink[] = { + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +/* Regardless of the input mode ISA always produces 16 bit output */ +static const u32 isa_supported_codes_pad_source[] = { + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + 0, +}; + +/* ISA configuration */ +struct ipu_isys_pixelformat isa_config_pfmts[] = { + {V4L2_FMT_IPU_ISA_CFG, 8, 8, 0, MEDIA_BUS_FMT_FIXED, 0}, + {}, +}; + +static const u32 isa_supported_codes_pad_cfg[] = { + MEDIA_BUS_FMT_FIXED, + 0, +}; + +static const u32 isa_supported_codes_pad_3a[] = { + MEDIA_BUS_FMT_FIXED, + 0, +}; + +static const u32 isa_supported_codes_pad_source_scaled[] = { + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_YUYV12_1X24, + 0, +}; + +static const u32 *isa_supported_codes[] = { + isa_supported_codes_pad_sink, + isa_supported_codes_pad_source, + isa_supported_codes_pad_cfg, + isa_supported_codes_pad_3a, + isa_supported_codes_pad_source_scaled, +}; + +static struct v4l2_subdev_internal_ops isa_sd_internal_ops = { + .open = ipu_isys_subdev_open, + .close = ipu_isys_subdev_close, +}; + +static int isa_config_vidioc_g_fmt_vid_out_mplane(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct ipu_isys_video *av = video_drvdata(file); + + fmt->fmt.pix_mp = av->mpix; + + return 0; +} + +static const struct ipu_isys_pixelformat * +isa_config_try_fmt_vid_out_mplane(struct ipu_isys_video *av, + struct v4l2_pix_format_mplane *mpix) +{ + const struct ipu_isys_pixelformat *pfmt = + ipu_isys_get_pixelformat(av, mpix->pixelformat); + + if (!pfmt) + return NULL; + mpix->pixelformat = pfmt->pixelformat; + mpix->num_planes = ISA_CFG_BUF_PLANES; + + mpix->plane_fmt[ISA_CFG_BUF_PLANE_PG].bytesperline = 0; + mpix->plane_fmt[ISA_CFG_BUF_PLANE_PG].sizeimage = + ALIGN(max_t(u32, sizeof(struct ia_css_process_group_light), + mpix->plane_fmt[ISA_CFG_BUF_PLANE_PG].sizeimage), + av->isys->line_align); + + mpix->plane_fmt[ISA_CFG_BUF_PLANE_DATA].bytesperline = 0; + mpix->plane_fmt[ISA_CFG_BUF_PLANE_DATA].sizeimage = + ALIGN(max(1U, + mpix->plane_fmt[ISA_CFG_BUF_PLANE_DATA].sizeimage), + av->isys->line_align); + + return pfmt; +} + +static int isa_config_vidioc_s_fmt_vid_out_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + + if (av->aq.vbq.streaming) + return -EBUSY; + + av->pfmt = isa_config_try_fmt_vid_out_mplane(av, &f->fmt.pix_mp); + av->mpix = f->fmt.pix_mp; + + return 0; +} + +static int isa_config_vidioc_try_fmt_vid_out_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + + isa_config_try_fmt_vid_out_mplane(av, &f->fmt.pix_mp); + return 0; +} + +static const struct v4l2_ioctl_ops isa_config_ioctl_ops = { + .vidioc_querycap = ipu_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ipu_isys_vidioc_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = isa_config_vidioc_g_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = isa_config_vidioc_s_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = + isa_config_vidioc_try_fmt_vid_out_mplane, + .vidioc_g_fmt_vid_cap_mplane = isa_config_vidioc_g_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_cap_mplane = isa_config_vidioc_s_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_cap_mplane = + isa_config_vidioc_try_fmt_vid_out_mplane, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, +}; + +static const struct v4l2_subdev_core_ops isa_sd_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ipu_isys_isa *isa = to_ipu_isys_isa(sd); + unsigned int i; + + if (enable) + return 0; + + for (i = 0; i < ISA_CFG_BUF_PLANES; i++) + isa->next_param[i] = NULL; + + return 0; +} + +static const struct v4l2_subdev_video_ops isa_sd_video_ops = { + .s_stream = set_stream, +}; + +static const struct v4l2_subdev_pad_ops isa_sd_pad_ops = { + .link_validate = ipu_isys_subdev_link_validate, + .get_fmt = ipu_isys_subdev_get_ffmt, + .set_fmt = ipu_isys_subdev_set_ffmt, + .get_selection = ipu_isys_subdev_get_sel, + .set_selection = ipu_isys_subdev_set_sel, + .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, +}; + +static struct v4l2_subdev_ops isa_sd_ops = { + .core = &isa_sd_core_ops, + .video = &isa_sd_video_ops, + .pad = &isa_sd_pad_ops, +}; + +static int isa_link_validate(struct media_link *link) +{ + struct ipu_isys_pipeline *ip; + struct media_pipeline *pipe; + + /* Non-video node source */ + if (is_media_entity_v4l2_subdev(link->source->entity)) + return v4l2_subdev_link_validate(link); + + pipe = link->sink->entity->pipe; + ip = to_ipu_isys_pipeline(pipe); + ip->nr_queues++; + + return 0; +} + +static struct media_entity_operations isa_entity_ops = { + .link_validate = isa_link_validate, +}; + +void ipu_isys_isa_cleanup(struct ipu_isys_isa *isa) +{ + v4l2_device_unregister_subdev(&isa->asd.sd); + ipu_isys_subdev_cleanup(&isa->asd); + ipu_isys_video_cleanup(&isa->av_scaled); + ipu_isys_video_cleanup(&isa->av_config); + ipu_isys_video_cleanup(&isa->av_3a); + ipu_isys_video_cleanup(&isa->av); +} + +static void isa_set_ffmt(struct v4l2_subdev *sd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct v4l2_subdev_fh *cfg, +#else + struct v4l2_subdev_pad_config *cfg, +#endif + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *ffmt = +#ifdef IPU_VC_SUPPORT + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->stream, + fmt->which); +#else + __ipu_isys_get_ffmt(sd, cfg, fmt->pad, fmt->which); +#endif + enum ipu_isys_subdev_pixelorder order; + enum isys_subdev_prop_tgt tgt; + + switch (fmt->pad) { + case ISA_PAD_SINK: + fmt->format.field = V4L2_FIELD_NONE; + *ffmt = fmt->format; + tgt = IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT; + ipu_isys_subdev_fmt_propagate(sd, cfg, &fmt->format, + NULL, tgt, fmt->pad, fmt->which); + return; + case ISA_PAD_SOURCE: { + struct v4l2_mbus_framefmt *sink_ffmt = +#ifdef IPU_VC_SUPPORT + __ipu_isys_get_ffmt(sd, cfg, ISA_PAD_SINK, + fmt->stream, fmt->which); +#else + __ipu_isys_get_ffmt(sd, cfg, ISA_PAD_SINK, fmt->which); +#endif + struct v4l2_rect *r = + __ipu_isys_get_selection(sd, cfg, + V4L2_SEL_TGT_CROP, + ISA_PAD_SOURCE, + fmt->which); + + ffmt->width = r->width; + ffmt->height = r->height; + ffmt->field = sink_ffmt->field; + order = ipu_isys_subdev_get_pixelorder(sink_ffmt->code); + ffmt->code = isa_supported_codes_pad_source[order]; + return; + } + case ISA_PAD_CONFIG: + case ISA_PAD_3A: + ffmt->code = MEDIA_BUS_FMT_FIXED; + ffmt->width = 0; + ffmt->height = 0; + fmt->format = *ffmt; + return; + case ISA_PAD_SOURCE_SCALED: { + struct v4l2_mbus_framefmt *sink_ffmt = +#ifdef IPU_VC_SUPPORT + __ipu_isys_get_ffmt(sd, cfg, ISA_PAD_SINK, + fmt->stream, fmt->which); +#else + __ipu_isys_get_ffmt(sd, cfg, ISA_PAD_SINK, fmt->which); +#endif + struct v4l2_rect *r = + __ipu_isys_get_selection(sd, cfg, + V4L2_SEL_TGT_CROP, + ISA_PAD_SOURCE_SCALED, + fmt->which); + + ffmt->width = r->width; + ffmt->height = r->height; + ffmt->field = sink_ffmt->field; + order = ipu_isys_subdev_get_pixelorder(sink_ffmt->code); + ffmt->code = + isa_supported_codes_pad_source_scaled[order]; + if (fmt->format.code == MEDIA_BUS_FMT_YUYV12_1X24) + ffmt->code = MEDIA_BUS_FMT_YUYV12_1X24; + + return; + } + default: + WARN_ON(1); + } +} + +static int isa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + return 0; +} + +static const struct v4l2_ctrl_ops isa_ctrl_ops = { + .s_ctrl = isa_s_ctrl, +}; + +static void isa_capture_done(struct ipu_isys_pipeline *ip, + struct ipu_fw_isys_resp_info_abi *info) +{ + struct ipu_isys_isa *isa = &ip->isys->isa; + struct ipu_isys_queue *aq = &isa->av_config.aq; + struct ipu_isys_buffer *ib; + unsigned long flags; + + if (WARN_ON_ONCE(list_empty(&aq->active))) + return; + + spin_lock_irqsave(&aq->lock, flags); + ib = list_last_entry(&aq->active, struct ipu_isys_buffer, head); + list_del(&ib->head); + dev_dbg(&ip->isys->adev->dev, "isa cfg: dequeued buffer %p", ib); + spin_unlock_irqrestore(&aq->lock, flags); + + ipu_isys_buf_calc_sequence_time(ib, info); + ipu_isys_queue_buf_done(ib); + + aq = &isa->av_3a.aq; + + if (isa->av_3a.vdev.entity.pipe != isa->av_config.vdev.entity.pipe) { + dev_dbg(&ip->isys->adev->dev, "3a disabled\n"); + return; + } + + if (WARN_ON_ONCE(list_empty(&aq->active))) + return; + + spin_lock_irqsave(&aq->lock, flags); + ib = list_last_entry(&aq->active, struct ipu_isys_buffer, head); + list_del(&ib->head); + dev_dbg(&ip->isys->adev->dev, "isa 3a: dequeued buffer %p", ib); + spin_unlock_irqrestore(&aq->lock, flags); + + ipu_isys_buf_calc_sequence_time(ib, info); + ipu_isys_queue_buf_done(ib); +} + +/* Maximum size of the buffer-specific process group. */ +#define PGL_SIZE PAGE_SIZE + +static int isa_3a_buf_init(struct vb2_buffer *vb) +{ + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); + + isa_buf->pgl.pg = kzalloc(PGL_SIZE, GFP_KERNEL); + if (!isa_buf->pgl.pg) + return -ENOMEM; + + return 0; +} + +static void isa_3a_buf_cleanup(struct vb2_buffer *vb) +{ + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); + + kfree(isa_buf->pgl.pg); +} + +static int isa_config_buf_init(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + int rval; + + rval = isa_3a_buf_init(vb); + if (rval) + return rval; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + + isa_buf->pgl.common_pg = + dma_alloc_attrs(&av->isys->adev->dev, PGL_SIZE << 1, + &isa_buf->pgl.iova, GFP_KERNEL | __GFP_ZERO, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &attrs +#else + attrs +#endif + ); + + dev_dbg(&av->isys->adev->dev, + "buf_init: index %u, cpu addr %p, dma addr %pad\n", +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index, +#else + vb->index, +#endif + isa_buf->pgl.common_pg, &isa_buf->pgl.iova); + + if (!isa_buf->pgl.common_pg) { + isa_3a_buf_cleanup(vb); + return -ENOMEM; + } + + return 0; +} + +static void isa_config_buf_cleanup(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + + dev_dbg(&av->isys->adev->dev, + "buf_cleanup: index %u, cpu addr %p, dma addr %pad\n", +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_buf.index, +#else + vb->index, +#endif + isa_buf->pgl.pg, &isa_buf->pgl.iova); + if (!isa_buf->pgl.pg) + return; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + + dma_free_attrs(&av->isys->adev->dev, PGL_SIZE << 1, + isa_buf->pgl.common_pg, isa_buf->pgl.iova, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + &attrs +#else + attrs +#endif + ); + + isa_3a_buf_cleanup(vb); +} + +static void +isa_prepare_firmware_stream_cfg(struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi *cfg) +{ + struct v4l2_rect *r; + unsigned int pad, cropping_location, res_info; + + if (av == &av->isys->isa.av) { + pad = ISA_PAD_SOURCE; + cropping_location = + IPU_FW_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED; + res_info = IPU_FW_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED; + } else if (av == &av->isys->isa.av_scaled) { + pad = ISA_PAD_SOURCE_SCALED; + cropping_location = + IPU_FW_ISYS_CROPPING_LOCATION_POST_ISA_SCALED; + res_info = IPU_FW_ISYS_RESOLUTION_INFO_POST_ISA_SCALED; + } else { + WARN_ON(1); + return; + } + + r = __ipu_isys_get_selection(&av->isys->isa.asd.sd, NULL, + V4L2_SEL_TGT_CROP, pad, + V4L2_SUBDEV_FORMAT_ACTIVE); + + cfg->crop[cropping_location].top_offset = r->top; + cfg->crop[cropping_location].left_offset = r->left; + cfg->crop[cropping_location].bottom_offset = r->top + r->height; + cfg->crop[cropping_location].right_offset = r->left + r->width; + + r = __ipu_isys_get_selection(&av->isys->isa.asd.sd, NULL, + V4L2_SEL_TGT_COMPOSE, pad, + V4L2_SUBDEV_FORMAT_ACTIVE); + + cfg->isa_cfg.isa_res[res_info].height = r->height; + cfg->isa_cfg.isa_res[res_info].width = r->width; + ipu_isys_prepare_firmware_stream_cfg_default(av, cfg); +} + +static void +isa_prepare_firmware_stream_cfg_param(struct ipu_isys_video *av, + struct ipu_fw_isys_stream_cfg_data_abi + *cfg) +{ + struct ipu_isys_isa *isa = &av->isys->isa; + struct ipu_isys_pipeline *ip = + to_ipu_isys_pipeline(av->vdev.entity.pipe); + + cfg->isa_cfg.cfg.blc = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_BLC); + cfg->isa_cfg.cfg.lsc = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_LSC); + cfg->isa_cfg.cfg.dpc = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_DPC); + cfg->isa_cfg.cfg.downscaler = + !!(isa->isa_en->val & V4L2_IPU_ISA_EN_SCALER); + cfg->isa_cfg.cfg.awb = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_AWB); + cfg->isa_cfg.cfg.af = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_AF); + cfg->isa_cfg.cfg.ae = !!(isa->isa_en->val & V4L2_IPU_ISA_EN_AE); + + cfg->isa_cfg.cfg.send_irq_stats_ready = 1; + cfg->isa_cfg.cfg.send_resp_stats_ready = 1; + ipu_isys_video_add_capture_done(ip, isa_capture_done); +} + +static bool is_capture_terminal(struct ia_css_terminal *t) +{ + switch (t->terminal_type) { + case IPU_FW_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IPU_FW_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IPU_FW_TERMINAL_TYPE_PARAM_SLICED_OUT: + return true; + default: + return false; + } +} + +/* Return the pointer to the terminal payload's IOVA. */ +static int isa_terminal_get_iova(struct device *dev, struct ia_css_terminal *t, + u32 **iova) +{ + switch (t->terminal_type) { + case IPU_FW_TERMINAL_TYPE_PARAM_CACHED_IN: + case IPU_FW_TERMINAL_TYPE_PARAM_CACHED_OUT:{ + struct ia_css_param_terminal *tpterm = (void *)t; + + *iova = &tpterm->param_payload.buffer; + break; + } + case IPU_FW_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IPU_FW_TERMINAL_TYPE_PARAM_SPATIAL_OUT:{ + struct ia_css_spatial_param_terminal *tpterm = + (void *)t; + + *iova = &tpterm->param_payload.buffer; + break; + } + case IPU_FW_TERMINAL_TYPE_PARAM_SLICED_IN: + case IPU_FW_TERMINAL_TYPE_PARAM_SLICED_OUT:{ + struct ia_css_sliced_param_terminal *tpterm = (void *)t; + + *iova = &tpterm->param_payload.buffer; + break; + } + case IPU_FW_TERMINAL_TYPE_PROGRAM:{ + struct ia_css_program_terminal *tpterm = (void *)t; + + *iova = &tpterm->param_payload.buffer; + break; + } + default: + dev_dbg(dev, "unhandled terminal type %u\n", t->terminal_type); + return -EINVAL; + } + + return 0; +} + +/* + * Validate a process group, and add the IOVA of the data plane to the + * offsets related to the start of the data plane. + */ +static int isa_import_pg(struct vb2_buffer *vb) +{ + void *__pg = vb2_plane_vaddr(vb, ISA_CFG_BUF_PLANE_PG); + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); + struct ia_css_process_group_light *pg = isa_buf->pgl.pg; + bool capture = aq->vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + u32 addr = vb2_dma_contig_plane_dma_addr(vb, + ISA_CFG_BUF_PLANE_DATA); + unsigned int i; + + if (!__pg) { + dev_warn(&av->isys->adev->dev, + "virtual mapping of the buffer failed\n"); + return -EINVAL; + } + + if (vb2_plane_size(vb, ISA_CFG_BUF_PLANE_PG) > PGL_SIZE) { + dev_dbg(&av->isys->adev->dev, + "too large process group, max %lu\n", PGL_SIZE); + return -EINVAL; + } + + /* + * Copy the light process group to a kernel buffer so that it + * cannot be modified by the user space. + */ + memcpy(pg, __pg, vb2_plane_size(vb, ISA_CFG_BUF_PLANE_PG)); + + if (pg->size > vb2_plane_size(vb, ISA_CFG_BUF_PLANE_PG)) { + dev_dbg(&av->isys->adev->dev, + "process group size too large (%u bytes, %lu bytes available)\n", + pg->size, vb2_plane_size(vb, ISA_CFG_BUF_PLANE_PG)); + return -EINVAL; + } + + if (!pg->terminal_count) { + dev_dbg(&av->isys->adev->dev, "no terminals defined\n"); + return -EINVAL; + } + + if ((void *)(ia_css_terminal_offsets(pg) + + pg->terminal_count * sizeof(uint16_t)) - (void *)pg + > pg->size) { + dev_dbg(&av->isys->adev->dev, + "terminal offsets do not fit in the buffer\n"); + return -EINVAL; + } + + for (i = 0; i < pg->terminal_count; i++) { + struct ia_css_terminal *t = to_ia_css_terminal(pg, i); + u32 *iova; + int rval; + + if ((void *)t + sizeof(*t) - (void *)pg > pg->size) { + dev_dbg(&av->isys->adev->dev, + "terminal %u does not fit in the buffer\n", i); + return -EINVAL; + } + + dev_dbg(&av->isys->adev->dev, + "terminal: terminal %u, size %u, capture %u / %u\n", + i, t->size, capture, is_capture_terminal(t)); + + if (capture != is_capture_terminal(t)) + continue; + + dev_dbg(&av->isys->adev->dev, "terminal: %u offset %u\n", i, + ia_css_terminal_offsets(pg)[i]); + + rval = isa_terminal_get_iova(&av->isys->adev->dev, t, &iova); + if (rval) + return rval; + + dev_dbg(&av->isys->adev->dev, + "terminal: offset 0x%x, address 0x%8.8x\n", + *iova, (u32) addr + *iova); + + if (addr + *iova < addr) { + dev_dbg(&av->isys->adev->dev, + "address space overflow\n"); + return -EINVAL; + } + + if (*iova > vb2_plane_size(vb, ISA_CFG_BUF_PLANE_DATA)) { + dev_dbg(&av->isys->adev->dev, + "offset outside the buffer\n"); + return -EINVAL; + } + + /* + * Add the IOVA of the data plane to the terminal + * payload's offset. + */ + *iova += addr; + } + + return 0; +} + +static int isa_terminal_buf_prepare(struct vb2_buffer *vb) +{ + struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + unsigned int i; + + for (i = 0; i < ISA_CFG_BUF_PLANES; i++) { + vb2_set_plane_payload(vb, i, av->mpix.plane_fmt[i].sizeimage); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + vb->v4l2_planes[i].data_offset = 0; +#else + vb->planes[i].data_offset = 0; +#endif + } + + return isa_import_pg(vb); +} + +/* + * Count relevant terminals in a light process group and add the + * number of found to the common light process group. + */ +static void +isa_config_count_valid_terminals(struct device *dev, + struct ia_css_process_group_light *cpg, + struct ia_css_process_group_light *pg, + bool capture) +{ + unsigned int i; + + for (i = 0; i < pg->terminal_count; i++) + if (capture == is_capture_terminal(to_ia_css_terminal(pg, i))) + cpg->terminal_count++; +} + +static void +isa_config_prepare_frame_buff_set_one(struct device *dev, + struct ia_css_process_group_light *cpg, + struct ia_css_process_group_light *pg, + dma_addr_t addr, bool capture, + unsigned int *terminal_count) +{ + unsigned int i; + + dev_dbg(dev, "terminal: size %u, count %u, offset %u\n", + pg->size, pg->terminal_count, pg->terminals_offset_offset); + + dev_dbg(dev, "terminal: copying %u terminal offsets to %p from %p\n", + pg->terminal_count, ia_css_terminal_offsets(cpg), + ia_css_terminal_offsets(pg)); + + for (i = 0; i < pg->terminal_count; i++) { + struct ia_css_terminal *t = to_ia_css_terminal(pg, i), *ct; + + dev_dbg(dev, + "terminal: parsing %u, size %u, capture %u / %u\n", + i, t->size, capture, is_capture_terminal(t)); + + if (capture != is_capture_terminal(t)) + continue; + + ia_css_terminal_offsets(cpg)[*terminal_count] = + ia_css_terminal_offset(cpg, *terminal_count); + + dev_dbg(dev, "terminal: %u offset %u\n", *terminal_count, + ia_css_terminal_offsets(cpg)[*terminal_count]); + + ct = to_ia_css_terminal(cpg, *terminal_count); + + dev_dbg(dev, + "terminal: copying terminal %p to %p (%u bytes)\n", + t, ct, t->size); + memcpy(ct, t, t->size); + + (*terminal_count)++; + } +} + +/* + * Move the terminals from a read-only or write-only light process + * group to a common process group. + */ +static void isa_config_prepare_frame_buff_set(struct vb2_buffer *__vb) +{ + struct ipu_isys_queue *aq = + vb2_queue_to_ipu_isys_queue(__vb->vb2_queue); + struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); + struct ipu_isys_isa *isa = &av->isys->isa; + struct vb2_buffer *vb[ISA_PARAM_QUEUES]; + struct ia_css_process_group_light *pg[ISA_PARAM_QUEUES]; + dma_addr_t addr[ISA_PARAM_QUEUES]; + struct ia_css_process_group_light *cpg; + struct ipu_isys_isa_buffer *__isa_buf; + unsigned int terminal_count = 0, i; + bool capture = &av->isys->isa.av_3a.aq == aq; + + dev_dbg(&av->isys->adev->dev, "%s: capture %u\n", av->vdev.name, + capture); + + isa->next_param[capture] = __vb; + + /* Proceed only when both cfg and stats buffers are available. */ + if (!isa->next_param[!capture]) + return; + + /* Obtain common process group light buffer from config buffer */ + __isa_buf = vb2_buffer_to_ipu_isys_isa_buffer( + isa->next_param[ISA_CFG_BUF_PLANE_PG]); + + for (i = 0; i < ISA_PARAM_QUEUES; i++) { + struct ipu_isys_isa_buffer *isa_buf; + + vb[i] = isa->next_param[i]; + isa_buf = vb2_buffer_to_ipu_isys_isa_buffer(vb[i]); + pg[i] = isa_buf->pgl.pg; + addr[i] = vb2_dma_contig_plane_dma_addr(vb[i], + ISA_CFG_BUF_PLANE_DATA); + + dma_sync_single_for_device(&av->isys->adev->dev, + addr[i], vb2_plane_size(vb[i], + ISA_CFG_BUF_PLANE_DATA), + DMA_TO_DEVICE); + + dev_dbg(&av->isys->adev->dev, + "terminal: queue %u, plane 0: vaddr %p, dma_addr %pad program group size %u program group terminals %u\n", + i, pg[i], &addr[i], pg[i]->size, pg[i]->terminal_count); + } + + cpg = __isa_buf->pgl.common_pg; + cpg->terminal_count = 0; + cpg->terminals_offset_offset = sizeof(*cpg); + + if (cpg->size > PGL_SIZE << 1) { + dev_err(&av->isys->adev->dev, + "not enough room for terms, %lu found, %u needed\n", + PGL_SIZE << 1, cpg->size); + return; + } + + for (i = 0; i < ISA_PARAM_QUEUES; i++) + isa_config_count_valid_terminals(&av->isys->adev->dev, + cpg, pg[i], i); + + for (i = 0; i < ISA_PARAM_QUEUES; i++) { + isa_config_prepare_frame_buff_set_one(&av->isys->adev->dev, cpg, + pg[i], addr[i], i, + &terminal_count); + + isa->next_param[i] = NULL; + } + + cpg->size = ia_css_terminal_offset(cpg, cpg->terminal_count); + + dev_dbg(&av->isys->adev->dev, "common pg size 0x%x count %d\n", + cpg->size, cpg->terminal_count); + + dma_sync_single_for_device(&av->isys->adev->dev, __isa_buf->pgl.iova, + PGL_SIZE << 1, DMA_TO_DEVICE); +} + +static void +isa_config_fill_frame_buff_set_pin(struct vb2_buffer *vb, + struct ipu_fw_isys_frame_buff_set_abi *set) +{ + struct ipu_isys_isa_buffer *isa_buf = + vb2_buffer_to_ipu_isys_isa_buffer(vb); + + set->process_group_light.addr = isa_buf->pgl.iova; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + set->process_group_light.param_buf_id = vb->v4l2_buf.index + 1; +#else + set->process_group_light.param_buf_id = vb->index + 1; +#endif +} + +static void isa_ctrl_init(struct v4l2_subdev *sd) +{ + struct ipu_isys_isa *isa = to_ipu_isys_isa(sd); + static const struct v4l2_ctrl_config cfg = { + .ops = &isa_ctrl_ops, + .id = V4L2_CID_IPU_ISA_EN, + .name = "ISA enable", + .type = V4L2_CTRL_TYPE_BITMASK, + .max = V4L2_IPU_ISA_EN_BLC + | V4L2_IPU_ISA_EN_LSC + | V4L2_IPU_ISA_EN_DPC + | V4L2_IPU_ISA_EN_SCALER + | V4L2_IPU_ISA_EN_AWB + | V4L2_IPU_ISA_EN_AF | V4L2_IPU_ISA_EN_AE, + }; + + isa->isa_en = v4l2_ctrl_new_custom(&isa->asd.ctrl_handler, &cfg, NULL); +} + +int ipu_isys_isa_init(struct ipu_isys_isa *isa, + struct ipu_isys *isys, void __iomem *base) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = ISA_PAD_SINK, + .format = { + .width = 4096, + .height = 3072, + }, + }; + struct v4l2_subdev_format fmt_config = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = ISA_PAD_CONFIG, + }; + struct v4l2_subdev_format fmt_3a = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = ISA_PAD_3A, + }; + int rval; + + isa->base = base; + + isa->asd.sd.entity.ops = &isa_entity_ops; + isa->asd.ctrl_init = isa_ctrl_init; + isa->asd.isys = isys; + + rval = ipu_isys_subdev_init(&isa->asd, &isa_sd_ops, 1, + NR_OF_ISA_PADS, +#ifdef IPU_VC_SUPPORT + NR_OF_ISA_STREAMS, +#endif + NR_OF_ISA_SOURCE_PADS, + NR_OF_ISA_SINK_PADS, + V4L2_SUBDEV_FL_HAS_EVENTS); + if (rval) + goto fail; + + isa->asd.pad[ISA_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; + isa->asd.pad[ISA_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + isa->asd.valid_tgts[ISA_PAD_SOURCE].crop = true; + isa->asd.pad[ISA_PAD_CONFIG].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; + isa->asd.pad[ISA_PAD_3A].flags = MEDIA_PAD_FL_SOURCE; + isa->asd.pad[ISA_PAD_SOURCE_SCALED].flags = MEDIA_PAD_FL_SOURCE; + isa->asd.valid_tgts[ISA_PAD_SOURCE_SCALED].compose = true; + isa->asd.valid_tgts[ISA_PAD_SOURCE_SCALED].crop = true; + + isa->asd.isl_mode = IPU_ISL_ISA; + isa->asd.supported_codes = isa_supported_codes; + isa->asd.set_ffmt = isa_set_ffmt; + ipu_isys_subdev_set_ffmt(&isa->asd.sd, NULL, &fmt); + ipu_isys_subdev_set_ffmt(&isa->asd.sd, NULL, &fmt_config); + ipu_isys_subdev_set_ffmt(&isa->asd.sd, NULL, &fmt_3a); + + isa->asd.sd.internal_ops = &isa_sd_internal_ops; + snprintf(isa->asd.sd.name, sizeof(isa->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " ISA"); + v4l2_set_subdevdata(&isa->asd.sd, &isa->asd); + rval = v4l2_device_register_subdev(&isys->v4l2_dev, &isa->asd.sd); + if (rval) { + dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); + goto fail; + } + + snprintf(isa->av.vdev.name, sizeof(isa->av.vdev.name), + IPU_ISYS_ENTITY_PREFIX " ISA capture"); + isa->av.isys = isys; + isa->av.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_RAW_NS; + isa->av.pfmts = ipu_isys_pfmts; + isa->av.try_fmt_vid_mplane = ipu_isys_video_try_fmt_vid_mplane_default; + isa->av.prepare_firmware_stream_cfg = isa_prepare_firmware_stream_cfg; + isa->av.aq.buf_prepare = ipu_isys_buf_prepare; + isa->av.aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + isa->av.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + isa->av.aq.vbq.buf_struct_size = sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&isa->av, &isa->asd.sd.entity, + ISA_PAD_SOURCE, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + snprintf(isa->av_config.vdev.name, sizeof(isa->av_config.vdev.name), + IPU_ISYS_ENTITY_PREFIX " ISA config"); + isa->av_config.isys = isys; + isa->av_config.pfmts = isa_config_pfmts; + isa->av_config.try_fmt_vid_mplane = isa_config_try_fmt_vid_out_mplane; + isa->av_config.prepare_firmware_stream_cfg = + isa_prepare_firmware_stream_cfg_param; + isa->av_config.vdev.ioctl_ops = &isa_config_ioctl_ops; + isa->av_config.aq.buf_init = isa_config_buf_init; + isa->av_config.aq.buf_cleanup = isa_config_buf_cleanup; + isa->av_config.aq.buf_prepare = isa_terminal_buf_prepare; + isa->av_config.aq.prepare_frame_buff_set = + isa_config_prepare_frame_buff_set; + isa->av_config.aq.fill_frame_buff_set_pin = + isa_config_fill_frame_buff_set_pin; + isa->av_config.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + isa->av_config.aq.vbq.io_modes = VB2_MMAP | VB2_DMABUF; + isa->av_config.aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_isa_buffer); + + rval = ipu_isys_video_init(&isa->av_config, &isa->asd.sd.entity, + ISA_PAD_CONFIG, MEDIA_PAD_FL_SOURCE, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + snprintf(isa->av_3a.vdev.name, sizeof(isa->av_3a.vdev.name), + IPU_ISYS_ENTITY_PREFIX " ISA 3A stats"); + isa->av_3a.isys = isys; + isa->av_3a.pfmts = isa_config_pfmts; + isa->av_3a.try_fmt_vid_mplane = isa_config_try_fmt_vid_out_mplane; + isa->av_3a.prepare_firmware_stream_cfg = + isa_prepare_firmware_stream_cfg_param; + isa->av_3a.vdev.ioctl_ops = &isa_config_ioctl_ops; + isa->av_3a.aq.buf_init = isa_3a_buf_init; + isa->av_3a.aq.buf_cleanup = isa_3a_buf_cleanup; + isa->av_3a.aq.buf_prepare = isa_terminal_buf_prepare; + isa->av_3a.aq.prepare_frame_buff_set = + isa_config_prepare_frame_buff_set; + isa->av_3a.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + isa->av_3a.aq.vbq.io_modes = VB2_MMAP | VB2_DMABUF; + isa->av_3a.aq.vbq.buf_struct_size = sizeof(struct ipu_isys_isa_buffer); + isa->av_3a.line_header_length = 4; /* Set to non-zero to force mplane*/ + + rval = ipu_isys_video_init(&isa->av_3a, &isa->asd.sd.entity, + ISA_PAD_3A, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + snprintf(isa->av_scaled.vdev.name, sizeof(isa->av_scaled.vdev.name), + IPU_ISYS_ENTITY_PREFIX " ISA scaled capture"); + isa->av_scaled.isys = isys; + isa->av_scaled.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_RAW_S; + isa->av_scaled.pfmts = isa->av.pfmts; + isa->av_scaled.try_fmt_vid_mplane = + ipu_isys_video_try_fmt_vid_mplane_default; + isa->av_scaled.prepare_firmware_stream_cfg = + isa_prepare_firmware_stream_cfg; + isa->av_scaled.aq.buf_prepare = ipu_isys_buf_prepare; + isa->av_scaled.aq.fill_frame_buff_set_pin = + ipu_isys_buffer_list_to_ipu_fw_isys_frame_buff_set_pin; + isa->av_scaled.aq.link_fmt_validate = ipu_isys_link_fmt_validate; + isa->av_scaled.aq.vbq.buf_struct_size = + sizeof(struct ipu_isys_video_buffer); + + rval = ipu_isys_video_init(&isa->av_scaled, &isa->asd.sd.entity, + ISA_PAD_SOURCE_SCALED, MEDIA_PAD_FL_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, "can't init video node\n"); + goto fail; + } + + return 0; + +fail: + ipu_isys_isa_cleanup(isa); + + return rval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-isys-isa.h b/drivers/media/pci/intel/ipu4/ipu4-isys-isa.h new file mode 100644 index 000000000000..649714dca2f4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-isys-isa.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_ISA_H +#define IPU_ISYS_ISA_H + +#include +#include + +#include "ipu-isys-queue.h" +#include "ipu-isys-subdev.h" +#include "ipu-isys-video.h" + +#define ISA_PAD_SINK 0 +#define ISA_PAD_SOURCE 1 +#define ISA_PAD_CONFIG 2 +#define ISA_PAD_3A 3 +#define ISA_PAD_SOURCE_SCALED 4 + +#define NR_OF_ISA_PADS 5 +#define NR_OF_ISA_SINK_PADS 2 +#define NR_OF_ISA_SOURCE_PADS 3 +#define NR_OF_ISA_STREAMS 1 + +struct ipu_isys; +struct ia_css_process_group_light; + +/* + * struct ipu_isa_buffer + * + * @ivb: Base buffer type which provides inheritance of + * isys buffer and vb2 buffer. + * @pgl: program group light DMA buffer + * @pgl.pg: process group, copy of the buffer's plane 0 + * but not mapped to user space + * @pgl.common_pg: A combined process group from both video buffers + * @pgl.iova: IOVA of common_pg + */ +struct ipu_isys_isa_buffer { + struct ipu_isys_video_buffer ivb; + struct { + struct ia_css_process_group_light *pg; + struct ia_css_process_group_light *common_pg; + dma_addr_t iova; + } pgl; +}; + +/* ISA CFG will use multiplanar buffers */ +#define ISA_CFG_BUF_PLANE_PG 0 +#define ISA_CFG_BUF_PLANE_DATA 1 +#define ISA_CFG_BUF_PLANES 2 + +#define ISA_PARAM_QUEUES 2 + +/* + * struct ipu_isys_isa + */ +struct ipu_isys_isa { + struct ipu_isys_subdev asd; + struct ipu_isys_video av; + struct ipu_isys_video av_config; + struct ipu_isys_video av_3a; + struct ipu_isys_video av_scaled; + + void __iomem *base; + + struct v4l2_ctrl *isa_en; + + struct vb2_buffer *next_param[ISA_PARAM_QUEUES]; /* config and 3a */ +}; + +#define to_ipu_isys_isa(sd) \ + container_of(to_ipu_isys_subdev(sd), \ + struct ipu_isys_isa, asd) + +#define vb2_buffer_to_ipu_isys_isa_buffer(__vb) \ + container_of(vb2_buffer_to_ipu_isys_video_buffer(__vb), \ + struct ipu_isys_isa_buffer, ivb) + +int ipu_isys_isa_init(struct ipu_isys_isa *isa, + struct ipu_isys *isys, void __iomem *base); +void ipu_isys_isa_cleanup(struct ipu_isys_isa *isa); +void ipu_isys_isa_isr(struct ipu_isys_isa *isa); + +#endif /* IPU_ISYS_ISA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4-isys.c b/drivers/media/pci/intel/ipu4/ipu4-isys.c new file mode 100644 index 000000000000..27bfe78aa020 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-isys.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include + +#include "ipu.h" +#include "ipu-platform-regs.h" +#include "ipu-platform-buttress-regs.h" +#include "ipu-platform-isys-csi2-reg.h" +#include "ipu-trace.h" +#include "ipu-isys.h" +#include "ipu-isys-video.h" +#include "ipu-isys-tpg.h" + +#ifndef V4L2_PIX_FMT_SBGGR14V32 +/* + * Non-vectorized 14bit definitions have been upstreamed. + * To keep various versions of the ipu4 builds compileable use local + * definitions when global one's doesn't exists. + */ +#define V4L2_PIX_FMT_SBGGR14V32 v4l2_fourcc('b', 'V', '0', 'M') +#define V4L2_PIX_FMT_SGBRG14V32 v4l2_fourcc('b', 'V', '0', 'N') +#define V4L2_PIX_FMT_SGRBG14V32 v4l2_fourcc('b', 'V', '0', 'O') +#define V4L2_PIX_FMT_SRGGB14V32 v4l2_fourcc('b', 'V', '0', 'P') +#endif + +const struct ipu_isys_pixelformat ipu_isys_pfmts[] = { + /* YUV vector format */ + {V4L2_PIX_FMT_YUYV420_V32, 24, 24, 0, MEDIA_BUS_FMT_YUYV12_1X24, + IPU_FW_ISYS_FRAME_FORMAT_YUV420_16}, + /* Raw bayer vector formats. */ + {V4L2_PIX_FMT_SBGGR14V32, 16, 14, 0, MEDIA_BUS_FMT_SBGGR14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG14V32, 16, 14, 0, MEDIA_BUS_FMT_SGBRG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG14V32, 16, 14, 0, MEDIA_BUS_FMT_SGRBG14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB14V32, 16, 14, 0, MEDIA_BUS_FMT_SRGGB14_1X14, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR12V32, 16, 12, 0, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG12V32, 16, 12, 0, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG12V32, 16, 12, 0, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB12V32, 16, 12, 0, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR10V32, 16, 10, 0, MEDIA_BUS_FMT_SBGGR10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG10V32, 16, 10, 0, MEDIA_BUS_FMT_SGBRG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG10V32, 16, 10, 0, MEDIA_BUS_FMT_SGRBG10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB10V32, 16, 10, 0, MEDIA_BUS_FMT_SRGGB10_1X10, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR8_16V32, 16, 8, 0, MEDIA_BUS_FMT_SBGGR8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG8_16V32, 16, 8, 0, MEDIA_BUS_FMT_SGBRG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG8_16V32, 16, 8, 0, MEDIA_BUS_FMT_SGRBG8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB8_16V32, 16, 8, 0, MEDIA_BUS_FMT_SRGGB8_1X8, + IPU_FW_ISYS_FRAME_FORMAT_RAW16}, + {V4L2_FMT_IPU_ISYS_META, 8, 8, 0, MEDIA_BUS_FMT_FIXED, + IPU_FW_ISYS_MIPI_DATA_TYPE_EMBEDDED}, + {} +}; + +struct ipu_trace_block isys_trace_blocks[] = { + { + .offset = TRACE_REG_IS_TRACE_UNIT_BASE, + .type = IPU_TRACE_BLOCK_TUN, + }, + { + .offset = TRACE_REG_IS_SP_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_IS_SP_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_IS_ISL_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_IS_MMU_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_CSI2_TM_BASE, + .type = IPU_TRACE_CSI2, + }, + { + .offset = TRACE_REG_CSI2_3PH_TM_BASE, + .type = IPU_TRACE_CSI2_3PH, + }, + { + /* Note! this covers all 9 blocks */ + .offset = TRACE_REG_CSI2_SIG2SIO_GR_BASE(0), + .type = IPU_TRACE_SIG2CIOS, + }, + { + /* Note! this covers all 9 blocks */ + .offset = TRACE_REG_CSI2_PH3_SIG2SIO_GR_BASE(0), + .type = IPU_TRACE_SIG2CIOS, + }, + { + .offset = TRACE_REG_IS_GPREG_TRACE_TIMER_RST_N, + .type = IPU_TRACE_TIMER_RST, + }, + { + .type = IPU_TRACE_BLOCK_END, + } +}; + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +void isys_setup_hw(struct ipu_isys *isys) +{ + void __iomem *base = isys->pdata->base; + const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold; + u32 irqs; + unsigned int i; + + /* Enable irqs for all MIPI busses */ + irqs = IPU_ISYS_UNISPART_IRQ_CSI2(0) | + IPU_ISYS_UNISPART_IRQ_CSI2(1) | + IPU_ISYS_UNISPART_IRQ_CSI2(2) | + IPU_ISYS_UNISPART_IRQ_CSI2(3) | + IPU_ISYS_UNISPART_IRQ_CSI2(4) | IPU_ISYS_UNISPART_IRQ_CSI2(5); + + irqs |= IPU_ISYS_UNISPART_IRQ_SW; + + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_EDGE); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_CLEAR); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_MASK); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_ENABLE); + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_MUX_REG); + + /* Write CDC FIFO threshold values for isys */ + for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++) + writel(thd[i], base + IPU_REG_ISYS_CDC_THRESHOLD(i)); +} +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4P +/* + * For new HW, extra common register (en_flush_for_idrain)added to the IBufCtrl + * of ISL_IS and CSI that enables the feature to send a DMA command with flush + * when draining. This means that a DMA command is send with the flush bit + * set(read post write check is performed) when a drain request comes in and + * iwake is enabled for that SID proc. + * This results in that all data is moved out of the system when the IDone is + * given back. Default the feature is off, to keep behavior as is when nothing + * is written, writing 0x1 to the register (reg 11 in common reg bank, + * addr ibuf_base + 0x2C) to enable this feature. + */ +static int ipu4p_isys_flush_idrain_en(struct ipu_isys *isys) +{ + void __iomem *base = isys->pdata->base; + + writel(1, base + CSI2_REG_CL0_IBUFCTL_EN_FLUSH_FOR_IDRAIN); + writel(1, base + CSI2_REG_CL1_IBUFCTL_EN_FLUSH_FOR_IDRAIN); + writel(1, base + IPU_REG_ISYS_IBUFCTL_EN_FLUSH_FOR_IDRAIN); + + return 0; +} + +static void ipu4p_isys_irq_cfg(struct ipu_isys *isys) +{ + void __iomem *base = isys->pdata->base; + int i, j; + struct { + u32 base; + u32 mask; + } irq_config[] = { + {IPU_REG_ISYS_UNISPART_IRQ_EDGE, 0x400018}, + {IPU_REG_ISYS_ISA_ACC_IRQ_CTRL_BASE, 0x0}, + {IPU_REG_ISYS_A_IRQ_CTRL_BASE, 0x0}, + {IPU_REG_ISYS_SIP0_IRQ_CTRL_BASE, 0xf}, + {IPU_REG_ISYS_SIP1_IRQ_CTRL_BASE, 0xf}, + }; + unsigned int offsets[4] = { + 0x0, 0x4, 0x10, 0x14 + }; + + for (i = 0; i < ARRAY_SIZE(irq_config); i++) { + for (j = 0; j < ARRAY_SIZE(offsets); j++) + writel(irq_config[i].mask, + base + irq_config[i].base + offsets[j]); + writel(0xffffffff, base + irq_config[i].base + 0xc); + } + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_MUX_REG); +} + +static void ipu4p_isys_bb_cfg(struct ipu_isys *isys) +{ + void __iomem *isp_base = isys->adev->isp->base; + unsigned int i, val; + unsigned int bbconfig[4][4] = { + {4, 13, 32, 0xf}, + {6, 13, 32, 0x15}, + {12, 13, 32, 0xf}, + {14, 13, 32, 0x15}, + }; + + /* Config building block */ + for (i = 0; i < 4; i++) { + unsigned int bb = bbconfig[i][0]; + unsigned int crc = bbconfig[i][1]; + unsigned int drc = bbconfig[i][2]; + unsigned int afe = bbconfig[i][3]; + + val = readl(isp_base + BUTTRESS_REG_CPHYX_DLL_OVRD(bb)); + val &= ~0x7e; + val |= crc << 1; + val |= 1; + writel(val, isp_base + BUTTRESS_REG_CPHYX_DLL_OVRD(bb)); + val = readl(isp_base + BUTTRESS_REG_DPHYX_DLL_OVRD(bb)); + val |= 1; + val |= drc << 1; + writel(val, isp_base + BUTTRESS_REG_DPHYX_DLL_OVRD(bb)); + val = afe | (2 << 29); + writel(val, isp_base + BUTTRESS_REG_BBX_AFE_CONFIG(bb)); + } +} + +static void ipu4p_isys_port_cfg(struct ipu_isys *isys) +{ + void __iomem *base = isys->pdata->base; + void __iomem *isp_base = isys->adev->isp->base; + + /* Port config */ + writel(0x3895, base + IPU_GPOFFSET + 0x14); + writel(0x3895, base + IPU_COMBO_GPOFFSET + 0x14); + writel((0x100 << 1) | (0x100 << 10) | (0x100 << 19), isp_base + + BUTTRESS_REG_CSI_BSCAN_EXCLUDE); +} + +void isys_setup_hw(struct ipu_isys *isys) +{ + ipu4p_isys_irq_cfg(isys); + ipu4p_isys_port_cfg(isys); + ipu4p_isys_bb_cfg(isys); + ipu4p_isys_flush_idrain_en(isys); +} +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +irqreturn_t isys_isr(struct ipu_bus_device *adev) +{ + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + void __iomem *base = isys->pdata->base; + u32 status; + + spin_lock(&isys->power_lock); + if (!isys->power) { + spin_unlock(&isys->power_lock); + return IRQ_NONE; + } + + status = readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + do { + writel(status, isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_CLEAR); + + if (isys->isr_csi2_bits & status) { + unsigned int i; + + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + if (IPU_ISYS_UNISPART_IRQ_CSI2(i) & status) + ipu_isys_csi2_isr(&isys->csi2[i]); + } + } + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + + /* + * Handle a single FW event per checking the CSI-2 + * receiver SOF status. This is done in order to avoid + * the case where events arrive to the event queue and + * one of them is a SOF event which then could be + * handled before the SOF interrupt. This would pose + * issues in sequence numbering which is based on SOF + * interrupts, always assumed to arrive before FW SOF + * events. + */ + if (status & IPU_ISYS_UNISPART_IRQ_SW && !isys_isr_one(adev)) + status = IPU_ISYS_UNISPART_IRQ_SW; + else + status = 0; + + status |= readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + } while (status & (isys->isr_csi2_bits + | IPU_ISYS_UNISPART_IRQ_SW) && + !isys->adev->isp->flr_done); + spin_unlock(&isys->power_lock); + + return IRQ_HANDLED; +} +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4P +irqreturn_t isys_isr(struct ipu_bus_device *adev) +{ + struct ipu_isys *isys = ipu_bus_get_drvdata(adev); + void __iomem *base = isys->pdata->base; + u32 status; + unsigned int i; + u32 sip0_status, sip1_status; + struct { + u32 *status; + u32 mask; + } csi2_irq_mask[] = { + {&sip0_status, IPU_ISYS_CSI2_D_IRQ_MASK}, + {&sip1_status, IPU_ISYS_CSI2_A_IRQ_MASK}, + {&sip1_status, IPU_ISYS_CSI2_B_IRQ_MASK}, + {&sip1_status, IPU_ISYS_CSI2_C_IRQ_MASK}, + {&sip1_status, IPU_ISYS_CSI2_D_IRQ_MASK}, + }; + + spin_lock(&isys->power_lock); + if (!isys->power) { + spin_unlock(&isys->power_lock); + return IRQ_NONE; + } + + /* read unis sw irq */ + status = readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + dev_dbg(&adev->dev, "isys irq status - unis sw irq = 0x%x", status); + + do { + /* clear unis sw irqs */ + writel(status, isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_CLEAR); + + /* read and clear sip irq status */ + sip0_status = readl(isys->pdata->base + + IPU_REG_ISYS_SIP0_IRQ_CTRL_STATUS); + sip1_status = readl(isys->pdata->base + + IPU_REG_ISYS_SIP1_IRQ_CTRL_STATUS); + dev_dbg(&adev->dev, "isys irq status - sip0 = 0x%x sip1 = 0x%x", + sip0_status, sip1_status); + writel(sip0_status, isys->pdata->base + + IPU_REG_ISYS_SIP0_IRQ_CTRL_CLEAR); + writel(sip1_status, isys->pdata->base + + IPU_REG_ISYS_SIP1_IRQ_CTRL_CLEAR); + + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + if (*csi2_irq_mask[i].status & csi2_irq_mask[i].mask) + ipu_isys_csi2_isr(&isys->csi2[i]); + } + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + + /* + * Handle a single FW event per checking the CSI-2 + * receiver SOF status. This is done in order to avoid + * the case where events arrive to the event queue and + * one of them is a SOF event which then could be + * handled before the SOF interrupt. This would pose + * issues in sequence numbering which is based on SOF + * interrupts, always assumed to arrive before FW SOF + * events. + */ + if (status & IPU_ISYS_UNISPART_IRQ_SW && !isys_isr_one(adev)) + status = IPU_ISYS_UNISPART_IRQ_SW; + else + status = 0; + + status |= readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + } while (status & (isys->isr_csi2_bits + | IPU_ISYS_UNISPART_IRQ_SW) && + !isys->adev->isp->flr_done); + spin_unlock(&isys->power_lock); + + return IRQ_HANDLED; +} +#endif + +int tpg_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ipu_isys_tpg *tpg = to_ipu_isys_tpg(sd); +#ifdef IPU_VC_SUPPORT + __u32 code = tpg->asd.ffmt[TPG_PAD_SOURCE][0].code; +#else + __u32 code = tpg->asd.ffmt[TPG_PAD_SOURCE].code; +#endif + unsigned int bpp = ipu_isys_mbus_code_to_bpp(code); + + /* + * MIPI_GEN block is CSI2 FB. Need to enable/disable TPG selection + * register to control the TPG streaming. + */ + if (tpg->sel) + writel(enable ? 1 : 0, tpg->sel); + + if (!enable) { + writel(0, tpg->base + MIPI_GEN_REG_COM_ENABLE); + return 0; + } + + writel(MIPI_GEN_COM_DTYPE_RAW(bpp), + tpg->base + MIPI_GEN_REG_COM_DTYPE); + writel(ipu_isys_mbus_code_to_mipi(code), + tpg->base + MIPI_GEN_REG_COM_VTYPE); + writel(0, tpg->base + MIPI_GEN_REG_COM_VCHAN); + + writel(0, tpg->base + MIPI_GEN_REG_SYNG_NOF_FRAMES); + +#ifdef IPU_VC_SUPPORT + writel(DIV_ROUND_UP(tpg->asd.ffmt[TPG_PAD_SOURCE][0].width * + bpp, BITS_PER_BYTE), + tpg->base + MIPI_GEN_REG_COM_WCOUNT); + writel(DIV_ROUND_UP(tpg->asd.ffmt[TPG_PAD_SOURCE][0].width, + MIPI_GEN_PPC), + tpg->base + MIPI_GEN_REG_SYNG_NOF_PIXELS); + writel(tpg->asd.ffmt[TPG_PAD_SOURCE][0].height, + tpg->base + MIPI_GEN_REG_SYNG_NOF_LINES); +#else + writel(DIV_ROUND_UP(tpg->asd.ffmt[TPG_PAD_SOURCE].width * + bpp, BITS_PER_BYTE), + tpg->base + MIPI_GEN_REG_COM_WCOUNT); + writel(DIV_ROUND_UP(tpg->asd.ffmt[TPG_PAD_SOURCE].width, + MIPI_GEN_PPC), + tpg->base + MIPI_GEN_REG_SYNG_NOF_PIXELS); + writel(tpg->asd.ffmt[TPG_PAD_SOURCE].height, + tpg->base + MIPI_GEN_REG_SYNG_NOF_LINES); +#endif + + writel(0, tpg->base + MIPI_GEN_REG_TPG_MODE); + writel(-1, tpg->base + MIPI_GEN_REG_TPG_HCNT_MASK); + writel(-1, tpg->base + MIPI_GEN_REG_TPG_VCNT_MASK); + writel(-1, tpg->base + MIPI_GEN_REG_TPG_XYCNT_MASK); + writel(0, tpg->base + MIPI_GEN_REG_TPG_HCNT_DELTA); + writel(0, tpg->base + MIPI_GEN_REG_TPG_VCNT_DELTA); + + v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); + + writel(2, tpg->base + MIPI_GEN_REG_COM_ENABLE); + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-psys.c b/drivers/media/pci/intel/ipu4/ipu4-psys.c new file mode 100644 index 000000000000..73e2b7ca22b7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-psys.c @@ -0,0 +1,1109 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#include +#else +#include +#endif +#include +#include + +#include "ipu.h" +#include "ipu-psys.h" +#include "ipu-platform-regs.h" +#include "ipu-trace.h" +#define CREATE_TRACE_POINTS +#define IPU_PG_KCMD_TRACE +#include "ipu-trace-event.h" + +static bool early_pg_transfer; +static bool enable_concurrency = true; +module_param(early_pg_transfer, bool, 0664); +module_param(enable_concurrency, bool, 0664); +MODULE_PARM_DESC(early_pg_transfer, + "Copy PGs back to user after resource allocation"); +MODULE_PARM_DESC(enable_concurrency, + "Enable concurrent execution of program groups"); + +struct ipu_trace_block psys_trace_blocks[] = { + { + .offset = TRACE_REG_PS_TRACE_UNIT_BASE, + .type = IPU_TRACE_BLOCK_TUN, + }, + { + .offset = TRACE_REG_PS_SPC_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_SPP0_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_SPP1_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_ISP0_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_ISP1_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_ISP2_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_ISP3_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_PS_SPC_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_SPP0_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_SPP1_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_MMU_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_ISL_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_ISP0_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_ISP1_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_ISP2_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_ISP3_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_PS_GPREG_TRACE_TIMER_RST_N, + .type = IPU_TRACE_TIMER_RST, + }, + { + .type = IPU_TRACE_BLOCK_END, + } +}; + +static int ipu_psys_kcmd_abort(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd); +static int ipu_psys_kcmd_queue(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd); + +static void set_sp_info_bits(void *base) +{ + int i; + + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + base + IPU_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER); + + for (i = 0; i < 4; i++) + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + base + IPU_REG_PSYS_INFO_SEG_CMEM_MASTER(i)); + for (i = 0; i < 4; i++) + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + base + IPU_REG_PSYS_INFO_SEG_XMEM_MASTER(i)); +} + +static void set_isp_info_bits(void *base) +{ + int i; + + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + base + IPU_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER); + + for (i = 0; i < 4; i++) + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + base + IPU_REG_PSYS_INFO_SEG_DATA_MASTER(i)); +} + +void ipu_psys_setup_hw(struct ipu_psys *psys) +{ + void __iomem *base = psys->pdata->base; + void __iomem *spc_regs_base = + base + psys->pdata->ipdata->hw_variant.spc_offset; + void *psys_iommu0_ctrl = base + + psys->pdata->ipdata->hw_variant.mmu_hw[0].offset + + IPU_PSYS_MMU0_CTRL_OFFSET; + const u8 *thd = psys->pdata->ipdata->hw_variant.cdc_fifo_threshold; + u32 irqs; + unsigned int i; + + /* Configure PSYS info bits */ + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, psys_iommu0_ctrl); + + set_sp_info_bits(spc_regs_base + IPU_PSYS_REG_SPC_STATUS_CTRL); + set_sp_info_bits(spc_regs_base + IPU_PSYS_REG_SPP0_STATUS_CTRL); + set_sp_info_bits(spc_regs_base + IPU_PSYS_REG_SPP1_STATUS_CTRL); + set_isp_info_bits(spc_regs_base + IPU_PSYS_REG_ISP0_STATUS_CTRL); + set_isp_info_bits(spc_regs_base + IPU_PSYS_REG_ISP1_STATUS_CTRL); + set_isp_info_bits(spc_regs_base + IPU_PSYS_REG_ISP2_STATUS_CTRL); + set_isp_info_bits(spc_regs_base + IPU_PSYS_REG_ISP3_STATUS_CTRL); + + /* Enable FW interrupt #0 */ + writel(0, base + IPU_REG_PSYS_GPDEV_FWIRQ(0)); + irqs = IPU_PSYS_GPDEV_IRQ_FWIRQ(0); + writel(irqs, base + IPU_REG_PSYS_GPDEV_IRQ_EDGE); + /* + * With pulse setting, driver misses interrupts. IUNIT integration + * HAS(v1.26) suggests to use pulse, but this seem to be error in + * documentation. + */ + writel(irqs, base + IPU_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE); + writel(irqs, base + IPU_REG_PSYS_GPDEV_IRQ_CLEAR); + writel(irqs, base + IPU_REG_PSYS_GPDEV_IRQ_MASK); + writel(irqs, base + IPU_REG_PSYS_GPDEV_IRQ_ENABLE); + + /* Write CDC FIFO threshold values for psys */ + for (i = 0; i < psys->pdata->ipdata->hw_variant.cdc_fifos; i++) + writel(thd[i], base + IPU_REG_PSYS_CDC_THRESHOLD(i)); +} + +/* + * Called to free up all resources associated with a kcmd. + * After this the kcmd doesn't anymore exist in the driver. + */ +void ipu_psys_kcmd_free(struct ipu_psys_kcmd *kcmd) +{ + struct ipu_psys *psys; + unsigned long flags; + + if (!kcmd) + return; + + psys = kcmd->fh->psys; + + if (!list_empty(&kcmd->list)) + list_del(&kcmd->list); + + spin_lock_irqsave(&psys->pgs_lock, flags); + if (kcmd->kpg) + kcmd->kpg->pg_size = 0; + spin_unlock_irqrestore(&psys->pgs_lock, flags); + + kfree(kcmd->pg_manifest); + kfree(kcmd->kbufs); + kfree(kcmd->buffers); + kfree(kcmd); +} + +static struct ipu_psys_kcmd *ipu_psys_copy_cmd(struct ipu_psys_command *cmd, + struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kcmd *kcmd; + struct ipu_psys_kbuffer *kpgbuf; + unsigned int i; + int ret, prevfd = 0; + + if (cmd->bufcount > IPU_MAX_PSYS_CMD_BUFFERS) + return NULL; + + if (!cmd->pg_manifest_size || + cmd->pg_manifest_size > KMALLOC_MAX_CACHE_SIZE) + return NULL; + + kcmd = kzalloc(sizeof(*kcmd), GFP_KERNEL); + if (!kcmd) + return NULL; + + kcmd->state = KCMD_STATE_NEW; + kcmd->fh = fh; + INIT_LIST_HEAD(&kcmd->list); + INIT_LIST_HEAD(&kcmd->started_list); + + mutex_lock(&fh->mutex); + kpgbuf = ipu_psys_lookup_kbuffer(fh, cmd->pg); + mutex_unlock(&fh->mutex); + if (!kpgbuf || !kpgbuf->sgt) + goto error; + + kcmd->pg_user = kpgbuf->kaddr; + kcmd->kpg = __get_pg_buf(psys, kpgbuf->len); + if (!kcmd->kpg) + goto error; + + memcpy(kcmd->kpg->pg, kcmd->pg_user, kcmd->kpg->pg_size); + + kcmd->pg_manifest = kzalloc(cmd->pg_manifest_size, GFP_KERNEL); + if (!kcmd->pg_manifest) + goto error; + + ret = copy_from_user(kcmd->pg_manifest, cmd->pg_manifest, + cmd->pg_manifest_size); + if (ret) + goto error; + + kcmd->pg_manifest_size = cmd->pg_manifest_size; + + kcmd->user_token = cmd->user_token; + kcmd->issue_id = cmd->issue_id; + kcmd->priority = cmd->priority; + if (kcmd->priority >= IPU_PSYS_CMD_PRIORITY_NUM) + goto error; + + kcmd->nbuffers = ipu_fw_psys_pg_get_terminal_count(kcmd); + kcmd->buffers = kcalloc(kcmd->nbuffers, sizeof(*kcmd->buffers), + GFP_KERNEL); + if (!kcmd->buffers) + goto error; + + kcmd->kbufs = kcalloc(kcmd->nbuffers, sizeof(kcmd->kbufs[0]), + GFP_KERNEL); + if (!kcmd->kbufs) + goto error; + + + if (!cmd->bufcount || kcmd->nbuffers > cmd->bufcount) + goto error; + + ret = copy_from_user(kcmd->buffers, cmd->buffers, + kcmd->nbuffers * sizeof(*kcmd->buffers)); + if (ret) + goto error; + + for (i = 0; i < kcmd->nbuffers; i++) { + struct ipu_fw_psys_terminal *terminal; + + terminal = ipu_fw_psys_pg_get_terminal(kcmd, i); + if (!terminal) + continue; + + + mutex_lock(&fh->mutex); + kcmd->kbufs[i] = ipu_psys_lookup_kbuffer(fh, + kcmd->buffers[i].base.fd); + mutex_unlock(&fh->mutex); + if (!kcmd->kbufs[i] || !kcmd->kbufs[i]->sgt || + kcmd->kbufs[i]->len < kcmd->buffers[i].bytes_used) + goto error; + if ((kcmd->kbufs[i]->flags & + IPU_BUFFER_FLAG_NO_FLUSH) || + (kcmd->buffers[i].flags & + IPU_BUFFER_FLAG_NO_FLUSH) || + prevfd == kcmd->buffers[i].base.fd) + continue; + + prevfd = kcmd->buffers[i].base.fd; + dma_sync_sg_for_device(&psys->adev->dev, + kcmd->kbufs[i]->sgt->sgl, + kcmd->kbufs[i]->sgt->orig_nents, + DMA_BIDIRECTIONAL); + } + + + return kcmd; +error: + ipu_psys_kcmd_free(kcmd); + + dev_dbg(&psys->adev->dev, "failed to copy cmd\n"); + + return NULL; +} + +static void ipu_psys_kcmd_run(struct ipu_psys *psys) +{ + struct ipu_psys_kcmd *kcmd = list_first_entry(&psys->started_kcmds_list, + struct ipu_psys_kcmd, + started_list); + int ret; + + ret = ipu_psys_move_resources(&psys->adev->dev, + &kcmd->kpg->resource_alloc, + &psys->resource_pool_started, + &psys->resource_pool_running); + if (!ret) { + psys->started_kcmds--; + psys->active_kcmds++; + kcmd->state = KCMD_STATE_RUNNING; + list_del(&kcmd->started_list); + kcmd->watchdog.expires = jiffies + + msecs_to_jiffies(psys->timeout); + add_timer(&kcmd->watchdog); + return; + } + + if (ret != -ENOSPC || !psys->active_kcmds) { + dev_err(&psys->adev->dev, + "kcmd %p failed to alloc resources %d, active_kcmds %d\n", + kcmd, ret, psys->active_kcmds); + ipu_psys_kcmd_abort(psys, kcmd); + return; + } +} + +/* + * Move kcmd into completed state (due to running finished or failure). + * Fill up the event struct and notify waiters. + */ +void ipu_psys_kcmd_complete(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd, int error) +{ + struct ipu_psys_fh *fh = kcmd->fh; + + trace_ipu_pg_kcmd(__func__, kcmd->user_token, kcmd->issue_id, + kcmd->priority, + ipu_fw_psys_pg_get_id(kcmd), + ipu_fw_psys_pg_load_cycles(kcmd), + ipu_fw_psys_pg_init_cycles(kcmd), + ipu_fw_psys_pg_processing_cycles(kcmd)); + + switch (kcmd->state) { + case KCMD_STATE_RUNNING: + if (try_to_del_timer_sync(&kcmd->watchdog) < 0) { + dev_err(&psys->adev->dev, + "could not cancel kcmd timer\n"); + return; + } + /* Fall through on purpose */ + case KCMD_STATE_RUN_PREPARED: + ipu_psys_free_resources(&kcmd->kpg->resource_alloc, + &psys->resource_pool_running); + if (psys->started_kcmds) + ipu_psys_kcmd_run(psys); + if (kcmd->state == KCMD_STATE_RUNNING) + psys->active_kcmds--; + break; + case KCMD_STATE_STARTED: + psys->started_kcmds--; + list_del(&kcmd->started_list); + /* Fall through on purpose */ + case KCMD_STATE_START_PREPARED: + ipu_psys_free_resources(&kcmd->kpg->resource_alloc, + &psys->resource_pool_started); + break; + default: + break; + } + + kcmd->ev.type = IPU_PSYS_EVENT_TYPE_CMD_COMPLETE; + kcmd->ev.user_token = kcmd->user_token; + kcmd->ev.issue_id = kcmd->issue_id; + kcmd->ev.error = error; + + if (kcmd->constraint.min_freq) + ipu_buttress_remove_psys_constraint(psys->adev->isp, + &kcmd->constraint); + + if (!early_pg_transfer && kcmd->pg_user && kcmd->kpg->pg) { + struct ipu_psys_kbuffer *kbuf; + + kbuf = ipu_psys_lookup_kbuffer_by_kaddr(kcmd->fh, + kcmd->pg_user); + + if (kbuf && kbuf->valid) + memcpy(kcmd->pg_user, + kcmd->kpg->pg, kcmd->kpg->pg_size); + else + dev_dbg(&psys->adev->dev, + "Skipping already unmapped buffer\n"); + } + + if (kcmd->state == KCMD_STATE_RUNNING || + kcmd->state == KCMD_STATE_STARTED) { + pm_runtime_mark_last_busy(&psys->adev->dev); + pm_runtime_put_autosuspend(&psys->adev->dev); + } + + kcmd->state = KCMD_STATE_COMPLETE; + + wake_up_interruptible(&fh->wait); +} + +/* + * Schedule next kcmd by finding a runnable kcmd from the highest + * priority queue in a round-robin fashion versus the client + * queues and running it. + * Any kcmds which fail to start are completed with an error. + */ +void ipu_psys_run_next(struct ipu_psys *psys) +{ + int p; + + /* + * Code below will crash if fhs is empty. Normally this + * shouldn't happen. + */ + if (list_empty(&psys->fhs)) { + WARN_ON(1); + return; + } + + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + int removed; + + do { + struct ipu_psys_fh *fh = list_first_entry(&psys->fhs, + struct + ipu_psys_fh, + list); + struct ipu_psys_fh *fh_last = + list_last_entry(&psys->fhs, + struct ipu_psys_fh, + list); + /* + * When a kcmd is scheduled from a fh, it might expose + * more runnable kcmds behind it in the same queue. + * Therefore loop running kcmds as long as some were + * scheduled. + */ + removed = 0; + do { + struct ipu_psys_fh *fh_next = + list_next_entry(fh, list); + struct ipu_psys_kcmd *kcmd; + int ret; + + mutex_lock(&fh->mutex); + + kcmd = fh->sched.new_kcmd_tail[p]; + /* + * If concurrency is disabled and there are + * already commands running on the PSYS, do not + * run new commands. + */ + if (!enable_concurrency && + psys->active_kcmds > 0) { + mutex_unlock(&fh->mutex); + return; + } + + /* Are there new kcmds available for running? */ + if (!kcmd) + goto next; + + ret = ipu_psys_kcmd_queue(psys, kcmd); + if (ret == -ENOSPC) + goto next; + + /* Update pointer to the first new kcmd */ + fh->sched.new_kcmd_tail[p] = NULL; + while (kcmd != list_last_entry( + &fh->sched.kcmds[p], + struct ipu_psys_kcmd, + list)) { + kcmd = list_next_entry(kcmd, list); + if (kcmd->state == KCMD_STATE_NEW) { + fh->sched.new_kcmd_tail[p] = + kcmd; + break; + } + } + + list_move_tail(&fh->list, &psys->fhs); + removed++; +next: + mutex_unlock(&fh->mutex); + if (fh == fh_last) + break; + fh = fh_next; + } while (1); + } while (removed > 0); + } +} + +/* + * Move kcmd into completed state. If kcmd is currently running, + * abort it. + */ +int ipu_psys_kcmd_abort(struct ipu_psys *psys, struct ipu_psys_kcmd *kcmd) +{ + int ret = 0; + + if (kcmd->state == KCMD_STATE_COMPLETE) + return 0; + + if ((kcmd->state == KCMD_STATE_RUNNING || + kcmd->state == KCMD_STATE_STARTED)) { + ret = ipu_fw_psys_pg_abort(kcmd); + if (ret) { + dev_err(&psys->adev->dev, "failed to abort kcmd!\n"); + goto out; + } + } + +out: + ipu_psys_kcmd_complete(psys, kcmd, ret); + + return ret; +} + +/* + * Submit kcmd into psys queue. If running fails, complete the kcmd + * with an error. + */ +static int ipu_psys_kcmd_start(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd) +{ + /* + * Found a runnable PG. Move queue to the list tail for round-robin + * scheduling and run the PG. Start the watchdog timer if the PG was + * started successfully. Enable PSYS power if requested. + */ + int ret; + + if (psys->adev->isp->flr_done) { + ipu_psys_kcmd_complete(psys, kcmd, -EIO); + return -EIO; + } + + ret = pm_runtime_get_sync(&psys->adev->dev); + if (ret < 0) { + dev_err(&psys->adev->dev, "failed to power on PSYS\n"); + ipu_psys_kcmd_complete(psys, kcmd, -EIO); + pm_runtime_put_noidle(&psys->adev->dev); + return ret; + } + + if (early_pg_transfer && kcmd->pg_user && kcmd->kpg->pg) + memcpy(kcmd->pg_user, kcmd->kpg->pg, kcmd->kpg->pg_size); + + ret = ipu_fw_psys_pg_start(kcmd); + if (ret) { + dev_err(&psys->adev->dev, "failed to start kcmd!\n"); + goto error; + } + + ipu_fw_psys_pg_dump(psys, kcmd, "run"); + + /* + * Starting from scci_master_20151228_1800, pg start api is split into + * two different calls, making driver responsible to flush pg between + * start and disown library calls. + */ + clflush_cache_range(kcmd->kpg->pg, kcmd->kpg->pg_size); + ret = ipu_fw_psys_pg_disown(kcmd); + if (ret) { + dev_err(&psys->adev->dev, "failed to start kcmd!\n"); + goto error; + } + + trace_ipu_pg_kcmd(__func__, kcmd->user_token, kcmd->issue_id, + kcmd->priority, + ipu_fw_psys_pg_get_id(kcmd), + ipu_fw_psys_pg_load_cycles(kcmd), + ipu_fw_psys_pg_init_cycles(kcmd), + ipu_fw_psys_pg_processing_cycles(kcmd)); + + switch (kcmd->state) { + case KCMD_STATE_RUN_PREPARED: + kcmd->state = KCMD_STATE_RUNNING; + psys->active_kcmds++; + kcmd->watchdog.expires = jiffies + + msecs_to_jiffies(psys->timeout); + add_timer(&kcmd->watchdog); + break; + case KCMD_STATE_START_PREPARED: + kcmd->state = KCMD_STATE_STARTED; + psys->started_kcmds++; + list_add_tail(&kcmd->started_list, &psys->started_kcmds_list); + break; + default: + WARN_ON(1); + ret = -EINVAL; + goto error; + } + return 0; + +error: + dev_err(&psys->adev->dev, "failed to start process group\n"); + ipu_psys_kcmd_complete(psys, kcmd, -EIO); + return ret; +} + +/* + * Move all kcmds in all queues forcily into completed state. + */ +static void ipu_psys_flush_kcmds(struct ipu_psys *psys, int error) +{ + struct ipu_psys_fh *fh; + struct ipu_psys_kcmd *kcmd; + int p; + + dev_err(&psys->dev, "flushing all commands with error: %d\n", error); + + list_for_each_entry(fh, &psys->fhs, list) { + mutex_lock(&fh->mutex); + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + fh->sched.new_kcmd_tail[p] = NULL; + list_for_each_entry(kcmd, &fh->sched.kcmds[p], list) { + if (kcmd->state == KCMD_STATE_COMPLETE) + continue; + ipu_psys_kcmd_complete(psys, kcmd, error); + } + } + mutex_unlock(&fh->mutex); + } +} + +/* + * Abort all currently running process groups and reset PSYS + * by power cycling it. PSYS power must not be acquired + * except by running kcmds when calling this. + */ +static void ipu_psys_reset(struct ipu_psys *psys) +{ +#ifdef CONFIG_PM + struct device *d = &psys->adev->isp->psys_iommu->dev; + int r; + + pm_runtime_dont_use_autosuspend(&psys->adev->dev); + r = pm_runtime_get_sync(d); + if (r < 0) { + pm_runtime_put_noidle(d); + dev_err(&psys->adev->dev, "power management failed\n"); + return; + } + + ipu_psys_flush_kcmds(psys, -EIO); + flush_workqueue(pm_wq); + r = pm_runtime_put_sync(d); /* Turn big red power knob off here */ + /* Power was successfully turned off if and only if zero was returned */ + if (r) + dev_warn(&psys->adev->dev, + "power management failed, PSYS reset may be incomplete\n"); + pm_runtime_use_autosuspend(&psys->adev->dev); + ipu_psys_run_next(psys); +#else + dev_err(&psys->adev->dev, + "power management disabled, can not reset PSYS\n"); +#endif +} + +void ipu_psys_watchdog_work(struct work_struct *work) +{ + struct ipu_psys *psys = container_of(work, + struct ipu_psys, watchdog_work); + struct ipu_psys_fh *fh; + + mutex_lock(&psys->mutex); + + /* Loop over all running kcmds */ + list_for_each_entry(fh, &psys->fhs, list) { + int p, r; + + mutex_lock(&fh->mutex); + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + struct ipu_psys_kcmd *kcmd; + + list_for_each_entry(kcmd, &fh->sched.kcmds[p], list) { + if (fh->sched.new_kcmd_tail[p] == kcmd) + break; + if (kcmd->state != KCMD_STATE_RUNNING) + continue; + + if (timer_pending(&kcmd->watchdog)) + continue; + /* Found an expired but running command */ + dev_err(&psys->adev->dev, + "kcmd:0x%llx[0x%llx] taking too long\n", + kcmd->user_token, kcmd->issue_id); + r = ipu_psys_kcmd_abort(psys, kcmd); + if (r) + goto stop_failed; + } + } + mutex_unlock(&fh->mutex); + } + + /* Kick command scheduler thread */ + atomic_set(&psys->wakeup_sched_thread_count, 1); + wake_up_interruptible(&psys->sched_cmd_wq); + mutex_unlock(&psys->mutex); + return; + +stop_failed: + mutex_unlock(&fh->mutex); + ipu_psys_reset(psys); + mutex_unlock(&psys->mutex); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 2) +static void ipu_psys_watchdog(unsigned long data) +{ + struct ipu_psys_kcmd *kcmd = (struct ipu_psys_kcmd *)data; +#else +static void ipu_psys_watchdog(struct timer_list *t) +{ + struct ipu_psys_kcmd *kcmd = from_timer(kcmd, t, watchdog); +#endif + struct ipu_psys *psys = kcmd->fh->psys; + + queue_work(IPU_PSYS_WORK_QUEUE, &psys->watchdog_work); +} + +static int ipu_psys_config_legacy_pg(struct ipu_psys_kcmd *kcmd) +{ + struct ipu_psys *psys = kcmd->fh->psys; + unsigned int i; + int ret; + + ret = ipu_fw_psys_pg_set_ipu_vaddress(kcmd, kcmd->kpg->pg_dma_addr); + if (ret) { + ret = -EIO; + goto error; + } + + for (i = 0; i < kcmd->nbuffers; i++) { + struct ipu_fw_psys_terminal *terminal; + u32 buffer; + + terminal = ipu_fw_psys_pg_get_terminal(kcmd, i); + if (!terminal) + continue; + + buffer = (u32) kcmd->kbufs[i]->dma_addr + + kcmd->buffers[i].data_offset; + + ret = ipu_fw_psys_terminal_set(terminal, i, kcmd, + buffer, kcmd->kbufs[i]->len); + if (ret == -EAGAIN) + continue; + + if (ret) { + dev_err(&psys->adev->dev, "Unable to set terminal\n"); + goto error; + } + } + + ipu_fw_psys_pg_set_token(kcmd, (uintptr_t) kcmd); + + ret = ipu_fw_psys_pg_submit(kcmd); + if (ret) { + dev_err(&psys->adev->dev, "failed to submit kcmd!\n"); + goto error; + } + + return 0; + +error: + dev_err(&psys->adev->dev, "failed to config legacy pg\n"); + return ret; +} + +static bool ipu_psys_kcmd_is_valid(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd) +{ + struct ipu_psys_fh *fh; + struct ipu_psys_kcmd *kcmd0; + int p; + + list_for_each_entry(fh, &psys->fhs, list) { + mutex_lock(&fh->mutex); + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + list_for_each_entry(kcmd0, &fh->sched.kcmds[p], list) { + if (kcmd0 == kcmd) { + mutex_unlock(&fh->mutex); + return true; + } + } + } + mutex_unlock(&fh->mutex); + } + + return false; +} + +static int ipu_psys_kcmd_queue(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd) +{ + int ret; + + if (kcmd->state != KCMD_STATE_NEW) { + WARN_ON(1); + return -EINVAL; + } + + if (!psys->started_kcmds) { + ret = ipu_psys_allocate_resources(&psys->adev->dev, + kcmd->kpg->pg, + kcmd->pg_manifest, + &kcmd->kpg->resource_alloc, + &psys->resource_pool_running); + if (!ret) { + if (kcmd->state == KCMD_STATE_NEW) + kcmd->state = KCMD_STATE_RUN_PREPARED; + return ipu_psys_kcmd_start(psys, kcmd); + } + + if (ret != -ENOSPC || !psys->active_kcmds) { + dev_err(&psys->adev->dev, + "kcmd %p failed to alloc resources (running)\n", + kcmd); + ipu_psys_kcmd_complete(psys, kcmd, ret); + /* kcmd_complete doesn't handle PM for KCMD_STATE_NEW */ + pm_runtime_put(&psys->adev->dev); + return -EINVAL; + } + } + + ret = ipu_psys_allocate_resources(&psys->adev->dev, + kcmd->kpg->pg, + kcmd->pg_manifest, + &kcmd->kpg->resource_alloc, + &psys->resource_pool_started); + if (!ret) { + kcmd->state = KCMD_STATE_START_PREPARED; + return ipu_psys_kcmd_start(psys, kcmd); + } + + if (ret != -ENOSPC || !psys->started_kcmds) { + dev_err(&psys->adev->dev, + "kcmd %p failed to alloc resources (started)\n", kcmd); + ipu_psys_kcmd_complete(psys, kcmd, ret); + /* kcmd_complete doesn't handle PM for KCMD_STATE_NEW */ + pm_runtime_put(&psys->adev->dev); + ret = -EINVAL; + } + return ret; +} + +int ipu_psys_kcmd_new(struct ipu_psys_command *cmd, struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kcmd *kcmd; + size_t pg_size; + int ret; + + if (psys->adev->isp->flr_done) + return -EIO; + + kcmd = ipu_psys_copy_cmd(cmd, fh); + if (!kcmd) + return -EINVAL; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 2) + init_timer(&kcmd->watchdog); + kcmd->watchdog.data = (unsigned long)kcmd; + kcmd->watchdog.function = &ipu_psys_watchdog; +#else + timer_setup(&kcmd->watchdog, ipu_psys_watchdog, 0); +#endif + + if (cmd->min_psys_freq) { + kcmd->constraint.min_freq = cmd->min_psys_freq; + ipu_buttress_add_psys_constraint(psys->adev->isp, + &kcmd->constraint); + } + + pg_size = ipu_fw_psys_pg_get_size(kcmd); + if (pg_size > kcmd->kpg->pg_size) { + dev_dbg(&psys->adev->dev, "pg size mismatch %zu %zu\n", + pg_size, kcmd->kpg->pg_size); + ret = -EINVAL; + goto error; + } + + ret = ipu_psys_config_legacy_pg(kcmd); + if (ret) + goto error; + + mutex_lock(&fh->mutex); + list_add_tail(&kcmd->list, &fh->sched.kcmds[cmd->priority]); + if (!fh->sched.new_kcmd_tail[cmd->priority] && + kcmd->state == KCMD_STATE_NEW) { + fh->sched.new_kcmd_tail[cmd->priority] = kcmd; + /* Kick command scheduler thread */ + atomic_set(&psys->wakeup_sched_thread_count, 1); + wake_up_interruptible(&psys->sched_cmd_wq); + } + mutex_unlock(&fh->mutex); + + dev_dbg(&psys->adev->dev, + "IOC_QCMD: user_token:%llx issue_id:0x%llx pri:%d\n", + cmd->user_token, cmd->issue_id, cmd->priority); + + return 0; + +error: + ipu_psys_kcmd_free(kcmd); + + return ret; +} + +void ipu_psys_handle_events(struct ipu_psys *psys) +{ + struct ipu_psys_kcmd *kcmd = NULL; + struct ipu_fw_psys_event event; + bool error; + + do { + memset(&event, 0, sizeof(event)); + if (!ipu_fw_psys_rcv_event(psys, &event)) + break; + + error = false; + kcmd = (struct ipu_psys_kcmd *)(unsigned long)event.token; + error = IS_ERR_OR_NULL(kcmd) ? true : false; + + dev_dbg(&psys->adev->dev, "psys received event status:%d\n", + event.status); + + if (error) { + dev_err(&psys->adev->dev, + "no token received, command unknown\n"); + pm_runtime_put(&psys->adev->dev); + ipu_psys_reset(psys); + pm_runtime_get(&psys->adev->dev); + break; + } + + if (ipu_psys_kcmd_is_valid(psys, kcmd)) + ipu_psys_kcmd_complete(psys, kcmd, + event.status == + IPU_PSYS_EVENT_CMD_COMPLETE || + event.status == + IPU_PSYS_EVENT_FRAGMENT_COMPLETE + ? 0 : -EIO); + /* Kick command scheduler thread */ + atomic_set(&psys->wakeup_sched_thread_count, 1); + wake_up_interruptible(&psys->sched_cmd_wq); + } while (1); +} + +int ipu_psys_fh_init(struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + int p; + + pm_runtime_use_autosuspend(&psys->adev->dev); + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) + INIT_LIST_HEAD(&fh->sched.kcmds[p]); + + return 0; +} + +int ipu_psys_fh_deinit(struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kcmd *kcmd, *kcmd0; + int p; + + mutex_lock(&psys->mutex); + mutex_lock(&fh->mutex); + + /* + * Set pg_user to NULL so that completed kcmds don't write + * their result to user space anymore. + */ + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) + list_for_each_entry(kcmd, &fh->sched.kcmds[p], list) + kcmd->pg_user = NULL; + + /* Prevent scheduler from running more kcmds */ + memset(fh->sched.new_kcmd_tail, 0, + sizeof(fh->sched.new_kcmd_tail)); + + /* Wait until kcmds are completed in this queue and free them */ + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + fh->sched.new_kcmd_tail[p] = NULL; + list_for_each_entry_safe( + kcmd, kcmd0, &fh->sched.kcmds[p], list) { + ipu_psys_kcmd_abort(psys, kcmd); + ipu_psys_kcmd_free(kcmd); + } + } + + /* disable runtime autosuspend for the last fh */ + if (list_empty(&psys->fhs)) + pm_runtime_dont_use_autosuspend(&psys->adev->dev); + + mutex_unlock(&fh->mutex); + mutex_unlock(&psys->mutex); + + return 0; +} + +static struct ipu_psys_kcmd *__ipu_get_completed_kcmd(struct ipu_psys_fh *fh) +{ + int p; + + for (p = 0; p < IPU_PSYS_CMD_PRIORITY_NUM; p++) { + struct ipu_psys_kcmd *kcmd; + + if (list_empty(&fh->sched.kcmds[p])) + continue; + + kcmd = list_first_entry(&fh->sched.kcmds[p], + struct ipu_psys_kcmd, list); + if (kcmd->state != KCMD_STATE_COMPLETE) + continue; + /* Found a kcmd in completed state */ + return kcmd; + + } + + return NULL; +} + +struct ipu_psys_kcmd *ipu_get_completed_kcmd(struct ipu_psys_fh *fh) +{ + struct ipu_psys_kcmd *kcmd; + + mutex_lock(&fh->mutex); + kcmd = __ipu_get_completed_kcmd(fh); + mutex_unlock(&fh->mutex); + + return kcmd; +} + +long ipu_ioctl_dqevent(struct ipu_psys_event *event, + struct ipu_psys_fh *fh, unsigned int f_flags) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kcmd *kcmd = NULL; + int rval; + + dev_dbg(&psys->adev->dev, "IOC_DQEVENT\n"); + + if (!(f_flags & O_NONBLOCK)) { + rval = wait_event_interruptible(fh->wait, + (kcmd = + ipu_get_completed_kcmd(fh))); + if (rval == -ERESTARTSYS) + return rval; + } + + mutex_lock(&fh->mutex); + if (!kcmd) { + kcmd = __ipu_get_completed_kcmd(fh); + if (!kcmd) { + mutex_unlock(&fh->mutex); + return -ENODATA; + } + } + + *event = kcmd->ev; + ipu_psys_kcmd_free(kcmd); + mutex_unlock(&fh->mutex); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4-resources.c b/drivers/media/pci/intel/ipu4/ipu4-resources.c new file mode 100644 index 000000000000..097ea1bb7ed9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4-resources.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015 - 2018 Intel Corporation + +#include +#include +#include +#include +#include + +#include + +#include "ipu-fw-psys.h" +#include "ipu-psys.h" + +static int ipu_resource_init(struct ipu_resource *res, u32 id, int elements) +{ + if (elements <= 0) { + res->bitmap = NULL; + return 0; + } + + res->bitmap = kcalloc(BITS_TO_LONGS(elements), sizeof(long), + GFP_KERNEL); + if (!res->bitmap) + return -ENOMEM; + res->elements = elements; + res->id = id; + return 0; +} + +static unsigned long +ipu_resource_alloc(struct ipu_resource *res, int n, + struct ipu_resource_alloc *alloc, + enum ipu_resource_type type) +{ + unsigned long p; + + if (n <= 0) { + alloc->elements = 0; + return 0; + } + + if (!res->bitmap) + return (unsigned long)(-ENOSPC); + + p = bitmap_find_next_zero_area(res->bitmap, res->elements, 0, n, 0); + alloc->resource = NULL; + + if (p >= res->elements) + return (unsigned long)(-ENOSPC); + bitmap_set(res->bitmap, p, n); + alloc->resource = res; + alloc->elements = n; + alloc->pos = p; + alloc->type = type; + + return p; +} + +static void ipu_resource_free(struct ipu_resource_alloc *alloc) +{ + if (alloc->elements <= 0) + return; + + if (alloc->type == IPU_RESOURCE_DFM) + *alloc->resource->bitmap &= ~(unsigned long)(alloc->elements); + else + bitmap_clear(alloc->resource->bitmap, alloc->pos, + alloc->elements); + alloc->resource = NULL; +} + +static void ipu_resource_cleanup(struct ipu_resource *res) +{ + kfree(res->bitmap); + res->bitmap = NULL; +} + +/********** IPU PSYS-specific resource handling **********/ + +int ipu_psys_resource_pool_init(struct ipu_psys_resource_pool + *pool) +{ + int i, j, k, ret; + + pool->cells = 0; + + for (i = 0; i < res_defs->num_dev_channels; i++) { + ret = ipu_resource_init(&pool->dev_channels[i], i, + res_defs->dev_channels[i]); + if (ret) + goto error; + } + + for (j = 0; j < res_defs->num_ext_mem_ids; j++) { + ret = ipu_resource_init(&pool->ext_memory[j], j, + res_defs->ext_mem_ids[j]); + if (ret) + goto memory_error; + } + + for (k = 0; k < res_defs->num_dfm_ids; k++) { + ret = ipu_resource_init(&pool->dfms[k], k, res_defs->dfms[k]); + if (ret) + goto dfm_error; + } + + return 0; + +dfm_error: + for (k--; k >= 0; k--) + ipu_resource_cleanup(&pool->dfms[k]); + +memory_error: + for (j--; j >= 0; j--) + ipu_resource_cleanup(&pool->ext_memory[j]); + +error: + for (i--; i >= 0; i--) + ipu_resource_cleanup(&pool->dev_channels[i]); + return ret; +} + + +void ipu_psys_resource_pool_cleanup(struct ipu_psys_resource_pool + *pool) +{ + u32 i; + + for (i = 0; i < res_defs->num_dev_channels; i++) + ipu_resource_cleanup(&pool->dev_channels[i]); + + for (i = 0; i < res_defs->num_ext_mem_ids; i++) + ipu_resource_cleanup(&pool->ext_memory[i]); + + for (i = 0; i < res_defs->num_dfm_ids; i++) + ipu_resource_cleanup(&pool->dfms[i]); +} + +static int ipu_psys_allocate_one_resource(const struct device *dev, + struct ipu_fw_psys_process *process, + struct ipu_resource *resource, + struct ipu_fw_generic_program_manifest *pm, + u32 resource_id, + struct ipu_psys_resource_alloc *alloc) +{ + const u16 resource_req = pm->dev_chn_size[resource_id]; + unsigned long retl; + + if (resource_req <= 0) + return 0; + + if (alloc->resources >= IPU_MAX_RESOURCES) { + dev_err(dev, "out of resource handles\n"); + return -ENOSPC; + } + retl = ipu_resource_alloc + (resource, resource_req, + &alloc->resource_alloc[alloc->resources], + IPU_RESOURCE_DEV_CHN); + if (IS_ERR_VALUE(retl)) { + dev_dbg(dev, "out of device channel resources\n"); + return (int)retl; + } + alloc->resources++; + + return 0; +} + +/* + * ext_mem_type_id is a generic type id for memory (like DMEM, VMEM) + * ext_mem_bank_id is detailed type id for memory (like DMEM0, DMEM1 etc.) + */ +static int ipu_psys_allocate_memory_resource( + const struct device *dev, + struct ipu_fw_psys_process *process, + struct ipu_resource *resource, + struct ipu_fw_generic_program_manifest *pm, + u32 ext_mem_type_id, u32 ext_mem_bank_id, + struct ipu_psys_resource_alloc *alloc) +{ + const u16 memory_resource_req = pm->ext_mem_size[ext_mem_type_id]; + + unsigned long retl; + + if (memory_resource_req <= 0) + return 0; + + if (alloc->resources >= IPU_MAX_RESOURCES) { + dev_err(dev, "out of resource handles\n"); + return -ENOSPC; + } + retl = ipu_resource_alloc + (resource, memory_resource_req, + &alloc->resource_alloc[alloc->resources], + IPU_RESOURCE_EXT_MEM); + if (IS_ERR_VALUE(retl)) { + dev_dbg(dev, "out of memory resources\n"); + return (int)retl; + } + + alloc->resources++; + + return 0; +} + +/* + * Allocate resources for pg from `pool'. Mark the allocated + * resources into `alloc'. Returns 0 on success, -ENOSPC + * if there are no enough resources, in which cases resources + * are not allocated at all, or some other error on other conditions. + */ +int ipu_psys_allocate_resources(const struct device *dev, + struct ipu_fw_psys_process_group *pg, + void *pg_manifest, + struct ipu_psys_resource_alloc + *alloc, struct ipu_psys_resource_pool + *pool) +{ + u32 resid; + u32 mem_type_id; + int ret, i; + u16 *process_offset_table; + u8 processes; + u32 cells = 0; + + if (!pg) + return -EINVAL; + process_offset_table = (u16 *)((u8 *) pg + pg->processes_offset); + processes = pg->process_count; + + for (i = 0; i < processes; i++) { + u32 cell; + struct ipu_fw_psys_process *process = + (struct ipu_fw_psys_process *) + ((char *)pg + process_offset_table[i]); + struct ipu_fw_generic_program_manifest pm; + + memset(&pm, 0, sizeof(pm)); + if (!process) { + dev_err(dev, "can not get process\n"); + ret = -ENOENT; + goto free_out; + } + + ret = ipu_fw_psys_get_program_manifest_by_process(&pm, + pg_manifest, + process); + if (ret < 0) { + dev_err(dev, "can not get manifest\n"); + goto free_out; + } + + if (pm.cell_id == res_defs->num_cells && + pm.cell_type_id == res_defs->num_cells_type) { + dev_dbg(dev, "ignore the cell requirement\n"); + cell = res_defs->num_cells; + } else if ((pm.cell_id != res_defs->num_cells && + pm.cell_type_id == res_defs->num_cells_type)) { + cell = ipu_fw_psys_get_process_cell_id(process, 0); + } else { + /* Find a free cell of desired type */ + u32 type = pm.cell_type_id; + + for (cell = 0; cell < res_defs->num_cells; cell++) + if (res_defs->cells[cell] == type && + ((pool->cells | cells) & (1 << cell)) == 0) + break; + if (cell >= res_defs->num_cells) { + dev_dbg(dev, "no free cells of right type\n"); + ret = -ENOSPC; + goto free_out; + } + ret = ipu_fw_psys_set_process_cell_id(process, 0, cell); + if (ret) + goto free_out; + } + if (cell < res_defs->num_cells) + cells |= 1 << cell; + if (pool->cells & cells) { + dev_dbg(dev, "out of cell resources\n"); + ret = -ENOSPC; + goto free_out; + } + if (pm.dev_chn_size) { + for (resid = 0; resid < res_defs->num_dev_channels; resid++) { + ret = ipu_psys_allocate_one_resource + (dev, process, + &pool->dev_channels[resid], &pm, resid, alloc); + if (ret) + goto free_out; + ret = ipu_fw_psys_set_process_dev_chn_offset(process, resid, + alloc->resource_alloc[alloc->resources - 1].pos); + if (ret) + goto free_out; + } + } + + if (pm.ext_mem_size) { + for (mem_type_id = 0; + mem_type_id < res_defs->num_ext_mem_types; mem_type_id++) { + u32 mem_bank_id = res_defs->num_ext_mem_ids; + + if (cell != res_defs->num_cells) + mem_bank_id = + res_defs->cell_mem[res_defs->cell_mem_row * + cell + mem_type_id]; + if (mem_bank_id == res_defs->num_ext_mem_ids) + continue; + + ret = ipu_psys_allocate_memory_resource + (dev, process, + &pool->ext_memory[mem_bank_id], + &pm, mem_type_id, mem_bank_id, alloc); + if (ret) + goto free_out; + /* no return value check here because fw api will + * do some checks, and would return non-zero + * except mem_type_id == 0. This may be caused by that + * above flow if allocating mem_bank_id is improper + */ + ipu_fw_psys_set_process_ext_mem + (process, mem_type_id, mem_bank_id, + alloc->resource_alloc[alloc->resources - 1].pos); + } + } + } + alloc->cells |= cells; + pool->cells |= cells; + return 0; + +free_out: + for (; i >= 0; i--) { + struct ipu_fw_psys_process *process = + (struct ipu_fw_psys_process *) + ((char *)pg + process_offset_table[i]); + struct ipu_fw_generic_program_manifest pm; + int retval; + + if (!process) + break; + + retval = ipu_fw_psys_get_program_manifest_by_process + (&pm, pg_manifest, process); + if (retval < 0) + break; + if ((pm.cell_id != res_defs->num_cells && + pm.cell_type_id == res_defs->num_cells_type)) + continue; + /* no return value check here because if finding free cell + * failed, process cell would not set then calling clear_cell + * will return non-zero. + */ + ipu_fw_psys_clear_process_cell(process); + } + dev_dbg(dev, "failed to allocate resources, ret %d\n", ret); + ipu_psys_free_resources(alloc, pool); + return ret; +} + +int ipu_psys_move_resources(const struct device *dev, + struct ipu_psys_resource_alloc *alloc, + struct ipu_psys_resource_pool + *source_pool, struct ipu_psys_resource_pool + *target_pool) +{ + int i; + + if (target_pool->cells & alloc->cells) { + dev_dbg(dev, "out of cell resources\n"); + return -ENOSPC; + } + + for (i = 0; i < alloc->resources; i++) { + unsigned long bitmap = 0; + unsigned int id = alloc->resource_alloc[i].resource->id; + unsigned long fbit, end; + + switch (alloc->resource_alloc[i].type) { + case IPU_RESOURCE_DEV_CHN: + bitmap_set(&bitmap, alloc->resource_alloc[i].pos, + alloc->resource_alloc[i].elements); + if (*target_pool->dev_channels[id].bitmap & bitmap) + return -ENOSPC; + break; + case IPU_RESOURCE_EXT_MEM: + end = alloc->resource_alloc[i].elements + + alloc->resource_alloc[i].pos; + + fbit = find_next_bit(target_pool->ext_memory[id].bitmap, + end, alloc->resource_alloc[i].pos); + /* if find_next_bit returns "end" it didn't find 1bit */ + if (end != fbit) + return -ENOSPC; + break; + case IPU_RESOURCE_DFM: + bitmap = alloc->resource_alloc[i].elements; + if (*target_pool->dfms[id].bitmap & bitmap) + return -ENOSPC; + break; + default: + dev_err(dev, "Illegal resource type\n"); + return -EINVAL; + } + } + + for (i = 0; i < alloc->resources; i++) { + u32 id = alloc->resource_alloc[i].resource->id; + + switch (alloc->resource_alloc[i].type) { + case IPU_RESOURCE_DEV_CHN: + bitmap_set(target_pool->dev_channels[id].bitmap, + alloc->resource_alloc[i].pos, + alloc->resource_alloc[i].elements); + ipu_resource_free(&alloc->resource_alloc[i]); + alloc->resource_alloc[i].resource = + &target_pool->dev_channels[id]; + break; + case IPU_RESOURCE_EXT_MEM: + bitmap_set(target_pool->ext_memory[id].bitmap, + alloc->resource_alloc[i].pos, + alloc->resource_alloc[i].elements); + ipu_resource_free(&alloc->resource_alloc[i]); + alloc->resource_alloc[i].resource = + &target_pool->ext_memory[id]; + break; + case IPU_RESOURCE_DFM: + *target_pool->dfms[id].bitmap |= + alloc->resource_alloc[i].elements; + *alloc->resource_alloc[i].resource->bitmap &= + ~(alloc->resource_alloc[i].elements); + alloc->resource_alloc[i].resource = + &target_pool->dfms[id]; + break; + default: + /* + * Just keep compiler happy. This case failed already + * in above loop. + */ + break; + } + } + + target_pool->cells |= alloc->cells; + source_pool->cells &= ~alloc->cells; + + return 0; +} + +/* Free resources marked in `alloc' from `resources' */ +void ipu_psys_free_resources(struct ipu_psys_resource_alloc + *alloc, struct ipu_psys_resource_pool *pool) +{ + unsigned int i; + + pool->cells &= ~alloc->cells; + alloc->cells = 0; + for (i = 0; i < alloc->resources; i++) + ipu_resource_free(&alloc->resource_alloc[i]); + alloc->resources = 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4.c b/drivers/media/pci/intel/ipu4/ipu4.c new file mode 100644 index 000000000000..c3615d225431 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include + +#include "ipu.h" +#include "ipu-cpd.h" +#include "ipu-isys.h" +#include "ipu-buttress.h" +#include "ipu-psys.h" +#include "ipu-platform.h" +#include "ipu-platform-regs.h" +#include "ipu-platform-buttress-regs.h" + +#ifdef CONFIG_VIDEO_INTEL_IPU4 +static struct ipu_receiver_electrical_params ipu4_ev_params[] = { + {0, 1500000000ul / 2, IPU_PCI_ID, IPU_HW_BXT_P_B1_REV, + .rcomp_val_combo = 11, + .rcomp_val_legacy = 11, + .ports[0].crc_val = 18, + .ports[0].drc_val = 29, + .ports[0].drc_val_combined = 29, + .ports[0].ctle_val = 4, + .ports[1].crc_val = 18, + .ports[1].drc_val = 29, + .ports[1].drc_val_combined = 31, + .ports[1].ctle_val = 4 + }, + {0, 1500000000ul / 2, IPU_PCI_ID, IPU_HW_BXT_P_D0_REV, + .rcomp_val_combo = 11, + .rcomp_val_legacy = 11, + .ports[0].crc_val = 18, + .ports[0].drc_val = 29, + .ports[0].drc_val_combined = 29, + .ports[0].ctle_val = 4, + .ports[1].crc_val = 18, + .ports[1].drc_val = 29, + .ports[1].drc_val_combined = 31, + .ports[1].ctle_val = 4 + }, + {0, 1500000000ul / 2, IPU_PCI_ID, IPU_HW_BXT_P_E0_REV, + .rcomp_val_combo = 11, + .rcomp_val_legacy = 11, + .ports[0].crc_val = 18, + .ports[0].drc_val = 29, + .ports[0].drc_val_combined = 29, + .ports[0].ctle_val = 4, + .ports[1].crc_val = 18, + .ports[1].drc_val = 29, + .ports[1].drc_val_combined = 31, + .ports[1].ctle_val = 4 + }, + {}, +}; + +static unsigned int ipu4_csi_offsets[] = { + 0x64000, 0x65000, 0x66000, 0x67000, 0x6C000, 0x6C800 +}; + +static unsigned char ipu4_csi_evlanecombine[] = { + 0, 0, 0, 0, 2, 0 +}; + +static unsigned int ipu4_tpg_offsets[] = { + IPU_TPG0_ADDR_OFFSET, + IPU_TPG1_ADDR_OFFSET +}; + +static unsigned int ipu4_tpg_sels[] = { + IPU_GPOFFSET + IPU_GPREG_MIPI_PKT_GEN0_SEL, + IPU_COMBO_GPOFFSET + IPU_GPREG_MIPI_PKT_GEN1_SEL +}; + +const struct ipu_isys_internal_pdata isys_ipdata = { + .csi2 = { + .nports = ARRAY_SIZE(ipu4_csi_offsets), + .offsets = ipu4_csi_offsets, + .evparams = ipu4_ev_params, + .evlanecombine = ipu4_csi_evlanecombine, + .evsetmask0 = 1 << 4, /* CSI port 4 */ + .evsetmask1 = 1 << 5, /* CSI port 5 */ + }, + .tpg = { + .ntpgs = ARRAY_SIZE(ipu4_tpg_offsets), + .offsets = ipu4_tpg_offsets, + .sels = ipu4_tpg_sels, + }, + .hw_variant = { + .offset = IPU_ISYS_OFFSET, + .nr_mmus = 2, + .mmu_hw = { + { + .offset = IPU_ISYS_IOMMU0_OFFSET, + .info_bits = + IPU_INFO_REQUEST_DESTINATION_PRIMARY, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = true, + }, + { + .offset = IPU_ISYS_IOMMU1_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 8, 16, 16, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8 + }, + .l1_zlw_en = { + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 + }, + .l1_zlw_1d_mode = { + 0, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + .l1_ins_zlw_ahead_pages = { + 0, 3, 3, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + }, + .dmem_offset = IPU_ISYS_DMEM_OFFSET, + .spc_offset = IPU_ISYS_SPC_OFFSET, + }, + .num_parallel_streams = IPU_ISYS_NUM_STREAMS, + .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, +}; + +const struct ipu_psys_internal_pdata psys_ipdata = { + .hw_variant = { + .offset = IPU_PSYS_OFFSET, + .nr_mmus = 3, + .mmu_hw = { + { + .offset = IPU_PSYS_IOMMU0_OFFSET, + .info_bits = + IPU_INFO_REQUEST_DESTINATION_PRIMARY, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = true, + }, + { + .offset = IPU_PSYS_IOMMU1_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 0, 0, 0, 0, 10, 8, 10, 8, 0, + 4, 4, 12, 0, 0, 0, 8 + }, + .l1_zlw_en = { + 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 1, 0, 0, 0, 0 + }, + .l1_zlw_1d_mode = { + 0, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 0 + }, + .l1_ins_zlw_ahead_pages = { + 0, 0, 0, 0, 3, 3, + 3, 3, 0, 3, 1, 3, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU_PSYS_IOMMU1R_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 16, 12, 12, 16 + }, + .l1_zlw_en = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 1, 1, 1, 1 + }, + .l1_zlw_1d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 1 + }, + .l1_ins_zlw_ahead_pages = { + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + }, + .dmem_offset = IPU_PSYS_DMEM_OFFSET, + .spc_offset = IPU_PSYS_SPC_OFFSET, + }, +}; + +/* + * This is meant only as reference for initialising the buttress control, + * because the different HW stepping can have different initial values + * + * There is a HW bug and IS_PWR and PS_PWR fields cannot be used to + * detect if power on/off is ready. Using IS_PWR_FSM and PS_PWR_FSM + * fields instead. + */ +const struct ipu_buttress_ctrl isys_buttress_ctrl = { + .divisor = IS_FREQ_CTL_DIVISOR, + .qos_floor = 0, + .freq_ctl = BUTTRESS_REG_IS_FREQ_CTL, + .pwr_sts_shift = BUTTRESS_PWR_STATE_IS_PWR_FSM_SHIFT, + .pwr_sts_mask = BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK, + .pwr_sts_on = BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY, + .pwr_sts_off = BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE, +}; + +/* + * This is meant only as reference for initialising the buttress control, + * because the different HW stepping can have different initial values + */ + +const struct ipu_buttress_ctrl psys_buttress_ctrl = { + .divisor = PS_FREQ_CTL_DEFAULT_RATIO, + .qos_floor = PS_FREQ_CTL_DEFAULT_RATIO, + .freq_ctl = BUTTRESS_REG_PS_FREQ_CTL, + .pwr_sts_shift = BUTTRESS_PWR_STATE_PS_PWR_FSM_SHIFT, + .pwr_sts_mask = BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK, + .pwr_sts_on = BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP, + .pwr_sts_off = BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE, +}; +#endif + +#ifdef CONFIG_VIDEO_INTEL_IPU4P + +/* + * ipu4p available hw ports start from sip0 port3 + * available ports are: + * s0p3, s1p0, s1p1, s1p2, s1p3 + */ +static unsigned int ipu4p_csi_offsets[] = { + 0x64300, 0x6c000, 0x6c100, 0x6c200, 0x6c300 +}; + +static unsigned char ipu4p_csi_evlanecombine[] = { + 0, 0, 0, 0, 0, 0 +}; + +static unsigned int ipu4p_tpg_offsets[] = { + IPU_TPG0_ADDR_OFFSET, + IPU_TPG1_ADDR_OFFSET +}; + +static unsigned int ipu4p_tpg_sels[] = { + IPU_GPOFFSET + IPU_GPREG_MIPI_PKT_GEN0_SEL, + IPU_COMBO_GPOFFSET + IPU_GPREG_MIPI_PKT_GEN1_SEL +}; + +const struct ipu_isys_internal_pdata isys_ipdata = { + .csi2 = { + .nports = ARRAY_SIZE(ipu4p_csi_offsets), + .offsets = ipu4p_csi_offsets, + .evlanecombine = ipu4p_csi_evlanecombine, + }, + .tpg = { + .ntpgs = ARRAY_SIZE(ipu4p_tpg_offsets), + .offsets = ipu4p_tpg_offsets, + .sels = ipu4p_tpg_sels, + }, + .hw_variant = { + .offset = IPU_ISYS_OFFSET, + .nr_mmus = 2, + .mmu_hw = { + { + .offset = IPU_ISYS_IOMMU0_OFFSET, + .info_bits = + IPU_INFO_REQUEST_DESTINATION_PRIMARY, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = true, + }, + { + .offset = IPU_ISYS_IOMMU1_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 5, 16, 6, 6, 6, 6, 6, 8, 0, + 0, 0, 0, 0, 0, 0, 5 + }, + .l1_zlw_en = { + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0 + }, + .l1_zlw_1d_mode = { + 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .l1_ins_zlw_ahead_pages = { + 0, 3, 3, 3, 3, 3, + 3, 3, 0, 0, 0, 0, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + }, + .dmem_offset = IPU_ISYS_DMEM_OFFSET, + .spc_offset = IPU_ISYS_SPC_OFFSET, + }, + .num_parallel_streams = IPU_ISYS_NUM_STREAMS, + .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, +}; + +const struct ipu_psys_internal_pdata psys_ipdata = { + .hw_variant = { + .offset = IPU_PSYS_OFFSET, + .nr_mmus = 3, + .mmu_hw = { + { + .offset = IPU_PSYS_IOMMU0_OFFSET, + .info_bits = + IPU_INFO_REQUEST_DESTINATION_PRIMARY, + .nr_l1streams = 0, + .nr_l2streams = 0, + .insert_read_before_invalidate = true, + }, + { + .offset = IPU_PSYS_IOMMU1_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 2, 5, 4, 2, 2, 10, 5, 16, 10, + 5, 0, 0, 0, 0, 0, 3 + }, + .l1_zlw_en = { + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0 + }, + .l1_zlw_1d_mode = { + 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0 + }, + .l1_ins_zlw_ahead_pages = { + 0, 0, 3, 3, 3, 3, + 3, 3, 3, 3, 0, 0, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + { + .offset = IPU_PSYS_IOMMU1R_OFFSET, + .info_bits = IPU_INFO_STREAM_ID_SET(0), + .nr_l1streams = IPU_MMU_MAX_TLB_L1_STREAMS, + .l1_block_sz = { + 2, 6, 5, 16, 16, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 3 + }, + .l1_zlw_en = { + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 + }, + .l1_zlw_1d_mode = { + 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .l1_ins_zlw_ahead_pages = { + 0, 0, 3, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + }, + .l1_zlw_2d_mode = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + .nr_l2streams = IPU_MMU_MAX_TLB_L2_STREAMS, + .l2_block_sz = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2 + }, + .insert_read_before_invalidate = false, + .zlw_invalidate = true, + .l1_stream_id_reg_offset = + IPU_MMU_L1_STREAM_ID_REG_OFFSET, + .l2_stream_id_reg_offset = + IPU_MMU_L2_STREAM_ID_REG_OFFSET, + }, + }, + .dmem_offset = IPU_PSYS_DMEM_OFFSET, + .spc_offset = IPU_PSYS_SPC_OFFSET, + }, +}; + +const struct ipu_buttress_ctrl isys_buttress_ctrl = { + .divisor = IS_FREQ_CTL_DIVISOR, + .qos_floor = 0, + .ovrd = 0, + .freq_ctl = BUTTRESS_REG_IS_FREQ_CTL, + .divisor_shift = BUTTRESS_REG_IS_FREQ_CTL_RATIO_SHIFT, + .pwr_sts_shift = BUTTRESS_PWR_STATE_IS_PWR_FSM_SHIFT, + .pwr_sts_mask = BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK, + .pwr_sts_on = BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY, + .pwr_sts_off = BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE, +}; + +const struct ipu_buttress_ctrl psys_buttress_ctrl = { + .divisor = PS_FREQ_CTL_DEFAULT_RATIO, + .qos_floor = PS_FREQ_CTL_DEFAULT_RATIO, + .ovrd = 1, + .freq_ctl = BUTTRESS_REG_PS_FREQ_CTL, + .divisor_shift = BUTTRESS_REG_PS_FREQ_CTL_RATIO_SHIFT, + .ovrd_shift = BUTTRESS_REG_PS_FREQ_CTL_OVRD_SHIFT, + .pwr_sts_shift = BUTTRESS_PWR_STATE_PS_PWR_FSM_SHIFT, + .pwr_sts_mask = BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK, + .pwr_sts_on = BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP, + .pwr_sts_off = BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE, +}; +#endif + +void ipu_configure_spc(struct ipu_device *isp, + const struct ipu_hw_variants *hw_variant, + int pkg_dir_idx, void __iomem *base, u64 *pkg_dir, + dma_addr_t pkg_dir_dma_addr) +{ + u32 val; + void __iomem *dmem_base = base + hw_variant->dmem_offset; + void __iomem *spc_regs_base = base + hw_variant->spc_offset; + + val = readl(spc_regs_base + IPU_PSYS_REG_SPC_STATUS_CTRL); + val |= IPU_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; + writel(val, spc_regs_base + IPU_PSYS_REG_SPC_STATUS_CTRL); + + if (isp->secure_mode) { + writel(IPU_PKG_DIR_IMR_OFFSET, dmem_base); + } else { + u32 server_addr; + + server_addr = ipu_cpd_pkg_dir_get_address(pkg_dir, pkg_dir_idx); + + writel(server_addr + + ipu_cpd_get_pg_icache_base(isp, pkg_dir_idx, + isp->cpd_fw->data, + isp->cpd_fw->size), + spc_regs_base + IPU_PSYS_REG_SPC_ICACHE_BASE); + writel(ipu_cpd_get_pg_entry_point(isp, pkg_dir_idx, + isp->cpd_fw->data, + isp->cpd_fw->size), + spc_regs_base + IPU_PSYS_REG_SPC_START_PC); + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + spc_regs_base + + IPU_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER); + writel(pkg_dir_dma_addr, dmem_base); + } +} +EXPORT_SYMBOL(ipu_configure_spc); + +int ipu_buttress_psys_freq_get(void *data, u64 *val) +{ + struct ipu_device *isp = data; + u32 reg_val, ratio; + int rval; + + rval = pm_runtime_get_sync(&isp->psys->dev); + if (rval < 0) { + pm_runtime_put(&isp->psys->dev); + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); + return rval; + } + + reg_val = readl(isp->base + BUTTRESS_REG_PS_FREQ_CAPABILITIES); + + pm_runtime_put(&isp->psys->dev); + + ratio = (reg_val & + BUTTRESS_PS_FREQ_CAPABILITIES_LAST_RESOLVED_RATIO_MASK) >> + BUTTRESS_PS_FREQ_CAPABILITIES_LAST_RESOLVED_RATIO_SHIFT; + + *val = BUTTRESS_PS_FREQ_STEP * ratio; + + return 0; +} + +int ipu_buttress_isys_freq_get(void *data, u64 *val) +{ + struct ipu_device *isp = data; + u32 reg_val; + int rval; + + rval = pm_runtime_get_sync(&isp->isys->dev); + if (rval < 0) { + pm_runtime_put(&isp->isys->dev); + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); + return rval; + } + + reg_val = readl(isp->base + BUTTRESS_REG_IS_FREQ_CTL); + + pm_runtime_put(&isp->isys->dev); + + /* Input system frequency specified as 1600MHz/divisor */ + *val = 1600 / (reg_val & BUTTRESS_IS_FREQ_CTL_DIVISOR_MASK); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_inc b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_inc new file mode 100644 index 000000000000..90a2ab46510c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_inc @@ -0,0 +1,26 @@ +IPU_ISYSLIB_INC = \ + -I$(IPU_ISYSLIB_ROOT)/buffer/interface \ + -I$(IPU_ISYSLIB_ROOT)/cell/interface \ + -I$(IPU_ISYSLIB_ROOT)/cell/src \ + -I$(IPU_ISYSLIB_ROOT)/device_access/interface \ + -I$(IPU_ISYSLIB_ROOT)/device_access/src \ + -I$(IPU_ISYSLIB_ROOT)/devices \ + -I$(IPU_ISYSLIB_ROOT)/devices/interface \ + -I$(IPU_ISYSLIB_ROOT)/devices/isys/cnlB0 \ + -I$(IPU_ISYSLIB_ROOT)/devices/src \ + -I$(IPU_ISYSLIB_ROOT)/fw_abi_common_types \ + -I$(IPU_ISYSLIB_ROOT)/fw_abi_common_types/cpu \ + -I$(IPU_ISYSLIB_ROOT)/isysapi/interface \ + -I$(IPU_ISYSLIB_ROOT)/pkg_dir/interface \ + -I$(IPU_ISYSLIB_ROOT)/pkg_dir/src \ + -I$(IPU_ISYSLIB_ROOT)/port/interface \ + -I$(IPU_ISYSLIB_ROOT)/reg_dump/src/isys/cnlB0_gen_reg_dump \ + -I$(IPU_ISYSLIB_ROOT)/regmem/interface \ + -I$(IPU_ISYSLIB_ROOT)/regmem/src \ + -I$(IPU_ISYSLIB_ROOT)/support \ + -I$(IPU_ISYSLIB_ROOT)/syscom/interface \ + -I$(IPU_ISYSLIB_ROOT)/syscom/src \ + -I$(IPU_ISYSLIB_ROOT)/trace/interface \ + -I$(IPU_ISYSLIB_ROOT)/utils/system_defs/ \ + -I$(IPU_ISYSLIB_ROOT)/vied \ + -I$(IPU_ISYSLIB_ROOT)/vied/vied/ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_src b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_src new file mode 100644 index 000000000000..c20760bdb5f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4pisys_src @@ -0,0 +1,19 @@ +IPU_ISYSLIB_SRC = \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_private.o \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_public.o \ + $(IPU_ISYSLIB_ROOT_REL)/isysapi/src/ia_css_isys_public_trace.o + +ifeq ($(CONFIG_VIDEO_INTEL_IPU), m) +IPU_ISYSLIB_SRC += \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/buffer_access.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_input_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_output_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_shared_buffer.o \ + $(IPU_ISYSLIB_ROOT_REL)/pkg_dir/src/ia_css_pkg_dir.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/queue.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/recv_port.o \ + $(IPU_ISYSLIB_ROOT_REL)/port/src/send_port.o \ + $(IPU_ISYSLIB_ROOT_REL)/reg_dump/src/reg_dump_generic_bridge.o \ + $(IPU_ISYSLIB_ROOT_REL)/syscom/src/ia_css_syscom.o +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_inc b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_inc new file mode 100644 index 000000000000..fb01678242ee --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_inc @@ -0,0 +1,52 @@ +IPU_PSYSLIB_INC = \ + -I$(IPU_PSYSLIB_ROOT)/buffer/interface \ + -I$(IPU_PSYSLIB_ROOT)/cell/interface \ + -I$(IPU_PSYSLIB_ROOT)/cell/src \ + -I$(IPU_PSYSLIB_ROOT)/client_pkg/interface \ + -I$(IPU_PSYSLIB_ROOT)/client_pkg/src \ + -I$(IPU_PSYSLIB_ROOT)/cpd/ \ + -I$(IPU_PSYSLIB_ROOT)/cpd/cpd_component/interface \ + -I$(IPU_PSYSLIB_ROOT)/cpd/cpd_metadata/interface \ + -I$(IPU_PSYSLIB_ROOT)/device_access/interface \ + -I$(IPU_PSYSLIB_ROOT)/device_access/src \ + -I$(IPU_PSYSLIB_ROOT)/devices \ + -I$(IPU_PSYSLIB_ROOT)/devices/interface \ + -I$(IPU_PSYSLIB_ROOT)/devices/psys/cnlB0 \ + -I$(IPU_PSYSLIB_ROOT)/devices/src \ + -I$(IPU_PSYSLIB_ROOT)/fw_abi_common_types \ + -I$(IPU_PSYSLIB_ROOT)/fw_abi_common_types/cpu \ + -I$(IPU_PSYSLIB_ROOT)/pkg_dir/interface \ + -I$(IPU_PSYSLIB_ROOT)/pkg_dir/src \ + -I$(IPU_PSYSLIB_ROOT)/port/interface \ + -I$(IPU_PSYSLIB_ROOT)/psys_private_pg/interface \ + -I$(IPU_PSYSLIB_ROOT)/psys_server/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/data/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/data/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/device/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/device/interface/cnlB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/dynamic/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/dynamic/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/kernel/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/param/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/param/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/psys_server_manifest/cnlB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/resource_model/cnlB0 \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/sim/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/sim/src \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/static/interface \ + -I$(IPU_PSYSLIB_ROOT)/psysapi/static/src \ + -I$(IPU_PSYSLIB_ROOT)/reg_dump/src/psys/cnlB0_gen_reg_dump \ + -I$(IPU_PSYSLIB_ROOT)/regmem/interface \ + -I$(IPU_PSYSLIB_ROOT)/regmem/src \ + -I$(IPU_PSYSLIB_ROOT)/routing_bitmap/interface \ + -I$(IPU_PSYSLIB_ROOT)/routing_bitmap/src \ + -I$(IPU_PSYSLIB_ROOT)/support \ + -I$(IPU_PSYSLIB_ROOT)/syscom/interface \ + -I$(IPU_PSYSLIB_ROOT)/syscom/src \ + -I$(IPU_PSYSLIB_ROOT)/trace/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied \ + -I$(IPU_PSYSLIB_ROOT)/vied/vied/ \ + -I$(IPU_PSYSLIB_ROOT)/vied_nci_acb/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied_parameters/interface \ + -I$(IPU_PSYSLIB_ROOT)/vied_parameters/src \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_src b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_src new file mode 100644 index 000000000000..3ed88d455bab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.ipu4ppsys_src @@ -0,0 +1,32 @@ +IPU_PSYSLIB_SRC = \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/buffer_access.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_input_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_output_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/buffer/src/cpu/ia_css_shared_buffer.o \ + $(IPU_PSYSLIB_ROOT_REL)/client_pkg/src/ia_css_client_pkg.o \ + $(IPU_PSYSLIB_ROOT_REL)/pkg_dir/src/ia_css_pkg_dir.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/queue.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/recv_port.o \ + $(IPU_PSYSLIB_ROOT_REL)/port/src/send_port.o \ + $(IPU_PSYSLIB_ROOT_REL)/psys_server/src/bxt_spctrl_process_group_cmd_impl.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/data/src/ia_css_program_group_data.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/device/src/ia_css_psys_device.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_buffer_set.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_process.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_process_group.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/dynamic/src/ia_css_psys_terminal.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/kernel/src/ia_css_kernel_bitmap.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/param/src/ia_css_program_group_param.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/sim/src/vied_nci_psys_system.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_program_group_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_program_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/psysapi/static/src/ia_css_psys_terminal_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/reg_dump/src/reg_dump_generic_bridge.o \ + $(IPU_PSYSLIB_ROOT_REL)/routing_bitmap/src/ia_css_rbm.o \ + $(IPU_PSYSLIB_ROOT_REL)/routing_bitmap/src/ia_css_rbm_manifest.o \ + $(IPU_PSYSLIB_ROOT_REL)/syscom/src/ia_css_syscom.o \ + $(IPU_PSYSLIB_ROOT_REL)/vied_parameters/src/ia_css_terminal.o \ + $(IPU_PSYSLIB_ROOT_REL)/vied_parameters/src/ia_css_terminal_manifest.o \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.isyslib b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.isyslib new file mode 100644 index 000000000000..f0b540d78d2b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.isyslib @@ -0,0 +1,47 @@ +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +PROGRAMS = isys_fw +SYSTEM = input_system_system +IPU_ISYSLIB_ROOT_REL = ipu4p-css/lib2600 +IPU_ISYSLIB_ROOT = $(srcpath)/$(src)/$(IPU_ISYSLIB_ROOT_REL) + +include $(srcpath)/$(src)/ipu4p-css/Makefile.ipu4pisys_inc +include $(srcpath)/$(src)/ipu4p-css/Makefile.ipu4pisys_src + +# +# copy wrapper here only for isys usage, psys would use the original one +# +$(shell cp -f $(srcpath)/$(src)/../ipu-wrapper.c $(srcpath)/$(src)/ipu4p-css/ipu-wrapper.c) + +intel-ipu4p-isys-csslib-objs := \ + ipu4p-css/libintel-ipu4p.o \ + $(IPU_ISYSLIB_SRC) + +ifeq ($(CONFIG_VIDEO_INTEL_IPU), m) +intel-ipu4p-isys-csslib-objs += ipu4p-css/ipu-wrapper.o +endif +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p-isys-csslib.o + +INCLUDES := -I$(srcpath)/$(src)/$(IPU_ISYSLIB_ROOT_REL) \ + -I$(srcpath)/$(src) \ + $(IPU_ISYSLIB_INC) + +DEFINES:= -D__HOST__ -D__KERNEL__ -DISYS_FPGA -DPSYS_FPGA + +DEFINES += -DSSID=1 +DEFINES += -DMMID=1 +DEFINES += -DPROGNAME=isys_fw +DEFINES += -DPROGMAP=\"isys_fw.map.h\" +DEFINES += -DSUBSYSTEM_INCLUDE=\ +DEFINES += -DCELL=input_system_unis_logic_sp_control_tile_sp +DEFINES += -DSPMAIN=isys_fw +DEFINES += -DRUN_INTEGRATION +DEFINES += -DDEBUG_SP_NCI +DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL=1 +DEFINES += -DHRT_ON_VIED_SUBSYSTEM_ACCESS=0 +DEFINES += -DHRT_USE_VIR_ADDRS +DEFINES += -DHRT_HW + +ccflags-y += $(INCLUDES) $(DEFINES) -fno-common diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.psyslib b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.psyslib new file mode 100644 index 000000000000..fe954d8e2e62 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/Makefile.psyslib @@ -0,0 +1,14 @@ +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +# note: this file only defines INCLUDES paths for lib2600psys +include $(srcpath)/$(src)/ipu4p-css/Makefile.ipu4ppsys_inc + +IPU_PSYSLIB_ROOT = $(srcpath)/$(src)/ipu4p-css/lib2600psys/lib +HOST_DEFINES += -DPSYS_SERVER_ON_SPC +HOST_DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL=1 + +ccflags-y += $(IPU_PSYSLIB_INC) $(HOST_DEFINES) + +obj-$(CONFIG_VIDEO_INTEL_IPU) += ipu4p-css/lib2600psys/ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4p-css/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/buffer.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/buffer.mk new file mode 100644 index 000000000000..c00a1133b440 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/buffer.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is BUFFER + +ifdef _H_BUFFER_MK +$(error ERROR: buffer.mk included multiple times, please check makefile) +else +_H_BUFFER_MK=1 +endif + +BUFFER_DIR=$${MODULES_DIR}/buffer + +BUFFER_INTERFACE=$(BUFFER_DIR)/interface +BUFFER_SOURCES_CPU=$(BUFFER_DIR)/src/cpu +BUFFER_SOURCES_CSS=$(BUFFER_DIR)/src/css + +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_output_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_input_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_shared_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/buffer_access.c +BUFFER_HOST_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_HOST_CPPFLAGS += -I$${MODULES_DIR}/support + +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_input_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_output_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_shared_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/buffer_access.c + +BUFFER_FW_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_FW_CPPFLAGS += -I$${MODULES_DIR}/support diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_access.h new file mode 100644 index 000000000000..e5fe647742c9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_access.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_ACCESS_H +#define __BUFFER_ACCESS_H + +#include "buffer_type.h" +/* #def to keep consistent the buffer load interfaces for host and css */ +#define IDM 0 + +void +buffer_load( + buffer_address address, + void *data, + unsigned int size, + unsigned int mm_id); + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int size, + unsigned int mm_id); + +#endif /* __BUFFER_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_type.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_type.h new file mode 100644 index 000000000000..de51f2394158 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/buffer_type.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_TYPE_H +#define __BUFFER_TYPE_H + +/* portable access to buffers in DDR */ + +#ifdef __VIED_CELL +typedef unsigned int buffer_address; +#else +/* workaround needed because shared_memory_access.h uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_access.h" +typedef host_virtual_address_t buffer_address; +#endif + +#endif /* __BUFFER_TYPE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_buffer_address.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_buffer_address.h new file mode 100644 index 000000000000..137bfb1fda16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_buffer_address.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_ADDRESS_H +#define __IA_CSS_BUFFER_ADDRESS_H + +#include "type_support.h" + +typedef uint32_t ia_css_buffer_address; /* CSS virtual address */ + +#define ia_css_buffer_address_null ((ia_css_buffer_address)0) + +#endif /* __IA_CSS_BUFFER_ADDRESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer.h new file mode 100644 index 000000000000..4e92e35b6184 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_H +#define __IA_CSS_INPUT_BUFFER_H + + +/* Input Buffers */ + +/* A CSS input buffer is a buffer in DDR that can be written by the CPU, + * and that can be read by CSS hardware, after the buffer has been handed over. + * Examples: command buffer, input frame buffer, parameter buffer + * An input buffer must be mapped into the CPU address space before it can be + * written by the CPU. + * After mapping, writing, and unmapping, the buffer can be handed over to the + * firmware. An input buffer is handed over to the CSS by mapping it to the + * CSS address space (by the CPU), and by passing the resulting CSS (virtial) + * address of the input buffer to the DA CSS hardware. + * The firmware can read from an input buffer as soon as it has been received + * CSS virtual address. + * The firmware should not write into an input buffer. + * The firmware hands over the input buffer (back to the CPU) by sending the + * buffer handle via a response. The host should unmap the buffer, + * before reusing it. + * The firmware should not read from the input buffer after returning the + * buffer handle to the CPU. + * + * A buffer may be pre-mapped to the CPU and/or to the CSS upon allocation, + * depending on the allocator's preference. In case of pre-mapped buffers, + * the map and unmap functions will only manage read and write access. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_input_buffer; /* input buffer handle */ +typedef void *ia_css_input_buffer_cpu_address; /* CPU virtual address */ +/* CSS virtual address */ +typedef ia_css_buffer_address ia_css_input_buffer_css_address; + +#endif /* __IA_CSS_INPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h new file mode 100644 index 000000000000..d3d01353ce43 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_input_buffer_cpu.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_CPU_H +#define __IA_CSS_INPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_input_buffer.h" + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b); + + +#endif /* __IA_CSS_INPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer.h new file mode 100644 index 000000000000..2c310ea92c6a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer.h @@ -0,0 +1,30 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_H +#define __IA_CSS_OUTPUT_BUFFER_H + +/* Output Buffers */ +/* A CSS output buffer a buffer in DDR that can be written by CSS hardware + * and that can be read by the host, after the buffer has been handed over + * Examples: output frame buffer + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_output_buffer; +typedef void *ia_css_output_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_output_buffer_css_address; + +#endif /* __IA_CSS_OUTPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h new file mode 100644 index 000000000000..0299fc3b7eb6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_output_buffer_cpu.h @@ -0,0 +1,48 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_CPU_H +#define __IA_CSS_OUTPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_output_buffer.h" + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b); +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b); + + +#endif /* __IA_CSS_OUTPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_return_token.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_return_token.h new file mode 100644 index 000000000000..440161d2f32b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_return_token.h @@ -0,0 +1,54 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RETURN_TOKEN_H +#define __IA_CSS_RETURN_TOKEN_H + +#include "storage_class.h" +#include "assert_support.h" /* For CT_ASSERT */ + +/* ia_css_return_token: data item of exacly 8 bytes (64 bits) + * which can be used to pass a return token back to the host +*/ +typedef unsigned long long ia_css_return_token; + +STORAGE_CLASS_INLINE void +ia_css_return_token_copy(ia_css_return_token *to, + const ia_css_return_token *from) +{ + /* copy a return token on VIED processor */ + int *dst = (int *)to; + int *src = (int *)from; + + dst[0] = src[0]; + dst[1] = src[1]; +} + +STORAGE_CLASS_INLINE void +ia_css_return_token_zero(ia_css_return_token *to) +{ + /* zero return token on VIED processor */ + int *dst = (int *)to; + + dst[0] = 0; + dst[1] = 0; +} + +STORAGE_CLASS_INLINE void _check_return_token_size(void) +{ + CT_ASSERT(sizeof(int) == 4); + CT_ASSERT(sizeof(ia_css_return_token) == 8); +} + +#endif /* __IA_CSS_RETURN_TOKEN_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer.h new file mode 100644 index 000000000000..558ec679f98a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_H +#define __IA_CSS_SHARED_BUFFER_H + +/* Shared Buffers */ +/* A CSS shared buffer is a buffer in DDR that can be read and written by the + * CPU and CSS. + * Both the CPU and CSS can have the buffer mapped simultaneously. + * Access rights are not managed by this interface, this could be done by means + * the read and write pointer of a queue, for example. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_shared_buffer; +typedef void *ia_css_shared_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_shared_buffer_css_address; + +#endif /* __IA_CSS_SHARED_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h new file mode 100644 index 000000000000..ff62914f99dc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/interface/ia_css_shared_buffer_cpu.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_CPU_H +#define __IA_CSS_SHARED_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_shared_buffer.h" + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b); + +#endif /* __IA_CSS_SHARED_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/buffer_access.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/buffer_access.c new file mode 100644 index 000000000000..f0c617fe501a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/buffer_access.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* implementation of buffer access from the CPU */ +/* using shared_memory interface */ + +#include "buffer_access.h" +#include "vied/shared_memory_access.h" + +void +buffer_load( + buffer_address address, + void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_load(mm_id, address, data, bytes); +} + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_store(mm_id, address, data, bytes); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.c new file mode 100644 index 000000000000..146d4109de44 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.c @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* provided interface */ +#include "ia_css_buffer.h" + +/* used interfaces */ +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + +ia_css_buffer_t +ia_css_buffer_alloc(vied_subsystem_t sid, vied_memory_t mid, unsigned int size) +{ + ia_css_buffer_t b; + + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + + b->css_address = shared_memory_map(sid, mid, b->mem); + b->size = size; + return b; +} + + +void +ia_css_buffer_free(vied_subsystem_t sid, vied_memory_t mid, ia_css_buffer_t b) +{ + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.h new file mode 100644 index 000000000000..0f99a06e9a89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_buffer.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_H +#define __IA_CSS_BUFFER_H + +/* workaround: needed because uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_map.h" + +typedef enum { + buffer_unmapped, /* buffer is not accessible by cpu, nor css */ + buffer_write, /* output buffer: css has write access */ + /* input buffer: cpu has write access */ + buffer_read, /* input buffer: css has read access */ + /* output buffer: cpu has read access */ + buffer_cpu, /* shared buffer: cpu has read/write access */ + buffer_css /* shared buffer: css has read/write access */ +} buffer_state; + +struct ia_css_buffer_s { + /* number of bytes bytes allocated */ + unsigned int size; + /* allocated virtual memory object */ + host_virtual_address_t mem; + /* virtual address to be used on css/firmware */ + vied_virtual_address_t css_address; + /* virtual address to be used on cpu/host */ + void *cpu_address; + buffer_state state; +}; + +typedef struct ia_css_buffer_s *ia_css_buffer_t; + +ia_css_buffer_t +ia_css_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_buffer_t b); + +#endif /* __IA_CSS_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c new file mode 100644 index 000000000000..2a128795d03e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_input_buffer.c @@ -0,0 +1,184 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_input_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_input_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map input buffer to CPU address space, acquire write access */ + b->state = buffer_write; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_write) + return NULL; + + /* unmap input buffer from CPU address space, release write access */ + b->state = buffer_unmapped; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + + /* now flush the cache */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_read) + return 0; + + /* unmap input buffer from CSS address space, release read access */ + b->state = buffer_unmapped; + + /* input buffer only, no need to invalidate cache */ + + return (ia_css_input_buffer_css_address)b->css_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c new file mode 100644 index 000000000000..30bc8d52a5a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_output_buffer.c @@ -0,0 +1,181 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_output_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_output_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map output buffer to CSS address space, acquire write access */ + b->state = buffer_write; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_write) + return 0; + + /* unmap output buffer from CSS address space, release write access */ + b->state = buffer_unmapped; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* now invalidate the cache */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b->cpu_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b->cpu_address; +} + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_read) + return NULL; + + /* unmap output buffer from CPU address space, release read access */ + b->state = buffer_unmapped; + + /* output only, no need to flush cache */ + + return b->cpu_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c new file mode 100644 index 000000000000..92b7110644fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/buffer/src/cpu/ia_css_shared_buffer.c @@ -0,0 +1,187 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_shared_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_shared_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map shared buffer to CPU address space */ + b->state = buffer_cpu; + + return b->cpu_address; +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_cpu) + return NULL; + + /* unmap shared buffer from CPU address space */ + b->state = buffer_unmapped; + + return b->cpu_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map shared buffer to CSS address space */ + b->state = buffer_css; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_css) + return 0; + + /* unmap shared buffer from CSS address space */ + b->state = buffer_unmapped; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to CSS after it was modified by the CPU */ + /* flush cache to ddr */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to the CPU after it has been modified by CSS */ +#ifndef HRT_HW + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* flush cache to ddr */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/cell.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/cell.mk new file mode 100644 index 000000000000..fa5e65022601 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/cell.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef _CELL_MK_ +_CELL_MK_ = 1 + + +CELL_DIR=$${MODULES_DIR}/cell +CELL_INTERFACE=$(CELL_DIR)/interface +CELL_SOURCES=$(CELL_DIR)/src + +CELL_HOST_FILES = +CELL_FW_FILES = + +CELL_HOST_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +CELL_FW_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +ifdef 0 +# Disabled until it is decided to go this way or not +include $(MODULES_DIR)/device_access/device_access.mk +CELL_HOST_FILES += $(DEVICE_ACCESS_HOST_FILES) +CELL_FW_FILES += $(DEVICE_ACCESS_FW_FILES) +CELL_HOST_CPPFLAGS += $(DEVICE_ACCESS_HOST_CPPFLAGS) +CELL_FW_CPPFLAGS += $(DEVICE_ACCESS_FW_CPPFLAGS) +endif + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/interface/ia_css_cell.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/interface/ia_css_cell.h new file mode 100644 index 000000000000..3fac3c791b6e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/interface/ia_css_cell.h @@ -0,0 +1,112 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_H +#define __IA_CSS_CELL_H + +#include "storage_class.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +#if 0 /* To be implemented after completing cell device properties */ +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_debug_pc(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stall_bits(unsigned int ssid, unsigned int cell_id); +#endif + +/* configure master ports */ + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value); + +/* Access memories */ + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr); + +/***********************************************************************/ + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch); + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id); + +/* include inline implementation */ +#include "ia_css_cell_impl.h" + +#endif /* __IA_CSS_CELL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/src/ia_css_cell_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/src/ia_css_cell_impl.h new file mode 100644 index 000000000000..60b2e234da1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cell/src/ia_css_cell_impl.h @@ -0,0 +1,272 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_IMPL_H +#define __IA_CSS_CELL_IMPL_H + +#include "ia_css_cell.h" + +#include "ia_css_cmem.h" +#include "ipu_device_cell_properties.h" +#include "storage_class.h" +#include "assert_support.h" +#include "platform_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_regs_addr(unsigned int cell_id) +{ + /* mem_id 0 is for registers */ + return ipu_device_cell_memory_address(cell_id, 0); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_dmem_addr(unsigned int cell_id) +{ + /* mem_id 1 is for DMEM */ + return ipu_device_cell_memory_address(cell_id, 1); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ipu_device_cell_memory_address( + cell_id, mem_id) + addr, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr) +{ + return ia_css_cmem_load_32( + ssid, ipu_device_cell_memory_address(cell_id, mem_id) + addr); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id) +{ + return ia_css_cmem_load_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = ia_css_cell_get_stat_ctrl(ssid, cell_id); + /* READY must be 1, START must be 0 */ + return (reg & (1 << IPU_DEVICE_CELL_STAT_CTRL_READY_BIT)) && + ((~reg) & (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT)); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc) +{ + /* set start PC */ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_START_PC_REG_ADDRESS, pc); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = 1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + unsigned int reg; + + reg = value << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id) +{ + ia_css_cell_start_prefetch(ssid, cell_id, 0); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch) +{ + unsigned int reg = 0; + + /* Set run bit and start bit */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT); + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT); + /* Invalidate the icache */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); + /* Optionally enable prefetching */ + reg |= ((prefetch == 1) ? + (1 << IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT) : + 0); + + /* store into register */ + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id) +{ + do { + ia_css_sleep(); + } while (!ia_css_cell_is_ready(ssid, cell_id)); +}; + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS, value); +} + +/* master port configuration */ + + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) + +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments, segment_size; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + segment_size = ipu_device_cell_master_segment_size(cell, master); + + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + value += segment_size; + } +} + +#endif /* __IA_CSS_CELL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/isys/subsystem_cnlB0.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/isys/subsystem_cnlB0.mk new file mode 100644 index 000000000000..4a7ef4f324f3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/isys/subsystem_cnlB0.mk @@ -0,0 +1,75 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +############################################################################ +# This file is used to specify versions and properties of ISYS firmware +# components. Please note that these are subsystem specific. System specific +# properties should go to system_$IPU_SYSVER.mk. Also the device versions +# should be defined under "devices" or should be taken from the SDK. +############################################################################ + +############################################################################ +# FIRMWARE RELATED VARIABLES +############################################################################ + +# Activate loading params and storing stats DDR<->REGs with DMA +ISYS_USE_ISA_DMA = 1 +#DMA does not work with AF due to a known bug +DISABLE_AF_STAT_DMA = 1 +# Used in ISA module +ISYS_ISL_DPC_DPC_V2 = 1 + +# Specification for Isys server's fixed globals' locations +REGMEM_OFFSET = 0 # Starting from 0 +REGMEM_SECURE_OFFSET = 4096 +REGMEM_SIZE = 36 +REGMEM_WORD_BYTES = 4 +FW_LOAD_NO_OF_REQUEST_OFFSET = 144 # Taken from REGMEM_OFFSET + REGMEM_SIZE*REGMEM_WORD_BYTES +FW_LOAD_NO_OF_REQUEST_SIZE_BYTES = 4 +# Total Used (@ REGMEM_OFFSET) = 148 # FW_LOAD_NO_OF_REQUEST_OFFSET + FW_LOAD_NO_OF_REQUEST_SIZE_BYTES +# Total Used (@ REGMEM_SECURE_OFFSET) = 144 # FW_LOAD_NO_OF_REQUEST_OFFSET + +# Workarounds: + +# This WA is not to pipeline store frame commands for SID processors that control a Str2Vec (ISA output) +WA_HSD1304553438 = 1 + +# FW workaround for HSD 1404347241. Disable clock gating for CSI2 DPHY Receiver ports +DISABLE_CSI2_RX_DPHY_CLK_GATE = 1 + +# Larger than specified frames that complete mid-line +WA_HSD1209062354 = 0 + +# WA to disable clock gating for the devices in the CSI receivers needed for using the mipi_pkt_gen device +WA_HSD1805168877 = 0 + +# Support IBUF soft-reset at stream start +SOFT_RESET_IBUF_STREAM_START_SUPPORT = 0 + +############################################################################ +# TESTING RELATED VARIABLES +############################################################################ + +# Cannot remove this define +# Used in mipi_capture, isys_utils.mk, and stream_controller.mk +ISYS_DISABLE_VERIFY_RECEIVED_SOF_EOF = 0 + +ISYS_ACCESS_BLOCKER_VERSION = v1 + +HAS_SPC = 1 + +# Support dual command context for VTIO - concurrent secure and non-secure streams +ISYS_HAS_DUAL_CMD_CTX_SUPPORT = 1 + +AB_CONFIG_ARRAY_SIZE = 50 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/system_cnlB0.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/system_cnlB0.mk new file mode 100644 index 000000000000..667282b519c4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/config/system_cnlB0.mk @@ -0,0 +1,96 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +#--- DEFINES REQUIRED TO COMPILE USING LLVM --- +# Enable LLVM/Volcano for IPU4P, SPs only. +VOLCANO_IPU4P = 1 +VOLCANO_SP2601 = 1 +#---------------------------------------------- + +# enable NO_ALIAS for LLVM +ENABLE_NO_ALIAS_FOR_LLVM = 1 + +LOGICAL_FW_INPUT_SYSTEM = input_system_system +LOGICAL_FW_PROCESSING_SYSTEM = processing_system_system +LOGICAL_FW_IPU_SYSTEM = ipu_system +LOGICAL_FW_ISP_SYSTEM = isp2601_default_system +SP_CONTROL_CELL = sp2601_control +SP_PROXY_CELL = sp2601_proxy +ISP_CELL = isp2601 +# The non-capital define isp2601 is used in the sdk, in order to distinguish +# between different isp versions the ISP_CELL_IDENTIFIER define is added. +ISP_CELL_IDENTIFIER = ISP2601 +HAS_IPFD = 1 +HAS_S2M_IN_ISYS_ISL_NONSOC_PATH = 0 +HAS_S2V_IN_ISYS_ISL_NONSOC_PATH = 1 +# ISL-IS non-SoC path has ISA with PAF and DPC-Pext support for IPU4P-B0 +HAS_ISA_IN_ISYS_ISL = 1 +HAS_PAF_IN_ISYS_ISL = 1 +HAS_DPC_PEXT_IN_ISYS_ISL = 1 +HAS_PMA_IF = 1 + +HAS_MIPIBE_IN_PSYS_ISL = 1 + +HAS_VPLESS_SUPPORT = 0 + +DLI_SYSTEM = hive_isp_css_2600_system +RESOURCE_MANAGER_VERSION = v2 +MEM_RESOURCE_VALIDATION_ERROR = 0 +OFS_SCALER_1_4K_TILEY_422_SUPPORT= 1 +PROGDESC_ACC_SYMBOLS_VERSION = v1 +DEVPROXY_INTERFACE_VERSION = v1 +FW_ABI_IPU_TYPES_VERSION = v1 + +HAS_ONLINE_MODE_SUPPORT_IN_ISYS_PSYS = 0 + +MMU_INTERFACE_VERSION = v2 +DEVICE_ACCESS_VERSION = v2 +PSYS_SERVER_VERSION = v3 +PSYS_SERVER_LOADER_VERSION = v1 +PSYS_HW_VERSION = CNL_B0_HW + +# Enable FW_DMA for loading firmware +PSYS_SERVER_ENABLE_FW_LOAD_DMA = 1 + +NCI_SPA_VERSION = v1 +MANIFEST_TOOL_VERSION = v2 +PSYS_CON_MGR_TOOL_VERSION = v1 +# TODO: Should be removed after performance issues OTF are solved +PSYS_PROC_MGR_VERSION = v1 +IPU_RESOURCES_VERSION = v2 + +HAS_ACC_CLUSTER_PAF_PAL = 1 +HAS_ACC_CLUSTER_PEXT_PAL = 1 +HAS_ACC_CLUSTER_GBL_PAL = 1 + +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PARAMBINTOOL_ISA_INIT_VERSION = cnlB0 + +# Select EQC2EQ version +# Version 1: uniform address space, equal EQ addresses regardless of EQC device +# Version 2: multiple addresses per EQ, depending on location of EQC device +EQC2EQ_VERSION = v1 + +# Select DMA instance for fw_load +FW_LOAD_DMA_INSTANCE = NCI_DMA_FW + +HAS_DMA_FW = 1 + +HAS_SIS = 0 +HAS_IDS = 1 + +PSYS_SERVER_ENABLE_TPROXY = 1 +PSYS_SERVER_ENABLE_DEVPROXY = 1 +NCI_OFS_VERSION = v1 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cpd_binary/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cpd_binary/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/cpd_binary/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/device_access.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/device_access.mk new file mode 100644 index 000000000000..1629d9af803b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/device_access.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifndef _DEVICE_ACCESS_MK_ +_DEVICE_ACCESS_MK_ = 1 + +# DEVICE_ACCESS_VERSION= +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk + +DEVICE_ACCESS_DIR=$${MODULES_DIR}/device_access +DEVICE_ACCESS_INTERFACE=$(DEVICE_ACCESS_DIR)/interface +DEVICE_ACCESS_SOURCES=$(DEVICE_ACCESS_DIR)/src + +DEVICE_ACCESS_HOST_FILES = + +DEVICE_ACCESS_FW_FILES = + +DEVICE_ACCESS_HOST_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS += \ + -I$(DEVICE_ACCESS_SOURCES)/$(DEVICE_ACCESS_VERSION) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_cmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_cmem.h new file mode 100644 index 000000000000..3dc47c29fcab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_cmem.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_H +#define __IA_CSS_CMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_cmem_address_t; +#else +#include +typedef vied_subsystem_address_t ia_css_cmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size); + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_cmem_cell.h" +#else +#include "ia_css_cmem_host.h" +#endif + +#endif /* __IA_CSS_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem.h new file mode 100644 index 000000000000..de2b94d8af54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_H +#define __IA_CSS_XMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_xmem_address_t; +#else +#include +typedef host_virtual_address_t ia_css_xmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_xmem_cell.h" +#else +#include "ia_css_xmem_host.h" +#endif + +#endif /* __IA_CSS_XMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem_cmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem_cmem.h new file mode 100644 index 000000000000..57aab3323c73 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/interface/ia_css_xmem_cmem.h @@ -0,0 +1,35 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_H +#define __IA_CSS_XMEM_CMEM_H + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size); + +/* include inline implementation */ +#include "ia_css_xmem_cmem_impl.h" + +#endif /* __IA_CSS_XMEM_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_cmem_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_cmem_host.h new file mode 100644 index 000000000000..22799e67214c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_cmem_host.h @@ -0,0 +1,121 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_HOST_H +#define __IA_CSS_CMEM_HOST_H + +/* This file is an inline implementation for the interface ia_css_cmem.h + * and should only be included there. */ + +#include "assert_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + return vied_subsystem_load_32(ssid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_cond_cmem_load_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + return vied_subsystem_load_32(ssid, address); + else + return 0; +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cond_cmem_store_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address, uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + *data32 = ia_css_cmem_load_32(ssid, address); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, *data32); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size) +{ + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, 0); + address += 4; + } +} + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p) +{ + NOT_USED(base_addr); + return (ia_css_cmem_address_t)(uintptr_t)p; +} + +#endif /* __IA_CSS_CMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h new file mode 100644 index 000000000000..adc178b75059 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_cmem_impl.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_IMPL_H +#define __IA_CSS_XMEM_CMEM_IMPL_H + +#include "ia_css_xmem_cmem.h" + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_cmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_xmem_load_32(mmid, src); + ia_css_cmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + +/* Copy data from cmem to xmem */ + +STORAGE_CLASS_INLINE void +ia_css_cmem_to_xmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_cmem_address_t src, + ia_css_xmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_xmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_cmem_load_32(mmid, src); + ia_css_xmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + + +#endif /* __IA_CSS_XMEM_CMEM_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_host.h new file mode 100644 index 000000000000..d94991fc1114 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/device_access/src/ia_css_xmem_host.h @@ -0,0 +1,84 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_HOST_H +#define __IA_CSS_XMEM_HOST_H + +#include "ia_css_xmem.h" +#include +#include "assert_support.h" +#include + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address) +{ + return shared_memory_load_8(mmid, address); +} + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + return shared_memory_load_16(mmid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + return shared_memory_load_32(mmid, address); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size) +{ + shared_memory_load(mmid, address, data, size); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value) +{ + shared_memory_store_8(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + shared_memory_store_16(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + shared_memory_store_32(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes) +{ + shared_memory_store(mmid, address, data, bytes); +} + +#endif /* __IA_CSS_XMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h new file mode 100644 index 000000000000..5102f6e44d2f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h @@ -0,0 +1,68 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H + +/* Destination values for master port 0 and bitfield "request_dest" */ +enum cio_M0_btrs_dest { + DEST_IS_BUT_REGS = 0, + DEST_IS_DDR, + RESERVED, + DEST_IS_SUBSYSTEM, + N_BTRS_DEST +}; + +/* Bit-field positions for M0 info bits */ +enum ia_css_info_bits_m0_pos { + IA_CSS_INFO_BITS_M0_SNOOPABLE_POS = 0, + IA_CSS_INFO_BITS_M0_IMR_DESTINED_POS = 1, + IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS = 4 +}; + +#define IA_CSS_INFO_BITS_M0_DDR \ + (DEST_IS_DDR << IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS) +#define IA_CSS_INFO_BITS_M0_SNOOPABLE (1 << IA_CSS_INFO_BITS_M0_SNOOPABLE_POS) + +/* Info bits as expected by the buttress */ +/* Deprecated because bit fields are not portable */ + +/* For master port 0*/ +union cio_M0_t { + struct { + unsigned int snoopable : 1; + unsigned int imr_destined : 1; + unsigned int spare0 : 2; + unsigned int request_dest : 2; + unsigned int spare1 : 26; + } as_bitfield; + unsigned int as_word; +}; + +/* For master port 1*/ +union cio_M1_t { + struct { + unsigned int spare0 : 1; + unsigned int deadline_pointer : 1; + unsigned int reserved : 1; + unsigned int zlw : 1; + unsigned int stream_id : 4; + unsigned int address_swizzling : 1; + unsigned int spare1 : 23; + } as_bitfield; + unsigned int as_word; +}; + + +#endif /* __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties.h new file mode 100644 index 000000000000..e6e1e9dcbe80 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_H +#define __IPU_DEVICE_CELL_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_cell_type_properties.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id); + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id); +#endif + +#include "ipu_device_cell_properties_func.h" + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_func.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_func.h new file mode 100644 index 000000000000..481b0504a237 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_func.h @@ -0,0 +1,164 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_FUNC_H +#define __IPU_DEVICE_CELL_PROPERTIES_FUNC_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_cell_properties_impl.h" +#include "ipu_device_cell_devices.h" +#include "assert_support.h" +#include "storage_class.h" + +enum {IA_CSS_CELL_MASTER_ADDRESS_WIDTH = 32}; + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void) +{ + return NUM_CELLS; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_memories; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + mem_size[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].mem_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + assert(mem_id != 0); + return ipu_device_cell_properties[cell_id].mem_databus_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_master_ports; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].segment_bits; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << ipu_device_cell_master_segment_bits(cell_id, master_id); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << (IA_CSS_CELL_MASTER_ADDRESS_WIDTH - + ipu_device_cell_master_segment_bits(cell_id, master_id)); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].stride; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].base_address_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_override_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_override_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + icache_align; +} + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_map_cell_id_to_crun_proc_id[cell_id]; +} +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_FUNC_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h new file mode 100644 index 000000000000..63397dc0b7fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_properties_struct.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H + +/* definitions for all cell types */ + +struct ipu_device_cell_count_s { + unsigned int num_memories; + unsigned int num_master_ports; + unsigned int num_stall_bits; + unsigned int icache_align; +}; + +struct ipu_device_cell_master_properties_s { + unsigned int segment_bits; + unsigned int stride; /* offset to register of next segment */ + unsigned int base_address_register; /* address of first base address + register */ + unsigned int info_bits_register; + unsigned int info_override_bits_register; +}; + +struct ipu_device_cell_type_properties_s { + const struct ipu_device_cell_count_s *count; + const struct ipu_device_cell_master_properties_s *master; + const unsigned int *reg_offset; /* offsets of registers, some depend + on cell type */ + const unsigned int *mem_size; +}; + +struct ipu_device_cell_properties_s { + const struct ipu_device_cell_type_properties_s *type_properties; + const unsigned int *mem_address; + const unsigned int *mem_databus_address; + /* const cell_master_port_properties_s* master_port_properties; */ +}; + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_type_properties.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_type_properties.h new file mode 100644 index 000000000000..72caed3eef0c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/interface/ipu_device_cell_type_properties.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_TYPE_PROPERTIES_H +#define __IPU_DEVICE_CELL_TYPE_PROPERTIES_H + +#define IPU_DEVICE_INVALID_MEM_ADDRESS 0xFFFFFFFF + +enum ipu_device_cell_stat_ctrl_bit { + IPU_DEVICE_CELL_STAT_CTRL_RESET_BIT = 0, + IPU_DEVICE_CELL_STAT_CTRL_START_BIT = 1, + IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT = 3, + IPU_DEVICE_CELL_STAT_CTRL_READY_BIT = 5, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_BIT = 6, + IPU_DEVICE_CELL_STAT_CTRL_STALL_BIT = 7, + IPU_DEVICE_CELL_STAT_CTRL_CLEAR_IRQ_MASK_FLAG_BIT = 8, + IPU_DEVICE_CELL_STAT_CTRL_BROKEN_IRQ_MASK_FLAG_BIT = 9, + IPU_DEVICE_CELL_STAT_CTRL_READY_IRQ_MASK_FLAG_BIT = 10, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_IRQ_MASK_FLAG_BIT = 11, + IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT = 12, + IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT = 13 +}; + +enum ipu_device_cell_reg_addr { + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS = 0x0, + IPU_DEVICE_CELL_START_PC_REG_ADDRESS = 0x4, + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS = 0x10, + IPU_DEVICE_CELL_ICACHE_INFO_BITS_REG_ADDRESS = 0x14 +}; + +enum ipu_device_cell_reg { + IPU_DEVICE_CELL_STAT_CTRL_REG, + IPU_DEVICE_CELL_START_PC_REG, + IPU_DEVICE_CELL_ICACHE_BASE_REG, + IPU_DEVICE_CELL_DEBUG_PC_REG, + IPU_DEVICE_CELL_STALL_REG, + IPU_DEVICE_CELL_NUM_REGS +}; + +enum ipu_device_cell_mem { + IPU_DEVICE_CELL_REGS, /* memory id of registers */ + IPU_DEVICE_CELL_PMEM, /* memory id of pmem */ + IPU_DEVICE_CELL_DMEM, /* memory id of dmem */ + IPU_DEVICE_CELL_BAMEM, /* memory id of bamem */ + IPU_DEVICE_CELL_VMEM /* memory id of vmem */ +}; +#define IPU_DEVICE_CELL_NUM_MEMORIES (IPU_DEVICE_CELL_VMEM + 1) + +enum ipu_device_cell_master { + IPU_DEVICE_CELL_MASTER_ICACHE, /* master port id of icache */ + IPU_DEVICE_CELL_MASTER_QMEM, + IPU_DEVICE_CELL_MASTER_CMEM, + IPU_DEVICE_CELL_MASTER_XMEM, + IPU_DEVICE_CELL_MASTER_XVMEM +}; +#define IPU_DEVICE_CELL_MASTER_NUM_MASTERS (IPU_DEVICE_CELL_MASTER_XVMEM + 1) + +#endif /* __IPU_DEVICE_CELL_TYPE_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_devices.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_devices.h new file mode 100644 index 000000000000..274c9518fd3d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_devices.h @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_DEVICES_H +#define __IPU_DEVICE_CELL_DEVICES_H + +/* define cell instances in ISYS */ + +#define SPC0_CELL input_system_unis_logic_sp_control_tile_sp + +enum ipu_device_isys_cell_id { + SPC0 +}; +#define NUM_CELLS (SPC0 + 1) + +#endif /* __IPU_DEVICE_CELL_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_defs.h new file mode 100644 index 000000000000..e811478b7d0f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_defs.h @@ -0,0 +1,22 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +/* Generated file - please do not edit. */ + +#ifndef _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define SPC0_REGS_CBUS_ADDRESS 0x0 +#define SPC0_DMEM_CBUS_ADDRESS 0x8000 +#define SPC0_DMEM_DBUS_ADDRESS 0x8000 +#define SPC0_DMEM_DMA_M0_ADDRESS 0x1010000 +#endif /* _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_impl.h new file mode 100644 index 000000000000..f350ae74b94d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/isys/cnlB0/ipu_device_cell_properties_impl.h @@ -0,0 +1,57 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_CELL_PROPERTIES_IMPL_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_sp2600_control_properties_impl.h" +#include "ipu_device_cell_properties_defs.h" +#include "ipu_device_cell_devices.h" +#include "ipu_device_cell_type_properties.h"/* IPU_DEVICE_INVALID_MEM_ADDRESS */ + +static const unsigned int +ipu_device_spc0_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + SPC0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spc0_databus_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* regs not accessible from DBUS */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_DBUS_ADDRESS +}; + +static const struct ipu_device_cell_properties_s +ipu_device_cell_properties[NUM_CELLS] = { + { + &ipu_device_sp2600_control_properties, + ipu_device_spc0_mem_address, + ipu_device_spc0_databus_mem_address + } +}; + +#ifdef C_RUN + +/* Mapping between hrt_hive_processors enum and cell_id's used in FW */ +static const int ipu_device_map_cell_id_to_crun_proc_id[NUM_CELLS] = { + 0 /* SPC0 */ +}; + +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h new file mode 100644 index 000000000000..430295cd9d94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/devices/src/ipu_device_sp2600_control_properties_impl.h @@ -0,0 +1,136 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H + +/* sp2600_control definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_control_registers { + /* control registers */ + IPU_DEVICE_SP2600_CONTROL_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_CONTROL_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_CONTROL_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_CONTROL_STALL = 0xA0 +}; + +enum ipu_device_sp2600_control_mems { + IPU_DEVICE_SP2600_CONTROL_REGS, + IPU_DEVICE_SP2600_CONTROL_PMEM, + IPU_DEVICE_SP2600_CONTROL_DMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_control_mem_size[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + 0x000AC, + 0x00000, + 0x10000 +}; + +enum ipu_device_sp2600_control_masters { + IPU_DEVICE_SP2600_CONTROL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_QMEM, + IPU_DEVICE_SP2600_CONTROL_CMEM, + IPU_DEVICE_SP2600_CONTROL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_control_masters[IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_control_stall_bits { + IPU_DEVICE_SP2600_CONTROL_STALL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_STALL_DMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_QMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_CMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_control_count = { + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS, + IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_control_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_control_properties = { + &ipu_device_sp2600_control_count, + ipu_device_sp2600_control_masters, + ipu_device_sp2600_control_reg_offset, + ipu_device_sp2600_control_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk new file mode 100644 index 000000000000..b1ffbf7ea21f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/fw_abi_cpu_types.mk @@ -0,0 +1,24 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +# MODULE is FW ABI COMMON TYPES + +FW_ABI_COMMON_TYPES_DIRS = -I$${MODULES_DIR}/fw_abi_common_types +FW_ABI_COMMON_TYPES_DIRS += -I$${MODULES_DIR}/fw_abi_common_types/cpu + +FW_ABI_COMMON_TYPES_HOST_FILES = +FW_ABI_COMMON_TYPES_HOST_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) + +FW_ABI_COMMON_TYPES_FW_FILES = +FW_ABI_COMMON_TYPES_FW_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h new file mode 100644 index 000000000000..21cc3f43f485 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_BASE_TYPES_H +#define __IA_CSS_TERMINAL_BASE_TYPES_H + + +#include "type_support.h" +#include "ia_css_terminal_defs.h" + +#define N_UINT16_IN_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_TERMINAL_STRUCT 5 + +#define SIZE_OF_TERMINAL_STRUCT_BITS \ + (IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + N_UINT16_IN_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* ==================== Base Terminal - START ==================== */ +struct ia_css_terminal_s { /**< Base terminal */ + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the process group */ + uint16_t size; /**< Size of this whole terminal layout-structure */ + uint16_t tm_index; /**< Index of the terminal manifest object */ + ia_css_terminal_ID_t ID; /**< Absolute referal ID for this terminal, valid ID's != 0 */ + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_STRUCT]; +}; +/* ==================== Base Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h new file mode 100644 index 000000000000..056e1b6d5d4b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H + +#include "ia_css_terminal_defs.h" + +#define N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT 5 +#define SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_UINT16_T_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_UINT32_T_BITS \ + + (N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT*IA_CSS_UINT8_T_BITS)) + +/* ==================== Base Terminal Manifest - START ==================== */ +struct ia_css_terminal_manifest_s { + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the program group manifest */ + uint16_t size; /**< Size of this whole terminal-manifest layout-structure */ + ia_css_terminal_ID_t ID; + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_terminal_manifest_s + ia_css_terminal_manifest_t; + +/* ==================== Base Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_base_types.h new file mode 100644 index 000000000000..3b80a17a6ad3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_base_types.h @@ -0,0 +1,38 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BASE_TYPES_H +#define __IA_CSS_BASE_TYPES_H + +#include "type_support.h" + +#define VIED_VADDRESS_BITS 32 +typedef uint32_t vied_vaddress_t; + +#define DEVICE_DESCRIPTOR_ID_BITS 32 +typedef struct { + uint8_t device_id; + uint8_t instance_id; + uint8_t channel_id; + uint8_t section_id; +} device_descriptor_fields_t; + +typedef union { + device_descriptor_fields_t fields; + uint32_t data; +} device_descriptor_id_t; + +typedef uint16_t ia_css_process_id_t; + +#endif /* __IA_CSS_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h new file mode 100644 index 000000000000..dbf1cf93756f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/fw_abi_common_types/ia_css_terminal_defs.h @@ -0,0 +1,105 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_DEFS_H +#define __IA_CSS_TERMINAL_DEFS_H + + +#include "type_support.h" + +#define IA_CSS_TERMINAL_ID_BITS 8 +typedef uint8_t ia_css_terminal_ID_t; +#define IA_CSS_TERMINAL_INVALID_ID ((ia_css_terminal_ID_t)(-1)) + +/* + * Terminal Base Type + */ +typedef enum ia_css_terminal_type { + /**< Data input */ + IA_CSS_TERMINAL_TYPE_DATA_IN = 0, + /**< Data output */ + IA_CSS_TERMINAL_TYPE_DATA_OUT, + /**< Type 6 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_STREAM, + /**< Type 1-5 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN, + /**< Type 1-5 parameter output */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + /**< Represent the new type of terminal for the + * explicit slicing, when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN, + /**< Represent the new type of terminal for the + * explicit slicing, when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT, + /**< State (private data) input */ + IA_CSS_TERMINAL_TYPE_STATE_IN, + /**< State (private data) output */ + IA_CSS_TERMINAL_TYPE_STATE_OUT, + IA_CSS_TERMINAL_TYPE_PROGRAM, + IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT, + IA_CSS_N_TERMINAL_TYPES +} ia_css_terminal_type_t; + +#define IA_CSS_TERMINAL_TYPE_BITS 32 + +/* Temporary redirection needed to facilicate merging with the drivers + in a backwards compatible manner */ +#define IA_CSS_TERMINAL_TYPE_PARAM_CACHED IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN + +/* + * Dimensions of the data objects. Note that a C-style + * data order is assumed. Data stored by row. + */ +typedef enum ia_css_dimension { + /**< The number of columns, i.e. the size of the row */ + IA_CSS_COL_DIMENSION = 0, + /**< The number of rows, i.e. the size of the column */ + IA_CSS_ROW_DIMENSION = 1, + IA_CSS_N_DATA_DIMENSION = 2 +} ia_css_dimension_t; + +#define IA_CSS_N_COMMAND_COUNT (4) + +#ifndef PIPE_GENERATION +/* Don't include these complex enum structures in Genpipe, it can't handle and it does not need them */ +/* + * enum ia_css_isys_link_id. Lists the link IDs used by the FW for On The Fly feature + */ +typedef enum ia_css_isys_link_id { + IA_CSS_ISYS_LINK_OFFLINE = 0, + IA_CSS_ISYS_LINK_MAIN_OUTPUT = 1, + IA_CSS_ISYS_LINK_PDAF_OUTPUT = 2 +} ia_css_isys_link_id_t; +#define N_IA_CSS_ISYS_LINK_ID (IA_CSS_ISYS_LINK_PDAF_OUTPUT + 1) + +/* + * enum ia_css_data_barrier_link_id. Lists the link IDs used by the FW for data barrier feature + */ +typedef enum ia_css_data_barrier_link_id { + IA_CSS_DATA_BARRIER_LINK_MEMORY = N_IA_CSS_ISYS_LINK_ID, + N_IA_CSS_DATA_BARRIER_LINK_ID +} ia_css_data_barrier_link_id_t; + +#endif /* #ifndef PIPE_GENERATION */ +#endif /* __IA_CSS_TERMINAL_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h new file mode 100644 index 000000000000..5e47fe7026bd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isys_fw_bridged_types.h @@ -0,0 +1,402 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_FW_BRIDGED_TYPES_H +#define __IA_CSS_ISYS_FW_BRIDGED_TYPES_H + +#include "platform_support.h" + +#include "ia_css_isysapi_fw_types.h" + +/** + * struct ia_css_isys_buffer_partition_comm - buffer partition information + * @num_gda_pages: Number of virtual gda pages available for each + * virtual stream + */ +struct ia_css_isys_buffer_partition_comm { + aligned_uint32(unsigned int, num_gda_pages[STREAM_ID_MAX]); +}; + +/** + * struct ia_css_isys_fw_config - contains the parts from + * ia_css_isys_device_cfg_data + * we need to transfer to the cell + * @num_send_queues: Number of send queues per queue + * type(N_IA_CSS_ISYS_QUEUE_TYPE) + * @num_recv_queues: Number of receive queues per queue + * type(N_IA_CSS_ISYS_QUEUE_TYPE) + */ +struct ia_css_isys_fw_config { + aligned_struct(struct ia_css_isys_buffer_partition_comm, + buffer_partition); + aligned_uint32(unsigned int, + num_send_queues[N_IA_CSS_ISYS_QUEUE_TYPE]); + aligned_uint32(unsigned int, + num_recv_queues[N_IA_CSS_ISYS_QUEUE_TYPE]); +}; + +/** + * struct ia_css_isys_resolution_comm: Generic resolution structure. + * @Width + * @Height + */ +struct ia_css_isys_resolution_comm { + aligned_uint32(unsigned int, width); + aligned_uint32(unsigned int, height); +}; + +/** + * struct ia_css_isys_output_pin_payload_comm + * @out_buf_id: Points to output pin buffer - buffer identifier + * @addr: Points to output pin buffer - CSS Virtual Address + * @compress: Request frame compression (1), or not (0) + * This must be the same as ia_css_isys_output_pin_info_comm::reserve_compression + */ +struct ia_css_isys_output_pin_payload_comm { + aligned_uint64(ia_css_return_token, out_buf_id); + aligned_uint32(ia_css_output_buffer_css_address, addr); + aligned_uint32(unsigned int, compress); +}; + +/** + * struct ia_css_isys_output_pin_info_comm + * @input_pin_id: input pin id/index which is source of + * the data for this output pin + * @output_res: output pin resolution + * @stride: output stride in Bytes (not valid for statistics) + * @watermark_in_lines: pin watermark level in lines + * @payload_buf_size: Size in Bytes of all buffers that will be supplied for capture + * on this pin (i.e. addressed by ia_css_isys_output_pin_payload::addr) + * @send_irq: assert if pin event should trigger irq + * @pt: pin type + * @ft: frame format type + * @link_id: identifies PPG to connect to, link_id = 0 implies offline + * while link_id > 0 implies buffer_chasing or online mode + * can be entered. + * @reserve_compression: Reserve compression resources for pin. + */ +struct ia_css_isys_output_pin_info_comm { + aligned_struct(struct ia_css_isys_resolution_comm, output_res); + aligned_uint32(unsigned int, stride); + aligned_uint32(unsigned int, watermark_in_lines); + aligned_uint32(unsigned int, payload_buf_size); + aligned_uint8(unsigned int, send_irq); + aligned_uint8(unsigned int, input_pin_id); + aligned_uint8(enum ia_css_isys_pin_type, pt); + aligned_uint8(enum ia_css_isys_frame_format_type, ft); + aligned_uint8(enum ia_css_isys_link_id, link_id); + aligned_uint8(unsigned int, reserve_compression); +}; + +/** + * struct ia_css_isys_param_pin_comm + * @param_buf_id: Points to param port buffer - buffer identifier + * @addr: Points to param pin buffer - CSS Virtual Address + */ +struct ia_css_isys_param_pin_comm { + aligned_uint64(ia_css_return_token, param_buf_id); + aligned_uint32(ia_css_input_buffer_css_address, addr); +}; + +/** + * struct ia_css_isys_input_pin_info_comm + * @input_res: input resolution + * @dt: mipi data type + * @mipi_store_mode: defines if legacy long packet header will be stored or + * hdiscarded if discarded, output pin pin type for this + * input pin can only be MIPI + * @bits_per_pix: native bits per pixel + * @dt_rename: mapped_dt + */ +struct ia_css_isys_input_pin_info_comm { + aligned_struct(struct ia_css_isys_resolution_comm, input_res); + aligned_uint8(enum ia_css_isys_mipi_data_type, dt); + aligned_uint8(enum ia_css_isys_mipi_store_mode, mipi_store_mode); + aligned_uint8(unsigned int, bits_per_pix); + aligned_uint8(unsigned int, mapped_dt); +}; + +/** + * ISA configuration fields, definition and macros + */ +#define ISA_CFG_FIELD_BLC_EN_LEN 1 +#define ISA_CFG_FIELD_BLC_EN_SHIFT 0 + +#define ISA_CFG_FIELD_LSC_EN_LEN 1 +#define ISA_CFG_FIELD_LSC_EN_SHIFT 1 + +#define ISA_CFG_FIELD_DPC_EN_LEN 1 +#define ISA_CFG_FIELD_DPC_EN_SHIFT 2 + +#define ISA_CFG_FIELD_DOWNSCALER_EN_LEN 1 +#define ISA_CFG_FIELD_DOWNSCALER_EN_SHIFT 3 + +#define ISA_CFG_FIELD_AWB_EN_LEN 1 +#define ISA_CFG_FIELD_AWB_EN_SHIFT 4 + +#define ISA_CFG_FIELD_AF_EN_LEN 1 +#define ISA_CFG_FIELD_AF_EN_SHIFT 5 + +#define ISA_CFG_FIELD_AE_EN_LEN 1 +#define ISA_CFG_FIELD_AE_EN_SHIFT 6 + +#define ISA_CFG_FIELD_PAF_TYPE_LEN 8 +#define ISA_CFG_FIELD_PAF_TYPE_SHIFT 7 + +#define ISA_CFG_FIELD_SEND_IRQ_STATS_READY_LEN 1 +#define ISA_CFG_FIELD_SEND_IRQ_STATS_READY_SHIFT 15 + +#define ISA_CFG_FIELD_SEND_RESP_STATS_READY_LEN 1 +#define ISA_CFG_FIELD_SEND_RESP_STATS_READY_SHIFT 16 + +/* Helper macros */ +#define ISA_CFG_GET_MASK_FROM_LEN(len) ((1 << (len)) - 1) +#define ISA_CFG_GET_MASK_FROM_TAG(tag) \ + (ISA_CFG_GET_MASK_FROM_LEN(ISA_CFG_FIELD_##tag##_LEN)) +#define ISA_CFG_GET_SHIFT_FROM_TAG(tag) \ + (ISA_CFG_FIELD_##tag##_SHIFT) +/* Get/Set macros */ +#define ISA_CFG_FIELD_GET(tag, word) \ + ( \ + ((word) >> (ISA_CFG_GET_SHIFT_FROM_TAG(tag))) &\ + ISA_CFG_GET_MASK_FROM_TAG(tag) \ + ) +#define ISA_CFG_FIELD_SET(tag, word, value) \ + word |= ( \ + ((value) & ISA_CFG_GET_MASK_FROM_TAG(tag)) << \ + ISA_CFG_GET_SHIFT_FROM_TAG(tag) \ + ) + +/** + * struct ia_css_isys_isa_cfg_comm. Describes the ISA cfg + */ +struct ia_css_isys_isa_cfg_comm { + aligned_struct(struct ia_css_isys_resolution_comm, + isa_res[N_IA_CSS_ISYS_RESOLUTION_INFO]); + aligned_uint32(/* multi-field packing */, cfg_fields); +}; + + /** + * struct ia_css_isys_cropping_comm - cropping coordinates + */ +struct ia_css_isys_cropping_comm { + aligned_int32(int, top_offset); + aligned_int32(int, left_offset); + aligned_int32(int, bottom_offset); + aligned_int32(int, right_offset); +}; + + /** + * struct ia_css_isys_stream_cfg_data_comm + * ISYS stream configuration data structure + * @isa_cfg: details about what ACCs are active if ISA is used + * @crop: defines cropping resolution for the + * maximum number of input pins which can be cropped, + * it is directly mapped to the HW devices + * @input_pins: input pin descriptors + * @output_pins: output pin descriptors + * @compfmt: de-compression setting for User Defined Data + * @nof_input_pins: number of input pins + * @nof_output_pins: number of output pins + * @send_irq_sof_discarded: send irq on discarded frame sof response + * - if '1' it will override the send_resp_sof_discarded and send + * the response + * - if '0' the send_resp_sof_discarded will determine whether to + * send the response + * @send_irq_eof_discarded: send irq on discarded frame eof response + * - if '1' it will override the send_resp_eof_discarded and send + * the response + * - if '0' the send_resp_eof_discarded will determine whether to + * send the response + * @send_resp_sof_discarded: send response for discarded frame sof detected, + * used only when send_irq_sof_discarded is '0' + * @send_resp_eof_discarded: send response for discarded frame eof detected, + * used only when send_irq_eof_discarded is '0' + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) + * @isl_use: indicates whether stream requires ISL and how + */ +struct ia_css_isys_stream_cfg_data_comm { + aligned_struct(struct ia_css_isys_isa_cfg_comm, isa_cfg); + aligned_struct(struct ia_css_isys_cropping_comm, + crop[N_IA_CSS_ISYS_CROPPING_LOCATION]); + aligned_struct(struct ia_css_isys_input_pin_info_comm, + input_pins[MAX_IPINS]); + aligned_struct(struct ia_css_isys_output_pin_info_comm, + output_pins[MAX_OPINS]); + aligned_uint32(unsigned int, compfmt); + aligned_uint8(unsigned int, nof_input_pins); + aligned_uint8(unsigned int, nof_output_pins); + aligned_uint8(unsigned int, send_irq_sof_discarded); + aligned_uint8(unsigned int, send_irq_eof_discarded); + aligned_uint8(unsigned int, send_resp_sof_discarded); + aligned_uint8(unsigned int, send_resp_eof_discarded); + aligned_uint8(enum ia_css_isys_stream_source, src); + aligned_uint8(enum ia_css_isys_mipi_vc, vc); + aligned_uint8(enum ia_css_isys_isl_use, isl_use); +}; + +/** + * struct ia_css_isys_frame_buff_set - frame buffer set + * @output_pins: output pin addresses + * @process_group_light: process_group_light buffer address + * @send_irq_sof: send irq on frame sof response + * - if '1' it will override the send_resp_sof and send the + * response + * - if '0' the send_resp_sof will determine whether to send the + * response + * @send_irq_eof: send irq on frame eof response + * - if '1' it will override the send_resp_eof and send the + * response + * - if '0' the send_resp_eof will determine whether to send the + * response + * @send_resp_sof: send response for frame sof detected, used only when + * send_irq_sof is '0' + * @send_resp_eof: send response for frame eof detected, used only when + * send_irq_eof is '0' + * @frame_counter: frame number associated with this buffer set. + */ +struct ia_css_isys_frame_buff_set_comm { + aligned_struct(struct ia_css_isys_output_pin_payload_comm, + output_pins[MAX_OPINS]); + aligned_struct(struct ia_css_isys_param_pin_comm, process_group_light); + aligned_uint8(unsigned int, send_irq_sof); + aligned_uint8(unsigned int, send_irq_eof); + aligned_uint8(unsigned int, send_irq_capture_ack); + aligned_uint8(unsigned int, send_irq_capture_done); + aligned_uint8(unsigned int, send_resp_sof); + aligned_uint8(unsigned int, send_resp_eof); + aligned_uint8(unsigned int, frame_counter); +}; + +/** + * struct ia_css_isys_error_info_comm + * @error: error code if something went wrong + * @error_details: depending on error code, it may contain additional + * error info + */ +struct ia_css_isys_error_info_comm { + aligned_enum(enum ia_css_isys_error, error); + aligned_uint32(unsigned int, error_details); +}; + +/** + * struct ia_css_isys_resp_info_comm + * @pin: this var is only valid for pin event related responses, + * contains pin addresses + * @process_group_light: this var is valid for stats ready related responses, + * contains process group addresses + * @error_info: error information from the FW + * @timestamp: Time information for event if available + * @stream_handle: stream id the response corresponds to + * @type: response type + * @pin_id: pin id that the pin payload corresponds to + * @acc_id: this var is valid for stats ready related responses, + * contains accelerator id that finished producing + * all related statistics + * @frame_counter: valid for STREAM_START_AND_CAPTURE_DONE, + * STREAM_CAPTURE_DONE and STREAM_CAPTURE_DISCARDED, + * @written_direct: indicates if frame was written direct (online mode) or not. + * + */ + +struct ia_css_isys_resp_info_comm { + aligned_uint64(ia_css_return_token, buf_id); /* Used internally only */ + aligned_struct(struct ia_css_isys_output_pin_payload_comm, pin); + aligned_struct(struct ia_css_isys_param_pin_comm, process_group_light); + aligned_struct(struct ia_css_isys_error_info_comm, error_info); + aligned_uint32(unsigned int, timestamp[2]); + aligned_uint8(unsigned int, stream_handle); + aligned_uint8(enum ia_css_isys_resp_type, type); + aligned_uint8(unsigned int, pin_id); + aligned_uint8(unsigned int, acc_id); + aligned_uint8(unsigned int, frame_counter); + aligned_uint8(unsigned int, written_direct); +}; + +/** + * struct ia_css_isys_proxy_error_info_comm + * @proxy_error: error code if something went wrong + * @proxy_error_details: depending on error code, it may contain additional + * error info + */ +struct ia_css_isys_proxy_error_info_comm { + aligned_enum(enum ia_css_proxy_error, error); + aligned_uint32(unsigned int, error_details); +}; + +/** + * struct ia_css_isys_proxy_resp_info_comm + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @error_info: details in struct definition + */ +struct ia_css_isys_proxy_resp_info_comm { + aligned_uint32(uint32_t, request_id); + aligned_struct(struct ia_css_isys_proxy_error_info_comm, error_info); +}; + +/** + * struct ia_css_proxy_write_queue_token + * @request_id: update id for the specific proxy write request + * @region_index: Region id for the proxy write request + * @offset: Offset of the write request according to the base address of the + * region + * @value: Value that is requested to be written with the proxy write request + */ +struct ia_css_proxy_write_queue_token { + aligned_uint32(uint32_t, request_id); + aligned_uint32(uint32_t, region_index); + aligned_uint32(uint32_t, offset); + aligned_uint32(uint32_t, value); +}; + +/* From here on type defines not coming from the ISYSAPI interface */ + +/** + * struct resp_queue_token + */ +struct resp_queue_token { + aligned_struct(struct ia_css_isys_resp_info_comm, resp_info); +}; + +/** + * struct send_queue_token + */ +struct send_queue_token { + aligned_uint64(ia_css_return_token, buf_handle); + aligned_uint32(ia_css_input_buffer_css_address, payload); + aligned_uint16(enum ia_css_isys_send_type, send_type); + aligned_uint16(unsigned int, stream_id); +}; + +/** + * struct proxy_resp_queue_token + */ +struct proxy_resp_queue_token { + aligned_struct(struct ia_css_isys_proxy_resp_info_comm, + proxy_resp_info); +}; + +/** + * struct proxy_send_queue_token + */ +struct proxy_send_queue_token { + aligned_uint32(uint32_t, request_id); + aligned_uint32(uint32_t, region_index); + aligned_uint32(uint32_t, offset); + aligned_uint32(uint32_t, value); +}; + +#endif /* __IA_CSS_ISYS_FW_BRIDGED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi.h new file mode 100644 index 000000000000..abbc8b8d26ed --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi.h @@ -0,0 +1,321 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_H +#define __IA_CSS_ISYSAPI_H + + +/* The following is needed for the function arguments */ +#include "ia_css_isysapi_types.h" + +/* To define the HANDLE */ +#include "type_support.h" + + +/** + * ia_css_isys_device_open() - configure ISYS device + * @ context : device handle output parameter + * @config: device configuration data struct ptr as input parameter, + * read only by css fw until function return + * Ownership, ISYS will only access read my_device during fct call + * Prepares and Sends to PG server (SP) the syscom and isys context + * Executes the host level 0 and 1 boot sequence and starts the PG server (SP) + * All streams must be stopped when calling ia_css_isys_device_open() + * + * Return: int type error code (errno.h) + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +extern int ia_css_isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config +); +extern int ia_css_isys_context_store_dmem( + const HANDLE *context, + const struct ia_css_isys_device_cfg_data *config +); +extern bool ia_css_isys_ab_spc_ready( + HANDLE *context +); +extern int ia_css_isys_device_open( + const struct ia_css_isys_device_cfg_data *config +); +#else +extern int ia_css_isys_device_open( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config +); +#endif + +/** + * ia_css_isys_device_open_ready() - Complete ISYS device configuration + * @ context : device handle output parameter + * read only by css fw until function return + * Requires the boot failure to be completed before it can return + * successfully (includes syscom and isys context) + * Initialise Host/ISYS messaging queues + * Must be called multiple times until it succeeds or it is determined by + * the driver that the boot seuqence has failed. + * All streams must be stopped when calling ia_css_isys_device_open() + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_device_open_ready( + HANDLE context +); + + /** + * ia_css_isys_stream_open() - open and configure a virtual stream + * @ stream_handle: stream handle + * @ stream_cfg: stream configuration data struct pointer, which is + * "read only" by ISYS until function return + * ownership, ISYS will only read access stream_cfg during fct call + * Pre-conditions: + * Any Isys/Ssys interface changes must call ia_css_isys_stream_open() + * Post-condition: + * On successful call, ISYS hardware resource (IBFctrl, ISL, DMAs) + * are acquired and ISYS server is able to handle stream specific commands + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_open( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_stream_cfg_data *stream_cfg +); + +/** + * ia_css_isys_stream_close() - close virtual stream + * @ stream_handle: stream identifier + * release ISYS resources by freeing up stream HW resources + * output pin buffers ownership is returned to the driver + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_close( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_start() - starts handling a mipi virtual stream + * @ stream_handle: stream identifier + * @next_frame: + * if next_frame != NULL: apply next_frame + * settings asynchronously and start stream + * This mode ensures that the first frame is captured + * and thus a minimal start up latency + * (preconditions: sensor streaming must be switched off) + * + * if next_frame == NULL: sensor can be in a streaming state, + * all capture indicates commands will be + * processed synchronously (e.g. on mipi SOF events) + * + * To be called once ia_css_isys_stream_open() successly called + * On success, the stream's HW resources are in active state + * + * Object ownership: During this function call, + * next_frame struct must be read but not modified by the ISYS, + * and in addition the driver is not allowed to modify it + * on function exit next_frame ownership is returned to + * the driver and is no longer accesses by iSYS + * next_frame contains a collection of + * ia_css_isys_output_pin * and ia_css_isys_input_pin * + * which point to the frame's "output/input pin info & data buffers", + * + * Upon the ia_css_isys_stream_start() call, + * ia_css_isys_output_pin* or ia_css_isys_input_pin* + * will now be owned by the ISYS + * these ptr will enable runtime/dynamic ISYS configuration and also + * to store and write captured payload data + * at the address specified in ia_css_isys_output_pin_payload + * These ptrs should no longer be accessed by any other + * code until (ia_css_isys_output_pin) gets handed + * back to the driver via the response mechansim + * ia_css_isys_stream_handle_response() + * the driver is responsible for providing valid + * ia_css_isys_output_pin* or ia_css_isys_output_pin* + * Pointers set to NULL will simply not be used by the ISYS + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_start( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_stream_stop() - Stops a mipi virtual stream + * @ stream_handle: stream identifier + * stop both accepting new commands and processing + * submitted capture indication commands + * Support for Secure Touch + * Precondition: stream must be started + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_stop( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_flush() - stops a mipi virtual stream but + * completes processing cmd backlog + * @ stream_handle: stream identifier + * stop accepting commands, but process + * the already submitted capture indicates + * Precondition: stream must be started + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_flush( + HANDLE context, + const unsigned int stream_handle +); + +/** + * ia_css_isys_stream_capture_indication() + * captures "next frame" on stream_handle + * @ stream_handle: stream identifier + * @ next_frame: frame pin payloads are provided atomically + * purpose: stream capture new frame command, Successfull calls will + * result in frame output pins being captured + * + * To be called once ia_css_isys_stream_start() is successly called + * On success, the stream's HW resources are in active state + * + * Object ownership: During this function call, + * next_frame struct must be read but not modified by the ISYS, + * and in addition the driver is not allowed to modify it + * on function exit next_frame ownership is returned to + * the driver and is no longer accesses by iSYS + * next_frame contains a collection of + * ia_css_isys_output_pin * and ia_css_isys_input_pin * + * which point to the frame's "output/input pin info & data buffers", + * + * Upon the ia_css_isys_stream_capture_indication() call, + * ia_css_isys_output_pin* or ia_css_isys_input_pin* + * will now be owned by the ISYS + * these ptr will enable runtime/dynamic ISYS configuration and also + * to store and write captured payload data + * at the address specified in ia_css_isys_output_pin_payload + * These ptrs should no longer be accessed by any other + * code until (ia_css_isys_output_pin) gets handed + * back to the driver via the response mechanism + * ia_css_isys_stream_handle_response() + * the driver is responsible for providing valid + * ia_css_isys_output_pin* or ia_css_isys_output_pin* + * Pointers set to NULL will simply not be used by the ISYS, and this + * refers specifically the following cases: + * - output pins from SOC path if the same datatype is also passed into ISAPF + * path or it has active MIPI output (not NULL) + * - full resolution pin from ISA (but not when bypassing ISA) + * - scaled pin from ISA (bypassing ISA for scaled pin is impossible) + * - output pins from MIPI path but only when the same datatype is also + * either forwarded to the ISAPF path based on the stream configuration + * (it is ok if the second output pin of this datatype is also skipped) + * or it has an active SOC output (not NULL) + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_capture_indication( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_stream_handle_response() - handle ISYS responses + * @received_response: provides response info from the + * "next response element" from ISYS server + * received_response will be written to during the fct call and + * can be read by the drv once fct is returned + * + * purpose: Allows the client to handle received ISYS responses + * Upon an IRQ event, the driver will call ia_css_isys_stream_handle_response() + * until the queue is emptied + * Responses returning IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY to the driver will + * hand back ia_css_isys_output_pin ownership to the drv + * ISYS FW will not write/read access ia_css_isys_output_pin + * once it belongs to the driver + * Pre-conditions: ISYS client must have sent a CMDs to ISYS srv + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_stream_handle_response( + HANDLE context, + struct ia_css_isys_resp_info *received_response +); + +/** + * ia_css_isys_device_close() - close ISYS device + * @context : device handle output parameter + * Purpose: Request for the cell to close + * All streams must be stopped when calling ia_css_isys_device_close() + * + * Return: int type error code (errno.h) + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +extern int ia_css_isys_context_destroy( + HANDLE context +); +extern void ia_css_isys_device_close( + void +); +#else +extern int ia_css_isys_device_close( + HANDLE context +); +#endif + +/** + * ia_css_isys_device_release() - release ISYS device + * @context : device handle output parameter + * @force: forces release or verifies the state before releasing + * Purpose: Free context forcibly or not + * Must be called after ia_css_isys_device_close() + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_device_release( + HANDLE context, + unsigned int force +); + +/** + * ia_css_isys_proxy_write_req() - issue a isys proxy write request + * @context : device handle output parameter + * Purpose: Issues a write request for the regions that are exposed + * by proxy interface + * Can be called any time between ia_css_isys_device_open + * ia_css_isys_device_close + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_proxy_write_req( + HANDLE context, + const struct ia_css_proxy_write_req_val *write_req_val +); + +/** + * ia_css_isys_proxy_handle_write_response() + * - Handles isys proxy write request responses + * @context : device handle output parameter + * Purpose: Handling the responses that are created by FW upon the completion + * proxy interface write request + * + * Return: int type error code (errno.h) + */ +extern int ia_css_isys_proxy_handle_write_response( + HANDLE context, + struct ia_css_proxy_write_req_resp *received_response +); + +#endif /* __IA_CSS_ISYSAPI_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h new file mode 100644 index 000000000000..938f726d1cfb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_types.h @@ -0,0 +1,512 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_FW_TYPES_H +#define __IA_CSS_ISYSAPI_FW_TYPES_H + + +/* Max number of Input/Output Pins */ +#define MAX_IPINS (4) +/* worst case is ISA use where a single input pin produces: +* Mipi output, NS Pixel Output, and Scaled Pixel Output. +* This is how the 2 is calculated +*/ +#define MAX_OPINS ((MAX_IPINS) + 2) + +/* Max number of supported virtual streams */ +#define STREAM_ID_MAX (8) + +/* Aligned with the approach of having one dedicated per stream */ +#define N_MAX_MSG_SEND_QUEUES (STREAM_ID_MAX) +/* Single return queue for all streams/commands type */ +#define N_MAX_MSG_RECV_QUEUES (1) +/* Single device queue for high priority commands (bypass in-order queue) */ +#define N_MAX_DEV_SEND_QUEUES (1) +/* Single dedicated send queue for proxy interface */ +#define N_MAX_PROXY_SEND_QUEUES (1) +/* Single dedicated recv queue for proxy interface */ +#define N_MAX_PROXY_RECV_QUEUES (1) +/* Send queues layout */ +#define BASE_PROXY_SEND_QUEUES (0) +#define BASE_DEV_SEND_QUEUES (BASE_PROXY_SEND_QUEUES + N_MAX_PROXY_SEND_QUEUES) +#define BASE_MSG_SEND_QUEUES (BASE_DEV_SEND_QUEUES + N_MAX_DEV_SEND_QUEUES) +#define N_MAX_SEND_QUEUES (BASE_MSG_SEND_QUEUES + N_MAX_MSG_SEND_QUEUES) +/* Recv queues layout */ +#define BASE_PROXY_RECV_QUEUES (0) +#define BASE_MSG_RECV_QUEUES (BASE_PROXY_RECV_QUEUES + N_MAX_PROXY_RECV_QUEUES) +#define N_MAX_RECV_QUEUES (BASE_MSG_RECV_QUEUES + N_MAX_MSG_RECV_QUEUES) + +#define MAX_QUEUE_SIZE (256) +#define MIN_QUEUE_SIZE (1) + +/* Consider 1 slot per stream since driver is not expected to pipeline + * device commands for the same stream */ +#define DEV_SEND_QUEUE_SIZE (STREAM_ID_MAX) + +/* Max number of supported SRAM buffer partitions */ +/* It refers to the size of stream partitions */ +/* These partitions are further subpartitioned internally */ +/* by the FW, but by declaring statically the stream */ +/* partitions we solve the buffer fragmentation issue */ +#define NOF_SRAM_BLOCKS_MAX (STREAM_ID_MAX) + +/* Max number of supported input pins routed in ISL */ +#define MAX_IPINS_IN_ISL (2) + +/* Max number of planes for frame formats supported by the FW */ +#define PIN_PLANES_MAX (4) + +/** + * enum ia_css_isys_resp_type + */ +enum ia_css_isys_resp_type { + IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, + IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK, + IA_CSS_ISYS_RESP_TYPE_FRAME_SOF, + IA_CSS_ISYS_RESP_TYPE_FRAME_EOF, + IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, + IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED, + IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED, + IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED, + IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED, + IA_CSS_ISYS_RESP_TYPE_STATS_DATA_READY, + N_IA_CSS_ISYS_RESP_TYPE +}; + +/** + * enum ia_css_isys_send_type + */ +enum ia_css_isys_send_type { + IA_CSS_ISYS_SEND_TYPE_STREAM_OPEN = 0, + IA_CSS_ISYS_SEND_TYPE_STREAM_START, + IA_CSS_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE, + IA_CSS_ISYS_SEND_TYPE_STREAM_CAPTURE, + IA_CSS_ISYS_SEND_TYPE_STREAM_STOP, + IA_CSS_ISYS_SEND_TYPE_STREAM_FLUSH, + IA_CSS_ISYS_SEND_TYPE_STREAM_CLOSE, + N_IA_CSS_ISYS_SEND_TYPE +}; + +/** + * enum ia_css_isys_queue_type + */ +enum ia_css_isys_queue_type { + IA_CSS_ISYS_QUEUE_TYPE_PROXY = 0, + IA_CSS_ISYS_QUEUE_TYPE_DEV, + IA_CSS_ISYS_QUEUE_TYPE_MSG, + N_IA_CSS_ISYS_QUEUE_TYPE +}; + +/** + * enum ia_css_isys_stream_source: Specifies a source for a stream + */ +enum ia_css_isys_stream_source { + IA_CSS_ISYS_STREAM_SRC_PORT_0 = 0, + IA_CSS_ISYS_STREAM_SRC_PORT_1, + IA_CSS_ISYS_STREAM_SRC_PORT_2, + IA_CSS_ISYS_STREAM_SRC_PORT_3, + IA_CSS_ISYS_STREAM_SRC_PORT_4, + IA_CSS_ISYS_STREAM_SRC_PORT_5, + IA_CSS_ISYS_STREAM_SRC_PORT_6, + IA_CSS_ISYS_STREAM_SRC_PORT_7, + IA_CSS_ISYS_STREAM_SRC_PORT_8, + IA_CSS_ISYS_STREAM_SRC_PORT_9, + IA_CSS_ISYS_STREAM_SRC_PORT_10, + IA_CSS_ISYS_STREAM_SRC_PORT_11, + IA_CSS_ISYS_STREAM_SRC_PORT_12, + IA_CSS_ISYS_STREAM_SRC_PORT_13, + IA_CSS_ISYS_STREAM_SRC_PORT_14, + IA_CSS_ISYS_STREAM_SRC_PORT_15, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_0, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_1, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_2, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_3, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_4, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_5, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_6, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_7, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_8, + IA_CSS_ISYS_STREAM_SRC_MIPIGEN_9, + N_IA_CSS_ISYS_STREAM_SRC +}; + +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT0 IA_CSS_ISYS_STREAM_SRC_PORT_0 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT1 IA_CSS_ISYS_STREAM_SRC_PORT_1 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT2 IA_CSS_ISYS_STREAM_SRC_PORT_2 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_PORT3 IA_CSS_ISYS_STREAM_SRC_PORT_3 + +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_PORTA IA_CSS_ISYS_STREAM_SRC_PORT_4 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_PORTB IA_CSS_ISYS_STREAM_SRC_PORT_5 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 IA_CSS_ISYS_STREAM_SRC_PORT_6 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 IA_CSS_ISYS_STREAM_SRC_PORT_7 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 IA_CSS_ISYS_STREAM_SRC_PORT_8 +#define IA_CSS_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 IA_CSS_ISYS_STREAM_SRC_PORT_9 + +#define IA_CSS_ISYS_STREAM_SRC_MIPIGEN_PORT0 IA_CSS_ISYS_STREAM_SRC_MIPIGEN_0 +#define IA_CSS_ISYS_STREAM_SRC_MIPIGEN_PORT1 IA_CSS_ISYS_STREAM_SRC_MIPIGEN_1 + +/** + * enum ia_css_isys_mipi_vc: MIPI csi2 spec + * supports upto 4 virtual per physical channel + */ +enum ia_css_isys_mipi_vc { + IA_CSS_ISYS_MIPI_VC_0 = 0, + IA_CSS_ISYS_MIPI_VC_1, + IA_CSS_ISYS_MIPI_VC_2, + IA_CSS_ISYS_MIPI_VC_3, + N_IA_CSS_ISYS_MIPI_VC +}; + +/** + * Supported Pixel Frame formats. Expandable if needed + */ +enum ia_css_isys_frame_format_type { + IA_CSS_ISYS_FRAME_FORMAT_NV11 = 0,/* 12 bit YUV 411, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12,/* 12 bit YUV 420, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12_16,/* 16 bit YUV 420, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV12_TILEY,/* 12 bit YUV 420, Intel + proprietary tiled format, + TileY + */ + IA_CSS_ISYS_FRAME_FORMAT_NV16,/* 16 bit YUV 422, Y, UV plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV21,/* 12 bit YUV 420, Y, VU plane */ + IA_CSS_ISYS_FRAME_FORMAT_NV61,/* 16 bit YUV 422, Y, VU plane */ + IA_CSS_ISYS_FRAME_FORMAT_YV12,/* 12 bit YUV 420, Y, V, U plane */ + IA_CSS_ISYS_FRAME_FORMAT_YV16,/* 16 bit YUV 422, Y, V, U plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420,/* 12 bit YUV 420, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_10,/* yuv420, 10 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_12,/* yuv420, 12 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_14,/* yuv420, 14 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV420_16,/* yuv420, 16 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_YUV422,/* 16 bit YUV 422, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV422_16,/* yuv422, 16 bits per subpixel */ + IA_CSS_ISYS_FRAME_FORMAT_UYVY,/* 16 bit YUV 422, UYVY interleaved */ + IA_CSS_ISYS_FRAME_FORMAT_YUYV,/* 16 bit YUV 422, YUYV interleaved */ + IA_CSS_ISYS_FRAME_FORMAT_YUV444,/* 24 bit YUV 444, Y, U, V plane */ + IA_CSS_ISYS_FRAME_FORMAT_YUV_LINE,/* Internal format, 2 y lines + followed by a uvinterleaved line + */ + IA_CSS_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */ + IA_CSS_ISYS_FRAME_FORMAT_RGB565,/* 16 bit RGB, 1 plane. Each 3 sub + pixels are packed into one 16 bit + value, 5 bits for R, 6 bits for G + and 5 bits for B. + */ + IA_CSS_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */ + IA_CSS_ISYS_FRAME_FORMAT_RGBA888,/* 32 bit RGBA, 1 plane, + A=Alpha (alpha is unused) + */ + IA_CSS_ISYS_FRAME_FORMAT_QPLANE6,/* Internal, for advanced ISP */ + IA_CSS_ISYS_FRAME_FORMAT_BINARY_8,/* byte stream, used for jpeg. */ + N_IA_CSS_ISYS_FRAME_FORMAT +}; +/* Temporary for driver compatibility */ +#define IA_CSS_ISYS_FRAME_FORMAT_RAW (IA_CSS_ISYS_FRAME_FORMAT_RAW16) + + +/** + * Supported MIPI data type. Keep in sync array in ia_css_isys_private.c + */ +enum ia_css_isys_mipi_data_type { + /** SYNCHRONIZATION SHORT PACKET DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_FRAME_START_CODE = 0x00, + IA_CSS_ISYS_MIPI_DATA_TYPE_FRAME_END_CODE = 0x01, + IA_CSS_ISYS_MIPI_DATA_TYPE_LINE_START_CODE = 0x02, /* Optional */ + IA_CSS_ISYS_MIPI_DATA_TYPE_LINE_END_CODE = 0x03, /* Optional */ + /** Reserved 0x04-0x07 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x04 = 0x04, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x05 = 0x05, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x06 = 0x06, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x07 = 0x07, + /** GENERIC SHORT PACKET DATA TYPES */ + /** They are used to keep the timing information for the + * opening/closing of shutters, triggering of flashes and etc. + */ + /* Generic Short Packet Code 1 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT1 = 0x08, + /* Generic Short Packet Code 2 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT2 = 0x09, + /* Generic Short Packet Code 3 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT3 = 0x0A, + /* Generic Short Packet Code 4 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT4 = 0x0B, + /* Generic Short Packet Code 5 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT5 = 0x0C, + /* Generic Short Packet Code 6 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT6 = 0x0D, + /* Generic Short Packet Code 7 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT7 = 0x0E, + /* Generic Short Packet Code 8 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_GENERIC_SHORT8 = 0x0F, + /** GENERIC LONG PACKET DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_NULL = 0x10, + IA_CSS_ISYS_MIPI_DATA_TYPE_BLANKING_DATA = 0x11, + /* Embedded 8-bit non Image Data */ + IA_CSS_ISYS_MIPI_DATA_TYPE_EMBEDDED = 0x12, + /** Reserved 0x13-0x17 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x13 = 0x13, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x14 = 0x14, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x15 = 0x15, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x16 = 0x16, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x17 = 0x17, + /** YUV DATA TYPES */ + /* 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8 = 0x18, + /* 10 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_10 = 0x19, + /* 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8_LEGACY = 0x1A, + /** Reserved 0x1B */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x1B = 0x1B, + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_8_SHIFT = 0x1C, + /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV420_10_SHIFT = 0x1D, + /* UYVY..UVYV, 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV422_8 = 0x1E, + /* UYVY..UVYV, 10 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_YUV422_10 = 0x1F, + /** RGB DATA TYPES */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_444 = 0x20, + /* BGR..BGR, 5 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_555 = 0x21, + /* BGR..BGR, 5 bits B and R, 6 bits G */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_565 = 0x22, + /* BGR..BGR, 6 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_666 = 0x23, + /* BGR..BGR, 8 bits per subpixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RGB_888 = 0x24, + /** Reserved 0x25-0x27 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x25 = 0x25, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x26 = 0x26, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x27 = 0x27, + /** RAW DATA TYPES */ + /* RAW data, 6 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_6 = 0x28, + /* RAW data, 7 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_7 = 0x29, + /* RAW data, 8 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_8 = 0x2A, + /* RAW data, 10 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_10 = 0x2B, + /* RAW data, 12 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_12 = 0x2C, + /* RAW data, 14 bits per pixel */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_14 = 0x2D, + /** Reserved 0x2E-2F are used with assigned meaning */ + /* RAW data, 16 bits per pixel, not specified in CSI-MIPI standard */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RAW_16 = 0x2E, + /* Binary byte stream, which is target at JPEG, not specified in + * CSI-MIPI standard + */ + IA_CSS_ISYS_MIPI_DATA_TYPE_BINARY_8 = 0x2F, + /** USER DEFINED 8-BIT DATA TYPES */ + /** For example, the data transmitter (e.g. the SoC sensor) can keep + * the JPEG data as the User Defined Data Type 4 and the MPEG data as + * the User Defined Data Type 7. + */ + /* User defined 8-bit data type 1 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF1 = 0x30, + /* User defined 8-bit data type 2 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF2 = 0x31, + /* User defined 8-bit data type 3 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF3 = 0x32, + /* User defined 8-bit data type 4 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF4 = 0x33, + /* User defined 8-bit data type 5 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF5 = 0x34, + /* User defined 8-bit data type 6 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF6 = 0x35, + /* User defined 8-bit data type 7 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF7 = 0x36, + /* User defined 8-bit data type 8 */ + IA_CSS_ISYS_MIPI_DATA_TYPE_USER_DEF8 = 0x37, + /** Reserved 0x38-0x3F */ + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x38 = 0x38, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x39 = 0x39, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3A = 0x3A, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3B = 0x3B, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3C = 0x3C, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3D = 0x3D, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3E = 0x3E, + IA_CSS_ISYS_MIPI_DATA_TYPE_RESERVED_0x3F = 0x3F, + + /* Keep always last and max value */ + N_IA_CSS_ISYS_MIPI_DATA_TYPE = 0x40 +}; + +/** enum ia_css_isys_pin_type: output pin buffer types. + * Buffers can be queued and de-queued to hand them over between IA and ISYS + */ +enum ia_css_isys_pin_type { + /* Captured as MIPI packets */ + IA_CSS_ISYS_PIN_TYPE_MIPI = 0, + /* Captured through the ISApf (with/without ISA) + * and the non-scaled output path + */ + IA_CSS_ISYS_PIN_TYPE_RAW_NS, + /* Captured through the ISApf + ISA and the scaled output path */ + IA_CSS_ISYS_PIN_TYPE_RAW_S, + /* Captured through the SoC path */ + IA_CSS_ISYS_PIN_TYPE_RAW_SOC, + /* Reserved for future use, maybe short packets */ + IA_CSS_ISYS_PIN_TYPE_METADATA_0, + /* Reserved for future use */ + IA_CSS_ISYS_PIN_TYPE_METADATA_1, + /* Legacy (non-PIV2), used for the AWB stats */ + IA_CSS_ISYS_PIN_TYPE_AWB_STATS, + /* Legacy (non-PIV2), used for the AF stats */ + IA_CSS_ISYS_PIN_TYPE_AF_STATS, + /* Legacy (non-PIV2), used for the AE stats */ + IA_CSS_ISYS_PIN_TYPE_HIST_STATS, + /* Used for the PAF FF*/ + IA_CSS_ISYS_PIN_TYPE_PAF_FF, + /* Keep always last and max value */ + N_IA_CSS_ISYS_PIN_TYPE +}; + +/** + * enum ia_css_isys_isl_use. Describes the ISL/ISA use + * (ISAPF path in after BXT A0) + */ +enum ia_css_isys_isl_use { + IA_CSS_ISYS_USE_NO_ISL_NO_ISA = 0, + IA_CSS_ISYS_USE_SINGLE_DUAL_ISL, + IA_CSS_ISYS_USE_SINGLE_ISA, + N_IA_CSS_ISYS_USE +}; + +/** + * enum ia_css_isys_mipi_store_mode. Describes if long MIPI packets reach MIPI + * SRAM with the long packet header or not. + * if not, then only option is to capture it with pin type MIPI. + */ +enum ia_css_isys_mipi_store_mode { + IA_CSS_ISYS_MIPI_STORE_MODE_NORMAL = 0, + IA_CSS_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER, + N_IA_CSS_ISYS_MIPI_STORE_MODE +}; + +/** + * enum ia_css_isys_mipi_dt_rename_mode. Describes if long MIPI packets have + * DT with some other DT format. + */ +enum ia_css_isys_mipi_dt_rename_mode { + IA_CSS_ISYS_MIPI_DT_NO_RENAME = 0, + IA_CSS_ISYS_MIPI_DT_RENAMED_MODE, + N_IA_CSS_ISYS_MIPI_DT_MODE +}; + +/** + * enum ia_css_isys_type_paf. Describes the Type of PAF enabled + * (PAF path in after cnlB0) + */ +enum ia_css_isys_type_paf { + /* PAF data not present */ + IA_CSS_ISYS_TYPE_NO_PAF = 0, + /* Type 2 sensor types, PAF coming separately from Image Frame */ + /* PAF data in interleaved format(RLRL or LRLR)*/ + IA_CSS_ISYS_TYPE_INTERLEAVED_PAF, + /* PAF data in non-interleaved format(LL/RR or RR/LL) */ + IA_CSS_ISYS_TYPE_NON_INTERLEAVED_PAF, + /* Type 3 sensor types , PAF data embedded in Image Frame*/ + /* Frame Embedded PAF in interleaved format(RLRL or LRLR)*/ + IA_CSS_ISYS_TYPE_FRAME_EMB_INTERLEAVED_PAF, + /* Frame Embedded PAF non-interleaved format(LL/RR or RR/LL)*/ + IA_CSS_ISYS_TYPE_FRAME_EMB_NON_INTERLEAVED_PAF, + N_IA_CSS_ISYS_TYPE_PAF +}; + +/** + * enum ia_css_isys_cropping_location. Enumerates the cropping locations + * in ISYS + */ +enum ia_css_isys_cropping_location { + /* Cropping executed in ISAPF (mainly), ISAPF preproc (odd column) and + * MIPI STR2MMIO (odd row) + */ + IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA = 0, + /* BXT A0 legacy mode which will never be implemented */ + IA_CSS_ISYS_CROPPING_LOCATION_RESERVED_1, + /* Cropping executed in StreamPifConv in the ISA output for + * RAW_NS pin + */ + IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED, + /* Cropping executed in StreamScaledPifConv in the ISA output for + * RAW_S pin + */ + IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED, + N_IA_CSS_ISYS_CROPPING_LOCATION +}; + +/** + * enum ia_css_isys_resolution_info. Describes the resolution, required to + * setup the various ISA GP registers. + */ +enum ia_css_isys_resolution_info { + /* Scaled ISA output resolution before the + * StreamScaledPifConv cropping + */ + IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED = 0, + /* Non-Scaled ISA output resolution before the + * StreamPifConv cropping + */ + IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED, + N_IA_CSS_ISYS_RESOLUTION_INFO +}; + +/** + * enum ia_css_isys_error. Describes the error type detected by the FW + */ +enum ia_css_isys_error { + IA_CSS_ISYS_ERROR_NONE = 0, /* No details */ + IA_CSS_ISYS_ERROR_FW_INTERNAL_CONSISTENCY, /* enum */ + IA_CSS_ISYS_ERROR_HW_CONSISTENCY, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION, /* enum */ + IA_CSS_ISYS_ERROR_INSUFFICIENT_RESOURCES, /* enum */ + IA_CSS_ISYS_ERROR_HW_REPORTED_STR2MMIO, /* HW code */ + IA_CSS_ISYS_ERROR_HW_REPORTED_SIG2CIO, /* HW code */ + IA_CSS_ISYS_ERROR_SENSOR_FW_SYNC, /* enum */ + IA_CSS_ISYS_ERROR_STREAM_IN_SUSPENSION, /* FW code */ + IA_CSS_ISYS_ERROR_RESPONSE_QUEUE_FULL, /* FW code */ + N_IA_CSS_ISYS_ERROR +}; + +/** + * enum ia_css_proxy_error. Describes the error type for the proxy detected by + * the FW + */ +enum ia_css_proxy_error { + IA_CSS_PROXY_ERROR_NONE = 0, + IA_CSS_PROXY_ERROR_INVALID_WRITE_REGION, + IA_CSS_PROXY_ERROR_INVALID_WRITE_OFFSET, + N_IA_CSS_PROXY_ERROR +}; + +#endif /* __IA_CSS_ISYSAPI_FW_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h new file mode 100644 index 000000000000..bc056157cedb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_fw_version.h @@ -0,0 +1,21 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_FW_VERSION_H +#define __IA_CSS_ISYSAPI_FW_VERSION_H + +/* ISYSAPI FW VERSION is taken from Makefile for FW tests */ +#define BXT_FW_RELEASE_VERSION ISYS_FIRMWARE_VERSION + +#endif /* __IA_CSS_ISYSAPI_FW_VERSION_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h new file mode 100644 index 000000000000..c002b33bdfaf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_defs.h @@ -0,0 +1,113 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H +#define __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H + +#include "ia_css_isysapi_proxy_region_types.h" + +/* + * Definitions for IPU4_B0_PROXY_INT + */ + +#if defined(IPU4_B0_PROXY_INT) + +/** + * enum ipu4_b0_ia_css_proxy_write_region. Provides the list of regions for ipu4B0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4_b0_ia_css_proxy_write_region { + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_0_ERROR_FILL_RATE = 0, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_1_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_2_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_3_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_4_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_5_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_6_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_7_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_8_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_9_ERROR_FILL_RATE, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_URGENT_THRESHOLD, + IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_CRITICAL_THRESHOLD, + N_IPU4_B0_IA_CSS_PROXY_WRITE_REGION +}; + +struct ia_css_proxy_write_region_description ipu4_b0_reg_write_desc[N_IPU4_B0_IA_CSS_PROXY_WRITE_REGION] = { + /* base_addr, offset */ + {0x64128, /*input_system_csi2_logic_s2m_a_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_0_ERROR_FILL_RATE*/ + {0x65128, /*input_system_csi2_logic_s2m_b_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_1_ERROR_FILL_RATE*/ + {0x66128, /*input_system_csi2_logic_s2m_c_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_2_ERROR_FILL_RATE*/ + {0x67128, /*input_system_csi2_logic_s2m_d_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_3_ERROR_FILL_RATE*/ + {0x6C128, /*input_system_csi2_3ph_logic_s2m_a_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_4_ERROR_FILL_RATE*/ + {0x6C928, /*input_system_csi2_3ph_logic_s2m_b_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_5_ERROR_FILL_RATE*/ + {0x6D128, /*input_system_csi2_3ph_logic_s2m_0_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_6_ERROR_FILL_RATE*/ + {0x6D928, /*input_system_csi2_3ph_logic_s2m_1_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_7_ERROR_FILL_RATE*/ + {0x6E128, /*input_system_csi2_3ph_logic_s2m_2_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_8_ERROR_FILL_RATE*/ + {0x6E928, /*input_system_csi2_3ph_logic_s2m_3_stream2mmio_err_mode_dc_ctrl_reg_id*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_STR2MMIO_MIPI_9_ERROR_FILL_RATE*/ + {0x7800C, /*input_system_unis_logic_gda_irq_urgent_threshold*/ 4}, /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_URGENT_THRESHOLD*/ + {0x78010, /*input_system_unis_logic_gda_irq_critical_threshold*/ 4} /*IPU4_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IRQ_CRITICAL_THRESHOLD*/ +}; + +#endif /*defined(IPU4_B0_PROXY_INT)*/ + +/* + * Definitions for IPU4P_A0_PROXY_INT + */ + +#if defined(IPU4P_A0_PROXY_INT) + +/** + * enum ipu4p_a0_ia_css_proxy_write_region. Provides the list of regions for ipu4pA0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4p_a0_ia_css_proxy_write_region { + N_IPU4P_A0_IA_CSS_PROXY_WRITE_REGION +}; + +#define IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE + +#ifndef IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE +struct ia_css_proxy_write_region_description ipu4p_a0_reg_write_desc[N_IPU4P_A0_IA_CSS_PROXY_WRITE_REGION] = { +} +#endif /*IPU4P_A0_NO_PROXY_WRITE_REGION_AVAILABLE*/ + +#endif /*defined(IPU4P_A0_PROXY_INT)*/ + +/* + * Definitions for IPU4P_B0_PROXY_INT + */ + +#if defined(IPU4P_B0_PROXY_INT) + +/** + * enum ipu4p_b0_ia_css_proxy_write_region. Provides the list of regions for ipu4pB0 that + * can be accessed (for writing purpose) through the proxy interface + */ +enum ipu4p_b0_ia_css_proxy_write_region { + IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IWAKE_THRESHOLD = 0, + IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_ENABLE_IWAKE, + N_IPU4P_B0_IA_CSS_PROXY_WRITE_REGION +}; + +struct ia_css_proxy_write_region_description ipu4p_b0_reg_write_desc[N_IPU4P_B0_IA_CSS_PROXY_WRITE_REGION] = { + /* base_addr, max_offset */ + /*input_system_unis_logic_gda_iwake_threshold*/ + {0x78014, 4}, /*IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_IWAKE_THRESHOLD*/ + /*input_system_unis_logic_gda_enable_iwake*/ + {0x7801C, 4} /*IPU4P_B0_IA_CSS_PROXY_WRITE_REGION_GDA_ENABLE_IWAKE*/ +}; + +#endif /*defined(IPU4P_B0_PROXY_INT)*/ + +#endif /* __IA_CSS_ISYSAPI_PROXY_REGION_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h new file mode 100644 index 000000000000..045f089e5a4c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_proxy_region_types.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H +#define __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H + + +struct ia_css_proxy_write_region_description { + uint32_t base_addr; + uint32_t offset; +}; + +#endif /* __IA_CSS_ISYSAPI_PROXY_REGION_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_types.h new file mode 100644 index 000000000000..481a7dc7b481 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/interface/ia_css_isysapi_types.h @@ -0,0 +1,349 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_TYPES_H +#define __IA_CSS_ISYSAPI_TYPES_H + +#include "ia_css_isysapi_fw_types.h" +#include "type_support.h" + +#include "ia_css_return_token.h" +#include "ia_css_output_buffer.h" +#include "ia_css_input_buffer.h" +#include "ia_css_terminal_defs.h" + +/** + * struct ia_css_isys_buffer_partition - buffer partition information + * @num_gda_pages: Number of virtual gda pages available for each virtual stream + */ +struct ia_css_isys_buffer_partition { + unsigned int num_gda_pages[STREAM_ID_MAX]; +}; + +/** + * This should contain the driver specified info for sys + */ +struct ia_css_driver_sys_config { + unsigned int ssid; + unsigned int mmid; + unsigned int num_send_queues; /* # of MSG send queues */ + unsigned int num_recv_queues; /* # of MSG recv queues */ + unsigned int send_queue_size; /* max # tokens per queue */ + unsigned int recv_queue_size; /* max # tokens per queue */ + + unsigned int icache_prefetch; /* enable prefetching for SPC */ +}; + +/** + * This should contain the driver specified info for proxy write queues + */ +struct ia_css_driver_proxy_config { + /* max # tokens per PROXY send/recv queue. + * Proxy queues are used for write access purpose + */ + unsigned int proxy_write_queue_size; +}; + + /** + * struct ia_css_isys_device_cfg_data - ISYS device configuration data + * @driver_sys + * @buffer_partition: Information required for the virtual SRAM + * space partition of the streams. + * @driver_proxy + * @secure: Driver needs to set 'secure' to indicate the intention + * when invoking ia_css_isys_context_create() in + * HAS_DUAL_CMD_CTX_SUPPORT case. If 'true', it's for + * secure case. + */ +struct ia_css_isys_device_cfg_data { + struct ia_css_driver_sys_config driver_sys; + struct ia_css_isys_buffer_partition buffer_partition; + struct ia_css_driver_proxy_config driver_proxy; + bool secure; + unsigned vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +/** + * struct ia_css_isys_resolution: Generic resolution structure. + * @Width + * @Height + */ +struct ia_css_isys_resolution { + unsigned int width; + unsigned int height; +}; + +/** + * struct ia_css_isys_output_pin_payload + * @out_buf_id: Points to output pin buffer - buffer identifier + * @addr: Points to output pin buffer - CSS Virtual Address + * @compressed: Request frame compression (1), or not (0) + * This must be the same as ia_css_isys_output_pin_info::reserve_compression + */ +struct ia_css_isys_output_pin_payload { + ia_css_return_token out_buf_id; + ia_css_output_buffer_css_address addr; + unsigned int compress; +}; + +/** + * struct ia_css_isys_output_pin_info + * @input_pin_id: input pin id/index which is source of + * the data for this output pin + * @output_res: output pin resolution + * @stride: output stride in Bytes (not valid for statistics) + * @pt: pin type + * @ft: frame format type + * @watermark_in_lines: pin watermark level in lines + * @send_irq: assert if pin event should trigger irq + * @link_id: identifies PPG to connect to, link_id = 0 implies offline + * while link_id > 0 implies buffer_chasing or online mode + * can be entered. + * @reserve_compression: Reserve compression resources for pin. + * @payload_buf_size: Minimum size in Bytes of all buffers that will be supplied for capture + * on this pin (i.e. addressed by ia_css_isys_output_pin_payload::addr) + */ +struct ia_css_isys_output_pin_info { + unsigned int input_pin_id; + struct ia_css_isys_resolution output_res; + unsigned int stride; + enum ia_css_isys_pin_type pt; + enum ia_css_isys_frame_format_type ft; + unsigned int watermark_in_lines; + unsigned int send_irq; + enum ia_css_isys_link_id link_id; + unsigned int reserve_compression; + unsigned int payload_buf_size; +}; + +/** + * struct ia_css_isys_param_pin + * @param_buf_id: Points to param buffer - buffer identifier + * @addr: Points to param buffer - CSS Virtual Address + */ +struct ia_css_isys_param_pin { + ia_css_return_token param_buf_id; + ia_css_input_buffer_css_address addr; +}; + +/** + * struct ia_css_isys_input_pin_info + * @input_res: input resolution + * @dt: mipi data type + * @mipi_store_mode: defines if legacy long packet header will be stored or + * discarded if discarded, output pin pin type for this + * input pin can only be MIPI + * @dt_rename_mode: defines if MIPI data is encapsulated in some other + * data type + * @mapped_dt: Encapsulating in mipi data type(what sensor sends) + */ +struct ia_css_isys_input_pin_info { + struct ia_css_isys_resolution input_res; + enum ia_css_isys_mipi_data_type dt; + enum ia_css_isys_mipi_store_mode mipi_store_mode; + enum ia_css_isys_mipi_dt_rename_mode dt_rename_mode; + enum ia_css_isys_mipi_data_type mapped_dt; +}; + +/** + * struct ia_css_isys_isa_cfg. Describes the ISA cfg + */ +struct ia_css_isys_isa_cfg { + /* Following sets resolution information neeed by the IS GP registers, + * For index IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED, + * it is needed when there is RAW_NS pin + * For index IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED, + * it is needed when there is RAW_S pin + */ + struct ia_css_isys_resolution isa_res[N_IA_CSS_ISYS_RESOLUTION_INFO]; + /* acc id 0, set if process required */ + unsigned int blc_enabled; + /* acc id 1, set if process required */ + unsigned int lsc_enabled; + /* acc id 2, set if process required */ + unsigned int dpc_enabled; + /* acc id 3, set if process required */ + unsigned int downscaler_enabled; + /* acc id 4, set if process required */ + unsigned int awb_enabled; + /* acc id 5, set if process required */ + unsigned int af_enabled; + /* acc id 6, set if process required */ + unsigned int ae_enabled; + /* acc id 7, disabled, or type of paf enabled*/ + enum ia_css_isys_type_paf paf_type; + /* Send irq for any statistics buffers which got completed */ + unsigned int send_irq_stats_ready; + /* Send response for any statistics buffers which got completed */ + unsigned int send_resp_stats_ready; +}; + +/** + * struct ia_css_isys_cropping - cropping coordinates + * Left/Top offsets are INCLUDED + * Right/Bottom offsets are EXCLUDED + * Horizontal: [left_offset,right_offset) + * Vertical: [top_offset,bottom_offset) + * Padding is supported + */ +struct ia_css_isys_cropping { + int top_offset; + int left_offset; + int bottom_offset; + int right_offset; +}; + + /** + * struct ia_css_isys_stream_cfg_data + * ISYS stream configuration data structure + * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1 + * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel) + * @isl_use: indicates whether stream requires ISL and how + * @compfmt: de-compression setting for User Defined Data + * @isa_cfg: details about what ACCs are active if ISA is used + * @crop: defines cropping resolution for the + * maximum number of input pins which can be cropped, + * it is directly mapped to the HW devices + * @send_irq_sof_discarded: send irq on discarded frame sof response + * - if '1' it will override the send_resp_sof_discarded and send + * the response + * - if '0' the send_resp_sof_discarded will determine whether to + * send the response + * @send_irq_eof_discarded: send irq on discarded frame eof response + * - if '1' it will override the send_resp_eof_discarded and send + * the response + * - if '0' the send_resp_eof_discarded will determine whether to + * send the response + * @send_resp_sof_discarded: send response for discarded frame sof detected, + * used only when send_irq_sof_discarded is '0' + * @send_resp_eof_discarded: send response for discarded frame eof detected, + * used only when send_irq_eof_discarded is '0' + * @the rest: input/output pin descriptors + */ +struct ia_css_isys_stream_cfg_data { + enum ia_css_isys_stream_source src; + enum ia_css_isys_mipi_vc vc; + enum ia_css_isys_isl_use isl_use; + unsigned int compfmt; + struct ia_css_isys_isa_cfg isa_cfg; + struct ia_css_isys_cropping crop[N_IA_CSS_ISYS_CROPPING_LOCATION]; + unsigned int send_irq_sof_discarded; + unsigned int send_irq_eof_discarded; + unsigned int send_resp_sof_discarded; + unsigned int send_resp_eof_discarded; + unsigned int nof_input_pins; + unsigned int nof_output_pins; + struct ia_css_isys_input_pin_info input_pins[MAX_IPINS]; + struct ia_css_isys_output_pin_info output_pins[MAX_OPINS]; +}; + +/** + * struct ia_css_isys_frame_buff_set - frame buffer set + * @output_pins: output pin addresses + * @process_group_light: process_group_light buffer address + * @send_irq_sof: send irq on frame sof response + * - if '1' it will override the send_resp_sof and send + * the response + * - if '0' the send_resp_sof will determine whether to send + * the response + * @send_irq_eof: send irq on frame eof response + * - if '1' it will override the send_resp_eof and send + * the response + * - if '0' the send_resp_eof will determine whether to send + * the response + * @send_resp_sof: send response for frame sof detected, + * used only when send_irq_sof is '0' + * @send_resp_eof: send response for frame eof detected, + * used only when send_irq_eof is '0' + * @frame_counter: frame number associated with this buffer set. + */ +struct ia_css_isys_frame_buff_set { + struct ia_css_isys_output_pin_payload output_pins[MAX_OPINS]; + struct ia_css_isys_param_pin process_group_light; + unsigned int send_irq_sof; + unsigned int send_irq_eof; + unsigned int send_irq_capture_ack; + unsigned int send_irq_capture_done; + unsigned int send_resp_sof; + unsigned int send_resp_eof; + uint8_t frame_counter; +}; + +/** + * struct ia_css_isys_resp_info + * @type: response type + * @stream_handle: stream id the response corresponds to + * @timestamp: Time information for event if available + * @error: error code if something went wrong + * @error_details: depending on error code, it may contain additional + * error info + * @pin: this var is valid for pin event related responses, + * contains pin addresses + * @pin_id: this var is valid for pin event related responses, + * contains pin id that the pin payload corresponds to + * @process_group_light: this var is valid for stats ready related responses, + * contains process group addresses + * @acc_id: this var is valid for stats ready related responses, + * contains accelerator id that finished producing + * all related statistics + * @frame_counter: valid for STREAM_START_AND_CAPTURE_DONE, + * STREAM_CAPTURE_DONE and STREAM_CAPTURE_DISCARDED + * @written_direct: indicates if frame was written direct (online mode) or to DDR. + */ +struct ia_css_isys_resp_info { + enum ia_css_isys_resp_type type; + unsigned int stream_handle; + unsigned int timestamp[2]; + enum ia_css_isys_error error; + unsigned int error_details; + struct ia_css_isys_output_pin_payload pin; + unsigned int pin_id; + struct ia_css_isys_param_pin process_group_light; + unsigned int acc_id; + uint8_t frame_counter; + uint8_t written_direct; +}; + +/** + * struct ia_css_proxy_write_req_val + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @region_index: region id for the write request + * @offset: Offset to the specific register within the region + * @value: Value to be written to register + */ +struct ia_css_proxy_write_req_val { + uint32_t request_id; + uint32_t region_index; + uint32_t offset; + uint32_t value; +}; + +/** + * struct ia_css_proxy_write_req_resp + * @request_id: Unique identifier for the write request + * (in case multiple write requests are issued for same register) + * @error: error code if something went wrong + * @error_details: error detail includes either offset or region index + * information which caused proxy request to be rejected + * (invalid access request) + */ +struct ia_css_proxy_write_req_resp { + uint32_t request_id; + enum ia_css_proxy_error error; + uint32_t error_details; +}; + + +#endif /* __IA_CSS_ISYSAPI_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/isysapi.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/isysapi.mk new file mode 100644 index 000000000000..0d06298f9acb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/isysapi.mk @@ -0,0 +1,77 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is ISYSAPI + +include $(MODULES_DIR)/config/isys/subsystem_$(IPU_SYSVER).mk + +ISYSAPI_DIR=$${MODULES_DIR}/isysapi + +ISYSAPI_INTERFACE=$(ISYSAPI_DIR)/interface +ISYSAPI_SOURCES=$(ISYSAPI_DIR)/src +ISYSAPI_EXTINCLUDE=$${MODULES_DIR}/support +ISYSAPI_EXTINTERFACE=$${MODULES_DIR}/syscom/interface + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_public.c + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_private.c + +# ISYSAPI Trace Log Level = ISYSAPI_TRACE_LOG_LEVEL_NORMAL +# Other options are [ISYSAPI_TRACE_LOG_LEVEL_OFF, ISYSAPI_TRACE_LOG_LEVEL_DEBUG] +ifndef ISYSAPI_TRACE_CONFIG_HOST + ISYSAPI_TRACE_CONFIG_HOST=ISYSAPI_TRACE_LOG_LEVEL_NORMAL +endif +ifndef ISYSAPI_TRACE_CONFIG_FW + ISYSAPI_TRACE_CONFIG_FW=ISYSAPI_TRACE_LOG_LEVEL_NORMAL +endif + +ISYSAPI_HOST_CPPFLAGS += -DISYSAPI_TRACE_CONFIG=$(ISYSAPI_TRACE_CONFIG_HOST) +ISYSAPI_FW_CPPFLAGS += -DISYSAPI_TRACE_CONFIG=$(ISYSAPI_TRACE_CONFIG_FW) + +ISYSAPI_HOST_FILES += $(ISYSAPI_SOURCES)/ia_css_isys_public_trace.c + +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_INTERFACE) +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_EXTINCLUDE) +ISYSAPI_HOST_CPPFLAGS += -I$(ISYSAPI_EXTINTERFACE) +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include/default_system +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/include/ipu/dai +ISYSAPI_HOST_CPPFLAGS += -I$(HIVESDK)/include/ipu + +ISYSAPI_FW_FILES += $(ISYSAPI_SOURCES)/isys_fw.c +ISYSAPI_FW_FILES += $(ISYSAPI_SOURCES)/isys_fw_utils.c + +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_INTERFACE) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_SOURCES)/$(IPU_SYSVER) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_EXTINCLUDE) +ISYSAPI_FW_CPPFLAGS += -I$(ISYSAPI_EXTINTERFACE) +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/systems/ipu_system/dai/include/default_system +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/include/ipu/dai +ISYSAPI_FW_CPPFLAGS += -I$(HIVESDK)/include/ipu + +ISYSAPI_FW_CPPFLAGS += -DWA_HSD1805168877=$(WA_HSD1805168877) + +ISYSAPI_HOST_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) + +ifeq ($(ISYS_HAS_DUAL_CMD_CTX_SUPPORT), 1) +ISYSAPI_HOST_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(ISYS_HAS_DUAL_CMD_CTX_SUPPORT) +ISYSAPI_FW_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(ISYS_HAS_DUAL_CMD_CTX_SUPPORT) +endif + +ifdef AB_CONFIG_ARRAY_SIZE +ISYSAPI_FW_CPPFLAGS += -DAB_CONFIG_ARRAY_SIZE=$(AB_CONFIG_ARRAY_SIZE) +else +ISYSAPI_FW_CPPFLAGS += -DAB_CONFIG_ARRAY_SIZE=1 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.c new file mode 100644 index 000000000000..8297a1ff2d1b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.c @@ -0,0 +1,980 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_isys_private.h" +/* The following is needed for the contained data types */ +#include "ia_css_isys_fw_bridged_types.h" +#include "ia_css_isysapi_types.h" +#include "ia_css_syscom_config.h" +/* + * The following header file is needed for the + * stddef.h (NULL), + * limits.h (CHAR_BIT definition). + */ +#include "type_support.h" +#include "error_support.h" +#include "ia_css_isysapi_trace.h" +#include "misc_support.h" +#include "cpu_mem_support.h" +#include "storage_class.h" + +#include "ia_css_shared_buffer_cpu.h" + +/* + * defines how many stream cfg host may sent concurrently + * before receiving the stream ack + */ +#define STREAM_CFG_BUFS_PER_MSG_QUEUE (1) +#define NEXT_FRAME_BUFS_PER_MSG_QUEUE \ + (ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_MSG] + 4 + 1) +/* + * There is an edge case that host has filled the full queue + * with capture requests (ctx->send_queue_size), + * SP reads and HW-queues all of them (4), + * while in the meantime host continues queueing capture requests + * without checking for responses which SP will have sent with each HW-queue + * capture request (if it does then the 4 is much more improbable to appear, + * but still not impossible). + * After this, host tries to queue an extra capture request + * even though there is no space in the msg queue because msg queue + * is checked at a later point, so +1 is needed + */ + +/* + * A DT is supported assuming when the MIPI packets + * have the same size even when even/odd lines are different, + * and the size is the average per line + */ +#define IA_CSS_UNSUPPORTED_DATA_TYPE (0) +static const uint32_t +ia_css_isys_extracted_bits_per_pixel_per_mipi_data_type[ + N_IA_CSS_ISYS_MIPI_DATA_TYPE] = { + /* + * Remove Prefix "IA_CSS_ISYS_MIPI_DATA_TYPE_" in comments + * to align with Checkpatch 80 characters requirements + * For detailed comments of each field, please refer to + * definition of enum ia_css_isys_mipi_data_type{} in + * isysapi/interface/ia_css_isysapi_fw_types.h + */ + 64, /* [0x00] FRAME_START_CODE */ + 64, /* [0x01] FRAME_END_CODE */ + 64, /* [0x02] LINE_START_CODE Optional */ + 64, /* [0x03] LINE_END_CODE Optional */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x04] RESERVED_0x04 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x05] RESERVED_0x05 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x06] RESERVED_0x06 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x07] RESERVED_0x07 */ + 64, /* [0x08] GENERIC_SHORT1 */ + 64, /* [0x09] GENERIC_SHORT2 */ + 64, /* [0x0A] GENERIC_SHORT3 */ + 64, /* [0x0B] GENERIC_SHORT4 */ + 64, /* [0x0C] GENERIC_SHORT5 */ + 64, /* [0x0D] GENERIC_SHORT6 */ + 64, /* [0x0E] GENERIC_SHORT7 */ + 64, /* [0x0F] GENERIC_SHORT8 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x10] NULL To be ignored */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x11] BLANKING_DATA To be ignored */ + 8, /* [0x12] EMBEDDED non Image Data */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x13] RESERVED_0x13 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x14] RESERVED_0x14 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x15] RESERVED_0x15 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x16] RESERVED_0x16 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x17] RESERVED_0x17 */ + 12, /* [0x18] YUV420_8 */ + 15, /* [0x19] YUV420_10 */ + 12, /* [0x1A] YUV420_8_LEGACY */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x1B] RESERVED_0x1B */ + 12, /* [0x1C] YUV420_8_SHIFT */ + 15, /* [0x1D] YUV420_10_SHIFT */ + 16, /* [0x1E] YUV422_8 */ + 20, /* [0x1F] YUV422_10 */ + 16, /* [0x20] RGB_444 */ + 16, /* [0x21] RGB_555 */ + 16, /* [0x22] RGB_565 */ + 18, /* [0x23] RGB_666 */ + 24, /* [0x24] RGB_888 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x25] RESERVED_0x25 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x26] RESERVED_0x26 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x27] RESERVED_0x27 */ + 6, /* [0x28] RAW_6 */ + 7, /* [0x29] RAW_7 */ + 8, /* [0x2A] RAW_8 */ + 10, /* [0x2B] RAW_10 */ + 12, /* [0x2C] RAW_12 */ + 14, /* [0x2D] RAW_14 */ + 16, /* [0x2E] RAW_16 */ + 8, /* [0x2F] BINARY_8 */ + 8, /* [0x30] USER_DEF1 */ + 8, /* [0x31] USER_DEF2 */ + 8, /* [0x32] USER_DEF3 */ + 8, /* [0x33] USER_DEF4 */ + 8, /* [0x34] USER_DEF5 */ + 8, /* [0x35] USER_DEF6 */ + 8, /* [0x36] USER_DEF7 */ + 8, /* [0x37] USER_DEF8 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x38] RESERVED_0x38 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x39] RESERVED_0x39 */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3A] RESERVED_0x3A */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3B] RESERVED_0x3B */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3C] RESERVED_0x3C */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3D] RESERVED_0x3D */ + IA_CSS_UNSUPPORTED_DATA_TYPE, /* [0x3E] RESERVED_0x3E */ + IA_CSS_UNSUPPORTED_DATA_TYPE /* [0x3F] RESERVED_0x3F */ +}; + +STORAGE_CLASS_INLINE int get_stream_cfg_buff_slot( + struct ia_css_isys_context *ctx, + int stream_handle, + int stream_cfg_buff_counter) +{ + NOT_USED(ctx); + return (stream_handle * STREAM_CFG_BUFS_PER_MSG_QUEUE) + + stream_cfg_buff_counter; +} + +STORAGE_CLASS_INLINE int get_next_frame_buff_slot( + struct ia_css_isys_context *ctx, + int stream_handle, + int next_frame_buff_counter) +{ + NOT_USED(ctx); + return (stream_handle * NEXT_FRAME_BUFS_PER_MSG_QUEUE) + + next_frame_buff_counter; +} + +STORAGE_CLASS_INLINE void free_comm_buff_shared_mem( + struct ia_css_isys_context *ctx, + int stream_handle, + int stream_cfg_buff_counter, + int next_frame_buff_counter) +{ + int buff_slot; + + /* Initialiser is the current value of stream_handle */ + for (; stream_handle >= 0; stream_handle--) { + /* + * Initialiser is the current value of stream_cfg_buff_counter + */ + for (; stream_cfg_buff_counter >= 0; + stream_cfg_buff_counter--) { + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, stream_cfg_buff_counter); + ia_css_shared_buffer_free( + ctx->ssid, ctx->mmid, + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot]); + } + /* Set for the next iteration */ + stream_cfg_buff_counter = STREAM_CFG_BUFS_PER_MSG_QUEUE - 1; + /* + * Initialiser is the current value of next_frame_buff_counter + */ + for (; next_frame_buff_counter >= 0; + next_frame_buff_counter--) { + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, next_frame_buff_counter); + ia_css_shared_buffer_free( + ctx->ssid, ctx->mmid, + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot]); + } + next_frame_buff_counter = NEXT_FRAME_BUFS_PER_MSG_QUEUE - 1; + } +} + +/* + * ia_css_isys_constr_comm_buff_queue() + */ +int ia_css_isys_constr_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + int stream_handle; + int stream_cfg_buff_counter; + int next_frame_buff_counter; + int buff_slot; + + verifret(ctx, EFAULT); /* Host Consistency */ + + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id = + (ia_css_shared_buffer *) + ia_css_cpu_mem_alloc(ctx-> + num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] * + STREAM_CFG_BUFS_PER_MSG_QUEUE * + sizeof(ia_css_shared_buffer)); + verifret(ctx->isys_comm_buffer_queue.pstream_cfg_buff_id != NULL, + EFAULT); + + ctx->isys_comm_buffer_queue.pnext_frame_buff_id = + (ia_css_shared_buffer *) + ia_css_cpu_mem_alloc(ctx-> + num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] * + NEXT_FRAME_BUFS_PER_MSG_QUEUE * + sizeof(ia_css_shared_buffer)); + if (ctx->isys_comm_buffer_queue.pnext_frame_buff_id == NULL) { + ia_css_cpu_mem_free( + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id); + verifret(0, EFAULT); /* return EFAULT; equivalent */ + } + + for (stream_handle = 0; stream_handle < + (int)ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + stream_handle++) { + /* Initialisation needs to happen here for both loops */ + stream_cfg_buff_counter = 0; + next_frame_buff_counter = 0; + + for (; stream_cfg_buff_counter < STREAM_CFG_BUFS_PER_MSG_QUEUE; + stream_cfg_buff_counter++) { + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, stream_cfg_buff_counter); + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot] = + ia_css_shared_buffer_alloc( + ctx->ssid, ctx->mmid, + sizeof(struct + ia_css_isys_stream_cfg_data_comm)); + if (ctx->isys_comm_buffer_queue.pstream_cfg_buff_id[ + buff_slot] == 0) { + goto SHARED_BUFF_ALLOC_FAILURE; + } + } + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] = 0; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] = 0; + for (; next_frame_buff_counter < + (int)NEXT_FRAME_BUFS_PER_MSG_QUEUE; + next_frame_buff_counter++) { + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, + next_frame_buff_counter); + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot] = + ia_css_shared_buffer_alloc( + ctx->ssid, ctx->mmid, + sizeof(struct + ia_css_isys_frame_buff_set_comm)); + if (ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot] == 0) { + goto SHARED_BUFF_ALLOC_FAILURE; + } + } + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] = 0; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] = 0; + } + + return 0; + +SHARED_BUFF_ALLOC_FAILURE: + /* stream_handle has correct value for calling the free function */ + /* prepare stream_cfg_buff_counter for calling the free function */ + stream_cfg_buff_counter--; + /* prepare next_frame_buff_counter for calling the free function */ + next_frame_buff_counter--; + free_comm_buff_shared_mem( + ctx, + stream_handle, + stream_cfg_buff_counter, + next_frame_buff_counter); + + verifret(0, EFAULT); /* return EFAULT; equivalent */ +} + +/* + * ia_css_isys_force_unmap_comm_buff_queue() + */ +int ia_css_isys_force_unmap_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + int stream_handle; + int buff_slot; + + verifret(ctx, EFAULT); /* Host Consistency */ + + IA_CSS_TRACE_0(ISYSAPI, WARNING, + "ia_css_isys_force_unmap_comm_buff_queue() called\n"); + for (stream_handle = 0; stream_handle < + (int)ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + stream_handle++) { + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]) <= + STREAM_CFG_BUFS_PER_MSG_QUEUE, EPROTO); + for (; ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] < + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle]; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]++) { + IA_CSS_TRACE_1(ISYSAPI, WARNING, + "CSS forced unmapping stream_cfg %d\n", + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]); + buff_slot = get_stream_cfg_buff_slot( + ctx, stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + ia_css_shared_buffer_css_unmap( + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot]); + } + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]) <= + NEXT_FRAME_BUFS_PER_MSG_QUEUE, EPROTO); + for (; ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] < + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle]; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]++) { + IA_CSS_TRACE_1(ISYSAPI, WARNING, + "CSS forced unmapping next_frame %d\n", + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]); + buff_slot = get_next_frame_buff_slot( + ctx, stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + ia_css_shared_buffer_css_unmap( + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot]); + } + } + + return 0; +} + +/* + * ia_css_isys_destr_comm_buff_queue() + */ +int ia_css_isys_destr_comm_buff_queue( + struct ia_css_isys_context *ctx) +{ + verifret(ctx, EFAULT); /* Host Consistency */ + + free_comm_buff_shared_mem( + ctx, + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] - 1, + STREAM_CFG_BUFS_PER_MSG_QUEUE - 1, + NEXT_FRAME_BUFS_PER_MSG_QUEUE - 1); + + ia_css_cpu_mem_free(ctx->isys_comm_buffer_queue.pnext_frame_buff_id); + ia_css_cpu_mem_free(ctx->isys_comm_buffer_queue.pstream_cfg_buff_id); + + return 0; +} + +STORAGE_CLASS_INLINE void resolution_host_to_css( + const struct ia_css_isys_resolution *resolution_host, + struct ia_css_isys_resolution_comm *resolution_css) +{ + resolution_css->width = resolution_host->width; + resolution_css->height = resolution_host->height; +} + +STORAGE_CLASS_INLINE void output_pin_payload_host_to_css( + const struct ia_css_isys_output_pin_payload *output_pin_payload_host, + struct ia_css_isys_output_pin_payload_comm *output_pin_payload_css) +{ + output_pin_payload_css->out_buf_id = + output_pin_payload_host->out_buf_id; + output_pin_payload_css->addr = output_pin_payload_host->addr; +#ifdef ENABLE_DEC400 + output_pin_payload_css->compress = output_pin_payload_host->compress; +#else + output_pin_payload_css->compress = 0; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void output_pin_info_host_to_css( + const struct ia_css_isys_output_pin_info *output_pin_info_host, + struct ia_css_isys_output_pin_info_comm *output_pin_info_css) +{ + output_pin_info_css->input_pin_id = output_pin_info_host->input_pin_id; + resolution_host_to_css( + &output_pin_info_host->output_res, + &output_pin_info_css->output_res); + output_pin_info_css->stride = output_pin_info_host->stride; + output_pin_info_css->pt = output_pin_info_host->pt; + output_pin_info_css->watermark_in_lines = + output_pin_info_host->watermark_in_lines; + output_pin_info_css->send_irq = output_pin_info_host->send_irq; + output_pin_info_css->ft = output_pin_info_host->ft; + output_pin_info_css->link_id = output_pin_info_host->link_id; +#ifdef ENABLE_DEC400 + output_pin_info_css->reserve_compression = output_pin_info_host->reserve_compression; + output_pin_info_css->payload_buf_size = output_pin_info_host->payload_buf_size; +#else + output_pin_info_css->reserve_compression = 0; + /* Though payload_buf_size was added for compression, set sane value for + * payload_buf_size, just in case... + */ + output_pin_info_css->payload_buf_size = + output_pin_info_host->stride * output_pin_info_host->output_res.height; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void param_pin_host_to_css( + const struct ia_css_isys_param_pin *param_pin_host, + struct ia_css_isys_param_pin_comm *param_pin_css) +{ + param_pin_css->param_buf_id = param_pin_host->param_buf_id; + param_pin_css->addr = param_pin_host->addr; +} + +STORAGE_CLASS_INLINE void input_pin_info_host_to_css( + const struct ia_css_isys_input_pin_info *input_pin_info_host, + struct ia_css_isys_input_pin_info_comm *input_pin_info_css) +{ + resolution_host_to_css( + &input_pin_info_host->input_res, + &input_pin_info_css->input_res); + if (input_pin_info_host->dt >= N_IA_CSS_ISYS_MIPI_DATA_TYPE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->dt out of range\n"); + return; + } + if (input_pin_info_host->dt_rename_mode >= N_IA_CSS_ISYS_MIPI_DT_MODE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->dt_rename_mode out of range\n"); + return; + } + /* Mapped DT check if data type renaming is being used*/ + if (input_pin_info_host->dt_rename_mode == IA_CSS_ISYS_MIPI_DT_RENAMED_MODE && + input_pin_info_host->mapped_dt >= N_IA_CSS_ISYS_MIPI_DATA_TYPE) { + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "input_pin_info_host->mapped_dt out of range\n"); + return; + } + input_pin_info_css->dt = input_pin_info_host->dt; + input_pin_info_css->mipi_store_mode = + input_pin_info_host->mipi_store_mode; + input_pin_info_css->bits_per_pix = + ia_css_isys_extracted_bits_per_pixel_per_mipi_data_type[ + input_pin_info_host->dt]; + if (input_pin_info_host->dt_rename_mode == IA_CSS_ISYS_MIPI_DT_RENAMED_MODE) { + input_pin_info_css->mapped_dt = input_pin_info_host->mapped_dt; + } + else { + input_pin_info_css->mapped_dt = N_IA_CSS_ISYS_MIPI_DATA_TYPE; + } +} + +STORAGE_CLASS_INLINE void isa_cfg_host_to_css( + const struct ia_css_isys_isa_cfg *isa_cfg_host, + struct ia_css_isys_isa_cfg_comm *isa_cfg_css) +{ + unsigned int i; + + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) { + resolution_host_to_css(&isa_cfg_host->isa_res[i], + &isa_cfg_css->isa_res[i]); + } + isa_cfg_css->cfg_fields = 0; + ISA_CFG_FIELD_SET(BLC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->blc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(LSC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->lsc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(DPC_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->dpc_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(DOWNSCALER_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->downscaler_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AWB_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->awb_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AF_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->af_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(AE_EN, isa_cfg_css->cfg_fields, + isa_cfg_host->ae_enabled ? 1 : 0); + ISA_CFG_FIELD_SET(PAF_TYPE, isa_cfg_css->cfg_fields, + isa_cfg_host->paf_type); + ISA_CFG_FIELD_SET(SEND_IRQ_STATS_READY, isa_cfg_css->cfg_fields, + isa_cfg_host->send_irq_stats_ready ? 1 : 0); + ISA_CFG_FIELD_SET(SEND_RESP_STATS_READY, isa_cfg_css->cfg_fields, + (isa_cfg_host->send_irq_stats_ready || + isa_cfg_host->send_resp_stats_ready) ? 1 : 0); +} + +STORAGE_CLASS_INLINE void cropping_host_to_css( + const struct ia_css_isys_cropping *cropping_host, + struct ia_css_isys_cropping_comm *cropping_css) +{ + cropping_css->top_offset = cropping_host->top_offset; + cropping_css->left_offset = cropping_host->left_offset; + cropping_css->bottom_offset = cropping_host->bottom_offset; + cropping_css->right_offset = cropping_host->right_offset; + +} + +STORAGE_CLASS_INLINE int stream_cfg_data_host_to_css( + const struct ia_css_isys_stream_cfg_data *stream_cfg_data_host, + struct ia_css_isys_stream_cfg_data_comm *stream_cfg_data_css) +{ + unsigned int i; + + stream_cfg_data_css->src = stream_cfg_data_host->src; + stream_cfg_data_css->vc = stream_cfg_data_host->vc; + stream_cfg_data_css->isl_use = stream_cfg_data_host->isl_use; + stream_cfg_data_css->compfmt = stream_cfg_data_host->compfmt; + stream_cfg_data_css->isa_cfg.cfg_fields = 0; + + switch (stream_cfg_data_host->isl_use) { + case IA_CSS_ISYS_USE_SINGLE_ISA: + isa_cfg_host_to_css(&stream_cfg_data_host->isa_cfg, + &stream_cfg_data_css->isa_cfg); + /* deliberate fall-through */ + case IA_CSS_ISYS_USE_SINGLE_DUAL_ISL: + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) { + cropping_host_to_css(&stream_cfg_data_host->crop[i], + &stream_cfg_data_css->crop[i]); + } + break; + case IA_CSS_ISYS_USE_NO_ISL_NO_ISA: + break; + default: + break; + } + + stream_cfg_data_css->send_irq_sof_discarded = + stream_cfg_data_host->send_irq_sof_discarded ? 1 : 0; + stream_cfg_data_css->send_irq_eof_discarded = + stream_cfg_data_host->send_irq_eof_discarded ? 1 : 0; + stream_cfg_data_css->send_resp_sof_discarded = + stream_cfg_data_host->send_irq_sof_discarded ? + 1 : stream_cfg_data_host->send_resp_sof_discarded; + stream_cfg_data_css->send_resp_eof_discarded = + stream_cfg_data_host->send_irq_eof_discarded ? + 1 : stream_cfg_data_host->send_resp_eof_discarded; + stream_cfg_data_css->nof_input_pins = + stream_cfg_data_host->nof_input_pins; + stream_cfg_data_css->nof_output_pins = + stream_cfg_data_host->nof_output_pins; + for (i = 0; i < stream_cfg_data_host->nof_input_pins; i++) { + input_pin_info_host_to_css( + &stream_cfg_data_host->input_pins[i], + &stream_cfg_data_css->input_pins[i]); + verifret(stream_cfg_data_css->input_pins[i].bits_per_pix, + EINVAL); + } + for (i = 0; i < stream_cfg_data_host->nof_output_pins; i++) { + output_pin_info_host_to_css( + &stream_cfg_data_host->output_pins[i], + &stream_cfg_data_css->output_pins[i]); + } + return 0; +} + +STORAGE_CLASS_INLINE void frame_buff_set_host_to_css( + const struct ia_css_isys_frame_buff_set *frame_buff_set_host, + struct ia_css_isys_frame_buff_set_comm *frame_buff_set_css) +{ + int i; + + for (i = 0; i < MAX_OPINS; i++) { + output_pin_payload_host_to_css( + &frame_buff_set_host->output_pins[i], + &frame_buff_set_css->output_pins[i]); + } + + param_pin_host_to_css(&frame_buff_set_host->process_group_light, + &frame_buff_set_css->process_group_light); + frame_buff_set_css->send_irq_sof = + frame_buff_set_host->send_irq_sof ? 1 : 0; + frame_buff_set_css->send_irq_eof = + frame_buff_set_host->send_irq_eof ? 1 : 0; + frame_buff_set_css->send_irq_capture_done = + (uint8_t)frame_buff_set_host->send_irq_capture_done; + frame_buff_set_css->send_irq_capture_ack = + frame_buff_set_host->send_irq_capture_ack ? 1 : 0; + frame_buff_set_css->send_resp_sof = + frame_buff_set_host->send_irq_sof ? + 1 : frame_buff_set_host->send_resp_sof; + frame_buff_set_css->send_resp_eof = + frame_buff_set_host->send_irq_eof ? + 1 : frame_buff_set_host->send_resp_eof; + frame_buff_set_css->frame_counter = + frame_buff_set_host->frame_counter; +} + +STORAGE_CLASS_INLINE void buffer_partition_host_to_css( + const struct ia_css_isys_buffer_partition *buffer_partition_host, + struct ia_css_isys_buffer_partition_comm *buffer_partition_css) +{ + int i; + + for (i = 0; i < STREAM_ID_MAX; i++) { + buffer_partition_css->num_gda_pages[i] = + buffer_partition_host->num_gda_pages[i]; + } +} + +STORAGE_CLASS_INLINE void output_pin_payload_css_to_host( + const struct ia_css_isys_output_pin_payload_comm * + output_pin_payload_css, + struct ia_css_isys_output_pin_payload *output_pin_payload_host) +{ + output_pin_payload_host->out_buf_id = + output_pin_payload_css->out_buf_id; + output_pin_payload_host->addr = output_pin_payload_css->addr; +#ifdef ENABLE_DEC400 + output_pin_payload_host->compress = output_pin_payload_css->compress; +#else + output_pin_payload_host->compress = 0; +#endif /* ENABLE_DEC400 */ +} + +STORAGE_CLASS_INLINE void param_pin_css_to_host( + const struct ia_css_isys_param_pin_comm *param_pin_css, + struct ia_css_isys_param_pin *param_pin_host) +{ + param_pin_host->param_buf_id = param_pin_css->param_buf_id; + param_pin_host->addr = param_pin_css->addr; + +} + +STORAGE_CLASS_INLINE void resp_info_css_to_host( + const struct ia_css_isys_resp_info_comm *resp_info_css, + struct ia_css_isys_resp_info *resp_info_host) +{ + resp_info_host->type = resp_info_css->type; + resp_info_host->timestamp[0] = resp_info_css->timestamp[0]; + resp_info_host->timestamp[1] = resp_info_css->timestamp[1]; + resp_info_host->stream_handle = resp_info_css->stream_handle; + resp_info_host->error = resp_info_css->error_info.error; + resp_info_host->error_details = + resp_info_css->error_info.error_details; + output_pin_payload_css_to_host( + &resp_info_css->pin, &resp_info_host->pin); + resp_info_host->pin_id = resp_info_css->pin_id; + param_pin_css_to_host(&resp_info_css->process_group_light, + &resp_info_host->process_group_light); + resp_info_host->acc_id = resp_info_css->acc_id; + resp_info_host->frame_counter = resp_info_css->frame_counter; + resp_info_host->written_direct = resp_info_css->written_direct; +} + +/* + * ia_css_isys_constr_fw_stream_cfg() + */ +int ia_css_isys_constr_fw_stream_cfg( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pstream_cfg_fw, + ia_css_shared_buffer *pbuf_stream_cfg_id, + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + ia_css_shared_buffer_cpu_address stream_cfg_cpu_addr; + ia_css_shared_buffer_css_address stream_cfg_css_addr; + int buff_slot; + int retval = 0; + unsigned int wrap_compensation; + const unsigned int wrap_condition = 0xFFFFFFFF; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(pstream_cfg_fw, EFAULT); /* Host Consistency */ + verifret(pbuf_stream_cfg_id, EFAULT); /* Host Consistency */ + verifret(stream_cfg, EFAULT); /* Host Consistency */ + + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle]) < + STREAM_CFG_BUFS_PER_MSG_QUEUE, EPROTO); + buff_slot = get_stream_cfg_buff_slot(ctx, stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + *pbuf_stream_cfg_id = + ctx->isys_comm_buffer_queue.pstream_cfg_buff_id[buff_slot]; + /* Host-FW Consistency */ + verifret(*pbuf_stream_cfg_id, EADDRNOTAVAIL); + + stream_cfg_cpu_addr = + ia_css_shared_buffer_cpu_map(*pbuf_stream_cfg_id); + /* Host-FW Consistency */ + verifret(stream_cfg_cpu_addr, EADDRINUSE); + + retval = stream_cfg_data_host_to_css(stream_cfg, stream_cfg_cpu_addr); + if (retval) + return retval; + + stream_cfg_cpu_addr = + ia_css_shared_buffer_cpu_unmap(*pbuf_stream_cfg_id); + /* Host Consistency */ + verifret(stream_cfg_cpu_addr, EADDRINUSE); + + stream_cfg_css_addr = + ia_css_shared_buffer_css_map(*pbuf_stream_cfg_id); + /* Host Consistency */ + verifret(stream_cfg_css_addr, EADDRINUSE); + + ia_css_shared_buffer_css_update(ctx->mmid, *pbuf_stream_cfg_id); + + *pstream_cfg_fw = stream_cfg_css_addr; + + /* + * cover head wrap around extreme case, + * in which case force tail to wrap around too + * while maintaining diff and modulo + */ + if (ctx->isys_comm_buffer_queue.stream_cfg_queue_head[stream_handle] == + wrap_condition) { + /* Value to be added to both head and tail */ + wrap_compensation = + /* + * Distance of wrap_condition to 0, + * will need to be added for wrapping around head to 0 + */ + (0 - wrap_condition) + + /* + * To force tail to also wrap around, + * since it has to happen concurrently + */ + STREAM_CFG_BUFS_PER_MSG_QUEUE + + /* To preserve the same modulo, + * since the previous will result in head modulo 0 + */ + (wrap_condition % STREAM_CFG_BUFS_PER_MSG_QUEUE); + ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[stream_handle] += + wrap_compensation; + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[stream_handle] += + wrap_compensation; + } + ctx->isys_comm_buffer_queue.stream_cfg_queue_head[stream_handle]++; + + return 0; +} + +/* + * ia_css_isys_constr_fw_next_frame() + */ +int ia_css_isys_constr_fw_next_frame( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pnext_frame_fw, + ia_css_shared_buffer *pbuf_next_frame_id, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + ia_css_shared_buffer_cpu_address next_frame_cpu_addr; + ia_css_shared_buffer_css_address next_frame_css_addr; + int buff_slot; + unsigned int wrap_compensation; + const unsigned int wrap_condition = 0xFFFFFFFF; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(pnext_frame_fw, EFAULT); /* Host Consistency */ + verifret(next_frame, EFAULT); /* Host Consistency */ + verifret(pbuf_next_frame_id, EFAULT); /* Host Consistency */ + + /* For some reason responses are not dequeued in time */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] - + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle]) < + NEXT_FRAME_BUFS_PER_MSG_QUEUE, EPERM); + buff_slot = get_next_frame_buff_slot(ctx, stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + *pbuf_next_frame_id = + ctx->isys_comm_buffer_queue.pnext_frame_buff_id[buff_slot]; + /* Host-FW Consistency */ + verifret(*pbuf_next_frame_id, EADDRNOTAVAIL); + + /* map it in cpu */ + next_frame_cpu_addr = + ia_css_shared_buffer_cpu_map(*pbuf_next_frame_id); + /* Host-FW Consistency */ + verifret(next_frame_cpu_addr, EADDRINUSE); + + frame_buff_set_host_to_css(next_frame, next_frame_cpu_addr); + + /* unmap the buffer from cpu */ + next_frame_cpu_addr = + ia_css_shared_buffer_cpu_unmap(*pbuf_next_frame_id); + /* Host Consistency */ + verifret(next_frame_cpu_addr, EADDRINUSE); + + /* map it to css */ + next_frame_css_addr = + ia_css_shared_buffer_css_map(*pbuf_next_frame_id); + /* Host Consistency */ + verifret(next_frame_css_addr, EADDRINUSE); + + ia_css_shared_buffer_css_update(ctx->mmid, *pbuf_next_frame_id); + + *pnext_frame_fw = next_frame_css_addr; + + /* + * cover head wrap around extreme case, + * in which case force tail to wrap around too + * while maintaining diff and modulo + */ + if (ctx->isys_comm_buffer_queue.next_frame_queue_head[stream_handle] == + wrap_condition) { + /* Value to be added to both head and tail */ + wrap_compensation = + /* + * Distance of wrap_condition to 0, + * will need to be added for wrapping around head to 0 + */ + (0 - wrap_condition) + + /* + * To force tail to also wrap around, + * since it has to happen concurrently + */ + NEXT_FRAME_BUFS_PER_MSG_QUEUE + + /* + * To preserve the same modulo, + * since the previous will result in head modulo 0 + */ + (wrap_condition % NEXT_FRAME_BUFS_PER_MSG_QUEUE); + ctx->isys_comm_buffer_queue. + next_frame_queue_head[stream_handle] += + wrap_compensation; + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[stream_handle] += + wrap_compensation; + } + ctx->isys_comm_buffer_queue.next_frame_queue_head[stream_handle]++; + + return 0; +} + +/* + * ia_css_isys_extract_fw_response() + */ +int ia_css_isys_extract_fw_response( + struct ia_css_isys_context *ctx, + const struct resp_queue_token *token, + struct ia_css_isys_resp_info *received_response) +{ + int buff_slot; + unsigned int css_address; + + verifret(ctx, EFAULT); /* Host Consistency */ + verifret(token, EFAULT); /* Host Consistency */ + verifret(received_response, EFAULT); /* Host Consistency */ + + resp_info_css_to_host(&(token->resp_info), received_response); + + switch (token->resp_info.type) { + case IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + stream_cfg_queue_head[token->resp_info.stream_handle] - + ctx->isys_comm_buffer_queue.stream_cfg_queue_tail[ + token->resp_info.stream_handle]) > 0, EPROTO); + buff_slot = get_stream_cfg_buff_slot(ctx, + token->resp_info.stream_handle, + ctx->isys_comm_buffer_queue. + stream_cfg_queue_tail[ + token->resp_info.stream_handle] % + STREAM_CFG_BUFS_PER_MSG_QUEUE); + verifret((ia_css_shared_buffer)HOST_ADDRESS( + token->resp_info.buf_id) == + ctx->isys_comm_buffer_queue. + pstream_cfg_buff_id[buff_slot], EIO); + ctx->isys_comm_buffer_queue.stream_cfg_queue_tail[ + token->resp_info.stream_handle]++; + css_address = ia_css_shared_buffer_css_unmap( + (ia_css_shared_buffer) + HOST_ADDRESS(token->resp_info.buf_id)); + verifret(css_address, EADDRINUSE); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + /* Host-FW Consistency */ + verifret((ctx->isys_comm_buffer_queue. + next_frame_queue_head[token->resp_info.stream_handle] - + ctx->isys_comm_buffer_queue.next_frame_queue_tail[ + token->resp_info.stream_handle]) > 0, EPROTO); + buff_slot = get_next_frame_buff_slot(ctx, + token->resp_info.stream_handle, + ctx->isys_comm_buffer_queue. + next_frame_queue_tail[ + token->resp_info.stream_handle] % + NEXT_FRAME_BUFS_PER_MSG_QUEUE); + verifret((ia_css_shared_buffer)HOST_ADDRESS( + token->resp_info.buf_id) == + ctx->isys_comm_buffer_queue. + pnext_frame_buff_id[buff_slot], EIO); + ctx->isys_comm_buffer_queue.next_frame_queue_tail[ + token->resp_info.stream_handle]++; + css_address = ia_css_shared_buffer_css_unmap( + (ia_css_shared_buffer) + HOST_ADDRESS(token->resp_info.buf_id)); + verifret(css_address, EADDRINUSE); + break; + default: + break; + } + + return 0; +} + +/* + * ia_css_isys_extract_proxy_response() + */ +int ia_css_isys_extract_proxy_response( + const struct proxy_resp_queue_token *token, + struct ia_css_proxy_write_req_resp *preceived_response) +{ + verifret(token, EFAULT); /* Host Consistency */ + verifret(preceived_response, EFAULT); /* Host Consistency */ + + preceived_response->request_id = token->proxy_resp_info.request_id; + preceived_response->error = token->proxy_resp_info.error_info.error; + preceived_response->error_details = + token->proxy_resp_info.error_info.error_details; + + return 0; +} + +/* + * ia_css_isys_prepare_param() + */ +int ia_css_isys_prepare_param( + struct ia_css_isys_fw_config *isys_fw_cfg, + const struct ia_css_isys_buffer_partition *buf_partition, + const unsigned int num_send_queues[], + const unsigned int num_recv_queues[]) +{ + unsigned int i; + + verifret(isys_fw_cfg, EFAULT); /* Host Consistency */ + verifret(buf_partition, EFAULT); /* Host Consistency */ + verifret(num_send_queues, EFAULT); /* Host Consistency */ + verifret(num_recv_queues, EFAULT); /* Host Consistency */ + + buffer_partition_host_to_css(buf_partition, + &isys_fw_cfg->buffer_partition); + for (i = 0; i < N_IA_CSS_ISYS_QUEUE_TYPE; i++) { + isys_fw_cfg->num_send_queues[i] = num_send_queues[i]; + isys_fw_cfg->num_recv_queues[i] = num_recv_queues[i]; + } + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.h new file mode 100644 index 000000000000..d53fa53c9a81 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_private.h @@ -0,0 +1,156 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_PRIVATE_H +#define __IA_CSS_ISYS_PRIVATE_H + + +#include "type_support.h" +/* Needed for the structure member ia_css_sys_context * sys */ +#include "ia_css_syscom.h" +/* Needed for the definitions of STREAM_ID_MAX */ +#include "ia_css_isysapi.h" +/* The following is needed for the function arguments */ +#include "ia_css_isys_fw_bridged_types.h" + +#include "ia_css_shared_buffer.h" + + +/* Set for the respective error handling */ +#define VERIFY_DEVSTATE 1 + +#if (VERIFY_DEVSTATE != 0) +/** + * enum device_state + */ +enum device_state { + IA_CSS_ISYS_DEVICE_STATE_IDLE = 0, + IA_CSS_ISYS_DEVICE_STATE_CONFIGURED = 1, + IA_CSS_ISYS_DEVICE_STATE_READY = 2 +}; +#endif /* VERIFY_DEVSTATE */ + +/** + * enum stream_state + */ +enum stream_state { + IA_CSS_ISYS_STREAM_STATE_IDLE = 0, + IA_CSS_ISYS_STREAM_STATE_OPENED = 1, + IA_CSS_ISYS_STREAM_STATE_STARTED = 2 +}; + + +/** + * struct ia_css_isys_comm_buffer_queue + */ +struct ia_css_isys_comm_buffer_queue { + ia_css_shared_buffer *pstream_cfg_buff_id; + unsigned int stream_cfg_queue_head[STREAM_ID_MAX]; + unsigned int stream_cfg_queue_tail[STREAM_ID_MAX]; + ia_css_shared_buffer *pnext_frame_buff_id; + unsigned int next_frame_queue_head[STREAM_ID_MAX]; + unsigned int next_frame_queue_tail[STREAM_ID_MAX]; +}; + + +/** + * struct ia_css_isys_context + */ +struct ia_css_isys_context { + struct ia_css_syscom_context *sys; + /* add here any isys specific members that need + to be passed into the isys api functions as input */ + unsigned int ssid; + unsigned int mmid; + unsigned int num_send_queues[N_IA_CSS_ISYS_QUEUE_TYPE]; + unsigned int num_recv_queues[N_IA_CSS_ISYS_QUEUE_TYPE]; + unsigned int send_queue_size[N_IA_CSS_ISYS_QUEUE_TYPE]; + struct ia_css_isys_comm_buffer_queue isys_comm_buffer_queue; + unsigned int stream_nof_output_pins[STREAM_ID_MAX]; +#if (VERIFY_DEVSTATE != 0) + enum device_state dev_state; +#endif /* VERIFY_DEVSTATE */ + enum stream_state stream_state_array[STREAM_ID_MAX]; + /* If true, this context is created based on secure config */ + bool secure; +}; + + +/** + * ia_css_isys_constr_comm_buff_queue() + */ +extern int ia_css_isys_constr_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_force_unmap_comm_buff_queue() + */ +extern int ia_css_isys_force_unmap_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_destr_comm_buff_queue() + */ +extern int ia_css_isys_destr_comm_buff_queue( + struct ia_css_isys_context *ctx +); + +/** + * ia_css_isys_constr_fw_stream_cfg() + */ +extern int ia_css_isys_constr_fw_stream_cfg( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pstream_cfg_fw, + ia_css_shared_buffer *pbuf_stream_cfg_id, + const struct ia_css_isys_stream_cfg_data *stream_cfg +); + +/** + * ia_css_isys_constr_fw_next_frame() + */ +extern int ia_css_isys_constr_fw_next_frame( + struct ia_css_isys_context *ctx, + const unsigned int stream_handle, + ia_css_shared_buffer_css_address *pnext_frame_fw, + ia_css_shared_buffer *pbuf_next_frame_id, + const struct ia_css_isys_frame_buff_set *next_frame +); + +/** + * ia_css_isys_extract_fw_response() + */ +extern int ia_css_isys_extract_fw_response( + struct ia_css_isys_context *ctx, + const struct resp_queue_token *token, + struct ia_css_isys_resp_info *received_response +); +extern int ia_css_isys_extract_proxy_response( + const struct proxy_resp_queue_token *token, + struct ia_css_proxy_write_req_resp *received_response +); + +/** + * ia_css_isys_prepare_param() + */ +extern int ia_css_isys_prepare_param( + struct ia_css_isys_fw_config *isys_fw_cfg, + const struct ia_css_isys_buffer_partition *buf_partition, + const unsigned int num_send_queues[], + const unsigned int num_recv_queues[] +); + +#endif /* __IA_CSS_ISYS_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public.c new file mode 100644 index 000000000000..478d49f51cdd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public.c @@ -0,0 +1,1283 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* TODO: REMOVE --> START IF EXTERNALLY INCLUDED/DEFINED */ +/* These are temporary, the correct numbers need to be inserted/linked */ +/* Until this happens, the following definitions stay here */ +#define INPUT_MIN_WIDTH 1 +#define INPUT_MAX_WIDTH 16384 +#define INPUT_MIN_HEIGHT 1 +#define INPUT_MAX_HEIGHT 16384 +#define OUTPUT_MIN_WIDTH 1 +#define OUTPUT_MAX_WIDTH 16384 +#define OUTPUT_MIN_HEIGHT 1 +#define OUTPUT_MAX_HEIGHT 16384 +/* REMOVE --> END IF EXTERNALLY INCLUDED/DEFINED */ + + +/* The FW bridged types are included through the following */ +#include "ia_css_isysapi.h" +/* The following provides the isys-sys context */ +#include "ia_css_isys_private.h" +/* The following provides the sys layer functions */ +#include "ia_css_syscom.h" + +#include "ia_css_cell.h" +#include "ipu_device_cell_properties.h" + +/* The following provides the tracing functions */ +#include "ia_css_isysapi_trace.h" +#include "ia_css_isys_public_trace.h" + +#include "ia_css_shared_buffer_cpu.h" +/* The following is needed for the + * stddef.h (NULL), + * limits.h (CHAR_BIT definition). + */ +#include "type_support.h" +#include "error_support.h" +#include "cpu_mem_support.h" +#include "math_support.h" +#include "misc_support.h" +#include "system_const.h" + +static int isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config); +static int isys_start_server( + const struct ia_css_isys_device_cfg_data *config); + +static int isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + int retval; + unsigned int stream_handle; + struct ia_css_isys_context *ctx; + struct ia_css_syscom_config sys; + /* Needs to be updated in case new type of queues are introduced */ + struct ia_css_syscom_queue_config input_queue_cfg[N_MAX_SEND_QUEUES]; + /* Needs to be updated in case new type of queues are introduced */ + struct ia_css_syscom_queue_config output_queue_cfg[N_MAX_RECV_QUEUES]; + struct ia_css_isys_fw_config isys_fw_cfg; + unsigned int proxy_write_queue_size; + unsigned int ssid; + unsigned int mmid; + unsigned int i; + + /* Printing "ENTRY isys_context_create" + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY isys_context_create\n"); + + verifret(config != NULL, EFAULT); + + /* Printing configuration information if tracing level = VERBOSE. */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_device_config_data(config); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + /* Runtime check for # of send and recv MSG queues */ + verifret(config->driver_sys.num_send_queues <= + N_MAX_MSG_SEND_QUEUES/*=STREAM_ID_MAX*/, EINVAL); + verifret(config->driver_sys.num_recv_queues <= + N_MAX_MSG_RECV_QUEUES, EINVAL); + + /* Runtime check for send and recv MSG queue sizes */ + verifret(config->driver_sys.send_queue_size <= MAX_QUEUE_SIZE, EINVAL); + verifret(config->driver_sys.recv_queue_size <= MAX_QUEUE_SIZE, EINVAL); + + /* TODO: return an error in case MAX_QUEUE_SIZE is exceeded + * (Similar to runtime check on MSG queue sizes) + */ + proxy_write_queue_size = uclip( + config->driver_proxy.proxy_write_queue_size, + MIN_QUEUE_SIZE, + MAX_QUEUE_SIZE); + + ctx = (struct ia_css_isys_context *) + ia_css_cpu_mem_alloc(sizeof(struct ia_css_isys_context)); + verifret(ctx != NULL, EFAULT); + *context = (HANDLE)ctx; + + /* Copy to the sys config the driver_sys config, + * and add the internal info (token sizes) + */ + ssid = config->driver_sys.ssid; + mmid = config->driver_sys.mmid; + sys.ssid = ssid; + sys.mmid = mmid; + + ctx->secure = config->secure; + /* Following operations need to be aligned with + * "enum ia_css_isys_queue_type" list (list of queue types) + */ + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + N_MAX_PROXY_SEND_QUEUES; + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + N_MAX_DEV_SEND_QUEUES; + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.num_send_queues; + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + N_MAX_PROXY_RECV_QUEUES; + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + 0; /* Common msg/dev return queue */ + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.num_recv_queues; + + sys.num_input_queues = + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + sys.num_output_queues = + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + + sys.input = input_queue_cfg; + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + i++) { + input_queue_cfg[BASE_PROXY_SEND_QUEUES + i].queue_size = + proxy_write_queue_size; + input_queue_cfg[BASE_PROXY_SEND_QUEUES + i].token_size = + sizeof(struct proxy_send_queue_token); + } + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + i++) { + input_queue_cfg[BASE_DEV_SEND_QUEUES + i].queue_size = + DEV_SEND_QUEUE_SIZE; + input_queue_cfg[BASE_DEV_SEND_QUEUES + i].token_size = + sizeof(struct send_queue_token); + } + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + i++) { + input_queue_cfg[BASE_MSG_SEND_QUEUES + i].queue_size = + config->driver_sys.send_queue_size; + input_queue_cfg[BASE_MSG_SEND_QUEUES + i].token_size = + sizeof(struct send_queue_token); + } + + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_PROXY] = + proxy_write_queue_size; + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_DEV] = + DEV_SEND_QUEUE_SIZE; + ctx->send_queue_size[IA_CSS_ISYS_QUEUE_TYPE_MSG] = + config->driver_sys.send_queue_size; + + sys.output = output_queue_cfg; + for (i = 0; + i < ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + i++) { + output_queue_cfg[BASE_PROXY_RECV_QUEUES + i].queue_size = + proxy_write_queue_size; + output_queue_cfg[BASE_PROXY_RECV_QUEUES + i].token_size = + sizeof(struct proxy_resp_queue_token); + } + /* There is no recv DEV queue */ + for (i = 0; + i < ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]; + i++) { + output_queue_cfg[BASE_MSG_RECV_QUEUES + i].queue_size = + config->driver_sys.recv_queue_size; + output_queue_cfg[BASE_MSG_RECV_QUEUES + i].token_size = + sizeof(struct resp_queue_token); + } + + sys.regs_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_REGS); + sys.dmem_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_DMEM); + +#if HAS_DUAL_CMD_CTX_SUPPORT + sys.dmem_addr += config->secure ? REGMEM_SECURE_OFFSET : REGMEM_OFFSET; +#endif + + /* Prepare the param */ + ia_css_isys_prepare_param( + &isys_fw_cfg, + &config->buffer_partition, + ctx->num_send_queues, + ctx->num_recv_queues); + + /* parameter struct to be passed to fw */ + sys.specific_addr = &isys_fw_cfg; + /* parameters size */ + sys.specific_size = sizeof(isys_fw_cfg); + sys.secure = config->secure; + if (config->secure) { + sys.vtl0_addr_mask = config->vtl0_addr_mask; + } + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "isys_context_create || call ia_css_syscom_open()\n"); + /* The allocation of the queues will take place within this call and + * info will be stored in sys_context output + */ + ctx->sys = ia_css_syscom_open(&sys, NULL); + if (!ctx->sys) { + ia_css_cpu_mem_free(ctx); + return -EFAULT; + } + + /* Update the context with the id's */ + ctx->ssid = ssid; + ctx->mmid = mmid; + + for (stream_handle = 0; stream_handle < STREAM_ID_MAX; + stream_handle++) { + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_IDLE; + } + + retval = ia_css_isys_constr_comm_buff_queue(ctx); + if (retval) { + ia_css_syscom_close(ctx->sys); + ia_css_syscom_release(ctx->sys, 1); + ia_css_cpu_mem_free(ctx); + return retval; + } + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_CONFIGURED; +#endif /* VERIFY_DEVSTATE */ + + /* Printing device configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + /* Printing "LEAVE isys_context_create" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE isys_context_create\n"); + return 0; +} + +static int isys_start_server( + const struct ia_css_isys_device_cfg_data *config) +{ + verifret(config != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "isys_start_server || start SPC\n"); + /* The firmware is loaded and syscom is ready, start the SPC */ + ia_css_cell_start_prefetch(config->driver_sys.ssid, SPC0, + config->driver_sys.icache_prefetch); + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, "SPC prefetch: %d\n", + config->driver_sys.icache_prefetch); + return 0; +} + +/** + * ia_css_isys_device_open() - open and configure ISYS device + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_isys_context_create( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + return isys_context_create(context, config); +} + +/* push context information to DMEM for FW to access */ +int ia_css_isys_context_store_dmem( + const HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *) *context; + + return ia_css_syscom_store_dmem(ctx->sys, config->driver_sys.ssid, config->vtl0_addr_mask); +} + +bool ia_css_isys_ab_spc_ready( + HANDLE * context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *) *context; + + return ia_css_syscom_is_ab_spc_ready(ctx->sys); +} + +int ia_css_isys_device_open( + const struct ia_css_isys_device_cfg_data *config) +{ + return isys_start_server(config); +} +#else +int ia_css_isys_device_open( + HANDLE * context, + const struct ia_css_isys_device_cfg_data *config) +{ + int retval; + + retval = isys_context_create(context, config); + if (retval) { + IA_CSS_TRACE_1(ISYSAPI, ERROR, "ia_css_isys_device_open() failed (retval %d)\n", retval); + return retval; + } + + isys_start_server(config); + return 0; +} +#endif + +/** + * ia_css_isys_device_open_ready() - open and configure ISYS device + */ +int ia_css_isys_device_open_ready(HANDLE context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int i; + int retval; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_OPEN" + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_DEVICE_OPEN\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_CONFIGURED, EPERM); +#endif /* VERIFY_DEVSTATE */ + + /* Open the ports for all the non-MSG send queues (PROXY + DEV) */ + for (i = 0; + i < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + i++) { + retval = ia_css_syscom_send_port_open(ctx->sys, i); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + /* Open the ports for all the recv queues (PROXY + MSG) */ + for (i = 0; + i < (ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG]); + i++) { + retval = ia_css_syscom_recv_port_open(ctx->sys, i); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_READY; +#endif /* VERIFY_DEVSTATE */ + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_OPEN_READY" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_DEVICE_OPEN_READY\n"); + return 0; +} + + + /** + * ia_css_isys_stream_open() - open and configure a virtual stream + */ +int ia_css_isys_stream_open( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int i; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address stream_cfg_fw = 0; + ia_css_shared_buffer buf_stream_cfg_id = (ia_css_shared_buffer)NULL; + /* Printing "ENTRY IA_CSS_ISYS_STREAM_OPEN" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_OPEN\n"); + + verifret(ctx, EFAULT); + + /* Printing stream configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_stream_config_data(stream_cfg); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_IDLE, EPERM); + + verifret(stream_cfg != NULL, EFAULT); + verifret(stream_cfg->src < N_IA_CSS_ISYS_STREAM_SRC, EINVAL); + verifret(stream_cfg->vc < N_IA_CSS_ISYS_MIPI_VC, EINVAL); + verifret(stream_cfg->isl_use < N_IA_CSS_ISYS_USE, EINVAL); + if (stream_cfg->isl_use != IA_CSS_ISYS_USE_NO_ISL_NO_ISA) { + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset >= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + OUTPUT_MIN_HEIGHT, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset <= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + OUTPUT_MAX_HEIGHT, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset >= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + OUTPUT_MIN_WIDTH, EINVAL); + + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset <= + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + OUTPUT_MAX_WIDTH, EINVAL); + } + verifret(stream_cfg->nof_input_pins <= MAX_IPINS, EINVAL); + verifret(stream_cfg->nof_output_pins <= MAX_OPINS, EINVAL); + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + /* Verify input pin */ + verifret( + stream_cfg->input_pins[i].input_res.width >= + INPUT_MIN_WIDTH && + stream_cfg->input_pins[i].input_res.width <= + INPUT_MAX_WIDTH && + stream_cfg->input_pins[i].input_res.height >= + INPUT_MIN_HEIGHT && + stream_cfg->input_pins[i].input_res.height <= + INPUT_MAX_HEIGHT, EINVAL); + verifret(stream_cfg->input_pins[i].dt < + N_IA_CSS_ISYS_MIPI_DATA_TYPE, EINVAL); +/* #ifdef To be removed when driver inits the value */ +#ifdef DRIVER_INIT_MIPI_STORE_MODE + verifret(stream_cfg->input_pins[i].mipi_store_mode < + N_IA_CSS_ISYS_MIPI_STORE_MODE, EINVAL); +#endif /* DRIVER_INIT_MIPI_STORE_MODE */ + } + for (i = 0; i < stream_cfg->nof_output_pins; i++) { + /* Verify output pin */ + verifret(stream_cfg->output_pins[i].input_pin_id < + stream_cfg->nof_input_pins, EINVAL); + verifret(stream_cfg->output_pins[i].pt < + N_IA_CSS_ISYS_PIN_TYPE, EINVAL); + verifret(stream_cfg->output_pins[i].ft < + N_IA_CSS_ISYS_FRAME_FORMAT, EINVAL); + /* Verify that the stride is aligned to 64 bytes: HW spec */ + verifret(stream_cfg->output_pins[i].stride%(XMEM_WIDTH/8) == + 0, EINVAL); + verifret((stream_cfg->output_pins[i].output_res.width >= + OUTPUT_MIN_WIDTH) && + (stream_cfg->output_pins[i].output_res.width <= + OUTPUT_MAX_WIDTH) && + (stream_cfg->output_pins[i].output_res.height >= + OUTPUT_MIN_HEIGHT) && + (stream_cfg->output_pins[i].output_res.height <= + OUTPUT_MAX_HEIGHT), EINVAL); + verifret((stream_cfg->output_pins[i].pt == + IA_CSS_ISYS_PIN_TYPE_MIPI) || + (stream_cfg-> + input_pins[stream_cfg->output_pins[i].input_pin_id].mipi_store_mode != + IA_CSS_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER), EINVAL); + if (stream_cfg->isl_use == IA_CSS_ISYS_USE_SINGLE_ISA) { + switch (stream_cfg->output_pins[i].pt) { + case IA_CSS_ISYS_PIN_TYPE_RAW_NS: + /* Ensure the PIFCONV cropped resolution + * matches the RAW_NS output pin resolution + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].bottom_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].top_offset + + (int)stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].right_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_NONSCALED].left_offset + + (int)stream_cfg->output_pins[i].output_res.width, EINVAL); + /* Ensure the ISAPF cropped resolution matches + * the Non-scaled ISA output resolution before + * the PIFCONV cropping, since nothing can + * modify the resolution in that part of + * the pipe + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset == + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].height, + EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset == + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].width, + EINVAL); + /* Ensure the Non-scaled ISA output resolution + * before the PIFCONV cropping bounds the + * RAW_NS pin output resolution since padding + * is not supported + */ + verifret(stream_cfg-> +isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].height >= +stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> +isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_NONSCALED].width >= +stream_cfg->output_pins[i].output_res.width, EINVAL); + break; + case IA_CSS_ISYS_PIN_TYPE_RAW_S: + /* Ensure the ScaledPIFCONV cropped resolution + * matches the RAW_S output pin resolution + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].bottom_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].top_offset + + (int)stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].right_offset == + stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_POST_ISA_SCALED].left_offset + + (int)stream_cfg->output_pins[i].output_res.width, EINVAL); + /* Ensure the ISAPF cropped resolution bounds + * the Scaled ISA output resolution before the + * ScaledPIFCONV cropping, since only IDS can + * modify the resolution, and this only to + * make it smaller + */ + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].bottom_offset >= + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].top_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].height, + EINVAL); + verifret(stream_cfg-> + crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].right_offset >= + stream_cfg->crop[IA_CSS_ISYS_CROPPING_LOCATION_PRE_ISA].left_offset + + (int)stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].width, + EINVAL); + /* Ensure the Scaled ISA output resolution + * before the ScaledPIFCONV cropping bounds + * the RAW_S pin output resolution since + * padding is not supported + */ + verifret(stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].height >= + stream_cfg->output_pins[i].output_res.height, EINVAL); + verifret(stream_cfg-> + isa_cfg.isa_res[IA_CSS_ISYS_RESOLUTION_INFO_POST_ISA_SCALED].width >= + stream_cfg->output_pins[i].output_res.width, EINVAL); + break; + default: + break; + } + } + } + + /* open 1 send queue/stream and a single receive queue + * if not existing + */ + retval = ia_css_syscom_send_port_open(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(retval != FW_ERROR_BUSY, EBUSY); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_OPEN; + retval = ia_css_isys_constr_fw_stream_cfg(ctx, stream_handle, + &stream_cfg_fw, &buf_stream_cfg_id, stream_cfg); + verifret(retval == 0, retval); + token.payload = stream_cfg_fw; + token.buf_handle = HOST_ADDRESS(buf_stream_cfg_id); + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_nof_output_pins[stream_handle] = + stream_cfg->nof_output_pins; + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_OPEN" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_OPEN\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_close() - close virtual stream + */ +int ia_css_isys_stream_close( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_CLOSE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_OPENED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_CLOSE; + token.stream_id = stream_handle; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + /* close 1 send queue/stream and the single receive queue + * if none is using it + */ + retval = ia_css_syscom_send_port_close(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + + ctx->stream_state_array[stream_handle] = IA_CSS_ISYS_STREAM_STATE_IDLE; + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_CLOSE\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_start() - starts handling a mipi virtual stream + */ +int ia_css_isys_stream_start( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address next_frame_fw = 0; + ia_css_shared_buffer buf_next_frame_id = (ia_css_shared_buffer)NULL; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_START" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_START\n"); + + verifret(ctx, EFAULT); + + /* Printing frame configuration and device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_isys_frame_buff_set(next_frame, + ctx->stream_nof_output_pins[stream_handle]); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_OPENED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + if (next_frame != NULL) { + token.send_type = + IA_CSS_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; + retval = ia_css_isys_constr_fw_next_frame(ctx, stream_handle, + &next_frame_fw, &buf_next_frame_id, next_frame); + verifret(retval == 0, retval); + token.payload = next_frame_fw; + token.buf_handle = HOST_ADDRESS(buf_next_frame_id); + } else { + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_START; + token.payload = 0; + token.buf_handle = 0; + } + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_STARTED; + /* Printing "LEAVE IA_CSS_ISYS_STREAM_START" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_START\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_stop() - Stops a mipi virtual stream + */ +int ia_css_isys_stream_stop( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_STOP" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_STOP\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_DEV_SEND_QUEUES)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_STOP; + token.stream_id = stream_handle; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_DEV_SEND_QUEUES), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_STOP" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_STOP\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_flush() - stops a mipi virtual stream but + * completes processing cmd backlog + */ +int ia_css_isys_stream_flush( + HANDLE context, + const unsigned int stream_handle) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_FLUSH" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_STREAM_FLUSH\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_FLUSH; + token.payload = 0; + token.buf_handle = 0; + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + ctx->stream_state_array[stream_handle] = + IA_CSS_ISYS_STREAM_STATE_OPENED; + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_FLUSH" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_STREAM_FLUSH\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_capture_indication() + * - captures "next frame" on stream_handle + */ +int ia_css_isys_stream_capture_indication( + HANDLE context, + const unsigned int stream_handle, + const struct ia_css_isys_frame_buff_set *next_frame) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct send_queue_token token; + ia_css_shared_buffer_css_address next_frame_fw = 0; + ia_css_shared_buffer buf_next_frame_id = (ia_css_shared_buffer)NULL; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_CAPTURE_INDICATION" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_STREAM_CAPTURE_INDICATION\n"); + + verifret(ctx, EFAULT); + + /* Printing frame configuration and device handle context information + *if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); + print_isys_frame_buff_set(next_frame, + ctx->stream_nof_output_pins[stream_handle]); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(stream_handle < STREAM_ID_MAX, EINVAL); + verifret(stream_handle < + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG], EINVAL); + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_STARTED, EPERM); + verifret(next_frame != NULL, EFAULT); + + packets = ia_css_syscom_send_port_available(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle)); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + { + token.send_type = IA_CSS_ISYS_SEND_TYPE_STREAM_CAPTURE; + retval = ia_css_isys_constr_fw_next_frame(ctx, stream_handle, + &next_frame_fw, &buf_next_frame_id, next_frame); + verifret(retval == 0, retval); + token.payload = next_frame_fw; + token.buf_handle = HOST_ADDRESS(buf_next_frame_id); + } + retval = ia_css_syscom_send_port_transfer(ctx->sys, + (BASE_MSG_SEND_QUEUES + stream_handle), &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_CAPTURE_INDICATION" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_STREAM_CAPTURE_INDICATION\n"); + + return 0; +} + + +/** + * ia_css_isys_stream_handle_response() - handle ISYS responses + */ +int ia_css_isys_stream_handle_response( + HANDLE context, + struct ia_css_isys_resp_info *received_response) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + int packets; + struct resp_queue_token token; + + /* Printing "ENTRY IA_CSS_ISYS_STREAM_HANDLE_RESPONSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_STREAM_HANDLE_RESPONSE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + verifret(received_response != NULL, EFAULT); + + packets = ia_css_syscom_recv_port_available( + ctx->sys, BASE_MSG_RECV_QUEUES); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + retval = ia_css_syscom_recv_port_transfer( + ctx->sys, BASE_MSG_RECV_QUEUES, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + retval = ia_css_isys_extract_fw_response( + ctx, &token, received_response); + verifret(retval == 0, retval); + + /* Printing received response information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_isys_resp_info(received_response); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + + verifret(received_response->type < N_IA_CSS_ISYS_RESP_TYPE, EINVAL); + verifret(received_response->stream_handle < STREAM_ID_MAX, EINVAL); + + if (received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY || + received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK || + received_response->type == IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED) { + verifret(received_response->pin.addr != 0, EFAULT); + verifret(received_response->pin.out_buf_id != 0, EFAULT); + verifret(received_response->pin_id < + ctx->stream_nof_output_pins[received_response->stream_handle], + EINVAL); + } + + /* Printing "LEAVE IA_CSS_ISYS_STREAM_HANDLE_RESPONSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_STREAM_HANDLE_RESPONSE\n"); + + return 0; +} + + +/** + * ia_css_isys_device_close() - close ISYS device + */ +static int isys_context_destroy(HANDLE context) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + unsigned int stream_handle; + unsigned int queue_id; + unsigned int nof_recv_queues; + int retval = 0; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY isys_context_destroy\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_READY, EPERM); +#endif /* VERIFY_DEVSTATE */ + + nof_recv_queues = ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_MSG] + + ctx->num_recv_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY]; + /* Close the ports for all the recv queues (MSG and PROXY) */ + for (queue_id = 0; queue_id < nof_recv_queues; queue_id++) { + retval = ia_css_syscom_recv_port_close( + ctx->sys, queue_id); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + /* Close the ports for PROXY send queue(s) */ + for (queue_id = 0; + queue_id < ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_PROXY] + + ctx->num_send_queues[IA_CSS_ISYS_QUEUE_TYPE_DEV]; + queue_id++) { + retval = ia_css_syscom_send_port_close( + ctx->sys, queue_id); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval == 0, EINVAL); + } + + for (stream_handle = 0; stream_handle < STREAM_ID_MAX; + stream_handle++) { + verifret(ctx->stream_state_array[stream_handle] == + IA_CSS_ISYS_STREAM_STATE_IDLE, EPERM); + } + + retval = ia_css_syscom_close(ctx->sys); + verifret(retval == 0, EBUSY); + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_CONFIGURED; +#endif /* VERIFY_DEVSTATE */ + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_CLOSE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE isys_context_destroy\n"); + + return 0; +} +/** + * ia_css_isys_device_close() - close ISYS device + */ +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_isys_context_destroy(HANDLE context) +{ + return isys_context_destroy(context); +} + +void ia_css_isys_device_close(void) +{ + /* Created for legacy, nothing to perform here */ +} + +#else +int ia_css_isys_device_close(HANDLE context) +{ + return isys_context_destroy(context); +} +#endif + +/** + * ia_css_isys_device_release() - release ISYS device + */ +int ia_css_isys_device_release(HANDLE context, unsigned int force) +{ + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + int retval = 0; + + /* Printing "ENTRY IA_CSS_ISYS_DEVICE_RELEASE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_DEVICE_RELEASE\n"); + + verifret(ctx, EFAULT); + + /* Printing device handle context information + * if tracing level = VERBOSE. + */ +#if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + print_handle_context(ctx); +#endif /* ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG */ + +#if (VERIFY_DEVSTATE != 0) + verifret(ctx->dev_state == IA_CSS_ISYS_DEVICE_STATE_CONFIGURED, EPERM); +#endif /* VERIFY_DEVSTATE */ + + retval = ia_css_syscom_release(ctx->sys, force); + verifret(retval == 0, EBUSY); + + /* If ia_css_isys_device_release called with force==1, this should + * happen after timeout, so no active transfers + * If ia_css_isys_device_release called with force==0, this should + * happen after SP has gone idle, so no active transfers + */ + ia_css_isys_force_unmap_comm_buff_queue(ctx); + ia_css_isys_destr_comm_buff_queue(ctx); + +#if (VERIFY_DEVSTATE != 0) + ctx->dev_state = IA_CSS_ISYS_DEVICE_STATE_IDLE; +#endif /* VERIFY_DEVSTATE */ + + ia_css_cpu_mem_free(ctx); + + /* Printing "LEAVE IA_CSS_ISYS_DEVICE_RELEASE" message + * if tracing level = VERBOSE. + */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_DEVICE_RELEASE\n"); + + return 0; +} + +/** + * ia_css_isys_proxy_write_req() - send ISYS proxy write requests + */ +int ia_css_isys_proxy_write_req( + HANDLE context, + const struct ia_css_proxy_write_req_val *write_req_val) +{ + + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + struct proxy_send_queue_token token; + int packets; + int retval = 0; + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ENTRY IA_CSS_ISYS_PROXY_WRITE_REQ\n"); + verifret(ctx, EFAULT); + verifret(write_req_val != NULL, EFAULT); + + packets = ia_css_syscom_send_port_available(ctx->sys, 0); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + token.request_id = write_req_val->request_id; + token.region_index = write_req_val->region_index; + token.offset = write_req_val->offset; + token.value = write_req_val->value; + + retval = ia_css_syscom_send_port_transfer(ctx->sys, 0, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "LEAVE IA_CSS_ISYS_PROXY_WRITE_REQ\n"); + + return 0; +} + +/** + * ia_css_isys_proxy_handle_write_response() - handle ISYS proxy responses + */ +int ia_css_isys_proxy_handle_write_response( + HANDLE context, + struct ia_css_proxy_write_req_resp *received_response) +{ + + struct ia_css_isys_context *ctx = (struct ia_css_isys_context *)context; + struct proxy_resp_queue_token token; + int retval = 0; + int packets; + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "ENTRY IA_CSS_ISYS_PROXY_HANDLE_WRITE_RESPONSE\n"); + verifret(ctx, EFAULT); + verifret(received_response != NULL, EFAULT); + + packets = ia_css_syscom_recv_port_available(ctx->sys, 0); + verifret(packets != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(packets >= 0, EINVAL); + verifret(packets > 0, EPERM); + + retval = ia_css_syscom_recv_port_transfer(ctx->sys, 0, &token); + verifret(retval != FW_ERROR_BAD_ADDRESS, EFAULT); + verifret(retval >= 0, EINVAL); + + + retval = ia_css_isys_extract_proxy_response(&token, received_response); + verifret(retval == 0, retval); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "LEAVE IA_CSS_ISYS_PROXY_HANDLE_WRITE_RESPONSE\n"); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.c new file mode 100644 index 000000000000..d6500a0cb605 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.c @@ -0,0 +1,379 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_isysapi_trace.h" +#include "ia_css_isys_public_trace.h" +#include "ia_css_isysapi_types.h" +#include "ia_css_isysapi.h" +#include "ia_css_isys_private.h" +#include "error_support.h" +#include "ia_css_syscom.h" + +/** + * print_handle_context - formatted print function for + * struct ia_css_isys_context *ctx variable + */ +int print_handle_context(struct ia_css_isys_context *ctx) +{ + unsigned int i; + + verifret(ctx != NULL, EFAULT); + /* Print ctx->(ssid, mmid, dev_state) */ + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "Print ia_css_isys_context *ctx\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_3(ISYSAPI, VERBOSE, + "\tia_css_isys_context->ssid = %d\n" + "\t\t\tia_css_isys_context->mmid = %d\n" + "\t\t\tia_css_isys_context->device_state = %d\n" + , ctx->ssid + , ctx->mmid + , ctx->dev_state); + /* Print ctx->(stream_state_array, stream_nof_output_pins) */ + for (i = 0; i < STREAM_ID_MAX; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_context->stream_state[i = %d] = %d\n" + "\t\t\tia_css_isys_context->stream_nof_output_pins[i = %d] = %d\n" + , i + , ctx->stream_state_array[i] + , i + , ctx->stream_nof_output_pins[i]); + } + /* Print ctx->ia_css_syscom_context */ + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, + "\tia_css_isys_context->ia_css_syscom_context = %p\n" + , (struct ia_css_syscom_context *)(ctx->sys)); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_device_config_data - formatted print function for + * struct ia_css_isys_device_cfg_data *config variable + */ +int print_device_config_data(const struct ia_css_isys_device_cfg_data *config) +{ + verifret(config != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, + VERBOSE, + "Print ia_css_isys_device_cfg_data *config\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_7(ISYSAPI, + VERBOSE, + "\tia_css_isys_device_cfg_data->driver_sys.ssid = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.mmid = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.num_send_queues = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.num_recv_queues = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.send_queue_size = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_sys.recv_queue_size = %d\n" + "\t\t\tia_css_isys_device_cfg_data->driver_proxy.proxy_write_queue_size = %d\n", + config->driver_sys.ssid, + config->driver_sys.mmid, + config->driver_sys.num_send_queues, + config->driver_sys.num_recv_queues, + config->driver_sys.send_queue_size, + config->driver_sys.recv_queue_size, + config->driver_proxy.proxy_write_queue_size); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_stream_config_data - formatted print function for + * ia_css_isys_stream_cfg_data stream_cfg variable + */ +int print_stream_config_data( + const struct ia_css_isys_stream_cfg_data *stream_cfg) +{ + unsigned int i; + + verifret(stream_cfg != NULL, EFAULT); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "Print ia_css_isys_stream_cfg_data stream_cfg\n" + "-------------------------------------------------------\n"); + IA_CSS_TRACE_5(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isl_use = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_stream_source = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_mipi_vc = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->nof_input_pins = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->nof_output_pins = %d\n" + , stream_cfg->isl_use + , stream_cfg->src + , stream_cfg->vc + , stream_cfg->nof_input_pins + , stream_cfg->nof_output_pins); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->send_irq_sof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_irq_eof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_resp_sof_discarded = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->send_resp_eof_discarded = %d\n" + , stream_cfg->send_irq_sof_discarded + , stream_cfg->send_irq_eof_discarded + , stream_cfg->send_resp_sof_discarded + , stream_cfg->send_resp_eof_discarded); + for (i = 0; i < stream_cfg->nof_input_pins; i++) { + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_mipi_data_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_resolution.width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_resolution.height = %d\n" + , i + , stream_cfg->input_pins[i].dt + , i + , stream_cfg->input_pins[i].input_res.width + , i + , stream_cfg->input_pins[i].input_res.height); + IA_CSS_TRACE_2(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_input_pin_info[i = %d].ia_css_isys_mipi_store_mode = %d\n" + , i + , stream_cfg->input_pins[i].mipi_store_mode); + } + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].top_offset = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].left_offset = %d\n" + , i + , stream_cfg->crop[i].top_offset + , i + , stream_cfg->crop[i].left_offset); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].bottom_offset = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_cropping[i = %d].right_offset = %d\n" + , i + , stream_cfg->crop[i].bottom_offset + , i + , stream_cfg->crop[i].right_offset); + } + for (i = 0; i < stream_cfg->nof_output_pins; i++) { + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_pin_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_frame_format_type = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].input_pin_id = %d\n" + , i + , stream_cfg->output_pins[i].pt + , i + , stream_cfg->output_pins[i].ft + , i + , stream_cfg->output_pins[i].input_pin_id); + IA_CSS_TRACE_6(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].watermark_in_lines = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].send_irq = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].stride = %d\n" + , i + , stream_cfg->output_pins[i].watermark_in_lines + , i + , stream_cfg->output_pins[i].send_irq + , i + , stream_cfg->output_pins[i].stride); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_resolution.width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_output_pin_info[i = %d].ia_css_isys_resolution.height = %d\n" + , i + , stream_cfg->output_pins[i].output_res.width + , i + , stream_cfg->output_pins[i].output_res.height); + } + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ia_css_isys_resolution[i = %d].width = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ia_css_isys_resolution[i = %d].height = %d\n" + , i + , stream_cfg->isa_cfg.isa_res[i].width + , i + , stream_cfg->isa_cfg.isa_res[i].height); + } + IA_CSS_TRACE_7(ISYSAPI, VERBOSE, + "\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.blc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.lsc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.dpc_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.downscaler_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.awb_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.af_enabled = %d\n" + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.ae_enabled = %d\n" + , stream_cfg->isa_cfg.blc_enabled + , stream_cfg->isa_cfg.lsc_enabled + , stream_cfg->isa_cfg.dpc_enabled + , stream_cfg->isa_cfg.downscaler_enabled + , stream_cfg->isa_cfg.awb_enabled + , stream_cfg->isa_cfg.af_enabled + , stream_cfg->isa_cfg.ae_enabled); + + IA_CSS_TRACE_1(ISYSAPI, VERBOSE, + "\t\t\tia_css_isys_stream_cfg_data->ia_css_isys_isa_cfg.paf_type = %d\n" + , stream_cfg->isa_cfg.paf_type); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_isys_frame_buff_set - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_frame_buff_set( + const struct ia_css_isys_frame_buff_set *next_frame, + const unsigned int nof_output_pins) +{ + unsigned int i; + + verifret(next_frame != NULL, EFAULT); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "Print ia_css_isys_frame_buff_set *next_frame\n" + "-------------------------------------------------------\n"); + for (i = 0; i < nof_output_pins; i++) { + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->ia_css_isys_output_pin_payload[i = %d].ia_css_return_token = %016lxu\n" + "\t\t\tia_css_isys_frame_buff_set->ia_css_isys_output_pin_payload[i = %d].ia_css_input_buffer_css_address = %08xu\n" + , i + , (unsigned long int) + next_frame->output_pins[i].out_buf_id + , i + , next_frame->output_pins[i].addr); + } + IA_CSS_TRACE_2(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->process_group_light.ia_css_return_token = %016lxu\n" + "\t\t\tia_css_isys_frame_buff_set->process_group_light.ia_css_input_buffer_css_address = %08xu\n" + , (unsigned long int) + next_frame->process_group_light.param_buf_id + , next_frame->process_group_light.addr); + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_frame_buff_set->send_irq_sof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_irq_eof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_resp_sof = %d\n" + "\t\t\tia_css_isys_frame_buff_set->send_resp_eof = %d\n" + , (int) next_frame->send_irq_sof + , (int) next_frame->send_irq_eof + , (int) next_frame->send_resp_sof + , (int) next_frame->send_resp_eof); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "-------------------------------------------------------\n"); + return 0; +} + +/** + * print_isys_resp_info - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_resp_info(struct ia_css_isys_resp_info *received_response) +{ + verifret(received_response != NULL, EFAULT); + + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, "ISYS_RESPONSE_INFO\n" + "-------------------------------------------------------\n"); + switch (received_response->type) { + case IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_WATERMARK\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_SOF: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_SOF\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_EOF: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_EOF\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_PIN_DATA_SKIPPED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED\n"); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED: + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = IA_CSS_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED\n"); + break; + default: + IA_CSS_TRACE_0(ISYSAPI, ERROR, + "\tia_css_isys_resp_info.ia_css_isys_resp_type = INVALID\n"); + break; + } + + IA_CSS_TRACE_4(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.type = %d\n" + "\t\t\tia_css_isys_resp_info.stream_handle = %d\n" + "\t\t\tia_css_isys_resp_info.time_stamp[0] = %d\n" + "\t\t\tia_css_isys_resp_info.time_stamp[1] = %d\n", + received_response->type, + received_response->stream_handle, + received_response->timestamp[0], + received_response->timestamp[1]); + IA_CSS_TRACE_7(ISYSAPI, VERBOSE, + "\tia_css_isys_resp_info.error = %d\n" + "\t\t\tia_css_isys_resp_info.error_details = %d\n" + "\t\t\tia_css_isys_resp_info.pin.out_buf_id = %016llxu\n" + "\t\t\tia_css_isys_resp_info.pin.addr = %016llxu\n" + "\t\t\tia_css_isys_resp_info.pin_id = %d\n" + "\t\t\tia_css_isys_resp_info.frame_counter = %d\n," + "\t\t\tia_css_isys_resp_info.written_direct = %d\n", + received_response->error, + received_response->error_details, + (unsigned long long)received_response->pin.out_buf_id, + (unsigned long long)received_response->pin.addr, + received_response->pin_id, + received_response->frame_counter, + received_response->written_direct); + IA_CSS_TRACE_0(ISYSAPI, VERBOSE, + "------------------------------------------------------\n"); + + return 0; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.h new file mode 100644 index 000000000000..5b6508058fd6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isys_public_trace.h @@ -0,0 +1,55 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYS_PUBLIC_TRACE_H +#define __IA_CSS_ISYS_PUBLIC_TRACE_H + +#include "ia_css_isysapi_trace.h" + +#include "ia_css_isysapi_types.h" + +#include "ia_css_isysapi.h" + +#include "ia_css_isys_private.h" +/** + * print_handle_context - formatted print function for + * struct ia_css_isys_context *ctx variable + */ +int print_handle_context(struct ia_css_isys_context *ctx); + +/** + * print_device_config_data - formatted print function for + * struct ia_css_isys_device_cfg_data *config variable + */ +int print_device_config_data(const struct ia_css_isys_device_cfg_data *config); +/** + * print_stream_config_data - formatted print function for + * ia_css_isys_stream_cfg_data stream_cfg variable + */ +int print_stream_config_data( + const struct ia_css_isys_stream_cfg_data *stream_cfg); +/** + * print_isys_frame_buff_set - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_frame_buff_set( + const struct ia_css_isys_frame_buff_set *next_frame, + const unsigned int nof_output_pins); +/** + * print_isys_isys_resp_info - formatted print function for + * struct ia_css_isys_frame_buff_set *next_frame variable + */ +int print_isys_resp_info(struct ia_css_isys_resp_info *received_response); + +#endif /* __IA_CSS_ISYS_PUBLIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isysapi_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isysapi_trace.h new file mode 100644 index 000000000000..c6b944f245b1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/isysapi/src/ia_css_isysapi_trace.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_ISYSAPI_TRACE_H +#define __IA_CSS_ISYSAPI_TRACE_H + +#include "ia_css_trace.h" + +#define ISYSAPI_TRACE_LOG_LEVEL_OFF 0 +#define ISYSAPI_TRACE_LOG_LEVEL_NORMAL 1 +#define ISYSAPI_TRACE_LOG_LEVEL_DEBUG 2 + +/* ISYSAPI and all the submodules in ISYSAPI will have + * the default tracing level set to this level + */ +#define ISYSAPI_TRACE_CONFIG_DEFAULT ISYSAPI_TRACE_LOG_LEVEL_NORMAL + +/* In case ISYSAPI_TRACE_CONFIG is not defined, set it to default level */ +#if !defined(ISYSAPI_TRACE_CONFIG) + #define ISYSAPI_TRACE_CONFIG ISYSAPI_TRACE_CONFIG_DEFAULT +#endif + +/* ISYSAPI Module tracing backend is mapped to + * TUNIT tracing for target platforms + */ +#ifdef IA_CSS_TRACE_PLATFORM_CELL + #ifndef HRT_CSIM + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE + #else + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #endif +#else + #define ISYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(ISYSAPI_TRACE_CONFIG)) + /* TRACE_OFF */ + #if ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_OFF + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + /* TRACE_NORMAL */ + #elif ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_NORMAL + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + /* TRACE_DEBUG */ + #elif ISYSAPI_TRACE_CONFIG == ISYSAPI_TRACE_LOG_LEVEL_DEBUG + #define ISYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED + #define ISYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No ISYSAPI_TRACE_CONFIG Tracing level defined" + #endif +#else + #error "ISYSAPI_TRACE_CONFIG not defined" +#endif + +#endif /* __IA_CSS_ISYSAPI_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h new file mode 100644 index 000000000000..a284d74bb4a6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir.h @@ -0,0 +1,99 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_H +#define __IA_CSS_PKG_DIR_H + +#include "ia_css_pkg_dir_storage_class.h" +#include "ia_css_pkg_dir_types.h" +#include "type_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +/* User is expected to call the verify function manually, + * other functions do not call it internally + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +int ia_css_pkg_dir_verify_header( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +enum ia_css_pkg_dir_version ia_css_pkg_dir_get_version( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_set_version( + ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version +); + + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_size( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_entry_get_version( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint8_t ia_css_pkg_dir_entry_get_type( + const ia_css_pkg_dir_entry_t *entry +); + +/* Get the address of the specified entry in the PKG_DIR + * Note: This function expects the complete PKG_DIR in the same memory space + * and the entries contains offsets and not addresses. + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +void *ia_css_pkg_dir_get_entry_address( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "ia_css_pkg_dir_impl.h" + +#endif + +#endif /* __IA_CSS_PKG_DIR_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h new file mode 100644 index 000000000000..ad194b0389eb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_iunit.h @@ -0,0 +1,46 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IUNIT_H +#define __IA_CSS_PKG_DIR_IUNIT_H + +/* In bootflow, pkg_dir only supports upto 16 entries in pkg_dir + * pkg_dir_header + Psys_server pg + Isys_server pg + 13 Client pg + */ + +enum { + IA_CSS_PKG_DIR_SIZE = 16, + IA_CSS_PKG_DIR_ENTRIES = IA_CSS_PKG_DIR_SIZE - 1 +}; + +#define IUNIT_MAX_CLIENT_PKG_ENTRIES 13 + +/* Example assignment of unique identifiers for the FW components + * This should match the identifiers in the manifest + */ +enum ia_css_pkg_dir_entry_type { + IA_CSS_PKG_DIR_HEADER = 0, + IA_CSS_PKG_DIR_PSYS_SERVER_PG, + IA_CSS_PKG_DIR_ISYS_SERVER_PG, + IA_CSS_PKG_DIR_CLIENT_PG +}; + +/* Fixed entries in the package directory */ +enum ia_css_pkg_dir_index { + IA_CSS_PKG_DIR_PSYS_INDEX = 0, + IA_CSS_PKG_DIR_ISYS_INDEX = 1, + IA_CSS_PKG_DIR_CLIENT_0 = 2 +}; + +#endif /* __IA_CSS_PKG_DIR_IUNIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h new file mode 100644 index 000000000000..cb64172151f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_storage_class.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_STORAGE_CLASS_H +#define __IA_CSS_PKG_DIR_STORAGE_CLASS_H + + +#include "storage_class.h" + +#ifndef __IA_CSS_PKG_DIR_INLINE__ +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C +#else +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PKG_DIR_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h new file mode 100644 index 000000000000..b024b3da2f9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/interface/ia_css_pkg_dir_types.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_TYPES_H +#define __IA_CSS_PKG_DIR_TYPES_H + +#include "type_support.h" + +struct ia_css_pkg_dir_entry { + uint32_t address[2]; + uint32_t size; + uint16_t version; + uint8_t type; + uint8_t unused; +}; + +typedef void ia_css_pkg_dir_t; +typedef struct ia_css_pkg_dir_entry ia_css_pkg_dir_entry_t; + +/* The version field of the pkg_dir header defines + * if entries contain offsets or pointers + */ +/* This is temporary, until all pkg_dirs use pointers */ +enum ia_css_pkg_dir_version { + IA_CSS_PKG_DIR_POINTER, + IA_CSS_PKG_DIR_OFFSET +}; + + +#endif /* __IA_CSS_PKG_DIR_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/pkg_dir.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/pkg_dir.mk new file mode 100644 index 000000000000..32c8a68f3653 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/pkg_dir.mk @@ -0,0 +1,29 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PKG DIR + +PKG_DIR_DIR = $${MODULES_DIR}/pkg_dir +PKG_DIR_INTERFACE = $(PKG_DIR_DIR)/interface +PKG_DIR_SOURCES = $(PKG_DIR_DIR)/src + +PKG_DIR_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir.c +PKG_DIR_CPPFLAGS = -I$(PKG_DIR_INTERFACE) +PKG_DIR_CPPFLAGS += -I$(PKG_DIR_SOURCES) +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/../isp/kernels/io_ls/common +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu/$(FW_ABI_IPU_TYPES_VERSION) + +PKG_DIR_CREATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_create.c +PKG_DIR_UPDATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_update.c diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c new file mode 100644 index 000000000000..348b56833e06 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir.c @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "storage_class.h" + +STORAGE_CLASS_INLINE int __ia_css_pkg_dir_avoid_warning_on_empty_file(void) +{ + return 0; +} + +#else +#include "ia_css_pkg_dir_impl.h" + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h new file mode 100644 index 000000000000..d5067d21398f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_impl.h @@ -0,0 +1,201 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IMPL_H +#define __IA_CSS_PKG_DIR_IMPL_H + +#include "ia_css_pkg_dir.h" +#include "ia_css_pkg_dir_int.h" +#include "error_support.h" +#include "type_support.h" +#include "assert_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + DECLARE_ERRVAL + struct ia_css_pkg_dir_entry *pkg_dir_header = NULL; + + verifexitval(pkg_dir != NULL, EFAULT); + + pkg_dir_header = (struct ia_css_pkg_dir_entry *)pkg_dir; + + /* First entry of the structure is the header, skip that */ + index++; + verifexitval(index < pkg_dir_header->size, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return NULL; + } + return &(pkg_dir_header[index]); +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +int ia_css_pkg_dir_verify_header(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + verifexitval(pkg_dir_header != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return -1; + } + return ((pkg_dir_header->address[0] == PKG_DIR_MAGIC_VAL_0) + && (pkg_dir_header->address[1] == PKG_DIR_MAGIC_VAL_1)) ? + 0 : -1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + uint32_t size = 0; + + verifexitval(pkg_dir_header != NULL, EFAULT); + size = pkg_dir_header->size; + verifexitval(size > 0, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return size - 1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +enum ia_css_pkg_dir_version +ia_css_pkg_dir_get_version(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + assert(pkg_dir_header != NULL); + return pkg_dir_header->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_set_version(ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 1; + } + pkg_dir_header->version = version; + return 0; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return sizeof(struct ia_css_pkg_dir_entry) * pkg_dir_header->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[0]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[1]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_size(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_entry_get_version(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint8_t ia_css_pkg_dir_entry_get_type(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->type; +} + + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +void *ia_css_pkg_dir_get_entry_address(const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + void *entry_blob = NULL; + const ia_css_pkg_dir_entry_t *pkg_dir_entry = + ia_css_pkg_dir_get_entry(pkg_dir, index-1); + + if ((pkg_dir_entry != NULL) && + (ia_css_pkg_dir_entry_get_size(pkg_dir_entry) > 0)) { + assert(ia_css_pkg_dir_entry_get_address_hi(pkg_dir_entry) == 0); + entry_blob = (void *)((char *)pkg_dir + + ia_css_pkg_dir_entry_get_address_lo(pkg_dir_entry)); + } + return entry_blob; +} + +#endif /* __IA_CSS_PKG_DIR_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h new file mode 100644 index 000000000000..203505fbee54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/pkg_dir/src/ia_css_pkg_dir_int.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_INT_H +#define __IA_CSS_PKG_DIR_INT_H + +/* + * Package Dir structure as specified in CSE FAS + * + * PKG DIR Header + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * 0 "_IUPKDR_" + * 1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version of the Structure + * Size: Size of the entire table (including header) in 16 byte chunks + * Type: Must be 0 for header + * + * Figure 13: PKG DIR Header + * + * + * PKG DIR Entry + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * N Address/Offset + * N+1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version # of the Component + * Size: Size of the component in bytes + * Type: Component Identifier + */ + +#define PKG_DIR_SIZE_BITS 24 +#define PKG_DIR_TYPE_BITS 7 + +#define PKG_DIR_MAGIC_VAL_1 (('_' << 24) | ('I' << 16) | ('U' << 8) | 'P') +#define PKG_DIR_MAGIC_VAL_0 (('K' << 24) | ('D' << 16) | ('R' << 8) | '_') + +#endif /* __IA_CSS_PKG_DIR_INT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/port_env_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/port_env_struct.h new file mode 100644 index 000000000000..4d39a4739a8b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/port_env_struct.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PORT_ENV_STRUCT_H +#define __PORT_ENV_STRUCT_H + +struct port_env { + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __PORT_ENV_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue.h new file mode 100644 index 000000000000..b233ab3baf01 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue.h @@ -0,0 +1,40 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_H +#define __QUEUE_H + +#include "queue_struct.h" +#include "port_env_struct.h" + +/* + * SYS queues are created by the host + * SYS queues cannot be accessed through the queue interface + * To send data into a queue a send_port must be opened. + * To receive data from a queue, a recv_port must be opened. + */ + +/* return required buffer size for queue */ +unsigned int +sys_queue_buf_size(unsigned int size, unsigned int token_size); + +/* + * initialize a queue that can hold at least 'size' tokens of + * 'token_size' bytes. + */ +void +sys_queue_init(struct sys_queue *q, unsigned int size, + unsigned int token_size, struct sys_queue_res *res); + +#endif /* __QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue_struct.h new file mode 100644 index 000000000000..ef48fcfded2b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/queue_struct.h @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_STRUCT_H +#define __QUEUE_STRUCT_H + +/* queue description, shared between sender and receiver */ + +#include "type_support.h" + +#ifdef __VIED_CELL +typedef struct {uint32_t v[2]; } host_buffer_address_t; +#else +typedef uint64_t host_buffer_address_t; +#endif + +typedef uint32_t vied_buffer_address_t; + + +struct sys_queue { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* reg no in subsystem's regmem */ + unsigned int rd_reg; + unsigned int _align; +}; + +struct sys_queue_res { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int reg; +}; + +#endif /* __QUEUE_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port.h new file mode 100644 index 000000000000..cce253b26668 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port.h @@ -0,0 +1,34 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_H +#define __RECV_PORT_H + + +struct recv_port; +struct sys_queue; +struct port_env; + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env); + +unsigned int +recv_port_available(const struct recv_port *p); + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data); + + +#endif /* __RECV_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port_struct.h new file mode 100644 index 000000000000..52ec563b13cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/recv_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_STRUCT_H +#define __RECV_PORT_STRUCT_H + +#include "buffer_type.h" + +struct recv_port { + buffer_address buffer; /* address of buffer in DDR */ + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer located in regmem */ + unsigned int rd_reg; /* index read pointer located in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; /* address of memory containing regmem */ +}; + +#endif /* __RECV_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port.h new file mode 100644 index 000000000000..04a160f3f019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port.h @@ -0,0 +1,52 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_H +#define __SEND_PORT_H + + +/* + * A send port can be used to send tokens into a queue. + * The interface can be used on any type of processor (host, SP, ...) + */ + +struct send_port; +struct sys_queue; +struct port_env; + +/* + * Open a send port on a queue. After the port is opened, tokens can be sent + */ +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env); + +/* + * Determine how many tokens can be sent + */ +unsigned int +send_port_available(const struct send_port *p); + +/* + * Send a token via a send port. The function returns the number of + * tokens that have been sent: + * 1: the token was accepted + * 0: the token was not accepted (full queue) + * The size of a token is determined at initialization. + */ +unsigned int +send_port_transfer(const struct send_port *p, const void *data); + + +#endif /* __SEND_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port_struct.h new file mode 100644 index 000000000000..f834c62bc3db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/interface/send_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_STRUCT_H +#define __SEND_PORT_STRUCT_H + +#include "buffer_type.h" + +struct send_port { + buffer_address buffer; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer in regmem */ + unsigned int rd_reg; /* index of read pointer in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __SEND_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/port.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/port.mk new file mode 100644 index 000000000000..b3801247802e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/port.mk @@ -0,0 +1,31 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PORT + +PORT_DIR=$${MODULES_DIR}/port + +PORT_INTERFACE=$(PORT_DIR)/interface +PORT_SOURCES1=$(PORT_DIR)/src + +PORT_HOST_FILES += $(PORT_SOURCES1)/send_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/recv_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/queue.c + +PORT_HOST_CPPFLAGS += -I$(PORT_INTERFACE) + +PORT_FW_FILES += $(PORT_SOURCES1)/send_port.c +PORT_FW_FILES += $(PORT_SOURCES1)/recv_port.c + +PORT_FW_CPPFLAGS += -I$(PORT_INTERFACE) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/queue.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/queue.c new file mode 100644 index 000000000000..eeec99dfe2d0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/queue.c @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "queue.h" + +#include "regmem_access.h" +#include "port_env_struct.h" + +unsigned int sys_queue_buf_size(unsigned int size, unsigned int token_size) +{ + return (size + 1) * token_size; +} + +void +sys_queue_init(struct sys_queue *q, unsigned int size, unsigned int token_size, + struct sys_queue_res *res) +{ + unsigned int buf_size; + + q->size = size + 1; + q->token_size = token_size; + buf_size = sys_queue_buf_size(size, token_size); + + /* acquire the shared buffer space */ + q->host_address = res->host_address; + res->host_address += buf_size; + q->vied_address = res->vied_address; + res->vied_address += buf_size; + + /* acquire the shared read and writer pointers */ + q->wr_reg = res->reg; + res->reg++; + q->rd_reg = res->reg; + res->reg++; + +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/recv_port.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/recv_port.c new file mode 100644 index 000000000000..31b36e9ceafb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/recv_port.c @@ -0,0 +1,95 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "recv_port.h" +#include "port_env_struct.h" /* for port_env */ +#include "queue_struct.h" /* for sys_queue */ +#include "recv_port_struct.h" /* for recv_port */ +#include "buffer_access.h" /* for buffer_load, buffer_address */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_invalidate */ +#endif + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; + +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +recv_port_index(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(rd, i, p->size); +} + +unsigned int +recv_port_available(const struct recv_port *p) +{ + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(wr, -rd, p->size); +} + +STORAGE_CLASS_INLINE void +recv_port_copy(const struct recv_port *p, unsigned int i, void *data) +{ + unsigned int rd = recv_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (rd * token_size); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_invalidate((void *)HOST_ADDRESS(p->buffer), + token_size*p->size); +#endif + buffer_load(addr, data, token_size, p->mmid); +} + +STORAGE_CLASS_INLINE void +recv_port_release(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = recv_port_index(p, i); + + regmem_store_32(p->mem_addr, p->rd_reg, rd, p->ssid); +} + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data) +{ + if (!recv_port_available(p)) + return 0; + recv_port_copy(p, 0, data); + recv_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/send_port.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/send_port.c new file mode 100644 index 000000000000..8d1fba08c5d5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/port/src/send_port.c @@ -0,0 +1,94 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "send_port.h" +#include "queue_struct.h" /* for sys_queue */ +#include "send_port_struct.h" /* for send_port */ +#include "port_env_struct.h" /* for port_env */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "buffer_access.h" /* for buffer_store, buffer_address */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_flush */ +#endif + +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +send_port_index(const struct send_port *p, unsigned int i) +{ + unsigned int wr = regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(wr, i, p->size); +} + +unsigned int +send_port_available(const struct send_port *p) +{ + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(rd, -(wr+1), p->size); +} + +STORAGE_CLASS_INLINE void +send_port_copy(const struct send_port *p, unsigned int i, const void *data) +{ + unsigned int wr = send_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (wr * token_size); + + buffer_store(addr, data, token_size, p->mmid); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(addr), token_size); +#endif +} + +STORAGE_CLASS_INLINE void +send_port_release(const struct send_port *p, unsigned int i) +{ + unsigned int wr = send_port_index(p, i); + + regmem_store_32(p->mem_addr, p->wr_reg, wr, p->ssid); +} + +unsigned int +send_port_transfer(const struct send_port *p, const void *data) +{ + if (!send_port_available(p)) + return 0; + send_port_copy(p, 0, data); + send_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.c new file mode 100644 index 000000000000..c51d65c8cb64 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.c @@ -0,0 +1,15 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#include "ia_css_debug_dump.h" + void ia_css_debug_dump(void) {} \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.h new file mode 100644 index 000000000000..5dd23ddbd180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/isys/cnlB0_gen_reg_dump/ia_css_debug_dump.h @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#ifndef __IA_CSS_DEBUG_DUMP_H_ + #define __IA_CSS_DEBUG_DUMP_H_ + void ia_css_debug_dump(void); + #endif /* __IA_CSS_DEBUG_DUMP_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c new file mode 100644 index 000000000000..9b9161ae78cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/reg_dump/src/reg_dump_generic_bridge.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include "ia_css_trace.h" +#ifdef USE_LOGICAL_SSIDS +/* + Logical names can be used to define the SSID + In order to resolve these names the following include file should be provided + and the define above should be enabled +*/ +#include +#endif + +#define REG_DUMP_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#define REG_DUMP_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + +/* SSID value is defined in test makefiles as either isys0 or psys0 */ +#define REG_DUMP_READ_REGISTER(addr) vied_subsystem_load_32(SSID, addr) + +#define REG_DUMP_PRINT_0(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_0(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define REG_DUMP_PRINT_1(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_1(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define EXPAND_VA_ARGS(x) x + +/* Including generated source code for reg_dump */ +#include "ia_css_debug_dump.c" diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/interface/regmem_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/interface/regmem_access.h new file mode 100644 index 000000000000..d4576af936f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/interface/regmem_access.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_H +#define __REGMEM_ACCESS_H + +#include "storage_class.h" + +enum regmem_id { + /* pass pkg_dir address to SPC in non-secure mode */ + PKG_DIR_ADDR_REG = 0, + /* pass syscom configuration to SPC */ + SYSCOM_CONFIG_REG = 1, + /* syscom state - modified by SP */ + SYSCOM_STATE_REG = 2, + /* syscom commands - modified by the host */ + SYSCOM_COMMAND_REG = 3, + /* Store interrupt status - updated by SP */ + SYSCOM_IRQ_REG = 4, + /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/ + SYSCOM_VTL0_ADDR_MASK = 5, +#if HAS_DUAL_CMD_CTX_SUPPORT + /* Initialized if trustlet exists - updated by host */ + TRUSTLET_STATUS = 6, + /* identify if SPC access blocker programming is completed - updated by SP */ + AB_SPC_STATUS = 7, + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 8 +#else + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 6 +#endif +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* Bit 0: for untrusted non-secure DRV driver on VTL0 + * Bit 1: for trusted secure TEE driver on VTL1 + */ +#define SYSCOM_IRQ_VTL0_MASK 0x1 +#define SYSCOM_IRQ_VTL1_MASK 0x2 +#endif + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_address, unsigned int reg, unsigned int ssid); + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_address, unsigned int reg, unsigned int value, + unsigned int ssid); + +#ifdef __VIED_CELL +#include "regmem_access_cell.h" +#else +#include "regmem_access_host.h" +#endif + +#endif /* __REGMEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/regmem.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/regmem.mk new file mode 100644 index 000000000000..24ebc1c325d8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/regmem.mk @@ -0,0 +1,32 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef REGMEM_MK +REGMEM_MK=1 + +# MODULE is REGMEM + +REGMEM_DIR=$${MODULES_DIR}/regmem + +REGMEM_INTERFACE=$(REGMEM_DIR)/interface +REGMEM_SOURCES=$(REGMEM_DIR)/src + +REGMEM_HOST_FILES = +REGMEM_FW_FILES = $(REGMEM_SOURCES)/regmem.c + +REGMEM_CPPFLAGS = -I$(REGMEM_INTERFACE) -I$(REGMEM_SOURCES) +REGMEM_HOST_CPPFLAGS = $(REGMEM_CPPFLAGS) +REGMEM_FW_CPPFLAGS = $(REGMEM_CPPFLAGS) + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_access_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_access_host.h new file mode 100644 index 000000000000..8878d7074fab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_access_host.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_HOST_H +#define __REGMEM_ACCESS_HOST_H + +#include "regmem_access.h" /* implemented interface */ + +#include "storage_class.h" +#include "regmem_const.h" +#include +#include "ia_css_cmem.h" + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_addr, unsigned int reg, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + return ia_css_cmem_load_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg)); +} + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_addr, unsigned int reg, + unsigned int value, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + ia_css_cmem_store_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg), + value); +} + +#endif /* __REGMEM_ACCESS_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_const.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_const.h new file mode 100644 index 000000000000..ac7e3a98a434 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/regmem/src/regmem_const.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_CONST_H +#define __REGMEM_CONST_H + +#ifndef REGMEM_SIZE +#define REGMEM_SIZE (16) +#endif /* REGMEM_SIZE */ +#ifndef REGMEM_OFFSET +#define REGMEM_OFFSET (0) +#endif /* REGMEM_OFFSET */ +#ifndef REGMEM_WORD_BYTES +#define REGMEM_WORD_BYTES (4) +#endif + +#endif /* __REGMEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/assert_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/assert_support.h new file mode 100644 index 000000000000..f904a494b53c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/assert_support.h @@ -0,0 +1,197 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ASSERT_SUPPORT_H +#define __ASSERT_SUPPORT_H + +/* This file provides support for run-time assertions + * and compile-time assertions. + * + * Run-time asstions are provided via the following syntax: + * assert(condition) + * Run-time assertions are disabled using the NDEBUG flag. + * + * Compile time assertions are provided via the following syntax: + * COMPILATION_ERROR_IF(condition); + * A compile-time assertion will fail to compile if the condition is false. + * The condition must be constant, such that it can be evaluated + * at compile time. + * + * OP___assert is deprecated. + */ + +#define IA_CSS_ASSERT(expr) assert(expr) + +#ifdef __KLOCWORK__ +/* Klocwork does not see that assert will lead to abortion + * as there is no good way to tell this to KW and the code + * should not depend on assert to function (actually the assert + * could be disabled in a release build) it was decided to + * disable the assert for KW scans (by defining NDEBUG) + */ +#define NDEBUG +#endif /* __KLOCWORK__ */ + +/** + * The following macro can help to test the size of a struct at compile + * time rather than at run-time. It does not work for all compilers; see + * below. + * + * Depending on the value of 'condition', the following macro is expanded to: + * - condition==true: + * an expression containing an array declaration with negative size, + * usually resulting in a compilation error + * - condition==false: + * (void) 1; // C statement with no effect + * + * example: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * SIZE_OF_HOST_SP_QUEUES_STRUCT); + * + * verify that the macro indeed triggers a compilation error with your compiler: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * (sizeof(struct host_sp_queues)+1) ); + * + * Not all compilers will trigger an error with this macro; + * use a search engine to search for BUILD_BUG_ON to find other methods. + */ +#define COMPILATION_ERROR_IF(condition) \ +((void)sizeof(char[1 - 2*!!(condition)])) + +/* Compile time assertion */ +#ifndef CT_ASSERT +#define CT_ASSERT(cnd) ((void)sizeof(char[(cnd)?1 : -1])) +#endif /* CT_ASSERT */ + +#ifdef NDEBUG + +#define assert(cnd) ((void)0) + +#else + +#include "storage_class.h" + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE +/* Windows kernel mode compilation */ +#include +#define assert(cnd) ASSERT(cnd) +#else +/* Windows usermode compilation */ +#include +#endif + +#elif defined(__HIVECC) + +/* + * target: assert disabled + * sched: assert enabled only when SCHED_DEBUG is defined + * unsched: assert enabled + */ +#if defined(HRT_HW) +#define assert(cnd) ((void)0) +#elif defined(HRT_SCHED) && !defined(DEBUG_SCHED) +#define assert(cnd) ((void)0) +#elif defined(PIPE_GENERATION) +#define assert(cnd) ((void)0) +#else +#include +#define assert(cnd) OP___csim_assert(cnd) +#endif + +#elif defined(__KERNEL__) +#include + +#ifndef KERNEL_ASSERT_TO_BUG +#ifndef KERNEL_ASSERT_TO_BUG_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#ifndef KERNEL_ASSERT_UNDEFINED +/* Default */ +#define KERNEL_ASSERT_TO_BUG +#endif /*KERNEL_ASSERT_UNDEFINED*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG +/* TODO: it would be cleaner to use this: + * #define assert(cnd) BUG_ON(cnd) + * but that causes many compiler warnings (==errors) under Android + * because it seems that the BUG_ON() macro is not seen as a check by + * gcc like the BUG() macro is. */ +#define assert(cnd) \ + do { \ + if (!(cnd)) { \ + BUG(); \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG_ON +#define assert(cnd) BUG_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON +#define assert(cnd) WARN_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#define assert(cnd) \ + do { \ + int not_cnd = !(cnd); \ + WARN_ON(not_cnd); \ + if (not_cnd) { \ + for (;;) { \ + } \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ + +#ifdef KERNEL_ASSERT_UNDEFINED +#include KERNEL_ASSERT_DEFINITION_FILESTRING +#endif /*KERNEL_ASSERT_UNDEFINED*/ + +#elif defined(__FIST__) || defined(__GNUC__) + +#include "assert.h" + +#else /* default is for unknown environments */ +#define assert(cnd) ((void)0) +#endif + +#endif /* NDEBUG */ + +#ifndef PIPE_GENERATION +/* Deprecated OP___assert, this is still used in ~1000 places + * in the code. This will be removed over time. + * The implementation for the pipe generation tool is in see support.isp.h */ +#define OP___assert(cnd) assert(cnd) + +#ifdef C_RUN +#define compile_time_assert(cond) OP___assert(cond) +#else +#include "storage_class.h" +extern void _compile_time_assert(void); +STORAGE_CLASS_INLINE void compile_time_assert(unsigned cond) +{ + /* Call undefined function if cond is false */ + if (!cond) + _compile_time_assert(); +} +#endif +#endif /* PIPE_GENERATION */ + +#endif /* __ASSERT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/cpu_mem_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/cpu_mem_support.h new file mode 100644 index 000000000000..fa349cac4b24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/cpu_mem_support.h @@ -0,0 +1,233 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __CPU_MEM_SUPPORT_H +#define __CPU_MEM_SUPPORT_H + +#include "storage_class.h" +#include "assert_support.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_copy(void *dst, const void *src, unsigned int size) +{ + /* memcpy cannot be used in in Windows (function is not allowed), + * and the safer function memcpy_s is not available on other platforms. + * Because usage of ia_css_cpu_mem_copy is minimal, we implement it here in an easy, + * but sub-optimal way. + */ + unsigned int i; + + assert(dst != NULL && src != NULL); + + if (!(dst != NULL && src != NULL)) { + return NULL; + } + for (i = 0; i < size; i++) { + ((char *)dst)[i] = ((char *)src)[i]; + } + return dst; +} + +#if defined(__KERNEL__) + +#include +#include +#include +#include + +/* TODO: remove, workaround for issue in hrt file ibuf_ctrl_2600_config.c + * error checking code added to SDK that uses calls to exit function + */ +#define exit(a) return + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + return ia_css_cpu_mem_alloc(size); /* todo: align to page size */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_protect(void *ptr, unsigned int size, int prot) +{ + /* nothing here yet */ +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); /* available in kernel in linux/string.h */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + kfree(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* parameter check here */ + if (ptr == NULL) + return; + + clflush_cache_range(ptr, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* for now same as flush */ + ia_css_cpu_mem_cache_flush(ptr, size); +} + +#elif defined(_MSC_VER) + +#include +#include +#include + +extern void *hrt_malloc(size_t bytes, int zero_mem); +extern void *hrt_free(void *ptr); +extern void hrt_mem_cache_flush(void *ptr, unsigned int size); +extern void hrt_mem_cache_invalidate(void *ptr, unsigned int size); + +#define malloc(a) hrt_malloc(a, 1) +#define free(a) hrt_free(a) + +#define CSS_PAGE_SIZE (1<<12) + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + unsigned int buffer_size = size; + + /* Currently hrt_malloc calls Windows ExAllocatePoolWithTag() routine + * to request system memory. If the number of bytes is equal or bigger + * than the page size, then the returned address is page aligned, + * but if it's smaller it's not necessarily page-aligned We agreed + * with Windows team that we allocate a full page + * if it's less than page size + */ + if (buffer_size < CSS_PAGE_SIZE) + buffer_size = CSS_PAGE_SIZE; + + return ia_css_cpu_mem_alloc(buffer_size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_flush(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_invalidate(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +#else + +#include +#include +#include +/* Needed for the MPROTECT */ +#include +#include +#include +#include + + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + int pagesize; + + pagesize = sysconf(_SC_PAGE_SIZE); + return memalign(pagesize, size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +#endif + +#endif /* __CPU_MEM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/error_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/error_support.h new file mode 100644 index 000000000000..9fe1f65125e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/error_support.h @@ -0,0 +1,110 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ERROR_SUPPORT_H +#define __ERROR_SUPPORT_H + +#if defined(__KERNEL__) +#include +#else +#include +#endif +#include + +/* OS-independent definition of IA_CSS errno values */ +/* #define IA_CSS_EINVAL 1 */ +/* #define IA_CSS_EFAULT 2 */ + +#ifdef __HIVECC +#define ERR_EMBEDDED 1 +#else +#define ERR_EMBEDDED 0 +#endif + +#if ERR_EMBEDDED +#define DECLARE_ERRVAL +#else +#define DECLARE_ERRVAL \ + int _errval = 0; +#endif + +/* Use "owl" in while to prevent compiler warnings in Windows */ +#define ALWAYS_FALSE ((void)0, 0) + +#define verifret(cond, error_type) \ +do { \ + if (!(cond)) { \ + return error_type; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmp(cond, error_tag) \ +do { \ + if (!(cond)) { \ + goto error_tag; \ + } \ +} while (ALWAYS_FALSE) + +#define verifexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#if ERR_EMBEDDED +#define verifexitval(cond, error_tag) \ +do { \ + assert(cond); \ +} while (ALWAYS_FALSE) +#else +#define verifexitval(cond, error_tag) \ +do { \ + if (!(cond)) { \ + _errval = (error_tag); \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) +#endif + +#if ERR_EMBEDDED +#define haserror(error_tag) (0) +#else +#define haserror(error_tag) \ + (_errval == (error_tag)) +#endif + +#if ERR_EMBEDDED +#define noerror() (1) +#else +#define noerror() \ + (_errval == 0) +#endif + +#define verifjmpexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmpexitsetretval(cond, retval) \ +do { \ + if (!(cond)) { \ + retval = -1; \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#endif /* __ERROR_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/math_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/math_support.h new file mode 100644 index 000000000000..633f86f1a1b0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/math_support.h @@ -0,0 +1,314 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MATH_SUPPORT_H +#define __MATH_SUPPORT_H + +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "type_support.h" +#include "assert_support.h" + +/* in case we have min/max/MIN/MAX macro's undefine them */ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#ifdef MIN /* also defined in include/hrt/numeric.h from SDK */ +#undef MIN +#endif +#ifdef MAX +#undef MAX +#endif + +#ifndef UINT16_MAX +#define UINT16_MAX (0xffffUL) +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffffUL) +#endif + +#define IS_ODD(a) ((a) & 0x1) +#define IS_EVEN(a) (!IS_ODD(a)) +#define IS_POWER2(a) (!((a)&((a)-1))) +#define IS_MASK_BITS_SET(a, b) ((a & b) != 0) + +/*To Find next power of 2 number from x */ +#define bit2(x) ((x) | ((x) >> 1)) +#define bit4(x) (bit2(x) | (bit2(x) >> 2)) +#define bit8(x) (bit4(x) | (bit4(x) >> 4)) +#define bit16(x) (bit8(x) | (bit8(x) >> 8)) +#define bit32(x) (bit16(x) | (bit16(x) >> 16)) +#define NEXT_POWER_OF_2(x) (bit32(x-1) + 1) + +/* force a value to a lower even value */ +#define EVEN_FLOOR(x) ((x) & ~1UL) + +/* A => B */ +#define IMPLIES(a, b) (!(a) || (b)) + +/* The ORIG_BITS th bit is the sign bit */ +/* Sign extends a ORIG_BITS bits long signed number to a 64-bit signed number */ +/* By type casting it can relimited to any valid type-size + * (32-bit signed or 16-bit or 8-bit) + */ +/* By masking it can be transformed to any arbitrary bit size */ +#define SIGN_EXTEND(VAL, ORIG_BITS) \ +((~(((VAL)&(1ULL<<((ORIG_BITS)-1)))-1))|(VAL)) + +#define EXTRACT_BIT(a, b) ((a >> b) & 1) + +/* for preprocessor and array sizing use MIN and MAX + otherwise use min and max */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CLIP(a, b, c) MIN((MAX((a), (b))), (c)) +/* Integer round-down division of a with b */ +#define FLOOR_DIV(a, b) ((b) ? ((a) / (b)) : 0) +/* Align a to the lower multiple of b */ +#define FLOOR_MUL(a, b) (FLOOR_DIV(a, b) * (b)) +/* Integer round-up division of a with b */ +#define CEIL_DIV(a, b) ((b) ? (((a) + (b) - 1) / (b)) : 0) +/* Align a to the upper multiple of b */ +#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) +/* Align a to the upper multiple of b - fast implementation + * for cases when b=pow(2,n) + */ +#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) +/* integer round-up division of a with pow(2,b) */ +#define CEIL_SHIFT(a, b) (((a) + (1UL << (b)) - 1) >> (b)) +/* Align a to the upper multiple of pow(2,b) */ +#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) +/* Absolute difference of a and b */ +#define ABS_DIF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define ABS(a) ABS_DIF(a, 0) +/* Square of x */ +#define SQR(x) ((x)*(x)) +/* Integer round-half-down division of a nad b */ +#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0) +/* Align a to the round-half-down multiple of b */ +#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) + +#define MAX3(a, b, c) MAX((a), MAX((b), (c))) +#define MIN3(a, b, c) MIN((a), MIN((b), (c))) +#define MAX4(a, b, c, d) MAX((MAX((a), (b))), (MAX((c), (d)))) +#define MIN4(a, b, c, d) MIN((MIN((a), (b))), (MIN((c), (d)))) + +/* min and max should not be macros as they will evaluate their arguments twice. + if you really need a macro (e.g. for CPP or for initializing an array) + use MIN() and MAX(), otherwise use min() and max() */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a)))) +#endif + +#ifndef BYTES +#define BYTES(bit) (((bit)+7)/8) +#endif + +#if !defined(PIPE_GENERATION) +STORAGE_CLASS_INLINE unsigned int max_value_bits(unsigned int bits) +{ + return (bits == 0) ? 0 : ((2 * ((1 << ((bits) - 1)) - 1)) + 1); +} +STORAGE_CLASS_INLINE unsigned int max_value_bytes(unsigned int bytes) +{ + return max_value_bits(IA_CSS_UINT8_T_BITS * bytes); +} +STORAGE_CLASS_INLINE int max(int a, int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE int min(int a, int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE int clip(int a, int b, int c) +{ + return min(max(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int umax(unsigned int a, unsigned int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE unsigned int umin(unsigned int a, unsigned int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uclip(unsigned int a, unsigned int b, + unsigned int c) +{ + return umin(umax(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int ceil_div(unsigned int a, unsigned int b) +{ + return CEIL_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul(unsigned int a, unsigned int b) +{ + return CEIL_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul2(unsigned int a, unsigned int b) +{ + return CEIL_MUL2(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift_mul(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT_MUL(a, b); +} + +STORAGE_CLASS_INLINE int abs_dif(int a, int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uabs_dif(unsigned int a, unsigned int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_div(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_mul(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_pow2(uint32_t a) +{ + unsigned int retval = 0; + + if (IS_POWER2(a)) { + retval = (unsigned int)a; + } else { + unsigned int v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + retval = (unsigned int)(v+1); + } + return retval; +} + +STORAGE_CLASS_INLINE unsigned int floor_log2(uint32_t a) +{ + static const uint8_t de_bruijn[] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + uint32_t v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + return (unsigned int)de_bruijn[(v*0x07C4ACDDU)>>27]; +} + +/* Divide by small power of two */ +STORAGE_CLASS_INLINE unsigned int +udiv2_small_i(uint32_t a, uint32_t b) +{ + assert(b <= 2); + return a >> (b-1); +} + +/* optimized divide for small results + * a will be divided by b + * outbits is the number of bits needed for the result + * the smaller the cheaper the function will be. + * if the result doesn't fit in the number of output bits + * the result is incorrect and the function will assert + */ +STORAGE_CLASS_INLINE unsigned int +udiv_medium(uint32_t a, uint32_t b, unsigned outbits) +{ + int bit; + unsigned res = 0; + unsigned mask; + +#ifdef VOLCANO +#pragma ipu unroll +#endif + for (bit = outbits-1 ; bit >= 0; bit--) { + mask = 1<= (b<= c ? a+b-c : a+b); +} + +/* + * For SP and ISP, SDK provides the definition of OP_asp_slor. + * We need it only for host + */ +STORAGE_CLASS_INLINE unsigned int OP_asp_slor(int a, int b, int c) +{ + return ((a << c) | b); +} +#else +#include "hive/customops.h" +#endif /* !defined(__VIED_CELL) */ + +#endif /* !defined(PIPE_GENERATION) */ + +#if !defined(__KERNEL__) +#define clamp(a, min_val, max_val) MIN(MAX((a), (min_val)), (max_val)) +#endif /* !defined(__KERNEL__) */ + +#endif /* __MATH_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/misc_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/misc_support.h new file mode 100644 index 000000000000..a2c2729e946d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/misc_support.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MISC_SUPPORT_H +#define __MISC_SUPPORT_H + +/* suppress compiler warnings on unused variables */ +#ifndef NOT_USED +#define NOT_USED(a) ((void)(a)) +#endif + +/* Calculate the total bytes for pow(2) byte alignment */ +#define tot_bytes_for_pow2_align(pow2, cur_bytes) \ + ((cur_bytes + (pow2 - 1)) & ~(pow2 - 1)) + +/* Display the macro value given a string */ +#define _STR(x) #x +#define STR(x) _STR(x) + +/* Concatenate */ +#ifndef CAT /* also defined in */ +#define _CAT(a, b) a ## b +#define CAT(a, b) _CAT(a, b) +#endif + +#define _CAT3(a, b, c) a ## b ## c +#define CAT3(a, b, c) _CAT3(a, b, c) + +/* NO_HOIST, NO_CSE, NO_ALIAS attributes must be ignored for host code */ +#ifndef __HIVECC +#ifndef NO_HOIST +#define NO_HOIST +#endif +#ifndef NO_CSE +#define NO_CSE +#endif +#ifndef NO_ALIAS +#define NO_ALIAS +#endif +#endif + +enum hive_method_id { + HIVE_METHOD_ID_CRUN, + HIVE_METHOD_ID_UNSCHED, + HIVE_METHOD_ID_SCHED, + HIVE_METHOD_ID_TARGET +}; + +/* Derive METHOD */ +#if defined(C_RUN) + #define HIVE_METHOD "crun" + #define HIVE_METHOD_ID HIVE_METHOD_ID_CRUN +#elif defined(HRT_UNSCHED) + #define HIVE_METHOD "unsched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_UNSCHED +#elif defined(HRT_SCHED) + #define HIVE_METHOD "sched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_SCHED +#else + #define HIVE_METHOD "target" + #define HIVE_METHOD_ID HIVE_METHOD_ID_TARGET + #define HRT_TARGET 1 +#endif + +#endif /* __MISC_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/platform_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/platform_support.h new file mode 100644 index 000000000000..1752efc7b4df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/platform_support.h @@ -0,0 +1,146 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PLATFORM_SUPPORT_H +#define __PLATFORM_SUPPORT_H + +#include "storage_class.h" + +#define MSEC_IN_SEC 1000 +#define NSEC_IN_MSEC 1000000 + +#if defined(_MSC_VER) +#include + +#define IA_CSS_EXTERN +#define SYNC_WITH(x) +#define CSS_ALIGN(d, a) _declspec(align(a)) d + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + /* Placeholder for driver team*/ +} + +STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) +{ + /* Placeholder for driver team*/ + (void)delay_time_ms; +} + +#elif defined(__HIVECC) +#include +#include + +#define IA_CSS_EXTERN extern +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + OP___schedule(); +} + +#elif defined(__KERNEL__) +#include +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __aligned(a) + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + usleep_range(1, 50); +} + +#elif defined(__GNUC__) +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) + +/* Define some __HIVECC specific macros to nothing to allow host code compilation */ +#ifndef NO_ALIAS +#define NO_ALIAS +#endif + +#ifndef SYNC_WITH +#define SYNC_WITH(x) +#endif + +#if defined(HRT_CSIM) + #include "hrt/host.h" /* Using hrt_sleep from hrt/host.h */ + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + /* For the SDK still using hrt_sleep */ + hrt_sleep(); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + /* For the SDK still using hrt_sleep */ + long unsigned int i = 0; + for (i = 0; i < delay_time_ms; i++) { + hrt_sleep(); + } + } +#else + #include + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + struct timespec delay_time; + + delay_time.tv_sec = 0; + delay_time.tv_nsec = 10; + nanosleep(&delay_time, NULL); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + struct timespec delay_time; + + if (delay_time_ms >= MSEC_IN_SEC) { + delay_time.tv_sec = delay_time_ms / MSEC_IN_SEC; + delay_time.tv_nsec = (delay_time_ms % MSEC_IN_SEC) * NSEC_IN_MSEC; + } else { + delay_time.tv_sec = 0; + delay_time.tv_nsec = delay_time_ms * NSEC_IN_MSEC; + } + nanosleep(&delay_time, NULL); + } +#endif + +#else +#include +#endif + +/*needed for the include in stdint.h for various environments */ +#include "type_support.h" +#include "storage_class.h" + +#define MAX_ALIGNMENT 8 +#define aligned_uint8(type, obj) CSS_ALIGN(uint8_t obj, 1) +#define aligned_int8(type, obj) CSS_ALIGN(int8_t obj, 1) +#define aligned_uint16(type, obj) CSS_ALIGN(uint16_t obj, 2) +#define aligned_int16(type, obj) CSS_ALIGN(int16_t obj, 2) +#define aligned_uint32(type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_int32(type, obj) CSS_ALIGN(int32_t obj, 4) + +/* needed as long as hivecc does not define the type (u)int64_t */ +#if defined(__HIVECC) +#define aligned_uint64(type, obj) CSS_ALIGN(unsigned long long obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(signed long long obj, 8) +#else +#define aligned_uint64(type, obj) CSS_ALIGN(uint64_t obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(int64_t obj, 8) +#endif +#define aligned_enum(enum_type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_struct(struct_type, obj) struct_type obj + +#endif /* __PLATFORM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/print_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/print_support.h new file mode 100644 index 000000000000..0b614f7ef12d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/print_support.h @@ -0,0 +1,90 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PRINT_SUPPORT_H +#define __PRINT_SUPPORT_H + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE + +/* TODO: Windows driver team to provide tracing mechanism for kernel mode + * e.g. DbgPrint and DbgPrintEx + */ +extern void FwTracePrintPWARN(const char *fmt, ...); +extern void FwTracePrintPRINT(const char *fmt, ...); +extern void FwTracePrintPERROR(const char *fmt, ...); +extern void FwTracePrintPDEBUG(const char *fmt, ...); + +#define PWARN(format, ...) FwTracePrintPWARN(format, __VA_ARGS__) +#define PRINT(format, ...) FwTracePrintPRINT(format, __VA_ARGS__) +#define PERROR(format, ...) FwTracePrintPERROR(format, __VA_ARGS__) +#define PDEBUG(format, ...) FwTracePrintPDEBUG(format, __VA_ARGS__) + +#else +/* Windows usermode compilation */ +#include + +/* To change the defines below, communicate with Windows team first + * to ensure they will not get flooded with prints + */ +/* This is temporary workaround to avoid flooding userspace + * Windows driver with prints + */ + +#define PWARN(format, ...) +#define PRINT(format, ...) +#define PERROR(format, ...) printf("error: " format, __VA_ARGS__) +#define PDEBUG(format, ...) + +#endif /* _KERNEL_MODE */ + +#elif defined(__HIVECC) +#include +/* To be revised + +#define PWARN(format) +#define PRINT(format) OP___printstring(format) +#define PERROR(variable) OP___dump(9999, arguments) +#define PDEBUG(variable) OP___dump(__LINE__, arguments) + +*/ + +#define PRINTSTRING(str) OP___printstring(str) + +#elif defined(__KERNEL__) +#include +#include + + +#define PWARN(format, arguments...) pr_debug(format, ##arguments) +#define PRINT(format, arguments...) pr_debug(format, ##arguments) +#define PERROR(format, arguments...) pr_debug(format, ##arguments) +#define PDEBUG(format, arguments...) pr_debug(format, ##arguments) + +#else +#include + +#define PRINT_HELPER(prefix, format, ...) printf(prefix format "%s", __VA_ARGS__) + +/* The trailing "" allows the edge case of printing single string */ +#define PWARN(...) PRINT_HELPER("warning: ", __VA_ARGS__, "") +#define PRINT(...) PRINT_HELPER("", __VA_ARGS__, "") +#define PERROR(...) PRINT_HELPER("error: ", __VA_ARGS__, "") +#define PDEBUG(...) PRINT_HELPER("debug: ", __VA_ARGS__, "") + +#define PRINTSTRING(str) PRINT(str) + +#endif + +#endif /* __PRINT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/storage_class.h new file mode 100644 index 000000000000..af19b4026220 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/storage_class.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __STORAGE_CLASS_H +#define __STORAGE_CLASS_H + +#define STORAGE_CLASS_EXTERN \ +extern + +#if defined(_MSC_VER) +#define STORAGE_CLASS_INLINE \ +static __inline +#elif defined(__HIVECC) +#define STORAGE_CLASS_INLINE \ +static inline +#else +#define STORAGE_CLASS_INLINE \ +static inline +#endif + +/* Register struct */ +#ifndef __register +#if defined(__HIVECC) && !defined(PIPE_GENERATION) +#define __register register +#else +#define __register +#endif +#endif + +/* Memory attribute */ +#ifndef MEM +#ifdef PIPE_GENERATION +#elif defined(__HIVECC) +#include +#else +#define MEM(any_mem) +#endif +#endif + +#endif /* __STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/type_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/type_support.h new file mode 100644 index 000000000000..a86da0e78941 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/support/type_support.h @@ -0,0 +1,80 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __TYPE_SUPPORT_H +#define __TYPE_SUPPORT_H + +/* Per the DLI spec, types are in "type_support.h" and + * "platform_support.h" is for unclassified/to be refactored + * platform specific definitions. + */ +#define IA_CSS_UINT8_T_BITS 8 +#define IA_CSS_UINT16_T_BITS 16 +#define IA_CSS_UINT32_T_BITS 32 +#define IA_CSS_INT32_T_BITS 32 +#define IA_CSS_UINT64_T_BITS 64 + + +#if defined(_MSC_VER) +#include +#include +#include +#include +#if defined(_M_X64) +#define HOST_ADDRESS(x) (unsigned long long)(x) +#else +#define HOST_ADDRESS(x) (unsigned long)(x) +#endif + +#elif defined(PARAM_GENERATION) +/* Nothing */ +#elif defined(__HIVECC) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif defined(__KERNEL__) +#include +#include + +#define CHAR_BIT (8) +#define HOST_ADDRESS(x) (unsigned long)(x) + +#elif defined(__GNUC__) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#else /* default is for the FIST environment */ +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#endif + +#if !defined(PIPE_GENERATION) && !defined(IO_GENERATION) +/* genpipe cannot handle the void* syntax */ +typedef void *HANDLE; +#endif + +#endif /* __TYPE_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom.h new file mode 100644 index 000000000000..5426d6d18e0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom.h @@ -0,0 +1,247 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_H +#define __IA_CSS_SYSCOM_H + + +/* + * The CSS Subsystem Communication Interface - Host side + * + * It provides subsystem initialzation, send ports and receive ports + * The PSYS and ISYS interfaces are implemented on top of this interface. + */ + +#include "ia_css_syscom_config.h" + +#define FW_ERROR_INVALID_PARAMETER (-1) +#define FW_ERROR_BAD_ADDRESS (-2) +#define FW_ERROR_BUSY (-3) +#define FW_ERROR_NO_MEMORY (-4) + +struct ia_css_syscom_context; + +/** + * ia_css_syscom_size() - provide syscom external buffer requirements + * @config: pointer to the configuration data (read) + * @size: pointer to the buffer size (write) + * + * Purpose: + * - Provide external buffer requirements + * - To be used for external buffer allocation + * + */ +extern void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size +); + +/** + * ia_css_syscom_open() - initialize a subsystem context + * @config: pointer to the configuration data (read) + * @buf: pointer to externally allocated buffers (read) + * @returns: struct ia_css_syscom_context* on success, 0 otherwise. + * + * Purpose: + * - initialize host side data structures + * - boot the subsystem? + * + */ +extern struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *config, + struct ia_css_syscom_buf *buf +); + +/** + * ia_css_syscom_close() - signal close to cell + * @context: pointer to the subsystem context + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if SPC is not ready yet. + * + * Purpose: + * Request from the Cell to terminate + */ +extern int +ia_css_syscom_close( + struct ia_css_syscom_context *context +); + +/** + * ia_css_syscom_release() - free context + * @context: pointer to the subsystem context + * @force: flag which specifies whether cell + * state will be checked before freeing the + * context. + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if cell + * is busy and call was not forced. + * + * Purpose: + * 2 modes, with first (force==true) immediately + * free context, and second (force==false) verifying + * that the cell state is ok and freeing context if so, + * returning error otherwise. + */ +extern int +ia_css_syscom_release( + struct ia_css_syscom_context *context, + unsigned int force +); + +/** + * Open a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be sent to a port without error. + * @context: pointer to the subsystem context + * @port: send port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Send a token to the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: send port index + * @token: pointer to the token value that is transferred to the subsystem + * @returns: number of tokens sent on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + const void *token +); + +/** + * Open a port for receiving tokens to the subsystem + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for receiving tokens to the subsystem + * Returns 0 on success, otherwise negative value of error code + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be received from a port without errors. + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Receive a token from the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: receive port index + * @token (output): pointer to (space for) the token to be received + * @returns: number of tokens received on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + void *token +); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/** + * ia_css_syscom_store_dmem() - store subsystem context information in DMEM + * @context: pointer to the subsystem context + * @ssid: subsystem id + * @vtl0_addr_mask: VTL0 address mask; only applicable when the passed in context is secure + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *context, + unsigned int ssid, + unsigned int vtl0_addr_mask +); + +/** + * ia_css_syscom_set_trustlet_status() - store truslet configuration setting + * @context: pointer to the subsystem context + * @trustlet_exist: 1 if trustlet exists + */ +extern void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +); + +/** + * ia_css_syscom_is_ab_spc_ready() - check if SPC access blocker programming is completed + * @context: pointer to the subsystem context + * @returns: 1 when status is ready. 0 otherwise + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +#endif /* __IA_CSS_SYSCOM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_config.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_config.h new file mode 100644 index 000000000000..2f5eb309df94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_config.h @@ -0,0 +1,97 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_H +#define __IA_CSS_SYSCOM_CONFIG_H + +#include +#include + +/* syscom size struct, output of ia_css_syscom_size, + * input for (external) allocation + */ +struct ia_css_syscom_size { + /* Size of host buffer */ + unsigned int cpu; + /* Size of shared config buffer (host to cell) */ + unsigned int shm; + /* Size of shared input queue buffers (host to cell) */ + unsigned int ibuf; + /* Size of shared output queue buffers (cell to host) */ + unsigned int obuf; +}; + +/* syscom buffer struct, output of (external) allocation, + * input for ia_css_syscom_open + */ +struct ia_css_syscom_buf { + char *cpu; /* host buffer */ + + /* shared memory buffer host address */ + host_virtual_address_t shm_host; + /* shared memory buffer cell address */ + vied_virtual_address_t shm_cell; + + /* input queue shared buffer host address */ + host_virtual_address_t ibuf_host; + /* input queue shared buffer cell address */ + vied_virtual_address_t ibuf_cell; + + /* output queue shared buffer host address */ + host_virtual_address_t obuf_host; + /* output queue shared buffer cell address */ + vied_virtual_address_t obuf_cell; +}; + +struct ia_css_syscom_queue_config { + unsigned int queue_size; /* tokens per queue */ + unsigned int token_size; /* bytes per token */ +}; + +/** + * Parameter struct for ia_css_syscom_open + */ +struct ia_css_syscom_config { + /* This member in no longer used in syscom. + It is kept to not break any driver builds, and will be removed when + all assignments have been removed from driver code */ + /* address of firmware in DDR/IMR */ + unsigned long long host_firmware_address; + + /* address of firmware in DDR, seen from SPC */ + unsigned int vied_firmware_address; + + unsigned int ssid; + unsigned int mmid; + + unsigned int num_input_queues; + unsigned int num_output_queues; + struct ia_css_syscom_queue_config *input; + struct ia_css_syscom_queue_config *output; + + unsigned int regs_addr; + unsigned int dmem_addr; + + /* firmware-specific configuration data */ + void *specific_addr; + unsigned int specific_size; + + /* if true; secure syscom in VTIO Case + * if false, non-secure syscom + */ + bool secure; + unsigned int vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_trace.h new file mode 100644 index 000000000000..2c32693c2a82 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/interface/ia_css_syscom_trace.h @@ -0,0 +1,51 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_SYSCOM_TRACE_H +#define __IA_CSS_SYSCOM_TRACE_H + +#include "ia_css_trace.h" + +#define SYSCOM_TRACE_LEVEL_DEFAULT 1 +#define SYSCOM_TRACE_LEVEL_DEBUG 2 + +/* Set to default level if no level is defined */ +#ifndef SYSCOM_TRACE_LEVEL +#define SYSCOM_TRACE_LEVEL SYSCOM_TRACE_LEVEL_DEFAULT +#endif /* SYSCOM_TRACE_LEVEL */ + +/* SYSCOM Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#define SYSCOM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + +#if (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEFAULT) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +#elif (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEBUG) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +#else +# error "Connection manager trace level not defined!" +#endif /* SYSCOM_TRACE_LEVEL */ + +#endif /* __IA_CSS_SYSCOM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom.c new file mode 100644 index 000000000000..cdf9df0531ff --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom.c @@ -0,0 +1,650 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_syscom.h" + +#include "ia_css_syscom_context.h" +#include "ia_css_syscom_config_fw.h" +#include "ia_css_syscom_trace.h" + +#include "queue.h" +#include "send_port.h" +#include "recv_port.h" +#include "regmem_access.h" + +#include "error_support.h" +#include "cpu_mem_support.h" + +#include "queue_struct.h" +#include "send_port_struct.h" +#include "recv_port_struct.h" + +#include "type_support.h" +#include +#include +#include "platform_support.h" + +#include "ia_css_cell.h" + +/* struct of internal buffer sizes */ +struct ia_css_syscom_size_intern { + unsigned int context; + unsigned int input_queue; + unsigned int output_queue; + unsigned int input_port; + unsigned int output_port; + + unsigned int fw_config; + unsigned int specific; + + unsigned int input_buffer; + unsigned int output_buffer; +}; + +/* Allocate buffers internally, when no buffers are provided */ +static int +ia_css_syscom_alloc( + unsigned int ssid, + unsigned int mmid, + const struct ia_css_syscom_size *size, + struct ia_css_syscom_buf *buf) +{ + /* zero the buffer to set all pointers to zero */ + memset(buf, 0, sizeof(*buf)); + + /* allocate cpu_mem */ + buf->cpu = (char *)ia_css_cpu_mem_alloc(size->cpu); + if (!buf->cpu) + goto EXIT7; + + /* allocate and map shared config buffer */ + buf->shm_host = shared_memory_alloc(mmid, size->shm); + if (!buf->shm_host) + goto EXIT6; + buf->shm_cell = shared_memory_map(ssid, mmid, buf->shm_host); + if (!buf->shm_cell) + goto EXIT5; + + /* allocate and map input queue buffer */ + buf->ibuf_host = shared_memory_alloc(mmid, size->ibuf); + if (!buf->ibuf_host) + goto EXIT4; + buf->ibuf_cell = shared_memory_map(ssid, mmid, buf->ibuf_host); + if (!buf->ibuf_cell) + goto EXIT3; + + /* allocate and map output queue buffer */ + buf->obuf_host = shared_memory_alloc(mmid, size->obuf); + if (!buf->obuf_host) + goto EXIT2; + buf->obuf_cell = shared_memory_map(ssid, mmid, buf->obuf_host); + if (!buf->obuf_cell) + goto EXIT1; + + return 0; + +EXIT1: shared_memory_free(mmid, buf->obuf_host); +EXIT2: shared_memory_unmap(ssid, mmid, buf->ibuf_cell); +EXIT3: shared_memory_free(mmid, buf->ibuf_host); +EXIT4: shared_memory_unmap(ssid, mmid, buf->shm_cell); +EXIT5: shared_memory_free(mmid, buf->shm_host); +EXIT6: ia_css_cpu_mem_free(buf->cpu); +EXIT7: return FW_ERROR_NO_MEMORY; +} + +static void +ia_css_syscom_size_intern( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size_intern *size) +{ + /* convert syscom config into syscom internal size struct */ + + unsigned int i; + + size->context = sizeof(struct ia_css_syscom_context); + size->input_queue = cfg->num_input_queues * sizeof(struct sys_queue); + size->output_queue = cfg->num_output_queues * sizeof(struct sys_queue); + size->input_port = cfg->num_input_queues * sizeof(struct send_port); + size->output_port = cfg->num_output_queues * sizeof(struct recv_port); + + size->fw_config = sizeof(struct ia_css_syscom_config_fw); + size->specific = cfg->specific_size; + + /* accumulate input queue buffer sizes */ + size->input_buffer = 0; + for (i = 0; i < cfg->num_input_queues; i++) { + size->input_buffer += + sys_queue_buf_size(cfg->input[i].queue_size, + cfg->input[i].token_size); + } + + /* accumulate outut queue buffer sizes */ + size->output_buffer = 0; + for (i = 0; i < cfg->num_output_queues; i++) { + size->output_buffer += + sys_queue_buf_size(cfg->output[i].queue_size, + cfg->output[i].token_size); + } +} + +static void +ia_css_syscom_size_extern( + const struct ia_css_syscom_size_intern *i, + struct ia_css_syscom_size *e) +{ + /* convert syscom internal size struct into external size struct */ + + e->cpu = i->context + i->input_queue + i->output_queue + + i->input_port + i->output_port; + e->shm = i->fw_config + i->input_queue + i->output_queue + i->specific; + e->ibuf = i->input_buffer; + e->obuf = i->output_buffer; +} + +/* Function that provides buffer sizes to be allocated */ +void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size) +{ + struct ia_css_syscom_size_intern i; + + ia_css_syscom_size_intern(cfg, &i); + ia_css_syscom_size_extern(&i, size); +} + +static struct ia_css_syscom_context* +ia_css_syscom_assign_buf( + const struct ia_css_syscom_size_intern *i, + const struct ia_css_syscom_buf *buf) +{ + struct ia_css_syscom_context *ctx; + char *cpu_mem_buf; + host_virtual_address_t shm_buf_host; + vied_virtual_address_t shm_buf_cell; + + /* host context */ + cpu_mem_buf = buf->cpu; + + ctx = (struct ia_css_syscom_context *)cpu_mem_buf; + ia_css_cpu_mem_set_zero(ctx, i->context); + cpu_mem_buf += i->context; + + ctx->input_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->input_queue; + + ctx->output_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->output_queue; + + ctx->send_port = (struct send_port *) cpu_mem_buf; + cpu_mem_buf += i->input_port; + + ctx->recv_port = (struct recv_port *) cpu_mem_buf; + + + /* cell config */ + shm_buf_host = buf->shm_host; + shm_buf_cell = buf->shm_cell; + + ctx->config_host_addr = shm_buf_host; + shm_buf_host += i->fw_config; + ctx->config_vied_addr = shm_buf_cell; + shm_buf_cell += i->fw_config; + + ctx->input_queue_host_addr = shm_buf_host; + shm_buf_host += i->input_queue; + ctx->input_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->input_queue; + + ctx->output_queue_host_addr = shm_buf_host; + shm_buf_host += i->output_queue; + ctx->output_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->output_queue; + + ctx->specific_host_addr = shm_buf_host; + ctx->specific_vied_addr = shm_buf_cell; + + ctx->ibuf_host_addr = buf->ibuf_host; + ctx->ibuf_vied_addr = buf->ibuf_cell; + + ctx->obuf_host_addr = buf->obuf_host; + ctx->obuf_vied_addr = buf->obuf_cell; + + return ctx; +} + +struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *cfg, + struct ia_css_syscom_buf *buf_extern +) +{ + struct ia_css_syscom_size_intern size_intern; + struct ia_css_syscom_size size; + struct ia_css_syscom_buf buf_intern; + struct ia_css_syscom_buf *buf; + struct ia_css_syscom_context *ctx; + struct ia_css_syscom_config_fw fw_cfg; + unsigned int i; + struct sys_queue_res res; + + IA_CSS_TRACE_0(SYSCOM, INFO, "Entered: ia_css_syscom_open\n"); + + /* error handling */ + if (cfg == NULL) + return NULL; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) start\n", cfg->secure); + + /* check members of cfg: TBD */ + + /* + * Check if SP is in valid state, have to wait if not ready. + * In some platform (Such as VP), it will need more time to wait due to system performance; + * If return NULL without wait for SPC0 ready, Driver load FW will failed + */ + ia_css_cell_wait(cfg->ssid, SPC0); + + ia_css_syscom_size_intern(cfg, &size_intern); + ia_css_syscom_size_extern(&size_intern, &size); + + if (buf_extern) { + /* use externally allocated buffers */ + buf = buf_extern; + } else { + /* use internally allocated buffers */ + buf = &buf_intern; + if (ia_css_syscom_alloc(cfg->ssid, cfg->mmid, &size, buf) != 0) + return NULL; + } + + /* assign buffer pointers */ + ctx = ia_css_syscom_assign_buf(&size_intern, buf); + /* only need to free internally allocated buffers */ + ctx->free_buf = !buf_extern; + + ctx->cell_regs_addr = cfg->regs_addr; + /* regmem is at cell_dmem_addr + REGMEM_OFFSET */ + ctx->cell_dmem_addr = cfg->dmem_addr; + + ctx->num_input_queues = cfg->num_input_queues; + ctx->num_output_queues = cfg->num_output_queues; + + ctx->env.mmid = cfg->mmid; + ctx->env.ssid = cfg->ssid; + ctx->env.mem_addr = cfg->dmem_addr; + + ctx->regmem_idx = SYSCOM_QPR_BASE_REG; + + /* initialize input queues */ + res.reg = SYSCOM_QPR_BASE_REG; + res.host_address = ctx->ibuf_host_addr; + res.vied_address = ctx->ibuf_vied_addr; + for (i = 0; i < cfg->num_input_queues; i++) { + sys_queue_init(ctx->input_queue + i, + cfg->input[i].queue_size, + cfg->input[i].token_size, &res); + } + + /* initialize output queues */ + res.host_address = ctx->obuf_host_addr; + res.vied_address = ctx->obuf_vied_addr; + for (i = 0; i < cfg->num_output_queues; i++) { + sys_queue_init(ctx->output_queue + i, + cfg->output[i].queue_size, + cfg->output[i].token_size, &res); + } + + /* fill shared queue structs */ + shared_memory_store(cfg->mmid, ctx->input_queue_host_addr, + ctx->input_queue, + cfg->num_input_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->input_queue_host_addr), + cfg->num_input_queues * sizeof(struct sys_queue)); + shared_memory_store(cfg->mmid, ctx->output_queue_host_addr, + ctx->output_queue, + cfg->num_output_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->output_queue_host_addr), + cfg->num_output_queues * sizeof(struct sys_queue)); + + /* Zero the queue buffers. Is this really needed? */ + shared_memory_zero(cfg->mmid, buf->ibuf_host, size.ibuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->ibuf_host), + size.ibuf); + shared_memory_zero(cfg->mmid, buf->obuf_host, size.obuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->obuf_host), + size.obuf); + + /* copy firmware specific data */ + if (cfg->specific_addr && cfg->specific_size) { + shared_memory_store(cfg->mmid, ctx->specific_host_addr, + cfg->specific_addr, cfg->specific_size); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->specific_host_addr), + cfg->specific_size); + } + + fw_cfg.num_input_queues = cfg->num_input_queues; + fw_cfg.num_output_queues = cfg->num_output_queues; + fw_cfg.input_queue = ctx->input_queue_vied_addr; + fw_cfg.output_queue = ctx->output_queue_vied_addr; + fw_cfg.specific_addr = ctx->specific_vied_addr; + fw_cfg.specific_size = cfg->specific_size; + + shared_memory_store(cfg->mmid, ctx->config_host_addr, + &fw_cfg, sizeof(struct ia_css_syscom_config_fw)); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(ctx->config_host_addr), + sizeof(struct ia_css_syscom_config_fw)); + +#if !HAS_DUAL_CMD_CTX_SUPPORT + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, cfg->ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, cfg->ssid); + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, cfg->ssid); +#endif + + /* Indicate if ctx is created for secure stream purpose */ + ctx->secure = cfg->secure; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) completed\n", cfg->secure); + return ctx; +} + + +int +ia_css_syscom_close( + struct ia_css_syscom_context *ctx +) { + int state; + + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle close request yet */ + return FW_ERROR_BUSY; + } + + /* set close request flag */ + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_INACTIVE, ctx->env.ssid); + + return 0; +} + +static void +ia_css_syscom_free(struct ia_css_syscom_context *ctx) +{ + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->ibuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->ibuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->obuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->obuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, + ctx->config_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->config_host_addr); + ia_css_cpu_mem_free(ctx); +} + +int +ia_css_syscom_release( + struct ia_css_syscom_context *ctx, + unsigned int force +) { + /* check if release is forced, an verify cell state if it is not */ + if (!force) { + if (!ia_css_cell_is_ready(ctx->env.ssid, SPC0)) + return FW_ERROR_BUSY; + } + + /* Reset the regmem idx */ + ctx->regmem_idx = 0; + + if (ctx->free_buf) + ia_css_syscom_free(ctx); + + return 0; +} + +int ia_css_syscom_send_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + send_port_open(ctx->send_port + port, + ctx->input_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_send_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +int ia_css_syscom_send_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_available(ctx->send_port + port); +} + +int ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + const void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_transfer(ctx->send_port + port, token); +} + +int ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, + SYSCOM_STATE_REG, ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + recv_port_open(ctx->recv_port + port, + ctx->output_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +/* + * Get the number of responses in the response queue + */ +int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_available(ctx->recv_port + port); +} + + +/* + * Dequeue the head of the response queue + * returns an error when the response queue is empty + */ +int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_transfer(ctx->recv_port + port, token); +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* + * store subsystem context information in DMEM + */ +int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *ctx, + unsigned int ssid, + unsigned int vtl0_addr_mask +) +{ + unsigned int read_back; + + NOT_USED(vtl0_addr_mask); + NOT_USED(read_back); + + if (ctx->secure) { + /* store VTL0 address mask in 'secure' context */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem VTL0_ADDR_MASK (%#x) @ dmem_addr %#x ssid %d\n", + vtl0_addr_mask, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_VTL0_ADDR_MASK, vtl0_addr_mask, ssid); + } + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, ssid); + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, ssid); + + return 0; +} + +/* + * store truslet configuration status setting + */ +void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +) +{ + unsigned int value; + + value = trustlet_exist ? TRUSTLET_EXIST : TRUSTLET_NOT_EXIST; + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_set_trustlet_status TRUSTLET_STATUS (%#x) @ dmem_addr %#x ssid %d\n", + value, dmem_addr, ssid); + regmem_store_32(dmem_addr, TRUSTLET_STATUS, value, ssid); +} + +/* + * check if SPC access blocker programming is completed + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +) +{ + unsigned int value; + + /* We only expect the call from non-secure context only */ + if (ctx->secure) { + IA_CSS_TRACE_0(SYSCOM, ERROR, "ia_css_syscom_is_spc_ab_ready - Please call from non-secure context\n"); + return false; + } + + value = regmem_load_32(ctx->cell_dmem_addr, AB_SPC_STATUS, ctx->env.ssid); + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_is_spc_ab_ready AB_SPC_STATUS @ dmem_addr %#x ssid %d - value %#x\n", + ctx->cell_dmem_addr, ctx->env.ssid, value); + + return (value == AB_SPC_READY); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_config_fw.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_config_fw.h new file mode 100644 index 000000000000..0cacd5a34934 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_config_fw.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_FW_H +#define __IA_CSS_SYSCOM_CONFIG_FW_H + +#include "type_support.h" + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_STATE_UNINIT = 0x57A7E000, + /* SP Syscom sets this when it is ready for use */ + SYSCOM_STATE_READY = 0x57A7E001, + /* SP Syscom sets this when no more syscom accesses will happen */ + SYSCOM_STATE_INACTIVE = 0x57A7E002 +}; + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_COMMAND_UNINIT = 0x57A7F000, + /* Host Syscom requests syscom to become inactive */ + SYSCOM_COMMAND_INACTIVE = 0x57A7F001 +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +enum { + /* Program load or explicit host setting should init to this */ + TRUSTLET_UNINIT = 0x57A8E000, + /* Host Syscom informs SP that Trustlet exists */ + TRUSTLET_EXIST = 0x57A8E001, + /* Host Syscom informs SP that Trustlet does not exist */ + TRUSTLET_NOT_EXIST = 0x57A8E002 +}; + +enum { + /* Program load or explicit setting initialized by SP */ + AB_SPC_NOT_READY = 0x57A8F000, + /* SP informs host that SPC access programming is completed */ + AB_SPC_READY = 0x57A8F001 +}; +#endif + +/* firmware config: data that sent from the host to SP via DDR */ +/* Cell copies data into a context */ + +struct ia_css_syscom_config_fw { + unsigned int firmware_address; + + unsigned int num_input_queues; + unsigned int num_output_queues; + unsigned int input_queue; /* hmm_ptr / struct queue* */ + unsigned int output_queue; /* hmm_ptr / struct queue* */ + + unsigned int specific_addr; /* vied virtual address */ + unsigned int specific_size; +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_FW_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_context.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_context.h new file mode 100644 index 000000000000..ecf22f6b7ac5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/src/ia_css_syscom_context.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONTEXT_H +#define __IA_CSS_SYSCOM_CONTEXT_H + +#include + +#include "port_env_struct.h" +#include + +/* host context */ +struct ia_css_syscom_context { + vied_virtual_address_t cell_firmware_addr; + unsigned int cell_regs_addr; + unsigned int cell_dmem_addr; + + struct port_env env; + + unsigned int num_input_queues; + unsigned int num_output_queues; + + /* array of input queues (from host to SP) */ + struct sys_queue *input_queue; + /* array of output queues (from SP to host) */ + struct sys_queue *output_queue; + + struct send_port *send_port; + struct recv_port *recv_port; + + unsigned int regmem_idx; + unsigned int free_buf; + + host_virtual_address_t config_host_addr; + host_virtual_address_t input_queue_host_addr; + host_virtual_address_t output_queue_host_addr; + host_virtual_address_t specific_host_addr; + host_virtual_address_t ibuf_host_addr; + host_virtual_address_t obuf_host_addr; + + vied_virtual_address_t config_vied_addr; + vied_virtual_address_t input_queue_vied_addr; + vied_virtual_address_t output_queue_vied_addr; + vied_virtual_address_t specific_vied_addr; + vied_virtual_address_t ibuf_vied_addr; + vied_virtual_address_t obuf_vied_addr; + + /* if true; secure syscom object as in VTIO Case + * if false, non-secure syscom + */ + bool secure; +}; + +#endif /* __IA_CSS_SYSCOM_CONTEXT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/syscom.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/syscom.mk new file mode 100644 index 000000000000..8d36b8928af5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/syscom/syscom.mk @@ -0,0 +1,42 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is SYSCOM + +SYSCOM_DIR=$${MODULES_DIR}/syscom + +SYSCOM_INTERFACE=$(SYSCOM_DIR)/interface +SYSCOM_SOURCES1=$(SYSCOM_DIR)/src + +SYSCOM_HOST_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom.c + +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_HOST_CPPFLAGS += -I$${MODULES_DIR}/devices +ifdef REGMEM_SECURE_OFFSET +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif + +SYSCOM_FW_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom_fw.c + +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_FW_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) +ifdef REGMEM_SECURE_OFFSET +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/interface/ia_css_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/interface/ia_css_trace.h new file mode 100644 index 000000000000..b85b1810f107 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/interface/ia_css_trace.h @@ -0,0 +1,883 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/*! \file */ + +#ifndef __IA_CSS_TRACE_H +#define __IA_CSS_TRACE_H + +/* +** Configurations +*/ + +/** + * STEP 1: Define {Module Name}_TRACE_METHOD to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * + * Example: + * #define NCI_DMA_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + */ + +/**< Use whatever method of tracing that best suits the platform + * this code is compiled for. + */ +#define IA_CSS_TRACE_METHOD_NATIVE 1 +/**< Use the Tracing NCI. */ +#define IA_CSS_TRACE_METHOD_TRACE 2 + +/** + * STEP 2: Define {Module Name}_TRACE_LEVEL_{Level} to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * {Level}, in decreasing order of severity, is one of the + * following values: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * + * Example: + * #define NCI_DMA_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + * #define NCI_DMA_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + */ +/**< Disables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_DISABLED 0 +/**< Enables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_ENABLED 1 + +/* + * Used in macro definition with do-while loop + * for removing checkpatch warnings + */ +#define IA_CSS_TRACE_FILE_DUMMY_DEFINE + +/** + * STEP 3: Define IA_CSS_TRACE_PRINT_FILE_LINE to have file name and + * line printed with every log message. + * + * Example: + * #define IA_CSS_TRACE_PRINT_FILE_LINE + */ + +/* +** Interface +*/ + +/* +** Static +*/ + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * at compile-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_0(module, severity, format) \ + IA_CSS_TRACE_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_1(module, severity, format, a1) \ + IA_CSS_TRACE_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_IMPL(module, 5, severity, format, a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_6(module, severity, format, a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_IMPL(module, 6, severity, format, a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_7(module, severity, format, a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Dynamic +*/ + +/** +* Declares, but does not define, dynamic tracing functions and variables +* for module \p module. For each module, place an instance of this macro +* in the compilation unit in which you want to use dynamic tracing facility +* so as to inform the compiler of the declaration of the available functions. +* An invocation of this function does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DEFINE +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) +/** +* Declares the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) + +/** +* Defines dynamic tracing functions and variables for module \p module. +* For each module, place an instance of this macro in one, and only one, +* of your SOURCE files so as to allow the linker resolve the related symbols. +* An invocation of this macro does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DECLARE +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) +/** +* Defines the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_DYNAMIC_0(module, severity, format) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_1(module, severity, format, a1) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 5, severity, format, \ + a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_6(module, severity, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 6, severity, format, \ + a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_7(module, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Implementation +*/ + +/* CAT */ +#define IA_CSS_TRACE_CAT_IMPL(a, b) a ## b +#define IA_CSS_TRACE_CAT(a, b) IA_CSS_TRACE_CAT_IMPL(a, b) + +/* Bridge */ +#if defined(__HIVECC) || defined(__GNUC__) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, arguments ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + ## arguments \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, \ + arguments ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + ## arguments); \ + } \ + } while (0) +#elif defined(_MSC_VER) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + __VA_ARGS__ \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + __VA_ARGS__); \ + } \ + } while (0) +#endif + +/* +** Native Backend +*/ + +#if defined(__HIVECC) + #define IA_CSS_TRACE_PLATFORM_CELL +#elif defined(__GNUC__) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, \ + format), ## arguments); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, \ + format), ## arguments); \ + } while (0) + +#elif defined(_MSC_VER) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) +#else + #error Unsupported platform! +#endif /* Platform */ + +#if defined(IA_CSS_TRACE_PLATFORM_CELL) + #include /* VOLATILE */ + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + do { \ + OP___printstring(__FILE__":") VOLATILE; \ + OP___printdec(__LINE__) VOLATILE; \ + OP___printstring("\n") VOLATILE; \ + } while (0) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + OP___printstring("["module"]:["severity"]:") \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_MSG_NATIVE(severity, module, format) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + OP___printstring("["module"]:["severity"]: "format) \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_ARG_NATIVE(module, severity, i, value) \ + do { \ + IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity); \ + OP___dump(i, value) VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 7, a7); \ + } while (0) + /* + ** Tracing Backend + */ +#if !defined(HRT_CSIM) && !defined(NO_TUNIT) + #include "vied_nci_tunit.h" +#endif + #define IA_CSS_TRACE_AUG_FORMAT_TRACE(format, module) \ + "[" module "]" format " : PID = %x : Timestamp = %d : PC = %x" + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + vied_nci_tunit_print(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + vied_nci_tunit_print1i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + vied_nci_tunit_print2i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + vied_nci_tunit_print3i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, a1, a2, a3, a4) \ + vied_nci_tunit_print4i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + vied_nci_tunit_print5i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + vied_nci_tunit_print6i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + vied_nci_tunit_print7i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6, a7) + +#elif defined(IA_CSS_TRACE_PLATFORM_HOST) + #include "print_support.h" + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + PRINT("%s:%d:\n", __FILE__, __LINE__) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, format) \ + "[" module "]:[" severity "]: " format + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) + + #define IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, format) \ + "["module"]:["severity"]: "format + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + IA_CSS_TRACE_TRACE(severity, module, format) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) +#endif + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_0(severity, module, format) +#define IA_CSS_TRACE_1_1_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_1_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_1_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_1_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_1 IA_CSS_TRACE_NATIVE_0 +#define IA_CSS_TRACE_1_1_1 IA_CSS_TRACE_NATIVE_1 +#define IA_CSS_TRACE_2_1_1 IA_CSS_TRACE_NATIVE_2 +#define IA_CSS_TRACE_3_1_1 IA_CSS_TRACE_NATIVE_3 +#define IA_CSS_TRACE_4_1_1 IA_CSS_TRACE_NATIVE_4 +#define IA_CSS_TRACE_5_1_1 IA_CSS_TRACE_NATIVE_5 +#define IA_CSS_TRACE_6_1_1 IA_CSS_TRACE_NATIVE_6 +#define IA_CSS_TRACE_7_1_1 IA_CSS_TRACE_NATIVE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_1 "Assert" +#define IA_CSS_TRACE_SEVERITY_ERROR_1 "Error" +#define IA_CSS_TRACE_SEVERITY_WARNING_1 "Warning" +#define IA_CSS_TRACE_SEVERITY_INFO_1 "Info" +#define IA_CSS_TRACE_SEVERITY_DEBUG_1 "Debug" +#define IA_CSS_TRACE_SEVERITY_VERBOSE_1 "Verbose" + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_0(severity, module, format) +#define IA_CSS_TRACE_1_2_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_2_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_2_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_2_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_1 IA_CSS_TRACE_TRACE_0 +#define IA_CSS_TRACE_1_2_1 IA_CSS_TRACE_TRACE_1 +#define IA_CSS_TRACE_2_2_1 IA_CSS_TRACE_TRACE_2 +#define IA_CSS_TRACE_3_2_1 IA_CSS_TRACE_TRACE_3 +#define IA_CSS_TRACE_4_2_1 IA_CSS_TRACE_TRACE_4 +#define IA_CSS_TRACE_5_2_1 IA_CSS_TRACE_TRACE_5 +#define IA_CSS_TRACE_6_2_1 IA_CSS_TRACE_TRACE_6 +#define IA_CSS_TRACE_7_2_1 IA_CSS_TRACE_TRACE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_2 VIED_NCI_TUNIT_MSG_SEVERITY_FATAL +#define IA_CSS_TRACE_SEVERITY_ERROR_2 VIED_NCI_TUNIT_MSG_SEVERITY_ERROR +#define IA_CSS_TRACE_SEVERITY_WARNING_2 VIED_NCI_TUNIT_MSG_SEVERITY_WARNING +#define IA_CSS_TRACE_SEVERITY_INFO_2 VIED_NCI_TUNIT_MSG_SEVERITY_NORMAL +#define IA_CSS_TRACE_SEVERITY_DEBUG_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER1 +#define IA_CSS_TRACE_SEVERITY_VERBOSE_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER2 + +/* +** Dynamicism +*/ + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) \ + do { \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void); \ + } while (0) + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + void IA_CSS_TRACE_CAT(module, _trace_configure)\ + (int argc, const char *const *argv); \ + } while (0) + +#include "platform_support.h" +#include "type_support.h" + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_assert); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_error); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_warning); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_info); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_debug); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_verbose); \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 0; \ + } + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) \ +void IA_CSS_TRACE_CAT(module, _trace_configure)(const int argc, \ + const char *const *const argv) \ +{ \ + int i = 1; \ + const char *levels = 0; \ + \ + while (i < argc) { \ + if (!strcmp(argv[i], "-" #module "_trace")) { \ + ++i; \ + \ + if (i < argc) { \ + levels = argv[i]; \ + \ + while (*levels) { \ + switch (*levels++) { \ + case 'a': \ + IA_CSS_TRACE_CAT \ + (module, _trace_assert_enable)(); \ + break; \ + \ + case 'e': \ + IA_CSS_TRACE_CAT \ + (module, _trace_error_enable)(); \ + break; \ + \ + case 'w': \ + IA_CSS_TRACE_CAT \ + (module, _trace_warning_enable)(); \ + break; \ + \ + case 'i': \ + IA_CSS_TRACE_CAT \ + (module, _trace_info_enable)(); \ + break; \ + \ + case 'd': \ + IA_CSS_TRACE_CAT \ + (module, _trace_debug_enable)(); \ + break; \ + \ + case 'v': \ + IA_CSS_TRACE_CAT \ + (module, _trace_verbose_enable)(); \ + break; \ + \ + default: \ + } \ + } \ + } \ + } \ + \ + ++i; \ + } \ +} + +#endif /* __IA_CSS_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/trace.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/trace.mk new file mode 100644 index 000000000000..b232880b882b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/trace/trace.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE Trace + +# Dependencies +IA_CSS_TRACE_SUPPORT = $${MODULES_DIR}/support + +# API +IA_CSS_TRACE = $${MODULES_DIR}/trace +IA_CSS_TRACE_INTERFACE = $(IA_CSS_TRACE)/interface + +# +# Host +# + +# Host CPP Flags +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules + +# +# Firmware +# + +# Firmware CPP Flags +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/utils/system_defs/system_const.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/utils/system_defs/system_const.h new file mode 100644 index 000000000000..161f28fced97 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/utils/system_defs/system_const.h @@ -0,0 +1,26 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SYSTEM_CONST_H +#define __SYSTEM_CONST_H + +/* The values included in this file should have been + * taken from system/device properties which + * are not currently available in SDK + */ + +#define XMEM_WIDTH (512) +#define MG_PPC (4) + +#endif /* __SYSTEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_access.h new file mode 100644 index 000000000000..1e81bad9f4ee --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_access.h @@ -0,0 +1,139 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_ACCESS_H +#define _SHARED_MEMORY_ACCESS_H + +#include +#include +#include + +typedef enum { + sm_esuccess, + sm_enomem, + sm_ezeroalloc, + sm_ebadvaddr, + sm_einternalerror, + sm_ecorruption, + sm_enocontiguousmem, + sm_enolocmem, + sm_emultiplefree, +} shared_memory_error; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the VIED subsystem + */ +typedef uint32_t vied_virtual_address_t; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the host + */ +typedef unsigned long long host_virtual_address_t; + +/** + * \brief List of physical addresses of (DDR) shared memory space. This is used to represent a list of physical pages. + */ +typedef struct shared_memory_physical_page_list_s *shared_memory_physical_page_list; +typedef struct shared_memory_physical_page_list_s +{ + shared_memory_physical_page_list next; + vied_physical_address_t address; +}shared_memory_physical_page_list_s; + + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param idm: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(vied_memory_t idm, vied_physical_address_t host_ddr_addr, size_t memory_size, size_t ps); + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(vied_memory_t idm); + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. Returns NULL when insufficient memory available + */ +host_virtual_address_t shared_memory_alloc(vied_memory_t idm, size_t bytes); + +/** + * \brief Free (DDR) shared memory space. +*/ +void shared_memory_free(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Translate a virtual host.address to a physical address. +*/ +vied_physical_address_t shared_memory_virtual_host_to_physical_address (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Return the allocated physical pages for a virtual host.address. +*/ +shared_memory_physical_page_list shared_memory_virtual_host_to_physical_pages (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Destroy a shared_memory_physical_page_list. +*/ +void shared_memory_physical_pages_list_destroy (shared_memory_physical_page_list ppl); + +/** + * \brief Store a byte into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_8 (vied_memory_t idm, host_virtual_address_t addr, uint8_t data); + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_16(vied_memory_t idm, host_virtual_address_t addr, uint16_t data); + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_32(vied_memory_t idm, host_virtual_address_t addr, uint32_t data); + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store(vied_memory_t idm, host_virtual_address_t addr, const void *data, size_t bytes); + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host virtual address + */ +void shared_memory_zero(vied_memory_t idm, host_virtual_address_t addr, size_t bytes); + +/** + * \brief Load a byte from (DDR) shared memory space using a host virtual address + */ +uint8_t shared_memory_load_8 (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host virtual address + */ +uint16_t shared_memory_load_16(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host virtual address + */ +uint32_t shared_memory_load_32(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host virtual address + */ +void shared_memory_load(vied_memory_t idm, host_virtual_address_t addr, void *data, size_t bytes); + +#endif /* _SHARED_MEMORY_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_map.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_map.h new file mode 100644 index 000000000000..1bbedcf9e7fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/shared_memory_map.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_MAP_H +#define _SHARED_MEMORY_MAP_H + +#include +#include +#include + +typedef void (*shared_memory_invalidate_mmu_tlb)(void); +typedef void (*shared_memory_set_page_table_base_address)(vied_physical_address_t); + +typedef void (*shared_memory_invalidate_mmu_tlb_ssid)(vied_subsystem_t id); +typedef void (*shared_memory_set_page_table_base_address_ssid)(vied_subsystem_t id, vied_physical_address_t); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will NOT be taken into account. +*/ +int shared_memory_map_initialize(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb inv_tlb, shared_memory_set_page_table_base_address sbt); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will be taken into account. +*/ +int shared_memory_map_initialize_ssid(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb_ssid inv_tlb, shared_memory_set_page_table_base_address_ssid sbt); + +/** + * \brief De-initialize the CSS virtual address system and MMU. +*/ +void shared_memory_map_uninitialize(vied_subsystem_t id, vied_memory_t idm); + +/** + * \brief Convert a host virtual address to a CSS virtual address and update the MMU. +*/ +vied_virtual_address_t shared_memory_map(vied_subsystem_t id, vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Free a CSS virtual address and update the MMU. +*/ +void shared_memory_unmap(vied_subsystem_t id, vied_memory_t idm, vied_virtual_address_t addr); + + +#endif /* _SHARED_MEMORY_MAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_config.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_config.h new file mode 100644 index 000000000000..912f016ead24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_config.h @@ -0,0 +1,33 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_CONFIG_H +#define _HRT_VIED_CONFIG_H + +/* Defines from the compiler: + * HRT_HOST - this is code running on the host + * HRT_CELL - this is code running on a cell + */ +#ifdef HRT_HOST +# define CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL 1 +# undef CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL + +#elif defined (HRT_CELL) +# undef CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +# define CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL 1 + +#else /* !HRT_CELL */ +/* Allow neither HRT_HOST nor HRT_CELL for testing purposes */ +#endif /* !HRT_CELL */ + +#endif /* _HRT_VIED_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_memory_access_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_memory_access_types.h new file mode 100644 index 000000000000..0b44492789e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_memory_access_types.h @@ -0,0 +1,36 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_MEMORY_ACCESS_TYPES_H +#define _HRT_VIED_MEMORY_ACCESS_TYPES_H + +/** Types for the VIED memory access interface */ + +#include "vied_types.h" + +/** + * \brief An identifier for a system memory. + * + * This identifier must be a compile-time constant. It is used in + * access to system memory. + */ +typedef unsigned int vied_memory_t; + +#ifndef __HIVECC +/** + * \brief The type for a physical address + */ +typedef unsigned long long vied_physical_address_t; +#endif + +#endif /* _HRT_VIED_MEMORY_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access.h new file mode 100644 index 000000000000..674f5fb5b0f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access.h @@ -0,0 +1,70 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_H + +#include +#include "vied_config.h" +#include "vied_subsystem_access_types.h" + +#if !defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) && \ + !defined(CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL) +#error Implementation selection macro for vied subsystem access not defined +#endif + +#if defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) +#ifndef __HIVECC +#error "Inline implementation of subsystem access not supported for host" +#endif +#define _VIED_SUBSYSTEM_ACCESS_INLINE static __inline +#include "vied_subsystem_access_impl.h" +#else +#define _VIED_SUBSYSTEM_ACCESS_INLINE +#endif + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr, uint8_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_16(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint16_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_32(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint32_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store(vied_subsystem_t dev, + vied_subsystem_address_t addr, + const void *data, unsigned int size); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint8_t vied_subsystem_load_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint16_t vied_subsystem_load_16(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint32_t vied_subsystem_load_32(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_load(vied_subsystem_t dev, + vied_subsystem_address_t addr, + void *data, unsigned int size); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_initialization.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_initialization.h new file mode 100644 index 000000000000..81f4d08d5ae0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_initialization.h @@ -0,0 +1,44 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H + +#include "vied_subsystem_access_types.h" + +/** @brief Initialises the access of a subsystem. + * @param[in] system The subsystem for which the access has to be initialised. + * + * vied_subsystem_access_initialize initilalises the access a subsystem. + * It sets the base address of the subsystem. This base address is extracted from the hsd file. + * + */ +void +vied_subsystem_access_initialize(vied_subsystem_t system); + + +/** @brief Initialises the access of multiple subsystems. + * @param[in] nr _subsystems The number of subsystems for which the access has to be initialised. + * @param[in] dev_base_addresses A pointer to an array of base addresses of subsystems. + * The size of this array must be "nr_subsystems". + * This array must be available during the accesses of the subsystem. + * + * vied_subsystems_access_initialize initilalises the access to multiple subsystems. + * It sets the base addresses of the subsystems that are provided by the array dev_base_addresses. + * + */ +void +vied_subsystems_access_initialize( unsigned int nr_subsystems + , const vied_subsystem_base_address_t *base_addresses); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_types.h new file mode 100644 index 000000000000..75fef6c4ddba --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_subsystem_access_types.h @@ -0,0 +1,34 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H + +/** Types for the VIED subsystem access interface */ +#include + +/** \brief An identifier for a VIED subsystem. + * + * This identifier must be a compile-time constant. It is used in + * access to a VIED subsystem. + */ +typedef unsigned int vied_subsystem_t; + + +/** \brief An address within a VIED subsystem */ +typedef uint32_t vied_subsystem_address_t; + +/** \brief A base address of a VIED subsystem seen from the host */ +typedef unsigned long long vied_subsystem_base_address_t; + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_types.h new file mode 100644 index 000000000000..0acfdbb00cfa --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600/vied/vied/vied_types.h @@ -0,0 +1,45 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_TYPES_H +#define _HRT_VIED_TYPES_H + +/** Types shared by VIED interfaces */ + +#include + +/** \brief An address within a VIED subsystem + * + * This will eventually replace teh vied_memory_address_t and vied_subsystem_address_t + */ +typedef uint32_t vied_address_t; + +/** \brief Memory address type + * + * A memory address is an offset within a memory. + */ +typedef uint32_t vied_memory_address_t; + +/** \brief Master port id */ +typedef int vied_master_port_id_t; + +/** + * \brief Require the existence of a certain type + * + * This macro can be used in interface header files to ensure that + * an implementation define type with a specified name exists. + */ +#define _VIED_REQUIRE_TYPE(T) enum { _VIED_SIZEOF_##T = sizeof(T) } + + +#endif /* _HRT_VIED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/Makefile b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/Makefile new file mode 100644 index 000000000000..068d3207a0d9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/Makefile @@ -0,0 +1,52 @@ +# +# Copyright (c) 2010 - 2018 Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# + +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +include $(srcpath)/$(src)/../Makefile.ipu4ppsys_src +include $(srcpath)/$(src)/../Makefile.ipu4ppsys_inc + +SSID = 0 +MMID = 0 +IPU_SYSVER = cnl + +IPU_PSYSLIB_ROOT_REL = lib +IPU_PSYSLIB_ROOT = $(srcpath)/$(src)/$(IPU_PSYSLIB_ROOT_REL) + +ccflags-y += -I$(srcpath)/$(src)/../../../ +ccflags-y += -I$(srcpath)/$(src)/../../ +ccflags-y += -DHAS_DUAL_CMD_CTX_SUPPORT=0 -DHAS_LATE_BINDING_SUPPORT=0 -DIPU_PSYS_LEGACY + +IPU_PSYSLIB_SRC += libcsspsys2600.o + +#CFLAGS = -W -Wall -Wstrict-prototypes -Wmissing-prototypes -O2 -fomit-frame-pointer -Wno-unused-variable +HOST_DEFINES += -DSSID=$(SSID) +HOST_DEFINES += -DMMID=$(MMID) +HOST_DEFINES += -DHRT_ON_VIED_SUBSYSTEM_ACCESS=$(SSID) +HOST_DEFINES += -DCFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +HOST_DEFINES += -DHRT_USE_VIR_ADDRS +HOST_DEFINES += -DHRT_HW +HOST_DEFINES += -DVIED_NCI_TUNIT_PSYS +HOST_DEFINES += -DFIRMWARE_RELEASE_VERSION +HOST_DEFINES += -DPSYS_SERVER_ON_SPC +HOST_DEFINES += -DAPI_SPLIT_START_STATE_UPDATE +HOST_DEFINES += -DHAS_DUAL_CMD_CTX_SUPPORT=0 +HOST_DEFINES += -DHAS_LATE_BINDING_SUPPORT=0 + +intel-ipu4p-psys-csslib-objs := ../../../ipu-wrapper.o \ + $(IPU_PSYSLIB_SRC) +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4p-psys-csslib.o + +ccflags-y += $(IPU_PSYSLIB_INC) $(HOST_DEFINES) -fno-common -v diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/CNL_program_group/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/CNL_program_group/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/CNL_program_group/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/ICL_program_group/ia_css_fw_pkg_release.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/ICL_program_group/ia_css_fw_pkg_release.h new file mode 100644 index 000000000000..98bea7bd0529 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/ICL_program_group/ia_css_fw_pkg_release.h @@ -0,0 +1,14 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#define IA_CSS_FW_PKG_RELEASE 0x20180927 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/buffer.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/buffer.mk new file mode 100644 index 000000000000..c00a1133b440 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/buffer.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is BUFFER + +ifdef _H_BUFFER_MK +$(error ERROR: buffer.mk included multiple times, please check makefile) +else +_H_BUFFER_MK=1 +endif + +BUFFER_DIR=$${MODULES_DIR}/buffer + +BUFFER_INTERFACE=$(BUFFER_DIR)/interface +BUFFER_SOURCES_CPU=$(BUFFER_DIR)/src/cpu +BUFFER_SOURCES_CSS=$(BUFFER_DIR)/src/css + +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_output_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_input_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/ia_css_shared_buffer.c +BUFFER_HOST_FILES += $(BUFFER_SOURCES_CPU)/buffer_access.c +BUFFER_HOST_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_HOST_CPPFLAGS += -I$${MODULES_DIR}/support + +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_input_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_output_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/ia_css_shared_buffer.c +BUFFER_FW_FILES += $(BUFFER_SOURCES_CSS)/buffer_access.c + +BUFFER_FW_CPPFLAGS += -I$(BUFFER_INTERFACE) +BUFFER_FW_CPPFLAGS += -I$${MODULES_DIR}/support diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_access.h new file mode 100644 index 000000000000..e5fe647742c9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_access.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_ACCESS_H +#define __BUFFER_ACCESS_H + +#include "buffer_type.h" +/* #def to keep consistent the buffer load interfaces for host and css */ +#define IDM 0 + +void +buffer_load( + buffer_address address, + void *data, + unsigned int size, + unsigned int mm_id); + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int size, + unsigned int mm_id); + +#endif /* __BUFFER_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_type.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_type.h new file mode 100644 index 000000000000..de51f2394158 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/buffer_type.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __BUFFER_TYPE_H +#define __BUFFER_TYPE_H + +/* portable access to buffers in DDR */ + +#ifdef __VIED_CELL +typedef unsigned int buffer_address; +#else +/* workaround needed because shared_memory_access.h uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_access.h" +typedef host_virtual_address_t buffer_address; +#endif + +#endif /* __BUFFER_TYPE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h new file mode 100644 index 000000000000..137bfb1fda16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_buffer_address.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_ADDRESS_H +#define __IA_CSS_BUFFER_ADDRESS_H + +#include "type_support.h" + +typedef uint32_t ia_css_buffer_address; /* CSS virtual address */ + +#define ia_css_buffer_address_null ((ia_css_buffer_address)0) + +#endif /* __IA_CSS_BUFFER_ADDRESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h new file mode 100644 index 000000000000..4e92e35b6184 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_H +#define __IA_CSS_INPUT_BUFFER_H + + +/* Input Buffers */ + +/* A CSS input buffer is a buffer in DDR that can be written by the CPU, + * and that can be read by CSS hardware, after the buffer has been handed over. + * Examples: command buffer, input frame buffer, parameter buffer + * An input buffer must be mapped into the CPU address space before it can be + * written by the CPU. + * After mapping, writing, and unmapping, the buffer can be handed over to the + * firmware. An input buffer is handed over to the CSS by mapping it to the + * CSS address space (by the CPU), and by passing the resulting CSS (virtial) + * address of the input buffer to the DA CSS hardware. + * The firmware can read from an input buffer as soon as it has been received + * CSS virtual address. + * The firmware should not write into an input buffer. + * The firmware hands over the input buffer (back to the CPU) by sending the + * buffer handle via a response. The host should unmap the buffer, + * before reusing it. + * The firmware should not read from the input buffer after returning the + * buffer handle to the CPU. + * + * A buffer may be pre-mapped to the CPU and/or to the CSS upon allocation, + * depending on the allocator's preference. In case of pre-mapped buffers, + * the map and unmap functions will only manage read and write access. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_input_buffer; /* input buffer handle */ +typedef void *ia_css_input_buffer_cpu_address; /* CPU virtual address */ +/* CSS virtual address */ +typedef ia_css_buffer_address ia_css_input_buffer_css_address; + +#endif /* __IA_CSS_INPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h new file mode 100644 index 000000000000..d3d01353ce43 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_input_buffer_cpu.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_INPUT_BUFFER_CPU_H +#define __IA_CSS_INPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_input_buffer.h" + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b); + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b); + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b); + + +#endif /* __IA_CSS_INPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h new file mode 100644 index 000000000000..2c310ea92c6a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer.h @@ -0,0 +1,30 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_H +#define __IA_CSS_OUTPUT_BUFFER_H + +/* Output Buffers */ +/* A CSS output buffer a buffer in DDR that can be written by CSS hardware + * and that can be read by the host, after the buffer has been handed over + * Examples: output frame buffer + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_output_buffer; +typedef void *ia_css_output_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_output_buffer_css_address; + +#endif /* __IA_CSS_OUTPUT_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h new file mode 100644 index 000000000000..0299fc3b7eb6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_output_buffer_cpu.h @@ -0,0 +1,48 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_OUTPUT_BUFFER_CPU_H +#define __IA_CSS_OUTPUT_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_output_buffer.h" + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b); + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b); +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b); + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b); + + +#endif /* __IA_CSS_OUTPUT_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h new file mode 100644 index 000000000000..558ec679f98a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_H +#define __IA_CSS_SHARED_BUFFER_H + +/* Shared Buffers */ +/* A CSS shared buffer is a buffer in DDR that can be read and written by the + * CPU and CSS. + * Both the CPU and CSS can have the buffer mapped simultaneously. + * Access rights are not managed by this interface, this could be done by means + * the read and write pointer of a queue, for example. + */ + +#include "ia_css_buffer_address.h" + +typedef struct ia_css_buffer_s *ia_css_shared_buffer; +typedef void *ia_css_shared_buffer_cpu_address; +typedef ia_css_buffer_address ia_css_shared_buffer_css_address; + +#endif /* __IA_CSS_SHARED_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h new file mode 100644 index 000000000000..ff62914f99dc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/interface/ia_css_shared_buffer_cpu.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SHARED_BUFFER_CPU_H +#define __IA_CSS_SHARED_BUFFER_CPU_H + +#include "vied/shared_memory_map.h" +#include "ia_css_shared_buffer.h" + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b); + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b); + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b); + +#endif /* __IA_CSS_SHARED_BUFFER_CPU_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c new file mode 100644 index 000000000000..f0c617fe501a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/buffer_access.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* implementation of buffer access from the CPU */ +/* using shared_memory interface */ + +#include "buffer_access.h" +#include "vied/shared_memory_access.h" + +void +buffer_load( + buffer_address address, + void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_load(mm_id, address, data, bytes); +} + +void +buffer_store( + buffer_address address, + const void *data, + unsigned int bytes, + unsigned int mm_id) +{ + shared_memory_store(mm_id, address, data, bytes); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c new file mode 100644 index 000000000000..146d4109de44 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.c @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* provided interface */ +#include "ia_css_buffer.h" + +/* used interfaces */ +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + +ia_css_buffer_t +ia_css_buffer_alloc(vied_subsystem_t sid, vied_memory_t mid, unsigned int size) +{ + ia_css_buffer_t b; + + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + + b->css_address = shared_memory_map(sid, mid, b->mem); + b->size = size; + return b; +} + + +void +ia_css_buffer_free(vied_subsystem_t sid, vied_memory_t mid, ia_css_buffer_t b) +{ + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h new file mode 100644 index 000000000000..0f99a06e9a89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_buffer.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BUFFER_H +#define __IA_CSS_BUFFER_H + +/* workaround: needed because uses size_t */ +#include "type_support.h" +#include "vied/shared_memory_map.h" + +typedef enum { + buffer_unmapped, /* buffer is not accessible by cpu, nor css */ + buffer_write, /* output buffer: css has write access */ + /* input buffer: cpu has write access */ + buffer_read, /* input buffer: css has read access */ + /* output buffer: cpu has read access */ + buffer_cpu, /* shared buffer: cpu has read/write access */ + buffer_css /* shared buffer: css has read/write access */ +} buffer_state; + +struct ia_css_buffer_s { + /* number of bytes bytes allocated */ + unsigned int size; + /* allocated virtual memory object */ + host_virtual_address_t mem; + /* virtual address to be used on css/firmware */ + vied_virtual_address_t css_address; + /* virtual address to be used on cpu/host */ + void *cpu_address; + buffer_state state; +}; + +typedef struct ia_css_buffer_s *ia_css_buffer_t; + +ia_css_buffer_t +ia_css_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size); + +void +ia_css_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_buffer_t b); + +#endif /* __IA_CSS_BUFFER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c new file mode 100644 index 000000000000..2a128795d03e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_input_buffer.c @@ -0,0 +1,184 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_input_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_input_buffer +ia_css_input_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_input_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_input_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_input_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_map(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map input buffer to CPU address space, acquire write access */ + b->state = buffer_write; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_cpu_address +ia_css_input_buffer_cpu_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_write) + return NULL; + + /* unmap input buffer from CPU address space, release write access */ + b->state = buffer_unmapped; + + /* return pre-mapped buffer */ + return b->cpu_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + + /* now flush the cache */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_map_no_invalidate(vied_memory_t mid, ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map input buffer to CSS address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only copy in case of simulation, otherwise it should just work */ + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return (ia_css_input_buffer_css_address)b->css_address; +} + + +ia_css_input_buffer_css_address +ia_css_input_buffer_css_unmap(ia_css_input_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_read) + return 0; + + /* unmap input buffer from CSS address space, release read access */ + b->state = buffer_unmapped; + + /* input buffer only, no need to invalidate cache */ + + return (ia_css_input_buffer_css_address)b->css_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c new file mode 100644 index 000000000000..30bc8d52a5a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_output_buffer.c @@ -0,0 +1,181 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_output_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_output_buffer +ia_css_output_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_output_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_output_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_output_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_map(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map output buffer to CSS address space, acquire write access */ + b->state = buffer_write; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_css_address +ia_css_output_buffer_css_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_write) + return 0; + + /* unmap output buffer from CSS address space, release write access */ + b->state = buffer_unmapped; + + return (ia_css_output_buffer_css_address)b->css_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* now invalidate the cache */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b->cpu_address; +} + + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_map_no_invalidate(vied_memory_t mid, ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map output buffer to CPU address space, acquire read access */ + b->state = buffer_read; + +#ifndef HRT_HW + /* only in simulation */ + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b->cpu_address; +} + +ia_css_output_buffer_cpu_address +ia_css_output_buffer_cpu_unmap(ia_css_output_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_read) + return NULL; + + /* unmap output buffer from CPU address space, release read access */ + b->state = buffer_unmapped; + + /* output only, no need to flush cache */ + + return b->cpu_address; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c new file mode 100644 index 000000000000..92b7110644fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/buffer/src/cpu/ia_css_shared_buffer.c @@ -0,0 +1,187 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_shared_buffer_cpu.h" +#include "ia_css_buffer.h" +#include "vied/shared_memory_access.h" +#include "vied/shared_memory_map.h" +#include "cpu_mem_support.h" + + +ia_css_shared_buffer +ia_css_shared_buffer_alloc( + vied_subsystem_t sid, + vied_memory_t mid, + unsigned int size) +{ + ia_css_shared_buffer b; + + /* allocate buffer container */ + b = ia_css_cpu_mem_alloc(sizeof(*b)); + if (b == NULL) + return NULL; + + b->mem = shared_memory_alloc(mid, size); + if (b->mem == 0) { + ia_css_cpu_mem_free(b); + return NULL; + } + +#ifndef HRT_HW + /* initialize the buffer to avoid warnings when copying */ + shared_memory_zero(mid, b->mem, size); + + /* in simulation, we need to allocate a shadow host buffer */ + b->cpu_address = ia_css_cpu_mem_alloc_page_aligned(size); + if (b->cpu_address == NULL) { + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); + return NULL; + } +#else + /* on hw / real platform we can use the pointer from + * shared memory alloc + */ + b->cpu_address = (void *)HOST_ADDRESS(b->mem); +#endif + + b->css_address = shared_memory_map(sid, mid, b->mem); + + b->size = size; + b->state = buffer_unmapped; + + return b; +} + + +void +ia_css_shared_buffer_free( + vied_subsystem_t sid, + vied_memory_t mid, + ia_css_shared_buffer b) +{ + if (b == NULL) + return; + if (b->state != buffer_unmapped) + return; + +#ifndef HRT_HW + /* only free if we actually allocated it separately */ + ia_css_cpu_mem_free(b->cpu_address); +#endif + shared_memory_unmap(sid, mid, b->css_address); + shared_memory_free(mid, b->mem); + ia_css_cpu_mem_free(b); +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_unmapped) + return NULL; + + /* map shared buffer to CPU address space */ + b->state = buffer_cpu; + + return b->cpu_address; +} + + +ia_css_shared_buffer_cpu_address +ia_css_shared_buffer_cpu_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + if (b->state != buffer_cpu) + return NULL; + + /* unmap shared buffer from CPU address space */ + b->state = buffer_unmapped; + + return b->cpu_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_map(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_unmapped) + return 0; + + /* map shared buffer to CSS address space */ + b->state = buffer_css; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer_css_address +ia_css_shared_buffer_css_unmap(ia_css_shared_buffer b) +{ + if (b == NULL) + return 0; + if (b->state != buffer_css) + return 0; + + /* unmap shared buffer from CSS address space */ + b->state = buffer_unmapped; + + return (ia_css_shared_buffer_css_address)b->css_address; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_css_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to CSS after it was modified by the CPU */ + /* flush cache to ddr */ + ia_css_cpu_mem_cache_flush(b->cpu_address, b->size); +#ifndef HRT_HW + /* copy data from CPU address space to CSS address space */ + shared_memory_store(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + + return b; +} + + +ia_css_shared_buffer +ia_css_shared_buffer_cpu_update(vied_memory_t mid, ia_css_shared_buffer b) +{ + if (b == NULL) + return NULL; + + /* flush the buffer to the CPU after it has been modified by CSS */ +#ifndef HRT_HW + /* copy data from CSS address space to CPU address space */ + shared_memory_load(mid, b->mem, b->cpu_address, b->size); +#else + (void)mid; +#endif + /* flush cache to ddr */ + ia_css_cpu_mem_cache_invalidate(b->cpu_address, b->size); + + return b; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/cell.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/cell.mk new file mode 100644 index 000000000000..fa5e65022601 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/cell.mk @@ -0,0 +1,43 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef _CELL_MK_ +_CELL_MK_ = 1 + + +CELL_DIR=$${MODULES_DIR}/cell +CELL_INTERFACE=$(CELL_DIR)/interface +CELL_SOURCES=$(CELL_DIR)/src + +CELL_HOST_FILES = +CELL_FW_FILES = + +CELL_HOST_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +CELL_FW_CPPFLAGS = \ + -I$(CELL_INTERFACE) \ + -I$(CELL_SOURCES) + +ifdef 0 +# Disabled until it is decided to go this way or not +include $(MODULES_DIR)/device_access/device_access.mk +CELL_HOST_FILES += $(DEVICE_ACCESS_HOST_FILES) +CELL_FW_FILES += $(DEVICE_ACCESS_FW_FILES) +CELL_HOST_CPPFLAGS += $(DEVICE_ACCESS_HOST_CPPFLAGS) +CELL_FW_CPPFLAGS += $(DEVICE_ACCESS_FW_CPPFLAGS) +endif + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/interface/ia_css_cell.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/interface/ia_css_cell.h new file mode 100644 index 000000000000..3fac3c791b6e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/interface/ia_css_cell.h @@ -0,0 +1,112 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_H +#define __IA_CSS_CELL_H + +#include "storage_class.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +#if 0 /* To be implemented after completing cell device properties */ +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_debug_pc(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stall_bits(unsigned int ssid, unsigned int cell_id); +#endif + +/* configure master ports */ + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell_id, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, + unsigned int cell_id, + unsigned int master, unsigned int segment, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value); + +/* Access memories */ + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value); + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr); + +/***********************************************************************/ + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value); + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id); + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch); + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id); + +/* include inline implementation */ +#include "ia_css_cell_impl.h" + +#endif /* __IA_CSS_CELL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h new file mode 100644 index 000000000000..60b2e234da1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell/src/ia_css_cell_impl.h @@ -0,0 +1,272 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_IMPL_H +#define __IA_CSS_CELL_IMPL_H + +#include "ia_css_cell.h" + +#include "ia_css_cmem.h" +#include "ipu_device_cell_properties.h" +#include "storage_class.h" +#include "assert_support.h" +#include "platform_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_regs_addr(unsigned int cell_id) +{ + /* mem_id 0 is for registers */ + return ipu_device_cell_memory_address(cell_id, 0); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_dmem_addr(unsigned int cell_id) +{ + /* mem_id 1 is for DMEM */ + return ipu_device_cell_memory_address(cell_id, 1); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_mem_store_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr, unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ipu_device_cell_memory_address( + cell_id, mem_id) + addr, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_mem_load_32(unsigned int ssid, unsigned int cell_id, + unsigned int mem_id, unsigned int addr) +{ + return ia_css_cmem_load_32( + ssid, ipu_device_cell_memory_address(cell_id, mem_id) + addr); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_get_stat_ctrl(unsigned int ssid, unsigned int cell_id) +{ + return ia_css_cmem_load_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_stat_ctrl(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_is_ready(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = ia_css_cell_get_stat_ctrl(ssid, cell_id); + /* READY must be 1, START must be 0 */ + return (reg & (1 << IPU_DEVICE_CELL_STAT_CTRL_READY_BIT)) && + ((~reg) & (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT)); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_pc(unsigned int ssid, unsigned int cell_id, + unsigned int pc) +{ + /* set start PC */ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_START_PC_REG_ADDRESS, pc); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_start_bit(unsigned int ssid, unsigned int cell_id) +{ + unsigned int reg; + + reg = 1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_run_bit(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + unsigned int reg; + + reg = value << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT; + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start(unsigned int ssid, unsigned int cell_id) +{ + ia_css_cell_start_prefetch(ssid, cell_id, 0); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_start_prefetch(unsigned int ssid, unsigned int cell_id, + bool prefetch) +{ + unsigned int reg = 0; + + /* Set run bit and start bit */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_START_BIT); + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT); + /* Invalidate the icache */ + reg |= (1 << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); + /* Optionally enable prefetching */ + reg |= ((prefetch == 1) ? + (1 << IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT) : + 0); + + /* store into register */ + ia_css_cell_set_stat_ctrl(ssid, cell_id, reg); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_wait(unsigned int ssid, unsigned int cell_id) +{ + do { + ia_css_sleep(); + } while (!ia_css_cell_is_ready(ssid, cell_id)); +}; + +STORAGE_CLASS_INLINE void +ia_css_cell_set_icache_base_address(unsigned int ssid, unsigned int cell_id, + unsigned int value) +{ + ia_css_cmem_store_32( + ssid, ia_css_cell_regs_addr(cell_id) + + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS, value); +} + +/* master port configuration */ + + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_info_override_bits(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_segment_base_address(unsigned int ssid, + unsigned int cell, + unsigned int master, unsigned int segment, unsigned int value) + +{ + unsigned int addr; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + assert(segment < ipu_device_cell_master_num_segments(cell, master)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + addr += segment * ipu_device_cell_master_stride(cell, master); + ia_css_cmem_store_32(ssid, addr, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_info_override_bits(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_info_override_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cell_set_master_base_address(unsigned int ssid, unsigned int cell, + unsigned int master, unsigned int value) +{ + unsigned int addr, s, stride, num_segments, segment_size; + + assert(cell < ipu_device_cell_num_devices()); + assert(master < ipu_device_cell_num_masters(cell)); + + addr = ipu_device_cell_memory_address(cell, 0); + addr += ipu_device_cell_master_base_reg(cell, master); + stride = ipu_device_cell_master_stride(cell, master); + num_segments = ipu_device_cell_master_num_segments(cell, master); + segment_size = ipu_device_cell_master_segment_size(cell, master); + + for (s = 0; s < num_segments; s++) { + ia_css_cmem_store_32(ssid, addr, value); + addr += stride; + value += segment_size; + } +} + +#endif /* __IA_CSS_CELL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/cell_program_load.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/cell_program_load.mk new file mode 100644 index 000000000000..ec5389aff4a0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/cell_program_load.mk @@ -0,0 +1,39 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +ifndef _CELL_PROGRAM_LOAD_MK_ +_CELL_PROGRAM_LOAD_MK_ = 1 + +CELL_PROGRAM_LOAD_DIR=$${MODULES_DIR}/cell_program_load +CELL_PROGRAM_LOAD_INTERFACE=$(CELL_PROGRAM_LOAD_DIR)/interface +CELL_PROGRAM_LOAD_SOURCES=$(CELL_PROGRAM_LOAD_DIR)/src + +CELL_PROGRAM_LOAD_HOST_FILES = $(CELL_PROGRAM_LOAD_SOURCES)/ia_css_cell_program_load.c + +CELL_PROGRAM_LOAD_FW_FILES = $(CELL_PROGRAM_LOAD_SOURCES)/ia_css_cell_program_load.c + +CELL_PROGRAM_LOAD_HOST_CPPFLAGS = \ + -I$(CELL_PROGRAM_LOAD_INTERFACE) \ + -I$(CELL_PROGRAM_LOAD_SOURCES) + +CELL_PROGRAM_LOAD_FW_CPPFLAGS = \ + -I$(CELL_PROGRAM_LOAD_INTERFACE) \ + -I$(CELL_PROGRAM_LOAD_SOURCES) + +ifeq ($(CRUN_DYNAMIC_LINK_PROGRAMS), 1) +CELL_PROGRAM_LOAD_HOST_CPPFLAGS += -DCRUN_DYNAMIC_LINK_PROGRAMS=1 +CELL_PROGRAM_LOAD_FW_CPPFLAGS += -DCRUN_DYNAMIC_LINK_PROGRAMS=1 +endif + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_group_load.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_group_load.h new file mode 100644 index 000000000000..812dd4ea09a8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_group_load.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_GROUP_LOAD_H +#define __IA_CSS_CELL_PROGRAM_GROUP_LOAD_H + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_xmem.h" +#include "ia_css_cell_program_struct.h" + +/* Load all programs in program group + * Return 0 on success, -1 on incorrect magic number, + * -2 on incorrect release tag + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_group_load( + unsigned int ssid, + unsigned int mmid, + /* program address as seen from caller */ + ia_css_xmem_address_t program_addr, + /* program address as seen from cell's icache */ + unsigned int program_addr_icache +); + +/* Load all programs in program group + * each group may have multiple entry function. This function will return + * the info of each entry function to allow user start any of them + * Return 0 on success, -1 on incorrect magic number, + * -2 on incorrect release tag + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_group_load_multi_entry( + unsigned int ssid, + unsigned int mmid, + /* program address as seen from caller */ + ia_css_xmem_address_t program_addr, + /* program address as seen from cell's icache */ + unsigned int program_addr_icache, + struct ia_css_cell_program_entry_func_info_s *entry_info, + unsigned int num_entry_info +); + +/* Load all programs in program group, except icache of first program + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_group_load_mem( + unsigned int ssid, + unsigned int mmid, + /* program address as seen from caller */ + ia_css_xmem_address_t program_addr, + /* program address as seen from cell's icache */ + unsigned int program_addr_icache +); + +#ifdef __INLINE_IA_CSS_CELL_PROGRAM_LOAD__ +#include "ia_css_cell_program_group_load_impl.h" +#endif + +#endif /* __IA_CSS_CELL_PROGRAM_GROUP_LOAD_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load.h new file mode 100644 index 000000000000..d7e689e9d569 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load.h @@ -0,0 +1,114 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_H +#define __IA_CSS_CELL_PROGRAM_LOAD_H + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_cell_program_struct.h" +#include "ia_css_xmem.h" + +/* Perform full program load: + * - load program header + * - initialize icache and start PC of exec entry function + * - initialize PMEM and DMEM + * Return 0 on success, -1 on incorrect magic number, + * -2 on incorrect release tag + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load( + unsigned int ssid, + unsigned int mmid, + /* program address as seen from caller */ + ia_css_xmem_address_t program_addr, + /* program address as seen from cell's icache */ + unsigned int program_addr_icache +); + +/* Perform full program load: + * - load program header + * - initialize icache and start PC of exec entry function + * - initialize info of all entry function + * - initialize PMEM and DMEM + * Return 0 on success, -1 on incorrect magic number, + * -2 on incorrect release tag + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_multi_entry( + unsigned int ssid, + unsigned int mmid, + /* program address as seen from caller */ + ia_css_xmem_address_t program_addr, + /* program address as seen from cell's icache */ + unsigned int program_addr_icache, + struct ia_css_cell_program_entry_func_info_s *entry_info +); + +/* Load program header, and initialize icache and start PC. + * After this, the cell may be started, but the entry function may not yet use + * global data, nor may code from PMEM be executed. + * Before accessing global data or executing code from PMEM + * the function ia_css_cell_load_program_mem must be executed. + */ + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_icache( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t program_addr, + unsigned int program_addr_icache); + +/* Load program header and finish the program load by + * initializing PMEM and DMEM. + * After this any code from the program may be be executed on the cell. + */ +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_mem( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t program_addr, + unsigned int program_addr_icache); + +/* set cell start PC to program init entry function */ +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +void +ia_css_cell_program_load_set_init_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info); + +/* set cell start PC to program exec entry function */ +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +void +ia_css_cell_program_load_set_exec_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info); + +/* set cell start PC to program done entry function */ +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +void +ia_css_cell_program_load_set_done_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info); + +#ifdef __INLINE_IA_CSS_CELL_PROGRAM_LOAD__ +#include "ia_css_cell_program_load_impl.h" +#endif + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_prog.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_prog.h new file mode 100644 index 000000000000..0f8f1852449c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_prog.h @@ -0,0 +1,84 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_PROG_H +#define __IA_CSS_CELL_PROGRAM_LOAD_PROG_H + +/* basic functions needed to implement all program(group) loads */ + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_cell_program_struct.h" +#include "ia_css_xmem.h" + + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +void +ia_css_cell_program_load_encode_entry_info( + struct ia_css_cell_program_entry_func_info_s *entry_info, + const struct ia_css_cell_program_s *prog); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +void +ia_css_cell_program_load_set_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info, + enum ia_css_cell_program_entry_func_id func_id); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_header( + unsigned int mmid, + ia_css_xmem_address_t host_addr, + struct ia_css_cell_program_s *prog); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_icache_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + const struct ia_css_cell_program_s *prog); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_entry_prog( + unsigned int ssid, + unsigned int mmid, + enum ia_css_cell_program_entry_func_id entry_func_id, + const struct ia_css_cell_program_s *prog); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_mem_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + const struct ia_css_cell_program_s *prog); + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +int +ia_css_cell_program_load_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + struct ia_css_cell_program_s *prog); + +#ifdef __INLINE_IA_CSS_CELL_PROGRAM_LOAD__ +#include "ia_css_cell_program_load_prog_impl.h" +#endif + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_PROG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_storage_class.h new file mode 100644 index 000000000000..8691e1402eaf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_load_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H +#define __IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifdef __INLINE_IA_CSS_CELL_PROGRAM_LOAD__ +#define IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#else +#define IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +#endif + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_struct.h new file mode 100644 index 000000000000..de3c3682ff8d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/interface/ia_css_cell_program_struct.h @@ -0,0 +1,114 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_STRUCT_H +#define __IA_CSS_CELL_PROGRAM_STRUCT_H + +#define IA_CSS_CELL_ID_UNDEFINED 0xFFFFFFFF +#define IA_CSS_CELL_PROGRAM_MAGIC_NUMBER 0xF1A30002 + +#define CSIM_PROGRAM_NAME_SIZE 64 + +enum ia_css_cell_program_entry_func_id { + IA_CSS_CELL_PROGRAM_INIT_FUNC_ID, + IA_CSS_CELL_PROGRAM_EXEC_FUNC_ID, + IA_CSS_CELL_PROGRAM_DONE_FUNC_ID, + IA_CSS_CELL_PROGRAM_NUM_FUNC_ID, +}; + +struct ia_css_cell_program_entry_func_info_s { + /* start PC value of program entry functions */ + unsigned int start[IA_CSS_CELL_PROGRAM_NUM_FUNC_ID]; + +#if defined(C_RUN) + /* entry function names */ + char func_name[IA_CSS_CELL_PROGRAM_NUM_FUNC_ID][CSIM_PROGRAM_NAME_SIZE]; + /* for crun use only */ + unsigned int cell_id; +#endif + /* base address for cell's registers */ + unsigned int regs_addr; + +}; + +struct ia_css_cell_program_s { + /* must be equal to IA_CSS_CELL_PROGRAM_MAGIC_NUMBER */ + unsigned int magic_number; + + /* offset of blob relative to start of this struct */ + unsigned int blob_offset; + /* size of the blob, not used */ + unsigned int blob_size; + + /* start PC value of program entry functions */ + unsigned int start[IA_CSS_CELL_PROGRAM_NUM_FUNC_ID]; + +#if defined(C_RUN) || defined(HRT_UNSCHED) || defined(HRT_SCHED) + /* program name */ + char prog_name[CSIM_PROGRAM_NAME_SIZE]; +#if defined(C_RUN) + /* entry function names */ + char func_name[IA_CSS_CELL_PROGRAM_NUM_FUNC_ID][CSIM_PROGRAM_NAME_SIZE]; +#endif +#endif + + /* offset of icache section in blob */ + unsigned int icache_source; + /* offset in the instruction space, not used */ + unsigned int icache_target; + /* icache section size, not used */ + unsigned int icache_size; + + /* offset of pmem section in blob */ + unsigned int pmem_source; + /* offset in the pmem, typically 0 */ + unsigned int pmem_target; + /* pmem section size, 0 if not used */ + unsigned int pmem_size; + + /* offset of data section in blob */ + unsigned int data_source; + /* offset of data section in dmem */ + unsigned int data_target; + /* size of dmem data section */ + unsigned int data_size; + + /* offset of bss section in dmem, to be zeroed */ + unsigned int bss_target; + /* size of bss section in dmem */ + unsigned int bss_size; + + /* for checking */ + unsigned int cell_id; + /* base address for cell's registers */ + unsigned int regs_addr; + + /* pmem data bus address */ + unsigned int cell_pmem_data_bus_addres; + /* dmem data bus address */ + unsigned int cell_dmem_data_bus_addres; + /* pmem config bus address */ + unsigned int cell_pmem_control_bus_addres; + /* dmem config bus address */ + unsigned int cell_dmem_control_bus_addres; + + /* offset to header of next program */ + unsigned int next; + /* Temporary workaround for a dma bug where it fails to trasfer + * data with size which is not multiple of 64 bytes + */ + unsigned int dummy[2]; +}; + +#endif /* __IA_CSS_CELL_PROGRAM_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_group_load_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_group_load_impl.h new file mode 100644 index 000000000000..20d71bb25d49 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_group_load_impl.h @@ -0,0 +1,128 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_GROUP_LOAD_IMPL_H +#define __IA_CSS_CELL_PROGRAM_GROUP_LOAD_IMPL_H + +#include "ia_css_cell_program_group_load.h" + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_cell_program_load_prog.h" +#include "ia_css_cell_program_struct.h" + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_group_load( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr) +{ + struct ia_css_cell_program_s prog; + unsigned int next; + int status = 0; + + do { + status = ia_css_cell_program_load_prog( + ssid, mmid, host_addr, vied_addr, &prog); + if (status) + return status; + + next = prog.next; + host_addr = + (ia_css_xmem_address_t)((unsigned long long)host_addr + next); + vied_addr += next; + } while (next); + + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_group_load_multi_entry( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + struct ia_css_cell_program_entry_func_info_s *entry_info, + unsigned int num_entry_info) +{ + struct ia_css_cell_program_s prog; + unsigned int next; + int status = 0; + unsigned int i = 0; + + do { + status = ia_css_cell_program_load_prog( + ssid, mmid, host_addr, vied_addr, &prog); + if (status) + return status; + if (i >= num_entry_info) { + /* more program than entry info, + * cause access out of bound. + */ + return 1; + } + ia_css_cell_program_load_encode_entry_info( + &entry_info[i], &prog); + + next = prog.next; + host_addr = + (ia_css_xmem_address_t)((unsigned long long)host_addr + next); + vied_addr += next; + i++; + } while (next); + + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_group_load_mem( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr) +{ + struct ia_css_cell_program_s prog; + unsigned int next; + int status = 0; + + status = ia_css_cell_program_load_header(mmid, host_addr, &prog); + if (status) + return status; + + /* load memories of first program */ + status = ia_css_cell_program_load_mem_prog( + ssid, mmid, host_addr, vied_addr, &prog); + if (status) + return status; + + /* return next from ia_css_cell_program_load_mem_prog? */ + next = prog.next; + + /* load next programs, if any */ + if (next) { + host_addr = + (ia_css_xmem_address_t)((unsigned long long)host_addr + next); + status = ia_css_cell_program_group_load( + ssid, mmid, host_addr, vied_addr + next); + if (status) + return status; + } + + return status; +} + +#endif /* __IA_CSS_CELL_PROGRAM_GROUP_LOAD_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load.c new file mode 100644 index 000000000000..0a1ea1ac2ed1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load.c @@ -0,0 +1,31 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_IA_CSS_CELL_PROGRAM_LOAD__ + +#include "storage_class.h" +STORAGE_CLASS_INLINE void __dummy(void) { } + +#else + +/* low-level functions */ +#include "ia_css_cell_program_load_prog_impl.h" + +/* functions for single, unmapped program load */ +#include "ia_css_cell_program_load_impl.h" + +/* functions for program group load */ +#include "ia_css_cell_program_group_load_impl.h" + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_bin.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_bin.h new file mode 100644 index 000000000000..523ce536cb09 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_bin.h @@ -0,0 +1,193 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_BIN_H +#define __IA_CSS_CELL_PROGRAM_LOAD_BIN_H + +#include "ia_css_cell_program_load_prog.h" + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_cell_program_struct.h" +#include "ia_css_cell_regs.h" +#include "misc_support.h" +#include "ia_css_fw_load.h" +#include "platform_support.h" +#include "ipu_device_buttress_properties_struct.h" + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +void +ia_css_cell_program_load_encode_entry_info( + struct ia_css_cell_program_entry_func_info_s *entry_info, + const struct ia_css_cell_program_s *prog) +{ + unsigned int i; + + for (i = 0; i < IA_CSS_CELL_PROGRAM_NUM_FUNC_ID; i++) + entry_info->start[i] = prog->start[i]; + + entry_info->regs_addr = prog->regs_addr; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +void +ia_css_cell_program_load_set_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info, + enum ia_css_cell_program_entry_func_id func_id) +{ + unsigned int start_pc; + + start_pc = entry_info->start[func_id]; + /* set start address */ + ia_css_cell_regs_set_start_pc(ssid, entry_info->regs_addr, start_pc); +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_load_icache_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + const struct ia_css_cell_program_s *prog) +{ + unsigned int regs_addr; + struct ia_css_cell_program_entry_func_info_s entry_info; + + NOT_USED(mmid); + NOT_USED(host_addr); + + if (prog->cell_id == IA_CSS_CELL_ID_UNDEFINED) + return -1; + + regs_addr = prog->regs_addr; + + /* set icache base address */ + ia_css_cell_regs_set_icache_base_address(ssid, regs_addr, + vied_addr + prog->blob_offset + prog->icache_source); + + /* set icache info bits */ + ia_css_cell_regs_set_icache_info_bits( + ssid, regs_addr, IA_CSS_INFO_BITS_M0_DDR); + + /* by default we set to start PC of exec entry function */ + ia_css_cell_program_load_encode_entry_info(&entry_info, prog); + ia_css_cell_program_load_set_start_pc( + ssid, &entry_info, IA_CSS_CELL_PROGRAM_EXEC_FUNC_ID); + + return 0; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_load_entry_prog( + unsigned int ssid, + unsigned int mmid, + enum ia_css_cell_program_entry_func_id entry_func_id, + const struct ia_css_cell_program_s *prog) +{ + struct ia_css_cell_program_entry_func_info_s entry_info; + + NOT_USED(mmid); + + if (prog->cell_id == IA_CSS_CELL_ID_UNDEFINED) + return -1; + + ia_css_cell_program_load_encode_entry_info(&entry_info, prog); + ia_css_cell_program_load_set_start_pc(ssid, &entry_info, entry_func_id); + + return 0; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C int +ia_css_cell_program_load_mem_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + const struct ia_css_cell_program_s *prog) +{ + unsigned int transferred = 0; + unsigned int pending = 0; + unsigned int dmem_addr; + unsigned int pmem_addr; + + NOT_USED(vied_addr); + +#ifdef ENABLE_FW_LOAD_DMA + pmem_addr = prog->cell_pmem_data_bus_addres; + dmem_addr = prog->cell_dmem_data_bus_addres; +#else + pmem_addr = prog->cell_pmem_control_bus_addres; + dmem_addr = prog->cell_dmem_control_bus_addres; +#endif + + /* Copy text section from ddr to pmem. */ + if (prog->pmem_size) { + transferred = ia_css_fw_copy_begin(mmid, + ssid, + host_addr + prog->blob_offset + + prog->pmem_source, + pmem_addr + prog->pmem_target, + prog->pmem_size); + + assert(prog->pmem_size == transferred); + /* If less bytes are transferred that requested, signal error, + * This architecture enforces DMA xfer size > pmem_size. + * So, a DMA transfer request should be xferable*/ + if (transferred != prog->pmem_size) + return 1; + pending++; + } + + /* Copy data section from ddr to dmem. */ + if (prog->data_size) { + transferred = ia_css_fw_copy_begin(mmid, + ssid, + host_addr + prog->blob_offset + + prog->data_source, + dmem_addr + prog->data_target, + prog->data_size); + assert(prog->data_size == transferred); + /* If less bytes are transferred that requested, signal error, + * This architecture enforces DMA xfer size > data_size. + * So, a DMA transfer request should be xferable*/ + if (transferred != prog->data_size) + return 1; /*FALSE*/ + pending++; + } + + /* Zero bss section in dmem.*/ + if (prog->bss_size) { + transferred = ia_css_fw_zero_begin(ssid, + dmem_addr + prog->bss_target, + prog->bss_size); + assert(prog->bss_size == transferred); + /* If less bytes are transferred that requested, signal error, + * This architecture enforces DMA xfer size > bss_size. + * So, a DMA transfer request should be xferable*/ + if (transferred != prog->bss_size) + return 1; + pending++; + } + + /* Wait for all fw load to complete */ + while (pending) { + pending -= ia_css_fw_end(pending); + ia_css_sleep(); + } + return 0; /*Success*/ +} + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_BIN_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_impl.h new file mode 100644 index 000000000000..6201fd583482 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_impl.h @@ -0,0 +1,134 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_IMPL_H +#define __IA_CSS_CELL_PROGRAM_LOAD_IMPL_H + +#include "ia_css_cell_program_load.h" + +#include "ia_css_cell_program_load_storage_class.h" +#include "ia_css_cell_program_load_prog.h" +#include "ia_css_cell_program_struct.h" + + + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C int +ia_css_cell_program_load( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr) +{ + struct ia_css_cell_program_s prog; + int status; + + status = ia_css_cell_program_load_prog( + ssid, mmid, host_addr, vied_addr, &prog); + + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C int +ia_css_cell_program_load_multi_entry( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + struct ia_css_cell_program_entry_func_info_s *entry_info) +{ + struct ia_css_cell_program_s prog; + int status; + + status = ia_css_cell_program_load_prog( + ssid, mmid, host_addr, vied_addr, &prog); + if (status) + return status; + + ia_css_cell_program_load_encode_entry_info(entry_info, &prog); + + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C int +ia_css_cell_program_load_icache( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr) +{ + struct ia_css_cell_program_s prog; + int status; + + status = ia_css_cell_program_load_header(mmid, host_addr, &prog); + if (status) + return status; + + status = ia_css_cell_program_load_icache_prog( + ssid, mmid, host_addr, vied_addr, &prog); + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C int +ia_css_cell_program_load_mem( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr) +{ + struct ia_css_cell_program_s prog; + int status; + + status = ia_css_cell_program_load_header(mmid, host_addr, &prog); + if (status) + return status; + + status = ia_css_cell_program_load_mem_prog( + ssid, mmid, host_addr, vied_addr, &prog); + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C void +ia_css_cell_program_load_set_init_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info) +{ + assert(entry_info != NULL); + + ia_css_cell_program_load_set_start_pc(ssid, entry_info, + IA_CSS_CELL_PROGRAM_INIT_FUNC_ID); +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C void +ia_css_cell_program_load_set_exec_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info) +{ + assert(entry_info != NULL); + + ia_css_cell_program_load_set_start_pc(ssid, entry_info, + IA_CSS_CELL_PROGRAM_EXEC_FUNC_ID); +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C void +ia_css_cell_program_load_set_done_start_pc( + unsigned int ssid, + const struct ia_css_cell_program_entry_func_info_s *entry_info) +{ + assert(entry_info != NULL); + + ia_css_cell_program_load_set_start_pc(ssid, entry_info, + IA_CSS_CELL_PROGRAM_DONE_FUNC_ID); +} + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_prog_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_prog_impl.h new file mode 100644 index 000000000000..f20bc2f6da52 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_program_load_prog_impl.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_PROGRAM_LOAD_PROG_IMPL_H +#define __IA_CSS_CELL_PROGRAM_LOAD_PROG_IMPL_H + +#include "ia_css_cell_program_load_prog.h" +#include "ia_css_fw_load.h" + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_load_prog( + unsigned int ssid, + unsigned int mmid, + ia_css_xmem_address_t host_addr, + unsigned int vied_addr, + struct ia_css_cell_program_s *prog) +{ + int status; + + status = ia_css_cell_program_load_header(mmid, host_addr, prog); + if (status) + return status; + + status = ia_css_cell_program_load_icache_prog( + ssid, mmid, host_addr, vied_addr, prog); + if (status) + return status; + + status = ia_css_cell_program_load_mem_prog( + ssid, mmid, host_addr, vied_addr, prog); + if (status) + return status; + + return status; +} + +IA_CSS_CELL_PROGRAM_LOAD_STORAGE_CLASS_C +int +ia_css_cell_program_load_header( + unsigned int mmid, + ia_css_xmem_address_t host_addr, + struct ia_css_cell_program_s *prog) +{ + + /* read the program header from DDR */ + ia_css_fw_load(mmid, + host_addr, + prog, + sizeof(struct ia_css_cell_program_s)); + + /* check magic number */ + if (prog->magic_number != IA_CSS_CELL_PROGRAM_MAGIC_NUMBER) + return -1; + + return 0; +} + +#if defined(C_RUN) || defined(HRT_UNSCHED) || defined(HRT_SCHED) +#include "ia_css_cell_program_load_csim.h" +#else +#include "ia_css_cell_program_load_bin.h" +#endif + +#endif /* __IA_CSS_CELL_PROGRAM_LOAD_PROG_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_regs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_regs.h new file mode 100644 index 000000000000..4eb283b58de6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cell_program_load/src/ia_css_cell_regs.h @@ -0,0 +1,78 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CELL_REGS_H +#define __IA_CSS_CELL_REGS_H + +#include "storage_class.h" +#include "ipu_device_cell_type_properties.h" +#include "ia_css_cmem.h" + +STORAGE_CLASS_INLINE void +ia_css_cell_regs_set_stat_ctrl(unsigned int ssid, unsigned int regs_addr, + unsigned int value) +{ + ia_css_cmem_store_32(ssid, + regs_addr + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE unsigned int +ia_css_cell_regs_get_stat_ctrl(unsigned int ssid, unsigned int regs_addr) +{ + return ia_css_cmem_load_32(ssid, + regs_addr + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_icache_invalidate(unsigned int ssid, unsigned int regs_addr) +{ + ia_css_cell_regs_set_stat_ctrl(ssid, regs_addr, + 1u << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_regs_set_start_pc(unsigned int ssid, unsigned int regs_addr, + unsigned int pc) +{ + ia_css_cmem_store_32(ssid, + regs_addr + IPU_DEVICE_CELL_START_PC_REG_ADDRESS, pc); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_regs_set_icache_base_address(unsigned int ssid, + unsigned int regs_addr, + unsigned int value) +{ + ia_css_cmem_store_32(ssid, + regs_addr + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS, value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_regs_set_icache_info_bits(unsigned int ssid, + unsigned int regs_addr, + unsigned int value) +{ + ia_css_cmem_store_32(ssid, + regs_addr + IPU_DEVICE_CELL_ICACHE_INFO_BITS_REG_ADDRESS, + value); +} + +STORAGE_CLASS_INLINE void +ia_css_cell_regs_icache_invalidate(unsigned int ssid, unsigned int regs_addr) +{ + ia_css_cell_regs_set_stat_ctrl(ssid, regs_addr, + 1u << IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT); +} + +#endif /* __IA_CSS_CELL_REGS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h new file mode 100644 index 000000000000..e8b0a48b27e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg.h @@ -0,0 +1,60 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_H +#define __IA_CSS_CLIENT_PKG_H + +#include "type_support.h" +#include "ia_css_client_pkg_storage_class.h" +/* for ia_css_client_pkg_header_s (ptr only), ia_css_client_pkg_t */ +#include "ia_css_client_pkg_types.h" + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_pg_manifest_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_list_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_desc_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_prog_bin_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t *offset, + uint32_t *size); + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +int ia_css_client_pkg_get_indexed_prog_desc_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t program_index, + uint32_t *offset, + uint32_t *size); + +#ifdef __INLINE_CLIENT_PKG__ +#include "ia_css_client_pkg_impl.h" +#endif + +#endif /* __IA_CSS_CLIENT_PKG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h new file mode 100644 index 000000000000..98af98d5d824 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H +#define __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __INLINE_CLIENT_PKG__ +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +#else +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_CLIENT_PKG_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_CLIENT_PKG_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h new file mode 100644 index 000000000000..ff5bf01358f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/interface/ia_css_client_pkg_types.h @@ -0,0 +1,44 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_TYPES_H +#define __IA_CSS_CLIENT_PKG_TYPES_H + +#include "type_support.h" + +typedef void ia_css_client_pkg_t; + +struct ia_css_client_pkg_header_s { + uint32_t prog_list_offset; + uint32_t prog_list_size; + uint32_t prog_desc_offset; + uint32_t prog_desc_size; + uint32_t pg_manifest_offset; + uint32_t pg_manifest_size; + uint32_t prog_bin_offset; + uint32_t prog_bin_size; +}; + +struct ia_css_client_pkg_prog_s { + uint32_t prog_id; + uint32_t prog_offset; + uint32_t prog_size; +}; + +struct ia_css_client_pkg_prog_list_s { + uint32_t prog_desc_count; + uint32_t prog_bin_count; +}; + +#endif /* __IA_CSS_CLIENT_PKG_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c new file mode 100644 index 000000000000..0b2fd86d09f3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_CLIENT_PKG__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_client_pkg_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_CLIENT_PKG__ */ +#include "ia_css_client_pkg_impl.h" +#endif /* __INLINE_CLIENT_PKG__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h new file mode 100644 index 000000000000..b79e5de02b89 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/client_pkg/src/ia_css_client_pkg_impl.h @@ -0,0 +1,161 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CLIENT_PKG_IMPL_H +#define __IA_CSS_CLIENT_PKG_IMPL_H + +#include "ia_css_client_pkg.h" +#include "ia_css_client_pkg_types.h" +#include "error_support.h" + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_pg_manifest_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->pg_manifest_offset; + *(size) = client_pkg_header->pg_manifest_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_list_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->prog_list_offset; + *(size) = client_pkg_header->prog_list_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_desc_offset_size( + const struct ia_css_client_pkg_header_s *client_pkg_header, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + + verifjmpexit(NULL != client_pkg_header); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + *(offset) = client_pkg_header->prog_desc_offset; + *(size) = client_pkg_header->prog_desc_size; + ret_val = 0; +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_prog_bin_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t *offset, + uint32_t *size) +{ + uint8_t i; + int ret_val = -1; + struct ia_css_client_pkg_header_s *client_pkg_header = NULL; + const struct ia_css_client_pkg_prog_list_s *pkg_prog_list = NULL; + const struct ia_css_client_pkg_prog_s *pkg_prog_bin_entry = NULL; + + verifjmpexit(NULL != client_pkg); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + client_pkg_header = + (struct ia_css_client_pkg_header_s *)((uint8_t *)client_pkg); + pkg_prog_list = + (struct ia_css_client_pkg_prog_list_s *)((uint8_t *)client_pkg + + client_pkg_header->prog_list_offset); + pkg_prog_bin_entry = + (struct ia_css_client_pkg_prog_s *)((uint8_t *)pkg_prog_list + + sizeof(struct ia_css_client_pkg_prog_list_s)); + pkg_prog_bin_entry += pkg_prog_list->prog_desc_count; + + for (i = 0; i < pkg_prog_list->prog_bin_count; i++) { + if (program_id == pkg_prog_bin_entry->prog_id) { + *(offset) = pkg_prog_bin_entry->prog_offset; + *(size) = pkg_prog_bin_entry->prog_size; + ret_val = 0; + break; + } else if (0 == pkg_prog_bin_entry->prog_size) { + /* We can have a variable number of program descriptors. + * The first non-valid one will have size set to 0 + */ + break; + } + pkg_prog_bin_entry++; + } +EXIT: + return ret_val; +} + +IA_CSS_CLIENT_PKG_STORAGE_CLASS_C +int ia_css_client_pkg_get_indexed_prog_desc_entry_offset_size( + const ia_css_client_pkg_t *client_pkg, + uint32_t program_id, + uint32_t program_index, + uint32_t *offset, + uint32_t *size) +{ + int ret_val = -1; + struct ia_css_client_pkg_header_s *client_pkg_header = NULL; + const struct ia_css_client_pkg_prog_list_s *pkg_prog_list = NULL; + const struct ia_css_client_pkg_prog_s *pkg_prog_desc_entry = NULL; + + verifjmpexit(NULL != client_pkg); + verifjmpexit(NULL != offset); + verifjmpexit(NULL != size); + + client_pkg_header = + (struct ia_css_client_pkg_header_s *)((uint8_t *)client_pkg); + pkg_prog_list = + (struct ia_css_client_pkg_prog_list_s *)((uint8_t *)client_pkg + + client_pkg_header->prog_list_offset); + pkg_prog_desc_entry = + (struct ia_css_client_pkg_prog_s *)((uint8_t *)pkg_prog_list + + sizeof(struct ia_css_client_pkg_prog_list_s)); + + verifjmpexit(program_index < pkg_prog_list->prog_desc_count); + verifjmpexit(program_id == pkg_prog_desc_entry[program_index].prog_id); + verifjmpexit(pkg_prog_desc_entry[program_index].prog_size > 0); + *(offset) = pkg_prog_desc_entry[program_index].prog_offset; + *(size) = pkg_prog_desc_entry[program_index].prog_size; + ret_val = 0; + +EXIT: + return ret_val; +} + +#endif /* __IA_CSS_CLIENT_PKG_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/psys/subsystem_cnlB0.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/psys/subsystem_cnlB0.mk new file mode 100644 index 000000000000..be397a0646bd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/psys/subsystem_cnlB0.mk @@ -0,0 +1,138 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +############################################################################ +# This file is used to specify versions and properties of PSYS firmware +# components. Please note that these are subsystem specific. System specific +# properties should go to system_$IPU_SYSVER.mk. Also the device versions +# should be defined under "devices" or should be taken from the SDK. +############################################################################ + +# define for DPCM Compression/ Decompression module +HAS_DPCM = 1 + +# See HSD 1805169230 +HAS_FWDMA_ALIGNMENT_ISSUE_SIGHTING = 1 + +# Activate loading params and storing stats DDR<->REGs with DMA. +PSYS_USE_ISA_DMA = 1 + +# Used in ISA module +PSYS_ISL_DPC_DPC_V2 = 0 + +# Use the DMA for terminal loading in Psys server +PSYS_SERVER_ENABLE_TERMINAL_LOAD_DMA = 1 + +# Assume OFS will be running concurrently with IPF, and prioritize according to rates of services on devproxy +CONCURRENT_OFS_IPF_PRIORITY_OPTIMIZATION_ENABLED = 1 + +# Enable clock gating of input feeder ibufctrl +ENABLE_IPFD_IBUFCTRL_CLK_GATE = 1 + +# Enable clock gating of input slice light ibufctrl +ENABLE_ISL_IBUFCTRL_CLK_GATE = 1 + +# Enable clock gating of GDC0 +ENABLE_GDC0_CLK_GATE = 1 + + +# define for VCA_VCR2_FF +HAS_VCA_VCR2_FF = 1 + +HAS_GMEM = 1 +HAS_64KB_GDC_MEM = 1 + +# define for enabling mmu_stream_id_lut support +ENABLE_MMU_STREAM_ID_LUT = 1 + +# define for enabling rgbir related chnages in devproxy +HAS_RGBIR = 1 + +# Specification for Psys server's fixed globals' locations +REGMEM_OFFSET = 0 +REGMEM_SECURE_OFFSET = 4096 +REGMEM_SIZE = 20 +REGMEM_WORD_BYTES = 4 +REGMEM_SIZE_BYTES = 80 +GPC_ISP_PERF_DATA_OFFSET = 80 # Taken from REGMEM_OFFSET + REGMEM_SIZE_BYTES +GPC_ISP_PERF_DATA_SIZE_BYTES = 80 +FW_LOAD_NO_OF_REQUEST_OFFSET = 160 # Taken from GPC_ISP_PERF_DATA_OFFSET + GPC_ISP_PERF_DATA_SIZE_BYTES +FW_LOAD_NO_OF_REQUEST_SIZE_BYTES = 4 +DISPATCHER_SCRATCH_SPACE_OFFSET = 4176 # Taken from REGMEM_SECURE_OFFSET + REGMEM_SIZE_BYTES +# Total Used (@ REGMEM_OFFSET) = 164 # FW_LOAD_NO_OF_REQUEST_OFFSET + FW_LOAD_NO_OF_REQUEST_SIZE_BYTES +# Total Used (@ REGMEM_SECURE_OFFSET) = 80 # REGMEM_SIZE_BYTES + +# use DMA NCI for OFS Service to reduce load in tproxy +DMA_NCI_IN_OFS_SERVICE = 1 +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PSYS_SERVER_MANIFEST_VERSION = cnlB0 +PSYS_RESOURCE_MODEL_VERSION = cnlB0 +PSYS_ACCESS_BLOCKER_VERSION = v1 + +# Disable support for PPG protocol to save codesize +PSYS_HAS_PPG_SUPPORT = 0 +# Disable support for late binding +PSYS_HAS_LATE_BINDING_SUPPORT = 0 + +# Specify PSYS server context spaces for caching context from DDR +PSYS_SERVER_NOF_CACHES = 4 +PSYS_SERVER_MAX_NUM_PROC_GRP = $(PSYS_SERVER_NOF_CACHES) +PSYS_SERVER_MAX_NUM_EXEC_PROC_GRP = 8 # Max PG's running, 4 running on Cores, 4 being updated on the host upon executing. +PSYS_SERVER_MAX_PROC_GRP_SIZE = 3352 +PSYS_SERVER_MAX_MANIFEST_SIZE = 3420 +PSYS_SERVER_MAX_CLIENT_PKG_SIZE = 2360 +PSYS_SERVER_MAX_BUFFER_SET_SIZE = 0 +PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_SECTIONS = 90 +PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS = 1 +# The caching scheme for this subsystem suits the method of queueing ahead separate PGs for frames in an interleaved +# fashion. As such there should be as many caches to support to heaviest two concurrent PGs, times two. This results +# in the following distribution of caches: two large ones for the maximum sized PG, two smaller ones for the +# second-largest sized PG. +PSYS_SERVER_CACHE_0_PROC_GRP_SIZE = $(PSYS_SERVER_MAX_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_0_MANIFEST_SIZE = $(PSYS_SERVER_MAX_MANIFEST_SIZE) +PSYS_SERVER_CACHE_0_CLIENT_PKG_SIZE = $(PSYS_SERVER_MAX_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_0_BUFFER_SET_SIZE = $(PSYS_SERVER_MAX_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +PSYS_SERVER_CACHE_1_PROC_GRP_SIZE = $(PSYS_SERVER_CACHE_0_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_1_MANIFEST_SIZE = $(PSYS_SERVER_CACHE_0_MANIFEST_SIZE) +PSYS_SERVER_CACHE_1_CLIENT_PKG_SIZE = $(PSYS_SERVER_CACHE_0_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_1_BUFFER_SET_SIZE = $(PSYS_SERVER_CACHE_0_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_1_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_CACHE_0_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_1_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +PSYS_SERVER_CACHE_2_PROC_GRP_SIZE = 1624 +PSYS_SERVER_CACHE_2_MANIFEST_SIZE = 1248 +PSYS_SERVER_CACHE_2_CLIENT_PKG_SIZE = 1040 +PSYS_SERVER_CACHE_2_BUFFER_SET_SIZE = 0 +PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_SECTIONS = 43 +PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +PSYS_SERVER_CACHE_3_PROC_GRP_SIZE = $(PSYS_SERVER_CACHE_2_PROC_GRP_SIZE) +PSYS_SERVER_CACHE_3_MANIFEST_SIZE = $(PSYS_SERVER_CACHE_2_MANIFEST_SIZE) +PSYS_SERVER_CACHE_3_CLIENT_PKG_SIZE = $(PSYS_SERVER_CACHE_2_CLIENT_PKG_SIZE) +PSYS_SERVER_CACHE_3_BUFFER_SET_SIZE = $(PSYS_SERVER_CACHE_2_BUFFER_SET_SIZE) +PSYS_SERVER_CACHE_3_NUMBER_OF_TERMINAL_SECTIONS = $(PSYS_SERVER_CACHE_2_NUMBER_OF_TERMINAL_SECTIONS) +PSYS_SERVER_CACHE_3_NUMBER_OF_TERMINAL_STORE_SECTIONS = $(PSYS_SERVER_MAX_NUMBER_OF_TERMINAL_STORE_SECTIONS) +# Support dual command context for VTIO - concurrent secure and non-secure streams +PSYS_HAS_DUAL_CMD_CTX_SUPPORT = 1 + +HAS_SPC = 1 +HAS_SPP0 = 1 +HAS_SPP1 = 1 +HAS_ISP0 = 1 +HAS_ISP1 = 1 +HAS_ISP2 = 1 +HAS_ISP3 = 1 + +AB_CONFIG_ARRAY_SIZE = 50 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/system_cnlB0.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/system_cnlB0.mk new file mode 100644 index 000000000000..667282b519c4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/config/system_cnlB0.mk @@ -0,0 +1,96 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +#--- DEFINES REQUIRED TO COMPILE USING LLVM --- +# Enable LLVM/Volcano for IPU4P, SPs only. +VOLCANO_IPU4P = 1 +VOLCANO_SP2601 = 1 +#---------------------------------------------- + +# enable NO_ALIAS for LLVM +ENABLE_NO_ALIAS_FOR_LLVM = 1 + +LOGICAL_FW_INPUT_SYSTEM = input_system_system +LOGICAL_FW_PROCESSING_SYSTEM = processing_system_system +LOGICAL_FW_IPU_SYSTEM = ipu_system +LOGICAL_FW_ISP_SYSTEM = isp2601_default_system +SP_CONTROL_CELL = sp2601_control +SP_PROXY_CELL = sp2601_proxy +ISP_CELL = isp2601 +# The non-capital define isp2601 is used in the sdk, in order to distinguish +# between different isp versions the ISP_CELL_IDENTIFIER define is added. +ISP_CELL_IDENTIFIER = ISP2601 +HAS_IPFD = 1 +HAS_S2M_IN_ISYS_ISL_NONSOC_PATH = 0 +HAS_S2V_IN_ISYS_ISL_NONSOC_PATH = 1 +# ISL-IS non-SoC path has ISA with PAF and DPC-Pext support for IPU4P-B0 +HAS_ISA_IN_ISYS_ISL = 1 +HAS_PAF_IN_ISYS_ISL = 1 +HAS_DPC_PEXT_IN_ISYS_ISL = 1 +HAS_PMA_IF = 1 + +HAS_MIPIBE_IN_PSYS_ISL = 1 + +HAS_VPLESS_SUPPORT = 0 + +DLI_SYSTEM = hive_isp_css_2600_system +RESOURCE_MANAGER_VERSION = v2 +MEM_RESOURCE_VALIDATION_ERROR = 0 +OFS_SCALER_1_4K_TILEY_422_SUPPORT= 1 +PROGDESC_ACC_SYMBOLS_VERSION = v1 +DEVPROXY_INTERFACE_VERSION = v1 +FW_ABI_IPU_TYPES_VERSION = v1 + +HAS_ONLINE_MODE_SUPPORT_IN_ISYS_PSYS = 0 + +MMU_INTERFACE_VERSION = v2 +DEVICE_ACCESS_VERSION = v2 +PSYS_SERVER_VERSION = v3 +PSYS_SERVER_LOADER_VERSION = v1 +PSYS_HW_VERSION = CNL_B0_HW + +# Enable FW_DMA for loading firmware +PSYS_SERVER_ENABLE_FW_LOAD_DMA = 1 + +NCI_SPA_VERSION = v1 +MANIFEST_TOOL_VERSION = v2 +PSYS_CON_MGR_TOOL_VERSION = v1 +# TODO: Should be removed after performance issues OTF are solved +PSYS_PROC_MGR_VERSION = v1 +IPU_RESOURCES_VERSION = v2 + +HAS_ACC_CLUSTER_PAF_PAL = 1 +HAS_ACC_CLUSTER_PEXT_PAL = 1 +HAS_ACC_CLUSTER_GBL_PAL = 1 + +# TODO use version naming scheme "v#" to decouple +# IPU_SYSVER from version. +PARAMBINTOOL_ISA_INIT_VERSION = cnlB0 + +# Select EQC2EQ version +# Version 1: uniform address space, equal EQ addresses regardless of EQC device +# Version 2: multiple addresses per EQ, depending on location of EQC device +EQC2EQ_VERSION = v1 + +# Select DMA instance for fw_load +FW_LOAD_DMA_INSTANCE = NCI_DMA_FW + +HAS_DMA_FW = 1 + +HAS_SIS = 0 +HAS_IDS = 1 + +PSYS_SERVER_ENABLE_TPROXY = 1 +PSYS_SERVER_ENABLE_DEVPROXY = 1 +NCI_OFS_VERSION = v1 diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk new file mode 100644 index 000000000000..8ecc3e42e55d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/cpd_component.mk @@ -0,0 +1,28 @@ +## +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +## + +# MODULE is cpd/cpd_component + +CPD_DIR = $${MODULES_DIR}/cpd +CPD_COMPONENT_DIR = $${MODULES_DIR}/cpd/cpd_component +CPD_COMPONENT_INTERFACE = $(CPD_COMPONENT_DIR)/interface +CPD_COMPONENT_SOURCES = $(CPD_COMPONENT_DIR)/src + +CPD_COMPONENT_FILES = $(CPD_COMPONENT_SOURCES)/ia_css_cpd_component_create.c +CPD_COMPONENT_FILES += $(CPD_COMPONENT_SOURCES)/ia_css_cpd_component.c +CPD_COMPONENT_CPPFLAGS = -I$(CPD_COMPONENT_INTERFACE) +CPD_COMPONENT_CPPFLAGS += -I$(CPD_COMPONENT_SOURCES) +CPD_COMPONENT_CPPFLAGS += -I$(CPD_DIR) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h new file mode 100644 index 000000000000..7ad3070b2fd7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_component/interface/ia_css_cpd_component_types.h @@ -0,0 +1,90 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_CPD_COMPONENT_TYPES_H +#define __IA_CSS_CPD_COMPONENT_TYPES_H + +/** @file + * This file contains datastructure related to generation of CPD file + */ + +#include "type_support.h" + +#define SIZE_OF_FW_ARCH_VERSION 7 +#define SIZE_OF_SYSTEM_VERSION 11 +#define SIZE_OF_COMPONENT_NAME 12 + +enum ia_css_cpd_component_endianness { + IA_CSSCPD_COMP_ENDIAN_RSVD, + IA_CSS_CPD_COMP_LITTLE_ENDIAN, + IA_CSS_CPD_COMP_BIG_ENDIAN +}; + +/** Module Data (components) Header + * Following data structure has been created using FAS section 5.25 + * Open : Should we add padding at the end of module directory + * (the component must be 512 aligned) + */ +typedef struct { + uint32_t header_size; + /**< Specifies endianness of the binary data */ + unsigned int endianness; + /**< fw_pkg_date is current date stored in 'binary decimal' + * representation e.g. 538248729 (0x20150619) + */ + uint32_t fw_pkg_date; + /**< hive_sdk_date is date of HIVE_SDK stored in + * 'binary decimal' representation + */ + uint32_t hive_sdk_date; + /**< compiler_date is date of ptools stored in + * 'binary decimal' representation + */ + uint32_t compiler_date; + /**< UNSCHED / SCHED / TARGET / CRUN */ + unsigned int target_platform_type; + /**< specifies the system version stored as string + * e.g. BXTB0_IPU4'\0' + */ + uint8_t system_version[SIZE_OF_SYSTEM_VERSION]; + /**< specifies fw architecture version e.g. for BXT CSS3.0'\0' */ + uint8_t fw_arch_version[SIZE_OF_FW_ARCH_VERSION]; + uint8_t rsvd[2]; +} ia_css_header_component_t; + +/** Module Data Directory = Directory Header + Directory Entry (0..n) + * Following two Data Structure has been taken from CSE Storage FAS (CPD desgin) + * Module Data Directory Header + */ +typedef struct { + uint32_t header_marker; + uint32_t number_of_entries; + uint8_t header_version; + uint8_t entry_version; + uint8_t header_length; /**< 0x10 (16) Fixed for this version*/ + uint8_t checksum; + uint32_t partition_name; +} ia_css_directory_header_component_t; + +/** Module Date Directory Entry + */ +typedef struct { + /**< character string describing the component name */ + uint8_t entry_name[SIZE_OF_COMPONENT_NAME]; + uint32_t offset; + uint32_t length; + uint32_t rsvd; /**< Must be 0 */ +} ia_css_directory_entry_component_t; + +#endif /* __IA_CSS_CPD_COMPONENT_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk new file mode 100644 index 000000000000..ac78815dfbd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/cpd_metadata.mk @@ -0,0 +1,29 @@ +## +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +## + + +# MODULE is CPD UTL (Metadata File Extension) + +CPD_DIR = $${MODULES_DIR}/cpd/ +CPD_METADATA_DIR = $${MODULES_DIR}/cpd/cpd_metadata +CPD_METADATA_INTERFACE = $(CPD_METADATA_DIR)/interface +CPD_METADATA_SOURCES = $(CPD_METADATA_DIR)/src + +CPD_METADATA_FILES = $(CPD_METADATA_SOURCES)/ia_css_cpd_metadata_create.c +CPD_METADATA_FILES += $(CPD_METADATA_SOURCES)/ia_css_cpd_metadata.c +CPD_METADATA_CPPFLAGS = -I$(CPD_METADATA_INTERFACE) \ + -I$(CPD_METADATA_SOURCES) \ + -I$(CPD_DIR) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h new file mode 100644 index 000000000000..a88c6aede08c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/cpd/cpd_metadata/interface/ia_css_cpd_metadata_types.h @@ -0,0 +1,111 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_CPD_METADATA_TYPES_H +#define __IA_CSS_CPD_METADATA_TYPES_H + +/** @file + * This file contains data structures related to generation of + * metadata file extension + */ +#include + +/* As per v0.2 manifest document + * Header = Extension Type (4) + Extension Length (4) + + * iUnit Image Type (4) + Reserved (16) + */ +#define IPU_METADATA_HEADER_RSVD_SIZE 16 +#define IPU_METADATA_HEADER_FIELDS_SIZE 12 +#define IPU_METADATA_HEADER_SIZE \ + (IPU_METADATA_HEADER_FIELDS_SIZE + IPU_METADATA_HEADER_RSVD_SIZE) + +/* iUnit metadata extension tpye value */ +#define IPU_METADATA_EXTENSION_TYPE 16 + +/* Unique id for level 0 bootloader component */ +#define IA_CSS_IUNIT_BTLDR_ID 0 +/* Unique id for psys server program group component */ +#define IA_CSS_IUNIT_PSYS_SERVER_ID 1 +/* Unique id for isys server program group component */ +#define IA_CSS_IUNIT_ISYS_SERVER_ID 2 +/* Initial Identifier for client program group component */ +#define IA_CSS_IUNIT_CLIENT_ID 3 + +/* Use this to parse date from release version from the iUnit component + * e.g. 20150701 + */ +#define IA_CSS_IUNIT_COMP_DATE_SIZE 8 +/* offset of release version in program group binary + * e.g. release_version = "scci_gerrit_20150716_2117" + * In cpd file we only use date/version for the component + */ +#define IA_CSS_IUNIT_DATE_OFFSET 12 + +#define IPU_METADATA_HASH_KEY_SIZE 32 +#define IPU_METADATA_ATTRIBUTE_SIZE 16 +#define IA_CSE_METADATA_COMPONENT_ID_MAX 127 + +typedef enum { + IA_CSS_CPD_METADATA_IMAGE_TYPE_RESERVED, + IA_CSS_CPD_METADATA_IMAGE_TYPE_BOOTLOADER, + IA_CSS_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE +} ia_css_cpd_metadata_image_type_t; + +typedef enum { + IA_CSS_CPD_MAIN_FW_TYPE_RESERVED, + IA_CSS_CPD_MAIN_FW_TYPE_PSYS_SERVER, + IA_CSS_CPD_MAIN_FW_TYPE_ISYS_SERVER, + IA_CSS_CPD_MAIN_FW_TYPE_CLIENT +} ia_css_cpd_iunit_main_fw_type_t; + +/** Data structure for component specific information + * Following data structure has been taken from CSE Manifest v0.2 + */ +typedef struct { + /**< Component ID - unique for each component */ + uint32_t id; + /**< Size of the components */ + uint32_t size; + /**< Version/date of when the components is being generated/created */ + uint32_t version; + /**< SHA 256 Hash Key for component */ + uint8_t sha2_hash[IPU_METADATA_HASH_KEY_SIZE]; + /**< component sp entry point + * - Only valid for btldr/psys/isys server component + */ + uint32_t entry_point; + /**< component icache base address + * - Only valid for btldr/psys/isys server component + */ + uint32_t icache_base_offset; + /**< Resevred - must be 0 */ + uint8_t attributes[IPU_METADATA_ATTRIBUTE_SIZE]; +} ia_css_cpd_metadata_component_t; + +/** Data structure for Metadata File Extension Header + */ +typedef struct { + /**< Specifies the binary image type + * - could be bootloader or main firmware + */ + ia_css_cpd_metadata_image_type_t image_type; + /**< Number of components available in metadata file extension + * (For btldr always 1) + */ + uint32_t component_count; + /**< Component specific information */ + ia_css_cpd_metadata_component_t *components; +} ia_css_cpd_metadata_desc_t; + +#endif /* __IA_CSS_CPD_METADATA_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/device_access.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/device_access.mk new file mode 100644 index 000000000000..1629d9af803b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/device_access.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifndef _DEVICE_ACCESS_MK_ +_DEVICE_ACCESS_MK_ = 1 + +# DEVICE_ACCESS_VERSION= +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk + +DEVICE_ACCESS_DIR=$${MODULES_DIR}/device_access +DEVICE_ACCESS_INTERFACE=$(DEVICE_ACCESS_DIR)/interface +DEVICE_ACCESS_SOURCES=$(DEVICE_ACCESS_DIR)/src + +DEVICE_ACCESS_HOST_FILES = + +DEVICE_ACCESS_FW_FILES = + +DEVICE_ACCESS_HOST_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS = \ + -I$(DEVICE_ACCESS_INTERFACE) \ + -I$(DEVICE_ACCESS_SOURCES) + +DEVICE_ACCESS_FW_CPPFLAGS += \ + -I$(DEVICE_ACCESS_SOURCES)/$(DEVICE_ACCESS_VERSION) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h new file mode 100644 index 000000000000..3dc47c29fcab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_cmem.h @@ -0,0 +1,58 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_H +#define __IA_CSS_CMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_cmem_address_t; +#else +#include +typedef vied_subsystem_address_t ia_css_cmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size); + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_cmem_cell.h" +#else +#include "ia_css_cmem_host.h" +#endif + +#endif /* __IA_CSS_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h new file mode 100644 index 000000000000..de2b94d8af54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_H +#define __IA_CSS_XMEM_H + +#include "type_support.h" +#include "storage_class.h" + +#ifdef __VIED_CELL +typedef unsigned int ia_css_xmem_address_t; +#else +#include +typedef host_virtual_address_t ia_css_xmem_address_t; +#endif + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address); + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value); + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes); + +/* Include inline implementation */ + +#ifdef __VIED_CELL +#include "ia_css_xmem_cell.h" +#else +#include "ia_css_xmem_host.h" +#endif + +#endif /* __IA_CSS_XMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h new file mode 100644 index 000000000000..57aab3323c73 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/interface/ia_css_xmem_cmem.h @@ -0,0 +1,35 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_H +#define __IA_CSS_XMEM_CMEM_H + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size); + +/* include inline implementation */ +#include "ia_css_xmem_cmem_impl.h" + +#endif /* __IA_CSS_XMEM_CMEM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h new file mode 100644 index 000000000000..22799e67214c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_cmem_host.h @@ -0,0 +1,121 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_CMEM_HOST_H +#define __IA_CSS_CMEM_HOST_H + +/* This file is an inline implementation for the interface ia_css_cmem.h + * and should only be included there. */ + +#include "assert_support.h" +#include "misc_support.h" + +STORAGE_CLASS_INLINE uint32_t +ia_css_cmem_load_32(unsigned int ssid, ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + return vied_subsystem_load_32(ssid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_cond_cmem_load_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + return vied_subsystem_load_32(ssid, address); + else + return 0; +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store_32(unsigned int ssid, ia_css_cmem_address_t address, + uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cond_cmem_store_32(bool cond, unsigned int ssid, + ia_css_cmem_address_t address, uint32_t data) +{ + /* Address has to be word aligned */ + assert(0 == address % 4); + if (cond) + vied_subsystem_store_32(ssid, address, data); +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_load(unsigned int ssid, ia_css_cmem_address_t address, void *data, + unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + *data32 = ia_css_cmem_load_32(ssid, address); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_store(unsigned int ssid, ia_css_cmem_address_t address, + const void *data, unsigned int size) +{ + uint32_t *data32 = (uint32_t *)data; + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + assert((long)data % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, *data32); + address += 4; + data32 += 1; + } +} + +STORAGE_CLASS_INLINE void +ia_css_cmem_zero(unsigned int ssid, ia_css_cmem_address_t address, + unsigned int size) +{ + uint32_t end = address + size; + + assert(size % 4 == 0); + assert(address % 4 == 0); + + while (address != end) { + ia_css_cmem_store_32(ssid, address, 0); + address += 4; + } +} + +STORAGE_CLASS_INLINE ia_css_cmem_address_t +ia_css_cmem_get_cmem_addr_from_dmem(unsigned int base_addr, void *p) +{ + NOT_USED(base_addr); + return (ia_css_cmem_address_t)(uintptr_t)p; +} + +#endif /* __IA_CSS_CMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h new file mode 100644 index 000000000000..adc178b75059 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_cmem_impl.h @@ -0,0 +1,79 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_CMEM_IMPL_H +#define __IA_CSS_XMEM_CMEM_IMPL_H + +#include "ia_css_xmem_cmem.h" + +#include "ia_css_cmem.h" +#include "ia_css_xmem.h" + +/* Copy data from xmem to cmem, e.g., from a program in DDR to a cell's DMEM */ +/* This may also be implemented using DMA */ + +STORAGE_CLASS_INLINE void +ia_css_xmem_to_cmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_cmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_xmem_load_32(mmid, src); + ia_css_cmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + +/* Copy data from cmem to xmem */ + +STORAGE_CLASS_INLINE void +ia_css_cmem_to_xmem_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_cmem_address_t src, + ia_css_xmem_address_t dst, + unsigned int size) +{ + /* copy from ddr to subsystem, e.g., cell dmem */ + ia_css_xmem_address_t end = dst + size; + + assert(size % 4 == 0); + assert((uintptr_t) dst % 4 == 0); + assert((uintptr_t) src % 4 == 0); + + while (dst != end) { + uint32_t data; + + data = ia_css_cmem_load_32(mmid, src); + ia_css_xmem_store_32(ssid, dst, data); + dst += 4; + src += 4; + } +} + + +#endif /* __IA_CSS_XMEM_CMEM_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h new file mode 100644 index 000000000000..d94991fc1114 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/device_access/src/ia_css_xmem_host.h @@ -0,0 +1,84 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_XMEM_HOST_H +#define __IA_CSS_XMEM_HOST_H + +#include "ia_css_xmem.h" +#include +#include "assert_support.h" +#include + +STORAGE_CLASS_INLINE uint8_t +ia_css_xmem_load_8(unsigned int mmid, ia_css_xmem_address_t address) +{ + return shared_memory_load_8(mmid, address); +} + +STORAGE_CLASS_INLINE uint16_t +ia_css_xmem_load_16(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + return shared_memory_load_16(mmid, address); +} + +STORAGE_CLASS_INLINE uint32_t +ia_css_xmem_load_32(unsigned int mmid, ia_css_xmem_address_t address) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + return shared_memory_load_32(mmid, address); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_load(unsigned int mmid, ia_css_xmem_address_t address, void *data, + unsigned int size) +{ + shared_memory_load(mmid, address, data, size); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_8(unsigned int mmid, ia_css_xmem_address_t address, + uint8_t value) +{ + shared_memory_store_8(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_16(unsigned int mmid, ia_css_xmem_address_t address, + uint16_t value) +{ + /* Address has to be half-word aligned */ + assert(0 == (uintptr_t) address % 2); + shared_memory_store_16(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store_32(unsigned int mmid, ia_css_xmem_address_t address, + uint32_t value) +{ + /* Address has to be word aligned */ + assert(0 == (uintptr_t) address % 4); + shared_memory_store_32(mmid, address, value); +} + +STORAGE_CLASS_INLINE void +ia_css_xmem_store(unsigned int mmid, ia_css_xmem_address_t address, + const void *data, unsigned int bytes) +{ + shared_memory_store(mmid, address, data, bytes); +} + +#endif /* __IA_CSS_XMEM_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h new file mode 100644 index 000000000000..5102f6e44d2f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/cnlB0/ipu_device_buttress_properties_struct.h @@ -0,0 +1,68 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H + +/* Destination values for master port 0 and bitfield "request_dest" */ +enum cio_M0_btrs_dest { + DEST_IS_BUT_REGS = 0, + DEST_IS_DDR, + RESERVED, + DEST_IS_SUBSYSTEM, + N_BTRS_DEST +}; + +/* Bit-field positions for M0 info bits */ +enum ia_css_info_bits_m0_pos { + IA_CSS_INFO_BITS_M0_SNOOPABLE_POS = 0, + IA_CSS_INFO_BITS_M0_IMR_DESTINED_POS = 1, + IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS = 4 +}; + +#define IA_CSS_INFO_BITS_M0_DDR \ + (DEST_IS_DDR << IA_CSS_INFO_BITS_M0_REQUEST_DEST_POS) +#define IA_CSS_INFO_BITS_M0_SNOOPABLE (1 << IA_CSS_INFO_BITS_M0_SNOOPABLE_POS) + +/* Info bits as expected by the buttress */ +/* Deprecated because bit fields are not portable */ + +/* For master port 0*/ +union cio_M0_t { + struct { + unsigned int snoopable : 1; + unsigned int imr_destined : 1; + unsigned int spare0 : 2; + unsigned int request_dest : 2; + unsigned int spare1 : 26; + } as_bitfield; + unsigned int as_word; +}; + +/* For master port 1*/ +union cio_M1_t { + struct { + unsigned int spare0 : 1; + unsigned int deadline_pointer : 1; + unsigned int reserved : 1; + unsigned int zlw : 1; + unsigned int stream_id : 4; + unsigned int address_swizzling : 1; + unsigned int spare1 : 23; + } as_bitfield; + unsigned int as_word; +}; + + +#endif /* __IPU_DEVICE_BUTTRESS_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h new file mode 100644 index 000000000000..e6e1e9dcbe80 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_H +#define __IPU_DEVICE_CELL_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_cell_type_properties.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id); + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id); + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id); +#endif + +#include "ipu_device_cell_properties_func.h" + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h new file mode 100644 index 000000000000..481b0504a237 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_func.h @@ -0,0 +1,164 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_FUNC_H +#define __IPU_DEVICE_CELL_PROPERTIES_FUNC_H + +/* define properties for all cells uses in ISYS */ + +#include "ipu_device_cell_properties_impl.h" +#include "ipu_device_cell_devices.h" +#include "assert_support.h" +#include "storage_class.h" + +enum {IA_CSS_CELL_MASTER_ADDRESS_WIDTH = 32}; + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_devices(void) +{ + return NUM_CELLS; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_memories(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_memories; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_size(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + mem_size[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + return ipu_device_cell_properties[cell_id].mem_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_databus_memory_address(const unsigned int cell_id, + const unsigned int mem_id) +{ + assert(cell_id < NUM_CELLS); + assert(mem_id < ipu_device_cell_num_memories(cell_id)); + assert(mem_id != 0); + return ipu_device_cell_properties[cell_id].mem_databus_address[mem_id]; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_num_masters(const unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + num_master_ports; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_bits(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].segment_bits; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_num_segments(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << ipu_device_cell_master_segment_bits(cell_id, master_id); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_segment_size(const unsigned int cell_id, + const unsigned int master_id) +{ + return 1u << (IA_CSS_CELL_MASTER_ADDRESS_WIDTH - + ipu_device_cell_master_segment_bits(cell_id, master_id)); +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_stride(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].stride; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_base_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].base_address_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_master_info_override_reg(const unsigned int cell_id, + const unsigned int master_id) +{ + assert(cell_id < NUM_CELLS); + assert(master_id < ipu_device_cell_num_masters(cell_id)); + return + ipu_device_cell_properties[cell_id].type_properties-> + master[master_id].info_override_bits_register; +} + +STORAGE_CLASS_INLINE unsigned int +ipu_device_cell_icache_align(unsigned int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_cell_properties[cell_id].type_properties->count-> + icache_align; +} + +#ifdef C_RUN +STORAGE_CLASS_INLINE int +ipu_device_cell_id_crun(int cell_id) +{ + assert(cell_id < NUM_CELLS); + return ipu_device_map_cell_id_to_crun_proc_id[cell_id]; +} +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_FUNC_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h new file mode 100644 index 000000000000..63397dc0b7fe --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_properties_struct.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H +#define __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H + +/* definitions for all cell types */ + +struct ipu_device_cell_count_s { + unsigned int num_memories; + unsigned int num_master_ports; + unsigned int num_stall_bits; + unsigned int icache_align; +}; + +struct ipu_device_cell_master_properties_s { + unsigned int segment_bits; + unsigned int stride; /* offset to register of next segment */ + unsigned int base_address_register; /* address of first base address + register */ + unsigned int info_bits_register; + unsigned int info_override_bits_register; +}; + +struct ipu_device_cell_type_properties_s { + const struct ipu_device_cell_count_s *count; + const struct ipu_device_cell_master_properties_s *master; + const unsigned int *reg_offset; /* offsets of registers, some depend + on cell type */ + const unsigned int *mem_size; +}; + +struct ipu_device_cell_properties_s { + const struct ipu_device_cell_type_properties_s *type_properties; + const unsigned int *mem_address; + const unsigned int *mem_databus_address; + /* const cell_master_port_properties_s* master_port_properties; */ +}; + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h new file mode 100644 index 000000000000..72caed3eef0c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_cell_type_properties.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_TYPE_PROPERTIES_H +#define __IPU_DEVICE_CELL_TYPE_PROPERTIES_H + +#define IPU_DEVICE_INVALID_MEM_ADDRESS 0xFFFFFFFF + +enum ipu_device_cell_stat_ctrl_bit { + IPU_DEVICE_CELL_STAT_CTRL_RESET_BIT = 0, + IPU_DEVICE_CELL_STAT_CTRL_START_BIT = 1, + IPU_DEVICE_CELL_STAT_CTRL_RUN_BIT = 3, + IPU_DEVICE_CELL_STAT_CTRL_READY_BIT = 5, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_BIT = 6, + IPU_DEVICE_CELL_STAT_CTRL_STALL_BIT = 7, + IPU_DEVICE_CELL_STAT_CTRL_CLEAR_IRQ_MASK_FLAG_BIT = 8, + IPU_DEVICE_CELL_STAT_CTRL_BROKEN_IRQ_MASK_FLAG_BIT = 9, + IPU_DEVICE_CELL_STAT_CTRL_READY_IRQ_MASK_FLAG_BIT = 10, + IPU_DEVICE_CELL_STAT_CTRL_SLEEP_IRQ_MASK_FLAG_BIT = 11, + IPU_DEVICE_CELL_STAT_CTRL_INVALIDATE_ICACHE_BIT = 12, + IPU_DEVICE_CELL_STAT_CTRL_ICACHE_ENABLE_PREFETCH_BIT = 13 +}; + +enum ipu_device_cell_reg_addr { + IPU_DEVICE_CELL_STAT_CTRL_REG_ADDRESS = 0x0, + IPU_DEVICE_CELL_START_PC_REG_ADDRESS = 0x4, + IPU_DEVICE_CELL_ICACHE_BASE_REG_ADDRESS = 0x10, + IPU_DEVICE_CELL_ICACHE_INFO_BITS_REG_ADDRESS = 0x14 +}; + +enum ipu_device_cell_reg { + IPU_DEVICE_CELL_STAT_CTRL_REG, + IPU_DEVICE_CELL_START_PC_REG, + IPU_DEVICE_CELL_ICACHE_BASE_REG, + IPU_DEVICE_CELL_DEBUG_PC_REG, + IPU_DEVICE_CELL_STALL_REG, + IPU_DEVICE_CELL_NUM_REGS +}; + +enum ipu_device_cell_mem { + IPU_DEVICE_CELL_REGS, /* memory id of registers */ + IPU_DEVICE_CELL_PMEM, /* memory id of pmem */ + IPU_DEVICE_CELL_DMEM, /* memory id of dmem */ + IPU_DEVICE_CELL_BAMEM, /* memory id of bamem */ + IPU_DEVICE_CELL_VMEM /* memory id of vmem */ +}; +#define IPU_DEVICE_CELL_NUM_MEMORIES (IPU_DEVICE_CELL_VMEM + 1) + +enum ipu_device_cell_master { + IPU_DEVICE_CELL_MASTER_ICACHE, /* master port id of icache */ + IPU_DEVICE_CELL_MASTER_QMEM, + IPU_DEVICE_CELL_MASTER_CMEM, + IPU_DEVICE_CELL_MASTER_XMEM, + IPU_DEVICE_CELL_MASTER_XVMEM +}; +#define IPU_DEVICE_CELL_MASTER_NUM_MASTERS (IPU_DEVICE_CELL_MASTER_XVMEM + 1) + +#endif /* __IPU_DEVICE_CELL_TYPE_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h new file mode 100644 index 000000000000..fd0c5a586c94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties.h @@ -0,0 +1,26 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_PROPERTIES_H +#define __IPU_DEVICE_GP_PROPERTIES_H + +#include "storage_class.h" +#include "ipu_device_gp_properties_types.h" + +STORAGE_CLASS_INLINE unsigned int +ipu_device_gp_mux_addr(const unsigned int device_id, const unsigned int mux_id); + +#include "ipu_device_gp_properties_func.h" + +#endif /* __IPU_DEVICE_GP_PROPERTIES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h new file mode 100644 index 000000000000..3032273696ea --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/interface/ipu_device_gp_properties_types.h @@ -0,0 +1,103 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_PROPERTIES_TYPES_H +#define __IPU_DEVICE_GP_PROPERTIES_TYPES_H + +enum ipu_device_gp_isa_value { + /* ISA_MUX_SEL options */ + IPU_DEVICE_GP_ISA_MUX_SEL_ICA = 0, /* Enable output after FF ICA */ + IPU_DEVICE_GP_ISA_MUX_SEL_LSC = 1, /* Enable output after FF LSC */ + IPU_DEVICE_GP_ISA_MUX_SEL_DPC = 2, /* Enable output after FF DPC */ + /* ICA stream block options */ + /* UNBLOCK signal received from ICA */ + IPU_DEVICE_GP_ISA_ICA_UNBLOCK = 0, + /* BLOCK signal received from ICA */ + IPU_DEVICE_GP_ISA_ICA_BLOCK = 1, + /* LSC stream block options */ + /* UNBLOCK signal received from LSC */ + IPU_DEVICE_GP_ISA_LSC_UNBLOCK = 0, + /* BLOCK signal received from LSC */ + IPU_DEVICE_GP_ISA_LSC_BLOCK = 1, + /* DPC stream block options */ + /* UNBLOCK signal received from DPC */ + IPU_DEVICE_GP_ISA_DPC_UNBLOCK = 0, + /* BLOCK signal received from DPC */ + IPU_DEVICE_GP_ISA_DPC_BLOCK = 1, + /* Defines needed only for bxtB0 */ + /* ISA_AWB_MUX_SEL options */ + /* Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_SEL_ICA = 0, + /* DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_SEL_DPC = 1, + /* ISA_AWB_MUX_SEL options */ + /* UNBLOCK DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_ICA_UNBLOCK = 0, + /* BLOCK DPC input */ + IPU_DEVICE_GP_ISA_AWB_MUX_ICA_BLOCK = 1, + /* ISA_AWB_MUX_SEL options */ + /* UNBLOCK Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_DPC_UNBLOCK = 0, + /* BLOCK Input Correction input */ + IPU_DEVICE_GP_ISA_AWB_MUX_DPC_BLOCK = 1, + + /* PAF STRM options */ + /* Disable streaming to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_DISABLE_STREAM = 0, + /* Enable stream0 to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_ENABLE_STREAM0 = 1, + /* Enable stream1 to PAF FF*/ + IPU_DEVICE_GP_ISA_PAF_ENABLE_STREAM1 = 2, + /* PAF SRC SEL options */ + /* External channel input */ + IPU_DEVICE_GP_ISA_PAF_SRC_SEL0 = 0, + /* DPC extracted input */ + IPU_DEVICE_GP_ISA_PAF_SRC_SEL1 = 1, + /* PAF_GDDPC_BLK options */ + IPU_DEVICE_GP_ISA_PAF_GDDPC_PORT_BLK0 = 0, + IPU_DEVICE_GP_ISA_PAF_GDDPC_PORT_BLK1 = 1, + /* PAF ISA STR_PORT options */ + IPU_DEVICE_GP_ISA_PAF_STR_PORT0 = 0, + IPU_DEVICE_GP_ISA_PAF_STR_PORT1 = 1, + + /* sis port block options */ + IPU_DEVICE_GP_ISA_SIS_PORT_UNBLOCK = 0, + IPU_DEVICE_GP_ISA_SIS_PORT_BLOCK = 1, + IPU_DEVICE_GP_ISA_CONF_INVALID = 0xFF +}; + +enum ipu_device_gp_psa_value { + /* Defines needed for bxtB0 */ + /* PSA_STILLS_MODE_MUX */ + IPU_DEVICE_GP_PSA_MUX_POST_RYNR_ROUTE_WO_DM = 0, + IPU_DEVICE_GP_PSA_MUX_POST_RYNR_ROUTE_W_DM = 1, + /* PSA_ACM_DEMUX */ + IPU_DEVICE_GP_PSA_DEMUX_PRE_ACM_ROUTE_TO_ACM = 0, + IPU_DEVICE_GP_PSA_DEMUX_PRE_ACM_ROUTE_TO_S2V = 1, + /* PSA_S2V_RGB_F_MUX */ + IPU_DEVICE_GP_PSA_MUX_PRE_S2V_RGB_F_FROM_ACM = 0, + IPU_DEVICE_GP_PSA_MUX_PRE_S2V_RGB_F_FROM_DM_OR_SPLITTER = 1, + /* PSA_V2S_RGB_4_DEMUX */ + IPU_DEVICE_GP_PSA_DEMUX_POST_V2S_RGB_4_TO_GTM = 0, + IPU_DEVICE_GP_PSA_DEMUX_POST_V2S_RGB_4_TO_ACM = 1, +}; + +enum ipu_device_gp_isl_value { + /* choose and route pixel stream to CSI BE */ + IPU_DEVICE_GP_ISL_CSI_BE_IN_USE = 0, + /* choose and route pixel stream bypass CSI BE */ + IPU_DEVICE_GP_ISL_CSI_BE_BYPASS +}; + +#endif /* __IPU_DEVICE_GP_PROPERTIES_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_acb_devices.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_acb_devices.h new file mode 100644 index 000000000000..4898fbb2e875 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_acb_devices.h @@ -0,0 +1,43 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_ACB_DEVICES_H +#define __IPU_DEVICE_ACB_DEVICES_H + +enum ipu_device_acb_id { + /* PSA accelerators */ + IPU_DEVICE_ACB_WBA_ID = 0, + IPU_DEVICE_ACB_RYNR_ID, + IPU_DEVICE_ACB_DEMOSAIC_ID, + IPU_DEVICE_ACB_ACM_ID, /* In CNLB0 ACM is called VCA in HW */ + IPU_DEVICE_ACB_GTC_ID, + IPU_DEVICE_ACB_YUV1_ID, + IPU_DEVICE_ACB_DVS_ID, + IPU_DEVICE_ACB_LACE_ID, + /* ISA accelerators */ + IPU_DEVICE_ACB_ICA_ID, + IPU_DEVICE_ACB_LSC_ID, + IPU_DEVICE_ACB_DPC_ID, + IPU_DEVICE_ACB_IDS_ID, + IPU_DEVICE_ACB_AWB_ID, + IPU_DEVICE_ACB_AF_ID, + IPU_DEVICE_ACB_AE_ID, + IPU_DEVICE_ACB_NUM_ACB +}; + +#define IPU_DEVICE_ACB_NUM_PSA_ACB (IPU_DEVICE_ACB_LACE_ID + 1) +#define IPU_DEVICE_ACB_NUM_ISA_ACB \ + (IPU_DEVICE_ACB_NUM_ACB - IPU_DEVICE_ACB_NUM_PSA_ACB) + +#endif /* __IPU_DEVICE_ACB_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_devices.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_devices.h new file mode 100644 index 000000000000..0c923d139638 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_devices.h @@ -0,0 +1,38 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_CELL_DEVICES_H +#define __IPU_DEVICE_CELL_DEVICES_H + +#define SPC0_CELL processing_system_sp_cluster_sp_cluster_logic_spc_tile_sp +#define SPP0_CELL processing_system_sp_cluster_sp_cluster_logic_spp_tile0_sp +#define SPP1_CELL processing_system_sp_cluster_sp_cluster_logic_spp_tile1_sp +#define ISP0_CELL processing_system_isp_tile0_logic_isp +#define ISP1_CELL processing_system_isp_tile1_logic_isp +#define ISP2_CELL processing_system_isp_tile2_logic_isp +#define ISP3_CELL processing_system_isp_tile3_logic_isp + +enum ipu_device_psys_cell_id { + SPC0, + SPP0, + SPP1, + ISP0, + ISP1, + ISP2, + ISP3 +}; +#define NUM_CELLS (ISP3 + 1) +#define NUM_ISP_CELLS 4 + +#endif /* __IPU_DEVICE_CELL_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_defs.h new file mode 100644 index 000000000000..2b80e2822a90 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_defs.h @@ -0,0 +1,65 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +/* Generated file - please do not edit. */ + +#ifndef _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ +#define SPC0_REGS_CBUS_ADDRESS 0x00000000 +#define SPC0_DMEM_CBUS_ADDRESS 0x00008000 +#define SPC0_DMEM_DBUS_ADDRESS 0x02000000 +#define SPC0_DMEM_DMA_M0_ADDRESS SPC0_DMEM_DBUS_ADDRESS +#define SPC0_DMEM_INT_DMA_M0_ADDRESS SPC0_DMEM_DBUS_ADDRESS +#define SPP0_REGS_CBUS_ADDRESS 0x00020000 +#define SPP0_DMEM_CBUS_ADDRESS 0x00028000 +#define SPP0_DMEM_DBUS_ADDRESS 0x02020000 +#define SPP1_REGS_CBUS_ADDRESS 0x00030000 +#define SPP1_DMEM_CBUS_ADDRESS 0x00038000 +#define SPP1_DMEM_DBUS_ADDRESS 0x02030000 +#define ISP0_REGS_CBUS_ADDRESS 0x001C0000 +#define ISP0_PMEM_CBUS_ADDRESS 0x001D0000 +#define ISP0_DMEM_CBUS_ADDRESS 0x001F0000 +#define ISP0_BAMEM_CBUS_ADDRESS 0x00200000 +#define ISP0_VMEM_CBUS_ADDRESS 0x00220000 +#define ISP1_REGS_CBUS_ADDRESS 0x00240000 +#define ISP1_PMEM_CBUS_ADDRESS 0x00250000 +#define ISP1_DMEM_CBUS_ADDRESS 0x00270000 +#define ISP1_BAMEM_CBUS_ADDRESS 0x00280000 +#define ISP1_VMEM_CBUS_ADDRESS 0x002A0000 +#define ISP2_REGS_CBUS_ADDRESS 0x002C0000 +#define ISP2_PMEM_CBUS_ADDRESS 0x002D0000 +#define ISP2_DMEM_CBUS_ADDRESS 0x002F0000 +#define ISP2_BAMEM_CBUS_ADDRESS 0x00300000 +#define ISP2_VMEM_CBUS_ADDRESS 0x00320000 +#define ISP3_REGS_CBUS_ADDRESS 0x00340000 +#define ISP3_PMEM_CBUS_ADDRESS 0x00350000 +#define ISP3_DMEM_CBUS_ADDRESS 0x00370000 +#define ISP3_BAMEM_CBUS_ADDRESS 0x00380000 +#define ISP3_VMEM_CBUS_ADDRESS 0x003A0000 +#define ISP0_PMEM_DBUS_ADDRESS 0x08000000 +#define ISP0_DMEM_DBUS_ADDRESS 0x08400000 +#define ISP0_BAMEM_DBUS_ADDRESS 0x09000000 +#define ISP0_VMEM_DBUS_ADDRESS 0x08800000 +#define ISP1_PMEM_DBUS_ADDRESS 0x0A000000 +#define ISP1_DMEM_DBUS_ADDRESS 0x0A400000 +#define ISP1_BAMEM_DBUS_ADDRESS 0x0B000000 +#define ISP1_VMEM_DBUS_ADDRESS 0x0A800000 +#define ISP2_PMEM_DBUS_ADDRESS 0x0C000000 +#define ISP2_DMEM_DBUS_ADDRESS 0x0C400000 +#define ISP2_BAMEM_DBUS_ADDRESS 0x0D000000 +#define ISP2_VMEM_DBUS_ADDRESS 0x0C800000 +#define ISP3_PMEM_DBUS_ADDRESS 0x0E000000 +#define ISP3_DMEM_DBUS_ADDRESS 0x0E400000 +#define ISP3_BAMEM_DBUS_ADDRESS 0x0F000000 +#define ISP3_VMEM_DBUS_ADDRESS 0x0E800000 +#endif /* _IPU_DEVICE_CELL_PROPERTIES_DEFS_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_impl.h new file mode 100644 index 000000000000..428a394e8136 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_cell_properties_impl.h @@ -0,0 +1,193 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_CELL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_CELL_PROPERTIES_IMPL_H + +#include "ipu_device_sp2600_control_properties_impl.h" +#include "ipu_device_sp2600_proxy_properties_impl.h" +#include "ipu_device_isp2600_properties_impl.h" +#include "ipu_device_cell_properties_defs.h" +#include "ipu_device_cell_devices.h" +#include "ipu_device_cell_type_properties.h"/* IPU_DEVICE_INVALID_MEM_ADDRESS */ + +static const unsigned int +ipu_device_spc0_mem_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + SPC0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp0_mem_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + SPP0_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP0_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp1_mem_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + SPP1_REGS_CBUS_ADDRESS, + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP1_DMEM_CBUS_ADDRESS +}; + +static const unsigned int +ipu_device_isp0_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP0_REGS_CBUS_ADDRESS, /* reg addr */ + ISP0_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP0_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP0_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP0_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp1_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP1_REGS_CBUS_ADDRESS, /* reg addr */ + ISP1_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP1_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP1_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP1_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp2_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP2_REGS_CBUS_ADDRESS, /* reg addr */ + ISP2_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP2_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP2_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP2_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_isp3_mem_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + ISP3_REGS_CBUS_ADDRESS, /* reg addr */ + ISP3_PMEM_CBUS_ADDRESS, /* pmem addr */ + ISP3_DMEM_CBUS_ADDRESS, /* dmem addr */ + ISP3_BAMEM_CBUS_ADDRESS,/* bamem addr */ + ISP3_VMEM_CBUS_ADDRESS /* vmem addr */ +}; + +static const unsigned int +ipu_device_spc0_mem_databus_address[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPC0_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp0_mem_databus_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP0_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_spp1_mem_databus_address[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no pmem */ + SPP1_DMEM_DBUS_ADDRESS +}; + +static const unsigned int +ipu_device_isp0_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP0_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP0_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP0_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP0_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp1_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP1_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP1_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP1_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP1_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp2_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP2_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP2_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP2_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP2_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const unsigned int +ipu_device_isp3_mem_databus_address[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + IPU_DEVICE_INVALID_MEM_ADDRESS, /* no reg addr */ + ISP3_PMEM_DBUS_ADDRESS, /* pmem databus addr */ + ISP3_DMEM_DBUS_ADDRESS, /* dmem databus addr */ + ISP3_BAMEM_DBUS_ADDRESS, /* bamem databus addr */ + ISP3_VMEM_DBUS_ADDRESS /* vmem databus addr */ +}; + +static const struct ipu_device_cell_properties_s +ipu_device_cell_properties[NUM_CELLS] = { + { + &ipu_device_sp2600_control_properties, + ipu_device_spc0_mem_address, + ipu_device_spc0_mem_databus_address + }, + { + &ipu_device_sp2600_proxy_properties, + ipu_device_spp0_mem_address, + ipu_device_spp0_mem_databus_address + }, + { + &ipu_device_sp2600_proxy_properties, + ipu_device_spp1_mem_address, + ipu_device_spp1_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp0_mem_address, + ipu_device_isp0_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp1_mem_address, + ipu_device_isp1_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp2_mem_address, + ipu_device_isp2_mem_databus_address + }, + { + &ipu_device_isp2600_properties, + ipu_device_isp3_mem_address, + ipu_device_isp3_mem_databus_address + } +}; + +#ifdef C_RUN + +/* Mapping between hrt_hive_processors enum and cell_id's used in FW */ +static const int ipu_device_map_cell_id_to_crun_proc_id[NUM_CELLS] = { + 4, /* SPC0 */ + 5, /* SPP0 */ + 6, /* SPP1 */ + 0, /* ISP0 */ + 1, /* ISP1 */ + 2, /* ISP2 */ + 3 /* ISP3 */ +}; + +#endif + +#endif /* __IPU_DEVICE_CELL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_ff_devices.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_ff_devices.h new file mode 100644 index 000000000000..d784fb47ffaa --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_ff_devices.h @@ -0,0 +1,57 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IPU_DEVICE_FF_DEVICES_H +#define __IPU_DEVICE_FF_DEVICES_H + +enum ipu_device_ff_id { + /* Names (shortened) as used in */ + /* PSA fixed functions */ /* ipu_device_ff_hrt.txt */ + IPU_DEVICE_FF_WBA_WBA = 0, /* WBA_WBA */ + IPU_DEVICE_FF_RYNR_SPLITTER, /* RYNR_RYNR_SPLITTER */ + IPU_DEVICE_FF_RYNR_COLLECTOR, /* RYNR_RYNR_COLLECTOR */ + IPU_DEVICE_FF_RYNR_BNLM, /* RYNR_BNLM */ + IPU_DEVICE_FF_RYNR_VCUD, /* RYNR_VCUD */ + IPU_DEVICE_FF_DEMOSAIC_DEMOSAIC,/* DEMOSAIC_DEMOSAIC */ + IPU_DEVICE_FF_ACM_CCM, /* VCA_VCR, name as used in CNLB0 HW */ + IPU_DEVICE_FF_ACM_ACM, /* VCA_ACM, name as used in CNLB0 HW */ + IPU_DEVICE_FF_VCA_VCR2, /* VCA_VCR, part of ACM */ + IPU_DEVICE_FF_GTC_CSC_CDS, /* GTC_CSC_CDS */ + IPU_DEVICE_FF_GTC_GTM, /* GTC_GTM */ + IPU_DEVICE_FF_YUV1_SPLITTER, /* YUV1_Processing_YUV_SPLITTER */ + IPU_DEVICE_FF_YUV1_IEFD, /* YUV1_Processing_IEFD*/ + IPU_DEVICE_FF_YUV1_YDS, /* YUV1_Processing_YDS */ + IPU_DEVICE_FF_YUV1_TCC, /* YUV1_Processing_TCC */ + IPU_DEVICE_FF_DVS_YBIN, /* DVS_YBIN */ + IPU_DEVICE_FF_DVS_DVS, /* DVS_DVS */ + IPU_DEVICE_FF_LACE_LACE, /* Lace_Stat_LACE_STAT */ + /* ISA fixed functions */ + IPU_DEVICE_FF_ICA_INL, /* Input_Corr_INL */ + IPU_DEVICE_FF_ICA_GBL, /* Input_Corr_GBL */ + IPU_DEVICE_FF_ICA_PCLN, /* Input_Corr_PCLN */ + IPU_DEVICE_FF_LSC_LSC, /* Bayer_Lsc_LSC */ + IPU_DEVICE_FF_DPC_DPC, /* Bayer_Dpc_GDDPC */ + IPU_DEVICE_FF_IDS_SCALER, /* Bayer_Scaler_SCALER */ + IPU_DEVICE_FF_AWB_AWRG, /* Stat_AWB_AWRG */ + IPU_DEVICE_FF_AF_AF, /* Stat_AF_AWB_FR_AF_AWB_FR_GRD */ + IPU_DEVICE_FF_AE_WGHT_HIST, /* Stat_AE_WGHT_HIST */ + IPU_DEVICE_FF_AE_CCM, /* Stat_AE_AE_CCM */ + IPU_DEVICE_FF_NUM_FF +}; + +#define IPU_DEVICE_FF_NUM_PSA_FF (IPU_DEVICE_FF_LACE_LACE + 1) +#define IPU_DEVICE_FF_NUM_ISA_FF \ + (IPU_DEVICE_FF_NUM_FF - IPU_DEVICE_FF_NUM_PSA_FF) + +#endif /* __IPU_DEVICE_FF_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_gp_devices.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_gp_devices.h new file mode 100644 index 000000000000..ab8cd6a783ce --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/psys/cnlB0/ipu_device_gp_devices.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_GP_DEVICES_H +#define __IPU_DEVICE_GP_DEVICES_H +#include "math_support.h" +#include "type_support.h" + +enum ipu_device_gp_id { + IPU_DEVICE_GP_PSA = 0, /* PSA */ + IPU_DEVICE_GP_ISA_STATIC, /* ISA Static */ + IPU_DEVICE_GP_ISA_RUNTIME, /* ISA Runtime */ + IPU_DEVICE_GP_ISL, /* ISL */ + IPU_DEVICE_GP_NUM_GP +}; + +enum ipu_device_gp_psa_mux_id { + /* Post RYNR/CCN: 0-To ACM (Video), 1-To Demosaic (Stills)*/ + IPU_DEVICE_GP_PSA_STILLS_MODE_MUX = 0, + /* Post Vec2Str 4: 0-To GTC, 1-To ACM */ + IPU_DEVICE_GP_PSA_V2S_RGB_4_DEMUX, + /* Post DM and pre ACM 0-CCM/ACM: 1-DM Componenet Splitter */ + IPU_DEVICE_GP_PSA_S2V_RGB_F_MUX, + /* Pre ACM/CCM: 0-To CCM/ACM, 1-To str2vec id_f */ + IPU_DEVICE_GP_PSA_ACM_DEMUX, + IPU_DEVICE_GP_PSA_MUX_NUM_MUX +}; + +enum ipu_device_gp_isa_static_mux_id { + IPU_DEVICE_GP_ISA_STATIC_MUX_SEL = 0, + IPU_DEVICE_GP_ISA_STATIC_PORTA_BLK, + IPU_DEVICE_GP_ISA_STATIC_PORTB_BLK, + IPU_DEVICE_GP_ISA_STATIC_PORTC_BLK, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_SEL, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_INPUT_CORR_PORT_BLK, + IPU_DEVICE_GP_ISA_STATIC_AWB_MUX_DPC_PORT_BLK, + IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX +}; + +enum ipu_device_gp_isa_runtime_mux_id { + IPU_DEVICE_GP_ISA_RUNTIME_FRAME_SIZE = 0, + IPU_DEVICE_GP_ISA_RUNTIME_SCALED_FRAME_SIZE, + IPU_DEVICE_GP_ISA_RUNTIME_MUX_NUM_MUX +}; + +enum ipu_device_gp_isl_mux_id { + IPU_DEVICE_GP_ISL_MIPI_BE_MUX = 0, + IPU_DEVICE_GP_ISL_MUX_NUM_MUX +}; + +#define IPU_DEVICE_GP_MAX_NUM MAX4((uint32_t)IPU_DEVICE_GP_PSA_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISA_RUNTIME_MUX_NUM_MUX, \ + (uint32_t)IPU_DEVICE_GP_ISL_MUX_NUM_MUX) + +#endif /* __IPU_DEVICE_GP_DEVICES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h new file mode 100644 index 000000000000..de733be67998 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_isp2600_properties_impl.h @@ -0,0 +1,151 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H +#define __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H + +/* isp2600 definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_isp2600_registers { + /* control registers */ + IPU_DEVICE_ISP2600_STAT_CTRL = 0x0, + IPU_DEVICE_ISP2600_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_ISP2600_ICACHE_BASE = 0x10, + IPU_DEVICE_ISP2600_ICACHE_INFO = 0x14, + IPU_DEVICE_ISP2600_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_ISP2600_QMEM_BASE = 0x1C, + + IPU_DEVICE_ISP2600_CMEM_BASE = 0x28, + + IPU_DEVICE_ISP2600_XMEM_BASE = 0x88, + IPU_DEVICE_ISP2600_XMEM_INFO = 0x8C, + IPU_DEVICE_ISP2600_XMEM_INFO_OVERRIDE = 0x90, + + IPU_DEVICE_ISP2600_XVMEM_BASE = 0xB8, + + /* debug registers */ + IPU_DEVICE_ISP2600_DEBUG_PC = 0x130, + IPU_DEVICE_ISP2600_STALL = 0x134 +}; + + +enum ipu_device_isp2600_memories { + IPU_DEVICE_ISP2600_REGS, + IPU_DEVICE_ISP2600_PMEM, + IPU_DEVICE_ISP2600_DMEM, + IPU_DEVICE_ISP2600_BAMEM, + IPU_DEVICE_ISP2600_VMEM, + IPU_DEVICE_ISP2600_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_isp2600_mem_size[IPU_DEVICE_ISP2600_NUM_MEMORIES] = { + 0x00140, + 0x14000, + 0x04000, + 0x20000, + 0x20000 +}; + + +enum ipu_device_isp2600_masters { + IPU_DEVICE_ISP2600_ICACHE, + IPU_DEVICE_ISP2600_QMEM, + IPU_DEVICE_ISP2600_CMEM, + IPU_DEVICE_ISP2600_XMEM, + IPU_DEVICE_ISP2600_XVMEM, + IPU_DEVICE_ISP2600_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_isp2600_masters[IPU_DEVICE_ISP2600_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_ISP2600_ICACHE_BASE, + IPU_DEVICE_ISP2600_ICACHE_INFO, + IPU_DEVICE_ISP2600_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_ISP2600_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 3, + 0xC, + IPU_DEVICE_ISP2600_CMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_ISP2600_XMEM_BASE, + IPU_DEVICE_ISP2600_XMEM_INFO, + IPU_DEVICE_ISP2600_XMEM_INFO_OVERRIDE + }, + { + 3, + 0xC, + IPU_DEVICE_ISP2600_XVMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + } +}; + +enum ipu_device_isp2600_stall_bits { + IPU_DEVICE_ISP2600_STALL_ICACHE0, + IPU_DEVICE_ISP2600_STALL_ICACHE1, + IPU_DEVICE_ISP2600_STALL_DMEM, + IPU_DEVICE_ISP2600_STALL_QMEM, + IPU_DEVICE_ISP2600_STALL_CMEM, + IPU_DEVICE_ISP2600_STALL_XMEM, + IPU_DEVICE_ISP2600_STALL_BAMEM, + IPU_DEVICE_ISP2600_STALL_VMEM, + IPU_DEVICE_ISP2600_STALL_XVMEM, + IPU_DEVICE_ISP2600_NUM_STALL_BITS +}; + +#define IPU_DEVICE_ISP2600_ICACHE_WORD_SIZE 64 /* 512 bits per instruction */ +#define IPU_DEVICE_ISP2600_ICACHE_BURST_SIZE 8 /* 8 instructions per burst */ + +static const struct ipu_device_cell_count_s ipu_device_isp2600_count = { + IPU_DEVICE_ISP2600_NUM_MEMORIES, + IPU_DEVICE_ISP2600_NUM_MASTERS, + IPU_DEVICE_ISP2600_NUM_STALL_BITS, + IPU_DEVICE_ISP2600_ICACHE_WORD_SIZE * + IPU_DEVICE_ISP2600_ICACHE_BURST_SIZE +}; + +static const unsigned int ipu_device_isp2600_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x130, 0x134 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_isp2600_properties = { + &ipu_device_isp2600_count, + ipu_device_isp2600_masters, + ipu_device_isp2600_reg_offset, + ipu_device_isp2600_mem_size +}; + +#endif /* __IPU_DEVICE_ISP2600_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h new file mode 100644 index 000000000000..430295cd9d94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_control_properties_impl.h @@ -0,0 +1,136 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H + +/* sp2600_control definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_control_registers { + /* control registers */ + IPU_DEVICE_SP2600_CONTROL_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_CONTROL_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_CONTROL_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_CONTROL_STALL = 0xA0 +}; + +enum ipu_device_sp2600_control_mems { + IPU_DEVICE_SP2600_CONTROL_REGS, + IPU_DEVICE_SP2600_CONTROL_PMEM, + IPU_DEVICE_SP2600_CONTROL_DMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_control_mem_size[IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES] = { + 0x000AC, + 0x00000, + 0x10000 +}; + +enum ipu_device_sp2600_control_masters { + IPU_DEVICE_SP2600_CONTROL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_QMEM, + IPU_DEVICE_SP2600_CONTROL_CMEM, + IPU_DEVICE_SP2600_CONTROL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_control_masters[IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_ICACHE_BASE, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO, + IPU_DEVICE_SP2600_CONTROL_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_CONTROL_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_CMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_CONTROL_XMEM_BASE, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO, + IPU_DEVICE_SP2600_CONTROL_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_control_stall_bits { + IPU_DEVICE_SP2600_CONTROL_STALL_ICACHE, + IPU_DEVICE_SP2600_CONTROL_STALL_DMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_QMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_CMEM, + IPU_DEVICE_SP2600_CONTROL_STALL_XMEM, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_control_count = { + IPU_DEVICE_SP2600_CONTROL_NUM_MEMORIES, + IPU_DEVICE_SP2600_CONTROL_NUM_MASTERS, + IPU_DEVICE_SP2600_CONTROL_NUM_STALL_BITS, + IPU_DEVICE_SP2600_CONTROL_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_CONTROL_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_control_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_control_properties = { + &ipu_device_sp2600_control_count, + ipu_device_sp2600_control_masters, + ipu_device_sp2600_control_reg_offset, + ipu_device_sp2600_control_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_CONTROL_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h new file mode 100644 index 000000000000..b3f120f9fea8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_fp_properties_impl.h @@ -0,0 +1,140 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H + +/* sp2600_fp definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_fp_registers { + /* control registers */ + IPU_DEVICE_SP2600_FP_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_FP_START_PC = 0x4, + + /* master port registers */ + IPU_DEVICE_SP2600_FP_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_FP_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_FP_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_FP_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_FP_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_FP_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_FP_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_FP_XMEM_BASE = 0x88, + IPU_DEVICE_SP2600_FP_XMEM_INFO = 0x8C, + IPU_DEVICE_SP2600_FP_XMEM_INFO_OVERRIDE = 0x90, + + /* debug registers */ + IPU_DEVICE_SP2600_FP_DEBUG_PC = 0xCC, + IPU_DEVICE_SP2600_FP_STALL = 0xD0 +}; + + +enum ipu_device_sp2600_fp_memories { + IPU_DEVICE_SP2600_FP_REGS, + IPU_DEVICE_SP2600_FP_PMEM, + IPU_DEVICE_SP2600_FP_DMEM, + IPU_DEVICE_SP2600_FP_DMEM1, + IPU_DEVICE_SP2600_FP_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_fp_mem_size[IPU_DEVICE_SP2600_FP_NUM_MEMORIES] = { + 0x000DC, + 0x00000, + 0x10000, + 0x08000 +}; + +enum ipu_device_sp2600_fp_masters { + IPU_DEVICE_SP2600_FP_ICACHE, + IPU_DEVICE_SP2600_FP_QMEM, + IPU_DEVICE_SP2600_FP_CMEM, + IPU_DEVICE_SP2600_FP_XMEM, + IPU_DEVICE_SP2600_FP_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_fp_masters[IPU_DEVICE_SP2600_FP_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_FP_ICACHE_BASE, + IPU_DEVICE_SP2600_FP_ICACHE_INFO, + IPU_DEVICE_SP2600_FP_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_FP_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 3, + 0xC, + IPU_DEVICE_SP2600_FP_CMEM_BASE, + IPU_DEVICE_SP2600_FP_CMEM_INFO, + IPU_DEVICE_SP2600_FP_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_FP_XMEM_BASE, + IPU_DEVICE_SP2600_FP_XMEM_INFO, + IPU_DEVICE_SP2600_FP_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_fp_stall_bits { + IPU_DEVICE_SP2600_FP_STALL_ICACHE, + IPU_DEVICE_SP2600_FP_STALL_DMEM, + IPU_DEVICE_SP2600_FP_STALL_QMEM, + IPU_DEVICE_SP2600_FP_STALL_CMEM, + IPU_DEVICE_SP2600_FP_STALL_XMEM, + IPU_DEVICE_SP2600_FP_STALL_DMEM1, + IPU_DEVICE_SP2600_FP_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_FP_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_FP_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_fp_count = { + IPU_DEVICE_SP2600_FP_NUM_MEMORIES, + IPU_DEVICE_SP2600_FP_NUM_MASTERS, + IPU_DEVICE_SP2600_FP_NUM_STALL_BITS, + IPU_DEVICE_SP2600_FP_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_FP_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_fp_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0x9C, 0xA0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_fp_properties = { + &ipu_device_sp2600_fp_count, + ipu_device_sp2600_fp_masters, + ipu_device_sp2600_fp_reg_offset, + ipu_device_sp2600_fp_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_FP_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h new file mode 100644 index 000000000000..6fdcd7faea9b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/devices/src/ipu_device_sp2600_proxy_properties_impl.h @@ -0,0 +1,138 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H +#define __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H + +/* sp2600_proxy definition */ + +#include "ipu_device_cell_properties_struct.h" + +enum ipu_device_sp2600_proxy_registers { + /* control registers */ + IPU_DEVICE_SP2600_PROXY_STAT_CTRL = 0x0, + IPU_DEVICE_SP2600_PROXY_START_PC = 0x4, + + /* THESE ADDRESSES NEED TO BE CHECKED !!!! */ + /* master port registers */ + IPU_DEVICE_SP2600_PROXY_ICACHE_BASE = 0x10, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO = 0x14, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO_OVERRIDE = 0x18, + + IPU_DEVICE_SP2600_PROXY_QMEM_BASE = 0x1C, + + IPU_DEVICE_SP2600_PROXY_CMEM_BASE = 0x28, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO = 0x2C, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO_OVERRIDE = 0x30, + + IPU_DEVICE_SP2600_PROXY_XMEM_BASE = 0x58, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO = 0x5C, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO_OVERRIDE = 0x60, + + /* debug registers */ + IPU_DEVICE_SP2600_PROXY_DEBUG_PC = 0x9C, + IPU_DEVICE_SP2600_PROXY_STALL = 0xA0 +}; + + +enum ipu_device_sp2600_proxy_memories { + IPU_DEVICE_SP2600_PROXY_REGS, + IPU_DEVICE_SP2600_PROXY_PMEM, + IPU_DEVICE_SP2600_PROXY_DMEM, + IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES +}; + +static const unsigned int +ipu_device_sp2600_proxy_mem_size[IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES] = { + 0x00AC, + 0x0000, + 0x4000 +}; + +enum ipu_device_sp2600_proxy_masters { + IPU_DEVICE_SP2600_PROXY_ICACHE, + IPU_DEVICE_SP2600_PROXY_QMEM, + IPU_DEVICE_SP2600_PROXY_CMEM, + IPU_DEVICE_SP2600_PROXY_XMEM, + IPU_DEVICE_SP2600_PROXY_NUM_MASTERS +}; + +static const struct ipu_device_cell_master_properties_s +ipu_device_sp2600_proxy_masters[IPU_DEVICE_SP2600_PROXY_NUM_MASTERS] = { + { + 0, + 0xC, + IPU_DEVICE_SP2600_PROXY_ICACHE_BASE, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO, + IPU_DEVICE_SP2600_PROXY_ICACHE_INFO_OVERRIDE + }, + { + 0, + 0xC, + IPU_DEVICE_SP2600_PROXY_QMEM_BASE, + 0xFFFFFFFF, + 0xFFFFFFFF + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_PROXY_CMEM_BASE, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO, + IPU_DEVICE_SP2600_PROXY_CMEM_INFO_OVERRIDE + }, + { + 2, + 0xC, + IPU_DEVICE_SP2600_PROXY_XMEM_BASE, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO, + IPU_DEVICE_SP2600_PROXY_XMEM_INFO_OVERRIDE + } +}; + +enum ipu_device_sp2600_proxy_stall_bits { + IPU_DEVICE_SP2600_PROXY_STALL_ICACHE, + IPU_DEVICE_SP2600_PROXY_STALL_DMEM, + IPU_DEVICE_SP2600_PROXY_STALL_QMEM, + IPU_DEVICE_SP2600_PROXY_STALL_CMEM, + IPU_DEVICE_SP2600_PROXY_STALL_XMEM, + IPU_DEVICE_SP2600_PROXY_NUM_STALL_BITS +}; + +/* 32 bits per instruction */ +#define IPU_DEVICE_SP2600_PROXY_ICACHE_WORD_SIZE 4 +/* 32 instructions per burst */ +#define IPU_DEVICE_SP2600_PROXY_ICACHE_BURST_SIZE 32 + +static const struct ipu_device_cell_count_s ipu_device_sp2600_proxy_count = { + IPU_DEVICE_SP2600_PROXY_NUM_MEMORIES, + IPU_DEVICE_SP2600_PROXY_NUM_MASTERS, + IPU_DEVICE_SP2600_PROXY_NUM_STALL_BITS, + IPU_DEVICE_SP2600_PROXY_ICACHE_WORD_SIZE * + IPU_DEVICE_SP2600_PROXY_ICACHE_BURST_SIZE +}; + +static const unsigned int +ipu_device_sp2600_proxy_reg_offset[/* CELL_NUM_REGS */] = { + 0x0, 0x4, 0x10, 0xCC, 0xD0 +}; + +static const struct ipu_device_cell_type_properties_s +ipu_device_sp2600_proxy_properties = { + &ipu_device_sp2600_proxy_count, + ipu_device_sp2600_proxy_masters, + ipu_device_sp2600_proxy_reg_offset, + ipu_device_sp2600_proxy_mem_size +}; + +#endif /* __IPU_DEVICE_SP2600_PROXY_PROPERTIES_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk new file mode 100644 index 000000000000..b1ffbf7ea21f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/fw_abi_cpu_types.mk @@ -0,0 +1,24 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# + +# MODULE is FW ABI COMMON TYPES + +FW_ABI_COMMON_TYPES_DIRS = -I$${MODULES_DIR}/fw_abi_common_types +FW_ABI_COMMON_TYPES_DIRS += -I$${MODULES_DIR}/fw_abi_common_types/cpu + +FW_ABI_COMMON_TYPES_HOST_FILES = +FW_ABI_COMMON_TYPES_HOST_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) + +FW_ABI_COMMON_TYPES_FW_FILES = +FW_ABI_COMMON_TYPES_FW_CPPFLAGS = $(FW_ABI_COMMON_TYPES_DIRS) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h new file mode 100644 index 000000000000..21cc3f43f485 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_BASE_TYPES_H +#define __IA_CSS_TERMINAL_BASE_TYPES_H + + +#include "type_support.h" +#include "ia_css_terminal_defs.h" + +#define N_UINT16_IN_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_TERMINAL_STRUCT 5 + +#define SIZE_OF_TERMINAL_STRUCT_BITS \ + (IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + N_UINT16_IN_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* ==================== Base Terminal - START ==================== */ +struct ia_css_terminal_s { /**< Base terminal */ + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the process group */ + uint16_t size; /**< Size of this whole terminal layout-structure */ + uint16_t tm_index; /**< Index of the terminal manifest object */ + ia_css_terminal_ID_t ID; /**< Absolute referal ID for this terminal, valid ID's != 0 */ + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_STRUCT]; +}; +/* ==================== Base Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h new file mode 100644 index 000000000000..056e1b6d5d4b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/cpu/ia_css_terminal_manifest_base_types.h @@ -0,0 +1,42 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H + +#include "ia_css_terminal_defs.h" + +#define N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT 5 +#define SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_UINT16_T_BITS \ + + IA_CSS_TERMINAL_ID_BITS \ + + IA_CSS_TERMINAL_TYPE_BITS \ + + IA_CSS_UINT32_T_BITS \ + + (N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT*IA_CSS_UINT8_T_BITS)) + +/* ==================== Base Terminal Manifest - START ==================== */ +struct ia_css_terminal_manifest_s { + ia_css_terminal_type_t terminal_type; /**< Type ia_css_terminal_type_t */ + int16_t parent_offset; /**< Offset to the program group manifest */ + uint16_t size; /**< Size of this whole terminal-manifest layout-structure */ + ia_css_terminal_ID_t ID; + uint8_t padding[N_PADDING_UINT8_IN_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_terminal_manifest_s + ia_css_terminal_manifest_t; + +/* ==================== Base Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h new file mode 100644 index 000000000000..3b80a17a6ad3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_base_types.h @@ -0,0 +1,38 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BASE_TYPES_H +#define __IA_CSS_BASE_TYPES_H + +#include "type_support.h" + +#define VIED_VADDRESS_BITS 32 +typedef uint32_t vied_vaddress_t; + +#define DEVICE_DESCRIPTOR_ID_BITS 32 +typedef struct { + uint8_t device_id; + uint8_t instance_id; + uint8_t channel_id; + uint8_t section_id; +} device_descriptor_fields_t; + +typedef union { + device_descriptor_fields_t fields; + uint32_t data; +} device_descriptor_id_t; + +typedef uint16_t ia_css_process_id_t; + +#endif /* __IA_CSS_BASE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h new file mode 100644 index 000000000000..dbf1cf93756f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_abi_common_types/ia_css_terminal_defs.h @@ -0,0 +1,105 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_DEFS_H +#define __IA_CSS_TERMINAL_DEFS_H + + +#include "type_support.h" + +#define IA_CSS_TERMINAL_ID_BITS 8 +typedef uint8_t ia_css_terminal_ID_t; +#define IA_CSS_TERMINAL_INVALID_ID ((ia_css_terminal_ID_t)(-1)) + +/* + * Terminal Base Type + */ +typedef enum ia_css_terminal_type { + /**< Data input */ + IA_CSS_TERMINAL_TYPE_DATA_IN = 0, + /**< Data output */ + IA_CSS_TERMINAL_TYPE_DATA_OUT, + /**< Type 6 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_STREAM, + /**< Type 1-5 parameter input */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN, + /**< Type 1-5 parameter output */ + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN, + /**< Represent the new type of terminal for the + * "spatial dependent parameters", when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + /**< Represent the new type of terminal for the + * explicit slicing, when params go in + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN, + /**< Represent the new type of terminal for the + * explicit slicing, when params go out + */ + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT, + /**< State (private data) input */ + IA_CSS_TERMINAL_TYPE_STATE_IN, + /**< State (private data) output */ + IA_CSS_TERMINAL_TYPE_STATE_OUT, + IA_CSS_TERMINAL_TYPE_PROGRAM, + IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT, + IA_CSS_N_TERMINAL_TYPES +} ia_css_terminal_type_t; + +#define IA_CSS_TERMINAL_TYPE_BITS 32 + +/* Temporary redirection needed to facilicate merging with the drivers + in a backwards compatible manner */ +#define IA_CSS_TERMINAL_TYPE_PARAM_CACHED IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN + +/* + * Dimensions of the data objects. Note that a C-style + * data order is assumed. Data stored by row. + */ +typedef enum ia_css_dimension { + /**< The number of columns, i.e. the size of the row */ + IA_CSS_COL_DIMENSION = 0, + /**< The number of rows, i.e. the size of the column */ + IA_CSS_ROW_DIMENSION = 1, + IA_CSS_N_DATA_DIMENSION = 2 +} ia_css_dimension_t; + +#define IA_CSS_N_COMMAND_COUNT (4) + +#ifndef PIPE_GENERATION +/* Don't include these complex enum structures in Genpipe, it can't handle and it does not need them */ +/* + * enum ia_css_isys_link_id. Lists the link IDs used by the FW for On The Fly feature + */ +typedef enum ia_css_isys_link_id { + IA_CSS_ISYS_LINK_OFFLINE = 0, + IA_CSS_ISYS_LINK_MAIN_OUTPUT = 1, + IA_CSS_ISYS_LINK_PDAF_OUTPUT = 2 +} ia_css_isys_link_id_t; +#define N_IA_CSS_ISYS_LINK_ID (IA_CSS_ISYS_LINK_PDAF_OUTPUT + 1) + +/* + * enum ia_css_data_barrier_link_id. Lists the link IDs used by the FW for data barrier feature + */ +typedef enum ia_css_data_barrier_link_id { + IA_CSS_DATA_BARRIER_LINK_MEMORY = N_IA_CSS_ISYS_LINK_ID, + N_IA_CSS_DATA_BARRIER_LINK_ID +} ia_css_data_barrier_link_id_t; + +#endif /* #ifndef PIPE_GENERATION */ +#endif /* __IA_CSS_TERMINAL_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/fw_load.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/fw_load.mk new file mode 100644 index 000000000000..0af62100cba8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/fw_load.mk @@ -0,0 +1,59 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is FW_LOAD + +# select implementation for fw_load +ifeq ($(FW_LOAD_DMA), 1) +FW_LOAD_IMPL = fwdma +else +FW_LOAD_IMPL = xmem +endif + +FW_LOAD_FW_CPPFLAGS = + +# select DMA instance for fw_load +ifeq ($(FW_LOAD_DMA_INSTANCE),) +$(error FW_LOAD_DMA_INSTANCE not specified) +else +ifeq ($(FW_LOAD_DMA_INSTANCE), NCI_DMA_EXT0) +FW_LOAD_FW_CPPFLAGS += -DFW_LOAD_INSTANCE_USE_DMA_EXT0 +else +ifeq ($(FW_LOAD_DMA_INSTANCE), NCI_DMA_FW) +FW_LOAD_FW_CPPFLAGS += -DFW_LOAD_INSTANCE_USE_DMA_FW +else +$(error FW_LOAD_DMA_INSTANCE $(FW_LOAD_DMA_INSTANCE) not supported) +endif +endif +endif + +FW_LOAD_DIR = $${MODULES_DIR}/fw_load +FW_LOAD_INTERFACE = $(FW_LOAD_DIR)/interface +FW_LOAD_SOURCES = $(FW_LOAD_DIR)/src/$(FW_LOAD_IMPL) + +# XMEM/FWDMA supports on SP side +FW_LOAD_FW_FILES = $(FW_LOAD_SOURCES)/ia_css_fw_load.c +FW_LOAD_FW_CPPFLAGS += -I$(FW_LOAD_INTERFACE) \ + -I$(FW_LOAD_SOURCES) \ + -I$(FW_LOAD_DIR)/src + +# Only XMEM supports on Host side +FW_LOAD_HOST_FILES = $(FW_LOAD_DIR)/src/xmem/ia_css_fw_load.c +FW_LOAD_HOST_CPPFLAGS = -I$(FW_LOAD_INTERFACE) \ + -I$(FW_LOAD_DIR)/src/xmem \ + -I$(FW_LOAD_DIR)/src + +ifdef FW_LOAD_NO_OF_REQUEST_OFFSET +FW_LOAD_FW_CPPFLAGS += -DFW_LOAD_NO_OF_REQUEST_ADDRESS=$(FW_LOAD_NO_OF_REQUEST_OFFSET) +endif # FW_LOAD_NO_OF_REQUEST_OFFSET diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load.h new file mode 100644 index 000000000000..d1f7926f39c6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load.h @@ -0,0 +1,155 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_H +#define __IA_CSS_FW_LOAD_H + +#include "ia_css_fw_load_storage_class.h" +#include "ia_css_xmem.h" +#include "ia_css_cmem.h" + +enum ia_css_fw_load_mode { + IA_CSS_DBUS_ADDRESS = 0, + IA_CSS_CBUS_ADDRESS +}; + +/* Perform Initialization for fwload + Client must call init before it calls any other API + */ + +IA_CSS_FW_LOAD_STORAGE_CLASS_H void +ia_css_fw_load_init(void); + +/* This motifies the use what address has to be passed into the 'dst' parameter + * of the ia_css_fw_copy function and the ia_css_fw_zero function. + * When this function returns IA_CSS_DBUS_ADDRESS, the user must pass a data-bus + * address, when the function returns IA_CSS_CBUS_ADDRESS, the user must pass a + * control-bus address. + * XMEM implementation will require control-bus address while, + * DMA implementation will require data-bus addresses. +*/ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_load_get_mode(void); + +/***************** FW LOAD BLOCKING FUNCTIONS *******************************/ +/* NOTE : User cannot use blocking functions immidiate after calling any + * non-blocking request functions. User must finish all the load request before + * it calls any blocking function. + * e.g. Following is the invalid use case. + * - ia_css_fw_load_copy_begin (non-blocking) then without ending this request, + * it calls ia_css_fw_load_copy (blocking). Client should not do this. + * But before calling ia_css_fw_load_copy, it shouold finish all request by + * calling ia_css_fw_end(). + */ + +/* Perform a single data transfer from DDR/IMR (src) to local variable(dst). + All arguments are multiples of 4. + The function returns when the transfer has completed. + The function may block. + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H void +ia_css_fw_load( + unsigned int mmid, + ia_css_xmem_address_t src, + void *dst, + unsigned int size +); + +/* Perform a single data transfer from DDR/IMR (src) to the subsystem (dst). + All arguments are multiples of 4. + The function returns when the transfer has completed. + The function may block. + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H void +ia_css_fw_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size +); + +/* Perform zeroing the memory in subsystem (dst) + The function returns when all transfers have completed. + The function may block. + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H void +ia_css_fw_zero( + unsigned int ssid, + ia_css_cmem_address_t dst, + unsigned int size); + +/***************** FW LOAD NON_BLOCKING FUNCTIONS ****************************/ + +/* Perform a single data transfer from DDR/IMR (src) to local variable(dst). + All arguments are multiples of 4. + The function returns when the transfer has completed. + The function will not block. + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_load_begin( + unsigned int mmid, + ia_css_xmem_address_t src, + void *dst, + unsigned int size +); + +/* START OF TRANSFER / SUBMIT */ +/* Start a single data transfer from DDR/IMR (src) to the subsystem (dst). + The function returns 1 when the transfer has been issued successfully. + When the transfer cannot be issued, the function returns 0. + The function will not block. + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_copy_begin( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size +); + +/* Perform zeroing the subsystem (dst) memory + This function will not block + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_zero_begin( + unsigned int ssid, + ia_css_cmem_address_t dst, + unsigned int size); + +/* END OF TRANSFER / ACKNOWLEDGES */ +/* Complete at most n transfers, + returns the number of transfers that could be completed + */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_end(unsigned int n); + +/* OPTIONALLY USED FUNCTIONS */ +/* Return the number of transactions that may be submitted without blocking */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_copy_begin_available(void); + +/* Return the number of transactions may be ended */ +IA_CSS_FW_LOAD_STORAGE_CLASS_H unsigned int +ia_css_fw_copy_end_available(void); + +#ifdef __INLINE_IA_CSS_FW_LOAD__ +#include "ia_css_fw_load_blocking_impl.h" +#include "ia_css_fw_load_non_blocking_impl.h" +#include "ia_css_fw_load_impl.h" +#endif + + +#endif /* __IA_CSS_FW_LOAD_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load_storage_class.h new file mode 100644 index 000000000000..10ad61f89ea9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/interface/ia_css_fw_load_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_STORAGE_CLASS_H +#define __IA_CSS_FW_LOAD_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __INLINE_IA_CSS_FW_LOAD__ +#define IA_CSS_FW_LOAD_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_FW_LOAD_STORAGE_CLASS_C +#else +#define IA_CSS_FW_LOAD_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_FW_LOAD_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_FW_LOAD_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load.c new file mode 100644 index 000000000000..5930a6b1e8d2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load.c @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/* C file with (optionally) inlined files */ + +/* Global variable for tracking the number of fw_load transactions */ +/* Needed in host side implementaion */ +#ifndef __VIED_CELL +unsigned int started; +#endif + +#ifdef __INLINE_IA_CSS_FW_LOAD__ +static inline int __avoid_warning_on_empty_file(void) { return 0; } +#else +#include "ia_css_fw_load_blocking_impl.h" +#include "ia_css_fw_load_non_blocking_impl.h" +#include "ia_css_fw_load_impl.h" +#endif /* __INLINE_IA_CSS_FW_LOAD__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_blocking_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_blocking_impl.h new file mode 100644 index 000000000000..02ad9c36156e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_blocking_impl.h @@ -0,0 +1,54 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_BLOCKING_IMPL_H +#define __IA_CSS_FW_LOAD_BLOCKING_IMPL_H + +#include "ia_css_fw_load.h" +#include "ia_css_fw_load_storage_class.h" +#include "ia_css_xmem_cmem.h" +#include "ia_css_xmem.h" +#include "ia_css_cmem.h" + +IA_CSS_FW_LOAD_STORAGE_CLASS_C void +ia_css_fw_load( + unsigned int mmid, + ia_css_xmem_address_t src, + void *dst, + unsigned int size) +{ + ia_css_xmem_load(mmid, src, dst, size); +} + +IA_CSS_FW_LOAD_STORAGE_CLASS_C void +ia_css_fw_copy( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + ia_css_xmem_to_cmem_copy(mmid, ssid, src, dst, size); +} + +IA_CSS_FW_LOAD_STORAGE_CLASS_C void +ia_css_fw_zero( + unsigned int ssid, + ia_css_cmem_address_t dst, + unsigned int size) +{ + ia_css_cmem_zero(ssid, dst, size); +} + +#endif /* __IA_CSS_FW_LOAD_BLOCKING_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_impl.h new file mode 100644 index 000000000000..a9b6db8a5f55 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_impl.h @@ -0,0 +1,26 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_IMPL_H +#define __IA_CSS_FW_LOAD_IMPL_H + +#include "ia_css_fw_load.h" + +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_load_get_mode(void) +{ + return IA_CSS_CBUS_ADDRESS; +} + +#endif /* __IA_CSS_FW_LOAD_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_host_state.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_host_state.h new file mode 100644 index 000000000000..1691e4522f78 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_host_state.h @@ -0,0 +1,21 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_NON_BLOCKING_HOST_STATE_H +#define __IA_CSS_FW_LOAD_NON_BLOCKING_HOST_STATE_H +/* Global variable for tracking the number of fw_load transactions */ +/* Used in xmem non blocking host side implementaion */ +extern unsigned int started; + +#endif /* __IA_CSS_FW_LOAD_NON_BLOCKING_HOST_STATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl.h new file mode 100644 index 000000000000..c8949aa49370 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl.h @@ -0,0 +1,125 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_H +#define __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_H + +#include "type_support.h" +#include "ia_css_fw_load.h" +#include "ia_css_fw_load_storage_class.h" +#include "math_support.h" +#include "error_support.h" + +#ifdef __VIED_CELL +#include "ia_css_fw_load_non_blocking_impl_sp.h" +#else +#include "ia_css_fw_load_non_blocking_impl_host.h" +#endif + +#define FW_LOAD_MAX_NB_TRANS UINT_MAX +#define FW_LOAD_XMEM_MAX_TRANSACTION_SUPPORT \ + umin(FW_LOAD_MAX_NB_TRANS, FW_LOAD_MAX_TRANS_SUPPORTED) + + +IA_CSS_FW_LOAD_STORAGE_CLASS_C void +ia_css_fw_load_init(void) +{ + fw_load_transaction_init(); +} + +/* START OF TRANSFER */ +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_load_begin( + unsigned int mmid, + ia_css_xmem_address_t src, + void *dst, + unsigned int size +) +{ + if (!ia_css_fw_copy_begin_available()) + return 0; + ia_css_fw_load(mmid, src, dst, size); + fw_load_transaction_add(); + return size; +} + +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_copy_begin( + unsigned int mmid, + unsigned int ssid, + ia_css_xmem_address_t src, + ia_css_cmem_address_t dst, + unsigned int size) +{ + /* Check if there is space to hold the ack event in the queue */ + if (!ia_css_fw_copy_begin_available()) + return 0; + ia_css_fw_copy(mmid, ssid, src, dst, size); + fw_load_transaction_add(); + return size; +} + + +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_zero_begin( + unsigned int ssid, + ia_css_cmem_address_t dst, + unsigned int size) +{ + if (!ia_css_fw_copy_begin_available()) + return 0; /*quote exceeded*/ + + ia_css_fw_zero(ssid, dst, size); + fw_load_transaction_add(); + return size; +} + +/* END OF TRANSFER */ +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_end(unsigned int n) +{ + int no_of_ack_received; + int fw_end_count; + int transaction_done; + bool success; + + no_of_ack_received = ia_css_fw_copy_end_available(); + fw_end_count = min(n, no_of_ack_received); + + transaction_done = 0; + + while (transaction_done < fw_end_count) { + success = fw_load_transaction_remove(); + assert(success == true); + transaction_done++; + } + return fw_end_count; +} + +/* OPTIONALLY USED */ +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_copy_begin_available(void) +{ + return (FW_LOAD_XMEM_MAX_TRANSACTION_SUPPORT - + ia_css_fw_copy_end_available()); +} + +IA_CSS_FW_LOAD_STORAGE_CLASS_C unsigned int +ia_css_fw_copy_end_available(void) +{ + /* check how many transactions are ready to be ended */ + return fw_load_transaction_get_finished(); +} + +#endif /* __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl_host.h new file mode 100644 index 000000000000..25a05cce2576 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/fw_load/src/xmem/ia_css_fw_load_non_blocking_impl_host.h @@ -0,0 +1,45 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_HOST_H +#define __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_HOST_H + +#include "storage_class.h" +#include "type_support.h" +#include "ia_css_fw_load_non_blocking_host_state.h" + +#define FW_LOAD_MAX_TRANS_SUPPORTED UINT_MAX + +STORAGE_CLASS_INLINE void fw_load_transaction_init(void) +{ + started = 0; +} + +STORAGE_CLASS_INLINE bool fw_load_transaction_add(void) +{ + started++; + return true; +} + +STORAGE_CLASS_INLINE bool fw_load_transaction_remove(void) +{ + started--; + return true; +} + +STORAGE_CLASS_INLINE unsigned int fw_load_transaction_get_finished(void) +{ + return started; +} +#endif /* __IA_CSS_FW_LOAD_NON_BLOCKING_IMPL_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h new file mode 100644 index 000000000000..a284d74bb4a6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir.h @@ -0,0 +1,99 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_H +#define __IA_CSS_PKG_DIR_H + +#include "ia_css_pkg_dir_storage_class.h" +#include "ia_css_pkg_dir_types.h" +#include "type_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +/* User is expected to call the verify function manually, + * other functions do not call it internally + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +int ia_css_pkg_dir_verify_header( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +enum ia_css_pkg_dir_version ia_css_pkg_dir_get_version( + const ia_css_pkg_dir_entry_t *pkg_dir_header +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_set_version( + ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version +); + + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint32_t ia_css_pkg_dir_entry_get_size( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint16_t ia_css_pkg_dir_entry_get_version( + const ia_css_pkg_dir_entry_t *entry +); + +IA_CSS_PKG_DIR_STORAGE_CLASS_H +uint8_t ia_css_pkg_dir_entry_get_type( + const ia_css_pkg_dir_entry_t *entry +); + +/* Get the address of the specified entry in the PKG_DIR + * Note: This function expects the complete PKG_DIR in the same memory space + * and the entries contains offsets and not addresses. + */ +IA_CSS_PKG_DIR_STORAGE_CLASS_H +void *ia_css_pkg_dir_get_entry_address( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index +); + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "ia_css_pkg_dir_impl.h" + +#endif + +#endif /* __IA_CSS_PKG_DIR_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h new file mode 100644 index 000000000000..ad194b0389eb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_iunit.h @@ -0,0 +1,46 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IUNIT_H +#define __IA_CSS_PKG_DIR_IUNIT_H + +/* In bootflow, pkg_dir only supports upto 16 entries in pkg_dir + * pkg_dir_header + Psys_server pg + Isys_server pg + 13 Client pg + */ + +enum { + IA_CSS_PKG_DIR_SIZE = 16, + IA_CSS_PKG_DIR_ENTRIES = IA_CSS_PKG_DIR_SIZE - 1 +}; + +#define IUNIT_MAX_CLIENT_PKG_ENTRIES 13 + +/* Example assignment of unique identifiers for the FW components + * This should match the identifiers in the manifest + */ +enum ia_css_pkg_dir_entry_type { + IA_CSS_PKG_DIR_HEADER = 0, + IA_CSS_PKG_DIR_PSYS_SERVER_PG, + IA_CSS_PKG_DIR_ISYS_SERVER_PG, + IA_CSS_PKG_DIR_CLIENT_PG +}; + +/* Fixed entries in the package directory */ +enum ia_css_pkg_dir_index { + IA_CSS_PKG_DIR_PSYS_INDEX = 0, + IA_CSS_PKG_DIR_ISYS_INDEX = 1, + IA_CSS_PKG_DIR_CLIENT_0 = 2 +}; + +#endif /* __IA_CSS_PKG_DIR_IUNIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h new file mode 100644 index 000000000000..cb64172151f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_storage_class.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_STORAGE_CLASS_H +#define __IA_CSS_PKG_DIR_STORAGE_CLASS_H + + +#include "storage_class.h" + +#ifndef __IA_CSS_PKG_DIR_INLINE__ +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C +#else +#define IA_CSS_PKG_DIR_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PKG_DIR_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PKG_DIR_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h new file mode 100644 index 000000000000..b024b3da2f9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/interface/ia_css_pkg_dir_types.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_TYPES_H +#define __IA_CSS_PKG_DIR_TYPES_H + +#include "type_support.h" + +struct ia_css_pkg_dir_entry { + uint32_t address[2]; + uint32_t size; + uint16_t version; + uint8_t type; + uint8_t unused; +}; + +typedef void ia_css_pkg_dir_t; +typedef struct ia_css_pkg_dir_entry ia_css_pkg_dir_entry_t; + +/* The version field of the pkg_dir header defines + * if entries contain offsets or pointers + */ +/* This is temporary, until all pkg_dirs use pointers */ +enum ia_css_pkg_dir_version { + IA_CSS_PKG_DIR_POINTER, + IA_CSS_PKG_DIR_OFFSET +}; + + +#endif /* __IA_CSS_PKG_DIR_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/pkg_dir.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/pkg_dir.mk new file mode 100644 index 000000000000..32c8a68f3653 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/pkg_dir.mk @@ -0,0 +1,29 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PKG DIR + +PKG_DIR_DIR = $${MODULES_DIR}/pkg_dir +PKG_DIR_INTERFACE = $(PKG_DIR_DIR)/interface +PKG_DIR_SOURCES = $(PKG_DIR_DIR)/src + +PKG_DIR_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir.c +PKG_DIR_CPPFLAGS = -I$(PKG_DIR_INTERFACE) +PKG_DIR_CPPFLAGS += -I$(PKG_DIR_SOURCES) +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/../isp/kernels/io_ls/common +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu +PKG_DIR_CPPFLAGS += -I$${MODULES_DIR}/fw_abi_common_types/ipu/$(FW_ABI_IPU_TYPES_VERSION) + +PKG_DIR_CREATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_create.c +PKG_DIR_UPDATE_FILES = $(PKG_DIR_DIR)/src/ia_css_pkg_dir_update.c diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c new file mode 100644 index 000000000000..348b56833e06 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir.c @@ -0,0 +1,27 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __IA_CSS_PKG_DIR_INLINE__ + +#include "storage_class.h" + +STORAGE_CLASS_INLINE int __ia_css_pkg_dir_avoid_warning_on_empty_file(void) +{ + return 0; +} + +#else +#include "ia_css_pkg_dir_impl.h" + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h new file mode 100644 index 000000000000..d5067d21398f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_impl.h @@ -0,0 +1,201 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_IMPL_H +#define __IA_CSS_PKG_DIR_IMPL_H + +#include "ia_css_pkg_dir.h" +#include "ia_css_pkg_dir_int.h" +#include "error_support.h" +#include "type_support.h" +#include "assert_support.h" + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +const ia_css_pkg_dir_entry_t *ia_css_pkg_dir_get_entry( + const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + DECLARE_ERRVAL + struct ia_css_pkg_dir_entry *pkg_dir_header = NULL; + + verifexitval(pkg_dir != NULL, EFAULT); + + pkg_dir_header = (struct ia_css_pkg_dir_entry *)pkg_dir; + + /* First entry of the structure is the header, skip that */ + index++; + verifexitval(index < pkg_dir_header->size, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return NULL; + } + return &(pkg_dir_header[index]); +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +int ia_css_pkg_dir_verify_header(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + verifexitval(pkg_dir_header != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + return -1; + } + return ((pkg_dir_header->address[0] == PKG_DIR_MAGIC_VAL_0) + && (pkg_dir_header->address[1] == PKG_DIR_MAGIC_VAL_1)) ? + 0 : -1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_num_entries( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + uint32_t size = 0; + + verifexitval(pkg_dir_header != NULL, EFAULT); + size = pkg_dir_header->size; + verifexitval(size > 0, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return size - 1; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +enum ia_css_pkg_dir_version +ia_css_pkg_dir_get_version(const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + assert(pkg_dir_header != NULL); + return pkg_dir_header->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_set_version(ia_css_pkg_dir_entry_t *pkg_dir_header, + enum ia_css_pkg_dir_version version) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 1; + } + pkg_dir_header->version = version; + return 0; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_get_size_in_bytes( + const ia_css_pkg_dir_entry_t *pkg_dir_header) +{ + DECLARE_ERRVAL + + verifexitval(pkg_dir_header != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return sizeof(struct ia_css_pkg_dir_entry) * pkg_dir_header->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_lo( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[0]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_address_hi( + const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->address[1]; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint32_t ia_css_pkg_dir_entry_get_size(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->size; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint16_t ia_css_pkg_dir_entry_get_version(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->version; +} + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +uint8_t ia_css_pkg_dir_entry_get_type(const ia_css_pkg_dir_entry_t *entry) +{ + DECLARE_ERRVAL + + verifexitval(entry != NULL, EFAULT); +EXIT: + if (haserror(EFAULT)) { + return 0; + } + return entry->type; +} + + +IA_CSS_PKG_DIR_STORAGE_CLASS_C +void *ia_css_pkg_dir_get_entry_address(const ia_css_pkg_dir_t *pkg_dir, + uint32_t index) +{ + void *entry_blob = NULL; + const ia_css_pkg_dir_entry_t *pkg_dir_entry = + ia_css_pkg_dir_get_entry(pkg_dir, index-1); + + if ((pkg_dir_entry != NULL) && + (ia_css_pkg_dir_entry_get_size(pkg_dir_entry) > 0)) { + assert(ia_css_pkg_dir_entry_get_address_hi(pkg_dir_entry) == 0); + entry_blob = (void *)((char *)pkg_dir + + ia_css_pkg_dir_entry_get_address_lo(pkg_dir_entry)); + } + return entry_blob; +} + +#endif /* __IA_CSS_PKG_DIR_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h new file mode 100644 index 000000000000..203505fbee54 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/pkg_dir/src/ia_css_pkg_dir_int.h @@ -0,0 +1,49 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PKG_DIR_INT_H +#define __IA_CSS_PKG_DIR_INT_H + +/* + * Package Dir structure as specified in CSE FAS + * + * PKG DIR Header + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * 0 "_IUPKDR_" + * 1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version of the Structure + * Size: Size of the entire table (including header) in 16 byte chunks + * Type: Must be 0 for header + * + * Figure 13: PKG DIR Header + * + * + * PKG DIR Entry + * Qword 63:56 55 54:48 47:32 31:24 23:0 + * N Address/Offset + * N+1 Rsvd Rsvd Type Version Rsvd Size + * + * Version: Version # of the Component + * Size: Size of the component in bytes + * Type: Component Identifier + */ + +#define PKG_DIR_SIZE_BITS 24 +#define PKG_DIR_TYPE_BITS 7 + +#define PKG_DIR_MAGIC_VAL_1 (('_' << 24) | ('I' << 16) | ('U' << 8) | 'P') +#define PKG_DIR_MAGIC_VAL_0 (('K' << 24) | ('D' << 16) | ('R' << 8) | '_') + +#endif /* __IA_CSS_PKG_DIR_INT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/port_env_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/port_env_struct.h new file mode 100644 index 000000000000..4d39a4739a8b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/port_env_struct.h @@ -0,0 +1,24 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PORT_ENV_STRUCT_H +#define __PORT_ENV_STRUCT_H + +struct port_env { + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __PORT_ENV_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue.h new file mode 100644 index 000000000000..b233ab3baf01 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue.h @@ -0,0 +1,40 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_H +#define __QUEUE_H + +#include "queue_struct.h" +#include "port_env_struct.h" + +/* + * SYS queues are created by the host + * SYS queues cannot be accessed through the queue interface + * To send data into a queue a send_port must be opened. + * To receive data from a queue, a recv_port must be opened. + */ + +/* return required buffer size for queue */ +unsigned int +sys_queue_buf_size(unsigned int size, unsigned int token_size); + +/* + * initialize a queue that can hold at least 'size' tokens of + * 'token_size' bytes. + */ +void +sys_queue_init(struct sys_queue *q, unsigned int size, + unsigned int token_size, struct sys_queue_res *res); + +#endif /* __QUEUE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue_struct.h new file mode 100644 index 000000000000..ef48fcfded2b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/queue_struct.h @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __QUEUE_STRUCT_H +#define __QUEUE_STRUCT_H + +/* queue description, shared between sender and receiver */ + +#include "type_support.h" + +#ifdef __VIED_CELL +typedef struct {uint32_t v[2]; } host_buffer_address_t; +#else +typedef uint64_t host_buffer_address_t; +#endif + +typedef uint32_t vied_buffer_address_t; + + +struct sys_queue { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* reg no in subsystem's regmem */ + unsigned int rd_reg; + unsigned int _align; +}; + +struct sys_queue_res { + host_buffer_address_t host_address; + vied_buffer_address_t vied_address; + unsigned int reg; +}; + +#endif /* __QUEUE_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port.h new file mode 100644 index 000000000000..cce253b26668 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port.h @@ -0,0 +1,34 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_H +#define __RECV_PORT_H + + +struct recv_port; +struct sys_queue; +struct port_env; + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env); + +unsigned int +recv_port_available(const struct recv_port *p); + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data); + + +#endif /* __RECV_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port_struct.h new file mode 100644 index 000000000000..52ec563b13cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/recv_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __RECV_PORT_STRUCT_H +#define __RECV_PORT_STRUCT_H + +#include "buffer_type.h" + +struct recv_port { + buffer_address buffer; /* address of buffer in DDR */ + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer located in regmem */ + unsigned int rd_reg; /* index read pointer located in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; /* address of memory containing regmem */ +}; + +#endif /* __RECV_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port.h new file mode 100644 index 000000000000..04a160f3f019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port.h @@ -0,0 +1,52 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_H +#define __SEND_PORT_H + + +/* + * A send port can be used to send tokens into a queue. + * The interface can be used on any type of processor (host, SP, ...) + */ + +struct send_port; +struct sys_queue; +struct port_env; + +/* + * Open a send port on a queue. After the port is opened, tokens can be sent + */ +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env); + +/* + * Determine how many tokens can be sent + */ +unsigned int +send_port_available(const struct send_port *p); + +/* + * Send a token via a send port. The function returns the number of + * tokens that have been sent: + * 1: the token was accepted + * 0: the token was not accepted (full queue) + * The size of a token is determined at initialization. + */ +unsigned int +send_port_transfer(const struct send_port *p, const void *data); + + +#endif /* __SEND_PORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port_struct.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port_struct.h new file mode 100644 index 000000000000..f834c62bc3db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/interface/send_port_struct.h @@ -0,0 +1,32 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __SEND_PORT_STRUCT_H +#define __SEND_PORT_STRUCT_H + +#include "buffer_type.h" + +struct send_port { + buffer_address buffer; + unsigned int size; + unsigned int token_size; + unsigned int wr_reg; /* index of write pointer in regmem */ + unsigned int rd_reg; /* index of read pointer in regmem */ + + unsigned int mmid; + unsigned int ssid; + unsigned int mem_addr; +}; + +#endif /* __SEND_PORT_STRUCT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/port.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/port.mk new file mode 100644 index 000000000000..b3801247802e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/port.mk @@ -0,0 +1,31 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PORT + +PORT_DIR=$${MODULES_DIR}/port + +PORT_INTERFACE=$(PORT_DIR)/interface +PORT_SOURCES1=$(PORT_DIR)/src + +PORT_HOST_FILES += $(PORT_SOURCES1)/send_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/recv_port.c +PORT_HOST_FILES += $(PORT_SOURCES1)/queue.c + +PORT_HOST_CPPFLAGS += -I$(PORT_INTERFACE) + +PORT_FW_FILES += $(PORT_SOURCES1)/send_port.c +PORT_FW_FILES += $(PORT_SOURCES1)/recv_port.c + +PORT_FW_CPPFLAGS += -I$(PORT_INTERFACE) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/queue.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/queue.c new file mode 100644 index 000000000000..eeec99dfe2d0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/queue.c @@ -0,0 +1,47 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "queue.h" + +#include "regmem_access.h" +#include "port_env_struct.h" + +unsigned int sys_queue_buf_size(unsigned int size, unsigned int token_size) +{ + return (size + 1) * token_size; +} + +void +sys_queue_init(struct sys_queue *q, unsigned int size, unsigned int token_size, + struct sys_queue_res *res) +{ + unsigned int buf_size; + + q->size = size + 1; + q->token_size = token_size; + buf_size = sys_queue_buf_size(size, token_size); + + /* acquire the shared buffer space */ + q->host_address = res->host_address; + res->host_address += buf_size; + q->vied_address = res->vied_address; + res->vied_address += buf_size; + + /* acquire the shared read and writer pointers */ + q->wr_reg = res->reg; + res->reg++; + q->rd_reg = res->reg; + res->reg++; + +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/recv_port.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/recv_port.c new file mode 100644 index 000000000000..31b36e9ceafb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/recv_port.c @@ -0,0 +1,95 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "recv_port.h" +#include "port_env_struct.h" /* for port_env */ +#include "queue_struct.h" /* for sys_queue */ +#include "recv_port_struct.h" /* for recv_port */ +#include "buffer_access.h" /* for buffer_load, buffer_address */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_invalidate */ +#endif + +void +recv_port_open(struct recv_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; + +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +recv_port_index(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(rd, i, p->size); +} + +unsigned int +recv_port_available(const struct recv_port *p) +{ + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + + return OP_std_modadd(wr, -rd, p->size); +} + +STORAGE_CLASS_INLINE void +recv_port_copy(const struct recv_port *p, unsigned int i, void *data) +{ + unsigned int rd = recv_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (rd * token_size); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_invalidate((void *)HOST_ADDRESS(p->buffer), + token_size*p->size); +#endif + buffer_load(addr, data, token_size, p->mmid); +} + +STORAGE_CLASS_INLINE void +recv_port_release(const struct recv_port *p, unsigned int i) +{ + unsigned int rd = recv_port_index(p, i); + + regmem_store_32(p->mem_addr, p->rd_reg, rd, p->ssid); +} + +unsigned int +recv_port_transfer(const struct recv_port *p, void *data) +{ + if (!recv_port_available(p)) + return 0; + recv_port_copy(p, 0, data); + recv_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/send_port.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/send_port.c new file mode 100644 index 000000000000..8d1fba08c5d5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/port/src/send_port.c @@ -0,0 +1,94 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "send_port.h" +#include "queue_struct.h" /* for sys_queue */ +#include "send_port_struct.h" /* for send_port */ +#include "port_env_struct.h" /* for port_env */ +#include "regmem_access.h" /* for regmem_load_32, regmem_store_32 */ +#include "buffer_access.h" /* for buffer_store, buffer_address */ +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "math_support.h" /* for OP_std_modadd */ +#include "type_support.h" /* for HOST_ADDRESS */ + +#ifndef __VIED_CELL +#include "cpu_mem_support.h" /* for ia_css_cpu_mem_cache_flush */ +#endif + +void +send_port_open(struct send_port *p, const struct sys_queue *q, + const struct port_env *env) +{ + p->mmid = env->mmid; + p->ssid = env->ssid; + p->mem_addr = env->mem_addr; + + p->size = q->size; + p->token_size = q->token_size; + p->wr_reg = q->wr_reg; + p->rd_reg = q->rd_reg; +#ifdef __VIED_CELL + p->buffer = q->vied_address; +#else + p->buffer = q->host_address; +#endif +} + +STORAGE_CLASS_INLINE unsigned int +send_port_index(const struct send_port *p, unsigned int i) +{ + unsigned int wr = regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(wr, i, p->size); +} + +unsigned int +send_port_available(const struct send_port *p) +{ + int rd = (int)regmem_load_32(p->mem_addr, p->rd_reg, p->ssid); + int wr = (int)regmem_load_32(p->mem_addr, p->wr_reg, p->ssid); + + return OP_std_modadd(rd, -(wr+1), p->size); +} + +STORAGE_CLASS_INLINE void +send_port_copy(const struct send_port *p, unsigned int i, const void *data) +{ + unsigned int wr = send_port_index(p, i); + unsigned int token_size = p->token_size; + buffer_address addr = p->buffer + (wr * token_size); + + buffer_store(addr, data, token_size, p->mmid); +#ifndef __VIED_CELL + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(addr), token_size); +#endif +} + +STORAGE_CLASS_INLINE void +send_port_release(const struct send_port *p, unsigned int i) +{ + unsigned int wr = send_port_index(p, i); + + regmem_store_32(p->mem_addr, p->wr_reg, wr, p->ssid); +} + +unsigned int +send_port_transfer(const struct send_port *p, const void *data) +{ + if (!send_port_available(p)) + return 0; + send_port_copy(p, 0, data); + send_port_release(p, 1); + return 1; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/interface/psys_infobits.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/interface/psys_infobits.h new file mode 100644 index 000000000000..11029a180531 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/interface/psys_infobits.h @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PSYS_INFOBITS_H +#define __PSYS_INFOBITS_H + +void ia_css_psys_set_master_port_regs(unsigned int ssid); + +#endif /* __PSYS_INFOBITS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/psys_infobits.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/psys_infobits.mk new file mode 100644 index 000000000000..c6641e293fe6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/psys_infobits.mk @@ -0,0 +1,29 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# PSYS_INFOBITS +# + +PSYS_INFOBITS_DIR = $${MODULES_DIR}/psys_infobits + +PSYS_INFOBITS_INTERFACE = $(PSYS_INFOBITS_DIR)/interface +PSYS_INFOBITS_SOURCES = $(PSYS_INFOBITS_DIR)/src + +PSYS_INFOBITS_CPPFLAGS := \ + -I$(PSYS_INFOBITS_INTERFACE) + +PSYS_INFOBITS_HOST_FILES = \ + $(PSYS_INFOBITS_SOURCES)/psys_infobits.c + +PSYS_INFOBITS_FW_FILES = $(PSYS_INFOBITS_HOST_FILES) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/src/psys_infobits.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/src/psys_infobits.c new file mode 100644 index 000000000000..5c43583f6193 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_infobits/src/psys_infobits.c @@ -0,0 +1,107 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "psys_infobits.h" + +#include "assert_support.h" +#include "ia_css_cell.h" +#include "ipu_device_cell_properties.h" +#include "ipu_device_cell_properties_impl.h" +#include "ipu_device_buttress_properties_struct.h" + +/* +** According to BXT CSS HAS PS the info bits as expected by buttress are +** Field---------Description---------------------Encoding---------------| + | 0 | CIOM0: Snoopable | 0 - non snoopable | + | | | 1 - snoopable | + ----------------------------------------------------------------------| + | 1 | CIOM0: VC0_RS_for_IMR | Deadline | + | | CIOM1: VC1_deadline_pointer | 0 - regular deadline | + | | | 1 - urgent deadline | + ----------------------------------------------------------------------| + | 2 | Deadline pointer reserved | | + ----------------------------------------------------------------------| + | 3 | CIOM1: Zero-length write (ZLW)| 0 - NOP | + | | | 1 - Convert transaction as ZLW + ----------------------------------------------------------------------| + | 5:4 | CIOM0: Request destination | Destination | + | | CIOM1: Stream_ID[1:0] | 00 - Buttress registers| + | | | 01 - Primary | + | | | 10 - Reserved | + | | | 11 - Input system | + ----------------------------------------------------------------------| + | 7:6 | CIOM1: Stream_ID[3:2] | For data prefetch | + ----------------------------------------------------------------------| + | 8 | CIOM1: Address swizzeling | | + ----------------------------------------------------------------------| + + ** As PSYS devices use MO port and the request destination is DDR + ** then bit 4 (Request destination) should be 1 (Primary), thus 0x10 +*/ + + +void ia_css_psys_set_master_port_regs(unsigned int ssid) +{ + /* set primary destination(DDR) */ + unsigned int info_bits = IA_CSS_INFO_BITS_M0_DDR; + enum ipu_device_psys_cell_id cell_id; + + COMPILATION_ERROR_IF(0 != SPC0); + + /* Configure SPC */ + cell_id = SPC0; + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_CONTROL_ICACHE, info_bits); + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_CONTROL_XMEM, info_bits); + ia_css_cell_set_master_base_address(ssid, cell_id, + IPU_DEVICE_SP2600_CONTROL_XMEM, 0); + +#if defined(HAS_SPP0) + /* Configure SPP0 proxy */ + cell_id = SPP0; + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_ICACHE, info_bits); + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_XMEM, info_bits); + ia_css_cell_set_master_base_address(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_XMEM, 0); + COMPILATION_ERROR_IF(SPP0 < SPC0); +#endif + +#if defined(HAS_SPP1) + /* Configure SPP1 proxy */ + cell_id = SPP1; + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_ICACHE, info_bits); + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_XMEM, info_bits); + ia_css_cell_set_master_base_address(ssid, cell_id, + IPU_DEVICE_SP2600_PROXY_XMEM, 0); + COMPILATION_ERROR_IF(SPP1 < SPC0); +#endif + +#if defined(HAS_ISP0) + /* Configure ISP(s) */ + for (cell_id = ISP0; cell_id < NUM_CELLS; cell_id++) { + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_CELL_MASTER_ICACHE, info_bits); + ia_css_cell_set_master_info_bits(ssid, cell_id, + IPU_DEVICE_CELL_MASTER_XMEM, info_bits); + ia_css_cell_set_master_base_address(ssid, cell_id, + IPU_DEVICE_CELL_MASTER_XMEM, 0); + } + COMPILATION_ERROR_IF(ISP0 < SPP0); +#endif +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h new file mode 100644 index 000000000000..6b2387352ae3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_private_pg/interface/ia_css_psys_private_pg_data.h @@ -0,0 +1,43 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PRIVATE_PG_DATA_H +#define __IA_CSS_PSYS_PRIVATE_PG_DATA_H + +#include "ipu_device_acb_devices.h" +#include "ipu_device_gp_devices.h" +#include "type_support.h" +#include "vied_nci_acb_route_type.h" + +#define PRIV_CONF_INVALID 0xFF + +struct ia_css_psys_pg_buffer_information_s { + unsigned int buffer_base_addr; + unsigned int bpe; + unsigned int buffer_width; + unsigned int buffer_height; + unsigned int num_of_buffers; + unsigned int dfm_port_addr; +}; + +typedef struct ia_css_psys_pg_buffer_information_s ia_css_psys_pg_buffer_information_t; + +struct ia_css_psys_private_pg_data { + nci_acb_route_t acb_route[IPU_DEVICE_ACB_NUM_ACB]; + uint8_t psa_mux_conf[IPU_DEVICE_GP_PSA_MUX_NUM_MUX]; + uint8_t isa_mux_conf[IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX]; + ia_css_psys_pg_buffer_information_t input_buffer_info; +}; + +#endif /* __IA_CSS_PSYS_PRIVATE_PG_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h new file mode 100644 index 000000000000..eee1d6ab0a49 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/interface/ia_css_bxt_spctrl_trace.h @@ -0,0 +1,107 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_BXT_SPCTRL_TRACE_H +#define __IA_CSS_BXT_SPCTRL_TRACE_H + +#include "ia_css_trace.h" + +/* Not using 0 to identify wrong configuration being passed from + * the .mk file outside. + * Log levels not in the range below will cause a + * "No BXT_SPCTRL_TRACE_CONFIG Tracing level defined" + */ +#define BXT_SPCTRL_TRACE_LOG_LEVEL_OFF 1 +#define BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL 2 +#define BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG 3 + +/* BXT_SPCTRL and all the submodules in BXT_SPCTRL will have the + * default tracing level set to the BXT_SPCTRL_TRACE_CONFIG level. + * If not defined in the psysapi.mk fill it will be set by + * default to no trace (BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL) + */ +#define BXT_SPCTRL_TRACE_CONFIG_DEFAULT BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL + +#if !defined(BXT_SPCTRL_TRACE_CONFIG) +# define BXT_SPCTRL_TRACE_CONFIG BXT_SPCTRL_TRACE_CONFIG_DEFAULT +#endif + +/* BXT_SPCTRL Module tracing backend is mapped to TUNIT tracing for + * target platforms + */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define BXT_SPCTRL_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(BXT_SPCTRL_TRACE_CONFIG)) + /* Module specific trace setting */ +# if BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_OFF + /* BXT_SPCTRL_TRACE_LOG_LEVEL_OFF */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED +# elif BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL + /* BXT_SPCTRL_TRACE_LOG_LEVEL_NORMAL */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED +# elif BXT_SPCTRL_TRACE_CONFIG == BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG + /* BXT_SPCTRL_TRACE_LOG_LEVEL_DEBUG */ +# define BXT_SPCTRL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED +# define BXT_SPCTRL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED +# else +# error "No BXT_SPCTRL_TRACE_CONFIG Tracing level defined" +# endif +#else +# error "BXT_SPCTRL_TRACE_CONFIG not defined" +#endif + +/* Overriding submodules in BXT_SPCTRL with a specific tracing level */ +/* #define BXT_SPCTRL_DYNAMIC_TRACING_OVERRIDE TRACE_LOG_LEVEL_VERBOSE */ + +#endif /* __IA_CSS_BXT_SPCTRL_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/psys_server.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/psys_server.mk new file mode 100644 index 000000000000..c4462c984793 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/psys_server.mk @@ -0,0 +1,81 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PSYS_SERVER + +include $(MODULES_DIR)/config/system_$(IPU_SYSVER).mk +include $(MODULES_DIR)/config/$(SUBSYSTEM)/subsystem_$(IPU_SYSVER).mk + +PSYS_SERVER_DIR=${MODULES_DIR}/psys_server + +# The watchdog should never be merged enabled +PSYS_SERVER_WATCHDOG_ENABLE ?= 0 + +PSYS_SERVER_INTERFACE=$(PSYS_SERVER_DIR)/interface +PSYS_SERVER_SOURCES=$(PSYS_SERVER_DIR)/src + +# PSYS API implementation files. Consider a new module for those to avoid +# having them together with firmware. +PSYS_SERVER_HOST_FILES += $${MODULES_DIR}/psysapi/device/src/ia_css_psys_device.c +PSYS_SERVER_HOST_FILES += $(PSYS_SERVER_SOURCES)/bxt_spctrl_process_group_cmd_impl.c + +PSYS_SERVER_HOST_CPPFLAGS += -I$(PSYS_SERVER_INTERFACE) + +PSYS_SERVER_HOST_CPPFLAGS += -DSSID=$(SSID) +PSYS_SERVER_HOST_CPPFLAGS += -DMMID=$(MMID) + + +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_cmd_queue_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_event_queue_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_init_fw.c +PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/psys_process_group_fw.c + +# Files that server modules need to use +PSYS_SERVER_SUPPORT_FILES = $(PSYS_SERVER_SOURCES)/dev_access_conv/$(IPU_SYSVER)/ia_css_psys_server_dev_access_type_conv.c +PSYS_SERVER_SUPPORT_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_config.c + +# Include those to build the release firmware. Otherwise replace by test code. +PSYS_SERVER_RELEASE_FW_FILES = $(PSYS_SERVER_SOURCES)/psys_server.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_proxy.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dev_access.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_terminal_load.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_remote_obj_access.c +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dma_access.c +ifeq ($(HAS_DEC400), 1) +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_dec400_access.c +endif +PSYS_SERVER_RELEASE_FW_FILES += $(PSYS_SERVER_SUPPORT_FILES) + +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_INTERFACE) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/$(IPU_SYSVER) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/$(PSYS_SERVER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/loader/$(PSYS_SERVER_LOADER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/access_blocker/$(PSYS_ACCESS_BLOCKER_VERSION) +PSYS_SERVER_FW_CPPFLAGS += -I$(PSYS_SERVER_SOURCES)/access_blocker/src + +PSYS_SERVER_FW_CPPFLAGS += -DSSID=$(SSID) +PSYS_SERVER_FW_CPPFLAGS += -DMMID=$(MMID) +PSYS_SERVER_FW_CPPFLAGS += -DHAS_DPCM=$(if $(HAS_DPCM),1,0) + +# PSYS server watchdog for debugging +ifeq ($(PSYS_SERVER_WATCHDOG_ENABLE), 1) + PSYS_SERVER_FW_FILES += $(PSYS_SERVER_SOURCES)/ia_css_psys_server_watchdog.c + PSYS_SERVER_FW_CPPFLAGS += -DPSYS_SERVER_WATCHDOG_DEBUG +endif + +PSYS_SERVER_FW_CPPFLAGS += -D$(PSYS_HW_VERSION) + +PSYS_SERVER_FW_CPPFLAGS += -DENABLE_TPROXY=$(PSYS_SERVER_ENABLE_TPROXY) +PSYS_SERVER_FW_CPPFLAGS += -DENABLE_DEVPROXY=$(PSYS_SERVER_ENABLE_DEVPROXY) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c new file mode 100644 index 000000000000..6f8aea782464 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psys_server/src/bxt_spctrl_process_group_cmd_impl.c @@ -0,0 +1,332 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_device.h" +#include "ia_css_psys_process_group_cmd_impl.h" +#include "ia_css_psysapi.h" +#include "ia_css_psys_terminal.h" +#include "ia_css_psys_process.h" +#include "ia_css_psys_process.psys.h" +#include "ia_css_psys_process_group.h" +#include "ia_css_psys_process_group.psys.h" +#include "ia_css_psys_program_group_manifest.h" +#include "type_support.h" +#include "error_support.h" +#include "misc_support.h" +#include "cpu_mem_support.h" +#include "ia_css_bxt_spctrl_trace.h" + +#if HAS_DUAL_CMD_CTX_SUPPORT +#define MAX_CLIENT_PGS 8 /* same as test_params.h */ +struct ia_css_process_group_context { + ia_css_process_group_t *pg; + bool secure; +}; +struct ia_css_process_group_context pg_contexts[MAX_CLIENT_PGS]; +static unsigned int num_of_pgs; + +STORAGE_CLASS_INLINE +struct ia_css_syscom_context *ia_css_process_group_get_context(ia_css_process_group_t *process_group) +{ + unsigned int i; + bool secure = false; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_get_context(): enter:\n"); + + for (i = 0; i < num_of_pgs; i++) { + if (pg_contexts[i].pg == process_group) { + secure = pg_contexts[i].secure; + break; + } + } + + IA_CSS_TRACE_1(BXT_SPCTRL, INFO, + "ia_css_process_group_get_context(): secure %d\n", secure); + return secure ? psys_syscom_secure : psys_syscom; +} + +int ia_css_process_group_store(ia_css_process_group_t *process_group, bool secure) +{ + IA_CSS_TRACE_2(BXT_SPCTRL, INFO, + "ia_css_process_group_store(): pg instance %d secure %d\n", num_of_pgs, secure); + + pg_contexts[num_of_pgs].pg = process_group; + pg_contexts[num_of_pgs].secure = secure; + num_of_pgs++; + return 0; +} +#else /* HAS_DUAL_CMD_CTX_SUPPORT */ +STORAGE_CLASS_INLINE +struct ia_css_syscom_context *ia_css_process_group_get_context(ia_css_process_group_t *process_group) +{ + NOT_USED(process_group); + + return psys_syscom; +} + +int ia_css_process_group_store(ia_css_process_group_t *process_group, bool secure) +{ + NOT_USED(process_group); + NOT_USED(secure); + + return 0; +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +int ia_css_process_group_on_create( + ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *program_group_manifest, + const ia_css_program_group_param_t *program_group_param) +{ + NOT_USED(process_group); + NOT_USED(program_group_manifest); + NOT_USED(program_group_param); + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_on_create(): enter:\n"); + + return 0; +} + +int ia_css_process_group_on_destroy( + ia_css_process_group_t *process_group) +{ + NOT_USED(process_group); + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_on_destroy(): enter:\n"); + + return 0; +} + +int ia_css_process_group_exec_cmd( + ia_css_process_group_t *process_group, + const ia_css_process_group_cmd_t cmd) +{ + int retval = -1; + ia_css_process_group_state_t state; + struct ia_css_psys_cmd_s psys_cmd; + bool cmd_queue_full; + unsigned int queue_id; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): enter:\n"); + + verifexit(process_group != NULL); + + state = ia_css_process_group_get_state(process_group); + + verifexit(state != IA_CSS_PROCESS_GROUP_ERROR); + verifexit(state < IA_CSS_N_PROCESS_GROUP_STATES); + + switch (cmd) { + case IA_CSS_PROCESS_GROUP_CMD_SUBMIT: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_SUBMIT:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_READY); + + /* External resource availability checks */ + verifexit(ia_css_can_process_group_submit(process_group)); + + process_group->state = IA_CSS_PROCESS_GROUP_BLOCKED; + break; + case IA_CSS_PROCESS_GROUP_CMD_START: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_START:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_BLOCKED); + + /* External resource state checks */ + verifexit(ia_css_can_process_group_start(process_group)); + + process_group->state = IA_CSS_PROCESS_GROUP_STARTED; + break; + case IA_CSS_PROCESS_GROUP_CMD_DISOWN: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_DISOWN:\n"); + verifexit(state == IA_CSS_PROCESS_GROUP_STARTED); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_START; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + verifexit(ia_css_process_group_print(process_group, NULL) == 0); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID, &psys_cmd); + verifexit(retval > 0); + break; + case IA_CSS_PROCESS_GROUP_CMD_STOP: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_STOP:\n"); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_STOP; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + queue_id = ia_css_process_group_get_base_queue_id(process_group); + verifexit(queue_id < IA_CSS_N_PSYS_CMD_QUEUE_ID); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + queue_id, &psys_cmd); + verifexit(retval > 0); + break; + case IA_CSS_PROCESS_GROUP_CMD_ABORT: + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_process_group_exec_cmd(): IA_CSS_PROCESS_GROUP_CMD_ABORT:\n"); + + /* Once the flushing of shared buffers is fixed this verifexit + * should be changed to be state = IA_CSS_PROCESS_GROUP_STARTED + */ + verifexit(state == IA_CSS_PROCESS_GROUP_BLOCKED); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = IA_CSS_PROCESS_GROUP_CMD_ABORT; + psys_cmd.msg = 0; + psys_cmd.context_handle = process_group->ipu_virtual_address; + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), + IA_CSS_PSYS_CMD_QUEUE_DEVICE_ID, &psys_cmd); + verifexit(retval > 0); + break; + default: + verifexit(false); + break; + } + + retval = 0; +EXIT: + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_process_group_exec_cmd failed (%i)\n", retval); + } + return retval; +} + +STORAGE_CLASS_INLINE int enqueue_buffer_set_cmd( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset, + uint16_t command + ) +{ + int retval = -1; + struct ia_css_psys_cmd_s psys_cmd; + bool cmd_queue_full; + unsigned int queue_id; + + verifexit(ia_css_process_group_get_state(process_group) + == IA_CSS_PROCESS_GROUP_STARTED); + + verifexit(queue_offset < + ia_css_process_group_get_num_queues(process_group)); + + queue_id = + ia_css_process_group_get_base_queue_id(process_group) + + queue_offset; + verifexit(queue_id < IA_CSS_N_PSYS_CMD_QUEUE_ID); + + cmd_queue_full = ia_css_is_psys_cmd_queue_full(ia_css_process_group_get_context(process_group), queue_id); + retval = EBUSY; + verifexit(cmd_queue_full == false); + + psys_cmd.command = command; + psys_cmd.msg = 0; + psys_cmd.context_handle = + ia_css_buffer_set_get_ipu_address(buffer_set); + + retval = ia_css_psys_cmd_queue_send(ia_css_process_group_get_context(process_group), queue_id, &psys_cmd); + verifexit(retval > 0); + + retval = 0; + +EXIT: + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "enqueue_buffer_set failed (%i)\n", retval); + } + return retval; +} + +int ia_css_enqueue_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_enqueue_buffer_set():\n"); + retval = enqueue_buffer_set_cmd( + process_group, + buffer_set, + queue_offset, + IA_CSS_PROCESS_GROUP_CMD_RUN); + + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_enqueue_buffer_set failed (%i)\n", retval); + } + return retval; +} + +int ia_css_enqueue_param_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *param_buffer_set) +{ +#if (HAS_LATE_BINDING_SUPPORT == 1) + int retval = -1; + + IA_CSS_TRACE_0(BXT_SPCTRL, INFO, + "ia_css_enqueue_param_buffer_set():\n"); + + retval = enqueue_buffer_set_cmd( + process_group, + param_buffer_set, + IA_CSS_PSYS_LATE_BINDING_QUEUE_OFFSET, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT); + + if (0 != retval) { + IA_CSS_TRACE_1(BXT_SPCTRL, ERROR, + "ia_css_enqueue_param_buffer_set failed (%i)\n", retval); + } +#else + int retval = -1; + + NOT_USED(process_group); + NOT_USED(param_buffer_set); + IA_CSS_TRACE_0(BXT_SPCTRL, ERROR, + "ia_css_enqueue_param_buffer_set failed, no late binding supported\n"); +#endif /* (HAS_LATE_BINDING_SUPPORT == 1) */ + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h new file mode 100644 index 000000000000..6ccca1d9b69e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data.h @@ -0,0 +1,418 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_H +#define __IA_CSS_PROGRAM_GROUP_DATA_H + +#include "ia_css_psys_data_storage_class.h" + +/*! \file */ + +/** @file ia_css_program_group_data.h + * + * Define the data objects that are passed to the process groups + * i.e. frames and matrices with their sub-structures + * + * The data objects are separate from the process group terminal, + * although they are stored by value rather than by reference and + * make the process group terminal dependendent on its definition + * + * This frame definition overloads the current CSS frame definition + * they are the same object, just a slightly different implementation + */ + +#include /* vied_vaddress_t */ + +#include +#include "ia_css_program_group_data_defs.h" /* ia_css_frame_format_type */ + +#include "ia_css_terminal_defs.h" + +/* + * Frame buffer state used for sequencing + * (see FAS 5.5.3) + * + * The buffer can be in DDR or a handle to a stream + */ +typedef enum ia_css_buffer_state { + IA_CSS_BUFFER_NULL = 0, + IA_CSS_BUFFER_UNDEFINED, + IA_CSS_BUFFER_EMPTY, + IA_CSS_BUFFER_NONEMPTY, + IA_CSS_BUFFER_FULL, + IA_CSS_N_BUFFER_STATES +} ia_css_buffer_state_t; + +#define IA_CSS_BUFFER_STATE_IN_BITS 32 + +/* + * Pointer state used to signal MMU invalidation + */ +typedef enum ia_css_pointer_state { + IA_CSS_POINTER_INVALID = 0, + IA_CSS_POINTER_VALID, + IA_CSS_N_POINTER_STATES +} ia_css_pointer_state_t; + +#define IA_CSS_POINTER_STATE_IN_BITS 32 + +/* + * Access direction needed to select the access port + */ +typedef enum ia_css_access_type { + IA_CSS_ACCESS_LOCKED = 0, + IA_CSS_ACCESS_READ, + IA_CSS_ACCESS_WRITE, + IA_CSS_ACCESS_MODIFY, + IA_CSS_N_ACCESS_TYPES +} ia_css_access_type_t; + +#define IA_CSS_ACCESS_TYPE_IN_BITS 32 + +/* + * Access attribute needed to select the access port + * - public : snooped + * - private: non-snooped + * Naming is a bit awkward, lack of inspiration + */ +typedef enum ia_css_access_scope { + IA_CSS_ACCESS_PRIVATE = 0, + IA_CSS_ACCESS_PUBLIC, + IA_CSS_N_ACCESS_SCOPES +} ia_css_access_scopes_t; + +#define IA_CSS_ACCESS_SCOPES_IN_BITS 32 + +#define IA_CSS_N_FRAME_PLANES 6 + +#define IA_CSS_FRAME_FORMAT_BITMAP_BITS 64 +typedef uint64_t ia_css_frame_format_bitmap_t; + +typedef struct ia_css_param_frame_descriptor_s ia_css_param_frame_descriptor_t; +typedef struct ia_css_param_frame_s ia_css_param_frame_t; + +typedef struct ia_css_frame_descriptor_s ia_css_frame_descriptor_t; +typedef struct ia_css_frame_s ia_css_frame_t; +typedef struct ia_css_fragment_descriptor_s ia_css_fragment_descriptor_t; + +typedef struct ia_css_stream_s ia_css_stream_t; + + +#define N_UINT64_IN_STREAM_STRUCT 1 + +#define IA_CSS_STREAM_STRUCT_BITS \ + (N_UINT64_IN_STREAM_STRUCT * 64) + +struct ia_css_stream_s { + uint64_t dummy; +}; + +struct ia_css_param_frame_descriptor_s { + uint16_t size; /**< Size of the descriptor */ + uint32_t buffer_count; /**< Number of parameter buffers */ +}; + +struct ia_css_param_frame_s { + /*< Base virtual addresses to parameters in subsystem virtual + * memory space + */ + vied_vaddress_t *data; +}; + +#define N_UINT32_IN_FRAME_DESC_STRUCT \ + (1 + IA_CSS_N_FRAME_PLANES + (IA_CSS_N_DATA_DIMENSION - 1)) +#define N_UINT16_IN_FRAME_DESC_STRUCT (1 + IA_CSS_N_DATA_DIMENSION) +#define N_UINT8_IN_FRAME_DESC_STRUCT 3 +#define N_PADDING_UINT8_IN_FRAME_DESC_STRUCT 3 + +#define IA_CSS_FRAME_DESCRIPTOR_STRUCT_BITS \ + (IA_CSS_FRAME_FORMAT_TYPE_BITS \ + + (N_UINT32_IN_FRAME_DESC_STRUCT * 32) \ + + (N_UINT16_IN_FRAME_DESC_STRUCT * 16) \ + + (N_UINT8_IN_FRAME_DESC_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_FRAME_DESC_STRUCT * 8)) + +/* + * Structure defining the frame (size and access) properties for + * inbuild types only. + * + * The inbuild types like FourCC, MIPI and CSS private types are supported + * by FW all other types are custom types which interpretation must be encoded + * on the buffer itself or known by the source and sink + */ +struct ia_css_frame_descriptor_s { + /**< Indicates if this is a generic type or inbuild with + * variable size descriptor + */ + ia_css_frame_format_type_t frame_format_type; + /**< Number of data planes (pointers) */ + uint32_t plane_count; + /**< Plane offsets accounting for fragments */ + uint32_t plane_offsets[IA_CSS_N_FRAME_PLANES]; + /**< Physical size aspects */ + uint32_t stride[IA_CSS_N_DATA_DIMENSION - 1]; + /**< Logical dimensions */ + uint16_t dimension[IA_CSS_N_DATA_DIMENSION]; + /**< Size of this descriptor */ + uint16_t size; + /**< Bits per pixel */ + uint8_t bpp; + /**< Bits per element */ + uint8_t bpe; + /**< 1 if terminal uses compressed datatype, 0 otherwise */ + uint8_t is_compressed; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_DESC_STRUCT]; +}; + +#define N_UINT32_IN_FRAME_STRUCT 2 +#define N_PADDING_UINT8_IN_FRAME_STRUCT 4 + +#define IA_CSS_FRAME_STRUCT_BITS \ + (IA_CSS_BUFFER_STATE_IN_BITS \ + + IA_CSS_ACCESS_TYPE_IN_BITS \ + + IA_CSS_POINTER_STATE_IN_BITS \ + + IA_CSS_ACCESS_SCOPES_IN_BITS \ + + VIED_VADDRESS_BITS \ + + (N_UINT32_IN_FRAME_STRUCT * 32) \ + + (N_PADDING_UINT8_IN_FRAME_STRUCT * 8)) + + +/* + * Main frame structure holding the main store and auxilary access properties + * the "pointer_state" and "access_scope" should be encoded on the + * "vied_vaddress_t" type + */ +struct ia_css_frame_s { + /**< State of the frame for purpose of sequencing */ + ia_css_buffer_state_t buffer_state; + /**< Access direction, may change when buffer state changes */ + ia_css_access_type_t access_type; + /**< State of the pointer for purpose of embedded MMU coherency */ + ia_css_pointer_state_t pointer_state; + /**< Access to the pointer for purpose of host cache coherency */ + ia_css_access_scopes_t access_scope; + /**< Base virtual address to data in subsystem virtual memory space */ + vied_vaddress_t data; + /**< Offset to buffer address within external buffer set structure */ + uint32_t data_index; + /**< Total allocation size in bytes */ + uint32_t data_bytes; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_STRUCT]; +}; + +#define N_UINT16_IN_FRAGMENT_DESC_STRUCT (3 * IA_CSS_N_DATA_DIMENSION) +#define N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT 4 + +#define IA_CSS_FRAGMENT_DESCRIPTOR_STRUCT_BITS \ + ((N_UINT16_IN_FRAME_DESC_STRUCT * 16) \ + + (N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT * 8)) + +/* + * Structure defining the fragment (size and access) properties. + * + * All cropping and padding effects are described by the difference between + * the frame size and its location and the fragment size(s) and location(s) + */ +struct ia_css_fragment_descriptor_s { + /**< Logical dimensions of the fragment */ + uint16_t dimension[IA_CSS_N_DATA_DIMENSION]; + /**< Logical location of the fragment in the frame */ + uint16_t index[IA_CSS_N_DATA_DIMENSION]; + /**< Fractional start (phase) of the fragment in the access unit */ + uint16_t offset[IA_CSS_N_DATA_DIMENSION]; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_FRAGMENT_DESC_STRUCT]; +}; + + +/*! Print the frame object to file/stream + + @param frame[in] frame object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_print( + const ia_css_frame_t *frame, void *fid); + +/*! Get the data buffer handle from the frame object + +@param frame[in] frame object + +@return buffer pointer, VIED_NULL on error +*/ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +const vied_vaddress_t *ia_css_frame_get_buffer_host_virtual_address( + const ia_css_frame_t *frame); + +/*! Get the data buffer handle from the frame object + + @param frame[in] frame object + + @return buffer pointer, VIED_NULL on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +vied_vaddress_t ia_css_frame_get_buffer(const ia_css_frame_t *frame); + +/*! Set the data buffer handle on the frame object + + @param frame[in] frame object + @param buffer[in] buffer pointer + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_buffer( + ia_css_frame_t *frame, vied_vaddress_t buffer); + +/*! Get the data buffer index in the frame object + + @param frame[in] frame object + + @return data buffer index on success, -1 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_get_data_index( + const ia_css_frame_t *frame); + +/*! Set the data buffer index in the frame object + + @param frame[in] frame object + @param data_index[in] data buffer index + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_data_index( + ia_css_frame_t *frame, + unsigned int data_index); + +/*! Set the data buffer size on the frame object + + @param frame[in] frame object + @param size[in] number of data bytes + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_data_bytes( + ia_css_frame_t *frame, unsigned size); + +/*! Get the data buffer state from the frame object + + @param frame[in] frame object + + @return buffer state, limit value on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_buffer_state_t ia_css_frame_get_buffer_state( + const ia_css_frame_t *frame); + +/*! Set the data buffer state of the frame object + + @param frame[in] frame object + @param buffer_state[in] buffer state + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_buffer_state(ia_css_frame_t *frame, + const ia_css_buffer_state_t buffer_state); + +/*! Get the data pointer state from the frame object + + @param frame[in] frame object + + @return pointer state, limit value on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_pointer_state_t ia_css_frame_get_pointer_state( + const ia_css_frame_t *frame); + +/*! Set the data pointer state of the frame object + + @param frame[in] frame object + @param pointer_state[in] pointer state + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_set_pointer_state(ia_css_frame_t *frame, + const ia_css_pointer_state_t pointer_state); + +/*! Print the frame descriptor object to file/stream + + @param frame_descriptor[in] frame descriptor object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_frame_descriptor_print( + const ia_css_frame_descriptor_t *frame_descriptor, void *fid); + +/*! Print the fragment descriptor object to file/stream + + @param fragment_descriptor[in] fragment descriptor object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +int ia_css_fragment_descriptor_print( + const ia_css_fragment_descriptor_t *fragment_descriptor, void *fid); + +/*! Compute the bitmap for the frame format type + + @param frame_format_type[in] frame format type + + @return 0 on error + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_frame_format_bitmap_t ia_css_frame_format_bit_mask( + const ia_css_frame_format_type_t frame_format_type); + +/*! clear frame format bitmap + + @return cleared bitmap + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +ia_css_frame_format_bitmap_t ia_css_frame_format_bitmap_clear(void); + + +/*! Compute the size of storage required for the data descriptor object + * on a terminal + *@param plane_count[in] The number of data planes in the buffer + */ +IA_CSS_PSYS_DATA_STORAGE_CLASS_H +size_t ia_css_sizeof_frame_descriptor( + const uint8_t plane_count); +/*! Compute the size of storage required for the kernel parameter descriptor + * object on a terminal + + @param section_count[in] The number of parameter sections in the buffer + + @return 0 on error + */ +extern size_t ia_css_sizeof_kernel_param_descriptor( + const uint16_t section_count); + +#ifdef __IA_CSS_PSYS_DATA_INLINE__ +#include "ia_css_program_group_data_impl.h" +#endif /* __IA_CSS_PSYS_DATA_INLINE__ */ + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h new file mode 100644 index 000000000000..3f177a19b98b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_program_group_data_defs.h @@ -0,0 +1,196 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H +#define __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H + + +/* + * Pre-defined frame format + * + * Those formats have inbuild support of traffic + * and access functions + * + * Note that the formats are for terminals, so there + * is no distinction between input and output formats + * - Custom formats with ot without descriptor + * - 4CC formats such as YUV variants + * - MIPI (line) formats as produced by CSI receivers + * - MIPI (sensor) formats such as Bayer or RGBC + * - CSS internal formats (private types) + * - CSS parameters (type 1 - 6) + */ +#define IA_CSS_FRAME_FORMAT_TYPE_BITS 32 +typedef enum ia_css_frame_format_type { + IA_CSS_DATA_CUSTOM_NO_DESCRIPTOR = 0, + IA_CSS_DATA_CUSTOM, + + /* 12 bit YUV 411, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV11, + /* bpp bit YUV 420, Y, U, V 3-plane (bpp/1.5 bpe) */ + IA_CSS_DATA_FORMAT_YUV420, + /* 12 bit YUV 420, Y, V, U 3-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_YV12, + /* 12 bit YUV 420, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV12, + /* 16 bit YUV 420, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV12_16, + /* 12 bit YUV 420, Intel proprietary tiled format, TileY */ + IA_CSS_DATA_FORMAT_NV12_TILEY, + /* 12 bit YUV 420, Y, VU 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV21, + /* bpp bit YUV 422, Y, U, V 3-plane (bpp/2 bpe) */ + IA_CSS_DATA_FORMAT_YUV422, + /* 16 bit YUV 422, Y, V, U 3-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_YV16, + /* 16 bit YUV 422, Y, UV 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV16, + /* 16 bit YUV 422, Y, VU 2-plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_NV61, + /* 16 bit YUV 422, UYVY 1-plane interleaved (8 bit per element) */ + IA_CSS_DATA_FORMAT_UYVY, + /* 16 bit YUV 422, YUYV 1-plane interleaved (8 bit per element) */ + IA_CSS_DATA_FORMAT_YUYV, + /* bpp bit YUV 444, Y, U, V 3-plane (bpp/3 bpe) */ + IA_CSS_DATA_FORMAT_YUV444, + /* 8 bit monochrome plane */ + IA_CSS_DATA_FORMAT_Y800, + + /* 5-6-5 bit packed (1-plane) RGB (16bpp, ~5 bpe) */ + IA_CSS_DATA_FORMAT_RGB565, + /* 24 bit RGB, 3 planes (8 bit per element) */ + IA_CSS_DATA_FORMAT_RGB888, + /* 32 bit RGB-Alpha, 1 plane (8 bit per element) */ + IA_CSS_DATA_FORMAT_RGBA888, + + /* bpp bit raw, [[Gr, R];[B, Gb]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_GRBG, + /* bpp bit raw, [[R, Gr];[Gb, B]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_RGGB, + /* bpp bit raw, [[B, Gb];[Gr, R]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_BGGR, + /* bpp bit raw, [[Gb, B];[R, Gr]] 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_BAYER_GBRG, + + /* bpp bit (NV12) YUV 420, Y, UV 2-plane derived 3-line, + * 2-Y, 1-UV (bpp/1.5 bpe): M420 format + */ + IA_CSS_DATA_FORMAT_YUV420_LINE, + /* Deprecated RAW, 1 plane */ + IA_CSS_DATA_FORMAT_RAW, + /* Deprecated RAW, 1 plane, packed */ + IA_CSS_DATA_FORMAT_RAW_PACKED, + /* Internal, for advanced ISP */ + IA_CSS_DATA_FORMAT_QPLANE6, + /* 1D byte stream, used for jpeg 1-plane */ + IA_CSS_DATA_FORMAT_BINARY_8, + /* Deprecated MIPI frame, 1D byte stream 1 plane */ + IA_CSS_DATA_FORMAT_MIPI, + /* 12 bit [[YY];[UYVY]] 1-plane interleaved 2-line + * (8 bit per element) + */ + IA_CSS_DATA_FORMAT_MIPI_YUV420_8, + /* 15 bit [[YY];[UYVY]] 1-plane interleaved 2-line + * (10 bit per element) + */ + IA_CSS_DATA_FORMAT_MIPI_YUV420_10, + /* 12 bit [[UY];[VY]] 1-plane interleaved 2-line (8 bit per element) */ + IA_CSS_DATA_FORMAT_MIPI_LEGACY_YUV420_8, + + /* Type 1-5 parameter, not fragmentable */ + IA_CSS_DATA_GENERIC_PARAMETER, + /* Video stabilisation Type 6 parameter, fragmentable */ + IA_CSS_DATA_DVS_PARAMETER, + /* Video stabilisation Type 6 parameter, coordinates */ + IA_CSS_DATA_DVS_COORDINATES, + /* Dead Pixel correction Type 6 parameter, fragmentable */ + IA_CSS_DATA_DPC_PARAMETER, + /* Lens Shading Correction Type 6 parameter, fragmentable */ + IA_CSS_DATA_LSC_PARAMETER, + /* 3A statistics output HI. */ + IA_CSS_DATA_S3A_STATISTICS_HI, + /* 3A statistics output LO. */ + IA_CSS_DATA_S3A_STATISTICS_LO, + /* histogram output */ + IA_CSS_DATA_S3A_HISTOGRAM, + /* GammaStar grid */ + IA_CSS_DATA_GAMMASTAR_GRID, + + /* Gr R B Gb Gr R B Gb in PIXELS (also called isys interleaved) */ + IA_CSS_DATA_FORMAT_BAYER_LINE_INTERLEAVED, + /* Gr R B Gb Gr R B Gb in VECTORS (VCC IMAGE, ISP NWAY depentdent) */ + IA_CSS_DATA_FORMAT_BAYER_VECTORIZED, + /* Gr R Gr R ... | B Gb B Gb .. in VECTORS (ISP NWAY depentdent) */ + IA_CSS_DATA_FORMAT_BAYER_GRBG_VECTORIZED, + + /* 16 bit YUV 420, Y even plane, Y uneven plane, + * UV plane vector interleaved + */ + IA_CSS_DATA_FORMAT_YUV420_VECTORIZED, + /* 16 bit YUV 420, YYUVYY vector interleaved */ + IA_CSS_DATA_FORMAT_YYUVYY_VECTORIZED, + + /* 12 bit YUV 420, Intel proprietary tiled format, TileYf */ + IA_CSS_DATA_FORMAT_NV12_TILEYF, + + /*Y samples appear first in the memory. All Y samples are array of WORDs; + * even number of lines ; + * Surface stride can be larger than the width of Y plane. + * This array is followed immediately by chroma array. + * Chroma array is an array of WORDs, with interleaved U/V samples. + * If the interleaved U/V plane is addresses as an * array of DWORDs, + * the least significant word contains U sample. The stride of the + * interleaved U/V plane is equal to Y plane. 10 bit data. + */ + IA_CSS_DATA_FORMAT_P010, + + /* MSB aligned version of P010*/ + IA_CSS_DATA_FORMAT_P010_MSB, + + /* P016/P012 Y samples appear first in the memory. + * All Y samples are array of WORDs; + * even number of lines ; + * Surface stride can be larger than the width of Y plane. + * This array is followed immediately by chroma array. + * Chroma array is an array of WORDs, with interleaved U/V samples. + * If the interleaved U/V plane is addresses as an * array of DWORDs, + * the least significant word contains U sample. The stride of the + * interleaved U/V plane is equal to Y plane. 12 bit data. + */ + IA_CSS_DATA_FORMAT_P016, + + /* MSB aligned version of P016*/ + IA_CSS_DATA_FORMAT_P016_MSB, + + /* TILEYYf representation of P010*/ + IA_CSS_DATA_FORMAT_P010_TILEYF, + + /* TILEYYf representation of P010 MSB aligned*/ + IA_CSS_DATA_FORMAT_P010_MSB_TILEYF, + + /* TILEYYf representation of P016*/ + IA_CSS_DATA_FORMAT_P016_TILEYF, + + /* TILEYYf representation of P016 MSB aligned*/ + IA_CSS_DATA_FORMAT_P016_MSB_TILEYF, + + /* consists of L and R PDAF pixel pairs. + * L and R can be interleaved or not. 1-plane (bpp == bpe) */ + IA_CSS_DATA_FORMAT_PAF, + + IA_CSS_N_FRAME_FORMAT_TYPES +} ia_css_frame_format_type_t; + + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_DEFS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h new file mode 100644 index 000000000000..6a4e3a28e533 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DATA_STORAGE_CLASS_H +#define __IA_CSS_PSYS_DATA_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_DATA_INLINE__ +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_DATA_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_DATA_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h new file mode 100644 index 000000000000..49afed9ce9df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/interface/ia_css_psys_data_trace.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DATA_TRACE_H +#define __IA_CSS_PSYS_DATA_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DATA_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DATA_TRACING_OVERRIDE)) + #define PSYS_DATA_TRACE_LEVEL_CONFIG PSYS_DATA_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DATA_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DATA_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DATA_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DATA_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DATA_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DATA_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DATA_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DATA_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DATA_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DATA_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DATA_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DATA_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c new file mode 100644 index 000000000000..edf3e55e6c39 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data.c @@ -0,0 +1,26 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_data_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifdef __IA_CSS_PSYS_DATA_INLINE__ +STORAGE_CLASS_INLINE int +__ia_css_program_group_data_avoid_warning_on_empty_file(void) { return 0; } +#else /* __IA_CSS_PSYS_DATA_INLINE__ */ +#include "ia_css_program_group_data_impl.h" +#endif /* __IA_CSS_PSYS_DATA_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h new file mode 100644 index 000000000000..f08a057e4480 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/data/src/ia_css_program_group_data_impl.h @@ -0,0 +1,455 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H +#define __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H + +#include "ia_css_program_group_data.h" +#include "ia_css_psys_data_trace.h" +#include "ia_css_terminal_defs.h" +#include /* for verifexit */ +#include /* for COMPILATION_ERROR_IF */ +#include /* for NOT_USED */ + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_print( + const ia_css_frame_t *frame, void *fid) +{ + int retval = -1; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, "ia_css_frame_print(): enter:\n"); + + verifexit(frame != NULL); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbuffer = %d\n", ia_css_frame_get_buffer(frame)); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbuffer_state = %d\n", ia_css_frame_get_buffer_state(frame)); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tbuffer_state = %s\n", + * ia_css_buffer_state_string(ia_css_frame_get_buffer_state(frame))); + */ + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tpointer_state = %d\n", ia_css_frame_get_pointer_state(frame)); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tpointer_state = %s\n", + * ia_css_pointer_state_string(ia_css_frame_get_pointer_state(frame))); + */ + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tdata_bytes = %d\n", frame->data_bytes); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +const vied_vaddress_t *ia_css_frame_get_buffer_host_virtual_address( + const ia_css_frame_t *frame) { + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer_host_virtual_address(): enter:\n"); + + verifexit(frame != NULL); + return &(frame->data); + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer_host_virtual_address invalid argument\n"); + } + return NULL; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +vied_vaddress_t ia_css_frame_get_buffer( + const ia_css_frame_t *frame) +{ + vied_vaddress_t buffer = VIED_NULL; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer(): enter:\n"); + + verifexit(frame != NULL); + buffer = frame->data; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer invalid argument\n"); + } + return buffer; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_buffer( + ia_css_frame_t *frame, + vied_vaddress_t buffer) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_buffer(): enter:\n"); + + verifexit(frame != NULL); + frame->data = buffer; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_buffer failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_get_data_index( + const ia_css_frame_t *frame) +{ + int data_index = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_data_index(): enter:\n"); + + verifexit(frame != NULL); + + data_index = frame->data_index; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_data_index invalid argument\n"); + } + return data_index; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_data_index( + ia_css_frame_t *frame, + unsigned int data_index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_data_index(): enter:\n"); + + verifexit(frame != NULL); + + frame->data_index = data_index; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_data_index failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_data_bytes( + ia_css_frame_t *frame, + unsigned int size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_data_bytes(): enter:\n"); + + verifexit(frame != NULL); + frame->data_bytes = size; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_data_bytes failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_buffer_state_t ia_css_frame_get_buffer_state( + const ia_css_frame_t *frame) +{ + ia_css_buffer_state_t buffer_state = IA_CSS_N_BUFFER_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_buffer_state(): enter:\n"); + + verifexit(frame != NULL); + buffer_state = frame->buffer_state; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_buffer_state invalid argument\n"); + } + return buffer_state; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_buffer_state( + ia_css_frame_t *frame, + const ia_css_buffer_state_t buffer_state) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_buffer_state(): enter:\n"); + + verifexit(frame != NULL); + frame->buffer_state = buffer_state; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_buffer_state failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_pointer_state_t ia_css_frame_get_pointer_state( + const ia_css_frame_t *frame) +{ + ia_css_pointer_state_t pointer_state = IA_CSS_N_POINTER_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_get_pointer_state(): enter:\n"); + + verifexit(frame != NULL); + pointer_state = frame->pointer_state; + +EXIT: + if (NULL == frame) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_get_pointer_state invalid argument\n"); + } + return pointer_state; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_set_pointer_state( + ia_css_frame_t *frame, + const ia_css_pointer_state_t pointer_state) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_set_pointer_state(): enter:\n"); + + verifexit(frame != NULL); + frame->pointer_state = pointer_state; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_set_pointer_state failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_frame_descriptor_print( + const ia_css_frame_descriptor_t *frame_descriptor, + void *fid) +{ + int retval = -1; + int i; + uint8_t frame_plane_count; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_frame_descriptor_print(): enter:\n"); + + COMPILATION_ERROR_IF(IA_CSS_N_DATA_DIMENSION <= 0); + + verifexit(frame_descriptor != NULL); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_frame_descriptor_print(): enter:\n"); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tframe_format_type = %d\n", + frame_descriptor->frame_format_type); + /* IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\tframe_format_type = %s\n", + * ia_css_frame_format_string(frame_descriptor->frame_format_type)); + */ + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbpp = %d\n", frame_descriptor->bpp); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tbpe = %d\n", frame_descriptor->bpe); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tis_compressed = %d\n", frame_descriptor->is_compressed); + + frame_plane_count = IA_CSS_N_FRAME_PLANES; + /* frame_plane_count = + * ia_css_frame_plane_count(frame_descriptor->frame_format_type); + */ + + verifexit(frame_plane_count > 0); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tplane_offsets[%d]: [\n", frame_plane_count); + for (i = 0; i < (int)frame_plane_count - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->plane_offsets[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d ]\n", frame_descriptor->plane_offsets[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tdimension[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->dimension[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", frame_descriptor->dimension[i]); + + COMPILATION_ERROR_IF(0 > (IA_CSS_N_DATA_DIMENSION - 2)); + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\tstride[%d] = {\n", IA_CSS_N_DATA_DIMENSION - 1); + i = 0; + if (IA_CSS_N_DATA_DIMENSION > 2) { + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 2; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", frame_descriptor->stride[i]); + } + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", frame_descriptor->stride[i]); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_frame_descriptor_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +int ia_css_fragment_descriptor_print( + const ia_css_fragment_descriptor_t *fragment_descriptor, + void *fid) +{ + int retval = -1; + int i; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DATA, INFO, + "ia_css_fragment_descriptor_print(): enter:\n"); + + verifexit(fragment_descriptor != NULL); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "dimension[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->dimension[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", fragment_descriptor->dimension[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "index[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->index[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d }\n", fragment_descriptor->index[i]); + + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "offset[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, + "\t%4d,\n", fragment_descriptor->offset[i]); + } + IA_CSS_TRACE_1(PSYSAPI_DATA, INFO, "\t%4d }\n", + fragment_descriptor->offset[i]); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DATA, ERROR, + "ia_css_fragment_descriptor_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_frame_format_bitmap_t ia_css_frame_format_bit_mask( + const ia_css_frame_format_type_t frame_format_type) +{ + ia_css_frame_format_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_format_bit_mask(): enter:\n"); + + if ((frame_format_type < IA_CSS_N_FRAME_FORMAT_TYPES) && + (frame_format_type < IA_CSS_FRAME_FORMAT_BITMAP_BITS)) { + bit_mask = (ia_css_frame_format_bitmap_t)1 << frame_format_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_frame_format_bit_mask invalid argument\n"); + } + + return bit_mask; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +ia_css_frame_format_bitmap_t ia_css_frame_format_bitmap_clear(void) +{ + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_frame_format_bitmap_clear(): enter:\n"); + + return 0; +} + +IA_CSS_PSYS_DATA_STORAGE_CLASS_C +size_t ia_css_sizeof_frame_descriptor( + const uint8_t plane_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DATA, VERBOSE, + "ia_css_sizeof_frame_descriptor(): enter:\n"); + + verifexit(plane_count > 0); + size += sizeof(ia_css_frame_descriptor_t); + size += plane_count * sizeof(uint32_t); + +EXIT: + if (0 == plane_count) { + IA_CSS_TRACE_0(PSYSAPI_DATA, WARNING, + "ia_css_sizeof_frame_descriptor invalid argument\n"); + } + return size; +} + +#endif /* __IA_CSS_PROGRAM_GROUP_DATA_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/cnlB0/ia_css_psys_transport_dep.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/cnlB0/ia_css_psys_transport_dep.h new file mode 100644 index 000000000000..7bb145c1b183 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/cnlB0/ia_css_psys_transport_dep.h @@ -0,0 +1,35 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TRANSPORT_DEP_H +#define __IA_CSS_PSYS_TRANSPORT_DEP_H + +/* + * The ID's of the Psys specific queues. + */ +typedef enum ia_css_psys_cmd_queues { + /**< The in-order queue for scheduled process groups */ + IA_CSS_PSYS_CMD_QUEUE_COMMAND_ID = 0, + /**< The in-order queue for commands changing psys or + * process group state + */ + IA_CSS_PSYS_CMD_QUEUE_DEVICE_ID, + /**< An in-order queue for dedicated PPG commands */ + IA_CSS_PSYS_CMD_QUEUE_PPG0_COMMAND_ID, + /**< An in-order queue for dedicated PPG commands */ + IA_CSS_PSYS_CMD_QUEUE_PPG1_COMMAND_ID, + IA_CSS_N_PSYS_CMD_QUEUE_ID +} ia_css_psys_cmd_queue_ID_t; + +#endif /* __IA_CSS_PSYS_TRANSPORT_DEP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h new file mode 100644 index 000000000000..dc8fa531b11e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device.h @@ -0,0 +1,516 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DEVICE_H +#define __IA_CSS_PSYS_DEVICE_H + +#include "ia_css_psys_init.h" +#include "ia_css_psys_transport.h" + +/*! \file */ + +/** @file ia_css_psys_device.h + * + * Define the interface to open the psys specific communication layer + * instance + */ + +#include /* vied_vaddress_t */ + +#include +#include + +#include +#include + +#define IA_CSS_PSYS_STATE_READY_PATTERN (0xF7F7F7F7) +#define IA_CSS_PSYS_STATE_RUNNING_PATTERN (0xE6E6E6E6) +#define IA_CSS_PSYS_STATE_STARTING_PATTERN (0xD5D5D5D5) +#define IA_CSS_PSYS_STATE_STARTED_PATTERN (0xC4C4C4C4) +#define IA_CSS_PSYS_STATE_INITIALIZING_PATTERN (0xB3B3B3B3) +#define IA_CSS_PSYS_STATE_INITIALIZED_PATTERN (0xA0A0A0A0) + +/* + * Defines the state of psys: + * - IA_CSS_PSYS_STATE_UNKNOWN = psys status is unknown (or not recognized) + * - IA_CSS_PSYS_STATE_INITIALING = some of the psys components are + * not initialized yet + * - IA_CSS_PSYS_STATE_INITIALIZED = psys components are initialized + * - IA_CSS_PSYS_STATE_STARTING = some of the psys components are initialized + * but not started yet + * - IA_CSS_PSYS_STATE_STARTED = psys components are started + * - IA_CSS_PSYS_STATE_RUNNING = some of the psys components are started + * but not ready yet + * - IA_CSS_PSYS_STATE_READY = psys is ready + * The state of psys can be obtained calling ia_css_psys_check_state() +*/ +typedef enum ia_css_psys_state { + IA_CSS_PSYS_STATE_UNKNOWN = 0, /**< psys state is unknown */ + /*< some of the psys components are not initialized yet*/ + IA_CSS_PSYS_STATE_INITIALIZING = IA_CSS_PSYS_STATE_INITIALIZING_PATTERN, + /**< psys components are initialized */ + IA_CSS_PSYS_STATE_INITIALIZED = IA_CSS_PSYS_STATE_INITIALIZED_PATTERN, + /**< some of the psys components are not started yet */ + IA_CSS_PSYS_STATE_STARTING = IA_CSS_PSYS_STATE_STARTING_PATTERN, + /**< psys components are started */ + IA_CSS_PSYS_STATE_STARTED = IA_CSS_PSYS_STATE_STARTED_PATTERN, + /**< some of the psys components are not ready yet */ + IA_CSS_PSYS_STATE_RUNNING = IA_CSS_PSYS_STATE_RUNNING_PATTERN, + /**< psys is ready */ + IA_CSS_PSYS_STATE_READY = IA_CSS_PSYS_STATE_READY_PATTERN, +} ia_css_psys_state_t; + +extern struct ia_css_syscom_context *psys_syscom; +#if HAS_DUAL_CMD_CTX_SUPPORT +extern struct ia_css_syscom_context *psys_syscom_secure; +#endif + +/*! Print the syscom creation descriptor to file/stream + + @param config[in] Psys syscom descriptor + @param fid[out] file/stream handle + + @return < 0 on error +*/ +extern int ia_css_psys_config_print( + const struct ia_css_syscom_config *config, void *fid); + +/*! Print the Psys syscom object to file/stream + + @param context[in] Psys syscom object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_psys_print( + const struct ia_css_syscom_context *context, void *fid); + +/*! Create the syscom creation descriptor + + @return NULL on error + */ +extern struct ia_css_syscom_config *ia_css_psys_specify(void); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Create the syscom creation descriptor for secure stream + + @param vtl0_addr_mask[in] VTL0 address mask that will be stored in 'secure' ctx + @return NULL on error + */ +extern struct ia_css_syscom_config *ia_css_psys_specify_secure(unsigned int vtl0_addr_mask); +#endif + +/*! Compute the size of storage required for allocating the Psys syscom object + + @param config[in] Psys syscom descriptor + + @return 0 on error + */ +extern size_t ia_css_sizeof_psys( + struct ia_css_syscom_config *config); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Open (and map the storage for) the Psys syscom object + This is the same as ia_css_psys_open() excluding server start. + Target for VTIO usage where multiple syscom objects need to be + created first before this API is invoked. + + @param buffer[in] storage buffers for the syscom object + in the kernel virtual memory space and + its Psys mapped version + @param config[in] Psys syscom descriptor + @return NULL on error + */ + +extern struct ia_css_syscom_context *ia_css_psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config); + +/*! Store the parameters of the Psys syscom object in DMEM, so + they can be communicated with FW. This step needs to be invoked + after SPC starts in ia_css_psys_open(), so SPC DMEM access blocker + programming already takes effective. + + @param context[in] Psys syscom object + @param config[in] Psys syscom descriptor + @return 0 if successful + */ +extern int ia_css_psys_context_store_dmem( + struct ia_css_syscom_context *context, + struct ia_css_syscom_config *config); + +/*! Start PSYS Server. Psys syscom object must have been created already. + Target for VTIO usage where multiple syscom objects need to be + created first before this API is invoked. + @param config[in] Psys syscom descriptor + + @return true if psys open started successfully + */ +extern int ia_css_psys_open( + struct ia_css_syscom_config *config); +#else +/*! Open (and map the storage for) the Psys syscom object + + @param buffer[in] storage buffers for the syscom object + in the kernel virtual memory space and + its Psys mapped version + @param config[in] Psys syscom descriptor + + Precondition(1): The buffer must be large enough to hold the syscom object. + Its size must be computed with the function "ia_css_sizeof_psys()". + The buffer must be created in the kernel memory space. + + Precondition(2): If buffer == NULL, the storage allocations and mapping + is performed in this function. Config must hold the handle to the Psys + virtual memory space + + Postcondition: The context is initialised in the provided/created buffer. + The syscom context pointer is the kernel space handle to the syscom object + + @return NULL on error + */ +extern struct ia_css_syscom_context *ia_css_psys_open( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +/*! completes the psys open procedure. Must be called multiple times + until it succeeds or driver determines the boot sequence has failed. + + @param context[in] Psys syscom object + + @return false if psys open has not completed successfully + */ +extern bool ia_css_psys_open_is_ready( + struct ia_css_syscom_context *context); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/*! Request close of a PSYS context + * The functionatlity is the same as ia_css_psys_close() which closes PSYS syscom object. + * Counterpart of ia_css_psys_context_create() + * @param context[in]: Psys context + * @return NULL if close is successful context otherwise + */ +extern struct ia_css_syscom_context *ia_css_psys_context_destroy( + struct ia_css_syscom_context *context); + +/*! Request close of a PSYS device for VTIO case + * @param None + * @return 0 if successful + */ +extern int ia_css_psys_close(void); +#else +/*! Request close of a PSYS context + * @param context[in]: Psys context + * @return NULL if close is successful context otherwise + */ +extern struct ia_css_syscom_context *ia_css_psys_close( + struct ia_css_syscom_context *context); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT*/ + +/*! Unmap and free the storage of the PSYS context + * @param context[in] Psys context + * @param force[in] Force release even if device is busy + * @return 0 if release is successful + * EINVAL if context is invalid + * EBUSY if device is not yet idle, and force==0 + */ +extern int ia_css_psys_release( + struct ia_css_syscom_context *context, + bool force); + +/*! Checks the state of the Psys syscom object + + @param context[in] Psys syscom object + + @return State of the syscom object + */ +extern ia_css_psys_state_t ia_css_psys_check_state( + struct ia_css_syscom_context *context); + +/*!Indicate if the designated cmd queue in the Psys syscom object is full + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return false if the cmd queue is not full or on error + */ + +extern bool ia_css_is_psys_cmd_queue_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if the designated cmd queue in the Psys syscom object is notfull + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return false if the cmd queue is full on error + */ +extern bool ia_css_is_psys_cmd_queue_not_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if the designated cmd queue in the Psys syscom object holds N space + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + @param N[in] Number of messages + + @return false if the cmd queue space is unavailable or on error + */ +extern bool ia_css_has_psys_cmd_queue_N_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const unsigned int N); + +/*!Return the free space count in the designated cmd queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + @return the space, < 0 on error + */ +extern int ia_css_psys_cmd_queue_get_available_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Indicate if there are any messages pending in the Psys syscom + * object event queues + + @param context[in] Psys syscom object + + @return false if there are no messages or on error + */ +extern bool ia_css_any_psys_event_queue_not_empty( + struct ia_css_syscom_context *context); + +/*!Indicate if the designated event queue in the Psys syscom object is empty + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return false if the event queue is not empty or on error + */ +extern bool ia_css_is_psys_event_queue_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Indicate if the designated event queue in the Psys syscom object is not empty + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return false if the receive queue is empty or on error + */ +extern bool ia_css_is_psys_event_queue_not_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Indicate if the designated event queue + * in the Psys syscom object holds N items + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param N[in] Number of messages + + @return false if the event queue has insufficient messages + available or on error +*/ +extern bool ia_css_has_psys_event_queue_N_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + const unsigned int N); + +/*!Return the message count in the designated event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + @return the messages, < 0 on error + */ +extern int ia_css_psys_event_queue_get_available_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*! Send (pass by value) a command on a queue in the Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID +@param cmd_msg_buffer[in] pointer to the command message buffer + +Precondition: The command message buffer must be large enough + to hold the command + +Postcondition: Either 0 or 1 commands have been sent + +Note: The message size is fixed and determined on creation + + @return the number of sent commands (1), <= 0 on error + */ +extern int ia_css_psys_cmd_queue_send( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer); + +/*! Send (pass by value) N commands on a queue in the Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + @param cmd_msg_buffer[in] Pointer to the command message buffer +@param N[in] Number of commands + +Precondition: The command message buffer must be large enough + to hold the commands + +Postcondition: Either 0 or up to and including N commands have been sent + + Note: The message size is fixed and determined on creation + + @return the number of sent commands, <= 0 on error + */ +extern int ia_css_psys_cmd_queue_send_N( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer, + const unsigned int N); + +/*! Receive (pass by value) an event from an event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param event_msg_buffer[out] pointer to the event message buffer + + Precondition: The event message buffer must be large enough to hold the event + + Postcondition: Either 0 or 1 events have been received + + Note: The event size is fixed and determined on creation + + @return the number of received events (1), <= 0 on error + */ +extern int ia_css_psys_event_queue_receive( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer); + +/*! Receive (pass by value) N events from an event queue in the + * Psys syscom object + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + @param event_msg_buffer[out] pointer to the event message buffer + @param N[in] Number of events + + Precondition: The event buffer must be large enough to hold the events + + Postcondition: Either 0 or up to and including N events have been received + + Note: The message size is fixed and determined on creation + + @return the number of received event messages, <= 0 on error + */ +extern int ia_css_psys_event_queue_receive_N( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer, + const unsigned int N); + + +/* + * Access functions to query the object stats + */ + + +/*!Return the size of the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern size_t ia_css_psys_get_size( + const struct ia_css_syscom_context *context); + +/*!Return the number of cmd queues in the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern unsigned int ia_css_psys_get_cmd_queue_count( + const struct ia_css_syscom_context *context); + +/*!Return the number of event queues in the Psys syscom object + + @param context[in] Psys syscom object + + @return 0 on error + */ +extern unsigned int ia_css_psys_get_event_queue_count( + const struct ia_css_syscom_context *context); + +/*!Return the size of the indicated Psys command queue + + @param context[in] Psys syscom object + @param id[in] Psys syscom cmd queue ID + + Note: The queue size is expressed in the number of fields + + @return 0 on error + */ +extern size_t ia_css_psys_get_cmd_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Return the size of the indicated Psys event queue + + @param context[in] Psys syscom object + @param id[in] Psys syscom event queue ID + + Note: The queue size is expressed in the number of fields + + @return 0 on error + */ +extern size_t ia_css_psys_get_event_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +/*!Return the command message size of the indicated Psys command queue + + @param context[in] Psys syscom object + + Note: The message size is expressed in uint8_t + + @return 0 on error + */ +extern size_t ia_css_psys_get_cmd_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id); + +/*!Return the event message size of the indicated Psys event queue + + @param context[in] Psys syscom object + + Note: The message size is expressed in uint8_t + + @return 0 on error + */ +extern size_t ia_css_psys_get_event_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id); + +#endif /* __IA_CSS_PSYS_DEVICE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h new file mode 100644 index 000000000000..8e5899bc66db --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_device_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DEVICE_TRACE_H +#define __IA_CSS_PSYS_DEVICE_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DEVICE_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DEVICE_TRACING_OVERRIDE)) + #define PSYS_DEVICE_TRACE_LEVEL_CONFIG \ + PSYS_DEVICE_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DEVICE_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DEVICE_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DEVICE_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DEVICE_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DEVICE_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DEVICE_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DEVICE_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DEVICE_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DEVICE_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DEVICE_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DEVICE_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h new file mode 100644 index 000000000000..1120b357632c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_init.h @@ -0,0 +1,37 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_INIT_H +#define __IA_CSS_PSYS_INIT_H + +#include /* vied_vaddress_t */ + +/* Init parameters passed to the fw on device open (non secure mode) */ +typedef struct ia_css_psys_server_init { + /* These members are used in PSS only and will be removed */ + /* Shared memory host address of pkg dir */ + unsigned long long host_ddr_pkg_dir; + /* Address of pkg_dir structure in DDR */ + vied_vaddress_t ddr_pkg_dir_address; + /* Size of Package dir in DDR */ + uint32_t pkg_dir_size; + + /* Prefetch configiration */ + /* enable prefetching on SPC, SPP0 and SPP1 */ + uint32_t icache_prefetch_sp; + /* enable prefetching on ISP0..N */ + uint32_t icache_prefetch_isp; +} ia_css_psys_server_init_t; + +#endif /* __IA_CSS_PSYS_INIT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h new file mode 100644 index 000000000000..e0d1e935c221 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/interface/ia_css_psys_transport.h @@ -0,0 +1,92 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TRANSPORT_H +#define __IA_CSS_PSYS_TRANSPORT_H + +#include /* ia_css_psys_cmd_queues */ +#include /* vied_vaddress_t */ + +#include + +typedef enum ia_css_psys_event_queues { + /**< The in-order queue for event returns */ + IA_CSS_PSYS_EVENT_QUEUE_MAIN_ID, + IA_CSS_N_PSYS_EVENT_QUEUE_ID +} ia_css_psys_event_queue_ID_t; + +typedef enum ia_css_psys_event_types { + /**< No error to report. */ + IA_CSS_PSYS_EVENT_TYPE_SUCCESS = 0, + /**< Unknown unhandled error */ + IA_CSS_PSYS_EVENT_TYPE_UNKNOWN_ERROR = 1, + /* Retrieving remote object: */ + /**< Object ID not found */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_NOT_FOUND = 2, + /**< Objects too big, or size is zero. */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_TOO_BIG = 3, + /**< Failed to load whole process group from tproxy/dma */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_DDR_TRANS_ERR = 4, + /**< The proper package could not be found */ + IA_CSS_PSYS_EVENT_TYPE_RET_REM_OBJ_NULL_PKG_DIR_ADDR = 5, + /* Process group: */ + /**< Failed to run, error while loading frame */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAME_ERR = 6, + /**< Failed to run, error while loading fragment */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_LOAD_FRAGMENT_ERR = 7, + /**< The process count of the process group is zero */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_COUNT_ZERO = 8, + /**< Process(es) initialization */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_PROCESS_INIT_ERR = 9, + /**< Aborted (after host request) */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_ABORT = 10, + /**< NULL pointer in the process group */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_NULL = 11, + /**< Process group validation failed */ + IA_CSS_PSYS_EVENT_TYPE_PROC_GRP_VALIDATION_ERR = 12 +} ia_css_psys_event_type_t; + +#define IA_CSS_PSYS_CMD_BITS 64 +struct ia_css_psys_cmd_s { + /**< The command issued to the process group */ + uint16_t command; + /**< Message field of the command */ + uint16_t msg; + /**< The context reference (process group/buffer set/...) */ + uint32_t context_handle; +}; + +#define IA_CSS_PSYS_EVENT_BITS 128 +struct ia_css_psys_event_s { + /**< The (return) status of the command issued to + * the process group this event refers to + */ + uint16_t status; + /**< The command issued to the process group this event refers to */ + uint16_t command; + /**< The context reference (process group/buffer set/...) */ + uint32_t context_handle; + /**< This token (size) must match the token registered + * in a process group + */ + uint64_t token; +}; + +struct ia_css_psys_buffer_s { + /**< The in-order queue for scheduled process groups */ + void *host_buffer; + vied_vaddress_t *isp_buffer; +}; + +#endif /* __IA_CSS_PSYS_TRANSPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c new file mode 100644 index 000000000000..106fe0a0da85 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/device/src/ia_css_psys_device.c @@ -0,0 +1,853 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include "ia_css_psys_device.h" +#include "ia_css_psys_device_trace.h" +#include "ia_css_psys_init.h" +#include "regmem_access.h" + +#include +#include +#include + +#include "ia_css_cell.h" + +#define IA_CSS_PSYS_CMD_QUEUE_SIZE 0x20 +#define IA_CSS_PSYS_EVENT_QUEUE_SIZE 0x40 + +static struct ia_css_syscom_queue_config ia_css_psys_cmd_queue_cfg[IA_CSS_N_PSYS_CMD_QUEUE_ID]; + +static struct ia_css_syscom_queue_config + ia_css_psys_event_queue_cfg[IA_CSS_N_PSYS_EVENT_QUEUE_ID] = { + {IA_CSS_PSYS_EVENT_QUEUE_SIZE, IA_CSS_PSYS_EVENT_BITS/8}, +}; + +static struct ia_css_syscom_config psys_syscom_config; +struct ia_css_syscom_context *psys_syscom; +#if HAS_DUAL_CMD_CTX_SUPPORT +static struct ia_css_syscom_config psys_syscom_config_secure; +struct ia_css_syscom_context *psys_syscom_secure; +#endif +static bool external_alloc = true; + +int ia_css_psys_config_print( + const struct ia_css_syscom_config *config, + void *fh) +{ + int retval = -1; + + NOT_USED(fh); + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_frame_print(): enter:\n"); + + verifexit(config != NULL); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DEVICE, ERROR, + "ia_css_frame_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_psys_print( + const struct ia_css_syscom_context *context, + void *fh) +{ + int retval = -1; + + NOT_USED(fh); + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_print(): enter:\n"); + + verifexit(context != NULL); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_print failed (%i)\n", retval); + } + return retval; +} + +static void set_syscom_config(struct ia_css_syscom_config *config) +{ + int i; + config->num_input_queues = IA_CSS_N_PSYS_CMD_QUEUE_ID; + config->num_output_queues = IA_CSS_N_PSYS_EVENT_QUEUE_ID; + /* The number of queues are different for different platforms + * so the array is initialized here + */ + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + ia_css_psys_cmd_queue_cfg[i].queue_size = IA_CSS_PSYS_CMD_QUEUE_SIZE; + ia_css_psys_cmd_queue_cfg[i].token_size = IA_CSS_PSYS_CMD_BITS/8; + } + config->input = ia_css_psys_cmd_queue_cfg; + config->output = ia_css_psys_event_queue_cfg; + config->vtl0_addr_mask = 0; +} + +struct ia_css_syscom_config *ia_css_psys_specify(void) +{ + struct ia_css_syscom_config *config = &psys_syscom_config; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_specify(): enter:\n"); + set_syscom_config(config); + config->secure = false; + + return config; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_config *ia_css_psys_specify_secure(unsigned int vtl0_addr_mask) +{ + struct ia_css_syscom_config *config = &psys_syscom_config_secure; + + IA_CSS_TRACE_1(PSYSAPI_DEVICE, INFO, "ia_css_psys_specify_secure(mask %#x): enter:\n", vtl0_addr_mask); + set_syscom_config(config); + config->secure = true; + config->vtl0_addr_mask = vtl0_addr_mask; + return config; +} +#endif + +size_t ia_css_sizeof_psys( + struct ia_css_syscom_config *config) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_sizeof_psys(): enter:\n"); + + NOT_USED(config); + + return size; +} + +/* Internal function to create syscom_context */ +static struct ia_css_syscom_context *psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + struct ia_css_syscom_context *context; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_context_create(): enter:\n"); + + if (config == NULL) + goto EXIT; + + if (buffer == NULL) { + /* Allocate locally */ + external_alloc = false; + } + + /* + * Here we would like to pass separately the sub-system ID + * and optionally the user pointer to be mapped, depending on + * where this open is called, and which virtual memory handles + * we see here. + */ + /* context = ia_css_syscom_open(get_virtual_memory_handle(vied_psys_ID), + * buffer, config); + */ + context = ia_css_syscom_open(config, NULL); + if (context == NULL) + goto EXIT; + + return context; + +EXIT: + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, "psys_context_create failed\n"); + return NULL; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_context *ia_css_psys_context_create( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + return psys_context_create(buffer, config); +} + +/* push context information to DMEM for FW to access */ +int ia_css_psys_context_store_dmem( + struct ia_css_syscom_context *context, + struct ia_css_syscom_config *config) +{ + return ia_css_syscom_store_dmem(context, config->ssid, config->vtl0_addr_mask); +} +#endif + +/* Internal function to start psys server */ +static int psys_start_server( + struct ia_css_syscom_config *config) +{ + ia_css_psys_server_init_t *server_config; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_start_server(): enter:\n"); + + /* Configure SPC icache prefetching and start SPC */ + server_config = (ia_css_psys_server_init_t *)config->specific_addr; + IA_CSS_TRACE_1(PSYSAPI_DEVICE, INFO, "SPC prefetch: %d\n", + server_config->icache_prefetch_sp); + ia_css_cell_start_prefetch(config->ssid, SPC0, + server_config->icache_prefetch_sp); + return 0; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +int ia_css_psys_open( + struct ia_css_syscom_config *config) +{ + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_open(): enter:\n"); + return psys_start_server(config); +} +#else +struct ia_css_syscom_context *ia_css_psys_open( + const struct ia_css_psys_buffer_s *buffer, + struct ia_css_syscom_config *config) +{ + struct ia_css_syscom_context *context; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "ia_css_psys_open(): enter:\n"); + + context = psys_context_create(buffer, config); + + /* Configure SPC icache prefetching and start SPC */ + psys_start_server(config); + + return context; +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +bool ia_css_psys_open_is_ready( + struct ia_css_syscom_context *context) +{ + int retval = -1; + bool ready = 0; + unsigned int i; + int syscom_retval; + + verifexit(context != NULL); + + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + syscom_retval = ia_css_syscom_send_port_open(context, i); + if (syscom_retval != 0) { + if (syscom_retval == FW_ERROR_BUSY) { + /* Do not print error */ + retval = 0; + } + /* Not ready yet */ + goto EXIT; + } + } + + for (i = 0; i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + syscom_retval = ia_css_syscom_recv_port_open(context, i); + if (syscom_retval != 0) { + if (syscom_retval == FW_ERROR_BUSY) { + /* Do not print error */ + retval = 0; + } + /* Not ready yet */ + goto EXIT; + } + } + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "ia_css_psys_open_is_ready(): complete:\n"); + + /* If this point reached, do not print error */ + retval = 0; + /* If this point reached, ready */ + ready = 1; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_open_is_ready failed\n"); + } + return ready; +} + +/* Internal function to close syscom_context */ +static struct ia_css_syscom_context *psys_context_destroy( + struct ia_css_syscom_context *context) +{ + /* Success: return NULL, Error: return context pointer value + * Intention is to change return type to int (errno), + * see commented values. + */ + + unsigned int i; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, "psys_context_destroy(): enter:\n"); + + /* NULL pointer check disabled, since there is no proper return value */ + + for (i = 0; i < IA_CSS_N_PSYS_CMD_QUEUE_ID; i++) { + if (ia_css_syscom_send_port_close(context, i) != 0) + return context; /* EINVAL */ + } + + for (i = 0; i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + if (ia_css_syscom_recv_port_close(context, i) != 0) + return context; /* EINVAL */ + } + + /* request device close */ + if (ia_css_syscom_close(context) != 0) + return context; /* EBUSY */ + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "psys_context_destroy(): leave: OK\n"); + return NULL; +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +struct ia_css_syscom_context *ia_css_psys_context_destroy( + struct ia_css_syscom_context *context) +{ + return psys_context_destroy(context); +} + +int ia_css_psys_close() +{ + /* Intentionally left blank for now since syscom objects should have + * been destroyed already by prior ia_css_psys_context_destroy() calls. + */ + return 0; +} +#else +struct ia_css_syscom_context *ia_css_psys_close( + struct ia_css_syscom_context *context) +{ + return psys_context_destroy(context); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +int ia_css_psys_release( + struct ia_css_syscom_context *context, + bool force) +{ + if (context == NULL) + return -EFAULT; + + /* try to free resources */ + if (ia_css_syscom_release(context, force) != 0) + return -EBUSY; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, INFO, + "ia_css_psys_release(): leave: OK\n"); + return 0; +} + +ia_css_psys_state_t ia_css_psys_check_state( + struct ia_css_syscom_context *context) +{ + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_check_state(): enter:\n"); + + NOT_USED(context); + + /* For the time being, return the READY state to be used by SPC test */ + return IA_CSS_PSYS_STATE_READY; +} + +bool ia_css_is_psys_cmd_queue_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + bool is_full = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_cmd_queue_full(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_full = (num_tokens == 0); + retval = 0; +EXIT: + if (retval != 0) { + is_full = true; + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_cmd_queue_full failed\n"); + } + return is_full; +} + +bool ia_css_is_psys_cmd_queue_not_full( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + bool is_not_full = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_cmd_queue_not_full(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_not_full = (num_tokens != 0); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_cmd_queue_not_full failed\n"); + } + return is_not_full; +} + +bool ia_css_has_psys_cmd_queue_N_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const unsigned int N) +{ + bool has_N_space = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_has_psys_cmd_queue_N_space(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + has_N_space = ((unsigned int)num_tokens >= N); +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_has_psys_cmd_queue_N_space failed\n"); + } + return has_N_space; +} + +int ia_css_psys_cmd_queue_get_available_space( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + int N_space = -1; + int num_tokens; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_get_available_space(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_send_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + N_space = (int)(num_tokens); +EXIT: + if (N_space < 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_get_available_space failed\n"); + } + return N_space; +} + +bool ia_css_any_psys_event_queue_not_empty( + struct ia_css_syscom_context *context) +{ + ia_css_psys_event_queue_ID_t i; + bool any_msg = false; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_any_psys_event_queue_not_empty(): enter:\n"); + verifexit(context != NULL); + + for (i = (ia_css_psys_event_queue_ID_t)0; + i < IA_CSS_N_PSYS_EVENT_QUEUE_ID; i++) { + any_msg = + any_msg || ia_css_is_psys_event_queue_not_empty(context, i); + } + +EXIT: + return any_msg; +} + +bool ia_css_is_psys_event_queue_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + bool is_empty = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_event_queue_empty(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, (unsigned int)id); + verifexit(num_tokens >= 0); + + is_empty = (num_tokens == 0); + retval = 0; +EXIT: + if (retval != 0) { + is_empty = true; + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_event_queue_empty failed\n"); + } + return is_empty; +} + +bool ia_css_is_psys_event_queue_not_empty( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + bool is_not_empty = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_is_psys_event_queue_not_empty(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + is_not_empty = (num_tokens != 0); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_is_psys_event_queue_not_empty failed\n"); + } + return is_not_empty; +} + +bool ia_css_has_psys_event_queue_N_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + const unsigned int N) +{ + bool has_N_msgs = false; + int num_tokens; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_has_psys_event_queue_N_msgs(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + has_N_msgs = ((unsigned int)num_tokens >= N); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_has_psys_event_queue_N_msgs failed\n"); + } + return has_N_msgs; +} + +int ia_css_psys_event_queue_get_available_msgs( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + int N_msgs = -1; + int num_tokens; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_get_available_msgs(): enter:\n"); + verifexit(context != NULL); + + num_tokens = ia_css_syscom_recv_port_available(context, + (unsigned int)id); + verifexit(num_tokens >= 0); + + N_msgs = (int)(num_tokens); +EXIT: + if (N_msgs < 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_get_available_msgs failed\n"); + } + return N_msgs; +} + +int ia_css_psys_cmd_queue_send( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer) +{ + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_send(): enter:\n"); + verifexit(context != NULL); + + verifexit(context != NULL); + /* The ~full check fails on receive queues */ + verifexit(ia_css_is_psys_cmd_queue_not_full(context, id)); + verifexit(cmd_msg_buffer != NULL); + + verifexit(ia_css_syscom_send_port_transfer(context, (unsigned int)id, + cmd_msg_buffer) >= 0); + + count = 1; +EXIT: + if (count == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_send failed\n"); + } + return count; +} + +int ia_css_psys_cmd_queue_send_N( + struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id, + const void *cmd_msg_buffer, + const unsigned int N) +{ + struct ia_css_psys_cmd_s *cmd_msg_buffer_loc = + (struct ia_css_psys_cmd_s *)cmd_msg_buffer; + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_cmd_queue_send_N(): enter:\n"); + verifexit(context != NULL); + + for (count = 0; count < (int)N; count++) { + int count_loc = ia_css_psys_cmd_queue_send(context, id, + (void *)(&cmd_msg_buffer_loc[count])); + + verifexit(count_loc == 1); + } + +EXIT: + if ((unsigned int) count < N) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_cmd_queue_send_N failed\n"); + } + return count; +} + +int ia_css_psys_event_queue_receive( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer) +{ + int count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_receive(): enter:\n"); + + verifexit(context != NULL); + /* The ~empty check fails on send queues */ + verifexit(ia_css_is_psys_event_queue_not_empty(context, id)); + verifexit(event_msg_buffer != NULL); + + verifexit(ia_css_syscom_recv_port_transfer(context, (unsigned int)id, + event_msg_buffer) >= 0); + + count = 1; +EXIT: + if (count == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_receive failed\n"); + } + return count; +} + +int ia_css_psys_event_queue_receive_N( + struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id, + void *event_msg_buffer, + const unsigned int N) +{ + struct ia_css_psys_event_s *event_msg_buffer_loc; + int count; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_event_queue_receive_N(): enter:\n"); + + event_msg_buffer_loc = (struct ia_css_psys_event_s *)event_msg_buffer; + + for (count = 0; count < (int)N; count++) { + int count_loc = ia_css_psys_event_queue_receive(context, id, + (void *)(&event_msg_buffer_loc[count])); + + verifexit(count_loc == 1); + } + +EXIT: + if ((unsigned int) count < N) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_event_queue_receive_N failed\n"); + } + return count; +} + +size_t ia_css_psys_get_size( + const struct ia_css_syscom_context *context) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ +EXIT: + if (size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_size failed\n"); + } + return size; +} + +unsigned int ia_css_psys_get_cmd_queue_count( + const struct ia_css_syscom_context *context) +{ + unsigned int count = 0; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_queue_count(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + count = (unsigned int)IA_CSS_N_PSYS_CMD_QUEUE_ID; + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_queue_count failed\n"); + } + return count; +} + +unsigned int ia_css_psys_get_event_queue_count( + const struct ia_css_syscom_context *context) +{ + unsigned int count = 0; + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_queue_count(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + count = (unsigned int)IA_CSS_N_PSYS_EVENT_QUEUE_ID; + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_event_queue_count failed\n"); + } + return count; +} + +size_t ia_css_psys_get_cmd_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + size_t queue_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_queue_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + queue_size = ia_css_psys_cmd_queue_cfg[id].queue_size; +EXIT: + if (queue_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_queue_size failed\n"); + } + return queue_size; +} + +size_t ia_css_psys_get_event_queue_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + size_t queue_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_queue_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + queue_size = ia_css_psys_event_queue_cfg[id].queue_size; +EXIT: + if (queue_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_event_queue_size failed\n"); + } + return queue_size; +} + +size_t ia_css_psys_get_cmd_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_cmd_queue_ID_t id) +{ + size_t msg_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_cmd_msg_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + msg_size = ia_css_psys_cmd_queue_cfg[id].token_size; +EXIT: + if (msg_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_msg_size failed\n"); + } + return msg_size; +} + +size_t ia_css_psys_get_event_msg_size( + const struct ia_css_syscom_context *context, + ia_css_psys_event_queue_ID_t id) +{ + size_t msg_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DEVICE, VERBOSE, + "ia_css_psys_get_event_msg_size(): enter:\n"); + + verifexit(context != NULL); + /* How can I query the context ? */ + NOT_USED(context); + msg_size = ia_css_psys_event_queue_cfg[id].token_size; +EXIT: + if (msg_size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DEVICE, ERROR, + "ia_css_psys_get_cmd_msg_size failed\n"); + } + return msg_size; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h new file mode 100644 index 000000000000..392b4359353f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_buffer_set.h @@ -0,0 +1,174 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYS_BUFFER_SET_H +#define __IA_CSS_PSYS_BUFFER_SET_H + +#include "ia_css_base_types.h" +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_process_types.h" +#include "ia_css_terminal_types.h" + +#define N_UINT64_IN_BUFFER_SET_STRUCT 1 +#define N_UINT16_IN_BUFFER_SET_STRUCT 1 +#define N_UINT8_IN_BUFFER_SET_STRUCT 1 +#define N_PADDING_UINT8_IN_BUFFER_SET_STRUCT 5 +#define SIZE_OF_BUFFER_SET \ + (N_UINT64_IN_BUFFER_SET_STRUCT * IA_CSS_UINT64_T_BITS \ + + VIED_VADDRESS_BITS \ + + VIED_VADDRESS_BITS \ + + N_UINT16_IN_BUFFER_SET_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_UINT8_IN_BUFFER_SET_STRUCT * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_BUFFER_SET_STRUCT * IA_CSS_UINT8_T_BITS) + +typedef struct ia_css_buffer_set_s ia_css_buffer_set_t; + +struct ia_css_buffer_set_s { + /* Token for user context reference */ + uint64_t token; + /* IPU virtual address of this buffer set */ + vied_vaddress_t ipu_virtual_address; + /* IPU virtual address of the process group corresponding to this buffer set */ + vied_vaddress_t process_group_handle; + /* Number of terminal buffer addresses in this structure */ + uint16_t terminal_count; + /* Frame id to associate with this buffer set */ + uint8_t frame_counter; + /* Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_BUFFER_SET_STRUCT]; +}; + + +/*! Construct a buffer set object at specified location + + @param buffer_set_mem[in] memory location to create buffer set object + @param process_group[in] process group corresponding to this buffer set + @param frame_counter[in] frame number for this buffer set object + + @return pointer to buffer set object on success, NULL on error + */ +ia_css_buffer_set_t *ia_css_buffer_set_create( + void *buffer_set_mem, + const ia_css_process_group_t *process_group, + const unsigned int frame_counter); + +/*! Compute size (in bytes) required for full buffer set object + + @param process_group[in] process group corresponding to this buffer set + + @return size in bytes of buffer set object on success, 0 on error + */ +size_t ia_css_sizeof_buffer_set( + const ia_css_process_group_t *process_group); + +/*! Set a buffer address in a buffer set object + + @param buffer_set[in] buffer set object to set buffer in + @param terminal_index[in] terminal index to use as a reference between + buffer and terminal + @param buffer[in] buffer address to store + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_buffer( + ia_css_buffer_set_t *buffer_set, + const unsigned int terminal_index, + const vied_vaddress_t buffer); + +/*! Get virtual buffer address from a buffer set object and terminal object by + resolving the index used + + @param buffer_set[in] buffer set object to get buffer from + @param terminal[in] terminal object to get buffer of + + @return virtual buffer address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_buffer( + const ia_css_buffer_set_t *buffer_set, + const ia_css_terminal_t *terminal); + +/*! Set ipu virtual address of a buffer set object within the buffer set object + + @param buffer_set[in] buffer set object to set ipu address in + @param ipu_vaddress[in] ipu virtual address of the buffer set object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_ipu_address( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t ipu_vaddress); + +/*! Get ipu virtual address from a buffer set object + + @param buffer_set[in] buffer set object to get ipu address from + + @return virtual buffer set address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_ipu_address( + const ia_css_buffer_set_t *buffer_set); + +/*! Set process group handle in a buffer set object + + @param buffer_set[in] buffer set object to set handle in + @param process_group_handle[in] process group handle of the buffer set + object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_process_group_handle( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t process_group_handle); + +/*! Get process group handle from a buffer set object + + @param buffer_set[in] buffer set object to get handle from + + @return virtual process group address on success, VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_buffer_set_get_process_group_handle( + const ia_css_buffer_set_t *buffer_set); + +/*! Set token of a buffer set object within the buffer set object + + @param buffer_set[in] buffer set object to set ipu address in + @param token[in] token of the buffer set object + + @return 0 on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_buffer_set_set_token( + ia_css_buffer_set_t *buffer_set, + const uint64_t token); + +/*! Get token from a buffer set object + + @param buffer_set[in] buffer set object to get token from + + @return token on success, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint64_t ia_css_buffer_set_get_token( + const ia_css_buffer_set_t *buffer_set); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_buffer_set_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_BUFFER_SET_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h new file mode 100644 index 000000000000..9a1e3a7a1294 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +#define __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h new file mode 100644 index 000000000000..e8a979dfce0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_dynamic_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_DYNAMIC_TRACE_H +#define __IA_CSS_PSYS_DYNAMIC_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_DYNAMIC_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_DYNAMIC_TRACING_OVERRIDE)) + #define PSYS_DYNAMIC_TRACE_LEVEL_CONFIG \ + PSYS_DYNAMIC_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_DYNAMIC_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_DYNAMIC_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_DYNAMIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_DYNAMIC_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_DYNAMIC_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_DYNAMIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h new file mode 100644 index 000000000000..f4ef80f74213 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.h @@ -0,0 +1,396 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_H +#define __IA_CSS_PSYS_PROCESS_H + +/*! \file */ + +/** @file ia_css_psys_process.h + * + * Define the methods on the process object that are not part of + * a single interface + */ + +#include +#include + +#include + +#include /* uint8_t */ + +/* + * Creation + */ +#include + +/* + * Internal resources + */ +#include + +/* + * Process manager + */ +#include + +/* + * Command processor + */ + +/*! Execute a command locally or send it to be processed remotely + + @param process[in] process object + @param cmd[in] command + + @return < 0 on invalid argument(s) or process state + */ +extern int ia_css_process_cmd( + ia_css_process_t *process, + const ia_css_process_cmd_t cmd); + +/*! Get the internal memory offset of the process object + + @param process[in] process object + @param mem_id[in] memory id + + @return internal memory offset, + IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_int_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_id); + + +/*! Get the external memory offset of the process object + + @param process[in] process object + @param mem_id[in] memory id + + @return external memory offset, + IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_ext_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + + +/*! Get the stored size of the process object + + @param process[in] process object + + @return size, 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_process_get_size(const ia_css_process_t *process); + +/*! Get the (pointer to) the process group parent of the process object + + @param process[in] process object + + @return the pointer to the parent, NULL on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_t *ia_css_process_get_parent( + const ia_css_process_t *process); + +/*! Set the (pointer to) the process group parent of the process object + + @param process[in] process object + @param parent[in] (pointer to the) process group parent object + + @return < 0 on invalid argument(s) + */ +extern int ia_css_process_set_parent( + ia_css_process_t *process, + ia_css_process_group_t *parent); + +/*! Get the unique ID of program used by the process object + + @param process[in] process object + + @return ID, 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_ID_t ia_css_process_get_program_ID( + const ia_css_process_t *process); + +/*! Get the state of the process object + + @param process[in] process object + + @return state, limit value (IA_CSS_N_PROCESS_STATES) on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_state_t ia_css_process_get_state( + const ia_css_process_t *process); + +/*! Set the state of the process object + + @param process[in] process object + @param state[in] state of the process + + @return < 0 on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_set_state( + ia_css_process_t *process, + ia_css_process_state_t state); + +/*! Get the assigned cell of the the process object + + @param process[in] process object + + @return cell ID, limit value (VIED_NCI_N_CELL_ID) on invalid argument + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_cell_ID_t ia_css_process_get_cell( + const ia_css_process_t *process); + +/*! Get the number of cells the process object depends on + + @param process[in] process object + + @return number of cells + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_cell_dependency_count( + const ia_css_process_t *process); + +/*! Get the number of terminals the process object depends on + + @param process[in] process object + + @return number of terminals + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_terminal_dependency_count( + const ia_css_process_t *process); + +/*! Set n-th cell dependency of a process object + + @param process[in] Process object + @param dep_index[in] dep index + @param id[in] dep id + + @return < 0 on invalid process argument + */ +extern int ia_css_process_set_cell_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id); + +/*! Get n-th cell dependency of a process object + + @param process[in] Process object + @param cell_num[in] n-th cell + + @return n-th cell dependency, + IA_CSS_PROCESS_INVALID_DEPENDENCY on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_id_t ia_css_process_get_cell_dependency( + const ia_css_process_t *process, + const unsigned int cell_num); + +/*! Set n-th terminal dependency of a process object + + @param process[in] Process object + @param dep_index[in] dep index + @param id[in] dep id + + @return < 0 on on invalid argument(s) + */ +extern int ia_css_process_set_terminal_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id); + +/*! Get n-th terminal dependency of a process object + + @param process[in] Process object + @param terminal_num[in] n-th cell + + @return n-th terminal dependency, + IA_CSS_PROCESS_INVALID_DEPENDENCY on invalid argument(s) +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_get_terminal_dependency( + const ia_css_process_t *process, + const unsigned int terminal_num); + +/*! Get the kernel bitmap of the the process object + + @param process[in] process object + + @return process kernel bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_kernel_bitmap_t ia_css_process_get_kernel_bitmap( + const ia_css_process_t *process); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_port_bitmap_ptr( + ia_css_process_t *process); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_active_port_bitmap_ptr( + ia_css_process_t *process); + + +/*! Get the cells bitmap of the the process object + + @param process[in] process object + + @return process cells bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_cells_bitmap( + const ia_css_process_t *process); + +/*! Sets the dfm device resource allocation bitmap of + * the process object + + @param process[in] process object + @param dfm_dev_id[in] dfm device id + @param bitmap[in] resource bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dfm_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap); + + +/*! Sets the active dfm ports bitmap of + * the process object + + @param process[in] process object + @param dfm_dev_id[in] dfm device id + @param bitmap[in] active ports bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dfm_active_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get the dfm port bitmap of the the process object + + @param process[in] process object + @param dfm_res_id dfm resource id + + @return bitmap of all DFM ports used by process, corresponding to the input dfm resource id + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_dfm_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id); + +/*! Get the dfm active port bitmap of the the process object + + @param process[in] process object + @param dfm_res_id[in] dfm resource id + + @return bitmap of all active DFM ports used by the process, corresponding to the input + dfm resource id + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_get_dfm_active_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id); + + +/*! Sets the cells bitmap of + * the process object + + @param process[in] process object + @param bitmap[in] bitmap + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_cells_bitmap( + ia_css_process_t *process, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get the device channel id-n resource allocation offset of the process object + + @param process[in] process object + @param dev_chn_id[in] channel id + + @return resource offset, IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_size_t ia_css_process_get_dev_chn( + const ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Get the ext mem type-n resource id of the the process object + + @param process[in] process object + @param mem_type[in] mem type + + @return resource offset, IA_CSS_PROCESS_INVALID_OFFSET on invalid argument(s) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_mem_ID_t ia_css_process_get_ext_mem_id( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type); + + +/*! Sets the device channel id-n resource allocation offset of + * the process object + + @param process[in] process object + @param dev_chn_id[in] channel id + @param offset[in] resource offset + + @return < 0 on invalid argument(s) or process state + */ +int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset); + +/*! Boolean test if the process object type is valid + + @param process[in] process object + @param p_manifest[in] program manifest + + @return true if the process object is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_process_valid( + const ia_css_process_t *process, + const ia_css_program_manifest_t *p_manifest); + +/*! Gets the program_idx from the process object + + @param process[in] process object + + @return program index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_get_program_idx( + const ia_css_process_t *process); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h new file mode 100644 index 000000000000..cab796560414 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.kernel.h @@ -0,0 +1,144 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_process.hsys.kernel.h + * + * Define the methods on the process object: Hsys kernel interface + */ + +#include + +#include + +/* + * Internal resources + */ + +/*! Clear all resource (offset) specifications + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_clear_all(ia_css_process_t *process); + +/*! Set the cell ID resource specification + + @param process[in] process object + @param cell_id[in] cell ID + + @return < 0 on error + */ +extern int ia_css_process_set_cell( + ia_css_process_t *process, + const vied_nci_cell_ID_t cell_id); + +/*! Clear cell ID resource specification + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_clear_cell(ia_css_process_t *process); + +/*! Set the memory resource (offset) specification for a memory + that belongs to the cell that is assigned to the process + + @param process[in] process object + @param mem_type_id[in] mem type ID + @param offset[in] offset + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_set_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t offset); + +/*! Clear the memory resource (offset) specification for a memory + type that belongs to the cell that is assigned to the process + + @param process[in] process object + @param mem_id[in] mem ID + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_clear_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (offset) specification for a memory + that does not belong to the cell that is assigned to the process + + @param process[in] process object + @param mem_type_id[in] mem type ID + @param offset[in] offset + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_set_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_ID_t mem_id, + const vied_nci_resource_size_t offset); + +/*! Clear the memory resource (offset) specification for a memory + type that does not belong to the cell that is assigned to the process + + @param process[in] process object + @param mem_id[in] mem ID + + Precondition: The cell ID must be set + + @return < 0 on error + */ +extern int ia_css_process_clear_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set a device channel resource (offset) specification + + @param process[in] process object + @param dev_chn_id[in] device channel ID + @param offset[in] offset + + @return < 0 on error + */ +extern int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset); + +/*! Clear a device channel resource (offset) specification + + @param process[in] process object + @param dev_chn_id[in] device channel ID + + @return < 0 on error + */ +extern int ia_css_process_clear_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id); + +#endif /* __IA_CSS_PSYS_PROCESS_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h new file mode 100644 index 000000000000..015a60b0e1af --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.hsys.user.h @@ -0,0 +1,85 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_HSYS_USER_H +#define __IA_CSS_PSYS_PROCESS_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_process.hsys.user.h + * + * Define the methods on the process object: Hsys user interface + */ + +#include /* ia_css_program_param_t */ + +#include +#include + +#include /* uint8_t */ + +/* + * Creation + */ + +/*! Compute the size of storage required for allocating the process object + + @param manifest[in] program manifest + @param param[in] program parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_process( + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param); + +/*! Create the process object + + @param raw_mem[in] pre allocated memory + @param manifest[in] program manifest + @param param[in] program parameters + + @return NULL on error + */ +extern ia_css_process_t *ia_css_process_create( + void *raw_mem, + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param, + const uint32_t program_idx); + +/*! Destroy (the storage of) the process object + + @param process[in] process object + + @return NULL + */ +extern ia_css_process_t *ia_css_process_destroy( + ia_css_process_t *process); + +/* + * Access functions + */ + +/*! Print the process object to file/stream + + @param process[in] process object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_process_print( + const ia_css_process_t *process, + void *fid); + +#endif /* __IA_CSS_PSYS_PROCESS_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h new file mode 100644 index 000000000000..ba1db574a438 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process.psys.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_PSYS_H +#define __IA_CSS_PSYS_PROCESS_PSYS_H + +/*! \file */ + +/** @file ia_css_psys_process.psys.h + * + * Define the methods on the process object: Psys embedded interface + */ + +#include + +/* + * Process manager + */ + +/*! Acquire the resources specificed in process object + + @param process[in] process object + + Postcondition: This is a try process if any of the + resources is not available, all succesfully acquired + ones will be release and the function will return an + error + + @return < 0 on error + */ +extern int ia_css_process_acquire(ia_css_process_t *process); + +/*! Release the resources specificed in process object + + @param process[in] process object + + @return < 0 on error + */ +extern int ia_css_process_release(ia_css_process_t *process); + + +#endif /* __IA_CSS_PSYS_PROCESS_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h new file mode 100644 index 000000000000..c0f6901adeb0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.h @@ -0,0 +1,366 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_H +#define __IA_CSS_PSYS_PROCESS_GROUP_H + +/*! \file */ + +/** @file ia_css_psys_process_group.h + * + * Define the methods on the process object that are not part of + * a single interface + */ +#include "ia_css_rbm.h" + +#include +#include + +#include /* uint8_t */ + +/* + * Creation + */ +#include + +/* + * Registration of user contexts / callback info + * External resources + * Sequencing resources + */ +#include + +/* + * Dispatcher + */ +#include + +/* + * Access to sub-structure handles / fields + */ + +#include "ia_css_terminal.h" + +/*! Get the number of fragments on the process group + + @param process_group[in] process group object + + Note: Future change is to have a fragment count per + independent subgraph + + @return the fragment count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_process_group_get_fragment_count( + const ia_css_process_group_t *process_group); + + +/*! Get the fragment state on the process group + + @param process_group[in] process group object + @param fragment_state[in] current fragment of processing + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_get_fragment_state( + const ia_css_process_group_t *process_group, + uint16_t *fragment_state); + +/*! Set the fragment state on the process group + + @param process_group[in] process group object + @param fragment_state[in] current fragment of processing + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_fragment_state( + ia_css_process_group_t *process_group, + uint16_t fragment_state); + +/*! Get the number of processes on the process group + + @param process_group[in] process group object + + @return the process count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_process_count( + const ia_css_process_group_t *process_group); + +/*! Get the number of terminals on the process group + + @param process_group[in] process group object + + Note: Future change is to have a terminal count per + independent subgraph + + @return the terminal count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_terminal_count( + const ia_css_process_group_t *process_group); + +/*! Get the PG load start timestamp + + @param process_group[in] process group object + + @return PG load start timestamp, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_load_start_ts( + const ia_css_process_group_t *process_group); + +/*! Get the PG load time in cycles + + @param process_group[in] process group object + + @return PG load time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_load_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the PG init time in cycles + + @param process_group[in] process group object + + @return PG init time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_init_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the PG processing time in cycles + + @param process_group[in] process group object + + @return PG processing time in cycles, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_get_pg_processing_cycles( + const ia_css_process_group_t *process_group); + +/*! Get the (pointer to) the terminal of the process group object + + @param process_group[in] process group object + @param terminal_type[in] terminal type of terminal + + @return the pointer to the terminal, NULL on error + */ + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_t *ia_css_process_group_get_terminal_from_type( + const ia_css_process_group_t *process_group, + const ia_css_terminal_type_t terminal_type); + +/*! Get the (pointer to) the terminal of the process group object + * for terminals which have only a single instance + * (cached in, cached out, program, program_ctrl_init) + + @param process_group[in] process group object + @param terminal_type[in] terminal type of terminal + + @return the pointer to the terminal, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +const ia_css_terminal_t *ia_css_process_group_get_single_instance_terminal( + const ia_css_process_group_t *process_group, + ia_css_terminal_type_t term_type); + +/*! Get the (pointer to) the indexed terminal of the process group object + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + @return the pointer to the terminal, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_t *ia_css_process_group_get_terminal( + const ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/*! Get the (pointer to) the indexed process of the process group object + + @param process_group[in] process group object + @param process_index[in] index of the process + + @return the pointer to the process, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_t *ia_css_process_group_get_process( + const ia_css_process_group_t *process_group, + const unsigned int process_index); + +/*! Get the stored size of the process group object + + @param process_group[in] process group object + + @return size, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_process_group_get_size( + const ia_css_process_group_t *process_group); + +/*! Get the state of the the process group object + + @param process_group[in] process group object + + @return state, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_state_t ia_css_process_group_get_state( + const ia_css_process_group_t *process_group); + +/*! Get the unique ID of program group used by the process group object + + @param process_group[in] process group object + + @return ID, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_group_ID_t ia_css_process_group_get_program_group_ID( + const ia_css_process_group_t *process_group); + +/*! Get the resource bitmap of the process group + + @param process_group[in] process group object + + @return the reource bitmap + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_nci_resource_bitmap_t ia_css_process_group_get_resource_bitmap( + const ia_css_process_group_t *process_group); + +/*! Set the resource bitmap of the process group + + @param process_group[in] process group object + @param resource_bitmap[in] the resource bitmap + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_resource_bitmap( + ia_css_process_group_t *process_group, + const vied_nci_resource_bitmap_t resource_bitmap); + +/*! Get the routing bitmap of the process group + + @param process_group[in] process group object + + @return routing bitmap (pointer) + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +const ia_css_rbm_t *ia_css_process_group_get_routing_bitmap( + const ia_css_process_group_t *process_group); + +/*! Set the routing bitmap of the process group + + @param process_group[in] process group object + @param rbm[in] routing bitmap + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_routing_bitmap( + ia_css_process_group_t *process_group, + const ia_css_rbm_t rbm); + +/*! Get IPU virtual address of process group + + @param process_group[in] process group object + @param ipu_vaddress[in/out] process group ipu virtual address + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_get_ipu_vaddress( + const ia_css_process_group_t *process_group, + vied_vaddress_t *ipu_vaddress); + +/*! Set IPU virtual address of process group + + @param process_group[in] process group object + @param ipu_vaddress[in] process group ipu address + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_ipu_vaddress( + ia_css_process_group_t *process_group, + vied_vaddress_t ipu_vaddress); + +/*! Get protocol version used by a process group + + @param process_group[in] process group object + + @return invalid protocol version on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_protocol_version( + const ia_css_process_group_t *process_group); + +/*! Get base queue id used by a process group + + @param process_group[in] process group object + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_base_queue_id( + ia_css_process_group_t *process_group); + +/*! Set base queue id used by a process group + + @param process_group[in] process group object + @param queue_id[in] process group queue id + + @return invalid queue id on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_base_queue_id( + ia_css_process_group_t *process_group, + uint8_t queue_id); + +/*! Get number of queues used by a process group + + @param process_group[in] process group object + + @return invalid number of queues (0) on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_process_group_get_num_queues( + ia_css_process_group_t *process_group); + +/*! Set number of queues used by a process group + + @param process_group[in] process group object + @param num_queues[in] process group number of queues + + @return -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_num_queues( + ia_css_process_group_t *process_group, + uint8_t num_queues); + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_process_group_has_vp(const ia_css_process_group_t *process_group); + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_group_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h new file mode 100644 index 000000000000..93cce2555de9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.kernel.h @@ -0,0 +1,324 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_process_group.hsys.kernel.h + * + * Define the methods on the process group object: Hsys kernel interface + */ + +#include + +#include +#include + +#include /* uint8_t */ + +/* + * Registration of user contexts / callback info + */ + +/*! Get the user (callback) token as registered in the process group + + @param process_group[in] process group object + + @return 0 on error + */ +extern uint64_t ia_css_process_group_get_token( + ia_css_process_group_t *process_group); + +/*! Set (register) a user (callback) token in the process group + + @param process_group[in] process group object + @param token[in] user token + + Note: The token value shall be non-zero. This token is + returned in each return message related to the process + group the token is registered with. + + @return < 0 on error + */ +extern int ia_css_process_group_set_token( + ia_css_process_group_t *process_group, + const uint64_t token); + +/* + * Passing of a (fragment) watermark + */ + +/*! Get the fragment progress limit of the process group + + @param process_group[in] process group object + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_process_group_get_fragment_limit( + const ia_css_process_group_t *process_group); + +/*! Set the new fragment progress limit of the process group + + @param process_group[in] process group object + @param fragment_limit[in] New limit value + + Note: The limit value must be less or equal to the fragment + count value. The process group will not make progress beyond + the limit value. The limit value can be modified asynchronously + If the limit value is reached before an update happens, the + process group will suspend and will not automatically resume. + + The limit is monotonically increasing. The default value is + equal to the fragment count + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_fragment_limit( + ia_css_process_group_t *process_group, + const uint16_t fragment_limit); + +/*! Clear the fragment progress limit of the process group + + @param process_group[in] process group object + + Note: This function sets the fragment limit to zero. + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_clear_fragment_limit( + ia_css_process_group_t *process_group); + +/* + * Commands + */ + +/*! Perform the start command on the process group + + @param process_group[in] process group object + + Note: Start is an action of the l-Scheduler it makes the + process group eligible for execution + + Precondition: The external resources that are attached to + the process group must be in the correct state, i.e. input + buffers are not-empty and output buffers not-full + + @return < 0 on error + */ +extern int ia_css_process_group_start( + ia_css_process_group_t *process_group); + +/*! Perform the suspend command on the process group + + @param process_group[in] process group object + + Note: Suspend indicates that the process group execution + is halted at the next fragment boundary. The process group + will not automatically resume + + Precondition: The process group must be running + + @return < 0 on error + */ +extern int ia_css_process_group_suspend( + ia_css_process_group_t *process_group); + +/*! Perform the resume command on the process group + + @param process_group[in] process group object + + Note: Resume indicates that the process group is again + eligible for execution + + Precondition: The process group must be started + + @return < 0 on error + */ +extern int ia_css_process_group_resume( + ia_css_process_group_t *process_group); + +/*! Perform the reset command on the process group + + @param process_group[in] process group object + + Note: Return the process group to the started state + + Precondition: The process group must be running or stopped + + @return < 0 on error + */ +extern int ia_css_process_group_reset( + ia_css_process_group_t *process_group); + +/*! Perform the abort command on the process group + + @param process_group[in] process group object + + Note: Force the process group to the stopped state + + Precondition: The process group must be running or started + + @return < 0 on error + */ +extern int ia_css_process_group_abort( + ia_css_process_group_t *process_group); + +/*! Release ownership of the process group + + @param process_group[in] process group object + + Note: Release notifies PSYS and hands over ownership of the + process group from SW to FW + + Precondition: The process group must be in the started state + + @return < 0 on error + */ +extern int ia_css_process_group_disown( + ia_css_process_group_t *process_group); + +/* + * External resources + */ + +/*! Set (register) a data buffer to the indexed terminal in the process group + + @param process_group[in] process group object + @param buffer[in] buffer handle + @param buffer_state[in] state of the buffer + @param terminal_index[in] index of the terminal + + Note: The buffer handle shall not be VIED_NULL, the buffer + state can be undefined; BUFFER_UNDEFINED + + Note: The buffer can be in memory or streaming over memory + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_attach_buffer( + ia_css_process_group_t *process_group, + vied_vaddress_t buffer, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index); + +/*! Get (unregister) the data buffer on the indexed terminal of + * the process group + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + Precondition: The process group must be stopped + + Postcondition: The buffer handle shall be reset to VIED_NULL, the buffer + state to BUFFER_NULL + + @return VIED_NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_process_group_detach_buffer( + ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/*! Set (register) a data buffer to the indexed terminal in the process group + + @param process_group[in] process group object + @param stream[in] stream handle + @param buffer_state[in] state of the buffer + @param terminal_index[in] index of the terminal + + Note: The stream handle shall not be zero, the buffer + state can be undefined; BUFFER_UNDEFINED + + Note: The stream is used exclusive to a buffer; the latter can be in memory + or streaming over memory + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_attach_stream( + ia_css_process_group_t *process_group, + uint32_t stream, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index); + +/*! Get (unregister) the stream handle on the indexed terminal of + * the process group + + @param process_group[in] process group object + @param terminal_index[in] index of the terminal + + Precondition: The process group must be stopped + + Postcondition: The stream handle shall be reset to zero, the buffer + state to BUFFER_NULL + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_detach_stream( + ia_css_process_group_t *process_group, + const unsigned int terminal_index); + +/* + * Sequencing resources + */ + +/*! Set a(n artificial) blocking resource (barrier) in + * the process group resource map + + @param process_group[in] process group object + @param barrier_index[in] index of the barrier + + Note: The barriers have to be set to force sequence between started + process groups + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_set_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index); + +/*! Clear a previously set blocking resource (barrier) in + * the process group resource map + + @param process_group[in] process group object + @param barrier_index[in] index of the barrier + + Precondition: The barriers must have been set + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_clear_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index); + +/*! Boolean test if the process group preconditions for start are satisfied + + @param process_group[in] process group object + + @return true if the process group can be started + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_process_group_start( + const ia_css_process_group_t *process_group); + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h new file mode 100644 index 000000000000..dfbcc8815c1e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.hsys.user.h @@ -0,0 +1,199 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H +#define __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_process_group.hsys.user.h + * + * Define the methods on the process group object: Hsys user interface + */ + +#include /* ia_css_program_group_param_t */ + +#include +#include +#include + +#include "ia_css_psys_dynamic_storage_class.h" + +#include /* uint8_t */ + +/* + * Creation + */ + +/*! Compute the size of storage required for allocating the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_process_group( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Create (the storage for) the process group object + + @param process_grp_mem[in/out] raw memory for process group + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return NULL on error + */ +extern ia_css_process_group_t *ia_css_process_group_create( + void *process_grp_mem, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Destroy (the storage of) the process group object + + @param process_group[in] process group object + + @return NULL + */ +extern ia_css_process_group_t *ia_css_process_group_destroy( + ia_css_process_group_t *process_group); + +/*! Print the process group object to file/stream + + @param process_group[in] process group object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_process_group_print( + const ia_css_process_group_t *process_group, + void *fid); + +/* + * Commands + */ + +/*! Perform the submit command on the process group + + @param process_group[in] process group object + + Note: Submit is an action of the h-Scheduler it makes the + process group eligible for the l-Scheduler + + Precondition: The external resources must be attached to + the process group + + @return < 0 on error + */ +extern int ia_css_process_group_submit( + ia_css_process_group_t *process_group); + +/*! Boolean test if the process group object type is valid + + @param process_group[in] process group object + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return true if the process group is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_process_group_valid( + const ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Boolean test if the process group preconditions for submit are satisfied + + @param process_group[in] process group object + + @return true if the process group can be submitted + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_process_group_submit( + const ia_css_process_group_t *process_group); + +/*! Boolean test if the preconditions on process group and buffer set are + satisfied for enqueuing buffer set + + @param process_group[in] process group object + @param buffer_set[in] buffer set object + + @return true if the buffer set can be enqueued + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_can_enqueue_buffer_set( + const ia_css_process_group_t *process_group, + const ia_css_buffer_set_t *buffer_set); + +/*! Compute the cyclecount required for executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint32_t ia_css_process_group_compute_cycle_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Compute the number of processes required for + * executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern uint8_t ia_css_process_group_compute_process_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Compute the number of terminals required for + * executing the process group object + + @param manifest[in] program group manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern uint8_t ia_css_process_group_compute_terminal_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Get private token as registered in the process group by the implementation + + @param process_group[in] process group object + + @return 0 on error + */ +extern uint64_t ia_css_process_group_get_private_token( + ia_css_process_group_t *process_group); + +/*! Set private token in the process group as needed by the implementation + + @param process_group[in] process group object + @param token[in] user token + + Note: The token value shall be non-zero. This token is private + to the implementation. This is in addition to the user token + + @return < 0 on error, 0 on success + */ +extern int ia_css_process_group_set_private_token( + ia_css_process_group_t *process_group, + const uint64_t token); + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h new file mode 100644 index 000000000000..6ceccfc2f9bc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group.psys.h @@ -0,0 +1,60 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H +#define __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H + +/*! \file */ + +/** @file ia_css_psys_process_group.psys.h + * + * Define the methods on the process group object: Psys embedded interface + */ + +#include + +/* + * Dispatcher + */ + +/*! Perform the run command on the process group + + @param process_group[in] process group object + + Note: Run indicates that the process group will execute + + Precondition: The process group must be started or + suspended and the processes have acquired the necessary + internal resources + + @return < 0 on error + */ +extern int ia_css_process_group_run( + ia_css_process_group_t *process_group); + +/*! Perform the stop command on the process group + + @param process_group[in] process group object + + Note: Stop indicates that the process group has completed execution + + Postcondition: The external resoruces can now be detached + + @return < 0 on error + */ +extern int ia_css_process_group_stop( + ia_css_process_group_t *process_group); + + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_PSYS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h new file mode 100644 index 000000000000..530f93ef6ce0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_group_cmd_impl.h @@ -0,0 +1,178 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H + +#include "type_support.h" +#include "ia_css_psys_process_group.h" +#include "ia_css_rbm_manifest_types.h" + +#define N_UINT64_IN_PROCESS_GROUP_STRUCT 2 +#define N_UINT32_IN_PROCESS_GROUP_STRUCT 5 +#define N_UINT16_IN_PROCESS_GROUP_STRUCT 5 +#define N_UINT8_IN_PROCESS_GROUP_STRUCT 7 +#define N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT 3 + +#define SIZE_OF_PROCESS_GROUP_STRUCT_BITS \ + (IA_CSS_RBM_BITS \ + + N_UINT64_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT64_T_BITS \ + + N_UINT32_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT32_T_BITS \ + + IA_CSS_PROGRAM_GROUP_ID_BITS \ + + IA_CSS_PROCESS_GROUP_STATE_BITS \ + + VIED_VADDRESS_BITS \ + + VIED_NCI_RESOURCE_BITMAP_BITS \ + + N_UINT16_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_UINT8_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_process_group_s { + /**< User (callback) token / user context reference, + * zero is an error value + */ + uint64_t token; + /**< private token / context reference, zero is an error value */ + uint64_t private_token; + /**< PG routing bitmap used to set connection between programs >*/ + ia_css_rbm_t routing_bitmap; + /**< Size of this structure */ + uint32_t size; + /**< The timestamp when PG load starts */ + uint32_t pg_load_start_ts; + /**< PG load time in cycles */ + uint32_t pg_load_cycles; + /**< PG init time in cycles */ + uint32_t pg_init_cycles; + /**< PG processing time in cycles */ + uint32_t pg_processing_cycles; + /**< Referral ID to program group FW */ + ia_css_program_group_ID_t ID; + /**< State of the process group FSM */ + ia_css_process_group_state_t state; + /**< Virtual address of process group in IPU */ + vied_vaddress_t ipu_virtual_address; + /**< Bitmap of the compute resources used by the process group */ + vied_nci_resource_bitmap_t resource_bitmap; + /**< Number of fragments offered on each terminal */ + uint16_t fragment_count; + /**< Current fragment of processing */ + uint16_t fragment_state; + /**< Watermark to control fragment processing */ + uint16_t fragment_limit; + /**< Array[process_count] of process addresses in this process group */ + uint16_t processes_offset; + /**< Array[terminal_count] of terminal addresses on this process group */ + uint16_t terminals_offset; + /**< Parameter dependent number of processes in this process group */ + uint8_t process_count; + /**< Parameter dependent number of terminals on this process group */ + uint8_t terminal_count; + /**< Parameter dependent number of independent subgraphs in + * this process group + */ + uint8_t subgraph_count; + /**< Process group protocol version */ + uint8_t protocol_version; + /**< Dedicated base queue id used for enqueueing payload buffer sets */ + uint8_t base_queue_id; + /**< Number of dedicated queues used */ + uint8_t num_queues; + /**< Mask the send_pg_done IRQ */ + uint8_t mask_irq; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_PROCESS_GROUP_STRUCT]; +}; + +/*! Callback after process group is created. Implementations can provide + * suitable actions needed when process group is created. + + @param process_group[in] process group object + @param program_group_manifest[in] program group manifest + @param program_group_param[in] program group parameters + + @return 0 on success and non-zero on failure + */ +extern int ia_css_process_group_on_create( + ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *program_group_manifest, + const ia_css_program_group_param_t *program_group_param); + +/*! Callback before process group is about to be destoyed. Any implementation + * specific cleanups can be done here. + + @param process_group[in] process group object + + @return 0 on success and non-zero on failure + */ +extern int ia_css_process_group_on_destroy( + ia_css_process_group_t *process_group); + +/* + * Command processor + */ + +/*! Execute a command locally or send it to be processed remotely + + @param process_group[in] process group object + @param cmd[in] command + + @return < 0 on error + */ +extern int ia_css_process_group_exec_cmd( + ia_css_process_group_t *process_group, + const ia_css_process_group_cmd_t cmd); + + +/*! Enqueue a buffer set corresponding to a persistent program group by + * sending a command to subsystem. + + @param process_group[in] process group object + @param buffer_set[in] buffer set + @param queue_offset[in] offset to be used from the queue id + specified in the process group object + (0 for first buffer set for frame, 1 + for late binding) + + @return < 0 on error + */ +extern int ia_css_enqueue_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set, + unsigned int queue_offset); + +/*! Enqueue a parameter buffer set corresponding to a persistent program + * group by sending a command to subsystem. + + @param process_group[in] process group object + @param buffer_set[in] parameter buffer set + + @return < 0 on error + */ +extern int ia_css_enqueue_param_buffer_set( + ia_css_process_group_t *process_group, + ia_css_buffer_set_t *buffer_set); + +/*! Need to store the 'secure' mode for each PG for FW test app only + * + * @param process_group[in] process group object + * @param secure[in] parameter buffer set + * + * @return < 0 on error + */ +extern int ia_css_process_group_store( + ia_css_process_group_t *process_group, + bool secure); + + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_CMD_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h new file mode 100644 index 000000000000..4fb064dc00df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_process_types.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_TYPES_H +#define __IA_CSS_PSYS_PROCESS_TYPES_H + +/*! \file */ + +/** @file ia_css_psys_process_types.h + * + * The types belonging to the terminal/process/process group dynamic module + */ + +#include +#include + +#include + +#define IA_CSS_PROCESS_INVALID_PROGRAM_IDX ((uint32_t)-1) + +/* private */ +typedef enum ia_css_process_group_cmd { + IA_CSS_PROCESS_GROUP_CMD_NOP = 0, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT, + IA_CSS_PROCESS_GROUP_CMD_ATTACH, + IA_CSS_PROCESS_GROUP_CMD_DETACH, + IA_CSS_PROCESS_GROUP_CMD_START, + IA_CSS_PROCESS_GROUP_CMD_DISOWN, + IA_CSS_PROCESS_GROUP_CMD_RUN, + IA_CSS_PROCESS_GROUP_CMD_STOP, + IA_CSS_PROCESS_GROUP_CMD_SUSPEND, + IA_CSS_PROCESS_GROUP_CMD_RESUME, + IA_CSS_PROCESS_GROUP_CMD_ABORT, + IA_CSS_PROCESS_GROUP_CMD_RESET, + IA_CSS_N_PROCESS_GROUP_CMDS +} ia_css_process_group_cmd_t; + +/* private */ +#define IA_CSS_PROCESS_GROUP_STATE_BITS 32 +typedef enum ia_css_process_group_state { + IA_CSS_PROCESS_GROUP_ERROR = 0, + IA_CSS_PROCESS_GROUP_CREATED, + IA_CSS_PROCESS_GROUP_READY, + IA_CSS_PROCESS_GROUP_BLOCKED, + IA_CSS_PROCESS_GROUP_STARTED, + IA_CSS_PROCESS_GROUP_RUNNING, + IA_CSS_PROCESS_GROUP_STALLED, + IA_CSS_PROCESS_GROUP_STOPPED, + IA_CSS_N_PROCESS_GROUP_STATES +} ia_css_process_group_state_t; + +/* private */ +typedef enum ia_css_process_cmd { + IA_CSS_PROCESS_CMD_NOP = 0, + IA_CSS_PROCESS_CMD_ACQUIRE, + IA_CSS_PROCESS_CMD_RELEASE, + IA_CSS_PROCESS_CMD_START, + IA_CSS_PROCESS_CMD_LOAD, + IA_CSS_PROCESS_CMD_STOP, + IA_CSS_PROCESS_CMD_SUSPEND, + IA_CSS_PROCESS_CMD_RESUME, + IA_CSS_N_PROCESS_CMDS +} ia_css_process_cmd_t; + +/* private */ +#define IA_CSS_PROCESS_STATE_BITS 32 +typedef enum ia_css_process_state { + IA_CSS_PROCESS_ERROR = 0, + IA_CSS_PROCESS_CREATED, + IA_CSS_PROCESS_READY, + IA_CSS_PROCESS_STARTED, + IA_CSS_PROCESS_RUNNING, + IA_CSS_PROCESS_STOPPED, + IA_CSS_PROCESS_SUSPENDED, + IA_CSS_N_PROCESS_STATES +} ia_css_process_state_t; + +/* public */ +typedef struct ia_css_process_group_s ia_css_process_group_t; +typedef struct ia_css_process_s ia_css_process_t; + +typedef struct ia_css_data_terminal_s ia_css_data_terminal_t; + +#endif /* __IA_CSS_PSYS_PROCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h new file mode 100644 index 000000000000..abf398299d16 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.h @@ -0,0 +1,316 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_H +#define __IA_CSS_PSYS_TERMINAL_H + +/*! \file */ + +/** @file ia_css_psys_terminal.h + * + * Define the methods on the terminal object that are not part of + * a single interface + */ + +#include /* ia_css_frame_t */ +#include /* ia_css_program_group_param_t */ + +#include +#include + +#include /* bool */ +#include /* FILE */ +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest_base_types.h" + +/* + * Creation + */ +#include + +/*! Boolean test if the terminal object type is input + + @param terminal[in] terminal object + + @return true if the terminal is input, false otherwise or on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_input( + const ia_css_terminal_t *terminal); + +/*! Get the stored size of the terminal object + + @param terminal[in] terminal object + + @return size, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +size_t ia_css_terminal_get_size( + const ia_css_terminal_t *terminal); + +/*! Get the type of the terminal object + + @param terminal[in] terminal object + + @return the type of the terminal, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_type_t ia_css_terminal_get_type( + const ia_css_terminal_t *terminal); + +/*! Set the type of the terminal object + + @param terminal[in] terminal object + @param terminal_type[in] type of the terminal + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_type( + ia_css_terminal_t *terminal, + const ia_css_terminal_type_t terminal_type); + +/*! Get the index of the terminal manifest object + + @param terminal[in] terminal object + + @return the index of the terminal manifest object, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_terminal_get_terminal_manifest_index( + const ia_css_terminal_t *terminal); + +/*! Set the index of the terminal manifest object + + @param terminal[in] terminal object + @param tm_index[in] terminal manifest index + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_terminal_manifest_index( + ia_css_terminal_t *terminal, + const uint16_t tm_index); + +/*! Get id of the terminal object + + @param terminal[in] terminal object + + @return id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_terminal_ID_t ia_css_terminal_get_ID( + const ia_css_terminal_t *terminal); + +/*! Get kernel id of the data terminal object + + @param dterminal[in] data terminal object + + @return kernel id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_get_kernel_id( + const ia_css_data_terminal_t *dterminal); + +/*! Get the connection type from the terminal object + + @param terminal[in] terminal object + + @return buffer type, limit value on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_connection_type_t ia_css_data_terminal_get_connection_type( + const ia_css_data_terminal_t *dterminal); + +/*! Set the connection type of the terminal object + + @param terminal[in] terminal object + @param connection_type[in] connection type + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_data_terminal_set_connection_type( + ia_css_data_terminal_t *dterminal, + const ia_css_connection_type_t connection_type); + +/*! Get link id of the data terminal object + + @param dterminal[in] data terminal object + + @return link id of terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_get_link_id( + const ia_css_data_terminal_t *dterminal); + + +/*! Set link id of the terminal object + + @param terminal[in] data terminal object + @param link_id[in] synchronization link id + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_data_terminal_set_link_id( + ia_css_data_terminal_t *dterminal, + const uint8_t link_id); + +/*! Get the (pointer to) the process group parent of the terminal object + + @param terminal[in] terminal object + + @return the pointer to the parent, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_process_group_t *ia_css_terminal_get_parent( + const ia_css_terminal_t *terminal); + +/*! Set the (pointer to) the process group parent of the terminal object + + @param terminal[in] terminal object + @param parent[in] (pointer to the) process group parent object + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_parent( + ia_css_terminal_t *terminal, + ia_css_process_group_t *parent); + +/*! Boolean test if the terminal object type is valid + + @param terminal[in] process terminal object + @param terminal_manifest[in] program terminal manifest + + @return true if the process terminal object is correct, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest); + +/* ================= Program Control Init Terminal - START ================= */ + +/*! + * Gets the program init terminal descripor size + * @param manifest[in] program control init terminal manifest + * @return size, error if < 0. + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +unsigned int +ia_css_program_control_init_terminal_get_descriptor_size( + const ia_css_program_control_init_terminal_manifest_t *manifest); + +/*! + * Initialize program control init terminal + * @param nof_fragments[in] Number of fragments + * @param terminal[in] program control init terminal + * @param manifest[in] program control init terminal manifest + * @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int +ia_css_program_control_init_terminal_init( + ia_css_program_control_init_terminal_t *terminal, + const ia_css_program_control_init_terminal_manifest_t *manifest); + +/*! + * Get a program desc for a program control init terminal + * @param terminal[in] program control init terminal + * @param manifest[in] program control init terminal manifest + * @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_program_desc_t * +ia_css_program_control_init_terminal_get_program_desc( + const ia_css_program_control_init_terminal_t *prog_ctrl_init_terminal, + const unsigned int program_index +); + +/*! + * Pretty prints the program control init termnial + * @param terminal[in] program control init terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +void ia_css_program_control_init_terminal_print( + const ia_css_program_control_init_terminal_t *terminal); + +/*! + * Gets a load section desc for a program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param load_section_index[in] section index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_load_section_desc_t * +ia_css_program_control_init_terminal_get_load_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int load_section_index +); + +/*! + * Gets process_id from program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_id_t ia_css_program_control_init_terminal_get_process_id( + const ia_css_program_control_init_program_desc_t *program_desc); + +/*! + * Set control info of program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param process_id unique process id used to identify the process + * among all active process + * @param num_done_events number of events required to close the process + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +void ia_css_program_control_init_terminal_set_control_info( + ia_css_program_control_init_program_desc_t *program_desc, + ia_css_process_id_t process_id, + uint8_t num_done_events); + +/*! + * Gets num_done_events value from program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_program_control_init_terminal_get_num_done_events( + const ia_css_program_control_init_program_desc_t *program_desc); + +/*! + * Gets a connect section desc for a program desc + * of a program control init terminal + * @param program_desc[in] program control init terminal program desc + * @param connect_section_index[in] section index + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_program_control_init_connect_section_desc_t * +ia_css_program_control_init_terminal_get_connect_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int connect_section_index +); + +/* ================= Program Control Init Terminal - END ================= */ + +#ifdef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_terminal_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_TERMINAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h new file mode 100644 index 000000000000..b8aa08c19754 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/interface/ia_css_psys_terminal.hsys.user.h @@ -0,0 +1,255 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_HSYS_USER_H +#define __IA_CSS_PSYS_TERMINAL_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_terminal.hsys.user.h + * + * Define the methods on the terminal object: Hsys user interface + */ + +#include /* ia_css_frame_t */ +#include /* ia_css_program_group_param_t */ + +#include +#include + +#include /* bool */ +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_kernel_bitmap.h" + +/* + * Creation + */ + +/* + * This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __VIED_CELL marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__VIED_CELL) +/*! Compute the size of storage required for allocating the terminal object + + @param manifest[in] terminal manifest + @param param[in] program group parameters + + @return 0 on error + */ +extern size_t ia_css_sizeof_terminal( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Create the terminal object + + @param raw_mem[in] pre allocated memory + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + @param enable_bitmap program group enable bitmap + + @return NULL on error + */ +extern ia_css_terminal_t *ia_css_terminal_create( + void *raw_mem, + const ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_param_t *terminal_param, + ia_css_kernel_bitmap_t enable_bitmap); + +/*! Destroy (the storage of) the process object + + @param terminal[in] terminal object + + @return NULL + */ +extern ia_css_terminal_t *ia_css_terminal_destroy( + ia_css_terminal_t *terminal); +#endif /* !defined(__VIED_CELL) */ + +/*! Print the terminal object to file/stream + + @param terminal[in] terminal object + @param fid[out] file/stream handle + + @return < 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_print( + const ia_css_terminal_t *terminal, + void *fid); + +/*! Get the (pointer to) the frame object in the terminal object + + @param terminal[in] terminal object + + @return the pointer to the frame, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_frame_t *ia_css_data_terminal_get_frame( + const ia_css_data_terminal_t *terminal); + +/*! Get the (pointer to) the frame descriptor object in the terminal object + + @param terminal[in] terminal object + + @return the pointer to the frame descriptor, NULL on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_frame_descriptor_t *ia_css_data_terminal_get_frame_descriptor( + const ia_css_data_terminal_t *dterminal); + +/*! Get the (pointer to) the fragment descriptor object in the terminal object + + @param terminal[in] terminal object + +@return the pointer to the fragment descriptor, NULL on error +*/ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +ia_css_fragment_descriptor_t + *ia_css_data_terminal_get_fragment_descriptor( + const ia_css_data_terminal_t *dterminal, + const unsigned int fragment_index); + +/*! Get the number of fragments on the terminal + + @param terminal[in] terminal object + + @return the fragment count, 0 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint16_t ia_css_data_terminal_get_fragment_count( + const ia_css_data_terminal_t *dterminal); + +/*! Get the number of section on the (param)terminal + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + + @return the section count, 0 on error + */ +extern uint16_t ia_css_param_terminal_compute_section_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! Get the number of planes on the (data)terminal + @param manifest[in] terminal manifest + @param terminal_param[in] terminal parameter + + @return the plane count, 1(default) on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +uint8_t ia_css_data_terminal_compute_plane_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param); + +/*! check if given terminal is parameter terminal. + + @param terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_parameter_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is program terminal. + + @program terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_program_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is program control init terminal. + + @program control init terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_program_control_init_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is spatial parameter terminal. + + @spatial terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_spatial_parameter_terminal( + const ia_css_terminal_t *terminal); + +/*! check if given terminal is data terminal. + + @param terminal[in] (base)terminal object + + @return true on success, false on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +bool ia_css_is_terminal_data_terminal( + const ia_css_terminal_t *terminal); + +/*! obtain buffer out of terminal(both data & param terminals can call this) + + @param terminal[in] (base)terminal object of either data or param terminal. + + @return vied address of buffer stored in terminal + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +vied_vaddress_t ia_css_terminal_get_buffer( + const ia_css_terminal_t *terminal); + +/*!store a buffer in the terminal. + + @param terminal[in] (base)terminal object of either data or param terminal. + @param buffer[in] buffer in vied (hrt address) space. + + @return 0 on success + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_H +int ia_css_terminal_set_buffer(ia_css_terminal_t *terminal, + vied_vaddress_t buffer); + +/*! Obtain terminal buffer index out of terminal object + + @param terminal[in] (base)terminal object of either data or param terminal. + + @return terminal buffer index stored in terminal object on success, -1 on error + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_get_terminal_index( + const ia_css_terminal_t *terminal); + +/*! Store a terminal buffer index in the terminal object + + @param terminal[in] (base)terminal object of either data or param terminal. + @param terminal_index[in] terminal buffer index + + @return 0 on success + */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_index( + ia_css_terminal_t *terminal, + unsigned int terminal_index); + +#endif /* __IA_CSS_PSYS_TERMINAL_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c new file mode 100644 index 000000000000..82d53831f9a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set.c @@ -0,0 +1,111 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "assert_support.h" +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_buffer_set.h" +#include "ia_css_psys_process_group.h" + +/* + * Functions to possibly inline + */ +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_buffer_set_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +STORAGE_CLASS_INLINE void __buffer_set_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF(SIZE_OF_BUFFER_SET != + CHAR_BIT * sizeof(ia_css_buffer_set_t)); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_buffer_set_t) % sizeof(uint64_t)); +} + +/* + * Functions not to inline + */ + +/* The below functions are not to be compiled for firmware */ +#if !defined(__HIVECC) + +ia_css_buffer_set_t *ia_css_buffer_set_create( + void *buffer_set_mem, + const ia_css_process_group_t *process_group, + const unsigned int frame_counter) +{ + ia_css_buffer_set_t *buffer_set = NULL; + unsigned int i; + int ret = -1; + + verifexit(buffer_set_mem != NULL); + verifexit(process_group != NULL); + + buffer_set = (ia_css_buffer_set_t *)buffer_set_mem; + + /* + * Set base struct members + */ + buffer_set->ipu_virtual_address = VIED_NULL; + ia_css_process_group_get_ipu_vaddress(process_group, + &buffer_set->process_group_handle); + buffer_set->frame_counter = frame_counter; + buffer_set->terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + /* + * Initialize adjacent buffer addresses + */ + for (i = 0; i < buffer_set->terminal_count; i++) { + vied_vaddress_t *buffer = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + sizeof(vied_vaddress_t) * i); + + *buffer = VIED_NULL; + } + ret = 0; + +EXIT: + if (ret != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_create failed\n"); + } + return buffer_set; +} + +size_t ia_css_sizeof_buffer_set( + const ia_css_process_group_t *process_group) +{ + size_t size = 0; + + verifexit(process_group != NULL); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_buffer_set(): enter:\n"); + + size = sizeof(ia_css_buffer_set_t) + + ia_css_process_group_get_terminal_count(process_group) * + sizeof(vied_vaddress_t); + +EXIT: + if (size == 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_buffer_set failed\n"); + } + return size; +} + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h new file mode 100644 index 000000000000..0399d76f3331 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_buffer_set_impl.h @@ -0,0 +1,241 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYS_BUFFER_SET_IMPL_H +#define __IA_CSS_PSYS_BUFFER_SET_IMPL_H + +#include "error_support.h" +#include "ia_css_psys_dynamic_trace.h" +#include "vied_nci_psys_system_global.h" +#include "ia_css_psys_terminal.hsys.user.h" + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_buffer( + ia_css_buffer_set_t *buffer_set, + const unsigned int terminal_index, + const vied_vaddress_t buffer) +{ + DECLARE_ERRVAL + vied_vaddress_t *buffer_ptr; + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + verifexitval(terminal_index < buffer_set->terminal_count, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_buffer(): enter:\n"); + + /* + * Set address in buffer set object + */ + buffer_ptr = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + terminal_index * sizeof(vied_vaddress_t)); + *buffer_ptr = buffer; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_buffer: invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_buffer( + const ia_css_buffer_set_t *buffer_set, + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + vied_vaddress_t buffer = VIED_NULL; + vied_vaddress_t *buffer_ptr; + int terminal_index; + + verifexitval(buffer_set != NULL, EFAULT); + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_buffer(): enter:\n"); + + /* + * Retrieve terminal index from terminal object + */ + terminal_index = ia_css_terminal_get_terminal_index(terminal); + verifexitval(terminal_index >= 0, EFAULT); + verifexitval(terminal_index < buffer_set->terminal_count, EFAULT); + + /* + * Retrieve address from buffer set object + */ + buffer_ptr = + (vied_vaddress_t *)( + (char *)buffer_set + + sizeof(ia_css_buffer_set_t) + + terminal_index * sizeof(vied_vaddress_t)); + buffer = *buffer_ptr; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_buffer: invalid argument\n"); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_ipu_address( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t ipu_vaddress) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_ipu_address(): enter:\n"); + + buffer_set->ipu_virtual_address = ipu_vaddress; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_ipu_address invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_ipu_address( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + vied_vaddress_t ipu_virtual_address = VIED_NULL; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_ipu_address(): enter:\n"); + + ipu_virtual_address = buffer_set->ipu_virtual_address; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_ipu_address: invalid argument\n"); + } + return ipu_virtual_address; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_process_group_handle( + ia_css_buffer_set_t *buffer_set, + const vied_vaddress_t process_group_handle) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_process_group_context(): enter:\n"); + + buffer_set->process_group_handle = process_group_handle; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_process_group_context invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_buffer_set_get_process_group_handle( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + vied_vaddress_t process_group_handle = VIED_NULL; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_process_group_handle(): enter:\n"); + + process_group_handle = buffer_set->process_group_handle; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_process_group_handle: invalid argument\n"); + } + return process_group_handle; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_buffer_set_set_token( + ia_css_buffer_set_t *buffer_set, + const uint64_t token) +{ + DECLARE_ERRVAL + int ret = -1; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_set_token(): enter:\n"); + + buffer_set->token = token; + + ret = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_set_token invalid argument\n"); + } + return ret; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint64_t ia_css_buffer_set_get_token( + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + uint64_t token = 0; + + verifexitval(buffer_set != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_buffer_set_get_token(): enter:\n"); + + token = buffer_set->token; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_buffer_set_get_token: invalid argument\n"); + } + return token; +} + +#endif /* __IA_CSS_PSYS_BUFFER_SET_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c new file mode 100644 index 000000000000..cca0fa73fb37 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process.c @@ -0,0 +1,1147 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_process.h" +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_process_private_types.h" +#include /* for NOT_USED */ + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __HIVECC marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__HIVECC) +size_t ia_css_sizeof_process( + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param) +{ + size_t size = 0, tmp_size; + + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_process(): enter:\n"); + + COMPILATION_ERROR_IF( + SIZE_OF_PROCESS_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_process_t))); + + COMPILATION_ERROR_IF(0 != sizeof(ia_css_process_t)%sizeof(uint64_t)); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + size += sizeof(ia_css_process_t); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + tmp_size = program_dependency_count*sizeof(vied_nci_resource_id_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + tmp_size = terminal_dependency_count*sizeof(uint8_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_process invalid argument\n"); + } + return size; +} + +ia_css_process_t *ia_css_process_create( + void *raw_mem, + const ia_css_program_manifest_t *manifest, + const ia_css_program_param_t *param, + const uint32_t program_idx) +{ + size_t tmp_size; + int retval = -1; + ia_css_process_t *process = NULL; + char *process_raw_ptr = (char *) raw_mem; + + /* size_t size = ia_css_sizeof_process(manifest, param); */ + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_create(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + verifexit(process_raw_ptr != NULL); + + process = (ia_css_process_t *) process_raw_ptr; + verifexit(process != NULL); + + process->kernel_bitmap = + ia_css_program_manifest_get_kernel_bitmap(manifest); + process->state = IA_CSS_PROCESS_CREATED; + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + /* A process requires at least one input or output */ + verifexit((program_dependency_count + + terminal_dependency_count) != 0); + + process_raw_ptr += sizeof(ia_css_process_t); + if (program_dependency_count != 0) { + process->cell_dependencies_offset = + (uint16_t) (process_raw_ptr - (char *)process); + tmp_size = + program_dependency_count * sizeof(vied_nci_resource_id_t); + process_raw_ptr += + tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + } else { + process->cell_dependencies_offset = 0; + } + + if (terminal_dependency_count != 0) { + process->terminal_dependencies_offset = + (uint16_t) (process_raw_ptr - (char *)process); + } + + process->size = (uint32_t)ia_css_sizeof_process(manifest, param); + + process->ID = ia_css_program_manifest_get_program_ID(manifest); + verifexit(process->ID != 0); + process->program_idx = program_idx; + + process->cell_dependency_count = program_dependency_count; + process->terminal_dependency_count = terminal_dependency_count; + + process->parent_offset = 0; + + verifexit(ia_css_process_clear_all(process) == 0); + + process->state = IA_CSS_PROCESS_READY; + retval = 0; + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_create(): Created successfully process %p ID 0x%x\n", + process, process->ID); + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_create failed (%i)\n", retval); + process = ia_css_process_destroy(process); + } + return process; +} + +ia_css_process_t *ia_css_process_destroy( + ia_css_process_t *process) +{ + + return process; +} +#endif + +int ia_css_process_set_cell( + ia_css_process_t *process, + const vied_nci_cell_ID_t cell_id) +{ + int retval = -1; + vied_nci_resource_bitmap_t bit_mask; + vied_nci_resource_bitmap_t resource_bitmap; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cell(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + + verifexit(parent != NULL); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + +/* Some programs are mapped on a fixed cell, + * when the process group is created + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_CREATED) || + /* If the process group has already been created, but no VP cell + * has been assigned to this process (i.e. not fixed in + * manifest), then we need to set the cell of this process + * while its parent state is READY (the ready state is set at + * the end of ia_css_process_group_create) + */ + (parent_state == IA_CSS_PROCESS_GROUP_READY))); + verifexit(state == IA_CSS_PROCESS_READY); + +/* Some programs are mapped on a fixed cell, thus check is not secure, + * but it will detect a preset, the process manager will do the secure check + */ + verifexit(ia_css_process_get_cell(process) == + VIED_NCI_N_CELL_ID); + + bit_mask = vied_nci_cell_bit_mask(cell_id); + resource_bitmap = ia_css_process_group_get_resource_bitmap(parent); + + verifexit(bit_mask != 0); + verifexit(vied_nci_is_bitmap_clear(bit_mask, resource_bitmap)); + + ia_css_process_cells_clear(process); + ia_css_process_cells_set_cell(process, 0, cell_id); + + resource_bitmap = vied_nci_bitmap_set(resource_bitmap, bit_mask); + + retval = ia_css_process_group_set_resource_bitmap( + parent, resource_bitmap); +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_cell invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_cell failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_cell( + ia_css_process_t *process) +{ + int retval = -1; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_t *parent; + vied_nci_resource_bitmap_t resource_bitmap; + vied_nci_resource_bitmap_t bit_mask; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_cell(): enter:\n"); + verifexit(process != NULL); + + cell_id = ia_css_process_get_cell(process); + parent = ia_css_process_get_parent(process); + + verifexit(parent != NULL); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + + bit_mask = vied_nci_cell_bit_mask(cell_id); + resource_bitmap = ia_css_process_group_get_resource_bitmap(parent); + + verifexit(bit_mask != 0); + verifexit(vied_nci_is_bitmap_set(bit_mask, resource_bitmap)); + + ia_css_process_cells_clear(process); + + resource_bitmap = vied_nci_bitmap_clear(resource_bitmap, bit_mask); + + retval = ia_css_process_group_set_resource_bitmap( + parent, resource_bitmap); +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_cell invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_cell failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_int_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + if (vied_nci_is_cell_mem_of_type(cell_id, mem_type_id, mem_type_id)) { + vied_nci_mem_ID_t mem_id = + vied_nci_cell_get_mem(cell_id, mem_type_id); + + process->int_mem_id[mem_type_id] = mem_id; + process->int_mem_offset[mem_type_id] = offset; + retval = 0; + } +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_int_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_int_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + int retval = -1; + uint16_t mem_index; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_int_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + +/* We could just clear the field, but lets check the state for + * consistency first + */ + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + if (vied_nci_is_cell_mem_of_type( + cell_id, mem_index, mem_type_id)) { + vied_nci_mem_ID_t mem_id = + vied_nci_cell_get_mem(cell_id, mem_index); + int mem_of_type; + + mem_of_type = + vied_nci_is_mem_of_type(mem_id, mem_type_id); + + assert(mem_of_type); + assert((process->int_mem_id[mem_type_id] == mem_id) || + (process->int_mem_id[mem_type_id] == + VIED_NCI_N_MEM_ID)); + process->int_mem_id[mem_type_id] = VIED_NCI_N_MEM_ID; + process->int_mem_offset[mem_type_id] = + IA_CSS_PROCESS_INVALID_OFFSET; + retval = 0; + } + } + +EXIT: + if (NULL == process || mem_type_id >= VIED_NCI_N_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_int_mem invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_int_mem failed (%i)\n", retval); + } +return retval; +} + +int ia_css_process_set_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_ID_t mem_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + vied_nci_cell_ID_t cell_id; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + vied_nci_mem_type_ID_t mem_type_id; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_ext_mem(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + cell_id = ia_css_process_get_cell(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + /* Check that the memory actually exists, "vied_nci_has_cell_mem_of_id()" + * will return false on error + */ + + mem_type_id = vied_nci_mem_get_type(mem_id); + if (((!vied_nci_has_cell_mem_of_id(cell_id, mem_id) && + (mem_type_id != VIED_NCI_PMEM_TYPE_ID)) + || vied_nci_mem_is_ext_type(mem_type_id)) && + (mem_id < VIED_NCI_N_MEM_ID)) { + + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + process->ext_mem_id[mem_type_id] = mem_id; + process->ext_mem_offset[mem_type_id] = offset; + retval = 0; + } + +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_ext_mem invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_ext_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_ext_mem( + ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_ext_mem(): enter:\n"); + + verifexit(process != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + verifexit(parent != NULL); + verifexit(state == IA_CSS_PROCESS_READY); + + parent_state = ia_css_process_group_get_state(parent); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + + process->ext_mem_id[mem_type_id] = VIED_NCI_N_MEM_ID; + process->ext_mem_offset[mem_type_id] = IA_CSS_PROCESS_INVALID_OFFSET; + + retval = 0; +EXIT: + if (NULL == process || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_ext_mem invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_ext_mem failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_cells_bitmap( + ia_css_process_t *process, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + int array_index = 0; + int bit_index; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cells_bitmap(): enter:\n"); + + verifexit(process != NULL); + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_CREATED) || + (parent_state == IA_CSS_PROCESS_GROUP_READY))); + verifexit(state == IA_CSS_PROCESS_READY); + + for (bit_index = 0; bit_index < VIED_NCI_N_CELL_ID; bit_index++) { + if (vied_nci_is_bit_set_in_bitmap(bitmap, bit_index)) { + verifexit(array_index < IA_CSS_PROCESS_MAX_CELLS); + ia_css_process_cells_set_cell(process, + array_index, (vied_nci_cell_ID_t)bit_index); + array_index++; + } + } + for (; array_index < IA_CSS_PROCESS_MAX_CELLS; array_index++) { + ia_css_process_cells_set_cell(process, + array_index, VIED_NCI_N_CELL_ID); + } + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_cells_bitmap invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_cells_bitmap failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t offset) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dev_chn(): enter:\n"); + + verifexit(process != NULL); + verifexit(dev_chn_id <= VIED_NCI_N_DEV_CHN_ID); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + + process->dev_chn_offset[dev_chn_id] = offset; + + retval = 0; +EXIT: + if (NULL == process || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_dev_chn invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_dev_chn invalid argument\n"); + } + return retval; +} + +int ia_css_process_set_dfm_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dfm_port(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); + +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_dev_id <= VIED_NCI_N_DEV_DFM_ID); + process->dfm_port_bitmap[dfm_dev_id] = bitmap; +#else + (void)bitmap; + (void)dfm_dev_id; +#endif + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_dfm_port invalid argument\n"); + } + return retval; +} + +int ia_css_process_set_dfm_active_port_bitmap( + ia_css_process_t *process, + const vied_nci_dev_dfm_id_t dfm_dev_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_dfm_active_port_bitmap(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + parent_state = ia_css_process_group_get_state(parent); + + /* TODO : separate process group start and run from + * process_group_exec_cmd() + */ + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) || + (parent_state == IA_CSS_PROCESS_GROUP_STARTED) || + (parent_state == IA_CSS_PROCESS_GROUP_RUNNING))); + verifexit(state == IA_CSS_PROCESS_READY); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_dev_id <= VIED_NCI_N_DEV_DFM_ID); + process->dfm_active_port_bitmap[dfm_dev_id] = bitmap; +#else + (void)bitmap; + (void)dfm_dev_id; +#endif + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_dfm_active_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_process_clear_dev_chn( + ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_dev_chn(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + state = ia_css_process_get_state(process); + + verifexit(((parent_state == IA_CSS_PROCESS_GROUP_BLOCKED) + || (parent_state == IA_CSS_PROCESS_GROUP_STARTED))); + verifexit(state == IA_CSS_PROCESS_READY); + + verifexit(dev_chn_id <= VIED_NCI_N_DEV_CHN_ID); + + process->dev_chn_offset[dev_chn_id] = IA_CSS_PROCESS_INVALID_OFFSET; + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_dev_chn invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_dev_chn failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_clear_all( + ia_css_process_t *process) +{ + int retval = -1; + ia_css_process_group_t *parent; + ia_css_process_group_state_t parent_state; + ia_css_process_state_t state; + int mem_index; + int dev_chn_index; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_clear_all(): enter:\n"); + + verifexit(process != NULL); + + parent = ia_css_process_get_parent(process); + state = ia_css_process_get_state(process); + + /* We should have a check on NULL != parent but it parent is NULL + * ia_css_process_group_get_state will return + * IA_CSS_N_PROCESS_GROUP_STATES so it will be filtered anyway later. + */ + + /* verifexit(parent != NULL); */ + + parent_state = ia_css_process_group_get_state(parent); + +/* Resource clear can only be called in excluded states contrary to set */ + verifexit((parent_state != IA_CSS_PROCESS_GROUP_RUNNING) || + (parent_state == IA_CSS_N_PROCESS_GROUP_STATES)); + verifexit((state == IA_CSS_PROCESS_CREATED) || + (state == IA_CSS_PROCESS_READY)); + + for (dev_chn_index = 0; dev_chn_index < VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + process->dev_chn_offset[dev_chn_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } +/* No difference whether a cell_id has been set or not, clear all */ + for (mem_index = 0; mem_index < VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + process->ext_mem_id[mem_index] = VIED_NCI_N_MEM_ID; + process->ext_mem_offset[mem_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + process->int_mem_id[mem_index] = VIED_NCI_N_MEM_ID; + process->int_mem_offset[mem_index] = + IA_CSS_PROCESS_INVALID_OFFSET; + } + + ia_css_process_cells_clear(process); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_clear_all invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_clear_all failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_acquire( + ia_css_process_t *process) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_acquire(): enter:\n"); + + verifexit(process != NULL); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_acquire invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_acquire failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_release( + ia_css_process_t *process) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_release(): enter:\n"); + + verifexit(process != NULL); + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_t invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_release failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_print(const ia_css_process_t *process, void *fid) +{ + int retval = -1; + int i, dev_chn_index; + uint16_t mem_index; + uint8_t cell_dependency_count, terminal_dependency_count; + vied_nci_cell_ID_t cell_id = ia_css_process_get_cell(process); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_print(process %p): enter:\n", process); + + verifexit(process != NULL); + + IA_CSS_TRACE_6(PSYSAPI_DYNAMIC, INFO, + "\tprocess %p, sizeof %d, programID %d, state %d, parent %p, cell %d\n", + process, + (int)ia_css_process_get_size(process), + (int)ia_css_process_get_program_ID(process), + (int)ia_css_process_get_state(process), + (void *)ia_css_process_get_parent(process), + (int)ia_css_process_get_cell(process)); + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + vied_nci_mem_ID_t mem_id = + (vied_nci_mem_ID_t)(process->int_mem_id[mem_index]); + if (cell_id == VIED_NCI_N_CELL_ID) { + verifexit(mem_id == VIED_NCI_N_MEM_ID); + continue; + } + verifexit(((mem_id == vied_nci_cell_get_mem(cell_id, mem_index)) + || (mem_id == VIED_NCI_N_MEM_ID))); + + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tinternal index %d, type %d, id %d offset 0x%x\n", + mem_index, + (int)vied_nci_cell_get_mem_type(cell_id, mem_index), + (int)mem_id, + process->int_mem_offset[mem_index]); + } + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + vied_nci_mem_ID_t mem_id = + (vied_nci_mem_ID_t)(process->ext_mem_id[mem_index]); + /* TODO: in case of an cells_bitmap = [], + * vied_nci_cell_get_mem_type will return a wrong result. + */ + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\texternal index %d, type %d, id %d offset 0x%x\n", + mem_index, + (int)vied_nci_cell_get_mem_type(cell_id, mem_index), + (int)mem_id, + process->ext_mem_offset[mem_index]); + NOT_USED(mem_id); + } + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tdevice channel index %d, type %d, offset 0x%x\n", + dev_chn_index, + (int)dev_chn_index, + process->dev_chn_offset[dev_chn_index]); + } +#if HAS_DFM + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_DFM_ID; + dev_chn_index++) { + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tdfm device index %d, type %d, bitmap 0x%x active_ports_bitmap 0x%x\n", + dev_chn_index, dev_chn_index, + process->dfm_port_bitmap[dev_chn_index], + process->dfm_active_port_bitmap[dev_chn_index]); + } +#endif + + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\tcells[%d] = 0x%x\n", + i, ia_css_process_cells_get_cell(process, i)); + } + + cell_dependency_count = + ia_css_process_get_cell_dependency_count(process); + if (cell_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tcell_dependencies[%d] {};\n", cell_dependency_count); + } else { + vied_nci_resource_id_t cell_dependency; + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tcell_dependencies[%d] {", cell_dependency_count); + for (i = 0; i < (int)cell_dependency_count - 1; i++) { + cell_dependency = + ia_css_process_get_cell_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d, ", cell_dependency); + } + cell_dependency = + ia_css_process_get_cell_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d}\n", cell_dependency); + (void)cell_dependency; + } + + terminal_dependency_count = + ia_css_process_get_terminal_dependency_count(process); + if (terminal_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tterminal_dependencies[%d] {};\n", + terminal_dependency_count); + } else { + uint8_t terminal_dependency; + + terminal_dependency_count = + ia_css_process_get_terminal_dependency_count(process); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tterminal_dependencies[%d] {", + terminal_dependency_count); + for (i = 0; i < (int)terminal_dependency_count - 1; i++) { + terminal_dependency = + ia_css_process_get_terminal_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d, ", terminal_dependency); + } + terminal_dependency = + ia_css_process_get_terminal_dependency(process, i); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "%4d}\n", terminal_dependency); + (void)terminal_dependency; + } + + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_print invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_parent( + ia_css_process_t *process, + ia_css_process_group_t *parent) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_parent(): enter:\n"); + + verifexit(process != NULL); + verifexit(parent != NULL); + + process->parent_offset = (uint16_t) ((char *)parent - (char *)process); + retval = 0; +EXIT: + if (NULL == process || NULL == parent) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_set_parent invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_parent failed (%i)\n", retval); + } + return retval; +} + +int ia_css_process_set_cell_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id) +{ + int retval = -1; + uint8_t *process_dep_ptr; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_cell_dependency(): enter:\n"); + verifexit(process != NULL); + + process_dep_ptr = + (uint8_t *)process + process->cell_dependencies_offset + + dep_index*sizeof(vied_nci_resource_id_t); + + + *process_dep_ptr = id; + retval = 0; +EXIT: + return retval; +} + +int ia_css_process_set_terminal_dependency( + const ia_css_process_t *process, + const unsigned int dep_index, + const vied_nci_resource_id_t id) +{ + int retval = -1; + uint8_t *terminal_dep_ptr; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_terminal_dependency(): enter:\n"); + verifexit(process != NULL); + verifexit(ia_css_process_get_terminal_dependency_count(process) > dep_index); + + terminal_dep_ptr = + (uint8_t *)process + process->terminal_dependencies_offset + + dep_index*sizeof(uint8_t); + + *terminal_dep_ptr = id; + retval = 0; +EXIT: + return retval; +} + +int ia_css_process_cmd( + ia_css_process_t *process, + const ia_css_process_cmd_t cmd) +{ + int retval = -1; + ia_css_process_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, "ia_css_process_cmd(): enter:\n"); + + verifexit(process != NULL); + + state = ia_css_process_get_state(process); + + verifexit(state != IA_CSS_PROCESS_ERROR); + verifexit(state < IA_CSS_N_PROCESS_STATES); + + switch (cmd) { + case IA_CSS_PROCESS_CMD_NOP: + break; + case IA_CSS_PROCESS_CMD_ACQUIRE: + verifexit(state == IA_CSS_PROCESS_READY); + break; + case IA_CSS_PROCESS_CMD_RELEASE: + verifexit(state == IA_CSS_PROCESS_READY); + break; + case IA_CSS_PROCESS_CMD_START: + verifexit((state == IA_CSS_PROCESS_READY) + || (state == IA_CSS_PROCESS_STOPPED)); + process->state = IA_CSS_PROCESS_STARTED; + break; + case IA_CSS_PROCESS_CMD_LOAD: + verifexit(state == IA_CSS_PROCESS_STARTED); + process->state = IA_CSS_PROCESS_RUNNING; + break; + case IA_CSS_PROCESS_CMD_STOP: + verifexit((state == IA_CSS_PROCESS_RUNNING) + || (state == IA_CSS_PROCESS_SUSPENDED)); + process->state = IA_CSS_PROCESS_STOPPED; + break; + case IA_CSS_PROCESS_CMD_SUSPEND: + verifexit(state == IA_CSS_PROCESS_RUNNING); + process->state = IA_CSS_PROCESS_SUSPENDED; + break; + case IA_CSS_PROCESS_CMD_RESUME: + verifexit(state == IA_CSS_PROCESS_SUSPENDED); + process->state = IA_CSS_PROCESS_RUNNING; + break; + case IA_CSS_N_PROCESS_CMDS: /* Fall through */ + default: + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_cmd invalid cmd (0x%x)\n", cmd); + goto EXIT; + } + retval = 0; +EXIT: + if (NULL == process) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_cmd invalid argument process\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_cmd failed (%i)\n", retval); + } + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c new file mode 100644 index 000000000000..46bb82804153 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group.c @@ -0,0 +1,886 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_process_group.h" +#include "ia_css_psys_dynamic_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_process_group_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* This header is need for cpu memset to 0 +* and process groups are not created in SP +*/ +#if !defined(__VIED_CELL) +#include "cpu_mem_support.h" +#endif + +/* This source file is created with the intention of sharing and +* compiled for host and firmware. Since there is no native 64bit +* data type support for firmware this wouldn't compile for SP +* tile. The part of the file that is not compilable are marked +* with the following __VIED_CELL marker and this comment. Once we +* come up with a solution to address this issue this will be +* removed. +*/ +#if !defined(__VIED_CELL) +static bool ia_css_process_group_is_program_enabled( + const ia_css_program_manifest_t *program_manifest, + ia_css_kernel_bitmap_t enable_bitmap) +{ + ia_css_kernel_bitmap_t program_bitmap = + ia_css_program_manifest_get_kernel_bitmap(program_manifest); + ia_css_program_type_t program_type = + ia_css_program_manifest_get_type(program_manifest); + ia_css_kernel_bitmap_t program_enable_bitmap; + + if (!ia_css_is_kernel_bitmap_intersection_empty(enable_bitmap, + program_bitmap)) { + + if (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB || + program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER || + program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + /* + * EXCLUSIVE_SUB programs are subsets of + * EXCLUSIVE_SUPER so the bits of the enable_bitmap + * that refer to those are those of their + * EXCLUSIVE_SUPER program (on which the depend) and + * not the subset that their own program_bitmap has + */ + if (program_type == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB || + program_type == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + ia_css_kernel_bitmap_t super_program_bitmap; + + const ia_css_program_group_manifest_t * + prog_group_manifest = + ia_css_program_manifest_get_parent(program_manifest); + uint8_t super_prog_idx = + ia_css_program_manifest_get_program_dependency( + program_manifest, 0); + const ia_css_program_manifest_t * + super_program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + prog_group_manifest, super_prog_idx); + + verifexit(super_program_manifest != NULL); + if (((program_type == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (ia_css_program_manifest_get_type( + super_program_manifest) != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER)) + || ((program_type == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) && + (ia_css_program_manifest_get_type( + super_program_manifest) != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER))) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_is_program_enabled(): Error\n"); + verifexit(0); + } + + super_program_bitmap = + ia_css_program_manifest_get_kernel_bitmap( + super_program_manifest); + program_enable_bitmap = + ia_css_kernel_bitmap_intersection( + enable_bitmap, + super_program_bitmap); + } else { + program_enable_bitmap = + ia_css_kernel_bitmap_intersection( + enable_bitmap, program_bitmap); + } + + if (ia_css_is_kernel_bitmap_equal( + program_enable_bitmap, program_bitmap)) { + return true; + } + } else if (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER) { + /* + * Virtual super programs are not selectable + * only the virtual sub programs + */ + return false; + } else { + return true; + } + } + +EXIT: + return false; +} + +static bool ia_css_process_group_is_terminal_enabled( + const ia_css_terminal_manifest_t *terminal_manifest, + ia_css_kernel_bitmap_t enable_bitmap) +{ + ia_css_terminal_type_t terminal_type; + + verifjmpexit(NULL != terminal_manifest); + terminal_type = ia_css_terminal_manifest_get_type(terminal_manifest); + + if (ia_css_is_terminal_manifest_data_terminal(terminal_manifest)) { + ia_css_data_terminal_manifest_t *data_term_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; + ia_css_kernel_bitmap_t term_bitmap = + ia_css_data_terminal_manifest_get_kernel_bitmap( + data_term_manifest); + /* + * Terminals depend on a kernel, + * if the kernel is present the program it contains and + * the terminal the program depends on are active + */ + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_bitmap)) { + return true; + } + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest)) { + ia_css_kernel_bitmap_t term_kernel_bitmap = ia_css_kernel_bitmap_clear(); + ia_css_spatial_param_terminal_manifest_t *spatial_term_man = + (ia_css_spatial_param_terminal_manifest_t *) + terminal_manifest; + + term_kernel_bitmap = + ia_css_kernel_bitmap_set( + term_kernel_bitmap, + spatial_term_man->kernel_id); + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_kernel_bitmap)) { + return true; + } + + } else if (ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest) && terminal_type == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN) { + return true; + + } else if (ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest) && terminal_type == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + /* + * For parameter out terminals, we disable the terminals + * if ALL the corresponding kernels are disabled, + * for parameter in terminals we cannot do this; + * even if kernels are disabled, it may be required that + * (HW) parameters must be supplied via the parameter + * in terminal (e.g. bypass bits). + */ + ia_css_kernel_bitmap_t term_kernel_bitmap = ia_css_kernel_bitmap_clear(); + ia_css_param_terminal_manifest_t *param_term_man = + (ia_css_param_terminal_manifest_t *)terminal_manifest; + ia_css_param_manifest_section_desc_t *section_desc; + unsigned int section = 0; + + for (section = 0; section < param_term_man-> + param_manifest_section_desc_count; section++) { + section_desc = + ia_css_param_terminal_manifest_get_prm_sct_desc( + param_term_man, section); + verifjmpexit(section_desc != NULL); + term_kernel_bitmap = ia_css_kernel_bitmap_set( + term_kernel_bitmap, + section_desc->kernel_id); + } + + if (!ia_css_is_kernel_bitmap_intersection_empty( + enable_bitmap, term_kernel_bitmap)) { + return true; + } + } else if (ia_css_is_terminal_manifest_program_terminal( + terminal_manifest)) { + return true; + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + terminal_manifest)) { + return true; + } +EXIT: + return false; +} + +size_t ia_css_sizeof_process_group( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = 0, tmp_size; + int i, error_val = -1; + uint8_t process_count, process_num; + uint8_t terminal_count; + ia_css_kernel_bitmap_t enable_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_process_group(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + COMPILATION_ERROR_IF( + SIZE_OF_PROCESS_GROUP_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_process_group_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_process_group_t) % sizeof(uint64_t)); + + process_count = + ia_css_process_group_compute_process_count(manifest, param); + terminal_count = + ia_css_process_group_compute_terminal_count(manifest, param); + + verifexit(process_count != 0); + verifexit(terminal_count != 0); + + size += sizeof(ia_css_process_group_t); + + tmp_size = process_count * sizeof(uint16_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + + tmp_size = terminal_count * sizeof(uint16_t); + size += tot_bytes_for_pow2_align(sizeof(uint64_t), tmp_size); + + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + process_num = 0; + for (i = 0; i < (int)ia_css_program_group_manifest_get_program_count( + manifest); i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst(manifest, i); + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + + if (ia_css_process_group_is_program_enabled( + program_manifest, enable_bitmap)) { + verifexit(process_num < process_count); + size += ia_css_sizeof_process( + program_manifest, program_param); + process_num++; + } + } + + verifexit(process_num == process_count); + + for (i = 0; i < (int)ia_css_program_group_manifest_get_terminal_count( + manifest); i++) { + ia_css_terminal_manifest_t *terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + if (ia_css_process_group_is_terminal_enabled( + terminal_manifest, enable_bitmap)) { + size += ia_css_sizeof_terminal( + terminal_manifest, param); + } + } + + error_val = 0; + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_process_group invalid argument\n"); + } + if (error_val != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_process_group ERROR(%d)\n", error_val); + } + return size; +} + +ia_css_process_group_t *ia_css_process_group_create( + void *process_grp_mem, + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = ia_css_sizeof_process_group(manifest, param); + int retval = -1; + int ret; + int i; + ia_css_process_group_t *process_group = NULL; + uint8_t process_count, process_num; + uint8_t terminal_count, terminal_num; + uint16_t fragment_count; + char *process_grp_raw_ptr; + uint16_t *process_tab_ptr, *terminal_tab_ptr; + ia_css_kernel_bitmap_t enable_bitmap; + uint8_t manifest_terminal_count; + + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(process_grp_mem %p, manifest %p, group_param %p): enter:\n", + process_grp_mem, manifest, param); + + verifexit(process_grp_mem != NULL); + verifexit(manifest != NULL); + verifexit(param != NULL); + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + + process_group = (ia_css_process_group_t *)process_grp_mem; + ia_css_cpu_mem_set_zero(process_group, size); + process_grp_raw_ptr = (char *) process_group; + + process_group->state = IA_CSS_PROCESS_GROUP_CREATED; + + process_group->protocol_version = + ia_css_program_group_param_get_protocol_version(param); + + fragment_count = ia_css_program_group_param_get_fragment_count(param); + process_count = + ia_css_process_group_compute_process_count(manifest, param); + terminal_count = + ia_css_process_group_compute_terminal_count(manifest, param); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + process_group->fragment_count = fragment_count; + process_group->process_count = process_count; + process_group->terminal_count = terminal_count; + + process_grp_raw_ptr += sizeof(ia_css_process_group_t); + process_tab_ptr = (uint16_t *) process_grp_raw_ptr; + process_group->processes_offset = + (uint16_t)(process_grp_raw_ptr - (char *)process_group); + + process_grp_raw_ptr += tot_bytes_for_pow2_align( + sizeof(uint64_t), process_count * sizeof(uint16_t)); + terminal_tab_ptr = (uint16_t *) process_grp_raw_ptr; + process_group->terminals_offset = + (uint16_t)(process_grp_raw_ptr - (char *)process_group); + + /* Move raw pointer to the first process */ + process_grp_raw_ptr += tot_bytes_for_pow2_align( + sizeof(uint64_t), terminal_count * sizeof(uint16_t)); + + /* Set default */ + verifexit(ia_css_process_group_set_fragment_limit( + process_group, fragment_count) == 0); + + /* Set process group terminal dependency list */ + /* This list is used during creating the process dependency list */ + manifest_terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + terminal_num = 0; + for (i = 0; i < (int)manifest_terminal_count; i++) { + ia_css_terminal_manifest_t *t_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + verifexit(NULL != t_manifest); + if (ia_css_process_group_is_terminal_enabled( + t_manifest, enable_bitmap)) { + ia_css_terminal_t *terminal = NULL; + ia_css_terminal_param_t *terminal_param = + ia_css_program_group_param_get_terminal_param( + param, i); + + verifexit(NULL != terminal_param); + terminal_tab_ptr[terminal_num] = + (uint16_t)(process_grp_raw_ptr - + (char *)process_group); + terminal = ia_css_terminal_create( + process_grp_raw_ptr, t_manifest, + terminal_param, enable_bitmap); + verifexit(terminal != NULL); + verifexit((ia_css_terminal_set_parent( + terminal, process_group) == 0)); + verifexit((ia_css_terminal_set_terminal_manifest_index( + terminal, i) == 0)); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create: terminal_manifest_index %d\n", + i); + + process_grp_raw_ptr += ia_css_terminal_get_size( + terminal); + terminal_num++; + } + } + verifexit(terminal_num == terminal_count); + + process_num = 0; + for (i = 0; i < (int)ia_css_program_group_manifest_get_program_count( + manifest); i++) { + ia_css_process_t *process = NULL; + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + unsigned int prog_dep_index, proc_dep_index; + unsigned int term_dep_index, term_index; + + if (ia_css_process_group_is_program_enabled( + program_manifest, enable_bitmap)) { + + verifexit(process_num < process_count); + + process_tab_ptr[process_num] = + (uint16_t)(process_grp_raw_ptr - + (char *)process_group); + process = ia_css_process_create( + process_grp_raw_ptr, + program_manifest, + program_param, + i); + verifexit(process != NULL); + + ia_css_process_set_parent(process, process_group); + if (ia_css_has_program_manifest_fixed_cell( + program_manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID( + program_manifest); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create: cell_id %d\n", + cell_id); + ia_css_process_set_cell(process, cell_id); + } + + process_grp_raw_ptr += ia_css_process_get_size( + process); + /* + * Set process dependencies of process derived + * from program manifest + */ + for (prog_dep_index = 0; prog_dep_index < + ia_css_program_manifest_get_program_dependency_count( + program_manifest); prog_dep_index++) { + uint8_t dep_prog_idx = + ia_css_program_manifest_get_program_dependency( + program_manifest, prog_dep_index); + const ia_css_program_manifest_t * + dep_prg_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, dep_prog_idx); + ia_css_program_ID_t id = + ia_css_program_manifest_get_program_ID( + dep_prg_manifest); + + verifexit(id != 0); + for (proc_dep_index = 0; + proc_dep_index < process_num; + proc_dep_index++) { + ia_css_process_t *dep_process = + ia_css_process_group_get_process( + process_group, + proc_dep_index); + + ia_css_process_set_cell_dependency( + process, + prog_dep_index, 0); + + if (ia_css_process_get_program_ID( + dep_process) == id) { + ia_css_process_set_cell_dependency( + process, + prog_dep_index, + proc_dep_index); + break; + } + } + } + process_num++; + + /* + * Set terminal dependencies of process derived + * from program manifest + */ + for (term_dep_index = 0; term_dep_index < + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest); term_dep_index++) { + uint8_t pm_term_index = + ia_css_program_manifest_get_terminal_dependency + (program_manifest, term_dep_index); + + verifexit(pm_term_index < manifest_terminal_count); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(): term_dep_index: %d, pm_term_index: %d\n", + term_dep_index, pm_term_index); + for (term_index = 0; + term_index < terminal_count; + term_index++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal( + process_group, + term_index); + + if (ia_css_terminal_get_terminal_manifest_index + (terminal) == pm_term_index) { + ia_css_process_set_terminal_dependency( + process, + term_dep_index, + term_index); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create() set_terminal_dependency(process: %d, dep_idx: %d, term_idx: %d)\n", + i, term_dep_index, term_index); + + break; + } + } + } + } + } + verifexit(process_num == process_count); + + process_group->size = + (uint32_t)ia_css_sizeof_process_group(manifest, param); + process_group->ID = + ia_css_program_group_manifest_get_program_group_ID(manifest); + + /* Initialize performance measurement fields to zero */ + process_group->pg_load_start_ts = 0; + process_group->pg_load_cycles = 0; + process_group->pg_init_cycles = 0; + process_group->pg_processing_cycles = 0; + + verifexit(process_group->ID != 0); + + ret = ia_css_process_group_on_create(process_group, manifest, param); + verifexit(ret == 0); + + process_group->state = IA_CSS_PROCESS_GROUP_READY; + retval = 0; + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_create(): Created successfully process group ID 0x%x\n", + process_group->ID); + +EXIT: + if (NULL == process_grp_mem || NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_create failed (%i)\n", retval); + process_group = ia_css_process_group_destroy(process_group); + } + return process_group; +} + +ia_css_process_group_t *ia_css_process_group_destroy( + ia_css_process_group_t *process_group) +{ + if (process_group != NULL) { + ia_css_process_group_on_destroy(process_group); + process_group = NULL; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_destroy invalid argument\n"); + } + return process_group; +} + +int ia_css_process_group_submit( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_submit(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_SUBMIT); +} + +int ia_css_process_group_start( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_start(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_START); +} + +int ia_css_process_group_stop( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_stop(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_STOP); +} + +int ia_css_process_group_run( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_run(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RUN); +} + +int ia_css_process_group_suspend( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_suspend(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_SUSPEND); +} + +int ia_css_process_group_resume( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_resume(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RESUME); +} + +int ia_css_process_group_reset( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_reset(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_RESET); +} + +int ia_css_process_group_abort( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_abort(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_ABORT); +} + +int ia_css_process_group_disown( + ia_css_process_group_t *process_group) +{ + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_disown(): enter:\n"); + + return ia_css_process_group_exec_cmd(process_group, + IA_CSS_PROCESS_GROUP_CMD_DISOWN); +} + +extern uint64_t ia_css_process_group_get_token( + ia_css_process_group_t *process_group) +{ + uint64_t token = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_token(): enter:\n"); + + verifexit(process_group != NULL); + + token = process_group->token; + +EXIT: + if (NULL == process_group) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_get_token invalid argument\n"); + } + return token; +} + +int ia_css_process_group_set_token( + ia_css_process_group_t *process_group, + const uint64_t token) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_token(): enter:\n"); + + verifexit(process_group != NULL); + verifexit(token != 0); + + process_group->token = token; + + retval = 0; +EXIT: + if (NULL == process_group || 0 == token) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_set_token invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_token failed (%i)\n", + retval); + } + return retval; +} + +extern uint64_t ia_css_process_group_get_private_token( + ia_css_process_group_t *process_group) +{ + uint64_t token = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_private_token(): enter:\n"); + + verifexit(process_group != NULL); + + token = process_group->private_token; + +EXIT: + if (NULL == process_group) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_get_private_token invalid argument\n"); + } + return token; +} + +int ia_css_process_group_set_private_token( + ia_css_process_group_t *process_group, + const uint64_t token) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_private_token(): enter:\n"); + + verifexit(process_group != NULL); + verifexit(token != 0); + + process_group->private_token = token; + + retval = 0; +EXIT: + if (NULL == process_group || 0 == token) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_set_private_token invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_private_token failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_process_group_compute_process_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + uint8_t process_count = 0; + ia_css_kernel_bitmap_t total_bitmap; + ia_css_kernel_bitmap_t enable_bitmap; + int i; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_compute_process_count(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + verifexit(ia_css_is_kernel_bitmap_subset(total_bitmap, enable_bitmap)); + verifexit(!ia_css_is_kernel_bitmap_empty(enable_bitmap)); + + for (i = 0; i < + (int)ia_css_program_group_manifest_get_program_count(manifest); + i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_kernel_bitmap_t program_bitmap = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest); + /* + * Programs can be orthogonal, + * a mutually exclusive subset, + * or a concurrent subset + */ + if (!ia_css_is_kernel_bitmap_intersection_empty(enable_bitmap, + program_bitmap)) { + ia_css_program_type_t program_type = + ia_css_program_manifest_get_type( + program_manifest); + /* + * An exclusive subnode < exclusive supernode, + * so simply don't count it + */ + if (program_type != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB && + program_type != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) { + process_count++; + } + } + } + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_compute_process_count invalid argument\n"); + } + return process_count; +} + +uint8_t ia_css_process_group_compute_terminal_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + uint8_t terminal_count = 0; + ia_css_kernel_bitmap_t total_bitmap, enable_bitmap; + int i; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_compute_terminal_count(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + enable_bitmap = + ia_css_program_group_param_get_kernel_enable_bitmap(param); + + verifexit(ia_css_is_program_group_manifest_valid(manifest)); + verifexit(ia_css_is_kernel_bitmap_subset(total_bitmap, enable_bitmap)); + verifexit(!ia_css_is_kernel_bitmap_empty(enable_bitmap)); + + for (i = 0; i < + (int)ia_css_program_group_manifest_get_terminal_count( + manifest); i++) { + ia_css_terminal_manifest_t *tmanifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + if (ia_css_process_group_is_terminal_enabled( + tmanifest, enable_bitmap)) { + terminal_count++; + } + } + +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_process_group_compute_terminal_count invalid argument\n"); + } + return terminal_count; +} +#endif /* !defined(__VIED_CELL) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h new file mode 100644 index 000000000000..f99602dc3c9e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_group_impl.h @@ -0,0 +1,1538 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H +#define __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H + +#include +#include +#include "ia_css_psys_process_group_cmd_impl.h" +#include +#include +#include +#include +#include +#include +#include "ia_css_terminal_manifest_types.h" + +#include "ia_css_rbm.h" + +#include /* ia_css_kernel_bitmap_t */ + +#include +#include +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +#include "ia_css_psys_dynamic_trace.h" + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_process_group_get_fragment_limit( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint16_t fragment_limit = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + fragment_limit = process_group->fragment_limit; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit invalid argument\n"); + } + return fragment_limit; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_fragment_limit( + ia_css_process_group_t *process_group, + const uint16_t fragment_limit) +{ + DECLARE_ERRVAL + int retval = -1; + uint16_t fragment_state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + retval = ia_css_process_group_get_fragment_state(process_group, + &fragment_state); + + verifexitval(retval == 0, EINVAL); + verifexitval(fragment_limit > fragment_state, EINVAL); + verifexitval(fragment_limit <= ia_css_process_group_get_fragment_count( + process_group), EINVAL); + + process_group->fragment_limit = fragment_limit; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_limit failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_clear_fragment_limit( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_clear_fragment_limit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + process_group->fragment_limit = 0; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_fragment_limit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_fragment_limit failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_attach_buffer( + ia_css_process_group_t *process_group, + vied_vaddress_t buffer, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + ia_css_terminal_t *terminal = NULL; + + NOT_USED(buffer_state); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_attach_buffer(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal = ia_css_process_group_get_terminal( + process_group, terminal_index); + + verifexitval(terminal != NULL, EINVAL); + verifexitval(ia_css_process_group_get_state(process_group) == + IA_CSS_PROCESS_GROUP_READY, EINVAL); + verifexitval(process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY || + process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, EINVAL); + + if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY) { + /* + * Legacy flow: + * Terminal address is part of the process group structure + */ + retval = ia_css_terminal_set_buffer( + terminal, buffer); + } else if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG) { + /* + * PPG flow: + * Terminal address is part of external buffer set structure + */ + retval = ia_css_terminal_set_terminal_index( + terminal, terminal_index); + } + verifexitval(retval == 0, EFAULT); + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\tTerminal %p has buffer 0x%x\n", terminal, buffer); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + verifexitval(frame != NULL, EINVAL); + + retval = ia_css_frame_set_buffer_state(frame, buffer_state); + verifexitval(retval == 0, EINVAL); + } + + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_buffer invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_buffer failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_process_group_detach_buffer( + ia_css_process_group_t *process_group, + const unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_vaddress_t buffer = VIED_NULL; + + ia_css_terminal_t *terminal = NULL; + ia_css_process_group_state_t state; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_detach_buffer(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal = + ia_css_process_group_get_terminal( + process_group, terminal_index); + state = ia_css_process_group_get_state(process_group); + + verifexitval(terminal != NULL, EINVAL); + verifexitval(state == IA_CSS_PROCESS_GROUP_READY, EINVAL); + + buffer = ia_css_terminal_get_buffer(terminal); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + verifexitval(frame != NULL, EINVAL); + + retval = ia_css_frame_set_buffer_state(frame, IA_CSS_BUFFER_NULL); + verifexitval(retval == 0, EINVAL); + } + ia_css_terminal_set_buffer(terminal, VIED_NULL); + + retval = 0; +EXIT: + /* + * buffer pointer will appear on output, + * regardless of subsequent fails to avoid memory leaks + */ + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_buffer invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_buffer failed (%i)\n", + retval); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_attach_stream( + ia_css_process_group_t *process_group, + uint32_t stream, + const ia_css_buffer_state_t buffer_state, + const unsigned int terminal_index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_attach_stream(): enter:\n"); + + NOT_USED(process_group); + NOT_USED(stream); + NOT_USED(buffer_state); + NOT_USED(terminal_index); + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_attach_stream failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_detach_stream( + ia_css_process_group_t *process_group, + const unsigned int terminal_index) +{ + int retval = -1; + uint32_t stream = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_detach_stream(): enter:\n"); + + NOT_USED(process_group); + NOT_USED(terminal_index); + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_detach_stream failed (%i)\n", + retval); + } + return stream; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_nci_resource_bitmap_t bit_mask; + vied_nci_resource_bitmap_t resource_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_barrier(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = + ia_css_process_group_get_resource_bitmap(process_group); + + bit_mask = vied_nci_barrier_bit_mask(barrier_index); + + verifexitval(bit_mask != 0, EINVAL); + verifexitval(vied_nci_is_bitmap_clear(bit_mask, resource_bitmap), EINVAL); + + resource_bitmap = vied_nci_bitmap_set(resource_bitmap, bit_mask); + + retval = + ia_css_process_group_set_resource_bitmap( + process_group, resource_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_barrier invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_barrier failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_clear_barrier( + ia_css_process_group_t *process_group, + const vied_nci_barrier_ID_t barrier_index) +{ + DECLARE_ERRVAL + int retval = -1; + vied_nci_resource_bitmap_t bit_mask, resource_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_clear_barrier(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = + ia_css_process_group_get_resource_bitmap(process_group); + + bit_mask = vied_nci_barrier_bit_mask(barrier_index); + + verifexitval(bit_mask != 0, EINVAL); + verifexitval(vied_nci_is_bitmap_set(bit_mask, resource_bitmap), EINVAL); + + resource_bitmap = vied_nci_bitmap_clear(resource_bitmap, bit_mask); + + retval = + ia_css_process_group_set_resource_bitmap( + process_group, resource_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_barrier invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_clear_barrier failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_print( + const ia_css_process_group_t *process_group, + void *fid) +{ + DECLARE_ERRVAL + int retval = -1; + int i; + + uint8_t process_count; + uint8_t terminal_count; + vied_vaddress_t ipu_vaddress = VIED_NULL; + ia_css_rbm_t routing_bitmap; + + NOT_USED(fid); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_print(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + retval = ia_css_process_group_get_ipu_vaddress(process_group, &ipu_vaddress); + verifexitval(retval == 0, EINVAL); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "=============== Process group print start ===============\n"); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tprocess_group cpu address = %p\n", process_group); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tipu_virtual_address = %#x\n", ipu_vaddress); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tsizeof(process_group) = %d\n", + (int)ia_css_process_group_get_size(process_group)); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tfragment_count = %d\n", + (int)ia_css_process_group_get_fragment_count(process_group)); + + routing_bitmap = *ia_css_process_group_get_routing_bitmap(process_group); + for (i = 0; i < (int)IA_CSS_RBM_NOF_ELEMS; i++) { + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "\trouting_bitmap[index = %d] = 0x%X\n", + i, (int)routing_bitmap.data[i]); + } + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\tprogram_group(process_group) = %d\n", + (int)ia_css_process_group_get_program_group_ID(process_group)); + process_count = ia_css_process_group_get_process_count(process_group); + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\t%d processes\n", (int)process_count); + for (i = 0; i < (int)process_count; i++) { + ia_css_process_t *process = + ia_css_process_group_get_process(process_group, i); + + retval = ia_css_process_print(process, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "\t%d terminals\n", (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + + retval = ia_css_terminal_print(terminal, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "=============== Process group print end ===============\n"); + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_print invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_process_group_valid( + const ia_css_process_group_t *process_group, + const ia_css_program_group_manifest_t *pg_manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint8_t proc_idx; + uint8_t prog_idx; + uint8_t proc_term_idx; + uint8_t process_count; + uint8_t program_count; + uint8_t terminal_count; + uint8_t man_terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_process_group_valid(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(pg_manifest != NULL, EFAULT); + NOT_USED(param); + + process_count = process_group->process_count; + terminal_count = process_group->terminal_count; + program_count = + ia_css_program_group_manifest_get_program_count(pg_manifest); + man_terminal_count = + ia_css_program_group_manifest_get_terminal_count(pg_manifest); + + /* Validate process group */ + invalid_flag = invalid_flag || + !(program_count >= process_count) || + !(man_terminal_count >= terminal_count) || + !(process_group->size > process_group->processes_offset) || + !(process_group->size > process_group->terminals_offset); + + /* Validate processes */ + for (proc_idx = 0; proc_idx < process_count; proc_idx++) { + const ia_css_process_t *process; + ia_css_program_ID_t prog_id; + bool no_match_found = true; + + process = ia_css_process_group_get_process( + process_group, proc_idx); + verifexitval(NULL != process, EFAULT); + prog_id = ia_css_process_get_program_ID(process); + for (prog_idx = 0; prog_idx < program_count; prog_idx++) { + ia_css_program_manifest_t *p_manifest = NULL; + + p_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + pg_manifest, prog_idx); + if (prog_id == + ia_css_program_manifest_get_program_ID( + p_manifest)) { + invalid_flag = invalid_flag || + !ia_css_is_process_valid( + process, p_manifest); + no_match_found = false; + break; + } + } + invalid_flag = invalid_flag || no_match_found; + } + + /* Validate terminals */ + for (proc_term_idx = 0; proc_term_idx < terminal_count; + proc_term_idx++) { + int man_term_idx; + const ia_css_terminal_t *terminal; + const ia_css_terminal_manifest_t *terminal_manifest; + + terminal = + ia_css_process_group_get_terminal( + process_group, proc_term_idx); + verifexitval(NULL != terminal, EFAULT); + man_term_idx = + ia_css_terminal_get_terminal_manifest_index(terminal); + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + pg_manifest, man_term_idx); + invalid_flag = invalid_flag || + !ia_css_is_terminal_valid(terminal, terminal_manifest); + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_process_group_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_process_group_submit( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int i; + bool can_submit = false; + int retval = -1; + uint8_t terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_submit(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + vied_vaddress_t buffer; + ia_css_buffer_state_t buffer_state; + + verifexitval(terminal != NULL, EINVAL); + + if (process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY) { + /* + * For legacy pg flow, buffer addresses are contained inside + * the process group structure, so these need to be validated + * on process group submission. + */ + buffer = ia_css_terminal_get_buffer(terminal); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tH: Terminal number(%d) is %p having buffer 0x%x\n", + i, terminal, buffer); + } + + /* buffer_state is applicable only for data terminals*/ + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EINVAL); + buffer_state = ia_css_frame_get_buffer_state(frame); + if ((buffer_state == IA_CSS_BUFFER_NULL) || + (buffer_state == IA_CSS_N_BUFFER_STATES)) { + break; + } + } else if ( + (ia_css_is_terminal_parameter_terminal(terminal) + != true) && + (ia_css_is_terminal_program_terminal(terminal) + != true) && + (ia_css_is_terminal_program_control_init_terminal(terminal) + != true) && + (ia_css_is_terminal_spatial_parameter_terminal( + terminal) != true)) { + /* neither data nor parameter terminal, so error.*/ + break; + } + + } + /* Only true if no check failed */ + can_submit = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit failed (%i)\n", + retval); + } + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_submit(): leave:\n"); + return can_submit; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_enqueue_buffer_set( + const ia_css_process_group_t *process_group, + const ia_css_buffer_set_t *buffer_set) +{ + DECLARE_ERRVAL + int i; + bool can_enqueue = false; + int retval = -1; + uint8_t terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_enqueue_buffer_set(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(buffer_set != NULL, EFAULT); + + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + + /* + * For ppg flow, buffer addresses are contained in the + * external buffer set structure, so these need to be + * validated before enqueueing. + */ + verifexitval(process_group->protocol_version == + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, EFAULT); + + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + vied_vaddress_t buffer; + ia_css_buffer_state_t buffer_state; + + verifexitval(terminal != NULL, EINVAL); + + buffer = ia_css_buffer_set_get_buffer(buffer_set, terminal); + IA_CSS_TRACE_3(PSYSAPI_DYNAMIC, INFO, + "\tH: Terminal number(%d) is %p having buffer 0x%x\n", + i, terminal, buffer); + + /* buffer_state is applicable only for data terminals*/ + if (ia_css_is_terminal_data_terminal(terminal) == true) { + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EINVAL); + buffer_state = ia_css_frame_get_buffer_state(frame); + if ((buffer_state == IA_CSS_BUFFER_NULL) || + (buffer_state == IA_CSS_N_BUFFER_STATES)) { + break; + } + } else if ( + (ia_css_is_terminal_parameter_terminal(terminal) + != true) && + (ia_css_is_terminal_program_terminal(terminal) + != true) && + (ia_css_is_terminal_program_control_init_terminal(terminal) + != true) && + (ia_css_is_terminal_spatial_parameter_terminal( + terminal) != true)) { + /* neither data nor parameter terminal, so error.*/ + break; + } + } + /* Only true if no check failed */ + can_enqueue = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_enqueue_buffer_set invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_enqueue_buffer_set failed (%i)\n", + retval); + } + return can_enqueue; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_can_process_group_start( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + int i; + bool can_start = false; + int retval = -1; + uint8_t terminal_count; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_can_process_group_start(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal_count = + ia_css_process_group_get_terminal_count(process_group); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_t *terminal = + ia_css_process_group_get_terminal(process_group, i); + ia_css_buffer_state_t buffer_state; + bool ok = false; + + verifexitval(terminal != NULL, EINVAL); + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* + * buffer_state is applicable only for data terminals + */ + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + bool is_input = ia_css_is_terminal_input(terminal); + /* + * check for NULL here. + * then invoke next 2 statements + */ + verifexitval(frame != NULL, EINVAL); + IA_CSS_TRACE_5(PSYSAPI_DYNAMIC, VERBOSE, + "\tTerminal %d: buffer_state %u, access_type %u, data_bytes %u, data %u\n", + i, frame->buffer_state, frame->access_type, + frame->data_bytes, frame->data); + buffer_state = ia_css_frame_get_buffer_state(frame); + + ok = ((is_input && + (buffer_state == IA_CSS_BUFFER_FULL)) || + (!is_input && (buffer_state == + IA_CSS_BUFFER_EMPTY))); + + } else if (ia_css_is_terminal_parameter_terminal(terminal) == + true) { + /* + * FIXME: + * is there any pre-requisite for param_terminal? + */ + ok = true; + } else if (ia_css_is_terminal_program_terminal(terminal) == + true) { + ok = true; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) == + true) { + ok = true; + } else if (ia_css_is_terminal_spatial_parameter_terminal( + terminal) == true) { + ok = true; + } else { + /* neither data nor parameter terminal, so error.*/ + break; + } + + if (!ok) + break; + } + /* Only true if no check failed */ + can_start = (i == terminal_count); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_submit invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_can_process_group_start failed (%i)\n", + retval); + } + return can_start; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_process_group_get_size( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_size(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + size = process_group->size; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_state_t ia_css_process_group_get_state( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + ia_css_process_group_state_t state = IA_CSS_N_PROCESS_GROUP_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + state = process_group->state; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_state invalid argument\n"); + } + return state; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +const ia_css_rbm_t *ia_css_process_group_get_routing_bitmap( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + const ia_css_rbm_t *rbm = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_routing_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + rbm = &(process_group->routing_bitmap); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_routing_bitmap invalid argument\n"); + } + return rbm; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_process_group_get_fragment_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + fragment_count = process_group->fragment_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_count invalid argument\n"); + } + return fragment_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_process_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t process_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_process_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_count = process_group->process_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_process_count invalid argument\n"); + } + return process_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_terminal_count( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal_count(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + terminal_count = process_group->terminal_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_load_start_ts( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_load_start_ts = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_load_start_ts(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_load_start_ts = process_group->pg_load_start_ts; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_load_start_ts invalid argument\n"); + } + return pg_load_start_ts; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_load_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_load_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_load_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_load_cycles = process_group->pg_load_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_load_cycles invalid argument\n"); + } + return pg_load_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_init_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_init_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_init_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_init_cycles = process_group->pg_init_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_init_cycles invalid argument\n"); + } + return pg_init_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_get_pg_processing_cycles( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint32_t pg_processing_cycles = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_pg_processing_cycles(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + pg_processing_cycles = process_group->pg_processing_cycles; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_pg_processing_cycles invalid argument\n"); + } + return pg_processing_cycles; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_t *ia_css_process_group_get_terminal_from_type( + const ia_css_process_group_t *process_group, + const ia_css_terminal_type_t terminal_type) +{ + unsigned int proc_cnt; + ia_css_terminal_t *terminal = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal_from_type(): enter:\n"); + + for (proc_cnt = 0; proc_cnt < (unsigned int)ia_css_process_group_get_terminal_count(process_group); proc_cnt++) { + terminal = ia_css_process_group_get_terminal(process_group, proc_cnt); + if (terminal == NULL) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal_from_type() Failed to get terminal %d", proc_cnt); + goto EXIT; + } + if (ia_css_terminal_get_type(terminal) == terminal_type) { + return terminal; + } + terminal = NULL; /* If not the expected type, return NULL */ + } +EXIT: + return terminal; +} + +/* Returns the terminal or NULL if it was not found + For some of those maybe valid to not exist at all in the process group */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +const ia_css_terminal_t *ia_css_process_group_get_single_instance_terminal( + const ia_css_process_group_t *process_group, + ia_css_terminal_type_t term_type) +{ + int i, term_count; + + assert(process_group != NULL); + + /* Those below have at most one instance per process group */ + assert(term_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + term_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT || + term_type == IA_CSS_TERMINAL_TYPE_PROGRAM || + term_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); + + term_count = ia_css_process_group_get_terminal_count(process_group); + + for (i = 0; i < term_count; i++) { + const ia_css_terminal_t *terminal = ia_css_process_group_get_terminal(process_group, i); + + if (ia_css_terminal_get_type(terminal) == term_type) { + /* Only one parameter terminal per process group */ + return terminal; + } + } + + return NULL; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_t *ia_css_process_group_get_terminal( + const ia_css_process_group_t *process_grp, + const unsigned int terminal_num) +{ + DECLARE_ERRVAL + ia_css_terminal_t *terminal_ptr = NULL; + uint16_t *terminal_offset_table; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_terminal(): enter:\n"); + + verifexitval(process_grp != NULL, EFAULT); + verifexitval(terminal_num < process_grp->terminal_count, EINVAL); + + terminal_offset_table = + (uint16_t *)((char *)process_grp + + process_grp->terminals_offset); + terminal_ptr = + (ia_css_terminal_t *)((char *)process_grp + + terminal_offset_table[terminal_num]); + + verifexitval(terminal_ptr != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_terminal invalid argument\n"); + } + return terminal_ptr; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_t *ia_css_process_group_get_process( + const ia_css_process_group_t *process_grp, + const unsigned int process_num) +{ + DECLARE_ERRVAL + ia_css_process_t *process_ptr = NULL; + uint16_t *process_offset_table; + + verifexitval(process_grp != NULL, EFAULT); + verifexitval(process_num < process_grp->process_count, EINVAL); + + process_offset_table = + (uint16_t *)((char *)process_grp + + process_grp->processes_offset); + process_ptr = + (ia_css_process_t *)((char *)process_grp + + process_offset_table[process_num]); + + verifexitval(process_ptr != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_process invalid argument\n"); + } + return process_ptr; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_group_ID_t ia_css_process_group_get_program_group_ID( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + ia_css_program_group_ID_t id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_program_group_ID(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + id = process_group->ID; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_program_group_ID invalid argument\n"); + } + return id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_group_get_resource_bitmap( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t resource_bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_resource_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + resource_bitmap = process_group->resource_bitmap; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_resource_bitmap invalid argument\n"); + } + return resource_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_resource_bitmap( + ia_css_process_group_t *process_group, + const vied_nci_resource_bitmap_t resource_bitmap) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_resource_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->resource_bitmap = resource_bitmap; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_resource_bitmap invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_resource_bitmap failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_routing_bitmap( + ia_css_process_group_t *process_group, + const ia_css_rbm_t rbm) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_routing_bitmap(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + process_group->routing_bitmap = rbm; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_routing_bitmap invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_routing_bitmap failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_group_compute_cycle_count( + const ia_css_program_group_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + uint32_t cycle_count = 0; + + NOT_USED(manifest); + NOT_USED(param); + + verifexitval(manifest != NULL, EFAULT); + verifexitval(param != NULL, EFAULT); + + cycle_count = 1; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_compute_cycle_count invalid argument\n"); + } + return cycle_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_fragment_state( + ia_css_process_group_t *process_group, + uint16_t fragment_state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_process_group_set_fragment_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(fragment_state <= ia_css_process_group_get_fragment_count( + process_group), EINVAL); + + process_group->fragment_state = fragment_state; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_fragment_state invalid argument process_group\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_fragment_state failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_get_fragment_state( + const ia_css_process_group_t *process_group, + uint16_t *fragment_state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_fragment_state(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(fragment_state != NULL, EFAULT); + + *fragment_state = process_group->fragment_state; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_state invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_fragment_state failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_get_ipu_vaddress( + const ia_css_process_group_t *process_group, + vied_vaddress_t *ipu_vaddress) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_ipu_vaddress(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + verifexitval(ipu_vaddress != NULL, EFAULT); + + *ipu_vaddress = process_group->ipu_virtual_address; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_ipu_vaddress invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_ipu_vaddress failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_ipu_vaddress( + ia_css_process_group_t *process_group, + vied_vaddress_t ipu_vaddress) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_ipu_vaddress(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->ipu_virtual_address = ipu_vaddress; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_ipu_vaddress invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_ipu_vaddress failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_protocol_version( + const ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t protocol_version = IA_CSS_PROCESS_GROUP_N_PROTOCOLS; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_protocol_version(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + protocol_version = process_group->protocol_version; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_protocol_version invalid argument\n"); + } + return protocol_version; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_base_queue_id( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t queue_id = IA_CSS_N_PSYS_CMD_QUEUE_ID; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_base_queue_id(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + queue_id = process_group->base_queue_id; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_base_queue_id invalid argument\n"); + } + return queue_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_base_queue_id( + ia_css_process_group_t *process_group, + uint8_t queue_id) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_base_queue_id(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->base_queue_id = queue_id; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_base_queue_id invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_group_get_num_queues( + ia_css_process_group_t *process_group) +{ + DECLARE_ERRVAL + uint8_t num_queues = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_get_num_queues(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + num_queues = process_group->num_queues; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_get_num_queues invalid argument\n"); + } + return num_queues; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_group_set_num_queues( + ia_css_process_group_t *process_group, + uint8_t num_queues) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_group_set_num_queues(): enter:\n"); + + verifexitval(process_group != NULL, EFAULT); + + process_group->num_queues = num_queues; + retval = 0; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_group_set_num_queues invalid argument\n"); + } + return retval; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_process_group_has_vp(const ia_css_process_group_t *process_group) +{ + bool has_vp = false; + uint32_t i; + + uint8_t process_count = ia_css_process_group_get_process_count(process_group); + + for (i = 0; i < process_count; i++) { + ia_css_process_t *process; + vied_nci_cell_ID_t cell_id; + + process = ia_css_process_group_get_process(process_group, i); + cell_id = ia_css_process_get_cell(process); + + if (VIED_NCI_VP_TYPE_ID == vied_nci_cell_get_type(cell_id)) { + has_vp = true; + break; + } + } + + return has_vp; +} + +#endif /* __IA_CSS_PSYS_PROCESS_GROUP_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h new file mode 100644 index 000000000000..5d0303012700 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_impl.h @@ -0,0 +1,637 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_IMPL_H +#define __IA_CSS_PSYS_PROCESS_IMPL_H + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_process_private_types.h" + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process.h */ +STORAGE_CLASS_INLINE vied_nci_cell_ID_t ia_css_process_cells_get_cell(const ia_css_process_t *process, int index) +{ + assert(index < IA_CSS_PROCESS_MAX_CELLS); + if (index >= IA_CSS_PROCESS_MAX_CELLS) { + return VIED_NCI_N_CELL_ID; + } +#if IA_CSS_PROCESS_MAX_CELLS == 1 + return process->cell_id; +#else + return process->cells[index]; +#endif +} + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process.h */ +STORAGE_CLASS_INLINE void ia_css_process_cells_set_cell(ia_css_process_t *process, int index, vied_nci_cell_ID_t cell_id) +{ + assert(index < IA_CSS_PROCESS_MAX_CELLS); + if (index >= IA_CSS_PROCESS_MAX_CELLS) { + return; + } +#if IA_CSS_PROCESS_MAX_CELLS == 1 + process->cell_id = cell_id; +#else + process->cells[index] = cell_id; +#endif +} + +/** Function only to be used in ia_css_psys_process_impl.h and ia_css_psys_process */ +STORAGE_CLASS_INLINE void ia_css_process_cells_clear(ia_css_process_t *process) +{ + int i; + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + ia_css_process_cells_set_cell(process, i, VIED_NCI_N_CELL_ID); + } +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_cell_ID_t ia_css_process_get_cell( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_cell_ID_t cell_id = VIED_NCI_N_CELL_ID; + int i = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +#if IA_CSS_PROCESS_MAX_CELLS > 1 + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + assert(VIED_NCI_N_CELL_ID == ia_css_process_cells_get_cell(process, i)); +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } +#else + (void)i; +#endif + cell_id = ia_css_process_cells_get_cell(process, 0); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell invalid argument\n"); + } + return cell_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_mem_ID_t ia_css_process_get_ext_mem_id( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_ext_mem(): enter:\n"); + + verifexitval(process != NULL && mem_type < VIED_NCI_N_DATA_MEM_TYPE_ID, EFAULT); + +EXIT: + if (!noerror()) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_ext_mem invalid argument\n"); + return IA_CSS_PROCESS_INVALID_OFFSET; + } + return process->ext_mem_id[mem_type]; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint32_t ia_css_process_get_program_idx( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_program_idx(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_program_idx invalid argument\n"); + return IA_CSS_PROCESS_INVALID_PROGRAM_IDX; + } + return process->program_idx; +} + + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_dev_chn( + const ia_css_process_t *process, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + DECLARE_ERRVAL + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dev_chn(): enter:\n"); + + verifexitval(process != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID, EFAULT); + +EXIT: + if (!noerror()) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dev_chn(): invalid arguments\n"); + return IA_CSS_PROCESS_INVALID_OFFSET; + } + return process->dev_chn_offset[dev_chn_id]; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_int_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_id) +{ + DECLARE_ERRVAL + vied_nci_resource_size_t int_mem_offset = IA_CSS_PROCESS_INVALID_OFFSET; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_int_mem_offset(): enter:\n"); + + verifexitval(process != NULL && mem_id < VIED_NCI_N_MEM_TYPE_ID, EFAULT); + +EXIT: + if (noerror()) { + int_mem_offset = process->int_mem_offset[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_int_mem_offset invalid argument\n"); + } + + return int_mem_offset; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_size_t ia_css_process_get_ext_mem_offset( + const ia_css_process_t *process, + const vied_nci_mem_type_ID_t mem_type_id) +{ + DECLARE_ERRVAL + vied_nci_resource_size_t ext_mem_offset = IA_CSS_PROCESS_INVALID_OFFSET; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_ext_mem_offset(): enter:\n"); + + verifexitval(process != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID, EFAULT); + +EXIT: + if (noerror()) { + ext_mem_offset = process->ext_mem_offset[mem_type_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_ext_mem_offset invalid argument\n"); + } + + return ext_mem_offset; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_process_get_size( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_size(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (noerror()) { + size = process->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_size invalid argument\n"); + } + + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_state_t ia_css_process_get_state( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_process_state_t state = IA_CSS_N_PROCESS_STATES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_state(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + +EXIT: + if (noerror()) { + state = process->state; + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_state invalid argument\n"); + } + + return state; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_process_set_state( + ia_css_process_t *process, + ia_css_process_state_t state) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_set_state(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + process->state = state; + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_set_state invalid argument\n"); + } + + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_cell_dependency_count( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + uint8_t cell_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_dependency_count(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + cell_dependency_count = process->cell_dependency_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell_dependency_count invalid argument\n"); + } + return cell_dependency_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_terminal_dependency_count( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + uint8_t terminal_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_terminal_dependency_count(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + terminal_dependency_count = process->terminal_dependency_count; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_terminal_dependency_count invalid argument process\n"); + } + return terminal_dependency_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_t *ia_css_process_get_parent( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_parent(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + parent = + (ia_css_process_group_t *) ((char *)process + process->parent_offset); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_parent invalid argument process\n"); + } + return parent; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_ID_t ia_css_process_get_program_ID( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_program_ID_t id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_program_ID(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + id = process->ID; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_program_ID invalid argument process\n"); + } + return id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_id_t ia_css_process_get_cell_dependency( + const ia_css_process_t *process, + const unsigned int cell_num) +{ + DECLARE_ERRVAL + vied_nci_resource_id_t cell_dependency = + IA_CSS_PROCESS_INVALID_DEPENDENCY; + vied_nci_resource_id_t *cell_dep_ptr = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_dependency(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + verifexitval(cell_num < process->cell_dependency_count, EFAULT); + + cell_dep_ptr = + (vied_nci_resource_id_t *) + ((char *)process + process->cell_dependencies_offset); + cell_dependency = *(cell_dep_ptr + cell_num); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cell_dependency invalid argument\n"); + } + return cell_dependency; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_process_get_terminal_dependency( + const ia_css_process_t *process, + const unsigned int terminal_num) +{ + DECLARE_ERRVAL + uint8_t *ter_dep_ptr = NULL; + uint8_t ter_dep = IA_CSS_PROCESS_INVALID_DEPENDENCY; + + verifexitval(process != NULL, EFAULT); + verifexitval(terminal_num < process->terminal_dependency_count, EFAULT); + + ter_dep_ptr = (uint8_t *) ((char *)process + + process->terminal_dependencies_offset); + + ter_dep = *(ter_dep_ptr + terminal_num); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_terminal_dependency invalid argument\n"); + } + return ter_dep; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_kernel_bitmap_t ia_css_process_get_kernel_bitmap( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_kernel_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + bitmap = process->kernel_bitmap; + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_kernel_bitmap invalid argument process\n"); + } + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_cells_bitmap( + const ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + vied_nci_cell_ID_t cell_id; + int i = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_cell_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); + + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + cell_id = ia_css_process_cells_get_cell(process, i); + if (VIED_NCI_N_CELL_ID != cell_id) { + bitmap |= (1 << cell_id); + } +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_cells_bitmap invalid argument process\n"); + } + + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_port_bitmap_ptr( + ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t *p_bitmap = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + p_bitmap = &process->dfm_port_bitmap[0]; +#else + p_bitmap = NULL; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return p_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t* ia_css_process_get_dfm_active_port_bitmap_ptr( + ia_css_process_t *process) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t *p_bitmap = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + p_bitmap = &process->dfm_active_port_bitmap[0]; +#else + p_bitmap = NULL; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return p_bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_dfm_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexitval(dfm_res_id < VIED_NCI_N_DEV_DFM_ID, EFAULT); + bitmap = process->dfm_port_bitmap[dfm_res_id]; +#else + bitmap = 0; + (void)dfm_res_id; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_port_bitmap invalid argument process\n"); + } + + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_nci_resource_bitmap_t ia_css_process_get_dfm_active_port_bitmap( + const ia_css_process_t *process, + vied_nci_dev_dfm_id_t dfm_res_id) +{ + DECLARE_ERRVAL + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_process_get_dfm_active_port_bitmap(): enter:\n"); + + verifexitval(process != NULL, EFAULT); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexitval(dfm_res_id < VIED_NCI_N_DEV_DFM_ID, EFAULT); + bitmap = process->dfm_active_port_bitmap[dfm_res_id]; +#else + bitmap = 0; + (void)dfm_res_id; +#endif +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_process_get_dfm_active_port_bitmap invalid argument process\n"); + } + return bitmap; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_process_valid( + const ia_css_process_t *process, + const ia_css_program_manifest_t *p_manifest) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + ia_css_program_ID_t prog_id; + ia_css_kernel_bitmap_t prog_kernel_bitmap; + + verifexitval(NULL != process, EFAULT); + verifexitval(NULL != p_manifest, EFAULT); + + prog_id = ia_css_process_get_program_ID(process); + verifjmpexit(prog_id == + ia_css_program_manifest_get_program_ID(p_manifest)); + + prog_kernel_bitmap = + ia_css_program_manifest_get_kernel_bitmap(p_manifest); + + invalid_flag = (process->size <= process->cell_dependencies_offset) || + (process->size <= process->terminal_dependencies_offset) || + !ia_css_is_kernel_bitmap_subset(prog_kernel_bitmap, + process->kernel_bitmap); + + if (ia_css_has_program_manifest_fixed_cell(p_manifest)) { + vied_nci_cell_ID_t cell_id; + + cell_id = ia_css_program_manifest_get_cell_ID(p_manifest); + invalid_flag = invalid_flag || + (cell_id != (vied_nci_cell_ID_t)(ia_css_process_get_cell(process))); + } + invalid_flag = invalid_flag || + ((process->cell_dependency_count + + process->terminal_dependency_count) == 0) || + (process->cell_dependency_count != + ia_css_program_manifest_get_program_dependency_count(p_manifest)) || + (process->terminal_dependency_count != + ia_css_program_manifest_get_terminal_dependency_count(p_manifest)); + + /* TODO: to be removed once all PGs pass validation */ + if (invalid_flag == true) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_is_process_valid(): false\n"); + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_process_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +#endif /* __IA_CSS_PSYS_PROCESS_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h new file mode 100644 index 000000000000..ae0affde9718 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_process_private_types.h @@ -0,0 +1,87 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H +#define __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H + +#include "ia_css_psys_process_types.h" +#include "vied_nci_psys_resource_model.h" + +#define N_UINT32_IN_PROCESS_STRUCT 2 +#define N_UINT16_IN_PROCESS_STRUCT 3 +#define N_UINT8_IN_PROCESS_STRUCT 2 + +#define SIZE_OF_PROCESS_STRUCT_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + (N_UINT32_IN_PROCESS_STRUCT * 32) \ + + IA_CSS_PROGRAM_ID_BITS \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + IA_CSS_PROCESS_STATE_BITS \ + + (N_UINT16_IN_PROCESS_STRUCT * 16) \ + + (VIED_NCI_N_MEM_TYPE_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (VIED_NCI_N_DATA_MEM_TYPE_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (VIED_NCI_N_DEV_CHN_ID * VIED_NCI_RESOURCE_SIZE_BITS) \ + + (IA_CSS_PROCESS_MAX_CELLS * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_N_MEM_TYPE_ID * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_N_DATA_MEM_TYPE_ID * VIED_NCI_RESOURCE_ID_BITS) \ + + (N_UINT8_IN_PROCESS_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_PROCESS_STRUCT * 8)) + +struct ia_css_process_s { + /**< Indicate which kernels lead to this process being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + uint32_t size; /**< Size of this structure */ + ia_css_program_ID_t ID; /**< Referal ID to a specific program FW */ + uint32_t program_idx; /**< Program Index into the PG manifest */ +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM port allocated to this process */ + vied_nci_resource_bitmap_t dfm_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; + /**< Active DFM ports which need a kick */ + vied_nci_resource_bitmap_t dfm_active_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; +#endif + /**< State of the process FSM dependent on the parent FSM */ + ia_css_process_state_t state; + int16_t parent_offset; /**< Reference to the process group */ + /**< Array[dependency_count] of ID's of the cells that provide input */ + uint16_t cell_dependencies_offset; + /**< Array[terminal_dependency_count] of indices of connected terminals */ + uint16_t terminal_dependencies_offset; + /**< (internal) Memory allocation offset given to this process */ + vied_nci_resource_size_t int_mem_offset[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory allocation offset given to this process */ + vied_nci_resource_size_t ext_mem_offset[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Device channel allocation offset given to this process */ + vied_nci_resource_size_t dev_chn_offset[VIED_NCI_N_DEV_CHN_ID]; + /**< Cells (VP, ACB) allocated for the process*/ +#if IA_CSS_PROCESS_MAX_CELLS == 1 + vied_nci_resource_id_t cell_id; +#else + vied_nci_resource_id_t cells[IA_CSS_PROCESS_MAX_CELLS]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + /**< (internal) Memory ID; This is redundant, derived from cell_id */ + vied_nci_resource_id_t int_mem_id[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory ID */ + vied_nci_resource_id_t ext_mem_id[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Number of processes (mapped on cells) this process depends on */ + uint8_t cell_dependency_count; + /**< Number of terminals this process depends on */ + uint8_t terminal_dependency_count; + /**< Padding bytes for 64bit alignment*/ +#if (N_PADDING_UINT8_IN_PROCESS_STRUCT > 0) + uint8_t padding[N_PADDING_UINT8_IN_PROCESS_STRUCT]; +#endif /*(N_PADDING_UINT8_IN_PROCESS_STRUCT > 0)*/ +}; + +#endif /* __IA_CSS_PSYS_PROCESS_PRIVATE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c new file mode 100644 index 000000000000..ea406f229273 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal.c @@ -0,0 +1,604 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_dynamic_storage_class.h" +#include "ia_css_psys_terminal_private_types.h" +#include "ia_css_terminal_types.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_DYNAMIC_INLINE__ +#include "ia_css_psys_terminal_impl.h" +#endif /* __IA_CSS_PSYS_DYNAMIC_INLINE__ */ + +STORAGE_CLASS_INLINE void __terminal_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_terminal_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_section_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SPATIAL_PARAM_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_spatial_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_spatial_param_terminal_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAME_GRID_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_frame_grid_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != sizeof( + ia_css_frame_grid_param_section_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_GRID_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_grid_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_grid_desc_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICED_PARAM_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_sliced_param_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_terminal_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAGMENT_SLICE_DESC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_slice_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_slice_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICE_PARAM_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_slice_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_slice_param_section_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_program_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_terminal_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_SEQ_INFO_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_info_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_SEQ_COMMANDS_STRUCT_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_kernel_fragment_sequencer_command_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_PARAM_SEC_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_fragment_param_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_param_section_desc_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_LOAD_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_load_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_load_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_CONNECT_SECTION_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_connect_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_connect_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS != + (CHAR_BIT * + sizeof(struct ia_css_program_desc_control_info_s))); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_PROG_DESC_STRUCT_BITS != + (CHAR_BIT * + sizeof(ia_css_program_control_init_program_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_program_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_CONTROL_INIT_TERM_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_program_control_init_terminal_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_control_init_terminal_t) % + sizeof(uint64_t)); +} + +/* + * Functions not to inline + */ + +/* + * This source file is created with the intention of sharing and + * compiled for host and firmware. Since there is no native 64bit + * data type support for firmware this wouldn't compile for SP + * tile. The part of the file that is not compilable are marked + * with the following __VIED_CELL marker and this comment. Once we + * come up with a solution to address this issue this will be + * removed. + */ +#if !defined(__VIED_CELL) +size_t ia_css_sizeof_terminal( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + size_t size = 0; + uint16_t fragment_count = + ia_css_program_group_param_get_fragment_count(param); + + COMPILATION_ERROR_IF( + SIZE_OF_DATA_TERMINAL_STRUCT_BITS != + (CHAR_BIT * sizeof(ia_css_data_terminal_t))); + + COMPILATION_ERROR_IF( + 0 != sizeof(ia_css_data_terminal_t)%sizeof(uint64_t)); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_sizeof_terminal(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + if (ia_css_is_terminal_manifest_parameter_terminal(manifest)) { + const ia_css_param_terminal_manifest_t *param_term_man = + (const ia_css_param_terminal_manifest_t *)manifest; + if (ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN) { + size = ia_css_param_in_terminal_get_descriptor_size( + param_term_man->param_manifest_section_desc_count); + } else if (ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + size = ia_css_param_out_terminal_get_descriptor_size( + param_term_man->param_manifest_section_desc_count, + fragment_count); + } else { + assert(NULL == "Invalid parameter terminal type"); + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_sizeof_terminal(): Invalid parameter terminal type:\n"); + verifjmpexit(0); + } + } else if (ia_css_is_terminal_manifest_data_terminal(manifest)) { + size += sizeof(ia_css_data_terminal_t); + size += fragment_count * sizeof(ia_css_fragment_descriptor_t); + } else if (ia_css_is_terminal_manifest_program_terminal(manifest)) { + ia_css_program_terminal_manifest_t *prog_term_man = + (ia_css_program_terminal_manifest_t *)manifest; + + size = ia_css_program_terminal_get_descriptor_size( + fragment_count, + prog_term_man-> + fragment_param_manifest_section_desc_count, + prog_term_man-> + kernel_fragment_sequencer_info_manifest_info_count, + (fragment_count * prog_term_man-> + max_kernel_fragment_sequencer_command_desc)); + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + manifest)) { + ia_css_spatial_param_terminal_manifest_t *spatial_param_term = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + size = ia_css_spatial_param_terminal_get_descriptor_size( + spatial_param_term-> + frame_grid_param_manifest_section_desc_count, + fragment_count); + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + manifest)) { + ia_css_program_control_init_terminal_manifest_t *progctrlinit_term_man = + (ia_css_program_control_init_terminal_manifest_t *)manifest; + + size = ia_css_program_control_init_terminal_get_descriptor_size( + progctrlinit_term_man); + } +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_sizeof_terminal invalid argument\n"); + } + return size; +} + +ia_css_terminal_t *ia_css_terminal_create( + void *raw_mem, + const ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_param_t *terminal_param, + ia_css_kernel_bitmap_t enable_bitmap) +{ + char *terminal_raw_ptr; + ia_css_terminal_t *terminal = NULL; + uint16_t fragment_count; + int i, j; + int retval = -1; + ia_css_program_group_param_t *param; + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_create(manifest %p, terminal_param %p): enter:\n", + manifest, terminal_param); + + param = ia_css_terminal_param_get_parent(terminal_param); + fragment_count = ia_css_program_group_param_get_fragment_count(param); + + verifexit(manifest != NULL); + verifexit(param != NULL); + + terminal_raw_ptr = (char *) raw_mem; + + terminal = (ia_css_terminal_t *) terminal_raw_ptr; + verifexit(terminal != NULL); + + terminal->size = (uint16_t)ia_css_sizeof_terminal(manifest, param); + verifexit(ia_css_terminal_set_type( + terminal, ia_css_terminal_manifest_get_type(manifest)) == 0); + + terminal->ID = ia_css_terminal_manifest_get_ID(manifest); + + verifexit(ia_css_terminal_set_buffer(terminal, + VIED_NULL) == 0); + + if (ia_css_is_terminal_manifest_data_terminal(manifest) == true) { + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + ia_css_kernel_bitmap_t intersection = + ia_css_kernel_bitmap_intersection(enable_bitmap, + ia_css_data_terminal_manifest_get_kernel_bitmap( + (const ia_css_data_terminal_manifest_t *)manifest)); + + verifexit(frame != NULL); + verifexit(ia_css_frame_set_buffer_state( + frame, IA_CSS_BUFFER_NULL) == 0); + verifexit(ia_css_is_kernel_bitmap_onehot(intersection) == + true); + + terminal_raw_ptr += sizeof(ia_css_data_terminal_t); + dterminal->fragment_descriptor_offset = + (uint16_t) (terminal_raw_ptr - (char *)terminal); + + dterminal->kernel_id = 0; + while (!ia_css_is_kernel_bitmap_empty(intersection)) { + intersection = ia_css_kernel_bitmap_shift( + intersection); + dterminal->kernel_id++; + } + assert(dterminal->kernel_id > 0); + dterminal->kernel_id -= 1; + + /* some terminal and fragment initialization */ + dterminal->frame_descriptor.frame_format_type = + terminal_param->frame_format_type; + for (i = 0; i < IA_CSS_N_DATA_DIMENSION; i++) { + dterminal->frame_descriptor.dimension[i] = + terminal_param->dimensions[i]; + } + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] = + terminal_param->stride; + dterminal->frame_descriptor.bpp = terminal_param->bpp; + dterminal->frame_descriptor.bpe = terminal_param->bpe; + switch (dterminal->frame_descriptor.frame_format_type) { + case IA_CSS_DATA_FORMAT_UYVY: + case IA_CSS_DATA_FORMAT_YUYV: + case IA_CSS_DATA_FORMAT_Y800: + case IA_CSS_DATA_FORMAT_RGB565: + case IA_CSS_DATA_FORMAT_RGBA888: + case IA_CSS_DATA_FORMAT_BAYER_GRBG: + case IA_CSS_DATA_FORMAT_BAYER_RGGB: + case IA_CSS_DATA_FORMAT_BAYER_BGGR: + case IA_CSS_DATA_FORMAT_BAYER_GBRG: + case IA_CSS_DATA_FORMAT_RAW: + case IA_CSS_DATA_FORMAT_RAW_PACKED: + case IA_CSS_DATA_FORMAT_YYUVYY_VECTORIZED: + case IA_CSS_DATA_FORMAT_PAF: + dterminal->frame_descriptor.plane_count = 1; + dterminal->frame_descriptor.plane_offsets[0] = 0; + break; + case IA_CSS_DATA_FORMAT_NV12: + case IA_CSS_DATA_FORMAT_NV21: + case IA_CSS_DATA_FORMAT_NV16: + case IA_CSS_DATA_FORMAT_NV61: + dterminal->frame_descriptor.plane_count = 2; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + break; + case IA_CSS_DATA_FORMAT_YUV444: + case IA_CSS_DATA_FORMAT_RGB888: + case IA_CSS_DATA_FORMAT_YUV420_VECTORIZED: + dterminal->frame_descriptor.plane_count = 3; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + dterminal->frame_descriptor.plane_offsets[2] = + dterminal->frame_descriptor.plane_offsets[1] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + break; + case IA_CSS_DATA_FORMAT_YUV420: + dterminal->frame_descriptor.plane_count = 3; + dterminal->frame_descriptor.plane_offsets[0] = 0; + dterminal->frame_descriptor.plane_offsets[1] = + dterminal->frame_descriptor.plane_offsets[0] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION] * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]; + dterminal->frame_descriptor.plane_offsets[2] = + dterminal->frame_descriptor.plane_offsets[1] + + dterminal->frame_descriptor.stride[IA_CSS_COL_DIMENSION]/2 * + dterminal->frame_descriptor.dimension[IA_CSS_ROW_DIMENSION]/2; + break; + default: + /* Unset, resulting in potential terminal connect issues */ + dterminal->frame_descriptor.plane_count = 1; + dterminal->frame_descriptor.plane_offsets[0] = 0; + break; + } + /* + * Initial solution for single fragment initialization + * TODO: + * where to get the fragment description params from??? + */ + if (fragment_count > 0) { + ia_css_fragment_descriptor_t *fragment_descriptor = + (ia_css_fragment_descriptor_t *) + terminal_raw_ptr; + + fragment_descriptor->index[IA_CSS_COL_DIMENSION] = + terminal_param->index[IA_CSS_COL_DIMENSION]; + fragment_descriptor->index[IA_CSS_ROW_DIMENSION] = + terminal_param->index[IA_CSS_ROW_DIMENSION]; + fragment_descriptor->offset[0] = + terminal_param->offset; + for (i = 0; i < IA_CSS_N_DATA_DIMENSION; i++) { + fragment_descriptor->dimension[i] = + terminal_param->fragment_dimensions[i]; + } + } + /* end fragment stuff */ + } else if (ia_css_is_terminal_manifest_parameter_terminal(manifest) == + true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + uint16_t section_count = + ((const ia_css_param_terminal_manifest_t *)manifest)-> + param_manifest_section_desc_count; + size_t curr_offset = 0; + + pterminal->param_section_desc_offset = + sizeof(ia_css_param_terminal_t); + + for (i = 0; i < section_count; i++) { + ia_css_param_section_desc_t *section = + ia_css_param_in_terminal_get_param_section_desc( + pterminal, i); + const ia_css_param_manifest_section_desc_t * + man_section = + ia_css_param_terminal_manifest_get_prm_sct_desc( + (const ia_css_param_terminal_manifest_t *)manifest, i); + + verifjmpexit(man_section != NULL); + verifjmpexit(section != NULL); + + section->mem_size = man_section->max_mem_size; + section->mem_offset = curr_offset; + curr_offset += man_section->max_mem_size; + } + } else if (ia_css_is_terminal_manifest_program_terminal(manifest) == + true && + ia_css_terminal_manifest_get_type(manifest) == + IA_CSS_TERMINAL_TYPE_PROGRAM) { /* for program terminal */ + ia_css_program_terminal_t *prog_terminal = + (ia_css_program_terminal_t *)terminal; + const ia_css_program_terminal_manifest_t *prog_terminal_man = + (const ia_css_program_terminal_manifest_t *)manifest; + ia_css_kernel_fragment_sequencer_info_desc_t + *sequencer_info_desc_base = NULL; + uint16_t section_count = prog_terminal_man-> + fragment_param_manifest_section_desc_count; + uint16_t manifest_info_count = + prog_terminal_man-> + kernel_fragment_sequencer_info_manifest_info_count; + /* information needs to come from user or manifest once + * the size sizeof function is updated. + */ + uint16_t nof_command_objs = 0; + size_t curr_offset = 0; + + prog_terminal->kernel_fragment_sequencer_info_desc_offset = + sizeof(ia_css_program_terminal_t); + prog_terminal->fragment_param_section_desc_offset = + prog_terminal-> + kernel_fragment_sequencer_info_desc_offset + + (fragment_count * manifest_info_count * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (nof_command_objs * + sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + NOT_USED(sequencer_info_desc_base); + for (i = 0; i < fragment_count; i++) { + for (j = 0; j < section_count; j++) { + ia_css_fragment_param_section_desc_t *section = + ia_css_program_terminal_get_frgmnt_prm_sct_desc( + prog_terminal, i, j, section_count); + const ia_css_fragment_param_manifest_section_desc_t * + man_section = +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc + (prog_terminal_man, j); + + verifjmpexit(man_section != NULL); + verifjmpexit(section != NULL); + + section->mem_size = man_section->max_mem_size; + section->mem_offset = curr_offset; + curr_offset += man_section->max_mem_size; + } + + sequencer_info_desc_base = + ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + prog_terminal, i, 0, + manifest_info_count); + + /* + * This offset cannot be initialized properly + * since the number of commands in every sequencer + * is not known at this point + */ + /*for (j = 0; j < manifest_info_count; j++) { + sequencer_info_desc_base[j]. + command_desc_offset = + prog_terminal-> + kernel_fragment_sequencer_info_desc_offset + + (manifest_info_count * + sizeof( + ia_css_kernel_fragment_sequencer_info_desc_t) + + (nof_command_objs * + sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t + )); + }*/ + } + } else if (ia_css_is_terminal_manifest_spatial_parameter_terminal( + manifest) == true) { + ia_css_spatial_param_terminal_t *spatial_param_terminal = + (ia_css_spatial_param_terminal_t *)terminal; + ia_css_spatial_param_terminal_manifest_t * + spatia_param_terminal_man = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + + /* Initialize the spatial terminal structure */ + spatial_param_terminal->fragment_grid_desc_offset = + sizeof(ia_css_spatial_param_terminal_t); + spatial_param_terminal->frame_grid_param_section_desc_offset = + spatial_param_terminal->fragment_grid_desc_offset + + (fragment_count * sizeof(ia_css_fragment_grid_desc_t)); + spatial_param_terminal->kernel_id = + spatia_param_terminal_man->kernel_id; + } else if (ia_css_is_terminal_manifest_sliced_terminal(manifest) == + true) { + ia_css_sliced_param_terminal_t *sliced_param_terminal = + (ia_css_sliced_param_terminal_t *)terminal; + ia_css_sliced_param_terminal_manifest_t + *sliced_param_terminal_man = + (ia_css_sliced_param_terminal_manifest_t *)manifest; + + /* Initialize the sliced terminal structure */ + sliced_param_terminal->fragment_slice_desc_offset = + sizeof(ia_css_sliced_param_terminal_t); + sliced_param_terminal->kernel_id = + sliced_param_terminal_man->kernel_id; + } else if (ia_css_is_terminal_manifest_program_control_init_terminal( + manifest) == true) { + verifjmpexit(ia_css_program_control_init_terminal_init( + (ia_css_program_control_init_terminal_t *) + terminal, + (const ia_css_program_control_init_terminal_manifest_t *) + manifest) == 0); + } else { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_create failed, not a data or param terminal. Returning (%i)\n", + EFAULT); + goto EXIT; + } + + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_create(): Created successfully terminal %p\n", + terminal); + + retval = 0; +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_terminal_create invalid argument\n"); + } + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_create failed (%i)\n", retval); + terminal = ia_css_terminal_destroy(terminal); + } + return terminal; +} + +ia_css_terminal_t *ia_css_terminal_destroy( + ia_css_terminal_t *terminal) +{ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_destroy(terminal %p): enter:\n", terminal); + return terminal; +} + +uint16_t ia_css_param_terminal_compute_section_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) /* Delete 2nd argument*/ +{ + uint16_t section_count = 0; + + NOT_USED(param); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_param_terminal_compute_section_count(): enter:\n"); + + verifexit(manifest != NULL); + section_count = ((const ia_css_param_terminal_manifest_t *)manifest)-> + param_manifest_section_desc_count; +EXIT: + if (NULL == manifest || NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_param_terminal_compute_section_count: invalid argument\n"); + } + return section_count; +} +#endif /* !defined(__VIED_CELL) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h new file mode 100644 index 000000000000..36fb0f1d469a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_impl.h @@ -0,0 +1,1868 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_IMPL_H +#define __IA_CSS_PSYS_TERMINAL_IMPL_H + +#include + +#include +#include + +#include +#include + +#include + + +#include +#include /* for verifexit, verifjmpexit */ +#include /* for COMPILATION_ERROR_IF */ +#include /* for NOT_USED */ +#include "ia_css_psys_terminal_private_types.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_psys_dynamic_trace.h" +#include "ia_css_psys_manifest_types.h" +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_types.h" + +STORAGE_CLASS_INLINE int ia_css_data_terminal_print(const ia_css_terminal_t *terminal, + void *fid) { + + DECLARE_ERRVAL + int retval = -1; + int i; + ia_css_data_terminal_t *dterminal = (ia_css_data_terminal_t *)terminal; + uint16_t fragment_count = + ia_css_data_terminal_get_fragment_count(dterminal); + verifexitval(fragment_count != 0, EINVAL); + + retval = ia_css_frame_descriptor_print( + ia_css_data_terminal_get_frame_descriptor(dterminal), + fid); + verifexitval(retval == 0, EINVAL); + + retval = ia_css_frame_print( + ia_css_data_terminal_get_frame(dterminal), fid); + verifexitval(retval == 0, EINVAL); + + for (i = 0; i < (int)fragment_count; i++) { + retval = ia_css_fragment_descriptor_print( + ia_css_data_terminal_get_fragment_descriptor( + dterminal, i), fid); + verifexitval(retval == 0, EINVAL); + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_print( + const ia_css_terminal_t *terminal, + void *fid) +{ + DECLARE_ERRVAL + int retval = -1; + ia_css_terminal_type_t term_type = ia_css_terminal_get_type(terminal); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, INFO, + "ia_css_terminal_print(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "\tTerminal %p sizeof %d, typeof %d, parent %p\n", + terminal, + (int)ia_css_terminal_get_size(terminal), + (int)ia_css_terminal_get_type(terminal), + (void *)ia_css_terminal_get_parent(terminal)); + + switch (term_type) { + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + ia_css_program_control_init_terminal_print( + (ia_css_program_control_init_terminal_t *)terminal); + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + ia_css_data_terminal_print(terminal, fid); + break; + default: + /* other terminal prints are currently not supported */ + break; + } + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_print failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_input( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + bool is_input = false; + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_input(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_type = ia_css_terminal_get_type(terminal); + + switch (terminal_type) { + case IA_CSS_TERMINAL_TYPE_DATA_IN: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_STATE_IN: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_PARAM_STREAM: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PROGRAM: + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + is_input = true; + break; + case IA_CSS_TERMINAL_TYPE_DATA_OUT: /* Fall through */ + case IA_CSS_TERMINAL_TYPE_STATE_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + is_input = false; + break; + default: + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_input: Unknown terminal type (%d)\n", + terminal_type); + goto EXIT; + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_input invalid argument\n"); + } + return is_input; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +size_t ia_css_terminal_get_size( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_size(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + size = terminal->size; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_type_t ia_css_terminal_get_type( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_type(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_type = terminal->terminal_type; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_type invalid argument\n"); + } + return terminal_type; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_type( + ia_css_terminal_t *terminal, + const ia_css_terminal_type_t terminal_type) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_type(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal->terminal_type = terminal_type; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_type invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_type failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_terminal_get_terminal_manifest_index( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + uint16_t terminal_manifest_index; + + terminal_manifest_index = 0xffff; + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_terminal_manifest_index(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + terminal_manifest_index = terminal->tm_index; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_terminal_manifest_index: invalid argument\n"); + } + return terminal_manifest_index; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_manifest_index( + ia_css_terminal_t *terminal, + const uint16_t terminal_manifest_index) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_terminal_manifest_index(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + terminal->tm_index = terminal_manifest_index; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_manifest_index: invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_manifest_index: failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_terminal_ID_t ia_css_terminal_get_ID( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_ID_t retval = IA_CSS_TERMINAL_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_ID(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + retval = terminal->ID; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_ID invalid argument\n"); + retval = 0; + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_get_kernel_id( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + uint8_t retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_kernel_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + retval = dterminal->kernel_id; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_kernel_id: invalid argument\n"); + retval = 0; + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_connection_type_t ia_css_data_terminal_get_connection_type( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_connection_type_t connection_type = IA_CSS_N_CONNECTION_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_connection_type(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + connection_type = dterminal->connection_type; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_connection_type: invalid argument\n"); + } + return connection_type; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_get_link_id( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + uint8_t link_id = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_link_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + link_id = dterminal->link_id; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_link_id: invalid argument\n"); + } + return link_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_data_terminal_set_link_id( + ia_css_data_terminal_t *dterminal, + const uint8_t link_id) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_set_link_id(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + dterminal->link_id = link_id; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_link_id: invalid argument terminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_link_id: failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_data_terminal_set_connection_type( + ia_css_data_terminal_t *dterminal, + const ia_css_connection_type_t connection_type) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_set_connection_type(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + dterminal->connection_type = connection_type; + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_connection_type: invalid argument dterminal\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_set_connection_type failed (%i)\n", + retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_group_t *ia_css_terminal_get_parent( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_parent(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + parent = (ia_css_process_group_t *) ((char *)terminal + + terminal->parent_offset); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_parent invalid argument\n"); + } + return parent; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_parent( + ia_css_terminal_t *terminal, + ia_css_process_group_t *parent) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_parent(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + verifexitval(parent != NULL, EFAULT); + + terminal->parent_offset = (uint16_t) ((char *)parent - + (char *)terminal); + + retval = 0; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_parent invalid argument\n"); + } + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_parent failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_frame_t *ia_css_data_terminal_get_frame( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_frame_t *frame = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + frame = (ia_css_frame_t *)(&(dterminal->frame)); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame invalid argument\n"); + } + return frame; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_frame_descriptor_t *ia_css_data_terminal_get_frame_descriptor( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_frame_descriptor_t *frame_descriptor = NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame_descriptor(): enter:\n"); + + verifexitval(dterminal != NULL, EFAULT); + + frame_descriptor = + (ia_css_frame_descriptor_t *)(&(dterminal->frame_descriptor)); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame_descriptor: invalid argument\n"); + } + return frame_descriptor; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_fragment_descriptor_t *ia_css_data_terminal_get_fragment_descriptor( + const ia_css_data_terminal_t *dterminal, + const unsigned int fragment_index) +{ + DECLARE_ERRVAL + ia_css_fragment_descriptor_t *fragment_descriptor = NULL; + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_frame_descriptor(): enter:\n"); + + fragment_count = ia_css_data_terminal_get_fragment_count(dterminal); + + verifexitval(dterminal != NULL, EFAULT); + verifexitval(fragment_count != 0, EINVAL); + verifexitval(fragment_index < fragment_count, EINVAL); + + fragment_descriptor = (ia_css_fragment_descriptor_t *) + ((char *)dterminal + dterminal->fragment_descriptor_offset); + + fragment_descriptor += fragment_index; +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_frame_descriptor: invalid argument\n"); + } + return fragment_descriptor; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint16_t ia_css_data_terminal_get_fragment_count( + const ia_css_data_terminal_t *dterminal) +{ + DECLARE_ERRVAL + ia_css_process_group_t *parent; + uint16_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_get_fragment_count(): enter:\n"); + + parent = ia_css_terminal_get_parent((ia_css_terminal_t *)dterminal); + + verifexitval(dterminal != NULL, EFAULT); + verifexitval(parent != NULL, EFAULT); + + fragment_count = ia_css_process_group_get_fragment_count(parent); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_get_fragment_count: invalid argument\n"); + } + return fragment_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_parameter_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_parameter_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_parameter_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_data_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_data_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_data_terminal invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_program_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_program_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_program_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_program_control_init_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_program_control_init_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_program_control_init_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_spatial_parameter_terminal( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_spatial_parameter_terminal(): enter:\n"); + + verifexitval(terminal != NULL, EFAULT); + + /* will return an error value on error */ + terminal_type = ia_css_terminal_get_type(terminal); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_spatial_param_terminal: invalid argument\n"); + } + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT); +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_data_terminal_compute_plane_count( + const ia_css_terminal_manifest_t *manifest, + const ia_css_program_group_param_t *param) +{ + DECLARE_ERRVAL + uint8_t plane_count = 1; + + NOT_USED(manifest); + NOT_USED(param); + + verifexitval(manifest != NULL, EFAULT); + verifexitval(param != NULL, EFAULT); + /* TODO: Implementation Missing*/ + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_data_terminal_compute_plane_count(): enter:\n"); +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_data_terminal_compute_plane_count: invalid argument\n"); + } + return plane_count; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +vied_vaddress_t ia_css_terminal_get_buffer( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + vied_vaddress_t buffer = VIED_NULL; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_buffer(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal)) { + ia_css_frame_t *frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EFAULT); + buffer = ia_css_frame_get_buffer(frame); + } else if (ia_css_is_terminal_parameter_terminal(terminal)) { + const ia_css_param_terminal_t *param_terminal = + (const ia_css_param_terminal_t *)terminal; + + buffer = param_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_program_terminal(terminal)) { + const ia_css_program_terminal_t *program_terminal = + (const ia_css_program_terminal_t *)terminal; + + buffer = program_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal)) { + const ia_css_program_control_init_terminal_t *program_ctrl_init_terminal = + (const ia_css_program_control_init_terminal_t *)terminal; + + buffer = program_ctrl_init_terminal->param_payload.buffer; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal)) { + const ia_css_spatial_param_terminal_t *spatial_terminal = + (const ia_css_spatial_param_terminal_t *)terminal; + + buffer = spatial_terminal->param_payload.buffer; + } +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_buffer: invalid argument terminal\n"); + } + return buffer; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_buffer( + ia_css_terminal_t *terminal, + vied_vaddress_t buffer) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_buffer(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* Currently using Frames inside data terminal , + * TODO: start directly using data. + */ + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + + verifexitval(frame != NULL, EFAULT); + retval = ia_css_frame_set_buffer(frame, buffer); + verifexitval(retval == 0, EINVAL); + } else if (ia_css_is_terminal_parameter_terminal(terminal) == true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_program_terminal(terminal) == true) { + ia_css_program_terminal_t *pterminal = + (ia_css_program_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) == true) { + ia_css_program_control_init_terminal_t *pterminal = + (ia_css_program_control_init_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal) == + true) { + ia_css_spatial_param_terminal_t *pterminal = + (ia_css_spatial_param_terminal_t *)terminal; + + pterminal->param_payload.buffer = buffer; + retval = 0; + } else { + return retval; + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_buffer failed (%i)\n", retval); + } + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_get_terminal_index( + const ia_css_terminal_t *terminal) +{ + DECLARE_ERRVAL + int terminal_index = -1; + + verifexitval(terminal != NULL, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_get_terminal_index(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal)) { + ia_css_frame_t *frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + + verifexitval(frame != NULL, EFAULT); + terminal_index = ia_css_frame_get_data_index(frame); + } else { + if (ia_css_is_terminal_parameter_terminal(terminal)) { + const ia_css_param_terminal_t *param_terminal = + (const ia_css_param_terminal_t *)terminal; + + terminal_index = param_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_program_terminal(terminal)) { + const ia_css_program_terminal_t *program_terminal = + (const ia_css_program_terminal_t *)terminal; + + terminal_index = program_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal)) { + const ia_css_program_control_init_terminal_t *program_ctrl_init_terminal = + (const ia_css_program_control_init_terminal_t *)terminal; + + terminal_index = program_ctrl_init_terminal->param_payload.terminal_index; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal)) { + const ia_css_spatial_param_terminal_t *spatial_terminal = + (const ia_css_spatial_param_terminal_t *)terminal; + + terminal_index = spatial_terminal->param_payload.terminal_index; + } else { + verifjmpexit(0); + } + } +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_get_terminal_index: invalid argument\n"); + } + return terminal_index; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int ia_css_terminal_set_terminal_index( + ia_css_terminal_t *terminal, + unsigned int terminal_index) +{ + DECLARE_ERRVAL + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_terminal_set_terminal_index(): enter:\n"); + + if (ia_css_is_terminal_data_terminal(terminal) == true) { + /* Currently using Frames inside data terminal , + * TODO: start directly using data. + */ + ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + ia_css_frame_t *frame = + ia_css_data_terminal_get_frame(dterminal); + + verifexitval(frame != NULL, EFAULT); + retval = ia_css_frame_set_data_index(frame, terminal_index); + verifexitval(retval == 0, EINVAL); + } else { + if (ia_css_is_terminal_parameter_terminal(terminal) == true) { + ia_css_param_terminal_t *pterminal = + (ia_css_param_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_program_terminal(terminal) == true) { + ia_css_program_terminal_t *pterminal = + (ia_css_program_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_program_control_init_terminal(terminal) + == true) { + ia_css_program_control_init_terminal_t *pterminal = + (ia_css_program_control_init_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else if (ia_css_is_terminal_spatial_parameter_terminal(terminal) == + true) { + ia_css_spatial_param_terminal_t *pterminal = + (ia_css_spatial_param_terminal_t *)terminal; + + pterminal->param_payload.terminal_index = terminal_index; + retval = 0; + } else { + return retval; + } + } + + retval = 0; +EXIT: + if (!noerror()) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, ERROR, + "ia_css_terminal_set_terminal_index failed (%i)\n", + retval); + } + return retval; +} + +STORAGE_CLASS_INLINE bool ia_css_is_data_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + + const ia_css_data_terminal_t *dterminal = + (ia_css_data_terminal_t *)terminal; + const ia_css_data_terminal_manifest_t *dt_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; + const ia_css_frame_descriptor_t *frame_descriptor; + ia_css_frame_format_bitmap_t man_frame_format_bitmap; + ia_css_frame_format_bitmap_t proc_frame_format_bitmap; + uint16_t max_value[IA_CSS_N_DATA_DIMENSION]; + uint16_t min_value[IA_CSS_N_DATA_DIMENSION]; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_data_terminal_valid enter\n"); + + frame_descriptor = + ia_css_data_terminal_get_frame_descriptor(dterminal); + verifexitval(frame_descriptor != NULL, EFAULT); + man_frame_format_bitmap = + ia_css_data_terminal_manifest_get_frame_format_bitmap( + dt_manifest); + proc_frame_format_bitmap = + ia_css_frame_format_bit_mask( + frame_descriptor->frame_format_type); + /* + * TODO: Replace by 'validation of frame format type'. + * Currently frame format type is not correctly set by manifest, + * waiting for HSD 1804260604 + */ + if (man_frame_format_bitmap > 0) { + if ((man_frame_format_bitmap & + proc_frame_format_bitmap) == 0) { + uint32_t *bitmap_arr = + (uint32_t *)&man_frame_format_bitmap; + + NOT_USED(bitmap_arr); + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Frame format type not defined in manifest\n"); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + " man bitmap_arr[]: %d,%d\n", + bitmap_arr[1], bitmap_arr[0]); + bitmap_arr = (uint32_t *)&proc_frame_format_bitmap; + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + " proc bitmap_arr[]: %d,%d\n", + bitmap_arr[1], bitmap_arr[0]); + } + } else { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Frame format bitmap not defined in manifest\n"); + } + ia_css_data_terminal_manifest_get_min_size(dt_manifest, min_value); + /* + * TODO: Replace by validation of Minimal frame column dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if ((frame_descriptor->dimension[IA_CSS_COL_DIMENSION] < + min_value[IA_CSS_COL_DIMENSION])) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Minimal frame column dimensions not set correctly (by manifest)\n"); + } + /* + * TODO: Replace by validation of Minimal frame row dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_ROW_DIMENSION] < + min_value[IA_CSS_ROW_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Minimal frame row dimensions not set correctly (by manifest)\n"); + } + + ia_css_data_terminal_manifest_get_max_size(dt_manifest, max_value); + /* + * TODO: Replace by validation of Maximal frame column dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_COL_DIMENSION] > + max_value[IA_CSS_COL_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Maximal frame column dimensions not set correctly (by manifest)\n"); + } + /* + * TODO: Replace by validation of Maximal frame row dimensions. + * Currently not correctly set by manifest yet, + * waiting for HSD 1804260604 + */ + if (frame_descriptor->dimension[IA_CSS_ROW_DIMENSION] > + max_value[IA_CSS_ROW_DIMENSION]) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "Maximal frame row dimensions not set correctly (by manifest)\n"); + } + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "min_value: [%d,%d]\n", + min_value[IA_CSS_COL_DIMENSION], + min_value[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "max_value: [%d,%d]\n", + max_value[IA_CSS_COL_DIMENSION], + max_value[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, VERBOSE, "frame dim: [%d,%d]\n", + frame_descriptor->dimension[IA_CSS_COL_DIMENSION], + frame_descriptor->dimension[IA_CSS_ROW_DIMENSION]); + /* + * TODO: Add validation of fragment dimensions. + * Currently not set by manifest yet, waiting for HSD 1804260604 + */ + NOT_USED(nof_fragments); + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_data_terminal_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } +} + +STORAGE_CLASS_INLINE void ia_css_program_terminal_seq_info_print( + const ia_css_kernel_fragment_sequencer_info_manifest_desc_t + *man_seq_info_desc, + const ia_css_kernel_fragment_sequencer_info_desc_t + *term_seq_info_desc) +{ + NOT_USED(man_seq_info_desc); + NOT_USED(term_seq_info_desc); + + /* slice dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_dimension[IA_CSS_COL_DIMENSION]); + + /* slice dimension row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_dimension[IA_CSS_ROW_DIMENSION]); + + /* slice count column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_count: %d\n", + term_seq_info_desc-> + fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_count[IA_CSS_COL_DIMENSION]); + + /* slice count row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_slice_count: %d\n", + term_seq_info_desc-> + fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + max_fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_slice_count: %d\n", + man_seq_info_desc-> + min_fragment_grid_slice_count[IA_CSS_ROW_DIMENSION]); + + /* decimation factor column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_point_decimation_factor: %d\n", + term_seq_info_desc-> + fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION] + ); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[IA_CSS_COL_DIMENSION] + ); + + /* decimation factor row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_point_decimation_factor: %d\n", + term_seq_info_desc-> + fragment_grid_point_decimation_factor[IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_point_decimation_factor: %d\n", + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + + /* index column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_topleft_index: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + + /* index row */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_topleft_index: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_topleft_index: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + + /* dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + + /* dimension column */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "fragment_grid_overlay_pixel_dimension: %d\n", + term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "max_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, VERBOSE, + "min_fragment_grid_overlay_pixel_dimension: %d\n", + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); +} + +STORAGE_CLASS_INLINE bool ia_css_is_program_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint16_t frag_idx; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_program_terminal_valid enter\n"); + + for (frag_idx = 0; frag_idx < nof_fragments; frag_idx++) { + uint16_t frag_seq_info_count, seq_idx; + const ia_css_program_terminal_t *prog_term; + const ia_css_program_terminal_manifest_t *prog_term_man; + + prog_term = (const ia_css_program_terminal_t *)terminal; + prog_term_man = + (const ia_css_program_terminal_manifest_t *) + terminal_manifest; + frag_seq_info_count = + prog_term_man-> + kernel_fragment_sequencer_info_manifest_info_count; + + for (seq_idx = 0; seq_idx < frag_seq_info_count; seq_idx++) { + const ia_css_kernel_fragment_sequencer_info_desc_t + *term_seq_info_desc; + const + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + man_seq_info_desc; + + term_seq_info_desc = + ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + prog_term, frag_idx, seq_idx, + frag_seq_info_count); + verifexitval(term_seq_info_desc != NULL, EFAULT); + man_seq_info_desc = + ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc + (prog_term_man, seq_idx); + verifexitval(man_seq_info_desc != NULL, EFAULT); + + ia_css_program_terminal_seq_info_print( + man_seq_info_desc, term_seq_info_desc); + /* slice dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION]); + + /* slice dimension row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + + /* slice count column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION]); + + /* slice count row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + + /* decimation factor column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION]); + + /* decimation factor row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + + /* index column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION]); + + /* index row */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + + /* dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION]); + + /* dimension column */ + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION] > + man_seq_info_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + invalid_flag = invalid_flag || + (term_seq_info_desc-> + fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION] < + man_seq_info_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + } + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_program_terminal_valid() invalid argument\n"); + return false; + } + if (invalid_flag == true) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, WARNING, + "ia_css_is_program_terminal_valid(): validation failed\n"); + /* TODO: program terminal parameters not correctly defined, + * disable validation result until issues has been solved + */ + return true; + } + return (!invalid_flag); +} + +STORAGE_CLASS_INLINE bool ia_css_is_sliced_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest, + const uint16_t nof_fragments) +{ + DECLARE_ERRVAL + bool invalid_flag = false; + uint16_t frag_idx; + + uint16_t slice_idx, section_idx; + + const ia_css_sliced_param_terminal_t *sliced_term = + (const ia_css_sliced_param_terminal_t *)terminal; + const ia_css_sliced_param_terminal_manifest_t *sliced_term_man = + (const ia_css_sliced_param_terminal_manifest_t *) + terminal_manifest; + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_sliced_terminal_valid enter\n"); + + for (frag_idx = 0; frag_idx < nof_fragments; frag_idx++) { + const ia_css_fragment_slice_desc_t *fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_term, frag_idx); + + verifexitval(fragment_slice_desc != NULL, EFAULT); + + for (slice_idx = 0; + slice_idx < fragment_slice_desc->slice_count; + slice_idx++) { + for (section_idx = 0; + section_idx < + sliced_term_man->sliced_param_section_count; + section_idx++) { + const + ia_css_sliced_param_manifest_section_desc_t * + slice_man_section_desc; + const ia_css_slice_param_section_desc_t * + slice_section_desc; + + slice_man_section_desc = + ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + sliced_term_man, section_idx); + slice_section_desc = + ia_css_sliced_param_terminal_get_slice_param_section_desc( + sliced_term, frag_idx, + slice_idx, section_idx, + sliced_term_man-> + sliced_param_section_count); + verifexitval(slice_man_section_desc != NULL, EFAULT); + verifexitval(slice_section_desc != NULL, EFAULT); + + invalid_flag = invalid_flag || + (slice_section_desc->mem_size > + slice_man_section_desc->max_mem_size); + } + } + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_sliced_terminal_valid() invalid argument\n"); + return false; + } else { + return (!invalid_flag); + } + +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +bool ia_css_is_terminal_valid( + const ia_css_terminal_t *terminal, + const ia_css_terminal_manifest_t *terminal_manifest) +{ + DECLARE_ERRVAL + bool is_valid = false; + uint16_t nof_fragments; + ia_css_terminal_type_t terminal_type = IA_CSS_TERMINAL_INVALID_ID; + + verifexitval(NULL != terminal, EFAULT); + verifexitval(NULL != terminal_manifest, EFAULT); + + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, VERBOSE, + "ia_css_is_terminal_valid enter\n"); + + nof_fragments = ia_css_data_terminal_get_fragment_count( + (const ia_css_data_terminal_t *)terminal); + terminal_type = ia_css_terminal_get_type(terminal); + + switch (terminal_type) { + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + is_valid = ia_css_is_data_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + is_valid = ia_css_is_program_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + /* Nothing to be validated for cached and spatial + * parameters, return valid + */ + is_valid = true; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + is_valid = ia_css_is_sliced_terminal_valid(terminal, + terminal_manifest, nof_fragments); + break; + default: + /* Terminal type unknown, return invalid */ + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, WARNING, + "ia_css_is_terminal_valid() Terminal type %x unknown\n", + (int)terminal_type); + is_valid = false; + break; + } + +EXIT: + if (haserror(EFAULT)) { + IA_CSS_TRACE_0(PSYSAPI_DYNAMIC, ERROR, + "ia_css_is_terminal_valid() invalid argument\n"); + return false; + } + /* TODO: to be removed once all PGs pass validation */ + if (is_valid == false) { + IA_CSS_TRACE_1(PSYSAPI_DYNAMIC, INFO, + "ia_css_is_terminal_valid(): type: %d validation failed\n", + terminal_type); + } + return is_valid; +} + +/* ================= Program Control Init Terminal - START ================= */ +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +int +ia_css_program_control_init_terminal_init( + ia_css_program_control_init_terminal_t *terminal, + const ia_css_program_control_init_terminal_manifest_t *manifest) +{ + int retval = -1; + unsigned int i; + unsigned int base_load_sec; + unsigned int base_connect_sec; + unsigned int load_index = 0; + unsigned int connect_index = 0; + unsigned int load_section_count = 0; + unsigned int connect_section_count = 0; + + ia_css_program_control_init_manifest_program_desc_t *man_progs; + + verifjmpexit(terminal != NULL); + + man_progs = + ia_css_program_control_init_terminal_manifest_get_program_desc(manifest, 0); + verifjmpexit(man_progs != NULL); + + for (i = 0; i < manifest->program_count; i++) { + load_section_count += man_progs[i].load_section_count; + connect_section_count += man_progs[i].connect_section_count; + } + + terminal->program_count = manifest->program_count; + terminal->program_section_desc_offset = + sizeof(ia_css_program_control_init_terminal_t); + + base_load_sec = /* base_load_sec relative to first program */ + terminal->program_count * + sizeof(ia_css_program_control_init_program_desc_t); + + base_connect_sec = base_load_sec + + load_section_count * + sizeof(ia_css_program_control_init_load_section_desc_t); + + for (i = 0; i < terminal->program_count; i++) { + ia_css_program_control_init_program_desc_t *prog; + + prog = ia_css_program_control_init_terminal_get_program_desc( + terminal, i); + verifjmpexit(prog != NULL); + + prog->load_section_count = man_progs[i].load_section_count; + prog->connect_section_count = man_progs[i].connect_section_count; + + prog->load_section_desc_offset = + base_load_sec + + load_index * + sizeof(ia_css_program_control_init_load_section_desc_t) - + i * sizeof(ia_css_program_control_init_program_desc_t); + prog->connect_section_desc_offset = + base_connect_sec + + connect_index * + sizeof(ia_css_program_control_init_connect_section_desc_t) - + i * sizeof(ia_css_program_control_init_program_desc_t); + + load_index += man_progs[i].load_section_count; + connect_index += man_progs[i].connect_section_count; + } + retval = 0; +EXIT: + return retval; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +unsigned int +ia_css_program_control_init_terminal_get_descriptor_size( + const ia_css_program_control_init_terminal_manifest_t *manifest) +{ + unsigned int i; + unsigned size = 0; + unsigned load_section_count = 0; + unsigned connect_section_count = 0; + ia_css_program_control_init_manifest_program_desc_t *man_progs; + verifjmpexit(manifest != NULL); + + man_progs = + ia_css_program_control_init_terminal_manifest_get_program_desc( + manifest, 0); + verifjmpexit(man_progs != NULL); + + for (i = 0; i < manifest->program_count; i++) { + load_section_count += man_progs[i].load_section_count; + connect_section_count += man_progs[i].connect_section_count; + } + + size = sizeof(ia_css_program_control_init_terminal_t) + + manifest->program_count * + sizeof(struct ia_css_program_control_init_program_desc_s) + + load_section_count * + sizeof(struct ia_css_program_control_init_load_section_desc_s) + + connect_section_count * + sizeof(struct ia_css_program_control_init_connect_section_desc_s); +EXIT: + return size; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +void ia_css_program_control_init_terminal_print( + const ia_css_program_control_init_terminal_t *terminal) +{ + unsigned int prog_idx, sec_idx; + ia_css_program_control_init_program_desc_t *prog; + ia_css_program_control_init_load_section_desc_t *load_sec; + ia_css_program_control_init_connect_section_desc_t *connect_sec; + + verifjmpexit(terminal != NULL); + + IA_CSS_TRACE_2(PSYSAPI_DYNAMIC, INFO, + "program_count: %d, payload_fragment_stride: %d\n", + terminal->program_count, + terminal->payload_fragment_stride); + + for (prog_idx = 0; prog_idx < terminal->program_count; prog_idx++) { + prog = ia_css_program_control_init_terminal_get_program_desc( + terminal, prog_idx); + verifjmpexit(prog != NULL); + + for (sec_idx = 0; sec_idx < prog->load_section_count; sec_idx++) { + load_sec = + ia_css_program_control_init_terminal_get_load_section_desc( + prog, sec_idx); + verifjmpexit(load_sec != NULL); + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "load_section>> device_descriptor_id: 0x%x, mem_offset: %d, " + "mem_size: %d, mode_bitmask: %x\n", + load_sec->device_descriptor_id.data, + load_sec->mem_offset, + load_sec->mem_size, + load_sec->mode_bitmask); + } + for (sec_idx = 0; sec_idx < prog->connect_section_count; sec_idx++) { + connect_sec = + ia_css_program_control_init_terminal_get_connect_section_desc( + prog, sec_idx); + verifjmpexit(connect_sec != NULL); + IA_CSS_TRACE_4(PSYSAPI_DYNAMIC, INFO, + "connect_section>> device_descriptor_id: 0x%x, " + "connect_terminal_ID: %d, connect_section_idx: %d, " + "mode_bitmask: %x\n", + connect_sec->device_descriptor_id.data, + connect_sec->connect_terminal_ID, + connect_sec->connect_section_idx, + connect_sec->mode_bitmask); + } + } +EXIT: + return; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_program_desc_t * +ia_css_program_control_init_terminal_get_program_desc( + const ia_css_program_control_init_terminal_t *prog_ctrl_init_terminal, + const unsigned int program_index) +{ + ia_css_program_control_init_program_desc_t *program_desc_base; + ia_css_program_control_init_program_desc_t *program_desc = NULL; + + verifjmpexit(prog_ctrl_init_terminal != NULL); + verifjmpexit(program_index < prog_ctrl_init_terminal->program_count); + + program_desc_base = (ia_css_program_control_init_program_desc_t *) + (((const char *)prog_ctrl_init_terminal) + + prog_ctrl_init_terminal->program_section_desc_offset); + program_desc = &(program_desc_base[program_index]); + +EXIT: + return program_desc; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_process_id_t ia_css_program_control_init_terminal_get_process_id( + const ia_css_program_control_init_program_desc_t *program_desc) +{ + ia_css_process_id_t process_id = 0; + + verifjmpexit(program_desc != NULL); + + process_id = program_desc->control_info.process_id; + +EXIT: + return process_id; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +uint8_t ia_css_program_control_init_terminal_get_num_done_events( + const ia_css_program_control_init_program_desc_t *program_desc) +{ + uint8_t num_done_events = 0; + + verifjmpexit(program_desc != NULL); + + num_done_events = program_desc->control_info.num_done_events; + +EXIT: + return num_done_events; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +void ia_css_program_control_init_terminal_set_control_info( + ia_css_program_control_init_program_desc_t *program_desc, + ia_css_process_id_t process_id, + uint8_t num_done_events) +{ + verifjmpexit(program_desc != NULL); + + program_desc->control_info.process_id = process_id; + program_desc->control_info.num_done_events = num_done_events; + +EXIT: + return; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_load_section_desc_t * +ia_css_program_control_init_terminal_get_load_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int load_section_index) +{ + ia_css_program_control_init_load_section_desc_t *load_section_desc_base; + ia_css_program_control_init_load_section_desc_t *load_section_desc = NULL; + + verifjmpexit(program_desc != NULL); + verifjmpexit(load_section_index < program_desc->load_section_count); + + load_section_desc_base = (ia_css_program_control_init_load_section_desc_t *) + (((const char *)program_desc) + + program_desc->load_section_desc_offset); + load_section_desc = &(load_section_desc_base[load_section_index]); + +EXIT: + return load_section_desc; +} + +IA_CSS_PSYS_DYNAMIC_STORAGE_CLASS_C +ia_css_program_control_init_connect_section_desc_t * +ia_css_program_control_init_terminal_get_connect_section_desc( + const ia_css_program_control_init_program_desc_t *program_desc, + const unsigned int connect_section_index) +{ + ia_css_program_control_init_connect_section_desc_t *connect_sec_desc_base; + ia_css_program_control_init_connect_section_desc_t *connect_sec_desc = NULL; + + verifjmpexit(program_desc != NULL); + verifjmpexit(connect_section_index < program_desc->connect_section_count); + + connect_sec_desc_base = + (ia_css_program_control_init_connect_section_desc_t *) + (((const char *)program_desc) + + program_desc->connect_section_desc_offset); + connect_sec_desc = &(connect_sec_desc_base[connect_section_index]); + +EXIT: + return connect_sec_desc; +} + +/* ================= Program Control Init Terminal - END ================= */ + +#endif /* __IA_CSS_PSYS_TERMINAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h new file mode 100644 index 000000000000..68626561acb5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/dynamic/src/ia_css_psys_terminal_private_types.h @@ -0,0 +1,186 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H +#define __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H + +#include "ia_css_terminal_types.h" +#include "ia_css_program_group_data.h" +#include "ia_css_psys_manifest_types.h" + +#define N_UINT16_IN_DATA_TERMINAL_STRUCT 1 +#define N_UINT8_IN_DATA_TERMINAL_STRUCT 3 +#define N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT 3 + +/* ========================= Data terminal - START ========================= */ + +#define SIZE_OF_DATA_TERMINAL_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_FRAME_DESCRIPTOR_STRUCT_BITS \ + + IA_CSS_FRAME_STRUCT_BITS \ + + IA_CSS_STREAM_STRUCT_BITS \ + + IA_CSS_UINT32_T_BITS \ + + IA_CSS_CONNECTION_TYPE_BITS \ + + (N_UINT16_IN_DATA_TERMINAL_STRUCT * 16) \ + + (N_UINT8_IN_DATA_TERMINAL_STRUCT * 8) \ + + (N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT * 8)) + +/* + * The (data) terminal can be attached to a buffer or a stream. + * The stream interface is not necessarily limited to strict in-order access. + * For a stream the restriction is that contrary to a buffer it cannot be + * addressed directly, i.e. it behaves as a port, + * but it may support stream_pos() and/or seek() operations + */ +struct ia_css_data_terminal_s { + /**< Data terminal base */ + ia_css_terminal_t base; + /**< Properties of the data attached to the terminal */ + ia_css_frame_descriptor_t frame_descriptor; + /**< Data buffer handle attached to the terminal */ + ia_css_frame_t frame; + /**< (exclusive) Data stream handle attached to the terminal + * if the data is sourced over a device port + */ + ia_css_stream_t stream; + /**< Reserved */ + uint32_t reserved; + /**< Connection {buffer, stream, ...} */ + ia_css_connection_type_t connection_type; + /**< Array[fragment_count] (fragment_count being equal for all + * terminals in a subgraph) of fragment descriptors + */ + uint16_t fragment_descriptor_offset; + /**< Kernel id where this terminal is connected to */ + uint8_t kernel_id; + /**< Indicate to which subgraph this terminal belongs + * for common constraints + */ + uint8_t subgraph_id; + /* Link ID of the data terminal */ + uint8_t link_id; + /**< Padding for 64bit alignment */ + uint8_t padding[N_PADDING_UINT8_IN_DATA_TERMINAL_STRUCT]; +}; +/* ========================== Data terminal - END ========================== */ + +/* ================= Program Control Init Terminal - START ================= */ +#define SIZE_OF_PROG_CONTROL_INIT_LOAD_SECTION_DESC_STRUCT_BITS \ + (DEVICE_DESCRIPTOR_ID_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + ) +struct ia_css_program_control_init_load_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; + /* Device descriptor */ + device_descriptor_id_t device_descriptor_id; /* 32 bits */ + /* (Applicable to) mode bitmask */ + uint32_t mode_bitmask; +}; + +#define MODE_BITMASK_MEMORY (1u << IA_CSS_CONNECTION_MEMORY) +#define MODE_BITMASK_MEMORY_STREAM (1u << IA_CSS_CONNECTION_MEMORY_STREAM) +#define MODE_BITMASK_STREAM (1u << IA_CSS_CONNECTION_STREAM) +#define MODE_BITMASK_DONT_CARE (MODE_BITMASK_MEMORY | MODE_BITMASK_MEMORY_STREAM | MODE_BITMASK_STREAM) + +#define N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT (5) +#define SIZE_OF_PROG_CONTROL_INIT_CONNECT_SECTION_DESC_STRUCT_BITS \ + (DEVICE_DESCRIPTOR_ID_BITS \ + + (1 * IA_CSS_UINT32_T_BITS) \ + + (1 * IA_CSS_UINT16_T_BITS) \ + + IA_CSS_TERMINAL_ID_BITS \ + + (N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT * \ + IA_CSS_UINT8_T_BITS) \ + ) +struct ia_css_program_control_init_connect_section_desc_s { + /* Device descriptor */ + device_descriptor_id_t device_descriptor_id; /* 32 bits */ + /* (Applicable to) mode bitmask */ + uint32_t mode_bitmask; + /* Connected terminal section (plane) index */ + uint16_t connect_section_idx; + /* Absolute referral ID for the connected terminal */ + ia_css_terminal_ID_t connect_terminal_ID; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_CTRL_INIT_CONNECT_SECT_STRUCT]; +}; + +#define N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO (1) +#define N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT (4) +#define SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS \ + (1 * IA_CSS_UINT16_T_BITS) \ + + (1 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO * IA_CSS_UINT8_T_BITS) + +#define SIZE_OF_PROG_CONTROL_INIT_PROG_DESC_STRUCT_BITS \ + (4 * IA_CSS_UINT16_T_BITS) \ + + (SIZE_OF_PROGRAM_DESC_CONTROL_INFO_STRUCT_BITS) \ + + (N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT * \ + IA_CSS_UINT8_T_BITS) + +struct ia_css_program_desc_control_info_s { + /* 12-bit process identifier */ + ia_css_process_id_t process_id; + /* number of done acks required to close the process */ + uint8_t num_done_events; + uint8_t padding[N_PADDING_UINT8_IN_PROG_DESC_CONTROL_INFO]; +}; + +struct ia_css_program_control_init_program_desc_s { + /* Number of load sections in this program */ + uint16_t load_section_count; + /* Points to variable size array of + * ia_css_program_control_init_load_section_desc_s + * in relation to its program_desc + */ + uint16_t load_section_desc_offset; + /* Number of connect sections in this program */ + uint16_t connect_section_count; + /* Points to variable size array of + * ia_css_program_control_init_connect_section_desc_s + * in relation to its program_desc + */ + uint16_t connect_section_desc_offset; + struct ia_css_program_desc_control_info_s control_info; + /* align to 64 bits */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_CTRL_INIT_PROGRAM_DESC_STRUCT]; +}; + +#define SIZE_OF_PROG_CONTROL_INIT_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + (1 * IA_CSS_UINT32_T_BITS) \ + + (2 * IA_CSS_UINT16_T_BITS) \ + ) +struct ia_css_program_control_init_terminal_s { + /* Parameter terminal base */ + ia_css_terminal_t base; + /* Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Fragment stride for the payload, used to find the base + * of the payload for a given fragment + */ + uint32_t payload_fragment_stride; + /* Points to the variable array of + * ia_css_program_control_init_program_desc_s + */ + uint16_t program_section_desc_offset; + /* Number of instantiated programs in program group (processes) */ + uint16_t program_count; +}; +/* ================= Program Control Init Terminal - END ================= */ + +#endif /* __IA_CSS_PSYS_TERMINAL_PRIVATE_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h new file mode 100644 index 000000000000..4c8fd33b331c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi.h @@ -0,0 +1,23 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYSAPI_H +#define __IA_CSS_PSYSAPI_H + +#include +#include +#include +#include + +#endif /* __IA_CSS_PSYSAPI_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h new file mode 100644 index 000000000000..5658a2988a08 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_fw_version.h @@ -0,0 +1,33 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_PSYSAPI_FW_VERSION_H +#define __IA_CSS_PSYSAPI_FW_VERSION_H + +/* PSYSAPI FW VERSION is taken from Makefile for FW tests */ +#define BXT_FW_RELEASE_VERSION PSYS_FIRMWARE_VERSION + +enum ia_css_process_group_protocol_version { + /* + * Legacy protocol + */ + IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY = 0, + /* + * Persistent process group support protocol + */ + IA_CSS_PROCESS_GROUP_PROTOCOL_PPG, + IA_CSS_PROCESS_GROUP_N_PROTOCOLS +}; + +#endif /* __IA_CSS_PSYSAPI_FW_VERSION_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h new file mode 100644 index 000000000000..e35ec24c77b3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/interface/ia_css_psysapi_trace.h @@ -0,0 +1,78 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYSAPI_TRACE_H +#define __IA_CSS_PSYSAPI_TRACE_H + +#include "ia_css_trace.h" + +#define PSYSAPI_TRACE_LOG_LEVEL_OFF 0 +#define PSYSAPI_TRACE_LOG_LEVEL_NORMAL 1 +#define PSYSAPI_TRACE_LOG_LEVEL_DEBUG 2 + +/* PSYSAPI and all the submodules in PSYSAPI will have the default tracing + * level set to the PSYSAPI_TRACE_CONFIG level. If not defined in the + * psysapi.mk fill it will be set by default to no trace + * (PSYSAPI_TRACE_LOG_LEVEL_OFF) + */ +#define PSYSAPI_TRACE_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +#if !defined(PSYSAPI_TRACE_CONFIG) + #define PSYSAPI_TRACE_CONFIG PSYSAPI_TRACE_CONFIG_DEFAULT +#endif + +/* Module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_TRACE_CONFIG)) + /* Module specific trace setting */ + #if PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_TRACE_CONFIG == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_TRACE_CONFIG Tracing level defined" + #endif +#else + #error "PSYSAPI_TRACE_CONFIG not defined" +#endif + +/* Overriding submodules in PSYSAPI with a specific tracing level */ +/* #define PSYSAPI_DYNAMIC_TRACING_OVERRIDE TRACE_LOG_LEVEL_VERBOSE */ + +#endif /* __IA_CSS_PSYSAPI_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h new file mode 100644 index 000000000000..3fec775eb019 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_kernel_bitmap.h @@ -0,0 +1,223 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_KERNEL_BITMAP_H +#define __IA_CSS_KERNEL_BITMAP_H + +/*! \file */ + +/** @file ia_css_kernel_bitmap.h + * + * The types and operations to make logic decisions given kernel bitmaps + * "ia_css_kernel_bitmap_t" can be larger than native types + */ + +#include +#include "vied_nci_psys_resource_model.h" + +#define IA_CSS_KERNEL_BITMAP_BITS 64 +#define IA_CSS_KERNEL_BITMAP_ELEM_TYPE uint32_t +#define IA_CSS_KERNEL_BITMAP_ELEM_BITS \ + (sizeof(IA_CSS_KERNEL_BITMAP_ELEM_TYPE)*8) +#define IA_CSS_KERNEL_BITMAP_NOF_ELEMS \ + ((IA_CSS_KERNEL_BITMAP_BITS) / (IA_CSS_KERNEL_BITMAP_ELEM_BITS)) + +/** An element is a 32 bit unsigned integer. 64 bit integers might cause + * problems in the compiler. + */ +typedef struct { + IA_CSS_KERNEL_BITMAP_ELEM_TYPE data[IA_CSS_KERNEL_BITMAP_NOF_ELEMS]; +} ia_css_kernel_bitmap_elems_t; + +/** Users should make no assumption about the actual type of + * ia_css_kernel_bitmap_t. + * Users should use IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS in + * case they erroneously assume that this type is uint64_t and they + * cannot change their implementation. + */ +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS +typedef ia_css_kernel_bitmap_elems_t ia_css_kernel_bitmap_t; +#else +typedef uint64_t ia_css_kernel_bitmap_t; +#if IA_CSS_KERNEL_BITMAP_BITS > 64 +#error IA_CSS_KERNEL_BITMAP_BITS > 64 not supported \ + with IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS +#endif +#endif + +/*! Print the bits of a kernel bitmap + + @return < 0 on error + */ +extern int ia_css_kernel_bitmap_print( + const ia_css_kernel_bitmap_t bitmap, + void *fid); + +/*! Create an empty kernel bitmap + + @return bitmap = 0 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_clear(void); + +/*! Creates the complement of a kernel bitmap + * @param bitmap[in] kernel bitmap + * @return ~bitmap + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_complement( + const ia_css_kernel_bitmap_t bitmap); + +/*! Create the union of two kernel bitmaps + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 | bitmap1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_union( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Create the intersection of two kernel bitmaps + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 & bitmap1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_intersection( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the kernel bitmaps is empty + + @param bitmap[in] kernel bitmap + + @return bitmap == 0 + */ +extern bool ia_css_is_kernel_bitmap_empty( + const ia_css_kernel_bitmap_t bitmap); + +/*! Check if the intersection of two kernel bitmaps is empty + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return (bitmap0 & bitmap1) == 0 + */ +extern bool ia_css_is_kernel_bitmap_intersection_empty( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the second kernel bitmap is a subset of the first (or equal) + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + Note: An empty set is always a subset, this function + returns true if bitmap 1 is empty + + @return (bitmap0 & bitmap1) == bitmap1 + */ +extern bool ia_css_is_kernel_bitmap_subset( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Check if the kernel bitmaps are equal + + @param bitmap0[in] kernel bitmap 0 + @param bitmap1[in] kernel bitmap 1 + + @return bitmap0 == bitmap1 + */ +extern bool ia_css_is_kernel_bitmap_equal( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1); + +/*! Right shift kernel bitmap + + @param bitmap0[in] kernel bitmap 0 + + @return bitmap0 >> 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_shift( + const ia_css_kernel_bitmap_t bitmap); + +/*! Check if the kernel bitmaps contains only a single element + + @param bitmap[in] kernel bitmap + + @return weight(bitmap) == 1 + */ +extern bool ia_css_is_kernel_bitmap_onehot( + const ia_css_kernel_bitmap_t bitmap); + +/*! Checks whether a specific kernel bit is set + * @return bitmap[index] == 1 + */ +extern int ia_css_is_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Create the union of a kernel bitmap with a onehot bitmap + * with a bit set at index + + @return bitmap[index] |= 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Creates kernel bitmap using a uint64 value. + * @return bitmap with the same bits set as in value (provided that width of bitmap is sufficient). + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_create_from_uint64( + const uint64_t value); + +/*! Converts an ia_css_kernel_bitmap_t type to uint64_t. Note that if + * ia_css_kernel_bitmap_t contains more then 64 bits, only the lowest 64 bits + * are returned. + * @return uint64_t representation of value +*/ +extern uint64_t ia_css_kernel_bitmap_to_uint64( + const ia_css_kernel_bitmap_t value); + +/*! Creates a kernel bitmap with the bit at index 'index' removed. + * @return ~(1 << index) & bitmap + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_unset( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Set a previously clear field of a kernel bitmap at index + + @return if bitmap[index] == 0, bitmap[index] -> 1, else 0 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set_unique( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index); + +/*! Create a onehot kernel bitmap with a bit set at index + + @return bitmap[index] = 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_bit_mask( + const unsigned int index); + +/*! Create a random bitmap + + @return bitmap[index] = 1 + */ +extern ia_css_kernel_bitmap_t ia_css_kernel_ran_bitmap(void); + +#endif /* __IA_CSS_KERNEL_BITMAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h new file mode 100644 index 000000000000..1ba29c7ab77e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/interface/ia_css_psys_kernel_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_KERNEL_TRACE_H +#define __IA_CSS_PSYS_KERNEL_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_KERNEL_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_KERNEL_TRACING_OVERRIDE)) + #define PSYS_KERNEL_TRACE_LEVEL_CONFIG \ + PSYS_KERNEL_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_KERNEL_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_KERNEL_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_KERNEL_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_KERNEL_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_KERNEL_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_KERNEL_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_KERNEL_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_KERNEL_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_KERNEL_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_KERNEL_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_KERNEL_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c new file mode 100644 index 000000000000..5fd9496bc3cc --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/kernel/src/ia_css_kernel_bitmap.c @@ -0,0 +1,413 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include +#include +#include +#include "ia_css_psys_kernel_trace.h" + +static int ia_css_kernel_bitmap_compute_weight( + const ia_css_kernel_bitmap_t bitmap); + +bool ia_css_is_kernel_bitmap_intersection_empty( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + ia_css_kernel_bitmap_t intersection; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_intersection_empty(): enter:\n"); + + intersection = ia_css_kernel_bitmap_intersection(bitmap0, bitmap1); + return ia_css_is_kernel_bitmap_empty(intersection); +} + +bool ia_css_is_kernel_bitmap_empty( + const ia_css_kernel_bitmap_t bitmap) +{ + unsigned int i; + bool is_empty = true; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_empty(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + is_empty &= bitmap.data[i] == 0; + } +#else + NOT_USED(i); + is_empty = (bitmap == 0); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return is_empty; +} + +bool ia_css_is_kernel_bitmap_equal( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + bool is_equal = true; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_equal(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + is_equal = is_equal && (bitmap0.data[i] == bitmap1.data[i]); + } +#else + NOT_USED(i); + is_equal = (bitmap0 == bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return is_equal; +} + +bool ia_css_is_kernel_bitmap_onehot( + const ia_css_kernel_bitmap_t bitmap) +{ + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_onehot(): enter:\n"); + return ia_css_kernel_bitmap_compute_weight(bitmap) == 1; +} + +bool ia_css_is_kernel_bitmap_subset( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + ia_css_kernel_bitmap_t intersection; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_subset(): enter:\n"); + + intersection = ia_css_kernel_bitmap_intersection(bitmap0, bitmap1); + return ia_css_is_kernel_bitmap_equal(intersection, bitmap1); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_clear(void) +{ + unsigned int i; + ia_css_kernel_bitmap_t bitmap; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_clear(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + bitmap.data[i] = 0; + } +#else + NOT_USED(i); + bitmap = 0; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return bitmap; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_complement( + const ia_css_kernel_bitmap_t bitmap) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_complement(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = ~bitmap.data[i]; + } +#else + NOT_USED(i); + result = ~bitmap; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_union( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_union(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] | bitmap1.data[i]); + } +#else + NOT_USED(i); + result = (bitmap0 | bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_intersection( + const ia_css_kernel_bitmap_t bitmap0, + const ia_css_kernel_bitmap_t bitmap1) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_intersection(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] & bitmap1.data[i]); + } +#else + NOT_USED(i); + result = (bitmap0 & bitmap1); +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return result; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_set(): enter:\n"); + + bit_mask = ia_css_kernel_bit_mask(index); + return ia_css_kernel_bitmap_union(bitmap, bit_mask); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_create_from_uint64( + const uint64_t value) +{ + unsigned int i; + ia_css_kernel_bitmap_t result; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_create_from_uint64(): enter:\n"); + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + result = ia_css_kernel_bitmap_clear(); + for (i = 0; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + /* masking is done implictly, the MSB bits of casting will be chopped off */ + result.data[i] = (IA_CSS_KERNEL_BITMAP_ELEM_TYPE) + (value >> (i * IA_CSS_KERNEL_BITMAP_ELEM_BITS)); + } +#if IA_CSS_KERNEL_BITMAP_BITS < 64 + if ((value >> IA_CSS_KERNEL_BITMAP_BITS) != 0) { + IA_CSS_TRACE_0(PSYSAPI_KERNEL, ERROR, + "ia_css_kernel_bitmap_create_from_uint64(): " + "kernel bitmap is not wide enough to encode value\n"); + assert(0); + } +#endif +#else + NOT_USED(i); + result = value; +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ + return result; +} + +uint64_t ia_css_kernel_bitmap_to_uint64( + const ia_css_kernel_bitmap_t value) +{ + const unsigned int bits64 = sizeof(uint64_t) * 8; + const unsigned int nof_elems_bits64 = bits64 / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + unsigned int i; + uint64_t res = 0; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_to_uint64(): enter:\n"); + + assert((bits64 % IA_CSS_KERNEL_BITMAP_ELEM_BITS) == 0); + assert(nof_elems_bits64 > 0); + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = 0; i < nof_elems_bits64; i++) { + res |= ((uint64_t)(value.data[i]) << (i * IA_CSS_KERNEL_BITMAP_ELEM_BITS)); + } + for (i = nof_elems_bits64; i < IA_CSS_KERNEL_BITMAP_NOF_ELEMS; i++) { + assert(value.data[i] == 0); + } + return res; +#else + (void)i; + (void)res; + (void)nof_elems_bits64; + return (uint64_t)value; +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_unset( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t result; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_unset(): enter:\n"); + + result = ia_css_kernel_bit_mask(index); + result = ia_css_kernel_bitmap_complement(result); + return ia_css_kernel_bitmap_intersection(bitmap, result); +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_set_unique( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + ia_css_kernel_bitmap_t ret; + ia_css_kernel_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_set_unique(): enter:\n"); + + ret = ia_css_kernel_bitmap_clear(); + bit_mask = ia_css_kernel_bit_mask(index); + + if (ia_css_is_kernel_bitmap_intersection_empty(bitmap, bit_mask) + && !ia_css_is_kernel_bitmap_empty(bit_mask)) { + ret = ia_css_kernel_bitmap_union(bitmap, bit_mask); + } + return ret; +} + +ia_css_kernel_bitmap_t ia_css_kernel_bit_mask( + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + ia_css_kernel_bitmap_t bit_mask = ia_css_kernel_bitmap_clear(); + + /* Assert disabled for staging, because some PGs do not satisfy this condition */ + /* assert(index < IA_CSS_KERNEL_BITMAP_BITS); */ + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bit_mask(): enter:\n"); +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + if (index < IA_CSS_KERNEL_BITMAP_BITS) { + elem_index = index / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + elem_bit_index = index % IA_CSS_KERNEL_BITMAP_ELEM_BITS; + assert(elem_index < IA_CSS_KERNEL_BITMAP_NOF_ELEMS); + + bit_mask.data[elem_index] = 1 << elem_bit_index; + } +#else + NOT_USED(elem_index); + NOT_USED(elem_bit_index); + if (index < IA_CSS_KERNEL_BITMAP_BITS) { + bit_mask = (ia_css_kernel_bitmap_t)1 << index; + } +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return bit_mask; +} + + +static int ia_css_kernel_bitmap_compute_weight( + const ia_css_kernel_bitmap_t bitmap) +{ + ia_css_kernel_bitmap_t loc_bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_compute_weight(): enter:\n"); + + loc_bitmap = bitmap; + + /* In fact; do not need the iterator "i" */ + for (i = 0; (i < IA_CSS_KERNEL_BITMAP_BITS) && + !ia_css_is_kernel_bitmap_empty(loc_bitmap); i++) { + weight += ia_css_is_kernel_bitmap_set(loc_bitmap, 0); + loc_bitmap = ia_css_kernel_bitmap_shift(loc_bitmap); + } + + return weight; +} + +int ia_css_is_kernel_bitmap_set( + const ia_css_kernel_bitmap_t bitmap, + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_is_kernel_bitmap_set(): enter:\n"); + + /* Assert disabled for staging, because some PGs do not satisfy this condition */ + /* assert(index < IA_CSS_KERNEL_BITMAP_BITS); */ + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + elem_index = index / IA_CSS_KERNEL_BITMAP_ELEM_BITS; + elem_bit_index = index % IA_CSS_KERNEL_BITMAP_ELEM_BITS; + assert(elem_index < IA_CSS_KERNEL_BITMAP_NOF_ELEMS); + return (((bitmap.data[elem_index] >> elem_bit_index) & 0x1) == 1); +#else + NOT_USED(elem_index); + NOT_USED(elem_bit_index); + return (((bitmap >> index) & 0x1) == 1); +#endif /* IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS */ +} + +ia_css_kernel_bitmap_t ia_css_kernel_bitmap_shift( + const ia_css_kernel_bitmap_t bitmap) +{ + int i; + unsigned int lsb_current_elem = 0; + unsigned int lsb_previous_elem = 0; + ia_css_kernel_bitmap_t loc_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, VERBOSE, + "ia_css_kernel_bitmap_shift(): enter:\n"); + + loc_bitmap = bitmap; + +#ifndef IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + for (i = IA_CSS_KERNEL_BITMAP_NOF_ELEMS - 1; i >= 0; i--) { + lsb_current_elem = bitmap.data[i] & 0x01; + loc_bitmap.data[i] >>= 1; + loc_bitmap.data[i] |= (lsb_previous_elem << (IA_CSS_KERNEL_BITMAP_ELEM_BITS - 1)); + lsb_previous_elem = lsb_current_elem; + } +#else + NOT_USED(i); + NOT_USED(lsb_current_elem); + NOT_USED(lsb_previous_elem); + loc_bitmap >>= 1; +#endif /* IA_CSS_KERNEL_BITMAP_USE_ELEMS */ + return loc_bitmap; +} + +int ia_css_kernel_bitmap_print( + const ia_css_kernel_bitmap_t bitmap, + void *fid) +{ + int retval = -1; + int bit; + unsigned int bit_index = 0; + ia_css_kernel_bitmap_t loc_bitmap; + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, + "ia_css_kernel_bitmap_print(): enter:\n"); + + NOT_USED(fid); + NOT_USED(bit); + + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, "kernel bitmap {\n"); + + loc_bitmap = bitmap; + + for (bit_index = 0; (bit_index < IA_CSS_KERNEL_BITMAP_BITS) && + !ia_css_is_kernel_bitmap_empty(loc_bitmap); bit_index++) { + + bit = ia_css_is_kernel_bitmap_set(loc_bitmap, 0); + loc_bitmap = ia_css_kernel_bitmap_shift(loc_bitmap); + IA_CSS_TRACE_2(PSYSAPI_KERNEL, INFO, "\t%d\t = %d\n", bit_index, bit); + } + IA_CSS_TRACE_0(PSYSAPI_KERNEL, INFO, "}\n"); + + retval = 0; + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h new file mode 100644 index 000000000000..485dd63e5a86 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.h @@ -0,0 +1,293 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_H + +/*! \file */ + +/** @file ia_css_program_group_param.h + * + * Define the methods on the program group parameter object that are not part + * of a single interface + */ +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ + +#include + +/*! Get the stored size of the program group parameter object + + @param param[in] program group parameter object + + @return size, 0 on error + */ +extern size_t ia_css_program_group_param_get_size( + const ia_css_program_group_param_t *param); + +/*! initialize program_group_param + + @param blob[in] program group parameter object + @param program_count[in] number of terminals. + @param terminal_count[in] number of terminals. + @param fragment_count[in] number of terminals. + + @return 0 if success, else failure. + */ +extern int ia_css_program_group_param_init( + ia_css_program_group_param_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count, + const enum ia_css_frame_format_type *frame_format_types); +/*! Get the program parameter object from a program group parameter object + + @param program_group_param[in] program group parameter object + @param i[in] program parameter index + + @return program parameter pointer, NULL on error + */ +extern ia_css_program_param_t *ia_css_program_group_param_get_program_param( + const ia_css_program_group_param_t *param, + const int i); + +/*! Get the terminal parameter object from a program group parameter object + + @param program_group_param[in] program group parameter object + @param i[in] terminal parameter index + + @return terminal parameter pointer, NULL on error + */ +extern ia_css_terminal_param_t *ia_css_program_group_param_get_terminal_param( + const ia_css_program_group_param_t *param, + const int i); + +/*! Get the fragment count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return fragment count, 0 on error + */ +extern uint16_t ia_css_program_group_param_get_fragment_count( + const ia_css_program_group_param_t *param); + +/*! Get the program count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return program count, 0 on error + */ +extern uint8_t ia_css_program_group_param_get_program_count( + const ia_css_program_group_param_t *param); + +/*! Get the terminal count from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return terminal count, 0 on error + */ +extern uint8_t ia_css_program_group_param_get_terminal_count( + const ia_css_program_group_param_t *param); + +/*! Set the protocol version in a program group parameter object + + @param program_group_param[in] program group parameter object + @param protocol_version[in] protocol version + + @return nonzero on error +*/ +extern int +ia_css_program_group_param_set_protocol_version( + ia_css_program_group_param_t *param, + uint8_t protocol_version); + +/*! Get the protocol version from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return protocol version +*/ +extern uint8_t +ia_css_program_group_param_get_protocol_version( + const ia_css_program_group_param_t *param); + +/*! Set the kernel enable bitmap from a program group parameter object + + @param param[in] program group parameter object + @param bitmap[in] kernel enable bitmap + + @return non-zero on error + */ +extern int ia_css_program_group_param_set_kernel_enable_bitmap( + ia_css_program_group_param_t *param, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the kernel enable bitmap from a program group parameter object + + @param program_group_param[in] program group parameter object + + @return kernel enable bitmap, 0 on error +*/ +extern ia_css_kernel_bitmap_t +ia_css_program_group_param_get_kernel_enable_bitmap( + const ia_css_program_group_param_t *param); + +/*! Get the stored size of the program parameter object + + @param param[in] program parameter object + + @return size, 0 on error + */ +extern size_t ia_css_program_param_get_size( + const ia_css_program_param_t *param); + +/*! Set the kernel enable bitmap from a program parameter object + + @param program_param[in] program parameter object + @param bitmap[in] kernel enable bitmap + + @return non-zero on error + */ +extern int ia_css_program_param_set_kernel_enable_bitmap( + ia_css_program_param_t *program_param, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the kernel enable bitmap from a program parameter object + + @param program_param[in] program parameter object + + Note: This function returns in fact the kernel enable of the program group + parameters + + @return kernel enable bitmap, 0 on error + */ +extern ia_css_kernel_bitmap_t ia_css_program_param_get_kernel_enable_bitmap( + const ia_css_program_param_t *param); + +/*! Get the stored size of the terminal parameter object + + @param param[in] terminal parameter object + + @return size, 0 on error + */ +extern size_t ia_css_terminal_param_get_size( + const ia_css_terminal_param_t *param); + +/*! Get the kernel enable bitmap from a terminal parameter object + + @param terminal_param[in] terminal parameter object + + Note: This function returns in fact the kernel enable of the program group + parameters + + @return kernel enable bitmap, 0 on error + */ +extern ia_css_kernel_bitmap_t ia_css_terminal_param_get_kernel_enable_bitmap( + const ia_css_terminal_param_t *param); + +/*! Get the parent object for this terminal param. + + @param terminal_param[in] terminal parameter object + + @return parent program group param object + */ +extern ia_css_program_group_param_t *ia_css_terminal_param_get_parent( + const ia_css_terminal_param_t *param); + +/*! Get the data format type associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return data format type (ia_css_data_format_type_t) + */ +extern ia_css_frame_format_type_t ia_css_terminal_param_get_frame_format_type( + const ia_css_terminal_param_t *terminal_param); + +/*! Set the data format type associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param data_format_type[in] data format type + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_frame_format_type( + ia_css_terminal_param_t *terminal_param, + const ia_css_frame_format_type_t data_format_type); + +/*! Get bits per pixel on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return bits per pixel + */ +extern uint8_t ia_css_terminal_param_get_bpp( + const ia_css_terminal_param_t *terminal_param); + +/*! Set bits per pixel on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param bpp[in] bits per pixel + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_bpp( + ia_css_terminal_param_t *terminal_param, + const uint8_t bpp); + +/*! Get dimensions on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param dimensions[out] dimension array + + @return non-zero on error. + */ +extern int ia_css_terminal_param_get_dimensions( + const ia_css_terminal_param_t *terminal_param, + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]); + +/*! Set dimensions on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param dimensions[in] dimension array + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_dimensions( + ia_css_terminal_param_t *terminal_param, + const uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]); + +/*! Get stride on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + + @return stride of the frame to be attached. + */ +extern uint32_t ia_css_terminal_param_get_stride( + const ia_css_terminal_param_t *terminal_param); + +/*! Set stride on the frame associated with the terminal. + + @param terminal_param[in] terminal parameter object + @param stride[in] stride + + @return non-zero on error. + */ +extern int ia_css_terminal_param_set_stride( + ia_css_terminal_param_t *terminal_param, + const uint32_t stride); + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h new file mode 100644 index 000000000000..7821f8147a1a --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param.sim.h @@ -0,0 +1,153 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H + +/*! \file */ + +/** @file ia_css_program_group_param.sim.h + * + * Define the methods on the program group parameter object: Simulation only + */ +#include + +#include + +#include + +/* Simulation */ + +/*! Create a program group parameter object from specification + + @param specification[in] specification (index) + @param manifest[in] program group manifest + + @return NULL on error + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_create( + const unsigned int specification, + const ia_css_program_group_manifest_t *manifest); + +/*! Destroy the program group parameter object + + @param program_group_param[in] program group parameter object + + @return NULL + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_destroy( + ia_css_program_group_param_t *param); + +/*! Compute the size of storage required for allocating + * the program group parameter object + + @param program_count[in] Number of programs in the process group + @param terminal_count[in] Number of terminals on the process group + @param fragment_count[in] Number of fragments on the terminals of + the process group + + @return 0 on error + */ +size_t ia_css_sizeof_program_group_param( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count); + +/*! Allocate (the store of) a program group parameter object + + @param program_count[in] Number of programs in the process group + @param terminal_count[in] Number of terminals on the process group + @param fragment_count[in] Number of fragments on the terminals of + the process group + + @return program group parameter pointer, NULL on error + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_alloc( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count); + +/*! Free (the store of) a program group parameter object + + @param program_group_param[in] program group parameter object + + @return NULL + */ +extern ia_css_program_group_param_t *ia_css_program_group_param_free( + ia_css_program_group_param_t *param); + +/*! Print the program group parameter object to file/stream + + @param param[in] program group parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_param_print( + const ia_css_program_group_param_t *param, + void *fid); + +/*! Allocate (the store of) a program parameter object + + @return program parameter pointer, NULL on error + */ +extern ia_css_program_param_t *ia_css_program_param_alloc(void); + +/*! Free (the store of) a program parameter object + + @param param[in] program parameter object + + @return NULL + */ +extern ia_css_program_param_t *ia_css_program_param_free( + ia_css_program_param_t *param); + +/*! Print the program parameter object to file/stream + + @param param[in] program parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_param_print( + const ia_css_program_param_t *param, + void *fid); + +/*! Allocate (the store of) a terminal parameter object + + @return terminal parameter pointer, NULL on error + */ +extern ia_css_terminal_param_t *ia_css_terminal_param_alloc(void); + +/*! Free (the store of) a terminal parameter object + + @param param[in] terminal parameter object + + @return NULL + */ +extern ia_css_terminal_param_t *ia_css_terminal_param_free( + ia_css_terminal_param_t *param); + +/*! Print the terminal parameter object to file/stream + + @param param[in] terminal parameter object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_terminal_param_print( + const ia_css_terminal_param_t *param, + void *fid); + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h new file mode 100644 index 000000000000..34f57584a227 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_program_group_param_types.h @@ -0,0 +1,64 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H + +/*! \file */ + +/** @file ia_css_program_group_param_types.h + * + * Define the parameter objects that are necessary to create the process + * groups i.e. enable parameters and parameters to set-up frame descriptors + */ + +#include +#include /* ia_css_kernel_bitmap_t */ +#include + +#include +/*! make this public so that driver can populate, + * size, bpp, dimensions for all terminals. + * + * Currently one API is provided to get frame_format_type. + * + * frame_format_type is set during ia_css_terminal_param_init(). + * Value for that is const and binary specific. + */ +struct ia_css_terminal_param_s { + uint32_t size; /**< Size of this structure */ + /**< Indicates if this is a generic type or inbuild + * with variable size descriptor + */ + ia_css_frame_format_type_t frame_format_type; + /**< offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION];/**< Logical dimensions */ + /**< Mapping to the index field of the terminal descriptor */ + uint16_t index[IA_CSS_N_DATA_DIMENSION]; + /**< Logical fragment dimension, + * TODO: fragment dimensions can be different per fragment + */ + uint16_t fragment_dimensions[IA_CSS_N_DATA_DIMENSION]; + uint32_t stride;/**< Stride of a frame */ + uint16_t offset;/**< Offset in bytes to first fragment */ + uint8_t bpp; /**< Bits per pixel */ + uint8_t bpe; /**< Bits per element */ +}; + +typedef struct ia_css_program_group_param_s ia_css_program_group_param_t; +typedef struct ia_css_program_param_s ia_css_program_param_t; +typedef struct ia_css_terminal_param_s ia_css_terminal_param_t; + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h new file mode 100644 index 000000000000..f59dfbf165e4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/interface/ia_css_psys_param_trace.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PARAM_TRACE_H +#define __IA_CSS_PSYS_PARAM_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_PARAM_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_PARAM_TRACING_OVERRIDE)) + #define PSYS_PARAM_TRACE_LEVEL_CONFIG PSYS_PARAM_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_PARAM_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_PARAM_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_PARAM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_PARAM_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_PARAM_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_PARAM_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_PARAM_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_PARAM_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_PARAM_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_PARAM_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_PARAM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c new file mode 100644 index 000000000000..067f69a4a01e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param.c @@ -0,0 +1,771 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ia_css_psys_param_trace.h" + +static int +ia_css_terminal_param_init(ia_css_terminal_param_t *terminal_param, + uint32_t offset, + enum ia_css_frame_format_type frame_format_type); + +static int +ia_css_program_param_init(ia_css_program_param_t *program_param, + int32_t offset); + +size_t ia_css_sizeof_program_group_param( + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_sizeof_program_group_param(): enter:\n"); + + verifexit(program_count != 0); + verifexit(terminal_count != 0); + verifexit(fragment_count != 0); + + size += sizeof(ia_css_program_group_param_t); + size += program_count * fragment_count * sizeof(ia_css_program_param_t); + size += terminal_count * sizeof(ia_css_terminal_param_t); +EXIT: + if (0 == program_count || 0 == terminal_count || 0 == fragment_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_sizeof_program_group_param invalid argument\n"); + } + return size; +} + +size_t ia_css_program_group_param_get_size( + const ia_css_program_group_param_t *program_group_param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_size(): enter:\n"); + + if (program_group_param != NULL) { + size = program_group_param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_size invalid argument\n"); + } + return size; +} + +size_t ia_css_program_param_get_size( + const ia_css_program_param_t *param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_get_size(): enter:\n"); + + if (param != NULL) { + size = param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_param_get_size invalid argument\n"); + } + return size; +} + +ia_css_program_param_t *ia_css_program_group_param_get_program_param( + const ia_css_program_group_param_t *param, + const int i) +{ + ia_css_program_param_t *program_param = NULL; + ia_css_program_param_t *program_param_base; + int program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_program_param(): enter:\n"); + + verifexit(param != NULL); + + program_count = + (int)ia_css_program_group_param_get_program_count(param); + + verifexit(i < program_count); + + program_param_base = (ia_css_program_param_t *) + (((char *)param) + param->program_param_offset); + + program_param = &program_param_base[i]; + +EXIT: + if (NULL == param || i >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_program_param invalid argument\n"); + } + return program_param; +} + +size_t ia_css_terminal_param_get_size( + const ia_css_terminal_param_t *param) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_size(): enter:\n"); + + if (param != NULL) { + size = param->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_size invalid argument\n"); + } + + return size; +} + +ia_css_terminal_param_t *ia_css_program_group_param_get_terminal_param( + const ia_css_program_group_param_t *param, + const int i) +{ + ia_css_terminal_param_t *terminal_param = NULL; + ia_css_terminal_param_t *terminal_param_base; + int program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_terminal_param(): enter:\n"); + + verifexit(param != NULL); + + program_count = + (int)ia_css_program_group_param_get_terminal_count(param); + + verifexit(i < program_count); + + terminal_param_base = (ia_css_terminal_param_t *) + (((char *)param) + param->terminal_param_offset); + terminal_param = &terminal_param_base[i]; +EXIT: + if (NULL == param || i >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_terminal_param invalid argument\n"); + } + return terminal_param; +} + +uint8_t ia_css_program_group_param_get_program_count( + const ia_css_program_group_param_t *param) +{ + uint8_t program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_program_count(): enter:\n"); + + if (param != NULL) { + program_count = param->program_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_program_count invalid argument\n"); + } + return program_count; +} + +uint8_t ia_css_program_group_param_get_terminal_count( + const ia_css_program_group_param_t *param) +{ + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_terminal_count(): enter:\n"); + + if (param != NULL) { + terminal_count = param->terminal_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +uint16_t ia_css_program_group_param_get_fragment_count( + const ia_css_program_group_param_t *param) +{ + uint8_t fragment_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_fragment_count(): enter:\n"); + + if (param != NULL) { + fragment_count = (uint8_t)param->fragment_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_fragment_count invalid argument\n"); + } + return fragment_count; +} + +int ia_css_program_group_param_set_protocol_version( + ia_css_program_group_param_t *param, + uint8_t protocol_version) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_set_protocol_version(): enter:\n"); + + if (param != NULL) { + param->protocol_version = protocol_version; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_set_protocol_version failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_program_group_param_get_protocol_version( + const ia_css_program_group_param_t *param) +{ + uint8_t protocol_version = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_protocol_version(): enter:\n"); + + if (param != NULL) { + protocol_version = param->protocol_version; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_protocol_version invalid argument\n"); + } + return protocol_version; +} + +int ia_css_program_group_param_set_kernel_enable_bitmap( + ia_css_program_group_param_t *param, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_set_kernel_enable_bitmap(): enter:\n"); + + if (param != NULL) { + param->kernel_enable_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_set_kernel_enable_bitmap failed (%i)\n", + retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_group_param_get_kernel_enable_bitmap( + const ia_css_program_group_param_t *param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_group_param_get_kernel_enable_bitmap(): enter:\n"); + + if (param != NULL) { + bitmap = param->kernel_enable_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_group_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_param_set_kernel_enable_bitmap( + ia_css_program_param_t *program_param, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_set_kernel_enable_bitmap(): enter:\n"); + + if (program_param != NULL) { + program_param->kernel_enable_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_set_kernel_enable_bitmap failed (%i)\n", + retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_param_get_kernel_enable_bitmap( + const ia_css_program_param_t *program_param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_program_param_get_kernel_enable_bitmap(): enter:\n"); + + verifexit(program_param != NULL); + verifexit(program_param->parent_offset != 0); + + base = (char *)((char *)program_param + program_param->parent_offset); + bitmap = ((ia_css_program_group_param_t *)base)->kernel_enable_bitmap; +EXIT: + if (NULL == program_param || 0 == program_param->parent_offset) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_program_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +ia_css_kernel_bitmap_t ia_css_terminal_param_get_kernel_enable_bitmap( + const ia_css_terminal_param_t *param) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_kernel_enable_bitmap(): enter:\n"); + + verifexit(param != NULL); + verifexit(param->parent_offset != 0); + + base = (char *)((char *)param + param->parent_offset); + bitmap = ((ia_css_program_group_param_t *)base)->kernel_enable_bitmap; +EXIT: + if (NULL == param || 0 == param->parent_offset) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_kernel_enable_bitmap invalid argument\n"); + } + return bitmap; +} + +ia_css_frame_format_type_t ia_css_terminal_param_get_frame_format_type( + const ia_css_terminal_param_t *param) +{ + ia_css_frame_format_type_t ft = IA_CSS_N_FRAME_FORMAT_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_frame_format_type(): enter:\n"); + + verifexit(param != NULL); + + ft = param->frame_format_type; +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_frame_format_type invalid argument\n"); + } + return ft; +} + +int ia_css_terminal_param_set_frame_format_type( + ia_css_terminal_param_t *param, + const ia_css_frame_format_type_t data_format_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_frame_format_type(): enter:\n"); + + if (param != NULL) { + param->frame_format_type = data_format_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_frame_format_type failed (%i)\n", + retval); + } + return retval; +} + +uint8_t ia_css_terminal_param_get_bpp( + const ia_css_terminal_param_t *param) +{ + uint8_t bpp = 0; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_bpp(): enter:\n"); + + verifexit(param != NULL); + + bpp = param->bpp; + +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_bpp invalid argument\n"); + } + return bpp; +} + +int ia_css_terminal_param_set_bpp( + ia_css_terminal_param_t *param, + const uint8_t bpp) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_bpp(): enter:\n"); + + if (param != NULL) { + param->bpp = bpp; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_bpp failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_get_dimensions( + const ia_css_terminal_param_t *param, + uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_dimensions(): enter:\n"); + + if (param != NULL) { + dimensions[IA_CSS_COL_DIMENSION] = + param->dimensions[IA_CSS_COL_DIMENSION]; + dimensions[IA_CSS_ROW_DIMENSION] = + param->dimensions[IA_CSS_ROW_DIMENSION]; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_get_dimensions failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_set_dimensions( + ia_css_terminal_param_t *param, + const uint16_t dimensions[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_set_dimensions(): enter:\n"); + + if (param != NULL) { + param->dimensions[IA_CSS_COL_DIMENSION] = + dimensions[IA_CSS_COL_DIMENSION]; + param->dimensions[IA_CSS_ROW_DIMENSION] = + dimensions[IA_CSS_ROW_DIMENSION]; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_set_dimensions failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_set_stride( + ia_css_terminal_param_t *param, + const uint32_t stride) +{ + int retval = -1; + + verifexit(param != NULL); + param->stride = stride; + retval = 0; + +EXIT: + return retval; +} + +uint32_t ia_css_terminal_param_get_stride( + const ia_css_terminal_param_t *param) +{ + uint32_t stride = 0; + + verifexit(param != NULL); + stride = param->stride; + +EXIT: + return stride; +} + + +static int ia_css_program_param_init( + ia_css_program_param_t *program_param, + int32_t offset) +{ + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_param_t))); + verifexit(program_param != NULL); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_param_init(): enter:\n"); + + program_param->size = sizeof(ia_css_program_param_t); + /* parent is at negative offset from current program.*/ + program_param->parent_offset = -offset; + /*TODO: Kernel_bitmap setting. ?*/ + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_init failed (%i)\n", retval); + } + return retval; +} + +static int +ia_css_terminal_param_init(ia_css_terminal_param_t *terminal_param, + uint32_t offset, + enum ia_css_frame_format_type frame_format_type) +{ + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_TERMINAL_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_terminal_param_t))); + verifexit(terminal_param != NULL); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_terminal_param_init(): enter:\n"); + + terminal_param->size = sizeof(ia_css_terminal_param_t); + /* parent is at negative offset from current program.*/ + terminal_param->parent_offset = -((int32_t)offset); + /*TODO: Kernel_bitmap setting. ?*/ + terminal_param->frame_format_type = frame_format_type; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_init failed (%i)\n", retval); + } + return retval; +} + +ia_css_program_group_param_t * +ia_css_terminal_param_get_parent( + const ia_css_terminal_param_t *param) +{ + ia_css_program_group_param_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, VERBOSE, + "ia_css_terminal_param_get_parent(): enter:\n"); + + verifexit(NULL != param); + + base = (char *)((char *)param + param->parent_offset); + + parent = (ia_css_program_group_param_t *)(base); +EXIT: + if (NULL == param) { + IA_CSS_TRACE_0(PSYSAPI_PARAM, WARNING, + "ia_css_terminal_param_get_parent invalid argument\n"); + } + return parent; +} + +int ia_css_program_group_param_init( + ia_css_program_group_param_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint16_t fragment_count, + const enum ia_css_frame_format_type *frame_format_types) +{ + int i = 0; + char *param_base; + uint32_t offset; + int retval = -1; + + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_GROUP_PARAM_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_group_param_t))); + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_group_param_init(): enter:\n"); + + assert(blob != 0); + + verifexit(blob != NULL); + verifexit(frame_format_types != NULL); + + blob->program_count = program_count; + blob->fragment_count = fragment_count; + blob->terminal_count = terminal_count; + blob->program_param_offset = sizeof(ia_css_program_group_param_t); + blob->terminal_param_offset = blob->program_param_offset + + sizeof(ia_css_program_param_t) * program_count; + + param_base = (char *)((char *)blob + blob->program_param_offset); + offset = blob->program_param_offset; + + for (i = 0; i < program_count; i++) { + ia_css_program_param_init( + (ia_css_program_param_t *)param_base, offset); + offset += sizeof(ia_css_program_param_t); + param_base += sizeof(ia_css_program_param_t); + } + + param_base = (char *)((char *)blob + blob->terminal_param_offset); + offset = blob->terminal_param_offset; + + for (i = 0; i < terminal_count; i++) { + ia_css_terminal_param_init( + (ia_css_terminal_param_t *)param_base, + offset, + frame_format_types[i]); + + offset += sizeof(ia_css_terminal_param_t); + param_base += sizeof(ia_css_terminal_param_t); + } + + /* + * For now, set legacy flow by default. This can be removed as soon + * as all hosts/drivers explicitly set the protocol version. + */ + blob->protocol_version = IA_CSS_PROCESS_GROUP_PROTOCOL_LEGACY; + + blob->size = (uint32_t)ia_css_sizeof_program_group_param(program_count, + terminal_count, + fragment_count); + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_init failed (%i)\n", retval); + } + return retval; +} + +int ia_css_program_group_param_print( + const ia_css_program_group_param_t *param, + void *fid) +{ + int retval = -1; + int i; + uint8_t program_count, terminal_count; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_group_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "sizeof(program_group_param) = %d\n", + (int)ia_css_program_group_param_get_size(param)); + + program_count = ia_css_program_group_param_get_program_count(param); + terminal_count = ia_css_program_group_param_get_terminal_count(param); + + bitmap = ia_css_program_group_param_get_kernel_enable_bitmap(param); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "%d program params\n", (int)program_count); + for (i = 0; i < (int)program_count; i++) { + ia_css_program_param_t *program_param = + ia_css_program_group_param_get_program_param(param, i); + + retval = ia_css_program_param_print(program_param, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, "%d terminal params\n", + (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_param_t *terminal_param = + ia_css_program_group_param_get_terminal_param(param, i); + + retval = ia_css_terminal_param_print(terminal_param, fid); + verifjmpexit(retval == 0); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_group_param_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_terminal_param_print( + const ia_css_terminal_param_t *param, + void *fid) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_terminal_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "sizeof(terminal_param) = %d\n", + (int)ia_css_terminal_param_get_size(param)); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, + "\tframe_format_type = %d\n", param->frame_format_type); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_terminal_param_print failed (%i)\n", retval); + } + return retval; +} + +int ia_css_program_param_print( + const ia_css_program_param_t *param, + void *fid) +{ + int retval = -1; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_PARAM, INFO, + "ia_css_program_param_print(): enter:\n"); + + verifexit(param != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_PARAM, INFO, "sizeof(program_param) = %d\n", + (int)ia_css_program_param_get_size(param)); + + bitmap = ia_css_program_param_get_kernel_enable_bitmap(param); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_PARAM, ERROR, + "ia_css_program_param_print failed (%i)\n", retval); + } + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h new file mode 100644 index 000000000000..6672737e51a1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/param/src/ia_css_program_group_param_private.h @@ -0,0 +1,80 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H +#define __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#include + +#define N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT 7 +#define SIZE_OF_PROGRAM_GROUP_PARAM_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + + IA_CSS_UINT16_T_BITS \ + + (3 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT * IA_CSS_UINT8_T_BITS)) + +/* tentative; co-design with ISP algorithm */ +struct ia_css_program_group_param_s { + /* The enable bits for each individual kernel */ + ia_css_kernel_bitmap_t kernel_enable_bitmap; + /* Size of this structure */ + uint32_t size; + uint32_t program_param_offset; + uint32_t terminal_param_offset; + /* Number of (explicit) fragments to use in a frame */ + uint16_t fragment_count; + /* Number of active programs */ + uint8_t program_count; + /* Number of active terminals */ + uint8_t terminal_count; + /* Program group protocol version */ + uint8_t protocol_version; + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_GROUP_PARAM_STRUCT]; +}; + +#define SIZE_OF_PROGRAM_PARAM_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + IA_CSS_UINT32_T_BITS \ + + IA_CSS_INT32_T_BITS) + +/* private */ +struct ia_css_program_param_s { + /* What to use this one for ? */ + ia_css_kernel_bitmap_t kernel_enable_bitmap; + /* Size of this structure */ + uint32_t size; + /* offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; +}; + +#define SIZE_OF_TERMINAL_PARAM_STRUCT_IN_BITS \ + (IA_CSS_UINT32_T_BITS \ + + IA_CSS_FRAME_FORMAT_TYPE_BITS \ + + IA_CSS_INT32_T_BITS \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION) \ + + IA_CSS_INT32_T_BITS \ + + IA_CSS_UINT16_T_BITS \ + + IA_CSS_UINT8_T_BITS \ + + (IA_CSS_UINT8_T_BITS * 1)) + +#endif /* __IA_CSS_PROGRAM_GROUP_PARAM_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.c new file mode 100644 index 000000000000..7543b93f279b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.c @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_server_manifest.h" + +/** + * Manifest of resources in use by PSYS itself + */ + +const vied_nci_resource_spec_t psys_server_manifest = { + /* internal memory */ + { /* resource id size offset*/ + {VIED_NCI_GMEM_TYPE_ID, 0, 0}, + {VIED_NCI_DMEM_TYPE_ID, VIED_NCI_DMEM0_MAX_SIZE, 0}, + {VIED_NCI_VMEM_TYPE_ID, 0, 0}, + {VIED_NCI_BAMEM_TYPE_ID, 0, 0}, + {VIED_NCI_PMEM_TYPE_ID, 0, 0} + }, + /* external memory */ + { /* resource id size offset*/ + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0}, + {VIED_NCI_N_MEM_ID, 0, 0} + }, + /* device channel */ + { /* resource id size offset*/ + {VIED_NCI_DEV_CHN_DMA_EXT0_ID, + PSYS_SERVER_DMA_CHANNEL_SIZE, + PSYS_SERVER_DMA_CHANNEL_OFFSET}, + {VIED_NCI_DEV_CHN_GDC_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_EXT1_READ_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_INTERNAL_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_IPFD_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_ISA_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_FW_ID, 0, 0}, + {VIED_NCI_DEV_CHN_DMA_CMPRS_ID, 0, 0} + } +}; diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.h new file mode 100644 index 000000000000..b4c7fbc32d5b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psys_server_manifest/cnlB0/ia_css_psys_server_manifest.h @@ -0,0 +1,29 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SERVER_MANIFEST_H +#define __IA_CSS_PSYS_SERVER_MANIFEST_H + +#include "vied_nci_psys_resource_model.h" + +/** + * Manifest of resources in use by PSYS itself + */ + +#define PSYS_SERVER_DMA_CHANNEL_SIZE 2 +#define PSYS_SERVER_DMA_CHANNEL_OFFSET 28 + +extern const vied_nci_resource_spec_t psys_server_manifest; + +#endif /* __IA_CSS_PSYS_SERVER_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psysapi.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psysapi.mk new file mode 100644 index 000000000000..e1977cbe2ca2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/psysapi.mk @@ -0,0 +1,122 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is PSYSAPI +# +ifdef _H_PSYSAPI_MK +$(error ERROR: psysapi.mk included multiple times, please check makefile) +else +_H_PSYSAPI_MK=1 +endif + +include $(MODULES_DIR)/config/psys/subsystem_$(IPU_SYSVER).mk + +PSYSAPI_DIR = $${MODULES_DIR}/psysapi + +PSYSAPI_PROCESS_HOST_FILES = $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process_group.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_buffer_set.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_terminal.c +PSYSAPI_PROCESS_HOST_FILES += $(PSYSAPI_DIR)/param/src/ia_css_program_group_param.c + +# Use PSYS_MANIFEST_HOST_FILES when only accessing manifest functions +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_group_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_terminal_manifest.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/sim/src/vied_nci_psys_system.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/data/src/ia_css_program_group_data.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c +PSYSAPI_MANIFEST_HOST_FILES += $(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION)/ia_css_psys_server_manifest.c + +# Use only kernel bitmap functionality from PSYS API +PSYSAPI_KERNEL_BITMAP_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c +PSYSAPI_KERNEL_BITMAP_CPPFLAGS += -I$(PSYSAPI_DIR)/kernel/interface +PSYSAPI_KERNEL_BITMAP_CPPFLAGS += -I$(PSYSAPI_DIR)/interface + +# Use PSYSAPI_HOST_FILES when program and process group are both needed +PSYSAPI_HOST_FILES = $(PSYSAPI_PROCESS_HOST_FILES) $(PSYSAPI_MANIFEST_HOST_FILES) + +# Use PSYSAPI_PROCESS_GROUP_HOST_FILES when program and process group are both needed but there is no +# implementation (yet) of the user customization functions defined in ia_css_psys_process_group_cmd_impl.h. +# Dummy implementations are provided in $(PSYSAPI_DIR)/sim/src/ia_css_psys_process_group_cmd_impl.c +PSYSAPI_PROCESS_GROUP_HOST_FILES = $(PSYSAPI_HOST_FILES) +PSYSAPI_PROCESS_GROUP_HOST_FILES += $(PSYSAPI_DIR)/sim/src/ia_css_psys_process_group_cmd_impl.c + +# for now disabled, implementation for now provided by psys api impl +#PSYSAPI_HOST_FILES += $(PSYSAPI_DIR)/device/src/ia_css_psys_device.c + +PSYSAPI_HOST_CPPFLAGS = -I$(PSYSAPI_DIR)/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/device/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/device/interface/$(IPU_SYSVER) +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/dynamic/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/dynamic/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/data/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/data/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/static/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/static/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/kernel/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/param/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/param/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/interface +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/src +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_HOST_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) + +PSYSAPI_FW_CPPFLAGS = $(PSYSAPI_HOST_CPPFLAGS) +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/static/interface +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/static/src +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_FW_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/sim/interface +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION) +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/private +PSYSAPI_SYSTEM_GLOBAL_CPPFLAGS += -I$(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION) + +# Defining the trace level for the PSYSAPI +PSYSAPI_HOST_CPPFLAGS += -DPSYSAPI_TRACE_CONFIG=PSYSAPI_TRACE_LOG_LEVEL_NORMAL +# Enable/Disable 'late binding' support and it's additional queues +PSYSAPI_HOST_CPPFLAGS += -DHAS_LATE_BINDING_SUPPORT=$(PSYS_HAS_LATE_BINDING_SUPPORT) + +#Example: how to switch to a different log level for a sub-module +#PSYSAPI_HOST_CPPFLAGS += -DPSYSAPI_DYNAMIC_TRACING_OVERRIDE=PSYSAPI_TRACE_LOG_LEVEL_DEBUG + +# enable host side implementation +# TODO: better name for the flag to enable the impl... +PSYSAPI_HOST_CPPFLAGS += -D__X86_SIM__ + +# Files for Firmware +PSYSAPI_FW_FILES = $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_process_group.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_terminal.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/dynamic/src/ia_css_psys_buffer_set.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/param/src/ia_css_program_group_param.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/data/src/ia_css_program_group_data.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/sim/src/vied_nci_psys_system.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/sim/src/ia_css_psys_sim_data.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_group_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_program_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/static/src/ia_css_psys_terminal_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/psys_server_manifest/$(PSYS_SERVER_MANIFEST_VERSION)/ia_css_psys_server_manifest.c +PSYSAPI_FW_FILES += $(PSYSAPI_DIR)/kernel/src/ia_css_kernel_bitmap.c + +# resource model +PSYSAPI_RESOURCE_MODEL_FILES = $(PSYSAPI_DIR)/resource_model/$(PSYS_RESOURCE_MODEL_VERSION)/vied_nci_psys_resource_model.c + +ifeq ($(PSYS_HAS_DUAL_CMD_CTX_SUPPORT), 1) +PSYSAPI_HOST_CPPFLAGS += -DHAS_DUAL_CMD_CTX_SUPPORT=$(PSYS_HAS_DUAL_CMD_CTX_SUPPORT) +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.c new file mode 100644 index 000000000000..20bfb729e641 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.c @@ -0,0 +1,323 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "vied_nci_psys_resource_model.h" + +/* + * Cell types by cell IDs + */ +const vied_nci_cell_type_ID_t vied_nci_cell_type[VIED_NCI_N_CELL_ID] = { + VIED_NCI_SP_CTRL_TYPE_ID, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_ACC_ISA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_OSA_TYPE_ID, + VIED_NCI_GDC_TYPE_ID, + VIED_NCI_GDC_TYPE_ID +}; + +/* + * Memory types by memory IDs + */ +const vied_nci_mem_type_ID_t vied_nci_mem_type[VIED_NCI_N_MEM_ID] = { + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_GMEM_TYPE_ID,/* VMEM4 is GMEM according to vied_nci_cell_mem */ + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID +}; + +/* + * Cell mem count by cell type ID + */ +const uint16_t vied_nci_N_cell_mem[VIED_NCI_N_CELL_TYPE_ID] = { + VIED_NCI_N_SP_CTRL_MEM, + VIED_NCI_N_SP_SERVER_MEM, + VIED_NCI_N_VP_MEM, + VIED_NCI_N_ACC_PSA_MEM, + VIED_NCI_N_ACC_ISA_MEM, + VIED_NCI_N_ACC_OSA_MEM +}; + +/* + * Cell mem type by cell type ID and memory index + */ +const vied_nci_mem_type_ID_t +vied_nci_cell_mem_type[VIED_NCI_N_CELL_TYPE_ID][VIED_NCI_N_MEM_TYPE_ID] = { + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_GMEM_TYPE_ID, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + }, + { + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID + } +}; + +/* + * Ext mem ID by memory index + */ +const vied_nci_mem_ID_t +vied_nci_ext_mem[VIED_NCI_N_MEM_TYPE_ID] = { + VIED_NCI_VMEM4_ID, /* VIED_NCI_GMEM_TYPE_ID */ + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID +}; + +/* + * Cell mem ID by cell ID and memory index + */ +const vied_nci_mem_ID_t +vied_nci_cell_mem[VIED_NCI_N_CELL_ID][VIED_NCI_N_MEM_TYPE_ID] = { + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM0_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM1_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_DMEM2_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM4_ID, + VIED_NCI_VMEM0_ID, + VIED_NCI_BAMEM0_ID, + VIED_NCI_PMEM0_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM5_ID, + VIED_NCI_VMEM1_ID, + VIED_NCI_BAMEM1_ID, + VIED_NCI_PMEM1_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM6_ID, + VIED_NCI_VMEM2_ID, + VIED_NCI_BAMEM2_ID, + VIED_NCI_PMEM2_ID + }, + { + VIED_NCI_VMEM4_ID, + VIED_NCI_DMEM7_ID, + VIED_NCI_VMEM3_ID, + VIED_NCI_BAMEM3_ID, + VIED_NCI_PMEM3_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + }, + { + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID, + VIED_NCI_N_MEM_ID + } +}; + +/* + * Memory sizes by mem ID + */ +const uint16_t vied_nci_mem_size[VIED_NCI_N_MEM_ID] = { + VIED_NCI_VMEM0_MAX_SIZE, + VIED_NCI_VMEM1_MAX_SIZE, + VIED_NCI_VMEM2_MAX_SIZE, + VIED_NCI_VMEM3_MAX_SIZE, + VIED_NCI_VMEM4_MAX_SIZE, + VIED_NCI_BAMEM0_MAX_SIZE, + VIED_NCI_BAMEM1_MAX_SIZE, + VIED_NCI_BAMEM2_MAX_SIZE, + VIED_NCI_BAMEM3_MAX_SIZE, + VIED_NCI_DMEM0_MAX_SIZE, + VIED_NCI_DMEM1_MAX_SIZE, + VIED_NCI_DMEM2_MAX_SIZE, + VIED_NCI_DMEM3_MAX_SIZE, + VIED_NCI_DMEM4_MAX_SIZE, + VIED_NCI_DMEM5_MAX_SIZE, + VIED_NCI_DMEM6_MAX_SIZE, + VIED_NCI_DMEM7_MAX_SIZE, + VIED_NCI_PMEM0_MAX_SIZE, + VIED_NCI_PMEM1_MAX_SIZE, + VIED_NCI_PMEM2_MAX_SIZE, + VIED_NCI_PMEM3_MAX_SIZE +}; + +/* + * Memory word sizes by mem type ID + */ +const uint16_t vied_nci_mem_word_size[VIED_NCI_N_DATA_MEM_TYPE_ID] = { + VIED_NCI_GMEM_WORD_SIZE, + VIED_NCI_DMEM_WORD_SIZE, + VIED_NCI_VMEM_WORD_SIZE, + VIED_NCI_BAMEM_WORD_SIZE +}; + +/* + * Number of channels by device ID + */ +const uint16_t vied_nci_dev_chn_size[VIED_NCI_N_DEV_CHN_ID] = { + VIED_NCI_DEV_CHN_DMA_EXT0_MAX_SIZE, + VIED_NCI_DEV_CHN_GDC_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_EXT1_READ_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_INTERNAL_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_IPFD_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_ISA_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_FW_MAX_SIZE, + VIED_NCI_DEV_CHN_DMA_CMPRS_MAX_SIZE +}; diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.h new file mode 100644 index 000000000000..6249d8af3eff --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/resource_model/cnlB0/vied_nci_psys_resource_model.h @@ -0,0 +1,300 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __VIED_NCI_PSYS_RESOURCE_MODEL_H +#define __VIED_NCI_PSYS_RESOURCE_MODEL_H + +#include "type_support.h" +#include "storage_class.h" + +#define HAS_DFM 0 +#define NON_RELOC_RESOURCE_SUPPORT 0 +#define IA_CSS_KERNEL_BITMAP_DO_NOT_USE_ELEMS + +/* Defines for the routing bitmap in the program group manifest. + */ +#define VIED_NCI_RBM_MAX_MUX_COUNT 0 +#define VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT 0 +#define VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT 0 +#define N_PADDING_UINT8_IN_RBM_MANIFEST 2 + +/* The amount of padding bytes needed to make + * ia_css_process_s structure 64 bit aligned + */ +#define N_PADDING_UINT8_IN_PROCESS_STRUCT 2 +#define N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST 0 + +/** + * Resource model for CNL B0 + */ + +/* + * Cell IDs + */ +typedef enum { + VIED_NCI_SP0_ID = 0, + VIED_NCI_SP1_ID, + VIED_NCI_SP2_ID, + VIED_NCI_VP0_ID, + VIED_NCI_VP1_ID, + VIED_NCI_VP2_ID, + VIED_NCI_VP3_ID, + VIED_NCI_ACC0_ID, + VIED_NCI_ACC1_ID, + VIED_NCI_ACC2_ID, + VIED_NCI_ACC3_ID, + VIED_NCI_ACC4_ID, + VIED_NCI_ACC5_ID, + VIED_NCI_ACC6_ID, + VIED_NCI_ACC7_ID, + VIED_NCI_GDC0_ID, + VIED_NCI_GDC1_ID, + VIED_NCI_N_CELL_ID +} vied_nci_cell_ID_t; + +/* + * Barrier bits (to model process group dependencies) + */ +typedef enum { + VIED_NCI_BARRIER0_ID, + VIED_NCI_BARRIER1_ID, + VIED_NCI_BARRIER2_ID, + VIED_NCI_BARRIER3_ID, + VIED_NCI_BARRIER4_ID, + VIED_NCI_BARRIER5_ID, + VIED_NCI_BARRIER6_ID, + VIED_NCI_BARRIER7_ID, + VIED_NCI_N_BARRIER_ID +} vied_nci_barrier_ID_t; + +/* + * Cell types + */ +typedef enum { + VIED_NCI_SP_CTRL_TYPE_ID = 0, + VIED_NCI_SP_SERVER_TYPE_ID, + VIED_NCI_VP_TYPE_ID, + VIED_NCI_ACC_PSA_TYPE_ID, + VIED_NCI_ACC_ISA_TYPE_ID, + VIED_NCI_ACC_OSA_TYPE_ID, + VIED_NCI_GDC_TYPE_ID, + VIED_NCI_N_CELL_TYPE_ID +} vied_nci_cell_type_ID_t; + +/* + * Memory IDs + */ +typedef enum { + VIED_NCI_VMEM0_ID = 0, + VIED_NCI_VMEM1_ID, + VIED_NCI_VMEM2_ID, + VIED_NCI_VMEM3_ID, + VIED_NCI_VMEM4_ID, + VIED_NCI_BAMEM0_ID, + VIED_NCI_BAMEM1_ID, + VIED_NCI_BAMEM2_ID, + VIED_NCI_BAMEM3_ID, + VIED_NCI_DMEM0_ID, + VIED_NCI_DMEM1_ID, + VIED_NCI_DMEM2_ID, + VIED_NCI_DMEM3_ID, + VIED_NCI_DMEM4_ID, + VIED_NCI_DMEM5_ID, + VIED_NCI_DMEM6_ID, + VIED_NCI_DMEM7_ID, + VIED_NCI_PMEM0_ID, + VIED_NCI_PMEM1_ID, + VIED_NCI_PMEM2_ID, + VIED_NCI_PMEM3_ID, + VIED_NCI_N_MEM_ID +} vied_nci_mem_ID_t; + +/* + * Memory types + */ +typedef enum { + VIED_NCI_GMEM_TYPE_ID = 0, + VIED_NCI_DMEM_TYPE_ID, + VIED_NCI_VMEM_TYPE_ID, + VIED_NCI_BAMEM_TYPE_ID, + VIED_NCI_PMEM_TYPE_ID, + VIED_NCI_N_MEM_TYPE_ID +} vied_nci_mem_type_ID_t; + +/* Excluding PMEM */ +#define VIED_NCI_N_DATA_MEM_TYPE_ID (VIED_NCI_N_MEM_TYPE_ID - 1) + +#define VIED_NCI_N_SP_CTRL_MEM 2 +#define VIED_NCI_N_SP_SERVER_MEM 2 +#define VIED_NCI_N_VP_MEM 4 +#define VIED_NCI_N_ACC_PSA_MEM 0 +#define VIED_NCI_N_ACC_ISA_MEM 0 +#define VIED_NCI_N_ACC_OSA_MEM 0 + +#define VIED_NCI_N_VP_CELL 4 +#define VIED_NCI_N_ACC_CELL 8 + +/* + * Device IDs + */ +typedef enum { + VIED_NCI_DEV_CHN_DMA_EXT0_ID = 0, + VIED_NCI_DEV_CHN_GDC_ID, + VIED_NCI_DEV_CHN_DMA_EXT1_READ_ID, + VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_ID, + VIED_NCI_DEV_CHN_DMA_INTERNAL_ID, + VIED_NCI_DEV_CHN_DMA_IPFD_ID, + VIED_NCI_DEV_CHN_DMA_ISA_ID, + VIED_NCI_DEV_CHN_DMA_FW_ID, + VIED_NCI_DEV_CHN_DMA_CMPRS_ID, + VIED_NCI_N_DEV_CHN_ID +} vied_nci_dev_chn_ID_t; + +typedef enum { + DFM_IS_NOT_AVAILABLE +} vied_nci_dev_dfm_id_t; + +#define VIED_NCI_N_DEV_DFM_ID 0 +/* + * Memory size (previously in vied_nci_psys_system.c) + * VMEM: in words, 64 Byte per word. + * BAMEM: in words, 64 Byte per word + * DMEM: in words, 4 Byte per word. + * PMEM: in words, 64 Byte per word. + */ +#define VIED_NCI_GMEM_WORD_SIZE 64 +#define VIED_NCI_DMEM_WORD_SIZE 4 +#define VIED_NCI_VMEM_WORD_SIZE 64 +#define VIED_NCI_BAMEM_WORD_SIZE 64 + +#define VIED_NCI_VMEM0_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM1_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM2_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM3_MAX_SIZE (0x0800) +#define VIED_NCI_VMEM4_MAX_SIZE (0x0800) +#define VIED_NCI_BAMEM0_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM1_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM2_MAX_SIZE (0x0400) +#define VIED_NCI_BAMEM3_MAX_SIZE (0x0400) +#define VIED_NCI_DMEM0_MAX_SIZE (0x4000) +#define VIED_NCI_DMEM1_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM2_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM3_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM4_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM5_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM6_MAX_SIZE (0x1000) +#define VIED_NCI_DMEM7_MAX_SIZE (0x1000) +#define VIED_NCI_PMEM0_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM1_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM2_MAX_SIZE (0x0500) +#define VIED_NCI_PMEM3_MAX_SIZE (0x0500) + +/* + * Number of channels per device + */ +#define VIED_NCI_DEV_CHN_DMA_EXT0_MAX_SIZE (30) +#define VIED_NCI_DEV_CHN_GDC_MAX_SIZE (4) +#define VIED_NCI_DEV_CHN_DMA_EXT1_READ_MAX_SIZE (30) +#define VIED_NCI_DEV_CHN_DMA_EXT1_WRITE_MAX_SIZE (20) +#define VIED_NCI_DEV_CHN_DMA_INTERNAL_MAX_SIZE (2) +#define VIED_NCI_DEV_CHN_DMA_IPFD_MAX_SIZE (5) +#define VIED_NCI_DEV_CHN_DMA_ISA_MAX_SIZE (2) +#define VIED_NCI_DEV_CHN_DMA_FW_MAX_SIZE (1) +#define VIED_NCI_DEV_CHN_DMA_CMPRS_MAX_SIZE (6) + +/* + * Storage of the resource and resource type enumerators + */ +#define VIED_NCI_RESOURCE_ID_BITS 8 +typedef uint8_t vied_nci_resource_id_t; + +#define VIED_NCI_RESOURCE_SIZE_BITS 16 +typedef uint16_t vied_nci_resource_size_t; + +#define VIED_NCI_RESOURCE_BITMAP_BITS 32 +typedef uint32_t vied_nci_resource_bitmap_t; + +#define IA_CSS_PROCESS_INVALID_DEPENDENCY ((vied_nci_resource_id_t)(-1)) +#define IA_CSS_PROCESS_INVALID_OFFSET ((vied_nci_resource_size_t)(-1)) +#define IA_CSS_PROCESS_MAX_CELLS 1 + +/* + * Resource specifications + * Note that the FAS uses the terminology local/remote memory. In the PSYS API, + * these are called internal/external memory. + */ + +/* resource spec for internal (local) memory */ +struct vied_nci_resource_spec_int_mem_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_int_mem_s + vied_nci_resource_spec_int_mem_t; + +/* resource spec for external (remote) memory */ +struct vied_nci_resource_spec_ext_mem_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_ext_mem_s + vied_nci_resource_spec_ext_mem_t; + +/* resource spec for device channel */ +struct vied_nci_resource_spec_dev_chn_s { + vied_nci_resource_id_t type_id; + vied_nci_resource_size_t size; + vied_nci_resource_size_t offset; +}; + +typedef struct vied_nci_resource_spec_dev_chn_s + vied_nci_resource_spec_dev_chn_t; + +/* resource spec for all contiguous resources */ +struct vied_nci_resource_spec_s { + vied_nci_resource_spec_int_mem_t int_mem[VIED_NCI_N_MEM_TYPE_ID]; + vied_nci_resource_spec_ext_mem_t ext_mem[VIED_NCI_N_DATA_MEM_TYPE_ID]; + vied_nci_resource_spec_dev_chn_t dev_chn[VIED_NCI_N_DEV_CHN_ID]; +}; + +typedef struct vied_nci_resource_spec_s vied_nci_resource_spec_t; + +#ifndef PIPE_GENERATION + +extern const vied_nci_cell_type_ID_t vied_nci_cell_type[VIED_NCI_N_CELL_ID]; +extern const vied_nci_mem_type_ID_t vied_nci_mem_type[VIED_NCI_N_MEM_ID]; +extern const uint16_t vied_nci_N_cell_mem[VIED_NCI_N_CELL_TYPE_ID]; +extern const vied_nci_mem_type_ID_t + vied_nci_cell_mem_type[VIED_NCI_N_CELL_TYPE_ID][VIED_NCI_N_MEM_TYPE_ID]; +extern const vied_nci_mem_ID_t + vied_nci_ext_mem[VIED_NCI_N_MEM_TYPE_ID]; +extern const vied_nci_mem_ID_t + vied_nci_cell_mem[VIED_NCI_N_CELL_ID][VIED_NCI_N_MEM_TYPE_ID]; +extern const uint16_t vied_nci_mem_size[VIED_NCI_N_MEM_ID]; +extern const uint16_t vied_nci_mem_word_size[VIED_NCI_N_DATA_MEM_TYPE_ID]; +extern const uint16_t vied_nci_dev_chn_size[VIED_NCI_N_DEV_CHN_ID]; + +STORAGE_CLASS_INLINE +uint32_t vied_nci_mem_is_ext_type(const vied_nci_mem_type_ID_t mem_type_id) +{ + return((mem_type_id == VIED_NCI_GMEM_TYPE_ID)); +} + +#endif /* PIPE_GENERATION */ + +#endif /* __VIED_NCI_PSYS_RESOURCE_MODEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h new file mode 100644 index 000000000000..5b053a27686b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_data.h @@ -0,0 +1,50 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_DATA_H +#define __IA_CSS_PSYS_SIM_DATA_H + +/*! Set the seed if the random number generator + + @param seed[in] Random number generator seed + */ +extern void ia_css_psys_ran_set_seed(const unsigned int seed); + +/*! Generate a random number of a specified bit depth + + @param bit_depth[in] The number of bits of the random output + + @return out, weight(out) <= bit_depth, 0 on error + */ +extern unsigned int ia_css_psys_ran_var(const unsigned int bit_depth); + +/*! Generate a random number of a specified range + + @param range[in] The range of the random output + + @return 0 <= out < range, 0 on error + */ +extern unsigned int ia_css_psys_ran_val(const unsigned int range); + +/*! Generate a random number in a specified interval + + @param lo[in] The lower bound of the random output range + @param hi[in] The higher bound of the random output range + + @return lo <= out < hi, 0 on error + */ +extern unsigned int ia_css_psys_ran_interval(const unsigned int lo, + const unsigned int hi); + +#endif /* __IA_CSS_PSYS_SIM_DATA_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h new file mode 100644 index 000000000000..61095257ec55 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_STORAGE_CLASS_H +#define __IA_CSS_PSYS_SIM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_SIM_INLINE__ +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_SIM_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_SIM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h new file mode 100644 index 000000000000..423ff1980270 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/ia_css_psys_sim_trace.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_SIM_TRACE_H +#define __IA_CSS_PSYS_SIM_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_SIM_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_SIM_TRACING_OVERRIDE)) + #define PSYS_SIM_TRACE_LEVEL_CONFIG PSYS_SIM_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_SIM_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_SIM_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_SIM_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_SIM_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_SIM_TRACE_METHOD PSYSAPI_TRACE_METHOD + #define PSYSAPI_SIM_TRACE_LEVEL_ASSERT PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_SIM_TRACE_LEVEL_ERROR PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_SIM_TRACE_LEVEL_WARNING PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_SIM_TRACE_LEVEL_INFO PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_SIM_TRACE_LEVEL_DEBUG PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_SIM_TRACE_LEVEL_VERBOSE PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_SIM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h new file mode 100644 index 000000000000..529bea763cc2 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/interface/vied_nci_psys_system_global.h @@ -0,0 +1,180 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __VIED_NCI_PSYS_SYSTEM_GLOBAL_H +#define __VIED_NCI_PSYS_SYSTEM_GLOBAL_H + +#include +#include "ia_css_base_types.h" +#include "ia_css_psys_sim_storage_class.h" +#include "vied_nci_psys_resource_model.h" + +/* + * Key system types + */ +/* Subsystem internal physical address */ +#define VIED_ADDRESS_BITS 32 + +/* typedef uint32_t vied_address_t; */ + +/* Subsystem internal virtual address */ + +/* Subsystem internal data bus */ +#define VIED_DATA_BITS 32 +typedef uint32_t vied_data_t; + +#define VIED_NULL ((vied_vaddress_t)0) + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bit_mask( + const unsigned index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_empty( + const vied_nci_resource_bitmap_t bitmap); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bit_set_in_bitmap( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +int vied_nci_bitmap_compute_weight( + const vied_nci_resource_bitmap_t bitmap); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_union( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_intersection( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_xor( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitfield_mask( + const unsigned int position, + const unsigned int size); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bitmap_set_bitfield( +const vied_nci_resource_bitmap_t bitmap, +const unsigned int index, +const unsigned int size); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_bit_mask_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const unsigned index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_cell_bit_mask( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_resource_bitmap_t vied_nci_barrier_bit_mask( + const vied_nci_barrier_ID_t barrier_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_cell_type_ID_t vied_nci_cell_get_type( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_mem_get_type( + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_mem_get_size( + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_dev_chn_get_size( + const vied_nci_dev_chn_ID_t dev_chn_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_cell_of_type( + const vied_nci_cell_ID_t cell_id, + const vied_nci_cell_type_ID_t cell_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_mem_of_type( + const vied_nci_mem_ID_t mem_id, + const vied_nci_mem_type_ID_t mem_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_is_cell_mem_of_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index, + const vied_nci_mem_type_ID_t mem_type_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +bool vied_nci_has_cell_mem_of_id( + const vied_nci_cell_ID_t cell_id, + const vied_nci_mem_ID_t mem_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +uint16_t vied_nci_cell_get_mem_count( + const vied_nci_cell_ID_t cell_id); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_cell_get_mem_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_ID_t vied_nci_cell_get_mem( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index); + +IA_CSS_PSYS_SIM_STORAGE_CLASS_H +vied_nci_mem_type_ID_t vied_nci_cell_type_get_mem_type( + const vied_nci_cell_type_ID_t cell_type_id, + const uint16_t mem_index); + +#ifdef __IA_CSS_PSYS_SIM_INLINE__ +#include "psys_system_global_impl.h" +#endif /* __IA_CSS_PSYS_SIM_INLINE__ */ + +#endif /* __VIED_NCI_PSYS_SYSTEM_GLOBAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c new file mode 100644 index 000000000000..6dccac823871 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/ia_css_psys_sim_data.c @@ -0,0 +1,91 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include + +#include "ia_css_psys_sim_trace.h" + +static unsigned int ia_css_psys_ran_seed; + +void ia_css_psys_ran_set_seed(const unsigned int seed) +{ + ia_css_psys_ran_seed = seed; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_psys_ran_set_seed(): enter:\n"); + +} + +static unsigned int ia_css_psys_ran_int (void) +{ + ia_css_psys_ran_seed = 1664525UL * ia_css_psys_ran_seed + 1013904223UL; + return ia_css_psys_ran_seed; +} + +unsigned int ia_css_psys_ran_var(const unsigned int bit_depth) +{ + unsigned int out; + unsigned int tmp; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "ia_css_psys_ran_var(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if (bit_depth > 32) + out = tmp; + else if (bit_depth == 0) + out = 0; + else + out = (unsigned short)(tmp >> (32 - bit_depth)); + + return out; +} + +unsigned int ia_css_psys_ran_val(const unsigned int range) +{ + unsigned int out; + unsigned int tmp; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "ia_css_psys_ran_val(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if (range > 1) + out = tmp % range; + else + out = 0; + + return out; +} + +unsigned int ia_css_psys_ran_interval(const unsigned int lo, + const unsigned int hi) +{ + unsigned int out; + unsigned int tmp; + unsigned int range = hi - lo; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_psys_ran_interval(): enter:\n"); + + tmp = ia_css_psys_ran_int(); + + if ((range > 1) && (lo < hi)) + out = lo + (tmp % range); + else + out = 0; + + return out; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h new file mode 100644 index 000000000000..ff51175548ec --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/psys_system_global_impl.h @@ -0,0 +1,485 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PSYS_SYSTEM_GLOBAL_IMPL_H +#define __PSYS_SYSTEM_GLOBAL_IMPL_H + +#include + +#include "ia_css_psys_sim_trace.h" +#include + +/* Use vied_bits instead, however for test purposes we uses explicit type + * checking + */ +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bit_mask( + const unsigned int index) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bit_mask(): enter:\n"); + + if (index < VIED_NCI_RESOURCE_BITMAP_BITS) + bit_mask = (vied_nci_resource_bitmap_t)1 << index; + + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bitmap_set(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return bitmap | bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_clear(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return bitmap & (~bit_mask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitfield_mask( + const unsigned int position, + const unsigned int size) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + vied_nci_resource_bitmap_t ones = (vied_nci_resource_bitmap_t)-1; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitfield_mask(): enter:\n"); + + if (position < VIED_NCI_RESOURCE_BITMAP_BITS) + bit_mask = (ones >> (sizeof(vied_nci_resource_bitmap_t) - size)) << position; + + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set_bitfield( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index, + const unsigned int size) +{ + vied_nci_resource_bitmap_t ret = 0; + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bit_mask_set_bitfield(): enter:\n"); + + bit_mask = vied_nci_bitfield_mask(index, size); + ret = vied_nci_bitmap_set(bitmap, bit_mask); + + return ret; +} + + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + vied_nci_resource_bitmap_t ret = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_set_unique(): enter:\n"); + + if ((bitmap & bit_mask) == 0) + ret = bitmap | bit_mask; + + return ret; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bit_mask_set_unique( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index) +{ + vied_nci_resource_bitmap_t ret = 0; + vied_nci_resource_bitmap_t bit_mask; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bit_mask_set_unique(): enter:\n"); + + bit_mask = vied_nci_bit_mask(index); + + if (((bitmap & bit_mask) == 0) && (bit_mask != 0)) + ret = bitmap | bit_mask; + + return ret; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_empty( + const vied_nci_resource_bitmap_t bitmap) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_empty(): enter:\n"); + + return (bitmap == 0); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_set( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_set(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return !vied_nci_is_bitmap_clear(bitmap, bit_mask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bit_set_in_bitmap( + const vied_nci_resource_bitmap_t bitmap, + const unsigned int index) +{ + + vied_nci_resource_bitmap_t bitmask; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bit_set_in_bitmap(): enter:\n"); + bitmask = vied_nci_bit_mask(index); + return vied_nci_is_bitmap_set(bitmap, bitmask); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_bitmap_clear( + const vied_nci_resource_bitmap_t bitmap, + const vied_nci_resource_bitmap_t bit_mask) +{ + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_bitmap_clear(): enter:\n"); + +/* + assert(vied_nci_is_bitmap_one_hot(bit_mask)); +*/ + return ((bitmap & bit_mask) == 0); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +int vied_nci_bitmap_compute_weight( + const vied_nci_resource_bitmap_t bitmap) +{ + vied_nci_resource_bitmap_t loc_bitmap = bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_compute_weight(): enter:\n"); + + /* Do not need the iterator "i" */ + for (i = 0; (i < VIED_NCI_RESOURCE_BITMAP_BITS) && + (loc_bitmap != 0); i++) { + weight += loc_bitmap & 0x01; + loc_bitmap >>= 1; + } + + return weight; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_union( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_bitmap_union(): enter:\n"); + return (bitmap0 | bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_intersection( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "ia_css_kernel_bitmap_intersection(): enter:\n"); + return (bitmap0 & bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_bitmap_xor( + const vied_nci_resource_bitmap_t bitmap0, + const vied_nci_resource_bitmap_t bitmap1) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, "vied_nci_bitmap_xor(): enter:\n"); + return (bitmap0 ^ bitmap1); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_cell_bit_mask( + const vied_nci_cell_ID_t cell_id) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_bit_mask(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (cell_id < VIED_NCI_RESOURCE_BITMAP_BITS)) { + bit_mask = (vied_nci_resource_bitmap_t)1 << cell_id; + } + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_resource_bitmap_t vied_nci_barrier_bit_mask( + const vied_nci_barrier_ID_t barrier_id) +{ + vied_nci_resource_bitmap_t bit_mask = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_barrier_bit_mask(): enter:\n"); + + if ((barrier_id < VIED_NCI_N_BARRIER_ID) && + ((barrier_id + VIED_NCI_N_CELL_ID) < VIED_NCI_RESOURCE_BITMAP_BITS)) { + bit_mask = (vied_nci_resource_bitmap_t)1 << + (barrier_id + VIED_NCI_N_CELL_ID); + } + return bit_mask; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_cell_type_ID_t vied_nci_cell_get_type( + const vied_nci_cell_ID_t cell_id) +{ + vied_nci_cell_type_ID_t cell_type = VIED_NCI_N_CELL_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_type(): enter:\n"); + + if (cell_id < VIED_NCI_N_CELL_ID) { + cell_type = vied_nci_cell_type[cell_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_cell_get_type(): invalid argument\n"); + } + + return cell_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_mem_get_type( + const vied_nci_mem_ID_t mem_id) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_mem_get_type(): enter:\n"); + + if (mem_id < VIED_NCI_N_MEM_ID) { + mem_type = vied_nci_mem_type[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_mem_get_type(): invalid argument\n"); + } + + return mem_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_mem_get_size( + const vied_nci_mem_ID_t mem_id) +{ + uint16_t mem_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_mem_get_size(): enter:\n"); + + if (mem_id < VIED_NCI_N_MEM_ID) { + mem_size = vied_nci_mem_size[mem_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_mem_get_size(): invalid argument\n"); + } + + return mem_size; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_dev_chn_get_size( + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + uint16_t dev_chn_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_dev_chn_get_size(): enter:\n"); + + if (dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + dev_chn_size = vied_nci_dev_chn_size[dev_chn_id]; + } else { + IA_CSS_TRACE_0(PSYSAPI_SIM, WARNING, + "vied_nci_dev_chn_get_size(): invalid argument\n"); + } + + return dev_chn_size; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_cell_of_type( + const vied_nci_cell_ID_t cell_id, + const vied_nci_cell_type_ID_t cell_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_cell_of_type(): enter:\n"); + + return ((vied_nci_cell_get_type(cell_id) == + cell_type_id) && (cell_type_id != + VIED_NCI_N_CELL_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_mem_of_type( + const vied_nci_mem_ID_t mem_id, + const vied_nci_mem_type_ID_t mem_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_mem_of_type(): enter:\n"); + + return ((vied_nci_mem_get_type(mem_id) == mem_type_id) && + (mem_type_id != VIED_NCI_N_MEM_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_is_cell_mem_of_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index, + const vied_nci_mem_type_ID_t mem_type_id) +{ + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_is_cell_mem_of_type(): enter:\n"); + + return ((vied_nci_cell_get_mem_type(cell_id, mem_index) == mem_type_id) + && (mem_type_id != VIED_NCI_N_MEM_TYPE_ID)); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +bool vied_nci_has_cell_mem_of_id( + const vied_nci_cell_ID_t cell_id, + const vied_nci_mem_ID_t mem_id) +{ + uint16_t mem_index; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_has_cell_mem_of_id(): enter:\n"); + + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + if ((vied_nci_cell_get_mem(cell_id, mem_index) == mem_id) && + (mem_id != VIED_NCI_N_MEM_ID)) { + break; + } + } + + return (mem_index < VIED_NCI_N_MEM_TYPE_ID); +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +uint16_t vied_nci_cell_get_mem_count( + const vied_nci_cell_ID_t cell_id) +{ + uint16_t mem_count = 0; + vied_nci_cell_type_ID_t cell_type; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem_count(): enter:\n"); + + cell_type = vied_nci_cell_get_type(cell_id); + + if (cell_type < VIED_NCI_N_CELL_TYPE_ID) + mem_count = vied_nci_N_cell_mem[cell_type]; + + return mem_count; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_cell_get_mem_type( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem_type(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_type = vied_nci_cell_mem_type[ + vied_nci_cell_get_type(cell_id)][mem_index]; + } + + return mem_type; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_ID_t vied_nci_cell_get_mem( + const vied_nci_cell_ID_t cell_id, + const uint16_t mem_index) +{ + vied_nci_mem_ID_t mem_id = VIED_NCI_N_MEM_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_get_mem(): enter:\n"); + + if ((cell_id < VIED_NCI_N_CELL_ID) && + (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_id = vied_nci_cell_mem[cell_id][mem_index]; + } + + return mem_id; +} + +IA_CSS_PSYS_SIM_STORAGE_CLASS_C +vied_nci_mem_type_ID_t vied_nci_cell_type_get_mem_type( + const vied_nci_cell_type_ID_t cell_type_id, + const uint16_t mem_index) +{ + vied_nci_mem_type_ID_t mem_type = VIED_NCI_N_MEM_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_SIM, VERBOSE, + "vied_nci_cell_type_get_mem_type(): enter:\n"); + + if ((cell_type_id < VIED_NCI_N_CELL_TYPE_ID) + && (mem_index < VIED_NCI_N_MEM_TYPE_ID)) { + mem_type = vied_nci_cell_mem_type[cell_type_id][mem_index]; + } + + return mem_type; +} + +#endif /* __PSYS_SYSTEM_GLOBAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c new file mode 100644 index 000000000000..b0e0aebb6e77 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/sim/src/vied_nci_psys_system.c @@ -0,0 +1,26 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_sim_storage_class.h" + +/* + * Functions to possibly inline + */ + +#ifdef __IA_CSS_PSYS_SIM_INLINE__ +STORAGE_CLASS_INLINE int +__ia_css_psys_system_global_avoid_warning_on_empty_file(void) { return 0; } +#else /* __IA_CSS_PSYS_SIM_INLINE__ */ +#include "psys_system_global_impl.h" +#endif /* __IA_CSS_PSYS_SIM_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h new file mode 100644 index 000000000000..4a2f96e9405e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_manifest_types.h @@ -0,0 +1,102 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_MANIFEST_TYPES_H +#define __IA_CSS_PSYS_MANIFEST_TYPES_H + +/*! \file */ + +/** @file ia_css_psys_manifest_types.h + * + * The types belonging to the terminal/program/ + * program group manifest static module + */ + +#include +#include "vied_nci_psys_resource_model.h" + + +/* This value is used in the manifest to indicate that the resource + * offset field must be ignored and the resource is relocatable + */ +#define IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE ((vied_nci_resource_size_t)(-1)) + +/* + * Connection type defining the interface source/sink + * + * Note that the connection type does not define the + * real-time configuration of the system, i.e. it + * does not describe whether a source and sink + * program group or sub-system operate synchronously + * that is a program script property {online, offline} + * (see FAS 5.16.3) + */ +#define IA_CSS_CONNECTION_BITMAP_BITS 8 +typedef uint8_t ia_css_connection_bitmap_t; + +#define IA_CSS_CONNECTION_TYPE_BITS 32 +typedef enum ia_css_connection_type { + /**< The terminal is in DDR */ + IA_CSS_CONNECTION_MEMORY = 0, + /**< The terminal is a (watermark) queued stream over DDR */ + IA_CSS_CONNECTION_MEMORY_STREAM, + /* The terminal is a device port */ + IA_CSS_CONNECTION_STREAM, + IA_CSS_N_CONNECTION_TYPES +} ia_css_connection_type_t; + +#define IA_CSS_PROGRAM_TYPE_BITS 32 +typedef enum ia_css_program_type { + IA_CSS_PROGRAM_TYPE_SINGULAR = 0, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER, + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER, + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB, + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER, +/* + * Future extension; A bitmap coding starts making more sense + * + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB_PARALLEL_SUPER, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER_PARALLEL_SUB, + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER_PARALLEL_SUPER, + */ + IA_CSS_N_PROGRAM_TYPES +} ia_css_program_type_t; + +#define IA_CSS_PROGRAM_GROUP_ID_BITS 32 +typedef uint32_t ia_css_program_group_ID_t; +#define IA_CSS_PROGRAM_ID_BITS 32 +typedef uint32_t ia_css_program_ID_t; + +#define IA_CSS_PROGRAM_INVALID_ID ((uint32_t)(-1)) +#define IA_CSS_PROGRAM_GROUP_INVALID_ID ((uint32_t)(-1)) + +typedef struct ia_css_program_group_manifest_s +ia_css_program_group_manifest_t; +typedef struct ia_css_program_manifest_s +ia_css_program_manifest_t; +typedef struct ia_css_data_terminal_manifest_s +ia_css_data_terminal_manifest_t; + +/* ============ Program Control Init Terminal Manifest - START ============ */ +typedef struct ia_css_program_control_init_manifest_program_desc_s + ia_css_program_control_init_manifest_program_desc_t; + +typedef struct ia_css_program_control_init_terminal_manifest_s + ia_css_program_control_init_terminal_manifest_t; +/* ============ Program Control Init Terminal Manifest - END ============ */ + +#endif /* __IA_CSS_PSYS_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h new file mode 100644 index 000000000000..ee8321ea1f12 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.h @@ -0,0 +1,311 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H + +#include "ia_css_psys_static_storage_class.h" + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.h + * + * Define the methods on the program group manifest object that are not part of + * a single interface + */ + +#include + +#include /* uint8_t */ + +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ +#include "ia_css_terminal_manifest.h" +#include "ia_css_rbm_manifest_types.h" + +#define IA_CSS_PROGRAM_GROUP_INVALID_ALIGNMENT ((uint8_t)(-1)) + +/*! Get the stored size of the program group manifest object + + @param manifest[in] program group manifest object + + @return size, 0 on invalid argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +size_t ia_css_program_group_manifest_get_size( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the program group ID of the program group manifest object + + @param manifest[in] program group manifest object + + @return program group ID, IA_CSS_PROGRAM_GROUP_INVALID_ID on invalid argument +*/ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_group_ID_t +ia_css_program_group_manifest_get_program_group_ID( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the program group ID of the program group manifest object + + @param manifest[in] program group manifest object + + @param program group ID + + @return 0 on success, -1 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +int ia_css_program_group_manifest_set_program_group_ID( + ia_css_program_group_manifest_t *manifest, + ia_css_program_group_ID_t id); + +/*! Get the storage alignment constraint of the program group binary data + + @param manifest[in] program group manifest object + + @return alignment, IA_CSS_PROGRAM_GROUP_INVALID_ALIGNMENT on invalid manifest + argument +*/ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_alignment( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the storage alignment constraint of the program group binary data + + @param manifest[in] program group manifest object + @param alignment[in] alignment desired + + @return < 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +int ia_css_program_group_manifest_set_alignment( + ia_css_program_group_manifest_t *manifest, + const uint8_t alignment); + +/*! Get the kernel enable bitmap of the program group + + @param manifest[in] program group manifest object + + @return bitmap, 0 on invalid manifest argument + */ +extern ia_css_kernel_bitmap_t +ia_css_program_group_manifest_get_kernel_bitmap( + const ia_css_program_group_manifest_t *manifest); + +/*! Set the kernel enable bitmap of the program group + + @param manifest[in] program group manifest object + @param kernel bitmap[in] kernel enable bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_group_manifest_set_kernel_bitmap( + ia_css_program_group_manifest_t *manifest, + const ia_css_kernel_bitmap_t bitmap); + +/*! Get the number of programs in the program group manifest object + + @param manifest[in] program group manifest object + + @return program count, 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_program_count( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the number of terminals in the program group manifest object + + @param manifest[in] program group manifest object + + @return terminal count, 0 on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +uint8_t ia_css_program_group_manifest_get_terminal_count( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) private data blob in the manifest + + @param manifest[in] program group manifest object + + @return private data blob, NULL on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +void *ia_css_program_group_manifest_get_private_data( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) routing bitmap (rbm) manifest + + @param manifest[in] program group manifest object + + @return rbm manifest, NULL on invalid manifest argument + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_rbm_manifest_t * +ia_css_program_group_manifest_get_rbm_manifest( + const ia_css_program_group_manifest_t *manifest); + +/*! Get the (pointer to) indexed program manifest in the program group manifest + * object + + @param manifest[in] program group manifest object + @param program_index[in] index of the program manifest object + + @return program manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_manifest_t * +ia_css_program_group_manifest_get_prgrm_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int program_index); + +/*! Get the (pointer to) indexed terminal manifest in the program group + * manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_terminal_manifest_t * +ia_css_program_group_manifest_get_term_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed data terminal manifest in the program group + * manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return data terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_data_terminal_manifest_t * +ia_css_program_group_manifest_get_data_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed parameter terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return parameter terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_param_terminal_manifest_t * +ia_css_program_group_manifest_get_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed spatial param terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return spatial param terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_spatial_param_terminal_manifest_t * +ia_css_program_group_manifest_get_spatial_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed sliced param terminal manifest in the program + * group manifest object + + @param manifest[in] program group manifest object + @param program_index[in] index of the terminal manifest object + + @return sliced param terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_sliced_param_terminal_manifest_t * +ia_css_program_group_manifest_get_sliced_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! Get the (pointer to) indexed program terminal manifest in the program group + * manifest object + + @parammanifest[in]program group manifest object + @paramprogram_index[in]index of the terminal manifest object + + @return program terminal manifest, NULL on invalid arguments + */ +IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +ia_css_program_terminal_manifest_t * +ia_css_program_group_manifest_get_program_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index); + +/*! initialize program group manifest + + @param manifest[in] program group manifest object + @param program_count[in] number of programs. + @param terminal_count[in] number of terminals. + @param program_deps[in] program dependencies for programs in pg. + @param terminal_deps[in] terminal dependencies for programs in pg. + @param terminal_type[in] array of terminal types, binary specific + static frame data + @param cached_in_param_section_count[in]Number of parameter terminal sections + @param cached_out_param_section_count[in] Number of parameter out terminal + @param spatial_param_section_count[in] Array[spatial_terminal_count] + with sections per cached out + terminal + @param sliced_in_param_section_count[in] Array[sliced_in_terminal_count] + with sections per sliced in + terminal + @param sliced_out_param_section_count[in] Array[sliced_out_terminal_count] + with sections per sliced out + terminal + @param fragment_param_section_count[in] Number of fragment parameter + sections of the program init + terminal, + @param kernel_fragment_seq_count[in] Number of kernel fragment + seqence info. + @param progctrlinit_load_section_counts[in] Number of progctrinit load + sections (size of array is program_count) + @param progctrlinit_connect_section_counts[in] Number of progctrinit connect + sections (size of array is program_count) + @return none; + */ +extern void ia_css_program_group_manifest_init( + ia_css_program_group_manifest_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependencies, + const uint8_t *terminal_dependencies, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_in_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts); + +#ifdef __IA_CSS_PSYS_STATIC_INLINE__ +#include "ia_css_psys_program_group_manifest_impl.h" +#endif /* __IA_CSS_PSYS_STATIC_INLINE__ */ + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h new file mode 100644 index 000000000000..ce802ff5dd8d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.hsys.user.h @@ -0,0 +1,69 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.hsys.user.h + * + * Define the methods on the program group manifest object: Hsys user interface + */ + +#include + +#include /* bool */ + +/*! Print the program group manifest object to file/stream + + @param manifest[in] program group manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_manifest_print( + const ia_css_program_group_manifest_t *manifest, + void *fid); + +/*! Read the program group manifest object from file/stream + + @param fid[in] file/stream handle + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_read( + void *fid); + +/*! Write the program group manifest object to file/stream + + @param manifest[in] program group manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_group_manifest_write( + const ia_css_program_group_manifest_t *manifest, + void *fid); + +/*! Boolean test if the program group manifest is valid + + @param manifest[in] program group manifest + + @return true if program group manifest is correct, false on error + */ +extern bool ia_css_is_program_group_manifest_valid( + const ia_css_program_group_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h new file mode 100644 index 000000000000..242f02108dd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_group_manifest.sim.h @@ -0,0 +1,127 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_program_group_manifest.sim.h + * + * Define the methods on the program group manifest object: Simulation only + */ + +#include + +#include /* uint8_t */ +#include "ia_css_terminal_defs.h" + +/*! Create a program group manifest object from specification + + @param specification[in] specification (index) + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_create( + const unsigned int specification); + +/*! Destroy the program group manifest object + + @param manifest[in] program group manifest + + @return NULL + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_destroy( + ia_css_program_group_manifest_t *manifest); + +/*! Compute the size of storage required for allocating + * the program group (PG) manifest object + + @param program_count[in] Number of programs in the PG + @param terminal_count[in] Number of terminals on the PG + @param program_dependency_count[in] Array[program_count] with the PG + @param terminal_dependency_count[in] Array[program_count] with the + terminal dependencies + @param terminal_type[in] Array[terminal_count] with the + terminal type + @param cached_in_param_section_count[in] Number of parameter + in terminal sections + @param cached_out_param_section_count[in] Number of parameter + out terminal sections + @param sliced_param_section_count[in] Array[sliced_terminal_count] + with sections per + sliced in terminal + @param sliced_out_param_section_count[in] Array[sliced_terminal_count] + with sections per + sliced out terminal + @param spatial_param_section_count[in] Array[spatial_terminal_count] + with sections per + spatial terminal + @param fragment_param_section_count[in] Number of fragment parameter + sections of the + program init terminal, + @param kernel_fragment_seq_count[in] Number of + kernel_fragment_seq_count. + @param progctrlinit_load_section_counts[in] Number of progctrinit load + sections (size of array is program_count) + @param progctrlinit_connect_section_counts[in] Number of progctrinit connect + sections (size of array is program_count) + @return 0 on error + */ +size_t ia_css_sizeof_program_group_manifest( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts); + +/*! Create (the storage for) the program group manifest object + + @param program_count[in] Number of programs in the program group + @param terminal_count[in] Number of terminals on the program group + @param program_dependency_count[in] Array[program_count] with the + program dependencies + @param terminal_dependency_count[in] Array[program_count] with the + terminal dependencies + @param terminal_type[in] Array[terminal_count] with the + terminal type + + @return NULL on error + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_alloc( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type); + +/*! Free (the storage of) the program group manifest object + + @param manifest[in] program group manifest + + @return NULL + */ +extern ia_css_program_group_manifest_t *ia_css_program_group_manifest_free( + ia_css_program_group_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h new file mode 100644 index 000000000000..b7333671ed4f --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.h @@ -0,0 +1,488 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.h + * + * Define the methods on the program manifest object that are not part of a + * single interface + */ + +#include + +#include /* uint8_t */ + +#include + +#include + +#include /* ia_css_kernel_bitmap_t */ + +/* + * Resources needs + */ +#include + +#define IA_CSS_PROGRAM_INVALID_DEPENDENCY ((uint8_t)(-1)) + +/*! Check if the program manifest object specifies a fixed cell allocation + + @param manifest[in] program manifest object + + @return has_fixed_cell, false on invalid argument + */ +extern bool ia_css_has_program_manifest_fixed_cell( + const ia_css_program_manifest_t *manifest); + +/*! Get the stored size of the program manifest object + + @param manifest[in] program manifest object + + @return size, 0 on invalid argument + */ +extern size_t ia_css_program_manifest_get_size( + const ia_css_program_manifest_t *manifest); + +/*! Get the program ID of the program manifest object + + @param manifest[in] program manifest object + + @return program ID, IA_CSS_PROGRAM_INVALID_ID on invalid argument + */ +extern ia_css_program_ID_t ia_css_program_manifest_get_program_ID( + const ia_css_program_manifest_t *manifest); + +/*! Set the program ID of the program manifest object + + @param manifest[in] program manifest object + + @param program ID + + @return 0 on success, -1 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_program_ID( + ia_css_program_manifest_t *manifest, + ia_css_program_ID_t id); + +/*! Get the (pointer to) the program group manifest parent of the program + * manifest object + + @param manifest[in] program manifest object + + @return the pointer to the parent, NULL on invalid manifest argument + */ +extern ia_css_program_group_manifest_t *ia_css_program_manifest_get_parent( + const ia_css_program_manifest_t *manifest); + +/*! Set the (pointer to) the program group manifest parent of the program + * manifest object + + @param manifest[in] program manifest object + @param program_offset[in] this program's offset from + program_group_manifest's base address. + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_parent_offset( + ia_css_program_manifest_t *manifest, + int32_t program_offset); + +/*! Get the type of the program manifest object + + @param manifest[in] program manifest object + + @return program type, limit value (IA_CSS_N_PROGRAM_TYPES) on invalid manifest + argument +*/ +extern ia_css_program_type_t ia_css_program_manifest_get_type( + const ia_css_program_manifest_t *manifest); + +/*! Set the type of the program manifest object + + @param manifest[in] program manifest object + @param program_type[in] program type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_type( + ia_css_program_manifest_t *manifest, + const ia_css_program_type_t program_type); + +/*! Set the cell id of the program manifest object + + @param manifest[in] program manifest object + @param program_cell_id[in] program cell id + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_cell_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_ID_t cell_id); + +/*! Set the cell type of the program manifest object + + @param manifest[in] program manifest object + @param program_cell_type[in] program cell type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_program_manifest_set_cell_type_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_type_ID_t cell_type_id); + +/*! Set cells bitmap for the program + + @param manifest[in] program manifest object + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_cells_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get cells bitmap for the program + + @param manifest[in] program manifest object + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_cells_bitmap( + const ia_css_program_manifest_t *manifest); + +/*! Set DFM port bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_dfm_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get bitmap of DFM ports requested for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return DFM port bitmap + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + + +/*! Set active DFM port specification bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param bitmap[in] bitmap + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_dfm_active_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap); + +/*! Get active DFM port specification bitmap for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_active_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + +/*! Set DFM device relocatability specification for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + @param is_relocatable[in] 1 if dfm device ports are relocatable, 0 otherwise + + @return 0 when not applicable and/or invalid arguments + */ +extern int ia_css_program_manifest_set_is_dfm_relocatable( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const uint8_t is_relocatable); + +/*! Get DFM device relocatability specification for the program + + @param manifest[in] program manifest object + @param dfm_type_id[in] DFM resource type ID + + @return 1 if dfm device ports are relocatable, 0 otherwise + */ +extern uint8_t ia_css_program_manifest_get_is_dfm_relocatable( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id); + + +/*! Get the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param int_mem_size[in] internal memory size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_int_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t int_mem_size); + +/*! Get the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Set the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param ext_mem_size[in] external memory size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_ext_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_size); + +/*! Get a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return 0 when not applicable and/or invalid arguments + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Set a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + @param dev_chn_size[in] device channel size + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_dev_chn_size( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_size); + +/*! Set a device channel resource (offset) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + @param dev_chn_offset[in] device channel offset + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_dev_chn_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_offset); + + +/*! Set the memory resource (offset) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type id + @param ext_mem_offset[in] external memory offset + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_ext_mem_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_offset); + +/*! Get a device channel resource (offset) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return Valid fixed offset (if value is greater or equal to 0) or + IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE if offset + is relocatable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +/*! Get the memory resource (offset) specification for a memory + that does not belong to the cell where the program will be mapped. + + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return Valid fixed offset (if value is greater or equal to 0) or + IA_CSS_PROGRAM_MANIFEST_RESOURCE_OFFSET_IS_RELOCATABLE if offset + is relocatable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + + +/*! Get the kernel composition of the program manifest object + + @param manifest[in] program manifest object + + @return bitmap, 0 on invalid arguments + */ +extern ia_css_kernel_bitmap_t ia_css_program_manifest_get_kernel_bitmap( + const ia_css_program_manifest_t *manifest); + +/*! Set the kernel dependency of the program manifest object + + @param manifest[in] program manifest object + @param kernel_bitmap[in] kernel composition bitmap + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_kernel_bitmap( + ia_css_program_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap); + +/*! Get the number of programs this programs depends on from the program group + * manifest object + + @param manifest[in] program manifest object + + @return program dependency count + */ +extern uint8_t ia_css_program_manifest_get_program_dependency_count( + const ia_css_program_manifest_t *manifest); + +/*! Get the index of the program which the programs at this index depends on + from the program manifest object + + @param manifest[in] program manifest object + + @return program dependency, + IA_CSS_PROGRAM_INVALID_DEPENDENCY on invalid arguments + */ +extern uint8_t ia_css_program_manifest_get_program_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index); + +/*! Set the index of the program which the programs at this index depends on + in the program manifest object + + @param manifest[in] program manifest object + + @return program dependency + */ +extern int ia_css_program_manifest_set_program_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t program_dependency, + const unsigned int index); + +/*! Get the number of terminals this programs depends on from the program group + * manifest object + + @param manifest[in] program manifest object + + @return program dependency count + */ +extern uint8_t ia_css_program_manifest_get_terminal_dependency_count( + const ia_css_program_manifest_t *manifest); + +/*! Get the index of the terminal which the programs at this index depends on + from the program manifest object + + @param manifest[in] program manifest object + + @return terminal dependency, IA_CSS_PROGRAM_INVALID_DEPENDENCY on error + */ +uint8_t ia_css_program_manifest_get_terminal_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index); + +/*! Set the index of the terminal which the programs at this index depends on + in the program manifest object + + @param manifest[in] program manifest object + + @return < 0 on invalid arguments + */ +extern int ia_css_program_manifest_set_terminal_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t terminal_dependency, + const unsigned int index); + +/*! Check if the program manifest object specifies a subnode program + + @param manifest[in] program manifest object + + @return is_subnode, false on invalid argument + */ +extern bool ia_css_is_program_manifest_subnode_program_type( + const ia_css_program_manifest_t *manifest); + +/*! Check if the program manifest object specifies a supernode program + + @param manifest[in] program manifest object + + @return is_supernode, false on invalid argument + */ +extern bool ia_css_is_program_manifest_supernode_program_type( + const ia_css_program_manifest_t *manifest); +/*! Check if the program manifest object specifies a singular program + + @param manifest[in] program manifest object + + @return is_singular, false on invalid argument + */ +extern bool ia_css_is_program_manifest_singular_program_type( + const ia_css_program_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h new file mode 100644 index 000000000000..9d737b75a576 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.kernel.h @@ -0,0 +1,96 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.hsys.kernel.h + * + * Define the methods on the program manifest object: Hsys kernel interface + */ + +#include + +#include + +#include /* uint8_t */ + +/* + * Resources needs + */ + +/*! Get the cell ID from the program manifest object + + @param manifest[in] program manifest object + + Note: If the cell ID is specified, the program this manifest belongs to + must be mapped on that instance. If the cell ID is invalid (limit value) + then the cell type ID must be specified instead + + @return cell ID, limit value if not specified + */ +extern vied_nci_cell_ID_t ia_css_program_manifest_get_cell_ID( + const ia_css_program_manifest_t *manifest); + +/*! Get the cell type ID from the program manifest object + + @param manifest[in] program manifest object + + Note: If the cell type ID is specified, the program this manifest belongs + to can be mapped on any instance of this clee type. If the cell type ID is + invalid (limit value) then a specific cell ID must be specified instead + + @return cell ID, limit value if not specified + */ +extern vied_nci_cell_type_ID_t ia_css_program_manifest_get_cell_type_ID( + const ia_css_program_manifest_t *manifest); + +/*! Get the memory resource (size) specification for a memory + that belongs to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Get the memory resource (size) specification for a memory + that does not belong to the cell where the program will be mapped + + @param manifest[in] program manifest object + @param mem_type_id[in] mem type ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id); + +/*! Get a device channel resource (size) specification + + @param manifest[in] program manifest object + @param dev_chn_id[in] device channel ID + + @return 0 when not applicable + */ +extern vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_KERNEL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h new file mode 100644 index 000000000000..087c84b7106e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.hsys.user.h @@ -0,0 +1,38 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.hsys.user.h + * + * Define the methods on the program manifest object: Hsys user interface + */ + +#include + +/*! Print the program manifest object to file/stream + + @param manifest[in] program manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_program_manifest_print( + const ia_css_program_manifest_t *manifest, + void *fid); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h new file mode 100644 index 000000000000..0c2cef11f30e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_program_manifest.sim.h @@ -0,0 +1,61 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H +#define __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_program_manifest.sim.h + * + * Define the methods on the program manifest object: Simulation only + */ + +#include + +#include /* uint8_t */ + +/*! Compute the size of storage required for allocating + * the program manifest object + + @param program_dependency_count[in] Number of programs this one depends on + @param terminal_dependency_count[in] Number of terminals this one depends on + + @return 0 on error + */ +extern size_t ia_css_sizeof_program_manifest( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +/*! Create (the storage for) the program manifest object + + @param program_dependency_count[in] Number of programs this one depends on + @param terminal_dependency_count[in] Number of terminals this one depends on + + @return NULL on error + */ +extern ia_css_program_manifest_t *ia_css_program_manifest_alloc( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +/*! Destroy (the storage of) the program manifest object + + @param manifest[in] program manifest + + @return NULL + */ +extern ia_css_program_manifest_t *ia_css_program_manifest_free( + ia_css_program_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_PROGRAM_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h new file mode 100644 index 000000000000..f3c832b5a4a3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_storage_class.h @@ -0,0 +1,28 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H +#define __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_PSYS_STATIC_INLINE__ +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +#else +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PSYS_STATIC_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PSYS_STATIC_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h new file mode 100644 index 000000000000..7c5612cd0969 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_static_trace.h @@ -0,0 +1,103 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_STATIC_TRACE_H +#define __IA_CSS_PSYS_STATIC_TRACE_H + +#include "ia_css_psysapi_trace.h" + +#define PSYS_STATIC_TRACE_LEVEL_CONFIG_DEFAULT PSYSAPI_TRACE_LOG_LEVEL_OFF + +/* Default sub-module tracing config */ +#if (!defined(PSYSAPI_STATIC_TRACING_OVERRIDE)) + #define PSYS_STATIC_TRACE_LEVEL_CONFIG \ + PSYS_STATIC_TRACE_LEVEL_CONFIG_DEFAULT +#endif + +/* Module/sub-module specific trace setting will be used if + * the trace level is not specified from the module or + PSYSAPI_STATIC_TRACING_OVERRIDE is defined + */ +#if (defined(PSYSAPI_STATIC_TRACING_OVERRIDE)) + /* Module/sub-module specific trace setting */ + #if PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_OFF + /* PSYSAPI_TRACE_LOG_LEVEL_OFF */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_NORMAL + /* PSYSAPI_TRACE_LOG_LEVEL_NORMAL */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_DISABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_DISABLED + #elif PSYSAPI_STATIC_TRACING_OVERRIDE == PSYSAPI_TRACE_LOG_LEVEL_DEBUG + /* PSYSAPI_TRACE_LOG_LEVEL_DEBUG */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + IA_CSS_TRACE_METHOD_NATIVE + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + IA_CSS_TRACE_LEVEL_ENABLED + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + IA_CSS_TRACE_LEVEL_ENABLED + #else + #error "No PSYSAPI_DATA Tracing level defined" + #endif +#else + /* Inherit Module trace setting */ + #define PSYSAPI_STATIC_TRACE_METHOD \ + PSYSAPI_TRACE_METHOD + #define PSYSAPI_STATIC_TRACE_LEVEL_ASSERT \ + PSYSAPI_TRACE_LEVEL_ASSERT + #define PSYSAPI_STATIC_TRACE_LEVEL_ERROR \ + PSYSAPI_TRACE_LEVEL_ERROR + #define PSYSAPI_STATIC_TRACE_LEVEL_WARNING \ + PSYSAPI_TRACE_LEVEL_WARNING + #define PSYSAPI_STATIC_TRACE_LEVEL_INFO \ + PSYSAPI_TRACE_LEVEL_INFO + #define PSYSAPI_STATIC_TRACE_LEVEL_DEBUG \ + PSYSAPI_TRACE_LEVEL_DEBUG + #define PSYSAPI_STATIC_TRACE_LEVEL_VERBOSE \ + PSYSAPI_TRACE_LEVEL_VERBOSE +#endif + +#endif /* __IA_CSS_PSYSAPI_STATIC_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h new file mode 100644 index 000000000000..0fa62b32e1a7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.h @@ -0,0 +1,423 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_H + +/*! \file */ + +/** @file ia_css_psys_terminal_manifest.h + * + * Define the methods on the terminal manifest object that are not part of a + * single interface + */ + +#include + +#include + +#include + +#include /* ia_css_frame_format_bitmap_t */ +#include /* ia_css_kernel_bitmap_t */ + +#include /* size_t */ +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_manifest_base_types.h" + + +/*! Check if the terminal manifest object specifies a spatial param terminal + * type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_spatial_parameter_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a program terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_program_terminal( + const ia_css_terminal_manifest_t *manifest); + + +/*! Check if the terminal manifest object specifies a program control init terminal type + * + * @param manifest[in] terminal manifest object + * + * @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_program_control_init_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a (cached) parameter + * terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_parameter_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a (sliced) parameter + * terminal type + + @param manifest[in] terminal manifest object + + @return is_parameter_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_sliced_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Check if the terminal manifest object specifies a data terminal type + + @param manifest[in] terminal manifest object + + @return is_data_terminal, false on invalid manifest argument + */ +extern bool ia_css_is_terminal_manifest_data_terminal( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the stored size of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return size, 0 on invalid manifest argument + */ +extern size_t ia_css_terminal_manifest_get_size( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the (pointer to) the program group manifest parent of the terminal + * manifest object + + @param manifest[in] terminal manifest object + + @return the pointer to the parent, NULL on invalid manifest argument + */ +extern ia_css_program_group_manifest_t *ia_css_terminal_manifest_get_parent( + const ia_css_terminal_manifest_t *manifest); + +/*! Set the (pointer to) the program group manifest parent of the terminal + * manifest object + + @param manifest[in] terminal manifest object + @param terminal_offset[in] this terminal's offset from + program_group_manifest base address. + + @return < 0 on invalid arguments + */ +extern int ia_css_terminal_manifest_set_parent_offset( + ia_css_terminal_manifest_t *manifest, + int32_t terminal_offset); + +/*! Get the type of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return terminal type, limit value (IA_CSS_N_TERMINAL_TYPES) on invalid + manifest argument +*/ +extern ia_css_terminal_type_t ia_css_terminal_manifest_get_type( + const ia_css_terminal_manifest_t *manifest); + +/*! Set the type of the terminal manifest object + + @param manifest[in] terminal manifest object + @param terminal_type[in] terminal type + + @return < 0 on invalid manifest argument + */ +extern int ia_css_terminal_manifest_set_type( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_type_t terminal_type); + +/*! Set the ID of the terminal manifest object + + @param manifest[in] terminal manifest object + @param ID[in] terminal ID + + @return < 0 on invalid manifest argument + */ +int ia_css_terminal_manifest_set_ID( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_ID_t ID); + +/*! Get the type of the terminal manifest object + + @param manifest[in] terminal manifest object + + @return terminal id, IA_CSS_TERMINAL_INVALID_ID on invalid manifest argument + */ +extern ia_css_terminal_ID_t ia_css_terminal_manifest_get_ID( + const ia_css_terminal_manifest_t *manifest); + +/*! Get the supported frame types of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return frame format bitmap, 0 on invalid manifest argument +*/ +extern ia_css_frame_format_bitmap_t + ia_css_data_terminal_manifest_get_frame_format_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the chosen frame type for the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param bitmap[in] frame format bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_frame_format_bitmap( + ia_css_data_terminal_manifest_t *manifest, + ia_css_frame_format_bitmap_t bitmap); + +/*! Check if the (data) terminal manifest object supports compression + + @param manifest[in] (data) terminal manifest object + + @return compression_support, true if compression is supported + */ +extern bool ia_css_data_terminal_manifest_can_support_compression( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the compression support feature of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param compression_support[in] set true to support compression + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_compression_support( + ia_css_data_terminal_manifest_t *manifest, + bool compression_support); + +/*! Set the supported connection types of the terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param bitmap[in] connection bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_connection_bitmap( + ia_css_data_terminal_manifest_t *manifest, ia_css_connection_bitmap_t bitmap); + +/*! Get the connection bitmap of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return connection bitmap, 0 on invalid manifest argument +*/ +extern ia_css_connection_bitmap_t + ia_css_data_terminal_manifest_get_connection_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Get the kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + + @return kernel bitmap, 0 on invalid manifest argument + */ +extern ia_css_kernel_bitmap_t ia_css_data_terminal_manifest_get_kernel_bitmap( + const ia_css_data_terminal_manifest_t *manifest); + +/*! Set the kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param kernel_bitmap[in] kernel dependency bitmap + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_kernel_bitmap( + ia_css_data_terminal_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap); + +/*! Set the unique kernel dependency of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param index[in] kernel dependency bitmap index + + @return < 0 on invalid argument(s) + */ +extern int ia_css_data_terminal_manifest_set_kernel_bitmap_unique( + ia_css_data_terminal_manifest_t *manifest, + const unsigned int index); + +/*! Set the min size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_min_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the max size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_max_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the min size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_min_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the max size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the frame array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_max_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the min fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_min_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Set the max fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_set_max_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the min fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param min_size[in] Minimum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_min_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]); + +/*! Get the max fragment size of the (data) terminal manifest object + + @param manifest[in] (data) terminal manifest object + @param max_size[in] Maximum size of the fragment array + + @return < 0 on invalid manifest argument + */ +extern int ia_css_data_terminal_manifest_get_max_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]); + +/*! + * Get the program control init connect section count for program prog. + * @param prog[in] program control init terminal program desc + * @return number of connect section for program prog. + */ + +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_connect_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog); + + +/*! + * Get the program control init load section count for program prog. + * @param prog[in] program control init terminal program desc + * @return number of load section for program prog. + */ + +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_load_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog); + +/*! + * Get the program control init terminal manifest size. + * @param nof_programs[in] Number of programs. + * @param nof_load_sections[in] Array of size nof_programs, + * encoding the number of load sections. + * @param nof_connect_sections[in] Array of size nof_programs, + * encoding the number of connect sections. + * @return < 0 on invalid manifest argument + */ +extern +unsigned int ia_css_program_control_init_terminal_manifest_get_size( + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections); + +/*! + * Get the program control init terminal manifest program desc. + * @param terminal[in] Program control init terminal. + * @param program[in] Number of programs. + * @return program control init terminal program desc (or NULL if error). + */ +extern +ia_css_program_control_init_manifest_program_desc_t * +ia_css_program_control_init_terminal_manifest_get_program_desc( + const ia_css_program_control_init_terminal_manifest_t *terminal, + unsigned int program); + +/*! + * Initialize the program control init terminal manifest. + * @param nof_programs[in] Number of programs + * @param nof_load_sections[in] Array of size nof_programs, + * encoding the number of load sections. + * @param nof_connect_sections[in] Array of size nof_programs, + * encoding the number of connect sections. + * @return < 0 on invalid manifest argument + */ +extern +int ia_css_program_control_init_terminal_manifest_init( + ia_css_program_control_init_terminal_manifest_t *terminal, + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections); + +/*! + * Pretty prints the program control init terminal manifest. + * @param terminal[in] Program control init terminal. + */ +extern +void ia_css_program_control_init_terminal_manifest_print( + ia_css_program_control_init_terminal_manifest_t *terminal); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h new file mode 100644 index 000000000000..1d2f06f3cbce --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.hsys.user.h @@ -0,0 +1,38 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H + +/*! \file */ + +/** @file ia_css_psys_terminal.hsys.user.h + * + * Define the methods on the termianl manifest object: Hsys user interface + */ + +#include + +/*! Print the terminal manifest object to file/stream + + @param manifest[in] terminal manifest object + @param fid[out] file/stream handle + + @return < 0 on error + */ +extern int ia_css_terminal_manifest_print( + const ia_css_terminal_manifest_t *manifest, + void *fid); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_HSYS_USER_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h new file mode 100644 index 000000000000..f7da810d82f1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/interface/ia_css_psys_terminal_manifest.sim.h @@ -0,0 +1,48 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H +#define __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H + +/*! \file */ + +/** @file ia_css_psys_terminal_manifest.sim.h + * + * Define the methods on the terminal manifest object: Simulation only + */ + +#include /* size_t */ +#include "ia_css_terminal.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_defs.h" + +/*! Create (the storage for) the terminal manifest object + + @param terminal_type[in] type of the terminal manifest {parameter, data} + + @return NULL on error + */ +extern ia_css_terminal_manifest_t *ia_css_terminal_manifest_alloc( + const ia_css_terminal_type_t terminal_type); + +/*! Destroy (the storage of) the terminal manifest object + + @param manifest[in] terminal manifest + + @return NULL + */ +extern ia_css_terminal_manifest_t *ia_css_terminal_manifest_free( + ia_css_terminal_manifest_t *manifest); + +#endif /* __IA_CSS_PSYS_TERMINAL_MANIFEST_SIM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c new file mode 100644 index 000000000000..5af4de746310 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest.c @@ -0,0 +1,1038 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_psys_static_storage_class.h" +#include "ia_css_psys_program_group_manifest.h" +#include "ia_css_rbm_manifest.h" + +/* + * Functions to possibly inline + */ + +#ifndef __IA_CSS_PSYS_STATIC_INLINE__ +#include "ia_css_psys_program_group_manifest_impl.h" +#endif /* __IA_CSS_PSYS_STATIC_INLINE__ */ + +/* + * Functions not to inline + */ + +/* + * We need to refactor those files in order to + * build in the firmware only what is needed, + * switches are put current to workaround compilation problems + * in the firmware (for example lack of uint64_t support) + * supported in the firmware + */ +#if !defined(__HIVECC) +size_t ia_css_sizeof_program_group_manifest( + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependency_count, + const uint8_t *terminal_dependency_count, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts) +{ + size_t size = 0; + int i = 0; + int j = 0; + int m = 0; + int n = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_sizeof_program_group_manifest(): enter:\n"); + + verifexit(program_count != 0); + verifexit(program_dependency_count != NULL); + verifexit(terminal_dependency_count != NULL); + + size += sizeof(ia_css_program_group_manifest_t); + + /* Private payload in the program group manifest */ + size += ceil_mul(sizeof(struct ia_css_psys_private_pg_data), + sizeof(uint64_t)); + /* RBM manifest in the program group manifest */ + size += ceil_mul(sizeof(ia_css_rbm_manifest_t), + sizeof(uint64_t)); + + for (i = 0; i < (int)program_count; i++) { + size += ia_css_sizeof_program_manifest( + program_dependency_count[i], + terminal_dependency_count[i]); + } + + for (i = 0; i < (int)terminal_count; i++) { + switch (terminal_type[i]) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + size += ia_css_param_terminal_manifest_get_size( + cached_in_param_section_count); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + size += ia_css_param_terminal_manifest_get_size( + cached_out_param_section_count); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + size += ia_css_spatial_param_terminal_manifest_get_size( + spatial_param_section_count[j]); + j++; + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + size += ia_css_program_terminal_manifest_get_size( + fragment_param_section_count, + kernel_fragment_seq_count); + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + size += ia_css_program_control_init_terminal_manifest_get_size( + program_count, + progctrlinit_load_section_counts, + progctrlinit_connect_section_counts); + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + size += sizeof(ia_css_data_terminal_manifest_t); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + size += ia_css_sliced_param_terminal_manifest_get_size( + sliced_param_section_count[m]); + m++; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + size += ia_css_sliced_param_terminal_manifest_get_size( + sliced_out_param_section_count[n]); + n++; + break; + default: + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_sizeof_program_group_manifest invalid argument\n"); + } + } + +EXIT: + if (0 == program_count || 0 == terminal_count || + NULL == program_dependency_count || + NULL == terminal_dependency_count) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_sizeof_program_group_manifest invalid argument\n"); + } + return size; +} + +/* + * Currently, the design of XNR kernel inside the *_pregdc program group, + * does not fit the exact model as is being asserted on in + * ia_css_is_program_group_manifest_valid. We therefore disable some checks. + * Further investigation is needed to determine whether *_pregdc program group + * can be canged or that the model must be changed. + * #define USE_SIMPLIFIED_GRAPH_MODEL 1 allows multiple programs to be + * connected to the same terminal, and it allows a kernel be mapped over + * multiple programs. + */ +#define USE_SIMPLIFIED_GRAPH_MODEL 1 + +/* + * Model and/or check refinements + * - Parallel programs do not yet have mutual exclusive alternatives + * - The pgram dependencies do not need to be acyclic + * - Parallel programs need to have an equal kernel requirement + */ +bool ia_css_is_program_group_manifest_valid( + const ia_css_program_group_manifest_t *manifest) +{ + int i; + bool is_valid = false; + uint8_t terminal_count; + uint8_t program_count; + ia_css_kernel_bitmap_t total_bitmap; + ia_css_kernel_bitmap_t check_bitmap; + ia_css_kernel_bitmap_t terminal_bitmap; + /* + * Use a standard bitmap type for the minimum logic to check the DAG, + * generic functions can be used for the kernel enable bitmaps; Later + */ + vied_nci_resource_bitmap_t resource_bitmap; + int terminal_bitmap_weight; + bool has_parameter_terminal_in = false; + bool has_parameter_terminal_out = false; + bool has_program_control_init_terminal = false; + bool has_program_terminal = false; + bool has_program_terminal_sequencer_info = false; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_group_manifest_valid(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(ia_css_program_group_manifest_get_size(manifest) != 0); + verifexit(ia_css_program_group_manifest_get_alignment(manifest) != 0); + verifexit(ia_css_program_group_manifest_get_program_group_ID(manifest) != 0); + + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + total_bitmap = + ia_css_program_group_manifest_get_kernel_bitmap(manifest); + check_bitmap = ia_css_kernel_bitmap_clear(); + resource_bitmap = vied_nci_bit_mask(VIED_NCI_RESOURCE_BITMAP_BITS); + terminal_bitmap = ia_css_kernel_bitmap_clear(); + + verifexit(program_count != 0); + verifexit(terminal_count != 0); + verifexit(!ia_css_is_kernel_bitmap_empty(total_bitmap)); + verifexit(vied_nci_is_bitmap_empty(resource_bitmap)); + + /* Check the kernel bitmaps for terminals */ + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_manifest_t *terminal_manifest_i = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + bool is_parameter_in = + (IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN == + ia_css_terminal_manifest_get_type( + terminal_manifest_i)); + bool is_parameter_out = + (IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT == + ia_css_terminal_manifest_get_type( + terminal_manifest_i)); + bool is_data = + ia_css_is_terminal_manifest_data_terminal( + terminal_manifest_i); + bool is_program = + ia_css_is_terminal_manifest_program_terminal( + terminal_manifest_i); + bool is_spatial_param = + ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest_i); + bool is_program_control_init = + ia_css_is_terminal_manifest_program_control_init_terminal( + terminal_manifest_i); + + if (is_parameter_in) { + /* + * There can be only one cached in parameter terminal + * it serves kernels, not programs + */ + verifexit(!has_parameter_terminal_in); + has_parameter_terminal_in = is_parameter_in; + } else if (is_parameter_out) { + /* + * There can be only one cached out parameter terminal + * it serves kernels, not programs + */ + verifexit(!has_parameter_terminal_out); + has_parameter_terminal_out = is_parameter_out; + } else if (is_data) { + ia_css_data_terminal_manifest_t *dterminal_manifest_i = + (ia_css_data_terminal_manifest_t *) + terminal_manifest_i; + ia_css_kernel_bitmap_t terminal_bitmap_i = + ia_css_data_terminal_manifest_get_kernel_bitmap( + dterminal_manifest_i); + /* + * A terminal must depend on kernels that are a subset + * of the total, correction, it can only depend on one + * kernel + */ + verifexit(!ia_css_is_kernel_bitmap_empty( + terminal_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, terminal_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_onehot( + terminal_bitmap_i)); + } else if (is_program) { + verifexit(!has_program_terminal); + verifexit(terminal_manifest_i); + has_program_terminal = is_program; + has_program_terminal_sequencer_info = + (((ia_css_program_terminal_manifest_t *) + terminal_manifest_i)-> + kernel_fragment_sequencer_info_manifest_info_count + != 0); + } else if (is_program_control_init) { + has_program_control_init_terminal = is_program_control_init; + } else { + const ia_css_spatial_param_terminal_manifest_t + *spatial_param_man = + (const ia_css_spatial_param_terminal_manifest_t *) + terminal_manifest_i; + verifexit(spatial_param_man); + verifexit(is_spatial_param); + + terminal_bitmap = + ia_css_kernel_bitmap_set(terminal_bitmap, + spatial_param_man->kernel_id); + verifexit(!ia_css_is_kernel_bitmap_empty(terminal_bitmap)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, terminal_bitmap)); + } + } + + /* Check the kernel bitmaps for programs */ + for (i = 0; i < (int)program_count; i++) { + int j; + ia_css_program_manifest_t *program_manifest_i = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + ia_css_program_type_t program_type_i = + ia_css_program_manifest_get_type(program_manifest_i); + ia_css_kernel_bitmap_t program_bitmap_i = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_i); + uint8_t program_dependency_count_i = + ia_css_program_manifest_get_program_dependency_count( + program_manifest_i); + uint8_t terminal_dependency_count_i = + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest_i); + uint8_t program_dependency_i0 = + ia_css_program_manifest_get_program_dependency( + program_manifest_i, 0); + bool is_sub_i = + ia_css_is_program_manifest_subnode_program_type( + program_manifest_i); + bool is_exclusive_sub_i = + (program_type_i == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB); + bool is_virtual_sub_i = + (program_type_i == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); + bool is_super_i = + ia_css_is_program_manifest_supernode_program_type( + program_manifest_i); + + /* + * A program must have kernels that + * are a subset of the total + */ + verifexit(!ia_css_is_kernel_bitmap_empty( + program_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + total_bitmap, program_bitmap_i)); + verifexit((program_type_i != IA_CSS_N_PROGRAM_TYPES)); + verifexit((program_dependency_count_i + terminal_dependency_count_i) != 0); + /* + * Checks for subnodes + * - Parallel subnodes cannot depend on terminals + * - Exclusive subnodes must depend on + * fewer terminals than the supernode + * - Subnodes only depend on a supernode of the same type + * - Must have a subset of the supernode's kernels + * (but not equal) + * - This tests only positive cases + * Checks for singular or supernodes + * - Cannot depend on exclusive subnodes + * - No intersection between kernels + * (too strict for multiple instances ?) + */ + if (is_sub_i) { + /* Subnode */ + ia_css_program_manifest_t *program_manifest_k = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, program_dependency_i0); + ia_css_program_type_t program_type_k = + ia_css_program_manifest_get_type( + program_manifest_k); + ia_css_kernel_bitmap_t program_bitmap_k = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_k); + + verifexit(program_dependency_count_i == 1); + if (is_exclusive_sub_i || is_virtual_sub_i) { + verifexit(terminal_dependency_count_i <= + ia_css_program_manifest_get_terminal_dependency_count( + program_manifest_k)); + } else{ + verifexit(terminal_dependency_count_i == 0); + } + verifexit(program_type_k == + (is_exclusive_sub_i ? + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER : + is_virtual_sub_i ? + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER : + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER)); + verifexit(!ia_css_is_kernel_bitmap_equal( + program_bitmap_k, program_bitmap_i)); + verifexit(ia_css_is_kernel_bitmap_subset( + program_bitmap_k, program_bitmap_i)); + } else { + /* Singular or Supernode */ + int k; + + for (k = 0; k < program_dependency_count_i; k++) { + uint8_t program_dependency_k = + ia_css_program_manifest_get_program_dependency( + program_manifest_i, k); + ia_css_program_manifest_t *program_manifest_k = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, (int)program_dependency_k); + ia_css_program_type_t program_type_k = + ia_css_program_manifest_get_type( + program_manifest_k); + ia_css_kernel_bitmap_t program_bitmap_k = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_k); + + verifexit(program_dependency_k < + program_count); + verifexit((program_type_k != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_k != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)); +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_k)); +#else + (void)program_bitmap_k; +#endif + } + } + + /* Check for relations */ + for (j = 0; j < (int)program_count; j++) { + int k; + ia_css_program_manifest_t *program_manifest_j = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, j); + ia_css_program_type_t program_type_j = + ia_css_program_manifest_get_type(program_manifest_j); + ia_css_kernel_bitmap_t program_bitmap_j = + ia_css_program_manifest_get_kernel_bitmap( + program_manifest_j); + uint8_t program_dependency_count_j = + ia_css_program_manifest_get_program_dependency_count( + program_manifest_j); + uint8_t program_dependency_j0 = + ia_css_program_manifest_get_program_dependency( + program_manifest_j, 0); + bool is_sub_j = + ia_css_is_program_manifest_subnode_program_type( + program_manifest_j); + bool is_super_j = + ia_css_is_program_manifest_supernode_program_type( + program_manifest_j); + bool is_virtual_sub_j = + (program_type_j == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); + bool is_j_subset_i = + ia_css_is_kernel_bitmap_subset( + program_bitmap_i, program_bitmap_j); + bool is_i_subset_j = + ia_css_is_kernel_bitmap_subset( + program_bitmap_j, program_bitmap_i); + + /* Test below would fail for i==j */ + if (i == j) + continue; + + /* Empty sets are always subsets, but meaningless */ + verifexit(!ia_css_is_kernel_bitmap_empty( + program_bitmap_j)); + + /* + * Checks for mutual subnodes + * - Parallel subnodes must have an equal + * set of kernels + * - Exclusive and virtual subnodes must + * have an unequal set of kernels + * Checks for subnodes + * - Subnodes must have a subset of kernels + */ + if (((program_type_i == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB))) { + + verifexit(program_dependency_count_j == 1); + verifexit(program_dependency_i0 != i); + verifexit(program_dependency_j0 != i); + + if (program_dependency_i0 == + program_dependency_j0) { + verifexit(is_sub_i); + /* + * Subnodes are subsets, + * not for virtual nodes + */ + if (!is_virtual_sub_i) + verifexit( + ((is_j_subset_i || + is_i_subset_j))); + /* + * That must be equal for + * parallel subnodes, + * must be unequal for + * exlusive and virtual subnodes + */ + verifexit( + ((is_j_subset_i && is_i_subset_j) ^ + (is_exclusive_sub_i | + is_virtual_sub_i))); + + } + if (is_j_subset_i || is_i_subset_j) { + verifexit(program_dependency_i0 == + program_dependency_j0); + } + } + + if (((program_type_i == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_PARALLEL_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB)) || + ((program_type_i == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER) && + (program_type_j == + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB))) { + + verifexit(program_dependency_count_j == 1); + verifexit(!is_i_subset_j); + + if (program_dependency_j0 == i) { + verifexit(program_dependency_i0 != + program_dependency_j0); + verifexit(is_super_i); + verifexit(is_j_subset_i); + + } + if (is_j_subset_i) { + verifexit(program_dependency_j0 == i); + } + } + + /* + * Checks for dependent nodes + * - Cannot depend on exclusive subnodes + * - No intersection between kernels + * (too strict for multiple instances ?) + * unless a subnode + */ + for (k = 0; k < (int)program_dependency_count_j; k++) { + uint8_t program_dependency_k = + ia_css_program_manifest_get_program_dependency( + program_manifest_j, k); + + verifexit((program_dependency_k < + program_count)); + if (program_dependency_k == i) { + /* program[j] depends on program[i] */ + verifexit((i != j)); + verifexit((program_type_i != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_i != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)); + verifexit(USE_SIMPLIFIED_GRAPH_MODEL || + (ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_j) ^ is_sub_j)); + } + } + + /* + * Checks for supernodes and subnodes + * - Detect nodes that kernel-wise are subsets, + * but not connected to the correct supernode + * - We do not (yet) detect if programs properly + * depend on all parallel nodes + */ + if (!ia_css_is_kernel_bitmap_intersection_empty( + program_bitmap_i, program_bitmap_j)) { + /* + * This test will pass if + * the program manifest is NULL, + * but that's no concern here + */ +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(!ia_css_is_program_manifest_singular_program_type( + program_manifest_i)); + verifexit(!ia_css_is_program_manifest_singular_program_type( + program_manifest_j)); + if (!is_virtual_sub_j) + verifexit((is_j_subset_i || is_i_subset_j)); +#else + (void)is_virtual_sub_j; +#endif + if (is_super_i) { + verifexit(is_sub_j); + verifexit(program_dependency_j0 == i); + } + if (is_super_j) { + verifexit(is_sub_i); + verifexit(program_dependency_i0 == j); + } + } + } + check_bitmap = ia_css_kernel_bitmap_union( + check_bitmap, program_bitmap_i); + /* + * A terminal can be bound to only a single + * (of multiple concurrent) program(s), + * i.e. the one that holds the iterator to control it + * Only singular and super nodes can depend on a terminal. + * This loop accumulates all terminal + * dependencies over all programs + */ + for (j = 0; j < (int)terminal_dependency_count_i; j++) { + uint8_t terminal_dependency = + ia_css_program_manifest_get_terminal_dependency( + program_manifest_i, j); + + verifexit(terminal_dependency < terminal_count); + if ((program_type_i != + IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) && + (program_type_i != + IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB)) { + /* If the subnode always came after the */ + /* supernode we could check for presence */ + resource_bitmap = + vied_nci_bit_mask_set_unique( + resource_bitmap, + terminal_dependency); +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit(!vied_nci_is_bitmap_empty( + resource_bitmap)); +#endif + } + } + } + verifexit(ia_css_is_kernel_bitmap_equal( + total_bitmap, check_bitmap)); + + terminal_bitmap_weight = + vied_nci_bitmap_compute_weight(resource_bitmap); + verifexit(terminal_bitmap_weight >= 0); + if (has_parameter_terminal_in || + has_parameter_terminal_out || + has_program_terminal || + has_program_control_init_terminal) { + int skip_terminal_count = 0; + + if (has_parameter_terminal_in) + skip_terminal_count++; + if (has_parameter_terminal_out) + skip_terminal_count++; + if (has_program_control_init_terminal) { + skip_terminal_count++; + } + if (has_program_terminal) + skip_terminal_count++; + if (has_program_terminal_sequencer_info) + skip_terminal_count--; +#if USE_SIMPLIFIED_GRAPH_MODEL == 0 + verifexit((terminal_bitmap_weight == + (terminal_count - skip_terminal_count))); +#endif + } else + verifexit((terminal_bitmap_weight == terminal_count)); + + is_valid = true; +EXIT: + if (is_valid == false) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_is_program_group_manifest_valid: failed\n"); + } + return is_valid; +} + +int ia_css_program_group_manifest_set_kernel_bitmap( + ia_css_program_group_manifest_t *manifest, + const ia_css_kernel_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = bitmap; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_kernel_bitmap invalid argument\n"); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_group_manifest_get_kernel_bitmap( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_kernel_bitmap invalid argument\n"); + } + return bitmap; +} + +void ia_css_program_group_manifest_init( + ia_css_program_group_manifest_t *blob, + const uint8_t program_count, + const uint8_t terminal_count, + const uint8_t *program_dependencies, + const uint8_t *terminal_dependencies, + const ia_css_terminal_type_t *terminal_type, + const uint16_t cached_in_param_section_count, + const uint16_t cached_out_param_section_count, + const uint16_t *spatial_param_section_count, + const uint16_t fragment_param_section_count, + const uint16_t *sliced_in_param_section_count, + const uint16_t *sliced_out_param_section_count, + const uint16_t kernel_fragment_seq_count, + const uint16_t *progctrlinit_load_section_counts, + const uint16_t *progctrlinit_connect_section_counts) +{ + int i = 0; + int j = 0; + int m = 0; + int n = 0; + int result; + uint32_t offset = 0; + char *prg_manifest_base, *terminal_manifest_base; + size_t program_size = 0; + + /* + * assert(blob != NULL); + */ + COMPILATION_ERROR_IF( + SIZE_OF_DATA_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_data_terminal_manifest_t))); + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_GROUP_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_group_manifest_t))); + COMPILATION_ERROR_IF( + SIZE_OF_PROGRAM_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_manifest_t))); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_group_manifest_init(): enter:\n"); + + for (i = 0; i < (int)program_count; i++) { + program_size += + ia_css_sizeof_program_manifest(program_dependencies[i], + terminal_dependencies[i]); + } + + /* A program group ID cannot be zero */ + blob->ID = 1; + blob->program_count = program_count; + blob->terminal_count = terminal_count; + blob->program_manifest_offset = sizeof(ia_css_program_group_manifest_t); + blob->terminal_manifest_offset = + (uint32_t)blob->program_manifest_offset + program_size; + + prg_manifest_base = (char *) + (((char *)blob) + blob->program_manifest_offset); + offset = blob->program_manifest_offset; + for (i = 0; i < (int)program_count; i++) { + ia_css_program_manifest_init( + (ia_css_program_manifest_t *)prg_manifest_base, + program_dependencies[i], terminal_dependencies[i]); + ia_css_program_manifest_set_parent_offset( + (ia_css_program_manifest_t *)prg_manifest_base, offset); + program_size = + ia_css_sizeof_program_manifest(program_dependencies[i], + terminal_dependencies[i]); + prg_manifest_base += program_size; + offset += (uint32_t)program_size; + } + + offset = blob->terminal_manifest_offset; + terminal_manifest_base = (char *) (((char *)blob) + offset); + for (i = 0; i < (int)terminal_count; i++) { + size_t terminal_size = 0; + ia_css_terminal_manifest_t *term_manifest = + (ia_css_terminal_manifest_t *)terminal_manifest_base; + + ia_css_terminal_manifest_set_parent_offset( + (ia_css_terminal_manifest_t *) + terminal_manifest_base, + offset); + switch (terminal_type[i]) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + result = ia_css_param_terminal_manifest_init( + (ia_css_param_terminal_manifest_t *) + term_manifest, + cached_in_param_section_count); + if (0 == result) { + terminal_size = + ia_css_param_terminal_manifest_get_size( + cached_in_param_section_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init failed in cached in terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + result = ia_css_param_terminal_manifest_init( + (ia_css_param_terminal_manifest_t *) + term_manifest, + cached_out_param_section_count); + if (0 == result) { + terminal_size = + ia_css_param_terminal_manifest_get_size( + cached_out_param_section_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + result = ia_css_spatial_param_terminal_manifest_init( + (ia_css_spatial_param_terminal_manifest_t *) + term_manifest, + spatial_param_section_count[j]); + if (0 == result) { + terminal_size = + ia_css_spatial_param_terminal_manifest_get_size( + spatial_param_section_count[j]); + j++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_spatial_param_terminal_manifest_init failed in spatial terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM: + result = ia_css_program_terminal_manifest_init( + (ia_css_program_terminal_manifest_t *) + term_manifest, + fragment_param_section_count, + kernel_fragment_seq_count); + if (0 == result) { + terminal_size = + ia_css_program_terminal_manifest_get_size( + fragment_param_section_count, + kernel_fragment_seq_count); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_program_terminal_manifest_init failed in program terminal\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT: + result = ia_css_program_control_init_terminal_manifest_init( + (ia_css_program_control_init_terminal_manifest_t *) + term_manifest, + program_count, + progctrlinit_load_section_counts, + progctrlinit_connect_section_counts); + if (0 == result) { + terminal_size = + ia_css_program_control_init_terminal_manifest_get_size( + program_count, + NULL, + NULL); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_program_control_init_terminal_manifest_init failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + terminal_size = sizeof(ia_css_data_terminal_manifest_t); + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + result = ia_css_sliced_param_terminal_manifest_init( + (ia_css_sliced_param_terminal_manifest_t *) + term_manifest, + sliced_in_param_section_count[m]); + if (0 == result) { + terminal_size = + ia_css_sliced_param_terminal_manifest_get_size( + sliced_in_param_section_count[m]); + m++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init in sliced terminal failed\n"); + } + break; + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + result = ia_css_sliced_param_terminal_manifest_init( + (ia_css_sliced_param_terminal_manifest_t *) + term_manifest, + sliced_out_param_section_count[n]); + if (0 == result) { + terminal_size = + ia_css_sliced_param_terminal_manifest_get_size( + sliced_out_param_section_count[n]); + n++; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_param_terminal_manifest_init in sliced out terminal failed\n"); + } + break; + default: + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_init invalid argument\n"); + } + term_manifest->size = (uint16_t)terminal_size; + term_manifest->terminal_type = terminal_type[i]; + terminal_manifest_base += terminal_size; + offset += (uint32_t)terminal_size; + } + + /* Set the private program group manifest blob offset */ + blob->private_data_offset = offset; + offset += ceil_mul(sizeof(struct ia_css_psys_private_pg_data), + sizeof(uint64_t)); + + /* Set the RBM manifest blob offset */ + blob->rbm_manifest_offset = offset; + offset += ceil_mul(sizeof(ia_css_rbm_manifest_t), + sizeof(uint64_t)); + + assert(offset <= UINT16_MAX); + blob->size = (uint16_t)offset; +} + +int ia_css_program_group_manifest_print( + const ia_css_program_group_manifest_t *manifest, + void *fid) +{ + int retval = -1; + int i; + uint8_t program_count, terminal_count; + ia_css_kernel_bitmap_t bitmap; + struct ia_css_psys_private_pg_data *priv_data; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_group_manifest_print(): enter:\n"); + + NOT_USED(fid); + + verifexit(manifest != NULL); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sizeof(manifest) = %d\n", + (int)ia_css_program_group_manifest_get_size(manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "alignment(manifest) = %d\n", + (int)ia_css_program_group_manifest_get_alignment(manifest)); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program group ID = %d\n", + (int)ia_css_program_group_manifest_get_program_group_ID( + manifest)); + + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + bitmap = ia_css_program_group_manifest_get_kernel_bitmap(manifest); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "%d program manifests\n", (int)program_count); + for (i = 0; i < (int)program_count; i++) { + ia_css_program_manifest_t *program_manifest = + ia_css_program_group_manifest_get_prgrm_mnfst( + manifest, i); + + retval = ia_css_program_manifest_print(program_manifest, fid); + verifjmpexit(retval == 0); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "%d terminal manifests\n", (int)terminal_count); + for (i = 0; i < (int)terminal_count; i++) { + ia_css_terminal_manifest_t *terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst( + manifest, i); + + retval = ia_css_terminal_manifest_print( + terminal_manifest, fid); + verifjmpexit(retval == 0); + } + + priv_data = + (struct ia_css_psys_private_pg_data *) + ia_css_program_group_manifest_get_private_data(manifest); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "private_data_offset %d\n", manifest->private_data_offset); + + for (i = 0; i < IPU_DEVICE_GP_PSA_MUX_NUM_MUX; i++) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "PSA MUX id %d mux val %d\n", i, + priv_data->psa_mux_conf[i]); + + } + + for (i = 0; i < IPU_DEVICE_GP_ISA_STATIC_MUX_NUM_MUX; i++) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "ISA MUX id %d mux val %d\n", i, + priv_data->isa_mux_conf[i]); + + } + + for (i = 0; i < IPU_DEVICE_ACB_NUM_ACB; i++) { + + if (priv_data->acb_route[i].in_select != + NCI_ACB_PORT_INVALID) { + + assert(priv_data->acb_route[i].in_select != + NCI_ACB_PORT_INVALID && + priv_data->acb_route[i].out_select != + NCI_ACB_PORT_INVALID); + + IA_CSS_TRACE_3(PSYSAPI_STATIC, INFO, + "Route Cell id %d In %d Out %d\n", i, + priv_data->acb_route[i].in_select, + priv_data->acb_route[i].out_select); + } + + } + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_base_addr 0x%x\n", + priv_data->input_buffer_info.buffer_base_addr); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: bpe = %d\n", + priv_data->input_buffer_info.bpe); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_width = %d\n", + priv_data->input_buffer_info.buffer_width); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: buffer_height = %d\n", + priv_data->input_buffer_info.buffer_height); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: num_of_buffers = %d\n", + priv_data->input_buffer_info.num_of_buffers); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "Input Buffer: dfm_port_addr = 0x%x\n", + priv_data->input_buffer_info.dfm_port_addr); + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_group_manifest_print failed (%i)\n", + retval); + } + return retval; +} +#endif /* !defined(__HIVECC) */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h new file mode 100644 index 000000000000..527b8cc00dd1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_manifest_impl.h @@ -0,0 +1,415 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H + +#include +#include +#include +#include +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_psys_private_pg_data.h" +#include /* Safer bit mask functions */ +#include "ia_css_psys_static_trace.h" +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +size_t ia_css_program_group_manifest_get_size( + const ia_css_program_group_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_size invalid argument\n"); + } + return size; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_group_ID_t +ia_css_program_group_manifest_get_program_group_ID( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_program_group_ID_t id = IA_CSS_PROGRAM_GROUP_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_group_ID(): enter:\n"); + + if (manifest != NULL) { + id = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_program_group_ID invalid argument\n"); + } + return id; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +int ia_css_program_group_manifest_set_program_group_ID( + ia_css_program_group_manifest_t *manifest, + ia_css_program_group_ID_t id) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_program_group_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = id; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_program_group_ID invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +int ia_css_program_group_manifest_set_alignment( + ia_css_program_group_manifest_t *manifest, + const uint8_t alignment) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_set_alignment(): enter:\n"); + + if (manifest != NULL) { + manifest->alignment = alignment; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_set_alignment invalid argument\n"); + } + return retval; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_alignment( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t alignment = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_alignment(): enter:\n"); + + if (manifest != NULL) { + alignment = manifest->alignment; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_alignment invalid argument\n"); + } + return alignment; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +void *ia_css_program_group_manifest_get_private_data( + const ia_css_program_group_manifest_t *manifest) +{ + void *private_data = NULL; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_private_data(%p): enter:\n", + manifest); + + verifexit(manifest != NULL); + + private_data = (void *)((const char *)manifest + + manifest->private_data_offset); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_private_data invalid argument\n"); + } + return private_data; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_rbm_manifest_t *ia_css_program_group_manifest_get_rbm_manifest( + const ia_css_program_group_manifest_t *manifest) +{ + ia_css_rbm_manifest_t *rbm_manifest = NULL; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_rbm_manifest(%p): enter:\n", + manifest); + + verifexit(manifest != NULL); + + rbm_manifest = (ia_css_rbm_manifest_t *)((const char *)manifest + + manifest->rbm_manifest_offset); + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_rbm_manifest invalid argument\n"); + } + return rbm_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_manifest_t * +ia_css_program_group_manifest_get_prgrm_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int program_index) +{ + ia_css_program_manifest_t *prg_manifest_base; + uint8_t *program_manifest = NULL; + uint8_t program_count; + unsigned int i; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_prgrm_mnfst(%p,%d): enter:\n", + manifest, program_index); + + program_count = + ia_css_program_group_manifest_get_program_count(manifest); + + verifexit(manifest != NULL); + verifexit(program_index < program_count); + + prg_manifest_base = (ia_css_program_manifest_t *)((char *)manifest + + manifest->program_manifest_offset); + if (program_index < program_count) { + program_manifest = (uint8_t *)prg_manifest_base; + for (i = 0; i < program_index; i++) { + program_manifest += ((ia_css_program_manifest_t *) + program_manifest)->size; + } + } + +EXIT: + if (NULL == manifest || program_index >= program_count) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_prgrm_mnfst invalid argument\n"); + } + return (ia_css_program_manifest_t *)program_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_data_terminal_manifest_t * +ia_css_program_group_manifest_get_data_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_data_terminal_manifest_t *data_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_data_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_data_terminal(terminal_manifest)); + + data_terminal_manifest = + (ia_css_data_terminal_manifest_t *)terminal_manifest; +EXIT: + return data_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_param_terminal_manifest_t * +ia_css_program_group_manifest_get_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_param_terminal_manifest_t *param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_parameter_terminal( + terminal_manifest)); + param_terminal_manifest = + (ia_css_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_spatial_param_terminal_manifest_t * +ia_css_program_group_manifest_get_spatial_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_spatial_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_spatial_parameter_terminal( + terminal_manifest)); + + spatial_param_terminal_manifest = + (ia_css_spatial_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return spatial_param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_sliced_param_terminal_manifest_t * +ia_css_program_group_manifest_get_sliced_param_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_sliced_param_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_sliced_terminal( + terminal_manifest)); + + sliced_param_terminal_manifest = + (ia_css_sliced_param_terminal_manifest_t *)terminal_manifest; +EXIT: + return sliced_param_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_program_terminal_manifest_t * +ia_css_program_group_manifest_get_program_terminal_manifest( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_program_terminal_manifest_t *program_terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_terminal_manifest(%p, %d): enter:\n", + manifest, (int)terminal_index); + + terminal_manifest = + ia_css_program_group_manifest_get_term_mnfst(manifest, + terminal_index); + + verifexit(ia_css_is_terminal_manifest_program_terminal( + terminal_manifest)); + + program_terminal_manifest = + (ia_css_program_terminal_manifest_t *)terminal_manifest; + EXIT: + return program_terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +ia_css_terminal_manifest_t * +ia_css_program_group_manifest_get_term_mnfst( + const ia_css_program_group_manifest_t *manifest, + const unsigned int terminal_index) +{ + ia_css_terminal_manifest_t *terminal_manifest = NULL; + ia_css_terminal_manifest_t *terminal_manifest_base; + uint8_t terminal_count; + uint8_t i = 0; + uint32_t offset; + + IA_CSS_TRACE_2(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_term_mnfst(%p,%d): enter:\n", + manifest, (int)terminal_index); + + verifexit(manifest != NULL); + + terminal_count = + ia_css_program_group_manifest_get_terminal_count(manifest); + + verifexit(terminal_index < terminal_count); + + terminal_manifest_base = + (ia_css_terminal_manifest_t *)((char *)manifest + + manifest->terminal_manifest_offset); + terminal_manifest = terminal_manifest_base; + while (i < terminal_index) { + offset = + (uint32_t)ia_css_terminal_manifest_get_size(terminal_manifest); + terminal_manifest = (ia_css_terminal_manifest_t *) + ((char *)terminal_manifest + offset); + i++; + } +EXIT: + return terminal_manifest; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_program_count( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t program_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_program_count(): enter:\n"); + + if (manifest != NULL) { + program_count = manifest->program_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_program_count invalid argument\n"); + } + return program_count; +} + +IA_CSS_PSYS_STATIC_STORAGE_CLASS_C +uint8_t ia_css_program_group_manifest_get_terminal_count( + const ia_css_program_group_manifest_t *manifest) +{ + uint8_t terminal_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_group_manifest_get_terminal_count(): enter:\n"); + + if (manifest != NULL) { + terminal_count = manifest->terminal_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_group_manifest_get_terminal_count invalid argument\n"); + } + return terminal_count; +} + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_MANIFEST_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h new file mode 100644 index 000000000000..502d59def6e9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_group_private.h @@ -0,0 +1,212 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H +#define __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H + +#include "ia_css_psys_manifest_types.h" +#include "ia_css_terminal_manifest_types.h" +#include "ia_css_kernel_bitmap.h" +#include "ia_css_program_group_data.h" +#include "vied_nci_psys_resource_model.h" +#include "ia_css_rbm_manifest_types.h" +#include +#include +#include + +#define SIZE_OF_PROGRAM_GROUP_MANIFEST_STRUCT_IN_BITS \ + ((IA_CSS_KERNEL_BITMAP_BITS) \ + + (IA_CSS_PROGRAM_GROUP_ID_BITS) \ + + (5 * IA_CSS_UINT16_T_BITS) \ + + (5 * IA_CSS_UINT8_T_BITS) \ + + (5 * IA_CSS_UINT8_T_BITS)) + +struct ia_css_program_group_manifest_s { + /**< Indicate kernels are present in this program group */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Referral ID to program group FW */ + ia_css_program_group_ID_t ID; + uint16_t program_manifest_offset; + uint16_t terminal_manifest_offset; + /**< Offset to private data (not part of the official API) */ + uint16_t private_data_offset; + /**< Offset to RBM manifest */ + uint16_t rbm_manifest_offset; + /**< Size of this structure */ + uint16_t size; + /**< Storage alignment requirement (in uint8_t) */ + uint8_t alignment; + /**< Total number of kernels in this program group */ + uint8_t kernel_count; + /**< Total number of program in this program group */ + uint8_t program_count; + /**< Total number of terminals on this program group */ + uint8_t terminal_count; + /**< Total number of independent subgraphs in this program group */ + uint8_t subgraph_count; + /**< Padding; esnures that rbm_manifest starts on 64bit alignment */ + uint8_t reserved[5]; +}; + +#define SIZE_OF_PROGRAM_MANIFEST_STRUCT_IN_BITS \ + (IA_CSS_KERNEL_BITMAP_BITS \ + + IA_CSS_PROGRAM_ID_BITS \ + + IA_CSS_PROGRAM_TYPE_BITS \ + + (3 * IA_CSS_UINT32_T_BITS) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (VIED_NCI_RESOURCE_BITMAP_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + IA_CSS_UINT16_T_BITS \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_MEM_TYPE_ID) \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_DATA_MEM_TYPE_ID * 2) \ + + (VIED_NCI_RESOURCE_SIZE_BITS * VIED_NCI_N_DEV_CHN_ID * 2) \ + + (IA_CSS_UINT8_T_BITS * VIED_NCI_N_DEV_DFM_ID) \ + + (IA_CSS_PROCESS_MAX_CELLS * VIED_NCI_RESOURCE_ID_BITS) \ + + (VIED_NCI_RESOURCE_ID_BITS) \ + + (2 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST * IA_CSS_UINT8_T_BITS)) +/* + * This structure contains only the information required for resource + * management and construction of the process group. + * The header for the program binary load is separate + */ + +struct ia_css_program_manifest_s { + /**< Indicate which kernels lead to this program being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Referral ID to a specific program FW, valid ID's != 0 */ + ia_css_program_ID_t ID; + /**< Specification of for exclusive or parallel programs */ + ia_css_program_type_t program_type; + /**< offset to add to reach parent. This is negative value.*/ + int32_t parent_offset; + uint32_t program_dependency_offset; + uint32_t terminal_dependency_offset; +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM port allocation of this program */ + vied_nci_resource_bitmap_t dfm_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; + /**< Active DFM ports which need a kick + * If an empty port is configured to run in active mode, the empty + * port and the corresponding full port(s) in the stream must be kicked. + * The empty port must always be kicked aster the full port. + */ + vied_nci_resource_bitmap_t dfm_active_port_bitmap[VIED_NCI_N_DEV_DFM_ID]; +#endif + /**< Size of this structure */ + uint16_t size; + /**< (internal) Memory allocation size needs of this program */ + vied_nci_resource_size_t int_mem_size[VIED_NCI_N_MEM_TYPE_ID]; + /**< (external) Memory allocation size needs of this program */ + vied_nci_resource_size_t ext_mem_size[VIED_NCI_N_DATA_MEM_TYPE_ID]; + vied_nci_resource_size_t ext_mem_offset[VIED_NCI_N_DATA_MEM_TYPE_ID]; + /**< Device channel allocation size needs of this program */ + vied_nci_resource_size_t dev_chn_size[VIED_NCI_N_DEV_CHN_ID]; + vied_nci_resource_size_t dev_chn_offset[VIED_NCI_N_DEV_CHN_ID]; +#if (VIED_NCI_N_DEV_DFM_ID > 0) + /**< DFM ports are relocatable if value is set to 1. + * The flag is per dfm port type. + * This will not be supported for now. + */ + uint8_t is_dfm_relocatable[VIED_NCI_N_DEV_DFM_ID]; +#endif + /** Array of all the cells this program needs */ +#if IA_CSS_PROCESS_MAX_CELLS == 1 + vied_nci_resource_id_t cell_id; +#else + vied_nci_resource_id_t cells[IA_CSS_PROCESS_MAX_CELLS]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + /**< (exclusive) indication of a cell type to be used by this program */ + vied_nci_resource_id_t cell_type_id; + + /**< Number of programs this program depends on */ + uint8_t program_dependency_count; + /**< Number of terminals this program depends on */ + uint8_t terminal_dependency_count; + /**< Padding bytes for 64bit alignment*/ +#if N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST > 0 + /*hivecc does not allow an array of zero length*/ + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_GROUP_MANFEST]; +#endif +}; + +/* + *Calculation for manual size check for struct ia_css_data_terminal_manifest_s + */ +#define SIZE_OF_DATA_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + IA_CSS_FRAME_FORMAT_BITMAP_BITS \ + + IA_CSS_CONNECTION_BITMAP_BITS \ + + IA_CSS_KERNEL_BITMAP_BITS \ + + (4 * (IA_CSS_UINT16_T_BITS * IA_CSS_N_DATA_DIMENSION)) \ + + IA_CSS_UINT16_T_BITS \ + + IA_CSS_UINT8_T_BITS \ + + (4*IA_CSS_UINT8_T_BITS)) +/* + * Inherited data terminal class + */ +struct ia_css_data_terminal_manifest_s { + /**< Data terminal base */ + ia_css_terminal_manifest_t base; + /**< Supported (4CC / MIPI / parameter) formats */ + ia_css_frame_format_bitmap_t frame_format_bitmap; + /**< Indicate which kernels lead to this terminal being used */ + ia_css_kernel_bitmap_t kernel_bitmap; + /**< Minimum size of the frame */ + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]; + /**< Maximum size of the frame */ + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]; + /**< Minimum size of a fragment that the program port can accept */ + uint16_t min_fragment_size[IA_CSS_N_DATA_DIMENSION]; + /**< Maximum size of a fragment that the program port can accept */ + uint16_t max_fragment_size[IA_CSS_N_DATA_DIMENSION]; + /**< Indicate if this terminal is derived from a principal terminal */ + uint16_t terminal_dependency; + /**< Indicate what (streaming) interface types this terminal supports */ + ia_css_connection_bitmap_t connection_bitmap; + /**< Indicates if compression is supported on the data associated with + * this terminal. '1' indicates compression is supported, + * '0' otherwise + */ + uint8_t compression_support; + uint8_t reserved[4]; +}; + +/* ============ Program Control Init Terminal Manifest - START ============ */ +#define N_PADDING_UINT8_IN_PROGCTRLINIT_MANIFEST_PROGRAM_DESC_STRUCT 4 +struct ia_css_program_control_init_manifest_program_desc_s { + uint16_t load_section_count; + uint16_t connect_section_count; + uint8_t padding[N_PADDING_UINT8_IN_PROGCTRLINIT_MANIFEST_PROGRAM_DESC_STRUCT]; +}; + +#define N_PADDING_UINT8_IN_PROGCTRLINIT_TERMINAL_MANIFEST_STRUCT 2 +struct ia_css_program_control_init_terminal_manifest_s { + ia_css_terminal_manifest_t base; + /* Number of programs in program group */ + uint32_t program_count; + /* + * Points to array of ia_css_program_control_init_terminal_program_desc_t + * with size program_count. + */ + uint16_t program_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROGCTRLINIT_TERMINAL_MANIFEST_STRUCT]; +}; +/* ============ Program Control Init Terminal Manifest - END ============ */ + +extern void ia_css_program_manifest_init( + ia_css_program_manifest_t *blob, + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count); + +#endif /* __IA_CSS_PSYS_PROGRAM_GROUP_PRIVATE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c new file mode 100644 index 000000000000..188f9d80193e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_program_manifest.c @@ -0,0 +1,1240 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include +#include +/* for ia_css_kernel_bitmap_t, ia_css_kernel_bitmap_print */ +#include + +#include +#include "ia_css_psys_program_group_private.h" +#include "ia_css_psys_static_trace.h" + +#include +#include + +size_t ia_css_sizeof_program_manifest( + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_sizeof_program_manifest(): enter:\n"); + + size += sizeof(ia_css_program_manifest_t); + size += program_dependency_count * sizeof(uint8_t); + size += terminal_dependency_count * sizeof(uint8_t); + size = ceil_mul(size, sizeof(uint64_t)); + + return size; +} + +bool ia_css_has_program_manifest_fixed_cell( + const ia_css_program_manifest_t *manifest) +{ + bool has_fixed_cell = false; + + vied_nci_cell_ID_t cell_id; + vied_nci_cell_type_ID_t cell_type_id; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_has_program_manifest_fixed_cell(): enter:\n"); + + verifexit(manifest != NULL); + + cell_id = ia_css_program_manifest_get_cell_ID(manifest); + cell_type_id = ia_css_program_manifest_get_cell_type_ID(manifest); + + has_fixed_cell = ((cell_id != VIED_NCI_N_CELL_ID) && + (cell_type_id == VIED_NCI_N_CELL_TYPE_ID)); + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_has_program_manifest_fixed_cell invalid argument\n"); + } + return has_fixed_cell; +} + +size_t ia_css_program_manifest_get_size( + const ia_css_program_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_size invalid argument\n"); + } + + return size; +} + +ia_css_program_ID_t ia_css_program_manifest_get_program_ID( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_ID_t program_id = IA_CSS_PROGRAM_INVALID_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_ID(): enter:\n"); + + if (manifest != NULL) { + program_id = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_ID invalid argument\n"); + } + return program_id; +} + +int ia_css_program_manifest_set_program_ID( + ia_css_program_manifest_t *manifest, + ia_css_program_ID_t id) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_program_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = id; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_program_ID failed (%i)\n", ret); + } + return ret; +} + +ia_css_program_group_manifest_t *ia_css_program_manifest_get_parent( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_group_manifest_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_parent(): enter:\n"); + + verifexit(manifest != NULL); + + base = (char *)((char *)manifest + manifest->parent_offset); + + parent = (ia_css_program_group_manifest_t *) (base); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_parent invalid argument\n"); + } + return parent; +} + +int ia_css_program_manifest_set_parent_offset( + ia_css_program_manifest_t *manifest, + int32_t program_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_parent_offset(): enter:\n"); + + verifexit(manifest != NULL); + + /* parent is at negative offset away from current program offset*/ + manifest->parent_offset = -program_offset; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_parent_offset failed (%i)\n", + retval); + } + return retval; +} + +ia_css_program_type_t ia_css_program_manifest_get_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type = IA_CSS_N_PROGRAM_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_type(): enter:\n"); + + if (manifest != NULL) { + program_type = manifest->program_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_type invalid argument\n"); + } + return program_type; +} + +int ia_css_program_manifest_set_type( + ia_css_program_manifest_t *manifest, + const ia_css_program_type_t program_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_type(): enter:\n"); + + if (manifest != NULL) { + manifest->program_type = program_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_type failed (%i)\n", retval); + } + return retval; +} + +ia_css_kernel_bitmap_t ia_css_program_manifest_get_kernel_bitmap( + const ia_css_program_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t kernel_bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + kernel_bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_kernel_bitmap invalid argument\n"); + } + return kernel_bitmap; +} + +int ia_css_program_manifest_set_kernel_bitmap( + ia_css_program_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = kernel_bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_kernel_bitmap failed (%i)\n", + retval); + } + return retval; +} + +vied_nci_cell_ID_t ia_css_program_manifest_get_cell_ID( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_cell_ID_t cell_id = VIED_NCI_N_CELL_ID; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cell_ID(): enter:\n"); + + verifexit(manifest != NULL); + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + cell_id = manifest->cell_id; +#else + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + assert(VIED_NCI_N_CELL_ID == manifest->cells[i]); +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } + cell_id = manifest->cells[0]; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cell_ID invalid argument\n"); + } + return cell_id; +} + +int ia_css_program_manifest_set_cell_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_ID_t cell_id) +{ + int retval = -1; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cell_ID(): enter:\n"); + if (manifest != NULL) { +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = cell_id; +#else + manifest->cells[0] = cell_id; + for (i = 1; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + manifest->cells[i] = VIED_NCI_N_CELL_ID; + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_cell_ID failed (%i)\n", retval); + } + return retval; +} + +vied_nci_cell_type_ID_t ia_css_program_manifest_get_cell_type_ID( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_cell_type_ID_t cell_type_id = VIED_NCI_N_CELL_TYPE_ID; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cell_type_ID(): enter:\n"); + + verifexit(manifest != NULL); + + cell_type_id = (vied_nci_cell_type_ID_t)(manifest->cell_type_id); +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cell_type_ID invalid argument\n"); + } + return cell_type_id; +} + +int ia_css_program_manifest_set_cell_type_ID( + ia_css_program_manifest_t *manifest, + const vied_nci_cell_type_ID_t cell_type_id) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cell_type_ID(): enter:\n"); + if (manifest != NULL) { + manifest->cell_type_id = cell_type_id; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_cell_type_ID failed (%i)\n", + retval); + } + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_int_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t int_mem_size = 0; + vied_nci_cell_type_ID_t cell_type_id; + int mem_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_int_mem_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_MEM_TYPE_ID); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + + /* loop over vied_nci_cell_mem_type to verify mem_type_id for a + * specific cell_type_id + */ + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; mem_index++) { + if ((int)mem_type_id == + (int)vied_nci_cell_type_get_mem_type( + cell_type_id, mem_index)) { + int_mem_size = manifest->int_mem_size[mem_index]; + } + } + +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_int_mem_size invalid argument\n"); + } + return int_mem_size; +} + +int ia_css_program_manifest_set_cells_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + int array_index = 0; + int bit_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_cells_bitmap(): enter:\n"); + + if (manifest != NULL) { + for (bit_index = 0; bit_index < VIED_NCI_N_CELL_ID; bit_index++) { + if (vied_nci_is_bit_set_in_bitmap(bitmap, bit_index)) { + verifexit(array_index < IA_CSS_PROCESS_MAX_CELLS); +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = (vied_nci_cell_ID_t)bit_index; +#else + manifest->cells[array_index] = (vied_nci_cell_ID_t)bit_index; +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + array_index++; + } + } + for (; array_index < IA_CSS_PROCESS_MAX_CELLS; array_index++) { +#if IA_CSS_PROCESS_MAX_CELLS == 1 + manifest->cell_id = VIED_NCI_N_CELL_ID; +#else + manifest->cells[array_index] = VIED_NCI_N_CELL_ID; +#endif /* IA_CSS_PROCESS_MAX_CELLS */ + } + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_cells_bitmap invalid argument\n"); + } +EXIT: + return retval; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_cells_bitmap( + const ia_css_program_manifest_t *manifest) +{ + vied_nci_resource_bitmap_t bitmap = 0; +#if IA_CSS_PROCESS_MAX_CELLS > 1 + int i = 0; +#endif /* IA_CSS_PROCESS_MAX_CELLS > 1 */ + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_cells_bitmap(): enter:\n"); + + verifexit(manifest != NULL); + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + bitmap = (1 << manifest->cell_id); +#else + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + if (VIED_NCI_N_CELL_ID != manifest->cells[i]) { + bitmap |= (1 << manifest->cells[i]); + } +#ifdef __HIVECC +#pragma hivecc unroll +#endif + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_cells_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_manifest_set_dfm_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dfm_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->dfm_port_bitmap[dfm_type_id] = bitmap; +#else + (void)bitmap; + (void)dfm_type_id; +#endif + retval = 0; + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dfm_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_program_manifest_set_dfm_active_port_bitmap( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const vied_nci_resource_bitmap_t bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dfm_active_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->dfm_active_port_bitmap[dfm_type_id] = bitmap; +#else + (void)bitmap; + (void)dfm_type_id; +#endif + retval = 0; + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dfm_active_port_bitmap invalid argument\n"); + } + return retval; +} + +int ia_css_program_manifest_set_is_dfm_relocatable( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id, + const uint8_t is_relocatable) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_is_dfm_relocatable(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + manifest->is_dfm_relocatable[dfm_type_id] = is_relocatable; +#else + (void)is_relocatable; + (void)dfm_type_id; +#endif + retval = 0; + + EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_is_dfm_relocatable invalid argument\n"); + } + + return retval; +} + +uint8_t ia_css_program_manifest_get_is_dfm_relocatable( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + uint8_t ret = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_is_dfm_relocatable(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + ret = manifest->is_dfm_relocatable[dfm_type_id]; +#else + ret = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_is_dfm_relocatable invalid argument\n"); + } + return ret; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dfm_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + bitmap = manifest->dfm_port_bitmap[dfm_type_id]; +#else + bitmap = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dfm_port_bitmap invalid argument\n"); + } + return bitmap; +} + +vied_nci_resource_bitmap_t ia_css_program_manifest_get_dfm_active_port_bitmap( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_dfm_id_t dfm_type_id) +{ + vied_nci_resource_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dfm_active_port_bitmap(): enter:\n"); + + verifexit(manifest != NULL); +#if (VIED_NCI_N_DEV_DFM_ID > 0) + verifexit(dfm_type_id < VIED_NCI_N_DEV_DFM_ID); + bitmap = manifest->dfm_active_port_bitmap[dfm_type_id]; +#else + bitmap = 0; + (void)dfm_type_id; +#endif +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dfm_active_port_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_program_manifest_set_int_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t int_mem_size) +{ + int retval = -1; + vied_nci_cell_type_ID_t cell_type_id; + int mem_index; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_int_mem_size(): enter:\n"); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + + if (manifest != NULL && mem_type_id < VIED_NCI_N_MEM_TYPE_ID) { + /* loop over vied_nci_cell_mem_type to verify mem_type_id for + * a specific cell_type_id + */ + for (mem_index = 0; mem_index < VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + if ((int)mem_type_id == + (int)vied_nci_cell_type_get_mem_type( + cell_type_id, mem_index)) { + manifest->int_mem_size[mem_index] = + int_mem_size; + retval = 0; + } + } + } + if (retval != 0) { + IA_CSS_TRACE_2(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_int_mem_size cell_type_id %d has no mem_type_id %d\n", + (int)cell_type_id, (int)mem_type_id); + } + + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t ext_mem_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_ext_mem_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + ext_mem_size = manifest->ext_mem_size[mem_type_id]; +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_ext_mem_size invalid argument\n"); + } + return ext_mem_size; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_ext_mem_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id) +{ + vied_nci_resource_size_t ext_mem_offset = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_ext_mem_offset(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID); + + ext_mem_offset = manifest->ext_mem_offset[mem_type_id]; +EXIT: + if (NULL == manifest || mem_type_id >= VIED_NCI_N_DATA_MEM_TYPE_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_ext_mem_offset invalid argument\n"); + } + return ext_mem_offset; +} + +int ia_css_program_manifest_set_ext_mem_size( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_ext_mem_size(): enter:\n"); + + if (manifest != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID) { + manifest->ext_mem_size[mem_type_id] = ext_mem_size; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_ext_mem_size invalid argument\n"); + } + + return retval; +} + +int ia_css_program_manifest_set_ext_mem_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_mem_type_ID_t mem_type_id, + const vied_nci_resource_size_t ext_mem_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_ext_mem_offset(): enter:\n"); + + if (manifest != NULL && mem_type_id < VIED_NCI_N_DATA_MEM_TYPE_ID) { + manifest->ext_mem_offset[mem_type_id] = ext_mem_offset; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_ext_mem_offset invalid argument\n"); + } + + return retval; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_size( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + vied_nci_resource_size_t dev_chn_size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dev_chn_size(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(dev_chn_id < VIED_NCI_N_DEV_CHN_ID); + + dev_chn_size = manifest->dev_chn_size[dev_chn_id]; +EXIT: + if (NULL == manifest || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dev_chn_size invalid argument\n"); + } + return dev_chn_size; +} + +vied_nci_resource_size_t ia_css_program_manifest_get_dev_chn_offset( + const ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id) +{ + vied_nci_resource_size_t dev_chn_offset = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_dev_chn_offset(): enter:\n"); + + verifexit(manifest != NULL); + verifexit(dev_chn_id < VIED_NCI_N_DEV_CHN_ID); + + dev_chn_offset = manifest->dev_chn_offset[dev_chn_id]; +EXIT: + if (NULL == manifest || dev_chn_id >= VIED_NCI_N_DEV_CHN_ID) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_dev_chn_offset invalid argument\n"); + } + return dev_chn_offset; +} + +int ia_css_program_manifest_set_dev_chn_size( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_size) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dev_chn_size(): enter:\n"); + + if (manifest != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + manifest->dev_chn_size[dev_chn_id] = dev_chn_size; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dev_chn_size invalid argument\n"); + } + + return retval; +} + +int ia_css_program_manifest_set_dev_chn_offset( + ia_css_program_manifest_t *manifest, + const vied_nci_dev_chn_ID_t dev_chn_id, + const vied_nci_resource_size_t dev_chn_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_dev_chn_offset(): enter:\n"); + + if (manifest != NULL && dev_chn_id < VIED_NCI_N_DEV_CHN_ID) { + manifest->dev_chn_offset[dev_chn_id] = dev_chn_offset; + retval = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_set_dev_chn_offset invalid argument\n"); + } + + return retval; +} + +uint8_t ia_css_program_manifest_get_program_dependency_count( + const ia_css_program_manifest_t *manifest) +{ + uint8_t program_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_dependency_count(): enter:\n"); + + if (manifest != NULL) { + program_dependency_count = manifest->program_dependency_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_dependency_count invalid argument\n"); + } + return program_dependency_count; +} + +uint8_t ia_css_program_manifest_get_program_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index) +{ + uint8_t program_dependency = IA_CSS_PROGRAM_INVALID_DEPENDENCY; + uint8_t *program_dep_ptr; + uint8_t program_dependency_count; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_program_dependency(): enter:\n"); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + + if (index < program_dependency_count) { + program_dep_ptr = + (uint8_t *)((uint8_t *)manifest + + manifest->program_dependency_offset + + index * sizeof(uint8_t)); + program_dependency = *program_dep_ptr; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_program_dependency invalid argument\n"); + } + return program_dependency; +} + +int ia_css_program_manifest_set_program_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t program_dependency, + const unsigned int index) +{ + int retval = -1; + uint8_t *program_dep_ptr; + uint8_t program_dependency_count; + uint8_t program_count; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_program_dependency(): enter:\n"); + + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + program_count = + ia_css_program_group_manifest_get_program_count( + ia_css_program_manifest_get_parent(manifest)); + + if ((index < program_dependency_count) && + (program_dependency < program_count)) { + program_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->program_dependency_offset + + index*sizeof(uint8_t)); + *program_dep_ptr = program_dependency; + retval = 0; + } + + if (retval != 0) { + IA_CSS_TRACE_3(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_program_dependency(m, %d, %d) failed (%i)\n", + program_dependency, index, retval); + } + return retval; +} + +uint8_t ia_css_program_manifest_get_terminal_dependency_count( + const ia_css_program_manifest_t *manifest) +{ + uint8_t terminal_dependency_count = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_terminal_dependency_count(): enter:\n"); + + if (manifest != NULL) { + terminal_dependency_count = manifest->terminal_dependency_count; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_terminal_dependency_count invalid argument\n"); + } + return terminal_dependency_count; +} + +uint8_t ia_css_program_manifest_get_terminal_dependency( + const ia_css_program_manifest_t *manifest, + const unsigned int index) +{ + uint8_t terminal_dependency = IA_CSS_PROGRAM_INVALID_DEPENDENCY; + uint8_t *terminal_dep_ptr; + uint8_t terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_get_terminal_dependency(): enter:\n"); + + if (index < terminal_dependency_count) { + terminal_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->terminal_dependency_offset + index); + terminal_dependency = *terminal_dep_ptr; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_program_manifest_get_terminal_dependency invalid argument\n"); + } + return terminal_dependency; +} + +int ia_css_program_manifest_set_terminal_dependency( + ia_css_program_manifest_t *manifest, + const uint8_t terminal_dependency, + const unsigned int index) +{ + int retval = -1; + uint8_t *terminal_dep_ptr; + uint8_t terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + uint8_t terminal_count = + ia_css_program_group_manifest_get_terminal_count( + ia_css_program_manifest_get_parent(manifest)); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_program_manifest_set_terminal_dependency(): enter:\n"); + + if ((index < terminal_dependency_count) && + (terminal_dependency < terminal_count)) { + terminal_dep_ptr = (uint8_t *)((uint8_t *)manifest + + manifest->terminal_dependency_offset + index); + *terminal_dep_ptr = terminal_dependency; + retval = 0; + } + + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_set_terminal_dependency failed (%i)\n", + retval); + } + return retval; +} + +bool ia_css_is_program_manifest_subnode_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_subnode_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_PARALLEL_SUB) || + (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUB) || + (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUB); +} + +bool ia_css_is_program_manifest_supernode_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_supernode_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); + +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_PARALLEL_SUPER) || + (program_type == IA_CSS_PROGRAM_TYPE_EXCLUSIVE_SUPER) || + (program_type == IA_CSS_PROGRAM_TYPE_VIRTUAL_SUPER); +} + +bool ia_css_is_program_manifest_singular_program_type( + const ia_css_program_manifest_t *manifest) +{ + ia_css_program_type_t program_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_program_manifest_singular_program_type(): enter:\n"); + + program_type = ia_css_program_manifest_get_type(manifest); + +/* The error return is the limit value, so no need to check on the manifest + * pointer + */ + return (program_type == IA_CSS_PROGRAM_TYPE_SINGULAR); +} + +void ia_css_program_manifest_init( + ia_css_program_manifest_t *blob, + const uint8_t program_dependency_count, + const uint8_t terminal_dependency_count) +{ + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_manifest_init(): enter:\n"); + + /*TODO: add assert*/ + if (!blob) + return; + + blob->ID = 1; + blob->program_dependency_count = program_dependency_count; + blob->terminal_dependency_count = terminal_dependency_count; + blob->program_dependency_offset = sizeof(ia_css_program_manifest_t); + blob->terminal_dependency_offset = blob->program_dependency_offset + + sizeof(uint8_t) * program_dependency_count; + blob->size = + (uint16_t)ia_css_sizeof_program_manifest( + program_dependency_count, + terminal_dependency_count); +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) + +#if defined(_MSC_VER) +/* WA for a visual studio compiler bug, refer to + developercommunity.visualstudio.com/content/problem/209359/ice-with-fpfast-in-156-and-msvc-daily-1413263051-p.html +*/ +#pragma optimize("", off) +#endif + +int ia_css_program_manifest_print( + const ia_css_program_manifest_t *manifest, + void *fid) +{ + int retval = -1; + int i, mem_index, dev_chn_index; + + vied_nci_cell_type_ID_t cell_type_id; + uint8_t program_dependency_count; + uint8_t terminal_dependency_count; + ia_css_kernel_bitmap_t bitmap; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_program_manifest_print(): enter:\n"); + + verifexit(manifest != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "sizeof(manifest) = %d\n", + (int)ia_css_program_manifest_get_size(manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "program ID = %d\n", + (int)ia_css_program_manifest_get_program_ID(manifest)); + + bitmap = ia_css_program_manifest_get_kernel_bitmap(manifest); + verifexit(ia_css_kernel_bitmap_print(bitmap, fid) == 0); + + if (ia_css_has_program_manifest_fixed_cell(manifest)) { + vied_nci_cell_ID_t cell_id = + ia_css_program_manifest_get_cell_ID(manifest); + + cell_type_id = vied_nci_cell_get_type(cell_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "cell(program) = %d\n", + (int)cell_id); + } else { + cell_type_id = + ia_css_program_manifest_get_cell_type_ID(manifest); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "cell type(program) = %d\n", + (int)cell_type_id); + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_MEM_TYPE_ID; + mem_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(internal mem) type = %d\n", + (int)vied_nci_cell_type_get_mem_type(cell_type_id, mem_index)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(internal mem) size = %d\n", + manifest->int_mem_size[mem_index]); + } + + for (mem_index = 0; mem_index < (int)VIED_NCI_N_DATA_MEM_TYPE_ID; + mem_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) type = %d\n", + (int)(vied_nci_mem_type_ID_t)mem_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) size = %d\n", + manifest->ext_mem_size[mem_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(external mem) offset = %d\n", + manifest->ext_mem_offset[mem_index]); + } + + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_CHN_ID; + dev_chn_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) type = %d\n", + (int)dev_chn_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) size = %d\n", + manifest->dev_chn_size[dev_chn_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(device channel) offset = %d\n", + manifest->dev_chn_offset[dev_chn_index]); + } +#if HAS_DFM + for (dev_chn_index = 0; dev_chn_index < (int)VIED_NCI_N_DEV_DFM_ID; + dev_chn_index++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) type = %d\n", + (int)dev_chn_index); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) port_bitmap = %d\n", + manifest->dfm_port_bitmap[dev_chn_index]); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) active_port_bitmap = %d\n", + manifest->dfm_active_port_bitmap[dev_chn_index]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(dfm port) is_dfm_relocatable = %d\n", + manifest->is_dfm_relocatable[dev_chn_index]); + } +#endif + +#if IA_CSS_PROCESS_MAX_CELLS == 1 + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(cells) bitmap = %d\n", + manifest->cell_id); +#else + for (i = 0; i < IA_CSS_PROCESS_MAX_CELLS; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\ttype(cells) bitmap = %d\n", + manifest->cells[i]); + } +#endif /* IA_CSS_PROCESS_MAX_CELLS == 1 */ + program_dependency_count = + ia_css_program_manifest_get_program_dependency_count(manifest); + if (program_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program_dependencies[%d] {};\n", + program_dependency_count); + } else { + uint8_t prog_dep; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "program_dependencies[%d] {\n", + program_dependency_count); + for (i = 0; i < (int)program_dependency_count - 1; i++) { + prog_dep = + ia_css_program_manifest_get_program_dependency( + manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t %4d,\n", prog_dep); + } + prog_dep = + ia_css_program_manifest_get_program_dependency(manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\t %4d }\n", prog_dep); + (void)prog_dep; + } + + terminal_dependency_count = + ia_css_program_manifest_get_terminal_dependency_count(manifest); + if (terminal_dependency_count == 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "terminal_dependencies[%d] {};\n", + terminal_dependency_count); + } else { + uint8_t term_dep; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "terminal_dependencies[%d] {\n", + terminal_dependency_count); + for (i = 0; i < (int)terminal_dependency_count - 1; i++) { + term_dep = + ia_css_program_manifest_get_terminal_dependency( + manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t %4d,\n", term_dep); + } + term_dep = + ia_css_program_manifest_get_terminal_dependency(manifest, i); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\t %4d }\n", term_dep); + (void)term_dep; + } + (void)cell_type_id; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_program_manifest_print failed (%i)\n", retval); + } + return retval; +} + +#if defined(_MSC_VER) +/* WA for a visual studio compiler bug */ +#pragma optimize("", off) +#endif + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c new file mode 100644 index 000000000000..c890b8a71f2c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/psysapi/static/src/ia_css_psys_terminal_manifest.c @@ -0,0 +1,1137 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + + +#include + +/* Data object types on the terminals */ +#include +/* for ia_css_kernel_bitmap_t, ia_css_kernel_bitmap_clear, ia_css_... */ +#include + +#include "ia_css_psys_program_group_private.h" +#include "ia_css_terminal_manifest.h" +#include "ia_css_terminal_manifest_types.h" + +#include +#include +#include +#include "ia_css_psys_static_trace.h" + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) +static const char *terminal_type_strings[IA_CSS_N_TERMINAL_TYPES + 1] = { + "IA_CSS_TERMINAL_TYPE_DATA_IN", + "IA_CSS_TERMINAL_TYPE_DATA_OUT", + "IA_CSS_TERMINAL_TYPE_PARAM_STREAM", + /**< Type 1-5 parameter input */ + "IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN", + /**< Type 1-5 parameter output */ + "IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT", + /**< Represent the new type of terminal for + * the "spatial dependent parameters", when params go in + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN", + /**< Represent the new type of terminal for + * the "spatial dependent parameters", when params go out + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT", + /**< Represent the new type of terminal for + * the explicit slicing, when params go in + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN", + /**< Represent the new type of terminal for + * the explicit slicing, when params go out + */ + "IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT", + /**< State (private data) input */ + "IA_CSS_TERMINAL_TYPE_STATE_IN", + /**< State (private data) output */ + "IA_CSS_TERMINAL_TYPE_STATE_OUT", + "IA_CSS_TERMINAL_TYPE_PROGRAM", + "IA_CSS_TERMINAL_TYPR_PROGRAM_CONTROL_INIT", + "UNDEFINED_TERMINAL_TYPE"}; + +#endif + +bool ia_css_is_terminal_manifest_spatial_parameter_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT)); +} + +bool ia_css_is_terminal_manifest_program_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM); +} + +bool ia_css_is_terminal_manifest_program_control_init_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_program_control_init_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT); +} + + +bool ia_css_is_terminal_manifest_parameter_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + /* will return an error value on error */ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_parameter_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT); +} + +bool ia_css_is_terminal_manifest_data_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + /* will return an error value on error */ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_data_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT)); +} + +bool ia_css_is_terminal_manifest_sliced_terminal( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_is_terminal_manifest_sliced_terminal(): enter:\n"); + + terminal_type = ia_css_terminal_manifest_get_type(manifest); + + return ((terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN) || + (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT)); +} + +size_t ia_css_terminal_manifest_get_size( + const ia_css_terminal_manifest_t *manifest) +{ + size_t size = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_size(): enter:\n"); + + if (manifest != NULL) { + size = manifest->size; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_terminal_manifest_get_size: invalid argument\n"); + } + return size; +} + +ia_css_terminal_type_t ia_css_terminal_manifest_get_type( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_type_t terminal_type = IA_CSS_N_TERMINAL_TYPES; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_type(): enter:\n"); + + if (manifest != NULL) { + terminal_type = manifest->terminal_type; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_terminal_manifest_get_type: invalid argument\n"); + } + return terminal_type; +} + +int ia_css_terminal_manifest_set_type( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_type_t terminal_type) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_type(): enter:\n"); + + if (manifest != NULL) { + manifest->terminal_type = terminal_type; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_type failed (%i)\n", + retval); + } + return retval; +} + +int ia_css_terminal_manifest_set_ID( + ia_css_terminal_manifest_t *manifest, + const ia_css_terminal_ID_t ID) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_ID(): enter:\n"); + + if (manifest != NULL) { + manifest->ID = ID; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_ID failed (%i)\n", + retval); + } + return retval; +} + +ia_css_terminal_ID_t ia_css_terminal_manifest_get_ID( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_terminal_ID_t retval; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_ID(): enter:\n"); + + if (manifest != NULL) { + retval = manifest->ID; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_get_ID failed\n"); + retval = IA_CSS_TERMINAL_INVALID_ID; + } + return retval; +} + +ia_css_program_group_manifest_t *ia_css_terminal_manifest_get_parent( + const ia_css_terminal_manifest_t *manifest) +{ + ia_css_program_group_manifest_t *parent = NULL; + char *base; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_get_parent(): enter:\n"); + + verifexit(manifest != NULL); + + base = (char *)((char *)manifest + manifest->parent_offset); + + parent = (ia_css_program_group_manifest_t *)(base); +EXIT: + return parent; +} + +int ia_css_terminal_manifest_set_parent_offset( + ia_css_terminal_manifest_t *manifest, + int32_t terminal_offset) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_terminal_manifest_set_parent_offset(): enter:\n"); + + verifexit(manifest != NULL); + + /* parent is at negative offset away from current terminal offset*/ + manifest->parent_offset = -terminal_offset; + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_set_parent_offset failed (%i)\n", + retval); + } + return retval; +} + +ia_css_frame_format_bitmap_t +ia_css_data_terminal_manifest_get_frame_format_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_frame_format_bitmap_t bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_frame_format_bitmap(): enter:\n"); + + if (manifest != NULL) { + bitmap = manifest->frame_format_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_frame_format_bitmap invalid argument\n"); + } + return bitmap; +} + +int ia_css_data_terminal_manifest_set_frame_format_bitmap( + ia_css_data_terminal_manifest_t *manifest, + ia_css_frame_format_bitmap_t bitmap) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_frame_format_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->frame_format_bitmap = bitmap; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_frame_format_bitmap failed (%i)\n", + ret); + } + + return ret; +} + +bool ia_css_data_terminal_manifest_can_support_compression( + const ia_css_data_terminal_manifest_t *manifest) +{ + bool compression_support = false; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_compression_support(): enter:\n"); + + if (manifest != NULL) { + /* compression_support is used boolean encoded in uint8_t. + * So we only need to check + * if this is non-zero + */ + compression_support = (manifest->compression_support != 0); + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_can_support_compression invalid argument\n"); + } + + return compression_support; +} + +int ia_css_data_terminal_manifest_set_compression_support( + ia_css_data_terminal_manifest_t *manifest, + bool compression_support) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_compression_support(): enter:\n"); + + if (manifest != NULL) { + manifest->compression_support = + (compression_support == true) ? 1 : 0; + ret = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_compression_support failed (%i)\n", + ret); + } + + return ret; +} + +ia_css_connection_bitmap_t ia_css_data_terminal_manifest_get_connection_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_connection_bitmap_t connection_bitmap = 0; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_connection_bitmap(): enter:\n"); + + if (manifest != NULL) { + connection_bitmap = manifest->connection_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_connection_bitmap invalid argument\n"); + } + return connection_bitmap; +} + +int ia_css_data_terminal_manifest_set_connection_bitmap( + ia_css_data_terminal_manifest_t *manifest, ia_css_connection_bitmap_t bitmap) +{ + int ret = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_connection_bitmap(): enter:\n"); + + if (manifest != NULL) { + assert(bitmap != 0); /* zero means there is no connection, this is invalid. */ + assert((bitmap >> IA_CSS_N_CONNECTION_TYPES) == 0); + + manifest->connection_bitmap = bitmap; + ret = 0; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_set_connection_bitmap invalid argument\n"); + } + return ret; +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) +ia_css_kernel_bitmap_t ia_css_data_terminal_manifest_get_kernel_bitmap( + const ia_css_data_terminal_manifest_t *manifest) +{ + ia_css_kernel_bitmap_t kernel_bitmap = ia_css_kernel_bitmap_clear(); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + kernel_bitmap = manifest->kernel_bitmap; + } else { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "ia_css_data_terminal_manifest_get_kernel_bitmap: invalid argument\n"); + } + return kernel_bitmap; +} + +int ia_css_data_terminal_manifest_set_kernel_bitmap( + ia_css_data_terminal_manifest_t *manifest, + const ia_css_kernel_bitmap_t kernel_bitmap) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_kernel_bitmap(): enter:\n"); + + if (manifest != NULL) { + manifest->kernel_bitmap = kernel_bitmap; + retval = 0; + } else { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_kernel_bitmap: failed (%i)\n", + retval); + } + + return retval; +} + +int ia_css_data_terminal_manifest_set_kernel_bitmap_unique( + ia_css_data_terminal_manifest_t *manifest, + const unsigned int index) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_kernel_bitmap_unique(): enter:\n"); + + if (manifest != NULL) { + ia_css_kernel_bitmap_t kernel_bitmap = + ia_css_kernel_bitmap_clear(); + + kernel_bitmap = ia_css_kernel_bitmap_set(kernel_bitmap, index); + verifexit(!ia_css_is_kernel_bitmap_empty(kernel_bitmap)); + verifexit(ia_css_data_terminal_manifest_set_kernel_bitmap( + manifest, kernel_bitmap) == 0); + retval = 0; + } + +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_kernel_bitmap_unique failed (%i)\n", + retval); + } + return retval; +} +#endif + +int ia_css_data_terminal_manifest_set_min_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_min_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->min_size[IA_CSS_COL_DIMENSION] = + min_size[IA_CSS_COL_DIMENSION]; + manifest->min_size[IA_CSS_ROW_DIMENSION] = + min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_min_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_max_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_max_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->max_size[IA_CSS_COL_DIMENSION] = + max_size[IA_CSS_COL_DIMENSION]; + manifest->max_size[IA_CSS_ROW_DIMENSION] = + max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_max_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_min_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_min_size(): enter:\n"); + + verifexit(manifest != NULL); + + min_size[IA_CSS_COL_DIMENSION] = + manifest->min_size[IA_CSS_COL_DIMENSION]; + min_size[IA_CSS_ROW_DIMENSION] = + manifest->min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_min_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_max_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_max_size(): enter:\n"); + + verifexit(manifest != NULL); + + max_size[IA_CSS_COL_DIMENSION] = + manifest->max_size[IA_CSS_COL_DIMENSION]; + max_size[IA_CSS_ROW_DIMENSION] = + manifest->max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_max_size: invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_min_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_min_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->min_fragment_size[IA_CSS_COL_DIMENSION] = + min_size[IA_CSS_COL_DIMENSION]; + manifest->min_fragment_size[IA_CSS_ROW_DIMENSION] = + min_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_min_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_set_max_fragment_size( + ia_css_data_terminal_manifest_t *manifest, + const uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_set_max_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + manifest->max_fragment_size[IA_CSS_COL_DIMENSION] = + max_size[IA_CSS_COL_DIMENSION]; + manifest->max_fragment_size[IA_CSS_ROW_DIMENSION] = + max_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_set_max_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_min_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t min_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_min_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + min_size[IA_CSS_COL_DIMENSION] = + manifest->min_fragment_size[IA_CSS_COL_DIMENSION]; + min_size[IA_CSS_ROW_DIMENSION] = + manifest->min_fragment_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_min_fragment_size invalid argument\n"); + } + return retval; +} + +int ia_css_data_terminal_manifest_get_max_fragment_size( + const ia_css_data_terminal_manifest_t *manifest, + uint16_t max_size[IA_CSS_N_DATA_DIMENSION]) +{ + int retval = -1; + + IA_CSS_TRACE_0(PSYSAPI_STATIC, VERBOSE, + "ia_css_data_terminal_manifest_get_max_fragment_size(): enter:\n"); + + verifexit(manifest != NULL); + + max_size[IA_CSS_COL_DIMENSION] = + manifest->max_fragment_size[IA_CSS_COL_DIMENSION]; + max_size[IA_CSS_ROW_DIMENSION] = + manifest->max_fragment_size[IA_CSS_ROW_DIMENSION]; + retval = 0; + +EXIT: + if (NULL == manifest) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, ERROR, + "ia_css_data_terminal_manifest_get_max_fragment_size invalid argument\n"); + } + return retval; +} + +/* We need to refactor those files in order to build in the firmware only + what is needed, switches are put current to workaround compilation problems + in the firmware (for example lack of uint64_t support) + supported in the firmware + */ +#if !defined(__HIVECC) + +#define PRINT_DIMENSION(name, var) IA_CSS_TRACE_3(PSYSAPI_STATIC, \ + INFO, "%s:\t%d %d\n", \ + (name), \ + (var)[IA_CSS_COL_DIMENSION], \ + (var)[IA_CSS_ROW_DIMENSION]) + +int ia_css_terminal_manifest_print( + const ia_css_terminal_manifest_t *manifest, + void *fid) +{ + int retval = -1; + ia_css_terminal_type_t terminal_type = + ia_css_terminal_manifest_get_type(manifest); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, + "ia_css_terminal_manifest_print(): enter:\n"); + + verifexit(manifest != NULL); + NOT_USED(fid); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "sizeof(manifest) = %d\n", + (int)ia_css_terminal_manifest_get_size(manifest)); + + PRINT("typeof(manifest) = %s\n", terminal_type_strings[terminal_type]); + + if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT) { + ia_css_param_terminal_manifest_t *pterminal_manifest = + (ia_css_param_terminal_manifest_t *)manifest; + uint16_t section_count = + pterminal_manifest->param_manifest_section_desc_count; + int i; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sections(manifest) = %d\n", (int)section_count); + for (i = 0; i < section_count; i++) { + const ia_css_param_manifest_section_desc_t *manifest = + ia_css_param_terminal_manifest_get_prm_sct_desc( + pterminal_manifest, i); + verifjmpexit(manifest != NULL); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_id = %d\n", (int)manifest->kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "mem_type_id = %d\n", + (int)manifest->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_mem_size = %d\n", + (int)manifest->max_mem_size); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "region_id = %d\n", + (int)manifest->region_id); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT) { + ia_css_sliced_param_terminal_manifest_t + *sliced_terminal_manifest = + (ia_css_sliced_param_terminal_manifest_t *)manifest; + uint32_t kernel_id; + uint16_t section_count; + uint16_t section_idx; + + kernel_id = sliced_terminal_manifest->kernel_id; + section_count = + sliced_terminal_manifest->sliced_param_section_count; + + NOT_USED(kernel_id); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_id = %d\n", (int)kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "section_count = %d\n", (int)section_count); + + for (section_idx = 0; section_idx < section_count; + section_idx++) { + ia_css_sliced_param_manifest_section_desc_t + *sliced_param_manifest_section_desc; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "section %d\n", (int)section_idx); + sliced_param_manifest_section_desc = + ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + sliced_terminal_manifest, section_idx); + verifjmpexit(sliced_param_manifest_section_desc != + NULL); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "mem_type_id = %d\n", + (int)sliced_param_manifest_section_desc->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "region_id = %d\n", + (int)sliced_param_manifest_section_desc->region_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_mem_size = %d\n", + (int)sliced_param_manifest_section_desc->max_mem_size); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM) { + ia_css_program_terminal_manifest_t *program_terminal_manifest = + (ia_css_program_terminal_manifest_t *)manifest; + uint32_t sequencer_info_kernel_id; + uint16_t max_kernel_fragment_sequencer_command_desc; + uint16_t kernel_fragment_sequencer_info_manifest_info_count; + uint16_t seq_info_idx; + + sequencer_info_kernel_id = + program_terminal_manifest->sequencer_info_kernel_id; + max_kernel_fragment_sequencer_command_desc = + program_terminal_manifest-> + max_kernel_fragment_sequencer_command_desc; + kernel_fragment_sequencer_info_manifest_info_count = + program_terminal_manifest-> + kernel_fragment_sequencer_info_manifest_info_count; + + NOT_USED(sequencer_info_kernel_id); + NOT_USED(max_kernel_fragment_sequencer_command_desc); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sequencer_info_kernel_id = %d\n", + (int)sequencer_info_kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "max_kernel_fragment_sequencer_command_desc = %d\n", + (int)max_kernel_fragment_sequencer_command_desc); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "kernel_fragment_sequencer_info_manifest_info_count = %d\n", + (int) + kernel_fragment_sequencer_info_manifest_info_count); + + for (seq_info_idx = 0; seq_info_idx < + kernel_fragment_sequencer_info_manifest_info_count; + seq_info_idx++) { + ia_css_kernel_fragment_sequencer_info_manifest_desc_t + *sequencer_info_manifest_desc; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "sequencer info %d\n", (int)seq_info_idx); + sequencer_info_manifest_desc = + ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc + (program_terminal_manifest, seq_info_idx); + verifjmpexit(sequencer_info_manifest_desc != NULL); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_slice_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_slice_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_slice_count[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_slice_count[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_count[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_slice_count[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_point_decimation_factor[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_point_decimation_factor[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_point_decimation_factor[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_overlay_on_pixel_topleft_index[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_overlay_on_pixel_topleft_index[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_topleft_index[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "min_fragment_grid_overlay_on_pixel_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + min_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + IA_CSS_TRACE_2(PSYSAPI_STATIC, INFO, + "max_fragment_grid_overlay_on_pixel_dimension[] = {%d, %d}\n", + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_COL_DIMENSION], + (int)sequencer_info_manifest_desc-> + max_fragment_grid_overlay_pixel_dimension[ + IA_CSS_ROW_DIMENSION]); + } + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PROGRAM_CONTROL_INIT) { + ia_css_program_control_init_terminal_manifest_t *progctrlinit_man = + (ia_css_program_control_init_terminal_manifest_t *)manifest; + ia_css_program_control_init_terminal_manifest_print(progctrlinit_man); + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_DATA_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_DATA_OUT) { + + ia_css_data_terminal_manifest_t *dterminal_manifest = + (ia_css_data_terminal_manifest_t *)manifest; + int i; + + NOT_USED(dterminal_manifest); + + verifexit(ia_css_kernel_bitmap_print( + ia_css_data_terminal_manifest_get_kernel_bitmap( + dterminal_manifest), fid) == 0); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "formats(manifest) = %04x\n", + (int)ia_css_data_terminal_manifest_get_frame_format_bitmap( + dterminal_manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "connection(manifest) = %04x\n", + (int)ia_css_data_terminal_manifest_get_connection_bitmap( + dterminal_manifest)); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "dependent(manifest) = %d\n", + (int)dterminal_manifest->terminal_dependency); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmin_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", dterminal_manifest->min_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", dterminal_manifest->min_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmax_size[%d] = {\n", IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", dterminal_manifest->max_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", dterminal_manifest->max_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmin_fragment_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", + dterminal_manifest->min_fragment_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", + dterminal_manifest->min_fragment_size[i]); + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\tmax_fragment_size[%d] = {\n", + IA_CSS_N_DATA_DIMENSION); + for (i = 0; i < (int)IA_CSS_N_DATA_DIMENSION - 1; i++) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d,\n", + dterminal_manifest->max_fragment_size[i]); + } + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, + "\t\t%4d }\n", + dterminal_manifest->max_fragment_size[i]); + + } else if (terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN || + terminal_type == IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT) { + + ia_css_spatial_param_terminal_manifest_t *stm = + (ia_css_spatial_param_terminal_manifest_t *)manifest; + ia_css_frame_grid_param_manifest_section_desc_t *sec; + int sec_count = + stm->frame_grid_param_manifest_section_desc_count; + ia_css_fragment_grid_manifest_desc_t *fragd = + &stm->common_fragment_grid_desc; + ia_css_frame_grid_manifest_desc_t *framed = + &stm->frame_grid_desc; + int sec_index; + + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "kernel_id:\t\t%d\n", + stm->kernel_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "compute_units_p_elem:\t%d\n", + stm->compute_units_p_elem); + + PRINT_DIMENSION("min_fragment_grid_dimension", + fragd->min_fragment_grid_dimension); + PRINT_DIMENSION("max_fragment_grid_dimension", + fragd->max_fragment_grid_dimension); + PRINT_DIMENSION("min_frame_grid_dimension", + framed->min_frame_grid_dimension); + PRINT_DIMENSION("max_frame_grid_dimension", + framed->max_frame_grid_dimension); + + NOT_USED(framed); + NOT_USED(fragd); + + for (sec_index = 0; sec_index < sec_count; sec_index++) { + sec = ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + stm, sec_index); + verifjmpexit(sec != NULL); + + IA_CSS_TRACE_0(PSYSAPI_STATIC, INFO, "--------------------------\n"); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tmem_type_id:\t%d\n", + sec->mem_type_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tregion_id:\t%d\n", + sec->region_id); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\telem_size:\t%d\n", + sec->elem_size); + IA_CSS_TRACE_1(PSYSAPI_STATIC, INFO, "\tmax_mem_size:\t%d\n", + sec->max_mem_size); + } + } else if (terminal_type < IA_CSS_N_TERMINAL_TYPES) { + IA_CSS_TRACE_0(PSYSAPI_STATIC, WARNING, + "terminal type can not be pretty printed, not supported\n"); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_1(PSYSAPI_STATIC, ERROR, + "ia_css_terminal_manifest_print failed (%i)\n", + retval); + } + return retval; +} + +/* Program control init Terminal */ +unsigned int ia_css_program_control_init_terminal_manifest_get_connect_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog) +{ + assert(prog); + return prog->connect_section_count; +} + + +unsigned int ia_css_program_control_init_terminal_manifest_get_load_section_count( + const ia_css_program_control_init_manifest_program_desc_t *prog) +{ + assert(prog); + return prog->load_section_count; +} + +unsigned int ia_css_program_control_init_terminal_manifest_get_size( + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections) +{ + (void)nof_load_sections; /* might be needed in future */ + (void)nof_connect_sections; /* might be needed in future */ + + return sizeof(ia_css_program_control_init_terminal_manifest_t) + + nof_programs * + sizeof(ia_css_program_control_init_manifest_program_desc_t); +} + +ia_css_program_control_init_manifest_program_desc_t * +ia_css_program_control_init_terminal_manifest_get_program_desc( + const ia_css_program_control_init_terminal_manifest_t *terminal, + unsigned int program) +{ + ia_css_program_control_init_manifest_program_desc_t *progs; + + assert(terminal != NULL); + assert(program < terminal->program_count); + + progs = (ia_css_program_control_init_manifest_program_desc_t *) + ((const char *)terminal + terminal->program_desc_offset); + + return &progs[program]; +} + +int ia_css_program_control_init_terminal_manifest_init( + ia_css_program_control_init_terminal_manifest_t *terminal, + const uint16_t nof_programs, + const uint16_t *nof_load_sections, + const uint16_t *nof_connect_sections) +{ + unsigned int i; + ia_css_program_control_init_manifest_program_desc_t *progs; + + if (terminal == NULL) { + return -EFAULT; + } + + terminal->program_count = nof_programs; + terminal->program_desc_offset = + sizeof(ia_css_program_control_init_terminal_manifest_t); + + progs = ia_css_program_control_init_terminal_manifest_get_program_desc( + terminal, 0); + + for (i = 0; i < nof_programs; i++) { + progs[i].load_section_count = nof_load_sections[i]; + progs[i].connect_section_count = nof_connect_sections[i]; + } + return 0; +} + +void ia_css_program_control_init_terminal_manifest_print( + ia_css_program_control_init_terminal_manifest_t *terminal) +{ + unsigned int i; + + ia_css_program_control_init_manifest_program_desc_t *progs; + + progs = ia_css_program_control_init_terminal_manifest_get_program_desc( + terminal, 0); + + assert(progs); + (void)progs; + + for (i = 0; i < terminal->program_count; i++) { + IA_CSS_TRACE_3(PSYSAPI_STATIC, INFO, + "program index: %d, load sec: %d, connect sec: %d\n", + i, + progs[i].load_section_count, + progs[i].connect_section_count); + } +} + +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.c new file mode 100644 index 000000000000..c51d65c8cb64 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.c @@ -0,0 +1,15 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#include "ia_css_debug_dump.h" + void ia_css_debug_dump(void) {} \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.h new file mode 100644 index 000000000000..5dd23ddbd180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/psys/cnlB0_gen_reg_dump/ia_css_debug_dump.h @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. +* Copyright (c) 2010 - 2018, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +*/ +#ifndef __IA_CSS_DEBUG_DUMP_H_ + #define __IA_CSS_DEBUG_DUMP_H_ + void ia_css_debug_dump(void); + #endif /* __IA_CSS_DEBUG_DUMP_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c new file mode 100644 index 000000000000..9b9161ae78cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/reg_dump/src/reg_dump_generic_bridge.c @@ -0,0 +1,39 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include +#include "ia_css_trace.h" +#ifdef USE_LOGICAL_SSIDS +/* + Logical names can be used to define the SSID + In order to resolve these names the following include file should be provided + and the define above should be enabled +*/ +#include +#endif + +#define REG_DUMP_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#define REG_DUMP_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED + +/* SSID value is defined in test makefiles as either isys0 or psys0 */ +#define REG_DUMP_READ_REGISTER(addr) vied_subsystem_load_32(SSID, addr) + +#define REG_DUMP_PRINT_0(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_0(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define REG_DUMP_PRINT_1(...) \ +EXPAND_VA_ARGS(IA_CSS_TRACE_1(REG_DUMP, VERBOSE, __VA_ARGS__)) +#define EXPAND_VA_ARGS(x) x + +/* Including generated source code for reg_dump */ +#include "ia_css_debug_dump.c" diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/interface/regmem_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/interface/regmem_access.h new file mode 100644 index 000000000000..d4576af936f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/interface/regmem_access.h @@ -0,0 +1,67 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_H +#define __REGMEM_ACCESS_H + +#include "storage_class.h" + +enum regmem_id { + /* pass pkg_dir address to SPC in non-secure mode */ + PKG_DIR_ADDR_REG = 0, + /* pass syscom configuration to SPC */ + SYSCOM_CONFIG_REG = 1, + /* syscom state - modified by SP */ + SYSCOM_STATE_REG = 2, + /* syscom commands - modified by the host */ + SYSCOM_COMMAND_REG = 3, + /* Store interrupt status - updated by SP */ + SYSCOM_IRQ_REG = 4, + /* Store VTL0_ADDR_MASK in trusted secure regision - provided by host.*/ + SYSCOM_VTL0_ADDR_MASK = 5, +#if HAS_DUAL_CMD_CTX_SUPPORT + /* Initialized if trustlet exists - updated by host */ + TRUSTLET_STATUS = 6, + /* identify if SPC access blocker programming is completed - updated by SP */ + AB_SPC_STATUS = 7, + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 8 +#else + /* first syscom queue pointer register */ + SYSCOM_QPR_BASE_REG = 6 +#endif +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* Bit 0: for untrusted non-secure DRV driver on VTL0 + * Bit 1: for trusted secure TEE driver on VTL1 + */ +#define SYSCOM_IRQ_VTL0_MASK 0x1 +#define SYSCOM_IRQ_VTL1_MASK 0x2 +#endif + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_address, unsigned int reg, unsigned int ssid); + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_address, unsigned int reg, unsigned int value, + unsigned int ssid); + +#ifdef __VIED_CELL +#include "regmem_access_cell.h" +#else +#include "regmem_access_host.h" +#endif + +#endif /* __REGMEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/regmem.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/regmem.mk new file mode 100644 index 000000000000..24ebc1c325d8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/regmem.mk @@ -0,0 +1,32 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +ifndef REGMEM_MK +REGMEM_MK=1 + +# MODULE is REGMEM + +REGMEM_DIR=$${MODULES_DIR}/regmem + +REGMEM_INTERFACE=$(REGMEM_DIR)/interface +REGMEM_SOURCES=$(REGMEM_DIR)/src + +REGMEM_HOST_FILES = +REGMEM_FW_FILES = $(REGMEM_SOURCES)/regmem.c + +REGMEM_CPPFLAGS = -I$(REGMEM_INTERFACE) -I$(REGMEM_SOURCES) +REGMEM_HOST_CPPFLAGS = $(REGMEM_CPPFLAGS) +REGMEM_FW_CPPFLAGS = $(REGMEM_CPPFLAGS) + +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_access_host.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_access_host.h new file mode 100644 index 000000000000..8878d7074fab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_access_host.h @@ -0,0 +1,41 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_ACCESS_HOST_H +#define __REGMEM_ACCESS_HOST_H + +#include "regmem_access.h" /* implemented interface */ + +#include "storage_class.h" +#include "regmem_const.h" +#include +#include "ia_css_cmem.h" + +STORAGE_CLASS_INLINE unsigned int +regmem_load_32(unsigned int mem_addr, unsigned int reg, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + return ia_css_cmem_load_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg)); +} + +STORAGE_CLASS_INLINE void +regmem_store_32(unsigned int mem_addr, unsigned int reg, + unsigned int value, unsigned int ssid) +{ + /* No need to add REGMEM_OFFSET, it is already included in mem_addr. */ + ia_css_cmem_store_32(ssid, mem_addr + (REGMEM_WORD_BYTES*reg), + value); +} + +#endif /* __REGMEM_ACCESS_HOST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_const.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_const.h new file mode 100644 index 000000000000..ac7e3a98a434 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/regmem/src/regmem_const.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __REGMEM_CONST_H +#define __REGMEM_CONST_H + +#ifndef REGMEM_SIZE +#define REGMEM_SIZE (16) +#endif /* REGMEM_SIZE */ +#ifndef REGMEM_OFFSET +#define REGMEM_OFFSET (0) +#endif /* REGMEM_OFFSET */ +#ifndef REGMEM_WORD_BYTES +#define REGMEM_WORD_BYTES (4) +#endif + +#endif /* __REGMEM_CONST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h new file mode 100644 index 000000000000..4a04a9890326 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm.h @@ -0,0 +1,173 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_H +#define __IA_CSS_RBM_H + +#include "ia_css_rbm_storage_class.h" +#include + +#define IA_CSS_RBM_BITS 64 +/** An element is a 32 bit unsigned integer. 64 bit integers might cause + * problems in the compiler. + */ +#define IA_CSS_RBM_ELEM_TYPE uint32_t +#define IA_CSS_RBM_ELEM_BITS \ + (sizeof(IA_CSS_RBM_ELEM_TYPE)*8) +#define IA_CSS_RBM_NOF_ELEMS \ + ((IA_CSS_RBM_BITS) / (IA_CSS_RBM_ELEM_BITS)) + +/** Users should make no assumption about the actual type of + * ia_css_rbm_t. + */ +typedef struct { + IA_CSS_RBM_ELEM_TYPE data[IA_CSS_RBM_NOF_ELEMS]; +} ia_css_rbm_elems_t; +typedef ia_css_rbm_elems_t ia_css_rbm_t; + +/** Print the bits of a routing bitmap + * @return < 0 on error + */ +IA_CSS_RBM_STORAGE_CLASS_H +int ia_css_rbm_print( + const ia_css_rbm_t bitmap, + void *fid); + +/** Create an empty routing bitmap + * @return bitmap = 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_clear(void); + +/** Creates the complement of a routing bitmap + * @param bitmap[in] routing bitmap + * @return ~bitmap + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_complement( + const ia_css_rbm_t bitmap); + +/** Create the union of two routing bitmaps + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 | bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_union( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Create the intersection of two routing bitmaps + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 & bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_intersection( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the routing bitmaps is empty + * @param bitmap[in] routing bitmap + * @return bitmap == 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_empty( + const ia_css_rbm_t bitmap); + +/** Check if the intersection of two routing bitmaps is empty + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return (bitmap0 & bitmap1) == 0 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_intersection_empty( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the second routing bitmap is a subset of the first (or equal) + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in routing bitmap 1 + * Note: An empty set is always a subset, this function + * returns true if bitmap 1 is empty + * @return (bitmap0 & bitmap1) == bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_subset( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Check if the routing bitmaps are equal + * @param bitmap0[in] routing bitmap 0 + * @param bitmap1[in] routing bitmap 1 + * @return bitmap0 == bitmap1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +bool ia_css_is_rbm_equal( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1); + +/** Checks whether a specific kernel bit is set + * @return bitmap[index] == 1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +int ia_css_is_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Create the union of a routing bitmap with a onehot bitmap + * with a bit set at index + * @return bitmap[index] |= 1 +*/ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Creates routing bitmap using a uint64 value. + * @return bitmap with the same bits set as in value (provided that width of bitmap is sufficient). + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_create_from_uint64( + const uint64_t value); + +/** Converts an ia_css_rbm_t type to uint64_t. Note that if + * ia_css_rbm_t contains more then 64 bits, only the lowest 64 bits + * are returned. + * @return uint64_t representation of value + */ +IA_CSS_RBM_STORAGE_CLASS_H +uint64_t ia_css_rbm_to_uint64( + const ia_css_rbm_t value); + +/** Creates a routing bitmap with the bit at index 'index' removed. + * @return ~(1 << index) & bitmap + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_unset( + const ia_css_rbm_t bitmap, + const unsigned int index); + +/** Create a onehot routing bitmap with a bit set at index + * @return bitmap[index] = 1 + */ +IA_CSS_RBM_STORAGE_CLASS_H +ia_css_rbm_t ia_css_rbm_bit_mask( + const unsigned int index); + +#ifdef __IA_CSS_RBM_INLINE__ +#include "ia_css_rbm_impl.h" +#endif /* __IA_CSS_RBM_INLINE__ */ + +#endif /* __IA_CSS_RBM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h new file mode 100644 index 000000000000..f497a7de90a9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest.h @@ -0,0 +1,133 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_MANIFEST_H +#define __IA_CSS_RBM_MANIFEST_H + +#include "type_support.h" +#include "ia_css_rbm_manifest_types.h" + +/** Returns the descriptor size of the RBM manifest. + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_size(void); + +/** Initializes the RBM manifest. + * @param rbm[in] Routing bitmap. + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +void +ia_css_rbm_manifest_init(struct ia_css_rbm_manifest_s *rbm); + +/** Returns a pointer to the array of mux descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_mux_desc_t * +ia_css_rbm_manifest_get_muxes(const ia_css_rbm_manifest_t *manifest); + +/** Returns the size of mux descriptors array. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_mux_count(const ia_css_rbm_manifest_t *manifest); + +/** Returns a pointer to the array of validation descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_validation_rule_t * +ia_css_rbm_manifest_get_validation_rules(const ia_css_rbm_manifest_t *manifest); + +/** Returns the size of the validation descriptor array. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_validation_rule_count(const ia_css_rbm_manifest_t *manifest); + +/** Returns a pointer to the array of terminal routing descriptors. + * @param manifest[in] Routing bitmap manifest. + * @return NULL on error + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +ia_css_rbm_terminal_routing_desc_t * +ia_css_rbm_manifest_get_terminal_routing_desc(const ia_css_rbm_manifest_t *manifest); + +/** \brief Returns the size of the terminal routing descriptor array. + * Note: pretty printing differs from on host and on IPU. + * @param manifest[in] Routing bitmap manifest. + * @return size + */ +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H +unsigned int +ia_css_rbm_manifest_get_terminal_routing_desc_count(const ia_css_rbm_manifest_t *manifest); + +/** Pretty prints the routing bitmap manifest. + * @param manifest[in] Routing bitmap manifest. + */ +void +ia_css_rbm_manifest_print(const ia_css_rbm_manifest_t *manifest); + +/** \brief Pretty prints a RBM (routing bitmap). + * Note: pretty printing differs from on host and on IPU. + * @param rbm[in] Routing bitmap. + * @param mux[in] List of mux descriptors corresponding to rbm. + * @param mux_desc_count[in] Number of muxes in list mux. + */ +void +ia_css_rbm_pretty_print( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count); + +/** \brief check for the validity of a routing bitmap. + * @param manifest[in] Routing bitmap manifest. + * @param rbm[in] Routing bitmap + * @return true on match. + */ +bool +ia_css_rbm_manifest_check_rbm_validity( + const ia_css_rbm_manifest_t *manifest, + const ia_css_rbm_t *rbm); + +/** \brief sets, using manifest info, the value of a mux in the routing bitmap. + * @param rbm[in] Routing bitmap. + * @param mux[in] List of mux descriptors corresponding to rbm. + * @param mux_count[in] Number of muxes in list mux. + * @param gp_dev_id[in] ID of sub system (PSA/ISA) where the mux is located. + * @param mux_id[in] ID of mux to set configuration for. + * @param value[in] Value of the mux. + * @return routing bitmap. + */ +ia_css_rbm_t +ia_css_rbm_set_mux( + ia_css_rbm_t rbm, + ia_css_rbm_mux_desc_t *mux, + unsigned int mux_count, + unsigned int gp_dev_id, + unsigned int mux_id, + unsigned int value); + +#ifdef __IA_CSS_RBM_MANIFEST_INLINE__ +#include "ia_css_rbm_manifest_impl.h" +#endif /* __IA_CSS_RBM_MANIFEST_INLINE__ */ + +#endif /* __IA_CSS_RBM_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h new file mode 100644 index 000000000000..ade20446b9f6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_manifest_types.h @@ -0,0 +1,95 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_MANIFEST_TYPES_H +#define __IA_CSS_RBM_MANIFEST_TYPES_H + +#include "ia_css_rbm.h" +#include "vied_nci_psys_resource_model.h" + +#ifndef VIED_NCI_RBM_MAX_MUX_COUNT +#error Please define VIED_NCI_RBM_MAX_MUX_COUNT +#endif +#ifndef VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT +#error Please define VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT +#endif +#ifndef VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT +#error Please define VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT +#endif +#ifndef N_PADDING_UINT8_IN_RBM_MANIFEST +#error Please define N_PADDING_UINT8_IN_RBM_MANIFEST +#endif + +#define SIZE_OF_RBM_MUX_DESC_S ( \ + (4 * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_mux_desc_s { + uint8_t gp_dev_id; + uint8_t mux_id; + uint8_t offset; + uint8_t size_bits; +} ia_css_rbm_mux_desc_t; + +#define SIZE_OF_RBM_VALIDATION_RULE_DESC_S ( \ + (2 * IA_CSS_RBM_BITS) \ + + (1 * IA_CSS_UINT32_T_BITS)) + +typedef struct ia_css_rbm_validation_rule_s { + ia_css_rbm_t match; /* RBM is an array of 32 bit elements */ + ia_css_rbm_t mask; + uint32_t expected_value; +} ia_css_rbm_validation_rule_t; + +#define SIZE_OF_RBM_TERMINAL_ROUTING_DESC_S ( \ + (4 * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_terminal_routing_desc_s { + uint8_t terminal_id; + uint8_t connection_state; + uint8_t mux_id; + uint8_t state; +} ia_css_rbm_terminal_routing_desc_t; + +#define SIZE_OF_RBM_MANIFEST_S ( \ + (VIED_NCI_RBM_MAX_MUX_COUNT * SIZE_OF_RBM_MUX_DESC_S) \ + + (VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT * SIZE_OF_RBM_VALIDATION_RULE_DESC_S) \ + + (VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT * SIZE_OF_RBM_TERMINAL_ROUTING_DESC_S) \ + + (3 * IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_RBM_MANIFEST * IA_CSS_UINT8_T_BITS)) + +typedef struct ia_css_rbm_manifest_s { +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT > 0 + ia_css_rbm_validation_rule_t + validation_rules[VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT]; +#endif + uint16_t mux_desc_count; + uint16_t validation_rule_count; + uint16_t terminal_routing_desc_count; + +#if VIED_NCI_RBM_MAX_MUX_COUNT > 0 + ia_css_rbm_mux_desc_t + mux_desc[VIED_NCI_RBM_MAX_MUX_COUNT]; +#endif + +#if VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT > 0 + ia_css_rbm_terminal_routing_desc_t + terminal_routing_desc[VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT]; +#endif + +#if N_PADDING_UINT8_IN_RBM_MANIFEST > 0 + uint8_t padding[N_PADDING_UINT8_IN_RBM_MANIFEST]; +#endif +} ia_css_rbm_manifest_t; + +#endif /* __IA_CSS_RBM_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h new file mode 100644 index 000000000000..9548e9a9fabb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_storage_class.h @@ -0,0 +1,36 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_STORAGE_CLASS_H +#define __IA_CSS_RBM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __IA_CSS_RBM_INLINE__ +#define IA_CSS_RBM_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_RBM_STORAGE_CLASS_C +#else +#define IA_CSS_RBM_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_RBM_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#ifndef __IA_CSS_RBM_MANIFEST_INLINE__ +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +#else +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_RBM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h new file mode 100644 index 000000000000..dd060323da5c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/interface/ia_css_rbm_trace.h @@ -0,0 +1,77 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_TRACE_H +#define __IA_CSS_RBM_TRACE_H + +#include "ia_css_trace.h" + +/* Not using 0 to identify wrong configuration being passed from the .mk file outside. +* Log levels not in the range below will cause a "No RBM_TRACE_CONFIG Tracing level defined" +*/ +#define RBM_TRACE_LOG_LEVEL_OFF 1 +#define RBM_TRACE_LOG_LEVEL_NORMAL 2 +#define RBM_TRACE_LOG_LEVEL_DEBUG 3 + +#define RBM_TRACE_CONFIG_DEFAULT RBM_TRACE_LOG_LEVEL_NORMAL + +#if !defined(RBM_TRACE_CONFIG) +# define RBM_TRACE_CONFIG RBM_TRACE_CONFIG_DEFAULT +#endif + +/* IPU_RESOURCE Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define RBM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#if (defined(RBM_TRACE_CONFIG)) +/* Module specific trace setting */ +# if RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_OFF +/* RBM_TRACE_LOG_LEVEL_OFF */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +# elif RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_NORMAL +/* RBM_TRACE_LOG_LEVEL_NORMAL */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_DISABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +# elif RBM_TRACE_CONFIG == RBM_TRACE_LOG_LEVEL_DEBUG +/* RBM_TRACE_LOG_LEVEL_DEBUG */ +# define RBM_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_DEBUG IA_CSS_TRACE_LEVEL_ENABLED +# define RBM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +# else +# error "No RBM_TRACE_CONFIG Tracing level defined" +# endif +#else +# error "RBM_TRACE_CONFIG not defined" +#endif + +#endif /* __RBM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk new file mode 100644 index 000000000000..f4251f9740fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/routing_bitmap.mk @@ -0,0 +1,39 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# + +ifdef _H_ROUTING_BITMAP_MK +$(error ERROR: routing_bitmap.mk included multiple times, please check makefile) +else +_H_ROUTING_BITMAP_MK=1 +endif + +ROUTING_BITMAP_FILES += $(ROUTING_BITMAP_DIR)/src/ia_css_rbm_manifest.c + +ROUTING_BITMAP_DIR = $(MODULES_DIR)/routing_bitmap +ROUTING_BITMAP_INTERFACE = $(ROUTING_BITMAP_DIR)/interface +ROUTING_BITMAP_SOURCES = $(ROUTING_BITMAP_DIR)/src + +ROUTING_BITMAP_CPPFLAGS = -I$(ROUTING_BITMAP_INTERFACE) +ROUTING_BITMAP_CPPFLAGS += -I$(ROUTING_BITMAP_SOURCES) + +ifeq ($(ROUTING_BITMAP_INLINE),1) +ROUTING_BITMAP_CPPFLAGS += -D__IA_CSS_RBM_INLINE__ +else +ROUTING_BITMAP_FILES += $(ROUTING_BITMAP_DIR)/src/ia_css_rbm.c +endif + +ifeq ($(ROUTING_BITMAP_MANIFEST_INLINE),1) +ROUTING_BITMAP_CPPFLAGS += -D__IA_CSS_RBM_MANIFEST_INLINE__ +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c new file mode 100644 index 000000000000..bc5bf14efbd7 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm.c @@ -0,0 +1,17 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_RBM_INLINE__ +#include "ia_css_rbm_impl.h" +#endif /* __IA_CSS_RBM_INLINE__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h new file mode 100644 index 000000000000..c8cd78d416a1 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_impl.h @@ -0,0 +1,338 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm.h" +#include "type_support.h" +#include "misc_support.h" +#include "assert_support.h" +#include "math_support.h" +#include "ia_css_rbm_trace.h" + +STORAGE_CLASS_INLINE int ia_css_rbm_compute_weight( + const ia_css_rbm_t bitmap); + +STORAGE_CLASS_INLINE ia_css_rbm_t ia_css_rbm_shift( + const ia_css_rbm_t bitmap); + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_intersection_empty( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + ia_css_rbm_t intersection; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_intersection_empty(): enter:\n"); + + intersection = ia_css_rbm_intersection(bitmap0, bitmap1); + return ia_css_is_rbm_empty(intersection); +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_empty( + const ia_css_rbm_t bitmap) +{ + unsigned int i; + bool is_empty = true; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_empty(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + is_empty &= bitmap.data[i] == 0; + } + return is_empty; +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_equal( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + bool is_equal = true; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_equal(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + is_equal = is_equal && (bitmap0.data[i] == bitmap1.data[i]); + } + return is_equal; +} + +IA_CSS_RBM_STORAGE_CLASS_C +bool ia_css_is_rbm_subset( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + ia_css_rbm_t intersection; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_subset(): enter:\n"); + + intersection = ia_css_rbm_intersection(bitmap0, bitmap1); + return ia_css_is_rbm_equal(intersection, bitmap1); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_clear(void) +{ + unsigned int i; + ia_css_rbm_t bitmap; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_clear(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + bitmap.data[i] = 0; + } + return bitmap; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_complement( + const ia_css_rbm_t bitmap) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_complement(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = ~bitmap.data[i]; + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_union( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_union(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] | bitmap1.data[i]); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_intersection( + const ia_css_rbm_t bitmap0, + const ia_css_rbm_t bitmap1) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_intersection(): enter:\n"); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + result.data[i] = (bitmap0.data[i] & bitmap1.data[i]); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + ia_css_rbm_t bit_mask; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_set(): enter:\n"); + + bit_mask = ia_css_rbm_bit_mask(index); + return ia_css_rbm_union(bitmap, bit_mask); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_create_from_uint64( + const uint64_t value) +{ + unsigned int i; + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_create_from_uint64(): enter:\n"); + + result = ia_css_rbm_clear(); + for (i = 0; i < IA_CSS_RBM_NOF_ELEMS; i++) { + /* masking is done implictly, the MSB bits of casting will be chopped off */ + result.data[i] = (IA_CSS_RBM_ELEM_TYPE) + (value >> (i * IA_CSS_RBM_ELEM_BITS)); + } + return result; +} + +IA_CSS_RBM_STORAGE_CLASS_C +uint64_t ia_css_rbm_to_uint64( + const ia_css_rbm_t value) +{ + const unsigned int bits64 = sizeof(uint64_t) * 8; + const unsigned int nof_elems_bits64 = bits64 / IA_CSS_RBM_ELEM_BITS; + unsigned int i; + uint64_t res = 0; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_to_uint64(): enter:\n"); + + assert((bits64 % IA_CSS_RBM_ELEM_BITS) == 0); + assert(nof_elems_bits64 > 0); + + for (i = 0; i < MIN(IA_CSS_RBM_NOF_ELEMS, nof_elems_bits64); i++) { + res |= ((uint64_t)(value.data[i]) << (i * IA_CSS_RBM_ELEM_BITS)); + } + for (i = nof_elems_bits64; i < IA_CSS_RBM_NOF_ELEMS; i++) { + assert(value.data[i] == 0); + } + return res; +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_unset( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + ia_css_rbm_t result; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_unset(): enter:\n"); + + result = ia_css_rbm_bit_mask(index); + result = ia_css_rbm_complement(result); + return ia_css_rbm_intersection(bitmap, result); +} + +IA_CSS_RBM_STORAGE_CLASS_C +ia_css_rbm_t ia_css_rbm_bit_mask( + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + ia_css_rbm_t bit_mask = ia_css_rbm_clear(); + + assert(index < IA_CSS_RBM_BITS); + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_bit_mask(): enter:\n"); + if (index < IA_CSS_RBM_BITS) { + elem_index = index / IA_CSS_RBM_ELEM_BITS; + elem_bit_index = index % IA_CSS_RBM_ELEM_BITS; + assert(elem_index < IA_CSS_RBM_NOF_ELEMS); + + bit_mask.data[elem_index] = 1 << elem_bit_index; + } + return bit_mask; +} + +STORAGE_CLASS_INLINE +int ia_css_rbm_compute_weight( + const ia_css_rbm_t bitmap) +{ + ia_css_rbm_t loc_bitmap; + int weight = 0; + int i; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_compute_weight(): enter:\n"); + + loc_bitmap = bitmap; + + /* In fact; do not need the iterator "i" */ + for (i = 0; (i < IA_CSS_RBM_BITS) && + !ia_css_is_rbm_empty(loc_bitmap); i++) { + weight += ia_css_is_rbm_set(loc_bitmap, 0); + loc_bitmap = ia_css_rbm_shift(loc_bitmap); + } + + return weight; +} + +IA_CSS_RBM_STORAGE_CLASS_C +int ia_css_is_rbm_set( + const ia_css_rbm_t bitmap, + const unsigned int index) +{ + unsigned int elem_index; + unsigned int elem_bit_index; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_is_rbm_set(): enter:\n"); + + assert(index < IA_CSS_RBM_BITS); + + elem_index = index / IA_CSS_RBM_ELEM_BITS; + elem_bit_index = index % IA_CSS_RBM_ELEM_BITS; + assert(elem_index < IA_CSS_RBM_NOF_ELEMS); + return (((bitmap.data[elem_index] >> elem_bit_index) & 0x1) == 1); +} + +STORAGE_CLASS_INLINE +ia_css_rbm_t ia_css_rbm_shift( + const ia_css_rbm_t bitmap) +{ + int i; + unsigned int lsb_current_elem = 0; + unsigned int lsb_previous_elem = 0; + ia_css_rbm_t loc_bitmap; + + IA_CSS_TRACE_0(RBM, VERBOSE, + "ia_css_rbm_shift(): enter:\n"); + + loc_bitmap = bitmap; + + for (i = IA_CSS_RBM_NOF_ELEMS - 1; i >= 0; i--) { + lsb_current_elem = bitmap.data[i] & 0x01; + loc_bitmap.data[i] >>= 1; + loc_bitmap.data[i] |= (lsb_previous_elem << (IA_CSS_RBM_ELEM_BITS - 1)); + lsb_previous_elem = lsb_current_elem; + } + return loc_bitmap; +} + +IA_CSS_RBM_STORAGE_CLASS_C +int ia_css_rbm_print( + const ia_css_rbm_t bitmap, + void *fid) +{ + int retval = -1; + int bit; + unsigned int bit_index = 0; + ia_css_rbm_t loc_bitmap; + + IA_CSS_TRACE_0(RBM, INFO, + "ia_css_rbm_print(): enter:\n"); + + NOT_USED(fid); + NOT_USED(bit); + + IA_CSS_TRACE_0(RBM, INFO, "kernel bitmap {\n"); + + loc_bitmap = bitmap; + + for (bit_index = 0; (bit_index < IA_CSS_RBM_BITS) && + !ia_css_is_rbm_empty(loc_bitmap); bit_index++) { + + bit = ia_css_is_rbm_set(loc_bitmap, 0); + loc_bitmap = ia_css_rbm_shift(loc_bitmap); + IA_CSS_TRACE_2(RBM, INFO, "\t%d\t = %d\n", bit_index, bit); + } + IA_CSS_TRACE_0(RBM, INFO, "}\n"); + + retval = 0; + return retval; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c new file mode 100644 index 000000000000..ef3beb8760b6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest.c @@ -0,0 +1,224 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm_manifest.h" +#include "ia_css_rbm.h" +#include "type_support.h" +#include "misc_support.h" +#include "assert_support.h" +#include "math_support.h" +#include "ia_css_rbm_trace.h" + +#ifndef __IA_CSS_RBM_MANIFEST_INLINE__ +#include "ia_css_rbm_manifest_impl.h" +#endif /* __IA_CSS_RBM_MANIFEST_INLINE__ */ + +STORAGE_CLASS_INLINE void +ia_css_rbm_print_with_header( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count, + bool print_header) +{ +#ifdef __HIVECC + ia_css_rbm_print(*rbm, NULL); + (void)print_header; + (void)mux_desc_count; + (void)mux; +#else + int i, j; + + assert(mux != NULL); + assert(rbm != NULL); + if (mux == NULL || rbm == NULL) + return; + + if (print_header) { + for (i = mux_desc_count - 1; i >= 0; i--) { + PRINT("%*d|", mux[i].size_bits, mux[i].mux_id); + } + PRINT("\n"); + } + for (i = mux_desc_count - 1; i >= 0; i--) { + for (j = mux[i].size_bits - 1; j >= 0; j--) { + PRINT("%d", ia_css_is_rbm_set(*rbm, j + mux[i].offset)); + } + PRINT("|"); + } +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_rbm_validation_rule_print( + ia_css_rbm_validation_rule_t *rule, + ia_css_rbm_mux_desc_t *mux_desc, + unsigned int mux_desc_count, + bool print_header) +{ + ia_css_rbm_print_with_header(&rule->match, mux_desc, mux_desc_count, print_header); +#ifdef __HIVECC + IA_CSS_TRACE_0(RBM, INFO, "Mask\n"); +#else + PRINT("\t"); +#endif + ia_css_rbm_print_with_header(&rule->mask, mux_desc, mux_desc_count, false); +#ifdef __HIVECC + IA_CSS_TRACE_1(RBM, INFO, "Rule expected_value: %d\n", rule->expected_value); +#else + PRINT("\t%d\n", rule->expected_value); +#endif +} + +void +ia_css_rbm_pretty_print( + const ia_css_rbm_t *rbm, + const ia_css_rbm_mux_desc_t *mux, + unsigned int mux_desc_count) +{ + ia_css_rbm_print_with_header(rbm, mux, mux_desc_count, false); +#ifndef __HIVECC + PRINT("\n"); +#endif +} + +void +ia_css_rbm_manifest_print( + const ia_css_rbm_manifest_t *manifest) +{ + int retval = -1; + unsigned int i; + bool print_header = true; + ia_css_rbm_mux_desc_t *muxes; + ia_css_rbm_validation_rule_t *validation_rule; + ia_css_rbm_terminal_routing_desc_t *terminal_routing_desc; + + verifjmpexit(manifest != NULL); + muxes = ia_css_rbm_manifest_get_muxes(manifest); + verifjmpexit(muxes != NULL || manifest->mux_desc_count == 0); + + for (i = 0; i < manifest->mux_desc_count; i++) { + IA_CSS_TRACE_4(RBM, INFO, "id: %d.%d offstet: %d size_bits: %d\n", + muxes[i].gp_dev_id, + muxes[i].mux_id, + muxes[i].offset, + muxes[i].size_bits); + } +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT != 0 + validation_rule = ia_css_rbm_manifest_get_validation_rules(manifest); + verifjmpexit(validation_rule != NULL || manifest->validation_rule_count == 0); + + for (i = 0; i < manifest->validation_rule_count; i++) { + ia_css_rbm_validation_rule_print(&validation_rule[i], muxes, manifest->mux_desc_count, print_header); + print_header = false; + } +#else + (void) validation_rule; + (void) print_header; +#endif + terminal_routing_desc = ia_css_rbm_manifest_get_terminal_routing_desc(manifest); + verifjmpexit(terminal_routing_desc != NULL || manifest->terminal_routing_desc_count == 0); + for (i = 0; i < manifest->terminal_routing_desc_count; i++) { + IA_CSS_TRACE_4(RBM, INFO, "terminal_id: %d connection_state: %d mux_id: %d state: %d\n", + terminal_routing_desc[i].terminal_id, + terminal_routing_desc[i].connection_state, + terminal_routing_desc[i].mux_id, + terminal_routing_desc[i].state); + } + + retval = 0; +EXIT: + if (retval != 0) { + IA_CSS_TRACE_0(RBM, ERROR, "ia_css_rbm_manifest_print failed\n"); + } +} + +bool +ia_css_rbm_manifest_check_rbm_validity( + const ia_css_rbm_manifest_t *manifest, + const ia_css_rbm_t *rbm) +{ + unsigned int i; + ia_css_rbm_t res; + ia_css_rbm_t final_rbm = ia_css_rbm_clear(); + ia_css_rbm_validation_rule_t *rules; + bool matches_rules; + + verifjmpexit(manifest != NULL); + verifjmpexit(rbm != NULL); + + if (ia_css_is_rbm_empty(*rbm)) { + IA_CSS_TRACE_0(RBM, ERROR, "ia_css_rbm_manifest_check_rbm_validity failes: RBM is empty.\n"); + return false; + } + +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT != 0 + rules = ia_css_rbm_manifest_get_validation_rules(manifest); + verifjmpexit(rules != NULL || manifest->validation_rule_count == 0); + + for (i = 0; i < manifest->validation_rule_count; i++) { + res = ia_css_rbm_intersection(*rbm, rules[i].mask); + matches_rules = ia_css_is_rbm_equal(res, rules[i].match); + + if (!matches_rules) + continue; + + if (rules[i].expected_value == 1) { + final_rbm = ia_css_rbm_union(final_rbm, res); + } else { + IA_CSS_TRACE_1(RBM, INFO, "ia_css_rbm_manifest_check_rbm_validity failes on rule %d\n", 1); + return false; + } + } +#else + (void)matches_rules; + (void)i; + (void)rules; + (void)res; +#endif + return ia_css_is_rbm_equal(final_rbm, *rbm); +EXIT: + return false; +} + +ia_css_rbm_t +ia_css_rbm_set_mux( + ia_css_rbm_t rbm, + ia_css_rbm_mux_desc_t *mux, + unsigned int mux_count, + unsigned int gp_dev_id, + unsigned int mux_id, + unsigned int value) +{ + unsigned int i; + + verifjmpexit(mux != NULL); + + for (i = 0; i < mux_count; i++) { + if (mux[i].gp_dev_id == gp_dev_id && mux[i].mux_id == mux_id) + break; + } + if (i >= mux_count) { + IA_CSS_TRACE_2(RBM, ERROR, + "ia_css_rbm_set_mux mux with mux_id %d.%d not found\n", gp_dev_id, mux_id); + return rbm; + } + if (value >= mux[i].size_bits) { + IA_CSS_TRACE_3(RBM, ERROR, + "ia_css_rbm_set_mux mux mux_id %d.%d, value %d illegal\n", gp_dev_id, mux_id, value); + return rbm; + } + rbm = ia_css_rbm_set(rbm, mux[i].offset + value); +EXIT: + return rbm; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h new file mode 100644 index 000000000000..7059b6bc898e --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/routing_bitmap/src/ia_css_rbm_manifest_impl.h @@ -0,0 +1,108 @@ + + +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_rbm_manifest.h" +#include "ia_css_rbm_trace.h" + +#include "type_support.h" +#include "math_support.h" +#include "error_support.h" +#include "assert_support.h" +#include "print_support.h" + +STORAGE_CLASS_INLINE +void __ia_css_rbm_manifest_check_struct(void) +{ + COMPILATION_ERROR_IF( + sizeof(ia_css_rbm_manifest_t) != (SIZE_OF_RBM_MANIFEST_S / IA_CSS_UINT8_T_BITS)); + COMPILATION_ERROR_IF( + (sizeof(ia_css_rbm_manifest_t) % 8 /* 64 bit */) != 0); +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_size(void) +{ + unsigned int size = sizeof(struct ia_css_rbm_manifest_s); + + return ceil_mul(size, sizeof(uint64_t)); +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +void +ia_css_rbm_manifest_init(struct ia_css_rbm_manifest_s *rbm) +{ + rbm->mux_desc_count = 0; + rbm->terminal_routing_desc_count = 0; + rbm->validation_rule_count = 0; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_mux_desc_t * +ia_css_rbm_manifest_get_muxes(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_MUX_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_mux_desc_t *)manifest->mux_desc; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_mux_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->mux_desc_count; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_validation_rule_t * +ia_css_rbm_manifest_get_validation_rules(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_VALIDATION_RULE_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_validation_rule_t *)manifest->validation_rules; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_validation_rule_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->validation_rule_count; +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +ia_css_rbm_terminal_routing_desc_t * +ia_css_rbm_manifest_get_terminal_routing_desc(const ia_css_rbm_manifest_t *manifest) +{ +#if VIED_NCI_RBM_MAX_TERMINAL_DESC_COUNT == 0 + (void)manifest; + return NULL; +#else + return (ia_css_rbm_terminal_routing_desc_t *)manifest->terminal_routing_desc; +#endif +} + +IA_CSS_RBM_MANIFEST_STORAGE_CLASS_C +unsigned int +ia_css_rbm_manifest_get_terminal_routing_desc_count(const ia_css_rbm_manifest_t *manifest) +{ + return manifest->terminal_routing_desc_count; +} diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/assert_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/assert_support.h new file mode 100644 index 000000000000..f904a494b53c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/assert_support.h @@ -0,0 +1,197 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ASSERT_SUPPORT_H +#define __ASSERT_SUPPORT_H + +/* This file provides support for run-time assertions + * and compile-time assertions. + * + * Run-time asstions are provided via the following syntax: + * assert(condition) + * Run-time assertions are disabled using the NDEBUG flag. + * + * Compile time assertions are provided via the following syntax: + * COMPILATION_ERROR_IF(condition); + * A compile-time assertion will fail to compile if the condition is false. + * The condition must be constant, such that it can be evaluated + * at compile time. + * + * OP___assert is deprecated. + */ + +#define IA_CSS_ASSERT(expr) assert(expr) + +#ifdef __KLOCWORK__ +/* Klocwork does not see that assert will lead to abortion + * as there is no good way to tell this to KW and the code + * should not depend on assert to function (actually the assert + * could be disabled in a release build) it was decided to + * disable the assert for KW scans (by defining NDEBUG) + */ +#define NDEBUG +#endif /* __KLOCWORK__ */ + +/** + * The following macro can help to test the size of a struct at compile + * time rather than at run-time. It does not work for all compilers; see + * below. + * + * Depending on the value of 'condition', the following macro is expanded to: + * - condition==true: + * an expression containing an array declaration with negative size, + * usually resulting in a compilation error + * - condition==false: + * (void) 1; // C statement with no effect + * + * example: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * SIZE_OF_HOST_SP_QUEUES_STRUCT); + * + * verify that the macro indeed triggers a compilation error with your compiler: + * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != + * (sizeof(struct host_sp_queues)+1) ); + * + * Not all compilers will trigger an error with this macro; + * use a search engine to search for BUILD_BUG_ON to find other methods. + */ +#define COMPILATION_ERROR_IF(condition) \ +((void)sizeof(char[1 - 2*!!(condition)])) + +/* Compile time assertion */ +#ifndef CT_ASSERT +#define CT_ASSERT(cnd) ((void)sizeof(char[(cnd)?1 : -1])) +#endif /* CT_ASSERT */ + +#ifdef NDEBUG + +#define assert(cnd) ((void)0) + +#else + +#include "storage_class.h" + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE +/* Windows kernel mode compilation */ +#include +#define assert(cnd) ASSERT(cnd) +#else +/* Windows usermode compilation */ +#include +#endif + +#elif defined(__HIVECC) + +/* + * target: assert disabled + * sched: assert enabled only when SCHED_DEBUG is defined + * unsched: assert enabled + */ +#if defined(HRT_HW) +#define assert(cnd) ((void)0) +#elif defined(HRT_SCHED) && !defined(DEBUG_SCHED) +#define assert(cnd) ((void)0) +#elif defined(PIPE_GENERATION) +#define assert(cnd) ((void)0) +#else +#include +#define assert(cnd) OP___csim_assert(cnd) +#endif + +#elif defined(__KERNEL__) +#include + +#ifndef KERNEL_ASSERT_TO_BUG +#ifndef KERNEL_ASSERT_TO_BUG_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON +#ifndef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#ifndef KERNEL_ASSERT_UNDEFINED +/* Default */ +#define KERNEL_ASSERT_TO_BUG +#endif /*KERNEL_ASSERT_UNDEFINED*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG +/* TODO: it would be cleaner to use this: + * #define assert(cnd) BUG_ON(cnd) + * but that causes many compiler warnings (==errors) under Android + * because it seems that the BUG_ON() macro is not seen as a check by + * gcc like the BUG() macro is. */ +#define assert(cnd) \ + do { \ + if (!(cnd)) { \ + BUG(); \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_BUG*/ + +#ifdef KERNEL_ASSERT_TO_BUG_ON +#define assert(cnd) BUG_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_BUG_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON +#define assert(cnd) WARN_ON(!(cnd)) +#endif /*KERNEL_ASSERT_TO_WARN_ON*/ + +#ifdef KERNEL_ASSERT_TO_WARN_ON_INF_LOOP +#define assert(cnd) \ + do { \ + int not_cnd = !(cnd); \ + WARN_ON(not_cnd); \ + if (not_cnd) { \ + for (;;) { \ + } \ + } \ + } while (0) +#endif /*KERNEL_ASSERT_TO_WARN_ON_INF_LOOP*/ + +#ifdef KERNEL_ASSERT_UNDEFINED +#include KERNEL_ASSERT_DEFINITION_FILESTRING +#endif /*KERNEL_ASSERT_UNDEFINED*/ + +#elif defined(__FIST__) || defined(__GNUC__) + +#include "assert.h" + +#else /* default is for unknown environments */ +#define assert(cnd) ((void)0) +#endif + +#endif /* NDEBUG */ + +#ifndef PIPE_GENERATION +/* Deprecated OP___assert, this is still used in ~1000 places + * in the code. This will be removed over time. + * The implementation for the pipe generation tool is in see support.isp.h */ +#define OP___assert(cnd) assert(cnd) + +#ifdef C_RUN +#define compile_time_assert(cond) OP___assert(cond) +#else +#include "storage_class.h" +extern void _compile_time_assert(void); +STORAGE_CLASS_INLINE void compile_time_assert(unsigned cond) +{ + /* Call undefined function if cond is false */ + if (!cond) + _compile_time_assert(); +} +#endif +#endif /* PIPE_GENERATION */ + +#endif /* __ASSERT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/cpu_mem_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/cpu_mem_support.h new file mode 100644 index 000000000000..fa349cac4b24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/cpu_mem_support.h @@ -0,0 +1,233 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __CPU_MEM_SUPPORT_H +#define __CPU_MEM_SUPPORT_H + +#include "storage_class.h" +#include "assert_support.h" +#include "type_support.h" + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_copy(void *dst, const void *src, unsigned int size) +{ + /* memcpy cannot be used in in Windows (function is not allowed), + * and the safer function memcpy_s is not available on other platforms. + * Because usage of ia_css_cpu_mem_copy is minimal, we implement it here in an easy, + * but sub-optimal way. + */ + unsigned int i; + + assert(dst != NULL && src != NULL); + + if (!(dst != NULL && src != NULL)) { + return NULL; + } + for (i = 0; i < size; i++) { + ((char *)dst)[i] = ((char *)src)[i]; + } + return dst; +} + +#if defined(__KERNEL__) + +#include +#include +#include +#include + +/* TODO: remove, workaround for issue in hrt file ibuf_ctrl_2600_config.c + * error checking code added to SDK that uses calls to exit function + */ +#define exit(a) return + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + return ia_css_cpu_mem_alloc(size); /* todo: align to page size */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_protect(void *ptr, unsigned int size, int prot) +{ + /* nothing here yet */ +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); /* available in kernel in linux/string.h */ +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + kfree(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* parameter check here */ + if (ptr == NULL) + return; + + clflush_cache_range(ptr, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* for now same as flush */ + ia_css_cpu_mem_cache_flush(ptr, size); +} + +#elif defined(_MSC_VER) + +#include +#include +#include + +extern void *hrt_malloc(size_t bytes, int zero_mem); +extern void *hrt_free(void *ptr); +extern void hrt_mem_cache_flush(void *ptr, unsigned int size); +extern void hrt_mem_cache_invalidate(void *ptr, unsigned int size); + +#define malloc(a) hrt_malloc(a, 1) +#define free(a) hrt_free(a) + +#define CSS_PAGE_SIZE (1<<12) + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + unsigned int buffer_size = size; + + /* Currently hrt_malloc calls Windows ExAllocatePoolWithTag() routine + * to request system memory. If the number of bytes is equal or bigger + * than the page size, then the returned address is page aligned, + * but if it's smaller it's not necessarily page-aligned We agreed + * with Windows team that we allocate a full page + * if it's less than page size + */ + if (buffer_size < CSS_PAGE_SIZE) + buffer_size = CSS_PAGE_SIZE; + + return ia_css_cpu_mem_alloc(buffer_size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_flush(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ +#ifdef _KERNEL_MODE + hrt_mem_cache_invalidate(ptr, size); +#else + (void)ptr; + (void)size; +#endif +} + +#else + +#include +#include +#include +/* Needed for the MPROTECT */ +#include +#include +#include +#include + + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc(unsigned int size) +{ + return malloc(size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_alloc_page_aligned(unsigned int size) +{ + int pagesize; + + pagesize = sysconf(_SC_PAGE_SIZE); + return memalign(pagesize, size); +} + +STORAGE_CLASS_INLINE void* +ia_css_cpu_mem_set_zero(void *dst, unsigned int size) +{ + return memset(dst, 0, size); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_free(void *ptr) +{ + free(ptr); +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_flush(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +STORAGE_CLASS_INLINE void +ia_css_cpu_mem_cache_invalidate(void *ptr, unsigned int size) +{ + /* not needed in simulation */ + (void)ptr; + (void)size; +} + +#endif + +#endif /* __CPU_MEM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/error_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/error_support.h new file mode 100644 index 000000000000..9fe1f65125e6 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/error_support.h @@ -0,0 +1,110 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __ERROR_SUPPORT_H +#define __ERROR_SUPPORT_H + +#if defined(__KERNEL__) +#include +#else +#include +#endif +#include + +/* OS-independent definition of IA_CSS errno values */ +/* #define IA_CSS_EINVAL 1 */ +/* #define IA_CSS_EFAULT 2 */ + +#ifdef __HIVECC +#define ERR_EMBEDDED 1 +#else +#define ERR_EMBEDDED 0 +#endif + +#if ERR_EMBEDDED +#define DECLARE_ERRVAL +#else +#define DECLARE_ERRVAL \ + int _errval = 0; +#endif + +/* Use "owl" in while to prevent compiler warnings in Windows */ +#define ALWAYS_FALSE ((void)0, 0) + +#define verifret(cond, error_type) \ +do { \ + if (!(cond)) { \ + return error_type; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmp(cond, error_tag) \ +do { \ + if (!(cond)) { \ + goto error_tag; \ + } \ +} while (ALWAYS_FALSE) + +#define verifexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#if ERR_EMBEDDED +#define verifexitval(cond, error_tag) \ +do { \ + assert(cond); \ +} while (ALWAYS_FALSE) +#else +#define verifexitval(cond, error_tag) \ +do { \ + if (!(cond)) { \ + _errval = (error_tag); \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) +#endif + +#if ERR_EMBEDDED +#define haserror(error_tag) (0) +#else +#define haserror(error_tag) \ + (_errval == (error_tag)) +#endif + +#if ERR_EMBEDDED +#define noerror() (1) +#else +#define noerror() \ + (_errval == 0) +#endif + +#define verifjmpexit(cond) \ +do { \ + if (!(cond)) { \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#define verifjmpexitsetretval(cond, retval) \ +do { \ + if (!(cond)) { \ + retval = -1; \ + goto EXIT; \ + } \ +} while (ALWAYS_FALSE) + +#endif /* __ERROR_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/math_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/math_support.h new file mode 100644 index 000000000000..633f86f1a1b0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/math_support.h @@ -0,0 +1,314 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MATH_SUPPORT_H +#define __MATH_SUPPORT_H + +#include "storage_class.h" /* for STORAGE_CLASS_INLINE */ +#include "type_support.h" +#include "assert_support.h" + +/* in case we have min/max/MIN/MAX macro's undefine them */ +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +#ifdef MIN /* also defined in include/hrt/numeric.h from SDK */ +#undef MIN +#endif +#ifdef MAX +#undef MAX +#endif + +#ifndef UINT16_MAX +#define UINT16_MAX (0xffffUL) +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffffUL) +#endif + +#define IS_ODD(a) ((a) & 0x1) +#define IS_EVEN(a) (!IS_ODD(a)) +#define IS_POWER2(a) (!((a)&((a)-1))) +#define IS_MASK_BITS_SET(a, b) ((a & b) != 0) + +/*To Find next power of 2 number from x */ +#define bit2(x) ((x) | ((x) >> 1)) +#define bit4(x) (bit2(x) | (bit2(x) >> 2)) +#define bit8(x) (bit4(x) | (bit4(x) >> 4)) +#define bit16(x) (bit8(x) | (bit8(x) >> 8)) +#define bit32(x) (bit16(x) | (bit16(x) >> 16)) +#define NEXT_POWER_OF_2(x) (bit32(x-1) + 1) + +/* force a value to a lower even value */ +#define EVEN_FLOOR(x) ((x) & ~1UL) + +/* A => B */ +#define IMPLIES(a, b) (!(a) || (b)) + +/* The ORIG_BITS th bit is the sign bit */ +/* Sign extends a ORIG_BITS bits long signed number to a 64-bit signed number */ +/* By type casting it can relimited to any valid type-size + * (32-bit signed or 16-bit or 8-bit) + */ +/* By masking it can be transformed to any arbitrary bit size */ +#define SIGN_EXTEND(VAL, ORIG_BITS) \ +((~(((VAL)&(1ULL<<((ORIG_BITS)-1)))-1))|(VAL)) + +#define EXTRACT_BIT(a, b) ((a >> b) & 1) + +/* for preprocessor and array sizing use MIN and MAX + otherwise use min and max */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CLIP(a, b, c) MIN((MAX((a), (b))), (c)) +/* Integer round-down division of a with b */ +#define FLOOR_DIV(a, b) ((b) ? ((a) / (b)) : 0) +/* Align a to the lower multiple of b */ +#define FLOOR_MUL(a, b) (FLOOR_DIV(a, b) * (b)) +/* Integer round-up division of a with b */ +#define CEIL_DIV(a, b) ((b) ? (((a) + (b) - 1) / (b)) : 0) +/* Align a to the upper multiple of b */ +#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) +/* Align a to the upper multiple of b - fast implementation + * for cases when b=pow(2,n) + */ +#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) +/* integer round-up division of a with pow(2,b) */ +#define CEIL_SHIFT(a, b) (((a) + (1UL << (b)) - 1) >> (b)) +/* Align a to the upper multiple of pow(2,b) */ +#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) +/* Absolute difference of a and b */ +#define ABS_DIF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define ABS(a) ABS_DIF(a, 0) +/* Square of x */ +#define SQR(x) ((x)*(x)) +/* Integer round-half-down division of a nad b */ +#define ROUND_HALF_DOWN_DIV(a, b) ((b) ? ((a) + (b / 2) - 1) / (b) : 0) +/* Align a to the round-half-down multiple of b */ +#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) + +#define MAX3(a, b, c) MAX((a), MAX((b), (c))) +#define MIN3(a, b, c) MIN((a), MIN((b), (c))) +#define MAX4(a, b, c, d) MAX((MAX((a), (b))), (MAX((c), (d)))) +#define MIN4(a, b, c, d) MIN((MIN((a), (b))), (MIN((c), (d)))) + +/* min and max should not be macros as they will evaluate their arguments twice. + if you really need a macro (e.g. for CPP or for initializing an array) + use MIN() and MAX(), otherwise use min() and max() */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a)))) +#endif + +#ifndef BYTES +#define BYTES(bit) (((bit)+7)/8) +#endif + +#if !defined(PIPE_GENERATION) +STORAGE_CLASS_INLINE unsigned int max_value_bits(unsigned int bits) +{ + return (bits == 0) ? 0 : ((2 * ((1 << ((bits) - 1)) - 1)) + 1); +} +STORAGE_CLASS_INLINE unsigned int max_value_bytes(unsigned int bytes) +{ + return max_value_bits(IA_CSS_UINT8_T_BITS * bytes); +} +STORAGE_CLASS_INLINE int max(int a, int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE int min(int a, int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE int clip(int a, int b, int c) +{ + return min(max(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int umax(unsigned int a, unsigned int b) +{ + return MAX(a, b); +} + +STORAGE_CLASS_INLINE unsigned int umin(unsigned int a, unsigned int b) +{ + return MIN(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uclip(unsigned int a, unsigned int b, + unsigned int c) +{ + return umin(umax(a, b), c); +} + +STORAGE_CLASS_INLINE unsigned int ceil_div(unsigned int a, unsigned int b) +{ + return CEIL_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul(unsigned int a, unsigned int b) +{ + return CEIL_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_mul2(unsigned int a, unsigned int b) +{ + return CEIL_MUL2(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_shift_mul(unsigned int a, unsigned int b) +{ + return CEIL_SHIFT_MUL(a, b); +} + +STORAGE_CLASS_INLINE int abs_dif(int a, int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int uabs_dif(unsigned int a, unsigned int b) +{ + return ABS_DIF(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_div(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_DIV(a, b); +} + +STORAGE_CLASS_INLINE unsigned int round_half_down_mul(unsigned int a, + unsigned int b) +{ + return ROUND_HALF_DOWN_MUL(a, b); +} + +STORAGE_CLASS_INLINE unsigned int ceil_pow2(uint32_t a) +{ + unsigned int retval = 0; + + if (IS_POWER2(a)) { + retval = (unsigned int)a; + } else { + unsigned int v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + retval = (unsigned int)(v+1); + } + return retval; +} + +STORAGE_CLASS_INLINE unsigned int floor_log2(uint32_t a) +{ + static const uint8_t de_bruijn[] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + uint32_t v = a; + + v |= v>>1; + v |= v>>2; + v |= v>>4; + v |= v>>8; + v |= v>>16; + return (unsigned int)de_bruijn[(v*0x07C4ACDDU)>>27]; +} + +/* Divide by small power of two */ +STORAGE_CLASS_INLINE unsigned int +udiv2_small_i(uint32_t a, uint32_t b) +{ + assert(b <= 2); + return a >> (b-1); +} + +/* optimized divide for small results + * a will be divided by b + * outbits is the number of bits needed for the result + * the smaller the cheaper the function will be. + * if the result doesn't fit in the number of output bits + * the result is incorrect and the function will assert + */ +STORAGE_CLASS_INLINE unsigned int +udiv_medium(uint32_t a, uint32_t b, unsigned outbits) +{ + int bit; + unsigned res = 0; + unsigned mask; + +#ifdef VOLCANO +#pragma ipu unroll +#endif + for (bit = outbits-1 ; bit >= 0; bit--) { + mask = 1<= (b<= c ? a+b-c : a+b); +} + +/* + * For SP and ISP, SDK provides the definition of OP_asp_slor. + * We need it only for host + */ +STORAGE_CLASS_INLINE unsigned int OP_asp_slor(int a, int b, int c) +{ + return ((a << c) | b); +} +#else +#include "hive/customops.h" +#endif /* !defined(__VIED_CELL) */ + +#endif /* !defined(PIPE_GENERATION) */ + +#if !defined(__KERNEL__) +#define clamp(a, min_val, max_val) MIN(MAX((a), (min_val)), (max_val)) +#endif /* !defined(__KERNEL__) */ + +#endif /* __MATH_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/misc_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/misc_support.h new file mode 100644 index 000000000000..a2c2729e946d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/misc_support.h @@ -0,0 +1,76 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __MISC_SUPPORT_H +#define __MISC_SUPPORT_H + +/* suppress compiler warnings on unused variables */ +#ifndef NOT_USED +#define NOT_USED(a) ((void)(a)) +#endif + +/* Calculate the total bytes for pow(2) byte alignment */ +#define tot_bytes_for_pow2_align(pow2, cur_bytes) \ + ((cur_bytes + (pow2 - 1)) & ~(pow2 - 1)) + +/* Display the macro value given a string */ +#define _STR(x) #x +#define STR(x) _STR(x) + +/* Concatenate */ +#ifndef CAT /* also defined in */ +#define _CAT(a, b) a ## b +#define CAT(a, b) _CAT(a, b) +#endif + +#define _CAT3(a, b, c) a ## b ## c +#define CAT3(a, b, c) _CAT3(a, b, c) + +/* NO_HOIST, NO_CSE, NO_ALIAS attributes must be ignored for host code */ +#ifndef __HIVECC +#ifndef NO_HOIST +#define NO_HOIST +#endif +#ifndef NO_CSE +#define NO_CSE +#endif +#ifndef NO_ALIAS +#define NO_ALIAS +#endif +#endif + +enum hive_method_id { + HIVE_METHOD_ID_CRUN, + HIVE_METHOD_ID_UNSCHED, + HIVE_METHOD_ID_SCHED, + HIVE_METHOD_ID_TARGET +}; + +/* Derive METHOD */ +#if defined(C_RUN) + #define HIVE_METHOD "crun" + #define HIVE_METHOD_ID HIVE_METHOD_ID_CRUN +#elif defined(HRT_UNSCHED) + #define HIVE_METHOD "unsched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_UNSCHED +#elif defined(HRT_SCHED) + #define HIVE_METHOD "sched" + #define HIVE_METHOD_ID HIVE_METHOD_ID_SCHED +#else + #define HIVE_METHOD "target" + #define HIVE_METHOD_ID HIVE_METHOD_ID_TARGET + #define HRT_TARGET 1 +#endif + +#endif /* __MISC_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/platform_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/platform_support.h new file mode 100644 index 000000000000..1752efc7b4df --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/platform_support.h @@ -0,0 +1,146 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PLATFORM_SUPPORT_H +#define __PLATFORM_SUPPORT_H + +#include "storage_class.h" + +#define MSEC_IN_SEC 1000 +#define NSEC_IN_MSEC 1000000 + +#if defined(_MSC_VER) +#include + +#define IA_CSS_EXTERN +#define SYNC_WITH(x) +#define CSS_ALIGN(d, a) _declspec(align(a)) d + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + /* Placeholder for driver team*/ +} + +STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) +{ + /* Placeholder for driver team*/ + (void)delay_time_ms; +} + +#elif defined(__HIVECC) +#include +#include + +#define IA_CSS_EXTERN extern +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + OP___schedule(); +} + +#elif defined(__KERNEL__) +#include +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __aligned(a) + +STORAGE_CLASS_INLINE void ia_css_sleep(void) +{ + usleep_range(1, 50); +} + +#elif defined(__GNUC__) +#include + +#define IA_CSS_EXTERN +#define CSS_ALIGN(d, a) d __attribute__((aligned(a))) + +/* Define some __HIVECC specific macros to nothing to allow host code compilation */ +#ifndef NO_ALIAS +#define NO_ALIAS +#endif + +#ifndef SYNC_WITH +#define SYNC_WITH(x) +#endif + +#if defined(HRT_CSIM) + #include "hrt/host.h" /* Using hrt_sleep from hrt/host.h */ + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + /* For the SDK still using hrt_sleep */ + hrt_sleep(); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + /* For the SDK still using hrt_sleep */ + long unsigned int i = 0; + for (i = 0; i < delay_time_ms; i++) { + hrt_sleep(); + } + } +#else + #include + STORAGE_CLASS_INLINE void ia_css_sleep(void) + { + struct timespec delay_time; + + delay_time.tv_sec = 0; + delay_time.tv_nsec = 10; + nanosleep(&delay_time, NULL); + } + STORAGE_CLASS_INLINE void ia_css_sleep_msec(long unsigned int delay_time_ms) + { + struct timespec delay_time; + + if (delay_time_ms >= MSEC_IN_SEC) { + delay_time.tv_sec = delay_time_ms / MSEC_IN_SEC; + delay_time.tv_nsec = (delay_time_ms % MSEC_IN_SEC) * NSEC_IN_MSEC; + } else { + delay_time.tv_sec = 0; + delay_time.tv_nsec = delay_time_ms * NSEC_IN_MSEC; + } + nanosleep(&delay_time, NULL); + } +#endif + +#else +#include +#endif + +/*needed for the include in stdint.h for various environments */ +#include "type_support.h" +#include "storage_class.h" + +#define MAX_ALIGNMENT 8 +#define aligned_uint8(type, obj) CSS_ALIGN(uint8_t obj, 1) +#define aligned_int8(type, obj) CSS_ALIGN(int8_t obj, 1) +#define aligned_uint16(type, obj) CSS_ALIGN(uint16_t obj, 2) +#define aligned_int16(type, obj) CSS_ALIGN(int16_t obj, 2) +#define aligned_uint32(type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_int32(type, obj) CSS_ALIGN(int32_t obj, 4) + +/* needed as long as hivecc does not define the type (u)int64_t */ +#if defined(__HIVECC) +#define aligned_uint64(type, obj) CSS_ALIGN(unsigned long long obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(signed long long obj, 8) +#else +#define aligned_uint64(type, obj) CSS_ALIGN(uint64_t obj, 8) +#define aligned_int64(type, obj) CSS_ALIGN(int64_t obj, 8) +#endif +#define aligned_enum(enum_type, obj) CSS_ALIGN(uint32_t obj, 4) +#define aligned_struct(struct_type, obj) struct_type obj + +#endif /* __PLATFORM_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/print_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/print_support.h new file mode 100644 index 000000000000..0b614f7ef12d --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/print_support.h @@ -0,0 +1,90 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __PRINT_SUPPORT_H +#define __PRINT_SUPPORT_H + +#if defined(_MSC_VER) +#ifdef _KERNEL_MODE + +/* TODO: Windows driver team to provide tracing mechanism for kernel mode + * e.g. DbgPrint and DbgPrintEx + */ +extern void FwTracePrintPWARN(const char *fmt, ...); +extern void FwTracePrintPRINT(const char *fmt, ...); +extern void FwTracePrintPERROR(const char *fmt, ...); +extern void FwTracePrintPDEBUG(const char *fmt, ...); + +#define PWARN(format, ...) FwTracePrintPWARN(format, __VA_ARGS__) +#define PRINT(format, ...) FwTracePrintPRINT(format, __VA_ARGS__) +#define PERROR(format, ...) FwTracePrintPERROR(format, __VA_ARGS__) +#define PDEBUG(format, ...) FwTracePrintPDEBUG(format, __VA_ARGS__) + +#else +/* Windows usermode compilation */ +#include + +/* To change the defines below, communicate with Windows team first + * to ensure they will not get flooded with prints + */ +/* This is temporary workaround to avoid flooding userspace + * Windows driver with prints + */ + +#define PWARN(format, ...) +#define PRINT(format, ...) +#define PERROR(format, ...) printf("error: " format, __VA_ARGS__) +#define PDEBUG(format, ...) + +#endif /* _KERNEL_MODE */ + +#elif defined(__HIVECC) +#include +/* To be revised + +#define PWARN(format) +#define PRINT(format) OP___printstring(format) +#define PERROR(variable) OP___dump(9999, arguments) +#define PDEBUG(variable) OP___dump(__LINE__, arguments) + +*/ + +#define PRINTSTRING(str) OP___printstring(str) + +#elif defined(__KERNEL__) +#include +#include + + +#define PWARN(format, arguments...) pr_debug(format, ##arguments) +#define PRINT(format, arguments...) pr_debug(format, ##arguments) +#define PERROR(format, arguments...) pr_debug(format, ##arguments) +#define PDEBUG(format, arguments...) pr_debug(format, ##arguments) + +#else +#include + +#define PRINT_HELPER(prefix, format, ...) printf(prefix format "%s", __VA_ARGS__) + +/* The trailing "" allows the edge case of printing single string */ +#define PWARN(...) PRINT_HELPER("warning: ", __VA_ARGS__, "") +#define PRINT(...) PRINT_HELPER("", __VA_ARGS__, "") +#define PERROR(...) PRINT_HELPER("error: ", __VA_ARGS__, "") +#define PDEBUG(...) PRINT_HELPER("debug: ", __VA_ARGS__, "") + +#define PRINTSTRING(str) PRINT(str) + +#endif + +#endif /* __PRINT_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/storage_class.h new file mode 100644 index 000000000000..af19b4026220 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/storage_class.h @@ -0,0 +1,51 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __STORAGE_CLASS_H +#define __STORAGE_CLASS_H + +#define STORAGE_CLASS_EXTERN \ +extern + +#if defined(_MSC_VER) +#define STORAGE_CLASS_INLINE \ +static __inline +#elif defined(__HIVECC) +#define STORAGE_CLASS_INLINE \ +static inline +#else +#define STORAGE_CLASS_INLINE \ +static inline +#endif + +/* Register struct */ +#ifndef __register +#if defined(__HIVECC) && !defined(PIPE_GENERATION) +#define __register register +#else +#define __register +#endif +#endif + +/* Memory attribute */ +#ifndef MEM +#ifdef PIPE_GENERATION +#elif defined(__HIVECC) +#include +#else +#define MEM(any_mem) +#endif +#endif + +#endif /* __STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/type_support.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/type_support.h new file mode 100644 index 000000000000..a86da0e78941 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/support/type_support.h @@ -0,0 +1,80 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __TYPE_SUPPORT_H +#define __TYPE_SUPPORT_H + +/* Per the DLI spec, types are in "type_support.h" and + * "platform_support.h" is for unclassified/to be refactored + * platform specific definitions. + */ +#define IA_CSS_UINT8_T_BITS 8 +#define IA_CSS_UINT16_T_BITS 16 +#define IA_CSS_UINT32_T_BITS 32 +#define IA_CSS_INT32_T_BITS 32 +#define IA_CSS_UINT64_T_BITS 64 + + +#if defined(_MSC_VER) +#include +#include +#include +#include +#if defined(_M_X64) +#define HOST_ADDRESS(x) (unsigned long long)(x) +#else +#define HOST_ADDRESS(x) (unsigned long)(x) +#endif + +#elif defined(PARAM_GENERATION) +/* Nothing */ +#elif defined(__HIVECC) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif defined(__KERNEL__) +#include +#include + +#define CHAR_BIT (8) +#define HOST_ADDRESS(x) (unsigned long)(x) + +#elif defined(__GNUC__) +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#else /* default is for the FIST environment */ +#include +#include +#include +#include +#define HOST_ADDRESS(x) (unsigned long)(x) + +#endif + +#if !defined(PIPE_GENERATION) && !defined(IO_GENERATION) +/* genpipe cannot handle the void* syntax */ +typedef void *HANDLE; +#endif + +#endif /* __TYPE_SUPPORT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h new file mode 100644 index 000000000000..5426d6d18e0b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom.h @@ -0,0 +1,247 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_H +#define __IA_CSS_SYSCOM_H + + +/* + * The CSS Subsystem Communication Interface - Host side + * + * It provides subsystem initialzation, send ports and receive ports + * The PSYS and ISYS interfaces are implemented on top of this interface. + */ + +#include "ia_css_syscom_config.h" + +#define FW_ERROR_INVALID_PARAMETER (-1) +#define FW_ERROR_BAD_ADDRESS (-2) +#define FW_ERROR_BUSY (-3) +#define FW_ERROR_NO_MEMORY (-4) + +struct ia_css_syscom_context; + +/** + * ia_css_syscom_size() - provide syscom external buffer requirements + * @config: pointer to the configuration data (read) + * @size: pointer to the buffer size (write) + * + * Purpose: + * - Provide external buffer requirements + * - To be used for external buffer allocation + * + */ +extern void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size +); + +/** + * ia_css_syscom_open() - initialize a subsystem context + * @config: pointer to the configuration data (read) + * @buf: pointer to externally allocated buffers (read) + * @returns: struct ia_css_syscom_context* on success, 0 otherwise. + * + * Purpose: + * - initialize host side data structures + * - boot the subsystem? + * + */ +extern struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *config, + struct ia_css_syscom_buf *buf +); + +/** + * ia_css_syscom_close() - signal close to cell + * @context: pointer to the subsystem context + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if SPC is not ready yet. + * + * Purpose: + * Request from the Cell to terminate + */ +extern int +ia_css_syscom_close( + struct ia_css_syscom_context *context +); + +/** + * ia_css_syscom_release() - free context + * @context: pointer to the subsystem context + * @force: flag which specifies whether cell + * state will be checked before freeing the + * context. + * @returns: 0 on success, -2 (FW_ERROR_BUSY) if cell + * is busy and call was not forced. + * + * Purpose: + * 2 modes, with first (force==true) immediately + * free context, and second (force==false) verifying + * that the cell state is ok and freeing context if so, + * returning error otherwise. + */ +extern int +ia_css_syscom_release( + struct ia_css_syscom_context *context, + unsigned int force +); + +/** + * Open a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for sending tokens to the subsystem + * @context: pointer to the subsystem context + * @port: send port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be sent to a port without error. + * @context: pointer to the subsystem context + * @port: send port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Send a token to the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: send port index + * @token: pointer to the token value that is transferred to the subsystem + * @returns: number of tokens sent on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + const void *token +); + +/** + * Open a port for receiving tokens to the subsystem + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Closes a port for receiving tokens to the subsystem + * Returns 0 on success, otherwise negative value of error code + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Get the number of tokens that can be received from a port without errors. + * @context: pointer to the subsystem context + * @port: receive port index + * @returns: number of available tokens on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *context, + unsigned int port +); + +/** + * Receive a token from the subsystem port + * The token size is determined during initialization + * @context: pointer to the subsystem context + * @port: receive port index + * @token (output): pointer to (space for) the token to be received + * @returns: number of tokens received on success, + * -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *context, + unsigned int port, + void *token +); + +#if HAS_DUAL_CMD_CTX_SUPPORT +/** + * ia_css_syscom_store_dmem() - store subsystem context information in DMEM + * @context: pointer to the subsystem context + * @ssid: subsystem id + * @vtl0_addr_mask: VTL0 address mask; only applicable when the passed in context is secure + * @returns: 0 on success, -1 (FW_ERROR_INVALID_PARAMETER) otherwise. + */ +extern int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *context, + unsigned int ssid, + unsigned int vtl0_addr_mask +); + +/** + * ia_css_syscom_set_trustlet_status() - store truslet configuration setting + * @context: pointer to the subsystem context + * @trustlet_exist: 1 if trustlet exists + */ +extern void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +); + +/** + * ia_css_syscom_is_ab_spc_ready() - check if SPC access blocker programming is completed + * @context: pointer to the subsystem context + * @returns: 1 when status is ready. 0 otherwise + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +); +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ + +#endif /* __IA_CSS_SYSCOM_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h new file mode 100644 index 000000000000..2f5eb309df94 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_config.h @@ -0,0 +1,97 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_H +#define __IA_CSS_SYSCOM_CONFIG_H + +#include +#include + +/* syscom size struct, output of ia_css_syscom_size, + * input for (external) allocation + */ +struct ia_css_syscom_size { + /* Size of host buffer */ + unsigned int cpu; + /* Size of shared config buffer (host to cell) */ + unsigned int shm; + /* Size of shared input queue buffers (host to cell) */ + unsigned int ibuf; + /* Size of shared output queue buffers (cell to host) */ + unsigned int obuf; +}; + +/* syscom buffer struct, output of (external) allocation, + * input for ia_css_syscom_open + */ +struct ia_css_syscom_buf { + char *cpu; /* host buffer */ + + /* shared memory buffer host address */ + host_virtual_address_t shm_host; + /* shared memory buffer cell address */ + vied_virtual_address_t shm_cell; + + /* input queue shared buffer host address */ + host_virtual_address_t ibuf_host; + /* input queue shared buffer cell address */ + vied_virtual_address_t ibuf_cell; + + /* output queue shared buffer host address */ + host_virtual_address_t obuf_host; + /* output queue shared buffer cell address */ + vied_virtual_address_t obuf_cell; +}; + +struct ia_css_syscom_queue_config { + unsigned int queue_size; /* tokens per queue */ + unsigned int token_size; /* bytes per token */ +}; + +/** + * Parameter struct for ia_css_syscom_open + */ +struct ia_css_syscom_config { + /* This member in no longer used in syscom. + It is kept to not break any driver builds, and will be removed when + all assignments have been removed from driver code */ + /* address of firmware in DDR/IMR */ + unsigned long long host_firmware_address; + + /* address of firmware in DDR, seen from SPC */ + unsigned int vied_firmware_address; + + unsigned int ssid; + unsigned int mmid; + + unsigned int num_input_queues; + unsigned int num_output_queues; + struct ia_css_syscom_queue_config *input; + struct ia_css_syscom_queue_config *output; + + unsigned int regs_addr; + unsigned int dmem_addr; + + /* firmware-specific configuration data */ + void *specific_addr; + unsigned int specific_size; + + /* if true; secure syscom in VTIO Case + * if false, non-secure syscom + */ + bool secure; + unsigned int vtl0_addr_mask; /* only applicable in 'secure' case */ +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h new file mode 100644 index 000000000000..2c32693c2a82 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/interface/ia_css_syscom_trace.h @@ -0,0 +1,51 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __IA_CSS_SYSCOM_TRACE_H +#define __IA_CSS_SYSCOM_TRACE_H + +#include "ia_css_trace.h" + +#define SYSCOM_TRACE_LEVEL_DEFAULT 1 +#define SYSCOM_TRACE_LEVEL_DEBUG 2 + +/* Set to default level if no level is defined */ +#ifndef SYSCOM_TRACE_LEVEL +#define SYSCOM_TRACE_LEVEL SYSCOM_TRACE_LEVEL_DEFAULT +#endif /* SYSCOM_TRACE_LEVEL */ + +/* SYSCOM Module tracing backend is mapped to TUNIT tracing for target platforms */ +#ifdef __HIVECC +# ifndef HRT_CSIM +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_TRACE +# else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +# endif +#else +# define SYSCOM_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE +#endif + +#define SYSCOM_TRACE_LEVEL_INFO IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_WARNING IA_CSS_TRACE_LEVEL_ENABLED +#define SYSCOM_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + +#if (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEFAULT) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_DISABLED +#elif (SYSCOM_TRACE_LEVEL == SYSCOM_TRACE_LEVEL_DEBUG) +# define SYSCOM_TRACE_LEVEL_VERBOSE IA_CSS_TRACE_LEVEL_ENABLED +#else +# error "Connection manager trace level not defined!" +#endif /* SYSCOM_TRACE_LEVEL */ + +#endif /* __IA_CSS_SYSCOM_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom.c new file mode 100644 index 000000000000..cdf9df0531ff --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom.c @@ -0,0 +1,650 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#include "ia_css_syscom.h" + +#include "ia_css_syscom_context.h" +#include "ia_css_syscom_config_fw.h" +#include "ia_css_syscom_trace.h" + +#include "queue.h" +#include "send_port.h" +#include "recv_port.h" +#include "regmem_access.h" + +#include "error_support.h" +#include "cpu_mem_support.h" + +#include "queue_struct.h" +#include "send_port_struct.h" +#include "recv_port_struct.h" + +#include "type_support.h" +#include +#include +#include "platform_support.h" + +#include "ia_css_cell.h" + +/* struct of internal buffer sizes */ +struct ia_css_syscom_size_intern { + unsigned int context; + unsigned int input_queue; + unsigned int output_queue; + unsigned int input_port; + unsigned int output_port; + + unsigned int fw_config; + unsigned int specific; + + unsigned int input_buffer; + unsigned int output_buffer; +}; + +/* Allocate buffers internally, when no buffers are provided */ +static int +ia_css_syscom_alloc( + unsigned int ssid, + unsigned int mmid, + const struct ia_css_syscom_size *size, + struct ia_css_syscom_buf *buf) +{ + /* zero the buffer to set all pointers to zero */ + memset(buf, 0, sizeof(*buf)); + + /* allocate cpu_mem */ + buf->cpu = (char *)ia_css_cpu_mem_alloc(size->cpu); + if (!buf->cpu) + goto EXIT7; + + /* allocate and map shared config buffer */ + buf->shm_host = shared_memory_alloc(mmid, size->shm); + if (!buf->shm_host) + goto EXIT6; + buf->shm_cell = shared_memory_map(ssid, mmid, buf->shm_host); + if (!buf->shm_cell) + goto EXIT5; + + /* allocate and map input queue buffer */ + buf->ibuf_host = shared_memory_alloc(mmid, size->ibuf); + if (!buf->ibuf_host) + goto EXIT4; + buf->ibuf_cell = shared_memory_map(ssid, mmid, buf->ibuf_host); + if (!buf->ibuf_cell) + goto EXIT3; + + /* allocate and map output queue buffer */ + buf->obuf_host = shared_memory_alloc(mmid, size->obuf); + if (!buf->obuf_host) + goto EXIT2; + buf->obuf_cell = shared_memory_map(ssid, mmid, buf->obuf_host); + if (!buf->obuf_cell) + goto EXIT1; + + return 0; + +EXIT1: shared_memory_free(mmid, buf->obuf_host); +EXIT2: shared_memory_unmap(ssid, mmid, buf->ibuf_cell); +EXIT3: shared_memory_free(mmid, buf->ibuf_host); +EXIT4: shared_memory_unmap(ssid, mmid, buf->shm_cell); +EXIT5: shared_memory_free(mmid, buf->shm_host); +EXIT6: ia_css_cpu_mem_free(buf->cpu); +EXIT7: return FW_ERROR_NO_MEMORY; +} + +static void +ia_css_syscom_size_intern( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size_intern *size) +{ + /* convert syscom config into syscom internal size struct */ + + unsigned int i; + + size->context = sizeof(struct ia_css_syscom_context); + size->input_queue = cfg->num_input_queues * sizeof(struct sys_queue); + size->output_queue = cfg->num_output_queues * sizeof(struct sys_queue); + size->input_port = cfg->num_input_queues * sizeof(struct send_port); + size->output_port = cfg->num_output_queues * sizeof(struct recv_port); + + size->fw_config = sizeof(struct ia_css_syscom_config_fw); + size->specific = cfg->specific_size; + + /* accumulate input queue buffer sizes */ + size->input_buffer = 0; + for (i = 0; i < cfg->num_input_queues; i++) { + size->input_buffer += + sys_queue_buf_size(cfg->input[i].queue_size, + cfg->input[i].token_size); + } + + /* accumulate outut queue buffer sizes */ + size->output_buffer = 0; + for (i = 0; i < cfg->num_output_queues; i++) { + size->output_buffer += + sys_queue_buf_size(cfg->output[i].queue_size, + cfg->output[i].token_size); + } +} + +static void +ia_css_syscom_size_extern( + const struct ia_css_syscom_size_intern *i, + struct ia_css_syscom_size *e) +{ + /* convert syscom internal size struct into external size struct */ + + e->cpu = i->context + i->input_queue + i->output_queue + + i->input_port + i->output_port; + e->shm = i->fw_config + i->input_queue + i->output_queue + i->specific; + e->ibuf = i->input_buffer; + e->obuf = i->output_buffer; +} + +/* Function that provides buffer sizes to be allocated */ +void +ia_css_syscom_size( + const struct ia_css_syscom_config *cfg, + struct ia_css_syscom_size *size) +{ + struct ia_css_syscom_size_intern i; + + ia_css_syscom_size_intern(cfg, &i); + ia_css_syscom_size_extern(&i, size); +} + +static struct ia_css_syscom_context* +ia_css_syscom_assign_buf( + const struct ia_css_syscom_size_intern *i, + const struct ia_css_syscom_buf *buf) +{ + struct ia_css_syscom_context *ctx; + char *cpu_mem_buf; + host_virtual_address_t shm_buf_host; + vied_virtual_address_t shm_buf_cell; + + /* host context */ + cpu_mem_buf = buf->cpu; + + ctx = (struct ia_css_syscom_context *)cpu_mem_buf; + ia_css_cpu_mem_set_zero(ctx, i->context); + cpu_mem_buf += i->context; + + ctx->input_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->input_queue; + + ctx->output_queue = (struct sys_queue *) cpu_mem_buf; + cpu_mem_buf += i->output_queue; + + ctx->send_port = (struct send_port *) cpu_mem_buf; + cpu_mem_buf += i->input_port; + + ctx->recv_port = (struct recv_port *) cpu_mem_buf; + + + /* cell config */ + shm_buf_host = buf->shm_host; + shm_buf_cell = buf->shm_cell; + + ctx->config_host_addr = shm_buf_host; + shm_buf_host += i->fw_config; + ctx->config_vied_addr = shm_buf_cell; + shm_buf_cell += i->fw_config; + + ctx->input_queue_host_addr = shm_buf_host; + shm_buf_host += i->input_queue; + ctx->input_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->input_queue; + + ctx->output_queue_host_addr = shm_buf_host; + shm_buf_host += i->output_queue; + ctx->output_queue_vied_addr = shm_buf_cell; + shm_buf_cell += i->output_queue; + + ctx->specific_host_addr = shm_buf_host; + ctx->specific_vied_addr = shm_buf_cell; + + ctx->ibuf_host_addr = buf->ibuf_host; + ctx->ibuf_vied_addr = buf->ibuf_cell; + + ctx->obuf_host_addr = buf->obuf_host; + ctx->obuf_vied_addr = buf->obuf_cell; + + return ctx; +} + +struct ia_css_syscom_context* +ia_css_syscom_open( + struct ia_css_syscom_config *cfg, + struct ia_css_syscom_buf *buf_extern +) +{ + struct ia_css_syscom_size_intern size_intern; + struct ia_css_syscom_size size; + struct ia_css_syscom_buf buf_intern; + struct ia_css_syscom_buf *buf; + struct ia_css_syscom_context *ctx; + struct ia_css_syscom_config_fw fw_cfg; + unsigned int i; + struct sys_queue_res res; + + IA_CSS_TRACE_0(SYSCOM, INFO, "Entered: ia_css_syscom_open\n"); + + /* error handling */ + if (cfg == NULL) + return NULL; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) start\n", cfg->secure); + + /* check members of cfg: TBD */ + + /* + * Check if SP is in valid state, have to wait if not ready. + * In some platform (Such as VP), it will need more time to wait due to system performance; + * If return NULL without wait for SPC0 ready, Driver load FW will failed + */ + ia_css_cell_wait(cfg->ssid, SPC0); + + ia_css_syscom_size_intern(cfg, &size_intern); + ia_css_syscom_size_extern(&size_intern, &size); + + if (buf_extern) { + /* use externally allocated buffers */ + buf = buf_extern; + } else { + /* use internally allocated buffers */ + buf = &buf_intern; + if (ia_css_syscom_alloc(cfg->ssid, cfg->mmid, &size, buf) != 0) + return NULL; + } + + /* assign buffer pointers */ + ctx = ia_css_syscom_assign_buf(&size_intern, buf); + /* only need to free internally allocated buffers */ + ctx->free_buf = !buf_extern; + + ctx->cell_regs_addr = cfg->regs_addr; + /* regmem is at cell_dmem_addr + REGMEM_OFFSET */ + ctx->cell_dmem_addr = cfg->dmem_addr; + + ctx->num_input_queues = cfg->num_input_queues; + ctx->num_output_queues = cfg->num_output_queues; + + ctx->env.mmid = cfg->mmid; + ctx->env.ssid = cfg->ssid; + ctx->env.mem_addr = cfg->dmem_addr; + + ctx->regmem_idx = SYSCOM_QPR_BASE_REG; + + /* initialize input queues */ + res.reg = SYSCOM_QPR_BASE_REG; + res.host_address = ctx->ibuf_host_addr; + res.vied_address = ctx->ibuf_vied_addr; + for (i = 0; i < cfg->num_input_queues; i++) { + sys_queue_init(ctx->input_queue + i, + cfg->input[i].queue_size, + cfg->input[i].token_size, &res); + } + + /* initialize output queues */ + res.host_address = ctx->obuf_host_addr; + res.vied_address = ctx->obuf_vied_addr; + for (i = 0; i < cfg->num_output_queues; i++) { + sys_queue_init(ctx->output_queue + i, + cfg->output[i].queue_size, + cfg->output[i].token_size, &res); + } + + /* fill shared queue structs */ + shared_memory_store(cfg->mmid, ctx->input_queue_host_addr, + ctx->input_queue, + cfg->num_input_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->input_queue_host_addr), + cfg->num_input_queues * sizeof(struct sys_queue)); + shared_memory_store(cfg->mmid, ctx->output_queue_host_addr, + ctx->output_queue, + cfg->num_output_queues * sizeof(struct sys_queue)); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->output_queue_host_addr), + cfg->num_output_queues * sizeof(struct sys_queue)); + + /* Zero the queue buffers. Is this really needed? */ + shared_memory_zero(cfg->mmid, buf->ibuf_host, size.ibuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->ibuf_host), + size.ibuf); + shared_memory_zero(cfg->mmid, buf->obuf_host, size.obuf); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(buf->obuf_host), + size.obuf); + + /* copy firmware specific data */ + if (cfg->specific_addr && cfg->specific_size) { + shared_memory_store(cfg->mmid, ctx->specific_host_addr, + cfg->specific_addr, cfg->specific_size); + ia_css_cpu_mem_cache_flush( + (void *)HOST_ADDRESS(ctx->specific_host_addr), + cfg->specific_size); + } + + fw_cfg.num_input_queues = cfg->num_input_queues; + fw_cfg.num_output_queues = cfg->num_output_queues; + fw_cfg.input_queue = ctx->input_queue_vied_addr; + fw_cfg.output_queue = ctx->output_queue_vied_addr; + fw_cfg.specific_addr = ctx->specific_vied_addr; + fw_cfg.specific_size = cfg->specific_size; + + shared_memory_store(cfg->mmid, ctx->config_host_addr, + &fw_cfg, sizeof(struct ia_css_syscom_config_fw)); + ia_css_cpu_mem_cache_flush((void *)HOST_ADDRESS(ctx->config_host_addr), + sizeof(struct ia_css_syscom_config_fw)); + +#if !HAS_DUAL_CMD_CTX_SUPPORT + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, cfg->ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, cfg->ssid); + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_open store CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, cfg->ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, cfg->ssid); +#endif + + /* Indicate if ctx is created for secure stream purpose */ + ctx->secure = cfg->secure; + + IA_CSS_TRACE_1(SYSCOM, INFO, "ia_css_syscom_open (secure %d) completed\n", cfg->secure); + return ctx; +} + + +int +ia_css_syscom_close( + struct ia_css_syscom_context *ctx +) { + int state; + + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle close request yet */ + return FW_ERROR_BUSY; + } + + /* set close request flag */ + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_INACTIVE, ctx->env.ssid); + + return 0; +} + +static void +ia_css_syscom_free(struct ia_css_syscom_context *ctx) +{ + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->ibuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->ibuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, ctx->obuf_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->obuf_host_addr); + shared_memory_unmap(ctx->env.ssid, ctx->env.mmid, + ctx->config_vied_addr); + shared_memory_free(ctx->env.mmid, ctx->config_host_addr); + ia_css_cpu_mem_free(ctx); +} + +int +ia_css_syscom_release( + struct ia_css_syscom_context *ctx, + unsigned int force +) { + /* check if release is forced, an verify cell state if it is not */ + if (!force) { + if (!ia_css_cell_is_ready(ctx->env.ssid, SPC0)) + return FW_ERROR_BUSY; + } + + /* Reset the regmem idx */ + ctx->regmem_idx = 0; + + if (ctx->free_buf) + ia_css_syscom_free(ctx); + + return 0; +} + +int ia_css_syscom_send_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + send_port_open(ctx->send_port + port, + ctx->input_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_send_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +int ia_css_syscom_send_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_available(ctx->send_port + port); +} + +int ia_css_syscom_send_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + const void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_input_queues, FW_ERROR_INVALID_PARAMETER); + + return send_port_transfer(ctx->send_port + port, token); +} + +int ia_css_syscom_recv_port_open( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + int state; + + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + /* check if SP syscom is ready to open the queue */ + state = regmem_load_32(ctx->cell_dmem_addr, + SYSCOM_STATE_REG, ctx->env.ssid); + if (state != SYSCOM_STATE_READY) { + /* SPC is not ready to handle messages yet */ + return FW_ERROR_BUSY; + } + + /* initialize the port */ + recv_port_open(ctx->recv_port + port, + ctx->output_queue + port, &(ctx->env)); + + return 0; +} + +int ia_css_syscom_recv_port_close( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check parameters */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return 0; +} + +/* + * Get the number of responses in the response queue + */ +int +ia_css_syscom_recv_port_available( + struct ia_css_syscom_context *ctx, + unsigned int port +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_available(ctx->recv_port + port); +} + + +/* + * Dequeue the head of the response queue + * returns an error when the response queue is empty + */ +int +ia_css_syscom_recv_port_transfer( + struct ia_css_syscom_context *ctx, + unsigned int port, + void *token +) +{ + /* check params */ + verifret(ctx != NULL, FW_ERROR_BAD_ADDRESS); + verifret(port < ctx->num_output_queues, FW_ERROR_INVALID_PARAMETER); + + return recv_port_transfer(ctx->recv_port + port, token); +} + +#if HAS_DUAL_CMD_CTX_SUPPORT +/* + * store subsystem context information in DMEM + */ +int +ia_css_syscom_store_dmem( + struct ia_css_syscom_context *ctx, + unsigned int ssid, + unsigned int vtl0_addr_mask +) +{ + unsigned int read_back; + + NOT_USED(vtl0_addr_mask); + NOT_USED(read_back); + + if (ctx->secure) { + /* store VTL0 address mask in 'secure' context */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem VTL0_ADDR_MASK (%#x) @ dmem_addr %#x ssid %d\n", + vtl0_addr_mask, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_VTL0_ADDR_MASK, vtl0_addr_mask, ssid); + } + /* store firmware configuration address */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem CONFIG_REG (%#x) @ dmem_addr %#x ssid %d\n", + ctx->config_vied_addr, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_CONFIG_REG, + ctx->config_vied_addr, ssid); + /* store syscom uninitialized state */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem STATE_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_STATE_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_STATE_REG, + SYSCOM_STATE_UNINIT, ssid); + /* store syscom uninitialized command */ + IA_CSS_TRACE_3(SYSCOM, INFO, "ia_css_syscom_store_dmem COMMAND_REG (%#x) @ dmem_addr %#x ssid %d\n", + SYSCOM_COMMAND_UNINIT, ctx->cell_dmem_addr, ssid); + regmem_store_32(ctx->cell_dmem_addr, SYSCOM_COMMAND_REG, + SYSCOM_COMMAND_UNINIT, ssid); + + return 0; +} + +/* + * store truslet configuration status setting + */ +void +ia_css_syscom_set_trustlet_status( + unsigned int dmem_addr, + unsigned int ssid, + bool trustlet_exist +) +{ + unsigned int value; + + value = trustlet_exist ? TRUSTLET_EXIST : TRUSTLET_NOT_EXIST; + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_set_trustlet_status TRUSTLET_STATUS (%#x) @ dmem_addr %#x ssid %d\n", + value, dmem_addr, ssid); + regmem_store_32(dmem_addr, TRUSTLET_STATUS, value, ssid); +} + +/* + * check if SPC access blocker programming is completed + */ +bool +ia_css_syscom_is_ab_spc_ready( + struct ia_css_syscom_context *ctx +) +{ + unsigned int value; + + /* We only expect the call from non-secure context only */ + if (ctx->secure) { + IA_CSS_TRACE_0(SYSCOM, ERROR, "ia_css_syscom_is_spc_ab_ready - Please call from non-secure context\n"); + return false; + } + + value = regmem_load_32(ctx->cell_dmem_addr, AB_SPC_STATUS, ctx->env.ssid); + IA_CSS_TRACE_3(SYSCOM, INFO, + "ia_css_syscom_is_spc_ab_ready AB_SPC_STATUS @ dmem_addr %#x ssid %d - value %#x\n", + ctx->cell_dmem_addr, ctx->env.ssid, value); + + return (value == AB_SPC_READY); +} +#endif /* HAS_DUAL_CMD_CTX_SUPPORT */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h new file mode 100644 index 000000000000..0cacd5a34934 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_config_fw.h @@ -0,0 +1,69 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONFIG_FW_H +#define __IA_CSS_SYSCOM_CONFIG_FW_H + +#include "type_support.h" + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_STATE_UNINIT = 0x57A7E000, + /* SP Syscom sets this when it is ready for use */ + SYSCOM_STATE_READY = 0x57A7E001, + /* SP Syscom sets this when no more syscom accesses will happen */ + SYSCOM_STATE_INACTIVE = 0x57A7E002 +}; + +enum { + /* Program load or explicit host setting should init to this */ + SYSCOM_COMMAND_UNINIT = 0x57A7F000, + /* Host Syscom requests syscom to become inactive */ + SYSCOM_COMMAND_INACTIVE = 0x57A7F001 +}; + +#if HAS_DUAL_CMD_CTX_SUPPORT +enum { + /* Program load or explicit host setting should init to this */ + TRUSTLET_UNINIT = 0x57A8E000, + /* Host Syscom informs SP that Trustlet exists */ + TRUSTLET_EXIST = 0x57A8E001, + /* Host Syscom informs SP that Trustlet does not exist */ + TRUSTLET_NOT_EXIST = 0x57A8E002 +}; + +enum { + /* Program load or explicit setting initialized by SP */ + AB_SPC_NOT_READY = 0x57A8F000, + /* SP informs host that SPC access programming is completed */ + AB_SPC_READY = 0x57A8F001 +}; +#endif + +/* firmware config: data that sent from the host to SP via DDR */ +/* Cell copies data into a context */ + +struct ia_css_syscom_config_fw { + unsigned int firmware_address; + + unsigned int num_input_queues; + unsigned int num_output_queues; + unsigned int input_queue; /* hmm_ptr / struct queue* */ + unsigned int output_queue; /* hmm_ptr / struct queue* */ + + unsigned int specific_addr; /* vied virtual address */ + unsigned int specific_size; +}; + +#endif /* __IA_CSS_SYSCOM_CONFIG_FW_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h new file mode 100644 index 000000000000..ecf22f6b7ac5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/src/ia_css_syscom_context.h @@ -0,0 +1,65 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_SYSCOM_CONTEXT_H +#define __IA_CSS_SYSCOM_CONTEXT_H + +#include + +#include "port_env_struct.h" +#include + +/* host context */ +struct ia_css_syscom_context { + vied_virtual_address_t cell_firmware_addr; + unsigned int cell_regs_addr; + unsigned int cell_dmem_addr; + + struct port_env env; + + unsigned int num_input_queues; + unsigned int num_output_queues; + + /* array of input queues (from host to SP) */ + struct sys_queue *input_queue; + /* array of output queues (from SP to host) */ + struct sys_queue *output_queue; + + struct send_port *send_port; + struct recv_port *recv_port; + + unsigned int regmem_idx; + unsigned int free_buf; + + host_virtual_address_t config_host_addr; + host_virtual_address_t input_queue_host_addr; + host_virtual_address_t output_queue_host_addr; + host_virtual_address_t specific_host_addr; + host_virtual_address_t ibuf_host_addr; + host_virtual_address_t obuf_host_addr; + + vied_virtual_address_t config_vied_addr; + vied_virtual_address_t input_queue_vied_addr; + vied_virtual_address_t output_queue_vied_addr; + vied_virtual_address_t specific_vied_addr; + vied_virtual_address_t ibuf_vied_addr; + vied_virtual_address_t obuf_vied_addr; + + /* if true; secure syscom object as in VTIO Case + * if false, non-secure syscom + */ + bool secure; +}; + +#endif /* __IA_CSS_SYSCOM_CONTEXT_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/syscom.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/syscom.mk new file mode 100644 index 000000000000..8d36b8928af5 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/syscom/syscom.mk @@ -0,0 +1,42 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is SYSCOM + +SYSCOM_DIR=$${MODULES_DIR}/syscom + +SYSCOM_INTERFACE=$(SYSCOM_DIR)/interface +SYSCOM_SOURCES1=$(SYSCOM_DIR)/src + +SYSCOM_HOST_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom.c + +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_HOST_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_HOST_CPPFLAGS += -I$${MODULES_DIR}/devices +ifdef REGMEM_SECURE_OFFSET +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_HOST_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif + +SYSCOM_FW_FILES += $(SYSCOM_SOURCES1)/ia_css_syscom_fw.c + +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_INTERFACE) +SYSCOM_FW_CPPFLAGS += -I$(SYSCOM_SOURCES1) +SYSCOM_FW_CPPFLAGS += -DREGMEM_OFFSET=$(REGMEM_OFFSET) +ifdef REGMEM_SECURE_OFFSET +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=$(REGMEM_SECURE_OFFSET) +else +SYSCOM_FW_CPPFLAGS += -DREGMEM_SECURE_OFFSET=0 +endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/interface/ia_css_trace.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/interface/ia_css_trace.h new file mode 100644 index 000000000000..b85b1810f107 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/interface/ia_css_trace.h @@ -0,0 +1,883 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +/*! \file */ + +#ifndef __IA_CSS_TRACE_H +#define __IA_CSS_TRACE_H + +/* +** Configurations +*/ + +/** + * STEP 1: Define {Module Name}_TRACE_METHOD to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * + * Example: + * #define NCI_DMA_TRACE_METHOD IA_CSS_TRACE_METHOD_NATIVE + */ + +/**< Use whatever method of tracing that best suits the platform + * this code is compiled for. + */ +#define IA_CSS_TRACE_METHOD_NATIVE 1 +/**< Use the Tracing NCI. */ +#define IA_CSS_TRACE_METHOD_TRACE 2 + +/** + * STEP 2: Define {Module Name}_TRACE_LEVEL_{Level} to one of the following. + * Where: + * {Module Name} is the name of the targeted module. + * {Level}, in decreasing order of severity, is one of the + * following values: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * + * Example: + * #define NCI_DMA_TRACE_LEVEL_ASSERT IA_CSS_TRACE_LEVEL_DISABLED + * #define NCI_DMA_TRACE_LEVEL_ERROR IA_CSS_TRACE_LEVEL_ENABLED + */ +/**< Disables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_DISABLED 0 +/**< Enables the corresponding trace level. */ +#define IA_CSS_TRACE_LEVEL_ENABLED 1 + +/* + * Used in macro definition with do-while loop + * for removing checkpatch warnings + */ +#define IA_CSS_TRACE_FILE_DUMMY_DEFINE + +/** + * STEP 3: Define IA_CSS_TRACE_PRINT_FILE_LINE to have file name and + * line printed with every log message. + * + * Example: + * #define IA_CSS_TRACE_PRINT_FILE_LINE + */ + +/* +** Interface +*/ + +/* +** Static +*/ + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * at compile-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_0(module, severity, format) \ + IA_CSS_TRACE_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_1(module, severity, format, a1) \ + IA_CSS_TRACE_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_IMPL(module, 5, severity, format, a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_6(module, severity, format, a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_IMPL(module, 6, severity, format, a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled at compile-time. + * @see IA_CSS_TRACE_0 + */ +#define IA_CSS_TRACE_7(module, severity, format, a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Dynamic +*/ + +/** +* Declares, but does not define, dynamic tracing functions and variables +* for module \p module. For each module, place an instance of this macro +* in the compilation unit in which you want to use dynamic tracing facility +* so as to inform the compiler of the declaration of the available functions. +* An invocation of this function does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DEFINE +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) +/** +* Declares the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) + +/** +* Defines dynamic tracing functions and variables for module \p module. +* For each module, place an instance of this macro in one, and only one, +* of your SOURCE files so as to allow the linker resolve the related symbols. +* An invocation of this macro does not enable any of the available tracing +* levels. Do not place a semicolon after a call to this macro. +* @see IA_CSS_TRACE_DYNAMIC_DECLARE +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) +/** +* Defines the configuration function for the dynamic api seperatly, if one +* wants to use it. +*/ +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC(module) \ + IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) + +/** + * Logs a message with zero arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @param module The targeted module. + * @param severity The severity level of the trace message. In decreasing order: + * {ASSERT, ERROR, WARNING, INFO, DEBUG, VERBOSE}. + * @param format The message to be traced. + */ +#define IA_CSS_TRACE_DYNAMIC_0(module, severity, format) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 0, severity, format) + +/** + * Logs a message with one argument if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_1(module, severity, format, a1) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 1, severity, format, a1) + +/** + * Logs a message with two arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_2(module, severity, format, a1, a2) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 2, severity, format, a1, a2) + +/** + * Logs a message with three arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_3(module, severity, format, a1, a2, a3) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 3, severity, format, a1, a2, a3) + +/** + * Logs a message with four arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_4(module, severity, format, a1, a2, a3, a4) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 4, severity, format, a1, a2, a3, a4) + +/** + * Logs a message with five arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_5(module, severity, format, a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 5, severity, format, \ + a1, a2, a3, a4, a5) + +/** + * Logs a message with six arguments if the targeted severity level is enabled + * both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_6(module, severity, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 6, severity, format, \ + a1, a2, a3, a4, a5, a6) + +/** + * Logs a message with seven arguments if the targeted severity level + * is enabled both at compile-time, and run-time. + * @see IA_CSS_TRACE_DYNAMIC_0 + */ +#define IA_CSS_TRACE_DYNAMIC_7(module, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_DYNAMIC_IMPL(module, 7, severity, format, \ + a1, a2, a3, a4, a5, a6, a7) + +/* +** Implementation +*/ + +/* CAT */ +#define IA_CSS_TRACE_CAT_IMPL(a, b) a ## b +#define IA_CSS_TRACE_CAT(a, b) IA_CSS_TRACE_CAT_IMPL(a, b) + +/* Bridge */ +#if defined(__HIVECC) || defined(__GNUC__) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, arguments ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + ## arguments \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, \ + arguments ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + ## arguments); \ + } \ + } while (0) +#elif defined(_MSC_VER) +#define IA_CSS_TRACE_IMPL(module, argument_count, severity, ...) \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_, \ + argument_count \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_LEVEL_ \ + ), \ + severity \ + ) \ + ( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_CAT( \ + IA_CSS_TRACE_SEVERITY_, \ + severity \ + ), \ + _ \ + ), \ + IA_CSS_TRACE_CAT( \ + module, \ + _TRACE_METHOD \ + ) \ + ), \ + #module, \ + __VA_ARGS__ \ + ) \ + ) + +/* Bridge */ +#define IA_CSS_TRACE_DYNAMIC_IMPL(module, argument_count, severity, ...) \ + do { \ + if (IA_CSS_TRACE_CAT(IA_CSS_TRACE_CAT(module, _trace_level_), \ + severity)) { \ + IA_CSS_TRACE_IMPL(module, argument_count, severity, \ + __VA_ARGS__); \ + } \ + } while (0) +#endif + +/* +** Native Backend +*/ + +#if defined(__HIVECC) + #define IA_CSS_TRACE_PLATFORM_CELL +#elif defined(__GNUC__) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, \ + format), ## arguments); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, arguments ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, \ + format), ## arguments); \ + } while (0) + +#elif defined(_MSC_VER) + #define IA_CSS_TRACE_PLATFORM_HOST + + #define IA_CSS_TRACE_NATIVE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) + /* TODO: In case Host Side tracing is needed to be mapped to the + * Tunit, the following "IA_CSS_TRACE_TRACE" needs to be modified from + * PRINT to vied_nci_tunit_print function calls + */ + #define IA_CSS_TRACE_TRACE(severity, module, format, ...) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + PRINT(IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, \ + module, format), __VA_ARGS__); \ + } while (0) +#else + #error Unsupported platform! +#endif /* Platform */ + +#if defined(IA_CSS_TRACE_PLATFORM_CELL) + #include /* VOLATILE */ + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + do { \ + OP___printstring(__FILE__":") VOLATILE; \ + OP___printdec(__LINE__) VOLATILE; \ + OP___printstring("\n") VOLATILE; \ + } while (0) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + OP___printstring("["module"]:["severity"]:") \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_MSG_NATIVE(severity, module, format) \ + do { \ + IA_CSS_TRACE_FILE_PRINT_COMMAND; \ + OP___printstring("["module"]:["severity"]: "format) \ + VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_ARG_NATIVE(module, severity, i, value) \ + do { \ + IA_CSS_TRACE_MODULE_SEVERITY_PRINT(module, severity); \ + OP___dump(i, value) VOLATILE; \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + } while (0) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + do { \ + IA_CSS_TRACE_MSG_NATIVE(severity, module, format); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 1, a1); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 2, a2); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 3, a3); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 4, a4); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 5, a5); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 6, a6); \ + IA_CSS_TRACE_ARG_NATIVE(module, severity, 7, a7); \ + } while (0) + /* + ** Tracing Backend + */ +#if !defined(HRT_CSIM) && !defined(NO_TUNIT) + #include "vied_nci_tunit.h" +#endif + #define IA_CSS_TRACE_AUG_FORMAT_TRACE(format, module) \ + "[" module "]" format " : PID = %x : Timestamp = %d : PC = %x" + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + vied_nci_tunit_print(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + vied_nci_tunit_print1i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + vied_nci_tunit_print2i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + vied_nci_tunit_print3i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, a1, a2, a3, a4) \ + vied_nci_tunit_print4i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + vied_nci_tunit_print5i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + vied_nci_tunit_print6i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + vied_nci_tunit_print7i(IA_CSS_TRACE_AUG_FORMAT_TRACE(format, \ + module), \ + severity, a1, a2, a3, a4, a5, a6, a7) + +#elif defined(IA_CSS_TRACE_PLATFORM_HOST) + #include "print_support.h" + + #ifdef IA_CSS_TRACE_PRINT_FILE_LINE + #define IA_CSS_TRACE_FILE_PRINT_COMMAND \ + PRINT("%s:%d:\n", __FILE__, __LINE__) + #else + #define IA_CSS_TRACE_FILE_PRINT_COMMAND + #endif + + #define IA_CSS_TRACE_FORMAT_AUG_NATIVE(severity, module, format) \ + "[" module "]:[" severity "]: " format + + #define IA_CSS_TRACE_NATIVE_0(severity, module, format) \ + IA_CSS_TRACE_NATIVE(severity, module, format) + + #define IA_CSS_TRACE_NATIVE_1(severity, module, format, a1) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1) + + #define IA_CSS_TRACE_NATIVE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_NATIVE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_NATIVE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_NATIVE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_NATIVE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_NATIVE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_NATIVE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_NATIVE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) + + #define IA_CSS_TRACE_FORMAT_AUG_TRACE(severity, module, format) \ + "["module"]:["severity"]: "format + + #define IA_CSS_TRACE_TRACE_0(severity, module, format) \ + IA_CSS_TRACE_TRACE(severity, module, format) + + #define IA_CSS_TRACE_TRACE_1(severity, module, format, a1) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1) + + #define IA_CSS_TRACE_TRACE_2(severity, module, format, a1, a2) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2) + + #define IA_CSS_TRACE_TRACE_3(severity, module, format, a1, a2, a3) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3) + + #define IA_CSS_TRACE_TRACE_4(severity, module, format, \ + a1, a2, a3, a4) \ + IA_CSS_TRACE_TRACE(severity, module, format, a1, a2, a3, a4) + + #define IA_CSS_TRACE_TRACE_5(severity, module, format, \ + a1, a2, a3, a4, a5) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5) + + #define IA_CSS_TRACE_TRACE_6(severity, module, format, \ + a1, a2, a3, a4, a5, a6) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6) + + #define IA_CSS_TRACE_TRACE_7(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) \ + IA_CSS_TRACE_TRACE(severity, module, format, \ + a1, a2, a3, a4, a5, a6, a7) +#endif + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_0(severity, module, format) +#define IA_CSS_TRACE_1_1_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_1_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_1_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_1_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_1_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_1_1 IA_CSS_TRACE_NATIVE_0 +#define IA_CSS_TRACE_1_1_1 IA_CSS_TRACE_NATIVE_1 +#define IA_CSS_TRACE_2_1_1 IA_CSS_TRACE_NATIVE_2 +#define IA_CSS_TRACE_3_1_1 IA_CSS_TRACE_NATIVE_3 +#define IA_CSS_TRACE_4_1_1 IA_CSS_TRACE_NATIVE_4 +#define IA_CSS_TRACE_5_1_1 IA_CSS_TRACE_NATIVE_5 +#define IA_CSS_TRACE_6_1_1 IA_CSS_TRACE_NATIVE_6 +#define IA_CSS_TRACE_7_1_1 IA_CSS_TRACE_NATIVE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_1 "Assert" +#define IA_CSS_TRACE_SEVERITY_ERROR_1 "Error" +#define IA_CSS_TRACE_SEVERITY_WARNING_1 "Warning" +#define IA_CSS_TRACE_SEVERITY_INFO_1 "Info" +#define IA_CSS_TRACE_SEVERITY_DEBUG_1 "Debug" +#define IA_CSS_TRACE_SEVERITY_VERBOSE_1 "Verbose" + +/* Disabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_0(severity, module, format) +#define IA_CSS_TRACE_1_2_0(severity, module, format, arg1) +#define IA_CSS_TRACE_2_2_0(severity, module, format, arg1, arg2) +#define IA_CSS_TRACE_3_2_0(severity, module, format, arg1, arg2, arg3) +#define IA_CSS_TRACE_4_2_0(severity, module, format, arg1, arg2, arg3, arg4) +#define IA_CSS_TRACE_5_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5) +#define IA_CSS_TRACE_6_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6) +#define IA_CSS_TRACE_7_2_0(severity, module, format, arg1, arg2, arg3, arg4, \ + arg5, arg6, arg7) + +/* Enabled */ +/* Legend: IA_CSS_TRACE_{Argument Count}_{Backend ID}_{Enabled} */ +#define IA_CSS_TRACE_0_2_1 IA_CSS_TRACE_TRACE_0 +#define IA_CSS_TRACE_1_2_1 IA_CSS_TRACE_TRACE_1 +#define IA_CSS_TRACE_2_2_1 IA_CSS_TRACE_TRACE_2 +#define IA_CSS_TRACE_3_2_1 IA_CSS_TRACE_TRACE_3 +#define IA_CSS_TRACE_4_2_1 IA_CSS_TRACE_TRACE_4 +#define IA_CSS_TRACE_5_2_1 IA_CSS_TRACE_TRACE_5 +#define IA_CSS_TRACE_6_2_1 IA_CSS_TRACE_TRACE_6 +#define IA_CSS_TRACE_7_2_1 IA_CSS_TRACE_TRACE_7 + +/* Enabled */ +/* Legend: IA_CSS_TRACE_SEVERITY_{Severity Level}_{Backend ID} */ +#define IA_CSS_TRACE_SEVERITY_ASSERT_2 VIED_NCI_TUNIT_MSG_SEVERITY_FATAL +#define IA_CSS_TRACE_SEVERITY_ERROR_2 VIED_NCI_TUNIT_MSG_SEVERITY_ERROR +#define IA_CSS_TRACE_SEVERITY_WARNING_2 VIED_NCI_TUNIT_MSG_SEVERITY_WARNING +#define IA_CSS_TRACE_SEVERITY_INFO_2 VIED_NCI_TUNIT_MSG_SEVERITY_NORMAL +#define IA_CSS_TRACE_SEVERITY_DEBUG_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER1 +#define IA_CSS_TRACE_SEVERITY_VERBOSE_2 VIED_NCI_TUNIT_MSG_SEVERITY_USER2 + +/* +** Dynamicism +*/ + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_IMPL(module) \ + do { \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void); \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void); \ + } while (0) + +#define IA_CSS_TRACE_DYNAMIC_DECLARE_CONFIG_FUNC_IMPL(module) \ + do { \ + IA_CSS_TRACE_FILE_DUMMY_DEFINE; \ + void IA_CSS_TRACE_CAT(module, _trace_configure)\ + (int argc, const char *const *argv); \ + } while (0) + +#include "platform_support.h" +#include "type_support.h" + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_IMPL(module) \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_assert); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_error); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_warning); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_info); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_debug); \ + static uint8_t IA_CSS_TRACE_CAT(module, _trace_level_verbose); \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_assert_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_assert) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_error_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_error) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_warning_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_warning) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_info_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_info) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_debug_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_debug) = 0; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_enable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 1; \ + } \ + \ + void IA_CSS_TRACE_CAT(module, _trace_verbose_disable)(void) \ + { \ + IA_CSS_TRACE_CAT(module, _trace_level_verbose) = 0; \ + } + +#define IA_CSS_TRACE_DYNAMIC_DEFINE_CONFIG_FUNC_IMPL(module) \ +void IA_CSS_TRACE_CAT(module, _trace_configure)(const int argc, \ + const char *const *const argv) \ +{ \ + int i = 1; \ + const char *levels = 0; \ + \ + while (i < argc) { \ + if (!strcmp(argv[i], "-" #module "_trace")) { \ + ++i; \ + \ + if (i < argc) { \ + levels = argv[i]; \ + \ + while (*levels) { \ + switch (*levels++) { \ + case 'a': \ + IA_CSS_TRACE_CAT \ + (module, _trace_assert_enable)(); \ + break; \ + \ + case 'e': \ + IA_CSS_TRACE_CAT \ + (module, _trace_error_enable)(); \ + break; \ + \ + case 'w': \ + IA_CSS_TRACE_CAT \ + (module, _trace_warning_enable)(); \ + break; \ + \ + case 'i': \ + IA_CSS_TRACE_CAT \ + (module, _trace_info_enable)(); \ + break; \ + \ + case 'd': \ + IA_CSS_TRACE_CAT \ + (module, _trace_debug_enable)(); \ + break; \ + \ + case 'v': \ + IA_CSS_TRACE_CAT \ + (module, _trace_verbose_enable)(); \ + break; \ + \ + default: \ + } \ + } \ + } \ + } \ + \ + ++i; \ + } \ +} + +#endif /* __IA_CSS_TRACE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/trace.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/trace.mk new file mode 100644 index 000000000000..b232880b882b --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/trace/trace.mk @@ -0,0 +1,40 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE Trace + +# Dependencies +IA_CSS_TRACE_SUPPORT = $${MODULES_DIR}/support + +# API +IA_CSS_TRACE = $${MODULES_DIR}/trace +IA_CSS_TRACE_INTERFACE = $(IA_CSS_TRACE)/interface + +# +# Host +# + +# Host CPP Flags +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_HOST_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules + +# +# Firmware +# + +# Firmware CPP Flags +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_SUPPORT) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE_INTERFACE) +IA_CSS_TRACE_FW_CPPFLAGS += -I$(IA_CSS_TRACE)/trace_modules diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_access.h new file mode 100644 index 000000000000..1e81bad9f4ee --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_access.h @@ -0,0 +1,139 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_ACCESS_H +#define _SHARED_MEMORY_ACCESS_H + +#include +#include +#include + +typedef enum { + sm_esuccess, + sm_enomem, + sm_ezeroalloc, + sm_ebadvaddr, + sm_einternalerror, + sm_ecorruption, + sm_enocontiguousmem, + sm_enolocmem, + sm_emultiplefree, +} shared_memory_error; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the VIED subsystem + */ +typedef uint32_t vied_virtual_address_t; + +/** + * \brief Virtual address of (DDR) shared memory space as seen from the host + */ +typedef unsigned long long host_virtual_address_t; + +/** + * \brief List of physical addresses of (DDR) shared memory space. This is used to represent a list of physical pages. + */ +typedef struct shared_memory_physical_page_list_s *shared_memory_physical_page_list; +typedef struct shared_memory_physical_page_list_s +{ + shared_memory_physical_page_list next; + vied_physical_address_t address; +}shared_memory_physical_page_list_s; + + +/** + * \brief Initialize the shared memory interface administration on the host. + * \param idm: id of ddr memory + * \param host_ddr_addr: physical address of memory as seen from host + * \param memory_size: size of ddr memory in bytes + * \param ps: size of page in bytes (for instance 4096) + */ +int shared_memory_allocation_initialize(vied_memory_t idm, vied_physical_address_t host_ddr_addr, size_t memory_size, size_t ps); + +/** + * \brief De-initialize the shared memory interface administration on the host. + * + */ +void shared_memory_allocation_uninitialize(vied_memory_t idm); + +/** + * \brief Allocate (DDR) shared memory space and return a host virtual address. Returns NULL when insufficient memory available + */ +host_virtual_address_t shared_memory_alloc(vied_memory_t idm, size_t bytes); + +/** + * \brief Free (DDR) shared memory space. +*/ +void shared_memory_free(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Translate a virtual host.address to a physical address. +*/ +vied_physical_address_t shared_memory_virtual_host_to_physical_address (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Return the allocated physical pages for a virtual host.address. +*/ +shared_memory_physical_page_list shared_memory_virtual_host_to_physical_pages (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Destroy a shared_memory_physical_page_list. +*/ +void shared_memory_physical_pages_list_destroy (shared_memory_physical_page_list ppl); + +/** + * \brief Store a byte into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_8 (vied_memory_t idm, host_virtual_address_t addr, uint8_t data); + +/** + * \brief Store a 16-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_16(vied_memory_t idm, host_virtual_address_t addr, uint16_t data); + +/** + * \brief Store a 32-bit word into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store_32(vied_memory_t idm, host_virtual_address_t addr, uint32_t data); + +/** + * \brief Store a number of bytes into (DDR) shared memory space using a host virtual address + */ +void shared_memory_store(vied_memory_t idm, host_virtual_address_t addr, const void *data, size_t bytes); + +/** + * \brief Set a number of bytes of (DDR) shared memory space to 0 using a host virtual address + */ +void shared_memory_zero(vied_memory_t idm, host_virtual_address_t addr, size_t bytes); + +/** + * \brief Load a byte from (DDR) shared memory space using a host virtual address + */ +uint8_t shared_memory_load_8 (vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 16-bit word from (DDR) shared memory space using a host virtual address + */ +uint16_t shared_memory_load_16(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a 32-bit word from (DDR) shared memory space using a host virtual address + */ +uint32_t shared_memory_load_32(vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Load a number of bytes from (DDR) shared memory space using a host virtual address + */ +void shared_memory_load(vied_memory_t idm, host_virtual_address_t addr, void *data, size_t bytes); + +#endif /* _SHARED_MEMORY_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_map.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_map.h new file mode 100644 index 000000000000..1bbedcf9e7fd --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/shared_memory_map.h @@ -0,0 +1,53 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _SHARED_MEMORY_MAP_H +#define _SHARED_MEMORY_MAP_H + +#include +#include +#include + +typedef void (*shared_memory_invalidate_mmu_tlb)(void); +typedef void (*shared_memory_set_page_table_base_address)(vied_physical_address_t); + +typedef void (*shared_memory_invalidate_mmu_tlb_ssid)(vied_subsystem_t id); +typedef void (*shared_memory_set_page_table_base_address_ssid)(vied_subsystem_t id, vied_physical_address_t); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will NOT be taken into account. +*/ +int shared_memory_map_initialize(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb inv_tlb, shared_memory_set_page_table_base_address sbt); + +/** + * \brief Initialize the CSS virtual address system and MMU. The subsystem id will be taken into account. +*/ +int shared_memory_map_initialize_ssid(vied_subsystem_t id, vied_memory_t idm, size_t mmu_ps, size_t mmu_pnrs, vied_physical_address_t ddr_addr, shared_memory_invalidate_mmu_tlb_ssid inv_tlb, shared_memory_set_page_table_base_address_ssid sbt); + +/** + * \brief De-initialize the CSS virtual address system and MMU. +*/ +void shared_memory_map_uninitialize(vied_subsystem_t id, vied_memory_t idm); + +/** + * \brief Convert a host virtual address to a CSS virtual address and update the MMU. +*/ +vied_virtual_address_t shared_memory_map(vied_subsystem_t id, vied_memory_t idm, host_virtual_address_t addr); + +/** + * \brief Free a CSS virtual address and update the MMU. +*/ +void shared_memory_unmap(vied_subsystem_t id, vied_memory_t idm, vied_virtual_address_t addr); + + +#endif /* _SHARED_MEMORY_MAP_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_config.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_config.h new file mode 100644 index 000000000000..912f016ead24 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_config.h @@ -0,0 +1,33 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_CONFIG_H +#define _HRT_VIED_CONFIG_H + +/* Defines from the compiler: + * HRT_HOST - this is code running on the host + * HRT_CELL - this is code running on a cell + */ +#ifdef HRT_HOST +# define CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL 1 +# undef CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL + +#elif defined (HRT_CELL) +# undef CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL +# define CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL 1 + +#else /* !HRT_CELL */ +/* Allow neither HRT_HOST nor HRT_CELL for testing purposes */ +#endif /* !HRT_CELL */ + +#endif /* _HRT_VIED_CONFIG_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h new file mode 100644 index 000000000000..0b44492789e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_memory_access_types.h @@ -0,0 +1,36 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_MEMORY_ACCESS_TYPES_H +#define _HRT_VIED_MEMORY_ACCESS_TYPES_H + +/** Types for the VIED memory access interface */ + +#include "vied_types.h" + +/** + * \brief An identifier for a system memory. + * + * This identifier must be a compile-time constant. It is used in + * access to system memory. + */ +typedef unsigned int vied_memory_t; + +#ifndef __HIVECC +/** + * \brief The type for a physical address + */ +typedef unsigned long long vied_physical_address_t; +#endif + +#endif /* _HRT_VIED_MEMORY_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h new file mode 100644 index 000000000000..674f5fb5b0f9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access.h @@ -0,0 +1,70 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_H + +#include +#include "vied_config.h" +#include "vied_subsystem_access_types.h" + +#if !defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) && \ + !defined(CFG_VIED_SUBSYSTEM_ACCESS_LIB_IMPL) +#error Implementation selection macro for vied subsystem access not defined +#endif + +#if defined(CFG_VIED_SUBSYSTEM_ACCESS_INLINE_IMPL) +#ifndef __HIVECC +#error "Inline implementation of subsystem access not supported for host" +#endif +#define _VIED_SUBSYSTEM_ACCESS_INLINE static __inline +#include "vied_subsystem_access_impl.h" +#else +#define _VIED_SUBSYSTEM_ACCESS_INLINE +#endif + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr, uint8_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_16(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint16_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store_32(vied_subsystem_t dev, + vied_subsystem_address_t addr, uint32_t data); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_store(vied_subsystem_t dev, + vied_subsystem_address_t addr, + const void *data, unsigned int size); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint8_t vied_subsystem_load_8 (vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint16_t vied_subsystem_load_16(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +uint32_t vied_subsystem_load_32(vied_subsystem_t dev, + vied_subsystem_address_t addr); + +_VIED_SUBSYSTEM_ACCESS_INLINE +void vied_subsystem_load(vied_subsystem_t dev, + vied_subsystem_address_t addr, + void *data, unsigned int size); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h new file mode 100644 index 000000000000..81f4d08d5ae0 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_initialization.h @@ -0,0 +1,44 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H + +#include "vied_subsystem_access_types.h" + +/** @brief Initialises the access of a subsystem. + * @param[in] system The subsystem for which the access has to be initialised. + * + * vied_subsystem_access_initialize initilalises the access a subsystem. + * It sets the base address of the subsystem. This base address is extracted from the hsd file. + * + */ +void +vied_subsystem_access_initialize(vied_subsystem_t system); + + +/** @brief Initialises the access of multiple subsystems. + * @param[in] nr _subsystems The number of subsystems for which the access has to be initialised. + * @param[in] dev_base_addresses A pointer to an array of base addresses of subsystems. + * The size of this array must be "nr_subsystems". + * This array must be available during the accesses of the subsystem. + * + * vied_subsystems_access_initialize initilalises the access to multiple subsystems. + * It sets the base addresses of the subsystems that are provided by the array dev_base_addresses. + * + */ +void +vied_subsystems_access_initialize( unsigned int nr_subsystems + , const vied_subsystem_base_address_t *base_addresses); + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_INITIALIZE_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h new file mode 100644 index 000000000000..75fef6c4ddba --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_subsystem_access_types.h @@ -0,0 +1,34 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H +#define _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H + +/** Types for the VIED subsystem access interface */ +#include + +/** \brief An identifier for a VIED subsystem. + * + * This identifier must be a compile-time constant. It is used in + * access to a VIED subsystem. + */ +typedef unsigned int vied_subsystem_t; + + +/** \brief An address within a VIED subsystem */ +typedef uint32_t vied_subsystem_address_t; + +/** \brief A base address of a VIED subsystem seen from the host */ +typedef unsigned long long vied_subsystem_base_address_t; + +#endif /* _HRT_VIED_SUBSYSTEM_ACCESS_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_types.h new file mode 100644 index 000000000000..0acfdbb00cfa --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied/vied/vied_types.h @@ -0,0 +1,45 @@ +/* +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ +#ifndef _HRT_VIED_TYPES_H +#define _HRT_VIED_TYPES_H + +/** Types shared by VIED interfaces */ + +#include + +/** \brief An address within a VIED subsystem + * + * This will eventually replace teh vied_memory_address_t and vied_subsystem_address_t + */ +typedef uint32_t vied_address_t; + +/** \brief Memory address type + * + * A memory address is an offset within a memory. + */ +typedef uint32_t vied_memory_address_t; + +/** \brief Master port id */ +typedef int vied_master_port_id_t; + +/** + * \brief Require the existence of a certain type + * + * This macro can be used in interface header files to ensure that + * an implementation define type with a specified name exists. + */ +#define _VIED_REQUIRE_TYPE(T) enum { _VIED_SIZEOF_##T = sizeof(T) } + + +#endif /* _HRT_VIED_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h new file mode 100644 index 000000000000..b09d9f4d5d42 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_nci_acb/interface/vied_nci_acb_route_type.h @@ -0,0 +1,39 @@ +/* + * Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef VIED_NCI_ACB_ROUTE_TYPE_H_ +#define VIED_NCI_ACB_ROUTE_TYPE_H_ + +#include "type_support.h" + +typedef enum { + NCI_ACB_PORT_ISP = 0, + NCI_ACB_PORT_ACC = 1, + NCI_ACB_PORT_INVALID = 0xFF +} nci_acb_port_t; + +typedef struct { + /* 0 = ISP, 1 = Acc */ + nci_acb_port_t in_select; + /* 0 = ISP, 1 = Acc */ + nci_acb_port_t out_select; + /* When set, Ack will be sent only when Eof arrives */ + uint32_t ignore_line_num; + /* Fork adapter to enable streaming to both output + * (next acb out and isp out) + */ + uint32_t fork_acb_output; +} nci_acb_route_t; + +#endif /* VIED_NCI_ACB_ROUTE_TYPE_H_ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h new file mode 100644 index 000000000000..1ea7e729078c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_param_storage_class.h @@ -0,0 +1,28 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_PARAM_STORAGE_CLASS_H +#define __IA_CSS_PARAM_STORAGE_CLASS_H + +#include "storage_class.h" + +#ifndef __INLINE_PARAMETERS__ +#define IA_CSS_PARAMETERS_STORAGE_CLASS_H STORAGE_CLASS_EXTERN +#define IA_CSS_PARAMETERS_STORAGE_CLASS_C +#else +#define IA_CSS_PARAMETERS_STORAGE_CLASS_H STORAGE_CLASS_INLINE +#define IA_CSS_PARAMETERS_STORAGE_CLASS_C STORAGE_CLASS_INLINE +#endif + +#endif /* __IA_CSS_PARAM_STORAGE_CLASS_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h new file mode 100644 index 000000000000..4cc71be3fc38 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal.h @@ -0,0 +1,188 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_H +#define __IA_CSS_TERMINAL_H + +#include "type_support.h" +#include "ia_css_terminal_types.h" +#include "ia_css_param_storage_class.h" + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_in_terminal_get_descriptor_size( + const unsigned int nof_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_section_desc_t * +ia_css_param_in_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_out_terminal_get_descriptor_size( + const unsigned int nof_sections, + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_section_desc_t * +ia_css_param_out_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index, + const unsigned int nof_sections, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_param_terminal_create( + ia_css_param_terminal_t *param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_grid_desc_t * +ia_css_spatial_param_terminal_get_fragment_grid_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_frame_grid_param_section_desc_t * +ia_css_spatial_param_terminal_get_frame_grid_param_section_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_spatial_param_terminal_create( + ia_css_spatial_param_terminal_t *spatial_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_fragments, + const uint32_t kernel_id +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_sliced_param_terminal_get_descriptor_size( + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_slice_desc_t * +ia_css_sliced_param_terminal_get_fragment_slice_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_slice_param_section_desc_t * +ia_css_sliced_param_terminal_get_slice_param_section_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index, + const unsigned int slice_index, + const unsigned int section_index, + const unsigned int nof_slice_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_sliced_param_terminal_create( + ia_css_sliced_param_terminal_t *sliced_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments, + const uint32_t kernel_id +); + + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_program_terminal_get_descriptor_size( + const unsigned int nof_fragments, + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_param_section_desc_t * +ia_css_program_terminal_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int section_index, + const unsigned int nof_fragment_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_kernel_fragment_sequencer_info_desc_t * +ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int info_index, + const unsigned int nof_kernel_fragment_sequencer_infos +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_create( + ia_css_program_terminal_t *program_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_get_command_base_offset( + const ia_css_program_terminal_t *program_terminal, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int commands_slots_used, + uint16_t *command_desc_offset +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +uint16_t *ia_css_program_terminal_get_line_count( + const ia_css_kernel_fragment_sequencer_command_desc_t + *kernel_fragment_sequencer_command_desc_base, + const unsigned int set_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments +); + +#ifdef __INLINE_PARAMETERS__ +#include "ia_css_terminal_impl.h" +#endif /* __INLINE_PARAMETERS__ */ + +#endif /* __IA_CSS_TERMINAL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h new file mode 100644 index 000000000000..ca0a436082cf --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest.h @@ -0,0 +1,109 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_H +#define __IA_CSS_TERMINAL_MANIFEST_H + +#include "type_support.h" +#include "ia_css_param_storage_class.h" +#include "ia_css_terminal_manifest_types.h" + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_param_terminal_manifest_get_size( + const unsigned int nof_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_param_terminal_manifest_init( + ia_css_param_terminal_manifest_t *param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_param_manifest_section_desc_t * +ia_css_param_terminal_manifest_get_prm_sct_desc( + const ia_css_param_terminal_manifest_t *param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_spatial_param_terminal_manifest_get_size( + const unsigned int nof_frame_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_spatial_param_terminal_manifest_init( + ia_css_spatial_param_terminal_manifest_t *spatial_param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_frame_grid_param_manifest_section_desc_t * +ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + const ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_sliced_param_terminal_manifest_get_size( + const unsigned int nof_slice_param_sections +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_sliced_param_terminal_manifest_init( + ia_css_sliced_param_terminal_manifest_t *sliced_param_terminal, + const uint16_t section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_sliced_param_manifest_section_desc_t * +ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + const ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +unsigned int ia_css_program_terminal_manifest_get_size( + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +int ia_css_program_terminal_manifest_init( + ia_css_program_terminal_manifest_t *program_terminal, + const uint16_t fragment_param_section_count, + const uint16_t kernel_fragment_seq_info_section_count +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_fragment_param_manifest_section_desc_t * +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int section_index +); + +IA_CSS_PARAMETERS_STORAGE_CLASS_H +ia_css_kernel_fragment_sequencer_info_manifest_desc_t * +ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int info_index +); + +#ifdef __INLINE_PARAMETERS__ +#include "ia_css_terminal_manifest_impl.h" +#endif /* __INLINE_PARAMETERS__ */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h new file mode 100644 index 000000000000..fe146395a8f4 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_manifest_types.h @@ -0,0 +1,342 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_TYPES_H +#define __IA_CSS_TERMINAL_MANIFEST_TYPES_H + + +#include "ia_css_terminal_defs.h" +#include "type_support.h" +#include "ia_css_base_types.h" +#include "ia_css_terminal_manifest_base_types.h" + +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT 1 +#define SIZE_OF_PARAM_TERMINAL_MANIFEST_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* =============== Cached Param Terminal Manifest - START ============== */ +struct ia_css_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* Indication of the kernel this parameter belongs to */ + uint8_t kernel_id; + /* Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_MANIFEST_SEC_STRUCT]; +}; + +typedef struct ia_css_param_manifest_section_desc_s + ia_css_param_manifest_section_desc_t; + + +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT 4 +#define SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + (2*IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT * IA_CSS_UINT8_T_BITS)) + +/* Frame constant parameters terminal manifest */ +struct ia_css_param_terminal_manifest_s { + /* Parameter terminal manifest base */ + ia_css_terminal_manifest_t base; + /* + * Number of cached parameter sections, coming from manifest + * but also shared by the terminal + */ + uint16_t param_manifest_section_desc_count; + /* + * Points to the variable array of + * struct ia_css_param_section_desc_s + */ + uint16_t param_manifest_section_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_MAN_STRUCT]; +}; + +typedef struct ia_css_param_terminal_manifest_s + ia_css_param_terminal_manifest_t; +/* ================= Cached Param Terminal Manifest - End ================ */ + + +/* ================= Spatial Param Terminal Manifest - START ============= */ + +#define SIZE_OF_FRAG_GRID_MAN_STRUCT_IN_BITS \ + ((IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) \ + + (IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS)) + +struct ia_css_fragment_grid_manifest_desc_s { + /* Min resolution width/height of the spatial parameters + * for the fragment measured in compute units + */ + uint16_t min_fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Max resolution width/height of the spatial parameters + * for the fragment measured in compute units + */ + uint16_t max_fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_fragment_grid_manifest_desc_s + ia_css_fragment_grid_manifest_desc_t; + +#define N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT 1 +#define SIZE_OF_FRAME_GRID_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_frame_grid_param_manifest_section_desc_s { + /* Maximum buffer total size allowed for + * this frame of parameters + */ + uint32_t max_mem_size; + /* Memory space targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory space */ + uint8_t region_id; + /* size in bytes of each compute unit for + * the specified memory space and region + */ + uint8_t elem_size; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_PARAM_MAN_SEC_STRUCT]; +}; + +typedef struct ia_css_frame_grid_param_manifest_section_desc_s + ia_css_frame_grid_param_manifest_section_desc_t; + +#define SIZE_OF_FRAME_GRID_MAN_STRUCT_IN_BITS \ + ((IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) \ + + (IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS)) + +struct ia_css_frame_grid_manifest_desc_s { + /* Min resolution width/height of the spatial parameters for + * the frame measured in compute units + */ + uint16_t min_frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Max resolution width/height of the spatial parameters for + * the frame measured in compute units + */ + uint16_t max_frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_frame_grid_manifest_desc_s + ia_css_frame_grid_manifest_desc_t; + +#define N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT 2 +#define SIZE_OF_SPATIAL_PARAM_TERM_MAN_STRUCT_IN_BITS \ + ((SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS) \ + + (SIZE_OF_FRAME_GRID_MAN_STRUCT_IN_BITS) \ + + (SIZE_OF_FRAG_GRID_MAN_STRUCT_IN_BITS) \ + + (2 * IA_CSS_UINT16_T_BITS) \ + + (2 * IA_CSS_UINT8_T_BITS) \ + + (N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT * \ + IA_CSS_UINT8_T_BITS)) + +struct ia_css_spatial_param_terminal_manifest_s { + /* Spatial Parameter terminal manifest base */ + ia_css_terminal_manifest_t base; + /* Contains limits for the frame spatial parameters */ + ia_css_frame_grid_manifest_desc_t frame_grid_desc; + /* + * Constains limits for the fragment spatial parameters + * - COMMON AMONG FRAGMENTS + */ + ia_css_fragment_grid_manifest_desc_t common_fragment_grid_desc; + /* + * Number of frame spatial parameter sections, they are set + * in slice-steps through frame processing + */ + uint16_t frame_grid_param_manifest_section_desc_count; + /* + * Points to the variable array of + * ia_css_frame_spatial_param_manifest_section_desc_t + */ + uint16_t frame_grid_param_manifest_section_desc_offset; + /* + * Indication of the kernel this spatial parameter terminal belongs to + * SHOULD MATCH TO INDEX AND BE USED ONLY FOR CHECK + */ + uint8_t kernel_id; + /* + * Groups together compute units in order to achieve alignment + * requirements for transfes and to achieve canonical frame + * representation + */ + uint8_t compute_units_p_elem; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SPATIAL_PARAM_TERM_MAN_STRUCT]; +}; + +typedef struct ia_css_spatial_param_terminal_manifest_s + ia_css_spatial_param_terminal_manifest_t; + +/* ================= Spatial Param Terminal Manifest - END ================ */ + +/* ================= Sliced Param Terminal Manifest - START =============== */ + +#define N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT (2) +#define SIZE_OF_SLICED_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 2 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_sliced_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* + * Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SLICED_TERMINAL_MAN_SECTION_STRUCT]; +}; + +typedef struct ia_css_sliced_param_manifest_section_desc_s + ia_css_sliced_param_manifest_section_desc_t; + +#define N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT 3 +#define SIZE_OF_SLICED_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + (SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS \ + + 2 * IA_CSS_UINT16_T_BITS \ + + 1 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Frame constant parameters terminal manifest */ +struct ia_css_sliced_param_terminal_manifest_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_manifest_t base; + /* + * Number of the array elements + * sliced_param_section_offset points to + */ + uint16_t sliced_param_section_count; + /* + * Points to array of ia_css_sliced_param_manifest_section_desc_s + * which constain info for the slicing of the parameters + */ + uint16_t sliced_param_section_offset; + /* Kernel identifier */ + uint8_t kernel_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_SLICED_TERMINAL_MANIFEST_STRUCT]; +}; + +typedef struct ia_css_sliced_param_terminal_manifest_s + ia_css_sliced_param_terminal_manifest_t; + +/* ================= Slice Param Terminal Manifest - End =============== */ + +/* ================= Program Terminal Manifest - START ================= */ + +#define N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT 1 +#define SIZE_OF_FRAG_PARAM_MAN_SEC_STRUCT_IN_BITS \ + (1 * IA_CSS_UINT32_T_BITS \ + + 3 * IA_CSS_UINT8_T_BITS \ + + N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Fragment constant parameters manifest */ +struct ia_css_fragment_param_manifest_section_desc_s { + /* Maximum size of the related parameter region */ + uint32_t max_mem_size; + /* Indication of the kernel this parameter belongs to */ + uint8_t kernel_id; + /* Memory targeted by this section + * (Register MMIO Interface/DMEM/VMEM/GMEM etc) + */ + uint8_t mem_type_id; + /* Region id within the specified memory space */ + uint8_t region_id; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_FRAG_PARAM_MAN_SEC_STRUCT]; +}; + +typedef struct ia_css_fragment_param_manifest_section_desc_s + ia_css_fragment_param_manifest_section_desc_t; + +#define SIZE_OF_KERNEL_FRAG_SEQ_INFO_MAN_STRUCT_IN_BITS \ + (10*IA_CSS_N_DATA_DIMENSION*IA_CSS_UINT16_T_BITS) + +struct ia_css_kernel_fragment_sequencer_info_manifest_desc_s { + /* Slice dimensions */ + uint16_t min_fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Slice dimensions */ + uint16_t max_fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t min_fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t max_fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + min_fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + max_fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + min_fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + max_fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Dimension of grid */ + int16_t + min_fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Dimension of grid */ + int16_t + max_fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +typedef struct ia_css_kernel_fragment_sequencer_info_manifest_desc_s + ia_css_kernel_fragment_sequencer_info_manifest_desc_t; + +#define N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT 2 +#define SIZE_OF_PROG_TERM_MAN_STRUCT_IN_BITS \ + ((SIZE_OF_TERMINAL_MANIFEST_STRUCT_IN_BITS) \ + + (IA_CSS_UINT32_T_BITS) \ + + (5*IA_CSS_UINT16_T_BITS) \ + + (N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT * IA_CSS_UINT8_T_BITS)) + +struct ia_css_program_terminal_manifest_s { + ia_css_terminal_manifest_t base; + /* Connection manager passes seq info as single blob at the moment */ + uint32_t sequencer_info_kernel_id; + /* Maximum number of command secriptors supported + * by the program group + */ + uint16_t max_kernel_fragment_sequencer_command_desc; + uint16_t fragment_param_manifest_section_desc_count; + uint16_t fragment_param_manifest_section_desc_offset; + uint16_t kernel_fragment_sequencer_info_manifest_info_count; + uint16_t kernel_fragment_sequencer_info_manifest_info_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROGRAM_TERM_MAN_STRUCT]; +}; + +typedef struct ia_css_program_terminal_manifest_s + ia_css_program_terminal_manifest_t; + +/* ==================== Program Terminal Manifest - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_MANIFEST_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h new file mode 100644 index 000000000000..c5c89fb7ec91 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/interface/ia_css_terminal_types.h @@ -0,0 +1,351 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_TYPES_H +#define __IA_CSS_TERMINAL_TYPES_H + +#include "type_support.h" +#include "ia_css_base_types.h" +#include "ia_css_terminal_base_types.h" + + +typedef struct ia_css_program_control_init_load_section_desc_s + ia_css_program_control_init_load_section_desc_t; +typedef struct ia_css_program_control_init_connect_section_desc_s + ia_css_program_control_init_connect_section_desc_t; +typedef struct ia_css_program_control_init_program_desc_s + ia_css_program_control_init_program_desc_t; +typedef struct ia_css_program_control_init_terminal_s + ia_css_program_control_init_terminal_t; + +typedef struct ia_css_program_terminal_s ia_css_program_terminal_t; +typedef struct ia_css_fragment_param_section_desc_s + ia_css_fragment_param_section_desc_t; +typedef struct ia_css_kernel_fragment_sequencer_info_desc_s + ia_css_kernel_fragment_sequencer_info_desc_t; +typedef struct ia_css_kernel_fragment_sequencer_command_desc_s + ia_css_kernel_fragment_sequencer_command_desc_t; + +typedef struct ia_css_sliced_param_terminal_s ia_css_sliced_param_terminal_t; +typedef struct ia_css_fragment_slice_desc_s ia_css_fragment_slice_desc_t; +typedef struct ia_css_slice_param_section_desc_s + ia_css_slice_param_section_desc_t; + +typedef struct ia_css_spatial_param_terminal_s ia_css_spatial_param_terminal_t; +typedef struct ia_css_frame_grid_desc_s ia_css_frame_grid_desc_t; +typedef struct ia_css_frame_grid_param_section_desc_s + ia_css_frame_grid_param_section_desc_t; +typedef struct ia_css_fragment_grid_desc_s ia_css_fragment_grid_desc_t; + +typedef struct ia_css_param_terminal_s ia_css_param_terminal_t; +typedef struct ia_css_param_section_desc_s ia_css_param_section_desc_t; + +typedef struct ia_css_param_payload_s ia_css_param_payload_t; +typedef struct ia_css_terminal_s ia_css_terminal_t; + +/* =================== Generic Parameter Payload - START =================== */ +#define N_UINT64_IN_PARAM_PAYLOAD_STRUCT 1 +#define N_UINT32_IN_PARAM_PAYLOAD_STRUCT 1 + +#define IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + (N_UINT64_IN_PARAM_PAYLOAD_STRUCT * IA_CSS_UINT64_T_BITS \ + + VIED_VADDRESS_BITS \ + + N_UINT32_IN_PARAM_PAYLOAD_STRUCT * IA_CSS_UINT32_T_BITS) + +struct ia_css_param_payload_s { + /* + * Temporary variable holding the host address of the parameter buffer + * as PSYS is handling the parameters on the host side for the moment + */ + uint64_t host_buffer; + /* + * Base virtual addresses to parameters in subsystem virtual + * memory space + * NOTE: Used in legacy pg flow + */ + vied_vaddress_t buffer; + /* + * Offset to buffer address within external buffer set structure + * NOTE: Used in ppg flow + */ + uint32_t terminal_index; +}; +/* =================== Generic Parameter Payload - End ==================== */ + + +/* ==================== Cached Param Terminal - START ==================== */ +#define N_UINT32_IN_PARAM_SEC_STRUCT 2 + +#define SIZE_OF_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* Frame constant parameters section */ +struct ia_css_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_PARAM_TERMINAL_STRUCT 1 +#define N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT 6 + +#define SIZE_OF_PARAM_TERMINAL_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT16_IN_PARAM_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +/* Frame constant parameters terminal */ +struct ia_css_param_terminal_s { + /* Parameter terminal base */ + ia_css_terminal_t base; + /* Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Points to the variable array of ia_css_param_section_desc_t */ + uint16_t param_section_desc_offset; + uint8_t padding[N_PADDING_UINT8_IN_PARAM_TERMINAL_STRUCT]; +}; +/* ==================== Cached Param Terminal - End ==================== */ + + +/* ==================== Spatial Param Terminal - START ==================== */ +#define N_UINT16_IN_FRAG_GRID_STRUCT (2 * IA_CSS_N_DATA_DIMENSION) + +#define SIZE_OF_FRAG_GRID_STRUCT_BITS \ + (N_UINT16_IN_FRAG_GRID_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_fragment_grid_desc_s { + /* + * Offset width/height of the top-left compute unit of the + * fragment compared to the frame + */ + uint16_t fragment_grid_index[IA_CSS_N_DATA_DIMENSION]; + /* + * Resolution width/height of the spatial parameters that + * correspond to the fragment measured in compute units + */ + uint16_t fragment_grid_dimension[IA_CSS_N_DATA_DIMENSION]; +}; + +#define N_UINT32_IN_FRAME_GRID_PARAM_SEC_STRUCT 3 +#define N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT 4 + +#define SIZE_OF_FRAME_GRID_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_FRAME_GRID_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT * IA_CSS_UINT8_T_BITS) + +/* + * A plane of parameters with spatial aspect + * (compute units correlated to pixel data) + */ +struct ia_css_frame_grid_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; + /* + * stride in bytes of each line of compute units for + * the specified memory space and region + */ + uint32_t stride; + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_PARAM_SEC_STRUCT]; +}; + +#define N_UINT16_IN_FRAME_GRID_STRUCT_STRUCT IA_CSS_N_DATA_DIMENSION +#define N_PADDING_UINT8_IN_FRAME_GRID_STRUCT 4 + +#define SIZE_OF_FRAME_GRID_STRUCT_BITS \ + (N_UINT16_IN_FRAME_GRID_STRUCT_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_FRAME_GRID_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_frame_grid_desc_s { + /* Resolution width/height of the frame of + * spatial parameters measured in compute units + */ + uint16_t frame_grid_dimension[IA_CSS_N_DATA_DIMENSION]; + uint8_t padding[N_PADDING_UINT8_IN_FRAME_GRID_STRUCT]; +}; + +#define N_UINT32_IN_SPATIAL_PARAM_TERM_STRUCT 1 +#define N_UINT16_IN_SPATIAL_PARAM_TERM_STRUCT 2 + +#define SIZE_OF_SPATIAL_PARAM_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + SIZE_OF_FRAME_GRID_STRUCT_BITS \ + + N_UINT32_IN_SPATIAL_PARAM_TERM_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_UINT16_IN_SPATIAL_PARAM_TERM_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_spatial_param_terminal_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_t base; + /* Spatial Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Contains info for the frame of spatial parameters */ + ia_css_frame_grid_desc_t frame_grid_desc; + /* Kernel identifier */ + uint32_t kernel_id; + /* + * Points to the variable array of + * ia_css_frame_grid_param_section_desc_t + */ + uint16_t frame_grid_param_section_desc_offset; + /* + * Points to array of ia_css_fragment_spatial_desc_t + * which constain info for the fragments of spatial parameters + */ + uint16_t fragment_grid_desc_offset; +}; +/* ==================== Spatial Param Terminal - END ==================== */ + + +/* ==================== Sliced Param Terminal - START ==================== */ +#define N_UINT32_IN_SLICE_PARAM_SECTION_DESC_STRUCT 2 + +#define SIZE_OF_SLICE_PARAM_SECTION_DESC_STRUCT_BITS \ + (N_UINT32_IN_SLICE_PARAM_SECTION_DESC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* A Slice of parameters ready to be trasferred from/to registers */ +struct ia_css_slice_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_FRAGMENT_SLICE_DESC_STRUCT 2 +#define N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT 4 + +#define SIZE_OF_FRAGMENT_SLICE_DESC_STRUCT_BITS \ + (N_UINT16_IN_FRAGMENT_SLICE_DESC_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_fragment_slice_desc_s { + /* + * Points to array of ia_css_slice_param_section_desc_t + * which constain info for each prameter slice + */ + uint16_t slice_section_desc_offset; + /* Number of slices for the parameters for this fragment */ + uint16_t slice_count; + uint8_t padding[N_PADDING_UINT8_FRAGMENT_SLICE_DESC_STRUCT]; +}; + +#define N_UINT32_IN_SLICED_PARAM_TERMINAL_STRUCT 1 +#define N_UINT16_IN_SLICED_PARAM_TERMINAL_STRUCT 1 +#define N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT 2 + +#define SIZE_OF_SLICED_PARAM_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT32_IN_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT32_T_BITS \ + + N_UINT16_IN_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_sliced_param_terminal_s { + /* Spatial Parameter terminal base */ + ia_css_terminal_t base; + /* Spatial Parameter buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Kernel identifier */ + uint32_t kernel_id; + /* + * Points to array of ia_css_fragment_slice_desc_t + * which constain info for the slicing of the parameters + */ + uint16_t fragment_slice_desc_offset; + uint8_t padding[N_PADDING_UINT8_SLICED_PARAM_TERMINAL_STRUCT]; +}; +/* ==================== Sliced Param Terminal - END ==================== */ + + +/* ==================== Program Terminal - START ==================== */ + +#define N_UINT32_IN_FRAG_PARAM_SEC_STRUCT 2 + +#define SIZE_OF_FRAG_PARAM_SEC_STRUCT_BITS \ + (N_UINT32_IN_FRAG_PARAM_SEC_STRUCT * IA_CSS_UINT32_T_BITS) + +/* Fragment constant parameters section */ +struct ia_css_fragment_param_section_desc_s { + /* Offset of the parameter allocation in memory */ + uint32_t mem_offset; + /* Memory allocation size needs of this parameter */ + uint32_t mem_size; +}; + +#define N_UINT16_IN_FRAG_SEQ_COMMAND_STRUCT IA_CSS_N_COMMAND_COUNT + +#define SIZE_OF_FRAG_SEQ_COMMANDS_STRUCT_BITS \ + (N_UINT16_IN_FRAG_SEQ_COMMAND_STRUCT * IA_CSS_UINT16_T_BITS) + +/* 4 commands packe together to save memory space */ +struct ia_css_kernel_fragment_sequencer_command_desc_s { + /* Contains the "(command_index%4) == index" command desc */ + uint16_t line_count[IA_CSS_N_COMMAND_COUNT]; +}; + +#define N_UINT16_IN_FRAG_SEQ_INFO_STRUCT (5 * IA_CSS_N_DATA_DIMENSION + 2) + +#define SIZE_OF_FRAG_SEQ_INFO_STRUCT_BITS \ + (N_UINT16_IN_FRAG_SEQ_INFO_STRUCT * IA_CSS_UINT16_T_BITS) + +struct ia_css_kernel_fragment_sequencer_info_desc_s { + /* Slice dimensions */ + uint16_t fragment_grid_slice_dimension[IA_CSS_N_DATA_DIMENSION]; + /* Nof slices */ + uint16_t fragment_grid_slice_count[IA_CSS_N_DATA_DIMENSION]; + /* Grid point decimation factor */ + uint16_t + fragment_grid_point_decimation_factor[IA_CSS_N_DATA_DIMENSION]; + /* Relative position of grid origin to pixel origin */ + int16_t + fragment_grid_overlay_pixel_topleft_index[IA_CSS_N_DATA_DIMENSION]; + /* Size of active fragment region */ + int16_t + fragment_grid_overlay_pixel_dimension[IA_CSS_N_DATA_DIMENSION]; + /* If >0 it overrides the standard fragment sequencer info */ + uint16_t command_count; + /* + * To be used only if command_count>0, points to the descriptors + * for the commands (ia_css_kernel_fragment_sequencer_command_desc_s) + */ + uint16_t command_desc_offset; +}; + +#define N_UINT16_IN_PROG_TERM_STRUCT 2 +#define N_PADDING_UINT8_IN_PROG_TERM_STRUCT 4 + +#define SIZE_OF_PROG_TERM_STRUCT_BITS \ + (SIZE_OF_TERMINAL_STRUCT_BITS \ + + IA_CSS_PARAM_PAYLOAD_STRUCT_BITS \ + + N_UINT16_IN_PROG_TERM_STRUCT * IA_CSS_UINT16_T_BITS \ + + N_PADDING_UINT8_IN_PROG_TERM_STRUCT * IA_CSS_UINT8_T_BITS) + +struct ia_css_program_terminal_s { + /* Program terminal base */ + ia_css_terminal_t base; + /* Program terminal buffer handle attached to the terminal */ + ia_css_param_payload_t param_payload; + /* Points to array of ia_css_fragment_param_desc_s */ + uint16_t fragment_param_section_desc_offset; + /* Points to array of ia_css_kernel_fragment_sequencer_info_s */ + uint16_t kernel_fragment_sequencer_info_desc_offset; + /* align to 64 */ + uint8_t padding[N_PADDING_UINT8_IN_PROG_TERM_STRUCT]; +}; +/* ==================== Program Terminal - END ==================== */ + +#endif /* __IA_CSS_TERMINAL_TYPES_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c new file mode 100644 index 000000000000..683fb3a88cd8 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_PARAMETERS__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_param_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_PARAMETERS__ */ +#include "ia_css_terminal_impl.h" +#endif /* __INLINE_PARAMETERS__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h new file mode 100644 index 000000000000..9ccf3931e8e3 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_impl.h @@ -0,0 +1,495 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_IMPL_H +#define __IA_CSS_TERMINAL_IMPL_H + +#include "ia_css_terminal.h" +#include "ia_css_terminal_types.h" +#include "error_support.h" +#include "assert_support.h" +#include "storage_class.h" + +/* Param Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_in_terminal_get_descriptor_size( + const unsigned int nof_sections) +{ + return sizeof(ia_css_param_terminal_t) + + nof_sections*sizeof(ia_css_param_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_section_desc_t *ia_css_param_in_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index) +{ + ia_css_param_section_desc_t *param_section_base; + ia_css_param_section_desc_t *param_section_desc = NULL; + + verifjmpexit(param_terminal != NULL); + + param_section_base = + (ia_css_param_section_desc_t *) + (((const char *)param_terminal) + + param_terminal->param_section_desc_offset); + param_section_desc = &(param_section_base[section_index]); + +EXIT: + return param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_out_terminal_get_descriptor_size( + const unsigned int nof_sections, + const unsigned int nof_fragments) +{ + return sizeof(ia_css_param_terminal_t) + + nof_fragments*nof_sections*sizeof(ia_css_param_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_section_desc_t *ia_css_param_out_terminal_get_param_section_desc( + const ia_css_param_terminal_t *param_terminal, + const unsigned int section_index, + const unsigned int nof_sections, + const unsigned int fragment_index) +{ + ia_css_param_section_desc_t *param_section_base; + ia_css_param_section_desc_t *param_section_desc = NULL; + + verifjmpexit(param_terminal != NULL); + + param_section_base = + (ia_css_param_section_desc_t *) + (((const char *)param_terminal) + + param_terminal->param_section_desc_offset); + param_section_desc = + &(param_section_base[(nof_sections * fragment_index) + + section_index]); + +EXIT: + return param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_param_terminal_create( + ia_css_param_terminal_t *param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal) +{ + if (param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN : + IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT; + param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + param_terminal->base.size = terminal_size; + param_terminal->param_section_desc_offset = + sizeof(ia_css_param_terminal_t); + + return 0; +} + +/* Spatial Param Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_spatial_param_terminal_get_descriptor_size( + const unsigned int nof_frame_param_sections, + const unsigned int nof_fragments) +{ + return sizeof(ia_css_spatial_param_terminal_t) + + nof_frame_param_sections * sizeof( + ia_css_frame_grid_param_section_desc_t) + + nof_fragments * sizeof(ia_css_fragment_grid_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_grid_desc_t * +ia_css_spatial_param_terminal_get_fragment_grid_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int fragment_index) +{ + ia_css_fragment_grid_desc_t *fragment_grid_desc_base; + ia_css_fragment_grid_desc_t *fragment_grid_desc = NULL; + + verifjmpexit(spatial_param_terminal != NULL); + + fragment_grid_desc_base = + (ia_css_fragment_grid_desc_t *) + (((const char *)spatial_param_terminal) + + spatial_param_terminal->fragment_grid_desc_offset); + fragment_grid_desc = &(fragment_grid_desc_base[fragment_index]); + +EXIT: + return fragment_grid_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_frame_grid_param_section_desc_t * +ia_css_spatial_param_terminal_get_frame_grid_param_section_desc( + const ia_css_spatial_param_terminal_t *spatial_param_terminal, + const unsigned int section_index) +{ + ia_css_frame_grid_param_section_desc_t * + frame_grid_param_section_base; + ia_css_frame_grid_param_section_desc_t * + frame_grid_param_section_desc = NULL; + + verifjmpexit(spatial_param_terminal != NULL); + + frame_grid_param_section_base = + (ia_css_frame_grid_param_section_desc_t *) + (((const char *)spatial_param_terminal) + + spatial_param_terminal->frame_grid_param_section_desc_offset); + frame_grid_param_section_desc = + &(frame_grid_param_section_base[section_index]); + +EXIT: + return frame_grid_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_spatial_param_terminal_create( + ia_css_spatial_param_terminal_t *spatial_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_fragments, + const uint32_t kernel_id) +{ + if (spatial_param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + spatial_param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN : + IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT; + spatial_param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + spatial_param_terminal->base.size = terminal_size; + spatial_param_terminal->kernel_id = kernel_id; + spatial_param_terminal->fragment_grid_desc_offset = + sizeof(ia_css_spatial_param_terminal_t); + spatial_param_terminal->frame_grid_param_section_desc_offset = + spatial_param_terminal->fragment_grid_desc_offset + + (nof_fragments * sizeof(ia_css_fragment_grid_desc_t)); + + return 0; +} + +/* Sliced terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_sliced_param_terminal_get_descriptor_size( + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments) +{ + unsigned int descriptor_size = 0; + unsigned int fragment_index; + unsigned int nof_slices_total = 0; + + verifjmpexit(nof_slices != NULL); + + for (fragment_index = 0; + fragment_index < nof_fragments; fragment_index++) { + nof_slices_total += nof_slices[fragment_index]; + } + + descriptor_size = + sizeof(ia_css_sliced_param_terminal_t) + + nof_fragments*sizeof(ia_css_fragment_slice_desc_t) + + nof_slices_total*nof_slice_param_sections*sizeof( + ia_css_fragment_param_section_desc_t); + +EXIT: + return descriptor_size; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_slice_desc_t * +ia_css_sliced_param_terminal_get_fragment_slice_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index +) +{ + ia_css_fragment_slice_desc_t *fragment_slice_desc_base; + ia_css_fragment_slice_desc_t *fragment_slice_desc = NULL; + + verifjmpexit(sliced_param_terminal != NULL); + + fragment_slice_desc_base = + (ia_css_fragment_slice_desc_t *) + (((const char *)sliced_param_terminal) + + sliced_param_terminal->fragment_slice_desc_offset); + fragment_slice_desc = &(fragment_slice_desc_base[fragment_index]); + +EXIT: + return fragment_slice_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_slice_param_section_desc_t * +ia_css_sliced_param_terminal_get_slice_param_section_desc( + const ia_css_sliced_param_terminal_t *sliced_param_terminal, + const unsigned int fragment_index, + const unsigned int slice_index, + const unsigned int section_index, + const unsigned int nof_slice_param_sections) +{ + ia_css_fragment_slice_desc_t *fragment_slice_desc; + ia_css_slice_param_section_desc_t *slice_param_section_desc_base; + ia_css_slice_param_section_desc_t *slice_param_section_desc = NULL; + + fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_param_terminal, + fragment_index + ); + verifjmpexit(fragment_slice_desc != NULL); + + slice_param_section_desc_base = + (ia_css_slice_param_section_desc_t *) + (((const char *)sliced_param_terminal) + + fragment_slice_desc->slice_section_desc_offset); + slice_param_section_desc = + &(slice_param_section_desc_base[( + slice_index * nof_slice_param_sections) + + section_index]); + +EXIT: + return slice_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_sliced_param_terminal_create( + ia_css_sliced_param_terminal_t *sliced_param_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const uint16_t is_input_terminal, + const unsigned int nof_slice_param_sections, + const unsigned int nof_slices[], + const unsigned int nof_fragments, + const uint32_t kernel_id) +{ + unsigned int fragment_index; + unsigned int nof_slices_total = 0; + + if (sliced_param_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + sliced_param_terminal->base.terminal_type = + is_input_terminal ? + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN : + IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT; + sliced_param_terminal->base.parent_offset = + 0 - ((int16_t)terminal_offset); + sliced_param_terminal->base.size = terminal_size; + sliced_param_terminal->kernel_id = kernel_id; + /* set here to use below to find the pointer */ + sliced_param_terminal->fragment_slice_desc_offset = + sizeof(ia_css_sliced_param_terminal_t); + for (fragment_index = 0; + fragment_index < nof_fragments; fragment_index++) { + ia_css_fragment_slice_desc_t *fragment_slice_desc = + ia_css_sliced_param_terminal_get_fragment_slice_desc( + sliced_param_terminal, + fragment_index); + /* + * Error handling not required at this point + * since everything has been constructed/validated just above + */ + fragment_slice_desc->slice_count = nof_slices[fragment_index]; + fragment_slice_desc->slice_section_desc_offset = + sliced_param_terminal->fragment_slice_desc_offset + + (nof_fragments * sizeof( + ia_css_fragment_slice_desc_t)) + + (nof_slices_total * nof_slice_param_sections * sizeof( + ia_css_slice_param_section_desc_t)); + nof_slices_total += nof_slices[fragment_index]; + } + + return 0; +} + +/* Program terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_program_terminal_get_descriptor_size( + const unsigned int nof_fragments, + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs) +{ + return sizeof(ia_css_program_terminal_t) + + nof_fragments * nof_fragment_param_sections * + sizeof(ia_css_fragment_param_section_desc_t) + + nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t) + + nof_command_objs * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_param_section_desc_t * +ia_css_program_terminal_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int section_index, + const unsigned int nof_fragment_param_sections) +{ + ia_css_fragment_param_section_desc_t * + fragment_param_section_desc_base; + ia_css_fragment_param_section_desc_t * + fragment_param_section_desc = NULL; + + verifjmpexit(program_terminal != NULL); + verifjmpexit(section_index < nof_fragment_param_sections); + + fragment_param_section_desc_base = + (ia_css_fragment_param_section_desc_t *) + (((const char *)program_terminal) + + program_terminal->fragment_param_section_desc_offset); + fragment_param_section_desc = + &(fragment_param_section_desc_base[(fragment_index * + nof_fragment_param_sections) + section_index]); + +EXIT: + return fragment_param_section_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_kernel_fragment_sequencer_info_desc_t * +ia_css_program_terminal_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_t *program_terminal, + const unsigned int fragment_index, + const unsigned int info_index, + const unsigned int nof_kernel_fragment_sequencer_infos) +{ + ia_css_kernel_fragment_sequencer_info_desc_t * + kernel_fragment_sequencer_info_desc_base; + ia_css_kernel_fragment_sequencer_info_desc_t * + kernel_fragment_sequencer_info_desc = NULL; + + verifjmpexit(program_terminal != NULL); + if (nof_kernel_fragment_sequencer_infos > 0) { + verifjmpexit(info_index < nof_kernel_fragment_sequencer_infos); + } + + kernel_fragment_sequencer_info_desc_base = + (ia_css_kernel_fragment_sequencer_info_desc_t *) + (((const char *)program_terminal) + + program_terminal->kernel_fragment_sequencer_info_desc_offset); + kernel_fragment_sequencer_info_desc = + &(kernel_fragment_sequencer_info_desc_base[(fragment_index * + nof_kernel_fragment_sequencer_infos) + info_index]); + +EXIT: + return kernel_fragment_sequencer_info_desc; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_create( + ia_css_program_terminal_t *program_terminal, + const uint16_t terminal_offset, + const uint16_t terminal_size, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int nof_command_objs) +{ + if (program_terminal == NULL) { + return -EFAULT; + } + + if (terminal_offset > (1<<15)) { + return -EINVAL; + } + + program_terminal->base.terminal_type = IA_CSS_TERMINAL_TYPE_PROGRAM; + program_terminal->base.parent_offset = 0-((int16_t)terminal_offset); + program_terminal->base.size = terminal_size; + program_terminal->kernel_fragment_sequencer_info_desc_offset = + sizeof(ia_css_program_terminal_t); + program_terminal->fragment_param_section_desc_offset = + program_terminal->kernel_fragment_sequencer_info_desc_offset + + (nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (nof_command_objs * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_get_command_base_offset( + const ia_css_program_terminal_t *program_terminal, + const unsigned int nof_fragments, + const unsigned int nof_kernel_fragment_sequencer_infos, + const unsigned int commands_slots_used, + uint16_t *command_desc_offset) +{ + if (command_desc_offset == NULL) { + return -EFAULT; + } + + *command_desc_offset = 0; + + if (program_terminal == NULL) { + return -EFAULT; + } + + *command_desc_offset = + program_terminal->kernel_fragment_sequencer_info_desc_offset + + (nof_fragments * nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_desc_t)) + + (commands_slots_used * sizeof( + ia_css_kernel_fragment_sequencer_command_desc_t)); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +uint16_t *ia_css_program_terminal_get_line_count( + const ia_css_kernel_fragment_sequencer_command_desc_t + *kernel_fragment_sequencer_command_desc_base, + const unsigned int set_count) +{ + uint16_t *line_count = NULL; + + verifjmpexit(kernel_fragment_sequencer_command_desc_base != NULL); + line_count = + (uint16_t *)&(kernel_fragment_sequencer_command_desc_base[ + set_count >> 2].line_count[set_count & 0x00000003]); +EXIT: + return line_count; +} + +#endif /* __IA_CSS_TERMINAL_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c new file mode 100644 index 000000000000..53c4708c7fc9 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest.c @@ -0,0 +1,20 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifdef __INLINE_PARAMETERS__ +#include "storage_class.h" +STORAGE_CLASS_INLINE int __ia_css_param_avoid_warning_on_empty_file(void) { return 0; } +#else /* __INLINE_PARAMETERS__ */ +#include "ia_css_terminal_manifest_impl.h" +#endif /* __INLINE_PARAMETERS__ */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h new file mode 100644 index 000000000000..39734136b117 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/src/ia_css_terminal_manifest_impl.h @@ -0,0 +1,347 @@ +/** +* Support for Intel Camera Imaging ISP subsystem. + * Copyright (c) 2010 - 2018, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. +*/ + +#ifndef __IA_CSS_TERMINAL_MANIFEST_IMPL_H +#define __IA_CSS_TERMINAL_MANIFEST_IMPL_H + +#include "ia_css_terminal_manifest.h" +#include "error_support.h" +#include "assert_support.h" +#include "storage_class.h" + +STORAGE_CLASS_INLINE void __terminal_manifest_dummy_check_alignment(void) +{ + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_terminal_manifest_t) % sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SPATIAL_PARAM_TERM_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_spatial_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_spatial_param_terminal_manifest_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAME_GRID_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_frame_grid_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_frame_grid_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PROG_TERM_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_program_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_program_terminal_manifest_t)%sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_FRAG_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_fragment_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_fragment_param_manifest_section_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_KERNEL_FRAG_SEQ_INFO_MAN_STRUCT_IN_BITS != + (CHAR_BIT * sizeof( + ia_css_kernel_fragment_sequencer_info_manifest_desc_t)) + ); + + COMPILATION_ERROR_IF(0 != sizeof( + ia_css_kernel_fragment_sequencer_info_manifest_desc_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_PARAM_TERMINAL_MANIFEST_STRUCT_IN_BITS != + (CHAR_BIT * sizeof(ia_css_sliced_param_terminal_manifest_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_terminal_manifest_t) % + sizeof(uint64_t)); + + COMPILATION_ERROR_IF( + SIZE_OF_SLICED_PARAM_MAN_SEC_STRUCT_IN_BITS != + (CHAR_BIT * sizeof + (ia_css_sliced_param_manifest_section_desc_t))); + + COMPILATION_ERROR_IF(0 != + sizeof(ia_css_sliced_param_manifest_section_desc_t) % + sizeof(uint64_t)); +} + +/* Parameter Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_param_terminal_manifest_get_size( + const unsigned int nof_sections) +{ + + return sizeof(ia_css_param_terminal_manifest_t) + + nof_sections*sizeof(ia_css_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_param_terminal_manifest_init( + ia_css_param_terminal_manifest_t *param_terminal, + const uint16_t section_count) +{ + if (param_terminal == NULL) { + return -EFAULT; + } + + param_terminal->param_manifest_section_desc_count = section_count; + param_terminal->param_manifest_section_desc_offset = sizeof( + ia_css_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_param_manifest_section_desc_t * +ia_css_param_terminal_manifest_get_prm_sct_desc( + const ia_css_param_terminal_manifest_t *param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_param_manifest_section_desc_t *param_manifest_section_base; + ia_css_param_manifest_section_desc_t * + param_manifest_section_desc = NULL; + + verifjmpexit(param_terminal_manifest != NULL); + + param_manifest_section_base = + (ia_css_param_manifest_section_desc_t *) + (((const char *)param_terminal_manifest) + + param_terminal_manifest->param_manifest_section_desc_offset); + + param_manifest_section_desc = + &(param_manifest_section_base[section_index]); + +EXIT: + return param_manifest_section_desc; +} + +/* Spatial Parameter Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_spatial_param_terminal_manifest_get_size( + const unsigned int nof_frame_param_sections) +{ + return sizeof(ia_css_spatial_param_terminal_manifest_t) + + nof_frame_param_sections * sizeof( + ia_css_frame_grid_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_spatial_param_terminal_manifest_init( + ia_css_spatial_param_terminal_manifest_t *spatial_param_terminal, + const uint16_t section_count) +{ + if (spatial_param_terminal == NULL) { + return -EFAULT; + } + + spatial_param_terminal-> + frame_grid_param_manifest_section_desc_count = section_count; + spatial_param_terminal-> + frame_grid_param_manifest_section_desc_offset = + sizeof(ia_css_spatial_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_frame_grid_param_manifest_section_desc_t * +ia_css_spatial_param_terminal_manifest_get_frm_grid_prm_sct_desc( + const ia_css_spatial_param_terminal_manifest_t * + spatial_param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_frame_grid_param_manifest_section_desc_t * + frame_param_manifest_section_base; + ia_css_frame_grid_param_manifest_section_desc_t * + frame_param_manifest_section_desc = NULL; + + verifjmpexit(spatial_param_terminal_manifest != NULL); + + frame_param_manifest_section_base = + (ia_css_frame_grid_param_manifest_section_desc_t *) + (((const char *)spatial_param_terminal_manifest) + + spatial_param_terminal_manifest-> + frame_grid_param_manifest_section_desc_offset); + frame_param_manifest_section_desc = + &(frame_param_manifest_section_base[section_index]); + +EXIT: + return frame_param_manifest_section_desc; +} + +/* Sliced Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_sliced_param_terminal_manifest_get_size( + const unsigned int nof_slice_param_sections) +{ + return sizeof(ia_css_spatial_param_terminal_manifest_t) + + nof_slice_param_sections * + sizeof(ia_css_sliced_param_manifest_section_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_sliced_param_terminal_manifest_init( + ia_css_sliced_param_terminal_manifest_t *sliced_param_terminal, + const uint16_t section_count) +{ + if (sliced_param_terminal == NULL) { + return -EFAULT; + } + + sliced_param_terminal->sliced_param_section_count = section_count; + sliced_param_terminal->sliced_param_section_offset = + sizeof(ia_css_sliced_param_terminal_manifest_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_sliced_param_manifest_section_desc_t * +ia_css_sliced_param_terminal_manifest_get_sliced_prm_sct_desc( + const ia_css_sliced_param_terminal_manifest_t * + sliced_param_terminal_manifest, + const unsigned int section_index) +{ + ia_css_sliced_param_manifest_section_desc_t * + sliced_param_manifest_section_base; + ia_css_sliced_param_manifest_section_desc_t * + sliced_param_manifest_section_desc = NULL; + + verifjmpexit(sliced_param_terminal_manifest != NULL); + + sliced_param_manifest_section_base = + (ia_css_sliced_param_manifest_section_desc_t *) + (((const char *)sliced_param_terminal_manifest) + + sliced_param_terminal_manifest-> + sliced_param_section_offset); + sliced_param_manifest_section_desc = + &(sliced_param_manifest_section_base[section_index]); + +EXIT: + return sliced_param_manifest_section_desc; +} + +/* Program Terminal */ +IA_CSS_PARAMETERS_STORAGE_CLASS_C +unsigned int ia_css_program_terminal_manifest_get_size( + const unsigned int nof_fragment_param_sections, + const unsigned int nof_kernel_fragment_sequencer_infos) +{ + return sizeof(ia_css_program_terminal_manifest_t) + + nof_fragment_param_sections * + sizeof(ia_css_fragment_param_manifest_section_desc_t) + + nof_kernel_fragment_sequencer_infos * + sizeof(ia_css_kernel_fragment_sequencer_info_manifest_desc_t); +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +int ia_css_program_terminal_manifest_init( + ia_css_program_terminal_manifest_t *program_terminal, + const uint16_t fragment_param_section_count, + const uint16_t kernel_fragment_seq_info_section_count) +{ + if (program_terminal == NULL) { + return -EFAULT; + } + + program_terminal->fragment_param_manifest_section_desc_count = + fragment_param_section_count; + program_terminal->fragment_param_manifest_section_desc_offset = + sizeof(ia_css_program_terminal_manifest_t); + + program_terminal->kernel_fragment_sequencer_info_manifest_info_count = + kernel_fragment_seq_info_section_count; + program_terminal->kernel_fragment_sequencer_info_manifest_info_offset = + sizeof(ia_css_program_terminal_manifest_t) + + fragment_param_section_count*sizeof( + ia_css_fragment_param_manifest_section_desc_t); + + return 0; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_fragment_param_manifest_section_desc_t * +ia_css_program_terminal_manifest_get_frgmnt_prm_sct_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int section_index) +{ + ia_css_fragment_param_manifest_section_desc_t * + fragment_param_manifest_section_base; + ia_css_fragment_param_manifest_section_desc_t * + fragment_param_manifest_section = NULL; + + verifjmpexit(program_terminal_manifest != NULL); + + fragment_param_manifest_section_base = + (ia_css_fragment_param_manifest_section_desc_t *) + (((const char *)program_terminal_manifest) + + program_terminal_manifest-> + fragment_param_manifest_section_desc_offset); + fragment_param_manifest_section = + &(fragment_param_manifest_section_base[section_index]); + +EXIT: + return fragment_param_manifest_section; +} + +IA_CSS_PARAMETERS_STORAGE_CLASS_C +ia_css_kernel_fragment_sequencer_info_manifest_desc_t * +ia_css_program_terminal_manifest_get_kernel_frgmnt_seq_info_desc( + const ia_css_program_terminal_manifest_t *program_terminal_manifest, + const unsigned int info_index) +{ + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + kernel_manifest_fragment_sequencer_info_manifest_desc_base; + ia_css_kernel_fragment_sequencer_info_manifest_desc_t * + kernel_manifest_fragment_sequencer_info_manifest_desc = NULL; + + verifjmpexit(program_terminal_manifest != NULL); + + kernel_manifest_fragment_sequencer_info_manifest_desc_base = + (ia_css_kernel_fragment_sequencer_info_manifest_desc_t *) + (((const char *)program_terminal_manifest) + + program_terminal_manifest-> + kernel_fragment_sequencer_info_manifest_info_offset); + + kernel_manifest_fragment_sequencer_info_manifest_desc = + &(kernel_manifest_fragment_sequencer_info_manifest_desc_base[ + info_index]); + +EXIT: + return kernel_manifest_fragment_sequencer_info_manifest_desc; +} + +#endif /* __IA_CSS_TERMINAL_MANIFEST_IMPL_H */ diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/vied_parameters.mk b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/vied_parameters.mk new file mode 100644 index 000000000000..834a1a4b2bab --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/lib/vied_parameters/vied_parameters.mk @@ -0,0 +1,76 @@ +# # # +# Support for Intel Camera Imaging ISP subsystem. +# Copyright (c) 2010 - 2018, Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details +# +# +# MODULE is VIED_PARAMETERS + +VIED_PARAMETERS_DIR=$${MODULES_DIR}/vied_parameters + +VIED_PARAMETERS_INTERFACE=$(VIED_PARAMETERS_DIR)/interface +VIED_PARAMETERS_SOURCES=$(VIED_PARAMETERS_DIR)/src +VIED_PARAMETERS_EXTINCLUDE = $${MODULES_DIR}/support + +VIED_PARAMETERS_DYNAMIC_HOST_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal.c +VIED_PARAMETERS_STATIC_HOST_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal_manifest.c + +VIED_PARAMETERS_HOST_FILES = $(VIED_PARAMETERS_DYNAMIC_HOST_FILES) +VIED_PARAMETERS_HOST_FILES += $(VIED_PARAMETERS_STATIC_HOST_FILES) + +VIED_PARAMETERS_ISA_CLIENT_HOST_FILES = $(VIED_PARAMETERS_SOURCES)/ia_css_isys_process_group.c +VIED_PARAMETERS_ISA_CLIENT_HOST_FILES += $(VIED_PARAMETERS_DIR)/client/ia_css_isys_parameter_client.c + +VIED_PARAMETERS_DYNAMIC_FW_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal.c +VIED_PARAMETERS_STATIC_FW_FILES += $(VIED_PARAMETERS_SOURCES)/ia_css_terminal_manifest.c + +VIED_PARAMETERS_FW_FILES = $(VIED_PARAMETERS_DYNAMIC_HOST_FILES) +VIED_PARAMETERS_FW_FILES += $(VIED_PARAMETERS_STATIC_HOST_FILES) +VIED_PARAMETERS_SUPPORT_CPPFLAGS = -I$(VIED_PARAMETERS_DIR)/support +VIED_PARAMETERS_SUPPORT_CPPFLAGS += -I$(VIED_PARAMETERS_DIR)/support/$(IPU_SYSVER) +VIED_PARAMETERS_ISA_CLIENT_HOST_CPPFLAGS = -I$(VIED_PARAMETERS_DIR)/client +VIED_PARAMETERS_PSA_UTILS_HOST_FILES = $(MODULES_DIR)/vied_parameters/support/ia_css_psys_parameter_utils.c +VIED_PARAMETERS_PSA_UTILS_HOST_FILES += $(MODULES_DIR)/vied_parameters/support/$(IPU_SYSVER)/ia_css_psys_parameter_utils_dep.c + +VIED_PARAMETERS_UTILS_HOST_CPPFLAGS = $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) + +VIED_PARAMETERS_ISA_UTILS_HOST_FILES = $(MODULES_DIR)/vied_parameters/support/ia_css_isys_parameter_utils.c +VIED_PARAMETERS_ISA_UTILS_HOST_FILES += $(MODULES_DIR)/vied_parameters/support/$(IPU_SYSVER)/ia_css_isys_parameter_utils_dep.c + +VIED_PARAMETERS_PRINT_CPPFLAGS += -I$(VIED_PARAMETERS_DIR)/print/interface +VIED_PARAMETERS_PRINT_FILES += $(VIED_PARAMETERS_DIR)/print/src/ia_css_terminal_print.c + +# VIED_PARAMETERS Trace Log Level = VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +# Other options are [VIED_PARAMETERS_TRACE_LOG_LEVEL_OFF, VIED_PARAMETERS_TRACE_LOG_LEVEL_DEBUG] +ifndef VIED_PARAMETERS_TRACE_CONFIG_HOST + VIED_PARAMETERS_TRACE_CONFIG_HOST=VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +endif +ifndef VIED_PARAMETERS_TRACE_CONFIG_FW + VIED_PARAMETERS_TRACE_CONFIG_FW=VIED_PARAMETERS_TRACE_LOG_LEVEL_NORMAL +endif + +VIED_PARAMETERS_HOST_CPPFLAGS += -DVIED_PARAMETERS_TRACE_CONFIG=$(VIED_PARAMETERS_TRACE_CONFIG_HOST) +VIED_PARAMETERS_FW_CPPFLAGS += -DVIED_PARAMETERS_TRACE_CONFIG=$(VIED_PARAMETERS_TRACE_CONFIG_FW) + +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_INTERFACE) +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_SOURCES) +VIED_PARAMETERS_HOST_CPPFLAGS += -I$(VIED_PARAMETERS_EXTINCLUDE) +VIED_PARAMETERS_HOST_CPPFLAGS += $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_INTERFACE) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_SOURCES) +VIED_PARAMETERS_FW_CPPFLAGS += -I$(VIED_PARAMETERS_EXTINCLUDE) +VIED_PARAMETERS_FW_CPPFLAGS += $(VIED_PARAMETERS_SUPPORT_CPPFLAGS) + +#For IPU interface +include $(MODULES_DIR)/fw_abi_common_types/cpu/fw_abi_cpu_types.mk +VIED_PARAMETERS_HOST_CPPFLAGS += $(FW_ABI_COMMON_TYPES_HOST_CPPFLAGS) + +VIED_PARAMETERS_FW_CPPFLAGS += $(FW_ABI_COMMON_TYPES_FW_CPPFLAGS) diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.c b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.c new file mode 100644 index 000000000000..805d1542c163 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2015--2018 Intel Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include + +#include "ipu.h" +#include "ipu-mmu.h" +#include "ipu-psys.h" +#include "ipu-wrapper.h" +#include "ipu-fw-psys.h" +#include "libcsspsys2600.h" + +#include +#include +#include +#include +#include + +int ipu_fw_psys_pg_start(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_start((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_start); + +int ipu_fw_psys_pg_disown(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_disown((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_disown); + +int ipu_fw_psys_pg_abort(struct ipu_psys_kcmd *kcmd) +{ + int rval; + + rval = ia_css_process_group_stop((ia_css_process_group_t *) + kcmd->kpg->pg); + if (rval) { + dev_err(&kcmd->fh->psys->adev->dev, + "failed to abort kcmd!\n"); + kcmd->pg_user = NULL; + rval = -EIO; + /* TODO: need to reset PSYS by power cycling it */ + } + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_abort); + +int ipu_fw_psys_pg_submit(struct ipu_psys_kcmd *kcmd) +{ + return -ia_css_process_group_submit((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_submit); + +static void *syscom_buffer; +static struct ia_css_syscom_config *syscom_config; +static struct ia_css_psys_server_init *server_init; + +int ipu_fw_psys_rcv_event(struct ipu_psys *psys, + struct ipu_fw_psys_event *event) +{ + return ia_css_psys_event_queue_receive(psys_syscom, + IA_CSS_PSYS_EVENT_QUEUE_MAIN_ID, + (struct ia_css_psys_event_s *)event); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_rcv_event); + +int ipu_fw_psys_terminal_set(struct ipu_fw_psys_terminal *terminal, + int terminal_idx, + struct ipu_psys_kcmd *kcmd, + u32 buffer, + unsigned size) +{ + ia_css_terminal_type_t type; + u32 buffer_state; + + type = ia_css_terminal_get_type((ia_css_terminal_t *)terminal); + + switch (type) { + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_CACHED_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SPATIAL_OUT: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_IN: + case IA_CSS_TERMINAL_TYPE_PARAM_SLICED_OUT: + case IA_CSS_TERMINAL_TYPE_PROGRAM: + buffer_state = IA_CSS_BUFFER_UNDEFINED; + break; + case IA_CSS_TERMINAL_TYPE_PARAM_STREAM: + case IA_CSS_TERMINAL_TYPE_DATA_IN: + case IA_CSS_TERMINAL_TYPE_STATE_IN: + buffer_state = IA_CSS_BUFFER_FULL; + break; + case IA_CSS_TERMINAL_TYPE_DATA_OUT: + case IA_CSS_TERMINAL_TYPE_STATE_OUT: + buffer_state = IA_CSS_BUFFER_EMPTY; + break; + default: + dev_err(&kcmd->fh->psys->adev->dev, + "unknown terminal type: 0x%x\n", type); + return -EAGAIN; + } + + if (type == IA_CSS_TERMINAL_TYPE_DATA_IN || + type == IA_CSS_TERMINAL_TYPE_DATA_OUT) { + ia_css_frame_t *frame; + + if (ia_css_data_terminal_set_connection_type( + (ia_css_data_terminal_t *)terminal, + IA_CSS_CONNECTION_MEMORY)) + return -EIO; + frame = ia_css_data_terminal_get_frame( + (ia_css_data_terminal_t *)terminal); + if (!frame) + return -EIO; + + if (ia_css_frame_set_data_bytes(frame, size)) + return -EIO; + } + + return -ia_css_process_group_attach_buffer( + (ia_css_process_group_t *)kcmd->kpg->pg, buffer, + buffer_state, terminal_idx); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_terminal_set); + +void ipu_fw_psys_pg_dump(struct ipu_psys *psys, + struct ipu_psys_kcmd *kcmd, + const char *note) +{ + ia_css_process_group_t *pg = (ia_css_process_group_t *)kcmd->kpg->pg; + ia_css_program_group_ID_t pgid = + ia_css_process_group_get_program_group_ID(pg); + uint8_t processes = ia_css_process_group_get_process_count( + (ia_css_process_group_t *)kcmd->kpg->pg); + unsigned int p, chn, mem; + + dev_dbg(&psys->adev->dev, "%s %s pgid %i has %i processes\n", + __func__, note, pgid, processes); + for (p = 0; p < processes; p++) { + ia_css_process_t *process = + ia_css_process_group_get_process(pg, p); + int cell = ia_css_process_get_cell(process); + dev_dbg(&psys->adev->dev, + "%s pgid %i process %i cell %i cell_bitmap = 0x%x size = %zu\n", + __func__, pgid, p, + cell, + ia_css_process_get_cells_bitmap(process), + ia_css_process_get_size(process)); + dev_dbg(&psys->adev->dev, + "%s pgid %i process %i kernel bitmap 0x%llx \n", + __func__, pgid, p, + ia_css_process_get_kernel_bitmap(process)); + for (mem = 0; mem < VIED_NCI_N_DATA_MEM_TYPE_ID; mem++ ) { + unsigned int mem_id = process->ext_mem_id[mem]; + dev_dbg(&psys->adev->dev, + "%s pgid %i process %i index %u type %d id %d offset 0x%x \n", + __func__, pgid, p, mem, + vied_nci_cell_get_mem_type(cell, mem), + mem_id, process->ext_mem_offset[mem]); + } + for (chn = 0; chn < VIED_NCI_N_DEV_CHN_ID; chn++ ) { + dev_dbg(&psys->adev->dev, + "%s pgid %i process %i dev_chn[%u] = %i\n", + __func__, pgid, p, chn, + ia_css_process_get_dev_chn(process, chn)); + } + } +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_dump); + +int ipu_fw_psys_pg_get_id(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_program_group_ID( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_id); + +int ipu_fw_psys_pg_get_terminal_count(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_terminal_count( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_terminal_count); + +int ipu_fw_psys_pg_get_size(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_size((ia_css_process_group_t *) + kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_size); + +int ipu_fw_psys_pg_set_ipu_vaddress(struct ipu_psys_kcmd *kcmd, + dma_addr_t vaddress) +{ + return ia_css_process_group_set_ipu_vaddress((ia_css_process_group_t *) + kcmd->kpg->pg, vaddress); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_set_ipu_vaddress); + +int ipu_fw_psys_pg_load_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_load_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_load_cycles); + +int ipu_fw_psys_pg_init_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_init_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_init_cycles); + +int ipu_fw_psys_pg_processing_cycles(struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_pg_processing_cycles( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_processing_cycles); + +struct ipu_fw_psys_terminal * +ipu_fw_psys_pg_get_terminal(struct ipu_psys_kcmd *kcmd, int index) +{ + return (struct ipu_fw_psys_terminal *)ia_css_process_group_get_terminal( + (ia_css_process_group_t *)kcmd->kpg->pg, index); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_terminal); + +void ipu_fw_psys_pg_set_token(struct ipu_psys_kcmd *kcmd, u64 token) +{ + ia_css_process_group_set_token((ia_css_process_group_t *)kcmd->kpg->pg, + token); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_set_token); + +int ipu_fw_psys_pg_get_protocol( + struct ipu_psys_kcmd *kcmd) +{ + return ia_css_process_group_get_protocol_version( + (ia_css_process_group_t *)kcmd->kpg->pg); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_protocol); + +static int libcsspsys2600_init(void); +int ipu_fw_psys_open(struct ipu_psys *psys) +{ + bool opened; + int retry = IPU_PSYS_OPEN_RETRY; + + ipu_wrapper_init(PSYS_MMID, &psys->adev->dev, + psys->pdata->base); + /* When fw psys open, make sure csslib init first */ + libcsspsys2600_init(); + + server_init->icache_prefetch_sp = psys->icache_prefetch_sp; + server_init->icache_prefetch_isp = psys->icache_prefetch_isp; + + psys_syscom = ia_css_psys_open(syscom_buffer, syscom_config); + if (!psys_syscom) { + dev_err(&psys->adev->dev, + "psys library open failed\n"); + return -ENODEV; + } + do { + opened = ia_css_psys_open_is_ready(psys_syscom); + if (opened) + break; + usleep_range(IPU_PSYS_OPEN_TIMEOUT_US, + IPU_PSYS_OPEN_TIMEOUT_US + 10); + retry--; + } while (retry > 0); + + if (!retry && !opened) { + dev_err(&psys->adev->dev, + "psys library open ready failed\n"); + ia_css_psys_close(psys_syscom); + ia_css_psys_release(psys_syscom, 1); + psys_syscom = NULL; + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_open); + +int ipu_fw_psys_close(struct ipu_psys *psys) +{ + int rval; + unsigned int retry = IPU_PSYS_CLOSE_TIMEOUT; + + if (!psys_syscom) + return 0; + + if (ia_css_psys_close(psys_syscom)) { + dev_err(&psys->adev->dev, + "psys library close ready failed\n"); + return 0; + } + + do { + rval = ia_css_psys_release(psys_syscom, 0); + if (rval && rval != -EBUSY) { + dev_dbg(&psys->adev->dev, "psys library release failed\n"); + break; + } + usleep_range(IPU_PSYS_CLOSE_TIMEOUT_US, + IPU_PSYS_CLOSE_TIMEOUT_US + 10); + } while (rval && --retry); + + psys_syscom = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_close); + +u64 ipu_fw_psys_pg_get_token(struct ipu_psys_kcmd *kcmd) +{ + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_pg_get_token); + +static const struct ipu_fw_resource_definitions default_defs = { + .cells = vied_nci_cell_type, + .num_cells = VIED_NCI_N_CELL_ID, + .num_cells_type = VIED_NCI_N_CELL_TYPE_ID, + .dev_channels = vied_nci_dev_chn_size, + .num_dev_channels = VIED_NCI_N_DEV_CHN_ID, + + .num_ext_mem_types = VIED_NCI_N_DATA_MEM_TYPE_ID, + .num_ext_mem_ids = VIED_NCI_N_MEM_ID, + .ext_mem_ids = vied_nci_mem_size, + + .cell_mem_row = VIED_NCI_N_MEM_TYPE_ID, + .cell_mem = (enum ipu_mem_id *)vied_nci_cell_mem, +}; + +const struct ipu_fw_resource_definitions *res_defs = &default_defs; +EXPORT_SYMBOL_GPL(res_defs); + +int ipu_fw_psys_set_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index, + u8 value) +{ + return ia_css_process_set_cell((ia_css_process_t *)ptr, + (vied_nci_cell_ID_t)value); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_cell_id); + +u8 ipu_fw_psys_get_process_cell_id(struct ipu_fw_psys_process *ptr, u8 index) +{ + return ia_css_process_get_cell((ia_css_process_t *)ptr); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_get_process_cell_id); + +int ipu_fw_psys_clear_process_cell(struct ipu_fw_psys_process *ptr) +{ + return ia_css_process_clear_cell((ia_css_process_t *)ptr); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_clear_process_cell); + +int ipu_fw_psys_set_process_dev_chn_offset(struct ipu_fw_psys_process *ptr, + u16 offset, u16 value) +{ + return ia_css_process_set_dev_chn((ia_css_process_t *)ptr, + (vied_nci_dev_chn_ID_t)offset, + (vied_nci_resource_size_t)value); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_dev_chn_offset); + +int ipu_fw_psys_set_process_ext_mem(struct ipu_fw_psys_process *ptr, + u16 type_id, u16 mem_id, u16 offset) +{ + return ia_css_process_set_ext_mem((ia_css_process_t *)ptr, mem_id, offset); +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_set_process_ext_mem); + +int ipu_fw_psys_get_program_manifest_by_process( + struct ipu_fw_generic_program_manifest *gen_pm, + const struct ipu_fw_psys_program_group_manifest *pg_manifest, + struct ipu_fw_psys_process *process) +{ + ia_css_program_ID_t process_id = + ia_css_process_get_program_ID( + (const ia_css_process_t *)process); + int programs = + ia_css_program_group_manifest_get_program_count( + (const ia_css_program_group_manifest_t *)pg_manifest); + int i; + + for (i = 0; i < programs; i++) { + ia_css_program_ID_t program_id; + ia_css_program_manifest_t *pm = + ia_css_program_group_manifest_get_prgrm_mnfst( + (const ia_css_program_group_manifest_t *) + pg_manifest, i); + if (!pm) + continue; + program_id = ia_css_program_manifest_get_program_ID(pm); + if (program_id == process_id) { + gen_pm->dev_chn_size = (u16 *)pm->dev_chn_size; + gen_pm->ext_mem_size = (u16 *)pm->ext_mem_size; + gen_pm->cell_id = pm->cell_id; + gen_pm->cell_type_id = pm->cell_type_id; + return 0; + } + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ipu_fw_psys_get_program_manifest_by_process); + +static int libcsspsys2600_init(void) +{ + int rval; + static bool csslib_init; + + if (csslib_init) + return 0; + + syscom_buffer = kzalloc(ia_css_sizeof_psys(NULL), GFP_KERNEL); + if (!syscom_buffer) + return -ENOMEM; + + syscom_config = kzalloc(sizeof(struct ia_css_syscom_config), + GFP_KERNEL); + if (!syscom_config) { + rval = -ENOMEM; + goto out_syscom_buffer_free; + } + + server_init = kzalloc(sizeof(struct ia_css_psys_server_init), + GFP_KERNEL); + if (!server_init) { + rval = -ENOMEM; + goto out_syscom_config_free; + } + + server_init->ddr_pkg_dir_address = 0; + server_init->host_ddr_pkg_dir = 0; + server_init->pkg_dir_size = 0; + + *syscom_config = *ia_css_psys_specify(); + syscom_config->specific_addr = server_init; + syscom_config->specific_size = sizeof(struct ia_css_psys_server_init); + syscom_config->ssid = PSYS_SSID; + syscom_config->mmid = PSYS_MMID; + syscom_config->regs_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_REGS); + syscom_config->dmem_addr = ipu_device_cell_memory_address(SPC0, + IPU_DEVICE_SP2600_CONTROL_DMEM); + csslib_init = true; + + return 0; + +out_syscom_config_free: + kfree(syscom_config); +out_syscom_buffer_free: + kfree(syscom_buffer); + + return rval; +} + +static void __exit libcsspsys2600_exit(void) +{ + kfree(syscom_buffer); + kfree(syscom_config); + kfree(server_init); +} + +module_init(libcsspsys2600_init); +module_exit(libcsspsys2600_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu psys css library"); diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.h b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.h new file mode 100644 index 000000000000..b8d790f56180 --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/lib2600psys/libcsspsys2600.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015--2018 Intel Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LIBCSSPSYS2600_H +#define LIBCSSPSYS2600_H + +#include +#include +#include +#include +#include +#include +#include + +extern struct ia_css_syscom_context *psys_syscom; +#endif diff --git a/drivers/media/pci/intel/ipu4/ipu4p-css/libintel-ipu4p.c b/drivers/media/pci/intel/ipu4/ipu4p-css/libintel-ipu4p.c new file mode 100644 index 000000000000..cb6fd0499c5c --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-css/libintel-ipu4p.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include +#include +#include "ipu-isys.h" +#include "ipu-wrapper.h" +#include + +#include "ipu-platform.h" + +#define ipu_lib_call_notrace_unlocked(func, isys, ...) \ + ({ \ + int rval; \ + \ + rval = -ia_css_isys_##func((isys)->fwcom, ##__VA_ARGS__); \ + \ + rval; \ + }) + +#define ipu_lib_call_notrace(func, isys, ...) \ + ({ \ + int rval; \ + \ + mutex_lock(&(isys)->lib_mutex); \ + \ + rval = ipu_lib_call_notrace_unlocked( \ + func, isys, ##__VA_ARGS__); \ + \ + mutex_unlock(&(isys)->lib_mutex); \ + \ + rval; \ + }) + +#define ipu_lib_call(func, isys, ...) \ + ({ \ + int rval; \ + dev_dbg(&(isys)->adev->dev, "hostlib: libcall %s\n", #func); \ + rval = ipu_lib_call_notrace(func, isys, ##__VA_ARGS__); \ + \ + rval; \ + }) + +static int wrapper_init_done; + +int ipu_fw_isys_close(struct ipu_isys *isys) +{ + struct device *dev = &isys->adev->dev; + int timeout = IPU_ISYS_TURNOFF_TIMEOUT; + int rval; + unsigned long flags; + + /* + * Ask library to stop the isys fw. Actual close takes + * some time as the FW must stop its actions including code fetch + * to SP icache. + */ + spin_lock_irqsave(&isys->power_lock, flags); + rval = ipu_lib_call(device_close, isys); + spin_unlock_irqrestore(&isys->power_lock, flags); + if (rval) + dev_err(dev, "Device close failure: %d\n", rval); + + /* release probably fails if the close failed. Let's try still */ + do { + usleep_range(IPU_ISYS_TURNOFF_DELAY_US, + 2 * IPU_ISYS_TURNOFF_DELAY_US); + rval = ipu_lib_call_notrace(device_release, isys, 0); + timeout--; + } while (rval != 0 && timeout); + + /* Spin lock to wait the interrupt handler to be finished */ + spin_lock_irqsave(&isys->power_lock, flags); + if (!rval) + isys->fwcom = NULL; /* No further actions needed */ + else + dev_err(dev, "Device release time out %d\n", rval); + spin_unlock_irqrestore(&isys->power_lock, flags); + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_close); + +int ipu_fw_isys_init(struct ipu_isys *isys, + unsigned int num_streams) +{ + int retry = IPU_ISYS_OPEN_RETRY; + unsigned int i; + + struct ia_css_isys_device_cfg_data isys_cfg = { + .driver_sys = { + .ssid = ISYS_SSID, + .mmid = ISYS_MMID, + .num_send_queues = clamp_t( + unsigned int, num_streams, 1, + IPU_ISYS_NUM_STREAMS), + .num_recv_queues = IPU_ISYS_NUM_RECV_QUEUE, + .send_queue_size = IPU_ISYS_SIZE_SEND_QUEUE, + .recv_queue_size = IPU_ISYS_SIZE_RECV_QUEUE, + .icache_prefetch = isys->icache_prefetch, + }, + }; + struct device *dev = &isys->adev->dev; + int rval; + + if (!wrapper_init_done) { + wrapper_init_done = true; + ipu_wrapper_init(ISYS_MMID, &isys->adev->dev, + isys->pdata->base); + } + + /* + * SRAM partitioning. Initially equal partitioning is set + * TODO: Fine tune the partitining based on the stream pixel load + */ + for (i = 0; i < min(IPU_NOF_SRAM_BLOCKS_MAX, + NOF_SRAM_BLOCKS_MAX); i++) { + if (i < isys_cfg.driver_sys.num_send_queues) + isys_cfg.buffer_partition.num_gda_pages[i] = + (IPU_DEVICE_GDA_NR_PAGES * + IPU_DEVICE_GDA_VIRT_FACTOR) / + isys_cfg.driver_sys.num_send_queues; + else + isys_cfg.buffer_partition.num_gda_pages[i] = 0; + } + + rval = -ia_css_isys_device_open(&isys->fwcom, &isys_cfg); + if (rval < 0) { + dev_err(dev, "isys device open failed %d\n", rval); + return rval; + } + + do { + usleep_range(IPU_ISYS_OPEN_TIMEOUT_US, + IPU_ISYS_OPEN_TIMEOUT_US + 10); + rval = ipu_lib_call(device_open_ready, isys); + if (!rval) + break; + retry--; + } while (retry > 0); + + if (!retry && rval) { + dev_err(dev, "isys device open ready failed %d\n", rval); + ipu_fw_isys_close(isys); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_init); + +void ipu_fw_isys_cleanup(struct ipu_isys *isys) +{ + ipu_lib_call(device_release, isys, 1); + isys->fwcom = NULL; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_cleanup); + +struct ipu_fw_isys_resp_info_abi *ipu_fw_isys_get_resp( + void *context, unsigned int queue, + struct ipu_fw_isys_resp_info_abi *response) +{ + struct ia_css_isys_resp_info apiresp; + int rval; + + rval = -ia_css_isys_stream_handle_response(context, &apiresp); + if (rval < 0) + return NULL; + + response->buf_id = 0; + response->type = apiresp.type; + response->timestamp[0] = apiresp.timestamp[0]; + response->timestamp[1] = apiresp.timestamp[1]; + response->stream_handle = apiresp.stream_handle; + response->error_info.error = apiresp.error; + response->error_info.error_details = apiresp.error_details; + response->pin.out_buf_id = apiresp.pin.out_buf_id; + response->pin.addr = apiresp.pin.addr; + response->pin_id = apiresp.pin_id; + response->process_group_light.param_buf_id = + apiresp.process_group_light.param_buf_id; + response->process_group_light.addr = + apiresp.process_group_light.addr; + response->acc_id = apiresp.acc_id; +#ifdef IPU_OTF_SUPPORT + response->frame_counter = apiresp.frame_counter; + response->written_direct = apiresp.written_direct; +#endif + + return response; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_get_resp); + +void ipu_fw_isys_put_resp(void *context, unsigned int queue) +{ + /* Nothing to do here really */ +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_put_resp); + +int ipu_fw_isys_simple_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + enum ipu_fw_isys_send_type send_type) +{ + int rval = -1; + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_START: + rval = ipu_lib_call(stream_start, isys, stream_handle, NULL); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_FLUSH: + rval = ipu_lib_call(stream_flush, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_STOP: + rval = ipu_lib_call(stream_stop, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE: + rval = ipu_lib_call(stream_close, isys, stream_handle); + break; + default: + WARN_ON(1); + } + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_simple_cmd); + +static void resolution_abi_to_api(const struct ipu_fw_isys_resolution_abi *abi, + struct ia_css_isys_resolution *api) +{ + api->width = abi->width; + api->height = abi->height; +} + +static void output_pin_payload_abi_to_api( + struct ipu_fw_isys_output_pin_payload_abi *abi, + struct ia_css_isys_output_pin_payload *api) +{ + api->out_buf_id = abi->out_buf_id; + api->addr = abi->addr; +} + +static void output_pin_info_abi_to_api( + struct ipu_fw_isys_output_pin_info_abi *abi, + struct ia_css_isys_output_pin_info *api) +{ + api->input_pin_id = abi->input_pin_id; + resolution_abi_to_api(&abi->output_res, &api->output_res); + api->stride = abi->stride; + api->pt = abi->pt; + api->watermark_in_lines = abi->watermark_in_lines; + api->payload_buf_size = abi->payload_buf_size; + api->send_irq = abi->send_irq; + api->ft = abi->ft; +#ifdef IPU_OTF_SUPPORT + api->link_id = abi->link_id; +#endif + api->reserve_compression = abi->reserve_compression; +} + +static void param_pin_abi_to_api(struct ipu_fw_isys_param_pin_abi *abi, + struct ia_css_isys_param_pin *api) +{ + api->param_buf_id = abi->param_buf_id; + api->addr = abi->addr; +} + +static void input_pin_info_abi_to_api( + struct ipu_fw_isys_input_pin_info_abi *abi, + struct ia_css_isys_input_pin_info *api) +{ + resolution_abi_to_api(&abi->input_res, &api->input_res); + api->dt = abi->dt; + api->mipi_store_mode = abi->mipi_store_mode; + api->mapped_dt = abi->mapped_dt; +} + +static void isa_cfg_abi_to_api(const struct ipu_fw_isys_isa_cfg_abi *abi, + struct ia_css_isys_isa_cfg *api) +{ + unsigned int i; + + for (i = 0; i < min(N_IPU_FW_ISYS_RESOLUTION_INFO, + N_IA_CSS_ISYS_RESOLUTION_INFO); i++) + resolution_abi_to_api(&abi->isa_res[i], &api->isa_res[i]); + + api->blc_enabled = abi->cfg.blc; + api->lsc_enabled = abi->cfg.lsc; + api->dpc_enabled = abi->cfg.dpc; + api->downscaler_enabled = abi->cfg.downscaler; + api->awb_enabled = abi->cfg.awb; + api->af_enabled = abi->cfg.af; + api->ae_enabled = abi->cfg.ae; + api->paf_type = abi->cfg.paf; + api->send_irq_stats_ready = abi->cfg.send_irq_stats_ready; + api->send_resp_stats_ready = abi->cfg.send_irq_stats_ready; +} + +static void cropping_abi_to_api(struct ipu_fw_isys_cropping_abi *abi, + struct ia_css_isys_cropping *api) +{ + api->top_offset = abi->top_offset; + api->left_offset = abi->left_offset; + api->bottom_offset = abi->bottom_offset; + api->right_offset = abi->right_offset; +} + +static void stream_cfg_abi_to_api(struct ipu_fw_isys_stream_cfg_data_abi *abi, + struct ia_css_isys_stream_cfg_data *api) +{ + unsigned int i; + + api->src = abi->src; + api->vc = abi->vc; + api->isl_use = abi->isl_use; + api->compfmt = abi->compfmt; + isa_cfg_abi_to_api(&abi->isa_cfg, &api->isa_cfg); + for (i = 0; i < min(N_IPU_FW_ISYS_CROPPING_LOCATION, + N_IA_CSS_ISYS_CROPPING_LOCATION); i++) + cropping_abi_to_api(&abi->crop[i], &api->crop[i]); + + api->send_irq_sof_discarded = abi->send_irq_sof_discarded; + api->send_irq_eof_discarded = abi->send_irq_eof_discarded; + api->send_resp_sof_discarded = abi->send_irq_sof_discarded; + api->send_resp_eof_discarded = abi->send_irq_eof_discarded; + api->nof_input_pins = abi->nof_input_pins; + api->nof_output_pins = abi->nof_output_pins; + for (i = 0; i < abi->nof_input_pins; i++) + input_pin_info_abi_to_api(&abi->input_pins[i], + &api->input_pins[i]); + + for (i = 0; i < abi->nof_output_pins; i++) + output_pin_info_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); +} + +static void frame_buff_set_abi_to_api( + struct ipu_fw_isys_frame_buff_set_abi *abi, + struct ia_css_isys_frame_buff_set *api) +{ + int i; + + for (i = 0; i < min(IPU_MAX_OPINS, MAX_OPINS); i++) + output_pin_payload_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); + + param_pin_abi_to_api(&abi->process_group_light, + &api->process_group_light); + + api->send_irq_sof = abi->send_irq_sof; + api->send_irq_eof = abi->send_irq_eof; + api->send_irq_capture_ack = abi->send_irq_capture_ack; + api->send_irq_capture_done = abi->send_irq_capture_done; +} + +int ipu_fw_isys_complex_cmd(struct ipu_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, + enum ipu_fw_isys_send_type send_type) +{ + union { + struct ia_css_isys_stream_cfg_data stream_cfg; + struct ia_css_isys_frame_buff_set buf; + } param; + int rval = -1; + + memset(¶m, 0, sizeof(param)); + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_capture_indication, + isys, stream_handle, ¶m.buf); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_OPEN: + stream_cfg_abi_to_api(cpu_mapped_buf, ¶m.stream_cfg); + rval = ipu_lib_call(stream_open, isys, stream_handle, + ¶m.stream_cfg); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_start, isys, stream_handle, + ¶m.buf); + break; + default: + WARN_ON(1); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_complex_cmd); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu library"); diff --git a/drivers/media/pci/intel/ipu4/ipu4p-isys-csi2.c b/drivers/media/pci/intel/ipu4/ipu4p-isys-csi2.c new file mode 100644 index 000000000000..580a90835bbb --- /dev/null +++ b/drivers/media/pci/intel/ipu4/ipu4p-isys-csi2.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include "ipu.h" +#include "ipu-buttress.h" +#include "ipu-isys.h" +#include "ipu-isys-csi2.h" +#include "ipu-platform-isys-csi2-reg.h" +#include "ipu-platform-regs.h" +#include "ipu-trace.h" +#include "ipu-isys-csi2.h" + +#define CSI2_UPDATE_TIME_TRY_NUM 3 +#define CSI2_UPDATE_TIME_MAX_DIFF 20 + +static int ipu4p_csi2_ev_correction_params(struct ipu_isys_csi2 + *csi2, unsigned int lanes) +{ + /* + * TBD: add implementation for ipu4p + * probably re-use ipu4 implementation + */ + return 0; +} + +static void ipu4p_isys_register_errors(struct ipu_isys_csi2 *csi2) +{ + u32 status; + unsigned int index; + struct ipu_isys *isys = csi2->isys; + void __iomem *isys_base = isys->pdata->base; + + index = csi2->index; + status = readl(isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(index) + 0x8); + writel(status, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(index) + 0xc); + + status &= 0xffff; + dev_dbg(&isys->adev->dev, "csi %d rxsync status 0x%x", index, status); + csi2->receiver_errors |= status; +} + +void ipu_isys_csi2_error(struct ipu_isys_csi2 *csi2) +{ + /* + * Strings corresponding to CSI-2 receiver errors are here. + * Corresponding macros are defined in the header file. + */ + static const struct ipu_isys_csi2_error { + const char *error_string; + bool is_info_only; + } errors[] = { + {"Single packet header error corrected", true}, + {"Multiple packet header errors detected", true}, + {"Payload checksum (CRC) error", true}, + {"FIFO overflow", false}, + {"Reserved short packet data type detected", true}, + {"Reserved long packet data type detected", true}, + {"Incomplete long packet detected", false}, + {"Frame sync error", false}, + {"Line sync error", false}, + {"DPHY recoverable synchronization error", true}, + {"DPHY non-recoverable synchronization error", false}, + {"Escape mode error", true}, + {"Escape mode trigger event", true}, + {"Escape mode ultra-low power state for data lane(s)", true}, + {"Escape mode ultra-low power state exit for clock lane", true}, + {"Inter-frame short packet discarded", true}, + {"Inter-frame long packet discarded", true}, + }; + u32 status; + unsigned int i; + + /* Register errors once more in case of error interrupts are disabled */ + ipu4p_isys_register_errors(csi2); + status = csi2->receiver_errors; + csi2->receiver_errors = 0; + + for (i = 0; i < ARRAY_SIZE(errors); i++) { + if (status & BIT(i)) { + if (errors[i].is_info_only) + dev_dbg(&csi2->isys->adev->dev, + "csi2-%i info: %s\n", + csi2->index, errors[i].error_string); + else + dev_err_ratelimited(&csi2->isys->adev->dev, + "csi2-%i error: %s\n", + csi2->index, + errors[i].error_string); + } + } +} + +int ipu_isys_csi2_set_stream(struct v4l2_subdev *sd, + struct ipu_isys_csi2_timing timing, + unsigned int nlanes, int enable) +{ + struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); + struct ipu_isys *isys = csi2->isys; + void __iomem *isys_base = isys->pdata->base; + unsigned int i; + u32 val, csi2part = 0; + + dev_dbg(&csi2->isys->adev->dev, "csi2 s_stream %d\n", enable); + if (!enable) { + ipu_isys_csi2_error(csi2); + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + val &= ~(CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING | + CSI2_CSI_RX_CONFIG_RELEASE_LP11); + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + writel(0, csi2->base + CSI2_REG_CSI_RX_ENABLE); + + writel(0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + 0x4); + writel(0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + + 0x10); + writel + (0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0x4); + writel + (0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0x10); + return 0; + } + + ipu4p_csi2_ev_correction_params(csi2, nlanes); + + writel(timing.ctermen, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_CLANE); + writel(timing.csettle, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_CLANE); + + for (i = 0; i < nlanes; i++) { + writel + (timing.dtermen, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_DLANE(i)); + writel + (timing.dsettle, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_DLANE(i)); + } + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + val |= CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING | + CSI2_CSI_RX_CONFIG_RELEASE_LP11; + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + writel(nlanes, csi2->base + CSI2_REG_CSI_RX_NOF_ENABLED_LANES); + writel(CSI2_CSI_RX_ENABLE_ENABLE, + csi2->base + CSI2_REG_CSI_RX_ENABLE); + +#ifdef IPU_VC_SUPPORT + /* SOF of VC0-VC3 enabled from CSI2PART register in B0 */ + for (i = 0; i < NR_OF_CSI2_VC; i++) + csi2part |= CSI2_IRQ_FS_VC(i) | CSI2_IRQ_FE_VC(i); +#else + csi2part |= CSI2_IRQ_FS_VC | CSI2_IRQ_FE_VC; +#endif + + /* Enable csi2 receiver error interrupts */ + writel(1, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index)); + writel(0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + 0x14); + writel(0xffffffff, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + 0xc); + writel(1, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + 0x4); + writel(1, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(csi2->index) + 0x10); + + csi2part |= 0xffff; + writel(csi2part, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index)); + writel(0, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0x14); + writel(0xffffffff, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0xc); + writel(csi2part, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0x4); + writel(csi2part, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(csi2->index) + 0x10); + + return 0; +} + +void ipu_isys_csi2_isr(struct ipu_isys_csi2 *csi2) +{ + u32 status = 0; +#ifdef IPU_VC_SUPPORT + unsigned int i, bus; +#else + unsigned int bus; +#endif + struct ipu_isys *isys = csi2->isys; + void __iomem *isys_base = isys->pdata->base; + + bus = csi2->index; + /* handle ctrl and ctrl0 irq */ + status = readl(isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(bus) + 0x8); + writel(status, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL_BASE(bus) + 0xc); + dev_dbg(&isys->adev->dev, "csi %d irq_ctrl status 0x%x", bus, status); + + if (!(status & BIT(0))) + return; + + status = readl(isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(bus) + 0x8); + writel(status, isys_base + + IPU_REG_ISYS_CSI_IRQ_CTRL0_BASE(bus) + 0xc); + dev_dbg(&isys->adev->dev, "csi %d irq_ctrl0 status 0x%x", bus, status); + /* register the csi sync error */ + csi2->receiver_errors |= status & 0xffff; + /* handle sof and eof event */ +#ifdef IPU_VC_SUPPORT + for (i = 0; i < NR_OF_CSI2_VC; i++) { + if (status & CSI2_IRQ_FS_VC(i)) + ipu_isys_csi2_sof_event(csi2, i); + + if (status & CSI2_IRQ_FE_VC(i)) + ipu_isys_csi2_eof_event(csi2, i); + } +#else + if (status & CSI2_IRQ_FS_VC) + ipu_isys_csi2_sof_event(csi2); + if (status & CSI2_IRQ_FE_VC) + ipu_isys_csi2_eof_event(csi2); +#endif +} + +static u64 tunit_time_to_us(struct ipu_isys *isys, u64 time) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 1000000; + + do_div(time, isys_clk); + + return time; +} + +static u64 tsc_time_to_tunit_time(struct ipu_isys *isys, + u64 tsc_base, u64 tunit_base, u64 tsc_time) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 100000; + u64 tsc_clk = IPU_BUTTRESS_TSC_CLK / 100000; + + tsc_time *= isys_clk; + tsc_base *= isys_clk; + do_div(tsc_time, tsc_clk); + do_div(tsc_base, tsc_clk); + + return tunit_base + tsc_time - tsc_base; +} + +static int update_timer_base(struct ipu_isys *isys) +{ + int rval, i; + u64 time; + + for (i = 0; i < CSI2_UPDATE_TIME_TRY_NUM; i++) { + rval = ipu_trace_get_timer(&isys->adev->dev, &time); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read Tunit timer.\n"); + return rval; + } + rval = ipu_buttress_tsc_read(isys->adev->isp, + &isys->tsc_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read TSC timer.\n"); + return rval; + } + rval = ipu_trace_get_timer(&isys->adev->dev, + &isys->tunit_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read Tunit timer.\n"); + return rval; + } + if (tunit_time_to_us(isys, isys->tunit_timer_base - time) < + CSI2_UPDATE_TIME_MAX_DIFF) + return 0; + } + dev_dbg(&isys->adev->dev, "Timer base values may not be accurate.\n"); + return 0; +} + +/* Extract the timestamp from trace message. + * The timestamp in the traces message contains two parts. + * The lower part contains bit0 ~ 15 of the total 64bit timestamp. + * The higher part contains bit14 ~ 63 of the 64bit timestamp. + * These two parts are sampled at different time. + * Two overlaped bits are used to identify if there's roll overs + * in the lower part during the two samples. + * If the two overlapped bits do not match, a fix is needed to + * handle the roll over. + */ +static u64 extract_time_from_short_packet_msg(struct + ipu_isys_csi2_monitor_message + *msg) +{ + u64 time_h = msg->timestamp_h << 14; + u64 time_l = msg->timestamp_l; + u64 time_h_ovl = time_h & 0xc000; + u64 time_h_h = time_h & (~0xffff); + + /* Fix possible roll overs. */ + if (time_h_ovl >= (time_l & 0xc000)) + return time_h_h | time_l; + else + return (time_h_h - 0x10000) | time_l; +} + +unsigned int ipu_isys_csi2_get_current_field(struct ipu_isys_pipeline *ip, + unsigned int *timestamp) +{ + struct ipu_isys_video *av = container_of(ip, struct ipu_isys_video, ip); + struct ipu_isys *isys = av->isys; + unsigned int field = V4L2_FIELD_TOP; + + /* + * Find the nearest message that has matched msg type, + * port id, virtual channel and packet type. + */ + unsigned int i = ip->short_packet_trace_index; + bool msg_matched = false; + unsigned int monitor_id; + + update_timer_base(isys); + + if (ip->csi2->index >= IPU_ISYS_MAX_CSI2_LEGACY_PORTS) + monitor_id = TRACE_REG_CSI2_3PH_TM_MONITOR_ID; + else + monitor_id = TRACE_REG_CSI2_TM_MONITOR_ID; + + dma_sync_single_for_cpu(&isys->adev->dev, + isys->short_packet_trace_buffer_dma_addr, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + do { + struct ipu_isys_csi2_monitor_message msg = + isys->short_packet_trace_buffer[i]; + u64 sof_time = tsc_time_to_tunit_time(isys, + isys->tsc_timer_base, + isys->tunit_timer_base, + (((u64) timestamp[1]) << + 32) | timestamp[0]); + u64 trace_time = extract_time_from_short_packet_msg(&msg); + u64 delta_time_us = tunit_time_to_us(isys, + (sof_time > trace_time) ? + sof_time - trace_time : + trace_time - sof_time); + + i = (i + 1) % IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER; + + if (msg.cmd == TRACE_REG_CMD_TYPE_D64MTS && + msg.monitor_id == monitor_id && + msg.fs == 1 && + msg.port == ip->csi2->index && +#ifdef IPU_VC_SUPPORT + msg.vc == ip->vc && +#endif + delta_time_us < IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT) { + field = (msg.sequence % 2) ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + ip->short_packet_trace_index = i; + msg_matched = true; + dev_dbg(&isys->adev->dev, + "Interlaced field ready. field = %d\n", field); + break; + } + } while (i != ip->short_packet_trace_index); + if (!msg_matched) + /* We have walked through the whole buffer. */ + dev_dbg(&isys->adev->dev, "No matched trace message found.\n"); + + return field; +} + +bool ipu_isys_csi2_skew_cal_required(struct ipu_isys_csi2 *csi2) +{ + __s64 link_freq; + int rval; + + if (!csi2) + return false; + +#ifdef IPU_VC_SUPPORT + /* Not yet ? */ + if (csi2->remote_streams != csi2->stream_count) + return false; + +#endif + rval = ipu_isys_csi2_get_link_freq(csi2, &link_freq); + if (rval) + return false; + + if (link_freq <= IPU_SKEW_CAL_LIMIT_HZ) + return false; + + return true; +} + +int ipu_isys_csi2_set_skew_cal(struct ipu_isys_csi2 *csi2, int enable) +{ + u32 val; + + val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG); + + if (enable) + val |= CSI2_CSI_RX_CONFIG_SKEWCAL_ENABLE; + else + val &= ~CSI2_CSI_RX_CONFIG_SKEWCAL_ENABLE; + + writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG); + + return 0; +} diff --git a/include/media/ipu-isys.h b/include/media/ipu-isys.h new file mode 100644 index 000000000000..f809ef362152 --- /dev/null +++ b/include/media/ipu-isys.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef MEDIA_IPU_H +#define MEDIA_IPU_H + +#include +#include + +#define IPU_ISYS_MAX_CSI2_LANES 4 + +struct ipu_isys_csi2_config { + unsigned int nlanes; + unsigned int port; +}; + +struct ipu_isys_subdev_i2c_info { + struct i2c_board_info board_info; + int i2c_adapter_id; +}; + +struct ipu_isys_subdev_info { + struct ipu_isys_csi2_config *csi2; + struct ipu_isys_subdev_i2c_info i2c; +}; + +struct ipu_isys_clk_mapping { + struct clk_lookup clkdev_data; + char *platform_clock_name; +}; + +struct ipu_isys_subdev_pdata { + struct ipu_isys_subdev_info **subdevs; + struct ipu_isys_clk_mapping *clk_map; +}; + +#endif /* MEDIA_IPU_H */ diff --git a/include/uapi/linux/ipu-isys-isa-fw.h b/include/uapi/linux/ipu-isys-isa-fw.h new file mode 100644 index 000000000000..f0914bce7966 --- /dev/null +++ b/include/uapi/linux/ipu-isys-isa-fw.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2014 - 2018 Intel Corporation */ + +#ifndef IPU_ISYS_ISA_FW_H +#define IPU_ISYS_ISA_FW_H + +#define ia_css_terminal_offsets(pg) \ + ((uint16_t *)((void *)(pg) + \ + (pg)->terminals_offset_offset)) + +#define to_ia_css_terminal(pg, i) \ + ((struct ia_css_terminal *)( \ + (void *)(pg) + ia_css_terminal_offsets(pg)[i])) + +#define ia_css_terminal_offset(pg, i) \ + (!(i) ? sizeof *(pg) + ((((pg)->terminal_count - 1) | 3) + 1) \ + * sizeof(uint16_t) : \ + ia_css_terminal_offsets(pg)[(i) - 1] \ + + to_ia_css_terminal(pg, (i) - 1)->size) + +/* BEGIN DEFINITIONS IMPORTED FROM FIRMWARE */ + +#define N_IPU_FW_ISYS_KERNEL_ID 20 + +struct ia_css_process_group_light { + uint32_t size; + uint16_t terminals_offset_offset; + uint16_t terminal_count; +}; + +enum ia_css_terminal_type { + IPU_FW_TERMINAL_TYPE_DATA_IN = 0, + IPU_FW_TERMINAL_TYPE_DATA_OUT, + IPU_FW_TERMINAL_TYPE_PARAM_STREAM, + IPU_FW_TERMINAL_TYPE_PARAM_CACHED_IN, + IPU_FW_TERMINAL_TYPE_PARAM_CACHED_OUT, + IPU_FW_TERMINAL_TYPE_PARAM_SPATIAL_IN, + IPU_FW_TERMINAL_TYPE_PARAM_SPATIAL_OUT, + IPU_FW_TERMINAL_TYPE_PARAM_SLICED_IN, + IPU_FW_TERMINAL_TYPE_PARAM_SLICED_OUT, + IPU_FW_TERMINAL_TYPE_STATE_IN, + IPU_FW_TERMINAL_TYPE_STATE_OUT, + IPU_FW_TERMINAL_TYPE_PROGRAM, + IPU_FW_N_TERMINAL_TYPES +}; + +struct ia_css_terminal { + enum ia_css_terminal_type terminal_type; + int16_t parent_offset; + uint16_t size; + uint16_t tm_index; + uint8_t id; + uint8_t padding[5]; +}; + +struct ia_css_param_payload { + uint64_t host_buffer; + uint32_t buffer; + uint8_t padding[4]; +}; + +struct ia_css_param_section_desc { + uint32_t mem_offset; + uint32_t mem_size; +}; + +struct ia_css_param_terminal { + struct ia_css_terminal base; + struct ia_css_param_payload param_payload; + uint16_t param_section_desc_offset; + uint8_t padding[6]; +}; + +struct ia_css_program_terminal { + struct ia_css_terminal base; + struct ia_css_param_payload param_payload; + uint16_t fragment_param_section_desc_offset; + uint16_t kernel_fragment_sequencer_info_desc_offset; + uint8_t padding[4]; +}; + +struct ia_css_sliced_param_terminal { + struct ia_css_terminal base; + struct ia_css_param_payload param_payload; + uint32_t kernel_id; + uint16_t fragment_slice_desc_offset; + uint8_t padding[2]; +}; + +enum ia_css_dimension { + IPU_FW_COL_DIMENSION = 0, + IPU_FW_ROW_DIMENSION = 1, + IPU_FW_N_DATA_DIMENSION = 2 +}; + +struct ia_css_frame_grid_desc { + uint16_t frame_grid_dimension[IPU_FW_N_DATA_DIMENSION]; + uint8_t padding[4]; +}; + +struct ia_css_spatial_param_terminal { + struct ia_css_terminal base; + struct ia_css_param_payload param_payload; + struct ia_css_frame_grid_desc frame_grid_desc; + uint32_t kernel_id; + uint16_t frame_grid_param_section_desc_offset; + uint16_t fragment_grid_desc_offset; +}; + +/* END DEFINITIONS IMPORTED FROM FIRMWARE */ + +#endif /* IPU_ISYS_ISA_FW_H */ diff --git a/include/uapi/linux/ipu-isys.h b/include/uapi/linux/ipu-isys.h new file mode 100644 index 000000000000..4c15ddb55496 --- /dev/null +++ b/include/uapi/linux/ipu-isys.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef UAPI_LINUX_IPU_ISYS_H +#define UAPI_LINUX_IPU_ISYS_H + +#define V4L2_CID_IPU_BASE (V4L2_CID_USER_BASE + 0x1080) + +#define V4L2_CID_IPU_ISA_EN (V4L2_CID_IPU_BASE + 1) +#define V4L2_CID_IPU_STORE_CSI2_HEADER (V4L2_CID_IPU_BASE + 2) + +#define V4L2_IPU_ISA_EN_BLC (1 << 0) +#define V4L2_IPU_ISA_EN_LSC (1 << 1) +#define V4L2_IPU_ISA_EN_DPC (1 << 2) +#define V4L2_IPU_ISA_EN_SCALER (1 << 3) +#define V4L2_IPU_ISA_EN_AWB (1 << 4) +#define V4L2_IPU_ISA_EN_AF (1 << 5) +#define V4L2_IPU_ISA_EN_AE (1 << 6) +#define NR_OF_IPU_ISA_CFG 7 + +#define V4L2_FMT_IPU_ISA_CFG v4l2_fourcc('i', 'p', '4', 'c') +#define V4L2_FMT_IPU_ISYS_META v4l2_fourcc('i', 'p', '4', 'm') + + +#define VIDIOC_IPU_GET_DRIVER_VERSION \ + _IOWR('v', BASE_VIDIOC_PRIVATE + 3, uint32_t) + +#endif /* UAPI_LINUX_IPU_ISYS_H */ diff --git a/include/uapi/linux/ipu-psys.h b/include/uapi/linux/ipu-psys.h new file mode 100644 index 000000000000..0f26dbfe436e --- /dev/null +++ b/include/uapi/linux/ipu-psys.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef _UAPI_IPU_PSYS_H +#define _UAPI_IPU_PSYS_H + +#include + +struct ipu_psys_capability { + uint32_t version; + uint8_t driver[20]; + uint32_t pg_count; + uint8_t dev_model[32]; + uint32_t reserved[17]; +} __attribute__ ((packed)); + +struct ipu_psys_event { + uint32_t type; /* IPU_PSYS_EVENT_TYPE_ */ + uint64_t user_token; + uint64_t issue_id; + uint32_t buffer_idx; + uint32_t error; + int32_t reserved[2]; +} __attribute__ ((packed)); + +#define IPU_PSYS_EVENT_TYPE_CMD_COMPLETE 1 +#define IPU_PSYS_EVENT_TYPE_BUFFER_COMPLETE 2 + +/** + * struct ipu_psys_buffer - for input/output terminals + * @len: total allocated size @ base address + * @userptr: user pointer + * @fd: DMA-BUF handle + * @data_offset:offset to valid data + * @bytes_used: amount of valid data including offset + * @flags: flags + */ +struct ipu_psys_buffer { + uint64_t len; + union { + int fd; + void __user *userptr; + uint64_t reserved; + } base; + uint32_t data_offset; + uint32_t bytes_used; + uint32_t flags; + uint32_t reserved[2]; +} __attribute__ ((packed)); + +#define IPU_BUFFER_FLAG_INPUT (1 << 0) +#define IPU_BUFFER_FLAG_OUTPUT (1 << 1) +#define IPU_BUFFER_FLAG_MAPPED (1 << 2) +#define IPU_BUFFER_FLAG_NO_FLUSH (1 << 3) +#define IPU_BUFFER_FLAG_DMA_HANDLE (1 << 4) +#define IPU_BUFFER_FLAG_USERPTR (1 << 5) + +#define IPU_PSYS_CMD_PRIORITY_HIGH 0 +#define IPU_PSYS_CMD_PRIORITY_MED 1 +#define IPU_PSYS_CMD_PRIORITY_LOW 2 +#define IPU_PSYS_CMD_PRIORITY_NUM 3 + +/** + * struct ipu_psys_command - processing command + * @issue_id: unique id for the command set by user + * @user_token: token of the command + * @priority: priority of the command + * @pg_manifest: userspace pointer to program group manifest + * @buffers: userspace pointers to array of psys dma buf structs + * @pg: process group DMA-BUF handle + * @pg_manifest_size: size of program group manifest + * @bufcount: number of buffers in buffers array + * @min_psys_freq: minimum psys frequency in MHz used for this cmd + * + * Specifies a processing command with input and output buffers. + */ +struct ipu_psys_command { + uint64_t issue_id; + uint64_t user_token; + uint32_t priority; + void __user *pg_manifest; + struct ipu_psys_buffer __user *buffers; + int pg; + uint32_t pg_manifest_size; + uint32_t bufcount; + uint32_t min_psys_freq; + uint32_t frame_counter; + uint32_t reserved[2]; +} __attribute__ ((packed)); + +struct ipu_psys_manifest { + uint32_t index; + uint32_t size; + void __user *manifest; + uint32_t reserved[5]; +} __attribute__ ((packed)); + +#define IPU_IOC_QUERYCAP _IOR('A', 1, struct ipu_psys_capability) +#define IPU_IOC_MAPBUF _IOWR('A', 2, int) +#define IPU_IOC_UNMAPBUF _IOWR('A', 3, int) +#define IPU_IOC_GETBUF _IOWR('A', 4, struct ipu_psys_buffer) +#define IPU_IOC_PUTBUF _IOWR('A', 5, struct ipu_psys_buffer) +#define IPU_IOC_QCMD _IOWR('A', 6, struct ipu_psys_command) +#define IPU_IOC_DQEVENT _IOWR('A', 7, struct ipu_psys_event) +#define IPU_IOC_CMD_CANCEL _IOWR('A', 8, struct ipu_psys_command) +#define IPU_IOC_GET_MANIFEST _IOWR('A', 9, struct ipu_psys_manifest) + +#endif /* _UAPI_IPU_PSYS_H */ From e96d194b32b1b723aca49505506f7fafa2031ff6 Mon Sep 17 00:00:00 2001 From: Meng Wei Date: Fri, 26 Oct 2018 09:53:06 +0800 Subject: [PATCH 1146/1276] media: platform: add IPU platform configuration data These data are used for i2c device detection and resource revervation. Signed-off-by: Chang Ying Signed-off-by: Meng Wei --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/intel/Kconfig | 95 ++ drivers/media/platform/intel/Makefile | 21 + .../media/platform/intel/ipu4-bxt-gp-pdata.c | 123 ++ .../media/platform/intel/ipu4-bxt-p-pdata.c | 1161 +++++++++++++++++ .../platform/intel/ipu4p-icl-rvp-pdata.c | 181 +++ 7 files changed, 1583 insertions(+) create mode 100644 drivers/media/platform/intel/Kconfig create mode 100644 drivers/media/platform/intel/Makefile create mode 100644 drivers/media/platform/intel/ipu4-bxt-gp-pdata.c create mode 100644 drivers/media/platform/intel/ipu4-bxt-p-pdata.c create mode 100644 drivers/media/platform/intel/ipu4p-icl-rvp-pdata.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 54fe90acb5b2..76b67179062d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -137,6 +137,7 @@ source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" source "drivers/media/platform/rcar-vin/Kconfig" source "drivers/media/platform/atmel/Kconfig" +source "drivers/media/platform/intel/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 41322ab65802..f8c6a9c14add 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += meson/ obj-y += cros-ec-cec/ +obj-y += intel/ diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig new file mode 100644 index 000000000000..7dc176af66f2 --- /dev/null +++ b/drivers/media/platform/intel/Kconfig @@ -0,0 +1,95 @@ +config INTEL_IPU4_BXT_P_PDATA + bool "Enable built in platform data for Broxton-P" + depends on VIDEO_INTEL_IPU && VIDEO_INTEL_IPU4 + ---help--- + Pre-ACPI system platform data is compiled inside kernel + +config INTEL_IPU4_BXT_GP_PDATA + bool "Enable built in platform data for Broxton-P" + depends on VIDEO_INTEL_IPU && VIDEO_INTEL_IPU4 + ---help--- + Pre-ACPI system platform data is compiled inside kernel + +config INTEL_IPU4P_ICL_RVP_PDATA + bool "Enable built in platform data for ipu4p" + depends on VIDEO_INTEL_IPU && VIDEO_INTEL_IPU4P + ---help--- + Pre-ACPI system platform data is compiled inside kernel + +config INTEL_IPU4_OV2740 + bool "Compile platform data for OV2740" + depends on INTEL_IPU4_BXT_P_PDATA + +config INTEL_IPU4_IMX185 + bool "Compile platform data for IMX185" + depends on INTEL_IPU4_BXT_P_PDATA + +config INTEL_IPU4_AR023Z + bool "Compile platform data for AR023Z" + ---help--- + Onsemi 2MP AR023Z camera sensor + +config INTEL_IPU4_IMX477 + bool "Compile platform data for IMX477 camera sensor" + depends on INTEL_IPU4_BXT_P_PDATA + ---help--- + Sony IMX477 sensor is enabled for DUAL Camera input. + +config INTEL_IPU4_OV13860 + bool "Compile platform data for OV13860" + ---help--- + Omnivision 13MP camera sensor + +config INTEL_IPU4_OV9281 + bool "Compile platform data for OV9281" + ---help--- + Omnivision 1MP camera sensor + +config INTEL_IPU4_OV10635 + bool "Compile platform data for OV10635" + ---help--- + Omnivision 1MP camera sensor + +config INTEL_IPU4_AR0231AT + bool "Compile platform data for AR0231AT" + ---help--- + AR0231 camera sensor for MAXIM 9286 + +config INTEL_IPU4_MAGNA + bool "Compile platform data for MAGNA" + depends on INTEL_IPU4_BXT_P_PDATA + ---help--- + MAGNA Camera Sensor + +config INTEL_IPU4_IMX274 + bool "Compile platform data for IMX274 camera sensor" + depends on INTEL_IPU4_BXT_P_PDATA + ---help--- + Sony 14MP camera sensor is enabled for HDR function. + +config INTEL_IPU4_OV10640 + bool "Compile platform data for OV10640" + ---help--- + Omnivision 1.4MP camera sensor + +config INTEL_IPU4_ADV7481 + bool "Compile platform data for ADV7481" + ---help--- + HDMI2MIPI convertor device ADV7481 + +config INTEL_IPU4_ADV7481_EVAL + bool "Compile platform data for ADV7481 evaluation board" + ---help--- + HDMI2MIPI convertor device ADV7481 eval board + +config INTEL_IPU4_IMX290 + bool "Compile platform data for IMX290" + depends on INTEL_IPU4_BXT_P_PDATA + ---help--- + "Sony 8MB camera sensor is enabled for HDR function" + +config INTEL_IPU4_OX03A10 + bool "Compile platorm data for OX03A10" + depends on INTEL_IPU4_BXT_P_PDATA + ---help--- + "ox03a10 camera sensor" diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile new file mode 100644 index 000000000000..b6d4f786f20b --- /dev/null +++ b/drivers/media/platform/intel/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2018, Intel Corporation. + +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +# force check the compile warning to make sure zero warnings +# note we may have build issue when gcc upgraded. +ccflags-y := -Wall -Wextra +ccflags-y += $(call cc-disable-warning, unused-parameter) +ccflags-y += $(call cc-disable-warning, implicit-fallthrough) +ccflags-y += $(call cc-disable-warning, missing-field-initializers) +ccflags-$(CONFIG_VIDEO_INTEL_IPU_WERROR) += -Werror + +ccflags-y += -I$(srcpath)/$(src)/../../../../include/ +ccflags-y += -I$(srcpath)/$(src)/../../pci/intel/ + +obj-$(CONFIG_INTEL_IPU4_BXT_P_PDATA) += ipu4-bxt-p-pdata.o +obj-$(CONFIG_INTEL_IPU4_BXT_GP_PDATA) += ipu4-bxt-gp-pdata.o +obj-$(CONFIG_INTEL_IPU4P_ICL_RVP_PDATA) += ipu4p-icl-rvp-pdata.o diff --git a/drivers/media/platform/intel/ipu4-bxt-gp-pdata.c b/drivers/media/platform/intel/ipu4-bxt-gp-pdata.c new file mode 100644 index 000000000000..9acfafc9cf42 --- /dev/null +++ b/drivers/media/platform/intel/ipu4-bxt-gp-pdata.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2016--2017 Intel Corporation. + * + * Author: Jouni Ukkonen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include "ipu.h" + +#define ADV7481_HDMI_LANES 4 +#define ADV7481_HDMI_I2C_ADDRESS 0xe0 + +#define ADV7481_LANES 1 +/* + * below i2c address is dummy one, to be able to register single + * ADV7481 chip as two sensors + */ +#define ADV7481_I2C_ADDRESS 0xe1 + + +#define GPIO_BASE 434 + + +static struct crlmodule_platform_data adv7481_cvbs_pdata = { + .ext_clk = 286363636, + .xshutdown = GPIO_BASE + 64, /*dummy for now*/ + .lanes = ADV7481_LANES, + .module_name = "ADV7481 CVBS", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config adv7481_cvbs_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info adv7481_cvbs_crl_sd = { + .csi2 = &adv7481_cvbs_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_I2C_ADDRESS, + .platform_data = &adv7481_cvbs_pdata, + }, + .i2c_adapter_id = 0, + } +}; + +static struct crlmodule_platform_data adv7481_hdmi_pdata = { + /* FIXME: may need to revisit */ + .ext_clk = 286363636, + .xshutdown = GPIO_BASE + 30, + .lanes = ADV7481_HDMI_LANES, + .module_name = "ADV7481 HDMI", + .crl_irq_pin = GPIO_BASE + 22, + .irq_pin_flags = (IRQF_TRIGGER_RISING | IRQF_ONESHOT), + .irq_pin_name = "ADV7481_HDMI_IRQ", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config adv7481_hdmi_csi2_cfg = { + .nlanes = ADV7481_HDMI_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info adv7481_hdmi_crl_sd = { + .csi2 = &adv7481_hdmi_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_HDMI_I2C_ADDRESS, + .platform_data = &adv7481_hdmi_pdata, + }, + .i2c_adapter_id = 0, + } +}; + + + +/* + * Map buttress output sensor clocks to sensors - + * this should be coming from ACPI, in Gordon Peak + * ADV7481 have its own oscillator, no buttres clock + * needed. + */ +struct ipu_isys_clk_mapping gp_mapping[] = { + { CLKDEV_INIT(NULL, NULL, NULL), NULL } +}; + +static struct ipu_isys_subdev_pdata pdata = { + .subdevs = (struct ipu_isys_subdev_info *[]) { + &adv7481_hdmi_crl_sd, + &adv7481_cvbs_crl_sd, + NULL, + }, + .clk_map = gp_mapping, +}; + +static void ipu4_quirk(struct pci_dev *pci_dev) +{ + pci_dev->dev.platform_data = &pdata; +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU_PCI_ID, + ipu4_quirk); diff --git a/drivers/media/platform/intel/ipu4-bxt-p-pdata.c b/drivers/media/platform/intel/ipu4-bxt-p-pdata.c new file mode 100644 index 000000000000..ba5707adebdc --- /dev/null +++ b/drivers/media/platform/intel/ipu4-bxt-p-pdata.c @@ -0,0 +1,1161 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2015 - 2018 Intel Corporation + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "ipu.h" + +#define GPIO_BASE 422 + +#ifdef CONFIG_INTEL_IPU4_OV2740 +#define OV2740_LANES 2 +#define OV2740_I2C_ADDRESS 0x36 +static struct crlmodule_platform_data ov2740_pdata = { + .xshutdown = GPIO_BASE + 64, + .lanes = OV2740_LANES, + .ext_clk = 19200000, + .op_sys_clock = (uint64_t []){ 72000000 }, + .module_name = "INT3474", + .id_string = "0x27 0x40", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config ov2740_csi2_cfg = { + .nlanes = OV2740_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info ov2740_crl_sd = { + .csi2 = &ov2740_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, OV2740_I2C_ADDRESS), + .platform_data = &ov2740_pdata, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_IMX185 +#define IMX185_LANES 4 +#define IMX185_I2C_ADDRESS 0x1a + +static struct crlmodule_platform_data imx185_pdata = { + .xshutdown = GPIO_BASE + 71, + .lanes = IMX185_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t []){ 55687500, 111375000, + 111375000, 222750000 }, + .module_name = "IMX185", + .id_string = "0x1 0x85", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config imx185_csi2_cfg = { + .nlanes = IMX185_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info imx185_crl_sd = { + .csi2 = &imx185_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX185_I2C_ADDRESS), + .platform_data = &imx185_pdata, + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_platform_data imx185_b_pdata = { + .xshutdown = GPIO_BASE + 73, + .lanes = IMX185_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t []){ 55687500, 111375000, + 111375000, 222750000 }, + .module_name = "IMX185", + .id_string = "0x1 0x85", + .suffix = 'b', +}; + +static struct ipu_isys_csi2_config imx185_b_csi2_cfg = { + .nlanes = IMX185_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info imx185_b_crl_sd = { + .csi2 = &imx185_b_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX185_I2C_ADDRESS), + .platform_data = &imx185_b_pdata, + }, + .i2c_adapter_id = 4, + } +}; + +#endif + +#ifdef CONFIG_INTEL_IPU4_AR023Z +#define AR023Z_MIPI_LANES 2 +/* Toshiba TC358778 Parallel-MIPI Bridge */ +#define TC358778_I2C_ADDRESS 0x0e + +static struct crlmodule_platform_data ar023z_pdata = { + .xshutdown = GPIO_BASE + 64, + .lanes = AR023Z_MIPI_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t []){317250000}, + .module_name = "AR023Z", + .id_string = "0x4401 0x64", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config ar023z_csi2_cfg = { + .nlanes = AR023Z_MIPI_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info ar023z_crl_sd = { + .csi2 = &ar023z_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, TC358778_I2C_ADDRESS), + .platform_data = &ar023z_pdata, + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_platform_data ar023z_b_pdata = { + .xshutdown = GPIO_BASE + 67, + .lanes = AR023Z_MIPI_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t []){317250000}, + .module_name = "AR023Z", + .id_string = "0x4401 0x64", + .suffix = 'b', +}; + +static struct ipu_isys_csi2_config ar023z_b_csi2_cfg = { + .nlanes = AR023Z_MIPI_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info ar023z_b_crl_sd = { + .csi2 = &ar023z_b_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, TC358778_I2C_ADDRESS), + .platform_data = &ar023z_b_pdata, + }, + .i2c_adapter_id = 4, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_IMX477 +#define IMX477_LANES 2 + +#define IMX477_I2C_ADDRESS 0x10 + +static struct crlmodule_platform_data imx477_pdata_master = { + .xshutdown = GPIO_BASE + 64, + .lanes = IMX477_LANES, + .ext_clk = 19200000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "IMX477-MASTER", + .id_string = "0x4 0x77", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config imx477_csi2_cfg_master = { + .nlanes = IMX477_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info imx477_crl_sd_master = { + .csi2 = &imx477_csi2_cfg_master, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX477_I2C_ADDRESS), + .platform_data = &imx477_pdata_master, + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_platform_data imx477_pdata_slave_1 = { + .xshutdown = GPIO_BASE + 67, + .lanes = IMX477_LANES, + .ext_clk = 19200000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "IMX477-SLAVE-1", + .id_string = "0x4 0x77", + .suffix = 'b', +}; + +static struct ipu_isys_csi2_config imx477_csi2_cfg_slave_1 = { + .nlanes = IMX477_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info imx477_crl_sd_slave_1 = { + .csi2 = &imx477_csi2_cfg_slave_1, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX477_I2C_ADDRESS), + .platform_data = &imx477_pdata_slave_1, + }, + .i2c_adapter_id = 4, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_IMX274 + +#define IMX274_LANES 4 +#define IMX274_I2C_ADDRESS 0x1a + +static struct crlmodule_platform_data imx274_pdata = { + .xshutdown = GPIO_BASE + 64, + .lanes = IMX274_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){720000000}, + .module_name = "IMX274", + .id_string = "0x6 0x9", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config imx274_csi2_cfg = { + .nlanes = IMX274_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info imx274_crl_sd = { + .csi2 = &imx274_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX274_I2C_ADDRESS), + .platform_data = &imx274_pdata + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_platform_data imx274_b_pdata = { + .xshutdown = GPIO_BASE + 67, + .lanes = IMX274_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){720000000}, + .module_name = "IMX274", + .id_string = "0x6 0x9", + .suffix = 'b', +}; + +static struct ipu_isys_csi2_config imx274_b_csi2_cfg = { + .nlanes = IMX274_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info imx274_b_crl_sd = { + .csi2 = &imx274_b_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX274_I2C_ADDRESS), + .platform_data = &imx274_b_pdata + }, + .i2c_adapter_id = 4, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_IMX290 + +#define IMX290_LANES 4 +#define IMX290_I2C_ADDRESS 0x1a + +static struct crlmodule_platform_data imx290_pdata = { + .xshutdown = GPIO_BASE + 64, + .lanes = IMX290_LANES, + .ext_clk = 37125000, + .op_sys_clock = (uint64_t []){222750000, 445500000}, + .module_name = "IMX290", + .id_string = "0x2 0x90", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config imx290_csi2_cfg = { + .nlanes = IMX290_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info imx290_crl_sd = { + .csi2 = &imx290_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, IMX290_I2C_ADDRESS), + .platform_data = &imx290_pdata + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_OV13860 + +#define OV13860_LANES 2 +#define OV13860_I2C_ADDRESS 0x10 + +static struct crlmodule_platform_data ov13860_pdata = { + .xshutdown = GPIO_BASE + 71, + .lanes = OV13860_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){ 600000000, 300000000}, + .module_name = "OV13860", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config ov13860_csi2_cfg = { + .nlanes = OV13860_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info ov13860_crl_sd = { + .csi2 = &ov13860_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, OV13860_I2C_ADDRESS), + .platform_data = &ov13860_pdata, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_OV9281 + +#define OV9281_LANES 2 +#define OV9281_I2C_ADDRESS 0x10 + +static struct crlmodule_platform_data ov9281_pdata = { + .xshutdown = GPIO_BASE + 71, + .lanes = OV9281_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){400000000}, + .module_name = "OV9281", + .id_string = "0x92 0x81", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config ov9281_csi2_cfg = { + .nlanes = OV9281_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info ov9281_crl_sd = { + .csi2 = &ov9281_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_NAME, OV9281_I2C_ADDRESS), + .platform_data = &ov9281_pdata, + }, + .i2c_adapter_id = 0, + } +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_BU64295) + +#define BU64295_VCM_ADDR 0x0c +#define BU64295_NAME "bu64295" + +static struct ipu_isys_subdev_info bu64295_sd = { + .i2c = { + .board_info = { + I2C_BOARD_INFO(BU64295_NAME, BU64295_VCM_ADDR), + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_ADV7481 + +#define ADV7481_LANES 4 +#define ADV7481_I2C_ADDRESS 0xe0 +#define ADV7481B_I2C_ADDRESS 0xe2 + +static struct crlmodule_platform_data adv7481_pdata = { + .xshutdown = GPIO_BASE + 63, + .lanes = ADV7481_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config adv7481_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info adv7481_crl_sd = { + .csi2 = &adv7481_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_I2C_ADDRESS, + .platform_data = &adv7481_pdata, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_ADV7481_EVAL + +#define ADV7481_LANES 4 +#define ADV7481_I2C_ADDRESS 0xe0 +#define ADV7481B_I2C_ADDRESS 0xe2 + +static struct crlmodule_platform_data adv7481_eval_pdata = { + .xshutdown = GPIO_BASE + 63, + .lanes = ADV7481_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481_EVAL", + .suffix = 'a', +}; + +static struct ipu_isys_csi2_config adv7481_eval_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info adv7481_eval_crl_sd = { + .csi2 = &adv7481_eval_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_I2C_ADDRESS, + .platform_data = &adv7481_eval_pdata, + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_platform_data adv7481b_eval_pdata = { + .xshutdown = GPIO_BASE + 63, + .lanes = ADV7481_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481B_EVAL", + .suffix = 'b', +}; + +static struct ipu_isys_csi2_config adv7481b_eval_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info adv7481b_eval_crl_sd = { + .csi2 = &adv7481b_eval_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481B_I2C_ADDRESS, + .platform_data = &adv7481b_eval_pdata, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_AGGREGATOR_STUB) + +#define VIDEO_AGGRE_LANES 4 +#define VIDEO_AGGRE_I2C_ADDRESS 0x3b +#define VIDEO_AGGRE_B_I2C_ADDRESS 0x3c + +static struct ipu_isys_csi2_config video_aggre_csi2_cfg = { + .nlanes = VIDEO_AGGRE_LANES, + .port = 0, +}; + +static struct ipu_isys_subdev_info video_aggre_stub_sd = { + .csi2 = &video_aggre_csi2_cfg, + .i2c = { + .board_info = { + .type = "video-aggre", + .addr = VIDEO_AGGRE_I2C_ADDRESS, + }, + .i2c_adapter_id = 2, + } +}; + +static struct ipu_isys_csi2_config video_aggre_b_csi2_cfg = { + .nlanes = VIDEO_AGGRE_LANES, + .port = 4, +}; + +static struct ipu_isys_subdev_info video_aggre_b_stub_sd = { + .csi2 = &video_aggre_b_csi2_cfg, + .i2c = { + .board_info = { + .type = "video-aggre", + .addr = VIDEO_AGGRE_B_I2C_ADDRESS, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_MAGNA +#define MAGNA_LANES 4 +#define MAGNA_PHY_ADDR 0x60 /* 0x30 for 7bit addr */ +#define MAGNA_ADDRESS_A 0x61 + +static struct crlmodule_platform_data magna_pdata = { + .lanes = MAGNA_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){ 400000000 }, + .module_name = "MAGNA", + .id_string = "0xa6 0x35", + /* + * The pin number of xshutdown will be determined + * and replaced inside TI964 driver. + * The number here stands for which GPIO to connect with. + * 1 means to connect sensor xshutdown to GPIO1 + */ + .xshutdown = 1, + /* + * this flags indicates the expected polarity for the LineValid + * indication received in Raw mode. + * 1 means LineValid is high for the duration of the video frame. + */ + .high_framevalid_flags = 1, +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_OV10635 +#define OV10635_LANES 4 +#define OV10635_I2C_PHY_ADDR 0x60 /* 0x30 for 7bit addr */ +#define OV10635A_I2C_ADDRESS 0x61 +#define OV10635B_I2C_ADDRESS 0x62 +#define OV10635C_I2C_ADDRESS 0x63 +#define OV10635D_I2C_ADDRESS 0x64 + +static struct crlmodule_platform_data ov10635_pdata = { + .lanes = OV10635_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){ 400000000 }, + .module_name = "OV10635", + .id_string = "0xa6 0x35", + /* + * The pin number of xshutdown will be determined + * and replaced inside TI964 driver. + * The number here stands for which GPIO to connect with. + * 1 means to connect sensor xshutdown to GPIO1 + */ + .xshutdown = 0, +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_OV10640 +#define OV10640_LANES 4 +#define OV10640_I2C_PHY_ADDR 0x60 /* 0x30 for 7bit addr */ +#define OV10640A_I2C_ADDRESS 0x61 +#define OV10640B_I2C_ADDRESS 0x62 +#define OV10640C_I2C_ADDRESS 0x63 +#define OV10640D_I2C_ADDRESS 0x64 + +static struct crlmodule_platform_data ov10640_pdata = { + .lanes = OV10640_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){ 400000000 }, + .module_name = "OV10640", + .id_string = "0xa6 0x40", + /* + * The pin number of xshutdown will be determined + * and replaced inside TI964 driver. + * The number here stands for which GPIO to connect with. + * 1 means to connect sensor xshutdown to GPIO1 + */ + .xshutdown = 1, +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_TI964) +#define TI964_I2C_ADAPTER 0 +#define TI964_I2C_ADAPTER_2 7 +#define TI964_I2C_ADDRESS 0x3d +#define TI964_LANES 4 + +static struct ipu_isys_csi2_config ti964_csi2_cfg = { + .nlanes = TI964_LANES, + .port = 0, +}; + +static struct ipu_isys_csi2_config ti964_csi2_cfg_2 = { + .nlanes = TI964_LANES, + .port = 4, +}; + +static struct ti964_subdev_info ti964_subdevs[] = { +#ifdef CONFIG_INTEL_IPU4_OV10635 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635A_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 0, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'a', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635B_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 1, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'b', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635C_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 2, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'c', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635D_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 3, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'd', + }, +#endif +#ifdef CONFIG_INTEL_IPU4_OV10640 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640A_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 0, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'a', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640B_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 1, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'b', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640C_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 2, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'c', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640D_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 3, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'd', + }, +#endif +#ifdef CONFIG_INTEL_IPU4_MAGNA + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = MAGNA_ADDRESS_A, + .platform_data = &magna_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + .rx_port = 0, + .phy_i2c_addr = MAGNA_PHY_ADDR, + .suffix = 'a', + }, +#endif +}; + +static struct ti964_subdev_info ti964_subdevs_2[] = { +#ifdef CONFIG_INTEL_IPU4_OV10635 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635A_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 0, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'e', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635B_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 1, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'f', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635C_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 2, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'g', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10635D_I2C_ADDRESS, + .platform_data = &ov10635_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 3, + .phy_i2c_addr = OV10635_I2C_PHY_ADDR, + .suffix = 'h', + }, +#endif +#ifdef CONFIG_INTEL_IPU4_OV10640 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640A_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 0, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'e', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640B_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 1, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'f', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640C_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 2, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'g', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OV10640D_I2C_ADDRESS, + .platform_data = &ov10640_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + .rx_port = 3, + .phy_i2c_addr = OV10640_I2C_PHY_ADDR, + .suffix = 'h', + }, +#endif +}; + +static struct ti964_pdata ti964_pdata = { + .subdev_info = ti964_subdevs, + .subdev_num = ARRAY_SIZE(ti964_subdevs), + .reset_gpio = GPIO_BASE + 63, + .suffix = 'a', +}; + +static struct ipu_isys_subdev_info ti964_sd = { + .csi2 = &ti964_csi2_cfg, + .i2c = { + .board_info = { + .type = "ti964", + .addr = TI964_I2C_ADDRESS, + .platform_data = &ti964_pdata, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER, + } +}; + +static struct ti964_pdata ti964_pdata_2 = { + .subdev_info = ti964_subdevs_2, + .subdev_num = ARRAY_SIZE(ti964_subdevs_2), + .reset_gpio = GPIO_BASE + 66, + .suffix = 'b', +}; + +static struct ipu_isys_subdev_info ti964_sd_2 = { + .csi2 = &ti964_csi2_cfg_2, + .i2c = { + .board_info = { + .type = "ti964", + .addr = TI964_I2C_ADDRESS, + .platform_data = &ti964_pdata_2, + }, + .i2c_adapter_id = TI964_I2C_ADAPTER_2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_OX03A10 +#define OX03A10_LANES 4 +#define OX03A10_I2C_PHY_ADDR 0x6c +#define OX03A10A_I2C_ADDRESS 0x30 +#define OX03A10B_I2C_ADDRESS 0x31 + +#define OX03A10A_SER_ADDRESS 0x58 +#define OX03A10B_SER_ADDRESS 0x59 + +static struct crlmodule_platform_data ox03a10_pdata = { + .lanes = OX03A10_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t[]){ 87750000 }, + .module_name = "OX03A10", + .id_string = "0x58 0x3 0x41", + .xshutdown = 1, +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_TI960) +#define TI960_I2C_ADAPTER 2 +#define TI960_I2C_ADAPTER_2 7 +#define TI960_LANES 4 + +static struct ipu_isys_csi2_config ti960_csi2_cfg = { + .nlanes = TI960_LANES, + .port = 0, +}; + +static struct ipu_isys_csi2_config ti960_csi2_cfg_2 = { + .nlanes = TI960_LANES, + .port = 4, +}; + +static struct ti960_subdev_info ti960_subdevs[] = { +#ifdef CONFIG_INTEL_IPU4_OX03A10 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OX03A10A_I2C_ADDRESS, + .platform_data = &ox03a10_pdata, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER, + .rx_port = 0, + .phy_i2c_addr = OX03A10_I2C_PHY_ADDR, + .ser_alias = OX03A10A_SER_ADDRESS, + .suffix = 'a', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OX03A10B_I2C_ADDRESS, + .platform_data = &ox03a10_pdata, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER, + .rx_port = 1, + .phy_i2c_addr = OX03A10_I2C_PHY_ADDR, + .ser_alias = OX03A10B_SER_ADDRESS, + .suffix = 'b', + }, +#endif +}; + +static struct ti960_subdev_info ti960_subdevs_2[] = { +#ifdef CONFIG_INTEL_IPU4_OX03A10 + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OX03A10A_I2C_ADDRESS, + .platform_data = &ox03a10_pdata, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER_2, + .rx_port = 0, + .phy_i2c_addr = OX03A10_I2C_PHY_ADDR, + .ser_alias = OX03A10A_SER_ADDRESS, + .suffix = 'e', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = OX03A10B_I2C_ADDRESS, + .platform_data = &ox03a10_pdata, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER_2, + .rx_port = 1, + .phy_i2c_addr = OX03A10_I2C_PHY_ADDR, + .ser_alias = OX03A10B_SER_ADDRESS, + .suffix = 'f', + }, +#endif +}; + +static struct ti960_pdata ti960_pdata = { + .subdev_info = ti960_subdevs, + .subdev_num = ARRAY_SIZE(ti960_subdevs), + .reset_gpio = GPIO_BASE + 62, + .suffix = 'a', +}; + +static struct ipu_isys_subdev_info ti960_sd = { + .csi2 = &ti960_csi2_cfg, + .i2c = { + .board_info = { + .type = "ti960", + .addr = TI960_I2C_ADDRESS, + .platform_data = &ti960_pdata, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER, + } +}; + +static struct ti960_pdata ti960_pdata_2 = { + .subdev_info = ti960_subdevs_2, + .subdev_num = ARRAY_SIZE(ti960_subdevs_2), + .reset_gpio = GPIO_BASE + 66, + .suffix = 'b', +}; + +static struct ipu_isys_subdev_info ti960_sd_2 = { + .csi2 = &ti960_csi2_cfg_2, + .i2c = { + .board_info = { + .type = "ti960", + .addr = TI960_I2C_ADDRESS, + .platform_data = &ti960_pdata_2, + }, + .i2c_adapter_id = TI960_I2C_ADAPTER_2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_AR0231AT +#define AR0231AT_LANES 4 +#define AR0231ATA_I2C_ADDRESS 0x11 +#define AR0231ATB_I2C_ADDRESS 0x12 +#define AR0231ATC_I2C_ADDRESS 0x13 +#define AR0231ATD_I2C_ADDRESS 0x14 + +static struct crlmodule_platform_data ar0231at_pdata = { + .lanes = AR0231AT_LANES, + .ext_clk = 27000000, + .op_sys_clock = (uint64_t[]){ 87750000 }, + .module_name = "AR0231AT", +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_MAX9286) +#define DS_MAX9286_LANES 4 +#define DS_MAX9286_I2C_ADAPTER 4 +#define DS_MAX9286_I2C_ADDRESS 0x48 + +static struct ipu_isys_csi2_config max9286_csi2_cfg = { + .nlanes = DS_MAX9286_LANES, + .port = 4, +}; + +static struct max9286_subdev_i2c_info max9286_subdevs[] = { +#ifdef CONFIG_INTEL_IPU4_AR0231AT + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATA_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER, + .suffix = 'a', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATB_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER, + .suffix = 'b', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATC_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER, + .suffix = 'c', + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATD_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER, + .suffix = 'd', + }, +#endif +}; + +static struct max9286_pdata max9286_pdata = { + .subdev_info = max9286_subdevs, + .subdev_num = ARRAY_SIZE(max9286_subdevs), + .reset_gpio = GPIO_BASE + 63, + .suffix = 'a', +}; + +static struct ipu_isys_subdev_info max9286_sd = { + .csi2 = &max9286_csi2_cfg, + .i2c = { + .board_info = { + .type = "max9286", + .addr = DS_MAX9286_I2C_ADDRESS, + .platform_data = &max9286_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER, + } +}; +#endif + +/* + * Map buttress output sensor clocks to sensors - + * this should be coming from ACPI + */ +static struct ipu_isys_clk_mapping clk_mapping[] = { + { CLKDEV_INIT("2-0036", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("2-001a", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("4-001a", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT("2-0010", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("4-0010", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT("2-a0e0", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("2-a0e2", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("0-0010", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("2-000e", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("4-000e", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT("0-0048", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("4-0048", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT(NULL, NULL, NULL), NULL } +}; + +static struct ipu_isys_subdev_pdata pdata = { + .subdevs = (struct ipu_isys_subdev_info *[]) { +#ifdef CONFIG_INTEL_IPU4_OV2740 + &ov2740_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_IMX185 + &imx185_crl_sd, + &imx185_b_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_AR023Z + &ar023z_crl_sd, + &ar023z_b_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_IMX477 + &imx477_crl_sd_slave_1, + &imx477_crl_sd_master, +#endif +#ifdef CONFIG_INTEL_IPU4_IMX274 + &imx274_crl_sd, + &imx274_b_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_IMX290 + &imx290_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_OV13860 + &ov13860_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_OV9281 + &ov9281_crl_sd, +#endif +#if IS_ENABLED(CONFIG_VIDEO_BU64295) + &bu64295_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_ADV7481 + &adv7481_crl_sd, +#endif +#ifdef CONFIG_INTEL_IPU4_ADV7481_EVAL + &adv7481_eval_crl_sd, + &adv7481b_eval_crl_sd, +#endif +#if IS_ENABLED(CONFIG_VIDEO_AGGREGATOR_STUB) + &video_aggre_stub_sd, + &video_aggre_b_stub_sd, +#endif +#if IS_ENABLED(CONFIG_VIDEO_TI964) + &ti964_sd, + &ti964_sd_2, +#endif +#if IS_ENABLED(CONFIG_VIDEO_TI960) + &ti960_sd, + &ti960_sd_2, +#endif +#if IS_ENABLED(CONFIG_VIDEO_MAX9286) + &max9286_sd, +#endif + NULL, + }, + .clk_map = clk_mapping, +}; + +static void ipu4_quirk(struct pci_dev *pci_dev) +{ + pci_dev->dev.platform_data = &pdata; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU_PCI_ID, ipu4_quirk); diff --git a/drivers/media/platform/intel/ipu4p-icl-rvp-pdata.c b/drivers/media/platform/intel/ipu4p-icl-rvp-pdata.c new file mode 100644 index 000000000000..f3906f4d4fa1 --- /dev/null +++ b/drivers/media/platform/intel/ipu4p-icl-rvp-pdata.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include +#include +#include +#include +#include + +#include +#include +#include "ipu.h" +#include + +#define IMX355_LANES 4 +#define IMX355_I2C_ADDRESS 0x1a +#define IMX319_LANES 4 +#define IMX319_I2C_ADDRESS 0x10 +#define AK7375_I2C_ADDRESS 0xc + +static struct ipu_isys_csi2_config imx355_csi2_cfg = { + .nlanes = IMX355_LANES, + .port = 4, /* WF camera */ +}; + +static struct ipu_isys_subdev_info imx355_sd = { + .csi2 = &imx355_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO("imx355", IMX355_I2C_ADDRESS), + }, + .i2c_adapter_id = 9, + } +}; + +/* FIXME: Remove this after hardware transition. */ +static struct ipu_isys_subdev_info imx355_sd2 = { + .csi2 = &imx355_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO("imx355", 0x10), + }, + .i2c_adapter_id = 9, + } +}; + +static struct ipu_isys_subdev_info ak7375_sd = { + .i2c = { + .board_info = { + I2C_BOARD_INFO("ak7375", AK7375_I2C_ADDRESS), + }, + .i2c_adapter_id = 9, + } +}; + +static struct ipu_isys_csi2_config imx319_csi2_cfg = { + .nlanes = IMX319_LANES, + .port = 0, /* UF camera */ +}; + +static struct ipu_isys_subdev_info imx319_sd = { + .csi2 = &imx319_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO("imx319", IMX319_I2C_ADDRESS), + }, + .i2c_adapter_id = 8, + } +}; + +#ifdef CONFIG_INTEL_IPU4_AR0231AT +#define AR0231AT_LANES 4 +#define AR0231ATA_I2C_ADDRESS 0x11 +#define AR0231ATB_I2C_ADDRESS 0x12 +#define AR0231ATC_I2C_ADDRESS 0x13 +#define AR0231ATD_I2C_ADDRESS 0x14 + +static struct crlmodule_platform_data ar0231at_pdata = { + .lanes = AR0231AT_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t[]){ 264000000 }, + .module_name = "AR0231AT", +}; +#endif + +#if IS_ENABLED(CONFIG_VIDEO_MAX9286) +#define DS_MAX9286_LANES 4 +#define DS_MAX9286_I2C_ADAPTER_B 3 +#define DS_MAX9286_I2C_ADDRESS 0x48 + +static struct ipu_isys_csi2_config max9286_b_csi2_cfg = { + .nlanes = DS_MAX9286_LANES, + .port = 4, +}; + +struct max9286_subdev_i2c_info max9286_b_subdevs[] = { +#ifdef CONFIG_INTEL_IPU4_AR0231AT + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATA_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER_B, + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATB_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER_B, + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATC_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER_B, + }, + { + .board_info = { + .type = CRLMODULE_NAME, + .addr = AR0231ATD_I2C_ADDRESS, + .platform_data = &ar0231at_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER_B, + }, +#endif +}; + +static struct max9286_pdata max9286_b_pdata = { + .subdev_info = max9286_b_subdevs, + .subdev_num = ARRAY_SIZE(max9286_b_subdevs), + .reset_gpio = 195, +}; + +static struct ipu_isys_subdev_info max9286_b_sd = { + .csi2 = &max9286_b_csi2_cfg, + .i2c = { + .board_info = { + .type = "max9286", + .addr = DS_MAX9286_I2C_ADDRESS, + .platform_data = &max9286_b_pdata, + }, + .i2c_adapter_id = DS_MAX9286_I2C_ADAPTER_B, + } +}; +#endif + +static struct ipu_isys_clk_mapping clk_mapping[] = { + { CLKDEV_INIT("3-0048", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT(NULL, NULL, NULL), NULL } +}; + +static struct ipu_isys_subdev_pdata pdata = { + .subdevs = (struct ipu_isys_subdev_info *[]) { + &imx355_sd, + &imx355_sd2, + &imx319_sd, + &ak7375_sd, +#if IS_ENABLED(CONFIG_VIDEO_MAX9286) + &max9286_b_sd, +#endif + NULL, + }, + .clk_map = clk_mapping, +}; + +static void ipu4p_quirk(struct pci_dev *pci_dev) +{ + pr_info("Intel platform data PCI quirk for IPU4P\n"); + pci_dev->dev.platform_data = &pdata; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU_PCI_ID, ipu4p_quirk); + +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Qiu, Tianshu "); +MODULE_AUTHOR("Kun Jiang "); +MODULE_LICENSE("GPL"); From bced5cd8e19bc923dd1bef2ad10680c9dd734475 Mon Sep 17 00:00:00 2001 From: "Yew, Chang Ching" Date: Tue, 23 Oct 2018 13:23:05 +0800 Subject: [PATCH 1147/1276] media: intel-ipu4: ici driver base code; WW43 firmware f61e455. Base code for IPU ICI driver (Image Capture Interface). These patches add input system driver for Camera IPU interface. Squash of following patches from https://github.com/intel/linux-intel-lts/tree/4.14/base/drivers/media/pci/intel/ici/ 153823 media: ici: Remove set_pipeline_format when setting stream format e54253 media-intel-ipu4: ICI: Use standard values for memory type enums ef18ca media: intel-ipu4: ici: YUYV format support for HDMI ec4a1c Add dependencies in Kconfig for ICI f50ea8 ici: Change fourcc value for ICI_FORMAT_RGB888 9f77cd media: ici: fix for mem-leak pointed out by KW e6c0df ici: Check null pointer to avoid null pointer dereferencing 79f804 media:intel-ipu: dma functions for ici driver 8c08f8 media: intel-ipu: ICI camera driver WW16 2018 FW version: 20180412 Change-Id: I7a5bf16cfccc11ab9a0e4d32fdb31f0355e8c50a Signed-off-by: Yew, Chang Ching --- drivers/media/pci/intel/Kconfig | 33 + drivers/media/pci/intel/Makefile | 13 +- drivers/media/pci/intel/ici-fw-isys.h | 25 + drivers/media/pci/intel/ici/Makefile | 73 + drivers/media/pci/intel/ici/ici-dma.c | 417 +++++ .../media/pci/intel/ici/ici-isys-csi2-be.c | 280 ++++ .../media/pci/intel/ici/ici-isys-csi2-be.h | 36 + drivers/media/pci/intel/ici/ici-isys-csi2.c | 532 ++++++ drivers/media/pci/intel/ici/ici-isys-csi2.h | 156 ++ .../media/pci/intel/ici/ici-isys-frame-buf.c | 950 +++++++++++ .../media/pci/intel/ici/ici-isys-frame-buf.h | 139 ++ .../pci/intel/ici/ici-isys-pipeline-device.c | 493 ++++++ .../pci/intel/ici/ici-isys-pipeline-device.h | 62 + .../media/pci/intel/ici/ici-isys-pipeline.c | 174 ++ .../media/pci/intel/ici/ici-isys-pipeline.h | 98 ++ .../pci/intel/ici/ici-isys-stream-device.c | 397 +++++ .../pci/intel/ici/ici-isys-stream-device.h | 58 + drivers/media/pci/intel/ici/ici-isys-stream.c | 1450 +++++++++++++++++ drivers/media/pci/intel/ici/ici-isys-stream.h | 85 + drivers/media/pci/intel/ici/ici-isys-subdev.c | 548 +++++++ drivers/media/pci/intel/ici/ici-isys-subdev.h | 110 ++ drivers/media/pci/intel/ici/ici-isys-tpg.c | 214 +++ drivers/media/pci/intel/ici/ici-isys-tpg.h | 47 + drivers/media/pci/intel/ici/ici-isys.c | 1367 ++++++++++++++++ drivers/media/pci/intel/ici/ici-isys.h | 212 +++ .../media/pci/intel/ici/libintel-ipu4_ici.c | 403 +++++ include/media/ici.h | 141 ++ include/uapi/linux/ici.h | 203 +++ 28 files changed, 8714 insertions(+), 2 deletions(-) create mode 100644 drivers/media/pci/intel/ici-fw-isys.h create mode 100644 drivers/media/pci/intel/ici/Makefile create mode 100644 drivers/media/pci/intel/ici/ici-dma.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-csi2-be.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-csi2-be.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-csi2.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-csi2.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-frame-buf.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-frame-buf.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-pipeline-device.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-pipeline-device.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-pipeline.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-pipeline.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-stream-device.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-stream-device.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-stream.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-stream.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-subdev.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-subdev.h create mode 100644 drivers/media/pci/intel/ici/ici-isys-tpg.c create mode 100644 drivers/media/pci/intel/ici/ici-isys-tpg.h create mode 100644 drivers/media/pci/intel/ici/ici-isys.c create mode 100644 drivers/media/pci/intel/ici/ici-isys.h create mode 100644 drivers/media/pci/intel/ici/libintel-ipu4_ici.c create mode 100644 include/media/ici.h create mode 100644 include/uapi/linux/ici.h diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig index f9fcbc6c72b2..714a668d6a2d 100644 --- a/drivers/media/pci/intel/Kconfig +++ b/drivers/media/pci/intel/Kconfig @@ -57,3 +57,36 @@ config VIDEO_INTEL_IPU_WERROR Recommended for driver developers only. If in doubt, say "N". + +config VIDEO_INTEL_ICI + depends on VIDEO_INTEL_IPU + bool "Compile for ICI driver" + ---help--- + If selected ICI driver will be compiled + +config VIDEO_INTEL_UOS + bool "Compile driver per UOS" + ---help--- + If selected UOS driver components will be compiled + +config VIDEO_INTEL_IPU_ACRN + depends on X86_64 + bool "Compile for virtio mediation" + +choice + prompt "Virtio driver type" + depends on VIDEO_INTEL_IPU_ACRN + default VIDEO_INTEL_IPU_VIRTIO_BE + +config VIDEO_INTEL_IPU_VIRTIO_BE + bool "Configure IPU4 as virtio backend" + depends on VBS + ---help--- + Configuring IPU4 driver as virtio backend + +config VIDEO_INTEL_IPU_VIRTIO_FE + bool "Configure IPU4 as virtio frontend" + ---help--- + Configuring IPU4 driver as virtio frontend + +endchoice diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile index f26961842450..d46da91ce643 100644 --- a/drivers/media/pci/intel/Makefile +++ b/drivers/media/pci/intel/Makefile @@ -1,5 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2010 - 2018, Intel Corporation. +# +# +# Makefile for the IPU3 cio2, ImGU and IPU4 drivers +# + +obj-y += ipu3/ # force check the compile warning to make sure zero warnings # note we may have build issue when gcc upgraded. @@ -9,6 +15,9 @@ subdir-ccflags-y += $(call cc-disable-warning, implicit-fallthrough) subdir-ccflags-y += $(call cc-disable-warning, missing-field-initializers) subdir-ccflags-$(CONFIG_VIDEO_INTEL_IPU_WERROR) += -Werror -obj-$(CONFIG_VIDEO_INTEL_IPU4) += ipu4/ +ifndef CONFIG_VIDEO_INTEL_ICI +obj-$(CONFIG_VIDEO_INTEL_IPU4) += ipu4/ obj-$(CONFIG_VIDEO_INTEL_IPU4P) += ipu4/ -obj-y += ipu3/ +endif +obj-$(CONFIG_VIDEO_INTEL_ICI) += ici/ +obj-$(CONFIG_VIDEO_INTEL_IPU_ACRN) += virtio/ diff --git a/drivers/media/pci/intel/ici-fw-isys.h b/drivers/media/pci/intel/ici-fw-isys.h new file mode 100644 index 000000000000..afd7537da02b --- /dev/null +++ b/drivers/media/pci/intel/ici-fw-isys.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef ICI_FW_ISYS_H +#define ICI_FW_ISYS_H + +#include "ipu-fw-com.h" + +struct ici_isys; +int ici_fw_isys_init(struct ici_isys *isys, unsigned int num_streams); +int ici_fw_isys_close(struct ici_isys *isys); +int ici_fw_isys_simple_cmd(struct ici_isys *isys, + const unsigned int stream_handle, + enum ipu_fw_isys_send_type send_type); +int ici_fw_isys_complex_cmd(struct ici_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, enum ipu_fw_isys_send_type send_type); +int ici_fw_isys_send_proxy_token(struct ici_isys *isys, + unsigned int req_id, + unsigned int index, + unsigned int offset, u32 value); +void ici_fw_isys_cleanup(struct ici_isys *isys); +#endif diff --git a/drivers/media/pci/intel/ici/Makefile b/drivers/media/pci/intel/ici/Makefile new file mode 100644 index 000000000000..59a2561dcede --- /dev/null +++ b/drivers/media/pci/intel/ici/Makefile @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2010 - 2018, Intel Corporation. + +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +ifdef CONFIG_VIDEO_INTEL_ICI +ccflags-y += -DHAS_DUAL_CMD_CTX_SUPPORT=0 -DIPU_VC_SUPPORT -DIPU_HAS_ISA -DIPU_PSYS_LEGACY -Wframe-larger-than=4096 + + +# work-around to re-use ipu4-css and libintel-ipu4_ici.c together when +# compiling ICI-ISYS +$(shell cp -r $(srcpath)/$(src)/../ipu4/ipu4-css/ $(srcpath)/$(src)/) +$(shell cp -f $(srcpath)/$(src)/libintel-ipu4_ici.c $(srcpath)/$(src)/ipu4-css/libintel-ipu4.c) +$(shell cp -f $(srcpath)/$(src)/../ipu4/ipu-platform-resources.h $(srcpath)/$(src)/) + +intel-ipu4-objs +=../ipu.o \ + ../ipu-bus.o \ + ici-dma.o \ + ../ipu-buttress.o \ + ../ipu-trace.o \ + ../ipu-cpd.o \ + ../ipu-fw-com.o \ + ../ipu4/ipu4.o + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4.o + +intel-ipu4-mmu-objs += ../ipu-mmu.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-mmu.o + +ici-isys-mod-objs += \ + ici-isys.o \ + ici-isys-csi2.o \ + ici-isys-tpg.o \ + ici-isys-csi2-be.o \ + ici-isys-stream.o \ + ici-isys-frame-buf.o \ + ici-isys-subdev.o \ + ici-isys-pipeline.o \ + ici-isys-pipeline-device.o \ + ici-isys-stream-device.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += ici-isys-mod.o + +intel-ipu4-psys-objs += ../ipu-psys.o \ + ../ipu4/ipu4-resources.o \ + ../ipu4/ipu4-psys.o \ + +ifndef CONFIG_VIDEO_INTEL_IPU_FW_LIB +intel-ipu4-psys-objs += ipu4-fw-resources.o \ + ../ipu-fw-psys.o +endif + +ifeq ($(CONFIG_COMPAT),y) +intel-ipu4-psys-objs += ../ipu-psys-compat32.o +endif + +obj-$(CONFIG_VIDEO_INTEL_IPU) += intel-ipu4-psys.o + +ifdef CONFIG_VIDEO_INTEL_IPU_FW_LIB +include $(srcpath)/$(src)/ipu4-css/Makefile.isyslib +include $(srcpath)/$(src)/ipu4-css/Makefile.psyslib +endif + +subdir-ccflags-y += -I$(srcpath)/$(src)/../../../../../include/ +subdir-ccflags-y += -I$(srcpath)/$(src)/../ +subdir-ccflags-y += -I$(srcpath)/$(src)/../ipu4/ +subdir-ccflags-y += -I$(srcpath)/$(src)/ +subdir-ccflags-y += -I$(srcpath)/$(src)/ipu4-css + +ccflags-y += -DPARAMETER_INTERFACE_V2 + +endif diff --git a/drivers/media/pci/intel/ici/ici-dma.c b/drivers/media/pci/intel/ici/ici-dma.c new file mode 100644 index 000000000000..da501a2e8744 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-dma.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu-dma.h" +#include "ipu-mmu.h" + +static struct page **__intel_ipu4_dma_alloc(struct device *dev, + size_t buf_size, + gfp_t gfp, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + int num_pages = buf_size >> PAGE_SHIFT; + int array_size = num_pages * sizeof(struct page *); + struct page **page_list; + int i = 0; + + if (array_size <= PAGE_SIZE) + page_list = kzalloc(array_size, GFP_KERNEL); + else + page_list = vzalloc(array_size); + if (!page_list) + return NULL; + + gfp |= __GFP_NOWARN; + + while (num_pages) { + int j, order = __fls(num_pages); + + page_list[i] = alloc_pages(gfp, order); + while (!page_list[i] && order) + page_list[i] = alloc_pages(gfp, --order); + if (!page_list[i]) + goto error; + + if (order) { + split_page(page_list[i], order); + j = 1 << order; + while (--j) + page_list[i + j] = page_list[i] + j; + } + + i += 1 << order; + num_pages -= 1 << order; + } + + return page_list; +error: + while (i--) + if (page_list[i]) + __free_pages(page_list[i], 0); + if (array_size <= PAGE_SIZE) + kfree(page_list); + else + vfree(page_list); + return NULL; +} + +static int __intel_ipu4_dma_free(struct device *dev, struct page **page_list, + size_t buf_size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + int num_pages = buf_size >> PAGE_SHIFT; + int array_size = num_pages * sizeof(struct page *); + int i; + + for (i = 0; i < num_pages; i++) { + if (page_list[i]) { + __free_pages(page_list[i], 0); + } + } + + if (array_size <= PAGE_SIZE) + kfree(page_list); + else + vfree(page_list); + return 0; +} + +static void intel_ipu4_dma_sync_single_for_cpu( + struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + + clflush_cache_range( + phys_to_virt(iommu_iova_to_phys( + mmu->dmap->domain, dma_handle)), size); +} + +static void intel_ipu4_dma_sync_sg_for_cpu( + struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction dir) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nents, i) { + clflush_cache_range( + phys_to_virt(iommu_iova_to_phys( + mmu->dmap->domain, + sg_dma_address(sg))), + sg->length); + } +} + +static void *intel_ipu4_dma_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct page **pages; + struct iova *iova; + struct vm_struct *area; + int i; + int rval; + + size = PAGE_ALIGN(size); + + iova = alloc_iova(&mmu->dmap->iovad, size >> PAGE_SHIFT, + dma_get_mask(dev) >> PAGE_SHIFT, 0); + if (!iova) + return NULL; + + pages = __intel_ipu4_dma_alloc(dev, size, gfp, attrs); + if (!pages) + goto out_free_iova; + + for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { + rval = iommu_map(mmu->dmap->domain, + (iova->pfn_lo + i) << PAGE_SHIFT, + page_to_phys(pages[i]), PAGE_SIZE, 0); + if (rval) + goto out_unmap; + } + + area = __get_vm_area(size, 0, VMALLOC_START, VMALLOC_END); + if (!area) + goto out_unmap; + + area->pages = pages; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) + if (map_vm_area(area, PAGE_KERNEL, &pages)) +#else + if (map_vm_area(area, PAGE_KERNEL, pages)) +#endif + goto out_vunmap; + + *dma_handle = iova->pfn_lo << PAGE_SHIFT; + + mmu->tlb_invalidate(mmu); + + return area->addr; + +out_vunmap: + vunmap(area->addr); + +out_unmap: + __intel_ipu4_dma_free(dev, pages, size, attrs); + for (i--; i >= 0; i--) { + iommu_unmap(mmu->dmap->domain, (iova->pfn_lo + i) << PAGE_SHIFT, + PAGE_SIZE); + } +out_free_iova: + __free_iova(&mmu->dmap->iovad, iova); + + return NULL; +} + +static void intel_ipu4_dma_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct vm_struct *area = find_vm_area(vaddr); + struct iova *iova = find_iova(&mmu->dmap->iovad, + dma_handle >> PAGE_SHIFT); + + if (WARN_ON(!area)) + return; + + if (WARN_ON(!area->pages)) + return; + + BUG_ON(!iova); + + size = PAGE_ALIGN(size); + + vunmap(vaddr); + + iommu_unmap(mmu->dmap->domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + + __intel_ipu4_dma_free(dev, area->pages, size, attrs); + + __free_iova(&mmu->dmap->iovad, iova); + + mmu->tlb_invalidate(mmu); +} + +static int intel_ipu4_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *addr, dma_addr_t iova, size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct vm_struct *area = find_vm_area(addr); + size_t count = PAGE_ALIGN(size) >> PAGE_SHIFT; + size_t i; + + if (!area) + return -EFAULT; + + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + + if (size > area->size) + return -EFAULT; + + for (i = 0; i < count; i++) + vm_insert_page(vma, vma->vm_start + (i << PAGE_SHIFT), + area->pages[i]); + + return 0; +} + +static void intel_ipu4_dma_unmap_sg(struct device *dev, + struct scatterlist *sglist, + int nents, enum dma_data_direction dir, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct iova *iova = find_iova(&mmu->dmap->iovad, + sg_dma_address(sglist) >> PAGE_SHIFT); + + if (!nents) + return; + + BUG_ON(!iova); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + intel_ipu4_dma_sync_sg_for_cpu(dev, sglist, nents, + DMA_BIDIRECTIONAL); + + iommu_unmap(mmu->dmap->domain, iova->pfn_lo << PAGE_SHIFT, + (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT); + + mmu->tlb_invalidate(mmu); + + __free_iova(&mmu->dmap->iovad, iova); +} + +static int intel_ipu4_dma_map_sg(struct device *dev, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct device *aiommu = to_ipu_bus_device(dev)->iommu; + struct ipu_mmu *mmu = dev_get_drvdata(aiommu); + struct scatterlist *sg; + struct iova *iova; + size_t size = 0; + uint32_t iova_addr; + int i; + + for_each_sg(sglist, sg, nents, i) + size += PAGE_ALIGN(sg->length) >> PAGE_SHIFT; + + dev_dbg(dev, "dmamap: mapping sg %d entries, %zu pages\n", nents, size); + + iova = alloc_iova(&mmu->dmap->iovad, size, + dma_get_mask(dev) >> PAGE_SHIFT, 0); + if (!iova) + return 0; + + dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, + iova->pfn_hi); + + iova_addr = iova->pfn_lo; + + for_each_sg(sglist, sg, nents, i) { + int rval; + + dev_dbg(dev, + "dmamap details: mapping entry %d: iova 0x%8.8x, \ + physical 0x%16.16llx\n", + i, iova_addr << PAGE_SHIFT, page_to_phys(sg_page(sg))); + rval = iommu_map(mmu->dmap->domain, iova_addr << PAGE_SHIFT, + page_to_phys(sg_page(sg)), + PAGE_ALIGN(sg->length), 0); + if (rval) + goto out_fail; + sg_dma_address(sg) = iova_addr << PAGE_SHIFT; +#ifdef CONFIG_NEED_SG_DMA_LENGTH + sg_dma_len(sg) = sg->length; +#endif /* CONFIG_NEED_SG_DMA_LENGTH */ + + iova_addr += PAGE_ALIGN(sg->length) >> PAGE_SHIFT; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) +#else + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) +#endif + intel_ipu4_dma_sync_sg_for_cpu(dev, sglist, nents, + DMA_BIDIRECTIONAL); + + mmu->tlb_invalidate(mmu); + + return nents; + +out_fail: + intel_ipu4_dma_unmap_sg(dev, sglist, i, dir, attrs); + + return 0; +} + +/* +* Create scatter-list for the already allocated DMA buffer +*/ +static int intel_ipu4_dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs *attrs +#else + unsigned long attrs +#endif + ) +{ + struct vm_struct *area = find_vm_area(cpu_addr); + int n_pages; + int ret = 0; + + if (WARN_ON(!area->pages)) + return -ENOMEM; + + n_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + + ret = sg_alloc_table_from_pages(sgt, area->pages, n_pages, 0, size, + GFP_KERNEL); + if (ret) + dev_dbg(dev, "IPU get sgt table fail\n"); + + return ret; +} + +const struct dma_map_ops ipu_dma_ops = { + .alloc = intel_ipu4_dma_alloc, + .free = intel_ipu4_dma_free, + .mmap = intel_ipu4_dma_mmap, + .map_sg = intel_ipu4_dma_map_sg, + .unmap_sg = intel_ipu4_dma_unmap_sg, + .sync_single_for_cpu = intel_ipu4_dma_sync_single_for_cpu, + .sync_single_for_device = intel_ipu4_dma_sync_single_for_cpu, + .sync_sg_for_cpu = intel_ipu4_dma_sync_sg_for_cpu, + .sync_sg_for_device = intel_ipu4_dma_sync_sg_for_cpu, + .get_sgtable = intel_ipu4_dma_get_sgtable, +}; +EXPORT_SYMBOL_GPL(ipu_dma_ops); + diff --git a/drivers/media/pci/intel/ici/ici-isys-csi2-be.c b/drivers/media/pci/intel/ici/ici-isys-csi2-be.c new file mode 100644 index 000000000000..6841263a8d16 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-csi2-be.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED + +#ifndef IPU4_DEBUG +#define IPU4_DEBUG 1 +#endif + +#include "./ici/ici-isys-csi2-be.h" +#include "isysapi/interface/ia_css_isysapi_fw_types.h" + +#define ici_asd_to_csi2_be(__asd) \ + container_of(__asd, struct ici_isys_csi2_be, asd) + +static const uint32_t ici_csi2_be_supported_codes_pad[] = { + ICI_FORMAT_SBGGR12, + ICI_FORMAT_SGBRG12, + ICI_FORMAT_SGRBG12, + ICI_FORMAT_SRGGB12, + ICI_FORMAT_SBGGR10, + ICI_FORMAT_SGBRG10, + ICI_FORMAT_SGRBG10, + ICI_FORMAT_SRGGB10, + ICI_FORMAT_SBGGR8, + ICI_FORMAT_SGBRG8, + ICI_FORMAT_SGRBG8, + ICI_FORMAT_SRGGB8, + 0, +}; + +static const uint32_t ici_csi2_be_soc_supported_codes_pad[] = { + ICI_FORMAT_RGB888, + ICI_FORMAT_RGB565, + ICI_FORMAT_UYVY, + ICI_FORMAT_YUYV, + ICI_FORMAT_SBGGR12, + ICI_FORMAT_SGBRG12, + ICI_FORMAT_SGRBG12, + ICI_FORMAT_SRGGB12, + ICI_FORMAT_SBGGR10, + ICI_FORMAT_SGBRG10, + ICI_FORMAT_SGRBG10, + ICI_FORMAT_SRGGB10, + ICI_FORMAT_SBGGR8, + ICI_FORMAT_SGBRG8, + ICI_FORMAT_SGRBG8, + ICI_FORMAT_SRGGB8, + 0, +}; + +static const uint32_t *ici_csi2_be_supported_codes[] = { + ici_csi2_be_supported_codes_pad, + ici_csi2_be_supported_codes_pad, +}; + +static const uint32_t *ici_csi2_be_soc_supported_codes[] = { + ici_csi2_be_soc_supported_codes_pad, + ici_csi2_be_soc_supported_codes_pad, +}; + +static int get_supported_code_index(uint32_t code) +{ + int i; + + for (i = 0; ici_csi2_be_supported_codes_pad[i]; i++) { + if (ici_csi2_be_supported_codes_pad[i] == code) + return i; + } + return -EINVAL; +} + +void ici_csi2_be_set_ffmt(struct ici_isys_subdev *asd, + unsigned pad, + struct ici_framefmt *ffmt) +{ + struct ici_framefmt *cur_ffmt = + __ici_isys_subdev_get_ffmt(asd, pad); + int idx=0; + if (!cur_ffmt) + return; + + ffmt->colorspace = 0; + memset(ffmt->reserved, 0, sizeof(ffmt->reserved)); + switch (pad) { + case CSI2_BE_ICI_PAD_SINK: + DEBUGK("%s: sink pad %u\n", __func__, pad); + if (ffmt->field != ICI_FIELD_ALTERNATE) + ffmt->field = ICI_FIELD_NONE; + *cur_ffmt = *ffmt; + + ici_isys_subdev_fmt_propagate(asd, pad, NULL, + ICI_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + ffmt); + break; + case CSI2_BE_ICI_PAD_SOURCE: { + struct ici_framefmt *sink_ffmt = + __ici_isys_subdev_get_ffmt(asd, + CSI2_BE_ICI_PAD_SINK); + + struct ici_rect *r = + &asd->crop[CSI2_BE_ICI_PAD_SOURCE]; + + u32 code = 0; + if (sink_ffmt) + code = sink_ffmt->pixelformat; + + idx = get_supported_code_index(code); + + DEBUGK("%s: source pad %u\n", __func__, pad); + + if (asd->valid_tgts[CSI2_BE_ICI_PAD_SOURCE].crop + && idx >= 0) { + int crop_info = 0; + + DEBUGK("%s: setting CROP, pad %u\n", __func__, + pad); + + if (r->top & 1) + crop_info |= CSI2_BE_ICI_CROP_VER; + if (r->left & 1) + crop_info |= CSI2_BE_ICI_CROP_HOR; + code = ici_csi2_be_supported_codes_pad[(( + idx & + CSI2_BE_ICI_CROP_MASK) + ^ + crop_info) + + + (idx & + ~CSI2_BE_ICI_CROP_MASK)]; + } + + DEBUGK("%s: setting to w:%u,h:%u,pf:%u,field:%u\n", + __func__, r->width, + r->height, code, sink_ffmt->field); + cur_ffmt->width = r->width; + cur_ffmt->height = r->height; + cur_ffmt->pixelformat = code; + cur_ffmt->field = sink_ffmt->field; + *ffmt = *cur_ffmt; + break; + } + default: + BUG_ON(1); + } +} + +static int ici_csi2_be_set_stream( + struct ici_isys_node *node, + void* ip, + int state) +{ + return 0; +} + +static int ici_csi2_be_pipeline_validate( + struct node_pipeline *inp, + struct ici_isys_node *node) +{ + struct ici_isys_subdev* asd = node->sd; + struct ici_isys_csi2_be *csi2_be = + ici_asd_to_csi2_be(asd); + struct ici_isys_pipeline *ip = + ici_nodepipe_to_pipeline(inp); + + ip->csi2_be = csi2_be; + return 0; +} + +int ici_isys_csi2_be_init(struct ici_isys_csi2_be + *csi2_be, + struct ici_isys *isys, + unsigned int type) +{ + struct ici_pad_framefmt pff = { + .pad.pad_idx = CSI2_BE_ICI_PAD_SINK, + .ffmt = { + .width = 4096, + .height = 3072, + }, + }; + int rval; + char name[ICI_MAX_NODE_NAME]; + + dev_info(&isys->adev->dev, "ici_isys_csi2_be_init\n"); + + csi2_be->asd.isys = isys; + if (type == ICI_BE_RAW) { + csi2_be->as.buf_list.css_pin_type = + IA_CSS_ISYS_PIN_TYPE_RAW_NS; + snprintf(name, sizeof(name), + IPU_ISYS_ENTITY_PREFIX " CSI2 BE"); + } else if (type >= ICI_BE_SOC) { + csi2_be->as.buf_list.css_pin_type = + IA_CSS_ISYS_PIN_TYPE_RAW_SOC; + snprintf(name, sizeof(name), + IPU_ISYS_ENTITY_PREFIX " CSI2 BE SOC %u", type-1); + } else { + return -EINVAL; + } + + rval = ici_isys_subdev_init(&csi2_be->asd, + name, + NR_OF_CSI2_BE_ICI_PADS, + 0); + if (rval) { + dev_err(&isys->adev->dev, "can't init subdevice\n"); + goto fail_subdev; + } + + csi2_be->asd.pads[CSI2_BE_ICI_PAD_SINK].flags = ICI_PAD_FLAGS_SINK + | ICI_PAD_FLAGS_MUST_CONNECT; + csi2_be->asd.pads[CSI2_BE_ICI_PAD_SOURCE].flags = + ICI_PAD_FLAGS_SOURCE; + + if (type == ICI_BE_RAW) + csi2_be->asd.valid_tgts[CSI2_BE_ICI_PAD_SOURCE].crop = true; + else + csi2_be->asd.valid_tgts[CSI2_BE_ICI_PAD_SOURCE].crop = false; + + csi2_be->asd.set_ffmt_internal = ici_csi2_be_set_ffmt; + + if (type == ICI_BE_RAW) { + csi2_be->asd.supported_codes = ici_csi2_be_supported_codes; + csi2_be->asd.be_mode = ICI_BE_RAW; + csi2_be->asd.isl_mode = ICI_ISL_CSI2_BE; + } else { + csi2_be->asd.supported_codes = ici_csi2_be_soc_supported_codes; + csi2_be->asd.be_mode = ICI_BE_SOC; + csi2_be->asd.isl_mode = ICI_ISL_OFF; + } + + csi2_be->asd.node.node_set_pad_ffmt(&csi2_be->asd.node, &pff); + /* ipu4_isys_csi2_be2_set_sel(&csi2_be->asd.sd, NULL, &sel); */ + /* csi2_be->asd.sd.internal_ops = &csi2_be_sd_internal_ops; */ + csi2_be->asd.node.node_set_streaming = + ici_csi2_be_set_stream; + csi2_be->asd.node.node_pipeline_validate = + ici_csi2_be_pipeline_validate; + + + csi2_be->as.isys = isys; + if (type == ICI_BE_RAW) + csi2_be->as.pfmts = ici_isys_pfmts; + else + csi2_be->as.pfmts = ici_isys_pfmts_be_soc; + + csi2_be->as.try_fmt_vid_mplane = + ici_isys_video_try_fmt_vid_mplane_default; + csi2_be->as.prepare_firmware_stream_cfg = + ici_isys_prepare_firmware_stream_cfg_default; + + rval = ici_isys_stream_init(&csi2_be->as, &csi2_be->asd, + &csi2_be->asd.node, CSI2_BE_ICI_PAD_SOURCE, + ICI_PAD_FLAGS_SINK); + if (rval) { + dev_err(&isys->adev->dev, "can't init stream node\n"); + goto fail_stream; + } + return 0; + +fail_stream: + ici_isys_subdev_cleanup(&csi2_be->asd); +fail_subdev: + return rval; +} +EXPORT_SYMBOL(ici_isys_csi2_be_init); + +void ici_isys_csi2_be_cleanup(struct ici_isys_csi2_be + *csi2_be) +{ + ici_isys_subdev_cleanup(&csi2_be->asd); + ici_isys_stream_cleanup(&csi2_be->as); +} +EXPORT_SYMBOL(ici_isys_csi2_be_cleanup); + +#endif /* ICI_ENABLED */ diff --git a/drivers/media/pci/intel/ici/ici-isys-csi2-be.h b/drivers/media/pci/intel/ici/ici-isys-csi2-be.h new file mode 100644 index 000000000000..428619d24520 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-csi2-be.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_CSI2_BE_H +#define ICI_ISYS_CSI2_BE_H + +#include "ici-isys-subdev.h" +#include "ici-isys-stream.h" + +#define CSI2_BE_ICI_PAD_SINK 0 +#define CSI2_BE_ICI_PAD_SOURCE 1 +#define NR_OF_CSI2_BE_ICI_PADS 2 + +#define CSI2_BE_ICI_CROP_HOR (1 << 0) +#define CSI2_BE_ICI_CROP_VER (1 << 1) +#define CSI2_BE_ICI_CROP_MASK (CSI2_BE_ICI_CROP_VER | CSI2_BE_ICI_CROP_HOR) + +struct ici_isys_csi2_be_pdata; +/* + * struct ici_isys_csi2_be + */ +struct ici_isys_csi2_be { + struct ici_isys_csi2_be_pdata *pdata; + struct ici_isys_subdev asd; + struct ici_isys_stream as; +}; + +int ici_isys_csi2_be_init(struct ici_isys_csi2_be + *csi2_be, + struct ici_isys *isys, unsigned int type); +void ici_isys_csi2_be_cleanup(struct ici_isys_csi2_be + *csi2_be); + +#endif /* ICI_ISYS_CSI2_BE_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-csi2.c b/drivers/media/pci/intel/ici/ici-isys-csi2.c new file mode 100644 index 000000000000..65416dd6d83b --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-csi2.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED +#include +#include "./ici/ici-isys-subdev.h" +#include "./ici/ici-isys-stream.h" +#include "./ici/ici-isys-csi2.h" +#include "isysapi/interface/ia_css_isysapi_fw_types.h" +#include "ipu-platform-isys-csi2-reg.h" +//#include "intel-ipu-isys-csi2-common.h" + +#define CSI2_ACCINV 8 + +#define ici_asd_to_csi2(__asd, index) \ + container_of(__asd, struct ici_isys_csi2, asd[index]) + +static const uint32_t ici_csi2_supported_codes_pad_sink[] = { + ICI_FORMAT_RGB888, + ICI_FORMAT_RGB565, + ICI_FORMAT_UYVY, + ICI_FORMAT_YUYV, + ICI_FORMAT_SBGGR12, + ICI_FORMAT_SGBRG12, + ICI_FORMAT_SGRBG12, + ICI_FORMAT_SRGGB12, + ICI_FORMAT_SBGGR10, + ICI_FORMAT_SGBRG10, + ICI_FORMAT_SGRBG10, + ICI_FORMAT_SRGGB10, + ICI_FORMAT_SBGGR8, + ICI_FORMAT_SGBRG8, + ICI_FORMAT_SGRBG8, + ICI_FORMAT_SRGGB8, + ICI_FORMAT_SBGGR10_DPCM8, + ICI_FORMAT_SGBRG10_DPCM8, + ICI_FORMAT_SGRBG10_DPCM8, + ICI_FORMAT_SRGGB10_DPCM8, + 0, +}; + +static const uint32_t ici_csi2_supported_codes_pad_source[] = { + ICI_FORMAT_RGB888, + ICI_FORMAT_RGB565, + ICI_FORMAT_UYVY, + ICI_FORMAT_YUYV, + ICI_FORMAT_SBGGR12, + ICI_FORMAT_SGBRG12, + ICI_FORMAT_SGRBG12, + ICI_FORMAT_SRGGB12, + ICI_FORMAT_SBGGR10, + ICI_FORMAT_SGBRG10, + ICI_FORMAT_SGRBG10, + ICI_FORMAT_SRGGB10, + ICI_FORMAT_SBGGR8, + ICI_FORMAT_SGBRG8, + ICI_FORMAT_SGRBG8, + ICI_FORMAT_SRGGB8, + 0, +}; + +static const uint32_t *ici_csi2_supported_codes[] = { + ici_csi2_supported_codes_pad_sink, + ici_csi2_supported_codes_pad_source, +}; + +void ici_csi2_set_ffmt(struct ici_isys_subdev *asd, + unsigned pad, + struct ici_framefmt *ffmt) +{ + struct ici_framefmt *cur_ffmt = + __ici_isys_subdev_get_ffmt(asd, pad); + + if (ffmt->field != ICI_FIELD_ALTERNATE) + ffmt->field = ICI_FIELD_NONE; + ffmt->colorspace = 0; + memset(ffmt->reserved, 0, sizeof(ffmt->reserved)); + + switch (pad) { + case CSI2_ICI_PAD_SINK: + if (cur_ffmt) + *cur_ffmt = *ffmt; + ici_isys_subdev_fmt_propagate(asd, pad, NULL, + ICI_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + ffmt); + break; + case CSI2_ICI_PAD_SOURCE:{ + struct ici_framefmt *sink_ffmt = + __ici_isys_subdev_get_ffmt(asd, + CSI2_ICI_PAD_SINK); + if (sink_ffmt) { + *cur_ffmt = *sink_ffmt; + cur_ffmt->pixelformat = + ici_isys_subdev_code_to_uncompressed + (sink_ffmt->pixelformat); + *ffmt = *cur_ffmt; + } + break; + } + default: + BUG_ON(1); + } +} + +static void ici_isys_csi2_error(struct ici_isys_csi2 + *csi2) +{ + /* + * Strings corresponding to CSI-2 receiver errors are here. + * Corresponding macros are defined in the header file. + */ + static const struct ici_isys_csi2_error { + const char *error_string; + bool is_info_only; + } errors[] = { + { + "Single packet header error corrected", true}, { + "Multiple packet header errors detected", true}, { + "Payload checksum (CRC) error", true}, { + "FIFO overflow", false}, { + "Reserved short packet data type detected", true}, { + "Reserved long packet data type detected", true}, { + "Incomplete long packet detected", false}, { + "Frame sync error", false}, { + "Line sync error", false}, { + "DPHY recoverable synchronization error", true}, { + "DPHY non-recoverable synchronization error", false}, { + "Escape mode error", true}, { + "Escape mode trigger event", true}, { + "Escape mode ultra-low power state for data lane(s)", true}, + { + "Escape mode ultra-low power state exit for clock lane", + true}, { + "Inter-frame short packet discarded", true}, { + "Inter-frame long packet discarded", true},}; + u32 status = csi2->receiver_errors; + unsigned int i; + + csi2->receiver_errors = 0; + + for (i = 0; i < ARRAY_SIZE(errors); i++) { + if (status & BIT(i)) { + if (errors[i].is_info_only) + dev_dbg(&csi2->isys->adev->dev, + "csi2-%i info: %s\n", + csi2->index, errors[i].error_string); + else + dev_err_ratelimited(&csi2->isys->adev->dev, + "csi2-%i error: %s\n", + csi2->index, + errors[i].error_string); + } + } +} + +#define DIV_SHIFT 8 + +static uint32_t calc_timing(int32_t a, int32_t b, int64_t link_freq, + int32_t accinv) +{ + return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT) + / (int32_t) (link_freq >> DIV_SHIFT)); +} + +int ici_isys_csi2_calc_timing(struct ici_isys_csi2 + *csi2, struct + ici_isys_csi2_timing + *timing, uint32_t accinv) +{ + int64_t link_frequency = 0; + + int idx, rval; + + struct ici_ext_subdev *sd = + (struct ici_ext_subdev*)csi2->ext_sd; + + struct ici_ext_sd_param param = { + .sd = sd, + .id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .type = ICI_EXT_SD_PARAM_TYPE_INT32, + }; + + if (!sd || !sd->get_param) { + dev_err(&csi2->isys->adev->dev, + "External device not available\n"); + return -ENODEV; + } + rval = sd->get_param(¶m); + if (rval) { + dev_info(&csi2->isys->adev->dev, "can't get link frequency\n"); + return rval; + } + + idx = param.val; + param.type = ICI_EXT_SD_PARAM_TYPE_INT64; + + rval = sd->get_menu_item(¶m, idx); + if (rval) { + dev_info(&csi2->isys->adev->dev, "can't get menu item\n"); + return rval; + } + + link_frequency = param.val; + dev_dbg(&csi2->isys->adev->dev, "%s: link frequency %lld\n", __func__, + link_frequency); + + if (!link_frequency) + return -EINVAL; + + timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A, + CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B, + link_frequency, accinv); + timing->csettle = + calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A, + CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B, link_frequency, + accinv); + dev_dbg(&csi2->isys->adev->dev, "ctermen %u\n", timing->ctermen); + dev_dbg(&csi2->isys->adev->dev, "csettle %u\n", timing->csettle); + + timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A, + CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B, + link_frequency, accinv); + timing->dsettle = + calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A, + CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B, link_frequency, + accinv); + dev_dbg(&csi2->isys->adev->dev, "dtermen %u\n", timing->dtermen); + dev_dbg(&csi2->isys->adev->dev, "dsettle %u\n", timing->dsettle); + + return 0; +} + +static void ici_isys_register_errors(struct + ici_isys_csi2 + *csi2) +{ + u32 status = readl(csi2->base + CSI2_REG_CSIRX_IRQ_STATUS); + + dev_dbg(&csi2->isys->adev->dev, + "ici_isys_register_errors\n"); + writel(status, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR); + csi2->receiver_errors |= status; +} + +static void ici_isys_csi2_sof_event(struct ici_isys_csi2 + *csi2, unsigned int vc) +{ + unsigned long flags; + + spin_lock_irqsave(&csi2->isys->lock, flags); + csi2->in_frame = true; + spin_unlock_irqrestore(&csi2->isys->lock, flags); +} + +static void ici_isys_csi2_eof_event(struct ici_isys_csi2 + *csi2, unsigned int vc) +{ + unsigned long flags; + + spin_lock_irqsave(&csi2->isys->lock, flags); + csi2->in_frame = false; + if (csi2->wait_for_sync) + complete(&csi2->eof_completion); + spin_unlock_irqrestore(&csi2->isys->lock, flags); +} + +void ici_isys_csi2_isr(struct ici_isys_csi2 *csi2) +{ + u32 status = readl(csi2->base + CSI2_REG_CSI2PART_IRQ_STATUS); + unsigned int i; + + writel(status, csi2->base + CSI2_REG_CSI2PART_IRQ_CLEAR); + + if (status & CSI2_CSI2PART_IRQ_CSIRX) + ici_isys_register_errors(csi2); + + for (i = 0; i < NR_OF_CSI2_ICI_VC; i++) { + if ((status & CSI2_IRQ_FS_VC(i))) + ici_isys_csi2_sof_event(csi2, i); + + if ((status & CSI2_IRQ_FE_VC(i))) + ici_isys_csi2_eof_event(csi2, i); + } + +} +EXPORT_SYMBOL(ici_isys_csi2_isr); + +void ici_isys_csi2_wait_last_eof(struct ici_isys_csi2 + *csi2) +{ + unsigned long flags; + int tout; + + spin_lock_irqsave(&csi2->isys->lock, flags); + if (!csi2->in_frame) { + spin_unlock_irqrestore(&csi2->isys->lock, flags); + return; + } + + reinit_completion(&csi2->eof_completion); + csi2->wait_for_sync = true; + spin_unlock_irqrestore(&csi2->isys->lock, flags); + tout = wait_for_completion_timeout(&csi2->eof_completion, + ICI_EOF_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(&csi2->isys->adev->dev, + "csi2-%d: timeout at sync to eof\n", csi2->index); + } + csi2->wait_for_sync = false; +} + +static void csi2_capture_done(struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info) +{ + ici_isys_frame_buf_capture_done(ip, info); + if (ip->csi2) + ici_isys_csi2_error(ip->csi2); +} + +int ici_csi2_set_stream( + struct ici_isys_node *node, + void* ip, + int state) +{ + struct ici_isys_subdev* asd = node->sd; + struct ici_isys_csi2 *csi2 = + ici_asd_to_csi2(asd, asd->index); + struct ici_isys_csi2_timing timing = { 0 }; + unsigned int i, nlanes; + int rval; + u32 csi2csirx = 0, csi2part = 0; + + dev_dbg(&csi2->isys->adev->dev, "csi2 s_stream %d\n", state); + + if (!state) { + ici_isys_csi2_error(csi2); + writel(0, csi2->base + CSI2_REG_CSI_RX_CONFIG); + writel(0, csi2->base + CSI2_REG_CSI_RX_ENABLE); + + /* Disable interrupts */ + writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_MASK); + writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_ENABLE); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE); + return 0; + } + + ici_isys_stream_add_capture_done(ip, csi2_capture_done); + + nlanes = csi2->nlanes; + + rval = ici_isys_csi2_calc_timing(csi2, + &timing, + CSI2_ACCINV); + if (rval) + return rval; + + writel(timing.ctermen, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_CLANE); + writel(timing.csettle, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_CLANE); + + for (i = 0; i < nlanes; i++) { + writel(timing.dtermen, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_DLANE(i)); + writel(timing.dsettle, + csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_DLANE(i)); + } + writel(CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING | + CSI2_CSI_RX_CONFIG_RELEASE_LP11, + csi2->base + CSI2_REG_CSI_RX_CONFIG); + + writel(nlanes, csi2->base + CSI2_REG_CSI_RX_NOF_ENABLED_LANES); + + writel(CSI2_CSI_RX_ENABLE_ENABLE, csi2->base + CSI2_REG_CSI_RX_ENABLE); + + /* SOF enabled from CSI2PART register in B0 */ + for (i = 0; i < NR_OF_CSI2_ICI_VC; i++) + csi2part |= CSI2_IRQ_FS_VC(i) | CSI2_IRQ_FE_VC(i); + + /* Enable csi2 receiver error interrupts */ + csi2csirx = BIT(CSI2_CSIRX_NUM_ERRORS) - 1; + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_EDGE); + writel(0, csi2->base + CSI2_REG_CSIRX_IRQ_LEVEL_NOT_PULSE); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_MASK); + writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_ENABLE); + + /* Enable csi2 error and SOF-related irqs */ + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_EDGE); + writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_LEVEL_NOT_PULSE); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_CLEAR); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK); + writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE); + + return 0; +} + +unsigned int ici_isys_csi2_get_current_field( + struct device* dev, + struct ici_isys_mipi_packet_header *ph) +{ + unsigned int field; + + /* Check if the first SOF packet is received. */ + if ((ph->dtype & ICI_ISYS_SHORT_PACKET_DTYPE_MASK) != 0) + dev_warn(dev, + "First short packet is not SOF.\n"); + field = (ph->word_count % 2) ? ICI_FIELD_TOP : + ICI_FIELD_BOTTOM; + dev_dbg(dev, + "Interlaced field ready. frame_num = %d field = %d\n", + ph->word_count, field); + + return field; +} + +static int ici_csi2_pipeline_validate( + struct node_pipeline *inp, + struct ici_isys_node *node) +{ + struct ici_isys_subdev* asd = node->sd; + struct ici_isys_csi2 *csi2 = + ici_asd_to_csi2(asd, asd->index); + struct ici_isys_pipeline *ip = + ici_nodepipe_to_pipeline(inp); + + if (ip->csi2) { + dev_err(&csi2->isys->adev->dev, + "Pipeline does not support > 1 CSI2 node\n"); + return -EINVAL; + } + node->pipe = inp; + ip->csi2 = csi2; + ip->asd_source = asd; + ip->vc = asd - csi2->asd; // index of asd element in csi2->asd array + ip->asd_source_pad_id = CSI2_ICI_PAD_SINK; + return 0; +} + +int ici_isys_csi2_init(struct ici_isys_csi2 *csi2, + struct ici_isys *isys, + void __iomem *base, unsigned int index) +{ + struct ici_pad_framefmt fmt = { + .pad.pad_idx = CSI2_ICI_PAD_SINK, + .ffmt = { + .width = 4096, + .height = 3072, + }, + }; + + int rval; + char name[ICI_MAX_NODE_NAME]; + unsigned int i; + + csi2->isys = isys; + csi2->base = base; + csi2->index = index; + + for(i=0; iasd[i].isys = isys; + rval = ici_isys_subdev_init(&csi2->asd[i], + name, + NR_OF_CSI2_ICI_PADS, + i); + if (rval) + goto fail; + + csi2->asd[i].pads[CSI2_ICI_PAD_SINK].flags = ICI_PAD_FLAGS_SINK; + csi2->asd[i].pads[CSI2_ICI_PAD_SOURCE].flags = ICI_PAD_FLAGS_SOURCE; + + csi2->asd[i].source = IA_CSS_ISYS_STREAM_SRC_CSI2_PORT0 + index; + csi2->asd[i].supported_codes = ici_csi2_supported_codes; + csi2->asd[i].set_ffmt_internal = ici_csi2_set_ffmt; + csi2->asd[i].node.node_set_streaming = + ici_csi2_set_stream; + csi2->asd[i].node.node_pipeline_validate = + ici_csi2_pipeline_validate; + + csi2->asd[i].node.node_set_pad_ffmt(&csi2->asd[i].node, &fmt); + + snprintf(csi2->as[i].node.name, sizeof(csi2->as[i].node.name), + IPU_ISYS_ENTITY_PREFIX " CSI-2 %u VC %u capture", index, i); + csi2->as[i].isys = isys; + csi2->as[i].try_fmt_vid_mplane = + ici_isys_video_try_fmt_vid_mplane_default; + csi2->as[i].prepare_firmware_stream_cfg = + ici_isys_prepare_firmware_stream_cfg_default; + csi2->as[i].packed = true; + csi2->as[i].buf_list.css_pin_type = IA_CSS_ISYS_PIN_TYPE_MIPI; + csi2->as[i].pfmts = ici_isys_pfmts_packed; + csi2->as[i].line_header_length = + INTEL_IPU4_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; + csi2->as[i].line_footer_length = + INTEL_IPU4_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; + init_completion(&csi2->eof_completion); + + rval = ici_isys_stream_init(&csi2->as[i], &csi2->asd[i], + &csi2->asd[i].node, CSI2_ICI_PAD_SOURCE, + ICI_PAD_FLAGS_SINK); + if (rval) { + dev_err(&isys->adev->dev, "can't init stream node\n"); + goto fail; + } + } + init_completion(&csi2->eof_completion); + + return 0; + +fail: + ici_isys_csi2_cleanup(csi2); + + return rval; +} +EXPORT_SYMBOL(ici_isys_csi2_init); + +void ici_isys_csi2_cleanup(struct ici_isys_csi2 *csi2) +{ + ici_isys_subdev_cleanup(&csi2->asd[0]); + ici_isys_stream_cleanup(&csi2->as[0]); +} +EXPORT_SYMBOL(ici_isys_csi2_cleanup); + +#endif /* ICI_ENABLED */ diff --git a/drivers/media/pci/intel/ici/ici-isys-csi2.h b/drivers/media/pci/intel/ici/ici-isys-csi2.h new file mode 100644 index 000000000000..504e413af8ea --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-csi2.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_CSI2_H +#define ICI_ISYS_CSI2_H + +#include "ici-isys-frame-buf.h" +#include "ici-isys-subdev.h" +#include "ici-isys-stream.h" + +struct ici_isys_csi2_pdata; + +#define CSI2_ICI_PAD_SINK 0 +#define CSI2_ICI_PAD_SOURCE 1 +#define NR_OF_CSI2_ICI_PADS 2 +#define NR_OF_CSI2_ICI_VC 4 + +#define ICI_ISYS_SHORT_PACKET_BUFFER_NUM 32 +#define ICI_ISYS_SHORT_PACKET_WIDTH 32 +#define ICI_ISYS_SHORT_PACKET_FRAME_PACKETS 2 +#define ICI_ISYS_SHORT_PACKET_EXTRA_PACKETS 64 +#define ICI_ISYS_SHORT_PACKET_UNITSIZE 8 +#define ICI_ISYS_SHORT_PACKET_GENERAL_DT 0 +#define ICI_ISYS_SHORT_PACKET_PT 0 +#define ICI_ISYS_SHORT_PACKET_FT 0 +#define ICI_ISYS_SHORT_PACKET_DTYPE_MASK 0x3f +#define ICI_ISYS_SHORT_PACKET_STRIDE \ + (ICI_ISYS_SHORT_PACKET_WIDTH * \ + ICI_ISYS_SHORT_PACKET_UNITSIZE) +#define ICI_ISYS_SHORT_PACKET_NUM(num_lines) \ + ((num_lines) * 2 + ICI_ISYS_SHORT_PACKET_FRAME_PACKETS + \ + ICI_ISYS_SHORT_PACKET_EXTRA_PACKETS) +#define ICI_ISYS_SHORT_PACKET_PKT_LINES(num_lines) \ + DIV_ROUND_UP(ICI_ISYS_SHORT_PACKET_NUM(num_lines) * \ + ICI_ISYS_SHORT_PACKET_UNITSIZE, \ + ICI_ISYS_SHORT_PACKET_STRIDE) +#define ICI_ISYS_SHORT_PACKET_BUF_SIZE(num_lines) \ + (ICI_ISYS_SHORT_PACKET_WIDTH * \ + ICI_ISYS_SHORT_PACKET_PKT_LINES(num_lines) * \ + ICI_ISYS_SHORT_PACKET_UNITSIZE) +#define IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER 256 +#define IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE 16 +#define IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE \ + (IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER * \ + IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE) +#define IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT 100 +#define IPU_ISYS_SHORT_PACKET_FROM_RECEIVER 0 +#define IPU_ISYS_SHORT_PACKET_FROM_TUNIT 1 + +#define ICI_EOF_TIMEOUT 1000 +#define ICI_EOF_TIMEOUT_JIFFIES msecs_to_jiffies(ICI_EOF_TIMEOUT) + +#define IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT 100 +#define IPU_ISYS_SHORT_PACKET_TRACE_EVENT_MASK 0x2082 +#define IPU_SKEW_CAL_LIMIT_HZ (1500000000ul / 2) + +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0 +#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8 + +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0 +#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85 +#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2 + +/* + * struct ici_isys_csi2 + * + */ +struct ici_isys_csi2 { + struct ici_isys_csi2_pdata *pdata; + struct ici_isys *isys; + struct ici_isys_subdev asd[NR_OF_CSI2_ICI_VC]; + struct ici_isys_stream as[NR_OF_CSI2_ICI_VC]; + void *ext_sd; + + void __iomem *base; + u32 receiver_errors; + unsigned int nlanes; + unsigned int index; + atomic_t sof_sequence; + + bool wait_for_sync; + bool in_frame; + struct completion eof_completion; +}; + +struct ici_isys_csi2_timing { + uint32_t ctermen; + uint32_t csettle; + uint32_t dtermen; + uint32_t dsettle; +}; + +/* + * This structure defines the MIPI packet header output + * from IPU4 MIPI receiver. Due to hardware conversion, + * this structure is not the same as defined in CSI-2 spec. + */ +__packed struct ici_isys_mipi_packet_header { + uint32_t word_count : 16, + dtype : 13, + sync : 2, + stype : 1; + uint32_t sid : 4, + port_id : 4, + reserved : 23, + odd_even : 1; +}; + +/* + * This structure defines the trace message content + * for CSI2 receiver monitor messages. + */ +__packed struct ici_isys_csi2_monitor_message { + uint64_t fe : 1, + fs : 1, + pe : 1, + ps : 1, + le : 1, + ls : 1, + reserved1 : 2, + sequence : 2, + reserved2 : 2, + flash_shutter : 4, + error_cause : 12, + fifo_overrun : 1, + crc_error : 2, + reserved3 : 1, + timestamp_l : 16, + port : 4, + vc : 2, + reserved4 : 2, + frame_sync : 4, + reserved5 : 4; + uint64_t reserved6 : 3, + cmd : 2, + reserved7 : 1, + monitor_id : 7, + reserved8 : 1, + timestamp_h : 50; +}; + +int ici_isys_csi2_init(struct ici_isys_csi2 *csi2, + struct ici_isys *isys, + void __iomem *base, unsigned int index); +void ici_isys_csi2_cleanup(struct ici_isys_csi2 *csi2); +void ici_isys_csi2_wait_last_eof(struct ici_isys_csi2 *csi2); +void ici_isys_csi2_isr(struct ici_isys_csi2 *csi2); +unsigned int ici_isys_csi2_get_current_field( + struct device* dev, struct ici_isys_mipi_packet_header *ph); + +#endif /* ICI_ISYS_CSI2_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-frame-buf.c b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c new file mode 100644 index 000000000000..8e62c273b35a --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED + +#include "isysapi/interface/ia_css_isysapi_types.h" +#include "isysapi/interface/ia_css_isysapi.h" +#include "./ici/ici-isys-frame-buf.h" + +#define get_frame_entry_to_buf_wrap(get_entry) \ + container_of(get_entry, struct ici_frame_buf_wrapper,\ + get_frame_entry) + +#define put_frame_entry_to_buf_wrap(put_entry) \ + container_of(put_entry, struct ici_frame_buf_wrapper,\ + put_frame_entry) + +static struct ici_frame_buf_wrapper +*ici_frame_buf_lookup(struct ici_isys_frame_buf_list + *buf_list, + struct ici_frame_info + *user_frame_info) +{ + struct ici_frame_buf_wrapper *buf; + int i; + int mem_type = user_frame_info->mem_type; + + list_for_each_entry(buf, &buf_list->getbuf_list, node) { + for (i = 0; i < user_frame_info->num_planes; i++) { + struct ici_frame_plane *new_plane = + &user_frame_info->frame_planes[i]; + struct ici_frame_plane *cur_plane = + &buf->frame_info.frame_planes[i]; + if (buf->state != ICI_BUF_PREPARED && + buf->state != ICI_BUF_DONE){ + continue; + } + + switch (mem_type) { + case ICI_MEM_USERPTR: + if (new_plane->mem.userptr == + cur_plane->mem.userptr) + return buf; + break; + case ICI_MEM_DMABUF: + if (new_plane->mem.dmafd == + cur_plane->mem.dmafd) + return buf; + break; + } + //TODO: add multiplaner checks + } + + } + return NULL; +} + +static void ici_put_userpages(struct device *dev, + struct ici_kframe_plane + *kframe_plane) +{ + struct sg_table *sgt = kframe_plane->sgt; + struct scatterlist *sgl; + unsigned int i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + DEFINE_DMA_ATTRS(attrs); +#else + unsigned long attrs; +#endif + + struct mm_struct* mm = current->active_mm; + if (!mm){ + dev_err(dev, "Failed to get active mm_struct ptr from current process.\n"); + return; + } + + down_read(&mm->mmap_sem); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + dma_unmap_sg_attrs(kframe_plane->dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, &attrs); +#else + attrs = DMA_ATTR_SKIP_CPU_SYNC; + dma_unmap_sg_attrs(kframe_plane->dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, attrs); +#endif + + for_each_sg(sgt->sgl, sgl, sgt->orig_nents, i) { + struct page *page = sg_page(sgl); + + unsigned int npages = PAGE_ALIGN(sgl->offset + sgl->length) + >> PAGE_SHIFT; + unsigned int page_no; + + for (page_no = 0; page_no < npages; ++page_no, ++page) { + set_page_dirty_lock(page); + put_page(page); + } + } + + kfree(sgt); + kframe_plane->sgt = NULL; + + up_read(&mm->mmap_sem); +} + +static void ici_put_dma(struct device *dev, + struct ici_kframe_plane + *kframe_plane) +{ + struct sg_table *sgt = kframe_plane->sgt; + + if (WARN_ON(!kframe_plane->db_attach)) { + pr_err("trying to unpin a not attached buffer\n"); + return; + } + + if (WARN_ON(!sgt)) { + pr_err("dmabuf buffer is already unpinned\n"); + return; + } + + if (kframe_plane->kaddr) { + dma_buf_vunmap(kframe_plane->db_attach->dmabuf, + kframe_plane->kaddr); + kframe_plane->kaddr = NULL; + } + dma_buf_unmap_attachment(kframe_plane->db_attach, sgt, + DMA_BIDIRECTIONAL); + + kframe_plane->dma_addr = 0; + kframe_plane->sgt = NULL; + +} + +static int ici_map_dma(struct device *dev, + struct ici_frame_plane + *frame_plane, + struct ici_kframe_plane + *kframe_plane) +{ + + int ret = 0; + int fd = frame_plane->mem.dmafd; + + kframe_plane->dbdbuf = dma_buf_get(fd); + if (!kframe_plane->dbdbuf) { + ret = -EINVAL; + goto error; + } + + if (frame_plane->length == 0) + kframe_plane->length = kframe_plane->dbdbuf->size; + else + kframe_plane->length = frame_plane->length; + + kframe_plane->fd = fd; + kframe_plane->db_attach = dma_buf_attach(kframe_plane->dbdbuf, dev); + + if (IS_ERR(kframe_plane->db_attach)) { + ret = PTR_ERR(kframe_plane->db_attach); + goto error_put; + } + + kframe_plane->sgt = dma_buf_map_attachment(kframe_plane->db_attach, + DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(kframe_plane->sgt)) { + ret = -EINVAL; + kframe_plane->sgt = NULL; + dev_dbg(dev, "map attachment failed\n"); + goto error_detach; + } + + kframe_plane->dma_addr = sg_dma_address(kframe_plane->sgt->sgl); + kframe_plane->kaddr = dma_buf_vmap(kframe_plane->dbdbuf); + + if (!kframe_plane->kaddr) { + ret = -EINVAL; + goto error_detach; + } + + dev_dbg(dev, "MAPBUF: mapped fd %d\n", fd); + + return 0; + +error_detach: + dma_buf_detach(kframe_plane->dbdbuf, kframe_plane->db_attach); +error_put: + dma_buf_put(kframe_plane->dbdbuf); +error: + return ret; +} + +static int ici_get_userpages(struct device *dev, + struct ici_frame_plane + *frame_plane, + struct ici_kframe_plane + *kframe_plane) +{ + unsigned long start, end, addr; + int npages, array_size; + struct page **pages; + int nr = 0; + int ret = 0; + struct sg_table *sgt; + unsigned int i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + DEFINE_DMA_ATTRS(attrs); +#else + unsigned long attrs; +#endif + + addr = (unsigned long)frame_plane->mem.userptr; + start = addr & PAGE_MASK; + end = PAGE_ALIGN(addr + frame_plane->length); + npages = (end - start) >> PAGE_SHIFT; + array_size = npages * sizeof(struct page *); + + if (!npages) + return -EINVAL; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, GFP_KERNEL); + else + pages = vzalloc(array_size); + + if (!pages) { + kfree(sgt); + return -ENOMEM; + } + + down_read(¤t->mm->mmap_sem); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + nr = get_user_pages( + current, current->mm, + start, npages, 1, 0, pages, NULL); +#else + nr = get_user_pages(start, npages, FOLL_WRITE, pages, NULL); +#endif + if (nr < npages) + goto error_free_pages; + + ret = sg_alloc_table_from_pages(sgt, pages, npages, + addr & ~PAGE_MASK, frame_plane->length, + GFP_KERNEL); + if (ret) { + dev_err(dev, "Failed to init sgt\n"); + goto error_free_pages; + } + + + kframe_plane->dev = dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, &attrs); +#else + attrs = DMA_ATTR_SKIP_CPU_SYNC; + sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, attrs); +#endif + + if (sgt->nents <= 0) { + dev_err(dev, "Failed to init dma_map\n"); + ret = -EIO; + goto error_dma_map; + } + kframe_plane->dma_addr = sg_dma_address(sgt->sgl); + kframe_plane->sgt = sgt; + +error_free_page_list: + if (pages) { + if (array_size <= PAGE_SIZE) + kfree(pages); + else + vfree(pages); + } + up_read(¤t->mm->mmap_sem); + return ret; + +error_dma_map: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_unmap_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, &attrs); +#else + dma_unmap_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, attrs); +#endif + +error_free_pages: + if (pages) { + for (i = 0; i < nr; i++) + put_page(pages[i]); + } + kfree(sgt); + goto error_free_page_list; +} + +static int ici_get_userpages_virt(struct device *dev, + struct ici_frame_plane + *frame_plane, + struct ici_kframe_plane + *kframe_plane, + struct page **pages) +{ + unsigned long addr; + int npages; + int ret = 0; + struct sg_table *sgt; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + DEFINE_DMA_ATTRS(attrs); +#else + unsigned long attrs; +#endif + + addr = (unsigned long)frame_plane->mem.userptr; + npages = kframe_plane->npages; + + if (!npages) + return -EINVAL; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + + ret = sg_alloc_table_from_pages(sgt, pages, npages, + addr & ~PAGE_MASK, frame_plane->length, + GFP_KERNEL); + if (ret) { + dev_err(dev, "Failed to init sgt\n"); + goto error_free_pages; + } + + + kframe_plane->dev = dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, &attrs); +#else + attrs = DMA_ATTR_SKIP_CPU_SYNC; + sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, attrs); +#endif + + if (sgt->nents <= 0) { + dev_err(dev, "Failed to init dma_map\n"); + ret = -EIO; + goto error_dma_map; + } + kframe_plane->dma_addr = sg_dma_address(sgt->sgl); + kframe_plane->sgt = sgt; + +error_free_page_list: + return ret; + +error_dma_map: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_unmap_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, &attrs); +#else + dma_unmap_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_FROM_DEVICE, attrs); +#endif + +error_free_pages: + kfree(sgt); + goto error_free_page_list; +} + +int ici_isys_get_buf(struct ici_isys_stream *as, + struct ici_frame_info *frame_info) +{ + int res; + unsigned i; + struct ici_frame_buf_wrapper *buf; + + struct ici_kframe_plane *kframe_plane; + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + int mem_type = frame_info->mem_type; + + if (mem_type != ICI_MEM_USERPTR && + mem_type != ICI_MEM_DMABUF) { + dev_err(&as->isys->adev->dev, "Memory type not supproted\n"); + return -EINVAL; + } + + if (!frame_info->frame_planes[0].length) { + dev_err(&as->isys->adev->dev, "User length not set\n"); + return -EINVAL; + } + buf = ici_frame_buf_lookup(buf_list, frame_info); + + if (buf) { + buf->state = ICI_BUF_PREPARED; + return 0; + } + + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf->buf_id = frame_info->frame_buf_id; + buf->buf_list = buf_list; + memcpy(&buf->frame_info, frame_info, sizeof(buf->frame_info)); + + switch (mem_type) { + case ICI_MEM_USERPTR: + if (!frame_info->frame_planes[0].mem.userptr) { + dev_err(&as->isys->adev->dev, + "User pointer not define\n"); + res = -EINVAL; + goto err_exit; + } + for (i = 0; i < frame_info->num_planes; i++) { + kframe_plane = &buf->kframe_info.planes[i]; + kframe_plane->mem_type = + ICI_MEM_USERPTR; + res = + ici_get_userpages( + &as->isys->adev->dev, + &frame_info-> + frame_planes[i], + kframe_plane); + if (res) + goto err_exit; + } + break; + case ICI_MEM_DMABUF: + for (i = 0; i < frame_info->num_planes; i++) { + kframe_plane = &buf->kframe_info.planes[i]; + kframe_plane->mem_type = + ICI_MEM_DMABUF; + res = ici_map_dma( + &as->isys->adev->dev, + &frame_info-> + frame_planes[i], + kframe_plane); + if (res) + goto err_exit; + } + break; + } + + mutex_lock(&buf_list->mutex); + buf->state = ICI_BUF_PREPARED; + list_add_tail(&buf->node, &buf_list->getbuf_list); + mutex_unlock(&buf_list->mutex); + return 0; + +err_exit: + kfree(buf); + return res; +} + +int ici_isys_get_buf_virt(struct ici_isys_stream *as, + struct ici_frame_buf_wrapper *frame_buf, + struct page **pages) +{ + int res; + unsigned i; + struct ici_frame_buf_wrapper *buf; + + struct ici_kframe_plane *kframe_plane; + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + int mem_type = frame_buf->frame_info.mem_type; + + if (mem_type != ICI_MEM_USERPTR && + mem_type != ICI_MEM_DMABUF) { + dev_err(&as->isys->adev->dev, "Memory type not supproted\n"); + return -EINVAL; + } + + if (!frame_buf->frame_info.frame_planes[0].length) { + dev_err(&as->isys->adev->dev, "User length not set\n"); + return -EINVAL; + } + buf = ici_frame_buf_lookup(buf_list, &frame_buf->frame_info); + + if (buf) { + buf->state = ICI_BUF_PREPARED; + return 0; + } + + + buf = frame_buf; + + buf->buf_list = buf_list; + + switch (mem_type) { + case ICI_MEM_USERPTR: + if (!frame_buf->frame_info.frame_planes[0].mem.userptr) { + dev_err(&as->isys->adev->dev, + "User pointer not define\n"); + return -EINVAL; + } + for (i = 0; i < frame_buf->frame_info.num_planes; i++) { + kframe_plane = &buf->kframe_info.planes[i]; + kframe_plane->mem_type = + ICI_MEM_USERPTR; + res = + ici_get_userpages_virt( + &as->isys->adev->dev, + &frame_buf->frame_info.frame_planes[i], + kframe_plane, + pages); + if (res) + return res; + } + break; + case ICI_MEM_DMABUF: + break; + } + + mutex_lock(&buf_list->mutex); + buf->state = ICI_BUF_PREPARED; + list_add_tail(&buf->node, &buf_list->getbuf_list); + mutex_unlock(&buf_list->mutex); + return 0; +} + +int ici_isys_put_buf(struct ici_isys_stream *as, + struct ici_frame_info *frame_info, + unsigned int f_flags) +{ + struct ici_frame_buf_wrapper *buf; + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + unsigned long flags = 0; + int rval; + + spin_lock_irqsave(&buf_list->lock, flags); + if (list_empty(&buf_list->putbuf_list)) { + /* Wait */ + if (!(f_flags & O_NONBLOCK)) { + spin_unlock_irqrestore(&buf_list->lock, flags); + rval = wait_event_interruptible(buf_list->wait, + !list_empty(&buf_list-> + putbuf_list)); + spin_lock_irqsave(&buf_list->lock, flags); + if (rval == -ERESTARTSYS) + return rval; + } + } + + if (list_empty(&buf_list->putbuf_list)) { + spin_unlock_irqrestore(&buf_list->lock, flags); + return -ENODATA; + } + + buf = list_entry(buf_list->putbuf_list.next, + struct ici_frame_buf_wrapper, node); + list_del(&buf->node); + spin_unlock_irqrestore(&buf_list->lock, flags); + + mutex_lock(&buf_list->mutex); + buf->state = ICI_BUF_DONE; + list_add_tail(&buf->node, + &buf_list->getbuf_list); + mutex_unlock(&buf_list->mutex); + + memcpy(frame_info, &buf->frame_info, sizeof(buf->frame_info)); + return 0; +} + +static void frame_buf_done( + struct ici_isys_frame_buf_list *buf_list, + struct ici_frame_buf_wrapper *buf) +{ + unsigned long flags = 0; + spin_lock_irqsave(&buf_list->lock, flags); + buf->state = ICI_BUF_READY; + list_add_tail(&buf->node, &buf_list->putbuf_list); + spin_unlock_irqrestore(&buf_list->lock, flags); + wake_up_interruptible(&buf_list->wait); +} + +void ici_isys_frame_buf_ready(struct ici_isys_pipeline + *ip, + struct ia_css_isys_resp_info *info) +{ + struct ici_frame_buf_wrapper *buf; + struct ici_isys_stream *as = + ici_pipeline_to_stream(ip); + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + struct ici_isys *isys = as->isys; + unsigned long flags = 0; + bool found = false; + + dev_dbg(&isys->adev->dev, "buffer: received buffer %8.8x\n", + info->pin.addr); + + spin_lock_irqsave(&buf_list->lock, flags); + + list_for_each_entry_reverse(buf, &buf_list->getbuf_list, node) { + struct ici_kframe_plane* plane; + + if (buf->state != ICI_BUF_ACTIVE) + continue; + plane = &buf->kframe_info.planes[0]; + if (plane->dma_addr == info->pin.addr) { + found = true; + break; + } + } + + if (!found) { + spin_unlock_irqrestore(&buf_list->lock, flags); + dev_err(&isys->adev->dev, + "WARNING: cannot find a matching video buffer!\n"); + return; + } + + list_del(&buf->node); + spin_unlock_irqrestore(&buf_list->lock, flags); + + /* + * For interlaced buffers, the notification to user space + * is postponed to capture_done event since the field + * information is available only at that time. + */ + if (ip->interlaced) { + spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags); + list_add(&buf->node, &buf_list->interlacebuf_list); + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, + flags); + } else { + buf->frame_info.field = ICI_FIELD_NONE; + frame_buf_done(buf_list, buf); + if (as->frame_done_notify_queue) + as->frame_done_notify_queue(); + } + + dev_dbg(&isys->adev->dev, "buffer: found buffer %p\n", buf); +} + +static void unmap_buf(struct ici_frame_buf_wrapper *buf) +{ + int i; + + for (i = 0; i < buf->frame_info.num_planes; i++) { + struct ici_kframe_plane *kframe_plane = + &buf->kframe_info.planes[i]; + switch (kframe_plane->mem_type) { + case ICI_MEM_USERPTR: + ici_put_userpages(kframe_plane->dev, + kframe_plane); + break; + case ICI_MEM_DMABUF: + ici_put_dma(kframe_plane->dev, + kframe_plane); + break; + default: + dev_err(&buf->buf_list->strm_dev->dev, "not supported memory type: %d\n", + kframe_plane->mem_type); + break; + } + } +} + +void ici_isys_frame_buf_stream_cancel(struct + ici_isys_stream + *as) +{ + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + struct ici_frame_buf_wrapper *buf; + struct ici_frame_buf_wrapper *next_buf; + + list_for_each_entry_safe(buf, next_buf, &buf_list->getbuf_list, node) { + list_del(&buf->node); + unmap_buf(buf); + } + list_for_each_entry_safe(buf, next_buf, &buf_list->putbuf_list, node) { + list_del(&buf->node); + unmap_buf(buf); + } + list_for_each_entry_safe(buf, next_buf, &buf_list->interlacebuf_list, + node) { + list_del(&buf->node); + unmap_buf(buf); + } +} + +int ici_isys_frame_buf_add_next( + struct ici_isys_stream *as, + struct ia_css_isys_frame_buff_set *css_buf) +{ + struct ici_frame_buf_wrapper *buf = NULL; + struct ici_isys_frame_buf_list *buf_list = &as->buf_list; + unsigned long flags = 0; + bool found = false; + + mutex_lock(&buf_list->mutex); + + list_for_each_entry(buf, &buf_list->getbuf_list, node) { + if (buf->state == ICI_BUF_PREPARED){ + found = true; + break; + } + } + + if (!found) { + /* No more buffers available */ + goto cleanup_mutex; + } + + + buf->state = ICI_BUF_ACTIVE; + mutex_unlock(&buf_list->mutex); + + css_buf->send_irq_sof = 1; + css_buf->output_pins[buf_list->fw_output].addr = + (uint32_t)buf->kframe_info.planes[0].dma_addr; + css_buf->output_pins[buf_list->fw_output].out_buf_id = + buf->buf_id + 1; + + if (buf_list->short_packet_bufs) { + struct ici_frame_short_buf* sb; + struct ici_isys_mipi_packet_header* ph; + struct ia_css_isys_output_pin_payload *output_pin; + spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags); + if (!list_empty(&buf_list->short_packet_incoming)) { + sb = list_entry(buf_list->short_packet_incoming.next, + struct ici_frame_short_buf, node); + list_del(&sb->node); + list_add_tail(&sb->node, &buf_list->short_packet_active); + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, + flags); + + ph = (struct ici_isys_mipi_packet_header*) + sb->buffer; + ph->word_count = 0xffff; + ph->dtype = 0xff; + dma_sync_single_for_cpu(sb->dev, sb->dma_addr, sizeof(*ph), + DMA_BIDIRECTIONAL); + output_pin = &css_buf->output_pins[ + buf_list->short_packet_output_pin]; + output_pin->addr = sb->dma_addr; + output_pin->out_buf_id = sb->buf_id + 1; + } else { + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, + flags); + dev_err(&as->isys->adev->dev, + "No more short packet buffers. Driver bug?"); + WARN_ON(1); + } + } + return 0; + +cleanup_mutex: + mutex_unlock(&buf_list->mutex); + return -ENODATA; +} + +void ici_isys_frame_buf_capture_done( + struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info) +{ + if (ip->interlaced) { + struct ici_isys_stream *as = + ici_pipeline_to_stream(ip); + struct ici_isys_frame_buf_list *buf_list = + &as->buf_list; + unsigned long flags = 0; + struct ici_frame_short_buf* sb; + struct ici_frame_buf_wrapper* buf; + struct ici_frame_buf_wrapper* buf_safe; + struct list_head list; + + spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags); + if(ip->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) + if (!list_empty(&buf_list->short_packet_active)) { + sb = list_last_entry(&buf_list->short_packet_active, + struct ici_frame_short_buf, node); + list_move(&sb->node, &buf_list->short_packet_incoming); + } + + list_cut_position(&list, + &buf_list->interlacebuf_list, + buf_list->interlacebuf_list.prev); + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, + flags); + + list_for_each_entry_safe(buf, buf_safe, &list, node) { + buf->frame_info.field = ip->cur_field; + list_del(&buf->node); + frame_buf_done(buf_list, buf); + if (as->frame_done_notify_queue) + as->frame_done_notify_queue(); + } + } +} + +void ici_isys_frame_short_packet_ready( + struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info) +{ + struct ici_isys_stream *as = + ici_pipeline_to_stream(ip); + struct ici_isys_frame_buf_list *buf_list = + &as->buf_list; + unsigned long flags = 0; + struct ici_frame_short_buf* sb; + + spin_lock_irqsave(&buf_list->short_packet_queue_lock, flags); + if (list_empty(&buf_list->short_packet_active)) { + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, + flags); + dev_err(&as->isys->adev->dev, + "active short buffer queue empty\n"); + return; + } + list_for_each_entry_reverse(sb, &buf_list->short_packet_active, + node) { + if (sb->dma_addr == info->pin.addr) { + ip->cur_field = + ici_isys_csi2_get_current_field( + &as->isys->adev->dev, + (struct ici_isys_mipi_packet_header*) + sb->buffer); + break; + } + } + spin_unlock_irqrestore(&buf_list->short_packet_queue_lock, flags); +} + +void ici_isys_frame_buf_short_packet_destroy( + struct ici_isys_stream* as) +{ + struct ici_isys_frame_buf_list *buf_list = + &as->buf_list; + unsigned int i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + unsigned long attrs; + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + if (!buf_list->short_packet_bufs) + return; + + for (i = 0 ; i < ICI_ISYS_SHORT_PACKET_BUFFER_NUM ; + i++) { + if (buf_list->short_packet_bufs[i].buffer) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_free_attrs(&as->isys->adev->dev, + buf_list->short_packet_bufs[i].length, + buf_list->short_packet_bufs[i].buffer, + buf_list->short_packet_bufs[i].dma_addr, &attrs); +#else + dma_free_attrs(&as->isys->adev->dev, + buf_list->short_packet_bufs[i].length, + buf_list->short_packet_bufs[i].buffer, + buf_list->short_packet_bufs[i].dma_addr, attrs); +#endif + } + kfree(buf_list->short_packet_bufs); + buf_list->short_packet_bufs = NULL; +} + +int ici_isys_frame_buf_short_packet_setup( + struct ici_isys_stream* as, + struct ici_stream_format* source_fmt) +{ + struct ici_isys_frame_buf_list *buf_list = + &as->buf_list; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + unsigned int i; + size_t buf_size; + + buf_size = + ICI_ISYS_SHORT_PACKET_BUF_SIZE(source_fmt->ffmt.height); + buf_list->num_short_packet_lines = + ICI_ISYS_SHORT_PACKET_PKT_LINES(source_fmt->ffmt.height); + + INIT_LIST_HEAD(&buf_list->short_packet_incoming); + INIT_LIST_HEAD(&buf_list->short_packet_active); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; +#endif + + as->ip.cur_field = ICI_FIELD_TOP; + + buf_list->short_packet_bufs = kzalloc( + sizeof(struct ici_frame_short_buf) * + ICI_ISYS_SHORT_PACKET_BUFFER_NUM, GFP_KERNEL); + if (!buf_list->short_packet_bufs) + return -ENOMEM; + + for (i = 0 ; i < ICI_ISYS_SHORT_PACKET_BUFFER_NUM ; + i++) { + struct ici_frame_short_buf* sb = + &buf_list->short_packet_bufs[i]; + sb->buf_id = i; + sb->buf_list = buf_list; + sb->length = buf_size; + sb->dev = &as->isys->adev->dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + sb->buffer = dma_alloc_attrs( + sb->dev, buf_size, &sb->dma_addr, GFP_KERNEL, &attrs); +#else + sb->buffer = dma_alloc_attrs( + sb->dev, buf_size, &sb->dma_addr, GFP_KERNEL, attrs); +#endif + if (!sb->buffer) { + ici_isys_frame_buf_short_packet_destroy(as); + return -ENOMEM; + } + list_add(&sb->node, &buf_list->short_packet_incoming); + } + return 0; +} + +int ici_isys_frame_buf_init( + struct ici_isys_frame_buf_list* buf_list) +{ + buf_list->drv_priv = NULL; + mutex_init(&buf_list->mutex); + spin_lock_init(&buf_list->lock); + spin_lock_init(&buf_list->short_packet_queue_lock); + INIT_LIST_HEAD(&buf_list->getbuf_list); + INIT_LIST_HEAD(&buf_list->putbuf_list); + INIT_LIST_HEAD(&buf_list->interlacebuf_list); + init_waitqueue_head(&buf_list->wait); + return 0; +} + +#endif /* #ICI_ENABLED */ diff --git a/drivers/media/pci/intel/ici/ici-isys-frame-buf.h b/drivers/media/pci/intel/ici/ici-isys-frame-buf.h new file mode 100644 index 000000000000..771967ce5360 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-frame-buf.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_FRAME_BUF_H +#define ICI_ISYS_FRAME_BUF_H + +#include +#include +#include + +struct ici_isys_pipeline; +struct ia_css_isys_frame_buff_set; +struct ici_stream_device; +struct ici_isys_stream; +struct ia_css_isys_resp_info; + +struct ici_kframe_plane { + struct device *dev; + unsigned int mem_type; + unsigned long length; + + /* For user_ptr */ + unsigned long page_offset; + + /* Common */ + dma_addr_t dma_addr; + struct sg_table *sgt; + + /* For DMA operation */ + int fd; + struct dma_buf_attachment *db_attach; + struct dma_buf *dbdbuf; + void *kaddr; + + /* For mediator */ + int npages; + u64 page_table_ref; +}; + +struct ici_kframe_info { + struct ici_kframe_plane planes[ICI_MAX_PLANES]; + int num_planes; +}; + +typedef enum frame_buf_state_ { + ICI_BUF_NOT_SET, + ICI_BUF_PREPARED, + ICI_BUF_ACTIVE, + ICI_BUF_READY, + ICI_BUF_DONE, +} frame_buf_state; + +struct ici_frame_buf_wrapper { + struct ici_kframe_info kframe_info; + struct ici_frame_info frame_info; + struct list_head node; + struct ici_isys_frame_buf_list *buf_list; + struct list_head uos_node; + struct ici_isys_frame_buf_list *uos_buf_list; + uint32_t buf_id; + frame_buf_state state; +}; + +struct ici_frame_short_buf { + void* buffer; + dma_addr_t dma_addr; + struct device* dev; + size_t length; + struct list_head node; + struct ici_isys_frame_buf_list *buf_list; + uint32_t buf_id; +}; + +struct ici_isys_frame_buf_list { + void *drv_priv; + struct mutex mutex; + struct list_head getbuf_list; + struct list_head putbuf_list; + + struct list_head interlacebuf_list; + + uint32_t css_pin_type; + unsigned int fw_output; + spinlock_t lock; + wait_queue_head_t wait; + struct ici_stream_device *strm_dev; + spinlock_t short_packet_queue_lock; + struct list_head short_packet_incoming; + struct list_head short_packet_active; + struct ici_frame_short_buf* short_packet_bufs; + uint32_t num_short_packet_lines; + uint32_t short_packet_output_pin; +}; + +int ici_isys_get_buf(struct ici_isys_stream *as, + struct ici_frame_info + *user_frame_info); + +int ici_isys_get_buf_virt(struct ici_isys_stream *as, + struct ici_frame_buf_wrapper *frame_buf, + struct page **pages); + +int ici_isys_put_buf(struct ici_isys_stream *as, + struct ici_frame_info + *user_frame_info, unsigned int f_flags); + +int ici_isys_frame_buf_init(struct + ici_isys_frame_buf_list + *buf_list); + +void ici_isys_frame_buf_ready( + struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info); + +int ici_isys_frame_buf_add_next( + struct ici_isys_stream *as, + struct ia_css_isys_frame_buff_set *css_buf); + +void ici_isys_frame_buf_stream_cancel( + struct ici_isys_stream *as); + +int ici_isys_frame_buf_short_packet_setup( + struct ici_isys_stream* as, + struct ici_stream_format* source_fmt); + +void ici_isys_frame_buf_short_packet_destroy( + struct ici_isys_stream* as); + +void ici_isys_frame_short_packet_ready( + struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info); + +void ici_isys_frame_buf_capture_done( + struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info); + +#endif /* ICI_ISYS_FRAME_BUF_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-pipeline-device.c b/drivers/media/pci/intel/ici/ici-isys-pipeline-device.c new file mode 100644 index 000000000000..a4e4d77b2a64 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-pipeline-device.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" + +#ifdef ICI_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./ici/ici-isys-pipeline-device.h" +#include "./ici/ici-isys-pipeline.h" + +static struct class *pipeline_class; + +static struct ici_isys_node* find_node( + struct ici_isys_pipeline_device *pipe_dev, + unsigned id); + +static int pipeline_device_open(struct inode *inode, struct file *file) +{ + struct ici_isys_pipeline_device *pipe_dev = + inode_to_ici_isys_pipeline_device(inode); + int rval = 0; + + file->private_data = pipe_dev; + + get_device(&pipe_dev->dev); + + DEBUGK("pipeline_device_open\n"); + + return rval; +} + +static int pipeline_device_release(struct inode *inode, + struct file *file) +{ + struct ici_isys_pipeline_device *pipe_dev = + inode_to_ici_isys_pipeline_device(inode); + + put_device(&pipe_dev->dev); + + DEBUGK("pipeline_device_release\n"); + + return 0; +} + +static int pipeline_enum_links(struct file *file, void *fh, + struct ici_links_query *links_query) +{ + struct ici_isys_node *node; + struct node_pipe* pipe; + struct node_pad* pad; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + struct ici_link_desc* link; + + node = find_node(pipe_dev, links_query->pad.node_id); + if (!node) + return -ENODEV; + if (links_query->pad.pad_idx >= node->nr_pads) + return -EINVAL; + + pad = &node->node_pad[links_query->pad.pad_idx]; + if (pad->pad_id != links_query->pad.pad_idx) + return -EINVAL; + + links_query->links_cnt = 0; + + list_for_each_entry(pipe, &node->node_pipes, list_entry) { + if (pipe->src_pad != pad && pipe->sink_pad != pad) + continue; + link = &links_query->links[links_query->links_cnt]; + link->source.node_id = pipe->src_pad->node->node_id; + link->source.pad_idx = pipe->src_pad->pad_id; + link->source.flags = pipe->src_pad->flags; + link->sink.node_id = pipe->sink_pad->node->node_id; + link->sink.pad_idx = pipe->sink_pad->pad_id; + link->sink.flags = pipe->sink_pad->flags; + link->flags = pipe->flags; + ++links_query->links_cnt; + if (WARN_ON(links_query->links_cnt >= + ICI_MAX_LINKS)) { + dev_warn(&pipe_dev->dev, + "Too many links defined. %d\n", + links_query->links_cnt); + break; + } + } + return 0; +} + +static int pipeline_enum_nodes(struct file *file, void *fh, + struct ici_node_desc *node_desc) +{ + struct ici_isys_pipeline_device* pipeline_dev = + file->private_data; + struct ici_isys_node *node; + struct ici_pad_desc* pad_desc; + int pad; + bool found = false; + + node_desc->node_count = 0; + list_for_each_entry(node, &pipeline_dev->nodes, node_entry) { + node_desc->node_count++; + if (node_desc->node_id != node->node_id) + continue; + + /* fill out the node data */ + found = true; + memcpy(node_desc->name, node->name, + sizeof(node_desc->name)); + node_desc->nr_pads = node->nr_pads; + for (pad=0; pad < node->nr_pads; pad++) { + pad_desc = &node_desc->node_pad[pad]; + pad_desc->pad_idx = node->node_pad[pad].pad_id; + pad_desc->node_id = node->node_id; + pad_desc->flags = node->node_pad[pad].flags; + } + } + if (node_desc->node_id == -1) + return 0; + if (!found) + return -ENODEV; + return 0; +} + +static struct ici_isys_node* find_node( + struct ici_isys_pipeline_device *pipe_dev, + unsigned id) +{ + struct ici_isys_node *ici_node; + + list_for_each_entry(ici_node, &pipe_dev->nodes, node_entry) { + if (ici_node->node_id == id) + return ici_node; + } + return NULL; +} + +static int ici_pipeline_get_supported_format(struct file *file, + void *fh, + struct ici_pad_supported_format_desc *format_desc) +{ + struct ici_isys_node *node; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + + node = find_node(pipe_dev, format_desc->pad.node_id); + if (!node) + return -ENODEV; + + if (node->node_get_pad_supported_format) + return node->node_get_pad_supported_format(node, + format_desc); + return -ENODEV; +} + +static struct node_pipe* find_pipe( + struct ici_isys_node* src_node, + struct ici_link_desc *link) +{ + struct node_pipe *np; + + list_for_each_entry(np, &src_node->node_pipes, list_entry) { + if (np->src_pad->node->node_id == link->source.node_id + && np->src_pad->pad_id == link->source.pad_idx + && np->sink_pad->node->node_id == + link->sink.node_id + && np->sink_pad->pad_id == link->sink.pad_idx) + + return np; + } + + return NULL; +} + +static int ici_setup_link(struct file *file, void *fh, + struct ici_link_desc *link) +{ + int rval = 0; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + struct ici_isys_node *src_node, *sink_node; + struct node_pipe *np; + + src_node = find_node(pipe_dev, link->source.node_id); + if (!src_node) + return -ENODEV; + + sink_node = find_node(pipe_dev, link->sink.node_id); + if (!sink_node) + return -ENODEV; + + np = find_pipe(src_node, link); + + if (np) { + np->flags = link->flags; + } else { + dev_warn(&pipe_dev->dev, "Link not found\n"); + return -ENODEV; + } + + np = find_pipe(sink_node, link); + if (np) + np->flags = link->flags | ICI_LINK_FLAG_BACKLINK; + else + dev_warn(&pipe_dev->dev, "Backlink not found\n"); + + return rval; +} + +int ici_pipeline_set_ffmt(struct file *file, void *fh, + struct ici_pad_framefmt *ffmt) +{ + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + struct ici_isys_node *node; + int rval = -ENODEV; + + node = find_node(pipe_dev, ffmt->pad.node_id); + if (!node) + return -ENODEV; + + if (node->node_set_pad_ffmt) + rval = node->node_set_pad_ffmt(node, ffmt); + + return rval; +} + +int ici_pipeline_get_ffmt(struct file *file, void *fh, + struct ici_pad_framefmt *ffmt) +{ + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + struct ici_isys_node *node; + int rval = -ENODEV; + + node = find_node(pipe_dev, ffmt->pad.node_id); + if (!node) + return -ENODEV; + + if (node->node_get_pad_ffmt) + rval = node->node_get_pad_ffmt(node, ffmt); + + return rval; +} + +static int ici_pipeline_set_sel(struct file *file, void *fh, + struct ici_pad_selection *pad_sel) +{ + struct ici_isys_node *node; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + + node = find_node(pipe_dev, pad_sel->pad.node_id); + if (!node) + return -ENODEV; + + if (node->node_set_pad_sel) + return node->node_set_pad_sel(node, pad_sel); + return -ENODEV; +} + +static int ici_pipeline_get_sel(struct file *file, void *fh, + struct ici_pad_selection *pad_sel) +{ + struct ici_isys_node *node; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + + node = find_node(pipe_dev, pad_sel->pad.node_id); + if (!node) + return -ENODEV; + + if (node->node_get_pad_sel) + return node->node_get_pad_sel(node, pad_sel); + return -ENODEV; +} + +static long ici_pipeline_ioctl_common(void __user *up, + struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + union { + struct ici_node_desc node_desc; + struct ici_link_desc link; + struct ici_pad_framefmt pad_prop; + struct ici_pad_supported_format_desc + format_desc; + struct ici_links_query links_query; + struct ici_pad_selection pad_sel; + } isys_ioctl_cmd_args; + int err = 0; + struct ici_isys_pipeline_device *pipe_dev = + file->private_data; + const struct ici_pipeline_ioctl_ops *ops; + + if (_IOC_SIZE(ioctl_cmd) > sizeof(isys_ioctl_cmd_args)) + return -ENOTTY; + + if (_IOC_DIR(ioctl_cmd) & _IOC_WRITE) { + err = copy_from_user(&isys_ioctl_cmd_args, up, + _IOC_SIZE(ioctl_cmd)); + if (err) + return -EFAULT; + } + + mutex_lock(&pipe_dev->mutex); + ops = pipe_dev->pipeline_ioctl_ops; + switch(ioctl_cmd) { + case ICI_IOC_ENUM_NODES: + err = ops->pipeline_enum_nodes(file, pipe_dev, + &isys_ioctl_cmd_args.node_desc); + break; + case ICI_IOC_ENUM_LINKS: + err = ops->pipeline_enum_links(file, pipe_dev, + &isys_ioctl_cmd_args.links_query); + break; + case ICI_IOC_SETUP_PIPE: + err = ops->pipeline_setup_pipe(file, pipe_dev, + &isys_ioctl_cmd_args.link); + break; + case ICI_IOC_SET_FRAMEFMT: + err = ops->pad_set_ffmt(file, pipe_dev, + &isys_ioctl_cmd_args.pad_prop); + break; + case ICI_IOC_GET_FRAMEFMT: + err = ops->pad_get_ffmt(file, pipe_dev, + &isys_ioctl_cmd_args.pad_prop); + break; + case ICI_IOC_GET_SUPPORTED_FRAMEFMT: + err = ops->pad_get_supported_format(file, pipe_dev, + &isys_ioctl_cmd_args.format_desc); + break; + case ICI_IOC_SET_SELECTION: + err = ops->pad_set_sel(file, pipe_dev, + &isys_ioctl_cmd_args.pad_sel); + break; + case ICI_IOC_GET_SELECTION: + err = ops->pad_get_sel(file, pipe_dev, + &isys_ioctl_cmd_args.pad_sel); + break; + default: + err = -ENOTTY; + break; + } + + mutex_unlock(&pipe_dev->mutex); + if (err < 0) + return err; + + if (_IOC_DIR(ioctl_cmd) & _IOC_READ) { + err = copy_to_user(up, &isys_ioctl_cmd_args, + _IOC_SIZE(ioctl_cmd)); + if (err) + return -EFAULT; + } + + return 0; +} + +static long ici_pipeline_ioctl(struct file *file, + unsigned int ioctl_cmd, unsigned long ioctl_arg) +{ + long status = 0; + void __user *up = (void __user *)ioctl_arg; + status = ici_pipeline_ioctl_common(up, file, ioctl_cmd, + ioctl_arg); + + return status; +} + +static long ici_pipeline_ioctl32(struct file *file, + unsigned int ioctl_cmd, unsigned long ioctl_arg) +{ + long status = 0; + void __user *up = compat_ptr(ioctl_arg); + status = ici_pipeline_ioctl_common(up, file, ioctl_cmd, + ioctl_arg); + + return status; +} + +static const struct ici_pipeline_ioctl_ops pipeline_ioctls = +{ + .pipeline_setup_pipe = ici_setup_link, + .pipeline_enum_nodes = pipeline_enum_nodes, + .pipeline_enum_links = pipeline_enum_links, + .pad_set_ffmt = ici_pipeline_set_ffmt, + .pad_get_ffmt = ici_pipeline_get_ffmt, + .pad_get_supported_format = + ici_pipeline_get_supported_format, + .pad_set_sel = ici_pipeline_set_sel, + .pad_get_sel = ici_pipeline_get_sel, + +}; + +static const struct file_operations ici_isys_pipeline_fops = +{ + .owner = THIS_MODULE, + .open = pipeline_device_open, + .unlocked_ioctl = ici_pipeline_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ici_pipeline_ioctl32, +#endif + .release = pipeline_device_release, +}; + +static void pipeline_device_main_release(struct device *sd) +{ +} + +int pipeline_device_register( + struct ici_isys_pipeline_device *pipe_dev, + struct ici_isys *isys) +{ + int rval = 0; + + pipeline_class = + class_create(THIS_MODULE, + ICI_PIPELINE_DEVICE_NAME); + if (IS_ERR(pipeline_class)) { + printk(KERN_WARNING "Failed to register device class %s\n", + ICI_PIPELINE_DEVICE_NAME); + return PTR_ERR(pipeline_class); + } + + pipe_dev->parent = &isys->adev->dev; + pipe_dev->minor = -1; + + cdev_init(&pipe_dev->cdev, &ici_isys_pipeline_fops); + pipe_dev->cdev.owner = ici_isys_pipeline_fops.owner; + + rval = cdev_add(&pipe_dev->cdev, + MKDEV(MAJOR_PIPELINE, MINOR_PIPELINE), 1); + if (rval) { + printk(KERN_ERR "%s: failed to add cdevice\n", __func__); + goto fail; + } + + pipe_dev->dev.class = pipeline_class; + pipe_dev->dev.devt = MKDEV(MAJOR_PIPELINE, MINOR_PIPELINE); + pipe_dev->dev.parent = pipe_dev->parent; + pipe_dev->dev.release = pipeline_device_main_release; + dev_set_name(&pipe_dev->dev, "%s", + ICI_PIPELINE_DEVICE_NAME); + rval = device_register(&pipe_dev->dev); + if (rval < 0) { + printk(KERN_ERR "%s: device_register failed\n", __func__); + goto out_cdev_del; + } + + strlcpy(pipe_dev->name, pipe_dev->dev.kobj.name, + sizeof(pipe_dev->name)); + pipe_dev->minor = MINOR_PIPELINE; + + DEBUGK("Device registered: %s\n", pipe_dev->name); + pipe_dev->pipeline_ioctl_ops = &pipeline_ioctls; + mutex_init(&pipe_dev->mutex); + INIT_LIST_HEAD(&pipe_dev->nodes); + + return 0; + +out_cdev_del: + cdev_del(&pipe_dev->cdev); + +fail: + return rval; +} +EXPORT_SYMBOL(pipeline_device_register); + +void pipeline_device_unregister( + struct ici_isys_pipeline_device* pipe_dev) +{ + DEBUGK("Pipeline device unregistering..."); + device_unregister(&pipe_dev->dev); + cdev_del(&pipe_dev->cdev); + class_destroy(pipeline_class); + mutex_destroy(&pipe_dev->mutex); +} +EXPORT_SYMBOL(pipeline_device_unregister); + + +#endif /*ICI_ENABLED*/ diff --git a/drivers/media/pci/intel/ici/ici-isys-pipeline-device.h b/drivers/media/pci/intel/ici/ici-isys-pipeline-device.h new file mode 100644 index 000000000000..b218b4d1f10a --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-pipeline-device.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_PIPELINE_DEVICE_H +#define ICI_ISYS_PIPELINE_DEVICE_H + +#include +#include +#include +#include +#include + +struct ici_pipeline_ioctl_ops; +struct ici_link_desc; +struct ici_pad_supported_format_desc; + +struct ici_isys_pipeline_device { + struct cdev cdev; + struct device dev; + struct device *parent; + int minor; + char name[32]; + struct mutex mutex; + const struct file_operations *fops; + struct list_head nodes; + const struct ici_pipeline_ioctl_ops *pipeline_ioctl_ops; + unsigned next_node_id; +}; + +/* Pipeline IOCTLs */ +struct ici_pipeline_ioctl_ops { + int (*pipeline_enum_nodes)(struct file *file, void *fh, + struct ici_node_desc *node_desc); + int (*pipeline_enum_links)(struct file *file, void *fh, + struct ici_links_query *links_query); + int (*pipeline_setup_pipe)(struct file *file, void *fh, + struct ici_link_desc *link); + int (*pad_set_ffmt)(struct file *file, void *fh, + struct ici_pad_framefmt* pad_ffmt); + int (*pad_get_ffmt)(struct file *file, void *fh, + struct ici_pad_framefmt* pad_ffmt); + int (*pad_get_supported_format)(struct file *file, void *fh, + struct ici_pad_supported_format_desc *format_desc); + int (*pad_set_sel)(struct file *file, void *fh, + struct ici_pad_selection* pad_sel); + int (*pad_get_sel)(struct file *file, void *fh, + struct ici_pad_selection* pad_sel); +}; + +int pipeline_device_register( + struct ici_isys_pipeline_device *pipe_dev, + struct ici_isys *isys); +void pipeline_device_unregister(struct ici_isys_pipeline_device + *pipe_dev); + +#define inode_to_ici_isys_pipeline_device(inode) \ + container_of((inode)->i_cdev,\ + struct ici_isys_pipeline_device, cdev) + +#endif /*ICI_ISYS_PIPELINE_DEVICE_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-pipeline.c b/drivers/media/pci/intel/ici/ici-isys-pipeline.c new file mode 100644 index 000000000000..563a76d0c415 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-pipeline.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" + +#ifdef ICI_ENABLED + +#include "./ici/ici-isys-pipeline.h" + +int ici_isys_pipeline_node_init( + struct ici_isys *isys, + struct ici_isys_node *node, + const char* name, + unsigned num_pads, + struct node_pad *node_pads) +{ + unsigned int pad_id; + + mutex_lock(&isys->pipeline_dev.mutex); + node->parent = &isys->pipeline_dev; + snprintf(node->name, sizeof(node->name), "%s", name); + if (num_pads > ICI_MAX_PADS) { + dev_warn(&isys->adev->dev, + "Too many external pads %d\n", num_pads); + num_pads = ICI_MAX_PADS; + } + node->nr_pads = num_pads; + node->node_pad = node_pads; + node->nr_pipes = 0; + node->node_id = isys->pipeline_dev.next_node_id++; + + INIT_LIST_HEAD(&node->node_entry); + INIT_LIST_HEAD(&node->iterate_node); + INIT_LIST_HEAD(&node->node_pipes); + + for (pad_id = 0; pad_id < num_pads; pad_id++) { + node->node_pad[pad_id].node = node; + node->node_pad[pad_id].pad_id = pad_id; + } + + list_add_tail(&node->node_entry, + &node->parent->nodes); + dev_info(&isys->adev->dev, + "Setup node \"%s\" with %d pads\n", + node->name, + node->nr_pads); + mutex_unlock(&isys->pipeline_dev.mutex); + return 0; +} + +void node_pads_cleanup(struct ici_isys_node *node) +{ + struct node_pipe *tmp, *q, *np; + list_for_each_entry_safe(np, q, &node->node_pipes, list_entry) { + tmp = np; + list_del(&np->list_entry); + kfree(tmp); + } +} + +static struct node_pipe* node_pad_add_link(struct ici_isys_node *node) +{ + struct node_pipe *np; + np = kzalloc(sizeof(*np), GFP_KERNEL); + if (!np) + return NULL; + + list_add_tail(&np->list_entry, &node->node_pipes); + node->nr_pipes++; + return np; +} + +int node_pad_create_link(struct ici_isys_node *src, + u16 src_pad, struct ici_isys_node *sink, + u16 sink_pad, u32 flags ) +{ + int rval = 0; + struct node_pipe *np; + struct node_pipe *rnp; + if(!src || !sink || !src->parent) + return -EINVAL; + + mutex_lock(&src->parent->mutex); + np = node_pad_add_link(src); + if(!np) { + rval = -ENOMEM; + goto cleanup_mutex; + } + + np->src_pad = &src->node_pad[src_pad]; + np->sink_pad = &sink->node_pad[sink_pad]; + np->flags = flags; + np->rev_pipe = NULL; + + rnp = node_pad_add_link(sink); + if(!rnp) { + rval = -ENOMEM; + goto cleanup_mutex; + } + + rnp->src_pad = &src->node_pad[src_pad]; + rnp->sink_pad = &sink->node_pad[sink_pad]; + rnp->flags = flags | ICI_LINK_FLAG_BACKLINK; + rnp->rev_pipe = np; + np->rev_pipe = rnp; + +cleanup_mutex: + mutex_unlock(&src->parent->mutex); + return rval; +} + +static int __ici_isys_pipeline_for_each_node( + ici_isys_pipeline_node_cb_fn cb_fn, + void* cb_data, + struct ici_isys_node* start_node, + struct ici_isys_pipeline *ip_active, + bool backwards) +{ + struct node_pipe *pipe; + struct ici_isys_node* node; + struct ici_isys_node* next_node = NULL; + int rval; + LIST_HEAD(node_list); + + if (!cb_fn || !start_node || !start_node->parent) + return -EINVAL; + + rval = cb_fn(cb_data, start_node, NULL); + if (rval) + return rval; + list_add_tail(&start_node->iterate_node, &node_list); + while (!list_empty(&node_list)) { + node = list_entry(node_list.next, + struct ici_isys_node, + iterate_node); + list_del(&node->iterate_node); + list_for_each_entry(pipe, &node->node_pipes, + list_entry) { + if (backwards && !(pipe->flags & ICI_LINK_FLAG_BACKLINK)) + continue; + else if (!backwards && (pipe->flags & ICI_LINK_FLAG_BACKLINK)) + continue; + if (ip_active && !(pipe->flags & ICI_LINK_FLAG_ENABLED)) + continue; + next_node = (backwards ? pipe->src_pad->node : + pipe->sink_pad->node); + rval = cb_fn(cb_data, next_node, pipe); + if (rval) + return rval; + list_add_tail(&next_node->iterate_node, + &node_list); + } + } + return 0; +} + +int ici_isys_pipeline_for_each_node( + ici_isys_pipeline_node_cb_fn cb_fn, + void* cb_data, + struct ici_isys_node* start_node, + struct ici_isys_pipeline *ip_active, + bool backwards) +{ + int rval = 0; + mutex_lock(&start_node->parent->mutex); + rval = __ici_isys_pipeline_for_each_node(cb_fn, + cb_data, start_node, ip_active, backwards); + mutex_unlock(&start_node->parent->mutex); + return rval; +} + +#endif /* ICI_ENABLED */ diff --git a/drivers/media/pci/intel/ici/ici-isys-pipeline.h b/drivers/media/pci/intel/ici/ici-isys-pipeline.h new file mode 100644 index 000000000000..8004d8df0c06 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-pipeline.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_PIPELINE_H +#define ICI_ISYS_PIPELINE_H + +#include +#include +#include + +#include + +#define ICI_ISYS_OUTPUT_PINS 11 +#define ICI_NUM_CAPTURE_DONE 2 +#define ICI_ISYS_MAX_PARALLEL_SOF 2 + +struct ici_isys_node; +struct ici_isys_subdev; +struct ici_isys_csi2_be; +struct ici_isys_csi2; +struct ici_isys_tpg; +struct ia_css_isys_resp_info; +struct ici_isys_pipeline; +struct ici_isys_stream; +struct node_pad; + +struct ici_sequence_info { + unsigned int sequence; + u64 timestamp; +}; + +struct ici_output_pin_data { + void (*pin_ready)(struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *info); + struct ici_isys_frame_buf_list *buf_list; +}; + +struct ici_isys_pipeline { + struct node_pipeline pipe; + struct ici_isys_pipeline_device *pipeline_dev; + int source; /* SSI stream source */ + int stream_handle; /* stream handle for CSS API */ + unsigned int nr_output_pins; /* How many firmware pins? */ + struct ici_isys_csi2_be *csi2_be; + struct ici_isys_csi2 *csi2; + struct ici_isys_subdev *asd_source; + int asd_source_pad_id; + unsigned int streaming; + struct completion stream_open_completion; + struct completion stream_close_completion; + struct completion stream_start_completion; + struct completion stream_stop_completion; + struct completion capture_ack_completion; + struct ici_isys *isys; + + void (*capture_done[ICI_NUM_CAPTURE_DONE]) + (struct ici_isys_pipeline *ip, + struct ia_css_isys_resp_info *resp); + struct ici_output_pin_data + output_pins[ICI_ISYS_OUTPUT_PINS]; + bool interlaced; + int error; + int cur_field; + unsigned int short_packet_source; + unsigned int short_packet_trace_index; + unsigned int vc; +}; + +int ici_isys_pipeline_node_init( + struct ici_isys *isys, + struct ici_isys_node *node, + const char* name, + unsigned num_pads, + struct node_pad *node_pads); + +int node_pad_create_link(struct ici_isys_node *src, + u16 src_pad, struct ici_isys_node *sink, + u16 sink_pad, u32 flags ); + +void node_pads_cleanup(struct ici_isys_node *node); + +typedef int (*ici_isys_pipeline_node_cb_fn)(void* cb_data, + struct ici_isys_node* node, + struct node_pipe* pipe); + +int ici_isys_pipeline_for_each_node( + ici_isys_pipeline_node_cb_fn cb_fn, + void* cb_data, + struct ici_isys_node* start_node, + struct ici_isys_pipeline* ip_active, + bool backwards); + +#define ici_nodepipe_to_pipeline(__np) \ + container_of(__np, struct ici_isys_pipeline, pipe) + +#endif /* ICI_ISYS_PIPELINE_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-stream-device.c b/drivers/media/pci/intel/ici/ici-isys-stream-device.c new file mode 100644 index 000000000000..96336980db7d --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-stream-device.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./ici/ici-isys-stream-device.h" +#include "./ici/ici-isys-pipeline-device.h" + +#define MAX_STREAM_DEVICES 64 + +static dev_t ici_stream_dev_t; +static struct class* stream_class; +static int stream_devices_registered = 0; +static int stream_device_init = 0; + +static int ici_stream_init(void); +static void ici_stream_exit(void); + +static int stream_device_open(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = inode_to_intel_ipu_stream_device(inode); + int rval = 0; + + get_device(&strm_dev->dev); + + file->private_data = strm_dev; + if (strm_dev->fops->open) + rval = strm_dev->fops->open(inode, file); + + if (rval) + put_device(&strm_dev->dev); + + return rval; +} + +static int stream_device_release(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = inode_to_intel_ipu_stream_device(inode); + int rval = 0; + + if (strm_dev->fops->release) + rval = strm_dev->fops->release(inode, file); + + put_device(&strm_dev->dev); + return rval; +} + +static unsigned int ici_fop_poll(struct file *file, struct poll_table_struct *poll) +{ + struct ici_stream_device *strm_dev = file->private_data; + unsigned int rval = POLLERR | POLLHUP; + + if (strm_dev->fops->poll) + rval = strm_dev->fops->poll(file, poll); + else + return DEFAULT_POLLMASK; + + return rval; +} + +#ifdef CONFIG_COMPAT +struct timeval32 { + __u32 tv_sec; + __u32 tv_usec; +} __attribute__((__packed__)); + +struct ici_frame_plane32 { + __u32 bytes_used; + __u32 length; + union { + compat_uptr_t userptr; + __s32 dmafd; + } mem; + __u32 data_offset; + __u32 reserved[2]; +} __attribute__((__packed__)); + +struct ici_frame_info32 { + __u32 frame_type; + __u32 field; + __u32 flag; + __u32 frame_buf_id; + struct timeval32 frame_timestamp; + __u32 frame_sequence_id; + __u32 mem_type; /* _DMA or _USER_PTR */ + struct ici_frame_plane32 frame_planes[ICI_MAX_PLANES]; /* multi-planar */ + __u32 num_planes; /* =1 single-planar > 1 multi-planar array size */ + __u32 reserved[2]; +} __attribute__((__packed__)); + +#define ICI_IOC_GET_BUF32 _IOWR(MAJOR_STREAM, 3, struct ici_frame_info32) +#define ICI_IOC_PUT_BUF32 _IOWR(MAJOR_STREAM, 4, struct ici_frame_info32) + +static void copy_from_user_frame_info32(struct ici_frame_info *kp, struct ici_frame_info32 __user *up) +{ + int i; + compat_uptr_t userptr; + + get_user(kp->frame_type, &up->frame_type); + get_user(kp->field, &up->field); + get_user(kp->flag, &up->flag); + get_user(kp->frame_buf_id, &up->frame_buf_id); + get_user(kp->frame_timestamp.tv_sec, &up->frame_timestamp.tv_sec); + get_user(kp->frame_timestamp.tv_usec, &up->frame_timestamp.tv_usec); + get_user(kp->frame_sequence_id, &up->frame_sequence_id); + get_user(kp->mem_type, &up->mem_type); + get_user(kp->num_planes, &up->num_planes); + for (i=0; inum_planes; i++) { + get_user(kp->frame_planes[i].bytes_used, &up->frame_planes[i].bytes_used); + get_user(kp->frame_planes[i].length, &up->frame_planes[i].length); + if(kp->mem_type==ICI_MEM_USERPTR) { + get_user(userptr, &up->frame_planes[i].mem.userptr); + kp->frame_planes[i].mem.userptr = (unsigned long) compat_ptr(userptr); + } else if (kp->mem_type==ICI_MEM_DMABUF) { + get_user(kp->frame_planes[i].mem.dmafd, &up->frame_planes[i].mem.dmafd); + }; + get_user(kp->frame_planes[i].data_offset, &up->frame_planes[i].data_offset); + } +} + +static void copy_to_user_frame_info32(struct ici_frame_info *kp, struct ici_frame_info32 __user *up) +{ + int i; + compat_uptr_t userptr; + + put_user(kp->frame_type, &up->frame_type); + put_user(kp->field, &up->field); + put_user(kp->flag, &up->flag); + put_user(kp->frame_buf_id, &up->frame_buf_id); + put_user(kp->frame_timestamp.tv_sec, &up->frame_timestamp.tv_sec); + put_user(kp->frame_timestamp.tv_usec, &up->frame_timestamp.tv_usec); + put_user(kp->frame_sequence_id, &up->frame_sequence_id); + put_user(kp->mem_type, &up->mem_type); + put_user(kp->num_planes, &up->num_planes); + for (i=0; inum_planes; i++) { + put_user(kp->frame_planes[i].bytes_used, &up->frame_planes[i].bytes_used); + put_user(kp->frame_planes[i].length, &up->frame_planes[i].length); + if(kp->mem_type==ICI_MEM_USERPTR) { + userptr = (unsigned long)compat_ptr(kp->frame_planes[i].mem.userptr); + put_user(userptr, &up->frame_planes[i].mem.userptr); + } else if (kp->mem_type==ICI_MEM_DMABUF) { + get_user(kp->frame_planes[i].mem.dmafd, &up->frame_planes[i].mem.dmafd); + } + put_user(kp->frame_planes[i].data_offset, &up->frame_planes[i].data_offset); + } +} + +static long ici_stream_ioctl32(struct file *file, __u32 ioctl_cmd, + unsigned long ioctl_arg) { + union { + struct ici_frame_info frame_info; + struct ici_stream_format sf; + } isys_ioctl_cmd_args; + + int err = 0; + struct ici_stream_device *strm_dev = file->private_data; + void __user *up = compat_ptr(ioctl_arg); + + mutex_lock(strm_dev->mutex); + + switch(ioctl_cmd) { + case ICI_IOC_STREAM_ON: + err = strm_dev->ipu_ioctl_ops->ici_stream_on(file, strm_dev); + break; + case ICI_IOC_STREAM_OFF: + err = strm_dev->ipu_ioctl_ops->ici_stream_off(file, strm_dev); + break; + case ICI_IOC_GET_BUF32: + copy_from_user_frame_info32(&isys_ioctl_cmd_args.frame_info, up); + err = strm_dev->ipu_ioctl_ops->ici_get_buf(file, strm_dev, &isys_ioctl_cmd_args.frame_info); + if (err) + break; + copy_to_user_frame_info32(&isys_ioctl_cmd_args.frame_info, up); + break; + case ICI_IOC_PUT_BUF32: + copy_from_user_frame_info32(&isys_ioctl_cmd_args.frame_info, up); + err = strm_dev->ipu_ioctl_ops->ici_put_buf(file, strm_dev, &isys_ioctl_cmd_args.frame_info); + if (err) + break; + copy_to_user_frame_info32(&isys_ioctl_cmd_args.frame_info, up); + break; + case ICI_IOC_SET_FORMAT: + if (_IOC_SIZE(ioctl_cmd) > sizeof(isys_ioctl_cmd_args)) + return -ENOTTY; + + err = copy_from_user(&isys_ioctl_cmd_args, up, + _IOC_SIZE(ioctl_cmd)); + if (err) + return -EFAULT; + + err = strm_dev->ipu_ioctl_ops->ici_set_format(file, strm_dev, &isys_ioctl_cmd_args.sf); + if (err) + break; + + err = copy_to_user(up, &isys_ioctl_cmd_args, _IOC_SIZE(ioctl_cmd)); + if (err) { + return -EFAULT; + } + break; + default: + err = -ENOTTY; + break; + } + + mutex_unlock(strm_dev->mutex); + if (err) { + return err; + } + + return 0; +} +#endif + +static long ici_stream_ioctl(struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) { + union { + struct ici_frame_info frame_info; + struct ici_stream_format sf; + } isys_ioctl_cmd_args; + int err = 0; + struct ici_stream_device *strm_dev = file->private_data; + void __user *up = (void __user *)ioctl_arg; + + bool copy = (ioctl_cmd != ICI_IOC_STREAM_ON && + ioctl_cmd != ICI_IOC_STREAM_OFF); + + if (copy) { + if (_IOC_SIZE(ioctl_cmd) > sizeof(isys_ioctl_cmd_args)) + return -ENOTTY; + + if (_IOC_DIR(ioctl_cmd) & _IOC_WRITE) { + err = copy_from_user(&isys_ioctl_cmd_args, up, + _IOC_SIZE(ioctl_cmd)); + if (err) + return -EFAULT; + } + } + + mutex_lock(strm_dev->mutex); + + switch(ioctl_cmd) { + case ICI_IOC_STREAM_ON: + err = strm_dev->ipu_ioctl_ops->ici_stream_on(file, strm_dev); + break; + case ICI_IOC_STREAM_OFF: + err = strm_dev->ipu_ioctl_ops->ici_stream_off(file, strm_dev); + break; + case ICI_IOC_GET_BUF: + err = strm_dev->ipu_ioctl_ops->ici_get_buf(file, strm_dev, &isys_ioctl_cmd_args.frame_info); + break; + case ICI_IOC_PUT_BUF: + err = strm_dev->ipu_ioctl_ops->ici_put_buf(file, strm_dev, &isys_ioctl_cmd_args.frame_info); + break; + case ICI_IOC_SET_FORMAT: + err = strm_dev->ipu_ioctl_ops->ici_set_format(file, strm_dev, &isys_ioctl_cmd_args.sf); + break; + default: + err = -ENOTTY; + break; + } + + mutex_unlock(strm_dev->mutex); + if (err) + return err; + + if (copy && _IOC_DIR(ioctl_cmd) & _IOC_READ) { + err = copy_to_user(up, &isys_ioctl_cmd_args, _IOC_SIZE(ioctl_cmd)); + if (err) + return -EFAULT; + } + + return 0; +} + +static const struct file_operations ici_stream_fops = { + .owner = THIS_MODULE, + .open = stream_device_open, /* calls strm_dev->fops->open() */ + .unlocked_ioctl = ici_stream_ioctl, /* calls strm_dev->ipu_ioctl_ops->() */ +#ifdef CONFIG_COMPAT + .compat_ioctl = ici_stream_ioctl32, +#endif + .release = stream_device_release, /* calls strm_dev->fops->release() */ + .poll = ici_fop_poll, /* calls strm_dev->fops->poll() */ +}; + +/* Called on device_unregister */ +static void base_device_release(struct device *sd) +{ +} + +int stream_device_register(struct ici_stream_device *strm_dev) +{ + int rval = 0; + int num; + + if (!stream_device_init) { + rval = ici_stream_init(); + if (rval) { + printk(KERN_ERR "%s: failed to init stream device\n", __func__); + return rval; + } + stream_device_init++; + } + num = stream_devices_registered; + + if (!(num < MAX_STREAM_DEVICES)) { + printk(KERN_WARNING "%s: wrong minor of stream device: %d\n", + __func__, num); + return -EINVAL; + } + strm_dev->minor = -1; + + cdev_init(&strm_dev->cdev, &ici_stream_fops); + strm_dev->cdev.owner = ici_stream_fops.owner; + + rval = cdev_add(&strm_dev->cdev, MKDEV(MAJOR(ici_stream_dev_t), num), 1); + if (rval) { + printk(KERN_WARNING "%s: failed to add cdevice\n", __func__); + return rval; + } + + strm_dev->dev.class = stream_class; + strm_dev->dev.devt = MKDEV(MAJOR(ici_stream_dev_t), num); + strm_dev->dev.parent = strm_dev->dev_parent; + dev_set_name(&strm_dev->dev, "%s%d", ICI_STREAM_DEVICE_NAME, num); + rval = device_register(&strm_dev->dev); + if (rval < 0) { + printk(KERN_WARNING "%s: device_register failed\n", __func__); + cdev_del(&strm_dev->cdev); + return rval; + } + + /* Release function will be called on device unregister, + it is needed to avoid errors */ + strm_dev->dev.release = base_device_release; + strlcpy(strm_dev->name, strm_dev->dev.kobj.name, sizeof(strm_dev->name)); + strm_dev->minor = num; + + printk(KERN_INFO "Device registered: %s\n", strm_dev->name); + stream_devices_registered++; + + return 0; +} + +void stream_device_unregister(struct ici_stream_device *strm_dev) +{ + device_unregister(&strm_dev->dev); + cdev_del(&strm_dev->cdev); + + stream_devices_registered--; + if (!stream_devices_registered) { + ici_stream_exit(); + stream_device_init--; + } +} + +static int ici_stream_init(void) +{ + int rval; + ici_stream_dev_t = MKDEV(MAJOR_STREAM, 0); + + rval = register_chrdev_region(ici_stream_dev_t, + MAX_STREAM_DEVICES, ICI_STREAM_DEVICE_NAME); + if (rval) { + printk(KERN_WARNING "can't register intel_ipu_ici stream chrdev region (%d)\n", rval); + return rval; + } + + stream_class = class_create(THIS_MODULE, ICI_STREAM_DEVICE_NAME); + if (IS_ERR(stream_class)) { + unregister_chrdev_region(ici_stream_dev_t, MAX_STREAM_DEVICES); + printk(KERN_WARNING "Failed to register device class %s\n", ICI_STREAM_DEVICE_NAME); + return PTR_ERR(stream_class); + } + + return 0; +} + +static void ici_stream_exit(void) +{ + class_unregister(stream_class); + //class_destroy(stream_class); + unregister_chrdev_region(ici_stream_dev_t, MAX_STREAM_DEVICES); + + printk(KERN_INFO "intel_ipu_ici stream device unregistered\n"); +} + diff --git a/drivers/media/pci/intel/ici/ici-isys-stream-device.h b/drivers/media/pci/intel/ici/ici-isys-stream-device.h new file mode 100644 index 000000000000..5aec89450920 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-stream-device.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_STREAM_DEVICE_H +#define ICI_ISYS_STREAM_DEVICE_H + +#include +#include +#include +#include +#include + +#include "ici-isys-frame-buf.h" +#include "ici-isys-pipeline.h" +#include "virtio/intel-ipu4-virtio-common.h" + +struct ici_ioctl_ops; +struct ici_frame_plane; + +struct ici_stream_device { + struct device dev; /* intel stream base dev */ + struct cdev cdev; /* character device */ + struct device *dev_parent; /* parent device ipu_bus */ + struct mutex *mutex; + const struct file_operations *fops; /* standard Linux fops */ + struct ici_isys_frame_buf_list *frame_buf_list; /* frame buffer wrapper pointer */ + char name[32]; /* device name */ + int minor; /* driver minor */ + unsigned long flags; /* stream device state machine */ + const struct ici_ioctl_ops *ipu_ioctl_ops; + //Mediator param + int virt_dev_id; + struct ipu4_virtio_priv *virt_priv; +}; + +struct ici_ioctl_ops { + int (*ici_set_format) (struct file *file, void *fh, + struct ici_stream_format *psf); + int (*ici_stream_on) (struct file *file, void *fh); + int (*ici_stream_off) (struct file *file, void *fh); + int (*ici_get_buf) (struct file *file, void *fh, + struct ici_frame_info *fram); + int (*ici_get_buf_virt) (struct file *file, void *fh, + struct ici_frame_buf_wrapper *fram, struct page **pages); + int (*ici_put_buf) (struct file *file, void *fh, + struct ici_frame_info *fram); +}; + +#define inode_to_intel_ipu_stream_device(inode) \ + container_of((inode)->i_cdev, struct ici_stream_device, cdev) + +int stream_device_register(struct ici_stream_device *strm_dev); + +void stream_device_unregister(struct ici_stream_device *strm_dev); + +#endif /* ICI_ISYS_STREAM_DEVICE_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-stream.c b/drivers/media/pci/intel/ici/ici-isys-stream.c new file mode 100644 index 000000000000..6a2c6e1c34fd --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-stream.c @@ -0,0 +1,1450 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED + +#include +#include +#include +#include +#include +#include "isysapi/interface/ia_css_isysapi_fw_types.h" +#include "isysapi/interface/ia_css_isysapi.h" +#include +#include +#include +#include "ipu-trace.h" +#include "ipu-fw-isys.h" +#include "ici-fw-isys.h" +#include "ipu-wrapper.h" +#include "./ici/ici-isys-stream.h" +#include "./ici/ici-isys-csi2.h" +#include "./ici/ici-isys-csi2-be.h" +#include + +#ifndef IPU4_DEBUG +#define IPU4_DEBUG 1 +#endif + +#define dev_to_stream(dev) \ + container_of(dev, struct ici_isys_stream, strm_dev) + +const struct ici_isys_pixelformat ici_isys_pfmts[] = { + /* YUV vector format */ + { ICI_FORMAT_YUYV, 24, 24, ICI_FORMAT_YUYV, IA_CSS_ISYS_FRAME_FORMAT_YUV420_16 }, + /* Raw bayer vector formats. */ + { ICI_FORMAT_SBGGR12, 16, 12, ICI_FORMAT_SBGGR12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGBRG12, 16, 12, ICI_FORMAT_SGBRG12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGRBG12, 16, 12, ICI_FORMAT_SGRBG12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SRGGB12, 16, 12, ICI_FORMAT_SRGGB12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SBGGR10, 16, 10, ICI_FORMAT_SBGGR10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGBRG10, 16, 10, ICI_FORMAT_SGBRG10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGRBG10, 16, 10, ICI_FORMAT_SGRBG10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SRGGB10, 16, 10, ICI_FORMAT_SRGGB10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SBGGR8, 16, 8, ICI_FORMAT_SBGGR8, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGBRG8, 16, 8, ICI_FORMAT_SGBRG8, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGRBG8, 16, 8, ICI_FORMAT_SGRBG8, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SRGGB8, 16, 8, ICI_FORMAT_SRGGB8, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + /*{ V4L2_FMT_INTEL_IPU4_ISYS_META, 8, 8, MEDIA_BUS_FMT_FIXED, IA_CSS_ISYS_MIPI_DATA_TYPE_EMBEDDED },*/ + { } +}; + +const struct ici_isys_pixelformat ici_isys_pfmts_be_soc[] = { + { ICI_FORMAT_UYVY, 16, 16, ICI_FORMAT_UYVY, IA_CSS_ISYS_FRAME_FORMAT_UYVY }, + { ICI_FORMAT_YUYV, 16, 16, ICI_FORMAT_YUYV, IA_CSS_ISYS_FRAME_FORMAT_YUYV }, + { ICI_FORMAT_RGB565, 32, 32, ICI_FORMAT_RGB565, IA_CSS_ISYS_FRAME_FORMAT_RGBA888 }, + { ICI_FORMAT_RGB888, 32, 32, ICI_FORMAT_RGB888, IA_CSS_ISYS_FRAME_FORMAT_RGBA888 }, + /* Raw bayer formats. */ + { ICI_FORMAT_SBGGR12, 16, 12, ICI_FORMAT_SBGGR12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGBRG12, 16, 12, ICI_FORMAT_SGBRG12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGRBG12, 16, 12, ICI_FORMAT_SGRBG12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SRGGB12, 16, 12, ICI_FORMAT_SRGGB12, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SBGGR10, 16, 10, ICI_FORMAT_SBGGR10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGBRG10, 16, 10, ICI_FORMAT_SGBRG10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SGRBG10, 16, 10, ICI_FORMAT_SGRBG10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SRGGB10, 16, 10, ICI_FORMAT_SRGGB10, IA_CSS_ISYS_FRAME_FORMAT_RAW16 }, + { ICI_FORMAT_SBGGR8, 8, 8, ICI_FORMAT_SBGGR8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SGBRG8, 8, 8, ICI_FORMAT_SGBRG8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SGRBG8, 8, 8, ICI_FORMAT_SGRBG8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SRGGB8, 8, 8, ICI_FORMAT_SRGGB8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { } +}; + +const struct ici_isys_pixelformat ici_isys_pfmts_packed[] = { + { ICI_FORMAT_UYVY, 16, 16, ICI_FORMAT_UYVY, IA_CSS_ISYS_FRAME_FORMAT_UYVY }, + { ICI_FORMAT_YUYV, 16, 16, ICI_FORMAT_YUYV, IA_CSS_ISYS_FRAME_FORMAT_YUYV }, + { ICI_FORMAT_RGB565, 16, 16, ICI_FORMAT_RGB565, IA_CSS_ISYS_FRAME_FORMAT_RGB565 }, + { ICI_FORMAT_RGB888, 24, 24, ICI_FORMAT_RGB888, IA_CSS_ISYS_FRAME_FORMAT_RGBA888 }, + { ICI_FORMAT_SBGGR12, 12, 12, ICI_FORMAT_SBGGR12, IA_CSS_ISYS_FRAME_FORMAT_RAW12 }, + { ICI_FORMAT_SGBRG12, 12, 12, ICI_FORMAT_SGBRG12, IA_CSS_ISYS_FRAME_FORMAT_RAW12 }, + { ICI_FORMAT_SGRBG12, 12, 12, ICI_FORMAT_SGRBG12, IA_CSS_ISYS_FRAME_FORMAT_RAW12 }, + { ICI_FORMAT_SRGGB12, 12, 12, ICI_FORMAT_SRGGB12, IA_CSS_ISYS_FRAME_FORMAT_RAW12 }, + { ICI_FORMAT_SBGGR10, 10, 10, ICI_FORMAT_SBGGR10, IA_CSS_ISYS_FRAME_FORMAT_RAW10 }, + { ICI_FORMAT_SGBRG10, 10, 10, ICI_FORMAT_SGBRG10, IA_CSS_ISYS_FRAME_FORMAT_RAW10 }, + { ICI_FORMAT_SGRBG10, 10, 10, ICI_FORMAT_SGRBG10, IA_CSS_ISYS_FRAME_FORMAT_RAW10 }, + { ICI_FORMAT_SRGGB10, 10, 10, ICI_FORMAT_SRGGB10, IA_CSS_ISYS_FRAME_FORMAT_RAW10 }, + { ICI_FORMAT_SBGGR8, 8, 8, ICI_FORMAT_SBGGR8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SGBRG8, 8, 8, ICI_FORMAT_SGBRG8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SGRBG8, 8, 8, ICI_FORMAT_SGRBG8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { ICI_FORMAT_SRGGB8, 8, 8, ICI_FORMAT_SRGGB8, IA_CSS_ISYS_FRAME_FORMAT_RAW8 }, + { } +}; + +struct pipeline_format_data { + struct ici_isys_stream *as; + struct ici_pad_framefmt pff; +}; + +struct pipeline_power_data { + struct ici_isys_stream *as; + int power; +}; + +static int pipeline_set_node_power(void* cb_data, + struct ici_isys_node* node, + struct node_pipe* pipe) +{ + struct pipeline_power_data* pwr_data = cb_data; + struct ici_isys_stream *as = pwr_data->as; + dev_info(&as->isys->adev->dev, + "Set ext sd \"%s\" power to %d\n", + node->name, pwr_data->power); + if (node->node_set_power) { + int ret = node->node_set_power(node, pwr_data->power); + if (ret < 0) + return ret; + } + return 0; +} + +static int pipeline_set_power(struct ici_isys_stream *as, + int state) +{ + struct pipeline_power_data pwr_data = { + .as = as, + .power = state, + }; + + return ici_isys_pipeline_for_each_node( + pipeline_set_node_power, + &pwr_data, + &as->node, + &as->ip, + true); +} + +static int intel_ipu4_isys_library_close(struct ici_isys *isys) +{ + struct device *dev = &isys->adev->dev; + int timeout = IPU_ISYS_TURNOFF_TIMEOUT; + int rval; + + /* + * Ask library to stop the isys fw. Actual close takes + * some time as the FW must stop its actions including code fetch + * to SP icache. + */ + rval = ipu_lib_call(device_close, isys); + if (rval) + dev_err(dev, "Device close failure: %d\n", rval); + + /* release probably fails if the close failed. Let's try still */ + do { + usleep_range(IPU_ISYS_TURNOFF_DELAY_US, + 2 * IPU_ISYS_TURNOFF_DELAY_US); + rval = ipu_lib_call_notrace(device_release, isys, 0); + timeout--; + } while (rval != 0 && timeout); + + if (!rval) + isys->fwcom = NULL; /* No further actions needed */ + else + dev_err(dev, "Device release time out %d\n", rval); + return rval; +} + +static unsigned int get_comp_format(u32 code) +{ + unsigned int predictor = 0; /* currently hard coded */ + unsigned int udt = ici_isys_format_code_to_mipi(code); + unsigned int scheme = ici_isys_get_compression_scheme(code); + + /* if data type is not user defined return here */ + if ((udt < ICI_ISYS_MIPI_CSI2_TYPE_USER_DEF(1)) + || (udt > ICI_ISYS_MIPI_CSI2_TYPE_USER_DEF(8))) + return 0; + + /* + * For each user defined type (1..8) there is configuration bitfield for + * decompression. + * + * | bit 3 | bits 2:0 | + * | predictor | scheme | + * compression schemes: + * 000 = no compression + * 001 = 10 - 6 - 10 + * 010 = 10 - 7 - 10 + * 011 = 10 - 8 - 10 + * 100 = 12 - 6 - 12 + * 101 = 12 - 7 - 12 + * 110 = 12 - 8 - 12 + */ + + return ((predictor << 3) | scheme) << + ((udt - ICI_ISYS_MIPI_CSI2_TYPE_USER_DEF(1)) * 4); +} + +static void csi_short_packet_prepare_firmware_stream_cfg_ici( + struct ici_isys_pipeline *ip, + struct ia_css_isys_stream_cfg_data *cfg) +{ + struct ici_isys_stream *as = + ici_pipeline_to_stream(ip); + struct ici_isys_frame_buf_list *buf_list = + &as->buf_list; + int input_pin = cfg->nof_input_pins++; + int output_pin = cfg->nof_output_pins++; + struct ia_css_isys_input_pin_info *input_info = + &cfg->input_pins[input_pin]; + struct ia_css_isys_output_pin_info *output_info = + &cfg->output_pins[output_pin]; + + /* + * Setting dt as ICI_ISYS_SHORT_PACKET_GENERAL_DT will cause + * MIPI receiver to receive all MIPI short packets. + */ + input_info->dt = ICI_ISYS_SHORT_PACKET_GENERAL_DT; + input_info->input_res.width = ICI_ISYS_SHORT_PACKET_WIDTH; + input_info->input_res.height = buf_list->num_short_packet_lines; + + ip->output_pins[output_pin].pin_ready = + ici_isys_frame_short_packet_ready; + ip->output_pins[output_pin].buf_list = buf_list; + buf_list->short_packet_output_pin = output_pin; + + output_info->input_pin_id = input_pin; + output_info->output_res.width = ICI_ISYS_SHORT_PACKET_WIDTH; + output_info->output_res.height = buf_list->num_short_packet_lines; + output_info->stride = ICI_ISYS_SHORT_PACKET_WIDTH * + ICI_ISYS_SHORT_PACKET_UNITSIZE; + output_info->pt = ICI_ISYS_SHORT_PACKET_PT; + output_info->ft = ICI_ISYS_SHORT_PACKET_FT; + output_info->send_irq = 1; +} + +static int csi_short_packet_configure_tunit( + struct ici_isys_pipeline *ip, + bool enable) +{ + struct ici_isys *isys = ip->isys; + void __iomem *isys_base = isys->pdata->base; + void __iomem *tunit_base = isys_base + TRACE_REG_IS_TRACE_UNIT_BASE; + void __iomem *csi2_tm_base; + void __iomem *event_mask_reg; + unsigned int trace_addr; + int rval; + int i; + + if (ip->csi2->index >= IPU_ISYS_MAX_CSI2_LEGACY_PORTS) { + csi2_tm_base = isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE; + trace_addr = TRACE_REG_CSI2_3PH_TM_TRACE_ADDRESS_VAL; + event_mask_reg = csi2_tm_base + + TRACE_REG_CSI2_3PH_TM_TRACE_DDR_EN_REG_IDX_P( + ip->csi2->index); + } else { + csi2_tm_base = isys->pdata->base + TRACE_REG_CSI2_TM_BASE; + trace_addr = TRACE_REG_CSI2_TM_TRACE_ADDRESS_VAL; + event_mask_reg = csi2_tm_base + + TRACE_REG_CSI2_TM_TRACE_DDR_EN_REG_IDX_P( + ip->csi2->index); + } + + if (!enable) { + writel(0, event_mask_reg); + writel(0, csi2_tm_base + + TRACE_REG_CSI2_TM_OVERALL_ENABLE_REG_IDX); + writel(0, tunit_base + TRACE_REG_TUN_DDR_ENABLE); + return 0; + } + + /* ring buffer base */ + writel(isys->short_packet_trace_buffer_dma_addr, + tunit_base + TRACE_REG_TUN_DRAM_BASE_ADDR); + + /* ring buffer end */ + writel(isys->short_packet_trace_buffer_dma_addr + + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE - + IPU_ISYS_SHORT_PACKET_TRACE_MSG_SIZE, + tunit_base + TRACE_REG_TUN_DRAM_END_ADDR); + + /* Infobits for ddr trace */ + writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY, + tunit_base + TRACE_REG_TUN_DDR_INFO_VAL); + + /* Remove reset from trace timers */ + writel(TRACE_REG_GPREG_TRACE_TIMER_RST_OFF, + isys_base + TRACE_REG_IS_GPREG_TRACE_TIMER_RST_N); + + /* Reset CSI2 monitor */ + writel(1, csi2_tm_base + TRACE_REG_CSI2_TM_RESET_REG_IDX); + + /* Set trace address register. */ + writel(trace_addr, csi2_tm_base + + TRACE_REG_CSI2_TM_TRACE_ADDRESS_REG_IDX); + writel(TRACE_REG_CSI2_TM_TRACE_HEADER_VAL, csi2_tm_base + + TRACE_REG_CSI2_TM_TRACE_HEADER_REG_IDX); + + /* Enable DDR trace. */ + writel(1, tunit_base + TRACE_REG_TUN_DDR_ENABLE); + + /* Enable trace for CSI2 port. */ +#if 0 + reg_val = readl(event_mask_reg); + reg_val |= TRACE_REG_CSI2_TM_EVENT_FS(ip->vc); + writel(reg_val, event_mask_reg); +#else + for (i = 0; i < IPU_ISYS_MAX_CSI2_LEGACY_PORTS + + IPU_ISYS_MAX_CSI2_COMBO_PORTS; i++) { + void __iomem *event_mask_reg = + (i < IPU_ISYS_MAX_CSI2_LEGACY_PORTS) ? + isys->pdata->base + TRACE_REG_CSI2_TM_BASE + + TRACE_REG_CSI2_TM_TRACE_DDR_EN_REG_IDX_P(i) : + isys->pdata->base + TRACE_REG_CSI2_3PH_TM_BASE + + TRACE_REG_CSI2_3PH_TM_TRACE_DDR_EN_REG_IDX_P(i); + + writel(IPU_ISYS_SHORT_PACKET_TRACE_EVENT_MASK, + event_mask_reg); + } +#endif + /* Enable CSI2 receiver monitor */ + writel(1, csi2_tm_base + TRACE_REG_CSI2_TM_OVERALL_ENABLE_REG_IDX); + + rval = ipu_buttress_tsc_read(isys->adev->isp, + &isys->tsc_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read TSC timer.\n"); + return rval; + } + rval = ipu_trace_get_timer(&isys->adev->dev, + &isys->tunit_timer_base); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to read Tunit timer.\n"); + return rval; + } + + return 0; +} + +static void get_stream_opened(struct ici_isys_stream *as) +{ + unsigned long flags; + + spin_lock_irqsave(&as->isys->lock, flags); + as->isys->stream_opened++; + spin_unlock_irqrestore(&as->isys->lock, flags); +} + +static void put_stream_opened(struct ici_isys_stream *as) +{ + unsigned long flags; + + spin_lock_irqsave(&as->isys->lock, flags); + as->isys->stream_opened--; + spin_unlock_irqrestore(&as->isys->lock, flags); +} + +static int get_stream_handle(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + unsigned int stream_handle; + unsigned long flags; + + spin_lock_irqsave(&as->isys->lock, flags); + for (stream_handle = 0; + stream_handle < INTEL_IPU4_ISYS_MAX_STREAMS; stream_handle++) + if (as->isys->ici_pipes[stream_handle] == NULL) + break; + if (stream_handle == INTEL_IPU4_ISYS_MAX_STREAMS) { + spin_unlock_irqrestore(&as->isys->lock, flags); + return -EBUSY; + } + as->isys->ici_pipes[stream_handle] = ip; + ip->stream_handle = stream_handle; + spin_unlock_irqrestore(&as->isys->lock, flags); + return 0; +} + +static void put_stream_handle(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + unsigned long flags; + + spin_lock_irqsave(&as->isys->lock, flags); + as->isys->ici_pipes[ip->stream_handle] = NULL; + ip->stream_handle = -1; + spin_unlock_irqrestore(&as->isys->lock, flags); +} + +/* Create stream and start it using the CSS library API. */ +static int start_stream_firmware(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + struct device *dev = &as->isys->adev->dev; + struct ia_css_isys_stream_cfg_data stream_cfg = { + .src = ip->source, + .vc = 0, + .isl_use = ICI_ISL_OFF, + .nof_input_pins = 1, + }; + struct ia_css_isys_frame_buff_set css_buf; + struct ici_pad_framefmt source_fmt = { + .pad.pad_idx = ip->asd_source_pad_id, + .ffmt = {0}, + + }; + struct ici_isys_node* be_csi2_node = NULL; + + int rval, rvalout, tout, i; + + rval = ip->asd_source->node.node_get_pad_ffmt( + &ip->asd_source->node, &source_fmt); + if (rval) + return rval; + stream_cfg.compfmt = get_comp_format(source_fmt.ffmt.pixelformat); + stream_cfg.input_pins[0].input_res.width = source_fmt.ffmt.width; + stream_cfg.input_pins[0].input_res.height = source_fmt.ffmt.height; + stream_cfg.input_pins[0].dt = + ici_isys_format_code_to_mipi(source_fmt.ffmt.pixelformat); + + /* + * Only CSI2-BE has the capability to do crop, + * so get the crop info from csi2-be. + */ + stream_cfg.crop[0].right_offset = source_fmt.ffmt.width; + stream_cfg.crop[0].bottom_offset = source_fmt.ffmt.height; + if (ip->csi2_be) { + struct ici_pad_selection ps = { + .pad.pad_idx = CSI2_BE_ICI_PAD_SOURCE, + .rect = {0}, + }; + be_csi2_node = &ip->csi2_be->asd.node; + if (be_csi2_node->node_get_pad_sel) + rval = be_csi2_node->node_get_pad_sel( + be_csi2_node, &ps); + else + rval = -ENODEV; + if (!rval) { + stream_cfg.crop[0].left_offset = ps.rect.left; + stream_cfg.crop[0].top_offset = ps.rect.top; + stream_cfg.crop[0].right_offset = ps.rect.left + + ps.rect.width; + stream_cfg.crop[0].bottom_offset = ps.rect.top + + ps.rect.height; + } + } + + as->prepare_firmware_stream_cfg(as, &stream_cfg); + + if (ip->interlaced) { + if (ip->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) + csi_short_packet_prepare_firmware_stream_cfg_ici(ip, + &stream_cfg); + else + csi_short_packet_configure_tunit(ip, 1); + } + +// csslib_dump_isys_stream_cfg(dev, &stream_cfg); //TODO implement corresponding function to dump command input to FW + + ip->nr_output_pins = stream_cfg.nof_output_pins; + + rval = get_stream_handle(as); + if (rval) { + dev_dbg(dev, "Can't get stream_handle\n"); + return rval; + } + + reinit_completion(&ip->stream_open_completion); +/* SKTODO: Debug start */ + printk("SKTODO: My stream open\n"); + printk("ia_css_isys_stream_source src = %d\n", stream_cfg.src); + printk("ia_css_isys_mipi_vc vc = %d\n", stream_cfg.vc); + printk("ia_css_isys_isl_use isl_use = %d\n", stream_cfg.isl_use); + printk("compfmt = %u\n", stream_cfg.compfmt); + printk("struct ia_css_isys_isa_cfg isa_cfg"); + for ( i = 0 ; i < N_IA_CSS_ISYS_CROPPING_LOCATION ; i++ ) { + printk("crop[%d].top_offset = %d\n", i, stream_cfg.crop[i].top_offset); + printk("crop[%d].left_offset = %d\n", i, stream_cfg.crop[i].left_offset); + printk("crop[%d].bottom_offset = %d\n", i, stream_cfg.crop[i].bottom_offset); + printk("crop[%d].right_offset = %d\n", i, stream_cfg.crop[i].right_offset); + } + printk("send_irq_sof_discarded = %u\n", stream_cfg.send_irq_sof_discarded); + printk("send_irq_eof_discarded = %u\n", stream_cfg.send_irq_eof_discarded); + printk("send_resp_sof_discarded = %u\n", stream_cfg.send_resp_sof_discarded); + printk("send_resp_eof_discarded = %u\n", stream_cfg.send_resp_eof_discarded); + printk("nof_input_pins = %u\n", stream_cfg.nof_input_pins); + printk("nof_output_pins = %u\n", stream_cfg.nof_output_pins); + for (i = 0 ; i < stream_cfg.nof_input_pins ; i++) { + printk("input_pins[%d].input_res.width = %u\n", i, stream_cfg.input_pins[i].input_res.width); + printk("input_pins[%d].input_res.height = %u\n", i, stream_cfg.input_pins[i].input_res.height); + printk("input_pins[%d].dt = %d\n", i, stream_cfg.input_pins[i].dt); + printk("input_pins[%d].mipi_store_mode = %d\n", i, stream_cfg.input_pins[i].mipi_store_mode); + } + for (i = 0 ; i < stream_cfg.nof_output_pins ; i++) { + printk("output_pins[%d].input_pin_id = %u\n", i, stream_cfg.output_pins[i].input_pin_id); + printk("output_pins[%d].output_res.width = %u\n", i, stream_cfg.output_pins[i].output_res.width); + printk("output_pins[%d].output_res.height = %u\n", i, stream_cfg.output_pins[i].output_res.height); + printk("output_pins[%d].stride = %u\n", i, stream_cfg.output_pins[i].stride); + printk("output_pins[%d].pt = %d\n", i, stream_cfg.output_pins[i].pt); + printk("output_pins[%d].ft = %d\n", i, stream_cfg.output_pins[i].ft); + printk("output_pins[%d].watermark_in_lines = %u\n", i, stream_cfg.output_pins[i].watermark_in_lines); + printk("output_pins[%d].send_irq = %u\n", i, stream_cfg.output_pins[i].send_irq); + } +/* SKTODO: Debug end */ + rval = ipu_lib_call(stream_open, as->isys, ip->stream_handle, &stream_cfg); + if (rval < 0) { + dev_err(dev, "can't open stream (%d)\n", rval); + goto out_put_stream_handle; + } + get_stream_opened(as); + + tout = wait_for_completion_timeout(&ip->stream_open_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(dev, "stream open time out\n"); + rval = -ETIMEDOUT; + goto out_put_stream_opened; + } + if (ip->error) { + dev_err(dev, "stream open error: %d\n", ip->error); + rval = -EIO; + goto out_put_stream_opened; + } + dev_dbg(dev, "start stream: open complete\n"); + + rval = ici_isys_frame_buf_add_next(as, &css_buf); + if (rval) { + dev_err(dev, "no buffers for streaming (%d)\n", rval); + goto out_stream_close; + } +//TODO implement corresponding function to dump command input to FW +// csslib_dump_isys_frame_buff_set(dev, &css_buf, +// stream_cfg.nof_output_pins); + + reinit_completion(&ip->stream_start_completion); + rval = ipu_lib_call(stream_start, as->isys, ip->stream_handle, + &css_buf); + if (rval < 0) { + dev_err(dev, "can't start streaming (%d)\n", rval); + goto out_stream_close; + } + + tout = wait_for_completion_timeout(&ip->stream_start_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(dev, "stream start time out\n"); + rval = -ETIMEDOUT; + goto out_stream_close; + } + if (ip->error) { + dev_err(dev, "stream start error: %d\n", ip->error); + rval = -EIO; + goto out_stream_close; + } + dev_dbg(dev, "start stream: complete\n"); + + return 0; + +out_stream_close: + reinit_completion(&ip->stream_close_completion); + + rvalout = ipu_lib_call(stream_close, as->isys, ip->stream_handle); + if (rvalout < 0) { + dev_dbg(dev, "can't close stream (%d)\n", rvalout); + } else { + tout = wait_for_completion_timeout(&ip->stream_close_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream close time out\n"); + else if (ip->error) + dev_err(dev, "stream close error: %d\n", ip->error); + else + dev_dbg(dev, "stream close complete\n"); + } + +out_put_stream_opened: + put_stream_opened(as); + +out_put_stream_handle: + put_stream_handle(as); + return rval; +} + +static void stop_streaming_firmware(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + struct device *dev = &as->isys->adev->dev; + int rval, tout; + + reinit_completion(&ip->stream_stop_completion); + rval = ipu_lib_call(stream_flush, as->isys, ip->stream_handle); + if (rval < 0) { + dev_err(dev, "can't stop stream (%d)\n", rval); + } else { + tout = wait_for_completion_timeout(&ip->stream_stop_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream stop time out\n"); + else if (ip->error) + dev_err(dev, "stream stop error: %d\n", ip->error); + else + dev_dbg(dev, "stop stream: complete\n"); + } + if (ip->interlaced && ip->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_TUNIT) + csi_short_packet_configure_tunit(ip, 0); +} + +static void close_streaming_firmware(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + struct device *dev = &as->isys->adev->dev; + int rval, tout; + + reinit_completion(&ip->stream_close_completion); + rval = ipu_lib_call(stream_close, as->isys, ip->stream_handle); + if (rval < 0) { + dev_err(dev, "can't close stream (%d)\n", rval); + } else { + tout = wait_for_completion_timeout(&ip->stream_close_completion, + IPU_LIB_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream close time out\n"); + else if (ip->error) + dev_err(dev, "stream close error: %d\n", ip->error); + else + dev_dbg(dev, "close stream: complete\n"); + } + put_stream_opened(as); + put_stream_handle(as); +} + +void ici_isys_stream_add_capture_done( + struct ici_isys_pipeline* ip, + void (*capture_done)(struct ici_isys_pipeline* ip, + struct ia_css_isys_resp_info* resp)) +{ + unsigned int i; + + /* Different instances may register same function. Add only once */ + for (i = 0; i < ICI_NUM_CAPTURE_DONE ; i++) + if (ip->capture_done[i] == capture_done) + return; + + for (i = 0; i < ICI_NUM_CAPTURE_DONE ; i++) { + if (ip->capture_done[i] == NULL) { + ip->capture_done[i] = capture_done; + return; + } + } + /* + * Too many call backs registered. Change to INTEL_IPU4_NUM_CAPTURE_DONE + * constant probably required. + */ + BUG(); +} + +void ici_isys_prepare_firmware_stream_cfg_default( + struct ici_isys_stream *as, + struct ia_css_isys_stream_cfg_data *cfg) +{ + struct ici_isys_pipeline *ip = &as->ip; + + struct ici_isys_frame_buf_list *bl = &as->buf_list; + + struct ia_css_isys_output_pin_info *pin_info; + int pin = cfg->nof_output_pins++; + + bl->fw_output = pin; + ip->output_pins[pin].pin_ready = ici_isys_frame_buf_ready; + ip->output_pins[pin].buf_list = bl; + + pin_info = &cfg->output_pins[pin]; + pin_info->input_pin_id = 0; + pin_info->output_res.width = as->strm_format.ffmt.width; + pin_info->output_res.height = as->strm_format.ffmt.height; + pin_info->stride = as->strm_format.pfmt.plane_fmt[0].bytesperline; + pin_info->pt = bl->css_pin_type; + pin_info->ft = as->pfmt->css_pixelformat; + pin_info->send_irq = 1; + cfg->vc = ip->vc; +} + +static int pipeline_validate_node(void* cb_data, + struct ici_isys_node* src_node, + struct node_pipe* pipe) +{ + int rval; + struct ici_isys_pipeline *ip = cb_data; + + dev_err(&ip->pipeline_dev->dev, "Validating node %s\n", + src_node->name); + if (src_node->node_pipeline_validate) { + rval = src_node->node_pipeline_validate(&ip->pipe, + src_node); + if (rval) + return rval; + } + if (pipe) { + struct ici_isys_node* sink_node = + pipe->sink_pad->node; + struct ici_pad_framefmt src_format = { + .pad.pad_idx = pipe->src_pad->pad_id, + }; + struct ici_pad_framefmt sink_format = { + .pad.pad_idx = pipe->sink_pad->pad_id, + }; + if (src_node->node_get_pad_ffmt) { + rval = src_node->node_get_pad_ffmt(src_node, + &src_format); + if (rval) + return rval; + } + if (sink_node->node_get_pad_ffmt) { + rval = sink_node->node_get_pad_ffmt(sink_node, + &sink_format); + if (rval) + return rval; + } + if (src_format.ffmt.width != sink_format.ffmt.width || + src_format.ffmt.height != sink_format.ffmt.height || + src_format.ffmt.pixelformat != sink_format.ffmt.pixelformat || + src_format.ffmt.field != sink_format.ffmt.field || + src_format.ffmt.colorspace != sink_format.ffmt.colorspace) { + dev_err(&ip->pipeline_dev->dev, "Formats don't match node (%d:%d) -> node (%d:%d)\n", + src_node->node_id, src_format.pad.pad_idx, + sink_node->node_id, sink_format.pad.pad_idx); + return -EINVAL; + } + } + return 0; +} + +static int pipeline_validate( + struct ici_isys_node *node, + struct ici_isys_pipeline *ip) +{ + return ici_isys_pipeline_for_each_node( + pipeline_validate_node, + ip, + node, + ip, + true); +} + +struct set_streaming_data { + struct ici_isys_pipeline *ip; + bool external; + int state; +}; + +static int set_streaming_node(void* cb_data, + struct ici_isys_node* node, + struct node_pipe* pipe) +{ + struct set_streaming_data* data = cb_data; + if (data->external != node->external) + return 0; + + if (node->node_set_streaming) + return node->node_set_streaming(node, data->ip, + data->state); + return 0; +} + + +static int set_streaming(struct ici_isys_node *node, + struct ici_isys_pipeline *ip, + bool external, + int state) +{ + struct set_streaming_data data = { + .ip = ip, + .external = external, + .state = state + }; + return ici_isys_pipeline_for_each_node( + set_streaming_node, + &data, + node, + ip, + true); +} + +static int ici_isys_set_streaming( + struct ici_isys_stream *as, + unsigned int state) +{ + struct ici_isys_pipeline *ip = &as->ip; + int rval = 0; + + dev_dbg(&as->isys->adev->dev, "set stream (intel_stream%d): %d\n", state, + as->strm_dev.minor); + + if (!state) { + stop_streaming_firmware(as); + + /* stop external sub-device now. */ + if (ip->csi2) { + ici_isys_csi2_wait_last_eof(ip->csi2); + } + + set_streaming(&as->node, ip, true, 0); + } + + rval = set_streaming(&as->node, ip, false, state); + if (rval) + goto out_stop_streaming; + + if (state) { + rval = start_stream_firmware(as); + if (rval) { + goto out_stop_streaming; + } + dev_dbg(&ip->isys->adev->dev, "set stream: source %d, stream_handle %d\n", + ip->source, ip->stream_handle); + + /* Start external sub-device now. */ + rval = set_streaming(&as->node, ip, true, state); + if (rval) + goto out_stop_streaming_firmware; + + } else { + close_streaming_firmware(as); + } + + ip->streaming = state; + return 0; + +out_stop_streaming_firmware: + stop_streaming_firmware(as); + +out_stop_streaming: + set_streaming(&as->node, ip, false, 0); + return rval; +} + +static void stream_buffers(struct ici_isys_stream *as) +{ + struct ici_isys_pipeline *ip = &as->ip; + struct ia_css_isys_frame_buff_set set = {}; + int rval; + + for (;;) { + rval = ici_isys_frame_buf_add_next(as, &set); + if (rval) { + break; + } +//TODO implement corresponding function to dump command input to FW +// csslib_dump_isys_frame_buff_set(&as->isys->adev->dev, &set, +// ip->nr_output_pins); + WARN_ON(ipu_lib_call( + stream_capture_indication, as->isys, + ip->stream_handle, &set) < 0); + } +} + +static int ici_isys_stream_on(struct file *file, void *fh) +{ + struct ici_isys_stream *as = + dev_to_stream(file->private_data); + struct ici_isys_pipeline *ip = &as->ip; + int rval, i; + + dev_dbg(&as->isys->adev->dev, + "stream_on: %u\n", as->strm_dev.minor); + + if (ip->streaming) { + dev_dbg(&as->isys->adev->dev, + "Already streaming\n"); + return 0; + } + + ip->csi2 = NULL; + ip->csi2_be = NULL; + ip->asd_source = NULL; + ip->asd_source_pad_id = 0; + rval = pipeline_validate(&as->node, ip); + if (rval) + return rval; + + if (!ip->asd_source) { + dev_err(&ip->isys->adev->dev, "set stream: Pipeline does not have a source\n"); + return -ENODEV; + } + + pipeline_set_power(as, 1); + + mutex_lock(&as->isys->stream_mutex); + ip->source = ip->asd_source->source; + + for (i = 0; i < ICI_NUM_CAPTURE_DONE; i++) + ip->capture_done[i] = NULL; + + if (ip->interlaced) { + pr_err("** SKTODO: INTERLACE ENABLED **\n"); + if (ip->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) { + rval = ici_isys_frame_buf_short_packet_setup( + as, &as->strm_format); + if (rval) + goto out_requeue; + } else { + memset(ip->isys->short_packet_trace_buffer, 0, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE); + dma_sync_single_for_device(&as->isys->adev->dev, + as->isys->short_packet_trace_buffer_dma_addr, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + ip->short_packet_trace_index = 0; + } + } + + rval = ici_isys_set_streaming(as, 1); + if (rval) + goto out_cleanup_short_packet; + + ip->streaming = 1; + + dev_dbg(&as->isys->adev->dev, "dispatching queued requests\n"); + stream_buffers(as); + dev_dbg(&as->isys->adev->dev, + "done dispatching queued requests\n"); + + mutex_unlock(&as->isys->stream_mutex); + + return 0; + +out_cleanup_short_packet: + ici_isys_frame_buf_short_packet_destroy(as); + +out_requeue: + ici_isys_frame_buf_stream_cancel(as); + mutex_unlock(&as->isys->stream_mutex); + pipeline_set_power(as, 0); + return rval; +} + +static int ici_isys_stream_off(struct file *file, void *fh) +{ + struct ici_isys_stream *as = + dev_to_stream(file->private_data); + struct ici_isys_pipeline *ip = &as->ip; + + mutex_lock(&as->isys->stream_mutex); + if (ip->streaming) + ici_isys_set_streaming(as, 0); + + ip->streaming = 0; + ici_isys_frame_buf_short_packet_destroy(as); + mutex_unlock(&as->isys->stream_mutex); + + ici_isys_frame_buf_stream_cancel(as); + pipeline_set_power(as, 0); + return 0; +} + +const struct ici_isys_pixelformat +*ici_isys_get_pixelformat( + struct ici_isys_stream *as, unsigned int pixelformat) +{ + const struct ici_isys_pixelformat *pfmt; + unsigned pad; + const unsigned *supported_codes; + + pad = as->pad.pad_id; + supported_codes = as->asd->supported_codes[pad]; + + for (pfmt = as->pfmts; pfmt->bpp; pfmt++) { + unsigned int i; + + if (pfmt->code != pixelformat) + continue; + + for (i = 0; supported_codes[i]; i++) { + if (pfmt->code == supported_codes[i]) + return pfmt; + } + } + + /* Not found. Get the default, i.e. the first defined one. */ + for (pfmt = as->pfmts; pfmt->bpp; pfmt++) { + if (pfmt->code == *supported_codes) + return pfmt; + } + + BUG(); +} + +const struct ici_isys_pixelformat +*ici_isys_video_try_fmt_vid_mplane_default( + struct ici_isys_stream *as, + struct ici_stream_format *mpix) +{ + const struct ici_isys_pixelformat *pfmt = + ici_isys_get_pixelformat(as, mpix->ffmt.pixelformat); + + mpix->ffmt.pixelformat = pfmt->pixelformat; + mpix->pfmt.num_planes = 1; + + if (!as->packed) + mpix->pfmt.plane_fmt[0].bytesperline = + mpix->ffmt.width * DIV_ROUND_UP(pfmt->bpp, + BITS_PER_BYTE); + else + mpix->pfmt.plane_fmt[0].bytesperline = DIV_ROUND_UP( + as->line_header_length + as->line_footer_length + + (unsigned int)mpix->ffmt.width * pfmt->bpp, + BITS_PER_BYTE); + + mpix->pfmt.plane_fmt[0].bytesperline = + ALIGN(mpix->pfmt.plane_fmt[0].bytesperline, + as->isys->line_align); + mpix->pfmt.plane_fmt[0].bpp = pfmt->bpp; + + /* + * (height + 1) * bytesperline due to a hardware issue: the DMA unit + * is a power of two, and a line should be transferred as few units + * as possible. The result is that up to line length more data than + * the image size may be transferred to memory after the image. + * Another limition is the GDA allocation unit size. For low + * resolution it gives a bigger number. Use larger one to avoid + * memory corruption. + */ + mpix->pfmt.plane_fmt[0].sizeimage = + max(max(mpix->pfmt.plane_fmt[0].sizeimage, + mpix->pfmt.plane_fmt[0].bytesperline * + mpix->ffmt.height + + max(mpix->pfmt.plane_fmt[0].bytesperline, + as->isys->pdata->ipdata->isys_dma_overshoot)), + 1U); + + if (mpix->ffmt.field == ICI_FIELD_ANY) + mpix->ffmt.field = ICI_FIELD_NONE; + + return pfmt; +} + +static int ici_s_fmt_vid_cap_mplane( + struct ici_isys_stream *as, + struct ici_stream_format *f) +{ + if (as->ip.streaming) + return -EBUSY; + + as->pfmt = as->try_fmt_vid_mplane(as, f); + as->strm_format = *f; + + return 0; +} + +/** + * Returns true if device does not support real interrupts and + * polling must be used. + */ +static int ici_poll_for_events( + struct ici_isys_stream *as) +{ +// return is_intel_ipu_hw_fpga(); + return 0; +} + +static void ipu_cleanup_fw_msg_bufs(struct ici_isys *isys) +{ + struct isys_fw_msgs *fwmsg, *fwmsg0; + unsigned long flags; + + spin_lock_irqsave(&isys->listlock, flags); + list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) + list_move(&fwmsg->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); +} + +static int stream_fop_open(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = + inode_to_intel_ipu_stream_device(inode); + struct ici_isys_stream* as = dev_to_stream(strm_dev); + struct ici_isys *isys = as->isys; + struct ipu_bus_device *adev = + to_ipu_bus_device(&isys->adev->dev); + struct ipu_device *isp = adev->isp; + int rval; + DEBUGK("%s: stream open (%p)\n", __func__, as); + + mutex_lock(&isys->mutex); + if (isys->reset_needed) { + mutex_unlock(&isys->mutex); + dev_warn(&isys->adev->dev, "isys power cycle required\n"); + return -EIO; + } + mutex_unlock(&isys->mutex); + + rval = ipu_buttress_authenticate(isp); + if (rval) { + dev_err(&isys->adev->dev, "FW authentication failed\n"); + return rval; + } + + rval = pm_runtime_get_sync(&isys->adev->dev); + if (rval < 0) { + pm_runtime_put_noidle(&isys->adev->dev); + return rval; + } + + mutex_lock(&isys->mutex); + + ipu_configure_spc(adev->isp, + &isys->pdata->ipdata->hw_variant, + IA_CSS_PKG_DIR_ISYS_INDEX, + isys->pdata->base, isys->pkg_dir, + isys->pkg_dir_dma_addr); + + + if (isys->ici_stream_opened++) { + /* Already open */ + mutex_unlock(&isys->mutex); + return 0; + } + + ipu_cleanup_fw_msg_bufs(isys); + + if (isys->fwcom) { + /* + * Something went wrong in previous shutdown. As we are now + * restarting isys we can safely delete old context. + */ + ici_fw_isys_cleanup(isys); + isys->fwcom = NULL; + } + + if (ici_poll_for_events(as)) { + static const struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO/2, + }; + + isys->isr_thread = kthread_run( + intel_ipu4_isys_isr_run_ici, as->isys, + IPU_ISYS_ENTITY_PREFIX); + + if (IS_ERR(isys->isr_thread)) { + rval = PTR_ERR(isys->isr_thread); + goto out_intel_ipu4_pipeline_pm_use; + } + + sched_setscheduler(isys->isr_thread, SCHED_FIFO, ¶m); + } + + rval = ici_fw_isys_init(as->isys, IPU_ISYS_NUM_STREAMS); + if (rval < 0) + goto out_lib_init; + + mutex_unlock(&isys->mutex); + + return 0; + +out_lib_init: + if (ici_poll_for_events(as)) + kthread_stop(isys->isr_thread); + +out_intel_ipu4_pipeline_pm_use: + isys->ici_stream_opened--; + mutex_unlock(&isys->mutex); + pm_runtime_put(&isys->adev->dev); + + return rval; +} + +static int stream_fop_release(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = + inode_to_intel_ipu_stream_device(inode); + struct ici_isys_stream* as = dev_to_stream(strm_dev); + int ret = 0; + DEBUGK("%s: stream release (%p)\n", __func__, as); + + if (as->ip.streaming) { + ici_isys_stream_off(file, NULL); + } + + mutex_lock(&as->isys->mutex); + + if (!--as->isys->ici_stream_opened) { + if (ici_poll_for_events(as)) + kthread_stop(as->isys->isr_thread); + + intel_ipu4_isys_library_close(as->isys); + if (as->isys->fwcom) { + as->isys->reset_needed = true; + ret = -EIO; + } + } + + mutex_unlock(&as->isys->mutex); + + pm_runtime_put(&as->isys->adev->dev); + return ret; +} + +static unsigned int stream_fop_poll(struct file *file, + struct poll_table_struct *poll) +{ + struct ici_isys_stream *as = + dev_to_stream(file->private_data); + struct ici_isys *isys = as->isys; + unsigned int res = 0; + + dev_dbg(&isys->adev->dev, "stream_fop_poll\n"); + + poll_wait(file, &as->buf_list.wait, poll); + + if (!list_empty(&as->buf_list.putbuf_list)) + res = POLLIN; + + dev_dbg(&isys->adev->dev, "stream_fop_poll res %u\n", res); + + return res; +} + +static int ici_isys_set_format(struct file *file, void *fh, + struct ici_stream_format *sf) +{ + int rval; + struct ici_isys_stream *as = + dev_to_stream(file->private_data); + struct ici_isys *isys = as->isys; + + DEBUGK("%s: ici stream set format (%p)\n \ + width: %u, height: %u, pixelformat: %u, field: %u, colorspace: %u\n", + __func__, as, + sf->ffmt.width, + sf->ffmt.height, + sf->ffmt.pixelformat, + sf->ffmt.field, + sf->ffmt.colorspace); + + if (sf->ffmt.field == ICI_FIELD_ALTERNATE) { + DEBUGK("Interlaced enabled\n"); + as->ip.interlaced = true; + as->ip.short_packet_source = 1; + } else { + as->ip.interlaced = false; + } + + rval = ici_s_fmt_vid_cap_mplane(as, sf); + if (rval) { + dev_err(&isys->adev->dev, "failed to set format (vid_cap) %d\n", rval); + return rval; + } + if (sf->pfmt.num_planes != 1) { + dev_err(&isys->adev->dev, "Invalid num of planes %d\n", + sf->pfmt.num_planes); + return rval; + } + if (!sf->pfmt.plane_fmt[0].sizeimage) { + dev_err(&isys->adev->dev, "Zero image size for plane 0\n"); + return rval; + } + + return 0; +} + +static int ici_isys_getbuf(struct file *file, void *fh, + struct ici_frame_info *user_frame_info) +{ + int rval = 0; + struct ici_isys_stream *as = dev_to_stream( + file->private_data); + struct ici_isys *isys = as->isys; + + //DEBUGK("%s: ici stream getbuf (%p)\n", __func__, as); + rval = ici_isys_get_buf(as, user_frame_info); + if(rval) { + dev_err(&isys->adev->dev, "failed to get buffer %d\n", rval); + return rval; + } + + mutex_lock(&as->isys->stream_mutex); + if (as->ip.streaming) { + stream_buffers(as); + } + mutex_unlock(&as->isys->stream_mutex); + return 0; +} + +static int ici_isys_getbuf_virt(struct file *file, void *fh, + struct ici_frame_buf_wrapper *user_frame_buf, struct page **pages) +{ + int rval = 0; + struct ici_isys_stream *as = dev_to_stream( + file->private_data); + struct ici_isys *isys = as->isys; + + rval = ici_isys_get_buf_virt(as, user_frame_buf, pages); + if (rval) { + dev_err(&isys->adev->dev, "failed to get buffer %d\n", rval); + return rval; + } + + mutex_lock(&as->isys->stream_mutex); + if (as->ip.streaming) { + stream_buffers(as); + } + mutex_unlock(&as->isys->stream_mutex); + return 0; +} + +static int ici_isys_putbuf(struct file *file, void *fh, + struct ici_frame_info *user_frame_info) +{ + int rval = 0; + struct ici_isys_stream *as = dev_to_stream(file->private_data); + struct ici_isys *isys = as->isys; + //DEBUGK("%s: ici stream putbuf (%p)\n", __func__, as); + rval = ici_isys_put_buf(as, user_frame_info, + file->f_flags); + if(rval) { + dev_err(&isys->adev->dev, "failed to put buffer %d\n", rval); + return rval; + } + return 0; +} + +static int ici_isys_stream_get_ffmt( + struct ici_isys_node* node, + struct ici_pad_framefmt* pff) +{ + struct ici_isys_stream *as = node->sd; + if (pff->pad.pad_idx != 0) { + dev_err(&as->isys->adev->dev, + "Stream only has pad 0\n"); + return -EINVAL; + } + pff->ffmt = as->strm_format.ffmt; + return 0; +} + +static const struct ici_ioctl_ops ioctl_ops_mplane_ici = { + .ici_set_format = ici_isys_set_format, + .ici_stream_on = ici_isys_stream_on, + .ici_stream_off = ici_isys_stream_off, + .ici_get_buf = ici_isys_getbuf, + .ici_get_buf_virt = ici_isys_getbuf_virt, + .ici_put_buf = ici_isys_putbuf, +}; + +static const struct file_operations ipu4_isys_ici_stream_fops = { + .owner = THIS_MODULE, + .poll = stream_fop_poll, + .open = stream_fop_open, + .release = stream_fop_release, +}; + +int ici_isys_stream_init( + struct ici_isys_stream *as, + struct ici_isys_subdev *asd, + struct ici_isys_node *node, + unsigned int pad, + unsigned long pad_flags) +{ + int rval; + char name[ICI_MAX_NODE_NAME]; + + mutex_init(&as->mutex); + init_completion(&as->ip.stream_open_completion); + init_completion(&as->ip.stream_close_completion); + init_completion(&as->ip.stream_start_completion); + init_completion(&as->ip.stream_stop_completion); + init_completion(&as->ip.capture_ack_completion); + as->ip.isys = as->isys; + as->ip.pipeline_dev = &as->isys->pipeline_dev; + as->asd = asd; + + as->strm_dev.ipu_ioctl_ops = &ioctl_ops_mplane_ici; + + ici_isys_frame_buf_init(&as->buf_list); + + as->pad.flags = pad_flags | ICI_PAD_FLAGS_MUST_CONNECT; + snprintf(name, sizeof(name), + "%s Stream", asd->node.name); + rval = ici_isys_pipeline_node_init(as->isys, + &as->node, name, 1, &as->pad); + if (rval) + goto out_init_fail; + + if (__ici_isys_subdev_get_ffmt(asd, pad)) + as->strm_format.ffmt = + *__ici_isys_subdev_get_ffmt(asd, pad); + + as->node.sd = as; + as->node.pipe = &as->ip.pipe; + as->node.node_get_pad_ffmt = + ici_isys_stream_get_ffmt; + + asd->node.pipe = &as->ip.pipe; + /*asd->node.ops = &entity_ops;*/ + as->strm_dev.fops = &ipu4_isys_ici_stream_fops; + + as->strm_dev.frame_buf_list = &as->buf_list; + as->strm_dev.mutex = &as->mutex; + as->strm_dev.dev_parent = &as->isys->adev->dev; + dev_set_drvdata(&as->strm_dev.dev, as); + + mutex_lock(&as->mutex); + + rval = stream_device_register(&as->strm_dev); + if (rval) + goto out_mutex_unlock; + + if (pad_flags & ICI_PAD_FLAGS_SINK) + rval = node_pad_create_link( + node, pad, &as->node, 0, 0); + else if (pad_flags & ICI_PAD_FLAGS_SOURCE) + rval = node_pad_create_link( + &as->node, 0, node, pad, 0); + + if (rval) { + printk(KERN_WARNING "can't create link\n"); + goto out_mutex_unlock; + } + + mutex_unlock(&as->mutex); + + return rval; + +out_mutex_unlock: + mutex_unlock(&as->mutex); + node_pads_cleanup(&as->asd->node); + //intel_ipu4_isys_framebuf_cleanup(&as->buf_list); +out_init_fail: + mutex_destroy(&as->mutex); + + return rval; +} + +void ici_isys_stream_cleanup(struct ici_isys_stream *as) +{ + list_del(&as->node.node_entry); + stream_device_unregister(&as->strm_dev); + node_pads_cleanup(&as->asd->node); + mutex_destroy(&as->mutex); + //intel_ipu4_isys_framebuf_cleanup(&as->buf_list); +} + +#endif //ICI_ENABLED + diff --git a/drivers/media/pci/intel/ici/ici-isys-stream.h b/drivers/media/pci/intel/ici/ici-isys-stream.h new file mode 100644 index 000000000000..77d89ed2ea79 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-stream.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_STREAM_H +#define ICI_ISYS_STREAM_H + +#include +#include +#include + +#include "ici-isys-stream-device.h" +#include "ici-isys-frame-buf.h" +#include "ici-isys-pipeline.h" + + +struct ici_isys; +struct ia_css_isys_stream_cfg_data; +struct ici_isys_subdev; + +struct ici_isys_pixelformat { + uint32_t pixelformat; + uint32_t bpp; + uint32_t bpp_packed; + uint32_t code; + uint32_t css_pixelformat; +}; + +struct ici_isys_stream { + /* Serialise access to other fields in the struct. */ + struct mutex mutex; + struct node_pad pad; + struct ici_isys_node node; + struct ici_stream_device strm_dev; + struct ici_stream_format strm_format; + const struct ici_isys_pixelformat *pfmts; + const struct ici_isys_pixelformat *pfmt; + struct ici_isys_frame_buf_list buf_list; + struct ici_isys_subdev* asd; + struct ici_isys *isys; /* its parent device */ + struct ici_isys_pipeline ip; + unsigned int streaming; + bool packed; + unsigned int line_header_length; /* bits */ + unsigned int line_footer_length; /* bits */ + const struct ici_isys_pixelformat *(*try_fmt_vid_mplane)( + struct ici_isys_stream *as, + struct ici_stream_format *mpix); + void (*prepare_firmware_stream_cfg)( + struct ici_isys_stream *as, + struct ia_css_isys_stream_cfg_data *cfg); + int (*frame_done_notify_queue)(void); +}; + +#define to_intel_ipu4_isys_ici_stream(__buf_list) \ + container_of(__buf_list, struct ici_isys_stream, buf_list) +#define ici_pipeline_to_stream(__ip) \ + container_of(__ip, struct ici_isys_stream, ip) + +extern const struct ici_isys_pixelformat ici_isys_pfmts[]; +extern const struct ici_isys_pixelformat ici_isys_pfmts_be_soc[]; +extern const struct ici_isys_pixelformat ici_isys_pfmts_packed[]; + +const struct ici_isys_pixelformat +*ici_isys_video_try_fmt_vid_mplane_default( + struct ici_isys_stream *as, + struct ici_stream_format *mpix); +void ici_isys_prepare_firmware_stream_cfg_default( + struct ici_isys_stream *as, + struct ia_css_isys_stream_cfg_data *cfg); + +int ici_isys_stream_init(struct ici_isys_stream *as, + struct ici_isys_subdev *asd, + struct ici_isys_node *node, + unsigned int pad, + unsigned long pad_flags); +void ici_isys_stream_cleanup(struct ici_isys_stream *as); + +void ici_isys_stream_add_capture_done( + struct ici_isys_pipeline* ip, + void (*capture_done)(struct ici_isys_pipeline* ip, + struct ia_css_isys_resp_info* resp)); + +#endif /* ICI_ISYS_STREAM_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-subdev.c b/drivers/media/pci/intel/ici/ici-isys-subdev.c new file mode 100644 index 000000000000..4d12a700d015 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-subdev.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED + +#include "./ici/ici-isys-subdev.h" +#include "./ici/ici-isys-pipeline.h" + +unsigned int ici_isys_format_code_to_bpp(u32 code) +{ + switch (code) { + case ICI_FORMAT_RGB888: + return 24; + case ICI_FORMAT_RGB565: + case ICI_FORMAT_UYVY: + case ICI_FORMAT_YUYV: + return 16; + case ICI_FORMAT_SBGGR12: + case ICI_FORMAT_SGBRG12: + case ICI_FORMAT_SGRBG12: + case ICI_FORMAT_SRGGB12: + return 12; + case ICI_FORMAT_SBGGR10: + case ICI_FORMAT_SGBRG10: + case ICI_FORMAT_SGRBG10: + case ICI_FORMAT_SRGGB10: + return 10; + case ICI_FORMAT_SBGGR8: + case ICI_FORMAT_SGBRG8: + case ICI_FORMAT_SGRBG8: + case ICI_FORMAT_SRGGB8: + case ICI_FORMAT_SBGGR10_DPCM8: + case ICI_FORMAT_SGBRG10_DPCM8: + case ICI_FORMAT_SGRBG10_DPCM8: + case ICI_FORMAT_SRGGB10_DPCM8: + return 8; + default: + BUG_ON(1); + } +} + +unsigned int ici_isys_format_code_to_mipi(u32 code) +{ + switch (code) { + case ICI_FORMAT_RGB565: + return ICI_ISYS_MIPI_CSI2_TYPE_RGB565; + case ICI_FORMAT_RGB888: + return ICI_ISYS_MIPI_CSI2_TYPE_RGB888; + case ICI_FORMAT_UYVY: + case ICI_FORMAT_YUYV: + return ICI_ISYS_MIPI_CSI2_TYPE_YUV422_8; + case ICI_FORMAT_SBGGR12: + case ICI_FORMAT_SGBRG12: + case ICI_FORMAT_SGRBG12: + case ICI_FORMAT_SRGGB12: + return ICI_ISYS_MIPI_CSI2_TYPE_RAW12; + case ICI_FORMAT_SBGGR10: + case ICI_FORMAT_SGBRG10: + case ICI_FORMAT_SGRBG10: + case ICI_FORMAT_SRGGB10: + return ICI_ISYS_MIPI_CSI2_TYPE_RAW10; + case ICI_FORMAT_SBGGR8: + case ICI_FORMAT_SGBRG8: + case ICI_FORMAT_SGRBG8: + case ICI_FORMAT_SRGGB8: + return ICI_ISYS_MIPI_CSI2_TYPE_RAW8; + case ICI_FORMAT_SBGGR10_DPCM8: + case ICI_FORMAT_SGBRG10_DPCM8: + case ICI_FORMAT_SGRBG10_DPCM8: + case ICI_FORMAT_SRGGB10_DPCM8: + return ICI_ISYS_MIPI_CSI2_TYPE_USER_DEF(1); + default: + BUG_ON(1); + } +} + +enum ici_isys_subdev_pixelorder +ici_isys_subdev_get_pixelorder(u32 code) +{ + switch (code) { + case ICI_FORMAT_SBGGR12: + case ICI_FORMAT_SBGGR10: + case ICI_FORMAT_SBGGR8: + case ICI_FORMAT_SBGGR10_DPCM8: + return ICI_ISYS_SUBDEV_PIXELORDER_BGGR; + case ICI_FORMAT_SGBRG12: + case ICI_FORMAT_SGBRG10: + case ICI_FORMAT_SGBRG8: + case ICI_FORMAT_SGBRG10_DPCM8: + return ICI_ISYS_SUBDEV_PIXELORDER_GBRG; + case ICI_FORMAT_SGRBG12: + case ICI_FORMAT_SGRBG10: + case ICI_FORMAT_SGRBG8: + case ICI_FORMAT_SGRBG10_DPCM8: + return ICI_ISYS_SUBDEV_PIXELORDER_GRBG; + case ICI_FORMAT_SRGGB12: + case ICI_FORMAT_SRGGB10: + case ICI_FORMAT_SRGGB8: + case ICI_FORMAT_SRGGB10_DPCM8: + return ICI_ISYS_SUBDEV_PIXELORDER_RGGB; + default: + BUG_ON(1); + } +} + +unsigned int ici_isys_get_compression_scheme(u32 code) +{ + switch (code) { + case ICI_FORMAT_SBGGR10_DPCM8: + case ICI_FORMAT_SGBRG10_DPCM8: + case ICI_FORMAT_SGRBG10_DPCM8: + case ICI_FORMAT_SRGGB10_DPCM8: + return 3; + default: + return 0; + } +} + +u32 ici_isys_subdev_code_to_uncompressed(u32 sink_code) +{ + switch (sink_code) { + case ICI_FORMAT_SBGGR10_DPCM8: + return ICI_FORMAT_SBGGR10; + case ICI_FORMAT_SGBRG10_DPCM8: + return ICI_FORMAT_SGBRG10; + case ICI_FORMAT_SGRBG10_DPCM8: + return ICI_FORMAT_SGRBG10; + case ICI_FORMAT_SRGGB10_DPCM8: + return ICI_FORMAT_SRGGB10; + default: + return sink_code; + } +} + +struct ici_framefmt *__ici_isys_subdev_get_ffmt( + struct ici_isys_subdev *asd, + unsigned pad) +{ + if (pad >= asd->num_pads) + return NULL; + + return &asd->ffmt[pad]; +} + +int ici_isys_subdev_get_ffmt( + struct ici_isys_node* node, + struct ici_pad_framefmt* pff) +{ + int ret = 0; + struct ici_framefmt *format_out; + struct ici_isys_subdev *asd = node->sd; + + mutex_lock(&asd->mutex); + format_out = __ici_isys_subdev_get_ffmt(asd, + pff->pad.pad_idx); + if (format_out) + pff->ffmt = *format_out; + else + ret = -EINVAL; + mutex_unlock(&asd->mutex); + return ret; +} + +static int __subdev_set_ffmt(struct ici_isys_subdev *asd, + struct ici_pad_framefmt *pff) +{ + unsigned int i; + unsigned pad = pff->pad.pad_idx; + unsigned pixelformat; + BUG_ON(!mutex_is_locked(&asd->mutex)); + + if (pad >= asd->num_pads) + return -EINVAL; + + pff->ffmt.width = clamp(pff->ffmt.width, + IPU_ISYS_MIN_WIDTH, + IPU_ISYS_MAX_WIDTH); + pff->ffmt.height = clamp(pff->ffmt.height, + IPU_ISYS_MIN_HEIGHT, + IPU_ISYS_MAX_HEIGHT); + + pixelformat = asd->supported_codes[pad][0]; + for (i = 0; asd->supported_codes[pad][i]; i++) { + if (asd->supported_codes[pad][i] == + pff->ffmt.pixelformat) { + + pixelformat = asd->supported_codes[pad][i]; + break; + } + } + pff->ffmt.pixelformat = pixelformat; + asd->set_ffmt_internal(asd, pad, &pff->ffmt); + asd->ffmt[pad] = pff->ffmt; + return 0; +} + +int ici_isys_subdev_set_ffmt( + struct ici_isys_node* node, + struct ici_pad_framefmt* pff) +{ + int res; + struct ici_isys_subdev *asd = node->sd; + + mutex_lock(&asd->mutex); + res = __subdev_set_ffmt(asd, pff); + mutex_unlock(&asd->mutex); + return res; +} + +int ici_isys_subdev_get_supported_format( + struct ici_isys_node* node, + struct ici_pad_supported_format_desc* psfd) +{ + struct ici_isys_subdev *asd = node->sd; + int pad = psfd->pad.pad_idx; + int idx = psfd->idx; + int i; + int rval = 0; + + mutex_lock(&asd->mutex); + if (!asd->supported_code_counts[pad]) { + for (i = 0; asd->supported_codes[pad][i]; i++) {} + asd->supported_code_counts[pad] = i; + } + + if (idx < asd->supported_code_counts[pad]) { + psfd->color_format = asd->supported_codes[pad][idx]; + psfd->min_width = IPU_ISYS_MIN_WIDTH; + psfd->max_width = IPU_ISYS_MAX_WIDTH; + psfd->min_height = IPU_ISYS_MIN_HEIGHT; + psfd->max_height = IPU_ISYS_MAX_HEIGHT; + } else { + rval = -EINVAL; + } + + mutex_unlock(&asd->mutex); + return rval; +} + + +int intel_ipu4_isys_subdev_set_crop_rect(struct ici_isys_subdev + *asd, unsigned pad, + struct ici_rect *r) +{ + struct node_pad *np; + struct ici_rect rmax = { 0 }; + struct ici_rect *rcrop; + unsigned int tgt; + struct ici_framefmt *ffmt = + __ici_isys_subdev_get_ffmt(asd, pad); + + if (!ffmt) + return -EINVAL; + if (!asd->valid_tgts[pad].crop) + return -EINVAL; + np = &asd->pads[pad]; + rcrop = &asd->crop[pad]; + + if (np->flags & ICI_PAD_FLAGS_SINK) { + rmax.width = ffmt->width; + rmax.height = ffmt->height; + tgt = ICI_ISYS_SUBDEV_PROP_TGT_SINK_CROP; + } else { + /* 0 is the sink pad. */ + rmax = asd->crop[0]; + tgt = ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP; + } + rcrop->width = clamp(r->width, IPU_ISYS_MIN_WIDTH, rmax.width); + rcrop->height = clamp(r->height, IPU_ISYS_MIN_HEIGHT, + rmax.height); + ici_isys_subdev_fmt_propagate(asd, pad, rcrop, tgt, NULL); + return 0; +} + +int intel_ipu4_isys_subdev_set_compose_rect(struct ici_isys_subdev + *asd, unsigned pad, + struct ici_rect *r) +{ + struct node_pad *np; + struct ici_rect rmax = { 0 }; + struct ici_rect *rcompose; + unsigned int tgt; + struct ici_framefmt *ffmt = + __ici_isys_subdev_get_ffmt(asd, pad); + + if (!ffmt) + return -EINVAL; + if (!asd->valid_tgts[pad].compose) + return -EINVAL; + np = &asd->pads[pad]; + rcompose = &asd->compose[pad]; + + if (np->flags & ICI_PAD_FLAGS_SINK) { + rmax = asd->crop[pad]; + tgt = ICI_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE; + } else { + /* 0 is the sink pad. */ + rmax = asd->compose[0]; + tgt = ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE; + } + rcompose->width = clamp(r->width, IPU_ISYS_MIN_WIDTH, + rmax.width); + rcompose->height = clamp(r->height, IPU_ISYS_MIN_HEIGHT, + rmax.height); + ici_isys_subdev_fmt_propagate(asd, pad, rcompose, tgt, + NULL); + return 0; +} + +int ici_isys_subdev_set_sel( + struct ici_isys_node* node, + struct ici_pad_selection* ps) +{ + struct ici_isys_subdev *asd = node->sd; + int rval = 0; + + if (WARN_ON(ps->pad.pad_idx >= asd->num_pads)) + return -EINVAL; + + mutex_lock(&asd->mutex); + switch (ps->sel_type) + { + case ICI_EXT_SEL_TYPE_COMPOSE: + rval = intel_ipu4_isys_subdev_set_compose_rect( + asd, ps->pad.pad_idx, &ps->rect); + break; + case ICI_EXT_SEL_TYPE_CROP: + rval = intel_ipu4_isys_subdev_set_crop_rect( + asd, ps->pad.pad_idx, &ps->rect); + break; + default: + rval = -EINVAL; + } + mutex_unlock(&asd->mutex); + return rval; +} + +int ici_isys_subdev_get_sel( + struct ici_isys_node* node, + struct ici_pad_selection* ps) +{ + struct ici_isys_subdev *asd = node->sd; + int rval = 0; + + if (WARN_ON(ps->pad.pad_idx >= asd->num_pads)) + return -EINVAL; + + mutex_lock(&asd->mutex); + switch (ps->sel_type) + { + case ICI_EXT_SEL_TYPE_COMPOSE: + ps->rect = asd->compose[ps->pad.pad_idx]; + break; + case ICI_EXT_SEL_TYPE_CROP: + ps->rect = asd->crop[ps->pad.pad_idx]; + break; + default: + rval = -EINVAL; + } + mutex_unlock(&asd->mutex); + return rval; +} + +void ici_isys_subdev_fmt_propagate( + struct ici_isys_subdev *asd, + unsigned pad, + struct ici_rect *r, + enum ici_isys_subdev_prop_tgt tgt, + struct ici_framefmt *ffmt) +{ + unsigned i; + struct ici_framefmt *ffmts[asd->num_pads]; + struct ici_rect *crops[asd->num_pads]; + struct ici_rect *compose[asd->num_pads]; + + if (WARN_ON(pad >= asd->num_pads)) + return; + + for (i = 0; i < asd->num_pads; i++) { + ffmts[i] = __ici_isys_subdev_get_ffmt(asd, pad); + crops[i] = &asd->crop[i]; + compose[i] = &asd->compose[i]; + } + + switch (tgt) { + case ICI_ISYS_SUBDEV_PROP_TGT_SINK_FMT: + crops[pad]->left = crops[pad]->top = 0; + crops[pad]->width = ffmt->width; + crops[pad]->height = ffmt->height; + ici_isys_subdev_fmt_propagate(asd, pad, + crops[pad], tgt + 1, + ffmt); + return; + case ICI_ISYS_SUBDEV_PROP_TGT_SINK_CROP: + if (WARN_ON(asd->pads[pad].flags & ICI_PAD_FLAGS_SOURCE)) + return; + compose[pad]->left = compose[pad]->top = 0; + compose[pad]->width = r->width; + compose[pad]->height = r->height; + ici_isys_subdev_fmt_propagate(asd, pad, + compose[pad], tgt + 1, + ffmt); + return; + case ICI_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE: + if (!(asd->pads[pad].flags & ICI_PAD_FLAGS_SINK)) + return; + + for (i = 1; i < asd->num_pads; i++) { + if (!(asd->pads[i].flags & ICI_PAD_FLAGS_SOURCE)) + continue; + + compose[i]->left = compose[i]->top = 0; + compose[i]->width = r->width; + compose[i]->height = r->height; + ici_isys_subdev_fmt_propagate(asd, i, + compose[i], + tgt + 1, + ffmt); + } + return; + case ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE: + if (WARN_ON(asd->pads[pad].flags & ICI_PAD_FLAGS_SINK)) + return; + + crops[pad]->left = crops[pad]->top = 0; + crops[pad]->width = r->width; + crops[pad]->height = r->height; + ici_isys_subdev_fmt_propagate(asd, pad, + crops[pad], tgt + 1, + ffmt); + return; + case ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP:{ + struct ici_framefmt fmt = { + .width = r->width, + .height = r->height, + /* + * Either use the code from sink pad + * or the current one. + */ + .pixelformat = (ffmt ? ffmt->pixelformat : + ffmts[pad]->pixelformat), + }; + + asd->set_ffmt_internal(asd, pad, &fmt); + return; + } + } +} + +int ici_isys_subdev_init(struct ici_isys_subdev *asd, + const char* name, + unsigned int num_pads, + unsigned int index) +{ + int res = 0; + + mutex_init(&asd->mutex); + asd->num_pads = num_pads; + asd->pads = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->pads), GFP_KERNEL); + + asd->ffmt = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->ffmt), GFP_KERNEL); + + asd->crop = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->crop), GFP_KERNEL); + + asd->compose = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->compose), GFP_KERNEL); + + asd->valid_tgts = devm_kcalloc(&asd->isys->adev->dev, num_pads, + sizeof(*asd->valid_tgts), + GFP_KERNEL); + + asd->supported_code_counts = devm_kcalloc(&asd->isys->adev->dev, + num_pads, sizeof(*asd->supported_code_counts), + GFP_KERNEL); + + if (!asd->pads || !asd->ffmt || !asd->crop || !asd->compose || + !asd->valid_tgts || !asd->supported_code_counts) { + res = -ENOMEM; + goto cleanup_allocs; + } + + asd->isl_mode = ICI_ISL_OFF; + asd->be_mode = ICI_BE_RAW; + asd->source = -1; + asd->index = index; + + asd->node.parent = &asd->isys->pipeline_dev; + asd->node.sd = asd; + asd->node.external = false; + + res = ici_isys_pipeline_node_init(asd->isys, + &asd->node, name, asd->num_pads, asd->pads); + if (res) + goto cleanup_allocs; + + asd->node.node_set_pad_ffmt = + ici_isys_subdev_set_ffmt; + asd->node.node_get_pad_ffmt = + ici_isys_subdev_get_ffmt; + asd->node.node_get_pad_supported_format = + ici_isys_subdev_get_supported_format; + asd->node.node_set_pad_sel = + ici_isys_subdev_set_sel; + asd->node.node_get_pad_sel = + ici_isys_subdev_get_sel; + + return 0; + +cleanup_allocs: + if (asd->valid_tgts) + devm_kfree(&asd->isys->adev->dev, asd->valid_tgts); + if (asd->compose) + devm_kfree(&asd->isys->adev->dev, asd->compose); + if (asd->crop) + devm_kfree(&asd->isys->adev->dev, asd->crop); + if (asd->ffmt) + devm_kfree(&asd->isys->adev->dev, asd->ffmt); + if (asd->pads) + devm_kfree(&asd->isys->adev->dev, asd->pads); + mutex_destroy(&asd->mutex); + return res; +} + +void ici_isys_subdev_cleanup( + struct ici_isys_subdev *asd) +{ + list_del(&asd->node.node_entry); + + if (asd->valid_tgts) + devm_kfree(&asd->isys->adev->dev, asd->valid_tgts); + if (asd->compose) + devm_kfree(&asd->isys->adev->dev, asd->compose); + if (asd->crop) + devm_kfree(&asd->isys->adev->dev, asd->crop); + if (asd->ffmt) + devm_kfree(&asd->isys->adev->dev, asd->ffmt); + if (asd->pads) + devm_kfree(&asd->isys->adev->dev, asd->pads); + mutex_destroy(&asd->mutex); +} + +#endif /*ICI_ENABLED*/ diff --git a/drivers/media/pci/intel/ici/ici-isys-subdev.h b/drivers/media/pci/intel/ici/ici-isys-subdev.h new file mode 100644 index 000000000000..e783fa104131 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-subdev.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_SUBDEV_H +#define ICI_ISYS_SUBDEV_H + +#include +#include "ici-isys-pipeline.h" + +struct node_subdev_format; + +#define ICI_ISYS_MIPI_CSI2_TYPE_NULL 0x10 +#define ICI_ISYS_MIPI_CSI2_TYPE_BLANKING 0x11 +#define ICI_ISYS_MIPI_CSI2_TYPE_EMBEDDED8 0x12 +#define ICI_ISYS_MIPI_CSI2_TYPE_YUV422_8 0x1e +#define ICI_ISYS_MIPI_CSI2_TYPE_RGB565 0x22 +#define ICI_ISYS_MIPI_CSI2_TYPE_RGB888 0x24 +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW6 0x28 +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW7 0x29 +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW8 0x2a +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW10 0x2b +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW12 0x2c +#define ICI_ISYS_MIPI_CSI2_TYPE_RAW14 0x2d +#define ICI_ISYS_MIPI_CSI2_TYPE_USER_DEF(i) (0x30 + (i)-1) /* 1-8 */ + +enum ici_be_mode { + ICI_BE_RAW = 0, + ICI_BE_SOC +}; + +enum ici_isl_mode { + ICI_ISL_OFF = 0, /* IA_CSS_ISYS_USE_NO_ISL_NO_ISA */ + ICI_ISL_CSI2_BE, /* IA_CSS_ISYS_USE_SINGLE_DUAL_ISL */ + ICI_ISL_ISA /* IA_CSS_ISYS_USE_SINGLE_ISA */ +}; + +enum ici_isys_subdev_pixelorder { + ICI_ISYS_SUBDEV_PIXELORDER_BGGR = 0, + ICI_ISYS_SUBDEV_PIXELORDER_GBRG, + ICI_ISYS_SUBDEV_PIXELORDER_GRBG, + ICI_ISYS_SUBDEV_PIXELORDER_RGGB, +}; + +enum ici_isys_subdev_prop_tgt { + ICI_ISYS_SUBDEV_PROP_TGT_SINK_FMT, + ICI_ISYS_SUBDEV_PROP_TGT_SINK_CROP, + ICI_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE, + ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE, + ICI_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP, +}; + +#define ICI_ISYS_SUBDEV_PROP_TGT_NR_OF \ + (INTEL_IPU4_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP + 1) + +struct ici_isys_subdev { + struct ici_isys_node node; + /* Serialise access to any other field in the struct */ + struct mutex mutex; + struct ici_isys *isys; + unsigned const *const *supported_codes; + unsigned* supported_code_counts; + unsigned int num_pads; + struct node_pad *pads; + struct ici_framefmt *ffmt; + struct ici_rect *crop; + struct ici_rect *compose; + struct { + bool crop; + bool compose; + } *valid_tgts; + enum ici_isl_mode isl_mode; + enum ici_be_mode be_mode; + int source; /* SSI stream source; -1 if unset */ + unsigned int index; /* index for sd array in csi2 */ + void (*set_ffmt_internal)( + struct ici_isys_subdev *asd, + unsigned pad, + struct ici_framefmt *format); +}; + +unsigned int ici_isys_format_code_to_bpp(u32 code); +unsigned int ici_isys_format_code_to_mipi(u32 code); +enum ici_isys_subdev_pixelorder +ici_isys_subdev_get_pixelorder(u32 code); +unsigned int ici_isys_get_compression_scheme(u32 code); +u32 ici_isys_subdev_code_to_uncompressed(u32 sink_code); + +struct ici_framefmt* __ici_isys_subdev_get_ffmt( + struct ici_isys_subdev *asd, + unsigned pad); +void ici_isys_subdev_fmt_propagate( + struct ici_isys_subdev *asd, + unsigned pad, + struct ici_rect *r, + enum ici_isys_subdev_prop_tgt + tgt, + struct ici_framefmt *ffmt); + +int ici_isys_subdev_init(struct ici_isys_subdev *asd, + const char* name, + unsigned int num_pads, + unsigned int index); +void ici_isys_subdev_cleanup( + struct ici_isys_subdev *asd); + +#define ici_node_to_subdev(__node) \ + container_of(__node, struct ici_isys_subdev, node) +#endif /* ICI_ISYS_SUBDEV_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys-tpg.c b/drivers/media/pci/intel/ici/ici-isys-tpg.c new file mode 100644 index 000000000000..250ed01a0cd6 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-tpg.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "./ici/ici-isys.h" +#ifdef ICI_ENABLED +#include +#include "./ici/ici-isys-subdev.h" +#include "./ici/ici-isys-stream.h" +#include "./ici/ici-isys-tpg.h" +#include "ipu-isys-tpg.h" +#include "isysapi/interface/ia_css_isysapi_fw_types.h" + +#define MIPI_GEN_PPC 4 + +#define ici_asd_to_tpg(__asd) \ + container_of(__asd, struct ici_isys_tpg, asd) + +static const uint32_t ici_tpg_supported_codes_pad[] = { + ICI_FORMAT_SBGGR8, + ICI_FORMAT_SGBRG8, + ICI_FORMAT_SGRBG8, + ICI_FORMAT_SRGGB8, + ICI_FORMAT_SBGGR10, + ICI_FORMAT_SGBRG10, + ICI_FORMAT_SGRBG10, + ICI_FORMAT_SRGGB10, + 0, +}; + +static const uint32_t *ici_tpg_supported_codes[] = { + ici_tpg_supported_codes_pad, +}; + +static int set_stream( + struct ici_isys_node *node, + void* ip, + int enable) +{ + struct ici_isys_subdev *asd = node->sd; + struct ici_isys_tpg *tpg = + to_ici_isys_tpg(asd); + unsigned int bpp = + ici_isys_format_code_to_bpp(tpg->asd. + ffmt[TPG_PAD_SOURCE]. + pixelformat); + /* + * In B0 MIPI_GEN block is CSI2 FB. Need to enable/disable TPG selection + * register to control the TPG streaming. + */ + writel(enable ? 1 : 0, tpg->sel); + + if (!enable) { + writel(0, tpg->base + MIPI_GEN_REG_COM_ENABLE); + return 0; + } + + writel(MIPI_GEN_COM_DTYPE_RAW(bpp), + tpg->base + MIPI_GEN_REG_COM_DTYPE); + writel(ici_isys_format_code_to_mipi + (tpg->asd.ffmt[TPG_PAD_SOURCE].pixelformat), + tpg->base + MIPI_GEN_REG_COM_VTYPE); + + writel(0, tpg->base + MIPI_GEN_REG_COM_VCHAN); + writel(DIV_ROUND_UP + (tpg->asd.ffmt[TPG_PAD_SOURCE].width * bpp, BITS_PER_BYTE), + tpg->base + MIPI_GEN_REG_COM_WCOUNT); + + writel(0, tpg->base + MIPI_GEN_REG_SYNG_NOF_FRAMES); + + writel(DIV_ROUND_UP(tpg->asd.ffmt[TPG_PAD_SOURCE].width, MIPI_GEN_PPC), + tpg->base + MIPI_GEN_REG_SYNG_NOF_PIXELS); + writel(tpg->asd.ffmt[TPG_PAD_SOURCE].height, + tpg->base + MIPI_GEN_REG_SYNG_NOF_LINES); + + writel(0, tpg->base + MIPI_GEN_REG_TPG_MODE); + + writel(-1, tpg->base + MIPI_GEN_REG_TPG_HCNT_MASK); + writel(-1, tpg->base + MIPI_GEN_REG_TPG_VCNT_MASK); + writel(-1, tpg->base + MIPI_GEN_REG_TPG_XYCNT_MASK); + writel(0, tpg->base + MIPI_GEN_REG_TPG_HCNT_DELTA); + writel(0, tpg->base + MIPI_GEN_REG_TPG_VCNT_DELTA); + + writel(2, tpg->base + MIPI_GEN_REG_COM_ENABLE); + return 0; +} + +static const char *const tpg_mode_items[] = { + "Ramp", + "Checkerboard", /* Does not work, disabled. */ + "Frame Based Colour", + NULL, +}; + +void ici_tpg_set_ffmt(struct ici_isys_subdev *asd, + unsigned pad, + struct ici_framefmt *ffmt) +{ + struct ici_framefmt *cur_ffmt = + __ici_isys_subdev_get_ffmt(asd, pad); + + ffmt->field = ICI_FIELD_NONE; + ffmt->colorspace = 0; + memset(ffmt->reserved, 0, sizeof(ffmt->reserved)); + if (cur_ffmt) { + *cur_ffmt = *ffmt; + dev_dbg(&asd->isys->adev->dev, "%s: TPG ici stream set format\n" + "width: %u, height: %u, pixelformat: %u, colorspace: %u field: %u\n", + __func__, + cur_ffmt->width, + cur_ffmt->height, + cur_ffmt->pixelformat, cur_ffmt->colorspace, cur_ffmt->field); + } +} + +static int ici_tpg_pipeline_validate( + struct node_pipeline *inp, + struct ici_isys_node *node) +{ + struct ici_isys_subdev* asd = node->sd; + struct ici_isys_tpg *tpg = + ici_asd_to_tpg(asd); + struct ici_isys_pipeline *ip = + ici_nodepipe_to_pipeline(inp); + + ip->asd_source = &tpg->asd; + ip->asd_source_pad_id = TPG_PAD_SOURCE; + return 0; +} + +int ici_isys_tpg_init(struct ici_isys_tpg *tpg, + struct ici_isys *isys, + void __iomem *base, void __iomem *sel, + unsigned int index) +{ + struct ici_pad_framefmt fmt = { + .pad.pad_idx = TPG_PAD_SOURCE, + .ffmt = { + .width = 4096, + .height = 3072, + }, + }; + + int rval; + char name[ICI_MAX_NODE_NAME]; + + dev_dbg(&isys->adev->dev, "ici_isys_tpg_init\n"); + + tpg->isys = isys; + tpg->base = base; + tpg->index = index; + tpg->sel = sel; + tpg->asd.isys = isys; + + snprintf(name, sizeof(name), + IPU_ISYS_ENTITY_PREFIX " TPG %u", index); + rval = ici_isys_subdev_init(&tpg->asd, + name, NR_OF_TPG_PADS, 0); + if (rval) + goto fail; + + tpg->asd.pads[TPG_PAD_SOURCE].flags = ICI_PAD_FLAGS_SOURCE; + + tpg->asd.source = IA_CSS_ISYS_STREAM_SRC_MIPIGEN_PORT0 + index; + tpg->asd.supported_codes = ici_tpg_supported_codes; + tpg->asd.set_ffmt_internal = ici_tpg_set_ffmt; + tpg->asd.node.node_set_streaming = set_stream; + tpg->asd.node.node_pipeline_validate = + ici_tpg_pipeline_validate; + tpg->asd.node.node_set_pad_ffmt(&tpg->asd.node, &fmt); + tpg->as.isys = isys; + tpg->as.try_fmt_vid_mplane = + ici_isys_video_try_fmt_vid_mplane_default; + tpg->as.prepare_firmware_stream_cfg = + ici_isys_prepare_firmware_stream_cfg_default; + tpg->as.pfmts = ici_isys_pfmts_packed; + tpg->as.packed = true; + tpg->as.buf_list.css_pin_type = IA_CSS_ISYS_PIN_TYPE_MIPI; + tpg->as.line_header_length = + INTEL_IPU4_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; + tpg->as.line_footer_length = + INTEL_IPU4_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; + + /*TODO:*/ + /* + * Buffer queue management call backs to be added. + */ + + rval = ici_isys_stream_init(&tpg->as, &tpg->asd, + &tpg->asd.node, TPG_PAD_SOURCE, + ICI_PAD_FLAGS_SINK); + if (rval) { + dev_err(&isys->adev->dev, "can't init stream node\n"); + goto fail; + } + + return 0; + +fail: + ici_isys_tpg_cleanup(tpg); + + return 1; +} +EXPORT_SYMBOL(ici_isys_tpg_init); + +void ici_isys_tpg_cleanup(struct ici_isys_tpg *tpg) +{ + ici_isys_subdev_cleanup(&tpg->asd); + ici_isys_stream_cleanup(&tpg->as); +} +EXPORT_SYMBOL(ici_isys_tpg_cleanup); + +#endif /*ICI_ENABLED*/ diff --git a/drivers/media/pci/intel/ici/ici-isys-tpg.h b/drivers/media/pci/intel/ici/ici-isys-tpg.h new file mode 100644 index 000000000000..5e6eeefc9e83 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys-tpg.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_TPG_H +#define ICI_ISYS_TPG_H + +#include "ici-isys-frame-buf.h" +#include "ici-isys-subdev.h" +#include "ici-isys-stream.h" + +struct intel_ipu4_isys_tpg_pdata; + +#define TPG_PAD_SOURCE 0 +#define NR_OF_TPG_PADS 1 + +/* + * struct ici_isys_tpg + * + * +*/ +struct ici_isys_tpg { + struct intel_ipu4_isys_tpg_pdata *pdata; + struct ici_isys *isys; + struct ici_isys_subdev asd; + struct ici_isys_stream as; + + void __iomem *base; + void __iomem *sel; + int streaming; + u32 receiver_errors; + unsigned int nlanes; + unsigned int index; + atomic_t sof_sequence; +}; + +#define to_ici_isys_tpg(sd) \ + container_of(sd, struct ici_isys_tpg, asd) + +int ici_isys_tpg_init(struct ici_isys_tpg *tpg, + struct ici_isys *isys, + void __iomem *base, void __iomem *sel, + unsigned int index); +void ici_isys_tpg_cleanup(struct ici_isys_tpg *tpg); + +#endif /* ICI_ISYS_TPG_H */ diff --git a/drivers/media/pci/intel/ici/ici-isys.c b/drivers/media/pci/intel/ici/ici-isys.c new file mode 100644 index 000000000000..466659baec24 --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys.c @@ -0,0 +1,1367 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ipu.h" +#include "ipu-bus.h" +#include "ipu-cpd.h" +#include "ipu-mmu.h" +#include "ipu-dma.h" +#include "ipu-platform-isys-csi2-reg.h" +#include "ipu-trace.h" +#include "ipu-buttress.h" +#include "isysapi/interface/ia_css_isysapi.h" +#include "./ici/ici-isys.h" +#include "./ici/ici-isys-csi2.h" +#include "./ici/ici-isys-pipeline-device.h" + +#ifdef ICI_ENABLED + +#define ISYS_PM_QOS_VALUE 300 + +#define INTEL_IPU4_ISYS_OUTPUT_PINS 11 +#define INTEL_IPU4_NUM_CAPTURE_DONE 2 + +/* Trace block definitions for isys */ +struct ipu_trace_block isys_trace_blocks[] = { + { + .offset = TRACE_REG_IS_TRACE_UNIT_BASE, + .type = IPU_TRACE_BLOCK_TUN, + }, + { + .offset = TRACE_REG_IS_SP_EVQ_BASE, + .type = IPU_TRACE_BLOCK_TM, + }, + { + .offset = TRACE_REG_IS_SP_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_IS_ISL_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_IS_MMU_GPC_BASE, + .type = IPU_TRACE_BLOCK_GPC, + }, + { + .offset = TRACE_REG_CSI2_TM_BASE, + .type = IPU_TRACE_CSI2, + }, + { + .offset = TRACE_REG_CSI2_3PH_TM_BASE, + .type = IPU_TRACE_CSI2_3PH, + }, + { + /* Note! this covers all 9 blocks */ + .offset = TRACE_REG_CSI2_SIG2SIO_GR_BASE(0), + .type = IPU_TRACE_SIG2CIOS, + }, + { + /* Note! this covers all 9 blocks */ + .offset = TRACE_REG_CSI2_PH3_SIG2SIO_GR_BASE(0), + .type = IPU_TRACE_SIG2CIOS, + }, + { + .offset = TRACE_REG_IS_GPREG_TRACE_TIMER_RST_N, + .type = IPU_TRACE_TIMER_RST, + }, + { + .type = IPU_TRACE_BLOCK_END, + } +}; + + +// Latest code structure doesnt do these functions. +// let it remain to gauge the impact and then remove. +#if 0 +static int isys_determine_legacy_csi_lane_configuration(struct ici_isys *isys) +{ + const struct csi_lane_cfg { + u32 reg_value; + int port_lanes[IPU_ISYS_MAX_CSI2_LEGACY_PORTS]; + } csi_lanes_to_cfg[] = { + { 0x0, { 4, 2, 0, 0 } }, /* no sensor defaults here */ + { 0x1, { 3, 2, 0, 0 } }, + { 0x2, { 2, 2, 0, 0 } }, + { 0x3, { 1, 2, 0, 0 } }, + { 0x4, { 4, 1, 0, 0 } }, + { 0x5, { 3, 1, 0, 0 } }, + { 0x6, { 2, 1, 0, 0 } }, + { 0x7, { 1, 1, 0, 0 } }, + { 0x8, { 4, 1, 0, 1 } }, + { 0x9, { 3, 1, 0, 1 } }, + { 0xa, { 2, 1, 0, 1 } }, + { 0xb, { 1, 1, 0, 1 } }, + { 0x10, { 2, 2, 2, 0 } }, + { 0x11, { 2, 2, 1, 0 } }, + { 0x18, { 2, 1, 2, 1 } }, + { 0x19, { 1, 1, 1, 1 } }, + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(csi_lanes_to_cfg); i++) { + for (j = 0; j < IPU_ISYS_MAX_CSI2_LEGACY_PORTS; j++) { + /* Port with no sensor can be handled as don't care */ + if (!isys->ici_csi2[j].nlanes) + continue; + if (csi_lanes_to_cfg[i].port_lanes[j] != + isys->ici_csi2[j].nlanes) + break; + } + + if (j < IPU_ISYS_MAX_CSI2_LEGACY_PORTS) + continue; + + isys->legacy_port_cfg = csi_lanes_to_cfg[i].reg_value; + dev_dbg(&isys->adev->dev, "Lane configuration value 0x%x\n,", + isys->legacy_port_cfg); + return 0; + } + dev_err(&isys->adev->dev, "Non supported CSI lane configuration\n,"); + return -EINVAL; +} + +static int isys_determine_csi_combo_lane_configuration(struct ici_isys *isys) +{ + const struct csi_lane_cfg { + u32 reg_value; + int port_lanes[IPU_ISYS_MAX_CSI2_COMBO_PORTS]; + } csi_lanes_to_cfg[] = { + { 0x1f, { 0, 0 } }, /* no sensor defaults here - disable all */ + { 0x10, { 4, 0 } }, + { 0x11, { 3, 0 } }, + { 0x12, { 2, 0 } }, + { 0x13, { 1, 0 } }, + { 0x14, { 3, 1 } }, + { 0x15, { 2, 1 } }, + { 0x16, { 1, 1 } }, + { 0x18, { 2, 2 } }, + { 0x19, { 1, 2 } }, + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(csi_lanes_to_cfg); i++) { + for (j = 0; j < IPU_ISYS_MAX_CSI2_COMBO_PORTS; j++) { + /* Port with no sensor can be handled as don't care */ + if (!isys->ici_csi2[j + IPU_ISYS_MAX_CSI2_LEGACY_PORTS].nlanes) + continue; + if (csi_lanes_to_cfg[i].port_lanes[j] != + isys->ici_csi2[j + IPU_ISYS_MAX_CSI2_LEGACY_PORTS].nlanes) + break; + } + + if (j < IPU_ISYS_MAX_CSI2_COMBO_PORTS) + continue; + + isys->combo_port_cfg = csi_lanes_to_cfg[i].reg_value; + dev_dbg(&isys->adev->dev, + "Combo port lane configuration value 0x%x\n", + isys->combo_port_cfg); + + return 0; + } + dev_err(&isys->adev->dev, + "Unsupported CSI2-combo lane configuration\n"); + return 0; +} + +#endif +struct isys_i2c_test { + u8 bus_nr; + u16 addr; + struct i2c_client *client; +}; + +static int isys_i2c_test(struct device *dev, void *priv) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct isys_i2c_test *test = priv; + + if (!client) + return 0; + + if (i2c_adapter_id(client->adapter) != test->bus_nr + || client->addr != test->addr) + return 0; + + test->client = client; + + return 0; +} + +static struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, + struct ipu_isys_subdev_info *sd_info) +{ + struct i2c_board_info *info = &sd_info->i2c.board_info; + struct isys_i2c_test test = { + .bus_nr = i2c_adapter_id(adapter), + .addr = info->addr, + }; + int rval; + + rval = i2c_for_each_dev(&test, isys_i2c_test); + if (rval || !test.client) + return NULL; + return test.client; +} + +static struct ici_ext_subdev *register_acpi_i2c_subdev( + struct ipu_isys_subdev_info *sd_info, struct i2c_client *client) +{ + struct i2c_board_info *info = &sd_info->i2c.board_info; + struct ici_ext_subdev *sd; + + request_module(I2C_MODULE_PREFIX "%s", info->type); + + /* ACPI overwrite with platform data */ + client->dev.platform_data = info->platform_data; + /* Change I2C client name to one in temporary platform data */ + strlcpy(client->name, info->type, sizeof(client->name)); + + if (device_reprobe(&client->dev)) + return NULL; + + if (!client->dev.driver) + return NULL; + + if (!try_module_get(client->dev.driver->owner)) + return NULL; + + sd = i2c_get_clientdata(client); + + module_put(client->dev.driver->owner); + + return sd; +} + +static int ext_device_setup_node(void* ipu_data, + struct ici_ext_subdev *sd, + const char* name) +{ + int rval; + struct ici_isys *isys = ipu_data; + sd->node.sd = sd; + sd->node.external = true; + rval = ici_isys_pipeline_node_init( + isys, &sd->node, name, sd->num_pads, sd->pads); + if (rval) + return rval; + sd->num_pads = sd->node.nr_pads; + return 0; +} + +static int isys_complete_ext_device_registration( + struct ici_isys *isys, + struct ici_ext_subdev *sd, + struct ipu_isys_csi2_config *csi2) +{ + int rval; + struct ici_ext_subdev_register sd_register = {0}; + unsigned int i; + + sd_register.ipu_data = isys; + sd_register.sd = sd; + sd_register.setup_node = ext_device_setup_node; + sd_register.create_link = node_pad_create_link; + rval = sd->do_register(&sd_register); + if (rval) { + dev_err(&isys->adev->dev, + "Failed to regsiter external subdev\n"); + return rval; + } + if (csi2) { + for (i = 0; i < NR_OF_CSI2_VC; i++) { + rval = node_pad_create_link(&sd->node, sd->src_pad, + &isys->ici_csi2[csi2->port].asd[i].node, + CSI2_ICI_PAD_SINK, 0); + if (rval) { + dev_warn(&isys->adev->dev, + "can't create link from external node\n"); + return rval; + } + + isys->ici_csi2[csi2->port].nlanes = csi2->nlanes; + isys->ici_csi2[csi2->port].ext_sd = sd; + } + } + return 0; +} + +static int isys_register_ext_subdev(struct ici_isys *isys, + struct ipu_isys_subdev_info *sd_info, + bool acpi_only) +{ + struct i2c_adapter *adapter = + i2c_get_adapter(sd_info->i2c.i2c_adapter_id); + struct ici_ext_subdev *sd; + struct i2c_client *client; + int rval; + + dev_info(&isys->adev->dev, + "creating new i2c subdev for %s (address %2.2x, bus %d)", + sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, + sd_info->i2c.i2c_adapter_id); + + if (!adapter) { + dev_warn(&isys->adev->dev, "can't find adapter\n"); + return -ENOENT; + } + if (sd_info->csi2) { + dev_info(&isys->adev->dev, "sensor device on CSI port: %d\n", + sd_info->csi2->port); + if (sd_info->csi2->port >= IPU_ISYS_MAX_CSI2_PORTS || + !isys->ici_csi2[sd_info->csi2->port].isys) { + dev_warn(&isys->adev->dev, "invalid csi2 port %u\n", + sd_info->csi2->port); + rval = -EINVAL; + goto skip_put_adapter; + } + } else { + dev_info(&isys->adev->dev, "non camera subdevice\n"); + } + + client = isys_find_i2c_subdev(adapter, sd_info); + + if (acpi_only) { + if (!client) { + dev_dbg(&isys->adev->dev, + "Matching ACPI device not found - postpone\n"); + rval = 0; + goto skip_put_adapter; + } + rval = 0; + goto skip_put_adapter; + if (!sd_info->acpiname) { + dev_dbg(&isys->adev->dev, + "No name in platform data\n"); + rval = 0; + goto skip_put_adapter; + } + if (strcmp(dev_name(&client->dev), sd_info->acpiname)) { + dev_dbg(&isys->adev->dev, "Names don't match: %s != %s", + dev_name(&client->dev), sd_info->acpiname); + rval = 0; + goto skip_put_adapter; + } + /* Acpi match found. Continue to reprobe */ + } else if (client) { + dev_dbg(&isys->adev->dev, "Device exists\n"); + rval = 0; + goto skip_put_adapter; + } + else if (sd_info->acpiname) { + dev_dbg(&isys->adev->dev, "ACPI name don't match: %s\n", + sd_info->acpiname); + rval = 0; + goto skip_put_adapter; + } + if (!client) { + dev_info(&isys->adev->dev, + "i2c device not found in ACPI table\n"); + client = i2c_new_device(adapter, + &sd_info->i2c.board_info); + sd = i2c_get_clientdata(client); + } else { + dev_info(&isys->adev->dev, "i2c device found in ACPI table\n"); + sd = register_acpi_i2c_subdev(sd_info, client); + } + + if (!sd) { + dev_warn(&isys->adev->dev, "can't create new i2c subdev\n"); + rval = -EINVAL; + goto skip_put_adapter; + } + + return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); + +skip_put_adapter: + i2c_put_adapter(adapter); + + return rval; +} + +static int isys_acpi_add_device(struct device *dev, void *priv, + struct ipu_isys_csi2_config *csi2, + bool reprobe) +{ + struct ici_isys *isys = priv; + struct i2c_client *client = i2c_verify_client(dev); + struct ici_ext_subdev *sd; + + if (!client) + return -ENODEV; + + if (reprobe) + if (device_reprobe(&client->dev)) + return -ENODEV; + + if (!client->dev.driver) + return -ENODEV; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->dev.driver->owner)) + return -ENODEV; + + sd = i2c_get_clientdata(client); + module_put(client->dev.driver->owner); + if (!sd) { + dev_warn(&isys->adev->dev, "can't create new i2c subdev\n"); + return -ENODEV; + } + + return isys_complete_ext_device_registration(isys, sd, csi2); +} + +static void isys_register_ext_subdevs(struct ici_isys *isys) +{ + struct ipu_isys_subdev_pdata *spdata = isys->pdata->spdata; + struct ipu_isys_subdev_info **sd_info; + + if (spdata) { + /* Scan spdata first to possibly override ACPI data */ + /* ACPI created devices */ + for (sd_info = spdata->subdevs; *sd_info; sd_info++) + isys_register_ext_subdev(isys, *sd_info, true); + + /* Scan non-acpi devices */ + for (sd_info = spdata->subdevs; *sd_info; sd_info++) + isys_register_ext_subdev(isys, *sd_info, false); + } else { + dev_info(&isys->adev->dev, "no subdevice info provided\n"); + } + + /* Handle real ACPI stuff */ + request_module("ipu4-acpi"); + ipu_get_acpi_devices(isys, &isys->adev->dev, + isys_acpi_add_device); +} + +static void isys_unregister_subdevices(struct ici_isys *isys) +{ + const struct ipu_isys_internal_tpg_pdata *tpg = + &isys->pdata->ipdata->tpg; + const struct ipu_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + unsigned int i; + + for (i = 0; i < NR_OF_CSI2_BE_SOC_STREAMS; i++) { + ici_isys_csi2_be_cleanup(&isys->ici_csi2_be[i]); + } + + for (i = 0; i < tpg->ntpgs; i++) { + ici_isys_tpg_cleanup(&isys->ici_tpg[i]); + } + + for (i = 0; i < csi2->nports; i++) { + ici_isys_csi2_cleanup(&isys->ici_csi2[i]); + } +} + +static int isys_register_subdevices(struct ici_isys *isys) +{ + const struct ipu_isys_internal_tpg_pdata *tpg = + &isys->pdata->ipdata->tpg; + const struct ipu_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + + unsigned int i, j, k; + int rval; + + BUG_ON(csi2->nports > IPU_ISYS_MAX_CSI2_PORTS); + BUG_ON(tpg->ntpgs > 2); + + for (i = 0; i < csi2->nports; i++) { + rval = ici_isys_csi2_init( + &isys->ici_csi2[i], isys, + isys->pdata->base + csi2->offsets[i], i); + if (rval) + goto fail; + + isys->isr_csi2_bits |= + IPU_ISYS_UNISPART_IRQ_CSI2(i); + } + + for (i = 0; i < tpg->ntpgs; i++) { + rval = ici_isys_tpg_init(&isys->ici_tpg[i], isys, + isys->pdata->base + tpg->offsets[i], + isys->pdata->base + tpg->sels[i], i); + if(rval) + goto fail; + } + + for (i = 0; i < NR_OF_CSI2_BE_SOC_STREAMS; i++) { + rval = ici_isys_csi2_be_init(&isys->ici_csi2_be[i], + isys, i); + if (rval) { + goto fail; + } + } + + for (i = 0; i < csi2->nports; i++) { + for (j = 0; j < NR_OF_CSI2_VC; j++ ) { + rval = node_pad_create_link( + &isys->ici_csi2[i].asd[j].node, CSI2_ICI_PAD_SOURCE, + &isys->ici_csi2_be[ICI_BE_RAW].asd.node, + CSI2_BE_ICI_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between csi2 and csi2_be\n"); + goto fail; + } + + for (k = 1; k < NR_OF_CSI2_BE_SOC_STREAMS; k++ ) { + rval = node_pad_create_link( + &isys->ici_csi2[i].asd[j].node, CSI2_ICI_PAD_SOURCE, + &isys->ici_csi2_be[k].asd.node, + CSI2_BE_ICI_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between csi2 and csi2_be soc\n"); + goto fail; + } + } + } + } + + for (i = 0; i < tpg->ntpgs; i++) { + rval = node_pad_create_link( + &isys->ici_tpg[i].asd.node, TPG_PAD_SOURCE, + &isys->ici_csi2_be[ICI_BE_RAW].asd.node, + CSI2_BE_ICI_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between tpg and csi2_be\n"); + goto fail; + } + + for (j = 1; j < NR_OF_CSI2_BE_SOC_STREAMS; j++) { + rval = node_pad_create_link( + &isys->ici_tpg[i].asd.node, TPG_PAD_SOURCE, + &isys->ici_csi2_be[j].asd.node, + CSI2_BE_ICI_PAD_SINK, 0); + if (rval) { + dev_info(&isys->adev->dev, + "can't create link between tpg and csi2_be soc\n"); + goto fail; + } + } + } + + return 0; + +fail: + isys_unregister_subdevices(isys); + return rval; +} + +static int isys_register_devices(struct ici_isys *isys) +{ + int rval; + +/* Pipeline device registration */ + DEBUGK("Pipeline device registering...\n"); + rval = pipeline_device_register(&isys->pipeline_dev, isys); + if (rval < 0) { + dev_info(&isys->pipeline_dev.dev, "can't register pipeline device\n"); + return rval; + } + + rval = isys_register_subdevices(isys); + if (rval) + goto out_pipeline_device_unregister; + + isys_register_ext_subdevs(isys); + +// Latest code structure doesnt do these functions. +// let it remain to gaugae impact and then remove. +#if 0 + rval = isys_determine_legacy_csi_lane_configuration(isys); + if (rval) + goto out_isys_unregister_subdevices; + + rval = isys_determine_csi_combo_lane_configuration(isys); + if (rval) + goto out_isys_unregister_subdevices; + +#ifndef CONFIG_PM + ipu_buttress_csi_port_config(isys->adev->isp, + isys->legacy_port_cfg, + isys->combo_port_cfg); +#endif +#endif + return 0; + +#if 0 +out_isys_unregister_subdevices: + isys_unregister_subdevices(isys); +#endif +out_pipeline_device_unregister: + pipeline_device_unregister(&isys->pipeline_dev); + + return rval; +} + +static void isys_unregister_devices(struct ici_isys *isys) +{ + pipeline_device_unregister(&isys->pipeline_dev); + DEBUGK("Pipeline device unregistered\n"); + isys_unregister_subdevices(isys); +} + +static void isys_setup_hw(struct ici_isys *isys) +{ + void __iomem *base = isys->pdata->base; + u32 irqs; + unsigned int i; + + /* Enable irqs for all MIPI busses */ + irqs = IPU_ISYS_UNISPART_IRQ_CSI2(0) | + IPU_ISYS_UNISPART_IRQ_CSI2(1) | + IPU_ISYS_UNISPART_IRQ_CSI2(2) | + IPU_ISYS_UNISPART_IRQ_CSI2(3) | + IPU_ISYS_UNISPART_IRQ_CSI2(4) | + IPU_ISYS_UNISPART_IRQ_CSI2(5); + + irqs |= IPU_ISYS_UNISPART_IRQ_SW; + + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_EDGE); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_CLEAR); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_MASK); + writel(irqs, base + IPU_REG_ISYS_UNISPART_IRQ_ENABLE); + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_MUX_REG); + + /* Write CDC FIFO threshold values for isys */ + for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++) + writel(isys->pdata->ipdata->hw_variant.cdc_fifo_threshold[i], + base + IPU_REG_ISYS_CDC_THRESHOLD(i)); +} + +#ifdef CONFIG_PM +static int isys_runtime_pm_resume(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ipu_device *isp = adev->isp; + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + unsigned long flags; + int ret; + + if (!isys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + + ipu_trace_restore(dev); + + pm_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); +#if 0 + ipu_buttress_csi_port_config(isp, + isys->legacy_port_cfg, + isys->combo_port_cfg); +#endif + ret = ipu_buttress_start_tsc_sync(isp); + if (ret) + return ret; + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 1; + spin_unlock_irqrestore(&isys->power_lock, flags); + + isys_setup_hw(isys); + + return 0; +} + +static int isys_runtime_pm_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + unsigned long flags; + + if (!isys) { + WARN(1, "%s called before probing. skipping.\n", __func__); + return 0; + } + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 0; + spin_unlock_irqrestore(&isys->power_lock, flags); + + ipu_trace_stop(dev); + mutex_lock(&isys->mutex); + isys->reset_needed = false; + mutex_unlock(&isys->mutex); + + pm_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + + return 0; +} + +static int isys_suspend(struct device *dev) +{ + struct ipu_bus_device *adev = to_ipu_bus_device(dev); + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + + /* If stream is open, refuse to suspend */ + if (isys->stream_opened) + return -EBUSY; + + return 0; +} + +static int isys_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops isys_pm_ops = { + .runtime_suspend = isys_runtime_pm_suspend, + .runtime_resume = isys_runtime_pm_resume, + .suspend = isys_suspend, + .resume = isys_resume, +}; +#define ISYS_PM_OPS (&isys_pm_ops) +#else +#define ISYS_PM_OPS NULL +#endif + +static void isys_remove(struct ipu_bus_device *adev) +{ + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + struct ipu_device *isp = adev->isp; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + + dev_info(&adev->dev, "removed\n"); + debugfs_remove_recursive(isys->debugfsdir); + + ipu_trace_uninit(&adev->dev); + isys_unregister_devices(isys); + pm_qos_remove_request(&isys->pm_qos); + + if (!isp->secure_mode) { + ipu_cpd_free_pkg_dir(adev, isys->pkg_dir, + isys->pkg_dir_dma_addr, + isys->pkg_dir_size); + ipu_buttress_unmap_fw_image(adev, &isys->fw_sgt); + release_firmware(isys->fw); + } + + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + dma_free_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; + dma_free_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, attrs); +#endif +} + +static int intel_ipu4_isys_icache_prefetch_get(void *data, u64 *val) +{ + struct ici_isys *isys = data; + + *val = isys->icache_prefetch; + return 0; +} + +static int intel_ipu4_isys_icache_prefetch_set(void *data, u64 val) +{ + struct ici_isys *isys = data; + + if (val != !!val) + return -EINVAL; + + isys->icache_prefetch = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(isys_icache_prefetch_fops, + intel_ipu4_isys_icache_prefetch_get, + intel_ipu4_isys_icache_prefetch_set, + "%llu\n"); + +static int intel_ipu4_isys_init_debugfs(struct ici_isys *isys) +{ + struct dentry *file; + struct dentry *dir; + + dir = debugfs_create_dir("isys", isys->adev->isp->ipu_dir); + if (IS_ERR(dir)) + return -ENOMEM; + + file = debugfs_create_file("icache_prefetch", S_IRUSR | S_IWUSR, + dir, isys, + &isys_icache_prefetch_fops); + if (IS_ERR(file)) + goto err; + + isys->debugfsdir = dir; + + return 0; +err: + debugfs_remove_recursive(dir); + return -ENOMEM; +} + +static int alloc_fw_msg_buffers(struct ici_isys *isys, int amount) +{ + dma_addr_t dma_addr; + struct isys_fw_msgs *addr; + unsigned int i; + unsigned long flags; + + for (i = 0; i < amount; i++) { + addr = dma_alloc_attrs(&isys->adev->dev, + sizeof(struct isys_fw_msgs), + &dma_addr, GFP_KERNEL, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + if (!addr) + break; + addr->dma_addr = dma_addr; + + spin_lock_irqsave(&isys->listlock, flags); + list_add(&addr->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); + } + if (i == amount) + return 0; + spin_lock_irqsave(&isys->listlock, flags); + while (!list_empty(&isys->framebuflist)) { + addr = list_first_entry(&isys->framebuflist, + struct isys_fw_msgs, head); + list_del(&addr->head); + spin_unlock_irqrestore(&isys->listlock, flags); + dma_free_attrs(&isys->adev->dev, + sizeof(struct isys_fw_msgs), + addr, addr->dma_addr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + NULL +#else + 0 +#endif + ); + spin_lock_irqsave(&isys->listlock, flags); + } + spin_unlock_irqrestore(&isys->listlock, flags); + return -ENOMEM; +} + +static int isys_probe(struct ipu_bus_device *adev) +{ + struct ipu_mmu *mmu = dev_get_drvdata(adev->iommu); + struct ici_isys *isys; + struct ipu_device *isp = adev->isp; + const struct firmware *uninitialized_var(fw); + int rval = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + struct dma_attrs attrs; +#else + unsigned long attrs; +#endif + + trace_printk("B|%d|TMWK\n", current->pid); + + /* Has the domain been attached? */ + if (!mmu || !isp->pkg_dir_dma_addr) { + trace_printk("E|TMWK\n"); + return -EPROBE_DEFER; + } + + isys = devm_kzalloc(&adev->dev, sizeof(*isys), GFP_KERNEL); + if (!isys) { + trace_printk("E|TMWK\n"); + return -ENOMEM; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + init_dma_attrs(&attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + isys->short_packet_trace_buffer = dma_alloc_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + &isys->short_packet_trace_buffer_dma_addr, GFP_KERNEL, &attrs); +#else + attrs = DMA_ATTR_NON_CONSISTENT; + isys->short_packet_trace_buffer = dma_alloc_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + &isys->short_packet_trace_buffer_dma_addr, GFP_KERNEL, attrs); +#endif + if (!isys->short_packet_trace_buffer) + return -ENOMEM; + + isys->adev = adev; + isys->pdata = adev->pdata; + + INIT_LIST_HEAD(&isys->requests); + + spin_lock_init(&isys->lock); + spin_lock_init(&isys->power_lock); + isys->power = 0; + + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); + mutex_init(&isys->lib_mutex); + + spin_lock_init(&isys->listlock); + INIT_LIST_HEAD(&isys->framebuflist); + INIT_LIST_HEAD(&isys->framebuflist_fw); + + dev_info(&adev->dev, "isys probe %p %p\n", adev, &adev->dev); + ipu_bus_set_drvdata(adev, isys); + + isys->line_align = IPU_ISYS_2600_MEM_LINE_ALIGN; + isys->icache_prefetch = is_ipu_hw_bxtp_e0(isp); + +#ifndef CONFIG_PM + isys_setup_hw(isys); +#endif + + if (!isp->secure_mode) { + fw = isp->cpd_fw; + + rval = ipu_buttress_map_fw_image( + adev, fw, &isys->fw_sgt); + if (rval) + goto release_firmware; + + isys->pkg_dir = ipu_cpd_create_pkg_dir( + adev, isp->cpd_fw->data, + sg_dma_address(isys->fw_sgt.sgl), + &isys->pkg_dir_dma_addr, + &isys->pkg_dir_size); + if (isys->pkg_dir == NULL) { + rval = -ENOMEM; + goto remove_shared_buffer; + } + } + + /* Debug fs failure is not fatal. */ + intel_ipu4_isys_init_debugfs(isys); + + ipu_trace_init(adev->isp, isys->pdata->base, &adev->dev, + isys_trace_blocks); + + pm_qos_add_request(&isys->pm_qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + alloc_fw_msg_buffers(isys, 20); + + pm_runtime_allow(&adev->dev); + pm_runtime_enable(&adev->dev); + + rval = isys_register_devices(isys); + if (rval) + goto out_remove_pkg_dir_shared_buffer; + + trace_printk("E|TMWK\n"); + return 0; + +out_remove_pkg_dir_shared_buffer: + if (!isp->secure_mode) + ipu_cpd_free_pkg_dir(adev, isys->pkg_dir, + isys->pkg_dir_dma_addr, + isys->pkg_dir_size); +remove_shared_buffer: + if (!isp->secure_mode) + ipu_buttress_unmap_fw_image( + adev, &isys->fw_sgt); +release_firmware: + if (!isp->secure_mode) + release_firmware(isys->fw); + ipu_trace_uninit(&adev->dev); + + trace_printk("E|TMWK\n"); + + mutex_destroy(&isys->mutex); + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->lib_mutex); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + dma_free_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, &attrs); +#else + dma_free_attrs(&adev->dev, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + isys->short_packet_trace_buffer, + isys->short_packet_trace_buffer_dma_addr, attrs); +#endif + return rval; +} + +struct fwmsg { + int type; + char *msg; + bool valid_ts; +}; + +static const struct fwmsg fw_msg[] = { + { IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + "STREAM_START_AND_CAPTURE_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + "STREAM_START_AND_CAPTURE_DONE", 1 }, + { IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1 }, + { IA_CSS_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1 }, + { IA_CSS_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1 }, + { -1, "UNKNOWN MESSAGE", 0 }, +}; + +static int resp_type_to_index(int type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fw_msg); i++) + if (fw_msg[i].type == type) + return i; + + return i - 1; +} + + +static u64 extract_time_from_short_packet_msg( + struct ici_isys_csi2_monitor_message *msg) + +{ + u64 time_h = msg->timestamp_h << 14; + u64 time_l = msg->timestamp_l; + u64 time_h_ovl = time_h & 0xc000; + u64 time_h_h = time_h & (~0xffff); + + /* Fix possible roll overs. */ + if (time_h_ovl >= (time_l & 0xc000)) + return time_h_h | time_l; + else + return (time_h_h - 0x10000) | time_l; +} +static u64 tunit_time_to_us(struct ici_isys *isys, u64 time) +{ + struct ipu_bus_device *adev = + to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 1000000; + return time / isys_clk; +} + +static u64 tsc_time_to_tunit_time(struct ici_isys *isys, + u64 tsc_base, u64 tunit_base, u64 tsc_time) +{ + struct ipu_bus_device *adev = + to_ipu_bus_device(isys->adev->iommu); + u64 isys_clk = IS_FREQ_SOURCE / adev->ctrl->divisor / 100000; + u64 tsc_clk = IPU_BUTTRESS_TSC_CLK / 100000; + + return (tsc_time - tsc_base) * isys_clk / tsc_clk + tunit_base; +} + +static int isys_isr_one_ici(struct ipu_bus_device *adev) +{ + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + struct ia_css_isys_resp_info resp; + struct ici_isys_pipeline *pipe; + u64 ts; + int rval; + unsigned int i; + + if (!isys->fwcom) + return 0; + + rval = ipu_lib_call_notrace_unlocked(stream_handle_response, + isys, &resp); + if (rval < 0) + return rval; + + ts = (u64)resp.timestamp[1] << 32 | resp.timestamp[0]; + + + if (resp.error == IA_CSS_ISYS_ERROR_STREAM_IN_SUSPENSION) + /* Suspension is kind of special case: not enough buffers */ + dev_dbg(&adev->dev, + "hostlib: error resp %02d %s, stream %u, error SUSPENSION, details %d, timestamp 0x%16.16llx, pin %d\n", + resp.type, + fw_msg[resp_type_to_index(resp.type)].msg, + resp.stream_handle, + resp.error_details, + fw_msg[resp_type_to_index(resp.type)].valid_ts ? + ts : 0, resp.pin_id); + else if (resp.error) + dev_dbg(&adev->dev, + "hostlib: error resp %02d %s, stream %u, error %d, details %d, timestamp 0x%16.16llx, pin %d\n", + resp.type, + fw_msg[resp_type_to_index(resp.type)].msg, + resp.stream_handle, + resp.error, resp.error_details, + fw_msg[resp_type_to_index(resp.type)].valid_ts ? + ts : 0, resp.pin_id); + else + dev_dbg(&adev->dev, + "hostlib: resp %02d %s, stream %u, timestamp 0x%16.16llx, pin %d\n", + resp.type, + fw_msg[resp_type_to_index(resp.type)].msg, + resp.stream_handle, + fw_msg[resp_type_to_index(resp.type)].valid_ts ? + ts : 0, resp.pin_id); + + if (resp.stream_handle >= INTEL_IPU4_ISYS_MAX_STREAMS) { + dev_err(&adev->dev, "bad stream handle %u\n", + resp.stream_handle); + return 0; + } + + pipe = isys->ici_pipes[resp.stream_handle]; + if (!pipe) { + dev_err(&adev->dev, "no pipeline for stream %u\n", + resp.stream_handle); + return 0; + } + pipe->error = resp.error; + + switch (resp.type) { + case IA_CSS_ISYS_RESP_TYPE_STREAM_OPEN_DONE: + complete(&pipe->stream_open_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CLOSE_ACK: + complete(&pipe->stream_close_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_ACK: + complete(&pipe->stream_start_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + complete(&pipe->stream_start_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_STOP_ACK: + complete(&pipe->stream_stop_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_FLUSH_ACK: + complete(&pipe->stream_stop_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_PIN_DATA_READY: + if (resp.pin_id < IPU_ISYS_OUTPUT_PINS && + pipe->output_pins[resp.pin_id].pin_ready) + pipe->output_pins[resp.pin_id].pin_ready(pipe, &resp); + else + dev_err(&adev->dev, + "%d:No data pin ready handler for pin id %d\n", + resp.stream_handle, resp.pin_id); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK: + complete(&pipe->capture_ack_completion); + break; + case IA_CSS_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IA_CSS_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE: + + if(pipe->interlaced && pipe->short_packet_source == + IPU_ISYS_SHORT_PACKET_FROM_TUNIT) { + unsigned int i = pipe->short_packet_trace_index; + bool msg_matched = false; + unsigned int monitor_id; + + if(pipe->csi2->index>= IPU_ISYS_MAX_CSI2_LEGACY_PORTS) + monitor_id = TRACE_REG_CSI2_3PH_TM_MONITOR_ID; + else + monitor_id = TRACE_REG_CSI2_TM_MONITOR_ID; + + dma_sync_single_for_cpu(&isys->adev->dev, + isys->short_packet_trace_buffer_dma_addr, + IPU_ISYS_SHORT_PACKET_TRACE_BUFFER_SIZE, + DMA_BIDIRECTIONAL); + + do { + struct ici_isys_csi2_monitor_message msg = isys->short_packet_trace_buffer[i]; + u64 sof_time = tsc_time_to_tunit_time(isys, + isys->tsc_timer_base, isys->tunit_timer_base, + (u64) resp.timestamp[1] << 32 | resp.timestamp[0]); + u64 trace_time = extract_time_from_short_packet_msg(&msg); + u64 delta_time_us = tunit_time_to_us(isys, + (sof_time > trace_time) ? + sof_time - trace_time : + trace_time - sof_time); + + i = (i + 1) % IPU_ISYS_SHORT_PACKET_TRACE_MSG_NUMBER; + if (msg.cmd == TRACE_REG_CMD_TYPE_D64MTS && + msg.monitor_id == monitor_id && + msg.fs == 1 && + msg.port == pipe->csi2->index && + msg.vc == pipe->vc && + delta_time_us < IPU_ISYS_SHORT_PACKET_TRACE_MAX_TIMESHIFT) { + pipe->cur_field = (msg.sequence % 2) ? + ICI_FIELD_TOP : ICI_FIELD_BOTTOM; + pipe->short_packet_trace_index = i; + msg_matched = true; + dev_dbg(&isys->adev->dev,"Interlaced field ready. field = %d\n", + pipe->cur_field); + break; + } + } while (i != pipe->short_packet_trace_index); + + if (!msg_matched) + /* We have walked through the whole buffer. */ + dev_dbg(&isys->adev->dev,"No matched trace message found.\n"); + } + + for (i = 0; i < INTEL_IPU4_NUM_CAPTURE_DONE; i++) + if (pipe->capture_done[i]) + pipe->capture_done[i](pipe, &resp); + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_SOF: + break; + case IA_CSS_ISYS_RESP_TYPE_FRAME_EOF: + break; + default: + dev_err(&adev->dev, "%d:unknown response type %u\n", + resp.stream_handle, resp.type); + break; + } + + return 0; +} + +static irqreturn_t isys_isr(struct ipu_bus_device *adev) +{ + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + void __iomem *base = isys->pdata->base; + u32 status; + + spin_lock(&isys->power_lock); + if (!isys->power) { + spin_unlock(&isys->power_lock); + return IRQ_NONE; + } + + status = readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + do { + writel(status, isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_CLEAR); + + if (isys->isr_csi2_bits & status) { + unsigned int i; + + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + if (status & + IPU_ISYS_UNISPART_IRQ_CSI2(i)){ + + ici_isys_csi2_isr( + &isys->ici_csi2[i]); + } + } + } + + writel(0, base + IPU_REG_ISYS_UNISPART_SW_IRQ_REG); + + /* + * Handle a single FW event per checking the CSI-2 + * receiver SOF status. This is done in order to avoid + * the case where events arrive to the event queue and + * one of them is a SOF event which then could be + * handled before the SOF interrupt. This would pose + * issues in sequence numbering which is based on SOF + * interrupts, always assumed to arrive before FW SOF + * events. + */ + if (status & IPU_ISYS_UNISPART_IRQ_SW && + !isys_isr_one_ici(adev)) + status = IPU_ISYS_UNISPART_IRQ_SW; + else + status = 0; + + status |= readl(isys->pdata->base + + IPU_REG_ISYS_UNISPART_IRQ_STATUS); + } while (status & (isys->isr_csi2_bits + | IPU_ISYS_UNISPART_IRQ_SW)); + + spin_unlock(&isys->power_lock); + return IRQ_HANDLED; +} + +static void isys_isr_poll_ici(struct ipu_bus_device *adev) +{ + struct ici_isys *isys = ipu_bus_get_drvdata(adev); + + if (!isys->fwcom) { + dev_dbg(&isys->adev->dev, + "got interrupt but device not configured yet\n"); + return; + } + + while (!isys_isr_one_ici(adev)); +} + +int intel_ipu4_isys_isr_run_ici(void *ptr) +{ + struct ici_isys *isys = ptr; + + while (!kthread_should_stop()) { + usleep_range(500, 1000); + if (isys->ici_stream_opened) + isys_isr_poll_ici(isys->adev); + } + + return 0; +} + +static struct ipu_bus_driver isys_driver = { + .probe = isys_probe, + .remove = isys_remove, + .isr = isys_isr, + .wanted = IPU_ISYS_NAME, + .drv = { + .name = IPU_ISYS_NAME, + .owner = THIS_MODULE, + .pm = ISYS_PM_OPS, + }, +}; + +module_ipu_bus_driver(isys_driver); + +MODULE_AUTHOR("Scott Kennedy "); +MODULE_AUTHOR("Marcin Mozejko "); +MODULE_AUTHOR("Sakari Ailus "); +MODULE_AUTHOR("Samu Onkalo "); +MODULE_AUTHOR("Jouni Högander "); +MODULE_AUTHOR("Jouni Ukkonen "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Intel intel_ipu4 ici input system driver"); + +#endif /* ICI_ENABLED */ + diff --git a/drivers/media/pci/intel/ici/ici-isys.h b/drivers/media/pci/intel/ici/ici-isys.h new file mode 100644 index 000000000000..ffd21a9810ea --- /dev/null +++ b/drivers/media/pci/intel/ici/ici-isys.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef ICI_ISYS_H +#define ICI_ISYS_H + +#define ICI_ENABLED + +#ifdef ICI_ENABLED +#define IPU4_DEBUG + +#include +#include + +#include "ipu.h" +#include "ipu-pdata.h" +#include "ipu-fw-isys.h" +#include "ici-fw-isys.h" +#include "ici-isys-stream.h" +#include "ici-isys-csi2.h" +#include "ici-isys-csi2-be.h" +#include "ici-isys-pipeline-device.h" +#include "ici-isys-tpg.h" +#include "ipu-platform.h" +#include "ipu4/ipu-platform-isys.h" +#include "ipu4/ipu-platform-regs.h" + +#define IPU_ISYS_ENTITY_PREFIX "Intel IPU4" + +#define IPU_ISYS_2600_MEM_LINE_ALIGN 64 + +#define IPU_ISYS_MAX_CSI2_PORTS IPU_ISYS_MAX_CSI2_LEGACY_PORTS+IPU_ISYS_MAX_CSI2_COMBO_PORTS +/* for TPG */ +#define INTEL_IPU4_ISYS_FREQ_BXT_FPGA 25000000UL +#define INTEL_IPU4_ISYS_FREQ_BXT 533000000UL + +#define IPU_ISYS_SIZE_RECV_QUEUE 40 +#define IPU_ISYS_SIZE_SEND_QUEUE 40 +#define IPU_ISYS_NUM_RECV_QUEUE 1 + +/* + * Device close takes some time from last ack message to actual stopping + * of the SP processor. As long as the SP processor runs we can't proceed with + * clean up of resources. + */ +#define IPU_ISYS_OPEN_TIMEOUT_US 1000 +#define IPU_ISYS_OPEN_RETRY 1000 +#define IPU_ISYS_TURNOFF_DELAY_US 1000 +#define IPU_ISYS_TURNOFF_TIMEOUT 1000 +#define IPU_LIB_CALL_TIMEOUT_MS 2000 +#define IPU_LIB_CALL_TIMEOUT_JIFFIES \ + msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) + +#define INTEL_IPU4_ISYS_CSI2_LONG_PACKET_HEADER_SIZE 32 +#define INTEL_IPU4_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE 32 + +/* + * For B0/1: FW support max 6 streams + */ +#define INTEL_IPU4_ISYS_MAX_STREAMS 6 + + +#define IPU_ISYS_MIN_WIDTH 1U +#define IPU_ISYS_MIN_HEIGHT 1U +#define IPU_ISYS_MAX_WIDTH 16384U +#define IPU_ISYS_MAX_HEIGHT 16384U + +struct task_struct; + +/* + * struct ici_isys + * + * @media_dev: Media device + * @v4l2_dev: V4L2 device + * @adev: ISYS ipu4 bus device + * @power: Is ISYS powered on or not? + * @isr_bits: Which bits does the ISR handle? + * @power_lock: Serialise access to power (power state in general) + * @lock: serialise access to pipes + * @pipes: pipelines per stream ID + * @fwcom: fwcom library private pointer + * @line_align: line alignment in memory + * @legacy_port_cfg: lane mappings for legacy CSI-2 ports + * @combo_port_cfg: lane mappings for D/C-PHY ports + * @isr_thread: for polling for events if interrupt delivery isn't available + * @reset_needed: Isys requires d0i0->i3 transition + * @video_opened: total number of opened file handles on video nodes + * @mutex: serialise access isys video open/release related operations + * @stream_mutex: serialise stream start and stop, queueing requests + * @pdata: platform data pointer + * @csi2: CSI-2 receivers + * @tpg: test pattern generators + * @csi2_be: CSI-2 back-ends + * @isa: Input system accelerator + * @fw: ISYS firmware binary (unsecure firmware) + * @fw_sgt: fw scatterlist + * @pkg_dir: host pointer to pkg_dir + * @pkg_dir_dma_addr: I/O virtual address for pkg_dir + * @pkg_dir_size: size of pkg_dir in bytes + */ +struct ici_isys { + struct ipu_bus_device *adev; + + int power; + spinlock_t power_lock; + u32 isr_csi2_bits; + spinlock_t lock; + struct ipu_isys_pipeline *pipes[IPU_ISYS_MAX_STREAMS]; + void *fwcom; + unsigned int line_align; + u32 legacy_port_cfg; + u32 combo_port_cfg; + struct task_struct *isr_thread; + bool reset_needed; + bool icache_prefetch; + unsigned int video_opened; + unsigned int stream_opened; + struct dentry *debugfsdir; + struct mutex mutex; + struct mutex stream_mutex; + struct mutex lib_mutex; + + struct ipu_isys_pdata *pdata; + + struct ici_isys_pipeline_device pipeline_dev; + + struct ici_isys_pipeline *ici_pipes[IPU_ISYS_MAX_STREAMS]; + struct ici_isys_csi2 ici_csi2[IPU_ISYS_MAX_CSI2_PORTS]; + struct ici_isys_tpg ici_tpg[2]; // TODO map to a macro + struct ici_isys_csi2_be ici_csi2_be[NR_OF_CSI2_BE_SOC_STREAMS]; + unsigned int ici_stream_opened; + + const struct firmware *fw; + struct sg_table fw_sgt; + + u64 *pkg_dir; + dma_addr_t pkg_dir_dma_addr; + unsigned pkg_dir_size; + + struct list_head requests; + struct pm_qos_request pm_qos; + struct ici_isys_csi2_monitor_message *short_packet_trace_buffer; + dma_addr_t short_packet_trace_buffer_dma_addr; + u64 tsc_timer_base; + u64 tunit_timer_base; + spinlock_t listlock; /* Protect framebuflist */ + struct list_head framebuflist; + struct list_head framebuflist_fw; +}; + +int intel_ipu4_isys_isr_run_ici(void *ptr); + +struct isys_fw_msgs { + union { + u64 dummy; + struct ipu_fw_isys_frame_buff_set_abi frame; + struct ipu_fw_isys_stream_cfg_data_abi stream; + } fw_msg; + struct list_head head; + dma_addr_t dma_addr; +}; + +#define ipu_lib_call_notrace_unlocked(func, isys, ...) \ + ({ \ + int rval; \ + \ + rval = -ia_css_isys_##func((isys)->fwcom, ##__VA_ARGS__);\ + \ + rval; \ + }) + +#define ipu_lib_call_notrace(func, isys, ...) \ + ({ \ + int rval; \ + \ + mutex_lock(&(isys)->lib_mutex); \ + \ + rval = ipu_lib_call_notrace_unlocked( \ + func, isys, ##__VA_ARGS__); \ + \ + mutex_unlock(&(isys)->lib_mutex); \ + \ + rval; \ + }) + +#define ipu_lib_call(func, isys, ...) \ + ({ \ + int rval; \ + dev_dbg(&(isys)->adev->dev, "hostlib: libcall %s\n", #func);\ + rval = ipu_lib_call_notrace(func, isys, ##__VA_ARGS__);\ + \ + rval; \ + }) + +#undef DEBUGK +#ifdef IPU4_DEBUG /* Macro for printing debug infos */ +# ifdef __KERNEL__ /* for kernel space */ +# define DEBUGK(fmt, args...) printk(KERN_DEBUG "IPU4: " fmt, ## args) +# else /* for user space */ +# define DEBUGK(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else /* no debug prints */ +# define DEBUGK(fmt, args...) +#endif + +#else /* ICI_ENABLED */ +#pragma message "IPU ICI version is DISABLED." +#endif /* ICI_ENABLED */ + +#endif /* ICI_ISYS_H */ diff --git a/drivers/media/pci/intel/ici/libintel-ipu4_ici.c b/drivers/media/pci/intel/ici/libintel-ipu4_ici.c new file mode 100644 index 000000000000..68584377548d --- /dev/null +++ b/drivers/media/pci/intel/ici/libintel-ipu4_ici.c @@ -0,0 +1,403 @@ +// SPDX-License_Identifier: GPL-2.0 +// Copyright (C) 2014 - 2018 Intel Corporation + +#include +#include +#include +#include "ici/ici-isys.h" +#include "ipu-wrapper.h" +#include + +#include "ipu-platform.h" + +#define ipu_lib_call_notrace_unlocked(func, isys, ...) \ + ({ \ + int rval; \ + \ + rval = -ia_css_isys_##func((isys)->fwcom, ##__VA_ARGS__); \ + \ + rval; \ + }) + +#define ipu_lib_call_notrace(func, isys, ...) \ + ({ \ + int rval; \ + \ + mutex_lock(&(isys)->lib_mutex); \ + \ + rval = ipu_lib_call_notrace_unlocked( \ + func, isys, ##__VA_ARGS__); \ + \ + mutex_unlock(&(isys)->lib_mutex); \ + \ + rval; \ + }) + +#define ipu_lib_call(func, isys, ...) \ + ({ \ + int rval; \ + dev_dbg(&(isys)->adev->dev, "hostlib: libcall %s\n", #func); \ + rval = ipu_lib_call_notrace(func, isys, ##__VA_ARGS__); \ + \ + rval; \ + }) + +static int wrapper_init_done; + +int ici_fw_isys_close(struct ici_isys *isys) +{ + struct device *dev = &isys->adev->dev; + int timeout = IPU_ISYS_TURNOFF_TIMEOUT; + int rval; + unsigned long flags; + + /* + * Ask library to stop the isys fw. Actual close takes + * some time as the FW must stop its actions including code fetch + * to SP icache. + */ + spin_lock_irqsave(&isys->power_lock, flags); + rval = ipu_lib_call(device_close, isys); + spin_unlock_irqrestore(&isys->power_lock, flags); + if (rval) + dev_err(dev, "Device close failure: %d\n", rval); + + /* release probably fails if the close failed. Let's try still */ + do { + usleep_range(IPU_ISYS_TURNOFF_DELAY_US, + 2 * IPU_ISYS_TURNOFF_DELAY_US); + rval = ipu_lib_call_notrace(device_release, isys, 0); + timeout--; + } while (rval != 0 && timeout); + + /* Spin lock to wait the interrupt handler to be finished */ + spin_lock_irqsave(&isys->power_lock, flags); + if (!rval) + isys->fwcom = NULL; /* No further actions needed */ + else + dev_err(dev, "Device release time out %d\n", rval); + spin_unlock_irqrestore(&isys->power_lock, flags); + return rval; +} +EXPORT_SYMBOL_GPL(ici_fw_isys_close); + +int ici_fw_isys_init(struct ici_isys *isys, + unsigned int num_streams) +{ + int retry = IPU_ISYS_OPEN_RETRY; + unsigned int i; + + struct ia_css_isys_device_cfg_data isys_cfg = { + .driver_sys = { + .ssid = ISYS_SSID, + .mmid = ISYS_MMID, + .num_send_queues = clamp_t( + unsigned int, num_streams, 1, + IPU_ISYS_NUM_STREAMS), + .num_recv_queues = IPU_ISYS_NUM_RECV_QUEUE, + .send_queue_size = IPU_ISYS_SIZE_SEND_QUEUE, + .recv_queue_size = IPU_ISYS_SIZE_RECV_QUEUE, + .icache_prefetch = isys->icache_prefetch, + }, + }; + struct device *dev = &isys->adev->dev; + int rval; + + if (!wrapper_init_done) { + wrapper_init_done = true; + ipu_wrapper_init(ISYS_MMID, &isys->adev->dev, + isys->pdata->base); + } + + /* + * SRAM partitioning. Initially equal partitioning is set + * TODO: Fine tune the partitining based on the stream pixel load + */ + for (i = 0; i < min(IPU_NOF_SRAM_BLOCKS_MAX, NOF_SRAM_BLOCKS_MAX); i++) { + if (i < isys_cfg.driver_sys.num_send_queues) + isys_cfg.buffer_partition.num_gda_pages[i] = + (IPU_DEVICE_GDA_NR_PAGES * + IPU_DEVICE_GDA_VIRT_FACTOR) / + isys_cfg.driver_sys.num_send_queues; + else + isys_cfg.buffer_partition.num_gda_pages[i] = 0; + } + + rval = -ia_css_isys_device_open(&isys->fwcom, &isys_cfg); + if (rval < 0) { + dev_err(dev, "isys device open failed %d\n", rval); + return rval; + } + + do { + usleep_range(IPU_ISYS_OPEN_TIMEOUT_US, + IPU_ISYS_OPEN_TIMEOUT_US + 10); + rval = ipu_lib_call(device_open_ready, isys); + if (!rval) + break; + retry--; + } while (retry > 0); + + if (!retry && rval) { + dev_err(dev, "isys device open ready failed %d\n", rval); + ici_fw_isys_close(isys); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ici_fw_isys_init); + +void ici_fw_isys_cleanup(struct ici_isys *isys) +{ + ipu_lib_call(device_release, isys, 1); + isys->fwcom = NULL; +} +EXPORT_SYMBOL_GPL(ici_fw_isys_cleanup); + +struct ipu_fw_isys_resp_info_abi *ipu_fw_isys_get_resp( + void *context, unsigned int queue, + struct ipu_fw_isys_resp_info_abi *response) +{ + struct ia_css_isys_resp_info apiresp; + int rval; + + rval = -ia_css_isys_stream_handle_response(context, &apiresp); + if (rval < 0) + return NULL; + + response->buf_id = 0; + response->type = apiresp.type; + response->timestamp[0] = apiresp.timestamp[0]; + response->timestamp[1] = apiresp.timestamp[1]; + response->stream_handle = apiresp.stream_handle; + response->error_info.error = apiresp.error; + response->error_info.error_details = apiresp.error_details; + response->pin.out_buf_id = apiresp.pin.out_buf_id; + response->pin.addr = apiresp.pin.addr; + response->pin_id = apiresp.pin_id; + response->process_group_light.param_buf_id = + apiresp.process_group_light.param_buf_id; + response->process_group_light.addr = + apiresp.process_group_light.addr; + response->acc_id = apiresp.acc_id; +#ifdef IPU_OTF_SUPPORT + response->frame_counter = apiresp.frame_counter; + response->written_direct = apiresp.written_direct; +#endif + + return response; +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_get_resp); + +void ipu_fw_isys_put_resp(void *context, unsigned int queue) +{ + /* Nothing to do here really */ +} +EXPORT_SYMBOL_GPL(ipu_fw_isys_put_resp); + +int ici_fw_isys_simple_cmd(struct ici_isys *isys, + const unsigned int stream_handle, + enum ipu_fw_isys_send_type send_type) +{ + int rval = -1; + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_START: + rval = ipu_lib_call(stream_start, isys, stream_handle, + NULL); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_FLUSH: + rval = ipu_lib_call(stream_flush, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_STOP: + rval = ipu_lib_call(stream_stop, isys, stream_handle); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_CLOSE: + rval = ipu_lib_call(stream_close, isys, stream_handle); + break; + default: + WARN_ON(1); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ici_fw_isys_simple_cmd); + +static void resolution_abi_to_api(const struct ipu_fw_isys_resolution_abi *abi, + struct ia_css_isys_resolution *api) +{ + api->width = abi->width; + api->height = abi->height; +} + +static void output_pin_payload_abi_to_api( + struct ipu_fw_isys_output_pin_payload_abi *abi, + struct ia_css_isys_output_pin_payload *api) +{ + api->out_buf_id = abi->out_buf_id; + api->addr = abi->addr; +} + +static void output_pin_info_abi_to_api( + struct ipu_fw_isys_output_pin_info_abi *abi, + struct ia_css_isys_output_pin_info *api) +{ + api->input_pin_id = abi->input_pin_id; + resolution_abi_to_api(&abi->output_res, &api->output_res); + api->stride = abi->stride; + api->pt = abi->pt; + api->watermark_in_lines = abi->watermark_in_lines; + api->payload_buf_size = abi->payload_buf_size; + api->send_irq = abi->send_irq; + api->ft = abi->ft; +#ifdef IPU_OTF_SUPPORT + api->link_id = abi->link_id; +#endif + api->reserve_compression = abi->reserve_compression; +} + +static void param_pin_abi_to_api(struct ipu_fw_isys_param_pin_abi *abi, + struct ia_css_isys_param_pin *api) +{ + api->param_buf_id = abi->param_buf_id; + api->addr = abi->addr; +} + +static void input_pin_info_abi_to_api( + struct ipu_fw_isys_input_pin_info_abi *abi, + struct ia_css_isys_input_pin_info *api) +{ + resolution_abi_to_api(&abi->input_res, &api->input_res); + api->dt = abi->dt; + api->mipi_store_mode = abi->mipi_store_mode; + api->mapped_dt = abi->mapped_dt; +} + +static void isa_cfg_abi_to_api(const struct ipu_fw_isys_isa_cfg_abi *abi, + struct ia_css_isys_isa_cfg *api) +{ + unsigned int i; + + + for (i = 0; i < N_IA_CSS_ISYS_RESOLUTION_INFO; i++) + resolution_abi_to_api(&abi->isa_res[i], &api->isa_res[i]); + + api->blc_enabled = abi->cfg.blc; + api->lsc_enabled = abi->cfg.lsc; + api->dpc_enabled = abi->cfg.dpc; + api->downscaler_enabled = abi->cfg.downscaler; + api->awb_enabled = abi->cfg.awb; + api->af_enabled = abi->cfg.af; + api->ae_enabled = abi->cfg.ae; + api->paf_type = abi->cfg.paf; + api->send_irq_stats_ready = abi->cfg.send_irq_stats_ready; + api->send_resp_stats_ready = abi->cfg.send_irq_stats_ready; +} + +static void cropping_abi_to_api(struct ipu_fw_isys_cropping_abi *abi, + struct ia_css_isys_cropping *api) +{ + api->top_offset = abi->top_offset; + api->left_offset = abi->left_offset; + api->bottom_offset = abi->bottom_offset; + api->right_offset = abi->right_offset; +} + +static void stream_cfg_abi_to_api(struct ipu_fw_isys_stream_cfg_data_abi *abi, + struct ia_css_isys_stream_cfg_data *api) +{ + unsigned int i; + + api->src = abi->src; + api->vc = abi->vc; + api->isl_use = abi->isl_use; + api->compfmt = abi->compfmt; + isa_cfg_abi_to_api(&abi->isa_cfg, &api->isa_cfg); + for (i = 0; i < N_IA_CSS_ISYS_CROPPING_LOCATION; i++) + cropping_abi_to_api(&abi->crop[i], &api->crop[i]); + + api->send_irq_sof_discarded = abi->send_irq_sof_discarded; + api->send_irq_eof_discarded = abi->send_irq_eof_discarded; + api->send_resp_sof_discarded = abi->send_irq_sof_discarded; + api->send_resp_eof_discarded = abi->send_irq_eof_discarded; + api->nof_input_pins = abi->nof_input_pins; + api->nof_output_pins = abi->nof_output_pins; + for (i = 0; i < abi->nof_input_pins; i++) + input_pin_info_abi_to_api(&abi->input_pins[i], + &api->input_pins[i]); + + for (i = 0; i < abi->nof_output_pins; i++) + output_pin_info_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); +} + +static void frame_buff_set_abi_to_api( + struct ipu_fw_isys_frame_buff_set_abi *abi, + struct ia_css_isys_frame_buff_set *api) +{ + int i; + + for (i = 0; i < min(IPU_MAX_OPINS, MAX_OPINS); i++) + output_pin_payload_abi_to_api(&abi->output_pins[i], + &api->output_pins[i]); + + param_pin_abi_to_api(&abi->process_group_light, + &api->process_group_light); + + api->send_irq_sof = abi->send_irq_sof; + api->send_irq_eof = abi->send_irq_eof; +} + +int ici_fw_isys_complex_cmd(struct ici_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, + enum ipu_fw_isys_send_type send_type) +{ + union { + struct ia_css_isys_stream_cfg_data stream_cfg; + struct ia_css_isys_frame_buff_set buf; + } param; + int rval = -1; + + memset(¶m, 0, sizeof(param)); + + switch (send_type) { + case IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_capture_indication, + isys, stream_handle, ¶m.buf); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_OPEN: + stream_cfg_abi_to_api(cpu_mapped_buf, ¶m.stream_cfg); + rval = ipu_lib_call(stream_open, isys, stream_handle, + ¶m.stream_cfg); + break; + case IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE: + frame_buff_set_abi_to_api(cpu_mapped_buf, ¶m.buf); + rval = ipu_lib_call(stream_start, isys, stream_handle, + ¶m.buf); + break; + default: + WARN_ON(1); + } + + return rval; +} +EXPORT_SYMBOL_GPL(ici_fw_isys_complex_cmd); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu library"); +EXPORT_SYMBOL_GPL(ia_css_isys_device_open); +EXPORT_SYMBOL_GPL(ia_css_isys_device_open_ready); +EXPORT_SYMBOL_GPL(ia_css_isys_device_close); +EXPORT_SYMBOL_GPL(ia_css_isys_device_release); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_open); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_close); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_start); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_stop); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_flush); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_capture_indication); +EXPORT_SYMBOL_GPL(ia_css_isys_stream_handle_response); + diff --git a/include/media/ici.h b/include/media/ici.h new file mode 100644 index 000000000000..bf5a542dafb3 --- /dev/null +++ b/include/media/ici.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __ICI_PRIV_H__ +#define __ICI_PRIV_H__ + +#include +#include + +struct ici_isys_node; +struct ici_isys_pipeline_device; + +struct node_pad { + struct ici_isys_node *node; + unsigned pad_id; + unsigned flags; +}; + +struct node_pipe { + struct node_pad *src_pad; + struct node_pad *sink_pad; + struct list_head list_entry; + unsigned flags; + struct node_pipe *rev_pipe; +}; + +struct node_pipeline { +}; + +struct ici_isys_node { + struct list_head node_entry; + struct list_head iterate_node; + struct ici_isys_pipeline_device *parent; + unsigned node_id; + char name[ICI_MAX_NODE_NAME]; + void *sd; + bool external; + struct node_pad *node_pad; + unsigned nr_pads; + struct list_head node_pipes; + unsigned nr_pipes; + unsigned stream_count; + unsigned use_count; + struct node_pipeline *pipe; /* Pipeline this node belongs to. */ + int (*node_pipeline_validate)(struct node_pipeline *inp, + struct ici_isys_node *node); + int (*node_set_power)(struct ici_isys_node* node, + int power); + int (*node_set_streaming)(struct ici_isys_node* node, + void* ip, + int streaming); + int (*node_get_pad_supported_format)( + struct ici_isys_node* node, + struct ici_pad_supported_format_desc* psfd); + int (*node_set_pad_ffmt)(struct ici_isys_node* node, + struct ici_pad_framefmt* pff); + int (*node_get_pad_ffmt)(struct ici_isys_node* node, + struct ici_pad_framefmt* pff); + int (*node_set_pad_sel)(struct ici_isys_node* node, + struct ici_pad_selection* ps); + int (*node_get_pad_sel)(struct ici_isys_node* node, + struct ici_pad_selection* ps); +}; + +enum ici_ext_sd_param_id { + ICI_EXT_SD_PARAM_ID_LINK_FREQ = 1, + ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + ICI_EXT_SD_PARAM_ID_HFLIP, + ICI_EXT_SD_PARAM_ID_VFLIP, + ICI_EXT_SD_PARAM_ID_FRAME_LENGTH_LINES, + ICI_EXT_SD_PARAM_ID_LINE_LENGTH_PIXELS, + ICI_EXT_SD_PARAM_ID_HBLANK, + ICI_EXT_SD_PARAM_ID_VBLANK, + ICI_EXT_SD_PARAM_ID_SENSOR_MODE, + ICI_EXT_SD_PARAM_ID_ANALOGUE_GAIN, + ICI_EXT_SD_PARAM_ID_EXPOSURE, + ICI_EXT_SD_PARAM_ID_TEST_PATTERN, + ICI_EXT_SD_PARAM_ID_GAIN, + ICI_EXT_SD_PARAM_ID_THERMAL_DATA, + ICI_EXT_SD_PARAM_ID_MIPI_LANES, + ICI_EXT_SD_PARAM_ID_WDR_MODE, +}; + +enum ici_ext_sd_param_type { + ICI_EXT_SD_PARAM_TYPE_INT32 = 0, + ICI_EXT_SD_PARAM_TYPE_INT64, + ICI_EXT_SD_PARAM_TYPE_STR, +}; + +struct ici_ext_sd_param_custom_data { + unsigned size; + char* data; +}; + +struct ici_ext_subdev; + +struct ici_ext_sd_param { + struct ici_ext_subdev* sd; + int id; + int type; + union { + s32 val; + s64 s64val; + struct ici_ext_sd_param_custom_data custom; + }; +}; + +struct ici_ext_subdev; + +struct ici_ext_subdev_register { + void* ipu_data; + struct ici_ext_subdev* sd; + int (*setup_node)(void* ipu_data, + struct ici_ext_subdev* sd, + const char* name); + int (*create_link)( + struct ici_isys_node* src, + u16 src_pad, + struct ici_isys_node* sink, + u16 sink_pad, + u32 flags); +}; + +struct ici_ext_subdev { + struct ici_isys_node node; + void* client; + unsigned num_pads; + struct node_pad pads[ICI_MAX_PADS]; + u16 src_pad; + int (*do_register)( + struct ici_ext_subdev_register* reg); + void (*do_unregister)(struct ici_ext_subdev* sd); + int (*set_param)(struct ici_ext_sd_param* param); + int (*get_param)(struct ici_ext_sd_param* param); + int (*get_menu_item)(struct ici_ext_sd_param* param, + unsigned idx); +}; + +#endif // __ICI_PRIV_H__ diff --git a/include/uapi/linux/ici.h b/include/uapi/linux/ici.h new file mode 100644 index 000000000000..92cf555aabc8 --- /dev/null +++ b/include/uapi/linux/ici.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef _UAPI_LINUX_ICI_H +#define _UAPI_LINUX_ICI_H + +#define IPU_MAX_BUF_FRAMES 16 +#define ICI_MAX_PLANES 4 + +#define ICI_MAX_PADS 4 +#define ICI_MAX_LINKS 64 +#define ICI_MAX_NODE_NAME 64 + +#define MAJOR_STREAM 'A' +#define ICI_STREAM_DEVICE_NAME "intel_stream" + +#define MAJOR_PIPELINE 'B' +#define MINOR_PIPELINE 0 +#define ICI_PIPELINE_DEVICE_NAME "intel_pipeline" + +#define ici_fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) + +enum ici_format +{ + ICI_FORMAT_RGB888 = ici_fourcc_code('X', 'R', '2', '4'), + ICI_FORMAT_RGB565 = ici_fourcc_code('R', 'G', '1', '6'), + ICI_FORMAT_UYVY = ici_fourcc_code('U', 'Y', 'V', 'Y'), + ICI_FORMAT_YUYV = ici_fourcc_code('Y', 'U', 'Y', 'V'), + ICI_FORMAT_SBGGR12 = ici_fourcc_code('B', 'G', '1', '2'), + ICI_FORMAT_SGBRG12 = ici_fourcc_code('G', 'B', '1', '2'), + ICI_FORMAT_SGRBG12 = ici_fourcc_code('B', 'A', '1', '2'), + ICI_FORMAT_SRGGB12 = ici_fourcc_code('R', 'G', '1', '2'), + ICI_FORMAT_SBGGR10 = ici_fourcc_code('B', 'G', '1', '0'), + ICI_FORMAT_SGBRG10 = ici_fourcc_code('G', 'B', '1', '0'), + ICI_FORMAT_SGRBG10 = ici_fourcc_code('B', 'A', '1', '0'), + ICI_FORMAT_SRGGB10 = ici_fourcc_code('R', 'G', '1', '0'), + ICI_FORMAT_SBGGR8 = ici_fourcc_code('B', 'A', '8', '1'), + ICI_FORMAT_SGBRG8 = ici_fourcc_code('G', 'B', 'R', 'G'), + ICI_FORMAT_SGRBG8 = ici_fourcc_code('G', 'R', 'B', 'G'), + ICI_FORMAT_SRGGB8 = ici_fourcc_code('R', 'G', 'G', 'B'), + ICI_FORMAT_SBGGR10_DPCM8 = ici_fourcc_code('b', 'B', 'A', '8'), + ICI_FORMAT_SGBRG10_DPCM8 = ici_fourcc_code('b', 'G', 'A', '8'), + ICI_FORMAT_SGRBG10_DPCM8 = ici_fourcc_code('B', 'D', '1', '0'), + ICI_FORMAT_SRGGB10_DPCM8 = ici_fourcc_code('b', 'R', 'A', '8'), + ICI_FORMAT_NV12 = ici_fourcc_code('N', 'V', '1', '2'), + ICI_FORMAT_COUNT = 22, +}; + +#define ICI_PAD_FLAGS_SINK (1 << 0) +#define ICI_PAD_FLAGS_SOURCE (1 << 1) +#define ICI_PAD_FLAGS_MUST_CONNECT (1 << 2) + +#define ICI_LINK_FLAG_ENABLED (1 << 0) +#define ICI_LINK_FLAG_BACKLINK (1 << 1) + +enum { + ICI_MEM_USERPTR = 2, + ICI_MEM_DMABUF = 4, +}; + +enum { + ICI_FIELD_ANY = 0, + ICI_FIELD_NONE = 1, + ICI_FIELD_TOP = 2, + ICI_FIELD_BOTTOM = 3, + ICI_FIELD_ALTERNATE = 7, +}; + +struct ici_rect { + __s32 left; + __s32 top; + __u32 width; + __u32 height; +}; + +struct ici_frame_plane { + __u32 bytes_used; + __u32 length; + union { + unsigned long userptr; + __s32 dmafd; + } mem; + __u32 data_offset; + __u32 reserved[2]; +}; + +struct ici_frame_info { + __u32 frame_type; + __u32 field; + __u32 flag; + __u32 frame_buf_id; + struct timeval frame_timestamp; + __u32 frame_sequence_id; + __u32 mem_type; /* _DMA or _USER_PTR */ + struct ici_frame_plane frame_planes[ICI_MAX_PLANES]; /* multi-planar */ + __u32 num_planes; /* =1 single-planar > 1 multi-planar array size */ + __u32 reserved[2]; +}; + +#define ICI_IOC_STREAM_ON _IO(MAJOR_STREAM, 1) +#define ICI_IOC_STREAM_OFF _IO(MAJOR_STREAM, 2) +#define ICI_IOC_GET_BUF _IOWR(MAJOR_STREAM, 3, struct ici_frame_info) +#define ICI_IOC_PUT_BUF _IOWR(MAJOR_STREAM, 4, struct ici_frame_info) +#define ICI_IOC_SET_FORMAT _IOWR(MAJOR_STREAM, 5, struct ici_stream_format) + +struct ici_plane_stream_format { + __u32 sizeimage; + __u32 bytesperline; + __u32 bpp; + __u16 reserved[6]; +} __attribute__ ((packed)); + +struct ici_framefmt { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; + __u32 colorspace; + __u8 flags; + __u8 reserved[8]; +}; + +struct ici_planefmt { + struct ici_plane_stream_format plane_fmt[ICI_MAX_PLANES]; + __u8 num_planes; +} __attribute__ ((packed)); + +struct ici_stream_format { + struct ici_framefmt ffmt; + struct ici_planefmt pfmt; +}; + +struct ici_pad_desc { + __u32 node_id; /* node ID */ + __u16 pad_idx; /* pad index */ + __u32 flags; + __u32 reserved[2]; +}; + +enum ici_ext_sel_type { + ICI_EXT_SEL_TYPE_NATIVE = 1, + ICI_EXT_SEL_TYPE_CROP, + ICI_EXT_SEL_TYPE_CROP_BOUNDS, + ICI_EXT_SEL_TYPE_COMPOSE, + ICI_EXT_SEL_TYPE_COMPOSE_BOUNDS, +}; + +struct ici_pad_supported_format_desc { + __u32 idx; + __u32 color_format; + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; + struct ici_pad_desc pad; +}; + +struct ici_pad_framefmt { + struct ici_framefmt ffmt; + struct ici_pad_desc pad; +}; + +struct ici_pad_selection { + __u32 sel_type; + struct ici_rect rect; + struct ici_pad_desc pad; +}; + +struct ici_node_desc { + __u32 node_count; + __s32 node_id; + unsigned nr_pads; + char name[ICI_MAX_NODE_NAME]; + struct ici_pad_desc node_pad[ICI_MAX_PADS]; +}; + +struct ici_link_desc { + struct ici_pad_desc source; + struct ici_pad_desc sink; + __u32 flags; + __u32 reserved[2]; +}; + +struct ici_links_query { + struct ici_pad_desc pad; /* pad index */ + __u16 links_cnt; /* number of connected links, described below */ + struct ici_link_desc links[ICI_MAX_LINKS]; + +}; + +#define ICI_IOC_ENUM_NODES _IOWR(MAJOR_PIPELINE, 1, struct ici_node_desc) +#define ICI_IOC_ENUM_LINKS _IOWR(MAJOR_PIPELINE, 2, struct ici_links_query) +#define ICI_IOC_SETUP_PIPE _IOWR(MAJOR_PIPELINE, 3, struct ici_link_desc) +#define ICI_IOC_SET_FRAMEFMT _IOWR(MAJOR_PIPELINE, 4, struct ici_pad_framefmt) +#define ICI_IOC_GET_FRAMEFMT _IOWR(MAJOR_PIPELINE, 5, struct ici_pad_framefmt) +#define ICI_IOC_GET_SUPPORTED_FRAMEFMT _IOWR(MAJOR_PIPELINE, 6, struct ici_pad_supported_format_desc) +#define ICI_IOC_SET_SELECTION _IOWR(MAJOR_PIPELINE, 7, struct ici_pad_selection) +#define ICI_IOC_GET_SELECTION _IOWR(MAJOR_PIPELINE, 8, struct ici_pad_selection) + +#endif // _UAPI_LINUX_ICI_H From eed336920210deac37327c0ea30ba21f77ec2432 Mon Sep 17 00:00:00 2001 From: "Yew, Chang Ching" Date: Tue, 23 Oct 2018 13:23:51 +0800 Subject: [PATCH 1148/1276] media: i2c: crlmodule-lite: base code (configurable register list) Base code for common sensor driver, crlmodule-lite to work with ICI driver Squash of below patches from https://github.com/intel/linux-intel-lts/tree/4.14/base/drivers/media/i2c/crlmodule-lite/ 43b66e media: i2c: crlmodule-lite: Add register settings to support multiple modes b99df4 media: i2c: crlmodule-lite: Add mask field f20974 media: i2c: crlmodule-lite: Remove op_sys_clock 33d606 media: intel-ipu4: [ICI] ACPI functionality for crlmodule-lite sensors e90ece crlmodule-lite: Fixing un-used variable warning issue 87737a media:platform/crlmodule-lite:to overcome ACRN UOS limitation of GPIOs. 3f72fb crlmodule-lite: Adding config dependencies between crlmodule & crlmodule-lite 045726 media: i2c: common camera sensor driver crlmodule-lite for ICI Change-Id: I12c120dedbfaf6c868e5b52b69d798bf1d4774f9 Signed-off-by: Yew, Chang Ching --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/crlmodule-lite/Kconfig | 7 + drivers/media/i2c/crlmodule-lite/Makefile | 9 + .../crl_adv7481_configuration.h | 703 +++++ .../crl_adv7481_cvbs_configuration.h | 264 ++ .../crl_adv7481_eval_configuration.h | 531 ++++ .../crl_adv7481_hdmi_configuration.c | 624 ++++ .../crl_adv7481_hdmi_configuration.h | 942 ++++++ .../crl_magna_configuration_ti964.h | 297 ++ .../media/i2c/crlmodule-lite/crlmodule-core.c | 2696 +++++++++++++++++ .../media/i2c/crlmodule-lite/crlmodule-data.c | 73 + .../i2c/crlmodule-lite/crlmodule-msrlist.c | 160 + .../i2c/crlmodule-lite/crlmodule-msrlist.h | 53 + .../media/i2c/crlmodule-lite/crlmodule-nvm.c | 139 + .../media/i2c/crlmodule-lite/crlmodule-nvm.h | 21 + .../media/i2c/crlmodule-lite/crlmodule-regs.c | 330 ++ .../media/i2c/crlmodule-lite/crlmodule-regs.h | 24 + .../i2c/crlmodule-lite/crlmodule-sensor-ds.h | 552 ++++ drivers/media/i2c/crlmodule-lite/crlmodule.h | 114 + include/media/as3638.h | 33 + include/media/crlmodule-lite.h | 25 + include/media/dw9714.h | 32 + include/media/lc898122.h | 14 + include/media/lm3643.h | 50 + 25 files changed, 7695 insertions(+) create mode 100644 drivers/media/i2c/crlmodule-lite/Kconfig create mode 100644 drivers/media/i2c/crlmodule-lite/Makefile create mode 100644 drivers/media/i2c/crlmodule-lite/crl_adv7481_configuration.h create mode 100644 drivers/media/i2c/crlmodule-lite/crl_adv7481_cvbs_configuration.h create mode 100644 drivers/media/i2c/crlmodule-lite/crl_adv7481_eval_configuration.h create mode 100644 drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.c create mode 100644 drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.h create mode 100644 drivers/media/i2c/crlmodule-lite/crl_magna_configuration_ti964.h create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-core.c create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-data.c create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.c create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.h create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-nvm.c create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-nvm.h create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-regs.c create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-regs.h create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule-sensor-ds.h create mode 100644 drivers/media/i2c/crlmodule-lite/crlmodule.h create mode 100644 include/media/as3638.h create mode 100644 include/media/crlmodule-lite.h create mode 100755 include/media/dw9714.h create mode 100644 include/media/lc898122.h create mode 100644 include/media/lm3643.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index dbcb2131ddf7..bb03546ab566 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -946,6 +946,7 @@ config VIDEO_S5K5BAF source "drivers/media/i2c/smiapp/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" source "drivers/media/i2c/crlmodule/Kconfig" +source "drivers/media/i2c/crlmodule-lite/Kconfig" config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 4ad43e933238..5860426e9400 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -115,3 +115,4 @@ obj-$(CONFIG_VIDEO_CRLMODULE) += crlmodule/ obj-$(CONFIG_VIDEO_TI964) += ti964.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o obj-$(CONFIG_VIDEO_TI960) += ti960.o +obj-$(CONFIG_VIDEO_CRLMODULE_LITE) += crlmodule-lite/ diff --git a/drivers/media/i2c/crlmodule-lite/Kconfig b/drivers/media/i2c/crlmodule-lite/Kconfig new file mode 100644 index 000000000000..5f6b506ae749 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_CRLMODULE_LITE + tristate "CRL Module sensor support for ICI driver" + depends on I2C + depends on VIDEO_INTEL_ICI + depends on !VIDEO_CRLMODULE + ---help--- + This is a generic driver for CRL based camera modules. diff --git a/drivers/media/i2c/crlmodule-lite/Makefile b/drivers/media/i2c/crlmodule-lite/Makefile new file mode 100644 index 000000000000..de5f5e4d3ccb --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) + +crlmodule-lite-objs += crlmodule-core.o crlmodule-data.o \ + crlmodule-regs.o crlmodule-nvm.o \ + crl_adv7481_hdmi_configuration.o \ + crlmodule-msrlist.o +obj-$(CONFIG_VIDEO_CRLMODULE_LITE) += crlmodule-lite.o + +ccflags-y += -Idrivers/media/i2c diff --git a/drivers/media/i2c/crlmodule-lite/crl_adv7481_configuration.h b/drivers/media/i2c/crlmodule-lite/crl_adv7481_configuration.h new file mode 100644 index 000000000000..4ebe85478a24 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_adv7481_configuration.h @@ -0,0 +1,703 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_ADV7481_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep adv7481_powerup_regset[] = { + {0xFF, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* SW reset */ + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, /* Delay 5ms */ + {0x01, CRL_REG_LEN_08BIT, 0x76, 0xE0}, /* ADI recommended setting */ + {0xF2, CRL_REG_LEN_08BIT, 0x01, 0xE0}, /* I2C Rd Auto-Increment=1 */ + {0xF3, CRL_REG_LEN_08BIT, 0x4C, 0xE0}, /* DPLL Map Address */ + {0xF4, CRL_REG_LEN_08BIT, 0x44, 0xE0}, /* CP Map Address */ + {0xF5, CRL_REG_LEN_08BIT, 0x68, 0xE0}, /* HDMI RX Map Address */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* EDID Map Address */ + {0xF7, CRL_REG_LEN_08BIT, 0x64, 0xE0}, /* HDMI RX Repeater Map Addr */ + {0xF8, CRL_REG_LEN_08BIT, 0x62, 0xE0}, /* HDMI RX Infoframe Map Addr */ + {0xF9, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CBUS Map Address Set */ + {0xFA, CRL_REG_LEN_08BIT, 0x82, 0xE0}, /* CEC Map Address Set */ + {0xFB, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* SDP Main Map Address */ + {0xFC, CRL_REG_LEN_08BIT, 0x90, 0xE0}, /* CSI-TXB Map Address */ + {0xFD, CRL_REG_LEN_08BIT, 0x94, 0xE0}, /* CSI-TXA Map Address */ + {0x00, CRL_REG_LEN_08BIT, 0x50, 0xE0}, /* Disable Chip Powerdown & + HDMI Rx Block */ + {0x40, CRL_REG_LEN_08BIT, 0x83, 0x64}, /* Enable HDCP 1.1 */ + {0x00, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* ADI recommended setting */ + {0x3D, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI recommended setting */ + {0x3E, CRL_REG_LEN_08BIT, 0x69, 0x68}, /* ADI recommended setting */ + {0x3F, CRL_REG_LEN_08BIT, 0x46, 0x68}, /* ADI recommended setting */ + {0x4E, CRL_REG_LEN_08BIT, 0xFE, 0x68}, /* ADI recommended setting */ + {0x4F, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* ADI recommended setting */ + {0x57, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI recommended setting */ + {0x58, CRL_REG_LEN_08BIT, 0x04, 0x68}, /* ADI recommended setting */ + {0x85, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI recommended setting */ + {0x83, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Enable All Terminations */ + {0xBE, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI recommended setting */ + {0x6C, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Manual Enable */ + {0xF8, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Asserted */ + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Audio Mute Speed = + Fastest Smallest Step Size */ + {0x70, CRL_REG_LEN_08BIT, 0xA0, 0x64}, /* Write primary edid size */ + {0x74, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* Enable manual edid */ + {0x7A, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Write edid sram select */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* Write edid map bus address */ + + {0x00*4, CRL_REG_LEN_32BIT, 0x00FFFFFF, 0x6C}, /* EDID programming */ + {0x01*4, CRL_REG_LEN_32BIT, 0xFFFFFF00, 0x6C}, /* EDID programming */ + {0x02*4, CRL_REG_LEN_32BIT, 0x4DD90100, 0x6C}, /* EDID programming */ + {0x03*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x04*4, CRL_REG_LEN_32BIT, 0x00110103, 0x6C}, /* EDID programming */ + {0x05*4, CRL_REG_LEN_32BIT, 0x80000078, 0x6C}, /* EDID programming */ + {0x06*4, CRL_REG_LEN_32BIT, 0x0A0DC9A0, 0x6C}, /* EDID programming */ + {0x07*4, CRL_REG_LEN_32BIT, 0x57479827, 0x6C}, /* EDID programming */ + {0x08*4, CRL_REG_LEN_32BIT, 0x12484C00, 0x6C}, /* EDID programming */ + {0x09*4, CRL_REG_LEN_32BIT, 0x00000101, 0x6C}, /* EDID programming */ + {0x0A*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0B*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0C*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0D*4, CRL_REG_LEN_32BIT, 0x0101011D, 0x6C}, /* EDID programming */ + {0x0E*4, CRL_REG_LEN_32BIT, 0x80D0721C, 0x6C}, /* EDID programming */ + {0x0F*4, CRL_REG_LEN_32BIT, 0x1620102C, 0x6C}, /* EDID programming */ + {0x10*4, CRL_REG_LEN_32BIT, 0x2580C48E, 0x6C}, /* EDID programming */ + {0x11*4, CRL_REG_LEN_32BIT, 0x2100009E, 0x6C}, /* EDID programming */ + {0x12*4, CRL_REG_LEN_32BIT, 0x011D8018, 0x6C}, /* EDID programming */ + {0x13*4, CRL_REG_LEN_32BIT, 0x711C1620, 0x6C}, /* EDID programming */ + {0x14*4, CRL_REG_LEN_32BIT, 0x582C2500, 0x6C}, /* EDID programming */ + {0x15*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x16*4, CRL_REG_LEN_32BIT, 0x009E0000, 0x6C}, /* EDID programming */ + {0x17*4, CRL_REG_LEN_32BIT, 0x00FC0048, 0x6C}, /* EDID programming */ + {0x18*4, CRL_REG_LEN_32BIT, 0x444D4920, 0x6C}, /* EDID programming */ + {0x19*4, CRL_REG_LEN_32BIT, 0x4C4C430A, 0x6C}, /* EDID programming */ + {0x1A*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1B*4, CRL_REG_LEN_32BIT, 0x000000FD, 0x6C}, /* EDID programming */ + {0x1C*4, CRL_REG_LEN_32BIT, 0x003B3D0F, 0x6C}, /* EDID programming */ + {0x1D*4, CRL_REG_LEN_32BIT, 0x2D08000A, 0x6C}, /* EDID programming */ + {0x1E*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1F*4, CRL_REG_LEN_32BIT, 0x202001C1, 0x6C}, /* EDID programming */ + {0x20*4, CRL_REG_LEN_32BIT, 0x02031E77, 0x6C}, /* EDID programming */ + {0x21*4, CRL_REG_LEN_32BIT, 0x4F941305, 0x6C}, /* EDID programming */ + {0x22*4, CRL_REG_LEN_32BIT, 0x03040201, 0x6C}, /* EDID programming */ + {0x23*4, CRL_REG_LEN_32BIT, 0x16150706, 0x6C}, /* EDID programming */ + {0x24*4, CRL_REG_LEN_32BIT, 0x1110121F, 0x6C}, /* EDID programming */ + {0x25*4, CRL_REG_LEN_32BIT, 0x23090701, 0x6C}, /* EDID programming */ + {0x26*4, CRL_REG_LEN_32BIT, 0x65030C00, 0x6C}, /* EDID programming */ + {0x27*4, CRL_REG_LEN_32BIT, 0x10008C0A, 0x6C}, /* EDID programming */ + {0x28*4, CRL_REG_LEN_32BIT, 0xD0902040, 0x6C}, /* EDID programming */ + {0x29*4, CRL_REG_LEN_32BIT, 0x31200C40, 0x6C}, /* EDID programming */ + {0x2A*4, CRL_REG_LEN_32BIT, 0x5500138E, 0x6C}, /* EDID programming */ + {0x2B*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x2C*4, CRL_REG_LEN_32BIT, 0x011D00BC, 0x6C}, /* EDID programming */ + {0x2D*4, CRL_REG_LEN_32BIT, 0x52D01E20, 0x6C}, /* EDID programming */ + {0x2E*4, CRL_REG_LEN_32BIT, 0xB8285540, 0x6C}, /* EDID programming */ + {0x2F*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x30*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x31*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x32*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x33*4, CRL_REG_LEN_32BIT, 0x9600C48E, 0x6C}, /* EDID programming */ + {0x34*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x35*4, CRL_REG_LEN_32BIT, 0x011D0072, 0x6C}, /* EDID programming */ + {0x36*4, CRL_REG_LEN_32BIT, 0x51D01E20, 0x6C}, /* EDID programming */ + {0x37*4, CRL_REG_LEN_32BIT, 0x6E285500, 0x6C}, /* EDID programming */ + {0x38*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x39*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x3A*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x3B*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x3C*4, CRL_REG_LEN_32BIT, 0x9600138E, 0x6C}, /* EDID programming */ + {0x3D*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x3E*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x3F*4, CRL_REG_LEN_32BIT, 0x000000CB, 0x6C}, /* EDID programming */ + + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* No MIPI frame start */ + {0x26, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* Disable sleep mode */ + {0x27, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* Disable escape mode */ + {0x7E, CRL_REG_LEN_08BIT, 0xA0, 0x94}, /* ADI recommended setting */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* ADI recommended setting */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x90}, /* ADI recommended setting */ + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, /* ADI recommended setting */ + {0x34, CRL_REG_LEN_08BIT, 0x55, 0x94}, /* ADI recommended setting */ + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, /* ADI recommended setting */ + {0xCA, CRL_REG_LEN_08BIT, 0x02, 0x94}, /* ADI recommended setting */ + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, /* ADI recommended setting */ + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, /* ADI recommended setting */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Power up DPHY */ + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI recommended setting */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_1080p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x5E, 0xE0}, /* Select Resolution 1080P */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 1080P shift left 44 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080P shift left 44 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 1080P shift left 44 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080P shift left 44 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_720p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x53, 0xE0}, /* Select Resolution 720P */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 720P shift left 40 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD8, 0x44}, /* 720P shift left 40 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 720P shift left 40 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD8, 0x44}, /* 720P shift left 40 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_VGA[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x88, 0xE0}, /* Select Resolution VGA */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_1080i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x54, 0xE0}, /* Select Resolution 1080i*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* 1080i shift left 44 pixel */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080i shift left 44 pixel */ + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, /* 1080i shift left 44 pixel */ + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, /* 1080i shift left 44 pixel */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_480i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* Select Resolution 480i */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_576p[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x4B, 0xE0}, /* Select Resolution 576p*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_mode_576i[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, /* YCrCb output */ + {0x05, CRL_REG_LEN_08BIT, 0x41, 0xE0}, /* Select Resolution 576i*/ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* CSC Depends on ip Packets - SDR422 set */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* ADI recommended setting */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI recommended setting */ + + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS TRISTATED */ + {0x10, CRL_REG_LEN_08BIT, 0xA0, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */ + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_streamon_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, /* Power-up CSI-TX */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* i2c_mipi_pll_en - 1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x94}, +}; + +static struct crl_sensor_detect_config adv7481_sensor_detect_regset[] = { + { + .reg = { 0x0019, CRL_REG_LEN_08BIT, 0x000000ff }, + .width = 5, + }, + { + .reg = { 0x0016, CRL_REG_LEN_16BIT, 0x0000ffff }, + .width = 7, + }, +}; + +static struct crl_pll_configuration adv7481_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + +}; + +static struct crl_subdev_rect_rep adv7481_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_1080p_rects), + .sd_rects = adv7481_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_1080p), + .mode_regs = adv7481_mode_1080p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_720p_rects), + .sd_rects = adv7481_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_720p), + .mode_regs = adv7481_mode_720p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_VGA_rects), + .sd_rects = adv7481_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_VGA), + .mode_regs = adv7481_mode_VGA, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_1080i_rects), + .sd_rects = adv7481_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_1080i), + .mode_regs = adv7481_mode_1080i, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_480i_rects), + .sd_rects = adv7481_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_480i), + .mode_regs = adv7481_mode_480i, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_576p_rects), + .sd_rects = adv7481_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = ARRAY_SIZE(adv7481_mode_576p), + .mode_regs = adv7481_mode_576p, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_576i_rects), + .sd_rects = adv7481_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = adv7481_mode_576i, + }, +}; + +static struct crl_sensor_subdev_config adv7481_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_crl_csi_data_fmt[] = { + { + .code = ICI_FORMAT_UYVY, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_mode_1080p), + .regs = adv7481_mode_1080p, /* default yuv422 format */ + }, +}; + +static struct crl_ctrl_data adv7481_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .name = "CTRL_ID_LINK_FREQ", + .type = CRL_CTRL_TYPE_MENU_INT, + .data.int_menu.def = 0, + .data.int_menu.max = ARRAY_SIZE(adv7481_pll_configurations) - 1, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_PA", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_CSI", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +/* Power items, they are enabled in the order they are listed here */ +static struct crl_power_seq_entity adv7481_power_items[] = { + { + .type = CRL_POWER_ETY_CLK_FRAMEWORK, + .val = 24000000, + }, + { + .type = CRL_POWER_ETY_GPIO_FROM_PDATA, + .val = 1, + }, +}; + +static struct crl_sensor_configuration adv7481_crl_configuration = { + + .power_items = ARRAY_SIZE(adv7481_power_items), + .power_entities = adv7481_power_items, + + .powerup_regs_items = ARRAY_SIZE(adv7481_powerup_regset), + .powerup_regs = adv7481_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_streamoff_regs), + .poweroff_regs = adv7481_streamoff_regs, + + .id_reg_items = ARRAY_SIZE(adv7481_sensor_detect_regset), + .id_regs = adv7481_sensor_detect_regset, + + .subdev_items = ARRAY_SIZE(adv7481_sensor_subdevs), + .subdevs = adv7481_sensor_subdevs, + + .sensor_limits = &adv7481_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_pll_configurations), + .pll_configs = adv7481_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_modes), + .modes = adv7481_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_streamon_regs), + .streamon_regs = adv7481_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_streamoff_regs), + .streamoff_regs = adv7481_streamoff_regs, + + .ctrl_items = ARRAY_SIZE(adv7481_ctrls), + .ctrl_bank = adv7481_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_crl_csi_data_fmt), + .csi_fmts = adv7481_crl_csi_data_fmt, +}; + +#endif /* __CRLMODULE_ADV7481_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crl_adv7481_cvbs_configuration.h b/drivers/media/i2c/crlmodule-lite/crl_adv7481_cvbs_configuration.h new file mode 100644 index 000000000000..5ea1f7c01f19 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_adv7481_cvbs_configuration.h @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep adv7481_cvbs_powerup_regset[] = { + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, //LLC/PIX/AUD/SPI PINS TRISTATED + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0xF2}, //Exit Power Down Mode + {0x52, CRL_REG_LEN_08BIT, 0xCD, 0xF2}, //ADI Required Write + {0x00, CRL_REG_LEN_08BIT, 0x00, 0xF2}, //INSEL = CVBS in on Ain 1 + {0x0E, CRL_REG_LEN_08BIT, 0x80, 0xF2}, //ADI Required Write + {0x9C, CRL_REG_LEN_08BIT, 0x00, 0xF2}, //ADI Required Write + {0x9C, CRL_REG_LEN_08BIT, 0xFF, 0xF2}, //ADI Required Write + {0x0E, CRL_REG_LEN_08BIT, 0x00, 0xF2}, //ADI Required Write + {0x80, CRL_REG_LEN_08BIT, 0x51, 0xF2}, //ADI Required Write + {0x81, CRL_REG_LEN_08BIT, 0x51, 0xF2}, //ADI Required Write + {0x82, CRL_REG_LEN_08BIT, 0x68, 0xF2}, //ADI Required Write + {0x03, CRL_REG_LEN_08BIT, 0x42, 0xF2}, //Tri-S Output Drivers, PwrDwn 656 pads + {0x04, CRL_REG_LEN_08BIT, 0x07, 0xF2}, //Power-up INTRQ pad, & Enable SFL + {0x13, CRL_REG_LEN_08BIT, 0x00, 0xF2}, //ADI Required Write + {0x17, CRL_REG_LEN_08BIT, 0x41, 0xF2}, //Select SH1 + {0x31, CRL_REG_LEN_08BIT, 0x12, 0xF2}, //ADI Required Write + {0x10, CRL_REG_LEN_08BIT, 0xC0, 0xE0}, //Enable 1-Lane MIPI Tx, enable pixel output and route SD through Pixel port + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x90}, //Enable 1-lane MIPI + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x90}, //Set Auto DPHY Timing + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, //ADI Required Write + {0xD2, CRL_REG_LEN_08BIT, 0x40, 0x90}, //ADI Required Write + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x90}, //ADI Required Write + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x90}, //ADI Required Write + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x90}, //ADI Required Write + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x90}, //i2c_dphy_pwdn - 1'b0 + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x90}, //ADI Required Write + {0x1E, CRL_REG_LEN_08BIT, 0xC0, 0x90}, //ADI Required Write +}; + + +static struct crl_register_write_rep adv7481_cvbs_streamon_regs[] = { + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x90}, //ADI Required Write + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x90}, //i2c_mipi_pll_en - 1'b1 + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x90}, //Power-up CSI-TX 21 + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x90}, //ADI Required Write +}; + +static struct crl_register_write_rep adv7481_cvbs_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x90}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x90}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x90}, /* i2c_mipi_pll_en - 1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x90}, +}; + + +static struct crl_pll_configuration adv7481_cvbs_pll_configurations[] = { + { + .input_clk = 286363636, + .op_sys_clk = 216000000, + .bitsperpixel = 16, + .pixel_rate_csi = 130000000, + .pixel_rate_pa = 130000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 130000000, + .bitsperpixel = 16, + .pixel_rate_csi = 130000000, + .pixel_rate_pa = 130000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +static struct crl_subdev_rect_rep adv7481_cvbs_ntsc_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 240, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 720, + .in_rect.height = 240, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_mode_rep adv7481_cvbs_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_cvbs_ntsc_rects), + .sd_rects = adv7481_cvbs_ntsc_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 240, + .comp_items = 0, + .ctrl_data = 0, + .mode_regs_items = 0, + .mode_regs = 0, + }, +}; + +static struct crl_sensor_subdev_config adv7481_cvbs_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 cvbs binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 cvbs pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_cvbs_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 720, + .y_addr_max = 240, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_cvbs_crl_csi_data_fmt[] = { + { + .code = ICI_FORMAT_UYVY, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, +}; + +static struct crl_ctrl_data adv7481_cvbs_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .name = "CTRL_ID_LINK_FREQ", + .type = CRL_CTRL_TYPE_MENU_INT, + .data.int_menu.def = 0, + .data.int_menu.max = ARRAY_SIZE(adv7481_cvbs_pll_configurations) - 1, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_PA", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_CSI", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +static struct crl_sensor_configuration adv7481_cvbs_crl_configuration = { + .sensor_init = NULL, + .sensor_cleanup = NULL, + + .onetime_init_regs_items = 0, //one time initialization is done by HDMI part + .onetime_init_regs = NULL, + + .powerup_regs_items = ARRAY_SIZE(adv7481_cvbs_powerup_regset), + .powerup_regs = adv7481_cvbs_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_cvbs_streamoff_regs), + .poweroff_regs = adv7481_cvbs_streamoff_regs, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = ARRAY_SIZE(adv7481_cvbs_sensor_subdevs), + .subdevs = adv7481_cvbs_sensor_subdevs, + + .sensor_limits = &adv7481_cvbs_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_cvbs_pll_configurations), + .pll_configs = adv7481_cvbs_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_cvbs_modes), + .modes = adv7481_cvbs_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_cvbs_streamon_regs), + .streamon_regs = adv7481_cvbs_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_cvbs_streamoff_regs), + .streamoff_regs = adv7481_cvbs_streamoff_regs, + + .ctrl_items = ARRAY_SIZE(adv7481_cvbs_ctrls), + .ctrl_bank = adv7481_cvbs_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_cvbs_crl_csi_data_fmt), + .csi_fmts = adv7481_cvbs_crl_csi_data_fmt, + + .addr_len = CRL_ADDR_7BIT, +}; + +#endif /* __CRLMODULE_ADV7481_CVBS_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crl_adv7481_eval_configuration.h b/drivers/media/i2c/crlmodule-lite/crl_adv7481_eval_configuration.h new file mode 100644 index 000000000000..575d2db42edc --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_adv7481_eval_configuration.h @@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + + +struct crl_ctrl_data_pair ctrl_data_lanes[] = { + { + .ctrl_id = ICI_EXT_SD_PARAM_ID_MIPI_LANES, + .data = 4, + }, + { + .ctrl_id = ICI_EXT_SD_PARAM_ID_MIPI_LANES, + .data = 2, + }, +}; +static struct crl_pll_configuration adv7481_eval_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 24, + .pixel_rate_csi = 800000000, + .pixel_rate_pa = 800000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_eval_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_eval_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_1080p_rects), + .sd_rects = adv7481_eval_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[0], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_720p_rects), + .sd_rects = adv7481_eval_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[0], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_VGA_rects), + .sd_rects = adv7481_eval_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_1080i_rects), + .sd_rects = adv7481_eval_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_480i_rects), + .sd_rects = adv7481_eval_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_576p_rects), + .sd_rects = adv7481_eval_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_eval_576i_rects), + .sd_rects = adv7481_eval_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .comp_items = 1, + .ctrl_data = &ctrl_data_lanes[1], + .mode_regs_items = 0, + .mode_regs = NULL, + }, +}; + +static struct crl_sensor_subdev_config adv7481_eval_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 pixel array", + }, +}; + +static struct crl_sensor_subdev_config adv7481b_eval_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481b binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481b pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_eval_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_eval_crl_csi_data_fmt[] = { + { + .code = ICI_FORMAT_YUYV, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = ICI_FORMAT_UYVY, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = ICI_FORMAT_RGB565, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = 0, + .regs = NULL, + }, + { + .code = ICI_FORMAT_RGB888, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 24, + .regs_items = 0, + .regs = NULL, + }, +}; + +static struct crl_ctrl_data adv7481_eval_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .name = "CTRL_ID_LINK_FREQ", + .type = CRL_CTRL_TYPE_MENU_INT, + .data.int_menu.def = 0, + .data.int_menu.max = ARRAY_SIZE(adv7481_eval_pll_configurations) - 1, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_PA", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_CSI", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_MIPI_LANES, + .name = "CTRL_ID_MIPI_LANES", + .type = CRL_CTRL_TYPE_CUSTOM, + .data.std_data.min = 2, + .data.std_data.max = 4, + .data.std_data.step = 2, + .data.std_data.def = 4, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + .param.type = ICI_EXT_SD_PARAM_TYPE_INT32, + }, +}; + +static struct crl_sensor_configuration adv7481_eval_crl_configuration = { + + .powerup_regs_items = 0, + .powerup_regs = NULL, + + .poweroff_regs_items = 0, + .poweroff_regs = NULL, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = 0, + .subdevs = adv7481_eval_sensor_subdevs, + + .sensor_limits = &adv7481_eval_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_eval_pll_configurations), + .pll_configs = adv7481_eval_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_eval_modes), + .modes = adv7481_eval_modes, + + .streamon_regs_items = 0, + .streamon_regs = NULL, + + .streamoff_regs_items = 0, + .streamoff_regs = NULL, + + .ctrl_items = ARRAY_SIZE(adv7481_eval_ctrls), + .ctrl_bank = adv7481_eval_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_eval_crl_csi_data_fmt), + .csi_fmts = adv7481_eval_crl_csi_data_fmt, +}; + +static struct crl_sensor_configuration adv7481b_eval_crl_configuration = { + + .powerup_regs_items = 0, + .powerup_regs = NULL, + + .poweroff_regs_items = 0, + .poweroff_regs = NULL, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = 0, + .subdevs = adv7481b_eval_sensor_subdevs, + + .sensor_limits = &adv7481_eval_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_eval_pll_configurations), + .pll_configs = adv7481_eval_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_eval_modes), + .modes = adv7481_eval_modes, + + .streamon_regs_items = 0, + .streamon_regs = NULL, + + .streamoff_regs_items = 0, + .streamoff_regs = NULL, + + .ctrl_items = ARRAY_SIZE(adv7481_eval_ctrls), + .ctrl_bank = adv7481_eval_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_eval_crl_csi_data_fmt), + .csi_fmts = adv7481_eval_crl_csi_data_fmt, +}; + +#endif /* __CRLMODULE_ADV7481_EVAL_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.c b/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.c new file mode 100644 index 000000000000..80f668a462ea --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include "crlmodule.h" +#include "crlmodule-regs.h" + +#define CREATE_ATTRIBUTE(attr) \ + if (device_create_file(&client->dev, &attr) != 0) { \ + dev_err(&client->dev, "ADV7481 couldn't register %s sysfs entry.\n", \ + #attr); \ + } \ + +#define REMOVE_ATTRIBUTE(attr) \ + device_remove_file(&client->dev, &attr); + +/* Size of the mondello KSV buffer in bytes */ +#define ADV7481_KSV_BUFFER_SIZE 0x80 +/* Size of a single KSV */ +#define ADV7481_KSV_SIZE 0x05 +/* Max number of devices (MAX_MONDELO_KSV_SIZE / HDCP_KSV_SIZE */ +#define ADV7481_MAX_DEVICES 0x19 + +#define ADV7481_AKSV_UPDATE_A_ST 0x08 +#define ADV7481_CABLE_DET_A_ST 0x40 +#define ADV7481_V_LOCKED_A_ST 0x02 +#define ADV7481_DE_REGEN_A_ST 0x01 + +#define ADV7481_GPIO 456 + +/* + * Prevents executing another hot plug reset until current one will finish + */ +static unsigned int in_hot_plug_reset = 0; + +/* + * When hot plug reset is executed, HPA bit is deasserted for 2 seconds. + * This timer is used to assert HPA bit again after that time without blocking. + */ +static struct timer_list hot_plug_reset_timer; + +static struct workqueue_struct *irq_workqueue = NULL; +static int hdmi_res_width; +static int hdmi_res_height; +static int hdmi_res_interlaced; + +static DEFINE_MUTEX(hot_plug_reset_lock); + +typedef struct { + struct work_struct work; + struct i2c_client *client; +} irq_task_t; + +/* ADV7481 HDCP B-status register */ +struct adv7481_bstatus { + union { + __u8 bstatus[2]; + struct { + __u8 device_count:7; + __u8 max_devs_exceeded:1; + __u8 depth:3; + __u8 max_cascade_exceeded:1; + __u8 hdmi_mode:1; + __u8 hdmi_reserved_2:1; + __u8 rsvd:2; + }; + }; +}; + +struct adv7481_dev_info { + struct adv7481_bstatus bstatus; + __u8 ksv[ADV7481_KSV_BUFFER_SIZE]; +}; + +struct adv7481_bcaps { + union { + __u8 bcaps; + struct { + __u8 fast_reauth:1; + __u8 features:1; + __u8 reserved:2; + __u8 fast:1; + __u8 ksv_fifo_ready:1; + __u8 repeater:1; + __u8 hdmi_reserved:1; + }; + }; +}; + +static int adv_i2c_write(struct i2c_client *client, u16 i2c_addr, u16 reg, u8 val) +{ + struct ici_ext_subdev *subdev = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + return crlmodule_write_reg(sensor, i2c_addr, reg, 1, 0xFF, val); +} + +static int adv_i2c_read(struct i2c_client *client, u16 i2c_addr, u16 reg, u32 *val) +{ + struct ici_ext_subdev *subdev = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_register_read_rep read_reg; + + read_reg.address = reg; + read_reg.len = CRL_REG_LEN_08BIT; + read_reg.dev_i2c_addr = i2c_addr; + return crlmodule_read_reg(sensor, read_reg, val); +} + +/* + * Writes the HDCP BKSV list & status when the system acts + * as an HDCP 1.4 repeater + */ +static long adv_write_bksv(struct i2c_client *client, + struct adv7481_dev_info *dev_info) +{ + unsigned int k = 0; + int ret = 0; + u32 reg; + struct ici_ext_subdev *subdev = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + dev_dbg(&client->dev, "%s: Writing ADV7481 BKSV list.\n", __func__); + + /* Clear BCAPS KSV list ready */ + ret = adv_i2c_write(client, 0x64, 0x78, 0x01); + if (ret) { + dev_err(&client->dev, "%s: Error clearing BCAPS KSV list ready!\n", __func__); + return ret; + } + + /* KSV_LIST_READY_PORT_A KSV list not ready */ + ret = adv_i2c_write(client, 0x64, 0x69, 0x00); + if (ret) { + dev_err(&client->dev, "%s: Error clearing KSV_LIST_READY_PORT_A register!\n", __func__); + return ret; + } + + /* Write the BSKV list, one device at a time */ + /* Writing the entire list in one call exceeds frame size */ + for (k = 0; k < ADV7481_MAX_DEVICES; ++k) { + unsigned int j = k * ADV7481_KSV_SIZE; + struct crl_register_write_rep adv_ksv_cmd[] = { + {0x80 + j, CRL_REG_LEN_08BIT, dev_info->ksv[j + 0], 0x64}, + {0x81 + j, CRL_REG_LEN_08BIT, dev_info->ksv[j + 1], 0x64}, + {0x82 + j, CRL_REG_LEN_08BIT, dev_info->ksv[j + 2], 0x64}, + {0x83 + j, CRL_REG_LEN_08BIT, dev_info->ksv[j + 3], 0x64}, + {0x84 + j, CRL_REG_LEN_08BIT, dev_info->ksv[j + 4], 0x64}, + }; + ret = crlmodule_write_regs(sensor, adv_ksv_cmd, ARRAY_SIZE(adv_ksv_cmd)); + + if (ret) { + dev_err(&client->dev, "%s: Error while writing BKSV list!\n", __func__); + return ret; + } + } + + /* Finally update the bstatus registers */ + ret = adv_i2c_read(client, 0x64, 0x42, ®); + + if (ret) { + dev_err(&client->dev, "%s: Error reading bstatus register!\n", __func__); + return ret; + } + + /* ADV recommendation: only update bits [0:11] */ + /* Take the lower nibble (bits [11:8]) of the input bstatus */ + /* Take the upper nibble (bits [15:12]) of the current register */ + dev_info->bstatus.bstatus[1] = + (dev_info->bstatus.bstatus[1] & 0x0F) | (reg & 0xF0); + { + struct crl_register_write_rep adv_cmd[] = { + {0x41, CRL_REG_LEN_08BIT, dev_info->bstatus.bstatus[0], 0x64}, + {0x42, CRL_REG_LEN_08BIT, dev_info->bstatus.bstatus[1], 0x64}, + {0x69, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* KSV_LIST_READY_PORT_A */ + }; + + ret = crlmodule_write_regs(sensor, adv_cmd, ARRAY_SIZE(adv_cmd)); + } + + return ret; +} + +static ssize_t adv_bcaps_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 val; + int ret; + struct i2c_client* client = container_of(dev, struct i2c_client, dev); + + ret = adv_i2c_read(client, 0x64, 0x40, &val); + + if (ret != 0) { + return -EIO; + } + + val = val & 0xFF; + *buf = val; + return 1; +} + +/* Declares bcaps attribute that will be exposed to user space via sysfs */ +static DEVICE_ATTR(bcaps, S_IRUGO, adv_bcaps_show, NULL); + +/* + * Writes provided BKSV value from user space to chip. + * BKSV should be formatted as adv7481_dev_info struct, + * it does basic validation and checks if provided buffer size matches size of adv7481_dev_info struct. + * In case of error return EIO. + */ +static ssize_t adv_bksv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct adv7481_dev_info dev_info; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + dev_dbg(&client->dev, "%s\n", __func__); + if (count != sizeof (struct adv7481_dev_info)) { + return -EIO; + } + + dev_info = *((struct adv7481_dev_info*) buf); + + ret = adv_write_bksv(client, &dev_info); + + if (ret != 0) { + return -EIO; + } + + return count; +} + +/* Declares bksv attribute that will be exposed to user space via sysfs */ +static DEVICE_ATTR(bksv, S_IWUSR | S_IWGRP, NULL, adv_bksv_store); + +/* + * Enables HPA_MAN_VALUE_PORT_A to enable hot plug detection. + */ +static void adv_hpa_assert(struct work_struct *work) +{ + irq_task_t *task = (irq_task_t*) work; + struct i2c_client *client = task->client; + + adv_i2c_write(client, 0x68, 0xF8, 0x01); + in_hot_plug_reset = 0; + kfree(work); +} + +/* + * Handles hpa timer interrupt, defers enalbing of HPA to adv_hpa_assert + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) +static void adv_hpa_reset_callback(unsigned long data) +{ + irq_task_t *task = NULL; + + task = (irq_task_t*) kmalloc(sizeof(irq_task_t), GFP_ATOMIC); + if (task) { + INIT_WORK( (struct work_struct*) task, adv_hpa_assert); + task->client = (struct i2c_client*) data; + queue_work(irq_workqueue, (struct work_struct*)task); + } +} +#else +static void adv_hpa_reset_callback(struct timer_list *t) +{ + irq_task_t *task = NULL; + + task = (irq_task_t*) kmalloc(sizeof(irq_task_t), GFP_ATOMIC); + if (task) { + INIT_WORK( (struct work_struct*) task, adv_hpa_assert); + queue_work(irq_workqueue, (struct work_struct*)task); + } +} +#endif + +/* + * Reauthenticates HDCP by disabling hot plug detection for 2 seconds. + * It can be triggered by user space by writing any value to "reauthenticate" attribute. + * After that time connected source will automatically ask for HDCP authentication once again. + * To prevent sleep, timer is used to delay enabling of hot plug by 2 seconds. + * In case that previous reauthentication is not completed, returns EBUSY. + * In case of error returns EIO. + */ +static ssize_t adv_reauthenticate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + dev_dbg(&client->dev, "%s\n", __func__); + + mutex_lock(&hot_plug_reset_lock); + + if (in_hot_plug_reset) { + mutex_unlock(&hot_plug_reset_lock); + return -EBUSY; + } + + /* Clear BCAPS KSV list ready */ + ret = adv_i2c_write(client, 0x64, 0x78, 0x01); + if (ret != 0) { + dev_err(&client->dev, "%s: Error clearing BCAPS KSV list ready!\n", __func__); + mutex_unlock(&hot_plug_reset_lock); + return -EIO; + } + + /* KSV_LIST_READY_PORT_A KSV list not ready */ + ret = adv_i2c_write(client, 0x64, 0x69, 0x00); + if (ret != 0) { + dev_err(&client->dev, "%s: Error clearing KSV_LIST_READY_PORT_A register!\n", __func__); + mutex_unlock(&hot_plug_reset_lock); + return -EIO; + } + + ret = adv_i2c_write(client, 0x68, 0xF8, 0x00); + + if (ret != 0) { + mutex_unlock(&hot_plug_reset_lock); + return -EIO; + } + + in_hot_plug_reset = 1; + mod_timer(&hot_plug_reset_timer, jiffies + msecs_to_jiffies(2000)); + + mutex_unlock(&hot_plug_reset_lock); + return count; +} + +/* Declares reauthenticate attribute that will be exposed to user space via sysfs */ +static DEVICE_ATTR(reauthenticate, S_IWUSR | S_IWGRP, NULL, adv_reauthenticate_store); + +/* Dummy show to prevent WARN when registering aksv attribute */ +static ssize_t adv_aksv_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + (void) dev; + (void) attr; + (void) buf; + + return -EIO; +} + +/* Declares aksv attribute that will be exposed to user space via sysfs, to notify about AKSV events */ +static DEVICE_ATTR(aksv, S_IRUGO, adv_aksv_show, NULL); + + +static ssize_t adv_hdmi_cable_connected_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char interlaced = 'p'; + if (hdmi_res_interlaced) { + interlaced = 'i'; + } + + return snprintf(buf, 20, "%dx%d%c", hdmi_res_width, hdmi_res_height, interlaced); +} +static DEVICE_ATTR(hdmi_cable_connected, S_IRUGO, adv_hdmi_cable_connected_show, NULL); + +static ssize_t adv_bstatus_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + u32 b0, b1; + int ret; + struct i2c_client* client = container_of(dev, struct i2c_client, dev); + dev_dbg(&client->dev, "Getting bstatus\n"); + ret = adv_i2c_read(client, 0x64, 0x41, &b0); + if (ret != 0) { + dev_err(&client->dev, "Error getting bstatus(0)\n"); + return -EIO; + } + dev_dbg(&client->dev, "btatus(0): 0x%x\n", b0 & 0xff); + ret = adv_i2c_read(client, 0x64, 0x42, &b1); + if (ret != 0) { + dev_err(&client->dev, "Error getting bstatus(1)\n"); + return -EIO; + } + dev_dbg(&client->dev, "bstatus(1): 0x%x\n", b1 & 0xff); + *buf = b0 & 0xff; + buf++; + *buf = b1 & 0xff; + return 2; +} +static DEVICE_ATTR(bstatus, S_IRUGO, adv_bstatus_show, NULL); + +// irq GPIO ping unavailable on ACRN UOS +#if (!IS_ENABLED(CONFIG_VIDEO_INTEL_UOS)) +static void adv_isr_bh(struct work_struct *work) +{ + irq_task_t *task = (irq_task_t*) work; + struct i2c_client *client = task->client; + + u32 interrupt_st; + u32 raw_value; + u32 temp[3]; + int ret = 0; + + struct crl_register_read_rep reg; + reg.address = 0x90; + reg.len = CRL_REG_LEN_08BIT; + reg.mask = 0xFF; + reg.dev_i2c_addr = 0xE0; + + dev_dbg(&client->dev, "%s\n", __func__); + + /* AKSV_UPDATE_A_ST: check interrupt status */ + ret = adv_i2c_read(client, 0xE0, 0x90, &interrupt_st); + + if (interrupt_st & 0x08 /*ADV7481_AKSV_UPDATE_A_ST*/) { + dev_dbg(&client->dev, "%s: ADV7481 ISR: AKSV_UPDATE_A_ST: 0x%x\n", + __func__, interrupt_st); + + /* Notify user space about AKSV event */ + sysfs_notify(&client->dev.kobj, NULL, "aksv"); + + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x91, 0x08); + } + + /* Check interrupt status for: CABLE_DET_A_ST, V_LOCKED_A_ST and DE_REGEN_LCK_A_ST */ + ret = adv_i2c_read(client, 0xE0, 0x72, &interrupt_st); + + /* If any of CABLE_DET_A_ST, V_LOCKED_A_ST and DE_REGEN_LCK_A_ST interrupts was set, + * get updated values of CABLE_DET_RAW, V_LOCKED_RAW and DE_REGEN_LCK_RAW + */ + if (interrupt_st) { + ret = adv_i2c_read(client, 0xE0, 0x71, &raw_value); + } + + /* Check CABLE_DET_A_ST interrupt */ + if ((interrupt_st & ADV7481_CABLE_DET_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x40); + + /* HDMI cable is connected */ + if (raw_value & ADV7481_CABLE_DET_A_ST) { + dev_dbg(&client->dev, "%s: ADV7481 ISR: HDMI cable connected\n", __func__); + ret = adv_i2c_write(client, 0xE0, 0x10, 0xA1); + } + else { + dev_dbg(&client->dev, "%s: ADV7481 ISR: HDMI cable disconnected\n", __func__); + } + } + + /* Check V_LOCKED_A_ST interrupt */ + if((interrupt_st & ADV7481_V_LOCKED_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x02); + /* Vertical sync filter has been locked, resolution height can be read */ + if (raw_value & ADV7481_V_LOCKED_A_ST) { + dev_dbg(&client->dev, "%s: ADV7481 ISR: Vertical Sync Filter Locked\n", __func__); + reg.dev_i2c_addr = 0x68; //HDMI_RX_MAP; + reg.address = 0x09; + adv_i2c_read(client, 0x68, 0x09, &temp[0]); + adv_i2c_read(client, 0x68, 0x0A, &temp[1]); + adv_i2c_read(client, 0x68, 0x0B, &temp[2]); + + temp[0] = temp[0] & 0x1F; + hdmi_res_height = (temp[0]<<8) + temp[1]; + if (temp[2] & 0x20) { + hdmi_res_height = hdmi_res_height << 1; + hdmi_res_interlaced = 1; + } + else { + hdmi_res_interlaced = 0; + } + + /* If resolution width was already read, notify user space about new resolution */ + if (hdmi_res_width) { + sysfs_notify(&client->dev.kobj, NULL, "hdmi_cable_connected"); + } + } + else { + dev_dbg(&client->dev, "%s: ADV7481 ISR: Vertical Sync Filter Lost\n", __func__); + hdmi_res_height = 0; + /* Notify user space about losing resolution */ + if (!hdmi_res_width) { + sysfs_notify(&client->dev.kobj, NULL, "hdmi_cable_connected"); + } + } + } + + /* Check DE_REGEN_A_ST interrupt */ + if((interrupt_st & ADV7481_DE_REGEN_A_ST)) { + /* Clear interrupt bit */ + ret = adv_i2c_write(client, 0xE0, 0x73, 0x01); + + /* DE regeneration has been locked, resolution height can be read */ + if (raw_value & ADV7481_DE_REGEN_A_ST) { + dev_dbg(&client->dev, "%s: ADV7481 ISR: DE Regeneration Locked\n", __func__); + reg.dev_i2c_addr = 0x68; //HDMI_RX_MAP; + reg.address = 0x07; + adv_i2c_read(client, 0x68, 0x07, &temp[0]); + adv_i2c_read(client, 0x68, 0x08, &temp[1]); + + temp[0] = temp[0] & 0x1F; + hdmi_res_width = (temp[0]<<8) + temp[1]; + + /* If resolution height was already read back, notify user space about new resolution */ + if (hdmi_res_height) { + sysfs_notify(&client->dev.kobj, NULL, "hdmi_cable_connected"); + } + } + else { + dev_dbg(&client->dev, "%s: ADV7481 ISR: DE Regeneration Lost\n", __func__); + hdmi_res_width = 0; + /* Notfiy user space about losing resolution */ + if (!hdmi_res_height) { + sysfs_notify(&client->dev.kobj, NULL, "hdmi_cable_connected"); + } + } + } +} + +static irq_handler_t adv7481_irq_handler(unsigned int irq, void *dev_id, + struct pt_regs *regs) +{ + irq_task_t *task = NULL; + struct i2c_client *client = (struct i2c_client*)dev_id; + + dev_dbg(&client->dev, "%s: Interrupt in ADV7481\n", __func__); + + task = (irq_task_t*) kmalloc(sizeof(irq_task_t), GFP_ATOMIC); + if (task) { + INIT_WORK( (struct work_struct*) task, adv_isr_bh); + task->client = client; + queue_work(irq_workqueue, (struct work_struct*)task); + } + + return (irq_handler_t)IRQ_HANDLED; +} + +static int unregister_gpio_irq(void) +{ + gpio_free(ADV7481_GPIO); + return 0; +} + +static int register_gpio_irq(struct i2c_client *client) +{ + int res = 0; + unsigned int irq; + + + if (!gpio_is_valid(ADV7481_GPIO)) { + dev_err(&client->dev, "%s: ADV7481 GPIO pin %d is invalid!\n", + __func__, ADV7481_GPIO); + return -ENODEV; + } else { + dev_dbg(&client->dev, "%s: GPIO %d is valid.\n", __func__, ADV7481_GPIO); + } + + res = gpio_request(ADV7481_GPIO, "ADV7481 Interrupt"); + if (res) { + dev_err(&client->dev, "%s: ADV7481 GPIO pin request failed!\n", __func__); + return -ENODEV; + } + + gpio_direction_input(ADV7481_GPIO); + irq = gpio_to_irq(ADV7481_GPIO); + res = request_irq(irq, + (irq_handler_t)adv7481_irq_handler, + IRQF_TRIGGER_RISING, + "adv7481_irq_handler", + client); + + dev_dbg(&client->dev, "%s: GPIO register GPIO IRQ result: %d\n", __func__, res); + + return res; +} +#endif + +int adv7481_sensor_init(struct i2c_client *client) +{ + dev_dbg(&client->dev, "%s ADV7481_sensor_init\n", __func__); + irq_workqueue = create_workqueue("adv7481_irq_workqueue"); +// irq GPIO ping unavailable on ACRN UOS +#if (!IS_ENABLED(CONFIG_VIDEO_INTEL_UOS)) + register_gpio_irq(client); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) + setup_timer(&hot_plug_reset_timer, adv_hpa_reset_callback, (unsigned long) client); +#else + timer_setup(&hot_plug_reset_timer, adv_hpa_reset_callback, 0); +#endif + CREATE_ATTRIBUTE(dev_attr_hdmi_cable_connected); + CREATE_ATTRIBUTE(dev_attr_bcaps); + CREATE_ATTRIBUTE(dev_attr_aksv); + CREATE_ATTRIBUTE(dev_attr_bksv); + CREATE_ATTRIBUTE(dev_attr_reauthenticate); + CREATE_ATTRIBUTE(dev_attr_bstatus); + + return 0; +} + +int adv7481_sensor_cleanup(struct i2c_client *client) +{ + dev_dbg(&client->dev, "%s: ADV7481_sensor_cleanup\n", __func__); + if (irq_workqueue != NULL) { + free_irq(gpio_to_irq(ADV7481_GPIO), client); +// irq GPIO ping unavailable on ACRN UOS +#if (!IS_ENABLED(CONFIG_VIDEO_INTEL_UOS)) + unregister_gpio_irq(); +#endif + del_timer(&hot_plug_reset_timer); + flush_workqueue(irq_workqueue); + destroy_workqueue(irq_workqueue); + irq_workqueue = NULL; + } + REMOVE_ATTRIBUTE(dev_attr_bstatus); + REMOVE_ATTRIBUTE(dev_attr_reauthenticate); + REMOVE_ATTRIBUTE(dev_attr_bksv); + REMOVE_ATTRIBUTE(dev_attr_aksv); + REMOVE_ATTRIBUTE(dev_attr_bcaps); + REMOVE_ATTRIBUTE(dev_attr_hdmi_cable_connected); + return 0; +} diff --git a/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.h b/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.h new file mode 100644 index 000000000000..489a3eb97878 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_adv7481_hdmi_configuration.h @@ -0,0 +1,942 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ +#define __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +static struct crl_register_write_rep adv7481_hdmi_onetime_init_regset[] = { + {0xFF, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x01, CRL_REG_LEN_08BIT, 0x76, 0xE0}, /* ADI Required Write */ + {0x05, CRL_REG_LEN_08BIT, 0x96, 0xE0}, /* Setting Vid_Std to + 1600x1200(UXGA)@60 */ + {0xF2, CRL_REG_LEN_08BIT, 0x01, 0xE0}, /* Enable I2C Read + Auto-Increment */ + {0xF3, CRL_REG_LEN_08BIT, 0x4C, 0xE0}, /* DPLL Map Address + Set to 0x4C */ + {0xF4, CRL_REG_LEN_08BIT, 0x44, 0xE0}, /* CP Map Address + Set to 0x44 */ + {0xF5, CRL_REG_LEN_08BIT, 0x68, 0xE0}, /* HDMI RX Map Address + Set to 0x68 */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* EDID Map Address + Set to 0x6C */ + {0xF7, CRL_REG_LEN_08BIT, 0x64, 0xE0}, /* HDMI RX Repeater Map Address + Set to 0x64 */ + {0xF8, CRL_REG_LEN_08BIT, 0x62, 0xE0}, /* HDMI RX Infoframe Map Address + Set to 0x62 */ + {0xF9, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CBUS Map Address + Set to 0xF0 */ + {0xFA, CRL_REG_LEN_08BIT, 0x82, 0xE0}, /* CEC Map Address + Set to 0x82 */ + {0xFB, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, /* SDP Main Map Address + Set to 0xF2 */ + {0xFC, CRL_REG_LEN_08BIT, 0x90, 0xE0}, /* CSI-TXB Map Address + Set to 0x90 */ + {0xFD, CRL_REG_LEN_08BIT, 0x94, 0xE0}, /* CSI-TXA Map Address + Set to 0x94 */ + {0x00, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* Disable chip powerdown & + Enable HDMI Rx block */ + + {0x40, CRL_REG_LEN_08BIT, 0xC3, 0x64}, /* Enable HDCP 1.1 Repeater */ + {0x69, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* KSV List not ready port A */ + {0x77, CRL_REG_LEN_08BIT, 0x08, 0x64}, /* Clear KSV List */ + {0x78, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* KSV_LIST_READY_CLR_A: + Clears the BCAPS ready bit */ + {0x68, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Disable dual ksv list + for port A */ + {0x41, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Reset b-status (1) */ + {0x42, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Reset b-status (2) */ + {0x91, CRL_REG_LEN_08BIT, 0x08, 0xE0}, /* AKSV Update Clear */ + + {0x00, CRL_REG_LEN_08BIT, 0x08, 0x68}, /* Foreground Channel = A */ + {0x98, CRL_REG_LEN_08BIT, 0xFF, 0x68}, /* ADI Required Write */ + {0x99, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI Required Write */ + {0x9A, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI Required Write */ + {0x9B, CRL_REG_LEN_08BIT, 0x0A, 0x68}, /* ADI Required Write */ + {0x9D, CRL_REG_LEN_08BIT, 0x40, 0x68}, /* ADI Required Write */ + {0xCB, CRL_REG_LEN_08BIT, 0x09, 0x68}, /* ADI Required Write */ + {0x3D, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI Required Write */ + {0x3E, CRL_REG_LEN_08BIT, 0x7B, 0x68}, /* ADI Required Write */ + {0x3F, CRL_REG_LEN_08BIT, 0x5E, 0x68}, /* ADI Required Write */ + {0x4E, CRL_REG_LEN_08BIT, 0xFE, 0x68}, /* ADI Required Write */ + {0x4F, CRL_REG_LEN_08BIT, 0x18, 0x68}, /* ADI Required Write */ + {0x57, CRL_REG_LEN_08BIT, 0xA3, 0x68}, /* ADI Required Write */ + {0x58, CRL_REG_LEN_08BIT, 0x04, 0x68}, /* ADI Required Write */ + {0x85, CRL_REG_LEN_08BIT, 0x10, 0x68}, /* ADI Required Write */ + {0x83, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Enable All Terminatio ns */ + {0xA3, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* ADI Required Write */ + {0xBE, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* ADI Required Write */ + {0x6C, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Manual Enable */ + {0xF8, CRL_REG_LEN_08BIT, 0x01, 0x68}, /* HPA Asserted */ + {0x0F, CRL_REG_LEN_08BIT, 0x00, 0x68}, /* Audio Mute Speed + Set to Fastest (Smallest Step Size) */ + {0x0E, CRL_REG_LEN_08BIT, 0xFF, 0xE0}, /* LLC/PIX/AUD/SPI PINS + TRISTATED */ + + {0x74, CRL_REG_LEN_08BIT, 0x43, 0xE0}, /* Enable interrupts */ + {0x75, CRL_REG_LEN_08BIT, 0x43, 0xE0}, + + {0x70, CRL_REG_LEN_08BIT, 0xA0, 0x64}, /* Write primary edid size */ + {0x74, CRL_REG_LEN_08BIT, 0x01, 0x64}, /* Enable manual edid */ + {0x7A, CRL_REG_LEN_08BIT, 0x00, 0x64}, /* Write edid sram select */ + {0xF6, CRL_REG_LEN_08BIT, 0x6C, 0xE0}, /* Write edid map bus address */ + + {0x00*4, CRL_REG_LEN_32BIT, 0x00FFFFFF, 0x6C}, /* EDID programming */ + {0x01*4, CRL_REG_LEN_32BIT, 0xFFFFFF00, 0x6C}, /* EDID programming */ + {0x02*4, CRL_REG_LEN_32BIT, 0x4DD90100, 0x6C}, /* EDID programming */ + {0x03*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x04*4, CRL_REG_LEN_32BIT, 0x00110103, 0x6C}, /* EDID programming */ + {0x05*4, CRL_REG_LEN_32BIT, 0x80000078, 0x6C}, /* EDID programming */ + {0x06*4, CRL_REG_LEN_32BIT, 0x0A0DC9A0, 0x6C}, /* EDID programming */ + {0x07*4, CRL_REG_LEN_32BIT, 0x57479827, 0x6C}, /* EDID programming */ + {0x08*4, CRL_REG_LEN_32BIT, 0x12484C00, 0x6C}, /* EDID programming */ + {0x09*4, CRL_REG_LEN_32BIT, 0x00000101, 0x6C}, /* EDID programming */ + {0x0A*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0B*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0C*4, CRL_REG_LEN_32BIT, 0x01010101, 0x6C}, /* EDID programming */ + {0x0D*4, CRL_REG_LEN_32BIT, 0x0101011D, 0x6C}, /* EDID programming */ + {0x0E*4, CRL_REG_LEN_32BIT, 0x80D0721C, 0x6C}, /* EDID programming */ + {0x0F*4, CRL_REG_LEN_32BIT, 0x1620102C, 0x6C}, /* EDID programming */ + {0x10*4, CRL_REG_LEN_32BIT, 0x2580C48E, 0x6C}, /* EDID programming */ + {0x11*4, CRL_REG_LEN_32BIT, 0x2100009E, 0x6C}, /* EDID programming */ + {0x12*4, CRL_REG_LEN_32BIT, 0x011D8018, 0x6C}, /* EDID programming */ + {0x13*4, CRL_REG_LEN_32BIT, 0x711C1620, 0x6C}, /* EDID programming */ + {0x14*4, CRL_REG_LEN_32BIT, 0x582C2500, 0x6C}, /* EDID programming */ + {0x15*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x16*4, CRL_REG_LEN_32BIT, 0x009E0000, 0x6C}, /* EDID programming */ + {0x17*4, CRL_REG_LEN_32BIT, 0x00FC0048, 0x6C}, /* EDID programming */ + {0x18*4, CRL_REG_LEN_32BIT, 0x444D4920, 0x6C}, /* EDID programming */ + {0x19*4, CRL_REG_LEN_32BIT, 0x4C4C430A, 0x6C}, /* EDID programming */ + {0x1A*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1B*4, CRL_REG_LEN_32BIT, 0x000000FD, 0x6C}, /* EDID programming */ + {0x1C*4, CRL_REG_LEN_32BIT, 0x003B3D0F, 0x6C}, /* EDID programming */ + {0x1D*4, CRL_REG_LEN_32BIT, 0x2D08000A, 0x6C}, /* EDID programming */ + {0x1E*4, CRL_REG_LEN_32BIT, 0x20202020, 0x6C}, /* EDID programming */ + {0x1F*4, CRL_REG_LEN_32BIT, 0x202001C1, 0x6C}, /* EDID programming */ + {0x20*4, CRL_REG_LEN_32BIT, 0x02031E77, 0x6C}, /* EDID programming */ + {0x21*4, CRL_REG_LEN_32BIT, 0x4F941305, 0x6C}, /* EDID programming */ + {0x22*4, CRL_REG_LEN_32BIT, 0x03040201, 0x6C}, /* EDID programming */ + {0x23*4, CRL_REG_LEN_32BIT, 0x16150706, 0x6C}, /* EDID programming */ + {0x24*4, CRL_REG_LEN_32BIT, 0x1110121F, 0x6C}, /* EDID programming */ + {0x25*4, CRL_REG_LEN_32BIT, 0x23090701, 0x6C}, /* EDID programming */ + {0x26*4, CRL_REG_LEN_32BIT, 0x65030C00, 0x6C}, /* EDID programming */ + {0x27*4, CRL_REG_LEN_32BIT, 0x10008C0A, 0x6C}, /* EDID programming */ + {0x28*4, CRL_REG_LEN_32BIT, 0xD0902040, 0x6C}, /* EDID programming */ + {0x29*4, CRL_REG_LEN_32BIT, 0x31200C40, 0x6C}, /* EDID programming */ + {0x2A*4, CRL_REG_LEN_32BIT, 0x5500138E, 0x6C}, /* EDID programming */ + {0x2B*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x2C*4, CRL_REG_LEN_32BIT, 0x011D00BC, 0x6C}, /* EDID programming */ + {0x2D*4, CRL_REG_LEN_32BIT, 0x52D01E20, 0x6C}, /* EDID programming */ + {0x2E*4, CRL_REG_LEN_32BIT, 0xB8285540, 0x6C}, /* EDID programming */ + {0x2F*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x30*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x31*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x32*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x33*4, CRL_REG_LEN_32BIT, 0x9600C48E, 0x6C}, /* EDID programming */ + {0x34*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x35*4, CRL_REG_LEN_32BIT, 0x011D0072, 0x6C}, /* EDID programming */ + {0x36*4, CRL_REG_LEN_32BIT, 0x51D01E20, 0x6C}, /* EDID programming */ + {0x37*4, CRL_REG_LEN_32BIT, 0x6E285500, 0x6C}, /* EDID programming */ + {0x38*4, CRL_REG_LEN_32BIT, 0xC48E2100, 0x6C}, /* EDID programming */ + {0x39*4, CRL_REG_LEN_32BIT, 0x001E8C0A, 0x6C}, /* EDID programming */ + {0x3A*4, CRL_REG_LEN_32BIT, 0xD08A20E0, 0x6C}, /* EDID programming */ + {0x3B*4, CRL_REG_LEN_32BIT, 0x2D10103E, 0x6C}, /* EDID programming */ + {0x3C*4, CRL_REG_LEN_32BIT, 0x9600138E, 0x6C}, /* EDID programming */ + {0x3D*4, CRL_REG_LEN_32BIT, 0x21000018, 0x6C}, /* EDID programming */ + {0x3E*4, CRL_REG_LEN_32BIT, 0x00000000, 0x6C}, /* EDID programming */ + {0x3F*4, CRL_REG_LEN_32BIT, 0x000000CB, 0x6C}, /* EDID programming */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_rgb565[] = { + {0x04, CRL_REG_LEN_08BIT, 0x02, 0xE0}, //RGB Out of CP + {0x12, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, //CSC Depends on ip Packets - SDR 444 + {0x17, CRL_REG_LEN_08BIT, 0xB8, 0xE0}, //Configure for RGB565 & Luma & Chroma Values Can Reach 254d + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, //CP-Insert_AV_Code + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, //ADI Required Write + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, //Enable LLC_DLL & Double LLC Timing + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, //LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled + {0x10, CRL_REG_LEN_08BIT, 0xC0, 0xE0}, //Enable 4-lane CSI Tx & Pixel Port + {0x7E, CRL_REG_LEN_08BIT, 0x98, 0x94}, //ADI Required Write +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_rgb888[] = { + {0x04, CRL_REG_LEN_08BIT, 0x02, 0xE0}, /* RGB Out of CP */ + {0x12, CRL_REG_LEN_08BIT, 0xF0, 0xE0}, /* CSC Depends on ip Packets - + SDR 444 */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, /* Luma & Chroma Values Can + Reach 254d */ + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, /* CP-Insert_AV_Code */ + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, /* Enable LLC_DLL & + Double LLC Timing */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, /* LLC/PIX/SPI PINS TRISTATED + AUD Outputs Enabled */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0x7E, CRL_REG_LEN_08BIT, 0x1B, 0x94}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_uyvy[] = { + {0x04, CRL_REG_LEN_08BIT, 0x00, 0xE0}, //YCrCb output + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, //CSC Depends on ip Packets - SDR422 set + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, //Luma & Chroma Values Can Reach 254d + {0x03, CRL_REG_LEN_08BIT, 0x86, 0xE0}, //CP-Insert_AV_Code + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, //ADI Required Write + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, //Enable LLC_DLL & Double LLC Timing + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, //LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled + {0x10, CRL_REG_LEN_08BIT, 0xC0, 0xE0}, //Enable 4-lane CSI Tx & Pixel Port + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, //Enable 4-lane MIPI + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, //Set Auto DPHY Timing + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, //ADI Required Write + {0x7E, CRL_REG_LEN_08BIT, 0x00, 0x94}, //ADI Required Write +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_yuyv[] = { + {0x1C, CRL_REG_LEN_08BIT, 0x3A, 0xE0}, /* Enable Interrupt*/ + {0x04, CRL_REG_LEN_08BIT, 0x40, 0xE0}, /* YCrCb output good=0xE0*/ + /* CSC Depends on ip Packets - SDR422 set */ + {0x12, CRL_REG_LEN_08BIT, 0xF2, 0xE0}, + /* Luma & Chroma Values Can Reach 254d */ + {0x17, CRL_REG_LEN_08BIT, 0x80, 0xE0}, + {0x7C, CRL_REG_LEN_08BIT, 0x00, 0x44}, /* ADI Required Write */ + {0x3E, CRL_REG_LEN_08BIT, 0x08, 0x44}, /* Invert order of Cb and Cr*/ + /* Enable LLC_DLL & Double LLC Timing */ + {0x0C, CRL_REG_LEN_08BIT, 0xE0, 0xE0}, + /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ + {0x0E, CRL_REG_LEN_08BIT, 0xDD, 0xE0}, + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0}, + /* Enable 4-lane CSI TXB & Pixel Port */ + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0x7E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* ADI Required Write */ +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_1080p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x5E, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 44 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_1080i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x54, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 44 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD4, 0x44}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_480p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x4A, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_720p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x53, 0xE0}, + {0x8B, CRL_REG_LEN_08BIT, 0x43, 0x44}, /* shift 40 pixel to right */ + {0x8C, CRL_REG_LEN_08BIT, 0xD8, 0x44}, + {0x8B, CRL_REG_LEN_08BIT, 0x4F, 0x44}, + {0x8D, CRL_REG_LEN_08BIT, 0xD8, 0x44}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_576p[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x4B, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_576i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x94}, /* Enable 1-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x41, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_480i[] = { + {0x00, CRL_REG_LEN_08BIT, 0x81, 0x94}, /* Enable 1-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA1, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x21, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x40, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_mode_vga[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0x10, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0xA0, 0xE0, 0xA0}, + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, + {0x1E, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, + {0xC9, CRL_REG_LEN_08BIT, 0x2D, 0x44}, + {0x05, CRL_REG_LEN_08BIT, 0x88, 0xE0}, + {0x00, CRL_REG_LEN_DELAY, 0x05, 0x00}, + {0x03, CRL_REG_LEN_08BIT, 0x00, 0xE0}, + {0x04, CRL_REG_LEN_08BIT | CRL_REG_READ_AND_UPDATE, 0x00, 0xE0, 0xFD}, + {0x37, CRL_REG_LEN_08BIT, 0x00, 0x44}, +}; + +static struct crl_register_write_rep adv7481_hdmi_powerup_regset[] = { + {0x00, CRL_REG_LEN_08BIT, 0x84, 0x94}, /* Enable 4-lane MIPI */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, /* Set Auto DPHY Timing */ + {0xDB, CRL_REG_LEN_08BIT, 0x10, 0x94}, /* ADI Required Write */ + {0xD6, CRL_REG_LEN_08BIT, 0x07, 0x94}, /* ADI Required Write */ + {0xC4, CRL_REG_LEN_08BIT, 0x0A, 0x94}, /* ADI Required Write */ + {0x71, CRL_REG_LEN_08BIT, 0x33, 0x94}, /* ADI Required Write */ + {0x72, CRL_REG_LEN_08BIT, 0x11, 0x94}, /* ADI Required Write */ + {0xF0, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* i2c_dphy_pwdn - 1'b0 */ + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Required Write */ + {0x1E, CRL_REG_LEN_08BIT, 0xC0, 0x94}, + /* ADI Required Write, transmit only Frame Start/End packets */ + {0xDA, CRL_REG_LEN_08BIT, 0x01, 0x94}, /* i2c_mipi_pll_en - 1'b1 */ +}; + +static struct crl_register_write_rep adv7481_hdmi_streamon_regs[] = { + {0x00, CRL_REG_LEN_DELAY, 0x02, 0x00}, + {0x00, CRL_REG_LEN_08BIT, 0x24, 0x94}, /* Power-up CSI-TX */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0xC1, CRL_REG_LEN_08BIT, 0x2B, 0x94}, /* ADI recommended setting */ + {0x00, CRL_REG_LEN_DELAY, 0x01, 0x00}, + {0x31, CRL_REG_LEN_08BIT, 0x80, 0x94}, /* ADI recommended setting */ +}; + +static struct crl_register_write_rep adv7481_hdmi_streamoff_regs[] = { + {0x31, CRL_REG_LEN_08BIT, 0x82, 0x94}, /* ADI Recommended Write */ + {0x1E, CRL_REG_LEN_08BIT, 0x00, 0x94}, /* Reset the clock Lane */ + {0x00, CRL_REG_LEN_08BIT, 0xA4, 0x94}, + {0xDA, CRL_REG_LEN_08BIT, 0x00, 0x94}, + /* i2c_mipi_pll_en -1'b0 Disable MIPI PLL */ + {0xC1, CRL_REG_LEN_08BIT, 0x3B, 0x94}, +}; + +static struct crl_pll_configuration adv7481_hdmi_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 297000000, + .bitsperpixel = 16, + .pixel_rate_csi = 594000000, + .pixel_rate_pa = 594000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 445500000, + .bitsperpixel = 24, + .pixel_rate_csi = 891000000, + .pixel_rate_pa = 891000000, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_1080p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_720p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_VGA_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 640, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_1080i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 540, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_480p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 480, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_480i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 240, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_576p_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 576, + }, +}; + +static struct crl_subdev_rect_rep adv7481_hdmi_576i_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1920, + .out_rect.height = 1080, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1920, + .in_rect.height = 1080, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 720, + .out_rect.height = 288, + }, +}; +static struct crl_mode_rep adv7481_hdmi_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_1080p_rects), + .sd_rects = adv7481_hdmi_1080p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1920, + .height = 1080, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_1080p), + .mode_regs = adv7481_hdmi_mode_1080p, + .comp_items = 0, + .ctrl_data = 0, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_720p_rects), + .sd_rects = adv7481_hdmi_720p_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_720p), + .mode_regs = adv7481_hdmi_mode_720p, + .comp_items = 0, + .ctrl_data = 0, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_VGA_rects), + .sd_rects = adv7481_hdmi_VGA_rects, + .binn_hor = 3, + .binn_vert = 2, + .scale_m = 1, + .width = 640, + .height = 480, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_vga), + .mode_regs = adv7481_hdmi_mode_vga, + .comp_items = 0, + .ctrl_data = 0, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_1080i_rects), + .sd_rects = adv7481_hdmi_1080i_rects, + .binn_hor = 1, + .binn_vert = 2, + .scale_m = 1, + .width = 1920, + .height = 540, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_1080i), + .mode_regs = adv7481_hdmi_mode_1080i, + .comp_items = 0, + .ctrl_data = 0, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_480p_rects), + .sd_rects = adv7481_hdmi_480p_rects, + .binn_hor = 2, + .binn_vert = 2, + .scale_m = 1, + .width = 720, + .height = 480, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_480p), + .mode_regs = adv7481_hdmi_mode_480p, + .comp_items = 0, + .ctrl_data = 0, + }, + + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_480i_rects), + .sd_rects = adv7481_hdmi_480i_rects, + .binn_hor = 2, + .binn_vert = 4, + .scale_m = 1, + .width = 720, + .height = 240, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_480i), + .mode_regs = adv7481_hdmi_mode_480i, + .comp_items = 0, + .ctrl_data = 0, + }, + + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_576p_rects), + .sd_rects = adv7481_hdmi_576p_rects, + .binn_hor = 2, + .binn_vert = 1, + .scale_m = 1, + .width = 720, + .height = 576, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_576p), + .mode_regs = adv7481_hdmi_mode_576p, + .comp_items = 0, + .ctrl_data = 0, + }, + { + .sd_rects_items = ARRAY_SIZE(adv7481_hdmi_576i_rects), + .sd_rects = adv7481_hdmi_576i_rects, + .binn_hor = 2, + .binn_vert = 3, + .scale_m = 1, + .width = 720, + .height = 288, + .mode_regs_items = ARRAY_SIZE(adv7481_hdmi_mode_576i), + .mode_regs = adv7481_hdmi_mode_576i, + .comp_items = 0, + .ctrl_data = 0, + }, +}; + +static struct crl_sensor_subdev_config adv7481_hdmi_sensor_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "adv7481 hdmi binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "adv7481 hdmi pixel array", + }, +}; + +static struct crl_sensor_limits adv7481_hdmi_sensor_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1920, + .y_addr_max = 1080, + .min_frame_length_lines = 160, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 6024, + .max_line_length_pixels = 32752, + .scaler_m_min = 1, + .scaler_m_max = 1, + .scaler_n_min = 1, + .scaler_n_max = 1, + .min_even_inc = 1, + .max_even_inc = 1, + .min_odd_inc = 1, + .max_odd_inc = 1, +}; + +static struct crl_csi_data_fmt adv7481_hdmi_crl_csi_data_fmt[] = { + { + .code = ICI_FORMAT_RGB565, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_rgb565), + .regs = adv7481_hdmi_mode_rgb565, + }, + { + .code = ICI_FORMAT_UYVY, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_uyvy), + .regs = adv7481_hdmi_mode_uyvy, + }, + { + .code = ICI_FORMAT_YUYV, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 16, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_yuyv), + .regs = adv7481_hdmi_mode_yuyv, + }, + { + .code = ICI_FORMAT_RGB888, + .pixel_order = CRL_PIXEL_ORDER_GRBG, + .bits_per_pixel = 24, + .regs_items = ARRAY_SIZE(adv7481_hdmi_mode_rgb888), + .regs = adv7481_hdmi_mode_rgb888, + }, +}; + +static struct crl_ctrl_data adv7481_hdmi_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .name = "CTRL_ID_LINK_FREQ", + .type = CRL_CTRL_TYPE_MENU_INT, + .data.int_menu.def = 0, + .data.int_menu.max = ARRAY_SIZE(adv7481_hdmi_pll_configurations) - 1, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_PA", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_CSI", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = 0, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +int adv7481_sensor_init(struct i2c_client*); +int adv7481_sensor_cleanup(struct i2c_client*); + +static struct crl_sensor_configuration adv7481_hdmi_crl_configuration = { + + .sensor_init = adv7481_sensor_init, + .sensor_cleanup = adv7481_sensor_cleanup, + + .onetime_init_regs_items = ARRAY_SIZE(adv7481_hdmi_onetime_init_regset), + .onetime_init_regs = adv7481_hdmi_onetime_init_regset, + + .powerup_regs_items = ARRAY_SIZE(adv7481_hdmi_powerup_regset), + .powerup_regs = adv7481_hdmi_powerup_regset, + + .poweroff_regs_items = ARRAY_SIZE(adv7481_hdmi_streamoff_regs), + .poweroff_regs = adv7481_hdmi_streamoff_regs, + + .id_reg_items = 0, + .id_regs = NULL, + + .subdev_items = ARRAY_SIZE(adv7481_hdmi_sensor_subdevs), + .subdevs = adv7481_hdmi_sensor_subdevs, + + .sensor_limits = &adv7481_hdmi_sensor_limits, + + .pll_config_items = ARRAY_SIZE(adv7481_hdmi_pll_configurations), + .pll_configs = adv7481_hdmi_pll_configurations, + + .modes_items = ARRAY_SIZE(adv7481_hdmi_modes), + .modes = adv7481_hdmi_modes, + + .streamon_regs_items = ARRAY_SIZE(adv7481_hdmi_streamon_regs), + .streamon_regs = adv7481_hdmi_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(adv7481_hdmi_streamoff_regs), + .streamoff_regs = adv7481_hdmi_streamoff_regs, + + .ctrl_items = ARRAY_SIZE(adv7481_hdmi_ctrls), + .ctrl_bank = adv7481_hdmi_ctrls, + + .csi_fmts_items = ARRAY_SIZE(adv7481_hdmi_crl_csi_data_fmt), + .csi_fmts = adv7481_hdmi_crl_csi_data_fmt, + + .addr_len = CRL_ADDR_7BIT, +}; + +#endif /* __CRLMODULE_ADV7481_HDMI_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crl_magna_configuration_ti964.h b/drivers/media/i2c/crlmodule-lite/crl_magna_configuration_ti964.h new file mode 100644 index 000000000000..c8d0d7b3550d --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crl_magna_configuration_ti964.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_MAGNA_TI964_CONFIGURATION_H_ +#define __CRLMODULE_MAGNA_TI964_CONFIGURATION_H_ + +#include "crlmodule-sensor-ds.h" + +#define TI964_I2C_PHY_ADDR 0x3d + +static struct crl_pll_configuration magna_ti964_pll_configurations[] = { + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 16, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 10, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + }, + { + .input_clk = 24000000, + .op_sys_clk = 400000000, + .bitsperpixel = 20, + .pixel_rate_csi = 529000000, + .pixel_rate_pa = 529000000, /* pixel_rate = MIPICLK*2 *4/12 */ + .csi_lanes = 4, + .comp_items = 0, + .ctrl_data = 0, + .pll_regs_items = 0, + .pll_regs = NULL, + } +}; + +static struct crl_subdev_rect_rep magna_ti964_1280_720_rects[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .in_rect.left = 0, + .in_rect.top = 0, + .in_rect.width = 1280, + .in_rect.height = 720, + .out_rect.left = 0, + .out_rect.top = 0, + .out_rect.width = 1280, + .out_rect.height = 720, + }, +}; + +static struct crl_register_write_rep magna_ti964_powerup_regs[] = { + {0x4c, CRL_REG_LEN_08BIT, 0x1, TI964_I2C_PHY_ADDR}, /* Select RX port 0 */ +}; + +static struct crl_register_write_rep magna_ti964_poweroff_regs[] = { + {0x1, CRL_REG_LEN_08BIT, 0x20, TI964_I2C_PHY_ADDR}, +}; + +static struct crl_mode_rep magna_ti964_modes[] = { + { + .sd_rects_items = ARRAY_SIZE(magna_ti964_1280_720_rects), + .sd_rects = magna_ti964_1280_720_rects, + .binn_hor = 1, + .binn_vert = 1, + .scale_m = 1, + .width = 1280, + .height = 720, + .min_llp = 2250, + .min_fll = 1320, + }, +}; + +static struct crl_sensor_subdev_config magna_ti964_subdevs[] = { + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "ti964", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_BINNER, + .name = "magna binner", + }, + { + .subdev_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .name = "magna pixel array", + } +}; + +static struct crl_sensor_limits magna_ti964_limits = { + .x_addr_min = 0, + .y_addr_min = 0, + .x_addr_max = 1280, + .y_addr_max = 720, + .min_frame_length_lines = 240, + .max_frame_length_lines = 65535, + .min_line_length_pixels = 320, + .max_line_length_pixels = 32752, +}; + +static struct crl_csi_data_fmt magna_ti964_crl_csi_data_fmt[] = { + { + .code = ICI_FORMAT_YUYV, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, + { + .code = ICI_FORMAT_UYVY, + .pixel_order = CRL_PIXEL_ORDER_IGNORE, + .bits_per_pixel = 16, + }, +}; + +static struct crl_ctrl_data magna_ti964_ctrls[] = { + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_SET_OP, + .context = SENSOR_IDLE, + .ctrl_id = ICI_EXT_SD_PARAM_ID_LINK_FREQ, + .name = "CTRL_ID_LINK_FREQ", + .type = CRL_CTRL_TYPE_MENU_INT, + .data.int_menu.def = 0, + .data.int_menu.max = 0, + .data.int_menu.menu = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_PIXEL_ARRAY, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_PA", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, + { + .sd_type = CRL_SUBDEV_TYPE_BINNER, + .op_type = CRL_CTRL_GET_OP, + .context = SENSOR_POWERED_ON, + .ctrl_id = ICI_EXT_SD_PARAM_ID_PIXEL_RATE, + .name = "CTRL_ID_PIXEL_RATE_CSI", + .type = CRL_CTRL_TYPE_INTEGER, + .data.std_data.min = 0, + .data.std_data.max = INT_MAX, + .data.std_data.step = 1, + .data.std_data.def = 0, + .flags = 0, + .impact = CRL_IMPACTS_NO_IMPACT, + .regs_items = 0, + .regs = 0, + .dep_items = 0, + .dep_ctrls = 0, + }, +}; + +struct crl_register_write_rep magna_ti964_streamon_regs[] = { + {0x1f, CRL_REG_LEN_08BIT, 0x2, TI964_I2C_PHY_ADDR}, + {0x33, CRL_REG_LEN_08BIT, 0x1, TI964_I2C_PHY_ADDR}, + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x7c, CRL_REG_LEN_08BIT, 0x80, TI964_I2C_PHY_ADDR}, + {0x20, CRL_REG_LEN_08BIT, 0xe0, TI964_I2C_PHY_ADDR}, +}; + +struct crl_register_write_rep magna_ti964_streamoff_regs[] = { + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x7c, CRL_REG_LEN_08BIT, 0x81, TI964_I2C_PHY_ADDR}, + {0x20, CRL_REG_LEN_08BIT, 0xf0, TI964_I2C_PHY_ADDR}, +}; + +struct crl_register_write_rep magna_ti964_onetime_init_regs[] = { + {0x8, CRL_REG_LEN_08BIT, 0x1c, TI964_I2C_PHY_ADDR}, + {0xa, CRL_REG_LEN_08BIT, 0x79, TI964_I2C_PHY_ADDR}, + {0xb, CRL_REG_LEN_08BIT, 0x79, TI964_I2C_PHY_ADDR}, + {0xd, CRL_REG_LEN_08BIT, 0xb9, TI964_I2C_PHY_ADDR}, + {0x10, CRL_REG_LEN_08BIT, 0x91, TI964_I2C_PHY_ADDR}, + {0x11, CRL_REG_LEN_08BIT, 0x85, TI964_I2C_PHY_ADDR}, + {0x12, CRL_REG_LEN_08BIT, 0x89, TI964_I2C_PHY_ADDR}, + {0x13, CRL_REG_LEN_08BIT, 0xc1, TI964_I2C_PHY_ADDR}, + {0x17, CRL_REG_LEN_08BIT, 0xe1, TI964_I2C_PHY_ADDR}, + {0x18, CRL_REG_LEN_08BIT, 0x0, TI964_I2C_PHY_ADDR}, /* Disable frame sync. */ + {0x19, CRL_REG_LEN_08BIT, 0x0, TI964_I2C_PHY_ADDR}, /* Frame sync high time. */ + {0x1a, CRL_REG_LEN_08BIT, 0x2, TI964_I2C_PHY_ADDR}, + {0x1b, CRL_REG_LEN_08BIT, 0xa, TI964_I2C_PHY_ADDR}, /* Frame sync low time. */ + {0x1c, CRL_REG_LEN_08BIT, 0xd3, TI964_I2C_PHY_ADDR}, + {0x21, CRL_REG_LEN_08BIT, 0x43, TI964_I2C_PHY_ADDR}, /* Enable best effort mode. */ + {0xb0, CRL_REG_LEN_08BIT, 0x10, TI964_I2C_PHY_ADDR}, + {0xb1, CRL_REG_LEN_08BIT, 0x14, TI964_I2C_PHY_ADDR}, + {0xb2, CRL_REG_LEN_08BIT, 0x1f, TI964_I2C_PHY_ADDR}, + {0xb3, CRL_REG_LEN_08BIT, 0x8, TI964_I2C_PHY_ADDR}, + {0x32, CRL_REG_LEN_08BIT, 0x1, TI964_I2C_PHY_ADDR}, /* Select CSI port 0 */ + {0x4c, CRL_REG_LEN_08BIT, 0x1, TI964_I2C_PHY_ADDR}, /* Select RX port 0 */ + {0x58, CRL_REG_LEN_08BIT, 0x58, TI964_I2C_PHY_ADDR}, + {0x5c, CRL_REG_LEN_08BIT, 0x18, TI964_I2C_PHY_ADDR}, /* TI913 alias addr 0xc */ + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x70, CRL_REG_LEN_08BIT, 0x1e, TI964_I2C_PHY_ADDR}, /* YUV422_8 */ + {0x7c, CRL_REG_LEN_08BIT, 0x81, TI964_I2C_PHY_ADDR}, /* Use RAW10 8bit mode */ + {0xd2, CRL_REG_LEN_08BIT, 0x84, TI964_I2C_PHY_ADDR}, + {0x4c, CRL_REG_LEN_08BIT, 0x12, TI964_I2C_PHY_ADDR}, /* Select RX port 1 */ + {0x58, CRL_REG_LEN_08BIT, 0x58, TI964_I2C_PHY_ADDR}, + {0x5c, CRL_REG_LEN_08BIT, 0x1a, TI964_I2C_PHY_ADDR}, /* TI913 alias addr 0xd */ + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x70, CRL_REG_LEN_08BIT, 0x5e, TI964_I2C_PHY_ADDR}, /* YUV422_8 */ + {0x7c, CRL_REG_LEN_08BIT, 0x81, TI964_I2C_PHY_ADDR}, /* Use RAW10 8bit mode */ + {0xd2, CRL_REG_LEN_08BIT, 0x84, TI964_I2C_PHY_ADDR}, + {0x4c, CRL_REG_LEN_08BIT, 0x24, TI964_I2C_PHY_ADDR}, /* Select RX port 2*/ + {0x58, CRL_REG_LEN_08BIT, 0x58, TI964_I2C_PHY_ADDR}, + {0x5c, CRL_REG_LEN_08BIT, 0x1c, TI964_I2C_PHY_ADDR}, /* TI913 alias addr 0xe */ + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x70, CRL_REG_LEN_08BIT, 0x9e, TI964_I2C_PHY_ADDR}, /* YUV422_8 */ + {0x7c, CRL_REG_LEN_08BIT, 0x81, TI964_I2C_PHY_ADDR}, /* Use RAW10 8bit mode */ + {0xd2, CRL_REG_LEN_08BIT, 0x84, TI964_I2C_PHY_ADDR}, + {0x4c, CRL_REG_LEN_08BIT, 0x38, TI964_I2C_PHY_ADDR}, /* Select RX port3 */ + {0x58, CRL_REG_LEN_08BIT, 0x58, TI964_I2C_PHY_ADDR}, + {0x5c, CRL_REG_LEN_08BIT, 0x1e, TI964_I2C_PHY_ADDR}, /* TI913 alias addr 0xf */ + {0x6d, CRL_REG_LEN_08BIT, 0x7f, TI964_I2C_PHY_ADDR}, + {0x70, CRL_REG_LEN_08BIT, 0xde, TI964_I2C_PHY_ADDR}, /* YUV422_8 */ + {0x7c, CRL_REG_LEN_08BIT, 0x81, TI964_I2C_PHY_ADDR}, /* Use RAW10 8bit mode */ + {0xd2, CRL_REG_LEN_08BIT, 0x84, TI964_I2C_PHY_ADDR}, + {0x6e, CRL_REG_LEN_08BIT, 0x89, TI964_I2C_PHY_ADDR}, +}; + +struct crl_sensor_configuration magna_ti964_crl_configuration = { + + .powerup_regs_items = ARRAY_SIZE(magna_ti964_powerup_regs), + .powerup_regs = magna_ti964_powerup_regs, + + .poweroff_regs_items = ARRAY_SIZE(magna_ti964_poweroff_regs), + .poweroff_regs = magna_ti964_poweroff_regs, + + .onetime_init_regs_items = ARRAY_SIZE(magna_ti964_onetime_init_regs), + .onetime_init_regs = magna_ti964_onetime_init_regs, + + .subdev_items = ARRAY_SIZE(magna_ti964_subdevs), + .subdevs = magna_ti964_subdevs, + + .pll_config_items = ARRAY_SIZE(magna_ti964_pll_configurations), + .pll_configs = magna_ti964_pll_configurations, + + .sensor_limits = &magna_ti964_limits, + + .modes_items = ARRAY_SIZE(magna_ti964_modes), + .modes = magna_ti964_modes, + + .streamon_regs_items = ARRAY_SIZE(magna_ti964_streamon_regs), + .streamon_regs = magna_ti964_streamon_regs, + + .streamoff_regs_items = ARRAY_SIZE(magna_ti964_streamoff_regs), + .streamoff_regs = magna_ti964_streamoff_regs, + + .ctrl_items = ARRAY_SIZE(magna_ti964_ctrls), + .ctrl_bank = magna_ti964_ctrls, + + .csi_fmts_items = ARRAY_SIZE(magna_ti964_crl_csi_data_fmt), + .csi_fmts = magna_ti964_crl_csi_data_fmt, + + .addr_len = CRL_ADDR_8BIT, +}; + +#endif /* __CRLMODULE_MAGNA_TI964_CONFIGURATION_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-core.c b/drivers/media/i2c/crlmodule-lite/crlmodule-core.c new file mode 100644 index 000000000000..59896f45b0db --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-core.c @@ -0,0 +1,2696 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" +#include "crlmodule-msrlist.h" + +static int init_ext_sd(struct i2c_client *client, + struct crl_subdev *ssd, int idx); +static void crlmodule_update_current_mode(struct crl_sensor *sensor); + +static int __crlmodule_get_variable_ref(struct crl_sensor *sensor, + enum crl_member_data_reference_ids ref, + u32 *val) +{ + switch (ref) { + case CRL_VAR_REF_OUTPUT_WIDTH: + *val = sensor->src->crop[CRL_PAD_SRC].width; + break; + case CRL_VAR_REF_OUTPUT_HEIGHT: + *val = sensor->src->crop[CRL_PAD_SRC].height; + break; + case CRL_VAR_REF_BITSPERPIXEL: + *val = sensor->sensor_ds->csi_fmts[ + sensor->fmt_index].bits_per_pixel; + break; + default: + return -EINVAL; + }; + + return 0; +} + +/* + * Get the data format index from the configuration definition data + */ +static int __crlmodule_get_data_fmt_index(struct crl_sensor *sensor, + u32 code) +{ + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->csi_fmts_items; i++) { + if (sensor->sensor_ds->csi_fmts[i].code == code) + return i; + } + + return -EINVAL; +} + +/* + * Find the index of the ctrl pointer from the array of ctrls + * maintained by the CRL module based on the ctrl id. + */ +static int __crlmodule_get_crl_ctrl_index(struct crl_sensor *sensor, + u32 id, unsigned int *index) +{ + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->ctrl_items; i++) + if (sensor->ctrl_bank[i].ctrl_id == id) + break; + + if (i >= sensor->sensor_ds->ctrl_items) + return -EINVAL; + + *index = i; + return 0; +} + +/* + * Finds the value of a specific ctrl based on the ctrl-id + */ +static int __crlmodule_get_param_value(struct crl_sensor *sensor, + u32 id, u32 *val) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int i; + int ret; + struct ici_ext_sd_param* param; + + ret = __crlmodule_get_crl_ctrl_index(sensor, id, &i); + if (ret) + return ret; + + /* If no corresponding ctrl created, return */ + if (sensor->ctrl_bank[i].param.id != id) { + dev_dbg(&client->dev, + "%s ctrl_id: 0x%x desc: %s not ready\n", __func__, id, + sensor->ctrl_bank[i].name); + return -ENODATA; + } + + param = &sensor->ctrl_bank[i].param; + switch (sensor->ctrl_bank[i].type) { + case CRL_CTRL_TYPE_MENU_INT: + if (param->val <= sensor->ctrl_bank[i].data.int_menu.max) + *val = sensor->ctrl_bank[i].data.int_menu.menu[param->val]; + else + *val = 0; + break; + case CRL_CTRL_TYPE_INTEGER: + default: + *val = param->val; + break; + } + + + dev_dbg(&client->dev, "%s ctrl_id: 0x%x desc: %s val: %d\n", + __func__, id, + sensor->ctrl_bank[i].name, *val); + return 0; +} + +/* + * Finds the v4l2 based on the control id + */ +static struct crl_ctrl_data *__crlmodule_get_ctrl( + struct crl_sensor *sensor, + u32 id) +{ + unsigned int i; + + if (__crlmodule_get_crl_ctrl_index(sensor, id, &i)) + return NULL; + + return &sensor->ctrl_bank[i]; +} + +/* + * Grab / Release controls based on the ctrl update context + */ +static void __crlmodule_enable_param(struct crl_sensor *sensor, + enum crl_ctrl_update_context ctxt, + bool enable) +{ + struct crl_ctrl_data *crl_ctrl; + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->ctrl_items; i++) { + crl_ctrl = &sensor->ctrl_bank[i]; + + if (crl_ctrl->context == ctxt) + crl_ctrl->enabled = enable; + } +} + +/* + * Checks if the ctrl sepecific data is satisfied in the mode and PLL + * selection logic. + */ +static bool __crlmodule_compare_ctrl_specific_data( + struct crl_sensor *sensor, + unsigned int items, + struct crl_ctrl_data_pair *ctrl_val) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int i; + u32 val; + int ret; + + /* Go through all the controls associated with this config */ + for (i = 0; i < items; i++) { + /* Get the value set for the control */ + ret = __crlmodule_get_param_value(sensor, ctrl_val[i].ctrl_id, + &val); + if (ret) { + dev_err(&client->dev, "%s ctrl_id: 0x%x not found\n", + __func__, ctrl_val[i].ctrl_id); + return false; + } + + /* Compare the value from the sensor definition file config */ + if (val != ctrl_val[i].data) { + dev_err(&client->dev, + "%s ctrl_id: 0x%x value not match %d != %d\n", + __func__, ctrl_val[i].ctrl_id, val, + ctrl_val[i].data); + return false; + } + } + + dev_dbg(&client->dev, "%s success\n", __func__); + return true; +} + +/* + * Finds the correct PLL settings index based on the parameters + */ +static int __crlmodule_update_pll_index(struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_pll_configuration *pll_config; + const struct crl_csi_data_fmt *fmts = + &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + unsigned int i; + u32 link_freq = 0; + + if (!sensor->link_freq || + sensor->link_freq->type != CRL_CTRL_TYPE_MENU_INT) { + dev_err(&client->dev, "%s Invalid link freq ctrl\n", + __func__); + return -EINVAL; + } + + sensor->link_freq->param.val = crl_ctrl->param.val; + if (crl_ctrl->param.val <= + sensor->link_freq->data.int_menu.max) { + link_freq =sensor->link_freq->data.int_menu.menu[ + crl_ctrl->param.val]; + } + + dev_dbg(&client->dev, "%s PLL Items: %d link_freq: %d\n", + __func__, sensor->sensor_ds->pll_config_items, + link_freq); + + for (i = 0; i < sensor->sensor_ds->pll_config_items; i++) { + pll_config = &sensor->sensor_ds->pll_configs[i]; + + if (pll_config->op_sys_clk != link_freq) + continue; + + if (pll_config->input_clk != sensor->platform_data->ext_clk) + continue; + + /* if pll_config->csi_lanes == 0, lanes do not matter */ + if (pll_config->csi_lanes) + if (sensor->platform_data->lanes != pll_config->csi_lanes) + continue; + + /* PLL config must match to bpps*/ + if (fmts->bits_per_pixel != pll_config->bitsperpixel) + continue; + + /* Check if there are any dynamic compare items */ + if (sensor->ext_ctrl_impacts_pll_selection && + !__crlmodule_compare_ctrl_specific_data(sensor, + pll_config->comp_items, + pll_config->ctrl_data)) + continue; + + /* Found PLL index */ + dev_dbg(&client->dev, "%s Found PLL index: %d for freq: %d\n", + __func__, i, link_freq); + + sensor->pll_index = i; + + /* Update the control values for pixelrate_pa and csi */ + sensor->pixel_rate_pa->param.s64val = pll_config->pixel_rate_pa; + sensor->pixel_rate_csi->param.s64val = pll_config->pixel_rate_csi; + return 0; + } + + dev_err(&client->dev, "%s no configuration found for freq: %d\n", + __func__, link_freq); + return -EINVAL; +} + +/* + * Perform the action for the dependency control + */ +static void __crlmodule_dep_ctrl_perform_action( + struct crl_sensor *sensor, + struct crl_dep_ctrl_provision *prov, + u32 *val, u32 *dep_val) +{ + enum crl_dep_ctrl_condition cond; + unsigned int i; + u32 temp; + + if (*val > *dep_val) + cond = CRL_DEP_CTRL_CONDITION_GREATER; + else if (*val < *dep_val) + cond = CRL_DEP_CTRL_CONDITION_LESSER; + else + cond = CRL_DEP_CTRL_CONDITION_EQUAL; + + for (i = 0; i < prov->action_items; i++) { + if (prov->action[i].cond == cond) + break; + } + + /* No handler found-. Return completed */ + if (i >= prov->action_items) + return; + + /* if this is dependency control, switch val and dep val */ + if (prov->action_type == CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + temp = *val; + *val = *dep_val; + *dep_val = temp; + } + + switch (prov->action[i].action) { + case CRL_DEP_CTRL_CONDITION_ADD: + *val = *dep_val + prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_SUBTRACT: + *val = *dep_val - prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_MULTIPLY: + *val = *dep_val * prov->action[i].action_value; + break; + case CRL_DEP_CTRL_CONDITION_DIVIDE: + *val = *dep_val / prov->action[i].action_value; + break; + } + + /* if this is dependency control, switch val and dep val back*/ + if (prov->action_type == CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + temp = *val; + *val = *dep_val; + *dep_val = temp; + } + + return; +} + +/* + * Parse the dynamic entity based on the Operand type + */ +static int __crlmodule_parse_dynamic_entity(struct crl_sensor *sensor, + struct crl_dynamic_entity entity, + u32 *val) +{ + switch (entity.entity_type) { + case CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST: + *val = entity.entity_val; + return 0; + case CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF: + return __crlmodule_get_variable_ref(sensor, + entity.entity_val, val); + case CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL: + return __crlmodule_get_param_value(sensor, + entity.entity_val, val); + case CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL: { + struct crl_register_read_rep reg; + + /* Note: Only 8bit registers are supported. */ + reg.address = entity.entity_val; + reg.len = CRL_REG_LEN_08BIT; + reg.mask = 0xff; + reg.dev_i2c_addr = CRL_I2C_ADDRESS_NO_OVERRIDE; + return crlmodule_read_reg(sensor, reg, val); + } + default: + break; + }; + + return -EINVAL; +} + +static int __crlmodule_calc_dynamic_entity_values( + struct crl_sensor *sensor, + unsigned int ops_items, + struct crl_arithmetic_ops *ops_arr, + unsigned int *val) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int i; + + /* perform the bitwise operation on val one by one */ + for (i = 0; i < ops_items; i++) { + struct crl_arithmetic_ops *ops = &ops_arr[i]; + u32 operand; + int ret = __crlmodule_parse_dynamic_entity(sensor, ops->operand, + &operand); + if (ret) { + dev_dbg(&client->dev, + "%s failed to parse dynamic entity: %d %d\n", + __func__, ops->operand.entity_type, + ops->operand.entity_val); + return ret; + } + + switch (ops->op) { + case CRL_BITWISE_AND: + *val &= operand; + break; + case CRL_BITWISE_OR: + *val |= operand; + break; + case CRL_BITWISE_LSHIFT: + *val <<= operand; + break; + case CRL_BITWISE_RSHIFT: + *val >>= operand; + break; + case CRL_BITWISE_XOR: + *val ^= operand; + break; + case CRL_BITWISE_COMPLEMENT: + *val = ~(*val); + break; + case CRL_ADD: + *val += operand; + break; + case CRL_SUBTRACT: + *val = *val > operand ? *val - operand : operand - *val; + break; + case CRL_MULTIPLY: + *val *= operand; + break; + case CRL_DIV: + if(operand==0) { + dev_err(&client->dev, "CRL_DIV error for operand returned is zero."); + return -EINVAL; + } + *val /= operand; + break; + case CRL_ASSIGNMENT: + *val = operand; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +/* + * Dynamic registers' value is not direct but depends on a referrence value. + * This kind of registers are mainly used in crlmodule's ctrl logic. + * + * This is to handle cases like the below examples, where mutliple registers + * need to be modified based on the input value "val" + * R3000 = val & 0xff and R3001 = val >> 8 & 0xff and R3002 = val >> 16 & 0xff + * R4001 = val and R4002 = val or + * R2800 = FLL - val and R2802 = LLP - val + */ +static int __crlmodule_update_dynamic_regs(struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl, + unsigned int val) +{ + unsigned int i; + + for (i = 0; i < crl_ctrl->regs_items; i++) { + struct crl_dynamic_register_access *reg = &crl_ctrl->regs[i]; + /* + * Each register group must start from the initial value, not + * as a continuation of the previous calculations. The sensor + * configurations must take care of this restriction. + */ + u32 val_t = val; + int ret; + + /* Get the value associated with the dynamic entity */ + ret = __crlmodule_calc_dynamic_entity_values(sensor, + reg->ops_items, + reg->ops, &val_t); + if (ret) + return ret; + + /* Now ready to write the value */ + ret = crlmodule_write_reg(sensor, reg->dev_i2c_addr, + reg->address, reg->len, + reg->mask, val_t); + if (ret) + return ret; + } + + return 0; +} + +/* + * Handles the dependency control actions. Dependency control is a control + * which' value depends on the current control. This information is encoded in + * the sensor configuration file. + */ +static int __crlmodule_handle_dependency_ctrl( + struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl, + unsigned int *val, + enum crl_dep_ctrl_action_type type) +{ + struct i2c_client *client = sensor->src->sd.client; + struct crl_ctrl_data *dep_crl_ctrl; + struct crl_dep_ctrl_provision *dep_prov; + unsigned int i, idx; + u32 dep_val; + int ret; + + dev_dbg(&client->dev, "%s ctrl_id: 0x%x dependency controls: %d\n", + __func__, crl_ctrl->ctrl_id, + crl_ctrl->dep_items); + + for (i = 0; i < crl_ctrl->dep_items; i++) { + dep_prov = &crl_ctrl->dep_ctrls[i]; + + /* If not the type, continue */ + if (dep_prov->action_type != type) + continue; + + /* Get the value from the dependency ctrl */ + ret = __crlmodule_get_param_value(sensor, dep_prov->ctrl_id, + &dep_val); + if (ret) { + dev_err(&client->dev, "%s ctrl_id: 0x%x not found\n", + __func__, dep_prov->ctrl_id); + /* TODO! Shoud continue? */ + continue; + } + + /* Perform the action */ + __crlmodule_dep_ctrl_perform_action(sensor, dep_prov, val, + &dep_val); + + /* if this is dependency control, update the register */ + if (dep_prov->action_type == + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL) { + ret = __crlmodule_get_crl_ctrl_index(sensor, + dep_prov->ctrl_id, &idx); + if (ret) + continue; + + dep_crl_ctrl = &sensor->ctrl_bank[idx]; + dev_dbg(&client->dev, + "%s crl_ctrl: 0x%p 0x%p\n", __func__, + &sensor->ctrl_bank[idx], + dep_crl_ctrl); + + ret = __crlmodule_update_dynamic_regs(sensor, + dep_crl_ctrl, dep_val); + if (ret) + continue; + } + } + return 0; +} + + +static int crlmodule_get_fmt_index(struct crl_sensor *sensor, + u8 pixel_order, u8 bpp) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_csi_data_fmt *f; + int i; + + /* + * Go through the fmt list and check if this format with matching bpp + * is supported by this module definition file + */ + for (i = 0; i < sensor->sensor_ds->csi_fmts_items; i++) { + f = &sensor->sensor_ds->csi_fmts[i]; + + if (f->pixel_order == pixel_order && f->bits_per_pixel == bpp) + return i; + } + + dev_err(&client->dev, "%s no supported format for order: %d bpp: %d\n", + __func__, pixel_order, bpp); + + return -EINVAL; +} + +static int __crlmodule_update_flip_info(struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl, + struct ici_ext_sd_param *param) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_csi_data_fmt *fmt = + &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + u8 bpp = fmt->bits_per_pixel; + u8 flip_info = sensor->flip_info; + u8 new_order = 0; + int i, ret; + + dev_dbg(&client->dev, "%s current flip_info: %d curr index: %d\n", + __func__, flip_info, sensor->fmt_index); + + switch (param->id) { + case ICI_EXT_SD_PARAM_ID_HFLIP: + flip_info &= CRL_FLIP_HFLIP_MASK; + flip_info |= param->val > 0 ? CRL_FLIP_HFLIP : 0; + break; + case ICI_EXT_SD_PARAM_ID_VFLIP: + flip_info &= CRL_FLIP_VFLIP_MASK; + flip_info |= param->val > 0 ? CRL_FLIP_VFLIP : 0; + break; + } + + dev_dbg(&client->dev, "%s flip success new flip_info: %d\n", + __func__, flip_info); + + /* First check if the module actually supports any pixelorder changes */ + for (i = 0; i < sensor->sensor_ds->flip_items; i++) { + if (flip_info == sensor->sensor_ds->flip_data[i].flip) { + new_order = sensor->sensor_ds->flip_data[i].pixel_order; + break; + } + } + + if (i >= sensor->sensor_ds->flip_items) { + dev_err(&client->dev, "%s flip not supported %d\n", + __func__, flip_info); + return -EINVAL; + } + + /* + * Flip changes only pixel order. So check if the supported format list + * has any format with new pixel order and current bits per pixel + */ + i = crlmodule_get_fmt_index(sensor, new_order, bpp); + if (i < 0) { + dev_err(&client->dev, "%s no format found order: %d bpp: %d\n", + __func__, new_order, bpp); + return -EINVAL; + } + + ret = __crlmodule_update_dynamic_regs(sensor, crl_ctrl, param->val); + if (ret) { + dev_err(&client->dev, "%s register access failed\n", __func__); + return ret; + } + + /* New format found. Update info */ + sensor->fmt_index = i; + sensor->flip_info = flip_info; + + dev_dbg(&client->dev, "%s flip success flip: %d new fmt index: %d\n", + __func__, flip_info, i); + + return 0; +} +static int __crlmodule_update_framesize(struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl, + struct ici_ext_sd_param *param) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + unsigned int val; + + switch (param->id) { + case ICI_EXT_SD_PARAM_ID_FRAME_LENGTH_LINES: + val = max(param->val, mode->min_fll); + break; + case ICI_EXT_SD_PARAM_ID_LINE_LENGTH_PIXELS: + val = max(param->val, mode->min_llp); + break; + default: + return -EINVAL; + } + + return __crlmodule_update_dynamic_regs(sensor, crl_ctrl, val); +} +static int __crlmodule_update_blanking(struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl, + struct ici_ext_sd_param *param) +{ + unsigned int val; + + switch (param->id) { + case ICI_EXT_SD_PARAM_ID_HBLANK: + val = sensor->pixel_array->crop[CRL_PA_PAD_SRC].width + + param->val; + break; + case ICI_EXT_SD_PARAM_ID_VBLANK: + val = sensor->pixel_array->crop[CRL_PA_PAD_SRC].height + + param->val; + break; + default: + return -EINVAL; + } + + return __crlmodule_update_dynamic_regs(sensor, crl_ctrl, val); +} + +static void __crlmodule_update_selection_impact_flags( + struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl) +{ + if (crl_ctrl->impact & CRL_IMPACTS_PLL_SELECTION) + sensor->ext_ctrl_impacts_pll_selection = true; + + if (crl_ctrl->impact & CRL_IMPACTS_MODE_SELECTION) + sensor->ext_ctrl_impacts_mode_selection = true; +} + +static struct crl_ctrl_data *__crlmodule_find_crlctrl( + struct crl_sensor *sensor, + struct ici_ext_sd_param *param) +{ + struct crl_ctrl_data *crl_ctrl; + unsigned int i; + + for (i = 0; i < sensor->sensor_ds->ctrl_items; i++) { + crl_ctrl = &sensor->ctrl_bank[i]; + if (crl_ctrl->param.sd == param->sd && + crl_ctrl->ctrl_id == param->id) + return crl_ctrl; + } + + return NULL; +} + +static int crlmodule_set_param(struct ici_ext_sd_param *param) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(param->sd); + struct i2c_client *client = sensor->src->sd.client; + struct crl_ctrl_data *crl_ctrl = NULL; + int ret = 0; + + dev_dbg(&client->dev, "%s id:%d val:%d\n", __func__, param->id, + param->val); + + /* + * Need to find the corresponding crlmodule wrapper for this param. + */ + crl_ctrl = __crlmodule_find_crlctrl(sensor, param); + if (!crl_ctrl) { + dev_err(&client->dev, "%s ctrl :0x%x not supported\n", + __func__, param->id); + return -EINVAL; + } + + dev_dbg(&client->dev, "%s id:0x%x name:%s\n", __func__, param->id, + crl_ctrl->name); + + if (!crl_ctrl->enabled || + crl_ctrl->flags & CRL_CTRL_FLAG_READ_ONLY) { + dev_err(&client->dev, "%s Control id:0x%x is not writeable\n", + __func__, param->id); + return -EINVAL; + } + + if (param->type != ICI_EXT_SD_PARAM_TYPE_INT32) { + dev_err(&client->dev, "%s Control id:0x%x only INT32 is supported\n", + __func__, param->id); + return -EINVAL; + } + + crl_ctrl->param.val = param->val; + + /* Then go through the mandatory controls */ + switch (param->id) { + case ICI_EXT_SD_PARAM_ID_LINK_FREQ: + /* Go through the supported list and compare the values */ + ret = __crlmodule_update_pll_index(sensor, crl_ctrl); + goto out; + }; + + /* update the selection impacts flags */ + __crlmodule_update_selection_impact_flags(sensor, crl_ctrl); + + /* + * Dependency control is a control whose value is affected by the value + * for the current control. For example, vblank can be a dependency + * control for exposure. Whenever exposure changes, the sensor can + * automatically adjust the vblank or rely on manual adjustment. In + * case of manual adjustment the sensor configuration file needs to + * specify the dependency control, the condition for an action and + * typs of action. + * + * Now check if there is any dependency controls for this. And if there + * are any we need to split the action to two. First if the current + * control needs to be changed, then do it before updating the register. + * If some other control is affected, then do it after wrriting the + * current values + * + * Now check in the dependency control list, if the action type is + * "self" and update the value accordingly now + */ + __crlmodule_handle_dependency_ctrl(sensor, crl_ctrl, ¶m->val, + CRL_DEP_CTRL_ACTION_TYPE_SELF); + + /* Handle specific controls */ + switch (param->id) { + case ICI_EXT_SD_PARAM_ID_HFLIP: + case ICI_EXT_SD_PARAM_ID_VFLIP: + ret = __crlmodule_update_flip_info(sensor, crl_ctrl, param); + goto out; + + case ICI_EXT_SD_PARAM_ID_VBLANK: + case ICI_EXT_SD_PARAM_ID_HBLANK: + if (sensor->blanking_ctrl_not_use) { + dev_info(&client->dev, "%s Blanking controls are not used \ + in this configuration, setting them has no effect\n", __func__); + /* Disable control*/ + crl_ctrl->enabled = false; + + } else { + ret = __crlmodule_update_blanking(sensor, crl_ctrl, param); + } + goto out; + + case ICI_EXT_SD_PARAM_ID_FRAME_LENGTH_LINES: + case ICI_EXT_SD_PARAM_ID_LINE_LENGTH_PIXELS: + ret = __crlmodule_update_framesize(sensor, crl_ctrl, param); + goto out; + + case ICI_EXT_SD_PARAM_ID_SENSOR_MODE: + sensor->sensor_mode = param->val; + crlmodule_update_current_mode(sensor); + goto out; + } + + ret = __crlmodule_update_dynamic_regs(sensor, crl_ctrl, param->val); + +out: + /* + * Now check in the dependency control list, if the action type is + * "dependency control" and update the value accordingly now + */ + if (!ret && crl_ctrl) + __crlmodule_handle_dependency_ctrl(sensor, crl_ctrl, ¶m->val, + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL); + + return ret; +} + +static int crlmodule_get_param(struct ici_ext_sd_param *param) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(param->sd); + struct i2c_client *client = sensor->src->sd.client; + struct crl_ctrl_data *crl_ctrl; + struct crl_dynamic_register_access *reg; + + /* + * Need to find the corresponding crlmodule wrapper for this param. + */ + crl_ctrl = __crlmodule_find_crlctrl(sensor, param); + if (!crl_ctrl) { + dev_err(&client->dev, "%s ctrl :0x%x not supported\n", + __func__, param->id); + return -EINVAL; + } + + dev_dbg(&client->dev, "%s id:0x%x name:%s\n", __func__, param->id, + crl_ctrl->name); + + if (crl_ctrl->flags & CRL_CTRL_FLAG_WRITE_ONLY) { + dev_err(&client->dev, "%s Control id:0x%x is not readable\n", + __func__, param->id); + return -EINVAL; + } + + param->type = ICI_EXT_SD_PARAM_TYPE_INT32; + if (!(crl_ctrl->flags & CRL_CTRL_FLAG_READ_ONLY)) { + param->val = crl_ctrl->param.val; + return 0; + } + + /* + * Found the crl control wrapper. Use the dynamic entity information + * to calculate the value for this control. For get control, there + * could be only one item in the crl_dynamic_register_access. ctrl-> + * regs_items must be 1. Also the crl_dynamic_register_access.address + * and crl_dynamic_register_access.len are not used. + * Instead the values to be found or calculated need to be encoded into + * crl_dynamic_register_access.crl_arithmetic_ops. It has possibility + * to read from registers, existing control values and simple arithmetic + * operations etc. + */ + if (!crl_ctrl->regs || !crl_ctrl->regs_items) { + dev_err(&client->dev, "%s no dynamic entities found\n", + __func__); + return -EINVAL; + } + if (crl_ctrl->regs_items > 1) + dev_warn(&client->dev, + "%s multiple dynamic entities, will skip the rest\n", + __func__); + reg = &crl_ctrl->regs[0]; + + /* Get the value associated with the dynamic entity */ + return __crlmodule_calc_dynamic_entity_values(sensor, reg->ops_items, + reg->ops, ¶m->val); +} + +static int crlmodule_get_menu_item( + struct ici_ext_sd_param *param, u32 idx) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(param->sd); + struct i2c_client *client = sensor->src->sd.client; + struct crl_ctrl_data *crl_ctrl; + + crl_ctrl = __crlmodule_find_crlctrl(sensor, param); + if (!crl_ctrl) { + dev_err(&client->dev, "%s ctrl :0x%x not supported\n", + __func__, param->id); + return -EINVAL; + } + + if (idx > crl_ctrl->max) { + dev_err(&client->dev, "%s Control id:0x%x has invalid index %u\n", + __func__, param->id, idx); + return -EINVAL; + } + switch (crl_ctrl->type) + { + case CRL_CTRL_TYPE_MENU_INT: + param->type = ICI_EXT_SD_PARAM_TYPE_INT64; + param->s64val = crl_ctrl->data.int_menu.menu[idx]; + break; + case CRL_CTRL_TYPE_MENU_ITEMS: + if (!param->custom.size || !param->custom.data) { + dev_err(&client->dev, "%s Control id:0x%x param->custom.data must be preallocated by caller\n", + __func__, param->id); + return -EINVAL; + } + param->type = ICI_EXT_SD_PARAM_TYPE_STR; + strncpy(param->custom.data, + crl_ctrl->data.menu_items.menu[idx], + param->custom.size - 1); + param->custom.data[param->custom.size - 1] = '\0'; + break; + default: + dev_err(&client->dev, "%s Control id:0x%x does not have a menu\n", + __func__, param->id); + return -EINVAL; + } + return 0; +} + +static int __crlmodule_init_link_freq_ctrl_menu( + struct crl_sensor *sensor, + struct crl_ctrl_data *crl_ctrl) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int items = 0; + unsigned int i; + + /* Cannot handle if the control type is not integer menu */ + if (crl_ctrl->type != CRL_CTRL_TYPE_MENU_INT) + return 0; + + /* If the menu contents exist, skip filling it dynamically */ + if (crl_ctrl->data.int_menu.menu) + return 0; + + sensor->link_freq_menu = devm_kzalloc(&client->dev, sizeof(s64) * + sensor->sensor_ds->pll_config_items, + GFP_KERNEL); + if (!sensor->link_freq_menu) + return -ENOMEM; + + for (i = 0; i < sensor->sensor_ds->pll_config_items; i++) { + bool dup = false; + unsigned int j; + + /* + * Skip the duplicate entries. We are using the value to match + * not the index + */ + for (j = 0; j < items && !dup; j++) + dup = (sensor->link_freq_menu[j] == + sensor->sensor_ds->pll_configs[i].op_sys_clk); + if (dup) + continue; + + sensor->link_freq_menu[items] = + sensor->sensor_ds->pll_configs[i].op_sys_clk; + items++; + } + + crl_ctrl->data.int_menu.menu = sensor->link_freq_menu; + + /* items will not be 0 as there will be atleast one pll_config_item */ + crl_ctrl->data.int_menu.max = items - 1; + + return 0; +} + +static int crlmodule_init_controls(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int pa_ctrls = 0; + unsigned int src_ctrls = 0; + struct crl_ctrl_data *crl_ctrl; + unsigned int i; + int rval; + + sensor->ctrl_bank = devm_kzalloc(&client->dev, + sizeof(struct crl_ctrl_data) * + sensor->sensor_ds->ctrl_items, + GFP_KERNEL); + if (!sensor->ctrl_bank) + return -ENOMEM; + + /* Prepare to initialise the ctrls from the crl wrapper */ + for (i = 0; i < sensor->sensor_ds->ctrl_items; i++) { + /* + * First copy the ctrls to the sensor as there could be + * more than one similar sensors in a product which could share + * the same configuration files + */ + sensor->ctrl_bank[i] = + sensor->sensor_ds->ctrl_bank[i]; + + crl_ctrl = &sensor->ctrl_bank[i]; + crl_ctrl->param.id = crl_ctrl->ctrl_id; + if (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_PIXEL_ARRAY) { + if (sensor->pixel_array) { + crl_ctrl->param.sd = + &sensor->pixel_array->sd; + } + pa_ctrls++; + } + + if (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER) { + if (sensor->scaler) { + crl_ctrl->param.sd = + &sensor->scaler->sd; + } + src_ctrls++; + } + if (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER) { + if (sensor->binner) { + crl_ctrl->param.sd = + &sensor->binner->sd; + } + src_ctrls++; + } + + /* populate the ctrl for the Link_freq dynamically */ + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_LINK_FREQ && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) { + rval = __crlmodule_init_link_freq_ctrl_menu(sensor, + crl_ctrl); + if (rval) + return rval; + } + } + dev_dbg(&client->dev, "%s pa_ctrls: %d src_ctrls: %d\n", __func__, + pa_ctrls, src_ctrls); + for (i = 0; i < sensor->sensor_ds->ctrl_items; i++) { + crl_ctrl = &sensor->ctrl_bank[i]; + switch (crl_ctrl->type) { + case CRL_CTRL_TYPE_MENU_ITEMS: + crl_ctrl->max = crl_ctrl->data.menu_items.size - 1; + break; + case CRL_CTRL_TYPE_MENU_INT: + crl_ctrl->max = crl_ctrl->data.int_menu.max; + crl_ctrl->def = crl_ctrl->data.int_menu.def; + break; + case CRL_CTRL_TYPE_INTEGER64: + case CRL_CTRL_TYPE_INTEGER: + case CRL_CTRL_TYPE_CUSTOM: + crl_ctrl->min = crl_ctrl->data.std_data.min; + crl_ctrl->max = crl_ctrl->data.std_data.max; + crl_ctrl->step = crl_ctrl->data.std_data.step; + crl_ctrl->def = crl_ctrl->data.std_data.def; + break; + case CRL_CTRL_TYPE_BOOLEAN: + case CRL_CTRL_TYPE_BUTTON: + case CRL_CTRL_TYPE_CTRL_CLASS: + default: + dev_err(&client->dev, + "%s Invalid control type\n", __func__); + continue; + break; + } + + /* + * Blanking and framesize controls access to same register, + * Blank controls are disabled if framesize controls exists. + */ + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_FRAME_LENGTH_LINES || + crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_LINE_LENGTH_PIXELS) + sensor->blanking_ctrl_not_use = 1; + + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_SENSOR_MODE) + sensor->direct_mode_in_use = 1; + + /* Save mandatory control references - link_freq in src sd */ + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_LINK_FREQ && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) + sensor->link_freq = crl_ctrl; + + /* Save mandatory control references - pixel_rate_pa PA sd */ + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_PIXEL_RATE && + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_PIXEL_ARRAY) + sensor->pixel_rate_pa = crl_ctrl; + + /* Save mandatory control references - pixel_rate_csi src sd */ + if (crl_ctrl->ctrl_id == ICI_EXT_SD_PARAM_ID_PIXEL_RATE && + (crl_ctrl->sd_type == CRL_SUBDEV_TYPE_SCALER || + crl_ctrl->sd_type == CRL_SUBDEV_TYPE_BINNER)) + sensor->pixel_rate_csi = crl_ctrl; + + dev_dbg(&client->dev, + "%s idx: %d ctrl_id: 0x%x ctrl_name: %s\n", + __func__, i, crl_ctrl->ctrl_id, crl_ctrl->name); + } + + return 0; +} + + +static bool __crlmodule_rect_matches(struct i2c_client *client, + const struct ici_rect *const rect1, + const struct ici_rect *const rect2) +{ + dev_dbg(&client->dev, "%s rect1 l:%d t:%d w:%d h:%d\n", __func__, + rect1->left, rect1->top, rect1->width, rect1->height); + dev_dbg(&client->dev, "%s rect2 l:%d t:%d w:%d h:%d\n", __func__, + rect2->left, rect2->top, rect2->width, rect2->height); + + return (rect1->left == rect2->left && + rect1->top == rect2->top && + rect1->width == rect2->width && + rect1->height == rect2->height); +} + +static int __crlmodule_update_hblank(struct crl_sensor *sensor, + struct crl_ctrl_data *hblank) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = sensor->sensor_ds->sensor_limits; + unsigned int width = sensor->pixel_array->crop[CRL_PA_PAD_SRC].width; + unsigned int min_llp, max_llp; + + if (mode->min_llp) + min_llp = mode->min_llp; /* mode specific limit */ + else if (limits->min_line_length_pixels) + min_llp = limits->min_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + min_llp = width; + + if (mode->max_llp) + max_llp = mode->max_llp; /* mode specific limit */ + else if (limits->min_line_length_pixels) + max_llp = limits->max_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + max_llp = USHRT_MAX; + + hblank->min = min_llp - width; + hblank->max = max_llp - width; + hblank->def = hblank->min; + return 0; +} + +static int __crlmodule_update_vblank(struct crl_sensor *sensor, + struct crl_ctrl_data *vblank) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + const struct crl_sensor_limits *limits = sensor->sensor_ds->sensor_limits; + unsigned int height = sensor->pixel_array->crop[CRL_PA_PAD_SRC].height; + unsigned int min_fll, max_fll; + + if (mode->min_fll) + min_fll = mode->min_fll; /* mode specific limit */ + else if (limits->min_frame_length_lines) + min_fll = limits->min_frame_length_lines; /* sensor limit */ + else /* No restrictions */ + min_fll = height; + + if (mode->max_fll) + max_fll = mode->max_fll; /* mode specific limit */ + else if (limits->min_line_length_pixels) + max_fll = limits->max_line_length_pixels; /* sensor limit */ + else /* No restrictions */ + max_fll = USHRT_MAX; + + vblank->min = min_fll - height; + vblank->max = max_fll - height; + vblank->def = vblank->min; + return 0; +} + +static void crlmodule_update_framesize(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *mode = sensor->current_mode; + struct crl_ctrl_data *llength; + struct crl_ctrl_data *flength; + + llength = __crlmodule_get_ctrl(sensor, ICI_EXT_SD_PARAM_ID_LINE_LENGTH_PIXELS); + flength = __crlmodule_get_ctrl(sensor, ICI_EXT_SD_PARAM_ID_FRAME_LENGTH_LINES); + + if (llength) { + llength->min = mode->min_llp; + llength->def = llength->min; + } + + if (flength) { + flength->min = mode->min_fll; + flength->def = flength->min; + } +} + +static int crlmodule_update_frame_blanking(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + struct crl_ctrl_data *vblank; + struct crl_ctrl_data *hblank; + int ret; + + vblank = __crlmodule_get_ctrl(sensor, ICI_EXT_SD_PARAM_ID_VBLANK); + hblank = __crlmodule_get_ctrl(sensor, ICI_EXT_SD_PARAM_ID_HBLANK); + + if (hblank) { + ret = __crlmodule_update_hblank(sensor, hblank); + if (ret) + return ret; + dev_dbg(&client->dev, "%s hblank:%d\n", __func__, hblank->param.val); + } + + if (vblank) { + ret = __crlmodule_update_vblank(sensor, vblank); + if (ret) + return ret; + dev_dbg(&client->dev, "%s vblank:%d\n", __func__, vblank->param.val); + } + + return 0; +} + +static void crlmodule_update_mode_bysel(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_mode_rep *this; + unsigned int i; + + dev_dbg(&client->dev, "%s look for w: %d, h: %d, in [%d] modes\n", + __func__, sensor->src->crop[CRL_PAD_SRC].width, + sensor->src->crop[CRL_PAD_SRC].height, + sensor->sensor_ds->modes_items); + + for (i = 0; i < sensor->sensor_ds->modes_items; i++) { + this = &sensor->sensor_ds->modes[i]; + + dev_dbg(&client->dev, "%s check mode list[%d] w: %d, h: %d\n", + __func__, i, this->width, this->height); + if (this->width != sensor->src->crop[CRL_PAD_SRC].width || + this->height != sensor->src->crop[CRL_PAD_SRC].height) + continue; + + if (sensor->pixel_array) { + dev_dbg(&client->dev, "%s Compare PA out rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->pixel_array->crop[CRL_PA_PAD_SRC], + &this->sd_rects[CRL_SD_PA_INDEX].out_rect)) + continue; + } + if (sensor->binner) { + dev_dbg(&client->dev, "%s binning hor: %d vs. %d\n", + __func__, + sensor->binning_horizontal, + this->binn_hor); + if (sensor->binning_horizontal != this->binn_hor) + continue; + + dev_dbg(&client->dev, "%s binning vert: %d vs. %d\n", + __func__, + sensor->binning_vertical, + this->binn_vert); + if (sensor->binning_vertical != this->binn_vert) + continue; + + dev_dbg(&client->dev, "%s binner in rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->binner->crop[CRL_PAD_SINK], + &this->sd_rects[CRL_SD_BINNER_INDEX].in_rect)) + continue; + + dev_dbg(&client->dev, "%s binner out rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->binner->crop[CRL_PAD_SRC], + &this->sd_rects[CRL_SD_BINNER_INDEX].out_rect)) + continue; + } + + if (sensor->scaler) { + dev_dbg(&client->dev, "%s scaler scale_m %d vs. %d\n", + __func__, sensor->scale_m, + this->scale_m); + if (sensor->scale_m != this->scale_m) + continue; + + dev_dbg(&client->dev, "%s scaler in rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->scaler->crop[CRL_PAD_SINK], + &this->sd_rects[CRL_SD_SCALER_INDEX].in_rect)) + continue; + + dev_dbg(&client->dev, "%s scaler out rect\n", __func__); + if (!__crlmodule_rect_matches(client, + &sensor->scaler->crop[CRL_PAD_SRC], + &this->sd_rects[CRL_SD_SCALER_INDEX].out_rect)) + continue; + } + + /* Check if there are any dynamic compare items */ + if (sensor->ext_ctrl_impacts_mode_selection && + !__crlmodule_compare_ctrl_specific_data(sensor, + this->comp_items, + this->ctrl_data)) + continue; + + /* Found a perfect match! */ + dev_dbg(&client->dev, "%s found mode. idx: %d\n", __func__, i); + break; + } + + /* If no modes found, fall back to the fail safe mode index */ + if (i >= sensor->sensor_ds->modes_items) { + i = sensor->sensor_ds->fail_safe_mode_index; + this = &sensor->sensor_ds->modes[i]; + dev_info(&client->dev, + "%s no matching mode, set to default: %d\n", + __func__, i); + } + + sensor->current_mode = this; +} + +static void crlmodule_update_mode_ctrl(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_mode_rep *this; + int i; + + dev_dbg(&client->dev, "%s Sensor Mode :%d\n", + __func__, sensor->sensor_mode); + /* point to selected mode */ + this = &sensor->sensor_ds->modes[sensor->sensor_mode]; + sensor->current_mode = this; + + for (i = 0; i < this->sd_rects_items; i++) { + + if (CRL_SUBDEV_TYPE_PIXEL_ARRAY == + this->sd_rects[i].subdev_type) { + sensor->pixel_array->crop[CRL_PA_PAD_SRC] = + this->sd_rects[CRL_SD_PA_INDEX].out_rect; + } + + if (CRL_SUBDEV_TYPE_BINNER == + this->sd_rects[i].subdev_type) { + sensor->binner->sink_fmt = + this->sd_rects[i].in_rect; + sensor->binner->crop[CRL_PAD_SINK] = + this->sd_rects[i].in_rect; + sensor->binner->crop[CRL_PAD_SRC] = + this->sd_rects[i].out_rect; + sensor->binning_vertical = this->binn_vert; + sensor->binning_horizontal = this->binn_hor; + if (this->binn_vert > 1) + sensor->binner->compose = + this->sd_rects[i].out_rect; + } + + if (CRL_SUBDEV_TYPE_SCALER == + this->sd_rects[i].subdev_type) { + sensor->scaler->crop[CRL_PAD_SINK] = + this->sd_rects[i].in_rect; + sensor->scaler->crop[CRL_PAD_SRC] = + this->sd_rects[i].out_rect; + sensor->scaler->sink_fmt = + this->sd_rects[i].in_rect; + sensor->scale_m = this->scale_m; + if (this->scale_m != 1) + sensor->scaler->compose = + this->sd_rects[i].out_rect; + } + } + + /* Set source */ + sensor->src->crop[CRL_PAD_SRC].width = this->width; + sensor->src->crop[CRL_PAD_SRC].height = this->height; +} + +static void crlmodule_update_current_mode(struct crl_sensor *sensor) +{ + const struct crl_mode_rep *this; + int i; + + if (sensor->direct_mode_in_use) + crlmodule_update_mode_ctrl(sensor); + else + crlmodule_update_mode_bysel(sensor); + + /* + * We have a valid mode now. If there are any mode specific "get" + * controls defined in the configuration it could be queried by the + * user space for any mode specific information. So go through the + * mode specific ctrls and update its value from the selected mode. + */ + + this = sensor->current_mode; + + for (i = 0; i < this->comp_items; i++) { + struct crl_ctrl_data_pair *ctrl_comp = &this->ctrl_data[i]; + unsigned int idx; + + /* Get the ctl_ctrl pointer corresponding ctrl id */ + if (__crlmodule_get_crl_ctrl_index(sensor, ctrl_comp->ctrl_id, + &idx)) + /* If not found, move to the next ctrl */ + continue; + + /* No need to update this control, if this is a set op ctrl */ + if (sensor->ctrl_bank[idx].op_type == CRL_CTRL_SET_OP) + continue; + + /* Update the control value */ + sensor->ctrl_bank[idx].param.val = ctrl_comp->data; + } + + if (sensor->blanking_ctrl_not_use) + crlmodule_update_framesize(sensor); + else + crlmodule_update_frame_blanking(sensor); +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int __crlmodule_get_format( + struct ici_ext_subdev* subdev, + struct ici_pad_framefmt* pff) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct ici_rect *r; + + if (pff->pad.pad_idx == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + pff->ffmt.width = r->width; + pff->ffmt.height = r->height; + pff->ffmt.pixelformat = + sensor->sensor_ds->csi_fmts[sensor->fmt_index].code; + pff->ffmt.field = + ((ssd->field == ICI_FIELD_ANY) ? + ICI_FIELD_NONE : ssd->field); + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_enum_pixelformat( + struct ici_isys_node* node, + struct ici_pad_supported_format_desc* psfd) +{ + struct ici_ext_subdev* subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + if (psfd->idx >= sensor->sensor_ds->csi_fmts_items) + return -EINVAL; + + psfd->color_format = + sensor->sensor_ds->csi_fmts[psfd->idx].code; + psfd->min_width = sensor->sensor_ds->sensor_limits->x_addr_min; + psfd->max_width = sensor->sensor_ds->sensor_limits->x_addr_max; + psfd->min_height = sensor->sensor_ds->sensor_limits->y_addr_min; + psfd->max_height = sensor->sensor_ds->sensor_limits->y_addr_max; + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_get_format( + struct ici_isys_node* node, + struct ici_pad_framefmt* pff) +{ + struct ici_ext_subdev* subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __crlmodule_get_format(subdev, pff); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int __crlmodule_sel_supported( + struct ici_ext_subdev *subdev, + u32 pad, + u32 type) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + if (ssd == sensor->pixel_array && pad == CRL_PA_PAD_SRC) { + switch (type) { + case ICI_EXT_SEL_TYPE_NATIVE: + case ICI_EXT_SEL_TYPE_CROP: + case ICI_EXT_SEL_TYPE_CROP_BOUNDS: + return 0; + } + } + if (ssd == sensor->binner) { + switch (type) { + case ICI_EXT_SEL_TYPE_COMPOSE: + case ICI_EXT_SEL_TYPE_COMPOSE_BOUNDS: + if (pad == CRL_PAD_SINK) + return 0; + break; + } + } + if (ssd == sensor->scaler) { + switch (type) { + case ICI_EXT_SEL_TYPE_CROP: + case ICI_EXT_SEL_TYPE_CROP_BOUNDS: + if (pad == CRL_PAD_SRC) + return 0; + break; + case ICI_EXT_SEL_TYPE_COMPOSE: + case ICI_EXT_SEL_TYPE_COMPOSE_BOUNDS: + if (pad == CRL_PAD_SINK) + return 0; + break; + } + } + return -EINVAL; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static void crlmodule_get_crop_compose( + struct ici_ext_subdev *subdev, + struct ici_rect **crops, + struct ici_rect **comps) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + unsigned int i; + + /* Currently we support only 2 pads */ + BUG_ON(subdev->num_pads > CRL_PADS); + + if (crops) + for (i = 0; i < subdev->num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_get_selection( + struct ici_isys_node *node, + struct ici_pad_selection* ps) +{ + struct ici_ext_subdev *subdev = node->sd; + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct ici_rect *comp, *crops[CRL_PADS]; + struct ici_rect sink_fmt; + int ret; + + ret = __crlmodule_sel_supported(subdev, ps->pad.pad_idx, + ps->sel_type); + if (ret) + return ret; + + crlmodule_get_crop_compose(subdev, crops, &comp); + + sink_fmt = ssd->sink_fmt; + + switch (ps->sel_type) { + case ICI_EXT_SEL_TYPE_CROP_BOUNDS: + case ICI_EXT_SEL_TYPE_NATIVE: + if (ssd == sensor->pixel_array) { + ps->rect.left = ps->rect.top = 0; + ps->rect.width = + sensor->sensor_ds->sensor_limits->x_addr_max; + ps->rect.height = + sensor->sensor_ds->sensor_limits->y_addr_max; + } else if (ps->pad.pad_idx == ssd->sink_pad) { + ps->rect = sink_fmt; + } else { + ps->rect = *comp; + } + break; + case ICI_EXT_SEL_TYPE_CROP: + case ICI_EXT_SEL_TYPE_COMPOSE_BOUNDS: + ps->rect = *crops[ps->pad.pad_idx]; + break; + case ICI_EXT_SEL_TYPE_COMPOSE: + ps->rect = *comp; + break; + } + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static void crlmodule_propagate( + struct ici_ext_subdev *subdev, + u32 type) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct ici_rect *comp, *crops[CRL_PADS]; + + crlmodule_get_crop_compose(subdev, crops, &comp); + + switch (type) { + case ICI_EXT_SEL_TYPE_CROP: + comp->width = crops[CRL_PAD_SINK]->width; + comp->height = crops[CRL_PAD_SINK]->height; + if (ssd == sensor->scaler) { + sensor->scale_m = 1; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + /* Fall through */ + case ICI_EXT_SEL_TYPE_COMPOSE: + *crops[CRL_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_compose( + struct ici_ext_subdev *subdev, + struct ici_rect *r) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct ici_rect *comp, *crops[CRL_PADS]; + + crlmodule_get_crop_compose(subdev, crops, &comp); + + r->top = 0; + r->left = 0; + + if (ssd == sensor->binner) { + sensor->binning_horizontal = crops[CRL_PAD_SINK]->width / + r->width; + sensor->binning_vertical = crops[CRL_PAD_SINK]->height / + r->height; + } else { + sensor->scale_m = crops[CRL_PAD_SINK]->width * + sensor->sensor_ds->sensor_limits->scaler_m_min / + r->width; + } + + *comp = *r; + + crlmodule_propagate(subdev, + ICI_EXT_SEL_TYPE_COMPOSE); + + crlmodule_update_current_mode(sensor); + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_crop( + struct ici_ext_subdev *subdev, + u32 pad, + struct ici_rect *r) +{ + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct ici_rect *src_size, *crops[CRL_PADS]; + + crlmodule_get_crop_compose(subdev, crops, NULL); + + if (pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + + if (ssd == sensor->src && pad == CRL_PAD_SRC) { + r->left = 0; + r->top = 0; + } + + r->width = min(r->width, src_size->width); + r->height = min(r->height, src_size->height); + + r->left = min_t(s32, r->left, src_size->width - r->width); + r->top = min_t(s32, r->top, src_size->height - r->height); + + *crops[pad] = *r; + + if (ssd != sensor->pixel_array && pad == CRL_PAD_SINK) + crlmodule_propagate(subdev, + ICI_EXT_SEL_TYPE_CROP); + + /* TODO! Should we short list supported mode? */ + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Modified based on the CRL Module changes + */ +static int crlmodule_set_format( + struct ici_isys_node *node, + struct ici_pad_framefmt *pff) +{ + struct ici_ext_subdev *subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct crl_subdev *ssd = to_crlmodule_subdev(subdev); + struct i2c_client *client = sensor->src->sd.client; + struct ici_rect *crops[CRL_PADS]; + + dev_dbg(&client->dev, "%s sd_name: %s pad: %d w: %d, h: %d code: 0x%x", + __func__, node->name, pff->pad.pad_idx, + pff->ffmt.width, + pff->ffmt.height, + pff->ffmt.pixelformat); + + mutex_lock(&sensor->mutex); + + /* Currently we only support ALTERNATE interlaced mode. */ + if (pff->ffmt.field != ICI_FIELD_ALTERNATE) + pff->ffmt.field = ICI_FIELD_NONE; + pff->ffmt.colorspace = 0; + memset(pff->ffmt.reserved, 0, sizeof(pff->ffmt.reserved)); + ssd->field = pff->ffmt.field; + + if (pff->pad.pad_idx == ssd->source_pad) { + u32 code = pff->ffmt.pixelformat; + int rval = __crlmodule_get_format(subdev, pff); + + if (!rval && subdev == &sensor->src->sd) { + /* Check if this code is supported, if yes get index */ + int idx = __crlmodule_get_data_fmt_index(sensor, code); + + if (idx < 0) { + dev_err(&client->dev, "%s invalid format\n", + __func__); + mutex_unlock(&sensor->mutex); + return -EINVAL; + } + + sensor->fmt_index = idx; + rval = __crlmodule_get_format(subdev, pff); + /* TODO! validate PLL? */ + } + mutex_unlock(&sensor->mutex); + return rval; + } + + pff->ffmt.width = + clamp_t(uint32_t, pff->ffmt.width, + sensor->sensor_ds->sensor_limits->x_addr_min, + sensor->sensor_ds->sensor_limits->x_addr_max); + pff->ffmt.height = + clamp_t(uint32_t, pff->ffmt.height, + sensor->sensor_ds->sensor_limits->y_addr_min, + sensor->sensor_ds->sensor_limits->y_addr_max); + + crlmodule_get_crop_compose(subdev, crops, NULL); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = pff->ffmt.width; + crops[ssd->sink_pad]->height = pff->ffmt.height; + ssd->sink_fmt = *crops[ssd->sink_pad]; + + crlmodule_propagate(subdev, ICI_EXT_SEL_TYPE_CROP); + + crlmodule_update_current_mode(sensor); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Slightly modified based on the CRL Module changes + */ +static int crlmodule_set_selection( + struct ici_isys_node *node, + struct ici_pad_selection* ps) +{ + struct ici_ext_subdev *subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + int ret; + + dev_dbg(&client->dev, "%s sd_name: %s sel w: %d, h: %d", + __func__, node->name, ps->rect.width, + ps->rect.height); + + ret = __crlmodule_sel_supported(subdev, ps->pad.pad_idx, + ps->sel_type); + if (ret) { + dev_dbg(&client->dev, + "%s sd_name: %s w: %d, h: %d not supported", + __func__, node->name, ps->rect.width, + ps->rect.height); + return ret; + } + + mutex_lock(&sensor->mutex); + + ps->rect.width = max_t(unsigned int, + sensor->sensor_ds->sensor_limits->x_addr_min, + ps->rect.width); + ps->rect.height = max_t(unsigned int, + sensor->sensor_ds->sensor_limits->y_addr_min, + ps->rect.height); + switch (ps->sel_type) { + case ICI_EXT_SEL_TYPE_CROP: + ret = crlmodule_set_crop(subdev, ps->pad.pad_idx, + &ps->rect); + break; + case ICI_EXT_SEL_TYPE_COMPOSE: + ret = crlmodule_set_compose(subdev, &ps->rect); + break; + default: + ret = -EINVAL; + } + + crlmodule_update_current_mode(sensor); + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int crlmodule_start_streaming(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + const struct crl_pll_configuration *pll; + const struct crl_csi_data_fmt *fmt; + int rval; + + dev_dbg(&client->dev, "%s start streaming pll_idx: %d fmt_idx: %d\n", + __func__, sensor->pll_index, + sensor->fmt_index); + + pll = &sensor->sensor_ds->pll_configs[sensor->pll_index]; + fmt = &sensor->sensor_ds->csi_fmts[sensor->fmt_index]; + + crlmodule_update_current_mode(sensor); + + rval = crlmodule_write_regs(sensor, fmt->regs, fmt->regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set format\n", __func__); + return rval; + } + + rval = crlmodule_write_regs(sensor, pll->pll_regs, pll->pll_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set plls\n", __func__); + return rval; + } + + /* Write mode list */ + rval = crlmodule_write_regs(sensor, + sensor->current_mode->mode_regs, + sensor->current_mode->mode_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return rval; + } + + /* Write stream on list */ + rval = crlmodule_write_regs(sensor, + sensor->sensor_ds->streamon_regs, + sensor->sensor_ds->streamon_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set stream\n", __func__); + return rval; + } + + return 0; +} + +static int crlmodule_stop_streaming(struct crl_sensor *sensor) +{ + return crlmodule_write_regs(sensor, + sensor->sensor_ds->streamoff_regs, + sensor->sensor_ds->streamoff_regs_items); +} + +static int crlmodule_set_stream( + struct ici_isys_node* node, + void* ip, + int enable) +{ + struct ici_ext_subdev *subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + int rval = 0; + + mutex_lock(&sensor->mutex); + + if (sensor->streaming == enable) + goto out; + + if (enable) { + + if (sensor->msr_list) { + rval = crlmodule_apply_msrlist(client, + sensor->msr_list); + if (rval) + dev_warn(&client->dev, "msrlist write error %d\n", + rval); + } + rval = crlmodule_start_streaming(sensor); + if (!rval) + sensor->streaming = 1; + } else { + rval = crlmodule_stop_streaming(sensor); + sensor->streaming = 0; + } + +out: + mutex_unlock(&sensor->mutex); + + /* SENSOR_IDLE control cannot be set when streaming*/ + __crlmodule_enable_param(sensor, SENSOR_IDLE, enable); + + /* SENSOR_STREAMING controls cannot be set when not streaming */ + __crlmodule_enable_param(sensor, SENSOR_STREAMING, !enable); + + /* SENSOR_POWERED_ON controls does not matter about streaming. */ + __crlmodule_enable_param(sensor, SENSOR_POWERED_ON, false); + + return rval; +} + +static int crlmodule_identify_module( + struct ici_ext_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + unsigned int size = 0; + char *id_string; + char *temp; + int i, ret; + u32 val; + + for (i = 0; i < sensor->sensor_ds->id_reg_items; i++) + size += sensor->sensor_ds->id_regs[i].width + 1; + + /* TODO! If no ID! return success? */ + if (!size) + return 0; + + /* Create string variabel to append module ID */ + id_string = kzalloc(size, GFP_KERNEL); + if (!id_string) + return -ENOMEM; + *id_string = '\0'; + + /* Go through each regs in the list and append to id_string */ + for (i = 0; i < sensor->sensor_ds->id_reg_items; i++) { + ret = crlmodule_read_reg(sensor, + sensor->sensor_ds->id_regs[i].reg, + &val); + if (ret) + goto out; + + temp = kzalloc(sensor->sensor_ds->id_regs[i].width, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto out; + } + snprintf(temp, sensor->sensor_ds->id_regs[i].width, "0x%x ", + val); + strcat(id_string, temp); + snprintf(id_string, sensor->sensor_ds->id_regs[i].width, + "%s 0x%x ", temp, val); + + kfree(temp); + } + + /* TODO! Check here if this module in the supported list + * Ideally the module manufacturer and id should be in platform + * data or ACPI and here the driver should read the value from the + * register and check if this matches to any in the supported + * platform data */ + +out: + dev_dbg(&client->dev, "%s module: %s", __func__, id_string); + kfree(id_string); + if (ret) + dev_err(&client->dev, "sensor detection failed\n"); + return ret; +} + +/* + * This function executes the initialisation routines after the power on + * is successfully completed. Following operations are done + * + * Initiases registers after sensor power up - if any such list is configured + * Ctrl handler framework intialisation + */ +static int crlmodule_run_poweron_init(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + int rval; + + dev_dbg(&client->dev, "%s set power up registers: %d\n", + __func__, sensor->sensor_ds->powerup_regs_items); + + /* Write the power up registers */ + rval = crlmodule_write_regs(sensor, sensor->sensor_ds->powerup_regs, + sensor->sensor_ds->powerup_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set powerup registers\n", + __func__); + return rval; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + dev_dbg(&client->dev, "%s init controls", __func__); + + + /* SENSOR_IDLE control can be set only when not streaming*/ + __crlmodule_enable_param(sensor, SENSOR_IDLE, false); + + /* SENSOR_STREAMING controls can be set only when streaming */ + __crlmodule_enable_param(sensor, SENSOR_STREAMING, true); + + /* SENSOR_POWERED_ON controls can be set after power on */ + __crlmodule_enable_param(sensor, SENSOR_POWERED_ON, false); + + mutex_lock(&sensor->mutex); + crlmodule_update_current_mode(sensor); + mutex_unlock(&sensor->mutex); + + return rval; +} + + +/* + * This function handles sensor power up routine failure because of any failed + * step in the routine. The index "i" is the index to last successfull power + * sequence entity successfull completed. This function executes the power + * senquence entities in the reverse or with undo value. + */ +static void crlmodule_undo_poweron_entities( + struct crl_sensor *sensor, + int rev_idx) +{ + struct i2c_client *client = sensor->src->sd.client; + struct crl_power_seq_entity *entity; + int idx; + + for (idx = rev_idx; idx >= 0; idx--) { + entity = &sensor->pwr_entity[idx]; + dev_dbg(&client->dev, "%s power type %d index %d\n", + __func__, entity->type, idx); + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + gpio_set_value(sensor->platform_data->xshutdown, + entity->undo_val); + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + gpio_set_value(entity->ent_number, entity->undo_val); + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + regulator_disable(entity->regulator_priv); + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + clk_disable_unprepare(sensor->xclk); + break; + default: + dev_err(&client->dev, "%s Invalid power type\n", __func__); + break; + } + + if (entity->delay) + usleep_range(entity->delay, entity->delay + 10); + } +} + +static int __crlmodule_powerup_sequence(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + struct crl_power_seq_entity *entity; + unsigned idx; + int rval; + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) { + entity = &sensor->pwr_entity[idx]; + dev_dbg(&client->dev, "%s power type %d index %d\n", + __func__, entity->type, idx); + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + gpio_set_value(sensor->platform_data->xshutdown, entity->val); + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + gpio_set_value(entity->ent_number, entity->val); + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + rval = regulator_enable(entity->regulator_priv); + if (rval) { + dev_err(&client->dev, "Failed to enable regulator: %d\n", + rval); + devm_regulator_put(entity->regulator_priv); + entity->regulator_priv = NULL; + goto error; + } + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + rval = clk_set_rate(sensor->xclk, sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock freq to %u\n", + sensor->platform_data->ext_clk); + goto error; + } + if (clk_get_rate(sensor->xclk) != sensor->platform_data->ext_clk) + dev_warn(&client->dev, + "warning: unable to set accurate clock freq %u\n", + sensor->platform_data->ext_clk); + rval = clk_prepare_enable(sensor->xclk); + if (rval) { + dev_err(&client->dev, "Failed to enable clock: %d\n", rval); + goto error; + } + break; + default: + dev_err(&client->dev, "Invalid power type\n"); + rval = -ENODEV; + goto error; + break; + } + + if (entity->delay) + usleep_range(entity->delay, entity->delay + 10); + } + return 0; +error: + dev_err(&client->dev, "Error:Power sequece failed\n"); + if (idx > 0) + crlmodule_undo_poweron_entities(sensor, idx-1); + return rval; +} + +static int crlmodule_set_power( + struct ici_isys_node* node, + int on) +{ + struct ici_ext_subdev *subdev = node->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + int ret = 0; + + pr_err("crlmodule_set_power %d\n", on); + if (on) { + ret = pm_runtime_get_sync(&client->dev); + pr_err("crlmodule_set_power val %d\n", ret); + if (ret < 0) { + pm_runtime_put(&client->dev); + return ret; + } + } + + mutex_lock(&sensor->power_mutex); + if (on && !sensor->power_count) { + usleep_range(2000, 3000); + ret = crlmodule_run_poweron_init(sensor); + if (ret < 0) { + pr_err("crlmodule_set_power err (2) %d\n", ret); + pm_runtime_put(&client->dev); + goto out; + } + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + + if (!on) + pm_runtime_put(&client->dev); + + pr_err("crlmodule_set_power ret %d\n", ret); + return ret; +} + +/* + * Function main code replicated from /drivers/media/i2c/smiapp/smiapp-core.c + * Modified based on the CRL Module changes + */ +static int crlmodule_init_subdevs( + struct ici_ext_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + struct crl_subdev *prev_sd = NULL; + int i = 0; + struct crl_subdev *sd; + int rval = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + + /* + * The scaler, binner and PA order matters. Sensor configuration file + * must maintain this order. PA sub dev is a must and binner and + * scaler can be omitted based on the sensor. But if scaler is present + * it must be the first sd. + */ + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_SCALER) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_BINNER) { + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + if (sensor->sensor_ds->subdevs[i].subdev_type + == CRL_SUBDEV_TYPE_PIXEL_ARRAY) { + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + i++; + } + + /* CRL MediaCTL IF driver can't handle if none of these sd's present! */ + if (!sensor->ssds_used) { + dev_err(&client->dev, "%s no subdevs present\n", __func__); + return -ENODEV; + } + + if (!sensor->sensor_ds->pll_config_items) { + dev_err(&client->dev, "%s no pll configurations\n", __func__); + return -ENODEV; + } + + /* TODO validate rest of the settings from the sensor definition file */ + + dev_dbg(&client->dev, "%s subdevs: %d\n", __func__, i); + + for (i = 0; i < sensor->ssds_used; i++) { + sd = &sensor->ssds[i]; + + sd->sensor = sensor; + if (sd == sensor->pixel_array) { + sd->npads = 1; + } else { + sd->npads = 2; + sd->source_pad = 1; + } + + sd->sink_fmt.width = + sensor->sensor_ds->sensor_limits->x_addr_max; + sd->sink_fmt.height = + sensor->sensor_ds->sensor_limits->y_addr_max; + sd->compose.width = sd->sink_fmt.width; + sd->compose.height = sd->sink_fmt.height; + sd->crop[sd->source_pad] = sd->compose; + //sd->pads[sd->source_pad].flags = ICI_PAD_FLAGS_SOURCE; + if (sd != sensor->pixel_array) { + sd->crop[sd->sink_pad] = sd->compose; + //sd->pads[sd->sink_pad].flags = ICI_PAD_FLAGS_SINK; + } + + rval = init_ext_sd(client, sd, i); + if (rval) + return rval; + + if (prev_sd == NULL) { + prev_sd = sd; + continue; + } + + if (sensor->reg.create_link) { + rval = sensor->reg.create_link(&sd->sd.node, + sd->source_pad, + &prev_sd->sd.node, + prev_sd->sink_pad, + 0); + if (rval) + return rval; + } + prev_sd = sd; + } + + return rval; +} + +static int __init_power_resources( + struct ici_ext_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + struct crl_power_seq_entity *entity; + unsigned idx; + + sensor->pwr_entity = devm_kzalloc(&client->dev, + sizeof(struct crl_power_seq_entity) * + sensor->sensor_ds->power_items, GFP_KERNEL); + + if (!sensor->pwr_entity) + return -ENOMEM; + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) + sensor->pwr_entity[idx] = + sensor->sensor_ds->power_entities[idx]; + + dev_dbg(&client->dev, "%s\n", __func__); + + for (idx = 0; idx < sensor->sensor_ds->power_items; idx++) { + int rval; + entity = &sensor->pwr_entity[idx]; + + switch (entity->type) { + case CRL_POWER_ETY_GPIO_FROM_PDATA: + if (devm_gpio_request_one(&client->dev, + sensor->platform_data->xshutdown, 0, + "CRL xshutdown") != 0) { + dev_err(&client->dev, "unable to acquire xshutdown %d\n", + sensor->platform_data->xshutdown); + return -ENODEV; + } + break; + case CRL_POWER_ETY_GPIO_CUSTOM: + if (devm_gpio_request_one(&client->dev, + entity->ent_number, 0, + "CRL Custom") != 0) { + dev_err(&client->dev, "unable to acquire custom gpio %d\n", + entity->ent_number); + return -ENODEV; + } + break; + case CRL_POWER_ETY_REGULATOR_FRAMEWORK: + entity->regulator_priv = devm_regulator_get(&client->dev, + entity->ent_name); + if (IS_ERR(entity->regulator_priv)) { + dev_err(&client->dev, "Failed to get regulator: %s\n", + entity->ent_name); + entity->regulator_priv = NULL; + return -ENODEV; + } + rval = regulator_set_voltage(entity->regulator_priv, + entity->val, + entity->val); + /* Not all regulator supports voltage change */ + if (rval < 0) + dev_info(&client->dev, + "Failed to set voltage %s %d\n", + entity->ent_name, entity->val); + break; + case CRL_POWER_ETY_CLK_FRAMEWORK: + sensor->xclk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(sensor->xclk)) { + dev_err(&client->dev, "Cannot get sensor clk\n"); + return -ENODEV; + } + break; + default: + dev_err(&client->dev, "Invalid Power item\n"); + return -ENODEV; + } + } + return 0; +} + +static int crlmodule_registered( + struct ici_ext_subdev_register *reg) +{ + struct ici_ext_subdev* subdev = reg->sd; + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + + int rval; + + if (!reg->sd || !reg->setup_node || !reg->create_link) + return -EINVAL; + + rval = __init_power_resources(subdev); + if (rval) + return -ENODEV; + + + /* Power up the sensor */ + if (pm_runtime_get_sync(&client->dev) < 0) { + pm_runtime_put(&client->dev); + return -ENODEV; + } + + /* one time init */ + rval = crlmodule_write_regs(sensor, sensor->sensor_ds->onetime_init_regs, + sensor->sensor_ds->onetime_init_regs_items); + if (rval) { + dev_err(&client->dev, "%s failed to set powerup registers\n", + __func__); + return -ENODEV; + } + + /* sensor specific init */ + if (sensor->sensor_ds->sensor_init) { + rval = sensor->sensor_ds->sensor_init(client); + + if (rval) { + dev_err(&client->dev, "%s failed to run sensor specific init\n", + __func__); + return -ENODEV; + } + } + /* Identify the module */ + rval = crlmodule_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out; + } + + sensor->reg = *reg; + + rval = crlmodule_init_subdevs(subdev); + if (rval) + goto out; + + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + sensor->scale_m = 1; + sensor->flip_info = CRL_FLIP_DEFAULT_NONE; + sensor->ext_ctrl_impacts_pll_selection = false; + sensor->ext_ctrl_impacts_mode_selection = false; + + rval = crlmodule_init_controls(sensor); + if (rval) + goto out; + + mutex_lock(&sensor->mutex); + crlmodule_update_current_mode(sensor); + mutex_unlock(&sensor->mutex); + rval = crlmodule_nvm_init(sensor); + +out: + dev_dbg(&client->dev, "%s rval: %d\n", __func__, rval); + /* crlmodule_power_off(sensor); */ + pm_runtime_put(&client->dev); + + return rval; +} + +static void crlmodule_unregistered( + struct ici_ext_subdev *subdev) +{ + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + struct i2c_client *client = sensor->src->sd.client; + dev_dbg(&client->dev, "%s\n", __func__); +} + +static int init_ext_sd(struct i2c_client *client, + struct crl_subdev *ssd, int idx) +{ + int rval; + struct ici_ext_subdev* sd = &ssd->sd; + struct crl_sensor *sensor = ssd->sensor; + char name[ICI_MAX_NODE_NAME]; + if (sensor->platform_data->suffix) + snprintf(name, + sizeof(name), "%s %c", + sensor->sensor_ds->subdevs[idx].name, + sensor->platform_data->suffix); + else + snprintf(name, + sizeof(name), "%s", + sensor->sensor_ds->subdevs[idx].name); + + sd->client = client; + sd->num_pads = ssd->npads; + sd->src_pad = ssd->source_pad; + sd->set_param = crlmodule_set_param; + sd->get_param = crlmodule_get_param; + sd->get_menu_item = crlmodule_get_menu_item; + if (sensor->reg.setup_node) { + rval = sensor->reg.setup_node(sensor->reg.ipu_data, + sd, name); + if (rval) + return rval; + } + sd->node.node_set_power = crlmodule_set_power; + sd->node.node_set_streaming = crlmodule_set_stream; + sd->node.node_get_pad_supported_format = + crlmodule_enum_pixelformat; + sd->node.node_set_pad_ffmt = crlmodule_set_format; + sd->node.node_get_pad_ffmt = crlmodule_get_format; + sd->node.node_set_pad_sel = crlmodule_set_selection; + sd->node.node_get_pad_sel = crlmodule_get_selection; + + return 0; +} + +#ifdef CONFIG_PM + +static int crlmodule_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ici_ext_subdev *sd = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(sd); + + crlmodule_undo_poweron_entities(sensor, + sensor->sensor_ds->power_items - 1); + return 0; +} + +static int crlmodule_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ici_ext_subdev *sd = + i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + + if (sensor->streaming) + crlmodule_stop_streaming(sensor); + + crlmodule_undo_poweron_entities(sensor, + sensor->sensor_ds->power_items - 1); + return 0; +} + +static int crlmodule_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ici_ext_subdev *sd = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(sd); + + return __crlmodule_powerup_sequence(sensor); +} + +static int crlmodule_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ici_ext_subdev *sd = + i2c_get_clientdata(client); + struct crl_subdev *ssd = to_crlmodule_subdev(sd); + struct crl_sensor *sensor = ssd->sensor; + int rval; + + rval = __crlmodule_powerup_sequence(sensor); + if (!rval && sensor->power_count) + rval = crlmodule_run_poweron_init(sensor); + if (!rval && sensor->streaming) + rval = crlmodule_start_streaming(sensor); + + return rval; +} + +#else + +#define crlmodule_runtime_suspend NULL +#define crlmodule_runtime_resume NULL +#define crlmodule_suspend NULL +#define crlmodule_resume NULL + +#endif /* CONFIG_PM */ + + +static int crlmodule_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct crl_sensor *sensor; + int ret; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + /* TODO! Create the sensor based on the interface */ + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + + ret = crlmodule_populate_ds(sensor, &client->dev); + if (ret) + return -ENODEV; + + sensor->src = &sensor->ssds[sensor->ssds_used]; + sensor->src->sensor = sensor; + + sensor->src->sd.client = client; + sensor->src->sd.do_register = crlmodule_registered; + sensor->src->sd.do_unregister = crlmodule_unregistered; + i2c_set_clientdata(client, &sensor->src->sd); + + pm_runtime_enable(&client->dev); + + /* Load IQ tuning registers from drvb file*/ + if (sensor->sensor_ds->msr_file_name) { + ret = crlmodule_load_msrlist(client, + sensor->sensor_ds->msr_file_name, + &sensor->msr_list); + if (ret) + dev_warn(&client->dev, + "msrlist loading failed. Ignore, move on\n"); + } else { + /* sensor will still continue streaming */ + dev_warn(&client->dev, "No msrlists associated with sensor\n"); + } + + return 0; +} + +static int crlmodule_remove(struct i2c_client *client) +{ + struct ici_ext_subdev *subdev = + i2c_get_clientdata(client); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + unsigned int i; + + if (sensor->sensor_ds->sensor_cleanup) + sensor->sensor_ds->sensor_cleanup(client); + + for (i = 0; i < sensor->ssds_used; i++) { + struct ici_ext_subdev *sd = + &sensor->ssds[i].sd; + if (sd->do_unregister) + sd->do_unregister(sd); + } + + i2c_set_clientdata(client, NULL); + + crlmodule_nvm_deinit(sensor); + crlmodule_release_ds(sensor); + crlmodule_release_msrlist(&sensor->msr_list); + + pm_runtime_disable(&client->dev); + + return 0; +} + + +static const struct i2c_device_id crlmodule_id_table[] = { + { CRLMODULE_LITE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, crlmodule_id_table); + +static const struct dev_pm_ops crlmodule_pm_ops = { + .runtime_suspend = crlmodule_runtime_suspend, + .runtime_resume = crlmodule_runtime_resume, + .suspend = crlmodule_suspend, + .resume = crlmodule_resume, +}; + +static struct i2c_driver crlmodule_i2c_driver = { + .driver = { + .name = CRLMODULE_LITE_NAME, + .pm = &crlmodule_pm_ops, + }, + .probe = crlmodule_probe, + .remove = crlmodule_remove, + .id_table = crlmodule_id_table, +}; + +module_i2c_driver(crlmodule_i2c_driver); + +MODULE_AUTHOR("Vinod Govindapillai "); +MODULE_AUTHOR("Jouni Ukkonen "); +MODULE_AUTHOR("Tommi Franttila "); +MODULE_DESCRIPTION("Generic driver for common register list based camera sensor modules"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-data.c b/drivers/media/i2c/crlmodule-lite/crlmodule-data.c new file mode 100644 index 000000000000..c22dc1fbf87a --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-data.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include "crlmodule.h" +#include "crl_adv7481_cvbs_configuration.h" +#include "crl_adv7481_hdmi_configuration.h" +#include "crl_adv7481_eval_configuration.h" +#include "crl_magna_configuration_ti964.h" + +static const struct crlmodule_sensors supported_sensors[] = { + { "ADV7481 CVBS", "adv7481_cvbs", &adv7481_cvbs_crl_configuration }, + { "ADV7481 HDMI", "adv7481_hdmi", &adv7481_hdmi_crl_configuration }, + { "ADV7481_EVAL", "adv7481_eval", &adv7481_eval_crl_configuration }, + { "ADV7481B_EVAL", "adv7481b_eval", &adv7481b_eval_crl_configuration }, + { "MAGNA_TI964", "magna_ti964", &magna_ti964_crl_configuration }, + { "i2c-ADV7481A:00", "adv7481_hdmi", &adv7481_hdmi_crl_configuration }, + { "i2c-ADV7481B:00", "adv7481_cvbs", &adv7481_cvbs_crl_configuration }, +}; + +/* + * Function to populate the CRL data structure from the sensor configuration + * definition file + */ +int crlmodule_populate_ds(struct crl_sensor *sensor, struct device *dev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_sensors); i++) { + /* Check the ACPI supported modules */ + if (!strcmp(dev_name(dev), supported_sensors[i].pname)) { + sensor->sensor_ds = supported_sensors[i].ds; + dev_info(dev, "%s %s selected\n", + __func__, supported_sensors[i].name); + return 0; + }; + + /* Check the non ACPI modules */ + if (!strcmp(sensor->platform_data->module_name, + supported_sensors[i].pname)) { + sensor->sensor_ds = supported_sensors[i].ds; + dev_info(dev, "%s %s selected\n", + __func__, supported_sensors[i].name); + return 0; + }; + } + + dev_err(dev, "%s No suitable configuration found for %s\n", + __func__, dev_name(dev)); + return -EINVAL; +} + +/* + * Function validate the contents CRL data structure to check if all the + * required fields are filled and are according to the limits. + */ +int crlmodule_validate_ds(struct crl_sensor *sensor) +{ + /* TODO! Revisit this. */ + return 0; +} + +/* Function to free all resources allocated for the CRL data structure */ +void crlmodule_release_ds(struct crl_sensor *sensor) +{ + /* + * TODO! Revisit this. + * Place for cleaning all the resources used for the generation + * of CRL data structure. + */ +} + diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.c b/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.c new file mode 100644 index 000000000000..c2ad74e59be9 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include "crlmodule-msrlist.h" +#include "crlmodule.h" + +/* + * + * DRVB file is part of the old structure of tagged + * binary container, which is used as such in crlmodule. + * Changes needs to be done in cameralibs to remove the + * tagged structure and convert to untagged drvb format. + * Below are the tagged binary data container structure + * definitions. Most of it is copied from libmsrlisthelper.c + * and some changes done for crlmodule. + * + */ + +static int crlmodule_write_msrlist(struct i2c_client *client, u8 *bufptr, + unsigned int size) +{ + /* + * + * The configuration data contains any number of sequences where + * the first byte (that is, u8) that marks the number of bytes + * in the sequence to follow, is indeed followed by the indicated + * number of bytes of actual data to be written to sensor. + * By convention, the first two bytes of actual data should be + * understood as an address in the sensor address space (hibyte + * followed by lobyte) where the remaining data in the sequence + * will be written. + * + */ + + u8 *ptr = bufptr; + int ret; + + while (ptr < bufptr + size) { + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + }; + + msg.len = *ptr++; + msg.buf = ptr; + ptr += msg.len; + + if (ptr > bufptr + size) + return -EINVAL; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "i2c write error: %d", ret); + return ret; + } + } + return 0; +} + +static int crlmodule_parse_msrlist(struct i2c_client *client, u8 *buffer, + unsigned int size) +{ + u8 *endptr8 = buffer + size; + int ret; + unsigned int dataset = 0; + struct tbd_data_record_header *header = + (struct tbd_data_record_header *)buffer; + + do { + + if ((u8 *)header + sizeof(*header) > endptr8) + return -EINVAL; + + if ((u8 *)header + header->data_offset + + header->data_size > endptr8) + return -EINVAL; + + dataset++; + + if (header->data_size && (header->flags & 1)) { + + ret = crlmodule_write_msrlist(client, + buffer + header->data_offset, + header->data_size); + if (ret) + return ret; + } + header = (struct tbd_data_record_header *)(buffer + + header->next_offset); + } while (header->next_offset); + + return 0; +} + + +int crlmodule_apply_msrlist(struct i2c_client *client, + const struct firmware *fw) +{ + struct tbd_header *header; + struct tbd_record_header *record; + + header = (struct tbd_header *)fw->data; + record = (struct tbd_record_header *)(header + 1); + + if (record->size && record->class_id != TBD_CLASS_DRV_ID) + return -EINVAL; + + return crlmodule_parse_msrlist(client, (u8 *)(record + 1), + record->size); +} + + +int crlmodule_load_msrlist(struct i2c_client *client, char *name, + const struct firmware **fw) +{ + + struct tbd_header *header; + struct tbd_record_header *record; + int ret = -ENOENT; + + ret = request_firmware(fw, name, &client->dev); + if (ret) { + dev_err(&client->dev, + "Error %d while requesting firmware %s\n", + ret, name); + return ret; + } + header = (struct tbd_header *)(*fw)->data; + + if (sizeof(*header) > (*fw)->size) + goto out; + + /* Check that we have drvb block. */ + if (memcmp(&header->tag, "DRVB", 4)) + goto out; + + if (header->size != (*fw)->size) + goto out; + + if (sizeof(*header) + sizeof(*record) > (*fw)->size) + goto out; + + + return 0; + +out: + crlmodule_release_msrlist(fw); + return ret; +} + + +void crlmodule_release_msrlist(const struct firmware **fw) +{ + release_firmware(*fw); + *fw = NULL; +} diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.h b/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.h new file mode 100644 index 000000000000..2b296c9f9d74 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-msrlist.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_MSRLIST_H__ +#define __CRLMODULE_MSRLIST_H__ + +#define TBD_CLASS_DRV_ID 2 + +struct i2c_client; +struct firmware; + +struct tbd_header { + /* Tag identifier, also checks endianness */ + u32 tag; + /* Container size including this header */ + u32 size; + /* Version, format 0xYYMMDDVV */ + u32 version; + /* Revision, format 0xYYMMDDVV */ + u32 revision; + /* Configuration flag bits set */ + u32 config_bits; + /* Global checksum, header included */ + u32 checksum; +} __packed; + +struct tbd_record_header { + /* Size of record including header */ + u32 size; + /* tbd_format_t enumeration values used */ + u8 format_id; + /* Packing method; 0 = no packing */ + u8 packing_key; + /* tbd_class_t enumeration values used */ + u16 class_id; +} __packed; + +struct tbd_data_record_header { + u16 next_offset; + u16 flags; + u16 data_offset; + u16 data_size; +} __packed; + +int crlmodule_load_msrlist(struct i2c_client *client, char *name, + const struct firmware **fw); +int crlmodule_apply_msrlist(struct i2c_client *client, + const struct firmware *fw); +void crlmodule_release_msrlist(const struct firmware **fw); + +#endif /* ifndef __CRLMODULE_MSRLIST_H__ */ diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.c b/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.c new file mode 100644 index 000000000000..935a967a525a --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" + +static ssize_t crlmodule_sysfs_nvm_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ici_ext_subdev *subdev = + i2c_get_clientdata(to_i2c_client(dev)); + struct crl_sensor *sensor = to_crlmodule_sensor(subdev); + + memcpy(buf, sensor->nvm_data, sensor->nvm_size); + return sensor->nvm_size; +} + +DEVICE_ATTR(nvm, S_IRUGO, crlmodule_sysfs_nvm_read, NULL); + +static unsigned int crlmodule_get_nvm_size(struct crl_sensor *sensor) +{ + + struct i2c_client *client = sensor->src->sd.client; + unsigned int i, size = 0; + + for (i = 0; i < sensor->sensor_ds->crl_nvm_info.nvm_blobs_items; i++) + size += sensor->sensor_ds->crl_nvm_info.nvm_config[i].size; + + if (size > PAGE_SIZE) { + dev_err(&client->dev, "nvm size too big\n"); + size = 0; + } + return size; +} + +static int crlmodule_get_nvm_data(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + int i; + int rval = 0; + + u8 *nvm_data = sensor->nvm_data; + + if (sensor->sensor_ds->crl_nvm_info.nvm_preop_regs_items) { + dev_dbg(&client->dev, + "%s perform pre-operations\n", __func__); + + rval = crlmodule_write_regs( + sensor, + sensor->sensor_ds->crl_nvm_info.nvm_preop_regs, + sensor->sensor_ds->crl_nvm_info.nvm_preop_regs_items); + if (rval) { + dev_err(&client->dev, + "failed to perform nvm pre-operations\n"); + return rval; + } + } + + for (i = 0; i < sensor->sensor_ds->crl_nvm_info.nvm_blobs_items; i++) { + + dev_dbg(&client->dev, + "%s read blob %d dev_addr: 0x%x start_addr: 0x%x size: %d", + __func__, i, + sensor->sensor_ds->crl_nvm_info.nvm_config->dev_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->start_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->size); + + crlmodule_block_read(sensor, + sensor->sensor_ds->crl_nvm_info.nvm_config->dev_addr, + sensor->sensor_ds->crl_nvm_info.nvm_config->start_addr, + sensor->sensor_ds->crl_nvm_info.nvm_flags + & CRL_NVM_ADDR_MODE_MASK, + sensor->sensor_ds->crl_nvm_info.nvm_config->size, + nvm_data); + + nvm_data += sensor->sensor_ds->crl_nvm_info.nvm_config->size; + sensor->sensor_ds->crl_nvm_info.nvm_config++; + } + + if (sensor->sensor_ds->crl_nvm_info.nvm_postop_regs_items) { + dev_dbg(&client->dev, "%s perform post-operations\n", + __func__); + rval = crlmodule_write_regs( + sensor, + sensor->sensor_ds->crl_nvm_info.nvm_postop_regs, + sensor->sensor_ds->crl_nvm_info.nvm_postop_regs_items); + if (rval) { + dev_err(&client->dev, + "failed to perform nvm post-operations\n"); + return rval; + } + } + return rval; +} + +int crlmodule_nvm_init(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int size = crlmodule_get_nvm_size(sensor); + int rval; + + if (size) { + sensor->nvm_data = devm_kzalloc(&client->dev, size, GFP_KERNEL); + if (sensor->nvm_data == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + return -ENOMEM; + } + sensor->nvm_size = size; + + rval = crlmodule_get_nvm_data(sensor); + if (rval) + goto err; + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto err; + } + } + + return 0; +err: + sensor->nvm_size = 0; + return rval; +} + +void crlmodule_nvm_deinit(struct crl_sensor *sensor) +{ + struct i2c_client *client = sensor->src->sd.client; + + if (sensor->nvm_size) { + device_remove_file(&client->dev, &dev_attr_nvm); + sensor->nvm_size = 0; + } +} diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.h b/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.h new file mode 100644 index 000000000000..9cbabfa950bd --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-nvm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_NVM_H_ +#define __CRLMODULE_NVM_H_ + +#include "crlmodule.h" + +#define CRL_NVM_ADDR_MODE_8BIT 0x00000001 +#define CRL_NVM_ADDR_MODE_16BIT 0x00000002 + +#define CRL_NVM_ADDR_MODE_MASK (CRL_NVM_ADDR_MODE_8BIT | \ + CRL_NVM_ADDR_MODE_16BIT) + + +int crlmodule_nvm_init(struct crl_sensor *sensor); +void crlmodule_nvm_deinit(struct crl_sensor *sensor); + +#endif /* __CRLMODULE_NVM_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-regs.c b/drivers/media/i2c/crlmodule-lite/crlmodule-regs.c new file mode 100644 index 000000000000..d7b6d0181410 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-regs.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include + +#include "crlmodule.h" +#include "crlmodule-nvm.h" +#include "crlmodule-regs.h" + +static int crlmodule_i2c_read(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 reg, + u8 len, u32 *val) +{ + struct i2c_client *client = sensor->src->sd.client; + struct i2c_msg msg[2]; + unsigned char data[4]; + int r; + + dev_dbg(&client->dev, "%s reg, len: [0x%04x, %d]", __func__, reg, len); + + if (len != CRL_REG_LEN_08BIT && len != CRL_REG_LEN_16BIT && + len != CRL_REG_LEN_24BIT && len != CRL_REG_LEN_32BIT) + return -EINVAL; + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) + msg[0].addr = client->addr; + else + msg[0].addr = dev_i2c_addr; + + msg[1].addr = msg[0].addr; + + msg[0].flags = 0; + msg[0].buf = data; + + if (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) { + msg[0].addr = msg[0].addr>>1; + msg[1].addr = msg[1].addr>>1; + } + + if ((sensor->sensor_ds->addr_len == CRL_ADDR_8BIT) || + (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT)) { + data[0] = (u8) (reg & 0xff); + msg[0].len = 1; + } else { + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + msg[0].len = 2; + } + + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + msg[1].len = len; + + r = i2c_transfer(client->adapter, msg, 2); + + if (r < 0) { + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case CRL_REG_LEN_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case CRL_REG_LEN_24BIT: + *val = (data[0] << 16) + (data[1] << 8) + data[2]; + break; + case CRL_REG_LEN_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case CRL_REG_LEN_08BIT: + *val = data[0]; + break; + } + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); + + return r; +} + +static int crlmodule_i2c_write(struct crl_sensor *sensor, u16 dev_i2c_addr, + u16 reg, u8 len, u32 val) +{ + struct i2c_client *client = sensor->src->sd.client; + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + int r; + unsigned char *data_offset; + + if (len != CRL_REG_LEN_08BIT && len != CRL_REG_LEN_16BIT && + len != CRL_REG_LEN_24BIT && len != CRL_REG_LEN_32BIT) + return -EINVAL; + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) + msg.addr = client->addr; + else + msg.addr = dev_i2c_addr; + + msg.flags = 0; /* Write */ + msg.buf = data; + + if (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT) + msg.addr = msg.addr>>1; + if ((sensor->sensor_ds->addr_len == CRL_ADDR_8BIT) || + (sensor->sensor_ds->addr_len == CRL_ADDR_7BIT)) { + data[0] = (u8) (reg & 0xff); + msg.len = 1 + len; + data_offset = &data[1]; + } else { + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + msg.len = 2 + len; + data_offset = &data[2]; + } + + dev_dbg(&client->dev, "%s len reg, val: [%d, 0x%04x, 0x%04x]", + __func__, len, reg, val); + + switch (len) { + case CRL_REG_LEN_08BIT: + data_offset[0] = val; + break; + case CRL_REG_LEN_16BIT: + data_offset[0] = val >> 8; + data_offset[1] = val; + break; + case CRL_REG_LEN_24BIT: + data_offset[0] = val >> 16; + data_offset[1] = val >> 8; + data_offset[2] = val; + break; + case CRL_REG_LEN_32BIT: + data_offset[0] = val >> 24; + data_offset[1] = val >> 16; + data_offset[2] = val >> 8; + data_offset[3] = val; + break; + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. retries: %d\n", + retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, reg, r); + + return r; +} + +int crlmodule_read_reg(struct crl_sensor *sensor, + const struct crl_register_read_rep reg, u32 *val) +{ + return crlmodule_i2c_read(sensor, reg.dev_i2c_addr, reg.address, + reg.len, val); +} + +int crlmodule_write_regs(struct crl_sensor *sensor, + const struct crl_register_write_rep *regs, int len) +{ + struct i2c_client *client = sensor->src->sd.client; + unsigned int i; + int ret; + u32 val; + + for (i = 0; i < len; i++) { + /* + * Sensor setting sequence may need some delay. + * delay value is specified by reg.val field + */ + if (regs[i].len == CRL_REG_LEN_DELAY) { + msleep(regs[i].val); + continue; + } + /* + * If the same register is being used for two settings, updating + * one value should not overwrite the other one. Such registers + * must be marked as CRL_REG_READ_AND_UPDATE. For such registers + * first read the register and update it + */ + val = regs[i].val; + if (regs[i].len & CRL_REG_READ_AND_UPDATE) { + ret = crlmodule_i2c_read(sensor, regs[i].dev_i2c_addr, + regs[i].address, + regs[i].len & CRL_REG_LEN_READ_MASK, &val); + if (ret) + return ret; + val |= regs[i].val; + } + + ret = crlmodule_i2c_write(sensor, regs[i].dev_i2c_addr, + regs[i].address, + regs[i].len & CRL_REG_LEN_READ_MASK, + val); + if (ret < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + ret, regs[i].address, regs[i].val); + return ret; + } + }; + + return 0; +} + +int crlmodule_write_reg(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 reg, + u8 len, u32 mask, u32 val) +{ + struct i2c_client *client = sensor->src->sd.client; + int ret; + u32 val2; + + /* + * Sensor setting sequence may need some delay. + * delay value is specified by reg.val field + */ + if (len == CRL_REG_LEN_DELAY) { + msleep(val); + return 0; + } + + /* + * If the same register is being used for two settings, updating + * one value should not overwrite the other one. Such registers + * must be marked as CRL_REG_READ_AND_UPDATE. For such registers + * first read the register and update it + */ + if (len & CRL_REG_READ_AND_UPDATE) { + u32 tmp; + + ret = crlmodule_i2c_read(sensor, dev_i2c_addr, reg, + len & CRL_REG_LEN_READ_MASK, &val2); + if (ret) + return ret; + + tmp = val2 & ~mask; + tmp |= val & mask; + val = tmp; + } else { + val &= mask; + } + + ret = crlmodule_i2c_write(sensor, dev_i2c_addr, reg, + len & CRL_REG_LEN_READ_MASK, val); + if (ret < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + ret, reg, val); + return ret; + } + + return 0; +} + +int crlmodule_block_read(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 addr, + u8 addr_mode, u16 len, u8 *buf) +{ + struct i2c_client *client = sensor->src->sd.client; + struct i2c_msg msg[2]; + u8 data[2]; + u16 offset = 0; + int r; + + memset(msg, 0, sizeof(msg)); + + if (dev_i2c_addr == CRL_I2C_ADDRESS_NO_OVERRIDE) { + msg[0].addr = client->addr; + msg[1].addr = client->addr; + } else { + msg[0].addr = dev_i2c_addr; + msg[1].addr = dev_i2c_addr; + } + + if (addr_mode & CRL_NVM_ADDR_MODE_8BIT) + msg[0].len = 1; + else if (addr_mode & CRL_NVM_ADDR_MODE_16BIT) + msg[0].len = 2; + else + return -EINVAL; + + msg[0].flags = 0; + msg[1].flags = I2C_M_RD; + + while (offset < len) { + if (addr_mode & CRL_NVM_ADDR_MODE_8BIT) { + data[0] = addr & 0xff; + } else { + data[0] = (addr >> 8) & 0xff; + data[1] = addr & 0xff; + } + + msg[0].buf = data; + msg[1].len = min(CRLMODULE_I2C_BLOCK_SIZE, len - offset); + msg[1].buf = &buf[offset]; + r = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (r != ARRAY_SIZE(msg)) { + if (r >= 0) + r = -EIO; + goto err; + } + addr += msg[1].len; + offset += msg[1].len; + } + return 0; +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + return r; +} diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-regs.h b/drivers/media/i2c/crlmodule-lite/crlmodule-regs.h new file mode 100644 index 000000000000..45341a16025d --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-regs.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_REGS_H_ +#define __CRLMODULE_REGS_H_ + +struct crl_sensor; +struct crl_register_read_rep; +struct crl_register_write_rep; + +#define CRLMODULE_I2C_BLOCK_SIZE 0x20 + +int crlmodule_read_reg(struct crl_sensor *sensor, + const struct crl_register_read_rep reg, u32 *val); +int crlmodule_write_regs(struct crl_sensor *sensor, + const struct crl_register_write_rep *regs, int len); +int crlmodule_write_reg(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 reg, + u8 len, u32 mask, u32 val); +int crlmodule_block_read(struct crl_sensor *sensor, u16 dev_i2c_addr, u16 addr, + u8 addr_mode, u16 len, u8 *buf); + +#endif /* __CRLMODULE_REGS_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule-sensor-ds.h b/drivers/media/i2c/crlmodule-lite/crlmodule-sensor-ds.h new file mode 100644 index 000000000000..e8a6a6f85ae7 --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule-sensor-ds.h @@ -0,0 +1,552 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_SENSOR_DS_H_ +#define __CRLMODULE_SENSOR_DS_H_ + +#include "crlmodule.h" + +#define CRL_SUBDEVS 3 + +/* Index for subdevs in any structure with multiple SDs */ +#define CRL_SD_PA_INDEX 0 +#define CRL_SD_BINNER_INDEX 1 +#define CRL_SD_SCALER_INDEX 2 + +#define CRL_REG_LEN_08BIT 1 +#define CRL_REG_LEN_16BIT 2 +#define CRL_REG_LEN_24BIT 3 +#define CRL_REG_LEN_32BIT 4 + +#define CRL_REG_READ_AND_UPDATE (1 << 3) +#define CRL_REG_LEN_READ_MASK 0x07 +#define CRL_REG_LEN_DELAY 0x10 + +#define CRL_FLIP_DEFAULT_NONE 0 +#define CRL_FLIP_HFLIP 1 +#define CRL_FLIP_VFLIP 2 +#define CRL_FLIP_HFLIP_VFLIP 3 + +#define CRL_FLIP_HFLIP_MASK 0xfe +#define CRL_FLIP_VFLIP_MASK 0xfd + +#define CRL_PIXEL_ORDER_GRBG 0 +#define CRL_PIXEL_ORDER_RGGB 1 +#define CRL_PIXEL_ORDER_BGGR 2 +#define CRL_PIXEL_ORDER_GBRG 3 +#define CRL_PIXEL_ORDER_IGNORE 255 + +/* Flag to notify configuration selction imact from Ctrls */ +#define CRL_IMPACTS_NO_IMPACT 0 +#define CRL_IMPACTS_PLL_SELECTION (1 << 1) +#define CRL_IMPACTS_MODE_SELECTION (1 << 2) + +/* + * In crl_dynamic_entity::entity_type is denoted by bits 6 and 7 + * 0 -> crl_dynamic_entity:entity_value is a constant + * 1 -> crl_dynamic_entity:entity_value is a referene to variable + * 2 -> crl_dynamic_entity:entity_value is a ctrl value + * 3 -> crl_dynamic_entity:entity_value is a 8 bit register address + */ +enum crl_dynamic_entity_type { + CRL_DYNAMIC_VAL_OPERAND_TYPE_CONST = 0, + CRL_DYNAMIC_VAL_OPERAND_TYPE_VAR_REF, + CRL_DYNAMIC_VAL_OPERAND_TYPE_CTRL_VAL, + CRL_DYNAMIC_VAL_OPERAND_TYPE_REG_VAL, /* Only 8bit registers */ +}; + +/* + * For some combo device which has some devices inside itself with different + * i2c address, adding flag to specify whether current device needs i2c + * address override. + * For back-compatibility, making flag equals 0. So existing sensor configure + * doesn't need to be modified. + */ +#define CRL_I2C_ADDRESS_NO_OVERRIDE 0 + +struct crl_sensor; +struct i2c_client; + +enum crl_subdev_type { + CRL_SUBDEV_TYPE_SCALER, + CRL_SUBDEV_TYPE_BINNER, + CRL_SUBDEV_TYPE_PIXEL_ARRAY, +}; + +enum crl_ctrl_op_type { + CRL_CTRL_SET_OP, + CRL_CTRL_GET_OP, +}; + +enum crl_ctrl_update_context { + SENSOR_IDLE, /* Powered on. But not streamind */ + SENSOR_STREAMING, /* Sensor streaming */ + SENSOR_POWERED_ON, /* streaming or idle */ +}; + +enum crl_operators { + CRL_BITWISE_AND = 0, + CRL_BITWISE_OR, + CRL_BITWISE_LSHIFT, + CRL_BITWISE_RSHIFT, + CRL_BITWISE_XOR, + CRL_BITWISE_COMPLEMENT, + CRL_ADD, + CRL_SUBTRACT, + CRL_MULTIPLY, + CRL_DIV, + CRL_ASSIGNMENT, +}; + +/* Replicated from videodev2.h */ +enum crl_ctrl_type { + CRL_CTRL_TYPE_INTEGER = 1, + CRL_CTRL_TYPE_BOOLEAN, + CRL_CTRL_TYPE_MENU_INT, + CRL_CTRL_TYPE_MENU_ITEMS, + CRL_CTRL_TYPE_BUTTON, + CRL_CTRL_TYPE_INTEGER64, + CRL_CTRL_TYPE_CTRL_CLASS, + CRL_CTRL_TYPE_CUSTOM, +}; + +enum crl_addr_len { + CRL_ADDR_16BIT = 0, + CRL_ADDR_8BIT, + CRL_ADDR_7BIT, +}; + +enum crl_operands { + CRL_CONSTANT = 0, + CRL_VARIABLE, + CRL_CONTROL, +}; + +/* References to the CRL driver member variables */ +enum crl_member_data_reference_ids { + CRL_VAR_REF_OUTPUT_WIDTH = 1, + CRL_VAR_REF_OUTPUT_HEIGHT, + CRL_VAR_REF_PA_CROP_WIDTH, + CRL_VAR_REF_PA_CROP_HEIGHT, + CRL_VAR_REF_FRAME_TIMING_WIDTH, + CRL_VAR_REF_FRAME_TIMING_HEIGHT, + CRL_VAR_REF_BINNER_WIDTH, + CRL_VAR_REF_BINNER_HEIGHT, + CRL_VAR_REF_H_BINN_FACTOR, + CRL_VAR_REF_V_BINN_FACTOR, + CRL_VAR_REF_SCALE_FACTOR, + CRL_VAR_REF_BITSPERPIXEL, + CRL_VAR_REF_PIXELRATE_PA, + CRL_VAR_REF_PIXELRATE_CSI, + CRL_VAR_REF_PIXELRATE_LINK_FREQ, +}; + +enum crl_frame_desc_type { + CRL_MBUS_FRAME_DESC_TYPE_PLATFORM, + CRL_MBUS_FRAME_DESC_TYPE_PARALLEL, + CRL_MBUS_FRAME_DESC_TYPE_CCP2, + CRL_MBUS_FRAME_DESC_TYPE_CSI2, +}; + +enum crl_pwr_ent_type { + CRL_POWER_ETY_GPIO_FROM_PDATA = 1, + CRL_POWER_ETY_GPIO_CUSTOM, + CRL_POWER_ETY_REGULATOR_FRAMEWORK, + CRL_POWER_ETY_CLK_FRAMEWORK, +}; + +struct crl_dynamic_entity { + enum crl_dynamic_entity_type entity_type; + u32 entity_val; +}; + +struct crl_arithmetic_ops { + enum crl_operators op; + struct crl_dynamic_entity operand; +}; + +struct crl_dynamic_calculated_entity { + u8 ops_items; + struct crl_arithmetic_ops *ops; +}; + +struct crl_register_write_rep { + u16 address; + u8 len; + u32 val; + u16 dev_i2c_addr; + u32 mask; +}; + +struct crl_register_read_rep { + u16 address; + u8 len; + u32 mask; + u16 dev_i2c_addr; +}; + +/* + * crl_dynamic_register_access is used mainly in the ctrl context. + * This is intended to provide some generic arithmetic operations on the values + * to be written to a control's register or on the values read from a register. + * These arithmetic operations are controlled using struct crl_arithmetic_ops. + * + * One important information is that this structure behave differently for the + * set controls and volatile get controls. + * + * For the set control operation, the usage of the members are straight forward. + * The set control can result into multiple register write operations. Hence + * there can be more than one crl_dynamic_register_access entries associated + * with a control which results into separate register writes. + * + * But for the volatile get control operation, where a control is used + * to query read only information from the sensor, there could be only one + * crl_dynamic_register_access entry. Because the result of a get control is + * a single value. crl_dynamic_register_access.address, len and mask values are + * not used in volatile get control context. Instead all the needed information + * must be encoded into member -> ops (struct crl_arithmetic_ops) + */ +struct crl_dynamic_register_access { + u16 address; + u8 len; + u32 mask; + u8 ops_items; + struct crl_arithmetic_ops *ops; + u16 dev_i2c_addr; +}; + +struct crl_sensor_detect_config { + struct crl_register_read_rep reg; /* Register to read */ + unsigned int width; /* width of the value in chars*/ +}; + +struct crl_sensor_subdev_config { + enum crl_subdev_type subdev_type; + char name[32]; +}; + +enum crl_ctrl_flag { + CRL_CTRL_FLAG_UPDATE = 1, + CRL_CTRL_FLAG_READ_ONLY = 2, + CRL_CTRL_FLAG_WRITE_ONLY = 4, +}; + +/* + * The ctrl id value pair which should be compared when selecting a + * configuration. This gives flexibility to provide any data through set ctrl + * and provide selection mechanism for a particular configuration + */ +struct crl_ctrl_data_pair { + u32 ctrl_id; + u32 data; +}; + +enum crl_dep_ctrl_action_type { + CRL_DEP_CTRL_ACTION_TYPE_SELF = 0, + CRL_DEP_CTRL_ACTION_TYPE_DEP_CTRL, +}; + +enum crl_dep_ctrl_condition { + CRL_DEP_CTRL_CONDITION_GREATER = 0, + CRL_DEP_CTRL_CONDITION_LESSER, + CRL_DEP_CTRL_CONDITION_EQUAL, +}; + +enum crl_dep_ctrl_action { + CRL_DEP_CTRL_CONDITION_ADD = 0, + CRL_DEP_CTRL_CONDITION_SUBTRACT, + CRL_DEP_CTRL_CONDITION_MULTIPLY, + CRL_DEP_CTRL_CONDITION_DIVIDE, +}; + +struct crl_dep_ctrl_cond_action { + enum crl_dep_ctrl_condition cond; + u32 cond_value; + enum crl_dep_ctrl_action action; + u32 action_value; +}; + +/* Dependency control provision */ +struct crl_dep_ctrl_provision { + u32 ctrl_id; + enum crl_dep_ctrl_action_type action_type; + unsigned int action_items; + struct crl_dep_ctrl_cond_action *action; +}; + +struct crl_sensor_limits { + unsigned int x_addr_max; + unsigned int y_addr_max; + unsigned int x_addr_min; + unsigned int y_addr_min; + unsigned int min_frame_length_lines; + unsigned int max_frame_length_lines; + unsigned int min_line_length_pixels; + unsigned int max_line_length_pixels; + u8 scaler_m_min; + u8 scaler_m_max; + u8 scaler_n_min; + u8 scaler_n_max; + u8 min_even_inc; + u8 max_even_inc; + u8 min_odd_inc; + u8 max_odd_inc; +}; + +struct crl_ctrl_data_std { + s64 min; + s64 max; + u64 step; + s64 def; +}; + +struct crl_ctrl_data_menu_items { + const char *const *menu; + unsigned int size; +}; + +struct crl_ctrl_data_int_menu { + const s64 *menu; + s64 max; + s64 def; +}; + +union crl_ctrl_data_types { + struct crl_ctrl_data_std std_data; + struct crl_ctrl_data_menu_items menu_items; + struct crl_ctrl_data_int_menu int_menu; +}; + +/* + * Please note a difference in the usage of "regs" member in case of a + * volatile get control for read only purpose. Please check the + * "struct crl_dynamic_register_access" declaration comments for more details. + * + * Read only controls must have "flags" CRL_CTRL_FLAG_READ_ONLY set. + */ +struct crl_ctrl_data { + enum crl_subdev_type sd_type; + enum crl_ctrl_op_type op_type; + enum crl_ctrl_update_context context; + char name[32]; + u32 ctrl_id; + enum crl_ctrl_type type; + union crl_ctrl_data_types data; + unsigned long flags; + u32 impact; /* If this control impact any config selection */ + struct ici_ext_sd_param param; + bool enabled; + unsigned int regs_items; + struct crl_dynamic_register_access *regs; + unsigned int dep_items; + struct crl_dep_ctrl_provision *dep_ctrls; + s64 min; + s64 max; + u64 step; + s64 def; +}; + +struct crl_pll_configuration { + s64 input_clk; + s64 op_sys_clk; + u8 bitsperpixel; + u32 pixel_rate_csi; + u32 pixel_rate_pa; + u8 csi_lanes; + unsigned int comp_items; + struct crl_ctrl_data_pair *ctrl_data; + unsigned int pll_regs_items; + const struct crl_register_write_rep *pll_regs; +}; + +struct crl_subdev_rect_rep { + enum crl_subdev_type subdev_type; + struct ici_rect in_rect; + struct ici_rect out_rect; +}; + +struct crl_mode_rep { + unsigned int sd_rects_items; + const struct crl_subdev_rect_rep *sd_rects; + u8 binn_hor; + u8 binn_vert; + u8 scale_m; + s32 width; + s32 height; + unsigned int comp_items; + struct crl_ctrl_data_pair *ctrl_data; + unsigned int mode_regs_items; + const struct crl_register_write_rep *mode_regs; + + /* + * Minimum and maximum value for line length pixels and frame length + * lines are added for modes. This facilitates easy handling of + * modes which binning skipping and affects the calculation of vblank and + * hblank values. + * + * The blank values are limited based on the following logic + * + * If mode specific limits are available + * vblank = clamp(min_llp - PA_width, max_llp - PA_width) + * hblank = clamp(min_fll - PA_Height, max_fll - PA_Height + * + * If mode specific blanking limits are not available, then the sensor + * limits will be used in the same manner. + * + * If sensor mode limits are not available, then the values will be + * written directly to the associated control registers. + */ + s32 min_llp; /* minimum/maximum value for line length pixels */ + s32 max_llp; + s32 min_fll; + s32 max_fll; /* minimum/maximum value for frame length lines */ +}; + +struct crl_csi_data_fmt { + u32 code; + u8 pixel_order; + u8 bits_per_pixel; + unsigned int regs_items; + const struct crl_register_write_rep *regs; +}; + +struct crl_flip_data { + u8 flip; + u8 pixel_order; +}; + +struct crl_power_seq_entity { + enum crl_pwr_ent_type type; + char ent_name[12]; + int ent_number; + u16 address; + unsigned int val; + unsigned int undo_val; /* Undo value if any previous step failed */ + unsigned int delay; /* delay in micro seconds */ + struct regulator *regulator_priv; /* R/W */ +}; + +struct crl_nvm_blob { + u8 dev_addr; + u16 start_addr; + u16 size; +}; + +struct crl_nvm { + unsigned int nvm_preop_regs_items; + const struct crl_register_write_rep *nvm_preop_regs; + + unsigned int nvm_postop_regs_items; + const struct crl_register_write_rep *nvm_postop_regs; + + unsigned int nvm_blobs_items; + struct crl_nvm_blob *nvm_config; + u32 nvm_flags; +}; + +/* Representation for v4l2_mbus_frame_desc_entry */ +struct crl_frame_desc { + struct crl_dynamic_entity flags; + struct crl_dynamic_entity bpp; + struct crl_dynamic_entity pixelcode; + struct crl_dynamic_entity start_line; + struct crl_dynamic_entity start_pixel; + struct crl_dynamic_calculated_entity width; + struct crl_dynamic_calculated_entity height; + struct crl_dynamic_entity length; + struct crl_dynamic_entity csi2_channel; + struct crl_dynamic_entity csi2_data_type; +}; + +typedef int (*sensor_specific_init)(struct i2c_client*); +typedef int (*sensor_specific_cleanup)(struct i2c_client*); + +struct crl_sensor_configuration { + + const struct crl_clock_entity *clock_entity; + + const unsigned int power_items; + const struct crl_power_seq_entity *power_entities; + const unsigned int power_delay; /* in micro seconds */ + + const unsigned int onetime_init_regs_items; + const struct crl_register_write_rep *onetime_init_regs; + + const unsigned int powerup_regs_items; + const struct crl_register_write_rep *powerup_regs; + + const unsigned int poweroff_regs_items; + const struct crl_register_write_rep *poweroff_regs; + + const unsigned int id_reg_items; + const struct crl_sensor_detect_config *id_regs; + + const unsigned int subdev_items; + const struct crl_sensor_subdev_config *subdevs; + + const struct crl_sensor_limits *sensor_limits; + + const unsigned int pll_config_items; + const struct crl_pll_configuration *pll_configs; + + const unsigned int modes_items; + const struct crl_mode_rep *modes; + /* + * Fail safe mode should be the largest resolution available in the + * mode list. If none of the mode parameters are matched, the driver + * will select this mode for streaming. + */ + const unsigned int fail_safe_mode_index; + + const unsigned int streamon_regs_items; + const struct crl_register_write_rep *streamon_regs; + + const unsigned int streamoff_regs_items; + const struct crl_register_write_rep *streamoff_regs; + + const unsigned int ctrl_items; + const struct crl_ctrl_data *ctrl_bank; + + const unsigned int csi_fmts_items; + const struct crl_csi_data_fmt *csi_fmts; + + const unsigned int flip_items; + const struct crl_flip_data *flip_data; + + struct crl_nvm crl_nvm_info; + + enum crl_addr_len addr_len; + + unsigned int frame_desc_entries; + enum crl_frame_desc_type frame_desc_type; + struct crl_frame_desc *frame_desc; + char *msr_file_name; + + sensor_specific_init sensor_init; + sensor_specific_cleanup sensor_cleanup; +}; + +struct crlmodule_sensors { + char *pname; + char *name; + struct crl_sensor_configuration *ds; +}; + +/* + * Function to populate the CRL data structure from the sensor configuration + * definition file + */ +int crlmodule_populate_ds(struct crl_sensor *sensor, struct device *dev); + +/* + * Function validate the contents CRL data structure to check if all the + * required fields are filled and are according to the limits. + */ +int crlmodule_validate_ds(struct crl_sensor *sensor); + +/* Function to free all resources allocated for the CRL data structure */ +void crlmodule_release_ds(struct crl_sensor *sensor); + +#endif /* __CRLMODULE_SENSOR_DS_H_ */ diff --git a/drivers/media/i2c/crlmodule-lite/crlmodule.h b/drivers/media/i2c/crlmodule-lite/crlmodule.h new file mode 100644 index 000000000000..f522409cb22e --- /dev/null +++ b/drivers/media/i2c/crlmodule-lite/crlmodule.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_PRIV_H_ +#define __CRLMODULE_PRIV_H_ + +#include +#include +#include +#include +#include "../../../../include/media/crlmodule-lite.h" +#include +#include "crlmodule-sensor-ds.h" + +#define CRL_SUBDEVS 3 + +#define CRL_PA_PAD_SRC 0 +#define CRL_PAD_SINK 0 +#define CRL_PAD_SRC 1 +#define CRL_PADS 2 + +struct crl_subdev { + struct ici_ext_subdev sd; + struct ici_rect sink_fmt; + struct ici_rect crop[2]; + struct ici_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct crl_sensor *sensor; + unsigned int field; +}; + +struct crl_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power mutex became necessity because of the v4l2_ctrl_handler_setup + * is being called from power on function which needs to be serialised + * but v4l2_ctrl_handler setup uses "mutex" so it cannot be used. + */ + struct mutex power_mutex; + + struct crl_subdev ssds[CRL_SUBDEVS]; + u32 ssds_used; + struct crl_subdev *src; + struct crl_subdev *binner; + struct crl_subdev *scaler; + struct crl_subdev *pixel_array; + + struct crlmodule_lite_platform_data *platform_data; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 sensor_mode; + u8 scale_m; + u8 fmt_index; + u8 flip_info; + u8 pll_index; + + + int power_count; + + bool streaming; + + struct crl_sensor_configuration *sensor_ds; + struct crl_ctrl_data *ctrl_bank; + + /* These are mandatory controls. So good to have reference to these */ + struct crl_ctrl_data *pixel_rate_pa; + struct crl_ctrl_data *link_freq; + struct crl_ctrl_data *pixel_rate_csi; + + s64 *link_freq_menu; + + /* If extra v4l2 contrl has an impact on PLL selection */ + bool ext_ctrl_impacts_pll_selection; + bool ext_ctrl_impacts_mode_selection; + bool blanking_ctrl_not_use; + bool direct_mode_in_use; + const struct crl_mode_rep *current_mode; + + struct clk *xclk; + struct crl_power_seq_entity *pwr_entity; + + u8 *nvm_data; + u16 nvm_size; + + /* Pointer to binary file which contains + * tunable IQ parameters like NR, DPC, BLC + * Not all MSR's are moved to the binary + * at the moment. + */ + const struct firmware *msr_list; + + struct ici_ext_subdev_register reg; +}; + +#define to_crlmodule_subdev(_sd) \ + container_of(_sd, struct crl_subdev, sd) + +#define to_crlmodule_sensor(_sd) \ + (to_crlmodule_subdev(_sd)->sensor) + +#endif /* __CRLMODULE_PRIV_H_ */ diff --git a/include/media/as3638.h b/include/media/as3638.h new file mode 100644 index 000000000000..8e847efaac9f --- /dev/null +++ b/include/media/as3638.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef __AS3638_H__ +#define __AS3638_H__ + +#define AS3638_NAME "as3638" +#define AS3638_I2C_ADDR 0x30 + +#define AS3638_FLASH_MAX_BRIGHTNESS_LED1 500 /* mA, IR LED */ +#define AS3638_TORCH_MAX_BRIGHTNESS_LED1 99 /* mA, IR LED */ +#define AS3638_FLASH_MAX_BRIGHTNESS_LED2 1200 /* mA, Ultra White LED */ +#define AS3638_TORCH_MAX_BRIGHTNESS_LED2 88 /* mA, Ultra White LED */ +#define AS3638_FLASH_MAX_BRIGHTNESS_LED3 1200 /* mA, Warm White LED */ +#define AS3638_TORCH_MAX_BRIGHTNESS_LED3 88 /* mA, Warm White LED */ + +enum as3638_led_id { + AS3638_LED1 = 0, + AS3638_LED2, + AS3638_LED3, + AS3638_LED_MAX, +}; +#define AS3638_NO_LED -1 + +struct as3638_platform_data { + int gpio_torch; + int gpio_strobe; + int gpio_reset; + u32 flash_max_brightness[AS3638_LED_MAX]; + u32 torch_max_brightness[AS3638_LED_MAX]; +}; + +#endif /* __AS3638_H__ */ diff --git a/include/media/crlmodule-lite.h b/include/media/crlmodule-lite.h new file mode 100644 index 000000000000..ce812e8d909d --- /dev/null +++ b/include/media/crlmodule-lite.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __CRLMODULE_LITE_H +#define __CRLMODULE_LITE_H + +#define CRLMODULE_LITE_NAME "crlmodule-lite" + +struct crlmodule_lite_platform_data { + unsigned short i2c_addr; + unsigned short i2c_adapter; + + unsigned int ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + const s64 *op_sys_clock; + + int xshutdown; /* gpio */ + char module_name[16]; /* module name from ACPI */ + char suffix; /* suffix to identify multi sensors, abcd.. */ +}; + +#endif /* __CRLMODULE_LITE_H */ diff --git a/include/media/dw9714.h b/include/media/dw9714.h new file mode 100755 index 000000000000..11737c6ca824 --- /dev/null +++ b/include/media/dw9714.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2015 - 2018 Intel Corporation + * + * Based on ATOMISP dw9714 implementation by + * Huang Shenbo + +#define DW9714_NAME "dw9714" + +#define DW9714_MAX_FOCUS_POS 1023 +#define DW9714_CTRL_STEPS 16 /* Keep this value power of 2 */ +#define DW9714_CTRL_DELAY_US 1000 + +#define VCM_DEFAULT_S 0x0 +#define VCM_VAL(data, s) (u16)((data) << 4 | (s)) + +struct dw9714_platform_data { + struct device *sensor_dev; + int gpio_xsd; /* Should be < 0 if not used */ +}; + +#ifdef CONFIG_INTEL_IPU4_OV13858 +extern bool vcm_in_use; +extern void crlmodule_vcm_gpio_set_value(unsigned int gpio, int value); +#endif +#endif diff --git a/include/media/lc898122.h b/include/media/lc898122.h new file mode 100644 index 000000000000..5c426407b5ed --- /dev/null +++ b/include/media/lc898122.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef __LC898122_HEADER__ +#define __LC898122_HEADER__ + +#define LC898122_VCM_ADDR 0x24 +#define LC898122_NAME "lc898122" + +struct lc898122_platform_data { + struct device *sensor_device; +}; + +#endif /* __LC898122_HEADER__ */ diff --git a/include/media/lm3643.h b/include/media/lm3643.h new file mode 100644 index 000000000000..58acf6f9662a --- /dev/null +++ b/include/media/lm3643.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2013 - 2018 Intel Corporation */ + +#ifndef __LM3643_H__ +#define __LM3643_H__ + + +#define LM3643_NAME "lm3643" +#define LM3643_I2C_ADDR_REVA (0x67) +#define LM3643_I2C_ADDR (0x63) + +#define LM3643_FLASH_TOUT_DEF 0xa /*150ms*/ + +#define LM3643_FLASH_BRT_MIN 10900 +#define LM3643_FLASH_BRT_STEP 11725 +#define LM3643_FLASH_BRT_MAX 1500000 +#define LM3643_FLASH_BRT_mA_TO_REG(a) \ + (((a) * 1000) < LM3643_FLASH_BRT_MIN ? LM3643_FLASH_BRT_MIN : \ + ((((a) * 1000) - LM3643_FLASH_BRT_MIN) / LM3643_FLASH_BRT_STEP)) + +#define LM3643_FLASH_BRT_REG_TO_mA(a) \ + (((a) * LM3643_FLASH_BRT_STEP + LM3643_FLASH_BRT_MIN) / 1000) + +#define LM3643_FLASH_DEFAULT_BRIGHTNESS 729000 + +#define LM3643_TORCH_BRT_MIN 977 +#define LM3643_TORCH_BRT_STEP 1400 +#define LM3643_TORCH_BRT_MAX 179000 +#define LM3643_TORCH_BRT_mA_TO_REG(a) \ + (((a) * 1000) < LM3643_TORCH_BRT_MIN ? LM3643_TORCH_BRT_MIN : \ + ((((a) * 1000) - LM3643_TORCH_BRT_MIN) / LM3643_TORCH_BRT_STEP)) + +#define LM3643_TORCH_DEFAULT_BRIGHTNESS 89 + + +#define LM3643_FLASH_TOUT_MIN 0 +#define LM3643_FLASH_TOUT_STEP 1 +#define LM3643_FLASH_TOUT_MAX 15 + +/* struct lm3643_platform_data + */ +struct lm3643_platform_data { + int gpio_torch; + int gpio_strobe; + int gpio_reset; + int flash_max_brightness; /*mA*/ + int torch_max_brightness; /*mA*/ +}; + +#endif /* __LM3643_H__ */ From e855f135b6594c34d286982771b68c8dff0fcc3e Mon Sep 17 00:00:00 2001 From: "Yew, Chang Ching" Date: Tue, 23 Oct 2018 13:25:38 +0800 Subject: [PATCH 1149/1276] media: platform: intel: platform driver to be used by ICI and crlmodule-lite Platform driver which ICI and crlmodule-lite use to retrieve platform-specific data. Squash of below patches from https://github.com/intel/linux-intel-lts/tree/4.14/base/drivers/media/platform/intel/ b61d75 media: intel-ipu4: bring back platform ACPI support b66238 media: platform: Platform data for ICI driver and crlmodule-lite Change-Id: I105972785e3532cfa71a1d8a243a1d7cc4bec886 Signed-off-by: Yew, Chang Ching --- drivers/media/platform/intel/Kconfig | 13 + drivers/media/platform/intel/Makefile | 2 + drivers/media/platform/intel/ipu4-acpi.c | 1068 +++++++++++++++++ .../platform/intel/ipu4-ici-bxt-p-pdata.c | 201 ++++ include/media/ipu4-acpi.h | 27 + 5 files changed, 1311 insertions(+) create mode 100644 drivers/media/platform/intel/ipu4-acpi.c create mode 100644 drivers/media/platform/intel/ipu4-ici-bxt-p-pdata.c create mode 100644 include/media/ipu4-acpi.h diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig index 7dc176af66f2..31e117e225a7 100644 --- a/drivers/media/platform/intel/Kconfig +++ b/drivers/media/platform/intel/Kconfig @@ -93,3 +93,16 @@ config INTEL_IPU4_OX03A10 depends on INTEL_IPU4_BXT_P_PDATA ---help--- "ox03a10 camera sensor" + +config INTEL_IPU4_ICI_BXT_P_PDATA + depends on VIDEO_INTEL_IPU && VIDEO_INTEL_ICI + bool "Enable built in platform data for Broxton-P ICI driver" + ---help--- + Pre-ACPI system platform data is compiled inside kernel + +config INTEL_IPU4_ADV7481_I2C_ID + int "I2C bus ID for ADV7481" + range 0 8 + default 0 + ---help--- + I2C bus number of ADV7481 Mondello diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile index b6d4f786f20b..cf4cd3bfaa9b 100644 --- a/drivers/media/platform/intel/Makefile +++ b/drivers/media/platform/intel/Makefile @@ -19,3 +19,5 @@ ccflags-y += -I$(srcpath)/$(src)/../../pci/intel/ obj-$(CONFIG_INTEL_IPU4_BXT_P_PDATA) += ipu4-bxt-p-pdata.o obj-$(CONFIG_INTEL_IPU4_BXT_GP_PDATA) += ipu4-bxt-gp-pdata.o obj-$(CONFIG_INTEL_IPU4P_ICL_RVP_PDATA) += ipu4p-icl-rvp-pdata.o +obj-$(CONFIG_INTEL_IPU4_ICI_BXT_P_PDATA) += ipu4-ici-bxt-p-pdata.o +obj-$(CONFIG_VIDEO_INTEL_IPU) += ipu4-acpi.o diff --git a/drivers/media/platform/intel/ipu4-acpi.c b/drivers/media/platform/intel/ipu4-acpi.c new file mode 100644 index 000000000000..94f53727ddbf --- /dev/null +++ b/drivers/media/platform/intel/ipu4-acpi.c @@ -0,0 +1,1068 @@ +/* + * Copyright (c) 2016--2018 Intel Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) +#include +#else +#include +#endif +#include +#include + +#define HID_BUFFER_SIZE 32 +#define VCM_BUFFER_SIZE 32 + +/* Data representation as it is in ACPI SSDB buffer */ +struct sensor_bios_data_packed { + u8 version; + u8 sku; + u8 guid_csi2[16]; + u8 devfunction; + u8 bus; + u32 dphylinkenfuses; + u32 clockdiv; + u8 link; + u8 lanes; + u32 csiparams[10]; + u32 maxlanespeed; + u8 sensorcalibfileidx; + u8 sensorcalibfileidxInMBZ[3]; + u8 romtype; + u8 vcmtype; + u8 platforminfo; + u8 platformsubinfo; + u8 flash; + u8 privacyled; + u8 degree; + u8 mipilinkdefined; + u32 mclkspeed; + u8 controllogicid; + u8 reserved1[3]; + u8 mclkport; + u8 reserved2[13]; +} __attribute__((__packed__)); + +/* Fields needed by ipu4 driver */ +struct sensor_bios_data { + struct device *dev; + u8 link; + u8 lanes; + u8 vcmtype; + u8 flash; + u8 degree; + u8 mclkport; + u32 mclkspeed; + u16 xshutdown; +}; + +static LIST_HEAD(devices); +static LIST_HEAD(new_devs); + +struct ipu4_i2c_helper { + int (*fn)(struct device *, void *, + struct ipu_isys_csi2_config *csi2, + bool reprobe); + void *driver_data; +}; + +struct ipu4_i2c_new_dev { + struct list_head list; + struct i2c_board_info info; + unsigned short int bus; +}; + +struct ipu4_camera_module_data { + struct list_head list; + struct ipu_isys_csi2_config csi2; + unsigned int ext_clk; + void *pdata; /* Ptr to generated platform data*/ + void *priv; /* Private for specific subdevice */ +}; + +struct ipu4_i2c_info { + unsigned short bus; + unsigned short addr; +}; + +struct ipu4_acpi_devices { + const char *hid_name; + const char *real_driver; + int (*get_platform_data)(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size); + void *priv_data; + size_t priv_size; + const struct ipu_regulator *regulators; +}; + +static uint64_t imx132_op_clocks[] = (uint64_t []){ 312000000, 0 }; + +/* + * Add a request to create new i2c devices later on. i2c_new_device can't be + * directly called from functions which are called by i2c_for_each_dev + * function. Both takes a same mutex inside i2c core code. + */ +static int add_new_i2c(unsigned short addr, unsigned short bus, + unsigned short flags, char *name, void *pdata) +{ + struct ipu4_i2c_new_dev *newdev = kzalloc(sizeof(*newdev), GFP_KERNEL); + + if (!newdev) + return -ENOMEM; + + newdev->info.flags = flags; + newdev->info.addr = addr; + newdev->bus = bus; + newdev->info.platform_data = pdata; + strlcpy(newdev->info.type, name, sizeof(newdev->info.type)); + + list_add(&newdev->list, &new_devs); + return 0; +} + +static int get_string_dsdt_data(struct device *dev, const u8 *dsdt, + int func, char *out, unsigned int size) +{ + struct acpi_handle *dev_handle = ACPI_HANDLE(dev); + union acpi_object *obj; + int ret = -ENODEV; + + obj = acpi_evaluate_dsm(dev_handle, (void *)dsdt, 0, func, NULL); + if (!obj) { + dev_err(dev, "No dsdt field\n"); + return -ENODEV; + } + dev_dbg(dev, "ACPI type %d", obj->type); + + if ((obj->type != ACPI_TYPE_STRING) || !obj->string.pointer) + goto exit; + + strlcpy(out, obj->string.pointer, + min((unsigned int)(obj->string.length + 1), size)); + dev_info(dev, "DSDT string id: %s\n", out); + + ret = 0; +exit: + ACPI_FREE(obj); + return ret; +} + +static int get_integer_dsdt_data(struct device *dev, const u8 *dsdt, + int func, u64 *out) +{ + struct acpi_handle *dev_handle = ACPI_HANDLE(dev); + union acpi_object *obj; + + obj = acpi_evaluate_dsm(dev_handle, (void *)dsdt, 0, func, NULL); + if (!obj) { + dev_err(dev, "No dsdt\n"); + return -ENODEV; + } + dev_dbg(dev, "ACPI type %d", obj->type); + + if (obj->type != ACPI_TYPE_INTEGER) { + ACPI_FREE(obj); + return -ENODEV; + } + *out = obj->integer.value; + ACPI_FREE(obj); + return 0; +} + +static int read_acpi_block(struct device *dev, char *id, void *data, u32 size) +{ + union acpi_object *obj; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_handle *dev_handle = ACPI_HANDLE(dev); + int status; + u32 buffer_length; + + status = acpi_evaluate_object(dev_handle, id, NULL, &buffer); + if (!ACPI_SUCCESS(status)) + return -ENODEV; + + obj = (union acpi_object *)buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_BUFFER) { + dev_err(dev, "Could't read acpi buffer\n"); + status = -ENODEV; + goto err; + } + + if (obj->buffer.length > size) { + dev_err(dev, "Given buffer is too small\n"); + status = -ENODEV; + goto err; + } + + memcpy(data, obj->buffer.pointer, min(size, obj->buffer.length)); + buffer_length = obj->buffer.length; + kfree(buffer.pointer); + + return buffer_length; +err: + kfree(buffer.pointer); + return status; +} + +static struct ipu4_camera_module_data *add_device_to_list( + struct list_head *devices) +{ + struct ipu4_camera_module_data *cam_device; + + cam_device = kzalloc(sizeof(*cam_device), GFP_KERNEL); + if (!cam_device) + return NULL; + + list_add(&cam_device->list, devices); + return cam_device; +} + +static int get_sensor_gpio(struct device *dev, int index) +{ + struct gpio_desc *gpiod_gpio; + int gpio; + + gpiod_gpio = gpiod_get_index(dev, NULL, index, GPIOD_ASIS); + if (IS_ERR(gpiod_gpio)) { + dev_err(dev, "No gpio from index %d\n", index); + return -ENODEV; + } + gpio = desc_to_gpio(gpiod_gpio); + gpiod_put(gpiod_gpio); + return gpio; +} + +static void *get_dsdt_vcm(struct device *dev, char *vcm, char *second) +{ + void *pdata = NULL; + const u8 dsdt_cam_vcm[] = { + 0x39, 0xA6, 0xC9, 0x75, 0x8A, 0x5C, 0x00, 0x4A, + 0x9F, 0x48, 0xA9, 0xC3, 0xB5, 0xDA, 0x78, 0x9F }; + int ret = get_string_dsdt_data(dev, dsdt_cam_vcm, 0, + vcm, VCM_BUFFER_SIZE); + if (ret < 0) { + dev_err(dev, "get vcm failed - using override: %s\n", second); + strlcpy(vcm, second, VCM_BUFFER_SIZE); + } + dev_dbg(dev, "vcm: %s\n", vcm); + + if (!strcasecmp(vcm, LC898122_NAME)) { + struct lc898122_platform_data *lc_pdata; + + dev_dbg(dev, "Setting up voice coil motor lc898821"); + lc_pdata = kzalloc(sizeof(struct lc898122_platform_data), + GFP_KERNEL); + if (lc_pdata) + lc_pdata->sensor_device = dev; + pdata = lc_pdata; + strlcpy(vcm, LC898122_NAME, VCM_BUFFER_SIZE); + } else if (!strcasecmp(vcm, DW9714_NAME)) { + struct dw9714_platform_data *dw_pdata; + + dev_dbg(dev, "Setting up voice coil motor dw9714"); + dw_pdata = kzalloc(sizeof(struct dw9714_platform_data), + GFP_KERNEL); + if (dw_pdata) { + dw_pdata->sensor_dev = dev; + if (gpiod_count(dev, NULL) > 1) + dw_pdata->gpio_xsd = get_sensor_gpio(dev, 1); + else + dw_pdata->gpio_xsd = -ENODEV; + } + pdata = dw_pdata; + strlcpy(vcm, DW9714_NAME, VCM_BUFFER_SIZE); + } + return pdata; +} + +static int get_i2c_info(struct device *dev, struct ipu4_i2c_info *i2c, int size) +{ + const u8 dsdt_cam_i2c[] = { + 0x49, 0x75, 0x25, 0x26, 0x71, 0x92, 0xA4, 0x4C, + 0xBB, 0x43, 0xC4, 0x89, 0x9D, 0x5A, 0x48, 0x81}; + u64 num_i2c; + int i; + int rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, 1, &num_i2c); + + if (rval < 0) { + dev_err(dev, "Failed to get number of I2C devices\n"); + return -ENODEV; + } + + for (i = 0; i < num_i2c && i < size; i++) { + u64 data; + + rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, i + 2, + &data); + if (rval < 0) { + dev_err(dev, "No i2c data\n"); + return -ENODEV; + } + + i2c[i].bus = ((data >> 24) & 0xff) - 1; + i2c[i].addr = (data >> 8) & 0xff; + } + return num_i2c; +} + +static int match_depend(struct device *dev, void *data) +{ + return (dev && dev->fwnode == data) ? 1 : 0; +} + +#define MAX_CONSUMERS 1 +struct ipu4_gpio_regulator { + struct regulator_consumer_supply consumers[MAX_CONSUMERS]; + struct regulator_init_data init_data; + struct gpio_regulator_config info; + struct platform_device pdev; + struct list_head list; +}; +static LIST_HEAD(ipu4_gpio_regulator_head); + +static int create_gpio_regulator(struct device *dev, int index, const char *name) +{ + struct ipu4_gpio_regulator *reg_device; + struct platform_device *cam_regs[1]; + int gpio; + int num_consumers = 0; + + gpio = get_sensor_gpio(dev, 1); + if (gpio < 0) + return gpio; + + reg_device = kzalloc(sizeof(*reg_device), GFP_KERNEL); + if (!reg_device) + return -ENOMEM; + + INIT_LIST_HEAD(®_device->list); + reg_device->consumers[num_consumers].supply = "VANA"; + reg_device->consumers[num_consumers].dev_name = name; + num_consumers++; + + reg_device->init_data.constraints.input_uV = 3300000; + reg_device->init_data.constraints.min_uV = 2800000; + reg_device->init_data.constraints.max_uV = 2800000; + reg_device->init_data.constraints.valid_ops_mask = + REGULATOR_CHANGE_STATUS; + reg_device->init_data.num_consumer_supplies = num_consumers; + reg_device->init_data.consumer_supplies = reg_device->consumers; + + reg_device->info.supply_name = dev_name(dev); + reg_device->info.enable_gpio = gpio; + reg_device->info.enable_high = 1; + reg_device->info.enabled_at_boot = 1; + reg_device->info.type = REGULATOR_VOLTAGE; + reg_device->info.init_data = ®_device->init_data; + reg_device->pdev.name = "gpio-regulator"; + reg_device->pdev.id = -1; + reg_device->pdev.dev.platform_data = ®_device->info; + cam_regs[0] = ®_device->pdev; + + platform_add_devices(cam_regs, 1); + list_add_tail(®_device->list, &ipu4_gpio_regulator_head); + + return 0; +} + +static int remove_gpio_regulator(void) +{ + struct ipu4_gpio_regulator *reg_device; + + while (!list_empty(&ipu4_gpio_regulator_head)) { + reg_device = list_first_entry(&ipu4_gpio_regulator_head, + struct ipu4_gpio_regulator, list); + list_del(®_device->list); + + platform_device_unregister(®_device->pdev); + kfree(reg_device); + } + + return 0; +} + +static int get_acpi_dep_data(struct device *dev, + struct sensor_bios_data *sensor) +{ + struct acpi_handle *dev_handle = ACPI_HANDLE(dev); + struct acpi_handle_list dep_devices; + acpi_status status; + int i; + + if (!acpi_has_method(dev_handle, "_DEP")) + return 0; + + status = acpi_evaluate_reference(dev_handle, "_DEP", NULL, + &dep_devices); + if (ACPI_FAILURE(status)) { + dev_dbg(dev, "Failed to evaluate _DEP.\n"); + return -ENODEV; + } + + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device *device; + struct acpi_device_info *info; + struct device *p_dev; + int match; + + status = acpi_get_object_info(dep_devices.handles[i], &info); + if (ACPI_FAILURE(status)) { + dev_dbg(dev, "Error reading _DEP device info\n"); + continue; + } + + match = info->valid & ACPI_VALID_HID && + !strcmp(info->hardware_id.string, "INT3472"); + + kfree(info); + + if (!match) + continue; + + /* Process device IN3472 created by acpi */ + if (acpi_bus_get_device(dep_devices.handles[i], &device)) + return -ENODEV; + + dev_dbg(dev, "Depend ACPI device found: %s\n", + dev_name(&device->dev)); + + p_dev = bus_find_device(&platform_bus_type, NULL, + &device->fwnode, match_depend); + if (p_dev) { + dev_dbg(dev, "Dependent platform device found %s\n", + dev_name(p_dev)); + sensor->dev = p_dev; + /* GPIO in index 1 is fixed regulator */ + create_gpio_regulator(p_dev, 1, dev_name(dev)); + } + } + return 0; +} + +static int get_acpi_ssdb_sensor_data(struct device *dev, + struct sensor_bios_data *sensor) +{ + struct sensor_bios_data_packed sensor_data; + int ret = read_acpi_block(dev, "SSDB", &sensor_data, + sizeof(sensor_data)); + if (ret < 0) + return ret; + + get_acpi_dep_data(dev, sensor); + + /* Xshutdown is not part of the ssdb data */ + sensor->link = sensor_data.link; + sensor->lanes = sensor_data.lanes; + sensor->mclkport = sensor_data.mclkport; + sensor->flash = sensor_data.flash; + sensor->mclkspeed = sensor_data.mclkspeed; + dev_dbg(dev, "sensor acpi data: link %d, lanes %d, mclk %d, flash %d, mclkspeed %d\n", + sensor->link, sensor->lanes, sensor->mclkport, sensor->flash, sensor->mclkspeed); + return 0; +} + +static int ipu_acpi_get_sensor_data(struct device *dev, + struct ipu4_camera_module_data *data, + struct sensor_bios_data *sensor) +{ + const u8 mipi_port_dsdt[] = { + 0xD8, 0x7B, 0x3B, 0xEA, 0x9B, 0xE0, 0x39, 0x42, + 0xAD, 0x6E, 0xED, 0x52, 0x5F, 0x3F, 0x26, 0xAB }; + const u8 mclk_out_dsdt[] = { + 0x51, 0x26, 0xBE, 0x8D, 0xC1, 0x70, 0x6F, 0x4C, + 0xAC, 0x87, 0xA3, 0x7C, 0xB4, 0x6E, 0x4A, 0xF6 }; + + int rval; + u64 acpi_data; + + if (sensor) { + /* Sensor data from ssdb block */ + data->csi2.port = sensor->link; + data->csi2.nlanes = sensor->lanes; + acpi_data = sensor->mclkport; + data->ext_clk = sensor->mclkspeed; + } else { + rval = get_integer_dsdt_data(dev, mipi_port_dsdt, 0, + &acpi_data); + if (rval < 0) { + dev_err(dev, "Can't get mipi port\n"); + return rval; + } + data->csi2.port = acpi_data & 0xf; + data->csi2.nlanes = (acpi_data & 0xf0) >> 4; + + rval = get_integer_dsdt_data(dev, mclk_out_dsdt, 0, &acpi_data); + if (rval < 0) { + dev_err(dev, "Can't get mclk info\n"); + return rval; + } + /* we have 24 MHz clock for sensors now */ + data->ext_clk = 286363636; + } + + /* dsdt data currently contains wrong numbers for combo ports */ + if (data->csi2.port >= 6) + data->csi2.port -= 2; + + if (data->csi2.nlanes == 0) + return -ENODEV; + + switch (acpi_data) { + case 0: + clk_add_alias(NULL, dev_name(dev), "ipu4_cam_clk0", NULL); + break; + case 1: + clk_add_alias(NULL, dev_name(dev), "ipu4_cam_clk1", NULL); + break; + case 2: + clk_add_alias(NULL, dev_name(dev), "ipu4_cam_clk2", NULL); + break; + default: + dev_err(dev, "Unknown clk data %u\n", (unsigned int)acpi_data); + break; + } + + dev_dbg(dev, "sensor: lanes %d, port %d, clk out %d, ext_clk %d\n", + data->csi2.nlanes, + data->csi2.port, (int)acpi_data, data->ext_clk); + return 0; +} + +static int get_custom_gpios(struct device *dev, + struct crlmodule_platform_data *pdata) +{ + int i, ret, c = gpiod_count(dev, NULL) - 1; + + for (i = 0; i < c; i++) { + ret = snprintf(pdata->custom_gpio[i].name, + sizeof(pdata->custom_gpio[i].name), + "custom_gpio%d", i); + if (ret < 0 || ret >= sizeof(pdata->custom_gpio[i].name)) { + dev_err(dev, "Failed to set custom gpio name\n"); + return -EINVAL; + } + /* First GPIO is xshutdown */ + pdata->custom_gpio[i].number = get_sensor_gpio(dev, i + 1); + if (pdata->custom_gpio[i].number < 0) { + dev_err(dev, "unable to get custom gpio number\n"); + return -ENODEV; + } + pdata->custom_gpio[i].val = 1; + pdata->custom_gpio[i].undo_val = 0; + } + + return 0; +} + +static int get_crlmodule_pdata(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size) +{ + struct sensor_bios_data sensor; + struct crlmodule_platform_data *pdata; + struct ipu4_i2c_info i2c[2]; + void *vcm_pdata; + char vcm[VCM_BUFFER_SIZE]; + int num = get_i2c_info(&client->dev, i2c, ARRAY_SIZE(i2c)); + int rval; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + sensor.dev = &client->dev; + + rval = get_acpi_ssdb_sensor_data(&client->dev, &sensor); + + ipu_acpi_get_sensor_data(&client->dev, data, + rval == 0 ? &sensor : NULL); + + data->pdata = pdata; + /* sensor.dev may here point to sensor or dependent device */ + pdata->xshutdown = get_sensor_gpio(sensor.dev, 0); + if (pdata->xshutdown < 0) { + rval = pdata->xshutdown; + goto err_free_pdata; + } + + rval = get_custom_gpios(sensor.dev, pdata); + if (rval) + goto err_free_pdata; + + pdata->lanes = data->csi2.nlanes; + pdata->ext_clk = data->ext_clk; + client->dev.platform_data = pdata; + + helper->fn(&client->dev, helper->driver_data, &data->csi2, true); + + if ((num <= 1) || !priv) + return 0; + + vcm_pdata = get_dsdt_vcm(&client->dev, vcm, priv); + + dev_info(&client->dev, "Creating vcm instance: bus: %d addr 0x%x %s\n", + i2c[1].bus, i2c[1].addr, vcm); + + return add_new_i2c(i2c[1].addr, i2c[1].bus, 0, vcm, vcm_pdata); + +err_free_pdata: + kfree(pdata); + data->pdata = NULL; + return rval; +} + +#if defined (CONFIG_VIDEO_INTEL_ICI) +static int get_crlmodule_lite_pdata(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size) +{ + struct sensor_bios_data sensor; + struct crlmodule_lite_platform_data *pdata; + struct ipu4_i2c_info i2c[2]; + void *vcm_pdata; + char vcm[VCM_BUFFER_SIZE]; + int num = get_i2c_info(&client->dev, i2c, ARRAY_SIZE(i2c)); + int rval; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + sensor.dev = &client->dev; + + rval = get_acpi_ssdb_sensor_data(&client->dev, &sensor); + + ipu_acpi_get_sensor_data(&client->dev, data, + rval == 0 ? &sensor : NULL); + + data->pdata = pdata; + /* sensor.dev may here point to sensor or dependent device */ +#if !defined(CONFIG_VIDEO_INTEL_UOS) + pdata->xshutdown = get_sensor_gpio(sensor.dev, 0); + if (pdata->xshutdown < 0) { + rval = pdata->xshutdown; + kfree(pdata); + data->pdata = NULL; + return rval; + } +#endif + pdata->lanes = data->csi2.nlanes; + pdata->ext_clk = data->ext_clk; + client->dev.platform_data = pdata; + + helper->fn(&client->dev, helper->driver_data, &data->csi2, true); + + if ((num <= 1) || !priv) + return 0; + + vcm_pdata = get_dsdt_vcm(&client->dev, vcm, priv); + + dev_info(&client->dev, "Creating vcm instance: bus: %d addr 0x%x %s\n", + i2c[1].bus, i2c[1].addr, vcm); + + return add_new_i2c(i2c[1].addr, i2c[1].bus, 0, vcm, vcm_pdata); +} +#endif + +static int get_smiapp_pdata(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + struct smiapp_platform_data *pdata; +#else + struct smiapp_hwconfig *pdata; +#endif + uint64_t *source = priv; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + data->pdata = pdata; + + data->priv = source; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + pdata->xshutdown = get_sensor_gpio(&client->dev, 0); + if (pdata->xshutdown < 0) + return -ENODEV; +#endif + + ipu_acpi_get_sensor_data(&client->dev, data, NULL); + + pdata->op_sys_clock = source; + pdata->lanes = data->csi2.nlanes; + pdata->ext_clk = data->ext_clk; + + client->dev.platform_data = pdata; + helper->fn(&client->dev, helper->driver_data, &data->csi2, true); + + return 0; +} + +static int get_lm3643_pdata(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size) +{ + struct lm3643_platform_data *pdata; + struct ipu4_i2c_info i2c[2]; + struct gpio_desc *gpiod_reset; + int i; + int num = get_i2c_info(&client->dev, i2c, ARRAY_SIZE(i2c)); + + if (num < 0) + return -ENODEV; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + gpiod_reset = gpiod_get_index(&client->dev, NULL, 0, GPIOD_ASIS); + if (IS_ERR(gpiod_reset)) { + pdata->gpio_reset = -1; + dev_info(&client->dev, "No reset for lm3643\n"); + } else { + pdata->gpio_reset = desc_to_gpio(gpiod_reset); + gpiod_put(gpiod_reset); + } + + /* These should be added to ACPI */ + data->pdata = pdata; + pdata->gpio_torch = -1; + pdata->gpio_strobe = -1; + pdata->flash_max_brightness = 500; + pdata->torch_max_brightness = 89; + + client->dev.platform_data = pdata; + helper->fn(&client->dev, helper->driver_data, NULL, true); + + /* + * Same I2C ACPI entry may contain several instances. I2C core + * ACPI code creates only the first one. Create rest of the instances + */ + dev_info(&client->dev, "Adding rest of lm3643 instances: %d\n", num); + for (i = 1; i < num; i++) { + int rval = add_new_i2c(i2c[i].addr, i2c[i].bus, + 0, client->name, pdata); + if (rval < 0) + return rval; + dev_info(&client->dev, "LM3643 instance: bus: %d addr 0x%x\n", + i2c[i].bus, i2c[i].addr); + return -ENOMEM; + } + + return 0; +}; + +static int get_as3638_pdata(struct i2c_client *client, + struct ipu4_camera_module_data *data, + struct ipu4_i2c_helper *helper, + void *priv, size_t size) +{ + struct as3638_platform_data *pdata; + struct gpio_desc *gpiod_pin; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + gpiod_pin = gpiod_get_index(&client->dev, NULL, 0, GPIOD_ASIS); + if (IS_ERR(gpiod_pin)) { + pdata->gpio_reset = -1; + dev_info(&client->dev, "No reset gpio for as3638\n"); + } else { + pdata->gpio_reset = desc_to_gpio(gpiod_pin); + gpiod_put(gpiod_pin); + } + + gpiod_pin = gpiod_get_index(&client->dev, NULL, 1, GPIOD_ASIS); + if (IS_ERR(gpiod_pin)) { + pdata->gpio_torch = -1; + dev_info(&client->dev, "No torch gpio for as3638\n"); + } else { + pdata->gpio_torch = desc_to_gpio(gpiod_pin); + gpiod_put(gpiod_pin); + } + + gpiod_pin = gpiod_get_index(&client->dev, NULL, 2, GPIOD_ASIS); + if (IS_ERR(gpiod_pin)) { + pdata->gpio_strobe = -1; + dev_info(&client->dev, "No strobe gpio for as3638\n"); + } else { + pdata->gpio_strobe = desc_to_gpio(gpiod_pin); + gpiod_put(gpiod_pin); + } + + /* These should be added to ACPI */ + data->pdata = pdata; + pdata->flash_max_brightness[AS3638_LED1] = + AS3638_FLASH_MAX_BRIGHTNESS_LED1; + pdata->torch_max_brightness[AS3638_LED1] = + AS3638_TORCH_MAX_BRIGHTNESS_LED1; + pdata->flash_max_brightness[AS3638_LED2] = + AS3638_FLASH_MAX_BRIGHTNESS_LED2; + pdata->torch_max_brightness[AS3638_LED2] = + AS3638_TORCH_MAX_BRIGHTNESS_LED2; + pdata->flash_max_brightness[AS3638_LED3] = + AS3638_FLASH_MAX_BRIGHTNESS_LED3; + pdata->torch_max_brightness[AS3638_LED3] = + AS3638_TORCH_MAX_BRIGHTNESS_LED3; + + client->dev.platform_data = pdata; + helper->fn(&client->dev, helper->driver_data, NULL, true); + + return 0; +}; + +static const struct ipu4_acpi_devices supported_devices[] = { + { "SONY230A", CRLMODULE_NAME, get_crlmodule_pdata, LC898122_NAME, 0, + imx230regulators }, + { "INT3477", CRLMODULE_NAME, get_crlmodule_pdata, NULL, 0, + ov8858regulators }, + { "INT3471", CRLMODULE_NAME, get_crlmodule_pdata, NULL, 0 }, + { "OV5670AA", CRLMODULE_NAME, get_crlmodule_pdata, NULL, 0 }, + { "SONY214A", CRLMODULE_NAME, get_crlmodule_pdata, "dw9714", 0 }, + { "SONY132A", SMIAPP_NAME, get_smiapp_pdata, imx132_op_clocks, + sizeof(imx132_op_clocks) }, + { "TXNW3643", LM3643_NAME, get_lm3643_pdata, NULL, 0 }, + { "AMS3638", AS3638_NAME, get_as3638_pdata, NULL, 0 }, +#if defined (CONFIG_VIDEO_INTEL_ICI) + { "ADV7481A", CRLMODULE_LITE_NAME, get_crlmodule_lite_pdata, NULL, 0 }, + { "ADV7481B", CRLMODULE_LITE_NAME, get_crlmodule_lite_pdata, NULL, 0 }, +#else + { "ADV7481A", CRLMODULE_NAME, get_crlmodule_pdata, NULL, 0 }, + { "ADV7481B", CRLMODULE_NAME, get_crlmodule_pdata, NULL, 0 }, +#endif +}; + +static int get_table_index(struct device *device, const __u8 *acpi_name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { + if (!strcmp(acpi_name, supported_devices[i].hid_name)) + return i; + } + + return -ENODEV; +} + +/* List of ACPI devices what we can handle */ +static const struct acpi_device_id ipu4_acpi_match[] = { + { "SONY230A", 0 }, + { "INT3477", 0 }, + { "INT3471", 0 }, + { "TXNW3643", 0 }, + { "AMS3638", 0 }, + { "SONY214A", 0 }, + { "SONY132A", 0 }, + { "OV5670AA", 0 }, + { "ADV7481A", 0 }, + { "ADV7481B", 0 }, + {}, +}; + +static int map_power_rails(char *src_dev_name, char *src_regulator, + struct device *dev, char *dest_rail) +{ + struct device *src_dev; + int rval; + + if (!src_dev_name) { + dev_dbg(dev, "Regulator device name missing"); + return -ENODEV; + } + + src_dev = bus_find_device_by_name(&platform_bus_type, NULL, + src_dev_name); + if (!src_dev) { + dev_dbg(dev, "Regulator device device not found"); + return -ENODEV; + } + + rval = regulator_register_supply_alias(dev, dest_rail, src_dev, + src_regulator); + if (rval < 0) { + dev_err(dev, "Regulator alias mapping fails %s, %s <-> %s, %s", + dev_name(src_dev), src_regulator, + dev_name(dev), dest_rail); + return -ENODEV; + } + return 0; +} + +static int ipu_acpi_pdata(struct i2c_client *client, + const struct acpi_device_id *acpi_id, + struct ipu4_i2c_helper *helper) +{ + struct ipu4_camera_module_data *camdata; + const struct ipu_regulator *regulators; + int index = get_table_index(&client->dev, acpi_id->id); + + if (index < 0) { + dev_err(&client->dev, + "Device is not in supported devices list\n"); + return -ENODEV; + } + + camdata = add_device_to_list(&devices); + if (!camdata) + return -ENOMEM; + + strlcpy(client->name, supported_devices[index].real_driver, + sizeof(client->name)); + + regulators = supported_devices[index].regulators; + while (regulators && regulators->src_dev_name) { + map_power_rails(regulators->src_dev_name, + regulators->src_rail, + &client->dev, + regulators->dest_rail); + regulators++; + } + + supported_devices[index].get_platform_data( + client, camdata, helper, + supported_devices[index].priv_data, + supported_devices[index].priv_size); + + return 0; +} + +static int ipu4_i2c_test(struct device *dev, void *priv) +{ + struct i2c_client *client = i2c_verify_client(dev); + const struct acpi_device_id *acpi_id; + + /* + * Check that we are handling only I2C devices which really has + * ACPI data and are one of the devices which we want to handle + */ + if (!ACPI_COMPANION(dev) || !client) + return 0; + + acpi_id = acpi_match_device(ipu4_acpi_match, dev); + if (!acpi_id) + return 0; + + /* + * Skip if platform data has already been added. + * Probably ACPI data overruled by kernel platform data + */ + if (client->dev.platform_data) { + dev_info(dev, "ACPI device has already platform data\n"); + return 0; + } + + /* Looks that we got what we are looking for */ + if (ipu_acpi_pdata(client, acpi_id, priv)) + dev_err(dev, "Failed to process ACPI data"); + + /* Don't return error since we want to process remaining devices */ + return 0; +} + +/* Scan all i2c devices and pick ones which we can handle */ +int ipu_get_acpi_devices(void *driver_data, + struct device *dev, + int (*fn) + (struct device *, void *, + struct ipu_isys_csi2_config *csi2, + bool reprobe)) +{ + struct ipu4_i2c_helper helper = { + .fn = fn, + .driver_data = driver_data, + }; + struct ipu4_i2c_new_dev *new_i2c_dev, *safe; + int rval; + + if ((!fn) || (!driver_data)) + return -ENODEV; + + rval = i2c_for_each_dev(&helper, ipu4_i2c_test); + if (rval < 0) + return rval; + + /* + * Some ACPI entries may contain several i2c devices. + * Create new devices here if those were added to list during + * ACPI processing + */ + list_for_each_entry_safe(new_i2c_dev, safe, &new_devs, list) { + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(new_i2c_dev->bus); + if (!adapter) { + dev_err(dev, "Failed to get adapter\n"); + list_del(&new_i2c_dev->list); + kfree(new_i2c_dev); + continue; + } + + dev_info(dev, "New i2c device: %s\n", new_i2c_dev->info.type); + request_module(I2C_MODULE_PREFIX "%s", new_i2c_dev->info.type); + + client = i2c_new_device(adapter, &new_i2c_dev->info); + if (client) + fn(&client->dev, driver_data, NULL, false); + else + dev_err(dev, "failed to add I2C device from ACPI\n"); + + i2c_put_adapter(adapter); + list_del(&new_i2c_dev->list); + kfree(new_i2c_dev); + } + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_get_acpi_devices); + +static void __exit ipu_acpi_exit(void) +{ + remove_gpio_regulator(); +} +module_exit(ipu_acpi_exit); + +MODULE_AUTHOR("Samu Onkalo "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPU4 ACPI support"); diff --git a/drivers/media/platform/intel/ipu4-ici-bxt-p-pdata.c b/drivers/media/platform/intel/ipu4-ici-bxt-p-pdata.c new file mode 100644 index 000000000000..9d2a2a086395 --- /dev/null +++ b/drivers/media/platform/intel/ipu4-ici-bxt-p-pdata.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "ipu.h" + +#define GPIO_BASE 422 + +#ifdef CONFIG_INTEL_IPU4_ADV7481 + +#define ADV7481_CVBS_LANES 1 +#define ADV7481_HDMI_LANES 4 +#define ADV7481_HDMI_I2C_ADDRESS 0xe0 +#define ADV7481_CVBS_I2C_ADDRESS 0xe1 +static struct crlmodule_lite_platform_data adv7481_hdmi_pdata_lite = { +#if (!IS_ENABLED(CONFIG_VIDEO_INTEL_UOS)) +// xshutdown GPIO pin unavailable on ACRN UOS + .xshutdown = GPIO_BASE + 63, +#endif + .lanes = ADV7481_HDMI_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481 HDMI" +}; +static struct ipu_isys_csi2_config adv7481_hdmi_csi2_cfg = { + .nlanes = ADV7481_HDMI_LANES, + .port = 0, +}; +static struct ipu_isys_subdev_info adv7481_hdmi_crl_sd_lite = { + .csi2 = &adv7481_hdmi_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_LITE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_HDMI_I2C_ADDRESS, + .platform_data = &adv7481_hdmi_pdata_lite, + }, + .i2c_adapter_id = CONFIG_INTEL_IPU4_ADV7481_I2C_ID, + } +}; + +static struct crlmodule_lite_platform_data adv7481_cvbs_pdata_lite = { +#if (!IS_ENABLED(CONFIG_VIDEO_INTEL_UOS)) +// xshutdown GPIO pin unavailable on ACRN UOS + .xshutdown = GPIO_BASE + 63, +#endif + .lanes = ADV7481_CVBS_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481 CVBS" +}; +static struct ipu_isys_csi2_config adv7481_cvbs_csi2_cfg = { + .nlanes = ADV7481_CVBS_LANES, + .port = 4, +}; +static struct ipu_isys_subdev_info adv7481_cvbs_crl_sd_lite = { + .csi2 = &adv7481_cvbs_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_LITE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_CVBS_I2C_ADDRESS, + .platform_data = &adv7481_cvbs_pdata_lite, + }, + .i2c_adapter_id = CONFIG_INTEL_IPU4_ADV7481_I2C_ID, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_ADV7481_EVAL + +#define ADV7481_LANES 4 +//below i2c address is dummy one, to be able to register single ADV7481 chip as two sensors +#define ADV7481_I2C_ADDRESS 0xe0 +#define ADV7481B_I2C_ADDRESS 0xe2 + +static struct crlmodule_lite_platform_data adv7481_eval_pdata_lite = { + .xshutdown = GPIO_BASE + 63, + .lanes = ADV7481_HDMI_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481_EVAL" +}; +static struct ipu_isys_csi2_config adv7481_eval_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 0, +}; +static struct ipu_isys_subdev_info adv7481_eval_crl_sd_lite = { + .csi2 = &adv7481_eval_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_LITE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481_I2C_ADDRESS, + .platform_data = &adv7481_eval_pdata_lite, + }, + .i2c_adapter_id = 2, + } +}; + +static struct crlmodule_lite_platform_data adv7481b_eval_pdata_lite = { + .xshutdown = GPIO_BASE + 63, + .lanes = ADV7481_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){600000000}, + .module_name = "ADV7481B_EVAL" +}; +static struct ipu_isys_csi2_config adv7481b_eval_csi2_cfg = { + .nlanes = ADV7481_LANES, + .port = 4, +}; +static struct ipu_isys_subdev_info adv7481b_eval_crl_sd_lite = { + .csi2 = &adv7481b_eval_csi2_cfg, + .i2c = { + .board_info = { + .type = CRLMODULE_LITE_NAME, + .flags = I2C_CLIENT_TEN, + .addr = ADV7481B_I2C_ADDRESS, + .platform_data = &adv7481b_eval_pdata_lite, + }, + .i2c_adapter_id = 2, + } +}; +#endif + +#ifdef CONFIG_INTEL_IPU4_MAGNA_TI964 + +#define MAGNA_TI964_MIPI_LANES 4 +#define TI964_I2C_ADDRESS 0x3d +static struct crlmodule_lite_platform_data magna_ti964_pdata = { + .xshutdown = GPIO_BASE + 63, + .lanes = MAGNA_TI964_MIPI_LANES, + .ext_clk = 24000000, + .op_sys_clock = (uint64_t []){ 400000000 }, + .module_name = "MAGNA_TI964", +}; +static struct ipu_isys_csi2_config magna_ti964_csi2_cfg = { + .nlanes = MAGNA_TI964_MIPI_LANES, + .port = 0, +}; +static struct ipu_isys_subdev_info magna_ti964_crl_sd = { + .csi2 = &magna_ti964_csi2_cfg, + .i2c = { + .board_info = { + I2C_BOARD_INFO(CRLMODULE_LITE_NAME, TI964_I2C_ADDRESS), + .platform_data = &magna_ti964_pdata, + }, + .i2c_adapter_id = 0, + } +}; + +#endif + +/* + * Map buttress output sensor clocks to sensors - + * this should be coming from ACPI + */ +struct ipu_isys_clk_mapping p_mapping[] = { + { CLKDEV_INIT("0-003d", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT("0-00e1", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("0-00e0", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT("2-a0e0", NULL, NULL), "OSC_CLK_OUT0" }, + { CLKDEV_INIT("2-a0e2", NULL, NULL), "OSC_CLK_OUT1" }, + { CLKDEV_INIT(NULL, NULL, NULL), NULL } +}; + +static struct ipu_isys_subdev_pdata pdata = { + .subdevs = (struct ipu_isys_subdev_info *[]) { +#ifdef CONFIG_INTEL_IPU4_ADV7481 + &adv7481_cvbs_crl_sd_lite, + &adv7481_hdmi_crl_sd_lite, +#endif +#ifdef CONFIG_INTEL_IPU4_ADV7481_EVAL + &adv7481_eval_crl_sd_lite, + &adv7481b_eval_crl_sd_lite, +#endif +#ifdef CONFIG_INTEL_IPU4_MAGNA_TI964 + &magna_ti964_crl_sd, +#endif + NULL, + }, + .clk_map = p_mapping, +}; + +static void ipu4_quirk(struct pci_dev *pci_dev) +{ + pci_dev->dev.platform_data = &pdata; +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU_PCI_ID, + ipu4_quirk); diff --git a/include/media/ipu4-acpi.h b/include/media/ipu4-acpi.h new file mode 100644 index 000000000000..b4c3611664f7 --- /dev/null +++ b/include/media/ipu4-acpi.h @@ -0,0 +1,27 @@ +/* SPDX-LIcense_Identifier: GPL-2.0 */ +/* Copyright (C) 2016 - 2018 Intel Corporation */ + +#ifndef MEDIA_INTEL_IPU4_ACPI_H +#define MEDIA_INTEL_IPU4_ACPI_H + +#include +#include "ipu-isys.h" + +int ipu_get_acpi_devices(void *driver_data, + struct device *dev, + int (*fn) + (struct device *, void *, + struct ipu_isys_csi2_config *csi2, + bool reprobe)); + +struct ipu_regulator { + char *src_dev_name; + char *src_rail; + char *dest_rail; +}; + +/* These can be override by plarform data. */ +extern const struct ipu_regulator imx230regulators[] __weak; +extern const struct ipu_regulator ov8858regulators[] __weak; + +#endif From 8aef605382b8ff1497126e69ac04173edac16a70 Mon Sep 17 00:00:00 2001 From: "Yew, Chang Ching" Date: Tue, 23 Oct 2018 13:26:38 +0800 Subject: [PATCH 1150/1276] media: intel-ipu4: virtio: IPU virtio backend and frontend drivers base-code Base code to enable IPU virtualization for ACRN squashed from following patches from https://github.com/intel/linux-intel-lts/tree/4.14/base/drivers/media/pci/intel/virtio/ These patches added the virtio front end driver support for user OS and virtio back end driver support for service OS. ff9935 media: intel-ipu4: [VIRT] Poll function to return if put buf list is not empty 4943b7 media: intel-ipu4: [VIRT] Fxied failed to stream close properly issue. a2d80c media: intel-ipu4: [VIRT] Adding x86_64 arch dependency. 7da2ee media: intel-ipu4: [VIRT] vhm_request struct removed the valid member. 1865e3 media: intel-ipu4: [VIRT] Fix vq get idx func handling of multi VQ concurrent request. 113db9 media: intel-ipu4: [VIRT] Fixed memory leak in BE get buf function. 35aa2a media: intel-ipu4: [VIRT] Implementing poll without wait at BE. dd2c7a media: intel-ipu4: [VIRT] Implementation for 2 VQs 660300 media: intel-ipu4: [VIRT] Fixing synchronization with locks a74061 media: intel-ipu4: [VIRT] Added 32-bit compatibility mode. 06ca2c media: intel-ipu4: [VIRT] Fixing buffer lookup issue a67055 media: intel-ipu4: [VIRT] Removing compilation warnings & printk 1d0bf8 media: intel-ipu4: [VIRT] Virtio implementation of PUT_BUF & modify get buf list 71c663 media: intel-ipu4: [VIRT] Creating ipu mediation baseline v0.1 848b4f media: intel-ipu4: [VIRT] Buffer mapping between sos & uos b5f40a media: intel-ipu4: [VIRT] Adding Buffer mapping & virtual stream initialization. f08c9d media: intel-ipu4: [VIRT] Base code for IPU virtio support. Change-Id: I3912b9838d7acef1806d2f9045602360b8f98212 Signed-off-by: Yew, Chang Ching --- drivers/media/pci/intel/virtio/Makefile | 10 + drivers/media/pci/intel/virtio/Makefile.virt | 22 + .../intel/virtio/intel-ipu4-para-virt-drv.c | 1330 +++++++++++++++++ .../intel/virtio/intel-ipu4-para-virt-drv.h | 38 + .../virtio/intel-ipu4-virtio-be-bridge.c | 202 +++ .../virtio/intel-ipu4-virtio-be-bridge.h | 26 + .../virtio/intel-ipu4-virtio-be-pipeline.c | 211 +++ .../virtio/intel-ipu4-virtio-be-pipeline.h | 27 + .../virtio/intel-ipu4-virtio-be-stream.c | 389 +++++ .../virtio/intel-ipu4-virtio-be-stream.h | 26 + .../pci/intel/virtio/intel-ipu4-virtio-be.c | 520 +++++++ .../pci/intel/virtio/intel-ipu4-virtio-be.h | 15 + .../intel/virtio/intel-ipu4-virtio-common.c | 61 + .../intel/virtio/intel-ipu4-virtio-common.h | 131 ++ .../virtio/intel-ipu4-virtio-fe-payload.c | 55 + .../virtio/intel-ipu4-virtio-fe-payload.h | 14 + .../virtio/intel-ipu4-virtio-fe-pipeline.c | 44 + .../virtio/intel-ipu4-virtio-fe-pipeline.h | 19 + .../pci/intel/virtio/intel-ipu4-virtio-fe.c | 243 +++ 19 files changed, 3383 insertions(+) create mode 100644 drivers/media/pci/intel/virtio/Makefile create mode 100644 drivers/media/pci/intel/virtio/Makefile.virt create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c diff --git a/drivers/media/pci/intel/virtio/Makefile b/drivers/media/pci/intel/virtio/Makefile new file mode 100644 index 000000000000..0f4eab5addfc --- /dev/null +++ b/drivers/media/pci/intel/virtio/Makefile @@ -0,0 +1,10 @@ +ifneq ($(EXTERNAL_BUILD), 1) +srcpath := $(srctree) +endif + +IPU_STEP = bxtB0 + +include $(srcpath)/$(src)/Makefile.virt + +ccflags-y += -I$(srcpath)/$(src)/../../../../../include/ +ccflags-y += -I$(srcpath)/$(src)/../ diff --git a/drivers/media/pci/intel/virtio/Makefile.virt b/drivers/media/pci/intel/virtio/Makefile.virt new file mode 100644 index 000000000000..c3c30c4bf921 --- /dev/null +++ b/drivers/media/pci/intel/virtio/Makefile.virt @@ -0,0 +1,22 @@ +ifndef IPU_STEP + $(error No IPU_STEP was defined. Stopping.) +endif + +TARGET_MODULE:=intel-ipu-virt-$(IPU_STEP) + +$(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-common.o + + +ifdef CONFIG_VIDEO_INTEL_IPU_VIRTIO_BE + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-pipeline.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-bridge.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-stream.o +else + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe-pipeline.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe-payload.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe.o + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-para-virt-drv.o +endif + +obj-$(CONFIG_VIDEO_INTEL_IPU_ACRN) := $(TARGET_MODULE).o diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c new file mode 100644 index 000000000000..3f6d541c87fe --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c @@ -0,0 +1,1330 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-para-virt-drv.h" +#include "intel-ipu4-virtio-fe-pipeline.h" +#include "intel-ipu4-virtio-fe-payload.h" +#include "./ici/ici-isys-stream.h" +#include "./ici/ici-isys-pipeline-device.h" + + +static dev_t virt_pipeline_dev_t; +static struct class *virt_pipeline_class; +static struct ici_isys_pipeline_device *pipeline_dev; + +static dev_t virt_stream_dev_t; +static struct class *virt_stream_class; +static int virt_stream_devs_registered; +static int stream_dev_init; + +static struct ipu4_virtio_ctx *g_fe_priv; + +struct mutex fop_mutex; + +#ifdef CONFIG_COMPAT +struct timeval32 { + __u32 tv_sec; + __u32 tv_usec; +} __attribute__((__packed__)); + +struct ici_frame_plane32 { + __u32 bytes_used; + __u32 length; + union { + compat_uptr_t userptr; + __s32 dmafd; + } mem; + __u32 data_offset; + __u32 reserved[2]; +} __attribute__((__packed__)); + +struct ici_frame_info32 { + __u32 frame_type; + __u32 field; + __u32 flag; + __u32 frame_buf_id; + struct timeval32 frame_timestamp; + __u32 frame_sequence_id; + __u32 mem_type; /* _DMA or _USER_PTR */ + struct ici_frame_plane32 frame_planes[ICI_MAX_PLANES]; /* multi-planar */ + __u32 num_planes; /* =1 single-planar > 1 multi-planar array size */ + __u32 reserved[2]; +} __attribute__((__packed__)); + +#define ICI_IOC_GET_BUF32 _IOWR(MAJOR_STREAM, 3, struct ici_frame_info32) +#define ICI_IOC_PUT_BUF32 _IOWR(MAJOR_STREAM, 4, struct ici_frame_info32) + +static void copy_from_user_frame_info32(struct ici_frame_info *kp, struct ici_frame_info32 __user *up) +{ + int i; + compat_uptr_t userptr; + + get_user(kp->frame_type, &up->frame_type); + get_user(kp->field, &up->field); + get_user(kp->flag, &up->flag); + get_user(kp->frame_buf_id, &up->frame_buf_id); + get_user(kp->frame_timestamp.tv_sec, &up->frame_timestamp.tv_sec); + get_user(kp->frame_timestamp.tv_usec, &up->frame_timestamp.tv_usec); + get_user(kp->frame_sequence_id, &up->frame_sequence_id); + get_user(kp->mem_type, &up->mem_type); + get_user(kp->num_planes, &up->num_planes); + for (i = 0; i < kp->num_planes; i++) { + get_user(kp->frame_planes[i].bytes_used, &up->frame_planes[i].bytes_used); + get_user(kp->frame_planes[i].length, &up->frame_planes[i].length); + if (kp->mem_type == ICI_MEM_USERPTR) { + get_user(userptr, &up->frame_planes[i].mem.userptr); + kp->frame_planes[i].mem.userptr = (unsigned long) compat_ptr(userptr); + } else if (kp->mem_type == ICI_MEM_DMABUF) { + get_user(kp->frame_planes[i].mem.dmafd, &up->frame_planes[i].mem.dmafd); + }; + get_user(kp->frame_planes[i].data_offset, &up->frame_planes[i].data_offset); + } +} + +static void copy_to_user_frame_info32(struct ici_frame_info *kp, struct ici_frame_info32 __user *up) +{ + int i; + compat_uptr_t userptr; + + put_user(kp->frame_type, &up->frame_type); + put_user(kp->field, &up->field); + put_user(kp->flag, &up->flag); + put_user(kp->frame_buf_id, &up->frame_buf_id); + put_user(kp->frame_timestamp.tv_sec, &up->frame_timestamp.tv_sec); + put_user(kp->frame_timestamp.tv_usec, &up->frame_timestamp.tv_usec); + put_user(kp->frame_sequence_id, &up->frame_sequence_id); + put_user(kp->mem_type, &up->mem_type); + put_user(kp->num_planes, &up->num_planes); + for (i = 0; i < kp->num_planes; i++) { + put_user(kp->frame_planes[i].bytes_used, &up->frame_planes[i].bytes_used); + put_user(kp->frame_planes[i].length, &up->frame_planes[i].length); + if (kp->mem_type == ICI_MEM_USERPTR) { + userptr = (unsigned long)compat_ptr(kp->frame_planes[i].mem.userptr); + put_user(userptr, &up->frame_planes[i].mem.userptr); + } else if (kp->mem_type == ICI_MEM_DMABUF) { + get_user(kp->frame_planes[i].mem.dmafd, &up->frame_planes[i].mem.dmafd); + } + put_user(kp->frame_planes[i].data_offset, &up->frame_planes[i].data_offset); + } +} +#endif + +static int get_userpages(struct device *dev, struct ici_frame_plane *frame_plane, + struct ici_kframe_plane *kframe_plane) +{ + unsigned long start, end, addr; + int npages, array_size; + struct page **pages; + int nr = 0; + int ret = 0; + struct sg_table *sgt; + unsigned int i; + u64 page_table_ref; + u64 *page_table; + addr = (unsigned long)frame_plane->mem.userptr; + start = addr & PAGE_MASK; + end = PAGE_ALIGN(addr + frame_plane->length); + npages = (end - start) >> PAGE_SHIFT; + array_size = npages * sizeof(struct page *); + + if (!npages) + return -EINVAL; + + page_table = kcalloc(npages, sizeof(*page_table), GFP_KERNEL); + if (!page_table) { + pr_err("Shared Page table for mediation failed\n"); + return -ENOMEM; + } + + pr_debug("%s:%d Number of Pages:%d frame_length:%d\n", __func__, __LINE__, npages, frame_plane->length); + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, GFP_KERNEL); + else + pages = vzalloc(array_size); + if (!pages) + return -ENOMEM; + + down_read(¤t->mm->mmap_sem); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + nr = get_user_pages(current, current->mm, + start, npages, 1, 0, pages, NULL); +#else + nr = get_user_pages(start, npages, FOLL_WRITE, pages, NULL); +#endif + if (nr < npages) + goto error_free_pages; + /* Share physical address of pages */ + for (i = 0; i < npages; i++) + page_table[i] = page_to_phys(pages[i]); + + pr_debug("UOS phy page add %lld offset:%ld\n", page_table[0], addr & ~PAGE_MASK); + page_table_ref = virt_to_phys(page_table); + kframe_plane->page_table_ref = page_table_ref; + kframe_plane->npages = npages; + up_read(¤t->mm->mmap_sem); + return ret; +error_free_pages: + if (pages) { + for (i = 0; i < nr; i++) + put_page(pages[i]); + } + kfree(sgt); + return -ENOMEM; +} + +static struct ici_frame_buf_wrapper *frame_buf_lookup(struct ici_isys_frame_buf_list *buf_list, struct ici_frame_info *user_frame_info) +{ + struct ici_frame_buf_wrapper *buf; + int i; + int mem_type = user_frame_info->mem_type; + + list_for_each_entry(buf, &buf_list->getbuf_list, uos_node) { + for (i = 0; i < user_frame_info->num_planes; i++) { + struct ici_frame_plane *new_plane = &user_frame_info->frame_planes[i]; + struct ici_frame_plane *cur_plane = &buf->frame_info.frame_planes[i]; + + if (buf->state != ICI_BUF_PREPARED && + buf->state != ICI_BUF_DONE) + continue; + + switch (mem_type) { + case ICI_MEM_USERPTR: + if (new_plane->mem.userptr == cur_plane->mem.userptr) + return buf; + break; + case ICI_MEM_DMABUF: + if (new_plane->mem.dmafd == cur_plane->mem.dmafd) + return buf; + break; + } + //TODO: add multiplaner checks + } + } + return NULL; +} +static void put_userpages(struct ici_kframe_plane *kframe_plane) +{ + struct sg_table *sgt = kframe_plane->sgt; + struct scatterlist *sgl; + unsigned int i; + struct mm_struct *mm = current->active_mm; + + if (!mm) { + pr_err("Failed to get active mm_struct ptr from current process.\n"); + return; + } + + down_read(&mm->mmap_sem); + for_each_sg(sgt->sgl, sgl, sgt->orig_nents, i) { + struct page *page = sg_page(sgl); + + unsigned int npages = PAGE_ALIGN(sgl->offset + sgl->length) >> PAGE_SHIFT; + unsigned int page_no; + + for (page_no = 0; page_no < npages; ++page_no, ++page) { + set_page_dirty_lock(page); + put_page(page); + } + } + + kfree(sgt); + kframe_plane->sgt = NULL; + + up_read(&mm->mmap_sem); +} + +static void put_dma(struct ici_kframe_plane *kframe_plane) +{ + struct sg_table *sgt = kframe_plane->sgt; + + if (WARN_ON(!kframe_plane->db_attach)) { + pr_err("trying to unpin a not attached buffer\n"); + return; + } + + if (WARN_ON(!sgt)) { + pr_err("dmabuf buffer is already unpinned\n"); + return; + } + + if (kframe_plane->kaddr) { + dma_buf_vunmap(kframe_plane->db_attach->dmabuf, + kframe_plane->kaddr); + kframe_plane->kaddr = NULL; + } + dma_buf_unmap_attachment(kframe_plane->db_attach, sgt, + DMA_BIDIRECTIONAL); + + kframe_plane->dma_addr = 0; + kframe_plane->sgt = NULL; + +} + +static int map_dma(struct device *dev, struct ici_frame_plane *frame_plane, + struct ici_kframe_plane *kframe_plane) +{ + + int ret = 0; + int fd = frame_plane->mem.dmafd; + + kframe_plane->dbdbuf = dma_buf_get(fd); + if (!kframe_plane->dbdbuf) { + ret = -EINVAL; + goto error; + } + + if (frame_plane->length == 0) + kframe_plane->length = kframe_plane->dbdbuf->size; + else + kframe_plane->length = frame_plane->length; + + kframe_plane->fd = fd; + kframe_plane->db_attach = dma_buf_attach(kframe_plane->dbdbuf, dev); + + if (IS_ERR(kframe_plane->db_attach)) { + ret = PTR_ERR(kframe_plane->db_attach); + goto error_put; + } + + kframe_plane->sgt = dma_buf_map_attachment(kframe_plane->db_attach, + DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(kframe_plane->sgt)) { + ret = -EINVAL; + kframe_plane->sgt = NULL; + pr_err("map attachment failed\n"); + goto error_detach; + } + + kframe_plane->dma_addr = sg_dma_address(kframe_plane->sgt->sgl); + kframe_plane->kaddr = dma_buf_vmap(kframe_plane->dbdbuf); + + if (!kframe_plane->kaddr) { + ret = -EINVAL; + goto error_detach; + } + + pr_debug("MAPBUF: mapped fd %d\n", fd); + + return 0; + +error_detach: + dma_buf_detach(kframe_plane->dbdbuf, kframe_plane->db_attach); +error_put: + dma_buf_put(kframe_plane->dbdbuf); +error: + return ret; +} + +static void unmap_buf(struct ici_frame_buf_wrapper *buf) +{ + int i; + + for (i = 0; i < buf->frame_info.num_planes; i++) { + struct ici_kframe_plane *kframe_plane = + &buf->kframe_info.planes[i]; + switch (kframe_plane->mem_type) { + case ICI_MEM_USERPTR: + put_userpages(kframe_plane); + break; + case ICI_MEM_DMABUF: + put_dma(kframe_plane); + break; + default: + pr_debug("not supported memory type: %d\n", kframe_plane->mem_type); + break; + } + } +} +struct ici_frame_buf_wrapper *get_buf(struct virtual_stream *vstream, struct ici_frame_info *frame_info) +{ + int res; + unsigned i; + struct ici_frame_buf_wrapper *buf; + + struct ici_kframe_plane *kframe_plane; + struct ici_isys_frame_buf_list *buf_list = &vstream->buf_list; + int mem_type = frame_info->mem_type; + + if (mem_type != ICI_MEM_USERPTR && mem_type != ICI_MEM_DMABUF) { + pr_err("Memory type not supproted\n"); + return NULL; + } + + if (!frame_info->frame_planes[0].length) { + pr_err("User length not set\n"); + return NULL; + } + + buf = frame_buf_lookup(buf_list, frame_info); + if (buf) { + pr_debug("Frame buffer found in the list: %ld\n", buf->frame_info.frame_planes[0].mem.userptr); + buf->state = ICI_BUF_PREPARED; + return buf; + } + pr_debug("Creating new buffer in the list\n"); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + buf->buf_id = frame_info->frame_buf_id; + buf->uos_buf_list = buf_list; + memcpy(&buf->frame_info, frame_info, sizeof(buf->frame_info)); + + switch (mem_type) { + case ICI_MEM_USERPTR: + if (!frame_info->frame_planes[0].mem.userptr) { + pr_err("User pointer not define\n"); + return NULL; + } + for (i = 0; i < frame_info->num_planes; i++) { + kframe_plane = &buf->kframe_info.planes[i]; + kframe_plane->mem_type = ICI_MEM_USERPTR; + res = get_userpages(&vstream->strm_dev.dev, &frame_info->frame_planes[i], + kframe_plane); + if (res) + return NULL; + } + break; + case ICI_MEM_DMABUF: + for (i = 0; i < frame_info->num_planes; i++) { + kframe_plane = &buf->kframe_info.planes[i]; + kframe_plane->mem_type = ICI_MEM_DMABUF; + res = map_dma(&vstream->strm_dev.dev, &frame_info->frame_planes[i], + kframe_plane); + if (res) + return NULL; + } + + break; + } + mutex_lock(&buf_list->mutex); + buf->state = ICI_BUF_PREPARED; + list_add_tail(&buf->uos_node, &buf_list->getbuf_list); + mutex_unlock(&buf_list->mutex); + return buf; +} + +//Call from Stream-OFF and if Stream-ON fails +void buf_stream_cancel(struct virtual_stream *vstream) +{ + struct ici_isys_frame_buf_list *buf_list = &vstream->buf_list; + struct ici_frame_buf_wrapper *buf; + struct ici_frame_buf_wrapper *next_buf; + + list_for_each_entry_safe(buf, next_buf, + &buf_list->getbuf_list, uos_node) { + list_del(&buf->uos_node); + } + list_for_each_entry_safe(buf, next_buf, + &buf_list->putbuf_list, uos_node) { + list_del(&buf->uos_node); + } +} + +static int virt_isys_set_format(struct file *file, void *fh, + struct ici_stream_format *sf) +{ + struct ici_stream_device *strm_dev = fh; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ipu4_virtio_req *req; + int rval = 0; + int op[10]; + + pr_debug("Calling Set Format\n"); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + op[0] = vstream->virt_dev_id; + op[1] = 0; + + req->payload = virt_to_phys(sf); + + intel_ipu4_virtio_create_req(req, IPU4_CMD_SET_FORMAT, &op[0]); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + dev_err(&strm_dev->dev, "Failed to open virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + return rval; +} + +static int virt_isys_stream_on(struct file *file, void *fh) +{ + struct ici_stream_device *strm_dev = fh; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ipu4_virtio_req *req; + int rval = 0; + int op[10]; + pr_debug("Calling Stream ON\n"); + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + op[0] = vstream->virt_dev_id; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_STREAM_ON, &op[0]); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + dev_err(&strm_dev->dev, "Failed to open virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req && !fe_ctx) + return -ENOMEM; + + return rval; +} + +static int virt_isys_stream_off(struct file *file, void *fh) +{ + struct ici_stream_device *strm_dev = fh; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ipu4_virtio_req *req; + int rval = 0; + int op[10]; + + pr_debug("Calling Stream OFF\n"); + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + op[0] = vstream->virt_dev_id; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_STREAM_OFF, &op[0]); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + dev_err(&strm_dev->dev, "Failed to open virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + buf_stream_cancel(vstream); + + return rval; +} + +static int virt_isys_getbuf(struct file *file, void *fh, + struct ici_frame_info *user_frame_info) +{ + struct ici_stream_device *strm_dev = fh; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ipu4_virtio_req *req; + struct ici_frame_buf_wrapper *buf; + int rval = 0; + int op[3]; + + pr_debug("Calling Get Buffer\n"); + + buf = get_buf(vstream, user_frame_info); + if (!buf) { + dev_err(&strm_dev->dev, "Failed to map buffer: %d\n", rval); + return -ENOMEM; + } + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = vstream->virt_dev_id; + op[1] = 0; + op[2] = user_frame_info->mem_type; + req->payload = virt_to_phys(buf); + + intel_ipu4_virtio_create_req(req, IPU4_CMD_GET_BUF, &op[0]); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + dev_err(&strm_dev->dev, "Failed to Get Buffer\n"); + kfree(req); + return rval; + } + kfree(req); + + return rval; +} + +static int virt_isys_putbuf(struct file *file, void *fh, + struct ici_frame_info *user_frame_info) +{ + struct ici_stream_device *strm_dev = fh; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ipu4_virtio_req *req; + int rval = 0; + int op[2]; + + pr_debug("Calling Put Buffer\n"); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = vstream->virt_dev_id; + op[1] = 0; + req->payload = virt_to_phys(user_frame_info); + + intel_ipu4_virtio_create_req(req, IPU4_CMD_PUT_BUF, &op[0]); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + dev_err(&strm_dev->dev, "Failed to Get Buffer\n"); + kfree(req); + return rval; + } + kfree(req); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req && !fe_ctx) + return -ENOMEM; + + return rval; +} + +static unsigned int stream_fop_poll(struct file *file, struct ici_stream_device *dev) +{ + struct ipu4_virtio_req *req; + struct virtual_stream *vstream = dev_to_vstream(dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + struct ici_stream_device *strm_dev = file->private_data; + int rval = 0; + int op[2]; + + dev_dbg(&strm_dev->dev, "stream_fop_poll %d\n", vstream->virt_dev_id); + get_device(&dev->dev); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = vstream->virt_dev_id; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_POLL, &op[0]); + + mutex_lock(&fop_mutex); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + if (rval) { + mutex_unlock(&fop_mutex); + dev_err(&strm_dev->dev, "Failed to open virtual device\n"); + kfree(req); + return rval; + } + + mutex_unlock(&fop_mutex); + + rval = req->func_ret; + kfree(req); + + return rval; +} + +static int virt_stream_fop_open(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = inode_to_intel_ipu_stream_device(inode); + struct ipu4_virtio_req *req; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + int rval = 0; + int op[3]; + pr_debug("%s %d", __func__, vstream->virt_dev_id); + get_device(&strm_dev->dev); + + file->private_data = strm_dev; + + if (!fe_ctx) + return -EINVAL; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) { + dev_err(&strm_dev->dev, "Virtio Req buffer failed\n"); + return -ENOMEM; + } + + op[0] = vstream->virt_dev_id; + op[1] = 1; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_DEVICE_OPEN, &op[0]); + + mutex_lock(&fop_mutex); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + if (rval) { + mutex_unlock(&fop_mutex); + dev_err(&strm_dev->dev, "Failed to open virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + mutex_unlock(&fop_mutex); + + return rval; +} + +static int virt_stream_fop_release(struct inode *inode, struct file *file) +{ + struct ici_stream_device *strm_dev = inode_to_intel_ipu_stream_device(inode); + struct ipu4_virtio_req *req; + struct virtual_stream *vstream = dev_to_vstream(strm_dev); + struct ipu4_virtio_ctx *fe_ctx = vstream->ctx; + int rval = 0; + int op[2]; + pr_debug("%s %d", __func__, vstream->virt_dev_id); + put_device(&strm_dev->dev); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = vstream->virt_dev_id; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_DEVICE_CLOSE, &op[0]); + + mutex_lock(&fop_mutex); + + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + if (rval) { + mutex_unlock(&fop_mutex); + dev_err(&strm_dev->dev, "Failed to close virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + mutex_unlock(&fop_mutex); + + return rval; +} + +static unsigned int virt_stream_fop_poll(struct file *file, + struct poll_table_struct *poll) +{ + struct ici_stream_device *as = file->private_data; + unsigned int res = POLLERR | POLLHUP; + + dev_dbg(&as->dev, "virt_stream_fop_poll for:%s\n", as->name); + + res = stream_fop_poll(file, as); + + //res = POLLIN; + + dev_dbg(&as->dev, "virt_stream_fop_poll res %u\n", res); + + return res; +} + +static long virt_stream_ioctl32(struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + union isys_ioctl_cmd_args { + struct ici_frame_info frame_info; + struct ici_stream_format sf; + }; + void __user *up = compat_ptr(ioctl_arg); + union isys_ioctl_cmd_args *data = NULL; + int err = 0; + struct ici_stream_device *dev = file->private_data; + + mutex_lock(dev->mutex); + switch (ioctl_cmd) { + case ICI_IOC_STREAM_ON: + pr_debug("IPU FE IOCTL STREAM_ON\n"); + err = virt_isys_stream_on(file, dev); + break; + case ICI_IOC_STREAM_OFF: + pr_debug("IPU FE IOCTL STREAM_OFF\n"); + err = virt_isys_stream_off(file, dev); + break; + case ICI_IOC_GET_BUF32: + pr_debug("IPU FE IOCTL GET_BUF\n"); + data = (union isys_ioctl_cmd_args *) kzalloc(sizeof(union isys_ioctl_cmd_args), GFP_KERNEL); + copy_from_user_frame_info32(&data->frame_info, up); + err = virt_isys_getbuf(file, dev, &data->frame_info); + copy_to_user_frame_info32(&data->frame_info, up); + kfree(data); + if (err) { + mutex_unlock(dev->mutex); + return -EFAULT; + } + break; + case ICI_IOC_PUT_BUF32: + pr_debug("IPU FE IOCTL PUT_BUF\n"); + data = (union isys_ioctl_cmd_args *) kzalloc(sizeof(union isys_ioctl_cmd_args), GFP_KERNEL); + copy_from_user_frame_info32(&data->frame_info, up); + err = virt_isys_putbuf(file, dev, &data->frame_info); + copy_to_user_frame_info32(&data->frame_info, up); + kfree(data); + if (err) { + mutex_unlock(dev->mutex); + return -EFAULT; + } + break; + case ICI_IOC_SET_FORMAT: + pr_debug("IPU FE IOCTL SET_FORMAT\n"); + if (_IOC_SIZE(ioctl_cmd) > sizeof(union isys_ioctl_cmd_args)) { + mutex_unlock(dev->mutex); + return -ENOTTY; + } + + data = (union isys_ioctl_cmd_args *) kzalloc(sizeof(union isys_ioctl_cmd_args), GFP_KERNEL); + err = copy_from_user(data, up, _IOC_SIZE(ioctl_cmd)); + if (err) { + kfree(data); + mutex_unlock(dev->mutex); + return -EFAULT; + } + err = virt_isys_set_format(file, dev, &data->sf); + err = copy_to_user(up, data, _IOC_SIZE(ioctl_cmd)); + if (err) { + kfree(data); + mutex_unlock(dev->mutex); + return -EFAULT; + } + kfree(data); + break; + + default: + err = -ENOTTY; + break; + } + + mutex_unlock(dev->mutex); + + return 0; +} + +static long virt_stream_ioctl(struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + union isys_ioctl_cmd_args { + struct ici_frame_info frame_info; + struct ici_stream_format sf; + }; + int err = 0; + union isys_ioctl_cmd_args *data = NULL; + struct ici_stream_device *dev = file->private_data; + void __user *up = (void __user *)ioctl_arg; + + bool copy = (ioctl_cmd != ICI_IOC_STREAM_ON && + ioctl_cmd != ICI_IOC_STREAM_OFF); + + if (copy) { + if (_IOC_SIZE(ioctl_cmd) > sizeof(union isys_ioctl_cmd_args)) + return -ENOTTY; + + data = (union isys_ioctl_cmd_args *) kzalloc(sizeof(union isys_ioctl_cmd_args), GFP_KERNEL); + if (_IOC_DIR(ioctl_cmd) & _IOC_WRITE) { + err = copy_from_user(data, up, + _IOC_SIZE(ioctl_cmd)); + if (err) { + kfree(data); + return -EFAULT; + } + } + } + + mutex_lock(dev->mutex); + switch (ioctl_cmd) { + case ICI_IOC_STREAM_ON: + err = virt_isys_stream_on(file, dev); + break; + case ICI_IOC_STREAM_OFF: + err = virt_isys_stream_off(file, dev); + break; + case ICI_IOC_GET_BUF: + err = virt_isys_getbuf(file, dev, &data->frame_info); + break; + case ICI_IOC_PUT_BUF: + err = virt_isys_putbuf(file, dev, &data->frame_info); + break; + case ICI_IOC_SET_FORMAT: + err = virt_isys_set_format(file, dev, &data->sf); + break; + default: + err = -ENOTTY; + break; + } + + mutex_unlock(dev->mutex); + + if (copy) { + err = copy_to_user(up, data, _IOC_SIZE(ioctl_cmd)); + kfree(data); + } + return 0; +} + + +static const struct file_operations virt_stream_fops = { + .owner = THIS_MODULE, + .open = virt_stream_fop_open, /* calls strm_dev->fops->open() */ + .unlocked_ioctl = virt_stream_ioctl, /* calls strm_dev->ipu_ioctl_ops->() */ +#ifdef CONFIG_COMPAT + .compat_ioctl = virt_stream_ioctl32, +#endif + .release = virt_stream_fop_release, /* calls strm_dev->fops->release() */ + .poll = virt_stream_fop_poll, /* calls strm_dev->fops->poll() */ +}; + +/* Called on device_unregister */ +static void base_device_release(struct device *sd) +{ +} + +int virt_frame_buf_init(struct ici_isys_frame_buf_list *buf_list) +{ + buf_list->drv_priv = NULL; + mutex_init(&buf_list->mutex); + spin_lock_init(&buf_list->lock); + spin_lock_init(&buf_list->short_packet_queue_lock); + INIT_LIST_HEAD(&buf_list->getbuf_list); + INIT_LIST_HEAD(&buf_list->putbuf_list); + INIT_LIST_HEAD(&buf_list->interlacebuf_list); + init_waitqueue_head(&buf_list->wait); + return 0; +} + +static int virt_ici_stream_init(struct virtual_stream *vstream, + struct ici_stream_device *strm_dev) +{ + int rval; + int num; + struct ipu4_virtio_ctx *fe_ctx; + + if (!stream_dev_init) { + virt_stream_dev_t = MKDEV(MAJOR_STREAM, 0); + + rval = register_chrdev_region(virt_stream_dev_t, + MAX_STREAM_DEVICES, ICI_STREAM_DEVICE_NAME); + if (rval) { + pr_err("can't register virt_ici stream chrdev region (%d)\n", rval); + return rval; + } + + virt_stream_class = class_create(THIS_MODULE, ICI_STREAM_DEVICE_NAME); + if (IS_ERR(virt_stream_class)) { + unregister_chrdev_region(virt_stream_dev_t, MAX_STREAM_DEVICES); + pr_err("Failed to register device class %s\n", ICI_STREAM_DEVICE_NAME); + return PTR_ERR(virt_stream_class); + } + stream_dev_init++; + } + + num = virt_stream_devs_registered; + strm_dev->minor = -1; + cdev_init(&strm_dev->cdev, &virt_stream_fops); + strm_dev->cdev.owner = virt_stream_fops.owner; + + rval = cdev_add(&strm_dev->cdev, MKDEV(MAJOR(virt_stream_dev_t), num), 1); + if (rval) { + pr_err("%s: failed to add cdevice\n", __func__); + return rval; + } + + strm_dev->dev.class = virt_stream_class; + strm_dev->dev.devt = MKDEV(MAJOR(virt_stream_dev_t), num); + dev_set_name(&strm_dev->dev, "%s%d", ICI_STREAM_DEVICE_NAME, num); + + rval = device_register(&strm_dev->dev); + if (rval < 0) { + pr_err("%s: device_register failed\n", __func__); + cdev_del(&strm_dev->cdev); + return rval; + } + strm_dev->dev.release = base_device_release; + strlcpy(strm_dev->name, strm_dev->dev.kobj.name, sizeof(strm_dev->name)); + strm_dev->minor = num; + vstream->virt_dev_id = num; + + virt_stream_devs_registered++; + + fe_ctx = kcalloc(1, sizeof(struct ipu4_virtio_ctx), + GFP_KERNEL); + + if (!fe_ctx) + return -ENOMEM; + + fe_ctx->bknd_ops = &ipu4_virtio_bknd_ops; + + if (fe_ctx->bknd_ops->init) { + rval = fe_ctx->bknd_ops->init(); + if (rval < 0) { + pr_err("failed to initialize backend.\n"); + return rval; + } + } + + fe_ctx->domid = fe_ctx->bknd_ops->get_vm_id(); + vstream->ctx = fe_ctx; + dev_dbg(&strm_dev->dev, "IPU FE registered with domid:%d\n", fe_ctx->domid); + + return 0; +} + +static void virt_ici_stream_exit(void) +{ + class_unregister(virt_stream_class); + unregister_chrdev_region(virt_stream_dev_t, MAX_STREAM_DEVICES); + + pr_notice("Virtual stream device unregistered\n"); +} + +static int virt_pipeline_fop_open(struct inode *inode, struct file *file) +{ + struct ici_isys_pipeline_device *dev = inode_to_ici_isys_pipeline_device(inode); + struct ipu4_virtio_req *req; + int rval = 0; + int op[2]; + pr_debug("virt pipeline open\n"); + get_device(&dev->dev); + + file->private_data = dev; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = dev->minor; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_PIPELINE_OPEN, &op[0]); + + rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_1); + if (rval) { + pr_err("Failed to open virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + return rval; +} + +static int virt_pipeline_fop_release(struct inode *inode, struct file *file) +{ + int rval = 0; + int op[2]; + struct ipu4_virtio_req *req; + + struct ici_isys_pipeline_device *pipe_dev = + inode_to_ici_isys_pipeline_device(inode); + + put_device(&pipe_dev->dev); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + op[0] = pipe_dev->minor; + op[1] = 0; + + intel_ipu4_virtio_create_req(req, IPU4_CMD_PIPELINE_CLOSE, &op[0]); + + rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_1); + if (rval) { + pr_err("Failed to close virtual device\n"); + kfree(req); + return rval; + } + kfree(req); + + return rval; +} + +static long virt_pipeline_ioctl_common(void __user *up, + struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + union isys_ioctl_cmd_args { + struct ici_node_desc node_desc; + struct ici_link_desc link; + struct ici_pad_framefmt pad_prop; + struct ici_pad_supported_format_desc + format_desc; + struct ici_links_query links_query; + struct ici_pad_selection pad_sel; + }; + int err = 0; + union isys_ioctl_cmd_args *data = NULL; + struct ici_isys_pipeline_device *dev = file->private_data; + + if (_IOC_SIZE(ioctl_cmd) > sizeof(union isys_ioctl_cmd_args)) + return -ENOTTY; + + data = (union isys_ioctl_cmd_args *) kzalloc(sizeof(union isys_ioctl_cmd_args), GFP_KERNEL); + if (_IOC_DIR(ioctl_cmd) & _IOC_WRITE) { + err = copy_from_user(data, up, + _IOC_SIZE(ioctl_cmd)); + if (err) { + kfree(data); + return -EFAULT; + } + } + mutex_lock(&dev->mutex); + switch (ioctl_cmd) { + case ICI_IOC_ENUM_NODES: + err = process_pipeline(file, g_fe_priv, + (void *)&data->node_desc, IPU4_CMD_ENUM_NODES); + break; + case ICI_IOC_ENUM_LINKS: + pr_debug("virt_pipeline_ioctl: ICI_IOC_ENUM_LINKS\n"); + err = process_pipeline(file, g_fe_priv, (void *)&data->links_query, IPU4_CMD_ENUM_LINKS); + break; + case ICI_IOC_SETUP_PIPE: + pr_debug("virt_pipeline_ioctl: ICI_IOC_SETUP_PIPE\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->link, IPU4_CMD_SETUP_PIPE); + break; + case ICI_IOC_SET_FRAMEFMT: + pr_debug("virt_pipeline_ioctl: ICI_IOC_SET_FRAMEFMT\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->pad_prop, IPU4_CMD_SET_FRAMEFMT); + break; + case ICI_IOC_GET_FRAMEFMT: + pr_debug("virt_pipeline_ioctl: ICI_IOC_GET_FRAMEFMT\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->pad_prop, IPU4_CMD_GET_FRAMEFMT); + break; + case ICI_IOC_GET_SUPPORTED_FRAMEFMT: + pr_debug("virt_pipeline_ioctl: ICI_IOC_GET_SUPPORTED_FRAMEFMT\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->format_desc, IPU4_CMD_GET_SUPPORTED_FRAMEFMT); + break; + case ICI_IOC_SET_SELECTION: + pr_debug("virt_pipeline_ioctl: ICI_IOC_SET_SELECTION\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->pad_sel, IPU4_CMD_SET_SELECTION); + break; + case ICI_IOC_GET_SELECTION: + pr_debug("virt_pipeline_ioctl: ICI_IOC_GET_SELECTION\n"); + err = process_pipeline(file, g_fe_priv, + (void *)&data->pad_sel, IPU4_CMD_GET_SELECTION); + break; + default: + err = -ENOTTY; + break; + } + + mutex_unlock(&dev->mutex); + if (err < 0) { + kfree(data); + return err; + } + + if (_IOC_DIR(ioctl_cmd) & _IOC_READ) { + err = copy_to_user(up, data, + _IOC_SIZE(ioctl_cmd)); + if (err) { + kfree(data); + return -EFAULT; + } + } + kfree(data); + + return 0; +} + +static long virt_pipeline_ioctl(struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + void __user *up = (void __user *)ioctl_arg; + return virt_pipeline_ioctl_common(up, file, ioctl_cmd, ioctl_arg); +} + +static long virt_pipeline_ioctl32(struct file *file, unsigned int ioctl_cmd, + unsigned long ioctl_arg) +{ + void __user *up = compat_ptr(ioctl_arg); + return virt_pipeline_ioctl_common(up, file, ioctl_cmd, ioctl_arg); +} + +static const struct file_operations virt_pipeline_fops = { + .owner = THIS_MODULE, + .open = virt_pipeline_fop_open, + .unlocked_ioctl = virt_pipeline_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = virt_pipeline_ioctl32, +#endif + .release = virt_pipeline_fop_release, +}; + +static int virt_fe_init(void) +{ + int rval; + + g_fe_priv = kcalloc(1, sizeof(struct ipu4_virtio_ctx), + GFP_KERNEL); + + if (!g_fe_priv) + return -ENOMEM; + + g_fe_priv->bknd_ops = &ipu4_virtio_bknd_ops; + + if (g_fe_priv->bknd_ops->init) { + rval = g_fe_priv->bknd_ops->init(); + if (rval < 0) { + pr_err("failed to initialize backend.\n"); + return rval; + } + } + + g_fe_priv->domid = g_fe_priv->bknd_ops->get_vm_id(); + + pr_debug("FE registered with domid:%d\n", g_fe_priv->domid); + + return 0; +} + +static int virt_ici_pipeline_init(void) +{ + int rval; + pr_notice("Initializing pipeline\n"); + virt_pipeline_dev_t = MKDEV(MAJOR_PIPELINE, 0); + + rval = register_chrdev_region(virt_pipeline_dev_t, + MAX_PIPELINE_DEVICES, ICI_PIPELINE_DEVICE_NAME); + if (rval) { + pr_err("can't register virt_ici stream chrdev region (%d)\n", + rval); + return rval; + } + + virt_pipeline_class = class_create(THIS_MODULE, ICI_PIPELINE_DEVICE_NAME); + if (IS_ERR(virt_pipeline_class)) { + unregister_chrdev_region(virt_pipeline_dev_t, MAX_PIPELINE_DEVICES); + pr_err("Failed to register device class %s\n", ICI_PIPELINE_DEVICE_NAME); + return PTR_ERR(virt_pipeline_class); + } + + pipeline_dev = kzalloc(sizeof(*pipeline_dev), GFP_KERNEL); + if (!pipeline_dev) + return -ENOMEM; + pipeline_dev->minor = -1; + cdev_init(&pipeline_dev->cdev, &virt_pipeline_fops); + pipeline_dev->cdev.owner = virt_pipeline_fops.owner; + + rval = cdev_add(&pipeline_dev->cdev, MKDEV(MAJOR_PIPELINE, MINOR_PIPELINE), 1); + if (rval) { + pr_err("%s: failed to add cdevice\n", __func__); + return rval; + } + + pipeline_dev->dev.class = virt_pipeline_class; + pipeline_dev->dev.devt = MKDEV(MAJOR_PIPELINE, MINOR_PIPELINE); + dev_set_name(&pipeline_dev->dev, "%s", ICI_PIPELINE_DEVICE_NAME); + + rval = device_register(&pipeline_dev->dev); + if (rval < 0) { + pr_err("%s: device_register failed\n", __func__); + cdev_del(&pipeline_dev->cdev); + return rval; + } + pipeline_dev->dev.release = base_device_release; + strlcpy(pipeline_dev->name, pipeline_dev->dev.kobj.name, sizeof(pipeline_dev->name)); + pipeline_dev->minor = MINOR_PIPELINE; + + return 0; +} + +static int __init virt_ici_init(void) +{ + struct virtual_stream *vstream; + int rval = 0, i; + pr_notice("Initializing IPU Para virtual driver\n"); + for (i = 0; i < MAX_ISYS_VIRT_STREAM; i++) { + + vstream = kzalloc(sizeof(*vstream), GFP_KERNEL); + if (!vstream) + return -ENOMEM; + mutex_init(&vstream->mutex); + mutex_init(&fop_mutex); + vstream->strm_dev.mutex = &vstream->mutex; + + rval = virt_frame_buf_init(&vstream->buf_list); + if (rval) + goto init_fail; + + dev_set_drvdata(&vstream->strm_dev.dev, vstream); + + mutex_lock(&vstream->mutex); + rval = virt_ici_stream_init(vstream, &vstream->strm_dev); + mutex_unlock(&vstream->mutex); + + if (rval) + goto init_fail; + } + + rval = virt_ici_pipeline_init(); + if (rval) + goto init_fail; + + rval = virt_fe_init(); + return rval; + +init_fail: + mutex_destroy(&vstream->mutex); + mutex_destroy(&fop_mutex); + kfree(vstream); + return rval; +} + +static void virt_ici_pipeline_exit(void) +{ + class_unregister(virt_pipeline_class); + unregister_chrdev_region(virt_pipeline_dev_t, MAX_PIPELINE_DEVICES); + if (pipeline_dev) + kfree((void *)pipeline_dev); + if (g_fe_priv) + kfree((void *)g_fe_priv); + + pr_notice("virt_ici pipeline device unregistered\n"); +} + +static void __exit virt_ici_exit(void) +{ + virt_ici_stream_exit(); + virt_ici_pipeline_exit(); +} + +module_init(virt_ici_init); +module_exit(virt_ici_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Intel IPU Para virtualize ici input system driver"); +MODULE_AUTHOR("Kushal Bandi "); + + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h new file mode 100644 index 000000000000..f44954b03be2 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef INTEL_IPU4_PARA_VIRT_H +#define INTEL_IPU4_PARA_VIRT_H + +#include +#include +#include +#include +#include +#include +#include + +#include "./ici/ici-isys-stream-device.h" +#include "./ici/ici-isys-frame-buf.h" +#include "intel-ipu4-virtio-common.h" + +#define MAX_STREAM_DEVICES 64 +#define MAX_PIPELINE_DEVICES 1 +#define MAX_ISYS_VIRT_STREAM 34 + +struct virtual_stream { + struct mutex mutex; + struct ici_stream_device strm_dev; + int virt_dev_id; + int actual_fd; + struct ipu4_virtio_ctx *ctx; + struct ici_isys_frame_buf_list buf_list; +}; + + +#define dev_to_vstream(dev) \ + container_of(dev, struct virtual_stream, strm_dev) + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c new file mode 100644 index 000000000000..b88fe6d75bc2 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "intel-ipu4-virtio-be-bridge.h" +#include "./ici/ici-isys-frame-buf.h" +#include "intel-ipu4-virtio-be-pipeline.h" +#include "intel-ipu4-virtio-be-stream.h" + +int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) +{ + int ret = 0; + if (!req) { + pr_err("IPU mediator: request is NULL\n"); + return -EINVAL; + } + if ((req->cmd < IPU4_CMD_DEVICE_OPEN) || + (req->cmd >= IPU4_CMD_GET_N)) { + pr_err("IPU mediator: invalid command\n"); + return -EINVAL; + } + switch (req->cmd) { + case IPU4_CMD_POLL: + /* + * Open video device node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + pr_debug("%s: process_poll pre", __func__); + req->stat = process_poll(domid, req); + pr_debug("%s: process_poll post", __func__); + break; + case IPU4_CMD_DEVICE_OPEN: + /* + * Open video device node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + pr_debug("DEVICE_OPEN: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); + ret = process_device_open(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_DEVICE_CLOSE: + /* + * Close video device node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + pr_debug("DEVICE_CLOSE: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); + ret = process_device_close(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_STREAM_ON: + /* Start Stream + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + pr_debug("STREAM ON: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); + ret = process_stream_on(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_STREAM_OFF: + /* Stop Stream + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + pr_debug("STREAM OFF: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); + ret = process_stream_off(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_GET_BUF: + /* Set Format of a given video node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + * op2 - Memory Type 1: USER_PTR 2: DMA_PTR + * op3 - Number of planes + * op4 - Buffer ID + * op5 - Length of Buffer + */ + + ret = process_get_buf(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_PUT_BUF: + /* Set Format of a given video node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + * op2 - Memory Type 1: USER_PTR 2: DMA_PTR + */ + ret = process_put_buf(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_SET_FORMAT: + ret = process_set_format(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_PIPELINE_OPEN: + ret = process_pipeline_open(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_PIPELINE_CLOSE: + ret = process_pipeline_close(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_ENUM_NODES: + ret = process_enum_nodes(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_ENUM_LINKS: + ret = process_enum_links(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_SETUP_PIPE: + ret = process_setup_pipe(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_SET_FRAMEFMT: + ret = process_set_framefmt(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_GET_FRAMEFMT: + ret = process_get_framefmt(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_GET_SUPPORTED_FRAMEFMT: + ret = process_get_supported_framefmt(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_SET_SELECTION: + ret = process_pad_set_sel(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + case IPU4_CMD_GET_SELECTION: + ret = process_pad_get_sel(domid, req); + if (ret) + req->stat = IPU4_REQ_ERROR; + else + req->stat = IPU4_REQ_PROCESSED; + break; + default: + return -EINVAL; + } + + return ret; +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h new file mode 100644 index 000000000000..25238f29bc33 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_BE_BRIDGE__ +#define __IPU4_VIRTIO_BE_BRIDGE__ + +#include +#include +#include +#include + +#include "intel-ipu4-virtio-common.h" + +int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req); + +void intel_ipu4_virtio_create_req(struct ipu4_virtio_req *req, + enum intel_ipu4_virtio_command cmd, int *op); + +int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req); + + +#endif + + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c new file mode 100644 index 000000000000..3adf5b4c9640 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "intel-ipu4-virtio-be-pipeline.h" +#include "./ici/ici-isys-pipeline.h" +#include "./ici/ici-isys-pipeline-device.h" + +static struct file *pipeline; +static int guestID = -1; + +int process_pipeline_open(int domid, struct ipu4_virtio_req *req) +{ + if (guestID != -1 && guestID != domid) { + pr_err("%s: pipeline device already opened by other guest! %d %d", __func__, guestID, domid); + return -1; + } + + pr_info("process_device_open: /dev/intel_pipeline"); + pipeline = filp_open("/dev/intel_pipeline", O_RDWR | O_NONBLOCK, 0); + guestID = domid; + + return 0; +} + +int process_pipeline_close(int domid, struct ipu4_virtio_req *req) +{ + pr_info("%s: %d", __func__, req->op[0]); + + filp_close(pipeline, 0); + guestID = -1; + + return 0; +} + +int process_enum_nodes(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_node_desc *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_node_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("process_enum_nodes: NULL host_virt"); + return 0; + } + + err = dev->pipeline_ioctl_ops->pipeline_enum_nodes(pipeline, dev, host_virt); + + return err; +} + +int process_enum_links(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_links_query *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_links_query *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pipeline_enum_links(pipeline, dev, host_virt); + + return err; +} +int process_get_supported_framefmt(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_pad_supported_format_desc *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_pad_supported_format_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pad_get_supported_format(pipeline, dev, host_virt); + + return err; +} + +int process_set_framefmt(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_pad_framefmt *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_pad_framefmt *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pad_set_ffmt(pipeline, dev, host_virt); + + return err; +} + +int process_get_framefmt(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_pad_framefmt *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_pad_framefmt *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pad_get_ffmt(pipeline, dev, host_virt); + + return err; +} + +int process_setup_pipe(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_link_desc *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_link_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pipeline_setup_pipe(pipeline, dev, host_virt); + + return err; +} + +int process_pad_set_sel(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_pad_selection *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_pad_selection *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pad_set_sel(pipeline, dev, host_virt); + + return err; +} + +int process_pad_get_sel(int domid, struct ipu4_virtio_req *req) +{ + int err = 0; + struct ici_isys_pipeline_device *dev = pipeline->private_data; + struct ici_pad_selection *host_virt; + + pr_debug("%s\n", __func__); + + host_virt = (struct ici_pad_selection *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("%s: NULL host_virt\n", __func__); + return 0; + } + err = dev->pipeline_ioctl_ops->pad_get_sel(pipeline, dev, host_virt); + + return err; +} + +/* + union isys_ioctl_cmd_args { + struct ici_node_desc node_desc; + struct ici_link_desc link; + struct ici_pad_framefmt pad_prop; + struct ici_pad_supported_format_desc + format_desc; + struct ici_links_query links_query; + struct ici_pad_selection pad_sel; + }; + + .pipeline_setup_pipe = ici_setup_link, + .pipeline_enum_nodes = pipeline_enum_nodes, + .pipeline_enum_links = pipeline_enum_links, + .pad_set_ffmt = ici_pipeline_set_ffmt, + .pad_get_ffmt = ici_pipeline_get_ffmt, + .pad_get_supported_format = + ici_pipeline_get_supported_format, + .pad_set_sel = ici_pipeline_set_sel, + .pad_get_sel = ici_pipeline_get_sel, + +*/ + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h new file mode 100644 index 000000000000..df65e88050ea --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_BE_PIPELINE__ +#define __IPU4_VIRTIO_BE_PIPELINE__ + +#include +#include + +#include "intel-ipu4-virtio-common.h" + +int process_pipeline_open(int domid, struct ipu4_virtio_req *req); +int process_pipeline_close(int domid, struct ipu4_virtio_req *req); +int process_enum_nodes(int domid, struct ipu4_virtio_req *req); +int process_enum_links(int domid, struct ipu4_virtio_req *req); +int process_get_supported_framefmt(int domid, struct ipu4_virtio_req *req); +int process_set_framefmt(int domid, struct ipu4_virtio_req *req); +int process_get_framefmt(int domid, struct ipu4_virtio_req *req); +int process_pad_set_sel(int domid, struct ipu4_virtio_req *req); +int process_pad_get_sel(int domid, struct ipu4_virtio_req *req); +int process_setup_pipe(int domid, struct ipu4_virtio_req *req); + +#endif + + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c new file mode 100644 index 000000000000..9c6bbf6fb5be --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "./ici/ici-isys-stream-device.h" +#include "./ici/ici-isys-stream.h" +#include "./ici/ici-isys-frame-buf.h" +#include "intel-ipu4-virtio-be-stream.h" +#include "intel-ipu4-virtio-be.h" + +#define MAX_SIZE 6 // max 2^6 + +#define dev_to_stream(dev) \ + container_of(dev, struct ici_isys_stream, strm_dev) + +DECLARE_HASHTABLE(STREAM_NODE_HASH, MAX_SIZE); +static bool hash_initialised; + +struct stream_node { + int client_id; + struct file *f; + struct hlist_node node; +}; + +int frame_done_callback(void) +{ + notify_fe(); + return 0; +} + +int process_device_open(int domid, struct ipu4_virtio_req *req) +{ + char node_name[25]; + struct stream_node *sn = NULL; + + if (!hash_initialised) { + hash_init(STREAM_NODE_HASH); + hash_initialised = true; + } + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + if (sn->client_id != domid) { + pr_err("process_device_open: stream device %d already opened by other guest!", sn->client_id); + return -EBUSY; + } + pr_info("process_device_open: stream device %d already opened by client %d", req->op[0], domid); + return 0; + } + } + + sprintf(node_name, "/dev/intel_stream%d", req->op[0]); + pr_info("process_device_open: %s", node_name); + sn = kzalloc(sizeof(struct stream_node), GFP_KERNEL); + sn->f = filp_open(node_name, O_RDWR | O_NONBLOCK, 0); + sn->client_id = domid; + + hash_add(STREAM_NODE_HASH, &sn->node, req->op[0]); + + return 0; +} + +int process_device_close(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + if (!hash_initialised) + return 0; //no node has been opened, do nothing + + pr_info("process_device_close: %d", req->op[0]); + + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_err("process_device_close: %d closed", req->op[0]); + hash_del(&sn->node); + filp_close(sn->f, 0); + kfree(sn); + } + } + + return 0; +} + +int process_set_format(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_stream_device *strm_dev; + struct ici_stream_format *host_virt; + int err, found; + + pr_debug("process_set_format: %d %d", hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = 0; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_err("process_set_format: node %d %p", req->op[0], sn); + found = 1; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + return -1; + } + + host_virt = (struct ici_stream_format *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("process_set_format: NULL host_virt"); + return -1; + } + + err = strm_dev->ipu_ioctl_ops->ici_set_format(sn->f, strm_dev, host_virt); + + if (err) + pr_err("intel_ipu4_pvirt: internal set fmt failed\n"); + + return 0; +} + +int process_poll(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_isys_stream *as; + bool found, empty; + unsigned long flags = 0; + + pr_debug("%s: %d %d", __func__, hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = false; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_debug("process_put_buf: node %d %p", req->op[0], sn); + found = true; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + as = dev_to_stream(sn->f->private_data); + spin_lock_irqsave(&as->buf_list.lock, flags); + empty = list_empty(&as->buf_list.putbuf_list); + spin_unlock_irqrestore(&as->buf_list.lock, flags); + if (!empty) { + req->func_ret = 1; + return IPU4_REQ_PROCESSED; + } else + return IPU4_REQ_NEEDS_FOLLOW_UP; +} + +int process_put_buf(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_stream_device *strm_dev; + struct ici_frame_info *host_virt; + int err, found; + + pr_debug("process_put_buf: %d %d", hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = 0; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_debug("process_put_buf: node %d %p", req->op[0], sn); + found = 1; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + return -1; + } + + host_virt = (struct ici_frame_info *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (host_virt == NULL) { + pr_err("process_put_buf: NULL host_virt"); + return -1; + } + err = strm_dev->ipu_ioctl_ops->ici_put_buf(sn->f, strm_dev, host_virt); + + if (err) + pr_err("process_put_buf: ici_put_buf failed\n"); + + return 0; +} + +int process_get_buf(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_frame_buf_wrapper *shared_buf; + struct ici_stream_device *strm_dev; + int k, i = 0; + void *pageaddr; + u64 *page_table = NULL; + struct page **data_pages = NULL; + int err, found; + + pr_debug("process_get_buf: %d %d", hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = 0; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_debug("process_get_buf: node %d %p", req->op[0], sn); + found = 1; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + pr_debug("GET_BUF: Mapping buffer\n"); + shared_buf = (struct ici_frame_buf_wrapper *)map_guest_phys(domid, req->payload, PAGE_SIZE); + if (!shared_buf) { + pr_err("SOS Failed to map Buffer from UserOS\n"); + req->stat = IPU4_REQ_ERROR; + } + data_pages = kcalloc(shared_buf->kframe_info.planes[0].npages, sizeof(struct page *), GFP_KERNEL); + if (data_pages == NULL) { + pr_err("SOS Failed alloc data page set\n"); + req->stat = IPU4_REQ_ERROR; + } + pr_debug("Total number of pages:%d\n", shared_buf->kframe_info.planes[0].npages); + + page_table = (u64 *)map_guest_phys(domid, shared_buf->kframe_info.planes[0].page_table_ref, PAGE_SIZE); + + if (page_table == NULL) { + pr_err("SOS Failed to map page table\n"); + req->stat = IPU4_REQ_ERROR; + kfree(data_pages); + return -1; + } + + else { + pr_debug("SOS first page %lld\n", page_table[0]); + k = 0; + for (i = 0; i < shared_buf->kframe_info.planes[0].npages; i++) { + pageaddr = map_guest_phys(domid, page_table[i], PAGE_SIZE); + if (pageaddr == NULL) { + pr_err("Cannot map pages from UOS\n"); + req->stat = IPU4_REQ_ERROR; + break; + } + + data_pages[k] = virt_to_page(pageaddr); + k++; + } + } + + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + kfree(data_pages); + return -1; + } + err = strm_dev->ipu_ioctl_ops->ici_get_buf_virt(sn->f, strm_dev, shared_buf, data_pages); + + if (err) + pr_err("process_get_buf: ici_get_buf_virt failed\n"); + + kfree(data_pages); + return 0; +} + +int process_stream_on(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_isys_stream *as; + struct ici_stream_device *strm_dev; + int err, found; + + pr_debug("process_stream_on: %d %d", hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = 0; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_err("process_stream_on: node %d %p", req->op[0], sn); + found = 1; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + return -1; + } + + as = dev_to_stream(strm_dev); + as->frame_done_notify_queue = frame_done_callback; + + err = strm_dev->ipu_ioctl_ops->ici_stream_on(sn->f, strm_dev); + + if (err) + pr_err("process_stream_on: stream on failed\n"); + + return 0; +} + +int process_stream_off(int domid, struct ipu4_virtio_req *req) +{ + struct stream_node *sn = NULL; + struct ici_stream_device *strm_dev; + struct ici_isys_stream *as; + int err, found; + + pr_debug("process_stream_off: %d %d", hash_initialised, req->op[0]); + + if (!hash_initialised) + return -1; + + found = 0; + hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { + if (sn != NULL) { + pr_err("process_stream_off: node %d %p", req->op[0], sn); + found = 1; + break; + } + } + + if (!found) { + pr_debug("%s: stream not found %d\n", __func__, req->op[0]); + return -1; + } + + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + return -1; + } + + err = strm_dev->ipu_ioctl_ops->ici_stream_off(sn->f, strm_dev); + + if (err) + pr_err("process_stream_off: stream off failed\n"); + + as = dev_to_stream(strm_dev); + as->frame_done_notify_queue(); + as->frame_done_notify_queue = NULL; + + return 0; +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h new file mode 100644 index 000000000000..0d85b3561274 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_BE_STREAM__ +#define __IPU4_VIRTIO_BE_STREAM__ + +#include +#include + +#include "intel-ipu4-virtio-common.h" + +int process_set_format(int domid, struct ipu4_virtio_req *req); +int process_device_open(int domid, struct ipu4_virtio_req *req); +int process_device_close(int domid, struct ipu4_virtio_req *req); +int process_poll(int domid, struct ipu4_virtio_req *req); +int process_put_buf(int domid, struct ipu4_virtio_req *req); +int process_stream_on(int domid, struct ipu4_virtio_req *req); +int process_stream_off(int domid, struct ipu4_virtio_req *req); +int process_get_buf(int domid, struct ipu4_virtio_req *req); + + +#endif + + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c new file mode 100644 index 000000000000..aa64d09adb35 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-virtio-be-bridge.h" +#include "intel-ipu4-virtio-be.h" + +/** + * struct ipu4_virtio_be_priv - Backend of virtio-rng based on VBS-K + * + * @dev : instance of struct virtio_dev_info + * @vqs : instances of struct virtio_vq_info + * @hwrng : device specific member + * @node : hashtable maintaining multiple connections + * from multiple guests/devices + */ +struct ipu4_virtio_be_priv { + struct virtio_dev_info dev; + struct virtio_vq_info vqs[IPU_VIRTIO_QUEUE_MAX]; + bool busy; + struct ipu4_virtio_req *pending_tx_req; + struct mutex lock; + /* + * Each VBS-K module might serve multiple connections + * from multiple guests/device models/VBS-Us, so better + * to maintain the connections in a list, and here we + * use hashtable as an example. + */ + struct hlist_node node; +}; + +struct vq_request_data { + struct virtio_vq_info *vq; + struct ipu4_virtio_req *req; + int len; + uint16_t idx; +}; + +struct vq_request_data vq_req; + +#define RNG_MAX_HASH_BITS 4 /* MAX is 2^4 */ +#define HASH_NAME vbs_hash + +DECLARE_HASHTABLE(HASH_NAME, RNG_MAX_HASH_BITS); +static int ipu_vbk_hash_initialized; +static int ipu_vbk_connection_cnt; + +/* function declarations */ +static int handle_kick(int client_id, long unsigned int *req_cnt); +static void ipu_vbk_reset(struct ipu4_virtio_be_priv *rng); +static void ipu_vbk_stop(struct ipu4_virtio_be_priv *rng); +static void ipu_vbk_flush(struct ipu4_virtio_be_priv *rng); + +#ifdef RUNTIME_CTRL +static int ipu_vbk_enable_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq); +static void ipu_vbk_disable_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq); +static void ipu_vbk_stop_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq); +static void ipu_vbk_flush_vq(struct ipu4_virtio_be_priv *rng, int index); +#endif + +/* hash table related functions */ +static void ipu_vbk_hash_init(void) +{ + if (ipu_vbk_hash_initialized) + return; + + hash_init(HASH_NAME); + ipu_vbk_hash_initialized = 1; +} + +static int ipu_vbk_hash_add(struct ipu4_virtio_be_priv *entry) +{ + if (!ipu_vbk_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_add(HASH_NAME, &entry->node, virtio_dev_client_id(&entry->dev)); + return 0; +} + +static struct ipu4_virtio_be_priv *ipu_vbk_hash_find(int client_id) +{ + struct ipu4_virtio_be_priv *entry; + int bkt; + + if (!ipu_vbk_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return NULL; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (virtio_dev_client_id(&entry->dev) == client_id) + return entry; + + pr_err("Not found item matching client_id!\n"); + return NULL; +} + +static int ipu_vbk_hash_del(int client_id) +{ + struct ipu4_virtio_be_priv *entry; + int bkt; + + if (!ipu_vbk_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (virtio_dev_client_id(&entry->dev) == client_id) { + hash_del(&entry->node); + return 0; + } + + pr_err("%s failed, not found matching client_id!\n", + __func__); + return -1; +} + +static int ipu_vbk_hash_del_all(void) +{ + struct ipu4_virtio_be_priv *entry; + int bkt; + + if (!ipu_vbk_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + hash_del(&entry->node); + + return 0; +} + +static void handle_vq_kick(struct ipu4_virtio_be_priv *priv, int vq_idx) +{ + struct iovec iov; + struct ipu4_virtio_be_priv *be; + struct virtio_vq_info *vq; + struct ipu4_virtio_req *req = NULL; + int len; + int ret; + uint16_t idx; + + pr_debug("%s: vq_idx %d\n", __func__, vq_idx); + + be = priv; + + if (!be) { + pr_err("rng is NULL! Cannot proceed!\n"); + return; + } + + vq = &(be->vqs[vq_idx]); + + while (virtio_vq_has_descs(vq)) { + virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + + /* device specific operations, for example: */ + pr_debug("iov base %p len %lx\n", iov.iov_base, iov.iov_len); + + if (iov.iov_len != sizeof(struct ipu4_virtio_req)) { + if (iov.iov_len == sizeof(int)) { + *((int *)iov.iov_base) = 1; + len = iov.iov_len; + printk(KERN_NOTICE "IPU VBK handle kick from vmid:%d\n", 1); + } else { + len = 0; + printk(KERN_WARNING "received request with wrong size"); + printk(KERN_WARNING "%zu != %zu\n", + iov.iov_len, + sizeof(struct ipu4_virtio_req)); + } + + pr_debug("vtrnd: vtrnd_notify(): %d\r\n", len); + virtio_vq_relchain(vq, idx, len); + continue; + } + + req = (struct ipu4_virtio_req *)iov.iov_base; + ret = intel_ipu4_virtio_msg_parse(1, req); + len = iov.iov_len; + + if (req->stat == IPU4_REQ_NEEDS_FOLLOW_UP) { + vq_req.vq = vq; + vq_req.req = req; + vq_req.idx = idx; + vq_req.len = len; + } else + virtio_vq_relchain(vq, idx, len); + } + pr_debug("IPU VBK data process on VQ Done\n"); + if (req && req->stat != IPU4_REQ_NEEDS_FOLLOW_UP) + virtio_vq_endchains(vq, 1); +} + +static int handle_kick(int client_id, long unsigned *ioreqs_map) +{ + int val[IPU_VIRTIO_QUEUE_MAX], i, count; + struct ipu4_virtio_be_priv *priv; + + if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX))) + return -EINVAL; + + pr_debug("%s: IPU VBK handle kick!\n", __func__); + + priv = ipu_vbk_hash_find(client_id); + if (priv == NULL) { + pr_err("%s: client %d not found!\n", + __func__, client_id); + return -EINVAL; + } + + count = ipu_virtio_vqs_index_get(&priv->dev, ioreqs_map, val, IPU_VIRTIO_QUEUE_MAX); + + for (i = 0; i < count; i++) { + if (val[i] >= 0) { + handle_vq_kick(priv, val[i]); + } + } + + return 0; +} + +static int ipu_vbk_open(struct inode *inode, struct file *f) +{ + struct ipu4_virtio_be_priv *priv; + struct virtio_dev_info *dev; + struct virtio_vq_info *vqs; + int i; + + priv = kcalloc(1, sizeof(struct ipu4_virtio_be_priv), + GFP_KERNEL); + + if (priv == NULL) { + pr_err("Failed to allocate memory for ipu4_virtio_be_priv!\n"); + return -ENOMEM; + } + + vqs = &priv->vqs[0]; + + dev = &priv->dev; + + strncpy(dev->name, "vbs_ipu", VBS_NAME_LEN); + dev->dev_notify = handle_kick; + + + for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) { + vqs[i].dev = dev; + vqs[i].vq_notify = NULL; + } + + /* link dev and vqs */ + dev->vqs = vqs; + + virtio_dev_init(dev, vqs, IPU_VIRTIO_QUEUE_MAX); + + priv->pending_tx_req = kcalloc(1, sizeof(struct ipu4_virtio_req), + GFP_KERNEL); + + mutex_init(&priv->lock); + + f->private_data = priv; + + /* init a hash table to maintain multi-connections */ + ipu_vbk_hash_init(); + + return 0; +} + +static int ipu_vbk_release(struct inode *inode, struct file *f) +{ + struct ipu4_virtio_be_priv *priv = f->private_data; + int i; + + if (!priv) + pr_err("%s: UNLIKELY rng NULL!\n", + __func__); + + ipu_vbk_stop(priv); + ipu_vbk_flush(priv); + for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) + virtio_vq_reset(&(priv->vqs[i])); + + /* device specific release */ + ipu_vbk_reset(priv); + + pr_debug("ipu_vbk_connection cnt is %d\n", + ipu_vbk_connection_cnt); + + if (priv && ipu_vbk_connection_cnt--) + ipu_vbk_hash_del(virtio_dev_client_id(&priv->dev)); + if (!ipu_vbk_connection_cnt) { + pr_debug("ipu4_virtio_be_priv remove all hash entries\n"); + ipu_vbk_hash_del_all(); + } + + kfree(priv); + + pr_debug("%s done\n", __func__); + return 0; +} + +static long ipu_vbk_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct ipu4_virtio_be_priv *priv = f->private_data; + void __user *argp = (void __user *)arg; + /*u64 __user *featurep = argp;*/ + /*u64 features;*/ + int r; + + if (priv == NULL) { + pr_err("No IPU backend private data\n"); + return -EINVAL; + } + switch (ioctl) { +/* + * case VHOST_GET_FEATURES: + * features = VHOST_NET_FEATURES; + * if (copy_to_user(featurep, &features, sizeof features)) + * return -EFAULT; + * return 0; + * case VHOST_SET_FEATURES: + * if (copy_from_user(&features, featurep, sizeof features)) + * return -EFAULT; + * if (features & ~VHOST_NET_FEATURES) + * return -EOPNOTSUPP; + * return vhost_net_set_features(n, features); + */ + case VBS_SET_VQ: + /* + * we handle this here because we want to register VHM client + * after handling VBS_K_SET_VQ request + */ + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) { + pr_err("VBS_K_SET_VQ: virtio_vqs_ioctl failed!\n"); + return -EFAULT; + } + /* Register VHM client */ + if (virtio_dev_register(&priv->dev) < 0) { + pr_err("failed to register VHM client!\n"); + return -EFAULT; + } + /* Added to local hash table */ + if (ipu_vbk_hash_add(priv) < 0) { + pr_err("failed to add to hashtable!\n"); + return -EFAULT; + } + /* Increment counter */ + ipu_vbk_connection_cnt++; + return r; + default: + /*mutex_lock(&n->dev.mutex);*/ + r = virtio_dev_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + else + ipu_vbk_flush(priv); + /*mutex_unlock(&n->dev.mutex);*/ + return r; + } +} + +int notify_fe(void) +{ + if (vq_req.vq) { + pr_debug("%s: notifying fe", __func__); + vq_req.req->func_ret = 1; + virtio_vq_relchain(vq_req.vq, vq_req.idx, vq_req.len); + virtio_vq_endchains(vq_req.vq, 1); + vq_req.vq = NULL; + } else + pr_debug("%s: NULL vq!", __func__); + + return 0; +} + +int ipu_virtio_vqs_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map, + int *vqs_index, int max_vqs_index) +{ + int idx = 0; + struct vhm_request *req; + int vcpu; + + if (dev == NULL) { + pr_err("%s: dev is NULL!\n", __func__); + return -EINVAL; + } + + while (idx < max_vqs_index) { + vcpu = find_first_bit(ioreqs_map, dev->_ctx.max_vcpu); + if (vcpu == dev->_ctx.max_vcpu) + break; + req = &dev->_ctx.req_buf[vcpu]; + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && + req->client == dev->_ctx.vhm_client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) { + /* currently we handle kick only, + * so read will return 0 + */ + pr_debug("%s: read request!\n", __func__); + if (dev->io_range_type == PIO_RANGE) + req->reqs.pio_request.value = 0; + else + req->reqs.mmio_request.value = 0; + } else { + pr_debug("%s: write request! type %d\n", + __func__, req->type); + if (dev->io_range_type == PIO_RANGE) + vqs_index[idx++] = req->reqs.pio_request.value; + else + vqs_index[idx++] = req->reqs.mmio_request.value; + } + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); + acrn_ioreq_complete_request(req->client, vcpu); + } + } + + return idx; +} + +/* device specific function to cleanup itself */ +static void ipu_vbk_reset(struct ipu4_virtio_be_priv *rng) +{ +} + +/* device specific function */ +static void ipu_vbk_stop(struct ipu4_virtio_be_priv *rng) +{ + virtio_dev_deregister(&rng->dev); +} + +/* device specific function */ +static void ipu_vbk_flush(struct ipu4_virtio_be_priv *rng) +{ +} + +#ifdef RUNTIME_CTRL +/* device specific function */ +static int ipu_vbk_enable_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq) +{ + return 0; +} + +/* device specific function */ +static void ipu_vbk_disable_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static void ipu_vbk_stop_vq(struct ipu4_virtio_be_priv *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static void ipu_vbk_flush_vq(struct ipu4_virtio_be_priv *rng, int index) +{ +} + +/* Set feature bits in kernel side device */ +static int ipu_vbk_set_features(struct ipu4_virtio_be_priv *rng, u64 features) +{ + return 0; +} +#endif + +static const struct file_operations vbs_fops = { + .owner = THIS_MODULE, + .release = ipu_vbk_release, + .unlocked_ioctl = ipu_vbk_ioctl, + .open = ipu_vbk_open, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_ipu", + .fops = &vbs_fops, +}; + +static int ipu_vbk_init(void) +{ + return misc_register(&vbs_misc); +} +module_init(ipu_vbk_init); + +static void ipu_vbk_exit(void) +{ + misc_deregister(&vbs_misc); +} +module_exit(ipu_vbk_exit); + +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("IPU4 virtio driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h new file mode 100644 index 000000000000..999b543b58f6 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_BE__ +#define __IPU4_VIRTIO_BE__ + +#include + +int notify_fe(void); +int ipu_virtio_vqs_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map, + int *vqs_index, int max_vqs_index); + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c new file mode 100644 index 000000000000..457c6bdf78a8 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include + +#include "intel-ipu4-virtio-common.h" + +DECLARE_HASHTABLE(ipu4_virtio_fe_hash, MAX_ENTRY_FE); + +void ipu4_virtio_fe_table_init(void) +{ + hash_init(ipu4_virtio_fe_hash); +} + +int ipu4_virtio_fe_add(struct ipu4_virtio_fe_info *fe_info) +{ + struct ipu4_virtio_fe_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = fe_info; + + hash_add(ipu4_virtio_fe_hash, &info_entry->node, + info_entry->info->client_id); + + return 0; +} + +struct ipu4_virtio_fe_info *ipu4_virtio_fe_find(int client_id) +{ + struct ipu4_virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(ipu4_virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) + return info_entry->info; + + return NULL; +} + +struct ipu4_virtio_fe_info *ipu4_virtio_fe_find_by_vmid(int vmid) +{ + struct ipu4_virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(ipu4_virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->vmid == vmid) + return info_entry->info; + + return NULL; +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h new file mode 100644 index 000000000000..8b2260b46169 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_COMMON_H__ +#define __IPU4_VIRTIO_COMMON_H__ + + +/* + * CWP uses physicall addresses for memory sharing, + * so size of one page ref will be 64-bits + */ + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(u64)) + +/* Defines size of requests circular buffer */ +#define REQ_RING_SIZE 128 + +#define MAX_NUMBER_OF_OPERANDS 64 + +#define MAX_ENTRY_FE 7 + +enum virio_queue_type { + IPU_VIRTIO_QUEUE_0 = 0, + IPU_VIRTIO_QUEUE_1, + IPU_VIRTIO_QUEUE_MAX +}; + +struct ipu4_virtio_req { + unsigned int req_id; + unsigned int stat; + unsigned int cmd; + unsigned int func_ret; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; + u64 payload; +}; +struct test_payload { + unsigned int data1; + long int data2; + char name[256]; +}; +/*Not used*/ +struct ipu4_virtio_resp { + unsigned int resp_id; + unsigned int stat; + unsigned int cmd; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; +}; + +/*Not used*/ +struct ipu4_virtio_fe_info { + struct ipu4_virtio_be_priv *priv; + int client_id; + int vmid; + int max_vcpu; + struct vhm_request *req_buf; +}; + +/*Not used*/ +struct ipu4_virtio_fe_info_entry { + struct ipu4_virtio_fe_info *info; + struct hlist_node node; +}; + +struct ipu4_bknd_ops { + /* backed initialization routine */ + int (*init)(void); + + /* backed cleanup routine */ + void (*cleanup)(void); + + /* retreiving id of current virtual machine */ + int (*get_vm_id)(void); + + int (*send_req)(int, struct ipu4_virtio_req *, int, int); +}; + +struct ipu4_virtio_ctx { + /* VM(domain) id of current VM instance */ + int domid; + + /* backend ops - hypervisor specific */ + struct ipu4_bknd_ops *bknd_ops; + + /* flag that shows whether backend is initialized */ + bool initialized; + + /* device global lock */ + struct mutex lock; +}; + +enum intel_ipu4_virtio_command { + IPU4_CMD_DEVICE_OPEN = 0x1, + IPU4_CMD_DEVICE_CLOSE, + IPU4_CMD_STREAM_ON, + IPU4_CMD_STREAM_OFF, + IPU4_CMD_GET_BUF, + IPU4_CMD_PUT_BUF, + IPU4_CMD_SET_FORMAT, + IPU4_CMD_ENUM_NODES, + IPU4_CMD_ENUM_LINKS, + IPU4_CMD_SETUP_PIPE, + IPU4_CMD_SET_FRAMEFMT, + IPU4_CMD_GET_FRAMEFMT, + IPU4_CMD_GET_SUPPORTED_FRAMEFMT, + IPU4_CMD_SET_SELECTION, + IPU4_CMD_GET_SELECTION, + IPU4_CMD_POLL, + IPU4_CMD_PIPELINE_OPEN, + IPU4_CMD_PIPELINE_CLOSE, + IPU4_CMD_GET_N +}; + +enum intel_ipu4_virtio_req_feedback { + IPU4_REQ_PROCESSED, + IPU4_REQ_NEEDS_FOLLOW_UP, + IPU4_REQ_ERROR, + IPU4_REQ_NOT_RESPONDED +}; +extern struct ipu4_bknd_ops ipu4_virtio_bknd_ops; + +void ipu4_virtio_fe_table_init(void); + +int ipu4_virtio_fe_add(struct ipu4_virtio_fe_info *fe_info); + +struct ipu4_virtio_fe_info *ipu4_virtio_fe_find(int client_id); + +struct ipu4_virtio_fe_info *ipu4_virtio_fe_find_by_vmid(int vmid); + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.c new file mode 100644 index 000000000000..44edf7414a15 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-virtio-fe-payload.h" + +void intel_ipu4_virtio_create_req(struct ipu4_virtio_req *req, + enum intel_ipu4_virtio_command cmd, int *op) +{ + int i; + + req->stat = IPU4_REQ_NOT_RESPONDED; + req->cmd = cmd; + + switch (cmd) { + case IPU4_CMD_POLL: + case IPU4_CMD_DEVICE_OPEN: + case IPU4_CMD_DEVICE_CLOSE: + case IPU4_CMD_STREAM_ON: + case IPU4_CMD_STREAM_OFF: + case IPU4_CMD_PUT_BUF: + case IPU4_CMD_SET_FORMAT: + case IPU4_CMD_ENUM_NODES: + case IPU4_CMD_ENUM_LINKS: + case IPU4_CMD_SETUP_PIPE: + case IPU4_CMD_SET_FRAMEFMT: + case IPU4_CMD_GET_FRAMEFMT: + case IPU4_CMD_GET_SUPPORTED_FRAMEFMT: + case IPU4_CMD_SET_SELECTION: + case IPU4_CMD_GET_SELECTION: + /* Open video device node + * op0 - virtual device node number + * op1 - Actual device fd. By default set to 0 + */ + for (i = 0; i < 2; i++) + req->op[i] = op[i]; + break; + case IPU4_CMD_GET_BUF: + for (i = 0; i < 3; i++) + req->op[i] = op[i]; + break; + default: + return; + } +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.h new file mode 100644 index 000000000000..173c31a54692 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-payload.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef __IPU4_VIRTIO_FE_PAYLOAD__ +#define __IPU4_VIRTIO_FE_PAYLOAD__ + +#include "intel-ipu4-virtio-common.h" + +void intel_ipu4_virtio_create_req(struct ipu4_virtio_req *req, + enum intel_ipu4_virtio_command cmd, int *op); + +#endif \ No newline at end of file diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c new file mode 100644 index 000000000000..0f5d8b6f83ec --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include "intel-ipu4-virtio-fe-payload.h" +#include "intel-ipu4-virtio-fe-pipeline.h" + +int process_pipeline(struct file *file, struct ipu4_virtio_ctx *fe_priv, + void *data, int cmd) +{ + struct ipu4_virtio_req *req; + int rval = 0; + int op[10]; + + op[0] = 0; + op[1] = 0; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->payload = virt_to_phys(data); + + intel_ipu4_virtio_create_req(req, cmd, &op[0]); + + rval = fe_priv->bknd_ops->send_req(fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_0); + if (rval) { + pr_err("Failed to send request to BE\n"); + kfree(req); + return rval; + } + + kfree(req); + + return rval; +} + diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.h new file mode 100644 index 000000000000..d1fbe106beda --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef IPU4_VIRTIO_FE_PIPELINE_H +#define IPU4_VIRTIO_FE_PIPELINE_H + +#include + +#include "virtio/intel-ipu4-virtio-common.h" + +int process_pipeline(struct file *file, + struct ipu4_virtio_ctx *fe_priv, + void *data, + int cmd); + + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c new file mode 100644 index 000000000000..d95e52a09b32 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "intel-ipu4-virtio-common.h" + +static DEFINE_IDA(index_ida); + +struct ipu4_virtio_uos { + struct virtqueue *vq[IPU_VIRTIO_QUEUE_MAX]; + struct completion have_data; + char name[25]; + unsigned int data_avail; + int index; + bool busy; + int vmid; +}; + +struct completion completion_queue[IPU_VIRTIO_QUEUE_MAX]; + +/* Assuming there will be one FE instance per VM */ +static struct ipu4_virtio_uos *ipu4_virtio_fe; + +static void ipu_virtio_fe_tx_done_vq_0(struct virtqueue *vq) +{ + struct ipu4_virtio_uos *priv = (struct ipu4_virtio_uos *)vq->vdev->priv; + + /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ + if (!virtqueue_get_buf(vq, &priv->data_avail)) + return; + + complete(&completion_queue[0]); + pr_debug("IPU FE:%s vmid:%d TX for VQ 0 done\n", __func__, priv->vmid); +} + +static void ipu_virtio_fe_tx_done_vq_1(struct virtqueue *vq) +{ + struct ipu4_virtio_uos *priv = (struct ipu4_virtio_uos *)vq->vdev->priv; + + /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ + if (!virtqueue_get_buf(vq, &priv->data_avail)) + return; + + complete(&completion_queue[1]); + pr_debug("IPU FE:%s vmid:%d TX for VQ 1 done\n", __func__, priv->vmid); +} + +/* The host will fill any buffer we give it with sweet, sweet randomness. */ +static void ipu_virtio_fe_register_buffer(struct ipu4_virtio_uos *vi, void *buf, size_t size, + int nqueue) +{ + struct scatterlist sg; + if (nqueue >= IPU_VIRTIO_QUEUE_MAX) { + pr_debug("Number of queue exceeding max queue number\n"); + return; + } + + sg_init_one(&sg, buf, size); + + /* There should always be room for one buffer. */ + virtqueue_add_inbuf(vi->vq[nqueue], &sg, 1, buf, GFP_KERNEL); + + virtqueue_kick(vi->vq[nqueue]); +} + +static int ipu_virtio_fe_probe_common(struct virtio_device *vdev) +{ + int err, index, i; + struct ipu4_virtio_uos *priv = NULL; + vq_callback_t *callbacks[] = {ipu_virtio_fe_tx_done_vq_0, + ipu_virtio_fe_tx_done_vq_1}; + static const char *names[] = {"csi_queue_0", "csi_queue_1"}; + priv = kzalloc(sizeof(struct ipu4_virtio_uos), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->index = index = ida_simple_get(&index_ida, 0, 0, GFP_KERNEL); + if (index < 0) { + err = index; + goto err_ida; + } + sprintf(priv->name, "virtio_.%d", index); + for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) + init_completion(&completion_queue[i]); + priv->vmid = -1; + vdev->priv = priv; + err = virtio_find_vqs(vdev, IPU_VIRTIO_QUEUE_MAX, + priv->vq, callbacks, names, NULL); + if (err) + goto err_find; + + ipu4_virtio_fe = priv; + + return 0; + +err_find: + ida_simple_remove(&index_ida, index); +err_ida: + kfree(priv); + return err; +} + +static void ipu_virtio_fe_remove_common(struct virtio_device *vdev) +{ + struct ipu4_virtio_uos *priv = vdev->priv; + int i; + + priv->data_avail = 0; + for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) + complete(&completion_queue[i]); + vdev->config->reset(vdev); + priv->busy = false; + + vdev->config->del_vqs(vdev); + //ida_simple_remove(&index_ida, priv->index); + kfree(priv); +} + +static int ipu_virtio_fe_send_req(int vmid, struct ipu4_virtio_req *req, + int wait, int idx) +{ + struct ipu4_virtio_uos *priv = ipu4_virtio_fe; + int ret = 0; + pr_debug("IPU FE:%s\n", __func__); + if (priv == NULL) { + pr_err("IPU Backend not connected\n"); + return -ENOENT; + } + + init_completion(&completion_queue[idx]); + ipu_virtio_fe_register_buffer(ipu4_virtio_fe, req, sizeof(*req), idx); + wait_for_completion(&completion_queue[idx]); + + return ret; +} +static int ipu_virtio_fe_get_vmid(void) +{ + struct ipu4_virtio_uos *priv = ipu4_virtio_fe; + + if (ipu4_virtio_fe == NULL) { + pr_err("IPU Backend not connected\n"); + return -1; + } + return priv->vmid; +} + +int ipu_virtio_fe_register(void) +{ + pr_debug("IPU FE:%s\n", __func__); + return 0; +} + +void ipu_virtio_fe_unregister(void) +{ + pr_debug("IPU FE:%s\n", __func__); + return; +} +static int virt_probe(struct virtio_device *vdev) +{ + return ipu_virtio_fe_probe_common(vdev); +} + +static void virt_remove(struct virtio_device *vdev) +{ + ipu_virtio_fe_remove_common(vdev); +} + +static void virt_scan(struct virtio_device *vdev) +{ + struct ipu4_virtio_uos *vi = (struct ipu4_virtio_uos *)vdev->priv; + int timeout = 1000; + + if (vi == NULL) { + pr_err("IPU No frontend private data\n"); + return; + } + ipu_virtio_fe_register_buffer(vi, &vi->vmid, sizeof(vi->vmid), + IPU_VIRTIO_QUEUE_0); + + while (timeout--) { + if (vi->vmid > 0) + break; + usleep_range(100, 120); + } + pr_debug("IPU FE:%s vmid:%d\n", __func__, vi->vmid); + + if (timeout < 0) + pr_err("IPU Cannot query vmid\n"); + +} + +#ifdef CONFIG_PM_SLEEP +static int virt_freeze(struct virtio_device *vdev) +{ + ipu_virtio_fe_remove_common(vdev); + return 0; +} + +static int virt_restore(struct virtio_device *vdev) +{ + return ipu_virtio_fe_probe_common(vdev); +} +#endif + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_IPU, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +struct ipu4_bknd_ops ipu4_virtio_bknd_ops = { + .init = ipu_virtio_fe_register, + .cleanup = ipu_virtio_fe_unregister, + .get_vm_id = ipu_virtio_fe_get_vmid, + .send_req = ipu_virtio_fe_send_req +}; + +static struct virtio_driver virtio_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virt_probe, + .remove = virt_remove, + .scan = virt_scan, +#ifdef CONFIG_PM_SLEEP + .freeze = virt_freeze, + .restore = virt_restore, +#endif +}; + + +module_virtio_driver(virtio_driver); +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("IPU4 virtio driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 0a5399baeb5e3733ca832c1db1e2fb10bdbfa02d Mon Sep 17 00:00:00 2001 From: Ong Hock Yu Date: Tue, 2 Oct 2018 08:50:23 +0800 Subject: [PATCH 1151/1276] media: intel-ipu4: [VIRT] Add multi streaming support on guest OS Change-Id: I416c99a032e9f2a9611c43ca917b28e3ff70fe18 Signed-off-by: Ong Hock Yu --- .../media/pci/intel/ici/ici-isys-frame-buf.c | 12 +- drivers/media/pci/intel/ici/ici-isys-stream.h | 1 - drivers/media/pci/intel/virtio/Makefile.virt | 2 + .../intel/virtio/intel-ipu4-para-virt-drv.c | 199 +++++----------- .../intel/virtio/intel-ipu4-para-virt-drv.h | 4 - .../virtio/intel-ipu4-virtio-be-bridge.c | 179 +++++++-------- .../virtio/intel-ipu4-virtio-be-bridge.h | 5 +- .../virtio/intel-ipu4-virtio-be-pipeline.c | 203 +++++++++++++--- .../virtio/intel-ipu4-virtio-be-pipeline.h | 20 +- .../intel-ipu4-virtio-be-request-queue.c | 57 +++++ .../intel-ipu4-virtio-be-request-queue.h | 27 +++ .../virtio/intel-ipu4-virtio-be-stream.c | 216 +++++++++++++----- .../virtio/intel-ipu4-virtio-be-stream.h | 20 +- .../pci/intel/virtio/intel-ipu4-virtio-be.c | 152 ++++++------ .../pci/intel/virtio/intel-ipu4-virtio-be.h | 12 +- .../intel/virtio/intel-ipu4-virtio-common.c | 77 +++++++ .../intel/virtio/intel-ipu4-virtio-common.h | 45 +++- .../virtio/intel-ipu4-virtio-fe-pipeline.c | 7 +- .../intel-ipu4-virtio-fe-request-queue.c | 72 ++++++ .../intel-ipu4-virtio-fe-request-queue.h | 14 ++ .../pci/intel/virtio/intel-ipu4-virtio-fe.c | 65 ++++-- 21 files changed, 914 insertions(+), 475 deletions(-) create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.h create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.c create mode 100644 drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.h diff --git a/drivers/media/pci/intel/ici/ici-isys-frame-buf.c b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c index 8e62c273b35a..bd99ba14a8af 100644 --- a/drivers/media/pci/intel/ici/ici-isys-frame-buf.c +++ b/drivers/media/pci/intel/ici/ici-isys-frame-buf.c @@ -495,6 +495,9 @@ int ici_isys_get_buf_virt(struct ici_isys_stream *as, return 0; } + pr_debug("%s: creating new buf object\n", __func__); + pr_debug("%s: mem.userptr %lu", __func__, + frame_buf->frame_info.frame_planes[0].mem.userptr); buf = frame_buf; @@ -585,6 +588,8 @@ static void frame_buf_done( list_add_tail(&buf->node, &buf_list->putbuf_list); spin_unlock_irqrestore(&buf_list->lock, flags); wake_up_interruptible(&buf_list->wait); + pr_debug("%s: Frame data arrived! %lu", __func__, + buf->frame_info.frame_planes[0].mem.userptr); } void ici_isys_frame_buf_ready(struct ici_isys_pipeline @@ -639,8 +644,6 @@ void ici_isys_frame_buf_ready(struct ici_isys_pipeline } else { buf->frame_info.field = ICI_FIELD_NONE; frame_buf_done(buf_list, buf); - if (as->frame_done_notify_queue) - as->frame_done_notify_queue(); } dev_dbg(&isys->adev->dev, "buffer: found buffer %p\n", buf); @@ -720,6 +723,9 @@ int ici_isys_frame_buf_add_next( buf->state = ICI_BUF_ACTIVE; mutex_unlock(&buf_list->mutex); + pr_debug("%s: add buf to FW! %lu", __func__, + buf->frame_info.frame_planes[0].mem.userptr); + css_buf->send_irq_sof = 1; css_buf->output_pins[buf_list->fw_output].addr = (uint32_t)buf->kframe_info.planes[0].dma_addr; @@ -797,8 +803,6 @@ void ici_isys_frame_buf_capture_done( buf->frame_info.field = ip->cur_field; list_del(&buf->node); frame_buf_done(buf_list, buf); - if (as->frame_done_notify_queue) - as->frame_done_notify_queue(); } } } diff --git a/drivers/media/pci/intel/ici/ici-isys-stream.h b/drivers/media/pci/intel/ici/ici-isys-stream.h index 77d89ed2ea79..457b123a65db 100644 --- a/drivers/media/pci/intel/ici/ici-isys-stream.h +++ b/drivers/media/pci/intel/ici/ici-isys-stream.h @@ -50,7 +50,6 @@ struct ici_isys_stream { void (*prepare_firmware_stream_cfg)( struct ici_isys_stream *as, struct ia_css_isys_stream_cfg_data *cfg); - int (*frame_done_notify_queue)(void); }; #define to_intel_ipu4_isys_ici_stream(__buf_list) \ diff --git a/drivers/media/pci/intel/virtio/Makefile.virt b/drivers/media/pci/intel/virtio/Makefile.virt index c3c30c4bf921..75f481b3b71d 100644 --- a/drivers/media/pci/intel/virtio/Makefile.virt +++ b/drivers/media/pci/intel/virtio/Makefile.virt @@ -8,11 +8,13 @@ $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-common.o ifdef CONFIG_VIDEO_INTEL_IPU_VIRTIO_BE + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-request-queue.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-pipeline.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-bridge.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-be-stream.o else + $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe-request-queue.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe-pipeline.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe-payload.o $(TARGET_MODULE)-objs += ../virtio/intel-ipu4-virtio-fe.o diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c index 3f6d541c87fe..23275846e198 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c @@ -18,6 +18,7 @@ #include "intel-ipu4-para-virt-drv.h" #include "intel-ipu4-virtio-fe-pipeline.h" #include "intel-ipu4-virtio-fe-payload.h" +#include "intel-ipu4-virtio-fe-request-queue.h" #include "./ici/ici-isys-stream.h" #include "./ici/ici-isys-pipeline-device.h" @@ -33,8 +34,6 @@ static int stream_dev_init; static struct ipu4_virtio_ctx *g_fe_priv; -struct mutex fop_mutex; - #ifdef CONFIG_COMPAT struct timeval32 { __u32 tv_sec; @@ -219,63 +218,6 @@ static struct ici_frame_buf_wrapper *frame_buf_lookup(struct ici_isys_frame_buf_ } return NULL; } -static void put_userpages(struct ici_kframe_plane *kframe_plane) -{ - struct sg_table *sgt = kframe_plane->sgt; - struct scatterlist *sgl; - unsigned int i; - struct mm_struct *mm = current->active_mm; - - if (!mm) { - pr_err("Failed to get active mm_struct ptr from current process.\n"); - return; - } - - down_read(&mm->mmap_sem); - for_each_sg(sgt->sgl, sgl, sgt->orig_nents, i) { - struct page *page = sg_page(sgl); - - unsigned int npages = PAGE_ALIGN(sgl->offset + sgl->length) >> PAGE_SHIFT; - unsigned int page_no; - - for (page_no = 0; page_no < npages; ++page_no, ++page) { - set_page_dirty_lock(page); - put_page(page); - } - } - - kfree(sgt); - kframe_plane->sgt = NULL; - - up_read(&mm->mmap_sem); -} - -static void put_dma(struct ici_kframe_plane *kframe_plane) -{ - struct sg_table *sgt = kframe_plane->sgt; - - if (WARN_ON(!kframe_plane->db_attach)) { - pr_err("trying to unpin a not attached buffer\n"); - return; - } - - if (WARN_ON(!sgt)) { - pr_err("dmabuf buffer is already unpinned\n"); - return; - } - - if (kframe_plane->kaddr) { - dma_buf_vunmap(kframe_plane->db_attach->dmabuf, - kframe_plane->kaddr); - kframe_plane->kaddr = NULL; - } - dma_buf_unmap_attachment(kframe_plane->db_attach, sgt, - DMA_BIDIRECTIONAL); - - kframe_plane->dma_addr = 0; - kframe_plane->sgt = NULL; - -} static int map_dma(struct device *dev, struct ici_frame_plane *frame_plane, struct ici_kframe_plane *kframe_plane) @@ -332,26 +274,6 @@ static int map_dma(struct device *dev, struct ici_frame_plane *frame_plane, return ret; } -static void unmap_buf(struct ici_frame_buf_wrapper *buf) -{ - int i; - - for (i = 0; i < buf->frame_info.num_planes; i++) { - struct ici_kframe_plane *kframe_plane = - &buf->kframe_info.planes[i]; - switch (kframe_plane->mem_type) { - case ICI_MEM_USERPTR: - put_userpages(kframe_plane); - break; - case ICI_MEM_DMABUF: - put_dma(kframe_plane); - break; - default: - pr_debug("not supported memory type: %d\n", kframe_plane->mem_type); - break; - } - } -} struct ici_frame_buf_wrapper *get_buf(struct virtual_stream *vstream, struct ici_frame_info *frame_info) { int res; @@ -450,7 +372,7 @@ static int virt_isys_set_format(struct file *file, void *fh, pr_debug("Calling Set Format\n"); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; op[0] = vstream->virt_dev_id; @@ -463,10 +385,10 @@ static int virt_isys_set_format(struct file *file, void *fh, rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { dev_err(&strm_dev->dev, "Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -480,7 +402,8 @@ static int virt_isys_stream_on(struct file *file, void *fh) int rval = 0; int op[10]; pr_debug("Calling Stream ON\n"); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; op[0] = vstream->virt_dev_id; @@ -491,14 +414,10 @@ static int virt_isys_stream_on(struct file *file, void *fh) rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { dev_err(&strm_dev->dev, "Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); - - req = kcalloc(1, sizeof(*req), GFP_KERNEL); - if (!req && !fe_ctx) - return -ENOMEM; + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -513,7 +432,7 @@ static int virt_isys_stream_off(struct file *file, void *fh) int op[10]; pr_debug("Calling Stream OFF\n"); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; op[0] = vstream->virt_dev_id; @@ -524,10 +443,10 @@ static int virt_isys_stream_off(struct file *file, void *fh) rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { dev_err(&strm_dev->dev, "Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); buf_stream_cancel(vstream); @@ -545,7 +464,7 @@ static int virt_isys_getbuf(struct file *file, void *fh, int rval = 0; int op[3]; - pr_debug("Calling Get Buffer\n"); + pr_debug("%s stream %d", __func__, vstream->virt_dev_id); buf = get_buf(vstream, user_frame_info); if (!buf) { @@ -553,7 +472,7 @@ static int virt_isys_getbuf(struct file *file, void *fh, return -ENOMEM; } - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -567,10 +486,12 @@ static int virt_isys_getbuf(struct file *file, void *fh, rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { dev_err(&strm_dev->dev, "Failed to Get Buffer\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); + + pr_debug("%s exit stream %d", __func__, vstream->virt_dev_id); return rval; } @@ -585,9 +506,9 @@ static int virt_isys_putbuf(struct file *file, void *fh, int rval = 0; int op[2]; - pr_debug("Calling Put Buffer\n"); + pr_debug("%s stream %d", __func__, vstream->virt_dev_id); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -600,14 +521,12 @@ static int virt_isys_putbuf(struct file *file, void *fh, rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { dev_err(&strm_dev->dev, "Failed to Get Buffer\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); - if (!req && !fe_ctx) - return -ENOMEM; + pr_debug("%s exit stream %d", __func__, vstream->virt_dev_id); return rval; } @@ -624,7 +543,7 @@ static unsigned int stream_fop_poll(struct file *file, struct ici_stream_device dev_dbg(&strm_dev->dev, "stream_fop_poll %d\n", vstream->virt_dev_id); get_device(&dev->dev); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -633,22 +552,16 @@ static unsigned int stream_fop_poll(struct file *file, struct ici_stream_device intel_ipu4_virtio_create_req(req, IPU4_CMD_POLL, &op[0]); - mutex_lock(&fop_mutex); - - rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, + IPU_VIRTIO_QUEUE_0); if (rval) { - mutex_unlock(&fop_mutex); dev_err(&strm_dev->dev, "Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } + ipu4_virtio_fe_req_queue_put(req); - mutex_unlock(&fop_mutex); - - rval = req->func_ret; - kfree(req); - - return rval; + return req->func_ret; } static int virt_stream_fop_open(struct inode *inode, struct file *file) @@ -667,7 +580,7 @@ static int virt_stream_fop_open(struct inode *inode, struct file *file) if (!fe_ctx) return -EINVAL; - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) { dev_err(&strm_dev->dev, "Virtio Req buffer failed\n"); return -ENOMEM; @@ -678,18 +591,14 @@ static int virt_stream_fop_open(struct inode *inode, struct file *file) intel_ipu4_virtio_create_req(req, IPU4_CMD_DEVICE_OPEN, &op[0]); - mutex_lock(&fop_mutex); - - rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, + IPU_VIRTIO_QUEUE_0); if (rval) { - mutex_unlock(&fop_mutex); dev_err(&strm_dev->dev, "Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); - - mutex_unlock(&fop_mutex); + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -705,7 +614,7 @@ static int virt_stream_fop_release(struct inode *inode, struct file *file) pr_debug("%s %d", __func__, vstream->virt_dev_id); put_device(&strm_dev->dev); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -714,18 +623,14 @@ static int virt_stream_fop_release(struct inode *inode, struct file *file) intel_ipu4_virtio_create_req(req, IPU4_CMD_DEVICE_CLOSE, &op[0]); - mutex_lock(&fop_mutex); - - rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, IPU_VIRTIO_QUEUE_1); + rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true, + IPU_VIRTIO_QUEUE_0); if (rval) { - mutex_unlock(&fop_mutex); dev_err(&strm_dev->dev, "Failed to close virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); - - mutex_unlock(&fop_mutex); + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -740,10 +645,7 @@ static unsigned int virt_stream_fop_poll(struct file *file, res = stream_fop_poll(file, as); - //res = POLLIN; - dev_dbg(&as->dev, "virt_stream_fop_poll res %u\n", res); - return res; } @@ -1014,7 +916,7 @@ static int virt_pipeline_fop_open(struct inode *inode, struct file *file) file->private_data = dev; - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -1023,14 +925,14 @@ static int virt_pipeline_fop_open(struct inode *inode, struct file *file) intel_ipu4_virtio_create_req(req, IPU4_CMD_PIPELINE_OPEN, &op[0]); - rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_1); + rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, + IPU_VIRTIO_QUEUE_0); if (rval) { pr_err("Failed to open virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); - + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -1045,7 +947,7 @@ static int virt_pipeline_fop_release(struct inode *inode, struct file *file) put_device(&pipe_dev->dev); - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -1054,13 +956,13 @@ static int virt_pipeline_fop_release(struct inode *inode, struct file *file) intel_ipu4_virtio_create_req(req, IPU4_CMD_PIPELINE_CLOSE, &op[0]); - rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_1); + rval = g_fe_priv->bknd_ops->send_req(g_fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { pr_err("Failed to close virtual device\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } @@ -1271,7 +1173,6 @@ static int __init virt_ici_init(void) if (!vstream) return -ENOMEM; mutex_init(&vstream->mutex); - mutex_init(&fop_mutex); vstream->strm_dev.mutex = &vstream->mutex; rval = virt_frame_buf_init(&vstream->buf_list); @@ -1288,6 +1189,10 @@ static int __init virt_ici_init(void) goto init_fail; } + rval = ipu4_virtio_fe_req_queue_init(); + if (rval) + goto init_fail; + rval = virt_ici_pipeline_init(); if (rval) goto init_fail; @@ -1297,7 +1202,6 @@ static int __init virt_ici_init(void) init_fail: mutex_destroy(&vstream->mutex); - mutex_destroy(&fop_mutex); kfree(vstream); return rval; } @@ -1318,6 +1222,7 @@ static void __exit virt_ici_exit(void) { virt_ici_stream_exit(); virt_ici_pipeline_exit(); + ipu4_virtio_fe_req_queue_free(); } module_init(virt_ici_init); diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h index f44954b03be2..d6d9210d937a 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.h @@ -18,10 +18,6 @@ #include "./ici/ici-isys-frame-buf.h" #include "intel-ipu4-virtio-common.h" -#define MAX_STREAM_DEVICES 64 -#define MAX_PIPELINE_DEVICES 1 -#define MAX_ISYS_VIRT_STREAM 34 - struct virtual_stream { struct mutex mutex; struct ici_stream_device strm_dev; diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c index b88fe6d75bc2..3b5eae651026 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.c @@ -10,15 +10,18 @@ #include #include #include +#include #include "intel-ipu4-virtio-be-bridge.h" #include "./ici/ici-isys-frame-buf.h" #include "intel-ipu4-virtio-be-pipeline.h" #include "intel-ipu4-virtio-be-stream.h" -int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) +int intel_ipu4_virtio_msg_parse(struct ipu4_virtio_req_info *req_info) { int ret = 0; + struct ipu4_virtio_req *req = req_info->request; + if (!req) { pr_err("IPU mediator: request is NULL\n"); return -EINVAL; @@ -28,6 +31,10 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) pr_err("IPU mediator: invalid command\n"); return -EINVAL; } + + if (!req_info) + return -1; + switch (req->cmd) { case IPU4_CMD_POLL: /* @@ -35,9 +42,11 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op0 - virtual device node number * op1 - Actual device fd. By default set to 0 */ - pr_debug("%s: process_poll pre", __func__); - req->stat = process_poll(domid, req); - pr_debug("%s: process_poll post", __func__); + pr_debug("%s: process_poll %d", + __func__, req->op[0]); + kthread_run(process_poll_thread, req_info, + "process_poll"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_DEVICE_OPEN: /* @@ -46,11 +55,9 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op1 - Actual device fd. By default set to 0 */ pr_debug("DEVICE_OPEN: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); - ret = process_device_open(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + kthread_run(process_device_open_thread, req_info, + "process_device_open"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_DEVICE_CLOSE: /* @@ -59,11 +66,9 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op1 - Actual device fd. By default set to 0 */ pr_debug("DEVICE_CLOSE: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); - ret = process_device_close(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + kthread_run(process_device_close_thread, req_info, + "process_device_close"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_STREAM_ON: /* Start Stream @@ -71,11 +76,9 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op1 - Actual device fd. By default set to 0 */ pr_debug("STREAM ON: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); - ret = process_stream_on(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + kthread_run(process_stream_on_thread, req_info, + "process_stream_on"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_STREAM_OFF: /* Stop Stream @@ -83,11 +86,9 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op1 - Actual device fd. By default set to 0 */ pr_debug("STREAM OFF: virtual_dev_id:%d actual_fd:%d\n", req->op[0], req->op[1]); - ret = process_stream_off(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + kthread_run(process_stream_off_thread, req_info, + "process_stream_off"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_GET_BUF: /* Set Format of a given video node @@ -99,11 +100,11 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op5 - Length of Buffer */ - ret = process_get_buf(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_get_buf %d", + __func__, req->op[0]); + kthread_run(process_get_buf_thread, req_info, + "process_get_buf"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_PUT_BUF: /* Set Format of a given video node @@ -111,88 +112,88 @@ int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req) * op1 - Actual device fd. By default set to 0 * op2 - Memory Type 1: USER_PTR 2: DMA_PTR */ - ret = process_put_buf(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_put_buf %d", + __func__, req->op[0]); + kthread_run(process_put_buf_thread, req_info, + "process_put_buf"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_SET_FORMAT: - ret = process_set_format(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_set_format %d", + __func__, req->op[0]); + kthread_run(process_set_format_thread, req_info, + "process_set_format"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_PIPELINE_OPEN: - ret = process_pipeline_open(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_pipeline_open %d", + __func__, req->op[0]); + kthread_run(process_pipeline_open_thread, req_info, + "process_pipeline_open"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_PIPELINE_CLOSE: - ret = process_pipeline_close(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_pipeline_close %d", + __func__, req->op[0]); + kthread_run(process_pipeline_close_thread, req_info, + "process_pipeline_close"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_ENUM_NODES: - ret = process_enum_nodes(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_enum_nodes %d", + __func__, req->op[0]); + kthread_run(process_enum_nodes_thread, req_info, + "process_enum_nodes"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_ENUM_LINKS: - ret = process_enum_links(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_enum_links %d", + __func__, req->op[0]); + kthread_run(process_enum_links_thread, req_info, + "process_enum_links"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_SETUP_PIPE: - ret = process_setup_pipe(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_setup_pipe %d", + __func__, req->op[0]); + kthread_run(process_setup_pipe_thread, req_info, + "process_setup_pipe"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_SET_FRAMEFMT: - ret = process_set_framefmt(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_set_framefmt %d", + __func__, req->op[0]); + kthread_run(process_set_framefmt_thread, req_info, + "process_set_framefmt"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_GET_FRAMEFMT: - ret = process_get_framefmt(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_get_framefmt %d", + __func__, req->op[0]); + kthread_run(process_get_framefmt_thread, req_info, + "process_get_framefmt"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_GET_SUPPORTED_FRAMEFMT: - ret = process_get_supported_framefmt(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_get_supported_framefmt %d", + __func__, req->op[0]); + kthread_run(process_get_supported_framefmt_thread, + req_info, "process_get_supported_framefmt"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_SET_SELECTION: - ret = process_pad_set_sel(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_pad_set_sel %d", + __func__, req->op[0]); + kthread_run(process_pad_set_sel_thread, req_info, + "process_pad_set_sel"); + req->stat = IPU4_REQ_PENDING; break; case IPU4_CMD_GET_SELECTION: - ret = process_pad_get_sel(domid, req); - if (ret) - req->stat = IPU4_REQ_ERROR; - else - req->stat = IPU4_REQ_PROCESSED; + pr_debug("%s process_pad_get_sel %d", + __func__, req->op[0]); + kthread_run(process_pad_get_sel_thread, req_info, + "process_pad_get_sel"); + req->stat = IPU4_REQ_PENDING; break; default: return -EINVAL; diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h index 25238f29bc33..18085882de85 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-bridge.h @@ -12,14 +12,13 @@ #include #include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-virtio-be-request-queue.h" -int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req); +int intel_ipu4_virtio_msg_parse(struct ipu4_virtio_req_info *req_info); void intel_ipu4_virtio_create_req(struct ipu4_virtio_req *req, enum intel_ipu4_virtio_command cmd, int *op); -int intel_ipu4_virtio_msg_parse(int domid, struct ipu4_virtio_req *req); - #endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c index 3adf5b4c9640..086421151f28 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c @@ -14,176 +14,321 @@ #include "intel-ipu4-virtio-be-pipeline.h" #include "./ici/ici-isys-pipeline.h" #include "./ici/ici-isys-pipeline-device.h" +#include "intel-ipu4-virtio-be.h" +#include "intel-ipu4-virtio-be-request-queue.h" static struct file *pipeline; static int guestID = -1; -int process_pipeline_open(int domid, struct ipu4_virtio_req *req) +int process_pipeline_open(struct ipu4_virtio_req_info *req_info) { + int domid = req_info->domid; if (guestID != -1 && guestID != domid) { pr_err("%s: pipeline device already opened by other guest! %d %d", __func__, guestID, domid); - return -1; + return IPU4_REQ_ERROR; } pr_info("process_device_open: /dev/intel_pipeline"); pipeline = filp_open("/dev/intel_pipeline", O_RDWR | O_NONBLOCK, 0); guestID = domid; - return 0; + return IPU4_REQ_PROCESSED; } -int process_pipeline_close(int domid, struct ipu4_virtio_req *req) +int process_pipeline_close(struct ipu4_virtio_req_info *req_info) { + struct ipu4_virtio_req *req = req_info->request; + pr_info("%s: %d", __func__, req->op[0]); filp_close(pipeline, 0); guestID = -1; - return 0; + return IPU4_REQ_PROCESSED; } -int process_enum_nodes(int domid, struct ipu4_virtio_req *req) +int process_enum_nodes(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_node_desc *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_node_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("process_enum_nodes: NULL host_virt"); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pipeline_enum_nodes(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_enum_links(int domid, struct ipu4_virtio_req *req) +int process_enum_links(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_links_query *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_links_query *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pipeline_enum_links(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_get_supported_framefmt(int domid, struct ipu4_virtio_req *req) +int process_get_supported_framefmt(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_pad_supported_format_desc *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_pad_supported_format_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pad_get_supported_format(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_set_framefmt(int domid, struct ipu4_virtio_req *req) +int process_set_framefmt(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_pad_framefmt *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_pad_framefmt *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pad_set_ffmt(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_get_framefmt(int domid, struct ipu4_virtio_req *req) +int process_get_framefmt(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_pad_framefmt *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_pad_framefmt *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pad_get_ffmt(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_setup_pipe(int domid, struct ipu4_virtio_req *req) +int process_setup_pipe(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_link_desc *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_link_desc *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pipeline_setup_pipe(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_pad_set_sel(int domid, struct ipu4_virtio_req *req) +int process_pad_set_sel(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_pad_selection *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_pad_selection *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pad_set_sel(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; } -int process_pad_get_sel(int domid, struct ipu4_virtio_req *req) +int process_pad_get_sel(struct ipu4_virtio_req_info *req_info) { int err = 0; struct ici_isys_pipeline_device *dev = pipeline->private_data; struct ici_pad_selection *host_virt; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("%s\n", __func__); host_virt = (struct ici_pad_selection *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("%s: NULL host_virt\n", __func__); - return 0; + return IPU4_REQ_ERROR; } err = dev->pipeline_ioctl_ops->pad_get_sel(pipeline, dev, host_virt); - return err; + if (err) + return IPU4_REQ_ERROR; + else + return IPU4_REQ_PROCESSED; +} + +int process_pipeline_open_thread(void *data) +{ + int status; + + status = process_pipeline_open(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_pipeline_close_thread(void *data) +{ + int status; + + status = process_pipeline_close(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_enum_nodes_thread(void *data) +{ + int status; + + status = process_enum_nodes(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_enum_links_thread(void *data) +{ + int status; + + status = process_enum_links(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_get_supported_framefmt_thread(void *data) +{ + int status; + + status = process_get_supported_framefmt(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_set_framefmt_thread(void *data) +{ + int status; + + status = process_set_framefmt(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_get_framefmt_thread(void *data) +{ + int status; + + status = process_get_framefmt(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_pad_set_sel_thread(void *data) +{ + int status; + + status = process_pad_set_sel(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_pad_get_sel_thread(void *data) +{ + int status; + + status = process_pad_get_sel(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_setup_pipe_thread(void *data) +{ + int status; + + status = process_setup_pipe(data); + notify_fe(status, data); + do_exit(0); + return 0; } /* diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h index df65e88050ea..3da8c243a2bc 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.h @@ -11,16 +11,16 @@ #include "intel-ipu4-virtio-common.h" -int process_pipeline_open(int domid, struct ipu4_virtio_req *req); -int process_pipeline_close(int domid, struct ipu4_virtio_req *req); -int process_enum_nodes(int domid, struct ipu4_virtio_req *req); -int process_enum_links(int domid, struct ipu4_virtio_req *req); -int process_get_supported_framefmt(int domid, struct ipu4_virtio_req *req); -int process_set_framefmt(int domid, struct ipu4_virtio_req *req); -int process_get_framefmt(int domid, struct ipu4_virtio_req *req); -int process_pad_set_sel(int domid, struct ipu4_virtio_req *req); -int process_pad_get_sel(int domid, struct ipu4_virtio_req *req); -int process_setup_pipe(int domid, struct ipu4_virtio_req *req); +int process_pipeline_open_thread(void *data); +int process_pipeline_close_thread(void *data); +int process_enum_nodes_thread(void *data); +int process_enum_links_thread(void *data); +int process_get_supported_framefmt_thread(void *data); +int process_set_framefmt_thread(void *data); +int process_get_framefmt_thread(void *data); +int process_pad_set_sel_thread(void *data); +int process_pad_get_sel_thread(void *data); +int process_setup_pipe_thread(void *data); #endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.c new file mode 100644 index 000000000000..cee9b55518c1 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-virtio-be-request-queue.h" + +struct ipu4_virtio_ring ipu4_virtio_be_req_queue; + +int ipu4_virtio_be_req_queue_init(void) +{ + int i; + struct ipu4_virtio_req_info *req; + + if (ipu4_virtio_ring_init(&ipu4_virtio_be_req_queue, REQ_RING_SIZE)) + return -1; + + for (i = 0; i < REQ_RING_SIZE; i++) { + req = kcalloc(1, sizeof(struct ipu4_virtio_req_info), GFP_KERNEL); + if (req == NULL) { + pr_err("%s failed to allocate memory for ipu4_virtio_req_info", + __func__); + return -1; + } + ipu4_virtio_ring_push(&ipu4_virtio_be_req_queue, req); + } + return 0; +} + +void ipu4_virtio_be_req_queue_free(void) +{ + int i; + struct ipu4_virtio_req_info *req_info; + + for (i = 0; i < REQ_RING_SIZE; i++) { + req_info = ipu4_virtio_ring_pop(&ipu4_virtio_be_req_queue); + if (req_info) + kfree(req_info); + else + break; + } + ipu4_virtio_ring_free(&ipu4_virtio_be_req_queue); +} + +struct ipu4_virtio_req_info *ipu4_virtio_be_req_queue_get(void) +{ + return ipu4_virtio_ring_pop(&ipu4_virtio_be_req_queue); +} + +int ipu4_virtio_be_req_queue_put( + struct ipu4_virtio_req_info *req) +{ + return ipu4_virtio_ring_push(&ipu4_virtio_be_req_queue, req); +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.h new file mode 100644 index 000000000000..febcf73152e2 --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-request-queue.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef IPU4_VIRTIO_FE_REQUEST_QUEUE_H +#define IPU4_VIRTIO_FE_REQUEST_QUEUE_H + +struct ipu4_virtio_vq_info { + int vq_idx; + int req_len; + uint16_t vq_buf_idx; +}; + +struct ipu4_virtio_req_info { + struct ipu4_virtio_req *request; + struct ipu4_virtio_vq_info vq_info; + int domid; + int client_id; +}; + +int ipu4_virtio_be_req_queue_init(void); +void ipu4_virtio_be_req_queue_free(void); +struct ipu4_virtio_req_info *ipu4_virtio_be_req_queue_get(void); +int ipu4_virtio_be_req_queue_put(struct ipu4_virtio_req_info *req); + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c index 9c6bbf6fb5be..c3257ea0dbd1 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c @@ -8,9 +8,9 @@ #include #include #include - #include #include + #include #include #include "./ici/ici-isys-stream-device.h" @@ -20,6 +20,7 @@ #include "intel-ipu4-virtio-be.h" #define MAX_SIZE 6 // max 2^6 +#define POLL_WAIT 500 //500ms #define dev_to_stream(dev) \ container_of(dev, struct ici_isys_stream, strm_dev) @@ -33,16 +34,13 @@ struct stream_node { struct hlist_node node; }; -int frame_done_callback(void) -{ - notify_fe(); - return 0; -} - -int process_device_open(int domid, struct ipu4_virtio_req *req) +int process_device_open(struct ipu4_virtio_req_info *req_info) { char node_name[25]; struct stream_node *sn = NULL; + struct ici_stream_device *strm_dev; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; if (!hash_initialised) { hash_init(STREAM_NODE_HASH); @@ -52,10 +50,10 @@ int process_device_open(int domid, struct ipu4_virtio_req *req) if (sn != NULL) { if (sn->client_id != domid) { pr_err("process_device_open: stream device %d already opened by other guest!", sn->client_id); - return -EBUSY; + return IPU4_REQ_ERROR; } pr_info("process_device_open: stream device %d already opened by client %d", req->op[0], domid); - return 0; + return IPU4_REQ_PROCESSED; } } @@ -63,44 +61,54 @@ int process_device_open(int domid, struct ipu4_virtio_req *req) pr_info("process_device_open: %s", node_name); sn = kzalloc(sizeof(struct stream_node), GFP_KERNEL); sn->f = filp_open(node_name, O_RDWR | O_NONBLOCK, 0); - sn->client_id = domid; + strm_dev = sn->f->private_data; + if (strm_dev == NULL) { + pr_err("Native IPU stream device not found\n"); + return IPU4_REQ_ERROR; + } + strm_dev->virt_dev_id = req->op[0]; + + sn->client_id = domid; hash_add(STREAM_NODE_HASH, &sn->node, req->op[0]); - return 0; + return IPU4_REQ_PROCESSED; } -int process_device_close(int domid, struct ipu4_virtio_req *req) +int process_device_close(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; + struct ipu4_virtio_req *req = req_info->request; + if (!hash_initialised) - return 0; //no node has been opened, do nothing + return IPU4_REQ_PROCESSED; //no node has been opened, do nothing pr_info("process_device_close: %d", req->op[0]); hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { if (sn != NULL) { - pr_err("process_device_close: %d closed", req->op[0]); hash_del(&sn->node); filp_close(sn->f, 0); kfree(sn); } } - return 0; + return IPU4_REQ_PROCESSED; } -int process_set_format(int domid, struct ipu4_virtio_req *req) +int process_set_format(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_stream_device *strm_dev; struct ici_stream_format *host_virt; int err, found; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("process_set_format: %d %d", hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = 0; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { @@ -113,19 +121,19 @@ int process_set_format(int domid, struct ipu4_virtio_req *req) if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } strm_dev = sn->f->private_data; if (strm_dev == NULL) { pr_err("Native IPU stream device not found\n"); - return -1; + return IPU4_REQ_ERROR; } host_virt = (struct ici_stream_format *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("process_set_format: NULL host_virt"); - return -1; + return IPU4_REQ_ERROR; } err = strm_dev->ipu_ioctl_ops->ici_set_format(sn->f, strm_dev, host_virt); @@ -133,33 +141,33 @@ int process_set_format(int domid, struct ipu4_virtio_req *req) if (err) pr_err("intel_ipu4_pvirt: internal set fmt failed\n"); - return 0; + return IPU4_REQ_PROCESSED; } -int process_poll(int domid, struct ipu4_virtio_req *req) +int process_poll(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_isys_stream *as; bool found, empty; unsigned long flags = 0; + struct ipu4_virtio_req *req = req_info->request; + int time_remain; pr_debug("%s: %d %d", __func__, hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = false; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { if (sn != NULL) { - pr_debug("process_put_buf: node %d %p", req->op[0], sn); found = true; break; } } - if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } as = dev_to_stream(sn->f->private_data); @@ -168,22 +176,37 @@ int process_poll(int domid, struct ipu4_virtio_req *req) spin_unlock_irqrestore(&as->buf_list.lock, flags); if (!empty) { req->func_ret = 1; + pr_debug("%s: done", __func__); return IPU4_REQ_PROCESSED; - } else - return IPU4_REQ_NEEDS_FOLLOW_UP; + } else { + time_remain = wait_event_interruptible_timeout( + as->buf_list.wait, + !list_empty(&as->buf_list.putbuf_list), + POLL_WAIT); + if (time_remain) { + req->func_ret = 1; + return IPU4_REQ_PROCESSED; + } else { + pr_err("%s poll timeout! %d", __func__, req->op[0]); + req->func_ret = 0; + return IPU4_REQ_ERROR; + } + } } -int process_put_buf(int domid, struct ipu4_virtio_req *req) +int process_put_buf(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_stream_device *strm_dev; struct ici_frame_info *host_virt; int err, found; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("process_put_buf: %d %d", hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = 0; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { @@ -196,29 +219,29 @@ int process_put_buf(int domid, struct ipu4_virtio_req *req) if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } strm_dev = sn->f->private_data; if (strm_dev == NULL) { pr_err("Native IPU stream device not found\n"); - return -1; + return IPU4_REQ_ERROR; } host_virt = (struct ici_frame_info *)map_guest_phys(domid, req->payload, PAGE_SIZE); if (host_virt == NULL) { pr_err("process_put_buf: NULL host_virt"); - return -1; + return IPU4_REQ_ERROR; } err = strm_dev->ipu_ioctl_ops->ici_put_buf(sn->f, strm_dev, host_virt); if (err) pr_err("process_put_buf: ici_put_buf failed\n"); - return 0; + return IPU4_REQ_PROCESSED; } -int process_get_buf(int domid, struct ipu4_virtio_req *req) +int process_get_buf(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_frame_buf_wrapper *shared_buf; @@ -228,11 +251,13 @@ int process_get_buf(int domid, struct ipu4_virtio_req *req) u64 *page_table = NULL; struct page **data_pages = NULL; int err, found; + struct ipu4_virtio_req *req = req_info->request; + int domid = req_info->domid; pr_debug("process_get_buf: %d %d", hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = 0; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { @@ -245,7 +270,7 @@ int process_get_buf(int domid, struct ipu4_virtio_req *req) if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } pr_debug("GET_BUF: Mapping buffer\n"); @@ -267,7 +292,7 @@ int process_get_buf(int domid, struct ipu4_virtio_req *req) pr_err("SOS Failed to map page table\n"); req->stat = IPU4_REQ_ERROR; kfree(data_pages); - return -1; + return IPU4_REQ_ERROR; } else { @@ -290,7 +315,7 @@ int process_get_buf(int domid, struct ipu4_virtio_req *req) if (strm_dev == NULL) { pr_err("Native IPU stream device not found\n"); kfree(data_pages); - return -1; + return IPU4_REQ_ERROR; } err = strm_dev->ipu_ioctl_ops->ici_get_buf_virt(sn->f, strm_dev, shared_buf, data_pages); @@ -298,20 +323,21 @@ int process_get_buf(int domid, struct ipu4_virtio_req *req) pr_err("process_get_buf: ici_get_buf_virt failed\n"); kfree(data_pages); - return 0; + return IPU4_REQ_PROCESSED; } -int process_stream_on(int domid, struct ipu4_virtio_req *req) +int process_stream_on(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_isys_stream *as; struct ici_stream_device *strm_dev; int err, found; + struct ipu4_virtio_req *req = req_info->request; pr_debug("process_stream_on: %d %d", hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = 0; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { @@ -324,37 +350,34 @@ int process_stream_on(int domid, struct ipu4_virtio_req *req) if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } strm_dev = sn->f->private_data; if (strm_dev == NULL) { pr_err("Native IPU stream device not found\n"); - return -1; + return IPU4_REQ_ERROR; } - as = dev_to_stream(strm_dev); - as->frame_done_notify_queue = frame_done_callback; - err = strm_dev->ipu_ioctl_ops->ici_stream_on(sn->f, strm_dev); if (err) pr_err("process_stream_on: stream on failed\n"); - return 0; + return IPU4_REQ_PROCESSED; } -int process_stream_off(int domid, struct ipu4_virtio_req *req) +int process_stream_off(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; struct ici_stream_device *strm_dev; - struct ici_isys_stream *as; int err, found; + struct ipu4_virtio_req *req = req_info->request; pr_debug("process_stream_off: %d %d", hash_initialised, req->op[0]); if (!hash_initialised) - return -1; + return IPU4_REQ_ERROR; found = 0; hash_for_each_possible(STREAM_NODE_HASH, sn, node, req->op[0]) { @@ -367,23 +390,100 @@ int process_stream_off(int domid, struct ipu4_virtio_req *req) if (!found) { pr_debug("%s: stream not found %d\n", __func__, req->op[0]); - return -1; + return IPU4_REQ_ERROR; } strm_dev = sn->f->private_data; if (strm_dev == NULL) { pr_err("Native IPU stream device not found\n"); - return -1; + return IPU4_REQ_ERROR; } err = strm_dev->ipu_ioctl_ops->ici_stream_off(sn->f, strm_dev); if (err) - pr_err("process_stream_off: stream off failed\n"); + pr_err("%s: stream off failed\n", + __func__); + + return IPU4_REQ_PROCESSED; +} - as = dev_to_stream(strm_dev); - as->frame_done_notify_queue(); - as->frame_done_notify_queue = NULL; +int process_set_format_thread(void *data) +{ + int status; + + status = process_set_format(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_device_open_thread(void *data) +{ + int status; + + status = process_device_open(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_device_close_thread(void *data) +{ + int status; + + status = process_device_close(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_poll_thread(void *data) +{ + int status; + + status = process_poll(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_put_buf_thread(void *data) +{ + int status; + + status = process_put_buf(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_stream_on_thread(void *data) +{ + int status; + + status = process_stream_on(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_stream_off_thread(void *data) +{ + int status; + + status = process_stream_off(data); + notify_fe(status, data); + do_exit(0); + return 0; +} + +int process_get_buf_thread(void *data) +{ + int status; + status = process_get_buf(data); + notify_fe(status, data); + do_exit(0); return 0; } diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h index 0d85b3561274..04a84a4c365a 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.h @@ -10,16 +10,16 @@ #include #include "intel-ipu4-virtio-common.h" - -int process_set_format(int domid, struct ipu4_virtio_req *req); -int process_device_open(int domid, struct ipu4_virtio_req *req); -int process_device_close(int domid, struct ipu4_virtio_req *req); -int process_poll(int domid, struct ipu4_virtio_req *req); -int process_put_buf(int domid, struct ipu4_virtio_req *req); -int process_stream_on(int domid, struct ipu4_virtio_req *req); -int process_stream_off(int domid, struct ipu4_virtio_req *req); -int process_get_buf(int domid, struct ipu4_virtio_req *req); - +#include "intel-ipu4-virtio-be-request-queue.h" + +int process_set_format_thread(void *data); +int process_device_open_thread(void *data); +int process_device_close_thread(void *data); +int process_poll_thread(void *data); +int process_put_buf_thread(void *data); +int process_stream_on_thread(void *data); +int process_stream_off_thread(void *data); +int process_get_buf_thread(void *data); #endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c index aa64d09adb35..4359d7b99c70 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c @@ -17,6 +17,7 @@ #include "intel-ipu4-virtio-common.h" #include "intel-ipu4-virtio-be-bridge.h" #include "intel-ipu4-virtio-be.h" +#include "intel-ipu4-virtio-be-request-queue.h" /** * struct ipu4_virtio_be_priv - Backend of virtio-rng based on VBS-K @@ -31,8 +32,8 @@ struct ipu4_virtio_be_priv { struct virtio_dev_info dev; struct virtio_vq_info vqs[IPU_VIRTIO_QUEUE_MAX]; bool busy; - struct ipu4_virtio_req *pending_tx_req; - struct mutex lock; + struct mutex mlock; + spinlock_t slock; /* * Each VBS-K module might serve multiple connections * from multiple guests/device models/VBS-Us, so better @@ -42,22 +43,12 @@ struct ipu4_virtio_be_priv { struct hlist_node node; }; -struct vq_request_data { - struct virtio_vq_info *vq; - struct ipu4_virtio_req *req; - int len; - uint16_t idx; -}; - -struct vq_request_data vq_req; - #define RNG_MAX_HASH_BITS 4 /* MAX is 2^4 */ #define HASH_NAME vbs_hash DECLARE_HASHTABLE(HASH_NAME, RNG_MAX_HASH_BITS); static int ipu_vbk_hash_initialized; static int ipu_vbk_connection_cnt; - /* function declarations */ static int handle_kick(int client_id, long unsigned int *req_cnt); static void ipu_vbk_reset(struct ipu4_virtio_be_priv *rng); @@ -150,19 +141,23 @@ static int ipu_vbk_hash_del_all(void) return 0; } -static void handle_vq_kick(struct ipu4_virtio_be_priv *priv, int vq_idx) +static void handle_vq_kick(int client_id, int vq_idx) { struct iovec iov; struct ipu4_virtio_be_priv *be; struct virtio_vq_info *vq; + struct ipu4_virtio_req_info *req_info = NULL; struct ipu4_virtio_req *req = NULL; int len; int ret; uint16_t idx; - pr_debug("%s: vq_idx %d\n", __func__, vq_idx); - - be = priv; + be = ipu_vbk_hash_find(client_id); + if (be == NULL) { + pr_err("%s: client %d not found!\n", + __func__, client_id); + return -EINVAL; + } if (!be) { pr_err("rng is NULL! Cannot proceed!\n"); @@ -174,9 +169,9 @@ static void handle_vq_kick(struct ipu4_virtio_be_priv *priv, int vq_idx) while (virtio_vq_has_descs(vq)) { virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + pr_debug("%s: vq index: %d vq buf index: %d req ptr: %lu\n", + __func__, vq_idx, idx, iov.iov_base); /* device specific operations, for example: */ - pr_debug("iov base %p len %lx\n", iov.iov_base, iov.iov_len); - if (iov.iov_len != sizeof(struct ipu4_virtio_req)) { if (iov.iov_len == sizeof(int)) { *((int *)iov.iov_base) = 1; @@ -195,20 +190,32 @@ static void handle_vq_kick(struct ipu4_virtio_be_priv *priv, int vq_idx) continue; } - req = (struct ipu4_virtio_req *)iov.iov_base; - ret = intel_ipu4_virtio_msg_parse(1, req); - len = iov.iov_len; + req_info = ipu4_virtio_be_req_queue_get(); + if (req_info) { + req = (struct ipu4_virtio_req *)iov.iov_base; + req_info->request = req; + req_info->vq_info.req_len = iov.iov_len; + req_info->vq_info.vq_buf_idx = idx; + req_info->vq_info.vq_idx = vq_idx; + req_info->domid = 1; + req_info->client_id = client_id; + ret = intel_ipu4_virtio_msg_parse(req_info); + } else { + pr_err("%s: Failed to get request buffer from queue!", __func__); + virtio_vq_relchain(vq, idx, iov.iov_len); + continue; + } - if (req->stat == IPU4_REQ_NEEDS_FOLLOW_UP) { - vq_req.vq = vq; - vq_req.req = req; - vq_req.idx = idx; - vq_req.len = len; - } else - virtio_vq_relchain(vq, idx, len); + if (req->stat != IPU4_REQ_PENDING) { + virtio_vq_relchain(vq, idx, iov.iov_len); + ipu4_virtio_be_req_queue_put(req_info); + } + pr_debug("%s ending request for stream %d", + __func__, req->op[0]); } pr_debug("IPU VBK data process on VQ Done\n"); - if (req && req->stat != IPU4_REQ_NEEDS_FOLLOW_UP) + if ((req == NULL) || (req && req->stat != + IPU4_REQ_PENDING)) virtio_vq_endchains(vq, 1); } @@ -229,11 +236,11 @@ static int handle_kick(int client_id, long unsigned *ioreqs_map) return -EINVAL; } - count = ipu_virtio_vqs_index_get(&priv->dev, ioreqs_map, val, IPU_VIRTIO_QUEUE_MAX); + count = virtio_vqs_index_get(&priv->dev, ioreqs_map, val, IPU_VIRTIO_QUEUE_MAX); for (i = 0; i < count; i++) { if (val[i] >= 0) { - handle_vq_kick(priv, val[i]); + handle_vq_kick(client_id, val[i]); } } @@ -273,16 +280,16 @@ static int ipu_vbk_open(struct inode *inode, struct file *f) virtio_dev_init(dev, vqs, IPU_VIRTIO_QUEUE_MAX); - priv->pending_tx_req = kcalloc(1, sizeof(struct ipu4_virtio_req), - GFP_KERNEL); - - mutex_init(&priv->lock); + mutex_init(&priv->mlock); + spin_lock_init(&priv->slock); f->private_data = priv; /* init a hash table to maintain multi-connections */ ipu_vbk_hash_init(); + ipu4_virtio_be_req_queue_init(); + return 0; } @@ -315,6 +322,8 @@ static int ipu_vbk_release(struct inode *inode, struct file *f) kfree(priv); + ipu4_virtio_be_req_queue_free(); + pr_debug("%s done\n", __func__); return 0; } @@ -381,63 +390,36 @@ static long ipu_vbk_ioctl(struct file *f, unsigned int ioctl, } } -int notify_fe(void) +int notify_fe(int status, struct ipu4_virtio_req_info *req_info) { - if (vq_req.vq) { - pr_debug("%s: notifying fe", __func__); - vq_req.req->func_ret = 1; - virtio_vq_relchain(vq_req.vq, vq_req.idx, vq_req.len); - virtio_vq_endchains(vq_req.vq, 1); - vq_req.vq = NULL; - } else - pr_debug("%s: NULL vq!", __func__); - - return 0; -} + struct virtio_vq_info *vq; + struct ipu4_virtio_be_priv *be; + unsigned long flags = 0; -int ipu_virtio_vqs_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map, - int *vqs_index, int max_vqs_index) -{ - int idx = 0; - struct vhm_request *req; - int vcpu; + pr_debug("%s: notifying fe %d vq idx: %d cmd: %d", + __func__, req_info->request->op[0], + req_info->vq_info.vq_idx, + req_info->request->cmd); - if (dev == NULL) { - pr_err("%s: dev is NULL!\n", __func__); + be = ipu_vbk_hash_find(req_info->client_id); + if (be == NULL) { + pr_err("%s: client %d not found!\n", + __func__, req_info->client_id); return -EINVAL; } - while (idx < max_vqs_index) { - vcpu = find_first_bit(ioreqs_map, dev->_ctx.max_vcpu); - if (vcpu == dev->_ctx.max_vcpu) - break; - req = &dev->_ctx.req_buf[vcpu]; - if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && - req->client == dev->_ctx.vhm_client_id) { - if (req->reqs.pio_request.direction == REQUEST_READ) { - /* currently we handle kick only, - * so read will return 0 - */ - pr_debug("%s: read request!\n", __func__); - if (dev->io_range_type == PIO_RANGE) - req->reqs.pio_request.value = 0; - else - req->reqs.mmio_request.value = 0; - } else { - pr_debug("%s: write request! type %d\n", - __func__, req->type); - if (dev->io_range_type == PIO_RANGE) - vqs_index[idx++] = req->reqs.pio_request.value; - else - vqs_index[idx++] = req->reqs.mmio_request.value; - } - smp_mb(); - atomic_set(&req->processed, REQ_STATE_COMPLETE); - acrn_ioreq_complete_request(req->client, vcpu); - } - } + vq = &(be->vqs[req_info->vq_info.vq_idx]); + + req_info->request->stat = status; - return idx; + spin_lock_irqsave(&be->slock, flags); + virtio_vq_relchain(vq, req_info->vq_info.vq_buf_idx, + req_info->vq_info.req_len); + virtio_vq_endchains(vq, 1); + ipu4_virtio_be_req_queue_put(req_info); + spin_unlock_irqrestore(&be->slock, flags); + + return 0; } /* device specific function to cleanup itself */ diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h index 999b543b58f6..14929bb66b02 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.h @@ -8,8 +8,14 @@ #include -int notify_fe(void); -int ipu_virtio_vqs_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map, - int *vqs_index, int max_vqs_index); +enum poll_status { + IPU4_POLL_PENDING = 0, + IPU4_POLL_AVAILABLE, + IPU4_POLL_STOP, + IPU4_POLL_SLEEP +}; + +int notify_fe(int status, struct ipu4_virtio_req_info *req_info); +void notify_poll_thread(int stream_id, enum poll_status status); #endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c index 457c6bdf78a8..5e3b53c9c6e1 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.c @@ -59,3 +59,80 @@ struct ipu4_virtio_fe_info *ipu4_virtio_fe_find_by_vmid(int vmid) return NULL; } + +int ipu4_virtio_fe_remove(int client_id) +{ + struct ipu4_virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(ipu4_virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} + +int ipu4_virtio_ring_init(struct ipu4_virtio_ring *ring, + int ring_size) +{ + ring->buffer = kcalloc(1, ring_size * sizeof(u64), GFP_KERNEL); + + if (!ring->buffer) { + pr_err("%s: failed to allocate memory!", __func__); + return -ENOMEM; + } + + ring->head = 0; + ring->tail = 0; + ring->used = 0; + ring->ring_size = ring_size; + spin_lock_init(&ring->lock); + + return 0; +} + +void ipu4_virtio_ring_free(struct ipu4_virtio_ring *ring) +{ + kfree(ring->buffer); + ring->buffer = NULL; +} + +int ipu4_virtio_ring_push(struct ipu4_virtio_ring *ring, void *data) +{ + int next; + + if (ring->used == ring->ring_size) {//ring full + pr_err("%s: Ring is full!! %d", __func__, ring->used); + return -1; + } + + next = ring->head + 1; + next %= ring->ring_size; + ring->buffer[ring->head] = (u64)data; + ring->head = next; + ring->used++; + + return 0; +} + +void *ipu4_virtio_ring_pop(struct ipu4_virtio_ring *ring) +{ + int next; + void *data; + + if (ring->used == 0) + return NULL; + + next = ring->tail + 1; + next %= ring->ring_size; + + data = (void *) ring->buffer[ring->tail]; + ring->tail = next; + + ring->used--; + + return data; +} \ No newline at end of file diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h index 8b2260b46169..78f40c3dabad 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common.h @@ -6,20 +6,19 @@ #ifndef __IPU4_VIRTIO_COMMON_H__ #define __IPU4_VIRTIO_COMMON_H__ - /* * CWP uses physicall addresses for memory sharing, * so size of one page ref will be 64-bits */ #define REFS_PER_PAGE (PAGE_SIZE/sizeof(u64)) - /* Defines size of requests circular buffer */ #define REQ_RING_SIZE 128 - #define MAX_NUMBER_OF_OPERANDS 64 - #define MAX_ENTRY_FE 7 +#define MAX_STREAM_DEVICES 64 +#define MAX_PIPELINE_DEVICES 1 +#define MAX_ISYS_VIRT_STREAM 34 enum virio_queue_type { IPU_VIRTIO_QUEUE_0 = 0, @@ -33,6 +32,8 @@ struct ipu4_virtio_req { unsigned int cmd; unsigned int func_ret; unsigned int op[MAX_NUMBER_OF_OPERANDS]; + struct completion wait; + struct list_head node; u64 payload; }; struct test_payload { @@ -114,10 +115,44 @@ enum intel_ipu4_virtio_command { enum intel_ipu4_virtio_req_feedback { IPU4_REQ_PROCESSED, - IPU4_REQ_NEEDS_FOLLOW_UP, + IPU4_REQ_PENDING, IPU4_REQ_ERROR, IPU4_REQ_NOT_RESPONDED }; + +struct ipu4_virtio_ring { + /* Buffer allocated for keeping ring entries */ + u64 *buffer; + + /* Index pointing to next free element in ring */ + int head; + + /* Index pointing to last released element in ring */ + int tail; + + /* Total number of elements that ring can contain */ + int ring_size; + + /* Number of location in ring has been used */ + unsigned int used; + + /* Multi thread sync */ + spinlock_t lock; +}; + +/* Create the ring buffer with given size */ +int ipu4_virtio_ring_init(struct ipu4_virtio_ring *ring, + int ring_size); + +/* Frees the ring buffers */ +void ipu4_virtio_ring_free(struct ipu4_virtio_ring *ring); + +/* Add a buffer to ring */ +int ipu4_virtio_ring_push(struct ipu4_virtio_ring *ring, void *data); + +/* Grab a buffer from ring */ +void *ipu4_virtio_ring_pop(struct ipu4_virtio_ring *ring); + extern struct ipu4_bknd_ops ipu4_virtio_bknd_ops; void ipu4_virtio_fe_table_init(void); diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c index 0f5d8b6f83ec..8122d8177104 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-pipeline.c @@ -11,6 +11,7 @@ #include "intel-ipu4-virtio-fe-payload.h" #include "intel-ipu4-virtio-fe-pipeline.h" +#include "intel-ipu4-virtio-fe-request-queue.h" int process_pipeline(struct file *file, struct ipu4_virtio_ctx *fe_priv, void *data, int cmd) @@ -22,7 +23,7 @@ int process_pipeline(struct file *file, struct ipu4_virtio_ctx *fe_priv, op[0] = 0; op[1] = 0; - req = kcalloc(1, sizeof(*req), GFP_KERNEL); + req = ipu4_virtio_fe_req_queue_get(); if (!req) return -ENOMEM; @@ -33,11 +34,11 @@ int process_pipeline(struct file *file, struct ipu4_virtio_ctx *fe_priv, rval = fe_priv->bknd_ops->send_req(fe_priv->domid, req, true, IPU_VIRTIO_QUEUE_0); if (rval) { pr_err("Failed to send request to BE\n"); - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } - kfree(req); + ipu4_virtio_fe_req_queue_put(req); return rval; } diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.c new file mode 100644 index 000000000000..09294da549ab --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) +/* + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include "intel-ipu4-virtio-common.h" +#include "intel-ipu4-virtio-fe-request-queue.h" + +struct ipu4_virtio_ring ipu4_virtio_fe_req_queue; + +int ipu4_virtio_fe_req_queue_init(void) +{ + int i; + struct ipu4_virtio_req *req; + + if (ipu4_virtio_ring_init(&ipu4_virtio_fe_req_queue, REQ_RING_SIZE)) + return -1; + + for (i = 0; i < REQ_RING_SIZE; i++) { + req = kcalloc(1, sizeof(struct ipu4_virtio_req), GFP_KERNEL); + if (req == NULL) { + pr_err("%s failed to allocate memory for ipu4_virtio_req", + __func__); + return -1; + } + init_completion(&req->wait); + ipu4_virtio_ring_push(&ipu4_virtio_fe_req_queue, req); + } + return 0; +} + +void ipu4_virtio_fe_req_queue_free(void) +{ + int i; + struct ipu4_virtio_req *req; + + for (i = 0; i < REQ_RING_SIZE; i++) { + req = ipu4_virtio_ring_pop(&ipu4_virtio_fe_req_queue); + if (req) + kfree(req); + else + break; + } + ipu4_virtio_ring_free(&ipu4_virtio_fe_req_queue); +} + +struct ipu4_virtio_req *ipu4_virtio_fe_req_queue_get(void) +{ + struct ipu4_virtio_req *req; + unsigned long flags = 0; + + spin_lock_irqsave(&ipu4_virtio_fe_req_queue.lock, flags); + req = ipu4_virtio_ring_pop(&ipu4_virtio_fe_req_queue); + spin_unlock_irqrestore(&ipu4_virtio_fe_req_queue.lock, flags); + if (req) + reinit_completion(&req->wait); + return req; +} + +int ipu4_virtio_fe_req_queue_put( + struct ipu4_virtio_req *req) +{ + unsigned long flags = 0; + int status; + + spin_lock_irqsave(&ipu4_virtio_fe_req_queue.lock, flags); + status = ipu4_virtio_ring_push(&ipu4_virtio_fe_req_queue, req); + spin_unlock_irqrestore(&ipu4_virtio_fe_req_queue.lock, flags); + return status; +} diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.h new file mode 100644 index 000000000000..9a36c99f9b5b --- /dev/null +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe-request-queue.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0) */ +/* + * Copyright (C) 2018 Intel Corporation + */ + +#ifndef IPU4_VIRTIO_FE_REQUEST_QUEUE_H +#define IPU4_VIRTIO_FE_REQUEST_QUEUE_H + +int ipu4_virtio_fe_req_queue_init(void); +void ipu4_virtio_fe_req_queue_free(void); +struct ipu4_virtio_req *ipu4_virtio_fe_req_queue_get(void); +int ipu4_virtio_fe_req_queue_put(struct ipu4_virtio_req *req); + +#endif diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c index d95e52a09b32..29b8b4767f02 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-fe.c @@ -17,40 +17,54 @@ static DEFINE_IDA(index_ida); struct ipu4_virtio_uos { struct virtqueue *vq[IPU_VIRTIO_QUEUE_MAX]; - struct completion have_data; char name[25]; unsigned int data_avail; + spinlock_t lock; int index; bool busy; int vmid; }; -struct completion completion_queue[IPU_VIRTIO_QUEUE_MAX]; - /* Assuming there will be one FE instance per VM */ static struct ipu4_virtio_uos *ipu4_virtio_fe; static void ipu_virtio_fe_tx_done_vq_0(struct virtqueue *vq) { struct ipu4_virtio_uos *priv = (struct ipu4_virtio_uos *)vq->vdev->priv; + struct ipu4_virtio_req *req; + unsigned long flags = 0; + + do { + spin_lock_irqsave(&priv->lock, flags); + req = (struct ipu4_virtio_req *) + virtqueue_get_buf(vq, &priv->data_avail); + spin_unlock_irqrestore(&priv->lock, flags); + if (req != NULL && + priv->data_avail == sizeof(struct ipu4_virtio_req)) { + complete(&req->wait); + } + } while (req != NULL); - /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!virtqueue_get_buf(vq, &priv->data_avail)) - return; - - complete(&completion_queue[0]); pr_debug("IPU FE:%s vmid:%d TX for VQ 0 done\n", __func__, priv->vmid); } static void ipu_virtio_fe_tx_done_vq_1(struct virtqueue *vq) { struct ipu4_virtio_uos *priv = (struct ipu4_virtio_uos *)vq->vdev->priv; + struct ipu4_virtio_req *req; + unsigned long flags = 0; + + do { + spin_lock_irqsave(&priv->lock, flags); + req = (struct ipu4_virtio_req *) + virtqueue_get_buf(vq, &priv->data_avail); + spin_unlock_irqrestore(&priv->lock, flags); + if (req != NULL && + priv->data_avail == sizeof(struct ipu4_virtio_req)) { + complete(&req->wait); + } + } while (req != NULL); - /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!virtqueue_get_buf(vq, &priv->data_avail)) - return; - - complete(&completion_queue[1]); pr_debug("IPU FE:%s vmid:%d TX for VQ 1 done\n", __func__, priv->vmid); } @@ -59,6 +73,8 @@ static void ipu_virtio_fe_register_buffer(struct ipu4_virtio_uos *vi, void *buf, int nqueue) { struct scatterlist sg; + unsigned long flags = 0; + if (nqueue >= IPU_VIRTIO_QUEUE_MAX) { pr_debug("Number of queue exceeding max queue number\n"); return; @@ -66,19 +82,25 @@ static void ipu_virtio_fe_register_buffer(struct ipu4_virtio_uos *vi, void *buf, sg_init_one(&sg, buf, size); + spin_lock_irqsave(&vi->lock, flags); /* There should always be room for one buffer. */ virtqueue_add_inbuf(vi->vq[nqueue], &sg, 1, buf, GFP_KERNEL); + spin_unlock_irqrestore(&vi->lock, flags); + virtqueue_kick(vi->vq[nqueue]); } static int ipu_virtio_fe_probe_common(struct virtio_device *vdev) { - int err, index, i; + int err, index; struct ipu4_virtio_uos *priv = NULL; - vq_callback_t *callbacks[] = {ipu_virtio_fe_tx_done_vq_0, + vq_callback_t *callbacks[] = { + ipu_virtio_fe_tx_done_vq_0, ipu_virtio_fe_tx_done_vq_1}; - static const char *names[] = {"csi_queue_0", "csi_queue_1"}; + static const char * const names[] = { + "csi_queue_0", + "csi_queue_1"}; priv = kzalloc(sizeof(struct ipu4_virtio_uos), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -89,8 +111,6 @@ static int ipu_virtio_fe_probe_common(struct virtio_device *vdev) goto err_ida; } sprintf(priv->name, "virtio_.%d", index); - for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) - init_completion(&completion_queue[i]); priv->vmid = -1; vdev->priv = priv; err = virtio_find_vqs(vdev, IPU_VIRTIO_QUEUE_MAX, @@ -98,6 +118,8 @@ static int ipu_virtio_fe_probe_common(struct virtio_device *vdev) if (err) goto err_find; + spin_lock_init(&priv->lock); + ipu4_virtio_fe = priv; return 0; @@ -112,11 +134,8 @@ static int ipu_virtio_fe_probe_common(struct virtio_device *vdev) static void ipu_virtio_fe_remove_common(struct virtio_device *vdev) { struct ipu4_virtio_uos *priv = vdev->priv; - int i; priv->data_avail = 0; - for (i = 0; i < IPU_VIRTIO_QUEUE_MAX; i++) - complete(&completion_queue[i]); vdev->config->reset(vdev); priv->busy = false; @@ -135,10 +154,8 @@ static int ipu_virtio_fe_send_req(int vmid, struct ipu4_virtio_req *req, pr_err("IPU Backend not connected\n"); return -ENOENT; } - - init_completion(&completion_queue[idx]); ipu_virtio_fe_register_buffer(ipu4_virtio_fe, req, sizeof(*req), idx); - wait_for_completion(&completion_queue[idx]); + wait_for_completion(&req->wait); return ret; } From 9f8e820a18b55a156d4cee5bec6fbbf5a83900e5 Mon Sep 17 00:00:00 2001 From: "Yew, Chang Ching" Date: Tue, 23 Oct 2018 17:03:22 +0800 Subject: [PATCH 1152/1276] media: intel-ipu4: Add acpiname for isys sd info ACPI support is still required for ACRN SOS for sensor enumeration Therefore the acpiname field is added back to isys subdev info Signed-off-by: Yew, Chang Ching --- include/media/ipu-isys.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/ipu-isys.h b/include/media/ipu-isys.h index f809ef362152..b2acb94a1fb1 100644 --- a/include/media/ipu-isys.h +++ b/include/media/ipu-isys.h @@ -22,6 +22,7 @@ struct ipu_isys_subdev_i2c_info { struct ipu_isys_subdev_info { struct ipu_isys_csi2_config *csi2; struct ipu_isys_subdev_i2c_info i2c; + char *acpiname; }; struct ipu_isys_clk_mapping { From d5712dad2414f1ac7a7746b8cb06ec0c0aa77a07 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 2 Nov 2018 15:29:29 +0800 Subject: [PATCH 1153/1276] pstore: add license for intel pstore pram driver Add license for intel pstore pram driver. Change-Id: Ie2d3f0fd05f48e0d1b8e092104f7472636306815 Signed-off-by: Tian, Baofeng --- drivers/platform/x86/intel_pstore_pram.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c index 8c8b1291545f..3b94281076c3 100644 --- a/drivers/platform/x86/intel_pstore_pram.c +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -1,5 +1,7 @@ /* + * Intel Pstore Pram Driver: Currently supports APL * Copyright (c) 2015, Intel Corporation. + * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -133,3 +135,8 @@ static void __exit intel_pram_exit(void) kfree(pram_data); } module_exit(intel_pram_exit); + +MODULE_AUTHOR("Baofeng Tian "); +MODULE_DESCRIPTION("Intel Pstore Pram Interface"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); From 5c2d87d89d58b12727b6f8f2b1dc608fc3c51beb Mon Sep 17 00:00:00 2001 From: Ong Hock Yu Date: Sun, 4 Nov 2018 02:30:44 +0000 Subject: [PATCH 1154/1276] media: intel-ipu4: [VIRT] fixed pipeline mutex object used but not being initialized issue. Change-Id: Iebf6a37042fd585d631d064843ad20cce1c0b57c Signed-off-by: Ong Hock Yu --- drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c index 23275846e198..b6278f7c921b 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-drv.c @@ -1158,6 +1158,7 @@ static int virt_ici_pipeline_init(void) pipeline_dev->dev.release = base_device_release; strlcpy(pipeline_dev->name, pipeline_dev->dev.kobj.name, sizeof(pipeline_dev->name)); pipeline_dev->minor = MINOR_PIPELINE; + mutex_init(&pipeline_dev->mutex); return 0; } From 4e5a3a22f960d75b2b7fae6515f8265be51ce745 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 2 Aug 2018 20:17:16 -0700 Subject: [PATCH 1155/1276] usb: dwc3: gadget: Properly check last unaligned/zero chain TRB Current check for the last extra TRB for zero and unaligned transfers does not account for isoc OUT. The last TRB of the Buffer Descriptor for isoc OUT transfers will be retired with HWO=0. As a result, we won't return early. The req->remaining will be updated to include the BUFSIZ count of the extra TRB, and the actual number of transferred bytes calculation will be wrong. To fix this, check whether it's a short or zero packet and the last TRB chain bit to return early. Fixes: c6267a51639b ("usb: dwc3: gadget: align transfers to wMaxPacketSize") Cc: Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index cefd586301c8..bf534fc97096 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2275,7 +2275,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, * with one TRB pending in the ring. We need to manually clear HWO bit * from that TRB. */ - if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) { + if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } From 212e7a56a966414b2f6327bd99b342cf587b152a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 09:37:34 +0300 Subject: [PATCH 1156/1276] usb: dwc3: gadget: fix ISOC TRB type on unaligned transfers When chaining ISOC TRBs together, only the first ISOC TRB should be of type ISOC_FIRST, all others should be of type ISOC. This patch fixes that. Fixes: c6267a51639b ("usb: dwc3: gadget: align transfers to wMaxPacketSize") Cc: # v4.11+ Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index bf534fc97096..b754ce5bfaff 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1073,7 +1073,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, - maxp - rem, false, 0, + maxp - rem, false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); @@ -1117,7 +1117,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, - false, 0, req->request.stream_id, + false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); } else if (req->request.zero && req->request.length && @@ -1133,7 +1133,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to handle ZLP */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - false, 0, req->request.stream_id, + false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); } else { From cabce66f0df85893c8e8be809d9de6d51c15277a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:15:05 +0300 Subject: [PATCH 1157/1276] usb: dwc3: gadget: combine unaligned and zero flags Both flags are used for the same purpose in dwc3: appending an extra TRB at the end to deal with controller requirements. By combining both flags into one, we make it clear that the situation is the same and that they should be treated equally. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 7 +++---- drivers/usb/dwc3/gadget.c | 17 ++++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5bfb62533e0f..4872cba8699b 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -847,11 +847,11 @@ struct dwc3_hwparams { * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb - * @unaligned: true for OUT endpoints with length not divisible by maxp + * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP + * or unaligned OUT) * @direction: IN or OUT direction flag * @mapped: true when request has been dma-mapped * @started: request is started - * @zero: wants a ZLP */ struct dwc3_request { struct usb_request request; @@ -867,11 +867,10 @@ struct dwc3_request { struct dwc3_trb *trb; dma_addr_t trb_dma; - unsigned unaligned:1; + unsigned needs_extra_trb:1; unsigned direction:1; unsigned mapped:1; unsigned started:1; - unsigned zero:1; }; /* diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b754ce5bfaff..d80c4e7323e3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1065,7 +1065,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->unaligned = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, i); @@ -1109,7 +1109,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->unaligned = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, 0); @@ -1125,7 +1125,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - req->zero = true; + req->needs_extra_trb = true; /* prepare normal TRB */ dwc3_prepare_one_trb(dep, req, true, 0); @@ -1407,7 +1407,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, dwc3_ep_inc_deq(dep); } - if (r->unaligned || r->zero) { + if (r->needs_extra_trb) { trb = r->trb + r->num_pending_sgs + 1; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); @@ -1418,7 +1418,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); - if (r->unaligned || r->zero) { + if (r->needs_extra_trb) { trb = r->trb + 1; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); @@ -2275,7 +2275,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, * with one TRB pending in the ring. We need to manually clear HWO bit * from that TRB. */ - if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } @@ -2352,11 +2352,10 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - if (req->unaligned || req->zero) { + if (req->needs_extra_trb) { ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); - req->unaligned = false; - req->zero = false; + req->needs_extra_trb = false; } req->request.actual = req->request.length - req->remaining; From f70e362864a839404163eae21e5d47c457aca304 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:32:07 +0300 Subject: [PATCH 1158/1276] usb: dwc3: gadget: track number of TRBs per request This will help us remove the wait_event() from our ->dequeue(). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/gadget.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4872cba8699b..0de78cb29f2c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -847,6 +847,7 @@ struct dwc3_hwparams { * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb + * @num_trbs: number of TRBs used by this request * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP * or unaligned OUT) * @direction: IN or OUT direction flag @@ -867,6 +868,8 @@ struct dwc3_request { struct dwc3_trb *trb; dma_addr_t trb_dma; + unsigned num_trbs; + unsigned needs_extra_trb:1; unsigned direction:1; unsigned mapped:1; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d80c4e7323e3..4091121d3ff5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1038,6 +1038,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, req->trb_dma = dwc3_trb_dma_offset(dep, trb); } + req->num_trbs++; + __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node, stream_id, short_not_ok, no_interrupt); } @@ -1072,6 +1074,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, false, 1, req->request.stream_id, @@ -1116,6 +1119,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, false, 1, req->request.stream_id, req->request.short_not_ok, @@ -1132,6 +1136,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to handle ZLP */ trb = &dep->trb_pool[dep->trb_enqueue]; + req->num_trbs++; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, false, 1, req->request.stream_id, req->request.short_not_ok, @@ -2256,6 +2261,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, dwc3_ep_inc_deq(dep); trace_dwc3_complete_trb(dep, trb); + req->num_trbs--; /* * If we're in the middle of series of chained TRBs and we From 4a196466aaeb2efcae06cbf4a1eecb86304762be Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:37:53 +0300 Subject: [PATCH 1159/1276] usb: dwc3: gadget: use num_trbs when skipping TRBs on ->dequeue() Now that we track how many TRBs a request uses, it's easier to skip over them in case of a call to usb_ep_dequeue(). Let's do so and simplify the code a bit. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4091121d3ff5..e8efa7447ff6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1365,6 +1365,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, break; } if (r == req) { + int i; + /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); @@ -1402,32 +1404,12 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - if (r->num_pending_sgs) { + for (i = 0; i < r->num_trbs; i++) { struct dwc3_trb *trb; - int i = 0; - - for (i = 0; i < r->num_pending_sgs; i++) { - trb = r->trb + i; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } - - if (r->needs_extra_trb) { - trb = r->trb + r->num_pending_sgs + 1; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } - } else { - struct dwc3_trb *trb = r->trb; + trb = r->trb + i; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); - - if (r->needs_extra_trb) { - trb = r->trb + 1; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } } goto out1; } @@ -1438,8 +1420,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } out1: - /* giveback the request */ - dwc3_gadget_giveback(dep, req, -ECONNRESET); out0: From 861eb7624427f8e5747ac3caaebfa4de87a7d49c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:42:29 +0300 Subject: [PATCH 1160/1276] usb: dwc3: gadget: extract dwc3_gadget_ep_skip_trbs() Extract the logic for skipping over TRBs to its own function. This makes the code slightly more readable and makes it easier to move this call to its final resting place as a following patch. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 61 +++++++++++++++------------------------ 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e8efa7447ff6..e5cc41b3b20d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1338,6 +1338,29 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, return ret; } +static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req) +{ + int i; + + /* + * If request was already started, this means we had to + * stop the transfer. With that we also need to ignore + * all TRBs used by the request, however TRBs can only + * be modified after completion of END_TRANSFER + * command. So what we do here is that we wait for + * END_TRANSFER completion and only after that, we jump + * over TRBs by clearing HWO and incrementing dequeue + * pointer. + */ + for (i = 0; i < req->num_trbs; i++) { + struct dwc3_trb *trb; + + trb = req->trb + i; + trb->ctrl &= ~DWC3_TRB_CTRL_HWO; + dwc3_ep_inc_deq(dep); + } +} + static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request) { @@ -1365,38 +1388,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, break; } if (r == req) { - int i; - /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); - - /* - * If request was already started, this means we had to - * stop the transfer. With that we also need to ignore - * all TRBs used by the request, however TRBs can only - * be modified after completion of END_TRANSFER - * command. So what we do here is that we wait for - * END_TRANSFER completion and only after that, we jump - * over TRBs by clearing HWO and incrementing dequeue - * pointer. - * - * Note that we have 2 possible types of transfers here: - * - * i) Linear buffer request - * ii) SG-list based request - * - * SG-list based requests will have r->num_pending_sgs - * set to a valid number (> 0). Linear requests, - * normally use a single TRB. - * - * For each of these two cases, if r->unaligned flag is - * set, one extra TRB has been used to align transfer - * size to wMaxPacketSize. - * - * All of these cases need to be taken into - * consideration so we don't mess up our TRB ring - * pointers. - */ wait_event_lock_irq(dep->wait_end_transfer, !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), dwc->lock); @@ -1404,13 +1397,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - for (i = 0; i < r->num_trbs; i++) { - struct dwc3_trb *trb; - - trb = r->trb + i; - trb->ctrl &= ~DWC3_TRB_CTRL_HWO; - dwc3_ep_inc_deq(dep); - } + dwc3_gadget_ep_skip_trbs(dep, r); goto out1; } dev_err(dwc->dev, "request %pK was not queued to %s\n", From 61895df209f2e1e5124231623e4ffdb220567d7a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:53:29 +0300 Subject: [PATCH 1161/1276] usb: dwc3: gadget: introduce cancelled_list This list will host cancelled requests who still have TRBs being processed. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 1 + drivers/usb/dwc3/gadget.h | 15 +++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0de78cb29f2c..24f0b108b7f6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -636,6 +636,7 @@ struct dwc3_event_buffer { /** * struct dwc3_ep - device side endpoint representation * @endpoint: usb endpoint + * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete @@ -659,6 +660,7 @@ struct dwc3_event_buffer { */ struct dwc3_ep { struct usb_ep endpoint; + struct list_head cancelled_list; struct list_head pending_list; struct list_head started_list; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e5cc41b3b20d..8e235417027f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2169,6 +2169,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) INIT_LIST_HEAD(&dep->pending_list); INIT_LIST_HEAD(&dep->started_list); + INIT_LIST_HEAD(&dep->cancelled_list); return 0; } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 2aacd1afd9ff..023a473648eb 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -79,6 +79,21 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) list_move_tail(&req->list, &dep->started_list); } +/** + * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list + * @req: the request to be moved + * + * Caller should take care of locking. This function will move @req from its + * current list to the endpoint's cancelled_list. + */ +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->started = false; + list_move_tail(&req->list, &dep->cancelled_list); +} + void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); From 6fbe71ceb0dc22838b6f7afdea82a90d87af96ca Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:54:25 +0300 Subject: [PATCH 1162/1276] usb: dwc3: gadget: move requests to cancelled_list Whenever we have a request in flight, we can move it to the cancelled list and later simply iterate over that list and skip over any TRBs we find. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8e235417027f..987704ede3a0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1361,6 +1361,17 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r } } +static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct dwc3_request *tmp; + + list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + dwc3_gadget_ep_skip_trbs(dep, req); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } +} + static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request) { @@ -1397,8 +1408,9 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (!r->trb) goto out0; - dwc3_gadget_ep_skip_trbs(dep, r); - goto out1; + dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + goto out0; } dev_err(dwc->dev, "request %pK was not queued to %s\n", request, ep->name); @@ -1406,7 +1418,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, goto out0; } -out1: dwc3_gadget_giveback(dep, req, -ECONNRESET); out0: From 693728689ce73798dcc1cbe88a7a56e631807ec8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 1 Aug 2018 13:56:50 +0300 Subject: [PATCH 1163/1276] usb: dwc3: gadget: remove wait_end_transfer Now that we have a list of cancelled requests, we can skip over TRBs when END_TRANSFER command completes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 --- drivers/usb/dwc3/gadget.c | 40 +-------------------------------------- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 24f0b108b7f6..131028501752 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -639,7 +639,6 @@ struct dwc3_event_buffer { * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint - * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete * @lock: spinlock for endpoint request queue traversal * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers @@ -664,8 +663,6 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; - wait_queue_head_t wait_end_transfer; - spinlock_t lock; void __iomem *regs; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 987704ede3a0..69c6f512884d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -639,8 +639,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); - init_waitqueue_head(&dep->wait_end_transfer); - if (usb_endpoint_xfer_control(desc)) goto out; @@ -1401,15 +1399,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (r == req) { /* wait until it is processed */ dwc3_stop_active_transfer(dep, true); - wait_event_lock_irq(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock); if (!r->trb) goto out0; dwc3_gadget_move_cancelled_request(req); - dwc3_gadget_ep_cleanup_cancelled_requests(dep); goto out0; } dev_err(dwc->dev, "request %pK was not queued to %s\n", @@ -1925,8 +1919,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; - int epnum; - u32 tmo_eps = 0; spin_lock_irqsave(&dwc->lock, flags); @@ -1935,36 +1927,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g) __dwc3_gadget_stop(dwc); - for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { - struct dwc3_ep *dep = dwc->eps[epnum]; - int ret; - - if (!dep) - continue; - - if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) - continue; - - ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock, msecs_to_jiffies(5)); - - if (ret <= 0) { - /* Timed out or interrupted! There's nothing much - * we can do so we just log here and print which - * endpoints timed out at the end. - */ - tmo_eps |= 1 << epnum; - dep->flags &= DWC3_EP_END_TRANSFER_PENDING; - } - } - - if (tmo_eps) { - dev_err(dwc->dev, - "end transfer timed out on endpoints 0x%x [bitmap]\n", - tmo_eps); - } - out: dwc->gadget_driver = NULL; spin_unlock_irqrestore(&dwc->lock, flags); @@ -2473,7 +2435,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (cmd == DWC3_DEPCMD_ENDTRANSFER) { dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; - wake_up(&dep->wait_end_transfer); + dwc3_gadget_ep_cleanup_cancelled_requests(dep); } break; case DWC3_DEPEVT_STREAMEVT: From df2fc42820c8c283efe003d11b1bec71b4e1ea1a Mon Sep 17 00:00:00 2001 From: Shen Jing Date: Thu, 1 Nov 2018 15:35:17 +0530 Subject: [PATCH 1164/1276] Revert "usb: gadget: ffs: Fix BUG when userland exits with submitted AIO transfers" This reverts commit b4194da3f9087dd38d91b40f9bec42d59ce589a8 since it causes list corruption followed by kernel panic: Workqueue: adb ffs_aio_cancel_worker RIP: 0010:__list_add_valid+0x4d/0x70 Call Trace: insert_work+0x47/0xb0 __queue_work+0xf6/0x400 queue_work_on+0x65/0x70 dwc3_gadget_giveback+0x44/0x50 [dwc3] dwc3_gadget_ep_dequeue+0x83/0x2d0 [dwc3] ? finish_wait+0x80/0x80 usb_ep_dequeue+0x1e/0x90 process_one_work+0x18c/0x3b0 worker_thread+0x3c/0x390 ? process_one_work+0x3b0/0x3b0 kthread+0x11e/0x140 ? kthread_create_worker_on_cpu+0x70/0x70 ret_from_fork+0x3a/0x50 This issue is seen with warm reboot stability testing. Signed-off-by: Shen Jing Signed-off-by: Saranya Gopal Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 3ada83d81bda..31e8bf3578c8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -215,7 +215,6 @@ struct ffs_io_data { struct mm_struct *mm; struct work_struct work; - struct work_struct cancellation_work; struct usb_ep *ep; struct usb_request *req; @@ -1073,31 +1072,22 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static void ffs_aio_cancel_worker(struct work_struct *work) -{ - struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, - cancellation_work); - - ENTER(); - - usb_ep_dequeue(io_data->ep, io_data->req); -} - static int ffs_aio_cancel(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; - struct ffs_data *ffs = io_data->ffs; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; int value; ENTER(); - if (likely(io_data && io_data->ep && io_data->req)) { - INIT_WORK(&io_data->cancellation_work, ffs_aio_cancel_worker); - queue_work(ffs->io_completion_wq, &io_data->cancellation_work); - value = -EINPROGRESS; - } else { + spin_lock_irq(&epfile->ffs->eps_lock); + + if (likely(io_data && io_data->ep && io_data->req)) + value = usb_ep_dequeue(io_data->ep, io_data->req); + else value = -EINVAL; - } + + spin_unlock_irq(&epfile->ffs->eps_lock); return value; } From 66d875d768a41de044338baaa9a2af5fb88b28af Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 13 Aug 2018 09:56:43 +0800 Subject: [PATCH 1165/1276] ANDROID: sdcardfs: fix -ENOENT lookup race issue on AOSP sdcardfs The negative lower dentry created by vfs_path_lookup could be reclaimed between vfs_path_lookup and d_hash_and_lookup. Therefore, it is unsafe to just lookup dcache again for the negative dentry cases. Without this patch, users could occasionally get trapped into `failed to create' under memory pressure. So here is a workaround to hack it and in my opinion sdcardfs should be refactored to close all races in the long term as pointed out in the code comment of this commit. Reproduce: (Thread 1) while true; do echo 3 > /proc/sys/vm/drop_caches done (Thread 2) i=0 while true; do echo 123 > /sdcard/$i i=$((i+1)) done Bug: 63872684 Cc: Daniel Rosenberg Cc: Miao Xie Cc: Chao Yu Change-Id: Ic033e1f84a8b271c1f48010f4e1f189982bbbea2 Signed-off-by: Gao Xiang --- fs/sdcardfs/lookup.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a5c9686090e0..aacd75cc8958 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -316,6 +316,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* no error: handle positive dentries */ if (!err) { +found: /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with * the inode of the graft path @@ -362,28 +363,27 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err && err != -ENOENT) goto out; - /* instatiate a new negative dentry */ - dname.name = name->name; - dname.len = name->len; - - /* See if the low-level filesystem might want - * to use its own hash - */ - lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); - if (IS_ERR(lower_dentry)) - return lower_dentry; - - if (!lower_dentry) { - /* We called vfs_path_lookup earlier, and did not get a negative - * dentry then. Don't confuse the lower filesystem by forcing - * one on it now... - */ - err = -ENOENT; + /* get a (very likely) new negative dentry */ + lower_dentry = lookup_one_len_unlocked(name->name, + lower_dir_dentry, name->len); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); goto out; } lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); + + /* + * partially close race from operating underlayfs directly, + * since lower_dentry could still become positive before entering + * .create (in other words, it should be rechecked in .create). + */ + if (unlikely(READ_ONCE(lower_dentry->d_inode))) {/* like d_inode_rcu */ + err = 0; + goto found; + } + sdcardfs_set_lower_path(dentry, &lower_path); /* From 7d8dbe4fac79745dc9616c6e0ecaa555d5ddb92f Mon Sep 17 00:00:00 2001 From: Lucas Rangit Magasweran Date: Sat, 14 Jul 2018 15:38:29 -0700 Subject: [PATCH 1166/1276] ACPI: battery: use cache_time as cache "enabled" When battery state is not cached the module parameter cache_time is 0 and battery->update_time starts at 0. However, it set to jiffies in each call to acpi_battery_get_state() and should not be used to determine if a cache time is used. Using battery->update_time causes the evaluation of time_before() even though cache_time is 0. This is a minor issue as the behavior is still as expected. Even if kernel HZ was very slow and jiffies remained equal, the expected branch (false) would be taken because time_before() is used instead of time_before_eq(). Signed-off-by: Lucas Rangit Magasweran Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index cb97b6105f52..1cb33c95388a 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -563,7 +563,7 @@ static int acpi_battery_get_state(struct acpi_battery *battery) if (!acpi_battery_present(battery)) return 0; - if (battery->update_time && + if (cache_time && time_before(jiffies, battery->update_time + msecs_to_jiffies(cache_time))) return 0; From d9d165b2e97228c31695f84ad4a3300527d6d46e Mon Sep 17 00:00:00 2001 From: Yi Yao Date: Sat, 20 Oct 2018 15:05:11 +0800 Subject: [PATCH 1167/1276] Turbo CPU freq before kernel decompress Change-Id: I7563991f8884f0b8795cb72c24abf3c1f34140a9 Tracked-On: Signed-off-by: Yi Yao --- arch/x86/boot/compressed/misc.c | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 8dd1d5ccae58..0acc3c97b185 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -319,6 +319,66 @@ static void parse_elf(void *output) free(phdrs); } +#ifdef CONFIG_TURBO_BEFORE_DECOMPRESS +static inline unsigned long long native_read_msr_local( + unsigned int msr) +{ + DECLARE_ARGS(val, low, high); + asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr)); + return EAX_EDX_VAL(val, low, high); +} + +static inline void native_write_msr_local(unsigned int msr, + unsigned low, unsigned high) +{ + asm volatile("wrmsr" : : "c" (msr), + "a"(low), "d" (high) : "memory"); +} + +#define rdmsrl_local(msr, val) \ + ((val) = native_read_msr_local((msr))) + +static inline void wrmsrl_local(unsigned msr, u64 val) +{ + native_write_msr_local(msr, (u32)val, (u32)(val >> 32)); +} + +static int core_get_max_pstate(void) +{ + u64 value; + + rdmsrl_local(MSR_PLATFORM_INFO, value); + return (value >> 8) & 0xFF; +} + +static int core_get_turbo_pstate(void) +{ + u64 value; + int nont, ret; + + rdmsrl_local(MSR_TURBO_RATIO_LIMIT, value); + nont = core_get_max_pstate(); + ret = (value) & 255; + if (ret <= nont) + ret = nont; + return ret; +} + +static void core_set_pstate(int pstate) +{ + u64 val; + + val = (u64)(pstate << 8); + + wrmsrl_local(MSR_IA32_PERF_CTL, val); +} + +static void cpu_turbo_once(void) +{ + core_set_pstate(core_get_turbo_pstate()); +} +#endif + /* * The compressed kernel image (ZO), has been moved so that its position * is against the end of the buffer used to hold the uncompressed kernel @@ -364,6 +424,10 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, lines = boot_params->screen_info.orig_video_lines; cols = boot_params->screen_info.orig_video_cols; +#ifdef CONFIG_TURBO_BEFORE_DECOMPRESS + cpu_turbo_once(); +#endif + console_init(); debug_putstr("early console in extract_kernel\n"); From 69222d6a1ff4bd31fba9f719e8fee7cf1dd4cfae Mon Sep 17 00:00:00 2001 From: Zhang Ning Date: Fri, 22 Dec 2017 14:47:53 +0800 Subject: [PATCH 1168/1276] disbale SD/SDIO on EMMC bus due to SDHCI bus 1 is for EMMC, disable SD/SDIO save 40ms for mmc_rescan Change-Id: Ia61f5477f162ef772700412d6a6ad9153f699431 Signed-off-by: Zhang Ning Reviewed-by: Zhang, Baoli --- drivers/mmc/host/sdhci-pci-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7bfd366d970d..7e36bdfde39c 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -725,6 +725,9 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ slot->host->mmc_host_ops.select_drive_strength = intel_select_drive_strength; + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_EMMC) { + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO | MMC_CAP2_NO_SD; + } return 0; } From d9f53974b6eac89fd575484bb8b4c82af8ae1afa Mon Sep 17 00:00:00 2001 From: xichen12 Date: Thu, 18 Oct 2018 16:37:08 +0800 Subject: [PATCH 1169/1276] move sdhci-pci & mmc_block to fs_initcall_sync due to mmc_rescan will cost 140ms for mmc detect. this will block dm-0 and rootfs setup. thus make kernel initial longer. so move them to early stage make mmc detect finishes early, finally save kernel initial time. saves about 140ms. Change-Id: I6072b60acd3caa1337bc8b5aee26edfb7446dbb7 Tracked-On: Signed-off-by: xichen12 --- drivers/mmc/core/block.c | 2 +- drivers/mmc/host/sdhci-pci-core.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index e201ccb3fda4..efc0557b7867 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -3094,7 +3094,7 @@ static void __exit mmc_blk_exit(void) bus_unregister(&mmc_rpmb_bus_type); } -module_init(mmc_blk_init); +fs_initcall_sync(mmc_blk_init); module_exit(mmc_blk_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7e36bdfde39c..e48429c0dfa7 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1954,7 +1954,17 @@ static struct pci_driver sdhci_driver = { }, }; -module_pci_driver(sdhci_driver); +static int __init sdhci_driver_init(void) +{ + return pci_register_driver(&sdhci_driver); +} +fs_initcall_sync(sdhci_driver_init); + +static void __exit sdhci_driver_exit(void) +{ + pci_unregister_driver(&sdhci_driver); +} +module_exit(sdhci_driver_exit); MODULE_AUTHOR("Pierre Ossman "); MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver"); From 76664e24c33ddd04e3022185c8a7e1e7c5bbed89 Mon Sep 17 00:00:00 2001 From: Yipeng Date: Wed, 20 Sep 2017 14:15:02 +0800 Subject: [PATCH 1170/1276] alarmtimer: add config to skip suspend flow Port from kernel4.4(bxtp_ivi_m). Unlike phone design, IVI should be always lighten on when driver is driving, without auto-sleep. Suspend is being used for 2 cases: a) driver shutdown system by pressing button; b) garage mode. Garage mode means that android system may needs to update some apps such as GPS when car is parking in garge, with wifi connected. It needs timer to wake up android system to see if upgrade is avalible. However, garage mode is introduced after N.car. Garage mode is not supported in current program, based on Android M release. As a result, alarmtimer wakup during suspend is not being used in IVI M. There are more operating systems to manage system on IVI. Android is not the only one. IOC is another OS (RTOS), with much higher priority to control Android OS, to manage system. Almost all inputs would be routed from IOC, to Android, including even the simplest signal, power_key for example. And there is a pre-defined communication protocol between IOC and Android, to share all messages, asynchronously, of cause. As a result, the suspend flow would be like: IOC suspend message received -> power event -> event handling by Android -> continuously send postpone message to IOC, until Android work finished -> send ready to suspend message to IOC, after work finished -> kernel suspend -> h/w seq to tell IOC it is ready to enter low power mode -> IOC lets system enter suspend. IOC received wakeup signal -> h/w seq to bring back SoC online -> kernel resume -> IOC send wakeup reason message to Android -> Android to decide whether to enter active mode (with screen on), or to enter garage mode (with screen off) (if Active mode) -> Android send stack ready signal to IOC -> event handling by Android (if Garage mode) -> Android send stack ready signal to IOC -> continuously send postpone message to IOC, until Android work finished -> send ready to suspend message to IOC, afte work finished -> suspend (the same as above) At last, wake lock mechanism is totally broken on IVI system. Actually from google's feedback, even based on N.Car, the answer to how to use general libsuspend interface to implement power feature, with wakelock mechanism supported, is to use "echo mem > /sys/power/state" directly. Things would be going to be right in O release. Signed-off-by: Yipeng Signed-off-by: xichen1x --- kernel/time/Kconfig | 9 +++++++++ kernel/time/alarmtimer.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index 78eabc41eaa6..8c9f2f62716b 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -130,5 +130,14 @@ config HIGH_RES_TIMERS hardware is not capable then this option only increases the size of the kernel image. +# Wakelock and suspend for IVI system needs to disable alarmtimer_suspend +config ANDROID_AUTO_SUSPEND_BEHAVIOR + bool "skip alarm timer suspend flow for Android auto usage" + default n + help + Say Y if it doesn't need alarm timer to wake up system, especially + in vehicle usage. + Say N if you are unsure. + endmenu endif diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index fa5de5e8de61..eee6ec9db620 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -245,6 +245,9 @@ EXPORT_SYMBOL_GPL(alarm_expires_remaining); */ static int alarmtimer_suspend(struct device *dev) { +#ifdef CONFIG_ANDROID_AUTO_SUSPEND_BEHAVIOR + return 0; +#else ktime_t min, now, expires; int i, ret, type; struct rtc_device *rtc; @@ -302,6 +305,7 @@ static int alarmtimer_suspend(struct device *dev) if (ret < 0) __pm_wakeup_event(ws, MSEC_PER_SEC); return ret; +#endif } static int alarmtimer_resume(struct device *dev) From 314ce03f1e468242fe983b97db3819f01d0d537f Mon Sep 17 00:00:00 2001 From: Ong Hock Yu Date: Tue, 6 Nov 2018 18:59:34 +0000 Subject: [PATCH 1171/1276] media: intel-ipu4: [VIRT] Fixed warning for "Add multi streaming support on guest OS" patch. Change-Id: I812a79ecb9f946dbf985d4595f1db42d7630877d Signed-off-by: Ong Hock Yu --- .../media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c | 2 +- drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c | 1 - drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c index 086421151f28..322d8ea2afc3 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-pipeline.c @@ -14,8 +14,8 @@ #include "intel-ipu4-virtio-be-pipeline.h" #include "./ici/ici-isys-pipeline.h" #include "./ici/ici-isys-pipeline-device.h" -#include "intel-ipu4-virtio-be.h" #include "intel-ipu4-virtio-be-request-queue.h" +#include "intel-ipu4-virtio-be.h" static struct file *pipeline; static int guestID = -1; diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c index c3257ea0dbd1..ab06be71a4c4 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-stream.c @@ -329,7 +329,6 @@ int process_get_buf(struct ipu4_virtio_req_info *req_info) int process_stream_on(struct ipu4_virtio_req_info *req_info) { struct stream_node *sn = NULL; - struct ici_isys_stream *as; struct ici_stream_device *strm_dev; int err, found; struct ipu4_virtio_req *req = req_info->request; diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c index 4359d7b99c70..cb642f41e4b6 100644 --- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c +++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be.c @@ -156,7 +156,7 @@ static void handle_vq_kick(int client_id, int vq_idx) if (be == NULL) { pr_err("%s: client %d not found!\n", __func__, client_id); - return -EINVAL; + return; } if (!be) { @@ -170,7 +170,7 @@ static void handle_vq_kick(int client_id, int vq_idx) virtio_vq_getchain(vq, &idx, &iov, 1, NULL); pr_debug("%s: vq index: %d vq buf index: %d req ptr: %lu\n", - __func__, vq_idx, idx, iov.iov_base); + __func__, vq_idx, idx, (long unsigned)iov.iov_base); /* device specific operations, for example: */ if (iov.iov_len != sizeof(struct ipu4_virtio_req)) { if (iov.iov_len == sizeof(int)) { From 1b84b3032075dd93d5b878dcde700618606ed6ff Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Thu, 8 Nov 2018 11:11:00 +0530 Subject: [PATCH 1172/1276] serial: 8250_dma: stop ongoing RX DMA on exception If we get an exeption interrupt. i.e. UART_IIR_RLSI, stop any ongoing RX DMA transfer otherwise it might generate more spurious interrupts and make port unavailable anymore. As has been seen on Intel Broxton system: ... [ 168.526281] serial8250: too much work for irq5 [ 168.535908] serial8250: too much work for irq5 [ 173.449464] serial8250_interrupt: 4439 callbacks suppressed [ 173.455694] serial8250: too much work for irq5 ... Signed-off-by: Jeremy Compostella --- drivers/tty/serial/8250/8250_port.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 3f779d25ec0c..bc5af52ea932 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1845,9 +1845,8 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) { switch (iir & 0x3f) { case UART_IIR_RX_TIMEOUT: - serial8250_rx_dma_flush(up); - /* fall-through */ case UART_IIR_RLSI: + serial8250_rx_dma_flush(up); return true; } return up->dma->rx_dma(up); From 6a99a10ac97ed404bfd353b913774bacea414665 Mon Sep 17 00:00:00 2001 From: "Vyas, Tarun" Date: Wed, 5 Sep 2018 08:14:21 -0700 Subject: [PATCH 1173/1276] staging: Add AVnu based Intel IGB driver This initial commit adds the AVnu based Intel IGB driver for the I210 ethernet controller to drivers/staging. It mirrors and will remain in sync with what's hosted at https://github.com/AVnu/igb_avb/tree/master/kmod which is the github repo for the AVnu IGB driver. It can be built as module with CONFIG_IGB_AVB=m. This is *exclusive* with the in-tree IGB driver located at drivers/ net/ethernet/intel/igb/, so, if one is enabled, then the other one will get disabled and vice-versa. Signed-off-by: Tarun Vyas --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/igb_avb/COPYING | 339 + drivers/staging/igb_avb/Kconfig | 18 + drivers/staging/igb_avb/LICENSE | 24 + drivers/staging/igb_avb/Makefile | 18 + drivers/staging/igb_avb/README | 65 + drivers/staging/igb_avb/e1000_82575.c | 3809 +++++++ drivers/staging/igb_avb/e1000_82575.h | 510 + drivers/staging/igb_avb/e1000_api.c | 1160 +++ drivers/staging/igb_avb/e1000_api.h | 152 + drivers/staging/igb_avb/e1000_defines.h | 1486 +++ drivers/staging/igb_avb/e1000_hw.h | 792 ++ drivers/staging/igb_avb/e1000_i210.c | 993 ++ drivers/staging/igb_avb/e1000_i210.h | 101 + drivers/staging/igb_avb/e1000_mac.c | 2149 ++++ drivers/staging/igb_avb/e1000_mac.h | 81 + drivers/staging/igb_avb/e1000_manage.c | 552 + drivers/staging/igb_avb/e1000_manage.h | 86 + drivers/staging/igb_avb/e1000_mbx.c | 523 + drivers/staging/igb_avb/e1000_mbx.h | 84 + drivers/staging/igb_avb/e1000_nvm.c | 973 ++ drivers/staging/igb_avb/e1000_nvm.h | 70 + drivers/staging/igb_avb/e1000_osdep.h | 141 + drivers/staging/igb_avb/e1000_phy.c | 3396 ++++++ drivers/staging/igb_avb/e1000_phy.h | 252 + drivers/staging/igb_avb/e1000_regs.h | 633 ++ drivers/staging/igb_avb/igb.h | 937 ++ drivers/staging/igb_avb/igb_avb.7 | 253 + drivers/staging/igb_avb/igb_debugfs.c | 26 + drivers/staging/igb_avb/igb_ethtool.c | 2994 ++++++ drivers/staging/igb_avb/igb_hwmon.c | 257 + drivers/staging/igb_avb/igb_main.c | 10875 ++++++++++++++++++++ drivers/staging/igb_avb/igb_param.c | 871 ++ drivers/staging/igb_avb/igb_procfs.c | 356 + drivers/staging/igb_avb/igb_ptp.c | 1364 +++ drivers/staging/igb_avb/igb_regtest.h | 256 + drivers/staging/igb_avb/igb_vmdq.c | 433 + drivers/staging/igb_avb/igb_vmdq.h | 43 + drivers/staging/igb_avb/kcompat.c | 1977 ++++ drivers/staging/igb_avb/kcompat.h | 4707 +++++++++ drivers/staging/igb_avb/kcompat_ethtool.c | 1169 +++ drivers/staging/igb_avb/pci.updates | 19 + drivers/staging/igb_avb/startup.sh | 23 + 44 files changed, 44970 insertions(+) create mode 100644 drivers/staging/igb_avb/COPYING create mode 100644 drivers/staging/igb_avb/Kconfig create mode 100644 drivers/staging/igb_avb/LICENSE create mode 100644 drivers/staging/igb_avb/Makefile create mode 100644 drivers/staging/igb_avb/README create mode 100644 drivers/staging/igb_avb/e1000_82575.c create mode 100644 drivers/staging/igb_avb/e1000_82575.h create mode 100644 drivers/staging/igb_avb/e1000_api.c create mode 100644 drivers/staging/igb_avb/e1000_api.h create mode 100644 drivers/staging/igb_avb/e1000_defines.h create mode 100644 drivers/staging/igb_avb/e1000_hw.h create mode 100644 drivers/staging/igb_avb/e1000_i210.c create mode 100644 drivers/staging/igb_avb/e1000_i210.h create mode 100644 drivers/staging/igb_avb/e1000_mac.c create mode 100644 drivers/staging/igb_avb/e1000_mac.h create mode 100644 drivers/staging/igb_avb/e1000_manage.c create mode 100644 drivers/staging/igb_avb/e1000_manage.h create mode 100644 drivers/staging/igb_avb/e1000_mbx.c create mode 100644 drivers/staging/igb_avb/e1000_mbx.h create mode 100644 drivers/staging/igb_avb/e1000_nvm.c create mode 100644 drivers/staging/igb_avb/e1000_nvm.h create mode 100644 drivers/staging/igb_avb/e1000_osdep.h create mode 100644 drivers/staging/igb_avb/e1000_phy.c create mode 100644 drivers/staging/igb_avb/e1000_phy.h create mode 100644 drivers/staging/igb_avb/e1000_regs.h create mode 100644 drivers/staging/igb_avb/igb.h create mode 100755 drivers/staging/igb_avb/igb_avb.7 create mode 100644 drivers/staging/igb_avb/igb_debugfs.c create mode 100644 drivers/staging/igb_avb/igb_ethtool.c create mode 100644 drivers/staging/igb_avb/igb_hwmon.c create mode 100644 drivers/staging/igb_avb/igb_main.c create mode 100644 drivers/staging/igb_avb/igb_param.c create mode 100644 drivers/staging/igb_avb/igb_procfs.c create mode 100644 drivers/staging/igb_avb/igb_ptp.c create mode 100644 drivers/staging/igb_avb/igb_regtest.h create mode 100644 drivers/staging/igb_avb/igb_vmdq.c create mode 100644 drivers/staging/igb_avb/igb_vmdq.h create mode 100644 drivers/staging/igb_avb/kcompat.c create mode 100644 drivers/staging/igb_avb/kcompat.h create mode 100644 drivers/staging/igb_avb/kcompat_ethtool.c create mode 100644 drivers/staging/igb_avb/pci.updates create mode 100755 drivers/staging/igb_avb/startup.sh diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 1abf76be2aa8..89735a5fd9e1 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -126,4 +126,6 @@ source "drivers/staging/axis-fifo/Kconfig" source "drivers/staging/erofs/Kconfig" +source "drivers/staging/igb_avb/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ab0cbe8815b1..f7d9b0acf361 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_EROFS_FS) += erofs/ +obj-$(CONFIG_IGB_AVB) += igb_avb/ diff --git a/drivers/staging/igb_avb/COPYING b/drivers/staging/igb_avb/COPYING new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/drivers/staging/igb_avb/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/drivers/staging/igb_avb/Kconfig b/drivers/staging/igb_avb/Kconfig new file mode 100644 index 000000000000..e3da46a6f381 --- /dev/null +++ b/drivers/staging/igb_avb/Kconfig @@ -0,0 +1,18 @@ +config IGB_AVB + tristate "Avnu IGB AVB driver" + depends on IGB=n + select DCA + default n + ---help--- + This is the Intel I210 Ethernet driver that lives + at https://github.com/AVnu/OpenAvnu/tree/master/ + kmod/igb. Note that this is different from drivers/ + net/ethernet/intel/igb. It can be used for developing + Audio/Video Bridging applications, Industrial Ethernet + applications which require precise timing control over + frame transmission, or test harnesses for measuring system + latencies and sampling events. It is exclusive with the + in-tree IGB driver, so only one of them can be enabled + at any point in time. + + To build this as a module, say M, if not sure say N. diff --git a/drivers/staging/igb_avb/LICENSE b/drivers/staging/igb_avb/LICENSE new file mode 100644 index 000000000000..b84d7002e5c4 --- /dev/null +++ b/drivers/staging/igb_avb/LICENSE @@ -0,0 +1,24 @@ + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2012 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + diff --git a/drivers/staging/igb_avb/Makefile b/drivers/staging/igb_avb/Makefile new file mode 100644 index 000000000000..eaae47e157f3 --- /dev/null +++ b/drivers/staging/igb_avb/Makefile @@ -0,0 +1,18 @@ +obj-$(CONFIG_IGB_AVB) += igb_avb.o + +igb_avb-y := igb_main.o \ + e1000_82575.o \ + e1000_i210.o \ + e1000_mac.o \ + e1000_nvm.o e1000_phy.o \ + e1000_manage.o \ + igb_param.o \ + igb_ethtool.o \ + kcompat.o \ + e1000_api.o \ + e1000_mbx.o \ + igb_vmdq.o \ + igb_procfs.o \ + igb_hwmon.o \ + igb_debugfs.o \ + igb_ptp.o diff --git a/drivers/staging/igb_avb/README b/drivers/staging/igb_avb/README new file mode 100644 index 000000000000..c08ff5d4d18d --- /dev/null +++ b/drivers/staging/igb_avb/README @@ -0,0 +1,65 @@ +INTRODUCTION + +This component demonstrates various features of the Intel I210 Ethernet +controller. These features can be used for developing Audio/Video Bridging +applications, Industrial Ethernet applications which require precise timing +control over frame transmission, or test harnesses for measuring system +latencies and sampling events. + +This component - igb_avb - is limited to the Intel I210 Ethernet controller. +The kernel module can be loaded in parallel to existing in-kernel igb modules +which may be used on other supported Intel LAN controllers. Modifications are +required to the in-kernel drivers if the existing in-kernel igb driver has +support for the Intel I210. + +BUILDING + +The kernel igb module should be built which supports the latest Linux kernel +3.x PTP clock support. Unlike the standard igb driver, this version enables +PTP by default (and will fail to build without kernel PTP support enabled). + +RUNNING + +To install the kernel mode driver, you must have root permissions. Typically, +the driver is loaded by removing the currently running igb and running igb_avb: + sudo rmmod igb + sudo modprobe i2c_algo_bit + sudo modprobe dca + sudo modprobe ptp + sudo insmod ./igb_avb.ko + +Another option is to install the igb_avb driver in the "updates" directory +which will override igb for the other drivers claiming the same device ID. This +will allow the coexistence of igb and igb_avb. Copy igb_avb.ko to: + sudo cp igb_avb.ko /lib/modules/`uname -r`/updates/ + sudo depmod -a + modprobe igb_avb + +As the AVB Transmit queues (0,1) are mapped to a user-space application, +typical LAN traffic must be steered away from these queues. The driver +implements one method registering an ndo_select_queue handler to map traffic to +queue[3]. Another possibly faster method uses the the transmit packet steering +(XPS) functionality available since 2.6.35. An example script is below + +#!/bin/bash + +INTERFACE=p2p1 +export INTERFACE + +rmmod igb +rmmod igb_avb +insmod ./igb_avb.ko +sleep 1 +ifconfig $INTERFACE down +echo 0 > /sys/class/net/$INTERFACE/queues/tx-0/xps_cpus +echo 0 > /sys/class/net/$INTERFACE/queues/tx-1/xps_cpus +echo f > /sys/class/net/$INTERFACE/queues/tx-2/xps_cpus +echo f > /sys/class/net/$INTERFACE/queues/tx-3/xps_cpus +ifconfig $INTERFACE up + +You map also want to disable the network manager from 'managing' your +interface. The easiest way is to find the interface configuration scripts on +your distribution. On Fedora 18, these are located at +/etc/sysconfig/network-scripts/ifcfg-. Edit the file to set +'BOOTPROTO=none'. This eliminates DHCP trying to configure the interface while +you may be doing user-space application configuration. diff --git a/drivers/staging/igb_avb/e1000_82575.c b/drivers/staging/igb_avb/e1000_82575.c new file mode 100644 index 000000000000..2fcc3bf3af39 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_82575.c @@ -0,0 +1,3809 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* + * 82575EB Gigabit Network Connection + * 82575EB Gigabit Backplane Connection + * 82575GB Gigabit Network Connection + * 82576 Gigabit Network Connection + * 82576 Quad Port Gigabit Mezzanine Adapter + * 82580 Gigabit Network Connection + * I350 Gigabit Network Connection + */ + +#include "e1000_api.h" +#include "e1000_i210.h" + +static s32 e1000_init_phy_params_82575(struct e1000_hw *hw); +static s32 e1000_init_mac_params_82575(struct e1000_hw *hw); +static s32 e1000_acquire_phy_82575(struct e1000_hw *hw); +static void e1000_release_phy_82575(struct e1000_hw *hw); +static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw); +static void e1000_release_nvm_82575(struct e1000_hw *hw); +static s32 e1000_check_for_link_82575(struct e1000_hw *hw); +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw); +static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw); +static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +static s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw); +static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 *data); +static s32 e1000_reset_hw_82575(struct e1000_hw *hw); +static s32 e1000_reset_hw_82580(struct e1000_hw *hw); +static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw, + u32 offset, u16 *data); +static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw, + u32 offset, u16 data); +static s32 e1000_set_d0_lplu_state_82580(struct e1000_hw *hw, + bool active); +static s32 e1000_set_d3_lplu_state_82580(struct e1000_hw *hw, + bool active); +static s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, + bool active); +static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw); +static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw); +static s32 e1000_get_media_type_82575(struct e1000_hw *hw); +#ifdef I2C_ENABLED +static s32 e1000_set_sfp_media_type_82575(struct e1000_hw *hw); +#endif +static s32 e1000_valid_led_default_82575(struct e1000_hw *hw, u16 *data); +static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, + u32 offset, u16 data); +static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw); +static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +static s32 e1000_get_phy_id_82575(struct e1000_hw *hw); +static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static bool e1000_sgmii_active_82575(struct e1000_hw *hw); +static s32 e1000_reset_init_script_82575(struct e1000_hw *hw); +static s32 e1000_read_mac_addr_82575(struct e1000_hw *hw); +static void e1000_config_collision_dist_82575(struct e1000_hw *hw); +static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw); +static void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw); +static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw); +static s32 e1000_set_pcie_completion_timeout(struct e1000_hw *hw); +static s32 e1000_reset_mdicnfg_82580(struct e1000_hw *hw); +static s32 e1000_validate_nvm_checksum_82580(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_82580(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_with_offset(struct e1000_hw *hw, + u16 offset); +static s32 e1000_validate_nvm_checksum_with_offset(struct e1000_hw *hw, + u16 offset); +static s32 e1000_validate_nvm_checksum_i350(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_i350(struct e1000_hw *hw); +static void e1000_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value); +static void e1000_clear_vfta_i350(struct e1000_hw *hw); + +static void e1000_i2c_start(struct e1000_hw *hw); +static void e1000_i2c_stop(struct e1000_hw *hw); +static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data); +static s32 e1000_clock_out_i2c_byte(struct e1000_hw *hw, u8 data); +static s32 e1000_get_i2c_ack(struct e1000_hw *hw); +static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data); +static s32 e1000_clock_out_i2c_bit(struct e1000_hw *hw, bool data); +static void e1000_raise_i2c_clk(struct e1000_hw *hw, u32 *i2cctl); +static void e1000_lower_i2c_clk(struct e1000_hw *hw, u32 *i2cctl); +static s32 e1000_set_i2c_data(struct e1000_hw *hw, u32 *i2cctl, bool data); +static bool e1000_get_i2c_data(u32 *i2cctl); + +static const u16 e1000_82580_rxpbs_table[] = { + 36, 72, 144, 1, 2, 4, 8, 16, 35, 70, 140 }; +#define E1000_82580_RXPBS_TABLE_SIZE \ + (sizeof(e1000_82580_rxpbs_table) / \ + sizeof(e1000_82580_rxpbs_table[0])) + +/** + * e1000_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO + * @hw: pointer to the HW structure + * + * Called to determine if the I2C pins are being used for I2C or as an + * external MDIO interface since the two options are mutually exclusive. + **/ +static bool e1000_sgmii_uses_mdio_82575(struct e1000_hw *hw) +{ + u32 reg = 0; + bool ext_mdio = false; + + DEBUGFUNC("e1000_sgmii_uses_mdio_82575"); + + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + reg = E1000_READ_REG(hw, E1000_MDIC); + ext_mdio = !!(reg & E1000_MDIC_DEST); + break; + case e1000_82580: + case e1000_i350: + case e1000_i354: + case e1000_i210: + case e1000_i211: + reg = E1000_READ_REG(hw, E1000_MDICNFG); + ext_mdio = !!(reg & E1000_MDICNFG_EXT_MDIO); + break; + default: + break; + } + return ext_mdio; +} + +/** + * e1000_init_phy_params_82575 - Init PHY func ptrs. + * @hw: pointer to the HW structure + **/ +static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext; + + DEBUGFUNC("e1000_init_phy_params_82575"); + + phy->ops.read_i2c_byte = e1000_read_i2c_byte_generic; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_generic; + + if (hw->phy.media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_82575; + + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + + phy->ops.acquire = e1000_acquire_phy_82575; + phy->ops.check_reset_block = e1000_check_reset_block_generic; + phy->ops.commit = e1000_phy_sw_reset_generic; + phy->ops.get_cfg_done = e1000_get_cfg_done_82575; + phy->ops.release = e1000_release_phy_82575; + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + + if (e1000_sgmii_active_82575(hw)) { + phy->ops.reset = e1000_phy_hw_reset_sgmii_82575; + ctrl_ext |= E1000_CTRL_I2C_ENA; + } else { + phy->ops.reset = e1000_phy_hw_reset_generic; + ctrl_ext &= ~E1000_CTRL_I2C_ENA; + } + + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + e1000_reset_mdicnfg_82580(hw); + + if (e1000_sgmii_active_82575(hw) && !e1000_sgmii_uses_mdio_82575(hw)) { + phy->ops.read_reg = e1000_read_phy_reg_sgmii_82575; + phy->ops.write_reg = e1000_write_phy_reg_sgmii_82575; + } else { + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + case e1000_i354: + phy->ops.read_reg = e1000_read_phy_reg_82580; + phy->ops.write_reg = e1000_write_phy_reg_82580; + break; + case e1000_i210: + case e1000_i211: + phy->ops.read_reg = e1000_read_phy_reg_gs40g; + phy->ops.write_reg = e1000_write_phy_reg_gs40g; + break; + default: + phy->ops.read_reg = e1000_read_phy_reg_igp; + phy->ops.write_reg = e1000_write_phy_reg_igp; + } + } + + /* Set phy->phy_addr and phy->id. */ + ret_val = e1000_get_phy_id_82575(hw); + + /* Verify phy id and set remaining function pointers */ + switch (phy->id) { + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1111_I_PHY_ID: + phy->type = e1000_phy_m88; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.get_info = e1000_get_phy_info_m88; + if (phy->id == I347AT4_E_PHY_ID || + phy->id == M88E1112_E_PHY_ID || + phy->id == M88E1340M_E_PHY_ID) + phy->ops.get_cable_length = + e1000_get_cable_length_m88_gen2; + else if (phy->id == M88E1543_E_PHY_ID || + phy->id == M88E1512_E_PHY_ID) + phy->ops.get_cable_length = + e1000_get_cable_length_m88_gen2; + else + phy->ops.get_cable_length = e1000_get_cable_length_m88; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + /* Check if this PHY is confgured for media swap. */ + if (phy->id == M88E1112_E_PHY_ID) { + u16 data; + + ret_val = phy->ops.write_reg(hw, + E1000_M88E1112_PAGE_ADDR, + 2); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, + E1000_M88E1112_MAC_CTRL_1, + &data); + if (ret_val) + goto out; + + data = (data & E1000_M88E1112_MAC_CTRL_1_MODE_MASK) >> + E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT; + if (data == E1000_M88E1112_AUTO_COPPER_SGMII || + data == E1000_M88E1112_AUTO_COPPER_BASEX) + hw->mac.ops.check_for_link = + e1000_check_for_link_media_swap; + } + if (phy->id == M88E1512_E_PHY_ID) { + ret_val = e1000_initialize_M88E1512_phy(hw); + if (ret_val) + goto out; + } + break; + case IGP03E1000_E_PHY_ID: + case IGP04E1000_E_PHY_ID: + phy->type = e1000_phy_igp_3; + phy->ops.check_polarity = e1000_check_polarity_igp; + phy->ops.get_info = e1000_get_phy_info_igp; + phy->ops.get_cable_length = e1000_get_cable_length_igp_2; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82575; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_generic; + break; + case I82580_I_PHY_ID: + case I350_I_PHY_ID: + phy->type = e1000_phy_82580; + phy->ops.check_polarity = e1000_check_polarity_82577; + phy->ops.force_speed_duplex = + e1000_phy_force_speed_duplex_82577; + phy->ops.get_cable_length = e1000_get_cable_length_82577; + phy->ops.get_info = e1000_get_phy_info_82577; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; + break; + case I210_I_PHY_ID: + phy->type = e1000_phy_i210; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.get_info = e1000_get_phy_info_m88; + phy->ops.get_cable_length = e1000_get_cable_length_m88_gen2; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82575 - Init NVM func ptrs. + * @hw: pointer to the HW structure + **/ +s32 e1000_init_nvm_params_82575(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_82575"); + + size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + /* + * Added to a constant, "size" becomes the left-shift value + * for setting word_size. + */ + size += NVM_WORD_SIZE_BASE_SHIFT; + + /* Just in case size is out of range, cap it to the largest + * EEPROM size supported + */ + if (size > 15) + size = 15; + + nvm->word_size = 1 << size; + if (hw->mac.type < e1000_i210) { + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? + 16 : 8; + break; + } + if (nvm->word_size == (1 << 15)) + nvm->page_size = 128; + + nvm->type = e1000_nvm_eeprom_spi; + } else { + nvm->type = e1000_nvm_flash_hw; + } + + /* Function Pointers */ + nvm->ops.acquire = e1000_acquire_nvm_82575; + nvm->ops.release = e1000_release_nvm_82575; + if (nvm->word_size < (1 << 15)) + nvm->ops.read = e1000_read_nvm_eerd; + else + nvm->ops.read = e1000_read_nvm_spi; + + nvm->ops.write = e1000_write_nvm_spi; + nvm->ops.validate = e1000_validate_nvm_checksum_generic; + nvm->ops.update = e1000_update_nvm_checksum_generic; + nvm->ops.valid_led_default = e1000_valid_led_default_82575; + + /* override generic family function pointers for specific descendants */ + switch (hw->mac.type) { + case e1000_82580: + nvm->ops.validate = e1000_validate_nvm_checksum_82580; + nvm->ops.update = e1000_update_nvm_checksum_82580; + break; + case e1000_i350: + case e1000_i354: + nvm->ops.validate = e1000_validate_nvm_checksum_i350; + nvm->ops.update = e1000_update_nvm_checksum_i350; + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82575 - Init MAC func ptrs. + * @hw: pointer to the HW structure + **/ +static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + + DEBUGFUNC("e1000_init_mac_params_82575"); + + /* Derives media type */ + e1000_get_media_type_82575(hw); + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set uta register count */ + mac->uta_reg_count = (hw->mac.type == e1000_82575) ? 0 : 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES_82575; + if (mac->type == e1000_82576) + mac->rar_entry_count = E1000_RAR_ENTRIES_82576; + if (mac->type == e1000_82580) + mac->rar_entry_count = E1000_RAR_ENTRIES_82580; + if (mac->type == e1000_i350 || mac->type == e1000_i354) + mac->rar_entry_count = E1000_RAR_ENTRIES_I350; + + /* Enable EEE default settings for EEE supported devices */ + if (mac->type >= e1000_i350) + dev_spec->eee_disable = false; + + /* Allow a single clear of the SW semaphore on I210 and newer */ + if (mac->type >= e1000_i210) + dev_spec->clear_semaphore_once = true; + + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = true; + /* FWSM register */ + mac->has_fwsm = true; + /* ARC supported; valid only if manageability features are enabled. */ + mac->arc_subsystem_valid = + !!(E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK); + + /* Function pointers */ + + /* bus type/speed/width */ + mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic; + /* reset */ + if (mac->type >= e1000_82580) + mac->ops.reset_hw = e1000_reset_hw_82580; + else + mac->ops.reset_hw = e1000_reset_hw_82575; + /* hw initialization */ + if ((mac->type == e1000_i210) || (mac->type == e1000_i211)) + mac->ops.init_hw = e1000_init_hw_i210; + else + mac->ops.init_hw = e1000_init_hw_82575; + /* link setup */ + mac->ops.setup_link = e1000_setup_link_generic; + /* physical interface link setup */ + mac->ops.setup_physical_interface = + (hw->phy.media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82575 : e1000_setup_serdes_link_82575; + /* physical interface shutdown */ + mac->ops.shutdown_serdes = e1000_shutdown_serdes_link_82575; + /* physical interface power up */ + mac->ops.power_up_serdes = e1000_power_up_serdes_link_82575; + /* check for link */ + mac->ops.check_for_link = e1000_check_for_link_82575; + /* read mac address */ + mac->ops.read_mac_addr = e1000_read_mac_addr_82575; + /* configure collision distance */ + mac->ops.config_collision_dist = e1000_config_collision_dist_82575; + /* multicast address update */ + mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic; + if (hw->mac.type == e1000_i350 || mac->type == e1000_i354) { + /* writing VFTA */ + mac->ops.write_vfta = e1000_write_vfta_i350; + /* clearing VFTA */ + mac->ops.clear_vfta = e1000_clear_vfta_i350; + } else { + /* writing VFTA */ + mac->ops.write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + mac->ops.clear_vfta = e1000_clear_vfta_generic; + } + if (hw->mac.type >= e1000_82580) + mac->ops.validate_mdi_setting = + e1000_validate_mdi_setting_crossover_generic; + /* ID LED init */ + mac->ops.id_led_init = e1000_id_led_init_generic; + /* blink LED */ + mac->ops.blink_led = e1000_blink_led_generic; + /* setup LED */ + mac->ops.setup_led = e1000_setup_led_generic; + /* cleanup LED */ + mac->ops.cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + mac->ops.led_on = e1000_led_on_generic; + mac->ops.led_off = e1000_led_off_generic; + /* clear hardware counters */ + mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82575; + /* link info */ + mac->ops.get_link_up_info = e1000_get_link_up_info_82575; + /* get thermal sensor data */ + mac->ops.get_thermal_sensor_data = + e1000_get_thermal_sensor_data_generic; + mac->ops.init_thermal_sensor_thresh = + e1000_init_thermal_sensor_thresh_generic; + /* acquire SW_FW sync */ + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_82575; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_82575; + if (mac->type >= e1000_i210) { + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_i210; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_i210; + } + + /* set lan id for port to determine which phy lock to use */ + hw->mac.ops.set_lan_id(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_init_function_pointers_82575 - Init func ptrs. + * @hw: pointer to the HW structure + * + * Called to initialize all function pointers and parameters. + **/ +void e1000_init_function_pointers_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82575"); + + hw->mac.ops.init_params = e1000_init_mac_params_82575; + hw->nvm.ops.init_params = e1000_init_nvm_params_82575; + hw->phy.ops.init_params = e1000_init_phy_params_82575; + hw->mbx.ops.init_params = e1000_init_mbx_params_pf; +} + +/** + * e1000_acquire_phy_82575 - Acquire rights to access PHY + * @hw: pointer to the HW structure + * + * Acquire access rights to the correct PHY. + **/ +static s32 e1000_acquire_phy_82575(struct e1000_hw *hw) +{ + u16 mask = E1000_SWFW_PHY0_SM; + + DEBUGFUNC("e1000_acquire_phy_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_SWFW_PHY2_SM; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_SWFW_PHY3_SM; + + return hw->mac.ops.acquire_swfw_sync(hw, mask); +} + +/** + * e1000_release_phy_82575 - Release rights to access PHY + * @hw: pointer to the HW structure + * + * A wrapper to release access rights to the correct PHY. + **/ +static void e1000_release_phy_82575(struct e1000_hw *hw) +{ + u16 mask = E1000_SWFW_PHY0_SM; + + DEBUGFUNC("e1000_release_phy_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_SWFW_PHY2_SM; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_SWFW_PHY3_SM; + + hw->mac.ops.release_swfw_sync(hw, mask); +} + +/** + * e1000_read_phy_reg_sgmii_82575 - Read PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the serial gigabit media independent + * interface and stores the retrieved information in data. + **/ +static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 *data) +{ + s32 ret_val = -E1000_ERR_PARAM; + + DEBUGFUNC("e1000_read_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %u is out of range\n", offset); + goto out; + } + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg_i2c(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_sgmii_82575 - Write PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the serial gigabit + * media independent interface. + **/ +static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 data) +{ + s32 ret_val = -E1000_ERR_PARAM; + + DEBUGFUNC("e1000_write_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + goto out; + } + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg_i2c(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_get_phy_id_82575 - Retrieve PHY addr and id + * @hw: pointer to the HW structure + * + * Retrieves the PHY address and ID for both PHY's which do and do not use + * sgmi interface. + **/ +static s32 e1000_get_phy_id_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + u32 ctrl_ext; + u32 mdic; + + DEBUGFUNC("e1000_get_phy_id_82575"); + + /* some i354 devices need an extra read for phy id */ + if (hw->mac.type == e1000_i354) + e1000_get_phy_id(hw); + + /* + * For SGMII PHYs, we try the list of possible addresses until + * we find one that works. For non-SGMII PHYs + * (e.g. integrated copper PHYs), an address of 1 should + * work. The result of this function should mean phy->phy_addr + * and phy->id are set correctly. + */ + if (!e1000_sgmii_active_82575(hw)) { + phy->addr = 1; + ret_val = e1000_get_phy_id(hw); + goto out; + } + + if (e1000_sgmii_uses_mdio_82575(hw)) { + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + mdic = E1000_READ_REG(hw, E1000_MDIC); + mdic &= E1000_MDIC_PHY_MASK; + phy->addr = mdic >> E1000_MDIC_PHY_SHIFT; + break; + case e1000_82580: + case e1000_i350: + case e1000_i354: + case e1000_i210: + case e1000_i211: + mdic = E1000_READ_REG(hw, E1000_MDICNFG); + mdic &= E1000_MDICNFG_PHY_MASK; + phy->addr = mdic >> E1000_MDICNFG_PHY_SHIFT; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + ret_val = e1000_get_phy_id(hw); + goto out; + } + + /* Power on sgmii phy if it is disabled */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, + ctrl_ext & ~E1000_CTRL_EXT_SDP3_DATA); + E1000_WRITE_FLUSH(hw); + msec_delay(300); + + /* + * The address field in the I2CCMD register is 3 bits and 0 is invalid. + * Therefore, we need to test 1-7 + */ + for (phy->addr = 1; phy->addr < 8; phy->addr++) { + ret_val = e1000_read_phy_reg_sgmii_82575(hw, PHY_ID1, &phy_id); + if (ret_val == E1000_SUCCESS) { + DEBUGOUT2("Vendor ID 0x%08X read at address %u\n", + phy_id, phy->addr); + /* + * At the time of this writing, The M88 part is + * the only supported SGMII PHY product. + */ + if (phy_id == M88_VENDOR) + break; + } else { + DEBUGOUT1("PHY address %u was unreadable\n", + phy->addr); + } + } + + /* A valid PHY type couldn't be found. */ + if (phy->addr == 8) { + phy->addr = 0; + ret_val = -E1000_ERR_PHY; + } else { + ret_val = e1000_get_phy_id(hw); + } + + /* restore previous sfp cage power state */ + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_sgmii_82575 - Performs a PHY reset + * @hw: pointer to the HW structure + * + * Resets the PHY using the serial gigabit media independent interface. + **/ +static s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + struct e1000_phy_info *phy = &hw->phy; + + DEBUGFUNC("e1000_phy_hw_reset_sgmii_82575"); + + /* + * This isn't a true "hard" reset, but is the only reset + * available to us at this time. + */ + + DEBUGOUT("Soft resetting SGMII attached PHY...\n"); + + if (!(hw->phy.ops.write_reg)) + goto out; + + /* + * SFP documentation requires the following to configure the SPF module + * to work on SGMII. No further documentation is given. + */ + ret_val = hw->phy.ops.write_reg(hw, 0x1B, 0x8084); + if (ret_val) + goto out; + + ret_val = hw->phy.ops.commit(hw); + if (ret_val) + goto out; + + if (phy->id == M88E1512_E_PHY_ID) + ret_val = e1000_initialize_M88E1512_phy(hw); +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82575 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: true to enable LPLU, false to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +static s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82575"); + + if (!(hw->phy.ops.read_reg)) + goto out; + + ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + goto out; + + if (active) { + data |= IGP02E1000_PM_D0_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &data); + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else { + data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82580 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: true to enable LPLU, false to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +static s32 e1000_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82580"); + + data = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + + if (active) { + data |= E1000_82580_PM_D0_LPLU; + + /* When LPLU is enabled, we should disable SmartSpeed */ + data &= ~E1000_82580_PM_SPD; + } else { + data &= ~E1000_82580_PM_D0_LPLU; + + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) + data |= E1000_82580_PM_SPD; + else if (phy->smart_speed == e1000_smart_speed_off) + data &= ~E1000_82580_PM_SPD; + } + + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, data); + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state_82580 - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 e1000_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_82580"); + + data = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + + if (!active) { + data &= ~E1000_82580_PM_D3_LPLU; + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) + data |= E1000_82580_PM_SPD; + else if (phy->smart_speed == e1000_smart_speed_off) + data &= ~E1000_82580_PM_SPD; + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= E1000_82580_PM_D3_LPLU; + /* When LPLU is enabled, we should disable SmartSpeed */ + data &= ~E1000_82580_PM_SPD; + } + + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, data); + return E1000_SUCCESS; +} + +/** + * e1000_acquire_nvm_82575 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclusive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_acquire_nvm_82575"); + + ret_val = e1000_acquire_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + if (ret_val) + goto out; + + /* + * Check if there is some access + * error this access may hook on + */ + if (hw->mac.type == e1000_i350) { + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + if (eecd & (E1000_EECD_BLOCKED | E1000_EECD_ABORT | + E1000_EECD_TIMEOUT)) { + /* Clear all access error flags */ + E1000_WRITE_REG(hw, E1000_EECD, eecd | + E1000_EECD_ERROR_CLR); + DEBUGOUT("Nvm bit banging access error detected and cleared.\n"); + } + } + + if (hw->mac.type == e1000_82580) { + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + if (eecd & E1000_EECD_BLOCKED) { + /* Clear access error flag */ + E1000_WRITE_REG(hw, E1000_EECD, eecd | + E1000_EECD_BLOCKED); + DEBUGOUT("Nvm bit banging access error detected and cleared.\n"); + } + } + + ret_val = e1000_acquire_nvm_generic(hw); + if (ret_val) + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + +out: + return ret_val; +} + +/** + * e1000_release_nvm_82575 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +static void e1000_release_nvm_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_82575"); + + e1000_release_nvm_generic(hw); + + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_82575 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; + + DEBUGFUNC("e1000_acquire_swfw_sync_82575"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_generic(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* + * Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) + */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_82575 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_82575"); + + while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS) + ; /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_get_cfg_done_82575 - Read config done bit + * @hw: pointer to the HW structure + * + * Read the management control register for the config done bit for + * completion status. NOTE: silicon which is EEPROM-less will fail trying + * to read the config done bit, so an error is *ONLY* logged and returns + * E1000_SUCCESS. If we were to return with error, EEPROM-less silicon + * would not be able to be reset or change link. + **/ +static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + u32 mask = E1000_NVM_CFG_DONE_PORT_0; + + DEBUGFUNC("e1000_get_cfg_done_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_NVM_CFG_DONE_PORT_1; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_NVM_CFG_DONE_PORT_2; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_NVM_CFG_DONE_PORT_3; + while (timeout) { + if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask) + break; + msec_delay(1); + timeout--; + } + if (!timeout) + DEBUGOUT("MNG configuration cycle has not completed.\n"); + + /* If EEPROM is not marked present, init the PHY manually */ + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) && + (hw->phy.type == e1000_phy_igp_3)) + e1000_phy_init_script_igp3(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_get_link_up_info_82575 - Get link speed/duplex info + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * This is a wrapper function, if using the serial gigabit media independent + * interface, use PCS to retrieve the link speed and duplex information. + * Otherwise, use the generic function to get the link speed and duplex info. + **/ +static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + s32 ret_val; + + DEBUGFUNC("e1000_get_link_up_info_82575"); + + if (hw->phy.media_type != e1000_media_type_copper) + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, speed, + duplex); + else + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, + duplex); + + return ret_val; +} + +/** + * e1000_check_for_link_82575 - Check for link + * @hw: pointer to the HW structure + * + * If sgmii is enabled, then use the pcs register to determine link, otherwise + * use the generic interface for determining link. + **/ +static s32 e1000_check_for_link_82575(struct e1000_hw *hw) +{ + s32 ret_val; + u16 speed, duplex; + + DEBUGFUNC("e1000_check_for_link_82575"); + + if (hw->phy.media_type != e1000_media_type_copper) { + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, &speed, + &duplex); + /* + * Use this flag to determine if link needs to be checked or + * not. If we have link clear the flag so that we do not + * continue to check for link. + */ + hw->mac.get_link_status = !hw->mac.serdes_has_link; + + /* + * Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) + DEBUGOUT("Error configuring flow control\n"); + } else { + ret_val = e1000_check_for_copper_link_generic(hw); + } + + return ret_val; +} + +/** + * e1000_check_for_link_media_swap - Check which M88E1112 interface linked + * @hw: pointer to the HW structure + * + * Poll the M88E1112 interfaces to see which interface achieved link. + */ +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + u8 port = 0; + + DEBUGFUNC("e1000_check_for_link_media_swap"); + + /* Check for copper. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_COPPER; + + /* Check for other. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 1); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_OTHER; + + /* Determine if a swap needs to happen. */ + if (port && (hw->dev_spec._82575.media_port != port)) { + hw->dev_spec._82575.media_port = port; + hw->dev_spec._82575.media_changed = true; + } + + if (port == E1000_MEDIA_PORT_COPPER) { + /* reset page to 0 */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + e1000_check_for_link_82575(hw); + } else { + e1000_check_for_link_82575(hw); + /* reset page to 0 */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_power_up_serdes_link_82575 - Power up the serdes link after shutdown + * @hw: pointer to the HW structure + **/ +static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_power_up_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return; + + /* Enable PCS to turn on link */ + reg = E1000_READ_REG(hw, E1000_PCS_CFG0); + reg |= E1000_PCS_CFG_PCS_EN; + E1000_WRITE_REG(hw, E1000_PCS_CFG0, reg); + + /* Power up the laser */ + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + + /* flush the write to verify completion */ + E1000_WRITE_FLUSH(hw); + msec_delay(1); +} + +/** + * e1000_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Using the physical coding sub-layer (PCS), retrieve the current speed and + * duplex, then store the values in the pointers provided. + **/ +static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, + u16 *speed, u16 *duplex) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 pcs; + u32 status; + + DEBUGFUNC("e1000_get_pcs_speed_and_duplex_82575"); + + /* + * Read the PCS Status register for link state. For non-copper mode, + * the status register is not accurate. The PCS status register is + * used instead. + */ + pcs = E1000_READ_REG(hw, E1000_PCS_LSTAT); + + /* + * The link up bit determines when link is up on autoneg. + */ + if (pcs & E1000_PCS_LSTS_LINK_OK) { + mac->serdes_has_link = true; + + /* Detect and store PCS speed */ + if (pcs & E1000_PCS_LSTS_SPEED_1000) + *speed = SPEED_1000; + else if (pcs & E1000_PCS_LSTS_SPEED_100) + *speed = SPEED_100; + else + *speed = SPEED_10; + + /* Detect and store PCS duplex */ + if (pcs & E1000_PCS_LSTS_DUPLEX_FULL) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + /* Check if it is an I354 2.5Gb backplane connection. */ + if (mac->type == e1000_i354) { + status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + !(status & E1000_STATUS_2P5_SKU_OVER)) { + *speed = SPEED_2500; + *duplex = FULL_DUPLEX; + DEBUGOUT("2500 Mbs, "); + DEBUGOUT("Full Duplex\n"); + } + } + + } else { + mac->serdes_has_link = false; + *speed = 0; + *duplex = 0; + } + + return E1000_SUCCESS; +} + +/** + * e1000_shutdown_serdes_link_82575 - Remove link during power down + * @hw: pointer to the HW structure + * + * In the case of serdes shut down sfp and PCS on driver unload + * when management pass thru is not enabled. + **/ +void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_shutdown_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return; + + if (!e1000_enable_mng_pass_thru(hw)) { + /* Disable PCS to turn off link */ + reg = E1000_READ_REG(hw, E1000_PCS_CFG0); + reg &= ~E1000_PCS_CFG_PCS_EN; + E1000_WRITE_REG(hw, E1000_PCS_CFG0, reg); + + /* shutdown the laser */ + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg |= E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + + /* flush the write to verify completion */ + E1000_WRITE_FLUSH(hw); + msec_delay(1); + } + + return; +} + +/** + * e1000_reset_hw_82575 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. + **/ +static s32 e1000_reset_hw_82575(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_reset_hw_82575"); + + /* + * Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + + /* set the completion timeout for interface */ + ret_val = e1000_set_pcie_completion_timeout(hw); + if (ret_val) + DEBUGOUT("PCI-E Set completion timeout has failed.\n"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + /* If EEPROM is not present, run manual init scripts */ + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES)) + e1000_reset_init_script_82575(hw); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_READ_REG(hw, E1000_ICR); + + /* Install any alternate MAC address into RAR0 */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + + return ret_val; +} + +/** + * e1000_init_hw_82575 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. + **/ +s32 e1000_init_hw_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + u16 i, rar_count = mac->rar_entry_count; + + DEBUGFUNC("e1000_init_hw_82575"); + + /* Initialize identification LED */ + ret_val = mac->ops.id_led_init(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + /* This is not fatal and we should not stop init due to this */ + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + mac->ops.clear_vfta(hw); + + /* Setup the receive address */ + e1000_init_rx_addrs_generic(hw, rar_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Zero out the Unicast HASH table */ + DEBUGOUT("Zeroing the UTA\n"); + for (i = 0; i < mac->uta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_UTA, i, 0); + + /* Setup link and flow control */ + ret_val = mac->ops.setup_link(hw); + + /* Set the default MTU size */ + hw->dev_spec._82575.mtu = 1500; + + /* + * Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82575(hw); + + return ret_val; +} + +/** + * e1000_setup_copper_link_82575 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Configures the link for auto-neg or forced speed and duplex. Then we check + * for link, once link is established calls to configure collision distance + * and flow control are called. + **/ +static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + u32 phpm_reg; + + DEBUGFUNC("e1000_setup_copper_link_82575"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Clear Go Link Disconnect bit on supported devices */ + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + case e1000_i210: + case e1000_i211: + phpm_reg = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + phpm_reg &= ~E1000_82580_PM_GO_LINKD; + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, phpm_reg); + break; + default: + break; + } + + ret_val = e1000_setup_serdes_link_82575(hw); + if (ret_val) + goto out; + + if (e1000_sgmii_active_82575(hw) && !hw->phy.reset_disable) { + /* allow time for SFP cage time to power up phy */ + msec_delay(300); + + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + goto out; + } + } + switch (hw->phy.type) { + case e1000_phy_i210: + case e1000_phy_m88: + switch (hw->phy.id) { + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I210_I_PHY_ID: + ret_val = e1000_copper_link_setup_m88_gen2(hw); + break; + default: + ret_val = e1000_copper_link_setup_m88(hw); + break; + } + break; + case e1000_phy_igp_3: + ret_val = e1000_copper_link_setup_igp(hw); + break; + case e1000_phy_82580: + ret_val = e1000_copper_link_setup_82577(hw); + break; + default: + ret_val = -E1000_ERR_PHY; + break; + } + + if (ret_val) + goto out; + + ret_val = e1000_setup_copper_link_generic(hw); +out: + return ret_val; +} + +/** + * e1000_setup_serdes_link_82575 - Setup link for serdes + * @hw: pointer to the HW structure + * + * Configure the physical coding sub-layer (PCS) link. The PCS link is + * used on copper connections where the serialized gigabit media independent + * interface (sgmii), or serdes fiber is being used. Configures the link + * for auto-negotiation or forces speed/duplex. + **/ +static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw) +{ + u32 ctrl_ext, ctrl_reg, reg, anadv_reg; + bool pcs_autoneg; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_setup_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return ret_val; + + /* + * On the 82575, SerDes loopback mode persists until it is + * explicitly turned off or a power cycle is performed. A read to + * the register does not indicate its status. Therefore, we ensure + * loopback mode is disabled during initialization. + */ + E1000_WRITE_REG(hw, E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK); + + /* power on the sfp cage if present */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + ctrl_reg = E1000_READ_REG(hw, E1000_CTRL); + ctrl_reg |= E1000_CTRL_SLU; + + /* set both sw defined pins on 82575/82576*/ + if (hw->mac.type == e1000_82575 || hw->mac.type == e1000_82576) + ctrl_reg |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1; + + reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + + /* default pcs_autoneg to the same setting as mac autoneg */ + pcs_autoneg = hw->mac.autoneg; + + switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* sgmii mode lets the phy handle forcing speed/duplex */ + pcs_autoneg = true; + /* autoneg time out should be disabled for SGMII mode */ + reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT); + break; + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + /* disable PCS autoneg and support parallel detect only */ + pcs_autoneg = false; + /* fall through to default case */ + default: + if (hw->mac.type == e1000_82575 || + hw->mac.type == e1000_82576) { + ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (data & E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT) + pcs_autoneg = false; + } + + /* + * non-SGMII modes only supports a speed of 1000/Full for the + * link so it is best to just force the MAC and let the pcs + * link either autoneg or be forced to 1000/Full + */ + ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD | + E1000_CTRL_FD | E1000_CTRL_FRCDPX; + + /* set speed of 1000/Full if speed/duplex is forced */ + reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL; + break; + } + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl_reg); + + /* + * New SerDes mode allows for forcing speed or autonegotiating speed + * at 1gb. Autoneg should be default set by most drivers. This is the + * mode that will be compatible with older link partners and switches. + * However, both are supported by the hardware and some drivers/tools. + */ + reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP | + E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK); + + if (pcs_autoneg) { + /* Set PCS register for autoneg */ + reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ + E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ + + /* Disable force flow control for autoneg */ + reg &= ~E1000_PCS_LCTL_FORCE_FCTRL; + + /* Configure flow control advertisement for autoneg */ + anadv_reg = E1000_READ_REG(hw, E1000_PCS_ANADV); + anadv_reg &= ~(E1000_TXCW_ASM_DIR | E1000_TXCW_PAUSE); + + switch (hw->fc.requested_mode) { + case e1000_fc_full: + case e1000_fc_rx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + anadv_reg |= E1000_TXCW_PAUSE; + break; + case e1000_fc_tx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + break; + default: + break; + } + + E1000_WRITE_REG(hw, E1000_PCS_ANADV, anadv_reg); + + DEBUGOUT1("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg); + } else { + /* Set PCS register for forced link */ + reg |= E1000_PCS_LCTL_FSD; /* Force Speed */ + + /* Force flow control for forced link */ + reg |= E1000_PCS_LCTL_FORCE_FCTRL; + + DEBUGOUT1("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg); + } + + E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg); + + if (!pcs_autoneg && !e1000_sgmii_active_82575(hw)) + e1000_force_mac_fc_generic(hw); + + return ret_val; +} + +/** + * e1000_get_media_type_82575 - derives current media type. + * @hw: pointer to the HW structure + * + * The media type is chosen reflecting few settings. + * The following are taken into account: + * - link mode set in the current port Init Control Word #3 + * - current link mode settings in CSR register + * - MDIO vs. I2C PHY control interface chosen + * - SFP module media type + **/ +static s32 e1000_get_media_type_82575(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext = 0; + u32 link_mode = 0; + + /* Set internal phy as default */ + dev_spec->sgmii_active = false; + dev_spec->module_plugged = false; + + /* Get CSR setting */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + + /* extract link mode setting */ + link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK; + + switch (link_mode) { + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + hw->phy.media_type = e1000_media_type_internal_serdes; + break; + case E1000_CTRL_EXT_LINK_MODE_GMII: + hw->phy.media_type = e1000_media_type_copper; + break; + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* Get phy control interface type set (MDIO vs. I2C)*/ + if (e1000_sgmii_uses_mdio_82575(hw)) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + break; + } + /* fall through for I2C based SGMII */ + case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: + /* read media type from SFP EEPROM */ +#ifdef I2C_ENABLED + printk(KERN_INFO "igb_avb I2C enabled - set_sfp_media_type_82575() called"); + ret_val = e1000_set_sfp_media_type_82575(hw); +#else + printk(KERN_INFO "igb_avb I2C disabled - set_sfp_media_type_82575() not necessary"); + hw->phy.media_type = e1000_media_type_unknown; +#endif + if ((ret_val != E1000_SUCCESS) || + (hw->phy.media_type == e1000_media_type_unknown)) { + /* + * If media type was not identified then return media + * type defined by the CTRL_EXT settings. + */ + hw->phy.media_type = e1000_media_type_internal_serdes; + + if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + } + + break; + } + + /* do not change link mode for 100BaseFX */ + if (dev_spec->eth_flags.e100_base_fx) + break; + + /* change current link mode setting */ + ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK; + + if (hw->phy.media_type == e1000_media_type_copper) + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII; + else + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES; + + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + break; + } + + return ret_val; +} + +/** + * e1000_set_sfp_media_type_82575 - derives SFP module media type. + * @hw: pointer to the HW structure + * + * The media type is chosen based on SFP module. + * compatibility flags retrieved from SFP ID EEPROM. + **/ +#ifdef I2C_ENABLED +static s32 e1000_set_sfp_media_type_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_ERR_CONFIG; + u32 ctrl_ext = 0; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct sfp_e1000_flags *eth_flags = &dev_spec->eth_flags; + u8 tranceiver_type = 0; + s32 timeout = 3; + + /* Turn I2C interface ON and power on sfp cage */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA); + + E1000_WRITE_FLUSH(hw); + + /* Read SFP module data */ + while (timeout) { + ret_val = e1000_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET), + &tranceiver_type); + if (ret_val == E1000_SUCCESS) + break; + msec_delay(100); + timeout--; + } + if (ret_val != E1000_SUCCESS) + goto out; + + ret_val = e1000_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET), + (u8 *)eth_flags); + if (ret_val != E1000_SUCCESS) + goto out; + + /* Check if there is some SFP module plugged and powered */ + if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) || + (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) { + dev_spec->module_plugged = true; + if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) { + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e100_base_fx) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e1000_base_t) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_copper; + } else { + hw->phy.media_type = e1000_media_type_unknown; + DEBUGOUT("PHY module has not been recognized\n"); + goto out; + } + } else { + hw->phy.media_type = e1000_media_type_unknown; + } + ret_val = E1000_SUCCESS; +out: + /* Restore I2C interface setting */ + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + return ret_val; +} +#endif +/** + * e1000_valid_led_default_82575 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +static s32 e1000_valid_led_default_82575(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_82575"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { + switch (hw->phy.media_type) { + case e1000_media_type_internal_serdes: + *data = ID_LED_DEFAULT_82575_SERDES; + break; + case e1000_media_type_copper: + default: + *data = ID_LED_DEFAULT; + break; + } + } +out: + return ret_val; +} + +/** + * e1000_sgmii_active_82575 - Return sgmii state + * @hw: pointer to the HW structure + * + * 82575 silicon has a serialized gigabit media independent interface (sgmii) + * which can be enabled for use in the embedded applications. Simply + * return the current state of the sgmii interface. + **/ +static bool e1000_sgmii_active_82575(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + return dev_spec->sgmii_active; +} + +/** + * e1000_reset_init_script_82575 - Inits HW defaults after reset + * @hw: pointer to the HW structure + * + * Inits recommended HW defaults after a reset when there is no EEPROM + * detected. This is only for the 82575. + **/ +static s32 e1000_reset_init_script_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_reset_init_script_82575"); + + if (hw->mac.type == e1000_82575) { + DEBUGOUT("Running reset init script for 82575\n"); + /* SerDes configuration via SERDESCTRL */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x00, 0x0C); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x01, 0x78); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x1B, 0x23); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x23, 0x15); + + /* CCM configuration via CCMCTL register */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_CCMCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_CCMCTL, 0x10, 0x00); + + /* PCIe lanes configuration */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x00, 0xEC); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x61, 0xDF); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x34, 0x05); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x2F, 0x81); + + /* PCIe PLL Configuration */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x02, 0x47); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x10, 0x00); + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr_82575 - Read device MAC address + * @hw: pointer to the HW structure + **/ +static s32 e1000_read_mac_addr_82575(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_mac_addr_82575"); + + /* + * If there's an alternate MAC address place it in RAR0 + * so that it will override the Si installed default perm + * address. + */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_mac_addr_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_config_collision_dist_82575 - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +static void e1000_config_collision_dist_82575(struct e1000_hw *hw) +{ + u32 tctl_ext; + + DEBUGFUNC("e1000_config_collision_dist_82575"); + + tctl_ext = E1000_READ_REG(hw, E1000_TCTL_EXT); + + tctl_ext &= ~E1000_TCTL_EXT_COLD; + tctl_ext |= E1000_COLLISION_DISTANCE << E1000_TCTL_EXT_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL_EXT, tctl_ext); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_power_down_phy_copper_82575 - Remove link during PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, remove the link. + **/ +static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + + if (!(phy->ops.check_reset_block)) + return; + + /* If the management interface is not enabled, then power down */ + if (!(e1000_enable_mng_pass_thru(hw) || phy->ops.check_reset_block(hw))) + e1000_power_down_phy_copper(hw); + + return; +} + +/** + * e1000_clear_hw_cntrs_82575 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_clear_hw_cntrs_82575"); + + e1000_clear_hw_cntrs_base_generic(hw); + + E1000_READ_REG(hw, E1000_PRC64); + E1000_READ_REG(hw, E1000_PRC127); + E1000_READ_REG(hw, E1000_PRC255); + E1000_READ_REG(hw, E1000_PRC511); + E1000_READ_REG(hw, E1000_PRC1023); + E1000_READ_REG(hw, E1000_PRC1522); + E1000_READ_REG(hw, E1000_PTC64); + E1000_READ_REG(hw, E1000_PTC127); + E1000_READ_REG(hw, E1000_PTC255); + E1000_READ_REG(hw, E1000_PTC511); + E1000_READ_REG(hw, E1000_PTC1023); + E1000_READ_REG(hw, E1000_PTC1522); + + E1000_READ_REG(hw, E1000_ALGNERRC); + E1000_READ_REG(hw, E1000_RXERRC); + E1000_READ_REG(hw, E1000_TNCRS); + E1000_READ_REG(hw, E1000_CEXTERR); + E1000_READ_REG(hw, E1000_TSCTC); + E1000_READ_REG(hw, E1000_TSCTFC); + + E1000_READ_REG(hw, E1000_MGTPRC); + E1000_READ_REG(hw, E1000_MGTPDC); + E1000_READ_REG(hw, E1000_MGTPTC); + + E1000_READ_REG(hw, E1000_IAC); + E1000_READ_REG(hw, E1000_ICRXOC); + + E1000_READ_REG(hw, E1000_ICRXPTC); + E1000_READ_REG(hw, E1000_ICRXATC); + E1000_READ_REG(hw, E1000_ICTXPTC); + E1000_READ_REG(hw, E1000_ICTXATC); + E1000_READ_REG(hw, E1000_ICTXQEC); + E1000_READ_REG(hw, E1000_ICTXQMTC); + E1000_READ_REG(hw, E1000_ICRXDMTC); + + E1000_READ_REG(hw, E1000_CBTMPC); + E1000_READ_REG(hw, E1000_HTDPMC); + E1000_READ_REG(hw, E1000_CBRMPC); + E1000_READ_REG(hw, E1000_RPTHC); + E1000_READ_REG(hw, E1000_HGPTC); + E1000_READ_REG(hw, E1000_HTCBDPC); + E1000_READ_REG(hw, E1000_HGORCL); + E1000_READ_REG(hw, E1000_HGORCH); + E1000_READ_REG(hw, E1000_HGOTCL); + E1000_READ_REG(hw, E1000_HGOTCH); + E1000_READ_REG(hw, E1000_LENERRS); + + /* This register should not be read in copper configurations */ + if ((hw->phy.media_type == e1000_media_type_internal_serdes) || + e1000_sgmii_active_82575(hw)) + E1000_READ_REG(hw, E1000_SCVPC); +} + +/** + * e1000_rx_fifo_flush_82575 - Clean rx fifo after Rx enable + * @hw: pointer to the HW structure + * + * After Rx enable, if manageability is enabled then there is likely some + * bad data at the start of the fifo and possibly in the DMA fifo. This + * function clears the fifos and flushes any packets that came in as rx was + * being enabled. + **/ +void e1000_rx_fifo_flush_82575(struct e1000_hw *hw) +{ + u32 rctl, rlpml, rxdctl[4], rfctl, temp_rctl, rx_enabled; + int i, ms_wait; + + DEBUGFUNC("e1000_rx_fifo_flush_82575"); + + /* disable IPv6 options as per hardware errata */ + rfctl = E1000_READ_REG(hw, E1000_RFCTL); + rfctl |= E1000_RFCTL_IPV6_EX_DIS; + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl); + + if (hw->mac.type != e1000_82575 || + !(E1000_READ_REG(hw, E1000_MANC) & E1000_MANC_RCV_TCO_EN)) + return; + + /* Disable all Rx queues */ + for (i = 0; i < 4; i++) { + rxdctl[i] = E1000_READ_REG(hw, E1000_RXDCTL(i)); + E1000_WRITE_REG(hw, E1000_RXDCTL(i), + rxdctl[i] & ~E1000_RXDCTL_QUEUE_ENABLE); + } + /* Poll all queues to verify they have shut down */ + for (ms_wait = 0; ms_wait < 10; ms_wait++) { + msec_delay(1); + rx_enabled = 0; + for (i = 0; i < 4; i++) + rx_enabled |= E1000_READ_REG(hw, E1000_RXDCTL(i)); + if (!(rx_enabled & E1000_RXDCTL_QUEUE_ENABLE)) + break; + } + + if (ms_wait == 10) + DEBUGOUT("Queue disable timed out after 10ms\n"); + + /* Clear RLPML, RCTL.SBP, RFCTL.LEF, and set RCTL.LPE so that all + * incoming packets are rejected. Set enable and wait 2ms so that + * any packet that was coming in as RCTL.EN was set is flushed + */ + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl & ~E1000_RFCTL_LEF); + + rlpml = E1000_READ_REG(hw, E1000_RLPML); + E1000_WRITE_REG(hw, E1000_RLPML, 0); + + rctl = E1000_READ_REG(hw, E1000_RCTL); + temp_rctl = rctl & ~(E1000_RCTL_EN | E1000_RCTL_SBP); + temp_rctl |= E1000_RCTL_LPE; + + E1000_WRITE_REG(hw, E1000_RCTL, temp_rctl); + E1000_WRITE_REG(hw, E1000_RCTL, temp_rctl | E1000_RCTL_EN); + E1000_WRITE_FLUSH(hw); + msec_delay(2); + + /* Enable Rx queues that were previously enabled and restore our + * previous state + */ + for (i = 0; i < 4; i++) + E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl[i]); + E1000_WRITE_REG(hw, E1000_RCTL, rctl); + E1000_WRITE_FLUSH(hw); + + E1000_WRITE_REG(hw, E1000_RLPML, rlpml); + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl); + + /* Flush receive errors generated by workaround */ + E1000_READ_REG(hw, E1000_ROC); + E1000_READ_REG(hw, E1000_RNBC); + E1000_READ_REG(hw, E1000_MPC); +} + +/** + * e1000_set_pcie_completion_timeout - set pci-e completion timeout + * @hw: pointer to the HW structure + * + * The defaults for 82575 and 82576 should be in the range of 50us to 50ms, + * however the hardware default for these parts is 500us to 1ms which is less + * than the 10ms recommended by the pci-e spec. To address this we need to + * increase the value to either 10ms to 200ms for capability version 1 config, + * or 16ms to 55ms for version 2. + **/ +static s32 e1000_set_pcie_completion_timeout(struct e1000_hw *hw) +{ + u32 gcr = E1000_READ_REG(hw, E1000_GCR); + s32 ret_val = E1000_SUCCESS; + u16 pcie_devctl2; + + /* only take action if timeout value is defaulted to 0 */ + if (gcr & E1000_GCR_CMPL_TMOUT_MASK) + goto out; + + /* + * if capababilities version is type 1 we can write the + * timeout of 10ms to 200ms through the GCR register + */ + if (!(gcr & E1000_GCR_CAP_VER2)) { + gcr |= E1000_GCR_CMPL_TMOUT_10ms; + goto out; + } + + /* + * for version 2 capabilities we need to write the config space + * directly in order to set the completion timeout value for + * 16ms to 55ms + */ + ret_val = e1000_read_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2, + &pcie_devctl2); + if (ret_val) + goto out; + + pcie_devctl2 |= PCIE_DEVICE_CONTROL2_16ms; + + ret_val = e1000_write_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2, + &pcie_devctl2); +out: + /* disable completion timeout resend */ + gcr &= ~E1000_GCR_CMPL_TMOUT_RESEND; + + E1000_WRITE_REG(hw, E1000_GCR, gcr); + return ret_val; +} + +/** + * e1000_vmdq_set_anti_spoofing_pf - enable or disable anti-spoofing + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * @pf: Physical Function pool - do not set anti-spoofing for the PF + * + * enables/disables L2 switch anti-spoofing functionality. + **/ +void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf) +{ + u32 reg_val, reg_offset; + + switch (hw->mac.type) { + case e1000_82576: + reg_offset = E1000_DTXSWC; + break; + case e1000_i350: + case e1000_i354: + reg_offset = E1000_TXSWC; + break; + default: + return; + } + + reg_val = E1000_READ_REG(hw, reg_offset); + if (enable) { + reg_val |= (E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + /* The PF can spoof - it has to in order to + * support emulation mode NICs + */ + reg_val ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); + } else { + reg_val &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + } + E1000_WRITE_REG(hw, reg_offset, reg_val); +} + +/** + * e1000_vmdq_set_loopback_pf - enable or disable vmdq loopback + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * + * enables/disables L2 switch loopback functionality. + **/ +void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable) +{ + u32 dtxswc; + + switch (hw->mac.type) { + case e1000_82576: + dtxswc = E1000_READ_REG(hw, E1000_DTXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + E1000_WRITE_REG(hw, E1000_DTXSWC, dtxswc); + break; + case e1000_i350: + case e1000_i354: + dtxswc = E1000_READ_REG(hw, E1000_TXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + E1000_WRITE_REG(hw, E1000_TXSWC, dtxswc); + break; + default: + /* Currently no other hardware supports loopback */ + break; + } + + +} + +/** + * e1000_vmdq_set_replication_pf - enable or disable vmdq replication + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * + * enables/disables replication of packets across multiple pools. + **/ +void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable) +{ + u32 vt_ctl = E1000_READ_REG(hw, E1000_VT_CTL); + + if (enable) + vt_ctl |= E1000_VT_CTL_VM_REPL_EN; + else + vt_ctl &= ~E1000_VT_CTL_VM_REPL_EN; + + E1000_WRITE_REG(hw, E1000_VT_CTL, vt_ctl); +} + +/** + * e1000_read_phy_reg_82580 - Read 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control register in the PHY at offset and stores the + * information read to data. + **/ +static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_82580"); + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_82580 - Write 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_82580"); + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_reset_mdicnfg_82580 - Reset MDICNFG destination and com_mdio bits + * @hw: pointer to the HW structure + * + * This resets the the MDICNFG.Destination and MDICNFG.Com_MDIO bits based on + * the values found in the EEPROM. This addresses an issue in which these + * bits are not restored from EEPROM after reset. + **/ +static s32 e1000_reset_mdicnfg_82580(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u32 mdicnfg; + u16 nvm_data = 0; + + DEBUGFUNC("e1000_reset_mdicnfg_82580"); + + if (hw->mac.type != e1000_82580) + goto out; + if (!e1000_sgmii_active_82575(hw)) + goto out; + + ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A + + NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1, + &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG); + if (nvm_data & NVM_WORD24_EXT_MDIO) + mdicnfg |= E1000_MDICNFG_EXT_MDIO; + if (nvm_data & NVM_WORD24_COM_MDIO) + mdicnfg |= E1000_MDICNFG_COM_MDIO; + E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg); +out: + return ret_val; +} + +/** + * e1000_reset_hw_82580 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets function or entire device (all ports, etc.) + * to a known state. + **/ +static s32 e1000_reset_hw_82580(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + /* BH SW mailbox bit in SW_FW_SYNC */ + u16 swmbsw_mask = E1000_SW_SYNCH_MB; + u32 ctrl; + bool global_device_reset = hw->dev_spec._82575.global_device_reset; + + DEBUGFUNC("e1000_reset_hw_82580"); + + hw->dev_spec._82575.global_device_reset = false; + + /* 82580 does not reliably do global_device_reset due to hw errata */ + if (hw->mac.type == e1000_82580) + global_device_reset = false; + + /* Get current control state. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* + * Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + /* Determine whether or not a global dev reset is requested */ + if (global_device_reset && hw->mac.ops.acquire_swfw_sync(hw, + swmbsw_mask)) + global_device_reset = false; + + if (global_device_reset && !(E1000_READ_REG(hw, E1000_STATUS) & + E1000_STAT_DEV_RST_SET)) + ctrl |= E1000_CTRL_DEV_RST; + else + ctrl |= E1000_CTRL_RST; + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + switch (hw->device_id) { + case E1000_DEV_ID_DH89XXCC_SGMII: + break; + default: + E1000_WRITE_FLUSH(hw); + break; + } + + /* Add delay to insure DEV_RST or RST has time to complete */ + msec_delay(5); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + /* clear global device reset status bit */ + E1000_WRITE_REG(hw, E1000_STATUS, E1000_STAT_DEV_RST_SET); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_READ_REG(hw, E1000_ICR); + + ret_val = e1000_reset_mdicnfg_82580(hw); + if (ret_val) + DEBUGOUT("Could not reset MDICNFG based on EEPROM\n"); + + /* Install any alternate MAC address into RAR0 */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + + /* Release semaphore */ + if (global_device_reset) + hw->mac.ops.release_swfw_sync(hw, swmbsw_mask); + + return ret_val; +} + +/** + * e1000_rxpbs_adjust_82580 - adjust RXPBS value to reflect actual Rx PBA size + * @data: data received by reading RXPBS register + * + * The 82580 uses a table based approach for packet buffer allocation sizes. + * This function converts the retrieved value into the correct table value + * 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 + * 0x0 36 72 144 1 2 4 8 16 + * 0x8 35 70 140 rsv rsv rsv rsv rsv + */ +u16 e1000_rxpbs_adjust_82580(u32 data) +{ + u16 ret_val = 0; + + if (data < E1000_82580_RXPBS_TABLE_SIZE) + ret_val = e1000_82580_rxpbs_table[data]; + + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_with_offset - Validate EEPROM + * checksum + * @hw: pointer to the HW structure + * @offset: offset in words of the checksum protected region + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +{ + s32 ret_val = E1000_SUCCESS; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_validate_nvm_checksum_with_offset"); + + for (i = offset; i < ((NVM_CHECKSUM_REG + offset) + 1); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + checksum += nvm_data; + } + + if (checksum != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_with_offset - Update EEPROM + * checksum + * @hw: pointer to the HW structure + * @offset: offset in words of the checksum protected region + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +s32 e1000_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum_with_offset"); + + for (i = offset; i < (NVM_CHECKSUM_REG + offset); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = hw->nvm.ops.write(hw, (NVM_CHECKSUM_REG + offset), 1, + &checksum); + if (ret_val) + DEBUGOUT("NVM Write Error while updating checksum.\n"); + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_82580 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM section checksum by reading/adding each word of + * the EEPROM and then verifies that the sum of the EEPROM is + * equal to 0xBABA. + **/ +static s32 e1000_validate_nvm_checksum_82580(struct e1000_hw *hw) +{ + s32 ret_val; + u16 eeprom_regions_count = 1; + u16 j, nvm_data; + u16 nvm_offset; + + DEBUGFUNC("e1000_validate_nvm_checksum_82580"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (nvm_data & NVM_COMPATIBILITY_BIT_MASK) { + /* if chekcsums compatibility bit is set validate checksums + * for all 4 ports. */ + eeprom_regions_count = 4; + } + + for (j = 0; j < eeprom_regions_count; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_validate_nvm_checksum_with_offset(hw, + nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_82580 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM section checksums for all 4 ports by reading/adding + * each word of the EEPROM up to the checksum. Then calculates the EEPROM + * checksum and writes the value to the EEPROM. + **/ +static s32 e1000_update_nvm_checksum_82580(struct e1000_hw *hw) +{ + s32 ret_val; + u16 j, nvm_data; + u16 nvm_offset; + + DEBUGFUNC("e1000_update_nvm_checksum_82580"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum compatibility bit.\n"); + goto out; + } + + if (!(nvm_data & NVM_COMPATIBILITY_BIT_MASK)) { + /* set compatibility bit to validate checksums appropriately */ + nvm_data = nvm_data | NVM_COMPATIBILITY_BIT_MASK; + ret_val = hw->nvm.ops.write(hw, NVM_COMPATIBILITY_REG_3, 1, + &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Write Error while updating checksum compatibility bit.\n"); + goto out; + } + } + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_update_nvm_checksum_with_offset(hw, nvm_offset); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_i350 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM section checksum by reading/adding each word of + * the EEPROM and then verifies that the sum of the EEPROM is + * equal to 0xBABA. + **/ +static s32 e1000_validate_nvm_checksum_i350(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 j; + u16 nvm_offset; + + DEBUGFUNC("e1000_validate_nvm_checksum_i350"); + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_validate_nvm_checksum_with_offset(hw, + nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_i350 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM section checksums for all 4 ports by reading/adding + * each word of the EEPROM up to the checksum. Then calculates the EEPROM + * checksum and writes the value to the EEPROM. + **/ +static s32 e1000_update_nvm_checksum_i350(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 j; + u16 nvm_offset; + + DEBUGFUNC("e1000_update_nvm_checksum_i350"); + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_update_nvm_checksum_with_offset(hw, nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * __e1000_access_emi_reg - Read/write EMI register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: pointer to value to read/write from/to the EMI address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_emi_reg(struct e1000_hw *hw, u16 address, + u16 *data, bool read) +{ + s32 ret_val; + + DEBUGFUNC("__e1000_access_emi_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_EMIDATA, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIDATA, *data); + + return ret_val; +} + +/** + * e1000_read_emi_reg - Read Extended Management Interface register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data) +{ + DEBUGFUNC("e1000_read_emi_reg"); + + return __e1000_access_emi_reg(hw, addr, data, true); +} + +/** + * e1000_initialize_M88E1512_phy - Initialize M88E1512 PHY + * @hw: pointer to the HW structure + * + * Initialize Marverl 1512 to work correctly with Avoton. + **/ +s32 e1000_initialize_M88E1512_phy(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_initialize_M88E1512_phy"); + + /* Check if this is correct PHY. */ + if (phy->id != M88E1512_E_PHY_ID) + goto out; + + /* Switch to PHY page 0xFF. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FF); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x214B); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2144); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x0C28); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2146); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xB233); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x214D); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xCC0C); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2159); + if (ret_val) + goto out; + + /* Switch to PHY page 0xFB. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FB); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_3, 0x000D); + if (ret_val) + goto out; + + /* Switch to PHY page 0x12. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x12); + if (ret_val) + goto out; + + /* Change mode to SGMII-to-Copper */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_MODE, 0x8001); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + msec_delay(1000); +out: + return ret_val; +} + +/** + * e1000_set_eee_i350 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @adv1g: boolean flag enabling 1G EEE advertisement + * @adv100m: boolean flag enabling 100M EEE advertisement + * + * Enable/disable EEE based on setting in dev_spec structure. + * + **/ +s32 e1000_set_eee_i350(struct e1000_hw *hw, bool adv1G, bool adv100M) +{ + u32 ipcnfg, eeer; + + DEBUGFUNC("e1000_set_eee_i350"); + + if ((hw->mac.type < e1000_i350) || + (hw->phy.media_type != e1000_media_type_copper)) + goto out; + ipcnfg = E1000_READ_REG(hw, E1000_IPCNFG); + eeer = E1000_READ_REG(hw, E1000_EEER); + + /* enable or disable per user setting */ + if (!(hw->dev_spec._82575.eee_disable)) { + u32 eee_su = E1000_READ_REG(hw, E1000_EEE_SU); + + if (adv100M) + ipcnfg |= E1000_IPCNFG_EEE_100M_AN; + else + ipcnfg &= ~E1000_IPCNFG_EEE_100M_AN; + + if (adv1G) + ipcnfg |= E1000_IPCNFG_EEE_1G_AN; + else + ipcnfg &= ~E1000_IPCNFG_EEE_1G_AN; + + eeer |= (E1000_EEER_TX_LPI_EN | E1000_EEER_RX_LPI_EN | + E1000_EEER_LPI_FC); + + /* This bit should not be set in normal operation. */ + if (eee_su & E1000_EEE_SU_LPI_CLK_STP) + DEBUGOUT("LPI Clock Stop Bit should not be set!\n"); + } else { + ipcnfg &= ~(E1000_IPCNFG_EEE_1G_AN | E1000_IPCNFG_EEE_100M_AN); + eeer &= ~(E1000_EEER_TX_LPI_EN | E1000_EEER_RX_LPI_EN | + E1000_EEER_LPI_FC); + } + E1000_WRITE_REG(hw, E1000_IPCNFG, ipcnfg); + E1000_WRITE_REG(hw, E1000_EEER, eeer); + E1000_READ_REG(hw, E1000_IPCNFG); + E1000_READ_REG(hw, E1000_EEER); +out: + + return E1000_SUCCESS; +} + +/** + * e1000_set_eee_i354 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @adv1g: boolean flag enabling 1G EEE advertisement + * @adv100m: boolean flag enabling 100M EEE advertisement + * + * Enable/disable EEE legacy mode based on setting in dev_spec structure. + * + **/ +s32 e1000_set_eee_i354(struct e1000_hw *hw, bool adv1G, bool adv100M) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_set_eee_i354"); + + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + if (!hw->dev_spec._82575.eee_disable) { + /* Switch to PHY page 18. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 18); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1543_EEE_CTRL_1, + &phy_data); + if (ret_val) + goto out; + + phy_data |= E1000_M88E1543_EEE_CTRL_1_MS; + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_EEE_CTRL_1, + phy_data); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + /* Turn on EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + if (adv100M) + phy_data |= E1000_EEE_ADV_100_SUPPORTED; + else + phy_data &= ~E1000_EEE_ADV_100_SUPPORTED; + + if (adv1G) + phy_data |= E1000_EEE_ADV_1000_SUPPORTED; + else + phy_data &= ~E1000_EEE_ADV_1000_SUPPORTED; + + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } else { + /* Turn off EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + phy_data &= ~(E1000_EEE_ADV_100_SUPPORTED | + E1000_EEE_ADV_1000_SUPPORTED); + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } + +out: + return ret_val; +} + +/** + * e1000_get_eee_status_i354 - Get EEE status + * @hw: pointer to the HW structure + * @status: EEE status + * + * Get EEE status by guessing based on whether Tx or Rx LPI indications have + * been received. + **/ +s32 e1000_get_eee_status_i354(struct e1000_hw *hw, bool *status) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_get_eee_status_i354"); + + /* Check if EEE is supported on this device. */ + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + ret_val = e1000_read_xmdio_reg(hw, E1000_PCS_STATUS_ADDR_I354, + E1000_PCS_STATUS_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + *status = phy_data & (E1000_PCS_STATUS_TX_LPI_RCVD | + E1000_PCS_STATUS_RX_LPI_RCVD) ? true : false; + +out: + return ret_val; +} + +/* Due to a hw errata, if the host tries to configure the VFTA register + * while performing queries from the BMC or DMA, then the VFTA in some + * cases won't be written. + */ + +/** + * e1000_clear_vfta_i350 - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void e1000_clear_vfta_i350(struct e1000_hw *hw) +{ + u32 offset; + int i; + + DEBUGFUNC("e1000_clear_vfta_350"); + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + for (i = 0; i < 10; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_write_vfta_i350 - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: register offset in VLAN filter table + * @value: register value written to VLAN filter table + * + * Writes value at the given offset in the register array which stores + * the VLAN filter table. + **/ +void e1000_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value) +{ + int i; + + DEBUGFUNC("e1000_write_vfta_350"); + + for (i = 0; i < 10; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_set_i2c_bb - Enable I2C bit-bang + * @hw: pointer to the HW structure + * + * Enable I2C bit-bang interface + * + **/ +s32 e1000_set_i2c_bb(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext, i2cparams; + + DEBUGFUNC("e1000_set_i2c_bb"); + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_I2C_ENA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + + i2cparams = E1000_READ_REG(hw, E1000_I2CPARAMS); + i2cparams |= E1000_I2CBB_EN; + i2cparams |= E1000_I2C_DATA_OE_N; + i2cparams |= E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, i2cparams); + E1000_WRITE_FLUSH(hw); + + return ret_val; +} + +/** + * e1000_read_i2c_byte_generic - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @dev_addr: device address + * @data: value read + * + * Performs byte read operation over I2C interface at + * a specified device address. + **/ +s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + s32 status = E1000_SUCCESS; + u32 max_retry = 10; + u32 retry = 1; + u16 swfw_mask = 0; + + bool nack = true; + + DEBUGFUNC("e1000_read_i2c_byte_generic"); + + swfw_mask = E1000_SWFW_PHY0_SM; + + do { + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) + != E1000_SUCCESS) { + status = E1000_ERR_SWFW_SYNC; + goto read_byte_out; + } + + e1000_i2c_start(hw); + + /* Device Address and write indication */ + status = e1000_clock_out_i2c_byte(hw, dev_addr); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, byte_offset); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_start(hw); + + /* Device Address and read indication */ + status = e1000_clock_out_i2c_byte(hw, (dev_addr | 0x1)); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_clock_in_i2c_byte(hw, data); + + status = e1000_clock_out_i2c_bit(hw, nack); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_stop(hw); + break; + +fail: + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msec_delay(100); + e1000_i2c_bus_clear(hw); + retry++; + if (retry < max_retry) + DEBUGOUT("I2C byte read error - Retrying.\n"); + else + DEBUGOUT("I2C byte read error.\n"); + + } while (retry < max_retry); + + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + +read_byte_out: + + return status; +} + +/** + * e1000_write_i2c_byte_generic - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: value to write + * + * Performs byte write operation over I2C interface at + * a specified device address. + **/ +s32 e1000_write_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + s32 status = E1000_SUCCESS; + u32 max_retry = 1; + u32 retry = 0; + u16 swfw_mask = 0; + + DEBUGFUNC("e1000_write_i2c_byte_generic"); + + swfw_mask = E1000_SWFW_PHY0_SM; + + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS) { + status = E1000_ERR_SWFW_SYNC; + goto write_byte_out; + } + + do { + e1000_i2c_start(hw); + + status = e1000_clock_out_i2c_byte(hw, dev_addr); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, byte_offset); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, data); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_stop(hw); + break; + +fail: + e1000_i2c_bus_clear(hw); + retry++; + if (retry < max_retry) + DEBUGOUT("I2C byte write error - Retrying.\n"); + else + DEBUGOUT("I2C byte write error.\n"); + } while (retry < max_retry); + + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + +write_byte_out: + + return status; +} + +/** + * e1000_i2c_start - Sets I2C start condition + * @hw: pointer to hardware structure + * + * Sets I2C start condition (High -> Low on SDA while SCL is High) + **/ +static void e1000_i2c_start(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_i2c_start"); + + /* Start condition must begin with data and clock high */ + e1000_set_i2c_data(hw, &i2cctl, 1); + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Setup time for start condition (4.7us) */ + usec_delay(E1000_I2C_T_SU_STA); + + e1000_set_i2c_data(hw, &i2cctl, 0); + + /* Hold time for start condition (4us) */ + usec_delay(E1000_I2C_T_HD_STA); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + +} + +/** + * e1000_i2c_stop - Sets I2C stop condition + * @hw: pointer to hardware structure + * + * Sets I2C stop condition (Low -> High on SDA while SCL is High) + **/ +static void e1000_i2c_stop(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_i2c_stop"); + + /* Stop condition must begin with data low and clock high */ + e1000_set_i2c_data(hw, &i2cctl, 0); + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Setup time for stop condition (4us) */ + usec_delay(E1000_I2C_T_SU_STO); + + e1000_set_i2c_data(hw, &i2cctl, 1); + + /* bus free time between stop and start (4.7us)*/ + usec_delay(E1000_I2C_T_BUF); +} + +/** + * e1000_clock_in_i2c_byte - Clocks in one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte to clock in + * + * Clocks in one byte data via I2C data/clock + **/ +static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data) +{ + s32 i; + bool bit = 0; + + DEBUGFUNC("e1000_clock_in_i2c_byte"); + + *data = 0; + for (i = 7; i >= 0; i--) { + e1000_clock_in_i2c_bit(hw, &bit); + *data |= bit << i; + } + +} + +/** + * e1000_clock_out_i2c_byte - Clocks out one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte clocked out + * + * Clocks out one byte data via I2C data/clock + **/ +static s32 e1000_clock_out_i2c_byte(struct e1000_hw *hw, u8 data) +{ + s32 status = E1000_SUCCESS; + s32 i; + u32 i2cctl; + bool bit = 0; + + DEBUGFUNC("e1000_clock_out_i2c_byte"); + + for (i = 7; i >= 0; i--) { + bit = (data >> i) & 0x1; + status = e1000_clock_out_i2c_bit(hw, bit); + + if (status != E1000_SUCCESS) + break; + } + + /* Release SDA line (set high) */ + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + i2cctl |= E1000_I2C_DATA_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, i2cctl); + E1000_WRITE_FLUSH(hw); + + return status; +} + +/** + * e1000_get_i2c_ack - Polls for I2C ACK + * @hw: pointer to hardware structure + * + * Clocks in/out one bit via I2C data/clock + **/ +static s32 e1000_get_i2c_ack(struct e1000_hw *hw) +{ + s32 status = E1000_SUCCESS; + u32 i = 0; + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + u32 timeout = 10; + bool ack = true; + + DEBUGFUNC("e1000_get_i2c_ack"); + + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + /* Wait until SCL returns high */ + for (i = 0; i < timeout; i++) { + usec_delay(1); + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + if (i2cctl & E1000_I2C_CLK_IN) + break; + } + if (!(i2cctl & E1000_I2C_CLK_IN)) + return E1000_ERR_I2C; + + ack = e1000_get_i2c_data(&i2cctl); + if (ack) { + DEBUGOUT("I2C ack was not received.\n"); + status = E1000_ERR_I2C; + } + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + + return status; +} + +/** + * e1000_clock_in_i2c_bit - Clocks in one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: read data value + * + * Clocks in one bit via I2C data/clock + **/ +static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_clock_in_i2c_bit"); + + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + *data = e1000_get_i2c_data(&i2cctl); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + +} + +/** + * e1000_clock_out_i2c_bit - Clocks in/out one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: data value to write + * + * Clocks out one bit via I2C data/clock + **/ +static s32 e1000_clock_out_i2c_bit(struct e1000_hw *hw, bool data) +{ + s32 status; + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_clock_out_i2c_bit"); + + status = e1000_set_i2c_data(hw, &i2cctl, data); + if (status == E1000_SUCCESS) { + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us. + * This also takes care of the data hold time. + */ + usec_delay(E1000_I2C_T_LOW); + } else { + status = E1000_ERR_I2C; + DEBUGOUT1("I2C data was not set to %X\n", data); + } + + return status; +} +/** + * e1000_raise_i2c_clk - Raises the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Raises the I2C clock line '0'->'1' + **/ +static void e1000_raise_i2c_clk(struct e1000_hw *hw, u32 *i2cctl) +{ + DEBUGFUNC("e1000_raise_i2c_clk"); + + *i2cctl |= E1000_I2C_CLK_OUT; + *i2cctl &= ~E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* SCL rise time (1000ns) */ + usec_delay(E1000_I2C_T_RISE); +} + +/** + * e1000_lower_i2c_clk - Lowers the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Lowers the I2C clock line '1'->'0' + **/ +static void e1000_lower_i2c_clk(struct e1000_hw *hw, u32 *i2cctl) +{ + + DEBUGFUNC("e1000_lower_i2c_clk"); + + *i2cctl &= ~E1000_I2C_CLK_OUT; + *i2cctl &= ~E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* SCL fall time (300ns) */ + usec_delay(E1000_I2C_T_FALL); +} + +/** + * e1000_set_i2c_data - Sets the I2C data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * @data: I2C data value (0 or 1) to set + * + * Sets the I2C data bit + **/ +static s32 e1000_set_i2c_data(struct e1000_hw *hw, u32 *i2cctl, bool data) +{ + s32 status = E1000_SUCCESS; + + DEBUGFUNC("e1000_set_i2c_data"); + + if (data) + *i2cctl |= E1000_I2C_DATA_OUT; + else + *i2cctl &= ~E1000_I2C_DATA_OUT; + + *i2cctl &= ~E1000_I2C_DATA_OE_N; + *i2cctl |= E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */ + usec_delay(E1000_I2C_T_RISE + E1000_I2C_T_FALL + E1000_I2C_T_SU_DATA); + + *i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + if (data != e1000_get_i2c_data(i2cctl)) { + status = E1000_ERR_I2C; + DEBUGOUT1("Error - I2C data was not set to %X.\n", data); + } + + return status; +} + +/** + * e1000_get_i2c_data - Reads the I2C SDA data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Returns the I2C data bit value + **/ +static bool e1000_get_i2c_data(u32 *i2cctl) +{ + bool data; + + DEBUGFUNC("e1000_get_i2c_data"); + + if (*i2cctl & E1000_I2C_DATA_IN) + data = 1; + else + data = 0; + + return data; +} + +/** + * e1000_i2c_bus_clear - Clears the I2C bus + * @hw: pointer to hardware structure + * + * Clears the I2C bus by sending nine clock pulses. + * Used when data line is stuck low. + **/ +void e1000_i2c_bus_clear(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + u32 i; + + DEBUGFUNC("e1000_i2c_bus_clear"); + + e1000_i2c_start(hw); + + e1000_set_i2c_data(hw, &i2cctl, 1); + + for (i = 0; i < 9; i++) { + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Min high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Min low period of clock is 4.7us*/ + usec_delay(E1000_I2C_T_LOW); + } + + e1000_i2c_start(hw); + + /* Put the i2c bus back to default state */ + e1000_i2c_stop(hw); +} + +static const u8 e1000_emc_temp_data[4] = { + E1000_EMC_INTERNAL_DATA, + E1000_EMC_DIODE1_DATA, + E1000_EMC_DIODE2_DATA, + E1000_EMC_DIODE3_DATA +}; +static const u8 e1000_emc_therm_limit[4] = { + E1000_EMC_INTERNAL_THERM_LIMIT, + E1000_EMC_DIODE1_THERM_LIMIT, + E1000_EMC_DIODE2_THERM_LIMIT, + E1000_EMC_DIODE3_THERM_LIMIT +}; + +/** + * e1000_get_thermal_sensor_data_generic - Gathers thermal sensor data + * @hw: pointer to hardware structure + * + * Updates the temperatures in mac.thermal_sensor_data + **/ +s32 e1000_get_thermal_sensor_data_generic(struct e1000_hw *hw) +{ + u16 ets_offset; + u16 ets_cfg; + u16 ets_sensor; + u8 num_sensors; + u8 sensor_index; + u8 sensor_location; + u8 i; + struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + DEBUGFUNC("e1000_get_thermal_sensor_data_generic"); + + if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0)) + return E1000_NOT_IMPLEMENTED; + + data->sensor[0].temp = (E1000_READ_REG(hw, E1000_THMJT) & 0xFF); + + /* Return the internal sensor only if ETS is unsupported */ + e1000_read_nvm(hw, NVM_ETS_CFG, 1, &ets_offset); + if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF)) + return E1000_SUCCESS; + + e1000_read_nvm(hw, ets_offset, 1, &ets_cfg); + if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT) + != NVM_ETS_TYPE_EMC) + return E1000_NOT_IMPLEMENTED; + + num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK); + if (num_sensors > E1000_MAX_SENSORS) + num_sensors = E1000_MAX_SENSORS; + + for (i = 1; i < num_sensors; i++) { + e1000_read_nvm(hw, (ets_offset + i), 1, &ets_sensor); + sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >> + NVM_ETS_DATA_INDEX_SHIFT); + sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >> + NVM_ETS_DATA_LOC_SHIFT); + + if (sensor_location != 0) + hw->phy.ops.read_i2c_byte(hw, + e1000_emc_temp_data[sensor_index], + E1000_I2C_THERMAL_SENSOR_ADDR, + &data->sensor[i].temp); + } + return E1000_SUCCESS; +} + +/** + * e1000_init_thermal_sensor_thresh_generic - Sets thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Sets the thermal sensor thresholds according to the NVM map + * and save off the threshold and location values into mac.thermal_sensor_data + **/ +s32 e1000_init_thermal_sensor_thresh_generic(struct e1000_hw *hw) +{ + u16 ets_offset; + u16 ets_cfg; + u16 ets_sensor; + u8 low_thresh_delta; + u8 num_sensors; + u8 sensor_index; + u8 sensor_location; + u8 therm_limit; + u8 i; + struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + DEBUGFUNC("e1000_init_thermal_sensor_thresh_generic"); + + if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0)) + return E1000_NOT_IMPLEMENTED; + + memset(data, 0, sizeof(struct e1000_thermal_sensor_data)); + + data->sensor[0].location = 0x1; + data->sensor[0].caution_thresh = + (E1000_READ_REG(hw, E1000_THHIGHTC) & 0xFF); + data->sensor[0].max_op_thresh = + (E1000_READ_REG(hw, E1000_THLOWTC) & 0xFF); + + /* Return the internal sensor only if ETS is unsupported */ + e1000_read_nvm(hw, NVM_ETS_CFG, 1, &ets_offset); + if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF)) + return E1000_SUCCESS; + + e1000_read_nvm(hw, ets_offset, 1, &ets_cfg); + if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT) + != NVM_ETS_TYPE_EMC) + return E1000_NOT_IMPLEMENTED; + + low_thresh_delta = ((ets_cfg & NVM_ETS_LTHRES_DELTA_MASK) >> + NVM_ETS_LTHRES_DELTA_SHIFT); + num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK); + + for (i = 1; i <= num_sensors; i++) { + e1000_read_nvm(hw, (ets_offset + i), 1, &ets_sensor); + sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >> + NVM_ETS_DATA_INDEX_SHIFT); + sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >> + NVM_ETS_DATA_LOC_SHIFT); + therm_limit = ets_sensor & NVM_ETS_DATA_HTHRESH_MASK; + + hw->phy.ops.write_i2c_byte(hw, + e1000_emc_therm_limit[sensor_index], + E1000_I2C_THERMAL_SENSOR_ADDR, + therm_limit); + + if ((i < E1000_MAX_SENSORS) && (sensor_location != 0)) { + data->sensor[i].location = sensor_location; + data->sensor[i].caution_thresh = therm_limit; + data->sensor[i].max_op_thresh = therm_limit - + low_thresh_delta; + } + } + return E1000_SUCCESS; +} diff --git a/drivers/staging/igb_avb/e1000_82575.h b/drivers/staging/igb_avb/e1000_82575.h new file mode 100644 index 000000000000..c6b61f71e353 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_82575.h @@ -0,0 +1,510 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_82575_H_ +#define _E1000_82575_H_ + +#define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_DEF1_DEF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_ON2)) +/* + * Receive Address Register Count + * Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * These entries are also used for MAC-based filtering. + */ +/* + * For 82576, there are an additional set of RARs that begin at an offset + * separate from the first set of RARs. + */ +#define E1000_RAR_ENTRIES_82575 16 +#define E1000_RAR_ENTRIES_82576 24 +#define E1000_RAR_ENTRIES_82580 24 +#define E1000_RAR_ENTRIES_I350 32 +#define E1000_SW_SYNCH_MB 0x00000100 +#define E1000_STAT_DEV_RST_SET 0x00100000 +#define E1000_CTRL_DEV_RST 0x20000000 + +struct e1000_adv_data_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + u32 data; + struct { + u32 datalen:16; /* Data buffer length */ + u32 rsvd:4; + u32 dtyp:4; /* Descriptor type */ + u32 dcmd:8; /* Descriptor command */ + } config; + } lower; + union { + u32 data; + struct { + u32 status:4; /* Descriptor status */ + u32 idx:4; + u32 popts:6; /* Packet Options */ + u32 paylen:18; /* Payload length */ + } options; + } upper; +}; + +#define E1000_TXD_DTYP_ADV_C 0x2 /* Advanced Context Descriptor */ +#define E1000_TXD_DTYP_ADV_D 0x3 /* Advanced Data Descriptor */ +#define E1000_ADV_TXD_CMD_DEXT 0x20 /* Descriptor extension (0 = legacy) */ +#define E1000_ADV_TUCMD_IPV4 0x2 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADV_TUCMD_IPV6 0x0 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADV_TUCMD_L4T_UDP 0x0 /* L4 Packet TYPE of UDP */ +#define E1000_ADV_TUCMD_L4T_TCP 0x4 /* L4 Packet TYPE of TCP */ +#define E1000_ADV_TUCMD_MKRREQ 0x10 /* Indicates markers are required */ +#define E1000_ADV_DCMD_EOP 0x1 /* End of Packet */ +#define E1000_ADV_DCMD_IFCS 0x2 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADV_DCMD_RS 0x8 /* Report Status */ +#define E1000_ADV_DCMD_VLE 0x40 /* Add VLAN tag */ +#define E1000_ADV_DCMD_TSE 0x80 /* TCP Seg enable */ +/* Extended Device Control */ +#define E1000_CTRL_EXT_NSICR 0x00000001 /* Disable Intr Clear all on read */ + +struct e1000_adv_context_desc { + union { + u32 ip_config; + struct { + u32 iplen:9; + u32 maclen:7; + u32 vlan_tag:16; + } fields; + } ip_setup; + u32 seq_num; + union { + u64 l4_config; + struct { + u32 mkrloc:9; + u32 tucmd:11; + u32 dtyp:4; + u32 adv:8; + u32 rsvd:4; + u32 idx:4; + u32 l4len:8; + u32 mss:16; + } fields; + } l4_setup; +}; + +/* SRRCTL bit definitions */ +#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ +#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 +#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ +#define E1000_SRRCTL_DESCTYPE_LEGACY 0x00000000 +#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000 +#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000 +#define E1000_SRRCTL_TIMESTAMP 0x40000000 +#define E1000_SRRCTL_DROP_EN 0x80000000 + +#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F +#define E1000_SRRCTL_BSIZEHDR_MASK 0x00003F00 + +#define E1000_TX_HEAD_WB_ENABLE 0x1 +#define E1000_TX_SEQNUM_WB_ENABLE 0x2 + +#define E1000_MRQC_ENABLE_RSS_4Q 0x00000002 +#define E1000_MRQC_ENABLE_VMDQ 0x00000003 +#define E1000_MRQC_ENABLE_VMDQ_RSS_2Q 0x00000005 +#define E1000_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000 +#define E1000_MRQC_ENABLE_RSS_8Q 0x00000002 + +#define E1000_VMRCTL_MIRROR_PORT_SHIFT 8 +#define E1000_VMRCTL_MIRROR_DSTPORT_MASK (7 << \ + E1000_VMRCTL_MIRROR_PORT_SHIFT) +#define E1000_VMRCTL_POOL_MIRROR_ENABLE (1 << 0) +#define E1000_VMRCTL_UPLINK_MIRROR_ENABLE (1 << 1) +#define E1000_VMRCTL_DOWNLINK_MIRROR_ENABLE (1 << 2) + +#define E1000_EICR_TX_QUEUE ( \ + E1000_EICR_TX_QUEUE0 | \ + E1000_EICR_TX_QUEUE1 | \ + E1000_EICR_TX_QUEUE2 | \ + E1000_EICR_TX_QUEUE3) + +#define E1000_EICR_RX_QUEUE ( \ + E1000_EICR_RX_QUEUE0 | \ + E1000_EICR_RX_QUEUE1 | \ + E1000_EICR_RX_QUEUE2 | \ + E1000_EICR_RX_QUEUE3) + +#define E1000_EIMS_RX_QUEUE E1000_EICR_RX_QUEUE +#define E1000_EIMS_TX_QUEUE E1000_EICR_TX_QUEUE + +#define EIMS_ENABLE_MASK ( \ + E1000_EIMS_RX_QUEUE | \ + E1000_EIMS_TX_QUEUE | \ + E1000_EIMS_TCP_TIMER | \ + E1000_EIMS_OTHER) + +/* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */ +#define E1000_IMIR_PORT_IM_EN 0x00010000 /* TCP port enable */ +#define E1000_IMIR_PORT_BP 0x00020000 /* TCP port check bypass */ +#define E1000_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */ +#define E1000_IMIREXT_CTRL_URG 0x00002000 /* Check URG bit in header */ +#define E1000_IMIREXT_CTRL_ACK 0x00004000 /* Check ACK bit in header */ +#define E1000_IMIREXT_CTRL_PSH 0x00008000 /* Check PSH bit in header */ +#define E1000_IMIREXT_CTRL_RST 0x00010000 /* Check RST bit in header */ +#define E1000_IMIREXT_CTRL_SYN 0x00020000 /* Check SYN bit in header */ +#define E1000_IMIREXT_CTRL_FIN 0x00040000 /* Check FIN bit in header */ +#define E1000_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */ + +/* Receive Descriptor - Advanced */ +union e1000_adv_rx_desc { + struct { + __le64 pkt_addr; /* Packet buffer address */ + __le64 hdr_addr; /* Header buffer address */ + } read; + struct { + struct { + union { + __le32 data; + struct { + __le16 pkt_info; /*RSS type, Pkt type*/ + /* Split Header, header buffer len */ + __le16 hdr_info; + } hs_rss; + } lo_dword; + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; /* Packet length */ + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define E1000_RXDADV_RSSTYPE_MASK 0x0000000F +#define E1000_RXDADV_RSSTYPE_SHIFT 12 +#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 +#define E1000_RXDADV_HDRBUFLEN_SHIFT 5 +#define E1000_RXDADV_SPLITHEADER_EN 0x00001000 +#define E1000_RXDADV_SPH 0x8000 +#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */ +#define E1000_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */ +#define E1000_RXDADV_ERR_HBO 0x00800000 + +/* RSS Hash results */ +#define E1000_RXDADV_RSSTYPE_NONE 0x00000000 +#define E1000_RXDADV_RSSTYPE_IPV4_TCP 0x00000001 +#define E1000_RXDADV_RSSTYPE_IPV4 0x00000002 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP 0x00000003 +#define E1000_RXDADV_RSSTYPE_IPV6_EX 0x00000004 +#define E1000_RXDADV_RSSTYPE_IPV6 0x00000005 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006 +#define E1000_RXDADV_RSSTYPE_IPV4_UDP 0x00000007 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP 0x00000008 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009 + +/* RSS Packet Types as indicated in the receive descriptor */ +#define E1000_RXDADV_PKTTYPE_ILMASK 0x000000F0 +#define E1000_RXDADV_PKTTYPE_TLMASK 0x00000F00 +#define E1000_RXDADV_PKTTYPE_NONE 0x00000000 +#define E1000_RXDADV_PKTTYPE_IPV4 0x00000010 /* IPV4 hdr present */ +#define E1000_RXDADV_PKTTYPE_IPV4_EX 0x00000020 /* IPV4 hdr + extensions */ +#define E1000_RXDADV_PKTTYPE_IPV6 0x00000040 /* IPV6 hdr present */ +#define E1000_RXDADV_PKTTYPE_IPV6_EX 0x00000080 /* IPV6 hdr + extensions */ +#define E1000_RXDADV_PKTTYPE_TCP 0x00000100 /* TCP hdr present */ +#define E1000_RXDADV_PKTTYPE_UDP 0x00000200 /* UDP hdr present */ +#define E1000_RXDADV_PKTTYPE_SCTP 0x00000400 /* SCTP hdr present */ +#define E1000_RXDADV_PKTTYPE_NFS 0x00000800 /* NFS hdr present */ + +#define E1000_RXDADV_PKTTYPE_IPSEC_ESP 0x00001000 /* IPSec ESP */ +#define E1000_RXDADV_PKTTYPE_IPSEC_AH 0x00002000 /* IPSec AH */ +#define E1000_RXDADV_PKTTYPE_LINKSEC 0x00004000 /* LinkSec Encap */ +#define E1000_RXDADV_PKTTYPE_ETQF 0x00008000 /* PKTTYPE is ETQF index */ +#define E1000_RXDADV_PKTTYPE_ETQF_MASK 0x00000070 /* ETQF has 8 indices */ +#define E1000_RXDADV_PKTTYPE_ETQF_SHIFT 4 /* Right-shift 4 bits */ + +/* LinkSec results */ +/* Security Processing bit Indication */ +#define E1000_RXDADV_LNKSEC_STATUS_SECP 0x00020000 +#define E1000_RXDADV_LNKSEC_ERROR_BIT_MASK 0x18000000 +#define E1000_RXDADV_LNKSEC_ERROR_NO_SA_MATCH 0x08000000 +#define E1000_RXDADV_LNKSEC_ERROR_REPLAY_ERROR 0x10000000 +#define E1000_RXDADV_LNKSEC_ERROR_BAD_SIG 0x18000000 + +#define E1000_RXDADV_IPSEC_STATUS_SECP 0x00020000 +#define E1000_RXDADV_IPSEC_ERROR_BIT_MASK 0x18000000 +#define E1000_RXDADV_IPSEC_ERROR_INVALID_PROTOCOL 0x08000000 +#define E1000_RXDADV_IPSEC_ERROR_INVALID_LENGTH 0x10000000 +#define E1000_RXDADV_IPSEC_ERROR_AUTHENTICATION_FAILED 0x18000000 + +/* Transmit Descriptor - Advanced */ +union e1000_adv_tx_desc { + struct { + __le64 buffer_addr; /* Address of descriptor's data buf */ + __le32 cmd_type_len; + __le32 olinfo_status; + } read; + struct { + __le64 rsvd; /* Reserved */ + __le32 nxtseq_seed; + __le32 status; + } wb; +}; + +/* Adv Transmit Descriptor Config Masks */ +#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ +#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ +#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ +#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ +#define E1000_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000 /* DDP hdr type or iSCSI */ +#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ +#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ +#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ +#define E1000_ADVTXD_MAC_LINKSEC 0x00040000 /* Apply LinkSec on pkt */ +#define E1000_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp pkt */ +#define E1000_ADVTXD_STAT_SN_CRC 0x00000002 /* NXTSEQ/SEED prsnt in WB */ +#define E1000_ADVTXD_IDX_SHIFT 4 /* Adv desc Index shift */ +#define E1000_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */ +/* 1st & Last TSO-full iSCSI PDU*/ +#define E1000_ADVTXD_POPTS_ISCO_FULL 0x00001800 +#define E1000_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */ +#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ + +/* Context descriptors */ +struct e1000_adv_tx_context_desc { + __le32 vlan_macip_lens; + __le32 seqnum_seed; + __le32 type_tucmd_mlhl; + __le32 mss_l4len_idx; +}; + +#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ +#define E1000_ADVTXD_VLAN_SHIFT 16 /* Adv ctxt vlan tag shift */ +#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADVTXD_TUCMD_IPV6 0x00000000 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */ +#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ +#define E1000_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */ +#define E1000_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */ +/* IPSec Encrypt Enable for ESP */ +#define E1000_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN 0x00004000 +/* Req requires Markers and CRC */ +#define E1000_ADVTXD_TUCMD_MKRREQ 0x00002000 +#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ +#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ +/* Adv ctxt IPSec SA IDX mask */ +#define E1000_ADVTXD_IPSEC_SA_INDEX_MASK 0x000000FF +/* Adv ctxt IPSec ESP len mask */ +#define E1000_ADVTXD_IPSEC_ESP_LEN_MASK 0x000000FF + +/* Additional Transmit Descriptor Control definitions */ +#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Tx Queue */ +#define E1000_TXDCTL_SWFLSH 0x04000000 /* Tx Desc. wbk flushing */ +/* Tx Queue Arbitration Priority 0=low, 1=high */ +#define E1000_TXDCTL_PRIORITY 0x08000000 + +/* Additional Receive Descriptor Control definitions */ +#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Rx Queue */ +#define E1000_RXDCTL_SWFLSH 0x04000000 /* Rx Desc. wbk flushing */ + +/* Direct Cache Access (DCA) definitions */ +#define E1000_DCA_CTRL_DCA_ENABLE 0x00000000 /* DCA Enable */ +#define E1000_DCA_CTRL_DCA_DISABLE 0x00000001 /* DCA Disable */ + +#define E1000_DCA_CTRL_DCA_MODE_CB1 0x00 /* DCA Mode CB1 */ +#define E1000_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */ + +#define E1000_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */ +#define E1000_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */ +#define E1000_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header ena */ +#define E1000_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload ena */ +#define E1000_DCA_RXCTRL_DESC_RRO_EN (1 << 9) /* DCA Rx Desc Relax Order */ + +#define E1000_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ +#define E1000_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ +#define E1000_DCA_TXCTRL_DESC_RRO_EN (1 << 9) /* Tx rd Desc Relax Order */ +#define E1000_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */ +#define E1000_DCA_TXCTRL_DATA_RRO_EN (1 << 13) /* Tx rd data Relax Order */ + +#define E1000_DCA_TXCTRL_CPUID_MASK_82576 0xFF000000 /* Tx CPUID Mask */ +#define E1000_DCA_RXCTRL_CPUID_MASK_82576 0xFF000000 /* Rx CPUID Mask */ +#define E1000_DCA_TXCTRL_CPUID_SHIFT_82576 24 /* Tx CPUID */ +#define E1000_DCA_RXCTRL_CPUID_SHIFT_82576 24 /* Rx CPUID */ + +/* Additional interrupt register bit definitions */ +#define E1000_ICR_LSECPNS 0x00000020 /* PN threshold - server */ +#define E1000_IMS_LSECPNS E1000_ICR_LSECPNS /* PN threshold - server */ +#define E1000_ICS_LSECPNS E1000_ICR_LSECPNS /* PN threshold - server */ + +/* ETQF register bit definitions */ +#define E1000_ETQF_FILTER_ENABLE (1 << 26) +#define E1000_ETQF_IMM_INT (1 << 29) +#define E1000_ETQF_1588 (1 << 30) +#define E1000_ETQF_QUEUE_ENABLE (1 << 31) +/* + * ETQF filter list: one static filter per filter consumer. This is + * to avoid filter collisions later. Add new filters + * here!! + * + * Current filters: + * EAPOL 802.1x (0x888e): Filter 0 + */ +#define E1000_ETQF_FILTER_EAPOL 0 + +#define E1000_FTQF_VF_BP 0x00008000 +#define E1000_FTQF_1588_TIME_STAMP 0x08000000 +#define E1000_FTQF_MASK 0xF0000000 +#define E1000_FTQF_MASK_PROTO_BP 0x10000000 +#define E1000_FTQF_MASK_SOURCE_ADDR_BP 0x20000000 +#define E1000_FTQF_MASK_DEST_ADDR_BP 0x40000000 +#define E1000_FTQF_MASK_SOURCE_PORT_BP 0x80000000 + +#define E1000_NVM_APME_82575 0x0400 +#define MAX_NUM_VFS 7 + +#define E1000_DTXSWC_MAC_SPOOF_MASK 0x000000FF /* Per VF MAC spoof cntrl */ +#define E1000_DTXSWC_VLAN_SPOOF_MASK 0x0000FF00 /* Per VF VLAN spoof cntrl */ +#define E1000_DTXSWC_LLE_MASK 0x00FF0000 /* Per VF Local LB enables */ +#define E1000_DTXSWC_VLAN_SPOOF_SHIFT 8 +#define E1000_DTXSWC_LLE_SHIFT 16 +#define E1000_DTXSWC_VMDQ_LOOPBACK_EN (1 << 31) /* global VF LB enable */ + +/* Easy defines for setting default pool, would normally be left a zero */ +#define E1000_VT_CTL_DEFAULT_POOL_SHIFT 7 +#define E1000_VT_CTL_DEFAULT_POOL_MASK (0x7 << E1000_VT_CTL_DEFAULT_POOL_SHIFT) + +/* Other useful VMD_CTL register defines */ +#define E1000_VT_CTL_IGNORE_MAC (1 << 28) +#define E1000_VT_CTL_DISABLE_DEF_POOL (1 << 29) +#define E1000_VT_CTL_VM_REPL_EN (1 << 30) + +/* Per VM Offload register setup */ +#define E1000_VMOLR_RLPML_MASK 0x00003FFF /* Long Packet Maximum Length mask */ +#define E1000_VMOLR_LPE 0x00010000 /* Accept Long packet */ +#define E1000_VMOLR_RSSE 0x00020000 /* Enable RSS */ +#define E1000_VMOLR_AUPE 0x01000000 /* Accept untagged packets */ +#define E1000_VMOLR_ROMPE 0x02000000 /* Accept overflow multicast */ +#define E1000_VMOLR_ROPE 0x04000000 /* Accept overflow unicast */ +#define E1000_VMOLR_BAM 0x08000000 /* Accept Broadcast packets */ +#define E1000_VMOLR_MPME 0x10000000 /* Multicast promiscuous mode */ +#define E1000_VMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_VMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_VMOLR_VPE 0x00800000 /* VLAN promiscuous enable */ +#define E1000_VMOLR_UPE 0x20000000 /* Unicast promisuous enable */ +#define E1000_DVMOLR_HIDVLAN 0x20000000 /* Vlan hiding enable */ +#define E1000_DVMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_DVMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_PBRWAC_WALPB 0x00000007 /* Wrap around event on LAN Rx PB */ +#define E1000_PBRWAC_PBE 0x00000008 /* Rx packet buffer empty */ + +#define E1000_VLVF_ARRAY_SIZE 32 +#define E1000_VLVF_VLANID_MASK 0x00000FFF +#define E1000_VLVF_POOLSEL_SHIFT 12 +#define E1000_VLVF_POOLSEL_MASK (0xFF << E1000_VLVF_POOLSEL_SHIFT) +#define E1000_VLVF_LVLAN 0x00100000 +#define E1000_VLVF_VLANID_ENABLE 0x80000000 + +#define E1000_VMVIR_VLANA_DEFAULT 0x40000000 /* Always use default VLAN */ +#define E1000_VMVIR_VLANA_NEVER 0x80000000 /* Never insert VLAN tag */ + +#define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */ + +#define E1000_IOVCTL 0x05BBC +#define E1000_IOVCTL_REUSE_VFQ 0x00000001 + +#define E1000_RPLOLR_STRVLAN 0x40000000 +#define E1000_RPLOLR_STRCRC 0x80000000 + +#define E1000_TCTL_EXT_COLD 0x000FFC00 +#define E1000_TCTL_EXT_COLD_SHIFT 10 + +#define E1000_DTXCTL_8023LL 0x0004 +#define E1000_DTXCTL_VLAN_ADDED 0x0008 +#define E1000_DTXCTL_OOS_ENABLE 0x0010 +#define E1000_DTXCTL_MDP_EN 0x0020 +#define E1000_DTXCTL_SPOOF_INT 0x0040 + +#define E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT (1 << 14) + +#define ALL_QUEUES 0xFFFF + +/* Rx packet buffer size defines */ +#define E1000_RXPBS_SIZE_MASK_82576 0x0000007F +void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable); +void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf); +void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable); +s32 e1000_init_nvm_params_82575(struct e1000_hw *hw); +s32 e1000_init_hw_82575(struct e1000_hw *hw); + +u16 e1000_rxpbs_adjust_82580(u32 data); +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data); +s32 e1000_set_eee_i350(struct e1000_hw *hw, bool adv1G, bool adv100M); +s32 e1000_set_eee_i354(struct e1000_hw *hw, bool adv1G, bool adv100M); +s32 e1000_get_eee_status_i354(struct e1000_hw *, bool *); +s32 e1000_initialize_M88E1512_phy(struct e1000_hw *hw); +#define E1000_I2C_THERMAL_SENSOR_ADDR 0xF8 +#define E1000_EMC_INTERNAL_DATA 0x00 +#define E1000_EMC_INTERNAL_THERM_LIMIT 0x20 +#define E1000_EMC_DIODE1_DATA 0x01 +#define E1000_EMC_DIODE1_THERM_LIMIT 0x19 +#define E1000_EMC_DIODE2_DATA 0x23 +#define E1000_EMC_DIODE2_THERM_LIMIT 0x1A +#define E1000_EMC_DIODE3_DATA 0x2A +#define E1000_EMC_DIODE3_THERM_LIMIT 0x30 + +s32 e1000_get_thermal_sensor_data_generic(struct e1000_hw *hw); +s32 e1000_init_thermal_sensor_thresh_generic(struct e1000_hw *hw); + +/* I2C SDA and SCL timing parameters for standard mode */ +#define E1000_I2C_T_HD_STA 4 +#define E1000_I2C_T_LOW 5 +#define E1000_I2C_T_HIGH 4 +#define E1000_I2C_T_SU_STA 5 +#define E1000_I2C_T_HD_DATA 5 +#define E1000_I2C_T_SU_DATA 1 +#define E1000_I2C_T_RISE 1 +#define E1000_I2C_T_FALL 1 +#define E1000_I2C_T_SU_STO 4 +#define E1000_I2C_T_BUF 5 + +s32 e1000_set_i2c_bb(struct e1000_hw *hw); +s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); +s32 e1000_write_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); +void e1000_i2c_bus_clear(struct e1000_hw *hw); +#endif /* _E1000_82575_H_ */ diff --git a/drivers/staging/igb_avb/e1000_api.c b/drivers/staging/igb_avb/e1000_api.c new file mode 100644 index 000000000000..87bccbd19fc6 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_api.c @@ -0,0 +1,1160 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +/** + * e1000_init_mac_params - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the MAC + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_mac_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->mac.ops.init_params) { + ret_val = hw->mac.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("MAC Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("mac.init_mac_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the NVM + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_nvm_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->nvm.ops.init_params) { + ret_val = hw->nvm.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("NVM Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("nvm.init_nvm_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_phy_params - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the PHY + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_phy_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->phy.ops.init_params) { + ret_val = hw->phy.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("PHY Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("phy.init_phy_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_mbx_params - Initialize mailbox function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the PHY + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_mbx_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->mbx.ops.init_params) { + ret_val = hw->mbx.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("Mailbox Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("mbx.init_mbx_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_set_mac_type - Sets MAC type + * @hw: pointer to the HW structure + * + * This function sets the mac type of the adapter based on the + * device ID stored in the hw structure. + * MUST BE FIRST FUNCTION CALLED (explicitly or through + * e1000_setup_init_funcs()). + **/ +s32 e1000_set_mac_type(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_set_mac_type"); + + switch (hw->device_id) { + case E1000_DEV_ID_82575EB_COPPER: + case E1000_DEV_ID_82575EB_FIBER_SERDES: + case E1000_DEV_ID_82575GB_QUAD_COPPER: + mac->type = e1000_82575; + break; + case E1000_DEV_ID_82576: + case E1000_DEV_ID_82576_FIBER: + case E1000_DEV_ID_82576_SERDES: + case E1000_DEV_ID_82576_QUAD_COPPER: + case E1000_DEV_ID_82576_QUAD_COPPER_ET2: + case E1000_DEV_ID_82576_NS: + case E1000_DEV_ID_82576_NS_SERDES: + case E1000_DEV_ID_82576_SERDES_QUAD: + mac->type = e1000_82576; + break; + case E1000_DEV_ID_82580_COPPER: + case E1000_DEV_ID_82580_FIBER: + case E1000_DEV_ID_82580_SERDES: + case E1000_DEV_ID_82580_SGMII: + case E1000_DEV_ID_82580_COPPER_DUAL: + case E1000_DEV_ID_82580_QUAD_FIBER: + case E1000_DEV_ID_DH89XXCC_SGMII: + case E1000_DEV_ID_DH89XXCC_SERDES: + case E1000_DEV_ID_DH89XXCC_BACKPLANE: + case E1000_DEV_ID_DH89XXCC_SFP: + mac->type = e1000_82580; + break; + case E1000_DEV_ID_I350_COPPER: + case E1000_DEV_ID_I350_FIBER: + case E1000_DEV_ID_I350_SERDES: + case E1000_DEV_ID_I350_SGMII: + case E1000_DEV_ID_I350_DA4: + mac->type = e1000_i350; + break; + case E1000_DEV_ID_I210_COPPER_FLASHLESS: + case E1000_DEV_ID_I210_SERDES_FLASHLESS: + case E1000_DEV_ID_I210_COPPER: + case E1000_DEV_ID_I210_COPPER_OEM1: + case E1000_DEV_ID_I210_COPPER_IT: + case E1000_DEV_ID_I210_FIBER: + case E1000_DEV_ID_I210_SERDES: + case E1000_DEV_ID_I210_SGMII: + case E1000_DEV_ID_I210_AUTOMOTIVE: + mac->type = e1000_i210; + break; + case E1000_DEV_ID_I211_COPPER: + mac->type = e1000_i211; + break; + + case E1000_DEV_ID_I354_BACKPLANE_1GBPS: + case E1000_DEV_ID_I354_SGMII: + case E1000_DEV_ID_I354_BACKPLANE_2_5GBPS: + mac->type = e1000_i354; + break; + default: + /* Should never have loaded on this device */ + ret_val = -E1000_ERR_MAC_INIT; + break; + } + + return ret_val; +} + +/** + * e1000_setup_init_funcs - Initializes function pointers + * @hw: pointer to the HW structure + * @init_device: true will initialize the rest of the function pointers + * getting the device ready for use. false will only set + * MAC type and the function pointers for the other init + * functions. Passing false will not generate any hardware + * reads or writes. + * + * This function must be called by a driver in order to use the rest + * of the 'shared' code files. Called by drivers only. + **/ +s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device) +{ + s32 ret_val; + + /* Can't do much good without knowing the MAC type. */ + ret_val = e1000_set_mac_type(hw); + if (ret_val) { + DEBUGOUT("ERROR: MAC type could not be set properly.\n"); + goto out; + } + + if (!hw->hw_addr) { + DEBUGOUT("ERROR: Registers not mapped\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* + * Init function pointers to generic implementations. We do this first + * allowing a driver module to override it afterward. + */ + e1000_init_mac_ops_generic(hw); + e1000_init_phy_ops_generic(hw); + e1000_init_nvm_ops_generic(hw); + e1000_init_mbx_ops_generic(hw); + + /* + * Set up the init function pointers. These are functions within the + * adapter family file that sets up function pointers for the rest of + * the functions in that family. + */ + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + case e1000_82580: + case e1000_i350: + case e1000_i354: + e1000_init_function_pointers_82575(hw); + break; + case e1000_i210: + case e1000_i211: + e1000_init_function_pointers_i210(hw); + break; + default: + DEBUGOUT("Hardware not supported\n"); + ret_val = -E1000_ERR_CONFIG; + break; + } + + /* + * Initialize the rest of the function pointers. These require some + * register reads/writes in some cases. + */ + if (!(ret_val) && init_device) { + ret_val = e1000_init_mac_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_nvm_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_phy_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_mbx_params(hw); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_get_bus_info - Obtain bus information for adapter + * @hw: pointer to the HW structure + * + * This will obtain information about the HW bus for which the + * adapter is attached and stores it in the hw structure. This is a + * function pointer entry point called by drivers. + **/ +s32 e1000_get_bus_info(struct e1000_hw *hw) +{ + if (hw->mac.ops.get_bus_info) + return hw->mac.ops.get_bus_info(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_clear_vfta - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * This clears the VLAN filter table on the adapter. This is a function + * pointer entry point called by drivers. + **/ +void e1000_clear_vfta(struct e1000_hw *hw) +{ + if (hw->mac.ops.clear_vfta) + hw->mac.ops.clear_vfta(hw); +} + +/** + * e1000_write_vfta - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: the 32-bit offset in which to write the value to. + * @value: the 32-bit value to write at location offset. + * + * This writes a 32-bit value to a 32-bit offset in the VLAN filter + * table. This is a function pointer entry point called by drivers. + **/ +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) +{ + if (hw->mac.ops.write_vfta) + hw->mac.ops.write_vfta(hw, offset, value); +} + +/** + * e1000_update_mc_addr_list - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates the Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void e1000_update_mc_addr_list(struct e1000_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count) +{ + if (hw->mac.ops.update_mc_addr_list) + hw->mac.ops.update_mc_addr_list(hw, mc_addr_list, + mc_addr_count); +} + +/** + * e1000_force_mac_fc - Force MAC flow control + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Currently no func pointer exists + * and all implementations are handled in the generic version of this + * function. + **/ +s32 e1000_force_mac_fc(struct e1000_hw *hw) +{ + return e1000_force_mac_fc_generic(hw); +} + +/** + * e1000_check_for_link - Check/Store link connection + * @hw: pointer to the HW structure + * + * This checks the link condition of the adapter and stores the + * results in the hw->mac structure. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_check_for_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.check_for_link) + return hw->mac.ops.check_for_link(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_check_mng_mode - Check management mode + * @hw: pointer to the HW structure + * + * This checks if the adapter has manageability enabled. + * This is a function pointer entry point called by drivers. + **/ +bool e1000_check_mng_mode(struct e1000_hw *hw) +{ + if (hw->mac.ops.check_mng_mode) + return hw->mac.ops.check_mng_mode(hw); + + return false; +} + +/** + * e1000_mng_write_dhcp_info - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length) +{ + return e1000_mng_write_dhcp_info_generic(hw, buffer, length); +} + +/** + * e1000_reset_hw - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_reset_hw(struct e1000_hw *hw) +{ + if (hw->mac.ops.reset_hw) + return hw->mac.ops.reset_hw(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_init_hw - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_init_hw(struct e1000_hw *hw) +{ + if (hw->mac.ops.init_hw) + return hw->mac.ops.init_hw(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_link - Configures link and flow control + * @hw: pointer to the HW structure + * + * This configures link and flow control settings for the adapter. This + * is a function pointer entry point called by drivers. While modules can + * also call this, they probably call their own version of this function. + **/ +s32 e1000_setup_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.setup_link) + return hw->mac.ops.setup_link(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_get_speed_and_duplex - Returns current speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to a 16-bit value to store the speed + * @duplex: pointer to a 16-bit value to store the duplex. + * + * This returns the speed and duplex of the adapter in the two 'out' + * variables passed in. This is a function pointer entry point called + * by drivers. + **/ +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + if (hw->mac.ops.get_link_up_info) + return hw->mac.ops.get_link_up_info(hw, speed, duplex); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_led - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_setup_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.setup_led) + return hw->mac.ops.setup_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_cleanup_led - Restores SW controllable LED + * @hw: pointer to the HW structure + * + * This restores the SW controllable LED to the value saved off by + * e1000_setup_led. This is a function pointer entry point called by drivers. + **/ +s32 e1000_cleanup_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.cleanup_led) + return hw->mac.ops.cleanup_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_blink_led - Blink SW controllable LED + * @hw: pointer to the HW structure + * + * This starts the adapter LED blinking. Request the LED to be setup first + * and cleaned up after. This is a function pointer entry point called by + * drivers. + **/ +s32 e1000_blink_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.blink_led) + return hw->mac.ops.blink_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_id_led_init - store LED configurations in SW + * @hw: pointer to the HW structure + * + * Initializes the LED config in SW. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_id_led_init(struct e1000_hw *hw) +{ + if (hw->mac.ops.id_led_init) + return hw->mac.ops.id_led_init(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_led_on - Turn on SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED on. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_led_on(struct e1000_hw *hw) +{ + if (hw->mac.ops.led_on) + return hw->mac.ops.led_on(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_led_off - Turn off SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED off. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_led_off(struct e1000_hw *hw) +{ + if (hw->mac.ops.led_off) + return hw->mac.ops.led_off(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_reset_adaptive - Reset adaptive IFS + * @hw: pointer to the HW structure + * + * Resets the adaptive IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void e1000_reset_adaptive(struct e1000_hw *hw) +{ + e1000_reset_adaptive_generic(hw); +} + +/** + * e1000_update_adaptive - Update adaptive IFS + * @hw: pointer to the HW structure + * + * Updates adapter IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void e1000_update_adaptive(struct e1000_hw *hw) +{ + e1000_update_adaptive_generic(hw); +} + +/** + * e1000_disable_pcie_master - Disable PCI-Express master access + * @hw: pointer to the HW structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. Currently no func pointer exists and all implementations are + * handled in the generic version of this function. + **/ +s32 e1000_disable_pcie_master(struct e1000_hw *hw) +{ + return e1000_disable_pcie_master_generic(hw); +} + +/** + * e1000_config_collision_dist - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +void e1000_config_collision_dist(struct e1000_hw *hw) +{ + if (hw->mac.ops.config_collision_dist) + hw->mac.ops.config_collision_dist(hw); +} + +/** + * e1000_rar_set - Sets a receive address register + * @hw: pointer to the HW structure + * @addr: address to set the RAR to + * @index: the RAR to set + * + * Sets a Receive Address Register (RAR) to the specified address. + **/ +int e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) +{ + if (hw->mac.ops.rar_set) + return hw->mac.ops.rar_set(hw, addr, index); + + return E1000_SUCCESS; +} + +/** + * e1000_validate_mdi_setting - Ensures valid MDI/MDIX SW state + * @hw: pointer to the HW structure + * + * Ensures that the MDI/MDIX SW state is valid. + **/ +s32 e1000_validate_mdi_setting(struct e1000_hw *hw) +{ + if (hw->mac.ops.validate_mdi_setting) + return hw->mac.ops.validate_mdi_setting(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_hash_mc_addr - Determines address location in multicast table + * @hw: pointer to the HW structure + * @mc_addr: Multicast address to hash. + * + * This hashes an address to determine its location in the multicast + * table. Currently no func pointer exists and all implementations + * are handled in the generic version of this function. + **/ +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) +{ + return e1000_hash_mc_addr_generic(hw, mc_addr); +} + +/** + * e1000_enable_tx_pkt_filtering - Enable packet filtering on TX + * @hw: pointer to the HW structure + * + * Enables packet filtering on transmit packets if manageability is enabled + * and host interface is enabled. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) +{ + return e1000_enable_tx_pkt_filtering_generic(hw); +} + +/** + * e1000_mng_host_if_write - Writes to the manageability host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface buffer + * @length: size of the buffer + * @offset: location in the buffer to write to + * @sum: sum of the data (not checksum) + * + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient + * way. Also fills up the sum of the buffer in *buffer parameter. + **/ +s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum) +{ + return e1000_mng_host_if_write_generic(hw, buffer, length, offset, sum); +} + +/** + * e1000_mng_write_cmd_header - Writes manageability command header + * @hw: pointer to the HW structure + * @hdr: pointer to the host interface command header + * + * Writes the command header after does the checksum calculation. + **/ +s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) +{ + return e1000_mng_write_cmd_header_generic(hw, hdr); +} + +/** + * e1000_mng_enable_host_if - Checks host interface is enabled + * @hw: pointer to the HW structure + * + * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND + * + * This function checks whether the HOST IF is enabled for command operation + * and also checks whether the previous command is completed. It busy waits + * in case of previous command is not completed. + **/ +s32 e1000_mng_enable_host_if(struct e1000_hw *hw) +{ + return e1000_mng_enable_host_if_generic(hw); +} + +/** + * e1000_check_reset_block - Verifies PHY can be reset + * @hw: pointer to the HW structure + * + * Checks if the PHY is in a state that can be reset or if manageability + * has it tied up. This is a function pointer entry point called by drivers. + **/ +s32 e1000_check_reset_block(struct e1000_hw *hw) +{ + if (hw->phy.ops.check_reset_block) + return hw->phy.ops.check_reset_block(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg - Reads PHY register + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the buffer to store the 16-bit read. + * + * Reads the PHY register and returns the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + if (hw->phy.ops.read_reg) + return hw->phy.ops.read_reg(hw, offset, data); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg - Writes PHY register + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + if (hw->phy.ops.write_reg) + return hw->phy.ops.write_reg(hw, offset, data); + + return E1000_SUCCESS; +} + +/** + * e1000_release_phy - Generic release PHY + * @hw: pointer to the HW structure + * + * Return if silicon family does not require a semaphore when accessing the + * PHY. + **/ +void e1000_release_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.release) + hw->phy.ops.release(hw); +} + +/** + * e1000_acquire_phy - Generic acquire PHY + * @hw: pointer to the HW structure + * + * Return success if silicon family does not require a semaphore when + * accessing the PHY. + **/ +s32 e1000_acquire_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.acquire) + return hw->phy.ops.acquire(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_kmrn_reg - Reads register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the location to store the 16-bit value read. + * + * Reads a register out of the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return e1000_read_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_write_kmrn_reg - Writes register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes a register to the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + return e1000_write_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_get_cable_length - Retrieves cable length estimation + * @hw: pointer to the HW structure + * + * This function estimates the cable length and stores them in + * hw->phy.min_length and hw->phy.max_length. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_get_cable_length(struct e1000_hw *hw) +{ + if (hw->phy.ops.get_cable_length) + return hw->phy.ops.get_cable_length(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_info - Retrieves PHY information from registers + * @hw: pointer to the HW structure + * + * This function gets some information from various PHY registers and + * populates hw->phy values with it. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_get_phy_info(struct e1000_hw *hw) +{ + if (hw->phy.ops.get_info) + return hw->phy.ops.get_info(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_hw_reset - Hard PHY reset + * @hw: pointer to the HW structure + * + * Performs a hard PHY reset. This is a function pointer entry point called + * by drivers. + **/ +s32 e1000_phy_hw_reset(struct e1000_hw *hw) +{ + if (hw->phy.ops.reset) + return hw->phy.ops.reset(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_commit - Soft PHY reset + * @hw: pointer to the HW structure + * + * Performs a soft PHY reset on those that apply. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_phy_commit(struct e1000_hw *hw) +{ + if (hw->phy.ops.commit) + return hw->phy.ops.commit(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_set_d0_lplu_state - Sets low power link up state for D0 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D0 + * and SmartSpeed is disabled when active is true, else clear lplu for D0 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) +{ + if (hw->phy.ops.set_d0_lplu_state) + return hw->phy.ops.set_d0_lplu_state(hw, active); + + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) +{ + if (hw->phy.ops.set_d3_lplu_state) + return hw->phy.ops.set_d3_lplu_state(hw, active); + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr - Reads MAC address + * @hw: pointer to the HW structure + * + * Reads the MAC address out of the adapter and stores it in the HW structure. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_mac_addr(struct e1000_hw *hw) +{ + if (hw->mac.ops.read_mac_addr) + return hw->mac.ops.read_mac_addr(hw); + + return e1000_read_mac_addr_generic(hw); +} + +/** + * e1000_read_pba_string - Read device part number string + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_pba_string(struct e1000_hw *hw, u8 *pba_num, u32 pba_num_size) +{ + return e1000_read_pba_string_generic(hw, pba_num, pba_num_size); +} + +/** + * e1000_read_pba_length - Read device part number string length + * @hw: pointer to the HW structure + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number length from the EEPROM and + * stores the value in pba_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_pba_length(struct e1000_hw *hw, u32 *pba_num_size) +{ + return e1000_read_pba_length_generic(hw, pba_num_size); +} + +/** + * e1000_validate_nvm_checksum - Verifies NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Validates the NVM checksum is correct. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_validate_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->nvm.ops.validate) + return hw->nvm.ops.validate(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_update_nvm_checksum - Updates NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Updates the NVM checksum. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 e1000_update_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->nvm.ops.update) + return hw->nvm.ops.update(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_reload_nvm - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +void e1000_reload_nvm(struct e1000_hw *hw) +{ + if (hw->nvm.ops.reload) + hw->nvm.ops.reload(hw); +} + +/** + * e1000_read_nvm - Reads NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to read + * @data: pointer to the properly sized buffer for the data. + * + * Reads 16-bit chunks of data from the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.read) + return hw->nvm.ops.read(hw, offset, words, data); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_write_nvm - Writes to NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to write + * @data: pointer to the properly sized buffer for the data. + * + * Writes 16-bit chunks of data to the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.write) + return hw->nvm.ops.write(hw, offset, words, data); + + return E1000_SUCCESS; +} + +/** + * e1000_write_8bit_ctrl_reg - Writes 8bit Control register + * @hw: pointer to the HW structure + * @reg: 32bit register offset + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset, + u8 data) +{ + return e1000_write_8bit_ctrl_reg_generic(hw, reg, offset, data); +} + +/** + * e1000_power_up_phy - Restores link in case of PHY power down + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void e1000_power_up_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.power_up) + hw->phy.ops.power_up(hw); + + e1000_setup_link(hw); +} + +/** + * e1000_power_down_phy - Power down PHY + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void e1000_power_down_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.power_down) + hw->phy.ops.power_down(hw); +} + +/** + * e1000_power_up_fiber_serdes_link - Power up serdes link + * @hw: pointer to the HW structure + * + * Power on the optics and PCS. + **/ +void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.power_up_serdes) + hw->mac.ops.power_up_serdes(hw); +} + +/** + * e1000_shutdown_fiber_serdes_link - Remove link during power down + * @hw: pointer to the HW structure + * + * Shutdown the optics and PCS on driver unload. + **/ +void e1000_shutdown_fiber_serdes_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.shutdown_serdes) + hw->mac.ops.shutdown_serdes(hw); +} + +/** + * e1000_get_thermal_sensor_data - Gathers thermal sensor data + * @hw: pointer to hardware structure + * + * Updates the temperatures in mac.thermal_sensor_data + **/ +s32 e1000_get_thermal_sensor_data(struct e1000_hw *hw) +{ + if (hw->mac.ops.get_thermal_sensor_data) + return hw->mac.ops.get_thermal_sensor_data(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_init_thermal_sensor_thresh - Sets thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Sets the thermal sensor thresholds according to the NVM map + **/ +s32 e1000_init_thermal_sensor_thresh(struct e1000_hw *hw) +{ + if (hw->mac.ops.init_thermal_sensor_thresh) + return hw->mac.ops.init_thermal_sensor_thresh(hw); + + return E1000_SUCCESS; +} + diff --git a/drivers/staging/igb_avb/e1000_api.h b/drivers/staging/igb_avb/e1000_api.h new file mode 100644 index 000000000000..32fce254685a --- /dev/null +++ b/drivers/staging/igb_avb/e1000_api.h @@ -0,0 +1,152 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_API_H_ +#define _E1000_API_H_ + +#include "e1000_hw.h" + +extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); +extern void e1000_rx_fifo_flush_82575(struct e1000_hw *hw); +extern void e1000_init_function_pointers_vf(struct e1000_hw *hw); +extern void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw); +extern void e1000_shutdown_fiber_serdes_link(struct e1000_hw *hw); +extern void e1000_init_function_pointers_i210(struct e1000_hw *hw); + +s32 e1000_set_obff_timer(struct e1000_hw *hw, u32 itr); +s32 e1000_set_mac_type(struct e1000_hw *hw); +s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device); +s32 e1000_init_mac_params(struct e1000_hw *hw); +s32 e1000_init_nvm_params(struct e1000_hw *hw); +s32 e1000_init_phy_params(struct e1000_hw *hw); +s32 e1000_init_mbx_params(struct e1000_hw *hw); +s32 e1000_get_bus_info(struct e1000_hw *hw); +void e1000_clear_vfta(struct e1000_hw *hw); +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value); +s32 e1000_force_mac_fc(struct e1000_hw *hw); +s32 e1000_check_for_link(struct e1000_hw *hw); +s32 e1000_reset_hw(struct e1000_hw *hw); +s32 e1000_init_hw(struct e1000_hw *hw); +s32 e1000_setup_link(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex); +s32 e1000_disable_pcie_master(struct e1000_hw *hw); +void e1000_config_collision_dist(struct e1000_hw *hw); +int e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index); +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr); +void e1000_update_mc_addr_list(struct e1000_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count); +s32 e1000_setup_led(struct e1000_hw *hw); +s32 e1000_cleanup_led(struct e1000_hw *hw); +s32 e1000_check_reset_block(struct e1000_hw *hw); +s32 e1000_blink_led(struct e1000_hw *hw); +s32 e1000_led_on(struct e1000_hw *hw); +s32 e1000_led_off(struct e1000_hw *hw); +s32 e1000_id_led_init(struct e1000_hw *hw); +void e1000_reset_adaptive(struct e1000_hw *hw); +void e1000_update_adaptive(struct e1000_hw *hw); +s32 e1000_get_cable_length(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting(struct e1000_hw *hw); +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset, + u8 data); +s32 e1000_get_phy_info(struct e1000_hw *hw); +void e1000_release_phy(struct e1000_hw *hw); +s32 e1000_acquire_phy(struct e1000_hw *hw); +s32 e1000_phy_hw_reset(struct e1000_hw *hw); +s32 e1000_phy_commit(struct e1000_hw *hw); +void e1000_power_up_phy(struct e1000_hw *hw); +void e1000_power_down_phy(struct e1000_hw *hw); +s32 e1000_read_mac_addr(struct e1000_hw *hw); +s32 e1000_read_pba_string(struct e1000_hw *hw, u8 *pba_num, u32 pba_num_size); +s32 e1000_read_pba_length(struct e1000_hw *hw, u32 *pba_num_size); +void e1000_reload_nvm(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum(struct e1000_hw *hw); +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); +s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active); +bool e1000_check_mng_mode(struct e1000_hw *hw); +bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw); +s32 e1000_mng_enable_host_if(struct e1000_hw *hw); +s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum); +s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr); +s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length); +s32 e1000_get_thermal_sensor_data(struct e1000_hw *hw); +s32 e1000_init_thermal_sensor_thresh(struct e1000_hw *hw); + +/* + * TBI_ACCEPT macro definition: + * + * This macro requires: + * a = a pointer to struct e1000_hw + * status = the 8 bit status field of the Rx descriptor with EOP set + * errors = the 8 bit error field of the Rx descriptor with EOP set + * length = the sum of all the length fields of the Rx descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * min_frame_size = the minimum frame length we want to accept. + * max_frame_size = the maximum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = true; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = false; + * } + * ... + */ + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +#define TBI_ACCEPT(a, status, errors, length, last_byte, \ + min_frame_size, max_frame_size) \ + (e1000_tbi_sbp_enabled_82543(a) && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((min_frame_size) - VLAN_TAG_SIZE)) && \ + ((length) <= ((max_frame_size) + 1))) : \ + (((length) > (min_frame_size)) && \ + ((length) <= ((max_frame_size) + VLAN_TAG_SIZE + 1))))) + +#ifndef E1000_MAX +#define E1000_MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef E1000_DIVIDE_ROUND_UP +#define E1000_DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b)) /* ceil(a/b) */ +#endif +#endif /* _E1000_API_H_ */ diff --git a/drivers/staging/igb_avb/e1000_defines.h b/drivers/staging/igb_avb/e1000_defines.h new file mode 100644 index 000000000000..4022e22be775 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_defines.h @@ -0,0 +1,1486 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_DEFINES_H_ +#define _E1000_DEFINES_H_ + +/* Number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ +#define E1000_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC E1000_WUFC_LNKC +#define E1000_WUS_MAG E1000_WUFC_MAG +#define E1000_WUS_EX E1000_WUFC_EX +#define E1000_WUS_MC E1000_WUFC_MC +#define E1000_WUS_BC E1000_WUFC_BC + +/* Extended Device Control */ +#define E1000_CTRL_EXT_LPCD 0x00000004 /* LCD Power Cycle Done */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* SW Definable Pin 4 data */ +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* SW Definable Pin 6 data */ +#define E1000_CTRL_EXT_SDP2_DATA 0x00000040 /* SW Definable Pin 2 data */ +#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* SW Definable Pin 3 data */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP2_DIR 0x00000400 /* Direction of SDP2 0=in 1=out */ +#define E1000_CTRL_EXT_SDP3_DIR 0x00000800 /* Direction of SDP3 0=in 1=out */ +#define E1000_CTRL_EXT_FORCE_SMBUS 0x00000800 /* Force SMBus mode */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +/* Physical Func Reset Done Indication */ +#define E1000_CTRL_EXT_PFRSTD 0x00004000 +#define E1000_CTRL_EXT_SDLPE 0X00040000 /* SerDes Low Power Enable */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clk Gating */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +/* Offset of the link mode field in Ctrl Ext register */ +#define E1000_CTRL_EXT_LINK_MODE_OFFSET 22 +#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 +#define E1000_CTRL_EXT_EIAME 0x01000000 +#define E1000_CTRL_EXT_IRCA 0x00000001 +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Drv loaded bit for FW */ +#define E1000_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */ +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_CTRL_EXT_PHYPDEN 0x00100000 +#define E1000_I2CCMD_REG_ADDR_SHIFT 16 +#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 +#define E1000_I2CCMD_OPCODE_READ 0x08000000 +#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 +#define E1000_I2CCMD_READY 0x20000000 +#define E1000_I2CCMD_ERROR 0x80000000 +#define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a)) +#define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a)) +#define E1000_MAX_SGMII_PHY_REG_ADDR 255 +#define E1000_I2CCMD_PHY_TIMEOUT 200 +#define E1000_IVAR_VALID 0x80 +#define E1000_GPIE_NSICR 0x00000001 +#define E1000_GPIE_MSIX_MODE 0x00000010 +#define E1000_GPIE_EIAME 0x40000000 +#define E1000_GPIE_PBA 0x80000000 + +/* Receive Descriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ + +#define E1000_RXDEXT_STATERR_TST 0x00000100 /* Time Stamp taken */ +#define E1000_RXDEXT_STATERR_LB 0x00040000 +#define E1000_RXDEXT_STATERR_CE 0x01000000 +#define E1000_RXDEXT_STATERR_SE 0x02000000 +#define E1000_RXDEXT_STATERR_SEQ 0x04000000 +#define E1000_RXDEXT_STATERR_CXE 0x10000000 +#define E1000_RXDEXT_STATERR_TCPE 0x20000000 +#define E1000_RXDEXT_STATERR_IPE 0x40000000 +#define E1000_RXDEXT_STATERR_RXE 0x80000000 + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Same mask, but for extended and packet split descriptors */ +#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \ + E1000_RXDEXT_STATERR_CE | \ + E1000_RXDEXT_STATERR_SE | \ + E1000_RXDEXT_STATERR_SEQ | \ + E1000_RXDEXT_STATERR_CXE | \ + E1000_RXDEXT_STATERR_RXE) + +#define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000 +#define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000 +#define E1000_MRQC_RSS_FIELD_IPV4 0x00020000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000 +#define E1000_MRQC_RSS_FIELD_IPV6 0x00100000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +/* Enable MAC address filtering */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 +/* Enable MNG packets to host memory */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 + +#define E1000_MANC2H_PORT_623 0x00000020 /* Port 0x26f */ +#define E1000_MANC2H_PORT_664 0x00000040 /* Port 0x298 */ +#define E1000_MDEF_PORT_623 0x00000800 /* Port 0x26f */ +#define E1000_MDEF_PORT_664 0x00000400 /* Port 0x298 */ + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promisc enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promisc enable */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* Rx desc min thresh size */ +#define E1000_RCTL_RDMTS_HEX 0x00010000 +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* Rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* Rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* Rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* Rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* Rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* Rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* Rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ + +/* Use byte values for the following shift parameters + * Usage: + * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) & + * E1000_PSRCTL_BSIZE0_MASK) | + * ((ROUNDUP(value1, 1024) >> E1000_PSRCTL_BSIZE1_SHIFT) & + * E1000_PSRCTL_BSIZE1_MASK) | + * ((ROUNDUP(value2, 1024) << E1000_PSRCTL_BSIZE2_SHIFT) & + * E1000_PSRCTL_BSIZE2_MASK) | + * ((ROUNDUP(value3, 1024) << E1000_PSRCTL_BSIZE3_SHIFT) |; + * E1000_PSRCTL_BSIZE3_MASK)) + * where value0 = [128..16256], default=256 + * value1 = [1024..64512], default=4096 + * value2 = [0..64512], default=4096 + * value3 = [0..64512], default=0 + */ + +#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F +#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00 +#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 +#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 + +#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ +#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ +#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ +#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ + +/* SWFW_SYNC Definitions */ +#define E1000_SWFW_EEP_SM 0x01 +#define E1000_SWFW_PHY0_SM 0x02 +#define E1000_SWFW_PHY1_SM 0x04 +#define E1000_SWFW_CSR_SM 0x08 +#define E1000_SWFW_PHY2_SM 0x20 +#define E1000_SWFW_PHY3_SM 0x40 +#define E1000_SWFW_SW_MNG_SM 0x400 + +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master reqs */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SDP0_DIR 0x00400000 /* SDP0 Data direction */ +#define E1000_CTRL_SDP1_DIR 0x00800000 /* SDP1 Data direction */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_I2C_ENA 0x02000000 /* I2C enable */ + +#define E1000_CONNSW_ENRGSRC 0x4 +#define E1000_CONNSW_PHYSD 0x400 +#define E1000_CONNSW_PHY_PDN 0x800 +#define E1000_CONNSW_SERDESD 0x200 +#define E1000_CONNSW_AUTOSENSE_CONF 0x2 +#define E1000_CONNSW_AUTOSENSE_EN 0x1 +#define E1000_PCS_CFG_PCS_EN 8 +#define E1000_PCS_LCTL_FLV_LINK_UP 1 +#define E1000_PCS_LCTL_FSV_10 0 +#define E1000_PCS_LCTL_FSV_100 2 +#define E1000_PCS_LCTL_FSV_1000 4 +#define E1000_PCS_LCTL_FDV_FULL 8 +#define E1000_PCS_LCTL_FSD 0x10 +#define E1000_PCS_LCTL_FORCE_LINK 0x20 +#define E1000_PCS_LCTL_FORCE_FCTRL 0x80 +#define E1000_PCS_LCTL_AN_ENABLE 0x10000 +#define E1000_PCS_LCTL_AN_RESTART 0x20000 +#define E1000_PCS_LCTL_AN_TIMEOUT 0x40000 +#define E1000_ENABLE_SERDES_LOOPBACK 0x0410 + +#define E1000_PCS_LSTS_LINK_OK 1 +#define E1000_PCS_LSTS_SPEED_100 2 +#define E1000_PCS_LSTS_SPEED_1000 4 +#define E1000_PCS_LSTS_DUPLEX_FULL 8 +#define E1000_PCS_LSTS_SYNK_OK 0x10 +#define E1000_PCS_LSTS_AN_COMPLETE 0x10000 + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Duplex 0=half 1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_SHIFT 2 +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Compltn by NVM */ +#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Master request status */ +#define E1000_STATUS_2P5_SKU 0x00001000 /* Val of 2.5GBE SKU strap */ +#define E1000_STATUS_2P5_SKU_OVER 0x00002000 /* Val of 2.5GBE SKU Over */ + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ +#define ADVERTISE_1000_FULL 0x0020 + +/* 1000/H is not supported, nor spec-compliant. */ +#define E1000_ALL_SPEED_DUPLEX ( \ + ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \ + ADVERTISE_100_FULL | ADVERTISE_1000_FULL) +#define E1000_ALL_NOT_GIG ( \ + ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \ + ADVERTISE_100_FULL) +#define E1000_ALL_100_SPEED (ADVERTISE_100_HALF | ADVERTISE_100_FULL) +#define E1000_ALL_10_SPEED (ADVERTISE_10_HALF | ADVERTISE_10_FULL) +#define E1000_ALL_HALF_DUPLEX (ADVERTISE_10_HALF | ADVERTISE_100_HALF) + +#define AUTONEG_ADVERTISE_SPEED_DEFAULT E1000_ALL_SPEED_DUPLEX + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 + +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +#define E1000_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */ + +/* Transmit Control */ +#define E1000_TCTL_EN 0x00000002 /* enable Tx */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Transmit Arbitration Count */ +#define E1000_TARC0_ENABLE 0x00000400 /* Enable Tx Queue 0 */ + +/* SerDes Control */ +#define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400 +#define E1000_SCTL_ENABLE_SERDES_LOOPBACK 0x0410 + +/* Receive Checksum Control */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */ +#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ + +/* Header split receive */ +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_ACK_DIS 0x00001000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 +#define E1000_RFCTL_LEF 0x00040000 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLD_SHIFT 12 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF + +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ + +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x3F00 +#define E1000_TX_PTR_GAP 0x1F + +/* Extended Configuration Control and Size */ +#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 +#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008 +#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 +#define E1000_EXTCNF_CTRL_GATE_PHY_CFG 0x00000080 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK 0x0FFF0000 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT 16 + +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 + +#define E1000_KABGTXD_BGSQLBIAS 0x00050000 + +/* PBA constants */ +#define E1000_PBA_8K 0x0008 /* 8KB */ +#define E1000_PBA_10K 0x000A /* 10KB */ +#define E1000_PBA_12K 0x000C /* 12KB */ +#define E1000_PBA_14K 0x000E /* 14KB */ +#define E1000_PBA_16K 0x0010 /* 16KB */ +#define E1000_PBA_18K 0x0012 +#define E1000_PBA_20K 0x0014 +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_26K 0x001A +#define E1000_PBA_30K 0x001E +#define E1000_PBA_32K 0x0020 +#define E1000_PBA_34K 0x0022 +#define E1000_PBA_35K 0x0023 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB */ +#define E1000_PBA_64K 0x0040 /* 64KB */ + +#define E1000_PBA_RXA_MASK 0xFFFF + +#define E1000_PBS_16K E1000_PBA_16K + +/* Uncorrectable/correctable ECC Error counts and enable bits */ +#define E1000_PBECCSTS_CORR_ERR_CNT_MASK 0x000000FF +#define E1000_PBECCSTS_UNCORR_ERR_CNT_MASK 0x0000FF00 +#define E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT 8 +#define E1000_PBECCSTS_ECC_ENABLE 0x00010000 + +#define IFS_MAX 80 +#define IFS_MIN 40 +#define IFS_RATIO 4 +#define IFS_STEP 10 +#define MIN_NUM_XMITS 1000 + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +#define E1000_SWSM2_LOCK 0x00000002 /* Secondary driver semaphore bit */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* Rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* Rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* Rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* Rx timer intr (ring 0) */ +#define E1000_ICR_VMMB 0x00000100 /* VM MB event */ +#define E1000_ICR_RXCFG 0x00000400 /* Rx /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_ECCER 0x00400000 /* Uncorrectable ECC Error */ +#define E1000_ICR_TS 0x00080000 /* Time Sync Interrupt */ +#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */ +/* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_INT_ASSERTED 0x80000000 +#define E1000_ICR_DOUTSYNC 0x10000000 /* NIC DMA out of sync */ +#define E1000_ICR_FER 0x00400000 /* Fatal Error */ + +#define E1000_ICR_THS 0x00800000 /* ICR.THS: Thermal Sensor Event*/ +#define E1000_ICR_MDDET 0x10000000 /* Malicious Driver Detect */ + +/* Extended Interrupt Cause Read */ +#define E1000_EICR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */ +#define E1000_EICR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */ +#define E1000_EICR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */ +#define E1000_EICR_RX_QUEUE3 0x00000008 /* Rx Queue 3 Interrupt */ +#define E1000_EICR_TX_QUEUE0 0x00000100 /* Tx Queue 0 Interrupt */ +#define E1000_EICR_TX_QUEUE1 0x00000200 /* Tx Queue 1 Interrupt */ +#define E1000_EICR_TX_QUEUE2 0x00000400 /* Tx Queue 2 Interrupt */ +#define E1000_EICR_TX_QUEUE3 0x00000800 /* Tx Queue 3 Interrupt */ +#define E1000_EICR_TCP_TIMER 0x40000000 /* TCP Timer */ +#define E1000_EICR_OTHER 0x80000000 /* Interrupt Cause Active */ +/* TCP Timer */ +#define E1000_TCPTIMER_KS 0x00000100 /* KickStart */ +#define E1000_TCPTIMER_COUNT_ENABLE 0x00000200 /* Count Enable */ +#define E1000_TCPTIMER_COUNT_FINISH 0x00000400 /* Count finish */ +#define E1000_TCPTIMER_LOOP 0x00000800 /* Loop */ + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Tx desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_VMMB E1000_ICR_VMMB /* Mail box activity */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* Rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* Rx timer intr */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_ECCER E1000_ICR_ECCER /* Uncorrectable ECC Error */ +#define E1000_IMS_TS E1000_ICR_TS /* Time Sync Interrupt */ +#define E1000_IMS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */ +#define E1000_IMS_DOUTSYNC E1000_ICR_DOUTSYNC /* NIC DMA out of sync */ +#define E1000_IMS_FER E1000_ICR_FER /* Fatal Error */ + +#define E1000_IMS_THS E1000_ICR_THS /* ICR.TS: Thermal Sensor Event*/ +#define E1000_IMS_MDDET E1000_ICR_MDDET /* Malicious Driver Detect */ +/* Extended Interrupt Mask Set */ +#define E1000_EIMS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EIMS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EIMS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EIMS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EIMS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EIMS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EIMS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EIMS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EIMS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EIMS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +/* Interrupt Cause Set */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ + +/* Extended Interrupt Cause Set */ +#define E1000_EICS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EICS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EICS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EICS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EICS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EICS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EICS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EICS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EICS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EICS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +#define E1000_EITR_ITR_INT_MASK 0x0000FFFF +/* E1000_EITR_CNT_IGNR is only for 82576 and newer */ +#define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */ +#define E1000_EITR_INTERVAL 0x00007FFC + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_MAX_TX_DESC_PREFETCH 0x0100001F /* GRAN=1, PTHRESH=31 */ +/* Enable the counting of descriptors still to be processed. */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* 802.1q VLAN Packet Size */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Receive Address + * Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * Technically, we have 16 spots. However, we reserve one of these spots + * (RAR[15]) for our directed address used by controllers with + * manageability enabled, allowing us room for 15 multicast addresses. + */ +#define E1000_RAR_ENTRIES 15 +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RAL_MAC_ADDR_LEN 4 +#define E1000_RAH_MAC_ADDR_LEN 2 +#define E1000_RAH_QUEUE_MASK_82575 0x000C0000 +#define E1000_RAH_POOL_1 0x00040000 + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_NVM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_INIT 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 +#define E1000_NOT_IMPLEMENTED 14 +#define E1000_ERR_MBX 15 +#define E1000_ERR_INVALID_ARGUMENT 16 +#define E1000_ERR_NO_SPACE 17 +#define E1000_ERR_NVM_PBA_SECTION 18 +#define E1000_ERR_I2C 19 +#define E1000_ERR_INVM_VALUE_NOT_FOUND 20 + +/* Loop limit on how long we wait for auto-negotiation to complete */ +#define FIBER_LINK_UP_LIMIT 50 +#define COPPER_LINK_UP_LIMIT 10 +#define PHY_AUTO_NEG_LIMIT 45 +#define PHY_FORCE_LIMIT 20 +/* Number of 100 microseconds we wait for PCI Express master disable */ +#define MASTER_DISABLE_TIMEOUT 800 +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 +/* Number of 2 milliseconds we wait for acquiring MDIO ownership. */ +#define MDIO_OWNERSHIP_TIMEOUT 10 +/* Number of milliseconds for NVM auto read done after MAC reset. */ +#define AUTO_READ_DONE_TIMEOUT 10 + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ + +#define E1000_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */ +#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable Tx timestamping */ + +#define E1000_TSYNCRXCTL_VALID 0x00000001 /* Rx timestamp valid */ +#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */ +#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00 +#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02 +#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04 +#define E1000_TSYNCRXCTL_TYPE_ALL 0x08 +#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A +#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable Rx timestamping */ +#define E1000_TSYNCRXCTL_SYSCFI 0x00000020 /* Sys clock frequency */ + +#define E1000_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF +#define E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01 +#define E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03 +#define E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04 + +#define E1000_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00 +#define E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300 +#define E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00 +#define E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00 +#define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00 +#define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00 + +#define E1000_TIMINCA_16NS_SHIFT 24 +#define E1000_TIMINCA_INCPERIOD_SHIFT 24 +#define E1000_TIMINCA_INCVALUE_MASK 0x00FFFFFF + +/* Time Sync Interrupt Cause/Mask Register Bits */ + +#define TSINTR_SYS_WRAP (1 << 0) /* SYSTIM Wrap around. */ +#define TSINTR_TXTS (1 << 1) /* Transmit Timestamp. */ +#define TSINTR_RXTS (1 << 2) /* Receive Timestamp. */ +#define TSINTR_TT0 (1 << 3) /* Target Time 0 Trigger. */ +#define TSINTR_TT1 (1 << 4) /* Target Time 1 Trigger. */ +#define TSINTR_AUTT0 (1 << 5) /* Auxiliary Timestamp 0 Taken. */ +#define TSINTR_AUTT1 (1 << 6) /* Auxiliary Timestamp 1 Taken. */ +#define TSINTR_TADJ (1 << 7) /* Time Adjust Done. */ + +#define TSYNC_INTERRUPTS TSINTR_TXTS +#define E1000_TSICR_TXTS TSINTR_TXTS + +/* TSAUXC Configuration Bits */ +#define TSAUXC_EN_TT0 (1 << 0) /* Enable target time 0. */ +#define TSAUXC_EN_TT1 (1 << 1) /* Enable target time 1. */ +#define TSAUXC_EN_CLK0 (1 << 2) /* Enable Configurable Frequency Clock 0. */ +#define TSAUXC_SAMP_AUT0 (1 << 3) /* Latch SYSTIML/H into AUXSTMPL/0. */ +#define TSAUXC_ST0 (1 << 4) /* Start Clock 0 Toggle on Target Time 0. */ +#define TSAUXC_EN_CLK1 (1 << 5) /* Enable Configurable Frequency Clock 1. */ +#define TSAUXC_SAMP_AUT1 (1 << 6) /* Latch SYSTIML/H into AUXSTMPL/1. */ +#define TSAUXC_ST1 (1 << 7) /* Start Clock 1 Toggle on Target Time 1. */ +#define TSAUXC_EN_TS0 (1 << 8) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT0 (1 << 9) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_EN_TS1 (1 << 10) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT1 (1 << 11) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_PLSG (1 << 17) /* Generate a pulse. */ +#define TSAUXC_DISABLE (1 << 31) /* Disable SYSTIM Count Operation. */ + +/* SDP Configuration Bits */ +#define AUX0_SEL_SDP0 (0 << 0) /* Assign SDP0 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP1 (1 << 0) /* Assign SDP1 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP2 (2 << 0) /* Assign SDP2 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP3 (3 << 0) /* Assign SDP3 to auxiliary time stamp 0. */ +#define AUX0_TS_SDP_EN (1 << 2) /* Enable auxiliary time stamp trigger 0. */ +#define AUX1_SEL_SDP0 (0 << 3) /* Assign SDP0 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP1 (1 << 3) /* Assign SDP1 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP2 (2 << 3) /* Assign SDP2 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP3 (3 << 3) /* Assign SDP3 to auxiliary time stamp 1. */ +#define AUX1_TS_SDP_EN (1 << 5) /* Enable auxiliary time stamp trigger 1. */ +#define TS_SDP0_SEL_TT0 (0 << 6) /* Target time 0 is output on SDP0. */ +#define TS_SDP0_SEL_TT1 (1 << 6) /* Target time 1 is output on SDP0. */ +#define TS_SDP0_SEL_FC0 (2 << 6) /* Freq clock 0 is output on SDP0. */ +#define TS_SDP0_SEL_FC1 (3 << 6) /* Freq clock 1 is output on SDP0. */ +#define TS_SDP0_EN (1 << 8) /* SDP0 is assigned to Tsync. */ +#define TS_SDP1_SEL_TT0 (0 << 9) /* Target time 0 is output on SDP1. */ +#define TS_SDP1_SEL_TT1 (1 << 9) /* Target time 1 is output on SDP1. */ +#define TS_SDP1_SEL_FC0 (2 << 9) /* Freq clock 0 is output on SDP1. */ +#define TS_SDP1_SEL_FC1 (3 << 9) /* Freq clock 1 is output on SDP1. */ +#define TS_SDP1_EN (1 << 11) /* SDP1 is assigned to Tsync. */ +#define TS_SDP2_SEL_TT0 (0 << 12) /* Target time 0 is output on SDP2. */ +#define TS_SDP2_SEL_TT1 (1 << 12) /* Target time 1 is output on SDP2. */ +#define TS_SDP2_SEL_FC0 (2 << 12) /* Freq clock 0 is output on SDP2. */ +#define TS_SDP2_SEL_FC1 (3 << 12) /* Freq clock 1 is output on SDP2. */ +#define TS_SDP2_EN (1 << 14) /* SDP2 is assigned to Tsync. */ +#define TS_SDP3_SEL_TT0 (0 << 15) /* Target time 0 is output on SDP3. */ +#define TS_SDP3_SEL_TT1 (1 << 15) /* Target time 1 is output on SDP3. */ +#define TS_SDP3_SEL_FC0 (2 << 15) /* Freq clock 0 is output on SDP3. */ +#define TS_SDP3_SEL_FC1 (3 << 15) /* Freq clock 1 is output on SDP3. */ +#define TS_SDP3_EN (1 << 17) /* SDP3 is assigned to Tsync. */ +/* TUPLE Filtering Configuration */ +#define E1000_TTQF_DISABLE_MASK 0xF0008000 /* TTQF Disable Mask */ +#define E1000_TTQF_QUEUE_ENABLE 0x100 /* TTQF Queue Enable Bit */ +#define E1000_TTQF_PROTOCOL_MASK 0xFF /* TTQF Protocol Mask */ +/* TTQF TCP Bit, shift with E1000_TTQF_PROTOCOL SHIFT */ +#define E1000_TTQF_PROTOCOL_TCP 0x0 +/* TTQF UDP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */ +#define E1000_TTQF_PROTOCOL_UDP 0x1 +/* TTQF SCTP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */ +#define E1000_TTQF_PROTOCOL_SCTP 0x2 +#define E1000_TTQF_PROTOCOL_SHIFT 5 /* TTQF Protocol Shift */ +#define E1000_TTQF_QUEUE_SHIFT 16 /* TTQF Queue Shfit */ +#define E1000_TTQF_RX_QUEUE_MASK 0x70000 /* TTQF Queue Mask */ +#define E1000_TTQF_MASK_ENABLE 0x10000000 /* TTQF Mask Enable Bit */ +#define E1000_IMIR_CLEAR_MASK 0xF001FFFF /* IMIR Reg Clear Mask */ +#define E1000_IMIR_PORT_BYPASS 0x20000 /* IMIR Port Bypass Bit */ +#define E1000_IMIR_PRIORITY_SHIFT 29 /* IMIR Priority Shift */ +#define E1000_IMIREXT_CLEAR_MASK 0x7FFFF /* IMIREXT Reg Clear Mask */ + +#define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */ +#define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */ +#define E1000_MDICNFG_PHY_MASK 0x03E00000 +#define E1000_MDICNFG_PHY_SHIFT 21 + +#define E1000_MEDIA_PORT_COPPER 1 +#define E1000_MEDIA_PORT_OTHER 2 +#define E1000_M88E1112_AUTO_COPPER_SGMII 0x2 +#define E1000_M88E1112_AUTO_COPPER_BASEX 0x3 +#define E1000_M88E1112_STATUS_LINK 0x0004 /* Interface Link Bit */ +#define E1000_M88E1112_MAC_CTRL_1 0x10 +#define E1000_M88E1112_MAC_CTRL_1_MODE_MASK 0x0380 /* Mode Select */ +#define E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT 7 +#define E1000_M88E1112_PAGE_ADDR 0x16 +#define E1000_M88E1112_STATUS 0x01 + +#define E1000_THSTAT_LOW_EVENT 0x20000000 /* Low thermal threshold */ +#define E1000_THSTAT_MID_EVENT 0x00200000 /* Mid thermal threshold */ +#define E1000_THSTAT_HIGH_EVENT 0x00002000 /* High thermal threshold */ +#define E1000_THSTAT_PWR_DOWN 0x00000001 /* Power Down Event */ +#define E1000_THSTAT_LINK_THROTTLE 0x00000002 /* Link Spd Throttle Event */ + +/* I350 EEE defines */ +#define E1000_IPCNFG_EEE_1G_AN 0x00000008 /* IPCNFG EEE Ena 1G AN */ +#define E1000_IPCNFG_EEE_100M_AN 0x00000004 /* IPCNFG EEE Ena 100M AN */ +#define E1000_EEER_TX_LPI_EN 0x00010000 /* EEER Tx LPI Enable */ +#define E1000_EEER_RX_LPI_EN 0x00020000 /* EEER Rx LPI Enable */ +#define E1000_EEER_LPI_FC 0x00040000 /* EEER Ena on Flow Cntrl */ +/* EEE status */ +#define E1000_EEER_EEE_NEG 0x20000000 /* EEE capability nego */ +#define E1000_EEER_RX_LPI_STATUS 0x40000000 /* Rx in LPI state */ +#define E1000_EEER_TX_LPI_STATUS 0x80000000 /* Tx in LPI state */ +#define E1000_EEE_LP_ADV_ADDR_I350 0x040F /* EEE LP Advertisement */ +#define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */ +#define E1000_M88E1543_EEE_CTRL_1 0x0 +#define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */ +#define E1000_EEE_ADV_DEV_I354 7 +#define E1000_EEE_ADV_ADDR_I354 60 +#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */ +#define E1000_EEE_ADV_1000_SUPPORTED (1 << 2) /* 1000BaseT EEE Supported */ +#define E1000_PCS_STATUS_DEV_I354 3 +#define E1000_PCS_STATUS_ADDR_I354 1 +#define E1000_PCS_STATUS_RX_LPI_RCVD 0x0400 +#define E1000_PCS_STATUS_TX_LPI_RCVD 0x0800 +#define E1000_M88E1512_CFG_REG_1 0x0010 +#define E1000_M88E1512_CFG_REG_2 0x0011 +#define E1000_M88E1512_CFG_REG_3 0x0007 +#define E1000_M88E1512_MODE 0x0014 +#define E1000_EEE_SU_LPI_CLK_STP 0x00800000 /* EEE LPI Clock Stop */ +#define E1000_EEE_LP_ADV_DEV_I210 7 /* EEE LP Adv Device */ +#define E1000_EEE_LP_ADV_ADDR_I210 61 /* EEE LP Adv Register */ +/* PCI Express Control */ +#define E1000_GCR_RXD_NO_SNOOP 0x00000001 +#define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002 +#define E1000_GCR_RXDSCR_NO_SNOOP 0x00000004 +#define E1000_GCR_TXD_NO_SNOOP 0x00000008 +#define E1000_GCR_TXDSCW_NO_SNOOP 0x00000010 +#define E1000_GCR_TXDSCR_NO_SNOOP 0x00000020 +#define E1000_GCR_CMPL_TMOUT_MASK 0x0000F000 +#define E1000_GCR_CMPL_TMOUT_10ms 0x00001000 +#define E1000_GCR_CMPL_TMOUT_RESEND 0x00010000 +#define E1000_GCR_CAP_VER2 0x00040000 + +#define PCIE_NO_SNOOP_ALL (E1000_GCR_RXD_NO_SNOOP | \ + E1000_GCR_RXDSCW_NO_SNOOP | \ + E1000_GCR_RXDSCR_NO_SNOOP | \ + E1000_GCR_TXD_NO_SNOOP | \ + E1000_GCR_TXDSCW_NO_SNOOP | \ + E1000_GCR_TXDSCR_NO_SNOOP) + +#define E1000_MMDAC_FUNC_DATA 0x4000 /* Data, no post increment */ + +/* mPHY address control and data registers */ +#define E1000_MPHY_ADDR_CTL 0x0024 /* Address Control Reg */ +#define E1000_MPHY_ADDR_CTL_OFFSET_MASK 0xFFFF0000 +#define E1000_MPHY_DATA 0x0E10 /* Data Register */ + +/* AFE CSR Offset for PCS CLK */ +#define E1000_MPHY_PCS_CLK_REG_OFFSET 0x0004 +/* Override for near end digital loopback. */ +#define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10 + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP 10T Half Dplx Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP 10T Full Dplx Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP 100TX Half Dplx Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP 100TX Full Dplx Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asym Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP 10T Half Dplx Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP 10T Full Dplx Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP 100TX Half Dplx Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP 100TX Full Dplx Capable */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +/* 1=Repeater/switch device port 0=DTE device */ +#define CR_1000T_REPEATER_DTE 0x0400 +/* 1=Configure PHY as Master 0=Configure PHY as Slave */ +#define CR_1000T_MS_VALUE 0x0800 +/* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ +#define CR_1000T_MS_ENABLE 0x1000 +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle err since last rd */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asym pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local Tx Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ + +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CONTROL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Register */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page Tx */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +#define PHY_CONTROL_LB 0x4000 /* PHY Loopback bit */ + +/* NVM Control */ +#define E1000_EECD_SK 0x00000001 /* NVM Clock */ +#define E1000_EECD_CS 0x00000002 /* NVM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* NVM Data In */ +#define E1000_EECD_DO 0x00000008 /* NVM Data Out */ +#define E1000_EECD_REQ 0x00000040 /* NVM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* NVM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* NVM Present */ +#define E1000_EECD_SIZE 0x00000200 /* NVM Size (0=64 word 1=256 word) */ +#define E1000_EECD_BLOCKED 0x00008000 /* Bit banging access blocked flag */ +#define E1000_EECD_ABORT 0x00010000 /* NVM operation aborted flag */ +#define E1000_EECD_TIMEOUT 0x00020000 /* NVM read operation timeout flag */ +#define E1000_EECD_ERROR_CLR 0x00040000 /* NVM error status clear bit */ +/* NVM Addressing bits based on type 0=small, 1=large */ +#define E1000_EECD_ADDR_BITS 0x00000400 +#define E1000_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */ +#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Ena Auto FLASH update */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SEC1VAL_VALID_MASK (E1000_EECD_AUTO_RD | E1000_EECD_PRES) +#define E1000_EECD_FLUPD_I210 0x00800000 /* Update FLASH */ +#define E1000_EECD_FLUDONE_I210 0x04000000 /* Update FLASH done */ +#define E1000_EECD_FLASH_DETECTED_I210 0x00080000 /* FLASH detected */ +#define E1000_EECD_SEC1VAL_I210 0x02000000 /* Sector One Valid */ +#define E1000_FLUDONE_ATTEMPTS 20000 +#define E1000_EERD_EEWR_MAX_COUNT 512 /* buffered EEPROM words rw */ +#define E1000_I210_FIFO_SEL_RX 0x00 +#define E1000_I210_FIFO_SEL_TX_QAV(_i) (0x02 + (_i)) +#define E1000_I210_FIFO_SEL_TX_LEGACY E1000_I210_FIFO_SEL_TX_QAV(0) +#define E1000_I210_FIFO_SEL_BMC2OS_TX 0x06 +#define E1000_I210_FIFO_SEL_BMC2OS_RX 0x01 + +#define E1000_I210_FLASH_SECTOR_SIZE 0x1000 /* 4KB FLASH sector unit size */ +/* Secure FLASH mode requires removing MSb */ +#define E1000_I210_FW_PTR_MASK 0x7FFF +/* Firmware code revision field word offset*/ +#define E1000_I210_FW_VER_OFFSET 328 + +#define E1000_NVM_RW_REG_DATA 16 /* Offset to data in NVM read/write regs */ +#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_NVM_RW_REG_START 1 /* Start operation */ +#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_NVM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */ +#define E1000_FLASH_UPDATES 2000 + +/* NVM Word Offsets */ +#define NVM_COMPAT 0x0003 +#define NVM_ID_LED_SETTINGS 0x0004 +#define NVM_VERSION 0x0005 +#define E1000_I210_NVM_FW_MODULE_PTR 0x0010 +#define E1000_I350_NVM_FW_MODULE_PTR 0x0051 +#define NVM_FUTURE_INIT_WORD1 0x0019 +#define NVM_ETRACK_WORD 0x0042 +#define NVM_ETRACK_HIWORD 0x0043 +#define NVM_COMB_VER_OFF 0x0083 +#define NVM_COMB_VER_PTR 0x003d + +/* NVM version defines */ +#define NVM_MAJOR_MASK 0xF000 +#define NVM_MINOR_MASK 0x0FF0 +#define NVM_IMAGE_ID_MASK 0x000F +#define NVM_COMB_VER_MASK 0x00FF +#define NVM_MAJOR_SHIFT 12 +#define NVM_MINOR_SHIFT 4 +#define NVM_COMB_VER_SHFT 8 +#define NVM_VER_INVALID 0xFFFF +#define NVM_ETRACK_SHIFT 16 +#define NVM_ETRACK_VALID 0x8000 +#define NVM_NEW_DEC_MASK 0x0F00 +#define NVM_HEX_CONV 16 +#define NVM_HEX_TENS 10 + +/* FW version defines */ +/* Offset of "Loader patch ptr" in Firmware Header */ +#define E1000_I350_NVM_FW_LOADER_PATCH_PTR_OFFSET 0x01 +/* Patch generation hour & minutes */ +#define E1000_I350_NVM_FW_VER_WORD1_OFFSET 0x04 +/* Patch generation month & day */ +#define E1000_I350_NVM_FW_VER_WORD2_OFFSET 0x05 +/* Patch generation year */ +#define E1000_I350_NVM_FW_VER_WORD3_OFFSET 0x06 +/* Patch major & minor numbers */ +#define E1000_I350_NVM_FW_VER_WORD4_OFFSET 0x07 + +#define NVM_MAC_ADDR 0x0000 +#define NVM_SUB_DEV_ID 0x000B +#define NVM_SUB_VEN_ID 0x000C +#define NVM_DEV_ID 0x000D +#define NVM_VEN_ID 0x000E +#define NVM_INIT_CTRL_2 0x000F +#define NVM_INIT_CTRL_4 0x0013 +#define NVM_LED_1_CFG 0x001C +#define NVM_LED_0_2_CFG 0x001F + +#define NVM_COMPAT_VALID_CSUM 0x0001 +#define NVM_FUTURE_INIT_WORD1_VALID_CSUM 0x0040 + +#define NVM_ETS_CFG 0x003E +#define NVM_ETS_LTHRES_DELTA_MASK 0x07C0 +#define NVM_ETS_LTHRES_DELTA_SHIFT 6 +#define NVM_ETS_TYPE_MASK 0x0038 +#define NVM_ETS_TYPE_SHIFT 3 +#define NVM_ETS_TYPE_EMC 0x000 +#define NVM_ETS_NUM_SENSORS_MASK 0x0007 +#define NVM_ETS_DATA_LOC_MASK 0x3C00 +#define NVM_ETS_DATA_LOC_SHIFT 10 +#define NVM_ETS_DATA_INDEX_MASK 0x0300 +#define NVM_ETS_DATA_INDEX_SHIFT 8 +#define NVM_ETS_DATA_HTHRESH_MASK 0x00FF +#define NVM_INIT_CONTROL2_REG 0x000F +#define NVM_INIT_CONTROL3_PORT_B 0x0014 +#define NVM_INIT_3GIO_3 0x001A +#define NVM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define NVM_INIT_CONTROL3_PORT_A 0x0024 +#define NVM_CFG 0x0012 +#define NVM_ALT_MAC_ADDR_PTR 0x0037 +#define NVM_CHECKSUM_REG 0x003F +#define NVM_COMPATIBILITY_REG_3 0x0003 +#define NVM_COMPATIBILITY_BIT_MASK 0x8000 + +#define E1000_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */ +#define E1000_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */ +#define E1000_NVM_CFG_DONE_PORT_2 0x100000 /* ...for third port */ +#define E1000_NVM_CFG_DONE_PORT_3 0x200000 /* ...for fourth port */ + +#define NVM_82580_LAN_FUNC_OFFSET(a) ((a) ? (0x40 + (0x40 * (a))) : 0) + +/* Mask bits for fields in Word 0x24 of the NVM */ +#define NVM_WORD24_COM_MDIO 0x0008 /* MDIO interface shared */ +#define NVM_WORD24_EXT_MDIO 0x0004 /* MDIO accesses routed extrnl */ +/* Offset of Link Mode bits for 82575/82576 */ +#define NVM_WORD24_LNK_MODE_OFFSET 8 +/* Offset of Link Mode bits for 82580 up */ +#define NVM_WORD24_82580_LNK_MODE_OFFSET 4 + +/* Mask bits for fields in Word 0x0f of the NVM */ +#define NVM_WORD0F_PAUSE_MASK 0x3000 +#define NVM_WORD0F_PAUSE 0x1000 +#define NVM_WORD0F_ASM_DIR 0x2000 + +/* Mask bits for fields in Word 0x1a of the NVM */ +#define NVM_WORD1A_ASPM_MASK 0x000C + +/* Mask bits for fields in Word 0x03 of the EEPROM */ +#define NVM_COMPAT_LOM 0x0800 + +/* length of string needed to store PBA number */ +#define E1000_PBANUM_LENGTH 11 + +/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */ +#define NVM_SUM 0xBABA + +/* PBA (printed board assembly) number words */ +#define NVM_PBA_OFFSET_0 8 +#define NVM_PBA_OFFSET_1 9 +#define NVM_PBA_PTR_GUARD 0xFAFA +#define NVM_RESERVED_WORD 0xFFFF +#define NVM_WORD_SIZE_BASE_SHIFT 6 + +/* NVM Commands - SPI */ +#define NVM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define NVM_READ_OPCODE_SPI 0x03 /* NVM read opcode */ +#define NVM_WRITE_OPCODE_SPI 0x02 /* NVM write opcode */ +#define NVM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define NVM_WREN_OPCODE_SPI 0x06 /* NVM set Write Enable latch */ +#define NVM_RDSR_OPCODE_SPI 0x05 /* NVM read Status register */ + +/* SPI NVM Status Register */ +#define NVM_STATUS_RDY_SPI 0x01 + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + +/* PCI/PCI-X/PCI-EX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA +#define PCI_HEADER_TYPE_REGISTER 0x0E +#define PCIE_LINK_STATUS 0x12 +#define PCIE_DEVICE_CONTROL2 0x28 + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 +#define PCIX_STATUS_LO_FUNC_MASK 0x7 +#define PCI_HEADER_TYPE_MULTIFUNC 0x80 +#define PCIE_LINK_WIDTH_MASK 0x3F0 +#define PCIE_LINK_WIDTH_SHIFT 4 +#define PCIE_LINK_SPEED_MASK 0x0F +#define PCIE_LINK_SPEED_2500 0x01 +#define PCIE_LINK_SPEED_5000 0x02 +#define PCIE_DEVICE_CONTROL2_16ms 0x0005 + +#ifndef ETH_ADDR_LEN +#define ETH_ADDR_LEN 6 +#endif + +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF + +/* Bit definitions for valid PHY IDs. + * I = Integrated + * E = External + */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define M88E1543_E_PHY_ID 0x01410EA0 +#define M88E1512_E_PHY_ID 0x01410DD0 +#define M88E1112_E_PHY_ID 0x01410C90 +#define I347AT4_E_PHY_ID 0x01410DC0 +#define M88E1340M_E_PHY_ID 0x01410DF0 +#define GG82563_E_PHY_ID 0x01410CA0 +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 +#define I82580_I_PHY_ID 0x015403A0 +#define I350_I_PHY_ID 0x015403B0 +#define I210_I_PHY_ID 0x01410C00 +#define IGP04E1000_E_PHY_ID 0x02A80391 +#define M88_VENDOR 0x0141 + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Reg */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Reg */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Cntrl */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for pg number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* meaning depends on reg 29 */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reverse enabled */ +/* MDI Crossover Mode bits 6:5 Manual MDI configuration */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +/* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 +/* Auto crossover enabled all speeds */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Tx */ + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +/* 0 = <50M + * 1 = 50-80M + * 2 = 80-110M + * 3 = 110-140M + * 4 = >140M + */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master + */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave + */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ + +/* Intel I347AT4 Registers */ +#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */ +#define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */ +#define I347AT4_PAGE_SELECT 0x16 + +/* I347AT4 Extended PHY Specific Control Register */ + +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master + */ +#define I347AT4_PSCR_DOWNSHIFT_ENABLE 0x0800 +#define I347AT4_PSCR_DOWNSHIFT_MASK 0x7000 +#define I347AT4_PSCR_DOWNSHIFT_1X 0x0000 +#define I347AT4_PSCR_DOWNSHIFT_2X 0x1000 +#define I347AT4_PSCR_DOWNSHIFT_3X 0x2000 +#define I347AT4_PSCR_DOWNSHIFT_4X 0x3000 +#define I347AT4_PSCR_DOWNSHIFT_5X 0x4000 +#define I347AT4_PSCR_DOWNSHIFT_6X 0x5000 +#define I347AT4_PSCR_DOWNSHIFT_7X 0x6000 +#define I347AT4_PSCR_DOWNSHIFT_8X 0x7000 + +/* I347AT4 PHY Cable Diagnostics Control */ +#define I347AT4_PCDC_CABLE_LENGTH_UNIT 0x0400 /* 0=cm 1=meters */ + +/* M88E1112 only registers */ +#define M88E1112_VCT_DSP_DISTANCE 0x001A + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL GG82563_REG(0, 16) /* PHY Spec Cntrl */ +#define GG82563_PHY_PAGE_SELECT GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 GG82563_REG(0, 26) /* PHY Spec Cntrl2 */ +#define GG82563_PHY_PAGE_SELECT_ALT GG82563_REG(0, 29) /* Alt Page Select */ + +/* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL GG82563_REG(2, 21) + +#define GG82563_PHY_DSP_DISTANCE GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +/* Kumeran Mode Control */ +#define GG82563_PHY_KMRN_MODE_CTRL GG82563_REG(193, 16) +#define GG82563_PHY_PWR_MGMT_CTRL GG82563_REG(193, 20) /* Pwr Mgt Ctrl */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_INBAND_CTRL GG82563_REG(194, 18) /* Inband Ctrl */ + +/* MDI Control */ +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_ERROR 0x40000000 +#define E1000_MDIC_DEST 0x80000000 + +/* SerDes Control */ +#define E1000_GEN_CTL_READY 0x80000000 +#define E1000_GEN_CTL_ADDRESS_SHIFT 8 +#define E1000_GEN_POLL_TIMEOUT 640 + +/* LinkSec register fields */ +#define E1000_LSECTXCAP_SUM_MASK 0x00FF0000 +#define E1000_LSECTXCAP_SUM_SHIFT 16 +#define E1000_LSECRXCAP_SUM_MASK 0x00FF0000 +#define E1000_LSECRXCAP_SUM_SHIFT 16 + +#define E1000_LSECTXCTRL_EN_MASK 0x00000003 +#define E1000_LSECTXCTRL_DISABLE 0x0 +#define E1000_LSECTXCTRL_AUTH 0x1 +#define E1000_LSECTXCTRL_AUTH_ENCRYPT 0x2 +#define E1000_LSECTXCTRL_AISCI 0x00000020 +#define E1000_LSECTXCTRL_PNTHRSH_MASK 0xFFFFFF00 +#define E1000_LSECTXCTRL_RSV_MASK 0x000000D8 + +#define E1000_LSECRXCTRL_EN_MASK 0x0000000C +#define E1000_LSECRXCTRL_EN_SHIFT 2 +#define E1000_LSECRXCTRL_DISABLE 0x0 +#define E1000_LSECRXCTRL_CHECK 0x1 +#define E1000_LSECRXCTRL_STRICT 0x2 +#define E1000_LSECRXCTRL_DROP 0x3 +#define E1000_LSECRXCTRL_PLSH 0x00000040 +#define E1000_LSECRXCTRL_RP 0x00000080 +#define E1000_LSECRXCTRL_RSV_MASK 0xFFFFFF33 + +/* Tx Rate-Scheduler Config fields */ +#define E1000_RTTBCNRC_RS_ENA 0x80000000 +#define E1000_RTTBCNRC_RF_DEC_MASK 0x00003FFF +#define E1000_RTTBCNRC_RF_INT_SHIFT 14 +#define E1000_RTTBCNRC_RF_INT_MASK \ + (E1000_RTTBCNRC_RF_DEC_MASK << E1000_RTTBCNRC_RF_INT_SHIFT) + +/* DMA Coalescing register fields */ +/* DMA Coalescing Watchdog Timer */ +#define E1000_DMACR_DMACWT_MASK 0x00003FFF +/* DMA Coalescing Rx Threshold */ +#define E1000_DMACR_DMACTHR_MASK 0x00FF0000 +#define E1000_DMACR_DMACTHR_SHIFT 16 +/* Lx when no PCIe transactions */ +#define E1000_DMACR_DMAC_LX_MASK 0x30000000 +#define E1000_DMACR_DMAC_LX_SHIFT 28 +#define E1000_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */ +/* DMA Coalescing BMC-to-OS Watchdog Enable */ +#define E1000_DMACR_DC_BMC2OSW_EN 0x00008000 + +/* DMA Coalescing Transmit Threshold */ +#define E1000_DMCTXTH_DMCTTHR_MASK 0x00000FFF + +#define E1000_DMCTLX_TTLX_MASK 0x00000FFF /* Time to LX request */ + +/* Rx Traffic Rate Threshold */ +#define E1000_DMCRTRH_UTRESH_MASK 0x0007FFFF +/* Rx packet rate in current window */ +#define E1000_DMCRTRH_LRPRCW 0x80000000 + +/* DMA Coal Rx Traffic Current Count */ +#define E1000_DMCCNT_CCOUNT_MASK 0x01FFFFFF + +/* Flow ctrl Rx Threshold High val */ +#define E1000_FCRTC_RTH_COAL_MASK 0x0003FFF0 +#define E1000_FCRTC_RTH_COAL_SHIFT 4 +/* Lx power decision based on DMA coal */ +#define E1000_PCIEMISC_LX_DECISION 0x00000080 + +#define E1000_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */ +#define E1000_RXPBS_SIZE_I210_MASK 0x0000003F /* Rx packet buffer size */ +#define E1000_TXPB0S_SIZE_I210_MASK 0x0000003F /* Tx packet buffer 0 size */ +#define I210_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */ +#define I210_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */ + +/* Proxy Filter Control */ +#define E1000_PROXYFC_D0 0x00000001 /* Enable offload in D0 */ +#define E1000_PROXYFC_EX 0x00000004 /* Directed exact proxy */ +#define E1000_PROXYFC_MC 0x00000008 /* Directed MC Proxy */ +#define E1000_PROXYFC_BC 0x00000010 /* Broadcast Proxy Enable */ +#define E1000_PROXYFC_ARP_DIRECTED 0x00000020 /* Directed ARP Proxy Ena */ +#define E1000_PROXYFC_IPV4 0x00000040 /* Directed IPv4 Enable */ +#define E1000_PROXYFC_IPV6 0x00000080 /* Directed IPv6 Enable */ +#define E1000_PROXYFC_NS 0x00000200 /* IPv6 Neighbor Solicitation */ +#define E1000_PROXYFC_ARP 0x00000800 /* ARP Request Proxy Ena */ +/* Proxy Status */ +#define E1000_PROXYS_CLEAR 0xFFFFFFFF /* Clear */ + +/* Firmware Status */ +#define E1000_FWSTS_FWRI 0x80000000 /* FW Reset Indication */ +/* VF Control */ +#define E1000_VTCTRL_RST 0x04000000 /* Reset VF */ + +#define E1000_STATUS_LAN_ID_MASK 0x00000000C /* Mask for Lan ID field */ +/* Lan ID bit field offset in status register */ +#define E1000_STATUS_LAN_ID_OFFSET 2 +#define E1000_VFTA_ENTRIES 128 +#define E1000_TQAVCC_QUEUEMODE 0x80000000 /* queue mode, 0=strict, 1=SR mode */ +#define E1000_TQAVCTRL_TXMODE 0x00000001 /* Transmit mode, 0=legacy, 1=QAV */ +#define E1000_TQAVCTRL_1588_STAT_EN 0x00000004 /* report DMA time of tx packets */ +#define E1000_TQAVCTRL_DATA_FETCH_ARB 0x00000010 /* data fetch arbitration */ +#define E1000_TQAVCTRL_DATA_TRAN_ARB 0x00000100 /* data tx arbitration */ +#define E1000_TQAVCTRL_DATA_TRAN_TIM 0x00000200 /* data launch time valid */ +#define E1000_TQAVCTRL_SP_WAIT_SR 0x00000400 /* stall SP to guarantee SR */ +#define E1000_TQAVCTRL_FETCH_TM_SHIFT (16) /* ... and associated shift value */ + +/* Tx packet buffer fields */ +#define E1000_TXPBSIZE_PBSZ_MASK 0x3F +#define E1000_TXPBSIZE_TX0PB_SHIFT 0 +#define E1000_TXPBSIZE_TX1PB_SHIFT 6 +#define E1000_TXPBSIZE_TX2PB_SHIFT 12 +#define E1000_TXPBSIZE_TX3PB_SHIFT 18 +#ifndef E1000_UNUSEDARG +#define E1000_UNUSEDARG +#endif /* E1000_UNUSEDARG */ +#ifndef ERROR_REPORT +#define ERROR_REPORT(fmt) do { } while (0) +#endif /* ERROR_REPORT */ +#define E1000_TSAUXC_SAMP_AUTO 0x00000008 /* sample current ts */ +#endif /* _E1000_DEFINES_H_ */ diff --git a/drivers/staging/igb_avb/e1000_hw.h b/drivers/staging/igb_avb/e1000_hw.h new file mode 100644 index 000000000000..74cb22ee8ead --- /dev/null +++ b/drivers/staging/igb_avb/e1000_hw.h @@ -0,0 +1,792 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include "e1000_osdep.h" +#include "e1000_regs.h" +#include "e1000_defines.h" + +struct e1000_hw; + +#define E1000_DEV_ID_82576 0x10C9 +#define E1000_DEV_ID_82576_FIBER 0x10E6 +#define E1000_DEV_ID_82576_SERDES 0x10E7 +#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8 +#define E1000_DEV_ID_82576_QUAD_COPPER_ET2 0x1526 +#define E1000_DEV_ID_82576_NS 0x150A +#define E1000_DEV_ID_82576_NS_SERDES 0x1518 +#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D +#define E1000_DEV_ID_82575EB_COPPER 0x10A7 +#define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9 +#define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6 +#define E1000_DEV_ID_82580_COPPER 0x150E +#define E1000_DEV_ID_82580_FIBER 0x150F +#define E1000_DEV_ID_82580_SERDES 0x1510 +#define E1000_DEV_ID_82580_SGMII 0x1511 +#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516 +#define E1000_DEV_ID_82580_QUAD_FIBER 0x1527 +#define E1000_DEV_ID_I350_COPPER 0x1521 +#define E1000_DEV_ID_I350_FIBER 0x1522 +#define E1000_DEV_ID_I350_SERDES 0x1523 +#define E1000_DEV_ID_I350_SGMII 0x1524 +#define E1000_DEV_ID_I350_DA4 0x1546 +#define E1000_DEV_ID_I210_COPPER 0x1533 +#define E1000_DEV_ID_I210_COPPER_OEM1 0x1534 +#define E1000_DEV_ID_I210_COPPER_IT 0x1535 +#define E1000_DEV_ID_I210_FIBER 0x1536 +#define E1000_DEV_ID_I210_SERDES 0x1537 +#define E1000_DEV_ID_I210_SGMII 0x1538 +#define E1000_DEV_ID_I210_AUTOMOTIVE 0x15F6 +#define E1000_DEV_ID_I210_COPPER_FLASHLESS 0x157B +#define E1000_DEV_ID_I210_SERDES_FLASHLESS 0x157C +#define E1000_DEV_ID_I211_COPPER 0x1539 +#define E1000_DEV_ID_I354_BACKPLANE_1GBPS 0x1F40 +#define E1000_DEV_ID_I354_SGMII 0x1F41 +#define E1000_DEV_ID_I354_BACKPLANE_2_5GBPS 0x1F45 +#define E1000_DEV_ID_DH89XXCC_SGMII 0x0438 +#define E1000_DEV_ID_DH89XXCC_SERDES 0x043A +#define E1000_DEV_ID_DH89XXCC_BACKPLANE 0x043C +#define E1000_DEV_ID_DH89XXCC_SFP 0x0440 + +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 +#define E1000_REVISION_4 4 + +#define E1000_FUNC_0 0 +#define E1000_FUNC_1 1 +#define E1000_FUNC_2 2 +#define E1000_FUNC_3 3 + +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN0 0 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN1 3 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN2 6 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN3 9 + +enum e1000_mac_type { + e1000_undefined = 0, + e1000_82575, + e1000_82576, + e1000_82580, + e1000_i350, + e1000_i354, + e1000_i210, + e1000_i211, + e1000_num_macs /* List is 1-based, so subtract 1 for true count. */ +}; + +enum e1000_media_type { + e1000_media_type_unknown = 0, + e1000_media_type_copper = 1, + e1000_media_type_fiber = 2, + e1000_media_type_internal_serdes = 3, + e1000_num_media_types +}; + +enum e1000_nvm_type { + e1000_nvm_unknown = 0, + e1000_nvm_none, + e1000_nvm_eeprom_spi, + e1000_nvm_flash_hw, + e1000_nvm_invm, + e1000_nvm_flash_sw +}; + +enum e1000_nvm_override { + e1000_nvm_override_none = 0, + e1000_nvm_override_spi_small, + e1000_nvm_override_spi_large, +}; + +enum e1000_phy_type { + e1000_phy_unknown = 0, + e1000_phy_none, + e1000_phy_m88, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, + e1000_phy_82580, + e1000_phy_vf, + e1000_phy_i210, +}; + +enum e1000_bus_type { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +}; + +enum e1000_bus_speed { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_120, + e1000_bus_speed_133, + e1000_bus_speed_2500, + e1000_bus_speed_5000, + e1000_bus_speed_reserved +}; + +enum e1000_bus_width { + e1000_bus_width_unknown = 0, + e1000_bus_width_pcie_x1, + e1000_bus_width_pcie_x2, + e1000_bus_width_pcie_x4 = 4, + e1000_bus_width_pcie_x8 = 8, + e1000_bus_width_32, + e1000_bus_width_64, + e1000_bus_width_reserved +}; + +enum e1000_1000t_rx_status { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +}; + +enum e1000_rev_polarity { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +}; + +enum e1000_fc_mode { + e1000_fc_none = 0, + e1000_fc_rx_pause, + e1000_fc_tx_pause, + e1000_fc_full, + e1000_fc_default = 0xFF +}; + +enum e1000_ms_type { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +}; + +enum e1000_smart_speed { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +}; + +enum e1000_serdes_link_state { + e1000_serdes_link_down = 0, + e1000_serdes_link_autoneg_progress, + e1000_serdes_link_autoneg_complete, + e1000_serdes_link_forced_up +}; + +#ifndef __le16 +#define __le16 u16 +#endif +#ifndef __le32 +#define __le32 u32 +#endif +#ifndef __le64 +#define __le64 u64 +#endif +/* Receive Descriptor */ +struct e1000_rx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + __le16 length; /* Length of data DMAed into data buffer */ + __le16 csum; /* Packet checksum */ + u8 status; /* Descriptor status */ + u8 errors; /* Descriptor Errors */ + __le16 special; +}; + +/* Receive Descriptor - Extended */ +union e1000_rx_desc_extended { + struct { + __le64 buffer_addr; + __le64 reserved; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + __le64 buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length0; /* length of buffer 0 */ + __le16 vlan; /* VLAN tag */ + } middle; + struct { + __le16 header_status; + /* length of buffers 1-3 */ + __le16 length[PS_PAGE_BUFFERS]; + } upper; + __le64 reserved; + } wb; /* writeback */ +}; + +/* Transmit Descriptor */ +struct e1000_tx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 cso; /* Checksum offset */ + u8 cmd; /* Descriptor control */ + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 css; /* Checksum start */ + __le16 special; + } fields; + } upper; +}; + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + __le32 ip_config; + struct { + u8 ipcss; /* IP checksum start */ + u8 ipcso; /* IP checksum offset */ + __le16 ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + __le32 tcp_config; + struct { + u8 tucss; /* TCP checksum start */ + u8 tucso; /* TCP checksum offset */ + __le16 tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + __le32 cmd_and_length; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 hdr_len; /* Header length */ + __le16 mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + __le64 buffer_addr; /* Address of the descriptor's buffer address */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 typ_len_ext; + u8 cmd; + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 popts; /* Packet Options */ + __le16 special; + } fields; + } upper; +}; + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + u64 crcerrs; + u64 algnerrc; + u64 symerrs; + u64 rxerrc; + u64 mpc; + u64 scc; + u64 ecol; + u64 mcc; + u64 latecol; + u64 colc; + u64 dc; + u64 tncrs; + u64 sec; + u64 cexterr; + u64 rlec; + u64 xonrxc; + u64 xontxc; + u64 xoffrxc; + u64 xofftxc; + u64 fcruc; + u64 prc64; + u64 prc127; + u64 prc255; + u64 prc511; + u64 prc1023; + u64 prc1522; + u64 gprc; + u64 bprc; + u64 mprc; + u64 gptc; + u64 gorc; + u64 gotc; + u64 rnbc; + u64 ruc; + u64 rfc; + u64 roc; + u64 rjc; + u64 mgprc; + u64 mgpdc; + u64 mgptc; + u64 tor; + u64 tot; + u64 tpr; + u64 tpt; + u64 ptc64; + u64 ptc127; + u64 ptc255; + u64 ptc511; + u64 ptc1023; + u64 ptc1522; + u64 mptc; + u64 bptc; + u64 tsctc; + u64 tsctfc; + u64 iac; + u64 icrxptc; + u64 icrxatc; + u64 ictxptc; + u64 ictxatc; + u64 ictxqec; + u64 ictxqmtc; + u64 icrxdmtc; + u64 icrxoc; + u64 cbtmpc; + u64 htdpmc; + u64 cbrdpc; + u64 cbrmpc; + u64 rpthc; + u64 hgptc; + u64 htcbdpc; + u64 hgorc; + u64 hgotc; + u64 lenerrs; + u64 scvpc; + u64 hrmpc; + u64 doosync; + u64 o2bgptc; + u64 o2bspc; + u64 b2ospc; + u64 b2ogprc; +}; + +struct e1000_phy_stats { + u32 idle_errors; + u32 receive_errors; +}; + +struct e1000_host_mng_dhcp_cookie { + u32 signature; + u8 status; + u8 reserved0; + u16 vlan_id; + u32 reserved1; + u16 reserved2; + u8 reserved3; + u8 checksum; +}; + +/* Host Interface "Rev 1" */ +struct e1000_host_command_header { + u8 command_id; + u8 command_length; + u8 command_options; + u8 checksum; +}; + +#define E1000_HI_MAX_DATA_LENGTH 252 +struct e1000_host_command_info { + struct e1000_host_command_header command_header; + u8 command_data[E1000_HI_MAX_DATA_LENGTH]; +}; + +/* Host Interface "Rev 2" */ +struct e1000_host_mng_command_header { + u8 command_id; + u8 checksum; + u16 reserved1; + u16 reserved2; + u16 command_length; +}; + +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 +struct e1000_host_mng_command_info { + struct e1000_host_mng_command_header command_header; + u8 command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; +}; + +#include "e1000_mac.h" +#include "e1000_phy.h" +#include "e1000_nvm.h" +#include "e1000_manage.h" +#include "e1000_mbx.h" + +/* Function pointers for the MAC. */ +struct e1000_mac_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*id_led_init)(struct e1000_hw *); + s32 (*blink_led)(struct e1000_hw *); + bool (*check_mng_mode)(struct e1000_hw *); + s32 (*check_for_link)(struct e1000_hw *); + s32 (*cleanup_led)(struct e1000_hw *); + void (*clear_hw_cntrs)(struct e1000_hw *); + void (*clear_vfta)(struct e1000_hw *); + s32 (*get_bus_info)(struct e1000_hw *); + void (*set_lan_id)(struct e1000_hw *); + s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *); + s32 (*led_on)(struct e1000_hw *); + s32 (*led_off)(struct e1000_hw *); + void (*update_mc_addr_list)(struct e1000_hw *, u8 *, u32); + s32 (*reset_hw)(struct e1000_hw *); + s32 (*init_hw)(struct e1000_hw *); + void (*shutdown_serdes)(struct e1000_hw *); + void (*power_up_serdes)(struct e1000_hw *); + s32 (*setup_link)(struct e1000_hw *); + s32 (*setup_physical_interface)(struct e1000_hw *); + s32 (*setup_led)(struct e1000_hw *); + void (*write_vfta)(struct e1000_hw *, u32, u32); + void (*config_collision_dist)(struct e1000_hw *); + int (*rar_set)(struct e1000_hw *, u8*, u32); + s32 (*read_mac_addr)(struct e1000_hw *); + s32 (*validate_mdi_setting)(struct e1000_hw *); + s32 (*get_thermal_sensor_data)(struct e1000_hw *); + s32 (*init_thermal_sensor_thresh)(struct e1000_hw *); + s32 (*acquire_swfw_sync)(struct e1000_hw *, u16); + void (*release_swfw_sync)(struct e1000_hw *, u16); +}; + +/* When to use various PHY register access functions: + * + * Func Caller + * Function Does Does When to use + * ~~~~~~~~~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * X_reg L,P,A n/a for simple PHY reg accesses + * X_reg_locked P,A L for multiple accesses of different regs + * on different pages + * X_reg_page A L,P for multiple accesses of different regs + * on the same page + * + * Where X=[read|write], L=locking, P=sets page, A=register access + * + */ +struct e1000_phy_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*check_polarity)(struct e1000_hw *); + s32 (*check_reset_block)(struct e1000_hw *); + s32 (*commit)(struct e1000_hw *); + s32 (*force_speed_duplex)(struct e1000_hw *); + s32 (*get_cfg_done)(struct e1000_hw *hw); + s32 (*get_cable_length)(struct e1000_hw *); + s32 (*get_info)(struct e1000_hw *); + s32 (*set_page)(struct e1000_hw *, u16); + s32 (*read_reg)(struct e1000_hw *, u32, u16 *); + s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *); + s32 (*read_reg_page)(struct e1000_hw *, u32, u16 *); + void (*release)(struct e1000_hw *); + s32 (*reset)(struct e1000_hw *); + s32 (*set_d0_lplu_state)(struct e1000_hw *, bool); + s32 (*set_d3_lplu_state)(struct e1000_hw *, bool); + s32 (*write_reg)(struct e1000_hw *, u32, u16); + s32 (*write_reg_locked)(struct e1000_hw *, u32, u16); + s32 (*write_reg_page)(struct e1000_hw *, u32, u16); + void (*power_up)(struct e1000_hw *); + void (*power_down)(struct e1000_hw *); + s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *); + s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8); +}; + +/* Function pointers for the NVM. */ +struct e1000_nvm_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*read)(struct e1000_hw *, u16, u16, u16 *); + void (*release)(struct e1000_hw *); + void (*reload)(struct e1000_hw *); + s32 (*update)(struct e1000_hw *); + s32 (*valid_led_default)(struct e1000_hw *, u16 *); + s32 (*validate)(struct e1000_hw *); + s32 (*write)(struct e1000_hw *, u16, u16, u16 *); +}; + +#define E1000_MAX_SENSORS 3 + +struct e1000_thermal_diode_data { + u8 location; + u8 temp; + u8 caution_thresh; + u8 max_op_thresh; +}; + +struct e1000_thermal_sensor_data { + struct e1000_thermal_diode_data sensor[E1000_MAX_SENSORS]; +}; + +struct e1000_mac_info { + struct e1000_mac_operations ops; + u8 addr[ETH_ADDR_LEN]; + u8 perm_addr[ETH_ADDR_LEN]; + + enum e1000_mac_type type; + + u32 collision_delta; + u32 ledctl_default; + u32 ledctl_mode1; + u32 ledctl_mode2; + u32 mc_filter_type; + u32 tx_packet_delta; + u32 txcw; + + u16 current_ifs_val; + u16 ifs_max_val; + u16 ifs_min_val; + u16 ifs_ratio; + u16 ifs_step_size; + u16 mta_reg_count; + u16 uta_reg_count; + + /* Maximum size of the MTA register table in all supported adapters */ +#define MAX_MTA_REG 128 + u32 mta_shadow[MAX_MTA_REG]; + u16 rar_entry_count; + + u8 forced_speed_duplex; + + bool adaptive_ifs; + bool has_fwsm; + bool arc_subsystem_valid; + bool asf_firmware_present; + bool autoneg; + bool autoneg_failed; + bool get_link_status; + bool in_ifs_mode; + enum e1000_serdes_link_state serdes_link_state; + bool serdes_has_link; + bool tx_pkt_filtering; + struct e1000_thermal_sensor_data thermal_sensor_data; +}; + +struct e1000_phy_info { + struct e1000_phy_operations ops; + enum e1000_phy_type type; + + enum e1000_1000t_rx_status local_rx; + enum e1000_1000t_rx_status remote_rx; + enum e1000_ms_type ms_type; + enum e1000_ms_type original_ms_type; + enum e1000_rev_polarity cable_polarity; + enum e1000_smart_speed smart_speed; + + u32 addr; + u32 id; + u32 reset_delay_us; /* in usec */ + u32 revision; + + enum e1000_media_type media_type; + + u16 autoneg_advertised; + u16 autoneg_mask; + u16 cable_length; + u16 max_cable_length; + u16 min_cable_length; + + u8 mdix; + + bool disable_polarity_correction; + bool is_mdix; + bool polarity_correction; + bool reset_disable; + bool speed_downgraded; + bool autoneg_wait_to_complete; +}; + +struct e1000_nvm_info { + struct e1000_nvm_operations ops; + enum e1000_nvm_type type; + enum e1000_nvm_override override; + + u32 flash_bank_size; + u32 flash_base_addr; + + u16 word_size; + u16 delay_usec; + u16 address_bits; + u16 opcode_bits; + u16 page_size; +}; + +struct e1000_bus_info { + enum e1000_bus_type type; + enum e1000_bus_speed speed; + enum e1000_bus_width width; + + u16 func; + u16 pci_cmd_word; +}; + +struct e1000_fc_info { + u32 high_water; /* Flow control high-water mark */ + u32 low_water; /* Flow control low-water mark */ + u16 pause_time; /* Flow control pause timer */ + u16 refresh_time; /* Flow control refresh timer */ + bool send_xon; /* Flow control send XON */ + bool strict_ieee; /* Strict IEEE mode */ + enum e1000_fc_mode current_mode; /* FC mode in effect */ + enum e1000_fc_mode requested_mode; /* FC mode requested by caller */ +}; + +struct e1000_mbx_operations { + s32 (*init_params)(struct e1000_hw *hw); + s32 (*read)(struct e1000_hw *, u32 *, u16, u16); + s32 (*write)(struct e1000_hw *, u32 *, u16, u16); + s32 (*read_posted)(struct e1000_hw *, u32 *, u16, u16); + s32 (*write_posted)(struct e1000_hw *, u32 *, u16, u16); + s32 (*check_for_msg)(struct e1000_hw *, u16); + s32 (*check_for_ack)(struct e1000_hw *, u16); + s32 (*check_for_rst)(struct e1000_hw *, u16); +}; + +struct e1000_mbx_stats { + u32 msgs_tx; + u32 msgs_rx; + + u32 acks; + u32 reqs; + u32 rsts; +}; + +struct e1000_mbx_info { + struct e1000_mbx_operations ops; + struct e1000_mbx_stats stats; + u32 timeout; + u32 usec_delay; + u16 size; +}; + +struct e1000_dev_spec_82575 { + bool sgmii_active; + bool global_device_reset; + bool eee_disable; + bool module_plugged; + bool clear_semaphore_once; + u32 mtu; + struct sfp_e1000_flags eth_flags; + u8 media_port; + bool media_changed; +}; + +struct e1000_dev_spec_vf { + u32 vf_number; + u32 v2p_mailbox; +}; + +struct e1000_hw { + void *back; + + u8 __iomem *hw_addr; + u8 __iomem *flash_address; + unsigned long io_base; + + struct e1000_mac_info mac; + struct e1000_fc_info fc; + struct e1000_phy_info phy; + struct e1000_nvm_info nvm; + struct e1000_bus_info bus; + struct e1000_mbx_info mbx; + struct e1000_host_mng_dhcp_cookie mng_cookie; + + union { + struct e1000_dev_spec_82575 _82575; + struct e1000_dev_spec_vf vf; + } dev_spec; + + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_device_id; + u16 vendor_id; + + u8 revision_id; +}; + +#include "e1000_82575.h" +#include "e1000_i210.h" + +/* These functions must be implemented by drivers */ +s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +s32 e1000_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); + +#endif diff --git a/drivers/staging/igb_avb/e1000_i210.c b/drivers/staging/igb_avb/e1000_i210.c new file mode 100644 index 000000000000..7e32fd112f33 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_i210.c @@ -0,0 +1,993 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + + +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw); +static void e1000_release_nvm_i210(struct e1000_hw *hw); +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw); +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw); +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data); + +/** + * e1000_acquire_nvm_i210 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclusive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_i210"); + + ret_val = e1000_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); + + return ret_val; +} + +/** + * e1000_release_nvm_i210 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +static void e1000_release_nvm_i210(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_i210"); + + e1000_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_i210 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ + + DEBUGFUNC("e1000_acquire_swfw_sync_i210"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_i210(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* + * Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) + */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_i210 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_i210"); + + while (e1000_get_hw_semaphore_i210(hw) != E1000_SUCCESS) + ; /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_get_hw_semaphore_i210 - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_i210"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + /* In rare circumstances, the SW semaphore may already be held + * unintentionally. Clear the semaphore once before giving up. + */ + if (hw->dev_spec._82575.clear_semaphore_once) { + hw->dev_spec._82575.clear_semaphore_once = false; + e1000_put_hw_semaphore_generic(hw); + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + } + } + + /* If we do not have the semaphore here, we have to give up. */ + if (i == timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_NVM; + } + } + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the Shadow Ram to read + * @words: number of words to read + * @data: word read from the Shadow Ram + * + * Reads a 16 bit word from the Shadow Ram using the EERD register. + * Uses necessary synchronization semaphores. + **/ +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_read_nvm_srrd_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to read in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_read_nvm_eerd(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow RAM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow RAM + * + * Writes data to Shadow RAM at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * data will not be committed to FLASH and also Shadow RAM will most likely + * contain an invalid checksum. + * + * If error code is returned, data and Shadow RAM may be inconsistent - buffer + * partially written. + **/ +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_write_nvm_srwr_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to write in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_write_nvm_srwr(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr - Write to Shadow Ram using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow Ram to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow Ram + * + * Writes data to Shadow Ram at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * Shadow Ram will most likely contain an invalid checksum. + **/ +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, k, eewr = 0; + u32 attempts = 100000; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_nvm_srwr"); + + /* + * A check for invalid values: offset too large, too many words, + * too many words for the offset, and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + for (i = 0; i < words; i++) { + eewr = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | + (data[i] << E1000_NVM_RW_REG_DATA) | + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_SRWR, eewr); + + for (k = 0; k < attempts; k++) { + if (E1000_NVM_RW_REG_DONE & + E1000_READ_REG(hw, E1000_SRWR)) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("Shadow RAM write EEWR timed out\n"); + break; + } + } + +out: + return ret_val; +} + +/** e1000_read_invm_word_i210 - Reads OTP + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Reads 16-bit words from the OTP. Return error when the word is not + * stored in OTP. + **/ +static s32 e1000_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data) +{ + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u32 invm_dword; + u16 i; + u8 record_type, word_address; + + DEBUGFUNC("e1000_read_invm_word_i210"); + + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); + /* Get record type */ + record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword); + if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE) + break; + if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE) + i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE) + i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) { + word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword); + if (word_address == address) { + *data = INVM_DWORD_TO_WORD_DATA(invm_dword); + DEBUGOUT2("Read INVM Word 0x%02x = %x", + address, *data); + status = E1000_SUCCESS; + break; + } + } + } + if (status != E1000_SUCCESS) + DEBUGOUT1("Requested word 0x%02x not found in OTP\n", address); + return status; +} + +/** e1000_read_invm_i210 - Read invm wrapper function for I210/I211 + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Wrapper function to return data formerly found in the NVM. + **/ +static s32 e1000_read_invm_i210(struct e1000_hw *hw, u16 offset, + u16 E1000_UNUSEDARG words, u16 *data) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_invm_i210"); + + /* Only the MAC addr is required to be present in the iNVM */ + switch (offset) { + case NVM_MAC_ADDR: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, &data[0]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+1, + &data[1]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+2, + &data[2]); + if (ret_val != E1000_SUCCESS) + DEBUGOUT("MAC Addr not found in iNVM\n"); + break; + case NVM_INIT_CTRL_2: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_2_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_INIT_CTRL_4: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_4_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_1_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_1_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_0_2_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_0_2_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_ID_LED_SETTINGS: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = ID_LED_RESERVED_FFFF; + ret_val = E1000_SUCCESS; + } + break; + case NVM_SUB_DEV_ID: + *data = hw->subsystem_device_id; + break; + case NVM_SUB_VEN_ID: + *data = hw->subsystem_vendor_id; + break; + case NVM_DEV_ID: + *data = hw->device_id; + break; + case NVM_VEN_ID: + *data = hw->vendor_id; + break; + default: + DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset); + *data = NVM_RESERVED_WORD; + break; + } + return ret_val; +} + +/** + * e1000_read_invm_version - Reads iNVM version and image type + * @hw: pointer to the HW structure + * @invm_ver: version structure for the version read + * + * Reads iNVM version and image type. + **/ +s32 e1000_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver) +{ + u32 *record = NULL; + u32 *next_record = NULL; + u32 i = 0; + u32 invm_dword = 0; + u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE / + E1000_INVM_RECORD_SIZE_IN_BYTES); + u32 buffer[E1000_INVM_SIZE]; + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u16 version = 0; + + DEBUGFUNC("e1000_read_invm_version"); + + /* Read iNVM memory */ + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); + buffer[i] = invm_dword; + } + + /* Read version number */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have first version location used */ + if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) { + version = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have second version location used */ + else if ((i == 1) && + ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + /* + * Check if we have odd version location + * used and it is the last one used + */ + else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) && + ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) && + (i != 1))) { + version = (*next_record & E1000_INVM_VER_FIELD_TWO) + >> 13; + status = E1000_SUCCESS; + break; + } + /* + * Check if we have even version location + * used and it is the last one used + */ + else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) && + ((*record & 0x3) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + } + + if (status == E1000_SUCCESS) { + invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK) + >> E1000_INVM_MAJOR_SHIFT; + invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK; + } + /* Read Image Type */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have image type in first location used */ + if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) { + invm_ver->invm_img_type = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have image type in first location used */ + else if ((((*record & 0x3) == 0) && + ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) || + ((((*record & 0x3) != 0) && (i != 1)))) { + invm_ver->invm_img_type = + (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23; + status = E1000_SUCCESS; + break; + } + } + return status; +} + +/** + * e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 status = E1000_SUCCESS; + s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *); + + DEBUGFUNC("e1000_validate_nvm_checksum_i210"); + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + + /* + * Replace the read function with semaphore grabbing with + * the one that skips this for a while. + * We have semaphore taken already here. + */ + read_op_ptr = hw->nvm.ops.read; + hw->nvm.ops.read = e1000_read_nvm_eerd; + + status = e1000_validate_nvm_checksum_generic(hw); + + /* Revert original read operation. */ + hw->nvm.ops.read = read_op_ptr; + + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + return status; +} + + +/** + * e1000_update_nvm_checksum_i210 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. Next commit EEPROM data onto the Flash. + **/ +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum_i210"); + + /* + * Read the first word from the EEPROM. If this times out or fails, do + * not continue or we could be in for a very long wait while every + * EEPROM read fails + */ + ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data); + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("EEPROM read failed\n"); + goto out; + } + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + /* + * Do not use hw->nvm.ops.write, hw->nvm.ops.read + * because we do not want to take the synchronization + * semaphores twice here. + */ + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data); + if (ret_val) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, + &checksum); + if (ret_val != E1000_SUCCESS) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Write Error while updating checksum.\n"); + goto out; + } + + hw->nvm.ops.release(hw); + + ret_val = e1000_update_flash_i210(hw); + } else { + ret_val = E1000_ERR_SWFW_SYNC; + } +out: + return ret_val; +} + +/** + * e1000_get_flash_presence_i210 - Check if flash device is detected. + * @hw: pointer to the HW structure + * + **/ +bool e1000_get_flash_presence_i210(struct e1000_hw *hw) +{ + u32 eec = 0; + bool ret_val = false; + + DEBUGFUNC("e1000_get_flash_presence_i210"); + + eec = E1000_READ_REG(hw, E1000_EECD); + + if (eec & E1000_EECD_FLASH_DETECTED_I210) + ret_val = true; + + return ret_val; +} + +/** + * e1000_update_flash_i210 - Commit EEPROM to the flash + * @hw: pointer to the HW structure + * + **/ +s32 e1000_update_flash_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u32 flup; + + DEBUGFUNC("e1000_update_flash_i210"); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == -E1000_ERR_NVM) { + DEBUGOUT("Flash update time out\n"); + goto out; + } + + flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I210; + E1000_WRITE_REG(hw, E1000_EECD, flup); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == E1000_SUCCESS) + DEBUGOUT("Flash update complete\n"); + else + DEBUGOUT("Flash update time out\n"); + +out: + return ret_val; +} + +/** + * e1000_pool_flash_update_done_i210 - Pool FLUDONE status. + * @hw: pointer to the HW structure + * + **/ +s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw) +{ + s32 ret_val = -E1000_ERR_NVM; + u32 i, reg; + + DEBUGFUNC("e1000_pool_flash_update_done_i210"); + + for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { + reg = E1000_READ_REG(hw, E1000_EECD); + if (reg & E1000_EECD_FLUDONE_I210) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + return ret_val; +} + +/** + * e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers + * @hw: pointer to the HW structure + * + * Initialize the i210/i211 NVM parameters and function pointers. + **/ +static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw) +{ + s32 ret_val; + struct e1000_nvm_info *nvm = &hw->nvm; + + DEBUGFUNC("e1000_init_nvm_params_i210"); + + ret_val = e1000_init_nvm_params_82575(hw); + nvm->ops.acquire = e1000_acquire_nvm_i210; + nvm->ops.release = e1000_release_nvm_i210; + nvm->ops.valid_led_default = e1000_valid_led_default_i210; + if (e1000_get_flash_presence_i210(hw)) { + hw->nvm.type = e1000_nvm_flash_hw; + nvm->ops.read = e1000_read_nvm_srrd_i210; + nvm->ops.write = e1000_write_nvm_srwr_i210; + nvm->ops.validate = e1000_validate_nvm_checksum_i210; + nvm->ops.update = e1000_update_nvm_checksum_i210; + } else { + hw->nvm.type = e1000_nvm_invm; + nvm->ops.read = e1000_read_invm_i210; + nvm->ops.write = e1000_null_write_nvm; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.update = e1000_null_ops_generic; + } + return ret_val; +} + +/** + * e1000_init_function_pointers_i210 - Init func ptrs. + * @hw: pointer to the HW structure + * + * Called to initialize all function pointers and parameters. + **/ +void e1000_init_function_pointers_i210(struct e1000_hw *hw) +{ + e1000_init_function_pointers_82575(hw); + hw->nvm.ops.init_params = e1000_init_nvm_params_i210; + + return; +} + +/** + * e1000_valid_led_default_i210 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_i210"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { + switch (hw->phy.media_type) { + case e1000_media_type_internal_serdes: + *data = ID_LED_DEFAULT_I210_SERDES; + break; + case e1000_media_type_copper: + default: + *data = ID_LED_DEFAULT_I210; + break; + } + } +out: + return ret_val; +} + +/** + * __e1000_access_xmdio_reg - Read/write XMDIO register + * @hw: pointer to the HW structure + * @address: XMDIO address to program + * @dev_addr: device address to program + * @data: pointer to value to read/write from/to the XMDIO address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_xmdio_reg(struct e1000_hw *hw, u16 address, + u8 dev_addr, u16 *data, bool read) +{ + s32 ret_val; + + DEBUGFUNC("__e1000_access_xmdio_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, address); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, E1000_MMDAC_FUNC_DATA | + dev_addr); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_MMDAAD, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, *data); + if (ret_val) + return ret_val; + + /* Recalibrate the device back to 0 */ + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, 0); + if (ret_val) + return ret_val; + + return ret_val; +} + +/** + * e1000_read_xmdio_reg - Read XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, data, true); +} + +/** + * e1000_write_xmdio_reg - Write XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be written to the XMDIO address + **/ +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, &data, false); +} + +/** + * e1000_pll_workaround_i210 + * @hw: pointer to the HW structure + * + * Works around an errata in the PLL circuit where it occasionally + * provides the wrong clock frequency after power up. + **/ +static s32 e1000_pll_workaround_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u32 wuc, mdicnfg, ctrl, ctrl_ext, reg_val; + u16 nvm_word, phy_word, pci_word, tmp_nvm; + int i; + + /* Get and set needed register values */ + wuc = E1000_READ_REG(hw, E1000_WUC); + mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG); + reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO; + E1000_WRITE_REG(hw, E1000_MDICNFG, reg_val); + + /* Get data from NVM, or set default */ + ret_val = e1000_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD, + &nvm_word); + if (ret_val != E1000_SUCCESS) + nvm_word = E1000_INVM_DEFAULT_AL; + tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL; + for (i = 0; i < E1000_MAX_PLL_TRIES; i++) { + /* check current state directly from internal PHY */ + e1000_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE | + E1000_PHY_PLL_FREQ_REG), &phy_word); + if ((phy_word & E1000_PHY_PLL_UNCONF) + != E1000_PHY_PLL_UNCONF) { + ret_val = E1000_SUCCESS; + break; + } else { + ret_val = -E1000_ERR_PHY; + } + /* directly reset the internal PHY */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl|E1000_CTRL_PHY_RST); + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + E1000_WRITE_REG(hw, E1000_WUC, 0); + reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16); + E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val); + + e1000_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + pci_word |= E1000_PCI_PMCSR_D3; + e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + msec_delay(1); + pci_word &= ~E1000_PCI_PMCSR_D3; + e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16); + E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val); + + /* restore WUC register */ + E1000_WRITE_REG(hw, E1000_WUC, wuc); + } + /* restore MDICNFG setting */ + E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg); + return ret_val; +} + +/** + * e1000_init_hw_i210 - Init hw for I210/I211 + * @hw: pointer to the HW structure + * + * Called to initialize hw for i210 hw family. + **/ +s32 e1000_init_hw_i210(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_init_hw_i210"); + if ((hw->mac.type >= e1000_i210) && + !(e1000_get_flash_presence_i210(hw))) { + ret_val = e1000_pll_workaround_i210(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + } + ret_val = e1000_init_hw_82575(hw); + return ret_val; +} diff --git a/drivers/staging/igb_avb/e1000_i210.h b/drivers/staging/igb_avb/e1000_i210.h new file mode 100644 index 000000000000..a14e897d26a0 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_i210.h @@ -0,0 +1,101 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_I210_H_ +#define _E1000_I210_H_ + +bool e1000_get_flash_presence_i210(struct e1000_hw *hw); +s32 e1000_update_flash_i210(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver); +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 *data); +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 data); +s32 e1000_init_hw_i210(struct e1000_hw *hw); + +#define E1000_STM_OPCODE 0xDB00 +#define E1000_EEPROM_FLASH_SIZE_WORD 0x11 + +#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \ + (u8)((invm_dword) & 0x7) +#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \ + (u8)(((invm_dword) & 0x0000FE00) >> 9) +#define INVM_DWORD_TO_WORD_DATA(invm_dword) \ + (u16)(((invm_dword) & 0xFFFF0000) >> 16) + +enum E1000_INVM_STRUCTURE_TYPE { + E1000_INVM_UNINITIALIZED_STRUCTURE = 0x00, + E1000_INVM_WORD_AUTOLOAD_STRUCTURE = 0x01, + E1000_INVM_CSR_AUTOLOAD_STRUCTURE = 0x02, + E1000_INVM_PHY_REGISTER_AUTOLOAD_STRUCTURE = 0x03, + E1000_INVM_RSA_KEY_SHA256_STRUCTURE = 0x04, + E1000_INVM_INVALIDATED_STRUCTURE = 0x0F, +}; + +#define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS 8 +#define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS 1 +#define E1000_INVM_ULT_BYTES_SIZE 8 +#define E1000_INVM_RECORD_SIZE_IN_BYTES 4 +#define E1000_INVM_VER_FIELD_ONE 0x1FF8 +#define E1000_INVM_VER_FIELD_TWO 0x7FE000 +#define E1000_INVM_IMGTYPE_FIELD 0x1F800000 + +#define E1000_INVM_MAJOR_MASK 0x3F0 +#define E1000_INVM_MINOR_MASK 0xF +#define E1000_INVM_MAJOR_SHIFT 4 + +#define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_OFF2)) +#define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_ON2)) + +/* NVM offset defaults for I211 devices */ +#define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 +#define NVM_INIT_CTRL_4_DEFAULT_I211 0x00C1 +#define NVM_LED_1_CFG_DEFAULT_I211 0x0184 +#define NVM_LED_0_2_CFG_DEFAULT_I211 0x200C + +/* PLL Defines */ +#define E1000_PCI_PMCSR 0x44 +#define E1000_PCI_PMCSR_D3 0x03 +#define E1000_MAX_PLL_TRIES 5 +#define E1000_PHY_PLL_UNCONF 0xFF +#define E1000_PHY_PLL_FREQ_PAGE 0xFC0000 +#define E1000_PHY_PLL_FREQ_REG 0x000E +#define E1000_INVM_DEFAULT_AL 0x202F +#define E1000_INVM_AUTOLOAD 0x0A +#define E1000_INVM_PLL_WO_VAL 0x0010 + +#endif diff --git a/drivers/staging/igb_avb/e1000_mac.c b/drivers/staging/igb_avb/e1000_mac.c new file mode 100644 index 000000000000..f848b995c932 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mac.c @@ -0,0 +1,2149 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw); +static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw); +static void e1000_config_collision_dist_generic(struct e1000_hw *hw); +static int e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); + +/** + * e1000_init_mac_ops_generic - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_mac_ops_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + DEBUGFUNC("e1000_init_mac_ops_generic"); + + /* General Setup */ + mac->ops.init_params = e1000_null_ops_generic; + mac->ops.init_hw = e1000_null_ops_generic; + mac->ops.reset_hw = e1000_null_ops_generic; + mac->ops.setup_physical_interface = e1000_null_ops_generic; + mac->ops.get_bus_info = e1000_null_ops_generic; + mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pcie; + mac->ops.read_mac_addr = e1000_read_mac_addr_generic; + mac->ops.config_collision_dist = e1000_config_collision_dist_generic; + mac->ops.clear_hw_cntrs = e1000_null_mac_generic; + /* LED */ + mac->ops.cleanup_led = e1000_null_ops_generic; + mac->ops.setup_led = e1000_null_ops_generic; + mac->ops.blink_led = e1000_null_ops_generic; + mac->ops.led_on = e1000_null_ops_generic; + mac->ops.led_off = e1000_null_ops_generic; + /* LINK */ + mac->ops.setup_link = e1000_null_ops_generic; + mac->ops.get_link_up_info = e1000_null_link_info; + mac->ops.check_for_link = e1000_null_ops_generic; + /* Management */ + mac->ops.check_mng_mode = e1000_null_mng_mode; + /* VLAN, MC, etc. */ + mac->ops.update_mc_addr_list = e1000_null_update_mc; + mac->ops.clear_vfta = e1000_null_mac_generic; + mac->ops.write_vfta = e1000_null_write_vfta; + mac->ops.rar_set = e1000_rar_set_generic; + mac->ops.validate_mdi_setting = e1000_validate_mdi_setting_generic; +} + +/** + * e1000_null_ops_generic - No-op function, returns 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_ops_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_ops_generic"); + return E1000_SUCCESS; +} + +/** + * e1000_null_mac_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_mac_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_mac_generic"); + return; +} + +/** + * e1000_null_link_info - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_link_info(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG *s, u16 E1000_UNUSEDARG *d) +{ + DEBUGFUNC("e1000_null_link_info"); + return E1000_SUCCESS; +} + +/** + * e1000_null_mng_mode - No-op function, return false + * @hw: pointer to the HW structure + **/ +bool e1000_null_mng_mode(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_mng_mode"); + return false; +} + +/** + * e1000_null_update_mc - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_update_mc(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) +{ + DEBUGFUNC("e1000_null_update_mc"); + return; +} + +/** + * e1000_null_write_vfta - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_write_vfta(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG a, u32 E1000_UNUSEDARG b) +{ + DEBUGFUNC("e1000_null_write_vfta"); + return; +} + +/** + * e1000_null_rar_set - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +int e1000_null_rar_set(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) +{ + DEBUGFUNC("e1000_null_rar_set"); + return E1000_SUCCESS; +} + +/** + * e1000_get_bus_info_pcie_generic - Get PCIe bus information + * @hw: pointer to the HW structure + * + * Determines and stores the system bus information for a particular + * network interface. The following bus information is determined and stored: + * bus speed, bus width, type (PCIe), and PCIe function. + **/ +s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_bus_info *bus = &hw->bus; + s32 ret_val; + u16 pcie_link_status; + + DEBUGFUNC("e1000_get_bus_info_pcie_generic"); + + bus->type = e1000_bus_type_pci_express; + + ret_val = e1000_read_pcie_cap_reg(hw, PCIE_LINK_STATUS, + &pcie_link_status); + if (ret_val) { + bus->width = e1000_bus_width_unknown; + bus->speed = e1000_bus_speed_unknown; + } else { + switch (pcie_link_status & PCIE_LINK_SPEED_MASK) { + case PCIE_LINK_SPEED_2500: + bus->speed = e1000_bus_speed_2500; + break; + case PCIE_LINK_SPEED_5000: + bus->speed = e1000_bus_speed_5000; + break; + default: + bus->speed = e1000_bus_speed_unknown; + break; + } + + bus->width = (enum e1000_bus_width)((pcie_link_status & + PCIE_LINK_WIDTH_MASK) >> PCIE_LINK_WIDTH_SHIFT); + } + + mac->ops.set_lan_id(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_set_lan_id_multi_port_pcie - Set LAN id for PCIe multiple port devices + * + * @hw: pointer to the HW structure + * + * Determines the LAN function id by reading memory-mapped registers + * and swaps the port value if requested. + **/ +static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + u32 reg; + + /* The status register reports the correct function number + * for the device regardless of function swap state. + */ + reg = E1000_READ_REG(hw, E1000_STATUS); + bus->func = (reg & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT; +} + +/** + * e1000_set_lan_id_single_port - Set LAN id for a single port device + * @hw: pointer to the HW structure + * + * Sets the LAN function id to zero for a single port device. + **/ +void e1000_set_lan_id_single_port(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + + bus->func = 0; +} + +/** + * e1000_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void e1000_clear_vfta_generic(struct e1000_hw *hw) +{ + u32 offset; + + DEBUGFUNC("e1000_clear_vfta_generic"); + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_write_vfta_generic - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: register offset in VLAN filter table + * @value: register value written to VLAN filter table + * + * Writes value at the given offset in the register array which stores + * the VLAN filter table. + **/ +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value) +{ + DEBUGFUNC("e1000_write_vfta_generic"); + + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_init_rx_addrs_generic - Initialize receive address's + * @hw: pointer to the HW structure + * @rar_count: receive address registers + * + * Setup the receive address registers by setting the base receive address + * register to the devices MAC address and clearing all the other receive + * address registers to 0. + **/ +void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count) +{ + u32 i; + u8 mac_addr[ETH_ADDR_LEN] = {0}; + + DEBUGFUNC("e1000_init_rx_addrs_generic"); + + /* Setup the receive address */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + hw->mac.ops.rar_set(hw, hw->mac.addr, 0); + + /* Zero out the other (rar_entry_count - 1) receive addresses */ + DEBUGOUT1("Clearing RAR[1-%u]\n", rar_count-1); + for (i = 1; i < rar_count; i++) + hw->mac.ops.rar_set(hw, mac_addr, i); +} + +/** + * e1000_check_alt_mac_addr_generic - Check for alternate MAC addr + * @hw: pointer to the HW structure + * + * Checks the nvm for an alternate MAC address. An alternate MAC address + * can be setup by pre-boot software and must be treated like a permanent + * address and must override the actual permanent MAC address. If an + * alternate MAC address is found it is programmed into RAR0, replacing + * the permanent address that was installed into RAR0 by the Si on reset. + * This function will return SUCCESS unless it encounters an error while + * reading the EEPROM. + **/ +s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) +{ + u32 i; + s32 ret_val; + u16 offset, nvm_alt_mac_addr_offset, nvm_data; + u8 alt_mac_addr[ETH_ADDR_LEN]; + + DEBUGFUNC("e1000_check_alt_mac_addr_generic"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &nvm_data); + if (ret_val) + return ret_val; + + /* Alternate MAC address is handled by the option ROM for 82580 + * and newer. SW support not required. + */ + if (hw->mac.type >= e1000_82580) + return E1000_SUCCESS; + + ret_val = hw->nvm.ops.read(hw, NVM_ALT_MAC_ADDR_PTR, 1, + &nvm_alt_mac_addr_offset); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if ((nvm_alt_mac_addr_offset == 0xFFFF) || + (nvm_alt_mac_addr_offset == 0x0000)) + /* There is no Alternate MAC Address */ + return E1000_SUCCESS; + + if (hw->bus.func == E1000_FUNC_1) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1; + if (hw->bus.func == E1000_FUNC_2) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN2; + + if (hw->bus.func == E1000_FUNC_3) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN3; + for (i = 0; i < ETH_ADDR_LEN; i += 2) { + offset = nvm_alt_mac_addr_offset + (i >> 1); + ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + alt_mac_addr[i] = (u8)(nvm_data & 0xFF); + alt_mac_addr[i + 1] = (u8)(nvm_data >> 8); + } + + /* if multicast bit is set, the alternate address will not be used */ + if (alt_mac_addr[0] & 0x01) { + DEBUGOUT("Ignoring Alternate Mac Address with MC bit set\n"); + return E1000_SUCCESS; + } + + /* We have a valid alternate MAC address, and we want to treat it the + * same as the normal permanent MAC address stored by the HW into the + * RAR. Do this by mapping this address into RAR0. + */ + hw->mac.ops.rar_set(hw, alt_mac_addr, 0); + + return E1000_SUCCESS; +} + +/** + * e1000_rar_set_generic - Set receive address register + * @hw: pointer to the HW structure + * @addr: pointer to the receive address + * @index: receive address array register + * + * Sets the receive address array register at index to the address passed + * in by addr. + **/ +static int e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) +{ + u32 rar_low, rar_high; + + DEBUGFUNC("e1000_rar_set_generic"); + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + + rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + + /* If MAC address zero, no need to set the AV bit */ + if (rar_low || rar_high) + rar_high |= E1000_RAH_AV; + + /* Some bridges will combine consecutive 32-bit writes into + * a single burst write, which will malfunction on some parts. + * The flushes avoid this. + */ + E1000_WRITE_REG(hw, E1000_RAL(index), rar_low); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG(hw, E1000_RAH(index), rar_high); + E1000_WRITE_FLUSH(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_hash_mc_addr_generic - Generate a multicast hash value + * @hw: pointer to the HW structure + * @mc_addr: pointer to a multicast address + * + * Generates a multicast address hash value which is used to determine + * the multicast filter table array address and new table value. + **/ +u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr) +{ + u32 hash_value, hash_mask; + u8 bit_shift = 0; + + DEBUGFUNC("e1000_hash_mc_addr_generic"); + + /* Register count multiplied by bits per register */ + hash_mask = (hw->mac.mta_reg_count * 32) - 1; + + /* For a mc_filter_type of 0, bit_shift is the number of left-shifts + * where 0xFF would still fall within the hash mask. + */ + while (hash_mask >> bit_shift != 0xFF) + bit_shift++; + + /* The portion of the address that is used for the hash table + * is determined by the mc_filter_type setting. + * The algorithm is such that there is a total of 8 bits of shifting. + * The bit_shift for a mc_filter_type of 0 represents the number of + * left-shifts where the MSB of mc_addr[5] would still fall within + * the hash_mask. Case 0 does this exactly. Since there are a total + * of 8 bits of shifting, then mc_addr[4] will shift right the + * remaining number of bits. Thus 8 - bit_shift. The rest of the + * cases are a variation of this algorithm...essentially raising the + * number of bits to shift mc_addr[5] left, while still keeping the + * 8-bit shifting total. + * + * For example, given the following Destination MAC Address and an + * mta register count of 128 (thus a 4096-bit vector and 0xFFF mask), + * we can see that the bit_shift for case 0 is 4. These are the hash + * values resulting from each mc_filter_type... + * [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + * + * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563 + * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6 + * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163 + * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634 + */ + switch (hw->mac.mc_filter_type) { + default: + case 0: + break; + case 1: + bit_shift += 1; + break; + case 2: + bit_shift += 2; + break; + case 3: + bit_shift += 4; + break; + } + + hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) | + (((u16) mc_addr[5]) << bit_shift))); + + return hash_value; +} + +/** + * e1000_update_mc_addr_list_generic - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates entire Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 hash_value, hash_bit, hash_reg; + int i; + + DEBUGFUNC("e1000_update_mc_addr_list_generic"); + + /* clear mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* update mta_shadow from mc_addr_list */ + for (i = 0; (u32) i < mc_addr_count; i++) { + hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list); + + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + mc_addr_list += (ETH_ADDR_LEN); + } + + /* replace the entire MTA table */ + for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_pcix_mmrbc_workaround_generic - Fix incorrect MMRBC value + * @hw: pointer to the HW structure + * + * In certain situations, a system BIOS may report that the PCIx maximum + * memory read byte count (MMRBC) value is higher than than the actual + * value. We check the PCIx command register with the current PCIx status + * register. + **/ +void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw) +{ + u16 cmd_mmrbc; + u16 pcix_cmd; + u16 pcix_stat_hi_word; + u16 stat_mmrbc; + + DEBUGFUNC("e1000_pcix_mmrbc_workaround_generic"); + + /* Workaround for PCI-X issue when BIOS sets MMRBC incorrectly */ + if (hw->bus.type != e1000_bus_type_pcix) + return; + + e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + } +} + +/** + * e1000_clear_hw_cntrs_base_generic - Clear base hardware counters + * @hw: pointer to the HW structure + * + * Clears the base hardware counters by reading the counter registers. + **/ +void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_clear_hw_cntrs_base_generic"); + + E1000_READ_REG(hw, E1000_CRCERRS); + E1000_READ_REG(hw, E1000_SYMERRS); + E1000_READ_REG(hw, E1000_MPC); + E1000_READ_REG(hw, E1000_SCC); + E1000_READ_REG(hw, E1000_ECOL); + E1000_READ_REG(hw, E1000_MCC); + E1000_READ_REG(hw, E1000_LATECOL); + E1000_READ_REG(hw, E1000_COLC); + E1000_READ_REG(hw, E1000_DC); + E1000_READ_REG(hw, E1000_SEC); + E1000_READ_REG(hw, E1000_RLEC); + E1000_READ_REG(hw, E1000_XONRXC); + E1000_READ_REG(hw, E1000_XONTXC); + E1000_READ_REG(hw, E1000_XOFFRXC); + E1000_READ_REG(hw, E1000_XOFFTXC); + E1000_READ_REG(hw, E1000_FCRUC); + E1000_READ_REG(hw, E1000_GPRC); + E1000_READ_REG(hw, E1000_BPRC); + E1000_READ_REG(hw, E1000_MPRC); + E1000_READ_REG(hw, E1000_GPTC); + E1000_READ_REG(hw, E1000_GORCL); + E1000_READ_REG(hw, E1000_GORCH); + E1000_READ_REG(hw, E1000_GOTCL); + E1000_READ_REG(hw, E1000_GOTCH); + E1000_READ_REG(hw, E1000_RNBC); + E1000_READ_REG(hw, E1000_RUC); + E1000_READ_REG(hw, E1000_RFC); + E1000_READ_REG(hw, E1000_ROC); + E1000_READ_REG(hw, E1000_RJC); + E1000_READ_REG(hw, E1000_TORL); + E1000_READ_REG(hw, E1000_TORH); + E1000_READ_REG(hw, E1000_TOTL); + E1000_READ_REG(hw, E1000_TOTH); + E1000_READ_REG(hw, E1000_TPR); + E1000_READ_REG(hw, E1000_TPT); + E1000_READ_REG(hw, E1000_MPTC); + E1000_READ_REG(hw, E1000_BPTC); +} + +/** + * e1000_check_for_copper_link_generic - Check for link (Copper) + * @hw: pointer to the HW structure + * + * Checks to see of the link status of the hardware has changed. If a + * change in link status has been detected, then we read the PHY registers + * to get the current speed/duplex if link exists. + **/ +s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + bool link; + + DEBUGFUNC("e1000_check_for_copper_link"); + + /* We only want to go out to the PHY registers to see if Auto-Neg + * has completed and/or if our link status has changed. The + * get_link_status flag is set upon receiving a Link Status + * Change or Rx Sequence Error interrupt. + */ + if (!mac->get_link_status) + return E1000_SUCCESS; + + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) + return E1000_SUCCESS; /* No link detected */ + + mac->get_link_status = false; + + /* Check if there was DownShift, must be checked + * immediately after link-up + */ + e1000_check_downshift_generic(hw); + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!mac->autoneg) + return -E1000_ERR_CONFIG; + + /* Auto-Neg is enabled. Auto Speed Detection takes care + * of MAC speed/duplex configuration. So we only need to + * configure Collision Distance in the MAC. + */ + mac->ops.config_collision_dist(hw); + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) + DEBUGOUT("Error configuring flow control\n"); + + return ret_val; +} + +/** + * e1000_check_for_fiber_link_generic - Check for link (Fiber) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val; + + DEBUGFUNC("e1000_check_for_fiber_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), the cable is plugged in (we have signal), + * and our link partner is not trying to auto-negotiate with us (we + * are receiving idles or data), we need to force link up. We also + * need to give auto-negotiation time to complete, in case the cable + * was just plugged in. The autoneg_failed flag does this. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if ((ctrl & E1000_CTRL_SWDPIN1) && !(status & E1000_STATUS_LU) && + !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = true; + return E1000_SUCCESS; + } + DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("Rx'ing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = true; + } + + return E1000_SUCCESS; +} + +/** + * e1000_check_for_serdes_link_generic - Check for link (Serdes) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val; + + DEBUGFUNC("e1000_check_for_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), and our link partner is not trying to + * auto-negotiate with us (we are receiving idles or data), + * we need to force link up. We also need to give auto-negotiation + * time to complete. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if (!(status & E1000_STATUS_LU) && !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = true; + return E1000_SUCCESS; + } + DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("Rx'ing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = true; + } else if (!(E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW))) { + /* If we force link for non-auto-negotiation switch, check + * link status based on MAC synchronization for internal + * serdes media type. + */ + /* SYNCH bit and IV bit are sticky. */ + usec_delay(10); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + mac->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - forced.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - force failed.\n"); + } + } + + if (E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW)) { + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_LU) { + /* SYNCH bit and IV bit are sticky, so reread rxcw. */ + usec_delay(10); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + mac->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - autoneg completed successfully.\n"); + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - invalid codewords detected in autoneg.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - no sync.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - autoneg failed\n"); + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_set_default_fc_generic - Set flow control default values + * @hw: pointer to the HW structure + * + * Read the EEPROM for the default values for flow control and store the + * values. + **/ +static s32 e1000_set_default_fc_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 nvm_data; + u16 nvm_offset = 0; + + DEBUGFUNC("e1000_set_default_fc_generic"); + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (hw->mac.type == e1000_i350) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(hw->bus.func); + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG + + nvm_offset, + 1, &nvm_data); + } else { + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG, + 1, &nvm_data); + } + + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (!(nvm_data & NVM_WORD0F_PAUSE_MASK)) + hw->fc.requested_mode = e1000_fc_none; + else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == + NVM_WORD0F_ASM_DIR) + hw->fc.requested_mode = e1000_fc_tx_pause; + else + hw->fc.requested_mode = e1000_fc_full; + + return E1000_SUCCESS; +} + +/** + * e1000_setup_link_generic - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +s32 e1000_setup_link_generic(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_setup_link_generic"); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not need to set it up again. + */ + if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) + return E1000_SUCCESS; + + /* If requested flow control is set to default, set flow control + * based on the EEPROM flow control settings. + */ + if (hw->fc.requested_mode == e1000_fc_default) { + ret_val = e1000_set_default_fc_generic(hw); + if (ret_val) + return ret_val; + } + + /* Save off the requested flow control mode for use later. Depending + * on the link partner's capabilities, we may or may not use this mode. + */ + hw->fc.current_mode = hw->fc.requested_mode; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", + hw->fc.current_mode); + + /* Call the necessary media_type subroutine to configure the link. */ + ret_val = hw->mac.ops.setup_physical_interface(hw); + if (ret_val) + return ret_val; + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + E1000_WRITE_REG(hw, E1000_FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, E1000_FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, E1000_FCAL, FLOW_CONTROL_ADDRESS_LOW); + + E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time); + + return e1000_set_fc_watermarks_generic(hw); +} + +/** + * e1000_commit_fc_settings_generic - Configure flow control + * @hw: pointer to the HW structure + * + * Write the flow control settings to the Transmit Config Word Register (TXCW) + * base on the flow control settings in e1000_mac_info. + **/ +static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 txcw; + + DEBUGFUNC("e1000_commit_fc_settings_generic"); + + /* Check for a software override of the flow control settings, and + * setup the device accordingly. If auto-negotiation is enabled, then + * software will have to set the "PAUSE" bits to the correct value in + * the Transmit Config Word Register (TXCW) and re-start auto- + * negotiation. However, if auto-negotiation is disabled, then + * software will have to manually configure the two flow control enable + * bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we + * do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + */ + switch (hw->fc.current_mode) { + case e1000_fc_none: + /* Flow control completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* Rx Flow control is enabled and Tx Flow control is disabled + * by a software over-ride. Since there really isn't a way to + * advertise that we are capable of Rx Pause ONLY, we will + * advertise that we support both symmetric and asymmetric Rx + * PAUSE. Later, we will disable the adapter's ability to send + * PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is disabled, + * by a software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* Flow control (both Rx and Tx) is enabled by a software + * over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + E1000_WRITE_REG(hw, E1000_TXCW, txcw); + mac->txcw = txcw; + + return E1000_SUCCESS; +} + +/** + * e1000_poll_fiber_serdes_link_generic - Poll for link up + * @hw: pointer to the HW structure + * + * Polls for link up by reading the status register, if link fails to come + * up with auto-negotiation, then the link is forced if a signal is detected. + **/ +static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 i, status; + s32 ret_val; + + DEBUGFUNC("e1000_poll_fiber_serdes_link_generic"); + + /* If we have a signal (the cable is plugged in, or assumed true for + * serdes media) then poll for a "Link-Up" indication in the Device + * Status Register. Time-out if a link isn't seen in 500 milliseconds + * seconds (Auto-negotiation should complete in less than 500 + * milliseconds even if the other end is doing it in SW). + */ + for (i = 0; i < FIBER_LINK_UP_LIMIT; i++) { + msec_delay(10); + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == FIBER_LINK_UP_LIMIT) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + mac->autoneg_failed = true; + /* AutoNeg failed to achieve a link, so we'll call + * mac->check_for_link. This routine will force the + * link up if we detect a signal. This will allow us to + * communicate with non-autonegotiating link partners. + */ + ret_val = mac->ops.check_for_link(hw); + if (ret_val) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + mac->autoneg_failed = false; + } else { + mac->autoneg_failed = false; + DEBUGOUT("Valid Link Found\n"); + } + + return E1000_SUCCESS; +} + +/** + * e1000_setup_fiber_serdes_link_generic - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Configures collision distance and flow control for fiber and serdes + * links. Upon successful setup, poll for link. + **/ +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_setup_fiber_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Take the link out of reset */ + ctrl &= ~E1000_CTRL_LRST; + + hw->mac.ops.config_collision_dist(hw); + + ret_val = e1000_commit_fc_settings_generic(hw); + if (ret_val) + return ret_val; + + /* Since auto-negotiation is enabled, take the link out of reset (the + * link will be in reset, because we previously reset the chip). This + * will restart auto-negotiation. If auto-negotiation is successful + * then the link-up status bit will be set and the flow control enable + * bits (RFCE and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + + /* For these adapters, the SW definable pin 1 is set when the optics + * detect a signal. If we have a signal, then poll for a "Link-Up" + * indication. + */ + if (hw->phy.media_type == e1000_media_type_internal_serdes || + (E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { + ret_val = e1000_poll_fiber_serdes_link_generic(hw); + } else { + DEBUGOUT("No signal detected\n"); + } + + return ret_val; +} + +/** + * e1000_config_collision_dist_generic - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +static void e1000_config_collision_dist_generic(struct e1000_hw *hw) +{ + u32 tctl; + + DEBUGFUNC("e1000_config_collision_dist_generic"); + + tctl = E1000_READ_REG(hw, E1000_TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_set_fc_watermarks_generic - Set flow control high/low watermarks + * @hw: pointer to the HW structure + * + * Sets the flow control high/low threshold (watermark) registers. If + * flow control XON frame transmission is enabled, then set XON frame + * transmission as well. + **/ +s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw) +{ + u32 fcrtl = 0, fcrth = 0; + + DEBUGFUNC("e1000_set_fc_watermarks_generic"); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames is not enabled, then these + * registers will be set to 0. + */ + if (hw->fc.current_mode & e1000_fc_tx_pause) { + /* We need to set up the Receive Threshold high and low water + * marks as well as (optionally) enabling the transmission of + * XON frames. + */ + fcrtl = hw->fc.low_water; + if (hw->fc.send_xon) + fcrtl |= E1000_FCRTL_XONE; + + fcrth = hw->fc.high_water; + } + E1000_WRITE_REG(hw, E1000_FCRTL, fcrtl); + E1000_WRITE_REG(hw, E1000_FCRTH, fcrth); + + return E1000_SUCCESS; +} + +/** + * e1000_force_mac_fc_generic - Force the MAC's flow control settings + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Sets the TFCE and RFCE bits in the + * device control register to reflect the adapter settings. TFCE and RFCE + * need to be explicitly set by software when a copper PHY is used because + * autonegotiation is managed by the PHY rather than the MAC. Software must + * also configure these bits when link is forced on a fiber connection. + **/ +s32 e1000_force_mac_fc_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_force_mac_fc_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc.current_mode" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and Tx flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + DEBUGOUT1("hw->fc.current_mode = %u\n", hw->fc.current_mode); + + switch (hw->fc.current_mode) { + case e1000_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_config_fc_after_link_up_generic - Configures flow control after link + * @hw: pointer to the HW structure + * + * Checks the status of auto-negotiation after link up to ensure that the + * speed and duplex were not forced. If the link needed to be forced, then + * flow control needs to be forced also. If auto-negotiation is enabled + * and did not fail, then we configure flow control based on our link + * partner. + **/ +s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + u32 pcs_status_reg, pcs_adv_reg, pcs_lp_ability_reg, pcs_ctrl_reg; + u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; + u16 speed, duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up_generic"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (mac->autoneg_failed) { + if (hw->phy.media_type == e1000_media_type_fiber || + hw->phy.media_type == e1000_media_type_internal_serdes) + ret_val = e1000_force_mac_fc_generic(hw); + } else { + if (hw->phy.media_type == e1000_media_type_copper) + ret_val = e1000_force_mac_fc_generic(hw); + } + + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->phy.media_type == e1000_media_type_copper) && mac->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); + return ret_val; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (Address 4) and the Auto_Negotiation Base + * Page Ability Register (Address 5) to determine how + * flow control was negotiated. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if (ret_val) + return ret_val; + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + * Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | E1000_fc_full + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise Rx + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == e1000_fc_full) { + hw->fc.current_mode = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = Tx PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } else { + /* Per the IEEE spec, at this point flow control + * should be disabled. + */ + hw->fc.current_mode = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + + if (duplex == HALF_DUPLEX) + hw->fc.current_mode = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc_generic(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have SerDes media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->phy.media_type == e1000_media_type_internal_serdes) && + mac->autoneg) { + /* Read the PCS_LSTS and check to see if AutoNeg + * has completed. + */ + pcs_status_reg = E1000_READ_REG(hw, E1000_PCS_LSTAT); + + if (!(pcs_status_reg & E1000_PCS_LSTS_AN_COMPLETE)) { + DEBUGOUT("PCS Auto Neg has not completed.\n"); + return ret_val; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (PCS_ANADV) and the Auto_Negotiation Base + * Page Ability Register (PCS_LPAB) to determine how + * flow control was negotiated. + */ + pcs_adv_reg = E1000_READ_REG(hw, E1000_PCS_ANADV); + pcs_lp_ability_reg = E1000_READ_REG(hw, E1000_PCS_LPAB); + + /* Two bits in the Auto Negotiation Advertisement Register + * (PCS_ANADV) and two bits in the Auto Negotiation Base + * Page Ability Register (PCS_LPAB) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + * Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise Rx + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == e1000_fc_full) { + hw->fc.current_mode = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + */ + else if (!(pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = Tx PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + */ + else if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + !(pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } else { + /* Per the IEEE spec, at this point flow control + * should be disabled. + */ + hw->fc.current_mode = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\n"); + } + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + pcs_ctrl_reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + pcs_ctrl_reg |= E1000_PCS_LCTL_FORCE_FCTRL; + E1000_WRITE_REG(hw, E1000_PCS_LCTL, pcs_ctrl_reg); + + ret_val = e1000_force_mac_fc_generic(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_speed_and_duplex_copper_generic - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Read the status register for the current speed/duplex and store the current + * speed and duplex for copper connections. + **/ +s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + u32 status; + + DEBUGFUNC("e1000_get_speed_and_duplex_copper_generic"); + + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_speed_and_duplex_fiber_generic - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Sets the speed and duplex to gigabit full duplex (the only possible option) + * for fiber/serdes links. + **/ +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw E1000_UNUSEDARG *hw, + u16 *speed, u16 *duplex) +{ + DEBUGFUNC("e1000_get_speed_and_duplex_fiber_serdes_generic"); + + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + + return E1000_SUCCESS; +} + +/** + * e1000_get_hw_semaphore_generic - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_generic"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_NVM; + } + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_put_hw_semaphore_generic - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + **/ +void e1000_put_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + + DEBUGFUNC("e1000_put_hw_semaphore_generic"); + + swsm = E1000_READ_REG(hw, E1000_SWSM); + + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + + E1000_WRITE_REG(hw, E1000_SWSM, swsm); +} + +/** + * e1000_get_auto_rd_done_generic - Check for auto read completion + * @hw: pointer to the HW structure + * + * Check EEPROM for Auto Read done bit. + **/ +s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw) +{ + s32 i = 0; + + DEBUGFUNC("e1000_get_auto_rd_done_generic"); + + while (i < AUTO_READ_DONE_TIMEOUT) { + if (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_AUTO_RD) + break; + msec_delay(1); + i++; + } + + if (i == AUTO_READ_DONE_TIMEOUT) { + DEBUGOUT("Auto read by HW from NVM has not completed.\n"); + return -E1000_ERR_RESET; + } + + return E1000_SUCCESS; +} + +/** + * e1000_valid_led_default_generic - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_generic"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) + *data = ID_LED_DEFAULT; + + return E1000_SUCCESS; +} + +/** + * e1000_id_led_init_generic - + * @hw: pointer to the HW structure + * + **/ +s32 e1000_id_led_init_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + const u32 ledctl_mask = 0x000000FF; + const u32 ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const u32 ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + u16 data, i, temp; + const u16 led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init_generic"); + + ret_val = hw->nvm.ops.valid_led_default(hw, &data); + if (ret_val) + return ret_val; + + mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL); + mac->ledctl_mode1 = mac->ledctl_default; + mac->ledctl_mode2 = mac->ledctl_default; + + for (i = 0; i < 4; i++) { + temp = (data >> (i << 2)) & led_mask; + switch (temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch (temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_setup_led_generic - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. + **/ +s32 e1000_setup_led_generic(struct e1000_hw *hw) +{ + u32 ledctl; + + DEBUGFUNC("e1000_setup_led_generic"); + + if (hw->mac.ops.setup_led != e1000_setup_led_generic) + return -E1000_ERR_CONFIG; + + if (hw->phy.media_type == e1000_media_type_fiber) { + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + hw->mac.ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << + E1000_LEDCTL_LED0_MODE_SHIFT); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + } else if (hw->phy.media_type == e1000_media_type_copper) { + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + } + + return E1000_SUCCESS; +} + +/** + * e1000_cleanup_led_generic - Set LED config to default operation + * @hw: pointer to the HW structure + * + * Remove the current LED configuration and set the LED configuration + * to the default value, saved from the EEPROM. + **/ +s32 e1000_cleanup_led_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_cleanup_led_generic"); + + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default); + return E1000_SUCCESS; +} + +/** + * e1000_blink_led_generic - Blink LED + * @hw: pointer to the HW structure + * + * Blink the LEDs which are set to be on. + **/ +s32 e1000_blink_led_generic(struct e1000_hw *hw) +{ + u32 ledctl_blink = 0; + u32 i; + + DEBUGFUNC("e1000_blink_led_generic"); + + if (hw->phy.media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* Set the blink bit for each LED that's "on" (0x0E) + * (or "off" if inverted) in ledctl_mode2. The blink + * logic in hardware only works when mode is set to "on" + * so it must be changed accordingly when the mode is + * "off" and inverted. + */ + ledctl_blink = hw->mac.ledctl_mode2; + for (i = 0; i < 32; i += 8) { + u32 mode = (hw->mac.ledctl_mode2 >> i) & + E1000_LEDCTL_LED0_MODE_MASK; + u32 led_default = hw->mac.ledctl_default >> i; + + if ((!(led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_ON)) || + ((led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_OFF))) { + ledctl_blink &= + ~(E1000_LEDCTL_LED0_MODE_MASK << i); + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_MODE_LED_ON) << i; + } + } + } + + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl_blink); + + return E1000_SUCCESS; +} + +/** + * e1000_led_on_generic - Turn LED on + * @hw: pointer to the HW structure + * + * Turn LED on. + **/ +s32 e1000_led_on_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_on_generic"); + + switch (hw->phy.media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_led_off_generic - Turn LED off + * @hw: pointer to the HW structure + * + * Turn LED off. + **/ +s32 e1000_led_off_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_off_generic"); + + switch (hw->phy.media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_set_pcie_no_snoop_generic - Set PCI-express capabilities + * @hw: pointer to the HW structure + * @no_snoop: bitmap of snoop events + * + * Set the PCI-express register to snoop for events enabled in 'no_snoop'. + **/ +void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop) +{ + u32 gcr; + + DEBUGFUNC("e1000_set_pcie_no_snoop_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + return; + + if (no_snoop) { + gcr = E1000_READ_REG(hw, E1000_GCR); + gcr &= ~(PCIE_NO_SNOOP_ALL); + gcr |= no_snoop; + E1000_WRITE_REG(hw, E1000_GCR, gcr); + } +} + +/** + * e1000_disable_pcie_master_generic - Disables PCI-express master access + * @hw: pointer to the HW structure + * + * Returns E1000_SUCCESS if successful, else returns -10 + * (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not caused + * the master requests to be disabled. + * + * Disables PCI-Express master access and verifies there are no pending + * requests. + **/ +s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 timeout = MASTER_DISABLE_TIMEOUT; + + DEBUGFUNC("e1000_disable_pcie_master_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + return E1000_SUCCESS; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + while (timeout) { + if (!(E1000_READ_REG(hw, E1000_STATUS) & + E1000_STATUS_GIO_MASTER_ENABLE) || + E1000_REMOVED(hw->hw_addr)) + break; + usec_delay(100); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Master requests are pending.\n"); + return -E1000_ERR_MASTER_REQUESTS_PENDING; + } + + return E1000_SUCCESS; +} + +/** + * e1000_reset_adaptive_generic - Reset Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Reset the Adaptive Interframe Spacing throttle to default values. + **/ +void e1000_reset_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_reset_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + return; + } + + mac->current_ifs_val = 0; + mac->ifs_min_val = IFS_MIN; + mac->ifs_max_val = IFS_MAX; + mac->ifs_step_size = IFS_STEP; + mac->ifs_ratio = IFS_RATIO; + + mac->in_ifs_mode = false; + E1000_WRITE_REG(hw, E1000_AIT, 0); +} + +/** + * e1000_update_adaptive_generic - Update Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Update the Adaptive Interframe Spacing Throttle value based on the + * time between transmitted packets and time between collisions. + **/ +void e1000_update_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_update_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + return; + } + + if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) { + if (mac->tx_packet_delta > MIN_NUM_XMITS) { + mac->in_ifs_mode = true; + if (mac->current_ifs_val < mac->ifs_max_val) { + if (!mac->current_ifs_val) + mac->current_ifs_val = mac->ifs_min_val; + else + mac->current_ifs_val += + mac->ifs_step_size; + E1000_WRITE_REG(hw, E1000_AIT, + mac->current_ifs_val); + } + } + } else { + if (mac->in_ifs_mode && + (mac->tx_packet_delta <= MIN_NUM_XMITS)) { + mac->current_ifs_val = 0; + mac->in_ifs_mode = false; + E1000_WRITE_REG(hw, E1000_AIT, 0); + } + } +} + +/** + * e1000_validate_mdi_setting_generic - Verify MDI/MDIx settings + * @hw: pointer to the HW structure + * + * Verify that when not using auto-negotiation that MDI/MDIx is correctly + * set, which is forced to MDI mode only. + **/ +static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_validate_mdi_setting_generic"); + + if (!hw->mac.autoneg && (hw->phy.mdix == 0 || hw->phy.mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->phy.mdix = 1; + return -E1000_ERR_CONFIG; + } + + return E1000_SUCCESS; +} + +/** + * e1000_validate_mdi_setting_crossover_generic - Verify MDI/MDIx settings + * @hw: pointer to the HW structure + * + * Validate the MDI/MDIx setting, allowing for auto-crossover during forced + * operation. + **/ +s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_validate_mdi_setting_crossover_generic"); + + return E1000_SUCCESS; +} + +/** + * e1000_write_8bit_ctrl_reg_generic - Write a 8bit CTRL register + * @hw: pointer to the HW structure + * @reg: 32bit register offset such as E1000_SCTL + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes an address/data control type register. There are several of these + * and they all have the format address << 8 | data and bit 31 is polled for + * completion. + **/ +s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data) +{ + u32 i, regvalue = 0; + + DEBUGFUNC("e1000_write_8bit_ctrl_reg_generic"); + + /* Set up the address and data */ + regvalue = ((u32)data) | (offset << E1000_GEN_CTL_ADDRESS_SHIFT); + E1000_WRITE_REG(hw, reg, regvalue); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < E1000_GEN_POLL_TIMEOUT; i++) { + usec_delay(5); + regvalue = E1000_READ_REG(hw, reg); + if (regvalue & E1000_GEN_CTL_READY) + break; + } + if (!(regvalue & E1000_GEN_CTL_READY)) { + DEBUGOUT1("Reg %08x did not indicate ready\n", reg); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} diff --git a/drivers/staging/igb_avb/e1000_mac.h b/drivers/staging/igb_avb/e1000_mac.h new file mode 100644 index 000000000000..a3878361095e --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mac.h @@ -0,0 +1,81 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MAC_H_ +#define _E1000_MAC_H_ + +void e1000_init_mac_ops_generic(struct e1000_hw *hw); +#ifndef E1000_REMOVED +#define E1000_REMOVED(a) (0) +#endif /* E1000_REMOVED */ +void e1000_null_mac_generic(struct e1000_hw *hw); +s32 e1000_null_ops_generic(struct e1000_hw *hw); +s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d); +bool e1000_null_mng_mode(struct e1000_hw *hw); +void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a); +void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b); +int e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a); +s32 e1000_blink_led_generic(struct e1000_hw *hw); +s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_cleanup_led_generic(struct e1000_hw *hw); +s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw); +s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw); +s32 e1000_force_mac_fc_generic(struct e1000_hw *hw); +s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw); +s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw); +void e1000_set_lan_id_single_port(struct e1000_hw *hw); +s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +s32 e1000_id_led_init_generic(struct e1000_hw *hw); +s32 e1000_led_on_generic(struct e1000_hw *hw); +s32 e1000_led_off_generic(struct e1000_hw *hw); +void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count); +s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw); +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_setup_led_generic(struct e1000_hw *hw); +s32 e1000_setup_link_generic(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw *hw); +s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data); + +u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr); + +void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw); +void e1000_clear_vfta_generic(struct e1000_hw *hw); +void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count); +void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw); +void e1000_put_hw_semaphore_generic(struct e1000_hw *hw); +s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw); +void e1000_reset_adaptive_generic(struct e1000_hw *hw); +void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop); +void e1000_update_adaptive_generic(struct e1000_hw *hw); +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value); + +#endif diff --git a/drivers/staging/igb_avb/e1000_manage.c b/drivers/staging/igb_avb/e1000_manage.c new file mode 100644 index 000000000000..36671fbdab13 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_manage.c @@ -0,0 +1,552 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +/** + * e1000_calculate_checksum - Calculate checksum for buffer + * @buffer: pointer to EEPROM + * @length: size of EEPROM to calculate a checksum for + * + * Calculates the checksum for some buffer on a specified length. The + * checksum calculated is returned. + **/ +u8 e1000_calculate_checksum(u8 *buffer, u32 length) +{ + u32 i; + u8 sum = 0; + + DEBUGFUNC("e1000_calculate_checksum"); + + if (!buffer) + return 0; + + for (i = 0; i < length; i++) + sum += buffer[i]; + + return (u8) (0 - sum); +} + +/** + * e1000_mng_enable_host_if_generic - Checks host interface is enabled + * @hw: pointer to the HW structure + * + * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND + * + * This function checks whether the HOST IF is enabled for command operation + * and also checks whether the previous command is completed. It busy waits + * in case of previous command is not completed. + **/ +s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw) +{ + u32 hicr; + u8 i; + + DEBUGFUNC("e1000_mng_enable_host_if_generic"); + + if (!hw->mac.arc_subsystem_valid) { + DEBUGOUT("ARC subsystem not valid.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + /* check the previous command is completed */ + for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay_irq(1); + } + + if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { + DEBUGOUT("Previous command timeout failed .\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + return E1000_SUCCESS; +} + +/** + * e1000_check_mng_mode_generic - Generic check management mode + * @hw: pointer to the HW structure + * + * Reads the firmware semaphore register and returns true (>0) if + * manageability is enabled, else false (0). + **/ +bool e1000_check_mng_mode_generic(struct e1000_hw *hw) +{ + u32 fwsm = E1000_READ_REG(hw, E1000_FWSM); + + DEBUGFUNC("e1000_check_mng_mode_generic"); + + + return (fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT); +} + +/** + * e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on Tx + * @hw: pointer to the HW structure + * + * Enables packet filtering on transmit packets if manageability is enabled + * and host interface is enabled. + **/ +bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw) +{ + struct e1000_host_mng_dhcp_cookie *hdr = &hw->mng_cookie; + u32 *buffer = (u32 *)&hw->mng_cookie; + u32 offset; + s32 ret_val, hdr_csum, csum; + u8 i, len; + + DEBUGFUNC("e1000_enable_tx_pkt_filtering_generic"); + + hw->mac.tx_pkt_filtering = true; + + /* No manageability, no filtering */ + if (!hw->mac.ops.check_mng_mode(hw)) { + hw->mac.tx_pkt_filtering = false; + return hw->mac.tx_pkt_filtering; + } + + /* If we can't read from the host interface for whatever + * reason, disable filtering. + */ + ret_val = e1000_mng_enable_host_if_generic(hw); + if (ret_val != E1000_SUCCESS) { + hw->mac.tx_pkt_filtering = false; + return hw->mac.tx_pkt_filtering; + } + + /* Read in the header. Length and offset are in dwords. */ + len = E1000_MNG_DHCP_COOKIE_LENGTH >> 2; + offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2; + for (i = 0; i < len; i++) + *(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, + offset + i); + hdr_csum = hdr->checksum; + hdr->checksum = 0; + csum = e1000_calculate_checksum((u8 *)hdr, + E1000_MNG_DHCP_COOKIE_LENGTH); + /* If either the checksums or signature don't match, then + * the cookie area isn't considered valid, in which case we + * take the safe route of assuming Tx filtering is enabled. + */ + if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) { + hw->mac.tx_pkt_filtering = true; + return hw->mac.tx_pkt_filtering; + } + + /* Cookie area is valid, make the final check for filtering. */ + if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) + hw->mac.tx_pkt_filtering = false; + + return hw->mac.tx_pkt_filtering; +} + +/** + * e1000_mng_write_cmd_header_generic - Writes manageability command header + * @hw: pointer to the HW structure + * @hdr: pointer to the host interface command header + * + * Writes the command header after does the checksum calculation. + **/ +s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) +{ + u16 i, length = sizeof(struct e1000_host_mng_command_header); + + DEBUGFUNC("e1000_mng_write_cmd_header_generic"); + + /* Write the whole command header structure with new checksum. */ + + hdr->checksum = e1000_calculate_checksum((u8 *)hdr, length); + + length >>= 2; + /* Write the relevant command block into the ram area. */ + for (i = 0; i < length; i++) { + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, + *((u32 *) hdr + i)); + E1000_WRITE_FLUSH(hw); + } + + return E1000_SUCCESS; +} + +/** + * e1000_mng_host_if_write_generic - Write to the manageability host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface buffer + * @length: size of the buffer + * @offset: location in the buffer to write to + * @sum: sum of the data (not checksum) + * + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient + * way. Also fills up the sum of the buffer in *buffer parameter. + **/ +s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, + u16 length, u16 offset, u8 *sum) +{ + u8 *tmp; + u8 *bufptr = buffer; + u32 data = 0; + u16 remaining, i, j, prev_bytes; + + DEBUGFUNC("e1000_mng_host_if_write_generic"); + + /* sum = only sum of the data and it is not checksum */ + + if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) + return -E1000_ERR_PARAM; + + tmp = (u8 *)&data; + prev_bytes = offset & 0x3; + offset >>= 2; + + if (prev_bytes) { + data = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset); + for (j = prev_bytes; j < sizeof(u32); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset, data); + length -= j - prev_bytes; + offset++; + } + + remaining = length & 0x3; + length -= remaining; + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block into the + * ram area. + */ + for (i = 0; i < length; i++) { + for (j = 0; j < sizeof(u32); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, + data); + } + if (remaining) { + for (j = 0; j < sizeof(u32); j++) { + if (j < remaining) + *(tmp + j) = *bufptr++; + else + *(tmp + j) = 0; + + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, + data); + } + + return E1000_SUCCESS; +} + +/** + * e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer, + u16 length) +{ + struct e1000_host_mng_command_header hdr; + s32 ret_val; + u32 hicr; + + DEBUGFUNC("e1000_mng_write_dhcp_info_generic"); + + hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; + hdr.command_length = length; + hdr.reserved1 = 0; + hdr.reserved2 = 0; + hdr.checksum = 0; + + /* Enable the host interface */ + ret_val = e1000_mng_enable_host_if_generic(hw); + if (ret_val) + return ret_val; + + /* Populate the host interface with the contents of "buffer". */ + ret_val = e1000_mng_host_if_write_generic(hw, buffer, length, + sizeof(hdr), &(hdr.checksum)); + if (ret_val) + return ret_val; + + /* Write the manageability command header */ + ret_val = e1000_mng_write_cmd_header_generic(hw, &hdr); + if (ret_val) + return ret_val; + + /* Tell the ARC a new command is pending. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + return E1000_SUCCESS; +} + +/** + * e1000_enable_mng_pass_thru - Check if management passthrough is needed + * @hw: pointer to the HW structure + * + * Verifies the hardware needs to leave interface enabled so that frames can + * be directed to and from the management interface. + **/ +bool e1000_enable_mng_pass_thru(struct e1000_hw *hw) +{ + u32 manc; + u32 fwsm, factps; + + DEBUGFUNC("e1000_enable_mng_pass_thru"); + + if (!hw->mac.asf_firmware_present) + return false; + + manc = E1000_READ_REG(hw, E1000_MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN)) + return false; + + if (hw->mac.has_fwsm) { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + factps = E1000_READ_REG(hw, E1000_FACTPS); + + if (!(factps & E1000_FACTPS_MNGCG) && + ((fwsm & E1000_FWSM_MODE_MASK) == + (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) + return true; + } else if ((manc & E1000_MANC_SMBUS_EN) && + !(manc & E1000_MANC_ASF_EN)) { + return true; + } + + return false; +} + +/** + * e1000_host_interface_command - Writes buffer to host interface + * @hw: pointer to the HW structure + * @buffer: contains a command to write + * @length: the byte length of the buffer, must be multiple of 4 bytes + * + * Writes a buffer to the Host Interface. Upon success, returns E1000_SUCCESS + * else returns E1000_ERR_HOST_INTERFACE_COMMAND. + **/ +s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length) +{ + u32 hicr, i; + + DEBUGFUNC("e1000_host_interface_command"); + + if (!(hw->mac.arc_subsystem_valid)) { + DEBUGOUT("Hardware doesn't support host interface command.\n"); + return E1000_SUCCESS; + } + + if (!hw->mac.asf_firmware_present) { + DEBUGOUT("Firmware is not present.\n"); + return E1000_SUCCESS; + } + + if (length == 0 || length & 0x3 || + length > E1000_HI_MAX_BLOCK_BYTE_LENGTH) { + DEBUGOUT("Buffer length failure.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block + * into the ram area. + */ + for (i = 0; i < length; i++) + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, + *((u32 *)buffer + i)); + + /* Setting this bit tells the ARC that a new command is pending. */ + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay(1); + } + + /* Check command successful completion. */ + if (i == E1000_HI_COMMAND_TIMEOUT || + (!(E1000_READ_REG(hw, E1000_HICR) & E1000_HICR_SV))) { + DEBUGOUT("Command has failed with no status valid.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + for (i = 0; i < length; i++) + *((u32 *)buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, + E1000_HOST_IF, + i); + + return E1000_SUCCESS; +} +/** + * e1000_load_firmware - Writes proxy FW code buffer to host interface + * and execute. + * @hw: pointer to the HW structure + * @buffer: contains a firmware to write + * @length: the byte length of the buffer, must be multiple of 4 bytes + * + * Upon success returns E1000_SUCCESS, returns E1000_ERR_CONFIG if not enabled + * in HW else returns E1000_ERR_HOST_INTERFACE_COMMAND. + **/ +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length) +{ + u32 hicr, hibba, fwsm, icr, i; + + DEBUGFUNC("e1000_load_firmware"); + + if (hw->mac.type < e1000_i210) { + DEBUGOUT("Hardware doesn't support loading FW by the driver\n"); + return -E1000_ERR_CONFIG; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + if (!(hicr & E1000_HICR_MEMORY_BASE_EN)) { + DEBUGOUT("E1000_HICR_MEMORY_BASE_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + + if (length == 0 || length & 0x3 || length > E1000_HI_FW_MAX_LENGTH) { + DEBUGOUT("Buffer length failure.\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + /* Clear notification from ROM-FW by reading ICR register */ + icr = E1000_READ_REG(hw, E1000_ICR_V2); + + /* Reset ROM-FW */ + hicr = E1000_READ_REG(hw, E1000_HICR); + hicr |= E1000_HICR_FW_RESET_ENABLE; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + hicr |= E1000_HICR_FW_RESET; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + E1000_WRITE_FLUSH(hw); + + /* Wait till MAC notifies about its readiness after ROM-FW reset */ + for (i = 0; i < (E1000_HI_COMMAND_TIMEOUT * 2); i++) { + icr = E1000_READ_REG(hw, E1000_ICR_V2); + if (icr & E1000_ICR_MNG) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Wait till MAC is ready to accept new FW code */ + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + if ((fwsm & E1000_FWSM_FW_VALID) && + ((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT == + E1000_FWSM_HI_EN_ONLY_MODE)) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant FW code block + * into the ram area in DWORDs via 1kB ram addressing window. + */ + for (i = 0; i < length; i++) { + if (!(i % E1000_HI_FW_BLOCK_DWORD_LENGTH)) { + /* Point to correct 1kB ram window */ + hibba = E1000_HI_FW_BASE_ADDRESS + + ((E1000_HI_FW_BLOCK_DWORD_LENGTH << 2) * + (i / E1000_HI_FW_BLOCK_DWORD_LENGTH)); + + E1000_WRITE_REG(hw, E1000_HIBBA, hibba); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, + i % E1000_HI_FW_BLOCK_DWORD_LENGTH, + *((u32 *)buffer + i)); + } + + /* Setting this bit tells the ARC that a new FW is ready to execute. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay(1); + } + + /* Check for successful FW start. */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("New FW did not start within timeout period.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + return E1000_SUCCESS; +} + diff --git a/drivers/staging/igb_avb/e1000_manage.h b/drivers/staging/igb_avb/e1000_manage.h new file mode 100644 index 000000000000..09afc1aed497 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_manage.h @@ -0,0 +1,86 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MANAGE_H_ +#define _E1000_MANAGE_H_ + +bool e1000_check_mng_mode_generic(struct e1000_hw *hw); +bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw); +s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw); +s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, + u16 length, u16 offset, u8 *sum); +s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr); +s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, + u8 *buffer, u16 length); +bool e1000_enable_mng_pass_thru(struct e1000_hw *hw); +u8 e1000_calculate_checksum(u8 *buffer, u32 length); +s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length); +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length); + +enum e1000_mng_mode { + e1000_mng_mode_none = 0, + e1000_mng_mode_asf, + e1000_mng_mode_pt, + e1000_mng_mode_ipmi, + e1000_mng_mode_host_if_only +}; + +#define E1000_FACTPS_MNGCG 0x20000000 + +#define E1000_FWSM_MODE_MASK 0xE +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 +#define E1000_FWSM_HI_EN_ONLY_MODE 0x4 + +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 +#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING 0x1 +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 + +#define E1000_VFTA_ENTRY_SHIFT 5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F + +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Num of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Num of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Process HI cmd limit */ +#define E1000_HI_FW_BASE_ADDRESS 0x10000 +#define E1000_HI_FW_MAX_LENGTH (64 * 1024) /* Num of bytes */ +#define E1000_HI_FW_BLOCK_DWORD_LENGTH 256 /* Num of DWORDs per page */ +#define E1000_HICR_MEMORY_BASE_EN 0x200 /* MB Enable bit - RO */ +#define E1000_HICR_EN 0x01 /* Enable bit - RO */ +/* Driver sets this bit when done to put command in RAM */ +#define E1000_HICR_C 0x02 +#define E1000_HICR_SV 0x04 /* Status Validity */ +#define E1000_HICR_FW_RESET_ENABLE 0x40 +#define E1000_HICR_FW_RESET 0x80 + +/* Intel(R) Active Management Technology signature */ +#define E1000_IAMT_SIGNATURE 0x544D4149 + +#endif diff --git a/drivers/staging/igb_avb/e1000_mbx.c b/drivers/staging/igb_avb/e1000_mbx.c new file mode 100644 index 000000000000..f2998f470ce5 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mbx.c @@ -0,0 +1,523 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_mbx.h" + +/** + * e1000_null_mbx_check_for_flag - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +static s32 e1000_null_mbx_check_for_flag(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG mbx_id) +{ + DEBUGFUNC("e1000_null_mbx_check_flag"); + + return E1000_SUCCESS; +} + +/** + * e1000_null_mbx_transact - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +static s32 e1000_null_mbx_transact(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG *msg, + u16 E1000_UNUSEDARG size, + u16 E1000_UNUSEDARG mbx_id) +{ + DEBUGFUNC("e1000_null_mbx_rw_msg"); + + return E1000_SUCCESS; +} + +/** + * e1000_read_mbx - Reads a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to read + * + * returns SUCCESS if it successfuly read message from buffer + **/ +s32 e1000_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_read_mbx"); + + /* limit read to size of mailbox */ + if (size > mbx->size) + size = mbx->size; + + if (mbx->ops.read) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); + + return ret_val; +} + +/** + * e1000_write_mbx - Write a message to the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +s32 e1000_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_mbx"); + + if (size > mbx->size) + ret_val = -E1000_ERR_MBX; + + else if (mbx->ops.write) + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_msg - checks to see if someone sent us mail + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_msg(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_msg"); + + if (mbx->ops.check_for_msg) + ret_val = mbx->ops.check_for_msg(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_ack - checks to see if someone sent us ACK + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_ack(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_ack"); + + if (mbx->ops.check_for_ack) + ret_val = mbx->ops.check_for_ack(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_rst - checks to see if other side has reset + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_rst(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_rst"); + + if (mbx->ops.check_for_rst) + ret_val = mbx->ops.check_for_rst(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_poll_for_msg - Wait for message notification + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification + **/ +static s32 e1000_poll_for_msg(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + DEBUGFUNC("e1000_poll_for_msg"); + + if (!countdown || !mbx->ops.check_for_msg) + goto out; + + while (countdown && mbx->ops.check_for_msg(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + usec_delay(mbx->usec_delay); + } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; +out: + return countdown ? E1000_SUCCESS : -E1000_ERR_MBX; +} + +/** + * e1000_poll_for_ack - Wait for message acknowledgement + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message acknowledgement + **/ +static s32 e1000_poll_for_ack(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + DEBUGFUNC("e1000_poll_for_ack"); + + if (!countdown || !mbx->ops.check_for_ack) + goto out; + + while (countdown && mbx->ops.check_for_ack(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + usec_delay(mbx->usec_delay); + } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; +out: + return countdown ? E1000_SUCCESS : -E1000_ERR_MBX; +} + +/** + * e1000_read_posted_mbx - Wait for message notification and receive message + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification and + * copied it into the receive buffer. + **/ +s32 e1000_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_read_posted_mbx"); + + if (!mbx->ops.read) + goto out; + + ret_val = e1000_poll_for_msg(hw, mbx_id); + + /* if ack received read message, otherwise we timed out */ + if (!ret_val) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); +out: + return ret_val; +} + +/** + * e1000_write_posted_mbx - Write a message to the mailbox, wait for ack + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer and + * received an ack to that message within delay * timeout period + **/ +s32 e1000_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_write_posted_mbx"); + + /* exit if either we can't write or there isn't a defined timeout */ + if (!mbx->ops.write || !mbx->timeout) + goto out; + + /* send msg */ + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + /* if msg sent wait until we receive an ack */ + if (!ret_val) + ret_val = e1000_poll_for_ack(hw, mbx_id); +out: + return ret_val; +} + +/** + * e1000_init_mbx_ops_generic - Initialize mbx function pointers + * @hw: pointer to the HW structure + * + * Sets the function pointers to no-op functions + **/ +void e1000_init_mbx_ops_generic(struct e1000_hw *hw) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + mbx->ops.init_params = e1000_null_ops_generic; + mbx->ops.read = e1000_null_mbx_transact; + mbx->ops.write = e1000_null_mbx_transact; + mbx->ops.check_for_msg = e1000_null_mbx_check_for_flag; + mbx->ops.check_for_ack = e1000_null_mbx_check_for_flag; + mbx->ops.check_for_rst = e1000_null_mbx_check_for_flag; + mbx->ops.read_posted = e1000_read_posted_mbx; + mbx->ops.write_posted = e1000_write_posted_mbx; +} + +static s32 e1000_check_for_bit_pf(struct e1000_hw *hw, u32 mask) +{ + u32 mbvficr = E1000_READ_REG(hw, E1000_MBVFICR); + s32 ret_val = -E1000_ERR_MBX; + + if (mbvficr & mask) { + ret_val = E1000_SUCCESS; + E1000_WRITE_REG(hw, E1000_MBVFICR, mask); + } + + return ret_val; +} + +/** + * e1000_check_for_msg_pf - checks to see if the VF has sent mail + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_msg_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_msg_pf"); + + if (!e1000_check_for_bit_pf(hw, E1000_MBVFICR_VFREQ_VF1 << vf_number)) { + ret_val = E1000_SUCCESS; + hw->mbx.stats.reqs++; + } + + return ret_val; +} + +/** + * e1000_check_for_ack_pf - checks to see if the VF has ACKed + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_ack_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_ack_pf"); + + if (!e1000_check_for_bit_pf(hw, E1000_MBVFICR_VFACK_VF1 << vf_number)) { + ret_val = E1000_SUCCESS; + hw->mbx.stats.acks++; + } + + return ret_val; +} + +/** + * e1000_check_for_rst_pf - checks to see if the VF has reset + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number) +{ + u32 vflre = E1000_READ_REG(hw, E1000_VFLRE); + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_rst_pf"); + + if (vflre & (1 << vf_number)) { + ret_val = E1000_SUCCESS; + E1000_WRITE_REG(hw, E1000_VFLRE, (1 << vf_number)); + hw->mbx.stats.rsts++; + } + + return ret_val; +} + +/** + * e1000_obtain_mbx_lock_pf - obtain mailbox lock + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * return SUCCESS if we obtained the mailbox lock + **/ +static s32 e1000_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + u32 p2v_mailbox; + + DEBUGFUNC("e1000_obtain_mbx_lock_pf"); + + /* Take ownership of the buffer */ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU); + + /* reserve mailbox for vf use */ + p2v_mailbox = E1000_READ_REG(hw, E1000_P2VMAILBOX(vf_number)); + if (p2v_mailbox & E1000_P2VMAILBOX_PFU) + ret_val = E1000_SUCCESS; + + return ret_val; +} + +/** + * e1000_write_mbx_pf - Places a message in the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @vf_number: the VF index + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +static s32 e1000_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, + u16 vf_number) +{ + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_write_mbx_pf"); + + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = e1000_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) + goto out_no_write; + + /* flush msg and acks as we are overwriting the message buffer */ + e1000_check_for_msg_pf(hw, vf_number); + e1000_check_for_ack_pf(hw, vf_number); + + /* copy the caller specified message to the mailbox memory buffer */ + for (i = 0; i < size; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VMBMEM(vf_number), i, msg[i]); + + /* Interrupt VF to tell it a message has been sent and release buffer*/ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_STS); + + /* update stats */ + hw->mbx.stats.msgs_tx++; + +out_no_write: + return ret_val; + +} + +/** + * e1000_read_mbx_pf - Read a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @vf_number: the VF index + * + * This function copies a message from the mailbox buffer to the caller's + * memory buffer. The presumption is that the caller knows that there was + * a message due to a VF request so no polling for message is needed. + **/ +static s32 e1000_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, + u16 vf_number) +{ + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_read_mbx_pf"); + + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = e1000_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) + goto out_no_read; + + /* copy the message to the mailbox memory buffer */ + for (i = 0; i < size; i++) + msg[i] = E1000_READ_REG_ARRAY(hw, E1000_VMBMEM(vf_number), i); + + /* Acknowledge the message and release buffer */ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + + /* update stats */ + hw->mbx.stats.msgs_rx++; + +out_no_read: + return ret_val; +} + +/** + * e1000_init_mbx_params_pf - set initial values for pf mailbox + * @hw: pointer to the HW structure + * + * Initializes the hw->mbx struct to correct values for pf mailbox + */ +s32 e1000_init_mbx_params_pf(struct e1000_hw *hw) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + + switch (hw->mac.type) { + case e1000_82576: + case e1000_i350: + case e1000_i354: + mbx->timeout = 0; + mbx->usec_delay = 0; + + mbx->size = E1000_VFMAILBOX_SIZE; + + mbx->ops.read = e1000_read_mbx_pf; + mbx->ops.write = e1000_write_mbx_pf; + mbx->ops.read_posted = e1000_read_posted_mbx; + mbx->ops.write_posted = e1000_write_posted_mbx; + mbx->ops.check_for_msg = e1000_check_for_msg_pf; + mbx->ops.check_for_ack = e1000_check_for_ack_pf; + mbx->ops.check_for_rst = e1000_check_for_rst_pf; + + mbx->stats.msgs_tx = 0; + mbx->stats.msgs_rx = 0; + mbx->stats.reqs = 0; + mbx->stats.acks = 0; + mbx->stats.rsts = 0; + default: + return E1000_SUCCESS; + } +} + diff --git a/drivers/staging/igb_avb/e1000_mbx.h b/drivers/staging/igb_avb/e1000_mbx.h new file mode 100644 index 000000000000..28900216ac25 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mbx.h @@ -0,0 +1,84 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MBX_H_ +#define _E1000_MBX_H_ + +#include "e1000_api.h" + +#define E1000_P2VMAILBOX_STS 0x00000001 /* Initiate message send to VF */ +#define E1000_P2VMAILBOX_ACK 0x00000002 /* Ack message recv'd from VF */ +#define E1000_P2VMAILBOX_VFU 0x00000004 /* VF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_PFU 0x00000008 /* PF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_RVFU 0x00000010 /* Reset VFU - used when VF stuck */ + +#define E1000_MBVFICR_VFREQ_MASK 0x000000FF /* bits for VF messages */ +#define E1000_MBVFICR_VFREQ_VF1 0x00000001 /* bit for VF 1 message */ +#define E1000_MBVFICR_VFACK_MASK 0x00FF0000 /* bits for VF acks */ +#define E1000_MBVFICR_VFACK_VF1 0x00010000 /* bit for VF 1 ack */ + +#define E1000_VFMAILBOX_SIZE 16 /* 16 32 bit words - 64 bytes */ + +/* If it's a E1000_VF_* msg then it originates in the VF and is sent to the + * PF. The reverse is true if it is E1000_PF_*. + * Message ACK's are the value or'd with 0xF0000000 + */ +/* Msgs below or'd with this are the ACK */ +#define E1000_VT_MSGTYPE_ACK 0x80000000 +/* Msgs below or'd with this are the NACK */ +#define E1000_VT_MSGTYPE_NACK 0x40000000 +/* Indicates that VF is still clear to send requests */ +#define E1000_VT_MSGTYPE_CTS 0x20000000 +#define E1000_VT_MSGINFO_SHIFT 16 +/* bits 23:16 are used for extra info for certain messages */ +#define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) + +#define E1000_VF_RESET 0x01 /* VF requests reset */ +#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */ +#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */ +#define E1000_VF_SET_MULTICAST_COUNT_MASK (0x1F << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_MULTICAST_OVERFLOW (0x80 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */ +#define E1000_VF_SET_VLAN_ADD (0x01 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_LPE 0x05 /* reqs to set VMOLR.LPE */ +#define E1000_VF_SET_PROMISC 0x06 /* reqs to clear VMOLR.ROPE/MPME*/ +#define E1000_VF_SET_PROMISC_UNICAST (0x01 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT) + +#define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ + +#define E1000_VF_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */ +#define E1000_VF_MBX_INIT_DELAY 500 /* microseconds between retries */ + +s32 e1000_read_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_write_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_read_posted_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_write_posted_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_check_for_msg(struct e1000_hw *, u16); +s32 e1000_check_for_ack(struct e1000_hw *, u16); +s32 e1000_check_for_rst(struct e1000_hw *, u16); +void e1000_init_mbx_ops_generic(struct e1000_hw *hw); +s32 e1000_init_mbx_params_pf(struct e1000_hw *); + +#endif /* _E1000_MBX_H_ */ diff --git a/drivers/staging/igb_avb/e1000_nvm.c b/drivers/staging/igb_avb/e1000_nvm.c new file mode 100644 index 000000000000..c328f40dfa20 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_nvm.c @@ -0,0 +1,973 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static void e1000_reload_nvm_generic(struct e1000_hw *hw); + +/** + * e1000_init_nvm_ops_generic - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_nvm_ops_generic(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + DEBUGFUNC("e1000_init_nvm_ops_generic"); + + /* Initialize function pointers */ + nvm->ops.init_params = e1000_null_ops_generic; + nvm->ops.acquire = e1000_null_ops_generic; + nvm->ops.read = e1000_null_read_nvm; + nvm->ops.release = e1000_null_nvm_generic; + nvm->ops.reload = e1000_reload_nvm_generic; + nvm->ops.update = e1000_null_ops_generic; + nvm->ops.valid_led_default = e1000_null_led_default; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.write = e1000_null_write_nvm; +} + +/** + * e1000_null_nvm_read - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_read_nvm(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b, + u16 E1000_UNUSEDARG *c) +{ + DEBUGFUNC("e1000_null_read_nvm"); + return E1000_SUCCESS; +} + +/** + * e1000_null_nvm_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_nvm_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_nvm_generic"); + return; +} + +/** + * e1000_null_led_default - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_led_default(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_null_led_default"); + return E1000_SUCCESS; +} + +/** + * e1000_null_write_nvm - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_write_nvm(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b, + u16 E1000_UNUSEDARG *c) +{ + DEBUGFUNC("e1000_null_write_nvm"); + return E1000_SUCCESS; +} + +/** + * e1000_raise_eec_clk - Raise EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Enable/Raise the EEPROM clock bit. + **/ +static void e1000_raise_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_lower_eec_clk - Lower EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Clear/Lower the EEPROM clock bit. + **/ +static void e1000_lower_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_shift_out_eec_bits - Shift data bits our to the EEPROM + * @hw: pointer to the HW structure + * @data: data to send to the EEPROM + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the EEPROM. So, the value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +static void e1000_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u32 mask; + + DEBUGFUNC("e1000_shift_out_eec_bits"); + + mask = 0x01 << (count - 1); + if (nvm->type == e1000_nvm_eeprom_spi) + eecd |= E1000_EECD_DO; + + do { + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + + usec_delay(nvm->delay_usec); + + e1000_raise_eec_clk(hw, &eecd); + e1000_lower_eec_clk(hw, &eecd); + + mask >>= 1; + } while (mask); + + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_shift_in_eec_bits - Shift data bits in from the EEPROM + * @hw: pointer to the HW structure + * @count: number of bits to shift in + * + * In order to read a register from the EEPROM, we need to shift 'count' bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the data out + * "DO" bit. During this "shifting in" process the data in "DI" bit should + * always be clear. + **/ +static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) +{ + u32 eecd; + u32 i; + u16 data; + + DEBUGFUNC("e1000_shift_in_eec_bits"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data <<= 1; + e1000_raise_eec_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~E1000_EECD_DI; + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_eec_clk(hw, &eecd); + } + + return data; +} + +/** + * e1000_poll_eerd_eewr_done - Poll for EEPROM read/write completion + * @hw: pointer to the HW structure + * @ee_reg: EEPROM flag for polling + * + * Polls the EEPROM status bit for either read or write completion based + * upon the value of 'ee_reg'. + **/ +s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg) +{ + u32 attempts = 100000; + u32 i, reg = 0; + + DEBUGFUNC("e1000_poll_eerd_eewr_done"); + + for (i = 0; i < attempts; i++) { + if (ee_reg == E1000_NVM_POLL_READ) + reg = E1000_READ_REG(hw, E1000_EERD); + else + reg = E1000_READ_REG(hw, E1000_EEWR); + + if (reg & E1000_NVM_RW_REG_DONE) + return E1000_SUCCESS; + + usec_delay(5); + } + + return -E1000_ERR_NVM; +} + +/** + * e1000_acquire_nvm_generic - Generic request for access to EEPROM + * @hw: pointer to the HW structure + * + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +s32 e1000_acquire_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + s32 timeout = E1000_NVM_GRANT_ATTEMPTS; + + DEBUGFUNC("e1000_acquire_nvm_generic"); + + E1000_WRITE_REG(hw, E1000_EECD, eecd | E1000_EECD_REQ); + eecd = E1000_READ_REG(hw, E1000_EECD); + + while (timeout) { + if (eecd & E1000_EECD_GNT) + break; + usec_delay(5); + eecd = E1000_READ_REG(hw, E1000_EECD); + timeout--; + } + + if (!timeout) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + DEBUGOUT("Could not acquire NVM grant\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_standby_nvm - Return EEPROM to standby state + * @hw: pointer to the HW structure + * + * Return the EEPROM to a standby state. + **/ +static void e1000_standby_nvm(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + + DEBUGFUNC("e1000_standby_nvm"); + + if (nvm->type == e1000_nvm_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + } +} + +/** + * e1000_stop_nvm - Terminate EEPROM command + * @hw: pointer to the HW structure + * + * Terminates the current command by inverting the EEPROM's chip select pin. + **/ +static void e1000_stop_nvm(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_stop_nvm"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + if (hw->nvm.type == e1000_nvm_eeprom_spi) { + /* Pull CS high */ + eecd |= E1000_EECD_CS; + e1000_lower_eec_clk(hw, &eecd); + } +} + +/** + * e1000_release_nvm_generic - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit. + **/ +void e1000_release_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_release_nvm_generic"); + + e1000_stop_nvm(hw); + + eecd = E1000_READ_REG(hw, E1000_EECD); + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_ready_nvm_eeprom - Prepares EEPROM for read/write + * @hw: pointer to the HW structure + * + * Setups the EEPROM for reading and writing. + **/ +static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u8 spi_stat_reg; + + DEBUGFUNC("e1000_ready_nvm_eeprom"); + + if (nvm->type == e1000_nvm_eeprom_spi) { + u16 timeout = NVM_MAX_RETRY_SPI; + + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(1); + + /* Read "Status Register" repeatedly until the LSB is cleared. + * The EEPROM will signal that the command has been completed + * by clearing bit 0 of the internal status register. If it's + * not cleared within 'timeout', then error out. + */ + while (timeout) { + e1000_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI, + hw->nvm.opcode_bits); + spi_stat_reg = (u8)e1000_shift_in_eec_bits(hw, 8); + if (!(spi_stat_reg & NVM_STATUS_RDY_SPI)) + break; + + usec_delay(5); + e1000_standby_nvm(hw); + timeout--; + } + + if (!timeout) { + DEBUGOUT("SPI NVM Status error\n"); + return -E1000_ERR_NVM; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_nvm_spi - Read EEPROM's using SPI + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM. + **/ +s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i = 0; + s32 ret_val; + u16 word_in; + u8 read_opcode = NVM_READ_OPCODE_SPI; + + DEBUGFUNC("e1000_read_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + ret_val = nvm->ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + e1000_standby_nvm(hw); + + if ((nvm->address_bits == 8) && (offset >= 128)) + read_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits); + + /* Read the data. SPI NVMs increment the address with each byte + * read and will roll over if reading beyond the end. This allows + * us to read the whole NVM from any offset + */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_eec_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + +release: + nvm->ops.release(hw); + + return ret_val; +} + +/** + * e1000_read_nvm_eerd - Reads EEPROM using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the EERD register. + **/ +s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, eerd = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_nvm_eerd"); + + /* A check for invalid values: offset too large, too many words, + * too many words for the offset, and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) + + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_EERD, eerd); + ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ); + if (ret_val) + break; + + data[i] = (E1000_READ_REG(hw, E1000_EERD) >> + E1000_NVM_RW_REG_DATA); + } + + if (ret_val) + DEBUGOUT1("NVM read error: %d\n", ret_val); + + return ret_val; +} + +/** + * e1000_write_nvm_spi - Write to EEPROM using SPI + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * Writes data to EEPROM at offset using SPI interface. + * + * If e1000_update_nvm_checksum is not called after this function , the + * EEPROM will most likely contain an invalid checksum. + **/ +s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + s32 ret_val = -E1000_ERR_NVM; + u16 widx = 0; + + DEBUGFUNC("e1000_write_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + while (widx < words) { + u8 write_opcode = NVM_WRITE_OPCODE_SPI; + + ret_val = nvm->ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) { + nvm->ops.release(hw); + return ret_val; + } + + e1000_standby_nvm(hw); + + /* Send the WRITE ENABLE command (8 bit opcode) */ + e1000_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI, + nvm->opcode_bits); + + e1000_standby_nvm(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the + * opcode + */ + if ((nvm->address_bits == 8) && (offset >= 128)) + write_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + e1000_shift_out_eec_bits(hw, write_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)((offset + widx) * 2), + nvm->address_bits); + + /* Loop to allow for up to whole page write of eeprom */ + while (widx < words) { + u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + e1000_shift_out_eec_bits(hw, word_out, 16); + widx++; + + if ((((offset + widx) * 2) % nvm->page_size) == 0) { + e1000_standby_nvm(hw); + break; + } + } + msec_delay(10); + nvm->ops.release(hw); + } + + return ret_val; +} + +/** + * e1000_read_pba_string_generic - Read device part number + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + **/ +s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, + u32 pba_num_size) +{ + s32 ret_val; + u16 nvm_data; + u16 pba_ptr; + u16 offset; + u16 length; + + DEBUGFUNC("e1000_read_pba_string_generic"); + + if ((hw->mac.type >= e1000_i210) && + !e1000_get_flash_presence_i210(hw)) { + DEBUGOUT("Flashless no PBA string\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + + if (pba_num == NULL) { + DEBUGOUT("PBA string buffer was null\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + /* if nvm_data is not ptr guard the PBA must be in legacy format which + * means pba_ptr is actually our second data word for the PBA number + * and we can decode it into an ascii string + */ + if (nvm_data != NVM_PBA_PTR_GUARD) { + DEBUGOUT("NVM PBA number is not stored as string\n"); + + /* make sure callers buffer is big enough to store the PBA */ + if (pba_num_size < E1000_PBANUM_LENGTH) { + DEBUGOUT("PBA string buffer too small\n"); + return E1000_ERR_NO_SPACE; + } + + /* extract hex string from data and pba_ptr */ + pba_num[0] = (nvm_data >> 12) & 0xF; + pba_num[1] = (nvm_data >> 8) & 0xF; + pba_num[2] = (nvm_data >> 4) & 0xF; + pba_num[3] = nvm_data & 0xF; + pba_num[4] = (pba_ptr >> 12) & 0xF; + pba_num[5] = (pba_ptr >> 8) & 0xF; + pba_num[6] = '-'; + pba_num[7] = 0; + pba_num[8] = (pba_ptr >> 4) & 0xF; + pba_num[9] = pba_ptr & 0xF; + + /* put a null character on the end of our string */ + pba_num[10] = '\0'; + + /* switch all the data but the '-' to hex char */ + for (offset = 0; offset < 10; offset++) { + if (pba_num[offset] < 0xA) + pba_num[offset] += '0'; + else if (pba_num[offset] < 0x10) + pba_num[offset] += 'A' - 0xA; + } + + return E1000_SUCCESS; + } + + ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + DEBUGOUT("NVM PBA number section invalid length\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + /* check if pba_num buffer is big enough */ + if (pba_num_size < (((u32)length * 2) - 1)) { + DEBUGOUT("PBA string buffer too small\n"); + return -E1000_ERR_NO_SPACE; + } + + /* trim pba length from start of string */ + pba_ptr++; + length--; + + for (offset = 0; offset < length; offset++) { + ret_val = hw->nvm.ops.read(hw, pba_ptr + offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + pba_num[offset * 2] = (u8)(nvm_data >> 8); + pba_num[(offset * 2) + 1] = (u8)(nvm_data & 0xFF); + } + pba_num[offset * 2] = '\0'; + + return E1000_SUCCESS; +} + +/** + * e1000_read_pba_length_generic - Read device part number length + * @hw: pointer to the HW structure + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number length from the EEPROM and + * stores the value in pba_num_size. + **/ +s32 e1000_read_pba_length_generic(struct e1000_hw *hw, u32 *pba_num_size) +{ + s32 ret_val; + u16 nvm_data; + u16 pba_ptr; + u16 length; + + DEBUGFUNC("e1000_read_pba_length_generic"); + + if (pba_num_size == NULL) { + DEBUGOUT("PBA buffer size was null\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + /* if data is not ptr guard the PBA must be in legacy format */ + if (nvm_data != NVM_PBA_PTR_GUARD) { + *pba_num_size = E1000_PBANUM_LENGTH; + return E1000_SUCCESS; + } + + ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + DEBUGOUT("NVM PBA number section invalid length\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + + /* Convert from length in u16 values to u8 chars, add 1 for NULL, + * and subtract 2 because length field is included in length. + */ + *pba_num_size = ((u32)length * 2) - 1; + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr_generic - Read device MAC address + * @hw: pointer to the HW structure + * + * Reads the device MAC address from the EEPROM and stores the value. + * Since devices with two ports use the same EEPROM, we increment the + * last bit in the MAC address for the second port. + **/ +s32 e1000_read_mac_addr_generic(struct e1000_hw *hw) +{ + u32 rar_high; + u32 rar_low; + u16 i; + + rar_high = E1000_READ_REG(hw, E1000_RAH(0)); + rar_low = E1000_READ_REG(hw, E1000_RAL(0)); + + for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++) + hw->mac.perm_addr[i] = (u8)(rar_low >> (i*8)); + + for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++) + hw->mac.perm_addr[i+4] = (u8)(rar_high >> (i*8)); + + for (i = 0; i < ETH_ADDR_LEN; i++) + hw->mac.addr[i] = hw->mac.perm_addr[i]; + + return E1000_SUCCESS; +} + +/** + * e1000_validate_nvm_checksum_generic - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_validate_nvm_checksum_generic"); + + for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + checksum += nvm_data; + } + + if (checksum != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_update_nvm_checksum_generic - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum"); + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + return ret_val; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum); + if (ret_val) + DEBUGOUT("NVM Write Error while updating checksum.\n"); + + return ret_val; +} + +/** + * e1000_reload_nvm_generic - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +static void e1000_reload_nvm_generic(struct e1000_hw *hw) +{ + u32 ctrl_ext; + + DEBUGFUNC("e1000_reload_nvm_generic"); + + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_get_fw_version - Get firmware version information + * @hw: pointer to the HW structure + * @fw_vers: pointer to output version structure + * + * unsupported/not present features return 0 in version structure + **/ +void e1000_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers) +{ + u16 eeprom_verh, eeprom_verl, etrack_test, fw_version; + u8 q, hval, rem, result; + u16 comb_verh, comb_verl, comb_offset; + + memset(fw_vers, 0, sizeof(struct e1000_fw_version)); + + /* basic eeprom version numbers, bits used vary by part and by tool + * used to create the nvm images */ + /* Check which data format we have */ + switch (hw->mac.type) { + case e1000_i211: + e1000_read_invm_version(hw, fw_vers); + return; + case e1000_82575: + case e1000_82576: + case e1000_82580: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + /* Use this format, unless EETRACK ID exists, + * then use alternate format + */ + if ((etrack_test & NVM_MAJOR_MASK) != NVM_ETRACK_VALID) { + hw->nvm.ops.read(hw, NVM_VERSION, 1, &fw_version); + fw_vers->eep_major = (fw_version & NVM_MAJOR_MASK) + >> NVM_MAJOR_SHIFT; + fw_vers->eep_minor = (fw_version & NVM_MINOR_MASK) + >> NVM_MINOR_SHIFT; + fw_vers->eep_build = (fw_version & NVM_IMAGE_ID_MASK); + goto etrack_id; + } + break; + case e1000_i210: + if (!(e1000_get_flash_presence_i210(hw))) { + e1000_read_invm_version(hw, fw_vers); + return; + } + /* fall through */ + case e1000_i350: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + /* find combo image version */ + hw->nvm.ops.read(hw, NVM_COMB_VER_PTR, 1, &comb_offset); + if ((comb_offset != 0x0) && + (comb_offset != NVM_VER_INVALID)) { + + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset + + 1), 1, &comb_verh); + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset), + 1, &comb_verl); + + /* get Option Rom version if it exists and is valid */ + if ((comb_verh && comb_verl) && + ((comb_verh != NVM_VER_INVALID) && + (comb_verl != NVM_VER_INVALID))) { + + fw_vers->or_valid = true; + fw_vers->or_major = + comb_verl >> NVM_COMB_VER_SHFT; + fw_vers->or_build = + (comb_verl << NVM_COMB_VER_SHFT) + | (comb_verh >> NVM_COMB_VER_SHFT); + fw_vers->or_patch = + comb_verh & NVM_COMB_VER_MASK; + } + } + break; + default: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + return; + } + hw->nvm.ops.read(hw, NVM_VERSION, 1, &fw_version); + fw_vers->eep_major = (fw_version & NVM_MAJOR_MASK) + >> NVM_MAJOR_SHIFT; + + /* check for old style version format in newer images*/ + if ((fw_version & NVM_NEW_DEC_MASK) == 0x0) { + eeprom_verl = (fw_version & NVM_COMB_VER_MASK); + } else { + eeprom_verl = (fw_version & NVM_MINOR_MASK) + >> NVM_MINOR_SHIFT; + } + /* Convert minor value to hex before assigning to output struct + * Val to be converted will not be higher than 99, per tool output + */ + q = eeprom_verl / NVM_HEX_CONV; + hval = q * NVM_HEX_TENS; + rem = eeprom_verl % NVM_HEX_CONV; + result = hval + rem; + fw_vers->eep_minor = result; + +etrack_id: + if ((etrack_test & NVM_MAJOR_MASK) == NVM_ETRACK_VALID) { + hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verl); + hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verh); + fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) + | eeprom_verl; + } else if ((etrack_test & NVM_ETRACK_VALID) == 0) { + hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verh); + hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verl); + fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) | + eeprom_verl; + } +} + diff --git a/drivers/staging/igb_avb/e1000_nvm.h b/drivers/staging/igb_avb/e1000_nvm.h new file mode 100644 index 000000000000..a4263113d72d --- /dev/null +++ b/drivers/staging/igb_avb/e1000_nvm.h @@ -0,0 +1,70 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_NVM_H_ +#define _E1000_NVM_H_ + +struct e1000_fw_version { + u32 etrack_id; + u16 eep_major; + u16 eep_minor; + u16 eep_build; + + u8 invm_major; + u8 invm_minor; + u8 invm_img_type; + + bool or_valid; + u16 or_major; + u16 or_build; + u16 or_patch; +}; + +void e1000_init_nvm_ops_generic(struct e1000_hw *hw); +s32 e1000_null_read_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c); +void e1000_null_nvm_generic(struct e1000_hw *hw); +s32 e1000_null_led_default(struct e1000_hw *hw, u16 *data); +s32 e1000_null_write_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c); +s32 e1000_acquire_nvm_generic(struct e1000_hw *hw); + +s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg); +s32 e1000_read_mac_addr_generic(struct e1000_hw *hw); +s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, + u32 pba_num_size); +s32 e1000_read_pba_length_generic(struct e1000_hw *hw, u32 *pba_num_size); +s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data); +s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw); +s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw); +void e1000_release_nvm_generic(struct e1000_hw *hw); +void e1000_get_fw_version(struct e1000_hw *hw, + struct e1000_fw_version *fw_vers); + +#define E1000_STM_OPCODE 0xDB00 + +#endif diff --git a/drivers/staging/igb_avb/e1000_osdep.h b/drivers/staging/igb_avb/e1000_osdep.h new file mode 100644 index 000000000000..3c6b79586cf8 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_osdep.h @@ -0,0 +1,141 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* glue for the OS independent part of e1000 + * includes register access macros + */ + +#ifndef _E1000_OSDEP_H_ +#define _E1000_OSDEP_H_ + +#include +#include +#include +#include +#include +#include "kcompat.h" + +#define usec_delay(x) udelay(x) +#define usec_delay_irq(x) udelay(x) +#ifndef msec_delay +#define msec_delay(x) do { \ + /* Don't mdelay in interrupt context! */ \ + if (in_interrupt()) \ + BUG(); \ + else \ + msleep(x); \ +} while (0) + +/* Some workarounds require millisecond delays and are run during interrupt + * context. Most notably, when establishing link, the phy may need tweaking + * but cannot process phy register reads/writes faster than millisecond + * intervals...and we establish link due to a "link status change" interrupt. + */ +#define msec_delay_irq(x) mdelay(x) + +#define E1000_READ_REG(x, y) e1000_read_reg(x, y) +#endif + +#define PCI_COMMAND_REGISTER PCI_COMMAND +#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE +#define ETH_ADDR_LEN ETH_ALEN + +#ifdef __BIG_ENDIAN +#define E1000_BIG_ENDIAN __BIG_ENDIAN +#endif + +#ifdef DEBUG +#define DEBUGOUT(S) pr_debug(S) +#define DEBUGOUT1(S, A...) pr_debug(S, ## A) +#else +#define DEBUGOUT(S) +#define DEBUGOUT1(S, A...) +#endif + +#ifdef DEBUG_FUNC +#define DEBUGFUNC(F) DEBUGOUT(F "\n") +#else +#define DEBUGFUNC(F) +#endif +#define DEBUGOUT2 DEBUGOUT1 +#define DEBUGOUT3 DEBUGOUT2 +#define DEBUGOUT7 DEBUGOUT3 + +#define E1000_REGISTER(a, reg) reg + +/* forward declaration */ +struct e1000_hw; + +/* write operations, indexed using DWORDS */ +#define E1000_WRITE_REG(hw, reg, val) \ +do { \ + u8 __iomem *hw_addr = READ_ONCE((hw)->hw_addr); \ + if (!E1000_REMOVED(hw_addr)) \ + writel((val), &hw_addr[(reg)]); \ +} while (0) + +u32 e1000_read_reg(struct e1000_hw *hw, u32 reg); + +#define E1000_WRITE_REG_ARRAY(hw, reg, idx, val) \ + E1000_WRITE_REG((hw), (reg) + ((idx) << 2), (val)) + +#define E1000_READ_REG_ARRAY(hw, reg, idx) ( \ + e1000_read_reg((hw), (reg) + ((idx) << 2))) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY +#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY + +#define E1000_WRITE_REG_ARRAY_WORD(a, reg, offset, value) ( \ + writew((value), ((a)->hw_addr + E1000_REGISTER(a, reg) + \ + ((offset) << 1)))) + +#define E1000_READ_REG_ARRAY_WORD(a, reg, offset) ( \ + readw((a)->hw_addr + E1000_REGISTER(a, reg) + ((offset) << 1))) + +#define E1000_WRITE_REG_ARRAY_BYTE(a, reg, offset, value) ( \ + writeb((value), ((a)->hw_addr + E1000_REGISTER(a, reg) + (offset)))) + +#define E1000_READ_REG_ARRAY_BYTE(a, reg, offset) ( \ + readb((a)->hw_addr + E1000_REGISTER(a, reg) + (offset))) + +#define E1000_WRITE_REG_IO(a, reg, offset) do { \ + outl(reg, ((a)->io_base)); \ + outl(offset, ((a)->io_base + 4)); \ + } while (0) + +#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, E1000_STATUS) + +#define E1000_WRITE_FLASH_REG(a, reg, value) ( \ + writel((value), ((a)->flash_address + reg))) + +#define E1000_WRITE_FLASH_REG16(a, reg, value) ( \ + writew((value), ((a)->flash_address + reg))) + +#define E1000_READ_FLASH_REG(a, reg) (readl((a)->flash_address + reg)) + +#define E1000_READ_FLASH_REG16(a, reg) (readw((a)->flash_address + reg)) + +#define E1000_REMOVED(h) unlikely(!(h)) + +#endif /* _E1000_OSDEP_H_ */ diff --git a/drivers/staging/igb_avb/e1000_phy.c b/drivers/staging/igb_avb/e1000_phy.c new file mode 100644 index 000000000000..0534e4a34af2 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_phy.c @@ -0,0 +1,3396 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static s32 e1000_wait_autoneg(struct e1000_hw *hw); +/* Cable length tables */ +static const u16 e1000_m88_cable_length_table[] = { + 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED }; +#define M88E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_m88_cable_length_table) / \ + sizeof(e1000_m88_cable_length_table[0])) + +static const u16 e1000_igp_2_cable_length_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, 0, 0, 0, 3, + 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41, 6, 10, 14, 18, 22, + 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61, 21, 26, 31, 35, 40, + 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82, 40, 45, 51, 56, 61, + 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, 60, 66, 72, 77, 82, + 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, 83, 89, 95, + 100, 105, 109, 113, 116, 119, 122, 124, 104, 109, 114, 118, 121, + 124}; +#define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_igp_2_cable_length_table) / \ + sizeof(e1000_igp_2_cable_length_table[0])) + +/** + * e1000_init_phy_ops_generic - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_phy_ops_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + DEBUGFUNC("e1000_init_phy_ops_generic"); + + /* Initialize function pointers */ + phy->ops.init_params = e1000_null_ops_generic; + phy->ops.acquire = e1000_null_ops_generic; + phy->ops.check_polarity = e1000_null_ops_generic; + phy->ops.check_reset_block = e1000_null_ops_generic; + phy->ops.commit = e1000_null_ops_generic; + phy->ops.force_speed_duplex = e1000_null_ops_generic; + phy->ops.get_cfg_done = e1000_null_ops_generic; + phy->ops.get_cable_length = e1000_null_ops_generic; + phy->ops.get_info = e1000_null_ops_generic; + phy->ops.set_page = e1000_null_set_page; + phy->ops.read_reg = e1000_null_read_reg; + phy->ops.read_reg_locked = e1000_null_read_reg; + phy->ops.read_reg_page = e1000_null_read_reg; + phy->ops.release = e1000_null_phy_generic; + phy->ops.reset = e1000_null_ops_generic; + phy->ops.set_d0_lplu_state = e1000_null_lplu_state; + phy->ops.set_d3_lplu_state = e1000_null_lplu_state; + phy->ops.write_reg = e1000_null_write_reg; + phy->ops.write_reg_locked = e1000_null_write_reg; + phy->ops.write_reg_page = e1000_null_write_reg; + phy->ops.power_up = e1000_null_phy_generic; + phy->ops.power_down = e1000_null_phy_generic; + phy->ops.read_i2c_byte = e1000_read_i2c_byte_null; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_null; +} + +/** + * e1000_null_set_page - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_set_page(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_null_set_page"); + return E1000_SUCCESS; +} + +/** + * e1000_null_read_reg - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_read_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_null_read_reg"); + return E1000_SUCCESS; +} + +/** + * e1000_null_phy_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_phy_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_phy_generic"); + return; +} + +/** + * e1000_null_lplu_state - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_lplu_state(struct e1000_hw E1000_UNUSEDARG *hw, + bool E1000_UNUSEDARG active) +{ + DEBUGFUNC("e1000_null_lplu_state"); + return E1000_SUCCESS; +} + +/** + * e1000_null_write_reg - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_write_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_null_write_reg"); + return E1000_SUCCESS; +} + +/** + * e1000_read_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value read + * + **/ +s32 e1000_read_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_read_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** + * e1000_write_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value to write + * + **/ +s32 e1000_write_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_write_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** + * e1000_check_reset_block_generic - Check if PHY reset is blocked + * @hw: pointer to the HW structure + * + * Read the PHY management control register and check whether a PHY reset + * is blocked. If a reset is not blocked return E1000_SUCCESS, otherwise + * return E1000_BLK_PHY_RESET (12). + **/ +s32 e1000_check_reset_block_generic(struct e1000_hw *hw) +{ + u32 manc; + + DEBUGFUNC("e1000_check_reset_block"); + + manc = E1000_READ_REG(hw, E1000_MANC); + + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +/** + * e1000_get_phy_id - Retrieve the PHY ID and revision + * @hw: pointer to the HW structure + * + * Reads the PHY registers and stores the PHY ID and possibly the PHY + * revision in the hardware structure. + **/ +s32 e1000_get_phy_id(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + + DEBUGFUNC("e1000_get_phy_id"); + + if (!phy->ops.read_reg) + return E1000_SUCCESS; + + ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); + if (ret_val) + return ret_val; + + phy->id = (u32)(phy_id << 16); + usec_delay(20); + ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); + if (ret_val) + return ret_val; + + phy->id |= (u32)(phy_id & PHY_REVISION_MASK); + phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_reset_dsp_generic - Reset PHY DSP + * @hw: pointer to the HW structure + * + * Reset the digital signal processor. + **/ +s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_phy_reset_dsp_generic"); + + if (!hw->phy.ops.write_reg) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1); + if (ret_val) + return ret_val; + + return hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0); +} + +/** + * e1000_read_phy_reg_mdic - Read MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control register in the PHY at offset and stores the + * information read to data. + **/ +s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + + DEBUGFUNC("e1000_read_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed + * Increasing the time out as testing showed failures with + * the lower time out + */ + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { + usec_delay_irq(50); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { + DEBUGOUT2("MDI Read offset error - requested %d, returned %d\n", + offset, + (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + return -E1000_ERR_PHY; + } + *data = (u16) mdic; + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_mdic - Write MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + + DEBUGFUNC("e1000_write_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = (((u32)data) | + (offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed + * Increasing the time out as testing showed failures with + * the lower time out + */ + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { + usec_delay_irq(50); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { + DEBUGOUT2("MDI Write offset error - requested %d, returned %d\n", + offset, + (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg_i2c - Read PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the i2c interface and stores the + * retrieved information in data. + **/ +s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + + DEBUGFUNC("e1000_read_phy_reg_i2c"); + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + (E1000_I2CCMD_OPCODE_READ)); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + /* Need to byte-swap the 16-bit value. */ + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_i2c - Write PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the i2c interface. + **/ +s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + u16 phy_data_swapped; + + DEBUGFUNC("e1000_write_phy_reg_i2c"); + + /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/ + if ((hw->phy.addr == 0) || (hw->phy.addr > 7)) { + DEBUGOUT1("PHY I2C Address %d is out of range.\n", + hw->phy.addr); + return -E1000_ERR_CONFIG; + } + + /* Swap the data bytes for the I2C interface */ + phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | + phy_data_swapped); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_sfp_data_byte - Reads SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to be read + * @data: read data buffer pointer + * + * Reads one byte from SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 e1000_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + DEBUGFUNC("e1000_read_sfp_data_byte"); + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + DEBUGOUT("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing with the + * EEPROM to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + data_local = E1000_READ_REG(hw, E1000_I2CCMD); + if (data_local & E1000_I2CCMD_READY) + break; + } + if (!(data_local & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (data_local & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + *data = (u8) data_local & 0xFF; + + return E1000_SUCCESS; +} + +/** + * e1000_write_sfp_data_byte - Writes SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to write to + * @data: data to write + * + * Writes one byte to SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + DEBUGFUNC("e1000_write_sfp_data_byte"); + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + DEBUGOUT("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + /* The programming interface is 16 bits wide + * so we need to read the whole word first + * then update appropriate byte lane and write + * the updated word back. + */ + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing + * with an EEPROM to write the data given. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + /* Set a command to read single word */ + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + /* Poll the ready bit to see if lastly + * launched I2C operation completed + */ + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) { + /* Check if this is READ or WRITE phase */ + if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) == + E1000_I2CCMD_OPCODE_READ) { + /* Write the selected byte + * lane and update whole word + */ + data_local = i2ccmd & 0xFF00; + data_local |= data; + i2ccmd = ((offset << + E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | data_local); + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + } else { + break; + } + } + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg_m88 - Read m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and storing the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_m88"); + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); + + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_write_phy_reg_m88 - Write m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_m88"); + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); + + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_set_page_igp - Set page as on IGP-like PHY(s) + * @hw: pointer to the HW structure + * @page: page to set (shifted left when necessary) + * + * Sets PHY page required for PHY register access. Assumes semaphore is + * already acquired. Note, this function sets phy.addr to 1 so the caller + * must set it appropriately (if necessary) after this function returns. + **/ +s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page) +{ + DEBUGFUNC("e1000_set_page_igp"); + + DEBUGOUT1("Setting page 0x%x\n", page); + + hw->phy.addr = 1; + + return e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, page); +} + +/** + * __e1000_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and stores the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +static s32 __e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("__e1000_read_phy_reg_igp"); + + if (!locked) { + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + if (offset > MAX_PHY_MULTI_PAGE_REG) + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (!ret_val) + ret_val = e1000_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + if (!locked) + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset and stores the + * retrieved information in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000_read_phy_reg_igp_locked - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset and stores the retrieved information + * in data. Assumes semaphore already acquired. + **/ +s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_igp(hw, offset, data, true); +} + +/** + * e1000_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +static s32 __e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_phy_reg_igp"); + + if (!locked) { + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + if (offset > MAX_PHY_MULTI_PAGE_REG) + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (!ret_val) + ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & + offset, + data); + if (!locked) + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000_write_phy_reg_igp_locked - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset. + * Assumes semaphore already acquired. + **/ +s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_igp(hw, offset, data, true); +} + +/** + * __e1000_read_kmrn_reg - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary. Then reads the PHY register at offset + * using the kumeran interface. The information retrieved is stored in data. + * Release any acquired semaphores before exiting. + **/ +static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) +{ + u32 kmrnctrlsta; + + DEBUGFUNC("__e1000_read_kmrn_reg"); + + if (!locked) { + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + E1000_WRITE_FLUSH(hw); + + usec_delay(2); + + kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA); + *data = (u16)kmrnctrlsta; + + if (!locked) + hw->phy.ops.release(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_kmrn_reg_generic - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset using the + * kumeran interface. The information retrieved is stored in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000_read_kmrn_reg_locked - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the kumeran interface. The + * information retrieved is stored in data. + * Assumes semaphore already acquired. + **/ +s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, true); +} + +/** + * __e1000_write_kmrn_reg - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary. Then write the data to PHY register + * at the offset using the kumeran interface. Release any acquired semaphores + * before exiting. + **/ +static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) +{ + u32 kmrnctrlsta; + + DEBUGFUNC("e1000_write_kmrn_reg_generic"); + + if (!locked) { + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + E1000_WRITE_FLUSH(hw); + + usec_delay(2); + + if (!locked) + hw->phy.ops.release(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_write_kmrn_reg_generic - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to the PHY register at the offset + * using the kumeran interface. Release the acquired semaphore before exiting. + **/ +s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000_write_kmrn_reg_locked - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Write the data to PHY register at the offset using the kumeran interface. + * Assumes semaphore already acquired. + **/ +s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, true); +} + +/** + * e1000_set_master_slave_mode - Setup PHY for Master/slave mode + * @hw: pointer to the HW structure + * + * Sets up Master/slave mode + **/ +static s32 e1000_set_master_slave_mode(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + /* Resolve Master/Slave mode */ + ret_val = hw->phy.ops.read_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->phy.original_ms_type = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : e1000_ms_auto; + + switch (hw->phy.ms_type) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + /* fall-through */ + default: + break; + } + + return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data); +} + +/** + * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link + * @hw: pointer to the HW structure + * + * Sets up Carrier-sense on Transmit and downshift values. + **/ +s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_82577"); + + if (hw->phy.reset_disable) + return E1000_SUCCESS; + + if (hw->phy.type == e1000_phy_82580) { + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + return ret_val; + } + } + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = hw->phy.ops.read_reg(hw, I82577_CFG_REG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= I82577_CFG_ASSERT_CRS_ON_TX; + + /* Enable downshift */ + phy_data |= I82577_CFG_ENABLE_DOWNSHIFT; + + ret_val = hw->phy.ops.write_reg(hw, I82577_CFG_REG, phy_data); + if (ret_val) + return ret_val; + + /* Set MDI/MDIX mode */ + ret_val = hw->phy.ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~I82577_PHY_CTRL2_MDIX_CFG_MASK; + /* Options: + * 0 - Auto (default) + * 1 - MDI mode + * 2 - MDI-X mode + */ + switch (hw->phy.mdix) { + case 1: + break; + case 2: + phy_data |= I82577_PHY_CTRL2_MANUAL_MDIX; + break; + case 0: + default: + phy_data |= I82577_PHY_CTRL2_AUTO_MDI_MDIX; + break; + } + ret_val = hw->phy.ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data); + if (ret_val) + return ret_val; + + return e1000_set_master_slave_mode(hw); +} + +/** + * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up MDI/MDI-X and polarity for m88 PHY's. If necessary, transmit clock + * and downshift values are set also. + **/ +s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_m88"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (phy->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (phy->disable_polarity_correction) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (phy->revision < E1000_REVISION_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((phy->revision == E1000_REVISION_2) && + (phy->id == M88E1111_I_PHY_ID)) { + /* 82573L PHY - set the downshift counter to 5x. */ + phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK; + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + } + ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + /* Commit the changes. */ + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_copper_link_setup_m88_gen2 - Setup m88 PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up MDI/MDI-X and polarity for i347-AT4, m88e1322 and m88e1112 PHY's. + * Also enables and sets the downshift parameters. + **/ +s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_m88_gen2"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (phy->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + /* M88E1112 does not support this mode) */ + if (phy->id != M88E1112_E_PHY_ID) { + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + } + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (phy->disable_polarity_correction) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + + /* Enable downshift and setting it to X6 */ + if (phy->id == M88E1543_E_PHY_ID) { + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_ENABLE; + ret_val = + phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + } + + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_MASK; + phy_data |= I347AT4_PSCR_DOWNSHIFT_6X; + phy_data |= I347AT4_PSCR_DOWNSHIFT_ENABLE; + + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Commit the changes. */ + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + ret_val = e1000_set_master_slave_mode(hw); + if (ret_val) + return ret_val; + + return E1000_SUCCESS; +} + +/** + * e1000_copper_link_setup_igp - Setup igp PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up LPLU, MDI/MDI-X, polarity, Smartspeed and Master/Slave config for + * igp PHY's. + **/ +s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_copper_link_setup_igp"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + return ret_val; + } + + /* Wait 100ms for MAC to configure PHY from NVM settings, to avoid + * timeout issues when LFS is enabled. + */ + msec_delay(100); + + /* disable lplu d0 during driver init */ + if (hw->phy.ops.set_d0_lplu_state) { + ret_val = hw->phy.ops.set_d0_lplu_state(hw, false); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + } + /* Configure mdi-mdix settings */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (phy->mdix) { + case 1: + data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->mac.autoneg) { + /* when autonegotiation advertisement is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. + */ + if (phy->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + + /* Set auto Master/Slave resolution process */ + ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data); + if (ret_val) + return ret_val; + + data &= ~CR_1000T_MS_ENABLE; + ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_set_master_slave_mode(hw); + } + + return ret_val; +} + +/** + * e1000_phy_setup_autoneg - Configure PHY for auto-negotiation + * @hw: pointer to the HW structure + * + * Reads the MII auto-neg advertisement register and/or the 1000T control + * register and if the PHY is already setup for auto-negotiation, then + * return successful. Otherwise, setup advertisement and flow control to + * the appropriate values for the wanted auto-negotiation. + **/ +static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg = 0; + + DEBUGFUNC("e1000_phy_setup_autoneg"); + + phy->autoneg_advertised &= phy->autoneg_mask; + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, + &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | + NWAY_AR_100TX_HD_CAPS | + NWAY_AR_10T_FD_CAPS | + NWAY_AR_10T_HD_CAPS); + mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); + + DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (phy->autoneg_advertised & ADVERTISE_1000_HALF) + DEBUGOUT("Advertise 1000mb Half duplex request denied!\n"); + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- + * negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc.current_mode) { + case e1000_fc_none: + /* Flow control (Rx & Tx) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: + /* Rx Flow control is enabled, and Tx Flow control is + * disabled, by a software over-ride. + * + * Since there really isn't a way to advertise that we are + * capable of Rx Pause ONLY, we will advertise that we + * support both symmetric and asymmetric Rx PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + * hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: + /* Flow control (both Rx and Tx) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) + ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + + return ret_val; +} + +/** + * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link + * @hw: pointer to the HW structure + * + * Performs initial bounds checking on autoneg advertisement parameter, then + * configure to advertise the full capability. Setup the PHY to autoneg + * and restart the negotiation process between the link partner. If + * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. + **/ +static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* Perform some bounds checking on the autoneg advertisement + * parameter. + */ + phy->autoneg_advertised &= phy->autoneg_mask; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (!phy->autoneg_advertised) + phy->autoneg_advertised = phy->autoneg_mask; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (phy->autoneg_wait_to_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->mac.get_link_status = true; + + return ret_val; +} + +/** + * e1000_setup_copper_link_generic - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). + **/ +s32 e1000_setup_copper_link_generic(struct e1000_hw *hw) +{ + s32 ret_val; + bool link; + + DEBUGFUNC("e1000_setup_copper_link_generic"); + + if (hw->mac.autoneg) { + /* Setup autoneg and flow control advertisement and perform + * autonegotiation. + */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + } else { + /* PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. + */ + DEBUGOUT("Forcing Speed and Duplex\n"); + ret_val = hw->phy.ops.force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + return ret_val; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = e1000_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, + &link); + if (ret_val) + return ret_val; + + if (link) { + DEBUGOUT("Valid link established!!!\n"); + hw->mac.ops.config_collision_dist(hw); + ret_val = e1000_config_fc_after_link_up_generic(hw); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Waits for link and returns + * successful if link up is successful, else -E1000_ERR_PHY (-2). + **/ +s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_igp"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + DEBUGOUT1("IGP PSCR: %X\n", phy_data); + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on IGP phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + } + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_m88 - Force speed/duplex for m88 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Resets the PHY to commit the + * changes. If time expires while waiting for link up, we reset the DSP. + * After reset, TX_CLK and CRS on Tx must be set. Return successful upon + * successful completion, else return corresponding error code. + **/ +s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_m88"); + + /* I210 and I211 devices support Auto-Crossover in forced operation. */ + if (phy->type != e1000_phy_i210) { + /* Clear Auto-Crossover to force MDI manually. M88E1000 + * requires MDI forced whenever speed and duplex are forced. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + /* Reset the phy to commit changes. */ + ret_val = hw->phy.ops.commit(hw); + if (ret_val) + return ret_val; + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) { + bool reset_dsp = true; + + switch (hw->phy.id) { + case I347AT4_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I210_I_PHY_ID: + reset_dsp = false; + break; + default: + if (hw->phy.type != e1000_phy_m88) + reset_dsp = false; + break; + } + + if (!reset_dsp) { + DEBUGOUT("Link taking longer than expected.\n"); + } else { + /* We didn't get link. + * Reset the DSP and cross our fingers. + */ + ret_val = phy->ops.write_reg(hw, + M88E1000_PHY_PAGE_SELECT, + 0x001d); + if (ret_val) + return ret_val; + ret_val = e1000_phy_reset_dsp_generic(hw); + if (ret_val) + return ret_val; + } + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + } + + if (hw->phy.type != e1000_phy_m88) + return E1000_SUCCESS; + + if (hw->phy.id == I347AT4_E_PHY_ID || + hw->phy.id == M88E1340M_E_PHY_ID || + hw->phy.id == M88E1112_E_PHY_ID) + return E1000_SUCCESS; + if (hw->phy.id == I210_I_PHY_ID) + return E1000_SUCCESS; + if ((hw->phy.id == M88E1543_E_PHY_ID) || + (hw->phy.id == M88E1512_E_PHY_ID)) + return E1000_SUCCESS; + ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Resetting the phy means we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock from + * the reset value of 2.5MHz. + */ + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* In addition, we must re-enable CRS on Tx for both half and full + * duplex. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex + * @hw: pointer to the HW structure + * + * Forces the speed and duplex settings of the PHY. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_ife"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data); + if (ret_val) + return ret_val; + + /* Disable MDI-X support for 10/100 */ + ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + return ret_val; + + data &= ~IFE_PMC_AUTO_MDIX; + data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data); + if (ret_val) + return ret_val; + + DEBUGOUT1("IFE PMC: %X\n", data); + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex + * @hw: pointer to the HW structure + * @phy_ctrl: pointer to current value of PHY_CONTROL + * + * Forces speed and duplex on the PHY by doing the following: disable flow + * control, force speed/duplex on the MAC, disable auto speed detection, + * disable auto-negotiation, configure duplex, configure speed, configure + * the collision distance, write configuration to CTRL register. The + * caller must write to the PHY_CONTROL register for these settings to + * take affect. + **/ +void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 ctrl; + + DEBUGFUNC("e1000_phy_force_speed_duplex_setup"); + + /* Turn off flow control when forcing speed/duplex */ + hw->fc.current_mode = e1000_fc_none; + + /* Force speed/duplex on the mac */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~E1000_CTRL_SPD_SEL; + + /* Disable Auto Speed Detection */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Disable autoneg on the phy */ + *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; + + /* Forcing Full or Half Duplex? */ + if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) { + ctrl &= ~E1000_CTRL_FD; + *phy_ctrl &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } else { + ctrl |= E1000_CTRL_FD; + *phy_ctrl |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } + + /* Forcing 10mb or 100mb? */ + if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) { + ctrl |= E1000_CTRL_SPD_100; + *phy_ctrl |= MII_CR_SPEED_100; + *phy_ctrl &= ~MII_CR_SPEED_1000; + DEBUGOUT("Forcing 100mb\n"); + } else { + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb\n"); + } + + hw->mac.ops.config_collision_dist(hw); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); +} + +/** + * e1000_set_d3_lplu_state_generic - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + return ret_val; + + if (!active) { + data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= IGP02E1000_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + data); + } + + return ret_val; +} + +/** + * e1000_check_downshift_generic - Checks whether a downshift in speed occurred + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns 1 + * + * A downshift is detected by querying the PHY link health. + **/ +s32 e1000_check_downshift_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_downshift_generic"); + + switch (phy->type) { + case e1000_phy_i210: + case e1000_phy_m88: + case e1000_phy_gg82563: + offset = M88E1000_PHY_SPEC_STATUS; + mask = M88E1000_PSSR_DOWNSHIFT; + break; + case e1000_phy_igp_2: + case e1000_phy_igp_3: + offset = IGP01E1000_PHY_LINK_HEALTH; + mask = IGP01E1000_PLHR_SS_DOWNGRADE; + break; + default: + /* speed downshift not supported */ + phy->speed_downgraded = false; + return E1000_SUCCESS; + } + + ret_val = phy->ops.read_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->speed_downgraded = !!(phy_data & mask); + + return ret_val; +} + +/** + * e1000_check_polarity_m88 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +s32 e1000_check_polarity_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_check_polarity_m88"); + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &data); + + if (!ret_val) + phy->cable_polarity = ((data & M88E1000_PSSR_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_check_polarity_igp - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY port status register, and the + * current speed (since there is no polarity at 100Mbps). + **/ +s32 e1000_check_polarity_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_igp"); + + /* Polarity is determined based on the speed of + * our connection. + */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + return ret_val; + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + offset = IGP01E1000_PHY_PCS_INIT_REG; + mask = IGP01E1000_PHY_POLARITY_MASK; + } else { + /* This really only applies to 10Mbps since + * there is no polarity for 100Mbps (always 0). + */ + offset = IGP01E1000_PHY_PORT_STATUS; + mask = IGP01E1000_PSSR_POLARITY_REVERSED; + } + + ret_val = phy->ops.read_reg(hw, offset, &data); + + if (!ret_val) + phy->cable_polarity = ((data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_check_polarity_ife - Check cable polarity for IFE PHY + * @hw: pointer to the HW structure + * + * Polarity is determined on the polarity reversal feature being enabled. + **/ +s32 e1000_check_polarity_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_ife"); + + /* Polarity is determined based on the reversal feature being enabled. + */ + if (phy->polarity_correction) { + offset = IFE_PHY_EXTENDED_STATUS_CONTROL; + mask = IFE_PESC_POLARITY_REVERSED; + } else { + offset = IFE_PHY_SPECIAL_CONTROL; + mask = IFE_PSC_FORCE_POLARITY; + } + + ret_val = phy->ops.read_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->cable_polarity = ((phy_data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_wait_autoneg - Wait for auto-neg completion + * @hw: pointer to the HW structure + * + * Waits for auto-negotiation to complete or for the auto-negotiation time + * limit to expire, which ever happens first. + **/ +static s32 e1000_wait_autoneg(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_wait_autoneg"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ + for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_AUTONEG_COMPLETE) + break; + msec_delay(100); + } + + /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation + * has completed. + */ + return ret_val; +} + +/** + * e1000_phy_has_link_generic - Polls PHY for link + * @hw: pointer to the HW structure + * @iterations: number of times to poll for link + * @usec_interval: delay between polling attempts + * @success: pointer to whether polling was successful or not + * + * Polls the PHY status register for link, 'iterations' number of times. + **/ +s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, bool *success) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_phy_has_link_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + for (i = 0; i < iterations; i++) { + /* Some PHYs require the PHY_STATUS register to be read + * twice due to the link bit being sticky. No harm doing + * it across the board. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) { + /* If the first read fails, another entity may have + * ownership of the resources, wait and try again to + * see if they have relinquished the resources yet. + */ + if (usec_interval >= 1000) + msec_delay(usec_interval/1000); + else + usec_delay(usec_interval); + } + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_LINK_STATUS) + break; + if (usec_interval >= 1000) + msec_delay(usec_interval/1000); + else + usec_delay(usec_interval); + } + + *success = (i < iterations); + + return ret_val; +} + +/** + * e1000_get_cable_length_m88 - Determine cable length for m88 PHY + * @hw: pointer to the HW structure + * + * Reads the PHY specific status register to retrieve the cable length + * information. The cable length is determined by averaging the minimum and + * maximum values to get the "average" cable length. The m88 PHY has four + * possible cable length values, which are: + * Register Value Cable Length + * 0 < 50 meters + * 1 50 - 80 meters + * 2 80 - 110 meters + * 3 110 - 140 meters + * 4 > 140 meters + **/ +s32 e1000_get_cable_length_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, index; + + DEBUGFUNC("e1000_get_cable_length_m88"); + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + index = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; + + phy->min_cable_length = e1000_m88_cable_length_table[index]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + + return E1000_SUCCESS; +} + +s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, phy_data2, is_cm; + u16 index, default_page; + + DEBUGFUNC("e1000_get_cable_length_m88_gen2"); + + switch (hw->phy.id) { + case I210_I_PHY_ID: + /* Get cable length from PHY Cable Diagnostics Control Reg */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + (I347AT4_PCDL + phy->addr), + &phy_data); + if (ret_val) + return ret_val; + + /* Check if the unit of cable length is meters or cm */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + I347AT4_PCDC, &phy_data2); + if (ret_val) + return ret_val; + + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + + /* Populate the phy structure with cable length in meters */ + phy->min_cable_length = phy_data / (is_cm ? 100 : 1); + phy->max_cable_length = phy_data / (is_cm ? 100 : 1); + phy->cable_length = phy_data / (is_cm ? 100 : 1); + break; + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case I347AT4_E_PHY_ID: + /* Remember the original page select and set it to 7 */ + ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, + &default_page); + if (ret_val) + return ret_val; + + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07); + if (ret_val) + return ret_val; + + /* Get cable length from PHY Cable Diagnostics Control Reg */ + ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr), + &phy_data); + if (ret_val) + return ret_val; + + /* Check if the unit of cable length is meters or cm */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2); + if (ret_val) + return ret_val; + + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + + /* Populate the phy structure with cable length in meters */ + phy->min_cable_length = phy_data / (is_cm ? 100 : 1); + phy->max_cable_length = phy_data / (is_cm ? 100 : 1); + phy->cable_length = phy_data / (is_cm ? 100 : 1); + + /* Reset the page select to its original value */ + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, + default_page); + if (ret_val) + return ret_val; + break; + + case M88E1112_E_PHY_ID: + /* Remember the original page select and set it to 5 */ + ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, + &default_page); + if (ret_val) + return ret_val; + + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x05); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, M88E1112_VCT_DSP_DISTANCE, + &phy_data); + if (ret_val) + return ret_val; + + index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; + + phy->min_cable_length = e1000_m88_cable_length_table[index]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; + + phy->cable_length = (phy->min_cable_length + + phy->max_cable_length) / 2; + + /* Reset the page select to its original value */ + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, + default_page); + if (ret_val) + return ret_val; + + break; + default: + return -E1000_ERR_PHY; + } + + return ret_val; +} + +/** + * e1000_get_cable_length_igp_2 - Determine cable length for igp2 PHY + * @hw: pointer to the HW structure + * + * The automatic gain control (agc) normalizes the amplitude of the + * received signal, adjusting for the attenuation produced by the + * cable. By reading the AGC registers, which represent the + * combination of coarse and fine gain value, the value can be put + * into a lookup table to obtain the approximate cable length + * for each channel. + **/ +s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, i, agc_value = 0; + u16 cur_agc_index, max_agc_index = 0; + u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1; + static const u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = { + IGP02E1000_PHY_AGC_A, + IGP02E1000_PHY_AGC_B, + IGP02E1000_PHY_AGC_C, + IGP02E1000_PHY_AGC_D + }; + + DEBUGFUNC("e1000_get_cable_length_igp_2"); + + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { + ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + /* Getting bits 15:9, which represent the combination of + * coarse and fine gain values. The result is a number + * that can be put into the lookup table to obtain the + * approximate cable length. + */ + cur_agc_index = ((phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK); + + /* Array index bound check. */ + if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) || + (cur_agc_index == 0)) + return -E1000_ERR_PHY; + + /* Remove min & max AGC values from calculation. */ + if (e1000_igp_2_cable_length_table[min_agc_index] > + e1000_igp_2_cable_length_table[cur_agc_index]) + min_agc_index = cur_agc_index; + if (e1000_igp_2_cable_length_table[max_agc_index] < + e1000_igp_2_cable_length_table[cur_agc_index]) + max_agc_index = cur_agc_index; + + agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; + } + + agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + + e1000_igp_2_cable_length_table[max_agc_index]); + agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); + + /* Calculate cable length with the error range of +/- 10 meters. */ + phy->min_cable_length = (((agc_value - IGP02E1000_AGC_RANGE) > 0) ? + (agc_value - IGP02E1000_AGC_RANGE) : 0); + phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_info_m88 - Retrieve PHY information + * @hw: pointer to the HW structure + * + * Valid for only copper links. Read the PHY status register (sticky read) + * to verify that link is up. Read the PHY special control register to + * determine the polarity and 10base-T extended distance. Read the PHY + * special status register to determine MDI/MDIx and current speed. If + * speed is 1000, then determine cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_m88"); + + if (phy->media_type != e1000_media_type_copper) { + DEBUGOUT("Phy info is only valid for copper media\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy->polarity_correction = !!(phy_data & + M88E1000_PSCR_POLARITY_REVERSAL); + + ret_val = e1000_check_polarity_m88(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(phy_data & M88E1000_PSSR_MDIX); + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + ret_val = hw->phy.ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + /* Set values to "undefined" */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return ret_val; +} + +/** + * e1000_get_phy_info_igp - Retrieve igp PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_igp"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + phy->polarity_correction = true; + + ret_val = e1000_check_polarity_igp(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & IGP01E1000_PSSR_MDIX); + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + ret_val = phy->ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + return ret_val; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return ret_val; +} + +/** + * e1000_get_phy_info_ife - Retrieves various IFE PHY states + * @hw: pointer to the HW structure + * + * Populates "phy" structure with various feature states. + **/ +s32 e1000_get_phy_info_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_ife"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.read_reg(hw, IFE_PHY_SPECIAL_CONTROL, &data); + if (ret_val) + return ret_val; + phy->polarity_correction = !(data & IFE_PSC_AUTO_POLARITY_DISABLE); + + if (phy->polarity_correction) { + ret_val = e1000_check_polarity_ife(hw); + if (ret_val) + return ret_val; + } else { + /* Polarity is forced */ + phy->cable_polarity = ((data & IFE_PSC_FORCE_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + } + + ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & IFE_PMC_MDIX_STATUS); + + /* The following parameters are undefined for 10/100 operation. */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + + return E1000_SUCCESS; +} + +/** + * e1000_phy_sw_reset_generic - PHY software reset + * @hw: pointer to the HW structure + * + * Does a software reset of the PHY by reading the PHY control register and + * setting/write the control register reset bit to the PHY. + **/ +s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_phy_sw_reset_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= MII_CR_RESET; + ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + usec_delay(1); + + return ret_val; +} + +/** + * e1000_phy_hw_reset_generic - PHY hardware reset + * @hw: pointer to the HW structure + * + * Verify the reset block is not blocking us from resetting. Acquire + * semaphore (if necessary) and read/set/write the device control reset + * bit in the PHY. Wait the appropriate delay time for the device to + * reset and release the semaphore (if necessary). + **/ +s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u32 ctrl; + + DEBUGFUNC("e1000_phy_hw_reset_generic"); + + if (phy->ops.check_reset_block) { + ret_val = phy->ops.check_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + } + + ret_val = phy->ops.acquire(hw); + if (ret_val) + return ret_val; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + usec_delay(phy->reset_delay_us); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + usec_delay(150); + + phy->ops.release(hw); + + return phy->ops.get_cfg_done(hw); +} + +/** + * e1000_get_cfg_done_generic - Generic configuration done + * @hw: pointer to the HW structure + * + * Generic function to wait 10 milli-seconds for configuration to complete + * and return success. + **/ +s32 e1000_get_cfg_done_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_get_cfg_done_generic"); + + msec_delay_irq(10); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_init_script_igp3 - Inits the IGP3 PHY + * @hw: pointer to the HW structure + * + * Initializes a Intel Gigabit PHY3 when an EEPROM is not present. + **/ +s32 e1000_phy_init_script_igp3(struct e1000_hw *hw) +{ + DEBUGOUT("Running IGP 3 PHY init script\n"); + + /* PHY init IGP 3 */ + /* Enable rise/fall, 10-mode work in class-A */ + hw->phy.ops.write_reg(hw, 0x2F5B, 0x9018); + /* Remove all caps from Replica path filter */ + hw->phy.ops.write_reg(hw, 0x2F52, 0x0000); + /* Bias trimming for ADC, AFE and Driver (Default) */ + hw->phy.ops.write_reg(hw, 0x2FB1, 0x8B24); + /* Increase Hybrid poly bias */ + hw->phy.ops.write_reg(hw, 0x2FB2, 0xF8F0); + /* Add 4% to Tx amplitude in Gig mode */ + hw->phy.ops.write_reg(hw, 0x2010, 0x10B0); + /* Disable trimming (TTT) */ + hw->phy.ops.write_reg(hw, 0x2011, 0x0000); + /* Poly DC correction to 94.6% + 2% for all channels */ + hw->phy.ops.write_reg(hw, 0x20DD, 0x249A); + /* ABS DC correction to 95.9% */ + hw->phy.ops.write_reg(hw, 0x20DE, 0x00D3); + /* BG temp curve trim */ + hw->phy.ops.write_reg(hw, 0x28B4, 0x04CE); + /* Increasing ADC OPAMP stage 1 currents to max */ + hw->phy.ops.write_reg(hw, 0x2F70, 0x29E4); + /* Force 1000 ( required for enabling PHY regs configuration) */ + hw->phy.ops.write_reg(hw, 0x0000, 0x0140); + /* Set upd_freq to 6 */ + hw->phy.ops.write_reg(hw, 0x1F30, 0x1606); + /* Disable NPDFE */ + hw->phy.ops.write_reg(hw, 0x1F31, 0xB814); + /* Disable adaptive fixed FFE (Default) */ + hw->phy.ops.write_reg(hw, 0x1F35, 0x002A); + /* Enable FFE hysteresis */ + hw->phy.ops.write_reg(hw, 0x1F3E, 0x0067); + /* Fixed FFE for short cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F54, 0x0065); + /* Fixed FFE for medium cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F55, 0x002A); + /* Fixed FFE for long cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F56, 0x002A); + /* Enable Adaptive Clip Threshold */ + hw->phy.ops.write_reg(hw, 0x1F72, 0x3FB0); + /* AHT reset limit to 1 */ + hw->phy.ops.write_reg(hw, 0x1F76, 0xC0FF); + /* Set AHT master delay to 127 msec */ + hw->phy.ops.write_reg(hw, 0x1F77, 0x1DEC); + /* Set scan bits for AHT */ + hw->phy.ops.write_reg(hw, 0x1F78, 0xF9EF); + /* Set AHT Preset bits */ + hw->phy.ops.write_reg(hw, 0x1F79, 0x0210); + /* Change integ_factor of channel A to 3 */ + hw->phy.ops.write_reg(hw, 0x1895, 0x0003); + /* Change prop_factor of channels BCD to 8 */ + hw->phy.ops.write_reg(hw, 0x1796, 0x0008); + /* Change cg_icount + enable integbp for channels BCD */ + hw->phy.ops.write_reg(hw, 0x1798, 0xD008); + /* Change cg_icount + enable integbp + change prop_factor_master + * to 8 for channel A + */ + hw->phy.ops.write_reg(hw, 0x1898, 0xD918); + /* Disable AHT in Slave mode on channel A */ + hw->phy.ops.write_reg(hw, 0x187A, 0x0800); + /* Enable LPLU and disable AN to 1000 in non-D0a states, + * Enable SPD+B2B + */ + hw->phy.ops.write_reg(hw, 0x0019, 0x008D); + /* Enable restart AN on an1000_dis change */ + hw->phy.ops.write_reg(hw, 0x001B, 0x2080); + /* Enable wh_fifo read clock in 10/100 modes */ + hw->phy.ops.write_reg(hw, 0x0014, 0x0045); + /* Restart AN, Speed selection is 1000 */ + hw->phy.ops.write_reg(hw, 0x0000, 0x1340); + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_type_from_id - Get PHY type from id + * @phy_id: phy_id read from the phy + * + * Returns the phy type from the id. + **/ +enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id) +{ + enum e1000_phy_type phy_type = e1000_phy_unknown; + + switch (phy_id) { + case M88E1000_I_PHY_ID: + case M88E1000_E_PHY_ID: + case M88E1111_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ + phy_type = e1000_phy_igp_2; + break; + case GG82563_E_PHY_ID: + phy_type = e1000_phy_gg82563; + break; + case IGP03E1000_E_PHY_ID: + phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + phy_type = e1000_phy_ife; + break; + case I82580_I_PHY_ID: + phy_type = e1000_phy_82580; + break; + case I210_I_PHY_ID: + phy_type = e1000_phy_i210; + break; + default: + phy_type = e1000_phy_unknown; + break; + } + return phy_type; +} + +/** + * e1000_determine_phy_address - Determines PHY address. + * @hw: pointer to the HW structure + * + * This uses a trial and error method to loop through possible PHY + * addresses. It tests each by reading the PHY ID registers and + * checking for a match. + **/ +s32 e1000_determine_phy_address(struct e1000_hw *hw) +{ + u32 phy_addr = 0; + u32 i; + enum e1000_phy_type phy_type = e1000_phy_unknown; + + hw->phy.id = phy_type; + + for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) { + hw->phy.addr = phy_addr; + i = 0; + + do { + e1000_get_phy_id(hw); + phy_type = e1000_get_phy_type_from_id(hw->phy.id); + + /* If phy_type is valid, break - we found our + * PHY address + */ + if (phy_type != e1000_phy_unknown) + return E1000_SUCCESS; + + msec_delay(1); + i++; + } while (i < 10); + } + + return -E1000_ERR_PHY_TYPE; +} + +/** + * e1000_power_up_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_up_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); + mii_reg &= ~MII_CR_POWER_DOWN; + hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); +} + +/** + * e1000_power_down_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_down_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); + mii_reg |= MII_CR_POWER_DOWN; + hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); + msec_delay(1); +} + +/** + * e1000_check_polarity_82577 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +s32 e1000_check_polarity_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_check_polarity_82577"); + + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); + + if (!ret_val) + phy->cable_polarity = ((data & I82577_PHY_STATUS2_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. + **/ +s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_82577"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on 82577 phy\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + } + + return ret_val; +} + +/** + * e1000_get_phy_info_82577 - Retrieve I82577 PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_82577"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + phy->polarity_correction = true; + + ret_val = e1000_check_polarity_82577(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & I82577_PHY_STATUS2_MDIX); + + if ((data & I82577_PHY_STATUS2_SPEED_MASK) == + I82577_PHY_STATUS2_SPEED_1000MBPS) { + ret_val = hw->phy.ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + return ret_val; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_cable_length_82577 - Determine cable length for 82577 PHY + * @hw: pointer to the HW structure + * + * Reads the diagnostic status register and verifies result is valid before + * placing it in the phy_cable_length field. + **/ +s32 e1000_get_cable_length_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, length; + + DEBUGFUNC("e1000_get_cable_length_82577"); + + ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data); + if (ret_val) + return ret_val; + + length = ((phy_data & I82577_DSTATUS_CABLE_LENGTH) >> + I82577_DSTATUS_CABLE_LENGTH_SHIFT); + + if (length == E1000_CABLE_LENGTH_UNDEFINED) + return -E1000_ERR_PHY; + + phy->cable_length = length; + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_gs40g - Write GS40G PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_write_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_write_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); + return ret_val; +} + +/** + * e1000_read_phy_reg_gs40g - Read GS40G PHY register + * @hw: pointer to the HW structure + * @offset: lower half is register offset to read to + * upper half is page to use. + * @data: data to read at register offset + * + * Acquires semaphore, if necessary, then reads the data in the PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_read_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_read_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); + return ret_val; +} + +/** + * e1000_read_phy_reg_mphy - Read mPHY control register + * @hw: pointer to the HW structure + * @address: address to be read + * @data: pointer to the read data + * + * Reads the mPHY control register in the PHY at offset and stores the + * information read to data. + **/ +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data) +{ + u32 mphy_ctrl = 0; + bool locked = false; + bool ready; + + DEBUGFUNC("e1000_read_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = true; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK & + ~E1000_MPHY_ADDRESS_FNC_OVERRIDE) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + *data = E1000_READ_REG(hw, E1000_MPHY_DATA); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_mphy - Write mPHY control register + * @hw: pointer to the HW structure + * @address: address to write to + * @data: data to write to register at offset + * @line_override: used when we want to use different line than default one + * + * Writes data to mPHY control register. + **/ +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override) +{ + u32 mphy_ctrl = 0; + bool locked = false; + bool ready; + + DEBUGFUNC("e1000_write_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = true; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + if (line_override) + mphy_ctrl |= E1000_MPHY_ADDRESS_FNC_OVERRIDE; + else + mphy_ctrl &= ~E1000_MPHY_ADDRESS_FNC_OVERRIDE; + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_DATA, data); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_is_mphy_ready - Check if mPHY control register is not busy + * @hw: pointer to the HW structure + * + * Returns mPHY control register status. + **/ +bool e1000_is_mphy_ready(struct e1000_hw *hw) +{ + u16 retry_count = 0; + u32 mphy_ctrl = 0; + bool ready = false; + + while (retry_count < 2) { + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_BUSY) { + usec_delay(20); + retry_count++; + continue; + } + ready = true; + break; + } + + if (!ready) + DEBUGOUT("ERROR READING mPHY control register, phy is busy.\n"); + + return ready; +} diff --git a/drivers/staging/igb_avb/e1000_phy.h b/drivers/staging/igb_avb/e1000_phy.h new file mode 100644 index 000000000000..a109c914ce8d --- /dev/null +++ b/drivers/staging/igb_avb/e1000_phy.h @@ -0,0 +1,252 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_PHY_H_ +#define _E1000_PHY_H_ + +void e1000_init_phy_ops_generic(struct e1000_hw *hw); +s32 e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data); +void e1000_null_phy_generic(struct e1000_hw *hw); +s32 e1000_null_lplu_state(struct e1000_hw *hw, bool active); +s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_null_set_page(struct e1000_hw *hw, u16 data); +s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); +s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); +s32 e1000_check_downshift_generic(struct e1000_hw *hw); +s32 e1000_check_polarity_m88(struct e1000_hw *hw); +s32 e1000_check_polarity_igp(struct e1000_hw *hw); +s32 e1000_check_polarity_ife(struct e1000_hw *hw); +s32 e1000_check_reset_block_generic(struct e1000_hw *hw); +s32 e1000_copper_link_setup_igp(struct e1000_hw *hw); +s32 e1000_copper_link_setup_m88(struct e1000_hw *hw); +s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw); +s32 e1000_get_cable_length_m88(struct e1000_hw *hw); +s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw); +s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw); +s32 e1000_get_cfg_done_generic(struct e1000_hw *hw); +s32 e1000_get_phy_id(struct e1000_hw *hw); +s32 e1000_get_phy_info_igp(struct e1000_hw *hw); +s32 e1000_get_phy_info_m88(struct e1000_hw *hw); +s32 e1000_get_phy_info_ife(struct e1000_hw *hw); +s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw); +void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl); +s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw); +s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw); +s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page); +s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active); +s32 e1000_setup_copper_link_generic(struct e1000_hw *hw); +s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, bool *success); +s32 e1000_phy_init_script_igp3(struct e1000_hw *hw); +enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id); +s32 e1000_determine_phy_address(struct e1000_hw *hw); +s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg); +s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg); +void e1000_power_up_phy_copper(struct e1000_hw *hw); +void e1000_power_down_phy_copper(struct e1000_hw *hw); +s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data); +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data); +s32 e1000_copper_link_setup_82577(struct e1000_hw *hw); +s32 e1000_check_polarity_82577(struct e1000_hw *hw); +s32 e1000_get_phy_info_82577(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw); +s32 e1000_get_cable_length_82577(struct e1000_hw *hw); +s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data); +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override); +bool e1000_is_mphy_ready(struct e1000_hw *hw); + +#define E1000_MAX_PHY_ADDR 8 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* Status */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* Control */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 /* Power Management */ +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* Page Select */ +#define BM_PHY_PAGE_SELECT 22 /* Page Select for BM */ +#define IGP_PAGE_SHIFT 5 +#define PHY_REG_MASK 0x1F + +/* GS40G - I210 PHY defines */ +#define GS40G_PAGE_SELECT 0x16 +#define GS40G_PAGE_SHIFT 16 +#define GS40G_OFFSET_MASK 0xFFFF +#define GS40G_PAGE_2 0x20000 +#define GS40G_MAC_REG2 0x15 +#define GS40G_MAC_LB 0x4140 +#define GS40G_MAC_SPEED_1G 0X0006 +#define GS40G_COPPER_SPEC 0x0010 + +#define HV_INTC_FC_PAGE_START 768 +#define I82578_ADDR_REG 29 +#define I82577_ADDR_REG 16 +#define I82577_CFG_REG 22 +#define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15) +#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift */ +#define I82577_CTRL_REG 23 + +/* 82577 specific PHY registers */ +#define I82577_PHY_CTRL_2 18 +#define I82577_PHY_LBK_CTRL 19 +#define I82577_PHY_STATUS_2 26 +#define I82577_PHY_DIAG_STATUS 31 + +/* I82577 PHY Status 2 */ +#define I82577_PHY_STATUS2_REV_POLARITY 0x0400 +#define I82577_PHY_STATUS2_MDIX 0x0800 +#define I82577_PHY_STATUS2_SPEED_MASK 0x0300 +#define I82577_PHY_STATUS2_SPEED_1000MBPS 0x0200 + +/* I82577 PHY Control 2 */ +#define I82577_PHY_CTRL2_MANUAL_MDIX 0x0200 +#define I82577_PHY_CTRL2_AUTO_MDI_MDIX 0x0400 +#define I82577_PHY_CTRL2_MDIX_CFG_MASK 0x0600 + +/* I82577 PHY Diagnostics Status */ +#define I82577_DSTATUS_CABLE_LENGTH 0x03FC +#define I82577_DSTATUS_CABLE_LENGTH_SHIFT 2 + +/* 82580 PHY Power Management */ +#define E1000_82580_PHY_POWER_MGMT 0xE14 +#define E1000_82580_PM_SPD 0x0001 /* Smart Power Down */ +#define E1000_82580_PM_D0_LPLU 0x0002 /* For D0a states */ +#define E1000_82580_PM_D3_LPLU 0x0004 /* For all other states */ +#define E1000_82580_PM_GO_LINKD 0x0020 /* Go Link Disconnect */ + +#define E1000_MPHY_DIS_ACCESS 0x80000000 /* disable_access bit */ +#define E1000_MPHY_ENA_ACCESS 0x40000000 /* enable_access bit */ +#define E1000_MPHY_BUSY 0x00010000 /* busy bit */ +#define E1000_MPHY_ADDRESS_FNC_OVERRIDE 0x20000000 /* fnc_override bit */ +#define E1000_MPHY_ADDRESS_MASK 0x0000FFFF /* address mask */ + +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 + +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */ + +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */ + +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 + +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_MDIX 0x0800 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 + +#define IGP02E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course=15:13, Fine=12:9 */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F +#define IGP02E1000_AGC_RANGE 15 + +#define E1000_CABLE_LENGTH_UNDEFINED 0xFF + +#define E1000_KMRNCTRLSTA_OFFSET 0x001F0000 +#define E1000_KMRNCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KMRNCTRLSTA_REN 0x00200000 +#define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */ +#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */ +#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */ +#define E1000_KMRNCTRLSTA_IBIST_DISABLE 0x0200 /* Kumeran IBIST Disable */ +#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */ + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Ctrl */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Ctrl */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control */ + +/* IFE PHY Extended Status Control */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 + +/* IFE PHY Special Control */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 +#define IFE_PSC_FORCE_POLARITY 0x0020 + +/* IFE PHY Special Control and LED Control */ +#define IFE_PSCL_PROBE_MODE 0x0020 +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +/* IFE PHY MDIX Control */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDI-X, 0=force MDI */ +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable auto, 0=disable */ + +/* SFP modules ID memory locations */ +#define E1000_SFF_IDENTIFIER_OFFSET 0x00 +#define E1000_SFF_IDENTIFIER_SFF 0x02 +#define E1000_SFF_IDENTIFIER_SFP 0x03 + +#define E1000_SFF_ETH_FLAGS_OFFSET 0x06 +/* Flags for SFP modules compatible with ETH up to 1Gb */ +struct sfp_e1000_flags { + u8 e1000_base_sx:1; + u8 e1000_base_lx:1; + u8 e1000_base_cx:1; + u8 e1000_base_t:1; + u8 e100_base_lx:1; + u8 e100_base_fx:1; + u8 e10_base_bx10:1; + u8 e10_base_px:1; +}; + +/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */ +#define E1000_SFF_VENDOR_OUI_TYCO 0x00407600 +#define E1000_SFF_VENDOR_OUI_FTL 0x00906500 +#define E1000_SFF_VENDOR_OUI_AVAGO 0x00176A00 +#define E1000_SFF_VENDOR_OUI_INTEL 0x001B2100 + +#endif diff --git a/drivers/staging/igb_avb/e1000_regs.h b/drivers/staging/igb_avb/e1000_regs.h new file mode 100644 index 000000000000..caf1d04dee87 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_regs.h @@ -0,0 +1,633 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_REGS_H_ +#define _E1000_REGS_H_ + +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_MDICNFG 0x00E04 /* MDI Config - RW */ +#define E1000_REGISTER_SET_SIZE 0x20000 /* CSR Size */ +#define E1000_EEPROM_INIT_CTRL_WORD_2 0x0F /* EEPROM Init Ctrl Word 2 */ +#define E1000_EEPROM_PCIE_CTRL_WORD_2 0x28 /* EEPROM PCIe Ctrl Word 2 */ +#define E1000_BARCTRL 0x5BBC /* BAR ctrl reg */ +#define E1000_BARCTRL_FLSIZE 0x0700 /* BAR ctrl Flsize */ +#define E1000_BARCTRL_CSRSIZE 0x2000 /* BAR ctrl CSR size */ +#define E1000_MPHY_ADDR_CTRL 0x0024 /* GbE MPHY Address Control */ +#define E1000_MPHY_DATA 0x0E10 /* GBE MPHY Data */ +#define E1000_MPHY_STAT 0x0E0C /* GBE MPHY Statistics */ +#define E1000_PPHY_CTRL 0x5b48 /* PCIe PHY Control */ +#define E1000_I350_BARCTRL 0x5BFC /* BAR ctrl reg */ +#define E1000_I350_DTXMXPKTSZ 0x355C /* Maximum sent packet size reg*/ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_TSSDP 0x0003C /* Time Sync SDP Configuration Register - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* Rx Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* Tx Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* Rx Configuration Word - RO */ +#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define E1000_EITR(_n) (0x01680 + (0x4 * (_n))) +#define E1000_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */ +#define E1000_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ +#define E1000_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ +#define E1000_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */ +#define E1000_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear Mask - RW */ +#define E1000_GPIE 0x01514 /* General Purpose Interrupt Enable - RW */ +#define E1000_IVAR0 0x01700 /* Interrupt Vector Allocation (array) - RW */ +#define E1000_IVAR_MISC 0x01740 /* IVAR for "other" causes - RW */ +#define E1000_TCTL 0x00400 /* Tx Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended Tx Control - RW */ +#define E1000_TIPG 0x00410 /* Tx Inter-packet gap -RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_LEDMUX 0x08130 /* LED MUX Control */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */ +#define E1000_I2CPARAMS 0x0102C /* SFPI2C Parameters Register - RW */ +#define E1000_I2CBB_EN 0x00000100 /* I2C - Bit Bang Enable */ +#define E1000_I2C_CLK_OUT 0x00000200 /* I2C- Clock */ +#define E1000_I2C_DATA_OUT 0x00000400 /* I2C- Data Out */ +#define E1000_I2C_DATA_OE_N 0x00000800 /* I2C- Data Output Enable */ +#define E1000_I2C_DATA_IN 0x00001000 /* I2C- Data In */ +#define E1000_I2C_CLK_OE_N 0x00002000 /* I2C- Clock Output Enable */ +#define E1000_I2C_CLK_IN 0x00004000 /* I2C- Clock In */ +#define E1000_I2C_CLK_STRETCH_DIS 0x00008000 /* I2C- Dis Clk Stretching */ +#define E1000_WDSTP 0x01040 /* Watchdog Setup - RW */ +#define E1000_SWDSTS 0x01044 /* SW Device Status - RW */ +#define E1000_FRTIMER 0x01048 /* Free Running Timer - RW */ +#define E1000_TCPTIMER 0x0104C /* TCP Timer - RW */ +#define E1000_VPDDIAG 0x01060 /* VPD Diagnostic - RO */ +#define E1000_ICR_V2 0x01500 /* Intr Cause - new location - RC */ +#define E1000_ICS_V2 0x01504 /* Intr Cause Set - new location - WO */ +#define E1000_IMS_V2 0x01508 /* Intr Mask Set/Read - new location - RW */ +#define E1000_IMC_V2 0x0150C /* Intr Mask Clear - new location - WO */ +#define E1000_IAM_V2 0x01510 /* Intr Ack Auto Mask - new location - RW */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ +#define E1000_RDFH 0x02410 /* Rx Data FIFO Head - RW */ +#define E1000_RDFT 0x02418 /* Rx Data FIFO Tail - RW */ +#define E1000_RDFHS 0x02420 /* Rx Data FIFO Head Saved - RW */ +#define E1000_RDFTS 0x02428 /* Rx Data FIFO Tail Saved - RW */ +#define E1000_RDFPC 0x02430 /* Rx Data FIFO Packet Count - RW */ +#define E1000_PBRTH 0x02458 /* PB Rx Arbitration Threshold - RW */ +#define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */ +/* Split and Replication Rx Control - RW */ +#define E1000_RDPUMB 0x025CC /* DMA Rx Descriptor uC Mailbox - RW */ +#define E1000_RDPUAD 0x025D0 /* DMA Rx Descriptor uC Addr Command - RW */ +#define E1000_RDPUWD 0x025D4 /* DMA Rx Descriptor uC Data Write - RW */ +#define E1000_RDPURD 0x025D8 /* DMA Rx Descriptor uC Data Read - RW */ +#define E1000_RDPUCTL 0x025DC /* DMA Rx Descriptor uC Control - RW */ +#define E1000_PBDIAG 0x02458 /* Packet Buffer Diagnostic - RW */ +#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */ +#define E1000_IRPBS 0x02404 /* Same as RXPBS, renamed for newer Si - RW */ +#define E1000_PBRWAC 0x024E8 /* Rx packet buffer wrap around counter - RO */ +#define E1000_RDTR 0x02820 /* Rx Delay Timer - RW */ +#define E1000_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer - RW */ +#define E1000_EMIADD 0x10 /* Extended Memory Indirect Address */ +#define E1000_EMIDATA 0x11 /* Extended Memory Indirect Data */ +#define E1000_SRWR 0x12018 /* Shadow Ram Write Register - RW */ +#define E1000_I210_FLMNGCTL 0x12038 +#define E1000_I210_FLMNGDATA 0x1203C +#define E1000_I210_FLMNGCNT 0x12040 + +#define E1000_I210_FLSWCTL 0x12048 +#define E1000_I210_FLSWDATA 0x1204C +#define E1000_I210_FLSWCNT 0x12050 + +#define E1000_I210_FLA 0x1201C + +#define E1000_INVM_DATA_REG(_n) (0x12120 + 4*(_n)) +#define E1000_INVM_SIZE 64 /* Number of INVM Data Registers */ + +/* QAV Tx mode control register */ +#define E1000_I210_TQAVCTRL 0x3570 +#define E1000_DTXMXPKTSZ 0x0355C + +/* High credit registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVHC(_n) (0x300C + 0x40 * (_n)) + +/* Queues fetch arbitration priority control register */ +#define E1000_I210_TQAVARBCTRL 0x3574 +/* Queues priority masks where _n and _p can be 0-3. */ +#define E1000_TQAVARBCTRL_QUEUE_PRI(_n, _p) ((_p) << (2 * (_n))) +/* QAV Tx mode control registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVCC(_n) (0x3004 + 0x40 * (_n)) + +/* QAV Tx mode control register bitfields masks */ +#define E1000_TQAVCC_IDLE_SLOPE 0xFFFF /* Idle slope */ +#define E1000_TQAVCC_KEEP_CREDITS (1 << 30) /* Keep credits opt enable */ +#define E1000_TQAVCC_QUEUE_MODE (1 << 31) /* SP vs. SR Tx mode */ + +/* Good transmitted packets counter registers */ +#define E1000_PQGPTC(_n) (0x010014 + (0x100 * (_n))) + +/* Queues packet buffer size masks where _n can be 0-3 and _s 0-63 [kB] */ +#define E1000_I210_TXPBS_SIZE(_n, _s) ((_s) << (6 * (_n))) + +#define E1000_MMDAC 13 /* MMD Access Control */ +#define E1000_MMDAAD 14 /* MMD Access Address/Data */ + +/* Convenience macros + * + * Note: "_n" is the queue number of the register to be written to. + * + * Example usage: + * E1000_RDBAL_REG(current_rx_queue) + */ +#define E1000_RDBAL(_n) ((_n) < 4 ? (0x02800 + ((_n) * 0x100)) : \ + (0x0C000 + ((_n) * 0x40))) +#define E1000_RDBAH(_n) ((_n) < 4 ? (0x02804 + ((_n) * 0x100)) : \ + (0x0C004 + ((_n) * 0x40))) +#define E1000_RDLEN(_n) ((_n) < 4 ? (0x02808 + ((_n) * 0x100)) : \ + (0x0C008 + ((_n) * 0x40))) +#define E1000_SRRCTL(_n) ((_n) < 4 ? (0x0280C + ((_n) * 0x100)) : \ + (0x0C00C + ((_n) * 0x40))) +#define E1000_RDH(_n) ((_n) < 4 ? (0x02810 + ((_n) * 0x100)) : \ + (0x0C010 + ((_n) * 0x40))) +#define E1000_RXCTL(_n) ((_n) < 4 ? (0x02814 + ((_n) * 0x100)) : \ + (0x0C014 + ((_n) * 0x40))) +#define E1000_DCA_RXCTRL(_n) E1000_RXCTL(_n) +#define E1000_RDT(_n) ((_n) < 4 ? (0x02818 + ((_n) * 0x100)) : \ + (0x0C018 + ((_n) * 0x40))) +#define E1000_RXDCTL(_n) ((_n) < 4 ? (0x02828 + ((_n) * 0x100)) : \ + (0x0C028 + ((_n) * 0x40))) +#define E1000_RQDPC(_n) ((_n) < 4 ? (0x02830 + ((_n) * 0x100)) : \ + (0x0C030 + ((_n) * 0x40))) +#define E1000_TDBAL(_n) ((_n) < 4 ? (0x03800 + ((_n) * 0x100)) : \ + (0x0E000 + ((_n) * 0x40))) +#define E1000_TDBAH(_n) ((_n) < 4 ? (0x03804 + ((_n) * 0x100)) : \ + (0x0E004 + ((_n) * 0x40))) +#define E1000_TDLEN(_n) ((_n) < 4 ? (0x03808 + ((_n) * 0x100)) : \ + (0x0E008 + ((_n) * 0x40))) +#define E1000_TDH(_n) ((_n) < 4 ? (0x03810 + ((_n) * 0x100)) : \ + (0x0E010 + ((_n) * 0x40))) +#define E1000_TXCTL(_n) ((_n) < 4 ? (0x03814 + ((_n) * 0x100)) : \ + (0x0E014 + ((_n) * 0x40))) +#define E1000_DCA_TXCTRL(_n) E1000_TXCTL(_n) +#define E1000_TDT(_n) ((_n) < 4 ? (0x03818 + ((_n) * 0x100)) : \ + (0x0E018 + ((_n) * 0x40))) +#define E1000_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) : \ + (0x0E028 + ((_n) * 0x40))) +#define E1000_TDWBAL(_n) ((_n) < 4 ? (0x03838 + ((_n) * 0x100)) : \ + (0x0E038 + ((_n) * 0x40))) +#define E1000_TDWBAH(_n) ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) : \ + (0x0E03C + ((_n) * 0x40))) +#define E1000_TARC(_n) (0x03840 + ((_n) * 0x100)) +#define E1000_RSRPD 0x02C00 /* Rx Small Packet Detect - RW */ +#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_PSRTYPE(_i) (0x05480 + ((_i) * 4)) +#define E1000_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \ + (0x054E0 + ((_i - 16) * 8))) +#define E1000_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \ + (0x054E4 + ((_i - 16) * 8))) +#define E1000_SHRAL(_i) (0x05438 + ((_i) * 8)) +#define E1000_SHRAH(_i) (0x0543C + ((_i) * 8)) +#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8)) +#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4)) +#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) +#define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8)) +#define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8)) +#define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8)) +#define E1000_PBSLAC 0x03100 /* Pkt Buffer Slave Access Control */ +#define E1000_PBSLAD(_n) (0x03110 + (0x4 * (_n))) /* Pkt Buffer DWORD */ +#define E1000_TXPBS 0x03404 /* Tx Packet Buffer Size - RW */ +/* Same as TXPBS, renamed for newer Si - RW */ +#define E1000_ITPBS 0x03404 +#define E1000_TDFH 0x03410 /* Tx Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* Tx Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* Tx Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* Tx Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* Tx Data FIFO Packet Count - RW */ +#define E1000_TDPUMB 0x0357C /* DMA Tx Desc uC Mail Box - RW */ +#define E1000_TDPUAD 0x03580 /* DMA Tx Desc uC Addr Command - RW */ +#define E1000_TDPUWD 0x03584 /* DMA Tx Desc uC Data Write - RW */ +#define E1000_TDPURD 0x03588 /* DMA Tx Desc uC Data Read - RW */ +#define E1000_TDPUCTL 0x0358C /* DMA Tx Desc uC Control - RW */ +#define E1000_DTXCTL 0x03590 /* DMA Tx Control - RW */ +#define E1000_DTXTCPFLGL 0x0359C /* DMA Tx Control flag low - RW */ +#define E1000_DTXTCPFLGH 0x035A0 /* DMA Tx Control flag high - RW */ +/* DMA Tx Max Total Allow Size Reqs - RW */ +#define E1000_DTXMXSZRQ 0x03540 +#define E1000_TIDV 0x03820 /* Tx Interrupt Delay Value - RW */ +#define E1000_TADV 0x0382C /* Tx Interrupt Absolute Delay Val - RW */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* Tx-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON Rx Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON Tx Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF Rx Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF Tx Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control Rx Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets Rx (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets Rx (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets Rx (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets Rx (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets Rx (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets Rx (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets Rx Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets Rx Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets Rx Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets Tx Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets Rx Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets Rx Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets Tx Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets Tx Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* Rx No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* Rx Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* Rx Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* Rx Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* Rx Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets Rx Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets Tx Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets Rx Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets Rx High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets Tx Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets Tx High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets Rx - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets Tx - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets Tx (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets Tx (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets Tx (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets Tx (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets Tx (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets Tx (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets Tx Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets Tx Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context Tx - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context Tx Fail - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Pkt Timer Expire Count */ +#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Abs Timer Expire Count */ +#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Pkt Timer Expire Count */ +#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Abs Timer Expire Count */ +#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Min Thresh Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Desc Min Thresh Count */ +#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ + +/* Virtualization statistical counters */ +#define E1000_PFVFGPRC(_n) (0x010010 + (0x100 * (_n))) +#define E1000_PFVFGPTC(_n) (0x010014 + (0x100 * (_n))) +#define E1000_PFVFGORC(_n) (0x010018 + (0x100 * (_n))) +#define E1000_PFVFGOTC(_n) (0x010034 + (0x100 * (_n))) +#define E1000_PFVFMPRC(_n) (0x010038 + (0x100 * (_n))) +#define E1000_PFVFGPRLBC(_n) (0x010040 + (0x100 * (_n))) +#define E1000_PFVFGPTLBC(_n) (0x010044 + (0x100 * (_n))) +#define E1000_PFVFGORLBC(_n) (0x010048 + (0x100 * (_n))) +#define E1000_PFVFGOTLBC(_n) (0x010050 + (0x100 * (_n))) + +/* LinkSec */ +#define E1000_LSECTXUT 0x04300 /* Tx Untagged Pkt Cnt */ +#define E1000_LSECTXPKTE 0x04304 /* Encrypted Tx Pkts Cnt */ +#define E1000_LSECTXPKTP 0x04308 /* Protected Tx Pkt Cnt */ +#define E1000_LSECTXOCTE 0x0430C /* Encrypted Tx Octets Cnt */ +#define E1000_LSECTXOCTP 0x04310 /* Protected Tx Octets Cnt */ +#define E1000_LSECRXUT 0x04314 /* Untagged non-Strict Rx Pkt Cnt */ +#define E1000_LSECRXOCTD 0x0431C /* Rx Octets Decrypted Count */ +#define E1000_LSECRXOCTV 0x04320 /* Rx Octets Validated */ +#define E1000_LSECRXBAD 0x04324 /* Rx Bad Tag */ +#define E1000_LSECRXNOSCI 0x04328 /* Rx Packet No SCI Count */ +#define E1000_LSECRXUNSCI 0x0432C /* Rx Packet Unknown SCI Count */ +#define E1000_LSECRXUNCH 0x04330 /* Rx Unchecked Packets Count */ +#define E1000_LSECRXDELAY 0x04340 /* Rx Delayed Packet Count */ +#define E1000_LSECRXLATE 0x04350 /* Rx Late Packets Count */ +#define E1000_LSECRXOK(_n) (0x04360 + (0x04 * (_n))) /* Rx Pkt OK Cnt */ +#define E1000_LSECRXINV(_n) (0x04380 + (0x04 * (_n))) /* Rx Invalid Cnt */ +#define E1000_LSECRXNV(_n) (0x043A0 + (0x04 * (_n))) /* Rx Not Valid Cnt */ +#define E1000_LSECRXUNSA 0x043C0 /* Rx Unused SA Count */ +#define E1000_LSECRXNUSA 0x043D0 /* Rx Not Using SA Count */ +#define E1000_LSECTXCAP 0x0B000 /* Tx Capabilities Register - RO */ +#define E1000_LSECRXCAP 0x0B300 /* Rx Capabilities Register - RO */ +#define E1000_LSECTXCTRL 0x0B004 /* Tx Control - RW */ +#define E1000_LSECRXCTRL 0x0B304 /* Rx Control - RW */ +#define E1000_LSECTXSCL 0x0B008 /* Tx SCI Low - RW */ +#define E1000_LSECTXSCH 0x0B00C /* Tx SCI High - RW */ +#define E1000_LSECTXSA 0x0B010 /* Tx SA0 - RW */ +#define E1000_LSECTXPN0 0x0B018 /* Tx SA PN 0 - RW */ +#define E1000_LSECTXPN1 0x0B01C /* Tx SA PN 1 - RW */ +#define E1000_LSECRXSCL 0x0B3D0 /* Rx SCI Low - RW */ +#define E1000_LSECRXSCH 0x0B3E0 /* Rx SCI High - RW */ +/* LinkSec Tx 128-bit Key 0 - WO */ +#define E1000_LSECTXKEY0(_n) (0x0B020 + (0x04 * (_n))) +/* LinkSec Tx 128-bit Key 1 - WO */ +#define E1000_LSECTXKEY1(_n) (0x0B030 + (0x04 * (_n))) +#define E1000_LSECRXSA(_n) (0x0B310 + (0x04 * (_n))) /* Rx SAs - RW */ +#define E1000_LSECRXPN(_n) (0x0B330 + (0x04 * (_n))) /* Rx SAs - RW */ +/* LinkSec Rx Keys - where _n is the SA no. and _m the 4 dwords of the 128 bit + * key - RW. + */ +#define E1000_LSECRXKEY(_n, _m) (0x0B350 + (0x10 * (_n)) + (0x04 * (_m))) + +#define E1000_SSVPC 0x041A0 /* Switch Security Violation Pkt Cnt */ +#define E1000_IPSCTRL 0xB430 /* IpSec Control Register */ +#define E1000_IPSRXCMD 0x0B408 /* IPSec Rx Command Register - RW */ +#define E1000_IPSRXIDX 0x0B400 /* IPSec Rx Index - RW */ +/* IPSec Rx IPv4/v6 Address - RW */ +#define E1000_IPSRXIPADDR(_n) (0x0B420 + (0x04 * (_n))) +/* IPSec Rx 128-bit Key - RW */ +#define E1000_IPSRXKEY(_n) (0x0B410 + (0x04 * (_n))) +#define E1000_IPSRXSALT 0x0B404 /* IPSec Rx Salt - RW */ +#define E1000_IPSRXSPI 0x0B40C /* IPSec Rx SPI - RW */ +/* IPSec Tx 128-bit Key - RW */ +#define E1000_IPSTXKEY(_n) (0x0B460 + (0x04 * (_n))) +#define E1000_IPSTXSALT 0x0B454 /* IPSec Tx Salt - RW */ +#define E1000_IPSTXIDX 0x0B450 /* IPSec Tx SA IDX - RW */ +#define E1000_PCS_CFG0 0x04200 /* PCS Configuration 0 - RW */ +#define E1000_PCS_LCTL 0x04208 /* PCS Link Control - RW */ +#define E1000_PCS_LSTAT 0x0420C /* PCS Link Status - RO */ +#define E1000_CBTMPC 0x0402C /* Circuit Breaker Tx Packet Count */ +#define E1000_HTDPMC 0x0403C /* Host Transmit Discarded Packets */ +#define E1000_CBRDPC 0x04044 /* Circuit Breaker Rx Dropped Count */ +#define E1000_CBRMPC 0x040FC /* Circuit Breaker Rx Packet Count */ +#define E1000_RPTHC 0x04104 /* Rx Packets To Host */ +#define E1000_HGPTC 0x04118 /* Host Good Packets Tx Count */ +#define E1000_HTCBDPC 0x04124 /* Host Tx Circuit Breaker Dropped Count */ +#define E1000_HGORCL 0x04128 /* Host Good Octets Received Count Low */ +#define E1000_HGORCH 0x0412C /* Host Good Octets Received Count High */ +#define E1000_HGOTCL 0x04130 /* Host Good Octets Transmit Count Low */ +#define E1000_HGOTCH 0x04134 /* Host Good Octets Transmit Count High */ +#define E1000_LENERRS 0x04138 /* Length Errors Count */ +#define E1000_SCVPC 0x04228 /* SerDes/SGMII Code Violation Pkt Count */ +#define E1000_HRMPC 0x0A018 /* Header Redirection Missed Packet Count */ +#define E1000_PCS_ANADV 0x04218 /* AN advertisement - RW */ +#define E1000_PCS_LPAB 0x0421C /* Link Partner Ability - RW */ +#define E1000_PCS_NPTX 0x04220 /* AN Next Page Transmit - RW */ +#define E1000_PCS_LPABNP 0x04224 /* Link Partner Ability Next Pg - RW */ +#define E1000_RXCSUM 0x05000 /* Rx Checksum Control - RW */ +#define E1000_RLPML 0x05004 /* Rx Long Packet Max Length */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RA2 0x054E0 /* 2nd half of Rx address array - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_VT_CTL 0x0581C /* VMDq Control - RW */ +#define E1000_CIAA 0x05B88 /* Config Indirect Access Address - RW */ +#define E1000_CIAD 0x05B8C /* Config Indirect Access Data - RW */ +#define E1000_VFQA0 0x0B000 /* VLAN Filter Queue Array 0 - RW Array */ +#define E1000_VFQA1 0x0B200 /* VLAN Filter Queue Array 1 - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_PBACL 0x05B68 /* MSIx PBA Clear - Read/Write 1's to clear */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ +#define E1000_HIBBA 0x8F40 /* Host Interface Buffer Base Address */ +/* Flexible Host Filter Table */ +#define E1000_FHFT(_n) (0x09000 + ((_n) * 0x100)) +/* Ext Flexible Host Filter Table */ +#define E1000_FHFT_EXT(_n) (0x09A00 + ((_n) * 0x100)) + +#define E1000_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ +/* Management Decision Filters */ +#define E1000_MDEF(_n) (0x05890 + (4 * (_n))) +#define E1000_SW_FW_SYNC 0x05B5C /* SW-FW Synchronization - RW */ +#define E1000_CCMCTL 0x05B48 /* CCM Control Register */ +#define E1000_GIOCTL 0x05B44 /* GIO Analog Control Register */ +#define E1000_SCCTL 0x05B4C /* PCIc PLL Configuration Register */ +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GCR2 0x05B64 /* PCI-Ex Control #2 */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +/* Driver-only SW semaphore (not used by BOOT agents) */ +#define E1000_SWSM2 0x05B58 +#define E1000_DCA_ID 0x05B70 /* DCA Requester ID Information - RO */ +#define E1000_DCA_CTRL 0x05B74 /* DCA Control - RW */ +#define E1000_UFUSE 0x05B78 /* UFUSE - RO */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Interface Control */ +#define E1000_FWSTS 0x08F0C /* FW Status */ + +/* RSS registers */ +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_IMIR(_i) (0x05A80 + ((_i) * 4)) /* Immediate Interrupt */ +#define E1000_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* Immediate INTR Ext*/ +#define E1000_IMIRVP 0x05AC0 /* Immediate INT Rx VLAN Priority -RW */ +#define E1000_MSIXBM(_i) (0x01600 + ((_i) * 4)) /* MSI-X Alloc Reg -RW */ +#define E1000_RETA(_i) (0x05C00 + ((_i) * 4)) /* Redirection Table - RW */ +#define E1000_RSSRK(_i) (0x05C80 + ((_i) * 4)) /* RSS Random Key - RW */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ +/* VT Registers */ +#define E1000_SWPBS 0x03004 /* Switch Packet Buffer Size - RW */ +#define E1000_MBVFICR 0x00C80 /* Mailbox VF Cause - RWC */ +#define E1000_MBVFIMR 0x00C84 /* Mailbox VF int Mask - RW */ +#define E1000_VFLRE 0x00C88 /* VF Register Events - RWC */ +#define E1000_VFRE 0x00C8C /* VF Receive Enables */ +#define E1000_VFTE 0x00C90 /* VF Transmit Enables */ +#define E1000_QDE 0x02408 /* Queue Drop Enable - RW */ +#define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */ +#define E1000_WVBR 0x03554 /* VM Wrong Behavior - RWS */ +#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */ +#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */ +#define E1000_IOVTCL 0x05BBC /* IOV Control Register */ +#define E1000_VMRCTL 0X05D80 /* Virtual Mirror Rule Control */ +#define E1000_VMRVLAN 0x05D90 /* Virtual Mirror Rule VLAN */ +#define E1000_VMRVM 0x05DA0 /* Virtual Mirror Rule VM */ +#define E1000_MDFB 0x03558 /* Malicious Driver free block */ +#define E1000_LVMMC 0x03548 /* Last VM Misbehavior cause */ +#define E1000_TXSWC 0x05ACC /* Tx Switch Control */ +#define E1000_SCCRL 0x05DB0 /* Storm Control Control */ +#define E1000_BSCTRH 0x05DB8 /* Broadcast Storm Control Threshold */ +#define E1000_MSCTRH 0x05DBC /* Multicast Storm Control Threshold */ +/* These act per VF so an array friendly macro is used */ +#define E1000_V2PMAILBOX(_n) (0x00C40 + (4 * (_n))) +#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n))) +#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n))) +#define E1000_VFVMBMEM(_n) (0x00800 + (_n)) +#define E1000_VMOLR(_n) (0x05AD0 + (4 * (_n))) +/* VLAN Virtual Machine Filter - RW */ +#define E1000_VLVF(_n) (0x05D00 + (4 * (_n))) +#define E1000_VMVIR(_n) (0x03700 + (4 * (_n))) +#define E1000_DVMOLR(_n) (0x0C038 + (0x40 * (_n))) /* DMA VM offload */ +#define E1000_VTCTRL(_n) (0x10000 + (0x100 * (_n))) /* VT Control */ +#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ +#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ +#define E1000_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */ +#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ +#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ +#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ +#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ +#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ +#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ +#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ +#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_TIMADJL 0x0B60C /* Time sync time adjustment offset Low - RW */ +#define E1000_TIMADJH 0x0B610 /* Time sync time adjustment offset High - RW */ +#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */ +#define E1000_TRGTTIML0 0x0B644 /* Target Time Register 0 Low - RW */ +#define E1000_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */ +#define E1000_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */ +#define E1000_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */ +#define E1000_FREQOUT0 0x0B654 /* Frequency Out 0 Control Register - RW */ +#define E1000_FREQOUT1 0x0B658 /* Frequency Out 1 Control Register - RW */ +#define E1000_AUXSTMPL0 0x0B65C /* Auxiliary Time Stamp 0 Register Low - RO */ +#define E1000_AUXSTMPH0 0x0B660 /* Auxiliary Time Stamp 0 Register High - RO */ +#define E1000_AUXSTMPL1 0x0B664 /* Auxiliary Time Stamp 1 Register Low - RO */ +#define E1000_AUXSTMPH1 0x0B668 /* Auxiliary Time Stamp 1 Register High - RO */ +#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */ +#define E1000_TSICR 0x0B66C /* Interrupt Cause Register */ +#define E1000_TSIM 0x0B674 /* Interrupt Mask Register */ + +/* Filtering Registers */ +#define E1000_SAQF(_n) (0x05980 + (4 * (_n))) /* Source Address Queue Fltr */ +#define E1000_DAQF(_n) (0x059A0 + (4 * (_n))) /* Dest Address Queue Fltr */ +#define E1000_SPQF(_n) (0x059C0 + (4 * (_n))) /* Source Port Queue Fltr */ +#define E1000_FTQF(_n) (0x059E0 + (4 * (_n))) /* 5-tuple Queue Fltr */ +#define E1000_TTQF(_n) (0x059E0 + (4 * (_n))) /* 2-tuple Queue Fltr */ +#define E1000_SYNQF(_n) (0x055FC + (4 * (_n))) /* SYN Packet Queue Fltr */ +#define E1000_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ + +#define E1000_RTTDCS 0x3600 /* Reedtown Tx Desc plane control and status */ +#define E1000_RTTPCS 0x3474 /* Reedtown Tx Packet Plane control and status */ +#define E1000_RTRPCS 0x2474 /* Rx packet plane control and status */ +#define E1000_RTRUP2TC 0x05AC4 /* Rx User Priority to Traffic Class */ +#define E1000_RTTUP2TC 0x0418 /* Transmit User Priority to Traffic Class */ +/* Tx Desc plane TC Rate-scheduler config */ +#define E1000_RTTDTCRC(_n) (0x3610 + ((_n) * 4)) +/* Tx Packet plane TC Rate-Scheduler Config */ +#define E1000_RTTPTCRC(_n) (0x3480 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler Config */ +#define E1000_RTRPTCRC(_n) (0x2480 + ((_n) * 4)) +/* Tx Desc Plane TC Rate-Scheduler Status */ +#define E1000_RTTDTCRS(_n) (0x3630 + ((_n) * 4)) +/* Tx Desc Plane TC Rate-Scheduler MMW */ +#define E1000_RTTDTCRM(_n) (0x3650 + ((_n) * 4)) +/* Tx Packet plane TC Rate-Scheduler Status */ +#define E1000_RTTPTCRS(_n) (0x34A0 + ((_n) * 4)) +/* Tx Packet plane TC Rate-scheduler MMW */ +#define E1000_RTTPTCRM(_n) (0x34C0 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler Status */ +#define E1000_RTRPTCRS(_n) (0x24A0 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler MMW */ +#define E1000_RTRPTCRM(_n) (0x24C0 + ((_n) * 4)) +/* Tx Desc plane VM Rate-Scheduler MMW*/ +#define E1000_RTTDVMRM(_n) (0x3670 + ((_n) * 4)) +/* Tx BCN Rate-Scheduler MMW */ +#define E1000_RTTBCNRM(_n) (0x3690 + ((_n) * 4)) +#define E1000_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select */ +#define E1000_RTTDVMRC 0x3608 /* Tx Desc Plane VM Rate-Scheduler Config */ +#define E1000_RTTDVMRS 0x360C /* Tx Desc Plane VM Rate-Scheduler Status */ +#define E1000_RTTBCNRC 0x36B0 /* Tx BCN Rate-Scheduler Config */ +#define E1000_RTTBCNRS 0x36B4 /* Tx BCN Rate-Scheduler Status */ +#define E1000_RTTBCNCR 0xB200 /* Tx BCN Control Register */ +#define E1000_RTTBCNTG 0x35A4 /* Tx BCN Tagging */ +#define E1000_RTTBCNCP 0xB208 /* Tx BCN Congestion point */ +#define E1000_RTRBCNCR 0xB20C /* Rx BCN Control Register */ +#define E1000_RTTBCNRD 0x36B8 /* Tx BCN Rate Drift */ +#define E1000_PFCTOP 0x1080 /* Priority Flow Control Type and Opcode */ +#define E1000_RTTBCNIDX 0xB204 /* Tx BCN Congestion Point */ +#define E1000_RTTBCNACH 0x0B214 /* Tx BCN Control High */ +#define E1000_RTTBCNACL 0x0B210 /* Tx BCN Control Low */ + +/* DMA Coalescing registers */ +#define E1000_DMACR 0x02508 /* Control Register */ +#define E1000_DMCTXTH 0x03550 /* Transmit Threshold */ +#define E1000_DMCTLX 0x02514 /* Time to Lx Request */ +#define E1000_DMCRTRH 0x05DD0 /* Receive Packet Rate Threshold */ +#define E1000_DMCCNT 0x05DD4 /* Current Rx Count */ +#define E1000_FCRTC 0x02170 /* Flow Control Rx high watermark */ +#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */ + +/* PCIe Parity Status Register */ +#define E1000_PCIEERRSTS 0x05BA8 + +#define E1000_PROXYS 0x5F64 /* Proxying Status */ +#define E1000_PROXYFC 0x5F60 /* Proxying Filter Control */ +/* Thermal sensor configuration and status registers */ +#define E1000_THMJT 0x08100 /* Junction Temperature */ +#define E1000_THLOWTC 0x08104 /* Low Threshold Control */ +#define E1000_THMIDTC 0x08108 /* Mid Threshold Control */ +#define E1000_THHIGHTC 0x0810C /* High Threshold Control */ +#define E1000_THSTAT 0x08110 /* Thermal Sensor Status */ + +/* Energy Efficient Ethernet "EEE" registers */ +#define E1000_IPCNFG 0x0E38 /* Internal PHY Configuration */ +#define E1000_LTRC 0x01A0 /* Latency Tolerance Reporting Control */ +#define E1000_EEER 0x0E30 /* Energy Efficient Ethernet "EEE"*/ +#define E1000_EEE_SU 0x0E34 /* EEE Setup */ +#define E1000_TLPIC 0x4148 /* EEE Tx LPI Count - TLPIC */ +#define E1000_RLPIC 0x414C /* EEE Rx LPI Count - RLPIC */ + +/* OS2BMC Registers */ +#define E1000_B2OSPC 0x08FE0 /* BMC2OS packets sent by BMC */ +#define E1000_B2OGPRC 0x04158 /* BMC2OS packets received by host */ +#define E1000_O2BGPTC 0x08FE4 /* OS2BMC packets received by BMC */ +#define E1000_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */ + +#endif diff --git a/drivers/staging/igb_avb/igb.h b/drivers/staging/igb_avb/igb.h new file mode 100644 index 000000000000..c8bbf307f908 --- /dev/null +++ b/drivers/staging/igb_avb/igb.h @@ -0,0 +1,937 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2016 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* Linux PRO/1000 Ethernet Driver main header file */ + +#ifndef _IGB_H_ +#define _IGB_H_ + +#include + +#ifndef IGB_NO_LRO +#include +#endif + +#include +#include +#include + +#ifdef SIOCETHTOOL +#include +#endif + +struct igb_adapter; + +struct igb_user_page; + +struct igb_user_page { + struct igb_user_page *prev; + struct igb_user_page *next; + struct page *page; + dma_addr_t page_dma; +}; +#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE) +#define IGB_DCA +#endif +#ifdef IGB_DCA +#include +#endif + +#include "kcompat.h" + +#ifdef HAVE_SCTP +#include +#endif + +#include "e1000_api.h" +#include "e1000_82575.h" +#include "e1000_manage.h" +#include "e1000_mbx.h" + +#define IGB_ERR(args...) pr_err(KERN_ERR "igb: " args) + +#define PFX "igb: " +#define DPRINTK(nlevel, klevel, fmt, args...) \ + (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ + printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ + __func__ , ## args)) + +#ifdef HAVE_PTP_1588_CLOCK +#ifdef HAVE_INCLUDE_LINUX_TIMECOUNTER_H +#include +#else +#include +#endif /* HAVE_INCLUDE_TIMECOUNTER_H */ +#include +#include +#endif /* HAVE_PTP_1588_CLOCK */ + +#ifdef HAVE_I2C_SUPPORT +#include +#include +#endif /* HAVE_I2C_SUPPORT */ + +#include +typedef u64 cycle_t; + +/* Interrupt defines */ +#define IGB_START_ITR 648 /* ~6000 ints/sec */ +#define IGB_4K_ITR 980 +#define IGB_20K_ITR 196 +#define IGB_70K_ITR 56 + +/* Interrupt modes, as used by the IntMode paramter */ +#define IGB_INT_MODE_LEGACY 0 +#define IGB_INT_MODE_MSI 1 +#define IGB_INT_MODE_MSIX 2 + +/* TX/RX descriptor defines */ +#define IGB_DEFAULT_TXD 256 +#define IGB_DEFAULT_TX_WORK 128 +#define IGB_MIN_TXD 80 +#define IGB_MAX_TXD 4096 + +#define IGB_DEFAULT_RXD 256 +#define IGB_MIN_RXD 80 +#define IGB_MAX_RXD 4096 + +#define IGB_MIN_ITR_USECS 10 /* 100k irq/sec */ +#define IGB_MAX_ITR_USECS 8191 /* 120 irq/sec */ + +#define NON_Q_VECTORS 1 +#define MAX_Q_VECTORS 10 + +/* Transmit and receive queues */ +#define IGB_MAX_RX_QUEUES 16 +#define IGB_MAX_RX_QUEUES_82575 4 +#define IGB_MAX_RX_QUEUES_I211 2 +#define IGB_MAX_TX_QUEUES 16 + +#define IGB_MAX_VF_MC_ENTRIES 30 +#define IGB_MAX_VF_FUNCTIONS 8 +#define IGB_82576_VF_DEV_ID 0x10CA +#define IGB_I350_VF_DEV_ID 0x1520 +#define IGB_MAX_UTA_ENTRIES 128 +#define MAX_EMULATION_MAC_ADDRS 16 +#define OUI_LEN 3 +#define IGB_MAX_VMDQ_QUEUES 8 + +struct vf_data_storage { + unsigned char vf_mac_addresses[ETH_ALEN]; + u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES]; + u16 num_vf_mc_hashes; + u16 default_vf_vlan_id; + u16 vlans_enabled; + unsigned char em_mac_addresses[MAX_EMULATION_MAC_ADDRS * ETH_ALEN]; + u32 uta_table_copy[IGB_MAX_UTA_ENTRIES]; + u32 flags; + unsigned long last_nack; +#ifdef IFLA_VF_MAX + u16 pf_vlan; /* When set, guest VLAN config not allowed. */ + u16 pf_qos; + u16 tx_rate; +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE + bool spoofchk_enabled; +#endif +#endif +}; + +#define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */ +#define IGB_VF_FLAG_UNI_PROMISC 0x00000002 /* VF has unicast promisc */ +#define IGB_VF_FLAG_MULTI_PROMISC 0x00000004 /* VF has multicast promisc */ +#define IGB_VF_FLAG_PF_SET_MAC 0x00000008 /* PF has set MAC address */ + +/* RX descriptor control thresholds. + * PTHRESH - MAC will consider prefetch if it has fewer than this number of + * descriptors available in its onboard memory. + * Setting this to 0 disables RX descriptor prefetch. + * HTHRESH - MAC will only prefetch if there are at least this many descriptors + * available in host memory. + * If PTHRESH is 0, this should also be 0. + * WTHRESH - RX descriptor writeback threshold - MAC will delay writing back + * descriptors until either it has this many to write back, or the + * ITR timer expires. + */ +#define IGB_RX_PTHRESH ((hw->mac.type == e1000_i354) ? 12 : 8) +#define IGB_RX_HTHRESH 8 +#define IGB_TX_PTHRESH ((hw->mac.type == e1000_i354) ? 20 : 8) +#define IGB_TX_HTHRESH 1 +#define IGB_RX_WTHRESH ((hw->mac.type == e1000_82576 && \ + adapter->msix_entries) ? 1 : 4) + +/* this is the size past which hardware will drop packets when setting LPE=0 */ +#define MAXIMUM_ETHERNET_VLAN_SIZE 1522 + +/* NOTE: netdev_alloc_skb reserves 16 bytes, NET_IP_ALIGN means we + * reserve 2 more, and skb_shared_info adds an additional 384 more, + * this adds roughly 448 bytes of extra data meaning the smallest + * allocation we could have is 1K. + * i.e. RXBUFFER_512 --> size-1024 slab + */ +/* Supported Rx Buffer Sizes */ +#define IGB_RXBUFFER_256 256 +#define IGB_RXBUFFER_2048 2048 +#define IGB_RXBUFFER_16384 16384 +#define IGB_RX_HDR_LEN IGB_RXBUFFER_256 +#if MAX_SKB_FRAGS < 8 +#define IGB_RX_BUFSZ ALIGN(MAX_JUMBO_FRAME_SIZE / MAX_SKB_FRAGS, 1024) +#else +#define IGB_RX_BUFSZ IGB_RXBUFFER_2048 +#endif + + +/* Packet Buffer allocations */ +#define IGB_PBA_BYTES_SHIFT 0xA +#define IGB_TX_HEAD_ADDR_SHIFT 7 +#define IGB_PBA_TX_MASK 0xFFFF0000 + +#define IGB_FC_PAUSE_TIME 0x0680 /* 858 usec */ + +/* How many Rx Buffers do we bundle into one write to the hardware ? */ +#define IGB_RX_BUFFER_WRITE 16 /* Must be power of 2 */ + +#define IGB_EEPROM_APME 0x0400 +#define AUTO_ALL_MODES 0 + +#ifndef IGB_MASTER_SLAVE +/* Switch to override PHY master/slave setting */ +#define IGB_MASTER_SLAVE e1000_ms_hw_default +#endif + +#define IGB_MNG_VLAN_NONE -1 + +#ifndef IGB_NO_LRO +#define IGB_LRO_MAX 32 /*Maximum number of LRO descriptors*/ +struct igb_lro_stats { + u32 flushed; + u32 coal; +}; + +/* + * igb_lro_header - header format to be aggregated by LRO + * @iph: IP header without options + * @tcp: TCP header + * @ts: Optional TCP timestamp data in TCP options + * + * This structure relies on the check above that verifies that the header + * is IPv4 and does not contain any options. + */ +struct igb_lrohdr { + struct iphdr iph; + struct tcphdr th; + __be32 ts[0]; +}; + +struct igb_lro_list { + struct sk_buff_head active; + struct igb_lro_stats stats; +}; + +#endif /* IGB_NO_LRO */ +struct igb_cb { +#ifndef IGB_NO_LRO +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + union { /* Union defining head/tail partner */ + struct sk_buff *head; + struct sk_buff *tail; + }; +#endif + __be32 tsecr; /* timestamp echo response */ + u32 tsval; /* timestamp value in host order */ + u32 next_seq; /* next expected sequence number */ + u16 free; /* 65521 minus total size */ + u16 mss; /* size of data portion of packet */ + u16 append_cnt; /* number of skb's appended */ +#endif /* IGB_NO_LRO */ +#ifdef HAVE_VLAN_RX_REGISTER + u16 vid; /* VLAN tag */ +#endif +}; +#define IGB_CB(skb) ((struct igb_cb *)(skb)->cb) + +enum igb_tx_flags { + /* cmd_type flags */ + IGB_TX_FLAGS_VLAN = 0x01, + IGB_TX_FLAGS_TSO = 0x02, + IGB_TX_FLAGS_TSTAMP = 0x04, + + /* olinfo flags */ + IGB_TX_FLAGS_IPV4 = 0x10, + IGB_TX_FLAGS_CSUM = 0x20, +}; + +/* VLAN info */ +#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000 +#define IGB_TX_FLAGS_VLAN_SHIFT 16 + +/* + * The largest size we can write to the descriptor is 65535. In order to + * maintain a power of two alignment we have to limit ourselves to 32K. + */ +#define IGB_MAX_TXD_PWR 15 +#define IGB_MAX_DATA_PER_TXD (1 << IGB_MAX_TXD_PWR) + +/* Tx Descriptors needed, worst case */ +#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IGB_MAX_DATA_PER_TXD) +#ifndef MAX_SKB_FRAGS +#define DESC_NEEDED 4 +#elif (MAX_SKB_FRAGS < 16) +#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4) +#else +#define DESC_NEEDED (MAX_SKB_FRAGS + 4) +#endif + +/* wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer */ +struct igb_tx_buffer { + union e1000_adv_tx_desc *next_to_watch; + unsigned long time_stamp; + struct sk_buff *skb; + unsigned int bytecount; + u16 gso_segs; + __be16 protocol; + + DEFINE_DMA_UNMAP_ADDR(dma); + DEFINE_DMA_UNMAP_LEN(len); + u32 tx_flags; +}; + +struct igb_rx_buffer { + dma_addr_t dma; +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + struct sk_buff *skb; +#else + struct page *page; + u32 page_offset; +#endif +}; + +struct igb_tx_queue_stats { + u64 packets; + u64 bytes; + u64 restart_queue; +}; + +struct igb_rx_queue_stats { + u64 packets; + u64 bytes; + u64 drops; + u64 csum_err; + u64 alloc_failed; +}; + +struct igb_rx_packet_stats { + u64 ipv4_packets; /* IPv4 headers processed */ + u64 ipv4e_packets; /* IPv4E headers with extensions processed */ + u64 ipv6_packets; /* IPv6 headers processed */ + u64 ipv6e_packets; /* IPv6E headers with extensions processed */ + u64 tcp_packets; /* TCP headers processed */ + u64 udp_packets; /* UDP headers processed */ + u64 sctp_packets; /* SCTP headers processed */ + u64 nfs_packets; /* NFS headers processe */ + u64 other_packets; +}; + +struct igb_ring_container { + struct igb_ring *ring; /* pointer to linked list of rings */ + unsigned int total_bytes; /* total bytes processed this int */ + unsigned int total_packets; /* total packets processed this int */ + u16 work_limit; /* total work allowed per interrupt */ + u8 count; /* total number of rings in vector */ + u8 itr; /* current ITR setting for ring */ +}; + +struct igb_ring { + struct igb_q_vector *q_vector; /* backlink to q_vector */ + struct net_device *netdev; /* back pointer to net_device */ + struct device *dev; /* device for dma mapping */ + union { /* array of buffer info structs */ + struct igb_tx_buffer *tx_buffer_info; + struct igb_rx_buffer *rx_buffer_info; + }; + void *desc; /* descriptor ring memory */ + unsigned long flags; /* ring specific flags */ + void __iomem *tail; /* pointer to ring tail register */ + dma_addr_t dma; /* phys address of the ring */ + unsigned int size; /* length of desc. ring in bytes */ + + u16 count; /* number of desc. in the ring */ + u8 queue_index; /* logical index of the ring*/ + u8 reg_idx; /* physical index of the ring */ + + /* everything past this point are written often */ + u16 next_to_clean; + u16 next_to_use; + u16 next_to_alloc; + + union { + /* TX */ + struct { + struct igb_tx_queue_stats tx_stats; + }; + /* RX */ + struct { + struct igb_rx_queue_stats rx_stats; + struct igb_rx_packet_stats pkt_stats; +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + u16 rx_buffer_len; +#else + struct sk_buff *skb; +#endif + }; + }; +#ifdef CONFIG_IGB_VMDQ_NETDEV + struct net_device *vmdq_netdev; + int vqueue_index; /* queue index for virtual netdev */ +#endif +} ____cacheline_internodealigned_in_smp; + +struct igb_q_vector { + struct igb_adapter *adapter; /* backlink */ + int cpu; /* CPU for DCA */ + u32 eims_value; /* EIMS mask value */ + + u16 itr_val; + u8 set_itr; + void __iomem *itr_register; + + struct igb_ring_container rx, tx; + + struct napi_struct napi; +#ifndef IGB_NO_LRO + struct igb_lro_list lrolist; /* LRO list for queue vector*/ +#endif + struct rcu_head rcu; /* to avoid race with update stats on free */ + char name[IFNAMSIZ + 9]; +#ifndef HAVE_NETDEV_NAPI_LIST + struct net_device poll_dev; +#endif + + /* for dynamic allocation of rings associated with this q_vector */ + struct igb_ring ring[0] ____cacheline_internodealigned_in_smp; +}; + +enum e1000_ring_flags_t { +#ifndef HAVE_NDO_SET_FEATURES + IGB_RING_FLAG_RX_CSUM, +#endif + IGB_RING_FLAG_RX_SCTP_CSUM, + IGB_RING_FLAG_RX_LB_VLAN_BSWAP, + IGB_RING_FLAG_TX_CTX_IDX, + IGB_RING_FLAG_TX_DETECT_HANG, +}; + +struct igb_mac_addr { + u8 addr[ETH_ALEN]; + u16 queue; + u16 state; /* bitmask */ +}; +#define IGB_MAC_STATE_DEFAULT 0x1 +#define IGB_MAC_STATE_MODIFIED 0x2 +#define IGB_MAC_STATE_IN_USE 0x4 + +#define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS) + +#define IGB_RX_DESC(R, i) \ + (&(((union e1000_adv_rx_desc *)((R)->desc))[i])) +#define IGB_TX_DESC(R, i) \ + (&(((union e1000_adv_tx_desc *)((R)->desc))[i])) +#define IGB_TX_CTXTDESC(R, i) \ + (&(((struct e1000_adv_tx_context_desc *)((R)->desc))[i])) + +#ifdef CONFIG_IGB_VMDQ_NETDEV +#define netdev_ring(ring) \ + ((ring->vmdq_netdev ? ring->vmdq_netdev : ring->netdev)) +#define ring_queue_index(ring) \ + ((ring->vmdq_netdev ? ring->vqueue_index : ring->queue_index)) +#else +#define netdev_ring(ring) (ring->netdev) +#define ring_queue_index(ring) (ring->queue_index) +#endif /* CONFIG_IGB_VMDQ_NETDEV */ + +/* igb_test_staterr - tests bits within Rx descriptor status and error fields */ +static inline __le32 igb_test_staterr(union e1000_adv_rx_desc *rx_desc, + const u32 stat_err_bits) +{ + return rx_desc->wb.upper.status_error & cpu_to_le32(stat_err_bits); +} + +/* igb_desc_unused - calculate if we have unused descriptors */ +static inline u16 igb_desc_unused(const struct igb_ring *ring) +{ + u16 ntc = ring->next_to_clean; + u16 ntu = ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} + +#ifdef CONFIG_BQL +static inline struct netdev_queue *txring_txq(const struct igb_ring *tx_ring) +{ + return netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index); +} +#endif /* CONFIG_BQL */ + +struct igb_therm_proc_data { + struct e1000_hw *hw; + struct e1000_thermal_diode_data *sensor_data; +}; + +#ifdef IGB_HWMON +#define IGB_HWMON_TYPE_LOC 0 +#define IGB_HWMON_TYPE_TEMP 1 +#define IGB_HWMON_TYPE_CAUTION 2 +#define IGB_HWMON_TYPE_MAX 3 + +struct hwmon_attr { + struct device_attribute dev_attr; + struct e1000_hw *hw; + struct e1000_thermal_diode_data *sensor; + char name[12]; + }; + +struct hwmon_buff { + struct device *device; + struct hwmon_attr *hwmon_list; + unsigned int n_hwmon; + }; +#endif /* IGB_HWMON */ +#define IGB_N_EXTTS 2 +#define IGB_N_PEROUT 2 +#define IGB_N_SDP 4 +#ifdef ETHTOOL_GRXFHINDIR +#define IGB_RETA_SIZE 128 +#endif /* ETHTOOL_GRXFHINDIR */ + +/* board specific private data structure */ +struct igb_adapter { +#ifdef HAVE_VLAN_RX_REGISTER + /* vlgrp must be first member of structure */ + struct vlan_group *vlgrp; +#else + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif + struct net_device *netdev; + + unsigned long state; + unsigned int flags; + + unsigned int num_q_vectors; + struct msix_entry *msix_entries; + + + /* TX */ + u16 tx_work_limit; + u32 tx_timeout_count; + int num_tx_queues; + struct igb_ring *tx_ring[IGB_MAX_TX_QUEUES]; + + /* RX */ + int num_rx_queues; + struct igb_ring *rx_ring[IGB_MAX_RX_QUEUES]; + + struct timer_list watchdog_timer; + struct timer_list dma_err_timer; + struct timer_list phy_info_timer; + u16 mng_vlan_id; + u32 bd_number; + u32 wol; + u32 en_mng_pt; + u16 link_speed; + u16 link_duplex; + u8 port_num; + + /* Interrupt Throttle Rate */ + u32 rx_itr_setting; + u32 tx_itr_setting; + + struct work_struct reset_task; + struct work_struct watchdog_task; + struct work_struct dma_err_task; + bool fc_autoneg; + u8 tx_timeout_factor; + +#ifdef DEBUG + bool tx_hang_detected; + bool disable_hw_reset; +#endif + u32 max_frame_size; + + /* OS defined structs */ + struct pci_dev *pdev; + /* user-dma specific variables */ + u32 uring_tx_init; + u32 uring_rx_init; +#ifndef HAVE_NETDEV_STATS_IN_NETDEV + struct net_device_stats net_stats; +#endif +#ifndef IGB_NO_LRO + struct igb_lro_stats lro_stats; +#endif + + /* structs defined in e1000_hw.h */ + struct e1000_hw hw; + struct e1000_hw_stats stats; + struct e1000_phy_info phy_info; + struct e1000_phy_stats phy_stats; + +#ifdef ETHTOOL_TEST + u32 test_icr; + struct igb_ring test_tx_ring; + struct igb_ring test_rx_ring; +#endif + + int msg_enable; + + struct igb_q_vector *q_vector[MAX_Q_VECTORS]; + u32 eims_enable_mask; + u32 eims_other; + + /* to not mess up cache alignment, always add to the bottom */ + u32 *config_space; + u16 tx_ring_count; + u16 rx_ring_count; + struct vf_data_storage *vf_data; +#ifdef IFLA_VF_MAX + int vf_rate_link_speed; +#endif + u32 lli_port; + u32 lli_size; + unsigned int vfs_allocated_count; + /* Malicious Driver Detection flag. Valid only when SR-IOV is enabled */ + bool mdd; + int int_mode; + u32 rss_queues; + u32 tss_queues; + u32 vmdq_pools; + char fw_version[32]; + u32 wvbr; + struct igb_mac_addr *mac_table; +#ifdef CONFIG_IGB_VMDQ_NETDEV + struct net_device *vmdq_netdev[IGB_MAX_VMDQ_QUEUES]; +#endif + int vferr_refcount; + int dmac; + u32 *shadow_vfta; + + /* External Thermal Sensor support flag */ + bool ets; +#ifdef IGB_HWMON + struct hwmon_buff igb_hwmon_buff; +#else /* IGB_HWMON */ +#ifdef IGB_PROCFS + struct proc_dir_entry *eth_dir; + struct proc_dir_entry *info_dir; + struct proc_dir_entry *therm_dir[E1000_MAX_SENSORS]; + struct igb_therm_proc_data therm_data[E1000_MAX_SENSORS]; + bool old_lsc; +#endif /* IGB_PROCFS */ +#endif /* IGB_HWMON */ + u32 etrack_id; + +#ifdef HAVE_PTP_1588_CLOCK + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + struct delayed_work ptp_overflow_work; + struct work_struct ptp_tx_work; + struct sk_buff *ptp_tx_skb; + struct hwtstamp_config tstamp_config; + unsigned long ptp_tx_start; + unsigned long last_rx_ptp_check; + unsigned long last_rx_timestamp; + spinlock_t tmreg_lock; + struct cyclecounter cc; + struct timecounter tc; + u32 tx_hwtstamp_timeouts; + u32 rx_hwtstamp_cleared; + +#ifdef HAVE_PTP_1588_CLOCK_PINS + struct ptp_pin_desc sdp_config[IGB_N_SDP]; +#endif /* HAVE_PTP_1588_CLOCK_PINS */ + struct { + struct timespec64 start; + struct timespec64 period; + } perout[IGB_N_PEROUT]; +#endif /* HAVE_PTP_1588_CLOCK */ + +#ifdef HAVE_I2C_SUPPORT + struct i2c_algo_bit_data i2c_algo; + struct i2c_adapter i2c_adap; + struct i2c_client *i2c_client; +#endif /* HAVE_I2C_SUPPORT */ + unsigned long link_check_timeout; + + int devrc; + + int copper_tries; + u16 eee_advert; +#ifdef ETHTOOL_GRXFHINDIR + u32 rss_indir_tbl_init; + u8 rss_indir_tbl[IGB_RETA_SIZE]; +#endif + struct mutex lock; +}; + +#ifdef CONFIG_IGB_VMDQ_NETDEV +struct igb_vmdq_adapter { +#ifdef HAVE_VLAN_RX_REGISTER + /* vlgrp must be first member of structure */ + struct vlan_group *vlgrp; +#else + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif + struct igb_adapter *real_adapter; + struct net_device *vnetdev; + struct net_device_stats net_stats; + struct igb_ring *tx_ring; + struct igb_ring *rx_ring; +}; +#endif + +#define IGB_FLAG_HAS_MSI (1 << 0) +#define IGB_FLAG_DCA_ENABLED (1 << 1) +#define IGB_FLAG_LLI_PUSH (1 << 2) +#define IGB_FLAG_QUAD_PORT_A (1 << 3) +#define IGB_FLAG_QUEUE_PAIRS (1 << 4) +#define IGB_FLAG_EEE (1 << 5) +#define IGB_FLAG_DMAC (1 << 6) +#define IGB_FLAG_DETECT_BAD_DMA (1 << 7) +#define IGB_FLAG_PTP (1 << 8) +#define IGB_FLAG_RSS_FIELD_IPV4_UDP (1 << 9) +#define IGB_FLAG_RSS_FIELD_IPV6_UDP (1 << 10) +#define IGB_FLAG_WOL_SUPPORTED (1 << 11) +#define IGB_FLAG_NEED_LINK_UPDATE (1 << 12) +#define IGB_FLAG_LOOPBACK_ENABLE (1 << 13) +#define IGB_FLAG_MEDIA_RESET (1 << 14) +#define IGB_FLAG_MAS_ENABLE (1 << 15) + +/* Media Auto Sense */ +#define IGB_MAS_ENABLE_0 0X0001 +#define IGB_MAS_ENABLE_1 0X0002 +#define IGB_MAS_ENABLE_2 0X0004 +#define IGB_MAS_ENABLE_3 0X0008 + +#define IGB_MIN_TXPBSIZE 20408 +#define IGB_TX_BUF_4096 4096 + +#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coal Flush */ + +/* DMA Coalescing defines */ +#define IGB_DMAC_DISABLE 0 +#define IGB_DMAC_MIN 250 +#define IGB_DMAC_500 500 +#define IGB_DMAC_EN_DEFAULT 1000 +#define IGB_DMAC_2000 2000 +#define IGB_DMAC_3000 3000 +#define IGB_DMAC_4000 4000 +#define IGB_DMAC_5000 5000 +#define IGB_DMAC_6000 6000 +#define IGB_DMAC_7000 7000 +#define IGB_DMAC_8000 8000 +#define IGB_DMAC_9000 9000 +#define IGB_DMAC_MAX 10000 + +#define IGB_82576_TSYNC_SHIFT 19 +#define IGB_82580_TSYNC_SHIFT 24 +#define IGB_TS_HDR_LEN 16 + +/* CEM Support */ +#define FW_HDR_LEN 0x4 +#define FW_CMD_DRV_INFO 0xDD +#define FW_CMD_DRV_INFO_LEN 0x5 +#define FW_CMD_RESERVED 0X0 +#define FW_RESP_SUCCESS 0x1 +#define FW_UNUSED_VER 0x0 +#define FW_MAX_RETRIES 3 +#define FW_STATUS_SUCCESS 0x1 +#define FW_FAMILY_DRV_VER 0Xffffffff + +#define IGB_MAX_LINK_TRIES 20 + +struct e1000_fw_hdr { + u8 cmd; + u8 buf_len; + union { + u8 cmd_resv; + u8 ret_status; + } cmd_or_resp; + u8 checksum; +}; + +#pragma pack(push, 1) +struct e1000_fw_drv_info { + struct e1000_fw_hdr hdr; + u8 port_num; + u32 drv_version; + u16 pad; /* end spacing to ensure length is mult. of dword */ + u8 pad2; /* end spacing to ensure length is mult. of dword2 */ +}; +#pragma pack(pop) + +enum e1000_state_t { + __IGB_TESTING, + __IGB_RESETTING, + __IGB_DOWN, + __IGB_PTP_TX_IN_PROGRESS, +}; + +extern char igb_driver_name[]; +extern char igb_driver_version[]; + +extern void igb_up(struct igb_adapter *); +extern void igb_down(struct igb_adapter *); +extern void igb_reinit_locked(struct igb_adapter *); +extern void igb_reset(struct igb_adapter *); +#ifdef ETHTOOL_SRXFHINDIR +extern void igb_write_rss_indir_tbl(struct igb_adapter *); +#endif +extern int igb_set_spd_dplx(struct igb_adapter *, u16); +extern int igb_setup_tx_resources(struct igb_ring *); +extern int igb_setup_rx_resources(struct igb_ring *); +extern void igb_free_tx_resources(struct igb_ring *); +extern void igb_free_rx_resources(struct igb_ring *); +extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_setup_tctl(struct igb_adapter *); +extern void igb_setup_rctl(struct igb_adapter *); +extern netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); +extern void igb_unmap_and_free_tx_resource(struct igb_ring *, + struct igb_tx_buffer *); +extern void igb_alloc_rx_buffers(struct igb_ring *, u16); +extern void igb_clean_rx_ring(struct igb_ring *); +extern int igb_setup_queues(struct igb_adapter *adapter); +extern void igb_update_stats(struct igb_adapter *); +extern bool igb_has_link(struct igb_adapter *adapter); +extern void igb_set_ethtool_ops(struct net_device *); +extern void igb_check_options(struct igb_adapter *); +extern void igb_power_up_link(struct igb_adapter *); +#ifdef HAVE_PTP_1588_CLOCK +extern void igb_ptp_init(struct igb_adapter *adapter); +extern void igb_ptp_stop(struct igb_adapter *adapter); +extern void igb_ptp_reset(struct igb_adapter *adapter); +extern void igb_ptp_tx_work(struct work_struct *work); +extern void igb_ptp_rx_hang(struct igb_adapter *adapter); +extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); +extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, + struct sk_buff *skb); +extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, + unsigned char *va, + struct sk_buff *skb); +extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd); +#endif /* HAVE_PTP_1588_CLOCK */ +#ifdef ETHTOOL_OPS_COMPAT +extern int ethtool_ioctl(struct ifreq *); +#endif +extern int igb_write_mc_addr_list(struct net_device *netdev); +extern int igb_add_mac_filter(struct igb_adapter *adapter, u8 *addr, u16 queue); +extern int igb_del_mac_filter(struct igb_adapter *adapter, u8 *addr, u16 queue); +extern int igb_available_rars(struct igb_adapter *adapter); +extern s32 igb_vlvf_set(struct igb_adapter *, u32, bool, u32); +extern void igb_configure_vt_default_pool(struct igb_adapter *adapter); +extern void igb_enable_vlan_tags(struct igb_adapter *adapter); +#ifndef HAVE_VLAN_RX_REGISTER +extern void igb_vlan_mode(struct net_device *, u32); +#endif + +#define E1000_PCS_CFG_IGN_SD 1 + +int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); +int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); +#ifdef IGB_HWMON +void igb_sysfs_exit(struct igb_adapter *adapter); +int igb_sysfs_init(struct igb_adapter *adapter); +#else +#ifdef IGB_PROCFS +int igb_procfs_init(struct igb_adapter *adapter); +void igb_procfs_exit(struct igb_adapter *adapter); +int igb_procfs_topdir_init(void); +void igb_procfs_topdir_exit(void); +#endif /* IGB_PROCFS */ +#endif /* IGB_HWMON */ + +#define IGB_BIND _IOW('E', 200, int) +#define IGB_UNBIND _IOW('E', 201, int) +#define IGB_MAPRING _IOW('E', 202, int) +#define IGB_MAP_TX_RING IGB_MAPRING +#define IGB_UNMAPRING _IOW('E', 203, int) +#define IGB_UNMAP_TX_RING IGB_UNMAPRING +#define IGB_MAPBUF _IOW('E', 204, int) +#define IGB_UNMAPBUF _IOW('E', 205, int) +#define IGB_LINKSPEED _IOW('E', 206, int) +#define IGB_MAP_RX_RING _IOW('E', 207, int) +#define IGB_UNMAP_RX_RING _IOW('E', 208, int) + +/*set of newly defined ioctl calls - new libigb compatibility + each of them is an equivalent of the old ioctl + changed numberiong convention: new_ioctl = old_ioctl + 100*/ + +#define IGB_IOCTL_MAPRING _IOW('E', 302, int) +#define IGB_IOCTL_MAP_TX_RING IGB_IOCTL_MAPRING +#define IGB_IOCTL_UNMAPRING _IOW('E', 303, int) +#define IGB_IOCTL_UNMAP_TX_RING IGB_IOCTL_UNMAPRING +#define IGB_IOCTL_MAPBUF _IOW('E', 304, int) +#define IGB_IOCTL_UNMAPBUF _IOW('E', 305, int) +#define IGB_IOCTL_MAP_RX_RING _IOW('E', 307, int) +#define IGB_IOCTL_UNMAP_RX_RING _IOW('E', 308, int) + + +/*END*/ + +#define IGB_BIND_NAMESZ 24 + +struct igb_bind_cmd { + char iface[IGB_BIND_NAMESZ]; + u32 mmap_size; +}; + +struct igb_pci_lookup { + struct igb_adapter *adapter; + char *pci_info; +}; + +/* used with both map/unmap ring & buf ioctls */ +struct igb_buf_cmd { + u64 physaddr; + u32 queue; + u32 mmap_size; + u64 pa; +}; + +struct igb_link_cmd { + u32 up; + u32 speed; + u32 duplex; +}; + +struct igb_private_data { + struct igb_adapter *adapter; + /* user-dma specific variable for buffer */ + struct igb_user_page *userpages; + /* user-dma specific variable for TX and RX */ + u32 uring_tx_init; + u32 uring_rx_init; +}; + +#endif /* _IGB_H_ */ diff --git a/drivers/staging/igb_avb/igb_avb.7 b/drivers/staging/igb_avb/igb_avb.7 new file mode 100755 index 000000000000..ed8dba1036ad --- /dev/null +++ b/drivers/staging/igb_avb/igb_avb.7 @@ -0,0 +1,253 @@ +.\" LICENSE +.\" +.\" This software program is released under the terms of a license agreement between you ('Licensee') and Intel. Do not use or load this software or any associated materials (collectively, the 'Software') until you have carefully read the full terms and conditions of the LICENSE located in this software package. By loading or using the Software, you agree to the terms of this Agreement. If you do not agree with the terms of this Agreement, do not install or use the Software. +.\" +.\" * Other names and brands may be claimed as the property of others. +.\" +.TH igb 1 "January 5, 2012" + +.SH NAME +igb \-This file describes the Linux* Base Driver for the Gigabit Family of Adapters. +.SH SYNOPSIS +.PD 0.4v +modprobe igb [